本合集分享的是,我當(dāng)初學(xué)習(xí)Linux驅(qū)動(dòng)的來(lái)時(shí)路——《《驅(qū)動(dòng)之路》開(kāi)篇:自序&前言》。
正文
在嵌入式 Linux 開(kāi)發(fā)中,我們經(jīng)常會(huì)在設(shè)備樹(shù)(DTS)中寫下這樣的配置:

神奇的是,驅(qū)動(dòng)程序無(wú)需手動(dòng)操作寄存器、無(wú)需硬編碼 GPIO 編號(hào),就能通過(guò)內(nèi)核提供的接口輕松獲取這個(gè)復(fù)位 GPIO,實(shí)現(xiàn)設(shè)備復(fù)位控制。這背后的底層邏輯是什么?今天就扒一扒這層底層邏輯。
1 DTS 到底在配置什么?
首先我們得明確,reset-gpios = <&gpio2 RK_PB4 GPIO_ACTIVE_HIGH>; 這行代碼不是給 CPU 看的,而是給內(nèi)核和驅(qū)動(dòng)的 “約定說(shuō)明書”,每一部分都有明確含義:
reset-gpios:是 “屬性名”,遵循 Linux 設(shè)備樹(shù)的 GPIO 屬性命名規(guī)范(后綴-gpios),告訴內(nèi)核 “這是一個(gè)用于復(fù)位功能的 GPIO 配置”。
&gpio2:是 “GPIO 控制器的引用”,表示這個(gè) GPIO 歸屬于gpio2控制器。
RK_PB4:是 “GPIO 引腳編號(hào)”,是芯片廠商定義的宏,本質(zhì)是一個(gè)整數(shù)。
GPIO_ACTIVE_HIGH:是 “GPIO 有效電平”,表示該 GPIO 為高電平有效。
PS:GPIO_ACTIVE_HIGH會(huì)被內(nèi)核解析并存儲(chǔ)在gpio_desc中,驅(qū)動(dòng)調(diào)用 gpiod_set_value() 時(shí):若配置為GPIO_ACTIVE_HIGH,驅(qū)動(dòng)傳入1則內(nèi)核設(shè)置 GPIO 為高電平,傳入0為低電平;若配置為GPIO_ACTIVE_LOW,驅(qū)動(dòng)傳入1則內(nèi)核自動(dòng)設(shè)置為低電平(有效電平),傳入0為高電平。驅(qū)動(dòng)無(wú)需關(guān)心 “有效電平是高還是低”,只需按 “邏輯值”(1 = 有效,0 = 無(wú)效)調(diào)用接口,內(nèi)核會(huì)自動(dòng)完成電平轉(zhuǎn)換。也就是說(shuō),gpiod_set_value() 設(shè)置的是“邏輯值”而非“物理值”。
簡(jiǎn)單說(shuō),這行配置的核心作用是:向內(nèi)核聲明 “當(dāng)前設(shè)備的復(fù)位 GPIO 是 gpio2 控制器的 PB4 引腳,高電平有效”,為后續(xù)驅(qū)動(dòng)獲取 GPIO 打下基礎(chǔ)。
2 DTS 配置到驅(qū)動(dòng)獲取的 4 個(gè)關(guān)鍵步驟
DTS 配置只是 “紙上約定”,驅(qū)動(dòng)能真正拿到 GPIO 并使用,依賴內(nèi)核的 “解析 - 注冊(cè) - 匹配 - 提供” 全流程。
步驟 1:內(nèi)核解析 DTS,提取 GPIO 配置信息
Linux 內(nèi)核啟動(dòng)時(shí),會(huì)先執(zhí)行 “設(shè)備樹(shù)解析階段”:
(1)內(nèi)核的設(shè)備樹(shù)解析器(dtb_parser)會(huì)遍歷 DTS 文件,找到所有帶-gpios后綴的屬性(包括reset-gpios)。
(2)解析器會(huì)識(shí)別&gpio2對(duì)應(yīng)的 GPIO 控制器(通過(guò) DTS 中g(shù)pio2節(jié)點(diǎn)的compatible屬性匹配內(nèi)核中的 GPIO 驅(qū)動(dòng),比如rockchip,rk3576-gpio).
(3)將RK_PB4引腳編號(hào)轉(zhuǎn)換為內(nèi)核統(tǒng)一的 “GPIO 全局編號(hào)”(這是關(guān)鍵!)。內(nèi)核為了管理所有 GPIO,會(huì)給每個(gè) GPIO 分配一個(gè)全局唯一的整數(shù) ID(GPIO 編號(hào)),其計(jì)算規(guī)則如下:
(4)解析器將 “GPIO 編號(hào) + 有效電平” 封裝成struct gpio_desc結(jié)構(gòu)體(GPIO 描述符,內(nèi)核中代表一個(gè) GPIO 的 “身份證”),并與當(dāng)前設(shè)備的struct device結(jié)構(gòu)體關(guān)聯(lián)(存在設(shè)備的私有數(shù)據(jù)中)。 至此,DTS 中的 “文字配置” 已被內(nèi)核解析為 “可操作的 GPIO 描述符”,就像把 “地址信息” 轉(zhuǎn)換成了 “快遞單號(hào)”,等待驅(qū)動(dòng)來(lái) “取件”。
步驟 2:GPIO 控制器驅(qū)動(dòng)初始化,提供操作接口
在解析 DTS 之前,內(nèi)核已經(jīng)加載了gpio2對(duì)應(yīng)的 GPIO 控制器驅(qū)動(dòng)(通常是芯片原廠 BSP 工程師編寫的平臺(tái)驅(qū)動(dòng)),該驅(qū)動(dòng)會(huì)完成兩件關(guān)鍵事:
(1)向內(nèi)核 GPIO 子系統(tǒng)注冊(cè)struct gpio_chip結(jié)構(gòu)體(GPIO 芯片描述符),其中包含:
GPIO 控制器的基地址(操作寄存器的物理地址,已通過(guò)ioremap映射為虛擬地址);
該控制器管理的 GPIO 數(shù)量(比如gpio2管理 32 個(gè)引腳);
操作 GPIO 的核心函數(shù)(request申請(qǐng)、free釋放、set_value設(shè)置電平、get_value讀取電平)。
(2)內(nèi)核 GPIO 子系統(tǒng)會(huì)為該控制器分配 “GPIO 基號(hào)”(比如gpio2分配 32-63 號(hào)),確保全局編號(hào)不重復(fù)。
這一步的作用是:讓內(nèi)核具備操作gpio2控制器下所有 GPIO 的能力,相當(dāng)于給 “快遞站” 配備了 “快遞員”(操作函數(shù)),能處理后續(xù)的 GPIO 申請(qǐng)和控制請(qǐng)求。
步驟 3:設(shè)備與驅(qū)動(dòng)匹配,驅(qū)動(dòng)申請(qǐng) GPIO 資源
當(dāng)內(nèi)核完成 DTS 解析和 GPIO 控制器初始化后,會(huì)啟動(dòng) “設(shè)備與驅(qū)動(dòng)匹配” 流程:
(1)內(nèi)核根據(jù) DTS 中當(dāng)前設(shè)備節(jié)點(diǎn)的compatible屬性,在驅(qū)動(dòng)鏈表中查找匹配的設(shè)備驅(qū)動(dòng)。 (2)驅(qū)動(dòng)匹配成功后,會(huì)執(zhí)行probe函數(shù),在probe函數(shù)中,驅(qū)動(dòng)會(huì)通過(guò)內(nèi)核提供的 GPIO 申請(qǐng)接口,獲取reset-gpios對(duì)應(yīng)的 GPIO。
比如:

