// SPDX-License-Identifier: GPL-2.0-only #define pr_fmt(fmt) "generic pinconfig core: " fmt #include #include #include #include #include #include #include #include #include "core.h" #include "pinconf.h" #include "pinctrl-utils.h" #include "pinmux.h" static int pinctrl_generic_pins_function_dt_subnode_to_map(struct pinctrl_dev *pctldev, struct device_node *parent, struct device_node *np, struct pinctrl_map **maps, unsigned int *num_maps, unsigned int *num_reserved_maps, const char **group_names, unsigned int ngroups) { struct device *dev = pctldev->dev; const char **functions; const char *group_name; unsigned long *configs; unsigned int num_configs, pin, *pins; int npins, ret, reserve = 1; npins = of_property_count_u32_elems(np, "pins"); if (npins < 1) { dev_err(dev, "invalid pinctrl group %pOFn.%pOFn %d\n", parent, np, npins); return npins; } group_name = devm_kasprintf(dev, GFP_KERNEL, "%pOFn.%pOFn", parent, np); if (!group_name) return -ENOMEM; group_names[ngroups] = group_name; pins = devm_kcalloc(dev, npins, sizeof(*pins), GFP_KERNEL); if (!pins) return -ENOMEM; functions = devm_kcalloc(dev, npins, sizeof(*functions), GFP_KERNEL); if (!functions) return -ENOMEM; for (int i = 0; i < npins; i++) { ret = of_property_read_u32_index(np, "pins", i, &pin); if (ret) return ret; pins[i] = pin; ret = of_property_read_string(np, "function", &functions[i]); if (ret) return ret; } ret = pinctrl_utils_reserve_map(pctldev, maps, num_reserved_maps, num_maps, reserve); if (ret) return ret; ret = pinctrl_utils_add_map_mux(pctldev, maps, num_reserved_maps, num_maps, group_name, parent->name); if (ret < 0) return ret; ret = pinctrl_generic_add_group(pctldev, group_name, pins, npins, functions); if (ret < 0) return dev_err_probe(dev, ret, "failed to add group %s: %d\n", group_name, ret); ret = pinconf_generic_parse_dt_config(np, pctldev, &configs, &num_configs); if (ret) return dev_err_probe(dev, ret, "failed to parse pin config of group %s\n", group_name); if (num_configs == 0) return 0; ret = pinctrl_utils_reserve_map(pctldev, maps, num_reserved_maps, num_maps, reserve); if (ret) return ret; ret = pinctrl_utils_add_map_configs(pctldev, maps, num_reserved_maps, num_maps, group_name, configs, num_configs, PIN_MAP_TYPE_CONFIGS_GROUP); kfree(configs); if (ret) return ret; return 0; }; /* * For platforms that do not define groups or functions in the driver, but * instead use the devicetree to describe them. This function will, unlike * pinconf_generic_dt_node_to_map() etc which rely on driver defined groups * and functions, create them in addition to parsing pinconf properties and * adding mappings. */ int pinctrl_generic_pins_function_dt_node_to_map(struct pinctrl_dev *pctldev, struct device_node *np, struct pinctrl_map **maps, unsigned int *num_maps) { struct device *dev = pctldev->dev; struct device_node *child_np; const char **group_names; unsigned int num_reserved_maps = 0; int ngroups = 0; int ret; *maps = NULL; *num_maps = 0; /* * Check if this is actually the pins node, or a parent containing * multiple pins nodes. */ if (!of_property_present(np, "pins")) goto parent; group_names = devm_kcalloc(dev, 1, sizeof(*group_names), GFP_KERNEL); if (!group_names) return -ENOMEM; ret = pinctrl_generic_pins_function_dt_subnode_to_map(pctldev, np, np, maps, num_maps, &num_reserved_maps, group_names, ngroups); if (ret) { pinctrl_utils_free_map(pctldev, *maps, *num_maps); return dev_err_probe(dev, ret, "error figuring out mappings for %s\n", np->name); } ret = pinmux_generic_add_function(pctldev, np->name, group_names, 1, NULL); if (ret < 0) { pinctrl_utils_free_map(pctldev, *maps, *num_maps); return dev_err_probe(dev, ret, "error adding function %s\n", np->name); } return 0; parent: for_each_available_child_of_node(np, child_np) ngroups += 1; group_names = devm_kcalloc(dev, ngroups, sizeof(*group_names), GFP_KERNEL); if (!group_names) return -ENOMEM; ngroups = 0; for_each_available_child_of_node_scoped(np, child_np) { ret = pinctrl_generic_pins_function_dt_subnode_to_map(pctldev, np, child_np, maps, num_maps, &num_reserved_maps, group_names, ngroups); if (ret) { pinctrl_utils_free_map(pctldev, *maps, *num_maps); return dev_err_probe(dev, ret, "error figuring out mappings for %s\n", np->name); } ngroups++; } ret = pinmux_generic_add_function(pctldev, np->name, group_names, ngroups, NULL); if (ret < 0) { pinctrl_utils_free_map(pctldev, *maps, *num_maps); return dev_err_probe(dev, ret, "error adding function %s\n", np->name); } return 0; } EXPORT_SYMBOL_GPL(pinctrl_generic_pins_function_dt_node_to_map);