17330dd0bSAdrian Chadd /*- 27330dd0bSAdrian Chadd * Copyright (c) 2011-2012 Stefan Bethke. 37330dd0bSAdrian Chadd * Copyright (c) 2014 Adrian Chadd. 47330dd0bSAdrian Chadd * All rights reserved. 57330dd0bSAdrian Chadd * 67330dd0bSAdrian Chadd * Redistribution and use in source and binary forms, with or without 77330dd0bSAdrian Chadd * modification, are permitted provided that the following conditions 87330dd0bSAdrian Chadd * are met: 97330dd0bSAdrian Chadd * 1. Redistributions of source code must retain the above copyright 107330dd0bSAdrian Chadd * notice, this list of conditions and the following disclaimer. 117330dd0bSAdrian Chadd * 2. Redistributions in binary form must reproduce the above copyright 127330dd0bSAdrian Chadd * notice, this list of conditions and the following disclaimer in the 137330dd0bSAdrian Chadd * documentation and/or other materials provided with the distribution. 147330dd0bSAdrian Chadd * 157330dd0bSAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 167330dd0bSAdrian Chadd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 177330dd0bSAdrian Chadd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 187330dd0bSAdrian Chadd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 197330dd0bSAdrian Chadd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 207330dd0bSAdrian Chadd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 217330dd0bSAdrian Chadd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 227330dd0bSAdrian Chadd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 237330dd0bSAdrian Chadd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 247330dd0bSAdrian Chadd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 257330dd0bSAdrian Chadd * SUCH DAMAGE. 267330dd0bSAdrian Chadd * 277330dd0bSAdrian Chadd * $FreeBSD$ 287330dd0bSAdrian Chadd */ 297330dd0bSAdrian Chadd 307330dd0bSAdrian Chadd #include <sys/param.h> 317330dd0bSAdrian Chadd #include <sys/bus.h> 327330dd0bSAdrian Chadd #include <sys/errno.h> 337330dd0bSAdrian Chadd #include <sys/kernel.h> 347330dd0bSAdrian Chadd #include <sys/module.h> 357330dd0bSAdrian Chadd #include <sys/socket.h> 367330dd0bSAdrian Chadd #include <sys/sockio.h> 377330dd0bSAdrian Chadd #include <sys/sysctl.h> 387330dd0bSAdrian Chadd #include <sys/systm.h> 397330dd0bSAdrian Chadd 407330dd0bSAdrian Chadd #include <net/if.h> 417330dd0bSAdrian Chadd #include <net/if_arp.h> 427330dd0bSAdrian Chadd #include <net/ethernet.h> 437330dd0bSAdrian Chadd #include <net/if_dl.h> 447330dd0bSAdrian Chadd #include <net/if_media.h> 457330dd0bSAdrian Chadd #include <net/if_types.h> 467330dd0bSAdrian Chadd 477330dd0bSAdrian Chadd #include <machine/bus.h> 48efce3748SRui Paulo #include <dev/iicbus/iic.h> 497330dd0bSAdrian Chadd #include <dev/iicbus/iiconf.h> 507330dd0bSAdrian Chadd #include <dev/iicbus/iicbus.h> 517330dd0bSAdrian Chadd #include <dev/mii/mii.h> 527330dd0bSAdrian Chadd #include <dev/mii/miivar.h> 537330dd0bSAdrian Chadd #include <dev/etherswitch/mdio.h> 547330dd0bSAdrian Chadd 557330dd0bSAdrian Chadd #include <dev/etherswitch/etherswitch.h> 567330dd0bSAdrian Chadd 577330dd0bSAdrian Chadd #include <dev/etherswitch/arswitch/arswitchreg.h> 587330dd0bSAdrian Chadd #include <dev/etherswitch/arswitch/arswitchvar.h> 597330dd0bSAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_reg.h> 6078549b94SAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_phy.h> 6178549b94SAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_vlans.h> 6278549b94SAdrian Chadd 637330dd0bSAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_8327.h> 647330dd0bSAdrian Chadd 657330dd0bSAdrian Chadd #include "mdio_if.h" 667330dd0bSAdrian Chadd #include "miibus_if.h" 677330dd0bSAdrian Chadd #include "etherswitch_if.h" 687330dd0bSAdrian Chadd 69*036e1c76SAdrian Chadd 70*036e1c76SAdrian Chadd static int 71*036e1c76SAdrian Chadd ar8327_vlan_op(struct arswitch_softc *sc, uint32_t op, uint32_t vid, 72*036e1c76SAdrian Chadd uint32_t data) 73*036e1c76SAdrian Chadd { 74*036e1c76SAdrian Chadd int err; 75*036e1c76SAdrian Chadd 76*036e1c76SAdrian Chadd /* 77*036e1c76SAdrian Chadd * Wait for the "done" bit to finish. 78*036e1c76SAdrian Chadd */ 79*036e1c76SAdrian Chadd if (arswitch_waitreg(sc->sc_dev, AR8327_REG_VTU_FUNC1, 80*036e1c76SAdrian Chadd AR8327_VTU_FUNC1_BUSY, 0, 5)) 81*036e1c76SAdrian Chadd return (EBUSY); 82*036e1c76SAdrian Chadd 83*036e1c76SAdrian Chadd /* 84*036e1c76SAdrian Chadd * If it's a "load" operation, then ensure 'data' is loaded 85*036e1c76SAdrian Chadd * in first. 86*036e1c76SAdrian Chadd */ 87*036e1c76SAdrian Chadd if ((op & AR8327_VTU_FUNC1_OP) == AR8327_VTU_FUNC1_OP_LOAD) { 88*036e1c76SAdrian Chadd err = arswitch_writereg(sc->sc_dev, AR8327_REG_VTU_FUNC0, data); 89*036e1c76SAdrian Chadd if (err) 90*036e1c76SAdrian Chadd return (err); 91*036e1c76SAdrian Chadd } 92*036e1c76SAdrian Chadd 93*036e1c76SAdrian Chadd /* 94*036e1c76SAdrian Chadd * Set the VID. 95*036e1c76SAdrian Chadd */ 96*036e1c76SAdrian Chadd op |= ((vid & 0xfff) << AR8327_VTU_FUNC1_VID_S); 97*036e1c76SAdrian Chadd 98*036e1c76SAdrian Chadd /* 99*036e1c76SAdrian Chadd * Set busy bit to start loading in the command. 100*036e1c76SAdrian Chadd */ 101*036e1c76SAdrian Chadd op |= AR8327_VTU_FUNC1_BUSY; 102*036e1c76SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_VTU_FUNC1, op); 103*036e1c76SAdrian Chadd 104*036e1c76SAdrian Chadd /* 105*036e1c76SAdrian Chadd * Finally - wait for it to load. 106*036e1c76SAdrian Chadd */ 107*036e1c76SAdrian Chadd if (arswitch_waitreg(sc->sc_dev, AR8327_REG_VTU_FUNC1, 108*036e1c76SAdrian Chadd AR8327_VTU_FUNC1_BUSY, 0, 5)) 109*036e1c76SAdrian Chadd return (EBUSY); 110*036e1c76SAdrian Chadd 111*036e1c76SAdrian Chadd return (0); 112*036e1c76SAdrian Chadd } 113*036e1c76SAdrian Chadd 1147330dd0bSAdrian Chadd static void 1157330dd0bSAdrian Chadd ar8327_phy_fixup(struct arswitch_softc *sc, int phy) 1167330dd0bSAdrian Chadd { 117db37238fSAdrian Chadd if (bootverbose) 118db37238fSAdrian Chadd device_printf(sc->sc_dev, 119db37238fSAdrian Chadd "%s: called; phy=%d; chiprev=%d\n", __func__, 120db37238fSAdrian Chadd phy, 121db37238fSAdrian Chadd sc->chip_rev); 1227330dd0bSAdrian Chadd switch (sc->chip_rev) { 1237330dd0bSAdrian Chadd case 1: 1247330dd0bSAdrian Chadd /* For 100M waveform */ 1257330dd0bSAdrian Chadd arswitch_writedbg(sc->sc_dev, phy, 0, 0x02ea); 1267330dd0bSAdrian Chadd /* Turn on Gigabit clock */ 1277330dd0bSAdrian Chadd arswitch_writedbg(sc->sc_dev, phy, 0x3d, 0x68a0); 1287330dd0bSAdrian Chadd break; 1297330dd0bSAdrian Chadd 1307330dd0bSAdrian Chadd case 2: 1317330dd0bSAdrian Chadd arswitch_writemmd(sc->sc_dev, phy, 0x7, 0x3c); 1327330dd0bSAdrian Chadd arswitch_writemmd(sc->sc_dev, phy, 0x4007, 0x0); 1337330dd0bSAdrian Chadd /* fallthrough */ 1347330dd0bSAdrian Chadd case 4: 1357330dd0bSAdrian Chadd arswitch_writemmd(sc->sc_dev, phy, 0x3, 0x800d); 1367330dd0bSAdrian Chadd arswitch_writemmd(sc->sc_dev, phy, 0x4003, 0x803f); 1377330dd0bSAdrian Chadd 1387330dd0bSAdrian Chadd arswitch_writedbg(sc->sc_dev, phy, 0x3d, 0x6860); 1397330dd0bSAdrian Chadd arswitch_writedbg(sc->sc_dev, phy, 0x5, 0x2c46); 1407330dd0bSAdrian Chadd arswitch_writedbg(sc->sc_dev, phy, 0x3c, 0x6000); 1417330dd0bSAdrian Chadd break; 1427330dd0bSAdrian Chadd } 1437330dd0bSAdrian Chadd } 1447330dd0bSAdrian Chadd 1457330dd0bSAdrian Chadd static uint32_t 1467330dd0bSAdrian Chadd ar8327_get_pad_cfg(struct ar8327_pad_cfg *cfg) 1477330dd0bSAdrian Chadd { 1487330dd0bSAdrian Chadd uint32_t t; 1497330dd0bSAdrian Chadd 1507330dd0bSAdrian Chadd if (!cfg) 1517330dd0bSAdrian Chadd return (0); 1527330dd0bSAdrian Chadd 1537330dd0bSAdrian Chadd t = 0; 1547330dd0bSAdrian Chadd switch (cfg->mode) { 1557330dd0bSAdrian Chadd case AR8327_PAD_NC: 1567330dd0bSAdrian Chadd break; 1577330dd0bSAdrian Chadd 1587330dd0bSAdrian Chadd case AR8327_PAD_MAC2MAC_MII: 1597330dd0bSAdrian Chadd t = AR8327_PAD_MAC_MII_EN; 1607330dd0bSAdrian Chadd if (cfg->rxclk_sel) 1617330dd0bSAdrian Chadd t |= AR8327_PAD_MAC_MII_RXCLK_SEL; 1627330dd0bSAdrian Chadd if (cfg->txclk_sel) 1637330dd0bSAdrian Chadd t |= AR8327_PAD_MAC_MII_TXCLK_SEL; 1647330dd0bSAdrian Chadd break; 1657330dd0bSAdrian Chadd 1667330dd0bSAdrian Chadd case AR8327_PAD_MAC2MAC_GMII: 1677330dd0bSAdrian Chadd t = AR8327_PAD_MAC_GMII_EN; 1687330dd0bSAdrian Chadd if (cfg->rxclk_sel) 1697330dd0bSAdrian Chadd t |= AR8327_PAD_MAC_GMII_RXCLK_SEL; 1707330dd0bSAdrian Chadd if (cfg->txclk_sel) 1717330dd0bSAdrian Chadd t |= AR8327_PAD_MAC_GMII_TXCLK_SEL; 1727330dd0bSAdrian Chadd break; 1737330dd0bSAdrian Chadd 1747330dd0bSAdrian Chadd case AR8327_PAD_MAC_SGMII: 1757330dd0bSAdrian Chadd t = AR8327_PAD_SGMII_EN; 1767330dd0bSAdrian Chadd 1777330dd0bSAdrian Chadd /* 1789ab21e32SAdrian Chadd * WAR for the Qualcomm Atheros AP136 board. 1797330dd0bSAdrian Chadd * It seems that RGMII TX/RX delay settings needs to be 1807330dd0bSAdrian Chadd * applied for SGMII mode as well, The ethernet is not 1817330dd0bSAdrian Chadd * reliable without this. 1827330dd0bSAdrian Chadd */ 1837330dd0bSAdrian Chadd t |= cfg->txclk_delay_sel << AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S; 1847330dd0bSAdrian Chadd t |= cfg->rxclk_delay_sel << AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S; 1857330dd0bSAdrian Chadd if (cfg->rxclk_delay_en) 1867330dd0bSAdrian Chadd t |= AR8327_PAD_RGMII_RXCLK_DELAY_EN; 1877330dd0bSAdrian Chadd if (cfg->txclk_delay_en) 1887330dd0bSAdrian Chadd t |= AR8327_PAD_RGMII_TXCLK_DELAY_EN; 1897330dd0bSAdrian Chadd 1907330dd0bSAdrian Chadd if (cfg->sgmii_delay_en) 1917330dd0bSAdrian Chadd t |= AR8327_PAD_SGMII_DELAY_EN; 1927330dd0bSAdrian Chadd 1937330dd0bSAdrian Chadd break; 1947330dd0bSAdrian Chadd 1957330dd0bSAdrian Chadd case AR8327_PAD_MAC2PHY_MII: 1967330dd0bSAdrian Chadd t = AR8327_PAD_PHY_MII_EN; 1977330dd0bSAdrian Chadd if (cfg->rxclk_sel) 1987330dd0bSAdrian Chadd t |= AR8327_PAD_PHY_MII_RXCLK_SEL; 1997330dd0bSAdrian Chadd if (cfg->txclk_sel) 2007330dd0bSAdrian Chadd t |= AR8327_PAD_PHY_MII_TXCLK_SEL; 2017330dd0bSAdrian Chadd break; 2027330dd0bSAdrian Chadd 2037330dd0bSAdrian Chadd case AR8327_PAD_MAC2PHY_GMII: 2047330dd0bSAdrian Chadd t = AR8327_PAD_PHY_GMII_EN; 2057330dd0bSAdrian Chadd if (cfg->pipe_rxclk_sel) 2067330dd0bSAdrian Chadd t |= AR8327_PAD_PHY_GMII_PIPE_RXCLK_SEL; 2077330dd0bSAdrian Chadd if (cfg->rxclk_sel) 2087330dd0bSAdrian Chadd t |= AR8327_PAD_PHY_GMII_RXCLK_SEL; 2097330dd0bSAdrian Chadd if (cfg->txclk_sel) 2107330dd0bSAdrian Chadd t |= AR8327_PAD_PHY_GMII_TXCLK_SEL; 2117330dd0bSAdrian Chadd break; 2127330dd0bSAdrian Chadd 2137330dd0bSAdrian Chadd case AR8327_PAD_MAC_RGMII: 2147330dd0bSAdrian Chadd t = AR8327_PAD_RGMII_EN; 2157330dd0bSAdrian Chadd t |= cfg->txclk_delay_sel << AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S; 2167330dd0bSAdrian Chadd t |= cfg->rxclk_delay_sel << AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S; 2177330dd0bSAdrian Chadd if (cfg->rxclk_delay_en) 2187330dd0bSAdrian Chadd t |= AR8327_PAD_RGMII_RXCLK_DELAY_EN; 2197330dd0bSAdrian Chadd if (cfg->txclk_delay_en) 2207330dd0bSAdrian Chadd t |= AR8327_PAD_RGMII_TXCLK_DELAY_EN; 2217330dd0bSAdrian Chadd break; 2227330dd0bSAdrian Chadd 2237330dd0bSAdrian Chadd case AR8327_PAD_PHY_GMII: 2247330dd0bSAdrian Chadd t = AR8327_PAD_PHYX_GMII_EN; 2257330dd0bSAdrian Chadd break; 2267330dd0bSAdrian Chadd 2277330dd0bSAdrian Chadd case AR8327_PAD_PHY_RGMII: 2287330dd0bSAdrian Chadd t = AR8327_PAD_PHYX_RGMII_EN; 2297330dd0bSAdrian Chadd break; 2307330dd0bSAdrian Chadd 2317330dd0bSAdrian Chadd case AR8327_PAD_PHY_MII: 2327330dd0bSAdrian Chadd t = AR8327_PAD_PHYX_MII_EN; 2337330dd0bSAdrian Chadd break; 2347330dd0bSAdrian Chadd } 2357330dd0bSAdrian Chadd 2367330dd0bSAdrian Chadd return (t); 2377330dd0bSAdrian Chadd } 2387330dd0bSAdrian Chadd 2397330dd0bSAdrian Chadd /* 2407330dd0bSAdrian Chadd * Map the hard-coded port config from the switch setup to 2417330dd0bSAdrian Chadd * the chipset port config (status, duplex, flow, etc.) 2427330dd0bSAdrian Chadd */ 2437330dd0bSAdrian Chadd static uint32_t 2447330dd0bSAdrian Chadd ar8327_get_port_init_status(struct ar8327_port_cfg *cfg) 2457330dd0bSAdrian Chadd { 2467330dd0bSAdrian Chadd uint32_t t; 2477330dd0bSAdrian Chadd 2487330dd0bSAdrian Chadd if (!cfg->force_link) 2497330dd0bSAdrian Chadd return (AR8X16_PORT_STS_LINK_AUTO); 2507330dd0bSAdrian Chadd 2517330dd0bSAdrian Chadd t = AR8X16_PORT_STS_TXMAC | AR8X16_PORT_STS_RXMAC; 2527330dd0bSAdrian Chadd t |= cfg->duplex ? AR8X16_PORT_STS_DUPLEX : 0; 2537330dd0bSAdrian Chadd t |= cfg->rxpause ? AR8X16_PORT_STS_RXFLOW : 0; 2547330dd0bSAdrian Chadd t |= cfg->txpause ? AR8X16_PORT_STS_TXFLOW : 0; 2557330dd0bSAdrian Chadd 2567330dd0bSAdrian Chadd switch (cfg->speed) { 2577330dd0bSAdrian Chadd case AR8327_PORT_SPEED_10: 2587330dd0bSAdrian Chadd t |= AR8X16_PORT_STS_SPEED_10; 2597330dd0bSAdrian Chadd break; 2607330dd0bSAdrian Chadd case AR8327_PORT_SPEED_100: 2617330dd0bSAdrian Chadd t |= AR8X16_PORT_STS_SPEED_100; 2627330dd0bSAdrian Chadd break; 2637330dd0bSAdrian Chadd case AR8327_PORT_SPEED_1000: 2647330dd0bSAdrian Chadd t |= AR8X16_PORT_STS_SPEED_1000; 2657330dd0bSAdrian Chadd break; 2667330dd0bSAdrian Chadd } 2677330dd0bSAdrian Chadd 2687330dd0bSAdrian Chadd return (t); 2697330dd0bSAdrian Chadd } 2709ab21e32SAdrian Chadd 2719ab21e32SAdrian Chadd /* 272f9950f9aSAdrian Chadd * Fetch the port data for the given port. 273f9950f9aSAdrian Chadd * 274f9950f9aSAdrian Chadd * This goes and does dirty things with the hints space 275f9950f9aSAdrian Chadd * to determine what the configuration parameters should be. 276f9950f9aSAdrian Chadd * 277f9950f9aSAdrian Chadd * Returns 1 if the structure was successfully parsed and 278f9950f9aSAdrian Chadd * the contents are valid; 0 otherwise. 279f9950f9aSAdrian Chadd */ 280f9950f9aSAdrian Chadd static int 281f9950f9aSAdrian Chadd ar8327_fetch_pdata_port(struct arswitch_softc *sc, 282f9950f9aSAdrian Chadd struct ar8327_port_cfg *pcfg, 283f9950f9aSAdrian Chadd int port) 284f9950f9aSAdrian Chadd { 285f9950f9aSAdrian Chadd int val; 286f9950f9aSAdrian Chadd char sbuf[128]; 287f9950f9aSAdrian Chadd 288f9950f9aSAdrian Chadd /* Check if force_link exists */ 289f9950f9aSAdrian Chadd val = 0; 290f9950f9aSAdrian Chadd snprintf(sbuf, 128, "port.%d.force_link", port); 291f9950f9aSAdrian Chadd (void) resource_int_value(device_get_name(sc->sc_dev), 292f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 293f9950f9aSAdrian Chadd sbuf, &val); 294f9950f9aSAdrian Chadd if (val != 1) 295f9950f9aSAdrian Chadd return (0); 296f9950f9aSAdrian Chadd pcfg->force_link = 1; 297f9950f9aSAdrian Chadd 298f9950f9aSAdrian Chadd /* force_link is set; let's parse the rest of the fields */ 299f9950f9aSAdrian Chadd snprintf(sbuf, 128, "port.%d.speed", port); 300f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 301f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 302f9950f9aSAdrian Chadd sbuf, &val) == 0) { 303f9950f9aSAdrian Chadd switch (val) { 304f9950f9aSAdrian Chadd case 10: 305f9950f9aSAdrian Chadd pcfg->speed = AR8327_PORT_SPEED_10; 306f9950f9aSAdrian Chadd break; 307f9950f9aSAdrian Chadd case 100: 308f9950f9aSAdrian Chadd pcfg->speed = AR8327_PORT_SPEED_100; 309f9950f9aSAdrian Chadd break; 310f9950f9aSAdrian Chadd case 1000: 311f9950f9aSAdrian Chadd pcfg->speed = AR8327_PORT_SPEED_1000; 312f9950f9aSAdrian Chadd break; 313f9950f9aSAdrian Chadd default: 314f9950f9aSAdrian Chadd device_printf(sc->sc_dev, 315f9950f9aSAdrian Chadd "%s: invalid port %d duplex value (%d)\n", 316f9950f9aSAdrian Chadd __func__, 317f9950f9aSAdrian Chadd port, 318f9950f9aSAdrian Chadd val); 319f9950f9aSAdrian Chadd return (0); 320f9950f9aSAdrian Chadd } 321f9950f9aSAdrian Chadd } 322f9950f9aSAdrian Chadd 323f9950f9aSAdrian Chadd snprintf(sbuf, 128, "port.%d.duplex", port); 324f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 325f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 326f9950f9aSAdrian Chadd sbuf, &val) == 0) 327f9950f9aSAdrian Chadd pcfg->duplex = val; 328f9950f9aSAdrian Chadd 329f9950f9aSAdrian Chadd snprintf(sbuf, 128, "port.%d.txpause", port); 330f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 331f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 332f9950f9aSAdrian Chadd sbuf, &val) == 0) 333f9950f9aSAdrian Chadd pcfg->txpause = val; 334f9950f9aSAdrian Chadd 335f9950f9aSAdrian Chadd snprintf(sbuf, 128, "port.%d.rxpause", port); 336f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 337f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 338f9950f9aSAdrian Chadd sbuf, &val) == 0) 339f9950f9aSAdrian Chadd pcfg->rxpause = val; 340f9950f9aSAdrian Chadd 34178549b94SAdrian Chadd #if 1 342f9950f9aSAdrian Chadd device_printf(sc->sc_dev, 343f9950f9aSAdrian Chadd "%s: port %d: speed=%d, duplex=%d, txpause=%d, rxpause=%d\n", 344f9950f9aSAdrian Chadd __func__, 345f9950f9aSAdrian Chadd port, 346f9950f9aSAdrian Chadd pcfg->speed, 347f9950f9aSAdrian Chadd pcfg->duplex, 348f9950f9aSAdrian Chadd pcfg->txpause, 349f9950f9aSAdrian Chadd pcfg->rxpause); 350f9950f9aSAdrian Chadd #endif 351f9950f9aSAdrian Chadd 352f9950f9aSAdrian Chadd return (1); 353f9950f9aSAdrian Chadd } 354f9950f9aSAdrian Chadd 355f9950f9aSAdrian Chadd /* 356f9950f9aSAdrian Chadd * Parse the pad configuration from the boot hints. 357f9950f9aSAdrian Chadd * 358f9950f9aSAdrian Chadd * The (mostly optional) fields are: 359f9950f9aSAdrian Chadd * 360f9950f9aSAdrian Chadd * uint32_t mode; 361f9950f9aSAdrian Chadd * uint32_t rxclk_sel; 362f9950f9aSAdrian Chadd * uint32_t txclk_sel; 363f9950f9aSAdrian Chadd * uint32_t txclk_delay_sel; 364f9950f9aSAdrian Chadd * uint32_t rxclk_delay_sel; 365f9950f9aSAdrian Chadd * uint32_t txclk_delay_en; 366f9950f9aSAdrian Chadd * uint32_t rxclk_delay_en; 367f9950f9aSAdrian Chadd * uint32_t sgmii_delay_en; 368f9950f9aSAdrian Chadd * uint32_t pipe_rxclk_sel; 369f9950f9aSAdrian Chadd * 370f9950f9aSAdrian Chadd * If mode isn't in the hints, 0 is returned. 371f9950f9aSAdrian Chadd * Else the structure is fleshed out and 1 is returned. 372f9950f9aSAdrian Chadd */ 373f9950f9aSAdrian Chadd static int 374f9950f9aSAdrian Chadd ar8327_fetch_pdata_pad(struct arswitch_softc *sc, 375f9950f9aSAdrian Chadd struct ar8327_pad_cfg *pc, 376f9950f9aSAdrian Chadd int pad) 377f9950f9aSAdrian Chadd { 378f9950f9aSAdrian Chadd int val; 379f9950f9aSAdrian Chadd char sbuf[128]; 380f9950f9aSAdrian Chadd 381f9950f9aSAdrian Chadd /* Check if mode exists */ 382f9950f9aSAdrian Chadd val = 0; 383f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.mode", pad); 384f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 385f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 386f9950f9aSAdrian Chadd sbuf, &val) != 0) 387f9950f9aSAdrian Chadd return (0); 388f9950f9aSAdrian Chadd 389f9950f9aSAdrian Chadd /* assume that 'mode' exists and was found */ 390f9950f9aSAdrian Chadd pc->mode = val; 391f9950f9aSAdrian Chadd 392f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.rxclk_sel", pad); 393f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 394f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 395f9950f9aSAdrian Chadd sbuf, &val) == 0) 396f9950f9aSAdrian Chadd pc->rxclk_sel = val; 397f9950f9aSAdrian Chadd 398f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.txclk_sel", pad); 399f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 400f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 401f9950f9aSAdrian Chadd sbuf, &val) == 0) 402f9950f9aSAdrian Chadd pc->txclk_sel = val; 403f9950f9aSAdrian Chadd 404f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.txclk_delay_sel", pad); 405f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 406f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 407f9950f9aSAdrian Chadd sbuf, &val) == 0) 408f9950f9aSAdrian Chadd pc->txclk_delay_sel = val; 409f9950f9aSAdrian Chadd 410f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.rxclk_delay_sel", pad); 411f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 412f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 413f9950f9aSAdrian Chadd sbuf, &val) == 0) 414f9950f9aSAdrian Chadd pc->rxclk_delay_sel = val; 415f9950f9aSAdrian Chadd 416f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.txclk_delay_en", pad); 417f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 418f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 419f9950f9aSAdrian Chadd sbuf, &val) == 0) 420f9950f9aSAdrian Chadd pc->txclk_delay_en = val; 421f9950f9aSAdrian Chadd 422f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.rxclk_delay_en", pad); 423f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 424f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 425f9950f9aSAdrian Chadd sbuf, &val) == 0) 426f9950f9aSAdrian Chadd pc->rxclk_delay_en = val; 427f9950f9aSAdrian Chadd 428f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.sgmii_delay_en", pad); 429f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 430f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 431f9950f9aSAdrian Chadd sbuf, &val) == 0) 432f9950f9aSAdrian Chadd pc->sgmii_delay_en = val; 433f9950f9aSAdrian Chadd 434f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.pipe_rxclk_sel", pad); 435f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 436f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 437f9950f9aSAdrian Chadd sbuf, &val) == 0) 438f9950f9aSAdrian Chadd pc->pipe_rxclk_sel = val; 439f9950f9aSAdrian Chadd 440db37238fSAdrian Chadd if (bootverbose) { 441f9950f9aSAdrian Chadd device_printf(sc->sc_dev, 442f9950f9aSAdrian Chadd "%s: pad %d: mode=%d, rxclk_sel=%d, txclk_sel=%d, " 443f9950f9aSAdrian Chadd "txclk_delay_sel=%d, rxclk_delay_sel=%d, txclk_delay_en=%d, " 444f9950f9aSAdrian Chadd "rxclk_enable_en=%d, sgmii_delay_en=%d, pipe_rxclk_sel=%d\n", 445f9950f9aSAdrian Chadd __func__, 446f9950f9aSAdrian Chadd pad, 447f9950f9aSAdrian Chadd pc->mode, 448f9950f9aSAdrian Chadd pc->rxclk_sel, 449f9950f9aSAdrian Chadd pc->txclk_sel, 450f9950f9aSAdrian Chadd pc->txclk_delay_sel, 451f9950f9aSAdrian Chadd pc->rxclk_delay_sel, 452f9950f9aSAdrian Chadd pc->txclk_delay_en, 453f9950f9aSAdrian Chadd pc->rxclk_delay_en, 454f9950f9aSAdrian Chadd pc->sgmii_delay_en, 455f9950f9aSAdrian Chadd pc->pipe_rxclk_sel); 456db37238fSAdrian Chadd } 457f9950f9aSAdrian Chadd 458f9950f9aSAdrian Chadd return (1); 459f9950f9aSAdrian Chadd } 460f9950f9aSAdrian Chadd 461f9950f9aSAdrian Chadd /* 462810bdeddSAdrian Chadd * Fetch the SGMII configuration block from the boot hints. 463810bdeddSAdrian Chadd */ 464810bdeddSAdrian Chadd static int 465810bdeddSAdrian Chadd ar8327_fetch_pdata_sgmii(struct arswitch_softc *sc, 466810bdeddSAdrian Chadd struct ar8327_sgmii_cfg *scfg) 467810bdeddSAdrian Chadd { 468810bdeddSAdrian Chadd int val; 469810bdeddSAdrian Chadd 470810bdeddSAdrian Chadd /* sgmii_ctrl */ 471810bdeddSAdrian Chadd val = 0; 472810bdeddSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 473810bdeddSAdrian Chadd device_get_unit(sc->sc_dev), 474810bdeddSAdrian Chadd "sgmii.ctrl", &val) != 0) 475810bdeddSAdrian Chadd return (0); 476810bdeddSAdrian Chadd scfg->sgmii_ctrl = val; 477810bdeddSAdrian Chadd 478810bdeddSAdrian Chadd /* serdes_aen */ 479810bdeddSAdrian Chadd val = 0; 480810bdeddSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 481810bdeddSAdrian Chadd device_get_unit(sc->sc_dev), 482810bdeddSAdrian Chadd "sgmii.serdes_aen", &val) != 0) 483810bdeddSAdrian Chadd return (0); 484810bdeddSAdrian Chadd scfg->serdes_aen = val; 485810bdeddSAdrian Chadd 486810bdeddSAdrian Chadd return (1); 487810bdeddSAdrian Chadd } 488810bdeddSAdrian Chadd 489810bdeddSAdrian Chadd /* 490b67ba111SAdrian Chadd * Fetch the LED configuration from the boot hints. 491b67ba111SAdrian Chadd */ 492b67ba111SAdrian Chadd static int 493b67ba111SAdrian Chadd ar8327_fetch_pdata_led(struct arswitch_softc *sc, 494b67ba111SAdrian Chadd struct ar8327_led_cfg *lcfg) 495b67ba111SAdrian Chadd { 496b67ba111SAdrian Chadd int val; 497b67ba111SAdrian Chadd 498b67ba111SAdrian Chadd val = 0; 499b67ba111SAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 500b67ba111SAdrian Chadd device_get_unit(sc->sc_dev), 501b67ba111SAdrian Chadd "led.ctrl0", &val) != 0) 502b67ba111SAdrian Chadd return (0); 503b67ba111SAdrian Chadd lcfg->led_ctrl0 = val; 504b67ba111SAdrian Chadd 505b67ba111SAdrian Chadd val = 0; 506b67ba111SAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 507b67ba111SAdrian Chadd device_get_unit(sc->sc_dev), 508b67ba111SAdrian Chadd "led.ctrl1", &val) != 0) 509b67ba111SAdrian Chadd return (0); 510b67ba111SAdrian Chadd lcfg->led_ctrl1 = val; 511b67ba111SAdrian Chadd 512b67ba111SAdrian Chadd val = 0; 513b67ba111SAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 514b67ba111SAdrian Chadd device_get_unit(sc->sc_dev), 515b67ba111SAdrian Chadd "led.ctrl2", &val) != 0) 516b67ba111SAdrian Chadd return (0); 517b67ba111SAdrian Chadd lcfg->led_ctrl2 = val; 518b67ba111SAdrian Chadd 519b67ba111SAdrian Chadd val = 0; 520b67ba111SAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 521b67ba111SAdrian Chadd device_get_unit(sc->sc_dev), 522b67ba111SAdrian Chadd "led.ctrl3", &val) != 0) 523b67ba111SAdrian Chadd return (0); 524b67ba111SAdrian Chadd lcfg->led_ctrl3 = val; 525b67ba111SAdrian Chadd 526b67ba111SAdrian Chadd val = 0; 527b67ba111SAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 528b67ba111SAdrian Chadd device_get_unit(sc->sc_dev), 529b67ba111SAdrian Chadd "led.open_drain", &val) != 0) 530b67ba111SAdrian Chadd return (0); 531b67ba111SAdrian Chadd lcfg->open_drain = val; 532b67ba111SAdrian Chadd 533b67ba111SAdrian Chadd return (1); 534b67ba111SAdrian Chadd } 535b67ba111SAdrian Chadd 536b67ba111SAdrian Chadd /* 5379ab21e32SAdrian Chadd * Initialise the ar8327 specific hardware features from 5389ab21e32SAdrian Chadd * the hints provided in the boot environment. 5399ab21e32SAdrian Chadd */ 5409ab21e32SAdrian Chadd static int 5419ab21e32SAdrian Chadd ar8327_init_pdata(struct arswitch_softc *sc) 5429ab21e32SAdrian Chadd { 5439ab21e32SAdrian Chadd struct ar8327_pad_cfg pc; 5449ab21e32SAdrian Chadd struct ar8327_port_cfg port_cfg; 545810bdeddSAdrian Chadd struct ar8327_sgmii_cfg scfg; 546b67ba111SAdrian Chadd struct ar8327_led_cfg lcfg; 547810bdeddSAdrian Chadd uint32_t t, new_pos, pos; 5489ab21e32SAdrian Chadd 549f9950f9aSAdrian Chadd /* Port 0 */ 5509ab21e32SAdrian Chadd bzero(&port_cfg, sizeof(port_cfg)); 551f9950f9aSAdrian Chadd sc->ar8327.port0_status = 0; 552f9950f9aSAdrian Chadd if (ar8327_fetch_pdata_port(sc, &port_cfg, 0)) 5539ab21e32SAdrian Chadd sc->ar8327.port0_status = ar8327_get_port_init_status(&port_cfg); 5549ab21e32SAdrian Chadd 555f9950f9aSAdrian Chadd /* Port 6 */ 5569ab21e32SAdrian Chadd bzero(&port_cfg, sizeof(port_cfg)); 557f9950f9aSAdrian Chadd sc->ar8327.port6_status = 0; 558f9950f9aSAdrian Chadd if (ar8327_fetch_pdata_port(sc, &port_cfg, 6)) 5599ab21e32SAdrian Chadd sc->ar8327.port6_status = ar8327_get_port_init_status(&port_cfg); 5609ab21e32SAdrian Chadd 5619ab21e32SAdrian Chadd /* Pad 0 */ 5629ab21e32SAdrian Chadd bzero(&pc, sizeof(pc)); 563f9950f9aSAdrian Chadd t = 0; 564f9950f9aSAdrian Chadd if (ar8327_fetch_pdata_pad(sc, &pc, 0)) 5659ab21e32SAdrian Chadd t = ar8327_get_pad_cfg(&pc); 5669ab21e32SAdrian Chadd #if 0 5679ab21e32SAdrian Chadd if (AR8X16_IS_SWITCH(sc, AR8337)) 5689ab21e32SAdrian Chadd t |= AR8337_PAD_MAC06_EXCHANGE_EN; 5697330dd0bSAdrian Chadd #endif 5709ab21e32SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PAD0_MODE, t); 5719ab21e32SAdrian Chadd 5729ab21e32SAdrian Chadd /* Pad 5 */ 5739ab21e32SAdrian Chadd bzero(&pc, sizeof(pc)); 574f9950f9aSAdrian Chadd t = 0; 575f9950f9aSAdrian Chadd if (ar8327_fetch_pdata_pad(sc, &pc, 5)) 5769ab21e32SAdrian Chadd t = ar8327_get_pad_cfg(&pc); 5779ab21e32SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PAD5_MODE, t); 5789ab21e32SAdrian Chadd 5799ab21e32SAdrian Chadd /* Pad 6 */ 5809ab21e32SAdrian Chadd bzero(&pc, sizeof(pc)); 581f9950f9aSAdrian Chadd t = 0; 582f9950f9aSAdrian Chadd if (ar8327_fetch_pdata_pad(sc, &pc, 6)) 5839ab21e32SAdrian Chadd t = ar8327_get_pad_cfg(&pc); 5849ab21e32SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PAD6_MODE, t); 5859ab21e32SAdrian Chadd 586810bdeddSAdrian Chadd pos = arswitch_readreg(sc->sc_dev, AR8327_REG_POWER_ON_STRIP); 587810bdeddSAdrian Chadd new_pos = pos; 588810bdeddSAdrian Chadd 5899ab21e32SAdrian Chadd /* XXX LED config */ 590b67ba111SAdrian Chadd bzero(&lcfg, sizeof(lcfg)); 591b67ba111SAdrian Chadd if (ar8327_fetch_pdata_led(sc, &lcfg)) { 592b67ba111SAdrian Chadd if (lcfg.open_drain) 593b67ba111SAdrian Chadd new_pos |= AR8327_POWER_ON_STRIP_LED_OPEN_EN; 594b67ba111SAdrian Chadd else 595b67ba111SAdrian Chadd new_pos &= ~AR8327_POWER_ON_STRIP_LED_OPEN_EN; 596b67ba111SAdrian Chadd 597b67ba111SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_LED_CTRL0, 598b67ba111SAdrian Chadd lcfg.led_ctrl0); 599b67ba111SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_LED_CTRL1, 600b67ba111SAdrian Chadd lcfg.led_ctrl1); 601b67ba111SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_LED_CTRL2, 602b67ba111SAdrian Chadd lcfg.led_ctrl2); 603b67ba111SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_LED_CTRL3, 604b67ba111SAdrian Chadd lcfg.led_ctrl3); 605b67ba111SAdrian Chadd 606b67ba111SAdrian Chadd if (new_pos != pos) 607b67ba111SAdrian Chadd new_pos |= AR8327_POWER_ON_STRIP_POWER_ON_SEL; 608b67ba111SAdrian Chadd } 6099ab21e32SAdrian Chadd 610810bdeddSAdrian Chadd /* SGMII config */ 611810bdeddSAdrian Chadd bzero(&scfg, sizeof(scfg)); 612810bdeddSAdrian Chadd if (ar8327_fetch_pdata_sgmii(sc, &scfg)) { 61378549b94SAdrian Chadd device_printf(sc->sc_dev, "%s: SGMII cfg?\n", __func__); 614810bdeddSAdrian Chadd t = scfg.sgmii_ctrl; 615810bdeddSAdrian Chadd if (sc->chip_rev == 1) 616810bdeddSAdrian Chadd t |= AR8327_SGMII_CTRL_EN_PLL | 617810bdeddSAdrian Chadd AR8327_SGMII_CTRL_EN_RX | 618810bdeddSAdrian Chadd AR8327_SGMII_CTRL_EN_TX; 619810bdeddSAdrian Chadd else 620810bdeddSAdrian Chadd t &= ~(AR8327_SGMII_CTRL_EN_PLL | 621810bdeddSAdrian Chadd AR8327_SGMII_CTRL_EN_RX | 622810bdeddSAdrian Chadd AR8327_SGMII_CTRL_EN_TX); 623810bdeddSAdrian Chadd 624810bdeddSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_SGMII_CTRL, t); 625810bdeddSAdrian Chadd 626810bdeddSAdrian Chadd if (scfg.serdes_aen) 627810bdeddSAdrian Chadd new_pos &= ~AR8327_POWER_ON_STRIP_SERDES_AEN; 628810bdeddSAdrian Chadd else 629810bdeddSAdrian Chadd new_pos |= AR8327_POWER_ON_STRIP_SERDES_AEN; 630810bdeddSAdrian Chadd } 631810bdeddSAdrian Chadd 632810bdeddSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_POWER_ON_STRIP, new_pos); 6339ab21e32SAdrian Chadd 6349ab21e32SAdrian Chadd return (0); 6359ab21e32SAdrian Chadd } 6367330dd0bSAdrian Chadd 6377330dd0bSAdrian Chadd static int 6387330dd0bSAdrian Chadd ar8327_hw_setup(struct arswitch_softc *sc) 6397330dd0bSAdrian Chadd { 6407330dd0bSAdrian Chadd int i; 6417330dd0bSAdrian Chadd int err; 6427330dd0bSAdrian Chadd 6437330dd0bSAdrian Chadd /* pdata fetch and setup */ 6447330dd0bSAdrian Chadd err = ar8327_init_pdata(sc); 6457330dd0bSAdrian Chadd if (err != 0) 6467330dd0bSAdrian Chadd return (err); 6477330dd0bSAdrian Chadd 6487330dd0bSAdrian Chadd /* XXX init leds */ 6497330dd0bSAdrian Chadd 6507330dd0bSAdrian Chadd for (i = 0; i < AR8327_NUM_PHYS; i++) { 6517330dd0bSAdrian Chadd /* phy fixup */ 6527330dd0bSAdrian Chadd ar8327_phy_fixup(sc, i); 6537330dd0bSAdrian Chadd 6547330dd0bSAdrian Chadd /* start PHY autonegotiation? */ 6557330dd0bSAdrian Chadd /* XXX is this done as part of the normal PHY setup? */ 6567330dd0bSAdrian Chadd 6577330dd0bSAdrian Chadd }; 6587330dd0bSAdrian Chadd 6597330dd0bSAdrian Chadd /* Let things settle */ 6607330dd0bSAdrian Chadd DELAY(1000); 6617330dd0bSAdrian Chadd 6627330dd0bSAdrian Chadd return (0); 6637330dd0bSAdrian Chadd } 6647330dd0bSAdrian Chadd 6657330dd0bSAdrian Chadd /* 6667330dd0bSAdrian Chadd * Initialise other global values, for the AR8327. 6677330dd0bSAdrian Chadd */ 6687330dd0bSAdrian Chadd static int 6697330dd0bSAdrian Chadd ar8327_hw_global_setup(struct arswitch_softc *sc) 6707330dd0bSAdrian Chadd { 6717330dd0bSAdrian Chadd uint32_t t; 6727330dd0bSAdrian Chadd 6737330dd0bSAdrian Chadd /* enable CPU port and disable mirror port */ 6747330dd0bSAdrian Chadd t = AR8327_FWD_CTRL0_CPU_PORT_EN | 6757330dd0bSAdrian Chadd AR8327_FWD_CTRL0_MIRROR_PORT; 6767330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_FWD_CTRL0, t); 6777330dd0bSAdrian Chadd 6787330dd0bSAdrian Chadd /* forward multicast and broadcast frames to CPU */ 6797330dd0bSAdrian Chadd t = (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_UC_FLOOD_S) | 6807330dd0bSAdrian Chadd (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_MC_FLOOD_S) | 6817330dd0bSAdrian Chadd (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_BC_FLOOD_S); 6827330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_FWD_CTRL1, t); 6837330dd0bSAdrian Chadd 6847330dd0bSAdrian Chadd /* enable jumbo frames */ 6857330dd0bSAdrian Chadd /* XXX need to macro-shift the value! */ 6867330dd0bSAdrian Chadd arswitch_modifyreg(sc->sc_dev, AR8327_REG_MAX_FRAME_SIZE, 6877330dd0bSAdrian Chadd AR8327_MAX_FRAME_SIZE_MTU, 9018 + 8 + 2); 6887330dd0bSAdrian Chadd 6897330dd0bSAdrian Chadd /* Enable MIB counters */ 6907330dd0bSAdrian Chadd arswitch_modifyreg(sc->sc_dev, AR8327_REG_MODULE_EN, 6917330dd0bSAdrian Chadd AR8327_MODULE_EN_MIB, AR8327_MODULE_EN_MIB); 6927330dd0bSAdrian Chadd 693db37238fSAdrian Chadd /* Disable EEE on all ports due to stability issues */ 694db37238fSAdrian Chadd t = arswitch_readreg(sc->sc_dev, AR8327_REG_EEE_CTRL); 695db37238fSAdrian Chadd t |= AR8327_EEE_CTRL_DISABLE_PHY(0) | 696db37238fSAdrian Chadd AR8327_EEE_CTRL_DISABLE_PHY(1) | 697db37238fSAdrian Chadd AR8327_EEE_CTRL_DISABLE_PHY(2) | 698db37238fSAdrian Chadd AR8327_EEE_CTRL_DISABLE_PHY(3) | 699db37238fSAdrian Chadd AR8327_EEE_CTRL_DISABLE_PHY(4); 700db37238fSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_EEE_CTRL, t); 701db37238fSAdrian Chadd 7024ff2f60dSAdrian Chadd /* Set the right number of ports */ 703749cac13SAdrian Chadd /* GMAC0 (CPU), GMAC1..5 (PHYs), GMAC6 (CPU) */ 704749cac13SAdrian Chadd sc->info.es_nports = 7; 7054ff2f60dSAdrian Chadd 7067330dd0bSAdrian Chadd return (0); 7077330dd0bSAdrian Chadd } 7087330dd0bSAdrian Chadd 7097330dd0bSAdrian Chadd /* 71078549b94SAdrian Chadd * Port setup. Called at attach time. 7117330dd0bSAdrian Chadd */ 7127330dd0bSAdrian Chadd static void 7137330dd0bSAdrian Chadd ar8327_port_init(struct arswitch_softc *sc, int port) 7147330dd0bSAdrian Chadd { 7157330dd0bSAdrian Chadd uint32_t t; 71678549b94SAdrian Chadd int ports; 71778549b94SAdrian Chadd 71878549b94SAdrian Chadd /* For now, port can see all other ports */ 71978549b94SAdrian Chadd ports = 0x7f; 7207330dd0bSAdrian Chadd 7219ab21e32SAdrian Chadd if (port == AR8X16_PORT_CPU) 7229ab21e32SAdrian Chadd t = sc->ar8327.port0_status; 7237330dd0bSAdrian Chadd else if (port == 6) 7249ab21e32SAdrian Chadd t = sc->ar8327.port6_status; 7257330dd0bSAdrian Chadd else 7267330dd0bSAdrian Chadd t = AR8X16_PORT_STS_LINK_AUTO; 7277330dd0bSAdrian Chadd 7287330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_STATUS(port), t); 7297330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_HEADER(port), 0); 7307330dd0bSAdrian Chadd 7310d2041a0SAdrian Chadd /* 7320d2041a0SAdrian Chadd * Default to 1 port group. 7330d2041a0SAdrian Chadd */ 7347330dd0bSAdrian Chadd t = 1 << AR8327_PORT_VLAN0_DEF_SVID_S; 7357330dd0bSAdrian Chadd t |= 1 << AR8327_PORT_VLAN0_DEF_CVID_S; 7367330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_VLAN0(port), t); 7377330dd0bSAdrian Chadd 7387330dd0bSAdrian Chadd t = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH << AR8327_PORT_VLAN1_OUT_MODE_S; 7397330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_VLAN1(port), t); 7407330dd0bSAdrian Chadd 74103b5d827SAdrian Chadd /* 74203b5d827SAdrian Chadd * This doesn't configure any ports which this port can "see". 74303b5d827SAdrian Chadd * bits 0-6 control which ports a frame coming into this port 74403b5d827SAdrian Chadd * can be sent out to. 74503b5d827SAdrian Chadd * 74603b5d827SAdrian Chadd * So by doing this, we're making it impossible to send frames out 74703b5d827SAdrian Chadd * to that port. 74803b5d827SAdrian Chadd */ 7497330dd0bSAdrian Chadd t = AR8327_PORT_LOOKUP_LEARN; 7507330dd0bSAdrian Chadd t |= AR8X16_PORT_CTRL_STATE_FORWARD << AR8327_PORT_LOOKUP_STATE_S; 75103b5d827SAdrian Chadd 75203b5d827SAdrian Chadd /* So this allows traffic to any port except ourselves */ 75378549b94SAdrian Chadd t |= (ports & ~(1 << port)); 7547330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_LOOKUP(port), t); 7557330dd0bSAdrian Chadd } 7567330dd0bSAdrian Chadd 7577330dd0bSAdrian Chadd static int 7587330dd0bSAdrian Chadd ar8327_port_vlan_setup(struct arswitch_softc *sc, etherswitch_port_t *p) 7597330dd0bSAdrian Chadd { 7607330dd0bSAdrian Chadd 761749cac13SAdrian Chadd /* Check: ADDTAG/STRIPTAG - exclusive */ 762749cac13SAdrian Chadd 763749cac13SAdrian Chadd ARSWITCH_LOCK(sc); 764749cac13SAdrian Chadd 765749cac13SAdrian Chadd /* Set the PVID. */ 766749cac13SAdrian Chadd if (p->es_pvid != 0) 767749cac13SAdrian Chadd sc->hal.arswitch_vlan_set_pvid(sc, p->es_port, p->es_pvid); 768749cac13SAdrian Chadd 769749cac13SAdrian Chadd /* 770749cac13SAdrian Chadd * DOUBLE_TAG 771749cac13SAdrian Chadd * VLAN_MODE_ADD 772749cac13SAdrian Chadd * VLAN_MODE_STRIP 773749cac13SAdrian Chadd */ 774749cac13SAdrian Chadd ARSWITCH_UNLOCK(sc); 7757330dd0bSAdrian Chadd return (0); 7767330dd0bSAdrian Chadd } 7777330dd0bSAdrian Chadd 77878549b94SAdrian Chadd /* 77978549b94SAdrian Chadd * Get the port VLAN configuration. 78078549b94SAdrian Chadd */ 7817330dd0bSAdrian Chadd static int 7827330dd0bSAdrian Chadd ar8327_port_vlan_get(struct arswitch_softc *sc, etherswitch_port_t *p) 7837330dd0bSAdrian Chadd { 784749cac13SAdrian Chadd 785749cac13SAdrian Chadd ARSWITCH_LOCK(sc); 786749cac13SAdrian Chadd 787749cac13SAdrian Chadd /* Retrieve the PVID */ 788749cac13SAdrian Chadd sc->hal.arswitch_vlan_get_pvid(sc, p->es_port, &p->es_pvid); 789749cac13SAdrian Chadd 790*036e1c76SAdrian Chadd /* Retrieve the current port configuration from the VTU */ 791749cac13SAdrian Chadd /* 792749cac13SAdrian Chadd * DOUBLE_TAG 793749cac13SAdrian Chadd * VLAN_MODE_ADD 794749cac13SAdrian Chadd * VLAN_MODE_STRIP 795749cac13SAdrian Chadd */ 796749cac13SAdrian Chadd 797749cac13SAdrian Chadd ARSWITCH_UNLOCK(sc); 7987330dd0bSAdrian Chadd return (0); 7997330dd0bSAdrian Chadd } 8007330dd0bSAdrian Chadd 8017330dd0bSAdrian Chadd static void 802*036e1c76SAdrian Chadd ar8327_port_disable_mirror(struct arswitch_softc *sc, int port) 803*036e1c76SAdrian Chadd { 804*036e1c76SAdrian Chadd 805*036e1c76SAdrian Chadd arswitch_modifyreg(sc->sc_dev, 806*036e1c76SAdrian Chadd AR8327_REG_PORT_LOOKUP(port), 807*036e1c76SAdrian Chadd AR8327_PORT_LOOKUP_ING_MIRROR_EN, 808*036e1c76SAdrian Chadd 0); 809*036e1c76SAdrian Chadd arswitch_modifyreg(sc->sc_dev, 810*036e1c76SAdrian Chadd AR8327_REG_PORT_HOL_CTRL1(port), 811*036e1c76SAdrian Chadd AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN, 812*036e1c76SAdrian Chadd 0); 813*036e1c76SAdrian Chadd } 814*036e1c76SAdrian Chadd 815*036e1c76SAdrian Chadd static void 8167330dd0bSAdrian Chadd ar8327_reset_vlans(struct arswitch_softc *sc) 8177330dd0bSAdrian Chadd { 8187330dd0bSAdrian Chadd int i; 819*036e1c76SAdrian Chadd uint32_t t; 82078549b94SAdrian Chadd int ports; 82178549b94SAdrian Chadd 82278549b94SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED); 82378549b94SAdrian Chadd ARSWITCH_LOCK(sc); 82478549b94SAdrian Chadd 82578549b94SAdrian Chadd /* Clear the existing VLAN configuration */ 82678549b94SAdrian Chadd memset(sc->vid, 0, sizeof(sc->vid)); 8277330dd0bSAdrian Chadd 8287330dd0bSAdrian Chadd /* 82903b5d827SAdrian Chadd * Disable mirroring. 83003b5d827SAdrian Chadd */ 83103b5d827SAdrian Chadd arswitch_modifyreg(sc->sc_dev, AR8327_REG_FWD_CTRL0, 83203b5d827SAdrian Chadd AR8327_FWD_CTRL0_MIRROR_PORT, 83303b5d827SAdrian Chadd (0xF << AR8327_FWD_CTRL0_MIRROR_PORT_S)); 83403b5d827SAdrian Chadd 83503b5d827SAdrian Chadd /* 83678549b94SAdrian Chadd * XXX TODO: disable any Q-in-Q port configuration, 83778549b94SAdrian Chadd * tagging, egress filters, etc. 8387330dd0bSAdrian Chadd */ 83978549b94SAdrian Chadd 84078549b94SAdrian Chadd /* 84178549b94SAdrian Chadd * For now, let's default to one portgroup, just so traffic 84278549b94SAdrian Chadd * flows. All ports can see other ports. There are two CPU GMACs 84378549b94SAdrian Chadd * (GMAC0, GMAC6), GMAC1..GMAC5 are external PHYs. 84478549b94SAdrian Chadd * 84578549b94SAdrian Chadd * (ETHERSWITCH_VLAN_PORT) 84678549b94SAdrian Chadd */ 84778549b94SAdrian Chadd ports = 0x7f; 84878549b94SAdrian Chadd 849*036e1c76SAdrian Chadd /* 850*036e1c76SAdrian Chadd * XXX TODO: set things up correctly for vlans! 851*036e1c76SAdrian Chadd */ 8527330dd0bSAdrian Chadd for (i = 0; i < AR8327_NUM_PORTS; i++) { 853*036e1c76SAdrian Chadd int egress, ingress; 85478549b94SAdrian Chadd 855*036e1c76SAdrian Chadd if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) { 856749cac13SAdrian Chadd sc->vid[i] = i | ETHERSWITCH_VID_VALID; 857*036e1c76SAdrian Chadd /* set egress == out_keep */ 858*036e1c76SAdrian Chadd ingress = AR8X16_PORT_VLAN_MODE_PORT_ONLY; 859*036e1c76SAdrian Chadd /* in_port_only, forward */ 860*036e1c76SAdrian Chadd egress = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH; 861*036e1c76SAdrian Chadd } else if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) { 862*036e1c76SAdrian Chadd ingress = AR8X16_PORT_VLAN_MODE_SECURE; 863*036e1c76SAdrian Chadd egress = AR8327_PORT_VLAN1_OUT_MODE_UNMOD; 864*036e1c76SAdrian Chadd } else { 865*036e1c76SAdrian Chadd /* set egress == out_keep */ 866*036e1c76SAdrian Chadd ingress = AR8X16_PORT_VLAN_MODE_PORT_ONLY; 867*036e1c76SAdrian Chadd /* in_port_only, forward */ 868*036e1c76SAdrian Chadd egress = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH; 869*036e1c76SAdrian Chadd } 870749cac13SAdrian Chadd 871749cac13SAdrian Chadd /* set pvid = 1; there's only one vlangroup to start with */ 872dd846bddSAdrian Chadd t = 1 << AR8327_PORT_VLAN0_DEF_SVID_S; 873dd846bddSAdrian Chadd t |= 1 << AR8327_PORT_VLAN0_DEF_CVID_S; 8747330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_VLAN0(i), t); 8757330dd0bSAdrian Chadd 8767330dd0bSAdrian Chadd t = AR8327_PORT_VLAN1_PORT_VLAN_PROP; 877*036e1c76SAdrian Chadd t |= egress << AR8327_PORT_VLAN1_OUT_MODE_S; 8787330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_VLAN1(i), t); 8797330dd0bSAdrian Chadd 880dd846bddSAdrian Chadd /* Ports can see other ports */ 881*036e1c76SAdrian Chadd /* XXX not entirely true for dot1q? */ 88278549b94SAdrian Chadd t = (ports & ~(1 << i)); /* all ports besides us */ 8837330dd0bSAdrian Chadd t |= AR8327_PORT_LOOKUP_LEARN; 8847330dd0bSAdrian Chadd 885*036e1c76SAdrian Chadd t |= ingress << AR8327_PORT_LOOKUP_IN_MODE_S; 8867330dd0bSAdrian Chadd t |= AR8X16_PORT_CTRL_STATE_FORWARD << AR8327_PORT_LOOKUP_STATE_S; 8877330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_LOOKUP(i), t); 888*036e1c76SAdrian Chadd } 88903b5d827SAdrian Chadd 89003b5d827SAdrian Chadd /* 89103b5d827SAdrian Chadd * Disable port mirroring entirely. 89203b5d827SAdrian Chadd */ 893*036e1c76SAdrian Chadd for (i = 0; i < AR8327_NUM_PORTS; i++) { 894*036e1c76SAdrian Chadd ar8327_port_disable_mirror(sc, i); 895*036e1c76SAdrian Chadd } 896*036e1c76SAdrian Chadd 897*036e1c76SAdrian Chadd /* 898*036e1c76SAdrian Chadd * If dot1q - set pvid; dot1q, etc. 899*036e1c76SAdrian Chadd */ 900*036e1c76SAdrian Chadd sc->vid[0] = 1; 901*036e1c76SAdrian Chadd if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) { 902*036e1c76SAdrian Chadd for (i = 0; i < AR8327_NUM_PORTS; i++) { 903*036e1c76SAdrian Chadd /* Each port - pvid 1 */ 904*036e1c76SAdrian Chadd sc->hal.arswitch_vlan_set_pvid(sc, i, sc->vid[0]); 905*036e1c76SAdrian Chadd } 906*036e1c76SAdrian Chadd /* Initialise vlan1 - all ports, untagged */ 907*036e1c76SAdrian Chadd sc->hal.arswitch_set_dot1q_vlan(sc, ports, ports, sc->vid[0]); 908*036e1c76SAdrian Chadd sc->vid[0] |= ETHERSWITCH_VID_VALID; 9097330dd0bSAdrian Chadd } 91078549b94SAdrian Chadd 91178549b94SAdrian Chadd ARSWITCH_UNLOCK(sc); 9127330dd0bSAdrian Chadd } 9137330dd0bSAdrian Chadd 9147330dd0bSAdrian Chadd static int 915749cac13SAdrian Chadd ar8327_vlan_get_port(struct arswitch_softc *sc, uint32_t *ports, int vid) 916749cac13SAdrian Chadd { 917749cac13SAdrian Chadd int port; 918749cac13SAdrian Chadd uint32_t reg; 919749cac13SAdrian Chadd 920749cac13SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 921749cac13SAdrian Chadd 922749cac13SAdrian Chadd /* For port based vlans the vlanid is the same as the port index. */ 923749cac13SAdrian Chadd port = vid & ETHERSWITCH_VID_MASK; 924749cac13SAdrian Chadd reg = arswitch_readreg(sc->sc_dev, AR8327_REG_PORT_LOOKUP(port)); 925749cac13SAdrian Chadd *ports = reg & 0x7f; 926749cac13SAdrian Chadd return (0); 927749cac13SAdrian Chadd } 928749cac13SAdrian Chadd 929749cac13SAdrian Chadd static int 930749cac13SAdrian Chadd ar8327_vlan_set_port(struct arswitch_softc *sc, uint32_t ports, int vid) 931749cac13SAdrian Chadd { 932749cac13SAdrian Chadd int err, port; 933749cac13SAdrian Chadd 934749cac13SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 935749cac13SAdrian Chadd 936749cac13SAdrian Chadd /* For port based vlans the vlanid is the same as the port index. */ 937749cac13SAdrian Chadd port = vid & ETHERSWITCH_VID_MASK; 938749cac13SAdrian Chadd 939749cac13SAdrian Chadd err = arswitch_modifyreg(sc->sc_dev, AR8327_REG_PORT_LOOKUP(port), 940749cac13SAdrian Chadd 0x7f, /* vlan membership mask */ 941749cac13SAdrian Chadd (ports & 0x7f)); 942749cac13SAdrian Chadd 943749cac13SAdrian Chadd if (err) 944749cac13SAdrian Chadd return (err); 945749cac13SAdrian Chadd return (0); 946749cac13SAdrian Chadd } 947749cac13SAdrian Chadd 948749cac13SAdrian Chadd static int 9497330dd0bSAdrian Chadd ar8327_vlan_getvgroup(struct arswitch_softc *sc, etherswitch_vlangroup_t *vg) 9507330dd0bSAdrian Chadd { 95178549b94SAdrian Chadd 95278549b94SAdrian Chadd return (ar8xxx_getvgroup(sc, vg)); 9537330dd0bSAdrian Chadd } 9547330dd0bSAdrian Chadd 9557330dd0bSAdrian Chadd static int 9567330dd0bSAdrian Chadd ar8327_vlan_setvgroup(struct arswitch_softc *sc, etherswitch_vlangroup_t *vg) 9577330dd0bSAdrian Chadd { 9587330dd0bSAdrian Chadd 95978549b94SAdrian Chadd return (ar8xxx_setvgroup(sc, vg)); 9607330dd0bSAdrian Chadd } 9617330dd0bSAdrian Chadd 9627330dd0bSAdrian Chadd static int 9637330dd0bSAdrian Chadd ar8327_get_pvid(struct arswitch_softc *sc, int port, int *pvid) 9647330dd0bSAdrian Chadd { 965749cac13SAdrian Chadd uint32_t reg; 9667330dd0bSAdrian Chadd 967749cac13SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 968749cac13SAdrian Chadd 969749cac13SAdrian Chadd /* 970749cac13SAdrian Chadd * XXX for now, assuming it's CVID; likely very wrong! 971749cac13SAdrian Chadd */ 972749cac13SAdrian Chadd port = port & ETHERSWITCH_VID_MASK; 973749cac13SAdrian Chadd reg = arswitch_readreg(sc->sc_dev, AR8327_REG_PORT_VLAN0(port)); 974749cac13SAdrian Chadd reg = reg >> AR8327_PORT_VLAN0_DEF_CVID_S; 975749cac13SAdrian Chadd reg = reg & 0xfff; 976749cac13SAdrian Chadd 977749cac13SAdrian Chadd *pvid = reg; 9787330dd0bSAdrian Chadd return (0); 9797330dd0bSAdrian Chadd } 9807330dd0bSAdrian Chadd 9817330dd0bSAdrian Chadd static int 9827330dd0bSAdrian Chadd ar8327_set_pvid(struct arswitch_softc *sc, int port, int pvid) 9837330dd0bSAdrian Chadd { 984749cac13SAdrian Chadd uint32_t t; 9857330dd0bSAdrian Chadd 986749cac13SAdrian Chadd /* Limit pvid to valid values */ 987749cac13SAdrian Chadd pvid &= 0x7f; 988749cac13SAdrian Chadd 989749cac13SAdrian Chadd t = pvid << AR8327_PORT_VLAN0_DEF_SVID_S; 990749cac13SAdrian Chadd t |= pvid << AR8327_PORT_VLAN0_DEF_CVID_S; 991749cac13SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_VLAN0(port), t); 992749cac13SAdrian Chadd 9937330dd0bSAdrian Chadd return (0); 9947330dd0bSAdrian Chadd } 9957330dd0bSAdrian Chadd 9964ff2f60dSAdrian Chadd static int 9974ff2f60dSAdrian Chadd ar8327_atu_flush(struct arswitch_softc *sc) 9984ff2f60dSAdrian Chadd { 9994ff2f60dSAdrian Chadd 10004ff2f60dSAdrian Chadd int ret; 10014ff2f60dSAdrian Chadd 10024ff2f60dSAdrian Chadd ret = arswitch_waitreg(sc->sc_dev, 10034ff2f60dSAdrian Chadd AR8327_REG_ATU_FUNC, 10044ff2f60dSAdrian Chadd AR8327_ATU_FUNC_BUSY, 10054ff2f60dSAdrian Chadd 0, 10064ff2f60dSAdrian Chadd 1000); 10074ff2f60dSAdrian Chadd 10084ff2f60dSAdrian Chadd if (ret) 10094ff2f60dSAdrian Chadd device_printf(sc->sc_dev, "%s: waitreg failed\n", __func__); 10104ff2f60dSAdrian Chadd 10114ff2f60dSAdrian Chadd if (!ret) 10124ff2f60dSAdrian Chadd arswitch_writereg(sc->sc_dev, 10134ff2f60dSAdrian Chadd AR8327_REG_ATU_FUNC, 10144ff2f60dSAdrian Chadd AR8327_ATU_FUNC_OP_FLUSH); 10154ff2f60dSAdrian Chadd return (ret); 10164ff2f60dSAdrian Chadd } 10174ff2f60dSAdrian Chadd 1018*036e1c76SAdrian Chadd static int 1019*036e1c76SAdrian Chadd ar8327_flush_dot1q_vlan(struct arswitch_softc *sc) 1020*036e1c76SAdrian Chadd { 1021*036e1c76SAdrian Chadd 1022*036e1c76SAdrian Chadd return (ar8327_vlan_op(sc, AR8327_VTU_FUNC1_OP_FLUSH, 0, 0)); 1023*036e1c76SAdrian Chadd } 1024*036e1c76SAdrian Chadd 1025*036e1c76SAdrian Chadd static int 1026*036e1c76SAdrian Chadd ar8327_purge_dot1q_vlan(struct arswitch_softc *sc, int vid) 1027*036e1c76SAdrian Chadd { 1028*036e1c76SAdrian Chadd 1029*036e1c76SAdrian Chadd return (ar8327_vlan_op(sc, AR8327_VTU_FUNC1_OP_PURGE, vid, 0)); 1030*036e1c76SAdrian Chadd } 1031*036e1c76SAdrian Chadd 1032*036e1c76SAdrian Chadd static int 1033*036e1c76SAdrian Chadd ar8327_get_dot1q_vlan(struct arswitch_softc *sc, uint32_t *ports, 1034*036e1c76SAdrian Chadd uint32_t *untagged_ports, int vid) 1035*036e1c76SAdrian Chadd { 1036*036e1c76SAdrian Chadd int i, r; 1037*036e1c76SAdrian Chadd uint32_t op, reg, val; 1038*036e1c76SAdrian Chadd 1039*036e1c76SAdrian Chadd op = AR8327_VTU_FUNC1_OP_GET_ONE; 1040*036e1c76SAdrian Chadd 1041*036e1c76SAdrian Chadd /* Filter out the vid flags; only grab the VLAN ID */ 1042*036e1c76SAdrian Chadd vid &= 0xfff; 1043*036e1c76SAdrian Chadd 1044*036e1c76SAdrian Chadd /* XXX TODO: the VTU here stores egress mode - keep, tag, untagged, none */ 1045*036e1c76SAdrian Chadd r = ar8327_vlan_op(sc, op, vid, 0); 1046*036e1c76SAdrian Chadd if (r != 0) { 1047*036e1c76SAdrian Chadd device_printf(sc->sc_dev, "%s: %d: op failed\n", __func__, vid); 1048*036e1c76SAdrian Chadd } 1049*036e1c76SAdrian Chadd 1050*036e1c76SAdrian Chadd reg = arswitch_readreg(sc->sc_dev, AR8327_REG_VTU_FUNC0); 1051*036e1c76SAdrian Chadd DPRINTF(sc->sc_dev, "%s: %d: reg=0x%08x\n", __func__, vid, reg); 1052*036e1c76SAdrian Chadd 1053*036e1c76SAdrian Chadd /* 1054*036e1c76SAdrian Chadd * If any of the bits are set, update the port mask. 1055*036e1c76SAdrian Chadd * Worry about the port config itself when getport() is called. 1056*036e1c76SAdrian Chadd */ 1057*036e1c76SAdrian Chadd *ports = 0; 1058*036e1c76SAdrian Chadd for (i = 0; i < AR8327_NUM_PORTS; i++) { 1059*036e1c76SAdrian Chadd val = reg >> AR8327_VTU_FUNC0_EG_MODE_S(i); 1060*036e1c76SAdrian Chadd val = val & 0x3; 1061*036e1c76SAdrian Chadd /* XXX KEEP (unmodified?) */ 1062*036e1c76SAdrian Chadd if (val == AR8327_VTU_FUNC0_EG_MODE_TAG) { 1063*036e1c76SAdrian Chadd *ports |= (1 << i); 1064*036e1c76SAdrian Chadd } else if (val == AR8327_VTU_FUNC0_EG_MODE_UNTAG) { 1065*036e1c76SAdrian Chadd *ports |= (1 << i); 1066*036e1c76SAdrian Chadd *untagged_ports |= (1 << i); 1067*036e1c76SAdrian Chadd } 1068*036e1c76SAdrian Chadd } 1069*036e1c76SAdrian Chadd 1070*036e1c76SAdrian Chadd return (0); 1071*036e1c76SAdrian Chadd } 1072*036e1c76SAdrian Chadd 1073*036e1c76SAdrian Chadd static int 1074*036e1c76SAdrian Chadd ar8327_set_dot1q_vlan(struct arswitch_softc *sc, uint32_t ports, 1075*036e1c76SAdrian Chadd uint32_t untagged_ports, int vid) 1076*036e1c76SAdrian Chadd { 1077*036e1c76SAdrian Chadd int i; 1078*036e1c76SAdrian Chadd uint32_t op, val, mode; 1079*036e1c76SAdrian Chadd 1080*036e1c76SAdrian Chadd op = AR8327_VTU_FUNC1_OP_LOAD; 1081*036e1c76SAdrian Chadd vid &= 0xfff; 1082*036e1c76SAdrian Chadd 1083*036e1c76SAdrian Chadd DPRINTF(sc->sc_dev, 1084*036e1c76SAdrian Chadd "%s: vid: %d, ports=0x%08x, untagged_ports=0x%08x\n", 1085*036e1c76SAdrian Chadd __func__, 1086*036e1c76SAdrian Chadd vid, 1087*036e1c76SAdrian Chadd ports, 1088*036e1c76SAdrian Chadd untagged_ports); 1089*036e1c76SAdrian Chadd 1090*036e1c76SAdrian Chadd /* 1091*036e1c76SAdrian Chadd * Mark it as valid; and that it should use per-VLAN MAC table, 1092*036e1c76SAdrian Chadd * not VID=0 when doing MAC lookups 1093*036e1c76SAdrian Chadd */ 1094*036e1c76SAdrian Chadd val = AR8327_VTU_FUNC0_VALID | AR8327_VTU_FUNC0_IVL; 1095*036e1c76SAdrian Chadd 1096*036e1c76SAdrian Chadd for (i = 0; i < AR8327_NUM_PORTS; i++) { 1097*036e1c76SAdrian Chadd if ((ports & BIT(i)) == 0) 1098*036e1c76SAdrian Chadd mode = AR8327_VTU_FUNC0_EG_MODE_NOT; 1099*036e1c76SAdrian Chadd else if (untagged_ports & BIT(i)) 1100*036e1c76SAdrian Chadd mode = AR8327_VTU_FUNC0_EG_MODE_UNTAG; 1101*036e1c76SAdrian Chadd else 1102*036e1c76SAdrian Chadd mode = AR8327_VTU_FUNC0_EG_MODE_TAG; 1103*036e1c76SAdrian Chadd 1104*036e1c76SAdrian Chadd val |= mode << AR8327_VTU_FUNC0_EG_MODE_S(i); 1105*036e1c76SAdrian Chadd } 1106*036e1c76SAdrian Chadd 1107*036e1c76SAdrian Chadd return (ar8327_vlan_op(sc, op, vid, val)); 1108*036e1c76SAdrian Chadd } 1109*036e1c76SAdrian Chadd 11107330dd0bSAdrian Chadd void 11117330dd0bSAdrian Chadd ar8327_attach(struct arswitch_softc *sc) 11127330dd0bSAdrian Chadd { 11137330dd0bSAdrian Chadd 11147330dd0bSAdrian Chadd sc->hal.arswitch_hw_setup = ar8327_hw_setup; 11157330dd0bSAdrian Chadd sc->hal.arswitch_hw_global_setup = ar8327_hw_global_setup; 11167330dd0bSAdrian Chadd 11177330dd0bSAdrian Chadd sc->hal.arswitch_port_init = ar8327_port_init; 111878549b94SAdrian Chadd 111978549b94SAdrian Chadd sc->hal.arswitch_vlan_getvgroup = ar8327_vlan_getvgroup; 112078549b94SAdrian Chadd sc->hal.arswitch_vlan_setvgroup = ar8327_vlan_setvgroup; 11217330dd0bSAdrian Chadd sc->hal.arswitch_port_vlan_setup = ar8327_port_vlan_setup; 11227330dd0bSAdrian Chadd sc->hal.arswitch_port_vlan_get = ar8327_port_vlan_get; 1123*036e1c76SAdrian Chadd sc->hal.arswitch_flush_dot1q_vlan = ar8327_flush_dot1q_vlan; 1124*036e1c76SAdrian Chadd sc->hal.arswitch_purge_dot1q_vlan = ar8327_purge_dot1q_vlan; 1125*036e1c76SAdrian Chadd sc->hal.arswitch_set_dot1q_vlan = ar8327_set_dot1q_vlan; 1126*036e1c76SAdrian Chadd sc->hal.arswitch_get_dot1q_vlan = ar8327_get_dot1q_vlan; 11277330dd0bSAdrian Chadd 11287330dd0bSAdrian Chadd sc->hal.arswitch_vlan_init_hw = ar8327_reset_vlans; 11297330dd0bSAdrian Chadd sc->hal.arswitch_vlan_get_pvid = ar8327_get_pvid; 11307330dd0bSAdrian Chadd sc->hal.arswitch_vlan_set_pvid = ar8327_set_pvid; 11317330dd0bSAdrian Chadd 1132749cac13SAdrian Chadd sc->hal.arswitch_get_port_vlan = ar8327_vlan_get_port; 1133749cac13SAdrian Chadd sc->hal.arswitch_set_port_vlan = ar8327_vlan_set_port; 1134749cac13SAdrian Chadd 11354ff2f60dSAdrian Chadd sc->hal.arswitch_atu_flush = ar8327_atu_flush; 11364ff2f60dSAdrian Chadd 113778549b94SAdrian Chadd /* 113878549b94SAdrian Chadd * Reading the PHY via the MDIO interface currently doesn't 113978549b94SAdrian Chadd * work correctly. 114078549b94SAdrian Chadd * 114178549b94SAdrian Chadd * So for now, just go direct to the PHY registers themselves. 114278549b94SAdrian Chadd * This has always worked on external devices, but not internal 114378549b94SAdrian Chadd * devices (AR934x, AR724x, AR933x.) 114478549b94SAdrian Chadd */ 114578549b94SAdrian Chadd sc->hal.arswitch_phy_read = arswitch_readphy_external; 114678549b94SAdrian Chadd sc->hal.arswitch_phy_write = arswitch_writephy_external; 114778549b94SAdrian Chadd 11487330dd0bSAdrian Chadd /* Set the switch vlan capabilities. */ 11497330dd0bSAdrian Chadd sc->info.es_vlan_caps = ETHERSWITCH_VLAN_DOT1Q | 11507330dd0bSAdrian Chadd ETHERSWITCH_VLAN_PORT | ETHERSWITCH_VLAN_DOUBLE_TAG; 11517330dd0bSAdrian Chadd sc->info.es_nvlangroups = AR8X16_MAX_VLANS; 11527330dd0bSAdrian Chadd } 1153