xref: /freebsd/sys/dev/etherswitch/arswitch/arswitch_vlans.c (revision 95ee2897e98f5d444f26ed2334cc7c439f9c16c6)
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 
31b9f07b86SLuiz Otavio O Souza #include <sys/param.h>
32b9f07b86SLuiz Otavio O Souza #include <sys/bus.h>
33b9f07b86SLuiz Otavio O Souza #include <sys/errno.h>
34104dc214SGleb Smirnoff #include <sys/lock.h>
35b9f07b86SLuiz Otavio O Souza #include <sys/kernel.h>
36104dc214SGleb Smirnoff #include <sys/mutex.h>
37b9f07b86SLuiz Otavio O Souza #include <sys/systm.h>
38b9f07b86SLuiz Otavio O Souza #include <sys/socket.h>
39b9f07b86SLuiz Otavio O Souza 
40b9f07b86SLuiz Otavio O Souza #include <net/if.h>
41b9f07b86SLuiz Otavio O Souza #include <dev/mii/mii.h>
42b9f07b86SLuiz Otavio O Souza 
43b9f07b86SLuiz Otavio O Souza #include <dev/etherswitch/etherswitch.h>
44b9f07b86SLuiz Otavio O Souza #include <dev/etherswitch/arswitch/arswitchreg.h>
45b9f07b86SLuiz Otavio O Souza #include <dev/etherswitch/arswitch/arswitchvar.h>
46b9f07b86SLuiz Otavio O Souza #include <dev/etherswitch/arswitch/arswitch_reg.h>
47b9f07b86SLuiz Otavio O Souza #include <dev/etherswitch/arswitch/arswitch_vlans.h>
48b9f07b86SLuiz Otavio O Souza 
49b9f07b86SLuiz Otavio O Souza #include "mdio_if.h"
50b9f07b86SLuiz Otavio O Souza #include "miibus_if.h"
51b9f07b86SLuiz Otavio O Souza #include "etherswitch_if.h"
52b9f07b86SLuiz Otavio O Souza 
53db4ff7dbSAdrian Chadd /*
54db4ff7dbSAdrian Chadd  * XXX TODO: teach about the AR934x SoC switch
55db4ff7dbSAdrian Chadd  */
56db4ff7dbSAdrian Chadd 
57b9f07b86SLuiz Otavio O Souza static int
ar8xxx_vlan_op(struct arswitch_softc * sc,uint32_t op,uint32_t vid,uint32_t data)58f35f94f4SAdrian Chadd ar8xxx_vlan_op(struct arswitch_softc *sc, uint32_t op, uint32_t vid,
59b9f07b86SLuiz Otavio O Souza 	uint32_t data)
60b9f07b86SLuiz Otavio O Souza {
61b9f07b86SLuiz Otavio O Souza 	int err;
62b9f07b86SLuiz Otavio O Souza 
63b9f07b86SLuiz Otavio O Souza 	if (arswitch_waitreg(sc->sc_dev, AR8X16_REG_VLAN_CTRL,
64b9f07b86SLuiz Otavio O Souza 	    AR8X16_VLAN_ACTIVE, 0, 5))
65b9f07b86SLuiz Otavio O Souza 		return (EBUSY);
66b9f07b86SLuiz Otavio O Souza 
67b9f07b86SLuiz Otavio O Souza 	/* Load the vlan data if needed. */
68b9f07b86SLuiz Otavio O Souza 	if (op == AR8X16_VLAN_OP_LOAD) {
69b9f07b86SLuiz Otavio O Souza 		err = arswitch_writereg(sc->sc_dev, AR8X16_REG_VLAN_DATA,
70b9f07b86SLuiz Otavio O Souza 		    (data & AR8X16_VLAN_MEMBER) | AR8X16_VLAN_VALID);
71b9f07b86SLuiz Otavio O Souza 		if (err)
72b9f07b86SLuiz Otavio O Souza 			return (err);
73b9f07b86SLuiz Otavio O Souza 	}
74b9f07b86SLuiz Otavio O Souza 
75b9f07b86SLuiz Otavio O Souza 	if (vid != 0)
76b9f07b86SLuiz Otavio O Souza 		op |= ((vid & ETHERSWITCH_VID_MASK) << AR8X16_VLAN_VID_SHIFT);
77b9f07b86SLuiz Otavio O Souza 	op |= AR8X16_VLAN_ACTIVE;
78b9f07b86SLuiz Otavio O Souza 	arswitch_writereg(sc->sc_dev, AR8X16_REG_VLAN_CTRL, op);
79b9f07b86SLuiz Otavio O Souza 
80b9f07b86SLuiz Otavio O Souza 	/* Wait for command processing. */
81b9f07b86SLuiz Otavio O Souza 	if (arswitch_waitreg(sc->sc_dev, AR8X16_REG_VLAN_CTRL,
82b9f07b86SLuiz Otavio O Souza 	    AR8X16_VLAN_ACTIVE, 0, 5))
83b9f07b86SLuiz Otavio O Souza 		return (EBUSY);
84b9f07b86SLuiz Otavio O Souza 
85b9f07b86SLuiz Otavio O Souza 	return (0);
86b9f07b86SLuiz Otavio O Souza }
87b9f07b86SLuiz Otavio O Souza 
88f35f94f4SAdrian Chadd int
ar8xxx_flush_dot1q_vlan(struct arswitch_softc * sc)89f35f94f4SAdrian Chadd ar8xxx_flush_dot1q_vlan(struct arswitch_softc *sc)
90b9f07b86SLuiz Otavio O Souza {
91b9f07b86SLuiz Otavio O Souza 
92b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
93f35f94f4SAdrian Chadd 	return (ar8xxx_vlan_op(sc, AR8X16_VLAN_OP_FLUSH, 0, 0));
94b9f07b86SLuiz Otavio O Souza }
95b9f07b86SLuiz Otavio O Souza 
96f35f94f4SAdrian Chadd int
ar8xxx_purge_dot1q_vlan(struct arswitch_softc * sc,int vid)97f35f94f4SAdrian Chadd ar8xxx_purge_dot1q_vlan(struct arswitch_softc *sc, int vid)
98b9f07b86SLuiz Otavio O Souza {
99b9f07b86SLuiz Otavio O Souza 
100b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
101f35f94f4SAdrian Chadd 	return (ar8xxx_vlan_op(sc, AR8X16_VLAN_OP_PURGE, vid, 0));
102b9f07b86SLuiz Otavio O Souza }
103b9f07b86SLuiz Otavio O Souza 
104749cac13SAdrian Chadd int
ar8xxx_get_dot1q_vlan(struct arswitch_softc * sc,uint32_t * ports,uint32_t * untagged_ports,int vid)105036e1c76SAdrian Chadd ar8xxx_get_dot1q_vlan(struct arswitch_softc *sc, uint32_t *ports,
106036e1c76SAdrian Chadd     uint32_t *untagged_ports, int vid)
107b9f07b86SLuiz Otavio O Souza {
108b9f07b86SLuiz Otavio O Souza 	uint32_t reg;
109b9f07b86SLuiz Otavio O Souza 	int err;
110b9f07b86SLuiz Otavio O Souza 
111b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
112f35f94f4SAdrian Chadd 	err = ar8xxx_vlan_op(sc, AR8X16_VLAN_OP_GET, vid, 0);
113b9f07b86SLuiz Otavio O Souza 	if (err)
114b9f07b86SLuiz Otavio O Souza 		return (err);
115b9f07b86SLuiz Otavio O Souza 
116b9f07b86SLuiz Otavio O Souza 	reg = arswitch_readreg(sc->sc_dev, AR8X16_REG_VLAN_DATA);
117b9f07b86SLuiz Otavio O Souza 	if ((reg & AR8X16_VLAN_VALID) == 0) {
118b9f07b86SLuiz Otavio O Souza 		*ports = 0;
119b9f07b86SLuiz Otavio O Souza 		return (EINVAL);
120b9f07b86SLuiz Otavio O Souza 	}
121b9f07b86SLuiz Otavio O Souza 	reg &= ((1 << (sc->numphys + 1)) - 1);
122b9f07b86SLuiz Otavio O Souza 	*ports = reg;
123036e1c76SAdrian Chadd 	*untagged_ports = reg;
124b9f07b86SLuiz Otavio O Souza 	return (0);
125b9f07b86SLuiz Otavio O Souza }
126b9f07b86SLuiz Otavio O Souza 
127749cac13SAdrian Chadd int
ar8xxx_set_dot1q_vlan(struct arswitch_softc * sc,uint32_t ports,uint32_t untagged_ports,int vid)128036e1c76SAdrian Chadd ar8xxx_set_dot1q_vlan(struct arswitch_softc *sc, uint32_t ports,
129036e1c76SAdrian Chadd     uint32_t untagged_ports, int vid)
130b9f07b86SLuiz Otavio O Souza {
131b9f07b86SLuiz Otavio O Souza 	int err;
132b9f07b86SLuiz Otavio O Souza 
133b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
134f35f94f4SAdrian Chadd 	err = ar8xxx_vlan_op(sc, AR8X16_VLAN_OP_LOAD, vid, ports);
135b9f07b86SLuiz Otavio O Souza 	if (err)
136b9f07b86SLuiz Otavio O Souza 		return (err);
137b9f07b86SLuiz Otavio O Souza 	return (0);
138b9f07b86SLuiz Otavio O Souza }
139b9f07b86SLuiz Otavio O Souza 
140749cac13SAdrian Chadd int
ar8xxx_get_port_vlan(struct arswitch_softc * sc,uint32_t * ports,int vid)141749cac13SAdrian Chadd ar8xxx_get_port_vlan(struct arswitch_softc *sc, uint32_t *ports, int vid)
142b9f07b86SLuiz Otavio O Souza {
143b9f07b86SLuiz Otavio O Souza 	int port;
144b9f07b86SLuiz Otavio O Souza 	uint32_t reg;
145b9f07b86SLuiz Otavio O Souza 
146b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
147b9f07b86SLuiz Otavio O Souza 	/* For port based vlans the vlanid is the same as the port index. */
148b9f07b86SLuiz Otavio O Souza 	port = vid & ETHERSWITCH_VID_MASK;
149b9f07b86SLuiz Otavio O Souza 	reg = arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_VLAN(port));
150b9f07b86SLuiz Otavio O Souza 	*ports = (reg >> AR8X16_PORT_VLAN_DEST_PORTS_SHIFT);
151b9f07b86SLuiz Otavio O Souza 	*ports &= AR8X16_VLAN_MEMBER;
152b9f07b86SLuiz Otavio O Souza 	return (0);
153b9f07b86SLuiz Otavio O Souza }
154b9f07b86SLuiz Otavio O Souza 
155749cac13SAdrian Chadd int
ar8xxx_set_port_vlan(struct arswitch_softc * sc,uint32_t ports,int vid)156749cac13SAdrian Chadd ar8xxx_set_port_vlan(struct arswitch_softc *sc, uint32_t ports, int vid)
157b9f07b86SLuiz Otavio O Souza {
158b9f07b86SLuiz Otavio O Souza 	int err, port;
159b9f07b86SLuiz Otavio O Souza 
160b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
161b9f07b86SLuiz Otavio O Souza 	/* For port based vlans the vlanid is the same as the port index. */
162b9f07b86SLuiz Otavio O Souza 	port = vid & ETHERSWITCH_VID_MASK;
163b9f07b86SLuiz Otavio O Souza 	err = arswitch_modifyreg(sc->sc_dev, AR8X16_REG_PORT_VLAN(port),
164b9f07b86SLuiz Otavio O Souza 	    AR8X16_VLAN_MEMBER << AR8X16_PORT_VLAN_DEST_PORTS_SHIFT,
165b9f07b86SLuiz Otavio O Souza 	    (ports & AR8X16_VLAN_MEMBER) << AR8X16_PORT_VLAN_DEST_PORTS_SHIFT);
166b9f07b86SLuiz Otavio O Souza 	if (err)
167b9f07b86SLuiz Otavio O Souza 		return (err);
168b9f07b86SLuiz Otavio O Souza 	return (0);
169b9f07b86SLuiz Otavio O Souza }
170b9f07b86SLuiz Otavio O Souza 
171b9f07b86SLuiz Otavio O Souza /*
172b9f07b86SLuiz Otavio O Souza  * Reset vlans to default state.
173b9f07b86SLuiz Otavio O Souza  */
174b9f07b86SLuiz Otavio O Souza void
ar8xxx_reset_vlans(struct arswitch_softc * sc)1756dcbabd7SAdrian Chadd ar8xxx_reset_vlans(struct arswitch_softc *sc)
176b9f07b86SLuiz Otavio O Souza {
177b9f07b86SLuiz Otavio O Souza 	uint32_t ports;
178b9f07b86SLuiz Otavio O Souza 	int i, j;
179b9f07b86SLuiz Otavio O Souza 
180b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
181b9f07b86SLuiz Otavio O Souza 
182b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK(sc);
183b9f07b86SLuiz Otavio O Souza 
184b9f07b86SLuiz Otavio O Souza 	/* Reset all vlan data. */
185b9f07b86SLuiz Otavio O Souza 	memset(sc->vid, 0, sizeof(sc->vid));
186b9f07b86SLuiz Otavio O Souza 
187b9f07b86SLuiz Otavio O Souza 	/* Disable the QinQ and egress filters for all ports. */
188b9f07b86SLuiz Otavio O Souza 	for (i = 0; i <= sc->numphys; i++) {
189b9f07b86SLuiz Otavio O Souza 		if (arswitch_modifyreg(sc->sc_dev, AR8X16_REG_PORT_CTRL(i),
190b9f07b86SLuiz Otavio O Souza 		    0x3 << AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT |
191b9f07b86SLuiz Otavio O Souza 		    AR8X16_PORT_CTRL_DOUBLE_TAG, 0)) {
192b9f07b86SLuiz Otavio O Souza 			ARSWITCH_UNLOCK(sc);
193b9f07b86SLuiz Otavio O Souza 			return;
194b9f07b86SLuiz Otavio O Souza 		}
195b9f07b86SLuiz Otavio O Souza 	}
196b9f07b86SLuiz Otavio O Souza 
197f35f94f4SAdrian Chadd 	if (sc->hal.arswitch_flush_dot1q_vlan(sc)) {
198b9f07b86SLuiz Otavio O Souza 		ARSWITCH_UNLOCK(sc);
199b9f07b86SLuiz Otavio O Souza 		return;
200b9f07b86SLuiz Otavio O Souza 	}
201b9f07b86SLuiz Otavio O Souza 
202b9f07b86SLuiz Otavio O Souza 	if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
203b9f07b86SLuiz Otavio O Souza 		/*
204b9f07b86SLuiz Otavio O Souza 		 * Reset the port based vlan settings and turn on the
205b9f07b86SLuiz Otavio O Souza 		 * ingress filter for all ports.
206b9f07b86SLuiz Otavio O Souza 		 */
207b9f07b86SLuiz Otavio O Souza 		ports = 0;
208b9f07b86SLuiz Otavio O Souza 		for (i = 0; i <= sc->numphys; i++)
209b9f07b86SLuiz Otavio O Souza 			arswitch_modifyreg(sc->sc_dev,
210b9f07b86SLuiz Otavio O Souza 			    AR8X16_REG_PORT_VLAN(i),
211b9f07b86SLuiz Otavio O Souza 			    AR8X16_PORT_VLAN_MODE_MASK |
212b9f07b86SLuiz Otavio O Souza 			    AR8X16_VLAN_MEMBER <<
213b9f07b86SLuiz Otavio O Souza 			    AR8X16_PORT_VLAN_DEST_PORTS_SHIFT,
214b9f07b86SLuiz Otavio O Souza 			    AR8X16_PORT_VLAN_MODE_SECURE <<
215b9f07b86SLuiz Otavio O Souza 			    AR8X16_PORT_VLAN_MODE_SHIFT);
216b9f07b86SLuiz Otavio O Souza 
217b9f07b86SLuiz Otavio O Souza 		/*
218b9f07b86SLuiz Otavio O Souza 		 * Setup vlan 1 as PVID for all switch ports.  Add all ports
219b9f07b86SLuiz Otavio O Souza 		 * as members of vlan 1.
220b9f07b86SLuiz Otavio O Souza 		 */
221b9f07b86SLuiz Otavio O Souza 		sc->vid[0] = 1;
222b9f07b86SLuiz Otavio O Souza 		/* Set PVID for everyone. */
223b9f07b86SLuiz Otavio O Souza 		for (i = 0; i <= sc->numphys; i++)
2246dcbabd7SAdrian Chadd 			sc->hal.arswitch_vlan_set_pvid(sc, i, sc->vid[0]);
225b9f07b86SLuiz Otavio O Souza 		ports = 0;
226b9f07b86SLuiz Otavio O Souza 		for (i = 0; i <= sc->numphys; i++)
227b9f07b86SLuiz Otavio O Souza 			ports |= (1 << i);
228036e1c76SAdrian Chadd 		sc->hal.arswitch_set_dot1q_vlan(sc, ports, sc->vid[0], sc->vid[0]);
229b9f07b86SLuiz Otavio O Souza 		sc->vid[0] |= ETHERSWITCH_VID_VALID;
230b9f07b86SLuiz Otavio O Souza 	} else if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) {
231b9f07b86SLuiz Otavio O Souza 		/* Initialize the port based vlans. */
232b9f07b86SLuiz Otavio O Souza 		for (i = 0; i <= sc->numphys; i++) {
233b9f07b86SLuiz Otavio O Souza 			sc->vid[i] = i | ETHERSWITCH_VID_VALID;
234b9f07b86SLuiz Otavio O Souza 			ports = 0;
235b9f07b86SLuiz Otavio O Souza 			for (j = 0; j <= sc->numphys; j++)
236b9f07b86SLuiz Otavio O Souza 				ports |= (1 << j);
237b9f07b86SLuiz Otavio O Souza 			arswitch_modifyreg(sc->sc_dev,
238b9f07b86SLuiz Otavio O Souza 			    AR8X16_REG_PORT_VLAN(i),
239b9f07b86SLuiz Otavio O Souza 			    AR8X16_PORT_VLAN_MODE_MASK |
240b9f07b86SLuiz Otavio O Souza 			    AR8X16_VLAN_MEMBER <<
241b9f07b86SLuiz Otavio O Souza 			    AR8X16_PORT_VLAN_DEST_PORTS_SHIFT,
242b9f07b86SLuiz Otavio O Souza 			    ports << AR8X16_PORT_VLAN_DEST_PORTS_SHIFT |
243b9f07b86SLuiz Otavio O Souza 			    AR8X16_PORT_VLAN_MODE_SECURE <<
244b9f07b86SLuiz Otavio O Souza 			    AR8X16_PORT_VLAN_MODE_PORT_ONLY);
245036e1c76SAdrian Chadd 			    /* XXX TODO: SECURE / PORT_ONLY is wrong? */
246b9f07b86SLuiz Otavio O Souza 		}
247b9f07b86SLuiz Otavio O Souza 	} else {
248b9f07b86SLuiz Otavio O Souza 		/* Disable the ingress filter and get everyone on all vlans. */
249b9f07b86SLuiz Otavio O Souza 		for (i = 0; i <= sc->numphys; i++)
250b9f07b86SLuiz Otavio O Souza 			arswitch_modifyreg(sc->sc_dev,
251b9f07b86SLuiz Otavio O Souza 			    AR8X16_REG_PORT_VLAN(i),
252b9f07b86SLuiz Otavio O Souza 			    AR8X16_PORT_VLAN_MODE_MASK |
253b9f07b86SLuiz Otavio O Souza 			    AR8X16_VLAN_MEMBER <<
254b9f07b86SLuiz Otavio O Souza 			    AR8X16_PORT_VLAN_DEST_PORTS_SHIFT,
255b9f07b86SLuiz Otavio O Souza 			    AR8X16_VLAN_MEMBER <<
256b9f07b86SLuiz Otavio O Souza 			    AR8X16_PORT_VLAN_DEST_PORTS_SHIFT |
257b9f07b86SLuiz Otavio O Souza 			    AR8X16_PORT_VLAN_MODE_SECURE <<
258b9f07b86SLuiz Otavio O Souza 			    AR8X16_PORT_VLAN_MODE_PORT_ONLY);
259b9f07b86SLuiz Otavio O Souza 	}
260b9f07b86SLuiz Otavio O Souza 	ARSWITCH_UNLOCK(sc);
261b9f07b86SLuiz Otavio O Souza }
262b9f07b86SLuiz Otavio O Souza 
263b9f07b86SLuiz Otavio O Souza int
ar8xxx_getvgroup(struct arswitch_softc * sc,etherswitch_vlangroup_t * vg)2646dcbabd7SAdrian Chadd ar8xxx_getvgroup(struct arswitch_softc *sc, etherswitch_vlangroup_t *vg)
265b9f07b86SLuiz Otavio O Souza {
266b9f07b86SLuiz Otavio O Souza 	int err;
267b9f07b86SLuiz Otavio O Souza 
268b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
269b9f07b86SLuiz Otavio O Souza 
270b9f07b86SLuiz Otavio O Souza 	if (vg->es_vlangroup > sc->info.es_nvlangroups)
271b9f07b86SLuiz Otavio O Souza 		return (EINVAL);
272b9f07b86SLuiz Otavio O Souza 
273b9f07b86SLuiz Otavio O Souza 	/* Reset the members ports. */
274b9f07b86SLuiz Otavio O Souza 	vg->es_untagged_ports = 0;
275b9f07b86SLuiz Otavio O Souza 	vg->es_member_ports = 0;
276b9f07b86SLuiz Otavio O Souza 
277b9f07b86SLuiz Otavio O Souza 	/* Not supported. */
278b9f07b86SLuiz Otavio O Souza 	vg->es_fid = 0;
279b9f07b86SLuiz Otavio O Souza 
280b9f07b86SLuiz Otavio O Souza 	/* Vlan ID. */
281b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK(sc);
282b9f07b86SLuiz Otavio O Souza 	vg->es_vid = sc->vid[vg->es_vlangroup];
283b9f07b86SLuiz Otavio O Souza 	if ((vg->es_vid & ETHERSWITCH_VID_VALID) == 0) {
284b9f07b86SLuiz Otavio O Souza 		ARSWITCH_UNLOCK(sc);
285b9f07b86SLuiz Otavio O Souza 		return (0);
286b9f07b86SLuiz Otavio O Souza 	}
287b9f07b86SLuiz Otavio O Souza 
288b9f07b86SLuiz Otavio O Souza 	/* Member Ports. */
289b9f07b86SLuiz Otavio O Souza 	switch (sc->vlan_mode) {
290b9f07b86SLuiz Otavio O Souza 	case ETHERSWITCH_VLAN_DOT1Q:
291749cac13SAdrian Chadd 		err = sc->hal.arswitch_get_dot1q_vlan(sc, &vg->es_member_ports,
292036e1c76SAdrian Chadd 		    &vg->es_untagged_ports,
293b9f07b86SLuiz Otavio O Souza 		    vg->es_vid);
294b9f07b86SLuiz Otavio O Souza 		break;
295b9f07b86SLuiz Otavio O Souza 	case ETHERSWITCH_VLAN_PORT:
296749cac13SAdrian Chadd 		err = sc->hal.arswitch_get_port_vlan(sc, &vg->es_member_ports,
297b9f07b86SLuiz Otavio O Souza 		    vg->es_vid);
298036e1c76SAdrian Chadd 		vg->es_untagged_ports = vg->es_member_ports;
299b9f07b86SLuiz Otavio O Souza 		break;
300b9f07b86SLuiz Otavio O Souza 	default:
301b9f07b86SLuiz Otavio O Souza 		vg->es_member_ports = 0;
302036e1c76SAdrian Chadd 		vg->es_untagged_ports = 0;
303b9f07b86SLuiz Otavio O Souza 		err = -1;
304b9f07b86SLuiz Otavio O Souza 	}
305b9f07b86SLuiz Otavio O Souza 	ARSWITCH_UNLOCK(sc);
306036e1c76SAdrian Chadd 
307b9f07b86SLuiz Otavio O Souza 	return (err);
308b9f07b86SLuiz Otavio O Souza }
309b9f07b86SLuiz Otavio O Souza 
310b9f07b86SLuiz Otavio O Souza int
ar8xxx_setvgroup(struct arswitch_softc * sc,etherswitch_vlangroup_t * vg)3116dcbabd7SAdrian Chadd ar8xxx_setvgroup(struct arswitch_softc *sc, etherswitch_vlangroup_t *vg)
312b9f07b86SLuiz Otavio O Souza {
313b9f07b86SLuiz Otavio O Souza 	int err, vid;
314b9f07b86SLuiz Otavio O Souza 
315b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
316b9f07b86SLuiz Otavio O Souza 
317b9f07b86SLuiz Otavio O Souza 	/* Check VLAN mode. */
318b9f07b86SLuiz Otavio O Souza 	if (sc->vlan_mode == 0)
319b9f07b86SLuiz Otavio O Souza 		return (EINVAL);
320b9f07b86SLuiz Otavio O Souza 
321b9f07b86SLuiz Otavio O Souza 	/*
322b9f07b86SLuiz Otavio O Souza 	 * Check if we are changing the vlanid for an already used vtu entry.
323b9f07b86SLuiz Otavio O Souza 	 * Then purge the entry first.
324b9f07b86SLuiz Otavio O Souza 	 */
325b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK(sc);
326b9f07b86SLuiz Otavio O Souza 	vid = sc->vid[vg->es_vlangroup];
327b9f07b86SLuiz Otavio O Souza 	if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q &&
328b9f07b86SLuiz Otavio O Souza 	    (vid & ETHERSWITCH_VID_VALID) != 0 &&
329b9f07b86SLuiz Otavio O Souza 	    (vid & ETHERSWITCH_VID_MASK) !=
330b9f07b86SLuiz Otavio O Souza 	    (vg->es_vid & ETHERSWITCH_VID_MASK)) {
331f35f94f4SAdrian Chadd 		err = sc->hal.arswitch_purge_dot1q_vlan(sc, vid);
332b9f07b86SLuiz Otavio O Souza 		if (err) {
333b9f07b86SLuiz Otavio O Souza 			ARSWITCH_UNLOCK(sc);
334b9f07b86SLuiz Otavio O Souza 			return (err);
335b9f07b86SLuiz Otavio O Souza 		}
336b9f07b86SLuiz Otavio O Souza 	}
337b9f07b86SLuiz Otavio O Souza 
338b9f07b86SLuiz Otavio O Souza 	/* Vlan ID. */
339b9f07b86SLuiz Otavio O Souza 	if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
340b9f07b86SLuiz Otavio O Souza 		sc->vid[vg->es_vlangroup] = vg->es_vid & ETHERSWITCH_VID_MASK;
341b9f07b86SLuiz Otavio O Souza 		/* Setting the vlanid to zero disables the vlangroup. */
342b9f07b86SLuiz Otavio O Souza 		if (sc->vid[vg->es_vlangroup] == 0) {
343b9f07b86SLuiz Otavio O Souza 			ARSWITCH_UNLOCK(sc);
344b9f07b86SLuiz Otavio O Souza 			return (0);
345b9f07b86SLuiz Otavio O Souza 		}
346b9f07b86SLuiz Otavio O Souza 		sc->vid[vg->es_vlangroup] |= ETHERSWITCH_VID_VALID;
347b9f07b86SLuiz Otavio O Souza 		vid = sc->vid[vg->es_vlangroup];
348b9f07b86SLuiz Otavio O Souza 	}
349b9f07b86SLuiz Otavio O Souza 
350b9f07b86SLuiz Otavio O Souza 	/* Member Ports. */
351b9f07b86SLuiz Otavio O Souza 	switch (sc->vlan_mode) {
352b9f07b86SLuiz Otavio O Souza 	case ETHERSWITCH_VLAN_DOT1Q:
353036e1c76SAdrian Chadd 		err = sc->hal.arswitch_set_dot1q_vlan(sc, vg->es_member_ports,
354036e1c76SAdrian Chadd 		    vg->es_untagged_ports, vid);
355b9f07b86SLuiz Otavio O Souza 		break;
356b9f07b86SLuiz Otavio O Souza 	case ETHERSWITCH_VLAN_PORT:
357749cac13SAdrian Chadd 		err = sc->hal.arswitch_set_port_vlan(sc, vg->es_member_ports, vid);
358b9f07b86SLuiz Otavio O Souza 		break;
359b9f07b86SLuiz Otavio O Souza 	default:
360b9f07b86SLuiz Otavio O Souza 		err = -1;
361b9f07b86SLuiz Otavio O Souza 	}
362b9f07b86SLuiz Otavio O Souza 	ARSWITCH_UNLOCK(sc);
363b9f07b86SLuiz Otavio O Souza 	return (err);
364b9f07b86SLuiz Otavio O Souza }
365b9f07b86SLuiz Otavio O Souza 
366b9f07b86SLuiz Otavio O Souza int
ar8xxx_get_pvid(struct arswitch_softc * sc,int port,int * pvid)3676dcbabd7SAdrian Chadd ar8xxx_get_pvid(struct arswitch_softc *sc, int port, int *pvid)
368b9f07b86SLuiz Otavio O Souza {
369b9f07b86SLuiz Otavio O Souza 	uint32_t reg;
370b9f07b86SLuiz Otavio O Souza 
371b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
372b9f07b86SLuiz Otavio O Souza 	reg = arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_VLAN(port));
373b9f07b86SLuiz Otavio O Souza 	*pvid = reg & 0xfff;
374b9f07b86SLuiz Otavio O Souza 	return (0);
375b9f07b86SLuiz Otavio O Souza }
376b9f07b86SLuiz Otavio O Souza 
377b9f07b86SLuiz Otavio O Souza int
ar8xxx_set_pvid(struct arswitch_softc * sc,int port,int pvid)3786dcbabd7SAdrian Chadd ar8xxx_set_pvid(struct arswitch_softc *sc, int port, int pvid)
379b9f07b86SLuiz Otavio O Souza {
380b9f07b86SLuiz Otavio O Souza 
381b9f07b86SLuiz Otavio O Souza 	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
382b9f07b86SLuiz Otavio O Souza 	return (arswitch_modifyreg(sc->sc_dev,
383b9f07b86SLuiz Otavio O Souza 	    AR8X16_REG_PORT_VLAN(port), 0xfff, pvid));
384b9f07b86SLuiz Otavio O Souza }
385