Bootstrap

哪些设备树节点会被转换为 platform_device以及获取中断号

        写驱动程序的时候,需要把驱动分为平台device和平台driver两部分。在平台device中会放入硬件所使用的资源,使用C代码来指定platform_device,当需要修改硬件资源时,比如说想去修改led的引脚时,需要重新修改C文件,重新编译内核。再后来我们使用了设备树,可以在设备树中指定硬件资源。设备树是dts文件,它会转换成dtb文件,最终给内核使用。内核会来解析dtb文件得到一系列的device_node,最后一步,将device_node转换成platform_device.

内核对设备树的处理

 

哪些设备树节点会被转换为 platform_device

1.根节点下有compatible属性的节点会被转化为platform_device.

2.根节点下含有 compatile 属性的子节点,如果一个节点的compatile属性,它的值是这4者之一 :"simple-bus","simple-mfd","isa","arm,amba-bus",那么它的子结点(需含 compatile 属性)也可以转换为 platform_device。

3. 总线 I2C、SPI 节点下的子节点:不转换为 platform_device某个总线下到子节点,应该交给对应的总线驱动程序来处理, 它们不应该被转换为 platform_device。

比如以下的节点中:
        /mytest 会被转换为 platform_device, 因为它兼容"simple-bus"; 它的子节点/mytest/mytest@0 也会被转换为 platform_device
        /i2c 节 点 一 般 表 示 i2c 控 制 器 , 它 会 被 转 换 为 platform_device, 在 内 核 中 有 对 应 的
platform_driver;
        /i2c/at24c02 节点不会被转换为 platform_device, 它被如何处理完全由父节点的 platform_driver 决定, 一般是被创建为一个 i2c_client。 类似的也有/spi 节点, 它一般也是用来表示 SPI 控制器, 它会被转换为 platform_device, 在内核中有对应的 platform_driver;
        /spi/flash@0 节点不会被转换为 platform_device, 它被如何处理完全由父节点的 platform_driver
决定, 一般是被创建为一个 spi_device。
/ {
          mytest {
              compatile = "mytest", "simple-bus";
              mytest@0 {
                    compatile = "mytest_0";
              };
          };
          
          i2c {
              compatile = "samsung,i2c";
              at24c02 {
                    compatile = "at24c02";                      
              };
          };

          spi {
              compatile = "samsung,spi";              
              flash@0 {
                    compatible = "winbond,w25q32dw";
                    spi-max-frequency = <25000000>;
                    reg = <0>;
                  };
          };
      };

        对于根目录下的第一级子节点,比如说I2c,它应该转换成platform_device,它会对应有一个platform_driver,当匹配时,driver中的probe函数就会被调用,对于I2c下面的这些子节点,比如说AT24C02应该交给probe函数来处理。如果仍然把这些子节点转成platfrom_device的话,就不太合适了。

2. device_node如何转换成platform_device,内核处理设备树的函数调用过程,这里不去分析;我们只需要得到如下结论:

首先来看一下,platform_device结构体的定义:

struct platform_device {
    const char    *name;
    int        id;
    bool        id_auto;
    struct device    dev;
    u32        num_resources;
    struct resource    *resource;   //指向一个动态生成的数组,数组的大小由num_resources决定。意味着平台设备中可以有0项,或1项,或多项资源。
                                 //这些资源来自设备树中的reg属性,如果设备树中设有reg属性,那么在对应的platform_device中就会有一项资源,
                                 //用来表示它所占用的内存空间。还可以指定一些中断属性,那么在platform_device就会表示它占据哪个中断号。
                                 //资源的类别:I/O资源,内存资源,中断资源,这3中资源都可以在设备树中指定。这些资源会从device_node转换得到。
                                 
    const struct platform_device_id    *id_entry;
    char *driver_override; /* Driver name to force a match */
    /* MFD cell pointer */
    struct mfd_cell *mfd_cell;
    /* arch specific additions */
    struct pdev_archdata    archdata;
};

