pinctrl驱动详解

内核文档

Documentation/pinctrl.txt有详细的pinctrl介绍,一般我们使用会通过如下接口,去获取设备的pinctrl;再获取不同的state;最后选择使用那种state

#include <linux/pinctrl/consumer.h>

struct pinctrl *p;
struct pinctrl_state *s1, *s2;

foo_probe()
{
        /* Setup */
        p = devm_pinctrl_get(&device);
        if (IS_ERR(p))
                ...

        s1 = pinctrl_lookup_state(foo->p, "pos-A");
        if (IS_ERR(s1))
                ...

        s2 = pinctrl_lookup_state(foo->p, "pos-B");
        if (IS_ERR(s2))
                ...
}

foo_switch()
{
        /* Enable on position A */
        ret = pinctrl_select_state(s1);
        if (ret < 0)
            ...

        ...

        /* Enable on position B */
        ret = pinctrl_select_state(s2);
        if (ret < 0)
            ...

        ...
}

设备树节点示例

这就是一个uart的设备树节点,通过devm_pinctrl_get解析出pinctrl;通过pinctrl_lookup_state获取default和sleep状态;最后根据情况使用pinctrl_select_state设置为对应的复用配置;uart1_pmx_func1就是func ; pinctrl-single,pins就是group ; GPIO30 AF1就是pin

uart1: uart@xxxxxxx {  /* nezhas evb use ap uart */
        pinctrl-names = "default","sleep";
        pinctrl-0 = <&uart1_pmx_func1 &uart1_pmx_func2>;
        pinctrl-1 = <&uart1_pmx_func1_sleep &uart1_pmx_func2>;
        edge-wakeup-gpio = <29>; /* GPIO57: AP UART rx pin */
        status = "okay";
};
pmx: pinmux@xxxxxx {
    ..............
    uart1_pmx_func1: uart1_pmx_func1 {
            pinctrl-single,pins = <
                    GPIO29 AF1
            >;
            MFP_DEFAULT;
    };
    uart1_pmx_func2: uart1_pmx_func2 {
            pinctrl-single,pins = <
                    GPIO30 AF1
            >;
            MFP_DEFAULT;
    };
    uart1_pmx_func1_sleep: uart1_pmx_func1_sleep {
            pinctrl-single,pins = <
                    GPIO29 AF0
            >;
            DS_MEDIUM;PULL_NONE;EDGE_BOTH;SL_NORMAL;
    };
    ............
}

debugfs

cat pinctrl-handles可以看到当前设备使用的那种state;及其对应的func;和group

/sys/kernel/debug/pinctrl # cat pinctrl-handles
Requested pin control handlers their pinmux maps:
device: d4017000.uart current state: default
  state: default
    type: MUX_GROUP controller pinctrl-single group: uart1_pmx_func1 (3) function: uart1_pmx_func1 (3)
    type: CONFIGS_GROUP controller pinctrl-single group uart1_pmx_func1 (3)config output drive strength: 0x200
config slew rate: 0x0
config input schmitt trigger: 0x0
config input bias pull up: 0x0
config input bias pull down: 0x0
config input schmitt enabled: 0x0
    type: MUX_GROUP controller pinctrl-single group: uart1_pmx_func2 (4) function: uart1_pmx_func2 (4)
    type: CONFIGS_GROUP controller pinctrl-single group uart1_pmx_func2 (4)config output drive strength: 0x200
config slew rate: 0x0
config input schmitt trigger: 0x0
config input bias pull up: 0x0
config input bias pull down: 0x0
config input schmitt enabled: 0x0
  state: sleep
    type: MUX_GROUP controller pinctrl-single group: uart1_pmx_func1_sleep (5) function: uart1_pmx_func1_sleep (5)
    type: CONFIGS_GROUP controller pinctrl-single group uart1_pmx_func1_sleep (5)config output drive strength: 0x200
