25 设备树里直接提供gpio口的中断号

时间:2022-04-17 10:30:39

通常是在设备树的设备节点里通过属性提供所用的gpio口,再在设备驱动代码里根据gpiod_to_irq(…)找到gpio口对应的中断号来使用.但有些场合下并不完全适用.如在描述i2c设备/spi设备时, 它们可以直接提供中断号:

struct i2c_client {
unsigned short flags;
unsigned short addr;
...
char name[I2C_NAME_SIZE];
struct i2c_adapter *adapter;
struct device dev;
int irq; //中断号
...
};

struct spi_device {
struct device dev;
struct spi_master *master;
u32 max_speed_hz;
u8 chip_select;
u8 bits_per_word;
u16 mode;
int irq; //中断号
...
};

在设备树里直接提供gpio口中断号的方法:

1 先确认设备树里的gpio控制器具有中断的功能:
pio: pinctrl@01c20800 {
...
gpio-controller; //这表示是一个gpio控制器
#gpio-cells = <3>; //表示使用此gpio控制器时需要用3个参数来指定
// 如用PA17的led: leds-gpios = <&pio 0 17 GPIO_ACTIVIE_HIGH>
interrupt-controller; //表示此设备还是一个中断控制器
#interrupt-cells = <3>; //表示使用此中断控制器的中断号时由3个参数来指定.
// <gpio组 组内io口序号 中断触发标志>
...
};


2 在使用gpio口的spi设备节点里(使用PA12下降沿):
myspidev {
...
interrupt-parent = <&pio>; //指定使用哪一个中断控制器
interrupts = <0 12 IRQ_TYPE_EDGE_FALLING>;
};

3 设备驱动代码里直接访问spi_device的irq成员即可获取中断号.

 gpio口的中断触发标志定义在: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

如实现一个提供gpio口中断的spi设备,此设备使用一个PA12 gpio口.
设备树里的描述:

&spi1 {
#address-cells = <1>;
#size-cells = <0>;

status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&spi1_pins &spi1_cs_pins>;
cs-gpios = <&pio 0 13 GPIO_ACTIVE_HIGH>;

myspidev {
compatible = "myspidev";
reg = <0>;
status = "okay";
spi-max-frequency = <100000>;
interrupt-parent = <&pio>;
interrupts = <0 12 IRQ_TYPE_EDGE_BOTH>;
};
};

设备驱动代码里中断号的使用:

/* mydrv.c */

#include <linux/init.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/interrupt.h>

irqreturn_t irq_func(int irqno, void *arg)
{
printk("irq ...\n");
return IRQ_HANDLED;
}

int myprobe(struct spi_device *spi)
{
int ret;

printk("in myprobe ...\n");
ret = devm_request_threaded_irq(&spi->dev, spi->irq, NULL, irq_func, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING|IRQF_ONESHOT, spi->modalias , spi);

return ret;
}

int myremove(struct spi_device *spi)
{
devm_free_irq(&spi->dev, spi->irq, spi);
printk("in myremove ...\n");
return 0;
}

struct of_device_id ids[] = {
{.compatible = "myspidev"},
{},
};

struct spi_driver mydrv = {
.probe = myprobe,
.remove = myremove,

.driver = {
.owner = THIS_MODULE,
.name = "myspidrv",
.of_match_table = ids,
},
};

module_spi_driver(mydrv);
MODULE_LICENSE("GPL");