匿名
未登录
登录
百问网嵌入式Linux wiki
搜索
查看“第五课. 中断系统中的设备树”的源代码
来自百问网嵌入式Linux wiki
名字空间
页面
讨论
更多
更多
页面选项
Read
查看源代码
历史
←
第五课. 中断系统中的设备树
因为以下原因,您没有权限编辑本页:
您所请求的操作仅限于该用户组的用户使用:
用户
您可以查看与复制此页面的源代码。
本套视频面向这些学员: 1. 有Linux驱动开发基础的人, 可以挑感兴趣的章节观看 2. 没有Linux驱动开发基础但是愿意学习的人,请按顺序全部观看, 我会以比较简单的LED驱动为例讲解 3. 完全没有Linux驱动知识,又不想深入学习的人, 比如应用开发人员,不得已要改改驱动, 等全部录完后,我会更新本文档,那时再列出您需要观看的章节。 第一课.设备树的引入与体验 第01节_字符设备驱动程序的三种写法 a. 驱动程序编写有3种方法:传统方法、使用总线设备驱动模型、使用设备树 b. 这3种方法也核心都是一样的: 分配、设置、注册 file_operations结构体 这个结构体中有.open, .read, .write, .ioctl等成员 驱动程序要实现这些成员,在这些成员函数中操作硬件 c. 这3种方法的差别在于:如何指定硬件资源,比如如何指定LED引脚是哪个 c.1 传统方法: 在驱动程序代码中写死硬件资源, 代码简单/不易扩展 c.2 总线设备驱动模型: 把驱动程序分为两部分(platform_driver, platform_device) 在platform_device中指定硬件资源, 在platform_driver中分配/设置/注册 file_operations结构体, 并从platform_device获得硬件资源 特点: 易于扩展,但是有很多冗余代码(每种配置都对应一个platform_device结构体), 硬件有变动时需要重新编译内核或驱动程序。 c.3 使用设备树指定硬件资源: 驱动程序也分为两部分(platform_driver, 设备树*.dts) 在设备树*.dts中指定硬件资源, dts被编译为dtb文件, 在启动单板时会将dtb文件传给内核, 内核根据dtb文件分配/设置/注册多个platform_device platform_driver的编写方法跟"总线设备驱动模型"一样。 特点: 易于扩展,没有冗余代码 硬件有变动时不需要重新编译内核或驱动程序,只需要提供不一样的dtb文件 注: dts - device tree source // 设备树源文件 dtb - device tree blob // 设备树二进制文件, 由dts编译得来 blob - binary large object 第02节_字符设备驱动的传统写法 a. 分配file_operations结构体 b. 设置file_operations结构体 该结构体中有.open,.read,.write等成员, 在这些成员函数中去操作硬件 c. 注册file_operations结构体: register_chrdev(major, name, &fops) d. 入口函数: 调用register_chrdev e. 出口函数: 调用unregister_chrdev 第03节_字符设备驱动的编译测试 第04节_总线设备驱动模型 a. 驱动程序分为platform_device和platform_driver两部分 platform_device : 指定硬件资源 platform_driver : 根据与之匹配的platform_device获得硬件资源, 并分配/设置/注册file_operations b. 如何确定platform_device和platform_driver是否匹配? b.1 platform_device含有name b.2 platform_driver.id_table"可能"指向一个数组, 每个数组项都有name, 表示该platform_driver所能支持的platform_device b.3 platform_driver.driver含有name, 表示该platform_driver所能支持的platform_device b.4 优先比较b.1, b.2两者的name, 若相同则表示互相匹配 b.5 如果platform_driver.id_table为NULL, 则比较b.1, b.3两者的name, 若相同则表示互相匹配 总线设备驱动模型只是一个编程技巧, 它把驱动程序分为"硬件相关的部分"、"变化不大的驱动程序本身", 这个技巧并不是驱动程序的核心, 核心仍然是"分配/设置/注册file_operations" 第05节_使用设备树时对应的驱动编程 a. 使用"总线设备驱动模型"编写的驱动程序分为platform_device和platform_driver两部分 platform_device : 指定硬件资源, 来自.c文件 platform_driver : 根据与之匹配的platform_device获得硬件资源, 并分配/设置/注册file_operations b. 实际上platform_device也可以来自设备树文件.dts dts文件被编译为dtb文件, dtb文件会传给内核, 内核会解析dtb文件, 构造出一系列的device_node结构体, device_node结构体会转换为platform_device结构体 所以: 我们可以在dts文件中指定资源, 不再需要在.c文件中设置platform_device结构体 c. "来自dts的platform_device结构体" 与 "我们写的platform_driver" 的匹配过程: "来自dts的platform_device结构体"里面有成员".dev.of_node", 它里面含有各种属性, 比如 compatible, reg, pin "我们写的platform_driver"里面有成员".driver.of_match_table", 它表示能支持哪些来自于dts的platform_device 如果"of_node中的compatible" 跟 "of_match_table中的compatible" 一致, 就表示匹配成功, 则调用 platform_driver中的probe函数; 在probe函数中, 可以继续从of_node中获得各种属性来确定硬件资源 第06节_只想使用不想深入研究怎么办 这是无水之源、无根之木, 只能寄希望于写驱动程序的人: 提供了文档/示例/程序写得好适配性强 一个写得好的驱动程序, 它会尽量确定所用资源, 只把不能确定的资源留给设备树, 让设备树来指定。 根据原理图确定"驱动程序无法确定的硬件资源", 再在设备树文件中填写对应内容 那么, 所填写内容的格式是什么? a. 看文档: 内核 Documentation/devicetree/bindings/ b. 参考同类型单板的设备树文件 c. 网上搜索 d. 实在没办法时, 只能去研究驱动源码 第二课. 设备树的规范(dts和dtb) 第01节_DTS格式 (1) 语法: Devicetree node格式: [label:] node-name[@unit-address] { [properties definitions] [child nodes] }; Property格式1: [label:] property-name = value; Property格式2(没有值): [label:] property-name; Property取值只有3种: arrays of cells(1个或多个32位数据, 64位数据使用2个32位数据表示), string(字符串), bytestring(1个或多个字节) 示例: a. Arrays of cells : cell就是一个32位的数据 interrupts = <17 0xc>; b. 64bit数据使用2个cell来表示: clock-frequency = <0x00000001 0x00000000>; c. A null-terminated string (有结束符的字符串): compatible = "simple-bus"; d. A bytestring(字节序列) : local-mac-address = [00 00 12 34 56 78]; // 每个byte使用2个16进制数来表示 local-mac-address = [000012345678]; // 每个byte使用2个16进制数来表示 e. 可以是各种值的组合, 用逗号隔开: compatible = "ns16550", "ns8250"; example = <0xf00f0000 19>, "a strange property format"; (2) DTS文件布局(layout): /dts-v1/; [memory reservations] // 格式为: /memreserve/ <address> <length>; / { [property definitions] [child nodes] }; (3) 特殊的、默认的属性: a. 根节点: #address-cells // 在它的子节点的reg属性中, 使用多少个u32整数来描述地址(address) #size-cells // 在它的子节点的reg属性中, 使用多少个u32整数来描述大小(size) compatible // 定义一系列的字符串, 用来指定内核中哪个machine_desc可以支持本设备 // 即这个板子兼容哪些平台 // uImage : smdk2410 smdk2440 mini2440 ==> machine_desc model // 咱这个板子是什么 // 比如有2款板子配置基本一致, 它们的compatible是一样的 // 那么就通过model来分辨这2款板子 b. /memory device_type = "memory"; reg // 用来指定内存的地址、大小 c. /chosen bootargs // 内核command line参数, 跟u-boot中设置的bootargs作用一样 d. /cpus /cpus结点下有1个或多个cpu子结点, cpu子结点中用reg属性用来标明自己是哪一个cpu 所以 /cpus 中有以下2个属性: #address-cells // 在它的子节点的reg属性中, 使用多少个u32整数来描述地址(address) #size-cells // 在它的子节点的reg属性中, 使用多少个u32整数来描述大小(size) // 必须设置为0 e. /cpus/cpu* device_type = "cpu"; reg // 表明自己是哪一个cpu (4) 引用其他节点: a. phandle : // 节点中的phandle属性, 它的取值必须是唯一的(不要跟其他的phandle值一样) pic@10000000 { phandle = <1>; interrupt-controller; }; another-device-node { interrupt-parent = <1>; // 使用phandle值为1来引用上述节点 }; b. label: PIC: pic@10000000 { interrupt-controller; }; another-device-node { interrupt-parent = <&PIC>; // 使用label来引用上述节点, // 使用lable时实际上也是使用phandle来引用, // 在编译dts文件为dtb文件时, 编译器dtc会在dtb中插入phandle属性 }; 官方文档: https://www.devicetree.org/specifications/ 第02节_DTB格式 官方文档: https://www.devicetree.org/specifications/ 内核文档: Documentation/devicetree/booting-without-of.txt DTB文件布局: ------------------------------ base -> | struct boot_param_header | ------------------------------ | (alignment gap) (*) | ------------------------------ | memory reserve map | ------------------------------ | (alignment gap) | ------------------------------ | | | device-tree structure | | | ------------------------------ | (alignment gap) | ------------------------------ | | | device-tree strings | | | -----> ------------------------------ | | --- (base + totalsize) 第三课. 内核对设备树的处理 Linux uses DT data for three major purposes: 1) platform identification, 2) runtime configuration, and 3) device population. 第01节_从源头分析_内核head.S对dtb的简单处理 bootloader启动内核时,会设置r0,r1,r2三个寄存器, r0一般设置为0; r1一般设置为machine id (在使用设备树时该参数没有被使用); r2一般设置ATAGS或DTB的开始地址 bootloader给内核传递的参数时有2种方法: ATAGS 或 DTB 对于ATAGS传参方法, 可以参考我们的"毕业班视频-自己写bootloader" 从www.100ask.net下载页面打开百度网盘, 打开如下目录: 100ask分享的所有文件 006_u-boot_内核_根文件系统(新1期_2期间的衔接) 视频 第002课_从0写bootloader_更深刻理解bootloader a. __lookup_processor_type : 使用汇编指令读取CPU ID, 根据该ID找到对应的proc_info_list结构体(里面含有这类CPU的初始化函数、信息) b. __vet_atags : 判断是否存在可用的ATAGS或DTB c. __create_page_tables : 创建页表, 即创建虚拟地址和物理地址的映射关系 d. __enable_mmu : 使能MMU, 以后就要使用虚拟地址了 e. __mmap_switched : 上述函数里将会调用__mmap_switched f. 把bootloader传入的r2参数, 保存到变量__atags_pointer中 g. 调用C函数start_kernel head.S/head-common.S : 把bootloader传来的r1值, 赋给了C变量: __machine_arch_type 把bootloader传来的r2值, 赋给了C变量: __atags_pointer // dtb首地址 第02节_对设备树中平台信息的处理(选择machine_desc) a. 设备树根结点的compatible属性列出了一系列的字符串, 表示它兼容的单板名, 从"最兼容"到次之 b. 内核中有多个machine_desc, 其中有dt_compat成员, 它指向一个字符串数组, 里面表示该machine_desc支持哪些单板 c. 使用compatile属性的值, 跟 每一个machine_desc.dt_compat 比较, 成绩为"吻合的compatile属性值的位置", 成绩越低越匹配, 对应的machine_desc即被选中 函数调用过程: start_kernel // init/main.c setup_arch(&command_line); // arch/arm/kernel/setup.c mdesc = setup_machine_fdt(__atags_pointer); // arch/arm/kernel/devtree.c early_init_dt_verify(phys_to_virt(dt_phys) // 判断是否有效的dtb, drivers/of/ftd.c initial_boot_params = params; mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach); // 找到最匹配的machine_desc, drivers/of/ftd.c while ((data = get_next_compat(&compat))) { score = of_flat_dt_match(dt_root, compat); if (score > 0 && score < best_score) { best_data = data; best_score = score; } } machine_desc = mdesc; 第03节_对设备树中运行时配置信息的处理 第04节_dtb转换为device_node(unflatten) 第05节_device_node转换为platform_device 第06节_platform_device跟platform_driver的匹配 第四课. u-boot对设备树的支持 第五课. 示例1: 在s3c2440上使用设备树 修改u-boot 修改内核 第六课. 示例2: 在LCD驱动中使用设备树 00-Linux设备树系列-简介 - 飞翔de刺猬 - CSDN博客.html https://blog.csdn.net/lhl_blog/article/details/82387486
返回至
第五课. 中断系统中的设备树
。
导航
导航
WIKI首页
官方店铺
资料下载
交流社区
所有页面
所有产品
MPU-Linux开发板
MCU-单片机开发板
Linux开发系列视频
单片机开发系列视频
所有模块配件
Wiki工具
Wiki工具
特殊页面
页面工具
页面工具
用户页面工具
更多
链入页面
相关更改
页面信息
页面日志