diff --git a/IMX6ULL/doc_pic/08_Interrupt/14_两类中断控制器处理流程_链式和层级.md b/IMX6ULL/doc_pic/08_Interrupt/14_两类中断控制器处理流程_链式和层级.md new file mode 100644 index 0000000..5443393 --- /dev/null +++ b/IMX6ULL/doc_pic/08_Interrupt/14_两类中断控制器处理流程_链式和层级.md @@ -0,0 +1,109 @@ +## 两类中断控制器处理流程_链式和层级 + +参考资料: + +* [linux kernel的中断子系统之(七):GIC代码分析](http://www.wowotech.net/irq_subsystem/gic_driver.html) + +* Linux 4.9.88内核源码 + + * `Linux-4.9.88\drivers\gpio\gpio-mxc.c` + * `Linux-4.9.88\arch\arm\boot\dts\imx6ull.dtsi` + +* Linux 5.4内核源码 + + * `Linux-5.4\drivers\pinctrl\stm32\pinctrl-stm32mp157.c` + * `Linux-5.4\drivers\irqchip\irq-stm32-exti.c` + * `Linux-5.4\arch\arm\boot\dts\stm32mp151.dtsi` + + + +### 1. 下级中断控制器的类别 + +在后续课程中我们把GIC之下的中断控制器分为两类:链式(chained)、层级(hierarchy)。 + +这个分类并没有官方定义,是我们根据代码概括出来的(Linux内核本来就缺乏文档)。 + +![image-20210703091444149](pic/08_Interrupt/073_two_type_intc.png) + + + +#### 1.1 链式中断控制器(chained) + +上图中,左边的"chained intc"就是链式中断控制器。 + +它底下的4个中断触发时,都会导致GIC的33号中断被触发。 + +处理中断时,需要**分辨**:是谁触发了GIC 33号中断?这需要读取"chained intc"中的寄存器。 + + + +#### 1.2 层级中断控制器(hierarchy) + +上图中,右边边的"hierarchy intc"就是链式中断控制器。 + +它底下的4个中断,跟GIC中的4个中断一一对应。 + +处理GIC 100~103号中断时,不需要读取"hierarchy intc"的寄存器来**分辨**是谁触发了中断。 + + + +### 2. 链式中断控制器的处理流程 + +下图中: + +* handleA、irq_dataA由GIC驱动提供 +* handleB、irq_dataB由GPIO驱动提供 +* handleC也是GPIO驱动提供 + +![image-20210627235754147](pic/08_Interrupt/072_chained_intc.png) + +* 假设GPIO模块下有4个引脚,都可以产生中断,都连接到GIC的33号中断 +* GPIO就是一个链式中断控制器,它底下有4个中断 +* 对于GPIO模块中0~3这四个hwirq,分配四个irq_desc + * 可以一下子分配4个:legacy,老方法 + * 也可以用到时再分配:linear,新方法 +* 假设这4个irq_desc的序号为100~103,在GPIO domain中记录(0,100) (1,101)(2,102) (3,103) +* 对于KEY,注册中断时就是:`request_irq(102, ...)` +* 按下KEY时: + * 程序从GIC中读取寄存器知道发生了33号中断,通过GIC irq_domain可以知道virq为17 + * 处理virq 17号中断:调用irq_desc[17].handle_irq,即handleB + * mask/ack中断: 调用irq_desc[17].irq_data->irq_chip的函数,即irq_dataA + * 细分中断源、处理 + * 读取GPIO寄存器,确定是GPIO里2号引脚发生中断 + * 通过GPIO irq_domain可以知道virq为102 + * 处理virq 102号中断:调用irq_desc[102].handle_irq,即handleC + * mask/ack中断: 调用irq_desc[102].irq_data->irq_chip的函数 + * 调用irq_desc[102].action链表中用户注册的函数 + * unmask中断: 调用irq_desc[102].irq_data->irq_chip的函数 + * unmask中断: 调用irq_desc[17].irq_data->irq_chip的函数 + +### 3. 层级中断控制器的处理流程 + +下图中: + +* handleA、irq_dataA由GIC驱动提供 +* irq_dataB由GPIO驱动提供,不需要handleB + +![image-20210703101721943](pic/08_Interrupt/074_hierarchy_intc.png) + +* 假设GPIO模块下有4个引脚,都可以产生中断,分别链接到GIC的100~103号中断 +* GPIO就是一个层级中断控制器 +* 对于GPIO模块中0~3这四个hwirq,分配四个irq_desc,用到时再分配 +* 假设这4个irq_desc的序号为234~237 + * 在GIC domain中记录(100,234) (101,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的函数 + +### 4. 处理流程对比 + +![image-20210703114531760](pic/08_Interrupt/075_compare_intc.png) \ No newline at end of file diff --git a/IMX6ULL/doc_pic/08_Interrupt/14_两类中断控制器处理流程_链式和层级.tif b/IMX6ULL/doc_pic/08_Interrupt/14_两类中断控制器处理流程_链式和层级.tif new file mode 100644 index 0000000..b5edd51 Binary files /dev/null and b/IMX6ULL/doc_pic/08_Interrupt/14_两类中断控制器处理流程_链式和层级.tif differ diff --git a/IMX6ULL/doc_pic/08_Interrupt/15_链式中断控制器驱动程序编写.md b/IMX6ULL/doc_pic/08_Interrupt/15_链式中断控制器驱动程序编写.md new file mode 100644 index 0000000..a2d8e49 --- /dev/null +++ b/IMX6ULL/doc_pic/08_Interrupt/15_链式中断控制器驱动程序编写.md @@ -0,0 +1,111 @@ +## 链式中断控制器驱动程序编写 + +参考资料: + +* [linux kernel的中断子系统之(七):GIC代码分析](http://www.wowotech.net/irq_subsystem/gic_driver.html) + +* Linux 4.9.88内核源码 + + * `Linux-4.9.88\drivers\gpio\gpio-mxc.c` + * `Linux-4.9.88\arch\arm\boot\dts\imx6ull.dtsi` + +* Linux 5.4内核源码 + + * `Linux-5.4\drivers\pinctrl\stm32\pinctrl-stm32mp157.c` + * `Linux-5.4\drivers\irqchip\irq-stm32-exti.c` + * `Linux-5.4\arch\arm\boot\dts\stm32mp151.dtsi` + + +* 本节视频源码在GIT仓库里 + + ```shell + doc_and_source_for_drivers\IMX6ULL\source\08_Interrupt\03_virtual_int_controller_legacy + doc_and_source_for_drivers\STM32MP157\source\A7\08_Interrupt\03_virtual_int_controller_legacy + ``` + + + +### 1. 链式中断控制器的重要函数和结构体 + +#### 1.1 回顾处理流程 + +为方便描述,假设下级的链式中断控制器就是GPIO控制器。 + +![image-20210630171229980](pic/08_Interrupt/071_importan_function.png) + +沿着中断的处理流程,GIC之下的中断控制器涉及这4个重要部分:handleB、GPIO Domain、handleC、irq_chip + +* **handleB**:处理GIC 33号中断,handleB由GPIO驱动提供 + + * 屏蔽GIC 33号中断:调用irq_dataA的irq_chip的函数,irq_dataA由GIC驱动提供 + * 细分并处理某个GPIO中断: + * 读取GPIO寄存器得到hwirq,通过**GPIO Domain**转换为virq,假设是102 + * 调用irq_desc[102].handle_irq,即handleC + * 清除GIC 33号中断:调用irq_dataA的irq_chip的函数,由GIC驱动提供 + +* **handleC**:处理GPIO 2号中断,handleC由GPIO驱动提供 + + * 屏蔽GPIO 2号中断:调用irq_dataB的**irq_chip**的函数,由GPIO驱动提供 + * 处理:调用actions链表中用户注册的函数 + * 清除GPIO 2号中断:调用irq_dataB的irq_chip的函数,由GPIO驱动提供 + + + + +#### 1.2 irq_domain的核心作用 + +怎么把handleB、GPIO Domain、handleC、irq_chip这4个结构体组织起来,irq_domain是核心。 + +我们从使用中断的流程来讲解。 + +* 在设备树里指定使用哪个中断 + + ```shell + gpio_keys_100ask { + compatible = "100ask,gpio_key"; + interrupt-parent = <&gpio5>; + interrupts = <3 IRQ_TYPE_EDGE_BOTH>, + }; + ``` + +* 内核解析、处理设备树的中断信息 + + * 根据`interrupt-parent`找到驱动程序注册的irq_domain + * 使用irq_domain.ops中的translate或xlate函数解析设备树,得到hwirq和type + + * 分配/找到irq_desc,得到virq + * 把(hwirq, virq)的关系存入irq_domain + * 把virq存入platform_device的resource中 + * 使用irq_domain.ops中的alloc或map函数进行设置 + * 可能是替换irq_desc[virq].handle_irq函数 + * 可能是替换irq_desc[virq].irq_data,里面有irq_chip + +* 用户的驱动程序注册中断 + + * 从platform_device的resource中得到中断号virq + * request_irq(virq, ..., func) + +* 发生中断、处理中断:处理流程见上面。 + + + +### 2. 硬件模型 + + 内核中有各类中断控制器的驱动程序,它们涉及的硬件过于复杂,从这些杂乱的代码中去讲清楚中断体系,比较难。 + + 我们实现一些虚拟的中断控制器,如下图所示。 + + 实际板子中,我们可以通过按键触发中断。 + + 对于这些虚拟的中断控制器,我们没有真实按键,通过devmem指令写GIC的PENDING寄存器触发中断。 + + ![image-20210703133953035](pic/08_Interrupt/076_virtual_intc_hardware.png) + + + +### 3. 编程 + +会涉及2个驱动程序:虚拟的中断控制器驱动程序,按键驱动程序,以及对应的设备树。 + + + diff --git a/IMX6ULL/doc_pic/08_Interrupt/pic/08_Interrupt/071_importan_function.png b/IMX6ULL/doc_pic/08_Interrupt/pic/08_Interrupt/071_importan_function.png new file mode 100644 index 0000000..f478849 Binary files /dev/null and b/IMX6ULL/doc_pic/08_Interrupt/pic/08_Interrupt/071_importan_function.png differ diff --git a/IMX6ULL/doc_pic/08_Interrupt/pic/08_Interrupt/072_chained_intc.png b/IMX6ULL/doc_pic/08_Interrupt/pic/08_Interrupt/072_chained_intc.png new file mode 100644 index 0000000..dbd58e6 Binary files /dev/null and b/IMX6ULL/doc_pic/08_Interrupt/pic/08_Interrupt/072_chained_intc.png differ diff --git a/IMX6ULL/doc_pic/08_Interrupt/pic/08_Interrupt/073_two_type_intc.png b/IMX6ULL/doc_pic/08_Interrupt/pic/08_Interrupt/073_two_type_intc.png new file mode 100644 index 0000000..c24b19a Binary files /dev/null and b/IMX6ULL/doc_pic/08_Interrupt/pic/08_Interrupt/073_two_type_intc.png differ diff --git a/IMX6ULL/doc_pic/08_Interrupt/pic/08_Interrupt/074_hierarchy_intc.png b/IMX6ULL/doc_pic/08_Interrupt/pic/08_Interrupt/074_hierarchy_intc.png new file mode 100644 index 0000000..d270628 Binary files /dev/null and b/IMX6ULL/doc_pic/08_Interrupt/pic/08_Interrupt/074_hierarchy_intc.png differ diff --git a/IMX6ULL/doc_pic/08_Interrupt/pic/08_Interrupt/075_compare_intc.png b/IMX6ULL/doc_pic/08_Interrupt/pic/08_Interrupt/075_compare_intc.png new file mode 100644 index 0000000..ad004d1 Binary files /dev/null and b/IMX6ULL/doc_pic/08_Interrupt/pic/08_Interrupt/075_compare_intc.png differ diff --git a/IMX6ULL/doc_pic/08_Interrupt/pic/08_Interrupt/076_virtual_intc_hardware.png b/IMX6ULL/doc_pic/08_Interrupt/pic/08_Interrupt/076_virtual_intc_hardware.png new file mode 100644 index 0000000..fa02edf Binary files /dev/null and b/IMX6ULL/doc_pic/08_Interrupt/pic/08_Interrupt/076_virtual_intc_hardware.png differ diff --git a/IMX6ULL/source/07_GPIO/04_gpio_use_pinctrl_ok/03_virtual_gpio_ok/virtual_gpio_driver0.c b/IMX6ULL/source/07_GPIO/04_gpio_use_pinctrl_ok/03_virtual_gpio_ok/virtual_gpio_driver0.c new file mode 100644 index 0000000..4585219 --- /dev/null +++ b/IMX6ULL/source/07_GPIO/04_gpio_use_pinctrl_ok/03_virtual_gpio_ok/virtual_gpio_driver0.c @@ -0,0 +1,128 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct gpio_chip * g_virt_gpio; +static int g_gpio_val = 0; + +static const struct of_device_id virtual_gpio_of_match[] = { + { .compatible = "100ask,virtual_gpio", }, + { }, +}; + +static int virt_gpio_direction_output(struct gpio_chip *gc, + unsigned offset, int val) +{ + printk("set pin %d as output %s\n", offset, val ? "high" : "low"); + return 0; +} + +static int virt_gpio_direction_input(struct gpio_chip *chip, + unsigned offset) +{ + printk("set pin %d as input\n", offset); + return 0; +} + + +static int virt_gpio_get_value(struct gpio_chip *gc, unsigned offset) +{ + int val; + val = (g_gpio_val & (1<dev, sizeof(*g_virt_gpio), GFP_KERNEL); + + /* 2. 设置gpio_chip */ + + /* 2.1 设置函数 */ + g_virt_gpio->label = pdev->name; + g_virt_gpio->direction_output = virt_gpio_direction_output; + g_virt_gpio->direction_input = virt_gpio_direction_input; + g_virt_gpio->get = virt_gpio_get_value; + g_virt_gpio->set = virt_gpio_set_value; + g_virt_gpio->request = gpiochip_generic_request; + + g_virt_gpio->parent = &pdev->dev; + g_virt_gpio->owner = THIS_MODULE; + + /* 2.2 设置base、ngpio值 */ + g_virt_gpio->base = -1; + ret = of_property_read_u32(pdev->dev.of_node, "ngpios", &val); + g_virt_gpio->ngpio = val; + + /* 3. 注册gpio_chip */ + ret = devm_gpiochip_add_data(&pdev->dev, g_virt_gpio, NULL); + + return 0; +} +static int virtual_gpio_remove(struct platform_device *pdev) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + return 0; +} + + +static struct platform_driver virtual_gpio_driver = { + .probe = virtual_gpio_probe, + .remove = virtual_gpio_remove, + .driver = { + .name = "100ask_virtual_gpio", + .of_match_table = of_match_ptr(virtual_gpio_of_match), + } +}; + + +/* 1. 入口函数 */ +static int __init virtual_gpio_init(void) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + /* 1.1 注册一个platform_driver */ + return platform_driver_register(&virtual_gpio_driver); +} + + +/* 2. 出口函数 */ +static void __exit virtual_gpio_exit(void) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + /* 2.1 反注册platform_driver */ + platform_driver_unregister(&virtual_gpio_driver); +} + +module_init(virtual_gpio_init); +module_exit(virtual_gpio_exit); + +MODULE_LICENSE("GPL"); + + diff --git a/IMX6ULL/source/08_Interrupt/03_virtual_int_controller_legacy/Makefile b/IMX6ULL/source/08_Interrupt/03_virtual_int_controller_legacy/Makefile new file mode 100644 index 0000000..588910a --- /dev/null +++ b/IMX6ULL/source/08_Interrupt/03_virtual_int_controller_legacy/Makefile @@ -0,0 +1,21 @@ + +# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR +# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量: +# 2.1 ARCH, 比如: export ARCH=arm64 +# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu- +# 2.3 PATH, 比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin +# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同, +# 请参考各开发板的高级用户使用手册 + +KERN_DIR = /home/book/100ask_imx6ull-sdk/Linux-4.9.88 + +all: + make -C $(KERN_DIR) M=`pwd` modules + +clean: + make -C $(KERN_DIR) M=`pwd` modules clean + rm -rf modules.order + +obj-m += virtual_int_controller.o +obj-m += gpio_key_drv.o + diff --git a/IMX6ULL/source/08_Interrupt/03_virtual_int_controller_legacy/gpio_key_drv.c b/IMX6ULL/source/08_Interrupt/03_virtual_int_controller_legacy/gpio_key_drv.c new file mode 100644 index 0000000..fb6f43c --- /dev/null +++ b/IMX6ULL/source/08_Interrupt/03_virtual_int_controller_legacy/gpio_key_drv.c @@ -0,0 +1,161 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +struct gpio_key{ + int gpio; + struct gpio_desc *gpiod; + int flag; + int irq; +} ; + +static struct gpio_key *gpio_keys_100ask; + +static irqreturn_t gpio_key_isr(int irq, void *dev_id) +{ + struct gpio_key *gpio_key = dev_id; + int val; + val = gpiod_get_value(gpio_key->gpiod); + + + printk("key %d %d\n", gpio_key->gpio, val); + + return IRQ_HANDLED; +} + +/* 1. 从platform_device获得GPIO + * 2. gpio=>irq + * 3. request_irq + */ +static int gpio_key_probe(struct platform_device *pdev) +{ + int err; + struct device_node *node = pdev->dev.of_node; + int count; + int i; + enum of_gpio_flags flag; + unsigned flags = GPIOF_IN; + + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + + count = of_gpio_count(node); + if (!count) + { + printk("%s %s line %d, there isn't any gpio available\n", __FILE__, __FUNCTION__, __LINE__); + return -1; + } + + gpio_keys_100ask = kzalloc(sizeof(struct gpio_key) * count, GFP_KERNEL); + for (i = 0; i < count; i++) + { + gpio_keys_100ask[i].gpio = of_get_gpio_flags(node, i, &flag); + if (gpio_keys_100ask[i].gpio < 0) + { + printk("%s %s line %d, of_get_gpio_flags fail\n", __FILE__, __FUNCTION__, __LINE__); + return -1; + } + gpio_keys_100ask[i].gpiod = gpio_to_desc(gpio_keys_100ask[i].gpio); + gpio_keys_100ask[i].flag = flag & OF_GPIO_ACTIVE_LOW; + + if (flag & OF_GPIO_ACTIVE_LOW) + flags |= GPIOF_ACTIVE_LOW; + + err = devm_gpio_request_one(&pdev->dev, gpio_keys_100ask[i].gpio, flags, NULL); + + + gpio_keys_100ask[i].irq = gpio_to_irq(gpio_keys_100ask[i].gpio); + } + + for (i = 0; i < count; i++) + { + err = request_irq(gpio_keys_100ask[i].irq, gpio_key_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "100ask_gpio_key", &gpio_keys_100ask[i]); + } + + return 0; + +} + +static int gpio_key_remove(struct platform_device *pdev) +{ + //int err; + struct device_node *node = pdev->dev.of_node; + int count; + int i; + + count = of_gpio_count(node); + for (i = 0; i < count; i++) + { + free_irq(gpio_keys_100ask[i].irq, &gpio_keys_100ask[i]); + } + kfree(gpio_keys_100ask); + return 0; +} + + +static const struct of_device_id ask100_keys[] = { + { .compatible = "100ask,gpio_key" }, + { }, +}; + +/* 1. 定义platform_driver */ +static struct platform_driver gpio_keys_driver = { + .probe = gpio_key_probe, + .remove = gpio_key_remove, + .driver = { + .name = "100ask_gpio_key", + .of_match_table = ask100_keys, + }, +}; + +/* 2. 在入口函数注册platform_driver */ +static int __init gpio_key_init(void) +{ + int err; + + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + + err = platform_driver_register(&gpio_keys_driver); + + return err; +} + +/* 3. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数 + * 卸载platform_driver + */ +static void __exit gpio_key_exit(void) +{ + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + + platform_driver_unregister(&gpio_keys_driver); +} + + +/* 7. 其他完善:提供设备信息,自动创建设备节点 */ + +module_init(gpio_key_init); +module_exit(gpio_key_exit); + +MODULE_LICENSE("GPL"); + + diff --git a/IMX6ULL/source/08_Interrupt/03_virtual_int_controller_legacy/virtual_int_controller.c b/IMX6ULL/source/08_Interrupt/03_virtual_int_controller_legacy/virtual_int_controller.c new file mode 100644 index 0000000..29c39fa --- /dev/null +++ b/IMX6ULL/source/08_Interrupt/03_virtual_int_controller_legacy/virtual_int_controller.c @@ -0,0 +1,172 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* FIXME: for gpio_get_value() replace this with direct register read */ +#include +#include +#include +#include +#include + +static struct irq_domain *virtual_intc_domain; + +static int virtual_intc_get_hwirq(void) +{ + return get_random_int() & 0x3; +} + +static void virtual_intc_irq_handler(struct irq_desc *desc) +{ + /* 它的功能时分辨是哪一个hwirq, 调用对应的irq_desc[].handle_irq */ + u32 irq_stat; + int hwirq; + + struct irq_chip *chip = irq_desc_get_chip(desc); + + chained_irq_enter(chip, desc); + + /* a. 分辨中断 */ + hwirq = virtual_intc_get_hwirq(); + + /* b. 调用irq_desc[].handle_irq(handleC) */ + generic_handle_irq(irq_find_mapping(virtual_intc_domain, hwirq)); + + chained_irq_exit(chip, desc); +} + +static void virtual_intc_irq_ack(struct irq_data *data) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); +} + +static void virtual_intc_irq_mask(struct irq_data *data) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); +} + +static void virtual_intc_irq_mask_ack(struct irq_data *data) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); +} + +static void virtual_intc_irq_unmask(struct irq_data *data) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); +} + +static void virtual_intc_irq_eoi(struct irq_data *data) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); +} + +static struct irq_chip virtual_intc_irq_chip = { + .name = "100ask_virtual_intc", + .irq_ack = virtual_intc_irq_ack , + .irq_mask = virtual_intc_irq_mask , + .irq_mask_ack = virtual_intc_irq_mask_ack , + .irq_unmask = virtual_intc_irq_unmask , + .irq_eoi = virtual_intc_irq_eoi , +}; + +static int virtual_intc_irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + /* 1. 给virq提供处理函数 + * 2. 提供irq_chip用来mask/unmask中断 + */ + + irq_set_chip_data(virq, h->host_data); + irq_set_chip_and_handler(virq, &virtual_intc_irq_chip, handle_edge_irq); /* handle_edge_irq就是handleC */ + //irq_set_nested_thread(virq, 1); + //irq_set_noprobe(virq); + + return 0; +} + + +static const struct irq_domain_ops virtual_intc_domain_ops = { + .xlate = irq_domain_xlate_onetwocell, + .map = virtual_intc_irq_map, +}; + +static int virtual_intc_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + int irq_to_parent; + int irq_base; + + /* 1. virutal intc 会向GIC发出n号中断 */ + /* 1.1 从设备树里获得virq_n */ + irq_to_parent = platform_get_irq(pdev, 0); + + + /* 1.2 设置它的irq_desc[].handle_irq, 它的功能时分辨是哪一个hwirq, 调用对应的irq_desc[].handle_irq */ + irq_set_chained_handler_and_data(irq_to_parent, virtual_intc_irq_handler, NULL); + + + /* 2. 分配/设置/注册一个irq_domain */ + irq_base = irq_alloc_descs(-1, 0, 4, numa_node_id()); + + virtual_intc_domain = irq_domain_add_legacy(np, 4, irq_base, 0, + &virtual_intc_domain_ops, NULL); + + return 0; +} +static int virtual_intc_remove(struct platform_device *pdev) +{ + return 0; +} + + + +static const struct of_device_id virtual_intc_of_match[] = { + { .compatible = "100ask,virtual_intc", }, + { }, +}; + + +static 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), + } +}; + + +/* 1. 入口函数 */ +static int __init virtual_intc_init(void) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + /* 1.1 注册一个platform_driver */ + return platform_driver_register(&virtual_intc_driver); +} + + +/* 2. 出口函数 */ +static void __exit virtual_intc_exit(void) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + /* 2.1 反注册platform_driver */ + platform_driver_unregister(&virtual_intc_driver); +} + +module_init(virtual_intc_init); +module_exit(virtual_intc_exit); + +MODULE_LICENSE("GPL"); + + diff --git a/IMX6ULL/source/08_Interrupt/03_virtual_int_controller_legacy/virtual_intc.dts b/IMX6ULL/source/08_Interrupt/03_virtual_int_controller_legacy/virtual_intc.dts new file mode 100644 index 0000000..5d3cafe --- /dev/null +++ b/IMX6ULL/source/08_Interrupt/03_virtual_int_controller_legacy/virtual_intc.dts @@ -0,0 +1,26 @@ + +#define n 100 + +/{ + virtual_intc: virtual_intc_100ask { + .compatible = "100ask,virtual_intc"; + + interrupt-controller; + #interrupt-cells = <2>; + + interrupt-parent = <&intc>; + interrupts = ; + + }; + + gpio_keys_100ask { + compatible = "100ask,gpio_key"; + interrupt-parent = <&virtual_intc>; + interrupts = <0 IRQ_TYPE_LEVEL_HIGH>, + <1 IRQ_TYPE_LEVEL_HIGH>, + <2 IRQ_TYPE_LEVEL_HIGH>, + <3 IRQ_TYPE_LEVEL_HIGH>; + }; + + +}; diff --git a/README.md b/README.md index 6c6ff3d..c3c81b7 100644 --- a/README.md +++ b/README.md @@ -272,13 +272,13 @@ git clone https://e.coding.net/weidongshan/linux/doc_and_source_for_drivers.git 09_中断的硬件框架 10_GIC介绍与编程 ``` + * 2021.06.26 发布"Interrupt子系统" ```shell 11_异常向量表的安装与调用 ``` - * 2021.06.28 发布"Interrupt子系统" ```shell @@ -286,6 +286,13 @@ git clone https://e.coding.net/weidongshan/linux/doc_and_source_for_drivers.git 13_GIC驱动程序分析 ``` +* 2021.07.03 发布"Interrupt子系统" + + ```shell + 14_两类中断控制器处理流程_链式和层级 + 15_链式中断控制器驱动程序编写 + ``` + diff --git a/STM32MP157/doc_pic/08_Interrupt/14_两类中断控制器处理流程_链式和层级.md b/STM32MP157/doc_pic/08_Interrupt/14_两类中断控制器处理流程_链式和层级.md new file mode 100644 index 0000000..5443393 --- /dev/null +++ b/STM32MP157/doc_pic/08_Interrupt/14_两类中断控制器处理流程_链式和层级.md @@ -0,0 +1,109 @@ +## 两类中断控制器处理流程_链式和层级 + +参考资料: + +* [linux kernel的中断子系统之(七):GIC代码分析](http://www.wowotech.net/irq_subsystem/gic_driver.html) + +* Linux 4.9.88内核源码 + + * `Linux-4.9.88\drivers\gpio\gpio-mxc.c` + * `Linux-4.9.88\arch\arm\boot\dts\imx6ull.dtsi` + +* Linux 5.4内核源码 + + * `Linux-5.4\drivers\pinctrl\stm32\pinctrl-stm32mp157.c` + * `Linux-5.4\drivers\irqchip\irq-stm32-exti.c` + * `Linux-5.4\arch\arm\boot\dts\stm32mp151.dtsi` + + + +### 1. 下级中断控制器的类别 + +在后续课程中我们把GIC之下的中断控制器分为两类:链式(chained)、层级(hierarchy)。 + +这个分类并没有官方定义,是我们根据代码概括出来的(Linux内核本来就缺乏文档)。 + +![image-20210703091444149](pic/08_Interrupt/073_two_type_intc.png) + + + +#### 1.1 链式中断控制器(chained) + +上图中,左边的"chained intc"就是链式中断控制器。 + +它底下的4个中断触发时,都会导致GIC的33号中断被触发。 + +处理中断时,需要**分辨**:是谁触发了GIC 33号中断?这需要读取"chained intc"中的寄存器。 + + + +#### 1.2 层级中断控制器(hierarchy) + +上图中,右边边的"hierarchy intc"就是链式中断控制器。 + +它底下的4个中断,跟GIC中的4个中断一一对应。 + +处理GIC 100~103号中断时,不需要读取"hierarchy intc"的寄存器来**分辨**是谁触发了中断。 + + + +### 2. 链式中断控制器的处理流程 + +下图中: + +* handleA、irq_dataA由GIC驱动提供 +* handleB、irq_dataB由GPIO驱动提供 +* handleC也是GPIO驱动提供 + +![image-20210627235754147](pic/08_Interrupt/072_chained_intc.png) + +* 假设GPIO模块下有4个引脚,都可以产生中断,都连接到GIC的33号中断 +* GPIO就是一个链式中断控制器,它底下有4个中断 +* 对于GPIO模块中0~3这四个hwirq,分配四个irq_desc + * 可以一下子分配4个:legacy,老方法 + * 也可以用到时再分配:linear,新方法 +* 假设这4个irq_desc的序号为100~103,在GPIO domain中记录(0,100) (1,101)(2,102) (3,103) +* 对于KEY,注册中断时就是:`request_irq(102, ...)` +* 按下KEY时: + * 程序从GIC中读取寄存器知道发生了33号中断,通过GIC irq_domain可以知道virq为17 + * 处理virq 17号中断:调用irq_desc[17].handle_irq,即handleB + * mask/ack中断: 调用irq_desc[17].irq_data->irq_chip的函数,即irq_dataA + * 细分中断源、处理 + * 读取GPIO寄存器,确定是GPIO里2号引脚发生中断 + * 通过GPIO irq_domain可以知道virq为102 + * 处理virq 102号中断:调用irq_desc[102].handle_irq,即handleC + * mask/ack中断: 调用irq_desc[102].irq_data->irq_chip的函数 + * 调用irq_desc[102].action链表中用户注册的函数 + * unmask中断: 调用irq_desc[102].irq_data->irq_chip的函数 + * unmask中断: 调用irq_desc[17].irq_data->irq_chip的函数 + +### 3. 层级中断控制器的处理流程 + +下图中: + +* handleA、irq_dataA由GIC驱动提供 +* irq_dataB由GPIO驱动提供,不需要handleB + +![image-20210703101721943](pic/08_Interrupt/074_hierarchy_intc.png) + +* 假设GPIO模块下有4个引脚,都可以产生中断,分别链接到GIC的100~103号中断 +* GPIO就是一个层级中断控制器 +* 对于GPIO模块中0~3这四个hwirq,分配四个irq_desc,用到时再分配 +* 假设这4个irq_desc的序号为234~237 + * 在GIC domain中记录(100,234) (101,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的函数 + +### 4. 处理流程对比 + +![image-20210703114531760](pic/08_Interrupt/075_compare_intc.png) \ No newline at end of file diff --git a/STM32MP157/doc_pic/08_Interrupt/14_两类中断控制器处理流程_链式和层级.tif b/STM32MP157/doc_pic/08_Interrupt/14_两类中断控制器处理流程_链式和层级.tif new file mode 100644 index 0000000..b5edd51 Binary files /dev/null and b/STM32MP157/doc_pic/08_Interrupt/14_两类中断控制器处理流程_链式和层级.tif differ diff --git a/STM32MP157/doc_pic/08_Interrupt/15_链式中断控制器驱动程序编写.md b/STM32MP157/doc_pic/08_Interrupt/15_链式中断控制器驱动程序编写.md new file mode 100644 index 0000000..a2d8e49 --- /dev/null +++ b/STM32MP157/doc_pic/08_Interrupt/15_链式中断控制器驱动程序编写.md @@ -0,0 +1,111 @@ +## 链式中断控制器驱动程序编写 + +参考资料: + +* [linux kernel的中断子系统之(七):GIC代码分析](http://www.wowotech.net/irq_subsystem/gic_driver.html) + +* Linux 4.9.88内核源码 + + * `Linux-4.9.88\drivers\gpio\gpio-mxc.c` + * `Linux-4.9.88\arch\arm\boot\dts\imx6ull.dtsi` + +* Linux 5.4内核源码 + + * `Linux-5.4\drivers\pinctrl\stm32\pinctrl-stm32mp157.c` + * `Linux-5.4\drivers\irqchip\irq-stm32-exti.c` + * `Linux-5.4\arch\arm\boot\dts\stm32mp151.dtsi` + + +* 本节视频源码在GIT仓库里 + + ```shell + doc_and_source_for_drivers\IMX6ULL\source\08_Interrupt\03_virtual_int_controller_legacy + doc_and_source_for_drivers\STM32MP157\source\A7\08_Interrupt\03_virtual_int_controller_legacy + ``` + + + +### 1. 链式中断控制器的重要函数和结构体 + +#### 1.1 回顾处理流程 + +为方便描述,假设下级的链式中断控制器就是GPIO控制器。 + +![image-20210630171229980](pic/08_Interrupt/071_importan_function.png) + +沿着中断的处理流程,GIC之下的中断控制器涉及这4个重要部分:handleB、GPIO Domain、handleC、irq_chip + +* **handleB**:处理GIC 33号中断,handleB由GPIO驱动提供 + + * 屏蔽GIC 33号中断:调用irq_dataA的irq_chip的函数,irq_dataA由GIC驱动提供 + * 细分并处理某个GPIO中断: + * 读取GPIO寄存器得到hwirq,通过**GPIO Domain**转换为virq,假设是102 + * 调用irq_desc[102].handle_irq,即handleC + * 清除GIC 33号中断:调用irq_dataA的irq_chip的函数,由GIC驱动提供 + +* **handleC**:处理GPIO 2号中断,handleC由GPIO驱动提供 + + * 屏蔽GPIO 2号中断:调用irq_dataB的**irq_chip**的函数,由GPIO驱动提供 + * 处理:调用actions链表中用户注册的函数 + * 清除GPIO 2号中断:调用irq_dataB的irq_chip的函数,由GPIO驱动提供 + + + + +#### 1.2 irq_domain的核心作用 + +怎么把handleB、GPIO Domain、handleC、irq_chip这4个结构体组织起来,irq_domain是核心。 + +我们从使用中断的流程来讲解。 + +* 在设备树里指定使用哪个中断 + + ```shell + gpio_keys_100ask { + compatible = "100ask,gpio_key"; + interrupt-parent = <&gpio5>; + interrupts = <3 IRQ_TYPE_EDGE_BOTH>, + }; + ``` + +* 内核解析、处理设备树的中断信息 + + * 根据`interrupt-parent`找到驱动程序注册的irq_domain + * 使用irq_domain.ops中的translate或xlate函数解析设备树,得到hwirq和type + + * 分配/找到irq_desc,得到virq + * 把(hwirq, virq)的关系存入irq_domain + * 把virq存入platform_device的resource中 + * 使用irq_domain.ops中的alloc或map函数进行设置 + * 可能是替换irq_desc[virq].handle_irq函数 + * 可能是替换irq_desc[virq].irq_data,里面有irq_chip + +* 用户的驱动程序注册中断 + + * 从platform_device的resource中得到中断号virq + * request_irq(virq, ..., func) + +* 发生中断、处理中断:处理流程见上面。 + + + +### 2. 硬件模型 + + 内核中有各类中断控制器的驱动程序,它们涉及的硬件过于复杂,从这些杂乱的代码中去讲清楚中断体系,比较难。 + + 我们实现一些虚拟的中断控制器,如下图所示。 + + 实际板子中,我们可以通过按键触发中断。 + + 对于这些虚拟的中断控制器,我们没有真实按键,通过devmem指令写GIC的PENDING寄存器触发中断。 + + ![image-20210703133953035](pic/08_Interrupt/076_virtual_intc_hardware.png) + + + +### 3. 编程 + +会涉及2个驱动程序:虚拟的中断控制器驱动程序,按键驱动程序,以及对应的设备树。 + + + diff --git a/STM32MP157/doc_pic/08_Interrupt/pic/08_Interrupt/071_importan_function.png b/STM32MP157/doc_pic/08_Interrupt/pic/08_Interrupt/071_importan_function.png new file mode 100644 index 0000000..f478849 Binary files /dev/null and b/STM32MP157/doc_pic/08_Interrupt/pic/08_Interrupt/071_importan_function.png differ diff --git a/STM32MP157/doc_pic/08_Interrupt/pic/08_Interrupt/072_chained_intc.png b/STM32MP157/doc_pic/08_Interrupt/pic/08_Interrupt/072_chained_intc.png new file mode 100644 index 0000000..dbd58e6 Binary files /dev/null and b/STM32MP157/doc_pic/08_Interrupt/pic/08_Interrupt/072_chained_intc.png differ diff --git a/STM32MP157/doc_pic/08_Interrupt/pic/08_Interrupt/073_two_type_intc.png b/STM32MP157/doc_pic/08_Interrupt/pic/08_Interrupt/073_two_type_intc.png new file mode 100644 index 0000000..c24b19a Binary files /dev/null and b/STM32MP157/doc_pic/08_Interrupt/pic/08_Interrupt/073_two_type_intc.png differ diff --git a/STM32MP157/doc_pic/08_Interrupt/pic/08_Interrupt/074_hierarchy_intc.png b/STM32MP157/doc_pic/08_Interrupt/pic/08_Interrupt/074_hierarchy_intc.png new file mode 100644 index 0000000..d270628 Binary files /dev/null and b/STM32MP157/doc_pic/08_Interrupt/pic/08_Interrupt/074_hierarchy_intc.png differ diff --git a/STM32MP157/doc_pic/08_Interrupt/pic/08_Interrupt/075_compare_intc.png b/STM32MP157/doc_pic/08_Interrupt/pic/08_Interrupt/075_compare_intc.png new file mode 100644 index 0000000..ad004d1 Binary files /dev/null and b/STM32MP157/doc_pic/08_Interrupt/pic/08_Interrupt/075_compare_intc.png differ diff --git a/STM32MP157/doc_pic/08_Interrupt/pic/08_Interrupt/076_virtual_intc_hardware.png b/STM32MP157/doc_pic/08_Interrupt/pic/08_Interrupt/076_virtual_intc_hardware.png new file mode 100644 index 0000000..fa02edf Binary files /dev/null and b/STM32MP157/doc_pic/08_Interrupt/pic/08_Interrupt/076_virtual_intc_hardware.png differ diff --git a/STM32MP157/source/A7/08_Interrupt/03_virtual_int_controller_legacy/Makefile b/STM32MP157/source/A7/08_Interrupt/03_virtual_int_controller_legacy/Makefile new file mode 100644 index 0000000..588910a --- /dev/null +++ b/STM32MP157/source/A7/08_Interrupt/03_virtual_int_controller_legacy/Makefile @@ -0,0 +1,21 @@ + +# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR +# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量: +# 2.1 ARCH, 比如: export ARCH=arm64 +# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu- +# 2.3 PATH, 比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin +# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同, +# 请参考各开发板的高级用户使用手册 + +KERN_DIR = /home/book/100ask_imx6ull-sdk/Linux-4.9.88 + +all: + make -C $(KERN_DIR) M=`pwd` modules + +clean: + make -C $(KERN_DIR) M=`pwd` modules clean + rm -rf modules.order + +obj-m += virtual_int_controller.o +obj-m += gpio_key_drv.o + diff --git a/STM32MP157/source/A7/08_Interrupt/03_virtual_int_controller_legacy/gpio_key_drv.c b/STM32MP157/source/A7/08_Interrupt/03_virtual_int_controller_legacy/gpio_key_drv.c new file mode 100644 index 0000000..fb6f43c --- /dev/null +++ b/STM32MP157/source/A7/08_Interrupt/03_virtual_int_controller_legacy/gpio_key_drv.c @@ -0,0 +1,161 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +struct gpio_key{ + int gpio; + struct gpio_desc *gpiod; + int flag; + int irq; +} ; + +static struct gpio_key *gpio_keys_100ask; + +static irqreturn_t gpio_key_isr(int irq, void *dev_id) +{ + struct gpio_key *gpio_key = dev_id; + int val; + val = gpiod_get_value(gpio_key->gpiod); + + + printk("key %d %d\n", gpio_key->gpio, val); + + return IRQ_HANDLED; +} + +/* 1. 从platform_device获得GPIO + * 2. gpio=>irq + * 3. request_irq + */ +static int gpio_key_probe(struct platform_device *pdev) +{ + int err; + struct device_node *node = pdev->dev.of_node; + int count; + int i; + enum of_gpio_flags flag; + unsigned flags = GPIOF_IN; + + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + + count = of_gpio_count(node); + if (!count) + { + printk("%s %s line %d, there isn't any gpio available\n", __FILE__, __FUNCTION__, __LINE__); + return -1; + } + + gpio_keys_100ask = kzalloc(sizeof(struct gpio_key) * count, GFP_KERNEL); + for (i = 0; i < count; i++) + { + gpio_keys_100ask[i].gpio = of_get_gpio_flags(node, i, &flag); + if (gpio_keys_100ask[i].gpio < 0) + { + printk("%s %s line %d, of_get_gpio_flags fail\n", __FILE__, __FUNCTION__, __LINE__); + return -1; + } + gpio_keys_100ask[i].gpiod = gpio_to_desc(gpio_keys_100ask[i].gpio); + gpio_keys_100ask[i].flag = flag & OF_GPIO_ACTIVE_LOW; + + if (flag & OF_GPIO_ACTIVE_LOW) + flags |= GPIOF_ACTIVE_LOW; + + err = devm_gpio_request_one(&pdev->dev, gpio_keys_100ask[i].gpio, flags, NULL); + + + gpio_keys_100ask[i].irq = gpio_to_irq(gpio_keys_100ask[i].gpio); + } + + for (i = 0; i < count; i++) + { + err = request_irq(gpio_keys_100ask[i].irq, gpio_key_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "100ask_gpio_key", &gpio_keys_100ask[i]); + } + + return 0; + +} + +static int gpio_key_remove(struct platform_device *pdev) +{ + //int err; + struct device_node *node = pdev->dev.of_node; + int count; + int i; + + count = of_gpio_count(node); + for (i = 0; i < count; i++) + { + free_irq(gpio_keys_100ask[i].irq, &gpio_keys_100ask[i]); + } + kfree(gpio_keys_100ask); + return 0; +} + + +static const struct of_device_id ask100_keys[] = { + { .compatible = "100ask,gpio_key" }, + { }, +}; + +/* 1. 定义platform_driver */ +static struct platform_driver gpio_keys_driver = { + .probe = gpio_key_probe, + .remove = gpio_key_remove, + .driver = { + .name = "100ask_gpio_key", + .of_match_table = ask100_keys, + }, +}; + +/* 2. 在入口函数注册platform_driver */ +static int __init gpio_key_init(void) +{ + int err; + + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + + err = platform_driver_register(&gpio_keys_driver); + + return err; +} + +/* 3. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数 + * 卸载platform_driver + */ +static void __exit gpio_key_exit(void) +{ + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + + platform_driver_unregister(&gpio_keys_driver); +} + + +/* 7. 其他完善:提供设备信息,自动创建设备节点 */ + +module_init(gpio_key_init); +module_exit(gpio_key_exit); + +MODULE_LICENSE("GPL"); + + diff --git a/STM32MP157/source/A7/08_Interrupt/03_virtual_int_controller_legacy/virtual_int_controller.c b/STM32MP157/source/A7/08_Interrupt/03_virtual_int_controller_legacy/virtual_int_controller.c new file mode 100644 index 0000000..29c39fa --- /dev/null +++ b/STM32MP157/source/A7/08_Interrupt/03_virtual_int_controller_legacy/virtual_int_controller.c @@ -0,0 +1,172 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* FIXME: for gpio_get_value() replace this with direct register read */ +#include +#include +#include +#include +#include + +static struct irq_domain *virtual_intc_domain; + +static int virtual_intc_get_hwirq(void) +{ + return get_random_int() & 0x3; +} + +static void virtual_intc_irq_handler(struct irq_desc *desc) +{ + /* 它的功能时分辨是哪一个hwirq, 调用对应的irq_desc[].handle_irq */ + u32 irq_stat; + int hwirq; + + struct irq_chip *chip = irq_desc_get_chip(desc); + + chained_irq_enter(chip, desc); + + /* a. 分辨中断 */ + hwirq = virtual_intc_get_hwirq(); + + /* b. 调用irq_desc[].handle_irq(handleC) */ + generic_handle_irq(irq_find_mapping(virtual_intc_domain, hwirq)); + + chained_irq_exit(chip, desc); +} + +static void virtual_intc_irq_ack(struct irq_data *data) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); +} + +static void virtual_intc_irq_mask(struct irq_data *data) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); +} + +static void virtual_intc_irq_mask_ack(struct irq_data *data) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); +} + +static void virtual_intc_irq_unmask(struct irq_data *data) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); +} + +static void virtual_intc_irq_eoi(struct irq_data *data) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); +} + +static struct irq_chip virtual_intc_irq_chip = { + .name = "100ask_virtual_intc", + .irq_ack = virtual_intc_irq_ack , + .irq_mask = virtual_intc_irq_mask , + .irq_mask_ack = virtual_intc_irq_mask_ack , + .irq_unmask = virtual_intc_irq_unmask , + .irq_eoi = virtual_intc_irq_eoi , +}; + +static int virtual_intc_irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + /* 1. 给virq提供处理函数 + * 2. 提供irq_chip用来mask/unmask中断 + */ + + irq_set_chip_data(virq, h->host_data); + irq_set_chip_and_handler(virq, &virtual_intc_irq_chip, handle_edge_irq); /* handle_edge_irq就是handleC */ + //irq_set_nested_thread(virq, 1); + //irq_set_noprobe(virq); + + return 0; +} + + +static const struct irq_domain_ops virtual_intc_domain_ops = { + .xlate = irq_domain_xlate_onetwocell, + .map = virtual_intc_irq_map, +}; + +static int virtual_intc_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + int irq_to_parent; + int irq_base; + + /* 1. virutal intc 会向GIC发出n号中断 */ + /* 1.1 从设备树里获得virq_n */ + irq_to_parent = platform_get_irq(pdev, 0); + + + /* 1.2 设置它的irq_desc[].handle_irq, 它的功能时分辨是哪一个hwirq, 调用对应的irq_desc[].handle_irq */ + irq_set_chained_handler_and_data(irq_to_parent, virtual_intc_irq_handler, NULL); + + + /* 2. 分配/设置/注册一个irq_domain */ + irq_base = irq_alloc_descs(-1, 0, 4, numa_node_id()); + + virtual_intc_domain = irq_domain_add_legacy(np, 4, irq_base, 0, + &virtual_intc_domain_ops, NULL); + + return 0; +} +static int virtual_intc_remove(struct platform_device *pdev) +{ + return 0; +} + + + +static const struct of_device_id virtual_intc_of_match[] = { + { .compatible = "100ask,virtual_intc", }, + { }, +}; + + +static 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), + } +}; + + +/* 1. 入口函数 */ +static int __init virtual_intc_init(void) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + /* 1.1 注册一个platform_driver */ + return platform_driver_register(&virtual_intc_driver); +} + + +/* 2. 出口函数 */ +static void __exit virtual_intc_exit(void) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + /* 2.1 反注册platform_driver */ + platform_driver_unregister(&virtual_intc_driver); +} + +module_init(virtual_intc_init); +module_exit(virtual_intc_exit); + +MODULE_LICENSE("GPL"); + + diff --git a/STM32MP157/source/A7/08_Interrupt/03_virtual_int_controller_legacy/virtual_intc.dts b/STM32MP157/source/A7/08_Interrupt/03_virtual_int_controller_legacy/virtual_intc.dts new file mode 100644 index 0000000..5d3cafe --- /dev/null +++ b/STM32MP157/source/A7/08_Interrupt/03_virtual_int_controller_legacy/virtual_intc.dts @@ -0,0 +1,26 @@ + +#define n 100 + +/{ + virtual_intc: virtual_intc_100ask { + .compatible = "100ask,virtual_intc"; + + interrupt-controller; + #interrupt-cells = <2>; + + interrupt-parent = <&intc>; + interrupts = ; + + }; + + gpio_keys_100ask { + compatible = "100ask,gpio_key"; + interrupt-parent = <&virtual_intc>; + interrupts = <0 IRQ_TYPE_LEVEL_HIGH>, + <1 IRQ_TYPE_LEVEL_HIGH>, + <2 IRQ_TYPE_LEVEL_HIGH>, + <3 IRQ_TYPE_LEVEL_HIGH>; + }; + + +};