linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH net-next 0/7] net: dsa: mv88e6xxx: add hardware VLAN support
@ 2015-08-13 16:52 Vivien Didelot
  2015-08-13 16:52 ` [PATCH net-next 1/7] net: dsa: add support for switchdev VLAN objects Vivien Didelot
                   ` (7 more replies)
  0 siblings, 8 replies; 12+ messages in thread
From: Vivien Didelot @ 2015-08-13 16:52 UTC (permalink / raw)
  To: netdev
  Cc: linux-kernel, kernel, David S. Miller, Guenter Roeck,
	Andrew Lunn, Florian Fainelli, Scott Feldman, Jiri Pirko,
	Chris Healy, Vivien Didelot

Hi All,

This patchset brings support to access hardware VLAN entries in DSA and
mv88e6xxx, through switchdev VLAN objects.

In the following example, ports swp[0-2] belong to bridge br0, and ports
swp[3-4] belong to bridge br1. Here's an example of what can be achieved
after this patchset:

    # bridge vlan add dev swp1 vid 100 master
    # bridge vlan add dev swp2 vid 100 master
    # bridge vlan add dev swp3 vid 100 master
    # bridge vlan add dev swp4 vid 100 master
    # bridge vlan del dev swp1 vid 100 master

The above commands correctly programmed hardware VLAN 100 for port swp2,
while ports swp3 and swp4 use software VLAN 100, as shown with:

    # bridge vlan
    port	vlan ids
    swp0	None
    swp0
    swp1	None
    swp1
    swp2	 100
    
    swp2	 100
    
    swp3	 100
    
    swp3
    swp4	 100
    
    swp4
    br0	None
    br1	None

Assuming that port 5 is the CPU port, the hardware VLAN table would
contain the following data:

    VID  FID  SID  0  1  2  3  4  5  6
    100    8    0  x  x  t  x  x  t  x

Where 'x' means excluded, and 't' means tagged.

Also, adding an FDB entry to VLAN 100 for port swp2 like this:

    # bridge fdb add 3c:97:0e:11:6e:30 dev swp2 vlan 100

Would result in the following example output:

    # bridge fdb
    # 01:00:5e:00:00:01 dev eth0 self permanent
    # 01:00:5e:00:00:01 dev eth1 self permanent
    # 00:50:d2:10:78:15 dev swp0 master br0 permanent
    # 00:50:d2:10:78:15 dev swp2 vlan 100 master br0 permanent
    # 3c:97:0e:11:6e:30 dev swp2 vlan 100 self static
    # 00:50:d2:10:78:15 dev swp3 master br1 permanent
    # 00:50:d2:10:78:15 dev swp3 vlan 100 master br1 permanent

And the Address Translation Unit would contain:

    DB   T/P  Vec State Addr
    008  Port 004   e   3c:97:0e:11:6e:30

Cheers,
-v

Vivien Didelot (7):
  net: dsa: add support for switchdev VLAN objects
  net: dsa: mv88e6xxx: flush VTU and STU entries
  net: dsa: mv88e6xxx: add VLAN Get Next support
  net: dsa: mv88e6xxx: add VLAN support to FDB dump
  net: dsa: mv88e6xxx: add VLAN Purge support
  net: dsa: mv88e6xxx: add VLAN Load support
  net: dsa: mv88e6xxx: use port 802.1Q mode Secure

 drivers/net/dsa/mv88e6352.c |   5 +
 drivers/net/dsa/mv88e6xxx.c | 510 +++++++++++++++++++++++++++++++++++++++++++-
 drivers/net/dsa/mv88e6xxx.h |  45 ++++
 include/net/dsa.h           |  11 +
 net/dsa/slave.c             | 158 ++++++++++++++
 5 files changed, 720 insertions(+), 9 deletions(-)

-- 
2.5.0


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

* [PATCH net-next 1/7] net: dsa: add support for switchdev VLAN objects
  2015-08-13 16:52 [PATCH net-next 0/7] net: dsa: mv88e6xxx: add hardware VLAN support Vivien Didelot
