内核文档
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相关寄存器到默认状态,再重启;就会跟直接上电,保持一样的状态了