GIC是ARM推出的一个通用的中断控制器,全志H3中使用了GIC的多核方案,符合GIC V2规格。ARM多核处理器一般搭建一个GIC来提供中断控制功能,中端控制器是连接外设中断系统和CPU系统的桥梁。
ARM上把中断分为三类:
- PPI(private peripheral interrupt) 16~31
这些中断一般是发送给特定的Cpu的,比如每个Cpu有自己对应的Physicaltimer,产生的中断信号就发送给这个特定的cpu进行处理
- SPI(shared processor interrupts)
这是常见的外部设备中断,也定义为共享中断,比如按键触发一个中断,手机触摸屏触发的中断,共享的意思是说可以多个Cpu或者说Core处理,不限定特定的Cpu。一般定义的硬件中断号范围31~1019.Cortex-A15和A9上的GIC最多支持224个SPI。
- SGI(software generated interrupts)
软件出发产生的中断,中断号范围0~15,也就是最前的16个中断,相当于IPI,简单的说Cpu_1要给Cpu_2发送特定信息,比如时间同步,全局进程调度信息,就通过软件中断方式,目标Cpu接受到这样的中断信息,可以获取到信息是哪个Cpu发送过来的,具体的中断ID是哪个数字,从而找到对应处理方式进行处理。
全志H3中断体系结构的拓扑如下所示,4个CPU核连接到root GIC,然而子中断控制器(比如GPIO控制器)不与CPU直接相连,而是上报至root GIC的某个SPI中断上,root GIC负责接收子中断控制器信息然后向某个CPU汇报。
在全志H3中,不是每个GPIO bank都可以作为中断控制器的,只有PA,PG,和PL可以作为中断控制器。PA这一bank下的PA0,PA1,PA2。。。等request line公用一个SPI-43号中断。
使用nanopi-m1的PA9 外部中断
在设备树源文件sun8i-h3-nanopi-m1.dts添加interrupt-demo节点
interrupt_demo: interrupt_demo {
compatible = "nanopi-m1,interrupt_demo";
interrupt-parent = <&pio>;
//interrupts = <0 9 IRQ_TYPE_EDGE_FALLING>;
key-gpio = <&pio 0 9 GPIO_ACTIVE_LOW>;
};
interrupt-demo节点用interrupt-parent属性指明其中断父亲属于pinctrl,pinctrl节点的描述如下所示,其中interrupt-cells为3说明interrupt-demo节点的interrupts属性值需要3个bytes来表示,
pio: pinctrl@01c20800 {
/* compatible is in per SoC .dtsi file */
reg = <0x01c20800 0x400>;
interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&ccu CLK_BUS_PIO>, <&osc24M>, <&osc32k>;
clock-names = "apb", "hosc", "losc";
gpio-controller;
#gpio-cells = <3>;
interrupt-controller;
#interrupt-cells = <3>;
......
};驱动程序
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
typedef struct
{
int gpio;
int irq;
}int_demo_data_t;
static irqreturn_t int_demo_isr(int irq, void *dev_id)
{
int_demo_data_t *data = dev_id;
printk("%s enter, gpio:%d, irq: %d\n", __func__, data->gpio, data->irq);
return IRQ_HANDLED;
}
static int int_demo_probe(struct platform_device *pdev) {
struct device *dev = &pdev->dev;
int irq_gpio = -1;
int irq = -1;
int ret = 0;
int i = 0;
int_demo_data_t *data ;
printk("%s enter.\n", __func__);
data = devm_kmalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data) {
printk("malloc fail for data\n");
return -1;
}
irq_gpio = of_get_named_gpio(dev->of_node,"key-gpio", 0);
data[0].gpio = irq_gpio;
irq = gpio_to_irq(irq_gpio);
data[0].irq = irq;
printk("%s: gpio: %d ---> irq (%d)\n", __func__, irq_gpio, irq);
ret = devm_request_any_context_irq(dev, irq,
int_demo_isr, IRQF_TRIGGER_FALLING, "key-gpio", data);
if (ret < 0) {
dev_err(dev, "Unable to claim irq %d; error %d\n",
irq, ret);
return -1;
}
return 0;
}
static int int_demo_remove(struct platform_device *pdev) {
printk("%s enter.\n", __func__);
return 0;
}
static const struct of_device_id int_demo_dt_ids[] = {
{ .compatible = "nanopi-m1,interrupt_demo", },
{},
};
static struct platform_driver int_demo_driver = {
.driver = {
.name = "interrupt_demo",
.of_match_table = int_demo_dt_ids,
},
.probe = int_demo_probe,
.remove = int_demo_remove,
};
static int int_demo_init(void)
{
platform_driver_register(&int_demo_driver);
return 0;
}
static void __exit int_demo_exit(void)
{
platform_driver_unregister(&int_demo_driver);
}
module_init(int_demo_init);
module_exit(int_demo_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("WU");
编写驱动程序的Makefile,编译,加载模块
root@wu:/mnt/driver_test/irq_test# [ 377.666140] int_demo_isr enter, gpio:9, irq: 71
[ 377.743009] int_demo_isr enter, gpio:9, irq: 71
[ 377.927100] int_demo_isr enter, gpio:9, irq: 71
[ 378.000418] int_demo_isr enter, gpio:9, irq: 71
[ 480.665685] int_demo_isr enter, gpio:9, irq: 71
root@wu:/mnt/driver_test/irq_test# cat /proc/interrupts
71: 0 0 0 0 sunxi_pio_edge 9 Edge key-gpio