baron · 3月27日 · 四川

irq domain介绍和代码导读

快速连接

👉👉👉【精选】ARMv8/ARMv9架构入门到精通-目录 👈👈👈


 title=

补充IRQ Domain介绍
在linux kernel中,我们使用下面两个ID来标识一个来自外设的中断:


1、IRQ number。CPU需要为每一个外设中断编号,我们称之IRQ Number。这个IRQ number是一个虚拟的interrupt ID,和硬件无关,仅仅是被CPU用来标识一个外设中断。


2、HW interrupt ID。对于interrupt controller而言,它收集了多个外设的interrupt request line并向上传递,因此,interrupt controller需要对外设中断进行编码。Interrupt controller用HW interrupt ID来标识外设的中断。在interrupt controller级联的情况下,仅仅用HW interrupt ID已经不能唯一标识一个外设中断,还需要知道该HW interrupt ID所属的interrupt controller(HW interrupt ID在不同的Interrupt controller上是会重复编码的)。


这样,CPU和interrupt controller在标识中断上就有了一些不同的概念,但是,对于驱动工程师而言,我们和CPU视角是一样的,我们只希望得到一个IRQ number,而不关系具体是那个interrupt controller上的那个HW interrupt ID。这样一个好处是在中断相关的硬件发生变化的时候,驱动软件不需要修改。因此,linux kernel中的中断子系统需要提供一个将HW interrupt ID映射到IRQ number上来的机制......


(本段转载自:http://www.wowotech.net/linux...

在这里插入图片描述

1、创建一个irq domain,注意是tree型的

(linux/drivers/irqchip/irq-gic-v3.c)

static int __init gic_init_bases(void __iomem *dist_base,
                 struct redist_region *rdist_regs,
                 u32 nr_redist_regions,
                 u64 redist_stride,
                 struct fwnode_handle *handle)
......
    gic_data.domain = irq_domain_create_tree(handle, &gic_irq_domain_ops, &gic_data);
......
}

2、为domain分配中断号

(linux/drivers/irqchip/irq-gic-v3.c)

static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
                unsigned int nr_irqs, void *arg)
{
    int i, ret;
    irq_hw_number_t hwirq;
    unsigned int type = IRQ_TYPE_NONE;
    struct irq_fwspec *fwspec = arg;

    ret = gic_irq_domain_translate(domain, fwspec, &hwirq, &type);
    if (ret)
        return ret;

    for (i = 0; i < nr_irqs; i++) {
        ret = gic_irq_domain_map(domain, virq + i, hwirq + i);
        if (ret)
            return ret;
    }

    return 0;
}

3、对irq进行map

(linux/drivers/irqchip/irq-gic-v3.c)

static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
                  irq_hw_number_t hw)
{
    struct irq_chip *chip = &gic_chip;
    struct irq_data *irqd = irq_desc_get_irq_data(irq_to_desc(irq));

    if (static_branch_likely(&supports_deactivate_key))
        chip = &gic_eoimode1_chip;

    switch (__get_intid_range(hw)) {
    case SGI_RANGE:
    case PPI_RANGE:
    case EPPI_RANGE:
        irq_set_percpu_devid(irq);
        irq_domain_set_info(d, irq, hw, chip, d->host_data,
                    handle_percpu_devid_irq, NULL, NULL);
        break;

    case SPI_RANGE:
    case ESPI_RANGE:
        irq_domain_set_info(d, irq, hw, chip, d->host_data,
                    handle_fasteoi_irq, NULL, NULL);
        irq_set_probe(irq);
        irqd_set_single_target(irqd);
        break;

    case LPI_RANGE:
        if (!gic_dist_supports_lpis())
            return -EPERM;
        irq_domain_set_info(d, irq, hw, chip, d->host_data,
                    handle_fasteoi_irq, NULL, NULL);
        break;

    default:
        return -EPERM;
    }

    /* Prevents SW retriggers which mess up the ACK/EOI ordering */
    irqd_set_handle_enforce_irqctx(irqd);
    return 0;
}

5、使用示例:

xxxx_yyyy_engine {
    compatible = "xxxx,yyyy_engine";
    interrupts = <0 52 0x4>,<0 57 0x4>;  // 两个硬件中断号
};

yyyy_irq1 = irq_of_parse_and_map(np, 0);  //map成linux kernel中的中断号
yyyy_irq2 = irq_of_parse_and_map(np, 1);  //map成linux kernel中的中断号


ret = request_irq(yyyy_irq1, handler1,IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "name1", NULL) //注册中断
ret = request_irq(yyyy_irq2, handler2,IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "name2", NULL) //注册中断

参考irqdomain


在这里插入图片描述
《ARMv8/ARMv9架构学习系列课程》全系列,共计51节课,超15h的视频课程


关注"Arm精选"公众号,备注进ARM交流讨论区。
图片1.png

推荐阅读
关注数
9466
内容数
212
以易懂、渐进、有序的方式,深入探讨ARMv8/ARMv9架构的核心概念。我们将从基础知识开始,逐步深入,覆盖最新的架构,不再纠缠于过时技术。本系列内容包含但不限于ARM基础、SOC芯片基础、Trustzone、gic、异常和中断、AMBA、Cache、MMU等内容,并将持续更新。
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息