Merge branch 'dsa-mv88e6xxx-ATU'
Vivien Didelot says: ==================== net: dsa: mv88e6xxx: improve ATU move operations This patchset completes the set of available Address Translation Unit operations. These Marvell switches have 4 operations to flush or (re)move, all or only non-static MAC addresses, from the entire set of databases or from just a particular one. The first 3 patches introduce a generic _mv88e6xxx_atu_flush_move function. The 4 remaining patches update a few FID operations in the driver on setup, when a port join or leave a VLAN, or change state. This is a step forward improving the hardware bridging support in DSA and 88E6352-compatible switches. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Коммит
6e75f3c39d
|
@ -1036,14 +1036,10 @@ out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _mv88e6xxx_atu_cmd(struct dsa_switch *ds, int fid, u16 cmd)
|
static int _mv88e6xxx_atu_cmd(struct dsa_switch *ds, u16 cmd)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
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);
|
ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_OP, cmd);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1051,15 +1047,98 @@ static int _mv88e6xxx_atu_cmd(struct dsa_switch *ds, int fid, u16 cmd)
|
||||||
return _mv88e6xxx_atu_wait(ds);
|
return _mv88e6xxx_atu_wait(ds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int _mv88e6xxx_atu_data_write(struct dsa_switch *ds,
|
||||||
|
struct mv88e6xxx_atu_entry *entry)
|
||||||
|
{
|
||||||
|
u16 data = entry->state & GLOBAL_ATU_DATA_STATE_MASK;
|
||||||
|
|
||||||
|
if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) {
|
||||||
|
unsigned int mask, shift;
|
||||||
|
|
||||||
|
if (entry->trunk) {
|
||||||
|
data |= GLOBAL_ATU_DATA_TRUNK;
|
||||||
|
mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
|
||||||
|
shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
|
||||||
|
} else {
|
||||||
|
mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
|
||||||
|
shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
data |= (entry->portv_trunkid << shift) & mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_DATA, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _mv88e6xxx_atu_flush_move(struct dsa_switch *ds,
|
||||||
|
struct mv88e6xxx_atu_entry *entry,
|
||||||
|
bool static_too)
|
||||||
|
{
|
||||||
|
int op;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = _mv88e6xxx_atu_wait(ds);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = _mv88e6xxx_atu_data_write(ds, entry);
|
||||||
|
if (err)
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _mv88e6xxx_atu_flush(struct dsa_switch *ds, u16 fid, bool static_too)
|
||||||
|
{
|
||||||
|
struct mv88e6xxx_atu_entry entry = {
|
||||||
|
.fid = fid,
|
||||||
|
.state = 0, /* EntryState bits must be 0 */
|
||||||
|
};
|
||||||
|
|
||||||
|
return _mv88e6xxx_atu_flush_move(ds, &entry, static_too);
|
||||||
|
}
|
||||||
|
|
||||||
static int _mv88e6xxx_flush_fid(struct dsa_switch *ds, int fid)
|
static int _mv88e6xxx_flush_fid(struct dsa_switch *ds, int fid)
|
||||||
{
|
{
|
||||||
int ret;
|
return _mv88e6xxx_atu_flush(ds, fid, false);
|
||||||
|
}
|
||||||
|
|
||||||
ret = _mv88e6xxx_atu_wait(ds);
|
static int _mv88e6xxx_atu_move(struct dsa_switch *ds, u16 fid, int from_port,
|
||||||
if (ret < 0)
|
int to_port, bool static_too)
|
||||||
return ret;
|
{
|
||||||
|
struct mv88e6xxx_atu_entry entry = {
|
||||||
|
.trunk = false,
|
||||||
|
.fid = fid,
|
||||||
|
};
|
||||||
|
|
||||||
return _mv88e6xxx_atu_cmd(ds, fid, GLOBAL_ATU_OP_FLUSH_NON_STATIC_DB);
|
/* EntryState bits must be 0xF */
|
||||||
|
entry.state = GLOBAL_ATU_DATA_STATE_MASK;
|
||||||
|
|
||||||
|
/* ToPort and FromPort are respectively in PortVec bits 7:4 and 3:0 */
|
||||||
|
entry.portv_trunkid = (to_port & 0x0f) << 4;
|
||||||
|
entry.portv_trunkid |= from_port & 0x0f;
|
||||||
|
|
||||||
|
return _mv88e6xxx_atu_flush_move(ds, &entry, static_too);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _mv88e6xxx_atu_remove(struct dsa_switch *ds, u16 fid, int port,
|
||||||
|
bool static_too)
|
||||||
|
{
|
||||||
|
/* Destination port 0xF means remove the entries */
|
||||||
|
return _mv88e6xxx_atu_move(ds, fid, port, 0x0f, static_too);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mv88e6xxx_set_port_state(struct dsa_switch *ds, int port, u8 state)
|
static int mv88e6xxx_set_port_state(struct dsa_switch *ds, int port, u8 state)
|
||||||
|
@ -1084,7 +1163,7 @@ static int mv88e6xxx_set_port_state(struct dsa_switch *ds, int port, u8 state)
|
||||||
*/
|
*/
|
||||||
if (oldstate >= PORT_CONTROL_STATE_LEARNING &&
|
if (oldstate >= PORT_CONTROL_STATE_LEARNING &&
|
||||||
state <= PORT_CONTROL_STATE_BLOCKING) {
|
state <= PORT_CONTROL_STATE_BLOCKING) {
|
||||||
ret = _mv88e6xxx_flush_fid(ds, ps->fid[port]);
|
ret = _mv88e6xxx_atu_remove(ds, 0, port, false);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto abort;
|
goto abort;
|
||||||
}
|
}
|
||||||
|
@ -1576,7 +1655,8 @@ static int _mv88e6xxx_vlan_init(struct dsa_switch *ds, u16 vid,
|
||||||
return -ENOSPC;
|
return -ENOSPC;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = _mv88e6xxx_flush_fid(ds, vlan.fid);
|
/* Clear all MAC addresses from the new database */
|
||||||
|
err = _mv88e6xxx_atu_flush(ds, vlan.fid, true);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
@ -1653,6 +1733,10 @@ int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, u16 vid)
|
||||||
if (err)
|
if (err)
|
||||||
goto unlock;
|
goto unlock;
|
||||||
|
|
||||||
|
err = _mv88e6xxx_atu_remove(ds, vlan.fid, port, false);
|
||||||
|
if (err)
|
||||||
|
goto unlock;
|
||||||
|
|
||||||
if (!keep)
|
if (!keep)
|
||||||
clear_bit(vlan.fid, ps->fid_bitmap);
|
clear_bit(vlan.fid, ps->fid_bitmap);
|
||||||
|
|
||||||
|
@ -1761,7 +1845,6 @@ static int _mv88e6xxx_atu_mac_read(struct dsa_switch *ds, unsigned char *addr)
|
||||||
static int _mv88e6xxx_atu_load(struct dsa_switch *ds,
|
static int _mv88e6xxx_atu_load(struct dsa_switch *ds,
|
||||||
struct mv88e6xxx_atu_entry *entry)
|
struct mv88e6xxx_atu_entry *entry)
|
||||||
{
|
{
|
||||||
u16 reg = 0;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = _mv88e6xxx_atu_wait(ds);
|
ret = _mv88e6xxx_atu_wait(ds);
|
||||||
|
@ -1772,28 +1855,15 @@ static int _mv88e6xxx_atu_load(struct dsa_switch *ds,
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) {
|
ret = _mv88e6xxx_atu_data_write(ds, entry);
|
||||||
unsigned int mask, shift;
|
|
||||||
|
|
||||||
if (entry->trunk) {
|
|
||||||
reg |= GLOBAL_ATU_DATA_TRUNK;
|
|
||||||
mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
|
|
||||||
shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
|
|
||||||
} else {
|
|
||||||
mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
|
|
||||||
shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
|
|
||||||
}
|
|
||||||
|
|
||||||
reg |= (entry->portv_trunkid << shift) & mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
reg |= entry->state & GLOBAL_ATU_DATA_STATE_MASK;
|
|
||||||
|
|
||||||
ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_DATA, reg);
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
return _mv88e6xxx_atu_cmd(ds, entry->fid, GLOBAL_ATU_OP_LOAD_DB);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _mv88e6xxx_port_vid_to_fid(struct dsa_switch *ds, int port, u16 vid)
|
static int _mv88e6xxx_port_vid_to_fid(struct dsa_switch *ds, int port, u16 vid)
|
||||||
|
@ -1884,7 +1954,11 @@ static int _mv88e6xxx_atu_getnext(struct dsa_switch *ds, u16 fid,
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = _mv88e6xxx_atu_cmd(ds, fid, GLOBAL_ATU_OP_GET_NEXT_DB);
|
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);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -2308,9 +2382,15 @@ static int mv88e6xxx_atu_show_db(struct seq_file *s, struct dsa_switch *ds,
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
ret = _mv88e6xxx_atu_cmd(ds, dbnum, GLOBAL_ATU_OP_GET_NEXT_DB);
|
ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_FID,
|
||||||
|
dbnum);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
ret = _mv88e6xxx_atu_cmd(ds, GLOBAL_ATU_OP_GET_NEXT_DB);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
data = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_ATU_DATA);
|
data = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_ATU_DATA);
|
||||||
if (data < 0)
|
if (data < 0)
|
||||||
return data;
|
return data;
|
||||||
|
@ -2635,6 +2715,11 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds)
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto unlock;
|
goto unlock;
|
||||||
|
|
||||||
|
/* Clear all ATU entries */
|
||||||
|
ret = _mv88e6xxx_atu_flush(ds, 0, true);
|
||||||
|
if (ret < 0)
|
||||||
|
goto unlock;
|
||||||
|
|
||||||
/* Clear all the VTU and STU entries */
|
/* Clear all the VTU and STU entries */
|
||||||
ret = _mv88e6xxx_vtu_stu_flush(ds);
|
ret = _mv88e6xxx_vtu_stu_flush(ds);
|
||||||
unlock:
|
unlock:
|
||||||
|
|
|
@ -226,12 +226,12 @@
|
||||||
#define GLOBAL_ATU_OP 0x0b
|
#define GLOBAL_ATU_OP 0x0b
|
||||||
#define GLOBAL_ATU_OP_BUSY BIT(15)
|
#define GLOBAL_ATU_OP_BUSY BIT(15)
|
||||||
#define GLOBAL_ATU_OP_NOP (0 << 12)
|
#define GLOBAL_ATU_OP_NOP (0 << 12)
|
||||||
#define GLOBAL_ATU_OP_FLUSH_ALL ((1 << 12) | GLOBAL_ATU_OP_BUSY)
|
#define GLOBAL_ATU_OP_FLUSH_MOVE_ALL ((1 << 12) | GLOBAL_ATU_OP_BUSY)
|
||||||
#define GLOBAL_ATU_OP_FLUSH_NON_STATIC ((2 << 12) | GLOBAL_ATU_OP_BUSY)
|
#define GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC ((2 << 12) | GLOBAL_ATU_OP_BUSY)
|
||||||
#define GLOBAL_ATU_OP_LOAD_DB ((3 << 12) | GLOBAL_ATU_OP_BUSY)
|
#define GLOBAL_ATU_OP_LOAD_DB ((3 << 12) | GLOBAL_ATU_OP_BUSY)
|
||||||
#define GLOBAL_ATU_OP_GET_NEXT_DB ((4 << 12) | GLOBAL_ATU_OP_BUSY)
|
#define GLOBAL_ATU_OP_GET_NEXT_DB ((4 << 12) | GLOBAL_ATU_OP_BUSY)
|
||||||
#define GLOBAL_ATU_OP_FLUSH_DB ((5 << 12) | GLOBAL_ATU_OP_BUSY)
|
#define GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB ((5 << 12) | GLOBAL_ATU_OP_BUSY)
|
||||||
#define GLOBAL_ATU_OP_FLUSH_NON_STATIC_DB ((6 << 12) | GLOBAL_ATU_OP_BUSY)
|
#define GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB ((6 << 12) | GLOBAL_ATU_OP_BUSY)
|
||||||
#define GLOBAL_ATU_OP_GET_CLR_VIOLATION ((7 << 12) | GLOBAL_ATU_OP_BUSY)
|
#define GLOBAL_ATU_OP_GET_CLR_VIOLATION ((7 << 12) | GLOBAL_ATU_OP_BUSY)
|
||||||
#define GLOBAL_ATU_DATA 0x0c
|
#define GLOBAL_ATU_DATA 0x0c
|
||||||
#define GLOBAL_ATU_DATA_TRUNK BIT(15)
|
#define GLOBAL_ATU_DATA_TRUNK BIT(15)
|
||||||
|
|
Загрузка…
Ссылка в новой задаче