xref: /freebsd/sys/dev/etherswitch/arswitch/arswitch.c (revision 54482989d34c94c6894cb51f65250a4d5946eb1b)
1a043e8c7SAdrian Chadd /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
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 
30a043e8c7SAdrian Chadd #include <sys/param.h>
31a043e8c7SAdrian Chadd #include <sys/bus.h>
32a043e8c7SAdrian Chadd #include <sys/errno.h>
33a043e8c7SAdrian Chadd #include <sys/kernel.h>
34104dc214SGleb Smirnoff #include <sys/malloc.h>
35a043e8c7SAdrian Chadd #include <sys/module.h>
36a043e8c7SAdrian Chadd #include <sys/socket.h>
37a043e8c7SAdrian Chadd #include <sys/sockio.h>
38a043e8c7SAdrian Chadd #include <sys/sysctl.h>
39a043e8c7SAdrian Chadd #include <sys/systm.h>
40a043e8c7SAdrian Chadd 
41a043e8c7SAdrian Chadd #include <net/if.h>
42104dc214SGleb Smirnoff #include <net/if_var.h>
43a043e8c7SAdrian Chadd #include <net/if_arp.h>
44a043e8c7SAdrian Chadd #include <net/ethernet.h>
45a043e8c7SAdrian Chadd #include <net/if_dl.h>
46a043e8c7SAdrian Chadd #include <net/if_media.h>
47a043e8c7SAdrian Chadd #include <net/if_types.h>
48a043e8c7SAdrian Chadd 
49a043e8c7SAdrian Chadd #include <machine/bus.h>
50efce3748SRui Paulo #include <dev/iicbus/iic.h>
51a043e8c7SAdrian Chadd #include <dev/iicbus/iiconf.h>
52a043e8c7SAdrian Chadd #include <dev/iicbus/iicbus.h>
53a043e8c7SAdrian Chadd #include <dev/mii/mii.h>
54a043e8c7SAdrian Chadd #include <dev/mii/miivar.h>
5571e8eac4SAdrian Chadd #include <dev/mdio/mdio.h>
56a043e8c7SAdrian Chadd 
57a043e8c7SAdrian Chadd #include <dev/etherswitch/etherswitch.h>
58a043e8c7SAdrian Chadd 
59a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitchreg.h>
60a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitchvar.h>
61a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_reg.h>
62a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_phy.h>
63b9f07b86SLuiz Otavio O Souza #include <dev/etherswitch/arswitch/arswitch_vlans.h>
64a043e8c7SAdrian Chadd 
65a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_8216.h>
66a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_8226.h>
67a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_8316.h>
68482d268dSAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_8327.h>
69a043e8c7SAdrian Chadd 
70a043e8c7SAdrian Chadd #include "mdio_if.h"
71a043e8c7SAdrian Chadd #include "miibus_if.h"
72a043e8c7SAdrian Chadd #include "etherswitch_if.h"
73a043e8c7SAdrian Chadd 
74c94dc808SAdrian Chadd /* Map ETHERSWITCH_PORT_LED_* to Atheros pattern codes */
75c94dc808SAdrian Chadd static int led_pattern_table[] = {
76c94dc808SAdrian Chadd 	[ETHERSWITCH_PORT_LED_DEFAULT] = 0x3,
77c94dc808SAdrian Chadd 	[ETHERSWITCH_PORT_LED_ON] = 0x2,
78c94dc808SAdrian Chadd 	[ETHERSWITCH_PORT_LED_OFF] = 0x0,
79c94dc808SAdrian Chadd 	[ETHERSWITCH_PORT_LED_BLINK] = 0x1
80c94dc808SAdrian Chadd };
81c94dc808SAdrian Chadd 
82a043e8c7SAdrian Chadd static inline int arswitch_portforphy(int phy);
83a043e8c7SAdrian Chadd static void arswitch_tick(void *arg);
842e6a8c1aSJustin Hibbits static int arswitch_ifmedia_upd(if_t);
852e6a8c1aSJustin Hibbits static void arswitch_ifmedia_sts(if_t, struct ifmediareq *);
86a9ad4222SAdrian Chadd static int ar8xxx_port_vlan_setup(struct arswitch_softc *sc,
87a9ad4222SAdrian Chadd     etherswitch_port_t *p);
88a9ad4222SAdrian Chadd static int ar8xxx_port_vlan_get(struct arswitch_softc *sc,
89a9ad4222SAdrian Chadd     etherswitch_port_t *p);
90c94dc808SAdrian Chadd static int arswitch_setled(struct arswitch_softc *sc, int phy, int led,
91c94dc808SAdrian Chadd     int style);
92a043e8c7SAdrian Chadd 
93a043e8c7SAdrian Chadd static int
94a043e8c7SAdrian Chadd arswitch_probe(device_t dev)
95a043e8c7SAdrian Chadd {
96a043e8c7SAdrian Chadd 	struct arswitch_softc *sc;
97a043e8c7SAdrian Chadd 	uint32_t id;
98*54482989SMark Johnston 	char *chipname;
99a043e8c7SAdrian Chadd 
100a043e8c7SAdrian Chadd 	sc = device_get_softc(dev);
101a043e8c7SAdrian Chadd 	bzero(sc, sizeof(*sc));
102a043e8c7SAdrian Chadd 	sc->page = -1;
10327a2ecaaSAdrian Chadd 
10427a2ecaaSAdrian Chadd 	/* AR8xxx probe */
105a043e8c7SAdrian Chadd 	id = arswitch_readreg(dev, AR8X16_REG_MASK_CTRL);
106dd843f87SAdrian Chadd 	sc->chip_rev = (id & AR8X16_MASK_CTRL_REV_MASK);
107e0bc8f8dSMichael Zhilin 	sc->chip_ver = (id & AR8X16_MASK_CTRL_VER_MASK) >> AR8X16_MASK_CTRL_VER_SHIFT;
108b2152161SAdrian Chadd 	switch (id & (AR8X16_MASK_CTRL_VER_MASK | AR8X16_MASK_CTRL_REV_MASK)) {
109b2152161SAdrian Chadd 	case 0x0101:
110a043e8c7SAdrian Chadd 		chipname = "AR8216";
111a043e8c7SAdrian Chadd 		sc->sc_switchtype = AR8X16_SWITCH_AR8216;
112a043e8c7SAdrian Chadd 		break;
113b2152161SAdrian Chadd 	case 0x0201:
114a043e8c7SAdrian Chadd 		chipname = "AR8226";
115a043e8c7SAdrian Chadd 		sc->sc_switchtype = AR8X16_SWITCH_AR8226;
116a043e8c7SAdrian Chadd 		break;
117b2152161SAdrian Chadd 	/* 0x0301 - AR8236 */
118b2152161SAdrian Chadd 	case 0x1000:
119b2152161SAdrian Chadd 	case 0x1001:
120a043e8c7SAdrian Chadd 		chipname = "AR8316";
121a043e8c7SAdrian Chadd 		sc->sc_switchtype = AR8X16_SWITCH_AR8316;
122a043e8c7SAdrian Chadd 		break;
1230e67bf94SAdrian Chadd 	case 0x1202:
1249682e347SAdrian Chadd 	case 0x1204:
1250e67bf94SAdrian Chadd 		chipname = "AR8327";
1260e67bf94SAdrian Chadd 		sc->sc_switchtype = AR8X16_SWITCH_AR8327;
1270e67bf94SAdrian Chadd 		sc->mii_lo_first = 1;
1280e67bf94SAdrian Chadd 		break;
129a043e8c7SAdrian Chadd 	default:
130a043e8c7SAdrian Chadd 		chipname = NULL;
131a043e8c7SAdrian Chadd 	}
13227a2ecaaSAdrian Chadd 
1331b334c8bSAdrian Chadd 	DPRINTF(sc, ARSWITCH_DBG_ANY, "chipname=%s, id=%08x\n", chipname, id);
134a043e8c7SAdrian Chadd 	if (chipname != NULL) {
135*54482989SMark Johnston 		device_set_descf(dev,
13678549b94SAdrian Chadd 		    "Atheros %s Ethernet Switch (ver %d rev %d)",
137*54482989SMark Johnston 		    chipname, sc->chip_ver, sc->chip_rev);
138a043e8c7SAdrian Chadd 		return (BUS_PROBE_DEFAULT);
139a043e8c7SAdrian Chadd 	}
140a043e8c7SAdrian Chadd 	return (ENXIO);
141a043e8c7SAdrian Chadd }
142a043e8c7SAdrian Chadd 
143a043e8c7SAdrian Chadd static int
144a043e8c7SAdrian Chadd arswitch_attach_phys(struct arswitch_softc *sc)
145a043e8c7SAdrian Chadd {
146a043e8c7SAdrian Chadd 	int phy, err = 0;
147a043e8c7SAdrian Chadd 	char name[IFNAMSIZ];
148a043e8c7SAdrian Chadd 
149a043e8c7SAdrian Chadd 	/* PHYs need an interface, so we generate a dummy one */
150a043e8c7SAdrian Chadd 	snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->sc_dev));
151a043e8c7SAdrian Chadd 	for (phy = 0; phy < sc->numphys; phy++) {
152a043e8c7SAdrian Chadd 		sc->ifp[phy] = if_alloc(IFT_ETHER);
1530774131eSMichael Zhilin 		if (sc->ifp[phy] == NULL) {
1540774131eSMichael Zhilin 			device_printf(sc->sc_dev, "couldn't allocate ifnet structure\n");
1550774131eSMichael Zhilin 			err = ENOMEM;
1560774131eSMichael Zhilin 			break;
1570774131eSMichael Zhilin 		}
1580774131eSMichael Zhilin 
1592e6a8c1aSJustin Hibbits 		if_setsoftc(sc->ifp[phy], sc);
1602e6a8c1aSJustin Hibbits 		if_setflagbits(sc->ifp[phy], IFF_UP | IFF_BROADCAST |
1612e6a8c1aSJustin Hibbits 		    IFF_DRV_RUNNING | IFF_SIMPLEX, 0);
162a043e8c7SAdrian Chadd 		sc->ifname[phy] = malloc(strlen(name)+1, M_DEVBUF, M_WAITOK);
163a043e8c7SAdrian Chadd 		bcopy(name, sc->ifname[phy], strlen(name)+1);
164a043e8c7SAdrian Chadd 		if_initname(sc->ifp[phy], sc->ifname[phy],
165a043e8c7SAdrian Chadd 		    arswitch_portforphy(phy));
166a043e8c7SAdrian Chadd 		err = mii_attach(sc->sc_dev, &sc->miibus[phy], sc->ifp[phy],
167a043e8c7SAdrian Chadd 		    arswitch_ifmedia_upd, arswitch_ifmedia_sts, \
168a043e8c7SAdrian Chadd 		    BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0);
16978549b94SAdrian Chadd #if 0
170a043e8c7SAdrian Chadd 		DPRINTF(sc->sc_dev, "%s attached to pseudo interface %s\n",
171a043e8c7SAdrian Chadd 		    device_get_nameunit(sc->miibus[phy]),
172a043e8c7SAdrian Chadd 		    sc->ifp[phy]->if_xname);
17378549b94SAdrian Chadd #endif
174a043e8c7SAdrian Chadd 		if (err != 0) {
175a043e8c7SAdrian Chadd 			device_printf(sc->sc_dev,
176a043e8c7SAdrian Chadd 			    "attaching PHY %d failed\n",
177a043e8c7SAdrian Chadd 			    phy);
178a043e8c7SAdrian Chadd 			return (err);
179a043e8c7SAdrian Chadd 		}
180a043e8c7SAdrian Chadd 
181c94dc808SAdrian Chadd 		if (AR8X16_IS_SWITCH(sc, AR8327)) {
182c94dc808SAdrian Chadd 			int led;
183c94dc808SAdrian Chadd 			char ledname[IFNAMSIZ+4];
184c94dc808SAdrian Chadd 
185c94dc808SAdrian Chadd 			for (led = 0; led < 3; led++) {
186c94dc808SAdrian Chadd 				sprintf(ledname, "%s%dled%d", name,
187c94dc808SAdrian Chadd 				    arswitch_portforphy(phy), led+1);
188c94dc808SAdrian Chadd 				sc->dev_led[phy][led].sc = sc;
189c94dc808SAdrian Chadd 				sc->dev_led[phy][led].phy = phy;
190c94dc808SAdrian Chadd 				sc->dev_led[phy][led].lednum = led;
191c94dc808SAdrian Chadd 			}
192c94dc808SAdrian Chadd 		}
193c94dc808SAdrian Chadd 	}
194c94dc808SAdrian Chadd 	return (0);
195c94dc808SAdrian Chadd }
196c94dc808SAdrian Chadd 
197a043e8c7SAdrian Chadd static int
198b9f07b86SLuiz Otavio O Souza arswitch_reset(device_t dev)
199b9f07b86SLuiz Otavio O Souza {
200b9f07b86SLuiz Otavio O Souza 
201b9f07b86SLuiz Otavio O Souza 	arswitch_writereg(dev, AR8X16_REG_MASK_CTRL,
202b9f07b86SLuiz Otavio O Souza 	    AR8X16_MASK_CTRL_SOFT_RESET);
203b9f07b86SLuiz Otavio O Souza 	DELAY(1000);
204b9f07b86SLuiz Otavio O Souza 	if (arswitch_readreg(dev, AR8X16_REG_MASK_CTRL) &
205b9f07b86SLuiz Otavio O Souza 	    AR8X16_MASK_CTRL_SOFT_RESET) {
206b9f07b86SLuiz Otavio O Souza 		device_printf(dev, "unable to reset switch\n");
207b9f07b86SLuiz Otavio O Souza 		return (-1);
208b9f07b86SLuiz Otavio O Souza 	}
209b9f07b86SLuiz Otavio O Souza 	return (0);
210b9f07b86SLuiz Otavio O Souza }
211b9f07b86SLuiz Otavio O Souza 
212b9f07b86SLuiz Otavio O Souza static int
213b9f07b86SLuiz Otavio O Souza arswitch_set_vlan_mode(struct arswitch_softc *sc, uint32_t mode)
214b9f07b86SLuiz Otavio O Souza {
215b9f07b86SLuiz Otavio O Souza 
216b9f07b86SLuiz Otavio O Souza 	/* Check for invalid modes. */
217b9f07b86SLuiz Otavio O Souza 	if ((mode & sc->info.es_vlan_caps) != mode)
218b9f07b86SLuiz Otavio O Souza 		return (EINVAL);
219b9f07b86SLuiz Otavio O Souza 
220b9f07b86SLuiz Otavio O Souza 	switch (mode) {
221b9f07b86SLuiz Otavio O Souza 	case ETHERSWITCH_VLAN_DOT1Q:
222b9f07b86SLuiz Otavio O Souza 		sc->vlan_mode = ETHERSWITCH_VLAN_DOT1Q;
223b9f07b86SLuiz Otavio O Souza 		break;
224b9f07b86SLuiz Otavio O Souza 	case ETHERSWITCH_VLAN_PORT:
225b9f07b86SLuiz Otavio O Souza 		sc->vlan_mode = ETHERSWITCH_VLAN_PORT;
226b9f07b86SLuiz Otavio O Souza 		break;
227b9f07b86SLuiz Otavio O Souza 	default:
228b9f07b86SLuiz Otavio O Souza 		sc->vlan_mode = 0;
22974b8d63dSPedro F. Giffuni 	}
230b9f07b86SLuiz Otavio O Souza 
231b9f07b86SLuiz Otavio O Souza 	/* Reset VLANs. */
2326dcbabd7SAdrian Chadd 	sc->hal.arswitch_vlan_init_hw(sc);
233b9f07b86SLuiz Otavio O Souza 
234b9f07b86SLuiz Otavio O Souza 	return (0);
235b9f07b86SLuiz Otavio O Souza }
236b9f07b86SLuiz Otavio O Souza 
237b9f07b86SLuiz Otavio O Souza static void
238a9ad4222SAdrian Chadd ar8xxx_port_init(struct arswitch_softc *sc, int port)
239b9f07b86SLuiz Otavio O Souza {
240b9f07b86SLuiz Otavio O Souza 
241b9f07b86SLuiz Otavio O Souza 	/* Port0 - CPU */
242df892897SAdrian Chadd 	if (port == AR8X16_PORT_CPU) {
243b9f07b86SLuiz Otavio O Souza 		arswitch_writereg(sc->sc_dev, AR8X16_REG_PORT_STS(0),
244b9f07b86SLuiz Otavio O Souza 		    (AR8X16_IS_SWITCH(sc, AR8216) ?
245b9f07b86SLuiz Otavio O Souza 		    AR8X16_PORT_STS_SPEED_100 : AR8X16_PORT_STS_SPEED_1000) |
246b9f07b86SLuiz Otavio O Souza 		    (AR8X16_IS_SWITCH(sc, AR8216) ? 0 : AR8X16_PORT_STS_RXFLOW) |
247b9f07b86SLuiz Otavio O Souza 		    (AR8X16_IS_SWITCH(sc, AR8216) ? 0 : AR8X16_PORT_STS_TXFLOW) |
248b9f07b86SLuiz Otavio O Souza 		    AR8X16_PORT_STS_RXMAC |
249b9f07b86SLuiz Otavio O Souza 		    AR8X16_PORT_STS_TXMAC |
250b9f07b86SLuiz Otavio O Souza 		    AR8X16_PORT_STS_DUPLEX);
251b9f07b86SLuiz Otavio O Souza 		arswitch_writereg(sc->sc_dev, AR8X16_REG_PORT_CTRL(0),
252b9f07b86SLuiz Otavio O Souza 		    arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_CTRL(0)) &
253b9f07b86SLuiz Otavio O Souza 		    ~AR8X16_PORT_CTRL_HEADER);
254df892897SAdrian Chadd 	} else {
255b9f07b86SLuiz Otavio O Souza 		/* Set ports to auto negotiation. */
256b9f07b86SLuiz Otavio O Souza 		arswitch_writereg(sc->sc_dev, AR8X16_REG_PORT_STS(port),
257b9f07b86SLuiz Otavio O Souza 		    AR8X16_PORT_STS_LINK_AUTO);
258b9f07b86SLuiz Otavio O Souza 		arswitch_writereg(sc->sc_dev, AR8X16_REG_PORT_CTRL(port),
259b9f07b86SLuiz Otavio O Souza 		    arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_CTRL(port)) &
260b9f07b86SLuiz Otavio O Souza 		    ~AR8X16_PORT_CTRL_HEADER);
261b9f07b86SLuiz Otavio O Souza 	}
262b9f07b86SLuiz Otavio O Souza }
263b9f07b86SLuiz Otavio O Souza 
264b9f07b86SLuiz Otavio O Souza static int
26562042c97SAdrian Chadd ar8xxx_atu_wait_ready(struct arswitch_softc *sc)
2664ff2f60dSAdrian Chadd {
2674ff2f60dSAdrian Chadd 	int ret;
2684ff2f60dSAdrian Chadd 
26962042c97SAdrian Chadd 	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
27062042c97SAdrian Chadd 
2714ff2f60dSAdrian Chadd 	ret = arswitch_waitreg(sc->sc_dev,
2724ff2f60dSAdrian Chadd 	    AR8216_REG_ATU,
2734ff2f60dSAdrian Chadd 	    AR8216_ATU_ACTIVE,
2744ff2f60dSAdrian Chadd 	    0,
2754ff2f60dSAdrian Chadd 	    1000);
2764ff2f60dSAdrian Chadd 
27762042c97SAdrian Chadd 	return (ret);
27862042c97SAdrian Chadd }
27962042c97SAdrian Chadd 
28062042c97SAdrian Chadd /*
28162042c97SAdrian Chadd  * Flush all ATU entries.
28262042c97SAdrian Chadd  */
28362042c97SAdrian Chadd static int
28462042c97SAdrian Chadd ar8xxx_atu_flush(struct arswitch_softc *sc)
28562042c97SAdrian Chadd {
28662042c97SAdrian Chadd 	int ret;
28762042c97SAdrian Chadd 
28862042c97SAdrian Chadd 	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
28962042c97SAdrian Chadd 
29062042c97SAdrian Chadd 	DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: flushing all ports\n", __func__);
29162042c97SAdrian Chadd 
29262042c97SAdrian Chadd 	ret = ar8xxx_atu_wait_ready(sc);
2934ff2f60dSAdrian Chadd 	if (ret)
2944ff2f60dSAdrian Chadd 		device_printf(sc->sc_dev, "%s: waitreg failed\n", __func__);
2954ff2f60dSAdrian Chadd 
2964ff2f60dSAdrian Chadd 	if (!ret)
2974ff2f60dSAdrian Chadd 		arswitch_writereg(sc->sc_dev,
2984ff2f60dSAdrian Chadd 		    AR8216_REG_ATU,
2992c6ceccaSAdrian Chadd 		    AR8216_ATU_OP_FLUSH | AR8216_ATU_ACTIVE);
3004ff2f60dSAdrian Chadd 
3014ff2f60dSAdrian Chadd 	return (ret);
3024ff2f60dSAdrian Chadd }
3034ff2f60dSAdrian Chadd 
30462042c97SAdrian Chadd /*
30562042c97SAdrian Chadd  * Flush ATU entries for a single port.
30662042c97SAdrian Chadd  */
30762042c97SAdrian Chadd static int
30862042c97SAdrian Chadd ar8xxx_atu_flush_port(struct arswitch_softc *sc, int port)
30962042c97SAdrian Chadd {
31062042c97SAdrian Chadd 	int ret, val;
31162042c97SAdrian Chadd 
31262042c97SAdrian Chadd 	DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: flushing port %d\n", __func__,
31362042c97SAdrian Chadd 	    port);
31462042c97SAdrian Chadd 
31562042c97SAdrian Chadd 	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
31662042c97SAdrian Chadd 
31762042c97SAdrian Chadd 	/* Flush unicast entries on port */
31862042c97SAdrian Chadd 	val = AR8216_ATU_OP_FLUSH_UNICAST;
31962042c97SAdrian Chadd 
32062042c97SAdrian Chadd 	/* TODO: bit 4 indicates whether to flush dynamic (0) or static (1) */
32162042c97SAdrian Chadd 
32262042c97SAdrian Chadd 	/* Which port */
32362042c97SAdrian Chadd 	val |= SM(port, AR8216_ATU_PORT_NUM);
32462042c97SAdrian Chadd 
32562042c97SAdrian Chadd 	ret = ar8xxx_atu_wait_ready(sc);
32662042c97SAdrian Chadd 	if (ret)
32762042c97SAdrian Chadd 		device_printf(sc->sc_dev, "%s: waitreg failed\n", __func__);
32862042c97SAdrian Chadd 
32962042c97SAdrian Chadd 	if (!ret)
33062042c97SAdrian Chadd 		arswitch_writereg(sc->sc_dev,
33162042c97SAdrian Chadd 		    AR8216_REG_ATU,
33262042c97SAdrian Chadd 		    val | AR8216_ATU_ACTIVE);
33362042c97SAdrian Chadd 
33462042c97SAdrian Chadd 	return (ret);
33562042c97SAdrian Chadd }
33662042c97SAdrian Chadd 
33762042c97SAdrian Chadd /*
33862042c97SAdrian Chadd  * XXX TODO: flush a single MAC address.
33962042c97SAdrian Chadd  */
34062042c97SAdrian Chadd 
34162042c97SAdrian Chadd /*
34262042c97SAdrian Chadd  * Fetch a single entry from the ATU.
34362042c97SAdrian Chadd  */
34462042c97SAdrian Chadd static int
34562042c97SAdrian Chadd ar8xxx_atu_fetch_table(struct arswitch_softc *sc, etherswitch_atu_entry_t *e,
34662042c97SAdrian Chadd     int atu_fetch_op)
34762042c97SAdrian Chadd {
34862042c97SAdrian Chadd 	uint32_t ret0, ret1, ret2, val;
34962042c97SAdrian Chadd 
35062042c97SAdrian Chadd 	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
35162042c97SAdrian Chadd 
35262042c97SAdrian Chadd 	switch (atu_fetch_op) {
35362042c97SAdrian Chadd 	case 0:
35462042c97SAdrian Chadd 		/* Initialise things for the first fetch */
35562042c97SAdrian Chadd 
35662042c97SAdrian Chadd 		DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: initializing\n", __func__);
35762042c97SAdrian Chadd 		(void) ar8xxx_atu_wait_ready(sc);
35862042c97SAdrian Chadd 
35962042c97SAdrian Chadd 		arswitch_writereg(sc->sc_dev,
36062042c97SAdrian Chadd 		    AR8216_REG_ATU, AR8216_ATU_OP_GET_NEXT);
36162042c97SAdrian Chadd 		arswitch_writereg(sc->sc_dev,
36262042c97SAdrian Chadd 		    AR8216_REG_ATU_DATA, 0);
36362042c97SAdrian Chadd 		arswitch_writereg(sc->sc_dev,
36462042c97SAdrian Chadd 		    AR8216_REG_ATU_CTRL2, 0);
36562042c97SAdrian Chadd 
36662042c97SAdrian Chadd 		return (0);
36762042c97SAdrian Chadd 	case 1:
36862042c97SAdrian Chadd 		DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: reading next\n", __func__);
36962042c97SAdrian Chadd 		/*
37062042c97SAdrian Chadd 		 * Attempt to read the next address entry; don't modify what
37162042c97SAdrian Chadd 		 * is there in AT_ADDR{4,5} as its used for the next fetch
37262042c97SAdrian Chadd 		 */
37362042c97SAdrian Chadd 		(void) ar8xxx_atu_wait_ready(sc);
37462042c97SAdrian Chadd 
37562042c97SAdrian Chadd 		/* Begin the next read event; not modifying anything */
37662042c97SAdrian Chadd 		val = arswitch_readreg(sc->sc_dev, AR8216_REG_ATU);
37762042c97SAdrian Chadd 		val |= AR8216_ATU_ACTIVE;
37862042c97SAdrian Chadd 		arswitch_writereg(sc->sc_dev, AR8216_REG_ATU, val);
37962042c97SAdrian Chadd 
38062042c97SAdrian Chadd 		/* Wait for it to complete */
38162042c97SAdrian Chadd 		(void) ar8xxx_atu_wait_ready(sc);
38262042c97SAdrian Chadd 
38362042c97SAdrian Chadd 		/* Fetch the ethernet address and ATU status */
38462042c97SAdrian Chadd 		ret0 = arswitch_readreg(sc->sc_dev, AR8216_REG_ATU);
38562042c97SAdrian Chadd 		ret1 = arswitch_readreg(sc->sc_dev, AR8216_REG_ATU_DATA);
38662042c97SAdrian Chadd 		ret2 = arswitch_readreg(sc->sc_dev, AR8216_REG_ATU_CTRL2);
38762042c97SAdrian Chadd 
38862042c97SAdrian Chadd 		/* If the status is zero, then we're done */
38962042c97SAdrian Chadd 		if (MS(ret2, AR8216_ATU_CTRL2_AT_STATUS) == 0)
39062042c97SAdrian Chadd 			return (-1);
39162042c97SAdrian Chadd 
39262042c97SAdrian Chadd 		/* MAC address */
39362042c97SAdrian Chadd 		e->es_macaddr[5] = MS(ret0, AR8216_ATU_ADDR5);
39462042c97SAdrian Chadd 		e->es_macaddr[4] = MS(ret0, AR8216_ATU_ADDR4);
39562042c97SAdrian Chadd 		e->es_macaddr[3] = MS(ret1, AR8216_ATU_ADDR3);
39662042c97SAdrian Chadd 		e->es_macaddr[2] = MS(ret1, AR8216_ATU_ADDR2);
39762042c97SAdrian Chadd 		e->es_macaddr[1] = MS(ret1, AR8216_ATU_ADDR1);
39862042c97SAdrian Chadd 		e->es_macaddr[0] = MS(ret1, AR8216_ATU_ADDR0);
39962042c97SAdrian Chadd 
40062042c97SAdrian Chadd 		/* Bitmask of ports this entry is for */
40162042c97SAdrian Chadd 		e->es_portmask = MS(ret2, AR8216_ATU_CTRL2_DESPORT);
40262042c97SAdrian Chadd 
40362042c97SAdrian Chadd 		/* TODO: other flags that are interesting */
40462042c97SAdrian Chadd 
40562042c97SAdrian Chadd 		DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: MAC %6D portmask 0x%08x\n",
40662042c97SAdrian Chadd 		    __func__,
40762042c97SAdrian Chadd 		    e->es_macaddr, ":", e->es_portmask);
40862042c97SAdrian Chadd 		return (0);
40962042c97SAdrian Chadd 	default:
41062042c97SAdrian Chadd 		return (-1);
41162042c97SAdrian Chadd 	}
41262042c97SAdrian Chadd 	return (-1);
41362042c97SAdrian Chadd }
41462042c97SAdrian Chadd 
41562042c97SAdrian Chadd /*
41662042c97SAdrian Chadd  * Configure aging register defaults.
41762042c97SAdrian Chadd  */
41862042c97SAdrian Chadd static int
41962042c97SAdrian Chadd ar8xxx_atu_learn_default(struct arswitch_softc *sc)
42062042c97SAdrian Chadd {
42162042c97SAdrian Chadd 	int ret;
42262042c97SAdrian Chadd 	uint32_t val;
42362042c97SAdrian Chadd 
42462042c97SAdrian Chadd 	DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: resetting learning\n", __func__);
42562042c97SAdrian Chadd 
42662042c97SAdrian Chadd 	/*
42762042c97SAdrian Chadd 	 * For now, configure the aging defaults:
42862042c97SAdrian Chadd 	 *
42962042c97SAdrian Chadd 	 * + ARP_EN - enable "acknowledgement" of ARP frames - they are
43062042c97SAdrian Chadd 	 *   forwarded to the CPU port
43162042c97SAdrian Chadd 	 * + LEARN_CHANGE_EN - hash table violations when learning MAC addresses
43262042c97SAdrian Chadd 	 *   will force an entry to be expired/updated and a new one to be
43362042c97SAdrian Chadd 	 *   programmed in.
43462042c97SAdrian Chadd 	 * + AGE_EN - enable address table aging
43562042c97SAdrian Chadd 	 * + AGE_TIME - set to 5 minutes
43662042c97SAdrian Chadd 	 */
43762042c97SAdrian Chadd 	val = 0;
43862042c97SAdrian Chadd 	val |= AR8216_ATU_CTRL_ARP_EN;
43962042c97SAdrian Chadd 	val |= AR8216_ATU_CTRL_LEARN_CHANGE;
44062042c97SAdrian Chadd 	val |= AR8216_ATU_CTRL_AGE_EN;
44162042c97SAdrian Chadd 	val |= 0x2b;	/* 5 minutes; bits 15:0 */
44262042c97SAdrian Chadd 
44362042c97SAdrian Chadd 	ret = arswitch_writereg(sc->sc_dev,
44462042c97SAdrian Chadd 	    AR8216_REG_ATU_CTRL,
44562042c97SAdrian Chadd 	    val);
44662042c97SAdrian Chadd 
44762042c97SAdrian Chadd 	if (ret)
44862042c97SAdrian Chadd 		device_printf(sc->sc_dev, "%s: writereg failed\n", __func__);
44962042c97SAdrian Chadd 
45062042c97SAdrian Chadd 	return (ret);
45162042c97SAdrian Chadd }
45262042c97SAdrian Chadd 
45362042c97SAdrian Chadd /*
45462042c97SAdrian Chadd  * XXX TODO: add another routine to configure the leaky behaviour
45562042c97SAdrian Chadd  * when unknown frames are received.  These must be consistent
45662042c97SAdrian Chadd  * between ethernet switches.
45762042c97SAdrian Chadd  */
45862042c97SAdrian Chadd 
45962042c97SAdrian Chadd /*
4602ba4bf8fSAdrian Chadd  * Fetch the configured switch MAC address.
4612ba4bf8fSAdrian Chadd  */
4622ba4bf8fSAdrian Chadd static int
4632ba4bf8fSAdrian Chadd ar8xxx_hw_get_switch_macaddr(struct arswitch_softc *sc, struct ether_addr *ea)
4642ba4bf8fSAdrian Chadd {
4652ba4bf8fSAdrian Chadd 	uint32_t ret0, ret1;
4662ba4bf8fSAdrian Chadd 	char *s;
4672ba4bf8fSAdrian Chadd 
4682ba4bf8fSAdrian Chadd 	s = (void *) ea;
4692ba4bf8fSAdrian Chadd 
4702ba4bf8fSAdrian Chadd 	ret0 = arswitch_readreg(sc->sc_dev, AR8X16_REG_SW_MAC_ADDR0);
4712ba4bf8fSAdrian Chadd 	ret1 = arswitch_readreg(sc->sc_dev, AR8X16_REG_SW_MAC_ADDR1);
4722ba4bf8fSAdrian Chadd 
4732ba4bf8fSAdrian Chadd 	s[5] = MS(ret0, AR8X16_REG_SW_MAC_ADDR0_BYTE5);
4742ba4bf8fSAdrian Chadd 	s[4] = MS(ret0, AR8X16_REG_SW_MAC_ADDR0_BYTE4);
4752ba4bf8fSAdrian Chadd 	s[3] = MS(ret1, AR8X16_REG_SW_MAC_ADDR1_BYTE3);
4762ba4bf8fSAdrian Chadd 	s[2] = MS(ret1, AR8X16_REG_SW_MAC_ADDR1_BYTE2);
4772ba4bf8fSAdrian Chadd 	s[1] = MS(ret1, AR8X16_REG_SW_MAC_ADDR1_BYTE1);
4782ba4bf8fSAdrian Chadd 	s[0] = MS(ret1, AR8X16_REG_SW_MAC_ADDR1_BYTE0);
4792ba4bf8fSAdrian Chadd 
4802ba4bf8fSAdrian Chadd 	return (0);
4812ba4bf8fSAdrian Chadd }
4822ba4bf8fSAdrian Chadd 
4832ba4bf8fSAdrian Chadd /*
4842ba4bf8fSAdrian Chadd  * Set the switch mac address.
4852ba4bf8fSAdrian Chadd  */
4862ba4bf8fSAdrian Chadd static int
4872ba4bf8fSAdrian Chadd ar8xxx_hw_set_switch_macaddr(struct arswitch_softc *sc,
4882ba4bf8fSAdrian Chadd     const struct ether_addr *ea)
4892ba4bf8fSAdrian Chadd {
4902ba4bf8fSAdrian Chadd 
4912ba4bf8fSAdrian Chadd 	return (ENXIO);
4922ba4bf8fSAdrian Chadd }
4932ba4bf8fSAdrian Chadd 
4942ba4bf8fSAdrian Chadd /*
49562042c97SAdrian Chadd  * XXX TODO: this attach routine does NOT free all memory, resources
49662042c97SAdrian Chadd  * upon failure!
49762042c97SAdrian Chadd  */
4984ff2f60dSAdrian Chadd static int
499a043e8c7SAdrian Chadd arswitch_attach(device_t dev)
500a043e8c7SAdrian Chadd {
5011b334c8bSAdrian Chadd 	struct arswitch_softc *sc = device_get_softc(dev);
5021b334c8bSAdrian Chadd 	struct sysctl_ctx_list *ctx;
5031b334c8bSAdrian Chadd 	struct sysctl_oid *tree;
504a043e8c7SAdrian Chadd 	int err = 0;
505df892897SAdrian Chadd 	int port;
506a043e8c7SAdrian Chadd 
507a043e8c7SAdrian Chadd 	/* sc->sc_switchtype is already decided in arswitch_probe() */
508a043e8c7SAdrian Chadd 	sc->sc_dev = dev;
509a043e8c7SAdrian Chadd 	mtx_init(&sc->sc_mtx, "arswitch", NULL, MTX_DEF);
510a043e8c7SAdrian Chadd 	sc->page = -1;
511a043e8c7SAdrian Chadd 	strlcpy(sc->info.es_name, device_get_desc(dev),
512a043e8c7SAdrian Chadd 	    sizeof(sc->info.es_name));
513a043e8c7SAdrian Chadd 
5141b334c8bSAdrian Chadd 	/* Debugging */
5151b334c8bSAdrian Chadd 	ctx = device_get_sysctl_ctx(sc->sc_dev);
5161b334c8bSAdrian Chadd 	tree = device_get_sysctl_tree(sc->sc_dev);
5171b334c8bSAdrian Chadd 	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
5181b334c8bSAdrian Chadd 	    "debug", CTLFLAG_RW, &sc->sc_debug, 0,
5191b334c8bSAdrian Chadd 	    "control debugging printfs");
5201b334c8bSAdrian Chadd 
52162042c97SAdrian Chadd 	/* Allocate a 128 entry ATU table; hopefully its big enough! */
52262042c97SAdrian Chadd 	/* XXX TODO: make this per chip */
52362042c97SAdrian Chadd 	sc->atu.entries = malloc(sizeof(etherswitch_atu_entry_t) * 128,
52462042c97SAdrian Chadd 	    M_DEVBUF, M_NOWAIT);
52562042c97SAdrian Chadd 	if (sc->atu.entries == NULL) {
52662042c97SAdrian Chadd 		device_printf(sc->sc_dev, "%s: failed to allocate ATU table\n",
52762042c97SAdrian Chadd 		    __func__);
52862042c97SAdrian Chadd 		return (ENXIO);
52962042c97SAdrian Chadd 	}
53062042c97SAdrian Chadd 	sc->atu.count = 0;
53162042c97SAdrian Chadd 	sc->atu.size = 128;
53262042c97SAdrian Chadd 
533ddbc4420SAdrian Chadd 	/* Default HAL methods */
534a9ad4222SAdrian Chadd 	sc->hal.arswitch_port_init = ar8xxx_port_init;
535a9ad4222SAdrian Chadd 	sc->hal.arswitch_port_vlan_setup = ar8xxx_port_vlan_setup;
536a9ad4222SAdrian Chadd 	sc->hal.arswitch_port_vlan_get = ar8xxx_port_vlan_get;
5376dcbabd7SAdrian Chadd 	sc->hal.arswitch_vlan_init_hw = ar8xxx_reset_vlans;
5382ba4bf8fSAdrian Chadd 	sc->hal.arswitch_hw_get_switch_macaddr = ar8xxx_hw_get_switch_macaddr;
5392ba4bf8fSAdrian Chadd 	sc->hal.arswitch_hw_set_switch_macaddr = ar8xxx_hw_set_switch_macaddr;
540749cac13SAdrian Chadd 
5416dcbabd7SAdrian Chadd 	sc->hal.arswitch_vlan_getvgroup = ar8xxx_getvgroup;
5426dcbabd7SAdrian Chadd 	sc->hal.arswitch_vlan_setvgroup = ar8xxx_setvgroup;
543749cac13SAdrian Chadd 
5446dcbabd7SAdrian Chadd 	sc->hal.arswitch_vlan_get_pvid = ar8xxx_get_pvid;
5456dcbabd7SAdrian Chadd 	sc->hal.arswitch_vlan_set_pvid = ar8xxx_set_pvid;
546749cac13SAdrian Chadd 
547749cac13SAdrian Chadd 	sc->hal.arswitch_get_dot1q_vlan = ar8xxx_get_dot1q_vlan;
548749cac13SAdrian Chadd 	sc->hal.arswitch_set_dot1q_vlan = ar8xxx_set_dot1q_vlan;
549f35f94f4SAdrian Chadd 	sc->hal.arswitch_flush_dot1q_vlan = ar8xxx_flush_dot1q_vlan;
550f35f94f4SAdrian Chadd 	sc->hal.arswitch_purge_dot1q_vlan = ar8xxx_purge_dot1q_vlan;
551749cac13SAdrian Chadd 	sc->hal.arswitch_get_port_vlan = ar8xxx_get_port_vlan;
552749cac13SAdrian Chadd 	sc->hal.arswitch_set_port_vlan = ar8xxx_set_port_vlan;
553749cac13SAdrian Chadd 
5544ff2f60dSAdrian Chadd 	sc->hal.arswitch_atu_flush = ar8xxx_atu_flush;
55562042c97SAdrian Chadd 	sc->hal.arswitch_atu_flush_port = ar8xxx_atu_flush_port;
55662042c97SAdrian Chadd 	sc->hal.arswitch_atu_learn_default = ar8xxx_atu_learn_default;
55762042c97SAdrian Chadd 	sc->hal.arswitch_atu_fetch_table = ar8xxx_atu_fetch_table;
558749cac13SAdrian Chadd 
55978549b94SAdrian Chadd 	sc->hal.arswitch_phy_read = arswitch_readphy_internal;
56078549b94SAdrian Chadd 	sc->hal.arswitch_phy_write = arswitch_writephy_internal;
561ddbc4420SAdrian Chadd 
562a043e8c7SAdrian Chadd 	/*
563a043e8c7SAdrian Chadd 	 * Attach switch related functions
564a043e8c7SAdrian Chadd 	 */
5658f5dbc22SMarius Strobl 	if (AR8X16_IS_SWITCH(sc, AR8216))
566a043e8c7SAdrian Chadd 		ar8216_attach(sc);
567a043e8c7SAdrian Chadd 	else if (AR8X16_IS_SWITCH(sc, AR8226))
568a043e8c7SAdrian Chadd 		ar8226_attach(sc);
569a043e8c7SAdrian Chadd 	else if (AR8X16_IS_SWITCH(sc, AR8316))
570a043e8c7SAdrian Chadd 		ar8316_attach(sc);
571482d268dSAdrian Chadd 	else if (AR8X16_IS_SWITCH(sc, AR8327))
572482d268dSAdrian Chadd 		ar8327_attach(sc);
57378549b94SAdrian Chadd 	else {
5741b334c8bSAdrian Chadd 		DPRINTF(sc, ARSWITCH_DBG_ANY,
5751b334c8bSAdrian Chadd 		    "%s: unknown switch (%d)?\n", __func__, sc->sc_switchtype);
576a043e8c7SAdrian Chadd 		return (ENXIO);
57778549b94SAdrian Chadd 	}
578a043e8c7SAdrian Chadd 
579b9f07b86SLuiz Otavio O Souza 	/* Common defaults. */
580a043e8c7SAdrian Chadd 	sc->info.es_nports = 5; /* XXX technically 6, but 6th not used */
581a043e8c7SAdrian Chadd 
582a043e8c7SAdrian Chadd 	/* XXX Defaults for externally connected AR8316 */
583a043e8c7SAdrian Chadd 	sc->numphys = 4;
584a043e8c7SAdrian Chadd 	sc->phy4cpu = 1;
585a043e8c7SAdrian Chadd 	sc->is_rgmii = 1;
586a043e8c7SAdrian Chadd 	sc->is_gmii = 0;
587b2152161SAdrian Chadd 	sc->is_mii = 0;
588a043e8c7SAdrian Chadd 
589a043e8c7SAdrian Chadd 	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
590a043e8c7SAdrian Chadd 	    "numphys", &sc->numphys);
591a043e8c7SAdrian Chadd 	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
592a043e8c7SAdrian Chadd 	    "phy4cpu", &sc->phy4cpu);
593a043e8c7SAdrian Chadd 	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
594a043e8c7SAdrian Chadd 	    "is_rgmii", &sc->is_rgmii);
595a043e8c7SAdrian Chadd 	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
596a043e8c7SAdrian Chadd 	    "is_gmii", &sc->is_gmii);
597b2152161SAdrian Chadd 	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
598b2152161SAdrian Chadd 	    "is_mii", &sc->is_mii);
599a043e8c7SAdrian Chadd 
600b9f07b86SLuiz Otavio O Souza 	if (sc->numphys > AR8X16_NUM_PHYS)
601b9f07b86SLuiz Otavio O Souza 		sc->numphys = AR8X16_NUM_PHYS;
602a043e8c7SAdrian Chadd 
603b9f07b86SLuiz Otavio O Souza 	/* Reset the switch. */
60478549b94SAdrian Chadd 	if (arswitch_reset(dev)) {
6051b334c8bSAdrian Chadd 		DPRINTF(sc, ARSWITCH_DBG_ANY,
6061b334c8bSAdrian Chadd 		    "%s: arswitch_reset: failed\n", __func__);
607b9f07b86SLuiz Otavio O Souza 		return (ENXIO);
60878549b94SAdrian Chadd 	}
609a043e8c7SAdrian Chadd 
610b2152161SAdrian Chadd 	err = sc->hal.arswitch_hw_setup(sc);
6111b334c8bSAdrian Chadd 	if (err != 0) {
6121b334c8bSAdrian Chadd 		DPRINTF(sc, ARSWITCH_DBG_ANY,
6131b334c8bSAdrian Chadd 		    "%s: hw_setup: err=%d\n", __func__, err);
614b2152161SAdrian Chadd 		return (err);
6151b334c8bSAdrian Chadd 	}
616b2152161SAdrian Chadd 
617a043e8c7SAdrian Chadd 	err = sc->hal.arswitch_hw_global_setup(sc);
6181b334c8bSAdrian Chadd 	if (err != 0) {
6191b334c8bSAdrian Chadd 		DPRINTF(sc, ARSWITCH_DBG_ANY,
6201b334c8bSAdrian Chadd 		    "%s: hw_global_setup: err=%d\n", __func__, err);
621a043e8c7SAdrian Chadd 		return (err);
6221b334c8bSAdrian Chadd 	}
623a043e8c7SAdrian Chadd 
62462042c97SAdrian Chadd 	/*
62562042c97SAdrian Chadd 	 * Configure the default address table learning parameters for this
62662042c97SAdrian Chadd 	 * switch.
62762042c97SAdrian Chadd 	 */
62862042c97SAdrian Chadd 	err = sc->hal.arswitch_atu_learn_default(sc);
62962042c97SAdrian Chadd 	if (err != 0) {
63062042c97SAdrian Chadd 		DPRINTF(sc, ARSWITCH_DBG_ANY,
63162042c97SAdrian Chadd 		    "%s: atu_learn_default: err=%d\n", __func__, err);
63262042c97SAdrian Chadd 		return (err);
63362042c97SAdrian Chadd 	}
63462042c97SAdrian Chadd 
635b9f07b86SLuiz Otavio O Souza 	/* Initialize the switch ports. */
636df892897SAdrian Chadd 	for (port = 0; port <= sc->numphys; port++) {
637ddbc4420SAdrian Chadd 		sc->hal.arswitch_port_init(sc, port);
638df892897SAdrian Chadd 	}
639b9f07b86SLuiz Otavio O Souza 
640a043e8c7SAdrian Chadd 	/*
641a043e8c7SAdrian Chadd 	 * Attach the PHYs and complete the bus enumeration.
642a043e8c7SAdrian Chadd 	 */
643a043e8c7SAdrian Chadd 	err = arswitch_attach_phys(sc);
6441b334c8bSAdrian Chadd 	if (err != 0) {
6451b334c8bSAdrian Chadd 		DPRINTF(sc, ARSWITCH_DBG_ANY,
6461b334c8bSAdrian Chadd 		    "%s: attach_phys: err=%d\n", __func__, err);
647a043e8c7SAdrian Chadd 		return (err);
6481b334c8bSAdrian Chadd 	}
649a043e8c7SAdrian Chadd 
650b9f07b86SLuiz Otavio O Souza 	/* Default to ingress filters off. */
651b9f07b86SLuiz Otavio O Souza 	err = arswitch_set_vlan_mode(sc, 0);
6521b334c8bSAdrian Chadd 	if (err != 0) {
6531b334c8bSAdrian Chadd 		DPRINTF(sc, ARSWITCH_DBG_ANY,
6541b334c8bSAdrian Chadd 		    "%s: set_vlan_mode: err=%d\n", __func__, err);
655b9f07b86SLuiz Otavio O Souza 		return (err);
6561b334c8bSAdrian Chadd 	}
657b9f07b86SLuiz Otavio O Souza 
658a043e8c7SAdrian Chadd 	bus_generic_probe(dev);
659a043e8c7SAdrian Chadd 	bus_enumerate_hinted_children(dev);
660a043e8c7SAdrian Chadd 	err = bus_generic_attach(dev);
6611b334c8bSAdrian Chadd 	if (err != 0) {
6621b334c8bSAdrian Chadd 		DPRINTF(sc, ARSWITCH_DBG_ANY,
6631b334c8bSAdrian Chadd 		    "%s: bus_generic_attach: err=%d\n", __func__, err);
664a043e8c7SAdrian Chadd 		return (err);
6651b334c8bSAdrian Chadd 	}
666a043e8c7SAdrian Chadd 
667a043e8c7SAdrian Chadd 	callout_init_mtx(&sc->callout_tick, &sc->sc_mtx, 0);
668454d507aSAleksandr Rybalko 
669454d507aSAleksandr Rybalko 	ARSWITCH_LOCK(sc);
670a043e8c7SAdrian Chadd 	arswitch_tick(sc);
671454d507aSAleksandr Rybalko 	ARSWITCH_UNLOCK(sc);
672a043e8c7SAdrian Chadd 
673a043e8c7SAdrian Chadd 	return (err);
674a043e8c7SAdrian Chadd }
675a043e8c7SAdrian Chadd 
676a043e8c7SAdrian Chadd static int
677a043e8c7SAdrian Chadd arswitch_detach(device_t dev)
678a043e8c7SAdrian Chadd {
679a043e8c7SAdrian Chadd 	struct arswitch_softc *sc = device_get_softc(dev);
680a043e8c7SAdrian Chadd 	int i;
681a043e8c7SAdrian Chadd 
682a043e8c7SAdrian Chadd 	callout_drain(&sc->callout_tick);
683a043e8c7SAdrian Chadd 
684a043e8c7SAdrian Chadd 	for (i=0; i < sc->numphys; i++) {
685a043e8c7SAdrian Chadd 		if (sc->miibus[i] != NULL)
686a043e8c7SAdrian Chadd 			device_delete_child(dev, sc->miibus[i]);
687a043e8c7SAdrian Chadd 		if (sc->ifp[i] != NULL)
688a043e8c7SAdrian Chadd 			if_free(sc->ifp[i]);
689a043e8c7SAdrian Chadd 		free(sc->ifname[i], M_DEVBUF);
690a043e8c7SAdrian Chadd 	}
691a043e8c7SAdrian Chadd 
69262042c97SAdrian Chadd 	free(sc->atu.entries, M_DEVBUF);
69362042c97SAdrian Chadd 
694a043e8c7SAdrian Chadd 	bus_generic_detach(dev);
695a043e8c7SAdrian Chadd 	mtx_destroy(&sc->sc_mtx);
696a043e8c7SAdrian Chadd 
697a043e8c7SAdrian Chadd 	return (0);
698a043e8c7SAdrian Chadd }
699a043e8c7SAdrian Chadd 
700a043e8c7SAdrian Chadd /*
701a043e8c7SAdrian Chadd  * Convert PHY number to port number. PHY0 is connected to port 1, PHY1 to
702a043e8c7SAdrian Chadd  * port 2, etc.
703a043e8c7SAdrian Chadd  */
704a043e8c7SAdrian Chadd static inline int
705a043e8c7SAdrian Chadd arswitch_portforphy(int phy)
706a043e8c7SAdrian Chadd {
707a043e8c7SAdrian Chadd 	return (phy+1);
708a043e8c7SAdrian Chadd }
709a043e8c7SAdrian Chadd 
710a043e8c7SAdrian Chadd static inline struct mii_data *
711a043e8c7SAdrian Chadd arswitch_miiforport(struct arswitch_softc *sc, int port)
712a043e8c7SAdrian Chadd {
713a043e8c7SAdrian Chadd 	int phy = port-1;
714a043e8c7SAdrian Chadd 
715a043e8c7SAdrian Chadd 	if (phy < 0 || phy >= sc->numphys)
716a043e8c7SAdrian Chadd 		return (NULL);
717a043e8c7SAdrian Chadd 	return (device_get_softc(sc->miibus[phy]));
718a043e8c7SAdrian Chadd }
719a043e8c7SAdrian Chadd 
7202e6a8c1aSJustin Hibbits static inline if_t
721a043e8c7SAdrian Chadd arswitch_ifpforport(struct arswitch_softc *sc, int port)
722a043e8c7SAdrian Chadd {
723a043e8c7SAdrian Chadd 	int phy = port-1;
724a043e8c7SAdrian Chadd 
725a043e8c7SAdrian Chadd 	if (phy < 0 || phy >= sc->numphys)
726a043e8c7SAdrian Chadd 		return (NULL);
727a043e8c7SAdrian Chadd 	return (sc->ifp[phy]);
728a043e8c7SAdrian Chadd }
729a043e8c7SAdrian Chadd 
730a043e8c7SAdrian Chadd /*
731a043e8c7SAdrian Chadd  * Convert port status to ifmedia.
732a043e8c7SAdrian Chadd  */
733a043e8c7SAdrian Chadd static void
734a043e8c7SAdrian Chadd arswitch_update_ifmedia(int portstatus, u_int *media_status, u_int *media_active)
735a043e8c7SAdrian Chadd {
736a043e8c7SAdrian Chadd 	*media_active = IFM_ETHER;
737a043e8c7SAdrian Chadd 	*media_status = IFM_AVALID;
738a043e8c7SAdrian Chadd 
739a043e8c7SAdrian Chadd 	if ((portstatus & AR8X16_PORT_STS_LINK_UP) != 0)
740a043e8c7SAdrian Chadd 		*media_status |= IFM_ACTIVE;
741a043e8c7SAdrian Chadd 	else {
742a043e8c7SAdrian Chadd 		*media_active |= IFM_NONE;
743a043e8c7SAdrian Chadd 		return;
744a043e8c7SAdrian Chadd 	}
745a043e8c7SAdrian Chadd 	switch (portstatus & AR8X16_PORT_STS_SPEED_MASK) {
746a043e8c7SAdrian Chadd 	case AR8X16_PORT_STS_SPEED_10:
747a043e8c7SAdrian Chadd 		*media_active |= IFM_10_T;
748a043e8c7SAdrian Chadd 		break;
749a043e8c7SAdrian Chadd 	case AR8X16_PORT_STS_SPEED_100:
750a043e8c7SAdrian Chadd 		*media_active |= IFM_100_TX;
751a043e8c7SAdrian Chadd 		break;
752a043e8c7SAdrian Chadd 	case AR8X16_PORT_STS_SPEED_1000:
753a043e8c7SAdrian Chadd 		*media_active |= IFM_1000_T;
754a043e8c7SAdrian Chadd 		break;
755a043e8c7SAdrian Chadd 	}
756a043e8c7SAdrian Chadd 	if ((portstatus & AR8X16_PORT_STS_DUPLEX) == 0)
757a043e8c7SAdrian Chadd 		*media_active |= IFM_FDX;
758a043e8c7SAdrian Chadd 	else
759a043e8c7SAdrian Chadd 		*media_active |= IFM_HDX;
760a043e8c7SAdrian Chadd 	if ((portstatus & AR8X16_PORT_STS_TXFLOW) != 0)
761a043e8c7SAdrian Chadd 		*media_active |= IFM_ETH_TXPAUSE;
762a043e8c7SAdrian Chadd 	if ((portstatus & AR8X16_PORT_STS_RXFLOW) != 0)
763a043e8c7SAdrian Chadd 		*media_active |= IFM_ETH_RXPAUSE;
764a043e8c7SAdrian Chadd }
765a043e8c7SAdrian Chadd 
766a043e8c7SAdrian Chadd /*
767a043e8c7SAdrian Chadd  * Poll the status for all PHYs.  We're using the switch port status because
768a043e8c7SAdrian Chadd  * thats a lot quicker to read than talking to all the PHYs.  Care must be
769a043e8c7SAdrian Chadd  * taken that the resulting ifmedia_active is identical to what the PHY will
770a043e8c7SAdrian Chadd  * compute, or gratuitous link status changes will occur whenever the PHYs
771a043e8c7SAdrian Chadd  * update function is called.
772a043e8c7SAdrian Chadd  */
773a043e8c7SAdrian Chadd static void
774a043e8c7SAdrian Chadd arswitch_miipollstat(struct arswitch_softc *sc)
775a043e8c7SAdrian Chadd {
776a043e8c7SAdrian Chadd 	int i;
777a043e8c7SAdrian Chadd 	struct mii_data *mii;
778a043e8c7SAdrian Chadd 	struct mii_softc *miisc;
779a043e8c7SAdrian Chadd 	int portstatus;
7804ff2f60dSAdrian Chadd 	int port_flap = 0;
781a043e8c7SAdrian Chadd 
782454d507aSAleksandr Rybalko 	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
783454d507aSAleksandr Rybalko 
784a043e8c7SAdrian Chadd 	for (i = 0; i < sc->numphys; i++) {
785a043e8c7SAdrian Chadd 		if (sc->miibus[i] == NULL)
786a043e8c7SAdrian Chadd 			continue;
787a043e8c7SAdrian Chadd 		mii = device_get_softc(sc->miibus[i]);
788ddbc4420SAdrian Chadd 		/* XXX This would be nice to have abstracted out to be per-chip */
789ddbc4420SAdrian Chadd 		/* AR8327/AR8337 has a different register base */
790ddbc4420SAdrian Chadd 		if (AR8X16_IS_SWITCH(sc, AR8327))
791ddbc4420SAdrian Chadd 			portstatus = arswitch_readreg(sc->sc_dev,
792ddbc4420SAdrian Chadd 			    AR8327_REG_PORT_STATUS(arswitch_portforphy(i)));
793ddbc4420SAdrian Chadd 		else
794a043e8c7SAdrian Chadd 			portstatus = arswitch_readreg(sc->sc_dev,
795a043e8c7SAdrian Chadd 			    AR8X16_REG_PORT_STS(arswitch_portforphy(i)));
7961b334c8bSAdrian Chadd #if 1
7971b334c8bSAdrian Chadd 		DPRINTF(sc, ARSWITCH_DBG_POLL, "p[%d]=0x%08x (%b)\n",
798b2152161SAdrian Chadd 		    i,
799a043e8c7SAdrian Chadd 		    portstatus,
8001b334c8bSAdrian Chadd 		    portstatus,
801a043e8c7SAdrian Chadd 		    "\20\3TXMAC\4RXMAC\5TXFLOW\6RXFLOW\7"
802a043e8c7SAdrian Chadd 		    "DUPLEX\11LINK_UP\12LINK_AUTO\13LINK_PAUSE");
803a043e8c7SAdrian Chadd #endif
8044ff2f60dSAdrian Chadd 		/*
8054ff2f60dSAdrian Chadd 		 * If the current status is down, but we have a link
8064ff2f60dSAdrian Chadd 		 * status showing up, we need to do an ATU flush.
8074ff2f60dSAdrian Chadd 		 */
8084ff2f60dSAdrian Chadd 		if ((mii->mii_media_status & IFM_ACTIVE) == 0 &&
8094ff2f60dSAdrian Chadd 		    (portstatus & AR8X16_PORT_STS_LINK_UP) != 0) {
8104ff2f60dSAdrian Chadd 			device_printf(sc->sc_dev, "%s: port %d: port -> UP\n",
8114ff2f60dSAdrian Chadd 			    __func__,
8124ff2f60dSAdrian Chadd 			    i);
8134ff2f60dSAdrian Chadd 			port_flap = 1;
8144ff2f60dSAdrian Chadd 		}
8154ff2f60dSAdrian Chadd 		/*
8164ff2f60dSAdrian Chadd 		 * and maybe if a port goes up->down?
8174ff2f60dSAdrian Chadd 		 */
8184ff2f60dSAdrian Chadd 		if ((mii->mii_media_status & IFM_ACTIVE) != 0 &&
8194ff2f60dSAdrian Chadd 		    (portstatus & AR8X16_PORT_STS_LINK_UP) == 0) {
8204ff2f60dSAdrian Chadd 			device_printf(sc->sc_dev, "%s: port %d: port -> DOWN\n",
8214ff2f60dSAdrian Chadd 			    __func__,
8224ff2f60dSAdrian Chadd 			    i);
8234ff2f60dSAdrian Chadd 			port_flap = 1;
8244ff2f60dSAdrian Chadd 		}
825a043e8c7SAdrian Chadd 		arswitch_update_ifmedia(portstatus, &mii->mii_media_status,
826a043e8c7SAdrian Chadd 		    &mii->mii_media_active);
827a043e8c7SAdrian Chadd 		LIST_FOREACH(miisc, &mii->mii_phys, mii_list) {
828a043e8c7SAdrian Chadd 			if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) !=
829a043e8c7SAdrian Chadd 			    miisc->mii_inst)
830a043e8c7SAdrian Chadd 				continue;
831a043e8c7SAdrian Chadd 			mii_phy_update(miisc, MII_POLLSTAT);
832a043e8c7SAdrian Chadd 		}
833a043e8c7SAdrian Chadd 	}
8344ff2f60dSAdrian Chadd 
8354ff2f60dSAdrian Chadd 	/* If a port went from down->up, flush the ATU */
8364ff2f60dSAdrian Chadd 	if (port_flap)
8374ff2f60dSAdrian Chadd 		sc->hal.arswitch_atu_flush(sc);
838a043e8c7SAdrian Chadd }
839a043e8c7SAdrian Chadd 
840a043e8c7SAdrian Chadd static void
841a043e8c7SAdrian Chadd arswitch_tick(void *arg)
842a043e8c7SAdrian Chadd {
843a043e8c7SAdrian Chadd 	struct arswitch_softc *sc = arg;
844a043e8c7SAdrian Chadd 
845a043e8c7SAdrian Chadd 	arswitch_miipollstat(sc);
846a043e8c7SAdrian Chadd 	callout_reset(&sc->callout_tick, hz, arswitch_tick, sc);
847a043e8c7SAdrian Chadd }
848a043e8c7SAdrian Chadd 
849454d507aSAleksandr Rybalko static void
850454d507aSAleksandr Rybalko arswitch_lock(device_t dev)
851454d507aSAleksandr Rybalko {
852454d507aSAleksandr Rybalko 	struct arswitch_softc *sc = device_get_softc(dev);
853454d507aSAleksandr Rybalko 
854454d507aSAleksandr Rybalko 	ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
855454d507aSAleksandr Rybalko 	ARSWITCH_LOCK(sc);
856454d507aSAleksandr Rybalko }
857454d507aSAleksandr Rybalko 
858454d507aSAleksandr Rybalko static void
859454d507aSAleksandr Rybalko arswitch_unlock(device_t dev)
860454d507aSAleksandr Rybalko {
861454d507aSAleksandr Rybalko 	struct arswitch_softc *sc = device_get_softc(dev);
862454d507aSAleksandr Rybalko 
863454d507aSAleksandr Rybalko 	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
864454d507aSAleksandr Rybalko 	ARSWITCH_UNLOCK(sc);
865454d507aSAleksandr Rybalko }
866454d507aSAleksandr Rybalko 
867a043e8c7SAdrian Chadd static etherswitch_info_t *
868a043e8c7SAdrian Chadd arswitch_getinfo(device_t dev)
869a043e8c7SAdrian Chadd {
870a043e8c7SAdrian Chadd 	struct arswitch_softc *sc = device_get_softc(dev);
871a043e8c7SAdrian Chadd 
872a043e8c7SAdrian Chadd 	return (&sc->info);
873a043e8c7SAdrian Chadd }
874a043e8c7SAdrian Chadd 
875a043e8c7SAdrian Chadd static int
876a9ad4222SAdrian Chadd ar8xxx_port_vlan_get(struct arswitch_softc *sc, etherswitch_port_t *p)
877a043e8c7SAdrian Chadd {
878b9f07b86SLuiz Otavio O Souza 	uint32_t reg;
879b9f07b86SLuiz Otavio O Souza 
880b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK(sc);
881b9f07b86SLuiz Otavio O Souza 
882b9f07b86SLuiz Otavio O Souza 	/* Retrieve the PVID. */
8836dcbabd7SAdrian Chadd 	sc->hal.arswitch_vlan_get_pvid(sc, p->es_port, &p->es_pvid);
884b9f07b86SLuiz Otavio O Souza 
885b9f07b86SLuiz Otavio O Souza 	/* Port flags. */
886b9f07b86SLuiz Otavio O Souza 	reg = arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_CTRL(p->es_port));
887b9f07b86SLuiz Otavio O Souza 	if (reg & AR8X16_PORT_CTRL_DOUBLE_TAG)
888b9f07b86SLuiz Otavio O Souza 		p->es_flags |= ETHERSWITCH_PORT_DOUBLE_TAG;
889b9f07b86SLuiz Otavio O Souza 	reg >>= AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT;
890b9f07b86SLuiz Otavio O Souza 	if ((reg & 0x3) == AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_ADD)
891b9f07b86SLuiz Otavio O Souza 		p->es_flags |= ETHERSWITCH_PORT_ADDTAG;
892b9f07b86SLuiz Otavio O Souza 	if ((reg & 0x3) == AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_STRIP)
893b9f07b86SLuiz Otavio O Souza 		p->es_flags |= ETHERSWITCH_PORT_STRIPTAG;
894b9f07b86SLuiz Otavio O Souza 	ARSWITCH_UNLOCK(sc);
895a043e8c7SAdrian Chadd 
896a9ad4222SAdrian Chadd 	return (0);
897a9ad4222SAdrian Chadd }
898a9ad4222SAdrian Chadd 
899a9ad4222SAdrian Chadd static int
900749cac13SAdrian Chadd arswitch_is_cpuport(struct arswitch_softc *sc, int port)
901749cac13SAdrian Chadd {
902749cac13SAdrian Chadd 
903749cac13SAdrian Chadd 	return ((port == AR8X16_PORT_CPU) ||
904749cac13SAdrian Chadd 	    ((AR8X16_IS_SWITCH(sc, AR8327) &&
905749cac13SAdrian Chadd 	      port == AR8327_PORT_GMAC6)));
906749cac13SAdrian Chadd }
907749cac13SAdrian Chadd 
908749cac13SAdrian Chadd static int
909a9ad4222SAdrian Chadd arswitch_getport(device_t dev, etherswitch_port_t *p)
910a9ad4222SAdrian Chadd {
911a9ad4222SAdrian Chadd 	struct arswitch_softc *sc;
912a9ad4222SAdrian Chadd 	struct mii_data *mii;
913a9ad4222SAdrian Chadd 	struct ifmediareq *ifmr;
914a9ad4222SAdrian Chadd 	int err;
915a9ad4222SAdrian Chadd 
916a9ad4222SAdrian Chadd 	sc = device_get_softc(dev);
917749cac13SAdrian Chadd 	/* XXX +1 is for AR8327; should make this configurable! */
918749cac13SAdrian Chadd 	if (p->es_port < 0 || p->es_port > sc->info.es_nports)
919a9ad4222SAdrian Chadd 		return (ENXIO);
920a9ad4222SAdrian Chadd 
921a9ad4222SAdrian Chadd 	err = sc->hal.arswitch_port_vlan_get(sc, p);
922a9ad4222SAdrian Chadd 	if (err != 0)
923a9ad4222SAdrian Chadd 		return (err);
924a9ad4222SAdrian Chadd 
925a043e8c7SAdrian Chadd 	mii = arswitch_miiforport(sc, p->es_port);
926749cac13SAdrian Chadd 	if (arswitch_is_cpuport(sc, p->es_port)) {
927a043e8c7SAdrian Chadd 		/* fill in fixed values for CPU port */
928a9ad4222SAdrian Chadd 		/* XXX is this valid in all cases? */
929f47857dcSAdrian Chadd 		p->es_flags |= ETHERSWITCH_PORT_CPU;
930b9f07b86SLuiz Otavio O Souza 		ifmr = &p->es_ifmr;
931a043e8c7SAdrian Chadd 		ifmr->ifm_count = 0;
932a043e8c7SAdrian Chadd 		ifmr->ifm_current = ifmr->ifm_active =
933a043e8c7SAdrian Chadd 		    IFM_ETHER | IFM_1000_T | IFM_FDX;
934a043e8c7SAdrian Chadd 		ifmr->ifm_mask = 0;
935a043e8c7SAdrian Chadd 		ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
936a043e8c7SAdrian Chadd 	} else if (mii != NULL) {
937a043e8c7SAdrian Chadd 		err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr,
938a043e8c7SAdrian Chadd 		    &mii->mii_media, SIOCGIFMEDIA);
939a043e8c7SAdrian Chadd 		if (err)
940a043e8c7SAdrian Chadd 			return (err);
941a043e8c7SAdrian Chadd 	} else {
942a043e8c7SAdrian Chadd 		return (ENXIO);
943a043e8c7SAdrian Chadd 	}
944c94dc808SAdrian Chadd 
945c94dc808SAdrian Chadd 	if (!arswitch_is_cpuport(sc, p->es_port) &&
946c94dc808SAdrian Chadd 	    AR8X16_IS_SWITCH(sc, AR8327)) {
947c94dc808SAdrian Chadd 		int led;
948c94dc808SAdrian Chadd 		p->es_nleds = 3;
949c94dc808SAdrian Chadd 
950c94dc808SAdrian Chadd 		for (led = 0; led < p->es_nleds; led++)
951c94dc808SAdrian Chadd 		{
952c94dc808SAdrian Chadd 			int style;
953c94dc808SAdrian Chadd 			uint32_t val;
954c94dc808SAdrian Chadd 
955c94dc808SAdrian Chadd 			/* Find the right style enum for our pattern */
956c94dc808SAdrian Chadd 			val = arswitch_readreg(dev,
957c94dc808SAdrian Chadd 			    ar8327_led_mapping[p->es_port-1][led].reg);
958c94dc808SAdrian Chadd 			val = (val>>ar8327_led_mapping[p->es_port-1][led].shift)&0x03;
959c94dc808SAdrian Chadd 
960c94dc808SAdrian Chadd 			for (style = 0; style < ETHERSWITCH_PORT_LED_MAX; style++)
961c94dc808SAdrian Chadd 			{
962c94dc808SAdrian Chadd 				if (led_pattern_table[style] == val) break;
963c94dc808SAdrian Chadd 			}
964c94dc808SAdrian Chadd 
965c94dc808SAdrian Chadd 			/* can't happen */
966c94dc808SAdrian Chadd 			if (style == ETHERSWITCH_PORT_LED_MAX)
967c94dc808SAdrian Chadd 				style = ETHERSWITCH_PORT_LED_DEFAULT;
968c94dc808SAdrian Chadd 
969c94dc808SAdrian Chadd 			p->es_led[led] = style;
970c94dc808SAdrian Chadd 		}
971c94dc808SAdrian Chadd 	} else
972c94dc808SAdrian Chadd 	{
973c94dc808SAdrian Chadd 		p->es_nleds = 0;
974c94dc808SAdrian Chadd 	}
975c94dc808SAdrian Chadd 
976a043e8c7SAdrian Chadd 	return (0);
977a043e8c7SAdrian Chadd }
978a043e8c7SAdrian Chadd 
979a043e8c7SAdrian Chadd static int
980a9ad4222SAdrian Chadd ar8xxx_port_vlan_setup(struct arswitch_softc *sc, etherswitch_port_t *p)
981a043e8c7SAdrian Chadd {
982b9f07b86SLuiz Otavio O Souza 	uint32_t reg;
983a9ad4222SAdrian Chadd 	int err;
984a043e8c7SAdrian Chadd 
985b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK(sc);
986a9ad4222SAdrian Chadd 
987b9f07b86SLuiz Otavio O Souza 	/* Set the PVID. */
988b9f07b86SLuiz Otavio O Souza 	if (p->es_pvid != 0)
9896dcbabd7SAdrian Chadd 		sc->hal.arswitch_vlan_set_pvid(sc, p->es_port, p->es_pvid);
990b9f07b86SLuiz Otavio O Souza 
991b9f07b86SLuiz Otavio O Souza 	/* Mutually exclusive. */
992b9f07b86SLuiz Otavio O Souza 	if (p->es_flags & ETHERSWITCH_PORT_ADDTAG &&
993b9f07b86SLuiz Otavio O Souza 	    p->es_flags & ETHERSWITCH_PORT_STRIPTAG) {
994b9f07b86SLuiz Otavio O Souza 		ARSWITCH_UNLOCK(sc);
995b9f07b86SLuiz Otavio O Souza 		return (EINVAL);
996b9f07b86SLuiz Otavio O Souza 	}
997b9f07b86SLuiz Otavio O Souza 
998b9f07b86SLuiz Otavio O Souza 	reg = 0;
999b9f07b86SLuiz Otavio O Souza 	if (p->es_flags & ETHERSWITCH_PORT_DOUBLE_TAG)
1000b9f07b86SLuiz Otavio O Souza 		reg |= AR8X16_PORT_CTRL_DOUBLE_TAG;
1001b9f07b86SLuiz Otavio O Souza 	if (p->es_flags & ETHERSWITCH_PORT_ADDTAG)
1002b9f07b86SLuiz Otavio O Souza 		reg |= AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_ADD <<
1003b9f07b86SLuiz Otavio O Souza 		    AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT;
1004b9f07b86SLuiz Otavio O Souza 	if (p->es_flags & ETHERSWITCH_PORT_STRIPTAG)
1005b9f07b86SLuiz Otavio O Souza 		reg |= AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_STRIP <<
1006b9f07b86SLuiz Otavio O Souza 		    AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT;
1007b9f07b86SLuiz Otavio O Souza 
1008b9f07b86SLuiz Otavio O Souza 	err = arswitch_modifyreg(sc->sc_dev,
1009b9f07b86SLuiz Otavio O Souza 	    AR8X16_REG_PORT_CTRL(p->es_port),
1010b9f07b86SLuiz Otavio O Souza 	    0x3 << AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT |
1011b9f07b86SLuiz Otavio O Souza 	    AR8X16_PORT_CTRL_DOUBLE_TAG, reg);
1012b9f07b86SLuiz Otavio O Souza 
1013b9f07b86SLuiz Otavio O Souza 	ARSWITCH_UNLOCK(sc);
1014a9ad4222SAdrian Chadd 	return (err);
1015a9ad4222SAdrian Chadd }
1016a9ad4222SAdrian Chadd 
1017a9ad4222SAdrian Chadd static int
1018a9ad4222SAdrian Chadd arswitch_setport(device_t dev, etherswitch_port_t *p)
1019a9ad4222SAdrian Chadd {
1020c94dc808SAdrian Chadd 	int err, i;
1021a9ad4222SAdrian Chadd 	struct arswitch_softc *sc;
1022a9ad4222SAdrian Chadd 	struct ifmedia *ifm;
1023a9ad4222SAdrian Chadd 	struct mii_data *mii;
10242e6a8c1aSJustin Hibbits 	if_t ifp;
1025a9ad4222SAdrian Chadd 
1026a9ad4222SAdrian Chadd 	sc = device_get_softc(dev);
1027749cac13SAdrian Chadd 	if (p->es_port < 0 || p->es_port > sc->info.es_nports)
1028a9ad4222SAdrian Chadd 		return (ENXIO);
1029a9ad4222SAdrian Chadd 
1030a9ad4222SAdrian Chadd 	/* Port flags. */
1031a9ad4222SAdrian Chadd 	if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
1032a9ad4222SAdrian Chadd 		err = sc->hal.arswitch_port_vlan_setup(sc, p);
1033b9f07b86SLuiz Otavio O Souza 		if (err)
1034b9f07b86SLuiz Otavio O Souza 			return (err);
1035b9f07b86SLuiz Otavio O Souza 	}
1036b9f07b86SLuiz Otavio O Souza 
1037c94dc808SAdrian Chadd 	/* Do not allow media or led changes on CPU port. */
1038749cac13SAdrian Chadd 	if (arswitch_is_cpuport(sc, p->es_port))
1039b9f07b86SLuiz Otavio O Souza 		return (0);
1040a043e8c7SAdrian Chadd 
1041c94dc808SAdrian Chadd 	if (AR8X16_IS_SWITCH(sc, AR8327))
1042c94dc808SAdrian Chadd 	{
1043c94dc808SAdrian Chadd 		for (i = 0; i < 3; i++)
1044c94dc808SAdrian Chadd 		{
1045c94dc808SAdrian Chadd 			int err;
1046c94dc808SAdrian Chadd 			err = arswitch_setled(sc, p->es_port-1, i, p->es_led[i]);
1047c94dc808SAdrian Chadd 			if (err)
1048c94dc808SAdrian Chadd 				return (err);
1049c94dc808SAdrian Chadd 		}
1050c94dc808SAdrian Chadd 	}
1051c94dc808SAdrian Chadd 
1052a043e8c7SAdrian Chadd 	mii = arswitch_miiforport(sc, p->es_port);
1053a043e8c7SAdrian Chadd 	if (mii == NULL)
1054a043e8c7SAdrian Chadd 		return (ENXIO);
1055a043e8c7SAdrian Chadd 
1056a043e8c7SAdrian Chadd 	ifp = arswitch_ifpforport(sc, p->es_port);
1057a043e8c7SAdrian Chadd 
1058a043e8c7SAdrian Chadd 	ifm = &mii->mii_media;
1059b9f07b86SLuiz Otavio O Souza 	return (ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA));
1060a043e8c7SAdrian Chadd }
1061a043e8c7SAdrian Chadd 
1062c94dc808SAdrian Chadd static int
1063c94dc808SAdrian Chadd arswitch_setled(struct arswitch_softc *sc, int phy, int led, int style)
1064c94dc808SAdrian Chadd {
1065c94dc808SAdrian Chadd 	int shift;
10666d011946SKristof Provost 	int err;
1067c94dc808SAdrian Chadd 
1068c94dc808SAdrian Chadd 	if (phy < 0 || phy > sc->numphys)
1069c94dc808SAdrian Chadd 		return EINVAL;
1070c94dc808SAdrian Chadd 
1071c94dc808SAdrian Chadd 	if (style < 0 || style > ETHERSWITCH_PORT_LED_MAX)
1072c94dc808SAdrian Chadd 		return (EINVAL);
1073c94dc808SAdrian Chadd 
10746d011946SKristof Provost 	ARSWITCH_LOCK(sc);
10756d011946SKristof Provost 
1076c94dc808SAdrian Chadd 	shift = ar8327_led_mapping[phy][led].shift;
10776d011946SKristof Provost 	err = (arswitch_modifyreg(sc->sc_dev,
1078c94dc808SAdrian Chadd 	    ar8327_led_mapping[phy][led].reg,
1079c94dc808SAdrian Chadd 	    0x03 << shift, led_pattern_table[style] << shift));
10806d011946SKristof Provost 	ARSWITCH_UNLOCK(sc);
10816d011946SKristof Provost 
10826d011946SKristof Provost 	return (err);
1083c94dc808SAdrian Chadd }
1084c94dc808SAdrian Chadd 
1085a043e8c7SAdrian Chadd static void
1086a043e8c7SAdrian Chadd arswitch_statchg(device_t dev)
1087a043e8c7SAdrian Chadd {
10881b334c8bSAdrian Chadd 	struct arswitch_softc *sc = device_get_softc(dev);
1089a043e8c7SAdrian Chadd 
10901b334c8bSAdrian Chadd 	DPRINTF(sc, ARSWITCH_DBG_POLL, "%s\n", __func__);
1091a043e8c7SAdrian Chadd }
1092a043e8c7SAdrian Chadd 
1093a043e8c7SAdrian Chadd static int
10942e6a8c1aSJustin Hibbits arswitch_ifmedia_upd(if_t ifp)
1095a043e8c7SAdrian Chadd {
10962e6a8c1aSJustin Hibbits 	struct arswitch_softc *sc = if_getsoftc(ifp);
10972e6a8c1aSJustin Hibbits 	struct mii_data *mii = arswitch_miiforport(sc, if_getdunit(ifp));
1098a043e8c7SAdrian Chadd 
1099a043e8c7SAdrian Chadd 	if (mii == NULL)
1100a043e8c7SAdrian Chadd 		return (ENXIO);
1101a043e8c7SAdrian Chadd 	mii_mediachg(mii);
1102a043e8c7SAdrian Chadd 	return (0);
1103a043e8c7SAdrian Chadd }
1104a043e8c7SAdrian Chadd 
1105a043e8c7SAdrian Chadd static void
11062e6a8c1aSJustin Hibbits arswitch_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr)
1107a043e8c7SAdrian Chadd {
11082e6a8c1aSJustin Hibbits 	struct arswitch_softc *sc = if_getsoftc(ifp);
11092e6a8c1aSJustin Hibbits 	struct mii_data *mii = arswitch_miiforport(sc, if_getdunit(ifp));
1110a043e8c7SAdrian Chadd 
11111b334c8bSAdrian Chadd 	DPRINTF(sc, ARSWITCH_DBG_POLL, "%s\n", __func__);
1112a043e8c7SAdrian Chadd 
1113a043e8c7SAdrian Chadd 	if (mii == NULL)
1114a043e8c7SAdrian Chadd 		return;
1115a043e8c7SAdrian Chadd 	mii_pollstat(mii);
1116a043e8c7SAdrian Chadd 	ifmr->ifm_active = mii->mii_media_active;
1117a043e8c7SAdrian Chadd 	ifmr->ifm_status = mii->mii_media_status;
1118a043e8c7SAdrian Chadd }
1119a043e8c7SAdrian Chadd 
1120b9f07b86SLuiz Otavio O Souza static int
1121b9f07b86SLuiz Otavio O Souza arswitch_getconf(device_t dev, etherswitch_conf_t *conf)
1122b9f07b86SLuiz Otavio O Souza {
1123b9f07b86SLuiz Otavio O Souza 	struct arswitch_softc *sc;
11242ba4bf8fSAdrian Chadd 	int ret;
1125b9f07b86SLuiz Otavio O Souza 
1126b9f07b86SLuiz Otavio O Souza 	sc = device_get_softc(dev);
1127b9f07b86SLuiz Otavio O Souza 
1128b9f07b86SLuiz Otavio O Souza 	/* Return the VLAN mode. */
1129b9f07b86SLuiz Otavio O Souza 	conf->cmd = ETHERSWITCH_CONF_VLAN_MODE;
1130b9f07b86SLuiz Otavio O Souza 	conf->vlan_mode = sc->vlan_mode;
1131b9f07b86SLuiz Otavio O Souza 
11322ba4bf8fSAdrian Chadd 	/* Return the switch ethernet address. */
11332ba4bf8fSAdrian Chadd 	ret = sc->hal.arswitch_hw_get_switch_macaddr(sc,
11342ba4bf8fSAdrian Chadd 	    &conf->switch_macaddr);
11352ba4bf8fSAdrian Chadd 	if (ret == 0) {
11362ba4bf8fSAdrian Chadd 		conf->cmd |= ETHERSWITCH_CONF_SWITCH_MACADDR;
11372ba4bf8fSAdrian Chadd 	}
11382ba4bf8fSAdrian Chadd 
1139b9f07b86SLuiz Otavio O Souza 	return (0);
1140b9f07b86SLuiz Otavio O Souza }
1141b9f07b86SLuiz Otavio O Souza 
1142b9f07b86SLuiz Otavio O Souza static int
1143b9f07b86SLuiz Otavio O Souza arswitch_setconf(device_t dev, etherswitch_conf_t *conf)
1144b9f07b86SLuiz Otavio O Souza {
1145b9f07b86SLuiz Otavio O Souza 	struct arswitch_softc *sc;
1146b9f07b86SLuiz Otavio O Souza 	int err;
1147b9f07b86SLuiz Otavio O Souza 
1148b9f07b86SLuiz Otavio O Souza 	sc = device_get_softc(dev);
1149b9f07b86SLuiz Otavio O Souza 
1150b9f07b86SLuiz Otavio O Souza 	/* Set the VLAN mode. */
1151b9f07b86SLuiz Otavio O Souza 	if (conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) {
1152b9f07b86SLuiz Otavio O Souza 		err = arswitch_set_vlan_mode(sc, conf->vlan_mode);
1153b9f07b86SLuiz Otavio O Souza 		if (err != 0)
1154b9f07b86SLuiz Otavio O Souza 			return (err);
1155b9f07b86SLuiz Otavio O Souza 	}
1156b9f07b86SLuiz Otavio O Souza 
11572ba4bf8fSAdrian Chadd 	/* TODO: Set the switch ethernet address. */
11582ba4bf8fSAdrian Chadd 
1159b9f07b86SLuiz Otavio O Souza 	return (0);
1160b9f07b86SLuiz Otavio O Souza }
1161b9f07b86SLuiz Otavio O Souza 
11626dcbabd7SAdrian Chadd static int
116362042c97SAdrian Chadd arswitch_atu_flush_all(device_t dev)
116462042c97SAdrian Chadd {
116562042c97SAdrian Chadd 	struct arswitch_softc *sc;
116662042c97SAdrian Chadd 	int err;
116762042c97SAdrian Chadd 
116862042c97SAdrian Chadd 	sc = device_get_softc(dev);
116962042c97SAdrian Chadd 	ARSWITCH_LOCK(sc);
117062042c97SAdrian Chadd 	err = sc->hal.arswitch_atu_flush(sc);
117162042c97SAdrian Chadd 	/* Invalidate cached ATU */
117262042c97SAdrian Chadd 	sc->atu.count = 0;
117362042c97SAdrian Chadd 	ARSWITCH_UNLOCK(sc);
117462042c97SAdrian Chadd 	return (err);
117562042c97SAdrian Chadd }
117662042c97SAdrian Chadd 
117762042c97SAdrian Chadd static int
117862042c97SAdrian Chadd arswitch_atu_flush_port(device_t dev, int port)
117962042c97SAdrian Chadd {
118062042c97SAdrian Chadd 	struct arswitch_softc *sc;
118162042c97SAdrian Chadd 	int err;
118262042c97SAdrian Chadd 
118362042c97SAdrian Chadd 	sc = device_get_softc(dev);
118462042c97SAdrian Chadd 	ARSWITCH_LOCK(sc);
118562042c97SAdrian Chadd 	err = sc->hal.arswitch_atu_flush_port(sc, port);
118662042c97SAdrian Chadd 	/* Invalidate cached ATU */
118762042c97SAdrian Chadd 	sc->atu.count = 0;
118862042c97SAdrian Chadd 	ARSWITCH_UNLOCK(sc);
118962042c97SAdrian Chadd 	return (err);
119062042c97SAdrian Chadd }
119162042c97SAdrian Chadd 
119262042c97SAdrian Chadd static int
119362042c97SAdrian Chadd arswitch_atu_fetch_table(device_t dev, etherswitch_atu_table_t *table)
119462042c97SAdrian Chadd {
119562042c97SAdrian Chadd 	struct arswitch_softc *sc;
119662042c97SAdrian Chadd 	int err, nitems;
119762042c97SAdrian Chadd 
119862042c97SAdrian Chadd 	sc = device_get_softc(dev);
119962042c97SAdrian Chadd 
120062042c97SAdrian Chadd 	ARSWITCH_LOCK(sc);
120162042c97SAdrian Chadd 	/* Initial setup */
120262042c97SAdrian Chadd 	nitems = 0;
120362042c97SAdrian Chadd 	err = sc->hal.arswitch_atu_fetch_table(sc, NULL, 0);
120462042c97SAdrian Chadd 
120562042c97SAdrian Chadd 	/* fetch - ideally yes we'd fetch into a separate table then switch */
12067ed08319SAdrian Chadd 	while (err == 0 && nitems < sc->atu.size) {
120762042c97SAdrian Chadd 		err = sc->hal.arswitch_atu_fetch_table(sc,
120862042c97SAdrian Chadd 		    &sc->atu.entries[nitems], 1);
120962042c97SAdrian Chadd 		if (err == 0) {
121062042c97SAdrian Chadd 			sc->atu.entries[nitems].id = nitems;
121162042c97SAdrian Chadd 			nitems++;
121262042c97SAdrian Chadd 		}
121362042c97SAdrian Chadd 	}
121462042c97SAdrian Chadd 	sc->atu.count = nitems;
121562042c97SAdrian Chadd 	ARSWITCH_UNLOCK(sc);
121662042c97SAdrian Chadd 
121762042c97SAdrian Chadd 	table->es_nitems = nitems;
121862042c97SAdrian Chadd 
121962042c97SAdrian Chadd 	return (0);
122062042c97SAdrian Chadd }
122162042c97SAdrian Chadd 
122262042c97SAdrian Chadd static int
122362042c97SAdrian Chadd arswitch_atu_fetch_table_entry(device_t dev, etherswitch_atu_entry_t *e)
122462042c97SAdrian Chadd {
122562042c97SAdrian Chadd 	struct arswitch_softc *sc;
122662042c97SAdrian Chadd 	int id;
122762042c97SAdrian Chadd 
122862042c97SAdrian Chadd 	sc = device_get_softc(dev);
122962042c97SAdrian Chadd 	id = e->id;
123062042c97SAdrian Chadd 
123162042c97SAdrian Chadd 	ARSWITCH_LOCK(sc);
123262042c97SAdrian Chadd 	if (id > sc->atu.count) {
123362042c97SAdrian Chadd 		ARSWITCH_UNLOCK(sc);
123462042c97SAdrian Chadd 		return (ENOENT);
123562042c97SAdrian Chadd 	}
123662042c97SAdrian Chadd 
123762042c97SAdrian Chadd 	memcpy(e, &sc->atu.entries[id], sizeof(*e));
123862042c97SAdrian Chadd 	ARSWITCH_UNLOCK(sc);
123962042c97SAdrian Chadd 	return (0);
124062042c97SAdrian Chadd }
124162042c97SAdrian Chadd 
124262042c97SAdrian Chadd static int
12436dcbabd7SAdrian Chadd arswitch_getvgroup(device_t dev, etherswitch_vlangroup_t *e)
12446dcbabd7SAdrian Chadd {
12456dcbabd7SAdrian Chadd 	struct arswitch_softc *sc = device_get_softc(dev);
12466dcbabd7SAdrian Chadd 
12476dcbabd7SAdrian Chadd 	return (sc->hal.arswitch_vlan_getvgroup(sc, e));
12486dcbabd7SAdrian Chadd }
12496dcbabd7SAdrian Chadd 
12506dcbabd7SAdrian Chadd static int
12516dcbabd7SAdrian Chadd arswitch_setvgroup(device_t dev, etherswitch_vlangroup_t *e)
12526dcbabd7SAdrian Chadd {
12536dcbabd7SAdrian Chadd 	struct arswitch_softc *sc = device_get_softc(dev);
12546dcbabd7SAdrian Chadd 
12556dcbabd7SAdrian Chadd 	return (sc->hal.arswitch_vlan_setvgroup(sc, e));
12566dcbabd7SAdrian Chadd }
12576dcbabd7SAdrian Chadd 
125878549b94SAdrian Chadd static int
125978549b94SAdrian Chadd arswitch_readphy(device_t dev, int phy, int reg)
126078549b94SAdrian Chadd {
126178549b94SAdrian Chadd 	struct arswitch_softc *sc = device_get_softc(dev);
126278549b94SAdrian Chadd 
126378549b94SAdrian Chadd 	return (sc->hal.arswitch_phy_read(dev, phy, reg));
126478549b94SAdrian Chadd }
126578549b94SAdrian Chadd 
126678549b94SAdrian Chadd static int
126778549b94SAdrian Chadd arswitch_writephy(device_t dev, int phy, int reg, int val)
126878549b94SAdrian Chadd {
126978549b94SAdrian Chadd 	struct arswitch_softc *sc = device_get_softc(dev);
127078549b94SAdrian Chadd 
127178549b94SAdrian Chadd 	return (sc->hal.arswitch_phy_write(dev, phy, reg, val));
127278549b94SAdrian Chadd }
127378549b94SAdrian Chadd 
1274a043e8c7SAdrian Chadd static device_method_t arswitch_methods[] = {
1275a043e8c7SAdrian Chadd 	/* Device interface */
1276a043e8c7SAdrian Chadd 	DEVMETHOD(device_probe,		arswitch_probe),
1277a043e8c7SAdrian Chadd 	DEVMETHOD(device_attach,	arswitch_attach),
1278a043e8c7SAdrian Chadd 	DEVMETHOD(device_detach,	arswitch_detach),
1279a043e8c7SAdrian Chadd 
1280a043e8c7SAdrian Chadd 	/* bus interface */
1281a043e8c7SAdrian Chadd 	DEVMETHOD(bus_add_child,	device_add_child_ordered),
1282a043e8c7SAdrian Chadd 
1283a043e8c7SAdrian Chadd 	/* MII interface */
1284a043e8c7SAdrian Chadd 	DEVMETHOD(miibus_readreg,	arswitch_readphy),
1285a043e8c7SAdrian Chadd 	DEVMETHOD(miibus_writereg,	arswitch_writephy),
1286a043e8c7SAdrian Chadd 	DEVMETHOD(miibus_statchg,	arswitch_statchg),
1287a043e8c7SAdrian Chadd 
1288a043e8c7SAdrian Chadd 	/* MDIO interface */
1289a043e8c7SAdrian Chadd 	DEVMETHOD(mdio_readreg,		arswitch_readphy),
1290a043e8c7SAdrian Chadd 	DEVMETHOD(mdio_writereg,	arswitch_writephy),
1291a043e8c7SAdrian Chadd 
1292a043e8c7SAdrian Chadd 	/* etherswitch interface */
1293454d507aSAleksandr Rybalko 	DEVMETHOD(etherswitch_lock,	arswitch_lock),
1294454d507aSAleksandr Rybalko 	DEVMETHOD(etherswitch_unlock,	arswitch_unlock),
1295a043e8c7SAdrian Chadd 	DEVMETHOD(etherswitch_getinfo,	arswitch_getinfo),
1296a043e8c7SAdrian Chadd 	DEVMETHOD(etherswitch_readreg,	arswitch_readreg),
1297a043e8c7SAdrian Chadd 	DEVMETHOD(etherswitch_writereg,	arswitch_writereg),
1298a043e8c7SAdrian Chadd 	DEVMETHOD(etherswitch_readphyreg,	arswitch_readphy),
1299a043e8c7SAdrian Chadd 	DEVMETHOD(etherswitch_writephyreg,	arswitch_writephy),
1300a043e8c7SAdrian Chadd 	DEVMETHOD(etherswitch_getport,	arswitch_getport),
1301a043e8c7SAdrian Chadd 	DEVMETHOD(etherswitch_setport,	arswitch_setport),
1302a043e8c7SAdrian Chadd 	DEVMETHOD(etherswitch_getvgroup,	arswitch_getvgroup),
1303a043e8c7SAdrian Chadd 	DEVMETHOD(etherswitch_setvgroup,	arswitch_setvgroup),
1304b9f07b86SLuiz Otavio O Souza 	DEVMETHOD(etherswitch_getconf,	arswitch_getconf),
1305b9f07b86SLuiz Otavio O Souza 	DEVMETHOD(etherswitch_setconf,	arswitch_setconf),
130662042c97SAdrian Chadd 	DEVMETHOD(etherswitch_flush_all, arswitch_atu_flush_all),
130762042c97SAdrian Chadd 	DEVMETHOD(etherswitch_flush_port, arswitch_atu_flush_port),
130862042c97SAdrian Chadd 	DEVMETHOD(etherswitch_fetch_table, arswitch_atu_fetch_table),
130962042c97SAdrian Chadd 	DEVMETHOD(etherswitch_fetch_table_entry, arswitch_atu_fetch_table_entry),
1310a043e8c7SAdrian Chadd 
1311a043e8c7SAdrian Chadd 	DEVMETHOD_END
1312a043e8c7SAdrian Chadd };
1313a043e8c7SAdrian Chadd 
1314a043e8c7SAdrian Chadd DEFINE_CLASS_0(arswitch, arswitch_driver, arswitch_methods,
1315a043e8c7SAdrian Chadd     sizeof(struct arswitch_softc));
1316a043e8c7SAdrian Chadd 
131742726c2fSJohn Baldwin DRIVER_MODULE(arswitch, mdio, arswitch_driver, 0, 0);
13183e38757dSJohn Baldwin DRIVER_MODULE(miibus, arswitch, miibus_driver, 0, 0);
13198933f7d6SJohn Baldwin DRIVER_MODULE(mdio, arswitch, mdio_driver, 0, 0);
1320829a13faSJohn Baldwin DRIVER_MODULE(etherswitch, arswitch, etherswitch_driver, 0, 0);
1321a043e8c7SAdrian Chadd MODULE_VERSION(arswitch, 1);
1322a043e8c7SAdrian Chadd MODULE_DEPEND(arswitch, miibus, 1, 1, 1); /* XXX which versions? */
1323a043e8c7SAdrian Chadd MODULE_DEPEND(arswitch, etherswitch, 1, 1, 1); /* XXX which versions? */
1324