linux内核pinctrl子系统分析

一、pinctrl子系统简介

在许多soc内部都包含有pin控制器,通过pin控制器的寄存器,我们可以配置一个或者一组引脚的功能和特性。各个厂商soc的pin脚在使用中,都有许多共同的特性,要么配置,要么复用pin脚。所以内核提供了一套代码来管理这些pin,这就是pinctrl子系统。主要实现的功能:

(1)管理系统中所有的可以控制的pin,在系统初始化的时候,枚举所有可以控制的pin,并标识这些pin。                                   (2)管理这些pin的复用(Multiplexing)。对于SOC而言,其引脚除了配置成普通的GPIO之外,若干个引脚还可以组成一个pin group,行程特定的功能。pin control subsystem要管理所有的pin group。                                                                                     (3)配置这些pin的特性。例如使能或关闭引脚上的pull-up、pull-down电阻,配置引脚的driver strength。                           

如图所示的pinctrl逻辑框图,实现pinctrl子系统的功能,core在初始化时,由处理器抽象pin描述,在pinctrl core中枚举所有的pin描述。当我们的驱动层driver使用pinctrl时,会进入pinctrl core会去分析这些pin的枚举,映射,设置,配置pin和pin group。

二、pinctrl 在dts文件中的描述

1、在使用pincrtl时,就涉及pinctrl在设备树中是如何描述的,在dts文件中,将一个处理器所使用的的pin用bank和group来描述

a、何为bank?

所谓pin bank,就是以引脚名为依据,就是一组GPIO控制器的描述,如s3c2440A这块芯片,阅读它的datasheet就可以知道有9组GPIO,如图所示:

所以我们在dts中就把这9组GPIO枚举为pin bank。这里分析一部分,其他的类似。 

gpa: gpa {                               gpf: gpf {  
        gpio-controller;                      gpio-controller;
        #gpio-cells = <2>;                  #gpio-cells = <2>;
    };                                                 interrupt-controller;                                                                                                                                                                             #interrupt-cells = <2>;                                                                                                                                                                      };                                            

如gpa: gpa 这个child node 就是描述GPA这个组,也就是gpa bank.。当然了 gpio-controller;表示这是一个GPIO控制器,有的GPIO控制器也可以是中断控制器,如gpf: gpf,之前的LED实验就用到了。#gpio-cells = <2>;表示使用这个bank的GPIO时,需要用两个32位数去描述。肯定是GPIO number和初始电平。不信可以阅读linux-4.19.8\Documentation\devicetree\bindings\pinctrl\samsung-pinctrl.txt

b、何为group?

从字面意思理解就是一个类似集群,将不同的pin number组合在一个。group以功能为依据,我们在JZ2440的LCD驱动中,需要使用gpc bank中的(1、2、3、4、8、9、10、11、12、13、14、15)gpd bank中的(0、1、2、3、4、5、6、7、8、9、10、11、12、13、14、15)来读写数据,我们把这些具体pin number组合在一起,我们称之为一个group。同时这些组合在一起的pin number又可能实现不同的功能,就用samsung,pin-function、samsung,pin-val等来描述区分。这样子samsung,pins和samsung,pin-function构成child node。来描述设备使用pinctrl时的具体功能。

lcd_pinctrl: lcd_pinctrl {
        samsung,pins = "gpc-8", "gpc-9", "gpc-10", "gpc-11", "gpc-12", "gpc-13", "gpc-14", "gpc-15",
                "gpd-0", "gpd-1", "gpd-2", "gpd-3", "gpd-4", "gpd-5", "gpd-6", "gpd-7",
                "gpd-8", "gpd-9", "gpd-10", "gpd-11", "gpd-12", "gpd-13", "gpd-14", "gpd-15",
                "gpc-1", "gpc-2", "gpc-3", "gpc-4";
        samsung,pin-function = <2>;
    };

samsung,pins:描述了LCD读写数据所使用的pin  number,

 samsung,pin-function:将这些GPIO初始值设置为2,具体是什么功能,有datasheet解释。这一个关于初始值的描述并不只是 samsung,pin-function,对于samsung来说,还有如下几种:具体阅读linux-4.19.8\Documentation\devicetree\bindings\pinctrl\samsung-pinctrl.txt

  - samsung,pin-val: Initial value of pin output buffer.
  - samsung,pin-pud: Pull up/down configuration.
  - samsung,pin-drv: Drive strength configuration.
  - samsung,pin-pud-pdn: Pull up/down configuration in power down mode.
  - samsung,pin-drv-pdn: Drive strength configuration in power down mode.

2、设备节点引用pinctrl

在lcd这个节点中dts文件是如下描述的,省略其他不需要的东西。

    fb0: fb@4d000000{
        compatible = "jz2440,lcd";
        ...
        pinctrl-names = "default";
        pinctrl-0 = <&lcd_pinctrl &lcd_backlight>;
        ...
}

state:pinctrl-names其实就是设置设备的某种初始状态,比如内核自己定义的"default","init","idel","sleep"状态;也可以是其他自己定义的状态, 比如串口的"flow_ctrl"状态(使用流量控制)。

pinctrl-0 就是正常引用pin  config,也就是调用lcd_pinctrl这个group配置LCD读写数据的GPIO。

三、pinctrl子系统涉及的文件

以JZ2440开发板的LCD的dts文件为例,介绍了pinctrl在dts中的几个重要组成部分。接下来真正开始分析pinctrl子系统。

1、pinctrl涉及的主要处理文件
Linux/drivers/pinctrl
core.c core.h                //pin control subsystem的core driver
pinctrl-utils.c             //pinctrl-utils.h    pin control subsystem的一些utility接口函数
pinmux.c pinmux.h            //pin control subsystem的core driver(pin muxing部分的代码,也称为pinmux driver)
pinconf.c pinconf.h            //pin control subsystem的core driver(pin config部分的代码,也称为pin config driver)
devicetree.c devicetree.h    //pin control subsystem的device tree代码
pinctrl-xxxx.c                //各种pin controller的low level driver。也就是pin number枚举,如pinctrl-samsung.c

2、pinctrl为其他驱动模块提供的统一接口
linux/include/linux/pinctrl
consumer.h    //其他的driver要使用pin control subsystem的下列接口: 
                       a、设置引脚复用功能 
                        b、配置引脚的电气特性 
devinfo.h    //这是for linux内核的驱动模型模块(driver model)使用的接口。struct device中包括了一个
                      struct dev_pin_info *pins的成员,这个成员描述了该设备的引脚的初始状态信息,在probe之前,driver model中的core driver在调用driver的probe函数之前会先设定pin state            
machine.h       //和machine模块的接口。

3、Low level pin controller driver接口
linux/include/linux/pinctrl
pinconf-generic.h     /这个接口主要是提供给各种pin controller driver使用的,不是外部接口。
pinconf.h                 //pin configuration 接口
pinctrl-state.h          //pin control state状态定义
pinmux.h                //pin mux function接口

四、soc初始化pinctrl

1、在第一节框图中就可以看见,遵循内核的一贯风格,注册。

每个SOC如果使用了pinctrl子系统,那么必然在系统初始化时需要向内核注册一个pinctrl。linux-4.19.8/drivers/pinctrl/core.c 中可以找到注册函数。

struct pinctrl_dev *devm_pinctrl_register(struct device *dev,
					  struct pinctrl_desc *pctldesc,//SOC对于自己pinctrl的描述
					  void *driver_data)//soc自己的私有数据
{
	struct pinctrl_dev **ptr, *pctldev;

	ptr = devres_alloc(devm_pinctrl_dev_release, sizeof(*ptr), GFP_KERNEL);
	if (!ptr)
		return ERR_PTR(-ENOMEM);

	pctldev = pinctrl_register(pctldesc, dev, driver_data);
	if (IS_ERR(pctldev)) {
		devres_free(ptr);
		return pctldev;
	}

	*ptr = pctldev;
	devres_add(dev, ptr);

	return pctldev;
}

分析 struct pinctrl_desc ,看看需要SOC提供些什么东西。

struct pinctrl_desc {
	const char *name;
	const struct pinctrl_pin_desc *pins;//pin number的描述列表
	unsigned int npins;//总共有多少pinctrl_pin_desc
	const struct pinctrl_ops *pctlops;//pinctrl全局操作函数
	const struct pinmux_ops *pmxops;//pin mux操作函数,也就是group的处理函数
	const struct pinconf_ops *confops;//pin config操作函数,也就是处理pin 上拉、下拉等等电气特性
	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
};

由结构体分析可以得出,SOC注册时需要枚举pin number,提供初始化这些pin 的函数。在linux-4.19.8/drivers/pinctrl/samsung/pinctrl-samsung.c中调用了devm_pinctrl_register,就来分析分析,底层是如何处理dts文件中这些pin的描述。

pinctrl-samsung.c也是作为一个platfom driver注册到系统中,通过of_match_table匹配注册,当然在dts文件中,肯定需要描述这个节点。一旦匹配成功进入probe函数。分析probe函数:

ctrl = samsung_pinctrl_get_soc_data(drvdata, pdev);//获取samsung_pin_ctrl数据,描述了一个 pin contrl

首先获取一个struct samsung_pin_ctrl数据。从函数名字就知道,获取的是具体的物理数据。通过samsung_pinctrl_get_soc_data_for_of_alias函数,获取得到pin control中有多少个bank,以及pinctro的l物理地址。

struct samsung_pin_ctrl {//描述一个pinctrl底层
	const struct samsung_pin_bank_data *pin_banks;//pinctrl中包含的pin bank列表
	unsigned int	nr_banks;//有多少个bank
	unsigned int	nr_ext_resources;//pin bank数据的物理基地址
	const struct samsung_retention_data *retention_data;//保留数据
//中断处理函数
	int		(*eint_gpio_init)(struct samsung_pinctrl_drv_data *);
	int		(*eint_wkup_init)(struct samsung_pinctrl_drv_data *);
	void		(*suspend)(struct samsung_pinctrl_drv_data *);
	void		(*resume)(struct samsung_pinctrl_drv_data *);
};
samsung_pinctrl_get_soc_data
    ctrl = samsung_pinctrl_get_soc_data_for_of_alias(pdev);//获取当前处理器的pin contrl描述 
        id = of_alias_get_id(node, "pinctrl");// 通过"pinctrl" 获取id
        of_data = of_device_get_match_data(&pdev->dev);  
        return &(of_data->ctrl[id]);
	d->pin_banks = devm_kcalloc(&pdev->dev, d->nr_banks,sizeof(*d->pin_banks), GFP_KERNEL);//为samsung_pinctrl_drv_data的pin bank分配内存

	for (i = 0; i < ctrl->nr_banks; ++i, ++bdata, ++bank) {//处理bank 数据
		bank->type = bdata->type;
		bank->pctl_offset = bdata->pctl_offset;
		bank->nr_pins = bdata->nr_pins;
		bank->eint_func = bdata->eint_func;
		bank->eint_type = bdata->eint_type;
		bank->eint_mask = bdata->eint_mask;
		bank->eint_offset = bdata->eint_offset;
		bank->name = bdata->name;

		spin_lock_init(&bank->slock);
		/*
			d->nr_pins初始值是0,循环完毕时,为总的pin数量
			bank->pin_base 每个bank的pin_base都是从上一个bank的bank->nr_pins开始
		*/
		bank->drvdata = d;
		bank->pin_base = d->nr_pins;
		d->nr_pins += bank->nr_pins;

		bank->eint_base = virt_base[0];
		bank->pctl_base = virt_base[bdata->pctl_res_idx];//pinctrl中bank的虚拟地址
	}
			
	for_each_child_of_node(node, np) {
		if (!of_find_property(np, "gpio-controller", NULL))//每个bank都是一个gpio controller
			continue;
		bank = d->pin_banks;
		for (i = 0; i < d->nr_banks; ++i, ++bank) {
			if (!strcmp(bank->name, np->name)) {
				bank->of_node = np;//初始化各个bank描述符中的device tree node成员
				break;
			}
		}
	}	
ret = samsung_pinctrl_register(pdev, drvdata);

2、SOC获取到了硬件信息,也就是pin bank以及pinctrl的地址信息,进入 samsung_pinctrl_register函数,进一步解析这些数据

	ctrldesc->pctlops = &samsung_pctrl_ops;
	ctrldesc->pmxops = &samsung_pinmux_ops;
	ctrldesc->confops = &samsung_pinconf_ops;

 SOC提供了三组操作函数,分别是:

a、pinctrl全局操作函数

struct pinctrl_ops {
	int (*get_groups_count) (struct pinctrl_dev *pctldev); //该pin controller支持多少个pin group
	const char *(*get_group_name) (struct pinctrl_dev *pctldev,//给定一个selector(index),获取指定的pin group的name
				       unsigned selector);
	int (*get_group_pins) (struct pinctrl_dev *pctldev,//给定一个selector(index)获取指定的pin group中pin的信息(该pin group//包括多少个pin,每个pin的ID是什么
			       unsigned selector,					//包括多少个pin,每个pin的ID是什么)
			       const unsigned **pins,			
			       unsigned *num_pins);
	void (*pin_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s,//debug fs的callback接口
			  unsigned offset);
	int (*dt_node_to_map) (struct pinctrl_dev *pctldev,//分析dts的一个child node中的pin  config 引用关系,并保存在pinctrl_map中
			       struct device_node *np_config,//entry 每一个entry表示一个setting(一个功能复用设定,或者电气特性设定)
			       struct pinctrl_map **map, unsigned *num_maps);
	void (*dt_free_map) (struct pinctrl_dev *pctldev, //dt_node_to_map的逆函数。
			     struct pinctrl_map *map, unsigned num_maps);
};

b、pinctrl复用pin操作函数

struct pinctrl_ops {
	int (*get_groups_count) (struct pinctrl_dev *pctldev); //该pin controller支持多少个pin group
	const char *(*get_group_name) (struct pinctrl_dev *pctldev,//给定一个selector(index),获取指定的pin group的name
				       unsigned selector);
	int (*get_group_pins) (struct pinctrl_dev *pctldev,//给定一个selector(index)获取指定的pin group中pin的信息(该pin group//包括多少个pin,每个pin的ID是什么
			       unsigned selector,					//包括多少个pin,每个pin的ID是什么)
			       const unsigned **pins,			
			       unsigned *num_pins);
	void (*pin_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s,//debug fs的callback接口
			  unsigned offset);
	int (*dt_node_to_map) (struct pinctrl_dev *pctldev,//分析dts的一个child node中的pin  config 引用关系,并保存在pinctrl_map中
			       struct device_node *np_config,//entry 每一个entry表示一个setting(一个功能复用设定,或者电气特性设定)
			       struct pinctrl_map **map, unsigned *num_maps);
	void (*dt_free_map) (struct pinctrl_dev *pctldev, //dt_node_to_map的逆函数。
			     struct pinctrl_map *map, unsigned num_maps);
};

c、pinctrl 配置pin操作函数

struct pinconf_ops {
#ifdef CONFIG_GENERIC_PINCONF
	bool is_generic;
#endif
	int (*pin_config_get) (struct pinctrl_dev *pctldev,//给定一个pin ID以及config type ID,获取该引脚上指定的type的配置。
			       unsigned pin,
			       unsigned long *config);
	int (*pin_config_set) (struct pinctrl_dev *pctldev,//设定一个指定pin的配置
			       unsigned pin,
			       unsigned long *configs,
			       unsigned num_configs);
	int (*pin_config_group_get) (struct pinctrl_dev *pctldev,//以pin group为单位,获取pin上的配置信息。
				     unsigned selector,
				     unsigned long *config);
	int (*pin_config_group_set) (struct pinctrl_dev *pctldev,//以pin group为单位,设定pin group 的特性配置。
				     unsigned selector,
				     unsigned long *configs,
				     unsigned num_configs);
	int (*pin_config_dbg_parse_modify) (struct pinctrl_dev *pctldev,//debug接口
					   const char *arg,
					   unsigned long *config);
	void (*pin_config_dbg_show) (struct pinctrl_dev *pctldev,//debug接口
				     struct seq_file *s,
				     unsigned offset);
	void (*pin_config_group_dbg_show) (struct pinctrl_dev *pctldev,//debug接口
					   struct seq_file *s,
					   unsigned selector);
	void (*pin_config_config_dbg_show) (struct pinctrl_dev *pctldev,//debug接口
					    struct seq_file *s,
					    unsigned long config);
};

 枚举pin,每一个pin 描述 都是pin-bank name + pin number,如dts文件中"gpc-8", "gpc-9", "gpc-10"等等

	//每一个pin 描述 都是pin-bank name + pin number
	/* for each pin, the name of the pin is pin-bank name + pin number */
	for (bank = 0; bank < drvdata->nr_banks; bank++) {//遍历bank
		pin_bank = &drvdata->pin_banks[bank];//取出bank
		for (pin = 0; pin < pin_bank->nr_pins; pin++) {//遍历每个bank的所有pin number
			sprintf(pin_names, "%s-%d", pin_bank->name, pin);
			pdesc = pindesc + pin_bank->pin_base + pin;//一个pin number的描述
			pdesc->name = pin_names;
			pin_names += PIN_NAME_LENGTH;
		}
	}

将这些信息保存在struct pinctrl_desc中,ctrldesc->pins = pindesc;//pin描述列表,ctrldesc->npins = drvdata->nr_pins;//pin 数量
 接下来分析 samsung_pinctrl_parse_dt(pdev, drvdata);

static int samsung_pinctrl_parse_dt(struct platform_device *pdev,
				    struct samsung_pinctrl_drv_data *drvdata)
{
	struct device *dev = &pdev->dev;
	struct samsung_pin_group *groups;
	struct samsung_pmx_func *functions;
	unsigned int grp_cnt = 0, func_cnt = 0;

	groups = samsung_pinctrl_create_groups(dev, drvdata, &grp_cnt);
	if (IS_ERR(groups)) {
		dev_err(dev, "failed to parse pin groups\n");
		return PTR_ERR(groups);
	}

	functions = samsung_pinctrl_create_functions(dev, drvdata, &func_cnt);
	if (IS_ERR(functions)) {
		dev_err(dev, "failed to parse pin functions\n");
		return PTR_ERR(functions);
	}

	drvdata->pin_groups = groups;//groups 保存了这个pin contrl 的所有pin  number
	drvdata->nr_groups = grp_cnt;//总共有多少个pin number
	drvdata->pmx_functions = functions;//准确的说是func 绑定的 group,形成的列表
	drvdata->nr_functions = func_cnt;//有多少个group

	return 0;
}

samsung_pinctrl_create_groups  //这里是整理所有ctrldesc->npins中的pin number,按照groups = {"gpc-8", "gpc-9", "gpc-10", ...}的格式,将所有pin number看成是一个group保存。也就是枚举了soc所有的pin number。

samsung_pinctrl_create_functions //通过统计device node中有多少个 配置,如:"samsung,pin-function"、"samsung,pin-val"来统计group,创建struct samsung_pmx_func 来描述这些group和func。也就是dts中设备节点中pin config。

这些group和func数据都是各个soc特有的数据,所以以私有数据传递进pinctrl core,自然也由soc自己来处理。后面会分析到。

如上分析:在struct pinctrl_desc中将pin 信息枚举在了const struct pinctrl_pin_desc *pins;也提供了soc自己的处理函数。那么就可以注册了。    drvdata->pctl_dev = devm_pinctrl_register(&pdev->dev, ctrldesc, drvdata);

pinctrl_add_gpio_range(drvdata->pctl_dev, &pin_bank->grange);这个是处理GPIO子系统和pinctrl子系统之间的联系。暂时不分析。

五、drvicer调用pinctrl子系统

soc在初始化时,已经读取dts文件中的pinctrl信息,初始化完毕,进行pinctrl注册。设备驱动时,就可以直接使用这个pinctrl。以LCD驱动为例:

LCD也是通过platform驱动模型进行注册。platform设备模型在匹配时,会调用__driver_attach

__driver_attach
    driver_probe_device(drv, dev)
        really_probe(dev, drv)
           ret = pinctrl_bind_pins(dev); //绑定设备使用pinctrl,获取一个pinctrl_dev handle
                dev->pins->p = devm_pinctrl_get(dev);
                    pinctrl_get(dev);
                         create_pinctrl
           pinctrl_init_done(dev);//配置设备使用的pin

每个platform匹配时,如果使用pinctrl,都需要向pinctrl core获取一个pinctrl handle,使用pin number时,直接使用pinctrl core提供的资源,当然,这些资源是soc初始化注册的。这就形成了一个闭环。接下来分析pinctrl core。

六、pinctrl core分析

首先介绍一下这个核心的结构体,将设备需要的pinctrl信息提取出来,保存在这个申请的handle中。具体如何如何保存信息?保存 了哪些信息,在设备注册接口分析。

struct pinctrl {
	struct list_head node;//链表头,设备的pinctrl handle都挂接在这里
	struct device *dev;//这个设备
	struct list_head states;//设备状态。如idle、sleep等等
	struct pinctrl_state *state;//设备的当前状态
	struct list_head dt_maps;//map table,设备pin 的配置
	struct kref users;
};

1、soc注册接口

分析pinctrl core如何处理soc传递的pinctrl_desc数据。

devm_pinctrl_register
    pinctrl_register(pctldesc, dev, driver_data);
        pinctrl_init_controller(pctldesc, dev, driver_data);
            pctldev = kzalloc(sizeof(*pctldev), GFP_KERNEL);//分配一个pctldev内存,这是pinctrl core核心数据结构
	        pctldev->owner = pctldesc->owner;//保存soc传递的desc数据
	        pctldev->desc = pctldesc;
	        pctldev->driver_data = driver_data;
            pinctrl_register_pins(pctldev, pctldesc->pins, pctldesc->npins);//将所有的pin number描述传递
                pinctrl_register_one_pin(pctldev, &pins[i]);//注册每一个pin nunmber
                    pindesc = kzalloc(sizeof(*pindesc), GFP_KERNEL);
                    pindesc->pctldev = pctldev;
                    pindesc->name = pin->name;
                    pindesc->drv_data = pin->drv_data;
                    radix_tree_insert(&pctldev->pin_desc_tree, pin->number, pindesc);//保存在pctldev->pin_desc_tree
      pinctrl_enable(pctldev);    
           list_add_tail(&pctldev->node, &pinctrldev_list); //将这个pctldev保存在链表pinctrldev_list

2、设备驱动调用接口

a、pinctrl core map table

mapping table这个database的建立也是动态的,当第一次调用pinctrl handle的get函数的时候,就会通过调用pinctrl_dt_to_map来建立该device需要的mapping entry。

ret = pinctrl_dt_to_map(p, pctldev);
/* For each defined state ID */
	for (state = 0; ; state++) {//pinctrl-0 pinctrl-1 pinctrl-2……表示了该设备的一个个的状态,这里每次循环分析一个pin state。
		/* Retrieve the pinctrl-* property */
		propname = kasprintf(GFP_KERNEL, "pinctrl-%d", state);//获取pinctrl-0 pinctrl-1 pinctrl-2……
		prop = of_find_property(np, propname, &size);//获取pinctrl-0 pinctrl-1 pinctrl-2……属性指针
		kfree(propname);
		if (!prop) {
			if (state == 0) {
				of_node_put(np);
				return -ENODEV;
			}
			break;
		}
		list = prop->value;//"pinctrl-%d" 的 属性值
		size /= sizeof(*list);//多少个32bit数据

		/* Determine whether pinctrl-names property names the state */
		ret = of_property_read_string_index(np, "pinctrl-names",//获取, "pinctrl-names" 属性的第state个字符串
						    state, &statename);//statename "default、idle、sleep"
		/*
		 * If not, statename is just the integer state ID. But rather
		 * than dynamically allocate it and have to free it later,
		 * just point part way into the property name for the string.
		 */
		if (ret < 0) {
			/* strlen("pinctrl-") == 8 */
			statename = prop->name + 8;//如果没有定义pinctrl-names属性,那么我们将pinctrl-0 pinctrl-1 pinctrl-2……中的那个ID取出来作为state name
		}

		/* For every referenced pin configuration node in it */
		for (config = 0; config < size; config++) {//pinctrl-x中含有一个或者多个配置,用phandle引用
			phandle = be32_to_cpup(list++);

			/* Look up the pin configuration node */
			np_config = of_find_node_by_phandle(phandle);//用phandle作为索引,在device tree中找他该phandle表示的那个pin configuration
			if (!np_config) {
				dev_err(p->dev,
					"prop %s index %i invalid phandle\n",
					prop->name, config);
				ret = -EINVAL;
				goto err;
			}

			/* Parse the node */
			ret = dt_to_map_one_config(p, pctldev, statename,//分析一个pin configuration
						   np_config);
			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);//如果该设备没有定义pin configuration,那么也要创建一个dummy的pin state。
			if (ret < 0)
				goto err;
		}
	}

