首页 > 代码库 > 基于设备树的TQ2440的中断(1)
基于设备树的TQ2440的中断(1)
作者
姓名:彭东林
E-mail:pengdonglin137@163.com
QQ:405728433
平台
板子:TQ2440
内核:Linux-4.9
u-boot: 2015.04
工具链: arm-none-linux-gnueabi-gcc 4.8.3
概述
在博文讓TQ2440也用上設備樹(1)将支持devicetree的Linux4.9移植到了tq2440上面,而在基於tiny4412的Linux內核移植 --- 实例学习中断背后的知识(1)中介绍了最新的Linux下中断的知识,下面我们再结合TQ2440来分析一下。
正文
一、基础知识
关于这部分请参考S3C2440的芯片手册或者博文TQ2440中断系统
下面简单介绍:
从图中可以看到,中断主要分为两级,我们可以理解为是两个中断控制器的嵌套或者级联。S3C2440总共支持60个中断源,包含了主中断源和子中断源。
寄存器功能介绍:
1、SRCPND 地址: 0x4A000000 功能:每一位代表一个主中断,置1表示有对应的主中断请求,对应位写入1可以清除中断
2、INTMOD 地址: 0x4A000004 功能:设置对应的主中断为IRQ还是FIQ, 置1表示FIQ
3、INTMSK 地址: 0x4A000008 功能:置1表示对应的主中断被屏蔽(不会影响SRCPND)
4、INTPND 地址: 0x4A000010 功能:表示对应的主中断被request,只可能有一位被置位,写入1可以清除中断
5、INTOFFSET 地址:0x4A000014 功能:存放的是发生中断请求的主中断号
6、SUBSRCPND 地址:0x4A000018 功能:每一位代表一个子中断,置一表示对应子中断请求,对应位写入1清除子中断请求
7、INTSUBMSK 地址:0x4A00001C 功能:置1表示对应的子中断被屏蔽
32个主中断:
15个子中断:
外部中断:
EINT0~7对应的GPIO是GPF0~7
EINT8~23对应的GPIO是GPG0~15
二、设备树
1、中断控制器
intc:interrupt-controller@4a000000 {compatible = "samsung,s3c2410-irq";reg = <0x4a000000 0x100>;interrupt-controller;#interrupt-cells = <4>;};
serial@50000000 {compatible = "samsung,s3c2440-uart";reg = <0x50000000 0x4000>;interrupts = <1 28 0 4>, <1 28 1 4>;status = "okay";clock-names = "uart";clocks = <&clock PCLK_UART0>;pinctrl-names = "default";pinctrl-0 = <0x3>;}; i2c:i2c@54000000 {compatible = "samsung,s3c2410-i2c";reg = <0x54000000 0x100>;interrupts = <0 0 27 3>;#address-cells = <1>;#size-cells = <0>;};
1 /* Translate our of irq notation 2 * format: <ctrl_num ctrl_irq parent_irq type> 3 */ 4 static int s3c24xx_irq_xlate_of(struct irq_domain *d, struct device_node *n, 5 const u32 *intspec, unsigned int intsize, 6 irq_hw_number_t *out_hwirq, unsigned int *out_type) 7 { 8 struct s3c_irq_intc *intc; 9 struct s3c_irq_intc *parent_intc;10 struct s3c_irq_data *irq_data;11 struct s3c_irq_data *parent_irq_data;12 int irqno;13 14 if (WARN_ON(intsize < 4)) //如果参数个数不能小于415 return -EINVAL;16 // 从这里知道,第一个参数不能大于2,只能是0和1, 0表示主中断,2表示子中断17 if (intspec[0] > 2 || !s3c_intc[intspec[0]]) {18 pr_err("controller number %d invalid\n", intspec[0]);19 return -EINVAL;20 }21 // s3c_intc[0]表示主中断控制器,s3c_intc[1]表示子中断控制器22 intc = s3c_intc[intspec[0]];23 24 // 第三个参数表示的是硬件中断号,从这里知道,主中断的硬件中断是0~31,子中断的硬件中断是32及其以上25 *out_hwirq = intspec[0] * 32 + intspec[2];26 // 第四个参数表示的是中断类型,可以查看IRQ_TYPE_SENSE_MASK定义,就知道含义:27 // 1表示上升沿触发,2表示下降沿触发,3表示双边沿触发,4表示高电平触发,8表示低电平触发,12表示高低电平触发28 *out_type = intspec[3] & IRQ_TYPE_SENSE_MASK;29 // 如果是主中断,则intc->parent为NULL, 否则非空30 parent_intc = intc->parent;31 if (parent_intc) { // 子中断32 irq_data = http://www.mamicode.com/&intc->irqs[intspec[2]];33 // 对于子中断,第二个参数才有意义,表示该子中断所隶属的主中断的硬件中断号34 irq_data->parent_irq = intspec[1];35 parent_irq_data = http://www.mamicode.com/&parent_intc->irqs[irq_data->parent_irq];36 parent_irq_data->sub_intc = intc;37 // sub_bits中记录该主中断下的子中断被被使用的情况38 parent_irq_data->sub_bits |= (1UL << intspec[2]);39 // 将主中断号映射成虚拟中断号40 /* parent_intc is always s3c_intc[0], so no offset */41 irqno = irq_create_mapping(parent_intc->domain, intspec[1]);42 if (irqno < 0) {43 pr_err("irq: could not map parent interrupt\n");44 return irqno;45 }46 // 这里设置irqno对应的irq_desc的handle_irq为s3c_irq_demux47 // 从函数名称中就可以看出, 这个函数会再次进行检测该主中断的哪个子中断被请求48 irq_set_chained_handler(irqno, s3c_irq_demux);49 }50 51 return 0;52 }
从这里我们知道,interrupts属性中的四个参数中的含义:
三、中断控制器驱动
1 static struct s3c24xx_irq_of_ctrl s3c2410_ctrl[] = { 2 { // 主中断 3 .name = "intc", 4 .offset = 0, 5 }, { // 子中断 6 .name = "subintc", 7 .offset = 0x18, // 寄存器地址偏移 8 .parent = &s3c_intc[0], 9 }10 };11 12 int __init s3c2410_init_intc_of(struct device_node *np,13 struct device_node *interrupt_parent)14 {15 return s3c_init_intc_of(np, interrupt_parent,16 s3c2410_ctrl, ARRAY_SIZE(s3c2410_ctrl));17 }18 IRQCHIP_DECLARE(s3c2410_irq, "samsung,s3c2410-irq", s3c2410_init_intc_of);
在内核启动的时候,函数s3c2410_init_intc_of会被调用。
1 static int __init s3c_init_intc_of(struct device_node *np, 2 struct device_node *interrupt_parent, 3 struct s3c24xx_irq_of_ctrl *s3c_ctrl, int num_ctrl) 4 { 5 struct s3c_irq_intc *intc; 6 struct s3c24xx_irq_of_ctrl *ctrl; 7 struct irq_domain *domain; 8 void __iomem *reg_base; 9 int i;10 11 // 对中断控制器的reg属性所表示地址空间进行映射12 reg_base = of_iomap(np, 0);13 14 // 为该中断控制器创建irq_domain,一共支持64个中断,对应的irq_domain_ops是s3c24xx_irq_ops_of15 domain = irq_domain_add_linear(np, num_ctrl * 32,16 &s3c24xx_irq_ops_of, NULL);17 // 依次处理两个中断控制器18 for (i = 0; i < num_ctrl; i++) {19 ctrl = &s3c_ctrl[i];20 21 pr_debug("irq: found controller %s\n", ctrl->name);22 // 主和子中断控制器各分配一个s3c_irq_intc23 intc = kzalloc(sizeof(struct s3c_irq_intc), GFP_KERNEL);24 // 主和子中断控制器共享一个irq_domain25 intc->domain = domain;26 // 为主和子中断控制器下的每个中断各分配一个s3c_irq_data结构体27 intc->irqs = kzalloc(sizeof(struct s3c_irq_data) * 32,28 GFP_KERNEL);29 30 if (ctrl->parent) { // 子中断控制器31 intc->reg_pending = reg_base + ctrl->offset; // SUBSRCPND32 intc->reg_mask = reg_base + ctrl->offset + 0x4; // INTSUBMSK33 34 // 由于先处理的是s3c2410_ctrl[0],所以在处理子中断的时候,*(ctrl->parent)非空35 // 是主中断控制器对应的s3c_irq_intc36 if (*(ctrl->parent)) {37 intc->parent = *(ctrl->parent);38 } else {39 pr_warn("irq: parent of %s missing\n",40 ctrl->name);41 kfree(intc->irqs);42 kfree(intc);43 continue;44 }45 } else { // 主中断控制器46 intc->reg_pending = reg_base + ctrl->offset; //SRCPND47 intc->reg_mask = reg_base + ctrl->offset + 0x08; //INTMSK48 intc->reg_intpnd = reg_base + ctrl->offset + 0x10; //INTPND49 }50 51 s3c24xx_clear_intc(intc); // 清除中断52 s3c_intc[i] = intc;53 }54 // 将handle_arch_irq设置为s3c24xx_handle_irq, 这样在发生中断后,会从汇编entry-armv.S中55 // 调转到s3c24xx_handle_irq56 set_handle_irq(s3c24xx_handle_irq);57 58 return 0;59 }
中断控制启动的初始化,结构体s3c24xx_irq_ops_of中的map和xlate还没有分析,放到下面分析。
四、interrupts属性的解析
---> of_platform_populate
---> irq_of_parse_and_map
---> of_irq_parse_one
---> irq_create_of_mapping
---> irq_create_fwspec_mapping
---> irq_domain_translate // 解析参数
---> s3c24xx_irq_xlate_of
---> irq_create_mapping // 创建hwirq到virq的映射
---> irq_domain_associate
---> s3c24xx_irq_map_of
详细分析请参考:基於tiny4412的Linux內核移植 --- 实例学习中断背后的知识(1)
这里我们只看看s3c24xx_irq_map_of。
在调用这个函数时,已经创建号hwirq对应的virq了:
1 static int s3c24xx_irq_map_of(struct irq_domain *h, unsigned int virq, 2 irq_hw_number_t hw) 3 { 4 unsigned int ctrl_num = hw / 32; //判断该hwirq属于主还是子中断控制器 5 unsigned int intc_hw = hw % 32; // 得到跟寄存器对应的硬件中断号 6 struct s3c_irq_intc *intc = s3c_intc[ctrl_num]; // 获得中断控制器对应的结构体 7 struct s3c_irq_intc *parent_intc = intc->parent; 8 struct s3c_irq_data *irq_data = http://www.mamicode.com/&intc->irqs[intc_hw]; // 每个中断都会有一个s3c_irq_data结构体 9 10 /* attach controller pointer to irq_data */11 irq_data->intc = intc;12 irq_data->offset = intc_hw; //跟硬件寄存器对应的硬件中断号,也就是对应的位号13 14 // 将virq对应的irq_desc的handle_irq初始化为handle_edge_irq,15 // 下面在s3c_irq_type中会根据中断触发类型再次修改16 if (!parent_intc) // 主中断控制器17 irq_set_chip_and_handler(virq, &s3c_irq_chip, handle_edge_irq);18 else // 子中断控制器19 irq_set_chip_and_handler(virq, &s3c_irq_level_chip,20 handle_edge_irq);21 22 irq_set_chip_data(virq, irq_data);23 24 return 0;25 }
上面主和子中断控制虽然使用的时同一个irq domain但是对应的irq_chip却是各自的。
1 static struct irq_chip s3c_irq_chip = {2 .name= "s3c",3 .irq_ack= s3c_irq_ack, // 清除中断4 .irq_mask= s3c_irq_mask, // 屏蔽中断5 .irq_unmask= s3c_irq_unmask, // 打开中断6 .irq_set_type= s3c_irq_type, //根据不同的触发类型设置不同的处理函数7 .irq_set_wake= s3c_irq_wake // 8 };
子irq_chip:
1 static struct irq_chip s3c_irq_level_chip = {2 .name= "s3c-level",3 .irq_mask= s3c_irq_mask,4 .irq_unmask= s3c_irq_unmask,5 .irq_ack= s3c_irq_ack,6 .irq_set_type= s3c_irq_type,7 };
在s3c_irq_mask中屏蔽中断的时候,如果屏蔽的是子中断,还需要判断目前该子中断所隶属的主中断是不是还有其他子中断在使用,如果没有的话,也会把该主中断也给屏蔽了。
同样,s3c_irq_unmask在打开子中断时也会将其所隶属的主中断也打开。
五、子中断分发s3c_irq_demux
在含有子中断的主中断被触发后,会执行主中断对应的virq的irq_desc的handle_irq,也就是这里的s3c_irq_demux:
1 static void s3c_irq_demux(struct irq_desc *desc) // 这里的desc是主中断的virq的irq_desc 2 { 3 struct irq_chip *chip = irq_desc_get_chip(desc); //获得主中断的irq_chip 4 struct s3c_irq_data *irq_data =http://www.mamicode.com/ irq_desc_get_chip_data(desc); 5 struct s3c_irq_intc *intc = irq_data->intc; // 参考s3c24xx_irq_xlate_of函数 6 struct s3c_irq_intc *sub_intc = irq_data->sub_intc; // 参考s3c24xx_irq_xlate_of函数 7 unsigned int n, offset, irq; 8 unsigned long src, msk; 9 10 /* we‘re using individual domains for the non-dt case11 * and one big domain for the dt case where the subintc12 * starts at hwirq number 32.13 */14 // 由于我们使用的是设备树,所以这里的offset是32,也就是子中断的起始号15 offset = irq_domain_get_of_node(intc->domain) ? 32 : 0;16 17 // 屏蔽该主中断,并清中断18 chained_irq_enter(chip, desc);19 // 读取子中断的pending寄存器,看那些子中断被触发了20 src = http://www.mamicode.com/readl_relaxed(sub_intc->reg_pending);21 msk = readl_relaxed(sub_intc->reg_mask);22 23 src &= ~msk; // 去掉被屏蔽的24 src &= irq_data->sub_bits; // 去掉没有初始化的25 // 此时src中存放的就是将要被处理的子中断,下面会一一处理26 while (src) {27 n = __ffs(src);28 src &= ~(1 << n);29 irq = irq_find_mapping(sub_intc->domain, offset + n); //根据hwirq找到virq30 generic_handle_irq(irq); // 处理31 }32 // 打开该主中断33 chained_irq_exit(chip, desc);34 }
六、设备驱动申请中断
1、串口驱动
drivers/tty/serial/samsung.c
函数调用:
s3c24xx_serial_probe
---> s3c24xx_serial_init_port
在函数s3c24xx_serial_init_port会申请中断
ret = platform_get_irq(platdev, 0); // UART0 receive interrupt
ret = platform_get_irq(platdev, 1); // UART0 transmit interrupt
2、I2C控制器
drivers/i2c/busses/i2c-s3c2410.c
在函数s3c24xx_i2c_probe中会申请中断:
i2c->irq = ret = platform_get_irq(pdev, 0)
七、查看系统信息
开机后,可以从/proc/interrupts中看到当前的中断资源申请信息:
[root@tq2440 ]# cat /proc/interrupts CPU0 7: 926 s3c-eint 7 Edge eth0 8: 0 s3c 8 Edge s3c2410-rtc tick 13: 567308 s3c 13 Edge samsung_time_irq 26: 0 s3c 26 Edge ohci_hcd:usb1 27: 4 s3c 27 Edge 54000000.i2c //I2C 30: 0 s3c 30 Edge s3c2410-rtc alarm 32: 53 s3c-level 32 Level 50000000.serial //UART0 RXD 33: 230 s3c-level 33 Level 50000000.serial //UART0 TXD 59: 0 s3c-level 59 Edge 53000000.watchdog
[ 1.851407] [<c035004c>] (s3c24xx_i2c_irq) from [<c00447f0>] (__handle_irq_event_percpu+0x3c/0x130)[ 1.851457] [<c00447f0>] (__handle_irq_event_percpu) from [<c0044900>] (handle_irq_event_percpu+0x1c/0x54)[ 1.851490] [<c0044900>] (handle_irq_event_percpu) from [<c0044960>] (handle_irq_event+0x28/0x3c)[ 1.851526] [<c0044960>] (handle_irq_event) from [<c00476c0>] (handle_edge_irq+0xbc/0x190)[ 1.851558] [<c00476c0>] (handle_edge_irq) from [<c00441c4>] (__handle_domain_irq+0x6c/0xcc)[ 1.851589] [<c00441c4>] (__handle_domain_irq) from [<c0009444>] (s3c24xx_handle_irq+0x6c/0x12c)[ 1.851622] [<c0009444>] (s3c24xx_handle_irq) from [<c000e5fc>] (__irq_svc+0x5c/0x78)
[ 2.393176] [<c0293410>] (s3c24xx_serial_tx_chars) from [<c00447f0>] (__handle_irq_event_percpu+0x3c/0x130)[ 2.393218] [<c00447f0>] (__handle_irq_event_percpu) from [<c0044900>] (handle_irq_event_percpu+0x1c/0x54)[ 2.393249] [<c0044900>] (handle_irq_event_percpu) from [<c0044960>] (handle_irq_event+0x28/0x3c)[ 2.393283] [<c0044960>] (handle_irq_event) from [<c004740c>] (handle_level_irq+0x90/0x114)[ 2.393312] [<c004740c>] (handle_level_irq) from [<c0043fd8>] (generic_handle_irq+0x2c/0x40)[ 2.393379] [<c0043fd8>] (generic_handle_irq) from [<c0242868>] (s3c_irq_demux+0xc8/0x140)[ 2.393424] [<c0242868>] (s3c_irq_demux) from [<c00441c4>] (__handle_domain_irq+0x6c/0xcc)[ 2.393460] [<c00441c4>] (__handle_domain_irq) from [<c0009444>] (s3c24xx_handle_irq+0x6c/0x12c)[ 2.393492] [<c0009444>] (s3c24xx_handle_irq) from [<c000e5fc>] (__irq_svc+0x5c/0x78)
[ 9.223769] [<c0294570>] (s3c24xx_serial_rx_chars) from [<c00447f0>] (__handle_irq_event_percpu+0x3c/0x130)[ 9.223817] [<c00447f0>] (__handle_irq_event_percpu) from [<c0044900>] (handle_irq_event_percpu+0x1c/0x54)[ 9.223851] [<c0044900>] (handle_irq_event_percpu) from [<c0044960>] (handle_irq_event+0x28/0x3c)[ 9.223883] [<c0044960>] (handle_irq_event) from [<c004740c>] (handle_level_irq+0x90/0x114)[ 9.223915] [<c004740c>] (handle_level_irq) from [<c0043fd8>] (generic_handle_irq+0x2c/0x40)[ 9.223978] [<c0043fd8>] (generic_handle_irq) from [<c0242868>] (s3c_irq_demux+0xc8/0x140)[ 9.224017] [<c0242868>] (s3c_irq_demux) from [<c00441c4>] (__handle_domain_irq+0x6c/0xcc)[ 9.224050] [<c00441c4>] (__handle_domain_irq) from [<c0009444>] (s3c24xx_handle_irq+0x6c/0x12c)[ 9.224081] [<c0009444>] (s3c24xx_handle_irq) from [<c000e5fc>] (__irq_svc+0x5c/0x78)
完。
基于设备树的TQ2440的中断(1)