config slew rate: 0x0
config input schmitt trigger: 0x3
config input bias pull up: 0x0
config input bias pull down: 0x0
config input schmitt enabled: 0x1
    type: MUX_GROUP controller pinctrl-single group: uart1_pmx_func2 (4) function: uart1_pmx_func2 (4)
    type: CONFIGS_GROUP controller pinctrl-single group uart1_pmx_func2 (4)config output drive strength: 0x200
config slew rate: 0x0
config input schmitt trigger: 0x0
config input bias pull up: 0x0
config input bias pull down: 0x0
config input schmitt enabled: 0x0

cat pinmux-functions可以看到对应func的groups

/sys/kernel/debug/pinctrl/d401e000.pinmux-pinctrl-single # cat pinmux-functions
function: twsi0_pmx_func, groups = [ twsi0_pmx_func ]
function: twsi0_pmx_gpio, groups = [ twsi0_pmx_gpio ]
function: dvc_pmx_func, groups = [ dvc_pmx_func ]
function: uart1_pmx_func1, groups = [ uart1_pmx_func1 ]
function: uart1_pmx_func2, groups = [ uart1_pmx_func2 ]
function: uart1_pmx_func1_sleep, groups = [ uart1_pmx_func1_sleep ]

cat pingroups可以看到group包含的pin

/sys/kernel/debug/pinctrl/d401e000.pinmux-pinctrl-single # cat pingroups
registered pin groups:
....
group: uart1_pmx_func1
pin 84 (PIN84)

group: uart1_pmx_func2
pin 85 (PIN85)

group: uart1_pmx_func1_sleep
pin 84 (PIN84)

group: emac_pmx_func0
pin 55 (PIN55)
pin 56 (PIN56)
pin 57 (PIN57)
pin 58 (PIN58)
pin 61 (PIN61)
pin 62 (PIN62)
pin 70 (PIN70)
....

cat pinconf-pins可以看到pin的电气属性等配置

/sys/kernel/debug/pinctrl/d401e000.pinmux-pinctrl-single # cat pinconf-pins
pin 84 (PIN84): input bias disabled, output drive strength (16 mA), input schmitt trigger, slew rate (0)
pin 85 (PIN85): input bias disabled, output drive strength (16 mA), input schmitt trigger, slew rate (0)

cat gpio-ranges可以看到gpio对应的pin

/sys/kernel/debug/pinctrl/d401e000.pinmux-pinctrl-single # cat gpio-ranges
GPIO ranges handled:
0: gpio-pxa GPIOS [0 - 31] PINS [55 - 86]
0: gpio-pxa GPIOS [0 - 22] PINS [87 - 109]
3: gpio-pxa GPIOS [3 - 31] PINS [110 - 138]
0: gpio-pxa GPIOS [0 - 2] PINS [139 - 141]
28: gpio-pxa GPIOS [28 - 30] PINS [51 - 53]

pinctrl驱动

pinctrl主机驱动会用devm_pinctrl_register注册时,放进去的那三个ops(下面的代码前几行就可以看出)来真正的配置pin的复用功能和电气属性;pinctrl的驱动注册简化如下

static int pcs_probe(struct platform_device *pdev){
    ....
    pcs->desc.pctlops = &pcs_pinctrl_ops;
    pcs->desc.pmxops = &pcs_pinmux_ops;
    if (pcs->is_pinconf)
            pcs->desc.confops = &pcs_pinconf_ops;
    pcs->desc.owner = THIS_MODULE;

    pcs->pctl = pinctrl_register(&pcs->desc, pcs->dev, pcs);
    ....
}

pinconf_ops

根据从设备树获取的pinctrl的信息;1.获取和设置单个pin的电气属性和驱动能力;2.获取和设置group的电气属性和驱动能力

