diff options
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/Kconfig | 23 | ||||
-rw-r--r-- | drivers/net/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/macb.c | 8 | ||||
-rw-r--r-- | drivers/net/mdio_sandbox.c | 92 | ||||
-rw-r--r-- | drivers/net/mscc_eswitch/Makefile | 10 | ||||
-rw-r--r-- | drivers/net/mscc_eswitch/jr2_switch.c | 119 | ||||
-rw-r--r-- | drivers/net/mscc_eswitch/luton_switch.c | 101 | ||||
-rw-r--r-- | drivers/net/mscc_eswitch/mscc_miim.c | 28 | ||||
-rw-r--r-- | drivers/net/mscc_eswitch/mscc_miim.h | 14 | ||||
-rw-r--r-- | drivers/net/mscc_eswitch/ocelot_switch.c | 104 | ||||
-rw-r--r-- | drivers/net/mscc_eswitch/serval_switch.c | 103 | ||||
-rw-r--r-- | drivers/net/mscc_eswitch/servalt_switch.c | 102 | ||||
-rw-r--r-- | drivers/net/phy/cortina.c | 5 | ||||
-rw-r--r-- | drivers/net/phy/ti.c | 16 | ||||
-rw-r--r-- | drivers/net/ti/Kconfig | 8 | ||||
-rw-r--r-- | drivers/net/ti/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/ti/am65-cpsw-nuss.c | 792 | ||||
-rw-r--r-- | drivers/net/ti/cpsw_mdio.c | 4 | ||||
-rw-r--r-- | drivers/net/ti/cpsw_mdio.h | 2 |
19 files changed, 998 insertions, 535 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index e6a4fdf30e..635f8d72c2 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -11,6 +11,29 @@ 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. + +config MDIO_SANDBOX + depends on DM_MDIO && SANDBOX + default y + bool "Sandbox: Mocked MDIO driver" + help + This driver implements dummy read/write/reset MDIO functions mimicking + a bus with a single PHY. + + This driver is used in for testing in test/dm/mdio.c + menuconfig NETDEVICES bool "Network device support" depends on NET diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 8d02a37896..40038427db 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -77,3 +77,4 @@ obj-y += ti/ obj-$(CONFIG_MEDIATEK_ETH) += mtk_eth.o obj-y += mscc_eswitch/ obj-$(CONFIG_HIGMACV300_ETH) += higmacv300.o +obj-$(CONFIG_MDIO_SANDBOX) += mdio_sandbox.o diff --git a/drivers/net/macb.c b/drivers/net/macb.c index c5560a7111..a7eddd647d 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -613,10 +613,12 @@ static int macb_phy_init(struct macb_device *macb, const char *name) /* First check for GMAC and that it is GiB capable */ if (gem_is_gigabit_capable(macb)) { - lpa = macb_mdio_read(macb, MII_STAT1000); + lpa = macb_mdio_read(macb, MII_LPA); - if (lpa & (LPA_1000FULL | LPA_1000HALF)) { - duplex = ((lpa & LPA_1000FULL) ? 1 : 0); + if (lpa & (LPA_1000FULL | LPA_1000HALF | LPA_1000XFULL | + LPA_1000XHALF)) { + duplex = ((lpa & (LPA_1000FULL | LPA_1000XFULL)) ? + 1 : 0); printf("%s: link up, 1000Mbps %s-duplex (lpa: 0x%04x)\n", name, diff --git a/drivers/net/mdio_sandbox.c b/drivers/net/mdio_sandbox.c new file mode 100644 index 0000000000..07515e078c --- /dev/null +++ b/drivers/net/mdio_sandbox.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2019 + * Alex Marginean, NXP + */ + +#include <dm.h> +#include <errno.h> +#include <miiphy.h> + +#define SANDBOX_PHY_ADDR 5 +#define SANDBOX_PHY_REG 0 + +struct mdio_sandbox_priv { + int enabled; + u16 reg; +}; + +static int mdio_sandbox_read(struct udevice *dev, int addr, int devad, int reg) +{ + struct mdio_sandbox_priv *priv = dev_get_priv(dev); + + if (!priv->enabled) + return -ENODEV; + + if (addr != SANDBOX_PHY_ADDR) + return -ENODEV; + if (devad != MDIO_DEVAD_NONE) + return -ENODEV; + if (reg != SANDBOX_PHY_REG) + return -ENODEV; + + return priv->reg; +} + +static int mdio_sandbox_write(struct udevice *dev, int addr, int devad, int reg, + u16 val) +{ + struct mdio_sandbox_priv *priv = dev_get_priv(dev); + + if (!priv->enabled) + return -ENODEV; + + if (addr != SANDBOX_PHY_ADDR) + return -ENODEV; + if (devad != MDIO_DEVAD_NONE) + return -ENODEV; + if (reg != SANDBOX_PHY_REG) + return -ENODEV; + + priv->reg = val; + + return 0; +} + +static int mdio_sandbox_reset(struct udevice *dev) +{ + struct mdio_sandbox_priv *priv = dev_get_priv(dev); + + priv->reg = 0; + + return 0; +} + +static const struct mdio_ops mdio_sandbox_ops = { + .read = mdio_sandbox_read, + .write = mdio_sandbox_write, + .reset = mdio_sandbox_reset, +}; + +static int mdio_sandbox_probe(struct udevice *dev) +{ + struct mdio_sandbox_priv *priv = dev_get_priv(dev); + + priv->enabled = 1; + + return 0; +} + +static const struct udevice_id mdio_sandbox_ids[] = { + { .compatible = "sandbox,mdio" }, + { } +}; + +U_BOOT_DRIVER(mdio_sandbox) = { + .name = "mdio_sandbox", + .id = UCLASS_MDIO, + .of_match = mdio_sandbox_ids, + .probe = mdio_sandbox_probe, + .ops = &mdio_sandbox_ops, + .priv_auto_alloc_size = sizeof(struct mdio_sandbox_priv), +}; diff --git a/drivers/net/mscc_eswitch/Makefile b/drivers/net/mscc_eswitch/Makefile index 02f39a76bb..d583fe9fc4 100644 --- a/drivers/net/mscc_eswitch/Makefile +++ b/drivers/net/mscc_eswitch/Makefile @@ -1,6 +1,6 @@ -obj-$(CONFIG_MSCC_OCELOT_SWITCH) += ocelot_switch.o mscc_xfer.o mscc_mac_table.o -obj-$(CONFIG_MSCC_LUTON_SWITCH) += luton_switch.o mscc_xfer.o mscc_mac_table.o -obj-$(CONFIG_MSCC_JR2_SWITCH) += jr2_switch.o mscc_xfer.o -obj-$(CONFIG_MSCC_SERVALT_SWITCH) += servalt_switch.o mscc_xfer.o -obj-$(CONFIG_MSCC_SERVAL_SWITCH) += serval_switch.o mscc_xfer.o mscc_mac_table.o +obj-$(CONFIG_MSCC_OCELOT_SWITCH) += ocelot_switch.o mscc_xfer.o mscc_mac_table.o mscc_miim.o +obj-$(CONFIG_MSCC_LUTON_SWITCH) += luton_switch.o mscc_xfer.o mscc_mac_table.o mscc_miim.o +obj-$(CONFIG_MSCC_JR2_SWITCH) += jr2_switch.o mscc_xfer.o mscc_miim.o +obj-$(CONFIG_MSCC_SERVALT_SWITCH) += servalt_switch.o mscc_xfer.o mscc_miim.o +obj-$(CONFIG_MSCC_SERVAL_SWITCH) += serval_switch.o mscc_xfer.o mscc_mac_table.o mscc_miim.o diff --git a/drivers/net/mscc_eswitch/jr2_switch.c b/drivers/net/mscc_eswitch/jr2_switch.c index 60d408f1c7..665517775e 100644 --- a/drivers/net/mscc_eswitch/jr2_switch.c +++ b/drivers/net/mscc_eswitch/jr2_switch.c @@ -17,20 +17,7 @@ #include <dt-bindings/mscc/jr2_data.h> #include "mscc_xfer.h" - -#define GCB_MIIM_MII_STATUS 0x0 -#define GCB_MIIM_STAT_BUSY BIT(3) -#define GCB_MIIM_MII_CMD 0x8 -#define GCB_MIIM_MII_CMD_SCAN BIT(0) -#define GCB_MIIM_MII_CMD_OPR_WRITE BIT(1) -#define GCB_MIIM_MII_CMD_OPR_READ BIT(2) -#define GCB_MIIM_MII_CMD_SINGLE_SCAN BIT(3) -#define GCB_MIIM_MII_CMD_WRDATA(x) ((x) << 4) -#define GCB_MIIM_MII_CMD_REGAD(x) ((x) << 20) -#define GCB_MIIM_MII_CMD_PHYAD(x) ((x) << 25) -#define GCB_MIIM_MII_CMD_VLD BIT(31) -#define GCB_MIIM_DATA 0xC -#define GCB_MIIM_DATA_ERROR (0x3 << 16) +#include "mscc_miim.h" #define ANA_AC_RAM_CTRL_RAM_INIT 0x94358 #define ANA_AC_STAT_GLOBAL_CFG_PORT_RESET 0x94370 @@ -279,13 +266,6 @@ struct jr2_private { struct jr2_phy_port_t ports[MAX_PORT]; }; -struct jr2_miim_dev { - void __iomem *regs; - phys_addr_t miim_base; - unsigned long miim_size; - struct mii_dev *bus; -}; - static const unsigned long jr2_regs_qs[] = { [MSCC_QS_XTR_RD] = 0x8, [MSCC_QS_XTR_FLUSH] = 0x18, @@ -294,99 +274,9 @@ static const unsigned long jr2_regs_qs[] = { [MSCC_QS_INJ_CTRL] = 0x34, }; -static struct jr2_miim_dev miim[JR2_MIIM_BUS_COUNT]; +static struct mscc_miim_dev miim[JR2_MIIM_BUS_COUNT]; static int miim_count = -1; -static int mscc_miim_wait_ready(struct jr2_miim_dev *miim) -{ - unsigned long deadline; - u32 val; - - deadline = timer_get_us() + 250000; - - do { - val = readl(miim->regs + GCB_MIIM_MII_STATUS); - } while (timer_get_us() <= deadline && (val & GCB_MIIM_STAT_BUSY)); - - if (val & GCB_MIIM_STAT_BUSY) - return -ETIMEDOUT; - - return 0; -} - -static int mscc_miim_read(struct mii_dev *bus, int addr, int devad, int reg) -{ - struct jr2_miim_dev *miim = (struct jr2_miim_dev *)bus->priv; - u32 val; - int ret; - - ret = mscc_miim_wait_ready(miim); - if (ret) - goto out; - - writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) | - GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_OPR_READ, - miim->regs + GCB_MIIM_MII_CMD); - - ret = mscc_miim_wait_ready(miim); - if (ret) - goto out; - - val = readl(miim->regs + GCB_MIIM_DATA); - if (val & GCB_MIIM_DATA_ERROR) { - ret = -EIO; - goto out; - } - - ret = val & 0xFFFF; - out: - return ret; -} - -static int mscc_miim_write(struct mii_dev *bus, int addr, int devad, int reg, - u16 val) -{ - struct jr2_miim_dev *miim = (struct jr2_miim_dev *)bus->priv; - int ret; - - ret = mscc_miim_wait_ready(miim); - if (ret < 0) - goto out; - - writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) | - GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_WRDATA(val) | - GCB_MIIM_MII_CMD_OPR_WRITE, miim->regs + GCB_MIIM_MII_CMD); - - out: - return ret; -} - -static struct mii_dev *jr2_mdiobus_init(phys_addr_t miim_base, - unsigned long miim_size) -{ - struct mii_dev *bus; - - bus = mdio_alloc(); - if (!bus) - return NULL; - - ++miim_count; - sprintf(bus->name, "miim-bus%d", miim_count); - - miim[miim_count].regs = ioremap(miim_base, miim_size); - miim[miim_count].miim_base = miim_base; - miim[miim_count].miim_size = miim_size; - bus->priv = &miim[miim_count]; - bus->read = mscc_miim_read; - bus->write = mscc_miim_write; - - if (mdio_register(bus)) - return NULL; - - miim[miim_count].bus = bus; - return bus; -} - static void jr2_cpu_capture_setup(struct jr2_private *priv) { /* ASM: No preamble and IFH prefix on CPU injected frames */ @@ -973,7 +863,7 @@ static int jr2_probe(struct udevice *dev) } /* Initialize miim buses */ - memset(&miim, 0x0, sizeof(struct jr2_miim_dev) * JR2_MIIM_BUS_COUNT); + memset(&miim, 0x0, sizeof(struct mscc_miim_dev) * JR2_MIIM_BUS_COUNT); /* iterate all the ports and find out on which bus they are */ i = 0; @@ -1008,7 +898,8 @@ static int jr2_probe(struct udevice *dev) /* If the bus is new then create a new bus */ if (!get_mdiobus(addr_base, addr_size)) priv->bus[miim_count] = - jr2_mdiobus_init(addr_base, addr_size); + mscc_mdiobus_init(miim, &miim_count, addr_base, + addr_size); /* Connect mdio bus with the port */ bus = get_mdiobus(addr_base, addr_size); diff --git a/drivers/net/mscc_eswitch/luton_switch.c b/drivers/net/mscc_eswitch/luton_switch.c index 94852b06e7..dffe81d873 100644 --- a/drivers/net/mscc_eswitch/luton_switch.c +++ b/drivers/net/mscc_eswitch/luton_switch.c @@ -17,18 +17,7 @@ #include "mscc_xfer.h" #include "mscc_mac_table.h" - -#define GCB_MIIM_MII_STATUS 0x0 -#define GCB_MIIM_STAT_BUSY BIT(3) -#define GCB_MIIM_MII_CMD 0x8 -#define GCB_MIIM_MII_CMD_OPR_WRITE BIT(1) -#define GCB_MIIM_MII_CMD_OPR_READ BIT(2) -#define GCB_MIIM_MII_CMD_WRDATA(x) ((x) << 4) -#define GCB_MIIM_MII_CMD_REGAD(x) ((x) << 20) -#define GCB_MIIM_MII_CMD_PHYAD(x) ((x) << 25) -#define GCB_MIIM_MII_CMD_VLD BIT(31) -#define GCB_MIIM_DATA 0xC -#define GCB_MIIM_DATA_ERROR (0x2 << 16) +#include "mscc_miim.h" #define ANA_PORT_VLAN_CFG(x) (0x00 + 0x80 * (x)) #define ANA_PORT_VLAN_CFG_AWARE_ENA BIT(20) @@ -189,13 +178,6 @@ struct luton_private { struct luton_phy_port_t ports[MAX_PORT]; }; -struct mscc_miim_dev { - void __iomem *regs; - phys_addr_t miim_base; - unsigned long miim_size; - struct mii_dev *bus; -}; - static const unsigned long luton_regs_qs[] = { [MSCC_QS_XTR_RD] = 0x18, [MSCC_QS_XTR_FLUSH] = 0x28, @@ -213,84 +195,6 @@ static const unsigned long luton_regs_ana_table[] = { static struct mscc_miim_dev miim[LUTON_MIIM_BUS_COUNT]; static int miim_count = -1; -static int mscc_miim_wait_ready(struct mscc_miim_dev *miim) -{ - return wait_for_bit_le32(miim->regs + GCB_MIIM_MII_STATUS, - GCB_MIIM_STAT_BUSY, false, 250, false); -} - -static int mscc_miim_read(struct mii_dev *bus, int addr, int devad, int reg) -{ - struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv; - u32 val; - int ret; - - ret = mscc_miim_wait_ready(miim); - if (ret) - goto out; - - writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) | - GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_OPR_READ, - miim->regs + GCB_MIIM_MII_CMD); - - ret = mscc_miim_wait_ready(miim); - if (ret) - goto out; - - val = readl(miim->regs + GCB_MIIM_DATA); - if (val & GCB_MIIM_DATA_ERROR) { - ret = -EIO; - goto out; - } - - ret = val & 0xFFFF; - out: - return ret; -} - -static int mscc_miim_write(struct mii_dev *bus, int addr, int devad, int reg, - u16 val) -{ - struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv; - int ret; - - ret = mscc_miim_wait_ready(miim); - if (ret < 0) - goto out; - - writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) | - GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_WRDATA(val) | - GCB_MIIM_MII_CMD_OPR_WRITE, miim->regs + GCB_MIIM_MII_CMD); - out: - return ret; -} - -static struct mii_dev *serval_mdiobus_init(phys_addr_t miim_base, - unsigned long miim_size) -{ - struct mii_dev *bus; - - bus = mdio_alloc(); - if (!bus) - return NULL; - - ++miim_count; - sprintf(bus->name, "miim-bus%d", miim_count); - - miim[miim_count].regs = ioremap(miim_base, miim_size); - miim[miim_count].miim_base = miim_base; - miim[miim_count].miim_size = miim_size; - bus->priv = &miim[miim_count]; - bus->read = mscc_miim_read; - bus->write = mscc_miim_write; - - if (mdio_register(bus)) - return NULL; - - miim[miim_count].bus = bus; - return bus; -} - static void luton_stop(struct udevice *dev) { struct luton_private *priv = dev_get_priv(dev); @@ -760,7 +664,8 @@ static int luton_probe(struct udevice *dev) /* If the bus is new then create a new bus */ if (!get_mdiobus(addr_base, addr_size)) priv->bus[miim_count] = - serval_mdiobus_init(addr_base, addr_size); + mscc_mdiobus_init(miim, &miim_count, addr_base, + addr_size); /* Connect mdio bus with the port */ bus = get_mdiobus(addr_base, addr_size); diff --git a/drivers/net/mscc_eswitch/mscc_miim.c b/drivers/net/mscc_eswitch/mscc_miim.c index 419dcc1dd6..d8ee8df47b 100644 --- a/drivers/net/mscc_eswitch/mscc_miim.c +++ b/drivers/net/mscc_eswitch/mscc_miim.c @@ -72,3 +72,31 @@ int mscc_miim_write(struct mii_dev *bus, int addr, int devad, int reg, out: return ret; } + +struct mii_dev *mscc_mdiobus_init(struct mscc_miim_dev *miim, int *miim_count, + phys_addr_t miim_base, + unsigned long miim_size) +{ + struct mii_dev *bus; + + bus = mdio_alloc(); + + if (!bus) + return NULL; + + *miim_count += 1; + sprintf(bus->name, "miim-bus%d", *miim_count); + + miim[*miim_count].regs = ioremap(miim_base, miim_size); + miim[*miim_count].miim_base = miim_base; + miim[*miim_count].miim_size = miim_size; + bus->priv = &miim[*miim_count]; + bus->read = mscc_miim_read; + bus->write = mscc_miim_write; + + if (mdio_register(bus)) + return NULL; + + miim[*miim_count].bus = bus; + return bus; +} diff --git a/drivers/net/mscc_eswitch/mscc_miim.h b/drivers/net/mscc_eswitch/mscc_miim.h index 0e5d5e3c81..feb1f40ae5 100644 --- a/drivers/net/mscc_eswitch/mscc_miim.h +++ b/drivers/net/mscc_eswitch/mscc_miim.h @@ -3,10 +3,22 @@ * Copyright (c) 2018 Microsemi Corporation */ +#ifndef _MSCC_MIIM_H_ +#define _MSCC_MIIM_H_ + struct mscc_miim_dev { void __iomem *regs; - void __iomem *phy_regs; + phys_addr_t miim_base; + unsigned long miim_size; + struct mii_dev *bus; }; int mscc_miim_read(struct mii_dev *bus, int addr, int devad, int reg); int mscc_miim_write(struct mii_dev *bus, int addr, int devad, int reg, u16 val); + +struct mii_dev *mscc_mdiobus_init(struct mscc_miim_dev *miim, int *miim_count, + phys_addr_t miim_base, + unsigned long miim_size); + + +#endif /* _MSCC_MIIM_H_ */ diff --git a/drivers/net/mscc_eswitch/ocelot_switch.c b/drivers/net/mscc_eswitch/ocelot_switch.c index 5c7e6961be..0ba84ab78a 100644 --- a/drivers/net/mscc_eswitch/ocelot_switch.c +++ b/drivers/net/mscc_eswitch/ocelot_switch.c @@ -17,6 +17,7 @@ #include "mscc_xfer.h" #include "mscc_mac_table.h" +#include "mscc_miim.h" #define PHY_CFG 0x0 #define PHY_CFG_ENA 0xF @@ -25,20 +26,6 @@ #define PHY_STAT 0x4 #define PHY_STAT_SUPERVISOR_COMPLETE BIT(0) -#define GCB_MIIM_MII_STATUS 0x0 -#define GCB_MIIM_STAT_BUSY BIT(3) -#define GCB_MIIM_MII_CMD 0x8 -#define GCB_MIIM_MII_CMD_SCAN BIT(0) -#define GCB_MIIM_MII_CMD_OPR_WRITE BIT(1) -#define GCB_MIIM_MII_CMD_OPR_READ BIT(2) -#define GCB_MIIM_MII_CMD_SINGLE_SCAN BIT(3) -#define GCB_MIIM_MII_CMD_WRDATA(x) ((x) << 4) -#define GCB_MIIM_MII_CMD_REGAD(x) ((x) << 20) -#define GCB_MIIM_MII_CMD_PHYAD(x) ((x) << 25) -#define GCB_MIIM_MII_CMD_VLD BIT(31) -#define GCB_MIIM_DATA 0xC -#define GCB_MIIM_DATA_ERROR (0x3 << 16) - #define ANA_PORT_VLAN_CFG(x) (0x7000 + 0x100 * (x)) #define ANA_PORT_VLAN_CFG_AWARE_ENA BIT(20) #define ANA_PORT_VLAN_CFG_POP_CNT(x) ((x) << 18) @@ -173,13 +160,6 @@ struct ocelot_private { struct ocelot_phy_port_t ports[MAX_PORT]; }; -struct mscc_miim_dev { - void __iomem *regs; - phys_addr_t miim_base; - unsigned long miim_size; - struct mii_dev *bus; -}; - static struct mscc_miim_dev miim[OCELOT_MIIM_BUS_COUNT]; static int miim_count = -1; @@ -209,85 +189,6 @@ static void mscc_phy_reset(void) } } -static int mscc_miim_wait_ready(struct mscc_miim_dev *miim) -{ - return wait_for_bit_le32(miim->regs + GCB_MIIM_MII_STATUS, - GCB_MIIM_STAT_BUSY, false, 250, false); -} - -static int mscc_miim_read(struct mii_dev *bus, int addr, int devad, int reg) -{ - struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv; - u32 val; - int ret; - - ret = mscc_miim_wait_ready(miim); - if (ret) - goto out; - - writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) | - GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_OPR_READ, - miim->regs + GCB_MIIM_MII_CMD); - - ret = mscc_miim_wait_ready(miim); - if (ret) - goto out; - - val = readl(miim->regs + GCB_MIIM_DATA); - if (val & GCB_MIIM_DATA_ERROR) { - ret = -EIO; - goto out; - } - - ret = val & 0xFFFF; - out: - return ret; -} - -static int mscc_miim_write(struct mii_dev *bus, int addr, int devad, int reg, - u16 val) -{ - struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv; - int ret; - - ret = mscc_miim_wait_ready(miim); - if (ret < 0) - goto out; - - writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) | - GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_WRDATA(val) | - GCB_MIIM_MII_CMD_OPR_WRITE, miim->regs + GCB_MIIM_MII_CMD); - out: - return ret; -} - -static struct mii_dev *ocelot_mdiobus_init(phys_addr_t miim_base, - unsigned long miim_size) -{ - struct mii_dev *bus; - - bus = mdio_alloc(); - - if (!bus) - return NULL; - - ++miim_count; - sprintf(bus->name, "miim-bus%d", miim_count); - - miim[miim_count].regs = ioremap(miim_base, miim_size); - miim[miim_count].miim_base = miim_base; - miim[miim_count].miim_size = miim_size; - bus->priv = &miim[miim_count]; - bus->read = mscc_miim_read; - bus->write = mscc_miim_write; - - if (mdio_register(bus)) - return NULL; - - miim[miim_count].bus = bus; - return bus; -} - __weak void mscc_switch_reset(void) { } @@ -682,7 +583,8 @@ static int ocelot_probe(struct udevice *dev) /* If the bus is new then create a new bus */ if (!get_mdiobus(addr_base, addr_size)) priv->bus[miim_count] = - ocelot_mdiobus_init(addr_base, addr_size); + mscc_mdiobus_init(miim, &miim_count, addr_base, + addr_size); /* Connect mdio bus with the port */ bus = get_mdiobus(addr_base, addr_size); diff --git a/drivers/net/mscc_eswitch/serval_switch.c b/drivers/net/mscc_eswitch/serval_switch.c index 2559f5d0cd..1a21360a96 100644 --- a/drivers/net/mscc_eswitch/serval_switch.c +++ b/drivers/net/mscc_eswitch/serval_switch.c @@ -17,18 +17,7 @@ #include "mscc_xfer.h" #include "mscc_mac_table.h" - -#define GCB_MIIM_MII_STATUS 0x0 -#define GCB_MIIM_STAT_BUSY BIT(3) -#define GCB_MIIM_MII_CMD 0x8 -#define GCB_MIIM_MII_CMD_OPR_WRITE BIT(1) -#define GCB_MIIM_MII_CMD_OPR_READ BIT(2) -#define GCB_MIIM_MII_CMD_WRDATA(x) ((x) << 4) -#define GCB_MIIM_MII_CMD_REGAD(x) ((x) << 20) -#define GCB_MIIM_MII_CMD_PHYAD(x) ((x) << 25) -#define GCB_MIIM_MII_CMD_VLD BIT(31) -#define GCB_MIIM_DATA 0xC -#define GCB_MIIM_DATA_ERROR (0x2 << 16) +#include "mscc_miim.h" #define ANA_PORT_VLAN_CFG(x) (0xc000 + 0x100 * (x)) #define ANA_PORT_VLAN_CFG_AWARE_ENA BIT(20) @@ -156,13 +145,6 @@ struct serval_private { struct serval_phy_port_t ports[MAX_PORT]; }; -struct mscc_miim_dev { - void __iomem *regs; - phys_addr_t miim_base; - unsigned long miim_size; - struct mii_dev *bus; -}; - static const unsigned long serval_regs_qs[] = { [MSCC_QS_XTR_RD] = 0x8, [MSCC_QS_XTR_FLUSH] = 0x18, @@ -180,84 +162,6 @@ static const unsigned long serval_regs_ana_table[] = { static struct mscc_miim_dev miim[SERVAL_MIIM_BUS_COUNT]; static int miim_count = -1; -static int mscc_miim_wait_ready(struct mscc_miim_dev *miim) -{ - return wait_for_bit_le32(miim->regs + GCB_MIIM_MII_STATUS, - GCB_MIIM_STAT_BUSY, false, 250, false); -} - -static int mscc_miim_read(struct mii_dev *bus, int addr, int devad, int reg) -{ - struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv; - u32 val; - int ret; - - ret = mscc_miim_wait_ready(miim); - if (ret) - goto out; - - writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) | - GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_OPR_READ, - miim->regs + GCB_MIIM_MII_CMD); - - ret = mscc_miim_wait_ready(miim); - if (ret) - goto out; - - val = readl(miim->regs + GCB_MIIM_DATA); - if (val & GCB_MIIM_DATA_ERROR) { - ret = -EIO; - goto out; - } - - ret = val & 0xFFFF; - out: - return ret; -} - -static int mscc_miim_write(struct mii_dev *bus, int addr, int devad, int reg, - u16 val) -{ - struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv; - int ret; - - ret = mscc_miim_wait_ready(miim); - if (ret < 0) - goto out; - - writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) | - GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_WRDATA(val) | - GCB_MIIM_MII_CMD_OPR_WRITE, miim->regs + GCB_MIIM_MII_CMD); - out: - return ret; -} - -static struct mii_dev *serval_mdiobus_init(phys_addr_t miim_base, - unsigned long miim_size) -{ - struct mii_dev *bus; - - bus = mdio_alloc(); - if (!bus) - return NULL; - - ++miim_count; - sprintf(bus->name, "miim-bus%d", miim_count); - - miim[miim_count].regs = ioremap(miim_base, miim_size); - miim[miim_count].miim_base = miim_base; - miim[miim_count].miim_size = miim_size; - bus->priv = &miim[miim_count]; - bus->read = mscc_miim_read; - bus->write = mscc_miim_write; - - if (mdio_register(bus)) - return NULL; - - miim[miim_count].bus = bus; - return bus; -} - static void serval_cpu_capture_setup(struct serval_private *priv) { int i; @@ -356,8 +260,6 @@ static void serdes_write(void __iomem *base, u32 addr) do { data = readl(base + HSIO_MCB_SERDES1G_CFG); } while (data & HSIO_MCB_SERDES1G_CFG_WR_ONE_SHOT); - - mdelay(100); } static void serdes1g_setup(void __iomem *base, uint32_t addr, @@ -636,7 +538,8 @@ static int serval_probe(struct udevice *dev) /* If the bus is new then create a new bus */ if (!get_mdiobus(addr_base, addr_size)) priv->bus[miim_count] = - serval_mdiobus_init(addr_base, addr_size); + mscc_mdiobus_init(miim, &miim_count, addr_base, + addr_size); /* Connect mdio bus with the port */ bus = get_mdiobus(addr_base, addr_size); diff --git a/drivers/net/mscc_eswitch/servalt_switch.c b/drivers/net/mscc_eswitch/servalt_switch.c index 995c62309d..d20ec49d56 100644 --- a/drivers/net/mscc_eswitch/servalt_switch.c +++ b/drivers/net/mscc_eswitch/servalt_switch.c @@ -16,18 +16,7 @@ #include <wait_bit.h> #include "mscc_xfer.h" - -#define GCB_MIIM_MII_STATUS 0x0 -#define GCB_MIIM_STAT_BUSY BIT(3) -#define GCB_MIIM_MII_CMD 0x8 -#define GCB_MIIM_MII_CMD_OPR_WRITE BIT(1) -#define GCB_MIIM_MII_CMD_OPR_READ BIT(2) -#define GCB_MIIM_MII_CMD_WRDATA(x) ((x) << 4) -#define GCB_MIIM_MII_CMD_REGAD(x) ((x) << 20) -#define GCB_MIIM_MII_CMD_PHYAD(x) ((x) << 25) -#define GCB_MIIM_MII_CMD_VLD BIT(31) -#define GCB_MIIM_DATA 0xC -#define GCB_MIIM_DATA_ERROR (0x3 << 16) +#include "mscc_miim.h" #define PHY_CFG 0x0 #define PHY_CFG_ENA 0x3 @@ -134,13 +123,6 @@ struct servalt_private { struct servalt_phy_port_t ports[MAX_PORT]; }; -struct mscc_miim_dev { - void __iomem *regs; - phys_addr_t miim_base; - unsigned long miim_size; - struct mii_dev *bus; -}; - static const unsigned long servalt_regs_qs[] = { [MSCC_QS_XTR_RD] = 0x8, [MSCC_QS_XTR_FLUSH] = 0x18, @@ -152,85 +134,6 @@ static const unsigned long servalt_regs_qs[] = { static struct mscc_miim_dev miim[SERVALT_MIIM_BUS_COUNT]; static int miim_count = -1; -static int mscc_miim_wait_ready(struct mscc_miim_dev *miim) -{ - return wait_for_bit_le32(miim->regs + GCB_MIIM_MII_STATUS, - GCB_MIIM_STAT_BUSY, false, 250, false); -} - -static int mscc_miim_read(struct mii_dev *bus, int addr, int devad, int reg) -{ - struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv; - u32 val; - int ret; - - ret = mscc_miim_wait_ready(miim); - if (ret) - goto out; - - writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) | - GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_OPR_READ, - miim->regs + GCB_MIIM_MII_CMD); - - ret = mscc_miim_wait_ready(miim); - if (ret) - goto out; - - val = readl(miim->regs + GCB_MIIM_DATA); - if (val & GCB_MIIM_DATA_ERROR) { - ret = -EIO; - goto out; - } - - ret = val & 0xFFFF; -out: - return ret; -} - -static int mscc_miim_write(struct mii_dev *bus, int addr, int devad, int reg, - u16 val) -{ - struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv; - int ret; - - ret = mscc_miim_wait_ready(miim); - if (ret < 0) - goto out; - - writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) | - GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_WRDATA(val) | - GCB_MIIM_MII_CMD_OPR_WRITE, miim->regs + GCB_MIIM_MII_CMD); - -out: - return ret; -} - -static struct mii_dev *servalt_mdiobus_init(phys_addr_t miim_base, - unsigned long miim_size) -{ - struct mii_dev *bus; - - bus = mdio_alloc(); - if (!bus) - return NULL; - - ++miim_count; - sprintf(bus->name, "miim-bus%d", miim_count); - - miim[miim_count].regs = ioremap(miim_base, miim_size); - miim[miim_count].miim_base = miim_base; - miim[miim_count].miim_size = miim_size; - bus->priv = &miim[miim_count]; - bus->read = mscc_miim_read; - bus->write = mscc_miim_write; - - if (mdio_register(bus)) - return NULL; - - miim[miim_count].bus = bus; - return bus; -} - static void mscc_phy_reset(void) { writel(0, BASE_DEVCPU_GCB + GCB_PHY_CFG + PHY_CFG); @@ -564,7 +467,8 @@ static int servalt_probe(struct udevice *dev) /* If the bus is new then create a new bus */ if (!get_mdiobus(addr_base, addr_size)) priv->bus[miim_count] = - servalt_mdiobus_init(addr_base, addr_size); + mscc_mdiobus_init(miim, &miim_count, addr_base, + addr_size); /* Connect mdio bus with the port */ bus = get_mdiobus(addr_base, addr_size); diff --git a/drivers/net/phy/cortina.c b/drivers/net/phy/cortina.c index a04a118f90..ec81dab3f6 100644 --- a/drivers/net/phy/cortina.c +++ b/drivers/net/phy/cortina.c @@ -176,8 +176,13 @@ void cs4340_upload_firmware(struct phy_device *phydev) printf("MMC read: dev # %u, block # %u, count %u ...\n", dev, blk, cnt); mmc_init(mmc); +#ifdef CONFIG_BLK + (void)blk_dread(mmc_get_blk_desc(mmc), blk, cnt, + addr); +#else (void)mmc->block_dev.block_read(&mmc->block_dev, blk, cnt, addr); +#endif } #endif diff --git a/drivers/net/phy/ti.c b/drivers/net/phy/ti.c index 25f1332ca9..7509936465 100644 --- a/drivers/net/phy/ti.c +++ b/drivers/net/phy/ti.c @@ -103,7 +103,7 @@ struct dp83867_private { int io_impedance; bool rxctrl_strap_quirk; int port_mirroring; - int clk_output_sel; + unsigned int clk_output_sel; }; static int dp83867_config_port_mirroring(struct phy_device *phydev) @@ -136,17 +136,11 @@ static int dp83867_of_init(struct phy_device *phydev) ofnode node; u16 val; - /* Optional configuration */ - node = phy_get_ofnode(phydev); if (!ofnode_valid(node)) return -EINVAL; - /* - * Keep the default value if ti,clk-output-sel is not set - * or to high - */ - + /* Keep the default value if ti,clk-output-sel is not set */ dp83867->clk_output_sel = ofnode_read_u32_default(node, "ti,clk-output-sel", DP83867_CLK_O_SEL_REF_CLK); @@ -162,14 +156,14 @@ static int dp83867_of_init(struct phy_device *phydev) dp83867->rxctrl_strap_quirk = true; dp83867->rx_id_delay = ofnode_read_u32_default(node, "ti,rx-internal-delay", - -1); + DEFAULT_RX_ID_DELAY); dp83867->tx_id_delay = ofnode_read_u32_default(node, "ti,tx-internal-delay", - -1); + DEFAULT_TX_ID_DELAY); dp83867->fifo_depth = ofnode_read_u32_default(node, "ti,fifo-depth", - -1); + DEFAULT_FIFO_DEPTH); if (ofnode_read_bool(node, "enet-phy-lane-swap")) dp83867->port_mirroring = DP83867_PORT_MIRRORING_EN; diff --git a/drivers/net/ti/Kconfig b/drivers/net/ti/Kconfig index 82bc9f5d03..ecf642de10 100644 --- a/drivers/net/ti/Kconfig +++ b/drivers/net/ti/Kconfig @@ -18,3 +18,11 @@ config DRIVER_TI_KEYSTONE_NET bool "TI Keystone 2 Ethernet" help This driver supports the TI Keystone 2 Ethernet subsystem + +config TI_AM65_CPSW_NUSS + bool "TI K3 AM65x MCU CPSW Nuss Ethernet controller driver" + depends on ARCH_K3 + select PHYLIB + help + This driver supports TI K3 MCU CPSW Nuss Ethernet controller + in Texas Instruments K3 AM65x SoCs. diff --git a/drivers/net/ti/Makefile b/drivers/net/ti/Makefile index ee3e4eb5d6..8d3808bb4b 100644 --- a/drivers/net/ti/Makefile +++ b/drivers/net/ti/Makefile @@ -5,3 +5,4 @@ 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 diff --git a/drivers/net/ti/am65-cpsw-nuss.c b/drivers/net/ti/am65-cpsw-nuss.c new file mode 100644 index 0000000000..e11fbdeed3 --- /dev/null +++ b/drivers/net/ti/am65-cpsw-nuss.c @@ -0,0 +1,792 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Texas Instruments K3 AM65 Ethernet Switch SubSystem Driver + * + * Copyright (C) 2019, Texas Instruments, Incorporated + * + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/processor.h> +#include <clk.h> +#include <dm.h> +#include <dm/lists.h> +#include <dma-uclass.h> +#include <dm/of_access.h> +#include <miiphy.h> +#include <net.h> +#include <phy.h> +#include <power-domain.h> +#include <linux/soc/ti/ti-udma.h> + +#include "cpsw_mdio.h" + +#define AM65_CPSW_CPSWNU_MAX_PORTS 2 + +#define AM65_CPSW_SS_BASE 0x0 +#define AM65_CPSW_SGMII_BASE 0x100 +#define AM65_CPSW_MDIO_BASE 0xf00 +#define AM65_CPSW_XGMII_BASE 0x2100 +#define AM65_CPSW_CPSW_NU_BASE 0x20000 +#define AM65_CPSW_CPSW_NU_ALE_BASE 0x1e000 + +#define AM65_CPSW_CPSW_NU_PORTS_OFFSET 0x1000 +#define AM65_CPSW_CPSW_NU_PORT_MACSL_OFFSET 0x330 + +#define AM65_CPSW_MDIO_BUS_FREQ_DEF 1000000 + +#define AM65_CPSW_CTL_REG 0x4 +#define AM65_CPSW_STAT_PORT_EN_REG 0x14 +#define AM65_CPSW_PTYPE_REG 0x18 + +#define AM65_CPSW_CTL_REG_P0_ENABLE BIT(2) +#define AM65_CPSW_CTL_REG_P0_TX_CRC_REMOVE BIT(13) +#define AM65_CPSW_CTL_REG_P0_RX_PAD BIT(14) + +#define AM65_CPSW_P0_FLOW_ID_REG 0x8 +#define AM65_CPSW_PN_RX_MAXLEN_REG 0x24 +#define AM65_CPSW_PN_REG_SA_L 0x308 +#define AM65_CPSW_PN_REG_SA_H 0x30c + +#define AM65_CPSW_ALE_CTL_REG 0x8 +#define AM65_CPSW_ALE_CTL_REG_ENABLE BIT(31) +#define AM65_CPSW_ALE_CTL_REG_RESET_TBL BIT(30) +#define AM65_CPSW_ALE_CTL_REG_BYPASS BIT(4) +#define AM65_CPSW_ALE_PN_CTL_REG(x) (0x40 + (x) * 4) +#define AM65_CPSW_ALE_PN_CTL_REG_MODE_FORWARD 0x3 +#define AM65_CPSW_ALE_PN_CTL_REG_MAC_ONLY BIT(11) + +#define AM65_CPSW_MACSL_CTL_REG 0x0 +#define AM65_CPSW_MACSL_CTL_REG_IFCTL_A BIT(15) +#define AM65_CPSW_MACSL_CTL_REG_GIG BIT(7) +#define AM65_CPSW_MACSL_CTL_REG_GMII_EN BIT(5) +#define AM65_CPSW_MACSL_CTL_REG_LOOPBACK BIT(1) +#define AM65_CPSW_MACSL_CTL_REG_FULL_DUPLEX BIT(0) +#define AM65_CPSW_MACSL_RESET_REG 0x8 +#define AM65_CPSW_MACSL_RESET_REG_RESET BIT(0) +#define AM65_CPSW_MACSL_STATUS_REG 0x4 +#define AM65_CPSW_MACSL_RESET_REG_PN_IDLE BIT(31) +#define AM65_CPSW_MACSL_RESET_REG_PN_E_IDLE BIT(30) +#define AM65_CPSW_MACSL_RESET_REG_PN_P_IDLE BIT(29) +#define AM65_CPSW_MACSL_RESET_REG_PN_TX_IDLE BIT(28) +#define AM65_CPSW_MACSL_RESET_REG_IDLE_MASK \ + (AM65_CPSW_MACSL_RESET_REG_PN_IDLE | \ + AM65_CPSW_MACSL_RESET_REG_PN_E_IDLE | \ + AM65_CPSW_MACSL_RESET_REG_PN_P_IDLE | \ + AM65_CPSW_MACSL_RESET_REG_PN_TX_IDLE) + +#define AM65_CPSW_CPPI_PKT_TYPE 0x7 + +struct am65_cpsw_port { + fdt_addr_t port_base; + fdt_addr_t macsl_base; + bool disabled; + u32 mac_control; +}; + +struct am65_cpsw_common { + struct udevice *dev; + fdt_addr_t ss_base; + fdt_addr_t cpsw_base; + fdt_addr_t mdio_base; + fdt_addr_t ale_base; + fdt_addr_t gmii_sel; + fdt_addr_t mac_efuse; + + struct clk fclk; + struct power_domain pwrdmn; + + u32 port_num; + struct am65_cpsw_port ports[AM65_CPSW_CPSWNU_MAX_PORTS]; + u32 rflow_id_base; + + struct mii_dev *bus; + u32 bus_freq; + + struct dma dma_tx; + struct dma dma_rx; + u32 rx_next; + u32 rx_pend; + bool started; +}; + +struct am65_cpsw_priv { + 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; +}; + +#ifdef PKTSIZE_ALIGN +#define UDMA_RX_BUF_SIZE PKTSIZE_ALIGN +#else +#define UDMA_RX_BUF_SIZE ALIGN(1522, ARCH_DMA_MINALIGN) +#endif + +#ifdef PKTBUFSRX +#define UDMA_RX_DESC_NUM PKTBUFSRX +#else +#define UDMA_RX_DESC_NUM 4 +#endif + +#define mac_hi(mac) (((mac)[0] << 0) | ((mac)[1] << 8) | \ + ((mac)[2] << 16) | ((mac)[3] << 24)) +#define mac_lo(mac) (((mac)[4] << 0) | ((mac)[5] << 8)) + +static void am65_cpsw_set_sl_mac(struct am65_cpsw_port *slave, + unsigned char *addr) +{ + writel(mac_hi(addr), + slave->port_base + AM65_CPSW_PN_REG_SA_H); + writel(mac_lo(addr), + slave->port_base + AM65_CPSW_PN_REG_SA_L); +} + +int am65_cpsw_macsl_reset(struct am65_cpsw_port *slave) +{ + u32 i = 100; + + /* Set the soft reset bit */ + writel(AM65_CPSW_MACSL_RESET_REG_RESET, + slave->macsl_base + AM65_CPSW_MACSL_RESET_REG); + + while ((readl(slave->macsl_base + AM65_CPSW_MACSL_RESET_REG) & + AM65_CPSW_MACSL_RESET_REG_RESET) && i--) + cpu_relax(); + + /* Timeout on the reset */ + return i; +} + +static int am65_cpsw_macsl_wait_for_idle(struct am65_cpsw_port *slave) +{ + u32 i = 100; + + while ((readl(slave->macsl_base + AM65_CPSW_MACSL_STATUS_REG) & + AM65_CPSW_MACSL_RESET_REG_IDLE_MASK) && i--) + cpu_relax(); + + return i; +} + +static int am65_cpsw_update_link(struct am65_cpsw_priv *priv) +{ + struct am65_cpsw_common *common = priv->cpsw_common; + struct am65_cpsw_port *port = &common->ports[priv->port_id]; + struct phy_device *phy = priv->phydev; + u32 mac_control = 0; + + if (phy->link) { /* link up */ + mac_control = /*AM65_CPSW_MACSL_CTL_REG_LOOPBACK |*/ + AM65_CPSW_MACSL_CTL_REG_GMII_EN; + if (phy->speed == 1000) + mac_control |= AM65_CPSW_MACSL_CTL_REG_GIG; + if (phy->duplex == DUPLEX_FULL) + mac_control |= AM65_CPSW_MACSL_CTL_REG_FULL_DUPLEX; + if (phy->speed == 100) + mac_control |= AM65_CPSW_MACSL_CTL_REG_IFCTL_A; + } + + if (mac_control == port->mac_control) + goto out; + + if (mac_control) { + printf("link up on port %d, speed %d, %s duplex\n", + priv->port_id, phy->speed, + (phy->duplex == DUPLEX_FULL) ? "full" : "half"); + } else { + printf("link down on port %d\n", priv->port_id); + } + + writel(mac_control, port->macsl_base + AM65_CPSW_MACSL_CTL_REG); + port->mac_control = mac_control; + +out: + return phy->link; +} + +#define AM65_GMII_SEL_MODE_MII 0 +#define AM65_GMII_SEL_MODE_RMII 1 +#define AM65_GMII_SEL_MODE_RGMII 2 + +#define AM65_GMII_SEL_RGMII_IDMODE BIT(4) + +static void am65_cpsw_gmii_sel_k3(struct am65_cpsw_priv *priv, + phy_interface_t phy_mode, int slave) +{ + struct am65_cpsw_common *common = priv->cpsw_common; + u32 reg; + u32 mode = 0; + bool rgmii_id = false; + + reg = readl(common->gmii_sel); + + dev_dbg(common->dev, "old gmii_sel: %08x\n", reg); + + switch (phy_mode) { + case PHY_INTERFACE_MODE_RMII: + mode = AM65_GMII_SEL_MODE_RMII; + break; + + case PHY_INTERFACE_MODE_RGMII: + mode = AM65_GMII_SEL_MODE_RGMII; + break; + + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + mode = AM65_GMII_SEL_MODE_RGMII; + rgmii_id = true; + break; + + default: + dev_warn(common->dev, + "Unsupported PHY mode: %u. Defaulting to MII.\n", + phy_mode); + /* fallthrough */ + case PHY_INTERFACE_MODE_MII: + mode = AM65_GMII_SEL_MODE_MII; + break; + }; + + if (rgmii_id) + mode |= AM65_GMII_SEL_RGMII_IDMODE; + + reg = mode; + dev_dbg(common->dev, "gmii_sel PHY mode: %u, new gmii_sel: %08x\n", + phy_mode, reg); + writel(reg, common->gmii_sel); + + reg = readl(common->gmii_sel); + if (reg != mode) + dev_err(common->dev, + "gmii_sel PHY mode NOT SET!: requested: %08x, gmii_sel: %08x\n", + mode, reg); +} + +static int am65_cpsw_start(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_platdata(dev); + struct am65_cpsw_priv *priv = dev_get_priv(dev); + struct am65_cpsw_common *common = priv->cpsw_common; + struct am65_cpsw_port *port = &common->ports[priv->port_id]; + struct am65_cpsw_port *port0 = &common->ports[0]; + int ret, i; + + ret = power_domain_on(&common->pwrdmn); + if (ret) { + dev_err(dev, "power_domain_on() failed %d\n", ret); + goto out; + } + + ret = clk_enable(&common->fclk); + if (ret) { + dev_err(dev, "clk enabled failed %d\n", ret); + goto err_off_pwrdm; + } + + common->rx_next = 0; + common->rx_pend = 0; + ret = dma_get_by_name(common->dev, "tx0", &common->dma_tx); + if (ret) { + dev_err(dev, "TX dma get failed %d\n", ret); + goto err_off_clk; + } + ret = dma_get_by_name(common->dev, "rx", &common->dma_rx); + if (ret) { + dev_err(dev, "RX dma get failed %d\n", ret); + goto err_free_tx; + } + + for (i = 0; i < UDMA_RX_DESC_NUM; i++) { + ret = dma_prepare_rcv_buf(&common->dma_rx, + net_rx_packets[i], + UDMA_RX_BUF_SIZE); + if (ret) { + dev_err(dev, "RX dma add buf failed %d\n", ret); + goto err_free_tx; + } + } + + ret = dma_enable(&common->dma_tx); + if (ret) { + dev_err(dev, "TX dma_enable failed %d\n", ret); + goto err_free_rx; + } + ret = dma_enable(&common->dma_rx); + if (ret) { + dev_err(dev, "RX dma_enable failed %d\n", ret); + goto err_dis_tx; + } + + /* Control register */ + writel(AM65_CPSW_CTL_REG_P0_ENABLE | + AM65_CPSW_CTL_REG_P0_TX_CRC_REMOVE | + AM65_CPSW_CTL_REG_P0_RX_PAD, + common->cpsw_base + AM65_CPSW_CTL_REG); + + /* disable priority elevation */ + writel(0, common->cpsw_base + AM65_CPSW_PTYPE_REG); + + /* enable statistics */ + writel(BIT(0) | BIT(priv->port_id), + common->cpsw_base + AM65_CPSW_STAT_PORT_EN_REG); + + /* Port 0 length register */ + writel(PKTSIZE_ALIGN, port0->port_base + AM65_CPSW_PN_RX_MAXLEN_REG); + + /* set base flow_id */ + writel(common->rflow_id_base, + port0->port_base + AM65_CPSW_P0_FLOW_ID_REG); + + /* Reset and enable the ALE */ + writel(AM65_CPSW_ALE_CTL_REG_ENABLE | AM65_CPSW_ALE_CTL_REG_RESET_TBL | + AM65_CPSW_ALE_CTL_REG_BYPASS, + common->ale_base + AM65_CPSW_ALE_CTL_REG); + + /* port 0 put into forward mode */ + writel(AM65_CPSW_ALE_PN_CTL_REG_MODE_FORWARD, + common->ale_base + AM65_CPSW_ALE_PN_CTL_REG(0)); + + /* PORT x configuration */ + + /* Port x Max length register */ + writel(PKTSIZE_ALIGN, port->port_base + AM65_CPSW_PN_RX_MAXLEN_REG); + + /* Port x set mac */ + am65_cpsw_set_sl_mac(port, pdata->enetaddr); + + /* Port x ALE: mac_only, Forwarding */ + writel(AM65_CPSW_ALE_PN_CTL_REG_MAC_ONLY | + AM65_CPSW_ALE_PN_CTL_REG_MODE_FORWARD, + common->ale_base + AM65_CPSW_ALE_PN_CTL_REG(priv->port_id)); + + port->mac_control = 0; + if (!am65_cpsw_macsl_reset(port)) { + dev_err(dev, "mac_sl reset failed\n"); + ret = -EFAULT; + goto err_dis_rx; + } + + ret = phy_startup(priv->phydev); + if (ret) { + dev_err(dev, "phy_startup failed\n"); + goto err_dis_rx; + } + + ret = am65_cpsw_update_link(priv); + if (!ret) { + ret = -ENODEV; + goto err_phy_shutdown; + } + + common->started = true; + + return 0; + +err_phy_shutdown: + phy_shutdown(priv->phydev); +err_dis_rx: + /* disable ports */ + writel(0, common->ale_base + AM65_CPSW_ALE_PN_CTL_REG(priv->port_id)); + writel(0, common->ale_base + AM65_CPSW_ALE_PN_CTL_REG(0)); + if (!am65_cpsw_macsl_wait_for_idle(port)) + dev_err(dev, "mac_sl idle timeout\n"); + writel(0, port->macsl_base + AM65_CPSW_MACSL_CTL_REG); + writel(0, common->ale_base + AM65_CPSW_ALE_CTL_REG); + writel(0, common->cpsw_base + AM65_CPSW_CTL_REG); + + dma_disable(&common->dma_rx); +err_dis_tx: + dma_disable(&common->dma_tx); +err_free_rx: + dma_free(&common->dma_rx); +err_free_tx: + dma_free(&common->dma_tx); +err_off_clk: + clk_disable(&common->fclk); +err_off_pwrdm: + power_domain_off(&common->pwrdmn); +out: + dev_err(dev, "%s end error\n", __func__); + + return ret; +} + +static int am65_cpsw_send(struct udevice *dev, void *packet, int length) +{ + struct am65_cpsw_priv *priv = dev_get_priv(dev); + struct am65_cpsw_common *common = priv->cpsw_common; + struct ti_udma_drv_packet_data packet_data; + int ret; + + packet_data.pkt_type = AM65_CPSW_CPPI_PKT_TYPE; + packet_data.dest_tag = priv->port_id; + ret = dma_send(&common->dma_tx, packet, length, &packet_data); + if (ret) { + dev_err(dev, "TX dma_send failed %d\n", ret); + return ret; + } + + return 0; +} + +static int am65_cpsw_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct am65_cpsw_priv *priv = dev_get_priv(dev); + struct am65_cpsw_common *common = priv->cpsw_common; + + /* try to receive a new packet */ + return dma_receive(&common->dma_rx, (void **)packetp, NULL); +} + +static int am65_cpsw_free_pkt(struct udevice *dev, uchar *packet, int length) +{ + struct am65_cpsw_priv *priv = dev_get_priv(dev); + struct am65_cpsw_common *common = priv->cpsw_common; + int ret; + + if (length > 0) { + u32 pkt = common->rx_next % UDMA_RX_DESC_NUM; + + ret = dma_prepare_rcv_buf(&common->dma_rx, + net_rx_packets[pkt], + UDMA_RX_BUF_SIZE); + if (ret) + dev_err(dev, "RX dma free_pkt failed %d\n", ret); + common->rx_next++; + } + + return 0; +} + +static void am65_cpsw_stop(struct udevice *dev) +{ + struct am65_cpsw_priv *priv = dev_get_priv(dev); + struct am65_cpsw_common *common = priv->cpsw_common; + struct am65_cpsw_port *port = &common->ports[priv->port_id]; + + if (!common->started) + return; + + phy_shutdown(priv->phydev); + + writel(0, common->ale_base + AM65_CPSW_ALE_PN_CTL_REG(priv->port_id)); + writel(0, common->ale_base + AM65_CPSW_ALE_PN_CTL_REG(0)); + if (!am65_cpsw_macsl_wait_for_idle(port)) + dev_err(dev, "mac_sl idle timeout\n"); + writel(0, port->macsl_base + AM65_CPSW_MACSL_CTL_REG); + writel(0, common->ale_base + AM65_CPSW_ALE_CTL_REG); + writel(0, common->cpsw_base + AM65_CPSW_CTL_REG); + + dma_disable(&common->dma_tx); + dma_free(&common->dma_tx); + + dma_disable(&common->dma_rx); + dma_free(&common->dma_rx); + + common->started = false; +} + +static int am65_cpsw_read_rom_hwaddr(struct udevice *dev) +{ + struct am65_cpsw_priv *priv = dev_get_priv(dev); + struct am65_cpsw_common *common = priv->cpsw_common; + struct eth_pdata *pdata = dev_get_platdata(dev); + u32 mac_hi, mac_lo; + + if (common->mac_efuse == FDT_ADDR_T_NONE) + return -1; + + mac_lo = readl(common->mac_efuse); + mac_hi = readl(common->mac_efuse + 4); + pdata->enetaddr[0] = (mac_hi >> 8) & 0xff; + pdata->enetaddr[1] = mac_hi & 0xff; + pdata->enetaddr[2] = (mac_lo >> 24) & 0xff; + pdata->enetaddr[3] = (mac_lo >> 16) & 0xff; + pdata->enetaddr[4] = (mac_lo >> 8) & 0xff; + pdata->enetaddr[5] = mac_lo & 0xff; + + return 0; +} + +static const struct eth_ops am65_cpsw_ops = { + .start = am65_cpsw_start, + .send = am65_cpsw_send, + .recv = am65_cpsw_recv, + .free_pkt = am65_cpsw_free_pkt, + .stop = am65_cpsw_stop, + .read_rom_hwaddr = am65_cpsw_read_rom_hwaddr, +}; + +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; + + if (!priv->has_phy || cpsw_common->bus) + return 0; + + cpsw_common->bus = cpsw_mdio_init(dev->name, + cpsw_common->mdio_base, + cpsw_common->bus_freq, + clk_get_rate(&cpsw_common->fclk)); + 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_platdata(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); + + if (!phydev) { + dev_err(dev, "phy_connect() failed\n"); + return -ENODEV; + } + + phydev->supported &= supported; + if (pdata->max_speed) { + ret = phy_set_supported(phydev, pdata->max_speed); + if (ret) + return ret; + } + 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); + + return ret; +} + +static int am65_cpsw_ofdata_parse_phy(struct udevice *dev, ofnode port_np) +{ + struct eth_pdata *pdata = dev_get_platdata(dev); + struct am65_cpsw_priv *priv = dev_get_priv(dev); + struct ofnode_phandle_args out_args; + const char *phy_mode; + int ret = 0; + + phy_mode = ofnode_read_string(port_np, "phy-mode"); + if (phy_mode) { + pdata->phy_interface = + phy_get_interface_by_name(phy_mode); + if (pdata->phy_interface == -1) { + dev_err(dev, "Invalid PHY mode '%s', port %u\n", + phy_mode, priv->port_id); + ret = -EINVAL; + goto out; + } + } + + ofnode_read_u32(port_np, "max-speed", (u32 *)&pdata->max_speed); + if (pdata->max_speed) + 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(port_np, "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; +} + +static int am65_cpsw_probe_cpsw(struct udevice *dev) +{ + struct am65_cpsw_priv *priv = dev_get_priv(dev); + struct eth_pdata *pdata = dev_get_platdata(dev); + struct am65_cpsw_common *cpsw_common; + ofnode ports_np, node; + int ret, i; + + priv->dev = dev; + + cpsw_common = calloc(1, sizeof(*priv->cpsw_common)); + if (!cpsw_common) + return -ENOMEM; + priv->cpsw_common = cpsw_common; + + cpsw_common->dev = dev; + cpsw_common->ss_base = dev_read_addr(dev); + if (cpsw_common->ss_base == FDT_ADDR_T_NONE) + return -EINVAL; + cpsw_common->mac_efuse = devfdt_get_addr_name(dev, "mac_efuse"); + /* no err check - optional */ + + ret = power_domain_get_by_index(dev, &cpsw_common->pwrdmn, 0); + if (ret) { + dev_err(dev, "failed to get pwrdmn: %d\n", ret); + return ret; + } + + ret = clk_get_by_name(dev, "fck", &cpsw_common->fclk); + if (ret) { + power_domain_free(&cpsw_common->pwrdmn); + dev_err(dev, "failed to get clock %d\n", ret); + return ret; + } + + 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; + + cpsw_common->rflow_id_base = 0; + cpsw_common->rflow_id_base = + dev_read_u32_default(dev, "ti,rx-flow-id-base", + cpsw_common->rflow_id_base); + + ports_np = dev_read_subnode(dev, "ports"); + if (!ofnode_valid(ports_np)) { + ret = -ENOENT; + goto out; + } + + ofnode_for_each_subnode(node, ports_np) { + const char *node_name; + u32 port_id; + bool disabled; + + node_name = ofnode_get_name(node); + + disabled = !ofnode_is_available(node); + + ret = ofnode_read_u32(node, "reg", &port_id); + if (ret) { + dev_err(dev, "%s: failed to get port_id (%d)\n", + node_name, ret); + goto out; + } + + if (port_id >= AM65_CPSW_CPSWNU_MAX_PORTS) { + dev_err(dev, "%s: invalid port_id (%d)\n", + node_name, port_id); + ret = -EINVAL; + goto out; + } + cpsw_common->port_num++; + + if (!port_id) + continue; + + priv->port_id = port_id; + cpsw_common->ports[port_id].disabled = disabled; + if (disabled) + continue; + + ret = am65_cpsw_ofdata_parse_phy(dev, node); + if (ret) + goto out; + } + + for (i = 0; i < AM65_CPSW_CPSWNU_MAX_PORTS; i++) { + struct am65_cpsw_port *port = &cpsw_common->ports[i]; + + port->port_base = cpsw_common->cpsw_base + + AM65_CPSW_CPSW_NU_PORTS_OFFSET + + (i * AM65_CPSW_CPSW_NU_PORTS_OFFSET); + port->macsl_base = port->port_base + + AM65_CPSW_CPSW_NU_PORT_MACSL_OFFSET; + } + + node = dev_read_subnode(dev, "cpsw-phy-sel"); + if (!ofnode_valid(node)) { + dev_err(dev, "can't find cpsw-phy-sel\n"); + ret = -ENOENT; + goto out; + } + + cpsw_common->gmii_sel = ofnode_get_addr(node); + if (cpsw_common->gmii_sel == FDT_ADDR_T_NONE) { + dev_err(dev, "failed to get gmii_sel base\n"); + goto out; + } + + node = dev_read_subnode(dev, "mdio"); + if (!ofnode_valid(node)) { + dev_err(dev, "can't find mdio\n"); + ret = -ENOENT; + goto out; + } + + cpsw_common->bus_freq = + dev_read_u32_default(dev, "bus_freq", + AM65_CPSW_MDIO_BUS_FREQ_DEF); + + am65_cpsw_gmii_sel_k3(priv, pdata->phy_interface, priv->port_id); + + ret = am65_cpsw_mdio_init(dev); + if (ret) + goto out; + + ret = am65_cpsw_phy_init(dev); + if (ret) + goto out; + + dev_info(dev, "K3 CPSW: nuss_ver: 0x%08X cpsw_ver: 0x%08X ale_ver: 0x%08X Ports:%u rflow_id_base:%u mdio_freq:%u\n", + readl(cpsw_common->ss_base), + readl(cpsw_common->cpsw_base), + readl(cpsw_common->ale_base), + cpsw_common->port_num, + cpsw_common->rflow_id_base, + cpsw_common->bus_freq); + +out: + clk_free(&cpsw_common->fclk); + power_domain_free(&cpsw_common->pwrdmn); + return ret; +} + +static const struct udevice_id am65_cpsw_nuss_ids[] = { + { .compatible = "ti,am654-cpsw-nuss" }, + { } +}; + +U_BOOT_DRIVER(am65_cpsw_nuss_slave) = { + .name = "am65_cpsw_nuss_slave", + .id = UCLASS_ETH, + .of_match = am65_cpsw_nuss_ids, + .probe = am65_cpsw_probe_cpsw, + .ops = &am65_cpsw_ops, + .priv_auto_alloc_size = sizeof(struct am65_cpsw_priv), + .platdata_auto_alloc_size = sizeof(struct eth_pdata), + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; diff --git a/drivers/net/ti/cpsw_mdio.c b/drivers/net/ti/cpsw_mdio.c index 70f547e6d7..6e8f652011 100644 --- a/drivers/net/ti/cpsw_mdio.c +++ b/drivers/net/ti/cpsw_mdio.c @@ -125,7 +125,7 @@ u32 cpsw_mdio_get_alive(struct mii_dev *bus) return val & GENMASK(15, 0); } -struct mii_dev *cpsw_mdio_init(const char *name, u32 mdio_base, +struct mii_dev *cpsw_mdio_init(const char *name, phys_addr_t mdio_base, u32 bus_freq, int fck_freq) { struct cpsw_mdio *cpsw_mdio; @@ -144,7 +144,7 @@ struct mii_dev *cpsw_mdio_init(const char *name, u32 mdio_base, return NULL; } - cpsw_mdio->regs = (struct cpsw_mdio_regs *)mdio_base; + cpsw_mdio->regs = (struct cpsw_mdio_regs *)(uintptr_t)mdio_base; if (!bus_freq || !fck_freq) cpsw_mdio->div = CPSW_MDIO_DIV_DEF; diff --git a/drivers/net/ti/cpsw_mdio.h b/drivers/net/ti/cpsw_mdio.h index 4a76d4e5c5..dbf4a2dcac 100644 --- a/drivers/net/ti/cpsw_mdio.h +++ b/drivers/net/ti/cpsw_mdio.h @@ -10,7 +10,7 @@ struct cpsw_mdio; -struct mii_dev *cpsw_mdio_init(const char *name, u32 mdio_base, +struct mii_dev *cpsw_mdio_init(const char *name, phys_addr_t mdio_base, u32 bus_freq, int fck_freq); void cpsw_mdio_free(struct mii_dev *bus); u32 cpsw_mdio_get_alive(struct mii_dev *bus); |