[U-Boot] [PATCH v5 1/4] 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 Reviewed-by: Bin Meng bmeng.cn@gmail.com Acked-by: Joe Hershberger joe.hershberger@ni.com ---
Changes in v2: - no change Changes in v3: - no change, just fighting with the email server Changes in v4: - fixed a couple of typos Changes in v5: - no change
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 c4f4e04ff8..f09f8c5f52 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..e425207d6e --- /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 a 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 Acked-by: Joe Hershberger joe.hershberger@ni.com Reviewed-by: Bin Meng bmeng.cn@gmail.com ---
Changes in v2: - no change Changes in v3: - no change, just fighting with the email server Changes in v4: - pulled in full binding example from Linux, added a note on why mdio-parent-bus is currently required in U-boot but not in Linux Changes in v5: - fixed a few typos, a multi-line comment format
doc/device-tree-bindings/net/mdio-mux.txt | 138 ++++++++++++++++++++++ 1 file changed, 138 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..b87efe9535 --- /dev/null +++ b/doc/device-tree-bindings/net/mdio-mux.txt @@ -0,0 +1,138 @@ +The expected structure of an MDIO MUX device tree node is described here. This +is heavily based on current Linux specification. +One notable difference to Linux is that mdio-parent-bus is currently required +by U-boot, not optional as is in Linux. Current U-boot MDIO MUX udevice class +implementation does not have specific support for MDIOs with an integrated MUX, +the property should be made optional if such support is added. + +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>; + compatible = "marvell,88e1149r"; + 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 */ + }; + phy12: ethernet-phy@2 { + reg = <2>; + compatible = "marvell,88e1149r"; + 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 */ + }; + phy13: ethernet-phy@3 { + reg = <3>; + compatible = "marvell,88e1149r"; + 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 */ + }; + phy14: ethernet-phy@4 { + reg = <4>; + compatible = "marvell,88e1149r"; + 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 */ + }; + }; + mdio@3 { + reg = <3>; + #address-cells = <1>; + #size-cells = <0>; + phy21: ethernet-phy@1 { + reg = <1>; + compatible = "marvell,88e1149r"; + marvell,reg-init = <3 0x10 0 0x5777>, + <3 0x11 0 0x00aa>, + <3 0x12 0 0x4105>, + <3 0x13 0 0x0a60>; + interrupt-parent = <&gpio>; + interrupts = <12 8>; /* Pin 12, active low */ + }; + phy22: ethernet-phy@2 { + reg = <2>; + compatible = "marvell,88e1149r"; + marvell,reg-init = <3 0x10 0 0x5777>, + <3 0x11 0 0x00aa>, + <3 0x12 0 0x4105>, + <3 0x13 0 0x0a60>; + interrupt-parent = <&gpio>; + interrupts = <12 8>; /* Pin 12, active low */ + }; + phy23: ethernet-phy@3 { + reg = <3>; + compatible = "marvell,88e1149r"; + marvell,reg-init = <3 0x10 0 0x5777>, + <3 0x11 0 0x00aa>, + <3 0x12 0 0x4105>, + <3 0x13 0 0x0a60>; + interrupt-parent = <&gpio>; + interrupts = <12 8>; /* Pin 12, active low */ + }; + phy24: ethernet-phy@4 { + reg = <4>; + compatible = "marvell,88e1149r"; + marvell,reg-init = <3 0x10 0 0x5777>, + <3 0x11 0 0x00aa>, + <3 0x12 0 0x4105>, + <3 0x13 0 0x0a60>; + interrupt-parent = <&gpio>; + interrupts = <12 8>; /* Pin 12, active low */ + }; + }; + };

Hi Alex,
On Fri, Jul 12, 2019 at 2:39 PM Alex 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 Acked-by: Joe Hershberger joe.hershberger@ni.com Reviewed-by: Bin Meng bmeng.cn@gmail.com
Changes in v2: - no change Changes in v3: - no change, just fighting with the email server Changes in v4: - pulled in full binding example from Linux, added a note on why mdio-parent-bus is currently required in U-boot but not in Linux Changes in v5: - fixed a few typos, a multi-line comment format
doc/device-tree-bindings/net/mdio-mux.txt | 138 ++++++++++++++++++++++ 1 file changed, 138 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..b87efe9535 --- /dev/null +++ b/doc/device-tree-bindings/net/mdio-mux.txt @@ -0,0 +1,138 @@ +The expected structure of an MDIO MUX device tree node is described here. This +is heavily based on current Linux specification. +One notable difference to Linux is that mdio-parent-bus is currently required +by U-boot, not optional as is in Linux. Current U-boot MDIO MUX udevice class
Sorry but I was saying "U-Boot" :) too nitpicking I am :)
[snip]
Regards, Bin

