Adrian Chadd 8f1cf028d3 AR8327: Fix up the ability to configure the vlangroup configuration for the CPU port
I messed up when doing the reset_vlans method - setting vid[0] = 1 here
was making it 'hidden' from configuration (as it needed ETHERSWITCH_VID_VALID
as well) and so there was no way to configure vlangroup0.

In per-port VLAN mode, vlangroup0 is for the CPU port (port0).
Now, it normally wouldn't really matter - the CPU port thus sees
all other ports. However there are two CPU ports on the AR8327 and
so port0 (arge0) was seeing all traffic on port6 (arge1).
If you thus tried to use arge1/port6 for anything (eg a WAN port)
in a bridge group then things would very upset very quickly.

Whilst here, add a comment to remind myself that yes, it'd be nice
if we could specify a boot-time switch config.

Tested:

* AP135 reference platform w/ AR8327N switch
2015-10-20 21:18:02 +00:00

1161 lines
28 KiB
C

/*-
* Copyright (c) 2011-2012 Stefan Bethke.
* Copyright (c) 2014 Adrian Chadd.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/errno.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <net/ethernet.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_types.h>
#include <machine/bus.h>
#include <dev/iicbus/iic.h>
#include <dev/iicbus/iiconf.h>
#include <dev/iicbus/iicbus.h>
#include <dev/mii/mii.h>
#include <dev/mii/miivar.h>
#include <dev/etherswitch/mdio.h>
#include <dev/etherswitch/etherswitch.h>
#include <dev/etherswitch/arswitch/arswitchreg.h>
#include <dev/etherswitch/arswitch/arswitchvar.h>
#include <dev/etherswitch/arswitch/arswitch_reg.h>
#include <dev/etherswitch/arswitch/arswitch_phy.h>
#include <dev/etherswitch/arswitch/arswitch_vlans.h>
#include <dev/etherswitch/arswitch/arswitch_8327.h>
#include "mdio_if.h"
#include "miibus_if.h"
#include "etherswitch_if.h"
/*
* AR8327 TODO:
*
* There should be a default hardware setup hint set for the default
* switch config. Otherwise the default is "all ports in one vlangroup",
* which means both CPU ports can see each other and that will quickly
* lead to traffic storms/loops.
*/
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)
{
if (bootverbose)
device_printf(sc->sc_dev,
"%s: called; phy=%d; chiprev=%d\n", __func__,
phy,
sc->chip_rev);
switch (sc->chip_rev) {
case 1:
/* For 100M waveform */
arswitch_writedbg(sc->sc_dev, phy, 0, 0x02ea);
/* Turn on Gigabit clock */
arswitch_writedbg(sc->sc_dev, phy, 0x3d, 0x68a0);
break;
case 2:
arswitch_writemmd(sc->sc_dev, phy, 0x7, 0x3c);
arswitch_writemmd(sc->sc_dev, phy, 0x4007, 0x0);
/* fallthrough */
case 4:
arswitch_writemmd(sc->sc_dev, phy, 0x3, 0x800d);
arswitch_writemmd(sc->sc_dev, phy, 0x4003, 0x803f);
arswitch_writedbg(sc->sc_dev, phy, 0x3d, 0x6860);
arswitch_writedbg(sc->sc_dev, phy, 0x5, 0x2c46);
arswitch_writedbg(sc->sc_dev, phy, 0x3c, 0x6000);
break;
}
}
static uint32_t
ar8327_get_pad_cfg(struct ar8327_pad_cfg *cfg)
{
uint32_t t;
if (!cfg)
return (0);
t = 0;
switch (cfg->mode) {
case AR8327_PAD_NC:
break;
case AR8327_PAD_MAC2MAC_MII:
t = AR8327_PAD_MAC_MII_EN;
if (cfg->rxclk_sel)
t |= AR8327_PAD_MAC_MII_RXCLK_SEL;
if (cfg->txclk_sel)
t |= AR8327_PAD_MAC_MII_TXCLK_SEL;
break;
case AR8327_PAD_MAC2MAC_GMII:
t = AR8327_PAD_MAC_GMII_EN;
if (cfg->rxclk_sel)
t |= AR8327_PAD_MAC_GMII_RXCLK_SEL;
if (cfg->txclk_sel)
t |= AR8327_PAD_MAC_GMII_TXCLK_SEL;
break;
case AR8327_PAD_MAC_SGMII:
t = AR8327_PAD_SGMII_EN;
/*
* WAR for the Qualcomm Atheros AP136 board.
* It seems that RGMII TX/RX delay settings needs to be
* applied for SGMII mode as well, The ethernet is not
* reliable without this.
*/
t |= cfg->txclk_delay_sel << AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S;
t |= cfg->rxclk_delay_sel << AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S;
if (cfg->rxclk_delay_en)
t |= AR8327_PAD_RGMII_RXCLK_DELAY_EN;
if (cfg->txclk_delay_en)
t |= AR8327_PAD_RGMII_TXCLK_DELAY_EN;
if (cfg->sgmii_delay_en)
t |= AR8327_PAD_SGMII_DELAY_EN;
break;
case AR8327_PAD_MAC2PHY_MII:
t = AR8327_PAD_PHY_MII_EN;
if (cfg->rxclk_sel)
t |= AR8327_PAD_PHY_MII_RXCLK_SEL;
if (cfg->txclk_sel)
t |= AR8327_PAD_PHY_MII_TXCLK_SEL;
break;
case AR8327_PAD_MAC2PHY_GMII:
t = AR8327_PAD_PHY_GMII_EN;
if (cfg->pipe_rxclk_sel)
t |= AR8327_PAD_PHY_GMII_PIPE_RXCLK_SEL;
if (cfg->rxclk_sel)
t |= AR8327_PAD_PHY_GMII_RXCLK_SEL;
if (cfg->txclk_sel)
t |= AR8327_PAD_PHY_GMII_TXCLK_SEL;
break;
case AR8327_PAD_MAC_RGMII:
t = AR8327_PAD_RGMII_EN;
t |= cfg->txclk_delay_sel << AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S;
t |= cfg->rxclk_delay_sel << AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S;
if (cfg->rxclk_delay_en)
t |= AR8327_PAD_RGMII_RXCLK_DELAY_EN;
if (cfg->txclk_delay_en)
t |= AR8327_PAD_RGMII_TXCLK_DELAY_EN;
break;
case AR8327_PAD_PHY_GMII:
t = AR8327_PAD_PHYX_GMII_EN;
break;
case AR8327_PAD_PHY_RGMII:
t = AR8327_PAD_PHYX_RGMII_EN;
break;
case AR8327_PAD_PHY_MII:
t = AR8327_PAD_PHYX_MII_EN;
break;
}
return (t);
}
/*
* Map the hard-coded port config from the switch setup to
* the chipset port config (status, duplex, flow, etc.)
*/
static uint32_t
ar8327_get_port_init_status(struct ar8327_port_cfg *cfg)
{
uint32_t t;
if (!cfg->force_link)
return (AR8X16_PORT_STS_LINK_AUTO);
t = AR8X16_PORT_STS_TXMAC | AR8X16_PORT_STS_RXMAC;
t |= cfg->duplex ? AR8X16_PORT_STS_DUPLEX : 0;
t |= cfg->rxpause ? AR8X16_PORT_STS_RXFLOW : 0;
t |= cfg->txpause ? AR8X16_PORT_STS_TXFLOW : 0;
switch (cfg->speed) {
case AR8327_PORT_SPEED_10:
t |= AR8X16_PORT_STS_SPEED_10;
break;
case AR8327_PORT_SPEED_100:
t |= AR8X16_PORT_STS_SPEED_100;
break;
case AR8327_PORT_SPEED_1000:
t |= AR8X16_PORT_STS_SPEED_1000;
break;
}
return (t);
}
/*
* Fetch the port data for the given port.
*
* This goes and does dirty things with the hints space
* to determine what the configuration parameters should be.
*
* Returns 1 if the structure was successfully parsed and
* the contents are valid; 0 otherwise.
*/
static int
ar8327_fetch_pdata_port(struct arswitch_softc *sc,
struct ar8327_port_cfg *pcfg,
int port)
{
int val;
char sbuf[128];
/* Check if force_link exists */
val = 0;
snprintf(sbuf, 128, "port.%d.force_link", port);
(void) resource_int_value(device_get_name(sc->sc_dev),
device_get_unit(sc->sc_dev),
sbuf, &val);
if (val != 1)
return (0);
pcfg->force_link = 1;
/* force_link is set; let's parse the rest of the fields */
snprintf(sbuf, 128, "port.%d.speed", port);
if (resource_int_value(device_get_name(sc->sc_dev),
device_get_unit(sc->sc_dev),
sbuf, &val) == 0) {
switch (val) {
case 10:
pcfg->speed = AR8327_PORT_SPEED_10;
break;
case 100:
pcfg->speed = AR8327_PORT_SPEED_100;
break;
case 1000:
pcfg->speed = AR8327_PORT_SPEED_1000;
break;
default:
device_printf(sc->sc_dev,
"%s: invalid port %d duplex value (%d)\n",
__func__,
port,
val);
return (0);
}
}
snprintf(sbuf, 128, "port.%d.duplex", port);
if (resource_int_value(device_get_name(sc->sc_dev),
device_get_unit(sc->sc_dev),
sbuf, &val) == 0)
pcfg->duplex = val;
snprintf(sbuf, 128, "port.%d.txpause", port);
if (resource_int_value(device_get_name(sc->sc_dev),
device_get_unit(sc->sc_dev),
sbuf, &val) == 0)
pcfg->txpause = val;
snprintf(sbuf, 128, "port.%d.rxpause", port);
if (resource_int_value(device_get_name(sc->sc_dev),
device_get_unit(sc->sc_dev),
sbuf, &val) == 0)
pcfg->rxpause = val;
#if 1
device_printf(sc->sc_dev,
"%s: port %d: speed=%d, duplex=%d, txpause=%d, rxpause=%d\n",
__func__,
port,
pcfg->speed,
pcfg->duplex,
pcfg->txpause,
pcfg->rxpause);
#endif
return (1);
}
/*
* Parse the pad configuration from the boot hints.
*
* The (mostly optional) fields are:
*
* uint32_t mode;
* uint32_t rxclk_sel;
* uint32_t txclk_sel;
* uint32_t txclk_delay_sel;
* uint32_t rxclk_delay_sel;
* uint32_t txclk_delay_en;
* uint32_t rxclk_delay_en;
* uint32_t sgmii_delay_en;
* uint32_t pipe_rxclk_sel;
*
* If mode isn't in the hints, 0 is returned.
* Else the structure is fleshed out and 1 is returned.
*/
static int
ar8327_fetch_pdata_pad(struct arswitch_softc *sc,
struct ar8327_pad_cfg *pc,
int pad)
{
int val;
char sbuf[128];
/* Check if mode exists */
val = 0;
snprintf(sbuf, 128, "pad.%d.mode", pad);
if (resource_int_value(device_get_name(sc->sc_dev),
device_get_unit(sc->sc_dev),
sbuf, &val) != 0)
return (0);
/* assume that 'mode' exists and was found */
pc->mode = val;
snprintf(sbuf, 128, "pad.%d.rxclk_sel", pad);
if (resource_int_value(device_get_name(sc->sc_dev),
device_get_unit(sc->sc_dev),
sbuf, &val) == 0)
pc->rxclk_sel = val;
snprintf(sbuf, 128, "pad.%d.txclk_sel", pad);
if (resource_int_value(device_get_name(sc->sc_dev),
device_get_unit(sc->sc_dev),
sbuf, &val) == 0)
pc->txclk_sel = val;
snprintf(sbuf, 128, "pad.%d.txclk_delay_sel", pad);
if (resource_int_value(device_get_name(sc->sc_dev),
device_get_unit(sc->sc_dev),
sbuf, &val) == 0)
pc->txclk_delay_sel = val;
snprintf(sbuf, 128, "pad.%d.rxclk_delay_sel", pad);
if (resource_int_value(device_get_name(sc->sc_dev),
device_get_unit(sc->sc_dev),
sbuf, &val) == 0)
pc->rxclk_delay_sel = val;
snprintf(sbuf, 128, "pad.%d.txclk_delay_en", pad);
if (resource_int_value(device_get_name(sc->sc_dev),
device_get_unit(sc->sc_dev),
sbuf, &val) == 0)
pc->txclk_delay_en = val;
snprintf(sbuf, 128, "pad.%d.rxclk_delay_en", pad);
if (resource_int_value(device_get_name(sc->sc_dev),
device_get_unit(sc->sc_dev),
sbuf, &val) == 0)
pc->rxclk_delay_en = val;
snprintf(sbuf, 128, "pad.%d.sgmii_delay_en", pad);
if (resource_int_value(device_get_name(sc->sc_dev),
device_get_unit(sc->sc_dev),
sbuf, &val) == 0)
pc->sgmii_delay_en = val;
snprintf(sbuf, 128, "pad.%d.pipe_rxclk_sel", pad);
if (resource_int_value(device_get_name(sc->sc_dev),
device_get_unit(sc->sc_dev),
sbuf, &val) == 0)
pc->pipe_rxclk_sel = val;
if (bootverbose) {
device_printf(sc->sc_dev,
"%s: pad %d: mode=%d, rxclk_sel=%d, txclk_sel=%d, "
"txclk_delay_sel=%d, rxclk_delay_sel=%d, txclk_delay_en=%d, "
"rxclk_enable_en=%d, sgmii_delay_en=%d, pipe_rxclk_sel=%d\n",
__func__,
pad,
pc->mode,
pc->rxclk_sel,
pc->txclk_sel,
pc->txclk_delay_sel,
pc->rxclk_delay_sel,
pc->txclk_delay_en,
pc->rxclk_delay_en,
pc->sgmii_delay_en,
pc->pipe_rxclk_sel);
}
return (1);
}
/*
* Fetch the SGMII configuration block from the boot hints.
*/
static int
ar8327_fetch_pdata_sgmii(struct arswitch_softc *sc,
struct ar8327_sgmii_cfg *scfg)
{
int val;
/* sgmii_ctrl */
val = 0;
if (resource_int_value(device_get_name(sc->sc_dev),
device_get_unit(sc->sc_dev),
"sgmii.ctrl", &val) != 0)
return (0);
scfg->sgmii_ctrl = val;
/* serdes_aen */
val = 0;
if (resource_int_value(device_get_name(sc->sc_dev),
device_get_unit(sc->sc_dev),
"sgmii.serdes_aen", &val) != 0)
return (0);
scfg->serdes_aen = val;
return (1);
}
/*
* Fetch the LED configuration from the boot hints.
*/
static int
ar8327_fetch_pdata_led(struct arswitch_softc *sc,
struct ar8327_led_cfg *lcfg)
{
int val;
val = 0;
if (resource_int_value(device_get_name(sc->sc_dev),
device_get_unit(sc->sc_dev),
"led.ctrl0", &val) != 0)
return (0);
lcfg->led_ctrl0 = val;
val = 0;
if (resource_int_value(device_get_name(sc->sc_dev),
device_get_unit(sc->sc_dev),
"led.ctrl1", &val) != 0)
return (0);
lcfg->led_ctrl1 = val;
val = 0;
if (resource_int_value(device_get_name(sc->sc_dev),
device_get_unit(sc->sc_dev),
"led.ctrl2", &val) != 0)
return (0);
lcfg->led_ctrl2 = val;
val = 0;
if (resource_int_value(device_get_name(sc->sc_dev),
device_get_unit(sc->sc_dev),
"led.ctrl3", &val) != 0)
return (0);
lcfg->led_ctrl3 = val;
val = 0;
if (resource_int_value(device_get_name(sc->sc_dev),
device_get_unit(sc->sc_dev),
"led.open_drain", &val) != 0)
return (0);
lcfg->open_drain = val;
return (1);
}
/*
* Initialise the ar8327 specific hardware features from
* the hints provided in the boot environment.
*/
static int
ar8327_init_pdata(struct arswitch_softc *sc)
{
struct ar8327_pad_cfg pc;
struct ar8327_port_cfg port_cfg;
struct ar8327_sgmii_cfg scfg;
struct ar8327_led_cfg lcfg;
uint32_t t, new_pos, pos;
/* Port 0 */
bzero(&port_cfg, sizeof(port_cfg));
sc->ar8327.port0_status = 0;
if (ar8327_fetch_pdata_port(sc, &port_cfg, 0))
sc->ar8327.port0_status = ar8327_get_port_init_status(&port_cfg);
/* Port 6 */
bzero(&port_cfg, sizeof(port_cfg));
sc->ar8327.port6_status = 0;
if (ar8327_fetch_pdata_port(sc, &port_cfg, 6))
sc->ar8327.port6_status = ar8327_get_port_init_status(&port_cfg);
/* Pad 0 */
bzero(&pc, sizeof(pc));
t = 0;
if (ar8327_fetch_pdata_pad(sc, &pc, 0))
t = ar8327_get_pad_cfg(&pc);
#if 0
if (AR8X16_IS_SWITCH(sc, AR8337))
t |= AR8337_PAD_MAC06_EXCHANGE_EN;
#endif
arswitch_writereg(sc->sc_dev, AR8327_REG_PAD0_MODE, t);
/* Pad 5 */
bzero(&pc, sizeof(pc));
t = 0;
if (ar8327_fetch_pdata_pad(sc, &pc, 5))
t = ar8327_get_pad_cfg(&pc);
arswitch_writereg(sc->sc_dev, AR8327_REG_PAD5_MODE, t);
/* Pad 6 */
bzero(&pc, sizeof(pc));
t = 0;
if (ar8327_fetch_pdata_pad(sc, &pc, 6))
t = ar8327_get_pad_cfg(&pc);
arswitch_writereg(sc->sc_dev, AR8327_REG_PAD6_MODE, t);
pos = arswitch_readreg(sc->sc_dev, AR8327_REG_POWER_ON_STRIP);
new_pos = pos;
/* XXX LED config */
bzero(&lcfg, sizeof(lcfg));
if (ar8327_fetch_pdata_led(sc, &lcfg)) {
if (lcfg.open_drain)
new_pos |= AR8327_POWER_ON_STRIP_LED_OPEN_EN;
else
new_pos &= ~AR8327_POWER_ON_STRIP_LED_OPEN_EN;
arswitch_writereg(sc->sc_dev, AR8327_REG_LED_CTRL0,
lcfg.led_ctrl0);
arswitch_writereg(sc->sc_dev, AR8327_REG_LED_CTRL1,
lcfg.led_ctrl1);
arswitch_writereg(sc->sc_dev, AR8327_REG_LED_CTRL2,
lcfg.led_ctrl2);
arswitch_writereg(sc->sc_dev, AR8327_REG_LED_CTRL3,
lcfg.led_ctrl3);
if (new_pos != pos)
new_pos |= AR8327_POWER_ON_STRIP_POWER_ON_SEL;
}
/* SGMII config */
bzero(&scfg, sizeof(scfg));
if (ar8327_fetch_pdata_sgmii(sc, &scfg)) {
device_printf(sc->sc_dev, "%s: SGMII cfg?\n", __func__);
t = scfg.sgmii_ctrl;
if (sc->chip_rev == 1)
t |= AR8327_SGMII_CTRL_EN_PLL |
AR8327_SGMII_CTRL_EN_RX |
AR8327_SGMII_CTRL_EN_TX;
else
t &= ~(AR8327_SGMII_CTRL_EN_PLL |
AR8327_SGMII_CTRL_EN_RX |
AR8327_SGMII_CTRL_EN_TX);
arswitch_writereg(sc->sc_dev, AR8327_REG_SGMII_CTRL, t);
if (scfg.serdes_aen)
new_pos &= ~AR8327_POWER_ON_STRIP_SERDES_AEN;
else
new_pos |= AR8327_POWER_ON_STRIP_SERDES_AEN;
}
arswitch_writereg(sc->sc_dev, AR8327_REG_POWER_ON_STRIP, new_pos);
return (0);
}
static int
ar8327_hw_setup(struct arswitch_softc *sc)
{
int i;
int err;
/* pdata fetch and setup */
err = ar8327_init_pdata(sc);
if (err != 0)
return (err);
/* XXX init leds */
for (i = 0; i < AR8327_NUM_PHYS; i++) {
/* phy fixup */
ar8327_phy_fixup(sc, i);
/* start PHY autonegotiation? */
/* XXX is this done as part of the normal PHY setup? */
};
/* Let things settle */
DELAY(1000);
return (0);
}
/*
* Initialise other global values, for the AR8327.
*/
static int
ar8327_hw_global_setup(struct arswitch_softc *sc)
{
uint32_t t;
/* enable CPU port and disable mirror port */
t = AR8327_FWD_CTRL0_CPU_PORT_EN |
AR8327_FWD_CTRL0_MIRROR_PORT;
arswitch_writereg(sc->sc_dev, AR8327_REG_FWD_CTRL0, t);
/* forward multicast and broadcast frames to CPU */
t = (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_UC_FLOOD_S) |
(AR8327_PORTS_ALL << AR8327_FWD_CTRL1_MC_FLOOD_S) |
(AR8327_PORTS_ALL << AR8327_FWD_CTRL1_BC_FLOOD_S);
arswitch_writereg(sc->sc_dev, AR8327_REG_FWD_CTRL1, t);
/* enable jumbo frames */
/* XXX need to macro-shift the value! */
arswitch_modifyreg(sc->sc_dev, AR8327_REG_MAX_FRAME_SIZE,
AR8327_MAX_FRAME_SIZE_MTU, 9018 + 8 + 2);
/* Enable MIB counters */
arswitch_modifyreg(sc->sc_dev, AR8327_REG_MODULE_EN,
AR8327_MODULE_EN_MIB, AR8327_MODULE_EN_MIB);
/* Disable EEE on all ports due to stability issues */
t = arswitch_readreg(sc->sc_dev, AR8327_REG_EEE_CTRL);
t |= AR8327_EEE_CTRL_DISABLE_PHY(0) |
AR8327_EEE_CTRL_DISABLE_PHY(1) |
AR8327_EEE_CTRL_DISABLE_PHY(2) |
AR8327_EEE_CTRL_DISABLE_PHY(3) |
AR8327_EEE_CTRL_DISABLE_PHY(4);
arswitch_writereg(sc->sc_dev, AR8327_REG_EEE_CTRL, t);
/* Set the right number of ports */
/* GMAC0 (CPU), GMAC1..5 (PHYs), GMAC6 (CPU) */
sc->info.es_nports = 7;
return (0);
}
/*
* Port setup. Called at attach time.
*/
static void
ar8327_port_init(struct arswitch_softc *sc, int port)
{
uint32_t t;
int ports;
/* For now, port can see all other ports */
ports = 0x7f;
if (port == AR8X16_PORT_CPU)
t = sc->ar8327.port0_status;
else if (port == 6)
t = sc->ar8327.port6_status;
else
t = AR8X16_PORT_STS_LINK_AUTO;
arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_STATUS(port), t);
arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_HEADER(port), 0);
/*
* Default to 1 port group.
*/
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(port), t);
t = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH << AR8327_PORT_VLAN1_OUT_MODE_S;
arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_VLAN1(port), t);
/*
* This doesn't configure any ports which this port can "see".
* bits 0-6 control which ports a frame coming into this port
* can be sent out to.
*
* So by doing this, we're making it impossible to send frames out
* to that port.
*/
t = AR8327_PORT_LOOKUP_LEARN;
t |= AR8X16_PORT_CTRL_STATE_FORWARD << AR8327_PORT_LOOKUP_STATE_S;
/* So this allows traffic to any port except ourselves */
t |= (ports & ~(1 << port));
arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_LOOKUP(port), t);
}
static int
ar8327_port_vlan_setup(struct arswitch_softc *sc, etherswitch_port_t *p)
{
/* Check: ADDTAG/STRIPTAG - exclusive */
ARSWITCH_LOCK(sc);
/* Set the PVID. */
if (p->es_pvid != 0)
sc->hal.arswitch_vlan_set_pvid(sc, p->es_port, p->es_pvid);
/*
* DOUBLE_TAG
* VLAN_MODE_ADD
* VLAN_MODE_STRIP
*/
ARSWITCH_UNLOCK(sc);
return (0);
}
/*
* Get the port VLAN configuration.
*/
static int
ar8327_port_vlan_get(struct arswitch_softc *sc, etherswitch_port_t *p)
{
ARSWITCH_LOCK(sc);
/* Retrieve the PVID */
sc->hal.arswitch_vlan_get_pvid(sc, p->es_port, &p->es_pvid);
/* Retrieve the current port configuration from the VTU */
/*
* DOUBLE_TAG
* VLAN_MODE_ADD
* VLAN_MODE_STRIP
*/
ARSWITCH_UNLOCK(sc);
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 t;
int ports;
ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
ARSWITCH_LOCK(sc);
/* Clear the existing VLAN configuration */
memset(sc->vid, 0, sizeof(sc->vid));
/*
* Disable mirroring.
*/
arswitch_modifyreg(sc->sc_dev, AR8327_REG_FWD_CTRL0,
AR8327_FWD_CTRL0_MIRROR_PORT,
(0xF << AR8327_FWD_CTRL0_MIRROR_PORT_S));
/*
* XXX TODO: disable any Q-in-Q port configuration,
* tagging, egress filters, etc.
*/
/*
* For now, let's default to one portgroup, just so traffic
* flows. All ports can see other ports. There are two CPU GMACs
* (GMAC0, GMAC6), GMAC1..GMAC5 are external PHYs.
*
* (ETHERSWITCH_VLAN_PORT)
*/
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) {
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);
t = AR8327_PORT_VLAN1_PORT_VLAN_PROP;
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;
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.
*/
for (i = 0; i < AR8327_NUM_PORTS; i++) {
ar8327_port_disable_mirror(sc, i);
}
/*
* If dot1q - set pvid; dot1q, etc.
*/
if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
sc->vid[0] = 1;
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);
}
static int
ar8327_vlan_get_port(struct arswitch_softc *sc, uint32_t *ports, int vid)
{
int port;
uint32_t reg;
ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
/* For port based vlans the vlanid is the same as the port index. */
port = vid & ETHERSWITCH_VID_MASK;
reg = arswitch_readreg(sc->sc_dev, AR8327_REG_PORT_LOOKUP(port));
*ports = reg & 0x7f;
return (0);
}
static int
ar8327_vlan_set_port(struct arswitch_softc *sc, uint32_t ports, int vid)
{
int err, port;
ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
/* For port based vlans the vlanid is the same as the port index. */
port = vid & ETHERSWITCH_VID_MASK;
err = arswitch_modifyreg(sc->sc_dev, AR8327_REG_PORT_LOOKUP(port),
0x7f, /* vlan membership mask */
(ports & 0x7f));
if (err)
return (err);
return (0);
}
static int
ar8327_vlan_getvgroup(struct arswitch_softc *sc, etherswitch_vlangroup_t *vg)
{
return (ar8xxx_getvgroup(sc, vg));
}
static int
ar8327_vlan_setvgroup(struct arswitch_softc *sc, etherswitch_vlangroup_t *vg)
{
return (ar8xxx_setvgroup(sc, vg));
}
static int
ar8327_get_pvid(struct arswitch_softc *sc, int port, int *pvid)
{
uint32_t reg;
ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
/*
* XXX for now, assuming it's CVID; likely very wrong!
*/
port = port & ETHERSWITCH_VID_MASK;
reg = arswitch_readreg(sc->sc_dev, AR8327_REG_PORT_VLAN0(port));
reg = reg >> AR8327_PORT_VLAN0_DEF_CVID_S;
reg = reg & 0xfff;
*pvid = reg;
return (0);
}
static int
ar8327_set_pvid(struct arswitch_softc *sc, int port, int pvid)
{
uint32_t t;
/* Limit pvid to valid values */
pvid &= 0x7f;
t = pvid << AR8327_PORT_VLAN0_DEF_SVID_S;
t |= pvid << AR8327_PORT_VLAN0_DEF_CVID_S;
arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_VLAN0(port), t);
return (0);
}
static int
ar8327_atu_flush(struct arswitch_softc *sc)
{
int ret;
ret = arswitch_waitreg(sc->sc_dev,
AR8327_REG_ATU_FUNC,
AR8327_ATU_FUNC_BUSY,
0,
1000);
if (ret)
device_printf(sc->sc_dev, "%s: waitreg failed\n", __func__);
if (!ret)
arswitch_writereg(sc->sc_dev,
AR8327_REG_ATU_FUNC,
AR8327_ATU_FUNC_OP_FLUSH);
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)
{
sc->hal.arswitch_hw_setup = ar8327_hw_setup;
sc->hal.arswitch_hw_global_setup = ar8327_hw_global_setup;
sc->hal.arswitch_port_init = ar8327_port_init;
sc->hal.arswitch_vlan_getvgroup = ar8327_vlan_getvgroup;
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;
sc->hal.arswitch_vlan_set_pvid = ar8327_set_pvid;
sc->hal.arswitch_get_port_vlan = ar8327_vlan_get_port;
sc->hal.arswitch_set_port_vlan = ar8327_vlan_set_port;
sc->hal.arswitch_atu_flush = ar8327_atu_flush;
/*
* Reading the PHY via the MDIO interface currently doesn't
* work correctly.
*
* So for now, just go direct to the PHY registers themselves.
* This has always worked on external devices, but not internal
* devices (AR934x, AR724x, AR933x.)
*/
sc->hal.arswitch_phy_read = arswitch_readphy_external;
sc->hal.arswitch_phy_write = arswitch_writephy_external;
/* Set the switch vlan capabilities. */
sc->info.es_vlan_caps = ETHERSWITCH_VLAN_DOT1Q |
ETHERSWITCH_VLAN_PORT | ETHERSWITCH_VLAN_DOUBLE_TAG;
sc->info.es_nvlangroups = AR8X16_MAX_VLANS;
}