设备树中的中断节点以及相关函数-28

设备树中的中断节点以及相关函数

设备树中的中断节点。

​ 如果一个设备需要用到中断功能,开发人员就需要在设备树中配置好中断属性信息,因为设备树是用来描述硬件信息的,然后Linux内核通过设备树配置的中断属性来配置中断功能。

设备树中断的参考绑定文档:

Documentation/devicetree/bindings/arm/gic.txt

​ 中断实际上是非常复杂的,但是作为开发人员,我们只需要关系怎么在设备树中指定中断,怎么在代码中获得中断就可以。其他的事情,比如设备树中的中断控制器,这些都是由原厂的 BSP工程师帮我们写好了,我们不需要来修改他。
比如,在imx6ull.dtsi文件,其中的inc节点就是imx6ull的中断控制器节点,如下所示:

intc : interrupt-controller@eea0100e {
    compatible = "arm, cortex-a7-gic";
    #interrupt-cells = <3>;
    interrupt-controller;
   	reg = 	<exe0a0100e ex100e>,
    		<oxeea0200e 0x100>;
};

比如,对于GPI0来说,GPI0的节点也可以作为中断控制器,在 imxbull.dtsi.文件中 GPI01的节点内容如下所示:

gpio1: gpio@e209ceee {
    compatible = "fsl,imx6ul-gpio","fs1,imx35-gpio";
    reg = <ex0209ce0e ex4000>;
    interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>,
    				<GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
    gpio-controller;
    #gpio-cells = <2>;
    interrupt-controller;
    #interrupt-cells =<2>;
};

​ 这些工作都是由原厂的BSP工程师来帮我们写好的,并不需要我们来写。除非将来你有机会去原厂工作,否则我们不会从头开始写一个设备树文件的。分工是非常明确的,我们需要关注的点是怎么在设备树里面描述一个外设的中断节点,我们来看一个例子。

key {
        #address-cells = <1>;
        #size-cells =<1>;
        compatible = "key";
        pinctrl-names = "default";
        pinctrl-0 =<&pinctrl_key>;
        key-gpio = <&gpio1 18 GPIO_ACTIVE_LOW>; /* KEYO */
        interrupt-parent = <&gpio1>;
        interrupts = <18 IRQ_TYPE_EDGE_BOTH>;/* FALLING RISING */
        status ="okay";
}

​ 在这个例子中,我们先使用pinctrl和 gpio子系统把这个引脚设置为了 gpio.功能,因为我们在使用中断的时候需要把引脚设置成输入。然后使用interrupt-parent和interrupts属性来描述中断。interrupt-parent的属性值是gpio1,也就是他的要使用gpiol这个中断控制器,为什么是 gpiol呢,因为我们的引脚使用的是gpiol里面的io18,所以我们使用的是gpio1这个中断控制器。interrupts属性设置的是中断源,为什么里面是俩个cells呢,因为我们在 gpiol这个中断控制器里面#interrupt-cells 的值为2,如下所示:

gpio1: gpio@0209ceee {
        compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
        reg = <exe209ce0e ex4000>;
        interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>,<GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
        gpio-controller;
        #gpio-cells = <2>;
        interrupt-controller;
        #interrupt-cells = <2>;
};

​ 例子中的第一个 cells 的18表示GHIO1组的18号I0。IRQ_TYPE_EDGE_BOTH表示上升沿和下降沿同时有效。IRQ_TYPE_EDGE_BOTH定义在文件 include/linux/irq.h中,定义如下

enum {
	IRQ_TYPE_NONE		= 0x00000000,
	IRQ_TYPE_EDGE_RISING	= 0x00000001,
	IRQ_TYPE_EDGE_FALLING	= 0x00000002,
	IRQ_TYPE_EDGE_BOTH	= (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING),
	IRQ_TYPE_LEVEL_HIGH	= 0x00000004,
	IRQ_TYPE_LEVEL_LOW	= 0x00000008,
	IRQ_TYPE_LEVEL_MASK	= (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH),
	IRQ_TYPE_SENSE_MASK	= 0x0000000f,
	IRQ_TYPE_DEFAULT	= IRQ_TYPE_SENSE_MASK,

	IRQ_TYPE_PROBE		= 0x00000010,

	IRQ_LEVEL		= (1 <<  8),
	IRQ_PER_CPU		= (1 <<  9),
	IRQ_NOPROBE		= (1 << 10),
	IRQ_NOREQUEST		= (1 << 11),
	IRQ_NOAUTOEN		= (1 << 12),
	IRQ_NO_BALANCING	= (1 << 13),
	IRQ_MOVE_PCNTXT		= (1 << 14),
	IRQ_NESTED_THREAD	= (1 << 15),
	IRQ_NOTHREAD		= (1 << 16),
	IRQ_PER_CPU_DEVID	= (1 << 17),
	IRQ_IS_POLLED		= (1 << 18),
	IRQ_DISABLE_UNLAZY	= (1 << 19),
};

