Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 13 Mar 2015 02:16:40 +0000 (UTC)
From:      Adrian Chadd <adrian@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r279943 - head/sys/dev/etherswitch/arswitch
Message-ID:  <201503130216.t2D2Gel2069219@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: adrian
Date: Fri Mar 13 02:16:39 2015
New Revision: 279943
URL: https://svnweb.freebsd.org/changeset/base/279943

Log:
  Commit 802.1q configuration support for the AR8327.
  
  This is slightly different to the other switches - the VLAN table
  (VTU) programs in the vlan port mapping /and/ the port config
  (tagged, untagged, passthrough, any.)
  
  So:
  
  * Add VTU operations to program the VTU (vlan table)
  * abstract out the mirror-disable function so it's .. well, a function.
  * setup the port to have a dot1q configuration for dot1q - the
    port security is VLAN (not per-port VLAN) and requires an entry
    in the VLAN table;
  * add set_dot1q / get_dot1q to program the VLAN table;
  * since the tagged/untagged ports are now programmed into the VTU,
    rather than global - plumb the ports /and/ untagged ports bitmaps
    through the arswitch API.
  
  Tested:
  
  * AP135 - QCA9558 SoC + AR8327N switch

Modified:
  head/sys/dev/etherswitch/arswitch/arswitch_8327.c
  head/sys/dev/etherswitch/arswitch/arswitch_vlans.c
  head/sys/dev/etherswitch/arswitch/arswitch_vlans.h
  head/sys/dev/etherswitch/arswitch/arswitchvar.h

Modified: head/sys/dev/etherswitch/arswitch/arswitch_8327.c
==============================================================================
--- head/sys/dev/etherswitch/arswitch/arswitch_8327.c	Fri Mar 13 01:18:46 2015	(r279942)
+++ head/sys/dev/etherswitch/arswitch/arswitch_8327.c	Fri Mar 13 02:16:39 2015	(r279943)
@@ -66,6 +66,51 @@
 #include "miibus_if.h"
 #include "etherswitch_if.h"
 
+
+static int
+ar8327_vlan_op(struct arswitch_softc *sc, uint32_t op, uint32_t vid,
+    uint32_t data)
+{
+	int err;
+
+	/*
+	 * Wait for the "done" bit to finish.
+	 */
+	if (arswitch_waitreg(sc->sc_dev, AR8327_REG_VTU_FUNC1,
+	    AR8327_VTU_FUNC1_BUSY, 0, 5))
+		return (EBUSY);
+
+	/*
+	 * If it's a "load" operation, then ensure 'data' is loaded
+	 * in first.
+	 */
+	if ((op & AR8327_VTU_FUNC1_OP) == AR8327_VTU_FUNC1_OP_LOAD) {
+		err = arswitch_writereg(sc->sc_dev, AR8327_REG_VTU_FUNC0, data);
+		if (err)
+			return (err);
+	}
+
+	/*
+	 * Set the VID.
+	 */
+	op |= ((vid & 0xfff) << AR8327_VTU_FUNC1_VID_S);
+
+	/*
+	 * Set busy bit to start loading in the command.
+	 */
+	op |= AR8327_VTU_FUNC1_BUSY;
+	arswitch_writereg(sc->sc_dev, AR8327_REG_VTU_FUNC1, op);
+
+	/*
+	 * Finally - wait for it to load.
+	 */
+	if (arswitch_waitreg(sc->sc_dev, AR8327_REG_VTU_FUNC1,
+	    AR8327_VTU_FUNC1_BUSY, 0, 5))
+		return (EBUSY);
+
+	return (0);
+}
+
 static void
 ar8327_phy_fixup(struct arswitch_softc *sc, int phy)
 {
@@ -742,7 +787,7 @@ ar8327_port_vlan_get(struct arswitch_sof
 	/* Retrieve the PVID */
 	sc->hal.arswitch_vlan_get_pvid(sc, p->es_port, &p->es_pvid);
 
-	/* Retrieve the current port configuration */
+	/* Retrieve the current port configuration from the VTU */
 	/*
 	 * DOUBLE_TAG
 	 * VLAN_MODE_ADD
@@ -754,10 +799,24 @@ ar8327_port_vlan_get(struct arswitch_sof
 }
 
 static void
+ar8327_port_disable_mirror(struct arswitch_softc *sc, int port)
+{
+
+	arswitch_modifyreg(sc->sc_dev,
+	    AR8327_REG_PORT_LOOKUP(port),
+	    AR8327_PORT_LOOKUP_ING_MIRROR_EN,
+	    0);
+	arswitch_modifyreg(sc->sc_dev,
+	    AR8327_REG_PORT_HOL_CTRL1(port),
+	    AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN,
+	    0);
+}
+
+static void
 ar8327_reset_vlans(struct arswitch_softc *sc)
 {
 	int i;
-	uint32_t mode, t;
+	uint32_t t;
 	int ports;
 
 	ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
@@ -787,43 +846,66 @@ ar8327_reset_vlans(struct arswitch_softc
 	 */
 	ports = 0x7f;
 
+	/*
+	 * XXX TODO: set things up correctly for vlans!
+	 */
 	for (i = 0; i < AR8327_NUM_PORTS; i++) {
+		int egress, ingress;
 
-		if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT)
+		if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) {
 			sc->vid[i] = i | ETHERSWITCH_VID_VALID;
+			/* set egress == out_keep */
+			ingress = AR8X16_PORT_VLAN_MODE_PORT_ONLY;
+			/* in_port_only, forward */
+			egress = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH;
+		} else if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
+			ingress = AR8X16_PORT_VLAN_MODE_SECURE;
+			egress = AR8327_PORT_VLAN1_OUT_MODE_UNMOD;
+		} else {
+			/* set egress == out_keep */
+			ingress = AR8X16_PORT_VLAN_MODE_PORT_ONLY;
+			/* in_port_only, forward */
+			egress = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH;
+		}
 
 		/* set pvid = 1; there's only one vlangroup to start with */
 		t = 1 << AR8327_PORT_VLAN0_DEF_SVID_S;
 		t |= 1 << AR8327_PORT_VLAN0_DEF_CVID_S;
 		arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_VLAN0(i), t);
 
