Pinctrl子系統之一了解基礎概念【轉】
- 2019 年 10 月 10 日
- 筆記
轉自:https://blog.csdn.net/u012830148/article/details/80609337
版權聲明:本文為博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。 本文鏈接:https://blog.csdn.net/u012830148/article/details/80609337 1.Linux Pinctrl子系統簡介 在許多soc內部都包含有pin控制器,通過pin控制器的寄存器,我們可以配置一個或者一組引腳的功能和特性。在軟件方面,Linux內核提供了pinctrl子系統,目的是為了統一各soc廠商的pin腳管理。
2.Linux Pinctrl子系統提供的功能 (1)管理系統中所有的可以控制的pin,在系統初始化的時候,枚舉所有可以控制的pin,並標識這些pin。
(2)管理這些pin的復用(Multiplexing)。對於SOC而言,其引腳除了配置成普通的GPIO之外,若干個引腳還可以組成一個pin group,行程特定的功能。pin control subsystem要管理所有的pin group。
(3)配置這些pin的特性。例如使能或關閉引腳上的pull-up、pull-down電阻,配置引腳的driver strength。
3.pinctrl相關概念 普通driver調用pin control subsystem的主要目標有兩個:
(1)設定該設備的功能復用。
(2)設定該device對應的pin的電氣特性。
設定設備的功能復用需要了解兩個概念,一個是function,另外一個是pin group。function是功能抽象,對應一個HW邏輯block,例如SPI0.雖然給定了具體的function name,我們並不能確定其使用的pins的情況。例如為了設計靈活,芯片內部的SPI0的功能引出到pin group{C6,C7,C8,C9},也可能引出的另外一個pin group{C22.C23,C24,C25},但毫無疑問,這兩個pin group不能同時active,畢竟芯片內部的SPI0的邏輯功能電路只有一個,因此只有給出function selector以及function的pin group selector才能進行function mux 的設定。
此外,由於電源管理的要求,某個device可能處於某個電源管理狀態,例如idle或者sleep,這時候,屬於device的所有pin就會需要處於另外的狀態。
綜合上述的需求,就定義了pin control state的概念,也就是說設備可能處於非常多的狀態中的一個,device driver可以切換設備處於的狀態。為了方便管理pin control state 就有了pin control state holder的概念,用來管理一個設備的所有的pin control狀態。
綜上所述,普通的driver調用pin control subsystem 的接口就是只有三個步驟:
(1)驅動加載或是運行時,獲取pin control state holder的句柄
(2)設定pin control的狀態
(3)驅動卸載或是退出時,釋放pin control state holder的句柄
4.與GPIO子系統的關係
上圖顯示了gpio子系統和pinctrl子系統之間的關係,即pinctrl子系統實際上也把gpio一起管理起來,所有的gpio操作也需要透過pinctrl子系統來完成,這樣,如果一個pin已經被申請為gpio,再通過pinctrl子系統申請為某個function時就會返回錯誤。
5.與統一設備驅動模型的關係 從"pinctrl子系統關係圖"中得知,linux kernel中的統一設備驅動模型也調用了pinctrl子系統的功能。linux kernel中的統一設備驅動模型提供了driver 和device的綁定機制,一旦匹配就會調用driver 的probe函數。
而在調用票probe函數之前,驅動模型實際上已經申請過一次pin了,(前提是dts文件中該驅動節點中有定義名為「default」的pinctrl配置)。
驅動模型中調用driver的probe函數的地方:
static int really_probe(struct device *dev, struct device_driver *drv) { int ret = 0; …… /* If using pinctrl, bind pins now before probing */ ret = pinctrl_bind_pins(dev); //對該device涉及的pin進行pin control相關設定 if (ret) goto probe_failed; if (driver_sysfs_add(dev)) { printk(KERN_ERR "%s: driver_sysfs_add(%s) failedn", __func__, dev_name(dev)); goto probe_failed; } if (dev->bus->probe) { //下面是真正的probe過程。 ret = dev->bus->probe(dev); if (ret) goto probe_failed; } else if (drv->probe) { ret = drv->probe(dev); if (ret) goto probe_failed; } driver_bound(dev); ret = 1; pr_debug("bus: '%s': %s: bound device %s to driver %sn", ….. } pinctrl_bind_pins函數定義:
#define PINCTRL_STATE_DEFAULT "default" #define PINCTRL_STATE_IDLE "idle" #define PINCTRL_STATE_SLEEP "sleep" int pinctrl_bind_pins(struct device *dev) { int ret; dev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL); if (!dev->pins) return -ENOMEM; dev->pins->p = devm_pinctrl_get(dev); //獲取pinctrl if (IS_ERR(dev->pins->p)) { dev_dbg(dev, "no pinctrl handlen"); ret = PTR_ERR(dev->pins->p); goto cleanup_alloc; } dev->pins->default_state = pinctrl_lookup_state(dev->pins->p, //查找這個pin的default狀態 PINCTRL_STATE_DEFAULT); if (IS_ERR(dev->pins->default_state)) { dev_dbg(dev, "no default pinctrl staten"); ret = 0; goto cleanup_get; } ret = pinctrl_select_state(dev->pins->p, dev->pins->default_state); //設置pin的default狀態 if (ret) { dev_dbg(dev, "failed to activate default pinctrl staten"); goto cleanup_get; } return 0; 從驅動模型的實現中,不難看出在驅動probe前,就已經申請到default的pin配置了,當然pinctrl的計數已經+1了。
Driver的probe函數可以通過devm_pinctrl_get獲得pinctrl的句柄,再自行調用pinctrl_select_state設置pin state。
6.與device tree的關係 在device tree source 文件中可以在驅動節點中定義該驅動需要用到的pin配置。
device-node-name{ //定義該device自己的屬性 pinctrl-names= "sleep","default","idle"; pinctrl-0 = &xxx_State_sleep; pinctrl-1 = &xxx_state_default; pinctrl-2 = &xxx_state_idle; } pinctrl-0 pinctrl-1 pinctrl-2 …..表示了該設備的一個個狀態,這裡我們定義了三個pinctrl-0 pinctrl-1 pinctrl-2,數字0、1、2就是pinctrl-names中對應的字符串數組的index。其中pinctrl-0就是「sleep」狀態,pinctrl-1就是「default」狀態,pinctrl-2就是「idle」狀態。而xxx_state_sleep,xxx_state_default,xxx_state_idel就是驅動具體的pin配置項了,需要在pinctrl設備節點處定義:
pinctrl@e01b0000{ pinctrl-names = "default"; pinctrl-0 = <&state_default>; state_default:pinctrl_default{ }; xxx_state_sleep:xxx_sleep{ xxx_mfp{ actions,groups = "fmp1_3_1spi0_ss","mfp1_3_1_spi0_miso","mfp1_5_4"; actions,function = "spi0"; }; }; Pinctrl子系統在加載時,會調用pinctrl_dt_to_map函數將dts文件中有關pinctrl的配置項解析出來,並根據dts各驅動節點對pinctrl的引用關係,將phandle掛到各個驅動的device tree子節點,各個驅動就可以通過自己的dev句柄獲得pinctrl的配置了。
7.與主控驅動的關係 在kernel的machine driver中,需要將主控的pinctrl相關硬件操作形象成一個符合linux pinctrl子系統規範的結構pinctrl_desc,並調用pinctrl_register註冊到pinctrl子系統中,這樣pinctrl子系統就可以將上層行為轉換成具體的硬件操作了:
struct pinctrl_desc { const char *name; struct pinctrl_pin_desc const *pins; //描述每個pin的信息為何。 unsigned int npins; //表示可以控制多少個pin。pins和npins這兩個成員就確定了一個pin controller所能控制的引腳信息。 const struct pinctrl_ops *pctlops;//全局的控制函數 const struct pinmux_ops *pmxops;//復用引腳的相關的操作函數 const struct pinconf_ops *confops; //用來配置引腳特性(如pull-up/pull-down) struct module *owner; }; pctlops成員callback函數說明:
struct pinctrl_ops { int (*get_groups_count) (struct pinctrl_dev *pctldev); //該pin controller支持多少個pin group const char *(*get_group_name) (struct pinctrl_dev *pctldev,//給定一個selector(index),獲取指定的pin group的name unsigned selector); int (*get_group_pins) (struct pinctrl_dev *pctldev, //給定一個selector(index)獲取指定的pin group中pin的信息(該pin group unsigned selector,, //包括多少個pin,每個pin的ID是什麼) const unsigned **pins, unsigned *num_pins); void (*pin_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s,//debug fs的callback接口 unsigned offset); int (*dt_node_to_map) (struct pinctrl_dev *pctldev, //分析一個pin configuration node並把分析結果保存成mapping table struct device_node *np_config, //entry 每一個entry表示一個setting(一個功能復用設定,或者電氣特性設定) struct pinctrl_map **map, unsigned *num_maps); void (*dt_free_map) (struct pinctrl_dev *pctldev, //dt_node_to_map的逆函數。 struct pinctrl_map *map, unsigned num_maps); }; pmxops成員callback函數說明:
struct pinmux_ops { int (*request) (struct pinctrl_dev *pctldev, unsigned offset);//pinctrl子系統進行具體的復用設定之前需要調用該函數,主要用來請底層的driver判斷某個引腳的復用設定是否ok int (*free) (struct pinctrl_dev *pctldev, unsigned offset);//request的逆函數,調用request函數請求佔用了某些pin的資源,調用free可以釋放這些資源。 int (*get_functions_count) (struct pinctrl_dev *pctldev);//返回pin controller支持的function的數目。 const char *(*get_function_name) (struct pinctrl_dev *pctldev,//給定一個selector(index)獲取指定function的name。 unsigned selector); int (*get_function_groups) (struct pinctrl_dev *pctldev,//給定一個selector(index)獲取指定的function 的pin group信息。 unsigned selector, const char * const **groups, unsigned * const num_groups); int (*enable) (struct pinctrl_dev *pctldev, unsigned func_selector, //enable一個function當然要給我出function selector和pin group的selector unsigned group_selector); void (*disable) (struct pinctrl_dev *pctldev, unsigned func_selector,//enable的逆函數 unsigned group_selector); int (*gpio_request_enable) (struct pinctrl_dev *pctldev,//request並且enable一個單獨的gpio pin struct pinctrl_gpio_range *range, unsigned offset); void (*gpio_disable_free) (struct pinctrl_dev *pctldev,//gpio_request_free的逆函數 struct pinctrl_gpio_range *range, unsigned offset); int (*gpio_set_direction) (struct pinctrl_dev *pctldev,//設定gpio方向的回調函數 struct pinctrl_gpio_range *range, unsigned offset, bool input); }; confops成員的callback函數說明:
struct pinconf_ops { #ifdef CONFIG_GENERIC_PINCONF bool is_generic; #endif int (*pin_config_get) (struct pinctrl_dev *pctldev,//給定一個pin ID以及config type ID,獲取該引腳上指定的type的配置。 unsigned pin, unsigned long *config); int (*pin_config_set) (struct pinctrl_dev *pctldev,//設定一個指定pin的配置 unsigned pin, unsigned long config); int (*pin_config_set_bulk) (struct pinctrl_dev *pctldev, unsigned pin, unsigned long *configs, unsigned num_configs); int (*pin_config_group_get) (struct pinctrl_dev *pctldev,//以pin group為單位,獲取pin上的配置信息。 unsigned selector, unsigned long *config); int (*pin_config_group_set) (struct pinctrl_dev *pctldev,//以pin group為單位,設定pin group 的特性配置。 unsigned selector, unsigned long config); int (*pin_config_group_set_bulk) (struct pinctrl_dev *pctldev, unsigned selector, unsigned long *configs, unsigned num_configs); int (*pin_config_dbg_parse_modify) (struct pinctrl_dev *pctldev, //debug接口 const char *arg, unsigned long *config); void (*pin_config_dbg_show) (struct pinctrl_dev *pctldev, //debug接口 struct seq_file *s, unsigned offset); void (*pin_config_group_dbg_show) (struct pinctrl_dev *pctldev, //debug接口 struct seq_file *s, unsigned selector); void (*pin_config_config_dbg_show) (struct pinctrl_dev *pctldev,//debug接口 struct seq_file *s, unsigned long config); }; 7.dts文件中的pinctrl關鍵詞表 7.1 pin命名表 pin的命名遵循IC spec上的命名,以下命名表以每個pin默認的功能命名,但實際使用中各個pin的功能會隨着配置發生變化。
在dts中使用關鍵詞「actions,pins」(不同廠商不同,這是炬芯的)後跟名字數組來定義需要使用哪些pin,如:
i2c0_over_uart0_pull_up{ actions,pins = 「P_UART0_RX」,「P_UART0_TX」; actions,pull = <2>; } 7.2 MUX Group-Function表 7.2.1 Mux Group命名表 pin group按照寄存器的名字和bit來命名。比如:MFP_CTL1 bit22:21,定義了10個pin的mux:P_LVDS_OEP,P_LVDS_OEN,P_LVDS_ODP,P_LVDS_ODN,P_LVDS_OCP,P_LVDS_OCN,P_LVDS_OBP,P_LVDS_OBN,P_LVDS_OAP,P_LVDS_OAN。該pin group的名字為mfp1_22_21.
有些mfp寄存器的cell中,設置某一個值會將多個pin配置為不同的功能。那麼這個cell中的pin就不能歸為同一個pin group。需按情況拆解開,那麼拆解開的pin group的名字還會加上一些後綴。
比如:根據IC SPEC,mfp1 bit[31:29]可控制3根pin:P_KS_IN0,P_KS_IN1,P_KS_IN2.該cell的0x11選項會將這2跟pin分別定義為pwm0,pwm1,pwm4.
那麼就將其分拆為3個group:分別為「mfp1_31_29_ks_in2」、「mfp1_31_29_ks_in2」、「mfp1_31_29_ks_in0」。這幾個mfp cell分割以後,他們之間仍然存在硬件上的互斥關係,比如將mfp1_31_29_ks_in2的function選為pwm0,mfp1_31_29_ks_in1的function選為pwm1,可以工作。但是將mfp1_31_29_ks_in2的function選為pwm0,mfp1_31_29_ks_in1的function選為jtag,就會返回錯誤。
在pin group的命名表中還會看到「xxx_dummy」的命名,這種pin group在pinmux設置中可能會用到。這些pin group只有一種mux功能,所以在mfp寄存器中不會表示,但是這些pin可能和gpio復用,申請這些pin有助於發現它和gpio存在的潛在復用錯誤。
在dts中使用關鍵詞「actions,groups」後跟mux Group名字數組來定義需要使用哪些Mux group,如:
sd0_mfp_cmd_clk{
actions,groups = "mfp2_8_7","mfp2_6_5";
actions,function = "sd0";
};
7.2.2MUX Group-function Mux Group所控制的pin,可以通過設置Mux Function的方式改變IC內部連通性,使得同一組pin用作不同的功能,例如:
sd0_mfp_cmd_clk{ actions,groups ="mfp2_8_7","mfp2_6_5"; actions,function = "jtag"; }; 7.2.3 Drive Group paddrv使用的配置值完全等同於IC spec中關於PAD_DRVx寄存器的定義。
對於某些pin可以設置pin的驅動能力(即供電能力),可以通過配置driver group的等級對pin的驅動能力進行配置。
在dts中使用關鍵詞「actions,paddrv」後跟驅動能力等級來定義「actions,groups」指定drive group所代表的pin組使用哪種驅動能力,如:
sd0_d0_d3_cmd_clk_paddrv{ actions,groups="paddrv1_19_18","paddrv1_17_16"; actions,paddrv = "1" /*level 1, range :0 ~ 3*/ } 表示「paddrv1_19_18」所代表的P_SD0_CMD和paddrv1_17_16 所代表的P_SD0_CLK,使用驅動能力1來提升數據傳輸穩定性。 7.2.4 Pin Pull Up/Down 在dts中使用關鍵詞「actions,pull」後跟上下拉數據定義「actions,pin」指定的pin組使用哪種上下拉,如:
sd0_pull_d0_d3_cmd{ actions,pins = "P_SD0_CMD","P_SD0_CLK"; actions,pull = <1>; }; 表示將P_SD0_CMD和P_SD0_CLK這兩個pin下拉。
actions,pull = <0>,表示上下拉功能關閉
actions,pull = <1>,表示下拉
actions,pull = <2>,表示上拉
7.2.5 GPIO-PIN pin除了可以復用作各種功能外,還可以配置成GPIO使用,pinctrl子系統將GPIO子系統也管理起來了,因此申請GPIO的時候會去檢查該gpio所對應的pin是否已經被其他驅動申請作其他功能了。如果已經被申請則申請時會報錯,反之亦然。
8 使用dts描述pinctrl配置 8.1dts中pinctrl配置方法 所有的pinctrl-state都定義在pin controller device節點中:
在kernel/arch/arm64/boot/dts/s700_pinctrl.dtsi 中
/ { pinctrl@e01b0000 { compatible = "actions,s700-pinctrl"; reg = <0 0xe01b0000 0 0x1000>; pinctrl-names = "default"; pinctrl-0 = <&state_default>; clocks = <&clock CLK_GPIO>; clock-names = "mfp"; state_default: pinctrl_default { }; 而個驅動引用定義在pin controller device 節點中的子節點,即pinctrl-state節點。
各驅動使用如下方法引用pinctrl-state節點:
pinctrl-N:描述該設備需要使用的pin的一個狀態(pin state),相當於上述的state。N的數值必須從0開始順序遞增。pinctrl-N屬性的值為pin configuration nodes的phandle。pinctrl-N屬性引用的pin configuration nodes必須是pin controller device node的直接子節點。
pinctrl-names:為每個pin state定義一個名字。每個名字順序對應一個pin state。比如pinctrl-0的名字為「default」,pinctrl-1的名字對應「idle」。若不能指定pinctrl-names屬性,這樣的話,pin state的名字就是該屬性的「N」字符。比如pinctrl-0的名字為字符「0」
mmc@e0330000{ pinctrl-names = "pinctrl_mmc0","share_uart2_5"; pinctrl-0 = <&mm0_pinctrl_state>; pinctrl-1 = <&mmc_share_uart_state>; } 上面的例子中,mmc驅動定義的pinctrl-state有兩個,其中mmc0_state_default是定義pin controller device node下的直接子節點,表示sd0 的pin group 作為sd功能使用,而mmc_share_uart_state則表示作為serial功能使用,其中mmc_state_default對應的pinctrl-names 屬性為default,而mmc_share_uart_state對應的pinctrl-names屬性為share_uart2_5。 8.2 pin mapping database的建立 pin controller device節點配置好後,內核是如何獲取到pin mapping database並使用的呢?
通過查看pinctrl子系統代碼,了解到pin mapping database建立有兩種情況:
一種情況是主控驅動調用pinctrl_register註冊machine的struct pinctrl_desc結構到pinctrl子系統時,由pinctrl_register調用pinctrl_get創建。
另一種就是各驅動在加載時由統一設備驅動模型在driver和device綁定時調用devm_pinctrl_get轉而調用pinctrl_get創建。
下一篇中通過代碼來具體分析pinctrl_get。 ———————————————— 版權聲明:本文為CSDN博主「白鯨入海」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。 原文鏈接:https://blog.csdn.net/u012830148/article/details/80609337