@ 2015-08-13 16:52 ` Vivien Didelot
  2015-08-13 20:16   ` Andrew Lunn
  2015-08-13 16:52 ` [PATCH net-next 2/7] net: dsa: mv88e6xxx: flush VTU and STU entries Vivien Didelot
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 12+ messages in thread
From: Vivien Didelot @ 2015-08-13 16:52 UTC (permalink / raw)
  To: netdev
  Cc: linux-kernel, kernel, David S. Miller, Guenter Roeck,
	Andrew Lunn, Florian Fainelli, Scott Feldman, Jiri Pirko,
	Chris Healy, Vivien Didelot

Add new functions in DSA drivers to access hardware VLAN entries through
SWITCHDEV_OBJ_PORT_VLAN objects:

 - port_pvid_get() and vlan_getnext() to dump a VLAN
 - port_vlan_del() to exclude a port from a VLAN
 - port_pvid_set() and port_vlan_add() to join a port to a VLAN

The DSA infrastructure will ensure that each VLAN of the given range
does not already belong to another bridge. If it does, it will fallback
to software VLAN and won't program the hardware.

Signed-off-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
---
 include/net/dsa.h |  11 ++++
 net/dsa/slave.c   | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 169 insertions(+)

diff --git a/include/net/dsa.h b/include/net/dsa.h
index 6356f43..bd9b765 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -298,6 +298,17 @@ struct dsa_switch_driver {
 				   u8 state);
 
 	/*
+	 * VLAN support
+	 */
+	int	(*port_pvid_get)(struct dsa_switch *ds, int port, u16 *pvid);
+	int	(*port_pvid_set)(struct dsa_switch *ds, int port, u16 pvid);
+	int	(*port_vlan_add)(struct dsa_switch *ds, int port, u16 vid,
+				 bool untagged);
+	int	(*port_vlan_del)(struct dsa_switch *ds, int port, u16 vid);
+	int	(*vlan_getnext)(struct dsa_switch *ds, u16 *vid,
+				unsigned long *ports, unsigned long *untagged);
+
+	/*
 	 * Forwarding database
 	 */
 	int	(*port_fdb_add)(struct dsa_switch *ds, int port,
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 2767584..880ead7 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -200,6 +200,152 @@ out:
 	return 0;
 }
 
+static int dsa_bridge_check_vlan_range(struct dsa_switch *ds,
+				       const struct net_device *bridge,
+				       u16 vid_begin, u16 vid_end)
+{
+	struct dsa_slave_priv *p;
+	struct net_device *dev, *vlan_br;
+	DECLARE_BITMAP(members, DSA_MAX_PORTS);
+	DECLARE_BITMAP(untagged, DSA_MAX_PORTS);
+	u16 vid;
+	int member, err;
+
+	if (!ds->drv->vlan_getnext || !vid_begin)
+		return -EOPNOTSUPP;
+
+	vid = vid_begin - 1;
+
+	do {
+		err = ds->drv->vlan_getnext(ds, &vid, members, untagged);
+		if (err)
+			break;
+
+		if (vid > vid_end)
+			break;
+
+		member = find_first_bit(members, DSA_MAX_PORTS);
+		if (member == DSA_MAX_PORTS)
+			continue;
+
+		dev = ds->ports[member];
+		p = netdev_priv(dev);
+		vlan_br = p->bridge_dev;
+		if (vlan_br == bridge)
+			continue;
+
+		netdev_dbg(vlan_br, "hardware VLAN %d already in use\n", vid);
+		return -EOPNOTSUPP;
+	} while (vid < vid_end);
+
+	return err == -ENOENT ? 0 : err;
+}
+
+static int dsa_slave_port_vlan_add(struct net_device *dev,
+				   struct switchdev_obj *obj)
+{
+	struct switchdev_obj_vlan *vlan = &obj->u.vlan;
+	struct dsa_slave_priv *p = netdev_priv(dev);
+	struct dsa_switch *ds = p->parent;
+	u16 vid;
+	int err;
+
+	switch (obj->trans) {
+	case SWITCHDEV_TRANS_PREPARE:
+		if (!ds->drv->port_vlan_add || !ds->drv->port_pvid_set)
+			return -EOPNOTSUPP;
+
+		/* If the requested port doesn't belong to the same bridge as
+		 * the VLAN members, fallback to software VLAN (hopefully).
+		 */
+		err = dsa_bridge_check_vlan_range(ds, p->bridge_dev,
+						  vlan->vid_begin,
+						  vlan->vid_end);
+		if (err)
+			return err;
+		break;
+	case SWITCHDEV_TRANS_COMMIT:
+		for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
+			err = ds->drv->port_vlan_add(ds, p->port, vid,
+						     vlan->flags &
+						     BRIDGE_VLAN_INFO_UNTAGGED);
+			if (!err && vlan->flags & BRIDGE_VLAN_INFO_PVID)
+				err = ds->drv->port_pvid_set(ds, p->port, vid);
+			if (err)
+				return err;
+		}
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static int dsa_slave_port_vlan_del(struct net_device *dev,
+				   struct switchdev_obj *obj)
+{
+	struct switchdev_obj_vlan *vlan = &obj->u.vlan;
+	struct dsa_slave_priv *p = netdev_priv(dev);
+	struct dsa_switch *ds = p->parent;
+	u16 vid;
+	int err;
+
+	if (!ds->drv->port_vlan_del)
+		return -EOPNOTSUPP;
+
+	for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
+		err = ds->drv->port_vlan_del(ds, p->port, vid);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static int dsa_slave_port_vlan_dump(struct net_device *dev,
+				    struct switchdev_obj *obj)
+{
+	struct switchdev_obj_vlan *vlan = &obj->u.vlan;
+	struct dsa_slave_priv *p = netdev_priv(dev);
+	struct dsa_switch *ds = p->parent;
+	DECLARE_BITMAP(members, DSA_MAX_PORTS);
+	DECLARE_BITMAP(untagged, DSA_MAX_PORTS);
+	u16 pvid, vid = 0;
+	int err;
+
+	if (!ds->drv->vlan_getnext || !ds->drv->port_pvid_get)
+		return -EOPNOTSUPP;
+
+	err = ds->drv->port_pvid_get(ds, p->port, &pvid);
+	if (err)
+		return err;
+
+	for (;;) {
+		err = ds->drv->vlan_getnext(ds, &vid, members, untagged);
+		if (err)
+			break;
+
+		if (!test_bit(p->port, members))
+			continue;
+
+		memset(vlan, 0, sizeof(*vlan));
+		vlan->vid_begin = vlan->vid_end = vid;
+
+		if (vid == pvid)
+			vlan->flags |= BRIDGE_VLAN_INFO_PVID;
+
+		if (test_bit(p->port, untagged))
+			vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
+
+		err = obj->cb(dev, obj);
+		if (err)
+			break;
+	}
+
+	return err == -ENOENT ? 0 : err;
+}
+
 static int dsa_slave_port_fdb_add(struct net_device *dev,
 				  struct switchdev_obj *obj)
 {
@@ -341,6 +487,9 @@ static int dsa_slave_port_obj_add(struct net_device *dev,
 	case SWITCHDEV_OBJ_PORT_FDB:
 		err = dsa_slave_port_fdb_add(dev, obj);
 		break;
+	case SWITCHDEV_OBJ_PORT_VLAN:
+		err = dsa_slave_port_vlan_add(dev, obj);
+		break;
 	default:
 		err = -EOPNOTSUPP;
 		break;
@@ -358,6 +507,9 @@ static int dsa_slave_port_obj_del(struct net_device *dev,
 	case SWITCHDEV_OBJ_PORT_FDB:
 		err = dsa_slave_port_fdb_del(dev, obj);
 		break;
+	case SWITCHDEV_OBJ_PORT_VLAN:
+		err = dsa_slave_port_vlan_del(dev, obj);
+		break;
 	default:
 		err = -EOPNOTSUPP;
 		break;
@@ -375,6 +527,9 @@ static int dsa_slave_port_obj_dump(struct net_device *dev,
 	case SWITCHDEV_OBJ_PORT_FDB:
 		err = dsa_slave_port_fdb_dump(dev, obj);
 		break;
+	case SWITCHDEV_OBJ_PORT_VLAN:
+		err = dsa_slave_port_vlan_dump(dev, obj);
+		break;
 	default:
 		err = -EOPNOTSUPP;
 		break;
@@ -794,6 +949,9 @@ static const struct net_device_ops dsa_slave_netdev_ops = {
 	.ndo_netpoll_cleanup	= dsa_slave_netpoll_cleanup,
 	.ndo_poll_controller	= dsa_slave_poll_controller,
 #endif
+	.ndo_bridge_getlink	= switchdev_port_bridge_getlink,
+	.ndo_bridge_setlink	= switchdev_port_bridge_setlink,
+	.ndo_bridge_dellink	= switchdev_port_bridge_dellink,
 };
 
 static const struct switchdev_ops dsa_slave_switchdev_ops = {
-- 
2.5.0


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

* [PATCH net-next 2/7] net: dsa: mv88e6xxx: flush VTU and STU entries
  2015-08-13 16:52 [PATCH net-next 0/7] net: dsa: mv88e6xxx: add hardware VLAN support Vivien Didelot
  2015-08-13 16:52 ` [PATCH net-next 1/7] net: dsa: add support for switchdev VLAN objects Vivien Didelot
@ 2015-08-13 16:52 ` Vivien Didelot
  2015-08-13 16:52 ` [PATCH net-next 3/7] net: dsa: mv88e6xxx: add VLAN Get Next support Vivien Didelot
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 12+ messages in thread
From: Vivien Didelot @ 2015-08-13 16:52 UTC (permalink / raw)
  To: netdev
  Cc: linux-kernel, kernel, David S. Miller, Guenter Roeck,
	Andrew Lunn, Florian Fainelli, Scott Feldman, Jiri Pirko,
	Chris Healy, Vivien Didelot

Implement the VTU Flush operation (which also flushes the STU), so that
warm boots won't preserved old entries.

Signed-off-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
---
 drivers/net/dsa/mv88e6xxx.c | 34 ++++++++++++++++++++++++++++++++++
 drivers/net/dsa/mv88e6xxx.h |  2 ++
 2 files changed, 36 insertions(+)

diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c
index 9978245..175353a 100644
--- a/drivers/net/dsa/mv88e6xxx.c
+++ b/drivers/net/dsa/mv88e6xxx.c
@@ -1182,6 +1182,34 @@ int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state)
 	return 0;
 }
 
+static int _mv88e6xxx_vtu_wait(struct dsa_switch *ds)
+{
+	return _mv88e6xxx_wait(ds, REG_GLOBAL, GLOBAL_VTU_OP,
+			       GLOBAL_VTU_OP_BUSY);
+}
+
+static int _mv88e6xxx_vtu_cmd(struct dsa_switch *ds, u16 op)
+{
+	int ret;
+
+	ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_OP, op);
+	if (ret < 0)
+		return ret;
+
+	return _mv88e6xxx_vtu_wait(ds);
+}
+
+static int _mv88e6xxx_vtu_stu_flush(struct dsa_switch *ds)
+{
+	int ret;
+
+	ret = _mv88e6xxx_vtu_wait(ds);
+	if (ret < 0)
+		return ret;
+
+	return _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_FLUSH_ALL);
+}
+
 static int _mv88e6xxx_atu_mac_write(struct dsa_switch *ds,
 				    const unsigned char *addr)
 {
@@ -2071,6 +2099,12 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds)
 	/* Wait for the flush to complete. */
 	mutex_lock(&ps->smi_mutex);
 	ret = _mv88e6xxx_stats_wait(ds);
+	if (ret < 0)
+		goto unlock;
+
+	/* Clear all the VTU and STU entries */
+	ret = _mv88e6xxx_vtu_stu_flush(ds);
+unlock:
 	mutex_unlock(&ps->smi_mutex);
 
 	return ret;
diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h
index 10fae32..76139ea 100644
--- a/drivers/net/dsa/mv88e6xxx.h
+++ b/drivers/net/dsa/mv88e6xxx.h
@@ -188,6 +188,8 @@
 #define GLOBAL_CONTROL_TCAM_EN		BIT(1)
 #define GLOBAL_CONTROL_EEPROM_DONE_EN	BIT(0)
 #define GLOBAL_VTU_OP		0x05
+#define GLOBAL_VTU_OP_BUSY	BIT(15)
+#define GLOBAL_VTU_OP_FLUSH_ALL		((0x01 << 12) | GLOBAL_VTU_OP_BUSY)
 #define GLOBAL_VTU_VID		0x06
 #define GLOBAL_VTU_DATA_0_3	0x07
 #define GLOBAL_VTU_DATA_4_7	0x08
