diff options
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/Kconfig | 11 | ||||
-rw-r--r-- | drivers/net/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/ag7xxx.c | 328 | ||||
-rw-r--r-- | drivers/net/fec_mxc.c | 1 | ||||
-rw-r--r-- | drivers/net/higmacv300.c | 597 | ||||
-rw-r--r-- | drivers/net/mscc_eswitch/Kconfig | 14 | ||||
-rw-r--r-- | drivers/net/mscc_eswitch/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/mscc_eswitch/jr2_switch.c | 1075 | ||||
-rw-r--r-- | drivers/net/mscc_eswitch/ocelot_switch.c | 27 | ||||
-rw-r--r-- | drivers/net/mscc_eswitch/servalt_switch.c | 622 | ||||
-rw-r--r-- | drivers/net/sh_eth.c | 31 | ||||
-rw-r--r-- | drivers/net/sun8i_emac.c | 112 | ||||
-rw-r--r-- | drivers/net/sunxi_emac.c | 28 | ||||
-rw-r--r-- | drivers/net/ti/cpsw-common.c | 127 | ||||
-rw-r--r-- | drivers/net/ti/cpsw.c | 202 | ||||
-rw-r--r-- | drivers/net/ti/keystone_net.c | 22 | ||||
-rw-r--r-- | drivers/net/zynq_gem.c | 53 |
17 files changed, 2917 insertions, 336 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 77d0b3a01f..6e436b56ab 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -190,7 +190,7 @@ config FEC_MXC_MDIO_BASE config FEC_MXC bool "FEC Ethernet controller" - depends on MX5 || MX6 || MX7 || IMX8 + depends on MX5 || MX6 || MX7 || IMX8 || VF610 help This driver supports the 10/100 Fast Ethernet controller for NXP i.MX processors. @@ -528,4 +528,13 @@ config MEDIATEK_ETH This Driver support MediaTek Ethernet GMAC Say Y to enable support for the MediaTek Ethernet GMAC. +config HIGMACV300_ETH + bool "HiSilicon Gigabit Ethernet Controller" + depends on DM_ETH + select DM_RESET + select PHYLIB + help + This driver supports HIGMACV300 Ethernet controller found on + HiSilicon SoCs. + endif # NETDEVICES diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 51be72b0aa..8d02a37896 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -76,3 +76,4 @@ obj-$(CONFIG_SNI_AVE) += sni_ave.o obj-y += ti/ obj-$(CONFIG_MEDIATEK_ETH) += mtk_eth.o obj-y += mscc_eswitch/ +obj-$(CONFIG_HIGMACV300_ETH) += higmacv300.o diff --git a/drivers/net/ag7xxx.c b/drivers/net/ag7xxx.c index 8146c3170e..7f1dee4b3e 100644 --- a/drivers/net/ag7xxx.c +++ b/drivers/net/ag7xxx.c @@ -3,6 +3,7 @@ * Atheros AR71xx / AR9xxx GMAC driver * * Copyright (C) 2016 Marek Vasut <marex@denx.de> + * Copyright (C) 2019 Rosy Song <rosysong@rosinson.com> */ #include <common.h> @@ -23,6 +24,8 @@ DECLARE_GLOBAL_DATA_PTR; enum ag7xxx_model { AG7XXX_MODEL_AG933X, AG7XXX_MODEL_AG934X, + AG7XXX_MODEL_AG953X, + AG7XXX_MODEL_AG956X }; /* MAC Configuration 1 */ @@ -99,8 +102,29 @@ enum ag7xxx_model { /* Rx Status */ #define AG7XXX_ETH_DMA_RX_STATUS 0x194 +/* Custom register at 0x1805002C */ +#define AG7XXX_ETH_XMII 0x2C +#define AG7XXX_ETH_XMII_TX_INVERT BIT(31) +#define AG7XXX_ETH_XMII_RX_DELAY_LSB 28 +#define AG7XXX_ETH_XMII_RX_DELAY_MASK 0x30000000 +#define AG7XXX_ETH_XMII_RX_DELAY_SET(x) \ + (((x) << AG7XXX_ETH_XMII_RX_DELAY_LSB) & AG7XXX_ETH_XMII_RX_DELAY_MASK) +#define AG7XXX_ETH_XMII_TX_DELAY_LSB 26 +#define AG7XXX_ETH_XMII_TX_DELAY_MASK 0x0c000000 +#define AG7XXX_ETH_XMII_TX_DELAY_SET(x) \ + (((x) << AG7XXX_ETH_XMII_TX_DELAY_LSB) & AG7XXX_ETH_XMII_TX_DELAY_MASK) +#define AG7XXX_ETH_XMII_GIGE BIT(25) + /* Custom register at 0x18070000 */ #define AG7XXX_GMAC_ETH_CFG 0x00 +#define AG7XXX_ETH_CFG_RXDV_DELAY_LSB 16 +#define AG7XXX_ETH_CFG_RXDV_DELAY_MASK 0x00030000 +#define AG7XXX_ETH_CFG_RXDV_DELAY_SET(x) \ + (((x) << AG7XXX_ETH_CFG_RXDV_DELAY_LSB) & AG7XXX_ETH_CFG_RXDV_DELAY_MASK) +#define AG7XXX_ETH_CFG_RXD_DELAY_LSB 14 +#define AG7XXX_ETH_CFG_RXD_DELAY_MASK 0x0000c000 +#define AG7XXX_ETH_CFG_RXD_DELAY_SET(x) \ + (((x) << AG7XXX_ETH_CFG_RXD_DELAY_LSB) & AG7XXX_ETH_CFG_RXD_DELAY_MASK) #define AG7XXX_ETH_CFG_SW_PHY_ADDR_SWAP BIT(8) #define AG7XXX_ETH_CFG_SW_PHY_SWAP BIT(7) #define AG7XXX_ETH_CFG_SW_ONLY_MODE BIT(6) @@ -197,24 +221,33 @@ static int ag7xxx_switch_reg_read(struct mii_dev *bus, int reg, u32 *val) u32 reg_addr; u32 phy_temp; u32 reg_temp; + u32 reg_temp_w = (reg & 0xfffffffc) >> 1; u16 rv = 0; int ret; - if (priv->model == AG7XXX_MODEL_AG933X) { + if (priv->model == AG7XXX_MODEL_AG933X || + priv->model == AG7XXX_MODEL_AG953X) { phy_addr = 0x1f; reg_addr = 0x10; - } else if (priv->model == AG7XXX_MODEL_AG934X) { + } else if (priv->model == AG7XXX_MODEL_AG934X || + priv->model == AG7XXX_MODEL_AG956X) { phy_addr = 0x18; reg_addr = 0x00; } else return -EINVAL; - ret = ag7xxx_switch_write(bus, phy_addr, reg_addr, reg >> 9); + if (priv->model == AG7XXX_MODEL_AG956X) + ret = ag7xxx_switch_write(bus, phy_addr, reg_addr, (reg >> 9) & 0x1ff); + else + ret = ag7xxx_switch_write(bus, phy_addr, reg_addr, reg >> 9); if (ret) return ret; phy_temp = ((reg >> 6) & 0x7) | 0x10; - reg_temp = (reg >> 1) & 0x1e; + if (priv->model == AG7XXX_MODEL_AG956X) + reg_temp = reg_temp_w & 0x1f; + else + reg_temp = (reg >> 1) & 0x1e; *val = 0; ret = ag7xxx_switch_read(bus, phy_temp, reg_temp | 0, &rv); @@ -222,7 +255,13 @@ static int ag7xxx_switch_reg_read(struct mii_dev *bus, int reg, u32 *val) return ret; *val |= rv; - ret = ag7xxx_switch_read(bus, phy_temp, reg_temp | 1, &rv); + if (priv->model == AG7XXX_MODEL_AG956X) { + phy_temp = (((reg_temp_w + 1) >> 5) & 0x7) | 0x10; + reg_temp = (reg_temp_w + 1) & 0x1f; + ret = ag7xxx_switch_read(bus, phy_temp, reg_temp, &rv); + } else { + ret = ag7xxx_switch_read(bus, phy_temp, reg_temp | 1, &rv); + } if (ret < 0) return ret; *val |= (rv << 16); @@ -237,23 +276,34 @@ static int ag7xxx_switch_reg_write(struct mii_dev *bus, int reg, u32 val) u32 reg_addr; u32 phy_temp; u32 reg_temp; + u32 reg_temp_w = (reg & 0xfffffffc) >> 1; int ret; - if (priv->model == AG7XXX_MODEL_AG933X) { + if (priv->model == AG7XXX_MODEL_AG933X || + priv->model == AG7XXX_MODEL_AG953X) { phy_addr = 0x1f; reg_addr = 0x10; - } else if (priv->model == AG7XXX_MODEL_AG934X) { + } else if (priv->model == AG7XXX_MODEL_AG934X || + priv->model == AG7XXX_MODEL_AG956X) { phy_addr = 0x18; reg_addr = 0x00; } else return -EINVAL; - ret = ag7xxx_switch_write(bus, phy_addr, reg_addr, reg >> 9); + if (priv->model == AG7XXX_MODEL_AG956X) + ret = ag7xxx_switch_write(bus, phy_addr, reg_addr, (reg >> 9) & 0x1ff); + else + ret = ag7xxx_switch_write(bus, phy_addr, reg_addr, reg >> 9); if (ret) return ret; - phy_temp = ((reg >> 6) & 0x7) | 0x10; - reg_temp = (reg >> 1) & 0x1e; + if (priv->model == AG7XXX_MODEL_AG956X) { + reg_temp = (reg_temp_w + 1) & 0x1f; + phy_temp = (((reg_temp_w + 1) >> 5) & 0x7) | 0x10; + } else { + phy_temp = ((reg >> 6) & 0x7) | 0x10; + reg_temp = (reg >> 1) & 0x1e; + } /* * The switch on AR933x has some special register behavior, which @@ -272,10 +322,18 @@ static int ag7xxx_switch_reg_write(struct mii_dev *bus, int reg, u32 val) if (ret < 0) return ret; } else { - ret = ag7xxx_switch_write(bus, phy_temp, reg_temp | 1, val >> 16); + if (priv->model == AG7XXX_MODEL_AG956X) + ret = ag7xxx_switch_write(bus, phy_temp, reg_temp, val >> 16); + else + ret = ag7xxx_switch_write(bus, phy_temp, reg_temp | 1, val >> 16); if (ret < 0) return ret; + if (priv->model == AG7XXX_MODEL_AG956X) { + phy_temp = ((reg_temp_w >> 5) & 0x7) | 0x10; + reg_temp = reg_temp_w & 0x1f; + } + ret = ag7xxx_switch_write(bus, phy_temp, reg_temp | 0, val & 0xffff); if (ret < 0) return ret; @@ -598,10 +656,19 @@ static int ag7xxx_mii_setup(struct udevice *dev) return 0; } - if (priv->model == AG7XXX_MODEL_AG934X) { - writel(AG7XXX_ETH_MII_MGMT_CFG_RESET | 0x4, + if (priv->model == AG7XXX_MODEL_AG934X) + reg = 0x4; + else if (priv->model == AG7XXX_MODEL_AG953X) + reg = 0x2; + else if (priv->model == AG7XXX_MODEL_AG956X) + reg = 0x7; + + if (priv->model == AG7XXX_MODEL_AG934X || + priv->model == AG7XXX_MODEL_AG953X || + priv->model == AG7XXX_MODEL_AG956X) { + writel(AG7XXX_ETH_MII_MGMT_CFG_RESET | reg, priv->regs + AG7XXX_ETH_MII_MGMT_CFG); - writel(0x4, priv->regs + AG7XXX_ETH_MII_MGMT_CFG); + writel(reg, priv->regs + AG7XXX_ETH_MII_MGMT_CFG); return 0; } @@ -698,14 +765,126 @@ static int ag933x_phy_setup_lan(struct udevice *dev) return 0; } +static int ag953x_phy_setup_wan(struct udevice *dev) +{ + int ret; + u32 reg = 0; + struct ar7xxx_eth_priv *priv = dev_get_priv(dev); + + /* Set wan port connect to GE0 */ + ret = ag7xxx_switch_reg_read(priv->bus, 0x8, ®); + if (ret) + return ret; + + ret = ag7xxx_switch_reg_write(priv->bus, 0x8, reg | BIT(28)); + if (ret) + return ret; + + /* Configure switch port 4 (GMAC0) */ + ret = ag7xxx_switch_write(priv->bus, 4, MII_BMCR, 0x9000); + if (ret) + return ret; + + return 0; +} + +static int ag953x_phy_setup_lan(struct udevice *dev) +{ + struct ar7xxx_eth_priv *priv = dev_get_priv(dev); + int i, ret; + u32 reg = 0; + + /* Reset the switch */ + ret = ag7xxx_switch_reg_read(priv->bus, 0, ®); + if (ret) + return ret; + + ret = ag7xxx_switch_reg_write(priv->bus, 0, reg | BIT(31)); + if (ret) + return ret; + + do { + ret = ag7xxx_switch_reg_read(priv->bus, 0, ®); + if (ret) + return ret; + } while (reg & BIT(31)); + + ret = ag7xxx_switch_reg_write(priv->bus, 0x100, 0x4e); + if (ret) + return ret; + + /* Set GMII mode */ + ret = ag7xxx_switch_reg_read(priv->bus, 0x4, ®); + if (ret) + return ret; + + ret = ag7xxx_switch_reg_write(priv->bus, 0x4, reg | BIT(6)); + if (ret) + return ret; + + /* Configure switch ports 0...4 (GMAC1) */ + for (i = 0; i < 5; i++) { + ret = ag7xxx_switch_write(priv->bus, i, MII_BMCR, 0x9000); + if (ret) + return ret; + } + + for (i = 0; i < 5; i++) { + ret = ag7xxx_switch_reg_write(priv->bus, (i + 2) * 0x100, BIT(9)); + if (ret) + return ret; + } + + /* QM Control */ + ret = ag7xxx_switch_reg_write(priv->bus, 0x38, 0xc000050e); + if (ret) + return ret; + + /* Disable Atheros header */ + ret = ag7xxx_switch_reg_write(priv->bus, 0x104, 0x4004); + if (ret) + return ret; + + /* Tag priority mapping */ + ret = ag7xxx_switch_reg_write(priv->bus, 0x70, 0xfa50); + if (ret) + return ret; + + /* Enable ARP packets to the CPU */ + ret = ag7xxx_switch_reg_read(priv->bus, 0x5c, ®); + if (ret) + return ret; + + ret = ag7xxx_switch_reg_write(priv->bus, 0x5c, reg | 0x100000); + if (ret) + return ret; + + /* Enable broadcast packets to the CPU */ + ret = ag7xxx_switch_reg_read(priv->bus, 0x2c, ®); + if (ret) + return ret; + + ret = ag7xxx_switch_reg_write(priv->bus, 0x2c, reg | BIT(25) | BIT(26)); + if (ret) + return ret; + + return 0; +} + static int ag933x_phy_setup_reset_set(struct udevice *dev, int port) { struct ar7xxx_eth_priv *priv = dev_get_priv(dev); int ret; - ret = ag7xxx_mdio_write(priv->bus, port, 0, MII_ADVERTISE, - ADVERTISE_ALL | ADVERTISE_PAUSE_CAP | - ADVERTISE_PAUSE_ASYM); + if (priv->model == AG7XXX_MODEL_AG953X || + priv->model == AG7XXX_MODEL_AG956X) { + ret = ag7xxx_switch_write(priv->bus, port, MII_ADVERTISE, + ADVERTISE_ALL); + } else { + ret = ag7xxx_mdio_write(priv->bus, port, 0, MII_ADVERTISE, + ADVERTISE_ALL | ADVERTISE_PAUSE_CAP | + ADVERTISE_PAUSE_ASYM); + } if (ret) return ret; @@ -714,8 +893,18 @@ static int ag933x_phy_setup_reset_set(struct udevice *dev, int port) ADVERTISE_1000FULL); if (ret) return ret; + } else if (priv->model == AG7XXX_MODEL_AG956X) { + ret = ag7xxx_switch_write(priv->bus, port, MII_CTRL1000, + ADVERTISE_1000FULL); + if (ret) + return ret; } + if (priv->model == AG7XXX_MODEL_AG953X || + priv->model == AG7XXX_MODEL_AG956X) + return ag7xxx_switch_write(priv->bus, port, MII_BMCR, + BMCR_ANENABLE | BMCR_RESET); + return ag7xxx_mdio_write(priv->bus, port, 0, MII_BMCR, BMCR_ANENABLE | BMCR_RESET); } @@ -724,13 +913,24 @@ static int ag933x_phy_setup_reset_fin(struct udevice *dev, int port) { struct ar7xxx_eth_priv *priv = dev_get_priv(dev); int ret; + u16 reg; - do { - ret = ag7xxx_mdio_read(priv->bus, port, 0, MII_BMCR); - if (ret < 0) - return ret; - mdelay(10); - } while (ret & BMCR_RESET); + if (priv->model == AG7XXX_MODEL_AG953X || + priv->model == AG7XXX_MODEL_AG956X) { + do { + ret = ag7xxx_switch_read(priv->bus, port, MII_BMCR, ®); + if (ret < 0) + return ret; + mdelay(10); + } while (reg & BMCR_RESET); + } else { + do { + ret = ag7xxx_mdio_read(priv->bus, port, 0, MII_BMCR); + if (ret < 0) + return ret; + mdelay(10); + } while (ret & BMCR_RESET); + } return 0; } @@ -739,10 +939,13 @@ static int ag933x_phy_setup_common(struct udevice *dev) { struct ar7xxx_eth_priv *priv = dev_get_priv(dev); int i, ret, phymax; + u16 reg; if (priv->model == AG7XXX_MODEL_AG933X) phymax = 4; - else if (priv->model == AG7XXX_MODEL_AG934X) + else if (priv->model == AG7XXX_MODEL_AG934X || + priv->model == AG7XXX_MODEL_AG953X || + priv->model == AG7XXX_MODEL_AG956X) phymax = 5; else return -EINVAL; @@ -757,7 +960,10 @@ static int ag933x_phy_setup_common(struct udevice *dev) return ret; /* Read out link status */ - ret = ag7xxx_mdio_read(priv->bus, phymax, 0, MII_MIPSCR); + if (priv->model == AG7XXX_MODEL_AG953X) + ret = ag7xxx_switch_read(priv->bus, phymax, MII_MIPSCR, ®); + else + ret = ag7xxx_mdio_read(priv->bus, phymax, 0, MII_MIPSCR); if (ret < 0) return ret; @@ -779,7 +985,11 @@ static int ag933x_phy_setup_common(struct udevice *dev) for (i = 0; i < phymax; i++) { /* Read out link status */ - ret = ag7xxx_mdio_read(priv->bus, i, 0, MII_MIPSCR); + if (priv->model == AG7XXX_MODEL_AG953X || + priv->model == AG7XXX_MODEL_AG956X) + ret = ag7xxx_switch_read(priv->bus, i, MII_MIPSCR, ®); + else + ret = ag7xxx_mdio_read(priv->bus, i, 0, MII_MIPSCR); if (ret < 0) return ret; } @@ -841,6 +1051,63 @@ static int ag934x_phy_setup(struct udevice *dev) return 0; } +static int ag956x_phy_setup(struct udevice *dev) +{ + struct ar7xxx_eth_priv *priv = dev_get_priv(dev); + int i, ret; + u32 reg, ctrl; + + ret = ag7xxx_switch_reg_read(priv->bus, 0x0, ®); + if (ret) + return ret; + if ((reg & 0xffff) >= 0x1301) + ctrl = 0xc74164de; + else + ctrl = 0xc74164d0; + + ret = ag7xxx_switch_reg_write(priv->bus, 0x4, BIT(7)); + if (ret) + return ret; + + ret = ag7xxx_switch_reg_write(priv->bus, 0xe0, ctrl); + if (ret) + return ret; + + ret = ag7xxx_switch_reg_write(priv->bus, 0x624, 0x7f7f7f7f); + if (ret) + return ret; + + /* + * Values suggested by the switch team when s17 in sgmii + * configuration. 0x10(S17_PWS_REG) = 0x602613a0 + */ + ret = ag7xxx_switch_reg_write(priv->bus, 0x10, 0x602613a0); + if (ret) + return ret; + + ret = ag7xxx_switch_reg_write(priv->bus, 0x7c, 0x0000007e); + if (ret) + return ret; + + /* AR8337/AR8334 v1.0 fixup */ + ret = ag7xxx_switch_reg_read(priv->bus, 0, ®); + if (ret) + return ret; + if ((reg & 0xffff) == 0x1301) { + for (i = 0; i < 5; i++) { + /* Turn on Gigabit clock */ + ret = ag7xxx_switch_write(priv->bus, i, 0x1d, 0x3d); + if (ret) + return ret; + ret = ag7xxx_switch_write(priv->bus, i, 0x1e, 0x6820); + if (ret) + return ret; + } + } + + return 0; +} + static int ag7xxx_mac_probe(struct udevice *dev) { struct ar7xxx_eth_priv *priv = dev_get_priv(dev); @@ -858,8 +1125,15 @@ static int ag7xxx_mac_probe(struct udevice *dev) ret = ag933x_phy_setup_wan(dev); else ret = ag933x_phy_setup_lan(dev); + } else if (priv->model == AG7XXX_MODEL_AG953X) { + if (priv->interface == PHY_INTERFACE_MODE_RMII) + ret = ag953x_phy_setup_wan(dev); + else + ret = ag953x_phy_setup_lan(dev); } else if (priv->model == AG7XXX_MODEL_AG934X) { ret = ag934x_phy_setup(dev); + } else if (priv->model == AG7XXX_MODEL_AG956X) { + ret = ag956x_phy_setup(dev); } else { return -EINVAL; } @@ -997,6 +1271,8 @@ static int ag7xxx_eth_ofdata_to_platdata(struct udevice *dev) static const struct udevice_id ag7xxx_eth_ids[] = { { .compatible = "qca,ag933x-mac", .data = AG7XXX_MODEL_AG933X }, { .compatible = "qca,ag934x-mac", .data = AG7XXX_MODEL_AG934X }, + { .compatible = "qca,ag953x-mac", .data = AG7XXX_MODEL_AG953X }, + { .compatible = "qca,ag956x-mac", .data = AG7XXX_MODEL_AG956X }, { } }; diff --git a/drivers/net/fec_mxc.c b/drivers/net/fec_mxc.c index 84f010d805..a672250e16 100644 --- a/drivers/net/fec_mxc.c +++ b/drivers/net/fec_mxc.c @@ -1491,6 +1491,7 @@ static const struct udevice_id fecmxc_ids[] = { { .compatible = "fsl,imx6ul-fec" }, { .compatible = "fsl,imx53-fec" }, { .compatible = "fsl,imx7d-fec" }, + { .compatible = "fsl,mvf600-fec" }, { } }; diff --git a/drivers/net/higmacv300.c b/drivers/net/higmacv300.c new file mode 100644 index 0000000000..1be8359133 --- /dev/null +++ b/drivers/net/higmacv300.c @@ -0,0 +1,597 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019, Linaro Limited + */ + +#include <asm/io.h> +#include <common.h> +#include <console.h> +#include <linux/bug.h> +#include <linux/mii.h> +#include <miiphy.h> +#include <net.h> +#include <reset.h> +#include <wait_bit.h> + +#define STATION_ADDR_LOW 0x0000 +#define STATION_ADDR_HIGH 0x0004 +#define MAC_DUPLEX_HALF_CTRL 0x0008 +#define PORT_MODE 0x0040 +#define PORT_EN 0x0044 +#define BIT_TX_EN BIT(2) +#define BIT_RX_EN BIT(1) +#define MODE_CHANGE_EN 0x01b4 +#define BIT_MODE_CHANGE_EN BIT(0) +#define MDIO_SINGLE_CMD 0x03c0 +#define BIT_MDIO_BUSY BIT(20) +#define MDIO_READ (BIT(17) | BIT_MDIO_BUSY) +#define MDIO_WRITE (BIT(16) | BIT_MDIO_BUSY) +#define MDIO_SINGLE_DATA 0x03c4 +#define MDIO_RDATA_STATUS 0x03d0 +#define BIT_MDIO_RDATA_INVALID BIT(0) +#define RX_FQ_START_ADDR 0x0500 +#define RX_FQ_DEPTH 0x0504 +#define RX_FQ_WR_ADDR 0x0508 +#define RX_FQ_RD_ADDR 0x050c +#define RX_FQ_REG_EN 0x0518 +#define RX_BQ_START_ADDR 0x0520 +#define RX_BQ_DEPTH 0x0524 +#define RX_BQ_WR_ADDR 0x0528 +#define RX_BQ_RD_ADDR 0x052c +#define RX_BQ_REG_EN 0x0538 +#define TX_BQ_START_ADDR 0x0580 +#define TX_BQ_DEPTH 0x0584 +#define TX_BQ_WR_ADDR 0x0588 +#define TX_BQ_RD_ADDR 0x058c +#define TX_BQ_REG_EN 0x0598 +#define TX_RQ_START_ADDR 0x05a0 +#define TX_RQ_DEPTH 0x05a4 +#define TX_RQ_WR_ADDR 0x05a8 +#define TX_RQ_RD_ADDR 0x05ac +#define TX_RQ_REG_EN 0x05b8 +#define BIT_START_ADDR_EN BIT(2) +#define BIT_DEPTH_EN BIT(1) +#define DESC_WR_RD_ENA 0x05cc +#define BIT_RX_OUTCFF_WR BIT(3) +#define BIT_RX_CFF_RD BIT(2) +#define BIT_TX_OUTCFF_WR BIT(1) +#define BIT_TX_CFF_RD BIT(0) +#define BITS_DESC_ENA (BIT_RX_OUTCFF_WR | BIT_RX_CFF_RD | \ + BIT_TX_OUTCFF_WR | BIT_TX_CFF_RD) + +/* MACIF_CTRL */ +#define RGMII_SPEED_1000 0x2c +#define RGMII_SPEED_100 0x2f +#define RGMII_SPEED_10 0x2d +#define MII_SPEED_100 0x0f +#define MII_SPEED_10 0x0d +#define GMAC_SPEED_1000 0x05 +#define GMAC_SPEED_100 0x01 +#define GMAC_SPEED_10 0x00 +#define GMAC_FULL_DUPLEX BIT(4) + +#define RX_DESC_NUM 64 +#define TX_DESC_NUM 2 +#define DESC_SIZE 32 +#define DESC_WORD_SHIFT 3 +#define DESC_BYTE_SHIFT 5 +#define DESC_CNT(n) ((n) >> DESC_BYTE_SHIFT) +#define DESC_BYTE(n) ((n) << DESC_BYTE_SHIFT) +#define DESC_VLD_FREE 0 +#define DESC_VLD_BUSY 1 + +#define MAC_MAX_FRAME_SIZE 1600 + +enum higmac_queue { + RX_FQ, + RX_BQ, + TX_BQ, + TX_RQ, +}; + +struct higmac_desc { + unsigned int buf_addr; + unsigned int buf_len:11; + unsigned int reserve0:5; + unsigned int data_len:11; + unsigned int reserve1:2; + unsigned int fl:2; + unsigned int descvid:1; + unsigned int reserve2[6]; +}; + +struct higmac_priv { + void __iomem *base; + void __iomem *macif_ctrl; + struct reset_ctl rst_phy; + struct higmac_desc *rxfq; + struct higmac_desc *rxbq; + struct higmac_desc *txbq; + struct higmac_desc *txrq; + int rxdesc_in_use; + struct mii_dev *bus; + struct phy_device *phydev; + int phyintf; + int phyaddr; +}; + +#define flush_desc(d) flush_cache((unsigned long)(d), sizeof(*(d))) +#define invalidate_desc(d) \ + invalidate_dcache_range((unsigned long)(d), \ + (unsigned long)(d) + sizeof(*(d))) + +static int higmac_write_hwaddr(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_platdata(dev); + struct higmac_priv *priv = dev_get_priv(dev); + unsigned char *mac = pdata->enetaddr; + u32 val; + + val = mac[1] | (mac[0] << 8); + writel(val, priv->base + STATION_ADDR_HIGH); + + val = mac[5] | (mac[4] << 8) | (mac[3] << 16) | (mac[2] << 24); + writel(val, priv->base + STATION_ADDR_LOW); + + return 0; +} + +static int higmac_free_pkt(struct udevice *dev, uchar *packet, int length) +{ + struct higmac_priv *priv = dev_get_priv(dev); + + /* Inform GMAC that the RX descriptor is no longer in use */ + writel(DESC_BYTE(priv->rxdesc_in_use), priv->base + RX_BQ_RD_ADDR); + + return 0; +} + +static int higmac_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct higmac_priv *priv = dev_get_priv(dev); + struct higmac_desc *fqd = priv->rxfq; + struct higmac_desc *bqd = priv->rxbq; + int fqw_pos, fqr_pos, bqw_pos, bqr_pos; + int timeout = 100000; + int len = 0; + int space; + int i; + + fqw_pos = DESC_CNT(readl(priv->base + RX_FQ_WR_ADDR)); + fqr_pos = DESC_CNT(readl(priv->base + RX_FQ_RD_ADDR)); + + if (fqw_pos >= fqr_pos) + space = RX_DESC_NUM - (fqw_pos - fqr_pos); + else + space = fqr_pos - fqw_pos; + + /* Leave one free to distinguish full filled from empty buffer */ + for (i = 0; i < space - 1; i++) { + fqd = priv->rxfq + fqw_pos; + invalidate_dcache_range(fqd->buf_addr, + fqd->buf_addr + MAC_MAX_FRAME_SIZE); + + if (++fqw_pos >= RX_DESC_NUM) + fqw_pos = 0; + + writel(DESC_BYTE(fqw_pos), priv->base + RX_FQ_WR_ADDR); + } + + bqr_pos = DESC_CNT(readl(priv->base + RX_BQ_RD_ADDR)); + bqd += bqr_pos; + /* BQ is only ever written by GMAC */ + invalidate_desc(bqd); + + do { + bqw_pos = DESC_CNT(readl(priv->base + RX_BQ_WR_ADDR)); + udelay(1); + } while (--timeout && bqw_pos == bqr_pos); + + if (!timeout) + return -ETIMEDOUT; + + if (++bqr_pos >= RX_DESC_NUM) + bqr_pos = 0; + + len = bqd->data_len; + + /* CPU should not have touched this buffer since we added it to FQ */ + invalidate_dcache_range(bqd->buf_addr, bqd->buf_addr + len); + *packetp = (void *)(unsigned long)bqd->buf_addr; + + /* Record the RX_BQ descriptor that is holding RX data */ + priv->rxdesc_in_use = bqr_pos; + + return len; +} + +static int higmac_send(struct udevice *dev, void *packet, int length) +{ + struct higmac_priv *priv = dev_get_priv(dev); + struct higmac_desc *bqd = priv->txbq; + int bqw_pos, rqw_pos, rqr_pos; + int timeout = 1000; + + flush_cache((unsigned long)packet, length); + + bqw_pos = DESC_CNT(readl(priv->base + TX_BQ_WR_ADDR)); + bqd += bqw_pos; + bqd->buf_addr = (unsigned long)packet; + bqd->descvid = DESC_VLD_BUSY; + bqd->data_len = length; + flush_desc(bqd); + + if (++bqw_pos >= TX_DESC_NUM) + bqw_pos = 0; + + writel(DESC_BYTE(bqw_pos), priv->base + TX_BQ_WR_ADDR); + + rqr_pos = DESC_CNT(readl(priv->base + TX_RQ_RD_ADDR)); + if (++rqr_pos >= TX_DESC_NUM) + rqr_pos = 0; + + do { + rqw_pos = DESC_CNT(readl(priv->base + TX_RQ_WR_ADDR)); + udelay(1); + } while (--timeout && rqr_pos != rqw_pos); + + if (!timeout) + return -ETIMEDOUT; + + writel(DESC_BYTE(rqr_pos), priv->base + TX_RQ_RD_ADDR); + + return 0; +} + +static int higmac_adjust_link(struct higmac_priv *priv) +{ + struct phy_device *phydev = priv->phydev; + int interface = priv->phyintf; + u32 val; + + switch (interface) { + case PHY_INTERFACE_MODE_RGMII: + if (phydev->speed == SPEED_1000) + val = RGMII_SPEED_1000; + else if (phydev->speed == SPEED_100) + val = RGMII_SPEED_100; + else + val = RGMII_SPEED_10; + break; + case PHY_INTERFACE_MODE_MII: + if (phydev->speed == SPEED_100) + val = MII_SPEED_100; + else + val = MII_SPEED_10; + break; + default: + debug("unsupported mode: %d\n", interface); + return -EINVAL; + } + + if (phydev->duplex) + val |= GMAC_FULL_DUPLEX; + + writel(val, priv->macif_ctrl); + + if (phydev->speed == SPEED_1000) + val = GMAC_SPEED_1000; + else if (phydev->speed == SPEED_100) + val = GMAC_SPEED_100; + else + val = GMAC_SPEED_10; + + writel(BIT_MODE_CHANGE_EN, priv->base + MODE_CHANGE_EN); + writel(val, priv->base + PORT_MODE); + writel(0, priv->base + MODE_CHANGE_EN); + writel(phydev->duplex, priv->base + MAC_DUPLEX_HALF_CTRL); + + return 0; +} + +static int higmac_start(struct udevice *dev) +{ + struct higmac_priv *priv = dev_get_priv(dev); + struct phy_device *phydev = priv->phydev; + int ret; + + ret = phy_startup(phydev); + if (ret) + return ret; + + if (!phydev->link) { + debug("%s: link down\n", phydev->dev->name); + return -ENODEV; + } + + ret = higmac_adjust_link(priv); + if (ret) + return ret; + + /* Enable port */ + writel(BITS_DESC_ENA, priv->base + DESC_WR_RD_ENA); + writel(BIT_TX_EN | BIT_RX_EN, priv->base + PORT_EN); + + return 0; +} + +static void higmac_stop(struct udevice *dev) +{ + struct higmac_priv *priv = dev_get_priv(dev); + + /* Disable port */ + writel(0, priv->base + PORT_EN); + writel(0, priv->base + DESC_WR_RD_ENA); +} + +static const struct eth_ops higmac_ops = { + .start = higmac_start, + .send = higmac_send, + .recv = higmac_recv, + .free_pkt = higmac_free_pkt, + .stop = higmac_stop, + .write_hwaddr = higmac_write_hwaddr, +}; + +static int higmac_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) +{ + struct higmac_priv *priv = bus->priv; + int ret; + + ret = wait_for_bit_le32(priv->base + MDIO_SINGLE_CMD, BIT_MDIO_BUSY, + false, 1000, false); + if (ret) + return ret; + + writel(MDIO_READ | addr << 8 | reg, priv->base + MDIO_SINGLE_CMD); + + ret = wait_for_bit_le32(priv->base + MDIO_SINGLE_CMD, BIT_MDIO_BUSY, + false, 1000, false); + if (ret) + return ret; + + if (readl(priv->base + MDIO_RDATA_STATUS) & BIT_MDIO_RDATA_INVALID) + return -EINVAL; + + return readl(priv->base + MDIO_SINGLE_DATA) >> 16; +} + +static int higmac_mdio_write(struct mii_dev *bus, int addr, int devad, + int reg, u16 value) +{ + struct higmac_priv *priv = bus->priv; + int ret; + + ret = wait_for_bit_le32(priv->base + MDIO_SINGLE_CMD, BIT_MDIO_BUSY, + false, 1000, false); + if (ret) + return ret; + + writel(value, priv->base + MDIO_SINGLE_DATA); + writel(MDIO_WRITE | addr << 8 | reg, priv->base + MDIO_SINGLE_CMD); + + return 0; +} + +static int higmac_init_rx_descs(struct higmac_desc *descs, int num) +{ + int i; + + for (i = 0; i < num; i++) { + struct higmac_desc *desc = &descs[i]; + + desc->buf_addr = (unsigned long)memalign(ARCH_DMA_MINALIGN, + MAC_MAX_FRAME_SIZE); + if (!desc->buf_addr) + goto free_bufs; + + desc->descvid = DESC_VLD_FREE; + desc->buf_len = MAC_MAX_FRAME_SIZE - 1; + flush_desc(desc); + } + + return 0; + +free_bufs: + while (--i > 0) + free((void *)(unsigned long)descs[i].buf_addr); + return -ENOMEM; +} + +static int higmac_init_hw_queue(struct higmac_priv *priv, + enum higmac_queue queue) +{ + struct higmac_desc *desc, **pdesc; + u32 regaddr, regen, regdep; + int depth; + int len; + + switch (queue) { + case RX_FQ: + regaddr = RX_FQ_START_ADDR; + regen = RX_FQ_REG_EN; + regdep = RX_FQ_DEPTH; + depth = RX_DESC_NUM; + pdesc = &priv->rxfq; + break; + case RX_BQ: + regaddr = RX_BQ_START_ADDR; + regen = RX_BQ_REG_EN; + regdep = RX_BQ_DEPTH; + depth = RX_DESC_NUM; + pdesc = &priv->rxbq; + break; + case TX_BQ: + regaddr = TX_BQ_START_ADDR; + regen = TX_BQ_REG_EN; + regdep = TX_BQ_DEPTH; + depth = TX_DESC_NUM; + pdesc = &priv->txbq; + break; + case TX_RQ: + regaddr = TX_RQ_START_ADDR; + regen = TX_RQ_REG_EN; + regdep = TX_RQ_DEPTH; + depth = TX_DESC_NUM; + pdesc = &priv->txrq; + break; + } + + /* Enable depth */ + writel(BIT_DEPTH_EN, priv->base + regen); + writel(depth << DESC_WORD_SHIFT, priv->base + regdep); + writel(0, priv->base + regen); + + len = depth * sizeof(*desc); + desc = memalign(ARCH_DMA_MINALIGN, len); + if (!desc) + return -ENOMEM; + memset(desc, 0, len); + flush_cache((unsigned long)desc, len); + *pdesc = desc; + + /* Set up RX_FQ descriptors */ + if (queue == RX_FQ) + higmac_init_rx_descs(desc, depth); + + /* Enable start address */ + writel(BIT_START_ADDR_EN, priv->base + regen); + writel((unsigned long)desc, priv->base + regaddr); + writel(0, priv->base + regen); + + return 0; +} + +static int higmac_hw_init(struct higmac_priv *priv) +{ + int ret; + + /* Initialize hardware queues */ + ret = higmac_init_hw_queue(priv, RX_FQ); + if (ret) + return ret; + + ret = higmac_init_hw_queue(priv, RX_BQ); + if (ret) + goto free_rx_fq; + + ret = higmac_init_hw_queue(priv, TX_BQ); + if (ret) + goto free_rx_bq; + + ret = higmac_init_hw_queue(priv, TX_RQ); + if (ret) + goto free_tx_bq; + + /* Reset phy */ + reset_deassert(&priv->rst_phy); + mdelay(10); + reset_assert(&priv->rst_phy); + mdelay(30); + reset_deassert(&priv->rst_phy); + mdelay(30); + + return 0; + +free_tx_bq: + free(priv->txbq); +free_rx_bq: + free(priv->rxbq); +free_rx_fq: + free(priv->rxfq); + return ret; +} + +static int higmac_probe(struct udevice *dev) +{ + struct higmac_priv *priv = dev_get_priv(dev); + struct phy_device *phydev; + struct mii_dev *bus; + int ret; + + ret = higmac_hw_init(priv); + if (ret) + return ret; + + bus = mdio_alloc(); + if (!bus) + return -ENOMEM; + + bus->read = higmac_mdio_read; + bus->write = higmac_mdio_write; + bus->priv = priv; + priv->bus = bus; + + ret = mdio_register_seq(bus, dev->seq); + if (ret) + return ret; + + phydev = phy_connect(bus, priv->phyaddr, dev, priv->phyintf); + if (!phydev) + return -ENODEV; + + phydev->supported &= PHY_GBIT_FEATURES; + phydev->advertising = phydev->supported; + priv->phydev = phydev; + + return phy_config(phydev); +} + +static int higmac_remove(struct udevice *dev) +{ + struct higmac_priv *priv = dev_get_priv(dev); + int i; + + mdio_unregister(priv->bus); + mdio_free(priv->bus); + + /* Free RX packet buffers */ + for (i = 0; i < RX_DESC_NUM; i++) + free((void *)(unsigned long)priv->rxfq[i].buf_addr); + + return 0; +} + +static int higmac_ofdata_to_platdata(struct udevice *dev) +{ + struct higmac_priv *priv = dev_get_priv(dev); + int phyintf = PHY_INTERFACE_MODE_NONE; + const char *phy_mode; + ofnode phy_node; + + priv->base = dev_remap_addr_index(dev, 0); + priv->macif_ctrl = dev_remap_addr_index(dev, 1); + + phy_mode = dev_read_string(dev, "phy-mode"); + if (phy_mode) + phyintf = phy_get_interface_by_name(phy_mode); + if (phyintf == PHY_INTERFACE_MODE_NONE) + return -ENODEV; + priv->phyintf = phyintf; + + phy_node = dev_read_subnode(dev, "phy"); + if (!ofnode_valid(phy_node)) { + debug("failed to find phy node\n"); + return -ENODEV; + } + priv->phyaddr = ofnode_read_u32_default(phy_node, "reg", 0); + + return reset_get_by_name(dev, "phy", &priv->rst_phy); +} + +static const struct udevice_id higmac_ids[] = { + { .compatible = "hisilicon,hi3798cv200-gmac" }, + { } +}; + +U_BOOT_DRIVER(eth_higmac) = { + .name = "eth_higmac", + .id = UCLASS_ETH, + .of_match = higmac_ids, + .ofdata_to_platdata = higmac_ofdata_to_platdata, + .probe = higmac_probe, + .remove = higmac_remove, + .ops = &higmac_ops, + .priv_auto_alloc_size = sizeof(struct higmac_priv), + .platdata_auto_alloc_size = sizeof(struct eth_pdata), +}; diff --git a/drivers/net/mscc_eswitch/Kconfig b/drivers/net/mscc_eswitch/Kconfig index 88e5a97c4b..6359d0b610 100644 --- a/drivers/net/mscc_eswitch/Kconfig +++ b/drivers/net/mscc_eswitch/Kconfig @@ -15,3 +15,17 @@ config MSCC_LUTON_SWITCH select PHYLIB help This driver supports the Luton network switch device. + +config MSCC_JR2_SWITCH + bool "Jaguar2 switch driver" + depends on DM_ETH && ARCH_MSCC + select PHYLIB + help + This driver supports the Jaguar2 network switch device. + +config MSCC_SERVALT_SWITCH + bool "Servalt switch driver" + depends on DM_ETH && ARCH_MSCC + select PHYLIB + help + This driver supports the Servalt network switch device. diff --git a/drivers/net/mscc_eswitch/Makefile b/drivers/net/mscc_eswitch/Makefile index 751a839a5f..bffd8ec77b 100644 --- a/drivers/net/mscc_eswitch/Makefile +++ b/drivers/net/mscc_eswitch/Makefile @@ -1,3 +1,5 @@ obj-$(CONFIG_MSCC_OCELOT_SWITCH) += ocelot_switch.o mscc_miim.o mscc_xfer.o mscc_mac_table.o obj-$(CONFIG_MSCC_LUTON_SWITCH) += luton_switch.o mscc_miim.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 diff --git a/drivers/net/mscc_eswitch/jr2_switch.c b/drivers/net/mscc_eswitch/jr2_switch.c new file mode 100644 index 0000000000..60d408f1c7 --- /dev/null +++ b/drivers/net/mscc_eswitch/jr2_switch.c @@ -0,0 +1,1075 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2018 Microsemi Corporation + */ + +#include <common.h> +#include <config.h> +#include <dm.h> +#include <dm/of_access.h> +#include <dm/of_addr.h> +#include <fdt_support.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <miiphy.h> +#include <net.h> +#include <wait_bit.h> + +#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) + +#define ANA_AC_RAM_CTRL_RAM_INIT 0x94358 +#define ANA_AC_STAT_GLOBAL_CFG_PORT_RESET 0x94370 + +#define ANA_CL_PORT_VLAN_CFG(x) (0x24018 + 0xc8 * (x)) +#define ANA_CL_PORT_VLAN_CFG_AWARE_ENA BIT(19) +#define ANA_CL_PORT_VLAN_CFG_POP_CNT(x) ((x) << 17) + +#define ANA_L2_COMMON_FWD_CFG 0x8a2a8 +#define ANA_L2_COMMON_FWD_CFG_CPU_DMAC_COPY_ENA BIT(6) + +#define ASM_CFG_STAT_CFG 0x3508 +#define ASM_CFG_PORT(x) (0x36c4 + 0x4 * (x)) +#define ASM_CFG_PORT_NO_PREAMBLE_ENA BIT(8) +#define ASM_CFG_PORT_INJ_FORMAT_CFG(x) ((x) << 1) +#define ASM_RAM_CTRL_RAM_INIT 0x39b8 + +#define DEV_DEV_CFG_DEV_RST_CTRL 0x0 +#define DEV_DEV_CFG_DEV_RST_CTRL_SPEED_SEL(x) ((x) << 20) +#define DEV_MAC_CFG_MAC_ENA 0x1c +#define DEV_MAC_CFG_MAC_ENA_RX_ENA BIT(4) +#define DEV_MAC_CFG_MAC_ENA_TX_ENA BIT(0) +#define DEV_MAC_CFG_MAC_IFG 0x34 +#define DEV_MAC_CFG_MAC_IFG_TX_IFG(x) ((x) << 8) +#define DEV_MAC_CFG_MAC_IFG_RX_IFG2(x) ((x) << 4) +#define DEV_MAC_CFG_MAC_IFG_RX_IFG1(x) (x) +#define DEV_PCS1G_CFG_PCS1G_CFG 0x40 +#define DEV_PCS1G_CFG_PCS1G_CFG_PCS_ENA BIT(0) +#define DEV_PCS1G_CFG_PCS1G_MODE 0x44 +#define DEV_PCS1G_CFG_PCS1G_SD 0x48 +#define DEV_PCS1G_CFG_PCS1G_ANEG 0x4c +#define DEV_PCS1G_CFG_PCS1G_ANEG_ADV_ABILITY(x) ((x) << 16) + +#define DSM_RAM_CTRL_RAM_INIT 0x8 + +#define HSIO_ANA_SERDES1G_DES_CFG 0xac +#define HSIO_ANA_SERDES1G_DES_CFG_BW_HYST(x) ((x) << 1) +#define HSIO_ANA_SERDES1G_DES_CFG_BW_ANA(x) ((x) << 5) +#define HSIO_ANA_SERDES1G_DES_CFG_MBTR_CTRL(x) ((x) << 8) +#define HSIO_ANA_SERDES1G_DES_CFG_PHS_CTRL(x) ((x) << 13) +#define HSIO_ANA_SERDES1G_IB_CFG 0xb0 +#define HSIO_ANA_SERDES1G_IB_CFG_RESISTOR_CTRL(x) (x) +#define HSIO_ANA_SERDES1G_IB_CFG_EQ_GAIN(x) ((x) << 6) +#define HSIO_ANA_SERDES1G_IB_CFG_ENA_OFFSET_COMP BIT(9) +#define HSIO_ANA_SERDES1G_IB_CFG_ENA_DETLEV BIT(11) +#define HSIO_ANA_SERDES1G_IB_CFG_ENA_CMV_TERM BIT(13) +#define HSIO_ANA_SERDES1G_IB_CFG_DET_LEV(x) ((x) << 19) +#define HSIO_ANA_SERDES1G_IB_CFG_ACJTAG_HYST(x) ((x) << 24) +#define HSIO_ANA_SERDES1G_OB_CFG 0xb4 +#define HSIO_ANA_SERDES1G_OB_CFG_RESISTOR_CTRL(x) (x) +#define HSIO_ANA_SERDES1G_OB_CFG_VCM_CTRL(x) ((x) << 4) +#define HSIO_ANA_SERDES1G_OB_CFG_CMM_BIAS_CTRL(x) ((x) << 10) +#define HSIO_ANA_SERDES1G_OB_CFG_AMP_CTRL(x) ((x) << 13) +#define HSIO_ANA_SERDES1G_OB_CFG_SLP(x) ((x) << 17) +#define HSIO_ANA_SERDES1G_SER_CFG 0xb8 +#define HSIO_ANA_SERDES1G_COMMON_CFG 0xbc +#define HSIO_ANA_SERDES1G_COMMON_CFG_IF_MODE BIT(0) +#define HSIO_ANA_SERDES1G_COMMON_CFG_ENA_LANE BIT(18) +#define HSIO_ANA_SERDES1G_COMMON_CFG_SYS_RST BIT(31) +#define HSIO_ANA_SERDES1G_PLL_CFG 0xc0 +#define HSIO_ANA_SERDES1G_PLL_CFG_FSM_ENA BIT(7) +#define HSIO_ANA_SERDES1G_PLL_CFG_FSM_CTRL_DATA(x) ((x) << 8) +#define HSIO_ANA_SERDES1G_PLL_CFG_ENA_RC_DIV2 BIT(21) +#define HSIO_DIG_SERDES1G_DFT_CFG0 0xc8 +#define HSIO_DIG_SERDES1G_TP_CFG 0xd4 +#define HSIO_DIG_SERDES1G_MISC_CFG 0xdc +#define HSIO_DIG_SERDES1G_MISC_CFG_LANE_RST BIT(0) +#define HSIO_MCB_SERDES1G_CFG 0xe8 +#define HSIO_MCB_SERDES1G_CFG_WR_ONE_SHOT BIT(31) +#define HSIO_MCB_SERDES1G_CFG_ADDR(x) (x) + +#define HSIO_ANA_SERDES6G_DES_CFG 0x11c +#define HSIO_ANA_SERDES6G_DES_CFG_SWAP_ANA BIT(0) +#define HSIO_ANA_SERDES6G_DES_CFG_BW_ANA(x) ((x) << 1) +#define HSIO_ANA_SERDES6G_DES_CFG_SWAP_HYST BIT(4) +#define HSIO_ANA_SERDES6G_DES_CFG_BW_HYST(x) ((x) << 5) +#define HSIO_ANA_SERDES6G_DES_CFG_CPMD_SEL(x) ((x) << 8) +#define HSIO_ANA_SERDES6G_DES_CFG_MBTR_CTRL(x) ((x) << 10) +#define HSIO_ANA_SERDES6G_DES_CFG_PHS_CTRL(x) ((x) << 13) +#define HSIO_ANA_SERDES6G_IB_CFG 0x120 +#define HSIO_ANA_SERDES6G_IB_CFG_REG_ENA BIT(0) +#define HSIO_ANA_SERDES6G_IB_CFG_EQZ_ENA BIT(1) +#define HSIO_ANA_SERDES6G_IB_CFG_SAM_ENA BIT(2) +#define HSIO_ANA_SERDES6G_IB_CFG_CAL_ENA(x) ((x) << 3) +#define HSIO_ANA_SERDES6G_IB_CFG_CONCUR BIT(4) +#define HSIO_ANA_SERDES6G_IB_CFG_SIG_DET_ENA BIT(5) +#define HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_OFF(x) ((x) << 7) +#define HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_LP(x) ((x) << 9) +#define HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_MID(x) ((x) << 11) +#define HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_HP(x) ((x) << 13) +#define HSIO_ANA_SERDES6G_IB_CFG_SIG_DET_CLK_SEL(x) ((x) << 15) +#define HSIO_ANA_SERDES6G_IB_CFG_TERM_MODE_SEL(x) ((x) << 18) +#define HSIO_ANA_SERDES6G_IB_CFG_ICML_ADJ(x) ((x) << 20) +#define HSIO_ANA_SERDES6G_IB_CFG_RTRM_ADJ(x) ((x) << 24) +#define HSIO_ANA_SERDES6G_IB_CFG_VBULK_SEL BIT(28) +#define HSIO_ANA_SERDES6G_IB_CFG_SOFSI(x) ((x) << 29) +#define HSIO_ANA_SERDES6G_IB_CFG1 0x124 +#define HSIO_ANA_SERDES6G_IB_CFG1_FILT_OFFSET BIT(4) +#define HSIO_ANA_SERDES6G_IB_CFG1_FILT_LP BIT(5) +#define HSIO_ANA_SERDES6G_IB_CFG1_FILT_MID BIT(6) +#define HSIO_ANA_SERDES6G_IB_CFG1_FILT_HP BIT(7) +#define HSIO_ANA_SERDES6G_IB_CFG1_SCALY(x) ((x) << 8) +#define HSIO_ANA_SERDES6G_IB_CFG1_TSDET(x) ((x) << 12) +#define HSIO_ANA_SERDES6G_IB_CFG1_TJTAG(x) ((x) << 17) +#define HSIO_ANA_SERDES6G_IB_CFG2 0x128 +#define HSIO_ANA_SERDES6G_IB_CFG2_UREG(x) (x) +#define HSIO_ANA_SERDES6G_IB_CFG2_UMAX(x) ((x) << 3) +#define HSIO_ANA_SERDES6G_IB_CFG2_TCALV(x) ((x) << 5) +#define HSIO_ANA_SERDES6G_IB_CFG2_OCALS(x) ((x) << 10) +#define HSIO_ANA_SERDES6G_IB_CFG2_OINFS(x) ((x) << 16) +#define HSIO_ANA_SERDES6G_IB_CFG2_OINFI(x) ((x) << 22) +#define HSIO_ANA_SERDES6G_IB_CFG2_TINFV(x) ((x) << 27) +#define HSIO_ANA_SERDES6G_IB_CFG3 0x12c +#define HSIO_ANA_SERDES6G_IB_CFG3_INI_OFFSET(x) (x) +#define HSIO_ANA_SERDES6G_IB_CFG3_INI_LP(x) ((x) << 6) +#define HSIO_ANA_SERDES6G_IB_CFG3_INI_MID(x) ((x) << 12) +#define HSIO_ANA_SERDES6G_IB_CFG3_INI_HP(x) ((x) << 18) +#define HSIO_ANA_SERDES6G_IB_CFG4 0x130 +#define HSIO_ANA_SERDES6G_IB_CFG4_MAX_OFFSET(x) (x) +#define HSIO_ANA_SERDES6G_IB_CFG4_MAX_LP(x) ((x) << 6) +#define HSIO_ANA_SERDES6G_IB_CFG4_MAX_MID(x) ((x) << 12) +#define HSIO_ANA_SERDES6G_IB_CFG4_MAX_HP(x) ((x) << 18) +#define HSIO_ANA_SERDES6G_IB_CFG5 0x134 +#define HSIO_ANA_SERDES6G_IB_CFG4_MIN_OFFSET(x) (x) +#define HSIO_ANA_SERDES6G_IB_CFG4_MIN_LP(x) ((x) << 6) +#define HSIO_ANA_SERDES6G_IB_CFG4_MIN_MID(x) ((x) << 12) +#define HSIO_ANA_SERDES6G_IB_CFG4_MIN_HP(x) ((x) << 18) +#define HSIO_ANA_SERDES6G_OB_CFG 0x138 +#define HSIO_ANA_SERDES6G_OB_CFG_RESISTOR_CTRL(x) (x) +#define HSIO_ANA_SERDES6G_OB_CFG_SR(x) ((x) << 4) +#define HSIO_ANA_SERDES6G_OB_CFG_SR_H BIT(8) +#define HSIO_ANA_SERDES6G_OB_CFG_SEL_RCTRL BIT(9) +#define HSIO_ANA_SERDES6G_OB_CFG_R_COR BIT(10) +#define HSIO_ANA_SERDES6G_OB_CFG_POST1(x) ((x) << 11) +#define HSIO_ANA_SERDES6G_OB_CFG_R_ADJ_PDR BIT(16) +#define HSIO_ANA_SERDES6G_OB_CFG_R_ADJ_MUX BIT(17) +#define HSIO_ANA_SERDES6G_OB_CFG_PREC(x) ((x) << 18) +#define HSIO_ANA_SERDES6G_OB_CFG_POST0(x) ((x) << 23) +#define HSIO_ANA_SERDES6G_OB_CFG_POL BIT(29) +#define HSIO_ANA_SERDES6G_OB_CFG_ENA1V_MODE(x) ((x) << 30) +#define HSIO_ANA_SERDES6G_OB_CFG_IDLE BIT(31) +#define HSIO_ANA_SERDES6G_OB_CFG1 0x13c +#define HSIO_ANA_SERDES6G_OB_CFG1_LEV(x) (x) +#define HSIO_ANA_SERDES6G_OB_CFG1_ENA_CAS(x) ((x) << 6) +#define HSIO_ANA_SERDES6G_SER_CFG 0x140 +#define HSIO_ANA_SERDES6G_COMMON_CFG 0x144 +#define HSIO_ANA_SERDES6G_COMMON_CFG_IF_MODE(x) (x) +#define HSIO_ANA_SERDES6G_COMMON_CFG_QRATE(x) (x << 2) +#define HSIO_ANA_SERDES6G_COMMON_CFG_ENA_LANE BIT(14) +#define HSIO_ANA_SERDES6G_COMMON_CFG_SYS_RST BIT(16) +#define HSIO_ANA_SERDES6G_PLL_CFG 0x148 +#define HSIO_ANA_SERDES6G_PLL_CFG_ROT_FRQ BIT(0) +#define HSIO_ANA_SERDES6G_PLL_CFG_ROT_DIR BIT(1) +#define HSIO_ANA_SERDES6G_PLL_CFG_RB_DATA_SEL BIT(2) +#define HSIO_ANA_SERDES6G_PLL_CFG_FSM_OOR_RECAL_ENA BIT(3) +#define HSIO_ANA_SERDES6G_PLL_CFG_FSM_FORCE_SET_ENA BIT(4) +#define HSIO_ANA_SERDES6G_PLL_CFG_FSM_ENA BIT(5) +#define HSIO_ANA_SERDES6G_PLL_CFG_FSM_CTRL_DATA(x) ((x) << 6) +#define HSIO_ANA_SERDES6G_PLL_CFG_ENA_ROT BIT(14) +#define HSIO_ANA_SERDES6G_PLL_CFG_DIV4 BIT(15) +#define HSIO_ANA_SERDES6G_PLL_CFG_ENA_OFFS(x) ((x) << 16) +#define HSIO_DIG_SERDES6G_MISC_CFG 0x108 +#define HSIO_DIG_SERDES6G_MISC_CFG_LANE_RST BIT(0) +#define HSIO_MCB_SERDES6G_CFG 0x168 +#define HSIO_MCB_SERDES6G_CFG_WR_ONE_SHOT BIT(31) +#define HSIO_MCB_SERDES6G_CFG_ADDR(x) (x) +#define HSIO_HW_CFGSTAT_HW_CFG 0x16c + +#define LRN_COMMON_ACCESS_CTRL 0x0 +#define LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT BIT(0) +#define LRN_COMMON_MAC_ACCESS_CFG0 0x4 +#define LRN_COMMON_MAC_ACCESS_CFG1 0x8 +#define LRN_COMMON_MAC_ACCESS_CFG2 0xc +#define LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_ADDR(x) (x) +#define LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_TYPE(x) ((x) << 12) +#define LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_VLD BIT(15) +#define LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_LOCKED BIT(16) +#define LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_CPU_COPY BIT(23) +#define LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_CPU_QU(x) ((x) << 24) + +#define QFWD_SYSTEM_SWITCH_PORT_MODE(x) (0x4 * (x)) +#define QFWD_SYSTEM_SWITCH_PORT_MODE_PORT_ENA BIT(17) + +#define QS_XTR_GRP_CFG(x) (0x0 + 4 * (x)) +#define QS_INJ_GRP_CFG(x) (0x24 + (x) * 4) + +#define QSYS_SYSTEM_RESET_CFG 0xf0 +#define QSYS_CALCFG_CAL_AUTO(x) (0x3d4 + 4 * (x)) +#define QSYS_CALCFG_CAL_CTRL 0x3e8 +#define QSYS_CALCFG_CAL_CTRL_CAL_MODE(x) ((x) << 11) +#define QSYS_RAM_CTRL_RAM_INIT 0x3ec + +#define REW_RAM_CTRL_RAM_INIT 0x53528 + +#define VOP_RAM_CTRL_RAM_INIT 0x43638 + +#define XTR_VALID_BYTES(x) (4 - ((x) & 3)) +#define MAC_VID 0 +#define CPU_PORT 53 +#define IFH_LEN 7 +#define JR2_BUF_CELL_SZ 60 +#define ETH_ALEN 6 +#define PGID_BROADCAST 510 +#define PGID_UNICAST 511 + +static const char * const regs_names[] = { + "port0", "port1", "port2", "port3", "port4", "port5", "port6", "port7", + "port8", "port9", "port10", "port11", "port12", "port13", "port14", + "port15", "port16", "port17", "port18", "port19", "port20", "port21", + "port22", "port23", "port24", "port25", "port26", "port27", "port28", + "port29", "port30", "port31", "port32", "port33", "port34", "port35", + "port36", "port37", "port38", "port39", "port40", "port41", "port42", + "port43", "port44", "port45", "port46", "port47", + "ana_ac", "ana_cl", "ana_l2", "asm", "hsio", "lrn", + "qfwd", "qs", "qsys", "rew", +}; + +#define REGS_NAMES_COUNT ARRAY_SIZE(regs_names) + 1 +#define MAX_PORT 48 + +enum jr2_ctrl_regs { + ANA_AC = MAX_PORT, + ANA_CL, + ANA_L2, + ASM, + HSIO, + LRN, + QFWD, + QS, + QSYS, + REW, +}; + +#define JR2_MIIM_BUS_COUNT 3 + +struct jr2_phy_port_t { + size_t phy_addr; + struct mii_dev *bus; + u8 serdes_index; + u8 phy_mode; +}; + +struct jr2_private { + void __iomem *regs[REGS_NAMES_COUNT]; + struct mii_dev *bus[JR2_MIIM_BUS_COUNT]; + 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, + [MSCC_QS_XTR_DATA_PRESENT] = 0x1c, + [MSCC_QS_INJ_WR] = 0x2c, + [MSCC_QS_INJ_CTRL] = 0x34, +}; + +static struct jr2_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 */ + writel(ASM_CFG_PORT_NO_PREAMBLE_ENA | + ASM_CFG_PORT_INJ_FORMAT_CFG(1), + priv->regs[ASM] + ASM_CFG_PORT(CPU_PORT)); + + /* Set Manual injection via DEVCPU_QS registers for CPU queue 0 */ + writel(0x5, priv->regs[QS] + QS_INJ_GRP_CFG(0)); + + /* Set Manual extraction via DEVCPU_QS registers for CPU queue 0 */ + writel(0x7, priv->regs[QS] + QS_XTR_GRP_CFG(0)); + + /* Enable CPU port for any frame transfer */ + setbits_le32(priv->regs[QFWD] + QFWD_SYSTEM_SWITCH_PORT_MODE(CPU_PORT), + QFWD_SYSTEM_SWITCH_PORT_MODE_PORT_ENA); + + /* Send a copy to CPU when found as forwarding entry */ + setbits_le32(priv->regs[ANA_L2] + ANA_L2_COMMON_FWD_CFG, + ANA_L2_COMMON_FWD_CFG_CPU_DMAC_COPY_ENA); +} + +static void jr2_port_init(struct jr2_private *priv, int port) +{ + void __iomem *regs = priv->regs[port]; + + /* Enable PCS */ + writel(DEV_PCS1G_CFG_PCS1G_CFG_PCS_ENA, + regs + DEV_PCS1G_CFG_PCS1G_CFG); + + /* Disable Signal Detect */ + writel(0, regs + DEV_PCS1G_CFG_PCS1G_SD); + + /* Enable MAC RX and TX */ + writel(DEV_MAC_CFG_MAC_ENA_RX_ENA | + DEV_MAC_CFG_MAC_ENA_TX_ENA, + regs + DEV_MAC_CFG_MAC_ENA); + + /* Clear sgmii_mode_ena */ + writel(0, regs + DEV_PCS1G_CFG_PCS1G_MODE); + + /* + * Clear sw_resolve_ena(bit 0) and set adv_ability to + * something meaningful just in case + */ + writel(DEV_PCS1G_CFG_PCS1G_ANEG_ADV_ABILITY(0x20), + regs + DEV_PCS1G_CFG_PCS1G_ANEG); + + /* Set MAC IFG Gaps */ + writel(DEV_MAC_CFG_MAC_IFG_TX_IFG(4) | + DEV_MAC_CFG_MAC_IFG_RX_IFG1(5) | + DEV_MAC_CFG_MAC_IFG_RX_IFG2(1), + regs + DEV_MAC_CFG_MAC_IFG); + + /* Set link speed and release all resets */ + writel(DEV_DEV_CFG_DEV_RST_CTRL_SPEED_SEL(2), + regs + DEV_DEV_CFG_DEV_RST_CTRL); + + /* Make VLAN aware for CPU traffic */ + writel(ANA_CL_PORT_VLAN_CFG_AWARE_ENA | + ANA_CL_PORT_VLAN_CFG_POP_CNT(1) | + MAC_VID, + priv->regs[ANA_CL] + ANA_CL_PORT_VLAN_CFG(port)); + + /* Enable CPU port for any frame transfer */ + setbits_le32(priv->regs[QFWD] + QFWD_SYSTEM_SWITCH_PORT_MODE(port), + QFWD_SYSTEM_SWITCH_PORT_MODE_PORT_ENA); +} + +static void serdes6g_write(void __iomem *base, u32 addr) +{ + u32 data; + + writel(HSIO_MCB_SERDES6G_CFG_WR_ONE_SHOT | + HSIO_MCB_SERDES6G_CFG_ADDR(addr), + base + HSIO_MCB_SERDES6G_CFG); + + do { + data = readl(base + HSIO_MCB_SERDES6G_CFG); + } while (data & HSIO_MCB_SERDES6G_CFG_WR_ONE_SHOT); +} + +static void serdes6g_setup(void __iomem *base, uint32_t addr, + phy_interface_t interface) +{ + u32 ib_if_mode = 0; + u32 ib_qrate = 0; + u32 ib_cal_ena = 0; + u32 ib1_tsdet = 0; + u32 ob_lev = 0; + u32 ob_ena_cas = 0; + u32 ob_ena1v_mode = 0; + u32 des_bw_ana = 0; + u32 pll_fsm_ctrl_data = 0; + + switch (interface) { + case PHY_INTERFACE_MODE_SGMII: + ib_if_mode = 1; + ib_qrate = 1; + ib_cal_ena = 1; + ib1_tsdet = 3; + ob_lev = 48; + ob_ena_cas = 2; + ob_ena1v_mode = 1; + des_bw_ana = 3; + pll_fsm_ctrl_data = 60; + break; + case PHY_INTERFACE_MODE_QSGMII: + ib_if_mode = 3; + ib1_tsdet = 16; + ob_lev = 24; + des_bw_ana = 5; + pll_fsm_ctrl_data = 120; + break; + default: + pr_err("Interface not supported\n"); + return; + } + + if (interface == PHY_INTERFACE_MODE_QSGMII) + writel(0xfff, base + HSIO_HW_CFGSTAT_HW_CFG); + + writel(HSIO_ANA_SERDES6G_COMMON_CFG_IF_MODE(3), + base + HSIO_ANA_SERDES6G_COMMON_CFG); + writel(HSIO_ANA_SERDES6G_PLL_CFG_FSM_CTRL_DATA(120) | + HSIO_ANA_SERDES6G_PLL_CFG_ENA_OFFS(3), + base + HSIO_ANA_SERDES6G_PLL_CFG); + writel(HSIO_ANA_SERDES6G_IB_CFG_REG_ENA | + HSIO_ANA_SERDES6G_IB_CFG_EQZ_ENA | + HSIO_ANA_SERDES6G_IB_CFG_SAM_ENA | + HSIO_ANA_SERDES6G_IB_CFG_CONCUR | + HSIO_ANA_SERDES6G_IB_CFG_SIG_DET_ENA | + HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_OFF(0) | + HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_LP(2) | + HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_MID(1) | + HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_HP(1) | + HSIO_ANA_SERDES6G_IB_CFG_SIG_DET_CLK_SEL(7) | + HSIO_ANA_SERDES6G_IB_CFG_TERM_MODE_SEL(1) | + HSIO_ANA_SERDES6G_IB_CFG_ICML_ADJ(5) | + HSIO_ANA_SERDES6G_IB_CFG_RTRM_ADJ(13) | + HSIO_ANA_SERDES6G_IB_CFG_VBULK_SEL | + HSIO_ANA_SERDES6G_IB_CFG_SOFSI(1), + base + HSIO_ANA_SERDES6G_IB_CFG); + writel(HSIO_ANA_SERDES6G_IB_CFG1_FILT_OFFSET | + HSIO_ANA_SERDES6G_IB_CFG1_FILT_LP | + HSIO_ANA_SERDES6G_IB_CFG1_FILT_MID | + HSIO_ANA_SERDES6G_IB_CFG1_FILT_HP | + HSIO_ANA_SERDES6G_IB_CFG1_SCALY(15) | + HSIO_ANA_SERDES6G_IB_CFG1_TSDET(3) | + HSIO_ANA_SERDES6G_IB_CFG1_TJTAG(8), + base + HSIO_ANA_SERDES6G_IB_CFG1); + writel(HSIO_DIG_SERDES6G_MISC_CFG_LANE_RST, + base + HSIO_DIG_SERDES6G_MISC_CFG); + + serdes6g_write(base, addr); + + writel(HSIO_ANA_SERDES6G_IB_CFG_REG_ENA | + HSIO_ANA_SERDES6G_IB_CFG_EQZ_ENA | + HSIO_ANA_SERDES6G_IB_CFG_SAM_ENA | + HSIO_ANA_SERDES6G_IB_CFG_CONCUR | + HSIO_ANA_SERDES6G_IB_CFG_SIG_DET_ENA | + HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_OFF(0) | + HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_LP(2) | + HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_MID(1) | + HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_HP(1) | + HSIO_ANA_SERDES6G_IB_CFG_SIG_DET_CLK_SEL(0) | + HSIO_ANA_SERDES6G_IB_CFG_TERM_MODE_SEL(1) | + HSIO_ANA_SERDES6G_IB_CFG_ICML_ADJ(5) | + HSIO_ANA_SERDES6G_IB_CFG_RTRM_ADJ(13) | + HSIO_ANA_SERDES6G_IB_CFG_VBULK_SEL | + HSIO_ANA_SERDES6G_IB_CFG_SOFSI(1), + base + HSIO_ANA_SERDES6G_IB_CFG); + writel(HSIO_ANA_SERDES6G_IB_CFG1_FILT_OFFSET | + HSIO_ANA_SERDES6G_IB_CFG1_FILT_LP | + HSIO_ANA_SERDES6G_IB_CFG1_FILT_MID | + HSIO_ANA_SERDES6G_IB_CFG1_FILT_HP | + HSIO_ANA_SERDES6G_IB_CFG1_SCALY(15) | + HSIO_ANA_SERDES6G_IB_CFG1_TSDET(16) | + HSIO_ANA_SERDES6G_IB_CFG1_TJTAG(8), + base + HSIO_ANA_SERDES6G_IB_CFG1); + + writel(0x0, base + HSIO_ANA_SERDES6G_SER_CFG); + writel(HSIO_ANA_SERDES6G_COMMON_CFG_IF_MODE(ib_if_mode) | + HSIO_ANA_SERDES6G_COMMON_CFG_QRATE(ib_qrate) | + HSIO_ANA_SERDES6G_COMMON_CFG_ENA_LANE | + HSIO_ANA_SERDES6G_COMMON_CFG_SYS_RST, + base + HSIO_ANA_SERDES6G_COMMON_CFG); + writel(HSIO_DIG_SERDES6G_MISC_CFG_LANE_RST, + base + HSIO_DIG_SERDES6G_MISC_CFG); + + writel(HSIO_ANA_SERDES6G_OB_CFG_RESISTOR_CTRL(1) | + HSIO_ANA_SERDES6G_OB_CFG_SR(7) | + HSIO_ANA_SERDES6G_OB_CFG_SR_H | + HSIO_ANA_SERDES6G_OB_CFG_ENA1V_MODE(ob_ena1v_mode) | + HSIO_ANA_SERDES6G_OB_CFG_POL, base + HSIO_ANA_SERDES6G_OB_CFG); + writel(HSIO_ANA_SERDES6G_OB_CFG1_LEV(ob_lev) | + HSIO_ANA_SERDES6G_OB_CFG1_ENA_CAS(ob_ena_cas), + base + HSIO_ANA_SERDES6G_OB_CFG1); + + writel(HSIO_ANA_SERDES6G_DES_CFG_BW_ANA(des_bw_ana) | + HSIO_ANA_SERDES6G_DES_CFG_BW_HYST(5) | + HSIO_ANA_SERDES6G_DES_CFG_MBTR_CTRL(2) | + HSIO_ANA_SERDES6G_DES_CFG_PHS_CTRL(6), + base + HSIO_ANA_SERDES6G_DES_CFG); + writel(HSIO_ANA_SERDES6G_PLL_CFG_FSM_CTRL_DATA(pll_fsm_ctrl_data) | + HSIO_ANA_SERDES6G_PLL_CFG_ENA_OFFS(3), + base + HSIO_ANA_SERDES6G_PLL_CFG); + + serdes6g_write(base, addr); + + /* set pll_fsm_ena = 1 */ + writel(HSIO_ANA_SERDES6G_PLL_CFG_FSM_ENA | + HSIO_ANA_SERDES6G_PLL_CFG_FSM_CTRL_DATA(pll_fsm_ctrl_data) | + HSIO_ANA_SERDES6G_PLL_CFG_ENA_OFFS(3), + base + HSIO_ANA_SERDES6G_PLL_CFG); + + serdes6g_write(base, addr); + + /* wait 20ms for pll bringup */ + mdelay(20); + + /* start IB calibration by setting ib_cal_ena and clearing lane_rst */ + writel(HSIO_ANA_SERDES6G_IB_CFG_REG_ENA | + HSIO_ANA_SERDES6G_IB_CFG_EQZ_ENA | + HSIO_ANA_SERDES6G_IB_CFG_SAM_ENA | + HSIO_ANA_SERDES6G_IB_CFG_CAL_ENA(ib_cal_ena) | + HSIO_ANA_SERDES6G_IB_CFG_CONCUR | + HSIO_ANA_SERDES6G_IB_CFG_SIG_DET_ENA | + HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_OFF(0) | + HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_LP(2) | + HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_MID(1) | + HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_HP(1) | + HSIO_ANA_SERDES6G_IB_CFG_SIG_DET_CLK_SEL(0) | + HSIO_ANA_SERDES6G_IB_CFG_TERM_MODE_SEL(1) | + HSIO_ANA_SERDES6G_IB_CFG_ICML_ADJ(5) | + HSIO_ANA_SERDES6G_IB_CFG_RTRM_ADJ(13) | + HSIO_ANA_SERDES6G_IB_CFG_VBULK_SEL | + HSIO_ANA_SERDES6G_IB_CFG_SOFSI(1), + base + HSIO_ANA_SERDES6G_IB_CFG); + writel(0x0, base + HSIO_DIG_SERDES6G_MISC_CFG); + + serdes6g_write(base, addr); + + /* wait 60 for calibration */ + mdelay(60); + + /* set ib_tsdet and ib_reg_pat_sel_offset back to correct values */ + writel(HSIO_ANA_SERDES6G_IB_CFG_REG_ENA | + HSIO_ANA_SERDES6G_IB_CFG_EQZ_ENA | + HSIO_ANA_SERDES6G_IB_CFG_SAM_ENA | + HSIO_ANA_SERDES6G_IB_CFG_CAL_ENA(ib_cal_ena) | + HSIO_ANA_SERDES6G_IB_CFG_CONCUR | + HSIO_ANA_SERDES6G_IB_CFG_SIG_DET_ENA | + HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_OFF(0) | + HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_LP(2) | + HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_MID(1) | + HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_HP(1) | + HSIO_ANA_SERDES6G_IB_CFG_SIG_DET_CLK_SEL(7) | + HSIO_ANA_SERDES6G_IB_CFG_TERM_MODE_SEL(1) | + HSIO_ANA_SERDES6G_IB_CFG_ICML_ADJ(5) | + HSIO_ANA_SERDES6G_IB_CFG_RTRM_ADJ(13) | + HSIO_ANA_SERDES6G_IB_CFG_VBULK_SEL | + HSIO_ANA_SERDES6G_IB_CFG_SOFSI(1), + base + HSIO_ANA_SERDES6G_IB_CFG); + writel(HSIO_ANA_SERDES6G_IB_CFG1_FILT_OFFSET | + HSIO_ANA_SERDES6G_IB_CFG1_FILT_LP | + HSIO_ANA_SERDES6G_IB_CFG1_FILT_MID | + HSIO_ANA_SERDES6G_IB_CFG1_FILT_HP | + HSIO_ANA_SERDES6G_IB_CFG1_SCALY(15) | + HSIO_ANA_SERDES6G_IB_CFG1_TSDET(ib1_tsdet) | + HSIO_ANA_SERDES6G_IB_CFG1_TJTAG(8), + base + HSIO_ANA_SERDES6G_IB_CFG1); + + serdes6g_write(base, addr); +} + +static void serdes1g_write(void __iomem *base, u32 addr) +{ + u32 data; + + writel(HSIO_MCB_SERDES1G_CFG_WR_ONE_SHOT | + HSIO_MCB_SERDES1G_CFG_ADDR(addr), + base + HSIO_MCB_SERDES1G_CFG); + + do { + data = readl(base + HSIO_MCB_SERDES1G_CFG); + } while (data & HSIO_MCB_SERDES1G_CFG_WR_ONE_SHOT); +} + +static void serdes1g_setup(void __iomem *base, uint32_t addr, + phy_interface_t interface) +{ + writel(0x0, base + HSIO_ANA_SERDES1G_SER_CFG); + writel(0x0, base + HSIO_DIG_SERDES1G_TP_CFG); + writel(0x0, base + HSIO_DIG_SERDES1G_DFT_CFG0); + writel(HSIO_ANA_SERDES1G_OB_CFG_RESISTOR_CTRL(1) | + HSIO_ANA_SERDES1G_OB_CFG_VCM_CTRL(4) | + HSIO_ANA_SERDES1G_OB_CFG_CMM_BIAS_CTRL(2) | + HSIO_ANA_SERDES1G_OB_CFG_AMP_CTRL(12) | + HSIO_ANA_SERDES1G_OB_CFG_SLP(3), + base + HSIO_ANA_SERDES1G_OB_CFG); + writel(HSIO_ANA_SERDES1G_IB_CFG_RESISTOR_CTRL(13) | + HSIO_ANA_SERDES1G_IB_CFG_EQ_GAIN(2) | + HSIO_ANA_SERDES1G_IB_CFG_ENA_OFFSET_COMP | + HSIO_ANA_SERDES1G_IB_CFG_ENA_DETLEV | + HSIO_ANA_SERDES1G_IB_CFG_ENA_CMV_TERM | + HSIO_ANA_SERDES1G_IB_CFG_DET_LEV(3) | + HSIO_ANA_SERDES1G_IB_CFG_ACJTAG_HYST(1), + base + HSIO_ANA_SERDES1G_IB_CFG); + writel(HSIO_ANA_SERDES1G_DES_CFG_BW_HYST(7) | + HSIO_ANA_SERDES1G_DES_CFG_BW_ANA(6) | + HSIO_ANA_SERDES1G_DES_CFG_MBTR_CTRL(2) | + HSIO_ANA_SERDES1G_DES_CFG_PHS_CTRL(6), + base + HSIO_ANA_SERDES1G_DES_CFG); + writel(HSIO_DIG_SERDES1G_MISC_CFG_LANE_RST, + base + HSIO_DIG_SERDES1G_MISC_CFG); + writel(HSIO_ANA_SERDES1G_PLL_CFG_FSM_ENA | + HSIO_ANA_SERDES1G_PLL_CFG_FSM_CTRL_DATA(0xc8) | + HSIO_ANA_SERDES1G_PLL_CFG_ENA_RC_DIV2, + base + HSIO_ANA_SERDES1G_PLL_CFG); + writel(HSIO_ANA_SERDES1G_COMMON_CFG_IF_MODE | + HSIO_ANA_SERDES1G_COMMON_CFG_ENA_LANE | + HSIO_ANA_SERDES1G_COMMON_CFG_SYS_RST, + base + HSIO_ANA_SERDES1G_COMMON_CFG); + + serdes1g_write(base, addr); + + setbits_le32(base + HSIO_ANA_SERDES1G_COMMON_CFG, + HSIO_ANA_SERDES1G_COMMON_CFG_SYS_RST); + + serdes1g_write(base, addr); + + clrbits_le32(base + HSIO_DIG_SERDES1G_MISC_CFG, + HSIO_DIG_SERDES1G_MISC_CFG_LANE_RST); + + serdes1g_write(base, addr); +} + +static int ram_init(u32 val, void __iomem *addr) +{ + writel(val, addr); + + if (wait_for_bit_le32(addr, BIT(1), false, 2000, false)) { + printf("Timeout in memory reset, reg = 0x%08x\n", val); + return 1; + } + + return 0; +} + +static int jr2_switch_init(struct jr2_private *priv) +{ + /* Initialize memories */ + ram_init(0x3, priv->regs[QSYS] + QSYS_RAM_CTRL_RAM_INIT); + ram_init(0x3, priv->regs[ASM] + ASM_RAM_CTRL_RAM_INIT); + ram_init(0x3, priv->regs[ANA_AC] + ANA_AC_RAM_CTRL_RAM_INIT); + ram_init(0x3, priv->regs[REW] + REW_RAM_CTRL_RAM_INIT); + + /* Reset counters */ + writel(0x1, priv->regs[ANA_AC] + ANA_AC_STAT_GLOBAL_CFG_PORT_RESET); + writel(0x1, priv->regs[ASM] + ASM_CFG_STAT_CFG); + + /* Enable switch-core and queue system */ + writel(0x1, priv->regs[QSYS] + QSYS_SYSTEM_RESET_CFG); + + return 0; +} + +static void jr2_switch_config(struct jr2_private *priv) +{ + writel(0x55555555, priv->regs[QSYS] + QSYS_CALCFG_CAL_AUTO(0)); + writel(0x55555555, priv->regs[QSYS] + QSYS_CALCFG_CAL_AUTO(1)); + writel(0x55555555, priv->regs[QSYS] + QSYS_CALCFG_CAL_AUTO(2)); + writel(0x55555555, priv->regs[QSYS] + QSYS_CALCFG_CAL_AUTO(3)); + + writel(readl(priv->regs[QSYS] + QSYS_CALCFG_CAL_CTRL) | + QSYS_CALCFG_CAL_CTRL_CAL_MODE(8), + priv->regs[QSYS] + QSYS_CALCFG_CAL_CTRL); +} + +static int jr2_initialize(struct jr2_private *priv) +{ + int ret, i; + + /* Initialize switch memories, enable core */ + ret = jr2_switch_init(priv); + if (ret) + return ret; + + jr2_switch_config(priv); + + for (i = 0; i < MAX_PORT; i++) + jr2_port_init(priv, i); + + jr2_cpu_capture_setup(priv); + + return 0; +} + +static inline int jr2_vlant_wait_for_completion(struct jr2_private *priv) +{ + if (wait_for_bit_le32(priv->regs[LRN] + LRN_COMMON_ACCESS_CTRL, + LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT, + false, 2000, false)) + return -ETIMEDOUT; + + return 0; +} + +static int jr2_mac_table_add(struct jr2_private *priv, + const unsigned char mac[ETH_ALEN], int pgid) +{ + u32 macl = 0, mach = 0; + + /* + * Set the MAC address to handle and the vlan associated in a format + * understood by the hardware. + */ + mach |= MAC_VID << 16; + mach |= ((u32)mac[0]) << 8; + mach |= ((u32)mac[1]) << 0; + macl |= ((u32)mac[2]) << 24; + macl |= ((u32)mac[3]) << 16; + macl |= ((u32)mac[4]) << 8; + macl |= ((u32)mac[5]) << 0; + + writel(mach, priv->regs[LRN] + LRN_COMMON_MAC_ACCESS_CFG0); + writel(macl, priv->regs[LRN] + LRN_COMMON_MAC_ACCESS_CFG1); + + writel(LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_ADDR(pgid) | + LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_TYPE(0x3) | + LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_CPU_COPY | + LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_CPU_QU(0) | + LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_VLD | + LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_LOCKED, + priv->regs[LRN] + LRN_COMMON_MAC_ACCESS_CFG2); + + writel(LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT, + priv->regs[LRN] + LRN_COMMON_ACCESS_CTRL); + + return jr2_vlant_wait_for_completion(priv); +} + +static int jr2_write_hwaddr(struct udevice *dev) +{ + struct jr2_private *priv = dev_get_priv(dev); + struct eth_pdata *pdata = dev_get_platdata(dev); + + return jr2_mac_table_add(priv, pdata->enetaddr, PGID_UNICAST); +} + +static void serdes_setup(struct jr2_private *priv) +{ + size_t mask; + int i = 0; + + for (i = 0; i < MAX_PORT; ++i) { + if (!priv->ports[i].bus || priv->ports[i].serdes_index == 0xff) + continue; + + mask = BIT(priv->ports[i].serdes_index); + if (priv->ports[i].serdes_index < SERDES1G_MAX) { + serdes1g_setup(priv->regs[HSIO], mask, + priv->ports[i].phy_mode); + } else { + mask >>= SERDES6G(0); + serdes6g_setup(priv->regs[HSIO], mask, + priv->ports[i].phy_mode); + } + } +} + +static int jr2_start(struct udevice *dev) +{ + struct jr2_private *priv = dev_get_priv(dev); + struct eth_pdata *pdata = dev_get_platdata(dev); + const unsigned char mac[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, + 0xff }; + int ret; + + ret = jr2_initialize(priv); + if (ret) + return ret; + + /* Set MAC address tables entries for CPU redirection */ + ret = jr2_mac_table_add(priv, mac, PGID_BROADCAST); + if (ret) + return ret; + + ret = jr2_mac_table_add(priv, pdata->enetaddr, PGID_UNICAST); + if (ret) + return ret; + + serdes_setup(priv); + + return 0; +} + +static void jr2_stop(struct udevice *dev) +{ +} + +static int jr2_send(struct udevice *dev, void *packet, int length) +{ + struct jr2_private *priv = dev_get_priv(dev); + u32 ifh[IFH_LEN]; + u32 *buf = packet; + + memset(ifh, '\0', IFH_LEN); + + /* Set DST PORT_MASK */ + ifh[0] = htonl(0); + ifh[1] = htonl(0x1FFFFF); + ifh[2] = htonl(~0); + /* Set DST_MODE to INJECT and UPDATE_FCS */ + ifh[5] = htonl(0x4c0); + + return mscc_send(priv->regs[QS], jr2_regs_qs, + ifh, IFH_LEN, buf, length); +} + +static int jr2_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct jr2_private *priv = dev_get_priv(dev); + u32 *rxbuf = (u32 *)net_rx_packets[0]; + int byte_cnt = 0; + + byte_cnt = mscc_recv(priv->regs[QS], jr2_regs_qs, rxbuf, IFH_LEN, + false); + + *packetp = net_rx_packets[0]; + + return byte_cnt; +} + +static struct mii_dev *get_mdiobus(phys_addr_t base, unsigned long size) +{ + int i = 0; + + for (i = 0; i < JR2_MIIM_BUS_COUNT; ++i) + if (miim[i].miim_base == base && miim[i].miim_size == size) + return miim[i].bus; + + return NULL; +} + +static void add_port_entry(struct jr2_private *priv, size_t index, + size_t phy_addr, struct mii_dev *bus, + u8 serdes_index, u8 phy_mode) +{ + priv->ports[index].phy_addr = phy_addr; + priv->ports[index].bus = bus; + priv->ports[index].serdes_index = serdes_index; + priv->ports[index].phy_mode = phy_mode; +} + +static int jr2_probe(struct udevice *dev) +{ + struct jr2_private *priv = dev_get_priv(dev); + int i; + int ret; + struct resource res; + fdt32_t faddr; + phys_addr_t addr_base; + unsigned long addr_size; + ofnode eth_node, node, mdio_node; + size_t phy_addr; + struct mii_dev *bus; + struct ofnode_phandle_args phandle; + struct phy_device *phy; + + if (!priv) + return -EINVAL; + + /* Get registers and map them to the private structure */ + for (i = 0; i < ARRAY_SIZE(regs_names); i++) { + priv->regs[i] = dev_remap_addr_name(dev, regs_names[i]); + if (!priv->regs[i]) { + debug + ("Error can't get regs base addresses for %s\n", + regs_names[i]); + return -ENOMEM; + } + } + + /* Initialize miim buses */ + memset(&miim, 0x0, sizeof(struct jr2_miim_dev) * JR2_MIIM_BUS_COUNT); + + /* iterate all the ports and find out on which bus they are */ + i = 0; + eth_node = dev_read_first_subnode(dev); + for (node = ofnode_first_subnode(eth_node); + ofnode_valid(node); + node = ofnode_next_subnode(node)) { + if (ofnode_read_resource(node, 0, &res)) + return -ENOMEM; + i = res.start; + + ret = ofnode_parse_phandle_with_args(node, "phy-handle", NULL, + 0, 0, &phandle); + if (ret) + continue; + + /* Get phy address on mdio bus */ + if (ofnode_read_resource(phandle.node, 0, &res)) + return -ENOMEM; + phy_addr = res.start; + + /* Get mdio node */ + mdio_node = ofnode_get_parent(phandle.node); + + if (ofnode_read_resource(mdio_node, 0, &res)) + return -ENOMEM; + faddr = cpu_to_fdt32(res.start); + + addr_base = ofnode_translate_address(mdio_node, &faddr); + addr_size = res.end - res.start; + + /* 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); + + /* Connect mdio bus with the port */ + bus = get_mdiobus(addr_base, addr_size); + + /* Get serdes info */ + ret = ofnode_parse_phandle_with_args(node, "phys", NULL, + 3, 0, &phandle); + if (ret) + return -ENOMEM; + + add_port_entry(priv, i, phy_addr, bus, phandle.args[1], + phandle.args[2]); + } + + for (i = 0; i < MAX_PORT; i++) { + if (!priv->ports[i].bus) + continue; + + phy = phy_connect(priv->ports[i].bus, + priv->ports[i].phy_addr, dev, + PHY_INTERFACE_MODE_NONE); + if (phy) + board_phy_config(phy); + } + + return 0; +} + +static int jr2_remove(struct udevice *dev) +{ + struct jr2_private *priv = dev_get_priv(dev); + int i; + + for (i = 0; i < JR2_MIIM_BUS_COUNT; i++) { + mdio_unregister(priv->bus[i]); + mdio_free(priv->bus[i]); + } + + return 0; +} + +static const struct eth_ops jr2_ops = { + .start = jr2_start, + .stop = jr2_stop, + .send = jr2_send, + .recv = jr2_recv, + .write_hwaddr = jr2_write_hwaddr, +}; + +static const struct udevice_id mscc_jr2_ids[] = { + {.compatible = "mscc,vsc7454-switch" }, + { /* Sentinel */ } +}; + +U_BOOT_DRIVER(jr2) = { + .name = "jr2-switch", + .id = UCLASS_ETH, + .of_match = mscc_jr2_ids, + .probe = jr2_probe, + .remove = jr2_remove, + .ops = &jr2_ops, + .priv_auto_alloc_size = sizeof(struct jr2_private), + .platdata_auto_alloc_size = sizeof(struct eth_pdata), +}; diff --git a/drivers/net/mscc_eswitch/ocelot_switch.c b/drivers/net/mscc_eswitch/ocelot_switch.c index bf08c35ba0..815c2da264 100644 --- a/drivers/net/mscc_eswitch/ocelot_switch.c +++ b/drivers/net/mscc_eswitch/ocelot_switch.c @@ -142,18 +142,16 @@ static const unsigned long ocelot_regs_ana_table[] = { static struct mscc_miim_dev miim[NUM_PHY]; -static int mscc_miim_reset(struct mii_dev *bus) +static void mscc_phy_reset(void) { - struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv; - - if (miim->phy_regs) { - writel(0, miim->phy_regs + PHY_CFG); - writel(PHY_CFG_RST | PHY_CFG_COMMON_RST - | PHY_CFG_ENA, miim->phy_regs + PHY_CFG); - mdelay(500); + writel(0, miim[INTERNAL].phy_regs + PHY_CFG); + writel(PHY_CFG_RST | PHY_CFG_COMMON_RST + | PHY_CFG_ENA, miim[INTERNAL].phy_regs + PHY_CFG); + if (wait_for_bit_le32(miim[INTERNAL].phy_regs + PHY_STAT, + PHY_STAT_SUPERVISOR_COMPLETE, + true, 2000, false)) { + pr_err("Timeout in phy reset\n"); } - - return 0; } /* For now only setup the internal mdio bus */ @@ -194,7 +192,6 @@ static struct mii_dev *ocelot_mdiobus_init(struct udevice *dev) miim[INTERNAL].phy_regs = ioremap(phy_base[PHY], phy_size[PHY]); miim[INTERNAL].regs = ioremap(phy_base[MIIM], phy_size[MIIM]); bus->priv = &miim[INTERNAL]; - bus->reset = mscc_miim_reset; bus->read = mscc_miim_read; bus->write = mscc_miim_write; @@ -210,13 +207,8 @@ __weak void mscc_switch_reset(void) static void ocelot_stop(struct udevice *dev) { - struct ocelot_private *priv = dev_get_priv(dev); - int i; - mscc_switch_reset(); - for (i = 0; i < NUM_PHY; i++) - if (priv->bus[i]) - mscc_miim_reset(priv->bus[i]); + mscc_phy_reset(); } static void ocelot_cpu_capture_setup(struct ocelot_private *priv) @@ -473,6 +465,7 @@ static int ocelot_probe(struct udevice *dev) } priv->bus[INTERNAL] = ocelot_mdiobus_init(dev); + mscc_phy_reset(); for (i = 0; i < 4; i++) { phy_connect(priv->bus[INTERNAL], i, dev, diff --git a/drivers/net/mscc_eswitch/servalt_switch.c b/drivers/net/mscc_eswitch/servalt_switch.c new file mode 100644 index 0000000000..995c62309d --- /dev/null +++ b/drivers/net/mscc_eswitch/servalt_switch.c @@ -0,0 +1,622 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2019 Microsemi Corporation + */ + +#include <common.h> +#include <config.h> +#include <dm.h> +#include <dm/of_access.h> +#include <dm/of_addr.h> +#include <fdt_support.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <miiphy.h> +#include <net.h> +#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) + +#define PHY_CFG 0x0 +#define PHY_CFG_ENA 0x3 +#define PHY_CFG_COMMON_RST BIT(2) +#define PHY_CFG_RST (0x3 << 3) +#define PHY_STAT 0x4 +#define PHY_STAT_SUPERVISOR_COMPLETE BIT(0) + +#define ANA_AC_RAM_CTRL_RAM_INIT 0x14fdc +#define ANA_AC_STAT_GLOBAL_CFG_PORT_RESET 0x15474 + +#define ANA_CL_PORT_VLAN_CFG(x) (0xa018 + 0xc8 * (x)) +#define ANA_CL_PORT_VLAN_CFG_AWARE_ENA BIT(19) +#define ANA_CL_PORT_VLAN_CFG_POP_CNT(x) ((x) << 17) + +#define ANA_L2_COMMON_FWD_CFG 0x18498 +#define ANA_L2_COMMON_FWD_CFG_CPU_DMAC_COPY_ENA BIT(6) + +#define ASM_CFG_STAT_CFG 0xb08 +#define ASM_CFG_PORT(x) (0xb74 + 0x4 * (x)) +#define ASM_CFG_PORT_NO_PREAMBLE_ENA BIT(8) +#define ASM_CFG_PORT_INJ_FORMAT_CFG(x) ((x) << 1) +#define ASM_RAM_CTRL_RAM_INIT 0xbfc + +#define DEV_DEV_CFG_DEV_RST_CTRL 0x0 +#define DEV_DEV_CFG_DEV_RST_CTRL_SPEED_SEL(x) ((x) << 20) +#define DEV_MAC_CFG_MAC_ENA 0x24 +#define DEV_MAC_CFG_MAC_ENA_RX_ENA BIT(4) +#define DEV_MAC_CFG_MAC_ENA_TX_ENA BIT(0) +#define DEV_MAC_CFG_MAC_IFG 0x3c +#define DEV_MAC_CFG_MAC_IFG_TX_IFG(x) ((x) << 8) +#define DEV_MAC_CFG_MAC_IFG_RX_IFG2(x) ((x) << 4) +#define DEV_MAC_CFG_MAC_IFG_RX_IFG1(x) (x) +#define DEV_PCS1G_CFG_PCS1G_CFG 0x48 +#define DEV_PCS1G_CFG_PCS1G_CFG_PCS_ENA BIT(0) +#define DEV_PCS1G_CFG_PCS1G_MODE 0x4c +#define DEV_PCS1G_CFG_PCS1G_SD 0x50 +#define DEV_PCS1G_CFG_PCS1G_ANEG 0x54 +#define DEV_PCS1G_CFG_PCS1G_ANEG_ADV_ABILITY(x) ((x) << 16) + +#define LRN_COMMON_ACCESS_CTRL 0x0 +#define LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT BIT(0) +#define LRN_COMMON_MAC_ACCESS_CFG0 0x4 +#define LRN_COMMON_MAC_ACCESS_CFG1 0x8 +#define LRN_COMMON_MAC_ACCESS_CFG2 0xc +#define LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_ADDR(x) (x) +#define LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_TYPE(x) ((x) << 12) +#define LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_VLD BIT(15) +#define LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_LOCKED BIT(16) +#define LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_CPU_COPY BIT(23) +#define LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_CPU_QU(x) ((x) << 24) + +#define QFWD_SYSTEM_SWITCH_PORT_MODE(x) (0x4400 + 0x4 * (x)) +#define QFWD_SYSTEM_SWITCH_PORT_MODE_PORT_ENA BIT(17) + +#define QS_XTR_GRP_CFG(x) (4 * (x)) +#define QS_INJ_GRP_CFG(x) (0x24 + (x) * 4) + +#define QSYS_SYSTEM_RESET_CFG 0x1048 +#define QSYS_CALCFG_CAL_AUTO 0x1134 +#define QSYS_CALCFG_CAL_CTRL 0x113c +#define QSYS_CALCFG_CAL_CTRL_CAL_MODE(x) ((x) << 11) +#define QSYS_RAM_CTRL_RAM_INIT 0x1140 + +#define REW_RAM_CTRL_RAM_INIT 0xFFF4 + +#define MAC_VID 0 +#define CPU_PORT 11 +#define IFH_LEN 7 +#define ETH_ALEN 6 +#define PGID_BROADCAST 50 +#define PGID_UNICAST 51 + +static const char * const regs_names[] = { + "port0", "port1", + "ana_ac", "ana_cl", "ana_l2", "asm", "lrn", "qfwd", "qs", "qsys", "rew", +}; + +#define REGS_NAMES_COUNT ARRAY_SIZE(regs_names) + 1 +#define MAX_PORT 2 + +enum servalt_ctrl_regs { + ANA_AC = MAX_PORT, + ANA_CL, + ANA_L2, + ASM, + LRN, + QFWD, + QS, + QSYS, + REW, +}; + +#define SERVALT_MIIM_BUS_COUNT 2 + +struct servalt_phy_port_t { + size_t phy_addr; + struct mii_dev *bus; +}; + +struct servalt_private { + void __iomem *regs[REGS_NAMES_COUNT]; + struct mii_dev *bus[SERVALT_MIIM_BUS_COUNT]; + 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, + [MSCC_QS_XTR_DATA_PRESENT] = 0x1c, + [MSCC_QS_INJ_WR] = 0x2c, + [MSCC_QS_INJ_CTRL] = 0x34, +}; + +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); + writel(PHY_CFG_RST | PHY_CFG_COMMON_RST + | PHY_CFG_ENA, BASE_DEVCPU_GCB + GCB_PHY_CFG + PHY_CFG); + if (wait_for_bit_le32((const void *)(BASE_DEVCPU_GCB + GCB_PHY_CFG) + + PHY_STAT, PHY_STAT_SUPERVISOR_COMPLETE, + true, 2000, false)) { + pr_err("Timeout in phy reset\n"); + } +} + +static void servalt_cpu_capture_setup(struct servalt_private *priv) +{ + /* ASM: No preamble and IFH prefix on CPU injected frames */ + writel(ASM_CFG_PORT_NO_PREAMBLE_ENA | + ASM_CFG_PORT_INJ_FORMAT_CFG(1), + priv->regs[ASM] + ASM_CFG_PORT(CPU_PORT)); + + /* Set Manual injection via DEVCPU_QS registers for CPU queue 0 */ + writel(0x5, priv->regs[QS] + QS_INJ_GRP_CFG(0)); + + /* Set Manual extraction via DEVCPU_QS registers for CPU queue 0 */ + writel(0x7, priv->regs[QS] + QS_XTR_GRP_CFG(0)); + + /* Enable CPU port for any frame transfer */ + setbits_le32(priv->regs[QFWD] + QFWD_SYSTEM_SWITCH_PORT_MODE(CPU_PORT), + QFWD_SYSTEM_SWITCH_PORT_MODE_PORT_ENA); + + /* Send a copy to CPU when found as forwarding entry */ + setbits_le32(priv->regs[ANA_L2] + ANA_L2_COMMON_FWD_CFG, + ANA_L2_COMMON_FWD_CFG_CPU_DMAC_COPY_ENA); +} + +static void servalt_port_init(struct servalt_private *priv, int port) +{ + void __iomem *regs = priv->regs[port]; + + /* Enable PCS */ + writel(DEV_PCS1G_CFG_PCS1G_CFG_PCS_ENA, + regs + DEV_PCS1G_CFG_PCS1G_CFG); + + /* Disable Signal Detect */ + writel(0, regs + DEV_PCS1G_CFG_PCS1G_SD); + + /* Enable MAC RX and TX */ + writel(DEV_MAC_CFG_MAC_ENA_RX_ENA | + DEV_MAC_CFG_MAC_ENA_TX_ENA, + regs + DEV_MAC_CFG_MAC_ENA); + + /* Clear sgmii_mode_ena */ + writel(0, regs + DEV_PCS1G_CFG_PCS1G_MODE); + + /* + * Clear sw_resolve_ena(bit 0) and set adv_ability to + * something meaningful just in case + */ + writel(DEV_PCS1G_CFG_PCS1G_ANEG_ADV_ABILITY(0x20), + regs + DEV_PCS1G_CFG_PCS1G_ANEG); + + /* Set MAC IFG Gaps */ + writel(DEV_MAC_CFG_MAC_IFG_TX_IFG(4) | + DEV_MAC_CFG_MAC_IFG_RX_IFG1(5) | + DEV_MAC_CFG_MAC_IFG_RX_IFG2(1), + regs + DEV_MAC_CFG_MAC_IFG); + + /* Set link speed and release all resets */ + writel(DEV_DEV_CFG_DEV_RST_CTRL_SPEED_SEL(2), + regs + DEV_DEV_CFG_DEV_RST_CTRL); + + /* Make VLAN aware for CPU traffic */ + writel(ANA_CL_PORT_VLAN_CFG_AWARE_ENA | + ANA_CL_PORT_VLAN_CFG_POP_CNT(1) | + MAC_VID, + priv->regs[ANA_CL] + ANA_CL_PORT_VLAN_CFG(port)); + + /* Enable CPU port for any frame transfer */ + setbits_le32(priv->regs[QFWD] + QFWD_SYSTEM_SWITCH_PORT_MODE(port), + QFWD_SYSTEM_SWITCH_PORT_MODE_PORT_ENA); +} + +static int ram_init(u32 val, void __iomem *addr) +{ + writel(val, addr); + + if (wait_for_bit_le32(addr, BIT(1), false, 2000, false)) { + printf("Timeout in memory reset, reg = 0x%08x\n", val); + return 1; + } + + return 0; +} + +static int servalt_switch_init(struct servalt_private *priv) +{ + /* Initialize memories */ + ram_init(0x3, priv->regs[QSYS] + QSYS_RAM_CTRL_RAM_INIT); + ram_init(0x3, priv->regs[ASM] + ASM_RAM_CTRL_RAM_INIT); + ram_init(0x3, priv->regs[ANA_AC] + ANA_AC_RAM_CTRL_RAM_INIT); + ram_init(0x3, priv->regs[REW] + REW_RAM_CTRL_RAM_INIT); + + /* Reset counters */ + writel(0x1, priv->regs[ANA_AC] + ANA_AC_STAT_GLOBAL_CFG_PORT_RESET); + writel(0x1, priv->regs[ASM] + ASM_CFG_STAT_CFG); + + /* Enable switch-core and queue system */ + writel(0x1, priv->regs[QSYS] + QSYS_SYSTEM_RESET_CFG); + + return 0; +} + +static void servalt_switch_config(struct servalt_private *priv) +{ + writel(0x55555555, priv->regs[QSYS] + QSYS_CALCFG_CAL_AUTO); + + writel(readl(priv->regs[QSYS] + QSYS_CALCFG_CAL_CTRL) | + QSYS_CALCFG_CAL_CTRL_CAL_MODE(8), + priv->regs[QSYS] + QSYS_CALCFG_CAL_CTRL); +} + +static int servalt_initialize(struct servalt_private *priv) +{ + int ret, i; + + /* Initialize switch memories, enable core */ + ret = servalt_switch_init(priv); + if (ret) + return ret; + + servalt_switch_config(priv); + + for (i = 0; i < MAX_PORT; i++) + servalt_port_init(priv, i); + + servalt_cpu_capture_setup(priv); + + return 0; +} + +static inline +int servalt_vlant_wait_for_completion(struct servalt_private *priv) +{ + if (wait_for_bit_le32(priv->regs[LRN] + LRN_COMMON_ACCESS_CTRL, + LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT, + false, 2000, false)) + return -ETIMEDOUT; + + return 0; +} + +static int servalt_mac_table_add(struct servalt_private *priv, + const unsigned char mac[ETH_ALEN], int pgid) +{ + u32 macl = 0, mach = 0; + + /* + * Set the MAC address to handle and the vlan associated in a format + * understood by the hardware. + */ + mach |= MAC_VID << 16; + mach |= ((u32)mac[0]) << 8; + mach |= ((u32)mac[1]) << 0; + macl |= ((u32)mac[2]) << 24; + macl |= ((u32)mac[3]) << 16; + macl |= ((u32)mac[4]) << 8; + macl |= ((u32)mac[5]) << 0; + + writel(mach, priv->regs[LRN] + LRN_COMMON_MAC_ACCESS_CFG0); + writel(macl, priv->regs[LRN] + LRN_COMMON_MAC_ACCESS_CFG1); + + writel(LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_ADDR(pgid) | + LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_TYPE(0x3) | + LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_CPU_COPY | + LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_CPU_QU(0) | + LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_VLD | + LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_LOCKED, + priv->regs[LRN] + LRN_COMMON_MAC_ACCESS_CFG2); + + writel(LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT, + priv->regs[LRN] + LRN_COMMON_ACCESS_CTRL); + + return servalt_vlant_wait_for_completion(priv); +} + +static int servalt_write_hwaddr(struct udevice *dev) +{ + struct servalt_private *priv = dev_get_priv(dev); + struct eth_pdata *pdata = dev_get_platdata(dev); + + return servalt_mac_table_add(priv, pdata->enetaddr, PGID_UNICAST); +} + +static int servalt_start(struct udevice *dev) +{ + struct servalt_private *priv = dev_get_priv(dev); + struct eth_pdata *pdata = dev_get_platdata(dev); + const unsigned char mac[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, + 0xff }; + int ret; + + ret = servalt_initialize(priv); + if (ret) + return ret; + + /* Set MAC address tables entries for CPU redirection */ + ret = servalt_mac_table_add(priv, mac, PGID_BROADCAST); + if (ret) + return ret; + + ret = servalt_mac_table_add(priv, pdata->enetaddr, PGID_UNICAST); + if (ret) + return ret; + + return 0; +} + +static void servalt_stop(struct udevice *dev) +{ +} + +static int servalt_send(struct udevice *dev, void *packet, int length) +{ + struct servalt_private *priv = dev_get_priv(dev); + u32 ifh[IFH_LEN]; + u32 *buf = packet; + + memset(ifh, '\0', IFH_LEN * 4); + + /* Set DST PORT_MASK */ + ifh[0] = htonl(0); + ifh[1] = htonl(0x1FFFFF); + ifh[2] = htonl(~0); + /* Set DST_MODE to INJECT and UPDATE_FCS */ + ifh[5] = htonl(0x4c0); + + return mscc_send(priv->regs[QS], servalt_regs_qs, + ifh, IFH_LEN, buf, length); +} + +static int servalt_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct servalt_private *priv = dev_get_priv(dev); + u32 *rxbuf = (u32 *)net_rx_packets[0]; + int byte_cnt = 0; + + byte_cnt = mscc_recv(priv->regs[QS], servalt_regs_qs, rxbuf, IFH_LEN, + false); + + *packetp = net_rx_packets[0]; + + return byte_cnt; +} + +static struct mii_dev *get_mdiobus(phys_addr_t base, unsigned long size) +{ + int i = 0; + + for (i = 0; i < SERVALT_MIIM_BUS_COUNT; ++i) + if (miim[i].miim_base == base && miim[i].miim_size == size) + return miim[i].bus; + + return NULL; +} + +static void add_port_entry(struct servalt_private *priv, size_t index, + size_t phy_addr, struct mii_dev *bus) +{ + priv->ports[index].phy_addr = phy_addr; + priv->ports[index].bus = bus; +} + +static int servalt_probe(struct udevice *dev) +{ + struct servalt_private *priv = dev_get_priv(dev); + int i; + struct resource res; + fdt32_t faddr; + phys_addr_t addr_base; + unsigned long addr_size; + ofnode eth_node, node, mdio_node; + size_t phy_addr; + struct mii_dev *bus; + struct ofnode_phandle_args phandle; + + if (!priv) + return -EINVAL; + + /* Get registers and map them to the private structure */ + for (i = 0; i < ARRAY_SIZE(regs_names); i++) { + priv->regs[i] = dev_remap_addr_name(dev, regs_names[i]); + if (!priv->regs[i]) { + debug + ("Error can't get regs base addresses for %s\n", + regs_names[i]); + return -ENOMEM; + } + } + + /* Initialize miim buses */ + memset(&miim, 0x0, sizeof(struct mscc_miim_dev) * + SERVALT_MIIM_BUS_COUNT); + + /* iterate all the ports and find out on which bus they are */ + i = 0; + eth_node = dev_read_first_subnode(dev); + for (node = ofnode_first_subnode(eth_node); + ofnode_valid(node); + node = ofnode_next_subnode(node)) { + if (ofnode_read_resource(node, 0, &res)) + return -ENOMEM; + i = res.start; + + ofnode_parse_phandle_with_args(node, "phy-handle", NULL, 0, 0, + &phandle); + + /* Get phy address on mdio bus */ + if (ofnode_read_resource(phandle.node, 0, &res)) + return -ENOMEM; + phy_addr = res.start; + + /* Get mdio node */ + mdio_node = ofnode_get_parent(phandle.node); + + if (ofnode_read_resource(mdio_node, 0, &res)) + return -ENOMEM; + faddr = cpu_to_fdt32(res.start); + + addr_base = ofnode_translate_address(mdio_node, &faddr); + addr_size = res.end - res.start; + + /* 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); + + /* Connect mdio bus with the port */ + bus = get_mdiobus(addr_base, addr_size); + add_port_entry(priv, i, phy_addr, bus); + } + + mscc_phy_reset(); + + for (i = 0; i < MAX_PORT; i++) { + if (!priv->ports[i].bus) + continue; + + phy_connect(priv->ports[i].bus, priv->ports[i].phy_addr, dev, + PHY_INTERFACE_MODE_NONE); + } + + return 0; +} + +static int servalt_remove(struct udevice *dev) +{ + struct servalt_private *priv = dev_get_priv(dev); + int i; + + for (i = 0; i < SERVALT_MIIM_BUS_COUNT; i++) { + mdio_unregister(priv->bus[i]); + mdio_free(priv->bus[i]); + } + + return 0; +} + +static const struct eth_ops servalt_ops = { + .start = servalt_start, + .stop = servalt_stop, + .send = servalt_send, + .recv = servalt_recv, + .write_hwaddr = servalt_write_hwaddr, +}; + +static const struct udevice_id mscc_servalt_ids[] = { + {.compatible = "mscc,vsc7437-switch" }, + { /* Sentinel */ } +}; + +U_BOOT_DRIVER(servalt) = { + .name = "servalt-switch", + .id = UCLASS_ETH, + .of_match = mscc_servalt_ids, + .probe = servalt_probe, + .remove = servalt_remove, + .ops = &servalt_ops, + .priv_auto_alloc_size = sizeof(struct servalt_private), + .platdata_auto_alloc_size = sizeof(struct eth_pdata), +}; diff --git a/drivers/net/sh_eth.c b/drivers/net/sh_eth.c index 2e1123c488..4646f2ba4e 100644 --- a/drivers/net/sh_eth.c +++ b/drivers/net/sh_eth.c @@ -769,19 +769,9 @@ static int sh_ether_start(struct udevice *dev) struct sh_eth_dev *eth = &priv->shdev; int ret; - ret = clk_enable(&priv->clk); - if (ret) - return ret; - ret = sh_eth_init_common(eth, pdata->enetaddr); if (ret) - goto err_clk; - - ret = sh_eth_phy_config(dev); - if (ret) { - printf(SHETHER_NAME ": phy config timeout\n"); - goto err_start; - } + return ret; ret = sh_eth_start_common(eth); if (ret) @@ -792,17 +782,17 @@ static int sh_ether_start(struct udevice *dev) err_start: sh_eth_tx_desc_free(eth); sh_eth_rx_desc_free(eth); -err_clk: - clk_disable(&priv->clk); return ret; } static void sh_ether_stop(struct udevice *dev) { struct sh_ether_priv *priv = dev_get_priv(dev); + struct sh_eth_dev *eth = &priv->shdev; + struct sh_eth_info *port_info = ð->port_info[eth->port]; + phy_shutdown(port_info->phydev); sh_eth_stop(&priv->shdev); - clk_disable(&priv->clk); } static int sh_ether_probe(struct udevice *udev) @@ -853,8 +843,20 @@ static int sh_ether_probe(struct udevice *udev) eth->port_info[eth->port].iobase = (void __iomem *)(BASE_IO_ADDR + 0x800 * eth->port); + ret = clk_enable(&priv->clk); + if (ret) + goto err_mdio_register; + + ret = sh_eth_phy_config(udev); + if (ret) { + printf(SHETHER_NAME ": phy config timeout\n"); + goto err_phy_config; + } + return 0; +err_phy_config: + clk_disable(&priv->clk); err_mdio_register: mdio_free(mdiodev); return ret; @@ -866,6 +868,7 @@ static int sh_ether_remove(struct udevice *udev) struct sh_eth_dev *eth = &priv->shdev; struct sh_eth_info *port_info = ð->port_info[eth->port]; + clk_disable(&priv->clk); free(port_info->phydev); mdio_unregister(priv->bus); mdio_free(priv->bus); diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c index c9798445c7..98bd7a5823 100644 --- a/drivers/net/sun8i_emac.c +++ b/drivers/net/sun8i_emac.c @@ -14,12 +14,14 @@ #include <asm/arch/clock.h> #include <asm/arch/gpio.h> #include <common.h> +#include <clk.h> #include <dm.h> #include <fdt_support.h> #include <linux/err.h> #include <malloc.h> #include <miiphy.h> #include <net.h> +#include <reset.h> #include <dt-bindings/pinctrl/sun4i-a10.h> #ifdef CONFIG_DM_GPIO #include <asm-generic/gpio.h> @@ -135,6 +137,8 @@ struct emac_eth_dev { phys_addr_t sysctl_reg; struct phy_device *phydev; struct mii_dev *bus; + struct clk tx_clk; + struct reset_ctl tx_rst; #ifdef CONFIG_DM_GPIO struct gpio_desc reset_gpio; #endif @@ -285,10 +289,18 @@ static int sun8i_emac_set_syscon(struct sun8i_eth_pdata *pdata, int ret; u32 reg; - reg = readl(priv->sysctl_reg + 0x30); + if (priv->variant == R40_GMAC) { + /* Select RGMII for R40 */ + reg = readl(priv->sysctl_reg + 0x164); + reg |= CCM_GMAC_CTRL_TX_CLK_SRC_INT_RGMII | + CCM_GMAC_CTRL_GPIT_RGMII | + CCM_GMAC_CTRL_TX_CLK_DELAY(CONFIG_GMAC_TX_DELAY); - if (priv->variant == R40_GMAC) + writel(reg, priv->sysctl_reg + 0x164); return 0; + } + + reg = readl(priv->sysctl_reg + 0x30); if (priv->variant == H3_EMAC) { ret = sun8i_emac_set_syscon_ephy(priv, ®); @@ -639,9 +651,24 @@ static int sun8i_eth_write_hwaddr(struct udevice *dev) return _sun8i_write_hwaddr(priv, pdata->enetaddr); } -static void sun8i_emac_board_setup(struct emac_eth_dev *priv) +static int sun8i_emac_board_setup(struct emac_eth_dev *priv) { struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + int ret; + + ret = clk_enable(&priv->tx_clk); + if (ret) { + dev_err(dev, "failed to enable TX clock\n"); + return ret; + } + + if (reset_valid(&priv->tx_rst)) { + ret = reset_deassert(&priv->tx_rst); + if (ret) { + dev_err(dev, "failed to deassert TX reset\n"); + goto err_tx_clk; + } + } if (priv->variant == H3_EMAC) { /* Only H3/H5 have clock controls for internal EPHY */ @@ -656,26 +683,11 @@ static void sun8i_emac_board_setup(struct emac_eth_dev *priv) } } - if (priv->variant == R40_GMAC) { - /* Set clock gating for emac */ - setbits_le32(&ccm->ahb_reset1_cfg, BIT(AHB_RESET_OFFSET_GMAC)); - - /* De-assert EMAC */ - setbits_le32(&ccm->ahb_gate1, BIT(AHB_GATE_OFFSET_GMAC)); + return 0; - /* Select RGMII for R40 */ - setbits_le32(&ccm->gmac_clk_cfg, - CCM_GMAC_CTRL_TX_CLK_SRC_INT_RGMII | - CCM_GMAC_CTRL_GPIT_RGMII); - setbits_le32(&ccm->gmac_clk_cfg, - CCM_GMAC_CTRL_TX_CLK_DELAY(CONFIG_GMAC_TX_DELAY)); - } else { - /* Set clock gating for emac */ - setbits_le32(&ccm->ahb_gate0, BIT(AHB_GATE_OFFSET_GMAC)); - - /* De-assert EMAC */ - setbits_le32(&ccm->ahb_reset0_cfg, BIT(AHB_RESET_OFFSET_GMAC)); - } +err_tx_clk: + clk_disable(&priv->tx_clk); + return ret; } #if defined(CONFIG_DM_GPIO) @@ -802,10 +814,14 @@ static int sun8i_emac_eth_probe(struct udevice *dev) struct sun8i_eth_pdata *sun8i_pdata = dev_get_platdata(dev); struct eth_pdata *pdata = &sun8i_pdata->eth_pdata; struct emac_eth_dev *priv = dev_get_priv(dev); + int ret; priv->mac_reg = (void *)pdata->iobase; - sun8i_emac_board_setup(priv); + ret = sun8i_emac_board_setup(priv); + if (ret) + return ret; + sun8i_emac_set_syscon(sun8i_pdata, priv); sun8i_mdio_init(dev->name, dev); @@ -834,8 +850,8 @@ static int sun8i_emac_eth_ofdata_to_platdata(struct udevice *dev) int offset = 0; #ifdef CONFIG_DM_GPIO int reset_flags = GPIOD_IS_OUT; - int ret = 0; #endif + int ret; pdata->iobase = devfdt_get_addr(dev); if (pdata->iobase == FDT_ADDR_T_NONE) { @@ -850,25 +866,35 @@ static int sun8i_emac_eth_ofdata_to_platdata(struct udevice *dev) return -EINVAL; } - if (priv->variant != R40_GMAC) { - offset = fdtdec_lookup_phandle(gd->fdt_blob, node, "syscon"); - if (offset < 0) { - debug("%s: cannot find syscon node\n", __func__); - return -EINVAL; - } - reg = fdt_getprop(gd->fdt_blob, offset, "reg", NULL); - if (!reg) { - debug("%s: cannot find reg property in syscon node\n", - __func__); - return -EINVAL; - } - priv->sysctl_reg = fdt_translate_address((void *)gd->fdt_blob, - offset, reg); - if (priv->sysctl_reg == FDT_ADDR_T_NONE) { - debug("%s: Cannot find syscon base address\n", - __func__); - return -EINVAL; - } + ret = clk_get_by_name(dev, "stmmaceth", &priv->tx_clk); + if (ret) { + dev_err(dev, "failed to get TX clock\n"); + return ret; + } + + ret = reset_get_by_name(dev, "stmmaceth", &priv->tx_rst); + if (ret && ret != -ENOENT) { + dev_err(dev, "failed to get TX reset\n"); + return ret; + } + + offset = fdtdec_lookup_phandle(gd->fdt_blob, node, "syscon"); + if (offset < 0) { + debug("%s: cannot find syscon node\n", __func__); + return -EINVAL; + } + + reg = fdt_getprop(gd->fdt_blob, offset, "reg", NULL); + if (!reg) { + debug("%s: cannot find reg property in syscon node\n", + __func__); + return -EINVAL; + } + priv->sysctl_reg = fdt_translate_address((void *)gd->fdt_blob, + offset, reg); + if (priv->sysctl_reg == FDT_ADDR_T_NONE) { + debug("%s: Cannot find syscon base address\n", __func__); + return -EINVAL; } pdata->phy_interface = -1; diff --git a/drivers/net/sunxi_emac.c b/drivers/net/sunxi_emac.c index 8dbd3c50c1..9a5f7fd3c7 100644 --- a/drivers/net/sunxi_emac.c +++ b/drivers/net/sunxi_emac.c @@ -6,6 +6,7 @@ */ #include <common.h> +#include <clk.h> #include <dm.h> #include <linux/err.h> #include <malloc.h> @@ -157,6 +158,7 @@ struct sunxi_sramc_regs { struct emac_eth_dev { struct emac_regs *regs; + struct clk clk; struct mii_dev *bus; struct phy_device *phydev; int link_printed; @@ -500,14 +502,12 @@ static int _sunxi_emac_eth_send(struct emac_eth_dev *priv, void *packet, return 0; } -static void sunxi_emac_board_setup(struct emac_eth_dev *priv) +static int sunxi_emac_board_setup(struct emac_eth_dev *priv) { - struct sunxi_ccm_reg *const ccm = - (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; struct sunxi_sramc_regs *sram = (struct sunxi_sramc_regs *)SUNXI_SRAMC_BASE; struct emac_regs *regs = priv->regs; - int pin; + int pin, ret; /* Map SRAM to EMAC */ setbits_le32(&sram->ctrl1, 0x5 << 2); @@ -517,10 +517,16 @@ static void sunxi_emac_board_setup(struct emac_eth_dev *priv) sunxi_gpio_set_cfgpin(pin, SUNXI_GPA_EMAC); /* Set up clock gating */ - setbits_le32(&ccm->ahb_gate0, 0x1 << AHB_GATE_OFFSET_EMAC); + ret = clk_enable(&priv->clk); + if (ret) { + dev_err(dev, "failed to enable emac clock\n"); + return ret; + } /* Set MII clock */ clrsetbits_le32(®s->mac_mcfg, 0xf << 2, 0xd << 2); + + return 0; } static int sunxi_emac_eth_start(struct udevice *dev) @@ -557,9 +563,19 @@ static int sunxi_emac_eth_probe(struct udevice *dev) { struct eth_pdata *pdata = dev_get_platdata(dev); struct emac_eth_dev *priv = dev_get_priv(dev); + int ret; priv->regs = (struct emac_regs *)pdata->iobase; - sunxi_emac_board_setup(priv); + + ret = clk_get_by_index(dev, 0, &priv->clk); + if (ret) { + dev_err(dev, "failed to get emac clock\n"); + return ret; + } + + ret = sunxi_emac_board_setup(priv); + if (ret) + return ret; return sunxi_emac_init_phy(priv, dev); } diff --git a/drivers/net/ti/cpsw-common.c b/drivers/net/ti/cpsw-common.c index 6c8ddbd936..ac12cfe9b8 100644 --- a/drivers/net/ti/cpsw-common.c +++ b/drivers/net/ti/cpsw-common.c @@ -16,35 +16,11 @@ DECLARE_GLOBAL_DATA_PTR; #define CTRL_MAC_REG(offset, id) ((offset) + 0x8 * (id)) -static int davinci_emac_3517_get_macid(struct udevice *dev, u16 offset, - int slave, u8 *mac_addr) +static void davinci_emac_3517_get_macid(u32 addr, u8 *mac_addr) { - void *fdt = (void *)gd->fdt_blob; - int node = dev_of_offset(dev); - u32 macid_lsb; - u32 macid_msb; - fdt32_t gmii = 0; - int syscon; - u32 addr; - - syscon = fdtdec_lookup_phandle(fdt, node, "syscon"); - if (syscon < 0) { - pr_err("Syscon offset not found\n"); - return -ENOENT; - } - - addr = (u32)map_physmem(fdt_translate_address(fdt, syscon, &gmii), - sizeof(u32), MAP_NOCACHE); - if (addr == FDT_ADDR_T_NONE) { - pr_err("Not able to get syscon address to get mac efuse address\n"); - return -ENOENT; - } - - addr += CTRL_MAC_REG(offset, slave); - /* try reading mac address from efuse */ - macid_lsb = readl(addr); - macid_msb = readl(addr + 4); + u32 macid_lsb = readl(addr); + u32 macid_msb = readl(addr + 4); mac_addr[0] = (macid_msb >> 16) & 0xff; mac_addr[1] = (macid_msb >> 8) & 0xff; @@ -52,20 +28,62 @@ static int davinci_emac_3517_get_macid(struct udevice *dev, u16 offset, mac_addr[3] = (macid_lsb >> 16) & 0xff; mac_addr[4] = (macid_lsb >> 8) & 0xff; mac_addr[5] = macid_lsb & 0xff; +} - return 0; +static void cpsw_am33xx_cm_get_macid(u32 addr, u8 *mac_addr) +{ + /* try reading mac address from efuse */ + u32 macid_lo = readl(addr); + u32 macid_hi = readl(addr + 4); + + mac_addr[5] = (macid_lo >> 8) & 0xff; + mac_addr[4] = macid_lo & 0xff; + mac_addr[3] = (macid_hi >> 24) & 0xff; + mac_addr[2] = (macid_hi >> 16) & 0xff; + mac_addr[1] = (macid_hi >> 8) & 0xff; + mac_addr[0] = macid_hi & 0xff; +} + +void ti_cm_get_macid(struct udevice *dev, struct cpsw_platform_data *data, + u8 *mac_addr) +{ + if (!strcmp(data->macid_sel_compat, "cpsw,am33xx")) + cpsw_am33xx_cm_get_macid(data->syscon_addr, mac_addr); + else if (!strcmp(data->macid_sel_compat, "davinci,emac")) + davinci_emac_3517_get_macid(data->syscon_addr, mac_addr); } -static int cpsw_am33xx_cm_get_macid(struct udevice *dev, u16 offset, int slave, - u8 *mac_addr) +int ti_cm_get_macid_addr(struct udevice *dev, int slave, + struct cpsw_platform_data *data) { void *fdt = (void *)gd->fdt_blob; int node = dev_of_offset(dev); - u32 macid_lo; - u32 macid_hi; fdt32_t gmii = 0; int syscon; - u32 addr; + u16 offset; + + if (of_machine_is_compatible("ti,dm8148")) { + offset = 0x630; + data->macid_sel_compat = "cpsw,am33xx"; + } else if (of_machine_is_compatible("ti,am33xx")) { + offset = 0x630; + data->macid_sel_compat = "cpsw,am33xx"; + } else if (device_is_compatible(dev, "ti,am3517-emac")) { + offset = 0x110; + data->macid_sel_compat = "davinci,emac"; + } else if (device_is_compatible(dev, "ti,dm816-emac")) { + offset = 0x30; + data->macid_sel_compat = "cpsw,am33xx"; + } else if (of_machine_is_compatible("ti,am43")) { + offset = 0x630; + data->macid_sel_compat = "cpsw,am33xx"; + } else if (of_machine_is_compatible("ti,dra7")) { + offset = 0x514; + data->macid_sel_compat = "davinci,emac"; + } else { + dev_err(dev, "incompatible machine/device type for reading mac address\n"); + return -ENOENT; + } syscon = fdtdec_lookup_phandle(fdt, node, "syscon"); if (syscon < 0) { @@ -73,49 +91,16 @@ static int cpsw_am33xx_cm_get_macid(struct udevice *dev, u16 offset, int slave, return -ENOENT; } - addr = (u32)map_physmem(fdt_translate_address(fdt, syscon, &gmii), - sizeof(u32), MAP_NOCACHE); - if (addr == FDT_ADDR_T_NONE) { + data->syscon_addr = (u32)map_physmem(fdt_translate_address(fdt, syscon, + &gmii), + sizeof(u32), MAP_NOCACHE); + if (data->syscon_addr == FDT_ADDR_T_NONE) { pr_err("Not able to get syscon address to get mac efuse address\n"); return -ENOENT; } - addr += CTRL_MAC_REG(offset, slave); - - /* try reading mac address from efuse */ - macid_lo = readl(addr); - macid_hi = readl(addr + 4); - - mac_addr[5] = (macid_lo >> 8) & 0xff; - mac_addr[4] = macid_lo & 0xff; - mac_addr[3] = (macid_hi >> 24) & 0xff; - mac_addr[2] = (macid_hi >> 16) & 0xff; - mac_addr[1] = (macid_hi >> 8) & 0xff; - mac_addr[0] = macid_hi & 0xff; + data->syscon_addr += CTRL_MAC_REG(offset, slave); return 0; -} - -int ti_cm_get_macid(struct udevice *dev, int slave, u8 *mac_addr) -{ - if (of_machine_is_compatible("ti,dm8148")) - return cpsw_am33xx_cm_get_macid(dev, 0x630, slave, mac_addr); - - if (of_machine_is_compatible("ti,am33xx")) - return cpsw_am33xx_cm_get_macid(dev, 0x630, slave, mac_addr); - - if (device_is_compatible(dev, "ti,am3517-emac")) - return davinci_emac_3517_get_macid(dev, 0x110, slave, mac_addr); - - if (device_is_compatible(dev, "ti,dm816-emac")) - return cpsw_am33xx_cm_get_macid(dev, 0x30, slave, mac_addr); - - if (of_machine_is_compatible("ti,am43")) - return cpsw_am33xx_cm_get_macid(dev, 0x630, slave, mac_addr); - - if (of_machine_is_compatible("ti,dra7")) - return davinci_emac_3517_get_macid(dev, 0x514, slave, mac_addr); - dev_err(dev, "incompatible machine/device type for reading mac address\n"); - return -ENOENT; } diff --git a/drivers/net/ti/cpsw.c b/drivers/net/ti/cpsw.c index f5fd02efe1..20ddb44dd8 100644 --- a/drivers/net/ti/cpsw.c +++ b/drivers/net/ti/cpsw.c @@ -33,24 +33,6 @@ DECLARE_GLOBAL_DATA_PTR; #define GIGABITEN BIT(7) #define FULLDUPLEXEN BIT(0) #define MIIEN BIT(15) - -/* reg offset */ -#define CPSW_HOST_PORT_OFFSET 0x108 -#define CPSW_SLAVE0_OFFSET 0x208 -#define CPSW_SLAVE1_OFFSET 0x308 -#define CPSW_SLAVE_SIZE 0x100 -#define CPSW_CPDMA_OFFSET 0x800 -#define CPSW_HW_STATS 0x900 -#define CPSW_STATERAM_OFFSET 0xa00 -#define CPSW_CPTS_OFFSET 0xc00 -#define CPSW_ALE_OFFSET 0xd00 -#define CPSW_SLIVER0_OFFSET 0xd80 -#define CPSW_SLIVER1_OFFSET 0xdc0 -#define CPSW_BD_OFFSET 0x2000 -#define CPSW_MDIO_DIV 0xff - -#define AM335X_GMII_SEL_OFFSET 0x630 - /* DMA Registers */ #define CPDMA_TXCONTROL 0x004 #define CPDMA_RXCONTROL 0x014 @@ -209,10 +191,10 @@ struct cpdma_chan { #define chan_read_ptr(chan, fld) ((void *)__raw_readl((chan)->fld)) #define for_active_slave(slave, priv) \ - slave = (priv)->slaves + (priv)->data.active_slave; if (slave) + slave = (priv)->slaves + ((priv)->data)->active_slave; if (slave) #define for_each_slave(slave, priv) \ for (slave = (priv)->slaves; slave != (priv)->slaves + \ - (priv)->data.slaves; slave++) + ((priv)->data)->slaves; slave++) struct cpsw_priv { #ifdef CONFIG_DM_ETH @@ -220,7 +202,7 @@ struct cpsw_priv { #else struct eth_device *dev; #endif - struct cpsw_platform_data data; + struct cpsw_platform_data *data; int host_port; struct cpsw_regs *regs; @@ -327,7 +309,7 @@ static int cpsw_ale_match_addr(struct cpsw_priv *priv, const u8 *addr) u32 ale_entry[ALE_ENTRY_WORDS]; int type, idx; - for (idx = 0; idx < priv->data.ale_entries; idx++) { + for (idx = 0; idx < priv->data->ale_entries; idx++) { u8 entry_addr[6]; cpsw_ale_read(priv, idx, ale_entry); @@ -346,7 +328,7 @@ static int cpsw_ale_match_free(struct cpsw_priv *priv) u32 ale_entry[ALE_ENTRY_WORDS]; int type, idx; - for (idx = 0; idx < priv->data.ale_entries; idx++) { + for (idx = 0; idx < priv->data->ale_entries; idx++) { cpsw_ale_read(priv, idx, ale_entry); type = cpsw_ale_get_entry_type(ale_entry); if (type == ALE_TYPE_FREE) @@ -360,7 +342,7 @@ static int cpsw_ale_find_ageable(struct cpsw_priv *priv) u32 ale_entry[ALE_ENTRY_WORDS]; int type, idx; - for (idx = 0; idx < priv->data.ale_entries; idx++) { + for (idx = 0; idx < priv->data->ale_entries; idx++) { cpsw_ale_read(priv, idx, ale_entry); type = cpsw_ale_get_entry_type(ale_entry); if (type != ALE_TYPE_ADDR && type != ALE_TYPE_VLAN_ADDR) @@ -500,7 +482,7 @@ static int cpsw_slave_update_link(struct cpsw_slave *slave, *link = phy->link; if (phy->link) { /* link up */ - mac_control = priv->data.mac_control; + mac_control = priv->data->mac_control; if (phy->speed == 1000) mac_control |= GIGABITEN; if (phy->duplex == DUPLEX_FULL) @@ -710,7 +692,7 @@ static int _cpsw_init(struct cpsw_priv *priv, u8 *enetaddr) priv->desc_free = &priv->descs[0]; /* initialize channels */ - if (priv->data.version == CPSW_CTRL_VERSION_2) { + if (priv->data->version == CPSW_CTRL_VERSION_2) { memset(&priv->rx_chan, 0, sizeof(struct cpdma_chan)); priv->rx_chan.hdp = priv->dma_regs + CPDMA_RXHDP_VER2; priv->rx_chan.cp = priv->dma_regs + CPDMA_RXCP_VER2; @@ -733,8 +715,8 @@ static int _cpsw_init(struct cpsw_priv *priv, u8 *enetaddr) /* clear dma state */ setbit_and_wait_for_clear32(priv->dma_regs + CPDMA_SOFTRESET); - if (priv->data.version == CPSW_CTRL_VERSION_2) { - for (i = 0; i < priv->data.channels; i++) { + if (priv->data->version == CPSW_CTRL_VERSION_2) { + for (i = 0; i < priv->data->channels; i++) { __raw_writel(0, priv->dma_regs + CPDMA_RXHDP_VER2 + 4 * i); __raw_writel(0, priv->dma_regs + CPDMA_RXFREE + 4 @@ -747,7 +729,7 @@ static int _cpsw_init(struct cpsw_priv *priv, u8 *enetaddr) * i); } } else { - for (i = 0; i < priv->data.channels; i++) { + for (i = 0; i < priv->data->channels; i++) { __raw_writel(0, priv->dma_regs + CPDMA_RXHDP_VER1 + 4 * i); __raw_writel(0, priv->dma_regs + CPDMA_RXFREE + 4 @@ -843,7 +825,7 @@ static void cpsw_slave_setup(struct cpsw_slave *slave, int slave_num, struct cpsw_priv *priv) { void *regs = priv->regs; - struct cpsw_slave_data *data = priv->data.slave_data + slave_num; + struct cpsw_slave_data *data = priv->data->slave_data + slave_num; slave->slave_num = slave_num; slave->data = data; slave->regs = regs + data->slave_reg_ofs; @@ -879,7 +861,7 @@ static int cpsw_phy_init(struct cpsw_priv *priv, struct cpsw_slave *slave) static void cpsw_phy_addr_update(struct cpsw_priv *priv) { - struct cpsw_platform_data *data = &priv->data; + struct cpsw_platform_data *data = priv->data; u16 alive = cpsw_mdio_get_alive(priv->bus); int active = data->active_slave; int new_addr = ffs(alive) - 1; @@ -899,7 +881,7 @@ static void cpsw_phy_addr_update(struct cpsw_priv *priv) int _cpsw_register(struct cpsw_priv *priv) { struct cpsw_slave *slave; - struct cpsw_platform_data *data = &priv->data; + struct cpsw_platform_data *data = priv->data; void *regs = (void *)data->cpsw_base; priv->slaves = malloc(sizeof(struct cpsw_slave) * data->slaves); @@ -988,7 +970,7 @@ int cpsw_register(struct cpsw_platform_data *data) } priv->dev = dev; - priv->data = *data; + priv->data = data; strcpy(dev->name, "cpsw"); dev->iobase = 0; @@ -1048,16 +1030,6 @@ static void cpsw_eth_stop(struct udevice *dev) return _cpsw_halt(priv); } - -static int cpsw_eth_probe(struct udevice *dev) -{ - struct cpsw_priv *priv = dev_get_priv(dev); - - priv->dev = dev; - - return _cpsw_register(priv); -} - static const struct eth_ops cpsw_eth_ops = { .start = cpsw_eth_start, .send = cpsw_eth_send, @@ -1079,9 +1051,9 @@ static void cpsw_gmii_sel_am3352(struct cpsw_priv *priv, u32 mask; u32 mode = 0; bool rgmii_id = false; - int slave = priv->data.active_slave; + int slave = priv->data->active_slave; - reg = readl(priv->data.gmii_sel); + reg = readl(priv->data->gmii_sel); switch (phy_mode) { case PHY_INTERFACE_MODE_RMII: @@ -1107,7 +1079,7 @@ static void cpsw_gmii_sel_am3352(struct cpsw_priv *priv, mask = GMII_SEL_MODE_MASK << (slave * 2) | BIT(slave + 6); mode <<= slave * 2; - if (priv->data.rmii_clock_external) { + if (priv->data->rmii_clock_external) { if (slave == 0) mode |= AM33XX_GMII_SEL_RMII1_IO_CLK_EN; else @@ -1124,7 +1096,7 @@ static void cpsw_gmii_sel_am3352(struct cpsw_priv *priv, reg &= ~mask; reg |= mode; - writel(reg, priv->data.gmii_sel); + writel(reg, priv->data->gmii_sel); } static void cpsw_gmii_sel_dra7xx(struct cpsw_priv *priv, @@ -1133,9 +1105,9 @@ static void cpsw_gmii_sel_dra7xx(struct cpsw_priv *priv, u32 reg; u32 mask; u32 mode = 0; - int slave = priv->data.active_slave; + int slave = priv->data->active_slave; - reg = readl(priv->data.gmii_sel); + reg = readl(priv->data->gmii_sel); switch (phy_mode) { case PHY_INTERFACE_MODE_RMII: @@ -1168,13 +1140,13 @@ static void cpsw_gmii_sel_dra7xx(struct cpsw_priv *priv, return; } - if (priv->data.rmii_clock_external) + if (priv->data->rmii_clock_external) dev_err(priv->dev, "RMII External clock is not supported\n"); reg &= ~mask; reg |= mode; - writel(reg, priv->data.gmii_sel); + writel(reg, priv->data->gmii_sel); } static void cpsw_phy_sel(struct cpsw_priv *priv, const char *compat, @@ -1188,13 +1160,28 @@ static void cpsw_phy_sel(struct cpsw_priv *priv, const char *compat, cpsw_gmii_sel_dra7xx(priv, phy_mode); } +static int cpsw_eth_probe(struct udevice *dev) +{ + struct cpsw_priv *priv = dev_get_priv(dev); + struct eth_pdata *pdata = dev_get_platdata(dev); + + priv->dev = dev; + priv->data = pdata->priv_pdata; + ti_cm_get_macid(dev, priv->data, pdata->enetaddr); + /* Select phy interface in control module */ + cpsw_phy_sel(priv, priv->data->phy_sel_compat, + pdata->phy_interface); + + return _cpsw_register(priv); +} + +#if CONFIG_IS_ENABLED(OF_CONTROL) static int cpsw_eth_ofdata_to_platdata(struct udevice *dev) { struct eth_pdata *pdata = dev_get_platdata(dev); - struct cpsw_priv *priv = dev_get_priv(dev); + struct cpsw_platform_data *data; struct gpio_desc *mode_gpios; const char *phy_mode; - const char *phy_sel_compat = NULL; const void *fdt = gd->fdt_blob; int node = dev_of_offset(dev); int subnode; @@ -1203,45 +1190,47 @@ static int cpsw_eth_ofdata_to_platdata(struct udevice *dev) int num_mode_gpios; int ret; + data = calloc(1, sizeof(struct cpsw_platform_data)); + pdata->priv_pdata = data; pdata->iobase = devfdt_get_addr(dev); - priv->data.version = CPSW_CTRL_VERSION_2; - priv->data.bd_ram_ofs = CPSW_BD_OFFSET; - priv->data.ale_reg_ofs = CPSW_ALE_OFFSET; - priv->data.cpdma_reg_ofs = CPSW_CPDMA_OFFSET; - priv->data.mdio_div = CPSW_MDIO_DIV; - priv->data.host_port_reg_ofs = CPSW_HOST_PORT_OFFSET, + data->version = CPSW_CTRL_VERSION_2; + data->bd_ram_ofs = CPSW_BD_OFFSET; + data->ale_reg_ofs = CPSW_ALE_OFFSET; + data->cpdma_reg_ofs = CPSW_CPDMA_OFFSET; + data->mdio_div = CPSW_MDIO_DIV; + data->host_port_reg_ofs = CPSW_HOST_PORT_OFFSET, pdata->phy_interface = -1; - priv->data.cpsw_base = pdata->iobase; - priv->data.channels = fdtdec_get_int(fdt, node, "cpdma_channels", -1); - if (priv->data.channels <= 0) { + data->cpsw_base = pdata->iobase; + data->channels = fdtdec_get_int(fdt, node, "cpdma_channels", -1); + if (data->channels <= 0) { printf("error: cpdma_channels not found in dt\n"); return -ENOENT; } - priv->data.slaves = fdtdec_get_int(fdt, node, "slaves", -1); - if (priv->data.slaves <= 0) { + data->slaves = fdtdec_get_int(fdt, node, "slaves", -1); + if (data->slaves <= 0) { printf("error: slaves not found in dt\n"); return -ENOENT; } - priv->data.slave_data = malloc(sizeof(struct cpsw_slave_data) * - priv->data.slaves); + data->slave_data = malloc(sizeof(struct cpsw_slave_data) * + data->slaves); - priv->data.ale_entries = fdtdec_get_int(fdt, node, "ale_entries", -1); - if (priv->data.ale_entries <= 0) { + data->ale_entries = fdtdec_get_int(fdt, node, "ale_entries", -1); + if (data->ale_entries <= 0) { printf("error: ale_entries not found in dt\n"); return -ENOENT; } - priv->data.bd_ram_ofs = fdtdec_get_int(fdt, node, "bd_ram_size", -1); - if (priv->data.bd_ram_ofs <= 0) { + data->bd_ram_ofs = fdtdec_get_int(fdt, node, "bd_ram_size", -1); + if (data->bd_ram_ofs <= 0) { printf("error: bd_ram_size not found in dt\n"); return -ENOENT; } - priv->data.mac_control = fdtdec_get_int(fdt, node, "mac_control", -1); - if (priv->data.mac_control <= 0) { + data->mac_control = fdtdec_get_int(fdt, node, "mac_control", -1); + if (data->mac_control <= 0) { printf("error: ale_entries not found in dt\n"); return -ENOENT; } @@ -1256,7 +1245,7 @@ static int cpsw_eth_ofdata_to_platdata(struct udevice *dev) } active_slave = fdtdec_get_int(fdt, node, "active_slave", 0); - priv->data.active_slave = active_slave; + data->active_slave = active_slave; fdt_for_each_subnode(subnode, fdt, node) { int len; @@ -1271,108 +1260,107 @@ static int cpsw_eth_ofdata_to_platdata(struct udevice *dev) pr_err("Not able to get MDIO address space\n"); return -ENOENT; } - priv->data.mdio_base = mdio_base; + data->mdio_base = mdio_base; } if (!strncmp(name, "slave", 5)) { u32 phy_id[2]; - if (slave_index >= priv->data.slaves) + if (slave_index >= data->slaves) continue; phy_mode = fdt_getprop(fdt, subnode, "phy-mode", NULL); if (phy_mode) - priv->data.slave_data[slave_index].phy_if = + data->slave_data[slave_index].phy_if = phy_get_interface_by_name(phy_mode); - priv->data.slave_data[slave_index].phy_of_handle = + data->slave_data[slave_index].phy_of_handle = fdtdec_lookup_phandle(fdt, subnode, "phy-handle"); - if (priv->data.slave_data[slave_index].phy_of_handle >= 0) { - priv->data.slave_data[slave_index].phy_addr = + if (data->slave_data[slave_index].phy_of_handle >= 0) { + data->slave_data[slave_index].phy_addr = fdtdec_get_int(gd->fdt_blob, - priv->data.slave_data[slave_index].phy_of_handle, + data->slave_data[slave_index].phy_of_handle, "reg", -1); } else { fdtdec_get_int_array(fdt, subnode, "phy_id", phy_id, 2); - priv->data.slave_data[slave_index].phy_addr = + data->slave_data[slave_index].phy_addr = phy_id[1]; } slave_index++; } if (!strncmp(name, "cpsw-phy-sel", 12)) { - priv->data.gmii_sel = cpsw_get_addr_by_node(fdt, - subnode); + data->gmii_sel = cpsw_get_addr_by_node(fdt, subnode); - if (priv->data.gmii_sel == FDT_ADDR_T_NONE) { + if (data->gmii_sel == FDT_ADDR_T_NONE) { pr_err("Not able to get gmii_sel reg address\n"); return -ENOENT; } if (fdt_get_property(fdt, subnode, "rmii-clock-ext", NULL)) - priv->data.rmii_clock_external = true; + data->rmii_clock_external = true; - phy_sel_compat = fdt_getprop(fdt, subnode, "compatible", - NULL); - if (!phy_sel_compat) { + data->phy_sel_compat = fdt_getprop(fdt, subnode, + "compatible", NULL); + if (!data->phy_sel_compat) { pr_err("Not able to get gmii_sel compatible\n"); return -ENOENT; } } } - priv->data.slave_data[0].slave_reg_ofs = CPSW_SLAVE0_OFFSET; - priv->data.slave_data[0].sliver_reg_ofs = CPSW_SLIVER0_OFFSET; + data->slave_data[0].slave_reg_ofs = CPSW_SLAVE0_OFFSET; + data->slave_data[0].sliver_reg_ofs = CPSW_SLIVER0_OFFSET; - if (priv->data.slaves == 2) { - priv->data.slave_data[1].slave_reg_ofs = CPSW_SLAVE1_OFFSET; - priv->data.slave_data[1].sliver_reg_ofs = CPSW_SLIVER1_OFFSET; + if (data->slaves == 2) { + data->slave_data[1].slave_reg_ofs = CPSW_SLAVE1_OFFSET; + data->slave_data[1].sliver_reg_ofs = CPSW_SLIVER1_OFFSET; } - ret = ti_cm_get_macid(dev, active_slave, pdata->enetaddr); + ret = ti_cm_get_macid_addr(dev, active_slave, data); if (ret < 0) { pr_err("cpsw read efuse mac failed\n"); return ret; } - pdata->phy_interface = priv->data.slave_data[active_slave].phy_if; + pdata->phy_interface = data->slave_data[active_slave].phy_if; if (pdata->phy_interface == -1) { debug("%s: Invalid PHY interface '%s'\n", __func__, phy_mode); return -EINVAL; } - /* Select phy interface in control module */ - cpsw_phy_sel(priv, phy_sel_compat, pdata->phy_interface); - return 0; } +static const struct udevice_id cpsw_eth_ids[] = { + { .compatible = "ti,cpsw" }, + { .compatible = "ti,am335x-cpsw" }, + { } +}; +#endif + int cpsw_get_slave_phy_addr(struct udevice *dev, int slave) { struct cpsw_priv *priv = dev_get_priv(dev); - struct cpsw_platform_data *data = &priv->data; + struct cpsw_platform_data *data = priv->data; return data->slave_data[slave].phy_addr; } -static const struct udevice_id cpsw_eth_ids[] = { - { .compatible = "ti,cpsw" }, - { .compatible = "ti,am335x-cpsw" }, - { } -}; - U_BOOT_DRIVER(eth_cpsw) = { .name = "eth_cpsw", .id = UCLASS_ETH, +#if CONFIG_IS_ENABLED(OF_CONTROL) .of_match = cpsw_eth_ids, .ofdata_to_platdata = cpsw_eth_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct eth_pdata), +#endif .probe = cpsw_eth_probe, .ops = &cpsw_eth_ops, .priv_auto_alloc_size = sizeof(struct cpsw_priv), - .platdata_auto_alloc_size = sizeof(struct eth_pdata), - .flags = DM_FLAG_ALLOC_PRIV_DMA, + .flags = DM_FLAG_ALLOC_PRIV_DMA | DM_FLAG_PRE_RELOC, }; #endif /* CONFIG_DM_ETH */ diff --git a/drivers/net/ti/keystone_net.c b/drivers/net/ti/keystone_net.c index a3ba91cc3f..4baeeb83f1 100644 --- a/drivers/net/ti/keystone_net.c +++ b/drivers/net/ti/keystone_net.c @@ -88,6 +88,7 @@ struct ks2_eth_priv { struct mii_dev *mdio_bus; int phy_addr; phy_interface_t phy_if; + int phy_of_handle; int sgmii_link_type; void *mdio_base; struct rx_buff_desc net_rx_buffs; @@ -588,6 +589,10 @@ static int ks2_eth_probe(struct udevice *dev) if (priv->has_mdio) { priv->phydev = phy_connect(priv->mdio_bus, priv->phy_addr, dev, priv->phy_if); +#ifdef CONFIG_DM_ETH + if (priv->phy_of_handle) + priv->phydev->node = offset_to_ofnode(priv->phy_of_handle); +#endif phy_config(priv->phydev); } @@ -679,6 +684,7 @@ static int ks2_eth_parse_slave_interface(int netcp, int slave, int phy; int dma_count; u32 dma_channel[8]; + const char *phy_mode; priv->slave_port = fdtdec_get_int(fdt, slave, "slave-port", -1); priv->net_rx_buffs.rx_flow = priv->slave_port * 8; @@ -700,7 +706,9 @@ static int ks2_eth_parse_slave_interface(int netcp, int slave, priv->link_type = fdtdec_get_int(fdt, slave, "link-interface", -1); phy = fdtdec_lookup_phandle(fdt, slave, "phy-handle"); + if (phy >= 0) { + priv->phy_of_handle = phy; priv->phy_addr = fdtdec_get_int(fdt, phy, "reg", -1); mdio = fdt_parent_offset(fdt, phy); @@ -717,7 +725,19 @@ static int ks2_eth_parse_slave_interface(int netcp, int slave, priv->sgmii_link_type = SGMII_LINK_MAC_PHY; priv->has_mdio = true; } else if (priv->link_type == LINK_TYPE_RGMII_LINK_MAC_PHY) { - priv->phy_if = PHY_INTERFACE_MODE_RGMII; + phy_mode = fdt_getprop(fdt, slave, "phy-mode", NULL); + if (phy_mode) { + priv->phy_if = phy_get_interface_by_name(phy_mode); + if (priv->phy_if != PHY_INTERFACE_MODE_RGMII && + priv->phy_if != PHY_INTERFACE_MODE_RGMII_ID && + priv->phy_if != PHY_INTERFACE_MODE_RGMII_RXID && + priv->phy_if != PHY_INTERFACE_MODE_RGMII_TXID) { + pr_err("invalid phy-mode\n"); + return -EINVAL; + } + } else { + priv->phy_if = PHY_INTERFACE_MODE_RGMII; + } pdata->phy_interface = priv->phy_if; priv->has_mdio = true; } diff --git a/drivers/net/zynq_gem.c b/drivers/net/zynq_gem.c index 3bd0093b7a..033efb8195 100644 --- a/drivers/net/zynq_gem.c +++ b/drivers/net/zynq_gem.c @@ -261,45 +261,6 @@ static int phywrite(struct zynq_gem_priv *priv, u32 phy_addr, ZYNQ_GEM_PHYMNTNC_OP_W_MASK, &data); } -static int phy_detection(struct udevice *dev) -{ - int i; - u16 phyreg = 0; - struct zynq_gem_priv *priv = dev->priv; - - if (priv->phyaddr != -1) { - phyread(priv, priv->phyaddr, PHY_DETECT_REG, &phyreg); - if ((phyreg != 0xFFFF) && - ((phyreg & PHY_DETECT_MASK) == PHY_DETECT_MASK)) { - /* Found a valid PHY address */ - debug("Default phy address %d is valid\n", - priv->phyaddr); - return 0; - } else { - debug("PHY address is not setup correctly %d\n", - priv->phyaddr); - priv->phyaddr = -1; - } - } - - debug("detecting phy address\n"); - if (priv->phyaddr == -1) { - /* detect the PHY address */ - for (i = 31; i >= 0; i--) { - phyread(priv, i, PHY_DETECT_REG, &phyreg); - if ((phyreg != 0xFFFF) && - ((phyreg & PHY_DETECT_MASK) == PHY_DETECT_MASK)) { - /* Found a valid PHY address */ - priv->phyaddr = i; - debug("Found valid phy address, %d\n", i); - return 0; - } - } - } - printf("PHY is not detected\n"); - return -1; -} - static int zynq_gem_setup_mac(struct udevice *dev) { u32 i, macaddrlow, macaddrhigh; @@ -345,28 +306,20 @@ static int zynq_phy_init(struct udevice *dev) /* Enable only MDIO bus */ writel(ZYNQ_GEM_NWCTRL_MDEN_MASK, ®s->nwctrl); - if ((priv->interface != PHY_INTERFACE_MODE_SGMII) && - (priv->interface != PHY_INTERFACE_MODE_GMII)) { - ret = phy_detection(dev); - if (ret) { - printf("GEM PHY init failed\n"); - return ret; - } - } - priv->phydev = phy_connect(priv->bus, priv->phyaddr, dev, priv->interface); if (!priv->phydev) return -ENODEV; - priv->phydev->supported &= supported | ADVERTISED_Pause | - ADVERTISED_Asym_Pause; if (priv->max_speed) { ret = phy_set_supported(priv->phydev, priv->max_speed); if (ret) return ret; } + priv->phydev->supported &= supported | ADVERTISED_Pause | + ADVERTISED_Asym_Pause; + priv->phydev->advertising = priv->phydev->supported; priv->phydev->node = priv->phy_of_node; |