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
arswitch_probe(device_t dev)94a043e8c7SAdrian Chadd arswitch_probe(device_t dev)
95a043e8c7SAdrian Chadd {
96a043e8c7SAdrian Chadd struct arswitch_softc *sc;
97a043e8c7SAdrian Chadd uint32_t id;
9854482989SMark 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) {
13554482989SMark Johnston device_set_descf(dev,
13678549b94SAdrian Chadd "Atheros %s Ethernet Switch (ver %d rev %d)",
13754482989SMark 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
arswitch_attach_phys(struct arswitch_softc * sc)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);
1532e6a8c1aSJustin Hibbits if_setsoftc(sc->ifp[phy], sc);
1542e6a8c1aSJustin Hibbits if_setflagbits(sc->ifp[phy], IFF_UP | IFF_BROADCAST |
1552e6a8c1aSJustin Hibbits IFF_DRV_RUNNING | IFF_SIMPLEX, 0);
156a043e8c7SAdrian Chadd sc->ifname[phy] = malloc(strlen(name)+1, M_DEVBUF, M_WAITOK);
157a043e8c7SAdrian Chadd bcopy(name, sc->ifname[phy], strlen(name)+1);
158a043e8c7SAdrian Chadd if_initname(sc->ifp[phy], sc->ifname[phy],
159a043e8c7SAdrian Chadd arswitch_portforphy(phy));
160a043e8c7SAdrian Chadd err = mii_attach(sc->sc_dev, &sc->miibus[phy], sc->ifp[phy],
161a043e8c7SAdrian Chadd arswitch_ifmedia_upd, arswitch_ifmedia_sts, \
162a043e8c7SAdrian Chadd BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0);
16378549b94SAdrian Chadd #if 0
164a043e8c7SAdrian Chadd DPRINTF(sc->sc_dev, "%s attached to pseudo interface %s\n",
165a043e8c7SAdrian Chadd device_get_nameunit(sc->miibus[phy]),
166a043e8c7SAdrian Chadd sc->ifp[phy]->if_xname);
16778549b94SAdrian Chadd #endif
168a043e8c7SAdrian Chadd if (err != 0) {
169a043e8c7SAdrian Chadd device_printf(sc->sc_dev,
170a043e8c7SAdrian Chadd "attaching PHY %d failed\n",
171a043e8c7SAdrian Chadd phy);
172a043e8c7SAdrian Chadd return (err);
173a043e8c7SAdrian Chadd }
174a043e8c7SAdrian Chadd
175c94dc808SAdrian Chadd if (AR8X16_IS_SWITCH(sc, AR8327)) {
176c94dc808SAdrian Chadd int led;
177c94dc808SAdrian Chadd char ledname[IFNAMSIZ+4];
178c94dc808SAdrian Chadd
179c94dc808SAdrian Chadd for (led = 0; led < 3; led++) {
180c94dc808SAdrian Chadd sprintf(ledname, "%s%dled%d", name,
181c94dc808SAdrian Chadd arswitch_portforphy(phy), led+1);
182c94dc808SAdrian Chadd sc->dev_led[phy][led].sc = sc;
183c94dc808SAdrian Chadd sc->dev_led[phy][led].phy = phy;
184c94dc808SAdrian Chadd sc->dev_led[phy][led].lednum = led;
185c94dc808SAdrian Chadd }
186c94dc808SAdrian Chadd }
187c94dc808SAdrian Chadd }
188c94dc808SAdrian Chadd return (0);
189c94dc808SAdrian Chadd }
190c94dc808SAdrian Chadd
191a043e8c7SAdrian Chadd static int
arswitch_reset(device_t dev)192b9f07b86SLuiz Otavio O Souza arswitch_reset(device_t dev)
193b9f07b86SLuiz Otavio O Souza {
194b9f07b86SLuiz Otavio O Souza
195b9f07b86SLuiz Otavio O Souza arswitch_writereg(dev, AR8X16_REG_MASK_CTRL,
196b9f07b86SLuiz Otavio O Souza AR8X16_MASK_CTRL_SOFT_RESET);
197b9f07b86SLuiz Otavio O Souza DELAY(1000);
198b9f07b86SLuiz Otavio O Souza if (arswitch_readreg(dev, AR8X16_REG_MASK_CTRL) &
199b9f07b86SLuiz Otavio O Souza AR8X16_MASK_CTRL_SOFT_RESET) {
200b9f07b86SLuiz Otavio O Souza device_printf(dev, "unable to reset switch\n");
201b9f07b86SLuiz Otavio O Souza return (-1);
202b9f07b86SLuiz Otavio O Souza }
203b9f07b86SLuiz Otavio O Souza return (0);
204b9f07b86SLuiz Otavio O Souza }
205b9f07b86SLuiz Otavio O Souza
206b9f07b86SLuiz Otavio O Souza static int
arswitch_set_vlan_mode(struct arswitch_softc * sc,uint32_t mode)207b9f07b86SLuiz Otavio O Souza arswitch_set_vlan_mode(struct arswitch_softc *sc, uint32_t mode)
208b9f07b86SLuiz Otavio O Souza {
209b9f07b86SLuiz Otavio O Souza
210b9f07b86SLuiz Otavio O Souza /* Check for invalid modes. */
211b9f07b86SLuiz Otavio O Souza if ((mode & sc->info.es_vlan_caps) != mode)
212b9f07b86SLuiz Otavio O Souza return (EINVAL);
213b9f07b86SLuiz Otavio O Souza
214b9f07b86SLuiz Otavio O Souza switch (mode) {
215b9f07b86SLuiz Otavio O Souza case ETHERSWITCH_VLAN_DOT1Q:
216b9f07b86SLuiz Otavio O Souza sc->vlan_mode = ETHERSWITCH_VLAN_DOT1Q;
217b9f07b86SLuiz Otavio O Souza break;
218b9f07b86SLuiz Otavio O Souza case ETHERSWITCH_VLAN_PORT:
219b9f07b86SLuiz Otavio O Souza sc->vlan_mode = ETHERSWITCH_VLAN_PORT;
220b9f07b86SLuiz Otavio O Souza break;
221b9f07b86SLuiz Otavio O Souza default:
222b9f07b86SLuiz Otavio O Souza sc->vlan_mode = 0;
22374b8d63dSPedro F. Giffuni }
224b9f07b86SLuiz Otavio O Souza
225b9f07b86SLuiz Otavio O Souza /* Reset VLANs. */
2266dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_init_hw(sc);
227b9f07b86SLuiz Otavio O Souza
228b9f07b86SLuiz Otavio O Souza return (0);
229b9f07b86SLuiz Otavio O Souza }
230b9f07b86SLuiz Otavio O Souza
231b9f07b86SLuiz Otavio O Souza static void
ar8xxx_port_init(struct arswitch_softc * sc,int port)232a9ad4222SAdrian Chadd ar8xxx_port_init(struct arswitch_softc *sc, int port)
233b9f07b86SLuiz Otavio O Souza {
234b9f07b86SLuiz Otavio O Souza
235b9f07b86SLuiz Otavio O Souza /* Port0 - CPU */
236df892897SAdrian Chadd if (port == AR8X16_PORT_CPU) {
237b9f07b86SLuiz Otavio O Souza arswitch_writereg(sc->sc_dev, AR8X16_REG_PORT_STS(0),
238b9f07b86SLuiz Otavio O Souza (AR8X16_IS_SWITCH(sc, AR8216) ?
239b9f07b86SLuiz Otavio O Souza AR8X16_PORT_STS_SPEED_100 : AR8X16_PORT_STS_SPEED_1000) |
240b9f07b86SLuiz Otavio O Souza (AR8X16_IS_SWITCH(sc, AR8216) ? 0 : AR8X16_PORT_STS_RXFLOW) |
241b9f07b86SLuiz Otavio O Souza (AR8X16_IS_SWITCH(sc, AR8216) ? 0 : AR8X16_PORT_STS_TXFLOW) |
242b9f07b86SLuiz Otavio O Souza AR8X16_PORT_STS_RXMAC |
243b9f07b86SLuiz Otavio O Souza AR8X16_PORT_STS_TXMAC |
244b9f07b86SLuiz Otavio O Souza AR8X16_PORT_STS_DUPLEX);
245b9f07b86SLuiz Otavio O Souza arswitch_writereg(sc->sc_dev, AR8X16_REG_PORT_CTRL(0),
246b9f07b86SLuiz Otavio O Souza arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_CTRL(0)) &
247b9f07b86SLuiz Otavio O Souza ~AR8X16_PORT_CTRL_HEADER);
248df892897SAdrian Chadd } else {
249b9f07b86SLuiz Otavio O Souza /* Set ports to auto negotiation. */
250b9f07b86SLuiz Otavio O Souza arswitch_writereg(sc->sc_dev, AR8X16_REG_PORT_STS(port),
251b9f07b86SLuiz Otavio O Souza AR8X16_PORT_STS_LINK_AUTO);
252b9f07b86SLuiz Otavio O Souza arswitch_writereg(sc->sc_dev, AR8X16_REG_PORT_CTRL(port),
253b9f07b86SLuiz Otavio O Souza arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_CTRL(port)) &
254b9f07b86SLuiz Otavio O Souza ~AR8X16_PORT_CTRL_HEADER);
255b9f07b86SLuiz Otavio O Souza }
256b9f07b86SLuiz Otavio O Souza }
257b9f07b86SLuiz Otavio O Souza
258b9f07b86SLuiz Otavio O Souza static int
ar8xxx_atu_wait_ready(struct arswitch_softc * sc)25962042c97SAdrian Chadd ar8xxx_atu_wait_ready(struct arswitch_softc *sc)
2604ff2f60dSAdrian Chadd {
2614ff2f60dSAdrian Chadd int ret;
2624ff2f60dSAdrian Chadd
26362042c97SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
26462042c97SAdrian Chadd
2654ff2f60dSAdrian Chadd ret = arswitch_waitreg(sc->sc_dev,
2664ff2f60dSAdrian Chadd AR8216_REG_ATU,
2674ff2f60dSAdrian Chadd AR8216_ATU_ACTIVE,
2684ff2f60dSAdrian Chadd 0,
2694ff2f60dSAdrian Chadd 1000);
2704ff2f60dSAdrian Chadd
27162042c97SAdrian Chadd return (ret);
27262042c97SAdrian Chadd }
27362042c97SAdrian Chadd
27462042c97SAdrian Chadd /*
27562042c97SAdrian Chadd * Flush all ATU entries.
27662042c97SAdrian Chadd */
27762042c97SAdrian Chadd static int
ar8xxx_atu_flush(struct arswitch_softc * sc)27862042c97SAdrian Chadd ar8xxx_atu_flush(struct arswitch_softc *sc)
27962042c97SAdrian Chadd {
28062042c97SAdrian Chadd int ret;
28162042c97SAdrian Chadd
28262042c97SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
28362042c97SAdrian Chadd
28462042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: flushing all ports\n", __func__);
28562042c97SAdrian Chadd
28662042c97SAdrian Chadd ret = ar8xxx_atu_wait_ready(sc);
2874ff2f60dSAdrian Chadd if (ret)
2884ff2f60dSAdrian Chadd device_printf(sc->sc_dev, "%s: waitreg failed\n", __func__);
2894ff2f60dSAdrian Chadd
2904ff2f60dSAdrian Chadd if (!ret)
2914ff2f60dSAdrian Chadd arswitch_writereg(sc->sc_dev,
2924ff2f60dSAdrian Chadd AR8216_REG_ATU,
2932c6ceccaSAdrian Chadd AR8216_ATU_OP_FLUSH | AR8216_ATU_ACTIVE);
2944ff2f60dSAdrian Chadd
2954ff2f60dSAdrian Chadd return (ret);
2964ff2f60dSAdrian Chadd }
2974ff2f60dSAdrian Chadd
29862042c97SAdrian Chadd /*
29962042c97SAdrian Chadd * Flush ATU entries for a single port.
30062042c97SAdrian Chadd */
30162042c97SAdrian Chadd static int
ar8xxx_atu_flush_port(struct arswitch_softc * sc,int port)30262042c97SAdrian Chadd ar8xxx_atu_flush_port(struct arswitch_softc *sc, int port)
30362042c97SAdrian Chadd {
30462042c97SAdrian Chadd int ret, val;
30562042c97SAdrian Chadd
30662042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: flushing port %d\n", __func__,
30762042c97SAdrian Chadd port);
30862042c97SAdrian Chadd
30962042c97SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
31062042c97SAdrian Chadd
31162042c97SAdrian Chadd /* Flush unicast entries on port */
31262042c97SAdrian Chadd val = AR8216_ATU_OP_FLUSH_UNICAST;
31362042c97SAdrian Chadd
31462042c97SAdrian Chadd /* TODO: bit 4 indicates whether to flush dynamic (0) or static (1) */
31562042c97SAdrian Chadd
31662042c97SAdrian Chadd /* Which port */
31762042c97SAdrian Chadd val |= SM(port, AR8216_ATU_PORT_NUM);
31862042c97SAdrian Chadd
31962042c97SAdrian Chadd ret = ar8xxx_atu_wait_ready(sc);
32062042c97SAdrian Chadd if (ret)
32162042c97SAdrian Chadd device_printf(sc->sc_dev, "%s: waitreg failed\n", __func__);
32262042c97SAdrian Chadd
32362042c97SAdrian Chadd if (!ret)
32462042c97SAdrian Chadd arswitch_writereg(sc->sc_dev,
32562042c97SAdrian Chadd AR8216_REG_ATU,
32662042c97SAdrian Chadd val | AR8216_ATU_ACTIVE);
32762042c97SAdrian Chadd
32862042c97SAdrian Chadd return (ret);
32962042c97SAdrian Chadd }
33062042c97SAdrian Chadd
33162042c97SAdrian Chadd /*
33262042c97SAdrian Chadd * XXX TODO: flush a single MAC address.
33362042c97SAdrian Chadd */
33462042c97SAdrian Chadd
33562042c97SAdrian Chadd /*
33662042c97SAdrian Chadd * Fetch a single entry from the ATU.
33762042c97SAdrian Chadd */
33862042c97SAdrian Chadd static int
ar8xxx_atu_fetch_table(struct arswitch_softc * sc,etherswitch_atu_entry_t * e,int atu_fetch_op)33962042c97SAdrian Chadd ar8xxx_atu_fetch_table(struct arswitch_softc *sc, etherswitch_atu_entry_t *e,
34062042c97SAdrian Chadd int atu_fetch_op)
34162042c97SAdrian Chadd {
34262042c97SAdrian Chadd uint32_t ret0, ret1, ret2, val;
34362042c97SAdrian Chadd
34462042c97SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
34562042c97SAdrian Chadd
34662042c97SAdrian Chadd switch (atu_fetch_op) {
34762042c97SAdrian Chadd case 0:
34862042c97SAdrian Chadd /* Initialise things for the first fetch */
34962042c97SAdrian Chadd
35062042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: initializing\n", __func__);
35162042c97SAdrian Chadd (void) ar8xxx_atu_wait_ready(sc);
35262042c97SAdrian Chadd
35362042c97SAdrian Chadd arswitch_writereg(sc->sc_dev,
35462042c97SAdrian Chadd AR8216_REG_ATU, AR8216_ATU_OP_GET_NEXT);
35562042c97SAdrian Chadd arswitch_writereg(sc->sc_dev,
35662042c97SAdrian Chadd AR8216_REG_ATU_DATA, 0);
35762042c97SAdrian Chadd arswitch_writereg(sc->sc_dev,
35862042c97SAdrian Chadd AR8216_REG_ATU_CTRL2, 0);
35962042c97SAdrian Chadd
36062042c97SAdrian Chadd return (0);
36162042c97SAdrian Chadd case 1:
36262042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: reading next\n", __func__);
36362042c97SAdrian Chadd /*
36462042c97SAdrian Chadd * Attempt to read the next address entry; don't modify what
36562042c97SAdrian Chadd * is there in AT_ADDR{4,5} as its used for the next fetch
36662042c97SAdrian Chadd */
36762042c97SAdrian Chadd (void) ar8xxx_atu_wait_ready(sc);
36862042c97SAdrian Chadd
36962042c97SAdrian Chadd /* Begin the next read event; not modifying anything */
37062042c97SAdrian Chadd val = arswitch_readreg(sc->sc_dev, AR8216_REG_ATU);
37162042c97SAdrian Chadd val |= AR8216_ATU_ACTIVE;
37262042c97SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8216_REG_ATU, val);
37362042c97SAdrian Chadd
37462042c97SAdrian Chadd /* Wait for it to complete */
37562042c97SAdrian Chadd (void) ar8xxx_atu_wait_ready(sc);
37662042c97SAdrian Chadd
37762042c97SAdrian Chadd /* Fetch the ethernet address and ATU status */
37862042c97SAdrian Chadd ret0 = arswitch_readreg(sc->sc_dev, AR8216_REG_ATU);
37962042c97SAdrian Chadd ret1 = arswitch_readreg(sc->sc_dev, AR8216_REG_ATU_DATA);
38062042c97SAdrian Chadd ret2 = arswitch_readreg(sc->sc_dev, AR8216_REG_ATU_CTRL2);
38162042c97SAdrian Chadd
38262042c97SAdrian Chadd /* If the status is zero, then we're done */
38362042c97SAdrian Chadd if (MS(ret2, AR8216_ATU_CTRL2_AT_STATUS) == 0)
38462042c97SAdrian Chadd return (-1);
38562042c97SAdrian Chadd
38662042c97SAdrian Chadd /* MAC address */
38762042c97SAdrian Chadd e->es_macaddr[5] = MS(ret0, AR8216_ATU_ADDR5);
38862042c97SAdrian Chadd e->es_macaddr[4] = MS(ret0, AR8216_ATU_ADDR4);
38962042c97SAdrian Chadd e->es_macaddr[3] = MS(ret1, AR8216_ATU_ADDR3);
39062042c97SAdrian Chadd e->es_macaddr[2] = MS(ret1, AR8216_ATU_ADDR2);
39162042c97SAdrian Chadd e->es_macaddr[1] = MS(ret1, AR8216_ATU_ADDR1);
39262042c97SAdrian Chadd e->es_macaddr[0] = MS(ret1, AR8216_ATU_ADDR0);
39362042c97SAdrian Chadd
39462042c97SAdrian Chadd /* Bitmask of ports this entry is for */
39562042c97SAdrian Chadd e->es_portmask = MS(ret2, AR8216_ATU_CTRL2_DESPORT);
39662042c97SAdrian Chadd
39762042c97SAdrian Chadd /* TODO: other flags that are interesting */
39862042c97SAdrian Chadd
39962042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: MAC %6D portmask 0x%08x\n",
40062042c97SAdrian Chadd __func__,
40162042c97SAdrian Chadd e->es_macaddr, ":", e->es_portmask);
40262042c97SAdrian Chadd return (0);
40362042c97SAdrian Chadd default:
40462042c97SAdrian Chadd return (-1);
40562042c97SAdrian Chadd }
40662042c97SAdrian Chadd return (-1);
40762042c97SAdrian Chadd }
40862042c97SAdrian Chadd
40962042c97SAdrian Chadd /*
41062042c97SAdrian Chadd * Configure aging register defaults.
41162042c97SAdrian Chadd */
41262042c97SAdrian Chadd static int
ar8xxx_atu_learn_default(struct arswitch_softc * sc)41362042c97SAdrian Chadd ar8xxx_atu_learn_default(struct arswitch_softc *sc)
41462042c97SAdrian Chadd {
41562042c97SAdrian Chadd int ret;
41662042c97SAdrian Chadd uint32_t val;
41762042c97SAdrian Chadd
41862042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: resetting learning\n", __func__);
41962042c97SAdrian Chadd
42062042c97SAdrian Chadd /*
42162042c97SAdrian Chadd * For now, configure the aging defaults:
42262042c97SAdrian Chadd *
42362042c97SAdrian Chadd * + ARP_EN - enable "acknowledgement" of ARP frames - they are
42462042c97SAdrian Chadd * forwarded to the CPU port
42562042c97SAdrian Chadd * + LEARN_CHANGE_EN - hash table violations when learning MAC addresses
42662042c97SAdrian Chadd * will force an entry to be expired/updated and a new one to be
42762042c97SAdrian Chadd * programmed in.
42862042c97SAdrian Chadd * + AGE_EN - enable address table aging
42962042c97SAdrian Chadd * + AGE_TIME - set to 5 minutes
43062042c97SAdrian Chadd */
43162042c97SAdrian Chadd val = 0;
43262042c97SAdrian Chadd val |= AR8216_ATU_CTRL_ARP_EN;
43362042c97SAdrian Chadd val |= AR8216_ATU_CTRL_LEARN_CHANGE;
43462042c97SAdrian Chadd val |= AR8216_ATU_CTRL_AGE_EN;
43562042c97SAdrian Chadd val |= 0x2b; /* 5 minutes; bits 15:0 */
43662042c97SAdrian Chadd
43762042c97SAdrian Chadd ret = arswitch_writereg(sc->sc_dev,
43862042c97SAdrian Chadd AR8216_REG_ATU_CTRL,
43962042c97SAdrian Chadd val);
44062042c97SAdrian Chadd
44162042c97SAdrian Chadd if (ret)
44262042c97SAdrian Chadd device_printf(sc->sc_dev, "%s: writereg failed\n", __func__);
44362042c97SAdrian Chadd
44462042c97SAdrian Chadd return (ret);
44562042c97SAdrian Chadd }
44662042c97SAdrian Chadd
44762042c97SAdrian Chadd /*
44862042c97SAdrian Chadd * XXX TODO: add another routine to configure the leaky behaviour
44962042c97SAdrian Chadd * when unknown frames are received. These must be consistent
45062042c97SAdrian Chadd * between ethernet switches.
45162042c97SAdrian Chadd */
45262042c97SAdrian Chadd
45362042c97SAdrian Chadd /*
4542ba4bf8fSAdrian Chadd * Fetch the configured switch MAC address.
4552ba4bf8fSAdrian Chadd */
4562ba4bf8fSAdrian Chadd static int
ar8xxx_hw_get_switch_macaddr(struct arswitch_softc * sc,struct ether_addr * ea)4572ba4bf8fSAdrian Chadd ar8xxx_hw_get_switch_macaddr(struct arswitch_softc *sc, struct ether_addr *ea)
4582ba4bf8fSAdrian Chadd {
4592ba4bf8fSAdrian Chadd uint32_t ret0, ret1;
4602ba4bf8fSAdrian Chadd char *s;
4612ba4bf8fSAdrian Chadd
4622ba4bf8fSAdrian Chadd s = (void *) ea;
4632ba4bf8fSAdrian Chadd
4642ba4bf8fSAdrian Chadd ret0 = arswitch_readreg(sc->sc_dev, AR8X16_REG_SW_MAC_ADDR0);
4652ba4bf8fSAdrian Chadd ret1 = arswitch_readreg(sc->sc_dev, AR8X16_REG_SW_MAC_ADDR1);
4662ba4bf8fSAdrian Chadd
4672ba4bf8fSAdrian Chadd s[5] = MS(ret0, AR8X16_REG_SW_MAC_ADDR0_BYTE5);
4682ba4bf8fSAdrian Chadd s[4] = MS(ret0, AR8X16_REG_SW_MAC_ADDR0_BYTE4);
4692ba4bf8fSAdrian Chadd s[3] = MS(ret1, AR8X16_REG_SW_MAC_ADDR1_BYTE3);
4702ba4bf8fSAdrian Chadd s[2] = MS(ret1, AR8X16_REG_SW_MAC_ADDR1_BYTE2);
4712ba4bf8fSAdrian Chadd s[1] = MS(ret1, AR8X16_REG_SW_MAC_ADDR1_BYTE1);
4722ba4bf8fSAdrian Chadd s[0] = MS(ret1, AR8X16_REG_SW_MAC_ADDR1_BYTE0);
4732ba4bf8fSAdrian Chadd
4742ba4bf8fSAdrian Chadd return (0);
4752ba4bf8fSAdrian Chadd }
4762ba4bf8fSAdrian Chadd
4772ba4bf8fSAdrian Chadd /*
4782ba4bf8fSAdrian Chadd * Set the switch mac address.
4792ba4bf8fSAdrian Chadd */
4802ba4bf8fSAdrian Chadd static int
ar8xxx_hw_set_switch_macaddr(struct arswitch_softc * sc,const struct ether_addr * ea)4812ba4bf8fSAdrian Chadd ar8xxx_hw_set_switch_macaddr(struct arswitch_softc *sc,
4822ba4bf8fSAdrian Chadd const struct ether_addr *ea)
4832ba4bf8fSAdrian Chadd {
4842ba4bf8fSAdrian Chadd
4852ba4bf8fSAdrian Chadd return (ENXIO);
4862ba4bf8fSAdrian Chadd }
4872ba4bf8fSAdrian Chadd
4882ba4bf8fSAdrian Chadd /*
48962042c97SAdrian Chadd * XXX TODO: this attach routine does NOT free all memory, resources
49062042c97SAdrian Chadd * upon failure!
49162042c97SAdrian Chadd */
4924ff2f60dSAdrian Chadd static int
arswitch_attach(device_t dev)493a043e8c7SAdrian Chadd arswitch_attach(device_t dev)
494a043e8c7SAdrian Chadd {
4951b334c8bSAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev);
4961b334c8bSAdrian Chadd struct sysctl_ctx_list *ctx;
4971b334c8bSAdrian Chadd struct sysctl_oid *tree;
498a043e8c7SAdrian Chadd int err = 0;
499df892897SAdrian Chadd int port;
500a043e8c7SAdrian Chadd
501a043e8c7SAdrian Chadd /* sc->sc_switchtype is already decided in arswitch_probe() */
502a043e8c7SAdrian Chadd sc->sc_dev = dev;
503a043e8c7SAdrian Chadd mtx_init(&sc->sc_mtx, "arswitch", NULL, MTX_DEF);
504a043e8c7SAdrian Chadd sc->page = -1;
505a043e8c7SAdrian Chadd strlcpy(sc->info.es_name, device_get_desc(dev),
506a043e8c7SAdrian Chadd sizeof(sc->info.es_name));
507a043e8c7SAdrian Chadd
5081b334c8bSAdrian Chadd /* Debugging */
5091b334c8bSAdrian Chadd ctx = device_get_sysctl_ctx(sc->sc_dev);
5101b334c8bSAdrian Chadd tree = device_get_sysctl_tree(sc->sc_dev);
5111b334c8bSAdrian Chadd SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
5121b334c8bSAdrian Chadd "debug", CTLFLAG_RW, &sc->sc_debug, 0,
5131b334c8bSAdrian Chadd "control debugging printfs");
5141b334c8bSAdrian Chadd
51562042c97SAdrian Chadd /* Allocate a 128 entry ATU table; hopefully its big enough! */
51662042c97SAdrian Chadd /* XXX TODO: make this per chip */
51762042c97SAdrian Chadd sc->atu.entries = malloc(sizeof(etherswitch_atu_entry_t) * 128,
51862042c97SAdrian Chadd M_DEVBUF, M_NOWAIT);
51962042c97SAdrian Chadd if (sc->atu.entries == NULL) {
52062042c97SAdrian Chadd device_printf(sc->sc_dev, "%s: failed to allocate ATU table\n",
52162042c97SAdrian Chadd __func__);
52262042c97SAdrian Chadd return (ENXIO);
52362042c97SAdrian Chadd }
52462042c97SAdrian Chadd sc->atu.count = 0;
52562042c97SAdrian Chadd sc->atu.size = 128;
52662042c97SAdrian Chadd
527ddbc4420SAdrian Chadd /* Default HAL methods */
528a9ad4222SAdrian Chadd sc->hal.arswitch_port_init = ar8xxx_port_init;
529a9ad4222SAdrian Chadd sc->hal.arswitch_port_vlan_setup = ar8xxx_port_vlan_setup;
530a9ad4222SAdrian Chadd sc->hal.arswitch_port_vlan_get = ar8xxx_port_vlan_get;
5316dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_init_hw = ar8xxx_reset_vlans;
5322ba4bf8fSAdrian Chadd sc->hal.arswitch_hw_get_switch_macaddr = ar8xxx_hw_get_switch_macaddr;
5332ba4bf8fSAdrian Chadd sc->hal.arswitch_hw_set_switch_macaddr = ar8xxx_hw_set_switch_macaddr;
534749cac13SAdrian Chadd
5356dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_getvgroup = ar8xxx_getvgroup;
5366dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_setvgroup = ar8xxx_setvgroup;
537749cac13SAdrian Chadd
5386dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_get_pvid = ar8xxx_get_pvid;
5396dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_set_pvid = ar8xxx_set_pvid;
540749cac13SAdrian Chadd
541749cac13SAdrian Chadd sc->hal.arswitch_get_dot1q_vlan = ar8xxx_get_dot1q_vlan;
542749cac13SAdrian Chadd sc->hal.arswitch_set_dot1q_vlan = ar8xxx_set_dot1q_vlan;
543f35f94f4SAdrian Chadd sc->hal.arswitch_flush_dot1q_vlan = ar8xxx_flush_dot1q_vlan;
544f35f94f4SAdrian Chadd sc->hal.arswitch_purge_dot1q_vlan = ar8xxx_purge_dot1q_vlan;
545749cac13SAdrian Chadd sc->hal.arswitch_get_port_vlan = ar8xxx_get_port_vlan;
546749cac13SAdrian Chadd sc->hal.arswitch_set_port_vlan = ar8xxx_set_port_vlan;
547749cac13SAdrian Chadd
5484ff2f60dSAdrian Chadd sc->hal.arswitch_atu_flush = ar8xxx_atu_flush;
54962042c97SAdrian Chadd sc->hal.arswitch_atu_flush_port = ar8xxx_atu_flush_port;
55062042c97SAdrian Chadd sc->hal.arswitch_atu_learn_default = ar8xxx_atu_learn_default;
55162042c97SAdrian Chadd sc->hal.arswitch_atu_fetch_table = ar8xxx_atu_fetch_table;
552749cac13SAdrian Chadd
55378549b94SAdrian Chadd sc->hal.arswitch_phy_read = arswitch_readphy_internal;
55478549b94SAdrian Chadd sc->hal.arswitch_phy_write = arswitch_writephy_internal;
555ddbc4420SAdrian Chadd
556a043e8c7SAdrian Chadd /*
557a043e8c7SAdrian Chadd * Attach switch related functions
558a043e8c7SAdrian Chadd */
5598f5dbc22SMarius Strobl if (AR8X16_IS_SWITCH(sc, AR8216))
560a043e8c7SAdrian Chadd ar8216_attach(sc);
561a043e8c7SAdrian Chadd else if (AR8X16_IS_SWITCH(sc, AR8226))
562a043e8c7SAdrian Chadd ar8226_attach(sc);
563a043e8c7SAdrian Chadd else if (AR8X16_IS_SWITCH(sc, AR8316))
564a043e8c7SAdrian Chadd ar8316_attach(sc);
565482d268dSAdrian Chadd else if (AR8X16_IS_SWITCH(sc, AR8327))
566482d268dSAdrian Chadd ar8327_attach(sc);
56778549b94SAdrian Chadd else {
5681b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY,
5691b334c8bSAdrian Chadd "%s: unknown switch (%d)?\n", __func__, sc->sc_switchtype);
570a043e8c7SAdrian Chadd return (ENXIO);
57178549b94SAdrian Chadd }
572a043e8c7SAdrian Chadd
573b9f07b86SLuiz Otavio O Souza /* Common defaults. */
574a043e8c7SAdrian Chadd sc->info.es_nports = 5; /* XXX technically 6, but 6th not used */
575a043e8c7SAdrian Chadd
576a043e8c7SAdrian Chadd /* XXX Defaults for externally connected AR8316 */
577a043e8c7SAdrian Chadd sc->numphys = 4;
578a043e8c7SAdrian Chadd sc->phy4cpu = 1;
579a043e8c7SAdrian Chadd sc->is_rgmii = 1;
580a043e8c7SAdrian Chadd sc->is_gmii = 0;
581b2152161SAdrian Chadd sc->is_mii = 0;
582a043e8c7SAdrian Chadd
583a043e8c7SAdrian Chadd (void) resource_int_value(device_get_name(dev), device_get_unit(dev),
584a043e8c7SAdrian Chadd "numphys", &sc->numphys);
585a043e8c7SAdrian Chadd (void) resource_int_value(device_get_name(dev), device_get_unit(dev),
586a043e8c7SAdrian Chadd "phy4cpu", &sc->phy4cpu);
587a043e8c7SAdrian Chadd (void) resource_int_value(device_get_name(dev), device_get_unit(dev),
588a043e8c7SAdrian Chadd "is_rgmii", &sc->is_rgmii);
589a043e8c7SAdrian Chadd (void) resource_int_value(device_get_name(dev), device_get_unit(dev),
590a043e8c7SAdrian Chadd "is_gmii", &sc->is_gmii);
591b2152161SAdrian Chadd (void) resource_int_value(device_get_name(dev), device_get_unit(dev),
592b2152161SAdrian Chadd "is_mii", &sc->is_mii);
593a043e8c7SAdrian Chadd
594b9f07b86SLuiz Otavio O Souza if (sc->numphys > AR8X16_NUM_PHYS)
595b9f07b86SLuiz Otavio O Souza sc->numphys = AR8X16_NUM_PHYS;
596a043e8c7SAdrian Chadd
597b9f07b86SLuiz Otavio O Souza /* Reset the switch. */
59878549b94SAdrian Chadd if (arswitch_reset(dev)) {
5991b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY,
6001b334c8bSAdrian Chadd "%s: arswitch_reset: failed\n", __func__);
601b9f07b86SLuiz Otavio O Souza return (ENXIO);
60278549b94SAdrian Chadd }
603a043e8c7SAdrian Chadd
604b2152161SAdrian Chadd err = sc->hal.arswitch_hw_setup(sc);
6051b334c8bSAdrian Chadd if (err != 0) {
6061b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY,
6071b334c8bSAdrian Chadd "%s: hw_setup: err=%d\n", __func__, err);
608b2152161SAdrian Chadd return (err);
6091b334c8bSAdrian Chadd }
610b2152161SAdrian Chadd
611a043e8c7SAdrian Chadd err = sc->hal.arswitch_hw_global_setup(sc);
6121b334c8bSAdrian Chadd if (err != 0) {
6131b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY,
6141b334c8bSAdrian Chadd "%s: hw_global_setup: err=%d\n", __func__, err);
615a043e8c7SAdrian Chadd return (err);
6161b334c8bSAdrian Chadd }
617a043e8c7SAdrian Chadd
61862042c97SAdrian Chadd /*
61962042c97SAdrian Chadd * Configure the default address table learning parameters for this
62062042c97SAdrian Chadd * switch.
62162042c97SAdrian Chadd */
62262042c97SAdrian Chadd err = sc->hal.arswitch_atu_learn_default(sc);
62362042c97SAdrian Chadd if (err != 0) {
62462042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY,
62562042c97SAdrian Chadd "%s: atu_learn_default: err=%d\n", __func__, err);
62662042c97SAdrian Chadd return (err);
62762042c97SAdrian Chadd }
62862042c97SAdrian Chadd
629b9f07b86SLuiz Otavio O Souza /* Initialize the switch ports. */
630df892897SAdrian Chadd for (port = 0; port <= sc->numphys; port++) {
631ddbc4420SAdrian Chadd sc->hal.arswitch_port_init(sc, port);
632df892897SAdrian Chadd }
633b9f07b86SLuiz Otavio O Souza
634a043e8c7SAdrian Chadd /*
635a043e8c7SAdrian Chadd * Attach the PHYs and complete the bus enumeration.
636a043e8c7SAdrian Chadd */
637a043e8c7SAdrian Chadd err = arswitch_attach_phys(sc);
6381b334c8bSAdrian Chadd if (err != 0) {
6391b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY,
6401b334c8bSAdrian Chadd "%s: attach_phys: err=%d\n", __func__, err);
641a043e8c7SAdrian Chadd return (err);
6421b334c8bSAdrian Chadd }
643a043e8c7SAdrian Chadd
644b9f07b86SLuiz Otavio O Souza /* Default to ingress filters off. */
645b9f07b86SLuiz Otavio O Souza err = arswitch_set_vlan_mode(sc, 0);
6461b334c8bSAdrian Chadd if (err != 0) {
6471b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY,
6481b334c8bSAdrian Chadd "%s: set_vlan_mode: err=%d\n", __func__, err);
649b9f07b86SLuiz Otavio O Souza return (err);
6501b334c8bSAdrian Chadd }
651b9f07b86SLuiz Otavio O Souza
652723da5d9SJohn Baldwin bus_identify_children(dev);
653a043e8c7SAdrian Chadd bus_enumerate_hinted_children(dev);
65418250ec6SJohn Baldwin bus_attach_children(dev);
655a043e8c7SAdrian Chadd
656a043e8c7SAdrian Chadd callout_init_mtx(&sc->callout_tick, &sc->sc_mtx, 0);
657454d507aSAleksandr Rybalko
658454d507aSAleksandr Rybalko ARSWITCH_LOCK(sc);
659a043e8c7SAdrian Chadd arswitch_tick(sc);
660454d507aSAleksandr Rybalko ARSWITCH_UNLOCK(sc);
661a043e8c7SAdrian Chadd
662a043e8c7SAdrian Chadd return (err);
663a043e8c7SAdrian Chadd }
664a043e8c7SAdrian Chadd
665a043e8c7SAdrian Chadd static int
arswitch_detach(device_t dev)666a043e8c7SAdrian Chadd arswitch_detach(device_t dev)
667a043e8c7SAdrian Chadd {
668a043e8c7SAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev);
669*aa48c1aeSJohn Baldwin int error, i;
670a043e8c7SAdrian Chadd
671a043e8c7SAdrian Chadd callout_drain(&sc->callout_tick);
672a043e8c7SAdrian Chadd
673*aa48c1aeSJohn Baldwin error = bus_generic_detach(dev);
674*aa48c1aeSJohn Baldwin if (error != 0)
675*aa48c1aeSJohn Baldwin return (error);
676*aa48c1aeSJohn Baldwin
677a043e8c7SAdrian Chadd for (i=0; i < sc->numphys; i++) {
678a043e8c7SAdrian Chadd if (sc->ifp[i] != NULL)
679a043e8c7SAdrian Chadd if_free(sc->ifp[i]);
680a043e8c7SAdrian Chadd free(sc->ifname[i], M_DEVBUF);
681a043e8c7SAdrian Chadd }
682a043e8c7SAdrian Chadd
68362042c97SAdrian Chadd free(sc->atu.entries, M_DEVBUF);
68462042c97SAdrian Chadd
685a043e8c7SAdrian Chadd mtx_destroy(&sc->sc_mtx);
686a043e8c7SAdrian Chadd
687a043e8c7SAdrian Chadd return (0);
688a043e8c7SAdrian Chadd }
689a043e8c7SAdrian Chadd
690a043e8c7SAdrian Chadd /*
691a043e8c7SAdrian Chadd * Convert PHY number to port number. PHY0 is connected to port 1, PHY1 to
692a043e8c7SAdrian Chadd * port 2, etc.
693a043e8c7SAdrian Chadd */
694a043e8c7SAdrian Chadd static inline int
arswitch_portforphy(int phy)695a043e8c7SAdrian Chadd arswitch_portforphy(int phy)
696a043e8c7SAdrian Chadd {
697a043e8c7SAdrian Chadd return (phy+1);
698a043e8c7SAdrian Chadd }
699a043e8c7SAdrian Chadd
700a043e8c7SAdrian Chadd static inline struct mii_data *
arswitch_miiforport(struct arswitch_softc * sc,int port)701a043e8c7SAdrian Chadd arswitch_miiforport(struct arswitch_softc *sc, int port)
702a043e8c7SAdrian Chadd {
703a043e8c7SAdrian Chadd int phy = port-1;
704a043e8c7SAdrian Chadd
705a043e8c7SAdrian Chadd if (phy < 0 || phy >= sc->numphys)
706a043e8c7SAdrian Chadd return (NULL);
707a043e8c7SAdrian Chadd return (device_get_softc(sc->miibus[phy]));
708a043e8c7SAdrian Chadd }
709a043e8c7SAdrian Chadd
7102e6a8c1aSJustin Hibbits static inline if_t
arswitch_ifpforport(struct arswitch_softc * sc,int port)711a043e8c7SAdrian Chadd arswitch_ifpforport(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 (sc->ifp[phy]);
718a043e8c7SAdrian Chadd }
719a043e8c7SAdrian Chadd
720a043e8c7SAdrian Chadd /*
721a043e8c7SAdrian Chadd * Convert port status to ifmedia.
722a043e8c7SAdrian Chadd */
723a043e8c7SAdrian Chadd static void
arswitch_update_ifmedia(int portstatus,u_int * media_status,u_int * media_active)724a043e8c7SAdrian Chadd arswitch_update_ifmedia(int portstatus, u_int *media_status, u_int *media_active)
725a043e8c7SAdrian Chadd {
726a043e8c7SAdrian Chadd *media_active = IFM_ETHER;
727a043e8c7SAdrian Chadd *media_status = IFM_AVALID;
728a043e8c7SAdrian Chadd
729a043e8c7SAdrian Chadd if ((portstatus & AR8X16_PORT_STS_LINK_UP) != 0)
730a043e8c7SAdrian Chadd *media_status |= IFM_ACTIVE;
731a043e8c7SAdrian Chadd else {
732a043e8c7SAdrian Chadd *media_active |= IFM_NONE;
733a043e8c7SAdrian Chadd return;
734a043e8c7SAdrian Chadd }
735a043e8c7SAdrian Chadd switch (portstatus & AR8X16_PORT_STS_SPEED_MASK) {
736a043e8c7SAdrian Chadd case AR8X16_PORT_STS_SPEED_10:
737a043e8c7SAdrian Chadd *media_active |= IFM_10_T;
738a043e8c7SAdrian Chadd break;
739a043e8c7SAdrian Chadd case AR8X16_PORT_STS_SPEED_100:
740a043e8c7SAdrian Chadd *media_active |= IFM_100_TX;
741a043e8c7SAdrian Chadd break;
742a043e8c7SAdrian Chadd case AR8X16_PORT_STS_SPEED_1000:
743a043e8c7SAdrian Chadd *media_active |= IFM_1000_T;
744a043e8c7SAdrian Chadd break;
745a043e8c7SAdrian Chadd }
746a043e8c7SAdrian Chadd if ((portstatus & AR8X16_PORT_STS_DUPLEX) == 0)
747a043e8c7SAdrian Chadd *media_active |= IFM_FDX;
748a043e8c7SAdrian Chadd else
749a043e8c7SAdrian Chadd *media_active |= IFM_HDX;
750a043e8c7SAdrian Chadd if ((portstatus & AR8X16_PORT_STS_TXFLOW) != 0)
751a043e8c7SAdrian Chadd *media_active |= IFM_ETH_TXPAUSE;
752a043e8c7SAdrian Chadd if ((portstatus & AR8X16_PORT_STS_RXFLOW) != 0)
753a043e8c7SAdrian Chadd *media_active |= IFM_ETH_RXPAUSE;
754a043e8c7SAdrian Chadd }
755a043e8c7SAdrian Chadd
756a043e8c7SAdrian Chadd /*
757a043e8c7SAdrian Chadd * Poll the status for all PHYs. We're using the switch port status because
758a043e8c7SAdrian Chadd * thats a lot quicker to read than talking to all the PHYs. Care must be
759a043e8c7SAdrian Chadd * taken that the resulting ifmedia_active is identical to what the PHY will
760a043e8c7SAdrian Chadd * compute, or gratuitous link status changes will occur whenever the PHYs
761a043e8c7SAdrian Chadd * update function is called.
762a043e8c7SAdrian Chadd */
763a043e8c7SAdrian Chadd static void
arswitch_miipollstat(struct arswitch_softc * sc)764a043e8c7SAdrian Chadd arswitch_miipollstat(struct arswitch_softc *sc)
765a043e8c7SAdrian Chadd {
766a043e8c7SAdrian Chadd int i;
767a043e8c7SAdrian Chadd struct mii_data *mii;
768a043e8c7SAdrian Chadd struct mii_softc *miisc;
769a043e8c7SAdrian Chadd int portstatus;
7704ff2f60dSAdrian Chadd int port_flap = 0;
771a043e8c7SAdrian Chadd
772454d507aSAleksandr Rybalko ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
773454d507aSAleksandr Rybalko
774a043e8c7SAdrian Chadd for (i = 0; i < sc->numphys; i++) {
775a043e8c7SAdrian Chadd if (sc->miibus[i] == NULL)
776a043e8c7SAdrian Chadd continue;
777a043e8c7SAdrian Chadd mii = device_get_softc(sc->miibus[i]);
778ddbc4420SAdrian Chadd /* XXX This would be nice to have abstracted out to be per-chip */
779ddbc4420SAdrian Chadd /* AR8327/AR8337 has a different register base */
780ddbc4420SAdrian Chadd if (AR8X16_IS_SWITCH(sc, AR8327))
781ddbc4420SAdrian Chadd portstatus = arswitch_readreg(sc->sc_dev,
782ddbc4420SAdrian Chadd AR8327_REG_PORT_STATUS(arswitch_portforphy(i)));
783ddbc4420SAdrian Chadd else
784a043e8c7SAdrian Chadd portstatus = arswitch_readreg(sc->sc_dev,
785a043e8c7SAdrian Chadd AR8X16_REG_PORT_STS(arswitch_portforphy(i)));
7861b334c8bSAdrian Chadd #if 1
7871b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_POLL, "p[%d]=0x%08x (%b)\n",
788b2152161SAdrian Chadd i,
789a043e8c7SAdrian Chadd portstatus,
7901b334c8bSAdrian Chadd portstatus,
791a043e8c7SAdrian Chadd "\20\3TXMAC\4RXMAC\5TXFLOW\6RXFLOW\7"
792a043e8c7SAdrian Chadd "DUPLEX\11LINK_UP\12LINK_AUTO\13LINK_PAUSE");
793a043e8c7SAdrian Chadd #endif
7944ff2f60dSAdrian Chadd /*
7954ff2f60dSAdrian Chadd * If the current status is down, but we have a link
7964ff2f60dSAdrian Chadd * status showing up, we need to do an ATU flush.
7974ff2f60dSAdrian Chadd */
7984ff2f60dSAdrian Chadd if ((mii->mii_media_status & IFM_ACTIVE) == 0 &&
7994ff2f60dSAdrian Chadd (portstatus & AR8X16_PORT_STS_LINK_UP) != 0) {
8004ff2f60dSAdrian Chadd device_printf(sc->sc_dev, "%s: port %d: port -> UP\n",
8014ff2f60dSAdrian Chadd __func__,
8024ff2f60dSAdrian Chadd i);
8034ff2f60dSAdrian Chadd port_flap = 1;
8044ff2f60dSAdrian Chadd }
8054ff2f60dSAdrian Chadd /*
8064ff2f60dSAdrian Chadd * and maybe if a port goes up->down?
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 -> DOWN\n",
8114ff2f60dSAdrian Chadd __func__,
8124ff2f60dSAdrian Chadd i);
8134ff2f60dSAdrian Chadd port_flap = 1;
8144ff2f60dSAdrian Chadd }
815a043e8c7SAdrian Chadd arswitch_update_ifmedia(portstatus, &mii->mii_media_status,
816a043e8c7SAdrian Chadd &mii->mii_media_active);
817a043e8c7SAdrian Chadd LIST_FOREACH(miisc, &mii->mii_phys, mii_list) {
818a043e8c7SAdrian Chadd if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) !=
819a043e8c7SAdrian Chadd miisc->mii_inst)
820a043e8c7SAdrian Chadd continue;
821a043e8c7SAdrian Chadd mii_phy_update(miisc, MII_POLLSTAT);
822a043e8c7SAdrian Chadd }
823a043e8c7SAdrian Chadd }
8244ff2f60dSAdrian Chadd
8254ff2f60dSAdrian Chadd /* If a port went from down->up, flush the ATU */
8264ff2f60dSAdrian Chadd if (port_flap)
8274ff2f60dSAdrian Chadd sc->hal.arswitch_atu_flush(sc);
828a043e8c7SAdrian Chadd }
829a043e8c7SAdrian Chadd
830a043e8c7SAdrian Chadd static void
arswitch_tick(void * arg)831a043e8c7SAdrian Chadd arswitch_tick(void *arg)
832a043e8c7SAdrian Chadd {
833a043e8c7SAdrian Chadd struct arswitch_softc *sc = arg;
834a043e8c7SAdrian Chadd
835a043e8c7SAdrian Chadd arswitch_miipollstat(sc);
836a043e8c7SAdrian Chadd callout_reset(&sc->callout_tick, hz, arswitch_tick, sc);
837a043e8c7SAdrian Chadd }
838a043e8c7SAdrian Chadd
839454d507aSAleksandr Rybalko static void
arswitch_lock(device_t dev)840454d507aSAleksandr Rybalko arswitch_lock(device_t dev)
841454d507aSAleksandr Rybalko {
842454d507aSAleksandr Rybalko struct arswitch_softc *sc = device_get_softc(dev);
843454d507aSAleksandr Rybalko
844454d507aSAleksandr Rybalko ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
845454d507aSAleksandr Rybalko ARSWITCH_LOCK(sc);
846454d507aSAleksandr Rybalko }
847454d507aSAleksandr Rybalko
848454d507aSAleksandr Rybalko static void
arswitch_unlock(device_t dev)849454d507aSAleksandr Rybalko arswitch_unlock(device_t dev)
850454d507aSAleksandr Rybalko {
851454d507aSAleksandr Rybalko struct arswitch_softc *sc = device_get_softc(dev);
852454d507aSAleksandr Rybalko
853454d507aSAleksandr Rybalko ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
854454d507aSAleksandr Rybalko ARSWITCH_UNLOCK(sc);
855454d507aSAleksandr Rybalko }
856454d507aSAleksandr Rybalko
857a043e8c7SAdrian Chadd static etherswitch_info_t *
arswitch_getinfo(device_t dev)858a043e8c7SAdrian Chadd arswitch_getinfo(device_t dev)
859a043e8c7SAdrian Chadd {
860a043e8c7SAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev);
861a043e8c7SAdrian Chadd
862a043e8c7SAdrian Chadd return (&sc->info);
863a043e8c7SAdrian Chadd }
864a043e8c7SAdrian Chadd
865a043e8c7SAdrian Chadd static int
ar8xxx_port_vlan_get(struct arswitch_softc * sc,etherswitch_port_t * p)866a9ad4222SAdrian Chadd ar8xxx_port_vlan_get(struct arswitch_softc *sc, etherswitch_port_t *p)
867a043e8c7SAdrian Chadd {
868b9f07b86SLuiz Otavio O Souza uint32_t reg;
869b9f07b86SLuiz Otavio O Souza
870b9f07b86SLuiz Otavio O Souza ARSWITCH_LOCK(sc);
871b9f07b86SLuiz Otavio O Souza
872b9f07b86SLuiz Otavio O Souza /* Retrieve the PVID. */
8736dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_get_pvid(sc, p->es_port, &p->es_pvid);
874b9f07b86SLuiz Otavio O Souza
875b9f07b86SLuiz Otavio O Souza /* Port flags. */
876b9f07b86SLuiz Otavio O Souza reg = arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_CTRL(p->es_port));
877b9f07b86SLuiz Otavio O Souza if (reg & AR8X16_PORT_CTRL_DOUBLE_TAG)
878b9f07b86SLuiz Otavio O Souza p->es_flags |= ETHERSWITCH_PORT_DOUBLE_TAG;
879b9f07b86SLuiz Otavio O Souza reg >>= AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT;
880b9f07b86SLuiz Otavio O Souza if ((reg & 0x3) == AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_ADD)
881b9f07b86SLuiz Otavio O Souza p->es_flags |= ETHERSWITCH_PORT_ADDTAG;
882b9f07b86SLuiz Otavio O Souza if ((reg & 0x3) == AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_STRIP)
883b9f07b86SLuiz Otavio O Souza p->es_flags |= ETHERSWITCH_PORT_STRIPTAG;
884b9f07b86SLuiz Otavio O Souza ARSWITCH_UNLOCK(sc);
885a043e8c7SAdrian Chadd
886a9ad4222SAdrian Chadd return (0);
887a9ad4222SAdrian Chadd }
888a9ad4222SAdrian Chadd
889a9ad4222SAdrian Chadd static int
arswitch_is_cpuport(struct arswitch_softc * sc,int port)890749cac13SAdrian Chadd arswitch_is_cpuport(struct arswitch_softc *sc, int port)
891749cac13SAdrian Chadd {
892749cac13SAdrian Chadd
893749cac13SAdrian Chadd return ((port == AR8X16_PORT_CPU) ||
894749cac13SAdrian Chadd ((AR8X16_IS_SWITCH(sc, AR8327) &&
895749cac13SAdrian Chadd port == AR8327_PORT_GMAC6)));
896749cac13SAdrian Chadd }
897749cac13SAdrian Chadd
898749cac13SAdrian Chadd static int
arswitch_getport(device_t dev,etherswitch_port_t * p)899a9ad4222SAdrian Chadd arswitch_getport(device_t dev, etherswitch_port_t *p)
900a9ad4222SAdrian Chadd {
901a9ad4222SAdrian Chadd struct arswitch_softc *sc;
902a9ad4222SAdrian Chadd struct mii_data *mii;
903a9ad4222SAdrian Chadd struct ifmediareq *ifmr;
904a9ad4222SAdrian Chadd int err;
905a9ad4222SAdrian Chadd
906a9ad4222SAdrian Chadd sc = device_get_softc(dev);
907749cac13SAdrian Chadd /* XXX +1 is for AR8327; should make this configurable! */
908749cac13SAdrian Chadd if (p->es_port < 0 || p->es_port > sc->info.es_nports)
909a9ad4222SAdrian Chadd return (ENXIO);
910a9ad4222SAdrian Chadd
911a9ad4222SAdrian Chadd err = sc->hal.arswitch_port_vlan_get(sc, p);
912a9ad4222SAdrian Chadd if (err != 0)
913a9ad4222SAdrian Chadd return (err);
914a9ad4222SAdrian Chadd
915a043e8c7SAdrian Chadd mii = arswitch_miiforport(sc, p->es_port);
916749cac13SAdrian Chadd if (arswitch_is_cpuport(sc, p->es_port)) {
917a043e8c7SAdrian Chadd /* fill in fixed values for CPU port */
918a9ad4222SAdrian Chadd /* XXX is this valid in all cases? */
919f47857dcSAdrian Chadd p->es_flags |= ETHERSWITCH_PORT_CPU;
920b9f07b86SLuiz Otavio O Souza ifmr = &p->es_ifmr;
921a043e8c7SAdrian Chadd ifmr->ifm_count = 0;
922a043e8c7SAdrian Chadd ifmr->ifm_current = ifmr->ifm_active =
923a043e8c7SAdrian Chadd IFM_ETHER | IFM_1000_T | IFM_FDX;
924a043e8c7SAdrian Chadd ifmr->ifm_mask = 0;
925a043e8c7SAdrian Chadd ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
926a043e8c7SAdrian Chadd } else if (mii != NULL) {
927a043e8c7SAdrian Chadd err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr,
928a043e8c7SAdrian Chadd &mii->mii_media, SIOCGIFMEDIA);
929a043e8c7SAdrian Chadd if (err)
930a043e8c7SAdrian Chadd return (err);
931a043e8c7SAdrian Chadd } else {
932a043e8c7SAdrian Chadd return (ENXIO);
933a043e8c7SAdrian Chadd }
934c94dc808SAdrian Chadd
935c94dc808SAdrian Chadd if (!arswitch_is_cpuport(sc, p->es_port) &&
936c94dc808SAdrian Chadd AR8X16_IS_SWITCH(sc, AR8327)) {
937c94dc808SAdrian Chadd int led;
938c94dc808SAdrian Chadd p->es_nleds = 3;
939c94dc808SAdrian Chadd
940c94dc808SAdrian Chadd for (led = 0; led < p->es_nleds; led++)
941c94dc808SAdrian Chadd {
942c94dc808SAdrian Chadd int style;
943c94dc808SAdrian Chadd uint32_t val;
944c94dc808SAdrian Chadd
945c94dc808SAdrian Chadd /* Find the right style enum for our pattern */
946c94dc808SAdrian Chadd val = arswitch_readreg(dev,
947c94dc808SAdrian Chadd ar8327_led_mapping[p->es_port-1][led].reg);
948c94dc808SAdrian Chadd val = (val>>ar8327_led_mapping[p->es_port-1][led].shift)&0x03;
949c94dc808SAdrian Chadd
950c94dc808SAdrian Chadd for (style = 0; style < ETHERSWITCH_PORT_LED_MAX; style++)
951c94dc808SAdrian Chadd {
952c94dc808SAdrian Chadd if (led_pattern_table[style] == val) break;
953c94dc808SAdrian Chadd }
954c94dc808SAdrian Chadd
955c94dc808SAdrian Chadd /* can't happen */
956c94dc808SAdrian Chadd if (style == ETHERSWITCH_PORT_LED_MAX)
957c94dc808SAdrian Chadd style = ETHERSWITCH_PORT_LED_DEFAULT;
958c94dc808SAdrian Chadd
959c94dc808SAdrian Chadd p->es_led[led] = style;
960c94dc808SAdrian Chadd }
961c94dc808SAdrian Chadd } else
962c94dc808SAdrian Chadd {
963c94dc808SAdrian Chadd p->es_nleds = 0;
964c94dc808SAdrian Chadd }
965c94dc808SAdrian Chadd
966a043e8c7SAdrian Chadd return (0);
967a043e8c7SAdrian Chadd }
968a043e8c7SAdrian Chadd
969a043e8c7SAdrian Chadd static int
ar8xxx_port_vlan_setup(struct arswitch_softc * sc,etherswitch_port_t * p)970a9ad4222SAdrian Chadd ar8xxx_port_vlan_setup(struct arswitch_softc *sc, etherswitch_port_t *p)
971a043e8c7SAdrian Chadd {
972b9f07b86SLuiz Otavio O Souza uint32_t reg;
973a9ad4222SAdrian Chadd int err;
974a043e8c7SAdrian Chadd
975b9f07b86SLuiz Otavio O Souza ARSWITCH_LOCK(sc);
976a9ad4222SAdrian Chadd
977b9f07b86SLuiz Otavio O Souza /* Set the PVID. */
978b9f07b86SLuiz Otavio O Souza if (p->es_pvid != 0)
9796dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_set_pvid(sc, p->es_port, p->es_pvid);
980b9f07b86SLuiz Otavio O Souza
981b9f07b86SLuiz Otavio O Souza /* Mutually exclusive. */
982b9f07b86SLuiz Otavio O Souza if (p->es_flags & ETHERSWITCH_PORT_ADDTAG &&
983b9f07b86SLuiz Otavio O Souza p->es_flags & ETHERSWITCH_PORT_STRIPTAG) {
984b9f07b86SLuiz Otavio O Souza ARSWITCH_UNLOCK(sc);
985b9f07b86SLuiz Otavio O Souza return (EINVAL);
986b9f07b86SLuiz Otavio O Souza }
987b9f07b86SLuiz Otavio O Souza
988b9f07b86SLuiz Otavio O Souza reg = 0;
989b9f07b86SLuiz Otavio O Souza if (p->es_flags & ETHERSWITCH_PORT_DOUBLE_TAG)
990b9f07b86SLuiz Otavio O Souza reg |= AR8X16_PORT_CTRL_DOUBLE_TAG;
991b9f07b86SLuiz Otavio O Souza if (p->es_flags & ETHERSWITCH_PORT_ADDTAG)
992b9f07b86SLuiz Otavio O Souza reg |= AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_ADD <<
993b9f07b86SLuiz Otavio O Souza AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT;
994b9f07b86SLuiz Otavio O Souza if (p->es_flags & ETHERSWITCH_PORT_STRIPTAG)
995b9f07b86SLuiz Otavio O Souza reg |= AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_STRIP <<
996b9f07b86SLuiz Otavio O Souza AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT;
997b9f07b86SLuiz Otavio O Souza
998b9f07b86SLuiz Otavio O Souza err = arswitch_modifyreg(sc->sc_dev,
999b9f07b86SLuiz Otavio O Souza AR8X16_REG_PORT_CTRL(p->es_port),
1000b9f07b86SLuiz Otavio O Souza 0x3 << AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT |
1001b9f07b86SLuiz Otavio O Souza AR8X16_PORT_CTRL_DOUBLE_TAG, reg);
1002b9f07b86SLuiz Otavio O Souza
1003b9f07b86SLuiz Otavio O Souza ARSWITCH_UNLOCK(sc);
1004a9ad4222SAdrian Chadd return (err);
1005a9ad4222SAdrian Chadd }
1006a9ad4222SAdrian Chadd
1007a9ad4222SAdrian Chadd static int
arswitch_setport(device_t dev,etherswitch_port_t * p)1008a9ad4222SAdrian Chadd arswitch_setport(device_t dev, etherswitch_port_t *p)
1009a9ad4222SAdrian Chadd {
1010c94dc808SAdrian Chadd int err, i;
1011a9ad4222SAdrian Chadd struct arswitch_softc *sc;
1012a9ad4222SAdrian Chadd struct ifmedia *ifm;
1013a9ad4222SAdrian Chadd struct mii_data *mii;
10142e6a8c1aSJustin Hibbits if_t ifp;
1015a9ad4222SAdrian Chadd
1016a9ad4222SAdrian Chadd sc = device_get_softc(dev);
1017749cac13SAdrian Chadd if (p->es_port < 0 || p->es_port > sc->info.es_nports)
1018a9ad4222SAdrian Chadd return (ENXIO);
1019a9ad4222SAdrian Chadd
1020a9ad4222SAdrian Chadd /* Port flags. */
1021a9ad4222SAdrian Chadd if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
1022a9ad4222SAdrian Chadd err = sc->hal.arswitch_port_vlan_setup(sc, p);
1023b9f07b86SLuiz Otavio O Souza if (err)
1024b9f07b86SLuiz Otavio O Souza return (err);
1025b9f07b86SLuiz Otavio O Souza }
1026b9f07b86SLuiz Otavio O Souza
1027c94dc808SAdrian Chadd /* Do not allow media or led changes on CPU port. */
1028749cac13SAdrian Chadd if (arswitch_is_cpuport(sc, p->es_port))
1029b9f07b86SLuiz Otavio O Souza return (0);
1030a043e8c7SAdrian Chadd
1031c94dc808SAdrian Chadd if (AR8X16_IS_SWITCH(sc, AR8327))
1032c94dc808SAdrian Chadd {
1033c94dc808SAdrian Chadd for (i = 0; i < 3; i++)
1034c94dc808SAdrian Chadd {
1035c94dc808SAdrian Chadd int err;
1036c94dc808SAdrian Chadd err = arswitch_setled(sc, p->es_port-1, i, p->es_led[i]);
1037c94dc808SAdrian Chadd if (err)
1038c94dc808SAdrian Chadd return (err);
1039c94dc808SAdrian Chadd }
1040c94dc808SAdrian Chadd }
1041c94dc808SAdrian Chadd
1042a043e8c7SAdrian Chadd mii = arswitch_miiforport(sc, p->es_port);
1043a043e8c7SAdrian Chadd if (mii == NULL)
1044a043e8c7SAdrian Chadd return (ENXIO);
1045a043e8c7SAdrian Chadd
1046a043e8c7SAdrian Chadd ifp = arswitch_ifpforport(sc, p->es_port);
1047a043e8c7SAdrian Chadd
1048a043e8c7SAdrian Chadd ifm = &mii->mii_media;
1049b9f07b86SLuiz Otavio O Souza return (ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA));
1050a043e8c7SAdrian Chadd }
1051a043e8c7SAdrian Chadd
1052c94dc808SAdrian Chadd static int
arswitch_setled(struct arswitch_softc * sc,int phy,int led,int style)1053c94dc808SAdrian Chadd arswitch_setled(struct arswitch_softc *sc, int phy, int led, int style)
1054c94dc808SAdrian Chadd {
1055c94dc808SAdrian Chadd int shift;
10566d011946SKristof Provost int err;
1057c94dc808SAdrian Chadd
1058c94dc808SAdrian Chadd if (phy < 0 || phy > sc->numphys)
1059c94dc808SAdrian Chadd return EINVAL;
1060c94dc808SAdrian Chadd
1061c94dc808SAdrian Chadd if (style < 0 || style > ETHERSWITCH_PORT_LED_MAX)
1062c94dc808SAdrian Chadd return (EINVAL);
1063c94dc808SAdrian Chadd
10646d011946SKristof Provost ARSWITCH_LOCK(sc);
10656d011946SKristof Provost
1066c94dc808SAdrian Chadd shift = ar8327_led_mapping[phy][led].shift;
10676d011946SKristof Provost err = (arswitch_modifyreg(sc->sc_dev,
1068c94dc808SAdrian Chadd ar8327_led_mapping[phy][led].reg,
1069c94dc808SAdrian Chadd 0x03 << shift, led_pattern_table[style] << shift));
10706d011946SKristof Provost ARSWITCH_UNLOCK(sc);
10716d011946SKristof Provost
10726d011946SKristof Provost return (err);
1073c94dc808SAdrian Chadd }
1074c94dc808SAdrian Chadd
1075a043e8c7SAdrian Chadd static void
arswitch_statchg(device_t dev)1076a043e8c7SAdrian Chadd arswitch_statchg(device_t dev)
1077a043e8c7SAdrian Chadd {
10781b334c8bSAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev);
1079a043e8c7SAdrian Chadd
10801b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_POLL, "%s\n", __func__);
1081a043e8c7SAdrian Chadd }
1082a043e8c7SAdrian Chadd
1083a043e8c7SAdrian Chadd static int
arswitch_ifmedia_upd(if_t ifp)10842e6a8c1aSJustin Hibbits arswitch_ifmedia_upd(if_t ifp)
1085a043e8c7SAdrian Chadd {
10862e6a8c1aSJustin Hibbits struct arswitch_softc *sc = if_getsoftc(ifp);
10872e6a8c1aSJustin Hibbits struct mii_data *mii = arswitch_miiforport(sc, if_getdunit(ifp));
1088a043e8c7SAdrian Chadd
1089a043e8c7SAdrian Chadd if (mii == NULL)
1090a043e8c7SAdrian Chadd return (ENXIO);
1091a043e8c7SAdrian Chadd mii_mediachg(mii);
1092a043e8c7SAdrian Chadd return (0);
1093a043e8c7SAdrian Chadd }
1094a043e8c7SAdrian Chadd
1095a043e8c7SAdrian Chadd static void
arswitch_ifmedia_sts(if_t ifp,struct ifmediareq * ifmr)10962e6a8c1aSJustin Hibbits arswitch_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr)
1097a043e8c7SAdrian Chadd {
10982e6a8c1aSJustin Hibbits struct arswitch_softc *sc = if_getsoftc(ifp);
10992e6a8c1aSJustin Hibbits struct mii_data *mii = arswitch_miiforport(sc, if_getdunit(ifp));
1100a043e8c7SAdrian Chadd
11011b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_POLL, "%s\n", __func__);
1102a043e8c7SAdrian Chadd
1103a043e8c7SAdrian Chadd if (mii == NULL)
1104a043e8c7SAdrian Chadd return;
1105a043e8c7SAdrian Chadd mii_pollstat(mii);
1106a043e8c7SAdrian Chadd ifmr->ifm_active = mii->mii_media_active;
1107a043e8c7SAdrian Chadd ifmr->ifm_status = mii->mii_media_status;
1108a043e8c7SAdrian Chadd }
1109a043e8c7SAdrian Chadd
1110b9f07b86SLuiz Otavio O Souza static int
arswitch_getconf(device_t dev,etherswitch_conf_t * conf)1111b9f07b86SLuiz Otavio O Souza arswitch_getconf(device_t dev, etherswitch_conf_t *conf)
1112b9f07b86SLuiz Otavio O Souza {
1113b9f07b86SLuiz Otavio O Souza struct arswitch_softc *sc;
11142ba4bf8fSAdrian Chadd int ret;
1115b9f07b86SLuiz Otavio O Souza
1116b9f07b86SLuiz Otavio O Souza sc = device_get_softc(dev);
1117b9f07b86SLuiz Otavio O Souza
1118b9f07b86SLuiz Otavio O Souza /* Return the VLAN mode. */
1119b9f07b86SLuiz Otavio O Souza conf->cmd = ETHERSWITCH_CONF_VLAN_MODE;
1120b9f07b86SLuiz Otavio O Souza conf->vlan_mode = sc->vlan_mode;
1121b9f07b86SLuiz Otavio O Souza
11222ba4bf8fSAdrian Chadd /* Return the switch ethernet address. */
11232ba4bf8fSAdrian Chadd ret = sc->hal.arswitch_hw_get_switch_macaddr(sc,
11242ba4bf8fSAdrian Chadd &conf->switch_macaddr);
11252ba4bf8fSAdrian Chadd if (ret == 0) {
11262ba4bf8fSAdrian Chadd conf->cmd |= ETHERSWITCH_CONF_SWITCH_MACADDR;
11272ba4bf8fSAdrian Chadd }
11282ba4bf8fSAdrian Chadd
1129b9f07b86SLuiz Otavio O Souza return (0);
1130b9f07b86SLuiz Otavio O Souza }
1131b9f07b86SLuiz Otavio O Souza
1132b9f07b86SLuiz Otavio O Souza static int
arswitch_setconf(device_t dev,etherswitch_conf_t * conf)1133b9f07b86SLuiz Otavio O Souza arswitch_setconf(device_t dev, etherswitch_conf_t *conf)
1134b9f07b86SLuiz Otavio O Souza {
1135b9f07b86SLuiz Otavio O Souza struct arswitch_softc *sc;
1136b9f07b86SLuiz Otavio O Souza int err;
1137b9f07b86SLuiz Otavio O Souza
1138b9f07b86SLuiz Otavio O Souza sc = device_get_softc(dev);
1139b9f07b86SLuiz Otavio O Souza
1140b9f07b86SLuiz Otavio O Souza /* Set the VLAN mode. */
1141b9f07b86SLuiz Otavio O Souza if (conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) {
1142b9f07b86SLuiz Otavio O Souza err = arswitch_set_vlan_mode(sc, conf->vlan_mode);
1143b9f07b86SLuiz Otavio O Souza if (err != 0)
1144b9f07b86SLuiz Otavio O Souza return (err);
1145b9f07b86SLuiz Otavio O Souza }
1146b9f07b86SLuiz Otavio O Souza
11472ba4bf8fSAdrian Chadd /* TODO: Set the switch ethernet address. */
11482ba4bf8fSAdrian Chadd
1149b9f07b86SLuiz Otavio O Souza return (0);
1150b9f07b86SLuiz Otavio O Souza }
1151b9f07b86SLuiz Otavio O Souza
11526dcbabd7SAdrian Chadd static int
arswitch_atu_flush_all(device_t dev)115362042c97SAdrian Chadd arswitch_atu_flush_all(device_t dev)
115462042c97SAdrian Chadd {
115562042c97SAdrian Chadd struct arswitch_softc *sc;
115662042c97SAdrian Chadd int err;
115762042c97SAdrian Chadd
115862042c97SAdrian Chadd sc = device_get_softc(dev);
115962042c97SAdrian Chadd ARSWITCH_LOCK(sc);
116062042c97SAdrian Chadd err = sc->hal.arswitch_atu_flush(sc);
116162042c97SAdrian Chadd /* Invalidate cached ATU */
116262042c97SAdrian Chadd sc->atu.count = 0;
116362042c97SAdrian Chadd ARSWITCH_UNLOCK(sc);
116462042c97SAdrian Chadd return (err);
116562042c97SAdrian Chadd }
116662042c97SAdrian Chadd
116762042c97SAdrian Chadd static int
arswitch_atu_flush_port(device_t dev,int port)116862042c97SAdrian Chadd arswitch_atu_flush_port(device_t dev, int port)
116962042c97SAdrian Chadd {
117062042c97SAdrian Chadd struct arswitch_softc *sc;
117162042c97SAdrian Chadd int err;
117262042c97SAdrian Chadd
117362042c97SAdrian Chadd sc = device_get_softc(dev);
117462042c97SAdrian Chadd ARSWITCH_LOCK(sc);
117562042c97SAdrian Chadd err = sc->hal.arswitch_atu_flush_port(sc, port);
117662042c97SAdrian Chadd /* Invalidate cached ATU */
117762042c97SAdrian Chadd sc->atu.count = 0;
117862042c97SAdrian Chadd ARSWITCH_UNLOCK(sc);
117962042c97SAdrian Chadd return (err);
118062042c97SAdrian Chadd }
118162042c97SAdrian Chadd
118262042c97SAdrian Chadd static int
arswitch_atu_fetch_table(device_t dev,etherswitch_atu_table_t * table)118362042c97SAdrian Chadd arswitch_atu_fetch_table(device_t dev, etherswitch_atu_table_t *table)
118462042c97SAdrian Chadd {
118562042c97SAdrian Chadd struct arswitch_softc *sc;
118662042c97SAdrian Chadd int err, nitems;
118762042c97SAdrian Chadd
118862042c97SAdrian Chadd sc = device_get_softc(dev);
118962042c97SAdrian Chadd
119062042c97SAdrian Chadd ARSWITCH_LOCK(sc);
119162042c97SAdrian Chadd /* Initial setup */
119262042c97SAdrian Chadd nitems = 0;
119362042c97SAdrian Chadd err = sc->hal.arswitch_atu_fetch_table(sc, NULL, 0);
119462042c97SAdrian Chadd
119562042c97SAdrian Chadd /* fetch - ideally yes we'd fetch into a separate table then switch */
11967ed08319SAdrian Chadd while (err == 0 && nitems < sc->atu.size) {
119762042c97SAdrian Chadd err = sc->hal.arswitch_atu_fetch_table(sc,
119862042c97SAdrian Chadd &sc->atu.entries[nitems], 1);
119962042c97SAdrian Chadd if (err == 0) {
120062042c97SAdrian Chadd sc->atu.entries[nitems].id = nitems;
120162042c97SAdrian Chadd nitems++;
120262042c97SAdrian Chadd }
120362042c97SAdrian Chadd }
120462042c97SAdrian Chadd sc->atu.count = nitems;
120562042c97SAdrian Chadd ARSWITCH_UNLOCK(sc);
120662042c97SAdrian Chadd
120762042c97SAdrian Chadd table->es_nitems = nitems;
120862042c97SAdrian Chadd
120962042c97SAdrian Chadd return (0);
121062042c97SAdrian Chadd }
121162042c97SAdrian Chadd
121262042c97SAdrian Chadd static int
arswitch_atu_fetch_table_entry(device_t dev,etherswitch_atu_entry_t * e)121362042c97SAdrian Chadd arswitch_atu_fetch_table_entry(device_t dev, etherswitch_atu_entry_t *e)
121462042c97SAdrian Chadd {
121562042c97SAdrian Chadd struct arswitch_softc *sc;
121662042c97SAdrian Chadd int id;
121762042c97SAdrian Chadd
121862042c97SAdrian Chadd sc = device_get_softc(dev);
121962042c97SAdrian Chadd id = e->id;
122062042c97SAdrian Chadd
122162042c97SAdrian Chadd ARSWITCH_LOCK(sc);
122262042c97SAdrian Chadd if (id > sc->atu.count) {
122362042c97SAdrian Chadd ARSWITCH_UNLOCK(sc);
122462042c97SAdrian Chadd return (ENOENT);
122562042c97SAdrian Chadd }
122662042c97SAdrian Chadd
122762042c97SAdrian Chadd memcpy(e, &sc->atu.entries[id], sizeof(*e));
122862042c97SAdrian Chadd ARSWITCH_UNLOCK(sc);
122962042c97SAdrian Chadd return (0);
123062042c97SAdrian Chadd }
123162042c97SAdrian Chadd
123262042c97SAdrian Chadd static int
arswitch_getvgroup(device_t dev,etherswitch_vlangroup_t * e)12336dcbabd7SAdrian Chadd arswitch_getvgroup(device_t dev, etherswitch_vlangroup_t *e)
12346dcbabd7SAdrian Chadd {
12356dcbabd7SAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev);
12366dcbabd7SAdrian Chadd
12376dcbabd7SAdrian Chadd return (sc->hal.arswitch_vlan_getvgroup(sc, e));
12386dcbabd7SAdrian Chadd }
12396dcbabd7SAdrian Chadd
12406dcbabd7SAdrian Chadd static int
arswitch_setvgroup(device_t dev,etherswitch_vlangroup_t * e)12416dcbabd7SAdrian Chadd arswitch_setvgroup(device_t dev, etherswitch_vlangroup_t *e)
12426dcbabd7SAdrian Chadd {
12436dcbabd7SAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev);
12446dcbabd7SAdrian Chadd
12456dcbabd7SAdrian Chadd return (sc->hal.arswitch_vlan_setvgroup(sc, e));
12466dcbabd7SAdrian Chadd }
12476dcbabd7SAdrian Chadd
124878549b94SAdrian Chadd static int
arswitch_readphy(device_t dev,int phy,int reg)124978549b94SAdrian Chadd arswitch_readphy(device_t dev, int phy, int reg)
125078549b94SAdrian Chadd {
125178549b94SAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev);
125278549b94SAdrian Chadd
125378549b94SAdrian Chadd return (sc->hal.arswitch_phy_read(dev, phy, reg));
125478549b94SAdrian Chadd }
125578549b94SAdrian Chadd
125678549b94SAdrian Chadd static int
arswitch_writephy(device_t dev,int phy,int reg,int val)125778549b94SAdrian Chadd arswitch_writephy(device_t dev, int phy, int reg, int val)
125878549b94SAdrian Chadd {
125978549b94SAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev);
126078549b94SAdrian Chadd
126178549b94SAdrian Chadd return (sc->hal.arswitch_phy_write(dev, phy, reg, val));
126278549b94SAdrian Chadd }
126378549b94SAdrian Chadd
1264a043e8c7SAdrian Chadd static device_method_t arswitch_methods[] = {
1265a043e8c7SAdrian Chadd /* Device interface */
1266a043e8c7SAdrian Chadd DEVMETHOD(device_probe, arswitch_probe),
1267a043e8c7SAdrian Chadd DEVMETHOD(device_attach, arswitch_attach),
1268a043e8c7SAdrian Chadd DEVMETHOD(device_detach, arswitch_detach),
1269a043e8c7SAdrian Chadd
1270a043e8c7SAdrian Chadd /* bus interface */
1271a043e8c7SAdrian Chadd DEVMETHOD(bus_add_child, device_add_child_ordered),
1272a043e8c7SAdrian Chadd
1273a043e8c7SAdrian Chadd /* MII interface */
1274a043e8c7SAdrian Chadd DEVMETHOD(miibus_readreg, arswitch_readphy),
1275a043e8c7SAdrian Chadd DEVMETHOD(miibus_writereg, arswitch_writephy),
1276a043e8c7SAdrian Chadd DEVMETHOD(miibus_statchg, arswitch_statchg),
1277a043e8c7SAdrian Chadd
1278a043e8c7SAdrian Chadd /* MDIO interface */
1279a043e8c7SAdrian Chadd DEVMETHOD(mdio_readreg, arswitch_readphy),
1280a043e8c7SAdrian Chadd DEVMETHOD(mdio_writereg, arswitch_writephy),
1281a043e8c7SAdrian Chadd
1282a043e8c7SAdrian Chadd /* etherswitch interface */
1283454d507aSAleksandr Rybalko DEVMETHOD(etherswitch_lock, arswitch_lock),
1284454d507aSAleksandr Rybalko DEVMETHOD(etherswitch_unlock, arswitch_unlock),
1285a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_getinfo, arswitch_getinfo),
1286a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_readreg, arswitch_readreg),
1287a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_writereg, arswitch_writereg),
1288a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_readphyreg, arswitch_readphy),
1289a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_writephyreg, arswitch_writephy),
1290a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_getport, arswitch_getport),
1291a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_setport, arswitch_setport),
1292a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_getvgroup, arswitch_getvgroup),
1293a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_setvgroup, arswitch_setvgroup),
1294b9f07b86SLuiz Otavio O Souza DEVMETHOD(etherswitch_getconf, arswitch_getconf),
1295b9f07b86SLuiz Otavio O Souza DEVMETHOD(etherswitch_setconf, arswitch_setconf),
129662042c97SAdrian Chadd DEVMETHOD(etherswitch_flush_all, arswitch_atu_flush_all),
129762042c97SAdrian Chadd DEVMETHOD(etherswitch_flush_port, arswitch_atu_flush_port),
129862042c97SAdrian Chadd DEVMETHOD(etherswitch_fetch_table, arswitch_atu_fetch_table),
129962042c97SAdrian Chadd DEVMETHOD(etherswitch_fetch_table_entry, arswitch_atu_fetch_table_entry),
1300a043e8c7SAdrian Chadd
1301a043e8c7SAdrian Chadd DEVMETHOD_END
1302a043e8c7SAdrian Chadd };
1303a043e8c7SAdrian Chadd
1304a043e8c7SAdrian Chadd DEFINE_CLASS_0(arswitch, arswitch_driver, arswitch_methods,
1305a043e8c7SAdrian Chadd sizeof(struct arswitch_softc));
1306a043e8c7SAdrian Chadd
130742726c2fSJohn Baldwin DRIVER_MODULE(arswitch, mdio, arswitch_driver, 0, 0);
13083e38757dSJohn Baldwin DRIVER_MODULE(miibus, arswitch, miibus_driver, 0, 0);
13098933f7d6SJohn Baldwin DRIVER_MODULE(mdio, arswitch, mdio_driver, 0, 0);
1310829a13faSJohn Baldwin DRIVER_MODULE(etherswitch, arswitch, etherswitch_driver, 0, 0);
1311a043e8c7SAdrian Chadd MODULE_VERSION(arswitch, 1);
1312a043e8c7SAdrian Chadd MODULE_DEPEND(arswitch, miibus, 1, 1, 1); /* XXX which versions? */
1313a043e8c7SAdrian Chadd MODULE_DEPEND(arswitch, etherswitch, 1, 1, 1); /* XXX which versions? */
1314