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