Device Tree引发的BSP和驱动变更

有了Device Tree后,大量的板级信息都不再需要,譬如过去经常在arch/arm/plat-xxx和arch/arm/mach-xxx实施的如下事情:

  1. 注册platform_device,绑定resource,即内存、IRQ等板级信息。
    透过Device Tree后,形如
	static struct resource xxx_resources[] = {  
 	[0] = {  
 			.start  =,  
            .end    =,  
            .flags  = IORESOURCE_MEM,  
       	},  
 	[1] = {  
            .start  =,  
            .end    =,  
            .flags  = IORESOURCE_IRQ,  
       	},  
	};  
   
	static struct platform_device xxx_device = {  
         	.name           = "xxx",  
            .id             = -1,  
            .dev            = {  
                                	.platform_data          = &xxx_data,  
                              },  
            .resource       = xxx_resources,  
            .num_resources  = ARRAY_SIZE(xxx_resources),   
	};  

之类的platform_device代码都不再需要,其中platform_device会由kernel自动展开。而这些resource实际来源于.dts中设备结点的reg、interrupts属性。典型地,大多数总线都与“simple_bus”兼容,而在SoC对应的machine的.init_machine成员函数中,调用of_platform_bus_probe(NULL, xxx_of_bus_ids, NULL);即可自动展开所有的platform_device。譬如,假设我们有个XXX SoC,则可在arch/arm/mach-xxx/的板文件中透过如下方式展开.dts中的设备结点对应的platform_device:

	static struct of_device_id xxx_of_bus_ids[] __initdata = {  
         { .compatible = "simple-bus", },  
           {},  
     };  
      
    void __init xxx_mach_init(void)  
     {  
             of_platform_bus_probe(NULL, xxx_of_bus_ids, NULL);  
     }  
      
    #ifdef CONFIG_ARCH_XXX  
      
    DT_MACHINE_START(XXX_DT, "Generic XXX (Flattened Device Tree)").init_machine   = xxx_mach_init,  
             …  
    MACHINE_END  
    #endif  

  1. 注册i2c_board_info,指定IRQ等板级信息。
    形如
  	static struct i2c_board_info __initdata afeb9260_i2c_devices[] = {  
             {  
                     I2C_BOARD_INFO("tlv320aic23", 0x1a),  
             }, {  
                     I2C_BOARD_INFO("fm3130", 0x68),  
             }, {  
                     I2C_BOARD_INFO("24c64", 0x50),  
             },  
     };  

之类的i2c_board_info代码,目前不再需要出现,现在只需要把tlv320aic23、fm3130、24c64这些设备结点填充作为相应的I2C controller结点的子结点即可,类似于前面的

 	i2c@1,0 {  
          compatible = "acme,a1234-i2c-bus";  
          …  
          rtc@58 {  
              compatible = "maxim,ds1338";  
              reg = <58>;  
              interrupts = < 7 3 >;  
          };  
    };  

Device Tree中的I2C client会透过I2C host驱动的probe()函数中调用of_i2c_register_devices(&i2c_dev->adapter);被自动展开。

  1. 注册spi_board_info,指定IRQ等板级信息。
    形如
  	static struct spi_board_info afeb9260_spi_devices[] = {  
             {       /* DataFlash chip */  
                     .modalias       = "mtd_dataflash",  
                     .chip_select    = 1,  
                     .max_speed_hz   = 15 * 1000 * 1000,  
                     .bus_num        = 0,  
             },  
    };  

之类的spi_board_info代码,目前不再需要出现,与I2C类似,现在只需要把mtd_dataflash之类的结点,作为SPI控制器的子结点即可,SPI host驱动的probe函数透过spi_register_master()注册master的时候,会自动展开依附于它的slave。

  1. 多个针对不同电路板的machine,以及相关的callback。

过去,ARM Linux针对不同的电路板会建立由MACHINE_START和MACHINE_END包围起来的针对这个machine的一系列callback,譬如:

 	MACHINE_START(VEXPRESS, "ARM-Versatile Express")  
             .atag_offset    = 0x100,  
             .smp            = smp_ops(vexpress_smp_ops),  
             .map_io         = v2m_map_io,  
             .init_early     = v2m_init_early,  
             .init_irq       = v2m_init_irq,  
             .timer          = &v2m_timer,  
             .handle_irq     = gic_handle_irq,  
             .init_machine   = v2m_init,  
             .restart        = vexpress_restart,  
     MACHINE_END  

