xref: /freebsd/sys/dev/etherswitch/ar40xx/ar40xx_main.c (revision dd21556857e8d40f66bf5ad54754d9d52669ebf7)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
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/clk/clk.h>
55 #include <dev/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 error, i;
258 
259 	device_printf(sc->sc_dev, "%s: called\n", __func__);
260 
261 	error = bus_generic_detach(dev);
262 	if (error != 0)
263 		return (error);
264 
265 	callout_drain(&sc->sc_phy_callout);
266 
267 	/* Free PHYs */
268 	for (i = 0; i < AR40XX_NUM_PHYS; i++) {
269 		if (sc->sc_phys.ifp[i] != NULL)
270 			if_free(sc->sc_phys.ifp[i]);
271 		free(sc->sc_phys.ifname[i], M_DEVBUF);
272 	}
273 
274 	mtx_destroy(&sc->sc_mtx);
275 
276 	return (0);
277 }
278 
279 static int
280 ar40xx_attach(device_t dev)
281 {
282 	struct ar40xx_softc *sc = device_get_softc(dev);
283 	phandle_t psgmii_p, root_p, mdio_p;
284 	int ret, i;
285 
286 	sc->sc_dev = dev;
287 	mtx_init(&sc->sc_mtx, "ar40xx_switch", NULL, MTX_DEF);
288 
289 	psgmii_p = OF_finddevice("/soc/ess-psgmii");
290 	if (psgmii_p == -1) {
291 		device_printf(dev,
292 		    "%s: couldn't find /soc/ess-psgmii DT node\n",
293 		    __func__);
294 		goto error;
295 	}
296 
297 	/*
298 	 * Get the ipq4019-mdio node here, to talk to our local PHYs
299 	 * if needed
300 	 */
301 	root_p = OF_finddevice("/soc");
302 	mdio_p = ofw_bus_find_compatible(root_p, "qcom,ipq4019-mdio");
303 	if (mdio_p == -1) {
304 		device_printf(dev, "%s: couldn't find ipq4019-mdio DT node\n",
305 		    __func__);
306 		goto error;
307 	}
308 	sc->sc_mdio_phandle = mdio_p;
309 	sc->sc_mdio_dev = OF_device_from_xref(OF_xref_from_node(mdio_p));
310 	if (sc->sc_mdio_dev == NULL) {
311 		device_printf(dev,
312 		    "%s: couldn't get mdio device (mdio_p=%u)\n",
313 		    __func__, mdio_p);
314 		goto error;
315 	}
316 
317 	/* get psgmii base address from psgmii node */
318 	ret = OF_decode_addr(psgmii_p, 0, &sc->sc_psgmii_mem_tag,
319 	    &sc->sc_psgmii_mem_handle,
320 	    &sc->sc_psgmii_mem_size);
321 	if (ret != 0) {
322 		device_printf(dev, "%s: couldn't map psgmii mem (%d)\n",
323 		    __func__, ret);
324 		goto error;
325 	}
326 
327 	/* get switch base address */
328 	sc->sc_ess_mem_rid = 0;
329 	sc->sc_ess_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
330 	    &sc->sc_ess_mem_rid, RF_ACTIVE);
331 	if (sc->sc_ess_mem_res == NULL) {
332 		device_printf(dev, "%s: failed to find memory resource\n",
333 		    __func__);
334 		goto error;
335 	}
336 	sc->sc_ess_mem_size = (size_t) bus_get_resource_count(dev,
337 	    SYS_RES_MEMORY, sc->sc_ess_mem_rid);
338 	if (sc->sc_ess_mem_size == 0) {
339 		device_printf(dev, "%s: failed to get device memory size\n",
340 		    __func__);
341 		goto error;
342 	}
343 
344 	ret = OF_getencprop(ofw_bus_get_node(dev), "switch_mac_mode",
345 	    &sc->sc_config.switch_mac_mode,
346 	    sizeof(sc->sc_config.switch_mac_mode));
347 	if (ret < 0) {
348 		device_printf(dev, "%s: missing switch_mac_mode property\n",
349 		    __func__);
350 		goto error;
351 	}
352 
353 	ret = OF_getencprop(ofw_bus_get_node(dev), "switch_cpu_bmp",
354 	    &sc->sc_config.switch_cpu_bmp,
355 	    sizeof(sc->sc_config.switch_cpu_bmp));
356 	if (ret < 0) {
357 		device_printf(dev, "%s: missing switch_cpu_bmp property\n",
358 		    __func__);
359 		goto error;
360 	}
361 
362 	ret = OF_getencprop(ofw_bus_get_node(dev), "switch_lan_bmp",
363 	    &sc->sc_config.switch_lan_bmp,
364 	    sizeof(sc->sc_config.switch_lan_bmp));
365 	if (ret < 0) {
366 		device_printf(dev, "%s: missing switch_lan_bmp property\n",
367 		    __func__);
368 		goto error;
369 	}
370 
371 	ret = OF_getencprop(ofw_bus_get_node(dev), "switch_wan_bmp",
372 	    &sc->sc_config.switch_wan_bmp,
373 	    sizeof(sc->sc_config.switch_wan_bmp));
374 	if (ret < 0) {
375 		device_printf(dev, "%s: missing switch_wan_bmp property\n",
376 		    __func__);
377 		goto error;
378 	}
379 
380 	ret = clk_get_by_ofw_name(dev, 0, "ess_clk", &sc->sc_ess_clk);
381 	if (ret != 0) {
382 		device_printf(dev, "%s: failed to find ess_clk (%d)\n",
383 		    __func__, ret);
384 		goto error;
385 	}
386 	ret = clk_enable(sc->sc_ess_clk);
387 	if (ret != 0) {
388 		device_printf(dev, "%s: failed to enable clock (%d)\n",
389 		    __func__, ret);
390 		goto error;
391 	}
392 
393 	ret = hwreset_get_by_ofw_name(dev, 0, "ess_rst", &sc->sc_ess_rst);
394 	if (ret != 0) {
395 		device_printf(dev, "%s: failed to find ess_rst (%d)\n",
396 		    __func__, ret);
397 		goto error;
398 	}
399 
400 	/*
401 	 * Ok, at this point we have enough resources to do an initial
402 	 * reset and configuration.
403 	 */
404 
405 	AR40XX_LOCK(sc);
406 
407 	/* Initial PSGMII/RGMII port configuration */
408 	ret = ar40xx_hw_psgmii_init_config(sc);
409 	if (ret != 0) {
410 		device_printf(sc->sc_dev,
411 		    "ERROR: failed to init PSGMII (%d)\n", ret);
412 		goto error_locked;
413 	}
414 
415 	/*
416 	 * ESS reset - this resets both the ethernet switch
417 	 * AND the ethernet block.
418 	 */
419 	ret = ar40xx_hw_ess_reset(sc);
420 	if (ret != 0) {
421 		device_printf(sc->sc_dev,
422 		    "ERROR: failed to reset ESS block (%d)\n", ret);
423 		goto error_locked;
424 	}
425 
426 	/*
427 	 * Check the PHY IDs for each of the PHYs from 0..4;
428 	 * this is useful to make sure that we can SEE the external
429 	 * PHY(s).
430 	 */
431 	if (bootverbose) {
432 		ret = ar40xx_hw_phy_get_ids(sc);
433 		if (ret != 0) {
434 			device_printf(sc->sc_dev,
435 			    "ERROR: failed to check PHY IDs (%d)\n", ret);
436 			goto error_locked;
437 		}
438 	}
439 
440 	/*
441 	 * Do PSGMII PHY self-test; work-around issues.
442 	 */
443 	ret = ar40xx_hw_psgmii_self_test(sc);
444 	if (ret != 0) {
445 		device_printf(sc->sc_dev,
446 		    "ERROR: failed to do PSGMII self-test (%d)\n", ret);
447 		goto error_locked;
448 	}
449 
450 	/* Return port config to runtime state */
451 	ret = ar40xx_hw_psgmii_self_test_clean(sc);
452 	if (ret != 0) {
453 		device_printf(sc->sc_dev,
454 		    "ERROR: failed to do PSGMII runtime config (%d)\n", ret);
455 		goto error_locked;
456 	}
457 
458 	/* mac_mode_init */
459 	ret = ar40xx_hw_psgmii_set_mac_mode(sc,
460 	    sc->sc_config.switch_mac_mode);
461 
462 	/* Initialise each hardware port */
463 	for (i = 0; i < AR40XX_NUM_PORTS; i++) {
464 		ret = ar40xx_hw_port_init(sc, i);
465 	}
466 
467 	/* initialise the global switch configuration */
468 	ret = ar40xx_hw_init_globals(sc);
469 
470 	/* reset the switch vlan/port learning config */
471 	ret = ar40xx_reset_switch(sc);
472 
473 	/* cpuport setup */
474 	ret = ar40xx_hw_port_cpuport_setup(sc);
475 
476 	AR40XX_UNLOCK(sc);
477 
478 #if 0
479 	/* We may end up needing the QM workaround code here.. */
480 	device_printf(dev, "%s: TODO: QM error check\n", __func__);
481 #endif
482 
483 	/* Attach PHYs */
484 	ret = ar40xx_attach_phys(sc);
485 
486 	bus_identify_children(dev);
487 	bus_enumerate_hinted_children(dev);
488 	bus_attach_children(dev);
489 
490 	/* Start timer */
491 	callout_init_mtx(&sc->sc_phy_callout, &sc->sc_mtx, 0);
492 
493 	/*
494 	 * Setup the etherswitch info block.
495 	 */
496 	strlcpy(sc->sc_info.es_name, device_get_desc(dev),
497 	    sizeof(sc->sc_info.es_name));
498 	sc->sc_info.es_nports = AR40XX_NUM_PORTS;
499 	sc->sc_info.es_vlan_caps = ETHERSWITCH_VLAN_DOT1Q;
500 	/* XXX TODO: double-tag / 802.1ad */
501 	sc->sc_info.es_nvlangroups = AR40XX_NUM_VTU_ENTRIES;
502 
503 	/*
504 	 * Fetch the initial port configuration.
505 	 */
506 	AR40XX_LOCK(sc);
507 	ar40xx_tick(sc);
508 	AR40XX_UNLOCK(sc);
509 
510 	ar40xx_sysctl_attach(sc);
511 
512 	return (0);
513 error_locked:
514 	AR40XX_UNLOCK(sc);
515 error:
516 	ar40xx_detach(dev);
517 	return (ENXIO);
518 }
519 
520 static void
521 ar40xx_lock(device_t dev)
522 {
523 	struct ar40xx_softc *sc = device_get_softc(dev);
524 
525 	AR40XX_LOCK(sc);
526 }
527 
528 static void
529 ar40xx_unlock(device_t dev)
530 {
531 	struct ar40xx_softc *sc = device_get_softc(dev);
532 
533 	AR40XX_LOCK_ASSERT(sc);
534 	AR40XX_UNLOCK(sc);
535 }
536 
537 static etherswitch_info_t *
538 ar40xx_getinfo(device_t dev)
539 {
540 	struct ar40xx_softc *sc = device_get_softc(dev);
541 
542 	return (&sc->sc_info);
543 }
544 
545 static int
546 ar40xx_readreg(device_t dev, int addr)
547 {
548 	struct ar40xx_softc *sc = device_get_softc(dev);
549 
550 	if (addr >= sc->sc_ess_mem_size - 1)
551 		return (-1);
552 
553 	AR40XX_REG_BARRIER_READ(sc);
554 
555 	return AR40XX_REG_READ(sc, addr);
556 }
557 
558 static int
559 ar40xx_writereg(device_t dev, int addr, int value)
560 {
561 	struct ar40xx_softc *sc = device_get_softc(dev);
562 
563 	if (addr >= sc->sc_ess_mem_size - 1)
564 		return (-1);
565 
566 	AR40XX_REG_WRITE(sc, addr, value);
567 	AR40XX_REG_BARRIER_WRITE(sc);
568 	return (0);
569 }
570 
571 /*
572  * Get the port configuration and status.
573  */
574 static int
575 ar40xx_getport(device_t dev, etherswitch_port_t *p)
576 {
577 	struct ar40xx_softc *sc = device_get_softc(dev);
578 	struct mii_data *mii = NULL;
579 	struct ifmediareq *ifmr;
580 	int err;
581 
582 	if (p->es_port < 0 || p->es_port > sc->sc_info.es_nports)
583 		return (ENXIO);
584 
585 	AR40XX_LOCK(sc);
586 	/* Fetch the current VLAN configuration for this port */
587 	/* PVID */
588 	ar40xx_hw_get_port_pvid(sc, p->es_port, &p->es_pvid);
589 
590 	/*
591 	 * The VLAN egress aren't appropriate to the ports;
592 	 * instead it's part of the VLAN group config.
593 	 */
594 
595 	/* Get MII config */
596 	mii = ar40xx_phy_miiforport(sc, p->es_port);
597 
598 	AR40XX_UNLOCK(sc);
599 
600 	if (p->es_port == 0) {
601 		/* CPU port */
602 		p->es_flags |= ETHERSWITCH_PORT_CPU;
603 		ifmr = &p->es_ifmr;
604 		ifmr->ifm_count = 0;
605 		ifmr->ifm_current = ifmr->ifm_active =
606 		     IFM_ETHER | IFM_1000_T | IFM_FDX;
607 		ifmr->ifm_mask = 0;
608 		ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
609 	} else if (mii != NULL) {
610 		/* non-CPU port */
611 		err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr,
612 		    &mii->mii_media, SIOCGIFMEDIA);
613 		if (err)
614 			return (err);
615 	} else {
616 		return (ENXIO);
617 	}
618 
619 	return (0);
620 }
621 
622 /*
623  * Set the port configuration and status.
624  */
625 static int
626 ar40xx_setport(device_t dev, etherswitch_port_t *p)
627 {
628 	struct ar40xx_softc *sc = device_get_softc(dev);
629 	struct ifmedia *ifm;
630 	struct mii_data *mii;
631 	if_t ifp;
632 	int ret;
633 
634 	if (p->es_port < 0 || p->es_port > sc->sc_info.es_nports)
635 		return (EINVAL);
636 
637 	/* Port flags */
638 	AR40XX_LOCK(sc);
639 	ret = ar40xx_hw_set_port_pvid(sc, p->es_port, p->es_pvid);
640 	if (ret != 0) {
641 		AR40XX_UNLOCK(sc);
642 		return (ret);
643 	}
644 	/* XXX TODO: tag strip/unstrip, double-tag, etc */
645 	AR40XX_UNLOCK(sc);
646 
647 	/* Don't change media config on CPU port */
648 	if (p->es_port == 0)
649 		return (0);
650 
651 	mii = ar40xx_phy_miiforport(sc, p->es_port);
652 	if (mii == NULL)
653 		return (ENXIO);
654 
655 	ifp = ar40xx_phy_ifpforport(sc, p->es_port);
656 
657 	ifm = &mii->mii_media;
658 	return (ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA));
659 
660 	return (0);
661 }
662 
663 /*
664  * Get the current VLAN group (per-port, ISL, dot1q) configuration.
665  *
666  * For now the only supported operating mode is dot1q.
667  */
668 static int
669 ar40xx_getvgroup(device_t dev, etherswitch_vlangroup_t *vg)
670 {
671 	struct ar40xx_softc *sc = device_get_softc(dev);
672 	int vid, ret;
673 
674 	if (vg->es_vlangroup > sc->sc_info.es_nvlangroups)
675 		return (EINVAL);
676 
677 	vg->es_untagged_ports = 0;
678 	vg->es_member_ports = 0;
679 	vg->es_fid = 0;
680 
681 	AR40XX_LOCK(sc);
682 
683 	/* Note: only supporting 802.1q VLAN config for now */
684 	if (sc->sc_vlan.vlan != 1) {
685 		vg->es_member_ports = 0;
686 		vg->es_untagged_ports = 0;
687 		AR40XX_UNLOCK(sc);
688 		return (-1);
689 	}
690 
691 	/* Get vlangroup mapping to VLAN id */
692 	vid = sc->sc_vlan.vlan_id[vg->es_vlangroup];
693 	if ((vid & ETHERSWITCH_VID_VALID) == 0) {
694 		/* Not an active vgroup; bail */
695 		AR40XX_UNLOCK(sc);
696 		return (0);
697 	}
698 	vg->es_vid = vid;
699 
700 	ret = ar40xx_hw_vtu_get_vlan(sc, vid, &vg->es_member_ports,
701 	    &vg->es_untagged_ports);
702 
703 	AR40XX_UNLOCK(sc);
704 
705 	if (ret == 0) {
706 		vg->es_vid |= ETHERSWITCH_VID_VALID;
707 	}
708 
709 	return (ret);
710 }
711 
712 /*
713  * Set the current VLAN group (per-port, ISL, dot1q) configuration.
714  *
715  * For now the only supported operating mode is dot1q.
716  */
717 static int
718 ar40xx_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
719 {
720 	struct ar40xx_softc *sc = device_get_softc(dev);
721 	int err, vid;
722 
723 	/* For now we only support 802.1q mode */
724 	if (sc->sc_vlan.vlan == 0)
725 		return (EINVAL);
726 
727 	AR40XX_LOCK(sc);
728 	vid = sc->sc_vlan.vlan_id[vg->es_vlangroup];
729 	/*
730 	 * If we have an 802.1q VID and it's different to the current one,
731 	 * purge the current VTU entry.
732 	 */
733 	if ((vid != 0) &&
734 	    ((vid & ETHERSWITCH_VID_VALID) != 0) &&
735 	    ((vid & ETHERSWITCH_VID_MASK) !=
736 	     (vg->es_vid & ETHERSWITCH_VID_MASK))) {
737 		AR40XX_DPRINTF(sc, AR40XX_DBG_VTU_OP,
738 		    "%s: purging VID %d first\n", __func__, vid);
739 		err = ar40xx_hw_vtu_flush(sc);
740 		if (err != 0) {
741 			AR40XX_UNLOCK(sc);
742 			return (err);
743 		}
744 	}
745 
746 	/* Update VLAN ID */
747 	vid = vg->es_vid & ETHERSWITCH_VID_MASK;
748 	sc->sc_vlan.vlan_id[vg->es_vlangroup] = vid;
749 	if (vid == 0) {
750 		/* Setting it to 0 disables the group */
751 		AR40XX_UNLOCK(sc);
752 		return (0);
753 	}
754 	/* Add valid bit for this entry */
755 	sc->sc_vlan.vlan_id[vg->es_vlangroup] = vid | ETHERSWITCH_VID_VALID;
756 
757 	/* Update hardware */
758 	err = ar40xx_hw_vtu_load_vlan(sc, vid, vg->es_member_ports,
759 	    vg->es_untagged_ports);
760 	if (err != 0) {
761 		AR40XX_UNLOCK(sc);
762 		return (err);
763 	}
764 
765 	/* Update the config for the given entry */
766 	sc->sc_vlan.vlan_ports[vg->es_vlangroup] = vg->es_member_ports;
767 	sc->sc_vlan.vlan_untagged[vg->es_vlangroup] = vg->es_untagged_ports;
768 
769 	AR40XX_UNLOCK(sc);
770 
771 	return (0);
772 }
773 
774 /*
775  * Get the current configuration mode.
776  */
777 static int
778 ar40xx_getconf(device_t dev, etherswitch_conf_t *conf)
779 {
780 	struct ar40xx_softc *sc = device_get_softc(dev);
781 	int ret;
782 
783 	AR40XX_LOCK(sc);
784 
785 	/* Only support dot1q VLAN for now */
786 	conf->cmd = ETHERSWITCH_CONF_VLAN_MODE;
787 	conf->vlan_mode = ETHERSWITCH_VLAN_DOT1Q;
788 
789 	/* Switch MAC address */
790 	ret = ar40xx_hw_read_switch_mac_address(sc, &conf->switch_macaddr);
791 	if (ret == 0)
792 		conf->cmd |= ETHERSWITCH_CONF_SWITCH_MACADDR;
793 
794 	AR40XX_UNLOCK(sc);
795 
796 	return (0);
797 }
798 
799 /*
800  * Set the current configuration and do a switch reset.
801  *
802  * For now the only supported operating mode is dot1q, don't
803  * allow it to be set to non-dot1q.
804  */
805 static int
806 ar40xx_setconf(device_t dev, etherswitch_conf_t *conf)
807 {
808 	struct ar40xx_softc *sc = device_get_softc(dev);
809 	int ret = 0;
810 
811 	if (conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) {
812 		/* Only support dot1q VLAN for now */
813 		if (conf->vlan_mode != ETHERSWITCH_VLAN_DOT1Q)
814 			return (EINVAL);
815 	}
816 
817 	if (conf->cmd & ETHERSWITCH_CONF_SWITCH_MACADDR) {
818 		AR40XX_LOCK(sc);
819 		ret = ar40xx_hw_read_switch_mac_address(sc,
820 		    &conf->switch_macaddr);
821 		AR40XX_UNLOCK(sc);
822 	}
823 
824 	return (ret);
825 }
826 
827 /*
828  * Flush all ATU entries.
829  */
830 static int
831 ar40xx_atu_flush_all(device_t dev)
832 {
833 	struct ar40xx_softc *sc = device_get_softc(dev);
834 	int ret;
835 
836 	AR40XX_LOCK(sc);
837 	ret = ar40xx_hw_atu_flush_all(sc);
838 	AR40XX_UNLOCK(sc);
839 	return (ret);
840 }
841 
842 /*
843  * Flush all ATU entries for the given port.
844  */
845 static int
846 ar40xx_atu_flush_port(device_t dev, int port)
847 {
848 	struct ar40xx_softc *sc = device_get_softc(dev);
849 	int ret;
850 
851 	AR40XX_LOCK(sc);
852 	ret = ar40xx_hw_atu_flush_port(sc, port);
853 	AR40XX_UNLOCK(sc);
854 	return (ret);
855 }
856 
857 /*
858  * Load the ATU table into local storage so it can be iterated
859  * over.
860  */
861 static int
862 ar40xx_atu_fetch_table(device_t dev, etherswitch_atu_table_t *table)
863 {
864 	struct ar40xx_softc *sc = device_get_softc(dev);
865 	int err, nitems;
866 
867 	memset(&sc->atu.entries, 0, sizeof(sc->atu.entries));
868 
869 	table->es_nitems = 0;
870 	nitems = 0;
871 
872 	AR40XX_LOCK(sc);
873 	sc->atu.count = 0;
874 	err = ar40xx_hw_atu_fetch_entry(sc, NULL, 0);
875 	if (err != 0)
876 		goto done;
877 
878 	while (nitems < AR40XX_NUM_ATU_ENTRIES) {
879 		err = ar40xx_hw_atu_fetch_entry(sc,
880 		    &sc->atu.entries[nitems], 1);
881 		if (err != 0)
882 			goto done;
883 		sc->atu.entries[nitems].id = nitems;
884 		nitems++;
885 	}
886 done:
887 	sc->atu.count = nitems;
888 	table->es_nitems = nitems;
889 	AR40XX_UNLOCK(sc);
890 
891 	return (0);
892 }
893 
894 /*
895  * Iterate over the ATU table entries that have been previously
896  * fetched.
897  */
898 static int
899 ar40xx_atu_fetch_table_entry(device_t dev, etherswitch_atu_entry_t *e)
900 {
901 	struct ar40xx_softc *sc = device_get_softc(dev);
902 	int id, err = 0;
903 
904 	id = e->id;
905 	AR40XX_LOCK(sc);
906 	if (id > sc->atu.count) {
907 		err = ENOENT;
908 		goto done;
909 	}
910 	memcpy(e, &sc->atu.entries[id], sizeof(*e));
911 done:
912 	AR40XX_UNLOCK(sc);
913 	return (err);
914 }
915 
916 static device_method_t ar40xx_methods[] = {
917 	/* Device interface */
918 	DEVMETHOD(device_probe,			ar40xx_probe),
919 	DEVMETHOD(device_attach,		ar40xx_attach),
920 	DEVMETHOD(device_detach,		ar40xx_detach),
921 
922 	/* bus interface */
923 	DEVMETHOD(bus_add_child,		device_add_child_ordered),
924 
925 	/* MII interface */
926 	DEVMETHOD(miibus_readreg,		ar40xx_readphy),
927 	DEVMETHOD(miibus_writereg,		ar40xx_writephy),
928 	DEVMETHOD(miibus_statchg,		ar40xx_statchg),
929 
930 	/* MDIO interface */
931 	DEVMETHOD(mdio_readreg,			ar40xx_readphy),
932 	DEVMETHOD(mdio_writereg,		ar40xx_writephy),
933 
934 	/* etherswitch interface */
935 	DEVMETHOD(etherswitch_lock,		ar40xx_lock),
936 	DEVMETHOD(etherswitch_unlock,		ar40xx_unlock),
937 	DEVMETHOD(etherswitch_getinfo,		ar40xx_getinfo),
938 	DEVMETHOD(etherswitch_readreg,		ar40xx_readreg),
939 	DEVMETHOD(etherswitch_writereg,		ar40xx_writereg),
940 	DEVMETHOD(etherswitch_readphyreg,	ar40xx_readphy),
941 	DEVMETHOD(etherswitch_writephyreg,	ar40xx_writephy),
942 	DEVMETHOD(etherswitch_getport,		ar40xx_getport),
943 	DEVMETHOD(etherswitch_setport,		ar40xx_setport),
944 	DEVMETHOD(etherswitch_getvgroup,	ar40xx_getvgroup),
945 	DEVMETHOD(etherswitch_setvgroup,	ar40xx_setvgroup),
946 	DEVMETHOD(etherswitch_getconf,		ar40xx_getconf),
947 	DEVMETHOD(etherswitch_setconf,		ar40xx_setconf),
948 	DEVMETHOD(etherswitch_flush_all,	ar40xx_atu_flush_all),
949 	DEVMETHOD(etherswitch_flush_port,	ar40xx_atu_flush_port),
950 	DEVMETHOD(etherswitch_fetch_table,	ar40xx_atu_fetch_table),
951 	DEVMETHOD(etherswitch_fetch_table_entry,
952 					     ar40xx_atu_fetch_table_entry),
953 
954 	DEVMETHOD_END
955 };
956 
957 DEFINE_CLASS_0(ar40xx, ar40xx_driver, ar40xx_methods,
958     sizeof(struct ar40xx_softc));
959 
960 DRIVER_MODULE(ar40xx, simplebus, ar40xx_driver, 0, 0);
961 DRIVER_MODULE(ar40xx, ofwbus, ar40xx_driver, 0, 0);
962 DRIVER_MODULE(miibus, ar40xx, miibus_driver, 0, 0);
963 DRIVER_MODULE(mdio, ar40xx, mdio_driver, 0, 0);
964 DRIVER_MODULE(etherswitch, ar40xx, etherswitch_driver, 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);
969