本文共 29058 字,大约阅读时间需要 96 分钟。
转自http://blog.rongpmcu.com/gpiozi-xi-tong-he-pinctrlzi-xi-tong-xia/
打算从两个角度来情景分析,先从bsp驱动工程师的角度,然后是驱动工程师的角度,下面以三星s3c6410 Pinctrl-samsung.c为例看看pinctrl输入参数的初始化过程(最开始的zynq平台的pin配置貌似是通过bitstreams来的,内核层没看到有关配置pin的代码,不过最新的zynq代码里加入了pinctrl,但我手上的恰好的较早其的zynq代码,所以这里以三星的代码为例子),不过这里贴的代码有点多(尽量将无关的代码删掉),耐心的看吧^_^
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\n"); 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 =; //}; //} //这里就是处理这些子节点 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\n"); 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\n"); 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\n"); 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\n"); 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\n"); 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\n"); 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\n"); 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_count
、get_function_name
、get_function_groups
、get_groups_count
、get_group_name
、get_group_pins
、request
(三星pinmux_ops
没有实现它)、enable
、pin_config_set
、pin_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 =; }; uart0_fctl: uart0-fctl { samsung,pins = "gpa-2", "gpa-3"; samsung,pin-function = <2>; samsung,pin-pud = ; };//下面部分是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\n", 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\n"); 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\n"); 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\n"); 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\n"); 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_enable
和samsung_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, ®, &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, ®_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\n"); 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\n"); 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\n"); goto cleanup_get; }...}
通过对gpio子系统和pinctrl子系统的分析,应该对这两个系统有了大致的概念了吧^_^ gpio子系统让驱动工程师不用关心底层gpio chip的具体实现,让bsp工程师不用关心上层驱动工程师的使用方式。pinctrl子系统帮我们管理了pin信息,包括了pin的mux和conf,同时也透明的处理了与gpio子系统的关联以及设备模型的关联。
转载地址:http://agqji.baihongyu.com/