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 697330dd0bSAdrian Chadd static void 707330dd0bSAdrian Chadd ar8327_phy_fixup(struct arswitch_softc *sc, int phy) 717330dd0bSAdrian Chadd { 72db37238fSAdrian Chadd if (bootverbose) 73db37238fSAdrian Chadd device_printf(sc->sc_dev, 74db37238fSAdrian Chadd "%s: called; phy=%d; chiprev=%d\n", __func__, 75db37238fSAdrian Chadd phy, 76db37238fSAdrian Chadd sc->chip_rev); 777330dd0bSAdrian Chadd switch (sc->chip_rev) { 787330dd0bSAdrian Chadd case 1: 797330dd0bSAdrian Chadd /* For 100M waveform */ 807330dd0bSAdrian Chadd arswitch_writedbg(sc->sc_dev, phy, 0, 0x02ea); 817330dd0bSAdrian Chadd /* Turn on Gigabit clock */ 827330dd0bSAdrian Chadd arswitch_writedbg(sc->sc_dev, phy, 0x3d, 0x68a0); 837330dd0bSAdrian Chadd break; 847330dd0bSAdrian Chadd 857330dd0bSAdrian Chadd case 2: 867330dd0bSAdrian Chadd arswitch_writemmd(sc->sc_dev, phy, 0x7, 0x3c); 877330dd0bSAdrian Chadd arswitch_writemmd(sc->sc_dev, phy, 0x4007, 0x0); 887330dd0bSAdrian Chadd /* fallthrough */ 897330dd0bSAdrian Chadd case 4: 907330dd0bSAdrian Chadd arswitch_writemmd(sc->sc_dev, phy, 0x3, 0x800d); 917330dd0bSAdrian Chadd arswitch_writemmd(sc->sc_dev, phy, 0x4003, 0x803f); 927330dd0bSAdrian Chadd 937330dd0bSAdrian Chadd arswitch_writedbg(sc->sc_dev, phy, 0x3d, 0x6860); 947330dd0bSAdrian Chadd arswitch_writedbg(sc->sc_dev, phy, 0x5, 0x2c46); 957330dd0bSAdrian Chadd arswitch_writedbg(sc->sc_dev, phy, 0x3c, 0x6000); 967330dd0bSAdrian Chadd break; 977330dd0bSAdrian Chadd } 987330dd0bSAdrian Chadd } 997330dd0bSAdrian Chadd 1007330dd0bSAdrian Chadd static uint32_t 1017330dd0bSAdrian Chadd ar8327_get_pad_cfg(struct ar8327_pad_cfg *cfg) 1027330dd0bSAdrian Chadd { 1037330dd0bSAdrian Chadd uint32_t t; 1047330dd0bSAdrian Chadd 1057330dd0bSAdrian Chadd if (!cfg) 1067330dd0bSAdrian Chadd return (0); 1077330dd0bSAdrian Chadd 1087330dd0bSAdrian Chadd t = 0; 1097330dd0bSAdrian Chadd switch (cfg->mode) { 1107330dd0bSAdrian Chadd case AR8327_PAD_NC: 1117330dd0bSAdrian Chadd break; 1127330dd0bSAdrian Chadd 1137330dd0bSAdrian Chadd case AR8327_PAD_MAC2MAC_MII: 1147330dd0bSAdrian Chadd t = AR8327_PAD_MAC_MII_EN; 1157330dd0bSAdrian Chadd if (cfg->rxclk_sel) 1167330dd0bSAdrian Chadd t |= AR8327_PAD_MAC_MII_RXCLK_SEL; 1177330dd0bSAdrian Chadd if (cfg->txclk_sel) 1187330dd0bSAdrian Chadd t |= AR8327_PAD_MAC_MII_TXCLK_SEL; 1197330dd0bSAdrian Chadd break; 1207330dd0bSAdrian Chadd 1217330dd0bSAdrian Chadd case AR8327_PAD_MAC2MAC_GMII: 1227330dd0bSAdrian Chadd t = AR8327_PAD_MAC_GMII_EN; 1237330dd0bSAdrian Chadd if (cfg->rxclk_sel) 1247330dd0bSAdrian Chadd t |= AR8327_PAD_MAC_GMII_RXCLK_SEL; 1257330dd0bSAdrian Chadd if (cfg->txclk_sel) 1267330dd0bSAdrian Chadd t |= AR8327_PAD_MAC_GMII_TXCLK_SEL; 1277330dd0bSAdrian Chadd break; 1287330dd0bSAdrian Chadd 1297330dd0bSAdrian Chadd case AR8327_PAD_MAC_SGMII: 1307330dd0bSAdrian Chadd t = AR8327_PAD_SGMII_EN; 1317330dd0bSAdrian Chadd 1327330dd0bSAdrian Chadd /* 1339ab21e32SAdrian Chadd * WAR for the Qualcomm Atheros AP136 board. 1347330dd0bSAdrian Chadd * It seems that RGMII TX/RX delay settings needs to be 1357330dd0bSAdrian Chadd * applied for SGMII mode as well, The ethernet is not 1367330dd0bSAdrian Chadd * reliable without this. 1377330dd0bSAdrian Chadd */ 1387330dd0bSAdrian Chadd t |= cfg->txclk_delay_sel << AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S; 1397330dd0bSAdrian Chadd t |= cfg->rxclk_delay_sel << AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S; 1407330dd0bSAdrian Chadd if (cfg->rxclk_delay_en) 1417330dd0bSAdrian Chadd t |= AR8327_PAD_RGMII_RXCLK_DELAY_EN; 1427330dd0bSAdrian Chadd if (cfg->txclk_delay_en) 1437330dd0bSAdrian Chadd t |= AR8327_PAD_RGMII_TXCLK_DELAY_EN; 1447330dd0bSAdrian Chadd 1457330dd0bSAdrian Chadd if (cfg->sgmii_delay_en) 1467330dd0bSAdrian Chadd t |= AR8327_PAD_SGMII_DELAY_EN; 1477330dd0bSAdrian Chadd 1487330dd0bSAdrian Chadd break; 1497330dd0bSAdrian Chadd 1507330dd0bSAdrian Chadd case AR8327_PAD_MAC2PHY_MII: 1517330dd0bSAdrian Chadd t = AR8327_PAD_PHY_MII_EN; 1527330dd0bSAdrian Chadd if (cfg->rxclk_sel) 1537330dd0bSAdrian Chadd t |= AR8327_PAD_PHY_MII_RXCLK_SEL; 1547330dd0bSAdrian Chadd if (cfg->txclk_sel) 1557330dd0bSAdrian Chadd t |= AR8327_PAD_PHY_MII_TXCLK_SEL; 1567330dd0bSAdrian Chadd break; 1577330dd0bSAdrian Chadd 1587330dd0bSAdrian Chadd case AR8327_PAD_MAC2PHY_GMII: 1597330dd0bSAdrian Chadd t = AR8327_PAD_PHY_GMII_EN; 1607330dd0bSAdrian Chadd if (cfg->pipe_rxclk_sel) 1617330dd0bSAdrian Chadd t |= AR8327_PAD_PHY_GMII_PIPE_RXCLK_SEL; 1627330dd0bSAdrian Chadd if (cfg->rxclk_sel) 1637330dd0bSAdrian Chadd t |= AR8327_PAD_PHY_GMII_RXCLK_SEL; 1647330dd0bSAdrian Chadd if (cfg->txclk_sel) 1657330dd0bSAdrian Chadd t |= AR8327_PAD_PHY_GMII_TXCLK_SEL; 1667330dd0bSAdrian Chadd break; 1677330dd0bSAdrian Chadd 1687330dd0bSAdrian Chadd case AR8327_PAD_MAC_RGMII: 1697330dd0bSAdrian Chadd t = AR8327_PAD_RGMII_EN; 1707330dd0bSAdrian Chadd t |= cfg->txclk_delay_sel << AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S; 1717330dd0bSAdrian Chadd t |= cfg->rxclk_delay_sel << AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S; 1727330dd0bSAdrian Chadd if (cfg->rxclk_delay_en) 1737330dd0bSAdrian Chadd t |= AR8327_PAD_RGMII_RXCLK_DELAY_EN; 1747330dd0bSAdrian Chadd if (cfg->txclk_delay_en) 1757330dd0bSAdrian Chadd t |= AR8327_PAD_RGMII_TXCLK_DELAY_EN; 1767330dd0bSAdrian Chadd break; 1777330dd0bSAdrian Chadd 1787330dd0bSAdrian Chadd case AR8327_PAD_PHY_GMII: 1797330dd0bSAdrian Chadd t = AR8327_PAD_PHYX_GMII_EN; 1807330dd0bSAdrian Chadd break; 1817330dd0bSAdrian Chadd 1827330dd0bSAdrian Chadd case AR8327_PAD_PHY_RGMII: 1837330dd0bSAdrian Chadd t = AR8327_PAD_PHYX_RGMII_EN; 1847330dd0bSAdrian Chadd break; 1857330dd0bSAdrian Chadd 1867330dd0bSAdrian Chadd case AR8327_PAD_PHY_MII: 1877330dd0bSAdrian Chadd t = AR8327_PAD_PHYX_MII_EN; 1887330dd0bSAdrian Chadd break; 1897330dd0bSAdrian Chadd } 1907330dd0bSAdrian Chadd 1917330dd0bSAdrian Chadd return (t); 1927330dd0bSAdrian Chadd } 1937330dd0bSAdrian Chadd 1947330dd0bSAdrian Chadd /* 1957330dd0bSAdrian Chadd * Map the hard-coded port config from the switch setup to 1967330dd0bSAdrian Chadd * the chipset port config (status, duplex, flow, etc.) 1977330dd0bSAdrian Chadd */ 1987330dd0bSAdrian Chadd static uint32_t 1997330dd0bSAdrian Chadd ar8327_get_port_init_status(struct ar8327_port_cfg *cfg) 2007330dd0bSAdrian Chadd { 2017330dd0bSAdrian Chadd uint32_t t; 2027330dd0bSAdrian Chadd 2037330dd0bSAdrian Chadd if (!cfg->force_link) 2047330dd0bSAdrian Chadd return (AR8X16_PORT_STS_LINK_AUTO); 2057330dd0bSAdrian Chadd 2067330dd0bSAdrian Chadd t = AR8X16_PORT_STS_TXMAC | AR8X16_PORT_STS_RXMAC; 2077330dd0bSAdrian Chadd t |= cfg->duplex ? AR8X16_PORT_STS_DUPLEX : 0; 2087330dd0bSAdrian Chadd t |= cfg->rxpause ? AR8X16_PORT_STS_RXFLOW : 0; 2097330dd0bSAdrian Chadd t |= cfg->txpause ? AR8X16_PORT_STS_TXFLOW : 0; 2107330dd0bSAdrian Chadd 2117330dd0bSAdrian Chadd switch (cfg->speed) { 2127330dd0bSAdrian Chadd case AR8327_PORT_SPEED_10: 2137330dd0bSAdrian Chadd t |= AR8X16_PORT_STS_SPEED_10; 2147330dd0bSAdrian Chadd break; 2157330dd0bSAdrian Chadd case AR8327_PORT_SPEED_100: 2167330dd0bSAdrian Chadd t |= AR8X16_PORT_STS_SPEED_100; 2177330dd0bSAdrian Chadd break; 2187330dd0bSAdrian Chadd case AR8327_PORT_SPEED_1000: 2197330dd0bSAdrian Chadd t |= AR8X16_PORT_STS_SPEED_1000; 2207330dd0bSAdrian Chadd break; 2217330dd0bSAdrian Chadd } 2227330dd0bSAdrian Chadd 2237330dd0bSAdrian Chadd return (t); 2247330dd0bSAdrian Chadd } 2259ab21e32SAdrian Chadd 2269ab21e32SAdrian Chadd /* 227f9950f9aSAdrian Chadd * Fetch the port data for the given port. 228f9950f9aSAdrian Chadd * 229f9950f9aSAdrian Chadd * This goes and does dirty things with the hints space 230f9950f9aSAdrian Chadd * to determine what the configuration parameters should be. 231f9950f9aSAdrian Chadd * 232f9950f9aSAdrian Chadd * Returns 1 if the structure was successfully parsed and 233f9950f9aSAdrian Chadd * the contents are valid; 0 otherwise. 234f9950f9aSAdrian Chadd */ 235f9950f9aSAdrian Chadd static int 236f9950f9aSAdrian Chadd ar8327_fetch_pdata_port(struct arswitch_softc *sc, 237f9950f9aSAdrian Chadd struct ar8327_port_cfg *pcfg, 238f9950f9aSAdrian Chadd int port) 239f9950f9aSAdrian Chadd { 240f9950f9aSAdrian Chadd int val; 241f9950f9aSAdrian Chadd char sbuf[128]; 242f9950f9aSAdrian Chadd 243f9950f9aSAdrian Chadd /* Check if force_link exists */ 244f9950f9aSAdrian Chadd val = 0; 245f9950f9aSAdrian Chadd snprintf(sbuf, 128, "port.%d.force_link", port); 246f9950f9aSAdrian Chadd (void) resource_int_value(device_get_name(sc->sc_dev), 247f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 248f9950f9aSAdrian Chadd sbuf, &val); 249f9950f9aSAdrian Chadd if (val != 1) 250f9950f9aSAdrian Chadd return (0); 251f9950f9aSAdrian Chadd pcfg->force_link = 1; 252f9950f9aSAdrian Chadd 253f9950f9aSAdrian Chadd /* force_link is set; let's parse the rest of the fields */ 254f9950f9aSAdrian Chadd snprintf(sbuf, 128, "port.%d.speed", port); 255f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 256f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 257f9950f9aSAdrian Chadd sbuf, &val) == 0) { 258f9950f9aSAdrian Chadd switch (val) { 259f9950f9aSAdrian Chadd case 10: 260f9950f9aSAdrian Chadd pcfg->speed = AR8327_PORT_SPEED_10; 261f9950f9aSAdrian Chadd break; 262f9950f9aSAdrian Chadd case 100: 263f9950f9aSAdrian Chadd pcfg->speed = AR8327_PORT_SPEED_100; 264f9950f9aSAdrian Chadd break; 265f9950f9aSAdrian Chadd case 1000: 266f9950f9aSAdrian Chadd pcfg->speed = AR8327_PORT_SPEED_1000; 267f9950f9aSAdrian Chadd break; 268f9950f9aSAdrian Chadd default: 269f9950f9aSAdrian Chadd device_printf(sc->sc_dev, 270f9950f9aSAdrian Chadd "%s: invalid port %d duplex value (%d)\n", 271f9950f9aSAdrian Chadd __func__, 272f9950f9aSAdrian Chadd port, 273f9950f9aSAdrian Chadd val); 274f9950f9aSAdrian Chadd return (0); 275f9950f9aSAdrian Chadd } 276f9950f9aSAdrian Chadd } 277f9950f9aSAdrian Chadd 278f9950f9aSAdrian Chadd snprintf(sbuf, 128, "port.%d.duplex", port); 279f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 280f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 281f9950f9aSAdrian Chadd sbuf, &val) == 0) 282f9950f9aSAdrian Chadd pcfg->duplex = val; 283f9950f9aSAdrian Chadd 284f9950f9aSAdrian Chadd snprintf(sbuf, 128, "port.%d.txpause", port); 285f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 286f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 287f9950f9aSAdrian Chadd sbuf, &val) == 0) 288f9950f9aSAdrian Chadd pcfg->txpause = val; 289f9950f9aSAdrian Chadd 290f9950f9aSAdrian Chadd snprintf(sbuf, 128, "port.%d.rxpause", port); 291f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 292f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 293f9950f9aSAdrian Chadd sbuf, &val) == 0) 294f9950f9aSAdrian Chadd pcfg->rxpause = val; 295f9950f9aSAdrian Chadd 29678549b94SAdrian Chadd #if 1 297f9950f9aSAdrian Chadd device_printf(sc->sc_dev, 298f9950f9aSAdrian Chadd "%s: port %d: speed=%d, duplex=%d, txpause=%d, rxpause=%d\n", 299f9950f9aSAdrian Chadd __func__, 300f9950f9aSAdrian Chadd port, 301f9950f9aSAdrian Chadd pcfg->speed, 302f9950f9aSAdrian Chadd pcfg->duplex, 303f9950f9aSAdrian Chadd pcfg->txpause, 304f9950f9aSAdrian Chadd pcfg->rxpause); 305f9950f9aSAdrian Chadd #endif 306f9950f9aSAdrian Chadd 307f9950f9aSAdrian Chadd return (1); 308f9950f9aSAdrian Chadd } 309f9950f9aSAdrian Chadd 310f9950f9aSAdrian Chadd /* 311f9950f9aSAdrian Chadd * Parse the pad configuration from the boot hints. 312f9950f9aSAdrian Chadd * 313f9950f9aSAdrian Chadd * The (mostly optional) fields are: 314f9950f9aSAdrian Chadd * 315f9950f9aSAdrian Chadd * uint32_t mode; 316f9950f9aSAdrian Chadd * uint32_t rxclk_sel; 317f9950f9aSAdrian Chadd * uint32_t txclk_sel; 318f9950f9aSAdrian Chadd * uint32_t txclk_delay_sel; 319f9950f9aSAdrian Chadd * uint32_t rxclk_delay_sel; 320f9950f9aSAdrian Chadd * uint32_t txclk_delay_en; 321f9950f9aSAdrian Chadd * uint32_t rxclk_delay_en; 322f9950f9aSAdrian Chadd * uint32_t sgmii_delay_en; 323f9950f9aSAdrian Chadd * uint32_t pipe_rxclk_sel; 324f9950f9aSAdrian Chadd * 325f9950f9aSAdrian Chadd * If mode isn't in the hints, 0 is returned. 326f9950f9aSAdrian Chadd * Else the structure is fleshed out and 1 is returned. 327f9950f9aSAdrian Chadd */ 328f9950f9aSAdrian Chadd static int 329f9950f9aSAdrian Chadd ar8327_fetch_pdata_pad(struct arswitch_softc *sc, 330f9950f9aSAdrian Chadd struct ar8327_pad_cfg *pc, 331f9950f9aSAdrian Chadd int pad) 332f9950f9aSAdrian Chadd { 333f9950f9aSAdrian Chadd int val; 334f9950f9aSAdrian Chadd char sbuf[128]; 335f9950f9aSAdrian Chadd 336f9950f9aSAdrian Chadd /* Check if mode exists */ 337f9950f9aSAdrian Chadd val = 0; 338f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.mode", pad); 339f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 340f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 341f9950f9aSAdrian Chadd sbuf, &val) != 0) 342f9950f9aSAdrian Chadd return (0); 343f9950f9aSAdrian Chadd 344f9950f9aSAdrian Chadd /* assume that 'mode' exists and was found */ 345f9950f9aSAdrian Chadd pc->mode = val; 346f9950f9aSAdrian Chadd 347f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.rxclk_sel", pad); 348f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 349f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 350f9950f9aSAdrian Chadd sbuf, &val) == 0) 351f9950f9aSAdrian Chadd pc->rxclk_sel = val; 352f9950f9aSAdrian Chadd 353f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.txclk_sel", pad); 354f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 355f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 356f9950f9aSAdrian Chadd sbuf, &val) == 0) 357f9950f9aSAdrian Chadd pc->txclk_sel = val; 358f9950f9aSAdrian Chadd 359f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.txclk_delay_sel", pad); 360f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 361f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 362f9950f9aSAdrian Chadd sbuf, &val) == 0) 363f9950f9aSAdrian Chadd pc->txclk_delay_sel = val; 364f9950f9aSAdrian Chadd 365f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.rxclk_delay_sel", pad); 366f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 367f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 368f9950f9aSAdrian Chadd sbuf, &val) == 0) 369f9950f9aSAdrian Chadd pc->rxclk_delay_sel = val; 370f9950f9aSAdrian Chadd 371f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.txclk_delay_en", pad); 372f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 373f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 374f9950f9aSAdrian Chadd sbuf, &val) == 0) 375f9950f9aSAdrian Chadd pc->txclk_delay_en = val; 376f9950f9aSAdrian Chadd 377f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.rxclk_delay_en", pad); 378f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 379f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 380f9950f9aSAdrian Chadd sbuf, &val) == 0) 381f9950f9aSAdrian Chadd pc->rxclk_delay_en = val; 382f9950f9aSAdrian Chadd 383f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.sgmii_delay_en", 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 pc->sgmii_delay_en = val; 388f9950f9aSAdrian Chadd 389f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.pipe_rxclk_sel", pad); 390f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 391f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 392f9950f9aSAdrian Chadd sbuf, &val) == 0) 393f9950f9aSAdrian Chadd pc->pipe_rxclk_sel = val; 394f9950f9aSAdrian Chadd 395db37238fSAdrian Chadd if (bootverbose) { 396f9950f9aSAdrian Chadd device_printf(sc->sc_dev, 397f9950f9aSAdrian Chadd "%s: pad %d: mode=%d, rxclk_sel=%d, txclk_sel=%d, " 398f9950f9aSAdrian Chadd "txclk_delay_sel=%d, rxclk_delay_sel=%d, txclk_delay_en=%d, " 399f9950f9aSAdrian Chadd "rxclk_enable_en=%d, sgmii_delay_en=%d, pipe_rxclk_sel=%d\n", 400f9950f9aSAdrian Chadd __func__, 401f9950f9aSAdrian Chadd pad, 402f9950f9aSAdrian Chadd pc->mode, 403f9950f9aSAdrian Chadd pc->rxclk_sel, 404f9950f9aSAdrian Chadd pc->txclk_sel, 405f9950f9aSAdrian Chadd pc->txclk_delay_sel, 406f9950f9aSAdrian Chadd pc->rxclk_delay_sel, 407f9950f9aSAdrian Chadd pc->txclk_delay_en, 408f9950f9aSAdrian Chadd pc->rxclk_delay_en, 409f9950f9aSAdrian Chadd pc->sgmii_delay_en, 410f9950f9aSAdrian Chadd pc->pipe_rxclk_sel); 411db37238fSAdrian Chadd } 412f9950f9aSAdrian Chadd 413f9950f9aSAdrian Chadd return (1); 414f9950f9aSAdrian Chadd } 415f9950f9aSAdrian Chadd 416f9950f9aSAdrian Chadd /* 417810bdeddSAdrian Chadd * Fetch the SGMII configuration block from the boot hints. 418810bdeddSAdrian Chadd */ 419810bdeddSAdrian Chadd static int 420810bdeddSAdrian Chadd ar8327_fetch_pdata_sgmii(struct arswitch_softc *sc, 421810bdeddSAdrian Chadd struct ar8327_sgmii_cfg *scfg) 422810bdeddSAdrian Chadd { 423810bdeddSAdrian Chadd int val; 424810bdeddSAdrian Chadd 425810bdeddSAdrian Chadd /* sgmii_ctrl */ 426810bdeddSAdrian Chadd val = 0; 427810bdeddSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 428810bdeddSAdrian Chadd device_get_unit(sc->sc_dev), 429810bdeddSAdrian Chadd "sgmii.ctrl", &val) != 0) 430810bdeddSAdrian Chadd return (0); 431810bdeddSAdrian Chadd scfg->sgmii_ctrl = val; 432810bdeddSAdrian Chadd 433810bdeddSAdrian Chadd /* serdes_aen */ 434810bdeddSAdrian Chadd val = 0; 435810bdeddSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 436810bdeddSAdrian Chadd device_get_unit(sc->sc_dev), 437810bdeddSAdrian Chadd "sgmii.serdes_aen", &val) != 0) 438810bdeddSAdrian Chadd return (0); 439810bdeddSAdrian Chadd scfg->serdes_aen = val; 440810bdeddSAdrian Chadd 441810bdeddSAdrian Chadd return (1); 442810bdeddSAdrian Chadd } 443810bdeddSAdrian Chadd 444810bdeddSAdrian Chadd /* 445b67ba111SAdrian Chadd * Fetch the LED configuration from the boot hints. 446b67ba111SAdrian Chadd */ 447b67ba111SAdrian Chadd static int 448b67ba111SAdrian Chadd ar8327_fetch_pdata_led(struct arswitch_softc *sc, 449b67ba111SAdrian Chadd struct ar8327_led_cfg *lcfg) 450b67ba111SAdrian Chadd { 451b67ba111SAdrian Chadd int val; 452b67ba111SAdrian Chadd 453b67ba111SAdrian Chadd val = 0; 454b67ba111SAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 455b67ba111SAdrian Chadd device_get_unit(sc->sc_dev), 456b67ba111SAdrian Chadd "led.ctrl0", &val) != 0) 457b67ba111SAdrian Chadd return (0); 458b67ba111SAdrian Chadd lcfg->led_ctrl0 = val; 459b67ba111SAdrian Chadd 460b67ba111SAdrian Chadd val = 0; 461b67ba111SAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 462b67ba111SAdrian Chadd device_get_unit(sc->sc_dev), 463b67ba111SAdrian Chadd "led.ctrl1", &val) != 0) 464b67ba111SAdrian Chadd return (0); 465b67ba111SAdrian Chadd lcfg->led_ctrl1 = val; 466b67ba111SAdrian Chadd 467b67ba111SAdrian Chadd val = 0; 468b67ba111SAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 469b67ba111SAdrian Chadd device_get_unit(sc->sc_dev), 470b67ba111SAdrian Chadd "led.ctrl2", &val) != 0) 471b67ba111SAdrian Chadd return (0); 472b67ba111SAdrian Chadd lcfg->led_ctrl2 = val; 473b67ba111SAdrian Chadd 474b67ba111SAdrian Chadd val = 0; 475b67ba111SAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 476b67ba111SAdrian Chadd device_get_unit(sc->sc_dev), 477b67ba111SAdrian Chadd "led.ctrl3", &val) != 0) 478b67ba111SAdrian Chadd return (0); 479b67ba111SAdrian Chadd lcfg->led_ctrl3 = val; 480b67ba111SAdrian Chadd 481b67ba111SAdrian Chadd val = 0; 482b67ba111SAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 483b67ba111SAdrian Chadd device_get_unit(sc->sc_dev), 484b67ba111SAdrian Chadd "led.open_drain", &val) != 0) 485b67ba111SAdrian Chadd return (0); 486b67ba111SAdrian Chadd lcfg->open_drain = val; 487b67ba111SAdrian Chadd 488b67ba111SAdrian Chadd return (1); 489b67ba111SAdrian Chadd } 490b67ba111SAdrian Chadd 491b67ba111SAdrian Chadd /* 4929ab21e32SAdrian Chadd * Initialise the ar8327 specific hardware features from 4939ab21e32SAdrian Chadd * the hints provided in the boot environment. 4949ab21e32SAdrian Chadd */ 4959ab21e32SAdrian Chadd static int 4969ab21e32SAdrian Chadd ar8327_init_pdata(struct arswitch_softc *sc) 4979ab21e32SAdrian Chadd { 4989ab21e32SAdrian Chadd struct ar8327_pad_cfg pc; 4999ab21e32SAdrian Chadd struct ar8327_port_cfg port_cfg; 500810bdeddSAdrian Chadd struct ar8327_sgmii_cfg scfg; 501b67ba111SAdrian Chadd struct ar8327_led_cfg lcfg; 502810bdeddSAdrian Chadd uint32_t t, new_pos, pos; 5039ab21e32SAdrian Chadd 504f9950f9aSAdrian Chadd /* Port 0 */ 5059ab21e32SAdrian Chadd bzero(&port_cfg, sizeof(port_cfg)); 506f9950f9aSAdrian Chadd sc->ar8327.port0_status = 0; 507f9950f9aSAdrian Chadd if (ar8327_fetch_pdata_port(sc, &port_cfg, 0)) 5089ab21e32SAdrian Chadd sc->ar8327.port0_status = ar8327_get_port_init_status(&port_cfg); 5099ab21e32SAdrian Chadd 510f9950f9aSAdrian Chadd /* Port 6 */ 5119ab21e32SAdrian Chadd bzero(&port_cfg, sizeof(port_cfg)); 512f9950f9aSAdrian Chadd sc->ar8327.port6_status = 0; 513f9950f9aSAdrian Chadd if (ar8327_fetch_pdata_port(sc, &port_cfg, 6)) 5149ab21e32SAdrian Chadd sc->ar8327.port6_status = ar8327_get_port_init_status(&port_cfg); 5159ab21e32SAdrian Chadd 5169ab21e32SAdrian Chadd /* Pad 0 */ 5179ab21e32SAdrian Chadd bzero(&pc, sizeof(pc)); 518f9950f9aSAdrian Chadd t = 0; 519f9950f9aSAdrian Chadd if (ar8327_fetch_pdata_pad(sc, &pc, 0)) 5209ab21e32SAdrian Chadd t = ar8327_get_pad_cfg(&pc); 5219ab21e32SAdrian Chadd #if 0 5229ab21e32SAdrian Chadd if (AR8X16_IS_SWITCH(sc, AR8337)) 5239ab21e32SAdrian Chadd t |= AR8337_PAD_MAC06_EXCHANGE_EN; 5247330dd0bSAdrian Chadd #endif 5259ab21e32SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PAD0_MODE, t); 5269ab21e32SAdrian Chadd 5279ab21e32SAdrian Chadd /* Pad 5 */ 5289ab21e32SAdrian Chadd bzero(&pc, sizeof(pc)); 529f9950f9aSAdrian Chadd t = 0; 530f9950f9aSAdrian Chadd if (ar8327_fetch_pdata_pad(sc, &pc, 5)) 5319ab21e32SAdrian Chadd t = ar8327_get_pad_cfg(&pc); 5329ab21e32SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PAD5_MODE, t); 5339ab21e32SAdrian Chadd 5349ab21e32SAdrian Chadd /* Pad 6 */ 5359ab21e32SAdrian Chadd bzero(&pc, sizeof(pc)); 536f9950f9aSAdrian Chadd t = 0; 537f9950f9aSAdrian Chadd if (ar8327_fetch_pdata_pad(sc, &pc, 6)) 5389ab21e32SAdrian Chadd t = ar8327_get_pad_cfg(&pc); 5399ab21e32SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PAD6_MODE, t); 5409ab21e32SAdrian Chadd 541810bdeddSAdrian Chadd pos = arswitch_readreg(sc->sc_dev, AR8327_REG_POWER_ON_STRIP); 542810bdeddSAdrian Chadd new_pos = pos; 543810bdeddSAdrian Chadd 5449ab21e32SAdrian Chadd /* XXX LED config */ 545b67ba111SAdrian Chadd bzero(&lcfg, sizeof(lcfg)); 546b67ba111SAdrian Chadd if (ar8327_fetch_pdata_led(sc, &lcfg)) { 547b67ba111SAdrian Chadd if (lcfg.open_drain) 548b67ba111SAdrian Chadd new_pos |= AR8327_POWER_ON_STRIP_LED_OPEN_EN; 549b67ba111SAdrian Chadd else 550b67ba111SAdrian Chadd new_pos &= ~AR8327_POWER_ON_STRIP_LED_OPEN_EN; 551b67ba111SAdrian Chadd 552b67ba111SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_LED_CTRL0, 553b67ba111SAdrian Chadd lcfg.led_ctrl0); 554b67ba111SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_LED_CTRL1, 555b67ba111SAdrian Chadd lcfg.led_ctrl1); 556b67ba111SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_LED_CTRL2, 557b67ba111SAdrian Chadd lcfg.led_ctrl2); 558b67ba111SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_LED_CTRL3, 559b67ba111SAdrian Chadd lcfg.led_ctrl3); 560b67ba111SAdrian Chadd 561b67ba111SAdrian Chadd if (new_pos != pos) 562b67ba111SAdrian Chadd new_pos |= AR8327_POWER_ON_STRIP_POWER_ON_SEL; 563b67ba111SAdrian Chadd } 5649ab21e32SAdrian Chadd 565810bdeddSAdrian Chadd /* SGMII config */ 566810bdeddSAdrian Chadd bzero(&scfg, sizeof(scfg)); 567810bdeddSAdrian Chadd if (ar8327_fetch_pdata_sgmii(sc, &scfg)) { 56878549b94SAdrian Chadd device_printf(sc->sc_dev, "%s: SGMII cfg?\n", __func__); 569810bdeddSAdrian Chadd t = scfg.sgmii_ctrl; 570810bdeddSAdrian Chadd if (sc->chip_rev == 1) 571810bdeddSAdrian Chadd t |= AR8327_SGMII_CTRL_EN_PLL | 572810bdeddSAdrian Chadd AR8327_SGMII_CTRL_EN_RX | 573810bdeddSAdrian Chadd AR8327_SGMII_CTRL_EN_TX; 574810bdeddSAdrian Chadd else 575810bdeddSAdrian Chadd t &= ~(AR8327_SGMII_CTRL_EN_PLL | 576810bdeddSAdrian Chadd AR8327_SGMII_CTRL_EN_RX | 577810bdeddSAdrian Chadd AR8327_SGMII_CTRL_EN_TX); 578810bdeddSAdrian Chadd 579810bdeddSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_SGMII_CTRL, t); 580810bdeddSAdrian Chadd 581810bdeddSAdrian Chadd if (scfg.serdes_aen) 582810bdeddSAdrian Chadd new_pos &= ~AR8327_POWER_ON_STRIP_SERDES_AEN; 583810bdeddSAdrian Chadd else 584810bdeddSAdrian Chadd new_pos |= AR8327_POWER_ON_STRIP_SERDES_AEN; 585810bdeddSAdrian Chadd } 586810bdeddSAdrian Chadd 587810bdeddSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_POWER_ON_STRIP, new_pos); 5889ab21e32SAdrian Chadd 5899ab21e32SAdrian Chadd return (0); 5909ab21e32SAdrian Chadd } 5917330dd0bSAdrian Chadd 5927330dd0bSAdrian Chadd static int 5937330dd0bSAdrian Chadd ar8327_hw_setup(struct arswitch_softc *sc) 5947330dd0bSAdrian Chadd { 5957330dd0bSAdrian Chadd int i; 5967330dd0bSAdrian Chadd int err; 5977330dd0bSAdrian Chadd 5987330dd0bSAdrian Chadd /* pdata fetch and setup */ 5997330dd0bSAdrian Chadd err = ar8327_init_pdata(sc); 6007330dd0bSAdrian Chadd if (err != 0) 6017330dd0bSAdrian Chadd return (err); 6027330dd0bSAdrian Chadd 6037330dd0bSAdrian Chadd /* XXX init leds */ 6047330dd0bSAdrian Chadd 6057330dd0bSAdrian Chadd for (i = 0; i < AR8327_NUM_PHYS; i++) { 6067330dd0bSAdrian Chadd /* phy fixup */ 6077330dd0bSAdrian Chadd ar8327_phy_fixup(sc, i); 6087330dd0bSAdrian Chadd 6097330dd0bSAdrian Chadd /* start PHY autonegotiation? */ 6107330dd0bSAdrian Chadd /* XXX is this done as part of the normal PHY setup? */ 6117330dd0bSAdrian Chadd 6127330dd0bSAdrian Chadd }; 6137330dd0bSAdrian Chadd 6147330dd0bSAdrian Chadd /* Let things settle */ 6157330dd0bSAdrian Chadd DELAY(1000); 6167330dd0bSAdrian Chadd 6177330dd0bSAdrian Chadd return (0); 6187330dd0bSAdrian Chadd } 6197330dd0bSAdrian Chadd 6207330dd0bSAdrian Chadd /* 6217330dd0bSAdrian Chadd * Initialise other global values, for the AR8327. 6227330dd0bSAdrian Chadd */ 6237330dd0bSAdrian Chadd static int 6247330dd0bSAdrian Chadd ar8327_hw_global_setup(struct arswitch_softc *sc) 6257330dd0bSAdrian Chadd { 6267330dd0bSAdrian Chadd uint32_t t; 6277330dd0bSAdrian Chadd 6287330dd0bSAdrian Chadd /* enable CPU port and disable mirror port */ 6297330dd0bSAdrian Chadd t = AR8327_FWD_CTRL0_CPU_PORT_EN | 6307330dd0bSAdrian Chadd AR8327_FWD_CTRL0_MIRROR_PORT; 6317330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_FWD_CTRL0, t); 6327330dd0bSAdrian Chadd 6337330dd0bSAdrian Chadd /* forward multicast and broadcast frames to CPU */ 6347330dd0bSAdrian Chadd t = (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_UC_FLOOD_S) | 6357330dd0bSAdrian Chadd (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_MC_FLOOD_S) | 6367330dd0bSAdrian Chadd (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_BC_FLOOD_S); 6377330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_FWD_CTRL1, t); 6387330dd0bSAdrian Chadd 6397330dd0bSAdrian Chadd /* enable jumbo frames */ 6407330dd0bSAdrian Chadd /* XXX need to macro-shift the value! */ 6417330dd0bSAdrian Chadd arswitch_modifyreg(sc->sc_dev, AR8327_REG_MAX_FRAME_SIZE, 6427330dd0bSAdrian Chadd AR8327_MAX_FRAME_SIZE_MTU, 9018 + 8 + 2); 6437330dd0bSAdrian Chadd 6447330dd0bSAdrian Chadd /* Enable MIB counters */ 6457330dd0bSAdrian Chadd arswitch_modifyreg(sc->sc_dev, AR8327_REG_MODULE_EN, 6467330dd0bSAdrian Chadd AR8327_MODULE_EN_MIB, AR8327_MODULE_EN_MIB); 6477330dd0bSAdrian Chadd 648db37238fSAdrian Chadd /* Disable EEE on all ports due to stability issues */ 649db37238fSAdrian Chadd t = arswitch_readreg(sc->sc_dev, AR8327_REG_EEE_CTRL); 650db37238fSAdrian Chadd t |= AR8327_EEE_CTRL_DISABLE_PHY(0) | 651db37238fSAdrian Chadd AR8327_EEE_CTRL_DISABLE_PHY(1) | 652db37238fSAdrian Chadd AR8327_EEE_CTRL_DISABLE_PHY(2) | 653db37238fSAdrian Chadd AR8327_EEE_CTRL_DISABLE_PHY(3) | 654db37238fSAdrian Chadd AR8327_EEE_CTRL_DISABLE_PHY(4); 655db37238fSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_EEE_CTRL, t); 656db37238fSAdrian Chadd 6574ff2f60dSAdrian Chadd /* Set the right number of ports */ 658*749cac13SAdrian Chadd /* GMAC0 (CPU), GMAC1..5 (PHYs), GMAC6 (CPU) */ 659*749cac13SAdrian Chadd sc->info.es_nports = 7; 6604ff2f60dSAdrian Chadd 6617330dd0bSAdrian Chadd return (0); 6627330dd0bSAdrian Chadd } 6637330dd0bSAdrian Chadd 6647330dd0bSAdrian Chadd /* 66578549b94SAdrian Chadd * Port setup. Called at attach time. 6667330dd0bSAdrian Chadd */ 6677330dd0bSAdrian Chadd static void 6687330dd0bSAdrian Chadd ar8327_port_init(struct arswitch_softc *sc, int port) 6697330dd0bSAdrian Chadd { 6707330dd0bSAdrian Chadd uint32_t t; 67178549b94SAdrian Chadd int ports; 67278549b94SAdrian Chadd 67378549b94SAdrian Chadd /* For now, port can see all other ports */ 67478549b94SAdrian Chadd ports = 0x7f; 6757330dd0bSAdrian Chadd 6769ab21e32SAdrian Chadd if (port == AR8X16_PORT_CPU) 6779ab21e32SAdrian Chadd t = sc->ar8327.port0_status; 6787330dd0bSAdrian Chadd else if (port == 6) 6799ab21e32SAdrian Chadd t = sc->ar8327.port6_status; 6807330dd0bSAdrian Chadd else 6817330dd0bSAdrian Chadd t = AR8X16_PORT_STS_LINK_AUTO; 6827330dd0bSAdrian Chadd 6837330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_STATUS(port), t); 6847330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_HEADER(port), 0); 6857330dd0bSAdrian Chadd 6860d2041a0SAdrian Chadd /* 6870d2041a0SAdrian Chadd * Default to 1 port group. 6880d2041a0SAdrian Chadd */ 6897330dd0bSAdrian Chadd t = 1 << AR8327_PORT_VLAN0_DEF_SVID_S; 6907330dd0bSAdrian Chadd t |= 1 << AR8327_PORT_VLAN0_DEF_CVID_S; 6917330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_VLAN0(port), t); 6927330dd0bSAdrian Chadd 6937330dd0bSAdrian Chadd t = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH << AR8327_PORT_VLAN1_OUT_MODE_S; 6947330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_VLAN1(port), t); 6957330dd0bSAdrian Chadd 69603b5d827SAdrian Chadd /* 69703b5d827SAdrian Chadd * This doesn't configure any ports which this port can "see". 69803b5d827SAdrian Chadd * bits 0-6 control which ports a frame coming into this port 69903b5d827SAdrian Chadd * can be sent out to. 70003b5d827SAdrian Chadd * 70103b5d827SAdrian Chadd * So by doing this, we're making it impossible to send frames out 70203b5d827SAdrian Chadd * to that port. 70303b5d827SAdrian Chadd */ 7047330dd0bSAdrian Chadd t = AR8327_PORT_LOOKUP_LEARN; 7057330dd0bSAdrian Chadd t |= AR8X16_PORT_CTRL_STATE_FORWARD << AR8327_PORT_LOOKUP_STATE_S; 70603b5d827SAdrian Chadd 70703b5d827SAdrian Chadd /* So this allows traffic to any port except ourselves */ 70878549b94SAdrian Chadd t |= (ports & ~(1 << port)); 7097330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_LOOKUP(port), t); 7107330dd0bSAdrian Chadd } 7117330dd0bSAdrian Chadd 7127330dd0bSAdrian Chadd static int 7137330dd0bSAdrian Chadd ar8327_port_vlan_setup(struct arswitch_softc *sc, etherswitch_port_t *p) 7147330dd0bSAdrian Chadd { 7157330dd0bSAdrian Chadd 716*749cac13SAdrian Chadd /* Check: ADDTAG/STRIPTAG - exclusive */ 717*749cac13SAdrian Chadd 718*749cac13SAdrian Chadd ARSWITCH_LOCK(sc); 719*749cac13SAdrian Chadd 720*749cac13SAdrian Chadd /* Set the PVID. */ 721*749cac13SAdrian Chadd if (p->es_pvid != 0) 722*749cac13SAdrian Chadd sc->hal.arswitch_vlan_set_pvid(sc, p->es_port, p->es_pvid); 723*749cac13SAdrian Chadd 724*749cac13SAdrian Chadd /* 725*749cac13SAdrian Chadd * DOUBLE_TAG 726*749cac13SAdrian Chadd * VLAN_MODE_ADD 727*749cac13SAdrian Chadd * VLAN_MODE_STRIP 728*749cac13SAdrian Chadd */ 729*749cac13SAdrian Chadd ARSWITCH_UNLOCK(sc); 7307330dd0bSAdrian Chadd return (0); 7317330dd0bSAdrian Chadd } 7327330dd0bSAdrian Chadd 73378549b94SAdrian Chadd /* 73478549b94SAdrian Chadd * Get the port VLAN configuration. 73578549b94SAdrian Chadd */ 7367330dd0bSAdrian Chadd static int 7377330dd0bSAdrian Chadd ar8327_port_vlan_get(struct arswitch_softc *sc, etherswitch_port_t *p) 7387330dd0bSAdrian Chadd { 739*749cac13SAdrian Chadd 740*749cac13SAdrian Chadd ARSWITCH_LOCK(sc); 741*749cac13SAdrian Chadd 742*749cac13SAdrian Chadd /* Retrieve the PVID */ 743*749cac13SAdrian Chadd sc->hal.arswitch_vlan_get_pvid(sc, p->es_port, &p->es_pvid); 744*749cac13SAdrian Chadd 745*749cac13SAdrian Chadd /* Retrieve the current port configuration */ 746*749cac13SAdrian Chadd /* 747*749cac13SAdrian Chadd * DOUBLE_TAG 748*749cac13SAdrian Chadd * VLAN_MODE_ADD 749*749cac13SAdrian Chadd * VLAN_MODE_STRIP 750*749cac13SAdrian Chadd */ 751*749cac13SAdrian Chadd 752*749cac13SAdrian Chadd ARSWITCH_UNLOCK(sc); 7537330dd0bSAdrian Chadd return (0); 7547330dd0bSAdrian Chadd } 7557330dd0bSAdrian Chadd 7567330dd0bSAdrian Chadd static void 7577330dd0bSAdrian Chadd ar8327_reset_vlans(struct arswitch_softc *sc) 7587330dd0bSAdrian Chadd { 7597330dd0bSAdrian Chadd int i; 7607330dd0bSAdrian Chadd uint32_t mode, t; 76178549b94SAdrian Chadd int ports; 76278549b94SAdrian Chadd 76378549b94SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED); 76478549b94SAdrian Chadd ARSWITCH_LOCK(sc); 76578549b94SAdrian Chadd 76678549b94SAdrian Chadd /* Clear the existing VLAN configuration */ 76778549b94SAdrian Chadd memset(sc->vid, 0, sizeof(sc->vid)); 7687330dd0bSAdrian Chadd 7697330dd0bSAdrian Chadd /* 77003b5d827SAdrian Chadd * Disable mirroring. 77103b5d827SAdrian Chadd */ 77203b5d827SAdrian Chadd arswitch_modifyreg(sc->sc_dev, AR8327_REG_FWD_CTRL0, 77303b5d827SAdrian Chadd AR8327_FWD_CTRL0_MIRROR_PORT, 77403b5d827SAdrian Chadd (0xF << AR8327_FWD_CTRL0_MIRROR_PORT_S)); 77503b5d827SAdrian Chadd 77603b5d827SAdrian Chadd /* 77778549b94SAdrian Chadd * XXX TODO: disable any Q-in-Q port configuration, 77878549b94SAdrian Chadd * tagging, egress filters, etc. 7797330dd0bSAdrian Chadd */ 78078549b94SAdrian Chadd 78178549b94SAdrian Chadd /* 78278549b94SAdrian Chadd * For now, let's default to one portgroup, just so traffic 78378549b94SAdrian Chadd * flows. All ports can see other ports. There are two CPU GMACs 78478549b94SAdrian Chadd * (GMAC0, GMAC6), GMAC1..GMAC5 are external PHYs. 78578549b94SAdrian Chadd * 78678549b94SAdrian Chadd * (ETHERSWITCH_VLAN_PORT) 78778549b94SAdrian Chadd */ 78878549b94SAdrian Chadd ports = 0x7f; 78978549b94SAdrian Chadd 7907330dd0bSAdrian Chadd for (i = 0; i < AR8327_NUM_PORTS; i++) { 79178549b94SAdrian Chadd 792*749cac13SAdrian Chadd if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) 793*749cac13SAdrian Chadd sc->vid[i] = i | ETHERSWITCH_VID_VALID; 794*749cac13SAdrian Chadd 795*749cac13SAdrian Chadd /* set pvid = 1; there's only one vlangroup to start with */ 796dd846bddSAdrian Chadd t = 1 << AR8327_PORT_VLAN0_DEF_SVID_S; 797dd846bddSAdrian Chadd t |= 1 << AR8327_PORT_VLAN0_DEF_CVID_S; 7987330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_VLAN0(i), t); 7997330dd0bSAdrian Chadd 8007330dd0bSAdrian Chadd /* set egress == out_keep */ 8017330dd0bSAdrian Chadd mode = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH; 8027330dd0bSAdrian Chadd 8037330dd0bSAdrian Chadd t = AR8327_PORT_VLAN1_PORT_VLAN_PROP; 8047330dd0bSAdrian Chadd t |= mode << AR8327_PORT_VLAN1_OUT_MODE_S; 8057330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_VLAN1(i), t); 8067330dd0bSAdrian Chadd 807dd846bddSAdrian Chadd /* Ports can see other ports */ 80878549b94SAdrian Chadd t = (ports & ~(1 << i)); /* all ports besides us */ 8097330dd0bSAdrian Chadd t |= AR8327_PORT_LOOKUP_LEARN; 8107330dd0bSAdrian Chadd 8117330dd0bSAdrian Chadd /* in_port_only, forward */ 8127330dd0bSAdrian Chadd t |= AR8X16_PORT_VLAN_MODE_PORT_ONLY << AR8327_PORT_LOOKUP_IN_MODE_S; 8137330dd0bSAdrian Chadd t |= AR8X16_PORT_CTRL_STATE_FORWARD << AR8327_PORT_LOOKUP_STATE_S; 8147330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_LOOKUP(i), t); 81503b5d827SAdrian Chadd 81603b5d827SAdrian Chadd /* 81703b5d827SAdrian Chadd * Disable port mirroring entirely. 81803b5d827SAdrian Chadd */ 81903b5d827SAdrian Chadd arswitch_modifyreg(sc->sc_dev, 82003b5d827SAdrian Chadd AR8327_REG_PORT_LOOKUP(i), 82103b5d827SAdrian Chadd AR8327_PORT_LOOKUP_ING_MIRROR_EN, 82203b5d827SAdrian Chadd 0); 82303b5d827SAdrian Chadd arswitch_modifyreg(sc->sc_dev, 82403b5d827SAdrian Chadd AR8327_REG_PORT_HOL_CTRL1(i), 82503b5d827SAdrian Chadd AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN, 82603b5d827SAdrian Chadd 0); 8277330dd0bSAdrian Chadd } 82878549b94SAdrian Chadd 82978549b94SAdrian Chadd ARSWITCH_UNLOCK(sc); 8307330dd0bSAdrian Chadd } 8317330dd0bSAdrian Chadd 8327330dd0bSAdrian Chadd static int 833*749cac13SAdrian Chadd ar8327_vlan_get_port(struct arswitch_softc *sc, uint32_t *ports, int vid) 834*749cac13SAdrian Chadd { 835*749cac13SAdrian Chadd int port; 836*749cac13SAdrian Chadd uint32_t reg; 837*749cac13SAdrian Chadd 838*749cac13SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 839*749cac13SAdrian Chadd 840*749cac13SAdrian Chadd /* For port based vlans the vlanid is the same as the port index. */ 841*749cac13SAdrian Chadd port = vid & ETHERSWITCH_VID_MASK; 842*749cac13SAdrian Chadd reg = arswitch_readreg(sc->sc_dev, AR8327_REG_PORT_LOOKUP(port)); 843*749cac13SAdrian Chadd *ports = reg & 0x7f; 844*749cac13SAdrian Chadd return (0); 845*749cac13SAdrian Chadd } 846*749cac13SAdrian Chadd 847*749cac13SAdrian Chadd static int 848*749cac13SAdrian Chadd ar8327_vlan_set_port(struct arswitch_softc *sc, uint32_t ports, int vid) 849*749cac13SAdrian Chadd { 850*749cac13SAdrian Chadd int err, port; 851*749cac13SAdrian Chadd 852*749cac13SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 853*749cac13SAdrian Chadd 854*749cac13SAdrian Chadd /* For port based vlans the vlanid is the same as the port index. */ 855*749cac13SAdrian Chadd port = vid & ETHERSWITCH_VID_MASK; 856*749cac13SAdrian Chadd 857*749cac13SAdrian Chadd err = arswitch_modifyreg(sc->sc_dev, AR8327_REG_PORT_LOOKUP(port), 858*749cac13SAdrian Chadd 0x7f, /* vlan membership mask */ 859*749cac13SAdrian Chadd (ports & 0x7f)); 860*749cac13SAdrian Chadd 861*749cac13SAdrian Chadd if (err) 862*749cac13SAdrian Chadd return (err); 863*749cac13SAdrian Chadd return (0); 864*749cac13SAdrian Chadd } 865*749cac13SAdrian Chadd 866*749cac13SAdrian Chadd static int 8677330dd0bSAdrian Chadd ar8327_vlan_getvgroup(struct arswitch_softc *sc, etherswitch_vlangroup_t *vg) 8687330dd0bSAdrian Chadd { 86978549b94SAdrian Chadd 87078549b94SAdrian Chadd /* XXX for now, no dot1q vlans */ 87178549b94SAdrian Chadd if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) 87278549b94SAdrian Chadd return (EINVAL); 87378549b94SAdrian Chadd return (ar8xxx_getvgroup(sc, vg)); 8747330dd0bSAdrian Chadd } 8757330dd0bSAdrian Chadd 8767330dd0bSAdrian Chadd static int 8777330dd0bSAdrian Chadd ar8327_vlan_setvgroup(struct arswitch_softc *sc, etherswitch_vlangroup_t *vg) 8787330dd0bSAdrian Chadd { 8797330dd0bSAdrian Chadd 88078549b94SAdrian Chadd /* XXX for now, no dot1q vlans */ 88178549b94SAdrian Chadd if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) 88278549b94SAdrian Chadd return (EINVAL); 88378549b94SAdrian Chadd return (ar8xxx_setvgroup(sc, vg)); 8847330dd0bSAdrian Chadd } 8857330dd0bSAdrian Chadd 8867330dd0bSAdrian Chadd static int 8877330dd0bSAdrian Chadd ar8327_get_pvid(struct arswitch_softc *sc, int port, int *pvid) 8887330dd0bSAdrian Chadd { 889*749cac13SAdrian Chadd uint32_t reg; 8907330dd0bSAdrian Chadd 891*749cac13SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 892*749cac13SAdrian Chadd 893*749cac13SAdrian Chadd /* 894*749cac13SAdrian Chadd * XXX for now, assuming it's CVID; likely very wrong! 895*749cac13SAdrian Chadd */ 896*749cac13SAdrian Chadd port = port & ETHERSWITCH_VID_MASK; 897*749cac13SAdrian Chadd reg = arswitch_readreg(sc->sc_dev, AR8327_REG_PORT_VLAN0(port)); 898*749cac13SAdrian Chadd reg = reg >> AR8327_PORT_VLAN0_DEF_CVID_S; 899*749cac13SAdrian Chadd reg = reg & 0xfff; 900*749cac13SAdrian Chadd 901*749cac13SAdrian Chadd *pvid = reg; 9027330dd0bSAdrian Chadd return (0); 9037330dd0bSAdrian Chadd } 9047330dd0bSAdrian Chadd 9057330dd0bSAdrian Chadd static int 9067330dd0bSAdrian Chadd ar8327_set_pvid(struct arswitch_softc *sc, int port, int pvid) 9077330dd0bSAdrian Chadd { 908*749cac13SAdrian Chadd uint32_t t; 9097330dd0bSAdrian Chadd 910*749cac13SAdrian Chadd /* Limit pvid to valid values */ 911*749cac13SAdrian Chadd pvid &= 0x7f; 912*749cac13SAdrian Chadd 913*749cac13SAdrian Chadd t = pvid << AR8327_PORT_VLAN0_DEF_SVID_S; 914*749cac13SAdrian Chadd t |= pvid << AR8327_PORT_VLAN0_DEF_CVID_S; 915*749cac13SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_VLAN0(port), t); 916*749cac13SAdrian Chadd 9177330dd0bSAdrian Chadd return (0); 9187330dd0bSAdrian Chadd } 9197330dd0bSAdrian Chadd 9204ff2f60dSAdrian Chadd static int 9214ff2f60dSAdrian Chadd ar8327_atu_flush(struct arswitch_softc *sc) 9224ff2f60dSAdrian Chadd { 9234ff2f60dSAdrian Chadd 9244ff2f60dSAdrian Chadd int ret; 9254ff2f60dSAdrian Chadd 9264ff2f60dSAdrian Chadd ret = arswitch_waitreg(sc->sc_dev, 9274ff2f60dSAdrian Chadd AR8327_REG_ATU_FUNC, 9284ff2f60dSAdrian Chadd AR8327_ATU_FUNC_BUSY, 9294ff2f60dSAdrian Chadd 0, 9304ff2f60dSAdrian Chadd 1000); 9314ff2f60dSAdrian Chadd 9324ff2f60dSAdrian Chadd if (ret) 9334ff2f60dSAdrian Chadd device_printf(sc->sc_dev, "%s: waitreg failed\n", __func__); 9344ff2f60dSAdrian Chadd 9354ff2f60dSAdrian Chadd if (!ret) 9364ff2f60dSAdrian Chadd arswitch_writereg(sc->sc_dev, 9374ff2f60dSAdrian Chadd AR8327_REG_ATU_FUNC, 9384ff2f60dSAdrian Chadd AR8327_ATU_FUNC_OP_FLUSH); 9394ff2f60dSAdrian Chadd return (ret); 9404ff2f60dSAdrian Chadd } 9414ff2f60dSAdrian Chadd 9427330dd0bSAdrian Chadd void 9437330dd0bSAdrian Chadd ar8327_attach(struct arswitch_softc *sc) 9447330dd0bSAdrian Chadd { 9457330dd0bSAdrian Chadd 9467330dd0bSAdrian Chadd sc->hal.arswitch_hw_setup = ar8327_hw_setup; 9477330dd0bSAdrian Chadd sc->hal.arswitch_hw_global_setup = ar8327_hw_global_setup; 9487330dd0bSAdrian Chadd 9497330dd0bSAdrian Chadd sc->hal.arswitch_port_init = ar8327_port_init; 95078549b94SAdrian Chadd 95178549b94SAdrian Chadd sc->hal.arswitch_vlan_getvgroup = ar8327_vlan_getvgroup; 95278549b94SAdrian Chadd sc->hal.arswitch_vlan_setvgroup = ar8327_vlan_setvgroup; 9537330dd0bSAdrian Chadd sc->hal.arswitch_port_vlan_setup = ar8327_port_vlan_setup; 9547330dd0bSAdrian Chadd sc->hal.arswitch_port_vlan_get = ar8327_port_vlan_get; 9557330dd0bSAdrian Chadd 9567330dd0bSAdrian Chadd sc->hal.arswitch_vlan_init_hw = ar8327_reset_vlans; 9577330dd0bSAdrian Chadd sc->hal.arswitch_vlan_get_pvid = ar8327_get_pvid; 9587330dd0bSAdrian Chadd sc->hal.arswitch_vlan_set_pvid = ar8327_set_pvid; 9597330dd0bSAdrian Chadd 960*749cac13SAdrian Chadd sc->hal.arswitch_get_port_vlan = ar8327_vlan_get_port; 961*749cac13SAdrian Chadd sc->hal.arswitch_set_port_vlan = ar8327_vlan_set_port; 962*749cac13SAdrian Chadd 9634ff2f60dSAdrian Chadd sc->hal.arswitch_atu_flush = ar8327_atu_flush; 9644ff2f60dSAdrian Chadd 96578549b94SAdrian Chadd /* 96678549b94SAdrian Chadd * Reading the PHY via the MDIO interface currently doesn't 96778549b94SAdrian Chadd * work correctly. 96878549b94SAdrian Chadd * 96978549b94SAdrian Chadd * So for now, just go direct to the PHY registers themselves. 97078549b94SAdrian Chadd * This has always worked on external devices, but not internal 97178549b94SAdrian Chadd * devices (AR934x, AR724x, AR933x.) 97278549b94SAdrian Chadd */ 97378549b94SAdrian Chadd sc->hal.arswitch_phy_read = arswitch_readphy_external; 97478549b94SAdrian Chadd sc->hal.arswitch_phy_write = arswitch_writephy_external; 97578549b94SAdrian Chadd 9767330dd0bSAdrian Chadd /* Set the switch vlan capabilities. */ 9777330dd0bSAdrian Chadd sc->info.es_vlan_caps = ETHERSWITCH_VLAN_DOT1Q | 9787330dd0bSAdrian Chadd ETHERSWITCH_VLAN_PORT | ETHERSWITCH_VLAN_DOUBLE_TAG; 9797330dd0bSAdrian Chadd sc->info.es_nvlangroups = AR8X16_MAX_VLANS; 9807330dd0bSAdrian Chadd } 981