通过前面硬件原理的介绍,我们知道配置一个pin需要经过mux控制寄存器和pad控制寄存器,对于输入引脚的配置,还需要另外配置输入选择寄存器。那么把这些概念用软件来实现就是 pin 驱动控制器的本质。在了解驱动前我们先来看几个关键结构体。
pinctrl_desc 结构体
使用 struct pinctrl_desc 抽象一个 pin controller,包含控制器的名字、pin脚的数量、pinmux功能、pinconf功能和pinctl功能。其中结构体 pinmux_ops 和 pinconf_ops 分别用于配置mux模式和pad电气属性,而 pinctrl_ops 则是控制一组pin,如uart、i2c、spi等外设的pin组。该结构的定义如下:
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 把系统中所有的 pin 描述出来,并建立索引。驱动为了和具体的 pin 对应上,再将这些描述的这些 pin 组织成一个 struct pinctrl_pin_desc 类型的数组,该类型的定义为:
struct?pinctrl_pin_desc?{
?unsigned?number;
?const?char?*name;
?void?*drv_data;
};
SoC中,有时需要将很多 pin 组合在一起,以实现特定的功能,例如 eqos 接口、i2c 接口等。因此 pin controller 需要以 group 为单位,访问、控制多个 pin,这就是 pin groups。
struct?group_desc?{
?const?char?*name;
?int?*pins;
?int?num_pins;
?void?*data;
};
在设备树里表示就是:
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 会在 struct pinctrl_ops 中抽象出三个回调函数,用来获取 pin groups 相关信息,如下:
pinctrl_ops
struct?pinctrl_ops?{
??//获取系统中pin?groups的个数,后续的操作,将以相应的索引为单位(类似数组的下标,个数为数组的大小)
?int?(*get_groups_count)?(struct?pinctrl_dev?*pctldev);
??//获取指定group(由索引selector指定)的名称
?const?char?*(*get_group_name)?(struct?pinctrl_dev?*pctldev,?unsigned?selector);
??//获取指定group的所有pins(由索引selector指定),结果保存在pins(指针数组)和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信息转换为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
为了兼容不同的应用场景,有很多管脚可以配置为不同的功能,例如A和B两个管脚,既可以当作普通的GPIO使用,又可以配置为I2C的的SCL和SDA,也可以配置为UART的TX和RX,这称作管脚的复用(简称 pin mux)。使用 struct pinmux_ops 来抽象 pin mux 有关的操作,如下:
struct?pinmux_ops?{
??//检查某个pin是否已作它用,用于管脚复用时的互斥
?int?(*request)?(struct?pinctrl_dev?*pctldev,?unsigned?offset);
??//request的反操作
?int?(*free)?(struct?pinctrl_dev?*pctldev,?unsigned?offset);
??//获取系统中function的个数
?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)设置为指定的function(func_selector)
?int?(*set_mux)?(struct?pinctrl_dev?*pctldev,?unsigned?func_selector,?unsigned?group_selector);
??//以下是gpio相关的操作
?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时,说明该pin?controller不允许某个pin作为gpio和其它功能同时使用
?bool?strict;
};
pinconf_ops
除了上面的 pin 和 pin group,有些管脚可以配置,比如上拉,下拉,高阻等。具体体现在 struct pinconf_ops 数据结构中,如下:
struct?pinconf_ops?{
#ifdef?CONFIG_GENERIC_PINCONF
?bool?is_generic;
#endif
??//获取指定?pin?的当前配置,保存在?config?指针中
?int?(*pin_config_get)?(struct?pinctrl_dev?*pctldev,?unsigned?pin,?unsigned?long?*config);
??//设置指定pin的配置
?int?(*pin_config_set)?(struct?pinctrl_dev?*pctldev,?unsigned?pin,?unsigned?long?*configs,?unsigned?num_configs);
??//获取指定pin?group的配置项
?int?(*pin_config_group_get)?(struct?pinctrl_dev?*pctldev,?unsigned?selector,?unsigned?long?*config);
??//设置指定pin?group的配置项
?int?(*pin_config_group_set)?(struct?pinctrl_dev?*pctldev,?unsigned?selector,?unsigned?long?*configs,?unsigned?num_configs);
??......
- pin state
根据前面的描述,pinctrl driver 抽象出来了一些离散的对象,并实现了这些对象的控制和配置方式。然后我们回到某一个具体的 device 上(如 lpuart,usdhc)。一个设备在某一状态下(如工作状态、休眠状态、等等),所使用的pin(pin group)、pin(pin group)的 function 和 configuration,是唯一确定的。所以固定的组合可以确定固定的状态,在设备树里用 pinctrl-names 指明状态名字,pinctrl-x 指明状态引脚。
- pin map
pin state 有关的信息是通过 pin map 收集,相关的数据结构如下:
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,
?//配置管脚复用
?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数组
?unsigned?long?*configs;
?//配置项的个数
?unsigned?num_configs;
};
pinctrl driver 确定了 pin map 各个字段的格式之后,就可以在 dts 文件中维护 pin state 以及相应的 mapping table。pinctrl core 在初始化的时候,会读取并解析 dts,并生成 pin map。
而各个 client device 可以在自己的 dts node 中,直接引用 pinctrl driver 定义的 pin state,并在设备驱动的相应的位置,调用 pinctrl subsystem 提供的 API(pinctrl_lookup_state,pinctrl_select_state),active 或者 deactive 这些 state。
pin controller 驱动初始化
pin controller 的设备树如下所示:
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驱动的初始化大致如下:
2702
下载ECAD模型