轉自:http://www.javashuo.com/article/p-cakaxokp-md.htmlnode
版權聲明:本文爲博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接和本聲明。
本文連接:https://blog.csdn.net/u012830148/article/details/80609337
1.Linux Pinctrl子系統簡介
在許多soc內部都包含有pin控制器,經過pin控制器的寄存器,咱們能夠配置一個或者一組引腳的功能和特性。在軟件方面,Linux內核提供了pinctrl子系統,目的是爲了統一各soc廠商的pin腳管理。linux
2.Linux Pinctrl子系統提供的功能
(1)管理系統中全部的能夠控制的pin,在系統初始化的時候,枚舉全部能夠控制的pin,並標識這些pin。數組
(2)管理這些pin的複用(Multiplexing)。對於SOC而言,其引腳除了配置成普通的GPIO以外,若干個引腳還能夠組成一個pin group,行程特定的功能。pin control subsystem要管理全部的pin group。app
(3)配置這些pin的特性。例如使能或關閉引腳上的pull-up、pull-down電阻,配置引腳的driver strength。ide
3.pinctrl相關概念
普通driver調用pin control subsystem的主要目標有兩個:函數
(1)設定該設備的功能複用。.net
(2)設定該device對應的pin的電氣特性。debug
設定設備的功能複用須要瞭解兩個概念,一個是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就會須要處於另外的狀態。blog
綜合上述的需求,就定義了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) failed\n",
__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 %s\n",
.....
}
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 handle\n");
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 state\n");
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 state\n");
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、一、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