static const struct pinconf_ops pcs_pinconf_ops = {
        .pin_config_get = pcs_pinconf_get,
        .pin_config_set = pcs_pinconf_set,
        .pin_config_group_get = pcs_pinconf_group_get,
        .pin_config_group_set = pcs_pinconf_group_set,
        .pin_config_dbg_show = pcs_pinconf_dbg_show,
        .pin_config_group_dbg_show = pcs_pinconf_group_dbg_show,
        .pin_config_config_dbg_show = pcs_pinconf_config_dbg_show,
        .is_generic = true,
};
static int pcs_pinconf_set(struct pinctrl_dev *pctldev,
                                unsigned pin, unsigned long config)
{
        struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev);
        struct pcs_function *func;
        unsigned offset = 0, shift = 0, i, data, ret;
        u16 arg;

        ret = pcs_get_function(pctldev, pin, &func);
        if (ret)
                return ret;

        for (i = 0; i < func->nconfs; i++) {
                if (pinconf_to_config_param(config) == func->conf[i].param) {
                        offset = pin * (pcs->width / BITS_PER_BYTE);
                        data = pcs->read(pcs->base + offset);
                        arg = pinconf_to_config_argument(config);
                        switch (func->conf[i].param) {
                        /* 2 parameters */
                        case PIN_CONFIG_INPUT_SCHMITT:
                        case PIN_CONFIG_DRIVE_STRENGTH:
                        case PIN_CONFIG_SLEW_RATE:
                        case PIN_CONFIG_LOW_POWER_MODE:
                                shift = ffs(func->conf[i].mask) - 1;
                                data &= ~func->conf[i].mask;
                                data |= (arg << shift) & func->conf[i].mask;
                                break;
                        /* 4 parameters */
                        case PIN_CONFIG_BIAS_DISABLE:
                                pcs_pinconf_clear_bias(pctldev, pin);
                                break;
                        case PIN_CONFIG_BIAS_PULL_DOWN:
                        case PIN_CONFIG_BIAS_PULL_UP:
                                if (arg)
                                        pcs_pinconf_clear_bias(pctldev, pin);
                                /* fall through */
                        case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
                                data &= ~func->conf[i].mask;
                                if (arg)
                                        data |= func->conf[i].enable;
                                else
                                        data |= func->conf[i].disable;
                                break;
                        default:
                                return -ENOTSUPP;
                        }
                        pcs->write(data, pcs->base + offset);
                        return 0;
                }
        }
        return -ENOTSUPP;
}

pinctrl_ops

主要获取从设备树获取的pinctrl的-----group的信息,比如数量,名字,包含的pin;映射pinctrl的state和func(.dt_node_to_map)

static const struct pinctrl_ops pcs_pinctrl_ops = {
        .get_groups_count = pcs_get_groups_count,
        .get_group_name = pcs_get_group_name,
        .get_group_pins = pcs_get_group_pins,
        .pin_dbg_show = pcs_pin_dbg_show,
        .dt_node_to_map = pcs_dt_node_to_map,
        .dt_free_map = pcs_dt_free_map,
};

pinmux_ops

1.主要获取从设备树获取的pinctrl的------functions的信息:比如数量,名字,所属groups;2.配置复用功能;3.配置成gpio

static const struct pinmux_ops pcs_pinmux_ops = {
        .get_functions_count = pcs_get_functions_count,
        .get_function_name = pcs_get_function_name,
        .get_function_groups = pcs_get_function_groups,
        .enable = pcs_enable,
        .disable = pcs_disable,
        .gpio_request_enable = pcs_request_gpio,
};
static int pcs_enable(struct pinctrl_dev *pctldev, unsigned fselector,
        unsigned group)
{
        struct pcs_device *pcs;
        struct pcs_function *func;
        int i;

        pcs = pinctrl_dev_get_drvdata(pctldev);
        /* If function mask is null, needn't enable it. */
        if (!pcs->fmask)
                return 0;
        func = radix_tree_lookup(&pcs->ftree, fselector);
        if (!func)
                return -EINVAL;

        dev_dbg(pcs->dev, "enabling %s function%i\n",
                func->name, fselector);

        for (i = 0; i < func->nvals; i++) {
                struct pcs_func_vals *vals;
                unsigned val, mask;

                vals = &func->vals[i];
                val = pcs->read(vals->reg);
                if (!vals->mask)
                        mask = pcs->fmask;
                else
                        mask = pcs->fmask & vals->mask;

                val &= ~mask;
                val |= (vals->val & mask);
                pcs->write(val, vals->reg);
        }

        return 0;
}

