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