[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
This commit is contained in:
Adrian Chadd 2018-02-02 22:05:36 +00:00
parent 5725799a3b
commit 62042c979d
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=328812
7 changed files with 405 additions and 28 deletions

View File

@ -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);
@ -939,6 +1143,86 @@ arswitch_setconf(device_t dev, etherswitch_conf_t *conf)
return (0);
}
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)
{
@ -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
};

View File

@ -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);

View File

@ -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.

View File

@ -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__);
@ -1058,6 +1077,39 @@ ar8327_atu_flush(struct arswitch_softc *sc)
return (ret);
}
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

View File

@ -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 |

View File

@ -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 AR8216_ATU_ADDR0_S 24
#define AR8X16_REG_ARL_CTRL2 0x0058
#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)

View File

@ -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 *,