X-Git-Url: http://git.cascardo.eti.br/?a=blobdiff_plain;f=drivers%2Fnet%2Fdsa%2Fmv88e6xxx.c;h=56ecbe49eb86c0dfcc390b68b5be922dc0a85b8e;hb=16fe24fc43e8d32b76030c79c151c7db691865f6;hp=af639ab4c55b64fd886df0413d090afd2fe618ba;hpb=d839c98f98826f0c13e46d5a4cecb46dd357b50e;p=cascardo%2Flinux.git diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index af639ab4c55b..56ecbe49eb86 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c @@ -19,6 +19,34 @@ #include #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) +{ + 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); + 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; - return mv88e6xxx_phy_wait(ds); + 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); } 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,13 +1348,212 @@ 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. */ @@ -1281,13 +1587,25 @@ abort: 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; }