/* Parse the function and configuration properties. At least a function * or one configuration must be specified.
*/
ret = of_property_read_string(np, "function", &function); if (ret < 0 && ret != -EINVAL) {
dev_err(dev, "Invalid function in DT\n"); return ret;
}
ret = pinconf_generic_parse_dt_config(np, NULL, &configs, &num_configs); if (ret < 0) return ret;
if (!function && num_configs == 0) {
dev_err(dev, "DT node must contain at least a function or config\n");
ret = -ENODEV; goto done;
}
/* Count the number of pins and groups and reallocate mappings. */
ret = of_property_count_strings(np, "pins"); if (ret == -EINVAL) {
num_pins = 0;
} elseif (ret < 0) {
dev_err(dev, "Invalid pins list in DT\n"); goto done;
} else {
num_pins = ret;
}
ret = of_property_count_strings(np, "groups"); if (ret == -EINVAL) {
num_groups = 0;
} elseif (ret < 0) {
dev_err(dev, "Invalid pin groups list in DT\n"); goto done;
} else {
num_groups = ret;
}
if (!num_pins && !num_groups) {
dev_err(dev, "No pin or group provided in DT node\n");
ret = -ENODEV; goto done;
}
if (function)
nmaps += num_groups; if (configs)
nmaps += num_pins + num_groups;
maps = krealloc(maps, sizeof(*maps) * nmaps, GFP_KERNEL); if (maps == NULL) {
ret = -ENOMEM; goto done;
}
*map = maps;
*num_maps = nmaps;
/* Iterate over pins and groups and create the mappings. */
of_property_for_each_string(np, "groups", prop, group) { if (function) {
maps[idx].type = PIN_MAP_TYPE_MUX_GROUP;
maps[idx].data.mux.group = group;
maps[idx].data.mux.function = function;
idx++;
}
if (configs) {
ret = sh_pfc_map_add_config(&maps[idx], group,
PIN_MAP_TYPE_CONFIGS_GROUP,
configs, num_configs); if (ret < 0) goto done;
idx++;
}
}
if (!configs) {
ret = 0; goto done;
}
of_property_for_each_string(np, "pins", prop, pin) {
ret = sh_pfc_map_add_config(&maps[idx], pin,
PIN_MAP_TYPE_CONFIGS_PIN,
configs, num_configs); if (ret < 0) goto done;
for (i = 0; i < num_maps; ++i) { if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP ||
map[i].type == PIN_MAP_TYPE_CONFIGS_PIN)
kfree(map[i].data.configs.configs);
}
for_each_child_of_node_scoped(np, child) {
ret = sh_pfc_dt_subnode_to_map(pctldev, child, map, num_maps,
&index); if (ret < 0) goto done;
}
/* If no mapping has been found in child nodes try the config node. */ if (*num_maps == 0) {
ret = sh_pfc_dt_subnode_to_map(pctldev, np, map, num_maps,
&index); if (ret < 0) goto done;
}
if (*num_maps) return 0;
dev_err(dev, "no mapping found in node %pOF\n", np);
ret = -EINVAL;
done: if (ret < 0)
sh_pfc_dt_free_map(pctldev, *map, *num_maps);
dev_dbg(pctldev->dev, "Configuring pin group %s\n", grp->name);
spin_lock_irqsave(&pfc->lock, flags);
for (i = 0; i < grp->nr_pins; ++i) { int idx = sh_pfc_get_pin_index(pfc, grp->pins[i]); struct sh_pfc_pin_config *cfg = &pmx->configs[idx];
/* * This driver cannot manage both gpio and mux when the gpio * pin is already enabled. So, this function fails.
*/ if (cfg->gpio_enabled) {
ret = -EBUSY; goto done;
}
ret = sh_pfc_config_mux(pfc, grp->mux[i], PINMUX_TYPE_FUNCTION); if (ret < 0) goto done;
}
/* All group pins are configured, mark the pins as muxed */ for (i = 0; i < grp->nr_pins; ++i) { int idx = sh_pfc_get_pin_index(pfc, grp->pins[i]); struct sh_pfc_pin_config *cfg = &pmx->configs[idx];
if (!pfc->gpio && !cfg->mux_mark) { /* If GPIOs are handled externally the pin mux type needs to be * set to GPIO here.
*/ conststruct sh_pfc_pin *pin = &pfc->info->pins[idx];
ret = sh_pfc_config_mux(pfc, pin->enum_id, PINMUX_TYPE_GPIO); if (ret < 0) goto done;
}
spin_lock_irqsave(&pfc->lock, flags);
cfg->gpio_enabled = false; /* If mux is already set, this configures it here */ if (cfg->mux_mark)
sh_pfc_config_mux(pfc, cfg->mux_mark, PINMUX_TYPE_FUNCTION);
spin_unlock_irqrestore(&pfc->lock, flags);
}
/* Check if the requested direction is supported by the pin. Not all * SoCs provide pin config data, so perform the check conditionally.
*/ if (pin->configs) {
dir = input ? SH_PFC_PIN_CFG_INPUT : SH_PFC_PIN_CFG_OUTPUT; if (!(pin->configs & dir)) return -EINVAL;
}
/* Convert the value to mA based on a full drive strength value of 24mA. * We can make the full value configurable later if needed.
*/ return (val + 1) * (size == 2 ? 6 : 3);
}
reg = sh_pfc_pinconf_find_drive_strength_reg(pfc, pin, &offset, &size); if (!reg) return -EINVAL;
step = size == 2 ? 6 : 3;
if (strength < step || strength > 24) return -EINVAL;
/* Convert the value from mA based on a full drive strength value of * 24mA. We can make the full value configurable later if needed.
*/
strength = strength / step - 1;
spin_lock_irqsave(&pfc->lock, flags);
val = sh_pfc_read(pfc, reg);
val &= ~GENMASK(offset + size - 1, offset);
val |= strength << offset;
sh_pfc_write(pfc, reg, val);
spin_unlock_irqrestore(&pfc->lock, flags);
return 0;
}
/* Check whether the requested parameter is supported for a pin. */ staticbool sh_pfc_pinconf_validate(struct sh_pfc *pfc, unsignedint _pin, enum pin_config_param param)
{ int idx = sh_pfc_get_pin_index(pfc, _pin); conststruct sh_pfc_pin *pin = &pfc->info->pins[idx];
switch (param) { case PIN_CONFIG_BIAS_DISABLE: return pin->configs & SH_PFC_PIN_CFG_PULL_UP_DOWN;
case PIN_CONFIG_BIAS_PULL_UP: return pin->configs & SH_PFC_PIN_CFG_PULL_UP;
case PIN_CONFIG_BIAS_PULL_DOWN: return pin->configs & SH_PFC_PIN_CFG_PULL_DOWN;
case PIN_CONFIG_DRIVE_STRENGTH: return pin->configs & SH_PFC_PIN_CFG_DRIVE_STRENGTH;
case PIN_CONFIG_POWER_SOURCE: return pin->configs & SH_PFC_PIN_CFG_IO_VOLTAGE_MASK;
for (i = 0; i < num_configs; i++) {
param = pinconf_to_config_param(configs[i]);
if (!sh_pfc_pinconf_validate(pfc, _pin, param)) return -ENOTSUPP;
switch (param) { case PIN_CONFIG_BIAS_PULL_UP: case PIN_CONFIG_BIAS_PULL_DOWN: case PIN_CONFIG_BIAS_DISABLE: if (!pfc->info->ops || !pfc->info->ops->set_bias) return -ENOTSUPP;
/* Allocate and initialize the pins and configs arrays. */
pmx->pins = devm_kcalloc(pfc->dev,
pfc->info->nr_pins, sizeof(*pmx->pins),
GFP_KERNEL); if (unlikely(!pmx->pins)) return -ENOMEM;
pmx->configs = devm_kcalloc(pfc->dev,
pfc->info->nr_pins, sizeof(*pmx->configs),
GFP_KERNEL); if (unlikely(!pmx->configs)) return -ENOMEM;
for (i = 0; i < pfc->info->nr_pins; ++i) { conststruct sh_pfc_pin *info = &pfc->info->pins[i]; struct pinctrl_pin_desc *pin = &pmx->pins[i];
/* If the pin number is equal to -1 all pins are considered */
pin->number = info->pin != (u16)-1 ? info->pin : i;
pin->name = info->name;
}
return 0;
}
int sh_pfc_register_pinctrl(struct sh_pfc *pfc)
{ struct sh_pfc_pinctrl *pmx; int ret;
pmx = devm_kzalloc(pfc->dev, sizeof(*pmx), GFP_KERNEL); if (unlikely(!pmx)) return -ENOMEM;
pmx->pfc = pfc;
ret = sh_pfc_map_pins(pfc, pmx); if (ret < 0) return ret;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.