From 70cc99d10ca17b8688575d2579e5a2f6c4fa6f29 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 4 Sep 2015 14:34:10 -0400 Subject: [PATCH 1/7] net: dsa: mv88e6xxx: extract FID write from ATU command Not every ATU commands apply to an FID, thus remove the FID writing from mv88e6xxx_atu_cmd and write it explicitly where needed, in order to ease introduction of such commands. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index 6f13f7206762..bf455f9abf8b 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c @@ -1036,14 +1036,10 @@ int mv88e6xxx_set_eee(struct dsa_switch *ds, int port, 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; - 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; @@ -1059,7 +1055,11 @@ static int _mv88e6xxx_flush_fid(struct dsa_switch *ds, int fid) if (ret < 0) return ret; - return _mv88e6xxx_atu_cmd(ds, fid, GLOBAL_ATU_OP_FLUSH_NON_STATIC_DB); + ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_FID, fid); + if (ret < 0) + return ret; + + return _mv88e6xxx_atu_cmd(ds, GLOBAL_ATU_OP_FLUSH_NON_STATIC_DB); } static int mv88e6xxx_set_port_state(struct dsa_switch *ds, int port, u8 state) @@ -1793,7 +1793,11 @@ static int _mv88e6xxx_atu_load(struct dsa_switch *ds, if (ret < 0) 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) @@ -1884,7 +1888,11 @@ static int _mv88e6xxx_atu_getnext(struct dsa_switch *ds, u16 fid, if (ret < 0) 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) return ret; @@ -2308,9 +2316,15 @@ static int mv88e6xxx_atu_show_db(struct seq_file *s, struct dsa_switch *ds, return ret; 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) 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); if (data < 0) return data; From 37705b731500b0ce9fb4ead21a7cdfc241a401fe Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 4 Sep 2015 14:34:11 -0400 Subject: [PATCH 2/7] net: dsa: mv88e6xxx: extract ATU data write access Other ATU commands need to write the ATU data register. To ease the introduction of such commands, extract the ATU data write access from _mv88e6xxx_atu_load to its own function. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx.c | 43 +++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index bf455f9abf8b..25e103c5ed16 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c @@ -1047,6 +1047,29 @@ static int _mv88e6xxx_atu_cmd(struct dsa_switch *ds, u16 cmd) 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_flush_fid(struct dsa_switch *ds, int fid) { int ret; @@ -1761,7 +1784,6 @@ static int _mv88e6xxx_atu_mac_read(struct dsa_switch *ds, unsigned char *addr) static int _mv88e6xxx_atu_load(struct dsa_switch *ds, struct mv88e6xxx_atu_entry *entry) { - u16 reg = 0; int ret; ret = _mv88e6xxx_atu_wait(ds); @@ -1772,24 +1794,7 @@ static int _mv88e6xxx_atu_load(struct dsa_switch *ds, if (ret < 0) return ret; - if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) { - 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); + ret = _mv88e6xxx_atu_data_write(ds, entry); if (ret < 0) return ret; From 7fb5e755153d7b40dd321f7c766dbd4e76c66711 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 4 Sep 2015 14:34:12 -0400 Subject: [PATCH 3/7] net: dsa: mv88e6xxx: rework ATU Flush operation 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 value of the EntryState bits will determine if the operation is either a Flush (0x0) or a Move (0xF). When moving entries from one port to another, entries will be removed if the destination port is 0xF. This patch renames these operations for consistency, add a new generic _mv88e6xxx_atu_flush_move function, and change _mv88e6xxx_flush_fid to use it. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx.c | 53 +++++++++++++++++++++++++++++-------- drivers/net/dsa/mv88e6xxx.h | 8 +++--- 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index 25e103c5ed16..c67090f0814d 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c @@ -1070,19 +1070,50 @@ static int _mv88e6xxx_atu_data_write(struct dsa_switch *ds, 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) { - int ret; - - ret = _mv88e6xxx_atu_wait(ds); - if (ret < 0) - return ret; - - ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_FID, fid); - if (ret < 0) - return ret; - - return _mv88e6xxx_atu_cmd(ds, GLOBAL_ATU_OP_FLUSH_NON_STATIC_DB); + return _mv88e6xxx_atu_flush(ds, fid, false); } static int mv88e6xxx_set_port_state(struct dsa_switch *ds, int port, u8 state) diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h index 9b6f3d9d5ae1..1cf3cf887902 100644 --- a/drivers/net/dsa/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx.h @@ -226,12 +226,12 @@ #define GLOBAL_ATU_OP 0x0b #define GLOBAL_ATU_OP_BUSY BIT(15) #define GLOBAL_ATU_OP_NOP (0 << 12) -#define GLOBAL_ATU_OP_FLUSH_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_ALL ((1 << 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_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_NON_STATIC_DB ((6 << 12) | GLOBAL_ATU_OP_BUSY) +#define GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB ((5 << 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_DATA 0x0c #define GLOBAL_ATU_DATA_TRUNK BIT(15) From c161d0a5edf35dfbb18e859825355791dd6fb2cc Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 4 Sep 2015 14:34:13 -0400 Subject: [PATCH 4/7] net: dsa: mv88e6xxx: flush ATU on initial setup Purge all MAC addresses from the entire set of address databases when the driver initializes the device. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index c67090f0814d..38c8d4ab296e 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c @@ -2685,6 +2685,11 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds) if (ret < 0) 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 */ ret = _mv88e6xxx_vtu_stu_flush(ds); unlock: From 7c400018c003d221a2db1ce46a5b88d7a37add1c Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 4 Sep 2015 14:34:14 -0400 Subject: [PATCH 5/7] net: dsa: mv88e6xxx: flush all addresses when adding a VLAN When choosing an address database for a new VLAN, flush every entries, not only the non-static ones. 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 38c8d4ab296e..88d669c9a200 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c @@ -1630,7 +1630,8 @@ static int _mv88e6xxx_vlan_init(struct dsa_switch *ds, u16 vid, 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) return err; From 9f4d55d2869d86bd5dc59ff28109f7fefedc325d Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 4 Sep 2015 14:34:15 -0400 Subject: [PATCH 6/7] net: dsa: mv88e6xxx: remove addresses when a port leaves a VLAN Add a new _mv88e6xxx_atu_move function to prepare the ATU data register for the move operation. The ports vector will contain the source port and destination port of the Move operation. If the destination port is 0xF, the MAC addresses mapped to the source port are removed for the address database(s). Then add a _mv88e6xxx_atu_remove wrapper to remove the MAC addresses from a VLAN database that are mapped to a given port, when it leaves it. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index 88d669c9a200..63407a1bf81e 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c @@ -1116,6 +1116,31 @@ static int _mv88e6xxx_flush_fid(struct dsa_switch *ds, int fid) return _mv88e6xxx_atu_flush(ds, fid, false); } +static int _mv88e6xxx_atu_move(struct dsa_switch *ds, u16 fid, int from_port, + int to_port, bool static_too) +{ + struct mv88e6xxx_atu_entry entry = { + .trunk = false, + .fid = fid, + }; + + /* 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) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); @@ -1708,6 +1733,10 @@ int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, u16 vid) if (err) goto unlock; + err = _mv88e6xxx_atu_remove(ds, vlan.fid, port, false); + if (err) + goto unlock; + if (!keep) clear_bit(vlan.fid, ps->fid_bitmap); From 2b8157b1c20bdac0e0b856b2cb1499c7f18f2b07 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 4 Sep 2015 14:34:16 -0400 Subject: [PATCH 7/7] net: dsa: mv88e6xxx: remove all MACs when disabling a port When we're moving a port from Learning or Forwarding state to Disabled or Blocking or Listening state, remove all non-static MAC addresses mapped to this port in the entire set of databases, not only one. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index 63407a1bf81e..30227ca2d5db 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c @@ -1163,7 +1163,7 @@ static int mv88e6xxx_set_port_state(struct dsa_switch *ds, int port, u8 state) */ if (oldstate >= PORT_CONTROL_STATE_LEARNING && state <= PORT_CONTROL_STATE_BLOCKING) { - ret = _mv88e6xxx_flush_fid(ds, ps->fid[port]); + ret = _mv88e6xxx_atu_remove(ds, 0, port, false); if (ret) goto abort; }