xref: /freebsd/sys/dev/etherswitch/e6000sw/e6000sw.c (revision b670c9bafc0e31c7609969bf374b2e80bdc00211)
15420071dSZbigniew Bodek /*-
25420071dSZbigniew Bodek  * Copyright (c) 2015 Semihalf
35420071dSZbigniew Bodek  * Copyright (c) 2015 Stormshield
4d7cecbd1SLuiz Otavio O Souza  * Copyright (c) 2018-2019, Rubicon Communications, LLC (Netgate)
55420071dSZbigniew Bodek  * All rights reserved.
65420071dSZbigniew Bodek  *
75420071dSZbigniew Bodek  * Redistribution and use in source and binary forms, with or without
85420071dSZbigniew Bodek  * modification, are permitted provided that the following conditions
95420071dSZbigniew Bodek  * are met:
105420071dSZbigniew Bodek  * 1. Redistributions of source code must retain the above copyright
115420071dSZbigniew Bodek  *    notice, this list of conditions and the following disclaimer.
125420071dSZbigniew Bodek  * 2. Redistributions in binary form must reproduce the above copyright
135420071dSZbigniew Bodek  *    notice, this list of conditions and the following disclaimer in the
145420071dSZbigniew Bodek  *    documentation and/or other materials provided with the distribution.
155420071dSZbigniew Bodek  *
165420071dSZbigniew Bodek  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
175420071dSZbigniew Bodek  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
185420071dSZbigniew Bodek  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
195420071dSZbigniew Bodek  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
205420071dSZbigniew Bodek  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
215420071dSZbigniew Bodek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
225420071dSZbigniew Bodek  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
235420071dSZbigniew Bodek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
245420071dSZbigniew Bodek  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
255420071dSZbigniew Bodek  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
265420071dSZbigniew Bodek  * SUCH DAMAGE.
275420071dSZbigniew Bodek  */
285420071dSZbigniew Bodek 
295420071dSZbigniew Bodek #include <sys/cdefs.h>
302c135a2aSLuiz Otavio O Souza #include "opt_platform.h"
312c135a2aSLuiz Otavio O Souza 
325420071dSZbigniew Bodek #include <sys/param.h>
3333c2a3cbSLuiz Otavio O Souza #include <sys/bus.h>
3433c2a3cbSLuiz Otavio O Souza #include <sys/errno.h>
355420071dSZbigniew Bodek #include <sys/kernel.h>
365420071dSZbigniew Bodek #include <sys/kthread.h>
375420071dSZbigniew Bodek #include <sys/module.h>
381e125351SHubert Mazur #include <sys/taskqueue.h>
3933c2a3cbSLuiz Otavio O Souza #include <sys/socket.h>
4033c2a3cbSLuiz Otavio O Souza #include <sys/sockio.h>
415420071dSZbigniew Bodek 
425420071dSZbigniew Bodek #include <net/if.h>
435420071dSZbigniew Bodek #include <net/if_media.h>
445420071dSZbigniew Bodek #include <net/if_types.h>
455420071dSZbigniew Bodek 
465420071dSZbigniew Bodek #include <dev/etherswitch/etherswitch.h>
475420071dSZbigniew Bodek #include <dev/mii/mii.h>
485420071dSZbigniew Bodek #include <dev/mii/miivar.h>
495420071dSZbigniew Bodek 
502c135a2aSLuiz Otavio O Souza #ifdef FDT
51f7c13d78SZbigniew Bodek #include <dev/ofw/ofw_bus.h>
520fd68d72SMarcin Wojtas #include <dev/ofw/ofw_bus_subr.h>
532c135a2aSLuiz Otavio O Souza #else
54e453e498SBrooks Davis #include <sys/stdarg.h>
552c135a2aSLuiz Otavio O Souza #endif
56f7c13d78SZbigniew Bodek 
575420071dSZbigniew Bodek #include "e6000swreg.h"
585420071dSZbigniew Bodek #include "etherswitch_if.h"
595420071dSZbigniew Bodek #include "miibus_if.h"
605420071dSZbigniew Bodek #include "mdio_if.h"
615420071dSZbigniew Bodek 
625420071dSZbigniew Bodek MALLOC_DECLARE(M_E6000SW);
635420071dSZbigniew Bodek MALLOC_DEFINE(M_E6000SW, "e6000sw", "e6000sw switch");
645420071dSZbigniew Bodek 
6533c2a3cbSLuiz Otavio O Souza #define	E6000SW_LOCK(_sc)		sx_xlock(&(_sc)->sx)
6633c2a3cbSLuiz Otavio O Souza #define	E6000SW_UNLOCK(_sc)		sx_unlock(&(_sc)->sx)
6733c2a3cbSLuiz Otavio O Souza #define	E6000SW_LOCK_ASSERT(_sc, _what)	sx_assert(&(_sc)->sx, (_what))
6833c2a3cbSLuiz Otavio O Souza #define	E6000SW_TRYLOCK(_sc)		sx_tryxlock(&(_sc)->sx)
69725962a9SMark Johnston #define	E6000SW_LOCKED(_sc)		sx_xlocked(&(_sc)->sx)
70d7cecbd1SLuiz Otavio O Souza #define	E6000SW_WAITREADY(_sc, _reg, _bit)				\
71d7cecbd1SLuiz Otavio O Souza     e6000sw_waitready((_sc), REG_GLOBAL, (_reg), (_bit))
72d7cecbd1SLuiz Otavio O Souza #define	E6000SW_WAITREADY2(_sc, _reg, _bit)				\
73d7cecbd1SLuiz Otavio O Souza     e6000sw_waitready((_sc), REG_GLOBAL2, (_reg), (_bit))
74d7cecbd1SLuiz Otavio O Souza #define	MDIO_READ(dev, addr, reg)					\
75d7cecbd1SLuiz Otavio O Souza     MDIO_READREG(device_get_parent(dev), (addr), (reg))
76d7cecbd1SLuiz Otavio O Souza #define	MDIO_WRITE(dev, addr, reg, val)					\
77d7cecbd1SLuiz Otavio O Souza     MDIO_WRITEREG(device_get_parent(dev), (addr), (reg), (val))
78d7cecbd1SLuiz Otavio O Souza 
795420071dSZbigniew Bodek 
805420071dSZbigniew Bodek typedef struct e6000sw_softc {
815420071dSZbigniew Bodek 	device_t		dev;
822c135a2aSLuiz Otavio O Souza #ifdef FDT
83f7c13d78SZbigniew Bodek 	phandle_t		node;
842c135a2aSLuiz Otavio O Souza #endif
855420071dSZbigniew Bodek 
865420071dSZbigniew Bodek 	struct sx		sx;
872e6a8c1aSJustin Hibbits 	if_t ifp[E6000SW_MAX_PORTS];
88f7c13d78SZbigniew Bodek 	char			*ifname[E6000SW_MAX_PORTS];
89f7c13d78SZbigniew Bodek 	device_t		miibus[E6000SW_MAX_PORTS];
901e125351SHubert Mazur 	struct taskqueue	*sc_tq;
911e125351SHubert Mazur 	struct timeout_task	sc_tt;
92ee7f62faSAdrian Chadd 	bool			is_shutdown;
935420071dSZbigniew Bodek 
94d7cecbd1SLuiz Otavio O Souza 	int			vlans[E6000SW_NUM_VLANS];
95091d140cSLuiz Otavio O Souza 	uint32_t		swid;
960e779c2fSLuiz Otavio O Souza 	uint32_t		vlan_mode;
975420071dSZbigniew Bodek 	uint32_t		cpuports_mask;
98f7c13d78SZbigniew Bodek 	uint32_t		fixed_mask;
99091d140cSLuiz Otavio O Souza 	uint32_t		fixed25_mask;
100ff2748ecSLuiz Otavio O Souza 	uint32_t		ports_mask;
101ff2748ecSLuiz Otavio O Souza 	int			phy_base;
102f7c13d78SZbigniew Bodek 	int			sw_addr;
103f7c13d78SZbigniew Bodek 	int			num_ports;
1045420071dSZbigniew Bodek } e6000sw_softc_t;
1055420071dSZbigniew Bodek 
1065420071dSZbigniew Bodek static etherswitch_info_t etherswitch_info = {
107f7c13d78SZbigniew Bodek 	.es_nports =		0,
1080e779c2fSLuiz Otavio O Souza 	.es_nvlangroups =	0,
109d7cecbd1SLuiz Otavio O Souza 	.es_vlan_caps =		ETHERSWITCH_VLAN_PORT | ETHERSWITCH_VLAN_DOT1Q,
1105420071dSZbigniew Bodek 	.es_name =		"Marvell 6000 series switch"
1115420071dSZbigniew Bodek };
1125420071dSZbigniew Bodek 
11333c2a3cbSLuiz Otavio O Souza static void e6000sw_identify(driver_t *, device_t);
11433c2a3cbSLuiz Otavio O Souza static int e6000sw_probe(device_t);
1152c135a2aSLuiz Otavio O Souza #ifdef FDT
1160fd68d72SMarcin Wojtas static int e6000sw_parse_fixed_link(e6000sw_softc_t *, phandle_t, uint32_t);
1170fd68d72SMarcin Wojtas static int e6000sw_parse_ethernet(e6000sw_softc_t *, phandle_t, uint32_t);
1182c135a2aSLuiz Otavio O Souza #endif
11933c2a3cbSLuiz Otavio O Souza static int e6000sw_attach(device_t);
12033c2a3cbSLuiz Otavio O Souza static int e6000sw_detach(device_t);
1219aba0637SLuiz Otavio O Souza static int e6000sw_read_xmdio(device_t, int, int, int);
1229aba0637SLuiz Otavio O Souza static int e6000sw_write_xmdio(device_t, int, int, int, int);
12333c2a3cbSLuiz Otavio O Souza static int e6000sw_readphy(device_t, int, int);
12433c2a3cbSLuiz Otavio O Souza static int e6000sw_writephy(device_t, int, int, int);
12546929064SAlbert Jakiela static int e6000sw_readphy_locked(device_t, int, int);
12646929064SAlbert Jakiela static int e6000sw_writephy_locked(device_t, int, int, int);
12733c2a3cbSLuiz Otavio O Souza static etherswitch_info_t* e6000sw_getinfo(device_t);
128ff2748ecSLuiz Otavio O Souza static int e6000sw_getconf(device_t, etherswitch_conf_t *);
129d7cecbd1SLuiz Otavio O Souza static int e6000sw_setconf(device_t, etherswitch_conf_t *);
13033c2a3cbSLuiz Otavio O Souza static void e6000sw_lock(device_t);
13133c2a3cbSLuiz Otavio O Souza static void e6000sw_unlock(device_t);
13233c2a3cbSLuiz Otavio O Souza static int e6000sw_getport(device_t, etherswitch_port_t *);
13333c2a3cbSLuiz Otavio O Souza static int e6000sw_setport(device_t, etherswitch_port_t *);
134d7cecbd1SLuiz Otavio O Souza static int e6000sw_set_vlan_mode(e6000sw_softc_t *, uint32_t);
13533c2a3cbSLuiz Otavio O Souza static int e6000sw_readreg_wrapper(device_t, int);
13633c2a3cbSLuiz Otavio O Souza static int e6000sw_writereg_wrapper(device_t, int, int);
13733c2a3cbSLuiz Otavio O Souza static int e6000sw_getvgroup_wrapper(device_t, etherswitch_vlangroup_t *);
13833c2a3cbSLuiz Otavio O Souza static int e6000sw_setvgroup_wrapper(device_t, etherswitch_vlangroup_t *);
13933c2a3cbSLuiz Otavio O Souza static int e6000sw_setvgroup(device_t, etherswitch_vlangroup_t *);
14033c2a3cbSLuiz Otavio O Souza static int e6000sw_getvgroup(device_t, etherswitch_vlangroup_t *);
14133c2a3cbSLuiz Otavio O Souza static void e6000sw_setup(device_t, e6000sw_softc_t *);
1421e125351SHubert Mazur static void e6000sw_tick(void *, int);
14333c2a3cbSLuiz Otavio O Souza static void e6000sw_set_atustat(device_t, e6000sw_softc_t *, int, int);
14433c2a3cbSLuiz Otavio O Souza static int e6000sw_atu_flush(device_t, e6000sw_softc_t *, int);
145d7cecbd1SLuiz Otavio O Souza static int e6000sw_vtu_flush(e6000sw_softc_t *);
146d7cecbd1SLuiz Otavio O Souza static int e6000sw_vtu_update(e6000sw_softc_t *, int, int, int, int, int);
14733c2a3cbSLuiz Otavio O Souza static __inline void e6000sw_writereg(e6000sw_softc_t *, int, int, int);
14833c2a3cbSLuiz Otavio O Souza static __inline uint32_t e6000sw_readreg(e6000sw_softc_t *, int, int);
1492e6a8c1aSJustin Hibbits static int e6000sw_ifmedia_upd(if_t);
1502e6a8c1aSJustin Hibbits static void e6000sw_ifmedia_sts(if_t, struct ifmediareq *);
15133c2a3cbSLuiz Otavio O Souza static int e6000sw_atu_mac_table(device_t, e6000sw_softc_t *, struct atu_opt *,
15233c2a3cbSLuiz Otavio O Souza     int);
15333c2a3cbSLuiz Otavio O Souza static int e6000sw_get_pvid(e6000sw_softc_t *, int, int *);
154d7cecbd1SLuiz Otavio O Souza static void e6000sw_set_pvid(e6000sw_softc_t *, int, int);
155595d629cSLuiz Otavio O Souza static __inline bool e6000sw_is_cpuport(e6000sw_softc_t *, int);
156595d629cSLuiz Otavio O Souza static __inline bool e6000sw_is_fixedport(e6000sw_softc_t *, int);
157091d140cSLuiz Otavio O Souza static __inline bool e6000sw_is_fixed25port(e6000sw_softc_t *, int);
158595d629cSLuiz Otavio O Souza static __inline bool e6000sw_is_phyport(e6000sw_softc_t *, int);
159ff2748ecSLuiz Otavio O Souza static __inline bool e6000sw_is_portenabled(e6000sw_softc_t *, int);
16033c2a3cbSLuiz Otavio O Souza static __inline struct mii_data *e6000sw_miiforphy(e6000sw_softc_t *,
16133c2a3cbSLuiz Otavio O Souza     unsigned int);
1625420071dSZbigniew Bodek 
1635420071dSZbigniew Bodek static device_method_t e6000sw_methods[] = {
1645420071dSZbigniew Bodek 	/* device interface */
1655420071dSZbigniew Bodek 	DEVMETHOD(device_identify,		e6000sw_identify),
1665420071dSZbigniew Bodek 	DEVMETHOD(device_probe,			e6000sw_probe),
1675420071dSZbigniew Bodek 	DEVMETHOD(device_attach,		e6000sw_attach),
1685420071dSZbigniew Bodek 	DEVMETHOD(device_detach,		e6000sw_detach),
1695420071dSZbigniew Bodek 
1705420071dSZbigniew Bodek 	/* bus interface */
1715420071dSZbigniew Bodek 	DEVMETHOD(bus_add_child,		device_add_child_ordered),
1725420071dSZbigniew Bodek 
1735420071dSZbigniew Bodek 	/* mii interface */
174725962a9SMark Johnston 	DEVMETHOD(miibus_readreg,		e6000sw_readphy),
175725962a9SMark Johnston 	DEVMETHOD(miibus_writereg,		e6000sw_writephy),
1765420071dSZbigniew Bodek 
1775420071dSZbigniew Bodek 	/* etherswitch interface */
1785420071dSZbigniew Bodek 	DEVMETHOD(etherswitch_getinfo,		e6000sw_getinfo),
179ff2748ecSLuiz Otavio O Souza 	DEVMETHOD(etherswitch_getconf,		e6000sw_getconf),
180d7cecbd1SLuiz Otavio O Souza 	DEVMETHOD(etherswitch_setconf,		e6000sw_setconf),
1815420071dSZbigniew Bodek 	DEVMETHOD(etherswitch_lock,		e6000sw_lock),
1825420071dSZbigniew Bodek 	DEVMETHOD(etherswitch_unlock,		e6000sw_unlock),
1835420071dSZbigniew Bodek 	DEVMETHOD(etherswitch_getport,		e6000sw_getport),
1845420071dSZbigniew Bodek 	DEVMETHOD(etherswitch_setport,		e6000sw_setport),
1855420071dSZbigniew Bodek 	DEVMETHOD(etherswitch_readreg,		e6000sw_readreg_wrapper),
1865420071dSZbigniew Bodek 	DEVMETHOD(etherswitch_writereg,		e6000sw_writereg_wrapper),
187ee1b7811SHubert Mazur 	DEVMETHOD(etherswitch_readphyreg,	e6000sw_readphy),
188ee1b7811SHubert Mazur 	DEVMETHOD(etherswitch_writephyreg,	e6000sw_writephy),
1895420071dSZbigniew Bodek 	DEVMETHOD(etherswitch_setvgroup,	e6000sw_setvgroup_wrapper),
1905420071dSZbigniew Bodek 	DEVMETHOD(etherswitch_getvgroup,	e6000sw_getvgroup_wrapper),
1915420071dSZbigniew Bodek 
1925420071dSZbigniew Bodek 	DEVMETHOD_END
1935420071dSZbigniew Bodek };
1945420071dSZbigniew Bodek 
1955420071dSZbigniew Bodek DEFINE_CLASS_0(e6000sw, e6000sw_driver, e6000sw_methods,
1965420071dSZbigniew Bodek     sizeof(e6000sw_softc_t));
1975420071dSZbigniew Bodek 
19842726c2fSJohn Baldwin DRIVER_MODULE(e6000sw, mdio, e6000sw_driver, 0, 0);
1993e38757dSJohn Baldwin DRIVER_MODULE(miibus, e6000sw, miibus_driver, 0, 0);
200c8e97aa6SAdrian Chadd DRIVER_MODULE_ORDERED(etherswitch, e6000sw, etherswitch_driver, 0, 0, SI_ORDER_ANY);
2015420071dSZbigniew Bodek MODULE_DEPEND(e6000sw, mdio, 1, 1, 1);
202322f3f58SAdrian Chadd MODULE_DEPEND(e6000sw, etherswitch, 1, 1, 1);
20333c2a3cbSLuiz Otavio O Souza 
2045420071dSZbigniew Bodek static void
e6000sw_identify(driver_t * driver,device_t parent)2055420071dSZbigniew Bodek e6000sw_identify(driver_t *driver, device_t parent)
2065420071dSZbigniew Bodek {
2075420071dSZbigniew Bodek 
208*b670c9baSAhmad Khalifa 	if (device_find_child(parent, "e6000sw", DEVICE_UNIT_ANY) == NULL)
209a05a6804SWarner Losh 		BUS_ADD_CHILD(parent, 0, "e6000sw", DEVICE_UNIT_ANY);
2105420071dSZbigniew Bodek }
2115420071dSZbigniew Bodek 
2125420071dSZbigniew Bodek static int
e6000sw_probe(device_t dev)2135420071dSZbigniew Bodek e6000sw_probe(device_t dev)
2145420071dSZbigniew Bodek {
2155420071dSZbigniew Bodek 	e6000sw_softc_t *sc;
2165420071dSZbigniew Bodek 	const char *description;
2172c135a2aSLuiz Otavio O Souza #ifdef FDT
2180fd68d72SMarcin Wojtas 	phandle_t switch_node;
2192c135a2aSLuiz Otavio O Souza #else
2209b2a503aSAdrian Chadd 	int is_6190 = 0;
2219b2a503aSAdrian Chadd 	int is_6190x = 0;
2222c135a2aSLuiz Otavio O Souza #endif
223f7c13d78SZbigniew Bodek 
2249aba0637SLuiz Otavio O Souza 	sc = device_get_softc(dev);
2252c135a2aSLuiz Otavio O Souza 	sc->dev = dev;
2262c135a2aSLuiz Otavio O Souza 
2272c135a2aSLuiz Otavio O Souza #ifdef FDT
2280fd68d72SMarcin Wojtas 	switch_node = ofw_bus_find_compatible(OF_finddevice("/"),
2290fd68d72SMarcin Wojtas 	    "marvell,mv88e6085");
2309aba0637SLuiz Otavio O Souza 	if (switch_node == 0) {
2319aba0637SLuiz Otavio O Souza 		switch_node = ofw_bus_find_compatible(OF_finddevice("/"),
2329aba0637SLuiz Otavio O Souza 		    "marvell,mv88e6190");
233f7c13d78SZbigniew Bodek 
234f7c13d78SZbigniew Bodek 		if (switch_node == 0)
235f7c13d78SZbigniew Bodek 			return (ENXIO);
2365420071dSZbigniew Bodek 
2379aba0637SLuiz Otavio O Souza 		/*
2389aba0637SLuiz Otavio O Souza 		 * Trust DTS and fix the port register offset for the MV88E6190
2399aba0637SLuiz Otavio O Souza 		 * detection bellow.
2409aba0637SLuiz Otavio O Souza 		 */
2419aba0637SLuiz Otavio O Souza 		sc->swid = MV88E6190;
2429aba0637SLuiz Otavio O Souza 	}
2439aba0637SLuiz Otavio O Souza 
2440fd68d72SMarcin Wojtas 	if (bootverbose)
2450fd68d72SMarcin Wojtas 		device_printf(dev, "Found switch_node: 0x%x\n", switch_node);
2460fd68d72SMarcin Wojtas 
247f7c13d78SZbigniew Bodek 	sc->node = switch_node;
248f7c13d78SZbigniew Bodek 
249f7c13d78SZbigniew Bodek 	if (OF_getencprop(sc->node, "reg", &sc->sw_addr,
250f7c13d78SZbigniew Bodek 	    sizeof(sc->sw_addr)) < 0)
251f7c13d78SZbigniew Bodek 		return (ENXIO);
2522c135a2aSLuiz Otavio O Souza #else
2532c135a2aSLuiz Otavio O Souza 	if (resource_int_value(device_get_name(sc->dev),
2542c135a2aSLuiz Otavio O Souza 	    device_get_unit(sc->dev), "addr", &sc->sw_addr) != 0)
2552c135a2aSLuiz Otavio O Souza 		return (ENXIO);
2562c135a2aSLuiz Otavio O Souza 	if (resource_int_value(device_get_name(sc->dev),
2579b2a503aSAdrian Chadd 	    device_get_unit(sc->dev), "is6190", &is_6190) != 0) {
2582c135a2aSLuiz Otavio O Souza 		/*
2592c135a2aSLuiz Otavio O Souza 		 * Check "is8190" to keep backward compatibility with
2602c135a2aSLuiz Otavio O Souza 		 * older setups.
2612c135a2aSLuiz Otavio O Souza 		 */
2622c135a2aSLuiz Otavio O Souza 		resource_int_value(device_get_name(sc->dev),
2632c135a2aSLuiz Otavio O Souza 		    device_get_unit(sc->dev), "is8190", &is_6190);
2649b2a503aSAdrian Chadd 	}
2659b2a503aSAdrian Chadd 	resource_int_value(device_get_name(sc->dev),
2669b2a503aSAdrian Chadd 	    device_get_unit(sc->dev), "is6190x", &is_6190x);
2679b2a503aSAdrian Chadd 		if (is_6190 != 0 && is_6190x != 0) {
2689b2a503aSAdrian Chadd 			device_printf(dev,
2699b2a503aSAdrian Chadd 			    "Cannot configure conflicting variants (6190 / 6190x)\n");
2709b2a503aSAdrian Chadd 			return (ENXIO);
2719b2a503aSAdrian Chadd 		}
2722c135a2aSLuiz Otavio O Souza 	if (is_6190 != 0)
2732c135a2aSLuiz Otavio O Souza 		sc->swid = MV88E6190;
2749b2a503aSAdrian Chadd 	else if (is_6190x != 0)
2759b2a503aSAdrian Chadd 		sc->swid = MV88E6190X;
2762c135a2aSLuiz Otavio O Souza #endif
277d7cecbd1SLuiz Otavio O Souza 	if (sc->sw_addr < 0 || sc->sw_addr > 32)
278d7cecbd1SLuiz Otavio O Souza 		return (ENXIO);
279f7c13d78SZbigniew Bodek 
280eb3ffa57SZbigniew Bodek 	/*
281eb3ffa57SZbigniew Bodek 	 * Create temporary lock, just to satisfy assertions,
282eb3ffa57SZbigniew Bodek 	 * when obtaining the switch ID. Destroy immediately afterwards.
283eb3ffa57SZbigniew Bodek 	 */
284eb3ffa57SZbigniew Bodek 	sx_init(&sc->sx, "e6000sw_tmp");
2855420071dSZbigniew Bodek 	E6000SW_LOCK(sc);
2869aba0637SLuiz Otavio O Souza 	sc->swid = e6000sw_readreg(sc, REG_PORT(sc, 0), SWITCH_ID) & 0xfff0;
28733c2a3cbSLuiz Otavio O Souza 	E6000SW_UNLOCK(sc);
288eb3ffa57SZbigniew Bodek 	sx_destroy(&sc->sx);
2895420071dSZbigniew Bodek 
290091d140cSLuiz Otavio O Souza 	switch (sc->swid) {
291091d140cSLuiz Otavio O Souza 	case MV88E6141:
292ff2748ecSLuiz Otavio O Souza 		description = "Marvell 88E6141";
293ff2748ecSLuiz Otavio O Souza 		sc->phy_base = 0x10;
294ff2748ecSLuiz Otavio O Souza 		sc->num_ports = 6;
295ff2748ecSLuiz Otavio O Souza 		break;
296091d140cSLuiz Otavio O Souza 	case MV88E6341:
297ff2748ecSLuiz Otavio O Souza 		description = "Marvell 88E6341";
298ff2748ecSLuiz Otavio O Souza 		sc->phy_base = 0x10;
299ff2748ecSLuiz Otavio O Souza 		sc->num_ports = 6;
300ff2748ecSLuiz Otavio O Souza 		break;
301091d140cSLuiz Otavio O Souza 	case MV88E6352:
3025420071dSZbigniew Bodek 		description = "Marvell 88E6352";
303ff2748ecSLuiz Otavio O Souza 		sc->num_ports = 7;
3045420071dSZbigniew Bodek 		break;
305091d140cSLuiz Otavio O Souza 	case MV88E6172:
3065420071dSZbigniew Bodek 		description = "Marvell 88E6172";
307ff2748ecSLuiz Otavio O Souza 		sc->num_ports = 7;
3085420071dSZbigniew Bodek 		break;
309091d140cSLuiz Otavio O Souza 	case MV88E6176:
3105420071dSZbigniew Bodek 		description = "Marvell 88E6176";
311ff2748ecSLuiz Otavio O Souza 		sc->num_ports = 7;
3125420071dSZbigniew Bodek 		break;
3139aba0637SLuiz Otavio O Souza 	case MV88E6190:
3149aba0637SLuiz Otavio O Souza 		description = "Marvell 88E6190";
3159aba0637SLuiz Otavio O Souza 		sc->num_ports = 11;
3169aba0637SLuiz Otavio O Souza 		break;
3179b2a503aSAdrian Chadd 	case MV88E6190X:
3189b2a503aSAdrian Chadd 		description = "Marvell 88E6190X";
3199b2a503aSAdrian Chadd 		sc->num_ports = 11;
3209b2a503aSAdrian Chadd 		break;
3215420071dSZbigniew Bodek 	default:
322091d140cSLuiz Otavio O Souza 		device_printf(dev, "Unrecognized device, id 0x%x.\n", sc->swid);
3235420071dSZbigniew Bodek 		return (ENXIO);
3245420071dSZbigniew Bodek 	}
3255420071dSZbigniew Bodek 
3265420071dSZbigniew Bodek 	device_set_desc(dev, description);
3275420071dSZbigniew Bodek 
3285420071dSZbigniew Bodek 	return (BUS_PROBE_DEFAULT);
3295420071dSZbigniew Bodek }
3305420071dSZbigniew Bodek 
3312c135a2aSLuiz Otavio O Souza #ifdef FDT
3325420071dSZbigniew Bodek static int
e6000sw_parse_fixed_link(e6000sw_softc_t * sc,phandle_t node,uint32_t port)3330fd68d72SMarcin Wojtas e6000sw_parse_fixed_link(e6000sw_softc_t *sc, phandle_t node, uint32_t port)
334f7c13d78SZbigniew Bodek {
335091d140cSLuiz Otavio O Souza 	int speed;
336091d140cSLuiz Otavio O Souza 	phandle_t fixed_link;
3370fd68d72SMarcin Wojtas 
3380fd68d72SMarcin Wojtas 	fixed_link = ofw_bus_find_child(node, "fixed-link");
3390fd68d72SMarcin Wojtas 
3400fd68d72SMarcin Wojtas 	if (fixed_link != 0) {
3410fd68d72SMarcin Wojtas 		sc->fixed_mask |= (1 << port);
3420fd68d72SMarcin Wojtas 
3439aba0637SLuiz Otavio O Souza 		if (OF_getencprop(fixed_link,
3449aba0637SLuiz Otavio O Souza 		    "speed", &speed, sizeof(speed)) < 0) {
3450fd68d72SMarcin Wojtas 			device_printf(sc->dev,
3460fd68d72SMarcin Wojtas 			    "Port %d has a fixed-link node without a speed "
3470fd68d72SMarcin Wojtas 			    "property\n", port);
3480fd68d72SMarcin Wojtas 			return (ENXIO);
3490fd68d72SMarcin Wojtas 		}
3509aba0637SLuiz Otavio O Souza 		if (speed == 2500 && (MVSWITCH(sc, MV88E6141) ||
3519b2a503aSAdrian Chadd 		     MVSWITCH(sc, MV88E6341) || MVSWITCH(sc, MV88E6190) || MVSWITCH(sc, MV88E6190X)))
3529aba0637SLuiz Otavio O Souza 			sc->fixed25_mask |= (1 << port);
3530fd68d72SMarcin Wojtas 	}
3540fd68d72SMarcin Wojtas 
3550fd68d72SMarcin Wojtas 	return (0);
3560fd68d72SMarcin Wojtas }
3570fd68d72SMarcin Wojtas 
3580fd68d72SMarcin Wojtas static int
e6000sw_parse_ethernet(e6000sw_softc_t * sc,phandle_t port_handle,uint32_t port)3590fd68d72SMarcin Wojtas e6000sw_parse_ethernet(e6000sw_softc_t *sc, phandle_t port_handle, uint32_t port) {
3600fd68d72SMarcin Wojtas 	phandle_t switch_eth, switch_eth_handle;
3610fd68d72SMarcin Wojtas 
3620fd68d72SMarcin Wojtas 	if (OF_getencprop(port_handle, "ethernet", (void*)&switch_eth_handle,
3630fd68d72SMarcin Wojtas 	    sizeof(switch_eth_handle)) > 0) {
3640fd68d72SMarcin Wojtas 		if (switch_eth_handle > 0) {
3650fd68d72SMarcin Wojtas 			switch_eth = OF_node_from_xref(switch_eth_handle);
3660fd68d72SMarcin Wojtas 
3670fd68d72SMarcin Wojtas 			device_printf(sc->dev, "CPU port at %d\n", port);
3680fd68d72SMarcin Wojtas 			sc->cpuports_mask |= (1 << port);
3690fd68d72SMarcin Wojtas 
3700fd68d72SMarcin Wojtas 			return (e6000sw_parse_fixed_link(sc, switch_eth, port));
3710fd68d72SMarcin Wojtas 		} else
3720fd68d72SMarcin Wojtas 			device_printf(sc->dev,
3730fd68d72SMarcin Wojtas 				"Port %d has ethernet property but it points "
3740fd68d72SMarcin Wojtas 				"to an invalid location\n", port);
3750fd68d72SMarcin Wojtas 	}
3760fd68d72SMarcin Wojtas 
3770fd68d72SMarcin Wojtas 	return (0);
3780fd68d72SMarcin Wojtas }
3790fd68d72SMarcin Wojtas 
3800fd68d72SMarcin Wojtas static int
e6000sw_parse_child_fdt(e6000sw_softc_t * sc,phandle_t child,int * pport)3810fd68d72SMarcin Wojtas e6000sw_parse_child_fdt(e6000sw_softc_t *sc, phandle_t child, int *pport)
3820fd68d72SMarcin Wojtas {
3830e779c2fSLuiz Otavio O Souza 	uint32_t port;
384f7c13d78SZbigniew Bodek 
3850e779c2fSLuiz Otavio O Souza 	if (pport == NULL)
386f7c13d78SZbigniew Bodek 		return (ENXIO);
387f7c13d78SZbigniew Bodek 
388091d140cSLuiz Otavio O Souza 	if (OF_getencprop(child, "reg", (void *)&port, sizeof(port)) < 0)
389091d140cSLuiz Otavio O Souza 		return (ENXIO);
390091d140cSLuiz Otavio O Souza 	if (port >= sc->num_ports)
391091d140cSLuiz Otavio O Souza 		return (ENXIO);
392091d140cSLuiz Otavio O Souza 	*pport = port;
393f7c13d78SZbigniew Bodek 
3940fd68d72SMarcin Wojtas 	if (e6000sw_parse_fixed_link(sc, child, port) != 0)
3950fd68d72SMarcin Wojtas 		return (ENXIO);
396f7c13d78SZbigniew Bodek 
3970fd68d72SMarcin Wojtas 	if (e6000sw_parse_ethernet(sc, child, port) != 0)
3980fd68d72SMarcin Wojtas 		return (ENXIO);
3990fd68d72SMarcin Wojtas 
400091d140cSLuiz Otavio O Souza 	if ((sc->fixed_mask & (1 << port)) != 0)
401091d140cSLuiz Otavio O Souza 		device_printf(sc->dev, "fixed port at %d\n", port);
402091d140cSLuiz Otavio O Souza 	else
403091d140cSLuiz Otavio O Souza 		device_printf(sc->dev, "PHY at port %d\n", port);
404f7c13d78SZbigniew Bodek 
405f7c13d78SZbigniew Bodek 	return (0);
406f7c13d78SZbigniew Bodek }
4072c135a2aSLuiz Otavio O Souza #else
4082c135a2aSLuiz Otavio O Souza 
4092c135a2aSLuiz Otavio O Souza static int
e6000sw_check_hint_val(device_t dev,int * val,char * fmt,...)4102c135a2aSLuiz Otavio O Souza e6000sw_check_hint_val(device_t dev, int *val, char *fmt, ...)
4112c135a2aSLuiz Otavio O Souza {
4122c135a2aSLuiz Otavio O Souza 	char *resname;
4132c135a2aSLuiz Otavio O Souza 	int err, len;
4142c135a2aSLuiz Otavio O Souza 	va_list ap;
4152c135a2aSLuiz Otavio O Souza 
4162c135a2aSLuiz Otavio O Souza 	len = min(strlen(fmt) * 2, 128);
4172c135a2aSLuiz Otavio O Souza 	if (len == 0)
4182c135a2aSLuiz Otavio O Souza 		return (-1);
4192c135a2aSLuiz Otavio O Souza 	resname = malloc(len, M_E6000SW, M_WAITOK);
4202c135a2aSLuiz Otavio O Souza 	memset(resname, 0, len);
4212c135a2aSLuiz Otavio O Souza 	va_start(ap, fmt);
4222c135a2aSLuiz Otavio O Souza 	vsnprintf(resname, len - 1, fmt, ap);
4232c135a2aSLuiz Otavio O Souza 	va_end(ap);
4242c135a2aSLuiz Otavio O Souza 	err = resource_int_value(device_get_name(dev), device_get_unit(dev),
4252c135a2aSLuiz Otavio O Souza 	    resname, val);
4262c135a2aSLuiz Otavio O Souza 	free(resname, M_E6000SW);
4272c135a2aSLuiz Otavio O Souza 
4282c135a2aSLuiz Otavio O Souza 	return (err);
4292c135a2aSLuiz Otavio O Souza }
4302c135a2aSLuiz Otavio O Souza 
4312c135a2aSLuiz Otavio O Souza static int
e6000sw_parse_hinted_port(e6000sw_softc_t * sc,int port)4322c135a2aSLuiz Otavio O Souza e6000sw_parse_hinted_port(e6000sw_softc_t *sc, int port)
4332c135a2aSLuiz Otavio O Souza {
4342c135a2aSLuiz Otavio O Souza 	int err, val;
4352c135a2aSLuiz Otavio O Souza 
4362c135a2aSLuiz Otavio O Souza 	err = e6000sw_check_hint_val(sc->dev, &val, "port%ddisabled", port);
4372c135a2aSLuiz Otavio O Souza 	if (err == 0 && val != 0)
4382c135a2aSLuiz Otavio O Souza 		return (1);
4392c135a2aSLuiz Otavio O Souza 
4402c135a2aSLuiz Otavio O Souza 	err = e6000sw_check_hint_val(sc->dev, &val, "port%dcpu", port);
4412c135a2aSLuiz Otavio O Souza 	if (err == 0 && val != 0) {
4422c135a2aSLuiz Otavio O Souza 		sc->cpuports_mask |= (1 << port);
4432c135a2aSLuiz Otavio O Souza 		sc->fixed_mask |= (1 << port);
4442c135a2aSLuiz Otavio O Souza 		if (bootverbose)
4452c135a2aSLuiz Otavio O Souza 			device_printf(sc->dev, "CPU port at %d\n", port);
4462c135a2aSLuiz Otavio O Souza 	}
4472c135a2aSLuiz Otavio O Souza 	err = e6000sw_check_hint_val(sc->dev, &val, "port%dspeed", port);
4482c135a2aSLuiz Otavio O Souza 	if (err == 0 && val != 0) {
4492c135a2aSLuiz Otavio O Souza 		sc->fixed_mask |= (1 << port);
4502c135a2aSLuiz Otavio O Souza 		if (val == 2500)
4512c135a2aSLuiz Otavio O Souza 			sc->fixed25_mask |= (1 << port);
4522c135a2aSLuiz Otavio O Souza 	}
4532c135a2aSLuiz Otavio O Souza 
4542c135a2aSLuiz Otavio O Souza 	if (bootverbose) {
4552c135a2aSLuiz Otavio O Souza 		if ((sc->fixed_mask & (1 << port)) != 0)
4562c135a2aSLuiz Otavio O Souza 			device_printf(sc->dev, "fixed port at %d\n", port);
4572c135a2aSLuiz Otavio O Souza 		else
4582c135a2aSLuiz Otavio O Souza 			device_printf(sc->dev, "PHY at port %d\n", port);
4592c135a2aSLuiz Otavio O Souza 	}
4602c135a2aSLuiz Otavio O Souza 
4612c135a2aSLuiz Otavio O Souza 	return (0);
4622c135a2aSLuiz Otavio O Souza }
4632c135a2aSLuiz Otavio O Souza #endif
464f7c13d78SZbigniew Bodek 
465f7c13d78SZbigniew Bodek static int
e6000sw_init_interface(e6000sw_softc_t * sc,int port)466f7c13d78SZbigniew Bodek e6000sw_init_interface(e6000sw_softc_t *sc, int port)
467f7c13d78SZbigniew Bodek {
468f7c13d78SZbigniew Bodek 	char name[IFNAMSIZ];
469f7c13d78SZbigniew Bodek 
470f7c13d78SZbigniew Bodek 	snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->dev));
471f7c13d78SZbigniew Bodek 
472f7c13d78SZbigniew Bodek 	sc->ifp[port] = if_alloc(IFT_ETHER);
4732e6a8c1aSJustin Hibbits 	if_setsoftc(sc->ifp[port], sc);
4742e6a8c1aSJustin Hibbits 	if_setflagbits(sc->ifp[port], IFF_UP | IFF_BROADCAST |
4752e6a8c1aSJustin Hibbits 	    IFF_DRV_RUNNING | IFF_SIMPLEX, 0);
4767118192aSZbigniew Bodek 	sc->ifname[port] = malloc(strlen(name) + 1, M_E6000SW, M_NOWAIT);
4777118192aSZbigniew Bodek 	if (sc->ifname[port] == NULL) {
4787118192aSZbigniew Bodek 		if_free(sc->ifp[port]);
479f7c13d78SZbigniew Bodek 		return (ENOMEM);
4807118192aSZbigniew Bodek 	}
481f7c13d78SZbigniew Bodek 	memcpy(sc->ifname[port], name, strlen(name) + 1);
482f7c13d78SZbigniew Bodek 	if_initname(sc->ifp[port], sc->ifname[port], port);
483f7c13d78SZbigniew Bodek 
484f7c13d78SZbigniew Bodek 	return (0);
485f7c13d78SZbigniew Bodek }
486f7c13d78SZbigniew Bodek 
487f7c13d78SZbigniew Bodek static int
e6000sw_attach_miibus(e6000sw_softc_t * sc,int port)488f7c13d78SZbigniew Bodek e6000sw_attach_miibus(e6000sw_softc_t *sc, int port)
489f7c13d78SZbigniew Bodek {
490f7c13d78SZbigniew Bodek 	int err;
491f7c13d78SZbigniew Bodek 
492f7c13d78SZbigniew Bodek 	err = mii_attach(sc->dev, &sc->miibus[port], sc->ifp[port],
493f7c13d78SZbigniew Bodek 	    e6000sw_ifmedia_upd, e6000sw_ifmedia_sts, BMSR_DEFCAPMASK,
494ff2748ecSLuiz Otavio O Souza 	    port + sc->phy_base, MII_OFFSET_ANY, 0);
495f7c13d78SZbigniew Bodek 	if (err != 0)
496f7c13d78SZbigniew Bodek 		return (err);
497f7c13d78SZbigniew Bodek 
498f7c13d78SZbigniew Bodek 	return (0);
499f7c13d78SZbigniew Bodek }
500f7c13d78SZbigniew Bodek 
5019aba0637SLuiz Otavio O Souza static void
e6000sw_serdes_power(device_t dev,int port,bool sgmii)5029aba0637SLuiz Otavio O Souza e6000sw_serdes_power(device_t dev, int port, bool sgmii)
5039aba0637SLuiz Otavio O Souza {
5049aba0637SLuiz Otavio O Souza 	uint32_t reg;
5059aba0637SLuiz Otavio O Souza 
5069aba0637SLuiz Otavio O Souza 	/* SGMII */
5079aba0637SLuiz Otavio O Souza 	reg = e6000sw_read_xmdio(dev, port, E6000SW_SERDES_DEV,
5089aba0637SLuiz Otavio O Souza 	    E6000SW_SERDES_SGMII_CTL);
5099aba0637SLuiz Otavio O Souza 	if (sgmii)
5109aba0637SLuiz Otavio O Souza 		reg &= ~E6000SW_SERDES_PDOWN;
5119aba0637SLuiz Otavio O Souza 	else
5129aba0637SLuiz Otavio O Souza 		reg |= E6000SW_SERDES_PDOWN;
5139aba0637SLuiz Otavio O Souza 	e6000sw_write_xmdio(dev, port, E6000SW_SERDES_DEV,
5149aba0637SLuiz Otavio O Souza 	    E6000SW_SERDES_SGMII_CTL, reg);
5159aba0637SLuiz Otavio O Souza 
5169aba0637SLuiz Otavio O Souza 	/* 10GBASE-R/10GBASE-X4/X2 */
5179aba0637SLuiz Otavio O Souza 	reg = e6000sw_read_xmdio(dev, port, E6000SW_SERDES_DEV,
5189aba0637SLuiz Otavio O Souza 	    E6000SW_SERDES_PCS_CTL1);
5199aba0637SLuiz Otavio O Souza 	if (sgmii)
5209aba0637SLuiz Otavio O Souza 		reg |= E6000SW_SERDES_PDOWN;
5219aba0637SLuiz Otavio O Souza 	else
5229aba0637SLuiz Otavio O Souza 		reg &= ~E6000SW_SERDES_PDOWN;
5239aba0637SLuiz Otavio O Souza 	e6000sw_write_xmdio(dev, port, E6000SW_SERDES_DEV,
5249aba0637SLuiz Otavio O Souza 	    E6000SW_SERDES_PCS_CTL1, reg);
5259aba0637SLuiz Otavio O Souza }
5269aba0637SLuiz Otavio O Souza 
527f7c13d78SZbigniew Bodek static int
e6000sw_attach(device_t dev)5285420071dSZbigniew Bodek e6000sw_attach(device_t dev)
5295420071dSZbigniew Bodek {
5309aba0637SLuiz Otavio O Souza 	bool sgmii;
5315420071dSZbigniew Bodek 	e6000sw_softc_t *sc;
5322c135a2aSLuiz Otavio O Souza #ifdef FDT
5330fd68d72SMarcin Wojtas 	phandle_t child, ports;
5342c135a2aSLuiz Otavio O Souza #endif
5350e779c2fSLuiz Otavio O Souza 	int err, port;
536091d140cSLuiz Otavio O Souza 	uint32_t reg;
5375420071dSZbigniew Bodek 
5385420071dSZbigniew Bodek 	err = 0;
5395420071dSZbigniew Bodek 	sc = device_get_softc(dev);
5405420071dSZbigniew Bodek 
541d7cecbd1SLuiz Otavio O Souza 	/*
542d7cecbd1SLuiz Otavio O Souza 	 * According to the Linux source code, all of the Switch IDs we support
543d7cecbd1SLuiz Otavio O Souza 	 * are multi_chip capable, and should go into multi-chip mode if the
544d7cecbd1SLuiz Otavio O Souza 	 * sw_addr != 0.
545d7cecbd1SLuiz Otavio O Souza 	 */
546d7cecbd1SLuiz Otavio O Souza 	if (MVSWITCH_MULTICHIP(sc))
547d7cecbd1SLuiz Otavio O Souza 		device_printf(dev, "multi-chip addressing mode (%#x)\n",
548d7cecbd1SLuiz Otavio O Souza 		    sc->sw_addr);
549ebde1aafSLuiz Otavio O Souza 	else
550ebde1aafSLuiz Otavio O Souza 		device_printf(dev, "single-chip addressing mode\n");
551ebde1aafSLuiz Otavio O Souza 
552eb3ffa57SZbigniew Bodek 	sx_init(&sc->sx, "e6000sw");
553eb3ffa57SZbigniew Bodek 
554f7c13d78SZbigniew Bodek 	E6000SW_LOCK(sc);
555f7c13d78SZbigniew Bodek 	e6000sw_setup(dev, sc);
5562c135a2aSLuiz Otavio O Souza 
5571e125351SHubert Mazur 	sc->sc_tq = taskqueue_create("e6000sw_taskq", M_NOWAIT,
5581e125351SHubert Mazur 	    taskqueue_thread_enqueue, &sc->sc_tq);
5591e125351SHubert Mazur 
5601e125351SHubert Mazur 	TIMEOUT_TASK_INIT(sc->sc_tq, &sc->sc_tt, 0, e6000sw_tick, sc);
5611e125351SHubert Mazur 	taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq",
5621e125351SHubert Mazur 	    device_get_nameunit(dev));
5630fd68d72SMarcin Wojtas 
5642c135a2aSLuiz Otavio O Souza #ifdef FDT
5652c135a2aSLuiz Otavio O Souza 	ports = ofw_bus_find_child(sc->node, "ports");
5660fd68d72SMarcin Wojtas 	if (ports == 0) {
5670fd68d72SMarcin Wojtas 		device_printf(dev, "failed to parse DTS: no ports found for "
5680fd68d72SMarcin Wojtas 		    "switch\n");
569ee1b7811SHubert Mazur 		E6000SW_UNLOCK(sc);
5700fd68d72SMarcin Wojtas 		return (ENXIO);
5710fd68d72SMarcin Wojtas 	}
5720fd68d72SMarcin Wojtas 
5730fd68d72SMarcin Wojtas 	for (child = OF_child(ports); child != 0; child = OF_peer(child)) {
5740e779c2fSLuiz Otavio O Souza 		err = e6000sw_parse_child_fdt(sc, child, &port);
5755420071dSZbigniew Bodek 		if (err != 0) {
576f7c13d78SZbigniew Bodek 			device_printf(sc->dev, "failed to parse DTS\n");
5775420071dSZbigniew Bodek 			goto out_fail;
5785420071dSZbigniew Bodek 		}
5792c135a2aSLuiz Otavio O Souza #else
5802c135a2aSLuiz Otavio O Souza 	for (port = 0; port < sc->num_ports; port++) {
5812c135a2aSLuiz Otavio O Souza 		err = e6000sw_parse_hinted_port(sc, port);
5822c135a2aSLuiz Otavio O Souza 		if (err != 0)
5832c135a2aSLuiz Otavio O Souza 			continue;
5842c135a2aSLuiz Otavio O Souza #endif
585f7c13d78SZbigniew Bodek 
586ff2748ecSLuiz Otavio O Souza 		/* Port is in use. */
587ff2748ecSLuiz Otavio O Souza 		sc->ports_mask |= (1 << port);
588f7c13d78SZbigniew Bodek 
589f7c13d78SZbigniew Bodek 		err = e6000sw_init_interface(sc, port);
590f7c13d78SZbigniew Bodek 		if (err != 0) {
591f7c13d78SZbigniew Bodek 			device_printf(sc->dev, "failed to init interface\n");
592f7c13d78SZbigniew Bodek 			goto out_fail;
5935420071dSZbigniew Bodek 		}
594f7c13d78SZbigniew Bodek 
595091d140cSLuiz Otavio O Souza 		if (e6000sw_is_fixedport(sc, port)) {
596091d140cSLuiz Otavio O Souza 			/* Link must be down to change speed force value. */
5979aba0637SLuiz Otavio O Souza 			reg = e6000sw_readreg(sc, REG_PORT(sc, port),
5989aba0637SLuiz Otavio O Souza 			    PSC_CONTROL);
599091d140cSLuiz Otavio O Souza 			reg &= ~PSC_CONTROL_LINK_UP;
600091d140cSLuiz Otavio O Souza 			reg |= PSC_CONTROL_FORCED_LINK;
6019aba0637SLuiz Otavio O Souza 			e6000sw_writereg(sc, REG_PORT(sc, port), PSC_CONTROL,
6029aba0637SLuiz Otavio O Souza 			    reg);
603091d140cSLuiz Otavio O Souza 
604091d140cSLuiz Otavio O Souza 			/*
605091d140cSLuiz Otavio O Souza 			 * Force speed, full-duplex, EEE off and flow-control
606091d140cSLuiz Otavio O Souza 			 * on.
607091d140cSLuiz Otavio O Souza 			 */
6085429f5f3SLuiz Otavio O Souza 			reg &= ~(PSC_CONTROL_SPD2500 | PSC_CONTROL_ALT_SPD |
6099aba0637SLuiz Otavio O Souza 			    PSC_CONTROL_FORCED_FC | PSC_CONTROL_FC_ON |
6105429f5f3SLuiz Otavio O Souza 			    PSC_CONTROL_FORCED_EEE);
611091d140cSLuiz Otavio O Souza 			if (e6000sw_is_fixed25port(sc, port))
6125429f5f3SLuiz Otavio O Souza 				reg |= PSC_CONTROL_SPD2500;
613091d140cSLuiz Otavio O Souza 			else
6145429f5f3SLuiz Otavio O Souza 				reg |= PSC_CONTROL_SPD1000;
6159b2a503aSAdrian Chadd 			if ((MVSWITCH(sc, MV88E6190) ||
6169b2a503aSAdrian Chadd 			    MVSWITCH(sc, MV88E6190X)) &&
6179aba0637SLuiz Otavio O Souza 			    e6000sw_is_fixed25port(sc, port))
6189aba0637SLuiz Otavio O Souza 				reg |= PSC_CONTROL_ALT_SPD;
619091d140cSLuiz Otavio O Souza 			reg |= PSC_CONTROL_FORCED_DPX | PSC_CONTROL_FULLDPX |
620091d140cSLuiz Otavio O Souza 			    PSC_CONTROL_FORCED_LINK | PSC_CONTROL_LINK_UP |
621091d140cSLuiz Otavio O Souza 			    PSC_CONTROL_FORCED_SPD;
6229b2a503aSAdrian Chadd 			if (!MVSWITCH(sc, MV88E6190) &&
6239b2a503aSAdrian Chadd 			    !MVSWITCH(sc, MV88E6190X))
6249aba0637SLuiz Otavio O Souza 				reg |= PSC_CONTROL_FORCED_FC | PSC_CONTROL_FC_ON;
6259aba0637SLuiz Otavio O Souza 			if (MVSWITCH(sc, MV88E6141) ||
6269aba0637SLuiz Otavio O Souza 			    MVSWITCH(sc, MV88E6341) ||
6279b2a503aSAdrian Chadd 			    MVSWITCH(sc, MV88E6190) ||
6289b2a503aSAdrian Chadd 			    MVSWITCH(sc, MV88E6190X))
629091d140cSLuiz Otavio O Souza 				reg |= PSC_CONTROL_FORCED_EEE;
6309aba0637SLuiz Otavio O Souza 			e6000sw_writereg(sc, REG_PORT(sc, port), PSC_CONTROL,
6319aba0637SLuiz Otavio O Souza 			    reg);
6329aba0637SLuiz Otavio O Souza 			/* Power on the SERDES interfaces. */
6339b2a503aSAdrian Chadd 			if ((MVSWITCH(sc, MV88E6190) ||
6349b2a503aSAdrian Chadd 			    MVSWITCH(sc, MV88E6190X)) &&
6359aba0637SLuiz Otavio O Souza 			    (port == 9 || port == 10)) {
6369aba0637SLuiz Otavio O Souza 				if (e6000sw_is_fixed25port(sc, port))
6379aba0637SLuiz Otavio O Souza 					sgmii = false;
6389aba0637SLuiz Otavio O Souza 				else
6399aba0637SLuiz Otavio O Souza 					sgmii = true;
6409aba0637SLuiz Otavio O Souza 				e6000sw_serdes_power(sc->dev, port, sgmii);
6419aba0637SLuiz Otavio O Souza 			}
642091d140cSLuiz Otavio O Souza 		}
643091d140cSLuiz Otavio O Souza 
644f7c13d78SZbigniew Bodek 		/* Don't attach miibus at CPU/fixed ports */
645f7c13d78SZbigniew Bodek 		if (!e6000sw_is_phyport(sc, port))
646f7c13d78SZbigniew Bodek 			continue;
647f7c13d78SZbigniew Bodek 
648f7c13d78SZbigniew Bodek 		err = e6000sw_attach_miibus(sc, port);
649f7c13d78SZbigniew Bodek 		if (err != 0) {
650f7c13d78SZbigniew Bodek 			device_printf(sc->dev, "failed to attach miibus\n");
651f7c13d78SZbigniew Bodek 			goto out_fail;
652f7c13d78SZbigniew Bodek 		}
653f7c13d78SZbigniew Bodek 	}
654f7c13d78SZbigniew Bodek 
655f7c13d78SZbigniew Bodek 	etherswitch_info.es_nports = sc->num_ports;
656f7c13d78SZbigniew Bodek 
6570e779c2fSLuiz Otavio O Souza 	/* Default to port vlan. */
658d7cecbd1SLuiz Otavio O Souza 	e6000sw_set_vlan_mode(sc, ETHERSWITCH_VLAN_PORT);
659d7cecbd1SLuiz Otavio O Souza 
660d7cecbd1SLuiz Otavio O Souza 	reg = e6000sw_readreg(sc, REG_GLOBAL, SWITCH_GLOBAL_STATUS);
661d7cecbd1SLuiz Otavio O Souza 	if (reg & SWITCH_GLOBAL_STATUS_IR)
662d7cecbd1SLuiz Otavio O Souza 		device_printf(dev, "switch is ready.\n");
6635420071dSZbigniew Bodek 	E6000SW_UNLOCK(sc);
6645420071dSZbigniew Bodek 
665723da5d9SJohn Baldwin 	bus_identify_children(dev);
66618250ec6SJohn Baldwin 	bus_attach_children(dev);
6675420071dSZbigniew Bodek 
6681e125351SHubert Mazur 	taskqueue_enqueue_timeout(sc->sc_tq, &sc->sc_tt, hz);
6695420071dSZbigniew Bodek 
6705420071dSZbigniew Bodek 	return (0);
6715420071dSZbigniew Bodek 
6725420071dSZbigniew Bodek out_fail:
673af298663SAdrian Chadd 	E6000SW_UNLOCK(sc);
6745420071dSZbigniew Bodek 	e6000sw_detach(dev);
6755420071dSZbigniew Bodek 
676f7c13d78SZbigniew Bodek 	return (err);
6775420071dSZbigniew Bodek }
6785420071dSZbigniew Bodek 
679d7cecbd1SLuiz Otavio O Souza static int
680d7cecbd1SLuiz Otavio O Souza e6000sw_waitready(e6000sw_softc_t *sc, uint32_t phy, uint32_t reg,
681d7cecbd1SLuiz Otavio O Souza     uint32_t busybit)
6825420071dSZbigniew Bodek {
6830a6542a3SWojciech Macek 	int i;
6845420071dSZbigniew Bodek 
685d7cecbd1SLuiz Otavio O Souza 	for (i = 0; i < E6000SW_RETRIES; i++) {
686d7cecbd1SLuiz Otavio O Souza 		if ((e6000sw_readreg(sc, phy, reg) & busybit) == 0)
6870a6542a3SWojciech Macek 			return (0);
688d7cecbd1SLuiz Otavio O Souza 		DELAY(1);
6890a6542a3SWojciech Macek 	}
6900a6542a3SWojciech Macek 
691d7cecbd1SLuiz Otavio O Souza 	return (1);
6925420071dSZbigniew Bodek }
6935420071dSZbigniew Bodek 
6949aba0637SLuiz Otavio O Souza /* XMDIO/Clause 45 access. */
6959aba0637SLuiz Otavio O Souza static int
6969aba0637SLuiz Otavio O Souza e6000sw_read_xmdio(device_t dev, int phy, int devaddr, int devreg)
6979aba0637SLuiz Otavio O Souza {
6989aba0637SLuiz Otavio O Souza 	e6000sw_softc_t *sc;
6999aba0637SLuiz Otavio O Souza 	uint32_t reg;
7009aba0637SLuiz Otavio O Souza 
7019aba0637SLuiz Otavio O Souza 	sc = device_get_softc(dev);
7029aba0637SLuiz Otavio O Souza 	E6000SW_LOCK_ASSERT(sc, SA_XLOCKED);
7039aba0637SLuiz Otavio O Souza 	if (E6000SW_WAITREADY2(sc, SMI_PHY_CMD_REG, SMI_CMD_BUSY)) {
7049aba0637SLuiz Otavio O Souza 		device_printf(dev, "Timeout while waiting for switch\n");
7059aba0637SLuiz Otavio O Souza 		return (ETIMEDOUT);
7069aba0637SLuiz Otavio O Souza 	}
7079aba0637SLuiz Otavio O Souza 
7089aba0637SLuiz Otavio O Souza 	reg = devaddr & SMI_CMD_REG_ADDR_MASK;
7099aba0637SLuiz Otavio O Souza 	reg |= (phy << SMI_CMD_DEV_ADDR) & SMI_CMD_DEV_ADDR_MASK;
7109aba0637SLuiz Otavio O Souza 
7119aba0637SLuiz Otavio O Souza 	/* Load C45 register address. */
7129aba0637SLuiz Otavio O Souza 	e6000sw_writereg(sc, REG_GLOBAL2, SMI_PHY_DATA_REG, devreg);
7139aba0637SLuiz Otavio O Souza 	e6000sw_writereg(sc, REG_GLOBAL2, SMI_PHY_CMD_REG,
7149aba0637SLuiz Otavio O Souza 	    reg | SMI_CMD_OP_C45_ADDR);
7159aba0637SLuiz Otavio O Souza 	if (E6000SW_WAITREADY2(sc, SMI_PHY_CMD_REG, SMI_CMD_BUSY)) {
7169aba0637SLuiz Otavio O Souza 		device_printf(dev, "Timeout while waiting for switch\n");
7179aba0637SLuiz Otavio O Souza 		return (ETIMEDOUT);
7189aba0637SLuiz Otavio O Souza 	}
7199aba0637SLuiz Otavio O Souza 
7209aba0637SLuiz Otavio O Souza 	/* Start C45 read operation. */
7219aba0637SLuiz Otavio O Souza 	e6000sw_writereg(sc, REG_GLOBAL2, SMI_PHY_CMD_REG,
7229aba0637SLuiz Otavio O Souza 	    reg | SMI_CMD_OP_C45_READ);
7239aba0637SLuiz Otavio O Souza 	if (E6000SW_WAITREADY2(sc, SMI_PHY_CMD_REG, SMI_CMD_BUSY)) {
7249aba0637SLuiz Otavio O Souza 		device_printf(dev, "Timeout while waiting for switch\n");
7259aba0637SLuiz Otavio O Souza 		return (ETIMEDOUT);
7269aba0637SLuiz Otavio O Souza 	}
7279aba0637SLuiz Otavio O Souza 
7289aba0637SLuiz Otavio O Souza 	/* Read C45 data. */
7299aba0637SLuiz Otavio O Souza 	reg = e6000sw_readreg(sc, REG_GLOBAL2, SMI_PHY_DATA_REG);
7309aba0637SLuiz Otavio O Souza 
7319aba0637SLuiz Otavio O Souza 	return (reg & PHY_DATA_MASK);
7329aba0637SLuiz Otavio O Souza }
7339aba0637SLuiz Otavio O Souza 
7349aba0637SLuiz Otavio O Souza static int
7359aba0637SLuiz Otavio O Souza e6000sw_write_xmdio(device_t dev, int phy, int devaddr, int devreg, int val)
7369aba0637SLuiz Otavio O Souza {
7379aba0637SLuiz Otavio O Souza 	e6000sw_softc_t *sc;
7389aba0637SLuiz Otavio O Souza 	uint32_t reg;
7399aba0637SLuiz Otavio O Souza 
7409aba0637SLuiz Otavio O Souza 	sc = device_get_softc(dev);
7419aba0637SLuiz Otavio O Souza 	E6000SW_LOCK_ASSERT(sc, SA_XLOCKED);
7429aba0637SLuiz Otavio O Souza 	if (E6000SW_WAITREADY2(sc, SMI_PHY_CMD_REG, SMI_CMD_BUSY)) {
7439aba0637SLuiz Otavio O Souza 		device_printf(dev, "Timeout while waiting for switch\n");
7449aba0637SLuiz Otavio O Souza 		return (ETIMEDOUT);
7459aba0637SLuiz Otavio O Souza 	}
7469aba0637SLuiz Otavio O Souza 
7479aba0637SLuiz Otavio O Souza 	reg = devaddr & SMI_CMD_REG_ADDR_MASK;
7489aba0637SLuiz Otavio O Souza 	reg |= (phy << SMI_CMD_DEV_ADDR) & SMI_CMD_DEV_ADDR_MASK;
7499aba0637SLuiz Otavio O Souza 
7509aba0637SLuiz Otavio O Souza 	/* Load C45 register address. */
7519aba0637SLuiz Otavio O Souza 	e6000sw_writereg(sc, REG_GLOBAL2, SMI_PHY_DATA_REG, devreg);
7529aba0637SLuiz Otavio O Souza 	e6000sw_writereg(sc, REG_GLOBAL2, SMI_PHY_CMD_REG,
7539aba0637SLuiz Otavio O Souza 	    reg | SMI_CMD_OP_C45_ADDR);
7549aba0637SLuiz Otavio O Souza 	if (E6000SW_WAITREADY2(sc, SMI_PHY_CMD_REG, SMI_CMD_BUSY)) {
7559aba0637SLuiz Otavio O Souza 		device_printf(dev, "Timeout while waiting for switch\n");
7569aba0637SLuiz Otavio O Souza 		return (ETIMEDOUT);
7579aba0637SLuiz Otavio O Souza 	}
7589aba0637SLuiz Otavio O Souza 
7599aba0637SLuiz Otavio O Souza 	/* Load data and start the C45 write operation. */
7609aba0637SLuiz Otavio O Souza 	e6000sw_writereg(sc, REG_GLOBAL2, SMI_PHY_DATA_REG, devreg);
7619aba0637SLuiz Otavio O Souza 	e6000sw_writereg(sc, REG_GLOBAL2, SMI_PHY_CMD_REG,
7629aba0637SLuiz Otavio O Souza 	    reg | SMI_CMD_OP_C45_WRITE);
7639aba0637SLuiz Otavio O Souza 
7649aba0637SLuiz Otavio O Souza 	return (0);
7659aba0637SLuiz Otavio O Souza }
7669aba0637SLuiz Otavio O Souza 
767725962a9SMark Johnston static int
768725962a9SMark Johnston e6000sw_readphy(device_t dev, int phy, int reg)
76946929064SAlbert Jakiela {
77046929064SAlbert Jakiela 	e6000sw_softc_t *sc;
771725962a9SMark Johnston 	int locked, ret;
77246929064SAlbert Jakiela 
77346929064SAlbert Jakiela 	sc = device_get_softc(dev);
77446929064SAlbert Jakiela 
775725962a9SMark Johnston 	locked = E6000SW_LOCKED(sc);
776725962a9SMark Johnston 	if (!locked)
77746929064SAlbert Jakiela 		E6000SW_LOCK(sc);
77846929064SAlbert Jakiela 	ret = e6000sw_readphy_locked(dev, phy, reg);
779725962a9SMark Johnston 	if (!locked)
78046929064SAlbert Jakiela 		E6000SW_UNLOCK(sc);
78146929064SAlbert Jakiela 
78246929064SAlbert Jakiela 	return (ret);
78346929064SAlbert Jakiela }
78446929064SAlbert Jakiela 
7855420071dSZbigniew Bodek /*
7865420071dSZbigniew Bodek  * PHY registers are paged. Put page index in reg 22 (accessible from every
7875420071dSZbigniew Bodek  * page), then access specific register.
7885420071dSZbigniew Bodek  */
7895420071dSZbigniew Bodek static int
79046929064SAlbert Jakiela e6000sw_readphy_locked(device_t dev, int phy, int reg)
7915420071dSZbigniew Bodek {
7925420071dSZbigniew Bodek 	e6000sw_softc_t *sc;
7935420071dSZbigniew Bodek 	uint32_t val;
7945420071dSZbigniew Bodek 
7955420071dSZbigniew Bodek 	sc = device_get_softc(dev);
79646929064SAlbert Jakiela 	E6000SW_LOCK_ASSERT(sc, SA_XLOCKED);
797ee1b7811SHubert Mazur 
798f7c13d78SZbigniew Bodek 	if (!e6000sw_is_phyport(sc, phy) || reg >= E6000SW_NUM_PHY_REGS) {
7995420071dSZbigniew Bodek 		device_printf(dev, "Wrong register address.\n");
8005420071dSZbigniew Bodek 		return (EINVAL);
8015420071dSZbigniew Bodek 	}
8025420071dSZbigniew Bodek 
803d7cecbd1SLuiz Otavio O Souza 	if (E6000SW_WAITREADY2(sc, SMI_PHY_CMD_REG, SMI_CMD_BUSY)) {
8040a6542a3SWojciech Macek 		device_printf(dev, "Timeout while waiting for switch\n");
805d7cecbd1SLuiz Otavio O Souza 		return (ETIMEDOUT);
8060a6542a3SWojciech Macek 	}
8070a6542a3SWojciech Macek 
808d7cecbd1SLuiz Otavio O Souza 	e6000sw_writereg(sc, REG_GLOBAL2, SMI_PHY_CMD_REG,
809d7cecbd1SLuiz Otavio O Souza 	    SMI_CMD_OP_C22_READ | (reg & SMI_CMD_REG_ADDR_MASK) |
810d7cecbd1SLuiz Otavio O Souza 	    ((phy << SMI_CMD_DEV_ADDR) & SMI_CMD_DEV_ADDR_MASK));
811d7cecbd1SLuiz Otavio O Souza 	if (E6000SW_WAITREADY2(sc, SMI_PHY_CMD_REG, SMI_CMD_BUSY)) {
8120a6542a3SWojciech Macek 		device_printf(dev, "Timeout while waiting for switch\n");
813d7cecbd1SLuiz Otavio O Souza 		return (ETIMEDOUT);
8140a6542a3SWojciech Macek 	}
8150a6542a3SWojciech Macek 
81633c2a3cbSLuiz Otavio O Souza 	val = e6000sw_readreg(sc, REG_GLOBAL2, SMI_PHY_DATA_REG);
8175420071dSZbigniew Bodek 
81833c2a3cbSLuiz Otavio O Souza 	return (val & PHY_DATA_MASK);
8195420071dSZbigniew Bodek }
8205420071dSZbigniew Bodek 
821725962a9SMark Johnston static int
822725962a9SMark Johnston e6000sw_writephy(device_t dev, int phy, int reg, int data)
82346929064SAlbert Jakiela {
82446929064SAlbert Jakiela 	e6000sw_softc_t *sc;
825725962a9SMark Johnston 	int locked, ret;
82646929064SAlbert Jakiela 
82746929064SAlbert Jakiela 	sc = device_get_softc(dev);
82846929064SAlbert Jakiela 
829725962a9SMark Johnston 	locked = E6000SW_LOCKED(sc);
830725962a9SMark Johnston 	if (!locked)
83146929064SAlbert Jakiela 		E6000SW_LOCK(sc);
83246929064SAlbert Jakiela 	ret = e6000sw_writephy_locked(dev, phy, reg, data);
833725962a9SMark Johnston 	if (!locked)
83446929064SAlbert Jakiela 		E6000SW_UNLOCK(sc);
83546929064SAlbert Jakiela 
83646929064SAlbert Jakiela 	return (ret);
83746929064SAlbert Jakiela }
83846929064SAlbert Jakiela 
8395420071dSZbigniew Bodek static int
84046929064SAlbert Jakiela e6000sw_writephy_locked(device_t dev, int phy, int reg, int data)
8415420071dSZbigniew Bodek {
8425420071dSZbigniew Bodek 	e6000sw_softc_t *sc;
8435420071dSZbigniew Bodek 
8445420071dSZbigniew Bodek 	sc = device_get_softc(dev);
84546929064SAlbert Jakiela 	E6000SW_LOCK_ASSERT(sc, SA_XLOCKED);
846ee1b7811SHubert Mazur 
847f7c13d78SZbigniew Bodek 	if (!e6000sw_is_phyport(sc, phy) || reg >= E6000SW_NUM_PHY_REGS) {
8485420071dSZbigniew Bodek 		device_printf(dev, "Wrong register address.\n");
8495420071dSZbigniew Bodek 		return (EINVAL);
8505420071dSZbigniew Bodek 	}
8515420071dSZbigniew Bodek 
852d7cecbd1SLuiz Otavio O Souza 	if (E6000SW_WAITREADY2(sc, SMI_PHY_CMD_REG, SMI_CMD_BUSY)) {
8530a6542a3SWojciech Macek 		device_printf(dev, "Timeout while waiting for switch\n");
854d7cecbd1SLuiz Otavio O Souza 		return (ETIMEDOUT);
8550a6542a3SWojciech Macek 	}
8560a6542a3SWojciech Macek 
8575420071dSZbigniew Bodek 	e6000sw_writereg(sc, REG_GLOBAL2, SMI_PHY_DATA_REG,
8585420071dSZbigniew Bodek 	    data & PHY_DATA_MASK);
859d7cecbd1SLuiz Otavio O Souza 	e6000sw_writereg(sc, REG_GLOBAL2, SMI_PHY_CMD_REG,
860d7cecbd1SLuiz Otavio O Souza 	    SMI_CMD_OP_C22_WRITE | (reg & SMI_CMD_REG_ADDR_MASK) |
861d7cecbd1SLuiz Otavio O Souza 	    ((phy << SMI_CMD_DEV_ADDR) & SMI_CMD_DEV_ADDR_MASK));
8620a6542a3SWojciech Macek 
863d7cecbd1SLuiz Otavio O Souza 	return (0);
8645420071dSZbigniew Bodek }
8655420071dSZbigniew Bodek 
8665420071dSZbigniew Bodek static int
8675420071dSZbigniew Bodek e6000sw_detach(device_t dev)
8685420071dSZbigniew Bodek {
8693ddaf820SJohn Baldwin 	int error, phy;
8705420071dSZbigniew Bodek 	e6000sw_softc_t *sc;
8715420071dSZbigniew Bodek 
8725420071dSZbigniew Bodek 	sc = device_get_softc(dev);
8731e125351SHubert Mazur 
874ee7f62faSAdrian Chadd 	E6000SW_LOCK(sc);
875ee7f62faSAdrian Chadd 	sc->is_shutdown = true;
876ee7f62faSAdrian Chadd 	if (sc->sc_tq != NULL) {
877ee7f62faSAdrian Chadd 		while (taskqueue_cancel_timeout(sc->sc_tq, &sc->sc_tt, NULL) != 0)
878ee7f62faSAdrian Chadd 			taskqueue_drain_timeout(sc->sc_tq, &sc->sc_tt);
879ee7f62faSAdrian Chadd 	}
880ee7f62faSAdrian Chadd 	E6000SW_UNLOCK(sc);
881ee7f62faSAdrian Chadd 
8823ddaf820SJohn Baldwin 	error = bus_generic_detach(dev);
8833ddaf820SJohn Baldwin 	if (error != 0)
8843ddaf820SJohn Baldwin 		return (error);
8853ddaf820SJohn Baldwin 
8861e125351SHubert Mazur 	if (sc->sc_tq != NULL)
8871e125351SHubert Mazur 		taskqueue_free(sc->sc_tq);
8881e125351SHubert Mazur 
8895420071dSZbigniew Bodek 	sx_destroy(&sc->sx);
890f7c13d78SZbigniew Bodek 	for (phy = 0; phy < sc->num_ports; phy++) {
8915420071dSZbigniew Bodek 		if (sc->ifp[phy] != NULL)
8925420071dSZbigniew Bodek 			if_free(sc->ifp[phy]);
8935420071dSZbigniew Bodek 		if (sc->ifname[phy] != NULL)
8945420071dSZbigniew Bodek 			free(sc->ifname[phy], M_E6000SW);
8955420071dSZbigniew Bodek 	}
8965420071dSZbigniew Bodek 
8975420071dSZbigniew Bodek 	return (0);
8985420071dSZbigniew Bodek }
8995420071dSZbigniew Bodek 
9005420071dSZbigniew Bodek static etherswitch_info_t*
9015420071dSZbigniew Bodek e6000sw_getinfo(device_t dev)
9025420071dSZbigniew Bodek {
9035420071dSZbigniew Bodek 
9045420071dSZbigniew Bodek 	return (&etherswitch_info);
9055420071dSZbigniew Bodek }
9065420071dSZbigniew Bodek 
907ff2748ecSLuiz Otavio O Souza static int
9080e779c2fSLuiz Otavio O Souza e6000sw_getconf(device_t dev, etherswitch_conf_t *conf)
909ff2748ecSLuiz Otavio O Souza {
9100e779c2fSLuiz Otavio O Souza 	struct e6000sw_softc *sc;
911ff2748ecSLuiz Otavio O Souza 
912ff2748ecSLuiz Otavio O Souza 	/* Return the VLAN mode. */
9130e779c2fSLuiz Otavio O Souza 	sc = device_get_softc(dev);
914ff2748ecSLuiz Otavio O Souza 	conf->cmd = ETHERSWITCH_CONF_VLAN_MODE;
9150e779c2fSLuiz Otavio O Souza 	conf->vlan_mode = sc->vlan_mode;
916ff2748ecSLuiz Otavio O Souza 
917ff2748ecSLuiz Otavio O Souza 	return (0);
918ff2748ecSLuiz Otavio O Souza }
919ff2748ecSLuiz Otavio O Souza 
920d7cecbd1SLuiz Otavio O Souza static int
921d7cecbd1SLuiz Otavio O Souza e6000sw_setconf(device_t dev, etherswitch_conf_t *conf)
922d7cecbd1SLuiz Otavio O Souza {
923d7cecbd1SLuiz Otavio O Souza 	struct e6000sw_softc *sc;
924d7cecbd1SLuiz Otavio O Souza 
925d7cecbd1SLuiz Otavio O Souza 	/* Set the VLAN mode. */
926d7cecbd1SLuiz Otavio O Souza 	sc = device_get_softc(dev);
927d7cecbd1SLuiz Otavio O Souza 	if (conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) {
928d7cecbd1SLuiz Otavio O Souza 		E6000SW_LOCK(sc);
929d7cecbd1SLuiz Otavio O Souza 		e6000sw_set_vlan_mode(sc, conf->vlan_mode);
930d7cecbd1SLuiz Otavio O Souza 		E6000SW_UNLOCK(sc);
931d7cecbd1SLuiz Otavio O Souza 	}
932d7cecbd1SLuiz Otavio O Souza 
933d7cecbd1SLuiz Otavio O Souza 	return (0);
934d7cecbd1SLuiz Otavio O Souza }
935d7cecbd1SLuiz Otavio O Souza 
9365420071dSZbigniew Bodek static void
9375420071dSZbigniew Bodek e6000sw_lock(device_t dev)
9385420071dSZbigniew Bodek {
9395420071dSZbigniew Bodek 	struct e6000sw_softc *sc;
9405420071dSZbigniew Bodek 
9415420071dSZbigniew Bodek 	sc = device_get_softc(dev);
9425420071dSZbigniew Bodek 
9435420071dSZbigniew Bodek 	E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED);
9445420071dSZbigniew Bodek 	E6000SW_LOCK(sc);
9455420071dSZbigniew Bodek }
9465420071dSZbigniew Bodek 
9475420071dSZbigniew Bodek static void
9485420071dSZbigniew Bodek e6000sw_unlock(device_t dev)
9495420071dSZbigniew Bodek {
9505420071dSZbigniew Bodek 	struct e6000sw_softc *sc;
9515420071dSZbigniew Bodek 
9525420071dSZbigniew Bodek 	sc = device_get_softc(dev);
9535420071dSZbigniew Bodek 
9545420071dSZbigniew Bodek 	E6000SW_LOCK_ASSERT(sc, SA_XLOCKED);
9555420071dSZbigniew Bodek 	E6000SW_UNLOCK(sc);
9565420071dSZbigniew Bodek }
9575420071dSZbigniew Bodek 
9585420071dSZbigniew Bodek static int
9595420071dSZbigniew Bodek e6000sw_getport(device_t dev, etherswitch_port_t *p)
9605420071dSZbigniew Bodek {
9615420071dSZbigniew Bodek 	struct mii_data *mii;
9625420071dSZbigniew Bodek 	int err;
9635420071dSZbigniew Bodek 	struct ifmediareq *ifmr;
964d7cecbd1SLuiz Otavio O Souza 	uint32_t reg;
9655420071dSZbigniew Bodek 
9665420071dSZbigniew Bodek 	e6000sw_softc_t *sc = device_get_softc(dev);
9675420071dSZbigniew Bodek 	E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED);
9685420071dSZbigniew Bodek 
96933c2a3cbSLuiz Otavio O Souza 	if (p->es_port >= sc->num_ports || p->es_port < 0)
97033c2a3cbSLuiz Otavio O Souza 		return (EINVAL);
971ff2748ecSLuiz Otavio O Souza 	if (!e6000sw_is_portenabled(sc, p->es_port))
972ff2748ecSLuiz Otavio O Souza 		return (0);
97333c2a3cbSLuiz Otavio O Souza 
9745420071dSZbigniew Bodek 	E6000SW_LOCK(sc);
9755420071dSZbigniew Bodek 	e6000sw_get_pvid(sc, p->es_port, &p->es_pvid);
9765420071dSZbigniew Bodek 
977d7cecbd1SLuiz Otavio O Souza 	/* Port flags. */
9789aba0637SLuiz Otavio O Souza 	reg = e6000sw_readreg(sc, REG_PORT(sc, p->es_port), PORT_CONTROL2);
979d7cecbd1SLuiz Otavio O Souza 	if (reg & PORT_CONTROL2_DISC_TAGGED)
980d7cecbd1SLuiz Otavio O Souza 		p->es_flags |= ETHERSWITCH_PORT_DROPTAGGED;
981d7cecbd1SLuiz Otavio O Souza 	if (reg & PORT_CONTROL2_DISC_UNTAGGED)
982d7cecbd1SLuiz Otavio O Souza 		p->es_flags |= ETHERSWITCH_PORT_DROPUNTAGGED;
983d7cecbd1SLuiz Otavio O Souza 
984d7cecbd1SLuiz Otavio O Souza 	err = 0;
985091d140cSLuiz Otavio O Souza 	if (e6000sw_is_fixedport(sc, p->es_port)) {
986091d140cSLuiz Otavio O Souza 		if (e6000sw_is_cpuport(sc, p->es_port))
9875420071dSZbigniew Bodek 			p->es_flags |= ETHERSWITCH_PORT_CPU;
9885420071dSZbigniew Bodek 		ifmr = &p->es_ifmr;
9895420071dSZbigniew Bodek 		ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
9905420071dSZbigniew Bodek 		ifmr->ifm_count = 0;
991091d140cSLuiz Otavio O Souza 		if (e6000sw_is_fixed25port(sc, p->es_port))
992091d140cSLuiz Otavio O Souza 			ifmr->ifm_active = IFM_2500_T;
993091d140cSLuiz Otavio O Souza 		else
994091d140cSLuiz Otavio O Souza 			ifmr->ifm_active = IFM_1000_T;
995091d140cSLuiz Otavio O Souza 		ifmr->ifm_active |= IFM_ETHER | IFM_FDX;
996091d140cSLuiz Otavio O Souza 		ifmr->ifm_current = ifmr->ifm_active;
997f7c13d78SZbigniew Bodek 		ifmr->ifm_mask = 0;
9985420071dSZbigniew Bodek 	} else {
9995420071dSZbigniew Bodek 		mii = e6000sw_miiforphy(sc, p->es_port);
10005420071dSZbigniew Bodek 		err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr,
10015420071dSZbigniew Bodek 		    &mii->mii_media, SIOCGIFMEDIA);
10025420071dSZbigniew Bodek 	}
10035420071dSZbigniew Bodek 	E6000SW_UNLOCK(sc);
100433c2a3cbSLuiz Otavio O Souza 
10055420071dSZbigniew Bodek 	return (err);
10065420071dSZbigniew Bodek }
10075420071dSZbigniew Bodek 
10085420071dSZbigniew Bodek static int
10095420071dSZbigniew Bodek e6000sw_setport(device_t dev, etherswitch_port_t *p)
10105420071dSZbigniew Bodek {
10115420071dSZbigniew Bodek 	e6000sw_softc_t *sc;
10125420071dSZbigniew Bodek 	int err;
10135420071dSZbigniew Bodek 	struct mii_data *mii;
1014d7cecbd1SLuiz Otavio O Souza 	uint32_t reg;
10155420071dSZbigniew Bodek 
10165420071dSZbigniew Bodek 	sc = device_get_softc(dev);
10175420071dSZbigniew Bodek 	E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED);
10185420071dSZbigniew Bodek 
101933c2a3cbSLuiz Otavio O Souza 	if (p->es_port >= sc->num_ports || p->es_port < 0)
102033c2a3cbSLuiz Otavio O Souza 		return (EINVAL);
1021ff2748ecSLuiz Otavio O Souza 	if (!e6000sw_is_portenabled(sc, p->es_port))
1022ff2748ecSLuiz Otavio O Souza 		return (0);
102333c2a3cbSLuiz Otavio O Souza 
102475f5224eSGanbold Tsagaankhuu 	E6000SW_LOCK(sc);
102575f5224eSGanbold Tsagaankhuu 
1026d7cecbd1SLuiz Otavio O Souza 	/* Port flags. */
10279aba0637SLuiz Otavio O Souza 	reg = e6000sw_readreg(sc, REG_PORT(sc, p->es_port), PORT_CONTROL2);
1028d7cecbd1SLuiz Otavio O Souza 	if (p->es_flags & ETHERSWITCH_PORT_DROPTAGGED)
1029d7cecbd1SLuiz Otavio O Souza 		reg |= PORT_CONTROL2_DISC_TAGGED;
1030d7cecbd1SLuiz Otavio O Souza 	else
1031d7cecbd1SLuiz Otavio O Souza 		reg &= ~PORT_CONTROL2_DISC_TAGGED;
1032d7cecbd1SLuiz Otavio O Souza 	if (p->es_flags & ETHERSWITCH_PORT_DROPUNTAGGED)
1033d7cecbd1SLuiz Otavio O Souza 		reg |= PORT_CONTROL2_DISC_UNTAGGED;
1034d7cecbd1SLuiz Otavio O Souza 	else
1035d7cecbd1SLuiz Otavio O Souza 		reg &= ~PORT_CONTROL2_DISC_UNTAGGED;
10369aba0637SLuiz Otavio O Souza 	e6000sw_writereg(sc, REG_PORT(sc, p->es_port), PORT_CONTROL2, reg);
1037d7cecbd1SLuiz Otavio O Souza 
103833c2a3cbSLuiz Otavio O Souza 	err = 0;
10395420071dSZbigniew Bodek 	if (p->es_pvid != 0)
10405420071dSZbigniew Bodek 		e6000sw_set_pvid(sc, p->es_port, p->es_pvid);
1041091d140cSLuiz Otavio O Souza 	if (e6000sw_is_phyport(sc, p->es_port)) {
10425420071dSZbigniew Bodek 		mii = e6000sw_miiforphy(sc, p->es_port);
10435420071dSZbigniew Bodek 		err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr, &mii->mii_media,
10445420071dSZbigniew Bodek 		    SIOCSIFMEDIA);
10455420071dSZbigniew Bodek 	}
10465420071dSZbigniew Bodek 	E6000SW_UNLOCK(sc);
104733c2a3cbSLuiz Otavio O Souza 
10485420071dSZbigniew Bodek 	return (err);
10495420071dSZbigniew Bodek }
10505420071dSZbigniew Bodek 
1051d7cecbd1SLuiz Otavio O Souza static __inline void
1052d7cecbd1SLuiz Otavio O Souza e6000sw_port_vlan_assign(e6000sw_softc_t *sc, int port, uint32_t fid,
1053d7cecbd1SLuiz Otavio O Souza     uint32_t members)
1054d7cecbd1SLuiz Otavio O Souza {
1055d7cecbd1SLuiz Otavio O Souza 	uint32_t reg;
1056d7cecbd1SLuiz Otavio O Souza 
10579aba0637SLuiz Otavio O Souza 	reg = e6000sw_readreg(sc, REG_PORT(sc, port), PORT_VLAN_MAP);
1058d7cecbd1SLuiz Otavio O Souza 	reg &= ~(PORT_MASK(sc) | PORT_VLAN_MAP_FID_MASK);
1059d7cecbd1SLuiz Otavio O Souza 	reg |= members & PORT_MASK(sc) & ~(1 << port);
1060d7cecbd1SLuiz Otavio O Souza 	reg |= (fid << PORT_VLAN_MAP_FID) & PORT_VLAN_MAP_FID_MASK;
10619aba0637SLuiz Otavio O Souza 	e6000sw_writereg(sc, REG_PORT(sc, port), PORT_VLAN_MAP, reg);
10629aba0637SLuiz Otavio O Souza 	reg = e6000sw_readreg(sc, REG_PORT(sc, port), PORT_CONTROL1);
1063d7cecbd1SLuiz Otavio O Souza 	reg &= ~PORT_CONTROL1_FID_MASK;
1064d7cecbd1SLuiz Otavio O Souza 	reg |= (fid >> 4) & PORT_CONTROL1_FID_MASK;
10659aba0637SLuiz Otavio O Souza 	e6000sw_writereg(sc, REG_PORT(sc, port), PORT_CONTROL1, reg);
1066d7cecbd1SLuiz Otavio O Souza }
1067d7cecbd1SLuiz Otavio O Souza 
1068d7cecbd1SLuiz Otavio O Souza static int
1069d7cecbd1SLuiz Otavio O Souza e6000sw_init_vlan(struct e6000sw_softc *sc)
1070d7cecbd1SLuiz Otavio O Souza {
1071d7cecbd1SLuiz Otavio O Souza 	int i, port, ret;
1072d7cecbd1SLuiz Otavio O Souza 	uint32_t members;
1073d7cecbd1SLuiz Otavio O Souza 
1074d7cecbd1SLuiz Otavio O Souza 	/* Disable all ports */
1075d7cecbd1SLuiz Otavio O Souza 	for (port = 0; port < sc->num_ports; port++) {
10769aba0637SLuiz Otavio O Souza 		ret = e6000sw_readreg(sc, REG_PORT(sc, port), PORT_CONTROL);
10779aba0637SLuiz Otavio O Souza 		e6000sw_writereg(sc, REG_PORT(sc, port), PORT_CONTROL,
1078d7cecbd1SLuiz Otavio O Souza 		    (ret & ~PORT_CONTROL_ENABLE));
1079d7cecbd1SLuiz Otavio O Souza 	}
1080d7cecbd1SLuiz Otavio O Souza 
1081d7cecbd1SLuiz Otavio O Souza 	/* Flush VTU. */
1082d7cecbd1SLuiz Otavio O Souza 	e6000sw_vtu_flush(sc);
1083d7cecbd1SLuiz Otavio O Souza 
1084d7cecbd1SLuiz Otavio O Souza 	for (port = 0; port < sc->num_ports; port++) {
1085d7cecbd1SLuiz Otavio O Souza 		/* Reset the egress and frame mode. */
10869aba0637SLuiz Otavio O Souza 		ret = e6000sw_readreg(sc, REG_PORT(sc, port), PORT_CONTROL);
1087d7cecbd1SLuiz Otavio O Souza 		ret &= ~(PORT_CONTROL_EGRESS | PORT_CONTROL_FRAME);
10889aba0637SLuiz Otavio O Souza 		e6000sw_writereg(sc, REG_PORT(sc, port), PORT_CONTROL, ret);
1089d7cecbd1SLuiz Otavio O Souza 
10908cf905e5SGordon Bergling 		/* Set the 802.1q mode. */
10919aba0637SLuiz Otavio O Souza 		ret = e6000sw_readreg(sc, REG_PORT(sc, port), PORT_CONTROL2);
1092d7cecbd1SLuiz Otavio O Souza 		ret &= ~PORT_CONTROL2_DOT1Q;
1093d7cecbd1SLuiz Otavio O Souza 		if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q)
1094d7cecbd1SLuiz Otavio O Souza 			ret |= PORT_CONTROL2_DOT1Q;
10959aba0637SLuiz Otavio O Souza 		e6000sw_writereg(sc, REG_PORT(sc, port), PORT_CONTROL2, ret);
1096d7cecbd1SLuiz Otavio O Souza 	}
1097d7cecbd1SLuiz Otavio O Souza 
1098d7cecbd1SLuiz Otavio O Souza 	for (port = 0; port < sc->num_ports; port++) {
1099d7cecbd1SLuiz Otavio O Souza 		if (!e6000sw_is_portenabled(sc, port))
1100d7cecbd1SLuiz Otavio O Souza 			continue;
1101d7cecbd1SLuiz Otavio O Souza 
11029aba0637SLuiz Otavio O Souza 		ret = e6000sw_readreg(sc, REG_PORT(sc, port), PORT_VID);
1103d7cecbd1SLuiz Otavio O Souza 
1104d7cecbd1SLuiz Otavio O Souza 		/* Set port priority */
1105d7cecbd1SLuiz Otavio O Souza 		ret &= ~PORT_VID_PRIORITY_MASK;
1106d7cecbd1SLuiz Otavio O Souza 
1107d7cecbd1SLuiz Otavio O Souza 		/* Set VID map */
1108d7cecbd1SLuiz Otavio O Souza 		ret &= ~PORT_VID_DEF_VID_MASK;
1109d7cecbd1SLuiz Otavio O Souza 		if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q)
1110d7cecbd1SLuiz Otavio O Souza 			ret |= 1;
1111d7cecbd1SLuiz Otavio O Souza 		else
1112d7cecbd1SLuiz Otavio O Souza 			ret |= (port + 1);
11139aba0637SLuiz Otavio O Souza 		e6000sw_writereg(sc, REG_PORT(sc, port), PORT_VID, ret);
1114d7cecbd1SLuiz Otavio O Souza 	}
1115d7cecbd1SLuiz Otavio O Souza 
1116d7cecbd1SLuiz Otavio O Souza 	/* Assign the member ports to each origin port. */
1117d7cecbd1SLuiz Otavio O Souza 	for (port = 0; port < sc->num_ports; port++) {
1118d7cecbd1SLuiz Otavio O Souza 		members = 0;
1119d7cecbd1SLuiz Otavio O Souza 		if (e6000sw_is_portenabled(sc, port)) {
1120d7cecbd1SLuiz Otavio O Souza 			for (i = 0; i < sc->num_ports; i++) {
1121d7cecbd1SLuiz Otavio O Souza 				if (i == port || !e6000sw_is_portenabled(sc, i))
1122d7cecbd1SLuiz Otavio O Souza 					continue;
1123d7cecbd1SLuiz Otavio O Souza 				members |= (1 << i);
1124d7cecbd1SLuiz Otavio O Souza 			}
1125d7cecbd1SLuiz Otavio O Souza 		}
1126d7cecbd1SLuiz Otavio O Souza 		/* Default to FID 0. */
1127d7cecbd1SLuiz Otavio O Souza 		e6000sw_port_vlan_assign(sc, port, 0, members);
1128d7cecbd1SLuiz Otavio O Souza 	}
1129d7cecbd1SLuiz Otavio O Souza 
1130d7cecbd1SLuiz Otavio O Souza 	/* Reset internal VLAN table. */
1131d7cecbd1SLuiz Otavio O Souza 	for (i = 0; i < nitems(sc->vlans); i++)
1132d7cecbd1SLuiz Otavio O Souza 		sc->vlans[i] = 0;
1133d7cecbd1SLuiz Otavio O Souza 
1134d7cecbd1SLuiz Otavio O Souza 	/* Create default VLAN (1). */
1135d7cecbd1SLuiz Otavio O Souza 	if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
1136d7cecbd1SLuiz Otavio O Souza 		sc->vlans[0] = 1;
1137d7cecbd1SLuiz Otavio O Souza 		e6000sw_vtu_update(sc, 0, sc->vlans[0], 1, 0, sc->ports_mask);
1138d7cecbd1SLuiz Otavio O Souza 	}
1139d7cecbd1SLuiz Otavio O Souza 
1140d7cecbd1SLuiz Otavio O Souza 	/* Enable all ports */
1141d7cecbd1SLuiz Otavio O Souza 	for (port = 0; port < sc->num_ports; port++) {
1142d7cecbd1SLuiz Otavio O Souza 		if (!e6000sw_is_portenabled(sc, port))
1143d7cecbd1SLuiz Otavio O Souza 			continue;
11449aba0637SLuiz Otavio O Souza 		ret = e6000sw_readreg(sc, REG_PORT(sc, port), PORT_CONTROL);
11459aba0637SLuiz Otavio O Souza 		e6000sw_writereg(sc, REG_PORT(sc, port), PORT_CONTROL,
1146d7cecbd1SLuiz Otavio O Souza 		    (ret | PORT_CONTROL_ENABLE));
1147d7cecbd1SLuiz Otavio O Souza 	}
1148d7cecbd1SLuiz Otavio O Souza 
1149d7cecbd1SLuiz Otavio O Souza 	return (0);
1150d7cecbd1SLuiz Otavio O Souza }
1151d7cecbd1SLuiz Otavio O Souza 
1152d7cecbd1SLuiz Otavio O Souza static int
1153d7cecbd1SLuiz Otavio O Souza e6000sw_set_vlan_mode(struct e6000sw_softc *sc, uint32_t mode)
1154d7cecbd1SLuiz Otavio O Souza {
1155d7cecbd1SLuiz Otavio O Souza 
1156d7cecbd1SLuiz Otavio O Souza 	E6000SW_LOCK_ASSERT(sc, SA_XLOCKED);
1157d7cecbd1SLuiz Otavio O Souza 	switch (mode) {
1158d7cecbd1SLuiz Otavio O Souza 	case ETHERSWITCH_VLAN_PORT:
1159d7cecbd1SLuiz Otavio O Souza 		sc->vlan_mode = ETHERSWITCH_VLAN_PORT;
1160d7cecbd1SLuiz Otavio O Souza 		etherswitch_info.es_nvlangroups = sc->num_ports;
1161d7cecbd1SLuiz Otavio O Souza 		return (e6000sw_init_vlan(sc));
1162d7cecbd1SLuiz Otavio O Souza 		break;
1163d7cecbd1SLuiz Otavio O Souza 	case ETHERSWITCH_VLAN_DOT1Q:
1164d7cecbd1SLuiz Otavio O Souza 		sc->vlan_mode = ETHERSWITCH_VLAN_DOT1Q;
1165d7cecbd1SLuiz Otavio O Souza 		etherswitch_info.es_nvlangroups = E6000SW_NUM_VLANS;
1166d7cecbd1SLuiz Otavio O Souza 		return (e6000sw_init_vlan(sc));
1167d7cecbd1SLuiz Otavio O Souza 		break;
1168d7cecbd1SLuiz Otavio O Souza 	default:
1169d7cecbd1SLuiz Otavio O Souza 		return (EINVAL);
1170d7cecbd1SLuiz Otavio O Souza 	}
1171d7cecbd1SLuiz Otavio O Souza }
1172d7cecbd1SLuiz Otavio O Souza 
11735420071dSZbigniew Bodek /*
11745420071dSZbigniew Bodek  * Registers in this switch are divided into sections, specified in
11755420071dSZbigniew Bodek  * documentation. So as to access any of them, section index and reg index
11765420071dSZbigniew Bodek  * is necessary. etherswitchcfg uses only one variable, so indexes were
11775420071dSZbigniew Bodek  * compressed into addr_reg: 32 * section_index + reg_index.
11785420071dSZbigniew Bodek  */
11795420071dSZbigniew Bodek static int
11805420071dSZbigniew Bodek e6000sw_readreg_wrapper(device_t dev, int addr_reg)
11815420071dSZbigniew Bodek {
11829aba0637SLuiz Otavio O Souza 	e6000sw_softc_t *sc;
11835420071dSZbigniew Bodek 
11849aba0637SLuiz Otavio O Souza 	sc = device_get_softc(dev);
11855420071dSZbigniew Bodek 	if ((addr_reg > (REG_GLOBAL2 * 32 + REG_NUM_MAX)) ||
11869aba0637SLuiz Otavio O Souza 	    (addr_reg < (REG_PORT(sc, 0) * 32))) {
11875420071dSZbigniew Bodek 		device_printf(dev, "Wrong register address.\n");
11885420071dSZbigniew Bodek 		return (EINVAL);
11895420071dSZbigniew Bodek 	}
11905420071dSZbigniew Bodek 
11915420071dSZbigniew Bodek 	return (e6000sw_readreg(device_get_softc(dev), addr_reg / 32,
11925420071dSZbigniew Bodek 	    addr_reg % 32));
11935420071dSZbigniew Bodek }
11945420071dSZbigniew Bodek 
11955420071dSZbigniew Bodek static int
11965420071dSZbigniew Bodek e6000sw_writereg_wrapper(device_t dev, int addr_reg, int val)
11975420071dSZbigniew Bodek {
11989aba0637SLuiz Otavio O Souza 	e6000sw_softc_t *sc;
11995420071dSZbigniew Bodek 
12009aba0637SLuiz Otavio O Souza 	sc = device_get_softc(dev);
12015420071dSZbigniew Bodek 	if ((addr_reg > (REG_GLOBAL2 * 32 + REG_NUM_MAX)) ||
12029aba0637SLuiz Otavio O Souza 	    (addr_reg < (REG_PORT(sc, 0) * 32))) {
12035420071dSZbigniew Bodek 		device_printf(dev, "Wrong register address.\n");
12045420071dSZbigniew Bodek 		return (EINVAL);
12055420071dSZbigniew Bodek 	}
120666548259SKornel Dulęba 	e6000sw_writereg(device_get_softc(dev), addr_reg / 32,
12075420071dSZbigniew Bodek 	    addr_reg % 32, val);
12085420071dSZbigniew Bodek 
12095420071dSZbigniew Bodek 	return (0);
12105420071dSZbigniew Bodek }
12115420071dSZbigniew Bodek 
12125420071dSZbigniew Bodek /*
12135420071dSZbigniew Bodek  * setvgroup/getvgroup called from etherswitchfcg need to be locked,
12145420071dSZbigniew Bodek  * while internal calls do not.
12155420071dSZbigniew Bodek  */
12165420071dSZbigniew Bodek static int
12175420071dSZbigniew Bodek e6000sw_setvgroup_wrapper(device_t dev, etherswitch_vlangroup_t *vg)
12185420071dSZbigniew Bodek {
12195420071dSZbigniew Bodek 	e6000sw_softc_t *sc;
12205420071dSZbigniew Bodek 	int ret;
12215420071dSZbigniew Bodek 
12225420071dSZbigniew Bodek 	sc = device_get_softc(dev);
12235420071dSZbigniew Bodek 	E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED);
12245420071dSZbigniew Bodek 
12255420071dSZbigniew Bodek 	E6000SW_LOCK(sc);
12265420071dSZbigniew Bodek 	ret = e6000sw_setvgroup(dev, vg);
12275420071dSZbigniew Bodek 	E6000SW_UNLOCK(sc);
12285420071dSZbigniew Bodek 
12295420071dSZbigniew Bodek 	return (ret);
12305420071dSZbigniew Bodek }
12315420071dSZbigniew Bodek 
12325420071dSZbigniew Bodek static int
12335420071dSZbigniew Bodek e6000sw_getvgroup_wrapper(device_t dev, etherswitch_vlangroup_t *vg)
12345420071dSZbigniew Bodek {
12355420071dSZbigniew Bodek 	e6000sw_softc_t *sc;
12365420071dSZbigniew Bodek 	int ret;
12375420071dSZbigniew Bodek 
12385420071dSZbigniew Bodek 	sc = device_get_softc(dev);
12395420071dSZbigniew Bodek 	E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED);
12405420071dSZbigniew Bodek 
12415420071dSZbigniew Bodek 	E6000SW_LOCK(sc);
12425420071dSZbigniew Bodek 	ret = e6000sw_getvgroup(dev, vg);
12435420071dSZbigniew Bodek 	E6000SW_UNLOCK(sc);
12445420071dSZbigniew Bodek 
12455420071dSZbigniew Bodek 	return (ret);
12465420071dSZbigniew Bodek }
12475420071dSZbigniew Bodek 
12480e779c2fSLuiz Otavio O Souza static int
12490e779c2fSLuiz Otavio O Souza e6000sw_set_port_vlan(e6000sw_softc_t *sc, etherswitch_vlangroup_t *vg)
12500e779c2fSLuiz Otavio O Souza {
12510e779c2fSLuiz Otavio O Souza 	uint32_t port;
12520e779c2fSLuiz Otavio O Souza 
12530e779c2fSLuiz Otavio O Souza 	port = vg->es_vlangroup;
12540e779c2fSLuiz Otavio O Souza 	if (port > sc->num_ports)
12550e779c2fSLuiz Otavio O Souza 		return (EINVAL);
12560e779c2fSLuiz Otavio O Souza 
12570e779c2fSLuiz Otavio O Souza 	if (vg->es_member_ports != vg->es_untagged_ports) {
12580e779c2fSLuiz Otavio O Souza 		device_printf(sc->dev, "Tagged ports not supported.\n");
12590e779c2fSLuiz Otavio O Souza 		return (EINVAL);
12600e779c2fSLuiz Otavio O Souza 	}
12610e779c2fSLuiz Otavio O Souza 
1262d7cecbd1SLuiz Otavio O Souza 	e6000sw_port_vlan_assign(sc, port, 0, vg->es_untagged_ports);
12630e779c2fSLuiz Otavio O Souza 	vg->es_vid = port | ETHERSWITCH_VID_VALID;
12640e779c2fSLuiz Otavio O Souza 
12650e779c2fSLuiz Otavio O Souza 	return (0);
12665420071dSZbigniew Bodek }
12675420071dSZbigniew Bodek 
12685420071dSZbigniew Bodek static int
1269d7cecbd1SLuiz Otavio O Souza e6000sw_set_dot1q_vlan(e6000sw_softc_t *sc, etherswitch_vlangroup_t *vg)
1270d7cecbd1SLuiz Otavio O Souza {
1271d7cecbd1SLuiz Otavio O Souza 	int i, vlan;
1272d7cecbd1SLuiz Otavio O Souza 
1273d7cecbd1SLuiz Otavio O Souza 	vlan = vg->es_vid & ETHERSWITCH_VID_MASK;
1274d7cecbd1SLuiz Otavio O Souza 
1275d7cecbd1SLuiz Otavio O Souza 	/* Set VLAN to '0' removes it from table. */
1276d7cecbd1SLuiz Otavio O Souza 	if (vlan == 0) {
1277d7cecbd1SLuiz Otavio O Souza 		e6000sw_vtu_update(sc, VTU_PURGE,
1278d7cecbd1SLuiz Otavio O Souza 		    sc->vlans[vg->es_vlangroup], 0, 0, 0);
1279d7cecbd1SLuiz Otavio O Souza 		sc->vlans[vg->es_vlangroup] = 0;
1280d7cecbd1SLuiz Otavio O Souza 		return (0);
1281d7cecbd1SLuiz Otavio O Souza 	}
1282d7cecbd1SLuiz Otavio O Souza 
1283d7cecbd1SLuiz Otavio O Souza 	/* Is this VLAN already in table ? */
1284d7cecbd1SLuiz Otavio O Souza 	for (i = 0; i < etherswitch_info.es_nvlangroups; i++)
1285d7cecbd1SLuiz Otavio O Souza 		if (i != vg->es_vlangroup && vlan == sc->vlans[i])
1286d7cecbd1SLuiz Otavio O Souza 			return (EINVAL);
1287d7cecbd1SLuiz Otavio O Souza 
1288d7cecbd1SLuiz Otavio O Souza 	sc->vlans[vg->es_vlangroup] = vlan;
1289d7cecbd1SLuiz Otavio O Souza 	e6000sw_vtu_update(sc, 0, vlan, vg->es_vlangroup + 1,
1290d7cecbd1SLuiz Otavio O Souza 	    vg->es_member_ports & sc->ports_mask,
1291d7cecbd1SLuiz Otavio O Souza 	    vg->es_untagged_ports & sc->ports_mask);
1292d7cecbd1SLuiz Otavio O Souza 
1293d7cecbd1SLuiz Otavio O Souza 	return (0);
1294d7cecbd1SLuiz Otavio O Souza }
1295d7cecbd1SLuiz Otavio O Souza 
1296d7cecbd1SLuiz Otavio O Souza static int
12975420071dSZbigniew Bodek e6000sw_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
12985420071dSZbigniew Bodek {
12995420071dSZbigniew Bodek 	e6000sw_softc_t *sc;
13005420071dSZbigniew Bodek 
13015420071dSZbigniew Bodek 	sc = device_get_softc(dev);
13025420071dSZbigniew Bodek 	E6000SW_LOCK_ASSERT(sc, SA_XLOCKED);
13035420071dSZbigniew Bodek 
13040e779c2fSLuiz Otavio O Souza 	if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT)
13050e779c2fSLuiz Otavio O Souza 		return (e6000sw_set_port_vlan(sc, vg));
1306d7cecbd1SLuiz Otavio O Souza 	else if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q)
1307d7cecbd1SLuiz Otavio O Souza 		return (e6000sw_set_dot1q_vlan(sc, vg));
13080e779c2fSLuiz Otavio O Souza 
13095420071dSZbigniew Bodek 	return (EINVAL);
13105420071dSZbigniew Bodek }
13115420071dSZbigniew Bodek 
13120e779c2fSLuiz Otavio O Souza static int
13130e779c2fSLuiz Otavio O Souza e6000sw_get_port_vlan(e6000sw_softc_t *sc, etherswitch_vlangroup_t *vg)
13140e779c2fSLuiz Otavio O Souza {
13150e779c2fSLuiz Otavio O Souza 	uint32_t port, reg;
13160e779c2fSLuiz Otavio O Souza 
13170e779c2fSLuiz Otavio O Souza 	port = vg->es_vlangroup;
13180e779c2fSLuiz Otavio O Souza 	if (port > sc->num_ports)
13190e779c2fSLuiz Otavio O Souza 		return (EINVAL);
13200e779c2fSLuiz Otavio O Souza 
13210e779c2fSLuiz Otavio O Souza 	if (!e6000sw_is_portenabled(sc, port)) {
13220e779c2fSLuiz Otavio O Souza 		vg->es_vid = port;
13230e779c2fSLuiz Otavio O Souza 		return (0);
13245420071dSZbigniew Bodek 	}
13250e779c2fSLuiz Otavio O Souza 
13269aba0637SLuiz Otavio O Souza 	reg = e6000sw_readreg(sc, REG_PORT(sc, port), PORT_VLAN_MAP);
1327d7cecbd1SLuiz Otavio O Souza 	vg->es_untagged_ports = vg->es_member_ports = reg & PORT_MASK(sc);
13280e779c2fSLuiz Otavio O Souza 	vg->es_vid = port | ETHERSWITCH_VID_VALID;
13290e779c2fSLuiz Otavio O Souza 	vg->es_fid = (reg & PORT_VLAN_MAP_FID_MASK) >> PORT_VLAN_MAP_FID;
13309aba0637SLuiz Otavio O Souza 	reg = e6000sw_readreg(sc, REG_PORT(sc, port), PORT_CONTROL1);
1331d7cecbd1SLuiz Otavio O Souza 	vg->es_fid |= (reg & PORT_CONTROL1_FID_MASK) << 4;
1332d7cecbd1SLuiz Otavio O Souza 
1333d7cecbd1SLuiz Otavio O Souza 	return (0);
1334d7cecbd1SLuiz Otavio O Souza }
1335d7cecbd1SLuiz Otavio O Souza 
1336d7cecbd1SLuiz Otavio O Souza static int
1337d7cecbd1SLuiz Otavio O Souza e6000sw_get_dot1q_vlan(e6000sw_softc_t *sc, etherswitch_vlangroup_t *vg)
1338d7cecbd1SLuiz Otavio O Souza {
1339d7cecbd1SLuiz Otavio O Souza 	int i, port;
1340d7cecbd1SLuiz Otavio O Souza 	uint32_t reg;
1341d7cecbd1SLuiz Otavio O Souza 
1342d7cecbd1SLuiz Otavio O Souza 	vg->es_fid = 0;
1343d7cecbd1SLuiz Otavio O Souza 	vg->es_vid = sc->vlans[vg->es_vlangroup];
1344d7cecbd1SLuiz Otavio O Souza 	vg->es_untagged_ports = vg->es_member_ports = 0;
1345d7cecbd1SLuiz Otavio O Souza 	if (vg->es_vid == 0)
1346d7cecbd1SLuiz Otavio O Souza 		return (0);
1347d7cecbd1SLuiz Otavio O Souza 
1348d7cecbd1SLuiz Otavio O Souza 	if (E6000SW_WAITREADY(sc, VTU_OPERATION, VTU_BUSY)) {
1349d7cecbd1SLuiz Otavio O Souza 		device_printf(sc->dev, "VTU unit is busy, cannot access\n");
1350d7cecbd1SLuiz Otavio O Souza 		return (EBUSY);
1351d7cecbd1SLuiz Otavio O Souza 	}
1352d7cecbd1SLuiz Otavio O Souza 
1353d7cecbd1SLuiz Otavio O Souza 	e6000sw_writereg(sc, REG_GLOBAL, VTU_VID, vg->es_vid - 1);
1354d7cecbd1SLuiz Otavio O Souza 
1355d7cecbd1SLuiz Otavio O Souza 	reg = e6000sw_readreg(sc, REG_GLOBAL, VTU_OPERATION);
1356d7cecbd1SLuiz Otavio O Souza 	reg &= ~VTU_OP_MASK;
1357d7cecbd1SLuiz Otavio O Souza 	reg |= VTU_GET_NEXT | VTU_BUSY;
1358d7cecbd1SLuiz Otavio O Souza 	e6000sw_writereg(sc, REG_GLOBAL, VTU_OPERATION, reg);
1359d7cecbd1SLuiz Otavio O Souza 	if (E6000SW_WAITREADY(sc, VTU_OPERATION, VTU_BUSY)) {
1360d7cecbd1SLuiz Otavio O Souza 		device_printf(sc->dev, "Timeout while reading\n");
1361d7cecbd1SLuiz Otavio O Souza 		return (EBUSY);
1362d7cecbd1SLuiz Otavio O Souza 	}
1363d7cecbd1SLuiz Otavio O Souza 
1364d7cecbd1SLuiz Otavio O Souza 	reg = e6000sw_readreg(sc, REG_GLOBAL, VTU_VID);
1365d7cecbd1SLuiz Otavio O Souza 	if (reg == VTU_VID_MASK || (reg & VTU_VID_VALID) == 0)
1366d7cecbd1SLuiz Otavio O Souza 		return (EINVAL);
1367d7cecbd1SLuiz Otavio O Souza 	if ((reg & VTU_VID_MASK) != vg->es_vid)
1368d7cecbd1SLuiz Otavio O Souza 		return (EINVAL);
1369d7cecbd1SLuiz Otavio O Souza 
1370d7cecbd1SLuiz Otavio O Souza 	vg->es_vid |= ETHERSWITCH_VID_VALID;
1371d7cecbd1SLuiz Otavio O Souza 	reg = e6000sw_readreg(sc, REG_GLOBAL, VTU_DATA);
1372d7cecbd1SLuiz Otavio O Souza 	for (i = 0; i < sc->num_ports; i++) {
1373d7cecbd1SLuiz Otavio O Souza 		if (i == VTU_PPREG(sc))
1374d7cecbd1SLuiz Otavio O Souza 			reg = e6000sw_readreg(sc, REG_GLOBAL, VTU_DATA2);
1375d7cecbd1SLuiz Otavio O Souza 		port = (reg >> VTU_PORT(sc, i)) & VTU_PORT_MASK;
1376d7cecbd1SLuiz Otavio O Souza 		if (port == VTU_PORT_UNTAGGED) {
1377d7cecbd1SLuiz Otavio O Souza 			vg->es_untagged_ports |= (1 << i);
1378d7cecbd1SLuiz Otavio O Souza 			vg->es_member_ports |= (1 << i);
1379d7cecbd1SLuiz Otavio O Souza 		} else if (port == VTU_PORT_TAGGED)
1380d7cecbd1SLuiz Otavio O Souza 			vg->es_member_ports |= (1 << i);
1381d7cecbd1SLuiz Otavio O Souza 	}
13825420071dSZbigniew Bodek 
13835420071dSZbigniew Bodek 	return (0);
13845420071dSZbigniew Bodek }
13855420071dSZbigniew Bodek 
13865420071dSZbigniew Bodek static int
13875420071dSZbigniew Bodek e6000sw_getvgroup(device_t dev, etherswitch_vlangroup_t *vg)
13885420071dSZbigniew Bodek {
13895420071dSZbigniew Bodek 	e6000sw_softc_t *sc;
13905420071dSZbigniew Bodek 
13915420071dSZbigniew Bodek 	sc = device_get_softc(dev);
13925420071dSZbigniew Bodek 	E6000SW_LOCK_ASSERT(sc, SA_XLOCKED);
13935420071dSZbigniew Bodek 
13940e779c2fSLuiz Otavio O Souza 	if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT)
13950e779c2fSLuiz Otavio O Souza 		return (e6000sw_get_port_vlan(sc, vg));
1396d7cecbd1SLuiz Otavio O Souza 	else if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q)
1397d7cecbd1SLuiz Otavio O Souza 		return (e6000sw_get_dot1q_vlan(sc, vg));
13985420071dSZbigniew Bodek 
13990e779c2fSLuiz Otavio O Souza 	return (EINVAL);
14005420071dSZbigniew Bodek }
14015420071dSZbigniew Bodek 
14025420071dSZbigniew Bodek static __inline struct mii_data*
14035420071dSZbigniew Bodek e6000sw_miiforphy(e6000sw_softc_t *sc, unsigned int phy)
14045420071dSZbigniew Bodek {
1405c8e97aa6SAdrian Chadd 	device_t mii_dev;
14065420071dSZbigniew Bodek 
1407f7c13d78SZbigniew Bodek 	if (!e6000sw_is_phyport(sc, phy))
14085420071dSZbigniew Bodek 		return (NULL);
1409c8e97aa6SAdrian Chadd 	mii_dev = sc->miibus[phy];
1410c8e97aa6SAdrian Chadd 	if (mii_dev == NULL)
1411c8e97aa6SAdrian Chadd 		return (NULL);
1412c8e97aa6SAdrian Chadd 	if (device_get_state(mii_dev) != DS_ATTACHED)
1413c8e97aa6SAdrian Chadd 		return (NULL);
14145420071dSZbigniew Bodek 
1415c8e97aa6SAdrian Chadd 	return (device_get_softc(mii_dev));
14165420071dSZbigniew Bodek }
14175420071dSZbigniew Bodek 
14185420071dSZbigniew Bodek static int
14192e6a8c1aSJustin Hibbits e6000sw_ifmedia_upd(if_t ifp)
14205420071dSZbigniew Bodek {
14215420071dSZbigniew Bodek 	e6000sw_softc_t *sc;
14225420071dSZbigniew Bodek 	struct mii_data *mii;
14235420071dSZbigniew Bodek 
14242e6a8c1aSJustin Hibbits 	sc = if_getsoftc(ifp);
14252e6a8c1aSJustin Hibbits 	mii = e6000sw_miiforphy(sc, if_getdunit(ifp));
14265420071dSZbigniew Bodek 	if (mii == NULL)
14275420071dSZbigniew Bodek 		return (ENXIO);
14285420071dSZbigniew Bodek 	mii_mediachg(mii);
14295420071dSZbigniew Bodek 
14305420071dSZbigniew Bodek 	return (0);
14315420071dSZbigniew Bodek }
14325420071dSZbigniew Bodek 
14335420071dSZbigniew Bodek static void
14342e6a8c1aSJustin Hibbits e6000sw_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr)
14355420071dSZbigniew Bodek {
14365420071dSZbigniew Bodek 	e6000sw_softc_t *sc;
14375420071dSZbigniew Bodek 	struct mii_data *mii;
14385420071dSZbigniew Bodek 
14392e6a8c1aSJustin Hibbits 	sc = if_getsoftc(ifp);
14402e6a8c1aSJustin Hibbits 	mii = e6000sw_miiforphy(sc, if_getdunit(ifp));
14415420071dSZbigniew Bodek 
14425420071dSZbigniew Bodek 	if (mii == NULL)
14435420071dSZbigniew Bodek 		return;
14445420071dSZbigniew Bodek 
14455420071dSZbigniew Bodek 	mii_pollstat(mii);
14465420071dSZbigniew Bodek 	ifmr->ifm_active = mii->mii_media_active;
14475420071dSZbigniew Bodek 	ifmr->ifm_status = mii->mii_media_status;
14485420071dSZbigniew Bodek }
14495420071dSZbigniew Bodek 
1450f7c13d78SZbigniew Bodek static int
1451f7c13d78SZbigniew Bodek e6000sw_smi_waitready(e6000sw_softc_t *sc, int phy)
1452f7c13d78SZbigniew Bodek {
1453f7c13d78SZbigniew Bodek 	int i;
1454f7c13d78SZbigniew Bodek 
1455f7c13d78SZbigniew Bodek 	for (i = 0; i < E6000SW_SMI_TIMEOUT; i++) {
145633c2a3cbSLuiz Otavio O Souza 		if ((MDIO_READ(sc->dev, phy, SMI_CMD) & SMI_CMD_BUSY) == 0)
145733c2a3cbSLuiz Otavio O Souza 			return (0);
145833c2a3cbSLuiz Otavio O Souza 		DELAY(1);
1459f7c13d78SZbigniew Bodek 	}
1460f7c13d78SZbigniew Bodek 
146133c2a3cbSLuiz Otavio O Souza 	return (1);
1462f7c13d78SZbigniew Bodek }
1463f7c13d78SZbigniew Bodek 
14645420071dSZbigniew Bodek static __inline uint32_t
14655420071dSZbigniew Bodek e6000sw_readreg(e6000sw_softc_t *sc, int addr, int reg)
14665420071dSZbigniew Bodek {
14675420071dSZbigniew Bodek 
14685420071dSZbigniew Bodek 	E6000SW_LOCK_ASSERT(sc, SA_XLOCKED);
14695420071dSZbigniew Bodek 
1470d7cecbd1SLuiz Otavio O Souza 	if (!MVSWITCH_MULTICHIP(sc))
1471f7c13d78SZbigniew Bodek 		return (MDIO_READ(sc->dev, addr, reg) & 0xffff);
1472f7c13d78SZbigniew Bodek 
1473f7c13d78SZbigniew Bodek 	if (e6000sw_smi_waitready(sc, sc->sw_addr)) {
1474f7c13d78SZbigniew Bodek 		printf("e6000sw: readreg timeout\n");
1475f7c13d78SZbigniew Bodek 		return (0xffff);
1476f7c13d78SZbigniew Bodek 	}
147733c2a3cbSLuiz Otavio O Souza 	MDIO_WRITE(sc->dev, sc->sw_addr, SMI_CMD,
1478d7cecbd1SLuiz Otavio O Souza 	    SMI_CMD_OP_C22_READ | (reg & SMI_CMD_REG_ADDR_MASK) |
1479d7cecbd1SLuiz Otavio O Souza 	    ((addr << SMI_CMD_DEV_ADDR) & SMI_CMD_DEV_ADDR_MASK));
1480f7c13d78SZbigniew Bodek 	if (e6000sw_smi_waitready(sc, sc->sw_addr)) {
1481f7c13d78SZbigniew Bodek 		printf("e6000sw: readreg timeout\n");
1482f7c13d78SZbigniew Bodek 		return (0xffff);
1483f7c13d78SZbigniew Bodek 	}
1484f7c13d78SZbigniew Bodek 
1485f7c13d78SZbigniew Bodek 	return (MDIO_READ(sc->dev, sc->sw_addr, SMI_DATA) & 0xffff);
14865420071dSZbigniew Bodek }
14875420071dSZbigniew Bodek 
14885420071dSZbigniew Bodek static __inline void
14895420071dSZbigniew Bodek e6000sw_writereg(e6000sw_softc_t *sc, int addr, int reg, int val)
14905420071dSZbigniew Bodek {
14915420071dSZbigniew Bodek 
14925420071dSZbigniew Bodek 	E6000SW_LOCK_ASSERT(sc, SA_XLOCKED);
14935420071dSZbigniew Bodek 
1494d7cecbd1SLuiz Otavio O Souza 	if (!MVSWITCH_MULTICHIP(sc)) {
1495f7c13d78SZbigniew Bodek 		MDIO_WRITE(sc->dev, addr, reg, val);
1496f7c13d78SZbigniew Bodek 		return;
1497f7c13d78SZbigniew Bodek 	}
1498f7c13d78SZbigniew Bodek 
1499f7c13d78SZbigniew Bodek 	if (e6000sw_smi_waitready(sc, sc->sw_addr)) {
1500f7c13d78SZbigniew Bodek 		printf("e6000sw: readreg timeout\n");
1501f7c13d78SZbigniew Bodek 		return;
1502f7c13d78SZbigniew Bodek 	}
1503f7c13d78SZbigniew Bodek 	MDIO_WRITE(sc->dev, sc->sw_addr, SMI_DATA, val);
150433c2a3cbSLuiz Otavio O Souza 	MDIO_WRITE(sc->dev, sc->sw_addr, SMI_CMD,
1505d7cecbd1SLuiz Otavio O Souza 	    SMI_CMD_OP_C22_WRITE | (reg & SMI_CMD_REG_ADDR_MASK) |
1506d7cecbd1SLuiz Otavio O Souza 	    ((addr << SMI_CMD_DEV_ADDR) & SMI_CMD_DEV_ADDR_MASK));
15075420071dSZbigniew Bodek }
15085420071dSZbigniew Bodek 
1509595d629cSLuiz Otavio O Souza static __inline bool
1510f7c13d78SZbigniew Bodek e6000sw_is_cpuport(e6000sw_softc_t *sc, int port)
15115420071dSZbigniew Bodek {
15125420071dSZbigniew Bodek 
1513595d629cSLuiz Otavio O Souza 	return ((sc->cpuports_mask & (1 << port)) ? true : false);
15145420071dSZbigniew Bodek }
15155420071dSZbigniew Bodek 
1516595d629cSLuiz Otavio O Souza static __inline bool
1517f7c13d78SZbigniew Bodek e6000sw_is_fixedport(e6000sw_softc_t *sc, int port)
1518f7c13d78SZbigniew Bodek {
1519f7c13d78SZbigniew Bodek 
1520595d629cSLuiz Otavio O Souza 	return ((sc->fixed_mask & (1 << port)) ? true : false);
1521f7c13d78SZbigniew Bodek }
1522f7c13d78SZbigniew Bodek 
1523595d629cSLuiz Otavio O Souza static __inline bool
1524091d140cSLuiz Otavio O Souza e6000sw_is_fixed25port(e6000sw_softc_t *sc, int port)
1525091d140cSLuiz Otavio O Souza {
1526091d140cSLuiz Otavio O Souza 
1527091d140cSLuiz Otavio O Souza 	return ((sc->fixed25_mask & (1 << port)) ? true : false);
1528091d140cSLuiz Otavio O Souza }
1529091d140cSLuiz Otavio O Souza 
1530091d140cSLuiz Otavio O Souza static __inline bool
1531f7c13d78SZbigniew Bodek e6000sw_is_phyport(e6000sw_softc_t *sc, int port)
1532f7c13d78SZbigniew Bodek {
1533f7c13d78SZbigniew Bodek 	uint32_t phy_mask;
1534f7c13d78SZbigniew Bodek 	phy_mask = ~(sc->fixed_mask | sc->cpuports_mask);
1535f7c13d78SZbigniew Bodek 
1536595d629cSLuiz Otavio O Souza 	return ((phy_mask & (1 << port)) ? true : false);
1537f7c13d78SZbigniew Bodek }
1538f7c13d78SZbigniew Bodek 
1539ff2748ecSLuiz Otavio O Souza static __inline bool
1540ff2748ecSLuiz Otavio O Souza e6000sw_is_portenabled(e6000sw_softc_t *sc, int port)
1541ff2748ecSLuiz Otavio O Souza {
1542ff2748ecSLuiz Otavio O Souza 
1543ff2748ecSLuiz Otavio O Souza 	return ((sc->ports_mask & (1 << port)) ? true : false);
1544ff2748ecSLuiz Otavio O Souza }
1545ff2748ecSLuiz Otavio O Souza 
1546d7cecbd1SLuiz Otavio O Souza static __inline void
15475420071dSZbigniew Bodek e6000sw_set_pvid(e6000sw_softc_t *sc, int port, int pvid)
15485420071dSZbigniew Bodek {
1549d7cecbd1SLuiz Otavio O Souza 	uint32_t reg;
15505420071dSZbigniew Bodek 
15519aba0637SLuiz Otavio O Souza 	reg = e6000sw_readreg(sc, REG_PORT(sc, port), PORT_VID);
1552d7cecbd1SLuiz Otavio O Souza 	reg &= ~PORT_VID_DEF_VID_MASK;
1553d7cecbd1SLuiz Otavio O Souza 	reg |= (pvid & PORT_VID_DEF_VID_MASK);
15549aba0637SLuiz Otavio O Souza 	e6000sw_writereg(sc, REG_PORT(sc, port), PORT_VID, reg);
15555420071dSZbigniew Bodek }
15565420071dSZbigniew Bodek 
15575420071dSZbigniew Bodek static __inline int
15585420071dSZbigniew Bodek e6000sw_get_pvid(e6000sw_softc_t *sc, int port, int *pvid)
15595420071dSZbigniew Bodek {
15605420071dSZbigniew Bodek 
15615420071dSZbigniew Bodek 	if (pvid == NULL)
15625420071dSZbigniew Bodek 		return (ENXIO);
15635420071dSZbigniew Bodek 
15649aba0637SLuiz Otavio O Souza 	*pvid = e6000sw_readreg(sc, REG_PORT(sc, port), PORT_VID) &
15655420071dSZbigniew Bodek 	    PORT_VID_DEF_VID_MASK;
15665420071dSZbigniew Bodek 
15675420071dSZbigniew Bodek 	return (0);
15685420071dSZbigniew Bodek }
15695420071dSZbigniew Bodek 
15702dd02006SWojciech Macek /*
15712dd02006SWojciech Macek  * Convert port status to ifmedia.
15722dd02006SWojciech Macek  */
15732dd02006SWojciech Macek static void
15742dd02006SWojciech Macek e6000sw_update_ifmedia(uint16_t portstatus, u_int *media_status, u_int *media_active)
15752dd02006SWojciech Macek {
15762dd02006SWojciech Macek 	*media_active = IFM_ETHER;
15772dd02006SWojciech Macek 	*media_status = IFM_AVALID;
15782dd02006SWojciech Macek 
15792dd02006SWojciech Macek 	if ((portstatus & PORT_STATUS_LINK_MASK) != 0)
15802dd02006SWojciech Macek 		*media_status |= IFM_ACTIVE;
15812dd02006SWojciech Macek 	else {
15822dd02006SWojciech Macek 		*media_active |= IFM_NONE;
15832dd02006SWojciech Macek 		return;
15842dd02006SWojciech Macek 	}
15852dd02006SWojciech Macek 
15862dd02006SWojciech Macek 	switch (portstatus & PORT_STATUS_SPEED_MASK) {
15872dd02006SWojciech Macek 	case PORT_STATUS_SPEED_10:
15882dd02006SWojciech Macek 		*media_active |= IFM_10_T;
15892dd02006SWojciech Macek 		break;
15902dd02006SWojciech Macek 	case PORT_STATUS_SPEED_100:
15912dd02006SWojciech Macek 		*media_active |= IFM_100_TX;
15922dd02006SWojciech Macek 		break;
15932dd02006SWojciech Macek 	case PORT_STATUS_SPEED_1000:
15942dd02006SWojciech Macek 		*media_active |= IFM_1000_T;
15952dd02006SWojciech Macek 		break;
15962dd02006SWojciech Macek 	}
15972dd02006SWojciech Macek 
15982dd02006SWojciech Macek 	if ((portstatus & PORT_STATUS_DUPLEX_MASK) == 0)
15992dd02006SWojciech Macek 		*media_active |= IFM_FDX;
16002dd02006SWojciech Macek 	else
16012dd02006SWojciech Macek 		*media_active |= IFM_HDX;
16022dd02006SWojciech Macek }
16032dd02006SWojciech Macek 
16045420071dSZbigniew Bodek static void
16051e125351SHubert Mazur e6000sw_tick(void *arg, int p __unused)
16065420071dSZbigniew Bodek {
16075420071dSZbigniew Bodek 	e6000sw_softc_t *sc;
1608595d629cSLuiz Otavio O Souza 	struct mii_data *mii;
16095420071dSZbigniew Bodek 	struct mii_softc *miisc;
16102dd02006SWojciech Macek 	uint16_t portstatus;
1611f7c13d78SZbigniew Bodek 	int port;
16125420071dSZbigniew Bodek 
16135420071dSZbigniew Bodek 	sc = arg;
16145420071dSZbigniew Bodek 
16155420071dSZbigniew Bodek 	E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED);
16162dd02006SWojciech Macek 
16175420071dSZbigniew Bodek 	E6000SW_LOCK(sc);
1618ee7f62faSAdrian Chadd 
1619ee7f62faSAdrian Chadd 	if (sc->is_shutdown) {
1620ee7f62faSAdrian Chadd 		E6000SW_UNLOCK(sc);
1621ee7f62faSAdrian Chadd 		return;
1622ee7f62faSAdrian Chadd 	}
1623ee7f62faSAdrian Chadd 
1624f7c13d78SZbigniew Bodek 	for (port = 0; port < sc->num_ports; port++) {
1625f7c13d78SZbigniew Bodek 		/* Tick only on PHY ports */
1626ff2748ecSLuiz Otavio O Souza 		if (!e6000sw_is_portenabled(sc, port) ||
1627ff2748ecSLuiz Otavio O Souza 		    !e6000sw_is_phyport(sc, port))
1628f7c13d78SZbigniew Bodek 			continue;
16292dd02006SWojciech Macek 
1630595d629cSLuiz Otavio O Souza 		mii = e6000sw_miiforphy(sc, port);
1631595d629cSLuiz Otavio O Souza 		if (mii == NULL)
1632595d629cSLuiz Otavio O Souza 			continue;
1633595d629cSLuiz Otavio O Souza 
16349aba0637SLuiz Otavio O Souza 		portstatus = e6000sw_readreg(sc, REG_PORT(sc, port),
1635595d629cSLuiz Otavio O Souza 		    PORT_STATUS);
16362dd02006SWojciech Macek 
16372dd02006SWojciech Macek 		e6000sw_update_ifmedia(portstatus,
1638595d629cSLuiz Otavio O Souza 		    &mii->mii_media_status, &mii->mii_media_active);
16392dd02006SWojciech Macek 
1640595d629cSLuiz Otavio O Souza 		LIST_FOREACH(miisc, &mii->mii_phys, mii_list) {
1641c78f603aSAdrian Chadd 			/*
1642c78f603aSAdrian Chadd 			 * Note: this is sometimes NULL during PHY
1643c78f603aSAdrian Chadd 			 * enumeration, although that shouldn't be
1644c78f603aSAdrian Chadd 			 * happening /after/ tick runs. To work
1645c78f603aSAdrian Chadd 			 * around this whilst the problem is being
1646c78f603aSAdrian Chadd 			 * debugged, just do a NULL check here and
1647c78f603aSAdrian Chadd 			 * continue.
1648c78f603aSAdrian Chadd 			 */
1649c78f603aSAdrian Chadd 			if (mii->mii_media.ifm_cur == NULL)
1650c78f603aSAdrian Chadd 				continue;
1651c78f603aSAdrian Chadd 
1652595d629cSLuiz Otavio O Souza 			if (IFM_INST(mii->mii_media.ifm_cur->ifm_media)
16535420071dSZbigniew Bodek 			    != miisc->mii_inst)
16545420071dSZbigniew Bodek 				continue;
16555420071dSZbigniew Bodek 			mii_phy_update(miisc, MII_POLLSTAT);
16565420071dSZbigniew Bodek 		}
16575420071dSZbigniew Bodek 	}
16585420071dSZbigniew Bodek 	E6000SW_UNLOCK(sc);
1659c78f603aSAdrian Chadd 	taskqueue_enqueue_timeout(sc->sc_tq, &sc->sc_tt, hz);
16605420071dSZbigniew Bodek }
16615420071dSZbigniew Bodek 
16625420071dSZbigniew Bodek static void
16635420071dSZbigniew Bodek e6000sw_setup(device_t dev, e6000sw_softc_t *sc)
16645420071dSZbigniew Bodek {
1665d7cecbd1SLuiz Otavio O Souza 	uint32_t atu_ctrl;
16665420071dSZbigniew Bodek 
1667d7cecbd1SLuiz Otavio O Souza 	/* Set aging time. */
1668d7cecbd1SLuiz Otavio O Souza 	atu_ctrl = e6000sw_readreg(sc, REG_GLOBAL, ATU_CONTROL);
1669d7cecbd1SLuiz Otavio O Souza 	atu_ctrl &= ~ATU_CONTROL_AGETIME_MASK;
1670d7cecbd1SLuiz Otavio O Souza 	atu_ctrl |= E6000SW_DEFAULT_AGETIME << ATU_CONTROL_AGETIME;
1671d7cecbd1SLuiz Otavio O Souza 	e6000sw_writereg(sc, REG_GLOBAL, ATU_CONTROL, atu_ctrl);
16725420071dSZbigniew Bodek 
16735420071dSZbigniew Bodek 	/* Send all with specific mac address to cpu port */
16745420071dSZbigniew Bodek 	e6000sw_writereg(sc, REG_GLOBAL2, MGMT_EN_2x, MGMT_EN_ALL);
16755420071dSZbigniew Bodek 	e6000sw_writereg(sc, REG_GLOBAL2, MGMT_EN_0x, MGMT_EN_ALL);
16765420071dSZbigniew Bodek 
1677453130d9SPedro F. Giffuni 	/* Disable Remote Management */
16785420071dSZbigniew Bodek 	e6000sw_writereg(sc, REG_GLOBAL, SWITCH_GLOBAL_CONTROL2, 0);
16795420071dSZbigniew Bodek 
16805420071dSZbigniew Bodek 	/* Disable loopback filter and flow control messages */
16815420071dSZbigniew Bodek 	e6000sw_writereg(sc, REG_GLOBAL2, SWITCH_MGMT,
16825420071dSZbigniew Bodek 	    SWITCH_MGMT_PRI_MASK |
16835420071dSZbigniew Bodek 	    (1 << SWITCH_MGMT_RSVD2CPU) |
16845420071dSZbigniew Bodek 	    SWITCH_MGMT_FC_PRI_MASK |
16855420071dSZbigniew Bodek 	    (1 << SWITCH_MGMT_FORCEFLOW));
16865420071dSZbigniew Bodek 
16875420071dSZbigniew Bodek 	e6000sw_atu_flush(dev, sc, NO_OPERATION);
16885420071dSZbigniew Bodek 	e6000sw_atu_mac_table(dev, sc, NULL, NO_OPERATION);
16895420071dSZbigniew Bodek 	e6000sw_set_atustat(dev, sc, 0, COUNT_ALL);
16905420071dSZbigniew Bodek }
16915420071dSZbigniew Bodek 
16925420071dSZbigniew Bodek static void
16935420071dSZbigniew Bodek e6000sw_set_atustat(device_t dev, e6000sw_softc_t *sc, int bin, int flag)
16945420071dSZbigniew Bodek {
16955420071dSZbigniew Bodek 
16969a2e6ca6SWarner Losh 	e6000sw_readreg(sc, REG_GLOBAL2, ATU_STATS);
16975420071dSZbigniew Bodek 	e6000sw_writereg(sc, REG_GLOBAL2, ATU_STATS, (bin << ATU_STATS_BIN ) |
16985420071dSZbigniew Bodek 	    (flag << ATU_STATS_FLAG));
16995420071dSZbigniew Bodek }
17005420071dSZbigniew Bodek 
17015420071dSZbigniew Bodek static int
17025420071dSZbigniew Bodek e6000sw_atu_mac_table(device_t dev, e6000sw_softc_t *sc, struct atu_opt *atu,
17035420071dSZbigniew Bodek     int flag)
17045420071dSZbigniew Bodek {
17055420071dSZbigniew Bodek 	uint16_t ret_opt;
17065420071dSZbigniew Bodek 	uint16_t ret_data;
17075420071dSZbigniew Bodek 
17085420071dSZbigniew Bodek 	if (flag == NO_OPERATION)
17095420071dSZbigniew Bodek 		return (0);
17105420071dSZbigniew Bodek 	else if ((flag & (LOAD_FROM_FIB | PURGE_FROM_FIB | GET_NEXT_IN_FIB |
17115420071dSZbigniew Bodek 	    GET_VIOLATION_DATA | CLEAR_VIOLATION_DATA)) == 0) {
17125420071dSZbigniew Bodek 		device_printf(dev, "Wrong Opcode for ATU operation\n");
17135420071dSZbigniew Bodek 		return (EINVAL);
17145420071dSZbigniew Bodek 	}
17155420071dSZbigniew Bodek 
1716d7cecbd1SLuiz Otavio O Souza 	if (E6000SW_WAITREADY(sc, ATU_OPERATION, ATU_UNIT_BUSY)) {
1717d7cecbd1SLuiz Otavio O Souza 		device_printf(dev, "ATU unit is busy, cannot access\n");
17185420071dSZbigniew Bodek 		return (EBUSY);
1719d7cecbd1SLuiz Otavio O Souza 	}
1720d7cecbd1SLuiz Otavio O Souza 
1721d7cecbd1SLuiz Otavio O Souza 	ret_opt = e6000sw_readreg(sc, REG_GLOBAL, ATU_OPERATION);
17225420071dSZbigniew Bodek 	if (flag & LOAD_FROM_FIB) {
17235420071dSZbigniew Bodek 		ret_data = e6000sw_readreg(sc, REG_GLOBAL, ATU_DATA);
17245420071dSZbigniew Bodek 		e6000sw_writereg(sc, REG_GLOBAL2, ATU_DATA, (ret_data &
17255420071dSZbigniew Bodek 		    ~ENTRY_STATE));
17265420071dSZbigniew Bodek 	}
17275420071dSZbigniew Bodek 	e6000sw_writereg(sc, REG_GLOBAL, ATU_MAC_ADDR01, atu->mac_01);
17285420071dSZbigniew Bodek 	e6000sw_writereg(sc, REG_GLOBAL, ATU_MAC_ADDR23, atu->mac_23);
17295420071dSZbigniew Bodek 	e6000sw_writereg(sc, REG_GLOBAL, ATU_MAC_ADDR45, atu->mac_45);
17305420071dSZbigniew Bodek 	e6000sw_writereg(sc, REG_GLOBAL, ATU_FID, atu->fid);
17315420071dSZbigniew Bodek 
1732d7cecbd1SLuiz Otavio O Souza 	e6000sw_writereg(sc, REG_GLOBAL, ATU_OPERATION,
1733d7cecbd1SLuiz Otavio O Souza 	    (ret_opt | ATU_UNIT_BUSY | flag));
17345420071dSZbigniew Bodek 
1735d7cecbd1SLuiz Otavio O Souza 	if (E6000SW_WAITREADY(sc, ATU_OPERATION, ATU_UNIT_BUSY))
1736d7cecbd1SLuiz Otavio O Souza 		device_printf(dev, "Timeout while waiting ATU\n");
17375420071dSZbigniew Bodek 	else if (flag & GET_NEXT_IN_FIB) {
17385420071dSZbigniew Bodek 		atu->mac_01 = e6000sw_readreg(sc, REG_GLOBAL,
17395420071dSZbigniew Bodek 		    ATU_MAC_ADDR01);
17405420071dSZbigniew Bodek 		atu->mac_23 = e6000sw_readreg(sc, REG_GLOBAL,
17415420071dSZbigniew Bodek 		    ATU_MAC_ADDR23);
17425420071dSZbigniew Bodek 		atu->mac_45 = e6000sw_readreg(sc, REG_GLOBAL,
17435420071dSZbigniew Bodek 		    ATU_MAC_ADDR45);
17445420071dSZbigniew Bodek 	}
17455420071dSZbigniew Bodek 
17465420071dSZbigniew Bodek 	return (0);
17475420071dSZbigniew Bodek }
17485420071dSZbigniew Bodek 
17495420071dSZbigniew Bodek static int
17505420071dSZbigniew Bodek e6000sw_atu_flush(device_t dev, e6000sw_softc_t *sc, int flag)
17515420071dSZbigniew Bodek {
1752d7cecbd1SLuiz Otavio O Souza 	uint32_t reg;
17535420071dSZbigniew Bodek 
17545420071dSZbigniew Bodek 	if (flag == NO_OPERATION)
17555420071dSZbigniew Bodek 		return (0);
17565420071dSZbigniew Bodek 
1757d7cecbd1SLuiz Otavio O Souza 	if (E6000SW_WAITREADY(sc, ATU_OPERATION, ATU_UNIT_BUSY)) {
1758d7cecbd1SLuiz Otavio O Souza 		device_printf(dev, "ATU unit is busy, cannot access\n");
17595420071dSZbigniew Bodek 		return (EBUSY);
1760d7cecbd1SLuiz Otavio O Souza 	}
1761d7cecbd1SLuiz Otavio O Souza 	reg = e6000sw_readreg(sc, REG_GLOBAL, ATU_OPERATION);
1762d7cecbd1SLuiz Otavio O Souza 	e6000sw_writereg(sc, REG_GLOBAL, ATU_OPERATION,
1763d7cecbd1SLuiz Otavio O Souza 	    (reg | ATU_UNIT_BUSY | flag));
1764d7cecbd1SLuiz Otavio O Souza 	if (E6000SW_WAITREADY(sc, ATU_OPERATION, ATU_UNIT_BUSY))
1765d7cecbd1SLuiz Otavio O Souza 		device_printf(dev, "Timeout while flushing ATU\n");
17665420071dSZbigniew Bodek 
1767d7cecbd1SLuiz Otavio O Souza 	return (0);
1768d7cecbd1SLuiz Otavio O Souza }
1769d7cecbd1SLuiz Otavio O Souza 
1770d7cecbd1SLuiz Otavio O Souza static int
1771d7cecbd1SLuiz Otavio O Souza e6000sw_vtu_flush(e6000sw_softc_t *sc)
1772d7cecbd1SLuiz Otavio O Souza {
1773d7cecbd1SLuiz Otavio O Souza 
1774d7cecbd1SLuiz Otavio O Souza 	if (E6000SW_WAITREADY(sc, VTU_OPERATION, VTU_BUSY)) {
1775d7cecbd1SLuiz Otavio O Souza 		device_printf(sc->dev, "VTU unit is busy, cannot access\n");
1776d7cecbd1SLuiz Otavio O Souza 		return (EBUSY);
1777d7cecbd1SLuiz Otavio O Souza 	}
1778d7cecbd1SLuiz Otavio O Souza 
1779d7cecbd1SLuiz Otavio O Souza 	e6000sw_writereg(sc, REG_GLOBAL, VTU_OPERATION, VTU_FLUSH | VTU_BUSY);
1780d7cecbd1SLuiz Otavio O Souza 	if (E6000SW_WAITREADY(sc, VTU_OPERATION, VTU_BUSY)) {
1781d7cecbd1SLuiz Otavio O Souza 		device_printf(sc->dev, "Timeout while flushing VTU\n");
1782d7cecbd1SLuiz Otavio O Souza 		return (ETIMEDOUT);
1783d7cecbd1SLuiz Otavio O Souza 	}
1784d7cecbd1SLuiz Otavio O Souza 
1785d7cecbd1SLuiz Otavio O Souza 	return (0);
1786d7cecbd1SLuiz Otavio O Souza }
1787d7cecbd1SLuiz Otavio O Souza 
1788d7cecbd1SLuiz Otavio O Souza static int
1789d7cecbd1SLuiz Otavio O Souza e6000sw_vtu_update(e6000sw_softc_t *sc, int purge, int vid, int fid,
1790d7cecbd1SLuiz Otavio O Souza     int members, int untagged)
1791d7cecbd1SLuiz Otavio O Souza {
1792d7cecbd1SLuiz Otavio O Souza 	int i, op;
1793d7cecbd1SLuiz Otavio O Souza 	uint32_t data[2];
1794d7cecbd1SLuiz Otavio O Souza 
1795d7cecbd1SLuiz Otavio O Souza 	if (E6000SW_WAITREADY(sc, VTU_OPERATION, VTU_BUSY)) {
1796d7cecbd1SLuiz Otavio O Souza 		device_printf(sc->dev, "VTU unit is busy, cannot access\n");
1797d7cecbd1SLuiz Otavio O Souza 		return (EBUSY);
1798d7cecbd1SLuiz Otavio O Souza 	}
1799d7cecbd1SLuiz Otavio O Souza 
1800d7cecbd1SLuiz Otavio O Souza 	*data = (vid & VTU_VID_MASK);
1801d7cecbd1SLuiz Otavio O Souza 	if (purge == 0)
1802d7cecbd1SLuiz Otavio O Souza 		*data |= VTU_VID_VALID;
1803d7cecbd1SLuiz Otavio O Souza 	e6000sw_writereg(sc, REG_GLOBAL, VTU_VID, *data);
1804d7cecbd1SLuiz Otavio O Souza 
1805d7cecbd1SLuiz Otavio O Souza 	if (purge == 0) {
1806d7cecbd1SLuiz Otavio O Souza 		data[0] = 0;
1807d7cecbd1SLuiz Otavio O Souza 		data[1] = 0;
1808d7cecbd1SLuiz Otavio O Souza 		for (i = 0; i < sc->num_ports; i++) {
1809d7cecbd1SLuiz Otavio O Souza 			if ((untagged & (1 << i)) != 0)
1810d7cecbd1SLuiz Otavio O Souza 				data[i / VTU_PPREG(sc)] |=
1811d7cecbd1SLuiz Otavio O Souza 				    VTU_PORT_UNTAGGED << VTU_PORT(sc, i);
1812d7cecbd1SLuiz Otavio O Souza 			else if ((members & (1 << i)) != 0)
1813d7cecbd1SLuiz Otavio O Souza 				data[i / VTU_PPREG(sc)] |=
1814d7cecbd1SLuiz Otavio O Souza 				    VTU_PORT_TAGGED << VTU_PORT(sc, i);
1815d7cecbd1SLuiz Otavio O Souza 			else
1816d7cecbd1SLuiz Otavio O Souza 				data[i / VTU_PPREG(sc)] |=
1817d7cecbd1SLuiz Otavio O Souza 				    VTU_PORT_DISCARD << VTU_PORT(sc, i);
1818d7cecbd1SLuiz Otavio O Souza 		}
1819d7cecbd1SLuiz Otavio O Souza 		e6000sw_writereg(sc, REG_GLOBAL, VTU_DATA, data[0]);
1820d7cecbd1SLuiz Otavio O Souza 		e6000sw_writereg(sc, REG_GLOBAL, VTU_DATA2, data[1]);
1821d7cecbd1SLuiz Otavio O Souza 		e6000sw_writereg(sc, REG_GLOBAL, VTU_FID,
1822d7cecbd1SLuiz Otavio O Souza 		    fid & VTU_FID_MASK(sc));
1823d7cecbd1SLuiz Otavio O Souza 		op = VTU_LOAD;
1824d7cecbd1SLuiz Otavio O Souza 	} else
1825d7cecbd1SLuiz Otavio O Souza 		op = VTU_PURGE;
1826d7cecbd1SLuiz Otavio O Souza 
1827d7cecbd1SLuiz Otavio O Souza 	e6000sw_writereg(sc, REG_GLOBAL, VTU_OPERATION, op | VTU_BUSY);
1828d7cecbd1SLuiz Otavio O Souza 	if (E6000SW_WAITREADY(sc, VTU_OPERATION, VTU_BUSY)) {
1829d7cecbd1SLuiz Otavio O Souza 		device_printf(sc->dev, "Timeout while flushing VTU\n");
1830d7cecbd1SLuiz Otavio O Souza 		return (ETIMEDOUT);
18315420071dSZbigniew Bodek 	}
18325420071dSZbigniew Bodek 
18335420071dSZbigniew Bodek 	return (0);
18345420071dSZbigniew Bodek }
1835