platform_device中含有resource数组, 它来自device_node的reg, interrupts属性;

在设备树中还有一些属性,它们并不对应什么资源,那么这些属性保存在哪里?

platform_device结构体中有struct device结构体,在它里面有一项of_node,它指向device_node结构体,因此以后想得到某个属性时,可以从platform_device中先找到dev,再找到of_node,从of_node中读取那些属性值。

struct device {

  struct device_node *of_node; /* associated device tree node */

};


platform_device.dev.of_node指向device_node, 可以通过它获得其他属性。

在代码中获得中断:

        1 对于 platform_device

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

unsigned int irq_of_parse_and_map(struct device_node *dev,
 int index)
 
        dev: 设备节点。
        index:索引号, interrupts 属性可能包含多条中断信息,通过 index 指定要获取的信息。
返回值:中断号。
        如果使用 GPIO 的话,可以使用 gpio_to_irq 函数来获取 gpio 对应的中断号,函数原型如
下:
int gpio_to_irq(unsigned int gpio)

    对于.c文件创建的普通的platform_device,  可以使用platform_get_resource获取相关资源,切记不适用与设备树的,设备树的节点会转换为platform_device,但是不会构造资源resource , 一 个 节 点 能 被 转 换 为 platform_device , 如 果 它 的 设 备 树 里 指 定 了 中 断 属 性 , 那 么 可 以 从 platform_device 中获得“中断资源”,函数如下,可以使用下列函数获得 IORESOURCE_IRQ 资源,即中断号:

/**
 * platform_get_resource - get a resource for a device
 * @dev: platform device
 * @type: resource type   // 取哪类资源?IORESOURCE_MEM、IORESOURCE_REG
*                      // IORESOURCE_IRQ等
 * @num: resource index  // 这类资源中的哪一个?
 */
struct resource *platform_get_resource(struct platform_device *dev,
				       unsigned int type, unsigned int num);
对于 platform_get_resource函数举例:
static int gpio_key_probe(struct platform_device *pdev)
{
	struct resource *res;
	int err;
/*
	if (!pdev->dev.of_node)	//如果不是由设备树构造的platform_device
	{
		res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
		if (res == NULL)
		{
			printk("gpio_key_probe platform_get_resource err\n");
			return -EINVAL;
		}

		irq = res->start;
	}
	else    //是由设备树构造的platform_device
	{
		irq = of_irq_get(pdev->dev.of_node, 0);
	}
*/
    irq = platform_get_irq(pdev,0);


	printk("get irq = %d\n", irq);
	
	err = request_irq(irq, gpio_key_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "100ask_gpio_key", NULL);
        
    return err;
    
}





在/home/ysy/linux/IMX6ULL/linux/linux-imx-rel_imx_4.1.15_2.1.0_ga/drivers/base/platform.c有platform_get_irq函数,相当于上面的函数,因此,在上述程序中,一个函数就可以实现。
int platform_get_irq(struct platform_device *dev, unsigned int num)
{
#ifdef CONFIG_SPARC
	/* sparc does not have irqs represented as IORESOURCE_IRQ resources */
	if (!dev || num >= dev->archdata.num_irqs)
		return -ENXIO;
	return dev->archdata.irqs[num];
#else
	struct resource *r;
	if (IS_ENABLED(CONFIG_OF_IRQ) && dev->dev.of_node) {
		int ret;

		ret = of_irq_get(dev->dev.of_node, num);
		if (ret >= 0 || ret == -EPROBE_DEFER)
			return ret;
	}

	r = platform_get_resource(dev, IORESOURCE_IRQ, num);
	/*
	 * The resources may pass trigger flags to the irqs that need
	 * to be set up. It so happens that the trigger flags for
	 * IORESOURCE_BITS correspond 1-to-1 to the IRQF_TRIGGER*
	 * settings.
	 */
	if (r && r->flags & IORESOURCE_BITS)
		irqd_set_trigger_type(irq_get_irq_data(r->start),
				      r->flags & IORESOURCE_BITS);

	return r ? r->start : -ENXIO;
#endif
}
EXPORT_SYMBOL_GPL(platform_get_irq);
  gpio_keys_100ask {
        compatible = "100ask,gpio_key";
		key-gpios = <&gpio1 18 GPIO_ACTIVE_HIGH>;
        //interrupt-parent = <&gpio1>;		
		//interrupts = <18 IRQ_TYPE_EDGE_BOTH>;
    };

