1249651Sadrian/*-
2249651Sadrian * Copyright (c) 2013 Luiz Otavio O Souza.
3249651Sadrian * Copyright (c) 2011-2012 Stefan Bethke.
4249651Sadrian * Copyright (c) 2012 Adrian Chadd.
5249651Sadrian * All rights reserved.
6249651Sadrian *
7249651Sadrian * Redistribution and use in source and binary forms, with or without
8249651Sadrian * modification, are permitted provided that the following conditions
9249651Sadrian * are met:
10249651Sadrian * 1. Redistributions of source code must retain the above copyright
11249651Sadrian *    notice, this list of conditions and the following disclaimer.
12249651Sadrian * 2. Redistributions in binary form must reproduce the above copyright
13249651Sadrian *    notice, this list of conditions and the following disclaimer in the
14249651Sadrian *    documentation and/or other materials provided with the distribution.
15249651Sadrian *
16249651Sadrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17249651Sadrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18249651Sadrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19249651Sadrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20249651Sadrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21249651Sadrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22249651Sadrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23249651Sadrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24249651Sadrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25249651Sadrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26249651Sadrian * SUCH DAMAGE.
27249651Sadrian *
28249651Sadrian * $FreeBSD$
29249651Sadrian */
30249651Sadrian
31249651Sadrian#include <sys/param.h>
32249651Sadrian#include <sys/bus.h>
33249651Sadrian#include <sys/errno.h>
34249651Sadrian#include <sys/kernel.h>
35249651Sadrian#include <sys/module.h>
36249651Sadrian#include <sys/socket.h>
37249651Sadrian#include <sys/sockio.h>
38249651Sadrian#include <sys/sysctl.h>
39249651Sadrian#include <sys/systm.h>
40249651Sadrian
41249651Sadrian#include <net/if.h>
42249651Sadrian#include <net/if_arp.h>
43249651Sadrian#include <net/ethernet.h>
44249651Sadrian#include <net/if_dl.h>
45249651Sadrian#include <net/if_media.h>
46249651Sadrian#include <net/if_types.h>
47249651Sadrian
48249651Sadrian#include <machine/bus.h>
49249651Sadrian#include <dev/mii/mii.h>
50249651Sadrian#include <dev/mii/miivar.h>
51249651Sadrian#include <dev/etherswitch/mdio.h>
52249651Sadrian
53249651Sadrian#include <dev/etherswitch/etherswitch.h>
54249651Sadrian
55249651Sadrian#include "mdio_if.h"
56249651Sadrian#include "miibus_if.h"
57249651Sadrian#include "etherswitch_if.h"
58249651Sadrian
59249651SadrianMALLOC_DECLARE(M_UKSWITCH);
60249651SadrianMALLOC_DEFINE(M_UKSWITCH, "ukswitch", "ukswitch data structures");
61249651Sadrian
62249651Sadrianstruct ukswitch_softc {
63249651Sadrian	struct mtx	sc_mtx;		/* serialize access to softc */
64249651Sadrian	device_t	sc_dev;
65249651Sadrian	int		media;		/* cpu port media */
66249651Sadrian	int		cpuport;	/* which PHY is connected to the CPU */
67249651Sadrian	int		phymask;	/* PHYs we manage */
68249651Sadrian	int		numports;	/* number of ports */
69249651Sadrian	int		ifpport[MII_NPHY];
70249651Sadrian	int		*portphy;
71249651Sadrian	char		**ifname;
72249651Sadrian	device_t	**miibus;
73249651Sadrian	struct ifnet	**ifp;
74249651Sadrian	struct callout	callout_tick;
75249651Sadrian	etherswitch_info_t	info;
76249651Sadrian};
77249651Sadrian
78249651Sadrian#define UKSWITCH_LOCK(_sc)			\
79249651Sadrian	    mtx_lock(&(_sc)->sc_mtx)
80249651Sadrian#define UKSWITCH_UNLOCK(_sc)			\
81249651Sadrian	    mtx_unlock(&(_sc)->sc_mtx)
82249651Sadrian#define UKSWITCH_LOCK_ASSERT(_sc, _what)	\
83249651Sadrian	    mtx_assert(&(_sc)->sc_mtx, (_what))
84249651Sadrian#define UKSWITCH_TRYLOCK(_sc)			\
85249651Sadrian	    mtx_trylock(&(_sc)->sc_mtx)
86249651Sadrian
87249651Sadrian#if defined(DEBUG)
88249651Sadrian#define	DPRINTF(dev, args...) device_printf(dev, args)
89249651Sadrian#else
90249651Sadrian#define	DPRINTF(dev, args...)
91249651Sadrian#endif
92249651Sadrian
93249651Sadrianstatic inline int ukswitch_portforphy(struct ukswitch_softc *, int);
94249651Sadrianstatic void ukswitch_tick(void *);
95249651Sadrianstatic int ukswitch_ifmedia_upd(struct ifnet *);
96249651Sadrianstatic void ukswitch_ifmedia_sts(struct ifnet *, struct ifmediareq *);
97249651Sadrian
98249651Sadrianstatic int
99249651Sadrianukswitch_probe(device_t dev)
100249651Sadrian{
101249651Sadrian	struct ukswitch_softc *sc;
102249651Sadrian
103249651Sadrian	sc = device_get_softc(dev);
104249651Sadrian	bzero(sc, sizeof(*sc));
105249651Sadrian
106249651Sadrian	device_set_desc_copy(dev, "Generic MDIO switch driver");
107249651Sadrian	return (BUS_PROBE_DEFAULT);
108249651Sadrian}
109249651Sadrian
110249651Sadrianstatic int
111249651Sadrianukswitch_attach_phys(struct ukswitch_softc *sc)
112249651Sadrian{
113249651Sadrian	int phy, port = 0, err = 0;
114249651Sadrian	char name[IFNAMSIZ];
115249651Sadrian
116249651Sadrian	/* PHYs need an interface, so we generate a dummy one */
117249651Sadrian	snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->sc_dev));
118249651Sadrian	for (phy = 0; phy < MII_NPHY; phy++) {
119249651Sadrian		if (((1 << phy) & sc->phymask) == 0)
120249651Sadrian			continue;
121249651Sadrian		sc->ifpport[phy] = port;
122249651Sadrian		sc->portphy[port] = phy;
123249651Sadrian		sc->ifp[port] = if_alloc(IFT_ETHER);
124249651Sadrian		sc->ifp[port]->if_softc = sc;
125249651Sadrian		sc->ifp[port]->if_flags |= IFF_UP | IFF_BROADCAST |
126249651Sadrian		    IFF_DRV_RUNNING | IFF_SIMPLEX;
127249651Sadrian		sc->ifname[port] = malloc(strlen(name)+1, M_UKSWITCH, M_WAITOK);
128249651Sadrian		bcopy(name, sc->ifname[port], strlen(name)+1);
129249651Sadrian		if_initname(sc->ifp[port], sc->ifname[port], port);
130249651Sadrian		sc->miibus[port] = malloc(sizeof(device_t), M_UKSWITCH,
131249651Sadrian		    M_WAITOK | M_ZERO);
132249651Sadrian		err = mii_attach(sc->sc_dev, sc->miibus[port], sc->ifp[port],
133249651Sadrian		    ukswitch_ifmedia_upd, ukswitch_ifmedia_sts, \
134249651Sadrian		    BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0);
135249651Sadrian		DPRINTF(sc->sc_dev, "%s attached to pseudo interface %s\n",
136249651Sadrian		    device_get_nameunit(*sc->miibus[port]),
137249651Sadrian		    sc->ifp[port]->if_xname);
138249651Sadrian		if (err != 0) {
139249651Sadrian			device_printf(sc->sc_dev,
140249651Sadrian			    "attaching PHY %d failed\n",
141249651Sadrian			    phy);
142249651Sadrian			break;
143249651Sadrian		}
144249651Sadrian		sc->info.es_nports = port + 1;
145249651Sadrian		if (++port >= sc->numports)
146249651Sadrian			break;
147249651Sadrian	}
148249651Sadrian	return (err);
149249651Sadrian}
150249651Sadrian
151249651Sadrianstatic int
152249651Sadrianukswitch_attach(device_t dev)
153249651Sadrian{
154249651Sadrian	struct ukswitch_softc *sc;
155249651Sadrian	int err = 0;
156249651Sadrian
157249651Sadrian	sc = device_get_softc(dev);
158249651Sadrian
159249651Sadrian	sc->sc_dev = dev;
160249651Sadrian	mtx_init(&sc->sc_mtx, "ukswitch", NULL, MTX_DEF);
161249651Sadrian	strlcpy(sc->info.es_name, device_get_desc(dev),
162249651Sadrian	    sizeof(sc->info.es_name));
163249651Sadrian
164249651Sadrian	/* XXX Defaults */
165249651Sadrian	sc->numports = 6;
166249651Sadrian	sc->phymask = 0x0f;
167250384Sadrian	sc->cpuport = -1;
168249651Sadrian	sc->media = 100;
169249651Sadrian
170249651Sadrian	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
171249651Sadrian	    "numports", &sc->numports);
172249651Sadrian	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
173249651Sadrian	    "phymask", &sc->phymask);
174249651Sadrian	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
175249651Sadrian	    "cpuport", &sc->cpuport);
176249651Sadrian	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
177249651Sadrian	    "media", &sc->media);
178249651Sadrian
179249651Sadrian	/* Support only fast and giga ethernet. */
180249651Sadrian	if (sc->media != 100 && sc->media != 1000)
181249651Sadrian		sc->media = 100;
182249651Sadrian
183250384Sadrian	if (sc->cpuport != -1)
184250384Sadrian		/* Always attach the cpu port. */
185250384Sadrian		sc->phymask |= (1 << sc->cpuport);
186249651Sadrian
187249651Sadrian	/* We do not support any vlan groups. */
188249651Sadrian	sc->info.es_nvlangroups = 0;
189249651Sadrian
190249651Sadrian	sc->ifp = malloc(sizeof(struct ifnet *) * sc->numports, M_UKSWITCH,
191249651Sadrian	    M_WAITOK | M_ZERO);
192249651Sadrian	sc->ifname = malloc(sizeof(char *) * sc->numports, M_UKSWITCH,
193249651Sadrian	    M_WAITOK | M_ZERO);
194249651Sadrian	sc->miibus = malloc(sizeof(device_t *) * sc->numports, M_UKSWITCH,
195249651Sadrian	    M_WAITOK | M_ZERO);
196249651Sadrian	sc->portphy = malloc(sizeof(int) * sc->numports, M_UKSWITCH,
197249651Sadrian	    M_WAITOK | M_ZERO);
198249651Sadrian
199249651Sadrian	/*
200249651Sadrian	 * Attach the PHYs and complete the bus enumeration.
201249651Sadrian	 */
202249651Sadrian	err = ukswitch_attach_phys(sc);
203249651Sadrian	if (err != 0)
204249651Sadrian		return (err);
205249651Sadrian
206249651Sadrian	bus_generic_probe(dev);
207249651Sadrian	bus_enumerate_hinted_children(dev);
208249651Sadrian	err = bus_generic_attach(dev);
209249651Sadrian	if (err != 0)
210249651Sadrian		return (err);
211249651Sadrian
212249651Sadrian	callout_init(&sc->callout_tick, 0);
213249651Sadrian
214249651Sadrian	ukswitch_tick(sc);
215249651Sadrian
216249651Sadrian	return (err);
217249651Sadrian}
218249651Sadrian
219249651Sadrianstatic int
220249651Sadrianukswitch_detach(device_t dev)
221249651Sadrian{
222249651Sadrian	struct ukswitch_softc *sc = device_get_softc(dev);
223249651Sadrian	int i, port;
224249651Sadrian
225249651Sadrian	callout_drain(&sc->callout_tick);
226249651Sadrian
227249651Sadrian	for (i=0; i < MII_NPHY; i++) {
228249651Sadrian		if (((1 << i) & sc->phymask) == 0)
229249651Sadrian			continue;
230249651Sadrian		port = ukswitch_portforphy(sc, i);
231249651Sadrian		if (sc->miibus[port] != NULL)
232249651Sadrian			device_delete_child(dev, (*sc->miibus[port]));
233249651Sadrian		if (sc->ifp[port] != NULL)
234249651Sadrian			if_free(sc->ifp[port]);
235249651Sadrian		free(sc->ifname[port], M_UKSWITCH);
236249651Sadrian		free(sc->miibus[port], M_UKSWITCH);
237249651Sadrian	}
238249651Sadrian
239249651Sadrian	free(sc->portphy, M_UKSWITCH);
240249651Sadrian	free(sc->miibus, M_UKSWITCH);
241249651Sadrian	free(sc->ifname, M_UKSWITCH);
242249651Sadrian	free(sc->ifp, M_UKSWITCH);
243249651Sadrian
244249651Sadrian	bus_generic_detach(dev);
245249651Sadrian	mtx_destroy(&sc->sc_mtx);
246249651Sadrian
247249651Sadrian	return (0);
248249651Sadrian}
249249651Sadrian
250249651Sadrian/*
251249651Sadrian * Convert PHY number to port number.
252249651Sadrian */
253249651Sadrianstatic inline int
254249651Sadrianukswitch_portforphy(struct ukswitch_softc *sc, int phy)
255249651Sadrian{
256249651Sadrian
257249651Sadrian	return (sc->ifpport[phy]);
258249651Sadrian}
259249651Sadrian
260249651Sadrianstatic inline struct mii_data *
261249651Sadrianukswitch_miiforport(struct ukswitch_softc *sc, int port)
262249651Sadrian{
263249651Sadrian
264249651Sadrian	if (port < 0 || port > sc->numports)
265249651Sadrian		return (NULL);
266249651Sadrian	return (device_get_softc(*sc->miibus[port]));
267249651Sadrian}
268249651Sadrian
269249651Sadrianstatic inline struct ifnet *
270249651Sadrianukswitch_ifpforport(struct ukswitch_softc *sc, int port)
271249651Sadrian{
272249651Sadrian
273249651Sadrian	if (port < 0 || port > sc->numports)
274249651Sadrian		return (NULL);
275249651Sadrian	return (sc->ifp[port]);
276249651Sadrian}
277249651Sadrian
278249651Sadrian/*
279249651Sadrian * Poll the status for all PHYs.
280249651Sadrian */
281249651Sadrianstatic void
282249651Sadrianukswitch_miipollstat(struct ukswitch_softc *sc)
283249651Sadrian{
284249651Sadrian	int i, port;
285249651Sadrian	struct mii_data *mii;
286249651Sadrian	struct mii_softc *miisc;
287249651Sadrian
288249651Sadrian	UKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
289249651Sadrian
290249651Sadrian	for (i = 0; i < MII_NPHY; i++) {
291249651Sadrian		if (((1 << i) & sc->phymask) == 0)
292249651Sadrian			continue;
293249651Sadrian		port = ukswitch_portforphy(sc, i);
294249651Sadrian		if ((*sc->miibus[port]) == NULL)
295249651Sadrian			continue;
296249651Sadrian		mii = device_get_softc(*sc->miibus[port]);
297249651Sadrian		LIST_FOREACH(miisc, &mii->mii_phys, mii_list) {
298249651Sadrian			if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) !=
299249651Sadrian			    miisc->mii_inst)
300249651Sadrian				continue;
301249651Sadrian			ukphy_status(miisc);
302249651Sadrian			mii_phy_update(miisc, MII_POLLSTAT);
303249651Sadrian		}
304249651Sadrian	}
305249651Sadrian}
306249651Sadrian
307249651Sadrianstatic void
308249651Sadrianukswitch_tick(void *arg)
309249651Sadrian{
310249651Sadrian	struct ukswitch_softc *sc = arg;
311249651Sadrian
312249651Sadrian	ukswitch_miipollstat(sc);
313249651Sadrian	callout_reset(&sc->callout_tick, hz, ukswitch_tick, sc);
314249651Sadrian}
315249651Sadrian
316249651Sadrianstatic void
317249651Sadrianukswitch_lock(device_t dev)
318249651Sadrian{
319249651Sadrian	struct ukswitch_softc *sc = device_get_softc(dev);
320249651Sadrian
321249651Sadrian	UKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
322249651Sadrian	UKSWITCH_LOCK(sc);
323249651Sadrian}
324249651Sadrian
325249651Sadrianstatic void
326249651Sadrianukswitch_unlock(device_t dev)
327249651Sadrian{
328249651Sadrian	struct ukswitch_softc *sc = device_get_softc(dev);
329249651Sadrian
330249651Sadrian	UKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
331249651Sadrian	UKSWITCH_UNLOCK(sc);
332249651Sadrian}
333249651Sadrian
334249651Sadrianstatic etherswitch_info_t *
335249651Sadrianukswitch_getinfo(device_t dev)
336249651Sadrian{
337249651Sadrian	struct ukswitch_softc *sc = device_get_softc(dev);
338249651Sadrian
339249651Sadrian	return (&sc->info);
340249651Sadrian}
341249651Sadrian
342249651Sadrianstatic int
343249651Sadrianukswitch_getport(device_t dev, etherswitch_port_t *p)
344249651Sadrian{
345249651Sadrian	struct ukswitch_softc *sc = device_get_softc(dev);
346249651Sadrian	struct mii_data *mii;
347249651Sadrian	struct ifmediareq *ifmr = &p->es_ifmr;
348250384Sadrian	int err, phy;
349249651Sadrian
350249651Sadrian	if (p->es_port < 0 || p->es_port >= sc->numports)
351249651Sadrian		return (ENXIO);
352250384Sadrian	p->es_pvid = 0;
353249651Sadrian
354250384Sadrian	phy = sc->portphy[p->es_port];
355249651Sadrian	mii = ukswitch_miiforport(sc, p->es_port);
356250384Sadrian	if (sc->cpuport != -1 && phy == sc->cpuport) {
357249651Sadrian		/* fill in fixed values for CPU port */
358250384Sadrian		p->es_flags |= ETHERSWITCH_PORT_CPU;
359249651Sadrian		ifmr->ifm_count = 0;
360249651Sadrian		if (sc->media == 100)
361249651Sadrian			ifmr->ifm_current = ifmr->ifm_active =
362249651Sadrian			    IFM_ETHER | IFM_100_TX | IFM_FDX;
363249651Sadrian		else
364249651Sadrian			ifmr->ifm_current = ifmr->ifm_active =
365249651Sadrian			    IFM_ETHER | IFM_1000_T | IFM_FDX;
366249651Sadrian		ifmr->ifm_mask = 0;
367249651Sadrian		ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
368249651Sadrian	} else if (mii != NULL) {
369249651Sadrian		err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr,
370249651Sadrian		    &mii->mii_media, SIOCGIFMEDIA);
371249651Sadrian		if (err)
372249651Sadrian			return (err);
373249651Sadrian	} else {
374249651Sadrian		return (ENXIO);
375249651Sadrian	}
376249651Sadrian	return (0);
377249651Sadrian}
378249651Sadrian
379249651Sadrianstatic int
380249651Sadrianukswitch_setport(device_t dev, etherswitch_port_t *p)
381249651Sadrian{
382249651Sadrian	struct ukswitch_softc *sc = device_get_softc(dev);
383249651Sadrian	struct ifmedia *ifm;
384249651Sadrian	struct mii_data *mii;
385249651Sadrian	struct ifnet *ifp;
386249651Sadrian	int err;
387249651Sadrian
388249651Sadrian	if (p->es_port < 0 || p->es_port >= sc->numports)
389249651Sadrian		return (ENXIO);
390249651Sadrian
391249651Sadrian	if (sc->portphy[p->es_port] == sc->cpuport)
392249651Sadrian		return (ENXIO);
393249651Sadrian
394249651Sadrian	mii = ukswitch_miiforport(sc, p->es_port);
395249651Sadrian	if (mii == NULL)
396249651Sadrian		return (ENXIO);
397249651Sadrian
398249651Sadrian	ifp = ukswitch_ifpforport(sc, p->es_port);
399249651Sadrian
400249651Sadrian	ifm = &mii->mii_media;
401249651Sadrian	err = ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA);
402249651Sadrian	return (err);
403249651Sadrian}
404249651Sadrian
405249651Sadrianstatic int
406249651Sadrianukswitch_getvgroup(device_t dev, etherswitch_vlangroup_t *vg)
407249651Sadrian{
408249651Sadrian
409249651Sadrian	/* Not supported. */
410249651Sadrian	vg->es_vid = 0;
411249651Sadrian	vg->es_member_ports = 0;
412249651Sadrian	vg->es_untagged_ports = 0;
413249651Sadrian	vg->es_fid = 0;
414249651Sadrian	return (0);
415249651Sadrian}
416249651Sadrian
417249651Sadrianstatic int
418249651Sadrianukswitch_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
419249651Sadrian{
420249651Sadrian
421249651Sadrian	/* Not supported. */
422249651Sadrian	return (0);
423249651Sadrian}
424249651Sadrian
425249651Sadrianstatic void
426249651Sadrianukswitch_statchg(device_t dev)
427249651Sadrian{
428249651Sadrian
429249651Sadrian	DPRINTF(dev, "%s\n", __func__);
430249651Sadrian}
431249651Sadrian
432249651Sadrianstatic int
433249651Sadrianukswitch_ifmedia_upd(struct ifnet *ifp)
434249651Sadrian{
435249651Sadrian	struct ukswitch_softc *sc = ifp->if_softc;
436249651Sadrian	struct mii_data *mii = ukswitch_miiforport(sc, ifp->if_dunit);
437249651Sadrian
438249651Sadrian	DPRINTF(sc->sc_dev, "%s\n", __func__);
439249651Sadrian	if (mii == NULL)
440249651Sadrian		return (ENXIO);
441249651Sadrian	mii_mediachg(mii);
442249651Sadrian	return (0);
443249651Sadrian}
444249651Sadrian
445249651Sadrianstatic void
446249651Sadrianukswitch_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
447249651Sadrian{
448249651Sadrian	struct ukswitch_softc *sc = ifp->if_softc;
449249651Sadrian	struct mii_data *mii = ukswitch_miiforport(sc, ifp->if_dunit);
450249651Sadrian
451249651Sadrian	DPRINTF(sc->sc_dev, "%s\n", __func__);
452249651Sadrian
453249651Sadrian	if (mii == NULL)
454249651Sadrian		return;
455249651Sadrian	mii_pollstat(mii);
456249651Sadrian	ifmr->ifm_active = mii->mii_media_active;
457249651Sadrian	ifmr->ifm_status = mii->mii_media_status;
458249651Sadrian}
459249651Sadrian
460249651Sadrianstatic int
461249651Sadrianukswitch_readphy(device_t dev, int phy, int reg)
462249651Sadrian{
463249651Sadrian	struct ukswitch_softc *sc;
464249651Sadrian	int data;
465249651Sadrian
466249651Sadrian	sc = device_get_softc(dev);
467249651Sadrian	UKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
468249651Sadrian
469249651Sadrian	if (phy < 0 || phy >= 32)
470249651Sadrian		return (ENXIO);
471249651Sadrian	if (reg < 0 || reg >= 32)
472249651Sadrian		return (ENXIO);
473249651Sadrian
474249651Sadrian	UKSWITCH_LOCK(sc);
475249651Sadrian	data = MDIO_READREG(device_get_parent(dev), phy, reg);
476249651Sadrian	UKSWITCH_UNLOCK(sc);
477249651Sadrian
478249651Sadrian	return (data);
479249651Sadrian}
480249651Sadrian
481249651Sadrianstatic int
482249651Sadrianukswitch_writephy(device_t dev, int phy, int reg, int data)
483249651Sadrian{
484249651Sadrian	struct ukswitch_softc *sc;
485249651Sadrian	int err;
486249651Sadrian
487249651Sadrian	sc = device_get_softc(dev);
488249651Sadrian	UKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
489249651Sadrian
490249651Sadrian	if (phy < 0 || phy >= 32)
491249651Sadrian		return (ENXIO);
492249651Sadrian	if (reg < 0 || reg >= 32)
493249651Sadrian		return (ENXIO);
494249651Sadrian
495249651Sadrian	UKSWITCH_LOCK(sc);
496249651Sadrian	err = MDIO_WRITEREG(device_get_parent(dev), phy, reg, data);
497249651Sadrian	UKSWITCH_UNLOCK(sc);
498249651Sadrian
499249651Sadrian	return (err);
500249651Sadrian}
501249651Sadrian
502249651Sadrianstatic int
503249651Sadrianukswitch_readreg(device_t dev, int addr)
504249651Sadrian{
505249651Sadrian	struct ukswitch_softc *sc;
506249651Sadrian
507249651Sadrian	sc = device_get_softc(dev);
508249651Sadrian	UKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
509249651Sadrian
510249651Sadrian	/* Not supported. */
511249651Sadrian	return (0);
512249651Sadrian}
513249651Sadrian
514249651Sadrianstatic int
515249651Sadrianukswitch_writereg(device_t dev, int addr, int value)
516249651Sadrian{
517249651Sadrian	struct ukswitch_softc *sc;
518249651Sadrian
519249651Sadrian	sc = device_get_softc(dev);
520249651Sadrian	UKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
521249651Sadrian
522249651Sadrian	/* Not supported. */
523249651Sadrian	return (0);
524249651Sadrian}
525249651Sadrian
526249651Sadrianstatic device_method_t ukswitch_methods[] = {
527249651Sadrian	/* Device interface */
528249651Sadrian	DEVMETHOD(device_probe,		ukswitch_probe),
529249651Sadrian	DEVMETHOD(device_attach,	ukswitch_attach),
530249651Sadrian	DEVMETHOD(device_detach,	ukswitch_detach),
531249651Sadrian
532249651Sadrian	/* bus interface */
533249651Sadrian	DEVMETHOD(bus_add_child,	device_add_child_ordered),
534249651Sadrian
535249651Sadrian	/* MII interface */
536249651Sadrian	DEVMETHOD(miibus_readreg,	ukswitch_readphy),
537249651Sadrian	DEVMETHOD(miibus_writereg,	ukswitch_writephy),
538249651Sadrian	DEVMETHOD(miibus_statchg,	ukswitch_statchg),
539249651Sadrian
540249651Sadrian	/* MDIO interface */
541249651Sadrian	DEVMETHOD(mdio_readreg,		ukswitch_readphy),
542249651Sadrian	DEVMETHOD(mdio_writereg,	ukswitch_writephy),
543249651Sadrian
544249651Sadrian	/* etherswitch interface */
545249651Sadrian	DEVMETHOD(etherswitch_lock,	ukswitch_lock),
546249651Sadrian	DEVMETHOD(etherswitch_unlock,	ukswitch_unlock),
547249651Sadrian	DEVMETHOD(etherswitch_getinfo,	ukswitch_getinfo),
548249651Sadrian	DEVMETHOD(etherswitch_readreg,	ukswitch_readreg),
549249651Sadrian	DEVMETHOD(etherswitch_writereg,	ukswitch_writereg),
550249651Sadrian	DEVMETHOD(etherswitch_readphyreg,	ukswitch_readphy),
551249651Sadrian	DEVMETHOD(etherswitch_writephyreg,	ukswitch_writephy),
552249651Sadrian	DEVMETHOD(etherswitch_getport,	ukswitch_getport),
553249651Sadrian	DEVMETHOD(etherswitch_setport,	ukswitch_setport),
554249651Sadrian	DEVMETHOD(etherswitch_getvgroup,	ukswitch_getvgroup),
555249651Sadrian	DEVMETHOD(etherswitch_setvgroup,	ukswitch_setvgroup),
556249651Sadrian
557249651Sadrian	DEVMETHOD_END
558249651Sadrian};
559249651Sadrian
560249651SadrianDEFINE_CLASS_0(ukswitch, ukswitch_driver, ukswitch_methods,
561249651Sadrian    sizeof(struct ukswitch_softc));
562249651Sadrianstatic devclass_t ukswitch_devclass;
563249651Sadrian
564249651SadrianDRIVER_MODULE(ukswitch, mdio, ukswitch_driver, ukswitch_devclass, 0, 0);
565249651SadrianDRIVER_MODULE(miibus, ukswitch, miibus_driver, miibus_devclass, 0, 0);
566249651SadrianDRIVER_MODULE(mdio, ukswitch, mdio_driver, mdio_devclass, 0, 0);
567249651SadrianDRIVER_MODULE(etherswitch, ukswitch, etherswitch_driver, etherswitch_devclass, 0, 0);
568249651SadrianMODULE_VERSION(ukswitch, 1);
569249651SadrianMODULE_DEPEND(ukswitch, miibus, 1, 1, 1); /* XXX which versions? */
570249651SadrianMODULE_DEPEND(ukswitch, etherswitch, 1, 1, 1); /* XXX which versions? */
571