All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFT/RFC PATCH 0/8] net: dsa: mv88e6xxx: Add support for HW bridging
@ 2015-02-23 19:35 Guenter Roeck
  2015-02-23 19:35 ` [RFT/RFC PATCH 1/8] net: dsa: mv88e6xxx: Factor out common initialization code Guenter Roeck
                   ` (8 more replies)
  0 siblings, 9 replies; 10+ messages in thread
From: Guenter Roeck @ 2015-02-23 19:35 UTC (permalink / raw)
  To: netdev
  Cc: David S. Miller, Andrew Lunn, Florian Fainelli, linux-kernel,
	Guenter Roeck

This patch series applies on top of net-next, plus Florian's patches
integrating dsa with SWITCHDEV for HW bridging. It also requires the patch
'net: dsa: Ensure that port array elements are initialized before being used'.

HW bridging support is currently only enabled for MV8865352, but it should be
straightforward to enable support for other Marvell chips by adding three lines
of initialization code to the respective source; see patch 8/8 for details.
mv886e3131.c would also have to use mv88e6xxx_setup_port_common(). 

Patches 1-3 prepare the mv88e6xxx code for adding HW bridging support.
Patch 4 adds core HW bridging support to the mv88e6xxx code. Patch 5-7
prepare the drivers for mv88e6352, mv88e6123_61_65, and mv88e6171 for
HW bridging support, without actually enabling it. Patch 8 enables
HW bridging support in the mv88e6352 driver.

Patches 5 and 8 could possibly be merged, but I thought it was better
to keep them separate because they logically serve a different purpose.

Testing has been minimal, and only with MV88E6352. Ports can be added to
and removed from a bridge, and I have been able to pass data through a port
configured as bridge port.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [RFT/RFC PATCH 1/8] net: dsa: mv88e6xxx: Factor out common initialization code
  2015-02-23 19:35 [RFT/RFC PATCH 0/8] net: dsa: mv88e6xxx: Add support for HW bridging Guenter Roeck
@ 2015-02-23 19:35 ` Guenter Roeck
  2015-02-23 19:35 ` [RFT/RFC PATCH 2/8] net: dsa: mv88e6xxx: Provide function for common port initialization Guenter Roeck
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Guenter Roeck @ 2015-02-23 19:35 UTC (permalink / raw)
  To: netdev
  Cc: David S. Miller, Andrew Lunn, Florian Fainelli, linux-kernel,
	Guenter Roeck

Code used and needed in mv886xxx.c should be initialized there as well,
so factor it out from the individual initialization files.

Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
 drivers/net/dsa/mv88e6123_61_65.c |  7 +++----
 drivers/net/dsa/mv88e6171.c       |  5 +++--
 drivers/net/dsa/mv88e6352.c       |  7 ++++---
 drivers/net/dsa/mv88e6xxx.c       | 11 +++++++++++
 drivers/net/dsa/mv88e6xxx.h       |  1 +
 5 files changed, 22 insertions(+), 9 deletions(-)

diff --git a/drivers/net/dsa/mv88e6123_61_65.c b/drivers/net/dsa/mv88e6123_61_65.c
index e9c736e..6ebe570 100644
--- a/drivers/net/dsa/mv88e6123_61_65.c
+++ b/drivers/net/dsa/mv88e6123_61_65.c
@@ -293,13 +293,12 @@ static int mv88e6123_61_65_setup_port(struct dsa_switch *ds, int p)
 
 static int mv88e6123_61_65_setup(struct dsa_switch *ds)
 {
-	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
 	int i;
 	int ret;
 
-	mutex_init(&ps->smi_mutex);
-	mutex_init(&ps->stats_mutex);
-	mutex_init(&ps->phy_mutex);
+	ret = mv88e6xxx_setup_common(ds);
+	if (ret < 0)
+		return ret;
 
 	ret = mv88e6123_61_65_switch_reset(ds);
 	if (ret < 0)
diff --git a/drivers/net/dsa/mv88e6171.c b/drivers/net/dsa/mv88e6171.c
index 9808c86..2374205 100644
--- a/drivers/net/dsa/mv88e6171.c
+++ b/drivers/net/dsa/mv88e6171.c
@@ -296,8 +296,9 @@ static int mv88e6171_setup(struct dsa_switch *ds)
 	int i;
 	int ret;
 
-	mutex_init(&ps->smi_mutex);
-	mutex_init(&ps->stats_mutex);
+	ret = mv88e6xxx_setup_common(ds);
+	if (ret < 0)
+		return ret;
 
 	ret = mv88e6171_switch_reset(ds);
 	if (ret < 0)
diff --git a/drivers/net/dsa/mv88e6352.c b/drivers/net/dsa/mv88e6352.c
index 7bc5998..75cf9e1 100644
--- a/drivers/net/dsa/mv88e6352.c
+++ b/drivers/net/dsa/mv88e6352.c
@@ -385,9 +385,10 @@ static int mv88e6352_setup(struct dsa_switch *ds)
 	int ret;
 	int i;
 
-	mutex_init(&ps->smi_mutex);
-	mutex_init(&ps->stats_mutex);
-	mutex_init(&ps->phy_mutex);
+	ret = mv88e6xxx_setup_common(ds);
+	if (ret < 0)
+		return ret;
+
 	mutex_init(&ps->eeprom_mutex);
 
 	ps->id = REG_READ(REG_PORT(0), 0x03) & 0xfff0;
diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c
index 2d5306a..7b767e3 100644
--- a/drivers/net/dsa/mv88e6xxx.c
+++ b/drivers/net/dsa/mv88e6xxx.c
@@ -704,6 +704,17 @@ int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
 	return 0;
 }
 