-- 
2.5.0


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

* [PATCH net-next 3/7] net: dsa: mv88e6xxx: add VLAN Get Next support
  2015-08-13 16:52 [PATCH net-next 0/7] net: dsa: mv88e6xxx: add hardware VLAN support Vivien Didelot
  2015-08-13 16:52 ` [PATCH net-next 1/7] net: dsa: add support for switchdev VLAN objects Vivien Didelot
  2015-08-13 16:52 ` [PATCH net-next 2/7] net: dsa: mv88e6xxx: flush VTU and STU entries Vivien Didelot
@ 2015-08-13 16:52 ` Vivien Didelot
  2015-08-13 16:52 ` [PATCH net-next 4/7] net: dsa: mv88e6xxx: add VLAN support to FDB dump Vivien Didelot
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 12+ messages in thread
From: Vivien Didelot @ 2015-08-13 16:52 UTC (permalink / raw)
  To: netdev
  Cc: linux-kernel, kernel, David S. Miller, Guenter Roeck,
	Andrew Lunn, Florian Fainelli, Scott Feldman, Jiri Pirko,
	Chris Healy, Vivien Didelot

Implement the port_pvid_get and vlan_getnext driver functions required
to dump VLAN entries from the hardware, with the VTU Get Next operation.

Some functions and structure will be shared with STU operations, since
their table format are similar (e.g. STU data entries are accessible
with the same registers as VTU entries, except with an offset of 2).

Signed-off-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
---
 drivers/net/dsa/mv88e6352.c |   2 +
 drivers/net/dsa/mv88e6xxx.c | 138 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/net/dsa/mv88e6xxx.h |  27 +++++++++
 3 files changed, 167 insertions(+)

diff --git a/drivers/net/dsa/mv88e6352.c b/drivers/net/dsa/mv88e6352.c
index a18f7c8..e6767ce 100644
--- a/drivers/net/dsa/mv88e6352.c
+++ b/drivers/net/dsa/mv88e6352.c
@@ -343,6 +343,8 @@ struct dsa_switch_driver mv88e6352_switch_driver = {
 	.port_join_bridge	= mv88e6xxx_join_bridge,
 	.port_leave_bridge	= mv88e6xxx_leave_bridge,
 	.port_stp_update	= mv88e6xxx_port_stp_update,
+	.port_pvid_get		= mv88e6xxx_port_pvid_get,
+	.vlan_getnext		= mv88e6xxx_vlan_getnext,
 	.port_fdb_add		= mv88e6xxx_port_fdb_add,
 	.port_fdb_del		= mv88e6xxx_port_fdb_del,
 	.port_fdb_getnext	= mv88e6xxx_port_fdb_getnext,
diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c
index 175353a..ecdd9da 100644
--- a/drivers/net/dsa/mv88e6xxx.c
+++ b/drivers/net/dsa/mv88e6xxx.c
@@ -2,6 +2,9 @@
  * net/dsa/mv88e6xxx.c - Marvell 88e6xxx switch chip support
  * Copyright (c) 2008 Marvell Semiconductor
  *
+ * Copyright (c) 2015 CMC Electronics, Inc.
+ *	Added support for VLAN Table Unit operations
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -1182,6 +1185,19 @@ int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state)
 	return 0;
 }
 
+int mv88e6xxx_port_pvid_get(struct dsa_switch *ds, int port, u16 *pvid)
+{
+	int ret;
+
+	ret = mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_DEFAULT_VLAN);
+	if (ret < 0)
+		return ret;
+
+	*pvid = ret & PORT_DEFAULT_VLAN_MASK;
+
+	return 0;
+}
+
 static int _mv88e6xxx_vtu_wait(struct dsa_switch *ds)
 {
 	return _mv88e6xxx_wait(ds, REG_GLOBAL, GLOBAL_VTU_OP,
@@ -1210,6 +1226,128 @@ static int _mv88e6xxx_vtu_stu_flush(struct dsa_switch *ds)
 	return _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_FLUSH_ALL);
 }
 
