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