历史上的今天
今天是:2025年02月16日(星期日)
2020年02月16日 | Exynos4412的外部中断是如何安排的?
2020-02-16 来源:eefocus
平台
Linux4.9
tiny4412
概述
结合tiny4412开发板分析一下Exynos4412的外部中断是如何组织的。
在Exynos4412的用户手册第9章Interrupt Controller列出了支持的外部中断:

第1列是按Shared Peripheral Interrupt 排序的
第2列是按Software Generated Interrupt + Peripheral Interrupt(PPI+SPI)排序的, 目前GIC提供了16个SGI中断和16个PPI中断
从上面可以看到,硬件上提供了32个外部中断,但是我们在第6章的GPIO Control一节说:

图2
上面说,有172个外部中断以及32个外部可唤醒中断,那么这些GPIO控制器提供的中断是如何跟GIC提供的那32个外部中断对应的呢?
其实上面图1中列出的从SPI-16到SPI-32仅仅对应的是图2中说的那32个外部可唤醒中断,而剩下的那172个会使用其他的SPI中断,并不是图1列出的那几个。
下面是结合设备树和GIC以及Combiner驱动得到的:

图3
上面图3列出的那些组GPIO会最终会共享SPI-47这个SPI中断,图3的EXT_INTxx跟图1的EINT没有任何关系,仅仅表示一种GPIO功能复用,而且这些中断是不具备wake up功能的。上面是硬件连接,那么在驱动层面是处理的呢?
这里我们需要关注下面两个驱动文件:
drivers/pinctrl/samsung/pinctrl-exynos.c
drivers/pinctrl/samsung/pinctrl-samsung.c
在populate的时候,SPI-47这个硬件中断会被映射为对应的虚拟中断(如virq_47),函数samsung_pinctrl_probe会获得virq_47:
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res)
drvdata->irq = res->start;
在执行ctrl->eint_gpio_init(drvdata)的时候将drvdata传入,在函数exynos_eint_gpio_init中,会注册virq_47的中断处理函数:
ret = devm_request_irq(dev, d->irq, exynos_eint_gpio_irq,
0, dev_name(dev), d);
也就是当GIC上的SPI-47这个中断被触发后,中断处理函数exynos_eint_gpio_irq会被调用。
然后就是对图3中的每一组GPIO都注册对应的irq_domain:
1 bank = d->pin_banks;
2 for (i = 0; i < d->nr_banks; ++i, ++bank) {
3 if (bank->eint_type != EINT_TYPE_GPIO)
4 continue;
5
6 bank->irq_domain = irq_domain_add_linear(bank->of_node,
7 bank->nr_pins, &exynos_eint_irqd_ops, bank);
8
9 bank->irq_chip = &exynos_gpio_irq_chip;
10 }
也就是图3中的每一组GPIO都可以是一个中断控制器,bank->nr_pins是该组GPIO有几个引脚,每一个引脚对应一个中断,所以也就表示该irq_domain可以最大支持的中断个数。第9行设置了irq_chip,用户控制每一个引脚中断的打开、关闭、清除等。
下面以GPA0_0这个引脚触发中断为例简单看看是如何处理的?
GPA1_5产生中断后,会在SPI-47上也引发中断,函数exynos_eint_gpio_irq会被调用,其中会查询到产生的中断的引脚,然后在对应的引脚所属的irq_domain中查询引脚对应的虚拟中断,最后执行到用户注册的中断处理函数。
1 static irqreturn_t exynos_eint_gpio_irq(int irq, void *data)
2 {
3 struct samsung_pinctrl_drv_data *d = data;
4 struct samsung_pin_bank *bank = d->pin_banks;
5 unsigned int svc, group, pin, virq;
6
7 svc = readl(d->virt_base + EXYNOS_SVC_OFFSET);
8 group = EXYNOS_SVC_GROUP(svc);
9 pin = svc & EXYNOS_SVC_NUM_MASK;
10
11 if (!group)
12 return IRQ_HANDLED;
13 bank += (group - 1);
14
15 virq = irq_linear_revmap(bank->irq_domain, pin);
16 if (!virq)
17 return IRQ_NONE;
18 generic_handle_irq(virq);
19 return IRQ_HANDLED;
20 }
第7行的宏EXYNOS_SVC_OFFSET是0xB08,这个需要结合Exynos4412的用户手册看:

