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
This commit is contained in:
parent
ce18f7cde8
commit
036e1c7646
@ -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_softc *sc, etherswitch_port_t *p)
|
||||
/* 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
|
||||
@ -753,11 +798,25 @@ ar8327_port_vlan_get(struct arswitch_softc *sc, etherswitch_port_t *p)
|
||||
return (0);
|
||||
}
|
||||
|
||||
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 *sc)
|
||||
*/
|
||||
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 *sc)
|
||||
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;
|
||||
|
@ -103,7 +103,8 @@ ar8xxx_purge_dot1q_vlan(struct arswitch_softc *sc, int vid)
|
||||
}
|
||||
|
||||
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_softc *sc, uint32_t *ports, int vid)
|
||||
}
|
||||
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 *sc)
|
||||
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 *sc)
|
||||
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 *sc, etherswitch_vlangroup_t *vg)
|
||||
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 *sc, etherswitch_vlangroup_t *vg)
|
||||
/* 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);
|
||||
|
@ -37,8 +37,10 @@ int ar8xxx_set_pvid(struct arswitch_softc *, int, int);
|
||||
|
||||
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);
|
||||
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user