net: dsa: mv88e6xxx: Fix false positive lockdep splat
[cascardo/linux.git] / drivers / net / dsa / mv88e6xxx.c
index 9f0c2b9..56ecbe4 100644 (file)
 #include <net/dsa.h>
 #include "mv88e6xxx.h"
 
+/* MDIO bus access can be nested in the case of PHYs connected to the
+ * internal MDIO bus of the switch, which is accessed via MDIO bus of
+ * the Ethernet interface. Avoid lockdep false positives by using
+ * mutex_lock_nested().
+ */
+static int mv88e6xxx_mdiobus_read(struct mii_bus *bus, int addr, u32 regnum)
+{
+       int ret;
+
+       mutex_lock_nested(&bus->mdio_lock, SINGLE_DEPTH_NESTING);
+       ret = bus->read(bus, addr, regnum);
+       mutex_unlock(&bus->mdio_lock);
+
+       return ret;
+}
+
+static int mv88e6xxx_mdiobus_write(struct mii_bus *bus, int addr, u32 regnum,
+                                  u16 val)
+{
+       int ret;
+
+       mutex_lock_nested(&bus->mdio_lock, SINGLE_DEPTH_NESTING);
+       ret = bus->write(bus, addr, regnum, val);
+       mutex_unlock(&bus->mdio_lock);
+
+       return ret;
+}
+
 /* If the switch's ADDR[4:0] strap pins are strapped to zero, it will
  * use all 32 SMI bus addresses on its SMI bus, and all switch registers
  * will be directly accessible on some {device address,register address}
@@ -33,7 +61,7 @@ static int mv88e6xxx_reg_wait_ready(struct mii_bus *bus, int sw_addr)
        int i;
 
        for (i = 0; i < 16; i++) {
-               ret = mdiobus_read(bus, sw_addr, SMI_CMD);
+               ret = mv88e6xxx_mdiobus_read(bus, sw_addr, SMI_CMD);
                if (ret < 0)
                        return ret;
 
@@ -49,7 +77,7 @@ int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg)
        int ret;
 
        if (sw_addr == 0)
-               return mdiobus_read(bus, addr, reg);
+               return mv88e6xxx_mdiobus_read(bus, addr, reg);
 
        /* Wait for the bus to become free. */
        ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
@@ -57,8 +85,8 @@ int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg)
                return ret;
 
        /* Transmit the read command. */
-       ret = mdiobus_write(bus, sw_addr, SMI_CMD,
-                           SMI_CMD_OP_22_READ | (addr << 5) | reg);
+       ret = mv88e6xxx_mdiobus_write(bus, sw_addr, SMI_CMD,
+                                     SMI_CMD_OP_22_READ | (addr << 5) | reg);
        if (ret < 0)
                return ret;
 
@@ -68,7 +96,7 @@ int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg)
                return ret;
 
        /* Read the data. */
-       ret = mdiobus_read(bus, sw_addr, SMI_DATA);
+       ret = mv88e6xxx_mdiobus_read(bus, sw_addr, SMI_DATA);
        if (ret < 0)
                return ret;
 
@@ -112,7 +140,7 @@ int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr,
        int ret;
 
        if (sw_addr == 0)
-               return mdiobus_write(bus, addr, reg, val);
+               return mv88e6xxx_mdiobus_write(bus, addr, reg, val);
 
        /* Wait for the bus to become free. */
        ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
@@ -120,13 +148,13 @@ int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr,
                return ret;
 
        /* Transmit the data to write. */
-       ret = mdiobus_write(bus, sw_addr, SMI_DATA, val);
+       ret = mv88e6xxx_mdiobus_write(bus, sw_addr, SMI_DATA, val);
        if (ret < 0)
                return ret;
 
        /* Transmit the write command. */
-       ret = mdiobus_write(bus, sw_addr, SMI_CMD,
-                           SMI_CMD_OP_22_WRITE | (addr << 5) | reg);
+       ret = mv88e6xxx_mdiobus_write(bus, sw_addr, SMI_CMD,
+                                     SMI_CMD_OP_22_WRITE | (addr << 5) | reg);
        if (ret < 0)
                return ret;
 
@@ -165,24 +193,6 @@ int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val)
        return ret;
 }
 
