From patchwork Thu Jun 11 19:45:01 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pratyush Yadav X-Patchwork-Id: 242147 List-Id: U-Boot discussion From: p.yadav at ti.com (Pratyush Yadav) Date: Fri, 12 Jun 2020 01:15:01 +0530 Subject: [PATCH v3 1/6] drivers: Add a new framework for multiplexer devices In-Reply-To: <20200611194506.7263-1-p.yadav@ti.com> References: <20200611194506.7263-1-p.yadav@ti.com> Message-ID: <20200611194506.7263-2-p.yadav@ti.com> From: Jean-Jacques Hiblot Add a new subsystem that handles multiplexer controllers. The API is the same as in Linux. Signed-off-by: Jean-Jacques Hiblot Signed-off-by: Pratyush Yadav Reviewed-by: Simon Glass --- drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/mux/Kconfig | 11 ++ drivers/mux/Makefile | 6 + drivers/mux/mux-uclass.c | 311 ++++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/dt-bindings/mux/mux.h | 17 ++ include/mux-internal.h | 109 ++++++++++++ include/mux.h | 147 ++++++++++++++++ 9 files changed, 605 insertions(+) create mode 100644 drivers/mux/Kconfig create mode 100644 drivers/mux/Makefile create mode 100644 drivers/mux/mux-uclass.c create mode 100644 include/dt-bindings/mux/mux.h create mode 100644 include/mux-internal.h create mode 100644 include/mux.h diff --git a/drivers/Kconfig b/drivers/Kconfig index e34a22708c..03a2e5f5ef 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -60,6 +60,8 @@ source "drivers/mmc/Kconfig" source "drivers/mtd/Kconfig" +source "drivers/mux/Kconfig" + source "drivers/net/Kconfig" source "drivers/nvme/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 94e8c5da17..76d8500bee 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_$(SPL_TPL_)INPUT) += input/ obj-$(CONFIG_$(SPL_TPL_)LED) += led/ obj-$(CONFIG_$(SPL_TPL_)MMC_SUPPORT) += mmc/ obj-y += mtd/ +obj-$(CONFIG_$(SPL_)MULTIPLEXER) += mux/ obj-$(CONFIG_$(SPL_TPL_)PCH_SUPPORT) += pch/ obj-$(CONFIG_$(SPL_TPL_)PCI) += pci/ obj-$(CONFIG_$(SPL_TPL_)PHY) += phy/ diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig new file mode 100644 index 0000000000..35c1c5673c --- /dev/null +++ b/drivers/mux/Kconfig @@ -0,0 +1,11 @@ +menu "Multiplexer drivers" + +config MULTIPLEXER + bool "Multiplexer Support" + depends on DM + help + The mux framework is a minimalistic subsystem that handles multiplexer + controllers. It provides the same API as Linux and mux drivers should + be portable with a minimum effort. + +endmenu diff --git a/drivers/mux/Makefile b/drivers/mux/Makefile new file mode 100644 index 0000000000..351e4363d3 --- /dev/null +++ b/drivers/mux/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2019 +# Jean-Jacques Hiblot + +obj-$(CONFIG_$(SPL_)MULTIPLEXER) += mux-uclass.o diff --git a/drivers/mux/mux-uclass.c b/drivers/mux/mux-uclass.c new file mode 100644 index 0000000000..3c89e0a389 --- /dev/null +++ b/drivers/mux/mux-uclass.c @@ -0,0 +1,311 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Multiplexer subsystem + * + * Based on the linux multiplexer framework + * + * Copyright (C) 2017 Axentia Technologies AB + * Author: Peter Rosin + * + * Copyright (C) 2017-2018 Texas Instruments Incorporated - http://www.ti.com/ + * Jean-Jacques Hiblot + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * The idle-as-is "state" is not an actual state that may be selected, it + * only implies that the state should not be changed. So, use that state + * as indication that the cached state of the multiplexer is unknown. + */ +#define MUX_CACHE_UNKNOWN MUX_IDLE_AS_IS + +/** + * mux_control_ops() - Get the mux_control ops. + * @dev: The client device. + * + * Return: A pointer to the 'mux_control_ops' of the device. + */ +static inline const struct mux_control_ops *mux_dev_ops(struct udevice *dev) +{ + return (const struct mux_control_ops *)dev->driver->ops; +} + +/** + * mux_control_set() - Set the state of the given mux controller. + * @mux: A multiplexer control + * @state: The new requested state. + * + * Return: 0 if OK, or a negative error code. + */ +static int mux_control_set(struct mux_control *mux, int state) +{ + int ret = mux_dev_ops(mux->dev)->set(mux, state); + + mux->cached_state = ret < 0 ? MUX_CACHE_UNKNOWN : state; + + return ret; +} + +unsigned int mux_control_states(struct mux_control *mux) +{ + return mux->states; +} + +/** + * __mux_control_select() - Select the given multiplexer state. + * @mux: The mux-control to request a change of state from. + * @state: The new requested state. + * + * Try to set the mux to the requested state. If not, try to revert if + * appropriate. + */ +static int __mux_control_select(struct mux_control *mux, int state) +{ + int ret; + + if (WARN_ON(state < 0 || state >= mux->states)) + return -EINVAL; + + if (mux->cached_state == state) + return 0; + + ret = mux_control_set(mux, state); + if (ret >= 0) + return 0; + + /* The mux update failed, try to revert if appropriate... */ + if (mux->idle_state != MUX_IDLE_AS_IS) + mux_control_set(mux, mux->idle_state); + + return ret; +} + +int mux_control_select(struct mux_control *mux, unsigned int state) +{ + int ret; + + if (mux->in_use) + return -EBUSY; + + ret = __mux_control_select(mux, state); + + if (ret < 0) + return ret; + + mux->in_use = true; + + return 0; +} + +int mux_control_deselect(struct mux_control *mux) +{ + int ret = 0; + + if (mux->idle_state != MUX_IDLE_AS_IS && + mux->idle_state != mux->cached_state) + ret = mux_control_set(mux, mux->idle_state); + + mux->in_use = false; + + return ret; +} + +static int mux_of_xlate_default(struct mux_chip *mux_chip, + struct ofnode_phandle_args *args, + struct mux_control **muxp) +{ + struct mux_control *mux; + int id; + + log_debug("%s(muxp=%p)\n", __func__, muxp); + + if (args->args_count > 1) { + debug("Invaild args_count: %d\n", args->args_count); + return -EINVAL; + } + + if (args->args_count) + id = args->args[0]; + else + id = 0; + + if (id >= mux_chip->controllers) { + dev_err(dev, "bad mux controller %u specified in %s\n", + id, ofnode_get_name(args->node)); + return -ERANGE; + } + + mux = &mux_chip->mux[id]; + mux->id = id; + *muxp = mux; + return 0; +} + +/** + * mux_get_by_indexed_prop() - Get a mux control by integer index + * @dev: The client device. + * @prop_name: Name of the device tree property. + * @index: The index of the mux to get + * @mux: A pointer to the 'mux_control' struct to initialize. + * + * Return: 0 of OK, -errno otherwise. + */ +static int mux_get_by_indexed_prop(struct udevice *dev, const char *prop_name, + int index, struct mux_control **mux) +{ + int ret; + struct ofnode_phandle_args args; + struct udevice *dev_mux; + const struct mux_control_ops *ops; + struct mux_chip *mux_chip; + + log_debug("%s(dev=%p, index=%d, mux=%p)\n", __func__, dev, index, mux); + + ret = dev_read_phandle_with_args(dev, prop_name, "#mux-control-cells", + 0, index, &args); + if (ret) { + debug("%s: fdtdec_parse_phandle_with_args failed: err=%d\n", + __func__, ret); + return ret; + } + + ret = uclass_get_device_by_ofnode(UCLASS_MUX, args.node, &dev_mux); + if (ret) { + debug("%s: uclass_get_device_by_ofnode failed: err=%d\n", + __func__, ret); + return ret; + } + + mux_chip = dev_get_uclass_priv(dev_mux); + + ops = mux_dev_ops(dev_mux); + if (ops->of_xlate) + ret = ops->of_xlate(mux_chip, &args, mux); + else + ret = mux_of_xlate_default(mux_chip, &args, mux); + if (ret) { + debug("of_xlate() failed: %d\n", ret); + return ret; + } + (*mux)->dev = dev_mux; + + return 0; +} + +int mux_get_by_index(struct udevice *dev, int index, struct mux_control **mux) +{ + return mux_get_by_indexed_prop(dev, "mux-controls", index, mux); +} + +int mux_control_get(struct udevice *dev, const char *name, + struct mux_control **mux) +{ + int index; + + debug("%s(dev=%p, name=%s, mux=%p)\n", __func__, dev, name, mux); + + index = dev_read_stringlist_search(dev, "mux-control-names", name); + if (index < 0) { + debug("fdt_stringlist_search() failed: %d\n", index); + return index; + } + + return mux_get_by_index(dev, index, mux); +} + +void mux_control_put(struct mux_control *mux) +{ + mux_control_deselect(mux); +} + +/** + * devm_mux_control_release() - Release the given managed mux. + * @dev: The client device. + * @res: Pointer to the mux to be released. + * + * This function is called by devres to release the mux. It reverses the + * effects of mux_control_get(). + */ +static void devm_mux_control_release(struct udevice *dev, void *res) +{ + mux_control_put(*(struct mux_control **)res); +} + +struct mux_control *devm_mux_control_get(struct udevice *dev, const char *id) +{ + int rc; + struct mux_control **mux; + + mux = devres_alloc(devm_mux_control_release, + sizeof(struct mux_control *), __GFP_ZERO); + if (unlikely(!mux)) + return ERR_PTR(-ENOMEM); + + rc = mux_control_get(dev, id, mux); + if (rc) + return ERR_PTR(rc); + + devres_add(dev, mux); + return *mux; +} + +int mux_alloc_controllers(struct udevice *dev, unsigned int controllers) +{ + int i; + struct mux_chip *mux_chip = dev_get_uclass_priv(dev); + + mux_chip->mux = devm_kmalloc(dev, + sizeof(struct mux_control) * controllers, + __GFP_ZERO); + if (!mux_chip->mux) + return -ENOMEM; + + mux_chip->controllers = controllers; + + for (i = 0; i < mux_chip->controllers; ++i) { + struct mux_control *mux = &mux_chip->mux[i]; + + mux->dev = dev; + mux->cached_state = MUX_CACHE_UNKNOWN; + mux->idle_state = MUX_IDLE_AS_IS; + mux->in_use = false; + mux->id = i; + } + + return 0; +} + +static int mux_uclass_post_probe(struct udevice *dev) +{ + int i, ret; + struct mux_chip *mux_chip = dev_get_uclass_priv(dev); + + /* Set all mux controllers to their idle state. */ + for (i = 0; i < mux_chip->controllers; ++i) { + struct mux_control *mux = &mux_chip->mux[i]; + + if (mux->idle_state == mux->cached_state) + continue; + + ret = mux_control_set(mux, mux->idle_state); + if (ret < 0) { + dev_err(&mux_chip->dev, "unable to set idle state\n"); + return ret; + } + } + return 0; +} + +UCLASS_DRIVER(mux) = { + .id = UCLASS_MUX, + .name = "mux", + .post_probe = mux_uclass_post_probe, + .per_device_auto_alloc_size = sizeof(struct mux_chip), +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 7837d459f1..066d7d51ff 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -70,6 +70,7 @@ enum uclass_id { UCLASS_MMC, /* SD / MMC card or chip */ UCLASS_MOD_EXP, /* RSA Mod Exp device */ UCLASS_MTD, /* Memory Technology Device (MTD) device */ + UCLASS_MUX, /* Multiplexer device */ UCLASS_NOP, /* No-op devices */ UCLASS_NORTHBRIDGE, /* Intel Northbridge / SDRAM controller */ UCLASS_NVME, /* NVM Express device */ diff --git a/include/dt-bindings/mux/mux.h b/include/dt-bindings/mux/mux.h new file mode 100644 index 0000000000..042719218d --- /dev/null +++ b/include/dt-bindings/mux/mux.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This header provides constants for most Multiplexer bindings. + * + * Most Multiplexer bindings specify an idle state. In most cases, the + * the multiplexer can be left as is when idle, and in some cases it can + * disconnect the input/output and leave the multiplexer in a high + * impedance state. + */ + +#ifndef _DT_BINDINGS_MUX_MUX_H +#define _DT_BINDINGS_MUX_MUX_H + +#define MUX_IDLE_AS_IS (-1) +#define MUX_IDLE_DISCONNECT (-2) + +#endif diff --git a/include/mux-internal.h b/include/mux-internal.h new file mode 100644 index 0000000000..93e3a5cdd7 --- /dev/null +++ b/include/mux-internal.h @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Based on the linux multiplexer framework + * + * Copyright (C) 2017 Axentia Technologies AB + * Author: Peter Rosin + * + * Copyright (C) 2017-2018 Texas Instruments Incorporated - http://www.ti.com/ + * Jean-Jacques Hiblot + */ + +#ifndef _MUX_INTERNAL_H +#define _MUX_INTERNAL_H + +/* See mux.h for background documentation. */ + +struct ofnode_phandle_args; + +/** + * struct mux_chip - Represents a chip holding mux controllers. + * @controllers: Number of mux controllers handled by the chip. + * @mux: Array of mux controllers that are handled. + * + * This a per-device uclass-private data. + */ +struct mux_chip { + unsigned int controllers; + struct mux_control *mux; +}; + +/** + * struct mux_control_ops - Mux controller operations for a mux chip. + * @set: Set the state of the given mux controller. + */ +struct mux_control_ops { + /** + * set - Apply a state to a multiplexer control + * + * @mux: A multiplexer control + * @return 0 if OK, or a negative error code. + */ + int (*set)(struct mux_control *mux, int state); + + /** + * of_xlate - Translate a client's device-tree (OF) multiplexer + * specifier. + * + * If this function pointer is set to NULL, the multiplexer core will + * use a default implementation, which assumes #mux-control-cells = <1> + * and that the DT cell contains a simple integer channel ID. + * + * @dev_mux: The multiplexer device. A single device may handle + * several multiplexer controls. + * @args: The multiplexer specifier values from device tree. + * @muxp: (out) A multiplexer control + * @return 0 if OK, or a negative error code. + */ + int (*of_xlate)(struct mux_chip *dev_mux, + struct ofnode_phandle_args *args, + struct mux_control **muxp); +}; + +/** + * struct mux_control - Represents a mux controller. + * @in_use: Whether the mux controller is in use or not. + * @dev: The client device. + * @cached_state: The current mux controller state, or -1 if none. + * @states: The number of mux controller states. + * @idle_state: The mux controller state to use when inactive, or one + * of MUX_IDLE_AS_IS and MUX_IDLE_DISCONNECT. + * @id: The index of the mux controller within the mux chip + * it is a part of. + * + * Mux drivers may only change @states and @idle_state, and may only do so + * between allocation and registration of the mux controller. Specifically, + * @cached_state is internal to the mux core and should never be written by + * mux drivers. + */ +struct mux_control { + bool in_use; + struct udevice *dev; + int cached_state; + unsigned int states; + int idle_state; + int id; +}; + +/** + * mux_control_get_index() - Get the index of the given mux controller + * @mux: The mux-control to get the index for. + * + * Return: The index of the mux controller within the mux chip the mux + * controller is a part of. + */ +static inline unsigned int mux_control_get_index(struct mux_control *mux) +{ + return mux->id; +} + +/** + * mux_alloc_controllers() - Allocate the given number of mux controllers. + * @dev: The client device. + * controllers: Number of controllers to allocate. + * + * Return: 0 of OK, -errno otherwise. + */ +int mux_alloc_controllers(struct udevice *dev, unsigned int controllers); + +#endif diff --git a/include/mux.h b/include/mux.h new file mode 100644 index 0000000000..85eb7d42fc --- /dev/null +++ b/include/mux.h @@ -0,0 +1,147 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Based on the linux multiplexer framework + * + * At its core, a multiplexer (or mux), also known as a data selector, is a + * device that selects between several analog or digital input signals and + * forwards it to a single output line. This notion can be extended to work + * with buses, like a I2C bus multiplexer for example. + * + * Copyright (C) 2017 Axentia Technologies AB + * Author: Peter Rosin + * + * Copyright (C) 2017-2018 Texas Instruments Incorporated - http://www.ti.com/ + * Jean-Jacques Hiblot + */ + +#ifndef _MUX_H_ +#define _MUX_H_ + +#include +#include + +struct udevice; +struct mux_control; + +#if CONFIG_IS_ENABLED(MULTIPLEXER) +/** + * mux_control_states() - Query the number of multiplexer states. + * @mux: The mux-control to query. + * + * Return: The number of multiplexer states. + */ +unsigned int mux_control_states(struct mux_control *mux); + +/** + * mux_control_select() - Select the given multiplexer state. + * @mux: The mux-control to request a change of state from. + * @state: The new requested state. + * + * On successfully selecting the mux-control state, it will be locked until + * there is a call to mux_control_deselect(). If the mux-control is already + * selected when mux_control_select() is called, the function will indicate + * -EBUSY + * + * Therefore, make sure to call mux_control_deselect() when the operation is + * complete and the mux-control is free for others to use, but do not call + * mux_control_deselect() if mux_control_select() fails. + * + * Return: 0 when the mux-control state has the requested state or a negative + * errno on error. + */ +int __must_check mux_control_select(struct mux_control *mux, + unsigned int state); +#define mux_control_try_select(mux) mux_control_select(mux) + +/** + * mux_control_deselect() - Deselect the previously selected multiplexer state. + * @mux: The mux-control to deselect. + * + * It is required that a single call is made to mux_control_deselect() for + * each and every successful call made to either of mux_control_select() or + * mux_control_try_select(). + * + * Return: 0 on success and a negative errno on error. An error can only + * occur if the mux has an idle state. Note that even if an error occurs, the + * mux-control is unlocked and is thus free for the next access. + */ +int mux_control_deselect(struct mux_control *mux); + +/** + * mux_get_by_index() = Get a mux by integer index. + * @dev: The client device. + * @index: The index of the mux to get. + * @mux: A pointer to the 'mux_control' struct to initialize. + * + * This looks up and initializes a mux. The index is relative to the client + * device. + * + * Return: 0 if OK, or a negative error code. + */ +int mux_get_by_index(struct udevice *dev, int index, struct mux_control **mux); + +/** + * mux_control_get() - Get the mux-control for a device. + * @dev: The device that needs a mux-control. + * @mux_name: The name identifying the mux-control. + * @mux: A pointer to the mux-control pointer. + * + * Return: 0 of OK, or a negative error code. + */ +int mux_control_get(struct udevice *dev, const char *name, + struct mux_control **mux); + +/** + * mux_control_put() - Put away the mux-control for good. + * @mux: The mux-control to put away. + * + * mux_control_put() reverses the effects of mux_control_get(). + */ +void mux_control_put(struct mux_control *mux); + +/** + * devm_mux_control_get() - Get the mux-control for a device, with resource + * management. + * @dev: The device that needs a mux-control. + * @mux_name: The name identifying the mux-control. + * + * Return: Pointer to the mux-control, or an ERR_PTR with a negative errno. + */ +struct mux_control *devm_mux_control_get(struct udevice *dev, + const char *mux_name); +#else +unsigned int mux_control_states(struct mux_control *mux) +{ + return -ENOSYS; +} + +int __must_check mux_control_select(struct mux_control *mux, + unsigned int state) +{ + return -ENOSYS; +} + +#define mux_control_try_select(mux) mux_control_select(mux) + +int mux_control_deselect(struct mux_control *mux) +{ + return -ENOSYS; +} + +struct mux_control *mux_control_get(struct udevice *dev, const char *mux_name) +{ + return NULL; +} + +void mux_control_put(struct mux_control *mux) +{ +} + +struct mux_control *devm_mux_control_get(struct udevice *dev, + const char *mux_name) +{ + return NULL; +} +#endif + +#endif From patchwork Thu Jun 11 19:45:02 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pratyush Yadav X-Patchwork-Id: 242148 List-Id: U-Boot discussion From: p.yadav at ti.com (Pratyush Yadav) Date: Fri, 12 Jun 2020 01:15:02 +0530 Subject: [PATCH v3 2/6] dm: board: complete the initialization of the muxes in initr_dm() In-Reply-To: <20200611194506.7263-1-p.yadav@ti.com> References: <20200611194506.7263-1-p.yadav@ti.com> Message-ID: <20200611194506.7263-3-p.yadav@ti.com> From: Jean-Jacques Hiblot This will probe the multiplexer devices that have a "u-boot,mux-autoprobe" property. As a consequence they will be put in their idle state. Signed-off-by: Jean-Jacques Hiblot Signed-off-by: Pratyush Yadav Reviewed-by: Simon Glass --- common/board_r.c | 12 ++++++++++++ drivers/mux/mux-uclass.c | 23 +++++++++++++++++++++++ include/mux.h | 12 ++++++++++++ 3 files changed, 47 insertions(+) diff --git a/common/board_r.c b/common/board_r.c index fa57fa9b69..aa1f98e851 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -46,6 +46,7 @@ #include #endif #include +#include #include #include #include @@ -334,6 +335,17 @@ static int initr_dm_devices(void) return ret; } + if (IS_ENABLED(CONFIG_MULTIPLEXER)) { + /* + * Initialize the multiplexer controls to their default state. + * This must be done early as other drivers may unknowingly + * rely on it. + */ + ret = dm_mux_init(); + if (ret) + return ret; + } + return 0; } diff --git a/drivers/mux/mux-uclass.c b/drivers/mux/mux-uclass.c index 3c89e0a389..3dad466101 100644 --- a/drivers/mux/mux-uclass.c +++ b/drivers/mux/mux-uclass.c @@ -303,6 +303,29 @@ static int mux_uclass_post_probe(struct udevice *dev) return 0; } +int dm_mux_init(void) +{ + struct uclass *uc; + struct udevice *dev; + int ret; + + ret = uclass_get(UCLASS_MUX, &uc); + if (ret < 0) { + log_debug("unable to get MUX uclass\n"); + return ret; + } + uclass_foreach_dev(dev, uc) { + if (dev_read_bool(dev, "u-boot,mux-autoprobe")) { + ret = device_probe(dev); + if (ret) + log_debug("unable to probe device %s\n", + dev->name); + } + } + + return 0; +} + UCLASS_DRIVER(mux) = { .id = UCLASS_MUX, .name = "mux", diff --git a/include/mux.h b/include/mux.h index 85eb7d42fc..23844f480a 100644 --- a/include/mux.h +++ b/include/mux.h @@ -109,6 +109,13 @@ void mux_control_put(struct mux_control *mux); */ struct mux_control *devm_mux_control_get(struct udevice *dev, const char *mux_name); +/** + * dm_mux_init() - Initialize the multiplexer controls to their default state. + * + * Return: 0 if OK, -errno otherwise. + */ +int dm_mux_init(void); + #else unsigned int mux_control_states(struct mux_control *mux) { @@ -142,6 +149,11 @@ struct mux_control *devm_mux_control_get(struct udevice *dev, { return NULL; } + +int dm_mux_init(void) +{ + return -ENOSYS; +} #endif #endif From patchwork Thu Jun 11 19:45:03 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pratyush Yadav X-Patchwork-Id: 242149 List-Id: U-Boot discussion From: p.yadav at ti.com (Pratyush Yadav) Date: Fri, 12 Jun 2020 01:15:03 +0530 Subject: [PATCH v3 3/6] drivers: mux: mmio-based syscon mux controller In-Reply-To: <20200611194506.7263-1-p.yadav@ti.com> References: <20200611194506.7263-1-p.yadav@ti.com> Message-ID: <20200611194506.7263-4-p.yadav@ti.com> From: Jean-Jacques Hiblot This adds a driver for mmio-based syscon multiplexers controlled by bitfields in a syscon register range. This is heavily based on the linux mmio-mux driver. Signed-off-by: Jean-Jacques Hiblot Reviewed-by: Simon Glass Signed-off-by: Pratyush Yadav Reviewed-by: Simon Glass --- drivers/mux/Kconfig | 14 +++++ drivers/mux/Makefile | 1 + drivers/mux/mmio.c | 143 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 158 insertions(+) create mode 100644 drivers/mux/mmio.c diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig index 35c1c5673c..f15ee4f833 100644 --- a/drivers/mux/Kconfig +++ b/drivers/mux/Kconfig @@ -8,4 +8,18 @@ config MULTIPLEXER controllers. It provides the same API as Linux and mux drivers should be portable with a minimum effort. +if MULTIPLEXER + +config MUX_MMIO + bool "MMIO register bitfield-controlled Multiplexer" + depends on MULTIPLEXER && SYSCON + help + MMIO register bitfield-controlled Multiplexer controller. + + The driver builds multiplexer controllers for bitfields in a syscon + register. For N bit wide bitfields, there will be 2^N possible + multiplexer states. + +endif + endmenu diff --git a/drivers/mux/Makefile b/drivers/mux/Makefile index 351e4363d3..78ebf04c7a 100644 --- a/drivers/mux/Makefile +++ b/drivers/mux/Makefile @@ -4,3 +4,4 @@ # Jean-Jacques Hiblot obj-$(CONFIG_$(SPL_)MULTIPLEXER) += mux-uclass.o +obj-$(CONFIG_$(SPL_)MUX_MMIO) += mmio.o diff --git a/drivers/mux/mmio.c b/drivers/mux/mmio.c new file mode 100644 index 0000000000..573e599dd1 --- /dev/null +++ b/drivers/mux/mmio.c @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MMIO register bitfield-controlled multiplexer driver + * Based on the linux mmio multiplexer driver + * + * Copyright (C) 2017 Pengutronix, Philipp Zabel + * Copyright (C) 2019 Texas Instrument, Jean-jacques Hiblot + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int mux_mmio_set(struct mux_control *mux, int state) +{ + struct regmap_field **fields = dev_get_priv(mux->dev); + + return regmap_field_write(fields[mux_control_get_index(mux)], state); +} + +static const struct mux_control_ops mux_mmio_ops = { + .set = mux_mmio_set, +}; + +static const struct udevice_id mmio_mux_of_match[] = { + { .compatible = "mmio-mux" }, + { /* sentinel */ }, +}; + +static int mmio_mux_probe(struct udevice *dev) +{ + struct regmap_field **fields; + struct mux_chip *mux_chip = dev_get_uclass_priv(dev); + struct regmap *regmap; + u32 *mux_reg_masks; + u32 *idle_states; + int num_fields; + int ret; + int i; + + regmap = syscon_node_to_regmap(dev_ofnode(dev->parent)); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(dev, "failed to get regmap: %d\n", ret); + return ret; + } + + num_fields = dev_read_size(dev, "mux-reg-masks"); + if (num_fields < 0) + return log_msg_ret("mux-reg-masks missing or invalid", -EINVAL); + + num_fields /= sizeof(u32); + if (num_fields == 0 || num_fields % 2) + ret = -EINVAL; + num_fields = num_fields / 2; + + ret = mux_alloc_controllers(dev, num_fields); + if (ret < 0) + return log_msg_ret("failed to allocate mux controllers", ret); + + fields = devm_kmalloc(dev, num_fields * sizeof(*fields), __GFP_ZERO); + if (!fields) + return -ENOMEM; + dev->priv = fields; + + mux_reg_masks = devm_kmalloc(dev, num_fields * 2 * sizeof(u32), + __GFP_ZERO); + if (!mux_reg_masks) + return -ENOMEM; + + ret = dev_read_u32_array(dev, "mux-reg-masks", mux_reg_masks, + num_fields * 2); + if (ret < 0) + return log_msg_ret("failed to read mux-reg-masks property", + ret); + + idle_states = devm_kmalloc(dev, num_fields * sizeof(u32), __GFP_ZERO); + if (!idle_states) + return -ENOMEM; + + ret = dev_read_u32_array(dev, "idle-states", idle_states, num_fields); + if (ret < 0) { + log_err("failed to read idle-states property: %d\n", ret); + devm_kfree(dev, idle_states); + idle_states = NULL; + } + + for (i = 0; i < num_fields; i++) { + struct mux_control *mux = &mux_chip->mux[i]; + struct reg_field field; + u32 reg, mask; + int bits; + + reg = mux_reg_masks[2 * i]; + mask = mux_reg_masks[2 * i + 1]; + + field.reg = reg; + field.msb = fls(mask) - 1; + field.lsb = ffs(mask) - 1; + + if (mask != GENMASK(field.msb, field.lsb)) + return log_msg_ret("invalid mask", -EINVAL); + + fields[i] = devm_regmap_field_alloc(dev, regmap, field); + if (IS_ERR(fields[i])) { + ret = PTR_ERR(fields[i]); + return log_msg_ret("Failed to allocate regmap field", + ret); + } + + bits = 1 + field.msb - field.lsb; + mux->states = 1 << bits; + + if (!idle_states) + continue; + + if (idle_states[i] != MUX_IDLE_AS_IS && + idle_states[i] >= mux->states) + return log_msg_ret("out of range idle state", -EINVAL); + + mux->idle_state = idle_states[i]; + } + + devm_kfree(dev, mux_reg_masks); + if (idle_states) + devm_kfree(dev, idle_states); + + return 0; +} + +U_BOOT_DRIVER(mmio_mux) = { + .name = "mmio-mux", + .id = UCLASS_MUX, + .of_match = mmio_mux_of_match, + .probe = mmio_mux_probe, + .ops = &mux_mmio_ops, +}; From patchwork Thu Jun 11 19:45:04 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pratyush Yadav X-Patchwork-Id: 242150 List-Id: U-Boot discussion From: p.yadav at ti.com (Pratyush Yadav) Date: Fri, 12 Jun 2020 01:15:04 +0530 Subject: [PATCH v3 4/6] cmd: Add a mux command In-Reply-To: <20200611194506.7263-1-p.yadav@ti.com> References: <20200611194506.7263-1-p.yadav@ti.com> Message-ID: <20200611194506.7263-5-p.yadav@ti.com> This command lets the user list, select, and deselect mux controllers introduced with the mux framework on the fly. It has 3 subcommands: list, select, and deselect. List: Lists all the mux present on the system. The muxes are listed for each chip. The chip is identified by its device name. Each chip can have a number of mux controllers. Each is listed in sequence and is assigned a sequential ID based on its position in the mux chip. It lists details like ID, whether the mux is currently selected or not, the current state, the idle state, and the number of states. A sample output would look something like: => mux list a-mux-controller: ID Selected Current State Idle State Num States 0 no unknown as-is 0x4 1 no 0x2 0x2 0x10 2 no 0x73 0x73 0x100 another-mux-controller: ID Selected Current State Idle State Num States 0 no 0x1 0x1 0x4 1 no 0x2 0x2 0x4 Select: Selects a given mux and puts it in the specified state. This subcommand takes 3 arguments: mux chip, mux ID, state to set the mux in. The arguments mux chip and mux ID are used to identify which mux needs to be selected, and then it is selected to the given state. The mux needs to be deselected before it can be selected again in another state. The state should be a hexadecimal number. For example: => mux list a-mux-controller: ID Selected Current State Idle State Num States 0 no 0x1 0x1 0x4 1 no 0x1 0x1 0x4 => mux select a-mux-controller 0 0x3 => mux list a-mux-controller: ID Selected Current State Idle State Num States 0 yes 0x3 0x1 0x4 1 no 0x1 0x1 0x4 Deselect: Deselects a given mux and puts it in its idle state. This subcommand takes 2 arguments: the mux chip and mux ID to identify which mux needs to be deselected. So in the above example, we can deselect mux 0 using: => mux deselect a-mux-controller 0 => mux list a-mux-controller: ID Selected Current State Idle State Num States 0 no 0x1 0x1 0x4 1 no 0x1 0x1 0x4 Signed-off-by: Pratyush Yadav Reviewed-by: Simon Glass --- cmd/Kconfig | 6 ++ cmd/Makefile | 1 + cmd/mux.c | 161 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 168 insertions(+) create mode 100644 cmd/mux.c diff --git a/cmd/Kconfig b/cmd/Kconfig index 192b3b262f..5e78f373d6 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1128,6 +1128,12 @@ config CMD_MTD help MTD commands support. +config CMD_MUX + bool "mux" + depends on MULTIPLEXER + help + List, select, and deselect mux controllers on the fly. + config CMD_NAND bool "nand" default y if NAND_SUNXI diff --git a/cmd/Makefile b/cmd/Makefile index 974ad48b0a..8c680420e1 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -99,6 +99,7 @@ obj-$(CONFIG_CMD_MTDPARTS) += mtdparts.o ifneq ($(CONFIG_CMD_NAND)$(CONFIG_CMD_SF),) obj-y += legacy-mtd-utils.o endif +obj-$(CONFIG_CMD_MUX) += mux.o obj-$(CONFIG_CMD_NAND) += nand.o obj-$(CONFIG_CMD_NET) += net.o obj-$(CONFIG_CMD_NVEDIT_EFI) += nvedit_efi.o diff --git a/cmd/mux.c b/cmd/mux.c new file mode 100644 index 0000000000..fd9c09c3ff --- /dev/null +++ b/cmd/mux.c @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * List, select, and deselect mux controllers on the fly. + * + * Copyright (c) 2020 Texas Instruments Inc. + * Author: Pratyush Yadav + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Find a mux based on its device name in argv[1] and index in the chip in + * argv[2]. + */ +static struct mux_control *cmd_mux_find(char *const argv[]) +{ + struct udevice *dev; + struct mux_chip *chip; + int ret; + unsigned long id; + + ret = strict_strtoul(argv[2], 10, &id); + if (ret) + return ERR_PTR(ret); + + ret = uclass_get_device_by_name(UCLASS_MUX, argv[1], &dev); + if (ret) + return ERR_PTR(ret); + + chip = dev_get_uclass_priv(dev); + if (!chip) + return ERR_PTR(ret); + + if (id >= chip->controllers) + return ERR_PTR(-EINVAL); + + return &chip->mux[id]; +} + +/* + * Print the details of a mux. The columns printed correspond to: "Selected", + * "Current State", "Idle State", and "Num States". + */ +static void print_mux(struct mux_control *mux) +{ + printf("%-10s\t", mux->in_use ? "yes" : "no"); + + if (mux->cached_state == MUX_IDLE_AS_IS) + printf("%-10s\t", "unknown"); + else + printf("0x%-8x\t", mux->cached_state); + + if (mux->idle_state == MUX_IDLE_AS_IS) + printf("%-10s\t", "as-is"); + else if (mux->idle_state == MUX_IDLE_DISCONNECT) + printf("%-10s\t", "disconnect"); + else + printf("0x%-8x\t", mux->idle_state); + + printf("0x%-8x\n", mux->states); +} + +static int do_mux_list(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + struct mux_chip *chip; + int j; + + for (uclass_first_device(UCLASS_MUX, &dev); + dev; + uclass_next_device(&dev)) { + chip = dev_get_uclass_priv(dev); + if (!chip) { + dev_err(dev, "can't find mux chip\n"); + continue; + } + + printf("%s:\n", dev->name); + printf("\tID\tSelected\tCurrent State\tIdle State\tNum States\n"); + for (j = 0; j < chip->controllers; j++) { + printf("\t%d\t", j); + print_mux(&chip->mux[j]); + } + printf("\n"); + } + + return 0; +} + +static int do_mux_select(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct mux_control *mux; + int ret; + unsigned long state; + + if (argc != 4) + return CMD_RET_USAGE; + + mux = cmd_mux_find(argv); + if (IS_ERR_OR_NULL(mux)) { + printf("Failed to find the specified mux\n"); + return CMD_RET_FAILURE; + } + + ret = strict_strtoul(argv[3], 16, &state); + if (ret) { + printf("Invalid state\n"); + return CMD_RET_FAILURE; + } + + ret = mux_control_select(mux, state); + if (ret) { + printf("Failed to select requested state\n"); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +static int do_mux_deselect(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct mux_control *mux; + int ret; + + if (argc != 3) + return CMD_RET_USAGE; + + mux = cmd_mux_find(argv); + if (IS_ERR_OR_NULL(mux)) { + printf("Failed to find the specified mux\n"); + return CMD_RET_FAILURE; + } + + ret = mux_control_deselect(mux); + if (ret) { + printf("Failed to deselect mux\n"); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +static char mux_help_text[] = + "list - List all Muxes and their states\n" + "select - Select the given mux state\n" + "deselect - Deselect the given mux and reset it to its idle state"; + +U_BOOT_CMD_WITH_SUBCMDS(mux, "List, select, and deselect muxes", mux_help_text, + U_BOOT_SUBCMD_MKENT(list, 1, 1, do_mux_list), + U_BOOT_SUBCMD_MKENT(select, 4, 0, do_mux_select), + U_BOOT_SUBCMD_MKENT(deselect, 3, 0, do_mux_deselect)); From patchwork Thu Jun 11 19:45:05 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pratyush Yadav X-Patchwork-Id: 242151 List-Id: U-Boot discussion From: p.yadav at ti.com (Pratyush Yadav) Date: Fri, 12 Jun 2020 01:15:05 +0530 Subject: [PATCH v3 5/6] sandbox: Enable memio operations in board_init In-Reply-To: <20200611194506.7263-1-p.yadav@ti.com> References: <20200611194506.7263-1-p.yadav@ti.com> Message-ID: <20200611194506.7263-6-p.yadav@ti.com> initr_dm_devices() is called somewhere after board_init(). It can be used by drivers to initialize devices to some default bootup state. Those devices might use mmio read/write operations to perform the initialization. One such example is the mux devices. The mux framework initializes the muxes to their default state after idle state in mux_uclass_post_probe(). One type of mux controller is the MMIO mux controller. Initializing a MMIO mux to idle state can require a mmio read and write operation. With memio disabled, the reads return 0 and the writes go off into the void. This makes it impossible to initialize muxes to their idle state on boot, and consequentially makes it impossible to test that in sandbox. These same initializations work fine on actual hardware (tested on TI J721E EVM). So, enable memio operations on boot so devices like mux can perform whatever initialization they need. state_reset_for_test() will disable it before running tests so tests still need to enable memio manually. Signed-off-by: Pratyush Yadav --- board/sandbox/sandbox.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/board/sandbox/sandbox.c b/board/sandbox/sandbox.c index 1372003018..d7eb207822 100644 --- a/board/sandbox/sandbox.c +++ b/board/sandbox/sandbox.c @@ -55,6 +55,8 @@ int board_init(void) if (IS_ENABLED(CONFIG_LED)) led_default_state(); + sandbox_set_enable_memio(true); + return 0; } From patchwork Thu Jun 11 19:45:06 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pratyush Yadav X-Patchwork-Id: 242152 List-Id: U-Boot discussion From: p.yadav at ti.com (Pratyush Yadav) Date: Fri, 12 Jun 2020 01:15:06 +0530 Subject: [PATCH v3 6/6] test: Add tests for the multiplexer framework In-Reply-To: <20200611194506.7263-1-p.yadav@ti.com> References: <20200611194506.7263-1-p.yadav@ti.com> Message-ID: <20200611194506.7263-7-p.yadav@ti.com> From: Jean-Jacques Hiblot Provide tests to check the behavior of the multiplexer framework. The test uses a mmio-based multiplexer. Signed-off-by: Jean-Jacques Hiblot Reviewed-by: Simon Glass Signed-off-by: Pratyush Yadav --- arch/sandbox/dts/test.dts | 25 ++++++ configs/sandbox_defconfig | 2 + test/dm/Makefile | 1 + test/dm/mux-mmio.c | 168 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 196 insertions(+) create mode 100644 test/dm/mux-mmio.c diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index e8701d2603..750779d20f 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -2,6 +2,7 @@ #include #include +#include / { model = "sandbox"; @@ -115,6 +116,11 @@ int64-value = /bits/ 64 <0x1111222233334444>; int-array = <5678 9123 4567>; interrupts-extended = <&irq 3 0>; + + mux-controls = <&muxcontroller0 0>, <&muxcontroller0 1>, + <&muxcontroller0 2>, <&muxcontroller0 3>; + mux-control-names = "mux0", "mux1", "mux2", "mux3"; + mux-syscon = <&syscon3>; }; another-test { @@ -158,6 +164,9 @@ compatible = "denx,u-boot-fdt-test"; ping-expect = <3>; ping-add = <3>; + + mux-controls = <&muxcontroller0 0>; + mux-control-names = "mux0"; }; phy_provider0: gen_phy at 0 { @@ -787,6 +796,22 @@ 0x58 8>; }; + syscon3: syscon at 3 { + compatible = "simple-mfd", "syscon"; + reg = <0x000100 0x10>; + + muxcontroller0: a-mux-controller { + compatible = "mmio-mux"; + #mux-control-cells = <1>; + + mux-reg-masks = <0x0 0x30>, /* 0: reg 0x0, bits 5:4 */ + <0xc 0x1E>, /* 1: reg 0xc, bits 4:1 */ + <0x4 0xFF>; /* 2: reg 0x4, bits 7:0 */ + idle-states = , <0x02>, <0x73>; + u-boot,mux-autoprobe; + }; + }; + timer { compatible = "sandbox,timer"; clock-frequency = <1000000>; diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 5b7569319b..c301d11992 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -154,6 +154,8 @@ CONFIG_SPI_FLASH_SPANSION=y CONFIG_SPI_FLASH_STMICRO=y CONFIG_SPI_FLASH_SST=y CONFIG_SPI_FLASH_WINBOND=y +CONFIG_MULTIPLEXER=y +CONFIG_MUX_MMIO=y CONFIG_DM_ETH=y CONFIG_NVME=y CONFIG_PCI=y diff --git a/test/dm/Makefile b/test/dm/Makefile index 6c18fd04ce..d7ab7e27f7 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -54,6 +54,7 @@ obj-$(CONFIG_DM_SPI_FLASH) += sf.o obj-$(CONFIG_SMEM) += smem.o obj-$(CONFIG_DM_SPI) += spi.o obj-y += syscon.o +obj-$(CONFIG_MUX_MMIO) += mux-mmio.o obj-$(CONFIG_DM_USB) += usb.o obj-$(CONFIG_DM_PMIC) += pmic.o obj-$(CONFIG_DM_REGULATOR) += regulator.o diff --git a/test/dm/mux-mmio.c b/test/dm/mux-mmio.c new file mode 100644 index 0000000000..91c383324f --- /dev/null +++ b/test/dm/mux-mmio.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017-2018 Texas Instruments Incorporated - http://www.ti.com/ + * Jean-Jacques Hiblot + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Test that mmio mux work correctly */ +static int dm_test_mux_mmio_default_state(struct unit_test_state *uts) +{ + struct udevice *dev, *dev_b; + struct regmap *map; + u32 val; + + sandbox_set_enable_memio(true); + + ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "a-test", + &dev)); + ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "b-test", + &dev_b)); + map = syscon_regmap_lookup_by_phandle(dev, "mux-syscon"); + ut_assertok_ptr(map); + ut_assert(map); + + /* Check default states. */ + ut_assertok(regmap_read(map, 0xc, &val)); + ut_asserteq(0x02, (val & 0x1E) >> 1); + ut_assertok(regmap_read(map, 0x4, &val)); + ut_asserteq(0x73, (val & 0xFF) >> 0); + + return 0; +} +DM_TEST(dm_test_mux_mmio_default_state, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +static int dm_test_mux_mmio_select(struct unit_test_state *uts) +{ + struct udevice *dev, *dev_b; + struct regmap *map; + struct mux_control *ctl0_a, *ctl0_b; + struct mux_control *ctl1; + struct mux_control *ctl_err; + u32 val; + int i; + + sandbox_set_enable_memio(true); + + ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "a-test", + &dev)); + ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "b-test", + &dev_b)); + map = syscon_regmap_lookup_by_phandle(dev, "mux-syscon"); + ut_assertok_ptr(map); + ut_assert(map); + + ut_assertok(mux_control_get(dev, "mux0", &ctl0_a)); + ut_assertok(mux_control_get(dev, "mux1", &ctl1)); + ut_asserteq(-ERANGE, mux_control_get(dev, "mux3", &ctl_err)); + ut_asserteq(-ENODATA, mux_control_get(dev, "dummy", &ctl_err)); + ut_assertok(mux_control_get(dev_b, "mux0", &ctl0_b)); + + for (i = 0; i < mux_control_states(ctl0_a); i++) { + /* Select a new state and verify the value in the regmap. */ + ut_assertok(mux_control_select(ctl0_a, i)); + ut_assertok(regmap_read(map, 0, &val)); + ut_asserteq(i, (val & 0x30) >> 4); + /* + * Deselect the mux and verify that the value in the regmap + * reflects the idle state (fixed to MUX_IDLE_AS_IS). + */ + ut_assertok(mux_control_deselect(ctl0_a)); + ut_assertok(regmap_read(map, 0, &val)); + ut_asserteq(i, (val & 0x30) >> 4); + } + + for (i = 0; i < mux_control_states(ctl1); i++) { + /* Select a new state and verify the value in the regmap. */ + ut_assertok(mux_control_select(ctl1, i)); + ut_assertok(regmap_read(map, 0xc, &val)); + ut_asserteq(i, (val & 0x1E) >> 1); + /* + * Deselect the mux and verify that the value in the regmap + * reflects the idle state (fixed to 2). + */ + ut_assertok(mux_control_deselect(ctl1)); + ut_assertok(regmap_read(map, 0xc, &val)); + ut_asserteq(2, (val & 0x1E) >> 1); + } + + /* Try unbalanced selection/deselection. */ + ut_assertok(mux_control_select(ctl0_a, 0)); + ut_asserteq(-EBUSY, mux_control_select(ctl0_a, 1)); + ut_asserteq(-EBUSY, mux_control_select(ctl0_a, 0)); + ut_assertok(mux_control_deselect(ctl0_a)); + + /* Try concurrent selection. */ + ut_assertok(mux_control_select(ctl0_a, 0)); + ut_assert(mux_control_select(ctl0_b, 0)); + ut_assertok(mux_control_deselect(ctl0_a)); + ut_assertok(mux_control_select(ctl0_b, 0)); + ut_assert(mux_control_select(ctl0_a, 0)); + ut_assertok(mux_control_deselect(ctl0_b)); + ut_assertok(mux_control_select(ctl0_a, 0)); + ut_assertok(mux_control_deselect(ctl0_a)); + + return 0; +} +DM_TEST(dm_test_mux_mmio_select, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test that managed API for mux work correctly */ +static int dm_test_devm_mux_mmio(struct unit_test_state *uts) +{ + struct udevice *dev, *dev_b; + struct mux_control *ctl0_a, *ctl0_b; + struct mux_control *ctl1; + struct mux_control *ctl_err; + + sandbox_set_enable_memio(true); + + ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "a-test", + &dev)); + ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "b-test", + &dev_b)); + + ctl0_a = devm_mux_control_get(dev, "mux0"); + ut_assertok_ptr(ctl0_a); + ut_assert(ctl0_a); + ctl1 = devm_mux_control_get(dev, "mux1"); + ut_assertok_ptr(ctl1); + ut_assert(ctl1); + ctl_err = devm_mux_control_get(dev, "mux3"); + ut_asserteq(-ERANGE, PTR_ERR(ctl_err)); + ctl_err = devm_mux_control_get(dev, "dummy"); + ut_asserteq(-ENODATA, PTR_ERR(ctl_err)); + + ctl0_b = devm_mux_control_get(dev_b, "mux0"); + ut_assertok_ptr(ctl0_b); + ut_assert(ctl0_b); + + /* Try concurrent selection. */ + ut_assertok(mux_control_select(ctl0_a, 0)); + ut_assert(mux_control_select(ctl0_b, 0)); + ut_assertok(mux_control_deselect(ctl0_a)); + ut_assertok(mux_control_select(ctl0_b, 0)); + ut_assert(mux_control_select(ctl0_a, 0)); + ut_assertok(mux_control_deselect(ctl0_b)); + + /* Remove one device and check that the mux is released. */ + ut_assertok(mux_control_select(ctl0_a, 0)); + ut_assert(mux_control_select(ctl0_b, 0)); + device_remove(dev, DM_REMOVE_NORMAL); + ut_assertok(mux_control_select(ctl0_b, 0)); + + device_remove(dev_b, DM_REMOVE_NORMAL); + return 0; +} +DM_TEST(dm_test_devm_mux_mmio, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);