From 2e7bd5ef98030999eaf4bf101707e595432fc8c2 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 31 Mar 2016 16:53:41 -0400 Subject: [PATCH 1/6] net: dsa: mv88e6xxx: protect SID register access Introduce a mv88e6xxx_has_stu() helper to protect the access to the GLOBAL_VTU_SID register, instead of checking switch families. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index 50454be86570d..29b24446bd2be 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c @@ -482,6 +482,16 @@ static bool mv88e6xxx_6352_family(struct dsa_switch *ds) return false; } +static bool mv88e6xxx_has_stu(struct dsa_switch *ds) +{ + /* Does the device have STU and dedicated SID registers for VTU ops? */ + if (mv88e6xxx_6097_family(ds) || mv88e6xxx_6165_family(ds) || + mv88e6xxx_6351_family(ds) || mv88e6xxx_6352_family(ds)) + return true; + + return false; +} + /* We expect the switch to perform auto negotiation if there is a real * phy. However, in the case of a fixed link phy, we force the port * settings from the fixed link settings. @@ -1329,7 +1339,9 @@ static int _mv88e6xxx_vtu_getnext(struct dsa_switch *ds, return ret; next.fid = ret & GLOBAL_VTU_FID_MASK; + } + if (mv88e6xxx_has_stu(ds)) { ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_VTU_SID); if (ret < 0) @@ -1412,8 +1424,7 @@ static int _mv88e6xxx_vtu_loadpurge(struct dsa_switch *ds, if (ret < 0) return ret; - if (mv88e6xxx_6097_family(ds) || mv88e6xxx_6165_family(ds) || - mv88e6xxx_6351_family(ds) || mv88e6xxx_6352_family(ds)) { + if (mv88e6xxx_has_stu(ds)) { reg = entry->sid & GLOBAL_VTU_SID_MASK; ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_SID, reg); if (ret < 0) From b426e5f7fe4cd7041b3d8bed58f1e60bba1d11bf Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 31 Mar 2016 16:53:42 -0400 Subject: [PATCH 2/6] net: dsa: mv88e6xxx: protect FID registers access Only switch families with 4096 address databases have dedicated FID registers for ATU and VTU operations. Factorize the access to the GLOBAL_ATU_FID register and introduce a mv88e6xxx_has_fid_reg() helper function to protect the access to GLOBAL_ATU_FID and GLOBAL_VTU_FID. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx.c | 42 ++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index 29b24446bd2be..ebb9ae927426e 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c @@ -482,6 +482,16 @@ static bool mv88e6xxx_6352_family(struct dsa_switch *ds) return false; } +static bool mv88e6xxx_has_fid_reg(struct dsa_switch *ds) +{ + /* Does the device have dedicated FID registers for ATU and VTU ops? */ + if (mv88e6xxx_6097_family(ds) || mv88e6xxx_6165_family(ds) || + mv88e6xxx_6351_family(ds) || mv88e6xxx_6352_family(ds)) + return true; + + return false; +} + static bool mv88e6xxx_has_stu(struct dsa_switch *ds) { /* Does the device have STU and dedicated SID registers for VTU ops? */ @@ -961,10 +971,16 @@ int mv88e6xxx_set_eee(struct dsa_switch *ds, int port, return ret; } -static int _mv88e6xxx_atu_cmd(struct dsa_switch *ds, u16 cmd) +static int _mv88e6xxx_atu_cmd(struct dsa_switch *ds, u16 fid, u16 cmd) { int ret; + if (mv88e6xxx_has_fid_reg(ds)) { + ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_FID, fid); + if (ret < 0) + return ret; + } + ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_OP, cmd); if (ret < 0) return ret; @@ -1011,11 +1027,6 @@ static int _mv88e6xxx_atu_flush_move(struct dsa_switch *ds, return err; if (entry->fid) { - err = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_FID, - entry->fid); - if (err) - return err; - op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB : GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB; } else { @@ -1023,7 +1034,7 @@ static int _mv88e6xxx_atu_flush_move(struct dsa_switch *ds, GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC; } - return _mv88e6xxx_atu_cmd(ds, op); + return _mv88e6xxx_atu_cmd(ds, entry->fid, op); } static int _mv88e6xxx_atu_flush(struct dsa_switch *ds, u16 fid, bool static_too) @@ -1331,8 +1342,7 @@ static int _mv88e6xxx_vtu_getnext(struct dsa_switch *ds, if (ret < 0) return ret; - if (mv88e6xxx_6097_family(ds) || mv88e6xxx_6165_family(ds) || - mv88e6xxx_6351_family(ds) || mv88e6xxx_6352_family(ds)) { + if (mv88e6xxx_has_fid_reg(ds)) { ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_VTU_FID); if (ret < 0) @@ -1429,7 +1439,9 @@ static int _mv88e6xxx_vtu_loadpurge(struct dsa_switch *ds, ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_SID, reg); if (ret < 0) return ret; + } + if (mv88e6xxx_has_fid_reg(ds)) { reg = entry->fid & GLOBAL_VTU_FID_MASK; ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_FID, reg); if (ret < 0) @@ -1976,11 +1988,7 @@ static int _mv88e6xxx_atu_load(struct dsa_switch *ds, if (ret < 0) return ret; - ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_FID, entry->fid); - if (ret < 0) - return ret; - - return _mv88e6xxx_atu_cmd(ds, GLOBAL_ATU_OP_LOAD_DB); + return _mv88e6xxx_atu_cmd(ds, entry->fid, GLOBAL_ATU_OP_LOAD_DB); } static int _mv88e6xxx_port_fdb_load(struct dsa_switch *ds, int port, @@ -2063,11 +2071,7 @@ static int _mv88e6xxx_atu_getnext(struct dsa_switch *ds, u16 fid, if (ret < 0) return ret; - ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_FID, fid); - if (ret < 0) - return ret; - - ret = _mv88e6xxx_atu_cmd(ds, GLOBAL_ATU_OP_GET_NEXT_DB); + ret = _mv88e6xxx_atu_cmd(ds, fid, GLOBAL_ATU_OP_GET_NEXT_DB); if (ret < 0) return ret; From f74df0be82d7d29747bfd68d955f4f573f9e5691 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 31 Mar 2016 16:53:43 -0400 Subject: [PATCH 3/6] net: dsa: mv88e6xxx: variable number of databases Marvell switch chips have different number of address databases. The code currently only supports models with 4096 databases. Such switch has dedicated FID registers for ATU and VTU operations. Models with fewer databases have their FID split in several registers. List them all but only support models with 4096 databases at the moment. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx.c | 38 +++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index ebb9ae927426e..f103319dbe141 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c @@ -482,6 +482,30 @@ static bool mv88e6xxx_6352_family(struct dsa_switch *ds) return false; } +static unsigned int mv88e6xxx_num_databases(struct dsa_switch *ds) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + + /* The following devices have 4-bit identifiers for 16 databases */ + if (ps->id == PORT_SWITCH_ID_6061) + return 16; + + /* The following devices have 6-bit identifiers for 64 databases */ + if (ps->id == PORT_SWITCH_ID_6065) + return 64; + + /* The following devices have 8-bit identifiers for 256 databases */ + if (mv88e6xxx_6095_family(ds) || mv88e6xxx_6185_family(ds)) + return 256; + + /* The following devices have 12-bit identifiers for 4096 databases */ + if (mv88e6xxx_6097_family(ds) || mv88e6xxx_6165_family(ds) || + mv88e6xxx_6351_family(ds) || mv88e6xxx_6352_family(ds)) + return 4096; + + return 0; +} + static bool mv88e6xxx_has_fid_reg(struct dsa_switch *ds) { /* Does the device have dedicated FID registers for ATU and VTU ops? */ @@ -1534,9 +1558,15 @@ static int _mv88e6xxx_stu_loadpurge(struct dsa_switch *ds, static int _mv88e6xxx_port_fid(struct dsa_switch *ds, int port, u16 *new, u16 *old) { + u16 upper_mask; u16 fid; int ret; + if (mv88e6xxx_num_databases(ds) == 4096) + upper_mask = 0xff; + else + return -EOPNOTSUPP; + /* Port's default FID bits 3:0 are located in reg 0x06, offset 12 */ ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_BASE_VLAN); if (ret < 0) @@ -1559,11 +1589,11 @@ static int _mv88e6xxx_port_fid(struct dsa_switch *ds, int port, u16 *new, if (ret < 0) return ret; - fid |= (ret & PORT_CONTROL_1_FID_11_4_MASK) << 4; + fid |= (ret & upper_mask) << 4; if (new) { - ret &= ~PORT_CONTROL_1_FID_11_4_MASK; - ret |= (*new >> 4) & PORT_CONTROL_1_FID_11_4_MASK; + ret &= ~upper_mask; + ret |= (*new >> 4) & upper_mask; ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_CONTROL_1, ret); @@ -1627,7 +1657,7 @@ static int _mv88e6xxx_fid_new(struct dsa_switch *ds, u16 *fid) * databases are not needed. Return the next positive available. */ *fid = find_next_zero_bit(fid_bitmap, MV88E6XXX_N_FID, 1); - if (unlikely(*fid == MV88E6XXX_N_FID)) + if (unlikely(*fid >= mv88e6xxx_num_databases(ds))) return -ENOSPC; /* Clear the database */ From 11ea809f1a74b006f08e7dad0b257e06c817f313 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 31 Mar 2016 16:53:44 -0400 Subject: [PATCH 4/6] net: dsa: mv88e6xxx: support 256 databases The 6185 family of devices has only 256 address databases. Their 8-bit FID for ATU and VTU operations are split into ATU Control and ATU/VTU Operation registers. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx.c | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index f103319dbe141..75a4abc595b1a 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c @@ -1003,6 +1003,20 @@ static int _mv88e6xxx_atu_cmd(struct dsa_switch *ds, u16 fid, u16 cmd) ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_FID, fid); if (ret < 0) return ret; + } else if (mv88e6xxx_num_databases(ds) == 256) { + /* ATU DBNum[7:4] are located in ATU Control 15:12 */ + ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_ATU_CONTROL); + if (ret < 0) + return ret; + + ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_CONTROL, + (ret & 0xfff) | + ((fid << 8) & 0xf000)); + if (ret < 0) + return ret; + + /* ATU DBNum[3:0] are located in ATU Operation 3:0 */ + cmd |= fid & 0xf; } ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_OP, cmd); @@ -1373,6 +1387,17 @@ static int _mv88e6xxx_vtu_getnext(struct dsa_switch *ds, return ret; next.fid = ret & GLOBAL_VTU_FID_MASK; + } else if (mv88e6xxx_num_databases(ds) == 256) { + /* VTU DBNum[7:4] are located in VTU Operation 11:8, and + * VTU DBNum[3:0] are located in VTU Operation 3:0 + */ + ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, + GLOBAL_VTU_OP); + if (ret < 0) + return ret; + + next.fid = (ret & 0xf00) >> 4; + next.fid |= ret & 0xf; } if (mv88e6xxx_has_stu(ds)) { @@ -1443,6 +1468,7 @@ int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port, static int _mv88e6xxx_vtu_loadpurge(struct dsa_switch *ds, struct mv88e6xxx_vtu_stu_entry *entry) { + u16 op = GLOBAL_VTU_OP_VTU_LOAD_PURGE; u16 reg = 0; int ret; @@ -1470,6 +1496,12 @@ static int _mv88e6xxx_vtu_loadpurge(struct dsa_switch *ds, ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_FID, reg); if (ret < 0) return ret; + } else if (mv88e6xxx_num_databases(ds) == 256) { + /* VTU DBNum[7:4] are located in VTU Operation 11:8, and + * VTU DBNum[3:0] are located in VTU Operation 3:0 + */ + op |= (entry->fid & 0xf0) << 8; + op |= entry->fid & 0xf; } reg = GLOBAL_VTU_VID_VALID; @@ -1479,7 +1511,7 @@ static int _mv88e6xxx_vtu_loadpurge(struct dsa_switch *ds, if (ret < 0) return ret; - return _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_VTU_LOAD_PURGE); + return _mv88e6xxx_vtu_cmd(ds, op); } static int _mv88e6xxx_stu_getnext(struct dsa_switch *ds, u8 sid, @@ -1564,6 +1596,8 @@ static int _mv88e6xxx_port_fid(struct dsa_switch *ds, int port, u16 *new, if (mv88e6xxx_num_databases(ds) == 4096) upper_mask = 0xff; + else if (mv88e6xxx_num_databases(ds) == 256) + upper_mask = 0xf; else return -EOPNOTSUPP; From f93dd042de08633ff481d092245f2d9b4a3cdb6a Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 31 Mar 2016 16:53:45 -0400 Subject: [PATCH 5/6] net: dsa: mv88e6xxx: map destination addresses for 6185 The 88E6185 switch also has a MapDA bit in its Port Control 2 register. When this bit is cleared, all frames are sent out to the CPU port. Set this bit to rely on address databases (ATU) hits and direct frames out of the correct ports, and thus allow hardware bridging. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index 75a4abc595b1a..0dda2817d0ecf 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c @@ -2523,7 +2523,8 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) reg = 0; if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) || mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) || - mv88e6xxx_6095_family(ds) || mv88e6xxx_6320_family(ds)) + mv88e6xxx_6095_family(ds) || mv88e6xxx_6320_family(ds) || + mv88e6xxx_6185_family(ds)) reg = PORT_CONTROL_2_MAP_DA; if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) || From 26892ffc80b4276f6f0d61232a769100023b38ab Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 31 Mar 2016 16:53:46 -0400 Subject: [PATCH 6/6] net: dsa: mv88e6131: enable hardware bridging By adding support for bridge operations, FDB operations, and optionally VLAN operations (for 802.1Q and VLAN filtering aware systems), the switch bridges ports correctly, the CPU is able to populate the hardware address databases, and thus hardware bridging becomes functional within the 88E6185 family of switches. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6131.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/net/dsa/mv88e6131.c b/drivers/net/dsa/mv88e6131.c index a92ca651c3990..24070287c2bcf 100644 --- a/drivers/net/dsa/mv88e6131.c +++ b/drivers/net/dsa/mv88e6131.c @@ -169,6 +169,17 @@ struct dsa_switch_driver mv88e6131_switch_driver = { .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, .get_sset_count = mv88e6xxx_get_sset_count, .adjust_link = mv88e6xxx_adjust_link, + .port_bridge_join = mv88e6xxx_port_bridge_join, + .port_bridge_leave = mv88e6xxx_port_bridge_leave, + .port_vlan_filtering = mv88e6xxx_port_vlan_filtering, + .port_vlan_prepare = mv88e6xxx_port_vlan_prepare, + .port_vlan_add = mv88e6xxx_port_vlan_add, + .port_vlan_del = mv88e6xxx_port_vlan_del, + .port_vlan_dump = mv88e6xxx_port_vlan_dump, + .port_fdb_prepare = mv88e6xxx_port_fdb_prepare, + .port_fdb_add = mv88e6xxx_port_fdb_add, + .port_fdb_del = mv88e6xxx_port_fdb_del, + .port_fdb_dump = mv88e6xxx_port_fdb_dump, }; MODULE_ALIAS("platform:mv88e6085");