层级中断控制器驱动程序编写

本文详细介绍了嵌入式系统中层级中断控制器的工作原理,包括GPIO和GIC驱动的角色。阐述了中断处理流程,从硬件中断到软件处理的转换,以及中断的mask、ack和unmask操作。同时,讲解了设备树如何配置中断,并展示了虚拟中断控制器的驱动程序实现,涉及中断域的递归处理和中断芯片的管理。最后,给出了一个虚拟中断控制器的编程示例。
摘要由CSDN通过智能技术生成

一、层级中断控制器的重要函数和结构体

1.1 回顾处理流程

  • handleA、irq_dataA由GIC驱动提供
  • irq_dataB由GPIO驱动提供,不需要handleB

在这里插入图片描述

  • 假设GPIO模块下有4个引脚,都可以产生中断,分别链接到GIC的100~103号中断
  • GPIO就是一个层级中断控制器
  • 对于GPIO模块中0~3这四个hwirq,分配四个irq_descss,用到时再分配
  • 假设这4个irq_desc的序列号为234~237
    • 在GIC domian中记录(100,234)、(100,235)、(102,236)、(103,237)
    • 在GPIO domain中记录(0,234)、(1,235)、(2,236)、(3,237)
  • 对于KEY,注册中断时就是:request_irq(236, …)
  • 按下KEY时:
    • 程序从GIC中读取寄存器知道发生了102号中断,通过GIC irq_domain可以知道virq为236
    • 处理virq 236号中断:调用irq_desc[236].handle_irq,即handleA
      • mask/ack中断:
        • 调用irq_desc[236].irq_data->irq_chip的函数,即irq_dataB
          • 它会调用父级irq_dataA->irq_chip的函数
        • 调用irq_desc[236].action链表中用户注册的函数
        • unmask中断:
          • 调用irq_desc[236].irq_data->irq_chip的函数,即irq_dataB
            • 它会调用父级irq_dataA->irq_chip的函数

1.2 irq_domain的核心作用

irq_domain可以把handleA、GIC domain和GPIO domain、irq_chipA和irq_chipB这四个结构体组织起来。
把上图中的层级中断控制器当做GPIO控制器。

在设备树里指定使用哪个中断

gpio_keys_100ask {
	compatible = "100ask,gpio_key";
	interrupt-parent = <&gpio5>;
	interrupts = <3 IRQ_TYPE_EDGE_BOTH>,
};
  • 内核解析、处理设备树的中断信息

    • 根据interrupt-parent找到驱动程序注册的GPIO irq_domain
    • GPIO irq_domain对设备树的解析
      • 使用GPIO irq_domain.ops中的translate或xltate函数解析设备树,得到hwirq和type
      • 分配/找到irq_desc,得到virq
        • 把(hwirq、virq)的关系存入GPIO irq_domain
        • 把virq存入platform_device的resource中
      • 修改得到对应的GIC_hwirq,调用父级GIC irq_domain继续解析
        • 把(GIC_hwirq, virq)的关系存入GIC irq_domain
      • 注意:对于同一个引脚中断,它在两个irq_domain里virq是相同的,hwirq可能不一样。
    • GPIO irq_domain对设备树的设置
      • 使用GPIO irq_domain.ops中的alloc函数进行设置
        • 替换irq_desc[virq].irq_data,里面有irq_chip改为irq_chipB,即GPIO的irq_chip
        • 调用父级GIC irq_domain的alloc继续设置
          • 设置irq_desc[virq].handle_irq为GIC的handle_irq,即上图中的handleA
  • 用户的驱动程序注册中断

    • 从platform_device的resource中得到中断号virq
    • request_irq(virq, …, func)
  • 发生中断、处理中断:处理流程见上面。

二、硬件模型

100ask实现了一些虚拟的中断控制器,如下图所示。
实际板子中,我们可以通过按键触发中断。
对于这些虚拟的中断控制器,我们没有真实按键,通过devmem指令写GIC的PENDING寄存器触发中断。
在这里插入图片描述

三、编程