On 7/12/2019 9:44 AM, Bin Meng wrote:
Hi Alex,
On Fri, Jul 12, 2019 at 2:39 PM Alex 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 Acked-by: Joe Hershberger joe.hershberger@ni.com Reviewed-by: Bin Meng bmeng.cn@gmail.com
Changes in v2: - no change Changes in v3: - no change, just fighting with the email server Changes in v4: - pulled in full binding example from Linux, added a note on why mdio-parent-bus is currently required in U-boot but not in Linux Changes in v5: - fixed a few typos, a multi-line comment format
doc/device-tree-bindings/net/mdio-mux.txt | 138 ++++++++++++++++++++++ 1 file changed, 138 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..b87efe9535 --- /dev/null +++ b/doc/device-tree-bindings/net/mdio-mux.txt @@ -0,0 +1,138 @@ +The expected structure of an MDIO MUX device tree node is described here. This +is heavily based on current Linux specification. +One notable difference to Linux is that mdio-parent-bus is currently required +by U-boot, not optional as is in Linux. Current U-boot MDIO MUX udevice class
Sorry but I was saying "U-Boot" :) too nitpicking I am :)
oops, I'll send a v6 :)
[snip]
Regards, Bin

This 2nd register is used by the follow-up MDIO MUX test.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com Reviewed-by: Bin Meng bmeng.cn@gmail.com Acked-by: Joe Hershberger joe.hershberger@ni.com ---
Changes in v2: - no change Changes in v3: - no change, just fighting with the email server Changes in v4: - no change Changes in v5: - no change
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

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 Acked-by: Joe Hershberger joe.hershberger@ni.com Reviewed-by: Bin Meng bmeng.cn@gmail.com ---
Changes in v2: - no change Changes in v3: - no change, just fighting with the email server Changes in v4: - a more explicit comment on why relative ordering between mdio-test and mdio-mux-test nodes in test.dts matters. Changes in v5: - no change
arch/Kconfig | 1 + arch/sandbox/dts/test.dts | 23 +++++++- 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, 212 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 a6b2de8527..6dd0874d7c 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -123,6 +123,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..a88cf75652 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -808,7 +808,28 @@ dma-names = "m2m", "tx0", "rx0"; };
- mdio-test { + /* + * keep mdio-mux ahead of mdio so that the mux is removed first at the + * end of the test. If parent mdio is removed first, clean-up of the + * mux will trigger a 2nd probe of parent-mdio, leaving parent-mdio + * active at the end of the test. That it turn doesn't allow the mdio + * class to be destroyed, triggering an error. + */ + 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 f09f8c5f52..4d85fb1716 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 1247b39a54..97119cec7c 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -79,3 +79,4 @@ obj-y += mscc_eswitch/ obj-$(CONFIG_HIGMACV300_ETH) += higmacv300.o obj-$(CONFIG_MDIO_SANDBOX) += mdio_sandbox.o obj-$(CONFIG_FSL_ENETC) += fsl_enetc.o fsl_enetc_mdio.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);
participants (3)
-
Alex Marginean
-
Alex Marginean
-
Bin Meng