itop4412 设备树 Interrupt
设备树配置
-
在这个目录下面添加:arch/arm/boot/dts/exynos4412-itop-elite.dts
text_key { compatible = "key_text"; pinctrl-names = "default"; pinctrl-0 = <&key_text>; interrupt-parent = <&gpx1>; /* 第一个参数表示gpx1 下面的第一个IO口 第二个参数表示 * 第二个参数表示中断类型 include/dt-bindings/interrupt-controller/irq.h * #define IRQ_TYPE_NONE 0 * #define IRQ_TYPE_EDGE_RISING 1 * #define IRQ_TYPE_EDGE_FALLING 2 * #define IRQ_TYPE_EDGE_BOTH (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING) * #define IRQ_TYPE_LEVEL_HIGH 4 * #define IRQ_TYPE_LEVEL_LOW 8 * 这个文件里面有详细的定义 */ interrupts = <1 IRQ_TYPE_EDGE_RISING>; gpios = <&gpx1 1 GPIO_ACTIVE_LOW>; status = "okay"; };
pinctrl-0 要把它配置成输入模式,这点应该不用过多ed解释
-
详细解释
-
我们在arch/arm/boot/dts/exynos4412-pinctrl.dtsi 这个目录下面
gpx1: gpx1 { gpio-controller; #gpio-cells = <2>; interrupt-controller; interrupt-parent = <&gic>; interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 25 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <2>; };
- 可以看到里面有个interrupt-controller 属性,这个属性代表中断控制器,是个空值,也就是说所有继承它的,都归它管理,继承是什么意思呢?,比如我想要gpx1 下面的外部中断,就要在里面写 interrupt-parent = <&gpx1>;
- interrupt-parent可以看到这个继承gic,这个节点,可以看到gic cells有三个属性,分别是GIC的类型,除了SPI(Shared Peripheral Interrupt)类型的,还有PPI(Private Peripheral Interrupt)类型的。第二个是中断号,这个查数据手册可以看到,最后一个是中断触发的条件。
- #interrupt-cells = <2> 可以看到这个2是,代表继承我,interrupts 里面的属性是两个,第一个值是 引脚号 第二个是中断类型,具体的看上面那个text_key的描述。
-
中断的类型
- 这个部分先放在这…
中断的使用
-
具体用到的函数
-
request_irq()
/* 可以看到它直接调用了一个函数,我们不分析它的源码,只看它的参数 * @Parma1 中断号 * @Parma2 回调函数,原型:irqreturn_t (*irq_handler_t)(int, void *); * @Parma3 中断的类型 * @Parma4 中断的名字 可以用cat /proc/interrupts 查看到名字 * @Parma5 一般是传参的 */ 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); }
-
irq_handler_t()
/* 这个是中断的回调函数 * int 代表中断号,可以根据中断号,来判断 * void* 表示传进来的参数 */ irqreturn_t (*irq_handler_t)(int, void *) irqreturn_t button_irq_hander(int irqno, void *dev) { }
-
free_irq()
// 这个函数一看名字,就知道是释放中断,注意申请之后,一定要释放 void *free_irq(unsigned int, void *);
-
-
使用的流程
先是通过设备树,获取中断号码,然后申请中断,编写回调函数,卸载模块的时候,应该释放
回调函数里面处理的问题,应该快速,时间非常短,不能有数据拷贝的工作,如果有数 据拷贝的工作,
应该用tasklt 或者工作队列,把耗时的操作放到这个地方执行。这样就把中断分为了上半部和下半部。
-
tasklt
这个就是软中断,不允许休眠,先看下结构体里面有啥
struct tasklet_struct { struct tasklet_struct *next; // 指向下一个tasklet_struct unsigned long state; // 状态 atomic_t count; // 计数器,记录tasklet_struct引用次数 void (*func)(unsigned long); // 执行的回调函数 unsigned long data; // 函数参数 };
tasklet_init()
/* 初始化一个结构体,也就是上面的结构体 * @Parma1 要初始化的tasket * @Parma2 执行的回调函数 * @Parma3 函数的参数 */ void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data)
tasklet_schedule()
/* 调度,这个函数主要放在,中断上半段执行完后使用 * @Parma1 tasket结构体指针 */ static inline void tasklet_schedule(struct tasklet_struct *t)
tasklet_kill()
/* 注销tasket对象 * @Parma1 tasket结构体指针 */ void tasklet_kill(struct tasklet_struct *t);
-
工作队列
类似kthread_worker,就是创建一个线程
先看结构体
struct work_struct { atomic_long_t data; struct list_head entry; work_func_t func; #ifdef CONFIG_LOCKDEP struct lockdep_map lockdep_map; #endif };
INIT_WORK()
/* 初始化work ,这个地方的函数原型为: * void work_fun(struct work_struct *work) * { * * } * */ #define INIT_WORK(_work, _func) __INIT_WORK((_work), (_func), 0)
schedule_work()
// 调度器 static inline bool schedule_work(struct work_struct *work)
-