Pinctrl子系统主要数据结构

参考资料:

  • 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

一、设备树

1.1 理想模型

在这里插入图片描述左边的state_0_node_a、state_1_node_a、state_2_node_a分别对应,右边的device的引用节点。
右边device里的pinctrl-0对应于"default"状态,pinctrl-1对应于"sleep"状态。
右边device里不同的状态,分别对应不同的pin controller的

1.2 实际的例子

在这里插入图片描述

这个图是imx6ull的设备树配置,右边是i2c1的配置,左边是pinctroller配置
右边部分:i2c1使用了pinctrl_i2c1的引脚配置,这个引脚的格式在左边,对应于i2c1grp。结构体内包含了I2C的SCL、SDA引脚的配置。

左边部分:设备驱动匹配到fsl,imx6ul-iomuxc后,会根据设备树分配、设置、注册引脚。

二、pincontroller数据结构

pinctroller的三大作用,有助于理解相关的数据结构:

  • 引脚命名和引脚的枚举(Enumerating and naming)
  • 引脚复用(Multiplexing):比如用作GPIO、I2C或其他功能
  • 引脚配置(Configuration):比如上拉、下拉、open drain、驱动强度等

2.1 pinctrl_desc和pinctrl_dev

这两个结构体就是代码中用来对应,左边的pin controller设备树的信息,包括注册、描述、获得、设置、复用、配置。

2.1.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);
struct pinctrl_dev {
	struct list_head node;
	struct pinctrl_desc *desc;				//需要提供一个pinctrl_desc
	struct radix_tree_root pin_desc_tree;
	struct list_head gpio_ranges;
	struct device *dev;
	struct module *owner;
	void *driver_data;
	struct pinctrl *p;
	struct pinctrl_state *hog_default;
	struct pinctrl_state *hog_sleep;
	struct mutex mutex;
#ifdef CONFIG_DEBUG_FS
	struct dentry *device_root;
#endif
};

struct pinctrl_desc {
	const char *name;
	const struct pinctrl_pin_desc *pins;				//结构体数组,描述的是单个引脚,支持哪些功能
	unsigned int npins;											//支持多少个引脚
	const struct pinctrl_ops *pctlops;				//下面三个是操作函数,pctops用来描述一组引脚
	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_desc示例:

    imx_pinctrl_desc->name = dev_name(&pdev->dev);
    imx_pinctrl_desc->pins = info->pins;
    imx_pinctrl_desc->npins = info->npins;
    imx_pinctrl_desc->pctlops = &imx_pctrl_ops;
    imx_pinctrl_desc->pmxops = &imx_pmx_ops;
    imx_pinctrl_desc->confops = &imx_pinconf_ops;
    imx_pinctrl_desc->owner = THIS_MODULE;

2.1.2 作用1:描述、获得引脚

使用结构体pinctrl_pin_desc来描述一个引脚,一个pin controller有多个引脚。

/**
 * 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_coutget_group_pins
  • 处理设备中pin_constroller中的某个节点:dt_node_to_map,把device_node转换为一系列的pinctrl_map
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);
};

2.1.3 作用2:引脚复用

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 *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);
	bool strict;
};

2.1.4 引脚配置

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

2.1.5 使用pinctrl_desc注册得到pinctrl_dev

调用devm_pinctrl_registerhuopinctrl_register,就可以根据pinctrl_desc构造出pinctrl_dev,并且把pinctrl_dev放入链表:

struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc,
							 struct device *dev, void *driver_data);
{
		//...
		struct pinctrl_dev *pctldev;
		pctldev = kzalloc(sizeof(*pctldev), GFP_KERNEL);

		pctldev->owner = pctldesc->owner;
		pctldev->desc = pctldesc;
		pctldev->driver_data = driver_data;

		ret = pinctrl_check_ops(pctldev);

		ret = pinmux_check_ops(pctldev);

		ret = pinconf_check_ops(pctldev);

		ret = pinctrl_register_pins(pctldev, pctldesc->pins, pctldesc->npins);

		list_add_tail(&pctldev->node, &pinctrldev_list);		//根据构造出的pinctrl_dev,放入链表
		//....
}

三、client的数据结构

在设备树中,使用pinctrl时格式如下:

/* For a client device requiring named states */
device {
	pinctrl-name = "active", "idle";
	pinctrl-0 = <&state_0_node_a>;
	pinctrl-1 = <&state_1_node_a &state_1_node_b>;
};

设备节点要么被转换为platform_device,或者其他结构体(比如i2c_client),但是里面都会有一个device结构体,比如:

struct platform_device {
	const char	*name;
	int		id;
	bool		id_auto;
	struct device	dev;									//这里包含一个device结构体
	u32		num_resources;
	struct resource	*resource;

	const struct platform_device_id	*id_entry;
	char *driver_override; /* Driver name to force a match */

	/* MFD cell pointer */
	struct mfd_cell *mfd_cell;

