Skip to content

Commit

Permalink
Merge branch 'mv88e6131-hw-bridging-6185'
Browse files Browse the repository at this point in the history
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: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Apr 5, 2016
2 parents 9ef280c + 26892ff commit 6e33804
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 27 deletions.
11 changes: 11 additions & 0 deletions drivers/net/dsa/mv88e6131.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
134 changes: 107 additions & 27 deletions drivers/net/dsa/mv88e6xxx.c
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,50 @@ 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? */
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
* phy. However, in the case of a fixed link phy, we force the port
* settings from the fixed link settings.
Expand Down Expand Up @@ -951,10 +995,30 @@ 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;
} 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);
if (ret < 0)
return ret;
Expand Down Expand Up @@ -1001,19 +1065,14 @@ 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 {
op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL :
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)
Expand Down Expand Up @@ -1321,15 +1380,27 @@ 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)
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)) {
ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL,
GLOBAL_VTU_SID);
if (ret < 0)
Expand Down Expand Up @@ -1397,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;

Expand All @@ -1412,17 +1484,24 @@ 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)
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)
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;
Expand All @@ -1432,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,
Expand Down Expand Up @@ -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,
u16 *old)
{
u16 upper_mask;
u16 fid;
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 */
ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_BASE_VLAN);
if (ret < 0)
Expand All @@ -1536,11 +1623,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);
Expand Down Expand Up @@ -1604,7 +1691,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 */
Expand Down Expand Up @@ -1965,11 +2052,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,
Expand Down Expand Up @@ -2052,11 +2135,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;

Expand Down Expand Up @@ -2444,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) ||
Expand Down

0 comments on commit 6e33804

Please sign in to comment.