一、pinctrl的接口使用示例
// 以下是一个使用pinctrl的简单例子static int leds_probe(struct platform_device * pdev){ ...... struct pinctrl * leds_pctrl; struct pinctrl_state * leds_pin_state; leds_pctrl = devm_pinctrl_get(&pdev->dev); // if (IS_ERR(leds_pctrl)) { printk("devm_pinctrl_get error\n"); return -1; } leds_pin_state = pinctrl_lookup_state(leds_pctrl, "leds_active"); if (IS_ERR(leds_pin_state)) { printk(KERN_ERR "pinctrl_lookup_state error\n"); return -1; } ret = pinctrl_select_state(leds_pctrl, leds_pin_state); if (ret < 0) { printk(KERN_ERR "pinctrl_select_state error\n"); return -1; } ...... return 0;}
- 调用devm_pinctrl_get获取pinctrl句柄,devm_pinctrl_get实际调用pinctrl_get
- 调用pinctrl_lookup_state查询所需的pinctrl_state
- 调用pinctrl_select_state设置对应管脚状态
二、pinctrl的接口分析
struct pinctrl *pinctrl_get(struct device *dev){ struct pinctrl *p; if (WARN_ON(!dev)) return ERR_PTR(-EINVAL); /* * See if somebody else (such as the device core) has already * obtained a handle to the pinctrl for this device. In that case, * return another pointer to it. */ p = find_pinctrl(dev); if (p != NULL) { dev_dbg(dev, "obtain a copy of previously claimed pinctrl\n"); kref_get(&p->users); return p; } return create_pinctrl(dev);}
- 首先调用find_pinctrl函数查找pinctrl句柄,如果找到就直接返回
- 如果find_pinctrl没有找到pinctrl句柄, 则创建create_pinctrl
static struct pinctrl *find_pinctrl(struct device *dev){ struct pinctrl *p; mutex_lock(&pinctrl_list_mutex); list_for_each_entry(p, &pinctrl_list, node) if (p->dev == dev) { mutex_unlock(&pinctrl_list_mutex); return p; } mutex_unlock(&pinctrl_list_mutex); return NULL;}
- 遍历pinctrl_list查找与dev相匹配的设备pinctrl句柄
static struct pinctrl *create_pinctrl(struct device *dev){ struct pinctrl *p; const char *devname; struct pinctrl_maps *maps_node; int i; struct pinctrl_map const *map; int ret; /* * create the state cookie holder struct pinctrl for each * mapping, this is what consumers will get when requesting * a pin control handle with pinctrl_get() */ p = kzalloc(sizeof(*p), GFP_KERNEL); if (p == NULL) { dev_err(dev, "failed to alloc struct pinctrl\n"); return ERR_PTR(-ENOMEM); } p->dev = dev; INIT_LIST_HEAD(&p->states); INIT_LIST_HEAD(&p->dt_maps); ret = pinctrl_dt_to_map(p); if (ret < 0) { kfree(p); return ERR_PTR(ret); } devname = dev_name(dev); mutex_lock(&pinctrl_maps_mutex); /* Iterate over the pin control maps to locate the right ones */ for_each_maps(maps_node, i, map) { /* Map must be for this device */ if (strcmp(map->dev_name, devname)) continue; ret = add_setting(p, map); /* * At this point the adding of a setting may: * * - Defer, if the pinctrl device is not yet available * - Fail, if the pinctrl device is not yet available, * AND the setting is a hog. We cannot defer that, since * the hog will kick in immediately after the device * is registered. * * If the error returned was not -EPROBE_DEFER then we * accumulate the errors to see if we end up with * an -EPROBE_DEFER later, as that is the worst case. */ if (ret == -EPROBE_DEFER) { pinctrl_free(p, false); mutex_unlock(&pinctrl_maps_mutex); return ERR_PTR(ret); } } mutex_unlock(&pinctrl_maps_mutex); if (ret < 0) { /* If some other error than deferral occured, return here */ pinctrl_free(p, false); return ERR_PTR(ret); } kref_init(&p->users); /* Add the pinctrl handle to the global list */ mutex_lock(&pinctrl_list_mutex); list_add_tail(&p->node, &pinctrl_list); mutex_unlock(&pinctrl_list_mutex); return p;}
- 动态创建pinctrl并进行相应初始化工作
- 调用pinctrl_dt_to_map将pinctrl设备树解析为pinctrl_map,并注册到pinctrl_maps
- 查找pinctrl_maps,调用add_setting将相应的map添加到pinctrl
- 将pinctrl添加到pinctrl_list以便于后面查找
int pinctrl_dt_to_map(struct pinctrl *p){ ...... /* For each defined state ID */ for (state = 0; ; state++) { ...... /* For every referenced pin configuration node in it */ for (config = 0; config < size; config++) { phandle = be32_to_cpup(list++); /* Look up the pin configuration node */ np_config = of_find_node_by_phandle(phandle); 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, statename, np_config); of_node_put(np_config); if (ret < 0) goto err; } ....... } ......}
- 首先找到对应的设备树结点np_config
- 然后调用dt_to_map_one_config去解析设备树
static int dt_to_map_one_config(struct pinctrl *p, const char *statename, ...... ret = ops->dt_node_to_map(pctldev, np_config, &map, &num_maps); if (ret < 0) return ret; /* Stash the mapping table chunk away for later use */ return dt_remember_or_free_map(p, statename, pctldev, map, num_maps);}
- 调用pctldev->desc->pctlops的dt_node_to_map函数去解析设备树(与具体的pinctrl控制器有关)
- 调用dt_remember_or_free_map去注册map
static int dt_remember_or_free_map(struct pinctrl *p, const char *statename, struct pinctrl_dev *pctldev, struct pinctrl_map *map, unsigned num_maps){ int i; struct pinctrl_dt_map *dt_map; /* Initialize common mapping table entry fields */ for (i = 0; i < num_maps; i++) { map[i].dev_name = dev_name(p->dev); map[i].name = statename; if (pctldev) map[i].ctrl_dev_name = dev_name(pctldev->dev); } /* Remember the converted mapping table entries */ dt_map = kzalloc(sizeof(*dt_map), GFP_KERNEL); if (!dt_map) { dev_err(p->dev, "failed to alloc struct pinctrl_dt_map\n"); dt_free_map(pctldev, map, num_maps); return -ENOMEM; } dt_map->pctldev = pctldev; dt_map->map = map; dt_map->num_maps = num_maps; list_add_tail(&dt_map->node, &p->dt_maps); return pinctrl_register_map(map, num_maps, false);}
- 动态分配dt_map,然后注册到pinctrl上
- 调用pinctrl_register_map创建pinctrl_maps,并将mpinctrl_maps注册到pinctrl_maps链表上,至此pinctrl_dt_to_map的工作基本完成,继续回到create_pinctrl
static int add_setting(struct pinctrl *p, struct pinctrl_map const *map){ struct pinctrl_state *state; struct pinctrl_setting *setting; int ret; state = find_state(p, map->name); if (!state) state = create_state(p, map->name); if (IS_ERR(state)) return PTR_ERR(state); if (map->type == PIN_MAP_TYPE_DUMMY_STATE) return 0; setting = kzalloc(sizeof(*setting), GFP_KERNEL); if (setting == NULL) { dev_err(p->dev, "failed to alloc struct pinctrl_setting\n"); return -ENOMEM; } setting->type = map->type; setting->pctldev = get_pinctrl_dev_from_devname(map->ctrl_dev_name); if (setting->pctldev == NULL) { kfree(setting); /* Do not defer probing of hogs (circular loop) */ if (!strcmp(map->ctrl_dev_name, map->dev_name)) return -ENODEV; /* * OK let us guess that the driver is not there yet, and * let's defer obtaining this pinctrl handle to later... */ dev_info(p->dev, "unknown pinctrl device %s in map entry, deferring probe", map->ctrl_dev_name); return -EPROBE_DEFER; } setting->dev_name = map->dev_name; switch (map->type) { case PIN_MAP_TYPE_MUX_GROUP: ret = pinmux_map_to_setting(map, setting); break; case PIN_MAP_TYPE_CONFIGS_PIN: case PIN_MAP_TYPE_CONFIGS_GROUP: ret = pinconf_map_to_setting(map, setting); break; default: ret = -EINVAL; break; } if (ret < 0) { kfree(setting); return ret; } list_add_tail(&setting->node, &state->settings); return 0;}
- 根据pinctrl和map名称查找state,如果没有找到就创建
- 动态创建setting并初始化,然后添加到state的settings链表上
struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p, const char *name) { struct pinctrl_state *state; state = find_state(p, name); if (!state) { if (pinctrl_dummy_state) { /* create dummy state */ dev_dbg(p->dev, "using pinctrl dummy state (%s)\n", name); state = create_state(p, name); } else state = ERR_PTR(-ENODEV); } return state;}
- 根据名称查找pinctrl里的state,返回pinctrl_state
int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state){ struct pinctrl_setting *setting, *setting2; struct pinctrl_state *old_state = p->state; int ret; if (p->state == state) return 0; if (p->state) { /* * For each pinmux setting in the old state, forget SW's record * of mux owner for that pingroup. Any pingroups which are * still owned by the new state will be re-acquired by the call * to pinmux_enable_setting() in the loop below. */ list_for_each_entry(setting, &p->state->settings, node) { if (setting->type != PIN_MAP_TYPE_MUX_GROUP) continue; pinmux_disable_setting(setting); } } p->state = NULL; /* 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); break; case PIN_MAP_TYPE_CONFIGS_PIN: case PIN_MAP_TYPE_CONFIGS_GROUP: ret = pinconf_apply_setting(setting); break; default: ret = -EINVAL; break; } if (ret < 0) { goto unapply_new_state; } } p->state = state; ......}
- 调用pinmux_enable_setting和pinconf_apply_setting应用相应settings