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