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