Xunwei Embedded Linux Driver Development Notes (6) - Interrupt and Interrupt below

Device tree nodes and related functions

one. Interrupt node in the device tree.

If a device needs to use the interrupt function, the developer needs to configure the interrupt attribute information in the device tree, because the device tree is used to describe the hardware information, and then the Linux kernel configures the interrupt function through the interrupt attribute configured in the device tree. Reference binding documentation for device tree breaks:

Documentation/devicetree/bindings/arm/gic.txt

Interrupts are actually very complex, but as developers, we only need to know how to specify interrupts in the device tree and how to get them in code. Other things, such as the interrupt controller in the device tree, are written by the original BSP engineer for us, and we don't need to modify it. For example, in the imx6ull.dtsi file, the inc node is the interrupt controller node of imx6ull, as shown in the following figure:
insert image description here
For example, for GPIO, the GPIO node can also be used as the interrupt controller. In the imx6ull.dtsi file, the GPIO1 node The content of the node is shown in the following figure:
insert image description heredescribe a peripheral interrupt node in the device tree, for example:

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

In this example, we first use pinctrl and gpio subsystem to set this pin as gpio function, because when using interrupt, we need to set the pin as input. Then use the interrupt-parent and interrupts properties to describe the interruption. The attribute value of interrupt-parent is gpio1, that is, to use the interrupt controller of gpio1, why is it gpio1, because the pin uses io18 in gpio1, so the interrupt controller of gpio1 is used. The interrupts attribute sets the interrupt source, why are there two cells in it, because the value of #interrupt-cells in the interrupt controller of gpio1 is 2, as shown in the following figure:

insert image description here
The 18 of the first cells in the example represents the IO 18 of the GPIO1 group. IRQ_TYPE_EDGE_BOTH means both rising and falling edges are valid. IRQ_TYPE_EDGE_BOTH is defined in the file include/linux/irq.h, and the definition is as follows:
insert image description here
So when configuring interrupts in the device tree, only two steps are required. The first step is to set the pin to the gpio function. The second step is to use the interrupt-parent and interrupts properties to describe the interruption.

2. Interrupt related functions

<1> Get the interrupt number related functions
. When writing the driver, you need to use the interrupt number. Each interrupt has an interrupt number. When the interrupt number is used, the interrupt information has been written into the device tree, so it can be extracted from the interupts attribute through the irq_of_parse_and_map function. To the corresponding device number, the function prototype is as follows:

unsigned int irq_of_parse_and_map(struct device_node *dev,int index)

Parameters: dev device node.
index: The index number, the interrupts attribute may contain multiple interrupt information, and the information to be obtained is specified by index.
Return Value: Interrupt number.
If you use GPIO, you can use the gpio_to_irq function to get the interrupt number corresponding to gpio. The function prototype is as follows:

 int gpio_to_irq(unsigned int gpio)

Parameters: gpio: GPIO number to get.
Return value: the corresponding interrupt number of the GPIO.

<2> The application for interrupt function is the same as GPIO. In the Linux kernel, if you want to use an interrupt, you also need to apply for it. The function used to apply for an interrupt is the request_irq function prototype:

 int request_irq( unsigned int irq, i rq_handler_t handler, unsigned long flags, const char *name, void *dev) 

Parameters:
irq: interrupt number to apply for interrupt.
handler: Interrupt handler function, which will be executed when an interrupt occurs.
flags: interrupt flags.
Interrupt flags can be viewed in the file include/linux/interrupt.h, and several commonly used interrupt flags are introduced, as shown in the following figure:
insert image description herename: Interrupt name, after setting, you can see the corresponding in the /proc/interrupts file the interrupt name.
dev: If the flags is set to IRQF_SHARED, dev is used to distinguish different interrupts. Generally, dev is set to the device structure, and dev will be passed to the second parameter of the interrupt handler function irq_handler_t.
Return value: 0 The interrupt application is successful, other negative value interrupt application fails, if it returns -EBUSY, it means that the interrupt has been applied.

< 3 > Interrupt handling function
When applying for an interrupt using the request_irq function, you need to set an interrupt handling function. The format of the interrupt handling function is as follows: The first parameter is the corresponding interrupt number for the interrupt handling function. The second parameter is a pointer to void, that is, a general-purpose pointer, which needs to be consistent with the dev parameter of the request_irq function. Used to distinguish different devices that share interrupts, dev can also point to a device data structure. The return value of the interrupt handler function is of type i rqreturn_t, and the type definition of irqreturn_t is as follows:

enum irqreturn {
    
     
		IRQ_NONE = (0 << 0), 
		IRQ_HANDLED = (1 << 0), 
		IRQ_WAKE_THREAD = (1 << 1), 
}; 
typedef enum irqreturn irqreturn_t;

It can be seen that irqreturn _t is an enumeration type, and there are three return values. Generally, the return value of the interrupt service function uses the following form:

< 4 > free_irq function
After the use of the interrupt is completed, the corresponding interrupt should be released through the free_irq function. If interrupts are not shared, then free_irq removes the interrupt handler and disables interrupts. The free_irq function prototype looks like this:

void free_irq(unsigned int irq,void *dev)

Parameters:
irq: Interrupt to release.
dev: If the interrupt is set to share ( IRQF_SHARED ), this parameter is used to distinguish the specific interrupt. Shared interrupts are disabled only when the last interrupt handler is released.
Return value: None

Key Interrupt Experiment

include header files

#include <linux/interrupt.h>

Modify device tree information

test_key{
    
    
	compatible = "keys";
	pinctrl-names = "default";
	pintrl-0=<&pinctrl_gpio_keys>;//通过宏 把管脚设置为gpio功能
	gpios = <&gpio1 18 GPIO_ACTIVE_LOW>;

}

