17330dd0bSAdrian Chadd /*- 2*718cf2ccSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3*718cf2ccSPedro F. Giffuni * 47330dd0bSAdrian Chadd * Copyright (c) 2011-2012 Stefan Bethke. 57330dd0bSAdrian Chadd * Copyright (c) 2014 Adrian Chadd. 67330dd0bSAdrian Chadd * All rights reserved. 77330dd0bSAdrian Chadd * 87330dd0bSAdrian Chadd * Redistribution and use in source and binary forms, with or without 97330dd0bSAdrian Chadd * modification, are permitted provided that the following conditions 107330dd0bSAdrian Chadd * are met: 117330dd0bSAdrian Chadd * 1. Redistributions of source code must retain the above copyright 127330dd0bSAdrian Chadd * notice, this list of conditions and the following disclaimer. 137330dd0bSAdrian Chadd * 2. Redistributions in binary form must reproduce the above copyright 147330dd0bSAdrian Chadd * notice, this list of conditions and the following disclaimer in the 157330dd0bSAdrian Chadd * documentation and/or other materials provided with the distribution. 167330dd0bSAdrian Chadd * 177330dd0bSAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 187330dd0bSAdrian Chadd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 197330dd0bSAdrian Chadd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 207330dd0bSAdrian Chadd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 217330dd0bSAdrian Chadd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 227330dd0bSAdrian Chadd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 237330dd0bSAdrian Chadd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 247330dd0bSAdrian Chadd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 257330dd0bSAdrian Chadd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 267330dd0bSAdrian Chadd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 277330dd0bSAdrian Chadd * SUCH DAMAGE. 287330dd0bSAdrian Chadd * 297330dd0bSAdrian Chadd * $FreeBSD$ 307330dd0bSAdrian Chadd */ 317330dd0bSAdrian Chadd 327330dd0bSAdrian Chadd #include <sys/param.h> 337330dd0bSAdrian Chadd #include <sys/bus.h> 347330dd0bSAdrian Chadd #include <sys/errno.h> 357330dd0bSAdrian Chadd #include <sys/kernel.h> 367330dd0bSAdrian Chadd #include <sys/module.h> 377330dd0bSAdrian Chadd #include <sys/socket.h> 387330dd0bSAdrian Chadd #include <sys/sockio.h> 397330dd0bSAdrian Chadd #include <sys/sysctl.h> 407330dd0bSAdrian Chadd #include <sys/systm.h> 417330dd0bSAdrian Chadd 427330dd0bSAdrian Chadd #include <net/if.h> 437330dd0bSAdrian Chadd #include <net/if_arp.h> 447330dd0bSAdrian Chadd #include <net/ethernet.h> 457330dd0bSAdrian Chadd #include <net/if_dl.h> 467330dd0bSAdrian Chadd #include <net/if_media.h> 477330dd0bSAdrian Chadd #include <net/if_types.h> 487330dd0bSAdrian Chadd 497330dd0bSAdrian Chadd #include <machine/bus.h> 50efce3748SRui Paulo #include <dev/iicbus/iic.h> 517330dd0bSAdrian Chadd #include <dev/iicbus/iiconf.h> 527330dd0bSAdrian Chadd #include <dev/iicbus/iicbus.h> 537330dd0bSAdrian Chadd #include <dev/mii/mii.h> 547330dd0bSAdrian Chadd #include <dev/mii/miivar.h> 5571e8eac4SAdrian Chadd #include <dev/mdio/mdio.h> 567330dd0bSAdrian Chadd 577330dd0bSAdrian Chadd #include <dev/etherswitch/etherswitch.h> 587330dd0bSAdrian Chadd 597330dd0bSAdrian Chadd #include <dev/etherswitch/arswitch/arswitchreg.h> 607330dd0bSAdrian Chadd #include <dev/etherswitch/arswitch/arswitchvar.h> 617330dd0bSAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_reg.h> 6278549b94SAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_phy.h> 6378549b94SAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_vlans.h> 6478549b94SAdrian Chadd 657330dd0bSAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_8327.h> 667330dd0bSAdrian Chadd 677330dd0bSAdrian Chadd #include "mdio_if.h" 687330dd0bSAdrian Chadd #include "miibus_if.h" 697330dd0bSAdrian Chadd #include "etherswitch_if.h" 707330dd0bSAdrian Chadd 718f1cf028SAdrian Chadd /* 728f1cf028SAdrian Chadd * AR8327 TODO: 738f1cf028SAdrian Chadd * 748f1cf028SAdrian Chadd * There should be a default hardware setup hint set for the default 758f1cf028SAdrian Chadd * switch config. Otherwise the default is "all ports in one vlangroup", 768f1cf028SAdrian Chadd * which means both CPU ports can see each other and that will quickly 778f1cf028SAdrian Chadd * lead to traffic storms/loops. 788f1cf028SAdrian Chadd */ 79036e1c76SAdrian Chadd 80c94dc808SAdrian Chadd /* Map port+led to register+shift */ 81c94dc808SAdrian Chadd struct ar8327_led_mapping ar8327_led_mapping[AR8327_NUM_PHYS][ETHERSWITCH_PORT_MAX_LEDS] = 82c94dc808SAdrian Chadd { 83c94dc808SAdrian Chadd { /* PHY0 */ 84c94dc808SAdrian Chadd {AR8327_REG_LED_CTRL0, 14 }, 85c94dc808SAdrian Chadd {AR8327_REG_LED_CTRL1, 14 }, 86c94dc808SAdrian Chadd {AR8327_REG_LED_CTRL2, 14 } 87c94dc808SAdrian Chadd }, 88c94dc808SAdrian Chadd { /* PHY1 */ 89c94dc808SAdrian Chadd {AR8327_REG_LED_CTRL3, 8 }, 90c94dc808SAdrian Chadd {AR8327_REG_LED_CTRL3, 10 }, 91c94dc808SAdrian Chadd {AR8327_REG_LED_CTRL3, 12 } 92c94dc808SAdrian Chadd }, 93c94dc808SAdrian Chadd { /* PHY2 */ 94c94dc808SAdrian Chadd {AR8327_REG_LED_CTRL3, 14 }, 95c94dc808SAdrian Chadd {AR8327_REG_LED_CTRL3, 16 }, 96c94dc808SAdrian Chadd {AR8327_REG_LED_CTRL3, 18 } 97c94dc808SAdrian Chadd }, 98c94dc808SAdrian Chadd { /* PHY3 */ 99c94dc808SAdrian Chadd {AR8327_REG_LED_CTRL3, 20 }, 100c94dc808SAdrian Chadd {AR8327_REG_LED_CTRL3, 22 }, 101c94dc808SAdrian Chadd {AR8327_REG_LED_CTRL3, 24 } 102c94dc808SAdrian Chadd }, 103c94dc808SAdrian Chadd { /* PHY4 */ 104c94dc808SAdrian Chadd {AR8327_REG_LED_CTRL0, 30 }, 105c94dc808SAdrian Chadd {AR8327_REG_LED_CTRL1, 30 }, 106c94dc808SAdrian Chadd {AR8327_REG_LED_CTRL2, 30 } 107c94dc808SAdrian Chadd } 108c94dc808SAdrian Chadd }; 109c94dc808SAdrian Chadd 110036e1c76SAdrian Chadd static int 111036e1c76SAdrian Chadd ar8327_vlan_op(struct arswitch_softc *sc, uint32_t op, uint32_t vid, 112036e1c76SAdrian Chadd uint32_t data) 113036e1c76SAdrian Chadd { 114036e1c76SAdrian Chadd int err; 115036e1c76SAdrian Chadd 116036e1c76SAdrian Chadd /* 117036e1c76SAdrian Chadd * Wait for the "done" bit to finish. 118036e1c76SAdrian Chadd */ 119036e1c76SAdrian Chadd if (arswitch_waitreg(sc->sc_dev, AR8327_REG_VTU_FUNC1, 120036e1c76SAdrian Chadd AR8327_VTU_FUNC1_BUSY, 0, 5)) 121036e1c76SAdrian Chadd return (EBUSY); 122036e1c76SAdrian Chadd 123036e1c76SAdrian Chadd /* 124036e1c76SAdrian Chadd * If it's a "load" operation, then ensure 'data' is loaded 125036e1c76SAdrian Chadd * in first. 126036e1c76SAdrian Chadd */ 127036e1c76SAdrian Chadd if ((op & AR8327_VTU_FUNC1_OP) == AR8327_VTU_FUNC1_OP_LOAD) { 128036e1c76SAdrian Chadd err = arswitch_writereg(sc->sc_dev, AR8327_REG_VTU_FUNC0, data); 129036e1c76SAdrian Chadd if (err) 130036e1c76SAdrian Chadd return (err); 131036e1c76SAdrian Chadd } 132036e1c76SAdrian Chadd 133036e1c76SAdrian Chadd /* 134036e1c76SAdrian Chadd * Set the VID. 135036e1c76SAdrian Chadd */ 136036e1c76SAdrian Chadd op |= ((vid & 0xfff) << AR8327_VTU_FUNC1_VID_S); 137036e1c76SAdrian Chadd 138036e1c76SAdrian Chadd /* 139036e1c76SAdrian Chadd * Set busy bit to start loading in the command. 140036e1c76SAdrian Chadd */ 141036e1c76SAdrian Chadd op |= AR8327_VTU_FUNC1_BUSY; 142036e1c76SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_VTU_FUNC1, op); 143036e1c76SAdrian Chadd 144036e1c76SAdrian Chadd /* 145036e1c76SAdrian Chadd * Finally - wait for it to load. 146036e1c76SAdrian Chadd */ 147036e1c76SAdrian Chadd if (arswitch_waitreg(sc->sc_dev, AR8327_REG_VTU_FUNC1, 148036e1c76SAdrian Chadd AR8327_VTU_FUNC1_BUSY, 0, 5)) 149036e1c76SAdrian Chadd return (EBUSY); 150036e1c76SAdrian Chadd 151036e1c76SAdrian Chadd return (0); 152036e1c76SAdrian Chadd } 153036e1c76SAdrian Chadd 1547330dd0bSAdrian Chadd static void 1557330dd0bSAdrian Chadd ar8327_phy_fixup(struct arswitch_softc *sc, int phy) 1567330dd0bSAdrian Chadd { 157db37238fSAdrian Chadd if (bootverbose) 158db37238fSAdrian Chadd device_printf(sc->sc_dev, 159db37238fSAdrian Chadd "%s: called; phy=%d; chiprev=%d\n", __func__, 160db37238fSAdrian Chadd phy, 161db37238fSAdrian Chadd sc->chip_rev); 1627330dd0bSAdrian Chadd switch (sc->chip_rev) { 1637330dd0bSAdrian Chadd case 1: 1647330dd0bSAdrian Chadd /* For 100M waveform */ 1657330dd0bSAdrian Chadd arswitch_writedbg(sc->sc_dev, phy, 0, 0x02ea); 1667330dd0bSAdrian Chadd /* Turn on Gigabit clock */ 1677330dd0bSAdrian Chadd arswitch_writedbg(sc->sc_dev, phy, 0x3d, 0x68a0); 1687330dd0bSAdrian Chadd break; 1697330dd0bSAdrian Chadd 1707330dd0bSAdrian Chadd case 2: 1717330dd0bSAdrian Chadd arswitch_writemmd(sc->sc_dev, phy, 0x7, 0x3c); 1727330dd0bSAdrian Chadd arswitch_writemmd(sc->sc_dev, phy, 0x4007, 0x0); 1737330dd0bSAdrian Chadd /* fallthrough */ 1747330dd0bSAdrian Chadd case 4: 1757330dd0bSAdrian Chadd arswitch_writemmd(sc->sc_dev, phy, 0x3, 0x800d); 1767330dd0bSAdrian Chadd arswitch_writemmd(sc->sc_dev, phy, 0x4003, 0x803f); 1777330dd0bSAdrian Chadd 1787330dd0bSAdrian Chadd arswitch_writedbg(sc->sc_dev, phy, 0x3d, 0x6860); 1797330dd0bSAdrian Chadd arswitch_writedbg(sc->sc_dev, phy, 0x5, 0x2c46); 1807330dd0bSAdrian Chadd arswitch_writedbg(sc->sc_dev, phy, 0x3c, 0x6000); 1817330dd0bSAdrian Chadd break; 1827330dd0bSAdrian Chadd } 1837330dd0bSAdrian Chadd } 1847330dd0bSAdrian Chadd 1857330dd0bSAdrian Chadd static uint32_t 1867330dd0bSAdrian Chadd ar8327_get_pad_cfg(struct ar8327_pad_cfg *cfg) 1877330dd0bSAdrian Chadd { 1887330dd0bSAdrian Chadd uint32_t t; 1897330dd0bSAdrian Chadd 1907330dd0bSAdrian Chadd if (!cfg) 1917330dd0bSAdrian Chadd return (0); 1927330dd0bSAdrian Chadd 1937330dd0bSAdrian Chadd t = 0; 1947330dd0bSAdrian Chadd switch (cfg->mode) { 1957330dd0bSAdrian Chadd case AR8327_PAD_NC: 1967330dd0bSAdrian Chadd break; 1977330dd0bSAdrian Chadd 1987330dd0bSAdrian Chadd case AR8327_PAD_MAC2MAC_MII: 1997330dd0bSAdrian Chadd t = AR8327_PAD_MAC_MII_EN; 2007330dd0bSAdrian Chadd if (cfg->rxclk_sel) 2017330dd0bSAdrian Chadd t |= AR8327_PAD_MAC_MII_RXCLK_SEL; 2027330dd0bSAdrian Chadd if (cfg->txclk_sel) 2037330dd0bSAdrian Chadd t |= AR8327_PAD_MAC_MII_TXCLK_SEL; 2047330dd0bSAdrian Chadd break; 2057330dd0bSAdrian Chadd 2067330dd0bSAdrian Chadd case AR8327_PAD_MAC2MAC_GMII: 2077330dd0bSAdrian Chadd t = AR8327_PAD_MAC_GMII_EN; 2087330dd0bSAdrian Chadd if (cfg->rxclk_sel) 2097330dd0bSAdrian Chadd t |= AR8327_PAD_MAC_GMII_RXCLK_SEL; 2107330dd0bSAdrian Chadd if (cfg->txclk_sel) 2117330dd0bSAdrian Chadd t |= AR8327_PAD_MAC_GMII_TXCLK_SEL; 2127330dd0bSAdrian Chadd break; 2137330dd0bSAdrian Chadd 2147330dd0bSAdrian Chadd case AR8327_PAD_MAC_SGMII: 2157330dd0bSAdrian Chadd t = AR8327_PAD_SGMII_EN; 2167330dd0bSAdrian Chadd 2177330dd0bSAdrian Chadd /* 2189ab21e32SAdrian Chadd * WAR for the Qualcomm Atheros AP136 board. 2197330dd0bSAdrian Chadd * It seems that RGMII TX/RX delay settings needs to be 2207330dd0bSAdrian Chadd * applied for SGMII mode as well, The ethernet is not 2217330dd0bSAdrian Chadd * reliable without this. 2227330dd0bSAdrian Chadd */ 2237330dd0bSAdrian Chadd t |= cfg->txclk_delay_sel << AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S; 2247330dd0bSAdrian Chadd t |= cfg->rxclk_delay_sel << AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S; 2257330dd0bSAdrian Chadd if (cfg->rxclk_delay_en) 2267330dd0bSAdrian Chadd t |= AR8327_PAD_RGMII_RXCLK_DELAY_EN; 2277330dd0bSAdrian Chadd if (cfg->txclk_delay_en) 2287330dd0bSAdrian Chadd t |= AR8327_PAD_RGMII_TXCLK_DELAY_EN; 2297330dd0bSAdrian Chadd 2307330dd0bSAdrian Chadd if (cfg->sgmii_delay_en) 2317330dd0bSAdrian Chadd t |= AR8327_PAD_SGMII_DELAY_EN; 2327330dd0bSAdrian Chadd 2337330dd0bSAdrian Chadd break; 2347330dd0bSAdrian Chadd 2357330dd0bSAdrian Chadd case AR8327_PAD_MAC2PHY_MII: 2367330dd0bSAdrian Chadd t = AR8327_PAD_PHY_MII_EN; 2377330dd0bSAdrian Chadd if (cfg->rxclk_sel) 2387330dd0bSAdrian Chadd t |= AR8327_PAD_PHY_MII_RXCLK_SEL; 2397330dd0bSAdrian Chadd if (cfg->txclk_sel) 2407330dd0bSAdrian Chadd t |= AR8327_PAD_PHY_MII_TXCLK_SEL; 2417330dd0bSAdrian Chadd break; 2427330dd0bSAdrian Chadd 2437330dd0bSAdrian Chadd case AR8327_PAD_MAC2PHY_GMII: 2447330dd0bSAdrian Chadd t = AR8327_PAD_PHY_GMII_EN; 2457330dd0bSAdrian Chadd if (cfg->pipe_rxclk_sel) 2467330dd0bSAdrian Chadd t |= AR8327_PAD_PHY_GMII_PIPE_RXCLK_SEL; 2477330dd0bSAdrian Chadd if (cfg->rxclk_sel) 2487330dd0bSAdrian Chadd t |= AR8327_PAD_PHY_GMII_RXCLK_SEL; 2497330dd0bSAdrian Chadd if (cfg->txclk_sel) 2507330dd0bSAdrian Chadd t |= AR8327_PAD_PHY_GMII_TXCLK_SEL; 2517330dd0bSAdrian Chadd break; 2527330dd0bSAdrian Chadd 2537330dd0bSAdrian Chadd case AR8327_PAD_MAC_RGMII: 2547330dd0bSAdrian Chadd t = AR8327_PAD_RGMII_EN; 2557330dd0bSAdrian Chadd t |= cfg->txclk_delay_sel << AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S; 2567330dd0bSAdrian Chadd t |= cfg->rxclk_delay_sel << AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S; 2577330dd0bSAdrian Chadd if (cfg->rxclk_delay_en) 2587330dd0bSAdrian Chadd t |= AR8327_PAD_RGMII_RXCLK_DELAY_EN; 2597330dd0bSAdrian Chadd if (cfg->txclk_delay_en) 2607330dd0bSAdrian Chadd t |= AR8327_PAD_RGMII_TXCLK_DELAY_EN; 2617330dd0bSAdrian Chadd break; 2627330dd0bSAdrian Chadd 2637330dd0bSAdrian Chadd case AR8327_PAD_PHY_GMII: 2647330dd0bSAdrian Chadd t = AR8327_PAD_PHYX_GMII_EN; 2657330dd0bSAdrian Chadd break; 2667330dd0bSAdrian Chadd 2677330dd0bSAdrian Chadd case AR8327_PAD_PHY_RGMII: 2687330dd0bSAdrian Chadd t = AR8327_PAD_PHYX_RGMII_EN; 2697330dd0bSAdrian Chadd break; 2707330dd0bSAdrian Chadd 2717330dd0bSAdrian Chadd case AR8327_PAD_PHY_MII: 2727330dd0bSAdrian Chadd t = AR8327_PAD_PHYX_MII_EN; 2737330dd0bSAdrian Chadd break; 2747330dd0bSAdrian Chadd } 2757330dd0bSAdrian Chadd 2767330dd0bSAdrian Chadd return (t); 2777330dd0bSAdrian Chadd } 2787330dd0bSAdrian Chadd 2797330dd0bSAdrian Chadd /* 2807330dd0bSAdrian Chadd * Map the hard-coded port config from the switch setup to 2817330dd0bSAdrian Chadd * the chipset port config (status, duplex, flow, etc.) 2827330dd0bSAdrian Chadd */ 2837330dd0bSAdrian Chadd static uint32_t 2847330dd0bSAdrian Chadd ar8327_get_port_init_status(struct ar8327_port_cfg *cfg) 2857330dd0bSAdrian Chadd { 2867330dd0bSAdrian Chadd uint32_t t; 2877330dd0bSAdrian Chadd 2887330dd0bSAdrian Chadd if (!cfg->force_link) 2897330dd0bSAdrian Chadd return (AR8X16_PORT_STS_LINK_AUTO); 2907330dd0bSAdrian Chadd 2917330dd0bSAdrian Chadd t = AR8X16_PORT_STS_TXMAC | AR8X16_PORT_STS_RXMAC; 2927330dd0bSAdrian Chadd t |= cfg->duplex ? AR8X16_PORT_STS_DUPLEX : 0; 2937330dd0bSAdrian Chadd t |= cfg->rxpause ? AR8X16_PORT_STS_RXFLOW : 0; 2947330dd0bSAdrian Chadd t |= cfg->txpause ? AR8X16_PORT_STS_TXFLOW : 0; 2957330dd0bSAdrian Chadd 2967330dd0bSAdrian Chadd switch (cfg->speed) { 2977330dd0bSAdrian Chadd case AR8327_PORT_SPEED_10: 2987330dd0bSAdrian Chadd t |= AR8X16_PORT_STS_SPEED_10; 2997330dd0bSAdrian Chadd break; 3007330dd0bSAdrian Chadd case AR8327_PORT_SPEED_100: 3017330dd0bSAdrian Chadd t |= AR8X16_PORT_STS_SPEED_100; 3027330dd0bSAdrian Chadd break; 3037330dd0bSAdrian Chadd case AR8327_PORT_SPEED_1000: 3047330dd0bSAdrian Chadd t |= AR8X16_PORT_STS_SPEED_1000; 3057330dd0bSAdrian Chadd break; 3067330dd0bSAdrian Chadd } 3077330dd0bSAdrian Chadd 3087330dd0bSAdrian Chadd return (t); 3097330dd0bSAdrian Chadd } 3109ab21e32SAdrian Chadd 3119ab21e32SAdrian Chadd /* 312f9950f9aSAdrian Chadd * Fetch the port data for the given port. 313f9950f9aSAdrian Chadd * 314f9950f9aSAdrian Chadd * This goes and does dirty things with the hints space 315f9950f9aSAdrian Chadd * to determine what the configuration parameters should be. 316f9950f9aSAdrian Chadd * 317f9950f9aSAdrian Chadd * Returns 1 if the structure was successfully parsed and 318f9950f9aSAdrian Chadd * the contents are valid; 0 otherwise. 319f9950f9aSAdrian Chadd */ 320f9950f9aSAdrian Chadd static int 321f9950f9aSAdrian Chadd ar8327_fetch_pdata_port(struct arswitch_softc *sc, 322f9950f9aSAdrian Chadd struct ar8327_port_cfg *pcfg, 323f9950f9aSAdrian Chadd int port) 324f9950f9aSAdrian Chadd { 325f9950f9aSAdrian Chadd int val; 326f9950f9aSAdrian Chadd char sbuf[128]; 327f9950f9aSAdrian Chadd 328f9950f9aSAdrian Chadd /* Check if force_link exists */ 329f9950f9aSAdrian Chadd val = 0; 330f9950f9aSAdrian Chadd snprintf(sbuf, 128, "port.%d.force_link", port); 331f9950f9aSAdrian Chadd (void) resource_int_value(device_get_name(sc->sc_dev), 332f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 333f9950f9aSAdrian Chadd sbuf, &val); 334f9950f9aSAdrian Chadd if (val != 1) 335f9950f9aSAdrian Chadd return (0); 336f9950f9aSAdrian Chadd pcfg->force_link = 1; 337f9950f9aSAdrian Chadd 338f9950f9aSAdrian Chadd /* force_link is set; let's parse the rest of the fields */ 339f9950f9aSAdrian Chadd snprintf(sbuf, 128, "port.%d.speed", port); 340f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 341f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 342f9950f9aSAdrian Chadd sbuf, &val) == 0) { 343f9950f9aSAdrian Chadd switch (val) { 344f9950f9aSAdrian Chadd case 10: 345f9950f9aSAdrian Chadd pcfg->speed = AR8327_PORT_SPEED_10; 346f9950f9aSAdrian Chadd break; 347f9950f9aSAdrian Chadd case 100: 348f9950f9aSAdrian Chadd pcfg->speed = AR8327_PORT_SPEED_100; 349f9950f9aSAdrian Chadd break; 350f9950f9aSAdrian Chadd case 1000: 351f9950f9aSAdrian Chadd pcfg->speed = AR8327_PORT_SPEED_1000; 352f9950f9aSAdrian Chadd break; 353f9950f9aSAdrian Chadd default: 354f9950f9aSAdrian Chadd device_printf(sc->sc_dev, 355f9950f9aSAdrian Chadd "%s: invalid port %d duplex value (%d)\n", 356f9950f9aSAdrian Chadd __func__, 357f9950f9aSAdrian Chadd port, 358f9950f9aSAdrian Chadd val); 359f9950f9aSAdrian Chadd return (0); 360f9950f9aSAdrian Chadd } 361f9950f9aSAdrian Chadd } 362f9950f9aSAdrian Chadd 363f9950f9aSAdrian Chadd snprintf(sbuf, 128, "port.%d.duplex", port); 364f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 365f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 366f9950f9aSAdrian Chadd sbuf, &val) == 0) 367f9950f9aSAdrian Chadd pcfg->duplex = val; 368f9950f9aSAdrian Chadd 369f9950f9aSAdrian Chadd snprintf(sbuf, 128, "port.%d.txpause", port); 370f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 371f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 372f9950f9aSAdrian Chadd sbuf, &val) == 0) 373f9950f9aSAdrian Chadd pcfg->txpause = val; 374f9950f9aSAdrian Chadd 375f9950f9aSAdrian Chadd snprintf(sbuf, 128, "port.%d.rxpause", port); 376f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 377f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 378f9950f9aSAdrian Chadd sbuf, &val) == 0) 379f9950f9aSAdrian Chadd pcfg->rxpause = val; 380f9950f9aSAdrian Chadd 38178549b94SAdrian Chadd #if 1 382f9950f9aSAdrian Chadd device_printf(sc->sc_dev, 383f9950f9aSAdrian Chadd "%s: port %d: speed=%d, duplex=%d, txpause=%d, rxpause=%d\n", 384f9950f9aSAdrian Chadd __func__, 385f9950f9aSAdrian Chadd port, 386f9950f9aSAdrian Chadd pcfg->speed, 387f9950f9aSAdrian Chadd pcfg->duplex, 388f9950f9aSAdrian Chadd pcfg->txpause, 389f9950f9aSAdrian Chadd pcfg->rxpause); 390f9950f9aSAdrian Chadd #endif 391f9950f9aSAdrian Chadd 392f9950f9aSAdrian Chadd return (1); 393f9950f9aSAdrian Chadd } 394f9950f9aSAdrian Chadd 395f9950f9aSAdrian Chadd /* 396f9950f9aSAdrian Chadd * Parse the pad configuration from the boot hints. 397f9950f9aSAdrian Chadd * 398f9950f9aSAdrian Chadd * The (mostly optional) fields are: 399f9950f9aSAdrian Chadd * 400f9950f9aSAdrian Chadd * uint32_t mode; 401f9950f9aSAdrian Chadd * uint32_t rxclk_sel; 402f9950f9aSAdrian Chadd * uint32_t txclk_sel; 403f9950f9aSAdrian Chadd * uint32_t txclk_delay_sel; 404f9950f9aSAdrian Chadd * uint32_t rxclk_delay_sel; 405f9950f9aSAdrian Chadd * uint32_t txclk_delay_en; 406f9950f9aSAdrian Chadd * uint32_t rxclk_delay_en; 407f9950f9aSAdrian Chadd * uint32_t sgmii_delay_en; 408f9950f9aSAdrian Chadd * uint32_t pipe_rxclk_sel; 409f9950f9aSAdrian Chadd * 410f9950f9aSAdrian Chadd * If mode isn't in the hints, 0 is returned. 411f9950f9aSAdrian Chadd * Else the structure is fleshed out and 1 is returned. 412f9950f9aSAdrian Chadd */ 413f9950f9aSAdrian Chadd static int 414f9950f9aSAdrian Chadd ar8327_fetch_pdata_pad(struct arswitch_softc *sc, 415f9950f9aSAdrian Chadd struct ar8327_pad_cfg *pc, 416f9950f9aSAdrian Chadd int pad) 417f9950f9aSAdrian Chadd { 418f9950f9aSAdrian Chadd int val; 419f9950f9aSAdrian Chadd char sbuf[128]; 420f9950f9aSAdrian Chadd 421f9950f9aSAdrian Chadd /* Check if mode exists */ 422f9950f9aSAdrian Chadd val = 0; 423f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.mode", pad); 424f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 425f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 426f9950f9aSAdrian Chadd sbuf, &val) != 0) 427f9950f9aSAdrian Chadd return (0); 428f9950f9aSAdrian Chadd 429f9950f9aSAdrian Chadd /* assume that 'mode' exists and was found */ 430f9950f9aSAdrian Chadd pc->mode = val; 431f9950f9aSAdrian Chadd 432f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.rxclk_sel", pad); 433f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 434f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 435f9950f9aSAdrian Chadd sbuf, &val) == 0) 436f9950f9aSAdrian Chadd pc->rxclk_sel = val; 437f9950f9aSAdrian Chadd 438f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.txclk_sel", pad); 439f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 440f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 441f9950f9aSAdrian Chadd sbuf, &val) == 0) 442f9950f9aSAdrian Chadd pc->txclk_sel = val; 443f9950f9aSAdrian Chadd 444f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.txclk_delay_sel", pad); 445f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 446f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 447f9950f9aSAdrian Chadd sbuf, &val) == 0) 448f9950f9aSAdrian Chadd pc->txclk_delay_sel = val; 449f9950f9aSAdrian Chadd 450f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.rxclk_delay_sel", pad); 451f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 452f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 453f9950f9aSAdrian Chadd sbuf, &val) == 0) 454f9950f9aSAdrian Chadd pc->rxclk_delay_sel = val; 455f9950f9aSAdrian Chadd 456f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.txclk_delay_en", pad); 457f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 458f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 459f9950f9aSAdrian Chadd sbuf, &val) == 0) 460f9950f9aSAdrian Chadd pc->txclk_delay_en = val; 461f9950f9aSAdrian Chadd 462f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.rxclk_delay_en", pad); 463f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 464f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 465f9950f9aSAdrian Chadd sbuf, &val) == 0) 466f9950f9aSAdrian Chadd pc->rxclk_delay_en = val; 467f9950f9aSAdrian Chadd 468f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.sgmii_delay_en", pad); 469f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 470f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 471f9950f9aSAdrian Chadd sbuf, &val) == 0) 472f9950f9aSAdrian Chadd pc->sgmii_delay_en = val; 473f9950f9aSAdrian Chadd 474f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.pipe_rxclk_sel", pad); 475f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 476f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev), 477f9950f9aSAdrian Chadd sbuf, &val) == 0) 478f9950f9aSAdrian Chadd pc->pipe_rxclk_sel = val; 479f9950f9aSAdrian Chadd 480db37238fSAdrian Chadd if (bootverbose) { 481f9950f9aSAdrian Chadd device_printf(sc->sc_dev, 482f9950f9aSAdrian Chadd "%s: pad %d: mode=%d, rxclk_sel=%d, txclk_sel=%d, " 483f9950f9aSAdrian Chadd "txclk_delay_sel=%d, rxclk_delay_sel=%d, txclk_delay_en=%d, " 484f9950f9aSAdrian Chadd "rxclk_enable_en=%d, sgmii_delay_en=%d, pipe_rxclk_sel=%d\n", 485f9950f9aSAdrian Chadd __func__, 486f9950f9aSAdrian Chadd pad, 487f9950f9aSAdrian Chadd pc->mode, 488f9950f9aSAdrian Chadd pc->rxclk_sel, 489f9950f9aSAdrian Chadd pc->txclk_sel, 490f9950f9aSAdrian Chadd pc->txclk_delay_sel, 491f9950f9aSAdrian Chadd pc->rxclk_delay_sel, 492f9950f9aSAdrian Chadd pc->txclk_delay_en, 493f9950f9aSAdrian Chadd pc->rxclk_delay_en, 494f9950f9aSAdrian Chadd pc->sgmii_delay_en, 495f9950f9aSAdrian Chadd pc->pipe_rxclk_sel); 496db37238fSAdrian Chadd } 497f9950f9aSAdrian Chadd 498f9950f9aSAdrian Chadd return (1); 499f9950f9aSAdrian Chadd } 500f9950f9aSAdrian Chadd 501f9950f9aSAdrian Chadd /* 502810bdeddSAdrian Chadd * Fetch the SGMII configuration block from the boot hints. 503810bdeddSAdrian Chadd */ 504810bdeddSAdrian Chadd static int 505810bdeddSAdrian Chadd ar8327_fetch_pdata_sgmii(struct arswitch_softc *sc, 506810bdeddSAdrian Chadd struct ar8327_sgmii_cfg *scfg) 507810bdeddSAdrian Chadd { 508810bdeddSAdrian Chadd int val; 509810bdeddSAdrian Chadd 510810bdeddSAdrian Chadd /* sgmii_ctrl */ 511810bdeddSAdrian Chadd val = 0; 512810bdeddSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 513810bdeddSAdrian Chadd device_get_unit(sc->sc_dev), 514810bdeddSAdrian Chadd "sgmii.ctrl", &val) != 0) 515810bdeddSAdrian Chadd return (0); 516810bdeddSAdrian Chadd scfg->sgmii_ctrl = val; 517810bdeddSAdrian Chadd 518810bdeddSAdrian Chadd /* serdes_aen */ 519810bdeddSAdrian Chadd val = 0; 520810bdeddSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 521810bdeddSAdrian Chadd device_get_unit(sc->sc_dev), 522810bdeddSAdrian Chadd "sgmii.serdes_aen", &val) != 0) 523810bdeddSAdrian Chadd return (0); 524810bdeddSAdrian Chadd scfg->serdes_aen = val; 525810bdeddSAdrian Chadd 526810bdeddSAdrian Chadd return (1); 527810bdeddSAdrian Chadd } 528810bdeddSAdrian Chadd 529810bdeddSAdrian Chadd /* 530b67ba111SAdrian Chadd * Fetch the LED configuration from the boot hints. 531b67ba111SAdrian Chadd */ 532b67ba111SAdrian Chadd static int 533b67ba111SAdrian Chadd ar8327_fetch_pdata_led(struct arswitch_softc *sc, 534b67ba111SAdrian Chadd struct ar8327_led_cfg *lcfg) 535b67ba111SAdrian Chadd { 536b67ba111SAdrian Chadd int val; 537b67ba111SAdrian Chadd 538b67ba111SAdrian Chadd val = 0; 539b67ba111SAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 540b67ba111SAdrian Chadd device_get_unit(sc->sc_dev), 541b67ba111SAdrian Chadd "led.ctrl0", &val) != 0) 542b67ba111SAdrian Chadd return (0); 543b67ba111SAdrian Chadd lcfg->led_ctrl0 = val; 544b67ba111SAdrian Chadd 545b67ba111SAdrian Chadd val = 0; 546b67ba111SAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 547b67ba111SAdrian Chadd device_get_unit(sc->sc_dev), 548b67ba111SAdrian Chadd "led.ctrl1", &val) != 0) 549b67ba111SAdrian Chadd return (0); 550b67ba111SAdrian Chadd lcfg->led_ctrl1 = val; 551b67ba111SAdrian Chadd 552b67ba111SAdrian Chadd val = 0; 553b67ba111SAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 554b67ba111SAdrian Chadd device_get_unit(sc->sc_dev), 555b67ba111SAdrian Chadd "led.ctrl2", &val) != 0) 556b67ba111SAdrian Chadd return (0); 557b67ba111SAdrian Chadd lcfg->led_ctrl2 = val; 558b67ba111SAdrian Chadd 559b67ba111SAdrian Chadd val = 0; 560b67ba111SAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 561b67ba111SAdrian Chadd device_get_unit(sc->sc_dev), 562b67ba111SAdrian Chadd "led.ctrl3", &val) != 0) 563b67ba111SAdrian Chadd return (0); 564b67ba111SAdrian Chadd lcfg->led_ctrl3 = val; 565b67ba111SAdrian Chadd 566b67ba111SAdrian Chadd val = 0; 567b67ba111SAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev), 568b67ba111SAdrian Chadd device_get_unit(sc->sc_dev), 569b67ba111SAdrian Chadd "led.open_drain", &val) != 0) 570b67ba111SAdrian Chadd return (0); 571b67ba111SAdrian Chadd lcfg->open_drain = val; 572b67ba111SAdrian Chadd 573b67ba111SAdrian Chadd return (1); 574b67ba111SAdrian Chadd } 575b67ba111SAdrian Chadd 576b67ba111SAdrian Chadd /* 5779ab21e32SAdrian Chadd * Initialise the ar8327 specific hardware features from 5789ab21e32SAdrian Chadd * the hints provided in the boot environment. 5799ab21e32SAdrian Chadd */ 5809ab21e32SAdrian Chadd static int 5819ab21e32SAdrian Chadd ar8327_init_pdata(struct arswitch_softc *sc) 5829ab21e32SAdrian Chadd { 5839ab21e32SAdrian Chadd struct ar8327_pad_cfg pc; 5849ab21e32SAdrian Chadd struct ar8327_port_cfg port_cfg; 585810bdeddSAdrian Chadd struct ar8327_sgmii_cfg scfg; 586b67ba111SAdrian Chadd struct ar8327_led_cfg lcfg; 587810bdeddSAdrian Chadd uint32_t t, new_pos, pos; 5889ab21e32SAdrian Chadd 589f9950f9aSAdrian Chadd /* Port 0 */ 5909ab21e32SAdrian Chadd bzero(&port_cfg, sizeof(port_cfg)); 591f9950f9aSAdrian Chadd sc->ar8327.port0_status = 0; 592f9950f9aSAdrian Chadd if (ar8327_fetch_pdata_port(sc, &port_cfg, 0)) 5939ab21e32SAdrian Chadd sc->ar8327.port0_status = ar8327_get_port_init_status(&port_cfg); 5949ab21e32SAdrian Chadd 595f9950f9aSAdrian Chadd /* Port 6 */ 5969ab21e32SAdrian Chadd bzero(&port_cfg, sizeof(port_cfg)); 597f9950f9aSAdrian Chadd sc->ar8327.port6_status = 0; 598f9950f9aSAdrian Chadd if (ar8327_fetch_pdata_port(sc, &port_cfg, 6)) 5999ab21e32SAdrian Chadd sc->ar8327.port6_status = ar8327_get_port_init_status(&port_cfg); 6009ab21e32SAdrian Chadd 6019ab21e32SAdrian Chadd /* Pad 0 */ 6029ab21e32SAdrian Chadd bzero(&pc, sizeof(pc)); 603f9950f9aSAdrian Chadd t = 0; 604f9950f9aSAdrian Chadd if (ar8327_fetch_pdata_pad(sc, &pc, 0)) 6059ab21e32SAdrian Chadd t = ar8327_get_pad_cfg(&pc); 6069ab21e32SAdrian Chadd #if 0 6079ab21e32SAdrian Chadd if (AR8X16_IS_SWITCH(sc, AR8337)) 6089ab21e32SAdrian Chadd t |= AR8337_PAD_MAC06_EXCHANGE_EN; 6097330dd0bSAdrian Chadd #endif 6109ab21e32SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PAD0_MODE, t); 6119ab21e32SAdrian Chadd 6129ab21e32SAdrian Chadd /* Pad 5 */ 6139ab21e32SAdrian Chadd bzero(&pc, sizeof(pc)); 614f9950f9aSAdrian Chadd t = 0; 615f9950f9aSAdrian Chadd if (ar8327_fetch_pdata_pad(sc, &pc, 5)) 6169ab21e32SAdrian Chadd t = ar8327_get_pad_cfg(&pc); 6179ab21e32SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PAD5_MODE, t); 6189ab21e32SAdrian Chadd 6199ab21e32SAdrian Chadd /* Pad 6 */ 6209ab21e32SAdrian Chadd bzero(&pc, sizeof(pc)); 621f9950f9aSAdrian Chadd t = 0; 622f9950f9aSAdrian Chadd if (ar8327_fetch_pdata_pad(sc, &pc, 6)) 6239ab21e32SAdrian Chadd t = ar8327_get_pad_cfg(&pc); 6249ab21e32SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PAD6_MODE, t); 6259ab21e32SAdrian Chadd 626810bdeddSAdrian Chadd pos = arswitch_readreg(sc->sc_dev, AR8327_REG_POWER_ON_STRIP); 627810bdeddSAdrian Chadd new_pos = pos; 628810bdeddSAdrian Chadd 6299ab21e32SAdrian Chadd /* XXX LED config */ 630b67ba111SAdrian Chadd bzero(&lcfg, sizeof(lcfg)); 631b67ba111SAdrian Chadd if (ar8327_fetch_pdata_led(sc, &lcfg)) { 632b67ba111SAdrian Chadd if (lcfg.open_drain) 633b67ba111SAdrian Chadd new_pos |= AR8327_POWER_ON_STRIP_LED_OPEN_EN; 634b67ba111SAdrian Chadd else 635b67ba111SAdrian Chadd new_pos &= ~AR8327_POWER_ON_STRIP_LED_OPEN_EN; 636b67ba111SAdrian Chadd 637b67ba111SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_LED_CTRL0, 638b67ba111SAdrian Chadd lcfg.led_ctrl0); 639b67ba111SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_LED_CTRL1, 640b67ba111SAdrian Chadd lcfg.led_ctrl1); 641b67ba111SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_LED_CTRL2, 642b67ba111SAdrian Chadd lcfg.led_ctrl2); 643b67ba111SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_LED_CTRL3, 644b67ba111SAdrian Chadd lcfg.led_ctrl3); 645b67ba111SAdrian Chadd 646b67ba111SAdrian Chadd if (new_pos != pos) 647b67ba111SAdrian Chadd new_pos |= AR8327_POWER_ON_STRIP_POWER_ON_SEL; 648b67ba111SAdrian Chadd } 6499ab21e32SAdrian Chadd 650810bdeddSAdrian Chadd /* SGMII config */ 651810bdeddSAdrian Chadd bzero(&scfg, sizeof(scfg)); 652810bdeddSAdrian Chadd if (ar8327_fetch_pdata_sgmii(sc, &scfg)) { 65378549b94SAdrian Chadd device_printf(sc->sc_dev, "%s: SGMII cfg?\n", __func__); 654810bdeddSAdrian Chadd t = scfg.sgmii_ctrl; 655810bdeddSAdrian Chadd if (sc->chip_rev == 1) 656810bdeddSAdrian Chadd t |= AR8327_SGMII_CTRL_EN_PLL | 657810bdeddSAdrian Chadd AR8327_SGMII_CTRL_EN_RX | 658810bdeddSAdrian Chadd AR8327_SGMII_CTRL_EN_TX; 659810bdeddSAdrian Chadd else 660810bdeddSAdrian Chadd t &= ~(AR8327_SGMII_CTRL_EN_PLL | 661810bdeddSAdrian Chadd AR8327_SGMII_CTRL_EN_RX | 662810bdeddSAdrian Chadd AR8327_SGMII_CTRL_EN_TX); 663810bdeddSAdrian Chadd 664810bdeddSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_SGMII_CTRL, t); 665810bdeddSAdrian Chadd 666810bdeddSAdrian Chadd if (scfg.serdes_aen) 667810bdeddSAdrian Chadd new_pos &= ~AR8327_POWER_ON_STRIP_SERDES_AEN; 668810bdeddSAdrian Chadd else 669810bdeddSAdrian Chadd new_pos |= AR8327_POWER_ON_STRIP_SERDES_AEN; 670810bdeddSAdrian Chadd } 671810bdeddSAdrian Chadd 672810bdeddSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_POWER_ON_STRIP, new_pos); 6739ab21e32SAdrian Chadd 6749ab21e32SAdrian Chadd return (0); 6759ab21e32SAdrian Chadd } 6767330dd0bSAdrian Chadd 6777330dd0bSAdrian Chadd static int 6787330dd0bSAdrian Chadd ar8327_hw_setup(struct arswitch_softc *sc) 6797330dd0bSAdrian Chadd { 6807330dd0bSAdrian Chadd int i; 6817330dd0bSAdrian Chadd int err; 6827330dd0bSAdrian Chadd 6837330dd0bSAdrian Chadd /* pdata fetch and setup */ 6847330dd0bSAdrian Chadd err = ar8327_init_pdata(sc); 6857330dd0bSAdrian Chadd if (err != 0) 6867330dd0bSAdrian Chadd return (err); 6877330dd0bSAdrian Chadd 6887330dd0bSAdrian Chadd /* XXX init leds */ 6897330dd0bSAdrian Chadd 6907330dd0bSAdrian Chadd for (i = 0; i < AR8327_NUM_PHYS; i++) { 6917330dd0bSAdrian Chadd /* phy fixup */ 6927330dd0bSAdrian Chadd ar8327_phy_fixup(sc, i); 6937330dd0bSAdrian Chadd 6947330dd0bSAdrian Chadd /* start PHY autonegotiation? */ 6957330dd0bSAdrian Chadd /* XXX is this done as part of the normal PHY setup? */ 6967330dd0bSAdrian Chadd 69774b8d63dSPedro F. Giffuni } 6987330dd0bSAdrian Chadd 6997330dd0bSAdrian Chadd /* Let things settle */ 7007330dd0bSAdrian Chadd DELAY(1000); 7017330dd0bSAdrian Chadd 7027330dd0bSAdrian Chadd return (0); 7037330dd0bSAdrian Chadd } 7047330dd0bSAdrian Chadd 7057330dd0bSAdrian Chadd /* 7067330dd0bSAdrian Chadd * Initialise other global values, for the AR8327. 7077330dd0bSAdrian Chadd */ 7087330dd0bSAdrian Chadd static int 7097330dd0bSAdrian Chadd ar8327_hw_global_setup(struct arswitch_softc *sc) 7107330dd0bSAdrian Chadd { 7117330dd0bSAdrian Chadd uint32_t t; 7127330dd0bSAdrian Chadd 7136d011946SKristof Provost ARSWITCH_LOCK(sc); 7146d011946SKristof Provost 7157330dd0bSAdrian Chadd /* enable CPU port and disable mirror port */ 7167330dd0bSAdrian Chadd t = AR8327_FWD_CTRL0_CPU_PORT_EN | 7177330dd0bSAdrian Chadd AR8327_FWD_CTRL0_MIRROR_PORT; 7187330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_FWD_CTRL0, t); 7197330dd0bSAdrian Chadd 7207330dd0bSAdrian Chadd /* forward multicast and broadcast frames to CPU */ 7217330dd0bSAdrian Chadd t = (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_UC_FLOOD_S) | 7227330dd0bSAdrian Chadd (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_MC_FLOOD_S) | 7237330dd0bSAdrian Chadd (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_BC_FLOOD_S); 7247330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_FWD_CTRL1, t); 7257330dd0bSAdrian Chadd 7267330dd0bSAdrian Chadd /* enable jumbo frames */ 7277330dd0bSAdrian Chadd /* XXX need to macro-shift the value! */ 7287330dd0bSAdrian Chadd arswitch_modifyreg(sc->sc_dev, AR8327_REG_MAX_FRAME_SIZE, 7297330dd0bSAdrian Chadd AR8327_MAX_FRAME_SIZE_MTU, 9018 + 8 + 2); 7307330dd0bSAdrian Chadd 7317330dd0bSAdrian Chadd /* Enable MIB counters */ 7327330dd0bSAdrian Chadd arswitch_modifyreg(sc->sc_dev, AR8327_REG_MODULE_EN, 7337330dd0bSAdrian Chadd AR8327_MODULE_EN_MIB, AR8327_MODULE_EN_MIB); 7347330dd0bSAdrian Chadd 735db37238fSAdrian Chadd /* Disable EEE on all ports due to stability issues */ 736db37238fSAdrian Chadd t = arswitch_readreg(sc->sc_dev, AR8327_REG_EEE_CTRL); 737db37238fSAdrian Chadd t |= AR8327_EEE_CTRL_DISABLE_PHY(0) | 738db37238fSAdrian Chadd AR8327_EEE_CTRL_DISABLE_PHY(1) | 739db37238fSAdrian Chadd AR8327_EEE_CTRL_DISABLE_PHY(2) | 740db37238fSAdrian Chadd AR8327_EEE_CTRL_DISABLE_PHY(3) | 741db37238fSAdrian Chadd AR8327_EEE_CTRL_DISABLE_PHY(4); 742db37238fSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_EEE_CTRL, t); 743db37238fSAdrian Chadd 7444ff2f60dSAdrian Chadd /* Set the right number of ports */ 745749cac13SAdrian Chadd /* GMAC0 (CPU), GMAC1..5 (PHYs), GMAC6 (CPU) */ 746749cac13SAdrian Chadd sc->info.es_nports = 7; 7474ff2f60dSAdrian Chadd 7486d011946SKristof Provost ARSWITCH_UNLOCK(sc); 7497330dd0bSAdrian Chadd return (0); 7507330dd0bSAdrian Chadd } 7517330dd0bSAdrian Chadd 7527330dd0bSAdrian Chadd /* 75378549b94SAdrian Chadd * Port setup. Called at attach time. 7547330dd0bSAdrian Chadd */ 7557330dd0bSAdrian Chadd static void 7567330dd0bSAdrian Chadd ar8327_port_init(struct arswitch_softc *sc, int port) 7577330dd0bSAdrian Chadd { 7587330dd0bSAdrian Chadd uint32_t t; 75978549b94SAdrian Chadd int ports; 76078549b94SAdrian Chadd 76178549b94SAdrian Chadd /* For now, port can see all other ports */ 76278549b94SAdrian Chadd ports = 0x7f; 7637330dd0bSAdrian Chadd 7649ab21e32SAdrian Chadd if (port == AR8X16_PORT_CPU) 7659ab21e32SAdrian Chadd t = sc->ar8327.port0_status; 7667330dd0bSAdrian Chadd else if (port == 6) 7679ab21e32SAdrian Chadd t = sc->ar8327.port6_status; 7687330dd0bSAdrian Chadd else 7697330dd0bSAdrian Chadd t = AR8X16_PORT_STS_LINK_AUTO; 7707330dd0bSAdrian Chadd 7717330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_STATUS(port), t); 7727330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_HEADER(port), 0); 7737330dd0bSAdrian Chadd 7740d2041a0SAdrian Chadd /* 7750d2041a0SAdrian Chadd * Default to 1 port group. 7760d2041a0SAdrian Chadd */ 7777330dd0bSAdrian Chadd t = 1 << AR8327_PORT_VLAN0_DEF_SVID_S; 7787330dd0bSAdrian Chadd t |= 1 << AR8327_PORT_VLAN0_DEF_CVID_S; 7797330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_VLAN0(port), t); 7807330dd0bSAdrian Chadd 7817330dd0bSAdrian Chadd t = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH << AR8327_PORT_VLAN1_OUT_MODE_S; 7827330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_VLAN1(port), t); 7837330dd0bSAdrian Chadd 78403b5d827SAdrian Chadd /* 78503b5d827SAdrian Chadd * This doesn't configure any ports which this port can "see". 78603b5d827SAdrian Chadd * bits 0-6 control which ports a frame coming into this port 78703b5d827SAdrian Chadd * can be sent out to. 78803b5d827SAdrian Chadd * 78903b5d827SAdrian Chadd * So by doing this, we're making it impossible to send frames out 79003b5d827SAdrian Chadd * to that port. 79103b5d827SAdrian Chadd */ 7927330dd0bSAdrian Chadd t = AR8327_PORT_LOOKUP_LEARN; 7937330dd0bSAdrian Chadd t |= AR8X16_PORT_CTRL_STATE_FORWARD << AR8327_PORT_LOOKUP_STATE_S; 79403b5d827SAdrian Chadd 79503b5d827SAdrian Chadd /* So this allows traffic to any port except ourselves */ 79678549b94SAdrian Chadd t |= (ports & ~(1 << port)); 7977330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_LOOKUP(port), t); 7987330dd0bSAdrian Chadd } 7997330dd0bSAdrian Chadd 8007330dd0bSAdrian Chadd static int 8017330dd0bSAdrian Chadd ar8327_port_vlan_setup(struct arswitch_softc *sc, etherswitch_port_t *p) 8027330dd0bSAdrian Chadd { 8037330dd0bSAdrian Chadd 804749cac13SAdrian Chadd /* Check: ADDTAG/STRIPTAG - exclusive */ 805749cac13SAdrian Chadd 806749cac13SAdrian Chadd ARSWITCH_LOCK(sc); 807749cac13SAdrian Chadd 808749cac13SAdrian Chadd /* Set the PVID. */ 809749cac13SAdrian Chadd if (p->es_pvid != 0) 810749cac13SAdrian Chadd sc->hal.arswitch_vlan_set_pvid(sc, p->es_port, p->es_pvid); 811749cac13SAdrian Chadd 812749cac13SAdrian Chadd /* 813749cac13SAdrian Chadd * DOUBLE_TAG 814749cac13SAdrian Chadd * VLAN_MODE_ADD 815749cac13SAdrian Chadd * VLAN_MODE_STRIP 816749cac13SAdrian Chadd */ 817749cac13SAdrian Chadd ARSWITCH_UNLOCK(sc); 8187330dd0bSAdrian Chadd return (0); 8197330dd0bSAdrian Chadd } 8207330dd0bSAdrian Chadd 82178549b94SAdrian Chadd /* 82278549b94SAdrian Chadd * Get the port VLAN configuration. 82378549b94SAdrian Chadd */ 8247330dd0bSAdrian Chadd static int 8257330dd0bSAdrian Chadd ar8327_port_vlan_get(struct arswitch_softc *sc, etherswitch_port_t *p) 8267330dd0bSAdrian Chadd { 827749cac13SAdrian Chadd 828749cac13SAdrian Chadd ARSWITCH_LOCK(sc); 829749cac13SAdrian Chadd 830749cac13SAdrian Chadd /* Retrieve the PVID */ 831749cac13SAdrian Chadd sc->hal.arswitch_vlan_get_pvid(sc, p->es_port, &p->es_pvid); 832749cac13SAdrian Chadd 833036e1c76SAdrian Chadd /* Retrieve the current port configuration from the VTU */ 834749cac13SAdrian Chadd /* 835749cac13SAdrian Chadd * DOUBLE_TAG 836749cac13SAdrian Chadd * VLAN_MODE_ADD 837749cac13SAdrian Chadd * VLAN_MODE_STRIP 838749cac13SAdrian Chadd */ 839749cac13SAdrian Chadd 840749cac13SAdrian Chadd ARSWITCH_UNLOCK(sc); 8417330dd0bSAdrian Chadd return (0); 8427330dd0bSAdrian Chadd } 8437330dd0bSAdrian Chadd 8447330dd0bSAdrian Chadd static void 845036e1c76SAdrian Chadd ar8327_port_disable_mirror(struct arswitch_softc *sc, int port) 846036e1c76SAdrian Chadd { 847036e1c76SAdrian Chadd 848036e1c76SAdrian Chadd arswitch_modifyreg(sc->sc_dev, 849036e1c76SAdrian Chadd AR8327_REG_PORT_LOOKUP(port), 850036e1c76SAdrian Chadd AR8327_PORT_LOOKUP_ING_MIRROR_EN, 851036e1c76SAdrian Chadd 0); 852036e1c76SAdrian Chadd arswitch_modifyreg(sc->sc_dev, 853036e1c76SAdrian Chadd AR8327_REG_PORT_HOL_CTRL1(port), 854036e1c76SAdrian Chadd AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN, 855036e1c76SAdrian Chadd 0); 856036e1c76SAdrian Chadd } 857036e1c76SAdrian Chadd 858036e1c76SAdrian Chadd static void 8597330dd0bSAdrian Chadd ar8327_reset_vlans(struct arswitch_softc *sc) 8607330dd0bSAdrian Chadd { 8617330dd0bSAdrian Chadd int i; 862036e1c76SAdrian Chadd uint32_t t; 86378549b94SAdrian Chadd int ports; 86478549b94SAdrian Chadd 86578549b94SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED); 86678549b94SAdrian Chadd ARSWITCH_LOCK(sc); 86778549b94SAdrian Chadd 86878549b94SAdrian Chadd /* Clear the existing VLAN configuration */ 86978549b94SAdrian Chadd memset(sc->vid, 0, sizeof(sc->vid)); 8707330dd0bSAdrian Chadd 8717330dd0bSAdrian Chadd /* 87203b5d827SAdrian Chadd * Disable mirroring. 87303b5d827SAdrian Chadd */ 87403b5d827SAdrian Chadd arswitch_modifyreg(sc->sc_dev, AR8327_REG_FWD_CTRL0, 87503b5d827SAdrian Chadd AR8327_FWD_CTRL0_MIRROR_PORT, 87603b5d827SAdrian Chadd (0xF << AR8327_FWD_CTRL0_MIRROR_PORT_S)); 87703b5d827SAdrian Chadd 87803b5d827SAdrian Chadd /* 87978549b94SAdrian Chadd * XXX TODO: disable any Q-in-Q port configuration, 88078549b94SAdrian Chadd * tagging, egress filters, etc. 8817330dd0bSAdrian Chadd */ 88278549b94SAdrian Chadd 88378549b94SAdrian Chadd /* 88478549b94SAdrian Chadd * For now, let's default to one portgroup, just so traffic 88578549b94SAdrian Chadd * flows. All ports can see other ports. There are two CPU GMACs 88678549b94SAdrian Chadd * (GMAC0, GMAC6), GMAC1..GMAC5 are external PHYs. 88778549b94SAdrian Chadd * 88878549b94SAdrian Chadd * (ETHERSWITCH_VLAN_PORT) 88978549b94SAdrian Chadd */ 89078549b94SAdrian Chadd ports = 0x7f; 89178549b94SAdrian Chadd 892036e1c76SAdrian Chadd /* 893036e1c76SAdrian Chadd * XXX TODO: set things up correctly for vlans! 894036e1c76SAdrian Chadd */ 8957330dd0bSAdrian Chadd for (i = 0; i < AR8327_NUM_PORTS; i++) { 896036e1c76SAdrian Chadd int egress, ingress; 89778549b94SAdrian Chadd 898036e1c76SAdrian Chadd if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) { 899749cac13SAdrian Chadd sc->vid[i] = i | ETHERSWITCH_VID_VALID; 900036e1c76SAdrian Chadd /* set egress == out_keep */ 901036e1c76SAdrian Chadd ingress = AR8X16_PORT_VLAN_MODE_PORT_ONLY; 902036e1c76SAdrian Chadd /* in_port_only, forward */ 903036e1c76SAdrian Chadd egress = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH; 904036e1c76SAdrian Chadd } else if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) { 905036e1c76SAdrian Chadd ingress = AR8X16_PORT_VLAN_MODE_SECURE; 906036e1c76SAdrian Chadd egress = AR8327_PORT_VLAN1_OUT_MODE_UNMOD; 907036e1c76SAdrian Chadd } else { 908036e1c76SAdrian Chadd /* set egress == out_keep */ 909036e1c76SAdrian Chadd ingress = AR8X16_PORT_VLAN_MODE_PORT_ONLY; 910036e1c76SAdrian Chadd /* in_port_only, forward */ 911036e1c76SAdrian Chadd egress = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH; 912036e1c76SAdrian Chadd } 913749cac13SAdrian Chadd 914749cac13SAdrian Chadd /* set pvid = 1; there's only one vlangroup to start with */ 915dd846bddSAdrian Chadd t = 1 << AR8327_PORT_VLAN0_DEF_SVID_S; 916dd846bddSAdrian Chadd t |= 1 << AR8327_PORT_VLAN0_DEF_CVID_S; 9177330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_VLAN0(i), t); 9187330dd0bSAdrian Chadd 9197330dd0bSAdrian Chadd t = AR8327_PORT_VLAN1_PORT_VLAN_PROP; 920036e1c76SAdrian Chadd t |= egress << AR8327_PORT_VLAN1_OUT_MODE_S; 9217330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_VLAN1(i), t); 9227330dd0bSAdrian Chadd 923dd846bddSAdrian Chadd /* Ports can see other ports */ 924036e1c76SAdrian Chadd /* XXX not entirely true for dot1q? */ 92578549b94SAdrian Chadd t = (ports & ~(1 << i)); /* all ports besides us */ 9267330dd0bSAdrian Chadd t |= AR8327_PORT_LOOKUP_LEARN; 9277330dd0bSAdrian Chadd 928036e1c76SAdrian Chadd t |= ingress << AR8327_PORT_LOOKUP_IN_MODE_S; 9297330dd0bSAdrian Chadd t |= AR8X16_PORT_CTRL_STATE_FORWARD << AR8327_PORT_LOOKUP_STATE_S; 9307330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_LOOKUP(i), t); 931036e1c76SAdrian Chadd } 93203b5d827SAdrian Chadd 93303b5d827SAdrian Chadd /* 93403b5d827SAdrian Chadd * Disable port mirroring entirely. 93503b5d827SAdrian Chadd */ 936036e1c76SAdrian Chadd for (i = 0; i < AR8327_NUM_PORTS; i++) { 937036e1c76SAdrian Chadd ar8327_port_disable_mirror(sc, i); 938036e1c76SAdrian Chadd } 939036e1c76SAdrian Chadd 940036e1c76SAdrian Chadd /* 941036e1c76SAdrian Chadd * If dot1q - set pvid; dot1q, etc. 942036e1c76SAdrian Chadd */ 943036e1c76SAdrian Chadd if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) { 9448f1cf028SAdrian Chadd sc->vid[0] = 1; 945036e1c76SAdrian Chadd for (i = 0; i < AR8327_NUM_PORTS; i++) { 946036e1c76SAdrian Chadd /* Each port - pvid 1 */ 947036e1c76SAdrian Chadd sc->hal.arswitch_vlan_set_pvid(sc, i, sc->vid[0]); 948036e1c76SAdrian Chadd } 949036e1c76SAdrian Chadd /* Initialise vlan1 - all ports, untagged */ 950036e1c76SAdrian Chadd sc->hal.arswitch_set_dot1q_vlan(sc, ports, ports, sc->vid[0]); 951036e1c76SAdrian Chadd sc->vid[0] |= ETHERSWITCH_VID_VALID; 9527330dd0bSAdrian Chadd } 95378549b94SAdrian Chadd 95478549b94SAdrian Chadd ARSWITCH_UNLOCK(sc); 9557330dd0bSAdrian Chadd } 9567330dd0bSAdrian Chadd 9577330dd0bSAdrian Chadd static int 958749cac13SAdrian Chadd ar8327_vlan_get_port(struct arswitch_softc *sc, uint32_t *ports, int vid) 959749cac13SAdrian Chadd { 960749cac13SAdrian Chadd int port; 961749cac13SAdrian Chadd uint32_t reg; 962749cac13SAdrian Chadd 963749cac13SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 964749cac13SAdrian Chadd 965749cac13SAdrian Chadd /* For port based vlans the vlanid is the same as the port index. */ 966749cac13SAdrian Chadd port = vid & ETHERSWITCH_VID_MASK; 967749cac13SAdrian Chadd reg = arswitch_readreg(sc->sc_dev, AR8327_REG_PORT_LOOKUP(port)); 968749cac13SAdrian Chadd *ports = reg & 0x7f; 969749cac13SAdrian Chadd return (0); 970749cac13SAdrian Chadd } 971749cac13SAdrian Chadd 972749cac13SAdrian Chadd static int 973749cac13SAdrian Chadd ar8327_vlan_set_port(struct arswitch_softc *sc, uint32_t ports, int vid) 974749cac13SAdrian Chadd { 975749cac13SAdrian Chadd int err, port; 976749cac13SAdrian Chadd 977749cac13SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 978749cac13SAdrian Chadd 979749cac13SAdrian Chadd /* For port based vlans the vlanid is the same as the port index. */ 980749cac13SAdrian Chadd port = vid & ETHERSWITCH_VID_MASK; 981749cac13SAdrian Chadd 982749cac13SAdrian Chadd err = arswitch_modifyreg(sc->sc_dev, AR8327_REG_PORT_LOOKUP(port), 983749cac13SAdrian Chadd 0x7f, /* vlan membership mask */ 984749cac13SAdrian Chadd (ports & 0x7f)); 985749cac13SAdrian Chadd 986749cac13SAdrian Chadd if (err) 987749cac13SAdrian Chadd return (err); 988749cac13SAdrian Chadd return (0); 989749cac13SAdrian Chadd } 990749cac13SAdrian Chadd 991749cac13SAdrian Chadd static int 9927330dd0bSAdrian Chadd ar8327_vlan_getvgroup(struct arswitch_softc *sc, etherswitch_vlangroup_t *vg) 9937330dd0bSAdrian Chadd { 99478549b94SAdrian Chadd 99578549b94SAdrian Chadd return (ar8xxx_getvgroup(sc, vg)); 9967330dd0bSAdrian Chadd } 9977330dd0bSAdrian Chadd 9987330dd0bSAdrian Chadd static int 9997330dd0bSAdrian Chadd ar8327_vlan_setvgroup(struct arswitch_softc *sc, etherswitch_vlangroup_t *vg) 10007330dd0bSAdrian Chadd { 10017330dd0bSAdrian Chadd 100278549b94SAdrian Chadd return (ar8xxx_setvgroup(sc, vg)); 10037330dd0bSAdrian Chadd } 10047330dd0bSAdrian Chadd 10057330dd0bSAdrian Chadd static int 10067330dd0bSAdrian Chadd ar8327_get_pvid(struct arswitch_softc *sc, int port, int *pvid) 10077330dd0bSAdrian Chadd { 1008749cac13SAdrian Chadd uint32_t reg; 10097330dd0bSAdrian Chadd 1010749cac13SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 1011749cac13SAdrian Chadd 1012749cac13SAdrian Chadd /* 1013749cac13SAdrian Chadd * XXX for now, assuming it's CVID; likely very wrong! 1014749cac13SAdrian Chadd */ 1015749cac13SAdrian Chadd port = port & ETHERSWITCH_VID_MASK; 1016749cac13SAdrian Chadd reg = arswitch_readreg(sc->sc_dev, AR8327_REG_PORT_VLAN0(port)); 1017749cac13SAdrian Chadd reg = reg >> AR8327_PORT_VLAN0_DEF_CVID_S; 1018749cac13SAdrian Chadd reg = reg & 0xfff; 1019749cac13SAdrian Chadd 1020749cac13SAdrian Chadd *pvid = reg; 10217330dd0bSAdrian Chadd return (0); 10227330dd0bSAdrian Chadd } 10237330dd0bSAdrian Chadd 10247330dd0bSAdrian Chadd static int 10257330dd0bSAdrian Chadd ar8327_set_pvid(struct arswitch_softc *sc, int port, int pvid) 10267330dd0bSAdrian Chadd { 1027749cac13SAdrian Chadd uint32_t t; 10287330dd0bSAdrian Chadd 1029749cac13SAdrian Chadd /* Limit pvid to valid values */ 1030749cac13SAdrian Chadd pvid &= 0x7f; 1031749cac13SAdrian Chadd 1032749cac13SAdrian Chadd t = pvid << AR8327_PORT_VLAN0_DEF_SVID_S; 1033749cac13SAdrian Chadd t |= pvid << AR8327_PORT_VLAN0_DEF_CVID_S; 1034749cac13SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_VLAN0(port), t); 1035749cac13SAdrian Chadd 10367330dd0bSAdrian Chadd return (0); 10377330dd0bSAdrian Chadd } 10387330dd0bSAdrian Chadd 10394ff2f60dSAdrian Chadd static int 10404ff2f60dSAdrian Chadd ar8327_atu_flush(struct arswitch_softc *sc) 10414ff2f60dSAdrian Chadd { 10424ff2f60dSAdrian Chadd 10434ff2f60dSAdrian Chadd int ret; 10444ff2f60dSAdrian Chadd 10454ff2f60dSAdrian Chadd ret = arswitch_waitreg(sc->sc_dev, 10464ff2f60dSAdrian Chadd AR8327_REG_ATU_FUNC, 10474ff2f60dSAdrian Chadd AR8327_ATU_FUNC_BUSY, 10484ff2f60dSAdrian Chadd 0, 10494ff2f60dSAdrian Chadd 1000); 10504ff2f60dSAdrian Chadd 10514ff2f60dSAdrian Chadd if (ret) 10524ff2f60dSAdrian Chadd device_printf(sc->sc_dev, "%s: waitreg failed\n", __func__); 10534ff2f60dSAdrian Chadd 10544ff2f60dSAdrian Chadd if (!ret) 10554ff2f60dSAdrian Chadd arswitch_writereg(sc->sc_dev, 10564ff2f60dSAdrian Chadd AR8327_REG_ATU_FUNC, 10574ff2f60dSAdrian Chadd AR8327_ATU_FUNC_OP_FLUSH); 10584ff2f60dSAdrian Chadd return (ret); 10594ff2f60dSAdrian Chadd } 10604ff2f60dSAdrian Chadd 1061036e1c76SAdrian Chadd static int 1062036e1c76SAdrian Chadd ar8327_flush_dot1q_vlan(struct arswitch_softc *sc) 1063036e1c76SAdrian Chadd { 1064036e1c76SAdrian Chadd 1065036e1c76SAdrian Chadd return (ar8327_vlan_op(sc, AR8327_VTU_FUNC1_OP_FLUSH, 0, 0)); 1066036e1c76SAdrian Chadd } 1067036e1c76SAdrian Chadd 1068036e1c76SAdrian Chadd static int 1069036e1c76SAdrian Chadd ar8327_purge_dot1q_vlan(struct arswitch_softc *sc, int vid) 1070036e1c76SAdrian Chadd { 1071036e1c76SAdrian Chadd 1072036e1c76SAdrian Chadd return (ar8327_vlan_op(sc, AR8327_VTU_FUNC1_OP_PURGE, vid, 0)); 1073036e1c76SAdrian Chadd } 1074036e1c76SAdrian Chadd 1075036e1c76SAdrian Chadd static int 1076036e1c76SAdrian Chadd ar8327_get_dot1q_vlan(struct arswitch_softc *sc, uint32_t *ports, 1077036e1c76SAdrian Chadd uint32_t *untagged_ports, int vid) 1078036e1c76SAdrian Chadd { 1079036e1c76SAdrian Chadd int i, r; 1080036e1c76SAdrian Chadd uint32_t op, reg, val; 1081036e1c76SAdrian Chadd 1082036e1c76SAdrian Chadd op = AR8327_VTU_FUNC1_OP_GET_ONE; 1083036e1c76SAdrian Chadd 1084036e1c76SAdrian Chadd /* Filter out the vid flags; only grab the VLAN ID */ 1085036e1c76SAdrian Chadd vid &= 0xfff; 1086036e1c76SAdrian Chadd 1087036e1c76SAdrian Chadd /* XXX TODO: the VTU here stores egress mode - keep, tag, untagged, none */ 1088036e1c76SAdrian Chadd r = ar8327_vlan_op(sc, op, vid, 0); 1089036e1c76SAdrian Chadd if (r != 0) { 1090036e1c76SAdrian Chadd device_printf(sc->sc_dev, "%s: %d: op failed\n", __func__, vid); 1091036e1c76SAdrian Chadd } 1092036e1c76SAdrian Chadd 1093036e1c76SAdrian Chadd reg = arswitch_readreg(sc->sc_dev, AR8327_REG_VTU_FUNC0); 10941b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_REGIO, "%s: %d: reg=0x%08x\n", __func__, vid, reg); 1095036e1c76SAdrian Chadd 1096036e1c76SAdrian Chadd /* 1097036e1c76SAdrian Chadd * If any of the bits are set, update the port mask. 1098036e1c76SAdrian Chadd * Worry about the port config itself when getport() is called. 1099036e1c76SAdrian Chadd */ 1100036e1c76SAdrian Chadd *ports = 0; 1101036e1c76SAdrian Chadd for (i = 0; i < AR8327_NUM_PORTS; i++) { 1102036e1c76SAdrian Chadd val = reg >> AR8327_VTU_FUNC0_EG_MODE_S(i); 1103036e1c76SAdrian Chadd val = val & 0x3; 1104036e1c76SAdrian Chadd /* XXX KEEP (unmodified?) */ 1105036e1c76SAdrian Chadd if (val == AR8327_VTU_FUNC0_EG_MODE_TAG) { 1106036e1c76SAdrian Chadd *ports |= (1 << i); 1107036e1c76SAdrian Chadd } else if (val == AR8327_VTU_FUNC0_EG_MODE_UNTAG) { 1108036e1c76SAdrian Chadd *ports |= (1 << i); 1109036e1c76SAdrian Chadd *untagged_ports |= (1 << i); 1110036e1c76SAdrian Chadd } 1111036e1c76SAdrian Chadd } 1112036e1c76SAdrian Chadd 1113036e1c76SAdrian Chadd return (0); 1114036e1c76SAdrian Chadd } 1115036e1c76SAdrian Chadd 1116036e1c76SAdrian Chadd static int 1117036e1c76SAdrian Chadd ar8327_set_dot1q_vlan(struct arswitch_softc *sc, uint32_t ports, 1118036e1c76SAdrian Chadd uint32_t untagged_ports, int vid) 1119036e1c76SAdrian Chadd { 1120036e1c76SAdrian Chadd int i; 1121036e1c76SAdrian Chadd uint32_t op, val, mode; 1122036e1c76SAdrian Chadd 1123036e1c76SAdrian Chadd op = AR8327_VTU_FUNC1_OP_LOAD; 1124036e1c76SAdrian Chadd vid &= 0xfff; 1125036e1c76SAdrian Chadd 11261b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_VLAN, 1127036e1c76SAdrian Chadd "%s: vid: %d, ports=0x%08x, untagged_ports=0x%08x\n", 1128036e1c76SAdrian Chadd __func__, 1129036e1c76SAdrian Chadd vid, 1130036e1c76SAdrian Chadd ports, 1131036e1c76SAdrian Chadd untagged_ports); 1132036e1c76SAdrian Chadd 1133036e1c76SAdrian Chadd /* 1134036e1c76SAdrian Chadd * Mark it as valid; and that it should use per-VLAN MAC table, 1135036e1c76SAdrian Chadd * not VID=0 when doing MAC lookups 1136036e1c76SAdrian Chadd */ 1137036e1c76SAdrian Chadd val = AR8327_VTU_FUNC0_VALID | AR8327_VTU_FUNC0_IVL; 1138036e1c76SAdrian Chadd 1139036e1c76SAdrian Chadd for (i = 0; i < AR8327_NUM_PORTS; i++) { 1140036e1c76SAdrian Chadd if ((ports & BIT(i)) == 0) 1141036e1c76SAdrian Chadd mode = AR8327_VTU_FUNC0_EG_MODE_NOT; 1142036e1c76SAdrian Chadd else if (untagged_ports & BIT(i)) 1143036e1c76SAdrian Chadd mode = AR8327_VTU_FUNC0_EG_MODE_UNTAG; 1144036e1c76SAdrian Chadd else 1145036e1c76SAdrian Chadd mode = AR8327_VTU_FUNC0_EG_MODE_TAG; 1146036e1c76SAdrian Chadd 1147036e1c76SAdrian Chadd val |= mode << AR8327_VTU_FUNC0_EG_MODE_S(i); 1148036e1c76SAdrian Chadd } 1149036e1c76SAdrian Chadd 1150036e1c76SAdrian Chadd return (ar8327_vlan_op(sc, op, vid, val)); 1151036e1c76SAdrian Chadd } 1152036e1c76SAdrian Chadd 11537330dd0bSAdrian Chadd void 11547330dd0bSAdrian Chadd ar8327_attach(struct arswitch_softc *sc) 11557330dd0bSAdrian Chadd { 11567330dd0bSAdrian Chadd 11577330dd0bSAdrian Chadd sc->hal.arswitch_hw_setup = ar8327_hw_setup; 11587330dd0bSAdrian Chadd sc->hal.arswitch_hw_global_setup = ar8327_hw_global_setup; 11597330dd0bSAdrian Chadd 11607330dd0bSAdrian Chadd sc->hal.arswitch_port_init = ar8327_port_init; 116178549b94SAdrian Chadd 116278549b94SAdrian Chadd sc->hal.arswitch_vlan_getvgroup = ar8327_vlan_getvgroup; 116378549b94SAdrian Chadd sc->hal.arswitch_vlan_setvgroup = ar8327_vlan_setvgroup; 11647330dd0bSAdrian Chadd sc->hal.arswitch_port_vlan_setup = ar8327_port_vlan_setup; 11657330dd0bSAdrian Chadd sc->hal.arswitch_port_vlan_get = ar8327_port_vlan_get; 1166036e1c76SAdrian Chadd sc->hal.arswitch_flush_dot1q_vlan = ar8327_flush_dot1q_vlan; 1167036e1c76SAdrian Chadd sc->hal.arswitch_purge_dot1q_vlan = ar8327_purge_dot1q_vlan; 1168036e1c76SAdrian Chadd sc->hal.arswitch_set_dot1q_vlan = ar8327_set_dot1q_vlan; 1169036e1c76SAdrian Chadd sc->hal.arswitch_get_dot1q_vlan = ar8327_get_dot1q_vlan; 11707330dd0bSAdrian Chadd 11717330dd0bSAdrian Chadd sc->hal.arswitch_vlan_init_hw = ar8327_reset_vlans; 11727330dd0bSAdrian Chadd sc->hal.arswitch_vlan_get_pvid = ar8327_get_pvid; 11737330dd0bSAdrian Chadd sc->hal.arswitch_vlan_set_pvid = ar8327_set_pvid; 11747330dd0bSAdrian Chadd 1175749cac13SAdrian Chadd sc->hal.arswitch_get_port_vlan = ar8327_vlan_get_port; 1176749cac13SAdrian Chadd sc->hal.arswitch_set_port_vlan = ar8327_vlan_set_port; 1177749cac13SAdrian Chadd 11784ff2f60dSAdrian Chadd sc->hal.arswitch_atu_flush = ar8327_atu_flush; 11794ff2f60dSAdrian Chadd 118078549b94SAdrian Chadd /* 118178549b94SAdrian Chadd * Reading the PHY via the MDIO interface currently doesn't 118278549b94SAdrian Chadd * work correctly. 118378549b94SAdrian Chadd * 118478549b94SAdrian Chadd * So for now, just go direct to the PHY registers themselves. 118578549b94SAdrian Chadd * This has always worked on external devices, but not internal 118678549b94SAdrian Chadd * devices (AR934x, AR724x, AR933x.) 118778549b94SAdrian Chadd */ 118878549b94SAdrian Chadd sc->hal.arswitch_phy_read = arswitch_readphy_external; 118978549b94SAdrian Chadd sc->hal.arswitch_phy_write = arswitch_writephy_external; 119078549b94SAdrian Chadd 11917330dd0bSAdrian Chadd /* Set the switch vlan capabilities. */ 11927330dd0bSAdrian Chadd sc->info.es_vlan_caps = ETHERSWITCH_VLAN_DOT1Q | 11937330dd0bSAdrian Chadd ETHERSWITCH_VLAN_PORT | ETHERSWITCH_VLAN_DOUBLE_TAG; 11947330dd0bSAdrian Chadd sc->info.es_nvlangroups = AR8X16_MAX_VLANS; 11957330dd0bSAdrian Chadd } 1196