aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorAdrian Chadd <adrian@FreeBSD.org>2018-02-02 22:05:36 +0000
committerAdrian Chadd <adrian@FreeBSD.org>2018-02-02 22:05:36 +0000
commit62042c979db43298b8e3d569cb9fd5bd7adc60ac (patch)
treee6056172f5be6411189571f3997dc10772427d0f /sys
parent5725799a3bc9cf22cf2fde3b8c844902a58039ce (diff)
downloadsrc-62042c979db43298b8e3d569cb9fd5bd7adc60ac.tar.gz
src-62042c979db43298b8e3d569cb9fd5bd7adc60ac.zip
[arswitch] begin tidying up the learning and ATU management, introduce ATU APIs.
* Refactor the initial learning configuration (port learning, address expiry, handling address moving between ports, etc, etc) into a separate HAL routine * and ensure that it's consistent between switch chips - the AR8216,8316,724x,9331 SoCs all share the same switch code. * .. the AR8327 needs doing - the defaults seem OK for now * .. the AR9340 is different but it's also programmed now. * Add support for flushing a single port worth of ATU entries * Add support for fetching the ATU table from AR8216 and derived chips Tested: * AR9344, Carambola 2 TODO: * Further testing on other chips * Add AR9340 support * Add AR8327 support
Notes
Notes: svn path=/head/; revision=328812
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/etherswitch/arswitch/arswitch.c292
-rw-r--r--sys/dev/etherswitch/arswitch/arswitch_7240.c2
-rw-r--r--sys/dev/etherswitch/arswitch/arswitch_8316.c5
-rw-r--r--sys/dev/etherswitch/arswitch/arswitch_8327.c59
-rw-r--r--sys/dev/etherswitch/arswitch/arswitch_9340.c32
-rw-r--r--sys/dev/etherswitch/arswitch/arswitchreg.h36
-rw-r--r--sys/dev/etherswitch/arswitch/arswitchvar.h9
7 files changed, 406 insertions, 29 deletions
diff --git a/sys/dev/etherswitch/arswitch/arswitch.c b/sys/dev/etherswitch/arswitch/arswitch.c
index 6e88eafa9c17..27d8fb06974c 100644
--- a/sys/dev/etherswitch/arswitch/arswitch.c
+++ b/sys/dev/etherswitch/arswitch/arswitch.c
@@ -289,16 +289,34 @@ ar8xxx_port_init(struct arswitch_softc *sc, int port)
}
static int
-ar8xxx_atu_flush(struct arswitch_softc *sc)
+ar8xxx_atu_wait_ready(struct arswitch_softc *sc)
{
int ret;
+ ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
+
ret = arswitch_waitreg(sc->sc_dev,
AR8216_REG_ATU,
AR8216_ATU_ACTIVE,
0,
1000);
+ return (ret);
+}
+
+/*
+ * Flush all ATU entries.
+ */
+static int
+ar8xxx_atu_flush(struct arswitch_softc *sc)
+{
+ int ret;
+
+ ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
+
+ DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: flushing all ports\n", __func__);
+
+ ret = ar8xxx_atu_wait_ready(sc);
if (ret)
device_printf(sc->sc_dev, "%s: waitreg failed\n", __func__);
@@ -310,6 +328,165 @@ ar8xxx_atu_flush(struct arswitch_softc *sc)
return (ret);
}
+/*
+ * Flush ATU entries for a single port.
+ */
+static int
+ar8xxx_atu_flush_port(struct arswitch_softc *sc, int port)
+{
+ int ret, val;
+
+ DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: flushing port %d\n", __func__,
+ port);
+
+ ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
+
+ /* Flush unicast entries on port */
+ val = AR8216_ATU_OP_FLUSH_UNICAST;
+
+ /* TODO: bit 4 indicates whether to flush dynamic (0) or static (1) */
+
+ /* Which port */
+ val |= SM(port, AR8216_ATU_PORT_NUM);
+
+ ret = ar8xxx_atu_wait_ready(sc);
+ if (ret)
+ device_printf(sc->sc_dev, "%s: waitreg failed\n", __func__);
+
+ if (!ret)
+ arswitch_writereg(sc->sc_dev,
+ AR8216_REG_ATU,
+ val | AR8216_ATU_ACTIVE);
+
+ return (ret);
+}
+
+/*
+ * XXX TODO: flush a single MAC address.
+ */
+
+/*
+ * Fetch a single entry from the ATU.
+ */
+static int
+ar8xxx_atu_fetch_table(struct arswitch_softc *sc, etherswitch_atu_entry_t *e,
+ int atu_fetch_op)
+{
+ uint32_t ret0, ret1, ret2, val;
+
+ ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
+
+ switch (atu_fetch_op) {
+ case 0:
+ /* Initialise things for the first fetch */
+
+ DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: initializing\n", __func__);
+ (void) ar8xxx_atu_wait_ready(sc);
+
+ arswitch_writereg(sc->sc_dev,
+ AR8216_REG_ATU, AR8216_ATU_OP_GET_NEXT);
+ arswitch_writereg(sc->sc_dev,
+ AR8216_REG_ATU_DATA, 0);
+ arswitch_writereg(sc->sc_dev,
+ AR8216_REG_ATU_CTRL2, 0);
+
+ return (0);
+ case 1:
+ DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: reading next\n", __func__);
+ /*
+ * Attempt to read the next address entry; don't modify what
+ * is there in AT_ADDR{4,5} as its used for the next fetch
+ */
+ (void) ar8xxx_atu_wait_ready(sc);
+
+ /* Begin the next read event; not modifying anything */
+ val = arswitch_readreg(sc->sc_dev, AR8216_REG_ATU);
+ val |= AR8216_ATU_ACTIVE;
+ arswitch_writereg(sc->sc_dev, AR8216_REG_ATU, val);
+
+ /* Wait for it to complete */
+ (void) ar8xxx_atu_wait_ready(sc);
+
+ /* Fetch the ethernet address and ATU status */
+ ret0 = arswitch_readreg(sc->sc_dev, AR8216_REG_ATU);
+ ret1 = arswitch_readreg(sc->sc_dev, AR8216_REG_ATU_DATA);
+ ret2 = arswitch_readreg(sc->sc_dev, AR8216_REG_ATU_CTRL2);
+
+ /* If the status is zero, then we're done */
+ if (MS(ret2, AR8216_ATU_CTRL2_AT_STATUS) == 0)
+ return (-1);
+
+ /* MAC address */
+ e->es_macaddr[5] = MS(ret0, AR8216_ATU_ADDR5);
+ e->es_macaddr[4] = MS(ret0, AR8216_ATU_ADDR4);
+ e->es_macaddr[3] = MS(ret1, AR8216_ATU_ADDR3);
+ e->es_macaddr[2] = MS(ret1, AR8216_ATU_ADDR2);
+ e->es_macaddr[1] = MS(ret1, AR8216_ATU_ADDR1);
+ e->es_macaddr[0] = MS(ret1, AR8216_ATU_ADDR0);
+
+ /* Bitmask of ports this entry is for */
+ e->es_portmask = MS(ret2, AR8216_ATU_CTRL2_DESPORT);
+
+ /* TODO: other flags that are interesting */
+
+ DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: MAC %6D portmask 0x%08x\n",
+ __func__,
+ e->es_macaddr, ":", e->es_portmask);
+ return (0);
+ default:
+ return (-1);
+ }
+ return (-1);
+}
+
+/*
+ * Configure aging register defaults.
+ */
+static int
+ar8xxx_atu_learn_default(struct arswitch_softc *sc)
+{
+ int ret;
+ uint32_t val;
+
+ DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: resetting learning\n", __func__);
+
+ /*
+ * For now, configure the aging defaults:
+ *
+ * + ARP_EN - enable "acknowledgement" of ARP frames - they are
+ * forwarded to the CPU port
+ * + LEARN_CHANGE_EN - hash table violations when learning MAC addresses
+ * will force an entry to be expired/updated and a new one to be
+ * programmed in.
+ * + AGE_EN - enable address table aging
+ * + AGE_TIME - set to 5 minutes
+ */
+ val = 0;
+ val |= AR8216_ATU_CTRL_ARP_EN;
+ val |= AR8216_ATU_CTRL_LEARN_CHANGE;
+ val |= AR8216_ATU_CTRL_AGE_EN;
+ val |= 0x2b; /* 5 minutes; bits 15:0 */
+
+ ret = arswitch_writereg(sc->sc_dev,
+ AR8216_REG_ATU_CTRL,
+ val);
+
+ if (ret)
+ device_printf(sc->sc_dev, "%s: writereg failed\n", __func__);
+
+ return (ret);
+}
+
+/*
+ * XXX TODO: add another routine to configure the leaky behaviour
+ * when unknown frames are received. These must be consistent
+ * between ethernet switches.
+ */
+
+/*
+ * XXX TODO: this attach routine does NOT free all memory, resources
+ * upon failure!
+ */
static int
arswitch_attach(device_t dev)
{
@@ -333,6 +510,18 @@ arswitch_attach(device_t dev)
"debug", CTLFLAG_RW, &sc->sc_debug, 0,
"control debugging printfs");
+ /* Allocate a 128 entry ATU table; hopefully its big enough! */
+ /* XXX TODO: make this per chip */
+ sc->atu.entries = malloc(sizeof(etherswitch_atu_entry_t) * 128,
+ M_DEVBUF, M_NOWAIT);
+ if (sc->atu.entries == NULL) {
+ device_printf(sc->sc_dev, "%s: failed to allocate ATU table\n",
+ __func__);
+ return (ENXIO);
+ }
+ sc->atu.count = 0;
+ sc->atu.size = 128;
+
/* Default HAL methods */
sc->hal.arswitch_port_init = ar8xxx_port_init;
sc->hal.arswitch_port_vlan_setup = ar8xxx_port_vlan_setup;
@@ -353,11 +542,13 @@ arswitch_attach(device_t dev)
sc->hal.arswitch_set_port_vlan = ar8xxx_set_port_vlan;
sc->hal.arswitch_atu_flush = ar8xxx_atu_flush;
+ sc->hal.arswitch_atu_flush_port = ar8xxx_atu_flush_port;
+ sc->hal.arswitch_atu_learn_default = ar8xxx_atu_learn_default;
+ sc->hal.arswitch_atu_fetch_table = ar8xxx_atu_fetch_table;
sc->hal.arswitch_phy_read = arswitch_readphy_internal;
sc->hal.arswitch_phy_write = arswitch_writephy_internal;
-
/*
* Attach switch related functions
*/
@@ -424,6 +615,17 @@ arswitch_attach(device_t dev)
return (err);
}
+ /*
+ * Configure the default address table learning parameters for this
+ * switch.
+ */
+ err = sc->hal.arswitch_atu_learn_default(sc);
+ if (err != 0) {
+ DPRINTF(sc, ARSWITCH_DBG_ANY,
+ "%s: atu_learn_default: err=%d\n", __func__, err);
+ return (err);
+ }
+
/* Initialize the switch ports. */
for (port = 0; port <= sc->numphys; port++) {
sc->hal.arswitch_port_init(sc, port);
@@ -481,6 +683,8 @@ arswitch_detach(device_t dev)
free(sc->ifname[i], M_DEVBUF);
}
+ free(sc->atu.entries, M_DEVBUF);
+
bus_generic_detach(dev);
mtx_destroy(&sc->sc_mtx);
@@ -940,6 +1144,86 @@ arswitch_setconf(device_t dev, etherswitch_conf_t *conf)
}
static int
+arswitch_atu_flush_all(device_t dev)
+{
+ struct arswitch_softc *sc;
+ int err;
+
+ sc = device_get_softc(dev);
+ ARSWITCH_LOCK(sc);
+ err = sc->hal.arswitch_atu_flush(sc);
+ /* Invalidate cached ATU */
+ sc->atu.count = 0;
+ ARSWITCH_UNLOCK(sc);
+ return (err);
+}
+
+static int
+arswitch_atu_flush_port(device_t dev, int port)
+{
+ struct arswitch_softc *sc;
+ int err;
+
+ sc = device_get_softc(dev);
+ ARSWITCH_LOCK(sc);
+ err = sc->hal.arswitch_atu_flush_port(sc, port);
+ /* Invalidate cached ATU */
+ sc->atu.count = 0;
+ ARSWITCH_UNLOCK(sc);
+ return (err);
+}
+
+static int
+arswitch_atu_fetch_table(device_t dev, etherswitch_atu_table_t *table)
+{
+ struct arswitch_softc *sc;
+ int err, nitems;
+
+ sc = device_get_softc(dev);
+
+ ARSWITCH_LOCK(sc);
+ /* Initial setup */
+ nitems = 0;
+ err = sc->hal.arswitch_atu_fetch_table(sc, NULL, 0);
+
+ /* fetch - ideally yes we'd fetch into a separate table then switch */
+ while (err != -1 && nitems < sc->atu.size) {
+ err = sc->hal.arswitch_atu_fetch_table(sc,
+ &sc->atu.entries[nitems], 1);
+ if (err == 0) {
+ sc->atu.entries[nitems].id = nitems;
+ nitems++;
+ }
+ }
+ sc->atu.count = nitems;
+ ARSWITCH_UNLOCK(sc);
+
+ table->es_nitems = nitems;
+
+ return (0);
+}
+
+static int
+arswitch_atu_fetch_table_entry(device_t dev, etherswitch_atu_entry_t *e)
+{
+ struct arswitch_softc *sc;
+ int id;
+
+ sc = device_get_softc(dev);
+ id = e->id;
+
+ ARSWITCH_LOCK(sc);
+ if (id > sc->atu.count) {
+ ARSWITCH_UNLOCK(sc);
+ return (ENOENT);
+ }
+
+ memcpy(e, &sc->atu.entries[id], sizeof(*e));
+ ARSWITCH_UNLOCK(sc);
+ return (0);
+}
+
+static int
arswitch_getvgroup(device_t dev, etherswitch_vlangroup_t *e)
{
struct arswitch_softc *sc = device_get_softc(dev);
@@ -1003,6 +1287,10 @@ static device_method_t arswitch_methods[] = {
DEVMETHOD(etherswitch_setvgroup, arswitch_setvgroup),
DEVMETHOD(etherswitch_getconf, arswitch_getconf),
DEVMETHOD(etherswitch_setconf, arswitch_setconf),
+ DEVMETHOD(etherswitch_flush_all, arswitch_atu_flush_all),
+ DEVMETHOD(etherswitch_flush_port, arswitch_atu_flush_port),
+ DEVMETHOD(etherswitch_fetch_table, arswitch_atu_fetch_table),
+ DEVMETHOD(etherswitch_fetch_table_entry, arswitch_atu_fetch_table_entry),
DEVMETHOD_END
};
diff --git a/sys/dev/etherswitch/arswitch/arswitch_7240.c b/sys/dev/etherswitch/arswitch/arswitch_7240.c
index d114c3e0bf0b..c559064fca39 100644
--- a/sys/dev/etherswitch/arswitch/arswitch_7240.c
+++ b/sys/dev/etherswitch/arswitch/arswitch_7240.c
@@ -101,8 +101,6 @@ ar7240_hw_global_setup(struct arswitch_softc *sc)
AR7240_GLOBAL_CTRL_MTU_MASK,
SM(1536, AR7240_GLOBAL_CTRL_MTU_MASK));
- /* XXX ARP? Frame Age enable? */
-
/* Service Tag */
arswitch_modifyreg(sc->sc_dev, AR8X16_REG_SERVICE_TAG,
AR8X16_SERVICE_TAG_MASK, 0);
diff --git a/sys/dev/etherswitch/arswitch/arswitch_8316.c b/sys/dev/etherswitch/arswitch/arswitch_8316.c
index 91791ed2d07d..27617a4570f2 100644
--- a/sys/dev/etherswitch/arswitch/arswitch_8316.c
+++ b/sys/dev/etherswitch/arswitch/arswitch_8316.c
@@ -140,11 +140,6 @@ ar8316_hw_global_setup(struct arswitch_softc *sc)
/* Setup TAG priority mapping. */
arswitch_writereg(sc->sc_dev, AR8X16_REG_TAG_PRIO, 0xfa50);
- /* Enable ARP frame acknowledge. */
- /* XXX TODO: aging? */
- arswitch_modifyreg(sc->sc_dev, AR8X16_REG_AT_CTRL, 0,
- AR8X16_AT_CTRL_ARP_EN);
-
/*
* Flood address table misses to all ports, and enable forwarding of
* broadcasts to the cpu port.
diff --git a/sys/dev/etherswitch/arswitch/arswitch_8327.c b/sys/dev/etherswitch/arswitch/arswitch_8327.c
index 3738aaec3d6f..df58f893c606 100644
--- a/sys/dev/etherswitch/arswitch/arswitch_8327.c
+++ b/sys/dev/etherswitch/arswitch/arswitch_8327.c
@@ -702,6 +702,14 @@ ar8327_hw_setup(struct arswitch_softc *sc)
return (0);
}
+static int
+ar8327_atu_learn_default(struct arswitch_softc *sc)
+{
+
+ device_printf(sc->sc_dev, "%s: TODO!\n", __func__);
+ return (0);
+}
+
/*
* Initialise other global values, for the AR8327.
*/
@@ -1037,9 +1045,8 @@ ar8327_set_pvid(struct arswitch_softc *sc, int port, int pvid)
}
static int
-ar8327_atu_flush(struct arswitch_softc *sc)
+ar8327_atu_wait_ready(struct arswitch_softc *sc)
{
-
int ret;
ret = arswitch_waitreg(sc->sc_dev,
@@ -1048,6 +1055,18 @@ ar8327_atu_flush(struct arswitch_softc *sc)
0,
1000);
+ return (ret);
+}
+
+static int
+ar8327_atu_flush(struct arswitch_softc *sc)
+{
+
+ int ret;
+
+ ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
+
+ ret = ar8327_atu_wait_ready(sc);
if (ret)
device_printf(sc->sc_dev, "%s: waitreg failed\n", __func__);
@@ -1059,6 +1078,39 @@ ar8327_atu_flush(struct arswitch_softc *sc)
}
static int
+ar8327_atu_flush_port(struct arswitch_softc *sc, int port)
+{
+ int ret;
+ uint32_t val;
+
+ ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
+
+ ret = ar8327_atu_wait_ready(sc);
+ if (ret)
+ device_printf(sc->sc_dev, "%s: waitreg failed\n", __func__);
+
+ val = AR8327_ATU_FUNC_OP_FLUSH_UNICAST;
+ val |= SM(port, AR8327_ATU_FUNC_PORT_NUM);
+
+ if (!ret)
+ arswitch_writereg(sc->sc_dev,
+ AR8327_REG_ATU_FUNC,
+ val | AR8327_ATU_FUNC_BUSY);
+
+ return (ret);
+}
+
+static int
+ar8327_atu_fetch_table(struct arswitch_softc *sc, etherswitch_atu_entry_t *e,
+ int atu_fetch_op)
+{
+
+ /* XXX TODO */
+ return (ENXIO);
+}
+
+
+static int
ar8327_flush_dot1q_vlan(struct arswitch_softc *sc)
{
@@ -1175,7 +1227,10 @@ ar8327_attach(struct arswitch_softc *sc)
sc->hal.arswitch_get_port_vlan = ar8327_vlan_get_port;
sc->hal.arswitch_set_port_vlan = ar8327_vlan_set_port;
+ sc->hal.arswitch_atu_learn_default = ar8327_atu_learn_default;
sc->hal.arswitch_atu_flush = ar8327_atu_flush;
+ sc->hal.arswitch_atu_flush_port = ar8327_atu_flush_port;
+ sc->hal.arswitch_atu_fetch_table = ar8327_atu_fetch_table;
/*
* Reading the PHY via the MDIO interface currently doesn't
diff --git a/sys/dev/etherswitch/arswitch/arswitch_9340.c b/sys/dev/etherswitch/arswitch/arswitch_9340.c
index 947385190b45..13cc4b77c5b0 100644
--- a/sys/dev/etherswitch/arswitch/arswitch_9340.c
+++ b/sys/dev/etherswitch/arswitch/arswitch_9340.c
@@ -76,6 +76,27 @@ ar9340_hw_setup(struct arswitch_softc *sc)
return (0);
}
+static int
+ar9340_atu_learn_default(struct arswitch_softc *sc)
+{
+
+ /* Enable aging, MAC replacing */
+ arswitch_writereg(sc->sc_dev, AR934X_REG_AT_CTRL,
+ 0x2b /* 5 min age time */ |
+ AR934X_AT_CTRL_AGE_EN |
+ AR934X_AT_CTRL_LEARN_CHANGE);
+
+ /* Enable ARP frame acknowledge */
+ arswitch_modifyreg(sc->sc_dev, AR934X_REG_QM_CTRL,
+ AR934X_QM_CTRL_ARP_EN, AR934X_QM_CTRL_ARP_EN);
+
+ /* Copy frame to CPU port, not just redirect it */
+ arswitch_modifyreg(sc->sc_dev, AR934X_REG_QM_CTRL,
+ AR934X_QM_CTRL_ARP_COPY_EN, AR934X_QM_CTRL_ARP_COPY_EN);
+
+ return (0);
+}
+
/*
* Initialise other global values for the AR9340.
*/
@@ -92,16 +113,6 @@ ar9340_hw_global_setup(struct arswitch_softc *sc)
/* Setup TAG priority mapping */
arswitch_writereg(sc->sc_dev, AR8X16_REG_TAG_PRIO, 0xfa50);
- /* Enable aging, MAC replacing */
- arswitch_writereg(sc->sc_dev, AR934X_REG_AT_CTRL,
- 0x2b /* 5 min age time */ |
- AR934X_AT_CTRL_AGE_EN |
- AR934X_AT_CTRL_LEARN_CHANGE);
-
- /* Enable ARP frame acknowledge */
- arswitch_modifyreg(sc->sc_dev, AR934X_REG_QM_CTRL,
- AR934X_QM_CTRL_ARP_EN, AR934X_QM_CTRL_ARP_EN);
-
/* Enable Broadcast frames transmitted to the CPU */
arswitch_modifyreg(sc->sc_dev, AR934X_REG_FLOOD_MASK,
AR934X_FLOOD_MASK_BC_DP(0),
@@ -201,6 +212,7 @@ ar9340_attach(struct arswitch_softc *sc)
sc->hal.arswitch_hw_setup = ar9340_hw_setup;
sc->hal.arswitch_hw_global_setup = ar9340_hw_global_setup;
+ sc->hal.arswitch_atu_learn_default = ar9340_atu_learn_default;
/* Set the switch vlan capabilities. */
sc->info.es_vlan_caps = ETHERSWITCH_VLAN_DOT1Q |
diff --git a/sys/dev/etherswitch/arswitch/arswitchreg.h b/sys/dev/etherswitch/arswitch/arswitchreg.h
index 2dd0b1a965e5..36b1c51198f3 100644
--- a/sys/dev/etherswitch/arswitch/arswitchreg.h
+++ b/sys/dev/etherswitch/arswitch/arswitchreg.h
@@ -137,25 +137,42 @@
#define AR8216_ATU_OP_GET_NEXT 0x6
#define AR8216_ATU_ACTIVE BIT(3)
#define AR8216_ATU_PORT_NUM BITS(8, 4)
+#define AR8216_ATU_PORT_NUM_S 8
#define AR8216_ATU_FULL_VIO BIT(12)
#define AR8216_ATU_ADDR4 BITS(16, 8)
+#define AR8216_ATU_ADDR4_S 16
#define AR8216_ATU_ADDR5 BITS(24, 8)
+#define AR8216_ATU_ADDR5_S 24
#define AR8216_REG_ATU_DATA 0x0054
#define AR8216_ATU_ADDR3 BITS(0, 8)
+#define AR8216_ATU_ADDR3_S 0
#define AR8216_ATU_ADDR2 BITS(8, 8)
+#define AR8216_ATU_ADDR2_S 8
#define AR8216_ATU_ADDR1 BITS(16, 8)
+#define AR8216_ATU_ADDR1_S 16
#define AR8216_ATU_ADDR0 BITS(24, 8)
-
-#define AR8X16_REG_ARL_CTRL2 0x0058
+#define AR8216_ATU_ADDR0_S 24
+
+#define AR8216_REG_ATU_CTRL2 0x0058
+#define AR8216_ATU_CTRL2_DESPORT BITS(0, 5)
+#define AR8216_ATU_CTRL2_DESPORT_S 0
+#define AR8216_ATU_CTRL2_AT_PRIORITY BITS(10, 2)
+#define AR8216_ATU_CTRL2_AT_PRIORITY_EN BIT(12)
+#define AR8216_ATU_CTRL2_MIRROR_EN BIT(13)
+#define AR8216_ATU_CTRL2_SA_DROP_EN BIT(14)
+#define AR8216_ATU_CTRL2_AT_STATUS BITS(16, 4)
+#define AR8216_ATU_CTRL2_AT_STATUS_S 16
+#define AR8216_ATU_CTRL2_VLAN_LEAKY_EN BIT(24)
+#define AR8216_ATU_CTRL2_REDIRECT2CPU BIT(25)
+#define AR8216_ATU_CTRL2_COPY2CPU BIT(26)
#define AR8216_REG_ATU_CTRL 0x005C
-#define AR8216_ATU_CTRL_AGE_EN BIT(17)
#define AR8216_ATU_CTRL_AGE_TIME BITS(0, 16)
#define AR8216_ATU_CTRL_AGE_TIME_S 0
-
-#define AR8X16_REG_AT_CTRL 0x005c
-#define AR8X16_AT_CTRL_ARP_EN (1 << 20)
+#define AR8216_ATU_CTRL_AGE_EN BIT(17)
+#define AR8216_ATU_CTRL_LEARN_CHANGE BIT(18)
+#define AR8216_ATU_CTRL_ARP_EN BIT(20)
#define AR8X16_REG_IP_PRIORITY_1 0x0060
#define AR8X16_REG_IP_PRIORITY_2 0x0064
@@ -339,6 +356,7 @@
#define AR934X_REG_QM_CTRL 0x3c
#define AR934X_QM_CTRL_ARP_EN (1 << 15)
+#define AR934X_QM_CTRL_ARP_COPY_EN (1 << 14)
#define AR934X_REG_AT_CTRL 0x5c
#define AR934X_AT_CTRL_AGE_TIME BITS(0, 15)
@@ -471,7 +489,7 @@
#define AR8327_REG_ATU_DATA2 0x608
#define AR8327_REG_ATU_FUNC 0x60c
-#define AR8327_ATU_FUNC_OP BITS(0, 4)
+#define AR8327_ATU_FUNC_OP BITS(0, 3)
#define AR8327_ATU_FUNC_OP_NOOP 0x0
#define AR8327_ATU_FUNC_OP_FLUSH 0x1
#define AR8327_ATU_FUNC_OP_LOAD 0x2
@@ -481,7 +499,9 @@
#define AR8327_ATU_FUNC_OP_GET_NEXT 0x6
#define AR8327_ATU_FUNC_OP_SEARCH_MAC 0x7
#define AR8327_ATU_FUNC_OP_CHANGE_TRUNK 0x8
-#define AR8327_ATU_FUNC_BUSY (1U << 31)
+#define AR8327_ATU_FUNC_BUSY BIT(3)
+#define AR8327_ATU_FUNC_PORT_NUM BITS(8, 4)
+#define AR8327_ATU_FUNC_PORT_NUM_S 8
#define AR8327_REG_VTU_FUNC0 0x0610
#define AR8327_VTU_FUNC0_EG_MODE BITS(4, 14)
diff --git a/sys/dev/etherswitch/arswitch/arswitchvar.h b/sys/dev/etherswitch/arswitch/arswitchvar.h
index e30938266d5f..bd13895a88cb 100644
--- a/sys/dev/etherswitch/arswitch/arswitchvar.h
+++ b/sys/dev/etherswitch/arswitch/arswitchvar.h
@@ -87,6 +87,13 @@ struct arswitch_softc {
int vid[AR8X16_MAX_VLANS];
uint32_t vlan_mode;
+ /* ATU (address table unit) support */
+ struct {
+ int count;
+ int size;
+ etherswitch_atu_entry_t *entries;
+ } atu;
+
struct {
/* Global setup */
int (* arswitch_hw_setup) (struct arswitch_softc *);
@@ -99,6 +106,8 @@ struct arswitch_softc {
int (* arswitch_atu_flush) (struct arswitch_softc *);
int (* arswitch_atu_flush_port) (struct arswitch_softc *, int);
int (* arswitch_atu_learn_default) (struct arswitch_softc *);
+ int (* arswitch_atu_fetch_table) (struct arswitch_softc *,
+ etherswitch_atu_entry_t *, int atu_fetch_op);
/* VLAN functions */
int (* arswitch_port_vlan_setup) (struct arswitch_softc *,