xref: /freebsd/sys/dev/etherswitch/arswitch/arswitch.c (revision 8933f7d6007768df741d4ebba87e3d5c9e78ee9c)
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 /*
4872ba4bf8fSAdrian Chadd  * Fetch the configured switch MAC address.
4882ba4bf8fSAdrian Chadd  */
4892ba4bf8fSAdrian Chadd static int
4902ba4bf8fSAdrian Chadd ar8xxx_hw_get_switch_macaddr(struct arswitch_softc *sc, struct ether_addr *ea)
4912ba4bf8fSAdrian Chadd {
4922ba4bf8fSAdrian Chadd 	uint32_t ret0, ret1;
4932ba4bf8fSAdrian Chadd 	char *s;
4942ba4bf8fSAdrian Chadd 
4952ba4bf8fSAdrian Chadd 	s = (void *) ea;
4962ba4bf8fSAdrian Chadd 
4972ba4bf8fSAdrian Chadd 	ret0 = arswitch_readreg(sc->sc_dev, AR8X16_REG_SW_MAC_ADDR0);
4982ba4bf8fSAdrian Chadd 	ret1 = arswitch_readreg(sc->sc_dev, AR8X16_REG_SW_MAC_ADDR1);
4992ba4bf8fSAdrian Chadd 
5002ba4bf8fSAdrian Chadd 	s[5] = MS(ret0, AR8X16_REG_SW_MAC_ADDR0_BYTE5);
5012ba4bf8fSAdrian Chadd 	s[4] = MS(ret0, AR8X16_REG_SW_MAC_ADDR0_BYTE4);
5022ba4bf8fSAdrian Chadd 	s[3] = MS(ret1, AR8X16_REG_SW_MAC_ADDR1_BYTE3);
5032ba4bf8fSAdrian Chadd 	s[2] = MS(ret1, AR8X16_REG_SW_MAC_ADDR1_BYTE2);
5042ba4bf8fSAdrian Chadd 	s[1] = MS(ret1, AR8X16_REG_SW_MAC_ADDR1_BYTE1);
5052ba4bf8fSAdrian Chadd 	s[0] = MS(ret1, AR8X16_REG_SW_MAC_ADDR1_BYTE0);
5062ba4bf8fSAdrian Chadd 
5072ba4bf8fSAdrian Chadd 	return (0);
5082ba4bf8fSAdrian Chadd }
5092ba4bf8fSAdrian Chadd 
5102ba4bf8fSAdrian Chadd /*
5112ba4bf8fSAdrian Chadd  * Set the switch mac address.
5122ba4bf8fSAdrian Chadd  */
5132ba4bf8fSAdrian Chadd static int
5142ba4bf8fSAdrian Chadd ar8xxx_hw_set_switch_macaddr(struct arswitch_softc *sc,
5152ba4bf8fSAdrian Chadd     const struct ether_addr *ea)
5162ba4bf8fSAdrian Chadd {
5172ba4bf8fSAdrian Chadd 
5182ba4bf8fSAdrian Chadd 	return (ENXIO);
5192ba4bf8fSAdrian Chadd }
5202ba4bf8fSAdrian Chadd 
5212ba4bf8fSAdrian Chadd /*
52262042c97SAdrian Chadd  * XXX TODO: this attach routine does NOT free all memory, resources
52362042c97SAdrian Chadd  * upon failure!
52462042c97SAdrian Chadd  */
5254ff2f60dSAdrian Chadd static int
526a043e8c7SAdrian Chadd arswitch_attach(device_t dev)
527a043e8c7SAdrian Chadd {
5281b334c8bSAdrian Chadd 	struct arswitch_softc *sc = device_get_softc(dev);
5291b334c8bSAdrian Chadd 	struct sysctl_ctx_list *ctx;
5301b334c8bSAdrian Chadd 	struct sysctl_oid *tree;
531a043e8c7SAdrian Chadd 	int err = 0;
532df892897SAdrian Chadd 	int port;
533a043e8c7SAdrian Chadd 
534a043e8c7SAdrian Chadd 	/* sc->sc_switchtype is already decided in arswitch_probe() */
535a043e8c7SAdrian Chadd 	sc->sc_dev = dev;
536a043e8c7SAdrian Chadd 	mtx_init(&sc->sc_mtx, "arswitch", NULL, MTX_DEF);
537a043e8c7SAdrian Chadd 	sc->page = -1;
538a043e8c7SAdrian Chadd 	strlcpy(sc->info.es_name, device_get_desc(dev),
539a043e8c7SAdrian Chadd 	    sizeof(sc->info.es_name));
540a043e8c7SAdrian Chadd 
5411b334c8bSAdrian Chadd 	/* Debugging */
5421b334c8bSAdrian Chadd 	ctx = device_get_sysctl_ctx(sc->sc_dev);
5431b334c8bSAdrian Chadd 	tree = device_get_sysctl_tree(sc->sc_dev);
5441b334c8bSAdrian Chadd 	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
5451b334c8bSAdrian Chadd 	    "debug", CTLFLAG_RW, &sc->sc_debug, 0,
5461b334c8bSAdrian Chadd 	    "control debugging printfs");
5471b334c8bSAdrian Chadd 
54862042c97SAdrian Chadd 	/* Allocate a 128 entry ATU table; hopefully its big enough! */
54962042c97SAdrian Chadd 	/* XXX TODO: make this per chip */
55062042c97SAdrian Chadd 	sc->atu.entries = malloc(sizeof(etherswitch_atu_entry_t) * 128,
55162042c97SAdrian Chadd 	    M_DEVBUF, M_NOWAIT);
55262042c97SAdrian Chadd 	if (sc->atu.entries == NULL) {
55362042c97SAdrian Chadd 		device_printf(sc->sc_dev, "%s: failed to allocate ATU table\n",
55462042c97SAdrian Chadd 		    __func__);
55562042c97SAdrian Chadd 		return (ENXIO);
55662042c97SAdrian Chadd 	}
55762042c97SAdrian Chadd 	sc->atu.count = 0;
55862042c97SAdrian Chadd 	sc->atu.size = 128;
55962042c97SAdrian Chadd 
560ddbc4420SAdrian Chadd 	/* Default HAL methods */
561a9ad4222SAdrian Chadd 	sc->hal.arswitch_port_init = ar8xxx_port_init;
562a9ad4222SAdrian Chadd 	sc->hal.arswitch_port_vlan_setup = ar8xxx_port_vlan_setup;
563a9ad4222SAdrian Chadd 	sc->hal.arswitch_port_vlan_get = ar8xxx_port_vlan_get;
5646dcbabd7SAdrian Chadd 	sc->hal.arswitch_vlan_init_hw = ar8xxx_reset_vlans;
5652ba4bf8fSAdrian Chadd 	sc->hal.arswitch_hw_get_switch_macaddr = ar8xxx_hw_get_switch_macaddr;
5662ba4bf8fSAdrian Chadd 	sc->hal.arswitch_hw_set_switch_macaddr = ar8xxx_hw_set_switch_macaddr;
567749cac13SAdrian Chadd 
5686dcbabd7SAdrian Chadd 	sc->hal.arswitch_vlan_getvgroup = ar8xxx_getvgroup;
5696dcbabd7SAdrian Chadd 	sc->hal.arswitch_vlan_setvgroup = ar8xxx_setvgroup;
570749cac13SAdrian Chadd 
5716dcbabd7SAdrian Chadd 	sc->hal.arswitch_vlan_get_pvid = ar8xxx_get_pvid;
5726dcbabd7SAdrian Chadd 	sc->hal.arswitch_vlan_set_pvid = ar8xxx_set_pvid;
573749cac13SAdrian Chadd 
574749cac13SAdrian Chadd 	sc->hal.arswitch_get_dot1q_vlan = ar8xxx_get_dot1q_vlan;
575749cac13SAdrian Chadd 	sc->hal.arswitch_set_dot1q_vlan = ar8xxx_set_dot1q_vlan;
576f35f94f4SAdrian Chadd 	sc->hal.arswitch_flush_dot1q_vlan = ar8xxx_flush_dot1q_vlan;
577f35f94f4SAdrian Chadd 	sc->hal.arswitch_purge_dot1q_vlan = ar8xxx_purge_dot1q_vlan;
578749cac13SAdrian Chadd 	sc->hal.arswitch_get_port_vlan = ar8xxx_get_port_vlan;
579749cac13SAdrian Chadd 	sc->hal.arswitch_set_port_vlan = ar8xxx_set_port_vlan;
580749cac13SAdrian Chadd 
5814ff2f60dSAdrian Chadd 	sc->hal.arswitch_atu_flush = ar8xxx_atu_flush;
58262042c97SAdrian Chadd 	sc->hal.arswitch_atu_flush_port = ar8xxx_atu_flush_port;
58362042c97SAdrian Chadd 	sc->hal.arswitch_atu_learn_default = ar8xxx_atu_learn_default;
58462042c97SAdrian Chadd 	sc->hal.arswitch_atu_fetch_table = ar8xxx_atu_fetch_table;
585749cac13SAdrian Chadd 
58678549b94SAdrian Chadd 	sc->hal.arswitch_phy_read = arswitch_readphy_internal;
58778549b94SAdrian Chadd 	sc->hal.arswitch_phy_write = arswitch_writephy_internal;
588ddbc4420SAdrian Chadd 
589a043e8c7SAdrian Chadd 	/*
590a043e8c7SAdrian Chadd 	 * Attach switch related functions
591a043e8c7SAdrian Chadd 	 */
59227a2ecaaSAdrian Chadd 	if (AR8X16_IS_SWITCH(sc, AR7240))
59327a2ecaaSAdrian Chadd 		ar7240_attach(sc);
594b2152161SAdrian Chadd 	else if (AR8X16_IS_SWITCH(sc, AR9340))
595b2152161SAdrian Chadd 		ar9340_attach(sc);
59627a2ecaaSAdrian Chadd 	else if (AR8X16_IS_SWITCH(sc, AR8216))
597a043e8c7SAdrian Chadd 		ar8216_attach(sc);
598a043e8c7SAdrian Chadd 	else if (AR8X16_IS_SWITCH(sc, AR8226))
599a043e8c7SAdrian Chadd 		ar8226_attach(sc);
600a043e8c7SAdrian Chadd 	else if (AR8X16_IS_SWITCH(sc, AR8316))
601a043e8c7SAdrian Chadd 		ar8316_attach(sc);
602482d268dSAdrian Chadd 	else if (AR8X16_IS_SWITCH(sc, AR8327))
603482d268dSAdrian Chadd 		ar8327_attach(sc);
60478549b94SAdrian Chadd 	else {
6051b334c8bSAdrian Chadd 		DPRINTF(sc, ARSWITCH_DBG_ANY,
6061b334c8bSAdrian Chadd 		    "%s: unknown switch (%d)?\n", __func__, sc->sc_switchtype);
607a043e8c7SAdrian Chadd 		return (ENXIO);
60878549b94SAdrian Chadd 	}
609a043e8c7SAdrian Chadd 
610b9f07b86SLuiz Otavio O Souza 	/* Common defaults. */
611a043e8c7SAdrian Chadd 	sc->info.es_nports = 5; /* XXX technically 6, but 6th not used */
612a043e8c7SAdrian Chadd 
613a043e8c7SAdrian Chadd 	/* XXX Defaults for externally connected AR8316 */
614a043e8c7SAdrian Chadd 	sc->numphys = 4;
615a043e8c7SAdrian Chadd 	sc->phy4cpu = 1;
616a043e8c7SAdrian Chadd 	sc->is_rgmii = 1;
617a043e8c7SAdrian Chadd 	sc->is_gmii = 0;
618b2152161SAdrian Chadd 	sc->is_mii = 0;
619a043e8c7SAdrian Chadd 
620a043e8c7SAdrian Chadd 	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
621a043e8c7SAdrian Chadd 	    "numphys", &sc->numphys);
622a043e8c7SAdrian Chadd 	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
623a043e8c7SAdrian Chadd 	    "phy4cpu", &sc->phy4cpu);
624a043e8c7SAdrian Chadd 	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
625a043e8c7SAdrian Chadd 	    "is_rgmii", &sc->is_rgmii);
626a043e8c7SAdrian Chadd 	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
627a043e8c7SAdrian Chadd 	    "is_gmii", &sc->is_gmii);
628b2152161SAdrian Chadd 	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
629b2152161SAdrian Chadd 	    "is_mii", &sc->is_mii);
630a043e8c7SAdrian Chadd 
631b9f07b86SLuiz Otavio O Souza 	if (sc->numphys > AR8X16_NUM_PHYS)
632b9f07b86SLuiz Otavio O Souza 		sc->numphys = AR8X16_NUM_PHYS;
633a043e8c7SAdrian Chadd 
634b9f07b86SLuiz Otavio O Souza 	/* Reset the switch. */
63578549b94SAdrian Chadd 	if (arswitch_reset(dev)) {
6361b334c8bSAdrian Chadd 		DPRINTF(sc, ARSWITCH_DBG_ANY,
6371b334c8bSAdrian Chadd 		    "%s: arswitch_reset: failed\n", __func__);
638b9f07b86SLuiz Otavio O Souza 		return (ENXIO);
63978549b94SAdrian Chadd 	}
640a043e8c7SAdrian Chadd 
641b2152161SAdrian Chadd 	err = sc->hal.arswitch_hw_setup(sc);
6421b334c8bSAdrian Chadd 	if (err != 0) {
6431b334c8bSAdrian Chadd 		DPRINTF(sc, ARSWITCH_DBG_ANY,
6441b334c8bSAdrian Chadd 		    "%s: hw_setup: err=%d\n", __func__, err);
645b2152161SAdrian Chadd 		return (err);
6461b334c8bSAdrian Chadd 	}
647b2152161SAdrian Chadd 
648a043e8c7SAdrian Chadd 	err = sc->hal.arswitch_hw_global_setup(sc);
6491b334c8bSAdrian Chadd 	if (err != 0) {
6501b334c8bSAdrian Chadd 		DPRINTF(sc, ARSWITCH_DBG_ANY,
6511b334c8bSAdrian Chadd 		    "%s: hw_global_setup: err=%d\n", __func__, err);
652a043e8c7SAdrian Chadd 		return (err);
6531b334c8bSAdrian Chadd 	}
654a043e8c7SAdrian Chadd 
65562042c97SAdrian Chadd 	/*
65662042c97SAdrian Chadd 	 * Configure the default address table learning parameters for this
65762042c97SAdrian Chadd 	 * switch.
65862042c97SAdrian Chadd 	 */
65962042c97SAdrian Chadd 	err = sc->hal.arswitch_atu_learn_default(sc);
66062042c97SAdrian Chadd 	if (err != 0) {
66162042c97SAdrian Chadd 		DPRINTF(sc, ARSWITCH_DBG_ANY,
66262042c97SAdrian Chadd 		    "%s: atu_learn_default: err=%d\n", __func__, err);
66362042c97SAdrian Chadd 		return (err);
66462042c97SAdrian Chadd 	}
66562042c97SAdrian Chadd 
666b9f07b86SLuiz Otavio O Souza 	/* Initialize the switch ports. */
667df892897SAdrian Chadd 	for (port = 0; port <= sc->numphys; port++) {
668ddbc4420SAdrian Chadd 		sc->hal.arswitch_port_init(sc, port);
669df892897SAdrian Chadd 	}
670b9f07b86SLuiz Otavio O Souza 
671a043e8c7SAdrian Chadd 	/*
672a043e8c7SAdrian Chadd 	 * Attach the PHYs and complete the bus enumeration.
673a043e8c7SAdrian Chadd 	 */
674a043e8c7SAdrian Chadd 	err = arswitch_attach_phys(sc);
6751b334c8bSAdrian Chadd 	if (err != 0) {
6761b334c8bSAdrian Chadd 		DPRINTF(sc, ARSWITCH_DBG_ANY,
6771b334c8bSAdrian Chadd 		    "%s: attach_phys: err=%d\n", __func__, err);
678a043e8c7SAdrian Chadd 		return (err);
6791b334c8bSAdrian Chadd 	}
680a043e8c7SAdrian Chadd 
681b9f07b86SLuiz Otavio O Souza 	/* Default to ingress filters off. */
682b9f07b86SLuiz Otavio O Souza 	err = arswitch_set_vlan_mode(sc, 0);
6831b334c8bSAdrian Chadd 	if (err != 0) {
6841b334c8bSAdrian Chadd 		DPRINTF(sc, ARSWITCH_DBG_ANY,
6851b334c8bSAdrian Chadd 		    "%s: set_vlan_mode: err=%d\n", __func__, err);
686b9f07b86SLuiz Otavio O Souza 		return (err);
6871b334c8bSAdrian Chadd 	}
688b9f07b86SLuiz Otavio O Souza 
689a043e8c7SAdrian Chadd 	bus_generic_probe(dev);
690a043e8c7SAdrian Chadd 	bus_enumerate_hinted_children(dev);
691a043e8c7SAdrian Chadd 	err = bus_generic_attach(dev);
6921b334c8bSAdrian Chadd 	if (err != 0) {
6931b334c8bSAdrian Chadd 		DPRINTF(sc, ARSWITCH_DBG_ANY,
6941b334c8bSAdrian Chadd 		    "%s: bus_generic_attach: err=%d\n", __func__, err);
695a043e8c7SAdrian Chadd 		return (err);
6961b334c8bSAdrian Chadd 	}
697a043e8c7SAdrian Chadd 
698a043e8c7SAdrian Chadd 	callout_init_mtx(&sc->callout_tick, &sc->sc_mtx, 0);
699454d507aSAleksandr Rybalko 
700454d507aSAleksandr Rybalko 	ARSWITCH_LOCK(sc);
701a043e8c7SAdrian Chadd 	arswitch_tick(sc);
702454d507aSAleksandr Rybalko 	ARSWITCH_UNLOCK(sc);
703a043e8c7SAdrian Chadd 
704a043e8c7SAdrian Chadd 	return (err);
705a043e8c7SAdrian Chadd }
706a043e8c7SAdrian Chadd 
707a043e8c7SAdrian Chadd static int
708a043e8c7SAdrian Chadd arswitch_detach(device_t dev)
709a043e8c7SAdrian Chadd {
710a043e8c7SAdrian Chadd 	struct arswitch_softc *sc = device_get_softc(dev);
711a043e8c7SAdrian Chadd 	int i;
712a043e8c7SAdrian Chadd 
713a043e8c7SAdrian Chadd 	callout_drain(&sc->callout_tick);
714a043e8c7SAdrian Chadd 
715a043e8c7SAdrian Chadd 	for (i=0; i < sc->numphys; i++) {
716a043e8c7SAdrian Chadd 		if (sc->miibus[i] != NULL)
717a043e8c7SAdrian Chadd 			device_delete_child(dev, sc->miibus[i]);
718a043e8c7SAdrian Chadd 		if (sc->ifp[i] != NULL)
719a043e8c7SAdrian Chadd 			if_free(sc->ifp[i]);
720a043e8c7SAdrian Chadd 		free(sc->ifname[i], M_DEVBUF);
721a043e8c7SAdrian Chadd 	}
722a043e8c7SAdrian Chadd 
72362042c97SAdrian Chadd 	free(sc->atu.entries, M_DEVBUF);
72462042c97SAdrian Chadd 
725a043e8c7SAdrian Chadd 	bus_generic_detach(dev);
726a043e8c7SAdrian Chadd 	mtx_destroy(&sc->sc_mtx);
727a043e8c7SAdrian Chadd 
728a043e8c7SAdrian Chadd 	return (0);
729a043e8c7SAdrian Chadd }
730a043e8c7SAdrian Chadd 
731a043e8c7SAdrian Chadd /*
732a043e8c7SAdrian Chadd  * Convert PHY number to port number. PHY0 is connected to port 1, PHY1 to
733a043e8c7SAdrian Chadd  * port 2, etc.
734a043e8c7SAdrian Chadd  */
735a043e8c7SAdrian Chadd static inline int
736a043e8c7SAdrian Chadd arswitch_portforphy(int phy)
737a043e8c7SAdrian Chadd {
738a043e8c7SAdrian Chadd 	return (phy+1);
739a043e8c7SAdrian Chadd }
740a043e8c7SAdrian Chadd 
741a043e8c7SAdrian Chadd static inline struct mii_data *
742a043e8c7SAdrian Chadd arswitch_miiforport(struct arswitch_softc *sc, int port)
743a043e8c7SAdrian Chadd {
744a043e8c7SAdrian Chadd 	int phy = port-1;
745a043e8c7SAdrian Chadd 
746a043e8c7SAdrian Chadd 	if (phy < 0 || phy >= sc->numphys)
747a043e8c7SAdrian Chadd 		return (NULL);
748a043e8c7SAdrian Chadd 	return (device_get_softc(sc->miibus[phy]));
749a043e8c7SAdrian Chadd }
750a043e8c7SAdrian Chadd 
751a043e8c7SAdrian Chadd static inline struct ifnet *
752a043e8c7SAdrian Chadd arswitch_ifpforport(struct arswitch_softc *sc, int port)
753a043e8c7SAdrian Chadd {
754a043e8c7SAdrian Chadd 	int phy = port-1;
755a043e8c7SAdrian Chadd 
756a043e8c7SAdrian Chadd 	if (phy < 0 || phy >= sc->numphys)
757a043e8c7SAdrian Chadd 		return (NULL);
758a043e8c7SAdrian Chadd 	return (sc->ifp[phy]);
759a043e8c7SAdrian Chadd }
760a043e8c7SAdrian Chadd 
761a043e8c7SAdrian Chadd /*
762a043e8c7SAdrian Chadd  * Convert port status to ifmedia.
763a043e8c7SAdrian Chadd  */
764a043e8c7SAdrian Chadd static void
765a043e8c7SAdrian Chadd arswitch_update_ifmedia(int portstatus, u_int *media_status, u_int *media_active)
766a043e8c7SAdrian Chadd {
767a043e8c7SAdrian Chadd 	*media_active = IFM_ETHER;
768a043e8c7SAdrian Chadd 	*media_status = IFM_AVALID;
769a043e8c7SAdrian Chadd 
770a043e8c7SAdrian Chadd 	if ((portstatus & AR8X16_PORT_STS_LINK_UP) != 0)
771a043e8c7SAdrian Chadd 		*media_status |= IFM_ACTIVE;
772a043e8c7SAdrian Chadd 	else {
773a043e8c7SAdrian Chadd 		*media_active |= IFM_NONE;
774a043e8c7SAdrian Chadd 		return;
775a043e8c7SAdrian Chadd 	}
776a043e8c7SAdrian Chadd 	switch (portstatus & AR8X16_PORT_STS_SPEED_MASK) {
777a043e8c7SAdrian Chadd 	case AR8X16_PORT_STS_SPEED_10:
778a043e8c7SAdrian Chadd 		*media_active |= IFM_10_T;
779a043e8c7SAdrian Chadd 		break;
780a043e8c7SAdrian Chadd 	case AR8X16_PORT_STS_SPEED_100:
781a043e8c7SAdrian Chadd 		*media_active |= IFM_100_TX;
782a043e8c7SAdrian Chadd 		break;
783a043e8c7SAdrian Chadd 	case AR8X16_PORT_STS_SPEED_1000:
784a043e8c7SAdrian Chadd 		*media_active |= IFM_1000_T;
785a043e8c7SAdrian Chadd 		break;
786a043e8c7SAdrian Chadd 	}
787a043e8c7SAdrian Chadd 	if ((portstatus & AR8X16_PORT_STS_DUPLEX) == 0)
788a043e8c7SAdrian Chadd 		*media_active |= IFM_FDX;
789a043e8c7SAdrian Chadd 	else
790a043e8c7SAdrian Chadd 		*media_active |= IFM_HDX;
791a043e8c7SAdrian Chadd 	if ((portstatus & AR8X16_PORT_STS_TXFLOW) != 0)
792a043e8c7SAdrian Chadd 		*media_active |= IFM_ETH_TXPAUSE;
793a043e8c7SAdrian Chadd 	if ((portstatus & AR8X16_PORT_STS_RXFLOW) != 0)
794a043e8c7SAdrian Chadd 		*media_active |= IFM_ETH_RXPAUSE;
795a043e8c7SAdrian Chadd }
796a043e8c7SAdrian Chadd 
797a043e8c7SAdrian Chadd /*
798a043e8c7SAdrian Chadd  * Poll the status for all PHYs.  We're using the switch port status because
799a043e8c7SAdrian Chadd  * thats a lot quicker to read than talking to all the PHYs.  Care must be
800a043e8c7SAdrian Chadd  * taken that the resulting ifmedia_active is identical to what the PHY will
801a043e8c7SAdrian Chadd  * compute, or gratuitous link status changes will occur whenever the PHYs
802a043e8c7SAdrian Chadd  * update function is called.
803a043e8c7SAdrian Chadd  */
804a043e8c7SAdrian Chadd static void
805a043e8c7SAdrian Chadd arswitch_miipollstat(struct arswitch_softc *sc)
806a043e8c7SAdrian Chadd {
807a043e8c7SAdrian Chadd 	int i;
808a043e8c7SAdrian Chadd 	struct mii_data *mii;
809a043e8c7SAdrian Chadd 	struct mii_softc *miisc;
810a043e8c7SAdrian Chadd 	int portstatus;
8114ff2f60dSAdrian Chadd 	int port_flap = 0;
812a043e8c7SAdrian Chadd 
813454d507aSAleksandr Rybalko 	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
814454d507aSAleksandr Rybalko 
815a043e8c7SAdrian Chadd 	for (i = 0; i < sc->numphys; i++) {
816a043e8c7SAdrian Chadd 		if (sc->miibus[i] == NULL)
817a043e8c7SAdrian Chadd 			continue;
818a043e8c7SAdrian Chadd 		mii = device_get_softc(sc->miibus[i]);
819ddbc4420SAdrian Chadd 		/* XXX This would be nice to have abstracted out to be per-chip */
820ddbc4420SAdrian Chadd 		/* AR8327/AR8337 has a different register base */
821ddbc4420SAdrian Chadd 		if (AR8X16_IS_SWITCH(sc, AR8327))
822ddbc4420SAdrian Chadd 			portstatus = arswitch_readreg(sc->sc_dev,
823ddbc4420SAdrian Chadd 			    AR8327_REG_PORT_STATUS(arswitch_portforphy(i)));
824ddbc4420SAdrian Chadd 		else
825a043e8c7SAdrian Chadd 			portstatus = arswitch_readreg(sc->sc_dev,
826a043e8c7SAdrian Chadd 			    AR8X16_REG_PORT_STS(arswitch_portforphy(i)));
8271b334c8bSAdrian Chadd #if 1
8281b334c8bSAdrian Chadd 		DPRINTF(sc, ARSWITCH_DBG_POLL, "p[%d]=0x%08x (%b)\n",
829b2152161SAdrian Chadd 		    i,
830a043e8c7SAdrian Chadd 		    portstatus,
8311b334c8bSAdrian Chadd 		    portstatus,
832a043e8c7SAdrian Chadd 		    "\20\3TXMAC\4RXMAC\5TXFLOW\6RXFLOW\7"
833a043e8c7SAdrian Chadd 		    "DUPLEX\11LINK_UP\12LINK_AUTO\13LINK_PAUSE");
834a043e8c7SAdrian Chadd #endif
8354ff2f60dSAdrian Chadd 		/*
8364ff2f60dSAdrian Chadd 		 * If the current status is down, but we have a link
8374ff2f60dSAdrian Chadd 		 * status showing up, we need to do an ATU flush.
8384ff2f60dSAdrian Chadd 		 */
8394ff2f60dSAdrian Chadd 		if ((mii->mii_media_status & IFM_ACTIVE) == 0 &&
8404ff2f60dSAdrian Chadd 		    (portstatus & AR8X16_PORT_STS_LINK_UP) != 0) {
8414ff2f60dSAdrian Chadd 			device_printf(sc->sc_dev, "%s: port %d: port -> UP\n",
8424ff2f60dSAdrian Chadd 			    __func__,
8434ff2f60dSAdrian Chadd 			    i);
8444ff2f60dSAdrian Chadd 			port_flap = 1;
8454ff2f60dSAdrian Chadd 		}
8464ff2f60dSAdrian Chadd 		/*
8474ff2f60dSAdrian Chadd 		 * and maybe if a port goes up->down?
8484ff2f60dSAdrian Chadd 		 */
8494ff2f60dSAdrian Chadd 		if ((mii->mii_media_status & IFM_ACTIVE) != 0 &&
8504ff2f60dSAdrian Chadd 		    (portstatus & AR8X16_PORT_STS_LINK_UP) == 0) {
8514ff2f60dSAdrian Chadd 			device_printf(sc->sc_dev, "%s: port %d: port -> DOWN\n",
8524ff2f60dSAdrian Chadd 			    __func__,
8534ff2f60dSAdrian Chadd 			    i);
8544ff2f60dSAdrian Chadd 			port_flap = 1;
8554ff2f60dSAdrian Chadd 		}
856a043e8c7SAdrian Chadd 		arswitch_update_ifmedia(portstatus, &mii->mii_media_status,
857a043e8c7SAdrian Chadd 		    &mii->mii_media_active);
858a043e8c7SAdrian Chadd 		LIST_FOREACH(miisc, &mii->mii_phys, mii_list) {
859a043e8c7SAdrian Chadd 			if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) !=
860a043e8c7SAdrian Chadd 			    miisc->mii_inst)
861a043e8c7SAdrian Chadd 				continue;
862a043e8c7SAdrian Chadd 			mii_phy_update(miisc, MII_POLLSTAT);
863a043e8c7SAdrian Chadd 		}
864a043e8c7SAdrian Chadd 	}
8654ff2f60dSAdrian Chadd 
8664ff2f60dSAdrian Chadd 	/* If a port went from down->up, flush the ATU */
8674ff2f60dSAdrian Chadd 	if (port_flap)
8684ff2f60dSAdrian Chadd 		sc->hal.arswitch_atu_flush(sc);
869a043e8c7SAdrian Chadd }
870a043e8c7SAdrian Chadd 
871a043e8c7SAdrian Chadd static void
872a043e8c7SAdrian Chadd arswitch_tick(void *arg)
873a043e8c7SAdrian Chadd {
874a043e8c7SAdrian Chadd 	struct arswitch_softc *sc = arg;
875a043e8c7SAdrian Chadd 
876a043e8c7SAdrian Chadd 	arswitch_miipollstat(sc);
877a043e8c7SAdrian Chadd 	callout_reset(&sc->callout_tick, hz, arswitch_tick, sc);
878a043e8c7SAdrian Chadd }
879a043e8c7SAdrian Chadd 
880454d507aSAleksandr Rybalko static void
881454d507aSAleksandr Rybalko arswitch_lock(device_t dev)
882454d507aSAleksandr Rybalko {
883454d507aSAleksandr Rybalko 	struct arswitch_softc *sc = device_get_softc(dev);
884454d507aSAleksandr Rybalko 
885454d507aSAleksandr Rybalko 	ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
886454d507aSAleksandr Rybalko 	ARSWITCH_LOCK(sc);
887454d507aSAleksandr Rybalko }
888454d507aSAleksandr Rybalko 
889454d507aSAleksandr Rybalko static void
890454d507aSAleksandr Rybalko arswitch_unlock(device_t dev)
891454d507aSAleksandr Rybalko {
892454d507aSAleksandr Rybalko 	struct arswitch_softc *sc = device_get_softc(dev);
893454d507aSAleksandr Rybalko 
894454d507aSAleksandr Rybalko 	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
895454d507aSAleksandr Rybalko 	ARSWITCH_UNLOCK(sc);
896454d507aSAleksandr Rybalko }
897454d507aSAleksandr Rybalko 
898a043e8c7SAdrian Chadd static etherswitch_info_t *
899a043e8c7SAdrian Chadd arswitch_getinfo(device_t dev)
900a043e8c7SAdrian Chadd {
901a043e8c7SAdrian Chadd 	struct arswitch_softc *sc = device_get_softc(dev);
902a043e8c7SAdrian Chadd 
903a043e8c7SAdrian Chadd 	return (&sc->info);
904a043e8c7SAdrian Chadd }
905a043e8c7SAdrian Chadd 
906a043e8c7SAdrian Chadd static int
907a9ad4222SAdrian Chadd ar8xxx_port_vlan_get(struct arswitch_softc *sc, etherswitch_port_t *p)
908a043e8c7SAdrian Chadd {
909b9f07b86SLuiz Otavio O Souza 	uint32_t reg;
910b9f07b86SLuiz Otavio O Souza 
911b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK(sc);
912b9f07b86SLuiz Otavio O Souza 
913b9f07b86SLuiz Otavio O Souza 	/* Retrieve the PVID. */
9146dcbabd7SAdrian Chadd 	sc->hal.arswitch_vlan_get_pvid(sc, p->es_port, &p->es_pvid);
915b9f07b86SLuiz Otavio O Souza 
916b9f07b86SLuiz Otavio O Souza 	/* Port flags. */
917b9f07b86SLuiz Otavio O Souza 	reg = arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_CTRL(p->es_port));
918b9f07b86SLuiz Otavio O Souza 	if (reg & AR8X16_PORT_CTRL_DOUBLE_TAG)
919b9f07b86SLuiz Otavio O Souza 		p->es_flags |= ETHERSWITCH_PORT_DOUBLE_TAG;
920b9f07b86SLuiz Otavio O Souza 	reg >>= AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT;
921b9f07b86SLuiz Otavio O Souza 	if ((reg & 0x3) == AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_ADD)
922b9f07b86SLuiz Otavio O Souza 		p->es_flags |= ETHERSWITCH_PORT_ADDTAG;
923b9f07b86SLuiz Otavio O Souza 	if ((reg & 0x3) == AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_STRIP)
924b9f07b86SLuiz Otavio O Souza 		p->es_flags |= ETHERSWITCH_PORT_STRIPTAG;
925b9f07b86SLuiz Otavio O Souza 	ARSWITCH_UNLOCK(sc);
926a043e8c7SAdrian Chadd 
927a9ad4222SAdrian Chadd 	return (0);
928a9ad4222SAdrian Chadd }
929a9ad4222SAdrian Chadd 
930a9ad4222SAdrian Chadd static int
931749cac13SAdrian Chadd arswitch_is_cpuport(struct arswitch_softc *sc, int port)
932749cac13SAdrian Chadd {
933749cac13SAdrian Chadd 
934749cac13SAdrian Chadd 	return ((port == AR8X16_PORT_CPU) ||
935749cac13SAdrian Chadd 	    ((AR8X16_IS_SWITCH(sc, AR8327) &&
936749cac13SAdrian Chadd 	      port == AR8327_PORT_GMAC6)));
937749cac13SAdrian Chadd }
938749cac13SAdrian Chadd 
939749cac13SAdrian Chadd static int
940a9ad4222SAdrian Chadd arswitch_getport(device_t dev, etherswitch_port_t *p)
941a9ad4222SAdrian Chadd {
942a9ad4222SAdrian Chadd 	struct arswitch_softc *sc;
943a9ad4222SAdrian Chadd 	struct mii_data *mii;
944a9ad4222SAdrian Chadd 	struct ifmediareq *ifmr;
945a9ad4222SAdrian Chadd 	int err;
946a9ad4222SAdrian Chadd 
947a9ad4222SAdrian Chadd 	sc = device_get_softc(dev);
948749cac13SAdrian Chadd 	/* XXX +1 is for AR8327; should make this configurable! */
949749cac13SAdrian Chadd 	if (p->es_port < 0 || p->es_port > sc->info.es_nports)
950a9ad4222SAdrian Chadd 		return (ENXIO);
951a9ad4222SAdrian Chadd 
952a9ad4222SAdrian Chadd 	err = sc->hal.arswitch_port_vlan_get(sc, p);
953a9ad4222SAdrian Chadd 	if (err != 0)
954a9ad4222SAdrian Chadd 		return (err);
955a9ad4222SAdrian Chadd 
956a043e8c7SAdrian Chadd 	mii = arswitch_miiforport(sc, p->es_port);
957749cac13SAdrian Chadd 	if (arswitch_is_cpuport(sc, p->es_port)) {
958a043e8c7SAdrian Chadd 		/* fill in fixed values for CPU port */
959a9ad4222SAdrian Chadd 		/* XXX is this valid in all cases? */
960f47857dcSAdrian Chadd 		p->es_flags |= ETHERSWITCH_PORT_CPU;
961b9f07b86SLuiz Otavio O Souza 		ifmr = &p->es_ifmr;
962a043e8c7SAdrian Chadd 		ifmr->ifm_count = 0;
963a043e8c7SAdrian Chadd 		ifmr->ifm_current = ifmr->ifm_active =
964a043e8c7SAdrian Chadd 		    IFM_ETHER | IFM_1000_T | IFM_FDX;
965a043e8c7SAdrian Chadd 		ifmr->ifm_mask = 0;
966a043e8c7SAdrian Chadd 		ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
967a043e8c7SAdrian Chadd 	} else if (mii != NULL) {
968a043e8c7SAdrian Chadd 		err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr,
969a043e8c7SAdrian Chadd 		    &mii->mii_media, SIOCGIFMEDIA);
970a043e8c7SAdrian Chadd 		if (err)
971a043e8c7SAdrian Chadd 			return (err);
972a043e8c7SAdrian Chadd 	} else {
973a043e8c7SAdrian Chadd 		return (ENXIO);
974a043e8c7SAdrian Chadd 	}
975c94dc808SAdrian Chadd 
976c94dc808SAdrian Chadd 	if (!arswitch_is_cpuport(sc, p->es_port) &&
977c94dc808SAdrian Chadd 	    AR8X16_IS_SWITCH(sc, AR8327)) {
978c94dc808SAdrian Chadd 		int led;
979c94dc808SAdrian Chadd 		p->es_nleds = 3;
980c94dc808SAdrian Chadd 
981c94dc808SAdrian Chadd 		for (led = 0; led < p->es_nleds; led++)
982c94dc808SAdrian Chadd 		{
983c94dc808SAdrian Chadd 			int style;
984c94dc808SAdrian Chadd 			uint32_t val;
985c94dc808SAdrian Chadd 
986c94dc808SAdrian Chadd 			/* Find the right style enum for our pattern */
987c94dc808SAdrian Chadd 			val = arswitch_readreg(dev,
988c94dc808SAdrian Chadd 			    ar8327_led_mapping[p->es_port-1][led].reg);
989c94dc808SAdrian Chadd 			val = (val>>ar8327_led_mapping[p->es_port-1][led].shift)&0x03;
990c94dc808SAdrian Chadd 
991c94dc808SAdrian Chadd 			for (style = 0; style < ETHERSWITCH_PORT_LED_MAX; style++)
992c94dc808SAdrian Chadd 			{
993c94dc808SAdrian Chadd 				if (led_pattern_table[style] == val) break;
994c94dc808SAdrian Chadd 			}
995c94dc808SAdrian Chadd 
996c94dc808SAdrian Chadd 			/* can't happen */
997c94dc808SAdrian Chadd 			if (style == ETHERSWITCH_PORT_LED_MAX)
998c94dc808SAdrian Chadd 				style = ETHERSWITCH_PORT_LED_DEFAULT;
999c94dc808SAdrian Chadd 
1000c94dc808SAdrian Chadd 			p->es_led[led] = style;
1001c94dc808SAdrian Chadd 		}
1002c94dc808SAdrian Chadd 	} else
1003c94dc808SAdrian Chadd 	{
1004c94dc808SAdrian Chadd 		p->es_nleds = 0;
1005c94dc808SAdrian Chadd 	}
1006c94dc808SAdrian Chadd 
1007a043e8c7SAdrian Chadd 	return (0);
1008a043e8c7SAdrian Chadd }
1009a043e8c7SAdrian Chadd 
1010a043e8c7SAdrian Chadd static int
1011a9ad4222SAdrian Chadd ar8xxx_port_vlan_setup(struct arswitch_softc *sc, etherswitch_port_t *p)
1012a043e8c7SAdrian Chadd {
1013b9f07b86SLuiz Otavio O Souza 	uint32_t reg;
1014a9ad4222SAdrian Chadd 	int err;
1015a043e8c7SAdrian Chadd 
1016b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK(sc);
1017a9ad4222SAdrian Chadd 
1018b9f07b86SLuiz Otavio O Souza 	/* Set the PVID. */
1019b9f07b86SLuiz Otavio O Souza 	if (p->es_pvid != 0)
10206dcbabd7SAdrian Chadd 		sc->hal.arswitch_vlan_set_pvid(sc, p->es_port, p->es_pvid);
1021b9f07b86SLuiz Otavio O Souza 
1022b9f07b86SLuiz Otavio O Souza 	/* Mutually exclusive. */
1023b9f07b86SLuiz Otavio O Souza 	if (p->es_flags & ETHERSWITCH_PORT_ADDTAG &&
1024b9f07b86SLuiz Otavio O Souza 	    p->es_flags & ETHERSWITCH_PORT_STRIPTAG) {
1025b9f07b86SLuiz Otavio O Souza 		ARSWITCH_UNLOCK(sc);
1026b9f07b86SLuiz Otavio O Souza 		return (EINVAL);
1027b9f07b86SLuiz Otavio O Souza 	}
1028b9f07b86SLuiz Otavio O Souza 
1029b9f07b86SLuiz Otavio O Souza 	reg = 0;
1030b9f07b86SLuiz Otavio O Souza 	if (p->es_flags & ETHERSWITCH_PORT_DOUBLE_TAG)
1031b9f07b86SLuiz Otavio O Souza 		reg |= AR8X16_PORT_CTRL_DOUBLE_TAG;
1032b9f07b86SLuiz Otavio O Souza 	if (p->es_flags & ETHERSWITCH_PORT_ADDTAG)
1033b9f07b86SLuiz Otavio O Souza 		reg |= AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_ADD <<
1034b9f07b86SLuiz Otavio O Souza 		    AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT;
1035b9f07b86SLuiz Otavio O Souza 	if (p->es_flags & ETHERSWITCH_PORT_STRIPTAG)
1036b9f07b86SLuiz Otavio O Souza 		reg |= AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_STRIP <<
1037b9f07b86SLuiz Otavio O Souza 		    AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT;
1038b9f07b86SLuiz Otavio O Souza 
1039b9f07b86SLuiz Otavio O Souza 	err = arswitch_modifyreg(sc->sc_dev,
1040b9f07b86SLuiz Otavio O Souza 	    AR8X16_REG_PORT_CTRL(p->es_port),
1041b9f07b86SLuiz Otavio O Souza 	    0x3 << AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT |
1042b9f07b86SLuiz Otavio O Souza 	    AR8X16_PORT_CTRL_DOUBLE_TAG, reg);
1043b9f07b86SLuiz Otavio O Souza 
1044b9f07b86SLuiz Otavio O Souza 	ARSWITCH_UNLOCK(sc);
1045a9ad4222SAdrian Chadd 	return (err);
1046a9ad4222SAdrian Chadd }
1047a9ad4222SAdrian Chadd 
1048a9ad4222SAdrian Chadd static int
1049a9ad4222SAdrian Chadd arswitch_setport(device_t dev, etherswitch_port_t *p)
1050a9ad4222SAdrian Chadd {
1051c94dc808SAdrian Chadd 	int err, i;
1052a9ad4222SAdrian Chadd 	struct arswitch_softc *sc;
1053a9ad4222SAdrian Chadd 	struct ifmedia *ifm;
1054a9ad4222SAdrian Chadd 	struct mii_data *mii;
1055a9ad4222SAdrian Chadd 	struct ifnet *ifp;
1056a9ad4222SAdrian Chadd 
1057a9ad4222SAdrian Chadd 	sc = device_get_softc(dev);
1058749cac13SAdrian Chadd 	if (p->es_port < 0 || p->es_port > sc->info.es_nports)
1059a9ad4222SAdrian Chadd 		return (ENXIO);
1060a9ad4222SAdrian Chadd 
1061a9ad4222SAdrian Chadd 	/* Port flags. */
1062a9ad4222SAdrian Chadd 	if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
1063a9ad4222SAdrian Chadd 		err = sc->hal.arswitch_port_vlan_setup(sc, p);
1064b9f07b86SLuiz Otavio O Souza 		if (err)
1065b9f07b86SLuiz Otavio O Souza 			return (err);
1066b9f07b86SLuiz Otavio O Souza 	}
1067b9f07b86SLuiz Otavio O Souza 
1068c94dc808SAdrian Chadd 	/* Do not allow media or led changes on CPU port. */
1069749cac13SAdrian Chadd 	if (arswitch_is_cpuport(sc, p->es_port))
1070b9f07b86SLuiz Otavio O Souza 		return (0);
1071a043e8c7SAdrian Chadd 
1072c94dc808SAdrian Chadd 	if (AR8X16_IS_SWITCH(sc, AR8327))
1073c94dc808SAdrian Chadd 	{
1074c94dc808SAdrian Chadd 		for (i = 0; i < 3; i++)
1075c94dc808SAdrian Chadd 		{
1076c94dc808SAdrian Chadd 			int err;
1077c94dc808SAdrian Chadd 			err = arswitch_setled(sc, p->es_port-1, i, p->es_led[i]);
1078c94dc808SAdrian Chadd 			if (err)
1079c94dc808SAdrian Chadd 				return (err);
1080c94dc808SAdrian Chadd 		}
1081c94dc808SAdrian Chadd 	}
1082c94dc808SAdrian Chadd 
1083a043e8c7SAdrian Chadd 	mii = arswitch_miiforport(sc, p->es_port);
1084a043e8c7SAdrian Chadd 	if (mii == NULL)
1085a043e8c7SAdrian Chadd 		return (ENXIO);
1086a043e8c7SAdrian Chadd 
1087a043e8c7SAdrian Chadd 	ifp = arswitch_ifpforport(sc, p->es_port);
1088a043e8c7SAdrian Chadd 
1089a043e8c7SAdrian Chadd 	ifm = &mii->mii_media;
1090b9f07b86SLuiz Otavio O Souza 	return (ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA));
1091a043e8c7SAdrian Chadd }
1092a043e8c7SAdrian Chadd 
1093c94dc808SAdrian Chadd static int
1094c94dc808SAdrian Chadd arswitch_setled(struct arswitch_softc *sc, int phy, int led, int style)
1095c94dc808SAdrian Chadd {
1096c94dc808SAdrian Chadd 	int shift;
10976d011946SKristof Provost 	int err;
1098c94dc808SAdrian Chadd 
1099c94dc808SAdrian Chadd 	if (phy < 0 || phy > sc->numphys)
1100c94dc808SAdrian Chadd 		return EINVAL;
1101c94dc808SAdrian Chadd 
1102c94dc808SAdrian Chadd 	if (style < 0 || style > ETHERSWITCH_PORT_LED_MAX)
1103c94dc808SAdrian Chadd 		return (EINVAL);
1104c94dc808SAdrian Chadd 
11056d011946SKristof Provost 	ARSWITCH_LOCK(sc);
11066d011946SKristof Provost 
1107c94dc808SAdrian Chadd 	shift = ar8327_led_mapping[phy][led].shift;
11086d011946SKristof Provost 	err = (arswitch_modifyreg(sc->sc_dev,
1109c94dc808SAdrian Chadd 	    ar8327_led_mapping[phy][led].reg,
1110c94dc808SAdrian Chadd 	    0x03 << shift, led_pattern_table[style] << shift));
11116d011946SKristof Provost 	ARSWITCH_UNLOCK(sc);
11126d011946SKristof Provost 
11136d011946SKristof Provost 	return (err);
1114c94dc808SAdrian Chadd }
1115c94dc808SAdrian Chadd 
1116a043e8c7SAdrian Chadd static void
1117a043e8c7SAdrian Chadd arswitch_statchg(device_t dev)
1118a043e8c7SAdrian Chadd {
11191b334c8bSAdrian Chadd 	struct arswitch_softc *sc = device_get_softc(dev);
1120a043e8c7SAdrian Chadd 
11211b334c8bSAdrian Chadd 	DPRINTF(sc, ARSWITCH_DBG_POLL, "%s\n", __func__);
1122a043e8c7SAdrian Chadd }
1123a043e8c7SAdrian Chadd 
1124a043e8c7SAdrian Chadd static int
1125a043e8c7SAdrian Chadd arswitch_ifmedia_upd(struct ifnet *ifp)
1126a043e8c7SAdrian Chadd {
1127a043e8c7SAdrian Chadd 	struct arswitch_softc *sc = ifp->if_softc;
1128a043e8c7SAdrian Chadd 	struct mii_data *mii = arswitch_miiforport(sc, ifp->if_dunit);
1129a043e8c7SAdrian Chadd 
1130a043e8c7SAdrian Chadd 	if (mii == NULL)
1131a043e8c7SAdrian Chadd 		return (ENXIO);
1132a043e8c7SAdrian Chadd 	mii_mediachg(mii);
1133a043e8c7SAdrian Chadd 	return (0);
1134a043e8c7SAdrian Chadd }
1135a043e8c7SAdrian Chadd 
1136a043e8c7SAdrian Chadd static void
1137a043e8c7SAdrian Chadd arswitch_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
1138a043e8c7SAdrian Chadd {
1139a043e8c7SAdrian Chadd 	struct arswitch_softc *sc = ifp->if_softc;
1140a043e8c7SAdrian Chadd 	struct mii_data *mii = arswitch_miiforport(sc, ifp->if_dunit);
1141a043e8c7SAdrian Chadd 
11421b334c8bSAdrian Chadd 	DPRINTF(sc, ARSWITCH_DBG_POLL, "%s\n", __func__);
1143a043e8c7SAdrian Chadd 
1144a043e8c7SAdrian Chadd 	if (mii == NULL)
1145a043e8c7SAdrian Chadd 		return;
1146a043e8c7SAdrian Chadd 	mii_pollstat(mii);
1147a043e8c7SAdrian Chadd 	ifmr->ifm_active = mii->mii_media_active;
1148a043e8c7SAdrian Chadd 	ifmr->ifm_status = mii->mii_media_status;
1149a043e8c7SAdrian Chadd }
1150a043e8c7SAdrian Chadd 
1151b9f07b86SLuiz Otavio O Souza static int
1152b9f07b86SLuiz Otavio O Souza arswitch_getconf(device_t dev, etherswitch_conf_t *conf)
1153b9f07b86SLuiz Otavio O Souza {
1154b9f07b86SLuiz Otavio O Souza 	struct arswitch_softc *sc;
11552ba4bf8fSAdrian Chadd 	int ret;
1156b9f07b86SLuiz Otavio O Souza 
1157b9f07b86SLuiz Otavio O Souza 	sc = device_get_softc(dev);
1158b9f07b86SLuiz Otavio O Souza 
1159b9f07b86SLuiz Otavio O Souza 	/* Return the VLAN mode. */
1160b9f07b86SLuiz Otavio O Souza 	conf->cmd = ETHERSWITCH_CONF_VLAN_MODE;
1161b9f07b86SLuiz Otavio O Souza 	conf->vlan_mode = sc->vlan_mode;
1162b9f07b86SLuiz Otavio O Souza 
11632ba4bf8fSAdrian Chadd 	/* Return the switch ethernet address. */
11642ba4bf8fSAdrian Chadd 	ret = sc->hal.arswitch_hw_get_switch_macaddr(sc,
11652ba4bf8fSAdrian Chadd 	    &conf->switch_macaddr);
11662ba4bf8fSAdrian Chadd 	if (ret == 0) {
11672ba4bf8fSAdrian Chadd 		conf->cmd |= ETHERSWITCH_CONF_SWITCH_MACADDR;
11682ba4bf8fSAdrian Chadd 	}
11692ba4bf8fSAdrian Chadd 
1170b9f07b86SLuiz Otavio O Souza 	return (0);
1171b9f07b86SLuiz Otavio O Souza }
1172b9f07b86SLuiz Otavio O Souza 
1173b9f07b86SLuiz Otavio O Souza static int
1174b9f07b86SLuiz Otavio O Souza arswitch_setconf(device_t dev, etherswitch_conf_t *conf)
1175b9f07b86SLuiz Otavio O Souza {
1176b9f07b86SLuiz Otavio O Souza 	struct arswitch_softc *sc;
1177b9f07b86SLuiz Otavio O Souza 	int err;
1178b9f07b86SLuiz Otavio O Souza 
1179b9f07b86SLuiz Otavio O Souza 	sc = device_get_softc(dev);
1180b9f07b86SLuiz Otavio O Souza 
1181b9f07b86SLuiz Otavio O Souza 	/* Set the VLAN mode. */
1182b9f07b86SLuiz Otavio O Souza 	if (conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) {
1183b9f07b86SLuiz Otavio O Souza 		err = arswitch_set_vlan_mode(sc, conf->vlan_mode);
1184b9f07b86SLuiz Otavio O Souza 		if (err != 0)
1185b9f07b86SLuiz Otavio O Souza 			return (err);
1186b9f07b86SLuiz Otavio O Souza 	}
1187b9f07b86SLuiz Otavio O Souza 
11882ba4bf8fSAdrian Chadd 	/* TODO: Set the switch ethernet address. */
11892ba4bf8fSAdrian Chadd 
1190b9f07b86SLuiz Otavio O Souza 	return (0);
1191b9f07b86SLuiz Otavio O Souza }
1192b9f07b86SLuiz Otavio O Souza 
11936dcbabd7SAdrian Chadd static int
119462042c97SAdrian Chadd arswitch_atu_flush_all(device_t dev)
119562042c97SAdrian Chadd {
119662042c97SAdrian Chadd 	struct arswitch_softc *sc;
119762042c97SAdrian Chadd 	int err;
119862042c97SAdrian Chadd 
119962042c97SAdrian Chadd 	sc = device_get_softc(dev);
120062042c97SAdrian Chadd 	ARSWITCH_LOCK(sc);
120162042c97SAdrian Chadd 	err = sc->hal.arswitch_atu_flush(sc);
120262042c97SAdrian Chadd 	/* Invalidate cached ATU */
120362042c97SAdrian Chadd 	sc->atu.count = 0;
120462042c97SAdrian Chadd 	ARSWITCH_UNLOCK(sc);
120562042c97SAdrian Chadd 	return (err);
120662042c97SAdrian Chadd }
120762042c97SAdrian Chadd 
120862042c97SAdrian Chadd static int
120962042c97SAdrian Chadd arswitch_atu_flush_port(device_t dev, int port)
121062042c97SAdrian Chadd {
121162042c97SAdrian Chadd 	struct arswitch_softc *sc;
121262042c97SAdrian Chadd 	int err;
121362042c97SAdrian Chadd 
121462042c97SAdrian Chadd 	sc = device_get_softc(dev);
121562042c97SAdrian Chadd 	ARSWITCH_LOCK(sc);
121662042c97SAdrian Chadd 	err = sc->hal.arswitch_atu_flush_port(sc, port);
121762042c97SAdrian Chadd 	/* Invalidate cached ATU */
121862042c97SAdrian Chadd 	sc->atu.count = 0;
121962042c97SAdrian Chadd 	ARSWITCH_UNLOCK(sc);
122062042c97SAdrian Chadd 	return (err);
122162042c97SAdrian Chadd }
122262042c97SAdrian Chadd 
122362042c97SAdrian Chadd static int
122462042c97SAdrian Chadd arswitch_atu_fetch_table(device_t dev, etherswitch_atu_table_t *table)
122562042c97SAdrian Chadd {
122662042c97SAdrian Chadd 	struct arswitch_softc *sc;
122762042c97SAdrian Chadd 	int err, nitems;
122862042c97SAdrian Chadd 
122962042c97SAdrian Chadd 	sc = device_get_softc(dev);
123062042c97SAdrian Chadd 
123162042c97SAdrian Chadd 	ARSWITCH_LOCK(sc);
123262042c97SAdrian Chadd 	/* Initial setup */
123362042c97SAdrian Chadd 	nitems = 0;
123462042c97SAdrian Chadd 	err = sc->hal.arswitch_atu_fetch_table(sc, NULL, 0);
123562042c97SAdrian Chadd 
123662042c97SAdrian Chadd 	/* fetch - ideally yes we'd fetch into a separate table then switch */
12377ed08319SAdrian Chadd 	while (err == 0 && nitems < sc->atu.size) {
123862042c97SAdrian Chadd 		err = sc->hal.arswitch_atu_fetch_table(sc,
123962042c97SAdrian Chadd 		    &sc->atu.entries[nitems], 1);
124062042c97SAdrian Chadd 		if (err == 0) {
124162042c97SAdrian Chadd 			sc->atu.entries[nitems].id = nitems;
124262042c97SAdrian Chadd 			nitems++;
124362042c97SAdrian Chadd 		}
124462042c97SAdrian Chadd 	}
124562042c97SAdrian Chadd 	sc->atu.count = nitems;
124662042c97SAdrian Chadd 	ARSWITCH_UNLOCK(sc);
124762042c97SAdrian Chadd 
124862042c97SAdrian Chadd 	table->es_nitems = nitems;
124962042c97SAdrian Chadd 
125062042c97SAdrian Chadd 	return (0);
125162042c97SAdrian Chadd }
125262042c97SAdrian Chadd 
125362042c97SAdrian Chadd static int
125462042c97SAdrian Chadd arswitch_atu_fetch_table_entry(device_t dev, etherswitch_atu_entry_t *e)
125562042c97SAdrian Chadd {
125662042c97SAdrian Chadd 	struct arswitch_softc *sc;
125762042c97SAdrian Chadd 	int id;
125862042c97SAdrian Chadd 
125962042c97SAdrian Chadd 	sc = device_get_softc(dev);
126062042c97SAdrian Chadd 	id = e->id;
126162042c97SAdrian Chadd 
126262042c97SAdrian Chadd 	ARSWITCH_LOCK(sc);
126362042c97SAdrian Chadd 	if (id > sc->atu.count) {
126462042c97SAdrian Chadd 		ARSWITCH_UNLOCK(sc);
126562042c97SAdrian Chadd 		return (ENOENT);
126662042c97SAdrian Chadd 	}
126762042c97SAdrian Chadd 
126862042c97SAdrian Chadd 	memcpy(e, &sc->atu.entries[id], sizeof(*e));
126962042c97SAdrian Chadd 	ARSWITCH_UNLOCK(sc);
127062042c97SAdrian Chadd 	return (0);
127162042c97SAdrian Chadd }
127262042c97SAdrian Chadd 
127362042c97SAdrian Chadd static int
12746dcbabd7SAdrian Chadd arswitch_getvgroup(device_t dev, etherswitch_vlangroup_t *e)
12756dcbabd7SAdrian Chadd {
12766dcbabd7SAdrian Chadd 	struct arswitch_softc *sc = device_get_softc(dev);
12776dcbabd7SAdrian Chadd 
12786dcbabd7SAdrian Chadd 	return (sc->hal.arswitch_vlan_getvgroup(sc, e));
12796dcbabd7SAdrian Chadd }
12806dcbabd7SAdrian Chadd 
12816dcbabd7SAdrian Chadd static int
12826dcbabd7SAdrian Chadd arswitch_setvgroup(device_t dev, etherswitch_vlangroup_t *e)
12836dcbabd7SAdrian Chadd {
12846dcbabd7SAdrian Chadd 	struct arswitch_softc *sc = device_get_softc(dev);
12856dcbabd7SAdrian Chadd 
12866dcbabd7SAdrian Chadd 	return (sc->hal.arswitch_vlan_setvgroup(sc, e));
12876dcbabd7SAdrian Chadd }
12886dcbabd7SAdrian Chadd 
128978549b94SAdrian Chadd static int
129078549b94SAdrian Chadd arswitch_readphy(device_t dev, int phy, int reg)
129178549b94SAdrian Chadd {
129278549b94SAdrian Chadd 	struct arswitch_softc *sc = device_get_softc(dev);
129378549b94SAdrian Chadd 
129478549b94SAdrian Chadd 	return (sc->hal.arswitch_phy_read(dev, phy, reg));
129578549b94SAdrian Chadd }
129678549b94SAdrian Chadd 
129778549b94SAdrian Chadd static int
129878549b94SAdrian Chadd arswitch_writephy(device_t dev, int phy, int reg, int val)
129978549b94SAdrian Chadd {
130078549b94SAdrian Chadd 	struct arswitch_softc *sc = device_get_softc(dev);
130178549b94SAdrian Chadd 
130278549b94SAdrian Chadd 	return (sc->hal.arswitch_phy_write(dev, phy, reg, val));
130378549b94SAdrian Chadd }
130478549b94SAdrian Chadd 
1305a043e8c7SAdrian Chadd static device_method_t arswitch_methods[] = {
1306a043e8c7SAdrian Chadd 	/* Device interface */
1307a043e8c7SAdrian Chadd 	DEVMETHOD(device_probe,		arswitch_probe),
1308a043e8c7SAdrian Chadd 	DEVMETHOD(device_attach,	arswitch_attach),
1309a043e8c7SAdrian Chadd 	DEVMETHOD(device_detach,	arswitch_detach),
1310a043e8c7SAdrian Chadd 
1311a043e8c7SAdrian Chadd 	/* bus interface */
1312a043e8c7SAdrian Chadd 	DEVMETHOD(bus_add_child,	device_add_child_ordered),
1313a043e8c7SAdrian Chadd 
1314a043e8c7SAdrian Chadd 	/* MII interface */
1315a043e8c7SAdrian Chadd 	DEVMETHOD(miibus_readreg,	arswitch_readphy),
1316a043e8c7SAdrian Chadd 	DEVMETHOD(miibus_writereg,	arswitch_writephy),
1317a043e8c7SAdrian Chadd 	DEVMETHOD(miibus_statchg,	arswitch_statchg),
1318a043e8c7SAdrian Chadd 
1319a043e8c7SAdrian Chadd 	/* MDIO interface */
1320a043e8c7SAdrian Chadd 	DEVMETHOD(mdio_readreg,		arswitch_readphy),
1321a043e8c7SAdrian Chadd 	DEVMETHOD(mdio_writereg,	arswitch_writephy),
1322a043e8c7SAdrian Chadd 
1323a043e8c7SAdrian Chadd 	/* etherswitch interface */
1324454d507aSAleksandr Rybalko 	DEVMETHOD(etherswitch_lock,	arswitch_lock),
1325454d507aSAleksandr Rybalko 	DEVMETHOD(etherswitch_unlock,	arswitch_unlock),
1326a043e8c7SAdrian Chadd 	DEVMETHOD(etherswitch_getinfo,	arswitch_getinfo),
1327a043e8c7SAdrian Chadd 	DEVMETHOD(etherswitch_readreg,	arswitch_readreg),
1328a043e8c7SAdrian Chadd 	DEVMETHOD(etherswitch_writereg,	arswitch_writereg),
1329a043e8c7SAdrian Chadd 	DEVMETHOD(etherswitch_readphyreg,	arswitch_readphy),
1330a043e8c7SAdrian Chadd 	DEVMETHOD(etherswitch_writephyreg,	arswitch_writephy),
1331a043e8c7SAdrian Chadd 	DEVMETHOD(etherswitch_getport,	arswitch_getport),
1332a043e8c7SAdrian Chadd 	DEVMETHOD(etherswitch_setport,	arswitch_setport),
1333a043e8c7SAdrian Chadd 	DEVMETHOD(etherswitch_getvgroup,	arswitch_getvgroup),
1334a043e8c7SAdrian Chadd 	DEVMETHOD(etherswitch_setvgroup,	arswitch_setvgroup),
1335b9f07b86SLuiz Otavio O Souza 	DEVMETHOD(etherswitch_getconf,	arswitch_getconf),
1336b9f07b86SLuiz Otavio O Souza 	DEVMETHOD(etherswitch_setconf,	arswitch_setconf),
133762042c97SAdrian Chadd 	DEVMETHOD(etherswitch_flush_all, arswitch_atu_flush_all),
133862042c97SAdrian Chadd 	DEVMETHOD(etherswitch_flush_port, arswitch_atu_flush_port),
133962042c97SAdrian Chadd 	DEVMETHOD(etherswitch_fetch_table, arswitch_atu_fetch_table),
134062042c97SAdrian Chadd 	DEVMETHOD(etherswitch_fetch_table_entry, arswitch_atu_fetch_table_entry),
1341a043e8c7SAdrian Chadd 
1342a043e8c7SAdrian Chadd 	DEVMETHOD_END
1343a043e8c7SAdrian Chadd };
1344a043e8c7SAdrian Chadd 
1345a043e8c7SAdrian Chadd DEFINE_CLASS_0(arswitch, arswitch_driver, arswitch_methods,
1346a043e8c7SAdrian Chadd     sizeof(struct arswitch_softc));
1347a043e8c7SAdrian Chadd static devclass_t arswitch_devclass;
1348a043e8c7SAdrian Chadd 
1349a043e8c7SAdrian Chadd DRIVER_MODULE(arswitch, mdio, arswitch_driver, arswitch_devclass, 0, 0);
13503e38757dSJohn Baldwin DRIVER_MODULE(miibus, arswitch, miibus_driver, 0, 0);
1351*8933f7d6SJohn Baldwin DRIVER_MODULE(mdio, arswitch, mdio_driver, 0, 0);
1352a043e8c7SAdrian Chadd DRIVER_MODULE(etherswitch, arswitch, etherswitch_driver, etherswitch_devclass, 0, 0);
1353a043e8c7SAdrian Chadd MODULE_VERSION(arswitch, 1);
1354a043e8c7SAdrian Chadd MODULE_DEPEND(arswitch, miibus, 1, 1, 1); /* XXX which versions? */
1355a043e8c7SAdrian Chadd MODULE_DEPEND(arswitch, etherswitch, 1, 1, 1); /* XXX which versions? */
1356