2 对于 I2C 设备、SPI 设备  
         
        对于 I2C 设备节点,I2C 总线驱动在处理设备树里的 I2C 子节点时,也会处理其中的中断信息。一个 I2C 设备会被转换为一个 i2c_client 结构体,中断号会保存在 i2c_client 的 irq 成员里,代码如下 (drivers/i2c/i2c-core.c):

        对于 SPI 设备节点,SPI 总线驱动在处理设备树里的 SPI 子节点时,也会处理其中的中断信息。一个 SPI 设备会被转换为一个 spi_device 结构体,中断号会保存在 spi_device 的 irq 成员里,代码如下 (drivers/spi/spi.c):

 3 调用 of_irq_get 获得中断号

        如果你的设备节点既不能转换为 platform_device,它也不是 I2C 设备,不是 SPI 设备,那么在驱动程序中可以自行调用 of_irq_get 函数去解析设备树,得到中断号。使用这个函数需要在设备树中指定那些中断信息,比较麻烦复杂 ,可以把gpio直接转化为中断号,即函数gpiod_to_irq().
4 对于 GPIO
        参考:drivers/input/keyboard/gpio_keys.c 可以使用 gpio_to_irq 或 gpiod_to_irq 获得中断号。 举例,假设在设备树中有如下节点:
gpio-keys {
		compatible = "gpio-keys";
		pinctrl-names = "default";

		user {
				label = "User Button";
				gpios = <&gpio5 1 GPIO_ACTIVE_HIGH>;
				gpio-key,wakeup;
				linux,code = <KEY_1>;
		};
};

那么可以使用下面的函数获得引脚和flag:

button->gpio = of_get_gpio_flags(pp, 0, &flags);
bdata->gpiod = gpio_to_desc(button->gpio);
再去使用 gpiod_to_irq 获得中断号:
irq = gpiod_to_irq(bdata->gpiod);

举例:

static int sr501_probe(struct platform_device *pdev)
{
	/* 1. 获得硬件信息 */
	sr501_gpio = gpiod_get(&pdev->dev, NULL, 0);
	gpiod_direction_input(sr501_gpio);

	irq = gpiod_to_irq(sr501_gpio);
	request_irq(irq, sr501_isr, IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, "sr501", NULL);

	/* 2. device_create */
	device_create(sr501_class, NULL, MKDEV(major, 0), NULL, "/dev/sr501");

	return 0;
}
static int gpio_key_probe(struct platform_device *pdev)

{

	//struct resource *res;

	int err;

/*

	if (!pdev->dev.of_node)	

	{

		res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);

		if (res == NULL)

		{

			printk("gpio_key_probe platform_get_resource err\n");

			return -EINVAL;

		}



		irq = res->start;

	}

	else

	{

		irq = of_irq_get(pdev->dev.of_node, 0);

	}

*/

	//irq = platform_get_irq(pdev,0);

	sr04_trig = gpiod_get(&pdev->dev, "key", GPIOD_IN);

	irq = gpiod_to_irq(sr04_trig);

	printk("get irq = %d\n", irq);

	

	err = request_irq(irq, gpio_key_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "100ask_gpio_key", NULL);

        

    return err;

    

}


    gpio_keys_100ask {
        compatible = "100ask,gpio_key";
		key-gpios = <&gpio1 18 GPIO_ACTIVE_HIGH>;
        //interrupt-parent = <&gpio1>;		
		//interrupts = <18 IRQ_TYPE_EDGE_BOTH>;
    };

;