pinctrl_register

调用栈如下;主要的三个地方都加了注释,下面的会一一的分析

pinctrl_register
    create_pinctrl
        pinctrl_dt_to_map //建立state跟func的map
            dt_to_map_one_config
                ops->dt_node_to_map
                    pinctrl_ops->dt_node_to_map
                        pcs_dt_node_to_map
                            pcs_parse_one_pinctrl_entry //先添加一个PIN_MAP_TYPE_MUX_GROUP的pinctrl_map
                                pcs_parse_pinconf // 再添加一个PIN_MAP_TYPE_CONFIGS_GROUP的pinctrl_map

注册pinctrl驱动时就会建立设备树节点跟pinctrl的map关系,也即设备的pinctrl的state跟具体func隐映射;比如某个设备的节点里的 pinctrl-names = "default","sleep";pinctrl-0 = <xxx>;pinctrl-1 = <yyy>;

int pinctrl_dt_to_map(struct pinctrl *p)
{
        struct device_node *np = p->dev->of_node;
        int state, ret;
        char *propname;
        struct property *prop;
        const char *statename;
        const __be32 *list;
        int size, config;
        phandle phandle;
        struct device_node *np_config;

        /* CONFIG_OF enabled, p->dev not instantiated from DT */
        if (!np) {
                dev_dbg(p->dev, "no of_node; not parsing pinctrl DT\n");
                return 0;
        }

        /* We may store pointers to property names within the node */
        of_node_get(np);

        /* For each defined state ID */
        for (state = 0; ; state++) {
                /* Retrieve the pinctrl-* property */
                propname = kasprintf(GFP_KERNEL, "pinctrl-%d", state);
                prop = of_find_property(np, propname, &size);
                kfree(propname);
                if (!prop)
                        break;
                list = prop->value;
                size /= sizeof(*list);

                /* Determine whether pinctrl-names property names the state */
                ret = of_property_read_string_index(np, "pinctrl-names",
                                                    state, &statename);
                /*
                 * 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;
                }

                /* 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;
                }

                /* No entries in DT? Generate a dummy state table entry */
                if (!size) {
                        ret = dt_remember_dummy_state(p, statename);
                        if (ret < 0)
                                goto err;
                }
        }

        return 0;

err:
        pinctrl_dt_free_maps(p);
        return ret;
}

给pinctrl_map的type赋值为PIN_MAP_TYPE_MUX_GROUP;匹配pinctrl_select_state里的pinmux_enable_setting,来设置复用功能