這里的核心接口是devm_gpiod_get_optional(devm_前綴表示內(nèi)核自動(dòng)管理資源,驅(qū)動(dòng)卸載時(shí)自動(dòng)釋放 GPIO),它接收參數(shù)"reset"對(duì)應(yīng) DTS 中的reset-gpios屬性(內(nèi)核會(huì)自動(dòng)拼接-gpios后綴)。
步驟 4:驅(qū)動(dòng)操作 GPIO,實(shí)現(xiàn)業(yè)務(wù)功能
通過(guò)層層函數(shù)調(diào)用,驅(qū)動(dòng)最終拿到reset_gpio(gpio_desc指針)。

然后就可以通過(guò)內(nèi)核提供的接口gpiod_set_value() 操作 GPIO 的電平,這里的gpiod_set_value() 接口會(huì)最終調(diào)用 GPIO 控制器驅(qū)動(dòng)中的set_value函數(shù),通過(guò)操作 GPIO 寄存器(如輸出控制寄存器、數(shù)據(jù)寄存器)實(shí)現(xiàn)電平切換,完成硬件層面的復(fù)位控制。

3 總結(jié)
DTS 配置reset-gpios后驅(qū)動(dòng)能直接獲取,本質(zhì)是 “三層約定 + 兩層封裝” 的結(jié)果:
三層約定:DTS 與內(nèi)核的 “配置格式約定”、GPIO 控制器與內(nèi)核的 “驅(qū)動(dòng)接口約定”、驅(qū)動(dòng)與內(nèi)核的 “資源申請(qǐng)約定”;
兩層封裝:內(nèi)核 GPIO 子系統(tǒng)封裝硬件差異、devm_機(jī)制封裝資源管理。
這種設(shè)計(jì)讓嵌入式開(kāi)發(fā)擺脫了 “寄存器操作的繁瑣” 和 “硬件差異的困擾”,驅(qū)動(dòng)開(kāi)發(fā)者只需專注于業(yè)務(wù)邏輯(比如何時(shí)觸發(fā)復(fù)位),無(wú)需關(guān)心底層實(shí)現(xiàn),這也是 Linux 設(shè)備樹(shù) “硬件描述與驅(qū)動(dòng)分離” 設(shè)計(jì)思想的核心體現(xiàn)。
最后留個(gè)問(wèn)題: reset-gpios里的GPIO_ACTIVE_HIGH能隨便寫嗎?我們下期分享~
(完)
審核編輯 黃宇
-
嵌入式
+關(guān)注
關(guān)注
5208文章
20599瀏覽量
336417 -
驅(qū)動(dòng)
+關(guān)注
關(guān)注
12文章
1987瀏覽量
88670 -
Linux
+關(guān)注
關(guān)注
88文章
11802瀏覽量
219433 -
GPIO
+關(guān)注
關(guān)注
16文章
1333瀏覽量
56413
發(fā)布評(píng)論請(qǐng)先 登錄
Linux reset子系統(tǒng)及驅(qū)動(dòng)實(shí)例
linux下如何修改gpio驅(qū)動(dòng)
74HC595驅(qū)動(dòng)點(diǎn)陣顯示一行
一個(gè)多行的字符串如何一行一行的執(zhí)行然后一行一行的顯示出來(lái)啊
想問(wèn)下為什么我把第一行和第二行換了個(gè)位置就會(huì)出現(xiàn)很多報(bào)錯(cuò)?
Labview 怎么用報(bào)表生成函數(shù) 一行一行自動(dòng)換行寫表格Excel
怎樣按照我的一個(gè)excel模版,一行一行的把數(shù)據(jù)寫入而且上一行的數(shù)據(jù)不會(huì)消失。
請(qǐng)問(wèn)下aio-3588sJd4觸摸屏的驅(qū)動(dòng)源碼在哪個(gè)目錄下?
NAU8822的IIC接口用GPIO-base的驅(qū)動(dòng),如何換成硬件IIC的驅(qū)動(dòng)?
簡(jiǎn)要分析Thread的通用GPIO設(shè)備驅(qū)動(dòng)
LINUX GPIO如何驅(qū)動(dòng)源碼移植
Linux內(nèi)核reset驅(qū)動(dòng)實(shí)例
驅(qū)動(dòng)之路#03:LCD 時(shí)序參數(shù)分析
驅(qū)動(dòng)之路#21:一行 reset-gpios,驅(qū)動(dòng)為何就能用 GPIO?
評(píng)論