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