Device Tree引发的BSP和驱动变更
有了Device Tree后,大量的板级信息都不再需要,譬如过去经常在arch/arm/plat-xxx和arch/arm/mach-xxx实施的如下事情:
- 注册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
- 注册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);被自动展开。
- 注册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。
- 多个针对不同电路板的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; }
Device Tree引发的BSP和驱动变更相关推荐
- ARM Linux 3.x的设备树(Device Tree)【转】
转自:http://blog.csdn.net/21cnbao/article/details/8457546 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] ARM Devi ...
- ARM Linux 3.x的设备树(Device Tree)
宋宝华 Barry Song <21cnbao@gmail.com> 1. ARM Device Tree起源 Linus Torvalds在2011年3月17日的ARM Linux ...
- linux内核 设备列表,Linux 设备树(Device Tree)(转载)
ARM Device Tree起源 Linus Torvalds在2011年3月17日的ARM Linux邮件列表宣称 "this whole ARM thing is a f*cking ...
- dtb文件linux位置,dtb文件的由来与ARM Linux 3.x的设备树(Device Tree)
1. ARM Device Tree起源 Linus Torvalds在2011年3月17日的ARM Linux邮件列表宣称"this whole ARM thing is a f*ckin ...
- linux驱动开发音频设备驱动,linux驱动开发—基于Device tree机制的驱动编写
摘要:媒介 Device Tree是一种用去描绘硬件的数据布局,类似板级描绘说话,发源于OpenFirmware(OF).正在现在遍及应用的kernel 2.6.x版本中,对分歧仄台.分歧硬件,往] ...
- Android 驱动(12)---Linux DTS(Device Tree Source)设备树详解
Linux DTS(Device Tree Source)设备树详解 Linux DTS(Device Tree Source)设备树详解之一(背景基础知识篇) Linux DTS(Device Tr ...
- Android 驱动开发(14)---深入学习Linux Device Tree
深入学习Linux Device Tree 这个世界需要的是全力以赴,战胜他人先战胜子自己!! Linux Device Tree可描述的信息包括: cpu的数量和类型 内存基地址和大小 总线 外设 ...
- Linux设备模型、平台设备驱动、设备树(device tree)、GPIO子系统以及pinctrl子系统介绍
文章目录 一.Linux设备模型介绍 (1)设备驱动模型总体介绍 (2)设备驱动模型文件表现 (3)设备驱动模型工作原理 [1]总线 [2]设备 [3]驱动 [4]注册流程 二.平台设备驱动介绍 (1 ...
- (DT系列四)驱动加载中, 如何取得device tree中的属性
转载于: http://blog.csdn.net/lichengtongxiazai/article/details/38941933 本文以At91rm9200平台为例,从源码实现的角度来分析驱动 ...
最新文章
- ****题(alb)
- c++函数重载机制实现原理
- chrome开发总结(交互/权限/存储)-爬虫
- RoboGuice入门
- Lead saved query bug
- 我国三大坐标系的区别(西安80、北京54、WGS-84)
- PLSQL 设置布局
- 社交网络分析之关系图(原理+Python代码)
- 实战第二步:如何做一份有针对性的竞品分析
- PDMS二次开发(十二)——螺栓材料统计功能一批bug修复之后再次用sample项目进行验证
- python语句中生成小数的语句_下列 Python 语句的输出结果是 。 print( 数量 {0}, 单价 {1} .format(100,285.6)) print(str.format(...
- python配对t检验_T检验第二篇(SPSS,SAS,R,Python) 配对T检验
- LM317的直流可调稳压电源Multisim仿真设计(附仿真+论文+参考资料)
- ue5-预计算可视性体积(PVS)
- html css标记文本,HTML图像标记和CSS核心基础和文本相关样式
- 如何在内网环境下解决rpm包的问题
- 防火墙NAT综合实验——nat控制,豁免,远程,DMZ区域(带命令)
- git clone时遇到问题:remote: Incorrect username or password ( access token )
- 网络入侵检测系统之Suricata(七)--DDOS流量检测模型
- java读取word文档的复杂表格_poi读取word表格 java POI 如何读取word的表格中的表格...
热门文章
- office、vs各版本软件下载
- 如何使用Hibernate Envers审核数据,包括用户名信息
- 1.2.2 通用的标量输运方程|1.2.3 控制方程的分类(OpenFOAM理论笔记系列)
- leetcode记录-524-通过删除字母匹配到字典里最长单词-双指针
- audio标签,播放音乐
- 2020第十一届蓝桥杯 平面切分 最详细题解
- UESTC 1599 wtmsb【优先队列+排序】
- python表情包多样化图形化聊天室_如何用python绘制一系列三维的逗比风格表情包...
- Tensorflow 2.x源码详解之开宗明义:基本介绍和张量(万文多图)
- 《开源圆桌派》第十一期“冰与火之歌”——如何平衡开源与安全间的天然矛盾?...