3 Pinctrl子系统主要数据结构
参考资料:
- Linux 5.x内核
- Documentation\devicetree\bindings\pinctrl\pinctrl-bindings.txt
- arch/arm/boot/dts/stm32mp151.dtsi
- arch/arm/boot/dts/stm32mp157-100ask-pinctrl.dtsi
- arch/arm/boot/dts/stm32mp15xx-100ask.dtsi
- drivers\pinctrl\stm32\pinctrl-stm32mp157.c
- drivers\pinctrl\stm32\pinctrl-stm32.c
- Linux 4.x内核文档
- Documentation\pinctrl.txt
- Documentation\devicetree\bindings\pinctrl\pinctrl-bindings.txt
- arch/arm/boot/dts/imx6ull-14x14-evk.dts
- arch/arm/boot/dts/100ask_imx6ull-14x14.dts
- drivers\pinctrl\freescale\pinctrl-imx6ul.c
- drivers\pinctrl\freescale\pinctrl-imx.c
3.1 pincontroller 的数据结构
记住pinctrl的三大作用,有助于理解所涉及的数据结构:
- 引脚枚举与命名(Enumerating and naming)
- 引脚复用(Multiplexing):比如用作GPIO、I2C或其他功能
- 引脚配置(Configuration):比如上拉、下来、open drain、驱动强度等
1. 结构体引入
pincontroller 虽然是一个软件的概念,但是它背后是有硬件支持的,所以可以使用一个结构体来表示它:pinctrl_dev。
怎么构造出 pinctrl_dev?我们只需要描述它:提供一个 pinctrl_desc,然后调用 pinctrl_register 就可以:
struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc,
struct device *dev, void *driver_data);
怎么使用 pinctrl_desc、pinctrl_dev 来描述一个 pin controller?这两个结构体定义如下:
/**
* struct pinctrl_desc - pin controller descriptor, register this to pin
* control subsystem
* @name: name for the pin controller
* @pins: an array of pin descriptors describing all the pins handled by
* this pin controller
* @npins: number of descriptors in the array, usually just ARRAY_SIZE()
* of the pins field above
* @pctlops: pin control operation vtable, to support global concepts like
* grouping of pins, this is optional.
* @pmxops: pinmux operations vtable, if you support pinmuxing in your driver
* @confops: pin config operations vtable, if you support pin configuration in
* your driver
* @owner: module providing the pin controller, used for refcounting
* @num_custom_params: Number of driver-specific custom parameters to be parsed
* from the hardware description
* @custom_params: List of driver_specific custom parameters to be parsed from
* the hardware description
* @custom_conf_items: Information how to print @params in debugfs, must be
* the same size as the @custom_params, i.e. @num_custom_params
*/
struct pinctrl_desc {
const char *name;
struct pinctrl_pin_desc const *pins;
unsigned int npins;
const struct pinctrl_ops *pctlops;
const struct pinmux_ops *pmxops;
const struct pinconf_ops *confops;
struct module *owner;
#ifdef CONFIG_GENERIC_PINCONF
unsigned int num_custom_params;
const struct pinconf_generic_params *custom_params;
const struct pin_config_item *custom_conf_items;
#endif
};
如 imx6ull 的 Pinctrl 子系统中:
iomuxc: iomuxc@020e0000 {
compatible = "fsl,imx6ul-iomuxc";
reg = <0x020e0000 0x4000>;
};
当 compatible 被匹配后,就会调用 probe 函数(在 pinctrl-imx6ul.c 中), 其中有
2. 作用1:描述、获得引脚
使用结构体 pinctrl_pin_desc (即:pins) 来描述一个引脚,一个 pin controller 有多个引脚,会有该结构体的一个数数组,用 pins 指向这个数组:
/**
* struct pinctrl_pin_desc - boards/machines provide information on their
* pins, pads or other muxable units in this struct
* @number: unique pin number from the global pin number space
* @name: a name for this pin
* @drv_data: driver-defined per-pin data. pinctrl core does not touch this
*/
struct pinctrl_pin_desc {
unsigned number;
const char *name;
void *drv_data;
};
使用 pinctrl_ops 来操作引脚,主要功能有二:
- 来取出某组的引脚:get_groups_count、get_group_pins
- 处理设备树中 pin controller 中的某个节点:dt_node_to_map,把 device_node 转换为一系列的 pinctrl_map
/**
* struct pinctrl_ops - global pin control operations, to be implemented by
* pin controller drivers.
* @get_groups_count: Returns the count of total number of groups registered.
* @get_group_name: return the group name of the pin group
* @get_group_pins: return an array of pins corresponding to a certain
* group selector @pins, and the size of the array in @num_pins
* @pin_dbg_show: optional debugfs display hook that will provide per-device
* info for a certain pin in debugfs
* @dt_node_to_map: parse a device tree "pin configuration node", and create
* mapping table entries for it. These are returned through the @map and
* @num_maps output parameters. This function is optional, and may be
* omitted for pinctrl drivers that do not support device tree.
* @dt_free_map: free mapping table entries created via @dt_node_to_map. The
* top-level @map pointer must be freed, along with any dynamically
* allocated members of the mapping table entries themselves. This
* function is optional, and may be omitted for pinctrl drivers that do
* not support device tree.
*/
struct pinctrl_ops {
int (*get_groups_count) (struct pinctrl_dev *pctldev);
const char *(*get_group_name) (struct pinctrl_dev *pctldev,
unsigned selector);
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);
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);
};
3. 作用2:引脚复用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7gVAlMHP-1650597060398)(assets/image-20220422095811884.png)]
在一些设备中需要的引脚可以用不同组,设置时会调用 struct pinmux_ops
的set_mux
函数。即把 IO 复用为某个功能。
/**
* struct pinmux_ops - pinmux operations, to be implemented by pin controller
* drivers that support pinmuxing
* @request: called by the core to see if a certain pin can be made
* available for muxing. This is called by the core to acquire the pins
* before selecting any actual mux setting across a function. The driver
* is allowed to answer "no" by returning a negative error code
* @free: the reverse function of the request() callback, frees a pin after
* being requested
* @get_functions_count: returns number of selectable named functions available
* in this pinmux driver
* @get_function_name: return the function name of the muxing selector,
* called by the core to figure out which mux setting it shall map a
* certain device to
* @get_function_groups: return an array of groups names (in turn
* referencing pins) connected to a certain function selector. The group
* name can be used with the generic @pinctrl_ops to retrieve the
* actual pins affected. The applicable groups will be returned in
* @groups and the number of groups in @num_groups
* @set_mux: enable a certain muxing function with a certain pin group. The
* driver does not need to figure out whether enabling this function
* conflicts some other use of the pins in that group, such collisions
* are handled by the pinmux subsystem. The @func_selector selects a
* certain function whereas @group_selector selects a certain set of pins
* to be used. On simple controllers the latter argument may be ignored
* @gpio_request_enable: requests and enables GPIO on a certain pin.
* Implement this only if you can mux every pin individually as GPIO. The
* affected GPIO range is passed along with an offset(pin number) into that
* specific GPIO range - function selectors and pin groups are orthogonal
* to this, the core will however make sure the pins do not collide.
* @gpio_disable_free: free up GPIO muxing on a certain pin, the reverse of
* @gpio_request_enable
* @gpio_set_direction: Since controllers may need different configurations
* depending on whether the GPIO is configured as input or output,
* a direction selector function may be implemented as a backing
* to the GPIO controllers that need pin muxing.
*/
struct pinmux_ops {
int (*request) (struct pinctrl_dev *pctldev, unsigned offset);
int (*free) (struct pinctrl_dev *pctldev, unsigned offset);
int (*get_functions_count) (struct pinctrl_dev *pctldev);
const char *(*get_function_name) (struct pinctrl_dev *pctldev,
unsigned selector);
int (*get_function_groups) (struct pinctrl_dev *pctldev,
unsigned selector,
const char * const **groups,
unsigned * const num_groups);
int (*set_mux) (struct pinctrl_dev *pctldev, unsigned func_selector,
unsigned group_selector);//重点
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);
}
4. 作用3:引脚配置
即配置 电气属性,如上下拉,输出强度。
/**
* struct pinconf_ops - pin config operations, to be implemented by
* pin configuration capable drivers.
* @is_generic: for pin controllers that want to use the generic interface,
* this flag tells the framework that it's generic.
* @pin_config_get: get the config of a certain pin, if the requested config
* is not available on this controller this should return -ENOTSUPP
* and if it is available but disabled it should return -EINVAL
* @pin_config_set: configure an individual pin
* @pin_config_group_get: get configurations for an entire pin group
* @pin_config_group_set: configure all pins in a group
* @pin_config_dbg_parse_modify: optional debugfs to modify a pin configuration
* @pin_config_dbg_show: optional debugfs display hook that will provide
* per-device info for a certain pin in debugfs
* @pin_config_group_dbg_show: optional debugfs display hook that will provide
* per-device info for a certain group in debugfs
* @pin_config_config_dbg_show: optional debugfs display hook that will decode
* and display a driver's pin configuration parameter
*/
struct pinconf_ops {
#ifdef CONFIG_GENERIC_PINCONF
bool is_generic;
#endif
int (*pin_config_get) (struct pinctrl_dev *pctldev,
unsigned pin,
unsigned long *config);
int (*pin_config_set) (struct pinctrl_dev *pctldev,
unsigned pin,
unsigned long *configs,
unsigned num_configs);
int (*pin_config_group_get) (struct pinctrl_dev *pctldev,
unsigned selector,
unsigned long *config);
int (*pin_config_group_set) (struct pinctrl_dev *pctldev,
unsigned selector,
unsigned long *configs,
unsigned num_configs);
int (*pin_config_dbg_parse_modify) (struct pinctrl_dev *pctldev,
const char *arg,
unsigned long *config);
void (*pin_config_dbg_show) (struct pinctrl_dev *pctldev,
struct seq_file *s,
unsigned offset);
void (*pin_config_group_dbg_show) (struct pinctrl_dev *pctldev,
struct seq_file *s,
unsigned selector);
void (*pin_config_config_dbg_show) (struct pinctrl_dev *pctldev,
struct seq_file *s,
unsigned long config);
};
5. 使用 pinctrl_desc 注册得到 pinctrl_dev
调用 devm_pinctrl_register 或 pinctrl_register,就可以根据 pinctrl_desc 构造出 pinctrl_dev,并且把 pinctrl_dev 放入链表:
ipctl->pctl = pinctrl_register(imx_pinctrl_desc, &pdev->dev, ipctl);//probe 函数中的调用
devm_pinctrl_register
pinctrl_register
struct pinctrl_dev *pctldev;
pctldev = kzalloc(sizeof(*pctldev), GFP_KERNEL);
pctldev->owner = pctldesc->owner;
pctldev->desc = pctldesc;
pctldev->driver_data = driver_data;
/* check core ops for sanity */
ret = pinctrl_check_ops(pctldev);
/* If we're implementing pinmuxing, check the ops for sanity */
ret = pinmux_check_ops(pctldev);
/* If we're implementing pinconfig, check the ops for sanity */
ret = pinconf_check_ops(pctldev);
/* Register all the pins */
ret = pinctrl_register_pins(pctldev, pctldesc->pins, pctldesc->npins);
list_add_tail(&pctldev->node, &pinctrldev_list);
3.2 client 的数据结构
在设备树中,使用 pinctrl 子系统时格式如下:
client 的设备节点要么被转换为 platform_device,或者其他结构体(比如i2c_client),但是里面都会有一个 device 结构体,比如:
1 dev_pin_info
每个device结构体里都有一个 dev_pin_info 结构体,用来保存设备的 pinctrl 信息:
/**
* struct dev_pin_info - pin state container for devices
* @p: pinctrl handle for the containing device
* @default_state: the default state for the handle, if found
*/
struct dev_pin_info {
struct pinctrl *p;
struct pinctrl_state *default_state;
#ifdef CONFIG_PM
struct pinctrl_state *sleep_state;
struct pinctrl_state *idle_state;
#endif
};
在 client 的节点中,有 pinctrl-names 属性,说明 pin 的状态,如 default ,sleep,这些状态就保存在 dev_pin_info 中。
其中将 sleep, idle 和 default 状态这些比较常用的拿出来,自定义的状态放 p 中的 *state。
2 pinctrl
/**
* struct pinctrl - per-device pin control state holder
* @node: global list node
* @dev: the device using this pin control handle
* @states: a list of states for this device
* @state: the current state
* @dt_maps: the mapping table chunks dynamically parsed from device tree for
* this device, if any
* @users: reference count
*/
struct pinctrl {
struct list_head node;
struct device *dev;
struct list_head states;
struct pinctrl_state *state;
struct list_head dt_maps;
struct kref users;
};
假设芯片上有多个 pin controller(即 iomux 下有多少个节点),那么这个设备使用哪个 pin controller?
这需要通过设备树来确定:
- 分析设备树,找到 pin controller
- 对于每个状态,比如 default、init,去分析 pin controller 中的设备树节点
- 使用 pin controller 的
pinctrl_ops.dt_node_to_map
来处理设备树的 pinctrl 节点信息,得到一系列的 pinctrl_map - 这些 pinctrl_map 放在
pinctrl.dt_maps
链表中 - 每个 pinctrl_map 都被转换为 pinctrl_setting,放在对应的
pinctrl_state.settings
链表中
- 使用 pin controller 的
3 pinctrl_map 和 pinctrl_setting
设备引用 pin controller 中的某个节点时,这个节点会被转换为一些列的 pinctrl_map:
- 转换为多少个 pinctrl_map,完全由具体的驱动决定
- 每个 pinctrl_map,又被转换为一个 pinctrl_setting
- 举例,设备节点里有:
pinctrl-0 = <&state_0_node_a>
- pinctrl-0 对应一个状态,会得到一个 pinctrl_state
- state_0_node_a 节点被解析为一系列的 pinctrl_map
- 这一系列的 pinctrl_map 被转换为一系列的 pinctrl_setting
- 这些 pinctrl_setting 被放入 pinctrl_state 的 settings 链表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3tGjAWKD-1650597060401)(assets/20_dt_to_map.png)]
4. 使用 pinctrl_setting
调用过程:
really_probe
pinctrl_bind_pins
pinctrl_select_state
/* Apply all the settings for the new state */
list_for_each_entry(setting, &state->settings, node) {
switch (setting->type) {
case PIN_MAP_TYPE_MUX_GROUP:
ret = pinmux_enable_setting(setting);
ret = ops->set_mux(...);
break;
case PIN_MAP_TYPE_CONFIGS_PIN:
case PIN_MAP_TYPE_CONFIGS_GROUP:
ret = pinconf_apply_setting(setting);
ret = ops->pin_config_group_set(...);
break;
default:
ret = -EINVAL;
break;
}