一、平台信息
1. 平台信息
- 以2440开发板为例,参考s3c2440的数据手册,得到如下中断寄存器信息:
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表示对应的子中断被屏蔽
二、中断的驱动实现方式
2. 不使用设备树的中断实现
2.1 mach-mini2440.c文件中注册中断
- 不用设备树时,如mini2440,在mach-mini2440.c中写入s3c2440_init_irq
#mach-mini2440.c里注册irq-s3c24xx.c drivers\irqchip 中的s3c2440_init_irq中断初始化函数
mach-mini2440.c arch\arm\mach-s3c24xx 17942 2018/9/10 113
MACHINE_START(MINI2440, "MINI2440")
/* Maintainer: Michel Pollet <buserror@gmail.com> */
.atag_offset = 0x100,
.map_io = mini2440_map_io,
.init_machine = mini2440_init,
.init_irq = s3c2440_init_irq,
.init_time = mini2440_init_time,
MACHINE_END
2.2 注册中断
- s3c2440_init_irq 给s3c_intc[]初始化
- 中断控制器的基地址在程序中写死
//s3c_intc[0]为主控制器,地址0x4a000000
//s3c_intc[1]为子控制器,地址0x4a000018
void __init s3c2440_init_irq(void)
{
pr_info("S3C2440: IRQ Support\n");
#ifdef CONFIG_FIQ
init_FIQ(FIQ_START);
#endif
s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2440base[0], NULL,
0x4a000000);
if (IS_ERR(s3c_intc[0])) {
pr_err("irq: could not create main interrupt controller\n");
return;
}
s3c24xx_init_intc(NULL, &init_eint[0], s3c_intc[0], 0x560000a4);
s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2440subint[0],
s3c_intc[0], 0x4a000018);
}
[===========]
3. 使用设备树的中断实现
设备树的编译与反编译
编译设备树
make dtbs
或
dtc -I dts -O dtb *.dts > my.dtb
- arch/arm/boot/dts/Makefile会读取内核下include/config/auto.conf文件
- 若CONFIG_ARCH_S3C24XX=y,编译arch/arm/boot/dts/Makefile对应的dts文件
反编译设备树
可参考附录的s3c2416-smdk2416.dts代码
./scripts/dtc/dtc -I dtb -O dts arch/arm/boot/dts/s3c2416-smdk2416.dtb > s3c2416-smdk2416.dts
设备树中断参数的修改
可以通过设备树向内核传递参数,但是参数如何传递和解析由程序决定。
- 通过查询内核文档可以知道怎么编写设备树
##通过以下命令,可以查询到相关文档的信息
grep "\"samsung,s3c2440-uart\"" * -nR
- 以2440开发板为例,参考
Documentation\devicetree\bindings\interrupt-controller\samsung,s3c24xx-irq.txt
- 得到的重要信息
Required properties:
- compatible: Compatible property value should be "samsung,s3c2410-irq"
for machines before s3c2416 and "samsung,s3c2416-irq" for s3c2416 and later.
- reg: Physical base address of the controller and length of memory mapped
region.
- interrupt-controller : Identifies the node as an interrupt controller
- #interrupt-cells : Specifies the number of cells needed to encode an
interrupt source. The value shall be 4 and interrupt descriptor shall
have the following format:
<ctrl_num parent_irq ctrl_irq type>
ctrl_num contains the controller to use:
- 0 ... main controller
- 1 ... sub controller
- 2 ... second main controller on s3c2416 and s3c2450
parent_irq contains the parent bit in the main controller and will be
ignored in main controllers
ctrl_irq contains the interrupt bit of the controller
type contains the trigger type to use
- 分析设备树
- 中断控制器
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>;
};
- 以uart为例
interrupts = <1 28 0 4>, <1 28 1 4>;`
-
<1 28 0 4>
-
1:是子控制器
-
28:主中断是28
-
0:子中断的硬件中断号为0
-
4:为高电平触发
-
定位到INT_RXD0中断源
-
<1 28 1 4>
-
1:是子控制器
-
28:主中断是28
-
1:子中断的硬件中断号为1
-
4:为高电平触发
-
定位到INT_TXD0中断源
i2c:i2c@54000000 {
compatible = "samsung,s3c2410-i2c";
reg = <0x54000000 0x100>;
//查数据手册P358的SRCPND寄存器信息,27刚好是i2c的中断号
//INT_IIC [27] 0 = Not requested, 1 = Requested 0
interrupts = <0 0 27 3>;
#address-cells = <1>;
#size-cells = <0>;
};
内核启动的过程调用设备树
一、内核部分
- dts ===> .dtb ============> device_node ========>platform_device
- 得到platform_device(获得资源),匹配platform_drv
- 上述处理过程如何触发?
a. 内核启动时初始化中断的入口:
start_kernel // init/main.c
init_IRQ(); //如果内核使用设备树则调用init_IRQ
of_irq_init(__irqchip_of_table); //扫描和初始化匹配设备树中的中断控制器
if (IS_ENABLED(CONFIG_OF) && !machine_desc->init_irq)
irqchip_init(); // 一般使用它
else
machine_desc->init_irq();
二、调用对应驱动程序
b. 设备树中的中断控制器的处理入口:
irqchip_init // drivers/irqchip/irqchip.c
of_irq_init(__irqchip_of_table); // 对设备树文件中每一个中断控制器节点, 调用对应的处理函数
为每一个符合的"interrupt-controller"节点,
并调用处理函数
(先调用root irq controller对应的函数, 再调用子控制器的函数, 再调用更下一级控制器的函数...)
of_irq_init
//建立个中断控制器desc数据结构体,调用中断控制器注册的初始化程序,
//来建立父子中断控制器之间的联系
of_irq_init
{
//遍历符合compatible的设备节点
//例子:遍历所有符合compatible = "samsung,s3c2416-irq"节点
for循环(匹配设备树的输入节点)
{
1.符合compatible的节点中,找"interrupt-controller"属性的节点
找中断控制器:匹配设备树输入节点的"interrupt-controller"属性
//match =__of_table_s3c2410_irq, 即match->data 调用__of_table_s3c2410_irq
2.分配一个of_intc_desc结构体, desc->irq_init_cb = match->data; // 驱动中IRQCHIP_DECLARE宏:match->data=init*irq函数
3.创建of_intc_desc的中断控制器desc结构体,并持续添加进循环双链表
}
//遍历刚刚的循环双链表
//1.先找出主中断控制器,调用驱动init*irq
//2.再找出子中断控制器,调用驱动init*irq,建立和中断控制器的联系
while (!list_empty(&intc_desc_list))//
}
总结:(先调用root irq controller对应的函数, 再调用子控制器的函数, 再调用更下一级控制器的函数…)
其中:
#define IRQCHIP_DECLARE(name, compat, fn) OF_DECLARE_2(irqchip, name, compat, fn)
#define OF_DECLARE_2(table, name, compat, fn) \
_OF_DECLARE(table, name, compat, fn, of_init_fn_2)
#define _OF_DECLARE(table, name, compat, fn, fn_type) \
static const struct of_device_id __of_table_##name \
__used __section(__##table##_of_table) \
= { .compatible = compat, \
.data = (fn == (fn_type)NULL) ? fn : fn }
展开为:
static const struct of_device_id __of_table_s3c2410_irq \
__used __section("__irqchip_of_table") \
= { .compatible = "samsung,s3c2410-irq", \
.data = s3c2410_init_intc_of }
建立irq_domain
- 每一个中断控制器都有一个irq_domain
- 通过查询irq_domain,可以获取硬件中断号和软件中断号之前的联系
irq_domain是核心:
a. 每一个中断控制器都有一个irq_domain
b. 对设备中断信息的解析,
b.1 需要调用 irq_domain->ops->xlate (即从设备树中获得hwirq, type)
b.2 获取未使用的virq, 保存: irq_domain->linear_revmap[hwirq] = virq;
b.3 在hwirq和virq之间建立联系:
要调用 irq_domain->ops->map, 比如根据hwirq的属性设置virq的中断处理函数(是一个分发函数还是可以直接处理中断)
irq_desc[virq].handle_irq = 常规函数;
如果这个hwirq有上一级中断, 假设它的中断号为virq', 还要设置:
irq_desc[virq'].handle_irq = 中断分发函数;
一个引用的是主中断,另一个引用的是子中断
4. 代码如何解析设备树文件
4.1 注册中断控制器驱动
- 1-10行:将中断控制器写入s3c2410_ctrl
- 12-17行:中断初始化函数
- 注册中断初始化函数
- IRQCHIP_DECLARE宏将"samsung,s3c2410-irq"和s3c2410_init_intc_of注册
- 当内核启动匹配设备树时,成功匹配会调用s3c2410_init_intc_of初始化函数
- s3c2410_init_intc_of实际调用->s3c_init_intc_of
static struct s3c24xx_irq_of_ctrl s3c2410_ctrl[] = {
{
.name = "intc",
.offset = 0,
}, {
.name = "subintc",
.offset = 0x18,
.parent = &s3c_intc[0],
}
};
int __init s3c2410_init_intc_of(struct device_node *np,
struct device_node *interrupt_parent)
{
return s3c_init_intc_of(np, interrupt_parent,
s3c2410_ctrl, ARRAY_SIZE(s3c2410_ctrl));
}
//当匹配到"samsung,s3c2410-irq"设备树时,自动调用s3c2410_init_intc_of初始化函数
//s3c2410_irq这个名字不重要,只是创建一个名字相关的of_device_id结构体,
//__of_table_##name
/*
__of_table_##name
={
compatible = compat,
.data = fn }
}
*/
IRQCHIP_DECLARE(s3c2410_irq, "samsung,s3c2410-irq", s3c2410_init_intc_of);
4.2 s3c_init_intc_of初始化函数解析
- 在内核启动时被调用,以24xx为例
- 因为.compatible = "samsung,s3c2410-irq"为,且interrupt-controller,只有主中断控制器这一个设备节点,所以
- 在设备树文件,中断部分只传递了主中断控制器的基地址
- 所以在irq-s3c24xx.c 文件中在s3c2416_ctrl[]写死了主、子控制器的偏移位置
- 所以s3c2416_init_intc_of仅被调用一次,即s3c_init_intc_of仅被调用一次
- 映射出中断控制器的基地址
- 这里选择父和子控制器共享domain方式,支持2 * 32 =64个中断源
- 清中断控制器
- 放控制器进全局数组s3c_intc[i] = intc;
- 设置中断汇编entry-armv.S跳转到s3c24xx_handle_irq
static int __init s3c_init_intc_of(struct device_node *np,
struct device_node *interrupt_parent,
struct s3c24xx_irq_of_ctrl *s3c_ctrl, int num_ctrl)
{
struct s3c_irq_intc *intc;
struct s3c24xx_irq_of_ctrl *ctrl;
struct irq_domain *domain;
void __iomem *reg_base;
int i;
//根据中断控制器设备节点,映射出寄存器的虚拟地址
reg_base = of_iomap(np, 0);
if (!reg_base) {
pr_err("irq-s3c24xx: could not map irq registers\n");
return -EINVAL;
}
//s3c24xx 只有主和子共2个控制器,这里采用共用domain,所以是2*32
//每个中断控制器都分配和注册一个反向映射domain域
//通过domains找3c24xx_irq_ops_of.map函数,映射硬件和虚拟中断号之间的联系
//通过domain通过s3c24xx_irq_ops_of.xlate调用分发函数demux,设置中断服务函数
domain = irq_domain_add_linear(np, num_ctrl * 32,
&s3c24xx_irq_ops_of, NULL);
if (!domain) {
pr_err("irq: could not create irq-domain\n");
return -EINVAL;
}
//num_ctrl = 2
for (i = 0; i < num_ctrl; i++) {
ctrl = &s3c_ctrl[i];
pr_debug("irq: found controller %s\n", ctrl->name);
intc = kzalloc(sizeof(struct s3c_irq_intc), GFP_KERNEL);
if (!intc)
return -ENOMEM;
//主和子控制器共享一个domain
intc->domain = domain;
intc->irqs = kcalloc(32, sizeof(struct s3c_irq_data),
GFP_KERNEL);
if (!intc->irqs) {
kfree(intc);
return -ENOMEM;
}
if (ctrl->parent) { //如果是子控制器
intc->reg_pending = reg_base + ctrl->offset; //SUBSRCPND寄存器
intc->reg_mask = reg_base + ctrl->offset + 0x4; //INTSUBMSK(偏移一个字)
if (*(ctrl->parent)) {
intc->parent = *(ctrl->parent);
} else {
pr_warn("irq: parent of %s missing\n",
ctrl->name);
kfree(intc->irqs);
kfree(intc);
continue;
}
} else { //主控制器
intc->reg_pending = reg_base + ctrl->offset; //参考:0x4A000000
intc->reg_mask = reg_base + ctrl->offset + 0x08; //参考:0x4A000008
intc->reg_intpnd = reg_base + ctrl->offset + 0x10; //参考:0x4A000010
}
s3c24xx_clear_intc(intc); //清中断
//i=0,主控制器
//i=1,子控制器
s3c_intc[i] = intc; //解析完设备树后,将解析得到中断控制器intc写入s3c_intc
}
// 将handle_arch_irq设置为s3c24xx_handle_irq
//ldr r1, =handle_arch_irq
//这样在发生中断后,会从汇编entry-armv.S中跳转到s3c24xx_handle_irq
set_handle_irq(s3c24xx_handle_irq);
return 0;
}
4.3 s3c24xx_irq_ops_of的注册?
在s3c_init_intc_of中的第23行:
domain = irq_domain_add_linear(np, num_ctrl * 32,
&s3c24xx_irq_ops_of, NULL);
s3c24xx_irq_ops_of的成员
static const struct irq_domain_ops s3c24xx_irq_ops_of = {
.map = s3c24xx_irq_map_of,//建立虚拟中断号和硬件中断号之间的映射
.xlate = s3c24xx_irq_xlate_of,//获取中断触发方式,调用中断分发函数s3c_irq_demux
};
s3c24xx_irq_map_of
static int s3c24xx_irq_map_of(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
unsigned int ctrl_num = hw / 32; //从硬件中断号判断是主还是子中断控制器
unsigned int intc_hw = hw % 32; //寄存器上的硬件中断号
struct s3c_irq_intc *intc = s3c_intc[ctrl_num]; //获取中断控制器的结构体
struct s3c_irq_intc *parent_intc = intc->parent; //获取该父中断控制器
struct s3c_irq_data *irq_data = &intc->irqs[intc_hw]; //每个硬件中断号都有一个s3c_irq_data结构体
/* attach controller pointer to irq_data */
irq_data->intc = intc; //指向本中断控制寄存器
irq_data->offset = intc_hw; //中断控制器上的中断号
if (!parent_intc) //如果是主irq控制器
irq_set_chip_and_handler(virq, &s3c_irq_chip, handle_edge_irq);
else
irq_set_chip_and_handler(virq, &s3c_irq_level_chip,
handle_edge_irq);
irq_set_chip_data(virq, irq_data);
return 0;
}
s3c24xx_irq_map_of调用s3c_irq_demux
- 中断分发函数
- 在含有子中断的主中断被触发后,会执行主中断对应的virq的irq_desc的handle_irq,也就是这里的s3c_irq_demux
static void s3c_irq_demux(struct irq_desc *desc)
{
struct irq_chip *chip = irq_desc_get_chip(desc); //获得主中断irq_chip
struct s3c_irq_data *irq_data = irq_desc_get_chip_data(desc);
struct s3c_irq_intc *intc = irq_data->intc; //获取主中断控制器
struct s3c_irq_intc *sub_intc = irq_data->sub_intc; //获取子中断控制器
unsigned int n, offset, irq;
unsigned long src, msk;
/* we're using individual domains for the non-dt case
* and one big domain for the dt case where the subintc
* starts at hwirq number 32.
*/
//这里使用设备树,offset=32,子中断启使号从32号开始
offset = irq_domain_get_of_node(intc->domain) ? 32 : 0;
// 屏蔽该主中断,并清中断
chained_irq_enter(chip, desc);
src = readl_relaxed(sub_intc->reg_pending);
msk = readl_relaxed(sub_intc->reg_mask);
src &= ~msk; //去掉被屏蔽的子中断
src &= irq_data->sub_bits; //去掉没有被初始化的?????
while (src) {
n = __ffs(src); //返回寄存器从0~31中,第一个置位的位置
src &= ~(1 << n);
irq = irq_find_mapping(sub_intc->domain, offset + n); //根据hwirq找到virq
generic_handle_irq(irq); //调用该子中断向量的处理函数
}
chained_irq_exit(chip, desc); //打开主中断
}
s3c24xx_irq_xlate_of
/* Translate our of irq notation
* format: <ctrl_num ctrl_irq parent_irq type>
*/
static int s3c24xx_irq_xlate_of(struct irq_domain *d, struct device_node *n,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, unsigned int *out_type)
{
struct s3c_irq_intc *intc;
struct s3c_irq_intc *parent_intc;
struct s3c_irq_data *irq_data;
struct s3c_irq_data *parent_irq_data;
int irqno;
//参数个数不能小于4
if (WARN_ON(intsize < 4))
return -EINVAL;
//第一个参数不能大于2,中断控制器(结构体)不能为空
/*
* Array holding pointers to the global controller structs
* [0] ... main_intc
* [1] ... sub_intc
* [2] ... main_intc2 on s3c2416
*/
if (intspec[0] > 2 || !s3c_intc[intspec[0]]) {
pr_err("controller number %d invalid\n", intspec[0]);
return -EINVAL;
}
//取出中断控制器
intc = s3c_intc[intspec[0]];
//解析(decode)出硬件中断号
//可知主中断号:0~31 子中断号:32~
*out_hwirq = intspec[0] * 32 + intspec[2];
//获取中断触发方式
*out_type = intspec[3] & IRQ_TYPE_SENSE_MASK;
//尝试获取父中断控制器
parent_intc = intc->parent;
//如果父中断控制器存在
if (parent_intc) {
irq_data = &intc->irqs[intspec[2]];
//记录父intc
irq_data->parent_irq = intspec[1];
parent_irq_data = &parent_intc->irqs[irq_data->parent_irq];
parent_irq_data->sub_intc = intc;
parent_irq_data->sub_bits |= (1UL << intspec[2]);
/* parent_intc is always s3c_intc[0], so no offset */
irqno = irq_create_mapping(parent_intc->domain, intspec[1]);
if (irqno < 0) {
pr_err("irq: could not map parent interrupt\n");
return irqno;
}
// 从函数名称中就可以看出,这个函数会再次进行检测该主中断的哪个子中断被请求
//这里是主中断才调用s3c_irq_demux
irq_set_chained_handler(irqno, s3c_irq_demux);
}
return 0;
}
5. 中断设备树的调用过程
这里通过由上到下的描述方式:
- 声明设备树
IRQCHIP_DECLARE(s3c2410_irq, "samsung,s3c2410-irq", s3c2410_init_intc_of);
-
当设备树匹配时,自动调用s3c2410_init_intc_of函数进行初始化
-
s3c2410_init_intc_of
- 解析设备树文件传进来的参数
- 注册s3c24xx_irq_ops_of的.map .xlate函数,获取domain
- set_handle_irq(s3c24xx_handle_irq)设置汇编中断向量地址跳转到s3c24xx_handle_irq
- s3c24xx_handle_irq中断控制器处理函数,查询
s3c24xx_handle_irq
(s3c24xx_handle_intc(s3c_intc[0], regs, 0))
//根据1.中断控制器的域;2.硬件中断号;3.寄存器,调用中断处理函数
handle_domain_irq(intc->domain, intc_offset + offset, regs);
__handle_domain_irq
__handle_domain_irq最终的中断处理函数
/**
* __handle_domain_irq - Invoke the handler for a HW irq belonging to a domain
* @domain: The domain where to perform the lookup
* @hwirq: The HW irq number to convert to a logical one
* @lookup: Whether to perform the domain lookup or not
* @regs: Register file coming from the low-level handling code
*
* Returns: 0 on success, or -EINVAL if conversion has failed
*/
int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,
bool lookup, struct pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs(regs);
unsigned int irq = hwirq; //保留硬件中断号作中断
int ret = 0;
irq_enter(); //进入中断,可能做关总中断、抢占和互斥等
#ifdef CONFIG_IRQ_DOMAIN
if (lookup)
irq = irq_find_mapping(domain, hwirq);
#endif
/*
* Some hardware gives randomly wrong interrupts. Rather
* than crashing, do something sensible.
*/
if (unlikely(!irq || irq >= nr_irqs)) {
ack_bad_irq(irq);
ret = -EINVAL;
} else {
generic_handle_irq(irq); //调用用户注册的中断处理函数
}
irq_exit(); //完成中断,解除中断保护
set_irq_regs(old_regs);
return ret;
}
generic_handle_irq
- generic_handle_irq最终会执行desc->handle_irq(desc)
- desc->handle_irq(desc)是用户注册的最终的中断处理程序
generic_handle_irq
generic_handle_irq_desc
desc->handle_irq(desc);//最终的中断处理程序
三、中断的申请调用
6. 设备驱动申请中断
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)
7. 查看系统信息
开机后,可以从/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
下面解释一下上面这些参数的含义:
下面我们在中断处理程序中加入log,看一下中断处理程序的调用栈:
1、 以I2C代表的主中断
[ 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、 以串口代表的子中断
TX:
[ 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)
RX:
[ 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)
8. 总结
- 设备树的使用使得配置中断控制寄存器比较灵活,不需要在代码写死
- linux下使用中断,用户不需要关心寄存器清中断的问题
9. 附录
9.1 s3c2416-smdk2416.dts
/dts-v1/;
/ {
compatible = "samsung,s3c2416";
interrupt-parent = <0x1>;
#address-cells = <0x1>;
#size-cells = <0x1>;
model = "SMDK2416";
aliases {
pinctrl0 = "/pinctrl@56000000";
serial0 = "/serial@50000000";
serial1 = "/serial@50004000";
serial2 = "/serial@50008000";
serial3 = "/serial@5000c000";
};
interrupt-controller@4a000000 {
compatible = "samsung,s3c2416-irq";
reg = <0x4a000000 0x100>;
interrupt-controller;
#interrupt-cells = <0x4>;
phandle = <0x1>;
};
pinctrl@56000000 {
reg = <0x56000000 0x1000>;
compatible = "samsung,s3c2416-pinctrl";
wakeup-interrupt-controller {
compatible = "samsung,s3c2410-wakeup-eint";
interrupts = <0x0 0x0 0x0 0x3 0x0 0x0 0x1 0x3 0x0 0x0 0x2 0x3 0x0 0x0 0x3 0x3 0x0 0x0 0x4 0x4 0x0 0x0 0x5 0x4>;
};
gpa {
gpio-controller;
#gpio-cells = <0x2>;
};
gpb {
gpio-controller;
#gpio-cells = <0x2>;
};
gpc {
gpio-controller;
#gpio-cells = <0x2>;
};
gpd {
gpio-controller;
#gpio-cells = <0x2>;
};
gpe {
gpio-controller;
#gpio-cells = <0x2>;
};
gpf {
gpio-controller;
#gpio-cells = <0x2>;
interrupt-controller;
#interrupt-cells = <0x2>;
phandle = <0xd>;
};
gpg {
gpio-controller;
#gpio-cells = <0x2>;
interrupt-controller;
#interrupt-cells = <0x2>;
};
gph {
gpio-controller;
#gpio-cells = <0x2>;
};
gpj {
gpio-controller;
#gpio-cells = <0x2>;
};
gpk {
gpio-controller;
#gpio-cells = <0x2>;
};
gpl {
gpio-controller;
#gpio-cells = <0x2>;
};
gpm {
gpio-controller;
#gpio-cells = <0x2>;
};
uart0-data {
samsung,pins = "gph-0", "gph-1";
samsung,pin-function = <0x2>;
phandle = <0x3>;
};
uart0-fctl {
samsung,pins = "gph-8", "gph-9";
samsung,pin-function = <0x2>;
phandle = <0x4>;
};
uart1-data {
samsung,pins = "gph-2", "gph-3";
samsung,pin-function = <0x2>;
phandle = <0x5>;
};
uart1-fctl {
samsung,pins = "gph-10", "gph-11";
samsung,pin-function = <0x2>;
phandle = <0x6>;
};
uart2-data {
samsung,pins = "gph-4", "gph-5";
samsung,pin-function = <0x2>;
phandle = <0x7>;
};
uart2-fctl {
samsung,pins = "gph-6", "gph-7";
samsung,pin-function = <0x2>;
};
uart3-data {
samsung,pins = "gph-6", "gph-7";
samsung,pin-function = <0x2>;
phandle = <0x8>;
};
extuart-clk {
samsung,pins = "gph-12";
samsung,pin-function = <0x2>;
};
i2c0-bus {
samsung,pins = "gpe-14", "gpe-15";
samsung,pin-function = <0x2>;
};
spi0-bus {
samsung,pins = "gpe-11", "gpe-12", "gpe-13";
samsung,pin-function = <0x2>;
};
sd0-clk {
samsung,pins = "gpe-5";
samsung,pin-function = <0x2>;
phandle = <0x9>;
};
sd0-cmd {
samsung,pins = "gpe-6";
samsung,pin-function = <0x2>;
phandle = <0xa>;
};
sd0-bus1 {
samsung,pins = "gpe-7";
samsung,pin-function = <0x2>;
phandle = <0xb>;
};
sd0-bus4 {
samsung,pins = "gpe-8", "gpe-9", "gpe-10";
samsung,pin-function = <0x2>;
phandle = <0xc>;
};
sd1-cmd {
samsung,pins = "gpl-8";
samsung,pin-function = <0x2>;
phandle = <0xf>;
};
sd1-clk {
samsung,pins = "gpl-9";
samsung,pin-function = <0x2>;
phandle = <0xe>;
};
sd1-bus1 {
samsung,pins = "gpl-0";
samsung,pin-function = <0x2>;
phandle = <0x10>;
};
sd1-bus4 {
samsung,pins = "gpl-1", "gpl-2", "gpl-3";
samsung,pin-function = <0x2>;
phandle = <0x11>;
};
};
timer@51000000 {
compatible = "samsung,s3c2410-pwm";
reg = <0x51000000 0x1000>;
interrupts = <0x0 0x0 0xa 0x3 0x0 0x0 0xb 0x3 0x0 0x0 0xc 0x3 0x0 0x0 0xd 0x3 0x0 0x0 0xe 0x3>;
#pwm-cells = <0x4>;
clocks = <0x2 0x52>;
clock-names = "timers";
};
serial@50000000 {
compatible = "samsung,s3c2440-uart";
reg = <0x50000000 0x4000>;
interrupts = <0x1 0x1c 0x0 0x4 0x1 0x1c 0x1 0x4>;
status = "okay";
clock-names = "uart", "clk_uart_baud2", "clk_uart_baud3";
clocks = <0x2 0x48 0x2 0x48 0x2 0x17>;
pinctrl-names = "default";
pinctrl-0 = <0x3 0x4>;
};
serial@50004000 {
compatible = "samsung,s3c2440-uart";
reg = <0x50004000 0x4000>;
interrupts = <0x1 0x17 0x3 0x4 0x1 0x17 0x4 0x4>;
status = "okay";
clock-names = "uart", "clk_uart_baud2", "clk_uart_baud3";
clocks = <0x2 0x49 0x2 0x49 0x2 0x17>;
pinctrl-names = "default";
pinctrl-0 = <0x5 0x6>;
};
serial@50008000 {
compatible = "samsung,s3c2440-uart";
reg = <0x50008000 0x4000>;
interrupts = <0x1 0xf 0x6 0x4 0x1 0xf 0x7 0x4>;
status = "okay";
clock-names = "uart", "clk_uart_baud2", "clk_uart_baud3";
clocks = <0x2 0x4a 0x2 0x4a 0x2 0x17>;
pinctrl-names = "default";
pinctrl-0 = <0x7>;
};
watchdog@53000000 {
compatible = "samsung,s3c2410-wdt";
reg = <0x53000000 0x100>;
interrupts = <0x1 0x9 0x1b 0x3>;
status = "okay";
clocks = <0x2 0x53>;
clock-names = "watchdog";
};
rtc@57000000 {
compatible = "samsung,s3c2416-rtc";
reg = <0x57000000 0x100>;
interrupts = <0x0 0x0 0x1e 0x3 0x0 0x0 0x8 0x3>;
status = "okay";
clocks = <0x2 0x54>;
clock-names = "rtc";
};
i2c@54000000 {
compatible = "samsung,s3c2440-i2c";
reg = <0x54000000 0x100>;
interrupts = <0x0 0x0 0x1b 0x3>;
#address-cells = <0x1>;
#size-cells = <0x0>;
status = "disabled";
clocks = <0x2 0x4c>;
clock-names = "i2c";
};
cpus {
cpu {
compatible = "arm,arm926ej-s";
};
};
clock-controller@4c000000 {
compatible = "samsung,s3c2416-clock";
reg = <0x4c000000 0x40>;
#clock-cells = <0x1>;
phandle = <0x2>;
};
serial@5000c000 {
compatible = "samsung,s3c2440-uart";
reg = <0x5000c000 0x4000>;
interrupts = <0x1 0x12 0x18 0x4 0x1 0x12 0x19 0x4>;
clock-names = "uart", "clk_uart_baud2", "clk_uart_baud3";
clocks = <0x2 0x4b 0x2 0x4b 0x2 0x17>;
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <0x8>;
};
sdhci@4ac00000 {
compatible = "samsung,s3c6410-sdhci";
reg = <0x4ac00000 0x100>;
interrupts = <0x0 0x0 0x15 0x3>;
clock-names = "hsmmc", "mmc_busclk.0", "mmc_busclk.2";
clocks = <0x2 0x3d 0x2 0x3d 0x2 0x22>;
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <0x9 0xa 0xb 0xc>;
bus-width = <0x4>;
cd-gpios = <0xd 0x1 0x0>;
cd-inverted;
};
sdhci@4a800000 {
compatible = "samsung,s3c6410-sdhci";
reg = <0x4a800000 0x100>;
interrupts = <0x0 0x0 0x14 0x3>;
clock-names = "hsmmc", "mmc_busclk.0", "mmc_busclk.2";
clocks = <0x2 0x3e 0x2 0x3e 0x2 0x23>;
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <0xe 0xf 0x10 0x11>;
bus-width = <0x4>;
broken-cd;
};
memory@30000000 {
device_type = "memory";
reg = <0x30000000 0x4000000>;
};
clocks {
compatible = "simple-bus";
xti {
compatible = "fixed-clock";
clock-frequency = <0xb71b00>;
clock-output-names = "xti";
#clock-cells = <0x0>;
};
};
};
10. 参考资料
基于设备树的TQ2440的中断(1)作者:彭东林
https://www.cnblogs.com/pengdonglin137/p/6847685.html
11. 建议阅读
基于设备树的TQ2440的中断(2)作者:彭东林
https://www.cnblogs.com/pengdonglin137/p/6848851.html
基於tiny4412的Linux內核移植 — 实例学习中断背后的知识(1)
http://www.cnblogs.com/pengdonglin137/p/6349209.html
Linux kernel的中断子系统之(一):综述
Linux kernel的中断子系统之(二):IRQ Domain介绍
linux kernel的中断子系统之(三):IRQ number和中断描述符
linux kernel的中断子系统之(四):High level irq event handler
Linux kernel中断子系统之(五):驱动申请中断API
Linux kernel的中断子系统之(六):ARM中断处理过程
linux kernel的中断子系统之(七):GIC代码分析
http://www.wowotech.net/irq_subsystem/interrupt_subsystem_architecture.html