通過前面硬件原理的介紹,我們知道配置一個(gè)pin需要經(jīng)過mux控制寄存器和pad控制寄存器,對于輸入引腳的配置,還需要另外配置輸入選擇寄存器。那么把這些概念用軟件來實(shí)現(xiàn)就是 pin 驅(qū)動(dòng)控制器的本質(zhì)。在了解驅(qū)動(dòng)前我們先來看幾個(gè)關(guān)鍵結(jié)構(gòu)體。
pinctrl_desc 結(jié)構(gòu)體
使用 struct pinctrl_desc 抽象一個(gè) pin controller,包含控制器的名字、pin腳的數(shù)量、pinmux功能、pinconf功能和pinctl功能。其中結(jié)構(gòu)體 pinmux_ops 和 pinconf_ops 分別用于配置mux模式和pad電氣屬性,而 pinctrl_ops 則是控制一組pin,如uart、i2c、spi等外設(shè)的pin組。該結(jié)構(gòu)的定義如下:
struct?pinctrl_desc?{
?const?char?*name;//pin控制器名字
?const?struct?pinctrl_pin_desc?*pins;//描述芯片的物理引腳pad資源
?unsigned?int?npins;
?const?struct?pinctrl_ops?*pctlops;//全局pin配置
?const?struct?pinmux_ops?*pmxops;?//mux配置
?const?struct?pinconf_ops?*confops;//電氣屬性配置
?struct?module?*owner;
?bool?link_consumers;
};
其中:
- pins
變量 pins 和 npins 把系統(tǒng)中所有的 pin 描述出來,并建立索引。驅(qū)動(dòng)為了和具體的 pin 對應(yīng)上,再將這些描述的這些 pin 組織成一個(gè) struct pinctrl_pin_desc 類型的數(shù)組,該類型的定義為:
struct?pinctrl_pin_desc?{
?unsigned?number;
?const?char?*name;
?void?*drv_data;
};
SoC中,有時(shí)需要將很多 pin 組合在一起,以實(shí)現(xiàn)特定的功能,例如 eqos 接口、i2c 接口等。因此 pin controller 需要以 group 為單位,訪問、控制多個(gè) pin,這就是 pin groups。
struct?group_desc?{
?const?char?*name;
?int?*pins;
?int?num_pins;
?void?*data;
};
在設(shè)備樹里表示就是:
pinctrl_eqos:?eqosgrp?{
?fsl,pins?=?<
??MX93_PAD_ENET1_MDC__ENET_QOS_MDC???0x57e
??MX93_PAD_ENET1_MDIO__ENET_QOS_MDIO???0x57e
??MX93_PAD_ENET1_RD0__ENET_QOS_RGMII_RD0???0x57e
??MX93_PAD_ENET1_RD1__ENET_QOS_RGMII_RD1???0x57e
??MX93_PAD_ENET1_RD2__ENET_QOS_RGMII_RD2???0x57e
??MX93_PAD_ENET1_RD3__ENET_QOS_RGMII_RD3???0x57e
??MX93_PAD_ENET1_RXC__CCM_ENET_QOS_CLOCK_GENERATE_RX_CLK?0x5fe
??MX93_PAD_ENET1_RX_CTL__ENET_QOS_RGMII_RX_CTL??0x57e
??MX93_PAD_ENET1_TD0__ENET_QOS_RGMII_TD0???0x57e
??MX93_PAD_ENET1_TD1__ENET_QOS_RGMII_TD1???0x57e
??MX93_PAD_ENET1_TD2__ENET_QOS_RGMII_TD2???0x57e
??MX93_PAD_ENET1_TD3__ENET_QOS_RGMII_TD3???0x57e
??MX93_PAD_ENET1_TXC__CCM_ENET_QOS_CLOCK_GENERATE_TX_CLK?0x5fe
??MX93_PAD_ENET1_TX_CTL__ENET_QOS_RGMII_TX_CTL??0x57e
?>;
};
pinctrl core 會(huì)在 struct pinctrl_ops 中抽象出三個(gè)回調(diào)函數(shù),用來獲取 pin groups 相關(guān)信息,如下:
pinctrl_ops
struct?pinctrl_ops?{
??//獲取系統(tǒng)中pin?groups的個(gè)數(shù),后續(xù)的操作,將以相應(yīng)的索引為單位(類似數(shù)組的下標(biāo),個(gè)數(shù)為數(shù)組的大?。?
?int?(*get_groups_count)?(struct?pinctrl_dev?*pctldev);
??//獲取指定group(由索引selector指定)的名稱
?const?char?*(*get_group_name)?(struct?pinctrl_dev?*pctldev,?unsigned?selector);
??//獲取指定group的所有pins(由索引selector指定),結(jié)果保存在pins(指針數(shù)組)和num_pins(指針)中
?int?(*get_group_pins)?(struct?pinctrl_dev?*pctldev,?unsigned?selector,?const?unsigned?**pins,?unsigned?*num_pins);
?void?(*pin_dbg_show)?(struct?pinctrl_dev?*pctldev,?struct?seq_file?*s,?unsigned?offset);
??//用于將device?tree中的pin?state信息轉(zhuǎn)換為pin?map
?int?(*dt_node_to_map)?(struct?pinctrl_dev?*pctldev,?struct?device_node?*np_config,?struct?pinctrl_map?**map,?unsigned?*num_maps);
?void?(*dt_free_map)?(struct?pinctrl_dev?*pctldev,?struct?pinctrl_map?*map,?unsigned?num_maps);
};
pinmux_ops
為了兼容不同的應(yīng)用場景,有很多管腳可以配置為不同的功能,例如A和B兩個(gè)管腳,既可以當(dāng)作普通的GPIO使用,又可以配置為I2C的的SCL和SDA,也可以配置為UART的TX和RX,這稱作管腳的復(fù)用(簡稱 pin mux)。使用 struct pinmux_ops 來抽象 pin mux 有關(guān)的操作,如下:
struct?pinmux_ops?{
??//檢查某個(gè)pin是否已作它用,用于管腳復(fù)用時(shí)的互斥
?int?(*request)?(struct?pinctrl_dev?*pctldev,?unsigned?offset);
??//request的反操作
?int?(*free)?(struct?pinctrl_dev?*pctldev,?unsigned?offset);
??//獲取系統(tǒng)中function的個(gè)數(shù)
?int?(*get_functions_count)?(struct?pinctrl_dev?*pctldev);
??//獲取指定function的名稱
?const?char?*(*get_function_name)?(struct?pinctrl_dev?*pctldev,?unsigned?selector);
??//獲取指定function所占用的pin?group
?int?(*get_function_groups)?(struct?pinctrl_dev?*pctldev,?unsigned?selector,?const?char?*?const?**groups,?unsigned?*num_groups);
??//將指定的pin?group(group_selector)設(shè)置為指定的function(func_selector)
?int?(*set_mux)?(struct?pinctrl_dev?*pctldev,?unsigned?func_selector,?unsigned?group_selector);
??//以下是gpio相關(guān)的操作
?int?(*gpio_request_enable)?(struct?pinctrl_dev?*pctldev,?struct?pinctrl_gpio_range?*range,?unsigned?offset);
?void?(*gpio_disable_free)?(struct?pinctrl_dev?*pctldev,?struct?pinctrl_gpio_range?*range,?unsigned?offset);
?int?(*gpio_set_direction)?(struct?pinctrl_dev?*pctldev,?struct?pinctrl_gpio_range?*range,?unsigned?offset,?bool?input);
??//為true時(shí),說明該pin?controller不允許某個(gè)pin作為gpio和其它功能同時(shí)使用
?bool?strict;
};
pinconf_ops
除了上面的 pin 和 pin group,有些管腳可以配置,比如上拉,下拉,高阻等。具體體現(xiàn)在 struct pinconf_ops 數(shù)據(jù)結(jié)構(gòu)中,如下:
struct?pinconf_ops?{
#ifdef?CONFIG_GENERIC_PINCONF
?bool?is_generic;
#endif
??//獲取指定?pin?的當(dāng)前配置,保存在?config?指針中
?int?(*pin_config_get)?(struct?pinctrl_dev?*pctldev,?unsigned?pin,?unsigned?long?*config);
??//設(shè)置指定pin的配置
?int?(*pin_config_set)?(struct?pinctrl_dev?*pctldev,?unsigned?pin,?unsigned?long?*configs,?unsigned?num_configs);
??//獲取指定pin?group的配置項(xiàng)
?int?(*pin_config_group_get)?(struct?pinctrl_dev?*pctldev,?unsigned?selector,?unsigned?long?*config);
??//設(shè)置指定pin?group的配置項(xiàng)
?int?(*pin_config_group_set)?(struct?pinctrl_dev?*pctldev,?unsigned?selector,?unsigned?long?*configs,?unsigned?num_configs);
??......
- pin state
根據(jù)前面的描述,pinctrl driver 抽象出來了一些離散的對象,并實(shí)現(xiàn)了這些對象的控制和配置方式。然后我們回到某一個(gè)具體的 device 上(如 lpuart,usdhc)。一個(gè)設(shè)備在某一狀態(tài)下(如工作狀態(tài)、休眠狀態(tài)、等等),所使用的pin(pin group)、pin(pin group)的 function 和 configuration,是唯一確定的。所以固定的組合可以確定固定的狀態(tài),在設(shè)備樹里用 pinctrl-names 指明狀態(tài)名字,pinctrl-x 指明狀態(tài)引腳。
- pin map
pin state 有關(guān)的信息是通過 pin map 收集,相關(guān)的數(shù)據(jù)結(jié)構(gòu)如下:
struct?pinctrl_map?{
??//device的名稱
?const?char?*dev_name;
??//pin?state的名稱
?const?char?*name;
??//該map的類型
?enum?pinctrl_map_type?type;
??//pin?controller?device的名字
?const?char?*ctrl_dev_name;
?union?{
??struct?pinctrl_map_mux?mux;
??struct?pinctrl_map_configs?configs;
?}?data;
};
enum?pinctrl_map_type?{
?PIN_MAP_TYPE_INVALID,
?//不需要任何配置,僅僅為了表示state的存在
?PIN_MAP_TYPE_DUMMY_STATE,
?//配置管腳復(fù)用
?PIN_MAP_TYPE_MUX_GROUP,
?//配置pin
?PIN_MAP_TYPE_CONFIGS_PIN,
?//配置pin?group
?PIN_MAP_TYPE_CONFIGS_GROUP,
};
struct?pinctrl_map_mux?{
?//group的名字
?const?char?*group;
?//function的名字
?const?char?*function;
};
struct?pinctrl_map_configs?{
?//該pin或者pin?group的名字
?const?char?*group_or_pin;
?//configuration數(shù)組
?unsigned?long?*configs;
?//配置項(xiàng)的個(gè)數(shù)
?unsigned?num_configs;
};
pinctrl driver 確定了 pin map 各個(gè)字段的格式之后,就可以在 dts 文件中維護(hù) pin state 以及相應(yīng)的 mapping table。pinctrl core 在初始化的時(shí)候,會(huì)讀取并解析 dts,并生成 pin map。
而各個(gè) client device 可以在自己的 dts node 中,直接引用 pinctrl driver 定義的 pin state,并在設(shè)備驅(qū)動(dòng)的相應(yīng)的位置,調(diào)用 pinctrl subsystem 提供的 API(pinctrl_lookup_state,pinctrl_select_state),active 或者 deactive 這些 state。
pin controller 驅(qū)動(dòng)初始化
pin controller 的設(shè)備樹如下所示:
iomuxc:?pinctrl@443c0000?{
?compatible?=?"fsl,imx93-iomuxc";
?reg?=?<0x443c0000?0x10000>;
?status?=?"okay";
};
&iomuxc?{
?pinctrl_eqos:?eqosgrp?{
??fsl,pins?=?<
???......
??>;
?};
?pinctrl_eqos_sleep:?eqosgrpsleep?{
??fsl,pins?=?<
???......
??>;
?};
?pinctrl_fec:?fecgrp?{
??fsl,pins?=?<
???......
??>;
?};
?pinctrl_fec_sleep:?fecsleepgrp?{
??fsl,pins?=?<
???......
??>;
?};
?......
?pinctrl_sai1:?sai1grp?{
??fsl,pins?=?<
???......
??>;
?};
?pinctrl_sai1_sleep:?sai1grpsleep?{
??fsl,pins?=?<
???......
??>;
?};
?......
};
pin controller驅(qū)動(dòng)的初始化大致如下: