Merge branch 'ib-mux-pinctrl' into devel

This commit is contained in:
Linus Walleij
2026-05-11 22:33:30 +02:00
11 changed files with 370 additions and 57 deletions

View File

@@ -0,0 +1,57 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/pinctrl/pinctrl-multiplexer.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Generic pinctrl device for on-board MUX Chips
maintainers:
- Frank Li <Frank.Li@nxp.com>
description:
Generic pinctrl device for on-board MUX Chips, which switch SoC signals
between different peripherals (e.g. MMC and UART).
The MUX select lines are often driven by a I2C GPIO expander.
properties:
compatible:
const: pinctrl-multiplexer
patternProperties:
'-grp$':
type: object
additionalProperties: false
properties:
mux-states:
maxItems: 1
required:
- mux-states
required:
- compatible
allOf:
- $ref: pinctrl.yaml#
unevaluatedProperties: false
examples:
- |
pinctrl-mux {
compatible = "pinctrl-multiplexer";
uart-grp {
mux-states = <&mux 0>;
};
spi-grp {
mux-states = <&mux 1>;
};
i2c-grp {
mux-states = <&mux 2>;
};
};

View File

@@ -27,7 +27,7 @@ description: |
properties:
$nodename:
pattern: "^(pinctrl|pinmux)(@[0-9a-f]+)?$"
pattern: "^(pinctrl|pinmux)(@[0-9a-f]+|-[a-z0-9]+)?$"
"#pinctrl-cells":
description: >

View File

