Contributors: 8
Author Tokens Token Proportion Commits Commit Proportion
Conor Dooley 1236 91.02% 3 30.00%
Frank Li 73 5.38% 1 10.00%
Linus Walleij 18 1.33% 1 10.00%
Tony Lindgren 12 0.88% 1 10.00%
Heiko Stübner 10 0.74% 1 10.00%
Haojian Zhuang 4 0.29% 1 10.00%
Mika Westerberg 3 0.22% 1 10.00%
Sören Brinkmann 2 0.15% 1 10.00%
Total 1358 10


// 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);