嵌入式系統(tǒng)磚家_gpio和pinctrl子系統(tǒng)的關(guān)系和區(qū)別

怎么樣才能讓自己成功一個合格的Linux驅(qū)動開發(fā)人員,很多人喜歡一頭扎進(jìn)源碼里摳各種細(xì)節(jié),我自己實(shí)踐出來的感受是:這樣去學(xué)習(xí)Linux驅(qū)動很累,進(jìn)步很慢。內(nèi)核里設(shè)計的很多驅(qū)動框架其實(shí)是在不斷的完善和抽象的,只有理解前人們設(shè)計框架時的思路和想法,才能融合他們的工作體系里。其實(shí)內(nèi)核的開發(fā)人員們寫了不少的文檔和說明,只是我們往往懶于搜索,這些驅(qū)動框架的原創(chuàng)者的一兩句總結(jié)可以很好地幫忙我們理解驅(qū)動。下面這篇筆記,會盡量少一點(diǎn)源碼分析。

一、概述

pinctrl和gpio子系統(tǒng)在內(nèi)核里的使用率非常高,和嵌入式產(chǎn)品的關(guān)聯(lián)非常大。從這兩個子系統(tǒng)開始學(xué)習(xí)驅(qū)動開發(fā)是個不錯的入門選擇。

gpio子系統(tǒng)提供了:

- 讀引腳值,高或者低

- 輸出高低電平;

- 部分gpio還負(fù)責(zé)接收IRQ,涉及irqchip子系統(tǒng);

pinctrl子系統(tǒng)提供了:

- 引腳復(fù)用,大多數(shù)引腳都可以通過配置寄存器來選擇復(fù)用成不同的功能,例如某個引腳即可用作為普通的gpio,也可以作為UART的TX;

- 引腳配置,一般包括上下拉、驅(qū)動能力等;


對于gpio子系統(tǒng):

