linux中断子系统 - 中断控制器的注册

文章系列


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和硬件没太大关系,主要是来控制一个“中断域”的行为。

5. Change Log


  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值