@@ -533,14 +533,16 @@ static struct mux_chip *of_find_mux_chip_by_node(struct device_node *np)
* @state: Pointer to where the requested state is returned, or NULL when
* the required multiplexer states are handled by other means.
* @optional: Whether to return NULL and silence errors when mux doesn't exist.
* @node: the device nodes, use dev->of_node if it is NULL.
*
* Return: Pointer to the mux-control on success, an ERR_PTR with a negative
* errno on error, or NULL if optional is true and mux doesn't exist.
*/
static struct mux_control *mux_get(struct device *dev, const char *mux_name,
unsigned int *state, bool optional)
unsigned int *state, bool optional,
struct device_node *node)
{
struct device_node *np = dev->of_node;
struct device_node *np = node ? node : dev->of_node;
struct of_phandle_args args;
struct mux_chip *mux_chip;
unsigned int controller;
@@ -635,7 +637,7 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name,
*/
struct mux_control *mux_control_get(struct device *dev, const char *mux_name)
{
struct mux_control *mux = mux_get(dev, mux_name, NULL, false);
struct mux_control *mux = mux_get(dev, mux_name, NULL, false, NULL);
if (!mux)
return ERR_PTR(-ENOENT);
@@ -654,7 +656,7 @@ EXPORT_SYMBOL_GPL(mux_control_get);
*/
struct mux_control *mux_control_get_optional(struct device *dev, const char *mux_name)
{
return mux_get(dev, mux_name, NULL, true);
return mux_get(dev, mux_name, NULL, true, NULL);
}
EXPORT_SYMBOL_GPL(mux_control_get_optional);
@@ -712,11 +714,14 @@ EXPORT_SYMBOL_GPL(devm_mux_control_get);
* @dev: The device that needs a mux-state.
* @mux_name: The name identifying the mux-state.
* @optional: Whether to return NULL and silence errors when mux doesn't exist.
* @np: the device nodes, use dev->of_node if it is NULL.
*
* Return: Pointer to the mux-state on success, an ERR_PTR with a negative
* errno on error, or NULL if optional is true and mux doesn't exist.
*/
static struct mux_state *mux_state_get(struct device *dev, const char *mux_name, bool optional)
static struct mux_state *
mux_state_get(struct device *dev, const char *mux_name, bool optional,
struct device_node *np)
{
struct mux_state *mstate;
@@ -724,7 +729,7 @@ static struct mux_state *mux_state_get(struct device *dev, const char *mux_name,
if (!mstate)
return ERR_PTR(-ENOMEM);
mstate->mux = mux_get(dev, mux_name, &mstate->state, optional);
mstate->mux = mux_get(dev, mux_name, &mstate->state, optional, np);
if (IS_ERR(mstate->mux)) {
int err = PTR_ERR(mstate->mux);
@@ -766,6 +771,7 @@ static void devm_mux_state_release(struct device *dev, void *res)
* @dev: The device that needs a mux-state.
* @mux_name: The name identifying the mux-state.
* @optional: Whether to return NULL and silence errors when mux doesn't exist.
* @np: The device nodes, use dev->of_node if it is NULL.
* @init: Optional function pointer for mux-state object initialisation.
* @exit: Optional function pointer for mux-state object cleanup on release.
*
@@ -773,7 +779,7 @@ static void devm_mux_state_release(struct device *dev, void *res)
* errno on error, or NULL if optional is true and mux doesn't exist.
*/
static struct mux_state *__devm_mux_state_get(struct device *dev, const char *mux_name,
bool optional,
bool optional, struct device_node *np,
int (*init)(struct mux_state *mstate),
int (*exit)(struct mux_state *mstate))
{
@@ -781,7 +787,7 @@ static struct mux_state *__devm_mux_state_get(struct device *dev, const char *mu
struct mux_state *mstate;
int ret;
mstate = mux_state_get(dev, mux_name, optional);
mstate = mux_state_get(dev, mux_name, optional, np);
if (IS_ERR(mstate))
return ERR_CAST(mstate);
else if (optional && !mstate)
@@ -815,20 +821,23 @@ static struct mux_state *__devm_mux_state_get(struct device *dev, const char *mu
}
/**
* devm_mux_state_get() - Get the mux-state for a device, with resource
* management.
* devm_mux_state_get_from_np() - Get the mux-state for a device, with resource
* management.
* @dev: The device that needs a mux-control.
* @mux_name: The name identifying the mux-control.
* @np: the device nodes, use dev->of_node if it is NULL.
*
* Return: Pointer to the mux-state, or an ERR_PTR with a negative errno.
*
* The mux-state will automatically be freed on release.
*/
struct mux_state *devm_mux_state_get(struct device *dev, const char *mux_name)
struct mux_state *
devm_mux_state_get_from_np(struct device *dev, const char *mux_name,
struct device_node *np)
{
return __devm_mux_state_get(dev, mux_name, false, NULL, NULL);
return __devm_mux_state_get(dev, mux_name, false, np, NULL, NULL);
}
EXPORT_SYMBOL_GPL(devm_mux_state_get);
EXPORT_SYMBOL_GPL(devm_mux_state_get_from_np);
/**
* devm_mux_state_get_optional() - Get the optional mux-state for a device,
@@ -843,7 +852,7 @@ EXPORT_SYMBOL_GPL(devm_mux_state_get);
*/
struct mux_state *devm_mux_state_get_optional(struct device *dev, const char *mux_name)
{
return __devm_mux_state_get(dev, mux_name, true, NULL, NULL);
return __devm_mux_state_get(dev, mux_name, true, NULL, NULL, NULL);
}
EXPORT_SYMBOL_GPL(devm_mux_state_get_optional);
@@ -861,7 +870,8 @@ EXPORT_SYMBOL_GPL(devm_mux_state_get_optional);
*/
struct mux_state *devm_mux_state_get_selected(struct device *dev, const char *mux_name)
{
return __devm_mux_state_get(dev, mux_name, false, mux_state_select, mux_state_deselect);
return __devm_mux_state_get(dev, mux_name, false, NULL,
mux_state_select, mux_state_deselect);
}
EXPORT_SYMBOL_GPL(devm_mux_state_get_selected);
@@ -881,7 +891,8 @@ EXPORT_SYMBOL_GPL(devm_mux_state_get_selected);
struct mux_state *devm_mux_state_get_optional_selected(struct device *dev,
const char *mux_name)
{
return __devm_mux_state_get(dev, mux_name, true, mux_state_select, mux_state_deselect);
return __devm_mux_state_get(dev, mux_name, true, NULL,
mux_state_select, mux_state_deselect);
}
EXPORT_SYMBOL_GPL(devm_mux_state_get_optional_selected);

View File

@@ -272,6 +272,15 @@ config PINCTRL_GEMINI
select GENERIC_PINCONF
select MFD_SYSCON
config PINCTRL_GENERIC_MUX
tristate "Generic Pinctrl driver by using multiplexer"
depends on MULTIPLEXER
select PINMUX
select GENERIC_PINCTRL
help
Generic pinctrl driver by MULTIPLEXER framework to control on
board pin selection.
config PINCTRL_INGENIC
bool "Pinctrl driver for the Ingenic JZ47xx SoCs"
default MACH_INGENIC

View File

@@ -29,6 +29,7 @@ obj-$(CONFIG_PINCTRL_EQUILIBRIUM) += pinctrl-equilibrium.o
obj-$(CONFIG_PINCTRL_EP93XX) += pinctrl-ep93xx.o
obj-$(CONFIG_PINCTRL_EYEQ5) += pinctrl-eyeq5.o
obj-$(CONFIG_PINCTRL_GEMINI) += pinctrl-gemini.o
obj-$(CONFIG_PINCTRL_GENERIC_MUX) += pinctrl-generic-mux.o
obj-$(CONFIG_PINCTRL_INGENIC) += pinctrl-ingenic.o
obj-$(CONFIG_PINCTRL_K210) += pinctrl-k210.o
obj-$(CONFIG_PINCTRL_K230) += pinctrl-k230.o

View File

@@ -172,6 +172,13 @@ 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);
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_name, unsigned int ngroups,
const char **functions, unsigned int *pins,
unsigned int npins);
#else
static inline int
pinctrl_generic_pins_function_dt_node_to_map(struct pinctrl_dev *pctldev,
@@ -181,4 +188,15 @@ pinctrl_generic_pins_function_dt_node_to_map(struct pinctrl_dev *pctldev,
{
return -ENOTSUPP;
}
static inline 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_name, unsigned int ngroups,
const char **functions, unsigned int *pins,
void *function_data)
{
return -ENOTSUPP;
}
#endif

View File

@@ -0,0 +1,184 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Generic Pin Control Driver for Board-Level Mux Chips
* Copyright 2026 NXP
*/
#include <linux/cleanup.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/mutex.h>
#include <linux/mux/consumer.h>
#include <linux/platform_device.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/slab.h>
#include "core.h"
#include "pinconf.h"
#include "pinmux.h"
#include "pinctrl-utils.h"
struct mux_pin_function {
struct mux_state *mux_state;
};
struct mux_pinctrl {
struct device *dev;
struct pinctrl_dev *pctl;
/* mutex protect [pinctrl|pinmux]_generic functions */
struct mutex lock;
};
static int
mux_pinmux_dt_node_to_map(struct pinctrl_dev *pctldev,
struct device_node *np_config,
struct pinctrl_map **maps, unsigned int *num_maps)
{
unsigned int num_reserved_maps = 0;
struct mux_pin_function *function;
const char **group_names;
int ret;
function = devm_kzalloc(pctldev->dev, sizeof(*function), GFP_KERNEL);
if (!function)
return -ENOMEM;
group_names = devm_kcalloc(pctldev->dev, 1, sizeof(*group_names), GFP_KERNEL);
if (!group_names)
return -ENOMEM;
function->mux_state = devm_mux_state_get_from_np(pctldev->dev, NULL, np_config);
if (IS_ERR(function->mux_state))
return PTR_ERR(function->mux_state);
ret = pinctrl_generic_to_map(pctldev, np_config, np_config, maps,
num_maps, &num_reserved_maps, group_names,
0, &np_config->name, NULL, 0);
if (ret)
return ret;
ret = pinmux_generic_add_function(pctldev, np_config->name, group_names,
1, function);
if (ret < 0) {
pinctrl_utils_free_map(pctldev, *maps, *num_maps);
return ret;
}
return 0;
}
static const struct pinctrl_ops mux_pinctrl_ops = {
.get_groups_count = pinctrl_generic_get_group_count,
.get_group_name = pinctrl_generic_get_group_name,
.get_group_pins = pinctrl_generic_get_group_pins,
.dt_node_to_map = mux_pinmux_dt_node_to_map,
.dt_free_map = pinctrl_utils_free_map,
};
static int mux_pinmux_set_mux(struct pinctrl_dev *pctldev,
unsigned int func_selector,
unsigned int group_selector)
{
struct mux_pinctrl *mpctl = pinctrl_dev_get_drvdata(pctldev);
const struct function_desc *function;
struct mux_pin_function *func;
int ret;
guard(mutex)(&mpctl->lock);
function = pinmux_generic_get_function(pctldev, func_selector);
func = function->data;
ret = mux_state_select(func->mux_state);
if (ret)
return ret;
return 0;
}
static void mux_pinmux_release_mux(struct pinctrl_dev *pctldev,
unsigned int func_selector,
unsigned int group_selector)
{
struct mux_pinctrl *mpctl = pinctrl_dev_get_drvdata(pctldev);
const struct function_desc *function;
struct mux_pin_function *func;
guard(mutex)(&mpctl->lock);
function = pinmux_generic_get_function(pctldev, func_selector);
func = function->data;
mux_state_deselect(func->mux_state);
}
static const struct pinmux_ops mux_pinmux_ops = {
.get_functions_count = pinmux_generic_get_function_count,
.get_function_name = pinmux_generic_get_function_name,
.get_function_groups = pinmux_generic_get_function_groups,
.set_mux = mux_pinmux_set_mux,
.release_mux = mux_pinmux_release_mux,
};
static int mux_pinctrl_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mux_pinctrl *mpctl;
struct pinctrl_desc *pctl_desc;
int ret;
mpctl = devm_kzalloc(dev, sizeof(*mpctl), GFP_KERNEL);
if (!mpctl)
return -ENOMEM;
mpctl->dev = dev;
platform_set_drvdata(pdev, mpctl);
pctl_desc = devm_kzalloc(dev, sizeof(*pctl_desc), GFP_KERNEL);
if (!pctl_desc)
return -ENOMEM;
ret = devm_mutex_init(dev, &mpctl->lock);
if (ret)
return ret;
pctl_desc->name = dev_name(dev);
pctl_desc->owner = THIS_MODULE;
pctl_desc->pctlops = &mux_pinctrl_ops;
pctl_desc->pmxops = &mux_pinmux_ops;
ret = devm_pinctrl_register_and_init(dev, pctl_desc, mpctl,
&mpctl->pctl);
if (ret)
return dev_err_probe(dev, ret, "Failed to register pinctrl.\n");
ret = pinctrl_enable(mpctl->pctl);
if (ret)
return dev_err_probe(dev, ret, "Failed to enable pinctrl.\n");
return 0;
}
static const struct of_device_id mux_pinctrl_of_match[] = {
{ .compatible = "pinctrl-multiplexer" },
{ }
};
MODULE_DEVICE_TABLE(of, mux_pinctrl_of_match);
static struct platform_driver mux_pinctrl_driver = {
.driver = {
.name = "generic-pinctrl-mux",
.of_match_table = mux_pinctrl_of_match,
},
.probe = mux_pinctrl_probe,
};
module_platform_driver(mux_pinctrl_driver);
MODULE_AUTHOR("Frank Li <Frank.Li@nxp.com>");
MODULE_DESCRIPTION("Generic Pin Control Driver for Board-Level Mux Chips");
MODULE_LICENSE("GPL");

View File

@@ -17,29 +17,18 @@
#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)
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,
const char **functions, unsigned int *pins,
unsigned int npins)
{
struct device *dev = pctldev->dev;
const char **functions;
unsigned int num_configs;
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;
}
int ret, reserve = 1;
group_name = devm_kasprintf(dev, GFP_KERNEL, "%pOFn.%pOFn", parent, np);
if (!group_name)
@@ -47,26 +36,6 @@ static int pinctrl_generic_pins_function_dt_subnode_to_map(struct pinctrl_dev *p
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;
@@ -102,6 +71,54 @@ static int pinctrl_generic_pins_function_dt_subnode_to_map(struct pinctrl_dev *p
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);
}
/*
* For platforms that do not define groups or functions in the driver, but

View File

@@ -517,6 +517,7 @@ void pinmux_disable_setting(const struct pinctrl_setting *setting)
{
struct pinctrl_dev *pctldev = setting->pctldev;
const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
const struct pinmux_ops *ops = pctldev->desc->pmxops;
int ret = 0;
const unsigned int *pins = NULL;
unsigned int num_pins = 0;
@@ -563,6 +564,10 @@ void pinmux_disable_setting(const struct pinctrl_setting *setting)
pins[i], desc->name, gname);
}
}
if (ops->release_mux)
ops->release_mux(pctldev, setting->data.mux.func,
setting->data.mux.group);
}
#ifdef CONFIG_DEBUG_FS

View File

@@ -60,7 +60,10 @@ struct mux_control *mux_control_get_optional(struct device *dev, const char *mux
void mux_control_put(struct mux_control *mux);
struct mux_control *devm_mux_control_get(struct device *dev, const char *mux_name);
struct mux_state *devm_mux_state_get(struct device *dev, const char *mux_name);
struct mux_state *
devm_mux_state_get_from_np(struct device *dev, const char *mux_name, struct device_node *np);
struct mux_state *devm_mux_state_get_optional(struct device *dev, const char *mux_name);
struct mux_state *devm_mux_state_get_selected(struct device *dev, const char *mux_name);
struct mux_state *devm_mux_state_get_optional_selected(struct device *dev, const char *mux_name);
@@ -161,4 +164,7 @@ static inline struct mux_state *devm_mux_state_get_optional_selected(struct devi
#endif /* CONFIG_MULTIPLEXER */
#define devm_mux_state_get(dev, mux_name) \
devm_mux_state_get_from_np(dev, mux_name, NULL)
#endif /* _LINUX_MUX_CONSUMER_H */

View File

@@ -51,6 +51,8 @@ struct pinctrl_gpio_range;
* are handled by the pinmux subsystem. The @func_selector selects a
* certain function whereas @group_selector selects a certain set of pins
* to be used. On simple controllers the latter argument may be ignored
* @release_mux: Release software resources acquired by @set_mux. This callback
* must not change hardware state to avoid glitches when switching mux.
* @gpio_request_enable: requests and enables GPIO on a certain pin.
* Implement this only if you can mux every pin individually as GPIO. The
* affected GPIO range is passed along with an offset(pin number) into that
@@ -80,6 +82,9 @@ struct pinmux_ops {
unsigned int selector);
int (*set_mux) (struct pinctrl_dev *pctldev, unsigned int func_selector,
unsigned int group_selector);
void (*release_mux) (struct pinctrl_dev *pctldev,
unsigned int func_selector,
unsigned int group_selector);
int (*gpio_request_enable) (struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
unsigned int offset);