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