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