+static int _mv88e6xxx_vtu_stu_data_read(struct dsa_switch *ds,
+					struct mv88e6xxx_vtu_stu_entry *entry,
+					unsigned int nibble_offset)
+{
+	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+	u16 regs[3];
+	int i;
+	int ret;
+
+	for (i = 0; i < 3; ++i) {
+		ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL,
+					  GLOBAL_VTU_DATA_0_3 + i);
+		if (ret < 0)
+			return ret;
+
+		regs[i] = ret;
+	}
+
+	for (i = 0; i < ps->num_ports; ++i) {
+		unsigned int shift = (i % 4) * 4 + nibble_offset;
+		u16 reg = regs[i / 4];
+
+		entry->data[i] = (reg >> shift) & GLOBAL_VTU_STU_DATA_MASK;
+	}
+
+	return 0;
+}
+
+static int _mv88e6xxx_vtu_getnext(struct dsa_switch *ds, u16 vid,
+				  struct mv88e6xxx_vtu_stu_entry *entry)
+{
+	struct mv88e6xxx_vtu_stu_entry next = { 0 };
+	int ret;
+
+	ret = _mv88e6xxx_vtu_wait(ds);
+	if (ret < 0)
+		return ret;
+
+	ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_VID,
+				   vid & GLOBAL_VTU_VID_MASK);
+	if (ret < 0)
+		return ret;
+
+	ret = _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_VTU_GET_NEXT);
+	if (ret < 0)
+		return ret;
+
+	ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_VTU_VID);
+	if (ret < 0)
+		return ret;
+
+	next.vid = ret & GLOBAL_VTU_VID_MASK;
+	next.valid = !!(ret & GLOBAL_VTU_VID_VALID);
+
+	if (next.valid) {
+		ret = _mv88e6xxx_vtu_stu_data_read(ds, &next, 0);
+		if (ret < 0)
+			return ret;
+
+		if (mv88e6xxx_6097_family(ds) || mv88e6xxx_6165_family(ds) ||
+		    mv88e6xxx_6351_family(ds) || mv88e6xxx_6352_family(ds)) {
+			ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL,
+						  GLOBAL_VTU_FID);
+			if (ret < 0)
+				return ret;
+
+			next.fid = ret & GLOBAL_VTU_FID_MASK;
+
+			ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL,
+						  GLOBAL_VTU_SID);
+			if (ret < 0)
+				return ret;
+
+			next.sid = ret & GLOBAL_VTU_SID_MASK;
+		}
+	}
+
+	*entry = next;
+	return 0;
+}
+
+int mv88e6xxx_vlan_getnext(struct dsa_switch *ds, u16 *vid,
+			   unsigned long *ports, unsigned long *untagged)
+{
+	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+	struct mv88e6xxx_vtu_stu_entry next;
+	int port;
+	int err;
+
+	if (*vid == 4095)
+		return -ENOENT;
+
+	mutex_lock(&ps->smi_mutex);
+	err = _mv88e6xxx_vtu_getnext(ds, *vid, &next);
+	mutex_unlock(&ps->smi_mutex);
+
+	if (err)
+		return err;
+
+	if (!next.valid)
+		return -ENOENT;
+
+	*vid = next.vid;
+
+	for (port = 0; port < ps->num_ports; ++port) {
+		clear_bit(port, ports);
+		clear_bit(port, untagged);
+
+		if (dsa_is_cpu_port(ds, port))
+			continue;
+
+		if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED ||
+		    next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED)
+			set_bit(port, ports);
+
+		if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED)
+			set_bit(port, untagged);
+	}
+
+	return 0;
+}
+
 static int _mv88e6xxx_atu_mac_write(struct dsa_switch *ds,
 				    const unsigned char *addr)
 {
diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h
index 76139ea..bb6fa9a 100644
--- a/drivers/net/dsa/mv88e6xxx.h
+++ b/drivers/net/dsa/mv88e6xxx.h
@@ -131,6 +131,7 @@
 #define PORT_CONTROL_1		0x05
 #define PORT_BASE_VLAN		0x06
 #define PORT_DEFAULT_VLAN	0x07
+#define PORT_DEFAULT_VLAN_MASK	0xfff
 #define PORT_CONTROL_2		0x08
 #define PORT_CONTROL_2_IGNORE_FCS	BIT(15)
 #define PORT_CONTROL_2_VTU_PRI_OVERRIDE	BIT(14)
@@ -172,6 +173,10 @@
 #define GLOBAL_MAC_23		0x02
 #define GLOBAL_MAC_45		0x03
 #define GLOBAL_ATU_FID		0x01	/* 6097 6165 6351 6352 */
+#define GLOBAL_VTU_FID		0x02	/* 6097 6165 6351 6352 */
+#define GLOBAL_VTU_FID_MASK	0xfff
+#define GLOBAL_VTU_SID		0x03	/* 6097 6165 6351 6352 */
+#define GLOBAL_VTU_SID_MASK	0x3f
 #define GLOBAL_CONTROL		0x04
 #define GLOBAL_CONTROL_SW_RESET		BIT(15)
 #define GLOBAL_CONTROL_PPU_ENABLE	BIT(14)
@@ -190,10 +195,18 @@
 #define GLOBAL_VTU_OP		0x05
 #define GLOBAL_VTU_OP_BUSY	BIT(15)
 #define GLOBAL_VTU_OP_FLUSH_ALL		((0x01 << 12) | GLOBAL_VTU_OP_BUSY)
+#define GLOBAL_VTU_OP_VTU_GET_NEXT	((0x04 << 12) | GLOBAL_VTU_OP_BUSY)
 #define GLOBAL_VTU_VID		0x06
+#define GLOBAL_VTU_VID_MASK	0xfff
+#define GLOBAL_VTU_VID_VALID	BIT(12)
 #define GLOBAL_VTU_DATA_0_3	0x07
 #define GLOBAL_VTU_DATA_4_7	0x08
 #define GLOBAL_VTU_DATA_8_11	0x09
+#define GLOBAL_VTU_STU_DATA_MASK		0x03
+#define GLOBAL_VTU_DATA_MEMBER_TAG_UNMODIFIED	0x00
+#define GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED	0x01
+#define GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED	0x02
+#define GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER	0x03
 #define GLOBAL_ATU_CONTROL	0x0a
 #define GLOBAL_ATU_CONTROL_LEARN2ALL	BIT(3)
 #define GLOBAL_ATU_OP		0x0b
@@ -328,6 +341,17 @@ struct mv88e6xxx_atu_entry {
 	u8	mac[ETH_ALEN];
 };
 
+struct mv88e6xxx_vtu_stu_entry {
+	/* VTU only */
+	u16	vid;
+	u16	fid;
+
+	/* VTU and STU */
+	u8	sid;
+	bool	valid;
+	u8	data[DSA_MAX_PORTS];
+};
+
 struct mv88e6xxx_priv_state {
 	/* When using multi-chip addressing, this mutex protects
 	 * access to the indirect access registers.  (In single-chip
@@ -428,6 +452,9 @@ int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
 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);
+int mv88e6xxx_port_pvid_get(struct dsa_switch *ds, int port, u16 *vid);
+int mv88e6xxx_vlan_getnext(struct dsa_switch *ds, u16 *vid,
+			   unsigned long *ports, unsigned long *untagged);
 int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
 			   const unsigned char *addr, u16 vid);
 int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
-- 
2.5.0


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

* [PATCH net-next 4/7] net: dsa: mv88e6xxx: add VLAN support to FDB dump
  2015-08-13 16:52 [PATCH net-next 0/7] net: dsa: mv88e6xxx: add hardware VLAN support Vivien Didelot
                   ` (2 preceding siblings ...)
  2015-08-13 16:52 ` [PATCH net-next 3/7] net: dsa: mv88e6xxx: add VLAN Get Next support Vivien Didelot
@ 2015-08-13 16:52 ` Vivien Didelot
  2015-08-13 16:52 ` [PATCH net-next 5/7] net: dsa: mv88e6xxx: add VLAN Purge support Vivien Didelot
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 12+ messages in thread
From: Vivien Didelot @ 2015-08-13 16:52 UTC (permalink / raw)
  To: netdev
  Cc: linux-kernel, kernel, David S. Miller, Guenter Roeck,
	Andrew Lunn, Florian Fainelli, Scott Feldman, Jiri Pirko,
	Chris Healy, Vivien Didelot

Add an helper function to read the next valid VLAN entry for a given
port. It is used in the VID to FID conversion function to retrieve the
forwarding database assigned to a given VLAN port.

Finally update the FDB getnext operation to iterate on the next valid
port VLAN when the end of the current database is reached.

Signed-off-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
---
 drivers/net/dsa/mv88e6xxx.c | 42 ++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 40 insertions(+), 2 deletions(-)

diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c
index ecdd9da..6c86bad 100644
--- a/drivers/net/dsa/mv88e6xxx.c
+++ b/drivers/net/dsa/mv88e6xxx.c
@@ -1307,6 +1307,29 @@ static int _mv88e6xxx_vtu_getnext(struct dsa_switch *ds, u16 vid,
 	return 0;
 }
 
+static int _mv88e6xxx_port_vtu_getnext(struct dsa_switch *ds, int port, u16 vid,
+				       struct mv88e6xxx_vtu_stu_entry *entry)
+{
+	int err;
+
+	do {
+		if (vid == 4095)
+			return -ENOENT;
+
+		err = _mv88e6xxx_vtu_getnext(ds, vid, entry);
+		if (err)
+			return err;
+
+		if (!entry->valid)
+			return -ENOENT;
+
+		vid = entry->vid;
+	} while (entry->data[port] != GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED &&
+		 entry->data[port] != GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED);
+
+	return 0;
+}
+
 int mv88e6xxx_vlan_getnext(struct dsa_switch *ds, u16 *vid,
 			   unsigned long *ports, unsigned long *untagged)
 {
@@ -1421,10 +1444,19 @@ static int _mv88e6xxx_atu_load(struct dsa_switch *ds,
 static int _mv88e6xxx_port_vid_to_fid(struct dsa_switch *ds, int port, u16 vid)
 {
 	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+	struct mv88e6xxx_vtu_stu_entry vlan;
+	int err;
 
 	if (vid == 0)
 		return ps->fid[port];
 
+	err = _mv88e6xxx_port_vtu_getnext(ds, port, vid - 1, &vlan);
+	if (err)
+		return err;
+
+	if (vlan.vid == vid)
+		return vlan.fid;
+
 	return -ENOENT;
 }
 
@@ -1548,8 +1580,14 @@ int mv88e6xxx_port_fdb_getnext(struct dsa_switch *ds, int port,
 
 	do {
 		if (is_broadcast_ether_addr(addr)) {
-			ret = -ENOENT;
-			goto unlock;
+			struct mv88e6xxx_vtu_stu_entry vtu;
+
+			ret = _mv88e6xxx_port_vtu_getnext(ds, port, *vid, &vtu);
+			if (ret < 0)
+				goto unlock;
+
+			*vid = vtu.vid;
+			fid = vtu.fid;
 		}
 
 		ret = _mv88e6xxx_atu_getnext(ds, fid, addr, &next);
-- 
2.5.0


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

* [PATCH net-next 5/7] net: dsa: mv88e6xxx: add VLAN Purge support
  2015-08-13 16:52 [PATCH net-next 0/7] net: dsa: mv88e6xxx: add hardware VLAN support Vivien Didelot
                   ` (3 preceding siblings ...)
  2015-08-13 16:52 ` [PATCH net-next 4/7] net: dsa: mv88e6xxx: add VLAN support to FDB dump Vivien Didelot
@ 2015-08-13 16:52 ` Vivien Didelot
  2015-08-13 16:52 ` [PATCH net-next 6/7] net: dsa: mv88e6xxx: add VLAN Load support Vivien Didelot
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 12+ messages in thread
From: Vivien Didelot @ 2015-08-13 16:52 UTC (permalink / raw)
  To: netdev
  Cc: linux-kernel, kernel, David S. Miller, Guenter Roeck,
	Andrew Lunn, Florian Fainelli, Scott Feldman, Jiri Pirko,
	Chris Healy, Vivien Didelot

Add support for the VTU Load Purge operation and implement the
port_vlan_del driver function to remove a port from a VLAN entry, and
delete the VLAN if the given port was its last member.

Signed-off-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
---
 drivers/net/dsa/mv88e6352.c |   1 +
 drivers/net/dsa/mv88e6xxx.c | 113 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/net/dsa/mv88e6xxx.h |   2 +
 3 files changed, 116 insertions(+)

diff --git a/drivers/net/dsa/mv88e6352.c b/drivers/net/dsa/mv88e6352.c
index e6767ce..cec38bb 100644
--- a/drivers/net/dsa/mv88e6352.c
+++ b/drivers/net/dsa/mv88e6352.c
@@ -344,6 +344,7 @@ struct dsa_switch_driver mv88e6352_switch_driver = {
 	.port_leave_bridge	= mv88e6xxx_leave_bridge,
 	.port_stp_update	= mv88e6xxx_port_stp_update,
 	.port_pvid_get		= mv88e6xxx_port_pvid_get,
+	.port_vlan_del		= mv88e6xxx_port_vlan_del,
 	.vlan_getnext		= mv88e6xxx_vlan_getnext,
 	.port_fdb_add		= mv88e6xxx_port_fdb_add,
 	.port_fdb_del		= mv88e6xxx_port_fdb_del,
diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c
index 6c86bad..8423924 100644
--- a/drivers/net/dsa/mv88e6xxx.c
+++ b/drivers/net/dsa/mv88e6xxx.c
@@ -1254,6 +1254,32 @@ static int _mv88e6xxx_vtu_stu_data_read(struct dsa_switch *ds,
 	return 0;
 }
 
+static int _mv88e6xxx_vtu_stu_data_write(struct dsa_switch *ds,
+					 struct mv88e6xxx_vtu_stu_entry *entry,
+					 unsigned int nibble_offset)
+{
+	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+	u16 regs[3] = { 0 };
+	int i;
+	int ret;
+
+	for (i = 0; i < ps->num_ports; ++i) {
+		unsigned int shift = (i % 4) * 4 + nibble_offset;
+		u8 data = entry->data[i];
+
+		regs[i / 4] |= (data & GLOBAL_VTU_STU_DATA_MASK) << shift;
+	}
+
+	for (i = 0; i < 3; ++i) {
+		ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL,
+					   GLOBAL_VTU_DATA_0_3 + i, regs[i]);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
 static int _mv88e6xxx_vtu_getnext(struct dsa_switch *ds, u16 vid,
 				  struct mv88e6xxx_vtu_stu_entry *entry)
 {
@@ -1307,6 +1333,93 @@ static int _mv88e6xxx_vtu_getnext(struct dsa_switch *ds, u16 vid,
 	return 0;
 }
 
+static int _mv88e6xxx_vtu_loadpurge(struct dsa_switch *ds,
+				    struct mv88e6xxx_vtu_stu_entry *entry)
+{
+	u16 reg = 0;
+	int ret;
+
+	ret = _mv88e6xxx_vtu_wait(ds);
+	if (ret < 0)
+		return ret;
+
+	if (!entry->valid)
+		goto loadpurge;
+
+	/* Write port member tags */
+	ret = _mv88e6xxx_vtu_stu_data_write(ds, entry, 0);
+	if (ret < 0)
+		return ret;
+
+	if (mv88e6xxx_6097_family(ds) || mv88e6xxx_6165_family(ds) ||
+	    mv88e6xxx_6351_family(ds) || mv88e6xxx_6352_family(ds)) {
+		reg = entry->sid & GLOBAL_VTU_SID_MASK;
+		ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_SID, reg);
+		if (ret < 0)
+			return ret;
+
+		reg = entry->fid & GLOBAL_VTU_FID_MASK;
+		ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_FID, reg);
+		if (ret < 0)
+			return ret;
+	}
+
+	reg = GLOBAL_VTU_VID_VALID;
+loadpurge:
+	reg |= entry->vid & GLOBAL_VTU_VID_MASK;
+	ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_VID, reg);
+	if (ret < 0)
+		return ret;
+
+	return _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_VTU_LOAD_PURGE);
+}
+
+int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, u16 vid)
+{
+	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+	struct mv88e6xxx_vtu_stu_entry vlan;
+	bool keep = false;
+	int i, err;
+
+	mutex_lock(&ps->smi_mutex);
+
+	err = _mv88e6xxx_vtu_getnext(ds, vid - 1, &vlan);
+	if (err)
+		goto unlock;
+
+	if (vlan.vid != vid || !vlan.valid ||
+	    vlan.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) {
+		err = -ENOENT;
+		goto unlock;
+	}
+
+	vlan.data[port] = GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
+
+	/* keep the VLAN unless all ports are excluded */
+	for (i = 0; i < ps->num_ports; ++i) {
+		if (dsa_is_cpu_port(ds, i))
+			continue;
+
+		if (vlan.data[i] != GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) {
+			keep = true;
+			break;
+		}
+	}
+
+	vlan.valid = keep;
+	err = _mv88e6xxx_vtu_loadpurge(ds, &vlan);
+	if (err)
+		goto unlock;
+
+	if (!keep)
+		clear_bit(vlan.fid, ps->fid_bitmap);
+
+unlock:
+	mutex_unlock(&ps->smi_mutex);
+
+	return err;
+}
+
 static int _mv88e6xxx_port_vtu_getnext(struct dsa_switch *ds, int port, u16 vid,
 				       struct mv88e6xxx_vtu_stu_entry *entry)
 {
diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h
index bb6fa9a..c70a3c1 100644
--- a/drivers/net/dsa/mv88e6xxx.h
+++ b/drivers/net/dsa/mv88e6xxx.h
@@ -195,6 +195,7 @@
 #define GLOBAL_VTU_OP		0x05
 #define GLOBAL_VTU_OP_BUSY	BIT(15)
 #define GLOBAL_VTU_OP_FLUSH_ALL		((0x01 << 12) | GLOBAL_VTU_OP_BUSY)
+#define GLOBAL_VTU_OP_VTU_LOAD_PURGE	((0x03 << 12) | GLOBAL_VTU_OP_BUSY)
 #define GLOBAL_VTU_OP_VTU_GET_NEXT	((0x04 << 12) | GLOBAL_VTU_OP_BUSY)
 #define GLOBAL_VTU_VID		0x06
 #define GLOBAL_VTU_VID_MASK	0xfff
@@ -453,6 +454,7 @@ 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);
 int mv88e6xxx_port_pvid_get(struct dsa_switch *ds, int port, u16 *vid);
