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? */
Cache object: bd002b4d08aa316c5914dd942e5b5c52
|