xref: /freebsd/sys/dev/etherswitch/ukswitch/ukswitch.c (revision da5432eda807c4b7232d030d5157d5b417ea4f52)
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_copy(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 (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 
160 static int
161 ukswitch_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 
231 static int
232 ukswitch_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  */
265 static inline int
266 ukswitch_portforphy(struct ukswitch_softc *sc, int phy)
267 {
268 
269 	return (sc->ifpport[phy]);
270 }
271 
272 static inline struct mii_data *
273 ukswitch_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 
281 static inline if_t
282 ukswitch_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  */
293 static void
294 ukswitch_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 
319 static void
320 ukswitch_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 
328 static void
329 ukswitch_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 
337 static void
338 ukswitch_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 
346 static etherswitch_info_t *
347 ukswitch_getinfo(device_t dev)
348 {
349 	struct ukswitch_softc *sc = device_get_softc(dev);
350 
351 	return (&sc->info);
352 }
353 
354 static int
355 ukswitch_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 
391 static int
392 ukswitch_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 
417 static int
418 ukswitch_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 
429 static int
430 ukswitch_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
431 {
432 
433 	/* Not supported. */
434 	return (0);
435 }
436 
437 static void
438 ukswitch_statchg(device_t dev)
439 {
440 
441 	DPRINTF(dev, "%s\n", __func__);
442 }
443 
444 static int
445 ukswitch_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 
457 static void
458 ukswitch_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 
472 static int
473 ukswitch_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 
493 static int
494 ukswitch_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 
514 static int
515 ukswitch_readreg(device_t dev, int addr)
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 
526 static int
527 ukswitch_writereg(device_t dev, int addr, int value)
528 {
529 	struct ukswitch_softc *sc;
530 
531 	sc = device_get_softc(dev);
532 	UKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
533 
534 	/* Not supported. */
535 	return (0);
536 }
537 
538 static 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 
572 DEFINE_CLASS_0(ukswitch, ukswitch_driver, ukswitch_methods,
573     sizeof(struct ukswitch_softc));
574 
575 DRIVER_MODULE(ukswitch, mdio, ukswitch_driver, 0, 0);
576 DRIVER_MODULE(miibus, ukswitch, miibus_driver, 0, 0);
577 DRIVER_MODULE(mdio, ukswitch, mdio_driver, 0, 0);
578 DRIVER_MODULE(etherswitch, ukswitch, etherswitch_driver, 0, 0);
579 MODULE_VERSION(ukswitch, 1);
580 MODULE_DEPEND(ukswitch, miibus, 1, 1, 1); /* XXX which versions? */
581 MODULE_DEPEND(ukswitch, etherswitch, 1, 1, 1); /* XXX which versions? */
582