-		/* set egress == out_keep */
-		mode = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH;
-
 		t = AR8327_PORT_VLAN1_PORT_VLAN_PROP;
-		t |= mode << AR8327_PORT_VLAN1_OUT_MODE_S;
+		t |= egress << AR8327_PORT_VLAN1_OUT_MODE_S;
 		arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_VLAN1(i), t);
 
 		/* Ports can see other ports */
+		/* XXX not entirely true for dot1q? */
 		t = (ports & ~(1 << i));	/* all ports besides us */
 		t |= AR8327_PORT_LOOKUP_LEARN;
 
-		/* in_port_only, forward */
-		t |= AR8X16_PORT_VLAN_MODE_PORT_ONLY << AR8327_PORT_LOOKUP_IN_MODE_S;
+		t |= ingress << AR8327_PORT_LOOKUP_IN_MODE_S;
 		t |= AR8X16_PORT_CTRL_STATE_FORWARD << AR8327_PORT_LOOKUP_STATE_S;
 		arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_LOOKUP(i), t);
+	}
 
-		/*
-		 * Disable port mirroring entirely.
-		 */
-		arswitch_modifyreg(sc->sc_dev,
-		    AR8327_REG_PORT_LOOKUP(i),
-		    AR8327_PORT_LOOKUP_ING_MIRROR_EN,
-		    0);
-		arswitch_modifyreg(sc->sc_dev,
-		    AR8327_REG_PORT_HOL_CTRL1(i),
-		    AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN,
-		    0);
+	/*
+	 * Disable port mirroring entirely.
+	 */
+	for (i = 0; i < AR8327_NUM_PORTS; i++) {
+		ar8327_port_disable_mirror(sc, i);
+	}
+
+	/*
+	 * If dot1q - set pvid; dot1q, etc.
+	 */
+	sc->vid[0] = 1;
+	if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
+		for (i = 0; i < AR8327_NUM_PORTS; i++) {
+			/* Each port - pvid 1 */
+			sc->hal.arswitch_vlan_set_pvid(sc, i, sc->vid[0]);
+		}
+		/* Initialise vlan1 - all ports, untagged */
+		sc->hal.arswitch_set_dot1q_vlan(sc, ports, ports, sc->vid[0]);
+		sc->vid[0] |= ETHERSWITCH_VID_VALID;
 	}
 
 	ARSWITCH_UNLOCK(sc);
