诞生树是每一个Linux驱动工程师都必须附近的一个常识点,有许多之前做单片机的石友刚战争Linux驱动时,会一脸懵!
其实诞生树的使用并莫得全球想像的那么复杂,对于大部单干程师来说,只有会修改即可。
许多粉丝留言说,但愿彭真诚提供一个诞生树到驱动明白的实例。
必须安排!
在学习诞生树之前,全球一定要搞明晰什么是platform总线,请详备学习底下这篇著述:
《手把手教Linux驱动10-platform总线详解》
对于诞生树表面部天职容请学习底下这篇著述:
《手把手教linux驱动11-linux诞生驱动长入模子》
对于驱动基础著述,不错去B站学习一口君的初学视频:
《从学Linux驱动初学视频》
https://www.bilibili.com/video/BV1d5411A7VJ?spm_id_from=333.999.0.0
有了这些基础常识后,咱们就不错来编写一个诞生树的实例,
底下彭真诚就给全球教悔若何我方添加一个诞生树节点,并如安在驱动中索要出诞生树的信息。
老轨则,代码从0入手编写,何况一道考证通过,并共享给全球。
二、测试平台本次测试在设备板上操作,操作环境如下:
1. 编译环境ubuntu 16.042. 交叉编译器具
root@ubuntu:/home/peng/linux-3.14# arm-none-linux-gnueabi-gcc -v Using built-in specs. COLLECT_GCC=arm-none-linux-gnueabi-gcc COLLECT_LTO_WRAPPER=/home/peng/toolchain/gcc-4.6.4/bin/../libexec/gcc/arm-arm1176jzfssf-linux-gnueabi/4.6.4/lto-wrapper Target: arm-arm1176jzfssf-linux-gnueabi ……………… gcc version 4.6.4 (crosstool-NG hg+default-2685dfa9de14 - tc0002)3. 设备板
设备板:fs4412 soc:exynos44124. 内核版块
Linux kernel 3.14.0三、内核明白诞生树一般历程
系统启动后,uboot会从网罗大概flash、sd卡中读取诞生树文献(具体由uboot敕令给出),
指令linux内核启动后,会把诞生树镜像保存到的内存地址传递给Linux内核,Linux内核会明白诞生树镜像,从诞生树中索要硬件信息并逐个驱动化。
其中诞生树信息会被辗转成struct platform_device类型变量。
而驱动要明白诞生树,必须界说 struct platform_driver类型结构体变量,并通过函数platform_driver_register()注册。
这两者都会注册到platform总线,当驱动和诞生树节点匹配得手后,就调用 struct platform_driver中.probe门径。
其中诞生树节点会封装在struct device_node结构体变量中 各个属性信息会封装在 struct property结构体变量中, 他们与struct platform_device结构体之间关系如下:
以下是一口君编写的驱动架构,
咱们只需要将测试代码填充到hello_probe()中即可:
static int hello_probe(struct platform_device *pdev) { printk("match ok \n"); //明白代码编写 return 0; } static int hello_remove(struct platform_device *pdev) { printk("hello_remove \n"); return 0; } static struct of_device_id beep_table[] = { {.compatible = "yikoulinux"}, }; static struct platform_driver hello_driver = { .probe = hello_probe, .driver.name = "duang", .remove = hello_remove, 亚洲综合欧美色五月俺也去 .driver = { .name = "yikoupeng", .of_match_table = beep_table, }, }; static int hello_init(void) { printk("hello_init \n"); return platform_driver_register(&hello_driver); } static void hello_exit(void) { printk("hello_exit \n"); platform_driver_unregister(&hello_driver); return; } MODULE_LICENSE("GPL"); module_init(hello_init); module_exit(hello_exit);五、诞生树节点
底下是给出的诞生树信息:
yikou_node{ compatible = "yikoulinux"; reg = <0x114000a0 0x4 0x139D0000 0x20>; reg-names = "peng"; interrupt-parent=<&gpx1>; interrupts =<1 2>,<2 2>; csm_gpios=<&gpx2 3 0 &gpx2 4 0 &gpx2 5 0 &gpx2 6 0>; crl0_gpio=<&gpx0 5 0>; crl1_gpio=<&gpx0 6 0>; rst_gpio=<&gpx0 7 0>; cfg_gpio=<&gpx0 4 0>; phy_ref_freq = <26000>; /* kHz */ suspend_poweroff; clock-names = "xusbxti", "otg"; yikou_node { compatible = "leadcore,dsi-panel"; panel_name = "lcd_rd_rm67295"; refresh_en = <1>; bits-per-pixel = <32>; }; };
其中包括常见reg、中断、整型值、bool值、字符串、子节点、时钟等属性。
一定要耀眼,许多属性的给出会因为使用的SOC平台的不同有所各异, 底下先容下GPIO和中断编写旨趣:
1. GPIOgpio信息的给出有以下两种门径:
csm_gpios=<&gpx2 3 0 &gpx2 4 0 &gpx2 5 0 &gpx2 6 0>;
crl0_gpio=<&gpx0 5 0>; crl1_gpio=<&gpx0 6 0>; rst_gpio=<&gpx0 7 0>; cfg_gpio=<&gpx0 4 0>;
第1种是公用吞并个名字,第2种是每一个gpio单独使用1个名字。
gpio需要指明父节点,对于gpio父节点的诠释下诠释文档(常常linux-3.14\Documentation下磋商于该内核版块的一些模块诠释,很迫切):
linux-3.14\Documentation\devicetree\bindings\gpio.txt
For example, the following could be used to describe gpios pins to use as chip select lines; with chip selects 0, 1 and 3 populated, and chip select 2 left empty: gpio1: gpio1 { gpio-controller #gpio-cells = <2>; }; gpio2: gpio2 { gpio-controller #gpio-cells = <1>; }; [...] chipsel-gpios = <&gpio1 12 0>,成人午夜免费无码区老司机视频 <&gpio1 13 0>, <0>, /* holes are permitted, means no GPIO 2 */ <&gpio2 2>; Note that gpio-specifier length is controller dependent. In the above example, &gpio1 uses 2 cells to specify a gpio, while &gpio2 only uses one. gpio-specifier may encode: bank, pin position inside the bank, whether pin is open-drain and whether pin is logically inverted. Exact meaning of each specifier cell is controller specific, and must be documented in the device tree binding for the device. Example of the node using GPIOs: node { gpios = <&qe_pio_e 18 0>; }; In this example gpio-specifier is "18 0" and encodes GPIO pin number, and empty GPIO flags as accepted by the "qe_pio_e" gpio-controller.
翻译归来成如下几点:
gpio父节点需要包含属性
gpio-controller、 暗示是gpi死心器 #gpio-cells = <2>; 暗示子节点包括2个属性
对于子节点是2个属性的函数 比如:
gpios = <&qe_pio_e 18 0>;
父节点是qe_pio_e 其中18暗示GPIO pin值,即是gpio底下惩办的pin脚序号,该pin值一般就需要查询用户手册&电路图。
2. 中断中断属性节点如下:
interrupt-parent=<&gpx1>; interrupts =<1 2>,<2 2>;
其中
interrupt-parent=<&gpx1>;: 该中断信号所述的中断死心器 interrupts =<1 2>,<2 2>; :描画中断属性,其中<>中第一个值暗示该中断所述中断死心器index,第二个值暗示中断触发情势
中断子节点神气如下:
linux-3.14\Documentation\devicetree\bindings\gpio.txt
Example of a peripheral using the GPIO module as an IRQ controller: funkyfpga@0 { compatible = "funky-fpga"; ... interrupt-parent = <&gpio1>; #父节点 interrupts = <4 3>; #节点属性 };
中断子节点诠释文档如下:
linux-3.14\Documentation\devicetree\bindings\interrupt-controller\interrupts.txt
b) two cells ------------ The #interrupt-cells property is set to 2 and the first cell defines the index of the interrupt within the controller, while the second cell is used to specify any of the following flags: - bits[3:0] trigger type and level flags 1 = low-to-high edge triggered 上涨沿 2 = high-to-low edge triggered 下落沿 4 = active high level-sensitive 高电平灵验 8 = active low level-sensitive 低电平灵验
咱们所填写的中断父节点gpx1界说如下(该文献由三星厂家出厂定制好):
linux-3.14\arch\arm\boot\dts\exynos4x12-pinctrl.dtsi
gpx1: gpx1 { gpio-controller; #gpio死心器 #gpio-cells = <2>; #子节点有2个属性 interrupt-controller; #中断死心器 interrupt-parent = <&gic>; #父节点gic interrupts = <0 24 0>, <0 25 0>, <0 26 0>, <0 27 0>, #子节点属性拘谨 <0 28 0>, <0 29 0>, <0 30 0>, <0 31 0>; #interrupt-cells = <2>; };
可见三星的exynos4412平台中gpx1,既不错做gpio死心器又不错做中断死心器,而gpx1动作中断死心器则路由到gic上。其中interrupts属性诠释如下:
linux-3.14\Documentation\devicetree\bindings\arm\gic.txt
Main node required properties: - compatible : should be one of: "arm,gic-400" "arm,cortex-a15-gic" "arm,cortex-a9-gic" "arm,cortex-a7-gic" "arm,arm11mp-gic" - interrupt-controller : Identifies the node as an interrupt controller - #interrupt-cells : Specifies the number of cells needed to encode an interrupt source. The type shall be a <u32> and the value shall be 3. The 1st cell is the interrupt type; 0 for SPI interrupts, 1 for PPI interrupts. The 2nd cell contains the interrupt number for the interrupt type. SPI interrupts are in the range [0-987]. PPI interrupts are in the range [0-15]. The 3rd cell is the flags, encoded as follows: bits[3:0] trigger type and level flags. 1 = low-to-high edge triggered 2 = high-to-low edge triggered 4 = active high level-sensitive 8 = active low level-sensitive bits[15:8] PPI interrupt cpu mask. Each bit corresponds to each of the 8 possible cpus attached to the GIC. A bit set to '1' indicated the interrupt is wired to that CPU. Only valid for PPI interrupts.
翻译归来:
interrupts = <0 24 0>
第1个0 暗示该中断是SPI类型中断,淌若是1暗示PPI类型中断
24暗示中断号(通过查询电路图和datasheet赢得)
第三个0暗示中断触发情势
再强调一遍 不同的平台gpio、中断死心器惩办可能不相似,是以填写门径可能会有各异,不成教条
六、驱动索要诞生树信息门径驱动明白代码与诞生树节点之间关系如下,代码与属性用相通情愫框出:
of着手的函数请参考《手把手教linux驱动11-linux诞生驱动长入模子》
七、编译(ubuntu中操作)驱动编译:
耀眼,内核必须提前编译好
诞生树编译:
编译诞生树敕令,各个厂家的SDK都不尽相通,本例制作参考。
除此除外驱动模块文献、诞生树文献若何导入给设备板,永逝也比拟大,本文不再给外出径。
八、加载模块(设备板上操作)加载模块后延伸遵循如下:
[root@peng test]# insmod driver.ko [ 26.880000] hello_init [ 26.880000] match ok [ 26.880000] mem_res1 : [0x114000a0] mem_res2:[0x139d0000] [ 26.885000] irq_res1 : [168] irq_res2:[169] [ 26.890000] mem_resp:[114000a0] [ 26.890000] [ 26.895000] phy_ref_freq:26000 [ 26.900000] suspend_poweroff [true] [ 26.900000] suspend_poweroff_test [false] [ 26.900000] [ 26.905000] csm_gpios :[231][232][233][234] [ 26.910000] CTL0:[217] CTL1:[218] RST:[219] CFG:[216] [ 26.915000] bits_per_pixel:32 [ 26.920000] panel_name:lcd_rd_rm67295 [ 26.925000] refresh_en [true]
其中打印的信息即是最终咱们明白出的诞生树里的硬件信息, 咱们就不错凭据这些信息进行关连资源肯求、驱动化。
同期诞生树中的信息,会以文献节点容貌创建在一下目次中:
本文转载自微信公众号「一口Linux」