所以我们在设备树里面配置中断的时候只需要俩个步骤即可,

  • 第一个步骤是把管脚设置为gpio功能。
  • 第二个步骤是使用interrupt-parent和 interrupts属性来描述中断。

中断相关函数

获取中断号相关函数

​ 编写驱动的时候需要用到中断号,每一个中断都有中断号,我们用到中断号,中断信息已经写到了设备树里面,因此可以通过 irq_of_parse_and_map函数从interupts属性中提取到对应的设备号,

函数原型如下:

#include <linux/of_irq.h>
unsigned int irq of_parse_and_map(struct device_node *dev,int index)

参数:

  • dev设备节点。
  • index:索引号,interrupts属性可能包含多条中断信息,通过 index指定要获取的信息。
  • 返回值:中断号。

如果使用 GPI0的话,可以使用 gpio_to_irq函数来获取 gpio对应的中断号,

函数原型如下:

#include <linux/gpio.h>
int gpio_to_irq(unsigned int gpio)

参数:

  • gpio:要获取的GPI0 编号。
  • 返回值: GPIO对应的中断号。

申请中断函数

同GPIO一样,在 Linux内核里面,如果我们要使用某个中断也是需要申请的,申请中断我们使用的函数是request_ira

函数原型:

#include <linux/interrupt.h>
int request_irq(unsigned int irq,irq_handler_t handler,unsigned long flags,const char *name,void *dev)

参数:

  • irq:要申请中断的中断号。
  • handler:中断处理函数,当中断发生以后就会执行此中断处理函数。
  • flags:中断标志。

中断标识可以在文件 include/linux/interrupt.h里面查看所有的中断标志,这里我们介绍几个常用的中断标志,如下所示:

#define IRQF_DISABLED 			0x00000020 /*中断禁止*/

#define IRQF_SAMPLE_RANDOM  	0x00000040    /*供系统产生随机数使用*/

#define IRQF_SHARED      		0x00000080 /*在设备之间可共享*/

#define IRQF_PROBE_SHARED   	0x00000100/*探测共享中断*/

#define IRQF_TIMER       		0x00000200/*专用于时钟中断*/

#define IRQF_PERCPU      		0x00000400/*每CPU周期执行中断*/

#define IRQF_NOBALANCING 		0x00000800/*复位中断*/

#define IRQF_IRQPOLL     		0x00001000/*共享中断中根据注册时间判断*/

#define IRQF_ONESHOT     		0x00002000/*硬件中断处理完后触发*/

#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_TRIGGER_MASK   (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | \

IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)

#define IRQF_TRIGGER_PROBE  	0x00000010/*触发式检测中断*/
  • name:中断名字,设置以后可以在/proc/interrupts 文件中看到对应的中断名字。
  • dev:如果将flags设置为 IRQF_SHARED的话,dev用来区分不同的中断,一般情况下将dev设置为设备结构体,dev会传递给中断处理函数 irg_handler_t的第二个参数。
  • 返回值:0中断申请成功,其他负值中断申请失败,如果返回-EBUSY的话表示中断已经被申请了。

中断处理函数

使用request_ing函数中请中断的时候需要设置中断处理函数,中断处理

函数原型:

irqreturn_t (*irq_handler_t) (int, void *)
  • 第一个参数是要中断处理函数要相应的中断号。
  • 第二个参数是一个指向void 的指针,也就是个通用指针,需要与request_irq函数的 dev参数保持一致。用于区分共享中断的不同设备, devy也可以指向设备数据结构。中断处理函数的返回值为 irgreturn_t类型,irgreturn_t类型定义如下所示
enum irgreturn {
    IRQ NONE =(0<<0),
    IRQ HANDLED =(1<< 0),
    IRQ WAKE_THREAD=(1<<1),
};
typedef enum irqreturn irqreturn_t;

可以看出 irgreturn_t是个枚举类型,一共有三种返回值。一般中断服务函数返回值使用如下形式:

return IRQ_RETVAL(IRQ_HANDLED)

  • free_irq函数

中断使用完成以后就要通过 free_irq函数释放掉相应的中断。如果中断不是共享的,那么free_irq会删除中断处理函数并且禁止中断。

函数原型:

void free _irq(unsigned int irq,void*dev)

参数:

  • irq:要释放的中断。
  • dev:如果中断设置为共享(IRQF_SHARED)的话,此参数用来区分具体的中断。共享中断只有在释放最后中断处理函数的时候才会被禁止掉。
  • 返回值:无。

  • 中断成功后可以通过下面的指令查看
