[U-Boot] [PATCH 1/2 v3] net: introduce MDIO DM class for MDIO devices

Alex Marginean alexm.osslist at gmail.com
Mon Jun 3 16:10:30 UTC 2019


Adds UCLASS_MDIO DM class supporting MDIO buses that are probed as
stand-alone devices.  Useful in particular for systems that support
DM_ETH and have a stand-alone MDIO hardware block shared by multiple
Ethernet interfaces.

Signed-off-by: Alex Marginean <alexm.osslist at gmail.com>
---

Changes in v2:
	- fixed several comments using wrong API names
	- dropped dm_ from names of internal functions that don't use udevice *
	- fixed UCLASS driver name
	- added missing mdio_unregister in dm_mdio_pre_remove
	- added a comment on why spaces in names aren't ok
	- added a comment on how static mdio_read/_write/_reset functions
	are used
Changes in v3:
	- none

 cmd/mdio.c             |   5 ++
 drivers/net/Kconfig    |  13 +++++
 include/dm/uclass-id.h |   1 +
 include/miiphy.h       |  49 ++++++++++++++++++
 net/Makefile           |   1 +
 net/mdio-uclass.c      | 115 +++++++++++++++++++++++++++++++++++++++++
 6 files changed, 184 insertions(+)
 create mode 100644 net/mdio-uclass.c

diff --git a/cmd/mdio.c b/cmd/mdio.c
index 5e219f699d..a6fa9266d0 100644
--- a/cmd/mdio.c
+++ b/cmd/mdio.c
@@ -203,6 +203,11 @@ static int do_mdio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 	if (argc < 2)
 		return CMD_RET_USAGE;
 
+#ifdef CONFIG_DM_MDIO
+	/* probe DM MII device before any operation so they are all accesible */
+	dm_mdio_probe_devices();
+#endif
+
 	/*
 	 * We use the last specified parameters, unless new ones are
 	 * entered.
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index e6a4fdf30e..6fba5a84dd 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -11,6 +11,19 @@ config DM_ETH
 	  This is currently implemented in net/eth-uclass.c
 	  Look in include/net.h for details.
 
+config DM_MDIO
+	bool "Enable Driver Model for MDIO devices"
+	depends on DM_ETH && PHYLIB
+	help
+	  Enable driver model for MDIO devices
+
+	  Adds UCLASS_MDIO DM class supporting MDIO buses that are probed as
+	  stand-alone devices.  Useful in particular for systems that support
+	  DM_ETH and have a stand-alone MDIO hardware block shared by multiple
+	  Ethernet interfaces.
+	  This is currently implemented in net/mdio-uclass.c
+	  Look in include/miiphy.h for details.
+
 menuconfig NETDEVICES
 	bool "Network device support"
 	depends on NET
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index 09e0ad5391..90667e62cf 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -58,6 +58,7 @@ enum uclass_id {
 	UCLASS_LPC,		/* x86 'low pin count' interface */
 	UCLASS_MAILBOX,		/* Mailbox controller */
 	UCLASS_MASS_STORAGE,	/* Mass storage device */
+	UCLASS_MDIO,		/* MDIO bus */
 	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 f11763affd..e6dd441983 100644
--- a/include/miiphy.h
+++ b/include/miiphy.h
@@ -118,4 +118,53 @@ int bb_miiphy_write(struct mii_dev *miidev, int addr, int devad, int reg,
 #define ESTATUS_1000XF		0x8000
 #define ESTATUS_1000XH		0x4000
 
+#ifdef CONFIG_DM_MDIO
+
+/**
+ * struct mdio_perdev_priv - Per-device class data for MDIO DM
+ *
+ * @mii_bus: Supporting MII legacy bus
+ */
+struct mdio_perdev_priv {
+	struct mii_dev *mii_bus;
+};
+
+/**
+ * struct mdio_ops - MDIO bus operations
+ *
+ * @read: Read from a PHY register
+ * @write: Write to a PHY register
+ * @reset: Reset the MDIO bus, NULL if not supported
+ */
+struct mdio_ops {
+	int (*read)(struct udevice *mdio_dev, int addr, int devad, int reg);
+	int (*write)(struct udevice *mdio_dev, int addr, int devad, int reg,
+		     u16 val);
+	int (*reset)(struct udevice *mdio_dev);
+};
+
+#define mdio_get_ops(dev) ((struct mdio_ops *)(dev)->driver->ops)
+
+/**
+ * dm_mdio_probe_devices - Call probe on all MII devices, currently used for
+ * MDIO console commands.
+ */
+void dm_mdio_probe_devices(void);
+
+/**
+ * dm_mdio_phy_connect - Wrapper over phy_connect for DM MDIO
+ *
+ * @dev: mdio dev
+ * @addr: PHY address on MDIO bus
+ * @ethdev: ethernet device to connect to the PHY
+ * @interface: MAC-PHY protocol
+ *
+ * @return pointer to phy_device, or 0 on error
+ */
+struct phy_device *dm_mdio_phy_connect(struct udevice *dev, int addr,
+				       struct udevice *ethdev,
+				       phy_interface_t interface);
+
+#endif
+
 #endif
diff --git a/net/Makefile b/net/Makefile
index ce36362168..6251ff3991 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_NET)      += eth-uclass.o
 else
 obj-$(CONFIG_NET)      += eth_legacy.o
 endif
