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 if (sc->ifp[port] == NULL) {
177 device_printf(sc->sc_dev, "couldn't allocate ifnet structure\n");
178 err = ENOMEM;
179 break;
180 }
181
182 sc->ifp[port]->if_softc = sc;
183 sc->ifp[port]->if_flags |= IFF_UP | IFF_BROADCAST |
184 IFF_DRV_RUNNING | IFF_SIMPLEX;
185 if_initname(sc->ifp[port], name, port);
186 sc->miibus[port] = malloc(sizeof(device_t), M_ADM6996FC,
187 M_WAITOK | M_ZERO);
188 if (sc->miibus[port] == NULL) {
189 err = ENOMEM;
190 goto failed;
191 }
192 err = mii_attach(sc->sc_dev, sc->miibus[port], sc->ifp[port],
193 adm6996fc_ifmedia_upd, adm6996fc_ifmedia_sts, \
194 BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0);
195 DPRINTF(sc->sc_dev, "%s attached to pseudo interface %s\n",
196 device_get_nameunit(*sc->miibus[port]),
197 sc->ifp[port]->if_xname);
198 if (err != 0) {
199 device_printf(sc->sc_dev,
200 "attaching PHY %d failed\n",
201 phy);
202 goto failed;
203 }
204 ++port;
205 }
206 sc->info.es_nports = port;
207 if (sc->cpuport != -1) {
208 /* assume cpuport is last one */
209 sc->ifpport[sc->cpuport] = port;
210 sc->portphy[port] = sc->cpuport;
211 ++sc->info.es_nports;
212 }
213 return (0);
214
215 failed:
216 for (phy = 0; phy < sc->numports; phy++) {
217 if (((1 << phy) & sc->phymask) == 0)
218 continue;
219 port = adm6996fc_portforphy(sc, phy);
220 if (sc->miibus[port] != NULL)
221 device_delete_child(sc->sc_dev, (*sc->miibus[port]));
222 if (sc->ifp[port] != NULL)
223 if_free(sc->ifp[port]);
224 if (sc->ifname[port] != NULL)
225 free(sc->ifname[port], M_ADM6996FC);
226 if (sc->miibus[port] != NULL)
227 free(sc->miibus[port], M_ADM6996FC);
228 }
229 return (err);
230 }
231
232 static int
adm6996fc_attach(device_t dev)233 adm6996fc_attach(device_t dev)
234 {
235 struct adm6996fc_softc *sc;
236 int err;
237
238 err = 0;
239 sc = device_get_softc(dev);
240
241 sc->sc_dev = dev;
242 mtx_init(&sc->sc_mtx, "adm6996fc", NULL, MTX_DEF);
243 strlcpy(sc->info.es_name, device_get_desc(dev),
244 sizeof(sc->info.es_name));
245
246 /* ADM6996FC Defaults */
247 sc->numports = 6;
248 sc->phymask = 0x1f;
249 sc->cpuport = 5;
250 sc->media = 100;
251
252 sc->info.es_nvlangroups = 16;
253 sc->info.es_vlan_caps = ETHERSWITCH_VLAN_PORT | ETHERSWITCH_VLAN_DOT1Q;
254
255 sc->ifp = malloc(sizeof(if_t) * sc->numports, M_ADM6996FC,
256 M_WAITOK | M_ZERO);
257 sc->ifname = malloc(sizeof(char *) * sc->numports, M_ADM6996FC,
258 M_WAITOK | M_ZERO);
259 sc->miibus = malloc(sizeof(device_t *) * sc->numports, M_ADM6996FC,
260 M_WAITOK | M_ZERO);
261 sc->portphy = malloc(sizeof(int) * sc->numports, M_ADM6996FC,
262 M_WAITOK | M_ZERO);
263
264 if (sc->ifp == NULL || sc->ifname == NULL || sc->miibus == NULL ||
265 sc->portphy == NULL) {
266 err = ENOMEM;
267 goto failed;
268 }
269
270 /*
271 * Attach the PHYs and complete the bus enumeration.
272 */
273 err = adm6996fc_attach_phys(sc);
274 if (err != 0)
275 goto failed;
276
277 bus_generic_probe(dev);
278 bus_enumerate_hinted_children(dev);
279 err = bus_generic_attach(dev);
280 if (err != 0)
281 goto failed;
282
283 callout_init(&sc->callout_tick, 0);
284
285 adm6996fc_tick(sc);
286
287 return (0);
288
289 failed:
290 if (sc->portphy != NULL)
291 free(sc->portphy, M_ADM6996FC);
292 if (sc->miibus != NULL)
293 free(sc->miibus, M_ADM6996FC);
294 if (sc->ifname != NULL)
295 free(sc->ifname, M_ADM6996FC);
296 if (sc->ifp != NULL)
297 free(sc->ifp, M_ADM6996FC);
298
299 return (err);
300 }
301
302 static int
adm6996fc_detach(device_t dev)303 adm6996fc_detach(device_t dev)
304 {
305 struct adm6996fc_softc *sc;
306 int i, port;
307
308 sc = device_get_softc(dev);
309
310 callout_drain(&sc->callout_tick);
311
312 for (i = 0; i < MII_NPHY; i++) {
313 if (((1 << i) & sc->phymask) == 0)
314 continue;
315 port = adm6996fc_portforphy(sc, i);
316 if (sc->miibus[port] != NULL)
317 device_delete_child(dev, (*sc->miibus[port]));
318 if (sc->ifp[port] != NULL)
319 if_free(sc->ifp[port]);
320 free(sc->ifname[port], M_ADM6996FC);
321 free(sc->miibus[port], M_ADM6996FC);
322 }
323
324 free(sc->portphy, M_ADM6996FC);
325 free(sc->miibus, M_ADM6996FC);
326 free(sc->ifname, M_ADM6996FC);
327 free(sc->ifp, M_ADM6996FC);
328
329 bus_generic_detach(dev);
330 mtx_destroy(&sc->sc_mtx);
331
332 return (0);
333 }
334
335 /*
336 * Convert PHY number to port number.
337 */
338 static inline int
adm6996fc_portforphy(struct adm6996fc_softc * sc,int phy)339 adm6996fc_portforphy(struct adm6996fc_softc *sc, int phy)
340 {
341
342 return (sc->ifpport[phy]);
343 }
344
345 static inline struct mii_data *
adm6996fc_miiforport(struct adm6996fc_softc * sc,int port)346 adm6996fc_miiforport(struct adm6996fc_softc *sc, int port)
347 {
348
349 if (port < 0 || port > sc->numports)
350 return (NULL);
351 if (port == sc->cpuport)
352 return (NULL);
353 return (device_get_softc(*sc->miibus[port]));
354 }
355
356 static inline if_t
adm6996fc_ifpforport(struct adm6996fc_softc * sc,int port)357 adm6996fc_ifpforport(struct adm6996fc_softc *sc, int port)
358 {
359
360 if (port < 0 || port > sc->numports)
361 return (NULL);
362 return (sc->ifp[port]);
363 }
364
365 /*
366 * Poll the status for all PHYs.
367 */
368 static void
adm6996fc_miipollstat(struct adm6996fc_softc * sc)369 adm6996fc_miipollstat(struct adm6996fc_softc *sc)
370 {
371 int i, port;
372 struct mii_data *mii;
373 struct mii_softc *miisc;
374
375 ADM6996FC_LOCK_ASSERT(sc, MA_NOTOWNED);
376
377 for (i = 0; i < MII_NPHY; i++) {
378 if (((1 << i) & sc->phymask) == 0)
379 continue;
380 port = adm6996fc_portforphy(sc, i);
381 if ((*sc->miibus[port]) == NULL)
382 continue;
383 mii = device_get_softc(*sc->miibus[port]);
384 LIST_FOREACH(miisc, &mii->mii_phys, mii_list) {
385 if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) !=
386 miisc->mii_inst)
387 continue;
388 ukphy_status(miisc);
389 mii_phy_update(miisc, MII_POLLSTAT);
390 }
391 }
392 }
393
394 static void
adm6996fc_tick(void * arg)395 adm6996fc_tick(void *arg)
396 {
397 struct adm6996fc_softc *sc;
398
399 sc = arg;
400
401 adm6996fc_miipollstat(sc);
402 callout_reset(&sc->callout_tick, hz, adm6996fc_tick, sc);
403 }
404
405 static void
adm6996fc_lock(device_t dev)406 adm6996fc_lock(device_t dev)
407 {
408 struct adm6996fc_softc *sc;
409
410 sc = device_get_softc(dev);
411
412 ADM6996FC_LOCK_ASSERT(sc, MA_NOTOWNED);
413 ADM6996FC_LOCK(sc);
414 }
415
416 static void
adm6996fc_unlock(device_t dev)417 adm6996fc_unlock(device_t dev)
418 {
419 struct adm6996fc_softc *sc;
420
421 sc = device_get_softc(dev);
422
423 ADM6996FC_LOCK_ASSERT(sc, MA_OWNED);
424 ADM6996FC_UNLOCK(sc);
425 }
426
427 static etherswitch_info_t *
adm6996fc_getinfo(device_t dev)428 adm6996fc_getinfo(device_t dev)
429 {
430 struct adm6996fc_softc *sc;
431
432 sc = device_get_softc(dev);
433
434 return (&sc->info);
435 }
436
437 static int
adm6996fc_getport(device_t dev,etherswitch_port_t * p)438 adm6996fc_getport(device_t dev, etherswitch_port_t *p)
439 {
440 struct adm6996fc_softc *sc;
441 struct mii_data *mii;
442 struct ifmediareq *ifmr;
443 device_t parent;
444 int err, phy;
445 int data1, data2;
446
447 int bcaddr[6] = {0x01, 0x03, 0x05, 0x07, 0x08, 0x09};
448 int vidaddr[6] = {0x28, 0x29, 0x2a, 0x2b, 0x2b, 0x2c};
449
450 sc = device_get_softc(dev);
451 ifmr = &p->es_ifmr;
452
453 if (p->es_port < 0 || p->es_port >= sc->numports)
454 return (ENXIO);
455
456 parent = device_get_parent(dev);
457
458 if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
459 data1 = ADM6996FC_READREG(parent, bcaddr[p->es_port]);
460 data2 = ADM6996FC_READREG(parent, vidaddr[p->es_port]);
461 /* only port 4 is hi bit */
462 if (p->es_port == 4)
463 data2 = (data2 >> 8) & 0xff;
464 else
465 data2 = data2 & 0xff;
466
467 p->es_pvid = ADM6996FC_PVIDBYDATA(data1, data2);
468 if (((data1 >> ADM6996FC_OPTE_SHIFT) & 0x01) == 1)
469 p->es_flags |= ETHERSWITCH_PORT_ADDTAG;
470 } else {
471 p->es_pvid = 0;
472 }
473
474 phy = sc->portphy[p->es_port];
475 mii = adm6996fc_miiforport(sc, p->es_port);
476 if (sc->cpuport != -1 && phy == sc->cpuport) {
477 /* fill in fixed values for CPU port */
478 p->es_flags |= ETHERSWITCH_PORT_CPU;
479 ifmr->ifm_count = 0;
480 if (sc->media == 100)
481 ifmr->ifm_current = ifmr->ifm_active =
482 IFM_ETHER | IFM_100_TX | IFM_FDX;
483 else
484 ifmr->ifm_current = ifmr->ifm_active =
485 IFM_ETHER | IFM_1000_T | IFM_FDX;
486 ifmr->ifm_mask = 0;
487 ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
488 } else if (mii != NULL) {
489 err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr,
490 &mii->mii_media, SIOCGIFMEDIA);
491 if (err)
492 return (err);
493 } else {
494 return (ENXIO);
495 }
496 return (0);
497 }
498
499 static int
adm6996fc_setport(device_t dev,etherswitch_port_t * p)500 adm6996fc_setport(device_t dev, etherswitch_port_t *p)
501 {
502 struct adm6996fc_softc *sc;
503 struct ifmedia *ifm;
504 struct mii_data *mii;
505 if_t ifp;
506 device_t parent;
507 int err;
508 int data;
509
510 int bcaddr[6] = {0x01, 0x03, 0x05, 0x07, 0x08, 0x09};
511 int vidaddr[6] = {0x28, 0x29, 0x2a, 0x2b, 0x2b, 0x2c};
512
513 sc = device_get_softc(dev);
514 parent = device_get_parent(dev);
515
516 if (p->es_port < 0 || p->es_port >= sc->numports)
517 return (ENXIO);
518
519 if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
520 data = ADM6996FC_READREG(parent, bcaddr[p->es_port]);
521 data &= ~(0xf << 10);
522 data |= (p->es_pvid & 0xf) << ADM6996FC_PVID_SHIFT;
523 if (p->es_flags & ETHERSWITCH_PORT_ADDTAG)
524 data |= 1 << ADM6996FC_OPTE_SHIFT;
525 else
526 data &= ~(1 << ADM6996FC_OPTE_SHIFT);
527 ADM6996FC_WRITEREG(parent, bcaddr[p->es_port], data);
528 data = ADM6996FC_READREG(parent, vidaddr[p->es_port]);
529 /* only port 4 is hi bit */
530 if (p->es_port == 4) {
531 data &= ~(0xff << 8);
532 data = data | (((p->es_pvid >> 4) & 0xff) << 8);
533 } else {
534 data &= ~0xff;
535 data = data | ((p->es_pvid >> 4) & 0xff);
536 }
537 ADM6996FC_WRITEREG(parent, vidaddr[p->es_port], data);
538 err = 0;
539 } else {
540 if (sc->portphy[p->es_port] == sc->cpuport)
541 return (ENXIO);
542 }
543
544 if (sc->portphy[p->es_port] != sc->cpuport) {
545 mii = adm6996fc_miiforport(sc, p->es_port);
546 if (mii == NULL)
547 return (ENXIO);
548
549 ifp = adm6996fc_ifpforport(sc, p->es_port);
550
551 ifm = &mii->mii_media;
552 err = ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA);
553 }
554 return (err);
555 }
556
557 static int
adm6996fc_getvgroup(device_t dev,etherswitch_vlangroup_t * vg)558 adm6996fc_getvgroup(device_t dev, etherswitch_vlangroup_t *vg)
559 {
560 struct adm6996fc_softc *sc;
561 device_t parent;
562 int datahi, datalo;
563
564 sc = device_get_softc(dev);
565 parent = device_get_parent(dev);
566
567 if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) {
568 if (vg->es_vlangroup <= 5) {
569 vg->es_vid = ETHERSWITCH_VID_VALID;
570 vg->es_vid |= vg->es_vlangroup;
571 datalo = ADM6996FC_READREG(parent,
572 ADM6996FC_VF0L + 2 * vg->es_vlangroup);
573 datahi = ADM6996FC_READREG(parent,
574 ADM6996FC_VF0H + 2 * vg->es_vlangroup);
575
576 vg->es_member_ports = datalo & 0x3f;
577 vg->es_untagged_ports = vg->es_member_ports;
578 vg->es_fid = 0;
579 } else {
580 vg->es_vid = 0;
581 }
582 } else if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
583 datalo = ADM6996FC_READREG(parent,
584 ADM6996FC_VF0L + 2 * vg->es_vlangroup);
585 datahi = ADM6996FC_READREG(parent,
586 ADM6996FC_VF0H + 2 * vg->es_vlangroup);
587
588 if (datahi & (1 << ADM6996FC_VV_SHIFT)) {
589 vg->es_vid = ETHERSWITCH_VID_VALID;
590 vg->es_vid |= datahi & 0xfff;
591 vg->es_member_ports = datalo & 0x3f;
592 vg->es_untagged_ports = (~datalo >> 6) & 0x3f;
593 vg->es_fid = 0;
594 } else {
595 vg->es_fid = 0;
596 }
597 } else {
598 vg->es_fid = 0;
599 }
600
601 return (0);
602 }
603
604 static int
adm6996fc_setvgroup(device_t dev,etherswitch_vlangroup_t * vg)605 adm6996fc_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
606 {
607 struct adm6996fc_softc *sc;
608 device_t parent;
609
610 sc = device_get_softc(dev);
611 parent = device_get_parent(dev);
612
613 if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) {
614 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0L + 2 * vg->es_vlangroup,
615 vg->es_member_ports);
616 } else if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
617 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0L + 2 * vg->es_vlangroup,
618 vg->es_member_ports | ((~vg->es_untagged_ports & 0x3f)<< 6));
619 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0H + 2 * vg->es_vlangroup,
620 (1 << ADM6996FC_VV_SHIFT) | vg->es_vid);
621 }
622
623 return (0);
624 }
625
626 static int
adm6996fc_getconf(device_t dev,etherswitch_conf_t * conf)627 adm6996fc_getconf(device_t dev, etherswitch_conf_t *conf)
628 {
629 struct adm6996fc_softc *sc;
630
631 sc = device_get_softc(dev);
632
633 /* Return the VLAN mode. */
634 conf->cmd = ETHERSWITCH_CONF_VLAN_MODE;
635 conf->vlan_mode = sc->vlan_mode;
636
637 return (0);
638 }
639
640 static int
adm6996fc_setconf(device_t dev,etherswitch_conf_t * conf)641 adm6996fc_setconf(device_t dev, etherswitch_conf_t *conf)
642 {
643 struct adm6996fc_softc *sc;
644 device_t parent;
645 int i;
646 int data;
647 int bcaddr[6] = {0x01, 0x03, 0x05, 0x07, 0x08, 0x09};
648
649 sc = device_get_softc(dev);
650 parent = device_get_parent(dev);
651
652 if ((conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) == 0)
653 return (0);
654
655 if (conf->vlan_mode == ETHERSWITCH_VLAN_PORT) {
656 sc->vlan_mode = ETHERSWITCH_VLAN_PORT;
657 data = ADM6996FC_READREG(parent, ADM6996FC_SC3);
658 data &= ~(1 << ADM6996FC_TBV_SHIFT);
659 ADM6996FC_WRITEREG(parent, ADM6996FC_SC3, data);
660 for (i = 0;i <= 5; ++i) {
661 data = ADM6996FC_READREG(parent, bcaddr[i]);
662 data &= ~(0xf << 10);
663 data |= (i << 10);
664 ADM6996FC_WRITEREG(parent, bcaddr[i], data);
665 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0L + 2 * i,
666 0x003f);
667 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0H + 2 * i,
668 (1 << ADM6996FC_VV_SHIFT) | 1);
669 }
670 } else if (conf->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
671 sc->vlan_mode = ETHERSWITCH_VLAN_DOT1Q;
672 data = ADM6996FC_READREG(parent, ADM6996FC_SC3);
673 data |= (1 << ADM6996FC_TBV_SHIFT);
674 ADM6996FC_WRITEREG(parent, ADM6996FC_SC3, data);
675 for (i = 0;i <= 5; ++i) {
676 data = ADM6996FC_READREG(parent, bcaddr[i]);
677 /* Private VID set 1 */
678 data &= ~(0xf << 10);
679 data |= (1 << 10);
680 ADM6996FC_WRITEREG(parent, bcaddr[i], data);
681 }
682 for (i = 2;i <= 15; ++i) {
683 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0H + 2 * i,
684 0x0000);
685 }
686 } else {
687 /*
688 ADM6996FC have no VLAN off. Then set Port base and
689 add all port to member. Use VLAN Filter 1 is reset
690 default.
691 */
692 sc->vlan_mode = 0;
693 data = ADM6996FC_READREG(parent, ADM6996FC_SC3);
694 data &= ~(1 << ADM6996FC_TBV_SHIFT);
695 ADM6996FC_WRITEREG(parent, ADM6996FC_SC3, data);
696 for (i = 0;i <= 5; ++i) {
697 data = ADM6996FC_READREG(parent, bcaddr[i]);
698 data &= ~(0xf << 10);
699 data |= (1 << 10);
700 if (i == 5)
701 data &= ~(1 << 4);
702 ADM6996FC_WRITEREG(parent, bcaddr[i], data);
703 }
704 /* default setting */
705 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0L + 2, 0x003f);
706 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0H + 2,
707 (1 << ADM6996FC_VV_SHIFT) | 1);
708 }
709
710
711 return (0);
712 }
713
714 static void
adm6996fc_statchg(device_t dev)715 adm6996fc_statchg(device_t dev)
716 {
717
718 DPRINTF(dev, "%s\n", __func__);
719 }
720
721 static int
adm6996fc_ifmedia_upd(if_t ifp)722 adm6996fc_ifmedia_upd(if_t ifp)
723 {
724 struct adm6996fc_softc *sc;
725 struct mii_data *mii;
726
727 sc = if_getsoftc(ifp);
728 mii = adm6996fc_miiforport(sc, if_getdunit(ifp));
729
730 DPRINTF(sc->sc_dev, "%s\n", __func__);
731 if (mii == NULL)
732 return (ENXIO);
733 mii_mediachg(mii);
734 return (0);
735 }
736
737 static void
adm6996fc_ifmedia_sts(if_t ifp,struct ifmediareq * ifmr)738 adm6996fc_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr)
739 {
740 struct adm6996fc_softc *sc;
741 struct mii_data *mii;
742
743 sc = if_getsoftc(ifp);
744 mii = adm6996fc_miiforport(sc, if_getdunit(ifp));
745
746 DPRINTF(sc->sc_dev, "%s\n", __func__);
747
748 if (mii == NULL)
749 return;
750 mii_pollstat(mii);
751 ifmr->ifm_active = mii->mii_media_active;
752 ifmr->ifm_status = mii->mii_media_status;
753 }
754
755 static int
adm6996fc_readphy(device_t dev,int phy,int reg)756 adm6996fc_readphy(device_t dev, int phy, int reg)
757 {
758 struct adm6996fc_softc *sc;
759 int data;
760
761 sc = device_get_softc(dev);
762 ADM6996FC_LOCK_ASSERT(sc, MA_NOTOWNED);
763
764 if (phy < 0 || phy >= 32)
765 return (ENXIO);
766 if (reg < 0 || reg >= 32)
767 return (ENXIO);
768
769 ADM6996FC_LOCK(sc);
770 data = ADM6996FC_READREG(device_get_parent(dev),
771 (ADM6996FC_PHY_C0 + ADM6996FC_PHY_SIZE * phy) + reg);
772 ADM6996FC_UNLOCK(sc);
773
774 return (data);
775 }
776
777 static int
adm6996fc_writephy(device_t dev,int phy,int reg,int data)778 adm6996fc_writephy(device_t dev, int phy, int reg, int data)
779 {
780 struct adm6996fc_softc *sc;
781 int err;
782
783 sc = device_get_softc(dev);
784 ADM6996FC_LOCK_ASSERT(sc, MA_NOTOWNED);
785
786 if (phy < 0 || phy >= 32)
787 return (ENXIO);
788 if (reg < 0 || reg >= 32)
789 return (ENXIO);
790
791 ADM6996FC_LOCK(sc);
792 err = ADM6996FC_WRITEREG(device_get_parent(dev),
793 (ADM6996FC_PHY_C0 + ADM6996FC_PHY_SIZE * phy) + reg, data);
794 ADM6996FC_UNLOCK(sc);
795
796 return (err);
797 }
798
799 static int
adm6996fc_readreg(device_t dev,int addr)800 adm6996fc_readreg(device_t dev, int addr)
801 {
802
803 return ADM6996FC_READREG(device_get_parent(dev), addr);
804 }
805
806 static int
adm6996fc_writereg(device_t dev,int addr,int value)807 adm6996fc_writereg(device_t dev, int addr, int value)
808 {
809 int err;
810
811 err = ADM6996FC_WRITEREG(device_get_parent(dev), addr, value);
812 return (err);
813 }
814
815 static device_method_t adm6996fc_methods[] = {
816 /* Device interface */
817 DEVMETHOD(device_probe, adm6996fc_probe),
818 DEVMETHOD(device_attach, adm6996fc_attach),
819 DEVMETHOD(device_detach, adm6996fc_detach),
820
821 /* bus interface */
822 DEVMETHOD(bus_add_child, device_add_child_ordered),
823
824 /* MII interface */
825 DEVMETHOD(miibus_readreg, adm6996fc_readphy),
826 DEVMETHOD(miibus_writereg, adm6996fc_writephy),
827 DEVMETHOD(miibus_statchg, adm6996fc_statchg),
828
829 /* MDIO interface */
830 DEVMETHOD(mdio_readreg, adm6996fc_readphy),
831 DEVMETHOD(mdio_writereg, adm6996fc_writephy),
832
833 /* etherswitch interface */
834 DEVMETHOD(etherswitch_lock, adm6996fc_lock),
835 DEVMETHOD(etherswitch_unlock, adm6996fc_unlock),
836 DEVMETHOD(etherswitch_getinfo, adm6996fc_getinfo),
837 DEVMETHOD(etherswitch_readreg, adm6996fc_readreg),
838 DEVMETHOD(etherswitch_writereg, adm6996fc_writereg),
839 DEVMETHOD(etherswitch_readphyreg, adm6996fc_readphy),
840 DEVMETHOD(etherswitch_writephyreg, adm6996fc_writephy),
841 DEVMETHOD(etherswitch_getport, adm6996fc_getport),
842 DEVMETHOD(etherswitch_setport, adm6996fc_setport),
843 DEVMETHOD(etherswitch_getvgroup, adm6996fc_getvgroup),
844 DEVMETHOD(etherswitch_setvgroup, adm6996fc_setvgroup),
845 DEVMETHOD(etherswitch_setconf, adm6996fc_setconf),
846 DEVMETHOD(etherswitch_getconf, adm6996fc_getconf),
847
848 DEVMETHOD_END
849 };
850
851 DEFINE_CLASS_0(adm6996fc, adm6996fc_driver, adm6996fc_methods,
852 sizeof(struct adm6996fc_softc));
853
854 DRIVER_MODULE(adm6996fc, mdio, adm6996fc_driver, 0, 0);
855 DRIVER_MODULE(miibus, adm6996fc, miibus_driver, 0, 0);
856 DRIVER_MODULE(mdio, adm6996fc, mdio_driver, 0, 0);
857 DRIVER_MODULE(etherswitch, adm6996fc, etherswitch_driver, 0, 0);
858 MODULE_VERSION(adm6996fc, 1);
859 MODULE_DEPEND(adm6996fc, miibus, 1, 1, 1); /* XXX which versions? */
860 MODULE_DEPEND(adm6996fc, etherswitch, 1, 1, 1); /* XXX which versions? */
861