xref: /freebsd/sys/dev/etherswitch/arswitch/arswitch_8327.c (revision 718cf2ccb9956613756ab15d7a0e28f2c8e91cab)
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