static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs,
                                                struct device_node *np,
                                                struct pinctrl_map **map,
                                                unsigned *num_maps,
                                                const char **pgnames)
{
        struct pcs_func_vals *vals;
        const __be32 *mux;
        int size, params, rows, *pins, index = 0, found = 0, res = -ENOMEM;
        struct pcs_function *function;

        if (pcs->bits_per_mux) {
                params = 3;
                mux = of_get_property(np, PCS_MUX_BITS_NAME, &size);
        } else {
                params = 2;
                mux = of_get_property(np, PCS_MUX_PINS_NAME, &size);
        }

        if (!mux) {
                dev_err(pcs->dev, "no valid property for %s\n", np->name);
                return -EINVAL;
        }

        if (size < (sizeof(*mux) * params)) {
                dev_err(pcs->dev, "bad data for %s\n", np->name);
                return -EINVAL;
        }

        size /= sizeof(*mux);   /* Number of elements in array */
        rows = size / params;

        vals = devm_kzalloc(pcs->dev, sizeof(*vals) * rows, GFP_KERNEL);
        if (!vals)
                return -ENOMEM;

        pins = devm_kzalloc(pcs->dev, sizeof(*pins) * rows, GFP_KERNEL);
        if (!pins)
                goto free_vals;

        while (index < size) {
                unsigned offset, val;
                int pin;

                offset = be32_to_cpup(mux + index++);
                val = be32_to_cpup(mux + index++);
                vals[found].reg = pcs->base + offset;
                vals[found].val = val;
                if (params == 3) {
                        val = be32_to_cpup(mux + index++);
                        vals[found].mask = val;
                }

                pin = pcs_get_pin_by_offset(pcs, offset);
                if (pin < 0) {
                        dev_err(pcs->dev,
                                "could not add functions for %s %ux\n",
                                np->name, offset);
                        break;
                }
                pins[found++] = pin;
        }

        pgnames[0] = np->name;
        function = pcs_add_function(pcs, np, np->name, vals, found, pgnames, 1);
        if (!function)
                goto free_pins;

        res = pcs_add_pingroup(pcs, np, np->name, pins, found);
        if (res < 0)
                goto free_function;

        (*map)->type = PIN_MAP_TYPE_MUX_GROUP;
        (*map)->data.mux.group = np->name;
        (*map)->data.mux.function = np->name;

        if (pcs->is_pinconf) {
                res = pcs_parse_pinconf(pcs, np, function, map);
                if (res)
                        goto free_pingroups;
                *num_maps = 2;
        } else {
                *num_maps = 1;
        }
        return 0;

free_pingroups:
        pcs_free_pingroups(pcs);
        *num_maps = 1;
free_function:
        pcs_remove_function(pcs, function);

free_pins:
        devm_kfree(pcs->dev, pins);

free_vals:
        devm_kfree(pcs->dev, vals);

        return res;
}

通过m++;给下一个pinctrl_map的type赋值为PIN_MAP_TYPE_CONFIGS_GROUP;匹配pinctrl_select_state里的pinconf_apply_setting来设置电气属性等配置

static int pcs_parse_pinconf(struct pcs_device *pcs, struct device_node *np,
                             struct pcs_function *func,
                             struct pinctrl_map **map)

{
        struct pinctrl_map *m = *map;
        int i = 0, nconfs = 0;
        unsigned long *settings = NULL, *s = NULL;
        struct pcs_conf_vals *conf = NULL;
        struct pcs_conf_type prop2[] = {
                { "pinctrl-single,drive-strength", PIN_CONFIG_DRIVE_STRENGTH, },
                { "pinctrl-single,slew-rate", PIN_CONFIG_SLEW_RATE, },
                { "pinctrl-single,input-schmitt", PIN_CONFIG_INPUT_SCHMITT, },
                { "pinctrl-single,lpm-output", PIN_CONFIG_LOW_POWER_MODE, },
        };
        struct pcs_conf_type prop4[] = {
                { "pinctrl-single,bias-pullup", PIN_CONFIG_BIAS_PULL_UP, },
                { "pinctrl-single,bias-pulldown", PIN_CONFIG_BIAS_PULL_DOWN, },
                { "pinctrl-single,input-schmitt-enable",
                        PIN_CONFIG_INPUT_SCHMITT_ENABLE, },
        };

        /* If pinconf isn't supported, don't parse properties in below. */
        if (!pcs->is_pinconf)
                return 0;

        /* cacluate how much properties are supported in current node */
        for (i = 0; i < ARRAY_SIZE(prop2); i++) {
                if (of_find_property(np, prop2[i].name, NULL))
                        nconfs++;
        }
        for (i = 0; i < ARRAY_SIZE(prop4); i++) {
                if (of_find_property(np, prop4[i].name, NULL))
                        nconfs++;
        }
        if (!nconfs)
                return 0;