	/* arch specific additions */
	struct pdev_archdata	archdata;
};

struct device {
	struct device		*parent;

	struct device_private	*p;

	struct kobject kobj;
	const char		*init_name; /* initial name of the device */
	const struct device_type *type;

	struct mutex		mutex;	/* mutex to synchronize calls to
					 * its driver.
					 */

	struct bus_type	*bus;		/* type of bus device is on */
	struct device_driver *driver;	/* which driver has allocated this
					   device */
	void		*platform_data;	/* Platform specific data, device
					   core doesn't touch it */
	void		*driver_data;	/* Driver data, set and get with
					   dev_set/get_drvdata */
	struct dev_links_info	links;
	struct dev_pm_info	power;
	struct dev_pm_domain	*pm_domain;

#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
	struct irq_domain	*msi_domain;
#endif
#ifdef CONFIG_PINCTRL
	struct dev_pin_info	*pins;				//这里就包含了一个pincontroller的信息
#endif
#ifdef CONFIG_GENERIC_MSI_IRQ
	struct list_head	msi_list;
#endif
	//....
};

3.1 dev_pin_info

每个device结构体里都有一个dev_pin_info结构体,用来保存设备的pinctrl信息:

//内核根据设备树中设置的pinctrl-name="default","sleep"等内容,构造出相应的pinctrl_state状态
struct dev_pin_info {
    struct pinctrl *p;		//包含了pinctrl的链表
    struct pinctrl_state *default_state;		//如果有的话就是default状态
    struct pinctrl_state *init_state;		//初始化状态
#ifdef CONFIG_PM
    struct pinctrl_state *sleep_state;		//休眠状态
    struct pinctrl_state *idle_state;		//空闲状态
#endif
};

//用来描述自己定义的状态
struct pinctrl {
	struct list_head node;					//链表
	struct device *dev;		//指向device设备
	struct list_head states;		//pinctrl状态链表
	struct pinctrl_state *state;	//pinctrl_state链表
	struct list_head dt_maps;
	struct kref users;
};

//设备树中设置了不同的状态,在代码中对应于不同的pinctrl_state
struct pinctrl_state {
    struct list_head node;
    const char *name;				//状态名字
    struct list_head settings;		//settings链表,状态对应的配置
};

//这里通过pinctrl_ops->dt_node_to_map来把设备节点转换成一系列的pinctrl_map
struct pinctrl_map {
    const char *dev_name;
    const char *name;
    enum pinctrl_map_type type;
    const char *ctrl_dev_name;
    union {
        struct pinctrl_map_mux mux;			//引脚复用
        struct pinctrl_map_configs configs;			//引脚配置
    } data;
};

struct pinctrl_map_mux {
    const char *group;					//用名字来表示组
    const char *function;				//用名字来表示功能
};

struct pinctrl_map_configs {
    const char *group_or_pin;			//表示配置哪一组或者哪一个引脚
    unsigned long *configs;			//数组表示一个或多个配置项(上拉,输出强度。。。)
    unsigned num_configs;			
};

//pinctrl_map会转换成pinctrl_setting
//这个pinctrl_setting会被存放在pinctrl_state下的settings链表里
struct pinctrl_setting {
	struct list_head node;
	enum pinctrl_map_type type;
	struct pinctrl_dev *pctldev;
	const char *dev_name;
	union {
		struct pinctrl_setting_mux mux;				//引脚复用
		struct pinctrl_setting_configs configs;		//引脚配置
	} data;
};

struct pinctrl_setting_mux {
	unsigned group;				//把当前组引脚,用整数表示
	unsigned func;			//配置成功能
};

struct pinctrl_setting_configs {			//与pinctrl_map_configs类似
	unsigned group_or_pin;
	unsigned long *configs;
	unsigned num_configs;
};

3.2 pinctrl

假设芯片上有多个pin controller,那么这个设备使用哪个pin controller?
这需要通过设备树来确定:

  • 分析设备树,找到pin controller
  • 对于每个状态,比如defualt、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.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链表

在这里插入图片描述

四、使用pinctrl_setting

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) {			//遍历所有的settings,根据不同的标志位进行设置
				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;
			}		

在这里插入图片描述

自我总结:

左边的设备树对应着pinctrl的各种引脚配置,这些配置分成了不同的组,不同的组里用到了不同的引脚,每一个引脚有各自的功能。
右边的设备里表明了一种设备,这个设备肯定使用到了芯片的某些功能,可以直接引用左边设备树的节点。

设备树在转换成驱动代码时,通过compatible匹配,左边的设备树主要的数据就是pinctrl_dev 下的pinctrl_desc结构体,它包含的信息不仅仅只有设备树里的各个引脚的功能,还包括了一些操作函数。这些函数用来配置引脚为不同的功能,不同的配置。
右边的设备树主要的数据结构体是dev_pin_info,它包含了设备的一些默认状态下的配置,这些配置操作就是pinctrl_desc中带的。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

习惯就好zz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值