+int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, u16 vid);
 int mv88e6xxx_vlan_getnext(struct dsa_switch *ds, u16 *vid,
 			   unsigned long *ports, unsigned long *untagged);
 int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
-- 
2.5.0


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

* [PATCH net-next 6/7] net: dsa: mv88e6xxx: add VLAN Load support
  2015-08-13 16:52 [PATCH net-next 0/7] net: dsa: mv88e6xxx: add hardware VLAN support Vivien Didelot
                   ` (4 preceding siblings ...)
  2015-08-13 16:52 ` [PATCH net-next 5/7] net: dsa: mv88e6xxx: add VLAN Purge support Vivien Didelot
@ 2015-08-13 16:52 ` Vivien Didelot
  2015-08-13 16:52 ` [PATCH net-next 7/7] net: dsa: mv88e6xxx: use port 802.1Q mode Secure Vivien Didelot
  2015-08-14  4:32 ` [PATCH net-next 0/7] net: dsa: mv88e6xxx: add hardware VLAN support David Miller
  7 siblings, 0 replies; 12+ messages in thread
From: Vivien Didelot @ 2015-08-13 16:52 UTC (permalink / raw)
  To: netdev
  Cc: linux-kernel, kernel, David S. Miller, Guenter Roeck,
	Andrew Lunn, Florian Fainelli, Scott Feldman, Jiri Pirko,
	Chris Healy, Vivien Didelot

Implement port_pvid_set and port_vlan_add to add new entries in the VLAN
hardware table, and join ports to them.

The patch also implement the STU Get Next and Load Purge operations,
since it is required to have a valid STU entry for at least all VLANs.

Each VLAN has its own forwarding database, with FID num_ports+1 to 4095.

Signed-off-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
---
 drivers/net/dsa/mv88e6352.c |   2 +
 drivers/net/dsa/mv88e6xxx.c | 169 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/net/dsa/mv88e6xxx.h |   9 +++
 3 files changed, 180 insertions(+)

diff --git a/drivers/net/dsa/mv88e6352.c b/drivers/net/dsa/mv88e6352.c
index cec38bb..14b7177 100644
--- a/drivers/net/dsa/mv88e6352.c
+++ b/drivers/net/dsa/mv88e6352.c
@@ -344,6 +344,8 @@ struct dsa_switch_driver mv88e6352_switch_driver = {
 	.port_leave_bridge	= mv88e6xxx_leave_bridge,
 	.port_stp_update	= mv88e6xxx_port_stp_update,
 	.port_pvid_get		= mv88e6xxx_port_pvid_get,
+	.port_pvid_set		= mv88e6xxx_port_pvid_set,
+	.port_vlan_add		= mv88e6xxx_port_vlan_add,
 	.port_vlan_del		= mv88e6xxx_port_vlan_del,
 	.vlan_getnext		= mv88e6xxx_vlan_getnext,
 	.port_fdb_add		= mv88e6xxx_port_fdb_add,
diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c
index 8423924..ca867e4 100644
--- a/drivers/net/dsa/mv88e6xxx.c
+++ b/drivers/net/dsa/mv88e6xxx.c
@@ -1198,6 +1198,12 @@ int mv88e6xxx_port_pvid_get(struct dsa_switch *ds, int port, u16 *pvid)
 	return 0;
 }
 
+int mv88e6xxx_port_pvid_set(struct dsa_switch *ds, int port, u16 pvid)
+{
+	return mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_DEFAULT_VLAN,
+				   pvid & PORT_DEFAULT_VLAN_MASK);
+}
+
 static int _mv88e6xxx_vtu_wait(struct dsa_switch *ds)
 {
 	return _mv88e6xxx_wait(ds, REG_GLOBAL, GLOBAL_VTU_OP,
@@ -1374,6 +1380,169 @@ loadpurge:
 	return _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_VTU_LOAD_PURGE);
 }
 
+static int _mv88e6xxx_stu_getnext(struct dsa_switch *ds, u8 sid,
+				  struct mv88e6xxx_vtu_stu_entry *entry)
+{
+	struct mv88e6xxx_vtu_stu_entry next = { 0 };
+	int ret;
+
+	ret = _mv88e6xxx_vtu_wait(ds);
+	if (ret < 0)
+		return ret;
+
+	ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_SID,
+				   sid & GLOBAL_VTU_SID_MASK);
+	if (ret < 0)
+		return ret;
+
+	ret = _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_STU_GET_NEXT);
+	if (ret < 0)
+		return ret;
+
+	ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_VTU_SID);
+	if (ret < 0)
+		return ret;
+
+	next.sid = ret & GLOBAL_VTU_SID_MASK;
+
+	ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_VTU_VID);
+	if (ret < 0)
+		return ret;
+
+	next.valid = !!(ret & GLOBAL_VTU_VID_VALID);
+
+	if (next.valid) {
+		ret = _mv88e6xxx_vtu_stu_data_read(ds, &next, 2);
+		if (ret < 0)
+			return ret;
+	}
+
+	*entry = next;
+	return 0;
+}
+
+static int _mv88e6xxx_stu_loadpurge(struct dsa_switch *ds,
+				    struct mv88e6xxx_vtu_stu_entry *entry)
+{
+	u16 reg = 0;
+	int ret;
+
+	ret = _mv88e6xxx_vtu_wait(ds);
+	if (ret < 0)
+		return ret;
+
+	if (!entry->valid)
+		goto loadpurge;
+
+	/* Write port states */
+	ret = _mv88e6xxx_vtu_stu_data_write(ds, entry, 2);
+	if (ret < 0)
+		return ret;
+
+	reg = GLOBAL_VTU_VID_VALID;
+loadpurge:
+	ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_VID, reg);
+	if (ret < 0)
+		return ret;
+
+	reg = entry->sid & GLOBAL_VTU_SID_MASK;
+	ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_SID, reg);
+	if (ret < 0)
+		return ret;
+
+	return _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_STU_LOAD_PURGE);
+}
+
+static int _mv88e6xxx_vlan_init(struct dsa_switch *ds, u16 vid,
+				struct mv88e6xxx_vtu_stu_entry *entry)
+{
+	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+	struct mv88e6xxx_vtu_stu_entry vlan = {
+		.valid = true,
+		.vid = vid,
+	};
+	int i;
+
+	/* exclude all ports except the CPU */
+	for (i = 0; i < ps->num_ports; ++i)
+		vlan.data[i] = dsa_is_cpu_port(ds, i) ?
+			GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED :
+			GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
+
+	if (mv88e6xxx_6097_family(ds) || mv88e6xxx_6165_family(ds) ||
+	    mv88e6xxx_6351_family(ds) || mv88e6xxx_6352_family(ds)) {
+		struct mv88e6xxx_vtu_stu_entry vstp;
+		int err;
+
+		/* Adding a VTU entry requires a valid STU entry. As VSTP is not
+		 * implemented, only one STU entry is needed to cover all VTU
+		 * entries. Thus, validate the SID 0.
+		 */
+		vlan.sid = 0;
+		err = _mv88e6xxx_stu_getnext(ds, GLOBAL_VTU_SID_MASK, &vstp);
+		if (err)
+			return err;
+
+		if (vstp.sid != vlan.sid || !vstp.valid) {
+			memset(&vstp, 0, sizeof(vstp));
+			vstp.valid = true;
+			vstp.sid = vlan.sid;
+
+			err = _mv88e6xxx_stu_loadpurge(ds, &vstp);
+			if (err)
+				return err;
+		}
+
+		/* Non-bridged ports and bridge groups use FIDs from 1 to
+		 * num_ports; VLANs use FIDs from num_ports+1 to 4095.
+		 */
+		vlan.fid = find_next_zero_bit(ps->fid_bitmap, VLAN_N_VID,
+					      ps->num_ports + 1);
+		if (unlikely(vlan.fid == VLAN_N_VID)) {
+			pr_err("no more FID available for VLAN %d\n", vid);
+			return -ENOSPC;
+		}
+
+		err = _mv88e6xxx_flush_fid(ds, vlan.fid);
+		if (err)
+			return err;
+
+		set_bit(vlan.fid, ps->fid_bitmap);
+	}
+
+	*entry = vlan;
+	return 0;
+}
+
+int mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, u16 vid,
+			    bool untagged)
+{
+	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+	struct mv88e6xxx_vtu_stu_entry vlan;
+	int err;
+
+	mutex_lock(&ps->smi_mutex);
+	err = _mv88e6xxx_vtu_getnext(ds, vid - 1, &vlan);
+	if (err)
+		goto unlock;
+
+	if (vlan.vid != vid || !vlan.valid) {
+		err = _mv88e6xxx_vlan_init(ds, vid, &vlan);
+		if (err)
+			goto unlock;
+	}
+
+	vlan.data[port] = untagged ?
+		GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED :
+		GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED;
+
+	err = _mv88e6xxx_vtu_loadpurge(ds, &vlan);
+unlock:
+	mutex_unlock(&ps->smi_mutex);
+
+	return err;
+}
+
 int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, u16 vid)
 {
 	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h
index c70a3c1..ca3268f 100644
--- a/drivers/net/dsa/mv88e6xxx.h
+++ b/drivers/net/dsa/mv88e6xxx.h
@@ -197,6 +197,8 @@
 #define GLOBAL_VTU_OP_FLUSH_ALL		((0x01 << 12) | GLOBAL_VTU_OP_BUSY)
 #define GLOBAL_VTU_OP_VTU_LOAD_PURGE	((0x03 << 12) | GLOBAL_VTU_OP_BUSY)
 #define GLOBAL_VTU_OP_VTU_GET_NEXT	((0x04 << 12) | GLOBAL_VTU_OP_BUSY)
+#define GLOBAL_VTU_OP_STU_LOAD_PURGE	((0x05 << 12) | GLOBAL_VTU_OP_BUSY)
+#define GLOBAL_VTU_OP_STU_GET_NEXT	((0x06 << 12) | GLOBAL_VTU_OP_BUSY)
 #define GLOBAL_VTU_VID		0x06
 #define GLOBAL_VTU_VID_MASK	0xfff
 #define GLOBAL_VTU_VID_VALID	BIT(12)
@@ -208,6 +210,10 @@
 #define GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED	0x01
 #define GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED	0x02
 #define GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER	0x03
+#define GLOBAL_STU_DATA_PORT_STATE_DISABLED	0x00
+#define GLOBAL_STU_DATA_PORT_STATE_BLOCKING	0x01
+#define GLOBAL_STU_DATA_PORT_STATE_LEARNING	0x02
+#define GLOBAL_STU_DATA_PORT_STATE_FORWARDING	0x03
 #define GLOBAL_ATU_CONTROL	0x0a
 #define GLOBAL_ATU_CONTROL_LEARN2ALL	BIT(3)
 #define GLOBAL_ATU_OP		0x0b
@@ -454,6 +460,9 @@ 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);
 int mv88e6xxx_port_pvid_get(struct dsa_switch *ds, int port, u16 *vid);
+int mv88e6xxx_port_pvid_set(struct dsa_switch *ds, int port, u16 vid);
+int mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, u16 vid,
+			    bool untagged);
 int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, u16 vid);
 int mv88e6xxx_vlan_getnext(struct dsa_switch *ds, u16 *vid,
 			   unsigned long *ports, unsigned long *untagged);
-- 
2.5.0


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

* [PATCH net-next 7/7] net: dsa: mv88e6xxx: use port 802.1Q mode Secure
  2015-08-13 16:52 [PATCH net-next 0/7] net: dsa: mv88e6xxx: add hardware VLAN support Vivien Didelot
                   ` (5 preceding siblings ...)
  2015-08-13 16:52 ` [PATCH net-next 6/7] net: dsa: mv88e6xxx: add VLAN Load support Vivien Didelot
@ 2015-08-13 16:52 ` Vivien Didelot
  2015-08-14  4:32 ` [PATCH net-next 0/7] net: dsa: mv88e6xxx: add hardware VLAN support David Miller
  7 siblings, 0 replies; 12+ messages in thread
From: Vivien Didelot @ 2015-08-13 16:52 UTC (permalink / raw)
  To: netdev
  Cc: linux-kernel, kernel, David S. Miller, Guenter Roeck,
	Andrew Lunn, Florian Fainelli, Scott Feldman, Jiri Pirko,
	Chris Healy, Vivien Didelot

This commit changes the 802.1Q mode of each port from Disabled to
Secure. This enables the VLAN support, by checking the VTU entries on
ingress.

Signed-off-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
---
 drivers/net/dsa/mv88e6xxx.c | 14 +++++++-------
 drivers/net/dsa/mv88e6xxx.h |  5 +++++
 2 files changed, 12 insertions(+), 7 deletions(-)

diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c
index ca867e4..332f2c8 100644
--- a/drivers/net/dsa/mv88e6xxx.c
+++ b/drivers/net/dsa/mv88e6xxx.c
@@ -2005,13 +2005,11 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port)
 			goto abort;
 	}
 
-	/* 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
-	 * on this port, do a destination address lookup on all
-	 * received packets as usual, disable ARP mirroring and don't
-	 * send a copy of all transmitted/received frames on this port
-	 * to the CPU.
+	/* Port Control 2: don't force a good FCS, set the maximum frame size to
+	 * 10240 bytes, enable secure 802.1q tags, don't discard tagged or
+	 * untagged frames on this port, do a destination address lookup on all
+	 * received packets as usual, disable ARP mirroring and don't send a
+	 * copy of all transmitted/received frames on this port to the CPU.
 	 */
 	reg = 0;
 	if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
@@ -2033,6 +2031,8 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port)
 			reg |= PORT_CONTROL_2_FORWARD_UNKNOWN;
 	}
 
+	reg |= PORT_CONTROL_2_8021Q_SECURE;
+
 	if (reg) {
 		ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
 					   PORT_CONTROL_2, reg);
diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h
index ca3268f..72ca887 100644
--- a/drivers/net/dsa/mv88e6xxx.h
+++ b/drivers/net/dsa/mv88e6xxx.h
@@ -140,6 +140,11 @@
 #define PORT_CONTROL_2_JUMBO_1522	(0x00 << 12)
 #define PORT_CONTROL_2_JUMBO_2048	(0x01 << 12)
 #define PORT_CONTROL_2_JUMBO_10240	(0x02 << 12)
+#define PORT_CONTROL_2_8021Q_MASK	(0x03 << 10)
+#define PORT_CONTROL_2_8021Q_DISABLED	(0x00 << 10)
+#define PORT_CONTROL_2_8021Q_FALLBACK	(0x01 << 10)
+#define PORT_CONTROL_2_8021Q_CHECK	(0x02 << 10)
+#define PORT_CONTROL_2_8021Q_SECURE	(0x03 << 10)
 #define PORT_CONTROL_2_DISCARD_TAGGED	BIT(9)
 #define PORT_CONTROL_2_DISCARD_UNTAGGED	BIT(8)
 #define PORT_CONTROL_2_MAP_DA		BIT(7)
-- 
2.5.0


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

* Re: [PATCH net-next 1/7] net: dsa: add support for switchdev VLAN objects
  2015-08-13 16:52 ` [PATCH net-next 1/7] net: dsa: add support for switchdev VLAN objects Vivien Didelot
