xref: /freebsd/sys/dev/etherswitch/ukswitch/ukswitch.c (revision aa3860851b9f6a6002d135b1cac7736e0995eedc)
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
ukswitch_probe(device_t dev)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
ukswitch_attach_phys(struct ukswitch_softc * sc)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
ukswitch_attach(device_t dev)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_generic_probe(dev);
213 	bus_enumerate_hinted_children(dev);
214 	err = bus_generic_attach(dev);
215 	if (err != 0)
216 		return (err);
217 
218 	callout_init(&sc->callout_tick, 0);
219 
220 	ukswitch_tick(sc);
221 
222 	return (err);
223 }
224 
225 static int
ukswitch_detach(device_t dev)226 ukswitch_detach(device_t dev)
227 {
228 	struct ukswitch_softc *sc = device_get_softc(dev);
229 	int i, port;
230 
231 	callout_drain(&sc->callout_tick);
232 
233 	for (i=0; i < MII_NPHY; i++) {
234 		if (((1 << i) & sc->phymask) == 0)
235 			continue;
236 		port = ukswitch_portforphy(sc, i);
237 		if (sc->miibus[port] != NULL)
238 			device_delete_child(dev, (*sc->miibus[port]));
239 		if (sc->ifp[port] != NULL)
240 			if_free(sc->ifp[port]);
241 		free(sc->ifname[port], M_UKSWITCH);
242 		free(sc->miibus[port], M_UKSWITCH);
243 	}
244 
245 	free(sc->portphy, M_UKSWITCH);
246 	free(sc->miibus, M_UKSWITCH);
247 	free(sc->ifname, M_UKSWITCH);
248 	free(sc->ifp, M_UKSWITCH);
249 
250 	bus_generic_detach(dev);
251 	mtx_destroy(&sc->sc_mtx);
252 
253 	return (0);
254 }
255 
256 /*
257  * Convert PHY number to port number.
258  */
259 static inline int
ukswitch_portforphy(struct ukswitch_softc * sc,int phy)260 ukswitch_portforphy(struct ukswitch_softc *sc, int phy)
261 {
262 
263 	return (sc->ifpport[phy]);
264 }
265 
266 static inline struct mii_data *
ukswitch_miiforport(struct ukswitch_softc * sc,int port)267 ukswitch_miiforport(struct ukswitch_softc *sc, int port)
268 {
269 
270 	if (port < 0 || port > sc->numports)
271 		return (NULL);
272 	return (device_get_softc(*sc->miibus[port]));
273 }
274 
275 static inline if_t
ukswitch_ifpforport(struct ukswitch_softc * sc,int port)276 ukswitch_ifpforport(struct ukswitch_softc *sc, int port)
277 {
278 
279 	if (port < 0 || port > sc->numports)
280 		return (NULL);
281 	return (sc->ifp[port]);
282 }
283 
284 /*
285  * Poll the status for all PHYs.
286  */
287 static void
ukswitch_miipollstat(struct ukswitch_softc * sc)288 ukswitch_miipollstat(struct ukswitch_softc *sc)
289 {
290 	int i, port;
291 	struct mii_data *mii;
292 	struct mii_softc *miisc;
293 
294 	UKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
295 
296 	for (i = 0; i < MII_NPHY; i++) {
297 		if (((1 << i) & sc->phymask) == 0)
298 			continue;
299 		port = ukswitch_portforphy(sc, i);
300 		if ((*sc->miibus[port]) == NULL)
301 			continue;
302 		mii = device_get_softc(*sc->miibus[port]);
303 		LIST_FOREACH(miisc, &mii->mii_phys, mii_list) {
304 			if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) !=
305 			    miisc->mii_inst)
306 				continue;
307 			ukphy_status(miisc);
308 			mii_phy_update(miisc, MII_POLLSTAT);
309 		}
310 	}
311 }
312 
313 static void
ukswitch_tick(void * arg)314 ukswitch_tick(void *arg)
315 {
316 	struct ukswitch_softc *sc = arg;
317 
318 	ukswitch_miipollstat(sc);
319 	callout_reset(&sc->callout_tick, hz, ukswitch_tick, sc);
320 }
321 
322 static void
ukswitch_lock(device_t dev)323 ukswitch_lock(device_t dev)
324 {
325 	struct ukswitch_softc *sc = device_get_softc(dev);
326 
327 	UKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
328 	UKSWITCH_LOCK(sc);
329 }
330 
331 static void
ukswitch_unlock(device_t dev)332 ukswitch_unlock(device_t dev)
333 {
334 	struct ukswitch_softc *sc = device_get_softc(dev);
335 
336 	UKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
337 	UKSWITCH_UNLOCK(sc);
338 }
339 
340 static etherswitch_info_t *
ukswitch_getinfo(device_t dev)341 ukswitch_getinfo(device_t dev)
342 {
343 	struct ukswitch_softc *sc = device_get_softc(dev);
344 
345 	return (&sc->info);
346 }
347 
348 static int
ukswitch_getport(device_t dev,etherswitch_port_t * p)349 ukswitch_getport(device_t dev, etherswitch_port_t *p)
350 {
351 	struct ukswitch_softc *sc = device_get_softc(dev);
352 	struct mii_data *mii;
353 	struct ifmediareq *ifmr = &p->es_ifmr;
354 	int err, phy;
355 
356 	if (p->es_port < 0 || p->es_port >= sc->numports)
357 		return (ENXIO);
358 	p->es_pvid = 0;
359 
360 	phy = sc->portphy[p->es_port];
361 	mii = ukswitch_miiforport(sc, p->es_port);
362 	if (sc->cpuport != -1 && phy == sc->cpuport) {
363 		/* fill in fixed values for CPU port */
364 		p->es_flags |= ETHERSWITCH_PORT_CPU;
365 		ifmr->ifm_count = 0;
366 		if (sc->media == 100)
367 			ifmr->ifm_current = ifmr->ifm_active =
368 			    IFM_ETHER | IFM_100_TX | IFM_FDX;
369 		else
370 			ifmr->ifm_current = ifmr->ifm_active =
371 			    IFM_ETHER | IFM_1000_T | IFM_FDX;
372 		ifmr->ifm_mask = 0;
373 		ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
374 	} else if (mii != NULL) {
375 		err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr,
376 		    &mii->mii_media, SIOCGIFMEDIA);
377 		if (err)
378 			return (err);
379 	} else {
380 		return (ENXIO);
381 	}
382 	return (0);
383 }
384 
385 static int
ukswitch_setport(device_t dev,etherswitch_port_t * p)386 ukswitch_setport(device_t dev, etherswitch_port_t *p)
387 {
388 	struct ukswitch_softc *sc = device_get_softc(dev);
389 	struct ifmedia *ifm;
390 	struct mii_data *mii;
391 	if_t ifp;
392 	int err;
393 
394 	if (p->es_port < 0 || p->es_port >= sc->numports)
395 		return (ENXIO);
396 
397 	if (sc->portphy[p->es_port] == sc->cpuport)
398 		return (ENXIO);
399 
400 	mii = ukswitch_miiforport(sc, p->es_port);
401 	if (mii == NULL)
402 		return (ENXIO);
403 
404 	ifp = ukswitch_ifpforport(sc, p->es_port);
405 
406 	ifm = &mii->mii_media;
407 	err = ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA);
408 	return (err);
409 }
410 
411 static int
ukswitch_getvgroup(device_t dev,etherswitch_vlangroup_t * vg)412 ukswitch_getvgroup(device_t dev, etherswitch_vlangroup_t *vg)
413 {
414 
415 	/* Not supported. */
416 	vg->es_vid = 0;
417 	vg->es_member_ports = 0;
418 	vg->es_untagged_ports = 0;
419 	vg->es_fid = 0;
420 	return (0);
421 }
422 
423 static int
ukswitch_setvgroup(device_t dev,etherswitch_vlangroup_t * vg)424 ukswitch_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
425 {
426 
427 	/* Not supported. */
428 	return (0);
429 }
430 
431 static void
ukswitch_statchg(device_t dev)432 ukswitch_statchg(device_t dev)
433 {
434 
435 	DPRINTF(dev, "%s\n", __func__);
436 }
437 
438 static int
ukswitch_ifmedia_upd(if_t ifp)439 ukswitch_ifmedia_upd(if_t ifp)
440 {
441 	struct ukswitch_softc *sc = if_getsoftc(ifp);
442 	struct mii_data *mii = ukswitch_miiforport(sc, if_getdunit(ifp));
443 
444 	DPRINTF(sc->sc_dev, "%s\n", __func__);
445 	if (mii == NULL)
446 		return (ENXIO);
447 	mii_mediachg(mii);
448 	return (0);
449 }
450 
451 static void
ukswitch_ifmedia_sts(if_t ifp,struct ifmediareq * ifmr)452 ukswitch_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr)
453 {
454 	struct ukswitch_softc *sc = if_getsoftc(ifp);
455 	struct mii_data *mii = ukswitch_miiforport(sc, if_getdunit(ifp));
456 
457 	DPRINTF(sc->sc_dev, "%s\n", __func__);
458 
459 	if (mii == NULL)
460 		return;
461 	mii_pollstat(mii);
462 	ifmr->ifm_active = mii->mii_media_active;
463 	ifmr->ifm_status = mii->mii_media_status;
464 }
465 
466 static int
ukswitch_readphy(device_t dev,int phy,int reg)467 ukswitch_readphy(device_t dev, int phy, int reg)
468 {
469 	struct ukswitch_softc *sc;
470 	int data;
471 
472 	sc = device_get_softc(dev);
473 	UKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
474 
475 	if (phy < 0 || phy >= 32)
476 		return (ENXIO);
477 	if (reg < 0 || reg >= 32)
478 		return (ENXIO);
479 
480 	UKSWITCH_LOCK(sc);
481 	data = MDIO_READREG(device_get_parent(dev), phy, reg);
482 	UKSWITCH_UNLOCK(sc);
483 
484 	return (data);
485 }
486 
487 static int
ukswitch_writephy(device_t dev,int phy,int reg,int data)488 ukswitch_writephy(device_t dev, int phy, int reg, int data)
489 {
490 	struct ukswitch_softc *sc;
491 	int err;
492 
493 	sc = device_get_softc(dev);
494 	UKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
495 
496 	if (phy < 0 || phy >= 32)
497 		return (ENXIO);
498 	if (reg < 0 || reg >= 32)
499 		return (ENXIO);
500 
501 	UKSWITCH_LOCK(sc);
502 	err = MDIO_WRITEREG(device_get_parent(dev), phy, reg, data);
503 	UKSWITCH_UNLOCK(sc);
504 
505 	return (err);
506 }
507 
508 static int
ukswitch_readreg(device_t dev,int addr)509 ukswitch_readreg(device_t dev, int addr)
510 {
511 	struct ukswitch_softc *sc __diagused;
512 
513 	sc = device_get_softc(dev);
514 	UKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
515 
516 	/* Not supported. */
517 	return (0);
518 }
519 
520 static int
ukswitch_writereg(device_t dev,int addr,int value)521 ukswitch_writereg(device_t dev, int addr, int value)
522 {
523 	struct ukswitch_softc *sc __diagused;
524 
525 	sc = device_get_softc(dev);
526 	UKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
527 
528 	/* Not supported. */
529 	return (0);
530 }
531 
532 static device_method_t ukswitch_methods[] = {
533 	/* Device interface */
534 	DEVMETHOD(device_probe,		ukswitch_probe),
535 	DEVMETHOD(device_attach,	ukswitch_attach),
536 	DEVMETHOD(device_detach,	ukswitch_detach),
537 
538 	/* bus interface */
539 	DEVMETHOD(bus_add_child,	device_add_child_ordered),
540 
541 	/* MII interface */
542 	DEVMETHOD(miibus_readreg,	ukswitch_readphy),
543 	DEVMETHOD(miibus_writereg,	ukswitch_writephy),
544 	DEVMETHOD(miibus_statchg,	ukswitch_statchg),
545 
546 	/* MDIO interface */
547 	DEVMETHOD(mdio_readreg,		ukswitch_readphy),
548 	DEVMETHOD(mdio_writereg,	ukswitch_writephy),
549 
550 	/* etherswitch interface */
551 	DEVMETHOD(etherswitch_lock,	ukswitch_lock),
552 	DEVMETHOD(etherswitch_unlock,	ukswitch_unlock),
553 	DEVMETHOD(etherswitch_getinfo,	ukswitch_getinfo),
554 	DEVMETHOD(etherswitch_readreg,	ukswitch_readreg),
555 	DEVMETHOD(etherswitch_writereg,	ukswitch_writereg),
556 	DEVMETHOD(etherswitch_readphyreg,	ukswitch_readphy),
557 	DEVMETHOD(etherswitch_writephyreg,	ukswitch_writephy),
558 	DEVMETHOD(etherswitch_getport,	ukswitch_getport),
559 	DEVMETHOD(etherswitch_setport,	ukswitch_setport),
560 	DEVMETHOD(etherswitch_getvgroup,	ukswitch_getvgroup),
561 	DEVMETHOD(etherswitch_setvgroup,	ukswitch_setvgroup),
562 
563 	DEVMETHOD_END
564 };
565 
566 DEFINE_CLASS_0(ukswitch, ukswitch_driver, ukswitch_methods,
567     sizeof(struct ukswitch_softc));
568 
569 DRIVER_MODULE(ukswitch, mdio, ukswitch_driver, 0, 0);
570 DRIVER_MODULE(miibus, ukswitch, miibus_driver, 0, 0);
571 DRIVER_MODULE(mdio, ukswitch, mdio_driver, 0, 0);
572 DRIVER_MODULE(etherswitch, ukswitch, etherswitch_driver, 0, 0);
573 MODULE_VERSION(ukswitch, 1);
574 MODULE_DEPEND(ukswitch, miibus, 1, 1, 1); /* XXX which versions? */
575 MODULE_DEPEND(ukswitch, etherswitch, 1, 1, 1); /* XXX which versions? */
576