1253572Sloos/*-
2253572Sloos * Copyright (c) 2013 Luiz Otavio O Souza.
3253572Sloos * Copyright (c) 2011-2012 Stefan Bethke.
4253572Sloos * Copyright (c) 2012 Adrian Chadd.
5253572Sloos * All rights reserved.
6253572Sloos *
7253572Sloos * Redistribution and use in source and binary forms, with or without
8253572Sloos * modification, are permitted provided that the following conditions
9253572Sloos * are met:
10253572Sloos * 1. Redistributions of source code must retain the above copyright
11253572Sloos *    notice, this list of conditions and the following disclaimer.
12253572Sloos * 2. Redistributions in binary form must reproduce the above copyright
13253572Sloos *    notice, this list of conditions and the following disclaimer in the
14253572Sloos *    documentation and/or other materials provided with the distribution.
15253572Sloos *
16253572Sloos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17253572Sloos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18253572Sloos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19253572Sloos * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20253572Sloos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21253572Sloos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22253572Sloos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23253572Sloos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24253572Sloos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25253572Sloos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26253572Sloos * SUCH DAMAGE.
27253572Sloos *
28253572Sloos * $FreeBSD$
29253572Sloos */
30253572Sloos
31253572Sloos#include <sys/param.h>
32253572Sloos#include <sys/bus.h>
33253572Sloos#include <sys/errno.h>
34253572Sloos#include <sys/kernel.h>
35253572Sloos#include <sys/systm.h>
36253572Sloos#include <sys/socket.h>
37253572Sloos
38253572Sloos#include <net/if.h>
39253572Sloos
40253572Sloos#include <dev/mii/mii.h>
41253572Sloos
42253572Sloos#include <dev/etherswitch/etherswitch.h>
43253572Sloos#include <dev/etherswitch/arswitch/arswitchreg.h>
44253572Sloos#include <dev/etherswitch/arswitch/arswitchvar.h>
45253572Sloos#include <dev/etherswitch/arswitch/arswitch_reg.h>
46253572Sloos#include <dev/etherswitch/arswitch/arswitch_vlans.h>
47253572Sloos
48253572Sloos#include "mdio_if.h"
49253572Sloos#include "miibus_if.h"
50253572Sloos#include "etherswitch_if.h"
51253572Sloos
52253572Sloosstatic int
53253572Sloosarswitch_vlan_op(struct arswitch_softc *sc, uint32_t op, uint32_t vid,
54253572Sloos	uint32_t data)
55253572Sloos{
56253572Sloos	int err;
57253572Sloos
58253572Sloos	if (arswitch_waitreg(sc->sc_dev, AR8X16_REG_VLAN_CTRL,
59253572Sloos	    AR8X16_VLAN_ACTIVE, 0, 5))
60253572Sloos		return (EBUSY);
61253572Sloos
62253572Sloos	/* Load the vlan data if needed. */
63253572Sloos	if (op == AR8X16_VLAN_OP_LOAD) {
64253572Sloos		err = arswitch_writereg(sc->sc_dev, AR8X16_REG_VLAN_DATA,
65253572Sloos		    (data & AR8X16_VLAN_MEMBER) | AR8X16_VLAN_VALID);
66253572Sloos		if (err)
67253572Sloos			return (err);
68253572Sloos	}
69253572Sloos
70253572Sloos	if (vid != 0)
71253572Sloos		op |= ((vid & ETHERSWITCH_VID_MASK) << AR8X16_VLAN_VID_SHIFT);
72253572Sloos	op |= AR8X16_VLAN_ACTIVE;
73253572Sloos	arswitch_writereg(sc->sc_dev, AR8X16_REG_VLAN_CTRL, op);
74253572Sloos
75253572Sloos	/* Wait for command processing. */
76253572Sloos	if (arswitch_waitreg(sc->sc_dev, AR8X16_REG_VLAN_CTRL,
77253572Sloos	    AR8X16_VLAN_ACTIVE, 0, 5))
78253572Sloos		return (EBUSY);
79253572Sloos
80253572Sloos	return (0);
81253572Sloos}
82253572Sloos
83253572Sloosstatic int
84253572Sloosarswitch_flush_dot1q_vlan(struct arswitch_softc *sc)
85253572Sloos{
86253572Sloos
87253572Sloos	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
88253572Sloos	return (arswitch_vlan_op(sc, AR8X16_VLAN_OP_FLUSH, 0, 0));
89253572Sloos}
90253572Sloos
91253572Sloosstatic int
92253572Sloosarswitch_purge_dot1q_vlan(struct arswitch_softc *sc, int vid)
93253572Sloos{
94253572Sloos
95253572Sloos	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
96253572Sloos	return (arswitch_vlan_op(sc, AR8X16_VLAN_OP_PURGE, vid, 0));
97253572Sloos}
98253572Sloos
99253572Sloosstatic int
100253572Sloosarswitch_get_dot1q_vlan(struct arswitch_softc *sc, uint32_t *ports, int vid)
101253572Sloos{
102253572Sloos	uint32_t reg;
103253572Sloos	int err;
104253572Sloos
105253572Sloos	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
106253572Sloos	err = arswitch_vlan_op(sc, AR8X16_VLAN_OP_GET, vid, 0);
107253572Sloos	if (err)
108253572Sloos		return (err);
109253572Sloos
110253572Sloos	reg = arswitch_readreg(sc->sc_dev, AR8X16_REG_VLAN_DATA);
111253572Sloos	if ((reg & AR8X16_VLAN_VALID) == 0) {
112253572Sloos		*ports = 0;
113253572Sloos		return (EINVAL);
114253572Sloos	}
115253572Sloos	reg &= ((1 << (sc->numphys + 1)) - 1);
116253572Sloos	*ports = reg;
117253572Sloos	return (0);
118253572Sloos}
119253572Sloos
120253572Sloosstatic int
121253572Sloosarswitch_set_dot1q_vlan(struct arswitch_softc *sc, uint32_t ports, int vid)
122253572Sloos{
123253572Sloos	int err;
124253572Sloos
125253572Sloos	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
126253572Sloos	err = arswitch_vlan_op(sc, AR8X16_VLAN_OP_LOAD, vid, ports);
127253572Sloos	if (err)
128253572Sloos		return (err);
129253572Sloos	return (0);
130253572Sloos}
131253572Sloos
132253572Sloosstatic int
133253572Sloosarswitch_get_port_vlan(struct arswitch_softc *sc, uint32_t *ports, int vid)
134253572Sloos{
135253572Sloos	int port;
136253572Sloos	uint32_t reg;
137253572Sloos
138253572Sloos	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
139253572Sloos	/* For port based vlans the vlanid is the same as the port index. */
140253572Sloos	port = vid & ETHERSWITCH_VID_MASK;
141253572Sloos	reg = arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_VLAN(port));
142253572Sloos	*ports = (reg >> AR8X16_PORT_VLAN_DEST_PORTS_SHIFT);
143253572Sloos	*ports &= AR8X16_VLAN_MEMBER;
144253572Sloos	return (0);
145253572Sloos}
146253572Sloos
147253572Sloosstatic int
148253572Sloosarswitch_set_port_vlan(struct arswitch_softc *sc, uint32_t ports, int vid)
149253572Sloos{
150253572Sloos	int err, port;
151253572Sloos
152253572Sloos	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
153253572Sloos	/* For port based vlans the vlanid is the same as the port index. */
154253572Sloos	port = vid & ETHERSWITCH_VID_MASK;
155253572Sloos	err = arswitch_modifyreg(sc->sc_dev, AR8X16_REG_PORT_VLAN(port),
156253572Sloos	    AR8X16_VLAN_MEMBER << AR8X16_PORT_VLAN_DEST_PORTS_SHIFT,
157253572Sloos	    (ports & AR8X16_VLAN_MEMBER) << AR8X16_PORT_VLAN_DEST_PORTS_SHIFT);
158253572Sloos	if (err)
159253572Sloos		return (err);
160253572Sloos	return (0);
161253572Sloos}
162253572Sloos
163253572Sloos/*
164253572Sloos * Reset vlans to default state.
165253572Sloos */
166253572Sloosvoid
167253572Sloosarswitch_reset_vlans(struct arswitch_softc *sc)
168253572Sloos{
169253572Sloos	uint32_t ports;
170253572Sloos	int i, j;
171253572Sloos
172253572Sloos	ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
173253572Sloos
174253572Sloos	ARSWITCH_LOCK(sc);
175253572Sloos
176253572Sloos	/* Reset all vlan data. */
177253572Sloos	memset(sc->vid, 0, sizeof(sc->vid));
178253572Sloos
179253572Sloos	/* Disable the QinQ and egress filters for all ports. */
180253572Sloos	for (i = 0; i <= sc->numphys; i++) {
181253572Sloos		if (arswitch_modifyreg(sc->sc_dev, AR8X16_REG_PORT_CTRL(i),
182253572Sloos		    0x3 << AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT |
183253572Sloos		    AR8X16_PORT_CTRL_DOUBLE_TAG, 0)) {
184253572Sloos			ARSWITCH_UNLOCK(sc);
185253572Sloos			return;
186253572Sloos		}
187253572Sloos	}
188253572Sloos
189253572Sloos	if (arswitch_flush_dot1q_vlan(sc)) {
190253572Sloos		ARSWITCH_UNLOCK(sc);
191253572Sloos		return;
192253572Sloos	}
193253572Sloos
194253572Sloos	if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
195253572Sloos		/*
196253572Sloos		 * Reset the port based vlan settings and turn on the
197253572Sloos		 * ingress filter for all ports.
198253572Sloos		 */
199253572Sloos		ports = 0;
200253572Sloos		for (i = 0; i <= sc->numphys; i++)
201253572Sloos			arswitch_modifyreg(sc->sc_dev,
202253572Sloos			    AR8X16_REG_PORT_VLAN(i),
203253572Sloos			    AR8X16_PORT_VLAN_MODE_MASK |
204253572Sloos			    AR8X16_VLAN_MEMBER <<
205253572Sloos			    AR8X16_PORT_VLAN_DEST_PORTS_SHIFT,
206253572Sloos			    AR8X16_PORT_VLAN_MODE_SECURE <<
207253572Sloos			    AR8X16_PORT_VLAN_MODE_SHIFT);
208253572Sloos
209253572Sloos		/*
210253572Sloos		 * Setup vlan 1 as PVID for all switch ports.  Add all ports
211253572Sloos		 * as members of vlan 1.
212253572Sloos		 */
213253572Sloos		sc->vid[0] = 1;
214253572Sloos		/* Set PVID for everyone. */
215253572Sloos		for (i = 0; i <= sc->numphys; i++)
216253572Sloos			arswitch_set_pvid(sc, i, sc->vid[0]);
217253572Sloos		ports = 0;
218253572Sloos		for (i = 0; i <= sc->numphys; i++)
219253572Sloos			ports |= (1 << i);
220253572Sloos		arswitch_set_dot1q_vlan(sc, ports, sc->vid[0]);
221253572Sloos		sc->vid[0] |= ETHERSWITCH_VID_VALID;
222253572Sloos	} else if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) {
223253572Sloos		/* Initialize the port based vlans. */
224253572Sloos		for (i = 0; i <= sc->numphys; i++) {
225253572Sloos			sc->vid[i] = i | ETHERSWITCH_VID_VALID;
226253572Sloos			ports = 0;
227253572Sloos			for (j = 0; j <= sc->numphys; j++)
228253572Sloos				ports |= (1 << j);
229253572Sloos			arswitch_modifyreg(sc->sc_dev,
230253572Sloos			    AR8X16_REG_PORT_VLAN(i),
231253572Sloos			    AR8X16_PORT_VLAN_MODE_MASK |
232253572Sloos			    AR8X16_VLAN_MEMBER <<
233253572Sloos			    AR8X16_PORT_VLAN_DEST_PORTS_SHIFT,
234253572Sloos			    ports << AR8X16_PORT_VLAN_DEST_PORTS_SHIFT |
235253572Sloos			    AR8X16_PORT_VLAN_MODE_SECURE <<
236253572Sloos			    AR8X16_PORT_VLAN_MODE_PORT_ONLY);
237253572Sloos		}
238253572Sloos	} else {
239253572Sloos		/* Disable the ingress filter and get everyone on all vlans. */
240253572Sloos		for (i = 0; i <= sc->numphys; i++)
241253572Sloos			arswitch_modifyreg(sc->sc_dev,
242253572Sloos			    AR8X16_REG_PORT_VLAN(i),
243253572Sloos			    AR8X16_PORT_VLAN_MODE_MASK |
244253572Sloos			    AR8X16_VLAN_MEMBER <<
245253572Sloos			    AR8X16_PORT_VLAN_DEST_PORTS_SHIFT,
246253572Sloos			    AR8X16_VLAN_MEMBER <<
247253572Sloos			    AR8X16_PORT_VLAN_DEST_PORTS_SHIFT |
248253572Sloos			    AR8X16_PORT_VLAN_MODE_SECURE <<
249253572Sloos			    AR8X16_PORT_VLAN_MODE_PORT_ONLY);
250253572Sloos	}
251253572Sloos	ARSWITCH_UNLOCK(sc);
252253572Sloos}
253253572Sloos
254253572Sloosint
255253572Sloosarswitch_getvgroup(device_t dev, etherswitch_vlangroup_t *vg)
256253572Sloos{
257253572Sloos	struct arswitch_softc *sc;
258253572Sloos	int err;
259253572Sloos
260253572Sloos	sc = device_get_softc(dev);
261253572Sloos	ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
262253572Sloos
263253572Sloos	if (vg->es_vlangroup > sc->info.es_nvlangroups)
264253572Sloos		return (EINVAL);
265253572Sloos
266253572Sloos	/* Reset the members ports. */
267253572Sloos	vg->es_untagged_ports = 0;
268253572Sloos	vg->es_member_ports = 0;
269253572Sloos
270253572Sloos	/* Not supported. */
271253572Sloos	vg->es_fid = 0;
272253572Sloos
273253572Sloos	/* Vlan ID. */
274253572Sloos	ARSWITCH_LOCK(sc);
275253572Sloos	vg->es_vid = sc->vid[vg->es_vlangroup];
276253572Sloos	if ((vg->es_vid & ETHERSWITCH_VID_VALID) == 0) {
277253572Sloos		ARSWITCH_UNLOCK(sc);
278253572Sloos		return (0);
279253572Sloos	}
280253572Sloos
281253572Sloos	/* Member Ports. */
282253572Sloos	switch (sc->vlan_mode) {
283253572Sloos	case ETHERSWITCH_VLAN_DOT1Q:
284253572Sloos		err = arswitch_get_dot1q_vlan(sc, &vg->es_member_ports,
285253572Sloos		    vg->es_vid);
286253572Sloos		break;
287253572Sloos	case ETHERSWITCH_VLAN_PORT:
288253572Sloos		err = arswitch_get_port_vlan(sc, &vg->es_member_ports,
289253572Sloos		    vg->es_vid);
290253572Sloos		break;
291253572Sloos	default:
292253572Sloos		vg->es_member_ports = 0;
293253572Sloos		err = -1;
294253572Sloos	}
295253572Sloos	ARSWITCH_UNLOCK(sc);
296253572Sloos	vg->es_untagged_ports = vg->es_member_ports;
297253572Sloos	return (err);
298253572Sloos}
299253572Sloos
300253572Sloosint
301253572Sloosarswitch_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
302253572Sloos{
303253572Sloos	struct arswitch_softc *sc;
304253572Sloos	int err, vid;
305253572Sloos
306253572Sloos	sc = device_get_softc(dev);
307253572Sloos	ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
308253572Sloos
309253572Sloos	/* Check VLAN mode. */
310253572Sloos	if (sc->vlan_mode == 0)
311253572Sloos		return (EINVAL);
312253572Sloos
313253572Sloos	/*
314253572Sloos	 * Check if we are changing the vlanid for an already used vtu entry.
315253572Sloos	 * Then purge the entry first.
316253572Sloos	 */
317253572Sloos	ARSWITCH_LOCK(sc);
318253572Sloos	vid = sc->vid[vg->es_vlangroup];
319253572Sloos	if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q &&
320253572Sloos	    (vid & ETHERSWITCH_VID_VALID) != 0 &&
321253572Sloos	    (vid & ETHERSWITCH_VID_MASK) !=
322253572Sloos	    (vg->es_vid & ETHERSWITCH_VID_MASK)) {
323253572Sloos		err = arswitch_purge_dot1q_vlan(sc, vid);
324253572Sloos		if (err) {
325253572Sloos			ARSWITCH_UNLOCK(sc);
326253572Sloos			return (err);
327253572Sloos		}
328253572Sloos	}
329253572Sloos
330253572Sloos	/* Vlan ID. */
331253572Sloos	if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
332253572Sloos		sc->vid[vg->es_vlangroup] = vg->es_vid & ETHERSWITCH_VID_MASK;
333253572Sloos		/* Setting the vlanid to zero disables the vlangroup. */
334253572Sloos		if (sc->vid[vg->es_vlangroup] == 0) {
335253572Sloos			ARSWITCH_UNLOCK(sc);
336253572Sloos			return (0);
337253572Sloos		}
338253572Sloos		sc->vid[vg->es_vlangroup] |= ETHERSWITCH_VID_VALID;
339253572Sloos		vid = sc->vid[vg->es_vlangroup];
340253572Sloos	}
341253572Sloos
342253572Sloos	/* Member Ports. */
343253572Sloos	switch (sc->vlan_mode) {
344253572Sloos	case ETHERSWITCH_VLAN_DOT1Q:
345253572Sloos		err = arswitch_set_dot1q_vlan(sc, vg->es_member_ports, vid);
346253572Sloos		break;
347253572Sloos	case ETHERSWITCH_VLAN_PORT:
348253572Sloos		err = arswitch_set_port_vlan(sc, vg->es_member_ports, vid);
349253572Sloos		break;
350253572Sloos	default:
351253572Sloos		err = -1;
352253572Sloos	}
353253572Sloos	ARSWITCH_UNLOCK(sc);
354253572Sloos	return (err);
355253572Sloos}
356253572Sloos
357253572Sloosint
358253572Sloosarswitch_get_pvid(struct arswitch_softc *sc, int port, int *pvid)
359253572Sloos{
360253572Sloos	uint32_t reg;
361253572Sloos
362253572Sloos	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
363253572Sloos	reg = arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_VLAN(port));
364253572Sloos	*pvid = reg & 0xfff;
365253572Sloos	return (0);
366253572Sloos}
367253572Sloos
368253572Sloosint
369253572Sloosarswitch_set_pvid(struct arswitch_softc *sc, int port, int pvid)
370253572Sloos{
371253572Sloos
372253572Sloos	ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
373253572Sloos	return (arswitch_modifyreg(sc->sc_dev,
374253572Sloos	    AR8X16_REG_PORT_VLAN(port), 0xfff, pvid));
375253572Sloos}
376