        func->conf = devm_kzalloc(pcs->dev,
                                  sizeof(struct pcs_conf_vals) * nconfs,
                                  GFP_KERNEL);
        if (!func->conf)
                return -ENOMEM;
        func->nconfs = nconfs;
        conf = &(func->conf[0]);
        m++;
        settings = devm_kzalloc(pcs->dev, sizeof(unsigned long) * nconfs,
                                GFP_KERNEL);
        if (!settings)
                return -ENOMEM;
        s = &settings[0];

        for (i = 0; i < ARRAY_SIZE(prop2); i++)
                pcs_add_conf2(pcs, np, prop2[i].name, prop2[i].param,
                              &conf, &s);
        for (i = 0; i < ARRAY_SIZE(prop4); i++)
                pcs_add_conf4(pcs, np, prop4[i].name, prop4[i].param,
                              &conf, &s);
        m->type = PIN_MAP_TYPE_CONFIGS_GROUP;
        m->data.configs.group_or_pin = np->name;
        m->data.configs.configs = settings;
        m->data.configs.num_configs = nconfs;
        return 0;
}

pinctrl_select_state

不管从前面debugfs直接的;还是从pcs_parse_one_pinctrl_entry和pcs_parse_pinconf看到 ----每个state会有两个type的map;1.MUX_GROUP;2.CONFIGS_GROUP

