1/*-
2 * Copyright (c) 2013 Luiz Otavio O Souza.
3 * Copyright (c) 2011-2012 Stefan Bethke.
4 * Copyright (c) 2012 Adrian Chadd.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * $FreeBSD$
29 */
30
31#include <sys/param.h>
32#include <sys/bus.h>
33#include <sys/errno.h>
34#include <sys/kernel.h>
35#include <sys/lock.h>
36#include <sys/malloc.h>
37#include <sys/module.h>
38#include <sys/mutex.h>
39#include <sys/socket.h>
40#include <sys/sockio.h>
41#include <sys/sysctl.h>
42#include <sys/systm.h>
43
44#include <net/if.h>
45#include <net/if_var.h>
46#include <net/ethernet.h>
47#include <net/if_media.h>
48#include <net/if_types.h>
49
50#include <machine/bus.h>
51#include <dev/mii/mii.h>
52#include <dev/mii/miivar.h>
53#include <dev/mdio/mdio.h>
54
55#include <dev/etherswitch/etherswitch.h>
56
57#include "mdio_if.h"
58#include "miibus_if.h"
59#include "etherswitch_if.h"
60
61MALLOC_DECLARE(M_UKSWITCH);
62MALLOC_DEFINE(M_UKSWITCH, "ukswitch", "ukswitch data structures");
63
64struct ukswitch_softc {
65	struct mtx	sc_mtx;		/* serialize access to softc */
66	device_t	sc_dev;
67	int		media;		/* cpu port media */
68	int		cpuport;	/* which PHY is connected to the CPU */
69	int		phymask;	/* PHYs we manage */
70	int		numports;	/* number of ports */
71	int		ifpport[MII_NPHY];
72	int		*portphy;
73	char		**ifname;
74	device_t	**miibus;
75	struct ifnet	**ifp;
76	struct callout	callout_tick;
77	etherswitch_info_t	info;
78};
79
80#define UKSWITCH_LOCK(_sc)			\
81	    mtx_lock(&(_sc)->sc_mtx)
82#define UKSWITCH_UNLOCK(_sc)			\
83	    mtx_unlock(&(_sc)->sc_mtx)
84#define UKSWITCH_LOCK_ASSERT(_sc, _what)	\
85	    mtx_assert(&(_sc)->sc_mtx, (_what))
86#define UKSWITCH_TRYLOCK(_sc)			\
87	    mtx_trylock(&(_sc)->sc_mtx)
88
89#if defined(DEBUG)
90#define	DPRINTF(dev, args...) device_printf(dev, args)
91#else
92#define	DPRINTF(dev, args...)
93#endif
94
95static inline int ukswitch_portforphy(struct ukswitch_softc *, int);
96static void ukswitch_tick(void *);
97static int ukswitch_ifmedia_upd(struct ifnet *);
98static void ukswitch_ifmedia_sts(struct ifnet *, struct ifmediareq *);
99
100static int
101ukswitch_probe(device_t dev)
102{
103	struct ukswitch_softc *sc;
104
105	sc = device_get_softc(dev);
106	bzero(sc, sizeof(*sc));
107
108	device_set_desc_copy(dev, "Generic MDIO switch driver");
109	return (BUS_PROBE_DEFAULT);
110}
111
112static int
113ukswitch_attach_phys(struct ukswitch_softc *sc)
114{
115	int phy, port = 0, err = 0;
116	char name[IFNAMSIZ];
117
118	/* PHYs need an interface, so we generate a dummy one */
119	snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->sc_dev));
120	for (phy = 0; phy < MII_NPHY; phy++) {
121		if (((1 << phy) & sc->phymask) == 0)
122			continue;
123		sc->ifpport[phy] = port;
124		sc->portphy[port] = phy;
125		sc->ifp[port] = if_alloc(IFT_ETHER);
126		sc->ifp[port]->if_softc = sc;
127		sc->ifp[port]->if_flags |= IFF_UP | IFF_BROADCAST |
128		    IFF_DRV_RUNNING | IFF_SIMPLEX;
129		sc->ifname[port] = malloc(strlen(name)+1, M_UKSWITCH, M_WAITOK);
130		bcopy(name, sc->ifname[port], strlen(name)+1);
131		if_initname(sc->ifp[port], sc->ifname[port], port);
132		sc->miibus[port] = malloc(sizeof(device_t), M_UKSWITCH,
133		    M_WAITOK | M_ZERO);
134		err = mii_attach(sc->sc_dev, sc->miibus[port], sc->ifp[port],
135		    ukswitch_ifmedia_upd, ukswitch_ifmedia_sts, \
136		    BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0);
137		DPRINTF(sc->sc_dev, "%s attached to pseudo interface %s\n",
138		    device_get_nameunit(*sc->miibus[port]),
139		    sc->ifp[port]->if_xname);
140		if (err != 0) {
141			device_printf(sc->sc_dev,
142			    "attaching PHY %d failed\n",
143			    phy);
144			break;
145		}
146		sc->info.es_nports = port + 1;
147		if (++port >= sc->numports)
148			break;
149	}
150	return (err);
151}
152
153static int
154ukswitch_attach(device_t dev)
155{
156	struct ukswitch_softc *sc;
157	int err = 0;
158
159	sc = device_get_softc(dev);
160
161	sc->sc_dev = dev;
162	mtx_init(&sc->sc_mtx, "ukswitch", NULL, MTX_DEF);
163	strlcpy(sc->info.es_name, device_get_desc(dev),
164	    sizeof(sc->info.es_name));
165
166	/* XXX Defaults */
167	sc->numports = 6;
168	sc->phymask = 0x0f;
169	sc->cpuport = -1;
170	sc->media = 100;
171
172	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
173	    "numports", &sc->numports);
174	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
175	    "phymask", &sc->phymask);
176	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
177	    "cpuport", &sc->cpuport);
178	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
179	    "media", &sc->media);
180
181	/* Support only fast and giga ethernet. */
182	if (sc->media != 100 && sc->media != 1000)
183		sc->media = 100;
184
185	if (sc->cpuport != -1)
186		/* Always attach the cpu port. */
187		sc->phymask |= (1 << sc->cpuport);
188
189	/* We do not support any vlan groups. */
190	sc->info.es_nvlangroups = 0;
191
192	sc->ifp = malloc(sizeof(struct ifnet *) * sc->numports, M_UKSWITCH,
193	    M_WAITOK | M_ZERO);
194	sc->ifname = malloc(sizeof(char *) * sc->numports, M_UKSWITCH,
195	    M_WAITOK | M_ZERO);
196	sc->miibus = malloc(sizeof(device_t *) * sc->numports, M_UKSWITCH,
197	    M_WAITOK | M_ZERO);
198	sc->portphy = malloc(sizeof(int) * sc->numports, M_UKSWITCH,
199	    M_WAITOK | M_ZERO);
200
201	/*
202	 * Attach the PHYs and complete the bus enumeration.
203	 */
204	err = ukswitch_attach_phys(sc);
205	if (err != 0)
206		return (err);
207
208	bus_generic_probe(dev);
209	bus_enumerate_hinted_children(dev);
210	err = bus_generic_attach(dev);
211	if (err != 0)
212		return (err);
213
214	callout_init(&sc->callout_tick, 0);
215
216	ukswitch_tick(sc);
217
218	return (err);
219}
220
221static int
222ukswitch_detach(device_t dev)
223{
224	struct ukswitch_softc *sc = device_get_softc(dev);
225	int i, port;
226
227	callout_drain(&sc->callout_tick);
228
229	for (i=0; i < MII_NPHY; i++) {
230		if (((1 << i) & sc->phymask) == 0)
231			continue;
232		port = ukswitch_portforphy(sc, i);
233		if (sc->miibus[port] != NULL)
234			device_delete_child(dev, (*sc->miibus[port]));
235		if (sc->ifp[port] != NULL)
236			if_free(sc->ifp[port]);
237		free(sc->ifname[port], M_UKSWITCH);
238		free(sc->miibus[port], M_UKSWITCH);
239	}
240
241	free(sc->portphy, M_UKSWITCH);
242	free(sc->miibus, M_UKSWITCH);
243	free(sc->ifname, M_UKSWITCH);
244	free(sc->ifp, M_UKSWITCH);
245
246	bus_generic_detach(dev);
247	mtx_destroy(&sc->sc_mtx);
248
249	return (0);
250}
251
252/*
253 * Convert PHY number to port number.
254 */
255static inline int
256ukswitch_portforphy(struct ukswitch_softc *sc, int phy)
257{
258
259	return (sc->ifpport[phy]);
260}
261
262static inline struct mii_data *
263ukswitch_miiforport(struct ukswitch_softc *sc, int port)
264{
265
266	if (port < 0 || port > sc->numports)
267		return (NULL);
268	return (device_get_softc(*sc->miibus[port]));
269}
270
271static inline struct ifnet *
272ukswitch_ifpforport(struct ukswitch_softc *sc, int port)
273{
274
275	if (port < 0 || port > sc->numports)
276		return (NULL);
277	return (sc->ifp[port]);
278}
279
280/*
281 * Poll the status for all PHYs.
282 */
283static void
284ukswitch_miipollstat(struct ukswitch_softc *sc)
285{
286	int i, port;
287	struct mii_data *mii;
288	struct mii_softc *miisc;
289
290	UKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
291
292	for (i = 0; i < MII_NPHY; i++) {
293		if (((1 << i) & sc->phymask) == 0)
294			continue;
295		port = ukswitch_portforphy(sc, i);
296		if ((*sc->miibus[port]) == NULL)
297			continue;
298		mii = device_get_softc(*sc->miibus[port]);
299		LIST_FOREACH(miisc, &mii->mii_phys, mii_list) {
300			if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) !=
301			    miisc->mii_inst)
302				continue;
303			ukphy_status(miisc);
304			mii_phy_update(miisc, MII_POLLSTAT);
305		}
306	}
307}
308
309static void
310ukswitch_tick(void *arg)
311{
312	struct ukswitch_softc *sc = arg;
313
314	ukswitch_miipollstat(sc);
315	callout_reset(&sc->callout_tick, hz, ukswitch_tick, sc);
316}
317
318static void
319ukswitch_lock(device_t dev)
320{
321	struct ukswitch_softc *sc = device_get_softc(dev);
322
323	UKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
324	UKSWITCH_LOCK(sc);
325}
326
327static void
328ukswitch_unlock(device_t dev)
329{
330	struct ukswitch_softc *sc = device_get_softc(dev);
331
332	UKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
333	UKSWITCH_UNLOCK(sc);
334}
335
336static etherswitch_info_t *
337ukswitch_getinfo(device_t dev)
338{
339	struct ukswitch_softc *sc = device_get_softc(dev);
340
341	return (&sc->info);
342}
343
344static int
345ukswitch_getport(device_t dev, etherswitch_port_t *p)
346{
347	struct ukswitch_softc *sc = device_get_softc(dev);
348	struct mii_data *mii;
349	struct ifmediareq *ifmr = &p->es_ifmr;
350	int err, phy;
351
352	if (p->es_port < 0 || p->es_port >= sc->numports)
353		return (ENXIO);
354	p->es_pvid = 0;
355
356	phy = sc->portphy[p->es_port];
357	mii = ukswitch_miiforport(sc, p->es_port);
358	if (sc->cpuport != -1 && phy == sc->cpuport) {
359		/* fill in fixed values for CPU port */
360		p->es_flags |= ETHERSWITCH_PORT_CPU;
361		ifmr->ifm_count = 0;
362		if (sc->media == 100)
363			ifmr->ifm_current = ifmr->ifm_active =
364			    IFM_ETHER | IFM_100_TX | IFM_FDX;
365		else
366			ifmr->ifm_current = ifmr->ifm_active =
367			    IFM_ETHER | IFM_1000_T | IFM_FDX;
368		ifmr->ifm_mask = 0;
369		ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
370	} else if (mii != NULL) {
371		err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr,
372		    &mii->mii_media, SIOCGIFMEDIA);
373		if (err)
374			return (err);
375	} else {
376		return (ENXIO);
377	}
378	return (0);
379}
380
381static int
382ukswitch_setport(device_t dev, etherswitch_port_t *p)
383{
384	struct ukswitch_softc *sc = device_get_softc(dev);
385	struct ifmedia *ifm;
386	struct mii_data *mii;
387	struct ifnet *ifp;
388	int err;
389
390	if (p->es_port < 0 || p->es_port >= sc->numports)
391		return (ENXIO);
392
393	if (sc->portphy[p->es_port] == sc->cpuport)
394		return (ENXIO);
395
396	mii = ukswitch_miiforport(sc, p->es_port);
397	if (mii == NULL)
398		return (ENXIO);
399
400	ifp = ukswitch_ifpforport(sc, p->es_port);
401
402	ifm = &mii->mii_media;
403	err = ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA);
404	return (err);
405}
406
407static int
408ukswitch_getvgroup(device_t dev, etherswitch_vlangroup_t *vg)
409{
410
411	/* Not supported. */
412	vg->es_vid = 0;
413	vg->es_member_ports = 0;
414	vg->es_untagged_ports = 0;
415	vg->es_fid = 0;
416	return (0);
417}
418
419static int
420ukswitch_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
421{
422
423	/* Not supported. */
424	return (0);
425}
426
427static void
428ukswitch_statchg(device_t dev)
429{
430
431	DPRINTF(dev, "%s\n", __func__);
432}
433
434static int
435ukswitch_ifmedia_upd(struct ifnet *ifp)
436{
437	struct ukswitch_softc *sc = ifp->if_softc;
438	struct mii_data *mii = ukswitch_miiforport(sc, ifp->if_dunit);
439
440	DPRINTF(sc->sc_dev, "%s\n", __func__);
441	if (mii == NULL)
442		return (ENXIO);
443	mii_mediachg(mii);
444	return (0);
445}
446
447static void
448ukswitch_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
449{
450	struct ukswitch_softc *sc = ifp->if_softc;
451	struct mii_data *mii = ukswitch_miiforport(sc, ifp->if_dunit);
452
453	DPRINTF(sc->sc_dev, "%s\n", __func__);
454
455	if (mii == NULL)
456		return;
457	mii_pollstat(mii);
458	ifmr->ifm_active = mii->mii_media_active;
459	ifmr->ifm_status = mii->mii_media_status;
460}
461
462static int
463ukswitch_readphy(device_t dev, int phy, int reg)
464{
465	struct ukswitch_softc *sc;
466	int data;
467
468	sc = device_get_softc(dev);
469	UKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
470
471	if (phy < 0 || phy >= 32)
472		return (ENXIO);
473	if (reg < 0 || reg >= 32)
474		return (ENXIO);
475
476	UKSWITCH_LOCK(sc);
477	data = MDIO_READREG(device_get_parent(dev), phy, reg);
478	UKSWITCH_UNLOCK(sc);
479
480	return (data);
481}
482
483static int
484ukswitch_writephy(device_t dev, int phy, int reg, int data)
485{
486	struct ukswitch_softc *sc;
487	int err;
488
489	sc = device_get_softc(dev);
490	UKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
491
492	if (phy < 0 || phy >= 32)
493		return (ENXIO);
494	if (reg < 0 || reg >= 32)
495		return (ENXIO);
496
497	UKSWITCH_LOCK(sc);
498	err = MDIO_WRITEREG(device_get_parent(dev), phy, reg, data);
499	UKSWITCH_UNLOCK(sc);
500
501	return (err);
502}
503
504static int
505ukswitch_readreg(device_t dev, int addr)
506{
507	struct ukswitch_softc *sc;
508
509	sc = device_get_softc(dev);
510	UKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
511
512	/* Not supported. */
513	return (0);
514}
515
516static int
517ukswitch_writereg(device_t dev, int addr, int value)
518{
519	struct ukswitch_softc *sc;
520
521	sc = device_get_softc(dev);
522	UKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
523
524	/* Not supported. */
525	return (0);
526}
527
528static device_method_t ukswitch_methods[] = {
529	/* Device interface */
530	DEVMETHOD(device_probe,		ukswitch_probe),
531	DEVMETHOD(device_attach,	ukswitch_attach),
532	DEVMETHOD(device_detach,	ukswitch_detach),
533
534	/* bus interface */
535	DEVMETHOD(bus_add_child,	device_add_child_ordered),
536
537	/* MII interface */
538	DEVMETHOD(miibus_readreg,	ukswitch_readphy),
539	DEVMETHOD(miibus_writereg,	ukswitch_writephy),
540	DEVMETHOD(miibus_statchg,	ukswitch_statchg),
541
542	/* MDIO interface */
543	DEVMETHOD(mdio_readreg,		ukswitch_readphy),
544	DEVMETHOD(mdio_writereg,	ukswitch_writephy),
545
546	/* etherswitch interface */
547	DEVMETHOD(etherswitch_lock,	ukswitch_lock),
548	DEVMETHOD(etherswitch_unlock,	ukswitch_unlock),
549	DEVMETHOD(etherswitch_getinfo,	ukswitch_getinfo),
550	DEVMETHOD(etherswitch_readreg,	ukswitch_readreg),
551	DEVMETHOD(etherswitch_writereg,	ukswitch_writereg),
552	DEVMETHOD(etherswitch_readphyreg,	ukswitch_readphy),
553	DEVMETHOD(etherswitch_writephyreg,	ukswitch_writephy),
554	DEVMETHOD(etherswitch_getport,	ukswitch_getport),
555	DEVMETHOD(etherswitch_setport,	ukswitch_setport),
556	DEVMETHOD(etherswitch_getvgroup,	ukswitch_getvgroup),
557	DEVMETHOD(etherswitch_setvgroup,	ukswitch_setvgroup),
558
559	DEVMETHOD_END
560};
561
562DEFINE_CLASS_0(ukswitch, ukswitch_driver, ukswitch_methods,
563    sizeof(struct ukswitch_softc));
564static devclass_t ukswitch_devclass;
565
566DRIVER_MODULE(ukswitch, mdio, ukswitch_driver, ukswitch_devclass, 0, 0);
567DRIVER_MODULE(miibus, ukswitch, miibus_driver, miibus_devclass, 0, 0);
568DRIVER_MODULE(mdio, ukswitch, mdio_driver, mdio_devclass, 0, 0);
569DRIVER_MODULE(etherswitch, ukswitch, etherswitch_driver, etherswitch_devclass, 0, 0);
570MODULE_VERSION(ukswitch, 1);
571MODULE_DEPEND(ukswitch, miibus, 1, 1, 1); /* XXX which versions? */
572MODULE_DEPEND(ukswitch, etherswitch, 1, 1, 1); /* XXX which versions? */
573