本文基于RockPI 4A
单板Linux4.4内核介绍中断申请的常用接口函数。
一、request_threaded_irq()
1、文件
kernel/irq/manage.c
2、定义
int request_threaded_irq(unsigned int irq, irq_handler_t handler,
irq_handler_t thread_fn, unsigned long irqflags,
const char *devname, void *dev_id)
{
...
## 此处解释了共享中断和dev_id的关系
/*
* Sanity-check: shared interrupts must pass in a real dev-ID,
* otherwise we'll have trouble later trying to figure out
* which interrupt is which (messes up the interrupt freeing
* logic etc).
*
* Also IRQF_COND_SUSPEND only makes sense for shared interrupts and
* it cannot be set along with IRQF_NO_SUSPEND.
*/
if (((irqflags & IRQF_SHARED) && !dev_id) ||
(!(irqflags & IRQF_SHARED) && (irqflags & IRQF_COND_SUSPEND)) ||
((irqflags & IRQF_NO_SUSPEND) && (irqflags & IRQF_COND_SUSPEND)))
return -EINVAL;
## 获取irq描述符
desc = irq_to_desc(irq);
...
action->handler = handler;
action->thread_fn = thread_fn;
action->flags = irqflags;
action->name = devname;
action->dev_id = dev_id;
...
## 把描述符加入到IRQ链表,完成中断的动态申请及注册。后续介绍
retval = __setup_irq(irq, desc, action);
...
}
说明:
1)、irq
:要申请的中断号,可通过platform_get_irq()
获取,见“Linux内核中断之获取中断号”。
2)、handler
:中断处理函数,发生中断时,先处理中断处理函数,然后返回IRQ_WAKE_THREAD
唤醒中断处理线程。中断处理函数尽可能简单。
中断处理函数定义:typedef irqreturn_t (*irq_handler_t)(int, void *);
中断返回值如下:
/**
* enum irqreturn
* @IRQ_NONE interrupt was not from this device or was not handled
* @IRQ_HANDLED interrupt was handled by this device
* @IRQ_WAKE_THREAD handler requests to wake the handler thread
*/
enum irqreturn {
IRQ_NONE = (0 << 0), ## 不是该设备中断或中断未处理
IRQ_HANDLED = (1 << 0), ## 中断已处理
IRQ_WAKE_THREAD = (1 << 1), ## 唤醒中断处理线程
};
3)、thread_fn
:中断处理线程,该参数可为NULL。类似于中断处理函数的下半部分。
4)、irqflags
:中断类型标志。
定义文件:include/linux/interrupt.h
,内容如下:
#define IRQF_TRIGGER_NONE 0x00000000
#define IRQF_TRIGGER_RISING 0x00000001 ## 上升沿触发中断
#define IRQF_TRIGGER_FALLING 0x00000002 ## 下降沿沿触发中断
#define IRQF_TRIGGER_HIGH 0x00000004 ## 高电平触发中断
#define IRQF_TRIGGER_LOW 0x00000008 ## 低电平触发中断
...
#define IRQF_SHARED 0x00000080 ## 多设备共享中断
...
5)、devname
:中断名称,可使用cat /proc/interrupts
命令查看。
6)、dev_id
:设备ID,该值唯一。
在使用共享中断时(即设置IRQF_SHARED
),必须传入dev_id
,在中断处理和释放函数中都会使用该参数。
注:
1、request_threaded_irq()
函数可替代request_irq
加tasklet
或workqueue
的方式。
2、对应的中断释放函数为:void free_irq(unsigned int, void *)
,需要和中断申请函数成对出现。
二、request_irq()
1、文件
include/linux/interrupt.h
2、定义
static inline int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev)
{
return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}
说明:
1)、__must_check
:指调用函数一定要处理函数的返回值,否则编译器会给出警告。
2)、request_irq()
函数本质上是中断处理线程thread_fn
为空的request_threaded_irq()
函数。
注:
对应的中断释放函数为:void free_irq(unsigned int, void *)
,需要和中断申请函数成对出现。
三、devm_request_threaded_irq()
1、文件
kernel/irq/devres.c
2、定义
int devm_request_threaded_irq(struct device *dev, unsigned int irq,
irq_handler_t handler, irq_handler_t thread_fn,
unsigned long irqflags, const char *devname,
void *dev_id)
{
...
## 1.申请设备资源
dr = devres_alloc(devm_irq_release, sizeof(struct irq_devres),
GFP_KERNEL);
...
## 2.调用request_threaded_irq()函数
rc = request_threaded_irq(irq, handler, thread_fn, irqflags, devname,
dev_id);
...
## 3.添加到设备资源列表中
devres_add(dev, dr);
...
}
说明:
devm_request_threaded_irq()
本质上还是使用request_threaded_irq()
函数实现中断申请。
两者区别:
1)多了一个dev
参数;
2)在设备驱动卸载时,中断会自动释放;
3)如果想单独释放中断,可使用void devm_free_irq(struct device *dev, unsigned int irq, void *dev_id)
函数。
四、devm_request_irq()
1、文件
include/linux/interrupt.h
2、定义
static inline int __must_check
devm_request_irq(struct device *dev, unsigned int irq, irq_handler_t handler,
unsigned long irqflags, const char *devname, void *dev_id)
{
return devm_request_threaded_irq(dev, irq, handler, NULL, irqflags,
devname, dev_id);
}
devm_request_irq()
函数本质上是中断处理线程thread_fn
为空的devm_request_threaded_irq()
函数。
五、代码举例
1、获取中断号
static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
void *data)
{
...
## 从dts中获取中断号,irq返回值为55
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
...
ret = dw_hdmi_bind(dev, master, data, encoder, iores, irq, plat_data);
...
}
2、申请中断
int dw_hdmi_bind(struct device *dev, struct device *master,
void *data, struct drm_encoder *encoder,
struct resource *iores, int irq,
const struct dw_hdmi_plat_data *plat_data)
{
...
## 申请中断,使用的是IRQF_SHARED,dev_id必须传入参数
ret = devm_request_threaded_irq(dev, irq, dw_hdmi_hardirq,
dw_hdmi_irq, IRQF_SHARED,
dev_name(dev), hdmi);
...
}
3、中断处理函数
static irqreturn_t dw_hdmi_hardirq(int irq, void *dev_id)
{
...
irqreturn_t ret = IRQ_NONE; ## 中断返回值
...
intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0);
if (intr_stat) {
hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
return IRQ_WAKE_THREAD; ## 中断返回值,唤醒中断处理线程
}
...
return ret;
}
4、中断处理线程
static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
{
struct dw_hdmi *hdmi = dev_id;
...
hdcp_stat = hdmi_readb(hdmi, HDMI_A_APIINTSTAT);
if (hdcp_stat) {
if (hdmi->hdcp && hdmi->hdcp->hdcp_isr)
hdmi->hdcp->hdcp_isr(hdmi->hdcp, hdcp_stat);
hdmi_writeb(hdmi, hdcp_stat, HDMI_A_APIINTCLR);
hdmi_writeb(hdmi, 0x00, HDMI_A_APIINTMSK);
}
return IRQ_HANDLED; ## 中断已处理
}
5、查看中断
root@linaro-alip:~# cat /proc/interrupts
CPU0 CPU1 CPU2 CPU3 CPU4 CPU5
14: 0 0 0 0 0 0 GICv3 29 Edge arch_timer
15: 14722 1928 1966 1848 3725 4894 GICv3 30 Edge arch_timer
...
55: 0 0 0 0 0 0 GICv3 55 Level ff940000.hdmi, dw-hdmi-cec
...
网友评论