可以从 request_irq(include/linux/interrupt.h)函数一路分析得到
irq_desc结构体(中断描述符)
- 中断信号告知CPU流程
外部设备1和外部设备n共享一个GPIO的中断号B,多个GPIO的中断又汇聚到GIC中,GIC去告知CPU发生了GPIO分类的中断。 - CPU处理中断信号流程
CPU收到GIC的GPIO分类的中断A后,调用irq_desc[A].handle_irq去,查询哪组GPIO引起了中断,如果多个GPIO发生了中断,我想应该是都一起处理的?然后找到GPIO中中断号为B的irq_desc[B].handle_irq来处理组里的中断,这个组里包含多个GPIO触发情况,所以要去硬件里读取那个引脚发生的。最后才是调用action链表里对应的用户中断函数处理。
//include/linux/irqdesc.h
struct irq_desc {
//...
struct irq_data irq_data;
//...
irq_flow_handler_t handle_irq; //中断处理函数
//...
struct irqaction *action; /* IRQ action list */ //驱动注册的处理函数
//...
const char *name;
} ____cacheline_internodealigned_in_smp;
irqaction结构体
当调用request_irq、request_threaded_irq注册中断处理函数时,内核就会构造一个irqaction结构体。
handler: 该指针所指向的函数就是在中断服务程序,当中断发生时内核便会调用这个指针指向的函数。
dev_id: 当存在共享中断号时,即flags = SA_SHIRQ,用以区分不同的成员。
//include/linux/interrupt.h
struct irqaction {
irq_handler_t handler; //中断处理上半部函数,处理紧急事情
void *dev_id; //作用:中断处理函数执行时,可以调用dev_id,卸载函数时用dev_id区分函数
void __percpu *percpu_dev_id;
struct irqaction *next; //是一个链表
irq_handler_t thread_fn; //handler执行完毕,linux唤醒对应内核线程,里面调用thread_fn
//handler和thread_fn可以只一个或两个都调用。
struct task_struct *thread;
struct irqaction *secondary;
unsigned int irq; //中断号
unsigned int flags; //设置一些标志位,如SA_SHIRQ共享中断号
unsigned long thread_flags;
unsigned long thread_mask;
const char *name;
struct proc_dir_entry *dir;
} ____cacheline_internodealigned_in_smp;
irq_data结构体
irq_data是一个中转站,包含irq_chip指针、irq_domain指针。
irq是软件中断号,hwirq是硬件中断号。irq_desc[B]的B是软件中断号
irq和hwirq之前的转换和建立是在irq_domain中建立的。
//include/linux/irq.h
struct irq_data {
u32 mask;
unsigned int irq; //软件中断号
unsigned long hwirq; //硬件中断号
struct irq_common_data *common;
struct irq_chip *chip; //硬件函数
struct irq_domain *domain; //建立irq、hwirq之间的联系
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
struct irq_data *parent_data;
#endif
void *chip_data;
};
irq_domain结构体
struct irq_domain与中断控制器对应,完成的工作是硬件中断号到Linux irq的映射
//include/linux/irqdomain.h
struct irq_domain {
struct list_head link;
const char *name;
const struct irq_domain_ops *ops; //各类操作函数,
//xlate解析设备树中断属性,提取hwirq、type>等信息
//map把hwirq转换为irq
void *host_data;
unsigned int flags;
/* Optional data */
struct fwnode_handle *fwnode;
enum irq_domain_bus_token bus_token;
struct irq_domain_chip_generic *gc;
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
struct irq_domain *parent;
#endif
/* reverse map data. The linear map gets appended to the irq_domain */
irq_hw_number_t hwirq_max;
unsigned int revmap_direct_max_irq;
unsigned int revmap_size;
struct radix_tree_root revmap_tree;
unsigned int linear_revmap[];
};
irq_chip结构体
struct irq_chip用于对中断控制器的硬件操作
//include/linux/irq.h
struct irq_chip {
const char *name;
// @irq_startup: start up the interrupt (defaults to ->enable if NULL)
unsigned int (*irq_startup)(struct irq_data *data);
// @irq_shutdown: shut down the interrupt (defaults to ->disable if NULL)
void (*irq_shutdown)(struct irq_data *data);
// @irq_enable: enable the interrupt (defaults to chip->unmask if NULL)
void (*irq_enable)(struct irq_data *data);
// @irq_disable: disable the interrupt
void (*irq_disable)(struct irq_data *data);
// @irq_ack: start of a new interrupt
void (*irq_ack)(struct irq_data *data);
// @irq_mask: mask an interrupt source
void (*irq_mask)(struct irq_data *data);
// @irq_mask_ack: ack and mask an interrupt source
void (*irq_mask_ack)(struct irq_data *data);
// @irq_unmask: unmask an interrupt source
void (*irq_unmask)(struct irq_data *data);
// @irq_eoi: end of interrupt
void (*irq_eoi)(struct irq_data *data);
//...
};
一些个人理解
既然上面说了可以从request_irq
来分析,那我确实去看了一下,大概是这样:
这个函数似乎只涉及了irqaction
和irq_desc
至于irq_data
似乎是一个糅合的结构体,包含了irq_domain
和irq_chip
、软硬件的中断号。
irq_domain
是BSP开发工程师提供的,会把irq的中断号映射成。
irq_chip
则是对中断寄存器的操作函数,似乎也是BSP工程师提供的。