会涉及2个驱动程序:虚拟的中断控制器驱动程序,按键驱动程序,以及对应的设备树。
虚拟的中断控制器驱动程序中,涉及2个递归处理。

3.1 alloc的递归处理

在这里插入图片描述

3.2 irq_chip的递归处理

在这里插入图片描述

virtual_intc.dts

/ {
	virtual_intc: virtual_intc_100ask {
		compatible = "100ask,virtual_intc";

		interrupt-controller;
		#interrupt-cells = <2>;

		interrupt-parent = <&intc>;
		upper_hwirq_base = <122>;
	};

	gpio_keys_100ask {
        compatible = "100ask,gpio_key";
		interrupt-parent = <&virtual_intc>;		// which interrupt use
		interrupts = <0 IRQ_TYPE_LEVEL_HIGH>,	// which interrupt line
					 <1 IRQ_TYPE_LEVEL_HIGH>,
				 	 <2 IRQ_TYPE_LEVEL_HIGH>,
					 <3 IRQ_TYPE_LEVEL_HIGH>;
    };
};

virtual_int_controller.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/random.h>
#include <linux/irqdomain.h>
#include <linux/irq.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/of_irq.h>

#include <dt-bindings/interrupt-controller/arm-gic.h>

static struct irq_domain *virtual_intc_domain;
static u32 upper_hwirq_base;

static int virtual_intc_domain_translate(struct irq_domain *d,
		struct irq_fwspec *fwspec, unsigned long *hwirq, unsigned int *type)
{
	if(is_of_node(fwspec->fwnode)) {
		if(fwspec->param_count != 2)
			return -EINVAL;

		*hwirq = fwspec->param[0];
		*type = fwspec->param[1];
		return 0;
	}
	
	return -EINVAL;
}

static void virtual_intc_irq_mask(struct irq_data *data)
{
	printk("%s %d\n", __FUNCTION__, __LINE__);
	irq_chip_mask_parent(data);
}

static void virtual_intc_irq_unmask(struct irq_data *data)
{
	printk("%s %d\n", __FUNCTION__, __LINE__);
	irq_chip_unmask_parent(data);
}

static void virtual_intc_irq_eoi(struct irq_data *data)
{
	printk("%s %d\n", __FUNCTION__, __LINE__);
	irq_chip_eoi_parent(data);
}

static struct irq_chip virtual_intc_chip = {
	.name = "virtual_intc",
	.irq_mask = virtual_intc_irq_mask,
	.irq_unmask = virtual_intc_irq_unmask,
	.irq_eoi = virtual_intc_irq_eoi,
};

static int virtual_intc_domain_alloc(struct irq_domain *domain,
		unsigned int irq, unsigned int nr_irqs, void *data)
{
	struct irq_fwspec *fwspec = data;
	struct irq_fwspec parent_fwspec;
	irq_hw_number_t hwirq;
	int i;

	hwirq = fwspec->param[0];
	for(i=0;i<nr_irqs;i++) {
		irq_domain_set_hwirq_and_chip(domain, irq+i, hwirq+i,
				&virtual_intc_chip, NULL);
	}

	parent_fwspec.fwnode = domain->parent->fwnode;
	parent_fwspec.param_count = 3;
	parent_fwspec.param[0] = GIC_SPI;
	parent_fwspec.param[1] = fwspec->param[0] + upper_hwirq_base;
	parent_fwspec.param[2] = fwspec->param[1];

	return irq_domain_alloc_irqs_parent(domain, irq, nr_irqs,
			&parent_fwspec);
}

static const struct irq_domain_ops virtual_intc_domain_ops = {
	.translate = virtual_intc_domain_translate,
	.alloc = virtual_intc_domain_alloc,
};

static int virtual_intc_probe(struct platform_device *pdev)
{
	struct irq_domain *parent_domain;
	struct device_node *parent;
	int err;

	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	err = of_property_read_u32(pdev->dev.of_node, "upper_hwirq_base",
			&upper_hwirq_base);
	parent = of_irq_find_parent(pdev->dev.of_node);
	parent_domain = irq_find_host(parent);

	virtual_intc_domain = irq_domain_add_hierarchy(parent_domain, 0, 4,
			pdev->dev.of_node, &virtual_intc_domain_ops,
			NULL);

	return 0;
}