+int mv88e6xxx_setup_common(struct dsa_switch *ds)
+{
+	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+
+	mutex_init(&ps->smi_mutex);
+	mutex_init(&ps->stats_mutex);
+	mutex_init(&ps->phy_mutex);
+
+	return 0;
+}
+
 static int __init mv88e6xxx_init(void)
 {
 #if IS_ENABLED(CONFIG_NET_DSA_MV88E6131)
diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h
index 5fd42ce..a02d95a 100644
--- a/drivers/net/dsa/mv88e6xxx.h
+++ b/drivers/net/dsa/mv88e6xxx.h
@@ -57,6 +57,7 @@ struct mv88e6xxx_hw_stat {
 	int reg;
 };
 
+int mv88e6xxx_setup_common(struct dsa_switch *ds);
 int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg);
 int mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg);
 int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr,
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFT/RFC PATCH 2/8] net: dsa: mv88e6xxx: Provide function for common port initialization
  2015-02-23 19:35 [RFT/RFC PATCH 0/8] net: dsa: mv88e6xxx: Add support for HW bridging Guenter Roeck
  2015-02-23 19:35 ` [RFT/RFC PATCH 1/8] net: dsa: mv88e6xxx: Factor out common initialization code Guenter Roeck
@ 2015-02-23 19:35 ` Guenter Roeck
  2015-02-23 19:35 ` [RFT/RFC PATCH 3/8] net: dsa: mv88e6xxx: Split mv88e6xxx_reg_read and mv88e6xxx_reg_write Guenter Roeck
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Guenter Roeck @ 2015-02-23 19:35 UTC (permalink / raw)
  To: netdev
  Cc: David S. Miller, Andrew Lunn, Florian Fainelli, linux-kernel,
	Guenter Roeck

Provide mv88e6xxx_setup_port_common() for common port initialization.
Currently only implement VLAN configuration since this will be needed
for hardware bridging. More can be added later if desired/needed.

Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
 drivers/net/dsa/mv88e6xxx.c | 25 +++++++++++++++++++++++++
 drivers/net/dsa/mv88e6xxx.h |  1 +
 2 files changed, 26 insertions(+)

diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c
index 7b767e3..ca7a9c6 100644
--- a/drivers/net/dsa/mv88e6xxx.c
+++ b/drivers/net/dsa/mv88e6xxx.c
@@ -704,6 +704,31 @@ int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
 	return 0;
 }
 
+int mv88e6xxx_setup_port_common(struct dsa_switch *ds, int port)
+{
+	u16 reg;
+
+	/* Port based VLAN map: give each port its own address
+	 * database, allow the CPU port to talk to each of the 'real'
+	 * ports, and allow each of the 'real' ports to only talk to
+	 * the upstream port.
+	 */
+	reg = (port & 0xf) << 12;
+	if (dsa_is_cpu_port(ds, port))
+		reg |= ds->phys_port_mask;
+	else
+		reg |= 1 << dsa_upstream_port(ds);
+
+	REG_WRITE(REG_PORT(port), 0x06, reg);
+
+	/* Default VLAN ID and priority: don't set a default VLAN
+	 * ID, and set the default packet priority to zero.
+	 */
+	REG_WRITE(REG_PORT(port), 0x07, 0x0000);
+
+	return 0;
+}
+
 int mv88e6xxx_setup_common(struct dsa_switch *ds)
 {
 	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h
index a02d95a..a4df496 100644
--- a/drivers/net/dsa/mv88e6xxx.h
+++ b/drivers/net/dsa/mv88e6xxx.h
@@ -57,6 +57,7 @@ struct mv88e6xxx_hw_stat {
 	int reg;
 };
 
+int mv88e6xxx_setup_port_common(struct dsa_switch *ds, int port);
 int mv88e6xxx_setup_common(struct dsa_switch *ds);
 int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg);
 int mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg);
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFT/RFC PATCH 3/8] net: dsa: mv88e6xxx: Split mv88e6xxx_reg_read and mv88e6xxx_reg_write
  2015-02-23 19:35 [RFT/RFC PATCH 0/8] net: dsa: mv88e6xxx: Add support for HW bridging Guenter Roeck
  2015-02-23 19:35 ` [RFT/RFC PATCH 1/8] net: dsa: mv88e6xxx: Factor out common initialization code Guenter Roeck
  2015-02-23 19:35 ` [RFT/RFC PATCH 2/8] net: dsa: mv88e6xxx: Provide function for common port initialization Guenter Roeck
@ 2015-02-23 19:35 ` Guenter Roeck
  2015-02-23 19:35 ` [RFT/RFC PATCH 4/8] net: dsa: mv88e6xxx: Add Hardware bridging support Guenter Roeck
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Guenter Roeck @ 2015-02-23 19:35 UTC (permalink / raw)
  To: netdev
  Cc: David S. Miller, Andrew Lunn, Florian Fainelli, linux-kernel,
	Guenter Roeck

Split mv88e6xxx_reg_read and mv88e6xxx_reg_write into two functions each,
one to acquire smi_mutex and one to get struct mii_bus *bus from
struct dsa_switch *ds and to call the actual read/write function.

Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
 drivers/net/dsa/mv88e6xxx.c | 35 ++++++++++++++++++++++++++---------
 1 file changed, 26 insertions(+), 9 deletions(-)

diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c
index ca7a9c6..d253f4e 100644
--- a/drivers/net/dsa/mv88e6xxx.c
+++ b/drivers/net/dsa/mv88e6xxx.c
@@ -72,19 +72,16 @@ int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg)
 	return ret & 0xffff;
 }
 
-int mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg)
+/* Must be called with SMI mutex held */
+static int _mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg)
 {
-	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
 	struct mii_bus *bus = dsa_host_dev_to_mii_bus(ds->master_dev);
 	int ret;
 
 	if (bus == NULL)
 		return -EINVAL;
 
-	mutex_lock(&ps->smi_mutex);
 	ret = __mv88e6xxx_reg_read(bus, ds->pd->sw_addr, addr, reg);
-	mutex_unlock(&ps->smi_mutex);
-
 	if (ret < 0)
 		return ret;
 
@@ -94,6 +91,18 @@ int mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg)
 	return ret;
 }
 
+int mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg)
+{
+	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+	int ret;
+
+	mutex_lock(&ps->smi_mutex);
+	ret = _mv88e6xxx_reg_read(ds, addr, reg);
+	mutex_unlock(&ps->smi_mutex);
+
+	return ret;
+}
+
 int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr,
 			  int reg, u16 val)
 {
@@ -125,11 +134,11 @@ int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr,
 	return 0;
 }
 
-int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val)
+/* Must be called with SMI mutex held */
+static int _mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg,
+				u16 val)
 {
-	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
 	struct mii_bus *bus = dsa_host_dev_to_mii_bus(ds->master_dev);
-	int ret;
 
 	if (bus == NULL)
 		return -EINVAL;
@@ -137,8 +146,16 @@ int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val)
 	dev_dbg(ds->master_dev, "-> addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n",
 		addr, reg, val);
 
+	return __mv88e6xxx_reg_write(bus, ds->pd->sw_addr, addr, reg, val);
+}
+
+int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val)
+{
+	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+	int ret;
+
 	mutex_lock(&ps->smi_mutex);
-	ret = __mv88e6xxx_reg_write(bus, ds->pd->sw_addr, addr, reg, val);
+	ret = _mv88e6xxx_reg_write(ds, addr, reg, val);
 	mutex_unlock(&ps->smi_mutex);
 
 	return ret;
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFT/RFC PATCH 4/8] net: dsa: mv88e6xxx: Add Hardware bridging support
  2015-02-23 19:35 [RFT/RFC PATCH 0/8] net: dsa: mv88e6xxx: Add support for HW bridging Guenter Roeck
                   ` (2 preceding siblings ...)
  2015-02-23 19:35 ` [RFT/RFC PATCH 3/8] net: dsa: mv88e6xxx: Split mv88e6xxx_reg_read and mv88e6xxx_reg_write Guenter Roeck
@ 2015-02-23 19:35 ` Guenter Roeck
  2015-02-23 19:35 ` [RFT/RFC PATCH 5/8] net: dsa: mv88e6352: Use common port initialization code Guenter Roeck
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Guenter Roeck @ 2015-02-23 19:35 UTC (permalink / raw)
  To: netdev
  Cc: David S. Miller, Andrew Lunn, Florian Fainelli, linux-kernel,
	Guenter Roeck

Bridge support is similar for all chips supported by the mv88e6xxx code,
so add the code there.

Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
 drivers/net/dsa/mv88e6xxx.c | 297 ++++++++++++++++++++++++++++++++++++++++++--
 drivers/net/dsa/mv88e6xxx.h |  15 +++
 2 files changed, 300 insertions(+), 12 deletions(-)

diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c
index d253f4e..5c97327 100644
--- a/drivers/net/dsa/mv88e6xxx.c
+++ b/drivers/net/dsa/mv88e6xxx.c
@@ -9,6 +9,7 @@
  */
 
 #include <linux/delay.h>
+#include <linux/if_bridge.h>
 #include <linux/jiffies.h>
 #include <linux/list.h>
 #include <linux/module.h>
@@ -644,6 +645,31 @@ int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds)
 	return mv88e6xxx_wait(ds, REG_GLOBAL2, 0x14, 0x8000);
 }
 
+/* Must be called with SMI lock held */
+static int _mv88e6xxx_wait(struct dsa_switch *ds, int reg, int offset, u16 mask)
+{
+	unsigned long timeout = jiffies + HZ / 10;
+
+	while (time_before(jiffies, timeout)) {
+		int ret;
+
+		ret = _mv88e6xxx_reg_read(ds, reg, offset);
+		if (ret < 0)
+			return ret;
+		if (!(ret & mask))
+			return 0;
+
+		usleep_range(1000, 2000);
+	}
+	return -ETIMEDOUT;
+}
+
+/* Must be called with SMI lock held */
+static int _mv88e6xxx_atu_wait(struct dsa_switch *ds)
+{
+	return _mv88e6xxx_wait(ds, REG_GLOBAL, 0x0b, 0x8000);
+}
+
 int mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int addr, int regnum)
 {
 	int ret;
@@ -721,29 +747,271 @@ int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
 	return 0;
 }
 
-int mv88e6xxx_setup_port_common(struct dsa_switch *ds, int port)
+static int _mv88e6xxx_flush_fid(struct dsa_switch *ds, int fid)
 {
-	u16 reg;
+	int ret;
+
+	ret = _mv88e6xxx_atu_wait(ds);
+	if (ret < 0) {
+		netdev_err(ds->ports[fid], "ATU busy\n");
+		return ret;
+	}
+	ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x01, fid);
+	if (ret < 0) {
+		netdev_err(ds->ports[fid],
+			   "Failed to set FID for flush operation\n");
+		return ret;
+	}
+	ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x0b, 0xe000);
+	if (ret < 0) {
+		netdev_err(ds->ports[fid],
+			   "Failed to flush forwarding database for FID %d\n",
+			   fid);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int mv88e6xxx_set_port_state(struct dsa_switch *ds, int port, u8 state)
+{
+	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+	int reg, ret;
+	u8 oldstate;
+
+	mutex_lock(&ps->smi_mutex);
+
+	reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), 0x04);
+	if (reg < 0)
+		goto abort;
+
+	oldstate = reg & 3;
+	if (oldstate != state) {
+		/* Flush forwarding database if we're moving a port
+		 * from Learning or Forwarding state to Disabled or
+		 * Blocking or Listening state.
+		 */
+		if ((oldstate & 2) && !(state & 2)) {
+			ret = _mv88e6xxx_flush_fid(ds, ps->fid[port]);
+			if (ret)
+				goto abort;
+		}
+		reg = (reg & ~3) | state;
+		ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), 0x04, reg);
+	}
+
+abort:
+	mutex_unlock(&ps->smi_mutex);
+	return ret;
+}
+
+/* Must be called with smi lock held */
+static int _mv88e6xxx_update_port_config(struct dsa_switch *ds, int port)
+{
+	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+	u8 fid = ps->fid[port];
+	u16 reg = fid << 12;
 
-	/* Port based VLAN map: give each port its own address
-	 * database, allow the CPU port to talk to each of the 'real'
-	 * ports, and allow each of the 'real' ports to only talk to
-	 * the upstream port.
-	 */
-	reg = (port & 0xf) << 12;
 	if (dsa_is_cpu_port(ds, port))
 		reg |= ds->phys_port_mask;
 	else
-		reg |= 1 << dsa_upstream_port(ds);
+		reg |= (ps->bridge_mask[fid] |
+		       (1 << dsa_upstream_port(ds))) & ~(1 << port);
+
+	netdev_dbg(ds->ports[port], "fid %d 0x06:=0x%x\n", fid, reg);
+
+	return _mv88e6xxx_reg_write(ds, REG_PORT(port), 0x06, reg);
+}
+
+/* Must be called with smi lock held */
+static int _mv88e6xxx_update_bridge_config(struct dsa_switch *ds, int fid)
+{
+	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+	int port;
+	u32 mask;
+	int ret;
+
+	mask = ds->phys_port_mask;
+	while (mask) {
+		port = __ffs(mask);
+		mask &= ~(1 << port);
+		if (ps->fid[port] != fid)
+			continue;
+
+		ret = _mv88e6xxx_update_port_config(ds, port);
+		if (ret)
+			return ret;
+	}
+
+	return _mv88e6xxx_flush_fid(ds, fid);
+}
+
+/* Bridge handling functions */
+
+int mv88e6xxx_join_bridge(struct dsa_switch *ds, int port, u32 br_port_mask)
+{
+	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+	int ret = 0;
+	u32 nmask;
+	int fid;
+
+	/* If the bridge group is not empty, join that group.
+	 * Otherwise create a new group.
+	 */
+	fid = ps->fid[port];
+	nmask = br_port_mask & ~(1 << port);
+	if (nmask)
+		fid = ps->fid[__ffs(nmask)];
+
+	nmask = ps->bridge_mask[fid] | (1 << port);
+	if (nmask != br_port_mask) {
+		netdev_err(ds->ports[port],
+			   "join: Bridge port mask mismatch fid=%d mask=0x%x expected 0x%x\n",
+			   fid, br_port_mask, nmask);
+		return -EINVAL;
+	}
+
+	mutex_lock(&ps->smi_mutex);
+
+	ps->bridge_mask[fid] = br_port_mask;
+
+	if (fid != ps->fid[port]) {
+		ps->fid_mask |= 1 << ps->fid[port];
+		ps->fid[port] = fid;
+		ret = _mv88e6xxx_update_bridge_config(ds, fid);
+	}
+
+	mutex_unlock(&ps->smi_mutex);
+
+	return ret;
+}
+
+int mv88e6xxx_leave_bridge(struct dsa_switch *ds, int port, u32 br_port_mask)
+{
+	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+	u8 fid, newfid;
+	int ret;
+
+	fid = ps->fid[port];
+
+	if (ps->bridge_mask[fid] != br_port_mask) {
+		netdev_err(ds->ports[port],
+			   "leave: Bridge port mask mismatch fid=%d mask=0x%x expected 0x%x\n",
+			   fid, br_port_mask, ps->bridge_mask[fid]);
+		return -EINVAL;
+	}
+
+	/* If the port was the last port of a bridge, we are done.
+	 * Otherwise assign a new fid to the port, and fix up
+	 * the bridge configuration.
+	 */
+	if (br_port_mask == (1 << port))
+		return 0;
+
+	mutex_lock(&ps->smi_mutex);
+
+	newfid = __ffs(ps->fid_mask);
+	ps->fid[port] = newfid;
+	ps->fid_mask &= (1 << newfid);
+	ps->bridge_mask[fid] &= ~(1 << port);
+	ps->bridge_mask[newfid] = 1 << port;
+
+	ret = _mv88e6xxx_update_bridge_config(ds, fid);
+	if (!ret)
+		ret = _mv88e6xxx_update_bridge_config(ds, newfid);
+
+	mutex_unlock(&ps->smi_mutex);
+
+	return ret;
+}
+
+int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state)
+{
+	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+	int stp_state;
+
+	switch (state) {
+	case BR_STATE_DISABLED:
+		stp_state = 0;
+		break;
+	case BR_STATE_BLOCKING:
+	case BR_STATE_LISTENING:
+		stp_state = 1;
+		break;
+	case BR_STATE_LEARNING:
+		stp_state = 2;
+		break;
+	case BR_STATE_FORWARDING:
+	default:
+		stp_state = 3;
+		break;
+	}
+
+	netdev_dbg(ds->ports[port], "port state %d [%d]\n", state, stp_state);
+
+	/* mv88e6xxx_port_stp_update may be called with softirqs disabled,
+	 * so we can not update the port state directly but need to schedule it.
+	 */
+	spin_lock(&ps->bridge_lock);
+	ps->port_state[port] = stp_state;
+	ps->port_state_update_mask |= 1 << port;
+	schedule_work(&ps->bridge_work);
+	spin_unlock(&ps->bridge_lock);
+
+	return 0;
+}
+
+static void mv88e6xxx_bridge_work(struct work_struct *work)
+{
+	struct mv88e6xxx_priv_state *ps;
+	struct dsa_switch *ds;
+	int port;
+
+	ps = container_of(work, struct mv88e6xxx_priv_state, bridge_work);
+	ds = ((struct dsa_switch *)ps) - 1;
+
+	spin_lock(&ps->bridge_lock);
+
+	while (ps->port_state_update_mask) {
+		port = __ffs(ps->port_state_update_mask);
+		mv88e6xxx_set_port_state(ds, port, ps->port_state[port]);
+		ps->port_state_update_mask &= ~(1 << port);
+	}
+
+	spin_unlock(&ps->bridge_lock);
+}
+
+int mv88e6xxx_setup_port_common(struct dsa_switch *ds, int port)
+{
+	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+	int ret, fid;
 
-	REG_WRITE(REG_PORT(port), 0x06, reg);
+	mutex_lock(&ps->smi_mutex);
 
 	/* Default VLAN ID and priority: don't set a default VLAN
 	 * ID, and set the default packet priority to zero.
 	 */
-	REG_WRITE(REG_PORT(port), 0x07, 0x0000);
+	ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), 0x07, 0x0000);
+	if (ret)
+		goto abort;
 
-	return 0;
+	/* Port based VLAN map: give each port its own address
+	 * database, allow the CPU port to talk to each of the 'real'
+	 * ports, and allow each of the 'real' ports to only talk to
+	 * the upstream port.
+	 */
+	fid = __ffs(ps->fid_mask);
+	ps->fid[port] = fid;
+	ps->fid_mask &= ~(1 << fid);
+
+	if (!dsa_is_cpu_port(ds, port))
+		ps->bridge_mask[fid] = 1 << port;
+
+	ret = _mv88e6xxx_update_port_config(ds, port);
+
+abort:
+	mutex_unlock(&ps->smi_mutex);
+	return ret;
 }
 
 int mv88e6xxx_setup_common(struct dsa_switch *ds)
@@ -753,6 +1021,11 @@ int mv88e6xxx_setup_common(struct dsa_switch *ds)
 	mutex_init(&ps->smi_mutex);
 	mutex_init(&ps->stats_mutex);
 	mutex_init(&ps->phy_mutex);
+	spin_lock_init(&ps->bridge_lock);
+
+	ps->fid_mask = (1 << DSA_MAX_PORTS) - 1;
+
+	INIT_WORK(&ps->bridge_work, mv88e6xxx_bridge_work);
 
 	return 0;
 }
diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h
index a4df496..708131b 100644
--- a/drivers/net/dsa/mv88e6xxx.h
+++ b/drivers/net/dsa/mv88e6xxx.h
@@ -49,6 +49,18 @@ struct mv88e6xxx_priv_state {
 	struct mutex eeprom_mutex;
 
 	int		id; /* switch product id */
+
+	/* hw bridging */
+
+	u32 fid_mask;
+	u8 fid[DSA_MAX_PORTS];
+	u16 bridge_mask[DSA_MAX_PORTS];
+
+	u32 port_state_update_mask;
+	u8 port_state[DSA_MAX_PORTS];
+
+	struct work_struct bridge_work;
+	spinlock_t bridge_lock;		/* protect access to hw bridge data */
 };
 
 struct mv88e6xxx_hw_stat {
@@ -93,6 +105,9 @@ int mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int addr, int regnum,
 int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e);
 int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
 		      struct phy_device *phydev, struct ethtool_eee *e);
+int mv88e6xxx_join_bridge(struct dsa_switch *ds, int port, u32 br_port_mask);
+int mv88e6xxx_leave_bridge(struct dsa_switch *ds, int port, u32 br_port_mask);
+int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state);
 
 extern struct dsa_switch_driver mv88e6131_switch_driver;
 extern struct dsa_switch_driver mv88e6123_61_65_switch_driver;
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFT/RFC PATCH 5/8] net: dsa: mv88e6352: Use common port initialization code
  2015-02-23 19:35 [RFT/RFC PATCH 0/8] net: dsa: mv88e6xxx: Add support for HW bridging Guenter Roeck
                   ` (3 preceding siblings ...)
  2015-02-23 19:35 ` [RFT/RFC PATCH 4/8] net: dsa: mv88e6xxx: Add Hardware bridging support Guenter Roeck
@ 2015-02-23 19:35 ` Guenter Roeck
  2015-02-23 19:35 ` [RFT/RFC PATCH 6/8] net: dsa: mv88e6123_61_65: Use common port configuration Guenter Roeck
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Guenter Roeck @ 2015-02-23 19:35 UTC (permalink / raw)
  To: netdev
  Cc: David S. Miller, Andrew Lunn, Florian Fainelli, linux-kernel,
	Guenter Roeck

This prepares the driver for hardware bridging.

Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
 drivers/net/dsa/mv88e6352.c | 19 +------------------
 1 file changed, 1 insertion(+), 18 deletions(-)

diff --git a/drivers/net/dsa/mv88e6352.c b/drivers/net/dsa/mv88e6352.c
index 75cf9e1..d7053db 100644
--- a/drivers/net/dsa/mv88e6352.c
+++ b/drivers/net/dsa/mv88e6352.c
@@ -220,23 +220,6 @@ static int mv88e6352_setup_port(struct dsa_switch *ds, int p)
 	 */
 	REG_WRITE(addr, 0x05, dsa_is_cpu_port(ds, p) ? 0x8000 : 0x0000);
 
-	/* Port based VLAN map: give each port its own address
-	 * database, allow the CPU port to talk to each of the 'real'
-	 * ports, and allow each of the 'real' ports to only talk to
-	 * the upstream port.
-	 */
-	val = (p & 0xf) << 12;
-	if (dsa_is_cpu_port(ds, p))
-		val |= ds->phys_port_mask;
-	else
-		val |= 1 << dsa_upstream_port(ds);
-	REG_WRITE(addr, 0x06, val);
-
-	/* Default VLAN ID and priority: don't set a default VLAN
-	 * ID, and set the default packet priority to zero.
-	 */
-	REG_WRITE(addr, 0x07, 0x0000);
-
 	/* Port Control 2: don't force a good FCS, set the maximum
 	 * frame size to 10240 bytes, don't let the switch add or
 	 * strip 802.1q tags, don't discard tagged or untagged frames
@@ -281,7 +264,7 @@ static int mv88e6352_setup_port(struct dsa_switch *ds, int p)
 	 */
 	REG_WRITE(addr, 0x19, 0x7654);
 
-	return 0;
+	return mv88e6xxx_setup_port_common(ds, p);
 }
 
 #ifdef CONFIG_NET_DSA_HWMON
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFT/RFC PATCH 6/8] net: dsa: mv88e6123_61_65: Use common port configuration
  2015-02-23 19:35 [RFT/RFC PATCH 0/8] net: dsa: mv88e6xxx: Add support for HW bridging Guenter Roeck
                   ` (4 preceding siblings ...)
  2015-02-23 19:35 ` [RFT/RFC PATCH 5/8] net: dsa: mv88e6352: Use common port initialization code Guenter Roeck
@ 2015-02-23 19:35 ` Guenter Roeck
  2015-02-23 19:35 ` [RFT/RFC PATCH 7/8] net: dsa: mv88e6171: " Guenter Roeck
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Guenter Roeck @ 2015-02-23 19:35 UTC (permalink / raw)
  To: netdev
  Cc: David S. Miller, Andrew Lunn, Florian Fainelli, linux-kernel,
	Guenter Roeck

This will simplify adding offloaded bridge support later on.

Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
 drivers/net/dsa/mv88e6123_61_65.c | 19 +------------------
 1 file changed, 1 insertion(+), 18 deletions(-)

diff --git a/drivers/net/dsa/mv88e6123_61_65.c b/drivers/net/dsa/mv88e6123_61_65.c
index 6ebe570..d8a3846 100644
--- a/drivers/net/dsa/mv88e6123_61_65.c
+++ b/drivers/net/dsa/mv88e6123_61_65.c
@@ -227,23 +227,6 @@ static int mv88e6123_61_65_setup_port(struct dsa_switch *ds, int p)
 	 */
 	REG_WRITE(addr, 0x05, dsa_is_cpu_port(ds, p) ? 0x8000 : 0x0000);
 
-	/* Port based VLAN map: give each port its own address
-	 * database, allow the CPU port to talk to each of the 'real'
-	 * ports, and allow each of the 'real' ports to only talk to
-	 * the upstream port.
-	 */
-	val = (p & 0xf) << 12;
-	if (dsa_is_cpu_port(ds, p))
-		val |= ds->phys_port_mask;
-	else
-		val |= 1 << dsa_upstream_port(ds);
-	REG_WRITE(addr, 0x06, val);
-
-	/* Default VLAN ID and priority: don't set a default VLAN
-	 * ID, and set the default packet priority to zero.
-	 */
-	REG_WRITE(addr, 0x07, 0x0000);
-
 	/* Port Control 2: don't force a good FCS, set the maximum
 	 * frame size to 10240 bytes, don't let the switch add or
 	 * strip 802.1q tags, don't discard tagged or untagged frames
@@ -288,7 +271,7 @@ static int mv88e6123_61_65_setup_port(struct dsa_switch *ds, int p)
 	 */
 	REG_WRITE(addr, 0x19, 0x7654);
 
-	return 0;
+	return mv88e6xxx_setup_port_common(ds, p);
 }
 
 static int mv88e6123_61_65_setup(struct dsa_switch *ds)
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFT/RFC PATCH 7/8] net: dsa: mv88e6171: Use common port configuration
  2015-02-23 19:35 [RFT/RFC PATCH 0/8] net: dsa: mv88e6xxx: Add support for HW bridging Guenter Roeck
                   ` (5 preceding siblings ...)
  2015-02-23 19:35 ` [RFT/RFC PATCH 6/8] net: dsa: mv88e6123_61_65: Use common port configuration Guenter Roeck
@ 2015-02-23 19:35 ` Guenter Roeck
  2015-02-23 19:35 ` [RFT/RFC PATCH 8/8] net: dsa: mv88e6352: Add support for hardware bridging Guenter Roeck
  2015-03-08 16:40 ` [RFT/RFC PATCH 0/8] net: dsa: mv88e6xxx: Add support for HW bridging Guenter Roeck
  8 siblings, 0 replies; 10+ messages in thread
