generic: ar8216: add AR8337N switch support
[openwrt/svn-archive/archive.git] / target / linux / generic / files / drivers / net / phy / ar8216.c
index d4ef7bd195af67b3f5d7cb2158995cd51b84eb5d..02fa401066ae4c198e124c5056a815737e3dedfb 100644 (file)
@@ -33,6 +33,8 @@
 #include <linux/lockdep.h>
 #include <linux/ar8216_platform.h>
 #include <linux/workqueue.h>
+#include <linux/of_device.h>
+
 #include "ar8216.h"
 
 /* size of the vlan table */
@@ -52,6 +54,7 @@ enum {
        AR8XXX_VER_AR8236 = 0x03,
        AR8XXX_VER_AR8316 = 0x10,
        AR8XXX_VER_AR8327 = 0x12,
+       AR8XXX_VER_AR8337 = 0x13,
 };
 
 struct ar8xxx_mib_desc {
@@ -86,8 +89,12 @@ struct ar8xxx_priv {
        struct switch_dev dev;
        struct mii_bus *mii_bus;
        struct phy_device *phy;
+
        u32 (*read)(struct ar8xxx_priv *priv, int reg);
        void (*write)(struct ar8xxx_priv *priv, int reg, u32 val);
+
+       int (*get_port_link)(unsigned port);
+
        const struct net_device_ops *ndo_old;
        struct net_device_ops ndo;
        struct mutex reg_mutex;
@@ -118,6 +125,12 @@ struct ar8xxx_priv {
        u8 vlan_table[AR8X16_MAX_VLANS];
        u8 vlan_tagged;
        u16 pvid[AR8X16_MAX_PORTS];
+
+       /* mirroring */
+       bool mirror_rx;
+       bool mirror_tx;
+       int source_port;
+       int monitor_port;
 };
 
 #define MIB_DESC(_s , _o, _n)  \
@@ -248,6 +261,11 @@ static inline bool chip_is_ar8327(struct ar8xxx_priv *priv)
        return priv->chip_ver == AR8XXX_VER_AR8327;
 }
 
+static inline bool chip_is_ar8337(struct ar8xxx_priv *priv)
+{
+       return priv->chip_ver == AR8XXX_VER_AR8337;
+}
+
 static inline void
 split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page)
 {
@@ -385,7 +403,7 @@ ar8xxx_mib_op(struct ar8xxx_priv *priv, u32 op)
 
        lockdep_assert_held(&priv->mib_lock);
 
-       if (chip_is_ar8327(priv))
+       if (chip_is_ar8327(priv) || chip_is_ar8337(priv))
                mib_func = AR8327_REG_MIB_FUNC;
        else
                mib_func = AR8216_REG_MIB_FUNC;
@@ -429,7 +447,7 @@ ar8xxx_mib_fetch_port_stat(struct ar8xxx_priv *priv, int port, bool flush)
 
        lockdep_assert_held(&priv->mib_lock);
 
-       if (chip_is_ar8327(priv))
+       if (chip_is_ar8327(priv) || chip_is_ar8337(priv))
                base = AR8327_REG_PORT_STATS_BASE(port);
        else if (chip_is_ar8236(priv) ||
                 chip_is_ar8316(priv))
@@ -472,12 +490,21 @@ ar8216_read_port_link(struct ar8xxx_priv *priv, int port,
        link->aneg = !!(status & AR8216_PORT_STATUS_LINK_AUTO);
        if (link->aneg) {
                link->link = !!(status & AR8216_PORT_STATUS_LINK_UP);
-               if (!link->link)
-                       return;
        } else {
                link->link = true;
+
+               if (priv->get_port_link) {
+                       int err;
+
+                       err = priv->get_port_link(port);
+                       if (err >= 0)
+                               link->link = !!err;
+               }
        }
 
+       if (!link->link)
+               return;
+
        link->duplex = !!(status & AR8216_PORT_STATUS_DUPLEX);
        link->tx_flow = !!(status & AR8216_PORT_STATUS_TXFLOW);
        link->rx_flow = !!(status & AR8216_PORT_STATUS_RXFLOW);
@@ -1050,12 +1077,17 @@ ar8327_hw_config_pdata(struct ar8xxx_priv *priv,
        if (!pdata)
                return -EINVAL;
 
+       priv->get_port_link = pdata->get_port_link;
+
        data = &priv->chip_data.ar8327;
 
        data->port0_status = ar8327_get_port_init_status(&pdata->port0_cfg);
        data->port6_status = ar8327_get_port_init_status(&pdata->port6_cfg);
 
        t = ar8327_get_pad_cfg(pdata->pad0_cfg);
+       if (chip_is_ar8337(priv))
+               t |= AR8337_PAD_MAC06_EXCHANGE_EN;
+
        priv->write(priv, AR8327_REG_PAD0_MODE, t);
        t = ar8327_get_pad_cfg(pdata->pad5_cfg);
        priv->write(priv, AR8327_REG_PAD5_MODE, t);
@@ -1076,16 +1108,79 @@ ar8327_hw_config_pdata(struct ar8xxx_priv *priv,
                priv->write(priv, AR8327_REG_LED_CTRL1, led_cfg->led_ctrl1);
                priv->write(priv, AR8327_REG_LED_CTRL2, led_cfg->led_ctrl2);
                priv->write(priv, AR8327_REG_LED_CTRL3, led_cfg->led_ctrl3);
+
+               if (new_pos != pos)
+                       new_pos |= AR8327_POWER_ON_STRIP_POWER_ON_SEL;
        }
 
-       if (new_pos != pos) {
-               new_pos |= AR8327_POWER_ON_STRIP_POWER_ON_SEL;
-               priv->write(priv, AR8327_REG_POWER_ON_STRIP, new_pos);
+       if (pdata->sgmii_cfg) {
+               t = pdata->sgmii_cfg->sgmii_ctrl;
+               if (priv->chip_rev == 1)
+                       t |= AR8327_SGMII_CTRL_EN_PLL |
+                            AR8327_SGMII_CTRL_EN_RX |
+                            AR8327_SGMII_CTRL_EN_TX;
+               else
+                       t &= ~(AR8327_SGMII_CTRL_EN_PLL |
+                              AR8327_SGMII_CTRL_EN_RX |
+                              AR8327_SGMII_CTRL_EN_TX);
+
+               priv->write(priv, AR8327_REG_SGMII_CTRL, t);
+
+               if (pdata->sgmii_cfg->serdes_aen)
+                       new_pos &= ~AR8327_POWER_ON_STRIP_SERDES_AEN;
+               else
+                       new_pos |= AR8327_POWER_ON_STRIP_SERDES_AEN;
        }
 
+       priv->write(priv, AR8327_REG_POWER_ON_STRIP, new_pos);
+
        return 0;
 }
 
+#ifdef CONFIG_OF
+static int
+ar8327_hw_config_of(struct ar8xxx_priv *priv, struct device_node *np)
+{
+       const __be32 *paddr;
+       int len;
+       int i;
+
+       paddr = of_get_property(np, "qca,ar8327-initvals", &len);
+       if (!paddr || len < (2 * sizeof(*paddr)))
+               return -EINVAL;
+
+       len /= sizeof(*paddr);
+
+       for (i = 0; i < len - 1; i += 2) {
+               u32 reg;
+               u32 val;
+
+               reg = be32_to_cpup(paddr + i);
+               val = be32_to_cpup(paddr + i + 1);
+
+               switch (reg) {
+               case AR8327_REG_PORT_STATUS(0):
+                       priv->chip_data.ar8327.port0_status = val;
+                       break;
+               case AR8327_REG_PORT_STATUS(6):
+                       priv->chip_data.ar8327.port6_status = val;
+                       break;
+               default:
+                       priv->write(priv, reg, val);
+                       break;
+               }
+       }
+
+       return 0;
+}
+#else
+static inline int
+ar8327_hw_config_of(struct ar8xxx_priv *priv, struct device_node *np)
+{
+       return -EINVAL;
+}
+#endif
+
 static int
 ar8327_hw_init(struct ar8xxx_priv *priv)
 {
@@ -1093,7 +1188,12 @@ ar8327_hw_init(struct ar8xxx_priv *priv)
        int ret;
        int i;
 
-       ret = ar8327_hw_config_pdata(priv, priv->phy->dev.platform_data);
+       if (priv->phy->dev.of_node)
+               ret = ar8327_hw_config_of(priv, priv->phy->dev.of_node);
+       else
+               ret = ar8327_hw_config_pdata(priv,
+                                            priv->phy->dev.platform_data);
+
        if (ret)
                return ret;
 
@@ -1406,6 +1506,98 @@ ar8xxx_sw_set_ports(struct switch_dev *dev, struct switch_val *val)
        return 0;
 }
 
+static void
+ar8327_set_mirror_regs(struct ar8xxx_priv *priv)
+{
+       int port;
+
+       /* reset all mirror registers */
+       ar8xxx_rmw(priv, AR8327_REG_FWD_CTRL0,
+                  AR8327_FWD_CTRL0_MIRROR_PORT,
+                  (0xF << AR8327_FWD_CTRL0_MIRROR_PORT_S));
+       for (port = 0; port < AR8327_NUM_PORTS; port++) {
+               ar8xxx_rmw(priv, AR8327_REG_PORT_LOOKUP(port),
+                          AR8327_PORT_LOOKUP_ING_MIRROR_EN,
+                          0);
+
+               ar8xxx_rmw(priv, AR8327_REG_PORT_HOL_CTRL1(port),
+                          AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN,
+                          0);
+       }
+
+       /* now enable mirroring if necessary */
+       if (priv->source_port >= AR8327_NUM_PORTS ||
+           priv->monitor_port >= AR8327_NUM_PORTS ||
+           priv->source_port == priv->monitor_port) {
+               return;
+       }
+
+       ar8xxx_rmw(priv, AR8327_REG_FWD_CTRL0,
+                  AR8327_FWD_CTRL0_MIRROR_PORT,
+                  (priv->monitor_port << AR8327_FWD_CTRL0_MIRROR_PORT_S));
+
+       if (priv->mirror_rx)
+               ar8xxx_rmw(priv, AR8327_REG_PORT_LOOKUP(priv->source_port),
+                          AR8327_PORT_LOOKUP_ING_MIRROR_EN,
+                          AR8327_PORT_LOOKUP_ING_MIRROR_EN);
+
+       if (priv->mirror_tx)
+               ar8xxx_rmw(priv, AR8327_REG_PORT_HOL_CTRL1(priv->source_port),
+                          AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN,
+                          AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN);
+}
+
+static void
+ar8216_set_mirror_regs(struct ar8xxx_priv *priv)
+{
+       int port;
+
+       /* reset all mirror registers */
+       ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CPUPORT,
+                  AR8216_GLOBAL_CPUPORT_MIRROR_PORT,
+                  (0xF << AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S));
+       for (port = 0; port < AR8216_NUM_PORTS; port++) {
+               ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port),
+                          AR8216_PORT_CTRL_MIRROR_RX,
+                          0);
+
+               ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port),
+                          AR8216_PORT_CTRL_MIRROR_TX,
+                          0);
+       }
+
+       /* now enable mirroring if necessary */
+       if (priv->source_port >= AR8216_NUM_PORTS ||
+           priv->monitor_port >= AR8216_NUM_PORTS ||
+           priv->source_port == priv->monitor_port) {
+               return;
+       }
+
+       ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CPUPORT,
+                  AR8216_GLOBAL_CPUPORT_MIRROR_PORT,
+                  (priv->monitor_port << AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S));
+
+       if (priv->mirror_rx)
+               ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(priv->source_port),
+                          AR8216_PORT_CTRL_MIRROR_RX,
+                          AR8216_PORT_CTRL_MIRROR_RX);
+
+       if (priv->mirror_tx)
+               ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(priv->source_port),
+                          AR8216_PORT_CTRL_MIRROR_TX,
+                          AR8216_PORT_CTRL_MIRROR_TX);
+}
+
+static void
+ar8xxx_set_mirror_regs(struct ar8xxx_priv *priv)
+{
+       if (chip_is_ar8327(priv) || chip_is_ar8337(priv)) {
+               ar8327_set_mirror_regs(priv);
+       } else {
+               ar8216_set_mirror_regs(priv);
+       }
+}
+
 static int
 ar8xxx_sw_hw_apply(struct switch_dev *dev)
 {
@@ -1469,6 +1661,9 @@ ar8xxx_sw_hw_apply(struct switch_dev *dev)
                priv->chip->setup_port(priv, i, egress, ingress, portmask[i],
                                       pvid);
        }