@ 2015-08-13 20:16   ` Andrew Lunn
  2015-08-13 21:01     ` Vivien Didelot
  0 siblings, 1 reply; 12+ messages in thread
From: Andrew Lunn @ 2015-08-13 20:16 UTC (permalink / raw)
  To: Vivien Didelot
  Cc: netdev, linux-kernel, kernel, David S. Miller, Guenter Roeck,
	Florian Fainelli, Scott Feldman, Jiri Pirko, Chris Healy

On Thu, Aug 13, 2015 at 12:52:17PM -0400, Vivien Didelot wrote:
> Add new functions in DSA drivers to access hardware VLAN entries through
> SWITCHDEV_OBJ_PORT_VLAN objects:
> 
>  - port_pvid_get() and vlan_getnext() to dump a VLAN
>  - port_vlan_del() to exclude a port from a VLAN
>  - port_pvid_set() and port_vlan_add() to join a port to a VLAN
> 
> The DSA infrastructure will ensure that each VLAN of the given range
> does not already belong to another bridge. If it does, it will fallback
> to software VLAN and won't program the hardware.
> 
> Signed-off-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
> ---
>  include/net/dsa.h |  11 ++++
>  net/dsa/slave.c   | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 169 insertions(+)
> 
> diff --git a/include/net/dsa.h b/include/net/dsa.h
> index 6356f43..bd9b765 100644
> --- a/include/net/dsa.h
> +++ b/include/net/dsa.h
> @@ -298,6 +298,17 @@ struct dsa_switch_driver {
>  				   u8 state);
>  
>  	/*
> +	 * VLAN support
> +	 */
> +	int	(*port_pvid_get)(struct dsa_switch *ds, int port, u16 *pvid);
> +	int	(*port_pvid_set)(struct dsa_switch *ds, int port, u16 pvid);
> +	int	(*port_vlan_add)(struct dsa_switch *ds, int port, u16 vid,
> +				 bool untagged);
> +	int	(*port_vlan_del)(struct dsa_switch *ds, int port, u16 vid);
> @@ -794,6 +949,9 @@ static const struct net_device_ops dsa_slave_netdev_ops = {
>  	.ndo_netpoll_cleanup	= dsa_slave_netpoll_cleanup,
>  	.ndo_poll_controller	= dsa_slave_poll_controller,
>  #endif
> +	.ndo_bridge_getlink	= switchdev_port_bridge_getlink,
> +	.ndo_bridge_setlink	= switchdev_port_bridge_setlink,
> +	.ndo_bridge_dellink	= switchdev_port_bridge_dellink,
>  };

