Pinctrl 子系统详解

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_opsset_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 链表中

在这里插入图片描述

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;
			}		

在这里插入图片描述

  • 3
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]: pinctrl子系统是Linux内核中的一个框架,用于统一管理不同系统芯片的引脚控制和配置。在pinctrl子系统中,有一个主要的结构体struct pinctrl_dev,用于表示一个引脚控制器设备。一般系统只会有一个struct pinctrl_dev实例。通过调用pinctrl_register_pins函数,可以为每个引脚分配一个独立的struct pin_desc结构体,并进行相应的赋值。struct pin_desc是pinctrl子系统用来管理每个引脚的最小单元。 引用\[2\]: 在pinctrl子系统中,引脚控制器并不只有一个,而是可以有多个。在设备树中,含有pinctrl-names和pinctrl-0属性的节点会拥有一个struct pinctrl结构体。pinctrl-names和pinctrl-0属性中的内容指定了要控制的引脚。有时候会遇到多个pinctrl-names和pinctrl-0属性,可以通过这种方式来配置不同的引脚控制。 引用\[3\]: pinctrl子系统的目的是为了统一各种不同的系统芯片中的引脚管理。在ARM的各种SOC芯片中,一个引脚可以被复用为不同功能的引脚,例如GPIO、SPI、I2C、UART等。pinctrl子系统的引入正是为了解决这种复用问题,使得不同SOC厂商的引脚管理能够在Linux内核中得到统一。 #### 引用[.reference_title] - *1* *3* [Linux pinctrl子系统框架流程详解(基于Kernel 3.16,arm,设备树)](https://blog.csdn.net/ZHONGkunjia/article/details/89873417)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [基于Linux的Pinctrl子系统框架源码分析](https://blog.csdn.net/qq_42017846/article/details/127795402)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值