arm: mach-k3: Add secure device support
[oweals/u-boot.git] / drivers / net / ag7xxx.c
index f28187058e88fa1dc0997c4d3ccc3e8c858ed977..7f1dee4b3e4d5b9e6a5b35cf34dde9f6136f05a0 100644 (file)
@@ -1,9 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * Atheros AR71xx / AR9xxx GMAC driver
  *
  * Copyright (C) 2016 Marek Vasut <marex@denx.de>
- *
- * SPDX-License-Identifier:    GPL-2.0+
+ * Copyright (C) 2019 Rosy Song <rosysong@rosinson.com>
  */
 
 #include <common.h>
@@ -24,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 */
@@ -100,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)
@@ -198,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);
@@ -223,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);
@@ -238,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
@@ -273,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;
@@ -599,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;
        }
 
@@ -699,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, &reg);
+       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, &reg);
+       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, &reg);
+               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, &reg);
+       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, &reg);
+       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, &reg);
+       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;
 
@@ -715,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);
 }
@@ -725,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, &reg);
+                       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;
 }
@@ -740,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;
@@ -758,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, &reg);
+               else
+                       ret = ag7xxx_mdio_read(priv->bus, phymax, 0, MII_MIPSCR);
                if (ret < 0)
                        return ret;
 
@@ -780,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, &reg);
+               else
+                       ret = ag7xxx_mdio_read(priv->bus, i, 0, MII_MIPSCR);
                if (ret < 0)
                        return ret;
        }
@@ -842,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, &reg);
+       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, &reg);
+       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);
@@ -859,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;
        }
@@ -998,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 },
        { }
 };