Browse Source

net: introduce MDIO DM class 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.

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>
Alex Marginean 5 years ago
parent
commit
c3452b50c3
6 changed files with 184 additions and 0 deletions
  1. 5 0
      cmd/mdio.c
  2. 13 0
      drivers/net/Kconfig
  3. 1 0
      include/dm/uclass-id.h
  4. 49 0
      include/miiphy.h
  5. 1 0
      net/Makefile
  6. 115 0
      net/mdio-uclass.c

+ 5 - 0
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.

+ 13 - 0
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

+ 1 - 0
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 */

+ 49 - 0
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

+ 1 - 0
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

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