1289947Szbb/*-
2289947Szbb * Copyright (c) 2015 Semihalf
3289947Szbb * Copyright (c) 2015 Stormshield
4289947Szbb * All rights reserved.
5289947Szbb *
6289947Szbb * Redistribution and use in source and binary forms, with or without
7289947Szbb * modification, are permitted provided that the following conditions
8289947Szbb * are met:
9289947Szbb * 1. Redistributions of source code must retain the above copyright
10289947Szbb *    notice, this list of conditions and the following disclaimer.
11289947Szbb * 2. Redistributions in binary form must reproduce the above copyright
12289947Szbb *    notice, this list of conditions and the following disclaimer in the
13289947Szbb *    documentation and/or other materials provided with the distribution.
14289947Szbb *
15289947Szbb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16289947Szbb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17289947Szbb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18289947Szbb * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19289947Szbb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20289947Szbb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21289947Szbb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22289947Szbb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23289947Szbb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24289947Szbb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25289947Szbb * SUCH DAMAGE.
26289947Szbb */
27289947Szbb
28289947Szbb#include <sys/cdefs.h>
29289947Szbb__FBSDID("$FreeBSD$");
30289947Szbb
31289947Szbb#include <sys/types.h>
32289947Szbb#include <sys/param.h>
33289947Szbb#include <sys/systm.h>
34289947Szbb#include <sys/sockio.h>
35289947Szbb#include <sys/kernel.h>
36289947Szbb#include <sys/kthread.h>
37289947Szbb#include <sys/socket.h>
38289947Szbb#include <sys/module.h>
39289947Szbb#include <sys/errno.h>
40289947Szbb#include <sys/bus.h>
41289947Szbb#include <sys/conf.h>
42289947Szbb#include <sys/uio.h>
43289947Szbb#include <sys/fcntl.h>
44289947Szbb
45289947Szbb#include <net/if.h>
46289947Szbb#include <net/if_media.h>
47289947Szbb#include <net/if_types.h>
48289947Szbb
49289947Szbb#include <machine/bus.h>
50289947Szbb#include <machine/resource.h>
51289947Szbb
52289947Szbb#include <arm/mv/mvwin.h>
53289947Szbb#include <arm/mv/mvreg.h>
54289947Szbb#include <arm/mv/mvvar.h>
55289947Szbb
56289947Szbb#include <dev/etherswitch/etherswitch.h>
57292738Sadrian#include <dev/mdio/mdio.h>
58289947Szbb#include <dev/mii/mii.h>
59289947Szbb#include <dev/mii/miivar.h>
60289947Szbb#include <dev/mge/if_mgevar.h>
61289947Szbb
62289947Szbb#include "e6000swreg.h"
63289947Szbb#include "etherswitch_if.h"
64289947Szbb#include "miibus_if.h"
65289947Szbb#include "mdio_if.h"
66289947Szbb
67289947SzbbMALLOC_DECLARE(M_E6000SW);
68289947SzbbMALLOC_DEFINE(M_E6000SW, "e6000sw", "e6000sw switch");
69289947Szbb
70289947Szbb#define E6000SW_LOCK(_sc)			\
71289947Szbb	    sx_xlock(&(_sc)->sx)
72289947Szbb#define E6000SW_UNLOCK(_sc)			\
73289947Szbb	    sx_unlock(&(_sc)->sx)
74289947Szbb#define E6000SW_LOCK_ASSERT(_sc, _what)		\
75289947Szbb	    sx_assert(&(_sc)->sx, (_what))
76289947Szbb#define E6000SW_TRYLOCK(_sc)			\
77289947Szbb	    sx_tryxlock(&(_sc)->sx)
78289947Szbb
79289947Szbbtypedef struct e6000sw_softc {
80289947Szbb	device_t		dev;
81289947Szbb
82289947Szbb	struct sx		sx;
83289947Szbb	struct ifnet		*ifp[E6000SW_NUM_PHYS];
84289947Szbb	char			*ifname[E6000SW_NUM_PHYS];
85289947Szbb	device_t		miibus[E6000SW_NUM_PHYS];
86289947Szbb	struct mii_data		*mii[E6000SW_NUM_PHYS];
87289947Szbb	struct callout		tick_callout;
88289947Szbb
89289947Szbb	uint32_t		cpuports_mask;
90289947Szbb
91289947Szbb	int			vid[E6000SW_NUM_VGROUPS];
92289947Szbb	int			members[E6000SW_NUM_VGROUPS];
93289947Szbb	int			vgroup[E6000SW_NUM_PORTS];
94289947Szbb} e6000sw_softc_t;
95289947Szbb
96289947Szbbstatic etherswitch_info_t etherswitch_info = {
97289947Szbb	.es_nports =		E6000SW_NUM_PORTS,
98289947Szbb	.es_nvlangroups =	E6000SW_NUM_VGROUPS,
99289947Szbb	.es_name =		"Marvell 6000 series switch"
100289947Szbb};
101289947Szbb
102289947Szbbstatic void e6000sw_identify(driver_t *driver, device_t parent);
103289947Szbbstatic int e6000sw_probe(device_t dev);
104289947Szbbstatic int e6000sw_attach(device_t dev);
105289947Szbbstatic int e6000sw_detach(device_t dev);
106289947Szbbstatic int e6000sw_readphy(device_t dev, int phy, int reg);
107289947Szbbstatic int e6000sw_writephy(device_t dev, int phy, int reg, int data);
108289947Szbbstatic etherswitch_info_t* e6000sw_getinfo(device_t dev);
109289947Szbbstatic void e6000sw_lock(device_t dev);
110289947Szbbstatic void e6000sw_unlock(device_t dev);
111289947Szbbstatic int e6000sw_getport(device_t dev, etherswitch_port_t *p);
112289947Szbbstatic int e6000sw_setport(device_t dev, etherswitch_port_t *p);
113289947Szbbstatic int e6000sw_readreg_wrapper(device_t dev, int addr_reg);
114289947Szbbstatic int e6000sw_writereg_wrapper(device_t dev, int addr_reg, int val);
115289947Szbbstatic int e6000sw_readphy_wrapper(device_t dev, int phy, int reg);
116289947Szbbstatic int e6000sw_writephy_wrapper(device_t dev, int phy, int reg, int data);
117289947Szbbstatic int e6000sw_getvgroup_wrapper(device_t dev, etherswitch_vlangroup_t *vg);
118289947Szbbstatic int e6000sw_setvgroup_wrapper(device_t dev, etherswitch_vlangroup_t *vg);
119289947Szbbstatic int e6000sw_setvgroup(device_t dev, etherswitch_vlangroup_t *vg);
120289947Szbbstatic int e6000sw_getvgroup(device_t dev, etherswitch_vlangroup_t *vg);
121289947Szbbstatic void e6000sw_setup(device_t dev, e6000sw_softc_t *sc);
122289947Szbbstatic void e6000sw_port_vlan_conf(e6000sw_softc_t *sc);
123289947Szbbstatic void e6000sw_tick(void *arg);
124289947Szbbstatic void e6000sw_set_atustat(device_t dev, e6000sw_softc_t *sc, int bin,
125289947Szbb    int flag);
126289947Szbbstatic int e6000sw_atu_flush(device_t dev, e6000sw_softc_t *sc, int flag);
127289947Szbbstatic __inline void e6000sw_writereg(e6000sw_softc_t *sc, int addr, int reg,
128289947Szbb    int val);
129289947Szbbstatic __inline uint32_t e6000sw_readreg(e6000sw_softc_t *sc, int addr,
130289947Szbb    int reg);
131289947Szbbstatic int e6000sw_ifmedia_upd(struct ifnet *ifp);
132289947Szbbstatic void e6000sw_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr);
133289947Szbbstatic int e6000sw_atu_mac_table(device_t dev, e6000sw_softc_t *sc, struct
134289947Szbb    atu_opt *atu, int flag);
135289947Szbbstatic int e6000sw_get_pvid(e6000sw_softc_t *sc, int port, int *pvid);
136289947Szbbstatic int e6000sw_set_pvid(e6000sw_softc_t *sc, int port, int pvid);
137289947Szbbstatic __inline int e6000sw_cpuport(e6000sw_softc_t *sc, int port);
138289947Szbbstatic __inline struct mii_data *e6000sw_miiforphy(e6000sw_softc_t *sc,
139289947Szbb    unsigned int phy);
140289947Szbb
141289947Szbbstatic struct proc *e6000sw_kproc;
142289947Szbb
143289947Szbbstatic device_method_t e6000sw_methods[] = {
144289947Szbb	/* device interface */
145289947Szbb	DEVMETHOD(device_identify,		e6000sw_identify),
146289947Szbb	DEVMETHOD(device_probe,			e6000sw_probe),
147289947Szbb	DEVMETHOD(device_attach,		e6000sw_attach),
148289947Szbb	DEVMETHOD(device_detach,		e6000sw_detach),
149289947Szbb
150289947Szbb	/* bus interface */
151289947Szbb	DEVMETHOD(bus_add_child,		device_add_child_ordered),
152289947Szbb
153289947Szbb	/* mii interface */
154289947Szbb	DEVMETHOD(miibus_readreg,		e6000sw_readphy),
155289947Szbb	DEVMETHOD(miibus_writereg,		e6000sw_writephy),
156289947Szbb
157289947Szbb	/* etherswitch interface */
158289947Szbb	DEVMETHOD(etherswitch_getinfo,		e6000sw_getinfo),
159289947Szbb	DEVMETHOD(etherswitch_lock,		e6000sw_lock),
160289947Szbb	DEVMETHOD(etherswitch_unlock,		e6000sw_unlock),
161289947Szbb	DEVMETHOD(etherswitch_getport,		e6000sw_getport),
162289947Szbb	DEVMETHOD(etherswitch_setport,		e6000sw_setport),
163289947Szbb	DEVMETHOD(etherswitch_readreg,		e6000sw_readreg_wrapper),
164289947Szbb	DEVMETHOD(etherswitch_writereg,		e6000sw_writereg_wrapper),
165289947Szbb	DEVMETHOD(etherswitch_readphyreg,	e6000sw_readphy_wrapper),
166289947Szbb	DEVMETHOD(etherswitch_writephyreg,	e6000sw_writephy_wrapper),
167289947Szbb	DEVMETHOD(etherswitch_setvgroup,	e6000sw_setvgroup_wrapper),
168289947Szbb	DEVMETHOD(etherswitch_getvgroup,	e6000sw_getvgroup_wrapper),
169289947Szbb
170289947Szbb	DEVMETHOD_END
171289947Szbb};
172289947Szbb
173289947Szbbstatic devclass_t e6000sw_devclass;
174289947Szbb
175289947SzbbDEFINE_CLASS_0(e6000sw, e6000sw_driver, e6000sw_methods,
176289947Szbb    sizeof(e6000sw_softc_t));
177289947Szbb
178289947SzbbDRIVER_MODULE(e6000sw, mdio, e6000sw_driver, e6000sw_devclass, 0, 0);
179289947SzbbDRIVER_MODULE(etherswitch, e6000sw, etherswitch_driver, etherswitch_devclass, 0,
180289947Szbb    0);
181289947SzbbDRIVER_MODULE(miibus, e6000sw, miibus_driver, miibus_devclass, 0, 0);
182289947SzbbMODULE_DEPEND(e6000sw, mdio, 1, 1, 1);
183289947Szbb
184289947Szbbstatic void
185289947Szbbe6000sw_identify(driver_t *driver, device_t parent)
186289947Szbb{
187289947Szbb
188289947Szbb	if (device_find_child(parent, "e6000sw", -1) == NULL)
189289947Szbb		BUS_ADD_CHILD(parent, 0, "e6000sw", -1);
190289947Szbb}
191289947Szbb
192289947Szbbstatic int
193289947Szbbe6000sw_probe(device_t dev)
194289947Szbb{
195289947Szbb	e6000sw_softc_t *sc;
196289947Szbb	const char *description;
197289947Szbb	unsigned int id;
198289947Szbb
199289947Szbb	sc = device_get_softc(dev);
200289947Szbb	bzero(sc, sizeof(e6000sw_softc_t));
201289947Szbb	sc->dev = dev;
202289947Szbb	/* Lock is necessary due to assertions. */
203289947Szbb	sx_init(&sc->sx, "e6000sw");
204289947Szbb	E6000SW_LOCK(sc);
205289947Szbb
206289947Szbb	id = e6000sw_readreg(sc, REG_PORT(0), SWITCH_ID);
207289947Szbb
208289947Szbb	switch (id & 0xfff0) {
209289947Szbb	case 0x3520:
210289947Szbb		description = "Marvell 88E6352";
211289947Szbb		break;
212289947Szbb	case 0x1720:
213289947Szbb		description = "Marvell 88E6172";
214289947Szbb		break;
215289947Szbb	case 0x1760:
216289947Szbb		description = "Marvell 88E6176";
217289947Szbb		break;
218289947Szbb	default:
219289947Szbb		E6000SW_UNLOCK(sc);
220289947Szbb		sx_destroy(&sc->sx);
221289947Szbb		device_printf(dev, "Unrecognized device.\n");
222289947Szbb		return (ENXIO);
223289947Szbb	}
224289947Szbb
225289947Szbb	device_set_desc(dev, description);
226289947Szbb
227289947Szbb	E6000SW_UNLOCK(sc);
228289947Szbb
229289947Szbb	return (BUS_PROBE_DEFAULT);
230289947Szbb}
231289947Szbb
232289947Szbbstatic int
233289947Szbbe6000sw_attach(device_t dev)
234289947Szbb{
235289947Szbb	e6000sw_softc_t *sc;
236289947Szbb	int phy, err, port;
237289947Szbb	char name[IFNAMSIZ];
238289947Szbb
239289947Szbb	err = 0;
240289947Szbb	sc = device_get_softc(dev);
241289947Szbb	E6000SW_LOCK(sc);
242289947Szbb	sc->cpuports_mask = E6000SW_CPUPORTS_MASK;
243289947Szbb	for (port = 0; port < E6000SW_NUM_PORTS; port++)
244289947Szbb		sc->vgroup[port] = E6000SW_PORT_NO_VGROUP;
245289947Szbb	e6000sw_setup(dev, sc);
246289947Szbb
247289947Szbb	snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->dev));
248289947Szbb	for (phy = 0; phy < E6000SW_NUM_PHYS; phy++) {
249289947Szbb		sc->ifp[phy] = if_alloc(IFT_ETHER);
250289947Szbb		if (sc->ifp[phy] == NULL)
251289947Szbb			goto out_fail;
252289947Szbb		sc->ifp[phy]->if_softc = sc;
253289947Szbb		sc->ifp[phy]->if_flags |= IFF_UP | IFF_BROADCAST |
254289947Szbb		    IFF_DRV_RUNNING | IFF_SIMPLEX;
255289947Szbb		sc->ifname[phy] = malloc(strlen(name) + 1, M_E6000SW, M_WAITOK);
256289947Szbb		if (sc->ifname[phy] == NULL)
257289947Szbb			goto out_fail;
258289947Szbb		bcopy(name, sc->ifname[phy], strlen(name) + 1);
259289947Szbb		if_initname(sc->ifp[phy], sc->ifname[phy], phy);
260289947Szbb		err = mii_attach(sc->dev, &sc->miibus[phy], sc->ifp[phy],
261289947Szbb		    e6000sw_ifmedia_upd, e6000sw_ifmedia_sts, BMSR_DEFCAPMASK,
262289947Szbb		    phy, MII_OFFSET_ANY, 0);
263289947Szbb		if (err != 0) {
264289947Szbb			device_printf(sc->dev,
265289947Szbb			    "attaching PHY %d failed\n",
266289947Szbb			    phy);
267289947Szbb			goto out_fail;
268289947Szbb		}
269289947Szbb		sc->mii[phy] = device_get_softc(sc->miibus[phy]);
270289947Szbb	}
271289947Szbb	E6000SW_UNLOCK(sc);
272289947Szbb
273289947Szbb	bus_generic_probe(dev);
274289947Szbb	bus_enumerate_hinted_children(dev);
275289947Szbb	bus_generic_attach(dev);
276289947Szbb
277289947Szbb	kproc_create(e6000sw_tick, sc, &e6000sw_kproc, 0, 0,
278289947Szbb	    "e6000sw tick kproc");
279289947Szbb
280289947Szbb	return (0);
281289947Szbb
282289947Szbbout_fail:
283289947Szbb	e6000sw_detach(dev);
284289947Szbb
285289947Szbb	return (ENXIO);
286289947Szbb}
287289947Szbb
288289947Szbbstatic __inline void
289289947Szbbe6000sw_poll_done(e6000sw_softc_t *sc)
290289947Szbb{
291289947Szbb
292289947Szbb	while (e6000sw_readreg(sc, REG_GLOBAL2, PHY_CMD) &
293289947Szbb	    (1 << PHY_CMD_SMI_BUSY))
294289947Szbb		continue;
295289947Szbb}
296289947Szbb
297289947Szbb
298289947Szbb/*
299289947Szbb * PHY registers are paged. Put page index in reg 22 (accessible from every
300289947Szbb * page), then access specific register.
301289947Szbb */
302289947Szbbstatic int
303289947Szbbe6000sw_readphy(device_t dev, int phy, int reg)
304289947Szbb{
305289947Szbb	e6000sw_softc_t *sc;
306289947Szbb	uint32_t val;
307289947Szbb
308289947Szbb	sc = device_get_softc(dev);
309289947Szbb	val = 0;
310289947Szbb
311289947Szbb	if (phy >= E6000SW_NUM_PHYS || reg >= E6000SW_NUM_PHY_REGS) {
312289947Szbb		device_printf(dev, "Wrong register address.\n");
313289947Szbb		return (EINVAL);
314289947Szbb	}
315289947Szbb
316289947Szbb	E6000SW_LOCK_ASSERT(sc, SA_XLOCKED);
317289947Szbb
318289947Szbb	e6000sw_poll_done(sc);
319289947Szbb	val |= 1 << PHY_CMD_SMI_BUSY;
320289947Szbb	val |= PHY_CMD_MODE_MDIO << PHY_CMD_MODE;
321289947Szbb	val |= PHY_CMD_OPCODE_READ << PHY_CMD_OPCODE;
322289947Szbb	val |= (reg << PHY_CMD_REG_ADDR) & PHY_CMD_REG_ADDR_MASK;
323289947Szbb	val |= (phy << PHY_CMD_DEV_ADDR) & PHY_CMD_DEV_ADDR_MASK;
324289947Szbb	e6000sw_writereg(sc, REG_GLOBAL2, SMI_PHY_CMD_REG, val);
325289947Szbb	e6000sw_poll_done(sc);
326289947Szbb	val = e6000sw_readreg(sc, REG_GLOBAL2, SMI_PHY_DATA_REG)
327289947Szbb		& PHY_DATA_MASK;
328289947Szbb
329289947Szbb	return (val);
330289947Szbb}
331289947Szbb
332289947Szbbstatic int
333289947Szbbe6000sw_writephy(device_t dev, int phy, int reg, int data)
334289947Szbb{
335289947Szbb	e6000sw_softc_t *sc;
336289947Szbb	uint32_t val;
337289947Szbb
338289947Szbb	sc = device_get_softc(dev);
339289947Szbb	val = 0;
340289947Szbb
341289947Szbb	if (phy >= E6000SW_NUM_PHYS || reg >= E6000SW_NUM_PHY_REGS) {
342289947Szbb		device_printf(dev, "Wrong register address.\n");
343289947Szbb		return (EINVAL);
344289947Szbb	}
345289947Szbb
346289947Szbb	E6000SW_LOCK_ASSERT(sc, SA_XLOCKED);
347289947Szbb
348289947Szbb	e6000sw_poll_done(sc);
349289947Szbb	val |= PHY_CMD_MODE_MDIO << PHY_CMD_MODE;
350289947Szbb	val |= 1 << PHY_CMD_SMI_BUSY;
351289947Szbb	val |= PHY_CMD_OPCODE_WRITE << PHY_CMD_OPCODE;
352289947Szbb	val |= (reg << PHY_CMD_REG_ADDR) & PHY_CMD_REG_ADDR_MASK;
353289947Szbb	val |= (phy << PHY_CMD_DEV_ADDR) & PHY_CMD_DEV_ADDR_MASK;
354289947Szbb	e6000sw_writereg(sc, REG_GLOBAL2, SMI_PHY_DATA_REG,
355289947Szbb			 data & PHY_DATA_MASK);
356289947Szbb	e6000sw_writereg(sc, REG_GLOBAL2, SMI_PHY_CMD_REG, val);
357289947Szbb	e6000sw_poll_done(sc);
358289947Szbb
359289947Szbb	return (0);
360289947Szbb}
361289947Szbb
362289947Szbbstatic int
363289947Szbbe6000sw_detach(device_t dev)
364289947Szbb{
365289947Szbb	int phy;
366289947Szbb	e6000sw_softc_t *sc;
367289947Szbb
368289947Szbb	sc = device_get_softc(dev);
369289947Szbb	bus_generic_detach(dev);
370289947Szbb	sx_destroy(&sc->sx);
371289947Szbb	for (phy = 0; phy < E6000SW_NUM_PHYS; phy++) {
372289947Szbb		if (sc->miibus[phy] != NULL)
373289947Szbb			device_delete_child(dev, sc->miibus[phy]);
374289947Szbb		if (sc->ifp[phy] != NULL)
375289947Szbb			if_free(sc->ifp[phy]);
376289947Szbb		if (sc->ifname[phy] != NULL)
377289947Szbb			free(sc->ifname[phy], M_E6000SW);
378289947Szbb	}
379289947Szbb
380289947Szbb	return (0);
381289947Szbb}
382289947Szbb
383289947Szbbstatic etherswitch_info_t*
384289947Szbbe6000sw_getinfo(device_t dev)
385289947Szbb{
386289947Szbb
387289947Szbb	return (&etherswitch_info);
388289947Szbb}
389289947Szbb
390289947Szbbstatic void
391289947Szbbe6000sw_lock(device_t dev)
392289947Szbb{
393289947Szbb	struct e6000sw_softc *sc;
394289947Szbb
395289947Szbb	sc = device_get_softc(dev);
396289947Szbb
397289947Szbb	E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED);
398289947Szbb	E6000SW_LOCK(sc);
399289947Szbb}
400289947Szbb
401289947Szbbstatic void
402289947Szbbe6000sw_unlock(device_t dev)
403289947Szbb{
404289947Szbb	struct e6000sw_softc *sc;
405289947Szbb
406289947Szbb	sc = device_get_softc(dev);
407289947Szbb
408289947Szbb	E6000SW_LOCK_ASSERT(sc, SA_XLOCKED);
409289947Szbb	E6000SW_UNLOCK(sc);
410289947Szbb}
411289947Szbb
412289947Szbbstatic int
413289947Szbbe6000sw_getport(device_t dev, etherswitch_port_t *p)
414289947Szbb{
415289947Szbb	struct mii_data *mii;
416289947Szbb	int err;
417289947Szbb	struct ifmediareq *ifmr;
418289947Szbb
419289947Szbb	err = 0;
420289947Szbb	e6000sw_softc_t *sc = device_get_softc(dev);
421289947Szbb	E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED);
422289947Szbb
423289947Szbb	E6000SW_LOCK(sc);
424289947Szbb
425289947Szbb	if (p->es_port >= E6000SW_NUM_PORTS ||
426289947Szbb	    p->es_port < 0) {
427289947Szbb		err = EINVAL;
428289947Szbb		goto out;
429289947Szbb	}
430289947Szbb
431289947Szbb	e6000sw_get_pvid(sc, p->es_port, &p->es_pvid);
432289947Szbb
433289947Szbb	if (e6000sw_cpuport(sc, p->es_port)) {
434289947Szbb		p->es_flags |= ETHERSWITCH_PORT_CPU;
435289947Szbb		ifmr = &p->es_ifmr;
436289947Szbb		ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
437289947Szbb		ifmr->ifm_count = 0;
438289947Szbb		ifmr->ifm_current = ifmr->ifm_active =
439289947Szbb		    IFM_ETHER | IFM_1000_T | IFM_FDX;
440289947Szbb		ifmr->ifm_mask = 0;
441289947Szbb	} else {
442289947Szbb		mii = e6000sw_miiforphy(sc, p->es_port);
443289947Szbb		err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr,
444289947Szbb		    &mii->mii_media, SIOCGIFMEDIA);
445289947Szbb	}
446289947Szbb
447289947Szbbout:
448289947Szbb	E6000SW_UNLOCK(sc);
449289947Szbb	return (err);
450289947Szbb}
451289947Szbb
452289947Szbbstatic int
453289947Szbbe6000sw_setport(device_t dev, etherswitch_port_t *p)
454289947Szbb{
455289947Szbb	e6000sw_softc_t *sc;
456289947Szbb	int err;
457289947Szbb	struct mii_data *mii;
458289947Szbb
459289947Szbb	err = 0;
460289947Szbb	sc = device_get_softc(dev);
461289947Szbb	E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED);
462289947Szbb
463289947Szbb	E6000SW_LOCK(sc);
464289947Szbb
465289947Szbb	if (p->es_port >= E6000SW_NUM_PORTS ||
466289947Szbb	    p->es_port < 0) {
467289947Szbb		err = EINVAL;
468289947Szbb		goto out;
469289947Szbb	}
470289947Szbb
471289947Szbb	if (p->es_pvid != 0)
472289947Szbb		e6000sw_set_pvid(sc, p->es_port, p->es_pvid);
473289947Szbb	if (!e6000sw_cpuport(sc, p->es_port)) {
474289947Szbb		mii = e6000sw_miiforphy(sc, p->es_port);
475289947Szbb		err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr, &mii->mii_media,
476289947Szbb		    SIOCSIFMEDIA);
477289947Szbb	}
478289947Szbb
479289947Szbbout:
480289947Szbb	E6000SW_UNLOCK(sc);
481289947Szbb	return (err);
482289947Szbb}
483289947Szbb
484289947Szbb/*
485289947Szbb * Registers in this switch are divided into sections, specified in
486289947Szbb * documentation. So as to access any of them, section index and reg index
487289947Szbb * is necessary. etherswitchcfg uses only one variable, so indexes were
488289947Szbb * compressed into addr_reg: 32 * section_index + reg_index.
489289947Szbb */
490289947Szbbstatic int
491289947Szbbe6000sw_readreg_wrapper(device_t dev, int addr_reg)
492289947Szbb{
493289947Szbb
494289947Szbb	if ((addr_reg > (REG_GLOBAL2 * 32 + REG_NUM_MAX)) ||
495289947Szbb	    (addr_reg < (REG_PORT(0) * 32))) {
496289947Szbb		device_printf(dev, "Wrong register address.\n");
497289947Szbb		return (EINVAL);
498289947Szbb	}
499289947Szbb
500289947Szbb	return (e6000sw_readreg(device_get_softc(dev), addr_reg / 32,
501289947Szbb	    addr_reg % 32));
502289947Szbb}
503289947Szbb
504289947Szbbstatic int
505289947Szbbe6000sw_writereg_wrapper(device_t dev, int addr_reg, int val)
506289947Szbb{
507289947Szbb
508289947Szbb	if ((addr_reg > (REG_GLOBAL2 * 32 + REG_NUM_MAX)) ||
509289947Szbb	    (addr_reg < (REG_PORT(0) * 32))) {
510289947Szbb		device_printf(dev, "Wrong register address.\n");
511289947Szbb		return (EINVAL);
512289947Szbb	}
513289947Szbb	e6000sw_writereg(device_get_softc(dev), addr_reg / 5,
514289947Szbb	    addr_reg % 32, val);
515289947Szbb
516289947Szbb	return (0);
517289947Szbb}
518289947Szbb
519289947Szbb/*
520289947Szbb * These wrappers are necessary because PHY accesses from etherswitchcfg
521289947Szbb * need to be synchronized with locks, while miibus PHY accesses do not.
522289947Szbb */
523289947Szbbstatic int
524289947Szbbe6000sw_readphy_wrapper(device_t dev, int phy, int reg)
525289947Szbb{
526289947Szbb	e6000sw_softc_t *sc;
527289947Szbb	int ret;
528289947Szbb
529289947Szbb	sc = device_get_softc(dev);
530289947Szbb	E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED);
531289947Szbb
532289947Szbb	E6000SW_LOCK(sc);
533289947Szbb	ret = e6000sw_readphy(dev, phy, reg);
534289947Szbb	E6000SW_UNLOCK(sc);
535289947Szbb
536289947Szbb	return (ret);
537289947Szbb}
538289947Szbb
539289947Szbbstatic int
540289947Szbbe6000sw_writephy_wrapper(device_t dev, int phy, int reg, int data)
541289947Szbb{
542289947Szbb	e6000sw_softc_t *sc;
543289947Szbb	int ret;
544289947Szbb
545289947Szbb	sc = device_get_softc(dev);
546289947Szbb	E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED);
547289947Szbb
548289947Szbb	E6000SW_LOCK(sc);
549289947Szbb	ret = e6000sw_writephy(dev, phy, reg, data);
550289947Szbb	E6000SW_UNLOCK(sc);
551289947Szbb
552289947Szbb	return (ret);
553289947Szbb}
554289947Szbb
555289947Szbb/*
556289947Szbb * setvgroup/getvgroup called from etherswitchfcg need to be locked,
557289947Szbb * while internal calls do not.
558289947Szbb */
559289947Szbbstatic int
560289947Szbbe6000sw_setvgroup_wrapper(device_t dev, etherswitch_vlangroup_t *vg)
561289947Szbb{
562289947Szbb	e6000sw_softc_t *sc;
563289947Szbb	int ret;
564289947Szbb
565289947Szbb	sc = device_get_softc(dev);
566289947Szbb	E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED);
567289947Szbb
568289947Szbb	E6000SW_LOCK(sc);
569289947Szbb	ret = e6000sw_setvgroup(dev, vg);
570289947Szbb	E6000SW_UNLOCK(sc);
571289947Szbb
572289947Szbb	return (ret);
573289947Szbb}
574289947Szbb
575289947Szbbstatic int
576289947Szbbe6000sw_getvgroup_wrapper(device_t dev, etherswitch_vlangroup_t *vg)
577289947Szbb{
578289947Szbb	e6000sw_softc_t *sc;
579289947Szbb	int ret;
580289947Szbb
581289947Szbb	sc = device_get_softc(dev);
582289947Szbb	E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED);
583289947Szbb
584289947Szbb	E6000SW_LOCK(sc);
585289947Szbb	ret = e6000sw_getvgroup(dev, vg);
586289947Szbb	E6000SW_UNLOCK(sc);
587289947Szbb
588289947Szbb	return (ret);
589289947Szbb}
590289947Szbb
591289947Szbbstatic __inline void
592289947Szbbe6000sw_flush_port(e6000sw_softc_t *sc, int port)
593289947Szbb{
594289947Szbb	uint32_t reg;
595289947Szbb
596289947Szbb	reg = e6000sw_readreg(sc, REG_PORT(port),
597289947Szbb	    PORT_VLAN_MAP);
598289947Szbb	reg &= ~PORT_VLAN_MAP_TABLE_MASK;
599289947Szbb	reg &= ~PORT_VLAN_MAP_FID_MASK;
600289947Szbb	e6000sw_writereg(sc, REG_PORT(port),
601289947Szbb	    PORT_VLAN_MAP, reg);
602289947Szbb	if (sc->vgroup[port] != E6000SW_PORT_NO_VGROUP) {
603289947Szbb		/*
604289947Szbb		 * If port belonged somewhere, owner-group
605289947Szbb		 * should have its entry removed.
606289947Szbb		 */
607289947Szbb		sc->members[sc->vgroup[port]] &= ~(1 << port);
608289947Szbb		sc->vgroup[port] = E6000SW_PORT_NO_VGROUP;
609289947Szbb	}
610289947Szbb}
611289947Szbb
612289947Szbbstatic __inline void
613289947Szbbe6000sw_port_assign_vgroup(e6000sw_softc_t *sc, int port, int fid, int vgroup,
614289947Szbb    int members)
615289947Szbb{
616289947Szbb	uint32_t reg;
617289947Szbb
618289947Szbb	reg = e6000sw_readreg(sc, REG_PORT(port),
619289947Szbb	    PORT_VLAN_MAP);
620289947Szbb	reg &= ~PORT_VLAN_MAP_TABLE_MASK;
621289947Szbb	reg &= ~PORT_VLAN_MAP_FID_MASK;
622289947Szbb	reg |= members & ~(1 << port);
623289947Szbb	reg |= (fid << PORT_VLAN_MAP_FID) & PORT_VLAN_MAP_FID_MASK;
624289947Szbb	e6000sw_writereg(sc, REG_PORT(port), PORT_VLAN_MAP,
625289947Szbb	    reg);
626289947Szbb	sc->vgroup[port] = vgroup;
627289947Szbb}
628289947Szbb
629289947Szbbstatic int
630289947Szbbe6000sw_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
631289947Szbb{
632289947Szbb	e6000sw_softc_t *sc;
633289947Szbb	int port, fid;
634289947Szbb
635289947Szbb	sc = device_get_softc(dev);
636289947Szbb	E6000SW_LOCK_ASSERT(sc, SA_XLOCKED);
637289947Szbb
638289947Szbb	if (vg->es_vlangroup >= E6000SW_NUM_VGROUPS)
639289947Szbb		return (EINVAL);
640289947Szbb	if (vg->es_member_ports != vg->es_untagged_ports) {
641289947Szbb		device_printf(dev, "Tagged ports not supported.\n");
642289947Szbb		return (EINVAL);
643289947Szbb	}
644289947Szbb
645289947Szbb	vg->es_untagged_ports &= PORT_VLAN_MAP_TABLE_MASK;
646289947Szbb	fid = vg->es_vlangroup + 1;
647289947Szbb	for (port = 0; port < E6000SW_NUM_PORTS; port++) {
648289947Szbb		if ((sc->members[vg->es_vlangroup] & (1 << port)) ||
649289947Szbb		    (vg->es_untagged_ports & (1 << port)))
650289947Szbb			e6000sw_flush_port(sc, port);
651289947Szbb		if (vg->es_untagged_ports & (1 << port))
652289947Szbb			e6000sw_port_assign_vgroup(sc, port, fid,
653289947Szbb			    vg->es_vlangroup, vg->es_untagged_ports);
654289947Szbb	}
655289947Szbb	sc->vid[vg->es_vlangroup] = vg->es_vid;
656289947Szbb	sc->members[vg->es_vlangroup] = vg->es_untagged_ports;
657289947Szbb
658289947Szbb	return (0);
659289947Szbb}
660289947Szbb
661289947Szbbstatic int
662289947Szbbe6000sw_getvgroup(device_t dev, etherswitch_vlangroup_t *vg)
663289947Szbb{
664289947Szbb	e6000sw_softc_t *sc;
665289947Szbb
666289947Szbb	sc = device_get_softc(dev);
667289947Szbb	E6000SW_LOCK_ASSERT(sc, SA_XLOCKED);
668289947Szbb
669289947Szbb	if (vg->es_vlangroup >= E6000SW_NUM_VGROUPS)
670289947Szbb		return (EINVAL);
671289947Szbb	vg->es_untagged_ports = vg->es_member_ports =
672289947Szbb	    sc->members[vg->es_vlangroup];
673289947Szbb	vg->es_vid = ETHERSWITCH_VID_VALID;
674289947Szbb
675289947Szbb	return (0);
676289947Szbb}
677289947Szbb
678289947Szbbstatic __inline struct mii_data*
679289947Szbbe6000sw_miiforphy(e6000sw_softc_t *sc, unsigned int phy)
680289947Szbb{
681289947Szbb
682289947Szbb	if (phy >= E6000SW_NUM_PHYS)
683289947Szbb		return (NULL);
684289947Szbb
685289947Szbb	return (device_get_softc(sc->miibus[phy]));
686289947Szbb}
687289947Szbb
688289947Szbbstatic int
689289947Szbbe6000sw_ifmedia_upd(struct ifnet *ifp)
690289947Szbb{
691289947Szbb	e6000sw_softc_t *sc;
692289947Szbb	struct mii_data *mii;
693289947Szbb
694289947Szbb	sc = ifp->if_softc;
695289947Szbb	mii = e6000sw_miiforphy(sc, ifp->if_dunit);
696289947Szbb	if (mii == NULL)
697289947Szbb		return (ENXIO);
698289947Szbb	mii_mediachg(mii);
699289947Szbb
700289947Szbb	return (0);
701289947Szbb}
702289947Szbb
703289947Szbbstatic void
704289947Szbbe6000sw_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
705289947Szbb{
706289947Szbb	e6000sw_softc_t *sc;
707289947Szbb	struct mii_data *mii;
708289947Szbb
709289947Szbb	sc = ifp->if_softc;
710289947Szbb	mii = e6000sw_miiforphy(sc, ifp->if_dunit);
711289947Szbb
712289947Szbb	if (mii == NULL)
713289947Szbb		return;
714289947Szbb
715289947Szbb	mii_pollstat(mii);
716289947Szbb	ifmr->ifm_active = mii->mii_media_active;
717289947Szbb	ifmr->ifm_status = mii->mii_media_status;
718289947Szbb}
719289947Szbb
720289947Szbbstatic __inline uint32_t
721289947Szbbe6000sw_readreg(e6000sw_softc_t *sc, int addr, int reg)
722289947Szbb{
723289947Szbb
724289947Szbb	E6000SW_LOCK_ASSERT(sc, SA_XLOCKED);
725289947Szbb
726289947Szbb	return (MDIO_READREG(device_get_parent(sc->dev), addr, reg));
727289947Szbb}
728289947Szbb
729289947Szbbstatic __inline void
730289947Szbbe6000sw_writereg(e6000sw_softc_t *sc, int addr, int reg, int val)
731289947Szbb{
732289947Szbb
733289947Szbb	E6000SW_LOCK_ASSERT(sc, SA_XLOCKED);
734289947Szbb
735289947Szbb	MDIO_WRITEREG(device_get_parent(sc->dev), addr, reg, val);
736289947Szbb}
737289947Szbb
738289947Szbbstatic __inline int
739289947Szbbe6000sw_cpuport(e6000sw_softc_t *sc, int port)
740289947Szbb{
741289947Szbb
742289947Szbb	return (sc->cpuports_mask & (1 << port));
743289947Szbb}
744289947Szbb
745289947Szbbstatic __inline int
746289947Szbbe6000sw_set_pvid(e6000sw_softc_t *sc, int port, int pvid)
747289947Szbb{
748289947Szbb
749289947Szbb	e6000sw_writereg(sc, REG_PORT(port), PORT_VID, pvid &
750289947Szbb	    PORT_VID_DEF_VID_MASK);
751289947Szbb
752289947Szbb	return (0);
753289947Szbb}
754289947Szbb
755289947Szbbstatic __inline int
756289947Szbbe6000sw_get_pvid(e6000sw_softc_t *sc, int port, int *pvid)
757289947Szbb{
758289947Szbb
759289947Szbb	if (pvid == NULL)
760289947Szbb		return (ENXIO);
761289947Szbb
762289947Szbb	*pvid = e6000sw_readreg(sc, REG_PORT(port), PORT_VID) &
763289947Szbb	    PORT_VID_DEF_VID_MASK;
764289947Szbb
765289947Szbb	return (0);
766289947Szbb}
767289947Szbb
768289947Szbbstatic void
769289947Szbbe6000sw_tick (void *arg)
770289947Szbb{
771289947Szbb	e6000sw_softc_t *sc;
772289947Szbb	struct mii_softc *miisc;
773289947Szbb	int i;
774289947Szbb
775289947Szbb	sc = arg;
776289947Szbb
777289947Szbb	E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED);
778289947Szbb	for (;;) {
779289947Szbb		E6000SW_LOCK(sc);
780289947Szbb		for (i = 0; i < E6000SW_NUM_PHYS; i++) {
781289947Szbb			mii_tick(sc->mii[i]);
782289947Szbb			LIST_FOREACH(miisc, &sc->mii[i]->mii_phys, mii_list) {
783289947Szbb				if (IFM_INST(sc->mii[i]->mii_media.ifm_cur->ifm_media)
784289947Szbb				    != miisc->mii_inst)
785289947Szbb					continue;
786289947Szbb				mii_phy_update(miisc, MII_POLLSTAT);
787289947Szbb			}
788289947Szbb		}
789289947Szbb		E6000SW_UNLOCK(sc);
790289947Szbb		pause("e6000sw tick", 1000);
791289947Szbb	}
792289947Szbb}
793289947Szbb
794289947Szbbstatic void
795289947Szbbe6000sw_setup(device_t dev, e6000sw_softc_t *sc)
796289947Szbb{
797289947Szbb	uint16_t atu_ctrl, atu_age;
798289947Szbb
799289947Szbb	/* Set aging time */
800289947Szbb	e6000sw_writereg(sc, REG_GLOBAL, ATU_CONTROL,
801289947Szbb	    (E6000SW_DEFAULT_AGETIME << ATU_CONTROL_AGETIME) |
802289947Szbb	    (1 << ATU_CONTROL_LEARN2ALL));
803289947Szbb
804289947Szbb	/* Send all with specific mac address to cpu port */
805289947Szbb	e6000sw_writereg(sc, REG_GLOBAL2, MGMT_EN_2x, MGMT_EN_ALL);
806289947Szbb	e6000sw_writereg(sc, REG_GLOBAL2, MGMT_EN_0x, MGMT_EN_ALL);
807289947Szbb
808298955Spfg	/* Disable Remote Management */
809289947Szbb	e6000sw_writereg(sc, REG_GLOBAL, SWITCH_GLOBAL_CONTROL2, 0);
810289947Szbb
811289947Szbb	/* Disable loopback filter and flow control messages */
812289947Szbb	e6000sw_writereg(sc, REG_GLOBAL2, SWITCH_MGMT,
813289947Szbb	    SWITCH_MGMT_PRI_MASK |
814289947Szbb	    (1 << SWITCH_MGMT_RSVD2CPU) |
815289947Szbb	    SWITCH_MGMT_FC_PRI_MASK |
816289947Szbb	    (1 << SWITCH_MGMT_FORCEFLOW));
817289947Szbb
818289947Szbb	/* Set VLAN configuration */
819289947Szbb	e6000sw_port_vlan_conf(sc);
820289947Szbb
821289947Szbb	e6000sw_atu_flush(dev, sc, NO_OPERATION);
822289947Szbb	e6000sw_atu_mac_table(dev, sc, NULL, NO_OPERATION);
823289947Szbb	e6000sw_set_atustat(dev, sc, 0, COUNT_ALL);
824289947Szbb
825289947Szbb	/* Set ATU AgeTime to 15 seconds */
826289947Szbb	atu_age = 1;
827289947Szbb
828289947Szbb	atu_ctrl = e6000sw_readreg(sc, REG_GLOBAL, ATU_CONTROL);
829289947Szbb
830289947Szbb	/* Set new AgeTime field */
831289947Szbb	atu_ctrl &= ~ATU_CONTROL_AGETIME_MASK;
832289947Szbb	e6000sw_writereg(sc, REG_GLOBAL, ATU_CONTROL, atu_ctrl |
833289947Szbb	    (atu_age << ATU_CONTROL_AGETIME));
834289947Szbb}
835289947Szbb
836289947Szbbstatic void
837289947Szbbe6000sw_port_vlan_conf(e6000sw_softc_t *sc)
838289947Szbb{
839289947Szbb	int port, ret;
840289947Szbb	etherswitch_vlangroup_t vg;
841289947Szbb	device_t dev;
842289947Szbb
843289947Szbb	dev = sc->dev;
844289947Szbb	/* Disable all ports */
845289947Szbb	for (port = 0; port < E6000SW_NUM_PORTS; port++) {
846289947Szbb		ret = e6000sw_readreg(sc, REG_PORT(port), PORT_CONTROL);
847289947Szbb		e6000sw_writereg(sc, REG_PORT(port), PORT_CONTROL,
848289947Szbb		    (ret & ~PORT_CONTROL_ENABLE));
849289947Szbb	}
850289947Szbb
851289947Szbb	/* Set port priority */
852289947Szbb	for (port = 0; port < E6000SW_NUM_PORTS; port++) {
853289947Szbb		ret = e6000sw_readreg(sc, REG_PORT(port), PORT_VID);
854289947Szbb		ret &= ~PORT_VID_PRIORITY_MASK;
855289947Szbb		e6000sw_writereg(sc, REG_PORT(port), PORT_VID, ret);
856289947Szbb	}
857289947Szbb
858289947Szbb	vg.es_vlangroup = 0;
859289947Szbb	vg.es_vid = 0;
860289947Szbb	vg.es_member_ports = vg.es_untagged_ports = E6000SW_DEF_VLANGROUP0;
861289947Szbb	e6000sw_setvgroup(dev, &vg);
862289947Szbb	vg.es_vlangroup = 1;
863289947Szbb	vg.es_vid = 1;
864289947Szbb	vg.es_member_ports = vg.es_untagged_ports = E6000SW_DEF_VLANGROUP1;
865289947Szbb	e6000sw_setvgroup(dev, &vg);
866289947Szbb
867289947Szbb	device_printf(dev, "Default vlangroups set.\n");
868289947Szbb	/* Set VID map */
869289947Szbb	for (port = 0; port < E6000SW_NUM_PORTS; port++) {
870289947Szbb		ret = e6000sw_readreg(sc, REG_PORT(port), PORT_VID);
871289947Szbb		ret &= ~PORT_VID_DEF_VID_MASK;
872289947Szbb		ret |= (port + 1);
873289947Szbb		e6000sw_writereg(sc, REG_PORT(port), PORT_VID, ret);
874289947Szbb	}
875289947Szbb
876289947Szbb	/* Enable all ports */
877289947Szbb	for (port = 0; port < E6000SW_NUM_PORTS; port++) {
878289947Szbb		ret = e6000sw_readreg(sc, REG_PORT(port), PORT_CONTROL);
879289947Szbb		e6000sw_writereg(sc, REG_PORT(port), PORT_CONTROL, (ret |
880289947Szbb		    PORT_CONTROL_ENABLE));
881289947Szbb	}
882289947Szbb}
883289947Szbb
884289947Szbbstatic void
885289947Szbbe6000sw_set_atustat(device_t dev, e6000sw_softc_t *sc, int bin, int flag)
886289947Szbb{
887289947Szbb	uint16_t ret;
888289947Szbb
889289947Szbb	ret = e6000sw_readreg(sc, REG_GLOBAL2, ATU_STATS);
890289947Szbb	e6000sw_writereg(sc, REG_GLOBAL2, ATU_STATS, (bin << ATU_STATS_BIN ) |
891289947Szbb	    (flag << ATU_STATS_FLAG));
892289947Szbb}
893289947Szbb
894289947Szbbstatic int
895289947Szbbe6000sw_atu_mac_table(device_t dev, e6000sw_softc_t *sc, struct atu_opt *atu,
896289947Szbb    int flag)
897289947Szbb{
898289947Szbb	uint16_t ret_opt;
899289947Szbb	uint16_t ret_data;
900289947Szbb	int retries;
901289947Szbb
902289947Szbb	if (flag == NO_OPERATION)
903289947Szbb		return (0);
904289947Szbb	else if ((flag & (LOAD_FROM_FIB | PURGE_FROM_FIB | GET_NEXT_IN_FIB |
905289947Szbb	    GET_VIOLATION_DATA | CLEAR_VIOLATION_DATA)) == 0) {
906289947Szbb		device_printf(dev, "Wrong Opcode for ATU operation\n");
907289947Szbb		return (EINVAL);
908289947Szbb	}
909289947Szbb
910289947Szbb	ret_opt = e6000sw_readreg(sc, REG_GLOBAL, ATU_OPERATION);
911289947Szbb
912289947Szbb	if (ret_opt & ATU_UNIT_BUSY) {
913289947Szbb		device_printf(dev, "ATU unit is busy, cannot access"
914289947Szbb		    "register\n");
915289947Szbb		return (EBUSY);
916289947Szbb	} else {
917289947Szbb		if(flag & LOAD_FROM_FIB) {
918289947Szbb			ret_data = e6000sw_readreg(sc, REG_GLOBAL, ATU_DATA);
919289947Szbb			e6000sw_writereg(sc, REG_GLOBAL2, ATU_DATA, (ret_data &
920289947Szbb			    ~ENTRY_STATE));
921289947Szbb		}
922289947Szbb		e6000sw_writereg(sc, REG_GLOBAL, ATU_MAC_ADDR01, atu->mac_01);
923289947Szbb		e6000sw_writereg(sc, REG_GLOBAL, ATU_MAC_ADDR23, atu->mac_23);
924289947Szbb		e6000sw_writereg(sc, REG_GLOBAL, ATU_MAC_ADDR45, atu->mac_45);
925289947Szbb		e6000sw_writereg(sc, REG_GLOBAL, ATU_FID, atu->fid);
926289947Szbb
927289947Szbb		e6000sw_writereg(sc, REG_GLOBAL, ATU_OPERATION, (ret_opt |
928289947Szbb		    ATU_UNIT_BUSY | flag));
929289947Szbb
930289947Szbb		retries = E6000SW_RETRIES;
931289947Szbb		while (--retries & (e6000sw_readreg(sc, REG_GLOBAL,
932289947Szbb		    ATU_OPERATION) & ATU_UNIT_BUSY))
933289947Szbb			DELAY(1);
934289947Szbb
935289947Szbb		if (retries == 0)
936289947Szbb			device_printf(dev, "Timeout while flushing\n");
937289947Szbb		else if (flag & GET_NEXT_IN_FIB) {
938289947Szbb			atu->mac_01 = e6000sw_readreg(sc, REG_GLOBAL,
939289947Szbb			    ATU_MAC_ADDR01);
940289947Szbb			atu->mac_23 = e6000sw_readreg(sc, REG_GLOBAL,
941289947Szbb			    ATU_MAC_ADDR23);
942289947Szbb			atu->mac_45 = e6000sw_readreg(sc, REG_GLOBAL,
943289947Szbb			    ATU_MAC_ADDR45);
944289947Szbb		}
945289947Szbb	}
946289947Szbb
947289947Szbb	return (0);
948289947Szbb}
949289947Szbb
950289947Szbbstatic int
951289947Szbbe6000sw_atu_flush(device_t dev, e6000sw_softc_t *sc, int flag)
952289947Szbb{
953289947Szbb	uint16_t ret;
954289947Szbb	int retries;
955289947Szbb
956289947Szbb	if (flag == NO_OPERATION)
957289947Szbb		return (0);
958289947Szbb
959289947Szbb	ret = e6000sw_readreg(sc, REG_GLOBAL, ATU_OPERATION);
960289947Szbb	if (ret & ATU_UNIT_BUSY) {
961289947Szbb		device_printf(dev, "Atu unit is busy, cannot flush\n");
962289947Szbb		return (EBUSY);
963289947Szbb	} else {
964289947Szbb		e6000sw_writereg(sc, REG_GLOBAL, ATU_OPERATION, (ret |
965289947Szbb		    ATU_UNIT_BUSY | flag));
966289947Szbb		retries = E6000SW_RETRIES;
967289947Szbb		while (--retries & (e6000sw_readreg(sc, REG_GLOBAL,
968289947Szbb		    ATU_OPERATION) & ATU_UNIT_BUSY))
969289947Szbb			DELAY(1);
970289947Szbb
971289947Szbb		if (retries == 0)
972289947Szbb			device_printf(dev, "Timeout while flushing\n");
973289947Szbb	}
974289947Szbb
975289947Szbb	return (0);
976289947Szbb}
977