net: am65-cpsw: cpsw_mdio: Switch to proper DM_MDIO framework
Add a new Kconfig symbol MDIO_TI_CPSW for the CPSW MDIO
driver and build it with proper DM support if enabled.
If MDIO_TI_CPSW is not enabled then we continue to
behave like before.
Clean up MDIO custom handling in am65-cpsw and use
dm_eth_phy_connect() to get the PHY.
Signed-off-by: Roger Quadros <rogerq@kernel.org>
Tested-by: Ravi Gunasekaran <r-gunasekaran@ti.com>
diff --git a/drivers/net/ti/Kconfig b/drivers/net/ti/Kconfig
index c75f4186..72eccc9 100644
--- a/drivers/net/ti/Kconfig
+++ b/drivers/net/ti/Kconfig
@@ -45,7 +45,15 @@
imply MISC_INIT_R
imply MISC
imply SYSCON
+ imply MDIO_TI_CPSW
select PHYLIB
help
This driver supports TI K3 MCU CPSW Nuss Ethernet controller
in Texas Instruments K3 AM65x SoCs.
+
+config MDIO_TI_CPSW
+ bool "TI CPSW MDIO interface support"
+ depends on DM_MDIO
+ help
+ This driver supports the TI CPSW MDIO interface found in various
+ TI SoCs.
diff --git a/drivers/net/ti/Makefile b/drivers/net/ti/Makefile
index 0ce0cf2..30c4c4b 100644
--- a/drivers/net/ti/Makefile
+++ b/drivers/net/ti/Makefile
@@ -5,4 +5,5 @@
obj-$(CONFIG_DRIVER_TI_CPSW) += cpsw.o cpsw-common.o cpsw_mdio.o
obj-$(CONFIG_DRIVER_TI_EMAC) += davinci_emac.o
obj-$(CONFIG_DRIVER_TI_KEYSTONE_NET) += keystone_net.o cpsw_mdio.o
-obj-$(CONFIG_TI_AM65_CPSW_NUSS) += am65-cpsw-nuss.o cpsw_mdio.o
+obj-$(CONFIG_TI_AM65_CPSW_NUSS) += am65-cpsw-nuss.o
+obj-$(CONFIG_MDIO_TI_CPSW) += cpsw_mdio.o
diff --git a/drivers/net/ti/am65-cpsw-nuss.c b/drivers/net/ti/am65-cpsw-nuss.c
index 6da018c..d68ed67 100644
--- a/drivers/net/ti/am65-cpsw-nuss.c
+++ b/drivers/net/ti/am65-cpsw-nuss.c
@@ -31,8 +31,6 @@
#include <linux/printk.h>
#include <linux/soc/ti/ti-udma.h>
-#include "cpsw_mdio.h"
-
#define AM65_CPSW_CPSWNU_MAX_PORTS 9
#define AM65_CPSW_SS_BASE 0x0
@@ -113,7 +111,6 @@
struct udevice *dev;
fdt_addr_t ss_base;
fdt_addr_t cpsw_base;
- fdt_addr_t mdio_base;
fdt_addr_t ale_base;
struct clk fclk;
@@ -122,13 +119,8 @@
u32 port_num;
struct am65_cpsw_port ports[AM65_CPSW_CPSWNU_MAX_PORTS];
- struct mii_dev *bus;
u32 bus_freq;
- struct gpio_desc mdio_gpio_reset;
- u32 reset_delay_us;
- u32 reset_post_delay_us;
-
struct dma dma_tx;
struct dma dma_rx;
u32 rx_next;
@@ -140,13 +132,7 @@
struct udevice *dev;
struct am65_cpsw_common *cpsw_common;
u32 port_id;
-
struct phy_device *phydev;
- bool has_phy;
- ofnode phy_node;
- u32 phy_addr;
-
- bool mdio_manual_mode;
};
#ifdef PKTSIZE_ALIGN
@@ -622,111 +608,15 @@
.read_rom_hwaddr = am65_cpsw_read_rom_hwaddr,
};
-static const struct soc_attr k3_mdio_soc_data[] = {
- { .family = "AM62X", .revision = "SR1.0" },
- { .family = "AM64X", .revision = "SR1.0" },
- { .family = "AM64X", .revision = "SR2.0" },
- { .family = "AM65X", .revision = "SR1.0" },
- { .family = "AM65X", .revision = "SR2.0" },
- { .family = "J7200", .revision = "SR1.0" },
- { .family = "J7200", .revision = "SR2.0" },
- { .family = "J721E", .revision = "SR1.0" },
- { .family = "J721E", .revision = "SR1.1" },
- { .family = "J721S2", .revision = "SR1.0" },
- { /* sentinel */ },
-};
-
-static ofnode am65_cpsw_find_mdio(ofnode parent)
-{
- ofnode node;
-
- ofnode_for_each_subnode(node, parent)
- if (ofnode_device_is_compatible(node, "ti,cpsw-mdio"))
- return node;
-
- return ofnode_null();
-}
-
-static int am65_cpsw_mdio_setup(struct udevice *dev)
-{
- struct am65_cpsw_priv *priv = dev_get_priv(dev);
- struct am65_cpsw_common *cpsw_common = priv->cpsw_common;
- struct udevice *mdio_dev;
- ofnode mdio;
- int ret;
-
- mdio = am65_cpsw_find_mdio(dev_ofnode(cpsw_common->dev));
- if (!ofnode_valid(mdio))
- return 0;
-
- /*
- * The MDIO controller is represented in the DT binding by a
- * subnode of the MAC controller.
- *
- * We don't have a DM driver for the MDIO device yet, and thus any
- * pinctrl setting on its node will be ignored.
- *
- * However, we do need to make sure the pins states tied to the
- * MDIO node are configured properly. Fortunately, the core DM
- * does that for use when we get a device, so we can work around
- * that whole issue by just requesting a dummy MDIO driver to
- * probe, and our pins will get muxed.
- */
- ret = uclass_get_device_by_ofnode(UCLASS_MDIO, mdio, &mdio_dev);
- if (ret)
- return ret;
-
- return 0;
-}
-
-static int am65_cpsw_mdio_init(struct udevice *dev)
-{
- struct am65_cpsw_priv *priv = dev_get_priv(dev);
- struct am65_cpsw_common *cpsw_common = priv->cpsw_common;
- int ret;
-
- if (!priv->has_phy || cpsw_common->bus)
- return 0;
-
- if (IS_ENABLED(CONFIG_DM_GPIO)) {
- if (dm_gpio_is_valid(&cpsw_common->mdio_gpio_reset)) {
- dm_gpio_set_value(&cpsw_common->mdio_gpio_reset, 1);
- udelay(cpsw_common->reset_delay_us);
- dm_gpio_set_value(&cpsw_common->mdio_gpio_reset, 0);
- if (cpsw_common->reset_post_delay_us > 0)
- udelay(cpsw_common->reset_post_delay_us);
- }
- }
-
- ret = am65_cpsw_mdio_setup(dev);
- if (ret)
- return ret;
-
- cpsw_common->bus = cpsw_mdio_init(dev->name,
- cpsw_common->mdio_base,
- cpsw_common->bus_freq,
- clk_get_rate(&cpsw_common->fclk),
- priv->mdio_manual_mode);
- if (!cpsw_common->bus)
- return -EFAULT;
-
- return 0;
-}
-
static int am65_cpsw_phy_init(struct udevice *dev)
{
struct am65_cpsw_priv *priv = dev_get_priv(dev);
- struct am65_cpsw_common *cpsw_common = priv->cpsw_common;
struct eth_pdata *pdata = dev_get_plat(dev);
struct phy_device *phydev;
u32 supported = PHY_GBIT_FEATURES;
int ret;
- phydev = phy_connect(cpsw_common->bus,
- priv->phy_addr,
- priv->dev,
- pdata->phy_interface);
-
+ phydev = dm_eth_phy_connect(dev);
if (!phydev) {
dev_err(dev, "phy_connect() failed\n");
return -ENODEV;
@@ -740,13 +630,10 @@
}
phydev->advertising = phydev->supported;
- if (ofnode_valid(priv->phy_node))
- phydev->node = priv->phy_node;
-
priv->phydev = phydev;
ret = phy_config(phydev);
if (ret < 0)
- pr_err("phy_config() failed: %d", ret);
+ dev_err(dev, "phy_config() failed: %d", ret);
return ret;
}
@@ -755,8 +642,6 @@
{
struct eth_pdata *pdata = dev_get_plat(dev);
struct am65_cpsw_priv *priv = dev_get_priv(dev);
- struct ofnode_phandle_args out_args;
- int ret = 0;
dev_read_u32(dev, "reg", &priv->port_id);
@@ -771,28 +656,7 @@
dev_err(dev, "Port %u speed froced to %uMbit\n",
priv->port_id, pdata->max_speed);
- priv->has_phy = true;
- ret = ofnode_parse_phandle_with_args(dev_ofnode(dev), "phy-handle",
- NULL, 0, 0, &out_args);
- if (ret) {
- dev_err(dev, "can't parse phy-handle port %u (%d)\n",
- priv->port_id, ret);
- priv->has_phy = false;
- ret = 0;
- }
-
- priv->phy_node = out_args.node;
- if (priv->has_phy) {
- ret = ofnode_read_u32(priv->phy_node, "reg", &priv->phy_addr);
- if (ret) {
- dev_err(dev, "failed to get phy_addr port %u (%d)\n",
- priv->port_id, ret);
- goto out;
- }
- }
-
-out:
- return ret;
+ return 0;
}
static int am65_cpsw_port_probe(struct udevice *dev)
@@ -811,10 +675,6 @@
sprintf(portname, "%s%s", dev->parent->name, dev->name);
device_set_name(dev, portname);
- priv->mdio_manual_mode = false;
- if (soc_device_match(k3_mdio_soc_data))
- priv->mdio_manual_mode = true;
-
ret = am65_cpsw_ofdata_parse_phy(dev);
if (ret)
goto out;
@@ -823,13 +683,8 @@
if (ret)
goto out;
- ret = am65_cpsw_mdio_init(dev);
- if (ret)
- goto out;
-
ret = am65_cpsw_phy_init(dev);
- if (ret)
- goto out;
+
out:
return ret;
}
@@ -837,7 +692,7 @@
static int am65_cpsw_probe_nuss(struct udevice *dev)
{
struct am65_cpsw_common *cpsw_common = dev_get_priv(dev);
- ofnode ports_np, node, mdio_np;
+ ofnode ports_np, node;
int ret, i;
struct udevice *port_dev;
@@ -862,25 +717,6 @@
cpsw_common->cpsw_base = cpsw_common->ss_base + AM65_CPSW_CPSW_NU_BASE;
cpsw_common->ale_base = cpsw_common->cpsw_base +
AM65_CPSW_CPSW_NU_ALE_BASE;
- cpsw_common->mdio_base = cpsw_common->ss_base + AM65_CPSW_MDIO_BASE;
-
- if (IS_ENABLED(CONFIG_DM_GPIO)) {
- /* get bus level PHY reset GPIO details */
- mdio_np = dev_read_subnode(dev, "mdio");
- if (!ofnode_valid(mdio_np)) {
- ret = -ENOENT;
- goto out;
- }
-
- cpsw_common->reset_delay_us = ofnode_read_u32_default(mdio_np, "reset-delay-us",
- DEFAULT_GPIO_RESET_DELAY);
- cpsw_common->reset_post_delay_us = ofnode_read_u32_default(mdio_np,
- "reset-post-delay-us",
- 0);
- ret = gpio_request_by_name_nodev(mdio_np, "reset-gpios", 0,
- &cpsw_common->mdio_gpio_reset,
- GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
- }
ports_np = dev_read_subnode(dev, "ethernet-ports");
if (!ofnode_valid(ports_np)) {
@@ -940,12 +776,11 @@
dev_read_u32_default(dev, "bus_freq",
AM65_CPSW_MDIO_BUS_FREQ_DEF);
- dev_info(dev, "K3 CPSW: nuss_ver: 0x%08X cpsw_ver: 0x%08X ale_ver: 0x%08X Ports:%u mdio_freq:%u\n",
+ dev_info(dev, "K3 CPSW: nuss_ver: 0x%08X cpsw_ver: 0x%08X ale_ver: 0x%08X Ports:%u\n",
readl(cpsw_common->ss_base),
readl(cpsw_common->cpsw_base),
readl(cpsw_common->ale_base),
- cpsw_common->port_num,
- cpsw_common->bus_freq);
+ cpsw_common->port_num);
out:
power_domain_free(&cpsw_common->pwrdmn);
@@ -976,14 +811,3 @@
.plat_auto = sizeof(struct eth_pdata),
.flags = DM_FLAG_ALLOC_PRIV_DMA | DM_FLAG_OS_PREPARE,
};
-
-static const struct udevice_id am65_cpsw_mdio_ids[] = {
- { .compatible = "ti,cpsw-mdio" },
- { }
-};
-
-U_BOOT_DRIVER(am65_cpsw_mdio) = {
- .name = "am65_cpsw_mdio",
- .id = UCLASS_MDIO,
- .of_match = am65_cpsw_mdio_ids,
-};
diff --git a/drivers/net/ti/cpsw_mdio.c b/drivers/net/ti/cpsw_mdio.c
index 74cc956..f1b1eba 100644
--- a/drivers/net/ti/cpsw_mdio.c
+++ b/drivers/net/ti/cpsw_mdio.c
@@ -5,11 +5,15 @@
* Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/
*/
+#include <clk.h>
#include <common.h>
+#include <dm/device_compat.h>
#include <log.h>
#include <malloc.h>
+#include <phy.h>
#include <asm/io.h>
#include <miiphy.h>
+#include <soc.h>
#include <wait_bit.h>
#include <linux/bitops.h>
#include <linux/delay.h>
@@ -22,6 +26,7 @@
#define CONTROL_FAULT BIT(19)
#define CONTROL_FAULT_ENABLE BIT(18)
#define CONTROL_DIV_MASK GENMASK(15, 0)
+#define CONTROL_MAX_DIV CONTROL_DIV_MASK
#define MDIO_MAN_MDCLK_O BIT(2)
#define MDIO_MAN_OE BIT(1)
@@ -72,6 +77,8 @@
*/
#define CPSW_MDIO_TIMEOUT 100 /* msecs */
+#define CPSW_MDIO_DEF_BUS_FREQ 2200000 /* 2.2 MHz */
+
enum cpsw_mdio_manual {
MDIO_PIN = 0,
MDIO_OE,
@@ -82,8 +89,35 @@
struct cpsw_mdio_regs *regs;
struct mii_dev *bus;
int div;
+ bool manual_mode;
+ struct clk clk;
+ unsigned long bus_freq;
};
+static int cpsw_mdio_enable(struct cpsw_mdio *data)
+{
+ int ret;
+
+ /* set enable and clock divider */
+ writel(data->div | CONTROL_ENABLE, &data->regs->control);
+ ret = wait_for_bit_le32(&data->regs->control,
+ CONTROL_IDLE, false, CPSW_MDIO_TIMEOUT, true);
+ if (ret)
+ return ret;
+
+ /*
+ * 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.
+ */
+ mdelay(1);
+
+ return 0;
+}
+
static void cpsw_mdio_disable(struct cpsw_mdio *mdio)
{
u32 reg;
@@ -206,10 +240,16 @@
}
}
+#if defined(CONFIG_DM_MDIO)
+#define MII_TO_CPSW_MDIO(bus) (dev_get_priv((struct udevice *)(bus)->priv))
+#else
+#define MII_TO_CPSW_MDIO(bus) ((bus)->priv)
+#endif
+
static int cpsw_mdio_sw_read(struct mii_dev *bus, int phy_id,
int dev_addr, int phy_reg)
{
- struct cpsw_mdio *mdio = bus->priv;
+ struct cpsw_mdio *mdio = MII_TO_CPSW_MDIO(bus);
u32 reg, i;
u8 ack;
@@ -266,7 +306,7 @@
static int cpsw_mdio_sw_write(struct mii_dev *bus, int phy_id,
int dev_addr, int phy_reg, u16 phy_data)
{
- struct cpsw_mdio *mdio = bus->priv;
+ struct cpsw_mdio *mdio = MII_TO_CPSW_MDIO(bus);
if ((phy_reg & ~PHY_REG_MASK) || (phy_id & ~PHY_ID_MASK))
return -EINVAL;
@@ -316,7 +356,7 @@
static int cpsw_mdio_read(struct mii_dev *bus, int phy_id,
int dev_addr, int phy_reg)
{
- struct cpsw_mdio *mdio = bus->priv;
+ struct cpsw_mdio *mdio = MII_TO_CPSW_MDIO(bus);
int data, ret;
u32 reg;
@@ -342,7 +382,7 @@
static int cpsw_mdio_write(struct mii_dev *bus, int phy_id, int dev_addr,
int phy_reg, u16 data)
{
- struct cpsw_mdio *mdio = bus->priv;
+ struct cpsw_mdio *mdio = MII_TO_CPSW_MDIO(bus);
u32 reg;
int ret;
@@ -361,9 +401,10 @@
return cpsw_mdio_wait_for_user_access(mdio);
}
+#if !defined(CONFIG_MDIO_TI_CPSW)
u32 cpsw_mdio_get_alive(struct mii_dev *bus)
{
- struct cpsw_mdio *mdio = bus->priv;
+ struct cpsw_mdio *mdio = MII_TO_CPSW_MDIO(bus);
u32 val;
val = readl(&mdio->regs->alive);
@@ -396,22 +437,11 @@
else
cpsw_mdio->div = (fck_freq / bus_freq) - 1;
cpsw_mdio->div &= CONTROL_DIV_MASK;
-
- /* set enable and clock divider */
- writel(cpsw_mdio->div | CONTROL_ENABLE | CONTROL_FAULT |
- CONTROL_FAULT_ENABLE, &cpsw_mdio->regs->control);
- wait_for_bit_le32(&cpsw_mdio->regs->control,
- CONTROL_IDLE, false, CPSW_MDIO_TIMEOUT, true);
-
- /*
- * 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.
- */
- mdelay(1);
+ ret = cpsw_mdio_enable(cpsw_mdio);
+ if (ret) {
+ debug("mdio_enable failed: %d\n", ret);
+ goto free_bus;
+ }
if (manual_mode) {
cpsw_mdio->bus->read = cpsw_mdio_sw_read;
@@ -452,3 +482,129 @@
mdio_free(bus);
free(mdio);
}
+
+#else
+
+static int cpsw_mdio_init_clk(struct cpsw_mdio *data)
+{
+ u32 mdio_in, div;
+
+ mdio_in = clk_get_rate(&data->clk);
+ div = (mdio_in / data->bus_freq) - 1;
+ if (div > CONTROL_MAX_DIV)
+ div = CONTROL_MAX_DIV;
+
+ data->div = div;
+ return cpsw_mdio_enable(data);
+}
+
+static int cpsw_mdio_bus_read(struct udevice *dev, int addr,
+ int devad, int reg)
+{
+ struct mdio_perdev_priv *pdata = (dev) ? dev_get_uclass_priv(dev) :
+ NULL;
+ struct cpsw_mdio *priv = dev_get_priv(dev);
+
+ if (pdata && pdata->mii_bus) {
+ if (priv->manual_mode)
+ return cpsw_mdio_sw_read(pdata->mii_bus, addr, devad, reg);
+ else
+ return cpsw_mdio_read(pdata->mii_bus, addr, devad, reg);
+ }
+
+ return -1;
+}
+
+static int cpsw_mdio_bus_write(struct udevice *dev, int addr,
+ int devad, int reg, u16 val)
+{
+ struct mdio_perdev_priv *pdata = (dev) ? dev_get_uclass_priv(dev) :
+ NULL;
+ struct cpsw_mdio *priv = dev_get_priv(dev);
+
+ if (pdata && pdata->mii_bus) {
+ if (priv->manual_mode)
+ return cpsw_mdio_sw_write(pdata->mii_bus, addr, devad, reg, val);
+ else
+ return cpsw_mdio_write(pdata->mii_bus, addr, devad, reg, val);
+ }
+
+ return -1;
+}
+
+static const struct mdio_ops cpsw_mdio_ops = {
+ .read = cpsw_mdio_bus_read,
+ .write = cpsw_mdio_bus_write,
+};
+
+static const struct soc_attr k3_mdio_soc_data[] = {
+ { .family = "AM62X", .revision = "SR1.0" },
+ { .family = "AM64X", .revision = "SR1.0" },
+ { .family = "AM64X", .revision = "SR2.0" },
+ { .family = "AM65X", .revision = "SR1.0" },
+ { .family = "AM65X", .revision = "SR2.0" },
+ { .family = "J7200", .revision = "SR1.0" },
+ { .family = "J7200", .revision = "SR2.0" },
+ { .family = "J721E", .revision = "SR1.0" },
+ { .family = "J721E", .revision = "SR1.1" },
+ { .family = "J721S2", .revision = "SR1.0" },
+ { /* sentinel */ },
+};
+
+static const struct udevice_id cpsw_mdio_ids[] = {
+ { .compatible = "ti,davinci_mdio", },
+ { .compatible = "ti,cpsw-mdio", },
+ { /* sentinel */ },
+};
+
+static int cpsw_mdio_probe(struct udevice *dev)
+{
+ struct cpsw_mdio *priv = dev_get_priv(dev);
+ int ret;
+
+ if (!priv) {
+ dev_err(dev, "dev_get_priv(dev %p) = NULL\n", dev);
+ return -ENOMEM;
+ }
+
+ priv->regs = dev_remap_addr(dev);
+
+ if (soc_device_match(k3_mdio_soc_data))
+ priv->manual_mode = true;
+
+ ret = clk_get_by_name(dev, "fck", &priv->clk);
+ if (ret) {
+ dev_err(dev, "failed to get clock %d\n", ret);
+ return ret;
+ }
+
+ priv->bus_freq = dev_read_u32_default(dev, "bus_freq",
+ CPSW_MDIO_DEF_BUS_FREQ);
+ ret = cpsw_mdio_init_clk(priv);
+ if (ret) {
+ dev_err(dev, "init clock failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cpsw_mdio_remove(struct udevice *dev)
+{
+ struct cpsw_mdio *priv = dev_get_priv(dev);
+
+ cpsw_mdio_disable(priv);
+
+ return 0;
+}
+
+U_BOOT_DRIVER(cpsw_mdio) = {
+ .name = "cpsw_mdio",
+ .id = UCLASS_MDIO,
+ .of_match = cpsw_mdio_ids,
+ .probe = cpsw_mdio_probe,
+ .remove = cpsw_mdio_remove,
+ .ops = &cpsw_mdio_ops,
+ .priv_auto = sizeof(struct cpsw_mdio),
+};
+#endif /* CONFIG_MDIO_TI_CPSW */
diff --git a/drivers/net/ti/cpsw_mdio.h b/drivers/net/ti/cpsw_mdio.h
index ddf65a4..240c972 100644
--- a/drivers/net/ti/cpsw_mdio.h
+++ b/drivers/net/ti/cpsw_mdio.h
@@ -10,9 +10,11 @@
struct cpsw_mdio;
+#if !defined(CONFIG_MDIO_TI_CPSW)
struct mii_dev *cpsw_mdio_init(const char *name, phys_addr_t mdio_base,
u32 bus_freq, int fck_freq, bool manual_mode);
void cpsw_mdio_free(struct mii_dev *bus);
u32 cpsw_mdio_get_alive(struct mii_dev *bus);
+#endif /* CONFIG_MDIO_TI_CPSW */
#endif /* CPSW_MDIO_H_ */