再看子函数dt_to_map_one_config

dt_to_map_one_config
    np_pctldev = of_get_next_parent(np_pctldev);//首先找到该pin configuration node对应的parent node(也就是pin 	
		                //controler对应的node),如果找不到或者是root node,则进入出错处理
    pctldev = get_pinctrl_dev_from_of_node(np_pctldev);//struct pinctrl_dev
		if (pctldev)//一旦找到pin control class device则跳出for循环
			break;
	ops = pctldev->desc->pctlops;
	ret = ops->dt_node_to_map(pctldev, np_config, &map, &num_maps);//调用底层的callback函数处理pin configuration 
							 //node。这也是合理的,毕竟很多的pin controller bindings是需要自己解析的
	dt_remember_or_free_map(p, statename, pctldev, map, num_maps);//将该pin configuration node的mapping entry信息注册到系统中

以samsung为例,map函数就是soc初始化时提供的ctrldesc->pctlops = &samsung_pctrl_ops;全局函数,samsung_dt_node_to_map

samsung_dt_node_to_map
    samsung_dt_subnode_to_map
     add_map_mux(map, reserved_maps,num_maps, group, np->full_name); //设备引脚复用table
     add_map_configs(dev, map, reserved_maps,num_maps, group, configs,num_configs);//设备引脚配置table

