1dfb5178fSStanislav Galabov /*-
2dfb5178fSStanislav Galabov  * Copyright (c) 2016 Stanislav Galabov.
3dfb5178fSStanislav Galabov  * All rights reserved.
4dfb5178fSStanislav Galabov  *
5dfb5178fSStanislav Galabov  * Redistribution and use in source and binary forms, with or without
6dfb5178fSStanislav Galabov  * modification, are permitted provided that the following conditions
7dfb5178fSStanislav Galabov  * are met:
8dfb5178fSStanislav Galabov  * 1. Redistributions of source code must retain the above copyright
9dfb5178fSStanislav Galabov  *    notice, this list of conditions and the following disclaimer.
10dfb5178fSStanislav Galabov  * 2. Redistributions in binary form must reproduce the above copyright
11dfb5178fSStanislav Galabov  *    notice, this list of conditions and the following disclaimer in the
12dfb5178fSStanislav Galabov  *    documentation and/or other materials provided with the distribution.
13dfb5178fSStanislav Galabov  *
14dfb5178fSStanislav Galabov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15dfb5178fSStanislav Galabov  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16dfb5178fSStanislav Galabov  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17dfb5178fSStanislav Galabov  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18dfb5178fSStanislav Galabov  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19dfb5178fSStanislav Galabov  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20dfb5178fSStanislav Galabov  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21dfb5178fSStanislav Galabov  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22dfb5178fSStanislav Galabov  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23dfb5178fSStanislav Galabov  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24dfb5178fSStanislav Galabov  * SUCH DAMAGE.
25dfb5178fSStanislav Galabov  */
26dfb5178fSStanislav Galabov 
27dfb5178fSStanislav Galabov #include <sys/param.h>
28dfb5178fSStanislav Galabov #include <sys/bus.h>
29dfb5178fSStanislav Galabov #include <sys/errno.h>
30dfb5178fSStanislav Galabov #include <sys/kernel.h>
31dfb5178fSStanislav Galabov #include <sys/lock.h>
32dfb5178fSStanislav Galabov #include <sys/malloc.h>
33dfb5178fSStanislav Galabov #include <sys/module.h>
34dfb5178fSStanislav Galabov #include <sys/mutex.h>
35dfb5178fSStanislav Galabov #include <sys/rman.h>
36dfb5178fSStanislav Galabov #include <sys/socket.h>
37dfb5178fSStanislav Galabov #include <sys/sockio.h>
38dfb5178fSStanislav Galabov #include <sys/sysctl.h>
39dfb5178fSStanislav Galabov #include <sys/systm.h>
40dfb5178fSStanislav Galabov 
41dfb5178fSStanislav Galabov #include <net/if.h>
42dfb5178fSStanislav Galabov #include <net/if_var.h>
43dfb5178fSStanislav Galabov #include <net/ethernet.h>
44dfb5178fSStanislav Galabov #include <net/if_media.h>
45dfb5178fSStanislav Galabov #include <net/if_types.h>
46dfb5178fSStanislav Galabov 
47dfb5178fSStanislav Galabov #include <machine/bus.h>
48dfb5178fSStanislav Galabov #include <dev/mii/mii.h>
49dfb5178fSStanislav Galabov #include <dev/mii/miivar.h>
50dfb5178fSStanislav Galabov #include <dev/mdio/mdio.h>
51dfb5178fSStanislav Galabov 
52dfb5178fSStanislav Galabov #include <dev/etherswitch/etherswitch.h>
53dfb5178fSStanislav Galabov #include <dev/etherswitch/mtkswitch/mtkswitchvar.h>
54dfb5178fSStanislav Galabov #include <dev/etherswitch/mtkswitch/mtkswitch_mt7620.h>
55dfb5178fSStanislav Galabov 
56dfb5178fSStanislav Galabov static int
mtkswitch_phy_read_locked(struct mtkswitch_softc * sc,int phy,int reg)57dfb5178fSStanislav Galabov mtkswitch_phy_read_locked(struct mtkswitch_softc *sc, int phy, int reg)
58dfb5178fSStanislav Galabov {
59dfb5178fSStanislav Galabov 	uint32_t data;
60dfb5178fSStanislav Galabov 
61dfb5178fSStanislav Galabov 	MTKSWITCH_WRITE(sc, MTKSWITCH_PIAC, PIAC_PHY_ACS_ST | PIAC_MDIO_ST |
62dfb5178fSStanislav Galabov 	    (reg << PIAC_MDIO_REG_ADDR_OFF) | (phy << PIAC_MDIO_PHY_ADDR_OFF) |
63dfb5178fSStanislav Galabov 	    PIAC_MDIO_CMD_READ);
64dfb5178fSStanislav Galabov 	while ((data = MTKSWITCH_READ(sc, MTKSWITCH_PIAC)) & PIAC_PHY_ACS_ST);
65dfb5178fSStanislav Galabov 
66dfb5178fSStanislav Galabov 	return ((int)(data & PIAC_MDIO_RW_DATA_MASK));
67dfb5178fSStanislav Galabov }
68dfb5178fSStanislav Galabov 
69dfb5178fSStanislav Galabov static int
mtkswitch_phy_read(device_t dev,int phy,int reg)70dfb5178fSStanislav Galabov mtkswitch_phy_read(device_t dev, int phy, int reg)
71dfb5178fSStanislav Galabov {
72dfb5178fSStanislav Galabov 	struct mtkswitch_softc *sc = device_get_softc(dev);
73dfb5178fSStanislav Galabov 	int data;
74dfb5178fSStanislav Galabov 
75dfb5178fSStanislav Galabov 	if ((phy < 0 || phy >= 32) || (reg < 0 || reg >= 32))
76dfb5178fSStanislav Galabov 		return (ENXIO);
77dfb5178fSStanislav Galabov 
78dfb5178fSStanislav Galabov 	MTKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
79dfb5178fSStanislav Galabov 	MTKSWITCH_LOCK(sc);
80dfb5178fSStanislav Galabov 	data = mtkswitch_phy_read_locked(sc, phy, reg);
81dfb5178fSStanislav Galabov 	MTKSWITCH_UNLOCK(sc);
82dfb5178fSStanislav Galabov 
83dfb5178fSStanislav Galabov 	return (data);
84dfb5178fSStanislav Galabov }
85dfb5178fSStanislav Galabov 
86dfb5178fSStanislav Galabov static int
mtkswitch_phy_write_locked(struct mtkswitch_softc * sc,int phy,int reg,int val)87dfb5178fSStanislav Galabov mtkswitch_phy_write_locked(struct mtkswitch_softc *sc, int phy, int reg,
88dfb5178fSStanislav Galabov     int val)
89dfb5178fSStanislav Galabov {
90dfb5178fSStanislav Galabov 
91dfb5178fSStanislav Galabov 	MTKSWITCH_WRITE(sc, MTKSWITCH_PIAC, PIAC_PHY_ACS_ST | PIAC_MDIO_ST |
92dfb5178fSStanislav Galabov 	    (reg << PIAC_MDIO_REG_ADDR_OFF) | (phy << PIAC_MDIO_PHY_ADDR_OFF) |
93dfb5178fSStanislav Galabov 	    (val & PIAC_MDIO_RW_DATA_MASK) | PIAC_MDIO_CMD_WRITE);
94dfb5178fSStanislav Galabov 	while (MTKSWITCH_READ(sc, MTKSWITCH_PIAC) & PIAC_PHY_ACS_ST);
95dfb5178fSStanislav Galabov 
96dfb5178fSStanislav Galabov 	return (0);
97dfb5178fSStanislav Galabov }
98dfb5178fSStanislav Galabov 
99dfb5178fSStanislav Galabov static int
mtkswitch_phy_write(device_t dev,int phy,int reg,int val)100dfb5178fSStanislav Galabov mtkswitch_phy_write(device_t dev, int phy, int reg, int val)
101dfb5178fSStanislav Galabov {
102dfb5178fSStanislav Galabov 	struct mtkswitch_softc *sc = device_get_softc(dev);
103dfb5178fSStanislav Galabov 	int res;
104dfb5178fSStanislav Galabov 
105dfb5178fSStanislav Galabov 	if ((phy < 0 || phy >= 32) || (reg < 0 || reg >= 32))
106dfb5178fSStanislav Galabov 		return (ENXIO);
107dfb5178fSStanislav Galabov 
108dfb5178fSStanislav Galabov 	MTKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
109dfb5178fSStanislav Galabov 	MTKSWITCH_LOCK(sc);
110dfb5178fSStanislav Galabov 	res = mtkswitch_phy_write_locked(sc, phy, reg, val);
111dfb5178fSStanislav Galabov 	MTKSWITCH_UNLOCK(sc);
112dfb5178fSStanislav Galabov 
113dfb5178fSStanislav Galabov 	return (res);
114dfb5178fSStanislav Galabov }
115dfb5178fSStanislav Galabov 
116dfb5178fSStanislav Galabov static uint32_t
mtkswitch_reg_read32(struct mtkswitch_softc * sc,int reg)117dfb5178fSStanislav Galabov mtkswitch_reg_read32(struct mtkswitch_softc *sc, int reg)
118dfb5178fSStanislav Galabov {
119dfb5178fSStanislav Galabov 
120dfb5178fSStanislav Galabov 	return (MTKSWITCH_READ(sc, reg));
121dfb5178fSStanislav Galabov }
122dfb5178fSStanislav Galabov 
123dfb5178fSStanislav Galabov static uint32_t
mtkswitch_reg_write32(struct mtkswitch_softc * sc,int reg,uint32_t val)124dfb5178fSStanislav Galabov mtkswitch_reg_write32(struct mtkswitch_softc *sc, int reg, uint32_t val)
125dfb5178fSStanislav Galabov {
126dfb5178fSStanislav Galabov 
127dfb5178fSStanislav Galabov 	MTKSWITCH_WRITE(sc, reg, val);
128dfb5178fSStanislav Galabov 	return (0);
129dfb5178fSStanislav Galabov }
130dfb5178fSStanislav Galabov 
131dfb5178fSStanislav Galabov static uint32_t
mtkswitch_reg_read32_mt7621(struct mtkswitch_softc * sc,int reg)132dfb5178fSStanislav Galabov mtkswitch_reg_read32_mt7621(struct mtkswitch_softc *sc, int reg)
133dfb5178fSStanislav Galabov {
134dfb5178fSStanislav Galabov 	uint32_t low, hi;
135dfb5178fSStanislav Galabov 
136dfb5178fSStanislav Galabov 	mtkswitch_phy_write_locked(sc, MTKSWITCH_GLOBAL_PHY,
137dfb5178fSStanislav Galabov 	    MTKSWITCH_GLOBAL_REG, MTKSWITCH_REG_ADDR(reg));
138dfb5178fSStanislav Galabov 	low = mtkswitch_phy_read_locked(sc, MTKSWITCH_GLOBAL_PHY,
139dfb5178fSStanislav Galabov 	    MTKSWITCH_REG_LO(reg));
140dfb5178fSStanislav Galabov 	hi = mtkswitch_phy_read_locked(sc, MTKSWITCH_GLOBAL_PHY,
141aeb665b5SEd Maste 	    MTKSWITCH_REG_HI(reg));
142dfb5178fSStanislav Galabov 	return (low | (hi << 16));
143dfb5178fSStanislav Galabov }
144dfb5178fSStanislav Galabov 
145dfb5178fSStanislav Galabov static uint32_t
mtkswitch_reg_write32_mt7621(struct mtkswitch_softc * sc,int reg,uint32_t val)146dfb5178fSStanislav Galabov mtkswitch_reg_write32_mt7621(struct mtkswitch_softc *sc, int reg, uint32_t val)
147dfb5178fSStanislav Galabov {
148dfb5178fSStanislav Galabov 
149dfb5178fSStanislav Galabov 	mtkswitch_phy_write_locked(sc, MTKSWITCH_GLOBAL_PHY,
150dfb5178fSStanislav Galabov 	    MTKSWITCH_GLOBAL_REG, MTKSWITCH_REG_ADDR(reg));
151dfb5178fSStanislav Galabov 	mtkswitch_phy_write_locked(sc, MTKSWITCH_GLOBAL_PHY,
152dfb5178fSStanislav Galabov 	    MTKSWITCH_REG_LO(reg), MTKSWITCH_VAL_LO(val));
153dfb5178fSStanislav Galabov 	mtkswitch_phy_write_locked(sc, MTKSWITCH_GLOBAL_PHY,
154dfb5178fSStanislav Galabov 	    MTKSWITCH_REG_HI(reg), MTKSWITCH_VAL_HI(val));
155dfb5178fSStanislav Galabov 	return (0);
156dfb5178fSStanislav Galabov }
157dfb5178fSStanislav Galabov 
158dfb5178fSStanislav Galabov static int
mtkswitch_reg_read(device_t dev,int reg)159dfb5178fSStanislav Galabov mtkswitch_reg_read(device_t dev, int reg)
160dfb5178fSStanislav Galabov {
161dfb5178fSStanislav Galabov 	struct mtkswitch_softc *sc = device_get_softc(dev);
162dfb5178fSStanislav Galabov 	uint32_t val;
163dfb5178fSStanislav Galabov 
164dfb5178fSStanislav Galabov 	val = sc->hal.mtkswitch_read(sc, MTKSWITCH_REG32(reg));
165dfb5178fSStanislav Galabov 	if (MTKSWITCH_IS_HI16(reg))
166dfb5178fSStanislav Galabov 		return (MTKSWITCH_HI16(val));
167dfb5178fSStanislav Galabov 	return (MTKSWITCH_LO16(val));
168dfb5178fSStanislav Galabov }
169dfb5178fSStanislav Galabov 
170dfb5178fSStanislav Galabov static int
mtkswitch_reg_write(device_t dev,int reg,int val)171dfb5178fSStanislav Galabov mtkswitch_reg_write(device_t dev, int reg, int val)
172dfb5178fSStanislav Galabov {
173dfb5178fSStanislav Galabov 	struct mtkswitch_softc *sc = device_get_softc(dev);
174dfb5178fSStanislav Galabov 	uint32_t tmp;
175dfb5178fSStanislav Galabov 
176dfb5178fSStanislav Galabov 	tmp = sc->hal.mtkswitch_read(sc, MTKSWITCH_REG32(reg));
177dfb5178fSStanislav Galabov 	if (MTKSWITCH_IS_HI16(reg)) {
178dfb5178fSStanislav Galabov 		tmp &= MTKSWITCH_LO16_MSK;
179dfb5178fSStanislav Galabov 		tmp |= MTKSWITCH_TO_HI16(val);
180dfb5178fSStanislav Galabov 	} else {
181dfb5178fSStanislav Galabov 		tmp &= MTKSWITCH_HI16_MSK;
182dfb5178fSStanislav Galabov 		tmp |= MTKSWITCH_TO_LO16(val);
183dfb5178fSStanislav Galabov 	}
184dfb5178fSStanislav Galabov 	sc->hal.mtkswitch_write(sc, MTKSWITCH_REG32(reg), tmp);
185dfb5178fSStanislav Galabov 
186dfb5178fSStanislav Galabov 	return (0);
187dfb5178fSStanislav Galabov }
188dfb5178fSStanislav Galabov 
189dfb5178fSStanislav Galabov static int
mtkswitch_reset(struct mtkswitch_softc * sc)190dfb5178fSStanislav Galabov mtkswitch_reset(struct mtkswitch_softc *sc)
191dfb5178fSStanislav Galabov {
192dfb5178fSStanislav Galabov 
193dfb5178fSStanislav Galabov 	/* We don't reset the switch for now */
194dfb5178fSStanislav Galabov 	return (0);
195dfb5178fSStanislav Galabov }
196dfb5178fSStanislav Galabov 
197dfb5178fSStanislav Galabov static int
mtkswitch_hw_setup(struct mtkswitch_softc * sc)198dfb5178fSStanislav Galabov mtkswitch_hw_setup(struct mtkswitch_softc *sc)
199dfb5178fSStanislav Galabov {
200dfb5178fSStanislav Galabov 
201dfb5178fSStanislav Galabov 	/*
202dfb5178fSStanislav Galabov 	 * TODO: parse the device tree and see if we need to configure
203dfb5178fSStanislav Galabov 	 *       ports, etc. differently. For now we fallback to defaults.
204dfb5178fSStanislav Galabov 	 */
205dfb5178fSStanislav Galabov 
206dfb5178fSStanislav Galabov 	/* Called early and hence unlocked */
207dfb5178fSStanislav Galabov 	return (0);
208dfb5178fSStanislav Galabov }
209dfb5178fSStanislav Galabov 
210dfb5178fSStanislav Galabov static int
mtkswitch_hw_global_setup(struct mtkswitch_softc * sc)211dfb5178fSStanislav Galabov mtkswitch_hw_global_setup(struct mtkswitch_softc *sc)
212dfb5178fSStanislav Galabov {
213dfb5178fSStanislav Galabov 	/* Currently does nothing */
214dfb5178fSStanislav Galabov 
215dfb5178fSStanislav Galabov 	/* Called early and hence unlocked */
216dfb5178fSStanislav Galabov 	return (0);
217dfb5178fSStanislav Galabov }
218dfb5178fSStanislav Galabov 
219dfb5178fSStanislav Galabov static void
mtkswitch_port_init(struct mtkswitch_softc * sc,int port)220dfb5178fSStanislav Galabov mtkswitch_port_init(struct mtkswitch_softc *sc, int port)
221dfb5178fSStanislav Galabov {
222dfb5178fSStanislav Galabov 	uint32_t val;
223dfb5178fSStanislav Galabov 
224dfb5178fSStanislav Galabov 	/* Called early and hence unlocked */
225dfb5178fSStanislav Galabov 
226dfb5178fSStanislav Galabov 	/* Set the port to secure mode */
2271027d6d6SStanislav Galabov 	val = sc->hal.mtkswitch_read(sc, MTKSWITCH_PCR(port));
2281027d6d6SStanislav Galabov 	val |= PCR_PORT_VLAN_SECURE;
2291027d6d6SStanislav Galabov 	sc->hal.mtkswitch_write(sc, MTKSWITCH_PCR(port), val);
230dfb5178fSStanislav Galabov 
231dfb5178fSStanislav Galabov 	/* Set port's vlan_attr to user port */
232dfb5178fSStanislav Galabov 	val = sc->hal.mtkswitch_read(sc, MTKSWITCH_PVC(port));
2331027d6d6SStanislav Galabov 	val &= ~PVC_VLAN_ATTR_MASK;
234dfb5178fSStanislav Galabov 	sc->hal.mtkswitch_write(sc, MTKSWITCH_PVC(port), val);
235dfb5178fSStanislav Galabov 
2361027d6d6SStanislav Galabov 	val = PMCR_CFG_DEFAULT;
2371027d6d6SStanislav Galabov 	if (port == sc->cpuport)
2381027d6d6SStanislav Galabov 		val |= PMCR_FORCE_LINK | PMCR_FORCE_DPX | PMCR_FORCE_SPD_1000 |
2391027d6d6SStanislav Galabov 		    PMCR_FORCE_MODE;
240dfb5178fSStanislav Galabov 	/* Set port's MAC to default settings */
2411027d6d6SStanislav Galabov 	sc->hal.mtkswitch_write(sc, MTKSWITCH_PMCR(port), val);
242dfb5178fSStanislav Galabov }
243dfb5178fSStanislav Galabov 
244dfb5178fSStanislav Galabov static uint32_t
mtkswitch_get_port_status(struct mtkswitch_softc * sc,int port)245dfb5178fSStanislav Galabov mtkswitch_get_port_status(struct mtkswitch_softc *sc, int port)
246dfb5178fSStanislav Galabov {
247dfb5178fSStanislav Galabov 	uint32_t val, res, tmp;
248dfb5178fSStanislav Galabov 
249dfb5178fSStanislav Galabov 	MTKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
250dfb5178fSStanislav Galabov 	res = 0;
251dfb5178fSStanislav Galabov 	val = sc->hal.mtkswitch_read(sc, MTKSWITCH_PMSR(port));
252dfb5178fSStanislav Galabov 
253dfb5178fSStanislav Galabov 	if (val & PMSR_MAC_LINK_STS)
254dfb5178fSStanislav Galabov 		res |= MTKSWITCH_LINK_UP;
255dfb5178fSStanislav Galabov 	if (val & PMSR_MAC_DPX_STS)
256dfb5178fSStanislav Galabov 		res |= MTKSWITCH_DUPLEX;
257dfb5178fSStanislav Galabov 	tmp = PMSR_MAC_SPD(val);
258dfb5178fSStanislav Galabov 	if (tmp == 0)
259dfb5178fSStanislav Galabov 		res |= MTKSWITCH_SPEED_10;
260dfb5178fSStanislav Galabov 	else if (tmp == 1)
261dfb5178fSStanislav Galabov 		res |= MTKSWITCH_SPEED_100;
262dfb5178fSStanislav Galabov 	else if (tmp == 2)
263dfb5178fSStanislav Galabov 		res |= MTKSWITCH_SPEED_1000;
264dfb5178fSStanislav Galabov 	if (val & PMSR_TX_FC_STS)
265dfb5178fSStanislav Galabov 		res |= MTKSWITCH_TXFLOW;
266dfb5178fSStanislav Galabov 	if (val & PMSR_RX_FC_STS)
267dfb5178fSStanislav Galabov 		res |= MTKSWITCH_RXFLOW;
268dfb5178fSStanislav Galabov 
269dfb5178fSStanislav Galabov 	return (res);
270dfb5178fSStanislav Galabov }
271dfb5178fSStanislav Galabov 
272dfb5178fSStanislav Galabov static int
mtkswitch_atu_flush(struct mtkswitch_softc * sc)273dfb5178fSStanislav Galabov mtkswitch_atu_flush(struct mtkswitch_softc *sc)
274dfb5178fSStanislav Galabov {
275dfb5178fSStanislav Galabov 
276dfb5178fSStanislav Galabov 	MTKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
277dfb5178fSStanislav Galabov 
278dfb5178fSStanislav Galabov 	/* Flush all non-static MAC addresses */
279dfb5178fSStanislav Galabov 	while (sc->hal.mtkswitch_read(sc, MTKSWITCH_ATC) & ATC_BUSY);
280dfb5178fSStanislav Galabov 	sc->hal.mtkswitch_write(sc, MTKSWITCH_ATC, ATC_BUSY |
281dfb5178fSStanislav Galabov 	    ATC_AC_MAT_NON_STATIC_MACS | ATC_AC_CMD_CLEAN);
282dfb5178fSStanislav Galabov 	while (sc->hal.mtkswitch_read(sc, MTKSWITCH_ATC) & ATC_BUSY);
283dfb5178fSStanislav Galabov 
284dfb5178fSStanislav Galabov 	return (0);
285dfb5178fSStanislav Galabov }
286dfb5178fSStanislav Galabov 
287dfb5178fSStanislav Galabov static int
mtkswitch_port_vlan_setup(struct mtkswitch_softc * sc,etherswitch_port_t * p)288dfb5178fSStanislav Galabov mtkswitch_port_vlan_setup(struct mtkswitch_softc *sc, etherswitch_port_t *p)
289dfb5178fSStanislav Galabov {
290dfb5178fSStanislav Galabov 	int err;
291dfb5178fSStanislav Galabov 
292dfb5178fSStanislav Galabov 	/*
293dfb5178fSStanislav Galabov 	 * Port behaviour wrt tag/untag/stack is currently defined per-VLAN.
294dfb5178fSStanislav Galabov 	 * So we say we don't support it here.
295dfb5178fSStanislav Galabov 	 */
296dfb5178fSStanislav Galabov 	if ((p->es_flags & (ETHERSWITCH_PORT_DOUBLE_TAG |
297dfb5178fSStanislav Galabov 	    ETHERSWITCH_PORT_ADDTAG | ETHERSWITCH_PORT_STRIPTAG)) != 0)
298dfb5178fSStanislav Galabov 		return (ENOTSUP);
299dfb5178fSStanislav Galabov 
300dfb5178fSStanislav Galabov 	MTKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
301dfb5178fSStanislav Galabov 	MTKSWITCH_LOCK(sc);
302dfb5178fSStanislav Galabov 
303dfb5178fSStanislav Galabov 	/* Set the PVID */
304dfb5178fSStanislav Galabov 	if (p->es_pvid != 0) {
305dfb5178fSStanislav Galabov 		err = sc->hal.mtkswitch_vlan_set_pvid(sc, p->es_port,
306dfb5178fSStanislav Galabov 		    p->es_pvid);
307dfb5178fSStanislav Galabov 		if (err != 0) {
308dfb5178fSStanislav Galabov 			MTKSWITCH_UNLOCK(sc);
309dfb5178fSStanislav Galabov 			return (err);
310dfb5178fSStanislav Galabov 		}
311dfb5178fSStanislav Galabov 	}
312dfb5178fSStanislav Galabov 
313dfb5178fSStanislav Galabov 	MTKSWITCH_UNLOCK(sc);
314dfb5178fSStanislav Galabov 
315dfb5178fSStanislav Galabov 	return (0);
316dfb5178fSStanislav Galabov }
317dfb5178fSStanislav Galabov 
318dfb5178fSStanislav Galabov static int
mtkswitch_port_vlan_get(struct mtkswitch_softc * sc,etherswitch_port_t * p)319dfb5178fSStanislav Galabov mtkswitch_port_vlan_get(struct mtkswitch_softc *sc, etherswitch_port_t *p)
320dfb5178fSStanislav Galabov {
321dfb5178fSStanislav Galabov 
322dfb5178fSStanislav Galabov 	MTKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
323dfb5178fSStanislav Galabov 	MTKSWITCH_LOCK(sc);
324dfb5178fSStanislav Galabov 
325dfb5178fSStanislav Galabov 	/* Retrieve the PVID */
326dfb5178fSStanislav Galabov 	sc->hal.mtkswitch_vlan_get_pvid(sc, p->es_port, &p->es_pvid);
327dfb5178fSStanislav Galabov 
328dfb5178fSStanislav Galabov 	/*
329dfb5178fSStanislav Galabov 	 * Port flags are not supported at the moment.
330dfb5178fSStanislav Galabov 	 * Port's tag/untag/stack behaviour is defined per-VLAN.
331dfb5178fSStanislav Galabov 	 */
332dfb5178fSStanislav Galabov 	p->es_flags = 0;
333dfb5178fSStanislav Galabov 
334dfb5178fSStanislav Galabov 	MTKSWITCH_UNLOCK(sc);
335dfb5178fSStanislav Galabov 
336dfb5178fSStanislav Galabov 	return (0);
337dfb5178fSStanislav Galabov }
338dfb5178fSStanislav Galabov 
339dfb5178fSStanislav Galabov static void
mtkswitch_invalidate_vlan(struct mtkswitch_softc * sc,uint32_t vid)340dfb5178fSStanislav Galabov mtkswitch_invalidate_vlan(struct mtkswitch_softc *sc, uint32_t vid)
341dfb5178fSStanislav Galabov {
342dfb5178fSStanislav Galabov 
343dfb5178fSStanislav Galabov 	while (sc->hal.mtkswitch_read(sc, MTKSWITCH_VTCR) & VTCR_BUSY);
344dfb5178fSStanislav Galabov 	sc->hal.mtkswitch_write(sc, MTKSWITCH_VTCR, VTCR_BUSY |
345dfb5178fSStanislav Galabov 	    VTCR_FUNC_VID_INVALID | (vid & VTCR_VID_MASK));
346dfb5178fSStanislav Galabov 	while (sc->hal.mtkswitch_read(sc, MTKSWITCH_VTCR) & VTCR_BUSY);
347dfb5178fSStanislav Galabov }
348dfb5178fSStanislav Galabov 
349dfb5178fSStanislav Galabov static void
mtkswitch_vlan_init_hw(struct mtkswitch_softc * sc)350dfb5178fSStanislav Galabov mtkswitch_vlan_init_hw(struct mtkswitch_softc *sc)
351dfb5178fSStanislav Galabov {
352dfb5178fSStanislav Galabov 	uint32_t val, vid, i;
353dfb5178fSStanislav Galabov 
354dfb5178fSStanislav Galabov 	MTKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
355dfb5178fSStanislav Galabov 	MTKSWITCH_LOCK(sc);
356dfb5178fSStanislav Galabov 	/* Reset all VLANs to defaults first */
357dfb5178fSStanislav Galabov 	for (i = 0; i < sc->info.es_nvlangroups; i++) {
358dfb5178fSStanislav Galabov 		mtkswitch_invalidate_vlan(sc, i);
359dfb5178fSStanislav Galabov 		if (sc->sc_switchtype == MTK_SWITCH_MT7620) {
360dfb5178fSStanislav Galabov 			val = sc->hal.mtkswitch_read(sc, MTKSWITCH_VTIM(i));
3611027d6d6SStanislav Galabov 			val &= ~(VTIM_MASK << VTIM_OFF(i));
362dfb5178fSStanislav Galabov 			val |= ((i + 1) << VTIM_OFF(i));
363dfb5178fSStanislav Galabov 			sc->hal.mtkswitch_write(sc, MTKSWITCH_VTIM(i), val);
364dfb5178fSStanislav Galabov 		}
365dfb5178fSStanislav Galabov 	}
366dfb5178fSStanislav Galabov 
367dfb5178fSStanislav Galabov 	/* Now, add all ports as untagged members of VLAN 1 */
368dfb5178fSStanislav Galabov 	if (sc->sc_switchtype == MTK_SWITCH_MT7620) {
369dfb5178fSStanislav Galabov 		/* MT7620 uses vid index instead of actual vid */
370dfb5178fSStanislav Galabov 		vid = 0;
371dfb5178fSStanislav Galabov 	} else {
372dfb5178fSStanislav Galabov 		/* MT7621 uses the vid itself */
373dfb5178fSStanislav Galabov 		vid = 1;
374dfb5178fSStanislav Galabov 	}
375dfb5178fSStanislav Galabov 	val = VAWD1_IVL_MAC | VAWD1_VTAG_EN | VAWD1_VALID;
376dfb5178fSStanislav Galabov 	for (i = 0; i < sc->info.es_nports; i++)
377dfb5178fSStanislav Galabov 		val |= VAWD1_PORT_MEMBER(i);
378dfb5178fSStanislav Galabov 	sc->hal.mtkswitch_write(sc, MTKSWITCH_VAWD1, val);
379dfb5178fSStanislav Galabov 	sc->hal.mtkswitch_write(sc, MTKSWITCH_VAWD2, 0);
380dfb5178fSStanislav Galabov 	val = VTCR_BUSY | VTCR_FUNC_VID_WRITE | vid;
381dfb5178fSStanislav Galabov 	sc->hal.mtkswitch_write(sc, MTKSWITCH_VTCR, val);
382dfb5178fSStanislav Galabov 
383dfb5178fSStanislav Galabov 	/* Set all port PVIDs to 1 */
384dfb5178fSStanislav Galabov 	for (i = 0; i < sc->info.es_nports; i++) {
385dfb5178fSStanislav Galabov 		sc->hal.mtkswitch_vlan_set_pvid(sc, i, 1);
386dfb5178fSStanislav Galabov 	}
387dfb5178fSStanislav Galabov 
388dfb5178fSStanislav Galabov 	MTKSWITCH_UNLOCK(sc);
389dfb5178fSStanislav Galabov }
390dfb5178fSStanislav Galabov 
391dfb5178fSStanislav Galabov static int
mtkswitch_vlan_getvgroup(struct mtkswitch_softc * sc,etherswitch_vlangroup_t * v)392dfb5178fSStanislav Galabov mtkswitch_vlan_getvgroup(struct mtkswitch_softc *sc, etherswitch_vlangroup_t *v)
393dfb5178fSStanislav Galabov {
394dfb5178fSStanislav Galabov 	uint32_t val, i;
395dfb5178fSStanislav Galabov 
396dfb5178fSStanislav Galabov 	MTKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
397dfb5178fSStanislav Galabov 
398dfb5178fSStanislav Galabov 	if ((sc->vlan_mode != ETHERSWITCH_VLAN_DOT1Q) ||
399dfb5178fSStanislav Galabov 	    (v->es_vlangroup > sc->info.es_nvlangroups))
400dfb5178fSStanislav Galabov 		return (EINVAL);
401dfb5178fSStanislav Galabov 
402dfb5178fSStanislav Galabov 	/* Reset the member ports. */
403dfb5178fSStanislav Galabov 	v->es_untagged_ports = 0;
404dfb5178fSStanislav Galabov 	v->es_member_ports = 0;
405dfb5178fSStanislav Galabov 
406dfb5178fSStanislav Galabov 	/* Not supported for now */
407dfb5178fSStanislav Galabov 	v->es_fid = 0;
408dfb5178fSStanislav Galabov 
409dfb5178fSStanislav Galabov 	MTKSWITCH_LOCK(sc);
410dfb5178fSStanislav Galabov 	if (sc->sc_switchtype == MTK_SWITCH_MT7620) {
411dfb5178fSStanislav Galabov 		v->es_vid = (sc->hal.mtkswitch_read(sc,
412dfb5178fSStanislav Galabov 		    MTKSWITCH_VTIM(v->es_vlangroup)) >>
413dfb5178fSStanislav Galabov 		    VTIM_OFF(v->es_vlangroup)) & VTIM_MASK;
414dfb5178fSStanislav Galabov 	} else {
415dfb5178fSStanislav Galabov 		v->es_vid = v->es_vlangroup;
416dfb5178fSStanislav Galabov 	}
417dfb5178fSStanislav Galabov 
418dfb5178fSStanislav Galabov 	while (sc->hal.mtkswitch_read(sc, MTKSWITCH_VTCR) & VTCR_BUSY);
419dfb5178fSStanislav Galabov 	sc->hal.mtkswitch_write(sc, MTKSWITCH_VTCR, VTCR_BUSY |
420dfb5178fSStanislav Galabov 	    VTCR_FUNC_VID_READ | (v->es_vlangroup & VTCR_VID_MASK));
421dfb5178fSStanislav Galabov 	while ((val = sc->hal.mtkswitch_read(sc, MTKSWITCH_VTCR)) & VTCR_BUSY);
422dfb5178fSStanislav Galabov 	if (val & VTCR_IDX_INVALID) {
423dfb5178fSStanislav Galabov 		MTKSWITCH_UNLOCK(sc);
424dfb5178fSStanislav Galabov 		return (0);
425dfb5178fSStanislav Galabov 	}
426dfb5178fSStanislav Galabov 
427dfb5178fSStanislav Galabov 	val = sc->hal.mtkswitch_read(sc, MTKSWITCH_VAWD1);
428dfb5178fSStanislav Galabov 	if (val & VAWD1_VALID)
429dfb5178fSStanislav Galabov 		v->es_vid |= ETHERSWITCH_VID_VALID;
430dfb5178fSStanislav Galabov 	else {
431dfb5178fSStanislav Galabov 		MTKSWITCH_UNLOCK(sc);
432dfb5178fSStanislav Galabov 		return (0);
433dfb5178fSStanislav Galabov 	}
434dfb5178fSStanislav Galabov 	v->es_member_ports = (val >> VAWD1_MEMBER_OFF) & VAWD1_MEMBER_MASK;
435dfb5178fSStanislav Galabov 
436dfb5178fSStanislav Galabov 	val = sc->hal.mtkswitch_read(sc, MTKSWITCH_VAWD2);
437dfb5178fSStanislav Galabov 	for (i = 0; i < sc->info.es_nports; i++) {
438dfb5178fSStanislav Galabov 		if ((val & VAWD2_PORT_MASK(i)) == VAWD2_PORT_UNTAGGED(i))
439dfb5178fSStanislav Galabov 			v->es_untagged_ports |= (1<<i);
440dfb5178fSStanislav Galabov 	}
441dfb5178fSStanislav Galabov 
442dfb5178fSStanislav Galabov 	MTKSWITCH_UNLOCK(sc);
443dfb5178fSStanislav Galabov 	return (0);
444dfb5178fSStanislav Galabov }
445dfb5178fSStanislav Galabov 
446dfb5178fSStanislav Galabov static int
mtkswitch_vlan_setvgroup(struct mtkswitch_softc * sc,etherswitch_vlangroup_t * v)447dfb5178fSStanislav Galabov mtkswitch_vlan_setvgroup(struct mtkswitch_softc *sc, etherswitch_vlangroup_t *v)
448dfb5178fSStanislav Galabov {
449dfb5178fSStanislav Galabov 	uint32_t val, i, vid;
450dfb5178fSStanislav Galabov 
451dfb5178fSStanislav Galabov 	MTKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
452dfb5178fSStanislav Galabov 
453dfb5178fSStanislav Galabov 	if ((sc->vlan_mode != ETHERSWITCH_VLAN_DOT1Q) ||
454dfb5178fSStanislav Galabov 	    (v->es_vlangroup > sc->info.es_nvlangroups))
455dfb5178fSStanislav Galabov 		return (EINVAL);
456dfb5178fSStanislav Galabov 
457dfb5178fSStanislav Galabov 	/* We currently don't support FID */
458dfb5178fSStanislav Galabov 	if (v->es_fid != 0)
459dfb5178fSStanislav Galabov 		return (EINVAL);
460dfb5178fSStanislav Galabov 
461dfb5178fSStanislav Galabov 	MTKSWITCH_LOCK(sc);
462dfb5178fSStanislav Galabov 	while (sc->hal.mtkswitch_read(sc, MTKSWITCH_VTCR) & VTCR_BUSY);
463dfb5178fSStanislav Galabov 	if (sc->sc_switchtype == MTK_SWITCH_MT7620) {
464dfb5178fSStanislav Galabov 		val = sc->hal.mtkswitch_read(sc,
465dfb5178fSStanislav Galabov 		    MTKSWITCH_VTIM(v->es_vlangroup));
4661027d6d6SStanislav Galabov 		val &= ~(VTIM_MASK << VTIM_OFF(v->es_vlangroup));
467dfb5178fSStanislav Galabov 		val |= ((v->es_vid & VTIM_MASK) << VTIM_OFF(v->es_vlangroup));
468dfb5178fSStanislav Galabov 		sc->hal.mtkswitch_write(sc, MTKSWITCH_VTIM(v->es_vlangroup),
469dfb5178fSStanislav Galabov 		    val);
470dfb5178fSStanislav Galabov 		vid = v->es_vlangroup;
471dfb5178fSStanislav Galabov 	} else
472dfb5178fSStanislav Galabov 		vid = v->es_vid;
473dfb5178fSStanislav Galabov 
474dfb5178fSStanislav Galabov 	/* We use FID 0 */
475dfb5178fSStanislav Galabov 	val = VAWD1_IVL_MAC | VAWD1_VTAG_EN | VAWD1_VALID;
476dfb5178fSStanislav Galabov 	val |= ((v->es_member_ports & VAWD1_MEMBER_MASK) << VAWD1_MEMBER_OFF);
477dfb5178fSStanislav Galabov 	sc->hal.mtkswitch_write(sc, MTKSWITCH_VAWD1, val);
478dfb5178fSStanislav Galabov 
479dfb5178fSStanislav Galabov 	/* Set tagged ports */
480dfb5178fSStanislav Galabov 	val = 0;
481dfb5178fSStanislav Galabov 	for (i = 0; i < sc->info.es_nports; i++)
482dfb5178fSStanislav Galabov 		if (((1<<i) & v->es_untagged_ports) == 0)
483dfb5178fSStanislav Galabov 			val |= VAWD2_PORT_TAGGED(i);
484dfb5178fSStanislav Galabov 	sc->hal.mtkswitch_write(sc, MTKSWITCH_VAWD2, val);
485dfb5178fSStanislav Galabov 
486dfb5178fSStanislav Galabov 	/* Write the VLAN entry */
487dfb5178fSStanislav Galabov 	sc->hal.mtkswitch_write(sc, MTKSWITCH_VTCR, VTCR_BUSY |
488dfb5178fSStanislav Galabov 	    VTCR_FUNC_VID_WRITE | (vid & VTCR_VID_MASK));
489dfb5178fSStanislav Galabov 	while ((val = sc->hal.mtkswitch_read(sc, MTKSWITCH_VTCR)) & VTCR_BUSY);
490dfb5178fSStanislav Galabov 
491dfb5178fSStanislav Galabov 	MTKSWITCH_UNLOCK(sc);
492dfb5178fSStanislav Galabov 
493dfb5178fSStanislav Galabov 	if (val & VTCR_IDX_INVALID)
494dfb5178fSStanislav Galabov 		return (EINVAL);
495dfb5178fSStanislav Galabov 
496dfb5178fSStanislav Galabov 	return (0);
497dfb5178fSStanislav Galabov }
498dfb5178fSStanislav Galabov 
499dfb5178fSStanislav Galabov static int
mtkswitch_vlan_get_pvid(struct mtkswitch_softc * sc,int port,int * pvid)500dfb5178fSStanislav Galabov mtkswitch_vlan_get_pvid(struct mtkswitch_softc *sc, int port, int *pvid)
501dfb5178fSStanislav Galabov {
502dfb5178fSStanislav Galabov 
503dfb5178fSStanislav Galabov 	MTKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
504dfb5178fSStanislav Galabov 
505dfb5178fSStanislav Galabov 	*pvid = sc->hal.mtkswitch_read(sc, MTKSWITCH_PPBV1(port));
506dfb5178fSStanislav Galabov 	*pvid = PPBV_VID_FROM_REG(*pvid);
507dfb5178fSStanislav Galabov 
508dfb5178fSStanislav Galabov 	return (0);
509dfb5178fSStanislav Galabov }
510dfb5178fSStanislav Galabov 
511dfb5178fSStanislav Galabov static int
mtkswitch_vlan_set_pvid(struct mtkswitch_softc * sc,int port,int pvid)512dfb5178fSStanislav Galabov mtkswitch_vlan_set_pvid(struct mtkswitch_softc *sc, int port, int pvid)
513dfb5178fSStanislav Galabov {
514dfb5178fSStanislav Galabov 	uint32_t val;
515dfb5178fSStanislav Galabov 
516dfb5178fSStanislav Galabov 	MTKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
517dfb5178fSStanislav Galabov 	val = PPBV_VID(pvid & PPBV_VID_MASK);
518dfb5178fSStanislav Galabov 	sc->hal.mtkswitch_write(sc, MTKSWITCH_PPBV1(port), val);
519dfb5178fSStanislav Galabov 	sc->hal.mtkswitch_write(sc, MTKSWITCH_PPBV2(port), val);
520dfb5178fSStanislav Galabov 
521dfb5178fSStanislav Galabov 	return (0);
522dfb5178fSStanislav Galabov }
523dfb5178fSStanislav Galabov 
524dfb5178fSStanislav Galabov extern void
mtk_attach_switch_mt7620(struct mtkswitch_softc * sc)525dfb5178fSStanislav Galabov mtk_attach_switch_mt7620(struct mtkswitch_softc *sc)
526dfb5178fSStanislav Galabov {
527dfb5178fSStanislav Galabov 
528dfb5178fSStanislav Galabov 	sc->portmap = 0x7f;
529dfb5178fSStanislav Galabov 	sc->phymap = 0x1f;
530dfb5178fSStanislav Galabov 
531dfb5178fSStanislav Galabov 	sc->info.es_nports = 7;
532dfb5178fSStanislav Galabov 	sc->info.es_vlan_caps = ETHERSWITCH_VLAN_DOT1Q;
533dfb5178fSStanislav Galabov 	sc->info.es_nvlangroups = 16;
534dfb5178fSStanislav Galabov 	sprintf(sc->info.es_name, "Mediatek GSW");
535dfb5178fSStanislav Galabov 
536dfb5178fSStanislav Galabov 	if (sc->sc_switchtype == MTK_SWITCH_MT7621) {
537dfb5178fSStanislav Galabov 		sc->hal.mtkswitch_read = mtkswitch_reg_read32_mt7621;
538dfb5178fSStanislav Galabov 		sc->hal.mtkswitch_write = mtkswitch_reg_write32_mt7621;
539dfb5178fSStanislav Galabov 		sc->info.es_nvlangroups = 4096;
540dfb5178fSStanislav Galabov 	} else {
541dfb5178fSStanislav Galabov 		sc->hal.mtkswitch_read = mtkswitch_reg_read32;
542dfb5178fSStanislav Galabov 		sc->hal.mtkswitch_write = mtkswitch_reg_write32;
543dfb5178fSStanislav Galabov 	}
544dfb5178fSStanislav Galabov 
545dfb5178fSStanislav Galabov 	sc->hal.mtkswitch_reset = mtkswitch_reset;
546dfb5178fSStanislav Galabov 	sc->hal.mtkswitch_hw_setup = mtkswitch_hw_setup;
547dfb5178fSStanislav Galabov 	sc->hal.mtkswitch_hw_global_setup = mtkswitch_hw_global_setup;
548dfb5178fSStanislav Galabov 	sc->hal.mtkswitch_port_init = mtkswitch_port_init;
549dfb5178fSStanislav Galabov 	sc->hal.mtkswitch_get_port_status = mtkswitch_get_port_status;
550dfb5178fSStanislav Galabov 	sc->hal.mtkswitch_atu_flush = mtkswitch_atu_flush;
551dfb5178fSStanislav Galabov 	sc->hal.mtkswitch_port_vlan_setup = mtkswitch_port_vlan_setup;
552dfb5178fSStanislav Galabov 	sc->hal.mtkswitch_port_vlan_get = mtkswitch_port_vlan_get;
553dfb5178fSStanislav Galabov 	sc->hal.mtkswitch_vlan_init_hw = mtkswitch_vlan_init_hw;
554dfb5178fSStanislav Galabov 	sc->hal.mtkswitch_vlan_getvgroup = mtkswitch_vlan_getvgroup;
555dfb5178fSStanislav Galabov 	sc->hal.mtkswitch_vlan_setvgroup = mtkswitch_vlan_setvgroup;
556dfb5178fSStanislav Galabov 	sc->hal.mtkswitch_vlan_get_pvid = mtkswitch_vlan_get_pvid;
557dfb5178fSStanislav Galabov 	sc->hal.mtkswitch_vlan_set_pvid = mtkswitch_vlan_set_pvid;
558dfb5178fSStanislav Galabov 	sc->hal.mtkswitch_phy_read = mtkswitch_phy_read;
559dfb5178fSStanislav Galabov 	sc->hal.mtkswitch_phy_write = mtkswitch_phy_write;
560dfb5178fSStanislav Galabov 	sc->hal.mtkswitch_reg_read = mtkswitch_reg_read;
561dfb5178fSStanislav Galabov 	sc->hal.mtkswitch_reg_write = mtkswitch_reg_write;
562dfb5178fSStanislav Galabov }
563