apm821xx: wndr4700: mx60: convert to DSA
[openwrt/staging/chunkeey.git] / target / linux / apm821xx / patches-4.19 / 703-qca8k_QCA8337N.patch
diff --git a/target/linux/apm821xx/patches-4.19/703-qca8k_QCA8337N.patch b/target/linux/apm821xx/patches-4.19/703-qca8k_QCA8337N.patch
new file mode 100644 (file)
index 0000000..47d7b44
--- /dev/null
@@ -0,0 +1,459 @@
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -27,11 +27,14 @@
+ #define QCA8K_NUM_FDB_RECORDS                         2048
+ #define QCA8K_CPU_PORT                                        0
++#define QCA8K_MAC5_PORT                                       5
++#define QCA8K_MAC6_PORT                                       6
+ /* Global control registers */
+ #define QCA8K_REG_MASK_CTRL                           0x000
+-#define   QCA8K_MASK_CTRL_ID_M                                0xff
+-#define   QCA8K_MASK_CTRL_ID_S                                8
++#define   QCA8K_MASK_CTRL_DEVICE_ID_S                 8
++#define         QCA8K_MASK_CTRL_DEVICE_ID_M                   GENMASK(15,8)
++#define   QCA8K_MASK_CTRL_REVISION_ID_M                       GENMASK(7, 0)
+ #define QCA8K_REG_PORT0_PAD_CTRL                      0x004
+ #define QCA8K_REG_PORT5_PAD_CTRL                      0x008
+ #define QCA8K_REG_PORT6_PAD_CTRL                      0x00c
+@@ -41,13 +44,19 @@
+ #define   QCA8K_PORT_PAD_RGMII_RX_DELAY(x)            \
+                                               ((0x10 + (x & 0x3)) << 20)
+ #define   QCA8K_PORT_PAD_RGMII_RX_DELAY_EN            BIT(24)
++#define   QCA8K_PORT_PAD_RGMII_TX_DELAY_EN            BIT(25)
+ #define   QCA8K_PORT_PAD_SGMII_EN                     BIT(7)
++#define QCA8K_REG_PWS_REG                             0x010
++#define   QCA8K_PWS_REG_8327_PACKAGE148_EN            BIT(30)
++#define   QCA8K_PWS_REG_8337_PACKAGEMIN_EN            BIT(28)
+ #define QCA8K_REG_MODULE_EN                           0x030
+ #define   QCA8K_MODULE_EN_MIB                         BIT(0)
+ #define QCA8K_REG_MIB                                 0x034
+ #define   QCA8K_MIB_FLUSH                             BIT(24)
+ #define   QCA8K_MIB_CPU_KEEP                          BIT(20)
+ #define   QCA8K_MIB_BUSY                              BIT(17)
++#define QCA8K_REG_MAX_FRAME_SIZE                      0x078
++#define   QCA8K_MAX_FRAME_SIZE_MTU                    GENMASK(14, 0)
+ #define QCA8K_GOL_MAC_ADDR0                           0x60
+ #define QCA8K_GOL_MAC_ADDR1                           0x64
+ #define QCA8K_REG_PORT_STATUS(_i)                     (0x07c + (_i) * 4)
+@@ -63,6 +72,7 @@
+ #define   QCA8K_PORT_STATUS_LINK_UP                   BIT(8)
+ #define   QCA8K_PORT_STATUS_LINK_AUTO                 BIT(9)
+ #define   QCA8K_PORT_STATUS_LINK_PAUSE                        BIT(10)
++#define   QCA8K_PORT_STATUS_LINK_FLOW_CONTROL         BIT(12)
+ #define QCA8K_REG_PORT_HDR_CTRL(_i)                   (0x9c + (_i * 4))
+ #define   QCA8K_PORT_HDR_CTRL_RX_MASK                 GENMASK(3, 2)
+ #define   QCA8K_PORT_HDR_CTRL_RX_S                    2
+@@ -114,6 +124,11 @@
+ #define   QCA8K_GLOBAL_FW_CTRL1_UC_DP_S                       0
+ #define QCA8K_PORT_LOOKUP_CTRL(_i)                    (0x660 + (_i) * 0xc)
+ #define   QCA8K_PORT_LOOKUP_MEMBER                    GENMASK(6, 0)
++#define         QCA8K_PORT_LOOKUP_VLAN_MODE_MASK              GENMASK(9, 8)
++#define   QCA8K_PORT_LOOKUP_VLAN_MODE_DISABLE         (0 << 8)
++#define   QCA8K_PORT_LOOKUP_VLAN_MODE_FALLBACK                (1 << 8)
++#define   QCA8K_PORT_LOOKUP_VLAN_MODE_CHECK           (2 << 8)
++#define   QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE          (3 << 8)
+ #define   QCA8K_PORT_LOOKUP_STATE_MASK                        GENMASK(18, 16)
+ #define   QCA8K_PORT_LOOKUP_STATE_DISABLED            (0 << 16)
+ #define   QCA8K_PORT_LOOKUP_STATE_BLOCKING            (1 << 16)
+@@ -141,6 +156,8 @@
+ /* QCA specific MII registers */
+ #define MII_ATH_MMD_ADDR                              0x0d
+ #define MII_ATH_MMD_DATA                              0x0e
++#define MII_ATH_DBG_ADDR                              0x1d
++#define MII_ATH_DBG_DATA                              0x1e
+ enum {
+       QCA8K_PORT_SPEED_10M = 0,
+@@ -168,6 +185,8 @@ struct qca8k_priv {
+       struct dsa_switch *ds;
+       struct mutex reg_mutex;
+       struct device *dev;
++      unsigned int chip_id;
++      unsigned int chip_rev;
+ };
+ struct qca8k_mib_desc {
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -11,6 +11,7 @@
+ #include <linux/netdevice.h>
+ #include <net/dsa.h>
+ #include <linux/of_net.h>
++#include <linux/of_mdio.h>
+ #include <linux/of_platform.h>
+ #include <linux/if_bridge.h>
+ #include <linux/mdio.h>
+@@ -192,6 +193,63 @@ qca8k_rmw(struct qca8k_priv *priv, u32 r
+ }
+ static void
++qca8k_phy_dbg_read(struct qca8k_priv *priv, int phy_addr,
++           u16 dbg_addr, u16 *dbg_data)
++{
++      struct mii_bus *bus = priv->bus;
++
++      mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
++      __mdiobus_write(bus, phy_addr, MII_ATH_DBG_ADDR, dbg_addr);
++      *dbg_data = __mdiobus_read(bus, phy_addr, MII_ATH_DBG_DATA);
++      mutex_unlock(&bus->mdio_lock);
++}
++
++static void
++qca8k_phy_dbg_write(struct qca8k_priv *priv, int phy_addr,
++                   u16 dbg_addr, u16 dbg_data)
++{
++      struct mii_bus *bus = priv->bus;
++
++      mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
++      __mdiobus_write(bus, phy_addr, MII_ATH_DBG_ADDR, dbg_addr);
++      __mdiobus_write(bus, phy_addr, MII_ATH_DBG_DATA, dbg_data);
++      mutex_unlock(&bus->mdio_lock);
++}
++
++static inline void
++qca8k_phy_mmd_prep(struct mii_bus *bus, int phy_addr, u16 addr, u16 reg)
++{
++      __mdiobus_write(bus, phy_addr, MII_ATH_MMD_ADDR, addr);
++      __mdiobus_write(bus, phy_addr, MII_ATH_MMD_DATA, reg);
++      __mdiobus_write(bus, phy_addr, MII_ATH_MMD_ADDR, addr | 0x4000);
++}
++
++static void
++qca8k_phy_mmd_write(struct qca8k_priv *priv, int phy_addr, u16 addr, u16 reg, u16 data)
++{
++      struct mii_bus *bus = priv->bus;
++
++      mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
++      qca8k_phy_mmd_prep(bus, phy_addr, addr, reg);
++      __mdiobus_write(bus, phy_addr, MII_ATH_MMD_DATA, data);
++      mutex_unlock(&bus->mdio_lock);
++}
++
++static u16
++qca8k_phy_mmd_read(struct qca8k_priv *priv, int phy_addr, u16 addr, u16 reg)
++{
++      struct mii_bus *bus = priv->bus;
++      u16 data;
++
++      mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
++      qca8k_phy_mmd_prep(bus, phy_addr, addr, reg);
++      data = __mdiobus_read(bus, phy_addr, MII_ATH_MMD_DATA);
++      mutex_unlock(&bus->mdio_lock);
++
++      return data;
++}
++
++static void
+ qca8k_reg_set(struct qca8k_priv *priv, u32 reg, u32 val)
+ {
+       qca8k_rmw(priv, reg, 0, val);
+@@ -417,16 +475,46 @@ qca8k_mib_init(struct qca8k_priv *priv)
+       mutex_unlock(&priv->reg_mutex);
+ }
++static bool is_qca833x_rgmii_compat(struct qca8k_priv *priv)
++{
++      int tmp;
++
++      if (!(of_device_is_compatible(priv->dev->of_node, "qca8k,qca8337") ||
++            of_device_is_compatible(priv->dev->of_node, "qca8k,qca8334")))
++              return false;
++
++      tmp = of_get_phy_mode(priv->ds->ports[QCA8K_CPU_PORT].dn);
++      if (tmp == PHY_INTERFACE_MODE_RGMII) {
++              tmp = of_get_phy_mode(priv->ds->ports[QCA8K_MAC5_PORT].dn);
++              if (tmp < 0)
++                      return true;
++      }
++      return false;
++}
++
+ static int
+-qca8k_set_pad_ctrl(struct qca8k_priv *priv, int port, int mode)
++qca8k_set_pad_ctrl(struct qca8k_priv *priv, int port)
+ {
+-      u32 reg;
++      struct device_node *dn = priv->ds->ports[port].dn;
++      int phy_mode = -1;
++      u32 reg, val = 0;
++      int rgmii_rxclk_delay = 0;
++      int rgmii_txclk_delay = 0;
++      bool legacy_compat = false;
++
++      if (!dn) {
++              pr_info("skip not specified port %d\n", port);
++              return 0;
++      }
+       switch (port) {
+-      case 0:
++      case QCA8K_CPU_PORT:
+               reg = QCA8K_REG_PORT0_PAD_CTRL;
+               break;
+-      case 6:
++      case QCA8K_MAC5_PORT:
++              reg = QCA8K_REG_PORT5_PAD_CTRL;
++              break;
++      case QCA8K_MAC6_PORT:
+               reg = QCA8K_REG_PORT6_PAD_CTRL;
+               break;
+       default:
+@@ -434,31 +522,78 @@ qca8k_set_pad_ctrl(struct qca8k_priv *pr
+               return -EINVAL;
+       }
++      /* Initialize port pad mode (xMII type, delays...) */
++      phy_mode = of_get_phy_mode(dn);
++      if (phy_mode < 0) {
++              if (is_qca833x_rgmii_compat(priv)) {
++                      if (port == 5) {
++                              pr_warn("legacy qca8k configuration found... please update your DT.");
++                              legacy_compat = true;
++                              rgmii_rxclk_delay = 3;
++                              rgmii_txclk_delay = 3;
++                      }
++              } else {
++                      pr_err("Can't find phy-mode for port %d\n", port);
++                      return phy_mode;
++              }
++      }
++
++      /* rgmii's txclk and rxclk delay can be set even on links that
++       * aren't  connected to a RGMII CPU interface. So make it possible
++       * to specify the value even if it seems seemingly strange.
++       */
++      if (legacy_compat ||
++          !of_property_read_u32(dn, "qca,rxclk-delay", &rgmii_rxclk_delay) ||
++          phy_mode == PHY_INTERFACE_MODE_RGMII_ID ||
++          phy_mode == PHY_INTERFACE_MODE_RGMII_RXID) {
++              if (rgmii_rxclk_delay > 3) {
++                      pr_err("Invalid or missing rxclk-delay\n");
++                      return -EINVAL;
++              }
++
++              val |= QCA8K_PORT_PAD_RGMII_RX_DELAY(rgmii_rxclk_delay) |
++                     QCA8K_PORT_PAD_RGMII_RX_DELAY_EN;
++
++      };
++
++      if (legacy_compat ||
++          !of_property_read_u32(dn, "qca,txclk-delay", &rgmii_txclk_delay) ||
++          phy_mode == PHY_INTERFACE_MODE_RGMII_ID ||
++          phy_mode == PHY_INTERFACE_MODE_RGMII_TXID) {
++              if (rgmii_txclk_delay > 3) {
++                      pr_err("Invalid or missing txclk-delay\n");
++                      return -EINVAL;
++              }
++
++              val |= QCA8K_PORT_PAD_RGMII_TX_DELAY(rgmii_txclk_delay) |
++                     QCA8K_PORT_PAD_RGMII_TX_DELAY_EN;
++      }
+       /* Configure a port to be directly connected to an external
+        * PHY or MAC.
+        */
+-      switch (mode) {
++      switch (phy_mode) {
+       case PHY_INTERFACE_MODE_RGMII:
+-              qca8k_write(priv, reg,
+-                          QCA8K_PORT_PAD_RGMII_EN |
+-                          QCA8K_PORT_PAD_RGMII_TX_DELAY(3) |
+-                          QCA8K_PORT_PAD_RGMII_RX_DELAY(3));
+-
+-              /* According to the datasheet, RGMII delay is enabled through
+-               * PORT5_PAD_CTRL for all ports, rather than individual port
+-               * registers
+-               */
+-              qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL,
+-                          QCA8K_PORT_PAD_RGMII_RX_DELAY_EN);
++      case PHY_INTERFACE_MODE_RGMII_ID:
++      case PHY_INTERFACE_MODE_RGMII_RXID:
++      case PHY_INTERFACE_MODE_RGMII_TXID:
++              val |= QCA8K_PORT_PAD_RGMII_EN;
+               break;
+       case PHY_INTERFACE_MODE_SGMII:
+-              qca8k_write(priv, reg, QCA8K_PORT_PAD_SGMII_EN);
++              if (port != 5)
++                      val |= QCA8K_PORT_PAD_SGMII_EN;
++              else {
++                      pr_err("Port 5 does not support SGMII\n");
++                      return -EINVAL;
++              }
++              break;
++      case PHY_INTERFACE_MODE_INTERNAL:
+               break;
+       default:
+-              pr_err("xMII mode %d not supported\n", mode);
++              pr_err("xMII mode %d not supported\n", phy_mode);
+               return -EINVAL;
+       }
++      qca8k_write(priv, reg, val);
+       return 0;
+ }
+@@ -471,17 +606,71 @@ qca8k_port_set_status(struct qca8k_priv
+       if (port > 0 && port < 6)
+               mask |= QCA8K_PORT_STATUS_LINK_AUTO;
+-      if (enable)
++      if (enable) {
++              /* hw limitation: if there's traffic when the port
++               * gets configured, the port may not work properly
++               * (no rx!). Currently, we need to disable it completely
++               * first.
++               */
++              u32 val;
++
++              val = qca8k_read(priv, QCA8K_REG_PORT_STATUS(port));
++              qca8k_write(priv, QCA8K_REG_PORT_STATUS(port), 0);
++              msleep(100);
++              val |= QCA8K_PORT_STATUS_LINK_FLOW_CONTROL;
++              qca8k_write(priv, QCA8K_REG_PORT_STATUS(port), val);
+               qca8k_reg_set(priv, QCA8K_REG_PORT_STATUS(port), mask);
+-      else
++      } else
+               qca8k_reg_clear(priv, QCA8K_REG_PORT_STATUS(port), mask);
+ }
++static void
++qca8327_phy_fixup(struct qca8k_priv *priv, int phy)
++{
++      switch (priv->chip_rev) {
++      case 1:
++              /* For 100M waveform */
++              qca8k_phy_dbg_write(priv, phy, 0, 0x02ea);
++              /* Turn on Gigabit clock */
++              qca8k_phy_dbg_write(priv, phy, 0x3d, 0x68a0);
++              break;
++
++      case 2:
++              qca8k_phy_mmd_write(priv, phy, 0x7, 0x3c, 0x0);
++              /* fallthrough */
++      case 4:
++              qca8k_phy_mmd_write(priv, phy, 0x3, 0x800d, 0x803f);
++              qca8k_phy_dbg_write(priv, phy, 0x3d, 0x6860);
++              qca8k_phy_dbg_write(priv, phy, 0x5, 0x2c46);
++              qca8k_phy_dbg_write(priv, phy, 0x3c, 0x6000);
++              break;
++      }
++}
++
++static int
++qca8k_to_real_phy(struct dsa_switch *ds, int phy)
++{
++      struct device_node *phy_dn, *port_dn;
++      int id;
++
++      port_dn = ds->ports[phy].dn;
++      if (!port_dn)
++              return -EINVAL;
++
++      phy_dn = of_parse_phandle(port_dn, "phy-handle", 0);
++      if (!phy_dn)
++              return -ENODEV;
++
++      id = of_mdio_parse_addr(ds->dev, phy_dn);
++      of_node_put(phy_dn);
++      return id;
++}
++
+ static int
+ qca8k_setup(struct dsa_switch *ds)
+ {
+       struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+-      int ret, i, phy_mode = -1;
++      int ret, i;
+       u32 mask;
+       /* Make sure that port 0 is the cpu port */
+@@ -498,13 +687,20 @@ qca8k_setup(struct dsa_switch *ds)
+       if (IS_ERR(priv->regmap))
+               pr_warn("regmap initialization failed");
+-      /* Initialize CPU port pad mode (xMII type, delays...) */
+-      phy_mode = of_get_phy_mode(ds->ports[QCA8K_CPU_PORT].dn);
+-      if (phy_mode < 0) {
+-              pr_err("Can't find phy-mode for master device\n");
+-              return phy_mode;
++      if (of_device_is_compatible(priv->dev->of_node, "qca,qca8327")) {
++              /* Enables the MAC interface configuration for the 148-pin
++               * package. All qca8327 are 148-pin */
++              qca8k_write(priv, QCA8K_REG_PWS_REG,
++                      QCA8K_PWS_REG_8327_PACKAGE148_EN);
+       }
+-      ret = qca8k_set_pad_ctrl(priv, QCA8K_CPU_PORT, phy_mode);
++
++      ret = qca8k_set_pad_ctrl(priv, QCA8K_CPU_PORT);
++      if (ret < 0)
++              return ret;
++      ret = qca8k_set_pad_ctrl(priv, QCA8K_MAC5_PORT);
++      if (ret < 0)
++              return ret;
++      ret = qca8k_set_pad_ctrl(priv, QCA8K_MAC6_PORT);
+       if (ret < 0)
+               return ret;
+@@ -530,6 +726,12 @@ qca8k_setup(struct dsa_switch *ds)
+               qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
+                         QCA8K_PORT_LOOKUP_MEMBER, 0);
++      /* Disable VLAN by default on all ports */
++      for (i = 0; i < QCA8K_NUM_PORTS; i++)
++              qca8k_reg_clear(priv,
++                              QCA8K_PORT_LOOKUP_CTRL(i),
++                              QCA8K_PORT_LOOKUP_VLAN_MODE_MASK);
++
+       /* Disable MAC by default on all user ports */
+       for (i = 1; i < QCA8K_NUM_PORTS; i++)
+               if (dsa_is_user_port(ds, i))
+@@ -542,6 +744,10 @@ qca8k_setup(struct dsa_switch *ds)
+                   BIT(0) << QCA8K_GLOBAL_FW_CTRL1_MC_DP_S |
+                   BIT(0) << QCA8K_GLOBAL_FW_CTRL1_UC_DP_S);
++      /* enable jumbo frames */
++      qca8k_rmw(priv, QCA8K_REG_MAX_FRAME_SIZE,
++                QCA8K_MAX_FRAME_SIZE_MTU, 9018 + 8 + 2);
++
+       /* Setup connection between CPU port & user ports */
+       for (i = 0; i < DSA_MAX_PORTS; i++) {
+               /* CPU port gets connected to all user ports of the switch */
+@@ -573,6 +779,12 @@ qca8k_setup(struct dsa_switch *ds)
+               }
+       }
++      if (of_device_is_compatible(priv->dev->of_node, "qca,qca8327")) {
++              for (i = 0; i < DSA_MAX_PORTS; i++)
++                      if (dsa_is_user_port(ds, i))
++                              qca8327_phy_fixup(priv, qca8k_to_real_phy(ds, i));
++      }
++
+       /* Flush the FDB table */
+       qca8k_fdb_flush(priv);
+@@ -910,10 +1130,12 @@ qca8k_sw_probe(struct mdio_device *mdiod
+       /* read the switches ID register */
+       id = qca8k_read(priv, QCA8K_REG_MASK_CTRL);
+-      id >>= QCA8K_MASK_CTRL_ID_S;
+-      id &= QCA8K_MASK_CTRL_ID_M;
+-      if (id != QCA8K_ID_QCA8337)
+-              return -ENODEV;
++      priv->chip_rev = (id & QCA8K_MASK_CTRL_REVISION_ID_M);
++      priv->chip_id = (id & QCA8K_MASK_CTRL_DEVICE_ID_M) >>
++              QCA8K_MASK_CTRL_DEVICE_ID_S;
++
++      pr_info("qca8k: chip id:%d revision:%d\n",
++              priv->chip_id, priv->chip_rev);
+       priv->ds = dsa_switch_alloc(&mdiodev->dev, DSA_MAX_PORTS);
+       if (!priv->ds)
+@@ -980,6 +1202,7 @@ static SIMPLE_DEV_PM_OPS(qca8k_pm_ops,
+ static const struct of_device_id qca8k_of_match[] = {
+       { .compatible = "qca,qca8334" },
+       { .compatible = "qca,qca8337" },
++      { .compatible = "qca,qca8327" },
+       { /* sentinel */ },
+ };