Linux驅(qū)動 | 手寫一個設備樹使用的實例
一、前言
設備樹是每一個Linux驅(qū)動工程師都必須掌握的一個知識點,有很多之前做單片機的朋友剛接觸Linux驅(qū)動時,會一臉懵!
其實設備樹的使用并沒有大家想像的那么復雜,對于大部分工程師來說,只要會修改即可。
很多粉絲留言說,希望彭老師提供一個設備樹到驅(qū)動解析的實例。
必須安排!
在學習設備樹之前,大家一定要搞清楚什么是platform總線,請詳細學習下面這篇文章:
《手把手教Linux驅(qū)動10-platform總線詳解》
關于設備樹理論部分內(nèi)容請學習下面這篇文章:
《手把手教linux驅(qū)動11-linux設備驅(qū)動統(tǒng)一模型》
關于驅(qū)動基礎文章,可以去B站學習一口君的入門視頻:
《從學Linux驅(qū)動入門視頻》
https://www.bilibili.com/video/BV1d5411A7VJ?spm_id_from=333.999.0.0
有了這些基礎知識后,我們就可以來編寫一個設備樹的實例,
下面彭老師就給大家講解如何自己添加一個設備樹節(jié)點,并如何在驅(qū)動中提取出設備樹的信息。
老規(guī)矩,代碼從0開始編寫,并且全部驗證通過,并分享給大家。
二、測試平臺
本次測試在開發(fā)板上操作,操作環(huán)境如下:
1. 編譯環(huán)境
- ubuntu 16.04
2. 交叉編譯工具
- 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. 開發(fā)板
- 開發(fā)板:fs4412
- soc:exynos4412
4. 內(nèi)核版本
- Linux kernel 3.14.0
三、內(nèi)核解析設備樹一般過程
系統(tǒng)啟動后,uboot會從網(wǎng)絡或者flash、sd卡中讀取設備樹文件(具體由uboot命令給出),
引導linux內(nèi)核啟動后,會把設備樹鏡像保存到的內(nèi)存地址傳遞給Linux內(nèi)核,Linux內(nèi)核會解析設備樹鏡像,從設備樹中提取硬件信息并逐一初始化。
其中設備樹信息會被轉(zhuǎn)換成struct platform_device類型變量。
而驅(qū)動要解析設備樹,必須定義 struct platform_driver類型結(jié)構體變量,并通過函數(shù)platform_driver_register()注冊。
這兩者都會注冊到platform總線,當驅(qū)動和設備樹節(jié)點匹配成功后,就調(diào)用 struct platform_driver中.probe方法。
其中設備樹節(jié)點會封裝在struct device_node結(jié)構體變量中 各個屬性信息會封裝在 struct property結(jié)構體變量中, 他們與struct platform_device結(jié)構體之間關系如下:
四、驅(qū)動架構
以下是一口君編寫的驅(qū)動架構,
我們只需要將測試代碼填充到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);
五、設備樹節(jié)點
下面是給出的設備樹信息:
- 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值、字符串、子節(jié)點、時鐘等屬性。
一定要注意,很多屬性的給出會因為使用的SOC平臺的不同有所差異, 下面介紹下GPIO和中斷編寫原理:
1. GPIO
gpio信息的給出有以下兩種方法:
- 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需要指明父節(jié)點,關于gpio父節(jié)點的說明下說明文檔(通常linux-3.14\Documentation下有關于該內(nèi)核版本的一些模塊說明,很重要):
- 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.
翻譯總結(jié)成如下幾點:
gpio父節(jié)點需要包含屬性
- gpio-controller、 表示是gpi控制器
- #gpio-cells = <2>; 表示子節(jié)點包括2個屬性
對于子節(jié)點是2個屬性的函數(shù) 比如:
- gpios = <&qe_pio_e 18 0>;
父節(jié)點是qe_pio_e 其中18表示GPIO pin值,就是gpio下面管理的pin腳序號,該pin值一般就需要查詢用戶手冊&電路圖。
2. 中斷
中斷屬性節(jié)點如下:
- interrupt-parent=<&gpx1>;
- interrupts =<1 2>,<2 2>;
其中
- interrupt-parent=<&gpx1>;: 該中斷信號所述的中斷控制器
- interrupts =<1 2>,<2 2>; :描述中斷屬性,其中<>中第一個值表示該中斷所述中斷控制器index,第二個值表示中斷觸發(fā)方式
中斷子節(jié)點格式如下:
- 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>; #父節(jié)點
- interrupts = <4 3>; #節(jié)點屬性
- };
中斷子節(jié)點說明文檔如下:
- 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 低電平有效
我們所填寫的中斷父節(jié)點gpx1定義如下(該文件由三星廠家出廠定制好):
- linux-3.14\arch\arm\boot\dts\exynos4x12-pinctrl.dtsi
- gpx1: gpx1 {
- gpio-controller; #gpio控制器
- #gpio-cells = <2>; #子節(jié)點有2個屬性
- interrupt-controller; #中斷控制器
- interrupt-parent = <&gic>; #父節(jié)點gic
- interrupts = <0 24 0>, <0 25 0>, <0 26 0>, <0 27 0>, #子節(jié)點屬性約束
- <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.
翻譯總結(jié):
- interrupts = <0 24 0>
第1個0 表示該中斷是SPI類型中斷,如果是1表示PPI類型中斷
24表示中斷號(通過查詢電路圖和datasheet獲得)
第三個0表示中斷觸發(fā)方式
再強調(diào)一遍 不同的平臺gpio、中斷控制器管理可能不一樣,所以填寫方法可能會有差異,不可教條
六、驅(qū)動提取設備樹信息方法
驅(qū)動解析代碼與設備樹節(jié)點之間關系如下,代碼與屬性用相同顏色框出:
of開頭的函數(shù)請參考《手把手教linux驅(qū)動11-linux設備驅(qū)動統(tǒng)一模型》
七、編譯(ubuntu中操作)
驅(qū)動編譯:
注意,內(nèi)核必須提前編譯好
設備樹編譯:
編譯設備樹命令,各個廠家的SDK都不盡相同,本例制作參考。
除此之外驅(qū)動模塊文件、設備樹文件如何導入給開發(fā)板,差別也比較大,本文不再給出步驟。
八、加載模塊(開發(fā)板上操作)
加載模塊后執(zhí)行結(jié)果如下:
- [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]
其中打印的信息就是最終我們解析出的設備樹里的硬件信息, 我們就可以根據(jù)這些信息進行相關資源申請、初始化。
同時設備樹中的信息,會以文件節(jié)點形式創(chuàng)建在一下目錄中:
本文轉(zhuǎn)載自微信公眾號「一口Linux」