这里介绍imx6u的重点,配置寄存器,设置服务中断函数。
一、中断API函数
每个中断都有一个中断号,linux有一个int变量表示中断号。
1.申请中断
int request_irq( unsigned int irq,
irq_handler_t handler,
unsigned long flags,
const char *name,
void *dev)
- irq: 要申请的中断号
- handler: 中断处理函数
- flags:中断标志
- name: 中断名字,自己设置
- dev: 一般是设备结构体,
- return : 0申请成功,负值申请失败
2.释放中断
coid free_irq(unsigned int irq,
void *dev)
- irq:要释放从中断
- dev:设备结构体
3.中断处理函数
irqreturn_t (*irq_handler) (int, void *)
- 参数一:中断处理函数要处理的中断号
- 参数二:指向void的指针,需要与dev参数一致。
- 返回值:枚举类型
enum irqreturn{
IRQ_NONE = (0<<0),
IRQ_HANDLED = (1<<0),
IRQ_WAKE_THREAD = (1<<1),
};
typedef enum irqretuen irqreturn_t;
4.使能和禁止
void enable_irq(unsigned int irq)
void diasble_irq(unsigned int irq)
输入为要处理的中断号,禁止中断会等到当前正在执行的中断处理函数执行完后才返回。即可能在处理函数中又被其他中断打断。不是立即禁止。
void disable_irq_nosync(unsigned int irq);
此函数调用后立即返回,不用等中断处理函数执行完。
local_irq_enable()
local_irq_disable()
用于全局中断的禁止和打开。
local_irq_save(flags)
local_irq_restore(flags)
这两个函数和上面一样,不过可以保存中断状态在flags。
二、上半部和下半部
Linux内核中断分为上半部和下半部的目的就是实现中断函数的快进快出,对时间敏感、执行速度快的操作放在上半部,时间长的放在下半部。
1.如果要处理的内容不希望被中断打断,放在上半部
2.处理的任务对时间敏感,放在上半部
3.处理的任务与硬件有关,放在上半部
4.其他任务,优先考虑放在下半部
1.软中断
struct softirq_action{
void (*action) (struct softirq_action *);
};
注册软中断处理函数
void open_softirq(int nr, void (*action)(struct softirq_action *))
- nr:要开启的软中断
- action:对应的处理函数
注册好后需要激活
void raise_softirq(unsigned int nr)
软中断必须在编译的时候静态注册
void __init softirq_init(void)
{}
2.tasklet
tasklet是利用软中断实现的另一种下半部机制,建议使用tasklet
struct tasklet_struct{
struct tasklet_struct *next, /*下一个tasklet*/
unsigned long state, /*tasklet状态*/
atomic_t count, /*计数器,对tasklet的引用*/
void (*func) (unsigned long), /*执行的函数*/
unsigned long data, /*函数func的参数*/
};
如果要使用tasklet,需要先定义,再初始化:
void tasklet_init(struct tasklet_struct *t, //要初始化的tasklet
void (*func)(unsigned long), //处理函数 自定义
unsigned long data); //输入函数的数据
可用下面一次性完成tasklet的初始化和定义
DECLARE_TASKLET(name, func, data)
初始化之后还没激活,需要在中断服务函数中调用函数
void tasklet_schedule(struct tasklet_struct *t)
使得tasklet在合适的时间开始运行
使用实例:
//定义
struct tasklet_struct test;
//tasklet处理函数
void test_func(unsigned long data)
{
}
//中断服务函数
irqreturn_t test_handler(int irq, void *dev_id)
{
//激活tasklet
tasklet_schedule(&test);
}
//驱动入口
static int __init irq_init(void)
{
tasklet_init(&test, test_func, data); //初始化tasklet
request_irq(XXX_irq, test_handler, 0, "xxx", &xxx_dev); //注册中断处理函数
}
3.工作队列
是下半部的另一种执行方式,如果你要推后的工作可以睡眠就选择工作队列,否则只能选择软中断或tasklet:
struct work_struct{
atomic_long_t data;
atruct list_head entry;
work_func_t func;
};
每个worker对应一个队列,创建工作挺简单,和tasklet类似,先定义结构体,再初始化,再激活即可
#define INIT_WORK(_work, _func) //初始化
bool schedule_work (struct work_struct *work) //激活
工作列表实例和上面tasklet类似,不做说明
三、设备树中断节点
linux通过读取设备树中断属性来配置中断
用一个例子来解释
gpio5: gpio@020ac000 {
compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
reg = <0x020ac000 0x4000>;
interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
};
第四行表示,中断类型是SPI,出发电平都是IRQ_TYPE_LEVEL_HIGH,不过中断源分别是74、75,74是前16位,75是后16位。
第八行表示,interrupt-controller节点为空,为中断控制器
第九行表示,此中断控制器下的cells大小,为2
fxls8471@le {
compatible = "fsl, fxls8471";
reg = <0x1e>;
position = <0>;
interrupt-parent = <&gpio5>;
interrupts = <0 8>;
};
第五行设置中断控制器,使用gpio5
第六行设置中断信息,0表示GPIO5——IO00, 8是低电平触发。
(为1上升沿触发,2下降沿触发,4高电平触发,8低电平触发)
四、获取中断号
unsigned int ire_of_parse_and_map(struct device_node *dev, //设备节点
int index) //索引号
//返回 中断号
int gpio_to_irq(unsigned int gpio)//要获取的gpio编号
//返回 GPIO对应的中断号
第一个函数可以从interupts属性中,提取对应的设备号。
第二个函数来获取gpio对应的中断号
五、驱动程序
见下章