client端使用pinctrl过程的情景分析--基于imx6ull

参考资料:
Linux4.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

一、回顾client的数据结构

在设备树中,使用pinctrl时格式如下:
client端使用pinctrl过程的情景分析--基于imx6ull_第1张图片设备节点要么被转换为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;			//有一个描述pinctrl的结构体
#endif
#ifdef CONFIG_GENERIC_MSI_IRQ
	struct list_head	msi_list;
#endif
	//....
};

struct pinctrl_state {
	struct list_head node;
	const char *name;
	struct list_head settings;			//状态下的一系列的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 {
	unsigned group_or_pin;			//引脚值或者组
	unsigned long *configs;		//配置值
	unsigned num_configs;			//多少个配置值
};

1.1 dev_pin_info

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

struct dev_pin_info {
	struct pinctrl *p;				//多个state链表
	struct pinctrl_state *default_state;
	struct pinctrl_state *init_state;
#ifdef CONFIG_PM
	struct pinctrl_state *sleep_state;
	struct pinctrl_state *idle_state;
#endif
};

1.2 pinctrl

假设芯片上有多个pin controller,那么这个设备使用哪个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链表中
      client端使用pinctrl过程的情景分析--基于imx6ull_第2张图片

1.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链表
      client端使用pinctrl过程的情景分析--基于imx6ull_第3张图片

二、client节点的pinctrl构造过程

2.1 函数调用

2.2 情景分析

really_probe	>>

int pinctrl_bind_pins(struct device *dev)
{
	int ret;

	dev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL);			//创建一个dev_pin_info结构体
	if (!dev->pins)
		return -ENOMEM;

	dev->pins->p = devm_pinctrl_get(dev);			//获取pinctrl结构体
	//...

	dev->pins->default_state = pinctrl_lookup_state(dev->pins->p,
					PINCTRL_STATE_DEFAULT);		//查找default状态
	//....

	dev->pins->init_state = pinctrl_lookup_state(dev->pins->p,
					PINCTRL_STATE_INIT);		//查找init状态
	if (IS_ERR(dev->pins->init_state)) {		//如果没有init状态
		//...
		ret = pinctrl_select_state(dev->pins->p,
					   dev->pins->default_state);		//就设置为default状态
	} else {
		ret = pinctrl_select_state(dev->pins->p, dev->pins->init_state);	//有的话就是init状态
	}

	//....

#ifdef CONFIG_PM
	//....
	dev->pins->sleep_state = pinctrl_lookup_state(dev->pins->p,
					PINCTRL_STATE_SLEEP);		//查找sleep状态
	//...

	dev->pins->idle_state = pinctrl_lookup_state(dev->pins->p,
					PINCTRL_STATE_IDLE);		//查找idle状态
	//...
#endif

	return 0;
	//...
}

struct pinctrl *devm_pinctrl_get(struct device *dev)
{
	struct pinctrl **ptr, *p;
	//.....
	p = pinctrl_get(dev);				//从dev中获取pinctrl
	//....

	return p;
}

struct pinctrl *pinctrl_get(struct device *dev)
{
	//.....
	p = find_pinctrl(dev);
	if (p != NULL) {			//第一次调用时必为空,就会去调用create_pinctrl()
		dev_dbg(dev, "obtain a copy of previously claimed pinctrl\n");
		kref_get(&p->users);
		return p;
	}

	return create_pinctrl(dev);
}

static struct pinctrl *create_pinctrl(struct device *dev)
{
	p = kzalloc(sizeof(*p), GFP_KERNEL);			//分配了一个pinctrl结构体
	//....

	ret = pinctrl_dt_to_map(p);				//处理设备树信息,把信息转换成一系列的map
	//....
	devname = dev_name(dev);
	//....
	for_each_maps(maps_node, i, map) {			//对于每一个map
		/* Map must be for this device */
		if (strcmp(map->dev_name, devname))
			continue;

		ret = add_setting(p, map);				//把map转换成setting

		//....
	}
	mutex_unlock(&pinctrl_maps_mutex);
	//....
	
	return p;
}

2.2.1 设备树转换为pinctrl_map