这些不同的machine会有不同的MACHINE ID,Uboot在启动Linux内核时会将MACHINE ID存放在r1寄存器,Linux启动时会匹配Bootloader传递的MACHINE ID和MACHINE_START声明的MACHINE ID,然后执行相应machine的一系列初始化函数。

引入Device Tree之后,MACHINE_START变更为DT_MACHINE_START,其中含有一个.dt_compat成员,用于表明相关的machine与.dts中root结点的compatible属性兼容关系。如果Bootloader传递给内核的Device Tree中root结点的compatible属性出现在某machine的.dt_compat表中,相关的machine就与对应的Device Tree匹配,从而引发这一machine的一系列初始化函数被执行。

	 static const char * const v2m_dt_match[] __initconst = {  
             "arm,vexpress",  
             "xen,xenvm",  
             NULL,  
     };  
     DT_MACHINE_START(VEXPRESS_DT, "ARM-Versatile Express")  
             .dt_compat      = v2m_dt_match,  
             .smp            = smp_ops(vexpress_smp_ops),  
             .map_io         = v2m_dt_map_io,  
             .init_early     = v2m_dt_init_early,  
             .init_irq       = v2m_dt_init_irq,  
             .timer          = &v2m_dt_timer,  
             .init_machine   = v2m_dt_init,  
             .handle_irq     = gic_handle_irq,  
             .restart        = vexpress_restart,  
     MACHINE_END  

Linux倡导针对多个SoC、多个电路板的通用DT machine,即一个DT machine的.dt_compat表含多个电路板.dts文件的root结点compatible属性字符串。之后,如果的电路板的初始化序列不一样,可以透过int of_machine_is_compatible(const char *compat) API判断具体的电路板是什么。

譬如arch/arm/mach-exynos/mach-exynos5-dt.c的EXYNOS5_DT machine同时兼容"samsung,exynos5250"和"samsung,exynos5440":

     static char const *exynos5_dt_compat[] __initdata = {  
             "samsung,exynos5250",  
             "samsung,exynos5440",  
             NULL  
     };  
      
     DT_MACHINE_START(EXYNOS5_DT, "SAMSUNG EXYNOS5 (Flattened Device Tree)")  
             /* Maintainer: Kukjin Kim <kgene.kim@samsung.com> */  
             .init_irq       = exynos5_init_irq,  
             .smp            = smp_ops(exynos_smp_ops),  
             .map_io         = exynos5_dt_map_io,  
             .handle_irq     = gic_handle_irq,  
             .init_machine   = exynos5_dt_machine_init,  
             .init_late      = exynos_init_late,  
             .timer          = &exynos4_timer,  
             .dt_compat      = exynos5_dt_compat,  
             .restart        = exynos5_restart,  
             .reserve        = exynos5_reserve,  
     MACHINE_END  

它的.init_machine成员函数就针对不同的machine进行了不同的分支处理:

 	 static void __init exynos5_dt_machine_init(void)  
     {if (of_machine_is_compatible("samsung,exynos5250"))  
                     of_platform_populate(NULL, of_default_bus_match_table,  
                                          exynos5250_auxdata_lookup, NULL);  
             else if (of_machine_is_compatible("samsung,exynos5440"))  
                     of_platform_populate(NULL, of_default_bus_match_table,  
                                          exynos5440_auxdata_lookup, NULL);  
     }  

使用Device Tree后,驱动需要与.dts中描述的设备结点进行匹配,从而引发驱动的probe()函数执行。对于platform_driver而言,需要添加一个OF匹配表,如前文的.dts文件的"acme,a1234-i2c-bus"兼容I2C控制器结点的OF匹配表可以是:

	 static const struct of_device_id a1234_i2c_of_match[] = {  
             { .compatible = "acme,a1234-i2c-bus ", },  
             {},  
     };  
     MODULE_DEVICE_TABLE(of, a1234_i2c_of_match);  
      
     static struct platform_driver i2c_a1234_driver = {  
             .driver = {  
                     .name = "a1234-i2c-bus ",  
                     .owner = THIS_MODULE,  
                     .of_match_table = a1234_i2c_of_match,  
             },  
             .probe = i2c_a1234_probe,  
             .remove = i2c_a1234_remove,  
     };  
     module_platform_driver(i2c_a1234_driver);  

