| // SPDX-License-Identifier: GPL-2.0-only | |
| #define pr_fmt(fmt) "generic pinconfig core: " fmt | |
| #include <linux/array_size.h> | |
| #include <linux/device.h> | |
| #include <linux/module.h> | |
| #include <linux/of.h> | |
| #include <linux/slab.h> | |
| #include <linux/pinctrl/pinconf-generic.h> | |
| #include <linux/pinctrl/pinconf.h> | |
| #include <linux/pinctrl/pinctrl.h> | |
| #include "core.h" | |
| #include "pinconf.h" | |
| #include "pinctrl-utils.h" | |
| #include "pinmux.h" | |
| int pinctrl_generic_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, | |
| void *data, unsigned int *pins, unsigned int npins) | |
| { | |
| struct device *dev = pctldev->dev; | |
| unsigned int num_configs; | |
| const char *group_name; | |
| unsigned long *configs; | |
| int ret, reserve = 1; | |
| group_name = devm_kasprintf(dev, GFP_KERNEL, "%pOFn.%pOFn", parent, np); | |
| if (!group_name) | |
| return -ENOMEM; | |
| group_names[ngroups] = group_name; | |
| 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, data); | |
| 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; | |
| }; | |
| EXPORT_SYMBOL_GPL(pinctrl_generic_to_map); | |
| 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; | |
| unsigned int pin, *pins; | |
| const char **functions; | |
| int npins, ret; | |
| 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; | |
| } | |
| 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; | |
| } | |
| return pinctrl_generic_to_map(pctldev, parent, np, maps, num_maps, | |
| num_reserved_maps, group_names, ngroups, | |
| functions, pins, npins); | |
| } | |
| static int pinctrl_generic_pinmux_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; | |
| unsigned int *pins, *muxes; | |
| int npins, ret; | |
| npins = of_property_count_u32_elems(np, "pinmux"); | |
| if (npins < 1) { | |
| dev_err(dev, "invalid pinctrl group %pOFn.%pOFn %d\n", | |
| parent, np, npins); | |
| return npins; | |
| } | |
| pins = devm_kcalloc(dev, npins, sizeof(*pins), GFP_KERNEL); | |
| if (!pins) | |
| return -ENOMEM; | |
| muxes = devm_kcalloc(dev, npins, sizeof(*muxes), GFP_KERNEL); | |
| if (!muxes) | |
| return -ENOMEM; | |
| for (int i = 0; i < npins; i++) { | |
| unsigned int pinmux; | |
| ret = of_property_read_u32_index(np, "pinmux", i, &pinmux); | |
| if (ret) | |
| return ret; | |
| pins[i] = pinmux >> 16; | |
| muxes[i] = pinmux & GENMASK(15, 0); | |
| } | |
| return pinctrl_generic_to_map(pctldev, parent, np, maps, num_maps, | |
| num_reserved_maps, group_names, ngroups, | |
| muxes, pins, npins); | |
| } | |
| static int pinctrl_generic_dt_node_to_map(struct pinctrl_dev *pctldev, | |
| struct device_node *np, | |
| struct pinctrl_map **maps, | |
| unsigned int *num_maps, | |
| int (dt_subnode_to_map)( | |
| struct pinctrl_dev *, | |
| struct device_node *, | |
| struct device_node *, | |
| struct pinctrl_map **, | |
| unsigned int *, | |
| unsigned int *, | |
| const char **, | |
| unsigned int)) | |
| { | |
| 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 = 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 = 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; | |
| } | |
| /* | |
| * 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) | |
| { | |
| return pinctrl_generic_dt_node_to_map(pctldev, np, maps, num_maps, | |
| &pinctrl_generic_pins_function_dt_subnode_to_map); | |
| } | |
| EXPORT_SYMBOL_GPL(pinctrl_generic_pins_function_dt_node_to_map); | |
| /* | |
| * 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. | |
| * | |
| * It assumes that the upper 16 bits of the pinmux items contain the pin | |
| * and the lower 16 the mux setting. | |
| */ | |
| int pinctrl_generic_pinmux_dt_node_to_map(struct pinctrl_dev *pctldev, | |
| struct device_node *np, | |
| struct pinctrl_map **maps, | |
| unsigned int *num_maps) | |
| { | |
| return pinctrl_generic_dt_node_to_map(pctldev, np, maps, num_maps, | |
| &pinctrl_generic_pinmux_dt_subnode_to_map); | |
| }; | |
| EXPORT_SYMBOL_GPL(pinctrl_generic_pinmux_dt_node_to_map); |