cat /proc/irq/申请的中断号/spurious   

流程

查找节点 》》 获取gpio编号 》》 设置gpio方向 》》 获取iqr号 》》 申请iqr

代码

  • driver1.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>

int gpio_num;
int irq = 0;
struct device_node *test_device_node;
struct property *test_node_property;


struct of_device_id of_match_table[] = {

    {.compatible = "test_keys"},
    {}

};

irq_handler_t test_key(int irq, void *args){

    printk("test_key \n");

    return IRQ_HANDLED;

}

int beep_probe(struct platform_device *pdev){

    int ret = 0;


    printk("beep_probe 匹配成功了 \n");
    test_device_node = of_find_node_by_path("/test_key");
    if (test_device_node == NULL)
    {
        printk("of_find_node_by_path is error \n");
        return -1;
    }
    
    gpio_num = of_get_named_gpio(test_device_node, "touch-gpio", 0);
    if (gpio_num < 0)
    {
        printk("of_get_named_gpio is error \n");
        return -1;
    }
    
    gpio_direction_input(gpio_num);

    
    //irq = gpio_to_irq(gpio_num);

    irq = irq_of_parse_and_map(test_device_node, 0);

    printk("irq is %d \n", irq);

    ret = request_irq(irq, test_key, IRQF_TRIGGER_RISING, "test_key", NULL);
    if (ret < 0)
    {
        printk("request_irq is error \n");
        return ret;
    }
    

    return 0;
}

int beep_remove(struct platform_device *pdev){
    printk("beep_remove \n");
    return 0;
}

const struct platform_device_id  beep_idtable = {
    .name = "test_keys"
};

struct platform_driver beep_device =
{
    .probe = beep_probe,
    .remove = beep_remove,
    .driver = {
        .name = "123",
        .owner = THIS_MODULE,
        .of_match_table = of_match_table
    },
    .id_table = &beep_idtable
};

static int beep_driver_init(void){
    printk(KERN_EMERG "hello world enter \n");
    int ret = 0;
    ret = platform_driver_register(&beep_device);
    if (ret < 0)
    {
        printk("platform_driver_register 失败\n");
    }

    printk("platform_driver_register ok\n");
    
    return 0;
}

static void beep_driver_exit(void){
    printk(KERN_EMERG "hello world exit! \n");
    free_irq(irq, NULL);
    platform_driver_unregister(&beep_device);
}

module_init(beep_driver_init);
module_exit(beep_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("LIYU");
  • 设备树itop-3399.dtsi 找到下面的代码
&i2c1 {
	status = "okay";
	i2c-scl-rising-time-ns = <150>;/*<140>;*/
	i2c-scl-falling-time-ns = <30>;
	clock-frequency = <100000>;

#if 0
	es8316: es8316@11 {
		#sound-dai-cells = <0>;
		compatible = "everest,es8316";
		reg = <0x11>;
		clocks = <&cru SCLK_I2S_8CH_OUT>;
		clock-names = "mclk";
		pinctrl-names = "default";
		pinctrl-0 = <&i2s_8ch_mclk>;
		spk-con-gpio = <&gpio0 RK_PB3 GPIO_ACTIVE_HIGH>;
	};

#endif
	
	/* add by cym 20190516  ybb*/
	
		ft5x06@38 {
        	compatible = "edt,ft5x0x_ts";
                reg = <0x38>;
		//touch-gpio = <&gpio1 20 IRQ_TYPE_EDGE_RISING>;
		//interrupt-parent = <&gpio1>;
        //        interrupts = <20 IRQ_TYPE_LEVEL_LOW>;
		reset-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>;
#if defined(LCD_TYPE_9_7)
                touch_type = <0>;       /*0:9.7, 1: 7.0*/
#elif defined(LCD_TYPE_7_0)
		touch_type = <1>;
#elif defined(LCD_TYPE_MIPI_7_0)
		touch_type = <1>;
#endif
        };

将下面的代码注释

	touch-gpio = <&gpio1 20 IRQ_TYPE_EDGE_RISING>;
	interrupt-parent = <&gpio1>;
    interrupts = <20 IRQ_TYPE_LEVEL_LOW>;

然后在根节点中添加下面的代码

test_key {
		compatible = "test_keys";
		pinctrl-names = "default";
		pinctrl-0 = <&i2c1_xfer>;
        reg = <0x38>;
		touch-gpio = <&gpio1 20 IRQ_TYPE_EDGE_RISING>;
		interrupt-parent = <&gpio1>;
        interrupts = <20 IRQ_TYPE_LEVEL_LOW>;
	
	};
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值