1 /*-
2 * Copyright (c) 2016 Hiroki Mori
3 * Copyright (c) 2013 Luiz Otavio O Souza.
4 * Copyright (c) 2011-2012 Stefan Bethke.
5 * Copyright (c) 2012 Adrian Chadd.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 /*
31 * This is Infineon ADM6996FC/M/MX driver code on etherswitch framework.
32 * Support PORT and DOT1Q VLAN.
33 * This code suppose ADM6996FC SDC/SDIO connect to SOC network interface
34 * MDC/MDIO.
35 * This code development on Netgear WGR614Cv7.
36 * etherswitchcfg command port option support addtag.
37 */
38
39 #include <sys/param.h>
40 #include <sys/bus.h>
41 #include <sys/errno.h>
42 #include <sys/kernel.h>
43 #include <sys/lock.h>
44 #include <sys/malloc.h>
45 #include <sys/module.h>
46 #include <sys/mutex.h>
47 #include <sys/socket.h>
48 #include <sys/sockio.h>
49 #include <sys/sysctl.h>
50 #include <sys/systm.h>
51
52 #include <net/if.h>
53 #include <net/if_var.h>
54 #include <net/ethernet.h>
55 #include <net/if_media.h>
56 #include <net/if_types.h>
57
58 #include <machine/bus.h>
59 #include <dev/mii/mii.h>
60 #include <dev/mii/miivar.h>
61 #include <dev/mdio/mdio.h>
62
63 #include <dev/etherswitch/etherswitch.h>
64
65 #include "mdio_if.h"
66 #include "miibus_if.h"
67 #include "etherswitch_if.h"
68
69 #define ADM6996FC_PRODUCT_CODE 0x7102
70
71 #define ADM6996FC_SC3 0x11
72 #define ADM6996FC_VF0L 0x40
73 #define ADM6996FC_VF0H 0x41
74 #define ADM6996FC_CI0 0xa0
75 #define ADM6996FC_CI1 0xa1
76 #define ADM6996FC_PHY_C0 0x200
77
78 #define ADM6996FC_PC_SHIFT 4
79 #define ADM6996FC_TBV_SHIFT 5
80 #define ADM6996FC_PVID_SHIFT 10
81 #define ADM6996FC_OPTE_SHIFT 4
82 #define ADM6996FC_VV_SHIFT 15
83
84 #define ADM6996FC_PHY_SIZE 0x20
85
86 MALLOC_DECLARE(M_ADM6996FC);
87 MALLOC_DEFINE(M_ADM6996FC, "adm6996fc", "adm6996fc data structures");
88
89 struct adm6996fc_softc {
90 struct mtx sc_mtx; /* serialize access to softc */
91 device_t sc_dev;
92 int vlan_mode;
93 int media; /* cpu port media */
94 int cpuport; /* which PHY is connected to the CPU */
95 int phymask; /* PHYs we manage */
96 int numports; /* number of ports */
97 int ifpport[MII_NPHY];
98 int *portphy;
99 char **ifname;
100 device_t **miibus;
101 if_t *ifp;
102 struct callout callout_tick;
103 etherswitch_info_t info;
104 };
105
106 #define ADM6996FC_LOCK(_sc) \
107 mtx_lock(&(_sc)->sc_mtx)
108 #define ADM6996FC_UNLOCK(_sc) \
109 mtx_unlock(&(_sc)->sc_mtx)
110 #define ADM6996FC_LOCK_ASSERT(_sc, _what) \
111 mtx_assert(&(_sc)->sc_mtx, (_what))
112 #define ADM6996FC_TRYLOCK(_sc) \
113 mtx_trylock(&(_sc)->sc_mtx)
114
115 #if defined(DEBUG)
116 #define DPRINTF(dev, args...) device_printf(dev, args)
117 #else
118 #define DPRINTF(dev, args...)
119 #endif
120
121 static inline int adm6996fc_portforphy(struct adm6996fc_softc *, int);
122 static void adm6996fc_tick(void *);
123 static int adm6996fc_ifmedia_upd(if_t);
124 static void adm6996fc_ifmedia_sts(if_t, struct ifmediareq *);
125
126 #define ADM6996FC_READREG(dev, x) \
127 MDIO_READREG(dev, ((x) >> 5), ((x) & 0x1f));
128 #define ADM6996FC_WRITEREG(dev, x, v) \
129 MDIO_WRITEREG(dev, ((x) >> 5), ((x) & 0x1f), v);
130
131 #define ADM6996FC_PVIDBYDATA(data1, data2) \
132 ((((data1) >> ADM6996FC_PVID_SHIFT) & 0x0f) | ((data2) << 4))
133
134 static int
adm6996fc_probe(device_t dev)135 adm6996fc_probe(device_t dev)
136 {
137 int data1, data2;
138 int pc;
139 struct adm6996fc_softc *sc;
140
141 sc = device_get_softc(dev);
142 bzero(sc, sizeof(*sc));
143
144 data1 = ADM6996FC_READREG(device_get_parent(dev), ADM6996FC_CI0);
145 data2 = ADM6996FC_READREG(device_get_parent(dev), ADM6996FC_CI1);
146 pc = ((data2 << 16) | data1) >> ADM6996FC_PC_SHIFT;
147 if (bootverbose)
148 device_printf(dev,"Chip Identifier Register %x %x\n", data1,
149 data2);
150
151 /* check Product Code */
152 if (pc != ADM6996FC_PRODUCT_CODE) {
153 return (ENXIO);
154 }
155
156 device_set_desc(dev, "Infineon ADM6996FC/M/MX MDIO switch driver");
157 return (BUS_PROBE_DEFAULT);
158 }
159
160 static int
adm6996fc_attach_phys(struct adm6996fc_softc * sc)161 adm6996fc_attach_phys(struct adm6996fc_softc *sc)
162 {
163 int phy, port, err;
164 char name[IFNAMSIZ];
165
166 port = 0;
167 err = 0;
168 /* PHYs need an interface, so we generate a dummy one */
169 snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->sc_dev));
170 for (phy = 0; phy < sc->numports; phy++) {
171 if (((1 << phy) & sc->phymask) == 0)
172 continue;
173 sc->ifpport[phy] = port;
174 sc->portphy[port] = phy;
175 sc->ifp[port] = if_alloc(IFT_ETHER);
176 sc->ifp[port]->if_softc = sc;
177 sc->ifp[port]->if_flags |= IFF_UP | IFF_BROADCAST |
178 IFF_DRV_RUNNING | IFF_SIMPLEX;
179 if_initname(sc->ifp[port], name, port);
180 sc->miibus[port] = malloc(sizeof(device_t), M_ADM6996FC,
181 M_WAITOK | M_ZERO);
182 err = mii_attach(sc->sc_dev, sc->miibus[port], sc->ifp[port],
183 adm6996fc_ifmedia_upd, adm6996fc_ifmedia_sts, \
184 BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0);
185 DPRINTF(sc->sc_dev, "%s attached to pseudo interface %s\n",
186 device_get_nameunit(*sc->miibus[port]),
187 sc->ifp[port]->if_xname);
188 if (err != 0) {
189 device_printf(sc->sc_dev,
190 "attaching PHY %d failed\n",
191 phy);
192 goto failed;
193 }
194 ++port;
195 }
196 sc->info.es_nports = port;
197 if (sc->cpuport != -1) {
198 /* assume cpuport is last one */
199 sc->ifpport[sc->cpuport] = port;
200 sc->portphy[port] = sc->cpuport;
201 ++sc->info.es_nports;
202 }
203 return (0);
204
205 failed:
206 for (phy = 0; phy < sc->numports; phy++) {
207 if (((1 << phy) & sc->phymask) == 0)
208 continue;
209 port = adm6996fc_portforphy(sc, phy);
210 if (sc->miibus[port] != NULL)
211 device_delete_child(sc->sc_dev, (*sc->miibus[port]));
212 if (sc->ifp[port] != NULL)
213 if_free(sc->ifp[port]);
214 if (sc->ifname[port] != NULL)
215 free(sc->ifname[port], M_ADM6996FC);
216 if (sc->miibus[port] != NULL)
217 free(sc->miibus[port], M_ADM6996FC);
218 }
219 return (err);
220 }
221
222 static int
adm6996fc_attach(device_t dev)223 adm6996fc_attach(device_t dev)
224 {
225 struct adm6996fc_softc *sc;
226 int err;
227
228 err = 0;
229 sc = device_get_softc(dev);
230
231 sc->sc_dev = dev;
232 mtx_init(&sc->sc_mtx, "adm6996fc", NULL, MTX_DEF);
233 strlcpy(sc->info.es_name, device_get_desc(dev),
234 sizeof(sc->info.es_name));
235
236 /* ADM6996FC Defaults */
237 sc->numports = 6;
238 sc->phymask = 0x1f;
239 sc->cpuport = 5;
240 sc->media = 100;
241
242 sc->info.es_nvlangroups = 16;
243 sc->info.es_vlan_caps = ETHERSWITCH_VLAN_PORT | ETHERSWITCH_VLAN_DOT1Q;
244
245 sc->ifp = malloc(sizeof(if_t) * sc->numports, M_ADM6996FC,
246 M_WAITOK | M_ZERO);
247 sc->ifname = malloc(sizeof(char *) * sc->numports, M_ADM6996FC,
248 M_WAITOK | M_ZERO);
249 sc->miibus = malloc(sizeof(device_t *) * sc->numports, M_ADM6996FC,
250 M_WAITOK | M_ZERO);
251 sc->portphy = malloc(sizeof(int) * sc->numports, M_ADM6996FC,
252 M_WAITOK | M_ZERO);
253
254 /*
255 * Attach the PHYs and complete the bus enumeration.
256 */
257 err = adm6996fc_attach_phys(sc);
258 if (err != 0)
259 goto failed;
260
261 bus_generic_probe(dev);
262 bus_enumerate_hinted_children(dev);
263 err = bus_generic_attach(dev);
264 if (err != 0)
265 goto failed;
266
267 callout_init(&sc->callout_tick, 0);
268
269 adm6996fc_tick(sc);
270
271 return (0);
272
273 failed:
274 free(sc->portphy, M_ADM6996FC);
275 free(sc->miibus, M_ADM6996FC);
276 free(sc->ifname, M_ADM6996FC);
277 free(sc->ifp, M_ADM6996FC);
278
279 return (err);
280 }
281
282 static int
adm6996fc_detach(device_t dev)283 adm6996fc_detach(device_t dev)
284 {
285 struct adm6996fc_softc *sc;
286 int i, port;
287
288 sc = device_get_softc(dev);
289
290 callout_drain(&sc->callout_tick);
291
292 for (i = 0; i < MII_NPHY; i++) {
293 if (((1 << i) & sc->phymask) == 0)
294 continue;
295 port = adm6996fc_portforphy(sc, i);
296 if (sc->miibus[port] != NULL)
297 device_delete_child(dev, (*sc->miibus[port]));
298 if (sc->ifp[port] != NULL)
299 if_free(sc->ifp[port]);
300 free(sc->ifname[port], M_ADM6996FC);
301 free(sc->miibus[port], M_ADM6996FC);
302 }
303
304 free(sc->portphy, M_ADM6996FC);
305 free(sc->miibus, M_ADM6996FC);
306 free(sc->ifname, M_ADM6996FC);
307 free(sc->ifp, M_ADM6996FC);
308
309 bus_generic_detach(dev);
310 mtx_destroy(&sc->sc_mtx);
311
312 return (0);
313 }
314
315 /*
316 * Convert PHY number to port number.
317 */
318 static inline int
adm6996fc_portforphy(struct adm6996fc_softc * sc,int phy)319 adm6996fc_portforphy(struct adm6996fc_softc *sc, int phy)
320 {
321
322 return (sc->ifpport[phy]);
323 }
324
325 static inline struct mii_data *
adm6996fc_miiforport(struct adm6996fc_softc * sc,int port)326 adm6996fc_miiforport(struct adm6996fc_softc *sc, int port)
327 {
328
329 if (port < 0 || port > sc->numports)
330 return (NULL);
331 if (port == sc->cpuport)
332 return (NULL);
333 return (device_get_softc(*sc->miibus[port]));
334 }
335
336 static inline if_t
adm6996fc_ifpforport(struct adm6996fc_softc * sc,int port)337 adm6996fc_ifpforport(struct adm6996fc_softc *sc, int port)
338 {
339
340 if (port < 0 || port > sc->numports)
341 return (NULL);
342 return (sc->ifp[port]);
343 }
344
345 /*
346 * Poll the status for all PHYs.
347 */
348 static void
adm6996fc_miipollstat(struct adm6996fc_softc * sc)349 adm6996fc_miipollstat(struct adm6996fc_softc *sc)
350 {
351 int i, port;
352 struct mii_data *mii;
353 struct mii_softc *miisc;
354
355 ADM6996FC_LOCK_ASSERT(sc, MA_NOTOWNED);
356
357 for (i = 0; i < MII_NPHY; i++) {
358 if (((1 << i) & sc->phymask) == 0)
359 continue;
360 port = adm6996fc_portforphy(sc, i);
361 if ((*sc->miibus[port]) == NULL)
362 continue;
363 mii = device_get_softc(*sc->miibus[port]);
364 LIST_FOREACH(miisc, &mii->mii_phys, mii_list) {
365 if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) !=
366 miisc->mii_inst)
367 continue;
368 ukphy_status(miisc);
369 mii_phy_update(miisc, MII_POLLSTAT);
370 }
371 }
372 }
373
374 static void
adm6996fc_tick(void * arg)375 adm6996fc_tick(void *arg)
376 {
377 struct adm6996fc_softc *sc;
378
379 sc = arg;
380
381 adm6996fc_miipollstat(sc);
382 callout_reset(&sc->callout_tick, hz, adm6996fc_tick, sc);
383 }
384
385 static void
adm6996fc_lock(device_t dev)386 adm6996fc_lock(device_t dev)
387 {
388 struct adm6996fc_softc *sc;
389
390 sc = device_get_softc(dev);
391
392 ADM6996FC_LOCK_ASSERT(sc, MA_NOTOWNED);
393 ADM6996FC_LOCK(sc);
394 }
395
396 static void
adm6996fc_unlock(device_t dev)397 adm6996fc_unlock(device_t dev)
398 {
399 struct adm6996fc_softc *sc;
400
401 sc = device_get_softc(dev);
402
403 ADM6996FC_LOCK_ASSERT(sc, MA_OWNED);
404 ADM6996FC_UNLOCK(sc);
405 }
406
407 static etherswitch_info_t *
adm6996fc_getinfo(device_t dev)408 adm6996fc_getinfo(device_t dev)
409 {
410 struct adm6996fc_softc *sc;
411
412 sc = device_get_softc(dev);
413
414 return (&sc->info);
415 }
416
417 static int
adm6996fc_getport(device_t dev,etherswitch_port_t * p)418 adm6996fc_getport(device_t dev, etherswitch_port_t *p)
419 {
420 struct adm6996fc_softc *sc;
421 struct mii_data *mii;
422 struct ifmediareq *ifmr;
423 device_t parent;
424 int err, phy;
425 int data1, data2;
426
427 int bcaddr[6] = {0x01, 0x03, 0x05, 0x07, 0x08, 0x09};
428 int vidaddr[6] = {0x28, 0x29, 0x2a, 0x2b, 0x2b, 0x2c};
429
430 sc = device_get_softc(dev);
431 ifmr = &p->es_ifmr;
432
433 if (p->es_port < 0 || p->es_port >= sc->numports)
434 return (ENXIO);
435
436 parent = device_get_parent(dev);
437
438 if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
439 data1 = ADM6996FC_READREG(parent, bcaddr[p->es_port]);
440 data2 = ADM6996FC_READREG(parent, vidaddr[p->es_port]);
441 /* only port 4 is hi bit */
442 if (p->es_port == 4)
443 data2 = (data2 >> 8) & 0xff;
444 else
445 data2 = data2 & 0xff;
446
447 p->es_pvid = ADM6996FC_PVIDBYDATA(data1, data2);
448 if (((data1 >> ADM6996FC_OPTE_SHIFT) & 0x01) == 1)
449 p->es_flags |= ETHERSWITCH_PORT_ADDTAG;
450 } else {
451 p->es_pvid = 0;
452 }
453
454 phy = sc->portphy[p->es_port];
455 mii = adm6996fc_miiforport(sc, p->es_port);
456 if (sc->cpuport != -1 && phy == sc->cpuport) {
457 /* fill in fixed values for CPU port */
458 p->es_flags |= ETHERSWITCH_PORT_CPU;
459 ifmr->ifm_count = 0;
460 if (sc->media == 100)
461 ifmr->ifm_current = ifmr->ifm_active =
462 IFM_ETHER | IFM_100_TX | IFM_FDX;
463 else
464 ifmr->ifm_current = ifmr->ifm_active =
465 IFM_ETHER | IFM_1000_T | IFM_FDX;
466 ifmr->ifm_mask = 0;
467 ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
468 } else if (mii != NULL) {
469 err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr,
470 &mii->mii_media, SIOCGIFMEDIA);
471 if (err)
472 return (err);
473 } else {
474 return (ENXIO);
475 }
476 return (0);
477 }
478
479 static int
adm6996fc_setport(device_t dev,etherswitch_port_t * p)480 adm6996fc_setport(device_t dev, etherswitch_port_t *p)
481 {
482 struct adm6996fc_softc *sc;
483 struct ifmedia *ifm;
484 struct mii_data *mii;
485 if_t ifp;
486 device_t parent;
487 int err;
488 int data;
489
490 int bcaddr[6] = {0x01, 0x03, 0x05, 0x07, 0x08, 0x09};
491 int vidaddr[6] = {0x28, 0x29, 0x2a, 0x2b, 0x2b, 0x2c};
492
493 sc = device_get_softc(dev);
494 parent = device_get_parent(dev);
495
496 if (p->es_port < 0 || p->es_port >= sc->numports)
497 return (ENXIO);
498
499 if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
500 data = ADM6996FC_READREG(parent, bcaddr[p->es_port]);
501 data &= ~(0xf << 10);
502 data |= (p->es_pvid & 0xf) << ADM6996FC_PVID_SHIFT;
503 if (p->es_flags & ETHERSWITCH_PORT_ADDTAG)
504 data |= 1 << ADM6996FC_OPTE_SHIFT;
505 else
506 data &= ~(1 << ADM6996FC_OPTE_SHIFT);
507 ADM6996FC_WRITEREG(parent, bcaddr[p->es_port], data);
508 data = ADM6996FC_READREG(parent, vidaddr[p->es_port]);
509 /* only port 4 is hi bit */
510 if (p->es_port == 4) {
511 data &= ~(0xff << 8);
512 data = data | (((p->es_pvid >> 4) & 0xff) << 8);
513 } else {
514 data &= ~0xff;
515 data = data | ((p->es_pvid >> 4) & 0xff);
516 }
517 ADM6996FC_WRITEREG(parent, vidaddr[p->es_port], data);
518 err = 0;
519 } else {
520 if (sc->portphy[p->es_port] == sc->cpuport)
521 return (ENXIO);
522 }
523
524 if (sc->portphy[p->es_port] != sc->cpuport) {
525 mii = adm6996fc_miiforport(sc, p->es_port);
526 if (mii == NULL)
527 return (ENXIO);
528
529 ifp = adm6996fc_ifpforport(sc, p->es_port);
530
531 ifm = &mii->mii_media;
532 err = ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA);
533 }
534 return (err);
535 }
536
537 static int
adm6996fc_getvgroup(device_t dev,etherswitch_vlangroup_t * vg)538 adm6996fc_getvgroup(device_t dev, etherswitch_vlangroup_t *vg)
539 {
540 struct adm6996fc_softc *sc;
541 device_t parent;
542 int datahi, datalo;
543
544 sc = device_get_softc(dev);
545 parent = device_get_parent(dev);
546
547 if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) {
548 if (vg->es_vlangroup <= 5) {
549 vg->es_vid = ETHERSWITCH_VID_VALID;
550 vg->es_vid |= vg->es_vlangroup;
551 datalo = ADM6996FC_READREG(parent,
552 ADM6996FC_VF0L + 2 * vg->es_vlangroup);
553 datahi = ADM6996FC_READREG(parent,
554 ADM6996FC_VF0H + 2 * vg->es_vlangroup);
555
556 vg->es_member_ports = datalo & 0x3f;
557 vg->es_untagged_ports = vg->es_member_ports;
558 vg->es_fid = 0;
559 } else {
560 vg->es_vid = 0;
561 }
562 } else if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
563 datalo = ADM6996FC_READREG(parent,
564 ADM6996FC_VF0L + 2 * vg->es_vlangroup);
565 datahi = ADM6996FC_READREG(parent,
566 ADM6996FC_VF0H + 2 * vg->es_vlangroup);
567
568 if (datahi & (1 << ADM6996FC_VV_SHIFT)) {
569 vg->es_vid = ETHERSWITCH_VID_VALID;
570 vg->es_vid |= datahi & 0xfff;
571 vg->es_member_ports = datalo & 0x3f;
572 vg->es_untagged_ports = (~datalo >> 6) & 0x3f;
573 vg->es_fid = 0;
574 } else {
575 vg->es_fid = 0;
576 }
577 } else {
578 vg->es_fid = 0;
579 }
580
581 return (0);
582 }
583
584 static int
adm6996fc_setvgroup(device_t dev,etherswitch_vlangroup_t * vg)585 adm6996fc_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
586 {
587 struct adm6996fc_softc *sc;
588 device_t parent;
589
590 sc = device_get_softc(dev);
591 parent = device_get_parent(dev);
592
593 if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) {
594 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0L + 2 * vg->es_vlangroup,
595 vg->es_member_ports);
596 } else if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
597 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0L + 2 * vg->es_vlangroup,
598 vg->es_member_ports | ((~vg->es_untagged_ports & 0x3f)<< 6));
599 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0H + 2 * vg->es_vlangroup,
600 (1 << ADM6996FC_VV_SHIFT) | vg->es_vid);
601 }
602
603 return (0);
604 }
605
606 static int
adm6996fc_getconf(device_t dev,etherswitch_conf_t * conf)607 adm6996fc_getconf(device_t dev, etherswitch_conf_t *conf)
608 {
609 struct adm6996fc_softc *sc;
610
611 sc = device_get_softc(dev);
612
613 /* Return the VLAN mode. */
614 conf->cmd = ETHERSWITCH_CONF_VLAN_MODE;
615 conf->vlan_mode = sc->vlan_mode;
616
617 return (0);
618 }
619
620 static int
adm6996fc_setconf(device_t dev,etherswitch_conf_t * conf)621 adm6996fc_setconf(device_t dev, etherswitch_conf_t *conf)
622 {
623 struct adm6996fc_softc *sc;
624 device_t parent;
625 int i;
626 int data;
627 int bcaddr[6] = {0x01, 0x03, 0x05, 0x07, 0x08, 0x09};
628
629 sc = device_get_softc(dev);
630 parent = device_get_parent(dev);
631
632 if ((conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) == 0)
633 return (0);
634
635 if (conf->vlan_mode == ETHERSWITCH_VLAN_PORT) {
636 sc->vlan_mode = ETHERSWITCH_VLAN_PORT;
637 data = ADM6996FC_READREG(parent, ADM6996FC_SC3);
638 data &= ~(1 << ADM6996FC_TBV_SHIFT);
639 ADM6996FC_WRITEREG(parent, ADM6996FC_SC3, data);
640 for (i = 0;i <= 5; ++i) {
641 data = ADM6996FC_READREG(parent, bcaddr[i]);
642 data &= ~(0xf << 10);
643 data |= (i << 10);
644 ADM6996FC_WRITEREG(parent, bcaddr[i], data);
645 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0L + 2 * i,
646 0x003f);
647 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0H + 2 * i,
648 (1 << ADM6996FC_VV_SHIFT) | 1);
649 }
650 } else if (conf->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
651 sc->vlan_mode = ETHERSWITCH_VLAN_DOT1Q;
652 data = ADM6996FC_READREG(parent, ADM6996FC_SC3);
653 data |= (1 << ADM6996FC_TBV_SHIFT);
654 ADM6996FC_WRITEREG(parent, ADM6996FC_SC3, data);
655 for (i = 0;i <= 5; ++i) {
656 data = ADM6996FC_READREG(parent, bcaddr[i]);
657 /* Private VID set 1 */
658 data &= ~(0xf << 10);
659 data |= (1 << 10);
660 ADM6996FC_WRITEREG(parent, bcaddr[i], data);
661 }
662 for (i = 2;i <= 15; ++i) {
663 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0H + 2 * i,
664 0x0000);
665 }
666 } else {
667 /*
668 ADM6996FC have no VLAN off. Then set Port base and
669 add all port to member. Use VLAN Filter 1 is reset
670 default.
671 */
672 sc->vlan_mode = 0;
673 data = ADM6996FC_READREG(parent, ADM6996FC_SC3);
674 data &= ~(1 << ADM6996FC_TBV_SHIFT);
675 ADM6996FC_WRITEREG(parent, ADM6996FC_SC3, data);
676 for (i = 0;i <= 5; ++i) {
677 data = ADM6996FC_READREG(parent, bcaddr[i]);
678 data &= ~(0xf << 10);
679 data |= (1 << 10);
680 if (i == 5)
681 data &= ~(1 << 4);
682 ADM6996FC_WRITEREG(parent, bcaddr[i], data);
683 }
684 /* default setting */
685 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0L + 2, 0x003f);
686 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0H + 2,
687 (1 << ADM6996FC_VV_SHIFT) | 1);
688 }
689
690
691 return (0);
692 }
693
694 static void
adm6996fc_statchg(device_t dev)695 adm6996fc_statchg(device_t dev)
696 {
697
698 DPRINTF(dev, "%s\n", __func__);
699 }
700
701 static int
adm6996fc_ifmedia_upd(if_t ifp)702 adm6996fc_ifmedia_upd(if_t ifp)
703 {
704 struct adm6996fc_softc *sc;
705 struct mii_data *mii;
706
707 sc = if_getsoftc(ifp);
708 mii = adm6996fc_miiforport(sc, if_getdunit(ifp));
709
710 DPRINTF(sc->sc_dev, "%s\n", __func__);
711 if (mii == NULL)
712 return (ENXIO);
713 mii_mediachg(mii);
714 return (0);
715 }
716
717 static void
adm6996fc_ifmedia_sts(if_t ifp,struct ifmediareq * ifmr)718 adm6996fc_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr)
719 {
720 struct adm6996fc_softc *sc;
721 struct mii_data *mii;
722
723 sc = if_getsoftc(ifp);
724 mii = adm6996fc_miiforport(sc, if_getdunit(ifp));
725
726 DPRINTF(sc->sc_dev, "%s\n", __func__);
727
728 if (mii == NULL)
729 return;
730 mii_pollstat(mii);
731 ifmr->ifm_active = mii->mii_media_active;
732 ifmr->ifm_status = mii->mii_media_status;
733 }
734
735 static int
adm6996fc_readphy(device_t dev,int phy,int reg)736 adm6996fc_readphy(device_t dev, int phy, int reg)
737 {
738 struct adm6996fc_softc *sc;
739 int data;
740
741 sc = device_get_softc(dev);
742 ADM6996FC_LOCK_ASSERT(sc, MA_NOTOWNED);
743
744 if (phy < 0 || phy >= 32)
745 return (ENXIO);
746 if (reg < 0 || reg >= 32)
747 return (ENXIO);
748
749 ADM6996FC_LOCK(sc);
750 data = ADM6996FC_READREG(device_get_parent(dev),
751 (ADM6996FC_PHY_C0 + ADM6996FC_PHY_SIZE * phy) + reg);
752 ADM6996FC_UNLOCK(sc);
753
754 return (data);
755 }
756
757 static int
adm6996fc_writephy(device_t dev,int phy,int reg,int data)758 adm6996fc_writephy(device_t dev, int phy, int reg, int data)
759 {
760 struct adm6996fc_softc *sc;
761 int err;
762
763 sc = device_get_softc(dev);
764 ADM6996FC_LOCK_ASSERT(sc, MA_NOTOWNED);
765
766 if (phy < 0 || phy >= 32)
767 return (ENXIO);
768 if (reg < 0 || reg >= 32)
769 return (ENXIO);
770
771 ADM6996FC_LOCK(sc);
772 err = ADM6996FC_WRITEREG(device_get_parent(dev),
773 (ADM6996FC_PHY_C0 + ADM6996FC_PHY_SIZE * phy) + reg, data);
774 ADM6996FC_UNLOCK(sc);
775
776 return (err);
777 }
778
779 static int
adm6996fc_readreg(device_t dev,int addr)780 adm6996fc_readreg(device_t dev, int addr)
781 {
782
783 return ADM6996FC_READREG(device_get_parent(dev), addr);
784 }
785
786 static int
adm6996fc_writereg(device_t dev,int addr,int value)787 adm6996fc_writereg(device_t dev, int addr, int value)
788 {
789 int err;
790
791 err = ADM6996FC_WRITEREG(device_get_parent(dev), addr, value);
792 return (err);
793 }
794
795 static device_method_t adm6996fc_methods[] = {
796 /* Device interface */
797 DEVMETHOD(device_probe, adm6996fc_probe),
798 DEVMETHOD(device_attach, adm6996fc_attach),
799 DEVMETHOD(device_detach, adm6996fc_detach),
800
801 /* bus interface */
802 DEVMETHOD(bus_add_child, device_add_child_ordered),
803
804 /* MII interface */
805 DEVMETHOD(miibus_readreg, adm6996fc_readphy),
806 DEVMETHOD(miibus_writereg, adm6996fc_writephy),
807 DEVMETHOD(miibus_statchg, adm6996fc_statchg),
808
809 /* MDIO interface */
810 DEVMETHOD(mdio_readreg, adm6996fc_readphy),
811 DEVMETHOD(mdio_writereg, adm6996fc_writephy),
812
813 /* etherswitch interface */
814 DEVMETHOD(etherswitch_lock, adm6996fc_lock),
815 DEVMETHOD(etherswitch_unlock, adm6996fc_unlock),
816 DEVMETHOD(etherswitch_getinfo, adm6996fc_getinfo),
817 DEVMETHOD(etherswitch_readreg, adm6996fc_readreg),
818 DEVMETHOD(etherswitch_writereg, adm6996fc_writereg),
819 DEVMETHOD(etherswitch_readphyreg, adm6996fc_readphy),
820 DEVMETHOD(etherswitch_writephyreg, adm6996fc_writephy),
821 DEVMETHOD(etherswitch_getport, adm6996fc_getport),
822 DEVMETHOD(etherswitch_setport, adm6996fc_setport),
823 DEVMETHOD(etherswitch_getvgroup, adm6996fc_getvgroup),
824 DEVMETHOD(etherswitch_setvgroup, adm6996fc_setvgroup),
825 DEVMETHOD(etherswitch_setconf, adm6996fc_setconf),
826 DEVMETHOD(etherswitch_getconf, adm6996fc_getconf),
827
828 DEVMETHOD_END
829 };
830
831 DEFINE_CLASS_0(adm6996fc, adm6996fc_driver, adm6996fc_methods,
832 sizeof(struct adm6996fc_softc));
833
834 DRIVER_MODULE(adm6996fc, mdio, adm6996fc_driver, 0, 0);
835 DRIVER_MODULE(miibus, adm6996fc, miibus_driver, 0, 0);
836 DRIVER_MODULE(mdio, adm6996fc, mdio_driver, 0, 0);
837 DRIVER_MODULE(etherswitch, adm6996fc, etherswitch_driver, 0, 0);
838 MODULE_VERSION(adm6996fc, 1);
839 MODULE_DEPEND(adm6996fc, miibus, 1, 1, 1); /* XXX which versions? */
840 MODULE_DEPEND(adm6996fc, etherswitch, 1, 1, 1); /* XXX which versions? */
841