-int mv88e6xxx_config_prio(struct dsa_switch *ds)
-{
-       /* Configure the IP ToS mapping registers. */
-       REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_0, 0x0000);
-       REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_1, 0x0000);
-       REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_2, 0x5555);
-       REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_3, 0x5555);
-       REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_4, 0xaaaa);
-       REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_5, 0xaaaa);
-       REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_6, 0xffff);
-       REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_7, 0xffff);
-
-       /* Configure the IEEE 802.1p priority mapping register. */
-       REG_WRITE(REG_GLOBAL, GLOBAL_IEEE_PRI, 0xfa41);
-
-       return 0;
-}
-
 int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr)
 {
        REG_WRITE(REG_GLOBAL, GLOBAL_MAC_01, (addr[0] << 8) | addr[1]);
@@ -217,20 +227,20 @@ int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr)
        return 0;
 }
 
-/* Must be called with phy mutex held */
+/* Must be called with SMI mutex held */
 static int _mv88e6xxx_phy_read(struct dsa_switch *ds, int addr, int regnum)
 {
        if (addr >= 0)
-               return mv88e6xxx_reg_read(ds, addr, regnum);
+               return _mv88e6xxx_reg_read(ds, addr, regnum);
        return 0xffff;
 }
 
-/* Must be called with phy mutex held */
+/* Must be called with SMI mutex held */
 static int _mv88e6xxx_phy_write(struct dsa_switch *ds, int addr, int regnum,
                                u16 val)
 {
        if (addr >= 0)
-               return mv88e6xxx_reg_write(ds, addr, regnum, val);
+               return _mv88e6xxx_reg_write(ds, addr, regnum, val);
        return 0;
 }
 
@@ -434,26 +444,113 @@ void mv88e6xxx_poll_link(struct dsa_switch *ds)
        }
 }
 
+static bool mv88e6xxx_6065_family(struct dsa_switch *ds)
+{
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+
+       switch (ps->id) {
+       case PORT_SWITCH_ID_6031:
+       case PORT_SWITCH_ID_6061:
+       case PORT_SWITCH_ID_6035:
+       case PORT_SWITCH_ID_6065:
+               return true;
+       }
+       return false;
+}
+
+static bool mv88e6xxx_6095_family(struct dsa_switch *ds)
+{
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+
+       switch (ps->id) {
+       case PORT_SWITCH_ID_6092:
+       case PORT_SWITCH_ID_6095:
+               return true;
+       }
+       return false;
+}
+
+static bool mv88e6xxx_6097_family(struct dsa_switch *ds)
+{
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+
+       switch (ps->id) {
+       case PORT_SWITCH_ID_6046:
+       case PORT_SWITCH_ID_6085:
+       case PORT_SWITCH_ID_6096:
+       case PORT_SWITCH_ID_6097:
+               return true;
+       }
+       return false;
+}
+
+static bool mv88e6xxx_6165_family(struct dsa_switch *ds)
+{
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+
+       switch (ps->id) {
+       case PORT_SWITCH_ID_6123:
+       case PORT_SWITCH_ID_6161:
+       case PORT_SWITCH_ID_6165:
+               return true;
+       }
+       return false;
+}
+
+static bool mv88e6xxx_6185_family(struct dsa_switch *ds)
+{
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+
+       switch (ps->id) {
+       case PORT_SWITCH_ID_6121:
+       case PORT_SWITCH_ID_6122:
+       case PORT_SWITCH_ID_6152:
+       case PORT_SWITCH_ID_6155:
+       case PORT_SWITCH_ID_6182:
+       case PORT_SWITCH_ID_6185:
+       case PORT_SWITCH_ID_6108:
+       case PORT_SWITCH_ID_6131:
+               return true;
+       }
+       return false;
+}
+
+static bool mv88e6xxx_6351_family(struct dsa_switch *ds)
+{
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+
+       switch (ps->id) {
+       case PORT_SWITCH_ID_6171:
+       case PORT_SWITCH_ID_6175:
+       case PORT_SWITCH_ID_6350:
+       case PORT_SWITCH_ID_6351:
+               return true;
+       }
+       return false;
+}
+
 static bool mv88e6xxx_6352_family(struct dsa_switch *ds)
 {
        struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
 
        switch (ps->id) {
-       case PORT_SWITCH_ID_6352:
        case PORT_SWITCH_ID_6172:
        case PORT_SWITCH_ID_6176:
+       case PORT_SWITCH_ID_6240:
+       case PORT_SWITCH_ID_6352:
                return true;
        }
        return false;
 }
 
-static int mv88e6xxx_stats_wait(struct dsa_switch *ds)
+/* Must be called with SMI mutex held */
+static int _mv88e6xxx_stats_wait(struct dsa_switch *ds)
 {
        int ret;
        int i;
 
        for (i = 0; i < 10; i++) {
-               ret = REG_READ(REG_GLOBAL, GLOBAL_STATS_OP);
+               ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_STATS_OP);
                if ((ret & GLOBAL_STATS_OP_BUSY) == 0)
                        return 0;
        }