Do these changes belong here?

Thanks
	Andrew

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

* Re: [PATCH net-next 1/7] net: dsa: add support for switchdev VLAN objects
  2015-08-13 20:16   ` Andrew Lunn
@ 2015-08-13 21:01     ` Vivien Didelot
  2015-08-14  0:55       ` Andrew Lunn
  0 siblings, 1 reply; 12+ messages in thread
From: Vivien Didelot @ 2015-08-13 21:01 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: netdev, linux-kernel, kernel, David S. Miller, Guenter Roeck,
	Florian Fainelli, Scott Feldman, Jiri Pirko, Chris Healy

Hi Andrew,

On 15-08-13 22:16:08, Andrew Lunn wrote:
> On Thu, Aug 13, 2015 at 12:52:17PM -0400, Vivien Didelot wrote:
> > Add new functions in DSA drivers to access hardware VLAN entries through
> > SWITCHDEV_OBJ_PORT_VLAN objects:
> > 
> >  - port_pvid_get() and vlan_getnext() to dump a VLAN
> >  - port_vlan_del() to exclude a port from a VLAN
> >  - port_pvid_set() and port_vlan_add() to join a port to a VLAN
> > 
> > The DSA infrastructure will ensure that each VLAN of the given range
> > does not already belong to another bridge. If it does, it will fallback
> > to software VLAN and won't program the hardware.
> > 
> > Signed-off-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
> > ---
> >  include/net/dsa.h |  11 ++++
> >  net/dsa/slave.c   | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
> >  2 files changed, 169 insertions(+)
> > 
> > diff --git a/include/net/dsa.h b/include/net/dsa.h
> > index 6356f43..bd9b765 100644
> > --- a/include/net/dsa.h
> > +++ b/include/net/dsa.h
> > @@ -298,6 +298,17 @@ struct dsa_switch_driver {
> >  				   u8 state);
> >  
> >  	/*
> > +	 * VLAN support
> > +	 */
> > +	int	(*port_pvid_get)(struct dsa_switch *ds, int port, u16 *pvid);
> > +	int	(*port_pvid_set)(struct dsa_switch *ds, int port, u16 pvid);
> > +	int	(*port_vlan_add)(struct dsa_switch *ds, int port, u16 vid,
> > +				 bool untagged);
> > +	int	(*port_vlan_del)(struct dsa_switch *ds, int port, u16 vid);
> > @@ -794,6 +949,9 @@ static const struct net_device_ops dsa_slave_netdev_ops = {
> >  	.ndo_netpoll_cleanup	= dsa_slave_netpoll_cleanup,
> >  	.ndo_poll_controller	= dsa_slave_poll_controller,
> >  #endif
> > +	.ndo_bridge_getlink	= switchdev_port_bridge_getlink,
> > +	.ndo_bridge_setlink	= switchdev_port_bridge_setlink,
> > +	.ndo_bridge_dellink	= switchdev_port_bridge_dellink,
> >  };
> 
> Do these changes belong here?

Yes, these are used by switchdev to access bridge requests and exchange
SWITCHDEV_OBJ_PORT_VLAN objects with DSA, through the its switchdev_ops.

Thanks,
-v

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

* Re: [PATCH net-next 1/7] net: dsa: add support for switchdev VLAN objects
  2015-08-13 21:01     ` Vivien Didelot
@ 2015-08-14  0:55       ` Andrew Lunn
  0 siblings, 0 replies; 12+ messages in thread
From: Andrew Lunn @ 2015-08-14  0:55 UTC (permalink / raw)
  To: Vivien Didelot
  Cc: netdev, linux-kernel, kernel, David S. Miller, Guenter Roeck,
	Florian Fainelli, Scott Feldman, Jiri Pirko, Chris Healy

On Thu, Aug 13, 2015 at 05:01:57PM -0400, Vivien Didelot wrote:
> Hi Andrew,
> 
> On 15-08-13 22:16:08, Andrew Lunn wrote:
> > On Thu, Aug 13, 2015 at 12:52:17PM -0400, Vivien Didelot wrote:
> > > Add new functions in DSA drivers to access hardware VLAN entries through
> > > SWITCHDEV_OBJ_PORT_VLAN objects:
> > > 
> > >  - port_pvid_get() and vlan_getnext() to dump a VLAN
> > >  - port_vlan_del() to exclude a port from a VLAN
> > >  - port_pvid_set() and port_vlan_add() to join a port to a VLAN
> > > 
> > > The DSA infrastructure will ensure that each VLAN of the given range
> > > does not already belong to another bridge. If it does, it will fallback
> > > to software VLAN and won't program the hardware.
> > > 
> > > Signed-off-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
> > > ---
> > >  include/net/dsa.h |  11 ++++
> > >  net/dsa/slave.c   | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
> > >  2 files changed, 169 insertions(+)
> > > 
> > > diff --git a/include/net/dsa.h b/include/net/dsa.h
> > > index 6356f43..bd9b765 100644
> > > --- a/include/net/dsa.h
> > > +++ b/include/net/dsa.h
> > > @@ -298,6 +298,17 @@ struct dsa_switch_driver {
> > >  				   u8 state);
> > >  
> > >  	/*
> > > +	 * VLAN support
> > > +	 */
> > > +	int	(*port_pvid_get)(struct dsa_switch *ds, int port, u16 *pvid);
> > > +	int	(*port_pvid_set)(struct dsa_switch *ds, int port, u16 pvid);
> > > +	int	(*port_vlan_add)(struct dsa_switch *ds, int port, u16 vid,
> > > +				 bool untagged);
> > > +	int	(*port_vlan_del)(struct dsa_switch *ds, int port, u16 vid);
> > > @@ -794,6 +949,9 @@ static const struct net_device_ops dsa_slave_netdev_ops = {
> > >  	.ndo_netpoll_cleanup	= dsa_slave_netpoll_cleanup,
> > >  	.ndo_poll_controller	= dsa_slave_poll_controller,
> > >  #endif
> > > +	.ndo_bridge_getlink	= switchdev_port_bridge_getlink,
> > > +	.ndo_bridge_setlink	= switchdev_port_bridge_setlink,
> > > +	.ndo_bridge_dellink	= switchdev_port_bridge_dellink,
> > >  };
> > 
> > Do these changes belong here?
> 
> Yes, these are used by switchdev to access bridge requests and exchange
> SWITCHDEV_OBJ_PORT_VLAN objects with DSA, through the its switchdev_ops.

O.K, so if a v2 is needed, please mention this in the change log. It
was not obvious to me.

    Thanks
	Andrew

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

* Re: [PATCH net-next 0/7] net: dsa: mv88e6xxx: add hardware VLAN support
  2015-08-13 16:52 [PATCH net-next 0/7] net: dsa: mv88e6xxx: add hardware VLAN support Vivien Didelot
                   ` (6 preceding siblings ...)
  2015-08-13 16:52 ` [PATCH net-next 7/7] net: dsa: mv88e6xxx: use port 802.1Q mode Secure Vivien Didelot
@ 2015-08-14  4:32 ` David Miller
  7 siblings, 0 replies; 12+ messages in thread
From: David Miller @ 2015-08-14  4:32 UTC (permalink / raw)
  To: vivien.didelot
  Cc: netdev, linux-kernel, kernel, linux, andrew, f.fainelli, sfeldma,
	jiri, cphealy

From: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Date: Thu, 13 Aug 2015 12:52:16 -0400

> This patchset brings support to access hardware VLAN entries in DSA and
> mv88e6xxx, through switchdev VLAN objects.

Looks good, series applied, thanks Vivien.

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

end of thread, other threads:[~2015-08-14  4:32 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-08-13 16:52 [PATCH net-next 0/7] net: dsa: mv88e6xxx: add hardware VLAN support Vivien Didelot
2015-08-13 16:52 ` [PATCH net-next 1/7] net: dsa: add support for switchdev VLAN objects Vivien Didelot
2015-08-13 20:16   ` Andrew Lunn
2015-08-13 21:01     ` Vivien Didelot
2015-08-14  0:55       ` Andrew Lunn
2015-08-13 16:52 ` [PATCH net-next 2/7] net: dsa: mv88e6xxx: flush VTU and STU entries Vivien Didelot
2015-08-13 16:52 ` [PATCH net-next 3/7] net: dsa: mv88e6xxx: add VLAN Get Next support Vivien Didelot
2015-08-13 16:52 ` [PATCH net-next 4/7] net: dsa: mv88e6xxx: add VLAN support to FDB dump Vivien Didelot
2015-08-13 16:52 ` [PATCH net-next 5/7] net: dsa: mv88e6xxx: add VLAN Purge support Vivien Didelot
2015-08-13 16:52 ` [PATCH net-next 6/7] net: dsa: mv88e6xxx: add VLAN Load support Vivien Didelot
2015-08-13 16:52 ` [PATCH net-next 7/7] net: dsa: mv88e6xxx: use port 802.1Q mode Secure Vivien Didelot
2015-08-14  4:32 ` [PATCH net-next 0/7] net: dsa: mv88e6xxx: add hardware VLAN support David Miller

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).