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