From: Guenter Roeck @ 2015-02-23 19:35 UTC (permalink / raw)
  To: netdev
  Cc: David S. Miller, Andrew Lunn, Florian Fainelli, linux-kernel,
	Guenter Roeck

Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
 drivers/net/dsa/mv88e6171.c | 19 +------------------
 1 file changed, 1 insertion(+), 18 deletions(-)

diff --git a/drivers/net/dsa/mv88e6171.c b/drivers/net/dsa/mv88e6171.c
index 2374205..339484a 100644
--- a/drivers/net/dsa/mv88e6171.c
+++ b/drivers/net/dsa/mv88e6171.c
@@ -226,23 +226,6 @@ static int mv88e6171_setup_port(struct dsa_switch *ds, int p)
 	 */
 	REG_WRITE(addr, 0x05, dsa_is_cpu_port(ds, p) ? 0x8000 : 0x0000);
 
-	/* Port based VLAN map: give each port its own address
-	 * database, allow the CPU port to talk to each of the 'real'
-	 * ports, and allow each of the 'real' ports to only talk to
-	 * the upstream port.
-	 */
-	val = (p & 0xf) << 12;
-	if (dsa_is_cpu_port(ds, p))
-		val |= ds->phys_port_mask;
-	else
-		val |= 1 << dsa_upstream_port(ds);
-	REG_WRITE(addr, 0x06, val);
-
-	/* Default VLAN ID and priority: don't set a default VLAN
-	 * ID, and set the default packet priority to zero.
-	 */
-	REG_WRITE(addr, 0x07, 0x0000);
-
 	/* Port Control 2: don't force a good FCS, set the maximum
 	 * frame size to 10240 bytes, don't let the switch add or
 	 * strip 802.1q tags, don't discard tagged or untagged frames
@@ -287,7 +270,7 @@ static int mv88e6171_setup_port(struct dsa_switch *ds, int p)
 	 */
 	REG_WRITE(addr, 0x19, 0x7654);
 
-	return 0;
+	return mv88e6xxx_setup_port_common(ds, p);
 }
 
 static int mv88e6171_setup(struct dsa_switch *ds)
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFT/RFC PATCH 8/8] net: dsa: mv88e6352: Add support for hardware bridging
  2015-02-23 19:35 [RFT/RFC PATCH 0/8] net: dsa: mv88e6xxx: Add support for HW bridging Guenter Roeck
                   ` (6 preceding siblings ...)
  2015-02-23 19:35 ` [RFT/RFC PATCH 7/8] net: dsa: mv88e6171: " Guenter Roeck
@ 2015-02-23 19:35 ` Guenter Roeck
  2015-03-08 16:40 ` [RFT/RFC PATCH 0/8] net: dsa: mv88e6xxx: Add support for HW bridging Guenter Roeck
  8 siblings, 0 replies; 10+ messages in thread
From: Guenter Roeck @ 2015-02-23 19:35 UTC (permalink / raw)
  To: netdev
  Cc: David S. Miller, Andrew Lunn, Florian Fainelli, linux-kernel,
	Guenter Roeck

Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
 drivers/net/dsa/mv88e6352.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/net/dsa/mv88e6352.c b/drivers/net/dsa/mv88e6352.c
index d7053db..7be7b03 100644
--- a/drivers/net/dsa/mv88e6352.c
+++ b/drivers/net/dsa/mv88e6352.c
@@ -713,6 +713,9 @@ struct dsa_switch_driver mv88e6352_switch_driver = {
 	.set_eeprom		= mv88e6352_set_eeprom,
 	.get_regs_len		= mv88e6xxx_get_regs_len,
 	.get_regs		= mv88e6xxx_get_regs,
+	.port_join_bridge	= mv88e6xxx_join_bridge,
+	.port_leave_bridge	= mv88e6xxx_leave_bridge,
+	.port_stp_update	= mv88e6xxx_port_stp_update,
 };
 
 MODULE_ALIAS("platform:mv88e6352");
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [RFT/RFC PATCH 0/8] net: dsa: mv88e6xxx: Add support for HW bridging
  2015-02-23 19:35 [RFT/RFC PATCH 0/8] net: dsa: mv88e6xxx: Add support for HW bridging Guenter Roeck
                   ` (7 preceding siblings ...)
  2015-02-23 19:35 ` [RFT/RFC PATCH 8/8] net: dsa: mv88e6352: Add support for hardware bridging Guenter Roeck