+
+       ar8xxx_set_mirror_regs(priv);
+
        mutex_unlock(&priv->reg_mutex);
        return 0;
 }
@@ -1490,7 +1685,13 @@ ar8xxx_sw_reset_switch(struct switch_dev *dev)
        for (i = 0; i < dev->ports; i++)
                priv->chip->init_port(priv, i);
 
+       priv->mirror_rx = false;
+       priv->mirror_tx = false;
+       priv->source_port = 0;
+       priv->monitor_port = 0;
+
        priv->chip->init_globals(priv);
+
        mutex_unlock(&priv->reg_mutex);
 
        return ar8xxx_sw_hw_apply(dev);
@@ -1524,6 +1725,106 @@ unlock:
        return ret;
 }
 
+static int
+ar8xxx_sw_set_mirror_rx_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+       mutex_lock(&priv->reg_mutex);
+       priv->mirror_rx = !!val->value.i;
+       ar8xxx_set_mirror_regs(priv);
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+static int
+ar8xxx_sw_get_mirror_rx_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       val->value.i = priv->mirror_rx;
+       return 0;
+}
+
+static int
+ar8xxx_sw_set_mirror_tx_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+       mutex_lock(&priv->reg_mutex);
+       priv->mirror_tx = !!val->value.i;
+       ar8xxx_set_mirror_regs(priv);
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+static int
+ar8xxx_sw_get_mirror_tx_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       val->value.i = priv->mirror_tx;
+       return 0;
+}
+
+static int
+ar8xxx_sw_set_mirror_monitor_port(struct switch_dev *dev,
+                                 const struct switch_attr *attr,
+                                 struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+       mutex_lock(&priv->reg_mutex);
+       priv->monitor_port = val->value.i;
+       ar8xxx_set_mirror_regs(priv);
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+static int
+ar8xxx_sw_get_mirror_monitor_port(struct switch_dev *dev,
+                                 const struct switch_attr *attr,
+                                 struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       val->value.i = priv->monitor_port;
+       return 0;
+}
+
+static int
+ar8xxx_sw_set_mirror_source_port(struct switch_dev *dev,
+                                const struct switch_attr *attr,
+                                struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+       mutex_lock(&priv->reg_mutex);
+       priv->source_port = val->value.i;
+       ar8xxx_set_mirror_regs(priv);
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+static int
+ar8xxx_sw_get_mirror_source_port(struct switch_dev *dev,
+                                const struct switch_attr *attr,
+                                struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       val->value.i = priv->source_port;
+       return 0;
+}
+
 static int
 ar8xxx_sw_set_port_reset_mib(struct switch_dev *dev,
                             const struct switch_attr *attr,
@@ -1617,7 +1918,87 @@ static struct switch_attr ar8xxx_sw_attr_globals[] = {
                .description = "Reset all MIB counters",
                .set = ar8xxx_sw_set_reset_mibs,
        },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_mirror_rx",
+               .description = "Enable mirroring of RX packets",
+               .set = ar8xxx_sw_set_mirror_rx_enable,
+               .get = ar8xxx_sw_get_mirror_rx_enable,
+               .max = 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_mirror_tx",
+               .description = "Enable mirroring of TX packets",
+               .set = ar8xxx_sw_set_mirror_tx_enable,
+               .get = ar8xxx_sw_get_mirror_tx_enable,
+               .max = 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "mirror_monitor_port",
+               .description = "Mirror monitor port",
+               .set = ar8xxx_sw_set_mirror_monitor_port,
+               .get = ar8xxx_sw_get_mirror_monitor_port,
+               .max = AR8216_NUM_PORTS - 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "mirror_source_port",
+               .description = "Mirror source port",
+               .set = ar8xxx_sw_set_mirror_source_port,
+               .get = ar8xxx_sw_get_mirror_source_port,
+               .max = AR8216_NUM_PORTS - 1
+       },
+};
 
+static struct switch_attr ar8327_sw_attr_globals[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable VLAN mode",
+               .set = ar8xxx_sw_set_vlan,
+               .get = ar8xxx_sw_get_vlan,
+               .max = 1
+       },
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mibs",
+               .description = "Reset all MIB counters",
+               .set = ar8xxx_sw_set_reset_mibs,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_mirror_rx",
+               .description = "Enable mirroring of RX packets",
+               .set = ar8xxx_sw_set_mirror_rx_enable,
+               .get = ar8xxx_sw_get_mirror_rx_enable,
+               .max = 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_mirror_tx",
+               .description = "Enable mirroring of TX packets",
+               .set = ar8xxx_sw_set_mirror_tx_enable,
+               .get = ar8xxx_sw_get_mirror_tx_enable,
+               .max = 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "mirror_monitor_port",
+               .description = "Mirror monitor port",
+               .set = ar8xxx_sw_set_mirror_monitor_port,
+               .get = ar8xxx_sw_get_mirror_monitor_port,
+               .max = AR8327_NUM_PORTS - 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "mirror_source_port",
+               .description = "Mirror source port",
+               .set = ar8xxx_sw_set_mirror_source_port,
+               .get = ar8xxx_sw_get_mirror_source_port,
+               .max = AR8327_NUM_PORTS - 1
+       },
 };
 
 static struct switch_attr ar8xxx_sw_attr_port[] = {
@@ -1669,6 +2050,28 @@ static const struct switch_dev_ops ar8xxx_sw_ops = {
        .get_port_link = ar8xxx_sw_get_port_link,
 };
 
+static const struct switch_dev_ops ar8327_sw_ops = {
+       .attr_global = {
+               .attr = ar8327_sw_attr_globals,
+               .n_attr = ARRAY_SIZE(ar8327_sw_attr_globals),
+       },
+       .attr_port = {
+               .attr = ar8xxx_sw_attr_port,
+               .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_port),
+       },
+       .attr_vlan = {
+               .attr = ar8xxx_sw_attr_vlan,
+               .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_vlan),
+       },
+       .get_port_pvid = ar8xxx_sw_get_pvid,
+       .set_port_pvid = ar8xxx_sw_set_pvid,
+       .get_vlan_ports = ar8xxx_sw_get_ports,
+       .set_vlan_ports = ar8xxx_sw_set_ports,
+       .apply_config = ar8xxx_sw_hw_apply,
+       .reset_switch = ar8xxx_sw_reset_switch,
+       .get_port_link = ar8xxx_sw_get_port_link,
+};
+
 static int
 ar8xxx_id_chip(struct ar8xxx_priv *priv)
 {
@@ -1710,6 +2113,10 @@ ar8xxx_id_chip(struct ar8xxx_priv *priv)
                priv->mii_lo_first = true;
                priv->chip = &ar8327_chip;
                break;
+       case AR8XXX_VER_AR8337:
+               priv->mii_lo_first = true;
+               priv->chip = &ar8327_chip;
+               break;
        default:
                pr_err("ar8216: Unknown Atheros device [ver=%d, rev=%d]\n",
                       priv->chip_ver, priv->chip_rev);
@@ -1849,6 +2256,12 @@ ar8xxx_probe_switch(struct ar8xxx_priv *priv)
                swdev->name = "Atheros AR8327";
                swdev->vlans = AR8X16_MAX_VLANS;
                swdev->ports = AR8327_NUM_PORTS;
+               swdev->ops = &ar8327_sw_ops;
+       } else if (chip_is_ar8337(priv)) {
+               swdev->name = "Atheros AR8337";
+               swdev->vlans = AR8X16_MAX_VLANS;
+               swdev->ports = AR8327_NUM_PORTS;
+               swdev->ops = &ar8327_sw_ops;
        } else {
                swdev->name = "Atheros AR8216";
                swdev->vlans = AR8216_NUM_VLANS;
@@ -1862,6 +2275,28 @@ ar8xxx_probe_switch(struct ar8xxx_priv *priv)
        return 0;
 }
 
