diff mbox series

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

Message ID 20190618145823.606-1-alexandru.marginean@nxp.com
State Superseded
Delegated to: Joe Hershberger
Headers show
Series [U-Boot,1/4,v3] net: add MDIO_MUX DM class | expand

Commit Message

Alexandru Marginean June 18, 2019, 2:58 p.m. UTC
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

Comments

Bin Meng July 8, 2019, 7:40 a.m. UTC | #1
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
Joe Hershberger July 9, 2019, 11:49 p.m. UTC | #2
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>
diff mbox series

Patch

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),
+};