static int virtual_intc_remove(struct platform_device *pdev)
{
	printk("%s %d\n", __FUNCTION__, __LINE__);
	return 0;
}

static const struct of_device_id virtual_intc_of_match[] = {
	{ .compatible = "100ask,virtual_intc" },
	{ },
};

struct platform_driver virtual_intc_driver = {
	.probe = virtual_intc_probe,
	.remove = virtual_intc_remove,
	.driver = {
		.name = "100ask_virtual_intc",
		.of_match_table = of_match_ptr(virtual_intc_of_match),
	},
};

static int __init virtual_intc_init(void)
{
	int ret = 0;
	printk("%s %d\n", __FUNCTION__, __LINE__);
	ret = platform_driver_register(&virtual_intc_driver);
	return ret;
}

static void __exit virtual_intc_exit(void)
{
	printk("%s %d\n", __FUNCTION__, __LINE__);
	platform_driver_unregister(&virtual_intc_driver);
}

module_init(virtual_intc_init);
module_exit(virtual_intc_exit);
MODULE_LICENSE("GPL");
[   54.331680] virtual_intc_init 128
[   54.335483] /home/chen/Documents/mygit/100ask_rk3288_module/16_interrupt/03_virtual_interrupt_hierarchy/virtual_int_controller.c virtual_intc_probe 92
[   56.579864] [dhd] CFG80211-ERROR) wl_cfg80211_netdev_notifier_call : wdev null. Do nothing
[   56.599063] [dhd] CFG80211-ERROR) wl_cfg80211_netdev_notifier_call : wdev null. Do nothing
[   56.599615] [dhd] CFG80211-ERROR) wl_cfg80211_netdev_notifier_call : wdev null. Do nothing
[   56.600222] [dhd] CFG80211-ERROR) wl_cfg80211_netdev_notifier_call : wdev null. Do nothing
[   62.034252] /home/chen/Documents/mygit/100ask_rk3288_module/16_interrupt/03_virtual_interrupt_hierarchy/gpio_key_drv.c gpio_key_init line 97
[   62.047533] /home/chen/Documents/mygit/100ask_rk3288_module/16_interrupt/03_virtual_interrupt_hierarchy/gpio_key_drv.c gpio_key_probe line 53
[   62.062901] virtual_intc_irq_unmask 40
[   62.066740] devm_request_irq 235 for 100ask_virtual_key0, err = 0
[   62.075188] virtual_intc_irq_unmask 40
[   62.080482] devm_request_irq 236 for 100ask_virtual_key1, err = 0
[   62.086763] virtual_intc_irq_unmask 40
[   62.092789] devm_request_irq 237 for 100ask_virtual_key2, err = 0
[   62.099254] virtual_intc_irq_unmask 40
[   62.103078] devm_request_irq 238 for 100ask_virtual_key3, err = 0
[  102.603422] key 100ask_virtual_key0 0
[  102.608214] virtual_intc_irq_eoi 46
[  105.535000] key 100ask_virtual_key0 1
[  105.539793] virtual_intc_irq_eoi 46
[  106.628216] key 100ask_virtual_key0 2
[  106.632963] virtual_intc_irq_eoi 46
[  117.007952] key 100ask_virtual_key1 0
[  117.012737] virtual_intc_irq_eoi 46
[  118.196356] key 100ask_virtual_key1 1
[  118.201144] virtual_intc_irq_eoi 46
[  122.844399] key 100ask_virtual_key2 0
[  122.849253] virtual_intc_irq_eoi 46
[  124.081962] key 100ask_virtual_key2 1
[  124.086749] virtual_intc_irq_eoi 46
[  128.300212] key 100ask_virtual_key3 0
[  128.305002] virtual_intc_irq_eoi 46
[  130.025301] key 100ask_virtual_key3 1
[  130.030091] virtual_intc_irq_eoi 46
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

习惯就好zz

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

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

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

打赏作者

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

抵扣说明:

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

余额充值