|
@@ -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 @@ out:
|
|
|
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,
|
|
|
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,
|
|
|
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,
|
|
|
if (ret < 0)
|
|
if (ret < 0)
|
|
|
return ret;
|
|
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,
|
|
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 @@ unlock:
|
|
|
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,
|
|
|
if (ret < 0)
|
|
if (ret < 0)
|
|
|
return ret;
|
|
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;
|
|
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 @@ loadpurge:
|
|
|
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 @@ loadpurge:
|
|
|
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,
|
|
|
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 |= (*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 = _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)
|
|
|
* 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,
|
|
|
if (ret < 0)
|
|
if (ret < 0)
|
|
|
return ret;
|
|
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,
|
|
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,
|
|
|
if (ret < 0)
|
|
if (ret < 0)
|
|
|
return ret;
|
|
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)
|
|
if (ret < 0)
|
|
|
return ret;
|
|
return ret;
|
|
|
|
|
|
|
@@ -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) ||
|