+obj-$(CONFIG_DM_MDIO)  += mdio-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-uclass.c b/net/mdio-uclass.c
new file mode 100644
index 0000000000..36a404ff44
--- /dev/null
+++ b/net/mdio-uclass.c
@@ -0,0 +1,115 @@
+// 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>
+
+void dm_mdio_probe_devices(void)
+{
+	struct udevice *it;
+	struct uclass *uc;
+
+	uclass_get(UCLASS_MDIO, &uc);
+	uclass_foreach_dev(it, uc) {
+		device_probe(it);
+	}
+}
+
+static int dm_mdio_post_bind(struct udevice *dev)
+{
+	/*
+	 * MDIO command doesn't like spaces in names, don't allow them to keep
+	 * it happy
+	 */
+	if (strchr(dev->name, ' ')) {
+		debug("\nError: MDIO device name \"%s\" has a space!\n",
+		      dev->name);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * Following read/write/reset functions are registered with legacy MII code.
+ * These are called for PHY operations by upper layers and we further call the
+ * DM MDIO driver functions.
+ */
+static int mdio_read(struct mii_dev *mii_bus, int addr, int devad, int reg)
+{
+	struct udevice *dev = mii_bus->priv;
+
+	return mdio_get_ops(dev)->read(dev, addr, devad, reg);
+}
+
+static int mdio_write(struct mii_dev *mii_bus, int addr, int devad, int reg,
+		      u16 val)
+{
+	struct udevice *dev = mii_bus->priv;
+
+	return mdio_get_ops(dev)->write(dev, addr, devad, reg, val);
+}
+
+static int mdio_reset(struct mii_dev *mii_bus)
+{
+	struct udevice *dev = mii_bus->priv;
+
+	if (mdio_get_ops(dev)->reset)
+		return mdio_get_ops(dev)->reset(dev);
+	else
+		return 0;
+}
+
+static int dm_mdio_post_probe(struct udevice *dev)
+{
+	struct mdio_perdev_priv *pdata = dev_get_uclass_priv(dev);
+
+	pdata->mii_bus = mdio_alloc();
+	pdata->mii_bus->read = mdio_read;
+	pdata->mii_bus->write = mdio_write;
+	pdata->mii_bus->reset = mdio_reset;
+	pdata->mii_bus->priv = dev;
+	strncpy(pdata->mii_bus->name, dev->name, MDIO_NAME_LEN);
+
+	return mdio_register(pdata->mii_bus);
+}
+
+static int dm_mdio_pre_remove(struct udevice *dev)
+{
+	struct mdio_perdev_priv *pdata = dev_get_uclass_priv(dev);
+	struct mdio_ops *ops = mdio_get_ops(dev);
+
+	if (ops->reset)
+		ops->reset(dev);
+	mdio_unregister(pdata->mii_bus);
+	mdio_free(pdata->mii_bus);
+
+	return 0;
+}
+
+struct phy_device *dm_mdio_phy_connect(struct udevice *dev, int addr,
+				       struct udevice *ethdev,
+				       phy_interface_t interface)
+{
+	struct mdio_perdev_priv *pdata = dev_get_uclass_priv(dev);
+
+	if (device_probe(dev))
+		return 0;
+
+	return phy_connect(pdata->mii_bus, addr, ethdev, interface);
+}
+
+UCLASS_DRIVER(mdio) = {
+	.id = UCLASS_MDIO,
+	.name = "mdio",
+	.post_bind  = dm_mdio_post_bind,
+	.post_probe = dm_mdio_post_probe,
+	.pre_remove = dm_mdio_pre_remove,
+	.per_device_auto_alloc_size = sizeof(struct mdio_perdev_priv),
+};
-- 
2.17.1



More information about the U-Boot mailing list