17330dd0bSAdrian Chadd /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro 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
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>
5371e8eac4SAdrian Chadd #include <dev/mdio/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
698f1cf028SAdrian Chadd /*
708f1cf028SAdrian Chadd * AR8327 TODO:
718f1cf028SAdrian Chadd *
728f1cf028SAdrian Chadd * There should be a default hardware setup hint set for the default
738f1cf028SAdrian Chadd * switch config. Otherwise the default is "all ports in one vlangroup",
748f1cf028SAdrian Chadd * which means both CPU ports can see each other and that will quickly
758f1cf028SAdrian Chadd * lead to traffic storms/loops.
768f1cf028SAdrian Chadd */
77036e1c76SAdrian Chadd
78c94dc808SAdrian Chadd /* Map port+led to register+shift */
79c94dc808SAdrian Chadd struct ar8327_led_mapping ar8327_led_mapping[AR8327_NUM_PHYS][ETHERSWITCH_PORT_MAX_LEDS] =
80c94dc808SAdrian Chadd {
81c94dc808SAdrian Chadd { /* PHY0 */
82c94dc808SAdrian Chadd {AR8327_REG_LED_CTRL0, 14 },
83c94dc808SAdrian Chadd {AR8327_REG_LED_CTRL1, 14 },
84c94dc808SAdrian Chadd {AR8327_REG_LED_CTRL2, 14 }
85c94dc808SAdrian Chadd },
86c94dc808SAdrian Chadd { /* PHY1 */
87c94dc808SAdrian Chadd {AR8327_REG_LED_CTRL3, 8 },
88c94dc808SAdrian Chadd {AR8327_REG_LED_CTRL3, 10 },
89c94dc808SAdrian Chadd {AR8327_REG_LED_CTRL3, 12 }
90c94dc808SAdrian Chadd },
91c94dc808SAdrian Chadd { /* PHY2 */
92c94dc808SAdrian Chadd {AR8327_REG_LED_CTRL3, 14 },
93c94dc808SAdrian Chadd {AR8327_REG_LED_CTRL3, 16 },
94c94dc808SAdrian Chadd {AR8327_REG_LED_CTRL3, 18 }
95c94dc808SAdrian Chadd },
96c94dc808SAdrian Chadd { /* PHY3 */
97c94dc808SAdrian Chadd {AR8327_REG_LED_CTRL3, 20 },
98c94dc808SAdrian Chadd {AR8327_REG_LED_CTRL3, 22 },
99c94dc808SAdrian Chadd {AR8327_REG_LED_CTRL3, 24 }
100c94dc808SAdrian Chadd },
101c94dc808SAdrian Chadd { /* PHY4 */
102c94dc808SAdrian Chadd {AR8327_REG_LED_CTRL0, 30 },
103c94dc808SAdrian Chadd {AR8327_REG_LED_CTRL1, 30 },
104c94dc808SAdrian Chadd {AR8327_REG_LED_CTRL2, 30 }
105c94dc808SAdrian Chadd }
106c94dc808SAdrian Chadd };
107c94dc808SAdrian Chadd
108036e1c76SAdrian Chadd static int
ar8327_vlan_op(struct arswitch_softc * sc,uint32_t op,uint32_t vid,uint32_t data)109036e1c76SAdrian Chadd ar8327_vlan_op(struct arswitch_softc *sc, uint32_t op, uint32_t vid,
110036e1c76SAdrian Chadd uint32_t data)
111036e1c76SAdrian Chadd {
112036e1c76SAdrian Chadd int err;
113036e1c76SAdrian Chadd
114036e1c76SAdrian Chadd /*
115036e1c76SAdrian Chadd * Wait for the "done" bit to finish.
116036e1c76SAdrian Chadd */
117036e1c76SAdrian Chadd if (arswitch_waitreg(sc->sc_dev, AR8327_REG_VTU_FUNC1,
118036e1c76SAdrian Chadd AR8327_VTU_FUNC1_BUSY, 0, 5))
119036e1c76SAdrian Chadd return (EBUSY);
120036e1c76SAdrian Chadd
121036e1c76SAdrian Chadd /*
122036e1c76SAdrian Chadd * If it's a "load" operation, then ensure 'data' is loaded
123036e1c76SAdrian Chadd * in first.
124036e1c76SAdrian Chadd */
125036e1c76SAdrian Chadd if ((op & AR8327_VTU_FUNC1_OP) == AR8327_VTU_FUNC1_OP_LOAD) {
126036e1c76SAdrian Chadd err = arswitch_writereg(sc->sc_dev, AR8327_REG_VTU_FUNC0, data);
127036e1c76SAdrian Chadd if (err)
128036e1c76SAdrian Chadd return (err);
129036e1c76SAdrian Chadd }
130036e1c76SAdrian Chadd
131036e1c76SAdrian Chadd /*
132036e1c76SAdrian Chadd * Set the VID.
133036e1c76SAdrian Chadd */
134036e1c76SAdrian Chadd op |= ((vid & 0xfff) << AR8327_VTU_FUNC1_VID_S);
135036e1c76SAdrian Chadd
136036e1c76SAdrian Chadd /*
137036e1c76SAdrian Chadd * Set busy bit to start loading in the command.
138036e1c76SAdrian Chadd */
139036e1c76SAdrian Chadd op |= AR8327_VTU_FUNC1_BUSY;
140036e1c76SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_VTU_FUNC1, op);
141036e1c76SAdrian Chadd
142036e1c76SAdrian Chadd /*
143036e1c76SAdrian Chadd * Finally - wait for it to load.
144036e1c76SAdrian Chadd */
145036e1c76SAdrian Chadd if (arswitch_waitreg(sc->sc_dev, AR8327_REG_VTU_FUNC1,
146036e1c76SAdrian Chadd AR8327_VTU_FUNC1_BUSY, 0, 5))
147036e1c76SAdrian Chadd return (EBUSY);
148036e1c76SAdrian Chadd
149036e1c76SAdrian Chadd return (0);
150036e1c76SAdrian Chadd }
151036e1c76SAdrian Chadd
1527330dd0bSAdrian Chadd static void
ar8327_phy_fixup(struct arswitch_softc * sc,int phy)1537330dd0bSAdrian Chadd ar8327_phy_fixup(struct arswitch_softc *sc, int phy)
1547330dd0bSAdrian Chadd {
155db37238fSAdrian Chadd if (bootverbose)
156db37238fSAdrian Chadd device_printf(sc->sc_dev,
157db37238fSAdrian Chadd "%s: called; phy=%d; chiprev=%d\n", __func__,
158db37238fSAdrian Chadd phy,
159db37238fSAdrian Chadd sc->chip_rev);
1607330dd0bSAdrian Chadd switch (sc->chip_rev) {
1617330dd0bSAdrian Chadd case 1:
1627330dd0bSAdrian Chadd /* For 100M waveform */
1637330dd0bSAdrian Chadd arswitch_writedbg(sc->sc_dev, phy, 0, 0x02ea);
1647330dd0bSAdrian Chadd /* Turn on Gigabit clock */
1657330dd0bSAdrian Chadd arswitch_writedbg(sc->sc_dev, phy, 0x3d, 0x68a0);
1667330dd0bSAdrian Chadd break;
1677330dd0bSAdrian Chadd
1687330dd0bSAdrian Chadd case 2:
1697330dd0bSAdrian Chadd arswitch_writemmd(sc->sc_dev, phy, 0x7, 0x3c);
1707330dd0bSAdrian Chadd arswitch_writemmd(sc->sc_dev, phy, 0x4007, 0x0);
1717330dd0bSAdrian Chadd /* fallthrough */
1727330dd0bSAdrian Chadd case 4:
1737330dd0bSAdrian Chadd arswitch_writemmd(sc->sc_dev, phy, 0x3, 0x800d);
1747330dd0bSAdrian Chadd arswitch_writemmd(sc->sc_dev, phy, 0x4003, 0x803f);
1757330dd0bSAdrian Chadd
1767330dd0bSAdrian Chadd arswitch_writedbg(sc->sc_dev, phy, 0x3d, 0x6860);
1777330dd0bSAdrian Chadd arswitch_writedbg(sc->sc_dev, phy, 0x5, 0x2c46);
1787330dd0bSAdrian Chadd arswitch_writedbg(sc->sc_dev, phy, 0x3c, 0x6000);
1797330dd0bSAdrian Chadd break;
1807330dd0bSAdrian Chadd }
1817330dd0bSAdrian Chadd }
1827330dd0bSAdrian Chadd
1837330dd0bSAdrian Chadd static uint32_t
ar8327_get_pad_cfg(struct ar8327_pad_cfg * cfg)1847330dd0bSAdrian Chadd ar8327_get_pad_cfg(struct ar8327_pad_cfg *cfg)
1857330dd0bSAdrian Chadd {
1867330dd0bSAdrian Chadd uint32_t t;
1877330dd0bSAdrian Chadd
1887330dd0bSAdrian Chadd if (!cfg)
1897330dd0bSAdrian Chadd return (0);
1907330dd0bSAdrian Chadd
1917330dd0bSAdrian Chadd t = 0;
1927330dd0bSAdrian Chadd switch (cfg->mode) {
1937330dd0bSAdrian Chadd case AR8327_PAD_NC:
1947330dd0bSAdrian Chadd break;
1957330dd0bSAdrian Chadd
1967330dd0bSAdrian Chadd case AR8327_PAD_MAC2MAC_MII:
1977330dd0bSAdrian Chadd t = AR8327_PAD_MAC_MII_EN;
1987330dd0bSAdrian Chadd if (cfg->rxclk_sel)
1997330dd0bSAdrian Chadd t |= AR8327_PAD_MAC_MII_RXCLK_SEL;
2007330dd0bSAdrian Chadd if (cfg->txclk_sel)
2017330dd0bSAdrian Chadd t |= AR8327_PAD_MAC_MII_TXCLK_SEL;
2027330dd0bSAdrian Chadd break;
2037330dd0bSAdrian Chadd
2047330dd0bSAdrian Chadd case AR8327_PAD_MAC2MAC_GMII:
2057330dd0bSAdrian Chadd t = AR8327_PAD_MAC_GMII_EN;
2067330dd0bSAdrian Chadd if (cfg->rxclk_sel)
2077330dd0bSAdrian Chadd t |= AR8327_PAD_MAC_GMII_RXCLK_SEL;
2087330dd0bSAdrian Chadd if (cfg->txclk_sel)
2097330dd0bSAdrian Chadd t |= AR8327_PAD_MAC_GMII_TXCLK_SEL;
2107330dd0bSAdrian Chadd break;
2117330dd0bSAdrian Chadd
2127330dd0bSAdrian Chadd case AR8327_PAD_MAC_SGMII:
2137330dd0bSAdrian Chadd t = AR8327_PAD_SGMII_EN;
2147330dd0bSAdrian Chadd
2157330dd0bSAdrian Chadd /*
2169ab21e32SAdrian Chadd * WAR for the Qualcomm Atheros AP136 board.
2177330dd0bSAdrian Chadd * It seems that RGMII TX/RX delay settings needs to be
2187330dd0bSAdrian Chadd * applied for SGMII mode as well, The ethernet is not
2197330dd0bSAdrian Chadd * reliable without this.
2207330dd0bSAdrian Chadd */
2217330dd0bSAdrian Chadd t |= cfg->txclk_delay_sel << AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S;
2227330dd0bSAdrian Chadd t |= cfg->rxclk_delay_sel << AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S;
2237330dd0bSAdrian Chadd if (cfg->rxclk_delay_en)
2247330dd0bSAdrian Chadd t |= AR8327_PAD_RGMII_RXCLK_DELAY_EN;
2257330dd0bSAdrian Chadd if (cfg->txclk_delay_en)
2267330dd0bSAdrian Chadd t |= AR8327_PAD_RGMII_TXCLK_DELAY_EN;
2277330dd0bSAdrian Chadd
2287330dd0bSAdrian Chadd if (cfg->sgmii_delay_en)
2297330dd0bSAdrian Chadd t |= AR8327_PAD_SGMII_DELAY_EN;
2307330dd0bSAdrian Chadd
2317330dd0bSAdrian Chadd break;
2327330dd0bSAdrian Chadd
2337330dd0bSAdrian Chadd case AR8327_PAD_MAC2PHY_MII:
2347330dd0bSAdrian Chadd t = AR8327_PAD_PHY_MII_EN;
2357330dd0bSAdrian Chadd if (cfg->rxclk_sel)
2367330dd0bSAdrian Chadd t |= AR8327_PAD_PHY_MII_RXCLK_SEL;
2377330dd0bSAdrian Chadd if (cfg->txclk_sel)
2387330dd0bSAdrian Chadd t |= AR8327_PAD_PHY_MII_TXCLK_SEL;
2397330dd0bSAdrian Chadd break;
2407330dd0bSAdrian Chadd
2417330dd0bSAdrian Chadd case AR8327_PAD_MAC2PHY_GMII:
2427330dd0bSAdrian Chadd t = AR8327_PAD_PHY_GMII_EN;
2437330dd0bSAdrian Chadd if (cfg->pipe_rxclk_sel)
2447330dd0bSAdrian Chadd t |= AR8327_PAD_PHY_GMII_PIPE_RXCLK_SEL;
2457330dd0bSAdrian Chadd if (cfg->rxclk_sel)
2467330dd0bSAdrian Chadd t |= AR8327_PAD_PHY_GMII_RXCLK_SEL;
2477330dd0bSAdrian Chadd if (cfg->txclk_sel)
2487330dd0bSAdrian Chadd t |= AR8327_PAD_PHY_GMII_TXCLK_SEL;
2497330dd0bSAdrian Chadd break;
2507330dd0bSAdrian Chadd
2517330dd0bSAdrian Chadd case AR8327_PAD_MAC_RGMII:
2527330dd0bSAdrian Chadd t = AR8327_PAD_RGMII_EN;
2537330dd0bSAdrian Chadd t |= cfg->txclk_delay_sel << AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S;
2547330dd0bSAdrian Chadd t |= cfg->rxclk_delay_sel << AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S;
2557330dd0bSAdrian Chadd if (cfg->rxclk_delay_en)
2567330dd0bSAdrian Chadd t |= AR8327_PAD_RGMII_RXCLK_DELAY_EN;
2577330dd0bSAdrian Chadd if (cfg->txclk_delay_en)
2587330dd0bSAdrian Chadd t |= AR8327_PAD_RGMII_TXCLK_DELAY_EN;
2597330dd0bSAdrian Chadd break;
2607330dd0bSAdrian Chadd
2617330dd0bSAdrian Chadd case AR8327_PAD_PHY_GMII:
2627330dd0bSAdrian Chadd t = AR8327_PAD_PHYX_GMII_EN;
2637330dd0bSAdrian Chadd break;
2647330dd0bSAdrian Chadd
2657330dd0bSAdrian Chadd case AR8327_PAD_PHY_RGMII:
2667330dd0bSAdrian Chadd t = AR8327_PAD_PHYX_RGMII_EN;
2677330dd0bSAdrian Chadd break;
2687330dd0bSAdrian Chadd
2697330dd0bSAdrian Chadd case AR8327_PAD_PHY_MII:
2707330dd0bSAdrian Chadd t = AR8327_PAD_PHYX_MII_EN;
2717330dd0bSAdrian Chadd break;
2727330dd0bSAdrian Chadd }
2737330dd0bSAdrian Chadd
2747330dd0bSAdrian Chadd return (t);
2757330dd0bSAdrian Chadd }
2767330dd0bSAdrian Chadd
2777330dd0bSAdrian Chadd /*
2787330dd0bSAdrian Chadd * Map the hard-coded port config from the switch setup to
2797330dd0bSAdrian Chadd * the chipset port config (status, duplex, flow, etc.)
2807330dd0bSAdrian Chadd */
2817330dd0bSAdrian Chadd static uint32_t
ar8327_get_port_init_status(struct ar8327_port_cfg * cfg)2827330dd0bSAdrian Chadd ar8327_get_port_init_status(struct ar8327_port_cfg *cfg)
2837330dd0bSAdrian Chadd {
2847330dd0bSAdrian Chadd uint32_t t;
2857330dd0bSAdrian Chadd
2867330dd0bSAdrian Chadd if (!cfg->force_link)
2877330dd0bSAdrian Chadd return (AR8X16_PORT_STS_LINK_AUTO);
2887330dd0bSAdrian Chadd
2897330dd0bSAdrian Chadd t = AR8X16_PORT_STS_TXMAC | AR8X16_PORT_STS_RXMAC;
2907330dd0bSAdrian Chadd t |= cfg->duplex ? AR8X16_PORT_STS_DUPLEX : 0;
2917330dd0bSAdrian Chadd t |= cfg->rxpause ? AR8X16_PORT_STS_RXFLOW : 0;
2927330dd0bSAdrian Chadd t |= cfg->txpause ? AR8X16_PORT_STS_TXFLOW : 0;
2937330dd0bSAdrian Chadd
2947330dd0bSAdrian Chadd switch (cfg->speed) {
2957330dd0bSAdrian Chadd case AR8327_PORT_SPEED_10:
2967330dd0bSAdrian Chadd t |= AR8X16_PORT_STS_SPEED_10;
2977330dd0bSAdrian Chadd break;
2987330dd0bSAdrian Chadd case AR8327_PORT_SPEED_100:
2997330dd0bSAdrian Chadd t |= AR8X16_PORT_STS_SPEED_100;
3007330dd0bSAdrian Chadd break;
3017330dd0bSAdrian Chadd case AR8327_PORT_SPEED_1000:
3027330dd0bSAdrian Chadd t |= AR8X16_PORT_STS_SPEED_1000;
3037330dd0bSAdrian Chadd break;
3047330dd0bSAdrian Chadd }
3057330dd0bSAdrian Chadd
3067330dd0bSAdrian Chadd return (t);
3077330dd0bSAdrian Chadd }
3089ab21e32SAdrian Chadd
3099ab21e32SAdrian Chadd /*
310f9950f9aSAdrian Chadd * Fetch the port data for the given port.
311f9950f9aSAdrian Chadd *
312f9950f9aSAdrian Chadd * This goes and does dirty things with the hints space
313f9950f9aSAdrian Chadd * to determine what the configuration parameters should be.
314f9950f9aSAdrian Chadd *
315f9950f9aSAdrian Chadd * Returns 1 if the structure was successfully parsed and
316f9950f9aSAdrian Chadd * the contents are valid; 0 otherwise.
317f9950f9aSAdrian Chadd */
318f9950f9aSAdrian Chadd static int
ar8327_fetch_pdata_port(struct arswitch_softc * sc,struct ar8327_port_cfg * pcfg,int port)319f9950f9aSAdrian Chadd ar8327_fetch_pdata_port(struct arswitch_softc *sc,
320f9950f9aSAdrian Chadd struct ar8327_port_cfg *pcfg,
321f9950f9aSAdrian Chadd int port)
322f9950f9aSAdrian Chadd {
323f9950f9aSAdrian Chadd int val;
324f9950f9aSAdrian Chadd char sbuf[128];
325f9950f9aSAdrian Chadd
326f9950f9aSAdrian Chadd /* Check if force_link exists */
327f9950f9aSAdrian Chadd val = 0;
328f9950f9aSAdrian Chadd snprintf(sbuf, 128, "port.%d.force_link", port);
329f9950f9aSAdrian Chadd (void) resource_int_value(device_get_name(sc->sc_dev),
330f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev),
331f9950f9aSAdrian Chadd sbuf, &val);
332f9950f9aSAdrian Chadd if (val != 1)
333f9950f9aSAdrian Chadd return (0);
334f9950f9aSAdrian Chadd pcfg->force_link = 1;
335f9950f9aSAdrian Chadd
336f9950f9aSAdrian Chadd /* force_link is set; let's parse the rest of the fields */
337f9950f9aSAdrian Chadd snprintf(sbuf, 128, "port.%d.speed", port);
338f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev),
339f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev),
340f9950f9aSAdrian Chadd sbuf, &val) == 0) {
341f9950f9aSAdrian Chadd switch (val) {
342f9950f9aSAdrian Chadd case 10:
343f9950f9aSAdrian Chadd pcfg->speed = AR8327_PORT_SPEED_10;
344f9950f9aSAdrian Chadd break;
345f9950f9aSAdrian Chadd case 100:
346f9950f9aSAdrian Chadd pcfg->speed = AR8327_PORT_SPEED_100;
347f9950f9aSAdrian Chadd break;
348f9950f9aSAdrian Chadd case 1000:
349f9950f9aSAdrian Chadd pcfg->speed = AR8327_PORT_SPEED_1000;
350f9950f9aSAdrian Chadd break;
351f9950f9aSAdrian Chadd default:
352f9950f9aSAdrian Chadd device_printf(sc->sc_dev,
353f9950f9aSAdrian Chadd "%s: invalid port %d duplex value (%d)\n",
354f9950f9aSAdrian Chadd __func__,
355f9950f9aSAdrian Chadd port,
356f9950f9aSAdrian Chadd val);
357f9950f9aSAdrian Chadd return (0);
358f9950f9aSAdrian Chadd }
359f9950f9aSAdrian Chadd }
360f9950f9aSAdrian Chadd
361f9950f9aSAdrian Chadd snprintf(sbuf, 128, "port.%d.duplex", port);
362f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev),
363f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev),
364f9950f9aSAdrian Chadd sbuf, &val) == 0)
365f9950f9aSAdrian Chadd pcfg->duplex = val;
366f9950f9aSAdrian Chadd
367f9950f9aSAdrian Chadd snprintf(sbuf, 128, "port.%d.txpause", port);
368f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev),
369f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev),
370f9950f9aSAdrian Chadd sbuf, &val) == 0)
371f9950f9aSAdrian Chadd pcfg->txpause = val;
372f9950f9aSAdrian Chadd
373f9950f9aSAdrian Chadd snprintf(sbuf, 128, "port.%d.rxpause", port);
374f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev),
375f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev),
376f9950f9aSAdrian Chadd sbuf, &val) == 0)
377f9950f9aSAdrian Chadd pcfg->rxpause = val;
378f9950f9aSAdrian Chadd
37978549b94SAdrian Chadd #if 1
380f9950f9aSAdrian Chadd device_printf(sc->sc_dev,
381f9950f9aSAdrian Chadd "%s: port %d: speed=%d, duplex=%d, txpause=%d, rxpause=%d\n",
382f9950f9aSAdrian Chadd __func__,
383f9950f9aSAdrian Chadd port,
384f9950f9aSAdrian Chadd pcfg->speed,
385f9950f9aSAdrian Chadd pcfg->duplex,
386f9950f9aSAdrian Chadd pcfg->txpause,
387f9950f9aSAdrian Chadd pcfg->rxpause);
388f9950f9aSAdrian Chadd #endif
389f9950f9aSAdrian Chadd
390f9950f9aSAdrian Chadd return (1);
391f9950f9aSAdrian Chadd }
392f9950f9aSAdrian Chadd
393f9950f9aSAdrian Chadd /*
394f9950f9aSAdrian Chadd * Parse the pad configuration from the boot hints.
395f9950f9aSAdrian Chadd *
396f9950f9aSAdrian Chadd * The (mostly optional) fields are:
397f9950f9aSAdrian Chadd *
398f9950f9aSAdrian Chadd * uint32_t mode;
399f9950f9aSAdrian Chadd * uint32_t rxclk_sel;
400f9950f9aSAdrian Chadd * uint32_t txclk_sel;
401f9950f9aSAdrian Chadd * uint32_t txclk_delay_sel;
402f9950f9aSAdrian Chadd * uint32_t rxclk_delay_sel;
403f9950f9aSAdrian Chadd * uint32_t txclk_delay_en;
404f9950f9aSAdrian Chadd * uint32_t rxclk_delay_en;
405f9950f9aSAdrian Chadd * uint32_t sgmii_delay_en;
406f9950f9aSAdrian Chadd * uint32_t pipe_rxclk_sel;
407f9950f9aSAdrian Chadd *
408f9950f9aSAdrian Chadd * If mode isn't in the hints, 0 is returned.
409f9950f9aSAdrian Chadd * Else the structure is fleshed out and 1 is returned.
410f9950f9aSAdrian Chadd */
411f9950f9aSAdrian Chadd static int
ar8327_fetch_pdata_pad(struct arswitch_softc * sc,struct ar8327_pad_cfg * pc,int pad)412f9950f9aSAdrian Chadd ar8327_fetch_pdata_pad(struct arswitch_softc *sc,
413f9950f9aSAdrian Chadd struct ar8327_pad_cfg *pc,
414f9950f9aSAdrian Chadd int pad)
415f9950f9aSAdrian Chadd {
416f9950f9aSAdrian Chadd int val;
417f9950f9aSAdrian Chadd char sbuf[128];
418f9950f9aSAdrian Chadd
419f9950f9aSAdrian Chadd /* Check if mode exists */
420f9950f9aSAdrian Chadd val = 0;
421f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.mode", pad);
422f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev),
423f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev),
424f9950f9aSAdrian Chadd sbuf, &val) != 0)
425f9950f9aSAdrian Chadd return (0);
426f9950f9aSAdrian Chadd
427f9950f9aSAdrian Chadd /* assume that 'mode' exists and was found */
428f9950f9aSAdrian Chadd pc->mode = val;
429f9950f9aSAdrian Chadd
430f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.rxclk_sel", pad);
431f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev),
432f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev),
433f9950f9aSAdrian Chadd sbuf, &val) == 0)
434f9950f9aSAdrian Chadd pc->rxclk_sel = val;
435f9950f9aSAdrian Chadd
436f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.txclk_sel", pad);
437f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev),
438f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev),
439f9950f9aSAdrian Chadd sbuf, &val) == 0)
440f9950f9aSAdrian Chadd pc->txclk_sel = val;
441f9950f9aSAdrian Chadd
442f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.txclk_delay_sel", pad);
443f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev),
444f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev),
445f9950f9aSAdrian Chadd sbuf, &val) == 0)
446f9950f9aSAdrian Chadd pc->txclk_delay_sel = val;
447f9950f9aSAdrian Chadd
448f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.rxclk_delay_sel", pad);
449f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev),
450f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev),
451f9950f9aSAdrian Chadd sbuf, &val) == 0)
452f9950f9aSAdrian Chadd pc->rxclk_delay_sel = val;
453f9950f9aSAdrian Chadd
454f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.txclk_delay_en", pad);
455f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev),
456f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev),
457f9950f9aSAdrian Chadd sbuf, &val) == 0)
458f9950f9aSAdrian Chadd pc->txclk_delay_en = val;
459f9950f9aSAdrian Chadd
460f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.rxclk_delay_en", pad);
461f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev),
462f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev),
463f9950f9aSAdrian Chadd sbuf, &val) == 0)
464f9950f9aSAdrian Chadd pc->rxclk_delay_en = val;
465f9950f9aSAdrian Chadd
466f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.sgmii_delay_en", pad);
467f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev),
468f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev),
469f9950f9aSAdrian Chadd sbuf, &val) == 0)
470f9950f9aSAdrian Chadd pc->sgmii_delay_en = val;
471f9950f9aSAdrian Chadd
472f9950f9aSAdrian Chadd snprintf(sbuf, 128, "pad.%d.pipe_rxclk_sel", pad);
473f9950f9aSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev),
474f9950f9aSAdrian Chadd device_get_unit(sc->sc_dev),
475f9950f9aSAdrian Chadd sbuf, &val) == 0)
476f9950f9aSAdrian Chadd pc->pipe_rxclk_sel = val;
477f9950f9aSAdrian Chadd
478db37238fSAdrian Chadd if (bootverbose) {
479f9950f9aSAdrian Chadd device_printf(sc->sc_dev,
480f9950f9aSAdrian Chadd "%s: pad %d: mode=%d, rxclk_sel=%d, txclk_sel=%d, "
481f9950f9aSAdrian Chadd "txclk_delay_sel=%d, rxclk_delay_sel=%d, txclk_delay_en=%d, "
482f9950f9aSAdrian Chadd "rxclk_enable_en=%d, sgmii_delay_en=%d, pipe_rxclk_sel=%d\n",
483f9950f9aSAdrian Chadd __func__,
484f9950f9aSAdrian Chadd pad,
485f9950f9aSAdrian Chadd pc->mode,
486f9950f9aSAdrian Chadd pc->rxclk_sel,
487f9950f9aSAdrian Chadd pc->txclk_sel,
488f9950f9aSAdrian Chadd pc->txclk_delay_sel,
489f9950f9aSAdrian Chadd pc->rxclk_delay_sel,
490f9950f9aSAdrian Chadd pc->txclk_delay_en,
491f9950f9aSAdrian Chadd pc->rxclk_delay_en,
492f9950f9aSAdrian Chadd pc->sgmii_delay_en,
493f9950f9aSAdrian Chadd pc->pipe_rxclk_sel);
494db37238fSAdrian Chadd }
495f9950f9aSAdrian Chadd
496f9950f9aSAdrian Chadd return (1);
497f9950f9aSAdrian Chadd }
498f9950f9aSAdrian Chadd
499f9950f9aSAdrian Chadd /*
500810bdeddSAdrian Chadd * Fetch the SGMII configuration block from the boot hints.
501810bdeddSAdrian Chadd */
502810bdeddSAdrian Chadd static int
ar8327_fetch_pdata_sgmii(struct arswitch_softc * sc,struct ar8327_sgmii_cfg * scfg)503810bdeddSAdrian Chadd ar8327_fetch_pdata_sgmii(struct arswitch_softc *sc,
504810bdeddSAdrian Chadd struct ar8327_sgmii_cfg *scfg)
505810bdeddSAdrian Chadd {
506810bdeddSAdrian Chadd int val;
507810bdeddSAdrian Chadd
508810bdeddSAdrian Chadd /* sgmii_ctrl */
509810bdeddSAdrian Chadd val = 0;
510810bdeddSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev),
511810bdeddSAdrian Chadd device_get_unit(sc->sc_dev),
512810bdeddSAdrian Chadd "sgmii.ctrl", &val) != 0)
513810bdeddSAdrian Chadd return (0);
514810bdeddSAdrian Chadd scfg->sgmii_ctrl = val;
515810bdeddSAdrian Chadd
516810bdeddSAdrian Chadd /* serdes_aen */
517810bdeddSAdrian Chadd val = 0;
518810bdeddSAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev),
519810bdeddSAdrian Chadd device_get_unit(sc->sc_dev),
520810bdeddSAdrian Chadd "sgmii.serdes_aen", &val) != 0)
521810bdeddSAdrian Chadd return (0);
522810bdeddSAdrian Chadd scfg->serdes_aen = val;
523810bdeddSAdrian Chadd
524810bdeddSAdrian Chadd return (1);
525810bdeddSAdrian Chadd }
526810bdeddSAdrian Chadd
527810bdeddSAdrian Chadd /*
528b67ba111SAdrian Chadd * Fetch the LED configuration from the boot hints.
529b67ba111SAdrian Chadd */
530b67ba111SAdrian Chadd static int
ar8327_fetch_pdata_led(struct arswitch_softc * sc,struct ar8327_led_cfg * lcfg)531b67ba111SAdrian Chadd ar8327_fetch_pdata_led(struct arswitch_softc *sc,
532b67ba111SAdrian Chadd struct ar8327_led_cfg *lcfg)
533b67ba111SAdrian Chadd {
534b67ba111SAdrian Chadd int val;
535b67ba111SAdrian Chadd
536b67ba111SAdrian Chadd val = 0;
537b67ba111SAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev),
538b67ba111SAdrian Chadd device_get_unit(sc->sc_dev),
539b67ba111SAdrian Chadd "led.ctrl0", &val) != 0)
540b67ba111SAdrian Chadd return (0);
541b67ba111SAdrian Chadd lcfg->led_ctrl0 = val;
542b67ba111SAdrian Chadd
543b67ba111SAdrian Chadd val = 0;
544b67ba111SAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev),
545b67ba111SAdrian Chadd device_get_unit(sc->sc_dev),
546b67ba111SAdrian Chadd "led.ctrl1", &val) != 0)
547b67ba111SAdrian Chadd return (0);
548b67ba111SAdrian Chadd lcfg->led_ctrl1 = val;
549b67ba111SAdrian Chadd
550b67ba111SAdrian Chadd val = 0;
551b67ba111SAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev),
552b67ba111SAdrian Chadd device_get_unit(sc->sc_dev),
553b67ba111SAdrian Chadd "led.ctrl2", &val) != 0)
554b67ba111SAdrian Chadd return (0);
555b67ba111SAdrian Chadd lcfg->led_ctrl2 = val;
556b67ba111SAdrian Chadd
557b67ba111SAdrian Chadd val = 0;
558b67ba111SAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev),
559b67ba111SAdrian Chadd device_get_unit(sc->sc_dev),
560b67ba111SAdrian Chadd "led.ctrl3", &val) != 0)
561b67ba111SAdrian Chadd return (0);
562b67ba111SAdrian Chadd lcfg->led_ctrl3 = val;
563b67ba111SAdrian Chadd
564b67ba111SAdrian Chadd val = 0;
565b67ba111SAdrian Chadd if (resource_int_value(device_get_name(sc->sc_dev),
566b67ba111SAdrian Chadd device_get_unit(sc->sc_dev),
567b67ba111SAdrian Chadd "led.open_drain", &val) != 0)
568b67ba111SAdrian Chadd return (0);
569b67ba111SAdrian Chadd lcfg->open_drain = val;
570b67ba111SAdrian Chadd
571b67ba111SAdrian Chadd return (1);
572b67ba111SAdrian Chadd }
573b67ba111SAdrian Chadd
574b67ba111SAdrian Chadd /*
5759ab21e32SAdrian Chadd * Initialise the ar8327 specific hardware features from
5769ab21e32SAdrian Chadd * the hints provided in the boot environment.
5779ab21e32SAdrian Chadd */
5789ab21e32SAdrian Chadd static int
ar8327_init_pdata(struct arswitch_softc * sc)5799ab21e32SAdrian Chadd ar8327_init_pdata(struct arswitch_softc *sc)
5809ab21e32SAdrian Chadd {
5819ab21e32SAdrian Chadd struct ar8327_pad_cfg pc;
5829ab21e32SAdrian Chadd struct ar8327_port_cfg port_cfg;
583810bdeddSAdrian Chadd struct ar8327_sgmii_cfg scfg;
584b67ba111SAdrian Chadd struct ar8327_led_cfg lcfg;
585810bdeddSAdrian Chadd uint32_t t, new_pos, pos;
5869ab21e32SAdrian Chadd
587f9950f9aSAdrian Chadd /* Port 0 */
5889ab21e32SAdrian Chadd bzero(&port_cfg, sizeof(port_cfg));
589f9950f9aSAdrian Chadd sc->ar8327.port0_status = 0;
590f9950f9aSAdrian Chadd if (ar8327_fetch_pdata_port(sc, &port_cfg, 0))
5919ab21e32SAdrian Chadd sc->ar8327.port0_status = ar8327_get_port_init_status(&port_cfg);
5929ab21e32SAdrian Chadd
593f9950f9aSAdrian Chadd /* Port 6 */
5949ab21e32SAdrian Chadd bzero(&port_cfg, sizeof(port_cfg));
595f9950f9aSAdrian Chadd sc->ar8327.port6_status = 0;
596f9950f9aSAdrian Chadd if (ar8327_fetch_pdata_port(sc, &port_cfg, 6))
5979ab21e32SAdrian Chadd sc->ar8327.port6_status = ar8327_get_port_init_status(&port_cfg);
5989ab21e32SAdrian Chadd
5999ab21e32SAdrian Chadd /* Pad 0 */
6009ab21e32SAdrian Chadd bzero(&pc, sizeof(pc));
601f9950f9aSAdrian Chadd t = 0;
602f9950f9aSAdrian Chadd if (ar8327_fetch_pdata_pad(sc, &pc, 0))
6039ab21e32SAdrian Chadd t = ar8327_get_pad_cfg(&pc);
6049ab21e32SAdrian Chadd #if 0
6059ab21e32SAdrian Chadd if (AR8X16_IS_SWITCH(sc, AR8337))
6069ab21e32SAdrian Chadd t |= AR8337_PAD_MAC06_EXCHANGE_EN;
6077330dd0bSAdrian Chadd #endif
6089ab21e32SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PAD0_MODE, t);
6099ab21e32SAdrian Chadd
6109ab21e32SAdrian Chadd /* Pad 5 */
6119ab21e32SAdrian Chadd bzero(&pc, sizeof(pc));
612f9950f9aSAdrian Chadd t = 0;
613f9950f9aSAdrian Chadd if (ar8327_fetch_pdata_pad(sc, &pc, 5))
6149ab21e32SAdrian Chadd t = ar8327_get_pad_cfg(&pc);
6159ab21e32SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PAD5_MODE, t);
6169ab21e32SAdrian Chadd
6179ab21e32SAdrian Chadd /* Pad 6 */
6189ab21e32SAdrian Chadd bzero(&pc, sizeof(pc));
619f9950f9aSAdrian Chadd t = 0;
620f9950f9aSAdrian Chadd if (ar8327_fetch_pdata_pad(sc, &pc, 6))
6219ab21e32SAdrian Chadd t = ar8327_get_pad_cfg(&pc);
6229ab21e32SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PAD6_MODE, t);
6239ab21e32SAdrian Chadd
624810bdeddSAdrian Chadd pos = arswitch_readreg(sc->sc_dev, AR8327_REG_POWER_ON_STRIP);
625810bdeddSAdrian Chadd new_pos = pos;
626810bdeddSAdrian Chadd
6279ab21e32SAdrian Chadd /* XXX LED config */
628b67ba111SAdrian Chadd bzero(&lcfg, sizeof(lcfg));
629b67ba111SAdrian Chadd if (ar8327_fetch_pdata_led(sc, &lcfg)) {
630b67ba111SAdrian Chadd if (lcfg.open_drain)
631b67ba111SAdrian Chadd new_pos |= AR8327_POWER_ON_STRIP_LED_OPEN_EN;
632b67ba111SAdrian Chadd else
633b67ba111SAdrian Chadd new_pos &= ~AR8327_POWER_ON_STRIP_LED_OPEN_EN;
634b67ba111SAdrian Chadd
635b67ba111SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_LED_CTRL0,
636b67ba111SAdrian Chadd lcfg.led_ctrl0);
637b67ba111SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_LED_CTRL1,
638b67ba111SAdrian Chadd lcfg.led_ctrl1);
639b67ba111SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_LED_CTRL2,
640b67ba111SAdrian Chadd lcfg.led_ctrl2);
641b67ba111SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_LED_CTRL3,
642b67ba111SAdrian Chadd lcfg.led_ctrl3);
643b67ba111SAdrian Chadd
644b67ba111SAdrian Chadd if (new_pos != pos)
645b67ba111SAdrian Chadd new_pos |= AR8327_POWER_ON_STRIP_POWER_ON_SEL;
646b67ba111SAdrian Chadd }
6479ab21e32SAdrian Chadd
648810bdeddSAdrian Chadd /* SGMII config */
649810bdeddSAdrian Chadd bzero(&scfg, sizeof(scfg));
650810bdeddSAdrian Chadd if (ar8327_fetch_pdata_sgmii(sc, &scfg)) {
65178549b94SAdrian Chadd device_printf(sc->sc_dev, "%s: SGMII cfg?\n", __func__);
652810bdeddSAdrian Chadd t = scfg.sgmii_ctrl;
653810bdeddSAdrian Chadd if (sc->chip_rev == 1)
654810bdeddSAdrian Chadd t |= AR8327_SGMII_CTRL_EN_PLL |
655810bdeddSAdrian Chadd AR8327_SGMII_CTRL_EN_RX |
656810bdeddSAdrian Chadd AR8327_SGMII_CTRL_EN_TX;
657810bdeddSAdrian Chadd else
658810bdeddSAdrian Chadd t &= ~(AR8327_SGMII_CTRL_EN_PLL |
659810bdeddSAdrian Chadd AR8327_SGMII_CTRL_EN_RX |
660810bdeddSAdrian Chadd AR8327_SGMII_CTRL_EN_TX);
661810bdeddSAdrian Chadd
662810bdeddSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_SGMII_CTRL, t);
663810bdeddSAdrian Chadd
664810bdeddSAdrian Chadd if (scfg.serdes_aen)
665810bdeddSAdrian Chadd new_pos &= ~AR8327_POWER_ON_STRIP_SERDES_AEN;
666810bdeddSAdrian Chadd else
667810bdeddSAdrian Chadd new_pos |= AR8327_POWER_ON_STRIP_SERDES_AEN;
668810bdeddSAdrian Chadd }
669810bdeddSAdrian Chadd
670810bdeddSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_POWER_ON_STRIP, new_pos);
6719ab21e32SAdrian Chadd
6729ab21e32SAdrian Chadd return (0);
6739ab21e32SAdrian Chadd }
6747330dd0bSAdrian Chadd
6757330dd0bSAdrian Chadd static int
ar8327_hw_setup(struct arswitch_softc * sc)6767330dd0bSAdrian Chadd ar8327_hw_setup(struct arswitch_softc *sc)
6777330dd0bSAdrian Chadd {
6787330dd0bSAdrian Chadd int i;
6797330dd0bSAdrian Chadd int err;
6807330dd0bSAdrian Chadd
6817330dd0bSAdrian Chadd /* pdata fetch and setup */
6827330dd0bSAdrian Chadd err = ar8327_init_pdata(sc);
6837330dd0bSAdrian Chadd if (err != 0)
6847330dd0bSAdrian Chadd return (err);
6857330dd0bSAdrian Chadd
6867330dd0bSAdrian Chadd /* XXX init leds */
6877330dd0bSAdrian Chadd
6887330dd0bSAdrian Chadd for (i = 0; i < AR8327_NUM_PHYS; i++) {
6897330dd0bSAdrian Chadd /* phy fixup */
6907330dd0bSAdrian Chadd ar8327_phy_fixup(sc, i);
6917330dd0bSAdrian Chadd
6927330dd0bSAdrian Chadd /* start PHY autonegotiation? */
6937330dd0bSAdrian Chadd /* XXX is this done as part of the normal PHY setup? */
6947330dd0bSAdrian Chadd
69574b8d63dSPedro F. Giffuni }
6967330dd0bSAdrian Chadd
6977330dd0bSAdrian Chadd /* Let things settle */
6987330dd0bSAdrian Chadd DELAY(1000);
6997330dd0bSAdrian Chadd
7007330dd0bSAdrian Chadd return (0);
7017330dd0bSAdrian Chadd }
7027330dd0bSAdrian Chadd
70362042c97SAdrian Chadd static int
ar8327_atu_learn_default(struct arswitch_softc * sc)70462042c97SAdrian Chadd ar8327_atu_learn_default(struct arswitch_softc *sc)
70562042c97SAdrian Chadd {
70662042c97SAdrian Chadd
70762042c97SAdrian Chadd device_printf(sc->sc_dev, "%s: TODO!\n", __func__);
70862042c97SAdrian Chadd return (0);
70962042c97SAdrian Chadd }
71062042c97SAdrian Chadd
7117330dd0bSAdrian Chadd /*
7127330dd0bSAdrian Chadd * Initialise other global values, for the AR8327.
7137330dd0bSAdrian Chadd */
7147330dd0bSAdrian Chadd static int
ar8327_hw_global_setup(struct arswitch_softc * sc)7157330dd0bSAdrian Chadd ar8327_hw_global_setup(struct arswitch_softc *sc)
7167330dd0bSAdrian Chadd {
7177330dd0bSAdrian Chadd uint32_t t;
7187330dd0bSAdrian Chadd
7196d011946SKristof Provost ARSWITCH_LOCK(sc);
7206d011946SKristof Provost
7217330dd0bSAdrian Chadd /* enable CPU port and disable mirror port */
7227330dd0bSAdrian Chadd t = AR8327_FWD_CTRL0_CPU_PORT_EN |
7237330dd0bSAdrian Chadd AR8327_FWD_CTRL0_MIRROR_PORT;
7247330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_FWD_CTRL0, t);
7257330dd0bSAdrian Chadd
7267330dd0bSAdrian Chadd /* forward multicast and broadcast frames to CPU */
7277330dd0bSAdrian Chadd t = (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_UC_FLOOD_S) |
7287330dd0bSAdrian Chadd (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_MC_FLOOD_S) |
7297330dd0bSAdrian Chadd (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_BC_FLOOD_S);
7307330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_FWD_CTRL1, t);
7317330dd0bSAdrian Chadd
7327330dd0bSAdrian Chadd /* enable jumbo frames */
7337330dd0bSAdrian Chadd /* XXX need to macro-shift the value! */
7347330dd0bSAdrian Chadd arswitch_modifyreg(sc->sc_dev, AR8327_REG_MAX_FRAME_SIZE,
7357330dd0bSAdrian Chadd AR8327_MAX_FRAME_SIZE_MTU, 9018 + 8 + 2);
7367330dd0bSAdrian Chadd
7377330dd0bSAdrian Chadd /* Enable MIB counters */
7387330dd0bSAdrian Chadd arswitch_modifyreg(sc->sc_dev, AR8327_REG_MODULE_EN,
7397330dd0bSAdrian Chadd AR8327_MODULE_EN_MIB, AR8327_MODULE_EN_MIB);
7407330dd0bSAdrian Chadd
741db37238fSAdrian Chadd /* Disable EEE on all ports due to stability issues */
742db37238fSAdrian Chadd t = arswitch_readreg(sc->sc_dev, AR8327_REG_EEE_CTRL);
743db37238fSAdrian Chadd t |= AR8327_EEE_CTRL_DISABLE_PHY(0) |
744db37238fSAdrian Chadd AR8327_EEE_CTRL_DISABLE_PHY(1) |
745db37238fSAdrian Chadd AR8327_EEE_CTRL_DISABLE_PHY(2) |
746db37238fSAdrian Chadd AR8327_EEE_CTRL_DISABLE_PHY(3) |
747db37238fSAdrian Chadd AR8327_EEE_CTRL_DISABLE_PHY(4);
748db37238fSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_EEE_CTRL, t);
749db37238fSAdrian Chadd
7504ff2f60dSAdrian Chadd /* Set the right number of ports */
751749cac13SAdrian Chadd /* GMAC0 (CPU), GMAC1..5 (PHYs), GMAC6 (CPU) */
752749cac13SAdrian Chadd sc->info.es_nports = 7;
7534ff2f60dSAdrian Chadd
7546d011946SKristof Provost ARSWITCH_UNLOCK(sc);
7557330dd0bSAdrian Chadd return (0);
7567330dd0bSAdrian Chadd }
7577330dd0bSAdrian Chadd
7587330dd0bSAdrian Chadd /*
75978549b94SAdrian Chadd * Port setup. Called at attach time.
7607330dd0bSAdrian Chadd */
7617330dd0bSAdrian Chadd static void
ar8327_port_init(struct arswitch_softc * sc,int port)7627330dd0bSAdrian Chadd ar8327_port_init(struct arswitch_softc *sc, int port)
7637330dd0bSAdrian Chadd {
7647330dd0bSAdrian Chadd uint32_t t;
76578549b94SAdrian Chadd int ports;
76678549b94SAdrian Chadd
76778549b94SAdrian Chadd /* For now, port can see all other ports */
76878549b94SAdrian Chadd ports = 0x7f;
7697330dd0bSAdrian Chadd
7709ab21e32SAdrian Chadd if (port == AR8X16_PORT_CPU)
7719ab21e32SAdrian Chadd t = sc->ar8327.port0_status;
7727330dd0bSAdrian Chadd else if (port == 6)
7739ab21e32SAdrian Chadd t = sc->ar8327.port6_status;
7747330dd0bSAdrian Chadd else
7757330dd0bSAdrian Chadd t = AR8X16_PORT_STS_LINK_AUTO;
7767330dd0bSAdrian Chadd
7777330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_STATUS(port), t);
7787330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_HEADER(port), 0);
7797330dd0bSAdrian Chadd
7800d2041a0SAdrian Chadd /*
7810d2041a0SAdrian Chadd * Default to 1 port group.
7820d2041a0SAdrian Chadd */
7837330dd0bSAdrian Chadd t = 1 << AR8327_PORT_VLAN0_DEF_SVID_S;
7847330dd0bSAdrian Chadd t |= 1 << AR8327_PORT_VLAN0_DEF_CVID_S;
7857330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_VLAN0(port), t);
7867330dd0bSAdrian Chadd
7877330dd0bSAdrian Chadd t = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH << AR8327_PORT_VLAN1_OUT_MODE_S;
7887330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_VLAN1(port), t);
7897330dd0bSAdrian Chadd
79003b5d827SAdrian Chadd /*
79103b5d827SAdrian Chadd * This doesn't configure any ports which this port can "see".
79203b5d827SAdrian Chadd * bits 0-6 control which ports a frame coming into this port
79303b5d827SAdrian Chadd * can be sent out to.
79403b5d827SAdrian Chadd *
79503b5d827SAdrian Chadd * So by doing this, we're making it impossible to send frames out
79603b5d827SAdrian Chadd * to that port.
79703b5d827SAdrian Chadd */
7987330dd0bSAdrian Chadd t = AR8327_PORT_LOOKUP_LEARN;
7997330dd0bSAdrian Chadd t |= AR8X16_PORT_CTRL_STATE_FORWARD << AR8327_PORT_LOOKUP_STATE_S;
80003b5d827SAdrian Chadd
80103b5d827SAdrian Chadd /* So this allows traffic to any port except ourselves */
80278549b94SAdrian Chadd t |= (ports & ~(1 << port));
8037330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_LOOKUP(port), t);
8047330dd0bSAdrian Chadd }
8057330dd0bSAdrian Chadd
8067330dd0bSAdrian Chadd static int
ar8327_port_vlan_setup(struct arswitch_softc * sc,etherswitch_port_t * p)8077330dd0bSAdrian Chadd ar8327_port_vlan_setup(struct arswitch_softc *sc, etherswitch_port_t *p)
8087330dd0bSAdrian Chadd {
8097330dd0bSAdrian Chadd
810749cac13SAdrian Chadd /* Check: ADDTAG/STRIPTAG - exclusive */
811749cac13SAdrian Chadd
812749cac13SAdrian Chadd ARSWITCH_LOCK(sc);
813749cac13SAdrian Chadd
814749cac13SAdrian Chadd /* Set the PVID. */
815749cac13SAdrian Chadd if (p->es_pvid != 0)
816749cac13SAdrian Chadd sc->hal.arswitch_vlan_set_pvid(sc, p->es_port, p->es_pvid);
817749cac13SAdrian Chadd
818749cac13SAdrian Chadd /*
819749cac13SAdrian Chadd * DOUBLE_TAG
820749cac13SAdrian Chadd * VLAN_MODE_ADD
821749cac13SAdrian Chadd * VLAN_MODE_STRIP
822749cac13SAdrian Chadd */
823749cac13SAdrian Chadd ARSWITCH_UNLOCK(sc);
8247330dd0bSAdrian Chadd return (0);
8257330dd0bSAdrian Chadd }
8267330dd0bSAdrian Chadd
82778549b94SAdrian Chadd /*
82878549b94SAdrian Chadd * Get the port VLAN configuration.
82978549b94SAdrian Chadd */
8307330dd0bSAdrian Chadd static int
ar8327_port_vlan_get(struct arswitch_softc * sc,etherswitch_port_t * p)8317330dd0bSAdrian Chadd ar8327_port_vlan_get(struct arswitch_softc *sc, etherswitch_port_t *p)
8327330dd0bSAdrian Chadd {
833749cac13SAdrian Chadd
834749cac13SAdrian Chadd ARSWITCH_LOCK(sc);
835749cac13SAdrian Chadd
836749cac13SAdrian Chadd /* Retrieve the PVID */
837749cac13SAdrian Chadd sc->hal.arswitch_vlan_get_pvid(sc, p->es_port, &p->es_pvid);
838749cac13SAdrian Chadd
839036e1c76SAdrian Chadd /* Retrieve the current port configuration from the VTU */
840749cac13SAdrian Chadd /*
841749cac13SAdrian Chadd * DOUBLE_TAG
842749cac13SAdrian Chadd * VLAN_MODE_ADD
843749cac13SAdrian Chadd * VLAN_MODE_STRIP
844749cac13SAdrian Chadd */
845749cac13SAdrian Chadd
846749cac13SAdrian Chadd ARSWITCH_UNLOCK(sc);
8477330dd0bSAdrian Chadd return (0);
8487330dd0bSAdrian Chadd }
8497330dd0bSAdrian Chadd
8507330dd0bSAdrian Chadd static void
ar8327_port_disable_mirror(struct arswitch_softc * sc,int port)851036e1c76SAdrian Chadd ar8327_port_disable_mirror(struct arswitch_softc *sc, int port)
852036e1c76SAdrian Chadd {
853036e1c76SAdrian Chadd
854036e1c76SAdrian Chadd arswitch_modifyreg(sc->sc_dev,
855036e1c76SAdrian Chadd AR8327_REG_PORT_LOOKUP(port),
856036e1c76SAdrian Chadd AR8327_PORT_LOOKUP_ING_MIRROR_EN,
857036e1c76SAdrian Chadd 0);
858036e1c76SAdrian Chadd arswitch_modifyreg(sc->sc_dev,
859036e1c76SAdrian Chadd AR8327_REG_PORT_HOL_CTRL1(port),
860036e1c76SAdrian Chadd AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN,
861036e1c76SAdrian Chadd 0);
862036e1c76SAdrian Chadd }
863036e1c76SAdrian Chadd
864036e1c76SAdrian Chadd static void
ar8327_reset_vlans(struct arswitch_softc * sc)8657330dd0bSAdrian Chadd ar8327_reset_vlans(struct arswitch_softc *sc)
8667330dd0bSAdrian Chadd {
8677330dd0bSAdrian Chadd int i;
868036e1c76SAdrian Chadd uint32_t t;
86978549b94SAdrian Chadd int ports;
87078549b94SAdrian Chadd
87178549b94SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
87278549b94SAdrian Chadd ARSWITCH_LOCK(sc);
87378549b94SAdrian Chadd
87478549b94SAdrian Chadd /* Clear the existing VLAN configuration */
87578549b94SAdrian Chadd memset(sc->vid, 0, sizeof(sc->vid));
8767330dd0bSAdrian Chadd
8777330dd0bSAdrian Chadd /*
87803b5d827SAdrian Chadd * Disable mirroring.
87903b5d827SAdrian Chadd */
88003b5d827SAdrian Chadd arswitch_modifyreg(sc->sc_dev, AR8327_REG_FWD_CTRL0,
88103b5d827SAdrian Chadd AR8327_FWD_CTRL0_MIRROR_PORT,
88203b5d827SAdrian Chadd (0xF << AR8327_FWD_CTRL0_MIRROR_PORT_S));
88303b5d827SAdrian Chadd
88403b5d827SAdrian Chadd /*
88578549b94SAdrian Chadd * XXX TODO: disable any Q-in-Q port configuration,
88678549b94SAdrian Chadd * tagging, egress filters, etc.
8877330dd0bSAdrian Chadd */
88878549b94SAdrian Chadd
88978549b94SAdrian Chadd /*
89078549b94SAdrian Chadd * For now, let's default to one portgroup, just so traffic
89178549b94SAdrian Chadd * flows. All ports can see other ports. There are two CPU GMACs
89278549b94SAdrian Chadd * (GMAC0, GMAC6), GMAC1..GMAC5 are external PHYs.
89378549b94SAdrian Chadd *
89478549b94SAdrian Chadd * (ETHERSWITCH_VLAN_PORT)
89578549b94SAdrian Chadd */
89678549b94SAdrian Chadd ports = 0x7f;
89778549b94SAdrian Chadd
898036e1c76SAdrian Chadd /*
899036e1c76SAdrian Chadd * XXX TODO: set things up correctly for vlans!
900036e1c76SAdrian Chadd */
9017330dd0bSAdrian Chadd for (i = 0; i < AR8327_NUM_PORTS; i++) {
902036e1c76SAdrian Chadd int egress, ingress;
90378549b94SAdrian Chadd
904036e1c76SAdrian Chadd if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) {
905749cac13SAdrian Chadd sc->vid[i] = i | ETHERSWITCH_VID_VALID;
906036e1c76SAdrian Chadd /* set egress == out_keep */
907036e1c76SAdrian Chadd ingress = AR8X16_PORT_VLAN_MODE_PORT_ONLY;
908036e1c76SAdrian Chadd /* in_port_only, forward */
909036e1c76SAdrian Chadd egress = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH;
910036e1c76SAdrian Chadd } else if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
911036e1c76SAdrian Chadd ingress = AR8X16_PORT_VLAN_MODE_SECURE;
912036e1c76SAdrian Chadd egress = AR8327_PORT_VLAN1_OUT_MODE_UNMOD;
913036e1c76SAdrian Chadd } else {
914036e1c76SAdrian Chadd /* set egress == out_keep */
915036e1c76SAdrian Chadd ingress = AR8X16_PORT_VLAN_MODE_PORT_ONLY;
916036e1c76SAdrian Chadd /* in_port_only, forward */
917036e1c76SAdrian Chadd egress = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH;
918036e1c76SAdrian Chadd }
919749cac13SAdrian Chadd
920749cac13SAdrian Chadd /* set pvid = 1; there's only one vlangroup to start with */
921dd846bddSAdrian Chadd t = 1 << AR8327_PORT_VLAN0_DEF_SVID_S;
922dd846bddSAdrian Chadd t |= 1 << AR8327_PORT_VLAN0_DEF_CVID_S;
9237330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_VLAN0(i), t);
9247330dd0bSAdrian Chadd
9257330dd0bSAdrian Chadd t = AR8327_PORT_VLAN1_PORT_VLAN_PROP;
926036e1c76SAdrian Chadd t |= egress << AR8327_PORT_VLAN1_OUT_MODE_S;
9277330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_VLAN1(i), t);
9287330dd0bSAdrian Chadd
929dd846bddSAdrian Chadd /* Ports can see other ports */
930036e1c76SAdrian Chadd /* XXX not entirely true for dot1q? */
93178549b94SAdrian Chadd t = (ports & ~(1 << i)); /* all ports besides us */
9327330dd0bSAdrian Chadd t |= AR8327_PORT_LOOKUP_LEARN;
9337330dd0bSAdrian Chadd
934036e1c76SAdrian Chadd t |= ingress << AR8327_PORT_LOOKUP_IN_MODE_S;
9357330dd0bSAdrian Chadd t |= AR8X16_PORT_CTRL_STATE_FORWARD << AR8327_PORT_LOOKUP_STATE_S;
9367330dd0bSAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_LOOKUP(i), t);
937036e1c76SAdrian Chadd }
93803b5d827SAdrian Chadd
93903b5d827SAdrian Chadd /*
94003b5d827SAdrian Chadd * Disable port mirroring entirely.
94103b5d827SAdrian Chadd */
942036e1c76SAdrian Chadd for (i = 0; i < AR8327_NUM_PORTS; i++) {
943036e1c76SAdrian Chadd ar8327_port_disable_mirror(sc, i);
944036e1c76SAdrian Chadd }
945036e1c76SAdrian Chadd
946036e1c76SAdrian Chadd /*
947036e1c76SAdrian Chadd * If dot1q - set pvid; dot1q, etc.
948036e1c76SAdrian Chadd */
949036e1c76SAdrian Chadd if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
9508f1cf028SAdrian Chadd sc->vid[0] = 1;
951036e1c76SAdrian Chadd for (i = 0; i < AR8327_NUM_PORTS; i++) {
952036e1c76SAdrian Chadd /* Each port - pvid 1 */
953036e1c76SAdrian Chadd sc->hal.arswitch_vlan_set_pvid(sc, i, sc->vid[0]);
954036e1c76SAdrian Chadd }
955036e1c76SAdrian Chadd /* Initialise vlan1 - all ports, untagged */
956036e1c76SAdrian Chadd sc->hal.arswitch_set_dot1q_vlan(sc, ports, ports, sc->vid[0]);
957036e1c76SAdrian Chadd sc->vid[0] |= ETHERSWITCH_VID_VALID;
9587330dd0bSAdrian Chadd }
95978549b94SAdrian Chadd
96078549b94SAdrian Chadd ARSWITCH_UNLOCK(sc);
9617330dd0bSAdrian Chadd }
9627330dd0bSAdrian Chadd
9637330dd0bSAdrian Chadd static int
ar8327_vlan_get_port(struct arswitch_softc * sc,uint32_t * ports,int vid)964749cac13SAdrian Chadd ar8327_vlan_get_port(struct arswitch_softc *sc, uint32_t *ports, int vid)
965749cac13SAdrian Chadd {
966749cac13SAdrian Chadd int port;
967749cac13SAdrian Chadd uint32_t reg;
968749cac13SAdrian Chadd
969749cac13SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
970749cac13SAdrian Chadd
971749cac13SAdrian Chadd /* For port based vlans the vlanid is the same as the port index. */
972749cac13SAdrian Chadd port = vid & ETHERSWITCH_VID_MASK;
973749cac13SAdrian Chadd reg = arswitch_readreg(sc->sc_dev, AR8327_REG_PORT_LOOKUP(port));
974749cac13SAdrian Chadd *ports = reg & 0x7f;
975749cac13SAdrian Chadd return (0);
976749cac13SAdrian Chadd }
977749cac13SAdrian Chadd
978749cac13SAdrian Chadd static int
ar8327_vlan_set_port(struct arswitch_softc * sc,uint32_t ports,int vid)979749cac13SAdrian Chadd ar8327_vlan_set_port(struct arswitch_softc *sc, uint32_t ports, int vid)
980749cac13SAdrian Chadd {
981749cac13SAdrian Chadd int err, port;
982749cac13SAdrian Chadd
983749cac13SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
984749cac13SAdrian Chadd
985749cac13SAdrian Chadd /* For port based vlans the vlanid is the same as the port index. */
986749cac13SAdrian Chadd port = vid & ETHERSWITCH_VID_MASK;
987749cac13SAdrian Chadd
988749cac13SAdrian Chadd err = arswitch_modifyreg(sc->sc_dev, AR8327_REG_PORT_LOOKUP(port),
989749cac13SAdrian Chadd 0x7f, /* vlan membership mask */
990749cac13SAdrian Chadd (ports & 0x7f));
991749cac13SAdrian Chadd
992749cac13SAdrian Chadd if (err)
993749cac13SAdrian Chadd return (err);
994749cac13SAdrian Chadd return (0);
995749cac13SAdrian Chadd }
996749cac13SAdrian Chadd
997749cac13SAdrian Chadd static int
ar8327_vlan_getvgroup(struct arswitch_softc * sc,etherswitch_vlangroup_t * vg)9987330dd0bSAdrian Chadd ar8327_vlan_getvgroup(struct arswitch_softc *sc, etherswitch_vlangroup_t *vg)
9997330dd0bSAdrian Chadd {
100078549b94SAdrian Chadd
100178549b94SAdrian Chadd return (ar8xxx_getvgroup(sc, vg));
10027330dd0bSAdrian Chadd }
10037330dd0bSAdrian Chadd
10047330dd0bSAdrian Chadd static int
ar8327_vlan_setvgroup(struct arswitch_softc * sc,etherswitch_vlangroup_t * vg)10057330dd0bSAdrian Chadd ar8327_vlan_setvgroup(struct arswitch_softc *sc, etherswitch_vlangroup_t *vg)
10067330dd0bSAdrian Chadd {
10077330dd0bSAdrian Chadd
100878549b94SAdrian Chadd return (ar8xxx_setvgroup(sc, vg));
10097330dd0bSAdrian Chadd }
10107330dd0bSAdrian Chadd
10117330dd0bSAdrian Chadd static int
ar8327_get_pvid(struct arswitch_softc * sc,int port,int * pvid)10127330dd0bSAdrian Chadd ar8327_get_pvid(struct arswitch_softc *sc, int port, int *pvid)
10137330dd0bSAdrian Chadd {
1014749cac13SAdrian Chadd uint32_t reg;
10157330dd0bSAdrian Chadd
1016749cac13SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
1017749cac13SAdrian Chadd
1018749cac13SAdrian Chadd /*
1019749cac13SAdrian Chadd * XXX for now, assuming it's CVID; likely very wrong!
1020749cac13SAdrian Chadd */
1021749cac13SAdrian Chadd port = port & ETHERSWITCH_VID_MASK;
1022749cac13SAdrian Chadd reg = arswitch_readreg(sc->sc_dev, AR8327_REG_PORT_VLAN0(port));
1023749cac13SAdrian Chadd reg = reg >> AR8327_PORT_VLAN0_DEF_CVID_S;
1024749cac13SAdrian Chadd reg = reg & 0xfff;
1025749cac13SAdrian Chadd
1026749cac13SAdrian Chadd *pvid = reg;
10277330dd0bSAdrian Chadd return (0);
10287330dd0bSAdrian Chadd }
10297330dd0bSAdrian Chadd
10307330dd0bSAdrian Chadd static int
ar8327_set_pvid(struct arswitch_softc * sc,int port,int pvid)10317330dd0bSAdrian Chadd ar8327_set_pvid(struct arswitch_softc *sc, int port, int pvid)
10327330dd0bSAdrian Chadd {
1033749cac13SAdrian Chadd uint32_t t;
10347330dd0bSAdrian Chadd
1035749cac13SAdrian Chadd /* Limit pvid to valid values */
1036749cac13SAdrian Chadd pvid &= 0x7f;
1037749cac13SAdrian Chadd
1038749cac13SAdrian Chadd t = pvid << AR8327_PORT_VLAN0_DEF_SVID_S;
1039749cac13SAdrian Chadd t |= pvid << AR8327_PORT_VLAN0_DEF_CVID_S;
1040749cac13SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_VLAN0(port), t);
1041749cac13SAdrian Chadd
10427330dd0bSAdrian Chadd return (0);
10437330dd0bSAdrian Chadd }
10447330dd0bSAdrian Chadd
10454ff2f60dSAdrian Chadd static int
ar8327_atu_wait_ready(struct arswitch_softc * sc)104662042c97SAdrian Chadd ar8327_atu_wait_ready(struct arswitch_softc *sc)
10474ff2f60dSAdrian Chadd {
10484ff2f60dSAdrian Chadd int ret;
10494ff2f60dSAdrian Chadd
10504ff2f60dSAdrian Chadd ret = arswitch_waitreg(sc->sc_dev,
10514ff2f60dSAdrian Chadd AR8327_REG_ATU_FUNC,
10524ff2f60dSAdrian Chadd AR8327_ATU_FUNC_BUSY,
10534ff2f60dSAdrian Chadd 0,
10544ff2f60dSAdrian Chadd 1000);
10554ff2f60dSAdrian Chadd
105662042c97SAdrian Chadd return (ret);
105762042c97SAdrian Chadd }
105862042c97SAdrian Chadd
105962042c97SAdrian Chadd static int
ar8327_atu_flush(struct arswitch_softc * sc)106062042c97SAdrian Chadd ar8327_atu_flush(struct arswitch_softc *sc)
106162042c97SAdrian Chadd {
106262042c97SAdrian Chadd
106362042c97SAdrian Chadd int ret;
106462042c97SAdrian Chadd
106562042c97SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
106662042c97SAdrian Chadd
106762042c97SAdrian Chadd ret = ar8327_atu_wait_ready(sc);
10684ff2f60dSAdrian Chadd if (ret)
10694ff2f60dSAdrian Chadd device_printf(sc->sc_dev, "%s: waitreg failed\n", __func__);
10704ff2f60dSAdrian Chadd
10714ff2f60dSAdrian Chadd if (!ret)
10724ff2f60dSAdrian Chadd arswitch_writereg(sc->sc_dev,
10734ff2f60dSAdrian Chadd AR8327_REG_ATU_FUNC,
1074cadf7a00SAdrian Chadd AR8327_ATU_FUNC_OP_FLUSH | AR8327_ATU_FUNC_BUSY);
10754ff2f60dSAdrian Chadd return (ret);
10764ff2f60dSAdrian Chadd }
10774ff2f60dSAdrian Chadd
1078036e1c76SAdrian Chadd static int
ar8327_atu_flush_port(struct arswitch_softc * sc,int port)107962042c97SAdrian Chadd ar8327_atu_flush_port(struct arswitch_softc *sc, int port)
108062042c97SAdrian Chadd {
108162042c97SAdrian Chadd int ret;
108262042c97SAdrian Chadd uint32_t val;
108362042c97SAdrian Chadd
108462042c97SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
108562042c97SAdrian Chadd
108662042c97SAdrian Chadd ret = ar8327_atu_wait_ready(sc);
108762042c97SAdrian Chadd if (ret)
108862042c97SAdrian Chadd device_printf(sc->sc_dev, "%s: waitreg failed\n", __func__);
108962042c97SAdrian Chadd
109062042c97SAdrian Chadd val = AR8327_ATU_FUNC_OP_FLUSH_UNICAST;
109162042c97SAdrian Chadd val |= SM(port, AR8327_ATU_FUNC_PORT_NUM);
109262042c97SAdrian Chadd
109362042c97SAdrian Chadd if (!ret)
109462042c97SAdrian Chadd arswitch_writereg(sc->sc_dev,
109562042c97SAdrian Chadd AR8327_REG_ATU_FUNC,
109662042c97SAdrian Chadd val | AR8327_ATU_FUNC_BUSY);
109762042c97SAdrian Chadd
109862042c97SAdrian Chadd return (ret);
109962042c97SAdrian Chadd }
110062042c97SAdrian Chadd
110165d59686SAdrian Chadd /*
110265d59686SAdrian Chadd * Fetch a single entry from the ATU.
110365d59686SAdrian Chadd */
110462042c97SAdrian Chadd static int
ar8327_atu_fetch_table(struct arswitch_softc * sc,etherswitch_atu_entry_t * e,int atu_fetch_op)110562042c97SAdrian Chadd ar8327_atu_fetch_table(struct arswitch_softc *sc, etherswitch_atu_entry_t *e,
110662042c97SAdrian Chadd int atu_fetch_op)
110762042c97SAdrian Chadd {
110865d59686SAdrian Chadd uint32_t ret0, ret1, ret2, val;
110962042c97SAdrian Chadd
111065d59686SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
111165d59686SAdrian Chadd
111265d59686SAdrian Chadd switch (atu_fetch_op) {
111365d59686SAdrian Chadd case 0:
111465d59686SAdrian Chadd /* Initialise things for the first fetch */
111565d59686SAdrian Chadd
111665d59686SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: initializing\n", __func__);
111765d59686SAdrian Chadd (void) ar8327_atu_wait_ready(sc);
111865d59686SAdrian Chadd
111965d59686SAdrian Chadd arswitch_writereg(sc->sc_dev,
112065d59686SAdrian Chadd AR8327_REG_ATU_FUNC, AR8327_ATU_FUNC_OP_GET_NEXT);
112165d59686SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_ATU_DATA0, 0);
112265d59686SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_ATU_DATA1, 0);
112365d59686SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_ATU_DATA2, 0);
112465d59686SAdrian Chadd
112565d59686SAdrian Chadd return (0);
112665d59686SAdrian Chadd case 1:
112765d59686SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: reading next\n", __func__);
112865d59686SAdrian Chadd /*
112965d59686SAdrian Chadd * Attempt to read the next address entry; don't modify what
113065d59686SAdrian Chadd * is there in these registers as its used for the next fetch
113165d59686SAdrian Chadd */
113265d59686SAdrian Chadd (void) ar8327_atu_wait_ready(sc);
113365d59686SAdrian Chadd
113465d59686SAdrian Chadd /* Begin the next read event; not modifying anything */
113565d59686SAdrian Chadd val = arswitch_readreg(sc->sc_dev, AR8327_REG_ATU_FUNC);
113665d59686SAdrian Chadd val |= AR8327_ATU_FUNC_BUSY;
113765d59686SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8327_REG_ATU_FUNC, val);
113865d59686SAdrian Chadd
113965d59686SAdrian Chadd /* Wait for it to complete */
114065d59686SAdrian Chadd (void) ar8327_atu_wait_ready(sc);
114165d59686SAdrian Chadd
114265d59686SAdrian Chadd /* Fetch the ethernet address and ATU status */
114365d59686SAdrian Chadd ret0 = arswitch_readreg(sc->sc_dev, AR8327_REG_ATU_DATA0);
114465d59686SAdrian Chadd ret1 = arswitch_readreg(sc->sc_dev, AR8327_REG_ATU_DATA1);
114565d59686SAdrian Chadd ret2 = arswitch_readreg(sc->sc_dev, AR8327_REG_ATU_DATA2);
114665d59686SAdrian Chadd
114765d59686SAdrian Chadd /* If the status is zero, then we're done */
114865d59686SAdrian Chadd if (MS(ret2, AR8327_ATU_FUNC_DATA2_STATUS) == 0)
114965d59686SAdrian Chadd return (-1);
115065d59686SAdrian Chadd
115165d59686SAdrian Chadd /* MAC address */
1152c9f70b7bSAdrian Chadd e->es_macaddr[5] = MS(ret0, AR8327_ATU_DATA0_MAC_ADDR3);
1153c9f70b7bSAdrian Chadd e->es_macaddr[4] = MS(ret0, AR8327_ATU_DATA0_MAC_ADDR2);
1154c9f70b7bSAdrian Chadd e->es_macaddr[3] = MS(ret0, AR8327_ATU_DATA0_MAC_ADDR1);
1155c9f70b7bSAdrian Chadd e->es_macaddr[2] = MS(ret0, AR8327_ATU_DATA0_MAC_ADDR0);
1156c9f70b7bSAdrian Chadd e->es_macaddr[0] = MS(ret1, AR8327_ATU_DATA1_MAC_ADDR5);
1157c9f70b7bSAdrian Chadd e->es_macaddr[1] = MS(ret1, AR8327_ATU_DATA1_MAC_ADDR4);
115865d59686SAdrian Chadd
115965d59686SAdrian Chadd /* Bitmask of ports this entry is for */
116065d59686SAdrian Chadd e->es_portmask = MS(ret1, AR8327_ATU_DATA1_DEST_PORT);
116165d59686SAdrian Chadd
116265d59686SAdrian Chadd /* TODO: other flags that are interesting */
116365d59686SAdrian Chadd
116465d59686SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: MAC %6D portmask 0x%08x\n",
116565d59686SAdrian Chadd __func__,
116665d59686SAdrian Chadd e->es_macaddr, ":", e->es_portmask);
116765d59686SAdrian Chadd return (0);
116865d59686SAdrian Chadd default:
116965d59686SAdrian Chadd return (-1);
117062042c97SAdrian Chadd }
117165d59686SAdrian Chadd return (-1);
117265d59686SAdrian Chadd }
117362042c97SAdrian Chadd static int
ar8327_flush_dot1q_vlan(struct arswitch_softc * sc)1174036e1c76SAdrian Chadd ar8327_flush_dot1q_vlan(struct arswitch_softc *sc)
1175036e1c76SAdrian Chadd {
1176036e1c76SAdrian Chadd
1177036e1c76SAdrian Chadd return (ar8327_vlan_op(sc, AR8327_VTU_FUNC1_OP_FLUSH, 0, 0));
1178036e1c76SAdrian Chadd }
1179036e1c76SAdrian Chadd
1180036e1c76SAdrian Chadd static int
ar8327_purge_dot1q_vlan(struct arswitch_softc * sc,int vid)1181036e1c76SAdrian Chadd ar8327_purge_dot1q_vlan(struct arswitch_softc *sc, int vid)
1182036e1c76SAdrian Chadd {
1183036e1c76SAdrian Chadd
1184036e1c76SAdrian Chadd return (ar8327_vlan_op(sc, AR8327_VTU_FUNC1_OP_PURGE, vid, 0));
1185036e1c76SAdrian Chadd }
1186036e1c76SAdrian Chadd
1187036e1c76SAdrian Chadd static int
ar8327_get_dot1q_vlan(struct arswitch_softc * sc,uint32_t * ports,uint32_t * untagged_ports,int vid)1188036e1c76SAdrian Chadd ar8327_get_dot1q_vlan(struct arswitch_softc *sc, uint32_t *ports,
1189036e1c76SAdrian Chadd uint32_t *untagged_ports, int vid)
1190036e1c76SAdrian Chadd {
1191036e1c76SAdrian Chadd int i, r;
1192036e1c76SAdrian Chadd uint32_t op, reg, val;
1193036e1c76SAdrian Chadd
1194036e1c76SAdrian Chadd op = AR8327_VTU_FUNC1_OP_GET_ONE;
1195036e1c76SAdrian Chadd
1196036e1c76SAdrian Chadd /* Filter out the vid flags; only grab the VLAN ID */
1197036e1c76SAdrian Chadd vid &= 0xfff;
1198036e1c76SAdrian Chadd
1199036e1c76SAdrian Chadd /* XXX TODO: the VTU here stores egress mode - keep, tag, untagged, none */
1200036e1c76SAdrian Chadd r = ar8327_vlan_op(sc, op, vid, 0);
1201036e1c76SAdrian Chadd if (r != 0) {
1202036e1c76SAdrian Chadd device_printf(sc->sc_dev, "%s: %d: op failed\n", __func__, vid);
1203036e1c76SAdrian Chadd }
1204036e1c76SAdrian Chadd
1205036e1c76SAdrian Chadd reg = arswitch_readreg(sc->sc_dev, AR8327_REG_VTU_FUNC0);
12061b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_REGIO, "%s: %d: reg=0x%08x\n", __func__, vid, reg);
1207036e1c76SAdrian Chadd
1208036e1c76SAdrian Chadd /*
1209036e1c76SAdrian Chadd * If any of the bits are set, update the port mask.
1210036e1c76SAdrian Chadd * Worry about the port config itself when getport() is called.
1211036e1c76SAdrian Chadd */
1212036e1c76SAdrian Chadd *ports = 0;
1213036e1c76SAdrian Chadd for (i = 0; i < AR8327_NUM_PORTS; i++) {
1214036e1c76SAdrian Chadd val = reg >> AR8327_VTU_FUNC0_EG_MODE_S(i);
1215036e1c76SAdrian Chadd val = val & 0x3;
1216036e1c76SAdrian Chadd /* XXX KEEP (unmodified?) */
1217036e1c76SAdrian Chadd if (val == AR8327_VTU_FUNC0_EG_MODE_TAG) {
1218036e1c76SAdrian Chadd *ports |= (1 << i);
1219036e1c76SAdrian Chadd } else if (val == AR8327_VTU_FUNC0_EG_MODE_UNTAG) {
1220036e1c76SAdrian Chadd *ports |= (1 << i);
1221036e1c76SAdrian Chadd *untagged_ports |= (1 << i);
1222036e1c76SAdrian Chadd }
1223036e1c76SAdrian Chadd }
1224036e1c76SAdrian Chadd
1225036e1c76SAdrian Chadd return (0);
1226036e1c76SAdrian Chadd }
1227036e1c76SAdrian Chadd
1228036e1c76SAdrian Chadd static int
ar8327_set_dot1q_vlan(struct arswitch_softc * sc,uint32_t ports,uint32_t untagged_ports,int vid)1229036e1c76SAdrian Chadd ar8327_set_dot1q_vlan(struct arswitch_softc *sc, uint32_t ports,
1230036e1c76SAdrian Chadd uint32_t untagged_ports, int vid)
1231036e1c76SAdrian Chadd {
1232036e1c76SAdrian Chadd int i;
1233036e1c76SAdrian Chadd uint32_t op, val, mode;
1234036e1c76SAdrian Chadd
1235036e1c76SAdrian Chadd op = AR8327_VTU_FUNC1_OP_LOAD;
1236036e1c76SAdrian Chadd vid &= 0xfff;
1237036e1c76SAdrian Chadd
12381b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_VLAN,
1239036e1c76SAdrian Chadd "%s: vid: %d, ports=0x%08x, untagged_ports=0x%08x\n",
1240036e1c76SAdrian Chadd __func__,
1241036e1c76SAdrian Chadd vid,
1242036e1c76SAdrian Chadd ports,
1243036e1c76SAdrian Chadd untagged_ports);
1244036e1c76SAdrian Chadd
1245036e1c76SAdrian Chadd /*
1246036e1c76SAdrian Chadd * Mark it as valid; and that it should use per-VLAN MAC table,
1247036e1c76SAdrian Chadd * not VID=0 when doing MAC lookups
1248036e1c76SAdrian Chadd */
1249036e1c76SAdrian Chadd val = AR8327_VTU_FUNC0_VALID | AR8327_VTU_FUNC0_IVL;
1250036e1c76SAdrian Chadd
1251036e1c76SAdrian Chadd for (i = 0; i < AR8327_NUM_PORTS; i++) {
1252036e1c76SAdrian Chadd if ((ports & BIT(i)) == 0)
1253036e1c76SAdrian Chadd mode = AR8327_VTU_FUNC0_EG_MODE_NOT;
1254036e1c76SAdrian Chadd else if (untagged_ports & BIT(i))
1255036e1c76SAdrian Chadd mode = AR8327_VTU_FUNC0_EG_MODE_UNTAG;
1256036e1c76SAdrian Chadd else
1257036e1c76SAdrian Chadd mode = AR8327_VTU_FUNC0_EG_MODE_TAG;
1258036e1c76SAdrian Chadd
1259036e1c76SAdrian Chadd val |= mode << AR8327_VTU_FUNC0_EG_MODE_S(i);
1260036e1c76SAdrian Chadd }
1261036e1c76SAdrian Chadd
1262036e1c76SAdrian Chadd return (ar8327_vlan_op(sc, op, vid, val));
1263036e1c76SAdrian Chadd }
1264036e1c76SAdrian Chadd
12657330dd0bSAdrian Chadd void
ar8327_attach(struct arswitch_softc * sc)12667330dd0bSAdrian Chadd ar8327_attach(struct arswitch_softc *sc)
12677330dd0bSAdrian Chadd {
12687330dd0bSAdrian Chadd
12697330dd0bSAdrian Chadd sc->hal.arswitch_hw_setup = ar8327_hw_setup;
12707330dd0bSAdrian Chadd sc->hal.arswitch_hw_global_setup = ar8327_hw_global_setup;
12717330dd0bSAdrian Chadd
12727330dd0bSAdrian Chadd sc->hal.arswitch_port_init = ar8327_port_init;
127378549b94SAdrian Chadd
127478549b94SAdrian Chadd sc->hal.arswitch_vlan_getvgroup = ar8327_vlan_getvgroup;
127578549b94SAdrian Chadd sc->hal.arswitch_vlan_setvgroup = ar8327_vlan_setvgroup;
12767330dd0bSAdrian Chadd sc->hal.arswitch_port_vlan_setup = ar8327_port_vlan_setup;
12777330dd0bSAdrian Chadd sc->hal.arswitch_port_vlan_get = ar8327_port_vlan_get;
1278036e1c76SAdrian Chadd sc->hal.arswitch_flush_dot1q_vlan = ar8327_flush_dot1q_vlan;
1279036e1c76SAdrian Chadd sc->hal.arswitch_purge_dot1q_vlan = ar8327_purge_dot1q_vlan;
1280036e1c76SAdrian Chadd sc->hal.arswitch_set_dot1q_vlan = ar8327_set_dot1q_vlan;
1281036e1c76SAdrian Chadd sc->hal.arswitch_get_dot1q_vlan = ar8327_get_dot1q_vlan;
12827330dd0bSAdrian Chadd
12837330dd0bSAdrian Chadd sc->hal.arswitch_vlan_init_hw = ar8327_reset_vlans;
12847330dd0bSAdrian Chadd sc->hal.arswitch_vlan_get_pvid = ar8327_get_pvid;
12857330dd0bSAdrian Chadd sc->hal.arswitch_vlan_set_pvid = ar8327_set_pvid;
12867330dd0bSAdrian Chadd
1287749cac13SAdrian Chadd sc->hal.arswitch_get_port_vlan = ar8327_vlan_get_port;
1288749cac13SAdrian Chadd sc->hal.arswitch_set_port_vlan = ar8327_vlan_set_port;
1289749cac13SAdrian Chadd
129062042c97SAdrian Chadd sc->hal.arswitch_atu_learn_default = ar8327_atu_learn_default;
12914ff2f60dSAdrian Chadd sc->hal.arswitch_atu_flush = ar8327_atu_flush;
129262042c97SAdrian Chadd sc->hal.arswitch_atu_flush_port = ar8327_atu_flush_port;
129362042c97SAdrian Chadd sc->hal.arswitch_atu_fetch_table = ar8327_atu_fetch_table;
12944ff2f60dSAdrian Chadd
129578549b94SAdrian Chadd /*
129678549b94SAdrian Chadd * Reading the PHY via the MDIO interface currently doesn't
129778549b94SAdrian Chadd * work correctly.
129878549b94SAdrian Chadd *
129978549b94SAdrian Chadd * So for now, just go direct to the PHY registers themselves.
130078549b94SAdrian Chadd * This has always worked on external devices, but not internal
130178549b94SAdrian Chadd * devices (AR934x, AR724x, AR933x.)
130278549b94SAdrian Chadd */
130378549b94SAdrian Chadd sc->hal.arswitch_phy_read = arswitch_readphy_external;
130478549b94SAdrian Chadd sc->hal.arswitch_phy_write = arswitch_writephy_external;
130578549b94SAdrian Chadd
13067330dd0bSAdrian Chadd /* Set the switch vlan capabilities. */
13077330dd0bSAdrian Chadd sc->info.es_vlan_caps = ETHERSWITCH_VLAN_DOT1Q |
13087330dd0bSAdrian Chadd ETHERSWITCH_VLAN_PORT | ETHERSWITCH_VLAN_DOUBLE_TAG;
13097330dd0bSAdrian Chadd sc->info.es_nvlangroups = AR8X16_MAX_VLANS;
13107330dd0bSAdrian Chadd }
1311