svn commit: r279943 - head/sys/dev/etherswitch/arswitch
Adrian Chadd
adrian at FreeBSD.org
Fri Mar 13 02:16:41 UTC 2015
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,
More information about the svn-src-head
mailing list