arswitch.c revision 262429
1189251Ssam/*-
2189251Ssam * Copyright (c) 2011-2012 Stefan Bethke.
3189251Ssam * Copyright (c) 2012 Adrian Chadd.
4189251Ssam * All rights reserved.
5189251Ssam *
6189251Ssam * Redistribution and use in source and binary forms, with or without
7189251Ssam * modification, are permitted provided that the following conditions
8189251Ssam * are met:
9189251Ssam * 1. Redistributions of source code must retain the above copyright
10189251Ssam *    notice, this list of conditions and the following disclaimer.
11189251Ssam * 2. Redistributions in binary form must reproduce the above copyright
12189251Ssam *    notice, this list of conditions and the following disclaimer in the
13189251Ssam *    documentation and/or other materials provided with the distribution.
14189251Ssam *
15189251Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16189251Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17189251Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18214734Srpaulo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19189251Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20189251Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21189251Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22189251Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23189251Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24189251Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25189251Ssam * SUCH DAMAGE.
26189251Ssam *
27189251Ssam * $FreeBSD: head/sys/dev/etherswitch/arswitch/arswitch.c 262429 2014-02-24 04:44:28Z adrian $
28189251Ssam */
29189251Ssam
30189251Ssam#include <sys/param.h>
31189251Ssam#include <sys/bus.h>
32189251Ssam#include <sys/errno.h>
33189251Ssam#include <sys/kernel.h>
34189251Ssam#include <sys/malloc.h>
35189251Ssam#include <sys/module.h>
36189251Ssam#include <sys/socket.h>
37189251Ssam#include <sys/sockio.h>
38189251Ssam#include <sys/sysctl.h>
39189251Ssam#include <sys/systm.h>
40189251Ssam
41189251Ssam#include <net/if.h>
42189251Ssam#include <net/if_var.h>
43189251Ssam#include <net/if_arp.h>
44189251Ssam#include <net/ethernet.h>
45189251Ssam#include <net/if_dl.h>
46189251Ssam#include <net/if_media.h>
47189251Ssam#include <net/if_types.h>
48189251Ssam
49189251Ssam#include <machine/bus.h>
50189251Ssam#include <dev/iicbus/iic.h>
51189251Ssam#include <dev/iicbus/iiconf.h>
52189251Ssam#include <dev/iicbus/iicbus.h>
53189251Ssam#include <dev/mii/mii.h>
54189251Ssam#include <dev/mii/miivar.h>
55189251Ssam#include <dev/etherswitch/mdio.h>
56189251Ssam
57189251Ssam#include <dev/etherswitch/etherswitch.h>
58189251Ssam
59189251Ssam#include <dev/etherswitch/arswitch/arswitchreg.h>
60189251Ssam#include <dev/etherswitch/arswitch/arswitchvar.h>
61189251Ssam#include <dev/etherswitch/arswitch/arswitch_reg.h>
62189251Ssam#include <dev/etherswitch/arswitch/arswitch_phy.h>
63189251Ssam#include <dev/etherswitch/arswitch/arswitch_vlans.h>
64189251Ssam
65189251Ssam#include <dev/etherswitch/arswitch/arswitch_7240.h>
66189251Ssam#include <dev/etherswitch/arswitch/arswitch_8216.h>
67189251Ssam#include <dev/etherswitch/arswitch/arswitch_8226.h>
68189251Ssam#include <dev/etherswitch/arswitch/arswitch_8316.h>
69189251Ssam#include <dev/etherswitch/arswitch/arswitch_9340.h>
70189251Ssam
71189251Ssam#include "mdio_if.h"
72189251Ssam#include "miibus_if.h"
73189251Ssam#include "etherswitch_if.h"
74189251Ssam
75189251Ssam#if	defined(DEBUG)
76189251Ssamstatic SYSCTL_NODE(_debug, OID_AUTO, arswitch, CTLFLAG_RD, 0, "arswitch");
77189251Ssam#endif
78189251Ssam
79189251Ssamstatic inline int arswitch_portforphy(int phy);
80189251Ssamstatic void arswitch_tick(void *arg);
81189251Ssamstatic int arswitch_ifmedia_upd(struct ifnet *);
82189251Ssamstatic void arswitch_ifmedia_sts(struct ifnet *, struct ifmediareq *);
83189251Ssamstatic int ar8xxx_port_vlan_setup(struct arswitch_softc *sc,
84189251Ssam    etherswitch_port_t *p);
85189251Ssamstatic int ar8xxx_port_vlan_get(struct arswitch_softc *sc,
86189251Ssam    etherswitch_port_t *p);
87189251Ssam
88189251Ssamstatic int
89189251Ssamarswitch_probe(device_t dev)
90189251Ssam{
91189251Ssam	struct arswitch_softc *sc;
92189251Ssam	uint32_t id;
93189251Ssam	char *chipname, desc[256];
94189251Ssam
95189251Ssam	sc = device_get_softc(dev);
96189251Ssam	bzero(sc, sizeof(*sc));
97189251Ssam	sc->page = -1;
98189251Ssam
99189251Ssam	/* AR7240 probe */
100189251Ssam	if (ar7240_probe(dev) == 0) {
101189251Ssam		chipname = "AR7240";
102189251Ssam		sc->sc_switchtype = AR8X16_SWITCH_AR7240;
103189251Ssam		sc->is_internal_switch = 1;
104189251Ssam		id = 0;
105189251Ssam		goto done;
106189251Ssam	}
107189251Ssam
108189251Ssam	/* AR9340 probe */
109189251Ssam	if (ar9340_probe(dev) == 0) {
110189251Ssam		chipname = "AR9340";
111189251Ssam		sc->sc_switchtype = AR8X16_SWITCH_AR9340;
112189251Ssam		sc->is_internal_switch = 1;
113189251Ssam		id = 0;
114189251Ssam		goto done;
115189251Ssam	}
116189251Ssam
117189251Ssam	/* AR8xxx probe */
118189251Ssam	id = arswitch_readreg(dev, AR8X16_REG_MASK_CTRL);
119189251Ssam	sc->chip_rev = (id & AR8X16_MASK_CTRL_REV_MASK);
120189251Ssam	sc->chip_ver = (id & AR8X16_MASK_CTRL_VER_MASK) > AR8X16_MASK_CTRL_VER_SHIFT;
121189251Ssam	switch (id & (AR8X16_MASK_CTRL_VER_MASK | AR8X16_MASK_CTRL_REV_MASK)) {
122189251Ssam	case 0x0101:
123189251Ssam		chipname = "AR8216";
124189251Ssam		sc->sc_switchtype = AR8X16_SWITCH_AR8216;
125189251Ssam		break;
126189251Ssam	case 0x0201:
127189251Ssam		chipname = "AR8226";
128189251Ssam		sc->sc_switchtype = AR8X16_SWITCH_AR8226;
129189251Ssam		break;
130189251Ssam	/* 0x0301 - AR8236 */
131189251Ssam	case 0x1000:
132189251Ssam	case 0x1001:
133189251Ssam		chipname = "AR8316";
134189251Ssam		sc->sc_switchtype = AR8X16_SWITCH_AR8316;
135189251Ssam		break;
136189251Ssam	case 0x1202:
137189251Ssam		chipname = "AR8327";
138189251Ssam		sc->sc_switchtype = AR8X16_SWITCH_AR8327;
139189251Ssam		sc->mii_lo_first = 1;
140189251Ssam		break;
141189251Ssam	default:
142189251Ssam		chipname = NULL;
143189251Ssam	}
144189251Ssam
145189251Ssamdone:
146189251Ssam
147189251Ssam	DPRINTF(dev, "chipname=%s, id=%08x\n", chipname, id);
148189251Ssam	if (chipname != NULL) {
149189251Ssam		snprintf(desc, sizeof(desc),
150189251Ssam		    "Atheros %s Ethernet Switch",
151189251Ssam		    chipname);
152189251Ssam		device_set_desc_copy(dev, desc);
153189251Ssam		return (BUS_PROBE_DEFAULT);
154189251Ssam	}
155189251Ssam	return (ENXIO);
156189251Ssam}
157189251Ssam
158189251Ssamstatic int
159189251Ssamarswitch_attach_phys(struct arswitch_softc *sc)
160189251Ssam{
161189251Ssam	int phy, err = 0;
162189251Ssam	char name[IFNAMSIZ];
163189251Ssam
164189251Ssam	/* PHYs need an interface, so we generate a dummy one */
165189251Ssam	snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->sc_dev));
166189251Ssam	for (phy = 0; phy < sc->numphys; phy++) {
167189251Ssam		sc->ifp[phy] = if_alloc(IFT_ETHER);
168189251Ssam		sc->ifp[phy]->if_softc = sc;
169189251Ssam		sc->ifp[phy]->if_flags |= IFF_UP | IFF_BROADCAST |
170189251Ssam		    IFF_DRV_RUNNING | IFF_SIMPLEX;
171189251Ssam		sc->ifname[phy] = malloc(strlen(name)+1, M_DEVBUF, M_WAITOK);
172189251Ssam		bcopy(name, sc->ifname[phy], strlen(name)+1);
173189251Ssam		if_initname(sc->ifp[phy], sc->ifname[phy],
174189251Ssam		    arswitch_portforphy(phy));
175189251Ssam		err = mii_attach(sc->sc_dev, &sc->miibus[phy], sc->ifp[phy],
176189251Ssam		    arswitch_ifmedia_upd, arswitch_ifmedia_sts, \
177189251Ssam		    BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0);
178189251Ssam		DPRINTF(sc->sc_dev, "%s attached to pseudo interface %s\n",
179189251Ssam		    device_get_nameunit(sc->miibus[phy]),
180189251Ssam		    sc->ifp[phy]->if_xname);
181189251Ssam		if (err != 0) {
182189251Ssam			device_printf(sc->sc_dev,
183189251Ssam			    "attaching PHY %d failed\n",
184189251Ssam			    phy);
185189251Ssam		}
186189251Ssam	}
187189251Ssam	return (err);
188189251Ssam}
189189251Ssam
190189251Ssamstatic int
191189251Ssamarswitch_reset(device_t dev)
192189251Ssam{
193189251Ssam
194189251Ssam	arswitch_writereg(dev, AR8X16_REG_MASK_CTRL,
195189251Ssam	    AR8X16_MASK_CTRL_SOFT_RESET);
196189251Ssam	DELAY(1000);
197189251Ssam	if (arswitch_readreg(dev, AR8X16_REG_MASK_CTRL) &
198189251Ssam	    AR8X16_MASK_CTRL_SOFT_RESET) {
199189251Ssam		device_printf(dev, "unable to reset switch\n");
200189251Ssam		return (-1);
201189251Ssam	}
202189251Ssam	return (0);
203189251Ssam}
204189251Ssam
205189251Ssamstatic int
206189251Ssamarswitch_set_vlan_mode(struct arswitch_softc *sc, uint32_t mode)
207189251Ssam{
208189251Ssam
209189251Ssam	/* Check for invalid modes. */
210189251Ssam	if ((mode & sc->info.es_vlan_caps) != mode)
211189251Ssam		return (EINVAL);
212189251Ssam
213189251Ssam	switch (mode) {
214189251Ssam	case ETHERSWITCH_VLAN_DOT1Q:
215189251Ssam		sc->vlan_mode = ETHERSWITCH_VLAN_DOT1Q;
216189251Ssam		break;
217189251Ssam	case ETHERSWITCH_VLAN_PORT:
218189251Ssam		sc->vlan_mode = ETHERSWITCH_VLAN_PORT;
219189251Ssam		break;
220189251Ssam	default:
221189251Ssam		sc->vlan_mode = 0;
222189251Ssam	};
223189251Ssam
224189251Ssam	/* Reset VLANs. */
225189251Ssam	sc->hal.arswitch_vlan_init_hw(sc);
226189251Ssam
227189251Ssam	return (0);
228189251Ssam}
229189251Ssam
230189251Ssamstatic void
231189251Ssamar8xxx_port_init(struct arswitch_softc *sc, int port)
232189251Ssam{
233189251Ssam
234189251Ssam	/* Port0 - CPU */
235189251Ssam	if (port == AR8X16_PORT_CPU) {
236189251Ssam		arswitch_writereg(sc->sc_dev, AR8X16_REG_PORT_STS(0),
237189251Ssam		    (AR8X16_IS_SWITCH(sc, AR8216) ?
238189251Ssam		    AR8X16_PORT_STS_SPEED_100 : AR8X16_PORT_STS_SPEED_1000) |
239189251Ssam		    (AR8X16_IS_SWITCH(sc, AR8216) ? 0 : AR8X16_PORT_STS_RXFLOW) |
240189251Ssam		    (AR8X16_IS_SWITCH(sc, AR8216) ? 0 : AR8X16_PORT_STS_TXFLOW) |
241189251Ssam		    AR8X16_PORT_STS_RXMAC |
242189251Ssam		    AR8X16_PORT_STS_TXMAC |
243189251Ssam		    AR8X16_PORT_STS_DUPLEX);
244189251Ssam		arswitch_writereg(sc->sc_dev, AR8X16_REG_PORT_CTRL(0),
245189251Ssam		    arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_CTRL(0)) &
246189251Ssam		    ~AR8X16_PORT_CTRL_HEADER);
247189251Ssam	} else {
248189251Ssam		/* Set ports to auto negotiation. */
249189251Ssam		arswitch_writereg(sc->sc_dev, AR8X16_REG_PORT_STS(port),
250189251Ssam		    AR8X16_PORT_STS_LINK_AUTO);
251189251Ssam		arswitch_writereg(sc->sc_dev, AR8X16_REG_PORT_CTRL(port),
252189251Ssam		    arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_CTRL(port)) &
253189251Ssam		    ~AR8X16_PORT_CTRL_HEADER);
254189251Ssam	}
255189251Ssam}
256189251Ssam
257189251Ssamstatic int
258189251Ssamarswitch_attach(device_t dev)
259189251Ssam{
260189251Ssam	struct arswitch_softc *sc;
261189251Ssam	int err = 0;
262189251Ssam	int port;
263189251Ssam
264189251Ssam	sc = device_get_softc(dev);
265189251Ssam
266189251Ssam	/* sc->sc_switchtype is already decided in arswitch_probe() */
267189251Ssam	sc->sc_dev = dev;
268189251Ssam	mtx_init(&sc->sc_mtx, "arswitch", NULL, MTX_DEF);
269189251Ssam	sc->page = -1;
270189251Ssam	strlcpy(sc->info.es_name, device_get_desc(dev),
271189251Ssam	    sizeof(sc->info.es_name));
272189251Ssam
273189251Ssam	/* Default HAL methods */
274214734Srpaulo	sc->hal.arswitch_port_init = ar8xxx_port_init;
275214734Srpaulo	sc->hal.arswitch_port_vlan_setup = ar8xxx_port_vlan_setup;
276189251Ssam	sc->hal.arswitch_port_vlan_get = ar8xxx_port_vlan_get;
277189251Ssam	sc->hal.arswitch_vlan_init_hw = ar8xxx_reset_vlans;
278189251Ssam	sc->hal.arswitch_vlan_getvgroup = ar8xxx_getvgroup;
279189251Ssam	sc->hal.arswitch_vlan_setvgroup = ar8xxx_setvgroup;
280189251Ssam	sc->hal.arswitch_vlan_get_pvid = ar8xxx_get_pvid;
281189251Ssam	sc->hal.arswitch_vlan_set_pvid = ar8xxx_set_pvid;
282189251Ssam
283214734Srpaulo	/*
284214734Srpaulo	 * Attach switch related functions
285214734Srpaulo	 */
286214734Srpaulo	if (AR8X16_IS_SWITCH(sc, AR7240))
287214734Srpaulo		ar7240_attach(sc);
288189251Ssam	else if (AR8X16_IS_SWITCH(sc, AR9340))
289189251Ssam		ar9340_attach(sc);
290189251Ssam	else if (AR8X16_IS_SWITCH(sc, AR8216))
291189251Ssam		ar8216_attach(sc);
292189251Ssam	else if (AR8X16_IS_SWITCH(sc, AR8226))
293189251Ssam		ar8226_attach(sc);
294189251Ssam	else if (AR8X16_IS_SWITCH(sc, AR8316))
295189251Ssam		ar8316_attach(sc);
296189251Ssam	else
297189251Ssam		return (ENXIO);
298189251Ssam
299189251Ssam	/* Common defaults. */
300189251Ssam	sc->info.es_nports = 5; /* XXX technically 6, but 6th not used */
301189251Ssam
302189251Ssam	/* XXX Defaults for externally connected AR8316 */
303189251Ssam	sc->numphys = 4;
304189251Ssam	sc->phy4cpu = 1;
305189251Ssam	sc->is_rgmii = 1;
306189251Ssam	sc->is_gmii = 0;
307189251Ssam	sc->is_mii = 0;
308189251Ssam
309189251Ssam	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
310189251Ssam	    "numphys", &sc->numphys);
311189251Ssam	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
312189251Ssam	    "phy4cpu", &sc->phy4cpu);
313189251Ssam	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
314189251Ssam	    "is_rgmii", &sc->is_rgmii);
315189251Ssam	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
316189251Ssam	    "is_gmii", &sc->is_gmii);
317189251Ssam	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
318189251Ssam	    "is_mii", &sc->is_mii);
319189251Ssam
320189251Ssam	if (sc->numphys > AR8X16_NUM_PHYS)
321189251Ssam		sc->numphys = AR8X16_NUM_PHYS;
322189251Ssam
323189251Ssam	/* Reset the switch. */
324189251Ssam	if (arswitch_reset(dev))
325214734Srpaulo		return (ENXIO);
326189251Ssam
327189251Ssam	err = sc->hal.arswitch_hw_setup(sc);
328189251Ssam	if (err != 0)
329189251Ssam		return (err);
330189251Ssam
331189251Ssam	err = sc->hal.arswitch_hw_global_setup(sc);
332189251Ssam	if (err != 0)
333189251Ssam		return (err);
334189251Ssam
335189251Ssam	/* Initialize the switch ports. */
336189251Ssam	for (port = 0; port <= sc->numphys; port++) {
337189251Ssam		sc->hal.arswitch_port_init(sc, port);
338189251Ssam	}
339189251Ssam
340189251Ssam	/*
341189251Ssam	 * Attach the PHYs and complete the bus enumeration.
342189251Ssam	 */
343189251Ssam	err = arswitch_attach_phys(sc);
344189251Ssam	if (err != 0)
345189251Ssam		return (err);
346189251Ssam
347214734Srpaulo	/* Default to ingress filters off. */
348214734Srpaulo	err = arswitch_set_vlan_mode(sc, 0);
349214734Srpaulo	if (err != 0)
350214734Srpaulo		return (err);
351214734Srpaulo
352214734Srpaulo	bus_generic_probe(dev);
353214734Srpaulo	bus_enumerate_hinted_children(dev);
354214734Srpaulo	err = bus_generic_attach(dev);
355214734Srpaulo	if (err != 0)
356214734Srpaulo		return (err);
357214734Srpaulo
358214734Srpaulo	callout_init_mtx(&sc->callout_tick, &sc->sc_mtx, 0);
359214734Srpaulo
360214734Srpaulo	ARSWITCH_LOCK(sc);
361214734Srpaulo	arswitch_tick(sc);
362214734Srpaulo	ARSWITCH_UNLOCK(sc);
363214734Srpaulo
364214734Srpaulo	return (err);
365214734Srpaulo}
366214734Srpaulo
367214734Srpaulostatic int
368214734Srpauloarswitch_detach(device_t dev)
369214734Srpaulo{
370214734Srpaulo	struct arswitch_softc *sc = device_get_softc(dev);
371214734Srpaulo	int i;
372214734Srpaulo
373214734Srpaulo	callout_drain(&sc->callout_tick);
374214734Srpaulo
375214734Srpaulo	for (i=0; i < sc->numphys; i++) {
376189251Ssam		if (sc->miibus[i] != NULL)
377189251Ssam			device_delete_child(dev, sc->miibus[i]);
378189251Ssam		if (sc->ifp[i] != NULL)
379			if_free(sc->ifp[i]);
380		free(sc->ifname[i], M_DEVBUF);
381	}
382
383	bus_generic_detach(dev);
384	mtx_destroy(&sc->sc_mtx);
385
386	return (0);
387}
388
389/*
390 * Convert PHY number to port number. PHY0 is connected to port 1, PHY1 to
391 * port 2, etc.
392 */
393static inline int
394arswitch_portforphy(int phy)
395{
396	return (phy+1);
397}
398
399static inline struct mii_data *
400arswitch_miiforport(struct arswitch_softc *sc, int port)
401{
402	int phy = port-1;
403
404	if (phy < 0 || phy >= sc->numphys)
405		return (NULL);
406	return (device_get_softc(sc->miibus[phy]));
407}
408
409static inline struct ifnet *
410arswitch_ifpforport(struct arswitch_softc *sc, int port)
411{
412	int phy = port-1;
413
414	if (phy < 0 || phy >= sc->numphys)
415		return (NULL);
416	return (sc->ifp[phy]);
417}
418
419/*
420 * Convert port status to ifmedia.
421 */
422static void
423arswitch_update_ifmedia(int portstatus, u_int *media_status, u_int *media_active)
424{
425	*media_active = IFM_ETHER;
426	*media_status = IFM_AVALID;
427
428	if ((portstatus & AR8X16_PORT_STS_LINK_UP) != 0)
429		*media_status |= IFM_ACTIVE;
430	else {
431		*media_active |= IFM_NONE;
432		return;
433	}
434	switch (portstatus & AR8X16_PORT_STS_SPEED_MASK) {
435	case AR8X16_PORT_STS_SPEED_10:
436		*media_active |= IFM_10_T;
437		break;
438	case AR8X16_PORT_STS_SPEED_100:
439		*media_active |= IFM_100_TX;
440		break;
441	case AR8X16_PORT_STS_SPEED_1000:
442		*media_active |= IFM_1000_T;
443		break;
444	}
445	if ((portstatus & AR8X16_PORT_STS_DUPLEX) == 0)
446		*media_active |= IFM_FDX;
447	else
448		*media_active |= IFM_HDX;
449	if ((portstatus & AR8X16_PORT_STS_TXFLOW) != 0)
450		*media_active |= IFM_ETH_TXPAUSE;
451	if ((portstatus & AR8X16_PORT_STS_RXFLOW) != 0)
452		*media_active |= IFM_ETH_RXPAUSE;
453}
454
455/*
456 * Poll the status for all PHYs.  We're using the switch port status because
457 * thats a lot quicker to read than talking to all the PHYs.  Care must be
458 * taken that the resulting ifmedia_active is identical to what the PHY will
459 * compute, or gratuitous link status changes will occur whenever the PHYs
460 * update function is called.
461 */
462static void
463arswitch_miipollstat(struct arswitch_softc *sc)
464{
465	int i;
466	struct mii_data *mii;
467	struct mii_softc *miisc;
468	int portstatus;
469
470	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
471
472	for (i = 0; i < sc->numphys; i++) {
473		if (sc->miibus[i] == NULL)
474			continue;
475		mii = device_get_softc(sc->miibus[i]);
476		/* XXX This would be nice to have abstracted out to be per-chip */
477		/* AR8327/AR8337 has a different register base */
478		if (AR8X16_IS_SWITCH(sc, AR8327))
479			portstatus = arswitch_readreg(sc->sc_dev,
480			    AR8327_REG_PORT_STATUS(arswitch_portforphy(i)));
481		else
482			portstatus = arswitch_readreg(sc->sc_dev,
483			    AR8X16_REG_PORT_STS(arswitch_portforphy(i)));
484
485#if 0
486		DPRINTF(sc->sc_dev, "p[%d]=%b\n",
487		    i,
488		    portstatus,
489		    "\20\3TXMAC\4RXMAC\5TXFLOW\6RXFLOW\7"
490		    "DUPLEX\11LINK_UP\12LINK_AUTO\13LINK_PAUSE");
491#endif
492		arswitch_update_ifmedia(portstatus, &mii->mii_media_status,
493		    &mii->mii_media_active);
494		LIST_FOREACH(miisc, &mii->mii_phys, mii_list) {
495			if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) !=
496			    miisc->mii_inst)
497				continue;
498			mii_phy_update(miisc, MII_POLLSTAT);
499		}
500	}
501}
502
503static void
504arswitch_tick(void *arg)
505{
506	struct arswitch_softc *sc = arg;
507
508	arswitch_miipollstat(sc);
509	callout_reset(&sc->callout_tick, hz, arswitch_tick, sc);
510}
511
512static void
513arswitch_lock(device_t dev)
514{
515	struct arswitch_softc *sc = device_get_softc(dev);
516
517	ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
518	ARSWITCH_LOCK(sc);
519}
520
521static void
522arswitch_unlock(device_t dev)
523{
524	struct arswitch_softc *sc = device_get_softc(dev);
525
526	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
527	ARSWITCH_UNLOCK(sc);
528}
529
530static etherswitch_info_t *
531arswitch_getinfo(device_t dev)
532{
533	struct arswitch_softc *sc = device_get_softc(dev);
534
535	return (&sc->info);
536}
537
538static int
539ar8xxx_port_vlan_get(struct arswitch_softc *sc, etherswitch_port_t *p)
540{
541	uint32_t reg;
542
543	ARSWITCH_LOCK(sc);
544
545	/* Retrieve the PVID. */
546	sc->hal.arswitch_vlan_get_pvid(sc, p->es_port, &p->es_pvid);
547
548	/* Port flags. */
549	reg = arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_CTRL(p->es_port));
550	if (reg & AR8X16_PORT_CTRL_DOUBLE_TAG)
551		p->es_flags |= ETHERSWITCH_PORT_DOUBLE_TAG;
552	reg >>= AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT;
553	if ((reg & 0x3) == AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_ADD)
554		p->es_flags |= ETHERSWITCH_PORT_ADDTAG;
555	if ((reg & 0x3) == AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_STRIP)
556		p->es_flags |= ETHERSWITCH_PORT_STRIPTAG;
557	ARSWITCH_UNLOCK(sc);
558
559	return (0);
560}
561
562static int
563arswitch_getport(device_t dev, etherswitch_port_t *p)
564{
565	struct arswitch_softc *sc;
566	struct mii_data *mii;
567	struct ifmediareq *ifmr;
568	int err;
569
570	sc = device_get_softc(dev);
571	if (p->es_port < 0 || p->es_port > sc->numphys)
572		return (ENXIO);
573
574	err = sc->hal.arswitch_port_vlan_get(sc, p);
575	if (err != 0)
576		return (err);
577
578	mii = arswitch_miiforport(sc, p->es_port);
579	if (p->es_port == AR8X16_PORT_CPU) {
580		/* fill in fixed values for CPU port */
581		/* XXX is this valid in all cases? */
582		p->es_flags |= ETHERSWITCH_PORT_CPU;
583		ifmr = &p->es_ifmr;
584		ifmr->ifm_count = 0;
585		ifmr->ifm_current = ifmr->ifm_active =
586		    IFM_ETHER | IFM_1000_T | IFM_FDX;
587		ifmr->ifm_mask = 0;
588		ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
589	} else if (mii != NULL) {
590		err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr,
591		    &mii->mii_media, SIOCGIFMEDIA);
592		if (err)
593			return (err);
594	} else {
595		return (ENXIO);
596	}
597	return (0);
598}
599
600static int
601ar8xxx_port_vlan_setup(struct arswitch_softc *sc, etherswitch_port_t *p)
602{
603	uint32_t reg;
604	int err;
605
606	ARSWITCH_LOCK(sc);
607
608	/* Set the PVID. */
609	if (p->es_pvid != 0)
610		sc->hal.arswitch_vlan_set_pvid(sc, p->es_port, p->es_pvid);
611
612	/* Mutually exclusive. */
613	if (p->es_flags & ETHERSWITCH_PORT_ADDTAG &&
614	    p->es_flags & ETHERSWITCH_PORT_STRIPTAG) {
615		ARSWITCH_UNLOCK(sc);
616		return (EINVAL);
617	}
618
619	reg = 0;
620	if (p->es_flags & ETHERSWITCH_PORT_DOUBLE_TAG)
621		reg |= AR8X16_PORT_CTRL_DOUBLE_TAG;
622	if (p->es_flags & ETHERSWITCH_PORT_ADDTAG)
623		reg |= AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_ADD <<
624		    AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT;
625	if (p->es_flags & ETHERSWITCH_PORT_STRIPTAG)
626		reg |= AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_STRIP <<
627		    AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT;
628
629	err = arswitch_modifyreg(sc->sc_dev,
630	    AR8X16_REG_PORT_CTRL(p->es_port),
631	    0x3 << AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT |
632	    AR8X16_PORT_CTRL_DOUBLE_TAG, reg);
633
634	ARSWITCH_UNLOCK(sc);
635	return (err);
636}
637
638static int
639arswitch_setport(device_t dev, etherswitch_port_t *p)
640{
641	int err;
642	struct arswitch_softc *sc;
643	struct ifmedia *ifm;
644	struct mii_data *mii;
645	struct ifnet *ifp;
646
647	sc = device_get_softc(dev);
648	if (p->es_port < 0 || p->es_port > sc->numphys)
649		return (ENXIO);
650
651	/* Port flags. */
652	if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
653		err = sc->hal.arswitch_port_vlan_setup(sc, p);
654		if (err)
655			return (err);
656	}
657
658	/* Do not allow media changes on CPU port. */
659	if (p->es_port == AR8X16_PORT_CPU)
660		return (0);
661
662	mii = arswitch_miiforport(sc, p->es_port);
663	if (mii == NULL)
664		return (ENXIO);
665
666	ifp = arswitch_ifpforport(sc, p->es_port);
667
668	ifm = &mii->mii_media;
669	return (ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA));
670}
671
672static void
673arswitch_statchg(device_t dev)
674{
675
676	DPRINTF(dev, "%s\n", __func__);
677}
678
679static int
680arswitch_ifmedia_upd(struct ifnet *ifp)
681{
682	struct arswitch_softc *sc = ifp->if_softc;
683	struct mii_data *mii = arswitch_miiforport(sc, ifp->if_dunit);
684
685	if (mii == NULL)
686		return (ENXIO);
687	mii_mediachg(mii);
688	return (0);
689}
690
691static void
692arswitch_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
693{
694	struct arswitch_softc *sc = ifp->if_softc;
695	struct mii_data *mii = arswitch_miiforport(sc, ifp->if_dunit);
696
697	DPRINTF(sc->sc_dev, "%s\n", __func__);
698
699	if (mii == NULL)
700		return;
701	mii_pollstat(mii);
702	ifmr->ifm_active = mii->mii_media_active;
703	ifmr->ifm_status = mii->mii_media_status;
704}
705
706static int
707arswitch_getconf(device_t dev, etherswitch_conf_t *conf)
708{
709	struct arswitch_softc *sc;
710
711	sc = device_get_softc(dev);
712
713	/* Return the VLAN mode. */
714	conf->cmd = ETHERSWITCH_CONF_VLAN_MODE;
715	conf->vlan_mode = sc->vlan_mode;
716
717	return (0);
718}
719
720static int
721arswitch_setconf(device_t dev, etherswitch_conf_t *conf)
722{
723	struct arswitch_softc *sc;
724	int err;
725
726	sc = device_get_softc(dev);
727
728	/* Set the VLAN mode. */
729	if (conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) {
730		err = arswitch_set_vlan_mode(sc, conf->vlan_mode);
731		if (err != 0)
732			return (err);
733	}
734
735	return (0);
736}
737
738static int
739arswitch_getvgroup(device_t dev, etherswitch_vlangroup_t *e)
740{
741	struct arswitch_softc *sc = device_get_softc(dev);
742
743	return (sc->hal.arswitch_vlan_getvgroup(sc, e));
744}
745
746static int
747arswitch_setvgroup(device_t dev, etherswitch_vlangroup_t *e)
748{
749	struct arswitch_softc *sc = device_get_softc(dev);
750
751	return (sc->hal.arswitch_vlan_setvgroup(sc, e));
752}
753
754static device_method_t arswitch_methods[] = {
755	/* Device interface */
756	DEVMETHOD(device_probe,		arswitch_probe),
757	DEVMETHOD(device_attach,	arswitch_attach),
758	DEVMETHOD(device_detach,	arswitch_detach),
759
760	/* bus interface */
761	DEVMETHOD(bus_add_child,	device_add_child_ordered),
762
763	/* MII interface */
764	DEVMETHOD(miibus_readreg,	arswitch_readphy),
765	DEVMETHOD(miibus_writereg,	arswitch_writephy),
766	DEVMETHOD(miibus_statchg,	arswitch_statchg),
767
768	/* MDIO interface */
769	DEVMETHOD(mdio_readreg,		arswitch_readphy),
770	DEVMETHOD(mdio_writereg,	arswitch_writephy),
771
772	/* etherswitch interface */
773	DEVMETHOD(etherswitch_lock,	arswitch_lock),
774	DEVMETHOD(etherswitch_unlock,	arswitch_unlock),
775	DEVMETHOD(etherswitch_getinfo,	arswitch_getinfo),
776	DEVMETHOD(etherswitch_readreg,	arswitch_readreg),
777	DEVMETHOD(etherswitch_writereg,	arswitch_writereg),
778	DEVMETHOD(etherswitch_readphyreg,	arswitch_readphy),
779	DEVMETHOD(etherswitch_writephyreg,	arswitch_writephy),
780	DEVMETHOD(etherswitch_getport,	arswitch_getport),
781	DEVMETHOD(etherswitch_setport,	arswitch_setport),
782	DEVMETHOD(etherswitch_getvgroup,	arswitch_getvgroup),
783	DEVMETHOD(etherswitch_setvgroup,	arswitch_setvgroup),
784	DEVMETHOD(etherswitch_getconf,	arswitch_getconf),
785	DEVMETHOD(etherswitch_setconf,	arswitch_setconf),
786
787	DEVMETHOD_END
788};
789
790DEFINE_CLASS_0(arswitch, arswitch_driver, arswitch_methods,
791    sizeof(struct arswitch_softc));
792static devclass_t arswitch_devclass;
793
794DRIVER_MODULE(arswitch, mdio, arswitch_driver, arswitch_devclass, 0, 0);
795DRIVER_MODULE(miibus, arswitch, miibus_driver, miibus_devclass, 0, 0);
796DRIVER_MODULE(mdio, arswitch, mdio_driver, mdio_devclass, 0, 0);
797DRIVER_MODULE(etherswitch, arswitch, etherswitch_driver, etherswitch_devclass, 0, 0);
798MODULE_VERSION(arswitch, 1);
799MODULE_DEPEND(arswitch, miibus, 1, 1, 1); /* XXX which versions? */
800MODULE_DEPEND(arswitch, etherswitch, 1, 1, 1); /* XXX which versions? */
801