Files
Sarah Emery 51ae996594 pinctrl: generic: serialise pinctrl_generic_dt_node_to_map()
pinctrl_generic_add_group() documents that the caller must take care of
locking, and pinmux_generic_add_function() needs it too, but
pinctrl_generic_dt_node_to_map() calls them without holding
pctldev->mutex, and the core caller in create_pinctrl() does not take it
either.

The driver core calls pinctrl_bind_pins() before probing a device, so
two devices that reference the same pin controller can run
pinctrl_generic_dt_node_to_map() on one pctldev at the same time.

Both `add` functions take the new selector from pctldev->num_groups or
pctldev->num_functions, and radix_tree_insert() at that index.
Two racing callers can read the same selector before either
has inserted, so the second insert collides and fails:

  k1-pinctrl d401e000.pinctrl:
  error -EEXIST: error adding function pcie2-0-cfg
  k1-pinctrl d401e000.pinctrl:
  does not have pin group pcie0-0-cfg.pcie0-0-pins

leaving one consumer without its pin configuration.

This was hit on a SpacemiT K3 board, where PCIe devices probe in parallel
against the single shared pin controller.

Take pctldev->mutex across the whole function, so that the groups and the
function referring are in a single critical section.

Fixes: 43722575e5 ("pinctrl: add generic functions + pins mapper")
Signed-off-by: Sarah Emery <[email protected]>
Signed-off-by: Linus Walleij <[email protected]>
2026-09-14 10:59:33 +02:00

298 lines
8.1 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
#define pr_fmt(fmt) "generic pinconfig core: " fmt
#include <linux/array_size.h>
#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/mutex.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)
goto err_free_map;
ret = pinctrl_generic_add_group(pctldev, group_name, pins, npins, data);
if (ret < 0) {
dev_err_probe(dev, ret, "failed to add group %s: %d\n",
group_name, ret);
goto err_free_map;
}
ret = pinconf_generic_parse_dt_config(np, pctldev, &configs, &num_configs);
if (ret) {
dev_err_probe(dev, ret, "failed to parse pin config of group %s\n",
group_name);
goto err_free_map;
}
if (num_configs == 0)
return 0;
ret = pinctrl_utils_reserve_map(pctldev, maps, num_reserved_maps, num_maps, reserve);
if (ret)
goto err_free_map;
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)
goto err_free_map;
return 0;
err_free_map:
pinctrl_utils_free_map(pctldev, *maps, *num_maps);
*maps = NULL;
*num_maps = 0;
*num_reserved_maps = 0;
return ret;
};
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;
guard(mutex)(&pctldev->mutex);
*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);