Modify the driver matching function

const struct of_device_id of_match_table[] = 
{
    
    
    {
    
    .compatible = "keys"},
    {
    
    },
};

Add the following code to beep_probe:

int beep_probe(struct platform_device *pdev){
    
    

    /*查找我们要查找的节点*/
    test_device_node = of_find_node_by_path("/test_key");
    if (test_device_node == NULL)
    {
    
    
        printk("of_find_by_path is erron\n");
        return -1;
    }    //获取gpio的节点
    
    gpio_num = of_get_named_gpio(test_device_node,"gpios",0);//获取gpio
    if (gpio_num<0)
    {
    
        
        printk("of_get_named_gpio is erron\n");
        return -1;
    }
    
    gpio_direction_input(gpio_num);//设置方向为输入

    irq = gpio_to_irq(gpio_num);//获取中断号

    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 erron\n");
        return -1;
    }
    return 0;
}

Burn into the driver: cat /proc/interrupts to see if the interrupt is loaded.
cat /proc/irq/48/spurious to view the number of interruptions.

Method 2: Modify the device tree code

Driver device tree code:

test_key{
    
    
	compatible = "keys";
	pinctrl-names = "default";
	pintrl-0=<&pinctrl_gpio_keys>;//通过宏 把管脚设置为gpio功能
	gpios = <&gpio1 18 GPIO_ACTIVE_LOW>;
//方法2 :在设备树上修改代码
	interrupt-parent = <&gpio1>;
	interrupts =<18 IRQ_TYPE_EDGE_BOTH>;/*FALLING RISING*/	
}

include header files

#include<linux/of_irq.h>

Driver code:

int beep_probe(struct platform_device *pdev){
    
    

    /*查找我们要查找的节点*/
    test_device_node = of_find_node_by_path("/test_key");
    if (test_device_node == NULL)
    {
    
    
        printk("of_find_by_path is erron\n");
        return -1;
    }    //获取gpio的节点
    
     /*获取GPIO的编号*/
    gpio_num = of_get_named_gpio(test_device_node,"gpios",0);//获取gpio
    if (gpio_num<0)
    {
    
        
        printk("of_get_named_gpio is erron\n");
        return -1;
    } 

    /*设置gpio的方向*/
    gpio_direction_input(gpio_num);//设置方向为输入

    //irq = gpio_to_irq(gpio_num);//获取中断号

    /*获取irq的编号*/
    irq = irq_of_parse_and_map(test_device_node,0);//第二种获取中断号的方式,索引号为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 erron\n");
        return -1;
    }
    return 0;
}

Interrupt the following tasklet

1. Tasklet related knowledge points

1. What is a tasklet?
Tasklet is a commonly used method for interrupt processing and interrupt processing, and tasklet is a special soft interrupt. There are also work queues and soft interrupts for processing interrupt context.
2. How to use tasklet to design interrupt context?
Block diagram:
insert image description here
Linux divides the interrupt into two parts, one is the upper part and the other is the lower part. In the upper part, we only deal with urgent things. At the same time, we can call tasklet to start the interrupt context. The more time-consuming ones should be placed in The following will deal with it. After calling the tasklet, the function bound by the tasklet will not be executed immediately, but will be executed after a short indeterminate time after the interruption.
3. Tasklet definition
Tasklet is represented by the tasklet_struct structure, each structure represents a tasklet alone, which is defined in <linux/interrupt.h> as:

struct tasklet_struct {
    
     
		struct tasklet_struct *next; 
		unsigned long state; 
		atomic_t count; 
		void (*func)(unsigned long);
		unsigned long data; 
}; 

1.next: The next tasklet in the linked list, easy to manage and set the tasklet;
2.state: The state of the tasklet
3.count: Indicates whether the tasklet is in the active state, if it is 0, it is in the active state, if not 0, Just in the inactive state
4.void (*func)(unsigned long): The func member in the structure is the binding function of the tasklet, and data is its only parameter.
5.date : The parameter passed when the function is executed

2. Tasklet related functions

<1>tasklet_schedule function
Role: schedule tasklet
function prototype:

void tasklet_schedule(struct tasklet_struct *t) 

Parameters: Pointer to the tasklet_struct structure.

<2>tasklet_init function
Function: Dynamically initialize tasklet (recommended)
function prototype:

void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data); 

Parameters:
*t : Pointer to the tasklet_struct structure.
func : The function that the tasklet is bound to.
data : The parameter passed when the function is executed.

<3>tasklet_kill function
Function: delete a tasklet
function prototype: tasklet_kill(struct tasklet_struct *t)
Parameters: pointer to the tasklet_struct structure
Note: This function will wait for the tasklet to finish executing, and then remove it. This function may cause sleep, so it is forbidden to use it in interrupt context.
3. Use the tasklet design to interrupt the following steps
Step 1: Define a tasklet structure
Step 2: Dynamically initialize the tasklet
Step 3: Write the function bound by the tasklet
Step 4: Call the tasklet before interrupting the above
Step 5: Delete the taskle when unloading the module

practice course

Initialize the structure:

struct tasklet_struct key_test;

Initialize in the probe function: tasklet_init

   //初始化
    tasklet_init(&key_test,test,0);

Interrupt the following work content:

void test(unsigned long data){
    
    
    int i = 100;
    while(i--)
    printk("test_key is %d\n",i);
}

irq_handler_t test_key(int irq, void *args)//中断处理函数
{
    
    
    printk("start\n");

    tasklet_schedule(&key_test); //启动中断下文

    printk("end\n");

    return IRQ_HANDLED;
}

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=324085355&siteId=291194637