[U-Boot] [PATCH 1/4 v3] net: add MDIO_MUX DM class

Adds a class for MDIO MUXes, which control access to a series of downstream child MDIOs. MDIO MUX drivers are required to implement a select function used to switch between child buses. MUX children are registered as MDIO buses and they can be used just like regular MDIOs.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com ---
Changes in v2: - no change Changes in v3: - no change, just fighting with the email server
drivers/net/Kconfig | 12 +++ include/dm/uclass-id.h | 1 + include/miiphy.h | 20 ++++ net/Makefile | 1 + net/mdio-mux-uclass.c | 232 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 266 insertions(+) create mode 100644 net/mdio-mux-uclass.c
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 635f8d72c2..0dc26ac254 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -24,6 +24,18 @@ config DM_MDIO This is currently implemented in net/mdio-uclass.c Look in include/miiphy.h for details.
+config DM_MDIO_MUX + bool "Enable Driver Model for MDIO MUX devices" + depends on DM_MDIO + help + Enable driver model for MDIO MUX devices + + Adds UCLASS_MDIO_MUX DM class supporting MDIO MUXes. Useful for + systems that support DM_MDIO and integrate one or multiple muxes on + the MDIO bus. + This is currently implemented in net/mdio-mux-uclass.c + Look in include/miiphy.h for details. + config MDIO_SANDBOX depends on DM_MDIO && SANDBOX default y diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 90667e62cf..b859a9ec04 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -59,6 +59,7 @@ enum uclass_id { UCLASS_MAILBOX, /* Mailbox controller */ UCLASS_MASS_STORAGE, /* Mass storage device */ UCLASS_MDIO, /* MDIO bus */ + UCLASS_MDIO_MUX, /* MDIO MUX/switch */ UCLASS_MISC, /* Miscellaneous device */ UCLASS_MMC, /* SD / MMC card or chip */ UCLASS_MOD_EXP, /* RSA Mod Exp device */ diff --git a/include/miiphy.h b/include/miiphy.h index e6dd441983..9b97d09f18 100644 --- a/include/miiphy.h +++ b/include/miiphy.h @@ -167,4 +167,24 @@ struct phy_device *dm_mdio_phy_connect(struct udevice *dev, int addr,
#endif
+#ifdef CONFIG_DM_MDIO_MUX + +/* indicates none of the child buses is selected */ +#define MDIO_MUX_SELECT_NONE -1 + +/** + * struct mdio_mux_ops - MDIO MUX operations + * + * @select: Selects a child bus + * @deselect: Clean up selection. Optional, can be NULL + */ +struct mdio_mux_ops { + int (*select)(struct udevice *mux, int cur, int sel); + int (*deselect)(struct udevice *mux, int sel); +}; + +#define mdio_mux_get_ops(dev) ((struct mdio_mux_ops *)(dev)->driver->ops) + +#endif + #endif diff --git a/net/Makefile b/net/Makefile index 6251ff3991..826544f05f 100644 --- a/net/Makefile +++ b/net/Makefile @@ -16,6 +16,7 @@ else obj-$(CONFIG_NET) += eth_legacy.o endif obj-$(CONFIG_DM_MDIO) += mdio-uclass.o +obj-$(CONFIG_DM_MDIO_MUX) += mdio-mux-uclass.o obj-$(CONFIG_NET) += eth_common.o obj-$(CONFIG_CMD_LINK_LOCAL) += link_local.o obj-$(CONFIG_NET) += net.o diff --git a/net/mdio-mux-uclass.c b/net/mdio-mux-uclass.c new file mode 100644 index 0000000000..e3fe12a531 --- /dev/null +++ b/net/mdio-mux-uclass.c @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2019 + * Alex Marginean, NXP + */ + +#include <common.h> +#include <dm.h> +#include <miiphy.h> +#include <dm/device-internal.h> +#include <dm/uclass-internal.h> +#include <dm/lists.h> + +#define MDIO_MUX_CHILD_DRV_NAME "mdio-mux-bus-drv" + +/** + * struct mdio_mux_perdev_priv - Per-device class data for MDIO MUX DM + * + * @parent_mdio: Parent DM MDIO device, this is called for actual MDIO I/O after + * setting up the mux. Typically this is a real MDIO device, + * unless there are cascaded muxes. + * @selected: Current child bus selection. Defaults to -1 + */ +struct mdio_mux_perdev_priv { + struct udevice *mdio_parent; + int selected; +}; + +/* + * This source file uses three types of devices, as follows: + * - mux is the hardware MDIO MUX which selects between the existing child MDIO + * buses, this is the device relevant for MDIO MUX class of drivers. + * - ch is a child MDIO bus, this is just a representation of an mux selection, + * not a real piece of hardware. + * - mdio_parent is the actual MDIO bus called to perform reads/writes after + * the MUX is configured. Typically this is a real MDIO device, unless there + * are cascaded muxes. + */ + +/** + * struct mdio_mux_ch_data - Per-device data for child MDIOs + * + * @sel: Selection value used by the MDIO MUX to access this child MDIO bus + */ +struct mdio_mux_ch_data { + int sel; +}; + +static struct udevice *mmux_get_parent_mdio(struct udevice *mux) +{ + struct mdio_mux_perdev_priv *pdata = dev_get_uclass_priv(mux); + + return pdata->mdio_parent; +} + +static struct mdio_ops *mmux_get_mdio_parent_ops(struct udevice *mux) +{ + return mdio_get_ops(mmux_get_parent_mdio(mux)); +} + +/* call driver select function before performing MDIO r/w */ +static int mmux_change_sel(struct udevice *ch, bool sel) +{ + struct udevice *mux = ch->parent; + struct mdio_mux_perdev_priv *priv = dev_get_uclass_priv(mux); + struct mdio_mux_ops *ops = mdio_mux_get_ops(mux); + struct mdio_mux_ch_data *ch_data = dev_get_parent_platdata(ch); + int err = 0; + + if (sel) { + err = ops->select(mux, priv->selected, ch_data->sel); + if (err) + return err; + + priv->selected = ch_data->sel; + } else { + if (ops->deselect) { + ops->deselect(mux, ch_data->sel); + priv->selected = MDIO_MUX_SELECT_NONE; + } + } + + return 0; +} + +/* Read wrapper, sets up the mux before issuing a read on parent MDIO bus */ +static int mmux_read(struct udevice *ch, int addr, int devad, + int reg) +{ + struct udevice *mux = ch->parent; + struct udevice *parent_mdio = mmux_get_parent_mdio(mux); + struct mdio_ops *parent_ops = mmux_get_mdio_parent_ops(mux); + int err; + + err = mmux_change_sel(ch, true); + if (err) + return err; + + err = parent_ops->read(parent_mdio, addr, devad, reg); + mmux_change_sel(ch, false); + + return err; +} + +/* Write wrapper, sets up the mux before issuing a write on parent MDIO bus */ +static int mmux_write(struct udevice *ch, int addr, int devad, + int reg, u16 val) +{ + struct udevice *mux = ch->parent; + struct udevice *parent_mdio = mmux_get_parent_mdio(mux); + struct mdio_ops *parent_ops = mmux_get_mdio_parent_ops(mux); + int err; + + err = mmux_change_sel(ch, true); + if (err) + return err; + + err = parent_ops->write(parent_mdio, addr, devad, reg, val); + mmux_change_sel(ch, false); + + return err; +} + +/* Reset wrapper, sets up the mux before issuing a reset on parent MDIO bus */ +static int mmux_reset(struct udevice *ch) +{ + struct udevice *mux = ch->parent; + struct udevice *parent_mdio = mmux_get_parent_mdio(mux); + struct mdio_ops *parent_ops = mmux_get_mdio_parent_ops(mux); + int err; + + /* reset is optional, if it's not implemented just exit */ + if (!parent_ops->reset) + return 0; + + err = mmux_change_sel(ch, true); + if (err) + return err; + + err = parent_ops->reset(parent_mdio); + mmux_change_sel(ch, false); + + return err; +} + +/* Picks up the mux selection value for each child */ +static int dm_mdio_mux_child_post_bind(struct udevice *ch) +{ + struct mdio_mux_ch_data *ch_data = dev_get_parent_platdata(ch); + + ch_data->sel = dev_read_u32_default(ch, "reg", MDIO_MUX_SELECT_NONE); + + if (ch_data->sel == MDIO_MUX_SELECT_NONE) + return -EINVAL; + + return 0; +} + +/* Explicitly bind child MDIOs after binding the mux */ +static int dm_mdio_mux_post_bind(struct udevice *mux) +{ + ofnode ch_node; + int err, first_err = 0; + + if (!ofnode_valid(mux->node)) { + debug("%s: no mux node found, no child MDIO busses set up\n", + __func__); + return 0; + } + + /* + * we're going by Linux bindings so the child nodes do not have + * compatible strings. We're going through them here and binding to + * them. + */ + dev_for_each_subnode(ch_node, mux) { + struct udevice *ch_dev; + const char *ch_name; + + ch_name = ofnode_get_name(ch_node); + + err = device_bind_driver_to_node(mux, MDIO_MUX_CHILD_DRV_NAME, + ch_name, ch_node, &ch_dev); + /* try to bind all, but keep 1st error */ + if (err && !first_err) + first_err = err; + } + + return first_err; +} + +/* Get a reference to the parent MDIO bus, it should be bound by now */ +static int dm_mdio_mux_post_probe(struct udevice *mux) +{ + struct mdio_mux_perdev_priv *priv = dev_get_uclass_priv(mux); + int err; + + priv->selected = MDIO_MUX_SELECT_NONE; + + /* pick up mdio parent from device tree */ + err = uclass_get_device_by_phandle(UCLASS_MDIO, mux, "mdio-parent-bus", + &priv->mdio_parent); + if (err) { + debug("%s: didn't find mdio-parent-bus\n", __func__); + return err; + } + + return 0; +} + +const struct mdio_ops mmux_child_mdio_ops = { + .read = mmux_read, + .write = mmux_write, + .reset = mmux_reset, +}; + +/* MDIO class driver used for MUX child MDIO buses */ +U_BOOT_DRIVER(mdio_mux_child) = { + .name = MDIO_MUX_CHILD_DRV_NAME, + .id = UCLASS_MDIO, + .ops = &mmux_child_mdio_ops, +}; + +UCLASS_DRIVER(mdio_mux) = { + .id = UCLASS_MDIO_MUX, + .name = "mdio-mux", + .child_post_bind = dm_mdio_mux_child_post_bind, + .post_bind = dm_mdio_mux_post_bind, + .post_probe = dm_mdio_mux_post_probe, + .per_device_auto_alloc_size = sizeof(struct mdio_mux_perdev_priv), + .per_child_platdata_auto_alloc_size = sizeof(struct mdio_mux_ch_data), +};

Adds a short bindings document describing the expected structure of a MDIO MUX dts node. This is based on Linux binding and the example is in fact copied from there.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com ---
Changes in v2: - no change Changes in v3: - no change, just fighting with the email server
doc/device-tree-bindings/net/mdio-mux.txt | 61 +++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 doc/device-tree-bindings/net/mdio-mux.txt
diff --git a/doc/device-tree-bindings/net/mdio-mux.txt b/doc/device-tree-bindings/net/mdio-mux.txt new file mode 100644 index 0000000000..fdf817fd93 --- /dev/null +++ b/doc/device-tree-bindings/net/mdio-mux.txt @@ -0,0 +1,61 @@ +The expected structure of an MDIO MUX device tree node is described here. This +is heavily based on current Linux specification. +The MDIO buses downstream of the MUX should be described in the device tree as +child nodes as indicated below. + +Required properties: +mdio-parent-bus = a phandle to the MDIO bus used to perform actual I/O. This is + typically a real MDIO device, unless there are cascaded MUXes. +#address-cells = <1>, each MDIO group is identified by one 32b value. +#size-cells = <0> + +Other properties: +The properties described here are sufficient for MDIO MUX DM class code, but +MUX drivers may define additional properties, either required or optional. + +Required properties in child nodes: +reg = value to be configured on the MUX to select the respective downstream + MDIO. + +Child nodes should normally contain PHY nodes, referenced by phandle from +ethernet nodes of the eth interfaces using these PHYs. + +Example structure, extracted from Linux bindings document: + + /* The parent MDIO bus. */ + smi1: mdio@1180000001900 { + compatible = "cavium,octeon-3860-mdio"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x11800 0x00001900 0x0 0x40>; + }; + + /* + An NXP sn74cbtlv3253 dual 1-of-4 switch controlled by a + pair of GPIO lines. Child busses 2 and 3 populated with 4 + PHYs each. + */ + mdio-mux { + compatible = "mdio-mux-gpio"; + gpios = <&gpio1 3 0>, <&gpio1 4 0>; + mdio-parent-bus = <&smi1>; + #address-cells = <1>; + #size-cells = <0>; + + mdio@2 { + reg = <2>; + #address-cells = <1>; + #size-cells = <0>; + + phy11: ethernet-phy@1 { + reg = <1>; + marvell,reg-init = <3 0x10 0 0x5777>, + <3 0x11 0 0x00aa>, + <3 0x12 0 0x4105>, + <3 0x13 0 0x0a60>; + interrupt-parent = <&gpio>; + interrupts = <10 8>; /* Pin 10, active low */ + }; + }; + }; +

On Tue, Jun 18, 2019 at 10:58 PM Alexandru Marginean alexandru.marginean@nxp.com wrote:
Adds a short bindings document describing the expected structure of a MDIO MUX dts node. This is based on Linux binding and the example is in fact copied from there.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com
Changes in v2: - no change Changes in v3: - no change, just fighting with the email server
doc/device-tree-bindings/net/mdio-mux.txt | 61 +++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 doc/device-tree-bindings/net/mdio-mux.txt
diff --git a/doc/device-tree-bindings/net/mdio-mux.txt b/doc/device-tree-bindings/net/mdio-mux.txt new file mode 100644 index 0000000000..fdf817fd93 --- /dev/null +++ b/doc/device-tree-bindings/net/mdio-mux.txt @@ -0,0 +1,61 @@ +The expected structure of an MDIO MUX device tree node is described here. This +is heavily based on current Linux specification. +The MDIO buses downstream of the MUX should be described in the device tree as +child nodes as indicated below.
+Required properties: +mdio-parent-bus = a phandle to the MDIO bus used to perform actual I/O. This is
typically a real MDIO device, unless there are cascaded MUXes.
In Linux, "mdio-parent-bus" is optional, but in U-Boot this is required?
+#address-cells = <1>, each MDIO group is identified by one 32b value. +#size-cells = <0>
+Other properties: +The properties described here are sufficient for MDIO MUX DM class code, but +MUX drivers may define additional properties, either required or optional.
+Required properties in child nodes: +reg = value to be configured on the MUX to select the respective downstream
MDIO.
+Child nodes should normally contain PHY nodes, referenced by phandle from +ethernet nodes of the eth interfaces using these PHYs.
+Example structure, extracted from Linux bindings document:
/* The parent MDIO bus. */
smi1: mdio@1180000001900 {
compatible = "cavium,octeon-3860-mdio";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x11800 0x00001900 0x0 0x40>;
};
/*
An NXP sn74cbtlv3253 dual 1-of-4 switch controlled by a
pair of GPIO lines. Child busses 2 and 3 populated with 4
PHYs each.
*/
It looks the example is not complete, which makes it hard for people to understand (ie: only bus 2 is listed below) without reading the Linux bindings doc.
mdio-mux {
compatible = "mdio-mux-gpio";
gpios = <&gpio1 3 0>, <&gpio1 4 0>;
mdio-parent-bus = <&smi1>;
#address-cells = <1>;
#size-cells = <0>;
mdio@2 {
reg = <2>;
#address-cells = <1>;
#size-cells = <0>;
phy11: ethernet-phy@1 {
reg = <1>;
marvell,reg-init = <3 0x10 0 0x5777>,
<3 0x11 0 0x00aa>,
<3 0x12 0 0x4105>,
<3 0x13 0 0x0a60>;
interrupt-parent = <&gpio>;
interrupts = <10 8>; /* Pin 10, active low */
};
};
};
Regards, Bin

On 7/8/2019 10:40 AM, Bin Meng wrote:
On Tue, Jun 18, 2019 at 10:58 PM Alexandru Marginean alexandru.marginean@nxp.com wrote:
Adds a short bindings document describing the expected structure of a MDIO MUX dts node. This is based on Linux binding and the example is in fact copied from there.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com
Changes in v2: - no change Changes in v3: - no change, just fighting with the email server
doc/device-tree-bindings/net/mdio-mux.txt | 61 +++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 doc/device-tree-bindings/net/mdio-mux.txt
diff --git a/doc/device-tree-bindings/net/mdio-mux.txt b/doc/device-tree-bindings/net/mdio-mux.txt new file mode 100644 index 0000000000..fdf817fd93 --- /dev/null +++ b/doc/device-tree-bindings/net/mdio-mux.txt @@ -0,0 +1,61 @@ +The expected structure of an MDIO MUX device tree node is described here. This +is heavily based on current Linux specification. +The MDIO buses downstream of the MUX should be described in the device tree as +child nodes as indicated below.
+Required properties: +mdio-parent-bus = a phandle to the MDIO bus used to perform actual I/O. This is
typically a real MDIO device, unless there are cascaded MUXes.
In Linux, "mdio-parent-bus" is optional, but in U-Boot this is required?
That was my plan, making it optional requires some extra code that I didn't put in. I could put that code in but it would be unused for now. I can do it, or the change can be done whenever someone adds a MUX which is driven over parent MDIO.
+#address-cells = <1>, each MDIO group is identified by one 32b value. +#size-cells = <0>
+Other properties: +The properties described here are sufficient for MDIO MUX DM class code, but +MUX drivers may define additional properties, either required or optional.
+Required properties in child nodes: +reg = value to be configured on the MUX to select the respective downstream
MDIO.
+Child nodes should normally contain PHY nodes, referenced by phandle from +ethernet nodes of the eth interfaces using these PHYs.
+Example structure, extracted from Linux bindings document:
/* The parent MDIO bus. */
smi1: mdio@1180000001900 {
compatible = "cavium,octeon-3860-mdio";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x11800 0x00001900 0x0 0x40>;
};
/*
An NXP sn74cbtlv3253 dual 1-of-4 switch controlled by a
pair of GPIO lines. Child busses 2 and 3 populated with 4
PHYs each.
*/
It looks the example is not complete, which makes it hard for people to understand (ie: only bus 2 is listed below) without reading the Linux bindings doc.
I did chop it down to the piece I though it's relevant, I can pull in the full example. Or I can replace it with the definition of the device I'm working on, that way the example is practical. 'mdio-mux-gpio' is not something I was planning to add to u-boot now.
Thank you! Alex
mdio-mux {
compatible = "mdio-mux-gpio";
gpios = <&gpio1 3 0>, <&gpio1 4 0>;
mdio-parent-bus = <&smi1>;
#address-cells = <1>;
#size-cells = <0>;
mdio@2 {
reg = <2>;
#address-cells = <1>;
#size-cells = <0>;
phy11: ethernet-phy@1 {
reg = <1>;
marvell,reg-init = <3 0x10 0 0x5777>,
<3 0x11 0 0x00aa>,
<3 0x12 0 0x4105>,
<3 0x13 0 0x0a60>;
interrupt-parent = <&gpio>;
interrupts = <10 8>; /* Pin 10, active low */
};
};
};
Regards, Bin _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot

This 2nd register is used by the follow-up MDIO MUX test.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com ---
Changes in v2: - no change Changes in v3: - no change, just fighting with the email server
drivers/net/mdio_sandbox.c | 16 +++++++++------- test/dm/mdio.c | 3 +++ 2 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/drivers/net/mdio_sandbox.c b/drivers/net/mdio_sandbox.c index 07515e078c..df053f5381 100644 --- a/drivers/net/mdio_sandbox.c +++ b/drivers/net/mdio_sandbox.c @@ -9,11 +9,11 @@ #include <miiphy.h>
#define SANDBOX_PHY_ADDR 5 -#define SANDBOX_PHY_REG 0 +#define SANDBOX_PHY_REG_CNT 2
struct mdio_sandbox_priv { int enabled; - u16 reg; + u16 reg[SANDBOX_PHY_REG_CNT]; };
static int mdio_sandbox_read(struct udevice *dev, int addr, int devad, int reg) @@ -27,10 +27,10 @@ static int mdio_sandbox_read(struct udevice *dev, int addr, int devad, int reg) return -ENODEV; if (devad != MDIO_DEVAD_NONE) return -ENODEV; - if (reg != SANDBOX_PHY_REG) + if (reg < 0 || reg > SANDBOX_PHY_REG_CNT) return -ENODEV;
- return priv->reg; + return priv->reg[reg]; }
static int mdio_sandbox_write(struct udevice *dev, int addr, int devad, int reg, @@ -45,10 +45,10 @@ static int mdio_sandbox_write(struct udevice *dev, int addr, int devad, int reg, return -ENODEV; if (devad != MDIO_DEVAD_NONE) return -ENODEV; - if (reg != SANDBOX_PHY_REG) + if (reg < 0 || reg > SANDBOX_PHY_REG_CNT) return -ENODEV;
- priv->reg = val; + priv->reg[reg] = val;
return 0; } @@ -56,8 +56,10 @@ static int mdio_sandbox_write(struct udevice *dev, int addr, int devad, int reg, static int mdio_sandbox_reset(struct udevice *dev) { struct mdio_sandbox_priv *priv = dev_get_priv(dev); + int i;
- priv->reg = 0; + for (i = 0; i < SANDBOX_PHY_REG_CNT; i++) + priv->reg[i] = 0;
return 0; } diff --git a/test/dm/mdio.c b/test/dm/mdio.c index 5b66255f7d..dc229aed6d 100644 --- a/test/dm/mdio.c +++ b/test/dm/mdio.c @@ -13,6 +13,9 @@
/* macros copied over from mdio_sandbox.c */ #define SANDBOX_PHY_ADDR 5 +#define SANDBOX_PHY_REG_CNT 2 + +/* test using 1st register, 0 */ #define SANDBOX_PHY_REG 0
#define TEST_REG_VALUE 0xabcd

On Tue, Jun 18, 2019 at 10:58 PM Alexandru Marginean alexandru.marginean@nxp.com wrote:
This 2nd register is used by the follow-up MDIO MUX test.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com
Changes in v2: - no change Changes in v3: - no change, just fighting with the email server
drivers/net/mdio_sandbox.c | 16 +++++++++------- test/dm/mdio.c | 3 +++ 2 files changed, 12 insertions(+), 7 deletions(-)
Reviewed-by: Bin Meng bmeng.cn@gmail.com

On Tue, Jun 18, 2019 at 10:01 AM Alexandru Marginean alexandru.marginean@nxp.com wrote:
This 2nd register is used by the follow-up MDIO MUX test.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com
Acked-by: Joe Hershberger joe.hershberger@ni.com

Adds a test using a makeshift MDIO MUX. The test is based on the existing MDIO test. It uses the last emulated PHY register to verify MUX selection.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com ---
Changes in v2: - no change Changes in v3: - no change, just fighting with the email server
arch/Kconfig | 1 + arch/sandbox/dts/test.dts | 21 +++++++- drivers/net/Kconfig | 10 ++++ drivers/net/Makefile | 1 + drivers/net/mdio_mux_sandbox.c | 97 ++++++++++++++++++++++++++++++++++ test/dm/Makefile | 1 + test/dm/mdio_mux.c | 80 ++++++++++++++++++++++++++++ 7 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 drivers/net/mdio_mux_sandbox.c create mode 100644 test/dm/mdio_mux.c
diff --git a/arch/Kconfig b/arch/Kconfig index 1e62a7615d..1a0f1ab8a7 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -122,6 +122,7 @@ config SANDBOX imply PCH imply PHYLIB imply DM_MDIO + imply DM_MDIO_MUX
config SH bool "SuperH architecture" diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index dd50a951a8..a05e437abf 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -808,7 +808,26 @@ dma-names = "m2m", "tx0", "rx0"; };
- mdio-test { + /* + * keep mdio-mux ahead of mdio, u-boot doesn't do reference count on + * these devices and we don't want mdio-parent-bus to be released before + * the mux. + */ + mdio-mux-test { + compatible = "sandbox,mdio-mux"; + #address-cells = <1>; + #size-cells = <0>; + mdio-parent-bus = <&mdio>; + + mdio-ch-test@0 { + reg = <0>; + }; + mdio-ch-test@1 { + reg = <1>; + }; + }; + + mdio: mdio-test { compatible = "sandbox,mdio"; }; }; diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 0dc26ac254..403df5e960 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -46,6 +46,16 @@ config MDIO_SANDBOX
This driver is used in for testing in test/dm/mdio.c
+config MDIO_MUX_SANDBOX + depends on DM_MDIO_MUX && MDIO_SANDBOX + default y + bool "Sandbox: Mocked MDIO-MUX driver" + help + This driver implements dummy select/deselect ops mimicking a MUX on + the MDIO bux. It uses mdio_sandbox driver as parent MDIO. + + This driver is used for testing in test/dm/mdio.c + menuconfig NETDEVICES bool "Network device support" depends on NET diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 40038427db..a3706ca27b 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -78,3 +78,4 @@ obj-$(CONFIG_MEDIATEK_ETH) += mtk_eth.o obj-y += mscc_eswitch/ obj-$(CONFIG_HIGMACV300_ETH) += higmacv300.o obj-$(CONFIG_MDIO_SANDBOX) += mdio_sandbox.o +obj-$(CONFIG_MDIO_MUX_SANDBOX) += mdio_mux_sandbox.o diff --git a/drivers/net/mdio_mux_sandbox.c b/drivers/net/mdio_mux_sandbox.c new file mode 100644 index 0000000000..3dba4d18a1 --- /dev/null +++ b/drivers/net/mdio_mux_sandbox.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2019 + * Alex Marginean, NXP + */ + +#include <dm.h> +#include <errno.h> +#include <miiphy.h> + +/* macros copied over from mdio_sandbox.c */ +#define SANDBOX_PHY_ADDR 5 +#define SANDBOX_PHY_REG_CNT 2 + +struct mdio_mux_sandbox_priv { + int enabled; + int sel; +}; + +static int mdio_mux_sandbox_mark_selection(struct udevice *dev, int sel) +{ + struct udevice *mdio; + struct mdio_ops *ops; + int err; + + /* + * find the sandbox parent mdio and write a register on the PHY there + * so the mux test can verify selection. + */ + err = uclass_get_device_by_name(UCLASS_MDIO, "mdio-test", &mdio); + if (err) + return err; + ops = mdio_get_ops(mdio); + return ops->write(mdio, SANDBOX_PHY_ADDR, MDIO_DEVAD_NONE, + SANDBOX_PHY_REG_CNT - 1, (u16)sel); +} + +static int mdio_mux_sandbox_select(struct udevice *dev, int cur, int sel) +{ + struct mdio_mux_sandbox_priv *priv = dev_get_priv(dev); + + if (!priv->enabled) + return -ENODEV; + + if (cur != priv->sel) + return -EINVAL; + + priv->sel = sel; + mdio_mux_sandbox_mark_selection(dev, priv->sel); + + return 0; +} + +static int mdio_mux_sandbox_deselect(struct udevice *dev, int sel) +{ + struct mdio_mux_sandbox_priv *priv = dev_get_priv(dev); + + if (!priv->enabled) + return -ENODEV; + + if (sel != priv->sel) + return -EINVAL; + + priv->sel = -1; + mdio_mux_sandbox_mark_selection(dev, priv->sel); + + return 0; +} + +static const struct mdio_mux_ops mdio_mux_sandbox_ops = { + .select = mdio_mux_sandbox_select, + .deselect = mdio_mux_sandbox_deselect, +}; + +static int mdio_mux_sandbox_probe(struct udevice *dev) +{ + struct mdio_mux_sandbox_priv *priv = dev_get_priv(dev); + + priv->enabled = 1; + priv->sel = -1; + + return 0; +} + +static const struct udevice_id mdio_mux_sandbox_ids[] = { + { .compatible = "sandbox,mdio-mux" }, + { } +}; + +U_BOOT_DRIVER(mdio_mux_sandbox) = { + .name = "mdio_mux_sandbox", + .id = UCLASS_MDIO_MUX, + .of_match = mdio_mux_sandbox_ids, + .probe = mdio_mux_sandbox_probe, + .ops = &mdio_mux_sandbox_ops, + .priv_auto_alloc_size = sizeof(struct mdio_mux_sandbox_priv), +}; diff --git a/test/dm/Makefile b/test/dm/Makefile index 3f042e3ab4..8b3477c136 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -61,4 +61,5 @@ obj-$(CONFIG_TEE) += tee.o obj-$(CONFIG_VIRTIO_SANDBOX) += virtio.o obj-$(CONFIG_DMA) += dma.o obj-$(CONFIG_DM_MDIO) += mdio.o +obj-$(CONFIG_DM_MDIO_MUX) += mdio_mux.o endif diff --git a/test/dm/mdio_mux.c b/test/dm/mdio_mux.c new file mode 100644 index 0000000000..f962e09dbc --- /dev/null +++ b/test/dm/mdio_mux.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2019 + * Alex Marginean, NXP + */ + +#include <common.h> +#include <dm.h> +#include <dm/test.h> +#include <misc.h> +#include <test/ut.h> +#include <miiphy.h> + +/* macros copied over from mdio_sandbox.c */ +#define SANDBOX_PHY_ADDR 5 +#define SANDBOX_PHY_REG_CNT 2 + +#define TEST_REG_VALUE 0xabcd + +static int dm_test_mdio_mux(struct unit_test_state *uts) +{ + struct uclass *uc; + struct udevice *mux; + struct udevice *mdio_ch0, *mdio_ch1, *mdio; + struct mdio_ops *ops, *ops_parent; + struct mdio_mux_ops *mmops; + u16 reg; + + ut_assertok(uclass_get(UCLASS_MDIO_MUX, &uc)); + + ut_assertok(uclass_get_device_by_name(UCLASS_MDIO_MUX, "mdio-mux-test", + &mux)); + + ut_assertok(uclass_get_device_by_name(UCLASS_MDIO, "mdio-ch-test@0", + &mdio_ch0)); + ut_assertok(uclass_get_device_by_name(UCLASS_MDIO, "mdio-ch-test@1", + &mdio_ch1)); + + ut_assertok(uclass_get_device_by_name(UCLASS_MDIO, "mdio-test", &mdio)); + + ops = mdio_get_ops(mdio_ch0); + ut_assertnonnull(ops); + ut_assertnonnull(ops->read); + ut_assertnonnull(ops->write); + + mmops = mdio_mux_get_ops(mux); + ut_assertnonnull(mmops); + ut_assertnonnull(mmops->select); + + ops_parent = mdio_get_ops(mdio); + ut_assertnonnull(ops); + ut_assertnonnull(ops->read); + + /* + * mux driver sets last register on the emulated PHY whenever a group + * is selected to the selection #. Just reading that register from + * either of the child buses should return the id of the child bus + */ + reg = ops->read(mdio_ch0, SANDBOX_PHY_ADDR, MDIO_DEVAD_NONE, + SANDBOX_PHY_REG_CNT - 1); + ut_asserteq(reg, 0); + + reg = ops->read(mdio_ch1, SANDBOX_PHY_ADDR, MDIO_DEVAD_NONE, + SANDBOX_PHY_REG_CNT - 1); + ut_asserteq(reg, 1); + + mmops->select(mux, MDIO_MUX_SELECT_NONE, 5); + reg = ops_parent->read(mdio, SANDBOX_PHY_ADDR, MDIO_DEVAD_NONE, + SANDBOX_PHY_REG_CNT - 1); + ut_asserteq(reg, 5); + + mmops->deselect(mux, 5); + reg = ops_parent->read(mdio, SANDBOX_PHY_ADDR, MDIO_DEVAD_NONE, + SANDBOX_PHY_REG_CNT - 1); + ut_asserteq(reg, (u16)MDIO_MUX_SELECT_NONE); + + return 0; +} + +DM_TEST(dm_test_mdio_mux, DM_TESTF_SCAN_FDT);

On Tue, Jun 18, 2019 at 10:58 PM Alexandru Marginean alexandru.marginean@nxp.com wrote:
Adds a test using a makeshift MDIO MUX. The test is based on the existing MDIO test. It uses the last emulated PHY register to verify MUX selection.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com
Changes in v2: - no change Changes in v3: - no change, just fighting with the email server
arch/Kconfig | 1 + arch/sandbox/dts/test.dts | 21 +++++++- drivers/net/Kconfig | 10 ++++ drivers/net/Makefile | 1 + drivers/net/mdio_mux_sandbox.c | 97 ++++++++++++++++++++++++++++++++++ test/dm/Makefile | 1 + test/dm/mdio_mux.c | 80 ++++++++++++++++++++++++++++ 7 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 drivers/net/mdio_mux_sandbox.c create mode 100644 test/dm/mdio_mux.c
diff --git a/arch/Kconfig b/arch/Kconfig index 1e62a7615d..1a0f1ab8a7 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -122,6 +122,7 @@ config SANDBOX imply PCH imply PHYLIB imply DM_MDIO
imply DM_MDIO_MUX
config SH bool "SuperH architecture" diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index dd50a951a8..a05e437abf 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -808,7 +808,26 @@ dma-names = "m2m", "tx0", "rx0"; };
mdio-test {
/*
* keep mdio-mux ahead of mdio, u-boot doesn't do reference count on
nits: U-Boot
* these devices and we don't want mdio-parent-bus to be released before
* the mux.
I did not get it why there is a ordering issue? Could you please elaborate?
*/
mdio-mux-test {
compatible = "sandbox,mdio-mux";
#address-cells = <1>;
#size-cells = <0>;
mdio-parent-bus = <&mdio>;
mdio-ch-test@0 {
reg = <0>;
};
mdio-ch-test@1 {
reg = <1>;
};
};
Test codes looks good to me though.
Regards, Bin

Hi Bin,
On 7/8/2019 10:40 AM, Bin Meng wrote:
On Tue, Jun 18, 2019 at 10:58 PM Alexandru Marginean alexandru.marginean@nxp.com wrote:
Adds a test using a makeshift MDIO MUX. The test is based on the existing MDIO test. It uses the last emulated PHY register to verify MUX selection.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com
Changes in v2: - no change Changes in v3: - no change, just fighting with the email server
arch/Kconfig | 1 + arch/sandbox/dts/test.dts | 21 +++++++- drivers/net/Kconfig | 10 ++++ drivers/net/Makefile | 1 + drivers/net/mdio_mux_sandbox.c | 97 ++++++++++++++++++++++++++++++++++ test/dm/Makefile | 1 + test/dm/mdio_mux.c | 80 ++++++++++++++++++++++++++++ 7 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 drivers/net/mdio_mux_sandbox.c create mode 100644 test/dm/mdio_mux.c
diff --git a/arch/Kconfig b/arch/Kconfig index 1e62a7615d..1a0f1ab8a7 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -122,6 +122,7 @@ config SANDBOX imply PCH imply PHYLIB imply DM_MDIO
imply DM_MDIO_MUX
config SH bool "SuperH architecture"
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index dd50a951a8..a05e437abf 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -808,7 +808,26 @@ dma-names = "m2m", "tx0", "rx0"; };
mdio-test {
/*
* keep mdio-mux ahead of mdio, u-boot doesn't do reference count on
nits: U-Boot
* these devices and we don't want mdio-parent-bus to be released before
* the mux.
I did not get it why there is a ordering issue? Could you please elaborate?
I can try. At the end of 'ut dm' classes and devices are removed, which by itself is fine. When MDIO buses are removed there's a reset issued, which is also fine. There is no explicit dependency between the mux and parent MDIO though, not described in the way that the code removing these devices would understand. If parent MDIO is removed first, then removal/reset of mux child MDIOs triggers a new probe of the parent MDIO device to execute reset on child MDIO buses. This leaves parent MDIO probed at the end of the test and test-main doesn't like that as it can't remove the MDIO class with parent MDIO left active. The call sequence looks like this:
Parent MDIO dev is removed first: dm_mdio_pre_remove: mdio-test mdio_sandbox_reset: mdio-test mdio_sandbox_remove: mdio-test
Then MUX is removed, along with its children: dm_mdio_pre_remove: mdio-ch-test@0 mmux_reset: mdio-mux-test/mdio-ch-test@0 mdio_sandbox_probe: mdio-test dm_mdio_post_probe: mdio-test mdio_sandbox_reset: mdio-test dm_mdio_pre_remove: mdio-ch-test@1 mmux_reset: mdio-mux-test/mdio-ch-test@1 mdio_sandbox_reset: mdio-test test/dm/test-main.c:72, dm_test_destroy(): 0 == uclass_destroy(uc): Expected 0, got -22
I think I can add a uclass device post_remove API and use that in MUX uclass to remove parent MDIO device, but that looks like an unsafe thing to do. There is no way for the MUX uclass code to know whether any other piece of software still holds a reference to parent MDIO device.
I could skip calling remove for child MDIOs during removal of the MUX, to avoid probing parent MDIO again, but that looks like a hack.
The fix to the general problem would involve some form of reference counting per device, but it doesn't make sense to add that just for a MDIO MUX test. In practice not removing a device that's no longer used isn't a big deal for u-boot I think.
Keeping dts nodes in that order works, but it is also a hack, it's pretty obscure, I'm not super happy with that either.
Any suggestions?
Thank you! Alex
*/
mdio-mux-test {
compatible = "sandbox,mdio-mux";
#address-cells = <1>;
#size-cells = <0>;
mdio-parent-bus = <&mdio>;
mdio-ch-test@0 {
reg = <0>;
};
mdio-ch-test@1 {
reg = <1>;
};
};
Test codes looks good to me though.
Regards, Bin _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot

On Tue, Jun 18, 2019 at 10:58 PM Alexandru Marginean alexandru.marginean@nxp.com wrote:
Adds a class for MDIO MUXes, which control access to a series of downstream child MDIOs. MDIO MUX drivers are required to implement a select function used to switch between child buses. MUX children are registered as MDIO buses and they can be used just like regular MDIOs.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com
Changes in v2: - no change Changes in v3: - no change, just fighting with the email server
drivers/net/Kconfig | 12 +++ include/dm/uclass-id.h | 1 + include/miiphy.h | 20 ++++ net/Makefile | 1 + net/mdio-mux-uclass.c | 232 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 266 insertions(+) create mode 100644 net/mdio-mux-uclass.c
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 635f8d72c2..0dc26ac254 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -24,6 +24,18 @@ config DM_MDIO This is currently implemented in net/mdio-uclass.c Look in include/miiphy.h for details.
+config DM_MDIO_MUX
bool "Enable Driver Model for MDIO MUX devices"
depends on DM_MDIO
help
Enable driver model for MDIO MUX devices
Adds UCLASS_MDIO_MUX DM class supporting MDIO MUXes. Useful for
systems that support DM_MDIO and integrate one or multiple muxes on
the MDIO bus.
This is currently implemented in net/mdio-mux-uclass.c
Look in include/miiphy.h for details.
config MDIO_SANDBOX depends on DM_MDIO && SANDBOX default y diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 90667e62cf..b859a9ec04 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -59,6 +59,7 @@ enum uclass_id { UCLASS_MAILBOX, /* Mailbox controller */ UCLASS_MASS_STORAGE, /* Mass storage device */ UCLASS_MDIO, /* MDIO bus */
UCLASS_MDIO_MUX, /* MDIO MUX/switch */ UCLASS_MISC, /* Miscellaneous device */ UCLASS_MMC, /* SD / MMC card or chip */ UCLASS_MOD_EXP, /* RSA Mod Exp device */
diff --git a/include/miiphy.h b/include/miiphy.h index e6dd441983..9b97d09f18 100644 --- a/include/miiphy.h +++ b/include/miiphy.h @@ -167,4 +167,24 @@ struct phy_device *dm_mdio_phy_connect(struct udevice *dev, int addr,
#endif
+#ifdef CONFIG_DM_MDIO_MUX
+/* indicates none of the child buses is selected */ +#define MDIO_MUX_SELECT_NONE -1
+/**
- struct mdio_mux_ops - MDIO MUX operations
- @select: Selects a child bus
- @deselect: Clean up selection. Optional, can be NULL
- */
+struct mdio_mux_ops {
int (*select)(struct udevice *mux, int cur, int sel);
int (*deselect)(struct udevice *mux, int sel);
+};
+#define mdio_mux_get_ops(dev) ((struct mdio_mux_ops *)(dev)->driver->ops)
+#endif
#endif diff --git a/net/Makefile b/net/Makefile index 6251ff3991..826544f05f 100644 --- a/net/Makefile +++ b/net/Makefile @@ -16,6 +16,7 @@ else obj-$(CONFIG_NET) += eth_legacy.o endif obj-$(CONFIG_DM_MDIO) += mdio-uclass.o +obj-$(CONFIG_DM_MDIO_MUX) += mdio-mux-uclass.o obj-$(CONFIG_NET) += eth_common.o obj-$(CONFIG_CMD_LINK_LOCAL) += link_local.o obj-$(CONFIG_NET) += net.o diff --git a/net/mdio-mux-uclass.c b/net/mdio-mux-uclass.c new file mode 100644 index 0000000000..e3fe12a531 --- /dev/null +++ b/net/mdio-mux-uclass.c @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2019
- Alex Marginean, NXP
- */
+#include <common.h> +#include <dm.h> +#include <miiphy.h> +#include <dm/device-internal.h> +#include <dm/uclass-internal.h> +#include <dm/lists.h>
+#define MDIO_MUX_CHILD_DRV_NAME "mdio-mux-bus-drv"
+/**
- struct mdio_mux_perdev_priv - Per-device class data for MDIO MUX DM
- @parent_mdio: Parent DM MDIO device, this is called for actual MDIO I/O after
setting up the mux. Typically this is a real MDIO device,
unless there are cascaded muxes.
- @selected: Current child bus selection. Defaults to -1
- */
+struct mdio_mux_perdev_priv {
struct udevice *mdio_parent;
int selected;
+};
+/*
- This source file uses three types of devices, as follows:
- mux is the hardware MDIO MUX which selects between the existing child MDIO
- buses, this is the device relevant for MDIO MUX class of drivers.
- ch is a child MDIO bus, this is just a representation of an mux selection,
nits: a mux selection
- not a real piece of hardware.
- mdio_parent is the actual MDIO bus called to perform reads/writes after
nits: 2 spaces before "after"
- the MUX is configured. Typically this is a real MDIO device, unless there
- are cascaded muxes.
- */
[snip]
Other than above, Reviewed-by: Bin Meng bmeng.cn@gmail.com
Regards, Bin

On Tue, Jun 18, 2019 at 10:01 AM Alexandru Marginean alexandru.marginean@nxp.com wrote:
Adds a class for MDIO MUXes, which control access to a series of downstream child MDIOs. MDIO MUX drivers are required to implement a select function used to switch between child buses. MUX children are registered as MDIO buses and they can be used just like regular MDIOs.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com
After addressing Bin's comments,
Acked-by: Joe Hershberger joe.hershberger@ni.com
participants (4)
-
Alex Marginean
-
Alexandru Marginean
-
Bin Meng
-
Joe Hershberger