Commit 6e338048 authored by David S. Miller's avatar David S. Miller

Merge branch 'mv88e6131-hw-bridging-6185'

Vivien Didelot says:

====================
net: dsa: mv88e6131: HW bridging support for 6185

All packets passing through a switch of the 6185 family are currently all
directed to the CPU port. This means that port bridging is software driven.

To enable hardware bridging for this switch family, we need to implement the
port mapping operations, the FDB operations, and optionally the VLAN operations
(for 802.1Q and VLAN filtering aware systems).

However this family only has 256 FDBs indexed by 8-bit identifiers, opposed to
4096 FDBs with 12-bit identifiers for other families such as 6352. It also
doesn't have dedicated FID registers for ATU and VTU operations.

This patchset fixes these differences, and enable hardware bridging for 6185.

Changes v1 -> v2:
 - Describe the different numbers of databases and prefer a feature-based logic
   over the current ID/family-based logic.
====================
Tested-by: default avatarAndrew Lunn <andrew@lunn.ch>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 9ef280c6 26892ffc
...@@ -169,6 +169,17 @@ struct dsa_switch_driver mv88e6131_switch_driver = { ...@@ -169,6 +169,17 @@ struct dsa_switch_driver mv88e6131_switch_driver = {
.get_ethtool_stats = mv88e6xxx_get_ethtool_stats, .get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
.get_sset_count = mv88e6xxx_get_sset_count, .get_sset_count = mv88e6xxx_get_sset_count,
.adjust_link = mv88e6xxx_adjust_link, .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"); MODULE_ALIAS("platform:mv88e6085");
......
...@@ -482,6 +482,50 @@ static bool mv88e6xxx_6352_family(struct dsa_switch *ds) ...@@ -482,6 +482,50 @@ static bool mv88e6xxx_6352_family(struct dsa_switch *ds)
return false; 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? */
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? */
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 /* 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 * phy. However, in the case of a fixed link phy, we force the port
* settings from the fixed link settings. * settings from the fixed link settings.
...@@ -951,10 +995,30 @@ int mv88e6xxx_set_eee(struct dsa_switch *ds, int port, ...@@ -951,10 +995,30 @@ int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
return ret; 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; int ret;
if (mv88e6xxx_has_fid_reg(ds)) {
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); ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_OP, cmd);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -1001,11 +1065,6 @@ static int _mv88e6xxx_atu_flush_move(struct dsa_switch *ds, ...@@ -1001,11 +1065,6 @@ static int _mv88e6xxx_atu_flush_move(struct dsa_switch *ds,
return err; return err;
if (entry->fid) { 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 : op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB :
GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB; GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB;
} else { } else {
...@@ -1013,7 +1072,7 @@ static int _mv88e6xxx_atu_flush_move(struct dsa_switch *ds, ...@@ -1013,7 +1072,7 @@ static int _mv88e6xxx_atu_flush_move(struct dsa_switch *ds,
GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC; 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) static int _mv88e6xxx_atu_flush(struct dsa_switch *ds, u16 fid, bool static_too)
...@@ -1321,15 +1380,27 @@ static int _mv88e6xxx_vtu_getnext(struct dsa_switch *ds, ...@@ -1321,15 +1380,27 @@ static int _mv88e6xxx_vtu_getnext(struct dsa_switch *ds,
if (ret < 0) if (ret < 0)
return ret; return ret;
if (mv88e6xxx_6097_family(ds) || mv88e6xxx_6165_family(ds) || if (mv88e6xxx_has_fid_reg(ds)) {
mv88e6xxx_6351_family(ds) || mv88e6xxx_6352_family(ds)) {
ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL,
GLOBAL_VTU_FID); GLOBAL_VTU_FID);
if (ret < 0) if (ret < 0)
return ret; return ret;
next.fid = ret & GLOBAL_VTU_FID_MASK; 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)) {
ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL,
GLOBAL_VTU_SID); GLOBAL_VTU_SID);
if (ret < 0) if (ret < 0)
...@@ -1397,6 +1468,7 @@ int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port, ...@@ -1397,6 +1468,7 @@ int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port,
static int _mv88e6xxx_vtu_loadpurge(struct dsa_switch *ds, static int _mv88e6xxx_vtu_loadpurge(struct dsa_switch *ds,
struct mv88e6xxx_vtu_stu_entry *entry) struct mv88e6xxx_vtu_stu_entry *entry)
{ {
u16 op = GLOBAL_VTU_OP_VTU_LOAD_PURGE;
u16 reg = 0; u16 reg = 0;
int ret; int ret;
...@@ -1412,17 +1484,24 @@ static int _mv88e6xxx_vtu_loadpurge(struct dsa_switch *ds, ...@@ -1412,17 +1484,24 @@ static int _mv88e6xxx_vtu_loadpurge(struct dsa_switch *ds,
if (ret < 0) if (ret < 0)
return ret; return ret;
if (mv88e6xxx_6097_family(ds) || mv88e6xxx_6165_family(ds) || if (mv88e6xxx_has_stu(ds)) {
mv88e6xxx_6351_family(ds) || mv88e6xxx_6352_family(ds)) {
reg = entry->sid & GLOBAL_VTU_SID_MASK; reg = entry->sid & GLOBAL_VTU_SID_MASK;
ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_SID, reg); ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_SID, reg);
if (ret < 0) if (ret < 0)
return ret; return ret;
}
if (mv88e6xxx_has_fid_reg(ds)) {
reg = entry->fid & GLOBAL_VTU_FID_MASK; reg = entry->fid & GLOBAL_VTU_FID_MASK;
ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_FID, reg); ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_FID, reg);
if (ret < 0) if (ret < 0)
return ret; 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; reg = GLOBAL_VTU_VID_VALID;
...@@ -1432,7 +1511,7 @@ static int _mv88e6xxx_vtu_loadpurge(struct dsa_switch *ds, ...@@ -1432,7 +1511,7 @@ static int _mv88e6xxx_vtu_loadpurge(struct dsa_switch *ds,
if (ret < 0) if (ret < 0)
return ret; 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, static int _mv88e6xxx_stu_getnext(struct dsa_switch *ds, u8 sid,
...@@ -1511,9 +1590,17 @@ static int _mv88e6xxx_stu_loadpurge(struct dsa_switch *ds, ...@@ -1511,9 +1590,17 @@ static int _mv88e6xxx_stu_loadpurge(struct dsa_switch *ds,
static int _mv88e6xxx_port_fid(struct dsa_switch *ds, int port, u16 *new, static int _mv88e6xxx_port_fid(struct dsa_switch *ds, int port, u16 *new,
u16 *old) u16 *old)
{ {
u16 upper_mask;
u16 fid; u16 fid;
int ret; int ret;
if (mv88e6xxx_num_databases(ds) == 4096)
upper_mask = 0xff;
else if (mv88e6xxx_num_databases(ds) == 256)
upper_mask = 0xf;
else
return -EOPNOTSUPP;
/* Port's default FID bits 3:0 are located in reg 0x06, offset 12 */ /* 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); ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_BASE_VLAN);
if (ret < 0) if (ret < 0)
...@@ -1536,11 +1623,11 @@ static int _mv88e6xxx_port_fid(struct dsa_switch *ds, int port, u16 *new, ...@@ -1536,11 +1623,11 @@ static int _mv88e6xxx_port_fid(struct dsa_switch *ds, int port, u16 *new,
if (ret < 0) if (ret < 0)
return ret; return ret;
fid |= (ret & PORT_CONTROL_1_FID_11_4_MASK) << 4; fid |= (ret & upper_mask) << 4;
if (new) { if (new) {
ret &= ~PORT_CONTROL_1_FID_11_4_MASK; ret &= ~upper_mask;
ret |= (*new >> 4) & PORT_CONTROL_1_FID_11_4_MASK; ret |= (*new >> 4) & upper_mask;
ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_CONTROL_1, ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_CONTROL_1,
ret); ret);
...@@ -1604,7 +1691,7 @@ static int _mv88e6xxx_fid_new(struct dsa_switch *ds, u16 *fid) ...@@ -1604,7 +1691,7 @@ static int _mv88e6xxx_fid_new(struct dsa_switch *ds, u16 *fid)
* databases are not needed. Return the next positive available. * databases are not needed. Return the next positive available.
*/ */
*fid = find_next_zero_bit(fid_bitmap, MV88E6XXX_N_FID, 1); *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; return -ENOSPC;
/* Clear the database */ /* Clear the database */
...@@ -1965,11 +2052,7 @@ static int _mv88e6xxx_atu_load(struct dsa_switch *ds, ...@@ -1965,11 +2052,7 @@ static int _mv88e6xxx_atu_load(struct dsa_switch *ds,
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_FID, entry->fid); return _mv88e6xxx_atu_cmd(ds, entry->fid, GLOBAL_ATU_OP_LOAD_DB);
if (ret < 0)
return ret;
return _mv88e6xxx_atu_cmd(ds, GLOBAL_ATU_OP_LOAD_DB);
} }
static int _mv88e6xxx_port_fdb_load(struct dsa_switch *ds, int port, static int _mv88e6xxx_port_fdb_load(struct dsa_switch *ds, int port,
...@@ -2052,11 +2135,7 @@ static int _mv88e6xxx_atu_getnext(struct dsa_switch *ds, u16 fid, ...@@ -2052,11 +2135,7 @@ static int _mv88e6xxx_atu_getnext(struct dsa_switch *ds, u16 fid,
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_FID, fid); ret = _mv88e6xxx_atu_cmd(ds, fid, GLOBAL_ATU_OP_GET_NEXT_DB);
if (ret < 0)
return ret;
ret = _mv88e6xxx_atu_cmd(ds, GLOBAL_ATU_OP_GET_NEXT_DB);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -2444,7 +2523,8 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) ...@@ -2444,7 +2523,8 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port)
reg = 0; reg = 0;
if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) || if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_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; reg = PORT_CONTROL_2_MAP_DA;
if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) || if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment