mmc: Merge the immutable mux branch into next

The mux branch contains updates to the mux core along with some
corresponding changes for a couple of consumer drivers, including an mmc
driver. Let's merge it into the next branch to get it tested and queued for
the next release.

Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
master
Ulf Hansson 2026-03-09 13:45:42 +01:00
commit 824254f3c2
8 changed files with 307 additions and 92 deletions

View File

@ -106,6 +106,11 @@ properties:
iommus:
maxItems: 1
mux-states:
description:
mux controller node to route the SD/SDIO/eMMC signals from SoC to cards.
maxItems: 1
power-domains:
maxItems: 1
@ -275,6 +280,7 @@ examples:
max-frequency = <195000000>;
power-domains = <&sysc R8A7790_PD_ALWAYS_ON>;
resets = <&cpg 314>;
mux-states = <&mux 0>;
};
sdhi1: mmc@ee120000 {

View File

@ -1453,27 +1453,16 @@ omap_i2c_probe(struct platform_device *pdev)
(1000 * omap->speed / 8);
}
if (of_property_present(node, "mux-states")) {
struct mux_state *mux_state;
mux_state = devm_mux_state_get(&pdev->dev, NULL);
if (IS_ERR(mux_state)) {
r = PTR_ERR(mux_state);
dev_dbg(&pdev->dev, "failed to get I2C mux: %d\n", r);
goto err_put_pm;
}
omap->mux_state = mux_state;
r = mux_state_select(omap->mux_state);
if (r) {
dev_err(&pdev->dev, "failed to select I2C mux: %d\n", r);
goto err_put_pm;
}
omap->mux_state = devm_mux_state_get_optional_selected(&pdev->dev, NULL);
if (IS_ERR(omap->mux_state)) {
r = PTR_ERR(omap->mux_state);
goto err_put_pm;
}
/* reset ASAP, clearing any IRQs */
r = omap_i2c_init(omap);
if (r)
goto err_mux_state_deselect;
goto err_put_pm;
if (omap->rev < OMAP_I2C_OMAP1_REV_2)
r = devm_request_irq(&pdev->dev, omap->irq, omap_i2c_omap1_isr,
@ -1515,9 +1504,6 @@ omap_i2c_probe(struct platform_device *pdev)
err_unuse_clocks:
omap_i2c_write_reg(omap, OMAP_I2C_CON_REG, 0);
err_mux_state_deselect:
if (omap->mux_state)
mux_state_deselect(omap->mux_state);
err_put_pm:
pm_runtime_put_sync(omap->dev);
err_disable_pm:

View File

@ -26,6 +26,7 @@
#include <linux/mmc/mmc.h>
#include <linux/mmc/slot-gpio.h>
#include <linux/module.h>
#include <linux/mux/consumer.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/pinctrl-state.h>
#include <linux/platform_data/tmio.h>
@ -1062,6 +1063,7 @@ int renesas_sdhi_probe(struct platform_device *pdev,
struct regulator_dev *rdev;
struct renesas_sdhi_dma *dma_priv;
struct device *dev = &pdev->dev;
struct mux_state *mux_state;
struct tmio_mmc_host *host;
struct renesas_sdhi *priv;
int num_irqs, irq, ret, i;
@ -1116,6 +1118,10 @@ int renesas_sdhi_probe(struct platform_device *pdev,
"state_uhs");
}
mux_state = devm_mux_state_get_optional_selected(&pdev->dev, NULL);
if (IS_ERR(mux_state))
return PTR_ERR(mux_state);
host = tmio_mmc_host_alloc(pdev, mmc_data);
if (IS_ERR(host))
return PTR_ERR(host);

View File

@ -4,10 +4,21 @@
#
config MULTIPLEXER
tristate
bool
config MUX_CORE
bool "Generic Multiplexer Support"
select MULTIPLEXER
help
This framework is designed to abstract multiplexer handling for
devices via various GPIO-, MMIO/Regmap or specific multiplexer
controller chips.
If unsure, say no.
if MULTIPLEXER
menu "Multiplexer drivers"
depends on MULTIPLEXER
config MUX_ADG792A
tristate "Analog Devices ADG792A/ADG792G Multiplexers"
@ -60,3 +71,5 @@ config MUX_MMIO
be called mux-mmio.
endmenu
endif # MULTIPLEXER

View File

@ -46,6 +46,16 @@ static const struct class mux_class = {
.name = "mux",
};
/**
* struct devm_mux_state_state - Tracks managed resources for mux-state objects.
* @mstate: Pointer to a mux state.
* @exit: An optional callback to execute before free.
*/
struct devm_mux_state_state {
struct mux_state *mstate;
int (*exit)(struct mux_state *mstate);
};
static DEFINE_IDA(mux_ida);
static int __init mux_init(void)
@ -516,17 +526,19 @@ static struct mux_chip *of_find_mux_chip_by_node(struct device_node *np)
return dev ? to_mux_chip(dev) : NULL;
}
/*
/**
* mux_get() - Get the mux-control for a device.
* @dev: The device that needs a mux-control.
* @mux_name: The name identifying the mux-control.
* @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.
*
* Return: A pointer to the mux-control, or an ERR_PTR with a negative errno.
* 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)
unsigned int *state, bool optional)
{
struct device_node *np = dev->of_node;
struct of_phandle_args args;
@ -542,7 +554,9 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name,
else
index = of_property_match_string(np, "mux-control-names",
mux_name);
if (index < 0) {
if (index < 0 && optional) {
return NULL;
} else if (index < 0) {
dev_err(dev, "mux controller '%s' not found\n",
mux_name);
return ERR_PTR(index);
@ -558,8 +572,12 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name,
"mux-controls", "#mux-control-cells",
index, &args);
if (ret) {
if (optional && ret == -ENOENT)
return NULL;
dev_err(dev, "%pOF: failed to get mux-%s %s(%i)\n",
np, state ? "state" : "control", mux_name ?: "", index);
np, state ? "state" : "control",
mux_name ?: "", index);
return ERR_PTR(ret);
}
@ -617,10 +635,29 @@ 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)
{
return mux_get(dev, mux_name, NULL);
struct mux_control *mux = mux_get(dev, mux_name, NULL, false);
if (!mux)
return ERR_PTR(-ENOENT);
return mux;
}
EXPORT_SYMBOL_GPL(mux_control_get);
/**
* mux_control_get_optional() - Get the optional mux-control for a device.
* @dev: The device that needs a mux-control.
* @mux_name: The name identifying the mux-control.
*
* Return: Pointer to the mux-control on success, an ERR_PTR with a negative
* errno on error, or NULL if mux doesn't exist.
*/
struct mux_control *mux_control_get_optional(struct device *dev, const char *mux_name)
{
return mux_get(dev, mux_name, NULL, true);
}
EXPORT_SYMBOL_GPL(mux_control_get_optional);
/**
* mux_control_put() - Put away the mux-control for good.
* @mux: The mux-control to put away.
@ -670,14 +707,16 @@ struct mux_control *devm_mux_control_get(struct device *dev,
}
EXPORT_SYMBOL_GPL(devm_mux_control_get);
/*
/**
* mux_state_get() - Get the mux-state for a device.
* @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.
*
* Return: A pointer to the mux-state, or an ERR_PTR with a negative errno.
* 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)
static struct mux_state *mux_state_get(struct device *dev, const char *mux_name, bool optional)
{
struct mux_state *mstate;
@ -685,12 +724,15 @@ 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);
mstate->mux = mux_get(dev, mux_name, &mstate->state, optional);
if (IS_ERR(mstate->mux)) {
int err = PTR_ERR(mstate->mux);
kfree(mstate);
return ERR_PTR(err);
} else if (!mstate->mux) {
kfree(mstate);
return optional ? NULL : ERR_PTR(-ENOENT);
}
return mstate;
@ -710,9 +752,66 @@ static void mux_state_put(struct mux_state *mstate)
static void devm_mux_state_release(struct device *dev, void *res)
{
struct mux_state *mstate = *(struct mux_state **)res;
struct devm_mux_state_state *devm_state = res;
if (devm_state->exit)
devm_state->exit(devm_state->mstate);
mux_state_put(devm_state->mstate);
}
/**
* __devm_mux_state_get() - Get the optional mux-state for a device,
* with resource management.
* @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.
* @init: Optional function pointer for mux-state object initialisation.
* @exit: Optional function pointer for mux-state object cleanup on release.
*
* 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 *__devm_mux_state_get(struct device *dev, const char *mux_name,
bool optional,
int (*init)(struct mux_state *mstate),
int (*exit)(struct mux_state *mstate))
{
struct devm_mux_state_state *devm_state;
struct mux_state *mstate;
int ret;
mstate = mux_state_get(dev, mux_name, optional);
if (IS_ERR(mstate))
return ERR_CAST(mstate);
else if (optional && !mstate)
return NULL;
else if (!mstate)
return ERR_PTR(-ENOENT);
devm_state = devres_alloc(devm_mux_state_release, sizeof(*devm_state), GFP_KERNEL);
if (!devm_state) {
ret = -ENOMEM;
goto err_devres_alloc;
}
if (init) {
ret = init(mstate);
if (ret)
goto err_mux_state_init;
}
devm_state->mstate = mstate;
devm_state->exit = exit;
devres_add(dev, devm_state);
return mstate;
err_mux_state_init:
devres_free(devm_state);
err_devres_alloc:
mux_state_put(mstate);
return ERR_PTR(ret);
}
/**
@ -722,29 +821,70 @@ static void devm_mux_state_release(struct device *dev, void *res)
* @mux_name: The name identifying the mux-control.
*
* 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(struct device *dev, const char *mux_name)
{
struct mux_state **ptr, *mstate;
ptr = devres_alloc(devm_mux_state_release, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
mstate = mux_state_get(dev, mux_name);
if (IS_ERR(mstate)) {
devres_free(ptr);
return mstate;
}
*ptr = mstate;
devres_add(dev, ptr);
return mstate;
return __devm_mux_state_get(dev, mux_name, false, NULL, NULL);
}
EXPORT_SYMBOL_GPL(devm_mux_state_get);
/**
* devm_mux_state_get_optional() - Get the optional mux-state for a device,
* with resource management.
* @dev: The device that needs a mux-state.
* @mux_name: The name identifying the mux-state.
*
* Return: Pointer to the mux-state on success, an ERR_PTR with a negative
* errno on error, or NULL if mux doesn't exist.
*
* The mux-state will automatically be freed on release.
*/
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);
}
EXPORT_SYMBOL_GPL(devm_mux_state_get_optional);
/**
* devm_mux_state_get_selected() - Get the mux-state for a device, with
* resource management.
* @dev: The device that needs a mux-state.
* @mux_name: The name identifying the mux-state.
*
* Return: Pointer to the mux-state, or an ERR_PTR with a negative errno.
*
* The returned mux-state (if valid) is already selected.
*
* The mux-state will automatically be deselected and freed on release.
*/
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);
}
EXPORT_SYMBOL_GPL(devm_mux_state_get_selected);
/**
* devm_mux_state_get_optional_selected() - Get the optional mux-state for
* a device, with resource management.
* @dev: The device that needs a mux-state.
* @mux_name: The name identifying the mux-state.
*
* Return: Pointer to the mux-state on success, an ERR_PTR with a negative
* errno on error, or NULL if mux doesn't exist.
*
* The returned mux-state (if valid) is already selected.
*
* The mux-state will automatically be deselected and freed on release.
*/
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);
}
EXPORT_SYMBOL_GPL(devm_mux_state_get_optional_selected);
/*
* Using subsys_initcall instead of module_init here to try to ensure - for
* the non-modular case - that the subsystem is initialized when mux consumers

View File

@ -126,16 +126,6 @@ static const struct of_device_id can_transceiver_phy_ids[] = {
};
MODULE_DEVICE_TABLE(of, can_transceiver_phy_ids);
/* Temporary wrapper until the multiplexer subsystem supports optional muxes */
static inline struct mux_state *
devm_mux_state_get_optional(struct device *dev, const char *mux_name)
{
if (!of_property_present(dev->of_node, "mux-states"))
return NULL;
return devm_mux_state_get(dev, mux_name);
}
static struct phy *can_transceiver_phy_xlate(struct device *dev,
const struct of_phandle_args *args)
{

View File

@ -939,21 +939,6 @@ static int rcar_gen3_phy_usb2_vbus_regulator_register(struct rcar_gen3_chan *cha
return rcar_gen3_phy_usb2_vbus_regulator_get_exclusive_enable(channel, enable);
}
/* Temporary wrapper until the multiplexer subsystem supports optional muxes */
static inline struct mux_state *
devm_mux_state_get_optional(struct device *dev, const char *mux_name)
{
if (!of_property_present(dev->of_node, "mux-states"))
return NULL;
return devm_mux_state_get(dev, mux_name);
}
static void rcar_gen3_phy_mux_state_deselect(void *data)
{
mux_state_deselect(data);
}
static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@ -1036,20 +1021,9 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
phy_set_drvdata(channel->rphys[i].phy, &channel->rphys[i]);
}
mux_state = devm_mux_state_get_optional(dev, NULL);
mux_state = devm_mux_state_get_optional_selected(dev, NULL);
if (IS_ERR(mux_state))
return PTR_ERR(mux_state);
if (mux_state) {
ret = mux_state_select(mux_state);
if (ret)
return dev_err_probe(dev, ret, "Failed to select USB mux\n");
ret = devm_add_action_or_reset(dev, rcar_gen3_phy_mux_state_deselect,
mux_state);
if (ret)
return dev_err_probe(dev, ret,
"Failed to register USB mux state deselect\n");
}
return dev_err_probe(dev, PTR_ERR(mux_state), "Failed to get USB mux\n");
if (channel->phy_data->no_adp_ctrl && channel->is_otg_channel) {
ret = rcar_gen3_phy_usb2_vbus_regulator_register(channel);

View File

@ -16,6 +16,8 @@ struct device;
struct mux_control;
struct mux_state;
#if IS_ENABLED(CONFIG_MULTIPLEXER)
unsigned int mux_control_states(struct mux_control *mux);
int __must_check mux_control_select_delay(struct mux_control *mux,
unsigned int state,
@ -54,11 +56,109 @@ int mux_control_deselect(struct mux_control *mux);
int mux_state_deselect(struct mux_state *mstate);
struct mux_control *mux_control_get(struct device *dev, const char *mux_name);
struct mux_control *mux_control_get_optional(struct device *dev, const char *mux_name);
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_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_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);
#else
static inline unsigned int mux_control_states(struct mux_control *mux)
{
return 0;
}
static inline int __must_check mux_control_select_delay(struct mux_control *mux,
unsigned int state, unsigned int delay_us)
{
return -EOPNOTSUPP;
}
static inline int __must_check mux_state_select_delay(struct mux_state *mstate,
unsigned int delay_us)
{
return -EOPNOTSUPP;
}
static inline int __must_check mux_control_try_select_delay(struct mux_control *mux,
unsigned int state,
unsigned int delay_us)
{
return -EOPNOTSUPP;
}
static inline int __must_check mux_state_try_select_delay(struct mux_state *mstate,
unsigned int delay_us)
{
return -EOPNOTSUPP;
}
static inline int __must_check mux_control_select(struct mux_control *mux,
unsigned int state)
{
return -EOPNOTSUPP;
}
static inline int __must_check mux_state_select(struct mux_state *mstate)
{
return -EOPNOTSUPP;
}
static inline int __must_check mux_control_try_select(struct mux_control *mux,
unsigned int state)
{
return -EOPNOTSUPP;
}
static inline int __must_check mux_state_try_select(struct mux_state *mstate)
{
return -EOPNOTSUPP;
}
static inline int mux_control_deselect(struct mux_control *mux)
{
return -EOPNOTSUPP;
}
static inline int mux_state_deselect(struct mux_state *mstate)
{
return -EOPNOTSUPP;
}
static inline struct mux_control *mux_control_get(struct device *dev, const char *mux_name)
{
return ERR_PTR(-EOPNOTSUPP);
}
static inline struct mux_control *mux_control_get_optional(struct device *dev,
const char *mux_name)
{
return NULL;
}
static inline void mux_control_put(struct mux_control *mux) {}
static inline struct mux_control *devm_mux_control_get(struct device *dev, const char *mux_name)
{
return ERR_PTR(-EOPNOTSUPP);
}
static inline struct mux_state *devm_mux_state_get(struct device *dev, const char *mux_name)
{
return ERR_PTR(-EOPNOTSUPP);
}
static inline struct mux_state *devm_mux_state_get_optional(struct device *dev,
const char *mux_name)
{
return NULL;
}
static inline struct mux_state *devm_mux_state_get_selected(struct device *dev,
const char *mux_name)
{
return ERR_PTR(-EOPNOTSUPP);
}
static inline struct mux_state *devm_mux_state_get_optional_selected(struct device *dev,
const char *mux_name)
{
return NULL;
}
#endif /* CONFIG_MULTIPLEXER */
#endif /* _LINUX_MUX_CONSUMER_H */