文章系列
linux中断子系统 - 中断及执行流程
linux中断子系统 - 申请中断
linux中断子系统 - irq_desc的创建
linux中断子系统 - 中断控制器的注册
1. 前言
中断控制器的代码都在目录drivers/irqchip/目录下,本文以GIC-V3中断控制器为例来介绍中断控制器的注册,代码在文件irq-gic-v3.c中。
和中断控制器密切关联的结构体struct irq_domain、struct irq_chip,及其API,本文也详细介绍
2. 中断控制器初始化
/* drivers/irqchip/irq-gic-v3.c */
IRQCHIP_DECLARE(gic_v3, "arm,gic-v3", gic_of_init);-------注册gic-v3,初始化入口为函数gic_of_init
gic_of_init的初始化流程如下图
2.1 __irq_domain_add
struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, int size,
irq_hw_number_t hwirq_max, int direct_max,
const struct irq_domain_ops *ops,
void *host_data)
{
struct irq_domain *domain;
struct device_node *of_node;
of_node = to_of_node(fwnode);
domain = kzalloc_node(sizeof(*domain) + (sizeof(unsigned int) * size),
GFP_KERNEL, of_node_to_nid(of_node));
if (WARN_ON(!domain))
return NULL;
of_node_get(of_node);
/* Fill structure */
INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL);-----初始化revmap_tree
domain->ops = ops;-------------------------------------ops为gic_irq_domain_ops(此结构体的各个函数在第三节介绍)
domain->host_data = host_data;
domain->fwnode = fwnode;
domain->hwirq_max = hwirq_max;
domain->revmap_size = size;
domain->revmap_direct_max_irq = direct_max;
irq_domain_check_hierarchy(domain);
mutex_lock(&irq_domain_mutex);
list_add(&domain->link, &irq_domain_list);------------把irq_domain加入到全局irq_domain_list中
mutex_unlock(&irq_domain_mutex);
pr_debug("Added domain %s\n", domain->name);
return domain;
}
2.2 set_handle_irq
void __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
{
if (handle_arch_irq)
return;
handle_arch_irq = handle_irq;--------------------设置中断入口函数(作用参考系列文章)
}
3. IrqDomain
3.1 struct irq_domain
struct irq_domain {
struct list_head link;
const char *name;
const struct irq_domain_ops *ops;---------------domain操作函数集
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[];---------------/
};
3.2 struct irq_domain_ops
struct irq_domain_ops {
//如果irq_domain不是HIERARCHY的,那么使用下面的函数
int (*match)(struct irq_domain *d, struct device_node *node,
enum irq_domain_bus_token bus_token);
int (*map)(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw);----使hwirq映射到virq
void (*unmap)(struct irq_domain *d, unsigned int virq);---------------------解除映射关系
int (*xlate)(struct irq_domain *d, struct device_node *node,
const u32 *intspec, unsigned int intsize,
unsigned long *out_hwirq, unsigned int *out_type);-----------------得到hwirq
//相反,如果是HIERARCHY的,那么使用下面的函数
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
/* extended V2 interfaces to support hierarchy irq_domains */
int (*alloc)(struct irq_domain *d, unsigned int virq,
unsigned int nr_irqs, void *arg);---------------------------------同map的作用
void (*free)(struct irq_domain *d, unsigned int virq,
unsigned int nr_irqs);
void (*activate)(struct irq_domain *d, struct irq_data *irq_data);
void (*deactivate)(struct irq_domain *d, struct irq_data *irq_data);
int (*translate)(struct irq_domain *d, struct irq_fwspec *fwspec,
unsigned long *out_hwirq, unsigned int *out_type);----------------同unmap的作用
#endif
};
3.3 结合gic-v3介绍irq_domain_ops的作用
static const struct irq_domain_ops gic_irq_domain_ops = {
.translate = gic_irq_domain_translate,
.alloc = gic_irq_domain_alloc,
.free = gic_irq_domain_free,
};
下面主要介绍函数gic_irq_domain_alloc,它的作用主要是初始化irq_desc,函数调用图如下,
void irq_domain_set_info(struct irq_domain *domain, unsigned int virq,
irq_hw_number_t hwirq, struct irq_chip *chip,
void *chip_data, irq_flow_handler_t handler,
void *handler_data, const char *handler_name)
{
irq_domain_set_hwirq_and_chip(domain, virq, hwirq, chip, chip_data);
__irq_set_handler(virq, handler, 0, handler_name);------最终设置desc->handle_irq = handler;
irq_set_handler_data(virq, handler_data);
}
最主要的是通过上图,我可看出,根据PPI、SPI、LPI,这里的handler分情况设置,在内核里有这么几种情况:
extern void handle_level_irq(struct irq_desc *desc);
extern void handle_fasteoi_irq(struct irq_desc *desc);
extern void handle_edge_irq(struct irq_desc *desc);
extern void handle_edge_eoi_irq(struct irq_desc *desc);
extern void handle_simple_irq(struct irq_desc *desc);
extern void handle_percpu_irq(struct irq_desc *desc);
extern void handle_percpu_devid_irq(struct irq_desc *desc);
extern void handle_bad_irq(struct irq_desc *desc);
extern void handle_nested_irq(unsigned int irq);
具体都应用到什么情况只能后续TODO,大致上是根据中断的触发情况来分的 ^_^
3.4 结合gic介绍irq_domain_ops的map函数作用(非HIERARCHY)
在文件drivers/irqchip/irq_gic.c中介绍了irq_domain_ops的另一种情况,具体定义如下:
static const struct irq_domain_ops gic_irq_domain_ops = {
.map = gic_irq_domain_map,
.unmap = gic_irq_domain_unmap,
};
gic_irq_domain_map函数和gic_irq_domain_alloc函数作用差不多,最终也是调用到irq_domain_set_info来初始化irq_desc。
4. IrqChip
struct irq_chip {
const char *name;
unsigned int (*irq_startup)(struct irq_data *data);
void (*irq_shutdown)(struct irq_data *data);
void (*irq_enable)(struct irq_data *data);
void (*irq_disable)(struct irq_data *data);
void (*irq_ack)(struct irq_data *data);
void (*irq_mask)(struct irq_data *data);
void (*irq_mask_ack)(struct irq_data *data);
void (*irq_unmask)(struct irq_data *data);
void (*irq_eoi)(struct irq_data *data);
int (*irq_set_affinity)(struct irq_data *data, const struct cpumask *dest, bool force);
int (*irq_retrigger)(struct irq_data *data);
int (*irq_set_type)(struct irq_data *data, unsigned int flow_type);
int (*irq_set_wake)(struct irq_data *data, unsigned int on);
void (*irq_bus_lock)(struct irq_data *data);
void (*irq_bus_sync_unlock)(struct irq_data *data);
void (*irq_cpu_online)(struct irq_data *data);
void (*irq_cpu_offline)(struct irq_data *data);
void (*irq_suspend)(struct irq_data *data);
void (*irq_resume)(struct irq_data *data);
void (*irq_pm_shutdown)(struct irq_data *data);
void (*irq_calc_mask)(struct irq_data *data);
void (*irq_print_chip)(struct irq_data *data, struct seq_file *p);
int (*irq_request_resources)(struct irq_data *data);
void (*irq_release_resources)(struct irq_data *data);
void (*irq_compose_msi_msg)(struct irq_data *data, struct msi_msg *msg);
void (*irq_write_msi_msg)(struct irq_data *data, struct msi_msg *msg);
int (*irq_get_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool *state);
int (*irq_set_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool state);
int (*irq_set_vcpu_affinity)(struct irq_data *data, void *vcpu_info);
void (*ipi_send_single)(struct irq_data *data, unsigned int cpu);
void (*ipi_send_mask)(struct irq_data *data, const struct cpumask *dest);
unsigned long flags;
};
我们在这里可以看出irq_chip主要是来操作中断控制器硬件的行为,而irq_domain和硬件没太大关系,主要是来控制一个“中断域”的行为。