1299910Ssgalabov/*-
2299910Ssgalabov * Copyright (c) 2016 Stanislav Galabov.
3299910Ssgalabov * All rights reserved.
4299910Ssgalabov *
5299910Ssgalabov * Redistribution and use in source and binary forms, with or without
6299910Ssgalabov * modification, are permitted provided that the following conditions
7299910Ssgalabov * are met:
8299910Ssgalabov * 1. Redistributions of source code must retain the above copyright
9299910Ssgalabov *    notice, this list of conditions and the following disclaimer.
10299910Ssgalabov * 2. Redistributions in binary form must reproduce the above copyright
11299910Ssgalabov *    notice, this list of conditions and the following disclaimer in the
12299910Ssgalabov *    documentation and/or other materials provided with the distribution.
13299910Ssgalabov *
14299910Ssgalabov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15299910Ssgalabov * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16299910Ssgalabov * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17299910Ssgalabov * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18299910Ssgalabov * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19299910Ssgalabov * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20299910Ssgalabov * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21299910Ssgalabov * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22299910Ssgalabov * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23299910Ssgalabov * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24299910Ssgalabov * SUCH DAMAGE.
25299910Ssgalabov *
26299910Ssgalabov * $FreeBSD: releng/11.0/sys/dev/etherswitch/mtkswitch/mtkswitch_rt3050.c 299910 2016-05-16 07:00:49Z sgalabov $
27299910Ssgalabov */
28299910Ssgalabov
29299910Ssgalabov#include <sys/param.h>
30299910Ssgalabov#include <sys/bus.h>
31299910Ssgalabov#include <sys/errno.h>
32299910Ssgalabov#include <sys/kernel.h>
33299910Ssgalabov#include <sys/lock.h>
34299910Ssgalabov#include <sys/malloc.h>
35299910Ssgalabov#include <sys/module.h>
36299910Ssgalabov#include <sys/mutex.h>
37299910Ssgalabov#include <sys/rman.h>
38299910Ssgalabov#include <sys/socket.h>
39299910Ssgalabov#include <sys/sockio.h>
40299910Ssgalabov#include <sys/sysctl.h>
41299910Ssgalabov#include <sys/systm.h>
42299910Ssgalabov
43299910Ssgalabov#include <net/if.h>
44299910Ssgalabov#include <net/if_var.h>
45299910Ssgalabov#include <net/ethernet.h>
46299910Ssgalabov#include <net/if_media.h>
47299910Ssgalabov#include <net/if_types.h>
48299910Ssgalabov
49299910Ssgalabov#include <machine/bus.h>
50299910Ssgalabov#include <dev/mii/mii.h>
51299910Ssgalabov#include <dev/mii/miivar.h>
52299910Ssgalabov#include <dev/mdio/mdio.h>
53299910Ssgalabov
54299910Ssgalabov#include <dev/etherswitch/etherswitch.h>
55299910Ssgalabov#include <dev/etherswitch/mtkswitch/mtkswitchvar.h>
56299910Ssgalabov#include <dev/etherswitch/mtkswitch/mtkswitch_rt3050.h>
57299910Ssgalabov
58299910Ssgalabovstatic int
59299910Ssgalabovmtkswitch_reg_read(device_t dev, int reg)
60299910Ssgalabov{
61299910Ssgalabov	struct mtkswitch_softc *sc = device_get_softc(dev);
62299910Ssgalabov	uint32_t val;
63299910Ssgalabov
64299910Ssgalabov	MTKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
65299910Ssgalabov	val = MTKSWITCH_READ(sc, MTKSWITCH_REG32(reg));
66299910Ssgalabov	if (MTKSWITCH_IS_HI16(reg))
67299910Ssgalabov		return (MTKSWITCH_HI16(val));
68299910Ssgalabov	return (MTKSWITCH_LO16(val));
69299910Ssgalabov}
70299910Ssgalabov
71299910Ssgalabovstatic int
72299910Ssgalabovmtkswitch_reg_write(device_t dev, int reg, int val)
73299910Ssgalabov{
74299910Ssgalabov	struct mtkswitch_softc *sc = device_get_softc(dev);
75299910Ssgalabov	uint32_t tmp;
76299910Ssgalabov
77299910Ssgalabov	MTKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
78299910Ssgalabov	tmp = MTKSWITCH_READ(sc, MTKSWITCH_REG32(reg));
79299910Ssgalabov	if (MTKSWITCH_IS_HI16(reg)) {
80299910Ssgalabov		tmp &= MTKSWITCH_LO16_MSK;
81299910Ssgalabov		tmp |= MTKSWITCH_TO_HI16(val);
82299910Ssgalabov	} else {
83299910Ssgalabov		tmp &= MTKSWITCH_HI16_MSK;
84299910Ssgalabov		tmp |= MTKSWITCH_TO_LO16(val);
85299910Ssgalabov	}
86299910Ssgalabov	MTKSWITCH_WRITE(sc, MTKSWITCH_REG32(reg), tmp);
87299910Ssgalabov
88299910Ssgalabov	return (0);
89299910Ssgalabov}
90299910Ssgalabov
91299910Ssgalabovstatic int
92299910Ssgalabovmtkswitch_phy_read(device_t dev, int phy, int reg)
93299910Ssgalabov{
94299910Ssgalabov	struct mtkswitch_softc *sc = device_get_softc(dev);
95299910Ssgalabov	int val;
96299910Ssgalabov
97299910Ssgalabov	MTKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
98299910Ssgalabov	MTKSWITCH_LOCK(sc);
99299910Ssgalabov	while (MTKSWITCH_READ(sc, MTKSWITCH_PCR0) & PCR0_ACTIVE);
100299910Ssgalabov	MTKSWITCH_WRITE(sc, MTKSWITCH_PCR0, PCR0_READ | PCR0_REG(reg) |
101299910Ssgalabov	    PCR0_PHY(phy));
102299910Ssgalabov	while (MTKSWITCH_READ(sc, MTKSWITCH_PCR0) & PCR0_ACTIVE);
103299910Ssgalabov	val = (MTKSWITCH_READ(sc, MTKSWITCH_PCR1) >> PCR1_DATA_OFF) &
104299910Ssgalabov	    PCR1_DATA_MASK;
105299910Ssgalabov	MTKSWITCH_UNLOCK(sc);
106299910Ssgalabov	return (val);
107299910Ssgalabov}
108299910Ssgalabov
109299910Ssgalabovstatic int
110299910Ssgalabovmtkswitch_phy_write(device_t dev, int phy, int reg, int val)
111299910Ssgalabov{
112299910Ssgalabov	struct mtkswitch_softc *sc = device_get_softc(dev);
113299910Ssgalabov
114299910Ssgalabov	MTKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
115299910Ssgalabov	MTKSWITCH_LOCK(sc);
116299910Ssgalabov	while (MTKSWITCH_READ(sc, MTKSWITCH_PCR0) & PCR0_ACTIVE);
117299910Ssgalabov	MTKSWITCH_WRITE(sc, MTKSWITCH_PCR0, PCR0_WRITE | PCR0_REG(reg) |
118299910Ssgalabov	    PCR0_PHY(phy) | PCR0_DATA(val));
119299910Ssgalabov	while (MTKSWITCH_READ(sc, MTKSWITCH_PCR0) & PCR0_ACTIVE);
120299910Ssgalabov	MTKSWITCH_UNLOCK(sc);
121299910Ssgalabov	return (0);
122299910Ssgalabov}
123299910Ssgalabov
124299910Ssgalabovstatic int
125299910Ssgalabovmtkswitch_reset(struct mtkswitch_softc *sc)
126299910Ssgalabov{
127299910Ssgalabov
128299910Ssgalabov	MTKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
129299910Ssgalabov	MTKSWITCH_LOCK(sc);
130299910Ssgalabov	MTKSWITCH_WRITE(sc, MTKSWITCH_STRT, STRT_RESET);
131299910Ssgalabov	while (MTKSWITCH_READ(sc, MTKSWITCH_STRT) != 0);
132299910Ssgalabov	MTKSWITCH_UNLOCK(sc);
133299910Ssgalabov
134299910Ssgalabov	return (0);
135299910Ssgalabov}
136299910Ssgalabov
137299910Ssgalabovstatic int
138299910Ssgalabovmtkswitch_hw_setup(struct mtkswitch_softc *sc)
139299910Ssgalabov{
140299910Ssgalabov
141299910Ssgalabov	/*
142299910Ssgalabov	 * TODO: parse the device tree and see if we need to configure
143299910Ssgalabov	 *       ports, etc. differently. For now we fallback to defaults.
144299910Ssgalabov	 */
145299910Ssgalabov
146299910Ssgalabov	/* Called early and hence unlocked */
147299910Ssgalabov	/* Set ports 0-4 to auto negotiation */
148299910Ssgalabov	MTKSWITCH_WRITE(sc, MTKSWITCH_FPA, FPA_ALL_AUTO);
149299910Ssgalabov
150299910Ssgalabov	return (0);
151299910Ssgalabov}
152299910Ssgalabov
153299910Ssgalabovstatic int
154299910Ssgalabovmtkswitch_hw_global_setup(struct mtkswitch_softc *sc)
155299910Ssgalabov{
156299910Ssgalabov
157299910Ssgalabov	/* Called early and hence unlocked */
158299910Ssgalabov	return (0);
159299910Ssgalabov}
160299910Ssgalabov
161299910Ssgalabovstatic void
162299910Ssgalabovmtkswitch_port_init(struct mtkswitch_softc *sc, int port)
163299910Ssgalabov{
164299910Ssgalabov	/* Called early and hence unlocked */
165299910Ssgalabov	/* Do nothing - ports are set to auto negotiation in hw_setup */
166299910Ssgalabov}
167299910Ssgalabov
168299910Ssgalabovstatic uint32_t
169299910Ssgalabovmtkswitch_get_port_status(struct mtkswitch_softc *sc, int port)
170299910Ssgalabov{
171299910Ssgalabov	uint32_t val, res;
172299910Ssgalabov
173299910Ssgalabov	MTKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
174299910Ssgalabov	res = 0;
175299910Ssgalabov	val = MTKSWITCH_READ(sc, MTKSWITCH_POA);
176299910Ssgalabov
177299910Ssgalabov	if (val & POA_PRT_LINK(port))
178299910Ssgalabov		res |= MTKSWITCH_LINK_UP;
179299910Ssgalabov	if (val & POA_PRT_DPX(port))
180299910Ssgalabov		res |= MTKSWITCH_DUPLEX;
181299910Ssgalabov
182299910Ssgalabov	if (MTKSWITCH_PORT_IS_100M(port)) {
183299910Ssgalabov		if (val & POA_FE_SPEED(port))
184299910Ssgalabov			res |= MTKSWITCH_SPEED_100;
185299910Ssgalabov		if (val & POA_FE_XFC(port))
186299910Ssgalabov			res |= (MTKSWITCH_TXFLOW | MTKSWITCH_RXFLOW);
187299910Ssgalabov	} else {
188299910Ssgalabov		switch (POA_GE_SPEED(val, port)) {
189299910Ssgalabov		case POA_GE_SPEED_10:
190299910Ssgalabov			res |= MTKSWITCH_SPEED_10;
191299910Ssgalabov			break;
192299910Ssgalabov		case POA_GE_SPEED_100:
193299910Ssgalabov			res |= MTKSWITCH_SPEED_100;
194299910Ssgalabov			break;
195299910Ssgalabov		case POA_GE_SPEED_1000:
196299910Ssgalabov			res |= MTKSWITCH_SPEED_1000;
197299910Ssgalabov			break;
198299910Ssgalabov		}
199299910Ssgalabov
200299910Ssgalabov		val = POA_GE_XFC(val, port);
201299910Ssgalabov		if (val & POA_GE_XFC_TX_MSK)
202299910Ssgalabov			res |= MTKSWITCH_TXFLOW;
203299910Ssgalabov		if (val & POA_GE_XFC_RX_MSK)
204299910Ssgalabov			res |= MTKSWITCH_RXFLOW;
205299910Ssgalabov	}
206299910Ssgalabov
207299910Ssgalabov	return (res);
208299910Ssgalabov}
209299910Ssgalabov
210299910Ssgalabovstatic int
211299910Ssgalabovmtkswitch_atu_flush(struct mtkswitch_softc *sc)
212299910Ssgalabov{
213299910Ssgalabov	return (0);
214299910Ssgalabov}
215299910Ssgalabov
216299910Ssgalabovstatic int
217299910Ssgalabovmtkswitch_port_vlan_setup(struct mtkswitch_softc *sc, etherswitch_port_t *p)
218299910Ssgalabov{
219299910Ssgalabov	uint32_t val;
220299910Ssgalabov	int err, invert = 0;
221299910Ssgalabov
222299910Ssgalabov	MTKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
223299910Ssgalabov	MTKSWITCH_LOCK(sc);
224299910Ssgalabov	/* Set the PVID. */
225299910Ssgalabov	if (p->es_pvid != 0) {
226299910Ssgalabov		err = sc->hal.mtkswitch_vlan_set_pvid(sc, p->es_port,
227299910Ssgalabov		    p->es_pvid);
228299910Ssgalabov		if (err != 0) {
229299910Ssgalabov			MTKSWITCH_UNLOCK(sc);
230299910Ssgalabov			return (err);
231299910Ssgalabov		}
232299910Ssgalabov	}
233299910Ssgalabov
234299910Ssgalabov	/* Mutually exclusive */
235299910Ssgalabov	if (p->es_flags & ETHERSWITCH_PORT_ADDTAG &&
236299910Ssgalabov	    p->es_flags & ETHERSWITCH_PORT_STRIPTAG) {
237299910Ssgalabov		invert = 1;
238299910Ssgalabov	}
239299910Ssgalabov
240299910Ssgalabov	val = MTKSWITCH_READ(sc, MTKSWITCH_SGC2);
241299910Ssgalabov	if (p->es_flags & ETHERSWITCH_PORT_DOUBLE_TAG)
242299910Ssgalabov		val |= SGC2_DOUBLE_TAG_PORT(p->es_port);
243299910Ssgalabov	else
244299910Ssgalabov		val &= ~SGC2_DOUBLE_TAG_PORT(p->es_port);
245299910Ssgalabov	MTKSWITCH_WRITE(sc, MTKSWITCH_SGC2, val);
246299910Ssgalabov
247299910Ssgalabov	val = MTKSWITCH_READ(sc, MTKSWITCH_POC2);
248299910Ssgalabov	if (invert) {
249299910Ssgalabov		if (val & POC2_UNTAG_PORT(p->es_port))
250299910Ssgalabov			val &= ~POC2_UNTAG_PORT(p->es_port);
251299910Ssgalabov		else
252299910Ssgalabov			val |= POC2_UNTAG_PORT(p->es_port);
253299910Ssgalabov	} else if (p->es_flags & ETHERSWITCH_PORT_STRIPTAG)
254299910Ssgalabov		val |= POC2_UNTAG_PORT(p->es_port);
255299910Ssgalabov	else
256299910Ssgalabov		val &= ~POC2_UNTAG_PORT(p->es_port);
257299910Ssgalabov	MTKSWITCH_WRITE(sc, MTKSWITCH_POC2, val);
258299910Ssgalabov	MTKSWITCH_UNLOCK(sc);
259299910Ssgalabov
260299910Ssgalabov	return (0);
261299910Ssgalabov}
262299910Ssgalabov
263299910Ssgalabovstatic int
264299910Ssgalabovmtkswitch_port_vlan_get(struct mtkswitch_softc *sc, etherswitch_port_t *p)
265299910Ssgalabov{
266299910Ssgalabov	uint32_t val;
267299910Ssgalabov
268299910Ssgalabov	MTKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
269299910Ssgalabov	MTKSWITCH_LOCK(sc);
270299910Ssgalabov
271299910Ssgalabov	/* Retrieve the PVID */
272299910Ssgalabov	sc->hal.mtkswitch_vlan_get_pvid(sc, p->es_port, &p->es_pvid);
273299910Ssgalabov
274299910Ssgalabov	/* Port flags */
275299910Ssgalabov	p->es_flags = 0;
276299910Ssgalabov	val = MTKSWITCH_READ(sc, MTKSWITCH_SGC2);
277299910Ssgalabov	if (val & SGC2_DOUBLE_TAG_PORT(p->es_port))
278299910Ssgalabov		p->es_flags |= ETHERSWITCH_PORT_DOUBLE_TAG;
279299910Ssgalabov
280299910Ssgalabov	val = MTKSWITCH_READ(sc, MTKSWITCH_POC2);
281299910Ssgalabov	if (val & POC2_UNTAG_PORT(p->es_port))
282299910Ssgalabov		p->es_flags |= ETHERSWITCH_PORT_STRIPTAG;
283299910Ssgalabov	else
284299910Ssgalabov		p->es_flags |= ETHERSWITCH_PORT_ADDTAG;
285299910Ssgalabov
286299910Ssgalabov	MTKSWITCH_UNLOCK(sc);
287299910Ssgalabov
288299910Ssgalabov	return (0);
289299910Ssgalabov}
290299910Ssgalabov
291299910Ssgalabovstatic void
292299910Ssgalabovmtkswitch_vlan_init_hw(struct mtkswitch_softc *sc)
293299910Ssgalabov{
294299910Ssgalabov	uint32_t val, vid;
295299910Ssgalabov	int i;
296299910Ssgalabov
297299910Ssgalabov	MTKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
298299910Ssgalabov	MTKSWITCH_LOCK(sc);
299299910Ssgalabov
300299910Ssgalabov	/* Reset everything to defaults first */
301299910Ssgalabov	for (i = 0; i < sc->info.es_nvlangroups; i++) {
302299910Ssgalabov		/* Remove all VLAN members and untag info, if any */
303299910Ssgalabov		if (i % 4 == 0) {
304299910Ssgalabov			MTKSWITCH_WRITE(sc, MTKSWITCH_VMSC(i), 0);
305299910Ssgalabov			if (sc->sc_switchtype != MTK_SWITCH_RT3050)
306299910Ssgalabov				MTKSWITCH_WRITE(sc, MTKSWITCH_VUB(i), 0);
307299910Ssgalabov		}
308299910Ssgalabov		/* Reset to default VIDs */
309299910Ssgalabov		val = MTKSWITCH_READ(sc, MTKSWITCH_VLANI(i));
310299910Ssgalabov		val &= ~(VLANI_MASK << VLANI_OFF(i));
311299910Ssgalabov		val |= ((i + 1) << VLANI_OFF(i));
312299910Ssgalabov		MTKSWITCH_WRITE(sc, MTKSWITCH_VLANI(i), val);
313299910Ssgalabov	}
314299910Ssgalabov
315299910Ssgalabov	/* Now, add all ports as untagged members to VLAN1 */
316299910Ssgalabov	vid = 0;
317299910Ssgalabov	val = MTKSWITCH_READ(sc, MTKSWITCH_VMSC(vid));
318299910Ssgalabov	val &= ~(VMSC_MASK << VMSC_OFF(vid));
319299910Ssgalabov	val |= (((1<<sc->numports)-1) << VMSC_OFF(vid));
320299910Ssgalabov	MTKSWITCH_WRITE(sc, MTKSWITCH_VMSC(vid), val);
321299910Ssgalabov	if (sc->sc_switchtype != MTK_SWITCH_RT3050) {
322299910Ssgalabov		val = MTKSWITCH_READ(sc, MTKSWITCH_VUB(vid));
323299910Ssgalabov		val &= ~(VUB_MASK << VUB_OFF(vid));
324299910Ssgalabov		val |= (((1<<sc->numports)-1) << VUB_OFF(vid));
325299910Ssgalabov		MTKSWITCH_WRITE(sc, MTKSWITCH_VUB(vid), val);
326299910Ssgalabov	}
327299910Ssgalabov	val = MTKSWITCH_READ(sc, MTKSWITCH_POC2);
328299910Ssgalabov	if (sc->sc_switchtype != MTK_SWITCH_RT3050)
329299910Ssgalabov		val |= POC2_UNTAG_VLAN;
330299910Ssgalabov	val |= ((1<<sc->numports)-1);
331299910Ssgalabov	MTKSWITCH_WRITE(sc, MTKSWITCH_POC2, val);
332299910Ssgalabov
333299910Ssgalabov	/* only the first vlangroup is valid */
334299910Ssgalabov	sc->valid_vlans = (1<<0);
335299910Ssgalabov
336299910Ssgalabov	/* Set all port PVIDs to 1 */
337299910Ssgalabov	vid = 1;
338299910Ssgalabov	for (i = 0; i < sc->info.es_nports; i++) {
339299910Ssgalabov		val = MTKSWITCH_READ(sc, MTKSWITCH_PVID(i));
340299910Ssgalabov		val &= ~(PVID_MASK << PVID_OFF(i));
341299910Ssgalabov		val |= (vid << PVID_OFF(i));
342299910Ssgalabov		MTKSWITCH_WRITE(sc, MTKSWITCH_PVID(i), val);
343299910Ssgalabov	}
344299910Ssgalabov
345299910Ssgalabov	MTKSWITCH_UNLOCK(sc);
346299910Ssgalabov}
347299910Ssgalabov
348299910Ssgalabovstatic int
349299910Ssgalabovmtkswitch_vlan_getvgroup(struct mtkswitch_softc *sc, etherswitch_vlangroup_t *v)
350299910Ssgalabov{
351299910Ssgalabov	uint32_t val;
352299910Ssgalabov
353299910Ssgalabov	MTKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
354299910Ssgalabov
355299910Ssgalabov	if ((sc->vlan_mode != ETHERSWITCH_VLAN_DOT1Q) ||
356299910Ssgalabov	    (v->es_vlangroup > sc->info.es_nvlangroups))
357299910Ssgalabov		return (EINVAL);
358299910Ssgalabov
359299910Ssgalabov	/* Reset the member ports. */
360299910Ssgalabov	v->es_untagged_ports = 0;
361299910Ssgalabov	v->es_member_ports = 0;
362299910Ssgalabov
363299910Ssgalabov	/* Not supported */
364299910Ssgalabov	v->es_fid = 0;
365299910Ssgalabov
366299910Ssgalabov	/* Vlan ID */
367299910Ssgalabov	v->es_vid = 0;
368299910Ssgalabov	if ((sc->valid_vlans & (1<<v->es_vlangroup)) == 0)
369299910Ssgalabov		return (0);
370299910Ssgalabov
371299910Ssgalabov	MTKSWITCH_LOCK(sc);
372299910Ssgalabov	v->es_vid = (MTKSWITCH_READ(sc, MTKSWITCH_VLANI(v->es_vlangroup)) >>
373299910Ssgalabov	    VLANI_OFF(v->es_vlangroup)) & VLANI_MASK;
374299910Ssgalabov	v->es_vid |= ETHERSWITCH_VID_VALID;
375299910Ssgalabov
376299910Ssgalabov	/* Member ports */
377299910Ssgalabov	v->es_member_ports = v->es_untagged_ports =
378299910Ssgalabov	    (MTKSWITCH_READ(sc, MTKSWITCH_VMSC(v->es_vlangroup)) >>
379299910Ssgalabov	    VMSC_OFF(v->es_vlangroup)) & VMSC_MASK;
380299910Ssgalabov
381299910Ssgalabov	val = MTKSWITCH_READ(sc, MTKSWITCH_POC2);
382299910Ssgalabov
383299910Ssgalabov	if ((val & POC2_UNTAG_VLAN) && sc->sc_switchtype != MTK_SWITCH_RT3050) {
384299910Ssgalabov		val = (MTKSWITCH_READ(sc, MTKSWITCH_VUB(v->es_vlangroup)) >>
385299910Ssgalabov		    VUB_OFF(v->es_vlangroup)) & VUB_MASK;
386299910Ssgalabov	} else {
387299910Ssgalabov		val &= VUB_MASK;
388299910Ssgalabov	}
389299910Ssgalabov	v->es_untagged_ports &= val;
390299910Ssgalabov
391299910Ssgalabov	MTKSWITCH_UNLOCK(sc);
392299910Ssgalabov	return (0);
393299910Ssgalabov}
394299910Ssgalabov
395299910Ssgalabovstatic int
396299910Ssgalabovmtkswitch_vlan_setvgroup(struct mtkswitch_softc *sc, etherswitch_vlangroup_t *v)
397299910Ssgalabov{
398299910Ssgalabov	uint32_t val, tmp;
399299910Ssgalabov
400299910Ssgalabov	if ((sc->vlan_mode != ETHERSWITCH_VLAN_DOT1Q) ||
401299910Ssgalabov	    (v->es_vlangroup > sc->info.es_nvlangroups))
402299910Ssgalabov		return (EINVAL);
403299910Ssgalabov
404299910Ssgalabov	MTKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
405299910Ssgalabov	MTKSWITCH_LOCK(sc);
406299910Ssgalabov	/* First, see if we can accomodate the request at all */
407299910Ssgalabov	val = MTKSWITCH_READ(sc, MTKSWITCH_POC2);
408299910Ssgalabov	if ((val & POC2_UNTAG_VLAN) == 0 ||
409299910Ssgalabov	    sc->sc_switchtype == MTK_SWITCH_RT3050) {
410299910Ssgalabov		val &= VUB_MASK;
411299910Ssgalabov		tmp = v->es_untagged_ports & v->es_member_ports;
412299910Ssgalabov		if (val != tmp) {
413299910Ssgalabov			/* Cannot accomodate request */
414299910Ssgalabov			MTKSWITCH_UNLOCK(sc);
415299910Ssgalabov			return (ENOTSUP);
416299910Ssgalabov		}
417299910Ssgalabov	} else {
418299910Ssgalabov		/* Prefer per-Vlan untag and set its members */
419299910Ssgalabov		val = MTKSWITCH_READ(sc, MTKSWITCH_VUB(v->es_vlangroup));
420299910Ssgalabov		val &= ~(VUB_MASK << VUB_OFF(v->es_vlangroup));
421299910Ssgalabov		val |= (((v->es_untagged_ports) & VUB_MASK) <<
422299910Ssgalabov		    VUB_OFF(v->es_vlangroup));
423299910Ssgalabov		MTKSWITCH_WRITE(sc, MTKSWITCH_VUB(v->es_vlangroup), val);
424299910Ssgalabov	}
425299910Ssgalabov
426299910Ssgalabov	/* Set VID */
427299910Ssgalabov	val = MTKSWITCH_READ(sc, MTKSWITCH_VLANI(v->es_vlangroup));
428299910Ssgalabov	val &= ~(VLANI_MASK << VLANI_OFF(v->es_vlangroup));
429299910Ssgalabov	val |= (v->es_vid & VLANI_MASK) << VLANI_OFF(v->es_vlangroup);
430299910Ssgalabov	MTKSWITCH_WRITE(sc, MTKSWITCH_VLANI(v->es_vlangroup), val);
431299910Ssgalabov
432299910Ssgalabov	/* Set members */
433299910Ssgalabov	val = MTKSWITCH_READ(sc, MTKSWITCH_VMSC(v->es_vlangroup));
434299910Ssgalabov	val &= ~(VMSC_MASK << VMSC_OFF(v->es_vlangroup));
435299910Ssgalabov	val |= (v->es_member_ports << VMSC_OFF(v->es_vlangroup));
436299910Ssgalabov	MTKSWITCH_WRITE(sc, MTKSWITCH_VMSC(v->es_vlangroup), val);
437299910Ssgalabov
438299910Ssgalabov	sc->valid_vlans |= (1<<v->es_vlangroup);
439299910Ssgalabov
440299910Ssgalabov	MTKSWITCH_UNLOCK(sc);
441299910Ssgalabov	return (0);
442299910Ssgalabov}
443299910Ssgalabov
444299910Ssgalabovstatic int
445299910Ssgalabovmtkswitch_vlan_get_pvid(struct mtkswitch_softc *sc, int port, int *pvid)
446299910Ssgalabov{
447299910Ssgalabov
448299910Ssgalabov	MTKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
449299910Ssgalabov	*pvid = (MTKSWITCH_READ(sc, MTKSWITCH_PVID(port)) >> PVID_OFF(port)) &
450299910Ssgalabov	    PVID_MASK;
451299910Ssgalabov
452299910Ssgalabov	return (0);
453299910Ssgalabov}
454299910Ssgalabov
455299910Ssgalabovstatic int
456299910Ssgalabovmtkswitch_vlan_set_pvid(struct mtkswitch_softc *sc, int port, int pvid)
457299910Ssgalabov{
458299910Ssgalabov	uint32_t val;
459299910Ssgalabov
460299910Ssgalabov	MTKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
461299910Ssgalabov	val = MTKSWITCH_READ(sc, MTKSWITCH_PVID(port));
462299910Ssgalabov	val &= ~(PVID_MASK << PVID_OFF(port));
463299910Ssgalabov	val |= (pvid & PVID_MASK) << PVID_OFF(port);
464299910Ssgalabov	MTKSWITCH_WRITE(sc, MTKSWITCH_PVID(port), val);
465299910Ssgalabov
466299910Ssgalabov	return (0);
467299910Ssgalabov}
468299910Ssgalabov
469299910Ssgalabovextern void
470299910Ssgalabovmtk_attach_switch_rt3050(struct mtkswitch_softc *sc)
471299910Ssgalabov{
472299910Ssgalabov
473299910Ssgalabov	sc->portmap = 0x7f;
474299910Ssgalabov	sc->phymap = 0x1f;
475299910Ssgalabov
476299910Ssgalabov	sc->info.es_nports = 7;
477299910Ssgalabov	sc->info.es_vlan_caps = ETHERSWITCH_VLAN_DOT1Q;
478299910Ssgalabov	sc->info.es_nvlangroups = 16;
479299910Ssgalabov	sprintf(sc->info.es_name, "Ralink ESW");
480299910Ssgalabov
481299910Ssgalabov	sc->hal.mtkswitch_reset = mtkswitch_reset;
482299910Ssgalabov	sc->hal.mtkswitch_hw_setup = mtkswitch_hw_setup;
483299910Ssgalabov	sc->hal.mtkswitch_hw_global_setup = mtkswitch_hw_global_setup;
484299910Ssgalabov	sc->hal.mtkswitch_port_init = mtkswitch_port_init;
485299910Ssgalabov	sc->hal.mtkswitch_get_port_status = mtkswitch_get_port_status;
486299910Ssgalabov	sc->hal.mtkswitch_atu_flush = mtkswitch_atu_flush;
487299910Ssgalabov	sc->hal.mtkswitch_port_vlan_setup = mtkswitch_port_vlan_setup;
488299910Ssgalabov	sc->hal.mtkswitch_port_vlan_get = mtkswitch_port_vlan_get;
489299910Ssgalabov	sc->hal.mtkswitch_vlan_init_hw = mtkswitch_vlan_init_hw;
490299910Ssgalabov	sc->hal.mtkswitch_vlan_getvgroup = mtkswitch_vlan_getvgroup;
491299910Ssgalabov	sc->hal.mtkswitch_vlan_setvgroup = mtkswitch_vlan_setvgroup;
492299910Ssgalabov	sc->hal.mtkswitch_vlan_get_pvid = mtkswitch_vlan_get_pvid;
493299910Ssgalabov	sc->hal.mtkswitch_vlan_set_pvid = mtkswitch_vlan_set_pvid;
494299910Ssgalabov	sc->hal.mtkswitch_phy_read = mtkswitch_phy_read;
495299910Ssgalabov	sc->hal.mtkswitch_phy_write = mtkswitch_phy_write;
496299910Ssgalabov	sc->hal.mtkswitch_reg_read = mtkswitch_reg_read;
497299910Ssgalabov	sc->hal.mtkswitch_reg_write = mtkswitch_reg_write;
498299910Ssgalabov}
499