+static int
+ar8xxx_start(struct ar8xxx_priv *priv)
+{
+       int ret;
+
+       priv->init = true;
+
+       ret = priv->chip->hw_init(priv);
+       if (ret)
+               return ret;
+
+       ret = ar8xxx_sw_reset_switch(&priv->dev);
+       if (ret)
+               return ret;
+
+       priv->init = false;
+
+       ar8xxx_mib_start(priv);
+
+       return 0;
+}
+
 static int
 ar8xxx_phy_config_init(struct phy_device *phydev)
 {
@@ -1872,6 +2307,9 @@ ar8xxx_phy_config_init(struct phy_device *phydev)
        if (WARN_ON(!priv))
                return -ENODEV;
 
+       if (chip_is_ar8327(priv) || chip_is_ar8337(priv))
+               return 0;
+
        priv->phy = phydev;
 
        if (phydev->addr != 0) {
@@ -1887,13 +2325,7 @@ ar8xxx_phy_config_init(struct phy_device *phydev)
                return 0;
        }
 
-       priv->init = true;
-
-       ret = priv->chip->hw_init(priv);
-       if (ret)
-               return ret;
-
-       ret = ar8xxx_sw_reset_switch(&priv->dev);
+       ret = ar8xxx_start(priv);
        if (ret)
                return ret;
 
@@ -1905,10 +2337,6 @@ ar8xxx_phy_config_init(struct phy_device *phydev)
                dev->eth_mangle_tx = ar8216_mangle_tx;
        }
 
