]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/etherswitch/ar40xx/ar40xx_main.c
Remove unused miibus_devclass and miibus_fdt_devclass.
[FreeBSD/FreeBSD.git] / sys / dev / etherswitch / ar40xx / ar40xx_main.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2022 Adrian Chadd <adrian@FreeBSD.org>.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 #include <sys/param.h>
29 #include <sys/bus.h>
30 #include <sys/errno.h>
31 #include <sys/kernel.h>
32 #include <sys/malloc.h>
33 #include <sys/module.h>
34 #include <sys/socket.h>
35 #include <sys/sockio.h>
36 #include <sys/sysctl.h>
37 #include <sys/systm.h>
38
39 #include <net/if.h>
40 #include <net/if_var.h>
41 #include <net/if_arp.h>
42 #include <net/ethernet.h>
43 #include <net/if_dl.h>
44 #include <net/if_media.h>
45 #include <net/if_types.h>
46
47 #include <machine/bus.h>
48 #include <dev/iicbus/iic.h>
49 #include <dev/iicbus/iiconf.h>
50 #include <dev/iicbus/iicbus.h>
51 #include <dev/mii/mii.h>
52 #include <dev/mii/miivar.h>
53 #include <dev/mdio/mdio.h>
54 #include <dev/extres/clk/clk.h>
55 #include <dev/extres/hwreset/hwreset.h>
56
57 #include <dev/fdt/fdt_common.h>
58 #include <dev/ofw/ofw_bus.h>
59 #include <dev/ofw/ofw_bus_subr.h>
60
61 #include <dev/etherswitch/etherswitch.h>
62
63 #include <dev/etherswitch/ar40xx/ar40xx_var.h>
64 #include <dev/etherswitch/ar40xx/ar40xx_reg.h>
65 #include <dev/etherswitch/ar40xx/ar40xx_phy.h>
66 #include <dev/etherswitch/ar40xx/ar40xx_debug.h>
67 #include <dev/etherswitch/ar40xx/ar40xx_hw.h>
68 #include <dev/etherswitch/ar40xx/ar40xx_hw_psgmii.h>
69 #include <dev/etherswitch/ar40xx/ar40xx_hw_port.h>
70 #include <dev/etherswitch/ar40xx/ar40xx_hw_mib.h>
71 #include <dev/etherswitch/ar40xx/ar40xx_hw_vtu.h>
72 #include <dev/etherswitch/ar40xx/ar40xx_hw_atu.h>
73
74 #include "mdio_if.h"
75 #include "miibus_if.h"
76 #include "etherswitch_if.h"
77
78 static struct ofw_compat_data compat_data[] = {
79         { "qcom,ess-switch",            1 },
80         { NULL,                         0 },
81 };
82
83 static int
84 ar40xx_probe(device_t dev)
85 {
86
87         if (! ofw_bus_status_okay(dev))
88                 return (ENXIO);
89
90         if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
91                 return (ENXIO);
92
93         device_set_desc(dev, "IPQ4018 ESS Switch fabric / PSGMII PHY");
94         return (BUS_PROBE_DEFAULT);
95 }
96
97 static void
98 ar40xx_tick(void *arg)
99 {
100         struct ar40xx_softc *sc = arg;
101
102         (void) ar40xx_phy_tick(sc);
103         callout_reset(&sc->sc_phy_callout, hz, ar40xx_tick, sc);
104 }
105
106 static void
107 ar40xx_statchg(device_t dev)
108 {
109         struct ar40xx_softc *sc = device_get_softc(dev);
110
111         AR40XX_DPRINTF(sc, AR40XX_DBG_PORT_STATUS, "%s\n", __func__);
112 }
113
114 static int
115 ar40xx_readphy(device_t dev, int phy, int reg)
116 {
117         struct ar40xx_softc *sc = device_get_softc(dev);
118
119         return MDIO_READREG(sc->sc_mdio_dev, phy, reg);
120 }
121
122 static int
123 ar40xx_writephy(device_t dev, int phy, int reg, int val)
124 {
125         struct ar40xx_softc *sc = device_get_softc(dev);
126
127         return MDIO_WRITEREG(sc->sc_mdio_dev, phy, reg, val);
128 }
129
130 /*
131  * Do the initial switch configuration.
132  */
133 static int
134 ar40xx_reset_switch(struct ar40xx_softc *sc)
135 {
136         int ret, i;
137
138         AR40XX_DPRINTF(sc, AR40XX_DBG_HW_INIT, "%s: called\n", __func__);
139
140         /* blank the VLAN config */
141         memset(&sc->sc_vlan, 0, sizeof(sc->sc_vlan));
142
143         /* initial vlan port mapping */
144         for (i = 0; i < AR40XX_NUM_VTU_ENTRIES; i++)
145                 sc->sc_vlan.vlan_id[i] = 0;
146
147         /* init vlan config */
148         ret = ar40xx_hw_vlan_init(sc);
149
150         /* init monitor config */
151         sc->sc_monitor.mirror_tx = false;
152         sc->sc_monitor.mirror_rx = false;
153         sc->sc_monitor.source_port = 0;
154         sc->sc_monitor.monitor_port = 0;
155
156         /* apply switch config */
157         ret = ar40xx_hw_sw_hw_apply(sc);
158
159         return (ret);
160 }
161
162 static int
163 ar40xx_sysctl_dump_port_state(SYSCTL_HANDLER_ARGS)
164 {
165         struct ar40xx_softc *sc = arg1;
166         int val = 0;
167         int error;
168         int i;
169
170         (void) i; (void) sc;
171
172         error = sysctl_handle_int(oidp, &val, 0, req);
173         if (error || !req->newptr)
174                 return (error);
175
176         if (val < 0 || val > 5) {
177                 return (EINVAL);
178         }
179
180         AR40XX_LOCK(sc);
181
182         device_printf(sc->sc_dev, "port %d: PORT_STATUS=0x%08x\n", val,
183             AR40XX_REG_READ(sc, AR40XX_REG_PORT_STATUS(val)));
184         device_printf(sc->sc_dev, "port %d: PORT_HEADER=0x%08x\n", val,
185             AR40XX_REG_READ(sc, AR40XX_REG_PORT_HEADER(val)));
186         device_printf(sc->sc_dev, "port %d: PORT_VLAN0=0x%08x\n", val,
187             AR40XX_REG_READ(sc, AR40XX_REG_PORT_VLAN0(val)));
188         device_printf(sc->sc_dev, "port %d: PORT_VLAN1=0x%08x\n", val,
189             AR40XX_REG_READ(sc, AR40XX_REG_PORT_VLAN1(val)));
190         device_printf(sc->sc_dev, "port %d: PORT_LOOKUP=0x%08x\n", val,
191             AR40XX_REG_READ(sc, AR40XX_REG_PORT_LOOKUP(val)));
192         device_printf(sc->sc_dev, "port %d: PORT_HOL_CTRL1=0x%08x\n", val,
193             AR40XX_REG_READ(sc, AR40XX_REG_PORT_HOL_CTRL1(val)));
194         device_printf(sc->sc_dev, "port %d: PORT_FLOWCTRL_THRESH=0x%08x\n",
195             val, AR40XX_REG_READ(sc, AR40XX_REG_PORT_FLOWCTRL_THRESH(val)));
196
197         AR40XX_UNLOCK(sc);
198
199         return (0);
200 }
201
202 static int
203 ar40xx_sysctl_dump_port_mibstats(SYSCTL_HANDLER_ARGS)
204 {
205         struct ar40xx_softc *sc = arg1;
206         int val = 0;
207         int error;
208         int i;
209
210         (void) i; (void) sc;
211
212         error = sysctl_handle_int(oidp, &val, 0, req);
213         if (error || !req->newptr)
214                 return (error);
215
216         if (val < 0 || val > 5) {
217                 return (EINVAL);
218         }
219
220         AR40XX_LOCK(sc);
221
222         /* Yes, this snapshots all ports */
223         (void) ar40xx_hw_mib_capture(sc);
224         (void) ar40xx_hw_mib_fetch(sc, val);
225
226         AR40XX_UNLOCK(sc);
227
228         return (0);
229 }
230
231
232 static int
233 ar40xx_sysctl_attach(struct ar40xx_softc *sc)
234 {
235         struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
236         struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
237
238         SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
239             "debug", CTLFLAG_RW, &sc->sc_debug, 0,
240             "debugging flags");
241
242         SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
243             "port_state", CTLTYPE_INT | CTLFLAG_RW, sc,
244             0, ar40xx_sysctl_dump_port_state, "I", "");
245
246         SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
247             "port_mibstats", CTLTYPE_INT | CTLFLAG_RW, sc,
248             0, ar40xx_sysctl_dump_port_mibstats, "I", "");
249
250         return (0);
251 }
252
253 static int
254 ar40xx_detach(device_t dev)
255 {
256         struct ar40xx_softc *sc = device_get_softc(dev);
257         int i;
258
259         device_printf(sc->sc_dev, "%s: called\n", __func__);
260
261         callout_drain(&sc->sc_phy_callout);
262
263         /* Free PHYs */
264         for (i = 0; i < AR40XX_NUM_PHYS; i++) {
265                 if (sc->sc_phys.miibus[i] != NULL)
266                         device_delete_child(dev, sc->sc_phys.miibus[i]);
267                 if (sc->sc_phys.ifp[i] != NULL)
268                         if_free(sc->sc_phys.ifp[i]);
269                 free(sc->sc_phys.ifname[i], M_DEVBUF);
270         }
271
272         bus_generic_detach(dev);
273         mtx_destroy(&sc->sc_mtx);
274
275         return (0);
276 }
277
278 static int
279 ar40xx_attach(device_t dev)
280 {
281         struct ar40xx_softc *sc = device_get_softc(dev);
282         phandle_t psgmii_p, root_p, mdio_p;
283         int ret, i;
284
285         sc->sc_dev = dev;
286         mtx_init(&sc->sc_mtx, "ar40xx_switch", NULL, MTX_DEF);
287
288         psgmii_p = OF_finddevice("/soc/ess-psgmii");
289         if (psgmii_p == -1) {
290                 device_printf(dev,
291                     "%s: couldn't find /soc/ess-psgmii DT node\n",
292                     __func__);
293                 goto error;
294         }
295
296         /*
297          * Get the ipq4019-mdio node here, to talk to our local PHYs
298          * if needed
299          */
300         root_p = OF_finddevice("/soc");
301         mdio_p = ofw_bus_find_compatible(root_p, "qcom,ipq4019-mdio");
302         if (mdio_p == -1) {
303                 device_printf(dev, "%s: couldn't find ipq4019-mdio DT node\n",
304                     __func__);
305                 goto error;
306         }
307         sc->sc_mdio_phandle = mdio_p;
308         sc->sc_mdio_dev = OF_device_from_xref(OF_xref_from_node(mdio_p));
309         if (sc->sc_mdio_dev == NULL) {
310                 device_printf(dev,
311                     "%s: couldn't get mdio device (mdio_p=%u)\n",
312                     __func__, mdio_p);
313                 goto error;
314         }
315
316         /* get psgmii base address from psgmii node */
317         ret = OF_decode_addr(psgmii_p, 0, &sc->sc_psgmii_mem_tag,
318             &sc->sc_psgmii_mem_handle,
319             &sc->sc_psgmii_mem_size);
320         if (ret != 0) {
321                 device_printf(dev, "%s: couldn't map psgmii mem (%d)\n",
322                     __func__, ret);
323                 goto error;
324         }
325
326         /* get switch base address */
327         sc->sc_ess_mem_rid = 0;
328         sc->sc_ess_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
329             &sc->sc_ess_mem_rid, RF_ACTIVE);
330         if (sc->sc_ess_mem_res == NULL) {
331                 device_printf(dev, "%s: failed to find memory resource\n",
332                     __func__);
333                 goto error;
334         }
335         sc->sc_ess_mem_size = (size_t) bus_get_resource_count(dev,
336             SYS_RES_MEMORY, sc->sc_ess_mem_rid);
337         if (sc->sc_ess_mem_size == 0) {
338                 device_printf(dev, "%s: failed to get device memory size\n",
339                     __func__);
340                 goto error;
341         }
342
343         ret = OF_getencprop(ofw_bus_get_node(dev), "switch_mac_mode",
344             &sc->sc_config.switch_mac_mode,
345             sizeof(sc->sc_config.switch_mac_mode));
346         if (ret < 0) {
347                 device_printf(dev, "%s: missing switch_mac_mode property\n",
348                     __func__);
349                 goto error;
350         }
351
352         ret = OF_getencprop(ofw_bus_get_node(dev), "switch_cpu_bmp",
353             &sc->sc_config.switch_cpu_bmp,
354             sizeof(sc->sc_config.switch_cpu_bmp));
355         if (ret < 0) {
356                 device_printf(dev, "%s: missing switch_cpu_bmp property\n",
357                     __func__);
358                 goto error;
359         }
360
361         ret = OF_getencprop(ofw_bus_get_node(dev), "switch_lan_bmp",
362             &sc->sc_config.switch_lan_bmp,
363             sizeof(sc->sc_config.switch_lan_bmp));
364         if (ret < 0) {
365                 device_printf(dev, "%s: missing switch_lan_bmp property\n",
366                     __func__);
367                 goto error;
368         }
369
370         ret = OF_getencprop(ofw_bus_get_node(dev), "switch_wan_bmp",
371             &sc->sc_config.switch_wan_bmp,
372             sizeof(sc->sc_config.switch_wan_bmp));
373         if (ret < 0) {
374                 device_printf(dev, "%s: missing switch_wan_bmp property\n",
375                     __func__);
376                 goto error;
377         }
378
379         ret = clk_get_by_ofw_name(dev, 0, "ess_clk", &sc->sc_ess_clk);
380         if (ret != 0) {
381                 device_printf(dev, "%s: failed to find ess_clk (%d)\n",
382                     __func__, ret);
383                 goto error;
384         }
385         ret = clk_enable(sc->sc_ess_clk);
386         if (ret != 0) {
387                 device_printf(dev, "%s: failed to enable clock (%d)\n",
388                     __func__, ret);
389                 goto error;
390         }
391
392         ret = hwreset_get_by_ofw_name(dev, 0, "ess_rst", &sc->sc_ess_rst);
393         if (ret != 0) {
394                 device_printf(dev, "%s: failed to find ess_rst (%d)\n",
395                     __func__, ret);
396                 goto error;
397         }
398
399         /*
400          * Ok, at this point we have enough resources to do an initial
401          * reset and configuration.
402          */
403
404         AR40XX_LOCK(sc);
405
406         /* Initial PSGMII/RGMII port configuration */
407         ret = ar40xx_hw_psgmii_init_config(sc);
408         if (ret != 0) {
409                 device_printf(sc->sc_dev,
410                     "ERROR: failed to init PSGMII (%d)\n", ret);
411                 goto error_locked;
412         }
413
414         /*
415          * ESS reset - this resets both the ethernet switch
416          * AND the ethernet block.
417          */
418         ret = ar40xx_hw_ess_reset(sc);
419         if (ret != 0) {
420                 device_printf(sc->sc_dev,
421                     "ERROR: failed to reset ESS block (%d)\n", ret);
422                 goto error_locked;
423         }
424
425         /*
426          * Check the PHY IDs for each of the PHYs from 0..4;
427          * this is useful to make sure that we can SEE the external
428          * PHY(s).
429          */
430         if (bootverbose) {
431                 ret = ar40xx_hw_phy_get_ids(sc);
432                 if (ret != 0) {
433                         device_printf(sc->sc_dev,
434                             "ERROR: failed to check PHY IDs (%d)\n", ret);
435                         goto error_locked;
436                 }
437         }
438
439         /*
440          * Do PSGMII PHY self-test; work-around issues.
441          */
442         ret = ar40xx_hw_psgmii_self_test(sc);
443         if (ret != 0) {
444                 device_printf(sc->sc_dev,
445                     "ERROR: failed to do PSGMII self-test (%d)\n", ret);
446                 goto error_locked;
447         }
448
449         /* Return port config to runtime state */
450         ret = ar40xx_hw_psgmii_self_test_clean(sc);
451         if (ret != 0) {
452                 device_printf(sc->sc_dev,
453                     "ERROR: failed to do PSGMII runtime config (%d)\n", ret);
454                 goto error_locked;
455         }
456
457         /* mac_mode_init */
458         ret = ar40xx_hw_psgmii_set_mac_mode(sc,
459             sc->sc_config.switch_mac_mode);
460
461         /* Initialise each hardware port */
462         for (i = 0; i < AR40XX_NUM_PORTS; i++) {
463                 ret = ar40xx_hw_port_init(sc, i);
464         }
465
466         /* initialise the global switch configuration */
467         ret = ar40xx_hw_init_globals(sc);
468
469         /* reset the switch vlan/port learning config */
470         ret = ar40xx_reset_switch(sc);
471
472         /* cpuport setup */
473         ret = ar40xx_hw_port_cpuport_setup(sc);
474
475         AR40XX_UNLOCK(sc);
476
477 #if 0
478         /* We may end up needing the QM workaround code here.. */
479         device_printf(dev, "%s: TODO: QM error check\n", __func__);
480 #endif
481
482         /* Attach PHYs */
483         ret = ar40xx_attach_phys(sc);
484
485         ret = bus_generic_probe(dev);
486         bus_enumerate_hinted_children(dev);
487         ret = bus_generic_attach(dev);
488
489         /* Start timer */
490         callout_init_mtx(&sc->sc_phy_callout, &sc->sc_mtx, 0);
491
492         /*
493          * Setup the etherswitch info block.
494          */
495         strlcpy(sc->sc_info.es_name, device_get_desc(dev),
496             sizeof(sc->sc_info.es_name));
497         sc->sc_info.es_nports = AR40XX_NUM_PORTS;
498         sc->sc_info.es_vlan_caps = ETHERSWITCH_VLAN_DOT1Q;
499         /* XXX TODO: double-tag / 802.1ad */
500         sc->sc_info.es_nvlangroups = AR40XX_NUM_VTU_ENTRIES;
501
502         /*
503          * Fetch the initial port configuration.
504          */
505         AR40XX_LOCK(sc);
506         ar40xx_tick(sc);
507         AR40XX_UNLOCK(sc);
508
509         ar40xx_sysctl_attach(sc);
510
511         return (0);
512 error_locked:
513         AR40XX_UNLOCK(sc);
514 error:
515         ar40xx_detach(dev);
516         return (ENXIO);
517 }
518
519 static void
520 ar40xx_lock(device_t dev)
521 {
522         struct ar40xx_softc *sc = device_get_softc(dev);
523
524         AR40XX_LOCK(sc);
525 }
526
527 static void
528 ar40xx_unlock(device_t dev)
529 {
530         struct ar40xx_softc *sc = device_get_softc(dev);
531
532         AR40XX_LOCK_ASSERT(sc);
533         AR40XX_UNLOCK(sc);
534 }
535
536 static etherswitch_info_t *
537 ar40xx_getinfo(device_t dev)
538 {
539         struct ar40xx_softc *sc = device_get_softc(dev);
540
541         return (&sc->sc_info);
542 }
543
544 static int
545 ar40xx_readreg(device_t dev, int addr)
546 {
547         struct ar40xx_softc *sc = device_get_softc(dev);
548
549         if (addr >= sc->sc_ess_mem_size - 1)
550                 return (-1);
551
552         AR40XX_REG_BARRIER_READ(sc);
553
554         return AR40XX_REG_READ(sc, addr);
555 }
556
557 static int
558 ar40xx_writereg(device_t dev, int addr, int value)
559 {
560         struct ar40xx_softc *sc = device_get_softc(dev);
561
562         if (addr >= sc->sc_ess_mem_size - 1)
563                 return (-1);
564
565         AR40XX_REG_WRITE(sc, addr, value);
566         AR40XX_REG_BARRIER_WRITE(sc);
567         return (0);
568 }
569
570 /*
571  * Get the port configuration and status.
572  */
573 static int
574 ar40xx_getport(device_t dev, etherswitch_port_t *p)
575 {
576         struct ar40xx_softc *sc = device_get_softc(dev);
577         struct mii_data *mii = NULL;
578         struct ifmediareq *ifmr;
579         int err;
580
581         if (p->es_port < 0 || p->es_port > sc->sc_info.es_nports)
582                 return (ENXIO);
583
584         AR40XX_LOCK(sc);
585         /* Fetch the current VLAN configuration for this port */
586         /* PVID */
587         ar40xx_hw_get_port_pvid(sc, p->es_port, &p->es_pvid);
588
589         /*
590          * The VLAN egress aren't appropriate to the ports;
591          * instead it's part of the VLAN group config.
592          */
593
594         /* Get MII config */
595         mii = ar40xx_phy_miiforport(sc, p->es_port);
596
597         AR40XX_UNLOCK(sc);
598
599         if (p->es_port == 0) {
600                 /* CPU port */
601                 p->es_flags |= ETHERSWITCH_PORT_CPU;
602                 ifmr = &p->es_ifmr;
603                 ifmr->ifm_count = 0;
604                 ifmr->ifm_current = ifmr->ifm_active =
605                      IFM_ETHER | IFM_1000_T | IFM_FDX;
606                 ifmr->ifm_mask = 0;
607                 ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
608         } else if (mii != NULL) {
609                 /* non-CPU port */
610                 err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr,
611                     &mii->mii_media, SIOCGIFMEDIA);
612                 if (err)
613                         return (err);
614         } else {
615                 return (ENXIO);
616         }
617
618         return (0);
619 }
620
621 /*
622  * Set the port configuration and status.
623  */
624 static int
625 ar40xx_setport(device_t dev, etherswitch_port_t *p)
626 {
627         struct ar40xx_softc *sc = device_get_softc(dev);
628         struct ifmedia *ifm;
629         struct mii_data *mii;
630         struct ifnet *ifp;
631         int ret;
632
633         if (p->es_port < 0 || p->es_port > sc->sc_info.es_nports)
634                 return (EINVAL);
635
636         /* Port flags */
637         AR40XX_LOCK(sc);
638         ret = ar40xx_hw_set_port_pvid(sc, p->es_port, p->es_pvid);
639         if (ret != 0) {
640                 AR40XX_UNLOCK(sc);
641                 return (ret);
642         }
643         /* XXX TODO: tag strip/unstrip, double-tag, etc */
644         AR40XX_UNLOCK(sc);
645
646         /* Don't change media config on CPU port */
647         if (p->es_port == 0)
648                 return (0);
649
650         mii = ar40xx_phy_miiforport(sc, p->es_port);
651         if (mii == NULL)
652                 return (ENXIO);
653
654         ifp = ar40xx_phy_ifpforport(sc, p->es_port);
655
656         ifm = &mii->mii_media;
657         return (ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA));
658
659         return (0);
660 }
661
662 /*
663  * Get the current VLAN group (per-port, ISL, dot1q) configuration.
664  *
665  * For now the only supported operating mode is dot1q.
666  */
667 static int
668 ar40xx_getvgroup(device_t dev, etherswitch_vlangroup_t *vg)
669 {
670         struct ar40xx_softc *sc = device_get_softc(dev);
671         int vid, ret;
672
673         if (vg->es_vlangroup > sc->sc_info.es_nvlangroups)
674                 return (EINVAL);
675
676         vg->es_untagged_ports = 0;
677         vg->es_member_ports = 0;
678         vg->es_fid = 0;
679
680         AR40XX_LOCK(sc);
681
682         /* Note: only supporting 802.1q VLAN config for now */
683         if (sc->sc_vlan.vlan != 1) {
684                 vg->es_member_ports = 0;
685                 vg->es_untagged_ports = 0;
686                 AR40XX_UNLOCK(sc);
687                 return (-1);
688         }
689
690         /* Get vlangroup mapping to VLAN id */
691         vid = sc->sc_vlan.vlan_id[vg->es_vlangroup];
692         if ((vid & ETHERSWITCH_VID_VALID) == 0) {
693                 /* Not an active vgroup; bail */
694                 AR40XX_UNLOCK(sc);
695                 return (0);
696         }
697         vg->es_vid = vid;
698
699         ret = ar40xx_hw_vtu_get_vlan(sc, vid, &vg->es_member_ports,
700             &vg->es_untagged_ports);
701
702         AR40XX_UNLOCK(sc);
703
704         if (ret == 0) {
705                 vg->es_vid |= ETHERSWITCH_VID_VALID;
706         }
707
708         return (ret);
709 }
710
711 /*
712  * Set the current VLAN group (per-port, ISL, dot1q) configuration.
713  *
714  * For now the only supported operating mode is dot1q.
715  */
716 static int
717 ar40xx_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
718 {
719         struct ar40xx_softc *sc = device_get_softc(dev);
720         int err, vid;
721
722         /* For now we only support 802.1q mode */
723         if (sc->sc_vlan.vlan == 0)
724                 return (EINVAL);
725
726         AR40XX_LOCK(sc);
727         vid = sc->sc_vlan.vlan_id[vg->es_vlangroup];
728         /*
729          * If we have an 802.1q VID and it's different to the current one,
730          * purge the current VTU entry.
731          */
732         if ((vid != 0) &&
733             ((vid & ETHERSWITCH_VID_VALID) != 0) &&
734             ((vid & ETHERSWITCH_VID_MASK) !=
735              (vg->es_vid & ETHERSWITCH_VID_MASK))) {
736                 AR40XX_DPRINTF(sc, AR40XX_DBG_VTU_OP,
737                     "%s: purging VID %d first\n", __func__, vid);
738                 err = ar40xx_hw_vtu_flush(sc);
739                 if (err != 0) {
740                         AR40XX_UNLOCK(sc);
741                         return (err);
742                 }
743         }
744
745         /* Update VLAN ID */
746         vid = vg->es_vid & ETHERSWITCH_VID_MASK;
747         sc->sc_vlan.vlan_id[vg->es_vlangroup] = vid;
748         if (vid == 0) {
749                 /* Setting it to 0 disables the group */
750                 AR40XX_UNLOCK(sc);
751                 return (0);
752         }
753         /* Add valid bit for this entry */
754         sc->sc_vlan.vlan_id[vg->es_vlangroup] = vid | ETHERSWITCH_VID_VALID;
755
756         /* Update hardware */
757         err = ar40xx_hw_vtu_load_vlan(sc, vid, vg->es_member_ports,
758             vg->es_untagged_ports);
759         if (err != 0) {
760                 AR40XX_UNLOCK(sc);
761                 return (err);
762         }
763
764         /* Update the config for the given entry */
765         sc->sc_vlan.vlan_ports[vg->es_vlangroup] = vg->es_member_ports;
766         sc->sc_vlan.vlan_untagged[vg->es_vlangroup] = vg->es_untagged_ports;
767
768         AR40XX_UNLOCK(sc);
769
770         return (0);
771 }
772
773 /*
774  * Get the current configuration mode.
775  */
776 static int
777 ar40xx_getconf(device_t dev, etherswitch_conf_t *conf)
778 {
779         struct ar40xx_softc *sc = device_get_softc(dev);
780         int ret;
781
782         AR40XX_LOCK(sc);
783
784         /* Only support dot1q VLAN for now */
785         conf->cmd = ETHERSWITCH_CONF_VLAN_MODE;
786         conf->vlan_mode = ETHERSWITCH_VLAN_DOT1Q;
787
788         /* Switch MAC address */
789         ret = ar40xx_hw_read_switch_mac_address(sc, &conf->switch_macaddr);
790         if (ret == 0)
791                 conf->cmd |= ETHERSWITCH_CONF_SWITCH_MACADDR;
792
793         AR40XX_UNLOCK(sc);
794
795         return (0);
796 }
797
798 /*
799  * Set the current configuration and do a switch reset.
800  *
801  * For now the only supported operating mode is dot1q, don't
802  * allow it to be set to non-dot1q.
803  */
804 static int
805 ar40xx_setconf(device_t dev, etherswitch_conf_t *conf)
806 {
807         struct ar40xx_softc *sc = device_get_softc(dev);
808         int ret = 0;
809
810         if (conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) {
811                 /* Only support dot1q VLAN for now */
812                 if (conf->vlan_mode != ETHERSWITCH_VLAN_DOT1Q)
813                         return (EINVAL);
814         }
815
816         if (conf->cmd & ETHERSWITCH_CONF_SWITCH_MACADDR) {
817                 AR40XX_LOCK(sc);
818                 ret = ar40xx_hw_read_switch_mac_address(sc,
819                     &conf->switch_macaddr);
820                 AR40XX_UNLOCK(sc);
821         }
822
823         return (ret);
824 }
825
826 /*
827  * Flush all ATU entries.
828  */
829 static int
830 ar40xx_atu_flush_all(device_t dev)
831 {
832         struct ar40xx_softc *sc = device_get_softc(dev);
833         int ret;
834
835         AR40XX_LOCK(sc);
836         ret = ar40xx_hw_atu_flush_all(sc);
837         AR40XX_UNLOCK(sc);
838         return (ret);
839 }
840
841 /*
842  * Flush all ATU entries for the given port.
843  */
844 static int
845 ar40xx_atu_flush_port(device_t dev, int port)
846 {
847         struct ar40xx_softc *sc = device_get_softc(dev);
848         int ret;
849
850         AR40XX_LOCK(sc);
851         ret = ar40xx_hw_atu_flush_port(sc, port);
852         AR40XX_UNLOCK(sc);
853         return (ret);
854 }
855
856 /*
857  * Load the ATU table into local storage so it can be iterated
858  * over.
859  */
860 static int
861 ar40xx_atu_fetch_table(device_t dev, etherswitch_atu_table_t *table)
862 {
863         struct ar40xx_softc *sc = device_get_softc(dev);
864         int err, nitems;
865
866         memset(&sc->atu.entries, 0, sizeof(sc->atu.entries));
867
868         table->es_nitems = 0;
869         nitems = 0;
870
871         AR40XX_LOCK(sc);
872         sc->atu.count = 0;
873         err = ar40xx_hw_atu_fetch_entry(sc, NULL, 0);
874         if (err != 0)
875                 goto done;
876
877         while (nitems < AR40XX_NUM_ATU_ENTRIES) {
878                 err = ar40xx_hw_atu_fetch_entry(sc,
879                     &sc->atu.entries[nitems], 1);
880                 if (err != 0)
881                         goto done;
882                 sc->atu.entries[nitems].id = nitems;
883                 nitems++;
884         }
885 done:
886         sc->atu.count = nitems;
887         table->es_nitems = nitems;
888         AR40XX_UNLOCK(sc);
889
890         return (0);
891 }
892
893 /*
894  * Iterate over the ATU table entries that have been previously
895  * fetched.
896  */
897 static int
898 ar40xx_atu_fetch_table_entry(device_t dev, etherswitch_atu_entry_t *e)
899 {
900         struct ar40xx_softc *sc = device_get_softc(dev);
901         int id, err = 0;
902
903         id = e->id;
904         AR40XX_LOCK(sc);
905         if (id > sc->atu.count) {
906                 err = ENOENT;
907                 goto done;
908         }
909         memcpy(e, &sc->atu.entries[id], sizeof(*e));
910 done:
911         AR40XX_UNLOCK(sc);
912         return (err);
913 }
914
915 static device_method_t ar40xx_methods[] = {
916         /* Device interface */
917         DEVMETHOD(device_probe,                 ar40xx_probe),
918         DEVMETHOD(device_attach,                ar40xx_attach),
919         DEVMETHOD(device_detach,                ar40xx_detach),
920
921         /* bus interface */
922         DEVMETHOD(bus_add_child,                device_add_child_ordered),
923
924         /* MII interface */
925         DEVMETHOD(miibus_readreg,               ar40xx_readphy),
926         DEVMETHOD(miibus_writereg,              ar40xx_writephy),
927         DEVMETHOD(miibus_statchg,               ar40xx_statchg),
928
929         /* MDIO interface */
930         DEVMETHOD(mdio_readreg,                 ar40xx_readphy),
931         DEVMETHOD(mdio_writereg,                ar40xx_writephy),
932
933         /* etherswitch interface */
934         DEVMETHOD(etherswitch_lock,             ar40xx_lock),
935         DEVMETHOD(etherswitch_unlock,           ar40xx_unlock),
936         DEVMETHOD(etherswitch_getinfo,          ar40xx_getinfo),
937         DEVMETHOD(etherswitch_readreg,          ar40xx_readreg),
938         DEVMETHOD(etherswitch_writereg,         ar40xx_writereg),
939         DEVMETHOD(etherswitch_readphyreg,       ar40xx_readphy),
940         DEVMETHOD(etherswitch_writephyreg,      ar40xx_writephy),
941         DEVMETHOD(etherswitch_getport,          ar40xx_getport),
942         DEVMETHOD(etherswitch_setport,          ar40xx_setport),
943         DEVMETHOD(etherswitch_getvgroup,        ar40xx_getvgroup),
944         DEVMETHOD(etherswitch_setvgroup,        ar40xx_setvgroup),
945         DEVMETHOD(etherswitch_getconf,          ar40xx_getconf),
946         DEVMETHOD(etherswitch_setconf,          ar40xx_setconf),
947         DEVMETHOD(etherswitch_flush_all,        ar40xx_atu_flush_all),
948         DEVMETHOD(etherswitch_flush_port,       ar40xx_atu_flush_port),
949         DEVMETHOD(etherswitch_fetch_table,      ar40xx_atu_fetch_table),
950         DEVMETHOD(etherswitch_fetch_table_entry,
951                                              ar40xx_atu_fetch_table_entry),
952
953         DEVMETHOD_END
954 };
955
956 DEFINE_CLASS_0(ar40xx, ar40xx_driver, ar40xx_methods,
957     sizeof(struct ar40xx_softc));
958 static devclass_t ar40xx_devclass;
959
960 DRIVER_MODULE(ar40xx, simplebus, ar40xx_driver, ar40xx_devclass, 0, 0);
961 DRIVER_MODULE(ar40xx, ofwbus, ar40xx_driver, ar40xx_devclass, 0, 0);
962 DRIVER_MODULE(miibus, ar40xx, miibus_driver, 0, 0);
963 DRIVER_MODULE(mdio, ar40xx, mdio_driver, mdio_devclass, 0, 0);
964 DRIVER_MODULE(etherswitch, ar40xx, etherswitch_driver, etherswitch_devclass, 0, 0);
965 MODULE_DEPEND(ar40xx, mdio, 1, 1, 1);
966 MODULE_DEPEND(ar40xx, miibus, 1, 1, 1);
967 MODULE_DEPEND(ar40xx, etherswitch, 1, 1, 1);
968 MODULE_VERSION(ar40xx, 1);