diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 5cb06f7673af2fc011dc13ded81ef91a561caffb..9ba21738569a2c8682f79ee2dd9a1dcecb116f23 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -216,6 +216,32 @@ static int mv88e6xxx_write(struct mv88e6xxx_chip *chip, return 0; } +/* 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 i, err; + + /* Wait until the previous operation is completed */ + for (i = 0; i < 16; ++i) { + err = mv88e6xxx_read(chip, addr, reg, &val); + if (err) + return err; + + if (!(val & BIT(15))) + break; + } + + if (i == 16) + return -ETIMEDOUT; + + /* Set the Update bit to trigger a write operation */ + val = BIT(15) | update; + + return mv88e6xxx_write(chip, addr, reg, val); +} + static int _mv88e6xxx_reg_read(struct mv88e6xxx_chip *chip, int addr, int reg) { u16 val; @@ -257,68 +283,6 @@ static int mv88e6xxx_reg_write(struct mv88e6xxx_chip *chip, int addr, return ret; } -static int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr) -{ - struct mv88e6xxx_chip *chip = ds_to_priv(ds); - 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]); - 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; -} - -static int mv88e6xxx_set_addr(struct dsa_switch *ds, u8 *addr) -{ - 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); -} - static int mv88e6xxx_mdio_read_direct(struct mv88e6xxx_chip *chip, int addr, int regnum) { @@ -1460,9 +1424,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 +2359,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 +2374,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 +2387,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 +2492,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 +2534,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 +2558,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 */ @@ -3016,13 +2957,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 +3052,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 +3106,234 @@ 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; +} - if (i != ds->index && i < DSA_MAX_SWITCHES) - nexthop = ds->rtable[i] & 0x1f; +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); +} + +static int mv88e6xxx_g2_set_device_mapping(struct mv88e6xxx_chip *chip) +{ + int target, port; + int err; - err = _mv88e6xxx_reg_write( - chip, REG_GLOBAL2, - GLOBAL2_DEVICE_MAPPING, - GLOBAL2_DEVICE_MAPPING_UPDATE | - (i << GLOBAL2_DEVICE_MAPPING_TARGET_SHIFT) | nexthop); + /* 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_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; - /* Initialise cross-chip port VLAN table to reset - * defaults. + 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_setup(struct mv88e6xxx_chip *chip) +{ + u16 reg; + int err; + + 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) @@ -3239,12 +3354,21 @@ static int mv88e6xxx_setup(struct dsa_switch *ds) 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 +3379,24 @@ static int mv88e6xxx_setup(struct dsa_switch *ds) 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) { @@ -3536,6 +3678,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 +3689,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 +3700,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 +3711,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 +3722,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 +3733,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 +3744,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 +3755,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 +3766,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 +3777,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 +3788,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 +3799,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 +3810,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 +3821,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 +3832,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 +3843,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 +3854,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, }, }; @@ -3834,6 +3993,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, diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index 83f06620133da7ff972bfc5a01e0a37809cd3cbb..899ca1d2afc34d1c643a153d4b7772fdd667e12f 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -294,15 +294,24 @@ #define GLOBAL2_TRUNK_MASK 0x07 #define GLOBAL2_TRUNK_MASK_UPDATE BIT(15) #define GLOBAL2_TRUNK_MASK_NUM_SHIFT 12 +#define GLOBAL2_TRUNK_MASK_HASK BIT(11) #define GLOBAL2_TRUNK_MAPPING 0x08 #define GLOBAL2_TRUNK_MAPPING_UPDATE BIT(15) #define GLOBAL2_TRUNK_MAPPING_ID_SHIFT 11 -#define GLOBAL2_INGRESS_OP 0x09 -#define GLOBAL2_INGRESS_DATA 0x0a +#define GLOBAL2_IRL_CMD 0x09 +#define GLOBAL2_IRL_CMD_BUSY BIT(15) +#define GLOBAL2_IRL_CMD_OP_INIT_ALL ((0x001 << 12) | GLOBAL2_IRL_CMD_BUSY) +#define GLOBAL2_IRL_CMD_OP_INIT_SEL ((0x010 << 12) | GLOBAL2_IRL_CMD_BUSY) +#define GLOBAL2_IRL_CMD_OP_WRITE_SEL ((0x011 << 12) | GLOBAL2_IRL_CMD_BUSY) +#define GLOBAL2_IRL_CMD_OP_READ_SEL ((0x100 << 12) | GLOBAL2_IRL_CMD_BUSY) +#define GLOBAL2_IRL_DATA 0x0a #define GLOBAL2_PVT_ADDR 0x0b +#define GLOBAL2_PVT_ADDR_BUSY BIT(15) +#define GLOBAL2_PVT_ADDR_OP_INIT_ONES ((0x01 << 12) | GLOBAL2_PVT_ADDR_BUSY) +#define GLOBAL2_PVT_ADDR_OP_WRITE_PVLAN ((0x03 << 12) | GLOBAL2_PVT_ADDR_BUSY) +#define GLOBAL2_PVT_ADDR_OP_READ ((0x04 << 12) | GLOBAL2_PVT_ADDR_BUSY) #define GLOBAL2_PVT_DATA 0x0c #define GLOBAL2_SWITCH_MAC 0x0d -#define GLOBAL2_SWITCH_MAC_BUSY BIT(15) #define GLOBAL2_ATU_STATS 0x0e #define GLOBAL2_PRIO_OVERRIDE 0x0f #define GLOBAL2_PRIO_OVERRIDE_FORCE_SNOOP BIT(7) @@ -374,11 +383,6 @@ enum mv88e6xxx_family { }; enum mv88e6xxx_cap { - /* Address Translation Unit. - * The ATU is used to lookup and learn MAC addresses. See GLOBAL_ATU_OP. - */ - MV88E6XXX_CAP_ATU, - /* Energy Efficient Ethernet. */ MV88E6XXX_CAP_EEE, @@ -388,17 +392,25 @@ enum mv88e6xxx_cap { */ MV88E6XXX_CAP_EEPROM, + /* Switch Global 2 Registers. + * The device contains a second set of global 16-bit registers. + */ + MV88E6XXX_CAP_GLOBAL2, + MV88E6XXX_CAP_G2_MGMT_EN_2X, /* (0x02) MGMT Enable Register 2x */ + MV88E6XXX_CAP_G2_MGMT_EN_0X, /* (0x03) MGMT Enable Register 0x */ + MV88E6XXX_CAP_G2_IRL_CMD, /* (0x09) Ingress Rate Command */ + MV88E6XXX_CAP_G2_IRL_DATA, /* (0x0a) Ingress Rate Data */ + MV88E6XXX_CAP_G2_PVT_ADDR, /* (0x0b) Cross Chip Port VLAN Addr */ + MV88E6XXX_CAP_G2_PVT_DATA, /* (0x0c) Cross Chip Port VLAN Data */ + MV88E6XXX_CAP_G2_SWITCH_MAC, /* (0x0d) Switch MAC/WoL/WoF */ + MV88E6XXX_CAP_G2_POT, /* (0x0f) Priority Override Table */ + /* Multi-chip Addressing Mode. * Some chips require an indirect SMI access when their SMI device * address is not zero. See SMI_CMD and SMI_DATA. */ MV88E6XXX_CAP_MULTI_CHIP, - /* Port State Filtering for 802.1D Spanning Tree. - * See PORT_CONTROL_STATE_* values in the PORT_CONTROL register. - */ - MV88E6XXX_CAP_PORTSTATE, - /* PHY Polling Unit. * See GLOBAL_CONTROL_PPU_ENABLE and GLOBAL_STATUS_PPU_POLLING. */ @@ -417,25 +429,12 @@ enum mv88e6xxx_cap { */ MV88E6XXX_CAP_STU, - /* Switch MAC/WoL/WoF register. - * This requires an indirect access to set the switch MAC address - * through GLOBAL2_SWITCH_MAC, otherwise GLOBAL_MAC_01, GLOBAL_MAC_23, - * and GLOBAL_MAC_45 are used with a direct access. - */ - MV88E6XXX_CAP_SWITCH_MAC_WOL_WOF, - /* Internal temperature sensor. * Available from any enabled port's PHY register 26, page 6. */ MV88E6XXX_CAP_TEMP, MV88E6XXX_CAP_TEMP_LIMIT, - /* In-chip Port Based VLANs. - * Each port VLANTable register (see PORT_BASE_VLAN) is used to restrict - * the output (or egress) ports to which it is allowed to send frames. - */ - MV88E6XXX_CAP_VLANTABLE, - /* VLAN Table Unit. * The VTU is used to program 802.1Q VLANs. See GLOBAL_VTU_OP. */ @@ -443,90 +442,124 @@ enum mv88e6xxx_cap { }; /* Bitmask of capabilities */ -#define MV88E6XXX_FLAG_ATU BIT(MV88E6XXX_CAP_ATU) #define MV88E6XXX_FLAG_EEE BIT(MV88E6XXX_CAP_EEE) #define MV88E6XXX_FLAG_EEPROM BIT(MV88E6XXX_CAP_EEPROM) +#define MV88E6XXX_FLAG_GLOBAL2 BIT(MV88E6XXX_CAP_GLOBAL2) +#define MV88E6XXX_FLAG_G2_MGMT_EN_2X BIT(MV88E6XXX_CAP_G2_MGMT_EN_2X) +#define MV88E6XXX_FLAG_G2_MGMT_EN_0X BIT(MV88E6XXX_CAP_G2_MGMT_EN_0X) +#define MV88E6XXX_FLAG_G2_IRL_CMD BIT(MV88E6XXX_CAP_G2_IRL_CMD) +#define MV88E6XXX_FLAG_G2_IRL_DATA BIT(MV88E6XXX_CAP_G2_IRL_DATA) +#define MV88E6XXX_FLAG_G2_PVT_ADDR BIT(MV88E6XXX_CAP_G2_PVT_ADDR) +#define MV88E6XXX_FLAG_G2_PVT_DATA BIT(MV88E6XXX_CAP_G2_PVT_DATA) +#define MV88E6XXX_FLAG_G2_SWITCH_MAC BIT(MV88E6XXX_CAP_G2_SWITCH_MAC) +#define MV88E6XXX_FLAG_G2_POT BIT(MV88E6XXX_CAP_G2_POT) #define MV88E6XXX_FLAG_MULTI_CHIP BIT(MV88E6XXX_CAP_MULTI_CHIP) -#define MV88E6XXX_FLAG_PORTSTATE BIT(MV88E6XXX_CAP_PORTSTATE) #define MV88E6XXX_FLAG_PPU BIT(MV88E6XXX_CAP_PPU) #define MV88E6XXX_FLAG_PPU_ACTIVE BIT(MV88E6XXX_CAP_PPU_ACTIVE) #define MV88E6XXX_FLAG_SMI_PHY BIT(MV88E6XXX_CAP_SMI_PHY) #define MV88E6XXX_FLAG_STU BIT(MV88E6XXX_CAP_STU) -#define MV88E6XXX_FLAG_SWITCH_MAC BIT(MV88E6XXX_CAP_SWITCH_MAC_WOL_WOF) #define MV88E6XXX_FLAG_TEMP BIT(MV88E6XXX_CAP_TEMP) #define MV88E6XXX_FLAG_TEMP_LIMIT BIT(MV88E6XXX_CAP_TEMP_LIMIT) -#define MV88E6XXX_FLAG_VLANTABLE BIT(MV88E6XXX_CAP_VLANTABLE) #define MV88E6XXX_FLAG_VTU BIT(MV88E6XXX_CAP_VTU) +/* Ingress Rate Limit unit */ +#define MV88E6XXX_FLAGS_IRL \ + (MV88E6XXX_FLAG_G2_IRL_CMD | \ + MV88E6XXX_FLAG_G2_IRL_DATA) + +/* Cross-chip Port VLAN Table */ +#define MV88E6XXX_FLAGS_PVT \ + (MV88E6XXX_FLAG_G2_PVT_ADDR | \ + MV88E6XXX_FLAG_G2_PVT_DATA) + #define MV88E6XXX_FLAGS_FAMILY_6095 \ - (MV88E6XXX_FLAG_ATU | \ + (MV88E6XXX_FLAG_GLOBAL2 | \ + MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAG_MULTI_CHIP | \ MV88E6XXX_FLAG_PPU | \ - MV88E6XXX_FLAG_VLANTABLE | \ MV88E6XXX_FLAG_VTU) #define MV88E6XXX_FLAGS_FAMILY_6097 \ - (MV88E6XXX_FLAG_ATU | \ + (MV88E6XXX_FLAG_GLOBAL2 | \ + MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ + MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ + MV88E6XXX_FLAG_G2_POT | \ MV88E6XXX_FLAG_MULTI_CHIP | \ MV88E6XXX_FLAG_PPU | \ MV88E6XXX_FLAG_STU | \ - MV88E6XXX_FLAG_VLANTABLE | \ - MV88E6XXX_FLAG_VTU) + MV88E6XXX_FLAG_VTU | \ + MV88E6XXX_FLAGS_IRL | \ + MV88E6XXX_FLAGS_PVT) #define MV88E6XXX_FLAGS_FAMILY_6165 \ - (MV88E6XXX_FLAG_MULTI_CHIP | \ + (MV88E6XXX_FLAG_GLOBAL2 | \ + MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ + MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ + MV88E6XXX_FLAG_G2_SWITCH_MAC | \ + MV88E6XXX_FLAG_G2_POT | \ + MV88E6XXX_FLAG_MULTI_CHIP | \ MV88E6XXX_FLAG_STU | \ - MV88E6XXX_FLAG_SWITCH_MAC | \ MV88E6XXX_FLAG_TEMP | \ - MV88E6XXX_FLAG_VTU) + MV88E6XXX_FLAG_VTU | \ + MV88E6XXX_FLAGS_IRL | \ + MV88E6XXX_FLAGS_PVT) #define MV88E6XXX_FLAGS_FAMILY_6185 \ - (MV88E6XXX_FLAG_ATU | \ + (MV88E6XXX_FLAG_GLOBAL2 | \ + MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAG_MULTI_CHIP | \ MV88E6XXX_FLAG_PPU | \ - MV88E6XXX_FLAG_VLANTABLE | \ MV88E6XXX_FLAG_VTU) #define MV88E6XXX_FLAGS_FAMILY_6320 \ - (MV88E6XXX_FLAG_ATU | \ - MV88E6XXX_FLAG_EEE | \ + (MV88E6XXX_FLAG_EEE | \ MV88E6XXX_FLAG_EEPROM | \ + MV88E6XXX_FLAG_GLOBAL2 | \ + MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ + MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ + MV88E6XXX_FLAG_G2_SWITCH_MAC | \ + MV88E6XXX_FLAG_G2_POT | \ MV88E6XXX_FLAG_MULTI_CHIP | \ - MV88E6XXX_FLAG_PORTSTATE | \ MV88E6XXX_FLAG_PPU_ACTIVE | \ MV88E6XXX_FLAG_SMI_PHY | \ - MV88E6XXX_FLAG_SWITCH_MAC | \ MV88E6XXX_FLAG_TEMP | \ MV88E6XXX_FLAG_TEMP_LIMIT | \ - MV88E6XXX_FLAG_VLANTABLE | \ - MV88E6XXX_FLAG_VTU) + MV88E6XXX_FLAG_VTU | \ + MV88E6XXX_FLAGS_IRL | \ + MV88E6XXX_FLAGS_PVT) #define MV88E6XXX_FLAGS_FAMILY_6351 \ - (MV88E6XXX_FLAG_ATU | \ + (MV88E6XXX_FLAG_GLOBAL2 | \ + MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ + MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ + MV88E6XXX_FLAG_G2_SWITCH_MAC | \ + MV88E6XXX_FLAG_G2_POT | \ MV88E6XXX_FLAG_MULTI_CHIP | \ - MV88E6XXX_FLAG_PORTSTATE | \ MV88E6XXX_FLAG_PPU_ACTIVE | \ MV88E6XXX_FLAG_SMI_PHY | \ MV88E6XXX_FLAG_STU | \ - MV88E6XXX_FLAG_SWITCH_MAC | \ MV88E6XXX_FLAG_TEMP | \ - MV88E6XXX_FLAG_VLANTABLE | \ - MV88E6XXX_FLAG_VTU) + MV88E6XXX_FLAG_VTU | \ + MV88E6XXX_FLAGS_IRL | \ + MV88E6XXX_FLAGS_PVT) #define MV88E6XXX_FLAGS_FAMILY_6352 \ - (MV88E6XXX_FLAG_ATU | \ - MV88E6XXX_FLAG_EEE | \ + (MV88E6XXX_FLAG_EEE | \ MV88E6XXX_FLAG_EEPROM | \ + MV88E6XXX_FLAG_GLOBAL2 | \ + MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ + MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ + MV88E6XXX_FLAG_G2_SWITCH_MAC | \ + MV88E6XXX_FLAG_G2_POT | \ MV88E6XXX_FLAG_MULTI_CHIP | \ - MV88E6XXX_FLAG_PORTSTATE | \ MV88E6XXX_FLAG_PPU_ACTIVE | \ MV88E6XXX_FLAG_SMI_PHY | \ MV88E6XXX_FLAG_STU | \ - MV88E6XXX_FLAG_SWITCH_MAC | \ MV88E6XXX_FLAG_TEMP | \ MV88E6XXX_FLAG_TEMP_LIMIT | \ - MV88E6XXX_FLAG_VLANTABLE | \ - MV88E6XXX_FLAG_VTU) + MV88E6XXX_FLAG_VTU | \ + MV88E6XXX_FLAGS_IRL | \ + MV88E6XXX_FLAGS_PVT) struct mv88e6xxx_info { enum mv88e6xxx_family family; @@ -535,6 +568,7 @@ struct mv88e6xxx_info { unsigned int num_databases; unsigned int num_ports; unsigned int port_base_addr; + unsigned int age_time_coeff; unsigned long flags; }; diff --git a/include/net/dsa.h b/include/net/dsa.h index 52ab18bc2b0d8eb097c77b90651549eb12edba48..2217a3f817f8e5072954941d34d089be28b1e136 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -141,6 +141,7 @@ struct dsa_switch_tree { struct dsa_port { struct net_device *netdev; struct device_node *dn; + unsigned int ageing_time; }; struct dsa_switch { @@ -329,6 +330,7 @@ struct dsa_switch_driver { /* * Bridge integration */ + int (*set_ageing_time)(struct dsa_switch *ds, unsigned int msecs); int (*port_bridge_join)(struct dsa_switch *ds, int port, struct net_device *bridge); void (*port_bridge_leave)(struct dsa_switch *ds, int port); diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 7236eb26dc97a532c36c6aae7bad9efec1d1d41c..fc91967452253b54c4c692f77d67ba9b08e59e74 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -333,6 +333,44 @@ static int dsa_slave_vlan_filtering(struct net_device *dev, return 0; } +static int dsa_fastest_ageing_time(struct dsa_switch *ds, + unsigned int ageing_time) +{ + int i; + + for (i = 0; i < DSA_MAX_PORTS; ++i) { + struct dsa_port *dp = &ds->ports[i]; + + if (dp && dp->ageing_time && dp->ageing_time < ageing_time) + ageing_time = dp->ageing_time; + } + + return ageing_time; +} + +static int dsa_slave_ageing_time(struct net_device *dev, + const struct switchdev_attr *attr, + struct switchdev_trans *trans) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + struct dsa_switch *ds = p->parent; + unsigned long ageing_jiffies = clock_t_to_jiffies(attr->u.ageing_time); + unsigned int ageing_time = jiffies_to_msecs(ageing_jiffies); + + /* bridge skips -EOPNOTSUPP, so skip the prepare phase */ + if (switchdev_trans_ph_prepare(trans)) + return 0; + + /* Keep the fastest ageing time in case of multiple bridges */ + ds->ports[p->port].ageing_time = ageing_time; + ageing_time = dsa_fastest_ageing_time(ds, ageing_time); + + if (ds->drv->set_ageing_time) + return ds->drv->set_ageing_time(ds, ageing_time); + + return 0; +} + static int dsa_slave_port_attr_set(struct net_device *dev, const struct switchdev_attr *attr, struct switchdev_trans *trans) @@ -346,6 +384,9 @@ static int dsa_slave_port_attr_set(struct net_device *dev, case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING: ret = dsa_slave_vlan_filtering(dev, attr, trans); break; + case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME: + ret = dsa_slave_ageing_time(dev, attr, trans); + break; default: ret = -EOPNOTSUPP; break;