gpio子系统和pinctrl子系统(三)

2023-12-07 04:18
文章标签 gpio 子系统 pinctrl

本文主要是介绍gpio子系统和pinctrl子系统(三),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

转自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代码,所以这里以三星的代码为例子),不过这里贴的代码有点多(尽量将无关的代码删掉),耐心的看吧^_^

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\n");return NULL;}//获取该节点对应的matchmatch = 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 basebank->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_pinsctrldesc->pctlops = &samsung_pctrl_ops;//这个是pinctrl chip driver根据自己平台的特性,可选的支持的//主要有request、get_functions_count、get_function_groups、//enable,和gpio相关的还有额外几个gpio_request_enable、gpio_disable_free、gpio_set_directionctrldesc->pmxops = &samsung_pinmux_ops;//这个是pinctrl chip driver根据自己平台的特性,可选的支持的//主要有pin_config_get、pin_config_set、pin_config_group_get、pin_config_group_setctrldesc->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_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\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_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\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子系统的关联以及设备模型的关联。


这篇关于gpio子系统和pinctrl子系统(三)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/464465

相关文章

A20 操作GPIO口

例如:先在 Sys_config.fex文件中 [1302_para] 1302_used      = 1 1302_clk             = port:PD05<1><default><default><0> 1302_dat             = port:PD06<1><default><default><0> 1302_rs

阿里云飞天洛神云网络子系统“齐天”:超大规模云网络智能运维的“定海神针”

云布道师 引言:近日,在南京上秦淮国际文化交流中心举办第八届未来网络发展大会上,阿里云凭借“超大规模云网络智能运维系统”一举斩获由中国通信学会专家组评选的“未来网络领先创新科技成果奖”,本次获奖也体现出阿里云在云网络技术领域科技创新实力获得业界的高度认可,评委专家一致认为“本项目针对云网络运维面临的成本、效率、实施性等挑战,突破了高性能运维数据管理、无人值守网络变更、高精度网络监控、全链路异

新路程------sil9135 hi3516a gpio功能确认

首先确认SCDT管脚 数据手册解释: 也就是hi3516端应该配置为in,这个pin用来表示有video进来,那么进来前后,pin的状态是如何变化的呢? 还有编程手册里 有寄存器可以读取这个SCDT的值,那么还要这个pin干什么呢?不太理解,以后解释 接下来是int pin,中断好配, hi3516a这边是gpio11_2,先看是否配成gpio /usr #

【STM32开发】GPIO最全解析及应用实例

目录 【1】GPIO概述 GPIO的基本概念 GPIO的应用 【2】GPIO功能描述 1.IO功能框图 2.知识补充 3.功能详述 浮空输入 上拉输入 下拉输入 模拟输入 推挽输出 开漏输出 复用开漏输出和复用推挽输出 【3】GPIO常用寄存器 相关寄存器介绍 4个32位配置寄存器 2个32位数据寄存器 1个32位 置位/复位寄存器 2个32位 复用功能配置寄存器 常用寄存器详述 GPIO端

Simulink代码生成: For Iterator子系统及其代码

本文研究Simulink中的For Iterator子系统及其生成的代码。 文章目录 1 Simulink中的For Iterator子系统2 For Iterator子系统建模示例3 For Iterator子系统的代码4 总结 1 Simulink中的For Iterator子系统 不管是在C语言还是Matlab脚本编程的时候,都避免不了使用for循环来反复执行某一段代码。在

集成电路学习:什么是GPIO通用输入输出

GPIO:通用输入输出         GPIO,全称General Purpose Input/Output,即通用输入/输出端口,是嵌入式系统中非常重要的基本硬件资源之一。以下是对GPIO的详细解析: 一、GPIO的定义与功能         GPIO是一种非常灵活的接口,可以实现数字输入、数字输出、模拟输入、模拟输出等多种功能。它作为微控制器、嵌入式系统或其他电子设备与外部世界进行

Linux字符设备驱动 -- regulator子系统

文章目录 环境regulator子系统简介:Regulator设备的注册Consumer设备的注册 环境 linux 4.9 armv8-A regulator子系统简介: 关于regulator子系统,可以看下这这些博客: Linux驱动之Regulator子系统Linux 内核之电源篇(加载流程) regulator,翻译就是调节器。一些可以输出电流电压的设备可以使用

在WIN10的linux子系统是存放在硬盘的哪里?

谷歌搜索 win10 linux location,然后里面有 stackoverflow 的几个链接。 里面说到 linux 存放位置在 C:\Users\{user}\AppData\Local\lxss\{username} 其中 {user} 指的是 windows 的用户,{username} 指的是 linux 里的用户。 以及你的 /home 文件夹会在

玩转 Windows 10 中的 Linux 子系统

在今年的 Build 2016 上,微软向全世界介绍了他们还处于 Beta 阶段的 Windows 下的 Linux 子系统Windows Subsystem for Linux(WSL),它可以让开发者们在 Windows 10 下通过 Bash shell 运行原生的 Ubuntu 用户态二进制程序。如果你参与了 Windows Insider 计划,你就可以在最新的 Windows 10 年

TI DSP TMS320F280025 Note9:GPIO输入输出与外部中断功能原理与应用

TMS320F280025 GPIO输入输出与外部中断功能原理与应用 文章目录 TMS320F280025 GPIO输入输出与外部中断功能原理与应用GPIO原理输入输出模式的共同特性1. 复用设置2. 内部上拉设置3. GPIO状态读取 对于输出模式输出电平设置开漏输出设置 对于输入模式极性设置采样类型不同步(异步输入)只同步到SYSCLKOUT使用采样窗口进行鉴定 外部输入中断G