[PATCH 1/7] net: davinci_mdio: separate driver

Sascha Hauer s.hauer at pengutronix.de
Fri Nov 8 05:14:59 PST 2024


The davinci MDIO driver can be reused for the CPSW found on TI K3 SoCs
which needs a new ethernet driver. Separate the MDIO driver from the
CPSW driver to make it usable with other ethernet drivers.

Signed-off-by: Sascha Hauer <s.hauer at pengutronix.de>
---
 drivers/net/Kconfig        |   5 ++
 drivers/net/Makefile       |   1 +
 drivers/net/cpsw.c         | 178 ------------------------------------------
 drivers/net/davinci_mdio.c | 188 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 194 insertions(+), 178 deletions(-)

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 13e9ff6924..7f0d277548 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -53,8 +53,13 @@ config DRIVER_NET_CS8900
 config DRIVER_NET_CPSW
 	bool "CPSW ethernet driver"
 	depends on ARCH_OMAP
+	select DRIVER_NET_TI_DAVINCI_MDIO
 	select PHYLIB
 
+config DRIVER_NET_TI_DAVINCI_MDIO
+	bool "TI Davinci MDIO driver"
+	depends on ARCH_OMAP3 || COMPILE_TEST
+
 config DRIVER_NET_DAVINCI_EMAC
 	bool "TI Davinci/OMAP EMAC ethernet driver"
 	depends on ARCH_OMAP3
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 207345cfa3..b451813f7c 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_DRIVER_NET_BCMGENET)	+= bcmgenet.o
 obj-$(CONFIG_DRIVER_NET_CS8900)		+= cs8900.o
 obj-$(CONFIG_DRIVER_NET_CPSW)		+= cpsw.o
 obj-$(CONFIG_DRIVER_NET_DAVINCI_EMAC)	+= davinci_emac.o
+obj-$(CONFIG_DRIVER_NET_TI_DAVINCI_MDIO) += davinci_mdio.o
 obj-$(CONFIG_DRIVER_NET_DESIGNWARE)	+= designware.o
 obj-$(CONFIG_DRIVER_NET_DESIGNWARE_GENERIC) += designware_generic.o
 obj-$(CONFIG_DRIVER_NET_DESIGNWARE_SOCFPGA) += designware_socfpga.o
diff --git a/drivers/net/cpsw.c b/drivers/net/cpsw.c
index 3e4f6502f9..b11930fdd0 100644
--- a/drivers/net/cpsw.c
+++ b/drivers/net/cpsw.c
@@ -28,8 +28,6 @@
 #define CPSW_VERSION_2		0x19010c
 
 #define BITMASK(bits)		((1 << (bits)) - 1)
-#define PHY_REG_MASK		0x1f
-#define PHY_ID_MASK		0x1f
 #define NUM_DESCS		(PKTBUFSRX * 2)
 #define PKT_MIN			60
 #define PKT_MAX			(1500 + 14 + 4 + 4)
@@ -60,34 +58,6 @@
 
 #define SLIVER_SIZE		0x40
 
