zoukankan      html  css  js  c++  java
  • gpio子系统和pinctrl子系统(下)

    情景分析

    打算从两个角度来情景分析,先从bsp驱动工程师的角度,然后是驱动工程师的角度,下面以三星s3c6410 Pinctrl-samsung.c为例看看pinctrl输入参数的初始化过程(最开始的zynq平台的pin配置貌似是通过bitstreams来的,内核层没看到有关配置pin的代码,不过最新的zynq代码里加入了pinctrl,但我手上的恰好的较早其的zynq代码,所以这里以三星的代码为例子),不过这里贴的代码有点多(尽量将无关的代码删掉),耐心的看吧_

    bsp驱动工程师的角度

    static int samsung_pinctrl_probe(struct platform_device *pdev)
    {
    	...
        ...
        ...
        //解析pinctrl信息,后面分析
        ctrl = samsung_pinctrl_get_soc_data(drvdata, pdev);
    	drvdata->ctrl = ctrl;
    	drvdata->dev = dev;
    	
        ...
    	...
        ...
        //向gpio子系统注册(三星有用gpio子系统)
    	ret = samsung_gpiolib_register(pdev, drvdata);
    	if (ret)
    		return ret;
    
    	//向pinctrl子系统注册
    	ret = samsung_pinctrl_register(pdev, drvdata);
    	if (ret) {
    		samsung_gpiolib_unregister(pdev, drvdata);
    		return ret;
    	}
    
    	...
        ...
        ...
        
    	return 0;
    }
    

    先贴下6410 pinctrl设备树信息(arch/arm/boot/dts/s3c64xx.dtsi):

    aliases {                                                                   
        i2c0 = &i2c0;                                                           
        pinctrl0 = &pinctrl0;                                                   
    }; 
    
    pinctrl0: pinctrl@7f008000 {                                            
        compatible = "samsung,s3c64xx-pinctrl";                             
        reg = <0x7f008000 0x1000>;                                          
        interrupt-parent = <&vic1>;                                         
        interrupts = <21>;                                                  
    
        pctrl_int_map: pinctrl-interrupt-map {                              
            interrupt-map = <0 &vic0 0>,                                    
                    <1 &vic0 1>,                                            
                    <2 &vic1 0>,                                            
                    <3 &vic1 1>;                                            
            #address-cells = <0>;                                           
            #size-cells = <0>;                                              
            #interrupt-cells = <1>;                                         
        };                                                                  
    
        wakeup-interrupt-controller {                                       
            compatible = "samsung,s3c64xx-wakeup-eint";                     
            interrupts = <0>, <1>, <2>, <3>;                                
            interrupt-parent = <&pctrl_int_map>;                            
        };                                                                  
    };  
    

    下面边看代码边对照上面的设备树描述,看看解析过程:

    static struct samsung_pin_ctrl *samsung_pinctrl_get_soc_data(
    				struct samsung_pinctrl_drv_data *d,
    				struct platform_device *pdev)
    {
    	int id;
    	const struct of_device_id *match;
    	struct device_node *node = pdev->dev.of_node;
    	struct device_node *np;
    	struct samsung_pin_ctrl *ctrl;
    	struct samsung_pin_bank *bank;
    	int i;
    
    	//获取pinctrl的alias id,其实就是上面的pinctrl0了
    	id = of_alias_get_id(node, "pinctrl");
    	if (id < 0) {
    		dev_err(&pdev->dev, "failed to get alias id
    ");
    		return NULL;
    	}
        //获取该节点对应的match
    	match = of_match_node(samsung_pinctrl_dt_match, node);
        //通过id找到对应的pinctrl,因为三星的有些soc是存在多个pinctrl的,
        //也就是说pinctrl0,pinctrl1等等同时存在,这里就是获取第id个,对于6410,就一个
        //struct samsung_pin_ctrl s3c64xx_pin_ctrl[] = {
        //    {
        //        /* pin-controller instance 1 data */
        //        .pin_banks	= s3c64xx_pin_banks0,
        //        .nr_banks	= ARRAY_SIZE(s3c64xx_pin_banks0),
        //        .eint_gpio_init = s3c64xx_eint_gpio_init,
        //        .eint_wkup_init = s3c64xx_eint_eint0_init,
        //        .label		= "S3C64xx-GPIO",
        //    },
        //};
    	对于exynos5420,就存在多个啦:
        //struct samsung_pin_ctrl exynos5420_pin_ctrl[] = {
        //    {
        //        /* pin-controller instance 0 data */
        //        .pin_banks	= exynos5420_pin_banks0,
        //        .nr_banks	= ARRAY_SIZE(exynos5420_pin_banks0),
        //        .geint_con	= EXYNOS_GPIO_ECON_OFFSET,
        //        .geint_mask	= EXYNOS_GPIO_EMASK_OFFSET,
        //        .geint_pend	= EXYNOS_GPIO_EPEND_OFFSET,
        //        .weint_con	= EXYNOS_WKUP_ECON_OFFSET,
        //        .weint_mask	= EXYNOS_WKUP_EMASK_OFFSET,
        //        .weint_pend	= EXYNOS_WKUP_EPEND_OFFSET,
        //        .svc		= EXYNOS_SVC_OFFSET,
        //        .eint_gpio_init = exynos_eint_gpio_init,
        //        .eint_wkup_init = exynos_eint_wkup_init,
        //        .label		= "exynos5420-gpio-ctrl0",
        //    }, {
        //        /* pin-controller instance 1 data */
        //        .pin_banks	= exynos5420_pin_banks1,
        //        .nr_banks	= ARRAY_SIZE(exynos5420_pin_banks1),
        //        .geint_con	= EXYNOS_GPIO_ECON_OFFSET,
        //       .geint_mask	= EXYNOS_GPIO_EMASK_OFFSET,
        //        .geint_pend	= EXYNOS_GPIO_EPEND_OFFSET,
        //        .svc		= EXYNOS_SVC_OFFSET,
        //        .eint_gpio_init = exynos_eint_gpio_init,
        //        .label		= "exynos5420-gpio-ctrl1",
        //    },
        //    ...
        //    ...
        //    ...
        //};
    	ctrl = (struct samsung_pin_ctrl *)match->data + id;
    	//提取pin ctrl里的banks信息,这里就是ARRAY_SIZE(s3c64xx_pin_banks0)
    	bank = ctrl->pin_banks;
        //遍历每一个bank,填充相应的信息
    	for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
    		spin_lock_init(&bank->slock);
    		bank->drvdata = d;
            //设置bank的pin base
    		bank->pin_base = ctrl->nr_pins;
            //更新ctrl->nr_pins,即该pin ctrl的pin数量,在后面的注册时会用到该成员
    		ctrl->nr_pins += bank->nr_pins;
    	}
    
    	//遍历该节点的每一个子节点,上面的s3c64xx.dtsi文件末尾有一个
        //#include "s3c64xx-pinctrl.dtsi" 语句,s3c64xx-pinctrl.dtsi里
        //的信息是对当前节点pinctrl0的补充,内容如下:
        //&pinctrl0 {                                                                     
        ///*                                                                          
        // * Pin banks                                                                
        // */                                                                         
    	//
        //gpa: gpa {                                                                  
        //    gpio-controller;                                                        
        //    #gpio-cells = <2>;                                                      
        //    interrupt-controller;                                                   
        //    #interrupt-cells = <2>;                                                 
        //};                                                                          
    	//
        //gpb: gpb {                                                                  
        //    gpio-controller;                                                        
        //    #gpio-cells = <2>;                                                      
        //    interrupt-controller;                                                   
        //    #interrupt-cells = <2>;                                                 
        //};                                                                          
    
        //gpc: gpc {                                                                  
        //    gpio-controller;                                                        
        //    #gpio-cells = <2>;                                                      
        //    interrupt-controller;                                                   
        //    #interrupt-cells = <2>;                                                 
        //};          
        //...
        //...
        //...
        //hsi_bus: hsi-bus {                                                          
        //    samsung,pins = "gpk-0", "gpk-1", "gpk-2", "gpk-3",                      
        //            "gpk-4", "gpk-5", "gpk-6", "gpk-7";                             
        //    samsung,pin-function = <3>;                                             
        //    samsung,pin-pud = <PIN_PULL_NONE>;                                      
        //};     
        //}
        //这里就是处理这些子节点
    	for_each_child_of_node(node, np) {
        	//如果该子节点没有gpio-controller属性,跳过处理,这里处理的是bank
            //只和gpio有关,所以跳过不关心的
    		if (!of_find_property(np, "gpio-controller", NULL))
    			continue;
    		bank = ctrl->pin_banks;
    		for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
    			if (!strcmp(bank->name, np->name)) {
                	//将bank对应到它自己的设备节点
    				bank->of_node = np;
    				break;
    			}
    		}
    	}
    
    	ctrl->base = pin_base;
    	pin_base += ctrl->nr_pins;
    
    	return ctrl;
    }
    

    填充完必要的信息,就开始注册了,先看pinctrl的注册吧!注意,传入的参数drvdata是已经经过前面的解析填入了很多信息的

    static int samsung_pinctrl_register(struct platform_device *pdev,
    				    struct samsung_pinctrl_drv_data *drvdata)
    {
    	struct pinctrl_desc *ctrldesc = &drvdata->pctl;
    	struct pinctrl_pin_desc *pindesc, *pdesc;
    	struct samsung_pin_bank *pin_bank;
    	char *pin_names;
    	int pin, bank, ret;
    
    	//初始化pinctrl_desc,register的时候要用
    	ctrldesc->name = "samsung-pinctrl";
    	ctrldesc->owner = THIS_MODULE;
        //这个ops是必须要的,里面的几个函数前面也都用到了,主要有
        //get_groups_count、dt_node_to_map、get_group_pins
    	ctrldesc->pctlops = &samsung_pctrl_ops;
        //这个是pinctrl chip driver根据自己平台的特性,可选的支持的
        //主要有request、get_functions_count、get_function_groups、
        //enable,和gpio相关的还有额外几个gpio_request_enable、gpio_disable_free、gpio_set_direction
    	ctrldesc->pmxops = &samsung_pinmux_ops;
        //这个是pinctrl chip driver根据自己平台的特性,可选的支持的
        //主要有pin_config_get、pin_config_set、pin_config_group_get、pin_config_group_set
    	ctrldesc->confops = &samsung_pinconf_ops;
    
    	//下面这部分也是pinctrl chip driver根据自己平台的特性必须填充的,用于表示该pinctrl chip
        //所有的pin信息
    	pindesc = devm_kzalloc(&pdev->dev, sizeof(*pindesc) *
    			drvdata->ctrl->nr_pins, GFP_KERNEL);
    	if (!pindesc) {
    		dev_err(&pdev->dev, "mem alloc for pin descriptors failed
    ");
    		return -ENOMEM;
    	}
    	ctrldesc->pins = pindesc;
    	ctrldesc->npins = drvdata->ctrl->nr_pins;//该成员就是samsung_pin_ctrl填充的
    
    	//填充pin号
    	/* dynamically populate the pin number and pin name for pindesc */
    	for (pin = 0, pdesc = pindesc; pin < ctrldesc->npins; pin++, pdesc++)
    		pdesc->number = pin + drvdata->ctrl->base;//该成员也是由samsung_pin_ctrl填充的
    
    	//分配空间,用于填充pin名字
    	/*
    	 * allocate space for storing the dynamically generated names for all
    	 * the pins which belong to this pin-controller.
    	 */
    	pin_names = devm_kzalloc(&pdev->dev, sizeof(char) * PIN_NAME_LENGTH *
    					drvdata->ctrl->nr_pins, GFP_KERNEL);
    	if (!pin_names) {
    		dev_err(&pdev->dev, "mem alloc for pin names failed
    ");
    		return -ENOMEM;
    	}
    
    	/* for each pin, the name of the pin is pin-bank name + pin number */
    	for (bank = 0; bank < drvdata->ctrl->nr_banks; bank++) {
    		pin_bank = &drvdata->ctrl->pin_banks[bank];
    		for (pin = 0; pin < pin_bank->nr_pins; pin++) {
            	//填充pin的名字,注意这里的格式,设备树里的命名就得按照该格式,即bank名字+pin号
    			sprintf(pin_names, "%s-%d", pin_bank->name, pin);
    			pdesc = pindesc + pin_bank->pin_base + pin;
    			pdesc->name = pin_names;
    			pin_names += PIN_NAME_LENGTH;
    		}
    	}
    	
        //到现在,离注册需要的条件就剩function和group的填充了,其实它们不是pinctrl子系统要求的,
        //但是回调函数的实现依赖这些,因此需要解析设备树信息来填充它们,后面会详细分析该函数
    	ret = samsung_pinctrl_parse_dt(pdev, drvdata);
    	if (ret)
    		return ret;
    	
        //一切准备好后,就注册了
    	drvdata->pctl_dev = pinctrl_register(ctrldesc, &pdev->dev, drvdata);
    	if (!drvdata->pctl_dev) {
    		dev_err(&pdev->dev, "could not register pinctrl driver
    ");
    		return -EINVAL;
    	}
    
    	//
    	for (bank = 0; bank < drvdata->ctrl->nr_banks; ++bank) {
    		pin_bank = &drvdata->ctrl->pin_banks[bank];
    		pin_bank->grange.name = pin_bank->name;
    		pin_bank->grange.id = bank;
    		pin_bank->grange.pin_base = pin_bank->pin_base;
    		pin_bank->grange.base = pin_bank->gpio_chip.base;
    		pin_bank->grange.npins = pin_bank->gpio_chip.ngpio;
    		pin_bank->grange.gc = &pin_bank->gpio_chip;
    		pinctrl_add_gpio_range(drvdata->pctl_dev, &pin_bank->grange);
    	}
    
    	return 0;
    }
    

    samsung_pinctrl_parse_dt分析:

    static int samsung_pinctrl_parse_dt(struct platform_device *pdev,
    				    struct samsung_pinctrl_drv_data *drvdata)
    {
    	...
        //获取pinctrl设备的子节点数量,前面已经讲过有哪些子节点了,不再重复
    	grp_cnt = of_get_child_count(dev_np);
    	if (!grp_cnt)
    		return -EINVAL;
    	
        //根据获取的数量,分配空间,每个配置节点对应于一个group(pin的集合)
    	groups = devm_kzalloc(dev, grp_cnt * sizeof(*groups), GFP_KERNEL);
    	if (!groups) {
    		dev_err(dev, "failed allocate memory for ping group list
    ");
    		return -EINVAL;
    	}
    	grp = groups;
    
    	//根据获取的数量,分配空间,每个配置节点对应的功能
    	functions = devm_kzalloc(dev, grp_cnt * sizeof(*functions), GFP_KERNEL);
    	if (!functions) {
    		dev_err(dev, "failed to allocate memory for function list
    ");
    		return -EINVAL;
    	}
    	func = functions;
    
    	//遍历每一个子节点,一个个处理
    	/*
    	 * Iterate over all the child nodes of the pin controller node
    	 * and create pin groups and pin function lists.
    	 */
    	for_each_child_of_node(dev_np, cfg_np) {
    		u32 function;
            //检查samsung,pins属性
    		if (!of_find_property(cfg_np, "samsung,pins", NULL))
    			continue;
    
    		//将samsung,pins属性里面指定的名字列表转换为pin号列表
            //,这里面会用到前面samsung_pinctrl_get_soc_data填充的信息来匹配
    		ret = samsung_pinctrl_parse_dt_pins(pdev, cfg_np,
    					&drvdata->pctl,	&pin_list, &npins);
    		if (ret)
    			return ret;
    
    		//下面就是构成一个pin group了,注意pin组的名字
            //,是配置节点名+GROUP_SUFFIX,GROUP_SUFFIX为-grp
    		/* derive pin group name from the node name */
    		gname = devm_kzalloc(dev, strlen(cfg_np->name) + GSUFFIX_LEN,
    					GFP_KERNEL);
    		if (!gname) {
    			dev_err(dev, "failed to alloc memory for group name
    ");
    			return -ENOMEM;
    		}
    		sprintf(gname, "%s%s", cfg_np->name, GROUP_SUFFIX);
    
    		grp->name = gname;
    		grp->pins = pin_list;
    		grp->num_pins = npins;
    		of_property_read_u32(cfg_np, "samsung,pin-function", &function);
    		grp->func = function;
    		grp++;
    
    		if (!of_find_property(cfg_np, "samsung,pin-function", NULL))
    			continue;
    
    		//如果存在samsung,pin-function属性,那么构建一个功能名
            //,功能名组合方式是配置节点名+FUNCTION_SUFFIX,FUNCTION_SUFFIX为-mux
    		/* derive function name from the node name */
    		fname = devm_kzalloc(dev, strlen(cfg_np->name) + FSUFFIX_LEN,
    					GFP_KERNEL);
    		if (!fname) {
    			dev_err(dev, "failed to alloc memory for func name
    ");
    			return -ENOMEM;
    		}
    		sprintf(fname, "%s%s", cfg_np->name, FUNCTION_SUFFIX);
    
    		func->name = fname;
    		func->groups = devm_kzalloc(dev, sizeof(char *), GFP_KERNEL);
    		if (!func->groups) {
    			dev_err(dev, "failed to alloc memory for group list "
    					"in pin function");
    			return -ENOMEM;
    		}
    		func->groups[0] = gname;
    		func->num_groups = 1;
    		func++;
    		func_idx++;
    	}
    	
        //存储下解析的数据信息
    	drvdata->pin_groups = groups;
    	drvdata->nr_groups = grp_cnt;
    	drvdata->pmx_functions = functions;
    	drvdata->nr_functions = func_idx;
    
    	return 0;
    }
    

    下面通过分析各个ops,来进一步理解下上面几个函数所起的作用:

    static const struct pinctrl_ops samsung_pctrl_ops = {
    	.get_groups_count	= samsung_get_group_count,
    	.get_group_name		= samsung_get_group_name,
    	.get_group_pins		= samsung_get_group_pins,
    	.dt_node_to_map		= samsung_dt_node_to_map,
    	.dt_free_map		= samsung_dt_free_map,
    };
    static const struct pinmux_ops samsung_pinmux_ops = {
    	.get_functions_count	= samsung_get_functions_count,
    	.get_function_name	= samsung_pinmux_get_fname,
    	.get_function_groups	= samsung_pinmux_get_groups,
    	.enable			= samsung_pinmux_enable,
    	.disable		= samsung_pinmux_disable,
        //由pinmux_gpio_direction间接调用,最开始应该是gpio子系统
        //的gpio_pin_direction_input、gpio_pin_direction_output触发
    	.gpio_set_direction	= samsung_pinmux_gpio_set_direction,
    };
    static const struct pinconf_ops samsung_pinconf_ops = {
    	.pin_config_get		= samsung_pinconf_get,
    	.pin_config_set		= samsung_pinconf_set,
    	.pin_config_group_get	= samsung_pinconf_group_get,
    	.pin_config_group_set	= samsung_pinconf_group_set,
    };
    

    从上面一路分析下路来,我们应该知道dt_node_to_map是最先调用的,其次是get_functions_countget_function_nameget_function_groupsget_groups_countget_group_nameget_group_pinsrequest(三星pinmux_ops没有实现它)、enablepin_config_setpin_config_group_set所以我打算就按这个顺序进行分析。

    调用dt_node_to_map的时候,从前文应该很清楚了吧,就是在某一个设备(pinctrl本身也算是一个设备,不过从前文贴出来的pinctrl0里,我没发现有pinctrl-xxx的属性,也就是说不需要对它做任何pin ctrl)用pinctrl_get请求解析自己设备树信息的时候,说的更准确点的话,就是解析该设备里某一个状态的某一个配置(一个状态可能需要多个配置来完成)的时候。下面用某一个子设备的设备树信息为例子,对应文件s3c6410-smdk6410.dts

    #define PIN_PULL_NONE   0  
    
    &uart0 {                                                                        
        pinctrl-names = "default";                                                  
        pinctrl-0 = <&uart0_data>, <&uart0_fctl>;                                   
        status = "okay";                                                            
    };
    uart0_data: uart0-data {                                                    
        samsung,pins = "gpa-0", "gpa-1";                                        
        samsung,pin-function = <2>;                                             
        samsung,pin-pud = <PIN_PULL_NONE>;                                      
    };       
    uart0_fctl: uart0-fctl {                                                    
        samsung,pins = "gpa-2", "gpa-3";                                        
        samsung,pin-function = <2>;                                             
        samsung,pin-pud = <PIN_PULL_NONE>;                                      
    };
    
    //下面部分是uart0的其他信息,和本文关心的pinctrl无关,之所以也列出来,只是不想让读者对这部分有误解
    uart0: serial@7f005000 {                                                
        compatible = "samsung,s3c6400-uart";                                
        reg = <0x7f005000 0x100>;                                           
        interrupt-parent = <&vic1>;                                         
        interrupts = <5>;                                                   
        clock-names = "uart", "clk_uart_baud2",                             
                "clk_uart_baud3";                                           
        clocks = <&clocks PCLK_UART0>, <&clocks PCLK_UART0>,                
                <&clocks SCLK_UART>;                                        
        status = "disabled";                                                
    };  
    

    对应的解析代码如下,从前文描述应该清楚,期望回调函数返回该设备该状态该配置下的所有设置信息(可能只存在mux设置,也可能同时存在mux和conf设置),而上面的设备树里的uart0只有一个状态,default,对应的配置有两个,一个是uart0_data,一个是uart0_fctl,它们都是对配置节点的引用,配置节点都是pinctrl节点下的子节点,下面看代码吧:

    static int samsung_dt_node_to_map(struct pinctrl_dev *pctldev,
    			struct device_node *np, struct pinctrl_map **maps,
    			unsigned *nmaps)
    {
    ...
    	//检查该节点(第一次调用应该是uart0_data节点,第二次调用应该是uart0_fctl节点啦)
        //含有多少个自己定义的属性,包括:
    	//{ "samsung,pin-pud", PINCFG_TYPE_PUD },
    	//{ "samsung,pin-drv", PINCFG_TYPE_DRV },
    	//{ "samsung,pin-con-pdn", PINCFG_TYPE_CON_PDN },
    	//{ "samsung,pin-pud-pdn", PINCFG_TYPE_PUD_PDN },    
    	/* count the number of config options specfied in the node */
    	for (idx = 0; idx < ARRAY_SIZE(pcfgs); idx++) {
    		if (of_find_property(np, pcfgs[idx].prop_cfg, NULL))
    			cfg_cnt++;
    	}
    
    	/*
    	 * Find out the number of map entries to create. All the config options
    	 * can be accomadated into a single config map entry.
    	 */
        //如果有,那么说明需要继续后面的conf操作
    	if (cfg_cnt)
    		map_cnt = 1;
        //如果存在samsung,pin-function属性,那么不仅要做后面的操作,还需要额外做一些mux操作
    	if (of_find_property(np, "samsung,pin-function", NULL))
    		map_cnt++;
    	if (!map_cnt) {
    		dev_err(dev, "node %s does not have either config or function "
    				"configurations
    ", np->name);
    		return -EINVAL;
    	}
    
    	//分配空间
    	/* Allocate memory for pin-map entries */
    	map = kzalloc(sizeof(*map) * map_cnt, GFP_KERNEL);
    	if (!map) {
    		dev_err(dev, "could not alloc memory for pin-maps
    ");
    		return -ENOMEM;
    	}
    	*nmaps = 0;
    
    	//从前面的分析应该清楚了组名的格式,下面就是根据配置节点名构建一个格式,然后到系统
        //里找对应的信息
    	/*
    	 * Allocate memory for pin group name. The pin group name is derived
    	 * from the node name from which these map entries are be created.
    	 */
    	gname = kzalloc(strlen(np->name) + GSUFFIX_LEN, GFP_KERNEL);
    	if (!gname) {
    		dev_err(dev, "failed to alloc memory for group name
    ");
    		goto free_map;
    	}
    	sprintf(gname, "%s%s", np->name, GROUP_SUFFIX);
    
    	/*
    	 * don't have config options? then skip over to creating function
    	 * map entries.
    	 */
    	if (!cfg_cnt)
    		goto skip_cfgs;
    
    	//根据前面获取的数量来分配配置节点空间
    	/* Allocate memory for config entries */
    	cfg = kzalloc(sizeof(*cfg) * cfg_cnt, GFP_KERNEL);
    	if (!cfg) {
    		dev_err(dev, "failed to alloc memory for configs
    ");
    		goto free_gname;
    	}
    
    	//将已经定义的,属于自己定义列表里面的属性值提取出来,对应于我们这里,都是PIN_PULL_NONE
    	/* Prepare a list of config settings */
    	for (idx = 0, cfg_cnt = 0; idx < ARRAY_SIZE(pcfgs); idx++) {
    		u32 value;
    		if (!of_property_read_u32(np, pcfgs[idx].prop_cfg, &value))
    			cfg[cfg_cnt++] =
    				PINCFG_PACK(pcfgs[idx].cfg_type, value);
    	}
    
    	//创建设置信息,如设置名字,类型,以及多少个conf操作,每一个conf值
    	/* create the config map entry */
    	map[*nmaps].data.configs.group_or_pin = gname;
    	map[*nmaps].data.configs.configs = cfg;
    	map[*nmaps].data.configs.num_configs = cfg_cnt;
    	map[*nmaps].type = PIN_MAP_TYPE_CONFIGS_GROUP;
    	*nmaps += 1;
    
    skip_cfgs:
    	/* create the function map entry */
    	if (of_find_property(np, "samsung,pin-function", NULL)) {
        	//如果存在samsung,pin-function属性,说明有mux的需求,处理它
            //这里是构建功能名,和前面初始化的时候一致
    		fname = kzalloc(strlen(np->name) + FSUFFIX_LEN,	GFP_KERNEL);
    		if (!fname) {
    			dev_err(dev, "failed to alloc memory for func name
    ");
    			goto free_cfg;
    		}
    		sprintf(fname, "%s%s", np->name, FUNCTION_SUFFIX);
    
    		//填充mux操作需要的信息,如哪一个设备,哪一个功能
    		map[*nmaps].data.mux.group = gname;
    		map[*nmaps].data.mux.function = fname;
    		map[*nmaps].type = PIN_MAP_TYPE_MUX_GROUP;
    		*nmaps += 1;
    	}
    
    	*maps = map;
    	return 0;
    ...
    }
    

    samsung_get_functions_count,它用于获取功能的总数量drvdata->nr_functions,前面已经分析过初始化这个的过程,所以这里就不再分析。samsung_pinmux_get_fname从已经初始化的数据结构里拿出对应索引上的name,name就是由配置节点名+-mux后缀构成。pinctrl_get的过程(pinmux_map_to_setting),会以map->data.mux.function为参数调用samsung_pinmux_get_fname获取该功能对应的索引来初始化setting->data.mux.func,然后在用samsung_pinmux_get_groups获取的组信息里,用前面解析出来的map[*nmaps].data.mux.group作为输入参数,获取该组的索引来初始化setting->data.mux.group。最后在pinctrl_select_state的时候,会通过上面的信息并结合最开始初始化的一些数据结构进行mux和conf操作。pinconf_map_to_setting的操作类似,不再重复。在pinctrl_select_state的时候samsung_pinmux_enablesamsung_pinconf_set有可能会触发,这里就不再继续分析了,但还是贴出代码吧!

    /* enable a specified pinmux by writing to registers */
    static int samsung_pinmux_enable(struct pinctrl_dev *pctldev, unsigned selector,
    					unsigned group)
    {
    	samsung_pinmux_setup(pctldev, selector, group, true);
    	return 0;
    }
    
    static void samsung_pinmux_setup(struct pinctrl_dev *pctldev, unsigned selector,
    					unsigned group, bool enable)
    {
    	struct samsung_pinctrl_drv_data *drvdata;
    	const unsigned int *pins;
    	struct samsung_pin_bank *bank;
    	void __iomem *reg;
    	u32 mask, shift, data, pin_offset, cnt;
    	unsigned long flags;
    
    	drvdata = pinctrl_dev_get_drvdata(pctldev);
    	pins = drvdata->pin_groups[group].pins;
    
    	/*
    	 * for each pin in the pin group selected, program the correspoding pin
    	 * pin function number in the config register.
    	 */
    	for (cnt = 0; cnt < drvdata->pin_groups[group].num_pins; cnt++) {
    		struct samsung_pin_bank_type *type;
    
    		pin_to_reg_bank(drvdata, pins[cnt] - drvdata->ctrl->base,
    				&reg, &pin_offset, &bank);
    		type = bank->type;
    		mask = (1 << type->fld_width[PINCFG_TYPE_FUNC]) - 1;
    		shift = pin_offset * type->fld_width[PINCFG_TYPE_FUNC];
    		if (shift >= 32) {
    			/* Some banks have two config registers */
    			shift -= 32;
    			reg += 4;
    		}
    
    		spin_lock_irqsave(&bank->slock, flags);
    
    		data = readl(reg + type->reg_offset[PINCFG_TYPE_FUNC]);
    		data &= ~(mask << shift);
    		if (enable)
    			data |= drvdata->pin_groups[group].func << shift;
    		writel(data, reg + type->reg_offset[PINCFG_TYPE_FUNC]);
    
    		spin_unlock_irqrestore(&bank->slock, flags);
    	}
    }
    
    /* set the pin config settings for a specified pin */
    static int samsung_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
    				unsigned long *configs, unsigned num_configs)
    {
    	int i, ret;
    
    	for (i = 0; i < num_configs; i++) {
    		ret = samsung_pinconf_rw(pctldev, pin, &configs[i], true);
    		if (ret < 0)
    			return ret;
    	} /* for each config */
    
    	return 0;
    }
    
    /* set or get the pin config settings for a specified pin */
    static int samsung_pinconf_rw(struct pinctrl_dev *pctldev, unsigned int pin,
    				unsigned long *config, bool set)
    {
    	struct samsung_pinctrl_drv_data *drvdata;
    	struct samsung_pin_bank_type *type;
    	struct samsung_pin_bank *bank;
    	void __iomem *reg_base;
    	enum pincfg_type cfg_type = PINCFG_UNPACK_TYPE(*config);
    	u32 data, width, pin_offset, mask, shift;
    	u32 cfg_value, cfg_reg;
    	unsigned long flags;
    
    	drvdata = pinctrl_dev_get_drvdata(pctldev);
    	pin_to_reg_bank(drvdata, pin - drvdata->ctrl->base, &reg_base,
    					&pin_offset, &bank);
    	type = bank->type;
    
    	if (cfg_type >= PINCFG_TYPE_NUM || !type->fld_width[cfg_type])
    		return -EINVAL;
    
    	width = type->fld_width[cfg_type];
    	cfg_reg = type->reg_offset[cfg_type];
    
    	spin_lock_irqsave(&bank->slock, flags);
    
    	mask = (1 << width) - 1;
    	shift = pin_offset * width;
    	data = readl(reg_base + cfg_reg);
    
    	if (set) {
    		cfg_value = PINCFG_UNPACK_VALUE(*config);
    		data &= ~(mask << shift);
    		data |= (cfg_value << shift);
    		writel(data, reg_base + cfg_reg);
    	} else {
    		data >>= shift;
    		data &= mask;
    		*config = PINCFG_PACK(cfg_type, data);
    	}
    
    	spin_unlock_irqrestore(&bank->slock, flags);
    
    	return 0;
    }
    
    /* set the pin config settings for a specified pin group */
    static int samsung_pinconf_group_set(struct pinctrl_dev *pctldev,
    			unsigned group, unsigned long *configs,
    			unsigned num_configs)
    {
    	struct samsung_pinctrl_drv_data *drvdata;
    	const unsigned int *pins;
    	unsigned int cnt;
    
    	drvdata = pinctrl_dev_get_drvdata(pctldev);
    	pins = drvdata->pin_groups[group].pins;
    
    	for (cnt = 0; cnt < drvdata->pin_groups[group].num_pins; cnt++)
    		samsung_pinconf_set(pctldev, pins[cnt], configs, num_configs);
    
    	return 0;
    }
    

    驱动工程师的角度

    一般会用到的接口:
    devm_pinctrl_get
    pinctrl_lookup_state
    pinctrl_select_state

    操作gpio时,会用到的接口:
    pinctrl_request_gpio
    pinctrl_gpio_direction_input
    pinctrl_gpio_direction_output

    还有一些额外变体,懒得贴了

    下面以gpio方式的api为例子继续分析,这样也好与文章最开始的gpio子系统结合起来理解!pinctrl_request_gpio在驱动里,主要有两类会用到它,一类是gpio子系统的实现者,即gpio-xxx.c那些文件,另一类是pinctrl的实现者,即pinctrl-xxx.c那些文件。它们在注册gpio chip时,将pinctrl_request_gpio作为gpio chip里request,这样间接将pinctrl操作交给gpio子系统自动完成。从gpio子系统分析可知,request的调用是在gpio_request或者gpiod_get间接触发。看一下pinctrl_request_gpio做了些什么:

    int pinctrl_request_gpio(unsigned gpio)
    {
    	struct pinctrl_dev *pctldev;
    	struct pinctrl_gpio_range *range;
    	int ret;
    	int pin;
    	//这里会通过gpio来取得该gpio对应的pctldev和range,还记得分析gpiochip_add时的
        //of_gpiochip_add_pin_range吧,这里就用到了它add的信息
    	ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range);
    	if (ret) {
    		if (pinctrl_ready_for_gpio_range(gpio))
    			ret = 0;
    		return ret;
    	}
    
    	mutex_lock(&pctldev->mutex);
    
    	/* Convert to the pin controllers number space */
        //有了range就好办了啦,它里面有gpio与pin号的对应关系,当然这关系是最开始从设备树里解析过来的
    	pin = gpio_to_pin(range, gpio);
    
    	//有了所有信息调用pinmux_request_gpio进一步request吧
    	ret = pinmux_request_gpio(pctldev, range, pin, gpio);
    
    	mutex_unlock(&pctldev->mutex);
    
    	return ret;
    }
    

    继续pinmux_request_gpio

    int pinmux_request_gpio(struct pinctrl_dev *pctldev,
    			struct pinctrl_gpio_range *range,
    			unsigned pin, unsigned gpio)
    {
    	const char *owner;
    	int ret;
    
    	/* Conjure some name stating what chip and pin this is taken by */
    	owner = kasprintf(GFP_KERNEL, "%s:%d", range->name, gpio);
    	if (!owner)
    		return -EINVAL;
    	//pin_request之前分析的时候有看到调用过,不过这次gpio的时候会传入range,导致它的
        //调用流程会有所不同,里面会触发pinmux_ops的gpio_request_enable回调,而不是request回调
    	ret = pin_request(pctldev, pin, owner, range);
    	if (ret < 0)
    		kfree(owner);
    
    	return ret;
    }
    

    最后看看设备驱动模型中pinctrl的影子,在bus_probe_device的时候,会调用device_attach,而device_attach里会调用__device_attach去attach,在匹配成功后,会调用driver_probe_device,它会导致really_probe的调用来进行驱动的probe,最终会导致pinctrl_bind_pins调用,这个函数会pinctrl_get并设置设备的初始状态,这个过程不需要驱动额外做任何事情,多么巧妙啊 

    int pinctrl_bind_pins(struct device *dev)
    {
    	int ret;
    
    	dev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL);
    	if (!dev->pins)
    		return -ENOMEM;
    
    	dev->pins->p = devm_pinctrl_get(dev);
    	if (IS_ERR(dev->pins->p)) {
    		dev_dbg(dev, "no pinctrl handle
    ");
    		ret = PTR_ERR(dev->pins->p);
    		goto cleanup_alloc;
    	}
    
    	dev->pins->default_state = pinctrl_lookup_state(dev->pins->p,
    					PINCTRL_STATE_DEFAULT);
    	if (IS_ERR(dev->pins->default_state)) {
    		dev_dbg(dev, "no default pinctrl state
    ");
    		ret = 0;
    		goto cleanup_get;
    	}
    
    	ret = pinctrl_select_state(dev->pins->p, dev->pins->default_state);
    	if (ret) {
    		dev_dbg(dev, "failed to activate default pinctrl state
    ");
    		goto cleanup_get;
    	}
    ...
    }
    

    总结

    通过对gpio子系统和pinctrl子系统的分析,应该对这两个系统有了大致的概念了吧_ gpio子系统让驱动工程师不用关心底层gpio chip的具体实现,让bsp工程师不用关心上层驱动工程师的使用方式。pinctrl子系统帮我们管理了pin信息,包括了pin的mux和conf,同时也透明的处理了与gpio子系统的关联以及设备模型的关联。

    完!
    2015年7月

  • 相关阅读:
    fescar中文官网
    mybatis 中的 update 返回值你真的明白吗
    数据库读写分离搭建
    git 回退各种场景操作
    听说noip2015有幻方
    noi2015的回忆和教训
    bzoj4026
    bzoj4127
    bzoj2119
    关于fft的一点总结
  • 原文地址:https://www.cnblogs.com/rongpmcu/p/7662756.html
Copyright © 2011-2022 走看看