- 對于Linux-4.14,源碼位于:drivers/gpio/*

- 文檔位于:Documentation/gpio/*

- 所有的GPIO都會被編號,從0~N;

- 內(nèi)核里使用GPIO子系統(tǒng)的代碼被稱為Consumer,Consumer是通過[devm_]gpio_request(gpio, label)來申請GPIO;

- 1個GPIO可以被設(shè)置為輸入或者輸出(gpiod_direction_output());

- 被設(shè)置出輸入時,可以讀到高或者電平(gpiod_direction_input());

- 被設(shè)置出輸出時,可以輸出高或者低電平(gpiod_set_value());

- GPIO可以被映射為Linux IRQ,暫時不關(guān)注這一塊;

- GPIO可以在/sys/class/gpio中被導(dǎo)出,供應(yīng)用層使用;

- struct gpio_chip用于表示一個gpio控制器,可通過gpiochip_add_data()將其注冊給gpio子系統(tǒng);


查看gpio子系統(tǒng)的文檔:

$ tree Documentation/gpio/


如何為一款CPU的添加gpio驅(qū)動:

1. 針對具體CPU構(gòu)造struct gpio_chip,部分成員如下:

2. 向gpio子系統(tǒng)注冊CPU的struct gpio_chip(gpiochip_add_data());


對于pinctrl子系統(tǒng):

- 硬件上的一個引腳通過軟件的配置可作為不同的功能的行為叫做引腳復(fù)用(pin muxing),gpio(input/output功能)是引腳復(fù)用的子集;

- pinctrl子系統(tǒng)是在3.4內(nèi)核中第一次被正式宣布的;

- 對于Linux-4.14,源碼位于:drivers/pinctrl/*;

- 文檔位于:Documentation/devicetree/bindings/pinctrl/*

- 會進(jìn)行一些健全性檢查,確保一個引腳不能同時用于兩個功能;

- 驅(qū)動可以選擇實(shí)現(xiàn)引腳復(fù)用接口(pin multiplexing interface)或引腳配置接口(pin configuration interface),或兩者都實(shí)現(xiàn);

- gpio子系統(tǒng)會引用pinctrl子系統(tǒng);

- struct pinctrl_desc用于表示一個pin控制器,可通過[devm_]pinctrl_register()將其注冊進(jìn)pinctrl子系統(tǒng);

- 單板在啟動時注冊一堆的能自動激活引腳復(fù)用配置的行為被稱為“pinmux hogs”;

- 類似regulator的get/enable/disable/put操作,pinctrl子系統(tǒng)提供出來的api也有g(shù)et/enable/disable/put操作;


如何為一款CPU的添加pinctrl驅(qū)動:

1. 針對具體CPU構(gòu)造struct gpio_chip,部分成員如下:

2. 向pinctrl子系統(tǒng)注冊CPU的struct pinctrl_desc(devm_pinctrl_register());



二、分析全志H3的gpio和pinctrl功能

我們以全志H3芯片+Linux-4.14內(nèi)核為例分析一下。對于H3+Linux-4.14,已經(jīng)沒有獨(dú)立的drivers/gpio/gpio-*.c驅(qū)動,因為gpio驅(qū)動和pinctrl驅(qū)動有著緊密的聯(lián)系,所以在pinctrl驅(qū)動里會同時向gpio子系統(tǒng)注冊struct gpio_chip,所以對于H3,我們只要分析pinctrl驅(qū)動就夠了。

(一) H3平臺的gpio功能

device端(不同的板子各自決定自己該怎么配置引腳):

arch/arm/boot/dts/sun8i-h3.dtsi

driver端(提供配置引腳的能力):

drivers/pinctrl/sunxi/pinctrl-sun8i-h3.c

sun8i_h3_pinctrl_probe

? ? sunxi_pinctrl_init(pdev, &sun8i_h3_pinctrl_data);

? ? ? ? sunxi_pinctrl_init_with_variant(_dev, _desc, 0)

sun8i_h3_pinctrl_data的定義如下:

...,中間省略

正好對應(yīng)芯片手冊描述的PA組~PG組的引腳;

struct sunxi_desc_pin包含了1個struct pinctrl_pin_desc ( 所有pinctrl驅(qū)動通用 ) 變量和1個指向struct sunxi_desc_function ( sunxi pinctrl驅(qū)動自定義的 ) 數(shù)組的指針:

* struct pinctrl_pin_desc用于描述一個引腳,對于PA0引腳,成員number=0,成員name="PA0",類似一個map(key=0,value="PA0");

* struct sunxi_desc_function用于描述一個引腳功能,對于PA0引腳,共有4個function(gpio_in、gpio_out、uart2[看來只是功能分組,并沒有指定具體是哪一個引腳功能]、jtag),第一個function的muxval=0x0(寄存器里的偏移值),name="gpio_in";


向gpio子系統(tǒng)注冊平臺gpio驅(qū)動:

1. 初始化struct gpio_chip(abstract a GPIO controller):

* pctl的類型為struct sunxi_pinctrl *pctl,它是pinctrl-sunxi.c大管家結(jié)構(gòu)體(wrapper for holding driver data together,跟pinctrl-samsung.c里的struct samsung_pinctrl_drv_data類似),本驅(qū)動涉及的所有需要在不同函數(shù)間共享的數(shù)據(jù)都會保存在此大管家結(jié)構(gòu)體內(nèi)。

* pctl->chip就是struct gpio_chip。

2. 注冊struct gpio_chip:

gpiochip_add_data(pctl->chip, pctl);

將H3的struct gpio_chip注冊給gpio子系統(tǒng)后,內(nèi)核就有了控制引腳的能力(設(shè)置輸入輸出、讀寫高低電平),但是還無法感知 CPU 有多少引腳可用。

3. 感知所有引腳:


如何通過gpio子系統(tǒng)讀寫gpio

1. 在device tree里選擇要使用的gpio引腳

以gpio-leds驅(qū)動為例:

<&pio 0 10 GPIO_ACTIVE_HIGH>;對應(yīng)引腳PA0。

2. 在驅(qū)動源碼里使用gpio子系統(tǒng)提供的統(tǒng)一接口來控制gpio

以gpio-leds驅(qū)動為例:

* gpio子系統(tǒng)API的前綴都是gpiod_,完整的API位于:include/linux/gpio/consumer.h;


(二) H3平臺的pinctrl功能

向pinctrl子系統(tǒng)注冊平臺pintrcl驅(qū)動:

1. 保存所有引腳的硬件信息:

只需保存引腳索引號即可,后面要配置引腳功能時,會通過大管家結(jié)構(gòu)體struct sunxi_pinctrl *pctl找到struct sunxi_pinctrl_desc,然后根據(jù)引腳索引號獲取到引腳的硬件信息。

2. 初始化struct pinctrl_desc(pin controller descriptor):

1) struct pinconf_ops sunxi_pconf_ops,負(fù)責(zé)提供獲取和設(shè)置引腳驅(qū)動能力、上下拉(pin configuration)的能力;

2) struct pinctrl_ops sunxi_pctrl_ops,不是必須的,各種雜活都在這里處理(解析設(shè)備樹等,類似ioctl());

3) struct pinmux_ops sunxi_pmx_ops,負(fù)責(zé)提供獲取和配置引腳(pin muxing)的能力;

3. 注冊struct pinctrl_desc:

devm_pinctrl_register(&pdev->dev, pctrl_desc, pctl);


如何通過pinctrl子系統(tǒng)配置引腳

1. 在device tree里添加引腳配置節(jié)點(diǎn):

以csi 相關(guān)的引腳配置為例:

PE0~PE11都被要求作為csi功能,在pinctrl-sun8i-h3.c中有填寫csi功能對應(yīng)的硬件配置信息:

一個“pin configuration node”必須被具體的驅(qū)動引用才能發(fā)揮作用:

2. 在每一個特定功能的驅(qū)動被probe時,它引用的引腳功能會得到初始化:

以csi控制器為例:

drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c

? ? really_probe

? ? ? ? pinctrl_bind_pins(dev);

? ? ? ? ? ? devm_pinctrl_get(dev);? ? // 通過pinctrl子系統(tǒng) 獲取引腳

? ? ? ? ? ? ? ? sunxi_pctrl_dt_node_to_map

? ? ? ? ? ? pinctrl_select_state(); // 設(shè)置引腳功能

? ? sun6i_csi_probe

* pinctr里子系統(tǒng)API的前綴都是pinctrl_,完整的API位于:include/linux/pinctrl/consumer.h;


總結(jié)

引腳的配置是很常用的功能,內(nèi)核對引腳管理的抽象卻很值得我們學(xué)習(xí)。一般來說,引腳配置這種基礎(chǔ)驅(qū)動是應(yīng)該包含在芯片廠商編寫的BSP里,板級定制只要掌握如何和pinctrl子系統(tǒng)和gpio子系統(tǒng)交互即可,但是當(dāng)遇到bug時,請仔細(xì)分析芯片廠商提供的驅(qū)動代碼,軟硬件上任何細(xì)微的改動都可能導(dǎo)致意想不到的的問題。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容