@@ -461,7 +558,8 @@ static int mv88e6xxx_stats_wait(struct dsa_switch *ds)
        return -ETIMEDOUT;
 }
 
-static int mv88e6xxx_stats_snapshot(struct dsa_switch *ds, int port)
+/* Must be called with SMI mutex held */
+static int _mv88e6xxx_stats_snapshot(struct dsa_switch *ds, int port)
 {
        int ret;
 
@@ -469,42 +567,45 @@ static int mv88e6xxx_stats_snapshot(struct dsa_switch *ds, int port)
                port = (port + 1) << 5;
 
        /* Snapshot the hardware statistics counters for this port. */
-       REG_WRITE(REG_GLOBAL, GLOBAL_STATS_OP,
-                 GLOBAL_STATS_OP_CAPTURE_PORT |
-                 GLOBAL_STATS_OP_HIST_RX_TX | port);
+       ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_STATS_OP,
+                                  GLOBAL_STATS_OP_CAPTURE_PORT |
+                                  GLOBAL_STATS_OP_HIST_RX_TX | port);
+       if (ret < 0)
+               return ret;
 
        /* Wait for the snapshotting to complete. */
-       ret = mv88e6xxx_stats_wait(ds);
+       ret = _mv88e6xxx_stats_wait(ds);
        if (ret < 0)
                return ret;
 
        return 0;
 }
 
-static void mv88e6xxx_stats_read(struct dsa_switch *ds, int stat, u32 *val)
+/* Must be called with SMI mutex held */
+static void _mv88e6xxx_stats_read(struct dsa_switch *ds, int stat, u32 *val)
 {
        u32 _val;
        int ret;
 
        *val = 0;
 
-       ret = mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_STATS_OP,
-                                 GLOBAL_STATS_OP_READ_CAPTURED |
-                                 GLOBAL_STATS_OP_HIST_RX_TX | stat);
+       ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_STATS_OP,
+                                  GLOBAL_STATS_OP_READ_CAPTURED |
+                                  GLOBAL_STATS_OP_HIST_RX_TX | stat);
        if (ret < 0)
                return;
 
-       ret = mv88e6xxx_stats_wait(ds);
+       ret = _mv88e6xxx_stats_wait(ds);
        if (ret < 0)
                return;
 
-       ret = mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_STATS_COUNTER_32);
+       ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_STATS_COUNTER_32);
        if (ret < 0)
                return;
 
        _val = ret << 16;
 
-       ret = mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_STATS_COUNTER_01);
+       ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_STATS_COUNTER_01);
        if (ret < 0)
                return;
 
@@ -587,11 +688,11 @@ static void _mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
        int ret;
        int i;
 
-       mutex_lock(&ps->stats_mutex);
+       mutex_lock(&ps->smi_mutex);
 
-       ret = mv88e6xxx_stats_snapshot(ds, port);
+       ret = _mv88e6xxx_stats_snapshot(ds, port);
        if (ret < 0) {
-               mutex_unlock(&ps->stats_mutex);
+               mutex_unlock(&ps->smi_mutex);
                return;
        }
 
@@ -608,8 +709,8 @@ static void _mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
                                goto error;
                        low = ret;
                        if (s->sizeof_stat == 4) {
-                               ret = mv88e6xxx_reg_read(ds, REG_PORT(port),
-                                                        s->reg - 0x100 + 1);
+                               ret = _mv88e6xxx_reg_read(ds, REG_PORT(port),
+                                                         s->reg - 0x100 + 1);
                                if (ret < 0)
                                        goto error;
                                high = ret;
@@ -617,14 +718,14 @@ static void _mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
                        data[i] = (((u64)high) << 16) | low;
                        continue;
                }
-               mv88e6xxx_stats_read(ds, s->reg, &low);
+               _mv88e6xxx_stats_read(ds, s->reg, &low);
                if (s->sizeof_stat == 8)
-                       mv88e6xxx_stats_read(ds, s->reg + 1, &high);
+                       _mv88e6xxx_stats_read(ds, s->reg + 1, &high);
 
                data[i] = (((u64)high) << 32) | low;
        }
 error:
-       mutex_unlock(&ps->stats_mutex);
+       mutex_unlock(&ps->smi_mutex);
 }
 
 /* All the statistics in the table */
@@ -694,7 +795,7 @@ int  mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp)
 
        *temp = 0;
 
-       mutex_lock(&ps->phy_mutex);
+       mutex_lock(&ps->smi_mutex);
 
        ret = _mv88e6xxx_phy_write(ds, 0x0, 0x16, 0x6);
        if (ret < 0)
@@ -727,19 +828,23 @@ int  mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp)
 
 error:
        _mv88e6xxx_phy_write(ds, 0x0, 0x16, 0x0);
-       mutex_unlock(&ps->phy_mutex);
+       mutex_unlock(&ps->smi_mutex);
        return ret;
 }
 #endif /* CONFIG_NET_DSA_HWMON */
 
-static int mv88e6xxx_wait(struct dsa_switch *ds, int reg, int offset, u16 mask)
+/* Must be called with SMI lock held */
+static int _mv88e6xxx_wait(struct dsa_switch *ds, int reg, int offset,
+                          u16 mask)
 {
        unsigned long timeout = jiffies + HZ / 10;
 
        while (time_before(jiffies, timeout)) {
                int ret;
 
-               ret = REG_READ(reg, offset);
+               ret = _mv88e6xxx_reg_read(ds, reg, offset);
+               if (ret < 0)
+                       return ret;
                if (!(ret & mask))
                        return 0;
 
@@ -748,10 +853,22 @@ static int mv88e6xxx_wait(struct dsa_switch *ds, int reg, int offset, u16 mask)
        return -ETIMEDOUT;
 }
 
-int mv88e6xxx_phy_wait(struct dsa_switch *ds)
+static int mv88e6xxx_wait(struct dsa_switch *ds, int reg, int offset, u16 mask)
 {
-       return mv88e6xxx_wait(ds, REG_GLOBAL2, GLOBAL2_SMI_OP,
-                             GLOBAL2_SMI_OP_BUSY);
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+       int ret;
+
+       mutex_lock(&ps->smi_mutex);
+       ret = _mv88e6xxx_wait(ds, reg, offset, mask);
+       mutex_unlock(&ps->smi_mutex);
+
+       return ret;
+}
+
+static int _mv88e6xxx_phy_wait(struct dsa_switch *ds)
+{
+       return _mv88e6xxx_wait(ds, REG_GLOBAL2, GLOBAL2_SMI_OP,
+                              GLOBAL2_SMI_OP_BUSY);
 }
 
 int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds)
@@ -766,25 +883,6 @@ int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds)
                              GLOBAL2_EEPROM_OP_BUSY);
 }
 
-/* Must be called with SMI lock held */
-static int _mv88e6xxx_wait(struct dsa_switch *ds, int reg, int offset, u16 mask)
-{
-       unsigned long timeout = jiffies + HZ / 10;
-
-       while (time_before(jiffies, timeout)) {
-               int ret;
-
-               ret = _mv88e6xxx_reg_read(ds, reg, offset);
-               if (ret < 0)
-                       return ret;
-               if (!(ret & mask))
-                       return 0;
-
-               usleep_range(1000, 2000);
-       }
-       return -ETIMEDOUT;
-}
-
 /* Must be called with SMI lock held */
 static int _mv88e6xxx_atu_wait(struct dsa_switch *ds)
 {
@@ -792,31 +890,40 @@ static int _mv88e6xxx_atu_wait(struct dsa_switch *ds)
                               GLOBAL_ATU_OP_BUSY);
 }
 
-/* Must be called with phy mutex held */
+/* Must be called with SMI mutex held */
 static int _mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int addr,
                                        int regnum)
 {
        int ret;
 
-       REG_WRITE(REG_GLOBAL2, GLOBAL2_SMI_OP,
-                 GLOBAL2_SMI_OP_22_READ | (addr << 5) | regnum);
+       ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_SMI_OP,
+                                  GLOBAL2_SMI_OP_22_READ | (addr << 5) |
+                                  regnum);
+       if (ret < 0)
+               return ret;
 
-       ret = mv88e6xxx_phy_wait(ds);
+       ret = _mv88e6xxx_phy_wait(ds);
        if (ret < 0)
                return ret;
 
-       return REG_READ(REG_GLOBAL2, GLOBAL2_SMI_DATA);
+       return _mv88e6xxx_reg_read(ds, REG_GLOBAL2, GLOBAL2_SMI_DATA);
 }
 
-/* Must be called with phy mutex held */
+/* Must be called with SMI mutex held */
 static int _mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int addr,
                                         int regnum, u16 val)
 {
-       REG_WRITE(REG_GLOBAL2, GLOBAL2_SMI_DATA, val);
-       REG_WRITE(REG_GLOBAL2, GLOBAL2_SMI_OP,
-                 GLOBAL2_SMI_OP_22_WRITE | (addr << 5) | regnum);
+       int ret;
+
+       ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_SMI_DATA, val);
+       if (ret < 0)
+               return ret;
+
+       ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_SMI_OP,
+                                  GLOBAL2_SMI_OP_22_WRITE | (addr << 5) |
+                                  regnum);
 
-       return mv88e6xxx_phy_wait(ds);
+       return _mv88e6xxx_phy_wait(ds);
 }
 
 int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e)
@@ -824,7 +931,7 @@ int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e)
        struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
        int reg;
 
-       mutex_lock(&ps->phy_mutex);
+       mutex_lock(&ps->smi_mutex);
 
        reg = _mv88e6xxx_phy_read_indirect(ds, port, 16);
        if (reg < 0)
@@ -833,7 +940,7 @@ int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e)
        e->eee_enabled = !!(reg & 0x0200);
        e->tx_lpi_enabled = !!(reg & 0x0100);
 
-       reg = mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_STATUS);
+       reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_STATUS);
        if (reg < 0)
                goto out;
 
@@ -841,7 +948,7 @@ int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e)
        reg = 0;
 
 out:
-       mutex_unlock(&ps->phy_mutex);
+       mutex_unlock(&ps->smi_mutex);
        return reg;
 }
 
@@ -852,7 +959,7 @@ int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
        int reg;
        int ret;
 
-       mutex_lock(&ps->phy_mutex);
+       mutex_lock(&ps->smi_mutex);
 
        ret = _mv88e6xxx_phy_read_indirect(ds, port, 16);
        if (ret < 0)
@@ -866,7 +973,7 @@ int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
 
        ret = _mv88e6xxx_phy_write_indirect(ds, port, 16, reg);
 out:
-       mutex_unlock(&ps->phy_mutex);
+       mutex_unlock(&ps->smi_mutex);
 
        return ret;
 }
@@ -1241,18 +1348,216 @@ static void mv88e6xxx_bridge_work(struct work_struct *work)
        }
 }
 
-int mv88e6xxx_setup_port_common(struct dsa_switch *ds, int port)
+static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port)
 {
        struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
        int ret, fid;
+       u16 reg;
 
        mutex_lock(&ps->smi_mutex);
 
+       if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
+           mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
+           mv88e6xxx_6185_family(ds) || mv88e6xxx_6095_family(ds) ||
+           mv88e6xxx_6065_family(ds)) {
+               /* MAC Forcing register: don't force link, speed,
+                * duplex or flow control state to any particular
+                * values on physical ports, but force the CPU port
+                * and all DSA ports to their maximum bandwidth and
+                * full duplex.
+                */
+               reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_PCS_CTRL);
+               if (dsa_is_cpu_port(ds, port) ||
+                   ds->dsa_port_mask & (1 << port)) {
+                       reg |= PORT_PCS_CTRL_FORCE_LINK |
+                               PORT_PCS_CTRL_LINK_UP |
+                               PORT_PCS_CTRL_DUPLEX_FULL |
+                               PORT_PCS_CTRL_FORCE_DUPLEX;
+                       if (mv88e6xxx_6065_family(ds))
+                               reg |= PORT_PCS_CTRL_100;
+                       else
+                               reg |= PORT_PCS_CTRL_1000;
+               } else {
+                       reg |= PORT_PCS_CTRL_UNFORCED;
+               }
+
+               ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
+                                          PORT_PCS_CTRL, reg);
+               if (ret)
+                       goto abort;
+       }
+
+       /* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock,
+        * disable Header mode, enable IGMP/MLD snooping, disable VLAN
+        * tunneling, determine priority by looking at 802.1p and IP
+        * priority fields (IP prio has precedence), and set STP state
+        * to Forwarding.
+        *
+        * If this is the CPU link, use DSA or EDSA tagging depending
+        * on which tagging mode was configured.
+        *
+        * If this is a link to another switch, use DSA tagging mode.
+        *
+        * If this is the upstream port for this switch, enable
+        * forwarding of unknown unicasts and multicasts.
+        */
+       reg = 0;
+       if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
+           mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
+           mv88e6xxx_6095_family(ds) || mv88e6xxx_6065_family(ds) ||
+           mv88e6xxx_6185_family(ds))
+               reg = PORT_CONTROL_IGMP_MLD_SNOOP |
+               PORT_CONTROL_USE_TAG | PORT_CONTROL_USE_IP |
+               PORT_CONTROL_STATE_FORWARDING;
+       if (dsa_is_cpu_port(ds, port)) {
+               if (mv88e6xxx_6095_family(ds) || mv88e6xxx_6185_family(ds))
+                       reg |= PORT_CONTROL_DSA_TAG;
+               if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
+                   mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds)) {
+                       if (ds->dst->tag_protocol == DSA_TAG_PROTO_EDSA)
+                               reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA;
+                       else
+                               reg |= PORT_CONTROL_FRAME_MODE_DSA;
+               }
+
+               if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
+                   mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
+                   mv88e6xxx_6095_family(ds) || mv88e6xxx_6065_family(ds) ||
+                   mv88e6xxx_6185_family(ds)) {
+                       if (ds->dst->tag_protocol == DSA_TAG_PROTO_EDSA)
+                               reg |= PORT_CONTROL_EGRESS_ADD_TAG;
+               }
+       }
+       if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
+           mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
+           mv88e6xxx_6095_family(ds) || mv88e6xxx_6065_family(ds)) {
+               if (ds->dsa_port_mask & (1 << port))
+                       reg |= PORT_CONTROL_FRAME_MODE_DSA;
+               if (port == dsa_upstream_port(ds))
+                       reg |= PORT_CONTROL_FORWARD_UNKNOWN |
+                               PORT_CONTROL_FORWARD_UNKNOWN_MC;
+       }
+       if (reg) {
+               ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
+                                          PORT_CONTROL, reg);
+               if (ret)
+                       goto abort;
+       }
+
+       /* Port Control 2: don't force a good FCS, set the maximum
+        * frame size to 10240 bytes, don't let the switch add or
+        * strip 802.1q tags, don't discard tagged or untagged frames
+        * on this port, do a destination address lookup on all
+        * received packets as usual, disable ARP mirroring and don't
+        * send a copy of all transmitted/received frames on this port
+        * to the CPU.
+        */
+       reg = 0;
+       if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
+           mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
+           mv88e6xxx_6095_family(ds))
+               reg = PORT_CONTROL_2_MAP_DA;
+
+       if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
+           mv88e6xxx_6165_family(ds))
+               reg |= PORT_CONTROL_2_JUMBO_10240;
+
+       if (mv88e6xxx_6095_family(ds) || mv88e6xxx_6185_family(ds)) {
+               /* Set the upstream port this port should use */
+               reg |= dsa_upstream_port(ds);
+               /* enable forwarding of unknown multicast addresses to
+                * the upstream port
+                */
+               if (port == dsa_upstream_port(ds))
+                       reg |= PORT_CONTROL_2_FORWARD_UNKNOWN;
+       }
+
+       if (reg) {
+               ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
+                                          PORT_CONTROL_2, reg);
+               if (ret)
+                       goto abort;
+       }
+
+       /* Port Association Vector: when learning source addresses
+        * of packets, add the address to the address database using
+        * a port bitmap that has only the bit for this port set and
+        * the other bits clear.
+        */
+       ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_ASSOC_VECTOR,
+                                  1 << port);
+       if (ret)
+               goto abort;
+
+       /* Egress rate control 2: disable egress rate control. */
+       ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_RATE_CONTROL_2,
+                                  0x0000);
+       if (ret)
+               goto abort;
+
+       if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
+           mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds)) {
+               /* Do not limit the period of time that this port can
+                * be paused for by the remote end or the period of
+                * time that this port can pause the remote end.
+                */
+               ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
+                                          PORT_PAUSE_CTRL, 0x0000);
+               if (ret)
+                       goto abort;
+
+               /* Port ATU control: disable limiting the number of
+                * address database entries that this port is allowed
+                * to use.
+                */
+               ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
+                                          PORT_ATU_CONTROL, 0x0000);
+               /* Priority Override: disable DA, SA and VTU priority
+                * override.
+                */
+               ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
+                                          PORT_PRI_OVERRIDE, 0x0000);
+               if (ret)
+                       goto abort;
+
+               /* Port Ethertype: use the Ethertype DSA Ethertype
+                * value.
+                */
+               ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
+                                          PORT_ETH_TYPE, ETH_P_EDSA);
+               if (ret)
+                       goto abort;
+               /* Tag Remap: use an identity 802.1p prio -> switch
+                * prio mapping.
+                */
+               ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
+                                          PORT_TAG_REGMAP_0123, 0x3210);
+               if (ret)
+                       goto abort;
+
+               /* Tag Remap 2: use an identity 802.1p prio -> switch
+                * prio mapping.
+                */
+               ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
+                                          PORT_TAG_REGMAP_4567, 0x7654);
+               if (ret)
+                       goto abort;
+       }
+
+       if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
+           mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
+           mv88e6xxx_6185_family(ds) || mv88e6xxx_6095_family(ds)) {
+               /* Rate Control: disable ingress rate limiting. */
+               ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
+                                          PORT_RATE_CONTROL, 0x0001);
+               if (ret)
+                       goto abort;
+       }
+
        /* Port Control 1: disable trunking, disable sending
         * learning messages to this port.
         */
-       ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_DEFAULT_VLAN,
-                                  0x0000);
+       ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_CONTROL_1, 0x0000);
        if (ret)
                goto abort;
 
@@ -1275,19 +1580,32 @@ int mv88e6xxx_setup_port_common(struct dsa_switch *ds, int port)
        /* Default VLAN ID and priority: don't set a default VLAN
         * ID, and set the default packet priority to zero.
         */
-       ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), 0x07, 0x0000);
+       ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_DEFAULT_VLAN,
+                                  0x0000);
 abort:
        mutex_unlock(&ps->smi_mutex);
        return ret;
 }
 
+int mv88e6xxx_setup_ports(struct dsa_switch *ds)
+{
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+       int ret;
+       int i;
+
+       for (i = 0; i < ps->num_ports; i++) {
+               ret = mv88e6xxx_setup_port(ds, i);
+               if (ret < 0)
+                       return ret;
+       }
+       return 0;
+}
+
 int mv88e6xxx_setup_common(struct dsa_switch *ds)
 {
        struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
 
        mutex_init(&ps->smi_mutex);
-       mutex_init(&ps->stats_mutex);
-       mutex_init(&ps->phy_mutex);
 
        ps->id = REG_READ(REG_PORT(0), PORT_SWITCH_ID) & 0xfff0;
 
@@ -1298,6 +1616,104 @@ int mv88e6xxx_setup_common(struct dsa_switch *ds)
        return 0;
 }
 
+int mv88e6xxx_setup_global(struct dsa_switch *ds)
+{
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+       int i;
+
+       /* Set the default address aging time to 5 minutes, and
+        * enable address learn messages to be sent to all message
+        * ports.
+        */
+       REG_WRITE(REG_GLOBAL, GLOBAL_ATU_CONTROL,
+                 0x0140 | GLOBAL_ATU_CONTROL_LEARN2ALL);
+
+       /* Configure the IP ToS mapping registers. */
+       REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_0, 0x0000);
+       REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_1, 0x0000);
+       REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_2, 0x5555);
+       REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_3, 0x5555);
+       REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_4, 0xaaaa);
+       REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_5, 0xaaaa);
+       REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_6, 0xffff);
+       REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_7, 0xffff);
+
+       /* Configure the IEEE 802.1p priority mapping register. */
+       REG_WRITE(REG_GLOBAL, GLOBAL_IEEE_PRI, 0xfa41);
+
+       /* Send all frames with destination addresses matching
+        * 01:80:c2:00:00:0x to the CPU port.
+        */
+       REG_WRITE(REG_GLOBAL2, GLOBAL2_MGMT_EN_0X, 0xffff);
+
+       /* Ignore removed tag data on doubly tagged packets, disable
+        * flow control messages, force flow control priority to the
+        * highest, and send all special multicast frames to the CPU
+        * port at the highest priority.
+        */
+       REG_WRITE(REG_GLOBAL2, GLOBAL2_SWITCH_MGMT,
+                 0x7 | GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x70 |
+                 GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI);
+
+       /* Program the DSA routing table. */
+       for (i = 0; i < 32; i++) {
+               int nexthop = 0x1f;
+
+               if (ds->pd->rtable &&
+                   i != ds->index && i < ds->dst->pd->nr_chips)
+                       nexthop = ds->pd->rtable[i] & 0x1f;
+
+               REG_WRITE(REG_GLOBAL2, GLOBAL2_DEVICE_MAPPING,
+                         GLOBAL2_DEVICE_MAPPING_UPDATE |
+                         (i << GLOBAL2_DEVICE_MAPPING_TARGET_SHIFT) |
+                         nexthop);
+       }
+
+       /* Clear all trunk masks. */
+       for (i = 0; i < 8; i++)
+               REG_WRITE(REG_GLOBAL2, GLOBAL2_TRUNK_MASK,
+                         0x8000 | (i << GLOBAL2_TRUNK_MASK_NUM_SHIFT) |
+                         ((1 << ps->num_ports) - 1));
+
+       /* Clear all trunk mappings. */
+       for (i = 0; i < 16; i++)
+               REG_WRITE(REG_GLOBAL2, GLOBAL2_TRUNK_MAPPING,
+                         GLOBAL2_TRUNK_MAPPING_UPDATE |
+                         (i << GLOBAL2_TRUNK_MAPPING_ID_SHIFT));
+
+       if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
+           mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds)) {
+               /* Send all frames with destination addresses matching
+                * 01:80:c2:00:00:2x to the CPU port.
+                */
+               REG_WRITE(REG_GLOBAL2, GLOBAL2_MGMT_EN_2X, 0xffff);
+
+               /* Initialise cross-chip port VLAN table to reset
+                * defaults.
+                */
+               REG_WRITE(REG_GLOBAL2, GLOBAL2_PVT_ADDR, 0x9000);
+
+               /* Clear the priority override table. */
+               for (i = 0; i < 16; i++)
+                       REG_WRITE(REG_GLOBAL2, GLOBAL2_PRIO_OVERRIDE,
+                                 0x8000 | (i << 8));
+       }
+
+       if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
+           mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
+           mv88e6xxx_6185_family(ds) || mv88e6xxx_6095_family(ds)) {
+               /* Disable ingress rate limiting by resetting all
+                * ingress rate limit registers to their initial
+                * state.
+                */
+               for (i = 0; i < ps->num_ports; i++)
+                       REG_WRITE(REG_GLOBAL2, GLOBAL2_INGRESS_OP,
+                                 0x9000 | (i << 8));
+       }
+
+       return 0;
+}
+
 int mv88e6xxx_switch_reset(struct dsa_switch *ds, bool ppu_active)
 {
        struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
@@ -1343,14 +1759,14 @@ int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg)
        struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
        int ret;
 
-       mutex_lock(&ps->phy_mutex);
+       mutex_lock(&ps->smi_mutex);
        ret = _mv88e6xxx_phy_write_indirect(ds, port, 0x16, page);
        if (ret < 0)
                goto error;
        ret = _mv88e6xxx_phy_read_indirect(ds, port, reg);
 error:
        _mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0);
-       mutex_unlock(&ps->phy_mutex);
+       mutex_unlock(&ps->smi_mutex);
        return ret;
 }
 
@@ -1360,7 +1776,7 @@ int mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page,
        struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
        int ret;
 
-       mutex_lock(&ps->phy_mutex);
+       mutex_lock(&ps->smi_mutex);
        ret = _mv88e6xxx_phy_write_indirect(ds, port, 0x16, page);
        if (ret < 0)
                goto error;
@@ -1368,7 +1784,7 @@ int mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page,
        ret = _mv88e6xxx_phy_write_indirect(ds, port, reg, val);
 error:
        _mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0);
-       mutex_unlock(&ps->phy_mutex);
+       mutex_unlock(&ps->smi_mutex);
        return ret;
 }
 
@@ -1391,9 +1807,9 @@ mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum)
        if (addr < 0)
                return addr;
 
-       mutex_lock(&ps->phy_mutex);
+       mutex_lock(&ps->smi_mutex);
        ret = _mv88e6xxx_phy_read(ds, addr, regnum);
-       mutex_unlock(&ps->phy_mutex);
+       mutex_unlock(&ps->smi_mutex);
        return ret;
 }
 
@@ -1407,9 +1823,9 @@ mv88e6xxx_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val)
        if (addr < 0)
                return addr;
 
-       mutex_lock(&ps->phy_mutex);
+       mutex_lock(&ps->smi_mutex);
        ret = _mv88e6xxx_phy_write(ds, addr, regnum, val);
-       mutex_unlock(&ps->phy_mutex);
+       mutex_unlock(&ps->smi_mutex);
        return ret;
 }
 
@@ -1423,9 +1839,9 @@ mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int port, int regnum)
        if (addr < 0)
                return addr;
 
-       mutex_lock(&ps->phy_mutex);
+       mutex_lock(&ps->smi_mutex);
        ret = _mv88e6xxx_phy_read_indirect(ds, addr, regnum);
-       mutex_unlock(&ps->phy_mutex);
+       mutex_unlock(&ps->smi_mutex);
        return ret;
 }
 
@@ -1440,9 +1856,9 @@ mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int port, int regnum,
        if (addr < 0)
                return addr;
 
-       mutex_lock(&ps->phy_mutex);
+       mutex_lock(&ps->smi_mutex);
        ret = _mv88e6xxx_phy_write_indirect(ds, addr, regnum, val);
-       mutex_unlock(&ps->phy_mutex);
+       mutex_unlock(&ps->smi_mutex);
        return ret;
 }