summaryrefslogtreecommitdiff
path: root/drivers/net/dsa/mv88e6xxx/chip.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/dsa/mv88e6xxx/chip.c')
-rw-r--r--drivers/net/dsa/mv88e6xxx/chip.c1028
1 files changed, 574 insertions, 454 deletions
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 5cb06f7673af..d36aedde8cb9 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -216,107 +216,48 @@ static int mv88e6xxx_write(struct mv88e6xxx_chip *chip,
return 0;
}
-static int _mv88e6xxx_reg_read(struct mv88e6xxx_chip *chip, int addr, int reg)
+/* Indirect write to single pointer-data register with an Update bit */
+static int mv88e6xxx_update(struct mv88e6xxx_chip *chip, int addr, int reg,
+ u16 update)
{
u16 val;
- int err;
-
- err = mv88e6xxx_read(chip, addr, reg, &val);
- if (err)
- return err;
+ int i, err;
- return val;
-}
+ /* Wait until the previous operation is completed */
+ for (i = 0; i < 16; ++i) {
+ err = mv88e6xxx_read(chip, addr, reg, &val);
+ if (err)
+ return err;
-static int mv88e6xxx_reg_read(struct mv88e6xxx_chip *chip, int addr, int reg)
-{
- int ret;
+ if (!(val & BIT(15)))
+ break;
+ }
- mutex_lock(&chip->reg_lock);
- ret = _mv88e6xxx_reg_read(chip, addr, reg);
- mutex_unlock(&chip->reg_lock);
+ if (i == 16)
+ return -ETIMEDOUT;
- return ret;
-}
+ /* Set the Update bit to trigger a write operation */
+ val = BIT(15) | update;
-static int _mv88e6xxx_reg_write(struct mv88e6xxx_chip *chip, int addr,
- int reg, u16 val)
-{
return mv88e6xxx_write(chip, addr, reg, val);
}
-static int mv88e6xxx_reg_write(struct mv88e6xxx_chip *chip, int addr,
- int reg, u16 val)
-{
- int ret;
-
- mutex_lock(&chip->reg_lock);
- ret = _mv88e6xxx_reg_write(chip, addr, reg, val);
- mutex_unlock(&chip->reg_lock);
-
- return ret;
-}
-
-static int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr)
+static int _mv88e6xxx_reg_read(struct mv88e6xxx_chip *chip, int addr, int reg)
{
- struct mv88e6xxx_chip *chip = ds_to_priv(ds);
+ u16 val;
int err;
- err = mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_MAC_01,
- (addr[0] << 8) | addr[1]);
- if (err)
- return err;
-
- err = mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_MAC_23,
- (addr[2] << 8) | addr[3]);
+ err = mv88e6xxx_read(chip, addr, reg, &val);
if (err)
return err;
- return mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_MAC_45,
- (addr[4] << 8) | addr[5]);
-}
-
-static int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr)
-{
- struct mv88e6xxx_chip *chip = ds_to_priv(ds);
- int ret;
- int i;
-
- for (i = 0; i < 6; i++) {
- int j;
-
- /* Write the MAC address byte. */
- ret = mv88e6xxx_reg_write(chip, REG_GLOBAL2, GLOBAL2_SWITCH_MAC,
- GLOBAL2_SWITCH_MAC_BUSY |
- (i << 8) | addr[i]);
- if (ret)
- return ret;
-
- /* Wait for the write to complete. */
- for (j = 0; j < 16; j++) {
- ret = mv88e6xxx_reg_read(chip, REG_GLOBAL2,
- GLOBAL2_SWITCH_MAC);
- if (ret < 0)
- return ret;
-
- if ((ret & GLOBAL2_SWITCH_MAC_BUSY) == 0)
- break;
- }
- if (j == 16)
- return -ETIMEDOUT;
- }
-
- return 0;
+ return val;
}
-static int mv88e6xxx_set_addr(struct dsa_switch *ds, u8 *addr)
+static int _mv88e6xxx_reg_write(struct mv88e6xxx_chip *chip, int addr,
+ int reg, u16 val)
{
- struct mv88e6xxx_chip *chip = ds_to_priv(ds);
-
- if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_SWITCH_MAC))
- return mv88e6xxx_set_addr_indirect(ds, addr);
- else
- return mv88e6xxx_set_addr_direct(ds, addr);
+ return mv88e6xxx_write(chip, addr, reg, val);
}
static int mv88e6xxx_mdio_read_direct(struct mv88e6xxx_chip *chip,
@@ -897,259 +838,12 @@ static int _mv88e6xxx_wait(struct mv88e6xxx_chip *chip, int reg, int offset,
return -ETIMEDOUT;
}
-static int mv88e6xxx_wait(struct mv88e6xxx_chip *chip, int reg,
- int offset, u16 mask)
-{
- int ret;
-
- mutex_lock(&chip->reg_lock);
- ret = _mv88e6xxx_wait(chip, reg, offset, mask);
- mutex_unlock(&chip->reg_lock);
-
- return ret;
-}
-
static int mv88e6xxx_mdio_wait(struct mv88e6xxx_chip *chip)
{
return _mv88e6xxx_wait(chip, REG_GLOBAL2, GLOBAL2_SMI_OP,
GLOBAL2_SMI_OP_BUSY);
}
-static int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds)
-{
- struct mv88e6xxx_chip *chip = ds_to_priv(ds);
-
- return mv88e6xxx_wait(chip, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
- GLOBAL2_EEPROM_OP_LOAD);
-}
-
-static int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds)
-{
- struct mv88e6xxx_chip *chip = ds_to_priv(ds);
-
- return mv88e6xxx_wait(chip, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
- GLOBAL2_EEPROM_OP_BUSY);
-}
-
-static int mv88e6xxx_read_eeprom_word(struct dsa_switch *ds, int addr)
-{
- struct mv88e6xxx_chip *chip = ds_to_priv(ds);
- int ret;
-
- mutex_lock(&chip->eeprom_mutex);
-
- ret = mv88e6xxx_reg_write(chip, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
- GLOBAL2_EEPROM_OP_READ |
- (addr & GLOBAL2_EEPROM_OP_ADDR_MASK));
- if (ret < 0)
- goto error;
-
- ret = mv88e6xxx_eeprom_busy_wait(ds);
- if (ret < 0)
- goto error;
-
- ret = mv88e6xxx_reg_read(chip, REG_GLOBAL2, GLOBAL2_EEPROM_DATA);
-error:
- mutex_unlock(&chip->eeprom_mutex);
- return ret;
-}
-
-static int mv88e6xxx_get_eeprom_len(struct dsa_switch *ds)
-{
- struct mv88e6xxx_chip *chip = ds_to_priv(ds);
-
- if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEPROM))
- return chip->eeprom_len;
-
- return 0;
-}
-
-static int mv88e6xxx_get_eeprom(struct dsa_switch *ds,
- struct ethtool_eeprom *eeprom, u8 *data)
-{
- struct mv88e6xxx_chip *chip = ds_to_priv(ds);
- int offset;
- int len;
- int ret;
-
- if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEPROM))
- return -EOPNOTSUPP;
-
- offset = eeprom->offset;
- len = eeprom->len;
- eeprom->len = 0;
-
- eeprom->magic = 0xc3ec4951;
-
- ret = mv88e6xxx_eeprom_load_wait(ds);
- if (ret < 0)
- return ret;
-
- if (offset & 1) {
- int word;
-
- word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
- if (word < 0)
- return word;
-
- *data++ = (word >> 8) & 0xff;
-
- offset++;
- len--;
- eeprom->len++;
- }
-
- while (len >= 2) {
- int word;
-
- word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
- if (word < 0)
- return word;
-
- *data++ = word & 0xff;
- *data++ = (word >> 8) & 0xff;
-
- offset += 2;
- len -= 2;
- eeprom->len += 2;
- }
-
- if (len) {
- int word;
-
- word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
- if (word < 0)
- return word;
-
- *data++ = word & 0xff;
-
- offset++;
- len--;
- eeprom->len++;
- }
-
- return 0;
-}
-
-static int mv88e6xxx_eeprom_is_readonly(struct dsa_switch *ds)
-{
- struct mv88e6xxx_chip *chip = ds_to_priv(ds);
- int ret;
-
- ret = mv88e6xxx_reg_read(chip, REG_GLOBAL2, GLOBAL2_EEPROM_OP);
- if (ret < 0)
- return ret;
-
- if (!(ret & GLOBAL2_EEPROM_OP_WRITE_EN))
- return -EROFS;
-
- return 0;
-}
-
-static int mv88e6xxx_write_eeprom_word(struct dsa_switch *ds, int addr,
- u16 data)
-{
- struct mv88e6xxx_chip *chip = ds_to_priv(ds);
- int ret;
-
- mutex_lock(&chip->eeprom_mutex);
-
- ret = mv88e6xxx_reg_write(chip, REG_GLOBAL2, GLOBAL2_EEPROM_DATA, data);
- if (ret < 0)
- goto error;
-
- ret = mv88e6xxx_reg_write(chip, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
- GLOBAL2_EEPROM_OP_WRITE |
- (addr & GLOBAL2_EEPROM_OP_ADDR_MASK));
- if (ret < 0)
- goto error;
-
- ret = mv88e6xxx_eeprom_busy_wait(ds);
-error:
- mutex_unlock(&chip->eeprom_mutex);
- return ret;
-}
-
-static int mv88e6xxx_set_eeprom(struct dsa_switch *ds,
- struct ethtool_eeprom *eeprom, u8 *data)
-{
- struct mv88e6xxx_chip *chip = ds_to_priv(ds);
- int offset;
- int ret;
- int len;
-
- if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEPROM))
- return -EOPNOTSUPP;
-
- if (eeprom->magic != 0xc3ec4951)
- return -EINVAL;
-
- ret = mv88e6xxx_eeprom_is_readonly(ds);
- if (ret)
- return ret;
-
- offset = eeprom->offset;
- len = eeprom->len;
- eeprom->len = 0;
-
- ret = mv88e6xxx_eeprom_load_wait(ds);
- if (ret < 0)
- return ret;
-
- if (offset & 1) {
- int word;
-
- word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
- if (word < 0)
- return word;
-
- word = (*data++ << 8) | (word & 0xff);
-
- ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word);
- if (ret < 0)
- return ret;
-
- offset++;
- len--;
- eeprom->len++;
- }
-
- while (len >= 2) {
- int word;
-
- word = *data++;
- word |= *data++ << 8;
-
- ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word);
- if (ret < 0)
- return ret;
-
- offset += 2;
- len -= 2;
- eeprom->len += 2;
- }
-
- if (len) {
- int word;
-
- word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
- if (word < 0)
- return word;
-
- word = (word & 0xff00) | *data++;
-
- ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word);
- if (ret < 0)
- return ret;
-
- offset++;
- len--;
- eeprom->len++;
- }
-
- return 0;
-}
-
static int _mv88e6xxx_atu_wait(struct mv88e6xxx_chip *chip)
{
return _mv88e6xxx_wait(chip, REG_GLOBAL, GLOBAL_ATU_OP,
@@ -1460,9 +1154,6 @@ static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port,
int stp_state;
int err;
- if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_PORTSTATE))
- return;
-
switch (state) {
case BR_STATE_DISABLED:
stp_state = PORT_CONTROL_STATE_DISABLED;
@@ -2398,11 +2089,6 @@ static int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_fdb *fdb,
struct switchdev_trans *trans)
{
- struct mv88e6xxx_chip *chip = ds_to_priv(ds);
-
- if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_ATU))
- return -EOPNOTSUPP;
-
/* We don't need any dynamic resource from the kernel (yet),
* so skip the prepare phase.
*/
@@ -2418,9 +2104,6 @@ static void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
GLOBAL_ATU_DATA_STATE_UC_STATIC;
struct mv88e6xxx_chip *chip = ds_to_priv(ds);
- if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_ATU))
- return;
-
mutex_lock(&chip->reg_lock);
if (_mv88e6xxx_port_fdb_load(chip, port, fdb->addr, fdb->vid, state))
netdev_err(ds->ports[port].netdev,
@@ -2434,9 +2117,6 @@ static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
struct mv88e6xxx_chip *chip = ds_to_priv(ds);
int ret;
- if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_ATU))
- return -EOPNOTSUPP;
-
mutex_lock(&chip->reg_lock);
ret = _mv88e6xxx_port_fdb_load(chip, port, fdb->addr, fdb->vid,
GLOBAL_ATU_DATA_STATE_UNUSED);
@@ -2542,9 +2222,6 @@ static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
u16 fid;
int err;
- if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_ATU))
- return -EOPNOTSUPP;
-
mutex_lock(&chip->reg_lock);
/* Dump port's default Filtering Information Database (VLAN ID 0) */
@@ -2587,9 +2264,6 @@ static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
struct mv88e6xxx_chip *chip = ds_to_priv(ds);
int i, err = 0;
- if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VLANTABLE))
- return -EOPNOTSUPP;
-
mutex_lock(&chip->reg_lock);
/* Assign the bridge and remap each port's VLANTable */
@@ -2614,9 +2288,6 @@ static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port)
struct net_device *bridge = chip->ports[port].bridge_dev;
int i;
- if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VLANTABLE))
- return;
-
mutex_lock(&chip->reg_lock);
/* Unassign the bridge and remap each port's VLANTable */
@@ -2744,6 +2415,17 @@ static int mv88e6xxx_power_on_serdes(struct mv88e6xxx_chip *chip)
return ret;
}
+static int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port,
+ int reg, u16 *val)
+{
+ int addr = chip->info->port_base_addr + port;
+
+ if (port >= chip->info->num_ports)
+ return -EINVAL;
+
+ return mv88e6xxx_read(chip, addr, reg, val);
+}
+
static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
{
struct dsa_switch *ds = chip->ds;
@@ -3016,13 +2698,70 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
return 0;
}
-static int mv88e6xxx_setup_global(struct mv88e6xxx_chip *chip)
+static int mv88e6xxx_g1_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr)
+{
+ int err;
+
+ err = mv88e6xxx_write(chip, REG_GLOBAL, GLOBAL_MAC_01,
+ (addr[0] << 8) | addr[1]);
+ if (err)
+ return err;
+
+ err = mv88e6xxx_write(chip, REG_GLOBAL, GLOBAL_MAC_23,
+ (addr[2] << 8) | addr[3]);
+ if (err)
+ return err;
+
+ return mv88e6xxx_write(chip, REG_GLOBAL, GLOBAL_MAC_45,
+ (addr[4] << 8) | addr[5]);
+}
+
+static int mv88e6xxx_g1_set_age_time(struct mv88e6xxx_chip *chip,
+ unsigned int msecs)
+{
+ const unsigned int coeff = chip->info->age_time_coeff;
+ const unsigned int min = 0x01 * coeff;
+ const unsigned int max = 0xff * coeff;
+ u8 age_time;
+ u16 val;
+ int err;
+
+ if (msecs < min || msecs > max)
+ return -ERANGE;
+
+ /* Round to nearest multiple of coeff */
+ age_time = (msecs + coeff / 2) / coeff;
+
+ err = mv88e6xxx_read(chip, REG_GLOBAL, GLOBAL_ATU_CONTROL, &val);
+ if (err)
+ return err;
+
+ /* AgeTime is 11:4 bits */
+ val &= ~0xff0;
+ val |= age_time << 4;
+
+ return mv88e6xxx_write(chip, REG_GLOBAL, GLOBAL_ATU_CONTROL, val);
+}
+
+static int mv88e6xxx_set_ageing_time(struct dsa_switch *ds,
+ unsigned int ageing_time)
+{
+ struct mv88e6xxx_chip *chip = ds_to_priv(ds);
+ int err;
+
+ mutex_lock(&chip->reg_lock);
+ err = mv88e6xxx_g1_set_age_time(chip, ageing_time);
+ mutex_unlock(&chip->reg_lock);
+
+ return err;
+}
+
+static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip)
{
struct dsa_switch *ds = chip->ds;
u32 upstream_port = dsa_upstream_port(ds);
u16 reg;
int err;
- int i;
/* Enable the PHY Polling Unit if present, don't discard any packets,
* and mask all interrupt sources.
@@ -3054,12 +2793,26 @@ static int mv88e6xxx_setup_global(struct mv88e6xxx_chip *chip)
if (err)
return err;
+ /* Clear all the VTU and STU entries */
+ err = _mv88e6xxx_vtu_stu_flush(chip);
+ if (err < 0)
+ return err;
+
/* Set the default address aging time to 5 minutes, and
* enable address learn messages to be sent to all message
* ports.
*/
- err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_ATU_CONTROL,
- 0x0140 | GLOBAL_ATU_CONTROL_LEARN2ALL);
+ err = mv88e6xxx_write(chip, REG_GLOBAL, GLOBAL_ATU_CONTROL,
+ GLOBAL_ATU_CONTROL_LEARN2ALL);
+ if (err)
+ return err;
+
+ err = mv88e6xxx_g1_set_age_time(chip, 300000);
+ if (err)
+ return err;
+
+ /* Clear all ATU entries */
+ err = _mv88e6xxx_atu_flush(chip, 0, true);
if (err)
return err;
@@ -3094,131 +2847,286 @@ static int mv88e6xxx_setup_global(struct mv88e6xxx_chip *chip)
if (err)
return err;
- /* Send all frames with destination addresses matching
- * 01:80:c2:00:00:0x to the CPU port.
- */
- err = _mv88e6xxx_reg_write(chip, REG_GLOBAL2, GLOBAL2_MGMT_EN_0X,
- 0xffff);
+ /* Clear the statistics counters for all ports */
+ err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_STATS_OP,
+ GLOBAL_STATS_OP_FLUSH_ALL);
if (err)
return err;
- /* 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.
- */
- err = _mv88e6xxx_reg_write(chip, REG_GLOBAL2, GLOBAL2_SWITCH_MGMT,
- 0x7 | GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x70 |
- GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI);
+ /* Wait for the flush to complete. */
+ err = _mv88e6xxx_stats_wait(chip);
if (err)
return err;
- /* Program the DSA routing table. */
- for (i = 0; i < 32; i++) {
- int nexthop = 0x1f;
+ return 0;
+}
+
+static int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip,
+ int target, int port)
+{
+ u16 val = (target << 8) | (port & 0xf);
+
+ return mv88e6xxx_update(chip, REG_GLOBAL2, GLOBAL2_DEVICE_MAPPING, val);
+}
- if (i != ds->index && i < DSA_MAX_SWITCHES)
- nexthop = ds->rtable[i] & 0x1f;
+static int mv88e6xxx_g2_set_device_mapping(struct mv88e6xxx_chip *chip)
+{
+ int target, port;
+ int err;
+
+ /* Initialize the routing port to the 32 possible target devices */
+ for (target = 0; target < 32; ++target) {
+ port = 0xf;
+
+ if (target < DSA_MAX_SWITCHES) {
+ port = chip->ds->rtable[target];
+ if (port == DSA_RTABLE_NONE)
+ port = 0xf;
+ }
- err = _mv88e6xxx_reg_write(
- chip, REG_GLOBAL2,
- GLOBAL2_DEVICE_MAPPING,
- GLOBAL2_DEVICE_MAPPING_UPDATE |
- (i << GLOBAL2_DEVICE_MAPPING_TARGET_SHIFT) | nexthop);
+ err = mv88e6xxx_g2_device_mapping_write(chip, target, port);
if (err)
- return err;
+ break;
}
- /* Clear all trunk masks. */
- for (i = 0; i < 8; i++) {
- err = _mv88e6xxx_reg_write(chip, REG_GLOBAL2,
- GLOBAL2_TRUNK_MASK,
- 0x8000 |
- (i << GLOBAL2_TRUNK_MASK_NUM_SHIFT) |
- ((1 << chip->info->num_ports) - 1));
+ return err;
+}
+
+static int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num,
+ bool hask, u16 mask)
+{
+ const u16 port_mask = BIT(chip->info->num_ports) - 1;
+ u16 val = (num << 12) | (mask & port_mask);
+
+ if (hask)
+ val |= GLOBAL2_TRUNK_MASK_HASK;
+
+ return mv88e6xxx_update(chip, REG_GLOBAL2, GLOBAL2_TRUNK_MASK, val);
+}
+
+static int mv88e6xxx_g2_trunk_mapping_write(struct mv88e6xxx_chip *chip, int id,
+ u16 map)
+{
+ const u16 port_mask = BIT(chip->info->num_ports) - 1;
+ u16 val = (id << 11) | (map & port_mask);
+
+ return mv88e6xxx_update(chip, REG_GLOBAL2, GLOBAL2_TRUNK_MAPPING, val);
+}
+
+static int mv88e6xxx_g2_clear_trunk(struct mv88e6xxx_chip *chip)
+{
+ const u16 port_mask = BIT(chip->info->num_ports) - 1;
+ int i, err;
+
+ /* Clear all eight possible Trunk Mask vectors */
+ for (i = 0; i < 8; ++i) {
+ err = mv88e6xxx_g2_trunk_mask_write(chip, i, false, port_mask);
if (err)
return err;
}
- /* Clear all trunk mappings. */
- for (i = 0; i < 16; i++) {
- err = _mv88e6xxx_reg_write(
- chip, REG_GLOBAL2,
- GLOBAL2_TRUNK_MAPPING,
- GLOBAL2_TRUNK_MAPPING_UPDATE |
- (i << GLOBAL2_TRUNK_MAPPING_ID_SHIFT));
+ /* Clear all sixteen possible Trunk ID routing vectors */
+ for (i = 0; i < 16; ++i) {
+ err = mv88e6xxx_g2_trunk_mapping_write(chip, i, 0);
if (err)
return err;
}
- if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
- mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
- mv88e6xxx_6320_family(chip)) {
- /* Send all frames with destination addresses matching
- * 01:80:c2:00:00:2x to the CPU port.
- */
- err = _mv88e6xxx_reg_write(chip, REG_GLOBAL2,
- GLOBAL2_MGMT_EN_2X, 0xffff);
+ return 0;
+}
+
+static int mv88e6xxx_g2_clear_irl(struct mv88e6xxx_chip *chip)
+{
+ int port, err;
+
+ /* Init all Ingress Rate Limit resources of all ports */
+ for (port = 0; port < chip->info->num_ports; ++port) {
+ /* XXX newer chips (like 88E6390) have different 2-bit ops */
+ err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_IRL_CMD,
+ GLOBAL2_IRL_CMD_OP_INIT_ALL |
+ (port << 8));
if (err)
- return err;
+ break;
+
+ /* Wait for the operation to complete */
+ err = _mv88e6xxx_wait(chip, REG_GLOBAL2, GLOBAL2_IRL_CMD,
+ GLOBAL2_IRL_CMD_BUSY);
+ if (err)
+ break;
+ }
+
+ return err;
+}
+
+/* Indirect write to the Switch MAC/WoL/WoF register */
+static int mv88e6xxx_g2_switch_mac_write(struct mv88e6xxx_chip *chip,
+ unsigned int pointer, u8 data)
+{
+ u16 val = (pointer << 8) | data;
+
+ return mv88e6xxx_update(chip, REG_GLOBAL2, GLOBAL2_SWITCH_MAC, val);
+}
+
+static int mv88e6xxx_g2_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr)
+{
+ int i, err;
+
+ for (i = 0; i < 6; i++) {
+ err = mv88e6xxx_g2_switch_mac_write(chip, i, addr[i]);
+ if (err)
+ break;
+ }
+
+ return err;
+}
+
+static int mv88e6xxx_g2_pot_write(struct mv88e6xxx_chip *chip, int pointer,
+ u8 data)
+{
+ u16 val = (pointer << 8) | (data & 0x7);
+
+ return mv88e6xxx_update(chip, REG_GLOBAL2, GLOBAL2_PRIO_OVERRIDE, val);
+}
+
+static int mv88e6xxx_g2_clear_pot(struct mv88e6xxx_chip *chip)
+{
+ int i, err;
+
+ /* Clear all sixteen possible Priority Override entries */
+ for (i = 0; i < 16; i++) {
+ err = mv88e6xxx_g2_pot_write(chip, i, 0);
+ if (err)
+ break;
+ }
+
+ return err;
+}
+
+static int mv88e6xxx_g2_eeprom_wait(struct mv88e6xxx_chip *chip)
+{
+ return _mv88e6xxx_wait(chip, REG_GLOBAL2, GLOBAL2_EEPROM_CMD,
+ GLOBAL2_EEPROM_CMD_BUSY |
+ GLOBAL2_EEPROM_CMD_RUNNING);
+}
+
+static int mv88e6xxx_g2_eeprom_cmd(struct mv88e6xxx_chip *chip, u16 cmd)
+{
+ int err;
+
+ err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_EEPROM_CMD, cmd);
+ if (err)
+ return err;
+
+ return mv88e6xxx_g2_eeprom_wait(chip);
+}
+
+static int mv88e6xxx_g2_eeprom_read16(struct mv88e6xxx_chip *chip,
+ u8 addr, u16 *data)
+{
+ u16 cmd = GLOBAL2_EEPROM_CMD_OP_READ | addr;
+ int err;
+
+ err = mv88e6xxx_g2_eeprom_wait(chip);
+ if (err)
+ return err;
+
+ err = mv88e6xxx_g2_eeprom_cmd(chip, cmd);
+ if (err)
+ return err;
+
+ return mv88e6xxx_read(chip, REG_GLOBAL2, GLOBAL2_EEPROM_DATA, data);
+}
+
+static int mv88e6xxx_g2_eeprom_write16(struct mv88e6xxx_chip *chip,
+ u8 addr, u16 data)
+{
+ u16 cmd = GLOBAL2_EEPROM_CMD_OP_WRITE | addr;
+ int err;
+
+ err = mv88e6xxx_g2_eeprom_wait(chip);
+ if (err)
+ return err;
+
+ err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_EEPROM_DATA, data);
+ if (err)
+ return err;
+
+ return mv88e6xxx_g2_eeprom_cmd(chip, cmd);
+}
+
+static int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip)
+{
+ u16 reg;
+ int err;
- /* Initialise cross-chip port VLAN table to reset
- * defaults.
+ if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X)) {
+ /* Consider the frames with reserved multicast destination
+ * addresses matching 01:80:c2:00:00:2x as MGMT.
*/
- err = _mv88e6xxx_reg_write(chip, REG_GLOBAL2,
- GLOBAL2_PVT_ADDR, 0x9000);
+ err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_MGMT_EN_2X,
+ 0xffff);
if (err)
return err;
-
- /* Clear the priority override table. */
- for (i = 0; i < 16; i++) {
- err = _mv88e6xxx_reg_write(chip, REG_GLOBAL2,
- GLOBAL2_PRIO_OVERRIDE,
- 0x8000 | (i << 8));
- if (err)
- return err;
- }
}
- if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
- mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
- mv88e6xxx_6185_family(chip) || mv88e6xxx_6095_family(chip) ||
- mv88e6xxx_6320_family(chip)) {
- /* Disable ingress rate limiting by resetting all
- * ingress rate limit registers to their initial
- * state.
+ if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X)) {
+ /* Consider the frames with reserved multicast destination
+ * addresses matching 01:80:c2:00:00:0x as MGMT.
*/
- for (i = 0; i < chip->info->num_ports; i++) {
- err = _mv88e6xxx_reg_write(chip, REG_GLOBAL2,
- GLOBAL2_INGRESS_OP,
- 0x9000 | (i << 8));
- if (err)
- return err;
- }
+ err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_MGMT_EN_0X,
+ 0xffff);
+ if (err)
+ return err;
}
- /* Clear the statistics counters for all ports */
- err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_STATS_OP,
- GLOBAL_STATS_OP_FLUSH_ALL);
+ /* 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 = GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI | (0x7 << 4);
+ if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X) ||
+ mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X))
+ reg |= GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x7;
+ err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_SWITCH_MGMT, reg);
if (err)
return err;
- /* Wait for the flush to complete. */
- err = _mv88e6xxx_stats_wait(chip);
+ /* Program the DSA routing table. */
+ err = mv88e6xxx_g2_set_device_mapping(chip);
if (err)
return err;
- /* Clear all ATU entries */
- err = _mv88e6xxx_atu_flush(chip, 0, true);
+ /* Clear all trunk masks and mapping. */
+ err = mv88e6xxx_g2_clear_trunk(chip);
if (err)
return err;
- /* Clear all the VTU and STU entries */
- err = _mv88e6xxx_vtu_stu_flush(chip);
- if (err < 0)
- return err;
+ if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_IRL)) {
+ /* Disable ingress rate limiting by resetting all per port
+ * ingress rate limit resources to their initial state.
+ */
+ err = mv88e6xxx_g2_clear_irl(chip);
+ if (err)
+ return err;
+ }
- return err;
+ if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_PVT)) {
+ /* Initialize Cross-chip Port VLAN Table to reset defaults */
+ err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_PVT_ADDR,
+ GLOBAL2_PVT_ADDR_OP_INIT_ONES);
+ if (err)
+ return err;
+ }
+
+ if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_POT)) {
+ /* Clear the priority override table. */
+ err = mv88e6xxx_g2_clear_pot(chip);
+ if (err)
+ return err;
+ }
+
+ return 0;
}
static int mv88e6xxx_setup(struct dsa_switch *ds)
@@ -3230,21 +3138,27 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)
chip->ds = ds;
ds->slave_mii_bus = chip->mdio_bus;
- if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEPROM))
- mutex_init(&chip->eeprom_mutex);
-
mutex_lock(&chip->reg_lock);
err = mv88e6xxx_switch_reset(chip);
if (err)
goto unlock;
- err = mv88e6xxx_setup_global(chip);
+ /* Setup Switch Port Registers */
+ for (i = 0; i < chip->info->num_ports; i++) {
+ err = mv88e6xxx_setup_port(chip, i);
+ if (err)
+ goto unlock;
+ }
+
+ /* Setup Switch Global 1 Registers */
+ err = mv88e6xxx_g1_setup(chip);
if (err)
goto unlock;
- for (i = 0; i < chip->info->num_ports; i++) {
- err = mv88e6xxx_setup_port(chip, i);
+ /* Setup Switch Global 2 Registers */
+ if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_GLOBAL2)) {
+ err = mv88e6xxx_g2_setup(chip);
if (err)
goto unlock;
}
@@ -3255,6 +3169,24 @@ unlock:
return err;
}
+static int mv88e6xxx_set_addr(struct dsa_switch *ds, u8 *addr)
+{
+ struct mv88e6xxx_chip *chip = ds_to_priv(ds);
+ int err;
+
+ mutex_lock(&chip->reg_lock);
+
+ /* Has an indirect Switch MAC/WoL/WoF register in Global 2? */
+ if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_SWITCH_MAC))
+ err = mv88e6xxx_g2_set_switch_mac(chip, addr);
+ else
+ err = mv88e6xxx_g1_set_switch_mac(chip, addr);
+
+ mutex_unlock(&chip->reg_lock);
+
+ return err;
+}
+
static int mv88e6xxx_mdio_page_read(struct dsa_switch *ds, int port, int page,
int reg)
{
@@ -3528,6 +3460,173 @@ static int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
}
#endif /* CONFIG_NET_DSA_HWMON */
+static int mv88e6xxx_get_eeprom_len(struct dsa_switch *ds)
+{
+ struct mv88e6xxx_chip *chip = ds_to_priv(ds);
+
+ return chip->eeprom_len;
+}
+
+static int mv88e6xxx_get_eeprom16(struct mv88e6xxx_chip *chip,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ unsigned int offset = eeprom->offset;
+ unsigned int len = eeprom->len;
+ u16 val;
+ int err;
+
+ eeprom->len = 0;
+
+ if (offset & 1) {
+ err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
+ if (err)
+ return err;
+
+ *data++ = (val >> 8) & 0xff;
+
+ offset++;
+ len--;
+ eeprom->len++;
+ }
+
+ while (len >= 2) {
+ err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
+ if (err)
+ return err;
+
+ *data++ = val & 0xff;
+ *data++ = (val >> 8) & 0xff;
+
+ offset += 2;
+ len -= 2;
+ eeprom->len += 2;
+ }
+
+ if (len) {
+ err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
+ if (err)
+ return err;
+
+ *data++ = val & 0xff;
+
+ offset++;
+ len--;
+ eeprom->len++;
+ }
+
+ return 0;
+}
+
+static int mv88e6xxx_get_eeprom(struct dsa_switch *ds,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ struct mv88e6xxx_chip *chip = ds_to_priv(ds);
+ int err;
+
+ mutex_lock(&chip->reg_lock);
+
+ if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_EEPROM16))
+ err = mv88e6xxx_get_eeprom16(chip, eeprom, data);
+ else
+ err = -EOPNOTSUPP;
+
+ mutex_unlock(&chip->reg_lock);
+
+ if (err)
+ return err;
+
+ eeprom->magic = 0xc3ec4951;
+
+ return 0;
+}
+
+static int mv88e6xxx_set_eeprom16(struct mv88e6xxx_chip *chip,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ unsigned int offset = eeprom->offset;
+ unsigned int len = eeprom->len;
+ u16 val;
+ int err;
+
+ /* Ensure the RO WriteEn bit is set */
+ err = mv88e6xxx_read(chip, REG_GLOBAL2, GLOBAL2_EEPROM_CMD, &val);
+ if (err)
+ return err;
+
+ if (!(val & GLOBAL2_EEPROM_CMD_WRITE_EN))
+ return -EROFS;
+
+ eeprom->len = 0;
+
+ if (offset & 1) {
+ err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
+ if (err)
+ return err;
+
+ val = (*data++ << 8) | (val & 0xff);
+
+ err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
+ if (err)
+ return err;
+
+ offset++;
+ len--;
+ eeprom->len++;
+ }
+
+ while (len >= 2) {
+ val = *data++;
+ val |= *data++ << 8;
+
+ err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
+ if (err)
+ return err;
+
+ offset += 2;
+ len -= 2;
+ eeprom->len += 2;
+ }
+
+ if (len) {
+ err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
+ if (err)
+ return err;
+
+ val = (val & 0xff00) | *data++;
+
+ err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
+ if (err)
+ return err;
+
+ offset++;
+ len--;
+ eeprom->len++;
+ }
+
+ return 0;
+}
+
+static int mv88e6xxx_set_eeprom(struct dsa_switch *ds,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ struct mv88e6xxx_chip *chip = ds_to_priv(ds);
+ int err;
+
+ if (eeprom->magic != 0xc3ec4951)
+ return -EINVAL;
+
+ mutex_lock(&chip->reg_lock);
+
+ if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_EEPROM16))
+ err = mv88e6xxx_set_eeprom16(chip, eeprom, data);
+ else
+ err = -EOPNOTSUPP;
+
+ mutex_unlock(&chip->reg_lock);
+
+ return err;
+}
+
static const struct mv88e6xxx_info mv88e6xxx_table[] = {
[MV88E6085] = {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6085,
@@ -3536,6 +3635,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.num_databases = 4096,
.num_ports = 10,
.port_base_addr = 0x10,
+ .age_time_coeff = 15000,
.flags = MV88E6XXX_FLAGS_FAMILY_6097,
},
@@ -3546,6 +3646,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.num_databases = 256,
.num_ports = 11,
.port_base_addr = 0x10,
+ .age_time_coeff = 15000,
.flags = MV88E6XXX_FLAGS_FAMILY_6095,
},
@@ -3556,6 +3657,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.num_databases = 4096,
.num_ports = 3,
.port_base_addr = 0x10,
+ .age_time_coeff = 15000,
.flags = MV88E6XXX_FLAGS_FAMILY_6165,
},
@@ -3566,6 +3668,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.num_databases = 256,
.num_ports = 8,
.port_base_addr = 0x10,
+ .age_time_coeff = 15000,
.flags = MV88E6XXX_FLAGS_FAMILY_6185,
},
@@ -3576,6 +3679,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.num_databases = 4096,
.num_ports = 6,
.port_base_addr = 0x10,
+ .age_time_coeff = 15000,
.flags = MV88E6XXX_FLAGS_FAMILY_6165,
},
@@ -3586,6 +3690,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.num_databases = 4096,
.num_ports = 6,
.port_base_addr = 0x10,
+ .age_time_coeff = 15000,
.flags = MV88E6XXX_FLAGS_FAMILY_6165,
},
@@ -3596,6 +3701,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.num_databases = 4096,
.num_ports = 7,
.port_base_addr = 0x10,
+ .age_time_coeff = 15000,
.flags = MV88E6XXX_FLAGS_FAMILY_6351,
},
@@ -3606,6 +3712,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.num_databases = 4096,
.num_ports = 7,
.port_base_addr = 0x10,
+ .age_time_coeff = 15000,
.flags = MV88E6XXX_FLAGS_FAMILY_6352,
},
@@ -3616,6 +3723,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.num_databases = 4096,
.num_ports = 7,
.port_base_addr = 0x10,
+ .age_time_coeff = 15000,
.flags = MV88E6XXX_FLAGS_FAMILY_6351,
},
@@ -3626,6 +3734,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.num_databases = 4096,
.num_ports = 7,
.port_base_addr = 0x10,
+ .age_time_coeff = 15000,
.flags = MV88E6XXX_FLAGS_FAMILY_6352,
},
@@ -3636,6 +3745,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.num_databases = 256,
.num_ports = 10,
.port_base_addr = 0x10,
+ .age_time_coeff = 15000,
.flags = MV88E6XXX_FLAGS_FAMILY_6185,
},
@@ -3646,6 +3756,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.num_databases = 4096,
.num_ports = 7,
.port_base_addr = 0x10,
+ .age_time_coeff = 15000,
.flags = MV88E6XXX_FLAGS_FAMILY_6352,
},
@@ -3656,6 +3767,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.num_databases = 4096,
.num_ports = 7,
.port_base_addr = 0x10,
+ .age_time_coeff = 15000,
.flags = MV88E6XXX_FLAGS_FAMILY_6320,
},
@@ -3666,6 +3778,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.num_databases = 4096,
.num_ports = 7,
.port_base_addr = 0x10,
+ .age_time_coeff = 15000,
.flags = MV88E6XXX_FLAGS_FAMILY_6320,
},
@@ -3676,6 +3789,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.num_databases = 4096,
.num_ports = 7,
.port_base_addr = 0x10,
+ .age_time_coeff = 15000,
.flags = MV88E6XXX_FLAGS_FAMILY_6351,
},
@@ -3686,6 +3800,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.num_databases = 4096,
.num_ports = 7,
.port_base_addr = 0x10,
+ .age_time_coeff = 15000,
.flags = MV88E6XXX_FLAGS_FAMILY_6351,
},
@@ -3696,6 +3811,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.num_databases = 4096,
.num_ports = 7,
.port_base_addr = 0x10,
+ .age_time_coeff = 15000,
.flags = MV88E6XXX_FLAGS_FAMILY_6352,
},
};
@@ -3714,12 +3830,15 @@ static const struct mv88e6xxx_info *mv88e6xxx_lookup_info(unsigned int prod_num)
static int mv88e6xxx_detect(struct mv88e6xxx_chip *chip)
{
const struct mv88e6xxx_info *info;
- int id, prod_num, rev;
+ unsigned int prod_num, rev;
+ u16 id;
+ int err;
- id = mv88e6xxx_reg_read(chip, chip->info->port_base_addr,
- PORT_SWITCH_ID);
- if (id < 0)
- return id;
+ mutex_lock(&chip->reg_lock);
+ err = mv88e6xxx_port_read(chip, 0, PORT_SWITCH_ID, &id);
+ mutex_unlock(&chip->reg_lock);
+ if (err)
+ return err;
prod_num = (id & 0xfff0) >> 4;
rev = id & 0x000f;
@@ -3834,6 +3953,7 @@ static struct dsa_switch_driver mv88e6xxx_switch_driver = {
.set_eeprom = mv88e6xxx_set_eeprom,
.get_regs_len = mv88e6xxx_get_regs_len,
.get_regs = mv88e6xxx_get_regs,
+ .set_ageing_time = mv88e6xxx_set_ageing_time,
.port_bridge_join = mv88e6xxx_port_bridge_join,
.port_bridge_leave = mv88e6xxx_port_bridge_leave,
.port_stp_state_set = mv88e6xxx_port_stp_state_set,
@@ -3903,7 +4023,7 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev)
if (IS_ERR(chip->reset))
return PTR_ERR(chip->reset);
- if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEPROM) &&
+ if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_EEPROM16) &&
!of_property_read_u32(np, "eeprom-length", &eeprom_len))
chip->eeprom_len = eeprom_len;