1250386Sadrian/*-
2250386Sadrian * Copyright (c) 2013 Luiz Otavio O Souza.
3250386Sadrian * Copyright (c) 2011-2012 Stefan Bethke.
4250386Sadrian * Copyright (c) 2012 Adrian Chadd.
5250386Sadrian * All rights reserved.
6250386Sadrian *
7250386Sadrian * Redistribution and use in source and binary forms, with or without
8250386Sadrian * modification, are permitted provided that the following conditions
9250386Sadrian * are met:
10250386Sadrian * 1. Redistributions of source code must retain the above copyright
11250386Sadrian *    notice, this list of conditions and the following disclaimer.
12250386Sadrian * 2. Redistributions in binary form must reproduce the above copyright
13250386Sadrian *    notice, this list of conditions and the following disclaimer in the
14250386Sadrian *    documentation and/or other materials provided with the distribution.
15250386Sadrian *
16250386Sadrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17250386Sadrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18250386Sadrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19250386Sadrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20250386Sadrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21250386Sadrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22250386Sadrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23250386Sadrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24250386Sadrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25250386Sadrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26250386Sadrian * SUCH DAMAGE.
27250386Sadrian *
28250386Sadrian * $FreeBSD$
29250386Sadrian */
30250386Sadrian
31250386Sadrian#include <sys/param.h>
32250386Sadrian#include <sys/bus.h>
33250386Sadrian#include <sys/errno.h>
34250386Sadrian#include <sys/kernel.h>
35262848Sbrueffer#include <sys/lock.h>
36262848Sbrueffer#include <sys/mutex.h>
37250386Sadrian#include <sys/systm.h>
38250386Sadrian#include <sys/socket.h>
39250386Sadrian
40250386Sadrian#include <net/if.h>
41250386Sadrian
42250386Sadrian#include <dev/mii/mii.h>
43250386Sadrian
44250386Sadrian#include <dev/etherswitch/etherswitch.h>
45250386Sadrian#include <dev/etherswitch/ip17x/ip17x_phy.h>
46250386Sadrian#include <dev/etherswitch/ip17x/ip17x_reg.h>
47250386Sadrian#include <dev/etherswitch/ip17x/ip17x_var.h>
48250386Sadrian#include <dev/etherswitch/ip17x/ip17x_vlans.h>
49250386Sadrian#include <dev/etherswitch/ip17x/ip175c.h>
50250386Sadrian
51250386Sadrian#include "mdio_if.h"
52250386Sadrian#include "miibus_if.h"
53250386Sadrian#include "etherswitch_if.h"
54250386Sadrian
55250386Sadrian/*
56250386Sadrian * Reset vlans to default state.
57250386Sadrian */
58250386Sadrianint
59250386Sadrianip17x_reset_vlans(struct ip17x_softc *sc, uint32_t vlan_mode)
60250386Sadrian{
61250386Sadrian	struct ip17x_vlan *v;
62250386Sadrian	int i, j, phy;
63250386Sadrian
64250386Sadrian	/* Do not add or strip vlan tags on any port. */
65250386Sadrian	sc->addtag = 0;
66250386Sadrian	sc->striptag = 0;
67250386Sadrian
68250386Sadrian	/* Reset all vlan data. */
69250386Sadrian	memset(sc->vlan, 0, sizeof(sc->vlan));
70250386Sadrian	memset(sc->pvid, 0, sizeof(uint32_t) * sc->numports);
71250386Sadrian
72250386Sadrian	if (vlan_mode == ETHERSWITCH_VLAN_PORT) {
73250386Sadrian
74250386Sadrian		/* Initialize port based vlans. */
75250386Sadrian		for (i = 0, phy = 0; phy < MII_NPHY; phy++) {
76250386Sadrian			if (((1 << phy) & sc->phymask) == 0)
77250386Sadrian				continue;
78250386Sadrian			v = &sc->vlan[i];
79253569Sloos			v->vlanid = i++ | ETHERSWITCH_VID_VALID;
80250386Sadrian			v->ports = (1 << sc->cpuport);
81250386Sadrian			for (j = 0; j < MII_NPHY; j++) {
82250386Sadrian				if (((1 << j) & sc->phymask) == 0)
83250386Sadrian					continue;
84250386Sadrian				v->ports |= (1 << j);
85250386Sadrian			}
86250386Sadrian		}
87250386Sadrian
88250386Sadrian	} else if (vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
89250386Sadrian
90250386Sadrian		/*
91250386Sadrian		 * Setup vlan 1 as PVID for all switch ports.  Add all ports as
92250386Sadrian		 * members of vlan 1.
93250386Sadrian		 */
94250386Sadrian		v = &sc->vlan[0];
95253569Sloos		v->vlanid = 1 | ETHERSWITCH_VID_VALID;
96253569Sloos		/* Set PVID to 1 for everyone. */
97250386Sadrian		for (i = 0; i < sc->numports; i++)
98253569Sloos			sc->pvid[i] = 1;
99250386Sadrian		for (i = 0; i < MII_NPHY; i++) {
100250386Sadrian			if ((sc->phymask & (1 << i)) == 0)
101250386Sadrian				continue;
102250386Sadrian			v->ports |= (1 << i);
103250386Sadrian		}
104250386Sadrian	}
105250386Sadrian
106250386Sadrian	return (0);
107250386Sadrian}
108250386Sadrian
109250386Sadrianint
110250386Sadrianip17x_getvgroup(device_t dev, etherswitch_vlangroup_t *vg)
111250386Sadrian{
112250386Sadrian	struct ip17x_softc *sc;
113250386Sadrian	uint32_t port;
114250386Sadrian	int i;
115250386Sadrian
116250386Sadrian	sc = device_get_softc(dev);
117250386Sadrian
118250386Sadrian	/* Vlan ID. */
119250386Sadrian	vg->es_vid = sc->vlan[vg->es_vlangroup].vlanid;
120250386Sadrian
121250386Sadrian	/* Member Ports. */
122250386Sadrian	vg->es_member_ports = 0;
123250386Sadrian	for (i = 0; i < MII_NPHY; i++) {
124250386Sadrian		if ((sc->phymask & (1 << i)) == 0)
125250386Sadrian			continue;
126250386Sadrian		if ((sc->vlan[vg->es_vlangroup].ports & (1 << i)) == 0)
127250386Sadrian			continue;
128250386Sadrian		port = sc->phyport[i];
129250386Sadrian		vg->es_member_ports |= (1 << port);
130250386Sadrian	}
131250386Sadrian
132250386Sadrian	/* Not supported. */
133250386Sadrian	vg->es_untagged_ports = vg->es_member_ports;
134250386Sadrian	vg->es_fid = 0;
135250386Sadrian
136250386Sadrian	return (0);
137250386Sadrian}
138250386Sadrian
139250386Sadrianint
140250386Sadrianip17x_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
141250386Sadrian{
142250386Sadrian	struct ip17x_softc *sc;
143250386Sadrian	uint32_t phy;
144250386Sadrian	int i;
145250386Sadrian
146250386Sadrian	sc = device_get_softc(dev);
147250386Sadrian
148250386Sadrian	/* Check VLAN mode. */
149250386Sadrian	if (sc->vlan_mode == 0)
150250386Sadrian		return (EINVAL);
151250386Sadrian
152250386Sadrian	/* IP175C don't support VLAN IDs > 15. */
153253569Sloos	if (IP17X_IS_SWITCH(sc, IP175C) &&
154253569Sloos	    (vg->es_vid & ETHERSWITCH_VID_MASK) > IP175C_LAST_VLAN)
155250386Sadrian		return (EINVAL);
156250386Sadrian
157250386Sadrian	/* Vlan ID. */
158253569Sloos	if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
159253569Sloos		for (i = 0; i < sc->info.es_nvlangroups; i++) {
160253569Sloos			/* Is this Vlan ID already set in another vlangroup ? */
161253569Sloos			if (i != vg->es_vlangroup &&
162253569Sloos			    sc->vlan[i].vlanid & ETHERSWITCH_VID_VALID &&
163253569Sloos			    (sc->vlan[i].vlanid & ETHERSWITCH_VID_MASK) ==
164253569Sloos			    (vg->es_vid & ETHERSWITCH_VID_MASK))
165253569Sloos				return (EINVAL);
166253569Sloos		}
167253569Sloos		sc->vlan[vg->es_vlangroup].vlanid = vg->es_vid &
168253569Sloos		    ETHERSWITCH_VID_MASK;
169253569Sloos		/* Setting the vlanid to zero disables the vlangroup. */
170253569Sloos		if (sc->vlan[vg->es_vlangroup].vlanid == 0) {
171253569Sloos			sc->vlan[vg->es_vlangroup].ports = 0;
172253569Sloos			return (sc->hal.ip17x_hw_setup(sc));
173253569Sloos		}
174253569Sloos		sc->vlan[vg->es_vlangroup].vlanid |= ETHERSWITCH_VID_VALID;
175253569Sloos	}
176250386Sadrian
177250386Sadrian	/* Member Ports. */
178250386Sadrian	sc->vlan[vg->es_vlangroup].ports = 0;
179250386Sadrian	for (i = 0; i < sc->numports; i++) {
180250386Sadrian		if ((vg->es_member_ports & (1 << i)) == 0)
181250386Sadrian			continue;
182250386Sadrian		phy = sc->portphy[i];
183250386Sadrian		sc->vlan[vg->es_vlangroup].ports |= (1 << phy);
184250386Sadrian	}
185250386Sadrian
186250386Sadrian	return (sc->hal.ip17x_hw_setup(sc));
187250386Sadrian}
188