分析到这里,pinctrl 就把这个child node的pinctrl的配置信息保存到了map table中。也就是struct list_head dt_maps;

b、add_setting

这个函数就是把map table中的配置信息,添加到pinctrl handle中。本质就是将pinctrl配置信息保存在struct pinctrl_setting的mux或者configs中。

add_setting
switch (map->type) {
	case PIN_MAP_TYPE_MUX_GROUP:
		ret = pinmux_map_to_setting(map, setting);//mux,引脚复用,也就是按照功能使用的一组引脚配置
		break;
	case PIN_MAP_TYPE_CONFIGS_PIN:
	case PIN_MAP_TYPE_CONFIGS_GROUP:
		ret = pinconf_map_to_setting(map, setting);//config,也就是引脚上拉、下拉等电气特性
		break;
list_add_tail(&setting->node, &state->settings); //保存在pinctrl handle中
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; //mux配置数据
		struct pinctrl_setting_configs configs;//config配置数据
	} data;
};

c、struct pinctrl

每个使用pinctrl的设备都需要申请一个pinctrl handle。设备节点存在多个state,如idle、sleep等等,前面提到的lcd就使用了一个状态,pinctrl-names = "default";每个状态下又存在多个设置,也就是GPIO配置。前文的lcd使用了两个配置,pinctrl-0 = <&lcd_pinctrl &lcd_backlight>;一个数据读写pinctrl配置,一个背光pinctrl配置。所以soc的设备在申请pinctrl hanle时就形成了一个链表结构。