int pinctrl_dt_to_map(struct pinctrl *p)
{
	//.....

	/* We may store pointers to property names within the node */
	of_node_get(np);

	/* For each defined state ID */
	for (state = 0; ; state++) {				//为每一个state
		/* Retrieve the pinctrl-* property */
		propname = kasprintf(GFP_KERNEL, "pinctrl-%d", state);		//获得属性,比如设备树上是pinctrl-0 = <&node1, &node2>;
		prop = of_find_property(np, propname, &size);
		kfree(propname);
		if (!prop) {
			if (state == 0) {
				of_node_put(np);
				return -ENODEV;
			}
			break;
		}
		list = prop->value;
		size /= sizeof(*list);

		/* Determine whether pinctrl-names property names the state */
		ret = of_property_read_string_index(np, "pinctrl-names",
						    state, &statename);				//获得pinctrl的名字
		//.....
		/* For every referenced pin configuration node in it */
		for (config = 0; config < size; config++) {				//解析设备树,config0就是上面的node0,config1就是node1
			phandle = be32_to_cpup(list++);

			/* Look up the pin configuration node */
			np_config = of_find_node_by_phandle(phandle);		//使用phandle找到节点,np_config来自于phandle的节点
			//....

			/* Parse the node */
			ret = dt_to_map_one_config(p, statename, np_config);		//处理每一个节点(pintrol-0=<&nodexxxxxx>)
			of_node_put(np_config);
			if (ret < 0)
				goto err;
		}

		/* No entries in DT? Generate a dummy state table entry */
		if (!size) {
			ret = dt_remember_dummy_state(p, statename);
			if (ret < 0)
				goto err;
		}
	}

	return 0;

err:
	pinctrl_dt_free_maps(p);
	return ret;
}

int pinctrl_dt_to_map(struct pinctrl *p)
{
	//......

	/* For each defined state ID */
	for (state = 0; ; state++) {
			//......
			/* Parse the node */
			ret = dt_to_map_one_config(p, statename, np_config);			//处理单个节点
			of_node_put(np_config);
			//......
	}
	//.....
}

static int dt_to_map_one_config(struct pinctrl *p, const char *statename,
				struct device_node *np_config)
{
	//.......
	const struct pinctrl_ops *ops;
	//...
	ret = ops->dt_node_to_map(pctldev, np_config, &map, &num_maps);			//设备树节点转换成map
	if (ret < 0)
		return ret;
	//........
}

driver/pinctrl/freescale/pinctrl-imx.c中有对应的ops->dt_node_to_map函数:

static const struct pinctrl_ops imx_pctrl_ops = {
    .get_groups_count = imx_get_groups_count,
    .get_group_name = imx_get_group_name,
    .get_group_pins = imx_get_group_pins,
    .pin_dbg_show = imx_pin_dbg_show,
    .dt_node_to_map = imx_dt_node_to_map,			//对应的函数,把设备树节点转换成map
    .dt_free_map = imx_dt_free_map,
};

static int imx_dt_node_to_map(struct pinctrl_dev *pctldev,
			struct device_node *np,
			struct pinctrl_map **map, unsigned *num_maps)
{
	//.....

	/*
	 * first find the group of this node and check if we need create
	 * config maps for pins
	 */
	grp = imx_pinctrl_find_group_by_name(info, np->name);				//根据节点的名字找到imx_pin_group
	if (!grp) {
		dev_err(info->dev, "unable to find group for node %s\n",
			np->name);
		return -EINVAL;
	}

	if (info->flags & IMX8_USE_SCU) {
		map_num += grp->npins;
	} else {
		for (i = 0; i < grp->npins; i++) {
			if (!(grp->pins[i].pin_conf.pin_memmap.config &
			    IMX_NO_PAD_CTL))
				map_num++;			//统计map的数量
		}
	}

	new_map = kmalloc(sizeof(struct pinctrl_map) * map_num, GFP_KERNEL);		//分配同等数量的pinctrl_map
	if (!new_map)
		return -ENOMEM;

	*map = new_map;
	*num_maps = map_num;

	/* create mux map */
	parent = of_get_parent(np);
	if (!parent) {
		kfree(new_map);
		return -EINVAL;
	}
	//对于第0个结构体,把group对应的节点名字,复用成function(imx6ul-evk)的功能
	new_map[0].type = PIN_MAP_TYPE_MUX_GROUP;
	new_map[0].data.mux.function = parent->name;
	new_map[0].data.mux.group = np->name;
	of_node_put(parent);

	/* create config map 创建config map */
	new_map++;
	for (i = j = 0; i < grp->npins; i++) {
		if (info->flags & IMX8_USE_SCU) {
			new_map[j].type = PIN_MAP_TYPE_CONFIGS_PIN;
			new_map[j].data.configs.group_or_pin =
					pin_get_name(pctldev, grp->pins[i].pin);
			new_map[j].data.configs.configs =
				(unsigned long *)&grp->pins[i].pin_conf.pin_scu.mux;
			new_map[j].data.configs.num_configs = 2;
			j++;
		} else if (!(grp->pins[i].pin_conf.pin_memmap.config & IMX_NO_PAD_CTL)) {
		//分别设置为设备树中对应的引脚
			new_map[j].type = PIN_MAP_TYPE_CONFIGS_PIN;		//type是配置值
			new_map[j].data.configs.group_or_pin =
					pin_get_name(pctldev, grp->pins[i].pin);		//来自于imx_pin_memmap的config值
			new_map[j].data.configs.configs =
				&grp->pins[i].pin_conf.pin_memmap.config;
			new_map[j].data.configs.num_configs = 1;
			j++;
		}
	}

	dev_dbg(pctldev->dev, "maps: function %s group %s num %d\n",
		(*map)->data.mux.function, (*map)->data.mux.group, map_num);

	return 0;
}