@@ -867,9 +949,6 @@ static int
 ar8327_vlan_getvgroup(struct arswitch_softc *sc, etherswitch_vlangroup_t *vg)
 {
 
-	/* XXX for now, no dot1q vlans */
-	if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q)
-		return (EINVAL);
 	return (ar8xxx_getvgroup(sc, vg));
 }
 
@@ -877,9 +956,6 @@ static int
 ar8327_vlan_setvgroup(struct arswitch_softc *sc, etherswitch_vlangroup_t *vg)
 {
 
-	/* XXX for now, no dot1q vlans */
-	if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q)
-		return (EINVAL);
 	return (ar8xxx_setvgroup(sc, vg));
 }
 
@@ -939,6 +1015,98 @@ ar8327_atu_flush(struct arswitch_softc *
 	return (ret);
 }
 
+static int
+ar8327_flush_dot1q_vlan(struct arswitch_softc *sc)
+{
+
+	return (ar8327_vlan_op(sc, AR8327_VTU_FUNC1_OP_FLUSH, 0, 0));
+}
+
+static int
+ar8327_purge_dot1q_vlan(struct arswitch_softc *sc, int vid)
+{
+
+	return (ar8327_vlan_op(sc, AR8327_VTU_FUNC1_OP_PURGE, vid, 0));
+}
+
+static int
+ar8327_get_dot1q_vlan(struct arswitch_softc *sc, uint32_t *ports,
+    uint32_t *untagged_ports, int vid)
+{
+	int i, r;
+	uint32_t op, reg, val;
+
+	op = AR8327_VTU_FUNC1_OP_GET_ONE;
+
+	/* Filter out the vid flags; only grab the VLAN ID */
+	vid &= 0xfff;
+
+	/* XXX TODO: the VTU here stores egress mode - keep, tag, untagged, none */
+	r = ar8327_vlan_op(sc, op, vid, 0);
+	if (r != 0) {
+		device_printf(sc->sc_dev, "%s: %d: op failed\n", __func__, vid);
+	}
+
+	reg = arswitch_readreg(sc->sc_dev, AR8327_REG_VTU_FUNC0);
+	DPRINTF(sc->sc_dev, "%s: %d: reg=0x%08x\n", __func__, vid, reg);
+
+	/*
+	 * If any of the bits are set, update the port mask.
+	 * Worry about the port config itself when getport() is called.
+	 */
+	*ports = 0;
+	for (i = 0; i < AR8327_NUM_PORTS; i++) {
+		val = reg >> AR8327_VTU_FUNC0_EG_MODE_S(i);
+		val = val & 0x3;
+		/* XXX KEEP (unmodified?) */
+		if (val == AR8327_VTU_FUNC0_EG_MODE_TAG) {
+			*ports |= (1 << i);
+		} else if (val == AR8327_VTU_FUNC0_EG_MODE_UNTAG) {
+			*ports |= (1 << i);
+			*untagged_ports |= (1 << i);
+		}
+	}
+
+	return (0);
+}
+
+static int
+ar8327_set_dot1q_vlan(struct arswitch_softc *sc, uint32_t ports,
+    uint32_t untagged_ports, int vid)
+{
+	int i;
+	uint32_t op, val, mode;
+
+	op = AR8327_VTU_FUNC1_OP_LOAD;
+	vid &= 0xfff;
+
+	DPRINTF(sc->sc_dev,
+	    "%s: vid: %d, ports=0x%08x, untagged_ports=0x%08x\n",
+	    __func__,
+	    vid,
+	    ports,
+	    untagged_ports);
+
+	/*
+	 * Mark it as valid; and that it should use per-VLAN MAC table,
+	 * not VID=0 when doing MAC lookups
+	 */
+	val = AR8327_VTU_FUNC0_VALID | AR8327_VTU_FUNC0_IVL;
+
+	for (i = 0; i < AR8327_NUM_PORTS; i++) {
+		if ((ports & BIT(i)) == 0)
+			mode = AR8327_VTU_FUNC0_EG_MODE_NOT;
+		else if (untagged_ports & BIT(i))
+			mode = AR8327_VTU_FUNC0_EG_MODE_UNTAG;
+		else
+			mode = AR8327_VTU_FUNC0_EG_MODE_TAG;
+
+		val |= mode << AR8327_VTU_FUNC0_EG_MODE_S(i);
+	}
+
+	return (ar8327_vlan_op(sc, op, vid, val));
+}
+
 void
 ar8327_attach(struct arswitch_softc *sc)
 {
@@ -952,6 +1120,10 @@ ar8327_attach(struct arswitch_softc *sc)
 	sc->hal.arswitch_vlan_setvgroup = ar8327_vlan_setvgroup;
 	sc->hal.arswitch_port_vlan_setup = ar8327_port_vlan_setup;
 	sc->hal.arswitch_port_vlan_get = ar8327_port_vlan_get;
+	sc->hal.arswitch_flush_dot1q_vlan = ar8327_flush_dot1q_vlan;
+	sc->hal.arswitch_purge_dot1q_vlan = ar8327_purge_dot1q_vlan;
+	sc->hal.arswitch_set_dot1q_vlan = ar8327_set_dot1q_vlan;
+	sc->hal.arswitch_get_dot1q_vlan = ar8327_get_dot1q_vlan;
 
 	sc->hal.arswitch_vlan_init_hw = ar8327_reset_vlans;
 	sc->hal.arswitch_vlan_get_pvid = ar8327_get_pvid;

Modified: head/sys/dev/etherswitch/arswitch/arswitch_vlans.c
==============================================================================
--- head/sys/dev/etherswitch/arswitch/arswitch_vlans.c	Fri Mar 13 01:18:46 2015	(r279942)
+++ head/sys/dev/etherswitch/arswitch/arswitch_vlans.c	Fri Mar 13 02:16:39 2015	(r279943)
@@ -103,7 +103,8 @@ ar8xxx_purge_dot1q_vlan(struct arswitch_
 }
 
 int
-ar8xxx_get_dot1q_vlan(struct arswitch_softc *sc, uint32_t *ports, int vid)
+ar8xxx_get_dot1q_vlan(struct arswitch_softc *sc, uint32_t *ports,
+    uint32_t *untagged_ports, int vid)
 {
 	uint32_t reg;
 	int err;
@@ -120,11 +121,13 @@ ar8xxx_get_dot1q_vlan(struct arswitch_so
 	}
 	reg &= ((1 << (sc->numphys + 1)) - 1);
 	*ports = reg;
+	*untagged_ports = reg;
 	return (0);
 }
 
 int
-ar8xxx_set_dot1q_vlan(struct arswitch_softc *sc, uint32_t ports, int vid)
+ar8xxx_set_dot1q_vlan(struct arswitch_softc *sc, uint32_t ports,
+    uint32_t untagged_ports, int vid)
 {
 	int err;
 
@@ -223,7 +226,7 @@ ar8xxx_reset_vlans(struct arswitch_softc
 		ports = 0;
 		for (i = 0; i <= sc->numphys; i++)
 			ports |= (1 << i);
-		sc->hal.arswitch_set_dot1q_vlan(sc, ports, sc->vid[0]);
+		sc->hal.arswitch_set_dot1q_vlan(sc, ports, sc->vid[0], sc->vid[0]);
 		sc->vid[0] |= ETHERSWITCH_VID_VALID;
 	} else if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) {
 		/* Initialize the port based vlans. */
@@ -240,6 +243,7 @@ ar8xxx_reset_vlans(struct arswitch_softc
 			    ports << AR8X16_PORT_VLAN_DEST_PORTS_SHIFT |
 			    AR8X16_PORT_VLAN_MODE_SECURE <<
 			    AR8X16_PORT_VLAN_MODE_PORT_ONLY);
+			    /* XXX TODO: SECURE / PORT_ONLY is wrong? */
 		}
 	} else {
 		/* Disable the ingress filter and get everyone on all vlans. */
@@ -286,18 +290,21 @@ ar8xxx_getvgroup(struct arswitch_softc *
 	switch (sc->vlan_mode) {
 	case ETHERSWITCH_VLAN_DOT1Q:
 		err = sc->hal.arswitch_get_dot1q_vlan(sc, &vg->es_member_ports,
+		    &vg->es_untagged_ports,
 		    vg->es_vid);
 		break;
 	case ETHERSWITCH_VLAN_PORT:
 		err = sc->hal.arswitch_get_port_vlan(sc, &vg->es_member_ports,
 		    vg->es_vid);
+		vg->es_untagged_ports = vg->es_member_ports;
 		break;
 	default:
 		vg->es_member_ports = 0;
+		vg->es_untagged_ports = 0;
 		err = -1;
 	}
 	ARSWITCH_UNLOCK(sc);
-	vg->es_untagged_ports = vg->es_member_ports;
+
 	return (err);
 }
 
@@ -344,7 +351,8 @@ ar8xxx_setvgroup(struct arswitch_softc *
 	/* Member Ports. */
 	switch (sc->vlan_mode) {
 	case ETHERSWITCH_VLAN_DOT1Q:
-		err = sc->hal.arswitch_set_dot1q_vlan(sc, vg->es_member_ports, vid);
+		err = sc->hal.arswitch_set_dot1q_vlan(sc, vg->es_member_ports,
+		    vg->es_untagged_ports, vid);
 		break;
 	case ETHERSWITCH_VLAN_PORT:
 		err = sc->hal.arswitch_set_port_vlan(sc, vg->es_member_ports, vid);

Modified: head/sys/dev/etherswitch/arswitch/arswitch_vlans.h
==============================================================================
--- head/sys/dev/etherswitch/arswitch/arswitch_vlans.h	Fri Mar 13 01:18:46 2015	(r279942)
+++ head/sys/dev/etherswitch/arswitch/arswitch_vlans.h	Fri Mar 13 02:16:39 2015	(r279943)
@@ -37,8 +37,10 @@ int ar8xxx_set_pvid(struct arswitch_soft
 
 int ar8xxx_flush_dot1q_vlan(struct arswitch_softc *sc);
 int ar8xxx_purge_dot1q_vlan(struct arswitch_softc *sc, int vid);
-int ar8xxx_get_dot1q_vlan(struct arswitch_softc *sc, uint32_t *ports, int vid);
-int ar8xxx_set_dot1q_vlan(struct arswitch_softc *sc, uint32_t ports, int vid);
+int ar8xxx_get_dot1q_vlan(struct arswitch_softc *sc, uint32_t *ports,
+    uint32_t *untagged_ports, int vid);
+int ar8xxx_set_dot1q_vlan(struct arswitch_softc *sc, uint32_t ports,
+    uint32_t untagged_ports, int vid);
 int ar8xxx_get_port_vlan(struct arswitch_softc *sc, uint32_t *ports, int vid);
 int ar8xxx_set_port_vlan(struct arswitch_softc *sc, uint32_t ports, int vid);
 

Modified: head/sys/dev/etherswitch/arswitch/arswitchvar.h
==============================================================================
--- head/sys/dev/etherswitch/arswitch/arswitchvar.h	Fri Mar 13 01:18:46 2015	(r279942)
+++ head/sys/dev/etherswitch/arswitch/arswitchvar.h	Fri Mar 13 02:16:39 2015	(r279943)
@@ -103,9 +103,9 @@ struct arswitch_softc {
 		int (* arswitch_purge_dot1q_vlan) (struct arswitch_softc *sc,
 		    int vid);
 		int (* arswitch_get_dot1q_vlan) (struct arswitch_softc *,
-		    uint32_t *ports, int vid);
+		    uint32_t *ports, uint32_t *untagged_ports, int vid);
 		int (* arswitch_set_dot1q_vlan) (struct arswitch_softc *sc,
-		    uint32_t ports, int vid);
+		    uint32_t ports, uint32_t untagged_ports, int vid);
 		int (* arswitch_get_port_vlan) (struct arswitch_softc *sc,
 		    uint32_t *ports, int vid);
 		int (* arswitch_set_port_vlan) (struct arswitch_softc *sc,



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201503130216.t2D2Gel2069219>