GIC驱动

本文以RK3288 GIC-400为例学习

kernel/driver/irqchip/irq-gic.c(GICV2版本驱动)

gic再dts中的定义
rk3288.dtsi
gic: interrupt-controller@ffc01000 {
compatible = “arm,gic-400”;
interrupt-controller;
#interrupt-cells = <3>;
#address-cells = <0>;

    reg = <0x0 0xffc01000 0x0 0x1000>,
          <0x0 0xffc02000 0x0 0x2000>,
          <0x0 0xffc04000 0x0 0x2000>,
          <0x0 0xffc06000 0x0 0x2000>;
    interrupts = <GIC_PPI 9 0xf04>;
};

1.dts中的gic设备匹配过程

irq-gic.c中通过IRQCHIP_DECLARE定义了很多gic类型支持,所有的入口都是gic_of_init

IRQCHIP_DECLARE(gic_400, “arm,gic-400”, gic_of_init);
IRQCHIP_DECLARE(arm11mp_gic, “arm,arm11mp-gic”, gic_of_init);
IRQCHIP_DECLARE(arm1176jzf_dc_gic, “arm,arm1176jzf-devchip-gic”, gic_of_init);
IRQCHIP_DECLARE(cortex_a15_gic, “arm,cortex-a15-gic”, gic_of_init);
IRQCHIP_DECLARE(cortex_a9_gic, “arm,cortex-a9-gic”, gic_of_init);
IRQCHIP_DECLARE(cortex_a7_gic, “arm,cortex-a7-gic”, gic_of_init);
IRQCHIP_DECLARE(msm_8660_qgic, “qcom,msm-8660-qgic”, gic_of_init);
IRQCHIP_DECLARE(msm_qgic2, “qcom,msm-qgic2”, gic_of_init);
IRQCHIP_DECLARE(pl390, “arm,pl390”, gic_of_init);

IRQCHIP_DECLARE会把所有被支持的gic保存到一个table里

kernel启动过程中start_kernel()会通过init_IRQ()初始化中断控制器.

start_kernel()---->init/main.c
|
init_IRQ()—>arch/arm/kernel/irq.c
|
irqchip_init()---->driver/irqchip/irqchip.c

void __init irqchip_init(void)
{
of_irq_init(__irqchip_of_table);
acpi_probe_device_table(irqchip);
}
__irqchip_of_table就是上面IRQCHIP_DECLARE保存的所有kernel支持的所有中断控制器of_device_id信息的table,
of_irq_init执行之前,kernel已经更具dts完成了device tree的初始化,dts中所有的设备都生产了设备节点,并且形成了树状结构.没一个节点代表一个device_node,
of_irq_init就是在所有的device node中寻找中断控制器节点,形成树状结构(扫描interrupt controller).之后,从root interrupt controller节点开始,对于没一个interrupt controller的device node,扫描irq chip table,进行匹配,一旦匹配到,就调用该interrupt controller的初始化函数gic_of_init,

2.驱动代码
当dts gic设备节点和irq chip driver匹配后,我们就进入了函数gic_of_init开始了gic初始化工作.

static int __init gic_of_init(struct device_node *node,
struct device_node *parent)
{

__gic_init_bases(gic_base, gic_len, cpu_vec, 0, node);

}
__gic_init_bases完成了主要工作,包括irq domain的注册,通过irq_domain_add_simple完成gic的注册,主要就是建立了硬件中断和中断号之间的映射关系.
之后的工作就是irq domain来负责对中断号进行转换并处理了.

作为一个interrupt controller,除了注册自己管理的irq_domain,还需要提供给上级适用的irq_handler,如果作为second GIC,上级是root GIC,那么久需要调用irq_set_chained_handler注册到root GIC中;上级是CPU,久需要调用set_handle_irq(gic_handle_irq)把这个irq_handler注册到平台的irq处理接口中,这条语句的功能就是,当CPU发生了中断最先调用的就是root GIC的gic_handle_irq.然后在此函数中进行gic的irq domain处理

3.irq_domain的注册
irq_domain的注册,需要一个irq_domain_ops的结构体,定义如下:
static const struct irq_domain_ops gic_irq_domain_ops = {
.map = gic_irq_domain_map,
.unmap = gic_irq_domain_unmap,
};
map和unmap是映射和解除映射操作,MAP回调函数是在创建hwirq到irq num关系的时候被调用的,注册irq domain只是一个空的关系表,而这个表的实质是在irq_of_parse_and_map里面进行的.

irq_map的调用过程
gic_of_init–>irq_of_parse_and_map—>irq_create_of_mapping->irq_create_fwspec_mapping–>irq_create_mapping–>irq_domain_associate–>{domain->ops->map(domain, virq, hwirq);}

也就是说,最终是通过调用irq domain的map函数实现最后的映射,实现中断号和gic的绑定

static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hw)
{…
if (hw < 32) {
irq_set_percpu_devid(irq);
irq_domain_set_info(d, irq, hw, chip, d->host_data,
handle_percpu_devid_irq, NULL, NULL);
irq_set_status_flags(irq, IRQ_NOAUTOEN);
} else {
irq_domain_set_info(d, irq, hw, chip, d->host_data,
handle_fasteoi_irq, NULL, NULL);
irq_set_probe(irq);
}
}
通过irq_domain_set_info实现主要功能
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_set_chip_and_handler_name(virq, chip, handler, handler_name);
irq_set_chip_data(virq, chip_data);
irq_set_handler_data(virq, handler_data);
}

__irq_set_handler函数是用来设置irq chip和相应的上层irq handler的,一般内核中断子系统已经实现了相应的函数,我们只需要按需赋值即可.
他负责对一个irq num调用所有通过irq_request注册的irq handler,称之为上层中断服务函数.

4.中断调用流程
参考https://blog.csdn.net/yhb1047818384/article/details/99706106
由上可知,root gic驱动除了提供irq domain以外,还有注册到CPU中断服务函数入口,而这个中断服务函数的入口就是gic_handle_irq.
__gic_init_bases里面会设置这个gic_handle_irq
set_handle_irq(gic_handle_irq);

static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
{
u32 irqstat, irqnr;
struct gic_chip_data *gic = &gic_data[0];
void __iomem *cpu_base = gic_data_cpu_base(gic);

do {
	irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
	irqnr = irqstat & GICC_IAR_INT_ID_MASK;

	if (likely(irqnr > 15 && irqnr < 1021)) {
		if (static_key_true(&supports_deactivate))
			writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
		handle_domain_irq(gic->domain, irqnr, regs);
		continue;
	}
	if (irqnr < 16) {
		writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
		if (static_key_true(&supports_deactivate))
			writel_relaxed(irqstat, cpu_base + GIC_CPU_DEACTIVATE);

#ifdef CONFIG_SMP
/*
* Ensure any shared data written by the CPU sending
* the IPI is read after we’ve read the ACK register
* on the GIC.
*
* Pairs with the write barrier in gic_raise_softirq
*/
smp_rmb();
handle_IPI(irqnr, regs);
#endif
continue;
}
break;
} while (1);
}

如上,中断来的时候会最先调用这个函数,会读取GIC寄存器获取hwirq,并且查找对应的irq num,irq_ 然后调用handle_domain_irq,最后调用上层irq handler

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

九月天-深圳专业软硬件开发

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值