create_pinctrl
    struct pinctrl *p; 
    p = kzalloc(sizeof(*p), GFP_KERNEL);  //分配struct pinctrl并初始化,在使用pinctrl_get()请求pin控件句柄时将获取到
    ret = pinctrl_dt_to_map(p, pctldev);//map
    add_setting(p, pctldev, map);//分析一个mapping entry,把这个setting的代码加入到handle中
    list_add_tail(&p->node, &pinctrl_list); // 把这个新增加的pinctrl handle加入到全局链表中 

3、pinctrl_init_done

pinctrl_init_done
    pinctrl_select_state(pins->p, pins->default_state);
        pinctrl_commit_state(p, 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);//mux set
			    break;
		    case PIN_MAP_TYPE_CONFIGS_PIN:
		    case PIN_MAP_TYPE_CONFIGS_GROUP:
			    ret = pinconf_apply_setting(setting);//config set
			    break;
		    default:
			    ret = -EINVAL;
			    break;
		    }

		    if (ret < 0) {
			    goto unapply_new_state;
		    }
	} 

七、GPIO子系统与pinctrl子系统的关联

待续。。。

八、总结

pincrtl子系统,就是在soc初始化时枚举所有的gpio。设备注册时,分析mux和configs。mux:设备以功能为依据,所用的一组GPIO;configs:一个GPIO。

  • 11
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值