@ 2015-03-08 16:40 ` Guenter Roeck
  8 siblings, 0 replies; 10+ messages in thread
From: Guenter Roeck @ 2015-03-08 16:40 UTC (permalink / raw)
  To: netdev; +Cc: David S. Miller, Andrew Lunn, Florian Fainelli, linux-kernel

On 02/23/2015 11:35 AM, Guenter Roeck wrote:
> This patch series applies on top of net-next, plus Florian's patches
> integrating dsa with SWITCHDEV for HW bridging. It also requires the patch
> 'net: dsa: Ensure that port array elements are initialized before being used'.
>
> HW bridging support is currently only enabled for MV8865352, but it should be
> straightforward to enable support for other Marvell chips by adding three lines
> of initialization code to the respective source; see patch 8/8 for details.
> mv886e3131.c would also have to use mv88e6xxx_setup_port_common().
>
> Patches 1-3 prepare the mv88e6xxx code for adding HW bridging support.
> Patch 4 adds core HW bridging support to the mv88e6xxx code. Patch 5-7
> prepare the drivers for mv88e6352, mv88e6123_61_65, and mv88e6171 for
> HW bridging support, without actually enabling it. Patch 8 enables
> HW bridging support in the mv88e6352 driver.
>
> Patches 5 and 8 could possibly be merged, but I thought it was better
> to keep them separate because they logically serve a different purpose.
>
> Testing has been minimal, and only with MV88E6352. Ports can be added to
> and removed from a bridge, and I have been able to pass data through a port
> configured as bridge port.
>
An updated version of this patch set is available in branch dsa-next
of my repository at kernel.org.
	git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git

Some bug fixes, added support to display fdb contents, and experimental
(untested) changes adding support for more chip types to the mv88e6131 driver.

Someone suggested that submitting series of RFC/RFT patches to the netdev
mailing list might be considered noise, so I'll refrain from doing that until
I believe that the patch set is ready for integration.

Guenter


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2015-03-08 16:40 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-02-23 19:35 [RFT/RFC PATCH 0/8] net: dsa: mv88e6xxx: Add support for HW bridging Guenter Roeck
2015-02-23 19:35 ` [RFT/RFC PATCH 1/8] net: dsa: mv88e6xxx: Factor out common initialization code Guenter Roeck
2015-02-23 19:35 ` [RFT/RFC PATCH 2/8] net: dsa: mv88e6xxx: Provide function for common port initialization Guenter Roeck
2015-02-23 19:35 ` [RFT/RFC PATCH 3/8] net: dsa: mv88e6xxx: Split mv88e6xxx_reg_read and mv88e6xxx_reg_write Guenter Roeck
2015-02-23 19:35 ` [RFT/RFC PATCH 4/8] net: dsa: mv88e6xxx: Add Hardware bridging support Guenter Roeck
2015-02-23 19:35 ` [RFT/RFC PATCH 5/8] net: dsa: mv88e6352: Use common port initialization code Guenter Roeck
2015-02-23 19:35 ` [RFT/RFC PATCH 6/8] net: dsa: mv88e6123_61_65: Use common port configuration Guenter Roeck
2015-02-23 19:35 ` [RFT/RFC PATCH 7/8] net: dsa: mv88e6171: " Guenter Roeck
2015-02-23 19:35 ` [RFT/RFC PATCH 8/8] net: dsa: mv88e6352: Add support for hardware bridging Guenter Roeck
2015-03-08 16:40 ` [RFT/RFC PATCH 0/8] net: dsa: mv88e6xxx: Add support for HW bridging Guenter Roeck

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.