#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/mdio/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"
struct ar8327_led_mapping ar8327_led_mapping[AR8327_NUM_PHYS][ETHERSWITCH_PORT_MAX_LEDS] =
{
{
{AR8327_REG_LED_CTRL0, 14 },
{AR8327_REG_LED_CTRL1, 14 },
{AR8327_REG_LED_CTRL2, 14 }
},
{
{AR8327_REG_LED_CTRL3, 8 },
{AR8327_REG_LED_CTRL3, 10 },
{AR8327_REG_LED_CTRL3, 12 }
},
{
{AR8327_REG_LED_CTRL3, 14 },
{AR8327_REG_LED_CTRL3, 16 },
{AR8327_REG_LED_CTRL3, 18 }
},
{
{AR8327_REG_LED_CTRL3, 20 },
{AR8327_REG_LED_CTRL3, 22 },
{AR8327_REG_LED_CTRL3, 24 }
},
{
{AR8327_REG_LED_CTRL0, 30 },
{AR8327_REG_LED_CTRL1, 30 },
{AR8327_REG_LED_CTRL2, 30 }
}
};
static int
ar8327_vlan_op(struct arswitch_softc *sc, uint32_t op, uint32_t vid,
uint32_t data)
{
int err;
if (arswitch_waitreg(sc->sc_dev, AR8327_REG_VTU_FUNC1,
AR8327_VTU_FUNC1_BUSY, 0, 5))
return (EBUSY);
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);
}
op |= ((vid & 0xfff) << AR8327_VTU_FUNC1_VID_S);
op |= AR8327_VTU_FUNC1_BUSY;
arswitch_writereg(sc->sc_dev, AR8327_REG_VTU_FUNC1, op);
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:
arswitch_writedbg(sc->sc_dev, phy, 0, 0x02ea);
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);
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;
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);
}
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);
}
static int
ar8327_fetch_pdata_port(struct arswitch_softc *sc,
struct ar8327_port_cfg *pcfg,
int port)
{
int val;
char sbuf[128];
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;
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);
}
static int
ar8327_fetch_pdata_pad(struct arswitch_softc *sc,
struct ar8327_pad_cfg *pc,
int pad)
{
int val;
char sbuf[128];
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);
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);
}
static int
ar8327_fetch_pdata_sgmii(struct arswitch_softc *sc,
struct ar8327_sgmii_cfg *scfg)
{
int val;
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;
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);
}
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);
}
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;
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);
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);
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);
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);
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;
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;
}
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;
err = ar8327_init_pdata(sc);
if (err != 0)
return (err);
for (i = 0; i < AR8327_NUM_PHYS; i++) {
ar8327_phy_fixup(sc, i);
}
DELAY(1000);
return (0);
}
static int
ar8327_atu_learn_default(struct arswitch_softc *sc)
{
device_printf(sc->sc_dev, "%s: TODO!\n", __func__);
return (0);
}
static int
ar8327_hw_global_setup(struct arswitch_softc *sc)
{
uint32_t t;
ARSWITCH_LOCK(sc);
t = AR8327_FWD_CTRL0_CPU_PORT_EN |
AR8327_FWD_CTRL0_MIRROR_PORT;
arswitch_writereg(sc->sc_dev, AR8327_REG_FWD_CTRL0, t);
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);
arswitch_modifyreg(sc->sc_dev, AR8327_REG_MAX_FRAME_SIZE,
AR8327_MAX_FRAME_SIZE_MTU, 9018 + 8 + 2);
arswitch_modifyreg(sc->sc_dev, AR8327_REG_MODULE_EN,
AR8327_MODULE_EN_MIB, AR8327_MODULE_EN_MIB);
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);
sc->info.es_nports = 7;
ARSWITCH_UNLOCK(sc);
return (0);
}
static void
ar8327_port_init(struct arswitch_softc *sc, int port)
{
uint32_t t;
int 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);
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);
t = AR8327_PORT_LOOKUP_LEARN;
t |= AR8X16_PORT_CTRL_STATE_FORWARD << AR8327_PORT_LOOKUP_STATE_S;
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)
{
ARSWITCH_LOCK(sc);
if (p->es_pvid != 0)
sc->hal.arswitch_vlan_set_pvid(sc, p->es_port, p->es_pvid);
ARSWITCH_UNLOCK(sc);
return (0);
}
static int
ar8327_port_vlan_get(struct arswitch_softc *sc, etherswitch_port_t *p)
{
ARSWITCH_LOCK(sc);
sc->hal.arswitch_vlan_get_pvid(sc, p->es_port, &p->es_pvid);
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);
memset(sc->vid, 0, sizeof(sc->vid));
arswitch_modifyreg(sc->sc_dev, AR8327_REG_FWD_CTRL0,
AR8327_FWD_CTRL0_MIRROR_PORT,
(0xF << AR8327_FWD_CTRL0_MIRROR_PORT_S));
ports = 0x7f;
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;
ingress = AR8X16_PORT_VLAN_MODE_PORT_ONLY;
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 {
ingress = AR8X16_PORT_VLAN_MODE_PORT_ONLY;
egress = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH;
}
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);
t = (ports & ~(1 << i));
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);
}
for (i = 0; i < AR8327_NUM_PORTS; i++) {
ar8327_port_disable_mirror(sc, i);
}
if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
sc->vid[0] = 1;
for (i = 0; i < AR8327_NUM_PORTS; i++) {
sc->hal.arswitch_vlan_set_pvid(sc, i, sc->vid[0]);
}
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);
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);
port = vid & ETHERSWITCH_VID_MASK;
err = arswitch_modifyreg(sc->sc_dev, AR8327_REG_PORT_LOOKUP(port),
0x7f,
(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);
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;
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_wait_ready(struct arswitch_softc *sc)
{
int ret;
ret = arswitch_waitreg(sc->sc_dev,
AR8327_REG_ATU_FUNC,
AR8327_ATU_FUNC_BUSY,
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__);
if (!ret)
arswitch_writereg(sc->sc_dev,
AR8327_REG_ATU_FUNC,
AR8327_ATU_FUNC_OP_FLUSH | AR8327_ATU_FUNC_BUSY);
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)
{
uint32_t ret0, ret1, ret2, val;
ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
switch (atu_fetch_op) {
case 0:
DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: initializing\n", __func__);
(void) ar8327_atu_wait_ready(sc);
arswitch_writereg(sc->sc_dev,
AR8327_REG_ATU_FUNC, AR8327_ATU_FUNC_OP_GET_NEXT);
arswitch_writereg(sc->sc_dev, AR8327_REG_ATU_DATA0, 0);
arswitch_writereg(sc->sc_dev, AR8327_REG_ATU_DATA1, 0);
arswitch_writereg(sc->sc_dev, AR8327_REG_ATU_DATA2, 0);
return (0);
case 1:
DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: reading next\n", __func__);
(void) ar8327_atu_wait_ready(sc);
val = arswitch_readreg(sc->sc_dev, AR8327_REG_ATU_FUNC);
val |= AR8327_ATU_FUNC_BUSY;
arswitch_writereg(sc->sc_dev, AR8327_REG_ATU_FUNC, val);
(void) ar8327_atu_wait_ready(sc);
ret0 = arswitch_readreg(sc->sc_dev, AR8327_REG_ATU_DATA0);
ret1 = arswitch_readreg(sc->sc_dev, AR8327_REG_ATU_DATA1);
ret2 = arswitch_readreg(sc->sc_dev, AR8327_REG_ATU_DATA2);
if (MS(ret2, AR8327_ATU_FUNC_DATA2_STATUS) == 0)
return (-1);
e->es_macaddr[5] = MS(ret0, AR8327_ATU_DATA0_MAC_ADDR3);
e->es_macaddr[4] = MS(ret0, AR8327_ATU_DATA0_MAC_ADDR2);
e->es_macaddr[3] = MS(ret0, AR8327_ATU_DATA0_MAC_ADDR1);
e->es_macaddr[2] = MS(ret0, AR8327_ATU_DATA0_MAC_ADDR0);
e->es_macaddr[0] = MS(ret1, AR8327_ATU_DATA1_MAC_ADDR5);
e->es_macaddr[1] = MS(ret1, AR8327_ATU_DATA1_MAC_ADDR4);
e->es_portmask = MS(ret1, AR8327_ATU_DATA1_DEST_PORT);
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);
}
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;
vid &= 0xfff;
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, ARSWITCH_DBG_REGIO, "%s: %d: reg=0x%08x\n", __func__, vid, reg);
*ports = 0;
for (i = 0; i < AR8327_NUM_PORTS; i++) {
val = reg >> AR8327_VTU_FUNC0_EG_MODE_S(i);
val = val & 0x3;
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, ARSWITCH_DBG_VLAN,
"%s: vid: %d, ports=0x%08x, untagged_ports=0x%08x\n",
__func__,
vid,
ports,
untagged_ports);
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_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;
sc->hal.arswitch_phy_read = arswitch_readphy_external;
sc->hal.arswitch_phy_write = arswitch_writephy_external;
sc->info.es_vlan_caps = ETHERSWITCH_VLAN_DOT1Q |
ETHERSWITCH_VLAN_PORT | ETHERSWITCH_VLAN_DOUBLE_TAG;
sc->info.es_nvlangroups = AR8X16_MAX_VLANS;
}