xref: /freebsd/sys/dev/etherswitch/arswitch/arswitch.c (revision 7ed0831923db7da247fd6bc2ee309eeec3f74d0a)
1a043e8c7SAdrian Chadd /*-
2718cf2ccSPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3718cf2ccSPedro F. Giffuni  *
4a043e8c7SAdrian Chadd  * Copyright (c) 2011-2012 Stefan Bethke.
5a043e8c7SAdrian Chadd  * Copyright (c) 2012 Adrian Chadd.
6a043e8c7SAdrian Chadd  * All rights reserved.
7a043e8c7SAdrian Chadd  *
8a043e8c7SAdrian Chadd  * Redistribution and use in source and binary forms, with or without
9a043e8c7SAdrian Chadd  * modification, are permitted provided that the following conditions
10a043e8c7SAdrian Chadd  * are met:
11a043e8c7SAdrian Chadd  * 1. Redistributions of source code must retain the above copyright
12a043e8c7SAdrian Chadd  *    notice, this list of conditions and the following disclaimer.
13a043e8c7SAdrian Chadd  * 2. Redistributions in binary form must reproduce the above copyright
14a043e8c7SAdrian Chadd  *    notice, this list of conditions and the following disclaimer in the
15a043e8c7SAdrian Chadd  *    documentation and/or other materials provided with the distribution.
16a043e8c7SAdrian Chadd  *
17a043e8c7SAdrian Chadd  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18a043e8c7SAdrian Chadd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19a043e8c7SAdrian Chadd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20a043e8c7SAdrian Chadd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21a043e8c7SAdrian Chadd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22a043e8c7SAdrian Chadd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23a043e8c7SAdrian Chadd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24a043e8c7SAdrian Chadd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25a043e8c7SAdrian Chadd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26a043e8c7SAdrian Chadd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27a043e8c7SAdrian Chadd  * SUCH DAMAGE.
28a043e8c7SAdrian Chadd  *
29a043e8c7SAdrian Chadd  * $FreeBSD$
30a043e8c7SAdrian Chadd  */
31a043e8c7SAdrian Chadd 
32a043e8c7SAdrian Chadd #include <sys/param.h>
33a043e8c7SAdrian Chadd #include <sys/bus.h>
34a043e8c7SAdrian Chadd #include <sys/errno.h>
35a043e8c7SAdrian Chadd #include <sys/kernel.h>
36104dc214SGleb Smirnoff #include <sys/malloc.h>
37a043e8c7SAdrian Chadd #include <sys/module.h>
38a043e8c7SAdrian Chadd #include <sys/socket.h>
39a043e8c7SAdrian Chadd #include <sys/sockio.h>
40a043e8c7SAdrian Chadd #include <sys/sysctl.h>
41a043e8c7SAdrian Chadd #include <sys/systm.h>
42a043e8c7SAdrian Chadd 
43a043e8c7SAdrian Chadd #include <net/if.h>
44104dc214SGleb Smirnoff #include <net/if_var.h>
45a043e8c7SAdrian Chadd #include <net/if_arp.h>
46a043e8c7SAdrian Chadd #include <net/ethernet.h>
47a043e8c7SAdrian Chadd #include <net/if_dl.h>
48a043e8c7SAdrian Chadd #include <net/if_media.h>
49a043e8c7SAdrian Chadd #include <net/if_types.h>
50a043e8c7SAdrian Chadd 
51a043e8c7SAdrian Chadd #include <machine/bus.h>
52efce3748SRui Paulo #include <dev/iicbus/iic.h>
53a043e8c7SAdrian Chadd #include <dev/iicbus/iiconf.h>
54a043e8c7SAdrian Chadd #include <dev/iicbus/iicbus.h>
55a043e8c7SAdrian Chadd #include <dev/mii/mii.h>
56a043e8c7SAdrian Chadd #include <dev/mii/miivar.h>
5771e8eac4SAdrian Chadd #include <dev/mdio/mdio.h>
58a043e8c7SAdrian Chadd 
59a043e8c7SAdrian Chadd #include <dev/etherswitch/etherswitch.h>
60a043e8c7SAdrian Chadd 
61a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitchreg.h>
62a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitchvar.h>
63a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_reg.h>
64a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_phy.h>
65b9f07b86SLuiz Otavio O Souza #include <dev/etherswitch/arswitch/arswitch_vlans.h>
66a043e8c7SAdrian Chadd 
6727a2ecaaSAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_7240.h>
68a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_8216.h>
69a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_8226.h>
70a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_8316.h>
71482d268dSAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_8327.h>
72b2152161SAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_9340.h>
73a043e8c7SAdrian Chadd 
74a043e8c7SAdrian Chadd #include "mdio_if.h"
75a043e8c7SAdrian Chadd #include "miibus_if.h"
76a043e8c7SAdrian Chadd #include "etherswitch_if.h"
77a043e8c7SAdrian Chadd 
78c94dc808SAdrian Chadd /* Map ETHERSWITCH_PORT_LED_* to Atheros pattern codes */
79c94dc808SAdrian Chadd static int led_pattern_table[] = {
80c94dc808SAdrian Chadd 	[ETHERSWITCH_PORT_LED_DEFAULT] = 0x3,
81c94dc808SAdrian Chadd 	[ETHERSWITCH_PORT_LED_ON] = 0x2,
82c94dc808SAdrian Chadd 	[ETHERSWITCH_PORT_LED_OFF] = 0x0,
83c94dc808SAdrian Chadd 	[ETHERSWITCH_PORT_LED_BLINK] = 0x1
84c94dc808SAdrian Chadd };
85c94dc808SAdrian Chadd 
86a043e8c7SAdrian Chadd static inline int arswitch_portforphy(int phy);
87a043e8c7SAdrian Chadd static void arswitch_tick(void *arg);
88a043e8c7SAdrian Chadd static int arswitch_ifmedia_upd(struct ifnet *);
89a043e8c7SAdrian Chadd static void arswitch_ifmedia_sts(struct ifnet *, struct ifmediareq *);
90a9ad4222SAdrian Chadd static int ar8xxx_port_vlan_setup(struct arswitch_softc *sc,
91a9ad4222SAdrian Chadd     etherswitch_port_t *p);
92a9ad4222SAdrian Chadd static int ar8xxx_port_vlan_get(struct arswitch_softc *sc,
93a9ad4222SAdrian Chadd     etherswitch_port_t *p);
94c94dc808SAdrian Chadd static int arswitch_setled(struct arswitch_softc *sc, int phy, int led,
95c94dc808SAdrian Chadd     int style);
96a043e8c7SAdrian Chadd 
97a043e8c7SAdrian Chadd static int
98a043e8c7SAdrian Chadd arswitch_probe(device_t dev)
99a043e8c7SAdrian Chadd {
100a043e8c7SAdrian Chadd 	struct arswitch_softc *sc;
101a043e8c7SAdrian Chadd 	uint32_t id;
102a043e8c7SAdrian Chadd 	char *chipname, desc[256];
103a043e8c7SAdrian Chadd 
104a043e8c7SAdrian Chadd 	sc = device_get_softc(dev);
105a043e8c7SAdrian Chadd 	bzero(sc, sizeof(*sc));
106a043e8c7SAdrian Chadd 	sc->page = -1;
10727a2ecaaSAdrian Chadd 
10827a2ecaaSAdrian Chadd 	/* AR7240 probe */
10927a2ecaaSAdrian Chadd 	if (ar7240_probe(dev) == 0) {
11027a2ecaaSAdrian Chadd 		chipname = "AR7240";
11127a2ecaaSAdrian Chadd 		sc->sc_switchtype = AR8X16_SWITCH_AR7240;
112b2152161SAdrian Chadd 		sc->is_internal_switch = 1;
113b2152161SAdrian Chadd 		id = 0;
114b2152161SAdrian Chadd 		goto done;
115b2152161SAdrian Chadd 	}
116b2152161SAdrian Chadd 
117b2152161SAdrian Chadd 	/* AR9340 probe */
118b2152161SAdrian Chadd 	if (ar9340_probe(dev) == 0) {
119b2152161SAdrian Chadd 		chipname = "AR9340";
120b2152161SAdrian Chadd 		sc->sc_switchtype = AR8X16_SWITCH_AR9340;
121b2152161SAdrian Chadd 		sc->is_internal_switch = 1;
122daa4deacSAleksandr Rybalko 		id = 0;
12327a2ecaaSAdrian Chadd 		goto done;
12427a2ecaaSAdrian Chadd 	}
12527a2ecaaSAdrian Chadd 
12627a2ecaaSAdrian Chadd 	/* AR8xxx probe */
127a043e8c7SAdrian Chadd 	id = arswitch_readreg(dev, AR8X16_REG_MASK_CTRL);
128dd843f87SAdrian Chadd 	sc->chip_rev = (id & AR8X16_MASK_CTRL_REV_MASK);
129dd843f87SAdrian Chadd 	sc->chip_ver = (id & AR8X16_MASK_CTRL_VER_MASK) > AR8X16_MASK_CTRL_VER_SHIFT;
130b2152161SAdrian Chadd 	switch (id & (AR8X16_MASK_CTRL_VER_MASK | AR8X16_MASK_CTRL_REV_MASK)) {
131b2152161SAdrian Chadd 	case 0x0101:
132a043e8c7SAdrian Chadd 		chipname = "AR8216";
133a043e8c7SAdrian Chadd 		sc->sc_switchtype = AR8X16_SWITCH_AR8216;
134a043e8c7SAdrian Chadd 		break;
135b2152161SAdrian Chadd 	case 0x0201:
136a043e8c7SAdrian Chadd 		chipname = "AR8226";
137a043e8c7SAdrian Chadd 		sc->sc_switchtype = AR8X16_SWITCH_AR8226;
138a043e8c7SAdrian Chadd 		break;
139b2152161SAdrian Chadd 	/* 0x0301 - AR8236 */
140b2152161SAdrian Chadd 	case 0x1000:
141b2152161SAdrian Chadd 	case 0x1001:
142a043e8c7SAdrian Chadd 		chipname = "AR8316";
143a043e8c7SAdrian Chadd 		sc->sc_switchtype = AR8X16_SWITCH_AR8316;
144a043e8c7SAdrian Chadd 		break;
1450e67bf94SAdrian Chadd 	case 0x1202:
1469682e347SAdrian Chadd 	case 0x1204:
1470e67bf94SAdrian Chadd 		chipname = "AR8327";
1480e67bf94SAdrian Chadd 		sc->sc_switchtype = AR8X16_SWITCH_AR8327;
1490e67bf94SAdrian Chadd 		sc->mii_lo_first = 1;
1500e67bf94SAdrian Chadd 		break;
151a043e8c7SAdrian Chadd 	default:
152a043e8c7SAdrian Chadd 		chipname = NULL;
153a043e8c7SAdrian Chadd 	}
15427a2ecaaSAdrian Chadd 
15527a2ecaaSAdrian Chadd done:
156b2152161SAdrian Chadd 
1571b334c8bSAdrian Chadd 	DPRINTF(sc, ARSWITCH_DBG_ANY, "chipname=%s, id=%08x\n", chipname, id);
158a043e8c7SAdrian Chadd 	if (chipname != NULL) {
159a043e8c7SAdrian Chadd 		snprintf(desc, sizeof(desc),
16078549b94SAdrian Chadd 		    "Atheros %s Ethernet Switch (ver %d rev %d)",
16178549b94SAdrian Chadd 		    chipname,
16278549b94SAdrian Chadd 		    sc->chip_ver,
16378549b94SAdrian Chadd 		    sc->chip_rev);
164a043e8c7SAdrian Chadd 		device_set_desc_copy(dev, desc);
165a043e8c7SAdrian Chadd 		return (BUS_PROBE_DEFAULT);
166a043e8c7SAdrian Chadd 	}
167a043e8c7SAdrian Chadd 	return (ENXIO);
168a043e8c7SAdrian Chadd }
169a043e8c7SAdrian Chadd 
170a043e8c7SAdrian Chadd static int
171a043e8c7SAdrian Chadd arswitch_attach_phys(struct arswitch_softc *sc)
172a043e8c7SAdrian Chadd {
173a043e8c7SAdrian Chadd 	int phy, err = 0;
174a043e8c7SAdrian Chadd 	char name[IFNAMSIZ];
175a043e8c7SAdrian Chadd 
176a043e8c7SAdrian Chadd 	/* PHYs need an interface, so we generate a dummy one */
177a043e8c7SAdrian Chadd 	snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->sc_dev));
178a043e8c7SAdrian Chadd 	for (phy = 0; phy < sc->numphys; phy++) {
179a043e8c7SAdrian Chadd 		sc->ifp[phy] = if_alloc(IFT_ETHER);
1800774131eSMichael Zhilin 		if (sc->ifp[phy] == NULL) {
1810774131eSMichael Zhilin 			device_printf(sc->sc_dev, "couldn't allocate ifnet structure\n");
1820774131eSMichael Zhilin 			err = ENOMEM;
1830774131eSMichael Zhilin 			break;
1840774131eSMichael Zhilin 		}
1850774131eSMichael Zhilin 
186a043e8c7SAdrian Chadd 		sc->ifp[phy]->if_softc = sc;
187a043e8c7SAdrian Chadd 		sc->ifp[phy]->if_flags |= IFF_UP | IFF_BROADCAST |
188a043e8c7SAdrian Chadd 		    IFF_DRV_RUNNING | IFF_SIMPLEX;
189a043e8c7SAdrian Chadd 		sc->ifname[phy] = malloc(strlen(name)+1, M_DEVBUF, M_WAITOK);
190a043e8c7SAdrian Chadd 		bcopy(name, sc->ifname[phy], strlen(name)+1);
191a043e8c7SAdrian Chadd 		if_initname(sc->ifp[phy], sc->ifname[phy],
192a043e8c7SAdrian Chadd 		    arswitch_portforphy(phy));
193a043e8c7SAdrian Chadd 		err = mii_attach(sc->sc_dev, &sc->miibus[phy], sc->ifp[phy],
194a043e8c7SAdrian Chadd 		    arswitch_ifmedia_upd, arswitch_ifmedia_sts, \
195a043e8c7SAdrian Chadd 		    BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0);
19678549b94SAdrian Chadd #if 0
197a043e8c7SAdrian Chadd 		DPRINTF(sc->sc_dev, "%s attached to pseudo interface %s\n",
198a043e8c7SAdrian Chadd 		    device_get_nameunit(sc->miibus[phy]),
199a043e8c7SAdrian Chadd 		    sc->ifp[phy]->if_xname);
20078549b94SAdrian Chadd #endif
201a043e8c7SAdrian Chadd 		if (err != 0) {
202a043e8c7SAdrian Chadd 			device_printf(sc->sc_dev,
203a043e8c7SAdrian Chadd 			    "attaching PHY %d failed\n",
204a043e8c7SAdrian Chadd 			    phy);
205a043e8c7SAdrian Chadd 			return (err);
206a043e8c7SAdrian Chadd 		}
207a043e8c7SAdrian Chadd 
208c94dc808SAdrian Chadd 		if (AR8X16_IS_SWITCH(sc, AR8327)) {
209c94dc808SAdrian Chadd 			int led;
210c94dc808SAdrian Chadd 			char ledname[IFNAMSIZ+4];
211c94dc808SAdrian Chadd 
212c94dc808SAdrian Chadd 			for (led = 0; led < 3; led++) {
213c94dc808SAdrian Chadd 				sprintf(ledname, "%s%dled%d", name,
214c94dc808SAdrian Chadd 				    arswitch_portforphy(phy), led+1);
215c94dc808SAdrian Chadd 				sc->dev_led[phy][led].sc = sc;
216c94dc808SAdrian Chadd 				sc->dev_led[phy][led].phy = phy;
217c94dc808SAdrian Chadd 				sc->dev_led[phy][led].lednum = led;
218c94dc808SAdrian Chadd 			}
219c94dc808SAdrian Chadd 		}
220c94dc808SAdrian Chadd 	}
221c94dc808SAdrian Chadd 	return (0);
222c94dc808SAdrian Chadd }
223c94dc808SAdrian Chadd 
224a043e8c7SAdrian Chadd static int
225b9f07b86SLuiz Otavio O Souza arswitch_reset(device_t dev)
226b9f07b86SLuiz Otavio O Souza {
227b9f07b86SLuiz Otavio O Souza 
228b9f07b86SLuiz Otavio O Souza 	arswitch_writereg(dev, AR8X16_REG_MASK_CTRL,
229b9f07b86SLuiz Otavio O Souza 	    AR8X16_MASK_CTRL_SOFT_RESET);
230b9f07b86SLuiz Otavio O Souza 	DELAY(1000);
231b9f07b86SLuiz Otavio O Souza 	if (arswitch_readreg(dev, AR8X16_REG_MASK_CTRL) &
232b9f07b86SLuiz Otavio O Souza 	    AR8X16_MASK_CTRL_SOFT_RESET) {
233b9f07b86SLuiz Otavio O Souza 		device_printf(dev, "unable to reset switch\n");
234b9f07b86SLuiz Otavio O Souza 		return (-1);
235b9f07b86SLuiz Otavio O Souza 	}
236b9f07b86SLuiz Otavio O Souza 	return (0);
237b9f07b86SLuiz Otavio O Souza }
238b9f07b86SLuiz Otavio O Souza 
239b9f07b86SLuiz Otavio O Souza static int
240b9f07b86SLuiz Otavio O Souza arswitch_set_vlan_mode(struct arswitch_softc *sc, uint32_t mode)
241b9f07b86SLuiz Otavio O Souza {
242b9f07b86SLuiz Otavio O Souza 
243b9f07b86SLuiz Otavio O Souza 	/* Check for invalid modes. */
244b9f07b86SLuiz Otavio O Souza 	if ((mode & sc->info.es_vlan_caps) != mode)
245b9f07b86SLuiz Otavio O Souza 		return (EINVAL);
246b9f07b86SLuiz Otavio O Souza 
247b9f07b86SLuiz Otavio O Souza 	switch (mode) {
248b9f07b86SLuiz Otavio O Souza 	case ETHERSWITCH_VLAN_DOT1Q:
249b9f07b86SLuiz Otavio O Souza 		sc->vlan_mode = ETHERSWITCH_VLAN_DOT1Q;
250b9f07b86SLuiz Otavio O Souza 		break;
251b9f07b86SLuiz Otavio O Souza 	case ETHERSWITCH_VLAN_PORT:
252b9f07b86SLuiz Otavio O Souza 		sc->vlan_mode = ETHERSWITCH_VLAN_PORT;
253b9f07b86SLuiz Otavio O Souza 		break;
254b9f07b86SLuiz Otavio O Souza 	default:
255b9f07b86SLuiz Otavio O Souza 		sc->vlan_mode = 0;
25674b8d63dSPedro F. Giffuni 	}
257b9f07b86SLuiz Otavio O Souza 
258b9f07b86SLuiz Otavio O Souza 	/* Reset VLANs. */
2596dcbabd7SAdrian Chadd 	sc->hal.arswitch_vlan_init_hw(sc);
260b9f07b86SLuiz Otavio O Souza 
261b9f07b86SLuiz Otavio O Souza 	return (0);
262b9f07b86SLuiz Otavio O Souza }
263b9f07b86SLuiz Otavio O Souza 
264b9f07b86SLuiz Otavio O Souza static void
265a9ad4222SAdrian Chadd ar8xxx_port_init(struct arswitch_softc *sc, int port)
266b9f07b86SLuiz Otavio O Souza {
267b9f07b86SLuiz Otavio O Souza 
268b9f07b86SLuiz Otavio O Souza 	/* Port0 - CPU */
269df892897SAdrian Chadd 	if (port == AR8X16_PORT_CPU) {
270b9f07b86SLuiz Otavio O Souza 		arswitch_writereg(sc->sc_dev, AR8X16_REG_PORT_STS(0),
271b9f07b86SLuiz Otavio O Souza 		    (AR8X16_IS_SWITCH(sc, AR8216) ?
272b9f07b86SLuiz Otavio O Souza 		    AR8X16_PORT_STS_SPEED_100 : AR8X16_PORT_STS_SPEED_1000) |
273b9f07b86SLuiz Otavio O Souza 		    (AR8X16_IS_SWITCH(sc, AR8216) ? 0 : AR8X16_PORT_STS_RXFLOW) |
274b9f07b86SLuiz Otavio O Souza 		    (AR8X16_IS_SWITCH(sc, AR8216) ? 0 : AR8X16_PORT_STS_TXFLOW) |
275b9f07b86SLuiz Otavio O Souza 		    AR8X16_PORT_STS_RXMAC |
276b9f07b86SLuiz Otavio O Souza 		    AR8X16_PORT_STS_TXMAC |
277b9f07b86SLuiz Otavio O Souza 		    AR8X16_PORT_STS_DUPLEX);
278b9f07b86SLuiz Otavio O Souza 		arswitch_writereg(sc->sc_dev, AR8X16_REG_PORT_CTRL(0),
279b9f07b86SLuiz Otavio O Souza 		    arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_CTRL(0)) &
280b9f07b86SLuiz Otavio O Souza 		    ~AR8X16_PORT_CTRL_HEADER);
281df892897SAdrian Chadd 	} else {
282b9f07b86SLuiz Otavio O Souza 		/* Set ports to auto negotiation. */
283b9f07b86SLuiz Otavio O Souza 		arswitch_writereg(sc->sc_dev, AR8X16_REG_PORT_STS(port),
284b9f07b86SLuiz Otavio O Souza 		    AR8X16_PORT_STS_LINK_AUTO);
285b9f07b86SLuiz Otavio O Souza 		arswitch_writereg(sc->sc_dev, AR8X16_REG_PORT_CTRL(port),
286b9f07b86SLuiz Otavio O Souza 		    arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_CTRL(port)) &
287b9f07b86SLuiz Otavio O Souza 		    ~AR8X16_PORT_CTRL_HEADER);
288b9f07b86SLuiz Otavio O Souza 	}
289b9f07b86SLuiz Otavio O Souza }
290b9f07b86SLuiz Otavio O Souza 
291b9f07b86SLuiz Otavio O Souza static int
29262042c97SAdrian Chadd ar8xxx_atu_wait_ready(struct arswitch_softc *sc)
2934ff2f60dSAdrian Chadd {
2944ff2f60dSAdrian Chadd 	int ret;
2954ff2f60dSAdrian Chadd 
29662042c97SAdrian Chadd 	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
29762042c97SAdrian Chadd 
2984ff2f60dSAdrian Chadd 	ret = arswitch_waitreg(sc->sc_dev,
2994ff2f60dSAdrian Chadd 	    AR8216_REG_ATU,
3004ff2f60dSAdrian Chadd 	    AR8216_ATU_ACTIVE,
3014ff2f60dSAdrian Chadd 	    0,
3024ff2f60dSAdrian Chadd 	    1000);
3034ff2f60dSAdrian Chadd 
30462042c97SAdrian Chadd 	return (ret);
30562042c97SAdrian Chadd }
30662042c97SAdrian Chadd 
30762042c97SAdrian Chadd /*
30862042c97SAdrian Chadd  * Flush all ATU entries.
30962042c97SAdrian Chadd  */
31062042c97SAdrian Chadd static int
31162042c97SAdrian Chadd ar8xxx_atu_flush(struct arswitch_softc *sc)
31262042c97SAdrian Chadd {
31362042c97SAdrian Chadd 	int ret;
31462042c97SAdrian Chadd 
31562042c97SAdrian Chadd 	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
31662042c97SAdrian Chadd 
31762042c97SAdrian Chadd 	DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: flushing all ports\n", __func__);
31862042c97SAdrian Chadd 
31962042c97SAdrian Chadd 	ret = ar8xxx_atu_wait_ready(sc);
3204ff2f60dSAdrian Chadd 	if (ret)
3214ff2f60dSAdrian Chadd 		device_printf(sc->sc_dev, "%s: waitreg failed\n", __func__);
3224ff2f60dSAdrian Chadd 
3234ff2f60dSAdrian Chadd 	if (!ret)
3244ff2f60dSAdrian Chadd 		arswitch_writereg(sc->sc_dev,
3254ff2f60dSAdrian Chadd 		    AR8216_REG_ATU,
3262c6ceccaSAdrian Chadd 		    AR8216_ATU_OP_FLUSH | AR8216_ATU_ACTIVE);
3274ff2f60dSAdrian Chadd 
3284ff2f60dSAdrian Chadd 	return (ret);
3294ff2f60dSAdrian Chadd }
3304ff2f60dSAdrian Chadd 
33162042c97SAdrian Chadd /*
33262042c97SAdrian Chadd  * Flush ATU entries for a single port.
33362042c97SAdrian Chadd  */
33462042c97SAdrian Chadd static int
33562042c97SAdrian Chadd ar8xxx_atu_flush_port(struct arswitch_softc *sc, int port)
33662042c97SAdrian Chadd {
33762042c97SAdrian Chadd 	int ret, val;
33862042c97SAdrian Chadd 
33962042c97SAdrian Chadd 	DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: flushing port %d\n", __func__,
34062042c97SAdrian Chadd 	    port);
34162042c97SAdrian Chadd 
34262042c97SAdrian Chadd 	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
34362042c97SAdrian Chadd 
34462042c97SAdrian Chadd 	/* Flush unicast entries on port */
34562042c97SAdrian Chadd 	val = AR8216_ATU_OP_FLUSH_UNICAST;
34662042c97SAdrian Chadd 
34762042c97SAdrian Chadd 	/* TODO: bit 4 indicates whether to flush dynamic (0) or static (1) */
34862042c97SAdrian Chadd 
34962042c97SAdrian Chadd 	/* Which port */
35062042c97SAdrian Chadd 	val |= SM(port, AR8216_ATU_PORT_NUM);
35162042c97SAdrian Chadd 
35262042c97SAdrian Chadd 	ret = ar8xxx_atu_wait_ready(sc);
35362042c97SAdrian Chadd 	if (ret)
35462042c97SAdrian Chadd 		device_printf(sc->sc_dev, "%s: waitreg failed\n", __func__);
35562042c97SAdrian Chadd 
35662042c97SAdrian Chadd 	if (!ret)
35762042c97SAdrian Chadd 		arswitch_writereg(sc->sc_dev,
35862042c97SAdrian Chadd 		    AR8216_REG_ATU,
35962042c97SAdrian Chadd 		    val | AR8216_ATU_ACTIVE);
36062042c97SAdrian Chadd 
36162042c97SAdrian Chadd 	return (ret);
36262042c97SAdrian Chadd }
36362042c97SAdrian Chadd 
36462042c97SAdrian Chadd /*
36562042c97SAdrian Chadd  * XXX TODO: flush a single MAC address.
36662042c97SAdrian Chadd  */
36762042c97SAdrian Chadd 
36862042c97SAdrian Chadd /*
36962042c97SAdrian Chadd  * Fetch a single entry from the ATU.
37062042c97SAdrian Chadd  */
37162042c97SAdrian Chadd static int
37262042c97SAdrian Chadd ar8xxx_atu_fetch_table(struct arswitch_softc *sc, etherswitch_atu_entry_t *e,
37362042c97SAdrian Chadd     int atu_fetch_op)
37462042c97SAdrian Chadd {
37562042c97SAdrian Chadd 	uint32_t ret0, ret1, ret2, val;
37662042c97SAdrian Chadd 
37762042c97SAdrian Chadd 	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
37862042c97SAdrian Chadd 
37962042c97SAdrian Chadd 	switch (atu_fetch_op) {
38062042c97SAdrian Chadd 	case 0:
38162042c97SAdrian Chadd 		/* Initialise things for the first fetch */
38262042c97SAdrian Chadd 
38362042c97SAdrian Chadd 		DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: initializing\n", __func__);
38462042c97SAdrian Chadd 		(void) ar8xxx_atu_wait_ready(sc);
38562042c97SAdrian Chadd 
38662042c97SAdrian Chadd 		arswitch_writereg(sc->sc_dev,
38762042c97SAdrian Chadd 		    AR8216_REG_ATU, AR8216_ATU_OP_GET_NEXT);
38862042c97SAdrian Chadd 		arswitch_writereg(sc->sc_dev,
38962042c97SAdrian Chadd 		    AR8216_REG_ATU_DATA, 0);
39062042c97SAdrian Chadd 		arswitch_writereg(sc->sc_dev,
39162042c97SAdrian Chadd 		    AR8216_REG_ATU_CTRL2, 0);
39262042c97SAdrian Chadd 
39362042c97SAdrian Chadd 		return (0);
39462042c97SAdrian Chadd 	case 1:
39562042c97SAdrian Chadd 		DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: reading next\n", __func__);
39662042c97SAdrian Chadd 		/*
39762042c97SAdrian Chadd 		 * Attempt to read the next address entry; don't modify what
39862042c97SAdrian Chadd 		 * is there in AT_ADDR{4,5} as its used for the next fetch
39962042c97SAdrian Chadd 		 */
40062042c97SAdrian Chadd 		(void) ar8xxx_atu_wait_ready(sc);
40162042c97SAdrian Chadd 
40262042c97SAdrian Chadd 		/* Begin the next read event; not modifying anything */
40362042c97SAdrian Chadd 		val = arswitch_readreg(sc->sc_dev, AR8216_REG_ATU);
40462042c97SAdrian Chadd 		val |= AR8216_ATU_ACTIVE;
40562042c97SAdrian Chadd 		arswitch_writereg(sc->sc_dev, AR8216_REG_ATU, val);
40662042c97SAdrian Chadd 
40762042c97SAdrian Chadd 		/* Wait for it to complete */
40862042c97SAdrian Chadd 		(void) ar8xxx_atu_wait_ready(sc);
40962042c97SAdrian Chadd 
41062042c97SAdrian Chadd 		/* Fetch the ethernet address and ATU status */
41162042c97SAdrian Chadd 		ret0 = arswitch_readreg(sc->sc_dev, AR8216_REG_ATU);
41262042c97SAdrian Chadd 		ret1 = arswitch_readreg(sc->sc_dev, AR8216_REG_ATU_DATA);
41362042c97SAdrian Chadd 		ret2 = arswitch_readreg(sc->sc_dev, AR8216_REG_ATU_CTRL2);
41462042c97SAdrian Chadd 
41562042c97SAdrian Chadd 		/* If the status is zero, then we're done */
41662042c97SAdrian Chadd 		if (MS(ret2, AR8216_ATU_CTRL2_AT_STATUS) == 0)
41762042c97SAdrian Chadd 			return (-1);
41862042c97SAdrian Chadd 
41962042c97SAdrian Chadd 		/* MAC address */
42062042c97SAdrian Chadd 		e->es_macaddr[5] = MS(ret0, AR8216_ATU_ADDR5);
42162042c97SAdrian Chadd 		e->es_macaddr[4] = MS(ret0, AR8216_ATU_ADDR4);
42262042c97SAdrian Chadd 		e->es_macaddr[3] = MS(ret1, AR8216_ATU_ADDR3);
42362042c97SAdrian Chadd 		e->es_macaddr[2] = MS(ret1, AR8216_ATU_ADDR2);
42462042c97SAdrian Chadd 		e->es_macaddr[1] = MS(ret1, AR8216_ATU_ADDR1);
42562042c97SAdrian Chadd 		e->es_macaddr[0] = MS(ret1, AR8216_ATU_ADDR0);
42662042c97SAdrian Chadd 
42762042c97SAdrian Chadd 		/* Bitmask of ports this entry is for */
42862042c97SAdrian Chadd 		e->es_portmask = MS(ret2, AR8216_ATU_CTRL2_DESPORT);
42962042c97SAdrian Chadd 
43062042c97SAdrian Chadd 		/* TODO: other flags that are interesting */
43162042c97SAdrian Chadd 
43262042c97SAdrian Chadd 		DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: MAC %6D portmask 0x%08x\n",
43362042c97SAdrian Chadd 		    __func__,
43462042c97SAdrian Chadd 		    e->es_macaddr, ":", e->es_portmask);
43562042c97SAdrian Chadd 		return (0);
43662042c97SAdrian Chadd 	default:
43762042c97SAdrian Chadd 		return (-1);
43862042c97SAdrian Chadd 	}
43962042c97SAdrian Chadd 	return (-1);
44062042c97SAdrian Chadd }
44162042c97SAdrian Chadd 
44262042c97SAdrian Chadd /*
44362042c97SAdrian Chadd  * Configure aging register defaults.
44462042c97SAdrian Chadd  */
44562042c97SAdrian Chadd static int
44662042c97SAdrian Chadd ar8xxx_atu_learn_default(struct arswitch_softc *sc)
44762042c97SAdrian Chadd {
44862042c97SAdrian Chadd 	int ret;
44962042c97SAdrian Chadd 	uint32_t val;
45062042c97SAdrian Chadd 
45162042c97SAdrian Chadd 	DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: resetting learning\n", __func__);
45262042c97SAdrian Chadd 
45362042c97SAdrian Chadd 	/*
45462042c97SAdrian Chadd 	 * For now, configure the aging defaults:
45562042c97SAdrian Chadd 	 *
45662042c97SAdrian Chadd 	 * + ARP_EN - enable "acknowledgement" of ARP frames - they are
45762042c97SAdrian Chadd 	 *   forwarded to the CPU port
45862042c97SAdrian Chadd 	 * + LEARN_CHANGE_EN - hash table violations when learning MAC addresses
45962042c97SAdrian Chadd 	 *   will force an entry to be expired/updated and a new one to be
46062042c97SAdrian Chadd 	 *   programmed in.
46162042c97SAdrian Chadd 	 * + AGE_EN - enable address table aging
46262042c97SAdrian Chadd 	 * + AGE_TIME - set to 5 minutes
46362042c97SAdrian Chadd 	 */
46462042c97SAdrian Chadd 	val = 0;
46562042c97SAdrian Chadd 	val |= AR8216_ATU_CTRL_ARP_EN;
46662042c97SAdrian Chadd 	val |= AR8216_ATU_CTRL_LEARN_CHANGE;
46762042c97SAdrian Chadd 	val |= AR8216_ATU_CTRL_AGE_EN;
46862042c97SAdrian Chadd 	val |= 0x2b;	/* 5 minutes; bits 15:0 */
46962042c97SAdrian Chadd 
47062042c97SAdrian Chadd 	ret = arswitch_writereg(sc->sc_dev,
47162042c97SAdrian Chadd 	    AR8216_REG_ATU_CTRL,
47262042c97SAdrian Chadd 	    val);
47362042c97SAdrian Chadd 
47462042c97SAdrian Chadd 	if (ret)
47562042c97SAdrian Chadd 		device_printf(sc->sc_dev, "%s: writereg failed\n", __func__);
47662042c97SAdrian Chadd 
47762042c97SAdrian Chadd 	return (ret);
47862042c97SAdrian Chadd }
47962042c97SAdrian Chadd 
48062042c97SAdrian Chadd /*
48162042c97SAdrian Chadd  * XXX TODO: add another routine to configure the leaky behaviour
48262042c97SAdrian Chadd  * when unknown frames are received.  These must be consistent
48362042c97SAdrian Chadd  * between ethernet switches.
48462042c97SAdrian Chadd  */
48562042c97SAdrian Chadd 
48662042c97SAdrian Chadd /*
48762042c97SAdrian Chadd  * XXX TODO: this attach routine does NOT free all memory, resources
48862042c97SAdrian Chadd  * upon failure!
48962042c97SAdrian Chadd  */
4904ff2f60dSAdrian Chadd static int
491a043e8c7SAdrian Chadd arswitch_attach(device_t dev)
492a043e8c7SAdrian Chadd {
4931b334c8bSAdrian Chadd 	struct arswitch_softc *sc = device_get_softc(dev);
4941b334c8bSAdrian Chadd 	struct sysctl_ctx_list *ctx;
4951b334c8bSAdrian Chadd 	struct sysctl_oid *tree;
496a043e8c7SAdrian Chadd 	int err = 0;
497df892897SAdrian Chadd 	int port;
498a043e8c7SAdrian Chadd 
499a043e8c7SAdrian Chadd 	/* sc->sc_switchtype is already decided in arswitch_probe() */
500a043e8c7SAdrian Chadd 	sc->sc_dev = dev;
501a043e8c7SAdrian Chadd 	mtx_init(&sc->sc_mtx, "arswitch", NULL, MTX_DEF);
502a043e8c7SAdrian Chadd 	sc->page = -1;
503a043e8c7SAdrian Chadd 	strlcpy(sc->info.es_name, device_get_desc(dev),
504a043e8c7SAdrian Chadd 	    sizeof(sc->info.es_name));
505a043e8c7SAdrian Chadd 
5061b334c8bSAdrian Chadd 	/* Debugging */
5071b334c8bSAdrian Chadd 	ctx = device_get_sysctl_ctx(sc->sc_dev);
5081b334c8bSAdrian Chadd 	tree = device_get_sysctl_tree(sc->sc_dev);
5091b334c8bSAdrian Chadd 	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
5101b334c8bSAdrian Chadd 	    "debug", CTLFLAG_RW, &sc->sc_debug, 0,
5111b334c8bSAdrian Chadd 	    "control debugging printfs");
5121b334c8bSAdrian Chadd 
51362042c97SAdrian Chadd 	/* Allocate a 128 entry ATU table; hopefully its big enough! */
51462042c97SAdrian Chadd 	/* XXX TODO: make this per chip */
51562042c97SAdrian Chadd 	sc->atu.entries = malloc(sizeof(etherswitch_atu_entry_t) * 128,
51662042c97SAdrian Chadd 	    M_DEVBUF, M_NOWAIT);
51762042c97SAdrian Chadd 	if (sc->atu.entries == NULL) {
51862042c97SAdrian Chadd 		device_printf(sc->sc_dev, "%s: failed to allocate ATU table\n",
51962042c97SAdrian Chadd 		    __func__);
52062042c97SAdrian Chadd 		return (ENXIO);
52162042c97SAdrian Chadd 	}
52262042c97SAdrian Chadd 	sc->atu.count = 0;
52362042c97SAdrian Chadd 	sc->atu.size = 128;
52462042c97SAdrian Chadd 
525ddbc4420SAdrian Chadd 	/* Default HAL methods */
526a9ad4222SAdrian Chadd 	sc->hal.arswitch_port_init = ar8xxx_port_init;
527a9ad4222SAdrian Chadd 	sc->hal.arswitch_port_vlan_setup = ar8xxx_port_vlan_setup;
528a9ad4222SAdrian Chadd 	sc->hal.arswitch_port_vlan_get = ar8xxx_port_vlan_get;
5296dcbabd7SAdrian Chadd 	sc->hal.arswitch_vlan_init_hw = ar8xxx_reset_vlans;
530749cac13SAdrian Chadd 
5316dcbabd7SAdrian Chadd 	sc->hal.arswitch_vlan_getvgroup = ar8xxx_getvgroup;
5326dcbabd7SAdrian Chadd 	sc->hal.arswitch_vlan_setvgroup = ar8xxx_setvgroup;
533749cac13SAdrian Chadd 
5346dcbabd7SAdrian Chadd 	sc->hal.arswitch_vlan_get_pvid = ar8xxx_get_pvid;
5356dcbabd7SAdrian Chadd 	sc->hal.arswitch_vlan_set_pvid = ar8xxx_set_pvid;
536749cac13SAdrian Chadd 
537749cac13SAdrian Chadd 	sc->hal.arswitch_get_dot1q_vlan = ar8xxx_get_dot1q_vlan;
538749cac13SAdrian Chadd 	sc->hal.arswitch_set_dot1q_vlan = ar8xxx_set_dot1q_vlan;
539f35f94f4SAdrian Chadd 	sc->hal.arswitch_flush_dot1q_vlan = ar8xxx_flush_dot1q_vlan;
540f35f94f4SAdrian Chadd 	sc->hal.arswitch_purge_dot1q_vlan = ar8xxx_purge_dot1q_vlan;
541749cac13SAdrian Chadd 	sc->hal.arswitch_get_port_vlan = ar8xxx_get_port_vlan;
542749cac13SAdrian Chadd 	sc->hal.arswitch_set_port_vlan = ar8xxx_set_port_vlan;
543749cac13SAdrian Chadd 
5444ff2f60dSAdrian Chadd 	sc->hal.arswitch_atu_flush = ar8xxx_atu_flush;
54562042c97SAdrian Chadd 	sc->hal.arswitch_atu_flush_port = ar8xxx_atu_flush_port;
54662042c97SAdrian Chadd 	sc->hal.arswitch_atu_learn_default = ar8xxx_atu_learn_default;
54762042c97SAdrian Chadd 	sc->hal.arswitch_atu_fetch_table = ar8xxx_atu_fetch_table;
548749cac13SAdrian Chadd 
54978549b94SAdrian Chadd 	sc->hal.arswitch_phy_read = arswitch_readphy_internal;
55078549b94SAdrian Chadd 	sc->hal.arswitch_phy_write = arswitch_writephy_internal;
551ddbc4420SAdrian Chadd 
552a043e8c7SAdrian Chadd 	/*
553a043e8c7SAdrian Chadd 	 * Attach switch related functions
554a043e8c7SAdrian Chadd 	 */
55527a2ecaaSAdrian Chadd 	if (AR8X16_IS_SWITCH(sc, AR7240))
55627a2ecaaSAdrian Chadd 		ar7240_attach(sc);
557b2152161SAdrian Chadd 	else if (AR8X16_IS_SWITCH(sc, AR9340))
558b2152161SAdrian Chadd 		ar9340_attach(sc);
55927a2ecaaSAdrian Chadd 	else if (AR8X16_IS_SWITCH(sc, AR8216))
560a043e8c7SAdrian Chadd 		ar8216_attach(sc);
561a043e8c7SAdrian Chadd 	else if (AR8X16_IS_SWITCH(sc, AR8226))
562a043e8c7SAdrian Chadd 		ar8226_attach(sc);
563a043e8c7SAdrian Chadd 	else if (AR8X16_IS_SWITCH(sc, AR8316))
564a043e8c7SAdrian Chadd 		ar8316_attach(sc);
565482d268dSAdrian Chadd 	else if (AR8X16_IS_SWITCH(sc, AR8327))
566482d268dSAdrian Chadd 		ar8327_attach(sc);
56778549b94SAdrian Chadd 	else {
5681b334c8bSAdrian Chadd 		DPRINTF(sc, ARSWITCH_DBG_ANY,
5691b334c8bSAdrian Chadd 		    "%s: unknown switch (%d)?\n", __func__, sc->sc_switchtype);
570a043e8c7SAdrian Chadd 		return (ENXIO);
57178549b94SAdrian Chadd 	}
572a043e8c7SAdrian Chadd 
573b9f07b86SLuiz Otavio O Souza 	/* Common defaults. */
574a043e8c7SAdrian Chadd 	sc->info.es_nports = 5; /* XXX technically 6, but 6th not used */
575a043e8c7SAdrian Chadd 
576a043e8c7SAdrian Chadd 	/* XXX Defaults for externally connected AR8316 */
577a043e8c7SAdrian Chadd 	sc->numphys = 4;
578a043e8c7SAdrian Chadd 	sc->phy4cpu = 1;
579a043e8c7SAdrian Chadd 	sc->is_rgmii = 1;
580a043e8c7SAdrian Chadd 	sc->is_gmii = 0;
581b2152161SAdrian Chadd 	sc->is_mii = 0;
582a043e8c7SAdrian Chadd 
583a043e8c7SAdrian Chadd 	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
584a043e8c7SAdrian Chadd 	    "numphys", &sc->numphys);
585a043e8c7SAdrian Chadd 	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
586a043e8c7SAdrian Chadd 	    "phy4cpu", &sc->phy4cpu);
587a043e8c7SAdrian Chadd 	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
588a043e8c7SAdrian Chadd 	    "is_rgmii", &sc->is_rgmii);
589a043e8c7SAdrian Chadd 	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
590a043e8c7SAdrian Chadd 	    "is_gmii", &sc->is_gmii);
591b2152161SAdrian Chadd 	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
592b2152161SAdrian Chadd 	    "is_mii", &sc->is_mii);
593a043e8c7SAdrian Chadd 
594b9f07b86SLuiz Otavio O Souza 	if (sc->numphys > AR8X16_NUM_PHYS)
595b9f07b86SLuiz Otavio O Souza 		sc->numphys = AR8X16_NUM_PHYS;
596a043e8c7SAdrian Chadd 
597b9f07b86SLuiz Otavio O Souza 	/* Reset the switch. */
59878549b94SAdrian Chadd 	if (arswitch_reset(dev)) {
5991b334c8bSAdrian Chadd 		DPRINTF(sc, ARSWITCH_DBG_ANY,
6001b334c8bSAdrian Chadd 		    "%s: arswitch_reset: failed\n", __func__);
601b9f07b86SLuiz Otavio O Souza 		return (ENXIO);
60278549b94SAdrian Chadd 	}
603a043e8c7SAdrian Chadd 
604b2152161SAdrian Chadd 	err = sc->hal.arswitch_hw_setup(sc);
6051b334c8bSAdrian Chadd 	if (err != 0) {
6061b334c8bSAdrian Chadd 		DPRINTF(sc, ARSWITCH_DBG_ANY,
6071b334c8bSAdrian Chadd 		    "%s: hw_setup: err=%d\n", __func__, err);
608b2152161SAdrian Chadd 		return (err);
6091b334c8bSAdrian Chadd 	}
610b2152161SAdrian Chadd 
611a043e8c7SAdrian Chadd 	err = sc->hal.arswitch_hw_global_setup(sc);
6121b334c8bSAdrian Chadd 	if (err != 0) {
6131b334c8bSAdrian Chadd 		DPRINTF(sc, ARSWITCH_DBG_ANY,
6141b334c8bSAdrian Chadd 		    "%s: hw_global_setup: err=%d\n", __func__, err);
615a043e8c7SAdrian Chadd 		return (err);
6161b334c8bSAdrian Chadd 	}
617a043e8c7SAdrian Chadd 
61862042c97SAdrian Chadd 	/*
61962042c97SAdrian Chadd 	 * Configure the default address table learning parameters for this
62062042c97SAdrian Chadd 	 * switch.
62162042c97SAdrian Chadd 	 */
62262042c97SAdrian Chadd 	err = sc->hal.arswitch_atu_learn_default(sc);
62362042c97SAdrian Chadd 	if (err != 0) {
62462042c97SAdrian Chadd 		DPRINTF(sc, ARSWITCH_DBG_ANY,
62562042c97SAdrian Chadd 		    "%s: atu_learn_default: err=%d\n", __func__, err);
62662042c97SAdrian Chadd 		return (err);
62762042c97SAdrian Chadd 	}
62862042c97SAdrian Chadd 
629b9f07b86SLuiz Otavio O Souza 	/* Initialize the switch ports. */
630df892897SAdrian Chadd 	for (port = 0; port <= sc->numphys; port++) {
631ddbc4420SAdrian Chadd 		sc->hal.arswitch_port_init(sc, port);
632df892897SAdrian Chadd 	}
633b9f07b86SLuiz Otavio O Souza 
634a043e8c7SAdrian Chadd 	/*
635a043e8c7SAdrian Chadd 	 * Attach the PHYs and complete the bus enumeration.
636a043e8c7SAdrian Chadd 	 */
637a043e8c7SAdrian Chadd 	err = arswitch_attach_phys(sc);
6381b334c8bSAdrian Chadd 	if (err != 0) {
6391b334c8bSAdrian Chadd 		DPRINTF(sc, ARSWITCH_DBG_ANY,
6401b334c8bSAdrian Chadd 		    "%s: attach_phys: err=%d\n", __func__, err);
641a043e8c7SAdrian Chadd 		return (err);
6421b334c8bSAdrian Chadd 	}
643a043e8c7SAdrian Chadd 
644b9f07b86SLuiz Otavio O Souza 	/* Default to ingress filters off. */
645b9f07b86SLuiz Otavio O Souza 	err = arswitch_set_vlan_mode(sc, 0);
6461b334c8bSAdrian Chadd 	if (err != 0) {
6471b334c8bSAdrian Chadd 		DPRINTF(sc, ARSWITCH_DBG_ANY,
6481b334c8bSAdrian Chadd 		    "%s: set_vlan_mode: err=%d\n", __func__, err);
649b9f07b86SLuiz Otavio O Souza 		return (err);
6501b334c8bSAdrian Chadd 	}
651b9f07b86SLuiz Otavio O Souza 
652a043e8c7SAdrian Chadd 	bus_generic_probe(dev);
653a043e8c7SAdrian Chadd 	bus_enumerate_hinted_children(dev);
654a043e8c7SAdrian Chadd 	err = bus_generic_attach(dev);
6551b334c8bSAdrian Chadd 	if (err != 0) {
6561b334c8bSAdrian Chadd 		DPRINTF(sc, ARSWITCH_DBG_ANY,
6571b334c8bSAdrian Chadd 		    "%s: bus_generic_attach: err=%d\n", __func__, err);
658a043e8c7SAdrian Chadd 		return (err);
6591b334c8bSAdrian Chadd 	}
660a043e8c7SAdrian Chadd 
661a043e8c7SAdrian Chadd 	callout_init_mtx(&sc->callout_tick, &sc->sc_mtx, 0);
662454d507aSAleksandr Rybalko 
663454d507aSAleksandr Rybalko 	ARSWITCH_LOCK(sc);
664a043e8c7SAdrian Chadd 	arswitch_tick(sc);
665454d507aSAleksandr Rybalko 	ARSWITCH_UNLOCK(sc);
666a043e8c7SAdrian Chadd 
667a043e8c7SAdrian Chadd 	return (err);
668a043e8c7SAdrian Chadd }
669a043e8c7SAdrian Chadd 
670a043e8c7SAdrian Chadd static int
671a043e8c7SAdrian Chadd arswitch_detach(device_t dev)
672a043e8c7SAdrian Chadd {
673a043e8c7SAdrian Chadd 	struct arswitch_softc *sc = device_get_softc(dev);
674a043e8c7SAdrian Chadd 	int i;
675a043e8c7SAdrian Chadd 
676a043e8c7SAdrian Chadd 	callout_drain(&sc->callout_tick);
677a043e8c7SAdrian Chadd 
678a043e8c7SAdrian Chadd 	for (i=0; i < sc->numphys; i++) {
679a043e8c7SAdrian Chadd 		if (sc->miibus[i] != NULL)
680a043e8c7SAdrian Chadd 			device_delete_child(dev, sc->miibus[i]);
681a043e8c7SAdrian Chadd 		if (sc->ifp[i] != NULL)
682a043e8c7SAdrian Chadd 			if_free(sc->ifp[i]);
683a043e8c7SAdrian Chadd 		free(sc->ifname[i], M_DEVBUF);
684a043e8c7SAdrian Chadd 	}
685a043e8c7SAdrian Chadd 
68662042c97SAdrian Chadd 	free(sc->atu.entries, M_DEVBUF);
68762042c97SAdrian Chadd 
688a043e8c7SAdrian Chadd 	bus_generic_detach(dev);
689a043e8c7SAdrian Chadd 	mtx_destroy(&sc->sc_mtx);
690a043e8c7SAdrian Chadd 
691a043e8c7SAdrian Chadd 	return (0);
692a043e8c7SAdrian Chadd }
693a043e8c7SAdrian Chadd 
694a043e8c7SAdrian Chadd /*
695a043e8c7SAdrian Chadd  * Convert PHY number to port number. PHY0 is connected to port 1, PHY1 to
696a043e8c7SAdrian Chadd  * port 2, etc.
697a043e8c7SAdrian Chadd  */
698a043e8c7SAdrian Chadd static inline int
699a043e8c7SAdrian Chadd arswitch_portforphy(int phy)
700a043e8c7SAdrian Chadd {
701a043e8c7SAdrian Chadd 	return (phy+1);
702a043e8c7SAdrian Chadd }
703a043e8c7SAdrian Chadd 
704a043e8c7SAdrian Chadd static inline struct mii_data *
705a043e8c7SAdrian Chadd arswitch_miiforport(struct arswitch_softc *sc, int port)
706a043e8c7SAdrian Chadd {
707a043e8c7SAdrian Chadd 	int phy = port-1;
708a043e8c7SAdrian Chadd 
709a043e8c7SAdrian Chadd 	if (phy < 0 || phy >= sc->numphys)
710a043e8c7SAdrian Chadd 		return (NULL);
711a043e8c7SAdrian Chadd 	return (device_get_softc(sc->miibus[phy]));
712a043e8c7SAdrian Chadd }
713a043e8c7SAdrian Chadd 
714a043e8c7SAdrian Chadd static inline struct ifnet *
715a043e8c7SAdrian Chadd arswitch_ifpforport(struct arswitch_softc *sc, int port)
716a043e8c7SAdrian Chadd {
717a043e8c7SAdrian Chadd 	int phy = port-1;
718a043e8c7SAdrian Chadd 
719a043e8c7SAdrian Chadd 	if (phy < 0 || phy >= sc->numphys)
720a043e8c7SAdrian Chadd 		return (NULL);
721a043e8c7SAdrian Chadd 	return (sc->ifp[phy]);
722a043e8c7SAdrian Chadd }
723a043e8c7SAdrian Chadd 
724a043e8c7SAdrian Chadd /*
725a043e8c7SAdrian Chadd  * Convert port status to ifmedia.
726a043e8c7SAdrian Chadd  */
727a043e8c7SAdrian Chadd static void
728a043e8c7SAdrian Chadd arswitch_update_ifmedia(int portstatus, u_int *media_status, u_int *media_active)
729a043e8c7SAdrian Chadd {
730a043e8c7SAdrian Chadd 	*media_active = IFM_ETHER;
731a043e8c7SAdrian Chadd 	*media_status = IFM_AVALID;
732a043e8c7SAdrian Chadd 
733a043e8c7SAdrian Chadd 	if ((portstatus & AR8X16_PORT_STS_LINK_UP) != 0)
734a043e8c7SAdrian Chadd 		*media_status |= IFM_ACTIVE;
735a043e8c7SAdrian Chadd 	else {
736a043e8c7SAdrian Chadd 		*media_active |= IFM_NONE;
737a043e8c7SAdrian Chadd 		return;
738a043e8c7SAdrian Chadd 	}
739a043e8c7SAdrian Chadd 	switch (portstatus & AR8X16_PORT_STS_SPEED_MASK) {
740a043e8c7SAdrian Chadd 	case AR8X16_PORT_STS_SPEED_10:
741a043e8c7SAdrian Chadd 		*media_active |= IFM_10_T;
742a043e8c7SAdrian Chadd 		break;
743a043e8c7SAdrian Chadd 	case AR8X16_PORT_STS_SPEED_100:
744a043e8c7SAdrian Chadd 		*media_active |= IFM_100_TX;
745a043e8c7SAdrian Chadd 		break;
746a043e8c7SAdrian Chadd 	case AR8X16_PORT_STS_SPEED_1000:
747a043e8c7SAdrian Chadd 		*media_active |= IFM_1000_T;
748a043e8c7SAdrian Chadd 		break;
749a043e8c7SAdrian Chadd 	}
750a043e8c7SAdrian Chadd 	if ((portstatus & AR8X16_PORT_STS_DUPLEX) == 0)
751a043e8c7SAdrian Chadd 		*media_active |= IFM_FDX;
752a043e8c7SAdrian Chadd 	else
753a043e8c7SAdrian Chadd 		*media_active |= IFM_HDX;
754a043e8c7SAdrian Chadd 	if ((portstatus & AR8X16_PORT_STS_TXFLOW) != 0)
755a043e8c7SAdrian Chadd 		*media_active |= IFM_ETH_TXPAUSE;
756a043e8c7SAdrian Chadd 	if ((portstatus & AR8X16_PORT_STS_RXFLOW) != 0)
757a043e8c7SAdrian Chadd 		*media_active |= IFM_ETH_RXPAUSE;
758a043e8c7SAdrian Chadd }
759a043e8c7SAdrian Chadd 
760a043e8c7SAdrian Chadd /*
761a043e8c7SAdrian Chadd  * Poll the status for all PHYs.  We're using the switch port status because
762a043e8c7SAdrian Chadd  * thats a lot quicker to read than talking to all the PHYs.  Care must be
763a043e8c7SAdrian Chadd  * taken that the resulting ifmedia_active is identical to what the PHY will
764a043e8c7SAdrian Chadd  * compute, or gratuitous link status changes will occur whenever the PHYs
765a043e8c7SAdrian Chadd  * update function is called.
766a043e8c7SAdrian Chadd  */
767a043e8c7SAdrian Chadd static void
768a043e8c7SAdrian Chadd arswitch_miipollstat(struct arswitch_softc *sc)
769a043e8c7SAdrian Chadd {
770a043e8c7SAdrian Chadd 	int i;
771a043e8c7SAdrian Chadd 	struct mii_data *mii;
772a043e8c7SAdrian Chadd 	struct mii_softc *miisc;
773a043e8c7SAdrian Chadd 	int portstatus;
7744ff2f60dSAdrian Chadd 	int port_flap = 0;
775a043e8c7SAdrian Chadd 
776454d507aSAleksandr Rybalko 	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
777454d507aSAleksandr Rybalko 
778a043e8c7SAdrian Chadd 	for (i = 0; i < sc->numphys; i++) {
779a043e8c7SAdrian Chadd 		if (sc->miibus[i] == NULL)
780a043e8c7SAdrian Chadd 			continue;
781a043e8c7SAdrian Chadd 		mii = device_get_softc(sc->miibus[i]);
782ddbc4420SAdrian Chadd 		/* XXX This would be nice to have abstracted out to be per-chip */
783ddbc4420SAdrian Chadd 		/* AR8327/AR8337 has a different register base */
784ddbc4420SAdrian Chadd 		if (AR8X16_IS_SWITCH(sc, AR8327))
785ddbc4420SAdrian Chadd 			portstatus = arswitch_readreg(sc->sc_dev,
786ddbc4420SAdrian Chadd 			    AR8327_REG_PORT_STATUS(arswitch_portforphy(i)));
787ddbc4420SAdrian Chadd 		else
788a043e8c7SAdrian Chadd 			portstatus = arswitch_readreg(sc->sc_dev,
789a043e8c7SAdrian Chadd 			    AR8X16_REG_PORT_STS(arswitch_portforphy(i)));
7901b334c8bSAdrian Chadd #if 1
7911b334c8bSAdrian Chadd 		DPRINTF(sc, ARSWITCH_DBG_POLL, "p[%d]=0x%08x (%b)\n",
792b2152161SAdrian Chadd 		    i,
793a043e8c7SAdrian Chadd 		    portstatus,
7941b334c8bSAdrian Chadd 		    portstatus,
795a043e8c7SAdrian Chadd 		    "\20\3TXMAC\4RXMAC\5TXFLOW\6RXFLOW\7"
796a043e8c7SAdrian Chadd 		    "DUPLEX\11LINK_UP\12LINK_AUTO\13LINK_PAUSE");
797a043e8c7SAdrian Chadd #endif
7984ff2f60dSAdrian Chadd 		/*
7994ff2f60dSAdrian Chadd 		 * If the current status is down, but we have a link
8004ff2f60dSAdrian Chadd 		 * status showing up, we need to do an ATU flush.
8014ff2f60dSAdrian Chadd 		 */
8024ff2f60dSAdrian Chadd 		if ((mii->mii_media_status & IFM_ACTIVE) == 0 &&
8034ff2f60dSAdrian Chadd 		    (portstatus & AR8X16_PORT_STS_LINK_UP) != 0) {
8044ff2f60dSAdrian Chadd 			device_printf(sc->sc_dev, "%s: port %d: port -> UP\n",
8054ff2f60dSAdrian Chadd 			    __func__,
8064ff2f60dSAdrian Chadd 			    i);
8074ff2f60dSAdrian Chadd 			port_flap = 1;
8084ff2f60dSAdrian Chadd 		}
8094ff2f60dSAdrian Chadd 		/*
8104ff2f60dSAdrian Chadd 		 * and maybe if a port goes up->down?
8114ff2f60dSAdrian Chadd 		 */
8124ff2f60dSAdrian Chadd 		if ((mii->mii_media_status & IFM_ACTIVE) != 0 &&
8134ff2f60dSAdrian Chadd 		    (portstatus & AR8X16_PORT_STS_LINK_UP) == 0) {
8144ff2f60dSAdrian Chadd 			device_printf(sc->sc_dev, "%s: port %d: port -> DOWN\n",
8154ff2f60dSAdrian Chadd 			    __func__,
8164ff2f60dSAdrian Chadd 			    i);
8174ff2f60dSAdrian Chadd 			port_flap = 1;
8184ff2f60dSAdrian Chadd 		}
819a043e8c7SAdrian Chadd 		arswitch_update_ifmedia(portstatus, &mii->mii_media_status,
820a043e8c7SAdrian Chadd 		    &mii->mii_media_active);
821a043e8c7SAdrian Chadd 		LIST_FOREACH(miisc, &mii->mii_phys, mii_list) {
822a043e8c7SAdrian Chadd 			if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) !=
823a043e8c7SAdrian Chadd 			    miisc->mii_inst)
824a043e8c7SAdrian Chadd 				continue;
825a043e8c7SAdrian Chadd 			mii_phy_update(miisc, MII_POLLSTAT);
826a043e8c7SAdrian Chadd 		}
827a043e8c7SAdrian Chadd 	}
8284ff2f60dSAdrian Chadd 
8294ff2f60dSAdrian Chadd 	/* If a port went from down->up, flush the ATU */
8304ff2f60dSAdrian Chadd 	if (port_flap)
8314ff2f60dSAdrian Chadd 		sc->hal.arswitch_atu_flush(sc);
832a043e8c7SAdrian Chadd }
833a043e8c7SAdrian Chadd 
834a043e8c7SAdrian Chadd static void
835a043e8c7SAdrian Chadd arswitch_tick(void *arg)
836a043e8c7SAdrian Chadd {
837a043e8c7SAdrian Chadd 	struct arswitch_softc *sc = arg;
838a043e8c7SAdrian Chadd 
839a043e8c7SAdrian Chadd 	arswitch_miipollstat(sc);
840a043e8c7SAdrian Chadd 	callout_reset(&sc->callout_tick, hz, arswitch_tick, sc);
841a043e8c7SAdrian Chadd }
842a043e8c7SAdrian Chadd 
843454d507aSAleksandr Rybalko static void
844454d507aSAleksandr Rybalko arswitch_lock(device_t dev)
845454d507aSAleksandr Rybalko {
846454d507aSAleksandr Rybalko 	struct arswitch_softc *sc = device_get_softc(dev);
847454d507aSAleksandr Rybalko 
848454d507aSAleksandr Rybalko 	ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
849454d507aSAleksandr Rybalko 	ARSWITCH_LOCK(sc);
850454d507aSAleksandr Rybalko }
851454d507aSAleksandr Rybalko 
852454d507aSAleksandr Rybalko static void
853454d507aSAleksandr Rybalko arswitch_unlock(device_t dev)
854454d507aSAleksandr Rybalko {
855454d507aSAleksandr Rybalko 	struct arswitch_softc *sc = device_get_softc(dev);
856454d507aSAleksandr Rybalko 
857454d507aSAleksandr Rybalko 	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
858454d507aSAleksandr Rybalko 	ARSWITCH_UNLOCK(sc);
859454d507aSAleksandr Rybalko }
860454d507aSAleksandr Rybalko 
861a043e8c7SAdrian Chadd static etherswitch_info_t *
862a043e8c7SAdrian Chadd arswitch_getinfo(device_t dev)
863a043e8c7SAdrian Chadd {
864a043e8c7SAdrian Chadd 	struct arswitch_softc *sc = device_get_softc(dev);
865a043e8c7SAdrian Chadd 
866a043e8c7SAdrian Chadd 	return (&sc->info);
867a043e8c7SAdrian Chadd }
868a043e8c7SAdrian Chadd 
869a043e8c7SAdrian Chadd static int
870a9ad4222SAdrian Chadd ar8xxx_port_vlan_get(struct arswitch_softc *sc, etherswitch_port_t *p)
871a043e8c7SAdrian Chadd {
872b9f07b86SLuiz Otavio O Souza 	uint32_t reg;
873b9f07b86SLuiz Otavio O Souza 
874b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK(sc);
875b9f07b86SLuiz Otavio O Souza 
876b9f07b86SLuiz Otavio O Souza 	/* Retrieve the PVID. */
8776dcbabd7SAdrian Chadd 	sc->hal.arswitch_vlan_get_pvid(sc, p->es_port, &p->es_pvid);
878b9f07b86SLuiz Otavio O Souza 
879b9f07b86SLuiz Otavio O Souza 	/* Port flags. */
880b9f07b86SLuiz Otavio O Souza 	reg = arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_CTRL(p->es_port));
881b9f07b86SLuiz Otavio O Souza 	if (reg & AR8X16_PORT_CTRL_DOUBLE_TAG)
882b9f07b86SLuiz Otavio O Souza 		p->es_flags |= ETHERSWITCH_PORT_DOUBLE_TAG;
883b9f07b86SLuiz Otavio O Souza 	reg >>= AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT;
884b9f07b86SLuiz Otavio O Souza 	if ((reg & 0x3) == AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_ADD)
885b9f07b86SLuiz Otavio O Souza 		p->es_flags |= ETHERSWITCH_PORT_ADDTAG;
886b9f07b86SLuiz Otavio O Souza 	if ((reg & 0x3) == AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_STRIP)
887b9f07b86SLuiz Otavio O Souza 		p->es_flags |= ETHERSWITCH_PORT_STRIPTAG;
888b9f07b86SLuiz Otavio O Souza 	ARSWITCH_UNLOCK(sc);
889a043e8c7SAdrian Chadd 
890a9ad4222SAdrian Chadd 	return (0);
891a9ad4222SAdrian Chadd }
892a9ad4222SAdrian Chadd 
893a9ad4222SAdrian Chadd static int
894749cac13SAdrian Chadd arswitch_is_cpuport(struct arswitch_softc *sc, int port)
895749cac13SAdrian Chadd {
896749cac13SAdrian Chadd 
897749cac13SAdrian Chadd 	return ((port == AR8X16_PORT_CPU) ||
898749cac13SAdrian Chadd 	    ((AR8X16_IS_SWITCH(sc, AR8327) &&
899749cac13SAdrian Chadd 	      port == AR8327_PORT_GMAC6)));
900749cac13SAdrian Chadd }
901749cac13SAdrian Chadd 
902749cac13SAdrian Chadd static int
903a9ad4222SAdrian Chadd arswitch_getport(device_t dev, etherswitch_port_t *p)
904a9ad4222SAdrian Chadd {
905a9ad4222SAdrian Chadd 	struct arswitch_softc *sc;
906a9ad4222SAdrian Chadd 	struct mii_data *mii;
907a9ad4222SAdrian Chadd 	struct ifmediareq *ifmr;
908a9ad4222SAdrian Chadd 	int err;
909a9ad4222SAdrian Chadd 
910a9ad4222SAdrian Chadd 	sc = device_get_softc(dev);
911749cac13SAdrian Chadd 	/* XXX +1 is for AR8327; should make this configurable! */
912749cac13SAdrian Chadd 	if (p->es_port < 0 || p->es_port > sc->info.es_nports)
913a9ad4222SAdrian Chadd 		return (ENXIO);
914a9ad4222SAdrian Chadd 
915a9ad4222SAdrian Chadd 	err = sc->hal.arswitch_port_vlan_get(sc, p);
916a9ad4222SAdrian Chadd 	if (err != 0)
917a9ad4222SAdrian Chadd 		return (err);
918a9ad4222SAdrian Chadd 
919a043e8c7SAdrian Chadd 	mii = arswitch_miiforport(sc, p->es_port);
920749cac13SAdrian Chadd 	if (arswitch_is_cpuport(sc, p->es_port)) {
921a043e8c7SAdrian Chadd 		/* fill in fixed values for CPU port */
922a9ad4222SAdrian Chadd 		/* XXX is this valid in all cases? */
923f47857dcSAdrian Chadd 		p->es_flags |= ETHERSWITCH_PORT_CPU;
924b9f07b86SLuiz Otavio O Souza 		ifmr = &p->es_ifmr;
925a043e8c7SAdrian Chadd 		ifmr->ifm_count = 0;
926a043e8c7SAdrian Chadd 		ifmr->ifm_current = ifmr->ifm_active =
927a043e8c7SAdrian Chadd 		    IFM_ETHER | IFM_1000_T | IFM_FDX;
928a043e8c7SAdrian Chadd 		ifmr->ifm_mask = 0;
929a043e8c7SAdrian Chadd 		ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
930a043e8c7SAdrian Chadd 	} else if (mii != NULL) {
931a043e8c7SAdrian Chadd 		err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr,
932a043e8c7SAdrian Chadd 		    &mii->mii_media, SIOCGIFMEDIA);
933a043e8c7SAdrian Chadd 		if (err)
934a043e8c7SAdrian Chadd 			return (err);
935a043e8c7SAdrian Chadd 	} else {
936a043e8c7SAdrian Chadd 		return (ENXIO);
937a043e8c7SAdrian Chadd 	}
938c94dc808SAdrian Chadd 
939c94dc808SAdrian Chadd 	if (!arswitch_is_cpuport(sc, p->es_port) &&
940c94dc808SAdrian Chadd 	    AR8X16_IS_SWITCH(sc, AR8327)) {
941c94dc808SAdrian Chadd 		int led;
942c94dc808SAdrian Chadd 		p->es_nleds = 3;
943c94dc808SAdrian Chadd 
944c94dc808SAdrian Chadd 		for (led = 0; led < p->es_nleds; led++)
945c94dc808SAdrian Chadd 		{
946c94dc808SAdrian Chadd 			int style;
947c94dc808SAdrian Chadd 			uint32_t val;
948c94dc808SAdrian Chadd 
949c94dc808SAdrian Chadd 			/* Find the right style enum for our pattern */
950c94dc808SAdrian Chadd 			val = arswitch_readreg(dev,
951c94dc808SAdrian Chadd 			    ar8327_led_mapping[p->es_port-1][led].reg);
952c94dc808SAdrian Chadd 			val = (val>>ar8327_led_mapping[p->es_port-1][led].shift)&0x03;
953c94dc808SAdrian Chadd 
954c94dc808SAdrian Chadd 			for (style = 0; style < ETHERSWITCH_PORT_LED_MAX; style++)
955c94dc808SAdrian Chadd 			{
956c94dc808SAdrian Chadd 				if (led_pattern_table[style] == val) break;
957c94dc808SAdrian Chadd 			}
958c94dc808SAdrian Chadd 
959c94dc808SAdrian Chadd 			/* can't happen */
960c94dc808SAdrian Chadd 			if (style == ETHERSWITCH_PORT_LED_MAX)
961c94dc808SAdrian Chadd 				style = ETHERSWITCH_PORT_LED_DEFAULT;
962c94dc808SAdrian Chadd 
963c94dc808SAdrian Chadd 			p->es_led[led] = style;
964c94dc808SAdrian Chadd 		}
965c94dc808SAdrian Chadd 	} else
966c94dc808SAdrian Chadd 	{
967c94dc808SAdrian Chadd 		p->es_nleds = 0;
968c94dc808SAdrian Chadd 	}
969c94dc808SAdrian Chadd 
970a043e8c7SAdrian Chadd 	return (0);
971a043e8c7SAdrian Chadd }
972a043e8c7SAdrian Chadd 
973a043e8c7SAdrian Chadd static int
974a9ad4222SAdrian Chadd ar8xxx_port_vlan_setup(struct arswitch_softc *sc, etherswitch_port_t *p)
975a043e8c7SAdrian Chadd {
976b9f07b86SLuiz Otavio O Souza 	uint32_t reg;
977a9ad4222SAdrian Chadd 	int err;
978a043e8c7SAdrian Chadd 
979b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK(sc);
980a9ad4222SAdrian Chadd 
981b9f07b86SLuiz Otavio O Souza 	/* Set the PVID. */
982b9f07b86SLuiz Otavio O Souza 	if (p->es_pvid != 0)
9836dcbabd7SAdrian Chadd 		sc->hal.arswitch_vlan_set_pvid(sc, p->es_port, p->es_pvid);
984b9f07b86SLuiz Otavio O Souza 
985b9f07b86SLuiz Otavio O Souza 	/* Mutually exclusive. */
986b9f07b86SLuiz Otavio O Souza 	if (p->es_flags & ETHERSWITCH_PORT_ADDTAG &&
987b9f07b86SLuiz Otavio O Souza 	    p->es_flags & ETHERSWITCH_PORT_STRIPTAG) {
988b9f07b86SLuiz Otavio O Souza 		ARSWITCH_UNLOCK(sc);
989b9f07b86SLuiz Otavio O Souza 		return (EINVAL);
990b9f07b86SLuiz Otavio O Souza 	}
991b9f07b86SLuiz Otavio O Souza 
992b9f07b86SLuiz Otavio O Souza 	reg = 0;
993b9f07b86SLuiz Otavio O Souza 	if (p->es_flags & ETHERSWITCH_PORT_DOUBLE_TAG)
994b9f07b86SLuiz Otavio O Souza 		reg |= AR8X16_PORT_CTRL_DOUBLE_TAG;
995b9f07b86SLuiz Otavio O Souza 	if (p->es_flags & ETHERSWITCH_PORT_ADDTAG)
996b9f07b86SLuiz Otavio O Souza 		reg |= AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_ADD <<
997b9f07b86SLuiz Otavio O Souza 		    AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT;
998b9f07b86SLuiz Otavio O Souza 	if (p->es_flags & ETHERSWITCH_PORT_STRIPTAG)
999b9f07b86SLuiz Otavio O Souza 		reg |= AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_STRIP <<
1000b9f07b86SLuiz Otavio O Souza 		    AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT;
1001b9f07b86SLuiz Otavio O Souza 
1002b9f07b86SLuiz Otavio O Souza 	err = arswitch_modifyreg(sc->sc_dev,
1003b9f07b86SLuiz Otavio O Souza 	    AR8X16_REG_PORT_CTRL(p->es_port),
1004b9f07b86SLuiz Otavio O Souza 	    0x3 << AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT |
1005b9f07b86SLuiz Otavio O Souza 	    AR8X16_PORT_CTRL_DOUBLE_TAG, reg);
1006b9f07b86SLuiz Otavio O Souza 
1007b9f07b86SLuiz Otavio O Souza 	ARSWITCH_UNLOCK(sc);
1008a9ad4222SAdrian Chadd 	return (err);
1009a9ad4222SAdrian Chadd }
1010a9ad4222SAdrian Chadd 
1011a9ad4222SAdrian Chadd static int
1012a9ad4222SAdrian Chadd arswitch_setport(device_t dev, etherswitch_port_t *p)
1013a9ad4222SAdrian Chadd {
1014c94dc808SAdrian Chadd 	int err, i;
1015a9ad4222SAdrian Chadd 	struct arswitch_softc *sc;
1016a9ad4222SAdrian Chadd 	struct ifmedia *ifm;
1017a9ad4222SAdrian Chadd 	struct mii_data *mii;
1018a9ad4222SAdrian Chadd 	struct ifnet *ifp;
1019a9ad4222SAdrian Chadd 
1020a9ad4222SAdrian Chadd 	sc = device_get_softc(dev);
1021749cac13SAdrian Chadd 	if (p->es_port < 0 || p->es_port > sc->info.es_nports)
1022a9ad4222SAdrian Chadd 		return (ENXIO);
1023a9ad4222SAdrian Chadd 
1024a9ad4222SAdrian Chadd 	/* Port flags. */
1025a9ad4222SAdrian Chadd 	if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
1026a9ad4222SAdrian Chadd 		err = sc->hal.arswitch_port_vlan_setup(sc, p);
1027b9f07b86SLuiz Otavio O Souza 		if (err)
1028b9f07b86SLuiz Otavio O Souza 			return (err);
1029b9f07b86SLuiz Otavio O Souza 	}
1030b9f07b86SLuiz Otavio O Souza 
1031c94dc808SAdrian Chadd 	/* Do not allow media or led changes on CPU port. */
1032749cac13SAdrian Chadd 	if (arswitch_is_cpuport(sc, p->es_port))
1033b9f07b86SLuiz Otavio O Souza 		return (0);
1034a043e8c7SAdrian Chadd 
1035c94dc808SAdrian Chadd 	if (AR8X16_IS_SWITCH(sc, AR8327))
1036c94dc808SAdrian Chadd 	{
1037c94dc808SAdrian Chadd 		for (i = 0; i < 3; i++)
1038c94dc808SAdrian Chadd 		{
1039c94dc808SAdrian Chadd 			int err;
1040c94dc808SAdrian Chadd 			err = arswitch_setled(sc, p->es_port-1, i, p->es_led[i]);
1041c94dc808SAdrian Chadd 			if (err)
1042c94dc808SAdrian Chadd 				return (err);
1043c94dc808SAdrian Chadd 		}
1044c94dc808SAdrian Chadd 	}
1045c94dc808SAdrian Chadd 
1046a043e8c7SAdrian Chadd 	mii = arswitch_miiforport(sc, p->es_port);
1047a043e8c7SAdrian Chadd 	if (mii == NULL)
1048a043e8c7SAdrian Chadd 		return (ENXIO);
1049a043e8c7SAdrian Chadd 
1050a043e8c7SAdrian Chadd 	ifp = arswitch_ifpforport(sc, p->es_port);
1051a043e8c7SAdrian Chadd 
1052a043e8c7SAdrian Chadd 	ifm = &mii->mii_media;
1053b9f07b86SLuiz Otavio O Souza 	return (ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA));
1054a043e8c7SAdrian Chadd }
1055a043e8c7SAdrian Chadd 
1056c94dc808SAdrian Chadd static int
1057c94dc808SAdrian Chadd arswitch_setled(struct arswitch_softc *sc, int phy, int led, int style)
1058c94dc808SAdrian Chadd {
1059c94dc808SAdrian Chadd 	int shift;
10606d011946SKristof Provost 	int err;
1061c94dc808SAdrian Chadd 
1062c94dc808SAdrian Chadd 	if (phy < 0 || phy > sc->numphys)
1063c94dc808SAdrian Chadd 		return EINVAL;
1064c94dc808SAdrian Chadd 
1065c94dc808SAdrian Chadd 	if (style < 0 || style > ETHERSWITCH_PORT_LED_MAX)
1066c94dc808SAdrian Chadd 		return (EINVAL);
1067c94dc808SAdrian Chadd 
10686d011946SKristof Provost 	ARSWITCH_LOCK(sc);
10696d011946SKristof Provost 
1070c94dc808SAdrian Chadd 	shift = ar8327_led_mapping[phy][led].shift;
10716d011946SKristof Provost 	err = (arswitch_modifyreg(sc->sc_dev,
1072c94dc808SAdrian Chadd 	    ar8327_led_mapping[phy][led].reg,
1073c94dc808SAdrian Chadd 	    0x03 << shift, led_pattern_table[style] << shift));
10746d011946SKristof Provost 	ARSWITCH_UNLOCK(sc);
10756d011946SKristof Provost 
10766d011946SKristof Provost 	return (err);
1077c94dc808SAdrian Chadd }
1078c94dc808SAdrian Chadd 
1079a043e8c7SAdrian Chadd static void
1080a043e8c7SAdrian Chadd arswitch_statchg(device_t dev)
1081a043e8c7SAdrian Chadd {
10821b334c8bSAdrian Chadd 	struct arswitch_softc *sc = device_get_softc(dev);
1083a043e8c7SAdrian Chadd 
10841b334c8bSAdrian Chadd 	DPRINTF(sc, ARSWITCH_DBG_POLL, "%s\n", __func__);
1085a043e8c7SAdrian Chadd }
1086a043e8c7SAdrian Chadd 
1087a043e8c7SAdrian Chadd static int
1088a043e8c7SAdrian Chadd arswitch_ifmedia_upd(struct ifnet *ifp)
1089a043e8c7SAdrian Chadd {
1090a043e8c7SAdrian Chadd 	struct arswitch_softc *sc = ifp->if_softc;
1091a043e8c7SAdrian Chadd 	struct mii_data *mii = arswitch_miiforport(sc, ifp->if_dunit);
1092a043e8c7SAdrian Chadd 
1093a043e8c7SAdrian Chadd 	if (mii == NULL)
1094a043e8c7SAdrian Chadd 		return (ENXIO);
1095a043e8c7SAdrian Chadd 	mii_mediachg(mii);
1096a043e8c7SAdrian Chadd 	return (0);
1097a043e8c7SAdrian Chadd }
1098a043e8c7SAdrian Chadd 
1099a043e8c7SAdrian Chadd static void
1100a043e8c7SAdrian Chadd arswitch_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
1101a043e8c7SAdrian Chadd {
1102a043e8c7SAdrian Chadd 	struct arswitch_softc *sc = ifp->if_softc;
1103a043e8c7SAdrian Chadd 	struct mii_data *mii = arswitch_miiforport(sc, ifp->if_dunit);
1104a043e8c7SAdrian Chadd 
11051b334c8bSAdrian Chadd 	DPRINTF(sc, ARSWITCH_DBG_POLL, "%s\n", __func__);
1106a043e8c7SAdrian Chadd 
1107a043e8c7SAdrian Chadd 	if (mii == NULL)
1108a043e8c7SAdrian Chadd 		return;
1109a043e8c7SAdrian Chadd 	mii_pollstat(mii);
1110a043e8c7SAdrian Chadd 	ifmr->ifm_active = mii->mii_media_active;
1111a043e8c7SAdrian Chadd 	ifmr->ifm_status = mii->mii_media_status;
1112a043e8c7SAdrian Chadd }
1113a043e8c7SAdrian Chadd 
1114b9f07b86SLuiz Otavio O Souza static int
1115b9f07b86SLuiz Otavio O Souza arswitch_getconf(device_t dev, etherswitch_conf_t *conf)
1116b9f07b86SLuiz Otavio O Souza {
1117b9f07b86SLuiz Otavio O Souza 	struct arswitch_softc *sc;
1118b9f07b86SLuiz Otavio O Souza 
1119b9f07b86SLuiz Otavio O Souza 	sc = device_get_softc(dev);
1120b9f07b86SLuiz Otavio O Souza 
1121b9f07b86SLuiz Otavio O Souza 	/* Return the VLAN mode. */
1122b9f07b86SLuiz Otavio O Souza 	conf->cmd = ETHERSWITCH_CONF_VLAN_MODE;
1123b9f07b86SLuiz Otavio O Souza 	conf->vlan_mode = sc->vlan_mode;
1124b9f07b86SLuiz Otavio O Souza 
1125b9f07b86SLuiz Otavio O Souza 	return (0);
1126b9f07b86SLuiz Otavio O Souza }
1127b9f07b86SLuiz Otavio O Souza 
1128b9f07b86SLuiz Otavio O Souza static int
1129b9f07b86SLuiz Otavio O Souza arswitch_setconf(device_t dev, etherswitch_conf_t *conf)
1130b9f07b86SLuiz Otavio O Souza {
1131b9f07b86SLuiz Otavio O Souza 	struct arswitch_softc *sc;
1132b9f07b86SLuiz Otavio O Souza 	int err;
1133b9f07b86SLuiz Otavio O Souza 
1134b9f07b86SLuiz Otavio O Souza 	sc = device_get_softc(dev);
1135b9f07b86SLuiz Otavio O Souza 
1136b9f07b86SLuiz Otavio O Souza 	/* Set the VLAN mode. */
1137b9f07b86SLuiz Otavio O Souza 	if (conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) {
1138b9f07b86SLuiz Otavio O Souza 		err = arswitch_set_vlan_mode(sc, conf->vlan_mode);
1139b9f07b86SLuiz Otavio O Souza 		if (err != 0)
1140b9f07b86SLuiz Otavio O Souza 			return (err);
1141b9f07b86SLuiz Otavio O Souza 	}
1142b9f07b86SLuiz Otavio O Souza 
1143b9f07b86SLuiz Otavio O Souza 	return (0);
1144b9f07b86SLuiz Otavio O Souza }
1145b9f07b86SLuiz Otavio O Souza 
11466dcbabd7SAdrian Chadd static int
114762042c97SAdrian Chadd arswitch_atu_flush_all(device_t dev)
114862042c97SAdrian Chadd {
114962042c97SAdrian Chadd 	struct arswitch_softc *sc;
115062042c97SAdrian Chadd 	int err;
115162042c97SAdrian Chadd 
115262042c97SAdrian Chadd 	sc = device_get_softc(dev);
115362042c97SAdrian Chadd 	ARSWITCH_LOCK(sc);
115462042c97SAdrian Chadd 	err = sc->hal.arswitch_atu_flush(sc);
115562042c97SAdrian Chadd 	/* Invalidate cached ATU */
115662042c97SAdrian Chadd 	sc->atu.count = 0;
115762042c97SAdrian Chadd 	ARSWITCH_UNLOCK(sc);
115862042c97SAdrian Chadd 	return (err);
115962042c97SAdrian Chadd }
116062042c97SAdrian Chadd 
116162042c97SAdrian Chadd static int
116262042c97SAdrian Chadd arswitch_atu_flush_port(device_t dev, int port)
116362042c97SAdrian Chadd {
116462042c97SAdrian Chadd 	struct arswitch_softc *sc;
116562042c97SAdrian Chadd 	int err;
116662042c97SAdrian Chadd 
116762042c97SAdrian Chadd 	sc = device_get_softc(dev);
116862042c97SAdrian Chadd 	ARSWITCH_LOCK(sc);
116962042c97SAdrian Chadd 	err = sc->hal.arswitch_atu_flush_port(sc, port);
117062042c97SAdrian Chadd 	/* Invalidate cached ATU */
117162042c97SAdrian Chadd 	sc->atu.count = 0;
117262042c97SAdrian Chadd 	ARSWITCH_UNLOCK(sc);
117362042c97SAdrian Chadd 	return (err);
117462042c97SAdrian Chadd }
117562042c97SAdrian Chadd 
117662042c97SAdrian Chadd static int
117762042c97SAdrian Chadd arswitch_atu_fetch_table(device_t dev, etherswitch_atu_table_t *table)
117862042c97SAdrian Chadd {
117962042c97SAdrian Chadd 	struct arswitch_softc *sc;
118062042c97SAdrian Chadd 	int err, nitems;
118162042c97SAdrian Chadd 
118262042c97SAdrian Chadd 	sc = device_get_softc(dev);
118362042c97SAdrian Chadd 
118462042c97SAdrian Chadd 	ARSWITCH_LOCK(sc);
118562042c97SAdrian Chadd 	/* Initial setup */
118662042c97SAdrian Chadd 	nitems = 0;
118762042c97SAdrian Chadd 	err = sc->hal.arswitch_atu_fetch_table(sc, NULL, 0);
118862042c97SAdrian Chadd 
118962042c97SAdrian Chadd 	/* fetch - ideally yes we'd fetch into a separate table then switch */
1190*7ed08319SAdrian Chadd 	while (err == 0 && nitems < sc->atu.size) {
119162042c97SAdrian Chadd 		err = sc->hal.arswitch_atu_fetch_table(sc,
119262042c97SAdrian Chadd 		    &sc->atu.entries[nitems], 1);
119362042c97SAdrian Chadd 		if (err == 0) {
119462042c97SAdrian Chadd 			sc->atu.entries[nitems].id = nitems;
119562042c97SAdrian Chadd 			nitems++;
119662042c97SAdrian Chadd 		}
119762042c97SAdrian Chadd 	}
119862042c97SAdrian Chadd 	sc->atu.count = nitems;
119962042c97SAdrian Chadd 	ARSWITCH_UNLOCK(sc);
120062042c97SAdrian Chadd 
120162042c97SAdrian Chadd 	table->es_nitems = nitems;
120262042c97SAdrian Chadd 
120362042c97SAdrian Chadd 	return (0);
120462042c97SAdrian Chadd }
120562042c97SAdrian Chadd 
120662042c97SAdrian Chadd static int
120762042c97SAdrian Chadd arswitch_atu_fetch_table_entry(device_t dev, etherswitch_atu_entry_t *e)
120862042c97SAdrian Chadd {
120962042c97SAdrian Chadd 	struct arswitch_softc *sc;
121062042c97SAdrian Chadd 	int id;
121162042c97SAdrian Chadd 
121262042c97SAdrian Chadd 	sc = device_get_softc(dev);
121362042c97SAdrian Chadd 	id = e->id;
121462042c97SAdrian Chadd 
121562042c97SAdrian Chadd 	ARSWITCH_LOCK(sc);
121662042c97SAdrian Chadd 	if (id > sc->atu.count) {
121762042c97SAdrian Chadd 		ARSWITCH_UNLOCK(sc);
121862042c97SAdrian Chadd 		return (ENOENT);
121962042c97SAdrian Chadd 	}
122062042c97SAdrian Chadd 
122162042c97SAdrian Chadd 	memcpy(e, &sc->atu.entries[id], sizeof(*e));
122262042c97SAdrian Chadd 	ARSWITCH_UNLOCK(sc);
122362042c97SAdrian Chadd 	return (0);
122462042c97SAdrian Chadd }
122562042c97SAdrian Chadd 
122662042c97SAdrian Chadd static int
12276dcbabd7SAdrian Chadd arswitch_getvgroup(device_t dev, etherswitch_vlangroup_t *e)
12286dcbabd7SAdrian Chadd {
12296dcbabd7SAdrian Chadd 	struct arswitch_softc *sc = device_get_softc(dev);
12306dcbabd7SAdrian Chadd 
12316dcbabd7SAdrian Chadd 	return (sc->hal.arswitch_vlan_getvgroup(sc, e));
12326dcbabd7SAdrian Chadd }
12336dcbabd7SAdrian Chadd 
12346dcbabd7SAdrian Chadd static int
12356dcbabd7SAdrian Chadd arswitch_setvgroup(device_t dev, etherswitch_vlangroup_t *e)
12366dcbabd7SAdrian Chadd {
12376dcbabd7SAdrian Chadd 	struct arswitch_softc *sc = device_get_softc(dev);
12386dcbabd7SAdrian Chadd 
12396dcbabd7SAdrian Chadd 	return (sc->hal.arswitch_vlan_setvgroup(sc, e));
12406dcbabd7SAdrian Chadd }
12416dcbabd7SAdrian Chadd 
124278549b94SAdrian Chadd static int
124378549b94SAdrian Chadd arswitch_readphy(device_t dev, int phy, int reg)
124478549b94SAdrian Chadd {
124578549b94SAdrian Chadd 	struct arswitch_softc *sc = device_get_softc(dev);
124678549b94SAdrian Chadd 
124778549b94SAdrian Chadd 	return (sc->hal.arswitch_phy_read(dev, phy, reg));
124878549b94SAdrian Chadd }
124978549b94SAdrian Chadd 
125078549b94SAdrian Chadd static int
125178549b94SAdrian Chadd arswitch_writephy(device_t dev, int phy, int reg, int val)
125278549b94SAdrian Chadd {
125378549b94SAdrian Chadd 	struct arswitch_softc *sc = device_get_softc(dev);
125478549b94SAdrian Chadd 
125578549b94SAdrian Chadd 	return (sc->hal.arswitch_phy_write(dev, phy, reg, val));
125678549b94SAdrian Chadd }
125778549b94SAdrian Chadd 
1258a043e8c7SAdrian Chadd static device_method_t arswitch_methods[] = {
1259a043e8c7SAdrian Chadd 	/* Device interface */
1260a043e8c7SAdrian Chadd 	DEVMETHOD(device_probe,		arswitch_probe),
1261a043e8c7SAdrian Chadd 	DEVMETHOD(device_attach,	arswitch_attach),
1262a043e8c7SAdrian Chadd 	DEVMETHOD(device_detach,	arswitch_detach),
1263a043e8c7SAdrian Chadd 
1264a043e8c7SAdrian Chadd 	/* bus interface */
1265a043e8c7SAdrian Chadd 	DEVMETHOD(bus_add_child,	device_add_child_ordered),
1266a043e8c7SAdrian Chadd 
1267a043e8c7SAdrian Chadd 	/* MII interface */
1268a043e8c7SAdrian Chadd 	DEVMETHOD(miibus_readreg,	arswitch_readphy),
1269a043e8c7SAdrian Chadd 	DEVMETHOD(miibus_writereg,	arswitch_writephy),
1270a043e8c7SAdrian Chadd 	DEVMETHOD(miibus_statchg,	arswitch_statchg),
1271a043e8c7SAdrian Chadd 
1272a043e8c7SAdrian Chadd 	/* MDIO interface */
1273a043e8c7SAdrian Chadd 	DEVMETHOD(mdio_readreg,		arswitch_readphy),
1274a043e8c7SAdrian Chadd 	DEVMETHOD(mdio_writereg,	arswitch_writephy),
1275a043e8c7SAdrian Chadd 
1276a043e8c7SAdrian Chadd 	/* etherswitch interface */
1277454d507aSAleksandr Rybalko 	DEVMETHOD(etherswitch_lock,	arswitch_lock),
1278454d507aSAleksandr Rybalko 	DEVMETHOD(etherswitch_unlock,	arswitch_unlock),
1279a043e8c7SAdrian Chadd 	DEVMETHOD(etherswitch_getinfo,	arswitch_getinfo),
1280a043e8c7SAdrian Chadd 	DEVMETHOD(etherswitch_readreg,	arswitch_readreg),
1281a043e8c7SAdrian Chadd 	DEVMETHOD(etherswitch_writereg,	arswitch_writereg),
1282a043e8c7SAdrian Chadd 	DEVMETHOD(etherswitch_readphyreg,	arswitch_readphy),
1283a043e8c7SAdrian Chadd 	DEVMETHOD(etherswitch_writephyreg,	arswitch_writephy),
1284a043e8c7SAdrian Chadd 	DEVMETHOD(etherswitch_getport,	arswitch_getport),
1285a043e8c7SAdrian Chadd 	DEVMETHOD(etherswitch_setport,	arswitch_setport),
1286a043e8c7SAdrian Chadd 	DEVMETHOD(etherswitch_getvgroup,	arswitch_getvgroup),
1287a043e8c7SAdrian Chadd 	DEVMETHOD(etherswitch_setvgroup,	arswitch_setvgroup),
1288b9f07b86SLuiz Otavio O Souza 	DEVMETHOD(etherswitch_getconf,	arswitch_getconf),
1289b9f07b86SLuiz Otavio O Souza 	DEVMETHOD(etherswitch_setconf,	arswitch_setconf),
129062042c97SAdrian Chadd 	DEVMETHOD(etherswitch_flush_all, arswitch_atu_flush_all),
129162042c97SAdrian Chadd 	DEVMETHOD(etherswitch_flush_port, arswitch_atu_flush_port),
129262042c97SAdrian Chadd 	DEVMETHOD(etherswitch_fetch_table, arswitch_atu_fetch_table),
129362042c97SAdrian Chadd 	DEVMETHOD(etherswitch_fetch_table_entry, arswitch_atu_fetch_table_entry),
1294a043e8c7SAdrian Chadd 
1295a043e8c7SAdrian Chadd 	DEVMETHOD_END
1296a043e8c7SAdrian Chadd };
1297a043e8c7SAdrian Chadd 
1298a043e8c7SAdrian Chadd DEFINE_CLASS_0(arswitch, arswitch_driver, arswitch_methods,
1299a043e8c7SAdrian Chadd     sizeof(struct arswitch_softc));
1300a043e8c7SAdrian Chadd static devclass_t arswitch_devclass;
1301a043e8c7SAdrian Chadd 
1302a043e8c7SAdrian Chadd DRIVER_MODULE(arswitch, mdio, arswitch_driver, arswitch_devclass, 0, 0);
1303a043e8c7SAdrian Chadd DRIVER_MODULE(miibus, arswitch, miibus_driver, miibus_devclass, 0, 0);
1304a043e8c7SAdrian Chadd DRIVER_MODULE(mdio, arswitch, mdio_driver, mdio_devclass, 0, 0);
1305a043e8c7SAdrian Chadd DRIVER_MODULE(etherswitch, arswitch, etherswitch_driver, etherswitch_devclass, 0, 0);
1306a043e8c7SAdrian Chadd MODULE_VERSION(arswitch, 1);
1307a043e8c7SAdrian Chadd MODULE_DEPEND(arswitch, miibus, 1, 1, 1); /* XXX which versions? */
1308a043e8c7SAdrian Chadd MODULE_DEPEND(arswitch, etherswitch, 1, 1, 1); /* XXX which versions? */
1309