-       priv->init = false;
-
-       ar8xxx_mib_start(priv);
-
        return 0;
 }
 
@@ -1965,6 +2393,8 @@ ar8xxx_phy_config_aneg(struct phy_device *phydev)
 
 static const u32 ar8xxx_phy_ids[] = {
        0x004dd033,
+       0x004dd034, /* AR8327 */
+       0x004dd036, /* AR8337 */
        0x004dd041,
        0x004dd042,
 };
@@ -2036,10 +2466,13 @@ ar8xxx_phy_probe(struct phy_device *phydev)
        if (ret)
                goto free_priv;
 
-       pr_info("%s: %s switch registered on %s\n",
-               swdev->devname, swdev->name, dev_name(&priv->mii_bus->dev));
+       pr_info("%s: %s rev. %u switch registered on %s\n",
+               swdev->devname, swdev->name, priv->chip_rev,
+               dev_name(&priv->mii_bus->dev));
 
 found:
+       priv->use_count++;
+
        if (phydev->addr == 0) {
                if (ar8xxx_has_gige(priv)) {
                        phydev->supported = SUPPORTED_1000baseT_Full;
@@ -2048,6 +2481,14 @@ found:
                        phydev->supported = SUPPORTED_100baseT_Full;
                        phydev->advertising = ADVERTISED_100baseT_Full;
                }
+
+               if (chip_is_ar8327(priv) || chip_is_ar8337(priv)) {
+                       priv->phy = phydev;
+
+                       ret = ar8xxx_start(priv);
+                       if (ret)
+                               goto err_unregister_switch;
+               }
        } else {
                if (ar8xxx_has_gige(priv)) {
                        phydev->supported |= SUPPORTED_1000baseT_Full;
@@ -2056,7 +2497,6 @@ found:
        }
 
        phydev->priv = priv;
-       priv->use_count++;
 
        list_add(&priv->list, &ar8xxx_dev_list);
 
@@ -2064,6 +2504,12 @@ found:
 
        return 0;
 
+err_unregister_switch:
+       if (--priv->use_count)
+               goto unlock;
+
+       unregister_switch(&priv->dev);
+
 free_priv:
        ar8xxx_free(priv);
 unlock: