xref: /freebsd/sys/dev/etherswitch/ukswitch/ukswitch.c (revision af23369a6deaaeb612ab266eb88b8bb8d560c322)
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 
63 MALLOC_DECLARE(M_UKSWITCH);
64 MALLOC_DEFINE(M_UKSWITCH, "ukswitch", "ukswitch data structures");
65 
66 struct 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 	if_t *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 
98 static inline int ukswitch_portforphy(struct ukswitch_softc *, int);
99 static void ukswitch_tick(void *);
100 static int ukswitch_ifmedia_upd(if_t);
101 static void ukswitch_ifmedia_sts(if_t, struct ifmediareq *);
102 
103 static int
104 ukswitch_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 
115 static int
116 ukswitch_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 		if_setsoftc(sc->ifp[port], sc);
136 		if_setflags(sc->ifp[port], 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 		    if_name(sc->ifp[port]));
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 
162 static int
163 ukswitch_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(if_t) * 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 
233 static int
234 ukswitch_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  */
267 static inline int
268 ukswitch_portforphy(struct ukswitch_softc *sc, int phy)
269 {
270 
271 	return (sc->ifpport[phy]);
272 }
273 
274 static inline struct mii_data *
275 ukswitch_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 
283 static inline if_t
284 ukswitch_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  */
295 static void
296 ukswitch_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 
321 static void
322 ukswitch_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 
330 static void
331 ukswitch_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 
339 static void
340 ukswitch_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 
348 static etherswitch_info_t *
349 ukswitch_getinfo(device_t dev)
350 {
351 	struct ukswitch_softc *sc = device_get_softc(dev);
352 
353 	return (&sc->info);
354 }
355 
356 static int
357 ukswitch_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 
393 static int
394 ukswitch_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 	if_t 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 
419 static int
420 ukswitch_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 
431 static int
432 ukswitch_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
433 {
434 
435 	/* Not supported. */
436 	return (0);
437 }
438 
439 static void
440 ukswitch_statchg(device_t dev)
441 {
442 
443 	DPRINTF(dev, "%s\n", __func__);
444 }
445 
446 static int
447 ukswitch_ifmedia_upd(if_t ifp)
448 {
449 	struct ukswitch_softc *sc = if_getsoftc(ifp);
450 	struct mii_data *mii = ukswitch_miiforport(sc, if_getdunit(ifp));
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 
459 static void
460 ukswitch_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr)
461 {
462 	struct ukswitch_softc *sc = if_getsoftc(ifp);
463 	struct mii_data *mii = ukswitch_miiforport(sc, if_getdunit(ifp));
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 
474 static int
475 ukswitch_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 
495 static int
496 ukswitch_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 
516 static int
517 ukswitch_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 
528 static int
529 ukswitch_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 
540 static 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 
574 DEFINE_CLASS_0(ukswitch, ukswitch_driver, ukswitch_methods,
575     sizeof(struct ukswitch_softc));
576 
577 DRIVER_MODULE(ukswitch, mdio, ukswitch_driver, 0, 0);
578 DRIVER_MODULE(miibus, ukswitch, miibus_driver, 0, 0);
579 DRIVER_MODULE(mdio, ukswitch, mdio_driver, 0, 0);
580 DRIVER_MODULE(etherswitch, ukswitch, etherswitch_driver, 0, 0);
581 MODULE_VERSION(ukswitch, 1);
582 MODULE_DEPEND(ukswitch, miibus, 1, 1, 1); /* XXX which versions? */
583 MODULE_DEPEND(ukswitch, etherswitch, 1, 1, 1); /* XXX which versions? */
584