所以pinctrl_select_state会先调用pinmux_enable_setting来设置复用功能;再调用pinconf_apply_setting来设置group中每个pin的电气属性和去驱动能力等配置

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) {
                /*
                 * The set of groups with a mux configuration in the old state
                 * may not be identical to the set of groups with a mux setting
                 * in the new state. While this might be unusual, it's entirely
                 * possible for the "user"-supplied mapping table to be written
                 * that way. For each group that was configured in the old state
                 * but not in the new state, this code puts that group into a
                 * safe/disabled state.
                 */
                list_for_each_entry(setting, &p->state->settings, node) {
                        bool found = false;
                        if (setting->type != PIN_MAP_TYPE_MUX_GROUP)
                                continue;
                        list_for_each_entry(setting2, &state->settings, node) {
                                if (setting2->type != PIN_MAP_TYPE_MUX_GROUP)
                                        continue;
                                if (setting2->data.mux.group ==
                                                setting->data.mux.group) {
                                        found = true;
                                        break;
                                }
                        }
                        if (!found)
                                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;

        return 0;

unapply_new_state:
        dev_err(p->dev, "Error applying setting, reverse things back\n");

        list_for_each_entry(setting2, &state->settings, node) {
                if (&setting2->node == &setting->node)
                        break;
                /*
                 * All we can do here is pinmux_disable_setting.
                 * That means that some pins are muxed differently now
                 * than they were before applying the setting (We can't
                 * "unmux a pin"!), but it's not a big deal since the pins
                 * are free to be muxed by another apply_setting.
                 */
                if (setting2->type == PIN_MAP_TYPE_MUX_GROUP)
                        pinmux_disable_setting(setting2);
        }

        /* There's no infinite recursive loop here because p->state is NULL */
        if (old_state)
                pinctrl_select_state(p, old_state);

        return ret;
}

pinmux_enable_setting

pinmux_enable_setting调用pinctrl_ops->get_group_pins获取对应的group,及其里面的pin信息;再用pin_request来pin注册描述符;再调用pin_desc_get获取pin的描述符,并填充复用信息,供debugfs参考;pinmux_ops->enable来设置复用pin为那种功能

int pinmux_enable_setting(struct pinctrl_setting const *setting)
{
        struct pinctrl_dev *pctldev = setting->pctldev;
        const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
        const struct pinmux_ops *ops = pctldev->desc->pmxops;
        int ret;
        const unsigned *pins;
        unsigned num_pins;
        int i;
        struct pin_desc *desc;

        ret = pctlops->get_group_pins(pctldev, setting->data.mux.group,
                                      &pins, &num_pins);
        if (ret) {
                /* errors only affect debug data, so just warn */
                dev_warn(pctldev->dev,
                         "could not get pins for group selector %d\n",
                         setting->data.mux.group);
                num_pins = 0;
        }

        /* Try to allocate all pins in this group, one by one */
        for (i = 0; i < num_pins; i++) {
                ret = pin_request(pctldev, pins[i], setting->dev_name, NULL);
                if (ret) {
                        dev_err(pctldev->dev,
                                "could not request pin %d on device %s\n",
                                pins[i], pinctrl_dev_get_name(pctldev));
                        goto err_pin_request;
                }
        }

        /* Now that we have acquired the pins, encode the mux setting */
        for (i = 0; i < num_pins; i++) {
                desc = pin_desc_get(pctldev, pins[i]);
                if (desc == NULL) {
                        dev_warn(pctldev->dev,
                                 "could not get pin desc for pin %d\n",
                                 pins[i]);
                        continue;
                }
                desc->mux_setting = &(setting->data.mux);
        }

        ret = ops->enable(pctldev, setting->data.mux.func,
                          setting->data.mux.group);

        if (ret)
                goto err_enable;

        return 0;
}

pinconf_apply_setting

pinctrl_select_state会调用pinconf_apply_setting;会调用pinconf_ops->pin_config_group_set来设置group中每个pin的电气属性和去驱动能力等配置

int pinconf_apply_setting(struct pinctrl_setting const *setting)
{
        struct pinctrl_dev *pctldev = setting->pctldev;
        const struct pinconf_ops *ops = pctldev->desc->confops;
        int i, ret;

        if (!ops) {
                dev_err(pctldev->dev, "missing confops\n");
                return -EINVAL;
        }

        switch (setting->type) {
        case PIN_MAP_TYPE_CONFIGS_PIN:
                if (!ops->pin_config_set) {
                        dev_err(pctldev->dev, "missing pin_config_set op\n");
                        return -EINVAL;
                }
                for (i = 0; i < setting->data.configs.num_configs; i++) {
                        ret = ops->pin_config_set(pctldev,
                                        setting->data.configs.group_or_pin,
                                        setting->data.configs.configs[i]);
                        if (ret < 0) {
                                dev_err(pctldev->dev,
                                        "pin_config_set op failed for pin %d config %08lx\n",
                                        setting->data.configs.group_or_pin,
                                        setting->data.configs.configs[i]);
                                return ret;
                        }
                }
                break;
        case PIN_MAP_TYPE_CONFIGS_GROUP:
                if (!ops->pin_config_group_set) {
                        dev_err(pctldev->dev,
                                "missing pin_config_group_set op\n");
                        return -EINVAL;
                }
                for (i = 0; i < setting->data.configs.num_configs; i++) {
                        ret = ops->pin_config_group_set(pctldev,
                                        setting->data.configs.group_or_pin,
                                        setting->data.configs.configs[i]);
                        if (ret < 0) {
                                dev_err(pctldev->dev,
                                        "pin_config_group_set op failed for group %d config %08lx\n",
                                        setting->data.configs.group_or_pin,
                                        setting->data.configs.configs[i]);
                                return ret;
                        }
                }
                break;
        default:
                return -EINVAL;
        }

        return 0;
}

实例分析

Q:直接上电跟reboot,某些led的状态不一致?

A:其实就是led在reboot前,已经从默认的状态,设置成gpio的复用功能和对应的电气属性了;所以,可以通过devmem设置pinctrl相关寄存器到默认状态,再重启;就会跟直接上电,保持一样的状态了

  • 21
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值