xref: /freebsd/sys/dev/etherswitch/arswitch/arswitch_vlans.c (revision 4d846d260e2b9a3d4d0a701462568268cbfe7a5b)
1b9f07b86SLuiz Otavio O Souza /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni  *
4b9f07b86SLuiz Otavio O Souza  * Copyright (c) 2013 Luiz Otavio O Souza.
5b9f07b86SLuiz Otavio O Souza  * Copyright (c) 2011-2012 Stefan Bethke.
6b9f07b86SLuiz Otavio O Souza  * Copyright (c) 2012 Adrian Chadd.
7b9f07b86SLuiz Otavio O Souza  * All rights reserved.
8b9f07b86SLuiz Otavio O Souza  *
9b9f07b86SLuiz Otavio O Souza  * Redistribution and use in source and binary forms, with or without
10b9f07b86SLuiz Otavio O Souza  * modification, are permitted provided that the following conditions
11b9f07b86SLuiz Otavio O Souza  * are met:
12b9f07b86SLuiz Otavio O Souza  * 1. Redistributions of source code must retain the above copyright
13b9f07b86SLuiz Otavio O Souza  *    notice, this list of conditions and the following disclaimer.
14b9f07b86SLuiz Otavio O Souza  * 2. Redistributions in binary form must reproduce the above copyright
15b9f07b86SLuiz Otavio O Souza  *    notice, this list of conditions and the following disclaimer in the
16b9f07b86SLuiz Otavio O Souza  *    documentation and/or other materials provided with the distribution.
17b9f07b86SLuiz Otavio O Souza  *
18b9f07b86SLuiz Otavio O Souza  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19b9f07b86SLuiz Otavio O Souza  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20b9f07b86SLuiz Otavio O Souza  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21b9f07b86SLuiz Otavio O Souza  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22b9f07b86SLuiz Otavio O Souza  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23b9f07b86SLuiz Otavio O Souza  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24b9f07b86SLuiz Otavio O Souza  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25b9f07b86SLuiz Otavio O Souza  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26b9f07b86SLuiz Otavio O Souza  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27b9f07b86SLuiz Otavio O Souza  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28b9f07b86SLuiz Otavio O Souza  * SUCH DAMAGE.
29b9f07b86SLuiz Otavio O Souza  *
30b9f07b86SLuiz Otavio O Souza  * $FreeBSD$
31b9f07b86SLuiz Otavio O Souza  */
32b9f07b86SLuiz Otavio O Souza 
33b9f07b86SLuiz Otavio O Souza #include <sys/param.h>
34b9f07b86SLuiz Otavio O Souza #include <sys/bus.h>
35b9f07b86SLuiz Otavio O Souza #include <sys/errno.h>
36104dc214SGleb Smirnoff #include <sys/lock.h>
37b9f07b86SLuiz Otavio O Souza #include <sys/kernel.h>
38104dc214SGleb Smirnoff #include <sys/mutex.h>
39b9f07b86SLuiz Otavio O Souza #include <sys/systm.h>
40b9f07b86SLuiz Otavio O Souza #include <sys/socket.h>
41b9f07b86SLuiz Otavio O Souza 
42b9f07b86SLuiz Otavio O Souza #include <net/if.h>
43b9f07b86SLuiz Otavio O Souza #include <dev/mii/mii.h>
44b9f07b86SLuiz Otavio O Souza 
45b9f07b86SLuiz Otavio O Souza #include <dev/etherswitch/etherswitch.h>
46b9f07b86SLuiz Otavio O Souza #include <dev/etherswitch/arswitch/arswitchreg.h>
47b9f07b86SLuiz Otavio O Souza #include <dev/etherswitch/arswitch/arswitchvar.h>
48b9f07b86SLuiz Otavio O Souza #include <dev/etherswitch/arswitch/arswitch_reg.h>
49b9f07b86SLuiz Otavio O Souza #include <dev/etherswitch/arswitch/arswitch_vlans.h>
50b9f07b86SLuiz Otavio O Souza 
51b9f07b86SLuiz Otavio O Souza #include "mdio_if.h"
52b9f07b86SLuiz Otavio O Souza #include "miibus_if.h"
53b9f07b86SLuiz Otavio O Souza #include "etherswitch_if.h"
54b9f07b86SLuiz Otavio O Souza 
55db4ff7dbSAdrian Chadd /*
56db4ff7dbSAdrian Chadd  * XXX TODO: teach about the AR934x SoC switch
57db4ff7dbSAdrian Chadd  */
58db4ff7dbSAdrian Chadd 
59b9f07b86SLuiz Otavio O Souza static int
60f35f94f4SAdrian Chadd ar8xxx_vlan_op(struct arswitch_softc *sc, uint32_t op, uint32_t vid,
61b9f07b86SLuiz Otavio O Souza 	uint32_t data)
62b9f07b86SLuiz Otavio O Souza {
63b9f07b86SLuiz Otavio O Souza 	int err;
64b9f07b86SLuiz Otavio O Souza 
65b9f07b86SLuiz Otavio O Souza 	if (arswitch_waitreg(sc->sc_dev, AR8X16_REG_VLAN_CTRL,
66b9f07b86SLuiz Otavio O Souza 	    AR8X16_VLAN_ACTIVE, 0, 5))
67b9f07b86SLuiz Otavio O Souza 		return (EBUSY);
68b9f07b86SLuiz Otavio O Souza 
69b9f07b86SLuiz Otavio O Souza 	/* Load the vlan data if needed. */
70b9f07b86SLuiz Otavio O Souza 	if (op == AR8X16_VLAN_OP_LOAD) {
71b9f07b86SLuiz Otavio O Souza 		err = arswitch_writereg(sc->sc_dev, AR8X16_REG_VLAN_DATA,
72b9f07b86SLuiz Otavio O Souza 		    (data & AR8X16_VLAN_MEMBER) | AR8X16_VLAN_VALID);
73b9f07b86SLuiz Otavio O Souza 		if (err)
74b9f07b86SLuiz Otavio O Souza 			return (err);
75b9f07b86SLuiz Otavio O Souza 	}
76b9f07b86SLuiz Otavio O Souza 
77b9f07b86SLuiz Otavio O Souza 	if (vid != 0)
78b9f07b86SLuiz Otavio O Souza 		op |= ((vid & ETHERSWITCH_VID_MASK) << AR8X16_VLAN_VID_SHIFT);
79b9f07b86SLuiz Otavio O Souza 	op |= AR8X16_VLAN_ACTIVE;
80b9f07b86SLuiz Otavio O Souza 	arswitch_writereg(sc->sc_dev, AR8X16_REG_VLAN_CTRL, op);
81b9f07b86SLuiz Otavio O Souza 
82b9f07b86SLuiz Otavio O Souza 	/* Wait for command processing. */
83b9f07b86SLuiz Otavio O Souza 	if (arswitch_waitreg(sc->sc_dev, AR8X16_REG_VLAN_CTRL,
84b9f07b86SLuiz Otavio O Souza 	    AR8X16_VLAN_ACTIVE, 0, 5))
85b9f07b86SLuiz Otavio O Souza 		return (EBUSY);
86b9f07b86SLuiz Otavio O Souza 
87b9f07b86SLuiz Otavio O Souza 	return (0);
88b9f07b86SLuiz Otavio O Souza }
89b9f07b86SLuiz Otavio O Souza 
90f35f94f4SAdrian Chadd int
91f35f94f4SAdrian Chadd ar8xxx_flush_dot1q_vlan(struct arswitch_softc *sc)
92b9f07b86SLuiz Otavio O Souza {
93b9f07b86SLuiz Otavio O Souza 
94b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
95f35f94f4SAdrian Chadd 	return (ar8xxx_vlan_op(sc, AR8X16_VLAN_OP_FLUSH, 0, 0));
96b9f07b86SLuiz Otavio O Souza }
97b9f07b86SLuiz Otavio O Souza 
98f35f94f4SAdrian Chadd int
99f35f94f4SAdrian Chadd ar8xxx_purge_dot1q_vlan(struct arswitch_softc *sc, int vid)
100b9f07b86SLuiz Otavio O Souza {
101b9f07b86SLuiz Otavio O Souza 
102b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
103f35f94f4SAdrian Chadd 	return (ar8xxx_vlan_op(sc, AR8X16_VLAN_OP_PURGE, vid, 0));
104b9f07b86SLuiz Otavio O Souza }
105b9f07b86SLuiz Otavio O Souza 
106749cac13SAdrian Chadd int
107036e1c76SAdrian Chadd ar8xxx_get_dot1q_vlan(struct arswitch_softc *sc, uint32_t *ports,
108036e1c76SAdrian Chadd     uint32_t *untagged_ports, int vid)
109b9f07b86SLuiz Otavio O Souza {
110b9f07b86SLuiz Otavio O Souza 	uint32_t reg;
111b9f07b86SLuiz Otavio O Souza 	int err;
112b9f07b86SLuiz Otavio O Souza 
113b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
114f35f94f4SAdrian Chadd 	err = ar8xxx_vlan_op(sc, AR8X16_VLAN_OP_GET, vid, 0);
115b9f07b86SLuiz Otavio O Souza 	if (err)
116b9f07b86SLuiz Otavio O Souza 		return (err);
117b9f07b86SLuiz Otavio O Souza 
118b9f07b86SLuiz Otavio O Souza 	reg = arswitch_readreg(sc->sc_dev, AR8X16_REG_VLAN_DATA);
119b9f07b86SLuiz Otavio O Souza 	if ((reg & AR8X16_VLAN_VALID) == 0) {
120b9f07b86SLuiz Otavio O Souza 		*ports = 0;
121b9f07b86SLuiz Otavio O Souza 		return (EINVAL);
122b9f07b86SLuiz Otavio O Souza 	}
123b9f07b86SLuiz Otavio O Souza 	reg &= ((1 << (sc->numphys + 1)) - 1);
124b9f07b86SLuiz Otavio O Souza 	*ports = reg;
125036e1c76SAdrian Chadd 	*untagged_ports = reg;
126b9f07b86SLuiz Otavio O Souza 	return (0);
127b9f07b86SLuiz Otavio O Souza }
128b9f07b86SLuiz Otavio O Souza 
129749cac13SAdrian Chadd int
130036e1c76SAdrian Chadd ar8xxx_set_dot1q_vlan(struct arswitch_softc *sc, uint32_t ports,
131036e1c76SAdrian Chadd     uint32_t untagged_ports, int vid)
132b9f07b86SLuiz Otavio O Souza {
133b9f07b86SLuiz Otavio O Souza 	int err;
134b9f07b86SLuiz Otavio O Souza 
135b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
136f35f94f4SAdrian Chadd 	err = ar8xxx_vlan_op(sc, AR8X16_VLAN_OP_LOAD, vid, ports);
137b9f07b86SLuiz Otavio O Souza 	if (err)
138b9f07b86SLuiz Otavio O Souza 		return (err);
139b9f07b86SLuiz Otavio O Souza 	return (0);
140b9f07b86SLuiz Otavio O Souza }
141b9f07b86SLuiz Otavio O Souza 
142749cac13SAdrian Chadd int
143749cac13SAdrian Chadd ar8xxx_get_port_vlan(struct arswitch_softc *sc, uint32_t *ports, int vid)
144b9f07b86SLuiz Otavio O Souza {
145b9f07b86SLuiz Otavio O Souza 	int port;
146b9f07b86SLuiz Otavio O Souza 	uint32_t reg;
147b9f07b86SLuiz Otavio O Souza 
148b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
149b9f07b86SLuiz Otavio O Souza 	/* For port based vlans the vlanid is the same as the port index. */
150b9f07b86SLuiz Otavio O Souza 	port = vid & ETHERSWITCH_VID_MASK;
151b9f07b86SLuiz Otavio O Souza 	reg = arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_VLAN(port));
152b9f07b86SLuiz Otavio O Souza 	*ports = (reg >> AR8X16_PORT_VLAN_DEST_PORTS_SHIFT);
153b9f07b86SLuiz Otavio O Souza 	*ports &= AR8X16_VLAN_MEMBER;
154b9f07b86SLuiz Otavio O Souza 	return (0);
155b9f07b86SLuiz Otavio O Souza }
156b9f07b86SLuiz Otavio O Souza 
157749cac13SAdrian Chadd int
158749cac13SAdrian Chadd ar8xxx_set_port_vlan(struct arswitch_softc *sc, uint32_t ports, int vid)
159b9f07b86SLuiz Otavio O Souza {
160b9f07b86SLuiz Otavio O Souza 	int err, port;
161b9f07b86SLuiz Otavio O Souza 
162b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
163b9f07b86SLuiz Otavio O Souza 	/* For port based vlans the vlanid is the same as the port index. */
164b9f07b86SLuiz Otavio O Souza 	port = vid & ETHERSWITCH_VID_MASK;
165b9f07b86SLuiz Otavio O Souza 	err = arswitch_modifyreg(sc->sc_dev, AR8X16_REG_PORT_VLAN(port),
166b9f07b86SLuiz Otavio O Souza 	    AR8X16_VLAN_MEMBER << AR8X16_PORT_VLAN_DEST_PORTS_SHIFT,
167b9f07b86SLuiz Otavio O Souza 	    (ports & AR8X16_VLAN_MEMBER) << AR8X16_PORT_VLAN_DEST_PORTS_SHIFT);
168b9f07b86SLuiz Otavio O Souza 	if (err)
169b9f07b86SLuiz Otavio O Souza 		return (err);
170b9f07b86SLuiz Otavio O Souza 	return (0);
171b9f07b86SLuiz Otavio O Souza }
172b9f07b86SLuiz Otavio O Souza 
173b9f07b86SLuiz Otavio O Souza /*
174b9f07b86SLuiz Otavio O Souza  * Reset vlans to default state.
175b9f07b86SLuiz Otavio O Souza  */
176b9f07b86SLuiz Otavio O Souza void
1776dcbabd7SAdrian Chadd ar8xxx_reset_vlans(struct arswitch_softc *sc)
178b9f07b86SLuiz Otavio O Souza {
179b9f07b86SLuiz Otavio O Souza 	uint32_t ports;
180b9f07b86SLuiz Otavio O Souza 	int i, j;
181b9f07b86SLuiz Otavio O Souza 
182b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
183b9f07b86SLuiz Otavio O Souza 
184b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK(sc);
185b9f07b86SLuiz Otavio O Souza 
186b9f07b86SLuiz Otavio O Souza 	/* Reset all vlan data. */
187b9f07b86SLuiz Otavio O Souza 	memset(sc->vid, 0, sizeof(sc->vid));
188b9f07b86SLuiz Otavio O Souza 
189b9f07b86SLuiz Otavio O Souza 	/* Disable the QinQ and egress filters for all ports. */
190b9f07b86SLuiz Otavio O Souza 	for (i = 0; i <= sc->numphys; i++) {
191b9f07b86SLuiz Otavio O Souza 		if (arswitch_modifyreg(sc->sc_dev, AR8X16_REG_PORT_CTRL(i),
192b9f07b86SLuiz Otavio O Souza 		    0x3 << AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT |
193b9f07b86SLuiz Otavio O Souza 		    AR8X16_PORT_CTRL_DOUBLE_TAG, 0)) {
194b9f07b86SLuiz Otavio O Souza 			ARSWITCH_UNLOCK(sc);
195b9f07b86SLuiz Otavio O Souza 			return;
196b9f07b86SLuiz Otavio O Souza 		}
197b9f07b86SLuiz Otavio O Souza 	}
198b9f07b86SLuiz Otavio O Souza 
199f35f94f4SAdrian Chadd 	if (sc->hal.arswitch_flush_dot1q_vlan(sc)) {
200b9f07b86SLuiz Otavio O Souza 		ARSWITCH_UNLOCK(sc);
201b9f07b86SLuiz Otavio O Souza 		return;
202b9f07b86SLuiz Otavio O Souza 	}
203b9f07b86SLuiz Otavio O Souza 
204b9f07b86SLuiz Otavio O Souza 	if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
205b9f07b86SLuiz Otavio O Souza 		/*
206b9f07b86SLuiz Otavio O Souza 		 * Reset the port based vlan settings and turn on the
207b9f07b86SLuiz Otavio O Souza 		 * ingress filter for all ports.
208b9f07b86SLuiz Otavio O Souza 		 */
209b9f07b86SLuiz Otavio O Souza 		ports = 0;
210b9f07b86SLuiz Otavio O Souza 		for (i = 0; i <= sc->numphys; i++)
211b9f07b86SLuiz Otavio O Souza 			arswitch_modifyreg(sc->sc_dev,
212b9f07b86SLuiz Otavio O Souza 			    AR8X16_REG_PORT_VLAN(i),
213b9f07b86SLuiz Otavio O Souza 			    AR8X16_PORT_VLAN_MODE_MASK |
214b9f07b86SLuiz Otavio O Souza 			    AR8X16_VLAN_MEMBER <<
215b9f07b86SLuiz Otavio O Souza 			    AR8X16_PORT_VLAN_DEST_PORTS_SHIFT,
216b9f07b86SLuiz Otavio O Souza 			    AR8X16_PORT_VLAN_MODE_SECURE <<
217b9f07b86SLuiz Otavio O Souza 			    AR8X16_PORT_VLAN_MODE_SHIFT);
218b9f07b86SLuiz Otavio O Souza 
219b9f07b86SLuiz Otavio O Souza 		/*
220b9f07b86SLuiz Otavio O Souza 		 * Setup vlan 1 as PVID for all switch ports.  Add all ports
221b9f07b86SLuiz Otavio O Souza 		 * as members of vlan 1.
222b9f07b86SLuiz Otavio O Souza 		 */
223b9f07b86SLuiz Otavio O Souza 		sc->vid[0] = 1;
224b9f07b86SLuiz Otavio O Souza 		/* Set PVID for everyone. */
225b9f07b86SLuiz Otavio O Souza 		for (i = 0; i <= sc->numphys; i++)
2266dcbabd7SAdrian Chadd 			sc->hal.arswitch_vlan_set_pvid(sc, i, sc->vid[0]);
227b9f07b86SLuiz Otavio O Souza 		ports = 0;
228b9f07b86SLuiz Otavio O Souza 		for (i = 0; i <= sc->numphys; i++)
229b9f07b86SLuiz Otavio O Souza 			ports |= (1 << i);
230036e1c76SAdrian Chadd 		sc->hal.arswitch_set_dot1q_vlan(sc, ports, sc->vid[0], sc->vid[0]);
231b9f07b86SLuiz Otavio O Souza 		sc->vid[0] |= ETHERSWITCH_VID_VALID;
232b9f07b86SLuiz Otavio O Souza 	} else if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) {
233b9f07b86SLuiz Otavio O Souza 		/* Initialize the port based vlans. */
234b9f07b86SLuiz Otavio O Souza 		for (i = 0; i <= sc->numphys; i++) {
235b9f07b86SLuiz Otavio O Souza 			sc->vid[i] = i | ETHERSWITCH_VID_VALID;
236b9f07b86SLuiz Otavio O Souza 			ports = 0;
237b9f07b86SLuiz Otavio O Souza 			for (j = 0; j <= sc->numphys; j++)
238b9f07b86SLuiz Otavio O Souza 				ports |= (1 << j);
239b9f07b86SLuiz Otavio O Souza 			arswitch_modifyreg(sc->sc_dev,
240b9f07b86SLuiz Otavio O Souza 			    AR8X16_REG_PORT_VLAN(i),
241b9f07b86SLuiz Otavio O Souza 			    AR8X16_PORT_VLAN_MODE_MASK |
242b9f07b86SLuiz Otavio O Souza 			    AR8X16_VLAN_MEMBER <<
243b9f07b86SLuiz Otavio O Souza 			    AR8X16_PORT_VLAN_DEST_PORTS_SHIFT,
244b9f07b86SLuiz Otavio O Souza 			    ports << AR8X16_PORT_VLAN_DEST_PORTS_SHIFT |
245b9f07b86SLuiz Otavio O Souza 			    AR8X16_PORT_VLAN_MODE_SECURE <<
246b9f07b86SLuiz Otavio O Souza 			    AR8X16_PORT_VLAN_MODE_PORT_ONLY);
247036e1c76SAdrian Chadd 			    /* XXX TODO: SECURE / PORT_ONLY is wrong? */
248b9f07b86SLuiz Otavio O Souza 		}
249b9f07b86SLuiz Otavio O Souza 	} else {
250b9f07b86SLuiz Otavio O Souza 		/* Disable the ingress filter and get everyone on all vlans. */
251b9f07b86SLuiz Otavio O Souza 		for (i = 0; i <= sc->numphys; i++)
252b9f07b86SLuiz Otavio O Souza 			arswitch_modifyreg(sc->sc_dev,
253b9f07b86SLuiz Otavio O Souza 			    AR8X16_REG_PORT_VLAN(i),
254b9f07b86SLuiz Otavio O Souza 			    AR8X16_PORT_VLAN_MODE_MASK |
255b9f07b86SLuiz Otavio O Souza 			    AR8X16_VLAN_MEMBER <<
256b9f07b86SLuiz Otavio O Souza 			    AR8X16_PORT_VLAN_DEST_PORTS_SHIFT,
257b9f07b86SLuiz Otavio O Souza 			    AR8X16_VLAN_MEMBER <<
258b9f07b86SLuiz Otavio O Souza 			    AR8X16_PORT_VLAN_DEST_PORTS_SHIFT |
259b9f07b86SLuiz Otavio O Souza 			    AR8X16_PORT_VLAN_MODE_SECURE <<
260b9f07b86SLuiz Otavio O Souza 			    AR8X16_PORT_VLAN_MODE_PORT_ONLY);
261b9f07b86SLuiz Otavio O Souza 	}
262b9f07b86SLuiz Otavio O Souza 	ARSWITCH_UNLOCK(sc);
263b9f07b86SLuiz Otavio O Souza }
264b9f07b86SLuiz Otavio O Souza 
265b9f07b86SLuiz Otavio O Souza int
2666dcbabd7SAdrian Chadd ar8xxx_getvgroup(struct arswitch_softc *sc, etherswitch_vlangroup_t *vg)
267b9f07b86SLuiz Otavio O Souza {
268b9f07b86SLuiz Otavio O Souza 	int err;
269b9f07b86SLuiz Otavio O Souza 
270b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
271b9f07b86SLuiz Otavio O Souza 
272b9f07b86SLuiz Otavio O Souza 	if (vg->es_vlangroup > sc->info.es_nvlangroups)
273b9f07b86SLuiz Otavio O Souza 		return (EINVAL);
274b9f07b86SLuiz Otavio O Souza 
275b9f07b86SLuiz Otavio O Souza 	/* Reset the members ports. */
276b9f07b86SLuiz Otavio O Souza 	vg->es_untagged_ports = 0;
277b9f07b86SLuiz Otavio O Souza 	vg->es_member_ports = 0;
278b9f07b86SLuiz Otavio O Souza 
279b9f07b86SLuiz Otavio O Souza 	/* Not supported. */
280b9f07b86SLuiz Otavio O Souza 	vg->es_fid = 0;
281b9f07b86SLuiz Otavio O Souza 
282b9f07b86SLuiz Otavio O Souza 	/* Vlan ID. */
283b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK(sc);
284b9f07b86SLuiz Otavio O Souza 	vg->es_vid = sc->vid[vg->es_vlangroup];
285b9f07b86SLuiz Otavio O Souza 	if ((vg->es_vid & ETHERSWITCH_VID_VALID) == 0) {
286b9f07b86SLuiz Otavio O Souza 		ARSWITCH_UNLOCK(sc);
287b9f07b86SLuiz Otavio O Souza 		return (0);
288b9f07b86SLuiz Otavio O Souza 	}
289b9f07b86SLuiz Otavio O Souza 
290b9f07b86SLuiz Otavio O Souza 	/* Member Ports. */
291b9f07b86SLuiz Otavio O Souza 	switch (sc->vlan_mode) {
292b9f07b86SLuiz Otavio O Souza 	case ETHERSWITCH_VLAN_DOT1Q:
293749cac13SAdrian Chadd 		err = sc->hal.arswitch_get_dot1q_vlan(sc, &vg->es_member_ports,
294036e1c76SAdrian Chadd 		    &vg->es_untagged_ports,
295b9f07b86SLuiz Otavio O Souza 		    vg->es_vid);
296b9f07b86SLuiz Otavio O Souza 		break;
297b9f07b86SLuiz Otavio O Souza 	case ETHERSWITCH_VLAN_PORT:
298749cac13SAdrian Chadd 		err = sc->hal.arswitch_get_port_vlan(sc, &vg->es_member_ports,
299b9f07b86SLuiz Otavio O Souza 		    vg->es_vid);
300036e1c76SAdrian Chadd 		vg->es_untagged_ports = vg->es_member_ports;
301b9f07b86SLuiz Otavio O Souza 		break;
302b9f07b86SLuiz Otavio O Souza 	default:
303b9f07b86SLuiz Otavio O Souza 		vg->es_member_ports = 0;
304036e1c76SAdrian Chadd 		vg->es_untagged_ports = 0;
305b9f07b86SLuiz Otavio O Souza 		err = -1;
306b9f07b86SLuiz Otavio O Souza 	}
307b9f07b86SLuiz Otavio O Souza 	ARSWITCH_UNLOCK(sc);
308036e1c76SAdrian Chadd 
309b9f07b86SLuiz Otavio O Souza 	return (err);
310b9f07b86SLuiz Otavio O Souza }
311b9f07b86SLuiz Otavio O Souza 
312b9f07b86SLuiz Otavio O Souza int
3136dcbabd7SAdrian Chadd ar8xxx_setvgroup(struct arswitch_softc *sc, etherswitch_vlangroup_t *vg)
314b9f07b86SLuiz Otavio O Souza {
315b9f07b86SLuiz Otavio O Souza 	int err, vid;
316b9f07b86SLuiz Otavio O Souza 
317b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
318b9f07b86SLuiz Otavio O Souza 
319b9f07b86SLuiz Otavio O Souza 	/* Check VLAN mode. */
320b9f07b86SLuiz Otavio O Souza 	if (sc->vlan_mode == 0)
321b9f07b86SLuiz Otavio O Souza 		return (EINVAL);
322b9f07b86SLuiz Otavio O Souza 
323b9f07b86SLuiz Otavio O Souza 	/*
324b9f07b86SLuiz Otavio O Souza 	 * Check if we are changing the vlanid for an already used vtu entry.
325b9f07b86SLuiz Otavio O Souza 	 * Then purge the entry first.
326b9f07b86SLuiz Otavio O Souza 	 */
327b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK(sc);
328b9f07b86SLuiz Otavio O Souza 	vid = sc->vid[vg->es_vlangroup];
329b9f07b86SLuiz Otavio O Souza 	if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q &&
330b9f07b86SLuiz Otavio O Souza 	    (vid & ETHERSWITCH_VID_VALID) != 0 &&
331b9f07b86SLuiz Otavio O Souza 	    (vid & ETHERSWITCH_VID_MASK) !=
332b9f07b86SLuiz Otavio O Souza 	    (vg->es_vid & ETHERSWITCH_VID_MASK)) {
333f35f94f4SAdrian Chadd 		err = sc->hal.arswitch_purge_dot1q_vlan(sc, vid);
334b9f07b86SLuiz Otavio O Souza 		if (err) {
335b9f07b86SLuiz Otavio O Souza 			ARSWITCH_UNLOCK(sc);
336b9f07b86SLuiz Otavio O Souza 			return (err);
337b9f07b86SLuiz Otavio O Souza 		}
338b9f07b86SLuiz Otavio O Souza 	}
339b9f07b86SLuiz Otavio O Souza 
340b9f07b86SLuiz Otavio O Souza 	/* Vlan ID. */
341b9f07b86SLuiz Otavio O Souza 	if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
342b9f07b86SLuiz Otavio O Souza 		sc->vid[vg->es_vlangroup] = vg->es_vid & ETHERSWITCH_VID_MASK;
343b9f07b86SLuiz Otavio O Souza 		/* Setting the vlanid to zero disables the vlangroup. */
344b9f07b86SLuiz Otavio O Souza 		if (sc->vid[vg->es_vlangroup] == 0) {
345b9f07b86SLuiz Otavio O Souza 			ARSWITCH_UNLOCK(sc);
346b9f07b86SLuiz Otavio O Souza 			return (0);
347b9f07b86SLuiz Otavio O Souza 		}
348b9f07b86SLuiz Otavio O Souza 		sc->vid[vg->es_vlangroup] |= ETHERSWITCH_VID_VALID;
349b9f07b86SLuiz Otavio O Souza 		vid = sc->vid[vg->es_vlangroup];
350b9f07b86SLuiz Otavio O Souza 	}
351b9f07b86SLuiz Otavio O Souza 
352b9f07b86SLuiz Otavio O Souza 	/* Member Ports. */
353b9f07b86SLuiz Otavio O Souza 	switch (sc->vlan_mode) {
354b9f07b86SLuiz Otavio O Souza 	case ETHERSWITCH_VLAN_DOT1Q:
355036e1c76SAdrian Chadd 		err = sc->hal.arswitch_set_dot1q_vlan(sc, vg->es_member_ports,
356036e1c76SAdrian Chadd 		    vg->es_untagged_ports, vid);
357b9f07b86SLuiz Otavio O Souza 		break;
358b9f07b86SLuiz Otavio O Souza 	case ETHERSWITCH_VLAN_PORT:
359749cac13SAdrian Chadd 		err = sc->hal.arswitch_set_port_vlan(sc, vg->es_member_ports, vid);
360b9f07b86SLuiz Otavio O Souza 		break;
361b9f07b86SLuiz Otavio O Souza 	default:
362b9f07b86SLuiz Otavio O Souza 		err = -1;
363b9f07b86SLuiz Otavio O Souza 	}
364b9f07b86SLuiz Otavio O Souza 	ARSWITCH_UNLOCK(sc);
365b9f07b86SLuiz Otavio O Souza 	return (err);
366b9f07b86SLuiz Otavio O Souza }
367b9f07b86SLuiz Otavio O Souza 
368b9f07b86SLuiz Otavio O Souza int
3696dcbabd7SAdrian Chadd ar8xxx_get_pvid(struct arswitch_softc *sc, int port, int *pvid)
370b9f07b86SLuiz Otavio O Souza {
371b9f07b86SLuiz Otavio O Souza 	uint32_t reg;
372b9f07b86SLuiz Otavio O Souza 
373b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
374b9f07b86SLuiz Otavio O Souza 	reg = arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_VLAN(port));
375b9f07b86SLuiz Otavio O Souza 	*pvid = reg & 0xfff;
376b9f07b86SLuiz Otavio O Souza 	return (0);
377b9f07b86SLuiz Otavio O Souza }
378b9f07b86SLuiz Otavio O Souza 
379b9f07b86SLuiz Otavio O Souza int
3806dcbabd7SAdrian Chadd ar8xxx_set_pvid(struct arswitch_softc *sc, int port, int pvid)
381b9f07b86SLuiz Otavio O Souza {
382b9f07b86SLuiz Otavio O Souza 
383b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
384b9f07b86SLuiz Otavio O Souza 	return (arswitch_modifyreg(sc->sc_dev,
385b9f07b86SLuiz Otavio O Souza 	    AR8X16_REG_PORT_VLAN(port), 0xfff, pvid));
386b9f07b86SLuiz Otavio O Souza }
387