[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