作者

彭东林
pengdonglin137@163.com
 

平台

Linux4.9
tiny4412
 

概述

结合tiny4412开发板分析一下Exynos4412的外部中断是如何组织的。
 

正文

在Exynos4412的用户手册第9章Interrupt Controller列出了支持的外部中断:
 
图1
 
第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, );
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,
, dev_name(dev), d);

也就是当GIC上的SPI-47这个中断被触发后,中断处理函数exynos_eint_gpio_irq会被调用。

然后就是对图3中的每一组GPIO都注册对应的irq_domain:

     bank = d->pin_banks;
for (i = ; i < d->nr_banks; ++i, ++bank) {
if (bank->eint_type != EINT_TYPE_GPIO)
continue; bank->irq_domain = irq_domain_add_linear(bank->of_node,
bank->nr_pins, &exynos_eint_irqd_ops, bank); bank->irq_chip = &exynos_gpio_irq_chip;
}
也就是图3中的每一组GPIO都可以是一个中断控制器,bank->nr_pins是该组GPIO有几个引脚,每一个引脚对应一个中断,所以也就表示该irq_domain可以最大支持的中断个数。第9行设置了irq_chip,用户控制每一个引脚中断的打开、关闭、清除等。
下面以GPA0_0这个引脚触发中断为例简单看看是如何处理的?
GPA1_5产生中断后,会在SPI-47上也引发中断,函数exynos_eint_gpio_irq会被调用,其中会查询到产生的中断的引脚,然后在对应的引脚所属的irq_domain中查询引脚对应的虚拟中断,最后执行到用户注册的中断处理函数。

 static irqreturn_t exynos_eint_gpio_irq(int irq, void *data)
{
struct samsung_pinctrl_drv_data *d = data;
struct samsung_pin_bank *bank = d->pin_banks;
unsigned int svc, group, pin, virq; svc = readl(d->virt_base + EXYNOS_SVC_OFFSET);
group = EXYNOS_SVC_GROUP(svc);
pin = svc & EXYNOS_SVC_NUM_MASK; if (!group)
return IRQ_HANDLED;
bank += (group - ); virq = irq_linear_revmap(bank->irq_domain, pin);
if (!virq)
return IRQ_NONE;
generic_handle_irq(virq);
return IRQ_HANDLED;
}
第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来说:
 for (i = ; i < d->nr_banks; ++i, ++bank) {
if (bank->eint_type != EINT_TYPE_WKUP)
continue; bank->irq_domain = irq_domain_add_linear(bank->of_node,
bank->nr_pins, &exynos_eint_irqd_ops, bank); bank->irq_chip = irq_chip; if (!of_find_property(bank->of_node, "interrupts", NULL)) {
bank->eint_type = EINT_TYPE_WKUP_MUX;
++muxed_banks;
continue;
} weint_data = devm_kzalloc(dev, bank->nr_pins
* sizeof(*weint_data), GFP_KERNEL); for (idx = ; idx < bank->nr_pins; ++idx) {
irq = irq_of_parse_and_map(bank->of_node, idx); weint_data[idx].irq = idx;
weint_data[idx].bank = bank;
irq_set_chained_handler_and_data(irq,
exynos_irq_eint0_15,
&weint_data[idx]);
}
}

在设备树中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虽然被共享了,但是其逻辑却很简单:
 static void exynos_irq_eint0_15(struct irq_desc *desc)
{
struct exynos_weint_data *eintd = irq_desc_get_handler_data(desc);
struct samsung_pin_bank *bank = eintd->bank;
struct irq_chip *chip = irq_desc_get_chip(desc);
int eint_irq; chained_irq_enter(chip, desc); eint_irq = irq_linear_revmap(bank->irq_domain, eintd->irq);
generic_handle_irq(eint_irq); chained_irq_exit(chip, desc);
}
 
下面是GPX2和GPX3:
这两组GPIO共享一个SPI中断,所以会涉及到mux操作。驱动也是在exynos_eint_wkup_init处理的:
 irq = irq_of_parse_and_map(wkup_np, );

     muxed_data = devm_kzalloc(dev, sizeof(*muxed_data)
+ muxed_banks*sizeof(struct samsung_pin_bank *), GFP_KERNEL); irq_set_chained_handler_and_data(irq, exynos_irq_demux_eint16_31,
muxed_data); bank = d->pin_banks;
idx = ;
for (i = ; i < d->nr_banks; ++i, ++bank) {
if (bank->eint_type != EINT_TYPE_WKUP_MUX)
continue; muxed_data->banks[idx++] = bank;
}
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的操作:
 static void exynos_irq_demux_eint16_31(struct irq_desc *desc)
{
struct irq_chip *chip = irq_desc_get_chip(desc);
struct exynos_muxed_weint_data *eintd = irq_desc_get_handler_data(desc);
struct samsung_pinctrl_drv_data *d = eintd->banks[]->drvdata;
unsigned long pend;
unsigned long mask;
int i; chained_irq_enter(chip, desc); for (i = ; i < eintd->nr_banks; ++i) {
struct samsung_pin_bank *b = eintd->banks[i];
pend = readl(d->virt_base + b->irq_chip->eint_pend
+ b->eint_offset);
mask = readl(d->virt_base + b->irq_chip->eint_mask
+ b->eint_offset);
exynos_irq_demux_eint(pend & ~mask, b->irq_domain);
} chained_irq_exit(chip, desc);
}

第12行的for循环依次处理GPX2和GPX3,主要就是看是不是有那个引脚的中断处于pengding中,如果有的话,函数exynos_irq_demux_eint会进行处理。

 static inline void exynos_irq_demux_eint(unsigned long pend,
struct irq_domain *domain)
{
unsigned int irq; while (pend) {
irq = fls(pend) - ;
generic_handle_irq(irq_find_mapping(domain, irq));
pend &= ~( << irq);
}
}
最后说一下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

 static void __init combiner_init(void __iomem *combiner_base,
struct device_node *np)
{
int i, irq;
unsigned int nr_irq; nr_irq = max_nr * IRQ_IN_COMBINER; combiner_data = kcalloc(max_nr, sizeof (*combiner_data), GFP_KERNEL); combiner_irq_domain = irq_domain_add_linear(np, nr_irq,
&combiner_irq_domain_ops, combiner_data); for (i = ; i < max_nr; i++) {
irq = irq_of_parse_and_map(np, i); combiner_init_one(&combiner_data[i], i,
combiner_base + (i >> ) * 0x10, irq);
combiner_cascade_irq(&combiner_data[i], irq);
}
}
第15行解析interrupt combiner节点的interrupts属性,获得对应的virq
第19行设置级联
 
完。
 
 
 
 

Exynos4412的外部中断是如何安排的?的更多相关文章

  1. 单片微机原理P2:80C51外部中断与定时器系统

    0. 外部中断 书上的废话当然是很多的了,对于中断我想大家应该早就有一个很直观的认识,就是"设置断点,执行外部外码,然后返回断点"这样的三个过程.中断给系统提供了一个良好的响应模式 ...

  2. STM32f103之外部中断

    一.背景 有个需求,IO口检测上升沿,然后做相应的动作.在此记录STM32F103的外部中断结构及配置方法, 以备下次快速上手使用. 有许多不太明白,又是老司机(:-D)帮忙,真的是站在别人的肩膀上会 ...

  3. STM32之EXTI——外部中断

    互联网的广大网友,大家早上中午晚上好.EXTI...故名思义..EX表外,出..I表示Intrrupt..所以合起来就是外部中断...说到这..我觉得我最近的六级水平(背单词)又进了一步,稍微自夸了下 ...

  4. 转别人的 STM32外部中断使用注意事项

    前言:这些问题都是我之前在工作中遇到的,后来觉得需要总结,自己记忆不好,所以在这个给自己打个mark. 一:触发方式 STM32 的外部中断是通过边沿来触发的,不支持电平触发: 二:外部中断分组 ST ...

  5. STM32外部中断初理解

    PA0,PB0...PG0--->EXTI0 PA1,PB1...PG1--->EXTI1 ....... PA15,PB15...PG15--->EXTI15 以上为GPIO和中断 ...

  6. STM32——外部中断EXIT实现

    外部中断实现步骤: 一.初始化,包括:1.AFIO时钟中断和GPIO时钟使能:                          2.GPIO初始化                           ...

  7. STM32 GPIO外部中断总结

    一.STM32中断分组: STM32 的每一个GPIO都能配置成一个外部中断触发源,这点也是 STM32 的强大之处.STM32 通过根据引脚的序号不同将众多中断触发源分成不同的组,比如:PA0,PB ...

  8. 向linux内核中添加外部中断驱动模块

    本文主要介绍外部中断驱动模块的编写,包括:1.linux模块的框架及混杂设备的注册.卸载.操作函数集.2.中断的申请及释放.3.等待队列的使用.4.工作队列的使用.5.定时器的使用.6.向linux内 ...

  9. STM32学习笔记(九) 外部中断,待机模式和事件唤醒

    学会知识只需要不段的积累和提高,但是如何将知识系统的讲解出来就需要深入的认知和系统的了解.外部中断和事件学习难度并不高,不过涉及到STM32的电源控制部分,还是值得认真了解的,在本文中我将以实际代码为 ...

随机推荐

  1. C# IEqualityComparer类型参数写法

    最近在使用Union.Except时,由于默认的对比不太好使,所以需要自定义对比器,下面附上代码. class MaterialListComparer : IEqualityComparer< ...

  2. java future 并发简单实现

    List<Future<>> futures = new ArrayList<>();List<T> t= new ArrayList<>( ...

  3. GPU下train 模型出现nan

    When training on GPU, the error "Model diverged with loss = NaN" is often caused by a sotm ...

  4. day17作业

    1.java.util  2.队列先进先出,栈堆先进后出   3.链表  4.LinkedList  5.TreeSet  6.Comparable  7.Map  8.next() 1.AC 2.A ...

  5. JS实现文本中查找并替换字符

    JS实现文本中查找并替换字符 效果图: 代码如下,复制即可使用: <!DOCTYPE html><html> <head> <style type=" ...

  6. CMD命令行下修改网络IP设置的方法

    1.全自动修改网络地址: 复制代码 代码如下: @echo off netsh interface ip set address name="本地连接" static 192.16 ...

  7. final在类和方法中的使用

    package final0; //final修饰的类不能继承//final修饰的方法不能继承public class TestFinal3 { public static void main(Str ...

  8. CentOS 下的邮件通知

    利用Shell邮件通知 echo "邮件内容" |mail -s 邮件主题 收件人地址 echo "This is Test mail."|mail -s &q ...

  9. 关于Sublime text3 配置及插件整理

    Package Control组件在线安装 按Ctrl+`调出console(注:避免热键冲突) 粘贴以下代码到命令行并回车: import urllib.request,os; pf = 'Pack ...

  10. Java学习之模拟纸牌游戏,List的ArrayList,Map的HashMap,重写Collections类的sort方法对指定类进行通过特定属性排序,输入异常处理等的学习

    首先放上测试效果图 设计框架 具体的代码实现 创建玩家类 public class Player implements Comparable<Player>{ int id; String ...