2.2.2 pinctr_map转换为pinctrl_setting

static int add_setting(struct pinctrl *p, struct pinctrl_map const *map)
{
	//....
	switch (map->type) {				//根据type来执行操作
	case PIN_MAP_TYPE_MUX_GROUP:			//如果是复用
		ret = pinmux_map_to_setting(map, setting);
		break;
	case PIN_MAP_TYPE_CONFIGS_PIN:
	case PIN_MAP_TYPE_CONFIGS_GROUP:		//如果是配置
		ret = pinconf_map_to_setting(map, setting);
		break;
	default:
		ret = -EINVAL;
		break;
	}
	//.....
	return 0;
}

int pinmux_map_to_setting(struct pinctrl_map const *map,
			  struct pinctrl_setting *setting)
{
	//....

	ret = pinmux_func_name_to_selector(pctldev, map->data.mux.function);		//需要把之前的名字转换成index
	//....
	setting->data.mux.func = ret;					//设置func的值为成员的值,用来记录

	ret = pmxops->get_function_groups(pctldev, setting->data.mux.func,
					  &groups, &num_groups);			//这个功能下面有哪一些组的引脚,哪些group
	//....
	if (map->data.mux.group) {
		group = map->data.mux.group;
		ret = match_string(groups, num_groups, group);			//判断data.mux.group能够复用为该function
		if (ret < 0) {
			dev_err(pctldev->dev,
				"invalid group \"%s\" for function \"%s\"\n",
				group, map->data.mux.function);
			return ret;
		}
	} else {
		group = groups[0];
	}

	ret = pinctrl_get_group_selector(pctldev, group);			//如果可以的话,就设置
	//...
	setting->data.mux.group = ret;				//设置group的值为成员的值,用来记录

	return 0;
}

int pinconf_map_to_setting(struct pinctrl_map const *map,
			  struct pinctrl_setting *setting)
{
	struct pinctrl_dev *pctldev = setting->pctldev;
	int pin;

	switch (setting->type) {
	case PIN_MAP_TYPE_CONFIGS_PIN:
		pin = pin_get_from_name(pctldev,
					map->data.configs.group_or_pin);			//从名字获得引脚的索引值(整数)
		//....
		setting->data.configs.group_or_pin = pin;		//保存获得的信息
		break;
	case PIN_MAP_TYPE_CONFIGS_GROUP:
		pin = pinctrl_get_group_selector(pctldev,
					 map->data.configs.group_or_pin);		//从名字获得组的索引值(整数)
		//....
		setting->data.configs.group_or_pin = pin;		//保存获得的信息
		break;
	default:
		return -EINVAL;
	}
	//把map里的configs拷贝到setting里面来
	setting->data.configs.num_configs = map->data.configs.num_configs;
	setting->data.configs.configs = map->data.configs.configs;

	return 0;
}

三、切换state情景分析

3.1 函数调用过程

really_probe
	ret = pinctrl_bind_pins(dev);		//绑定引脚
		dev->pins->p = devm_pinctrl_get(dev);		//创建pinctrl结构体,得到一系列的setting
			p = pinctrl_get(dev);		//获取pinctrl结构体
				return create_pinctrl(dev);		//创建pinctrl结构体
		dev->pins->default_state = pinctrl_lookup_state(dev->pins->p,
                    PINCTRL_STATE_DEFAULT);			//找到default状态
		dev->pins->init_state = pinctrl_lookup_state(dev->pins->p,
                    PINCTRL_STATE_INIT);			//找到init状态
        if (IS_ERR(dev->pins->init_state)) {		//如果没有init状态
        	ret = pinctrl_select_state(dev->pins->p,
                       dev->pins->default_state);		//设置为default状态
                       ret = pinmux_enable_setting(setting);		//设置复用状态
                       		ret = ops->set_mux(pctldev, setting->data.mux.func,
					               setting->data.mux.group);		//最终会设置复用状态
                       ret = pinconf_apply_setting(setting);			//应用成某一种功能
                       		        ret = ops->pin_config_set(pctldev,
						                setting->data.configs.group_or_pin,
						                setting->data.configs.configs,
						                setting->data.configs.num_configs);		//配置成某些功能

        } else {
        	ret = pinctrl_select_state(dev->pins->p, dev->pins->init_state);		//有就设置init状态
        }
		dev->pins->sleep_state = pinctrl_lookup_state(dev->pins->p,
                    PINCTRL_STATE_SLEEP);		//找到sleep状态
		dev->pins->idle_state = pinctrl_lookup_state(dev->pins->p,
                    PINCTRL_STATE_IDLE);			//找到idle状态

3.2 情景分析

从pinctrl的创建开始,读取设备树的信息,来构建map和setting的配置
当pinctrl构造好后,把所有的state状态一一获取,并默认配置成init状态,如果没有init状态就设置成default状态
所有的状态都是setting中的一些配置值。

你可能感兴趣的:(#,Pinctrl,100ask,pin,controller,pinctrl,client,kernel)