对于I2C和SPI从设备而言,同样也可以透过of_match_table添加匹配的.dts中的相关结点的compatible属性,如sound/soc/codecs/wm8753.c中的:

			.............
    1533 static const struct of_device_id wm8753_of_match[] = {  
    1534         { .compatible = "wlf,wm8753", },  
    1535         { }  
    1536 };  
    1537 MODULE_DEVICE_TABLE(of, wm8753_of_match);  
    1587 static struct spi_driver wm8753_spi_driver = {  
    1588         .driver = {  
    1589                 .name   = "wm8753",  
    1590                 .owner  = THIS_MODULE,  
    1591                 .of_match_table = wm8753_of_match,  
    1592         },  
    1593         .probe          = wm8753_spi_probe,  
    1594         .remove         = wm8753_spi_remove,  
    1595 };  
    1640 static struct i2c_driver wm8753_i2c_driver = {  
    1641         .driver = {  
    1642                 .name = "wm8753",  
    1643                 .owner = THIS_MODULE,  
    1644                 .of_match_table = wm8753_of_match,  
    1645         },  
    1646         .probe =    wm8753_i2c_probe,  
    1647         .remove =   wm8753_i2c_remove,  
    1648         .id_table = wm8753_i2c_id,  
    1649 };  

不过这边有一点需要提醒的是,I2C和SPI外设驱动和Device Tree中设备结点的compatible 属性还有一种弱式匹配方法,就是别名匹配。compatible 属性的组织形式为,,别名其实就是去掉compatible 属性中逗号前的manufacturer前缀。关于这一点,可查看drivers/spi/spi.c的源代码,函数spi_match_device()暴露了更多的细节,如果别名出现在设备spi_driver的id_table里面,或者别名与spi_driver的name字段相同,SPI设备和驱动都可以匹配上:

     static int spi_match_device(struct device *dev, struct device_driver *drv)  
     {  
             const struct spi_device *spi = to_spi_device(dev);  
             const struct spi_driver *sdrv = to_spi_driver(drv);  
      
             /* Attempt an OF style match */  
             if (of_driver_match_device(dev, drv))  
                     return 1;  
      
             /* Then try ACPI */  
             if (acpi_driver_match_device(dev, drv))  
                     return 1;  
      
             if (sdrv->id_table)  
                     return !!spi_match_id(sdrv->id_table, spi);  
      
             return strcmp(spi->modalias, drv->name) == 0;  
     }  
     static const struct spi_device_id *spi_match_id(const struct spi_device_id *id,  
                                                     const struct spi_device *sdev)  
     {  
             while (id->name[0]) {  
                     if (!strcmp(sdev->modalias, id->name))  
                             return id;  
                     id++;  
             }  
             return NULL;  
     }  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
驱动开发工具devicetree是一种用于描述硬件设备的数据结构,通过一种独立于平台的方式来定义和配置设备硬件信息。它被广泛用于Linux内核驱动开发中,特别适用于嵌入式系统领域。 devicetree的主要目的是解决移植性和可维护性的问题。由于不同的硬件平台具有不同的设备组件和接口,驱动程序通常需要根据特定硬件的配置进行开发和调试,这极大地增加了驱动程序的移植难度和维护成本。而使用devicetree驱动程序可以以一种独立于硬件平台的方式编写,减少了大量针对特定硬件修改和移植的工作。 devicetree的工作原理是通过描述设备的层次结构和属性来定义设备之间的连接和功能配置。它以一种树状结构的形式组织设备和接口,每个节点都包含设备的相关信息,如设备类型、中断号、寄存器起始地址、设备引脚和引脚功能等。驱动程序可以根据devicetree中的信息来进行设备的初始化和驱动操作。 使用devicetree有许多优势。首先,它提供了一种清晰和可扩展的设备描述方式,使得驱动程序的开发和调试更加方便和高效。其次,它提供了一种独立于平台的硬件描述语言,使得同一个驱动程序可以在不同的硬件平台上使用。此外,devicetree还可以简化系统引导和设备配置的过程,提高了系统的可维护性和可移植性。 总之,驱动开发工具devicetree是一种用于描述硬件设备的数据结构,它通过一种独立于平台的方式来定义和配置设备硬件信息,解决了驱动程序移植性和可维护性的问题。它在嵌入式系统领域具有广泛的应用,并带来了许多优势,提高了驱动程序的开发效率和系统的可移植性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值