-struct cpsw_mdio_regs {
-	u32	version;
-	u32	control;
-#define CONTROL_IDLE		(1 << 31)
-#define CONTROL_ENABLE		(1 << 30)
-
-	u32	alive;
-	u32	link;
-	u32	linkintraw;
-	u32	linkintmasked;
-	u32	__reserved_0[2];
-	u32	userintraw;
-	u32	userintmasked;
-	u32	userintmaskset;
-	u32	userintmaskclr;
-	u32	__reserved_1[20];
-
-	struct {
-		u32 access;
-		u32 physel;
-#define USERACCESS_GO		(1 << 31)
-#define USERACCESS_WRITE	(1 << 30)
-#define USERACCESS_ACK		(1 << 29)
-#define USERACCESS_READ		(0)
-#define USERACCESS_DATA		(0xffff)
-	} user[0];
-};
-
 struct cpsw_regs {
 	u32	id_ver;
 	u32	control;
@@ -226,12 +196,6 @@ struct cpsw_priv {
 	struct cpsw_slave		*slaves;
 };
 
-struct cpsw_mdio_priv {
-	struct device			*dev;
-	struct mii_bus			miibus;
-	struct cpsw_mdio_regs		*mdio_regs;
-};
-
 static int cpsw_ale_get_field(u32 *ale_entry, u32 start, u32 bits)
 {
 	int idx;
@@ -524,148 +488,6 @@ static inline void cpsw_ale_port_state(struct cpsw_priv *priv, int port,
 	writel(tmp, priv->ale_regs + offset);
 }
 
-/* wait until hardware is ready for another user access */
-static u32 wait_for_user_access(struct cpsw_mdio_priv *priv)
-{
-	struct cpsw_mdio_regs *mdio_regs = priv->mdio_regs;
-	u32 tmp;
-	uint64_t start = get_time_ns();
-
-	do {
-		tmp = readl(&mdio_regs->user[0].access);
-
-		if (!(tmp & USERACCESS_GO))
-			break;
-
-		if (is_timeout(start, 100 * MSECOND)) {
-			dev_err(priv->dev, "timeout waiting for user access\n");
-			break;
-		}
-	} while (1);
-
-	return tmp;
-}
-
-static int cpsw_mdio_read(struct mii_bus *bus, int phy_id, int phy_reg)
-{
-	struct cpsw_mdio_priv *priv = bus->priv;
-	struct cpsw_mdio_regs *mdio_regs = priv->mdio_regs;
-
-	u32 tmp;
-
-	if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK)
-		return -EINVAL;
-
-	wait_for_user_access(priv);
-
-	tmp = (USERACCESS_GO | USERACCESS_READ | (phy_reg << 21) |
-	       (phy_id << 16));
-	writel(tmp, &mdio_regs->user[0].access);
-
-	tmp = wait_for_user_access(priv);
-
-	return (tmp & USERACCESS_ACK) ? (tmp & USERACCESS_DATA) : -1;
-}
-
-static int cpsw_mdio_write(struct mii_bus *bus, int phy_id, int phy_reg, u16 value)
-{
-	struct cpsw_mdio_priv *priv = bus->priv;
-	struct cpsw_mdio_regs *mdio_regs = priv->mdio_regs;
-	u32 tmp;
-
-	if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK)
-		return -EINVAL;
-
-	wait_for_user_access(priv);
-	tmp = (USERACCESS_GO | USERACCESS_WRITE | (phy_reg << 21) |
-		   (phy_id << 16) | (value & USERACCESS_DATA));
-	writel(tmp, &mdio_regs->user[0].access);
-	wait_for_user_access(priv);
-
-	return 0;
-}
-
-static int cpsw_mdio_probe(struct device *dev)
-{
-	struct resource *iores;
-	struct cpsw_mdio_priv *priv;
-	uint64_t start;
-	uint32_t phy_mask;
-	int ret;
-
-	priv = xzalloc(sizeof(*priv));
-
-	/* If we can't request I/O memory region, we'll assume parent did
-	 * it for us
-	 */
-	iores = dev_request_mem_resource(dev, 0);
-	if (IS_ERR(iores) && PTR_ERR(iores) == -EBUSY)
-		iores = dev_get_resource(dev, IORESOURCE_MEM, 0);
-	if (IS_ERR(iores))
-		return PTR_ERR(iores);
-	priv->mdio_regs = IOMEM(iores->start);
-	priv->miibus.read = cpsw_mdio_read;
-	priv->miibus.write = cpsw_mdio_write;
-	priv->miibus.priv = priv;
-	priv->miibus.parent = dev;
-
-	/*
-	 * set enable and clock divider
-	 *
-	 * FIXME: Use a clock to calculate the divider
-	 */
-	writel(0xff | CONTROL_ENABLE, &priv->mdio_regs->control);
-
-	/*
-	 * wait for scan logic to settle:
-	 * the scan time consists of (a) a large fixed component, and (b) a
-	 * small component that varies with the mii bus frequency.  These
-	 * were estimated using measurements at 1.1 and 2.2 MHz on tnetv107x
-	 * silicon.  Since the effect of (b) was found to be largely
-	 * negligible, we keep things simple here.
-	 */
-	udelay(2000);
-
-	start = get_time_ns();
-	while (1) {
-		phy_mask = readl(&priv->mdio_regs->alive);
-		if (phy_mask) {
-			dev_info(dev, "detected phy mask 0x%x\n", phy_mask);
-			phy_mask = ~phy_mask;
-			break;
-		}
-		if (is_timeout(start, 256 * MSECOND)) {
-			dev_err(dev, "no live phy, scanning all\n");
-			phy_mask = 0;
-			break;
-		}
-	}
-
-	priv->miibus.phy_mask = phy_mask;
-
-	ret = mdiobus_register(&priv->miibus);
-	if (ret)
-		return ret;
-
-	return 0;
-}
-
-static __maybe_unused struct of_device_id cpsw_mdio_dt_ids[] = {
-	{
-		.compatible = "ti,cpsw-mdio",
-	}, {
-		/* sentinel */
-	}
-};
-MODULE_DEVICE_TABLE(of, cpsw_mdio_dt_ids);
-
-static struct driver cpsw_mdio_driver = {
-	.name   = "cpsw-mdio",
-	.probe  = cpsw_mdio_probe,
-	.of_compatible = DRV_OF_COMPAT(cpsw_mdio_dt_ids),
-};
-coredevice_platform_driver(cpsw_mdio_driver);
-
 static inline void soft_reset(struct cpsw_priv *priv, void *reg)
 {
 	int ret;
diff --git a/drivers/net/davinci_mdio.c b/drivers/net/davinci_mdio.c
new file mode 100644
index 0000000000..c5cd28fba4
--- /dev/null
+++ b/drivers/net/davinci_mdio.c
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * DaVinci MDIO Module driver
+ */
+#include <driver.h>
+#include <linux/phy.h>
+#include <clock.h>
+#include <xfuncs.h>
+#include <io.h>
+
+#define PHY_REG_MASK		0x1f
+#define PHY_ID_MASK		0x1f
+
+struct cpsw_mdio_regs {
+	u32	version;
+	u32	control;
+#define CONTROL_IDLE		(1 << 31)
+#define CONTROL_ENABLE		(1 << 30)
+
+	u32	alive;
+	u32	link;
+	u32	linkintraw;
+	u32	linkintmasked;
+	u32	__reserved_0[2];
+	u32	userintraw;
+	u32	userintmasked;
+	u32	userintmaskset;
+	u32	userintmaskclr;
+	u32	__reserved_1[20];
+
+	struct {
+		u32 access;
+		u32 physel;
+#define USERACCESS_GO		(1 << 31)
+#define USERACCESS_WRITE	(1 << 30)
+#define USERACCESS_ACK		(1 << 29)
+#define USERACCESS_READ		(0)
+#define USERACCESS_DATA		(0xffff)
+	} user[0];
+};
+
+struct cpsw_mdio_priv {
+	struct device			*dev;
+	struct mii_bus			miibus;
+	struct cpsw_mdio_regs		*mdio_regs;
+};
+
+/* wait until hardware is ready for another user access */
+static u32 wait_for_user_access(struct cpsw_mdio_priv *priv)
+{
+	struct cpsw_mdio_regs *mdio_regs = priv->mdio_regs;
+	u32 tmp;
+	uint64_t start = get_time_ns();
+
+	do {
+		tmp = readl(&mdio_regs->user[0].access);
+
+		if (!(tmp & USERACCESS_GO))
+			break;
+
+		if (is_timeout(start, 100 * MSECOND)) {
+			dev_err(priv->dev, "timeout waiting for user access\n");
+			break;
+		}
+	} while (1);
+
+	return tmp;
+}
+
+static int cpsw_mdio_read(struct mii_bus *bus, int phy_id, int phy_reg)
+{
+	struct cpsw_mdio_priv *priv = bus->priv;
+	struct cpsw_mdio_regs *mdio_regs = priv->mdio_regs;
+
+	u32 tmp;
+
+	if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK)
+		return -EINVAL;
+
+	wait_for_user_access(priv);
+
+	tmp = (USERACCESS_GO | USERACCESS_READ | (phy_reg << 21) |
+	       (phy_id << 16));
+	writel(tmp, &mdio_regs->user[0].access);
+
+	tmp = wait_for_user_access(priv);
+
+	return (tmp & USERACCESS_ACK) ? (tmp & USERACCESS_DATA) : -1;
+}
+
+static int cpsw_mdio_write(struct mii_bus *bus, int phy_id, int phy_reg, u16 value)
+{
+	struct cpsw_mdio_priv *priv = bus->priv;
+	struct cpsw_mdio_regs *mdio_regs = priv->mdio_regs;
+	u32 tmp;
+
+	if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK)
+		return -EINVAL;
+
+	wait_for_user_access(priv);
+	tmp = (USERACCESS_GO | USERACCESS_WRITE | (phy_reg << 21) |
+		   (phy_id << 16) | (value & USERACCESS_DATA));
+	writel(tmp, &mdio_regs->user[0].access);
+	wait_for_user_access(priv);
+
+	return 0;
+}
+
+static int cpsw_mdio_probe(struct device *dev)
+{
+	struct resource *iores;
+	struct cpsw_mdio_priv *priv;
+	uint64_t start;
+	uint32_t phy_mask;
+	int ret;
+
+	priv = xzalloc(sizeof(*priv));
+
+	/* If we can't request I/O memory region, we'll assume parent did
+	 * it for us
+	 */
+	iores = dev_request_mem_resource(dev, 0);
+	if (IS_ERR(iores) && PTR_ERR(iores) == -EBUSY)
+		iores = dev_get_resource(dev, IORESOURCE_MEM, 0);
+	if (IS_ERR(iores))
+		return PTR_ERR(iores);
+	priv->mdio_regs = IOMEM(iores->start);
+	priv->miibus.read = cpsw_mdio_read;
+	priv->miibus.write = cpsw_mdio_write;
+	priv->miibus.priv = priv;
+	priv->miibus.parent = dev;
+
+	/*
+	 * set enable and clock divider
+	 *
+	 * FIXME: Use a clock to calculate the divider
+	 */
+	writel(0xff | CONTROL_ENABLE, &priv->mdio_regs->control);
+
+	/*
+	 * wait for scan logic to settle:
+	 * the scan time consists of (a) a large fixed component, and (b) a
+	 * small component that varies with the mii bus frequency.  These
+	 * were estimated using measurements at 1.1 and 2.2 MHz on tnetv107x
+	 * silicon.  Since the effect of (b) was found to be largely
+	 * negligible, we keep things simple here.
+	 */
+	udelay(2000);
+
+	start = get_time_ns();
+	while (1) {
+		phy_mask = readl(&priv->mdio_regs->alive);
+		if (phy_mask) {
+			dev_info(dev, "detected phy mask 0x%x\n", phy_mask);
+			phy_mask = ~phy_mask;
+			break;
+		}
+		if (is_timeout(start, 256 * MSECOND)) {
+			dev_err(dev, "no live phy, scanning all\n");
+			phy_mask = 0;
+			break;
+		}
+	}
+
+	priv->miibus.phy_mask = phy_mask;
+
+	ret = mdiobus_register(&priv->miibus);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static __maybe_unused struct of_device_id cpsw_mdio_dt_ids[] = {
+	{
+		.compatible = "ti,cpsw-mdio",
+	}, {
+		/* sentinel */
+	}
+};
+MODULE_DEVICE_TABLE(of, cpsw_mdio_dt_ids);
+
+static struct driver cpsw_mdio_driver = {
+	.name   = "cpsw-mdio",
+	.probe  = cpsw_mdio_probe,
+	.of_compatible = DRV_OF_COMPAT(cpsw_mdio_dt_ids),
+};
+coredevice_platform_driver(cpsw_mdio_driver);

-- 
2.39.5




More information about the barebox mailing list