当GPA1_5触发中断话,[7:3]的值就是2,而[2:0]就是5
第13行根据得到的group编号得到bank,也就得到了irq_domain
第9行得到的就是引发中断的引脚编号,也就是hwirq
第15行利用上面找到的irq_domain和hwirq,就可以得到对应virq,然后就可以继续处理该virq,一般此时就会调用到用户注册的具体的中断处理程序。
跟上面同样的道理,可以得到下面的图4和图5:
图4

图5
分析方法跟图3一样。
下面的几组GPIO跟前面的略有不同:GPX和GPZ。其中GPX一共有32个引脚,对应的是图2说的32个具有wake up功能的外部中断,而GPZ不同之处是,它没有直接连到GIC上,而是连到了Combiner上。
GPX:

图6
对于GPX0和GPX1来说,每一个引脚都对应到一个SPI中断。而GPX2和GPX3的所有引脚共享一个SPI中断。下面我们看看在驱动层面主要是在函数exynos_eint_wkup_init中处理的:
对于GPX0和GPX1来说:
1 for (i = 0; i < d->nr_banks; ++i, ++bank) {
2 if (bank->eint_type != EINT_TYPE_WKUP)
3 continue;
4
5 bank->irq_domain = irq_domain_add_linear(bank->of_node,
6 bank->nr_pins, &exynos_eint_irqd_ops, bank);
7
8 bank->irq_chip = irq_chip;
9
10 if (!of_find_property(bank->of_node, "interrupts", NULL)) {
11 bank->eint_type = EINT_TYPE_WKUP_MUX;
12 ++muxed_banks;
13 continue;
14 }
15
16 weint_data = devm_kzalloc(dev, bank->nr_pins
17 * sizeof(*weint_data), GFP_KERNEL);
18
19 for (idx = 0; idx < bank->nr_pins; ++idx) {
20 irq = irq_of_parse_and_map(bank->of_node, idx);
21
22 weint_data[idx].irq = idx;
23 weint_data[idx].bank = bank;
24 irq_set_chained_handler_and_data(irq,
25 exynos_irq_eint0_15,
26 &weint_data[idx]);
27 }
28 }
在设备树中GPX0和GPX1设置interupts属性,而GPX2和GPX3没有。所以执行完上面的逻辑,muxed_banks为2,就是GPX2和GPX3,而其bank->eint_type被设置为了EINT_TYPE_WKUP_MUX。
第20行会解析GPX0和GPX1节点interrupts属性,并将每一个hwirq都映射为对应的virq
第24行调用irq_set_chained_handler_and_data设置SPI中断对应的handler,可以理解为级联。意味着GPX0、GPX1的引脚会在所属的bank的irq_domain里映射一次,而对应的SPI中断也会在GIC的irq_domain里映射一次。因为每个irq都传递了对应的weint_data[idx],所以exynost_irq_eint0_15虽然被共享了,但是其逻辑却很简单:
1 static void exynos_irq_eint0_15(struct irq_desc *desc)
2 {
3 struct exynos_weint_data *eintd = irq_desc_get_handler_data(desc);
4 struct samsung_pin_bank *bank = eintd->bank;
5 struct irq_chip *chip = irq_desc_get_chip(desc);
6 int eint_irq;
7
8 chained_irq_enter(chip, desc);
9
10 eint_irq = irq_linear_revmap(bank->irq_domain, eintd->irq);
11 generic_handle_irq(eint_irq);
12
13 chained_irq_exit(chip, desc);
14 }
下面是GPX2和GPX3:
这两组GPIO共享一个SPI中断,所以会涉及到mux操作。驱动也是在exynos_eint_wkup_init处理的:
1 irq = irq_of_parse_and_map(wkup_np, 0);
2
3 muxed_data = devm_kzalloc(dev, sizeof(*muxed_data)
4 + muxed_banks*sizeof(struct samsung_pin_bank *), GFP_KERNEL);
5
6 irq_set_chained_handler_and_data(irq, exynos_irq_demux_eint16_31,
7 muxed_data);
8
9 bank = d->pin_banks;
10 idx = 0;
11 for (i = 0; i < d->nr_banks; ++i, ++bank) {
12 if (bank->eint_type != EINT_TYPE_WKUP_MUX)
13 continue;
14
15 muxed_data->banks[idx++] = bank;
16 }
17 muxed_data->nr_banks = muxed_banks;
第1行解析设备树里"wakeup-interrupt-controller"节点的interrupts属性,映射获得对应的virq
第6行设置级联
第15行的for循环中,banks[0]对应的是GPX2,banks[1]对应的是GPX3,
由于GPX2和GPX3共享了一个SPI中断,所以第6行的exynos_irq_demux_eint16_31就需要完成mux的操作:
1 static void exynos_irq_demux_eint16_31(struct irq_desc *desc)
2 {
3 struct irq_chip *chip = irq_desc_get_chip(desc);
4 struct exynos_muxed_weint_data *eintd = irq_desc_get_handler_data(desc);
5 struct samsung_pinctrl_drv_data *d = eintd->banks[0]->drvdata;
6 unsigned long pend;
7 unsigned long mask;
8 int i;
9
10 chained_irq_enter(chip, desc);
11
12 for (i = 0; i < eintd->nr_banks; ++i) {
13 struct samsung_pin_bank *b = eintd->banks[i];
14 pend = readl(d->virt_base + b->irq_chip->eint_pend
15 + b->eint_offset);
16 mask = readl(d->virt_base + b->irq_chip->eint_mask
17 + b->eint_offset);
18 exynos_irq_demux_eint(pend & ~mask, b->irq_domain);
19 }
20
21 chained_irq_exit(chip, desc);
22 }
第12行的for循环依次处理GPX2和GPX3,主要就是看是不是有那个引脚的中断处于pengding中,如果有的话,函数exynos_irq_demux_eint会进行处理。
1 static inline void exynos_irq_demux_eint(unsigned long pend,
2 struct irq_domain *domain)
3 {
4 unsigned int irq;
5
6 while (pend) {
7 irq = fls(pend) - 1;
8 generic_handle_irq(irq_find_mapping(domain, irq));
9 pend &= ~(1 << irq);
10 }
11 }
最后说一下GPZ这组GPIO:

图7
结合设备树,可以看到GPZ使用了Combiner的Group10的第0号中断源。
Exynos4412的第10章有队Interrupts Combiner的介绍,Interrupt Combiner也是一个中断控制器,跟GIC之间采用级联的方式。Interrupt Combiner后面可以支持116路中断源,然后将这些中断源进行分组上报给GIC,每一组对应一个GIC上中断,在interrupt combiner的设备节点可以看到它使用了GIC上的20个SPI中断,也就是将116个中断分成20个组,每组下面最多支持8个中断源,每组对应一个GIC上的SPI中断,每组下面对应那些中断源都是定死的。
inerrupt combiner对应的驱动文件是:drivers/irqchip/exynos-combiner.c
1 static void __init combiner_init(void __iomem *combiner_base,
史海拾趣
|
请教一个比较急的问题,我现在在用HCNR201这个线性光耦来搭电路,输入输出5V供电,运放使用的是358,但是现在有个问题,就是这个电路只能在输入是1.8V一下,有变化,当输入超过1.8V的时候输出保持在1.8V不变。而且在输出变化的时候,会有频率很高的 ...… 查看全部问答> |
|
请问为什么我用KEIL C调试的时候,跟踪变量的时候在第一个函数的变量都能在右下角的窗口中显示,但是进入第2个函数后里面的变量就不能在窗口中显示了,窗口是空白的,请问这是为什么啊… 查看全部问答> |
|
我在写一个2440 ADC的驱动程序,程序已经被windows启动起来了,但是只要一执行到以下初始化红色的地方,程序就在串口输出崩溃信息。我的问题是,是不是触摸屏也在使用这个ADC。而2个驱动程序不能互相竞争这个寄存器? 我感觉不应该这样的,我在初 ...… 查看全部问答> |
|
2440开发板,wince5.0 在试SD卡时,只能读不能写。排除本身SD卡锁的可能,卡锁的两个位置都试过了,而且SD卡在PC机上也试过读写正常。SD卡的格式是FAT. 我通PC机与开发板的ActiveSync连接,可以看到SD卡的文件,要删除一个文件时,显示“文件可能 ...… 查看全部问答> |
|
汇编写的斐波那挈数列 哪个牛人帮我看一下 我是刚学汇编的 有些不懂 运行结果是乱码 请教大家 斐波那挈 哪个牛人帮我看一下 我是刚学汇编的 有些不懂 运行结果是乱码 请教大家 data segment x dw 24 dup ...… 查看全部问答> |
|
LED照明行业是一个新兴的行业,它以其独特的优点深受人们的青睐。如今在光电工程中,提高光效,节约能源和高可靠性已经成为人们共同追求的目的。我们在讨论和使用LED光源时,都会想到LED的寿命长、节约能源、亮度高等特点。也正是因为如此LED光源才 ...… 查看全部问答> |




