diff options
| author | Adrian Chadd <adrian@FreeBSD.org> | 2013-05-08 20:58:41 +0000 |
|---|---|---|
| committer | Adrian Chadd <adrian@FreeBSD.org> | 2013-05-08 20:58:41 +0000 |
| commit | 248dd6039ded56dd72f1a77cf0148a9b113ad3c8 (patch) | |
| tree | ad86c0d0b1bd4a53c4f82bd2f68a69bfc45ee226 /sys/dev/etherswitch/ip17x | |
| parent | a858494b9d20bf17cdf4253170d5e28e69964ad3 (diff) | |
Notes
Diffstat (limited to 'sys/dev/etherswitch/ip17x')
| -rw-r--r-- | sys/dev/etherswitch/ip17x/ip175c.c | 249 | ||||
| -rw-r--r-- | sys/dev/etherswitch/ip17x/ip175c.h | 43 | ||||
| -rw-r--r-- | sys/dev/etherswitch/ip17x/ip175d.c | 219 | ||||
| -rw-r--r-- | sys/dev/etherswitch/ip17x/ip175d.h | 43 | ||||
| -rw-r--r-- | sys/dev/etherswitch/ip17x/ip17x.c | 614 | ||||
| -rw-r--r-- | sys/dev/etherswitch/ip17x/ip17x_phy.c | 102 | ||||
| -rw-r--r-- | sys/dev/etherswitch/ip17x/ip17x_phy.h | 38 | ||||
| -rw-r--r-- | sys/dev/etherswitch/ip17x/ip17x_reg.h | 44 | ||||
| -rw-r--r-- | sys/dev/etherswitch/ip17x/ip17x_var.h | 95 | ||||
| -rw-r--r-- | sys/dev/etherswitch/ip17x/ip17x_vlans.c | 167 | ||||
| -rw-r--r-- | sys/dev/etherswitch/ip17x/ip17x_vlans.h | 36 |
11 files changed, 1650 insertions, 0 deletions
diff --git a/sys/dev/etherswitch/ip17x/ip175c.c b/sys/dev/etherswitch/ip17x/ip175c.c new file mode 100644 index 000000000000..b106e2905349 --- /dev/null +++ b/sys/dev/etherswitch/ip17x/ip175c.c @@ -0,0 +1,249 @@ +/*- + * Copyright (c) 2013 Luiz Otavio O Souza. + * Copyright (c) 2011-2012 Stefan Bethke. + * Copyright (c) 2012 Adrian Chadd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/systm.h> +#include <sys/socket.h> + +#include <net/if.h> + +#include <dev/mii/mii.h> + +#include <dev/etherswitch/etherswitch.h> +#include <dev/etherswitch/ip17x/ip17x_phy.h> +#include <dev/etherswitch/ip17x/ip17x_reg.h> +#include <dev/etherswitch/ip17x/ip17x_var.h> +#include <dev/etherswitch/ip17x/ip17x_vlans.h> +#include <dev/etherswitch/ip17x/ip175c.h> + +/* + * IP175C specific functions. + */ + +/* + * Reset the switch. + */ +static int +ip175c_reset(struct ip17x_softc *sc) +{ + uint32_t data; + + /* Reset all the switch settings. */ + if (ip17x_writephy(sc->sc_dev, IP175C_RESET_PHY, IP175C_RESET_REG, + 0x175c)) + return (-1); + DELAY(2); + + /* Force IP175C mode. */ + data = ip17x_readphy(sc->sc_dev, IP175C_MODE_PHY, IP175C_MODE_REG); + if (data == 0x175a) { + if (ip17x_writephy(sc->sc_dev, IP175C_MODE_PHY, IP175C_MODE_REG, + 0x175c)) + return (-1); + } + + return (0); +} + +static int +ip175c_port_vlan_setup(struct ip17x_softc *sc) +{ + struct ip17x_vlan *v; + uint32_t ports[IP175X_NUM_PORTS], reg[IP175X_NUM_PORTS/2]; + int i, err, phy; + + KASSERT(sc->cpuport == 5, ("cpuport != 5 not supported for IP175C")); + KASSERT(sc->numports == 6, ("numports != 6 not supported for IP175C")); + + /* Build the port access masks. */ + memset(ports, 0, sizeof(ports)); + for (i = 0; i < sc->info.es_nports; i++) { + phy = sc->portphy[i]; + v = &sc->vlan[i]; + ports[phy] = v->ports; + } + + /* Move the cpuport bit to its correct place. */ + for (i = 0; i < sc->numports; i++) { + if (ports[i] & (1 << sc->cpuport)) { + ports[i] |= (1 << 7); + ports[i] &= ~(1 << sc->cpuport); + } + } + + /* And now build the switch register data. */ + memset(reg, 0, sizeof(reg)); + for (i = 0; i < (sc->numports / 2); i++) + reg[i] = ports[i * 2] << 8 | ports[i * 2 + 1]; + + /* Update the switch resgisters. */ + err = ip17x_writephy(sc->sc_dev, 29, 19, reg[0]); + if (err == 0) + err = ip17x_writephy(sc->sc_dev, 29, 20, reg[1]); + if (err == 0) + err = ip17x_updatephy(sc->sc_dev, 29, 21, 0xff00, reg[2]); + if (err == 0) + err = ip17x_updatephy(sc->sc_dev, 30, 18, 0x00ff, reg[2]); + return (err); +} + +static int +ip175c_dot1q_vlan_setup(struct ip17x_softc *sc) +{ + struct ip17x_vlan *v; + uint32_t data; + uint32_t vlans[IP17X_MAX_VLANS]; + int i, j; + + KASSERT(sc->cpuport == 5, ("cpuport != 5 not supported for IP175C")); + KASSERT(sc->numports == 6, ("numports != 6 not supported for IP175C")); + + /* Add and strip VLAN tags. */ + data = (sc->addtag & ~(1 << IP175X_CPU_PORT)) << 11; + data |= (sc->striptag & ~(1 << IP175X_CPU_PORT)) << 6; + if (sc->addtag & (1 << IP175X_CPU_PORT)) + data |= (1 << 1); + if (sc->striptag & (1 << IP175X_CPU_PORT)) + data |= (1 << 0); + if (ip17x_writephy(sc->sc_dev, 29, 23, data)) + return (-1); + + /* Set the VID_IDX_SEL to 0. */ + if (ip17x_updatephy(sc->sc_dev, 30, 9, 0x70, 0)) + return (-1); + + /* Calculate the port masks. */ + memset(vlans, 0, sizeof(vlans)); + for (i = 0; i < IP17X_MAX_VLANS; i++) { + v = &sc->vlan[i]; + if (v->vlanid == 0) + continue; + vlans[v->vlanid] = v->ports; + } + + for (j = 0, i = 1; i <= IP17X_MAX_VLANS / 2; i++) { + data = vlans[j++] & 0x3f; + data |= (vlans[j++] & 0x3f) << 8; + if (ip17x_writephy(sc->sc_dev, 30, i, data)) + return (-1); + } + + /* Port default VLAN ID. */ + for (i = 0; i < sc->numports; i++) { + if (i == IP175X_CPU_PORT) { + if (ip17x_writephy(sc->sc_dev, 29, 30, sc->pvid[i])) + return (-1); + } else { + if (ip17x_writephy(sc->sc_dev, 29, 24 + i, sc->pvid[i])) + return (-1); + } + } + + return (0); +} + +/* + * Set the Switch configuration. + */ +static int +ip175c_hw_setup(struct ip17x_softc *sc) +{ + + switch (sc->vlan_mode) { + case ETHERSWITCH_VLAN_PORT: + return (ip175c_port_vlan_setup(sc)); + break; + case ETHERSWITCH_VLAN_DOT1Q: + return (ip175c_dot1q_vlan_setup(sc)); + break; + } + return (-1); +} + +/* + * Set the switch VLAN mode. + */ +static int +ip175c_set_vlan_mode(struct ip17x_softc *sc, uint32_t mode) +{ + + switch (mode) { + case ETHERSWITCH_VLAN_DOT1Q: + /* Enable VLAN tag processing. */ + ip17x_updatephy(sc->sc_dev, 30, 9, 0x80, 0x80); + sc->vlan_mode = mode; + break; + case ETHERSWITCH_VLAN_PORT: + default: + /* Disable VLAN tag processing. */ + ip17x_updatephy(sc->sc_dev, 30, 9, 0x80, 0); + sc->vlan_mode = ETHERSWITCH_VLAN_PORT; + break; + }; + + /* Reset vlans. */ + ip17x_reset_vlans(sc, sc->vlan_mode); + + /* Update switch configuration. */ + ip175c_hw_setup(sc); + + return (0); +} + +/* + * Get the switch VLAN mode. + */ +static int +ip175c_get_vlan_mode(struct ip17x_softc *sc) +{ + + return (sc->vlan_mode); +} + +void +ip175c_attach(struct ip17x_softc *sc) +{ + + sc->hal.ip17x_reset = ip175c_reset; + sc->hal.ip17x_hw_setup = ip175c_hw_setup; + sc->hal.ip17x_get_vlan_mode = ip175c_get_vlan_mode; + sc->hal.ip17x_set_vlan_mode = ip175c_set_vlan_mode; + + /* Defaults for IP175C. */ + sc->cpuport = IP175X_CPU_PORT; + sc->numports = IP175X_NUM_PORTS; + sc->info.es_vlan_caps = ETHERSWITCH_VLAN_PORT | ETHERSWITCH_VLAN_DOT1Q; + + device_printf(sc->sc_dev, "type: IP175C\n"); +} diff --git a/sys/dev/etherswitch/ip17x/ip175c.h b/sys/dev/etherswitch/ip17x/ip175c.h new file mode 100644 index 000000000000..8abb4b696196 --- /dev/null +++ b/sys/dev/etherswitch/ip17x/ip175c.h @@ -0,0 +1,43 @@ +/*- + * Copyright (c) 2013 Luiz Otavio O Souza. + * Copyright (c) 2011-2012 Stefan Bethke. + * Copyright (c) 2012 Adrian Chadd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef __IP175C_H__ +#define __IP175C_H__ + +#define IP175C_MODE_PHY 29 +#define IP175C_MODE_REG 31 +#define IP175C_RESET_PHY 30 +#define IP175C_RESET_REG 0 + +#define IP175C_LAST_VLAN 15 + +void ip175c_attach(struct ip17x_softc *sc); + +#endif /* __IP175C_H__ */ diff --git a/sys/dev/etherswitch/ip17x/ip175d.c b/sys/dev/etherswitch/ip17x/ip175d.c new file mode 100644 index 000000000000..8bc28433e04d --- /dev/null +++ b/sys/dev/etherswitch/ip17x/ip175d.c @@ -0,0 +1,219 @@ +/*- + * Copyright (c) 2013 Luiz Otavio O Souza. + * Copyright (c) 2011-2012 Stefan Bethke. + * Copyright (c) 2012 Adrian Chadd. + * Copyright (C) 2008 Patrick Horn. + * Copyright (C) 2008, 2010 Martin Mares. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/systm.h> +#include <sys/socket.h> + +#include <net/if.h> + +#include <dev/mii/mii.h> + +#include <dev/etherswitch/etherswitch.h> +#include <dev/etherswitch/ip17x/ip17x_phy.h> +#include <dev/etherswitch/ip17x/ip17x_reg.h> +#include <dev/etherswitch/ip17x/ip17x_var.h> +#include <dev/etherswitch/ip17x/ip17x_vlans.h> +#include <dev/etherswitch/ip17x/ip175d.h> + +/* + * IP175D specific functions. + */ + +/* + * Reset the switch to default state. + */ +static int +ip175d_reset(struct ip17x_softc *sc) +{ + + /* Reset all the switch settings. */ + ip17x_writephy(sc->sc_dev, IP175D_RESET_PHY, IP175D_RESET_REG, 0x175d); + DELAY(2); + + /* Disable the special tagging mode. */ + ip17x_updatephy(sc->sc_dev, 21, 22, 0x3, 0x0); + + /* Set 802.1q protocol type. */ + ip17x_writephy(sc->sc_dev, 22, 3, 0x8100); + + return (0); +} + +/* + * Set the Switch configuration. + */ +static int +ip175d_hw_setup(struct ip17x_softc *sc) +{ + struct ip17x_vlan *v; + uint32_t ports[IP17X_MAX_VLANS]; + uint32_t addtag[IP17X_MAX_VLANS]; + uint32_t striptag[IP17X_MAX_VLANS]; + uint32_t vlan_mask; + int i, j; + + vlan_mask = 0; + for (i = 0; i < IP17X_MAX_VLANS; i++) { + + ports[i] = 0; + addtag[i] = 0; + striptag[i] = 0; + + v = &sc->vlan[i]; + if (v->vlanid == 0 || sc->vlan_mode == 0) { + /* Vlangroup disabled. Reset the filter. */ + ip17x_writephy(sc->sc_dev, 22, 14 + i, i + 1); + ports[i] = 0x3f; + continue; + } + + vlan_mask |= (1 << i); + ports[i] = v->ports; + + /* Setup the filter, write the VLAN id. */ + ip17x_writephy(sc->sc_dev, 22, 14 + i, v->vlanid); + + for (j = 0; j < MII_NPHY; j++) { + if ((ports[i] & (1 << j)) == 0) + continue; + if (sc->addtag & (1 << j)) + addtag[i] |= (1 << j); + if (sc->striptag & (1 << j)) + striptag[i] |= (1 << j); + } + } + + /* Write the port masks, tag adds and removals. */ + for (i = 0; i < IP17X_MAX_VLANS / 2; i++) { + ip17x_writephy(sc->sc_dev, 23, i, + ports[2 * i] | (ports[2 * i + 1] << 8)); + ip17x_writephy(sc->sc_dev, 23, i + 8, + addtag[2 * i] | (addtag[2 * i + 1] << 8)); + ip17x_writephy(sc->sc_dev, 23, i + 16, + striptag[2 * i] | (striptag[2 * i + 1] << 8)); + } + + /* Write the in use vlan mask. */ + ip17x_writephy(sc->sc_dev, 22, 10, vlan_mask); + + /* Write the PVID of each port. */ + for (i = 0; i < sc->numports; i++) + ip17x_writephy(sc->sc_dev, 22, 4 + i, sc->pvid[i]); + + return (0); +} + +/* + * Set the switch VLAN mode. + */ +static int +ip175d_set_vlan_mode(struct ip17x_softc *sc, uint32_t mode) +{ + + switch (mode) { + case ETHERSWITCH_VLAN_DOT1Q: + /* + * VLAN classification rules: tag-based VLANs, + * use VID to classify, drop packets that cannot + * be classified. + */ + ip17x_updatephy(sc->sc_dev, 22, 0, 0x3fff, 0x003f); + sc->vlan_mode = mode; + break; + case ETHERSWITCH_VLAN_PORT: + sc->vlan_mode = mode; + /* fallthrough */ + default: + /* + * VLAN classification rules: everything off & + * clear table. + */ + ip17x_updatephy(sc->sc_dev, 22, 0, 0xbfff, 0x8000); + sc->vlan_mode = 0; + break; + }; + + if (sc->vlan_mode != 0) { + /* + * Ingress rules: CFI=1 dropped, null VID is untagged, VID=1 passed, + * VID=0xfff discarded, admin both tagged and untagged, ingress + * filters enabled. + */ + ip17x_updatephy(sc->sc_dev, 22, 1, 0x0fff, 0x0c3f); + + /* Egress rules: IGMP processing off, keep VLAN header off. */ + ip17x_updatephy(sc->sc_dev, 22, 2, 0x0fff, 0x0000); + } else { + ip17x_updatephy(sc->sc_dev, 22, 1, 0x0fff, 0x043f); + ip17x_updatephy(sc->sc_dev, 22, 2, 0x0fff, 0x0020); + } + + /* Reset vlans. */ + ip17x_reset_vlans(sc, sc->vlan_mode); + + /* Update switch configuration. */ + ip175d_hw_setup(sc); + + return (0); +} + +/* + * Get the switch VLAN mode. + */ +static int +ip175d_get_vlan_mode(struct ip17x_softc *sc) +{ + + return (sc->vlan_mode); +} + +void +ip175d_attach(struct ip17x_softc *sc) +{ + + sc->hal.ip17x_reset = ip175d_reset; + sc->hal.ip17x_hw_setup = ip175d_hw_setup; + sc->hal.ip17x_get_vlan_mode = ip175d_get_vlan_mode; + sc->hal.ip17x_set_vlan_mode = ip175d_set_vlan_mode; + + /* Defaults for IP175C. */ + sc->cpuport = IP175X_CPU_PORT; + sc->numports = IP175X_NUM_PORTS; + sc->info.es_vlan_caps = ETHERSWITCH_VLAN_DOT1Q; + + device_printf(sc->sc_dev, "type: IP175D\n"); +} diff --git a/sys/dev/etherswitch/ip17x/ip175d.h b/sys/dev/etherswitch/ip17x/ip175d.h new file mode 100644 index 000000000000..d8a3033c2094 --- /dev/null +++ b/sys/dev/etherswitch/ip17x/ip175d.h @@ -0,0 +1,43 @@ +/*- + * Copyright (c) 2013 Luiz Otavio O Souza. + * Copyright (c) 2011-2012 Stefan Bethke. + * Copyright (c) 2012 Adrian Chadd. + * Copyright (C) 2008 Patrick Horn. + * Copyright (C) 2008, 2010 Martin Mares. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef __IP175D_H__ +#define __IP175D_H__ + +#define IP175D_ID_PHY 20 +#define IP175D_ID_REG 0 +#define IP175D_RESET_PHY 20 +#define IP175D_RESET_REG 2 + +void ip175d_attach(struct ip17x_softc *sc); + +#endif /* __IP175D_H__ */ diff --git a/sys/dev/etherswitch/ip17x/ip17x.c b/sys/dev/etherswitch/ip17x/ip17x.c new file mode 100644 index 000000000000..5ac055420471 --- /dev/null +++ b/sys/dev/etherswitch/ip17x/ip17x.c @@ -0,0 +1,614 @@ +/*- + * Copyright (c) 2013 Luiz Otavio O Souza. + * Copyright (c) 2011-2012 Stefan Bethke. + * Copyright (c) 2012 Adrian Chadd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/errno.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/sysctl.h> +#include <sys/systm.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> + +#include <machine/bus.h> +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> +#include <dev/etherswitch/mdio.h> + +#include <dev/etherswitch/etherswitch.h> +#include <dev/etherswitch/ip17x/ip17x_phy.h> +#include <dev/etherswitch/ip17x/ip17x_reg.h> +#include <dev/etherswitch/ip17x/ip17x_var.h> +#include <dev/etherswitch/ip17x/ip17x_vlans.h> +#include <dev/etherswitch/ip17x/ip175c.h> +#include <dev/etherswitch/ip17x/ip175d.h> + +#include "mdio_if.h" +#include "miibus_if.h" +#include "etherswitch_if.h" + +MALLOC_DECLARE(M_IP17X); +MALLOC_DEFINE(M_IP17X, "ip17x", "ip17x data structures"); + +static void ip17x_tick(void *); +static int ip17x_ifmedia_upd(struct ifnet *); +static void ip17x_ifmedia_sts(struct ifnet *, struct ifmediareq *); + +static int +ip17x_probe(device_t dev) +{ + struct ip17x_softc *sc; + uint32_t oui, model, phy_id1, phy_id2; + + sc = device_get_softc(dev); + + /* Read ID from PHY 0. */ + phy_id1 = MDIO_READREG(device_get_parent(dev), 0, MII_PHYIDR1); + phy_id2 = MDIO_READREG(device_get_parent(dev), 0, MII_PHYIDR2); + + oui = MII_OUI(phy_id1, phy_id2), + model = MII_MODEL(phy_id2); + /* We only care about IC+ devices. */ + if (oui != IP17X_OUI) { + device_printf(dev, + "Unsupported IC+ switch. Unknown OUI: %#x\n", oui); + return (ENXIO); + } + + switch (model) { + case IP17X_IP175A: + sc->sc_switchtype = IP17X_SWITCH_IP175A; + break; + case IP17X_IP175C: + sc->sc_switchtype = IP17X_SWITCH_IP175C; + break; + default: + device_printf(dev, "Unsupported IC+ switch model: %#x\n", + model); + return (ENXIO); + } + + /* IP175D has a specific ID register. */ + model = MDIO_READREG(device_get_parent(dev), IP175D_ID_PHY, + IP175D_ID_REG); + if (model == 0x175d) + sc->sc_switchtype = IP17X_SWITCH_IP175D; + else { + /* IP178 has more PHYs. Try it. */ + model = MDIO_READREG(device_get_parent(dev), 5, MII_PHYIDR1); + if (phy_id1 == model) + sc->sc_switchtype = IP17X_SWITCH_IP178C; + } + + device_set_desc_copy(dev, "IC+ IP17x switch driver"); + return (BUS_PROBE_DEFAULT); +} + +static int +ip17x_attach_phys(struct ip17x_softc *sc) +{ + int err, phy, port; + char name[IFNAMSIZ]; + + port = err = 0; + + /* PHYs need an interface, so we generate a dummy one */ + snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->sc_dev)); + for (phy = 0; phy < MII_NPHY; phy++) { + if (((1 << phy) & sc->phymask) == 0) + continue; + sc->phyport[phy] = port; + sc->portphy[port] = phy; + sc->ifp[port] = if_alloc(IFT_ETHER); + sc->ifp[port]->if_softc = sc; + sc->ifp[port]->if_flags |= IFF_UP | IFF_BROADCAST | + IFF_DRV_RUNNING | IFF_SIMPLEX; + sc->ifname[port] = malloc(strlen(name)+1, M_IP17X, M_WAITOK); + bcopy(name, sc->ifname[port], strlen(name)+1); + if_initname(sc->ifp[port], sc->ifname[port], port); + sc->miibus[port] = malloc(sizeof(device_t), M_IP17X, + M_WAITOK | M_ZERO); + err = mii_attach(sc->sc_dev, sc->miibus[port], sc->ifp[port], + ip17x_ifmedia_upd, ip17x_ifmedia_sts, \ + BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0); + DPRINTF(sc->sc_dev, "%s attached to pseudo interface %s\n", + device_get_nameunit(*sc->miibus[port]), + sc->ifp[port]->if_xname); + if (err != 0) { + device_printf(sc->sc_dev, + "attaching PHY %d failed\n", + phy); + break; + } + sc->info.es_nports = port + 1; + if (++port >= sc->numports) + break; + } + return (err); +} + +static int +ip17x_attach(device_t dev) +{ + struct ip17x_softc *sc; + int err; + + sc = device_get_softc(dev); + + sc->sc_dev = dev; + mtx_init(&sc->sc_mtx, "ip17x", NULL, MTX_DEF); + strlcpy(sc->info.es_name, device_get_desc(dev), + sizeof(sc->info.es_name)); + + /* XXX Defaults */ + sc->phymask = 0x0f; + sc->media = 100; + + (void) resource_int_value(device_get_name(dev), device_get_unit(dev), + "phymask", &sc->phymask); + + /* Number of vlans supported by the switch. */ + sc->info.es_nvlangroups = IP17X_MAX_VLANS; + + /* Attach the switch related functions. */ + if (IP17X_IS_SWITCH(sc, IP175C)) + ip175c_attach(sc); + else if (IP17X_IS_SWITCH(sc, IP175D)) + ip175d_attach(sc); + else + /* We don't have support to all the models yet :-/ */ + return (ENXIO); + + /* Always attach the cpu port. */ + sc->phymask |= (1 << sc->cpuport); + + sc->ifp = malloc(sizeof(struct ifnet *) * sc->numports, M_IP17X, + M_WAITOK | M_ZERO); + sc->pvid = malloc(sizeof(uint32_t) * sc->numports, M_IP17X, + M_WAITOK | M_ZERO); + sc->ifname = malloc(sizeof(char *) * sc->numports, M_IP17X, + M_WAITOK | M_ZERO); + sc->miibus = malloc(sizeof(device_t *) * sc->numports, M_IP17X, + M_WAITOK | M_ZERO); + sc->portphy = malloc(sizeof(int) * sc->numports, M_IP17X, + M_WAITOK | M_ZERO); + + /* Initialize the switch. */ + sc->hal.ip17x_reset(sc); + + /* + * Attach the PHYs and complete the bus enumeration. + */ + err = ip17x_attach_phys(sc); + if (err != 0) + return (err); + + /* + * Set the switch to port based vlans or disabled (if not supported + * on this model). + */ + sc->hal.ip17x_set_vlan_mode(sc, ETHERSWITCH_VLAN_PORT); + + bus_generic_probe(dev); + bus_enumerate_hinted_children(dev); + err = bus_generic_attach(dev); + if (err != 0) + return (err); + + callout_init(&sc->callout_tick, 0); + + ip17x_tick(sc); + + return (0); +} + +static int +ip17x_detach(device_t dev) +{ + struct ip17x_softc *sc; + int i, port; + + sc = device_get_softc(dev); + callout_drain(&sc->callout_tick); + + for (i=0; i < MII_NPHY; i++) { + if (((1 << i) & sc->phymask) == 0) + continue; + port = sc->phyport[i]; + if (sc->miibus[port] != NULL) + device_delete_child(dev, (*sc->miibus[port])); + if (sc->ifp[port] != NULL) + if_free(sc->ifp[port]); + free(sc->ifname[port], M_IP17X); + free(sc->miibus[port], M_IP17X); + } + + free(sc->portphy, M_IP17X); + free(sc->miibus, M_IP17X); + free(sc->ifname, M_IP17X); + free(sc->pvid, M_IP17X); + free(sc->ifp, M_IP17X); + + /* Reset the switch. */ + sc->hal.ip17x_reset(sc); + + bus_generic_detach(dev); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static inline struct mii_data * +ip17x_miiforport(struct ip17x_softc *sc, int port) +{ + + if (port < 0 || port > sc->numports) + return (NULL); + return (device_get_softc(*sc->miibus[port])); +} + +static inline struct ifnet * +ip17x_ifpforport(struct ip17x_softc *sc, int port) +{ + + if (port < 0 || port > sc->numports) + return (NULL); + return (sc->ifp[port]); +} + +/* + * Poll the status for all PHYs. + */ +static void +ip17x_miipollstat(struct ip17x_softc *sc) +{ + struct mii_softc *miisc; + struct mii_data *mii; + int i, port; + + IP17X_LOCK_ASSERT(sc, MA_NOTOWNED); + + for (i = 0; i < MII_NPHY; i++) { + if (((1 << i) & sc->phymask) == 0) + continue; + port = sc->phyport[i]; + if ((*sc->miibus[port]) == NULL) + continue; + mii = device_get_softc(*sc->miibus[port]); + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { + if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) != + miisc->mii_inst) + continue; + ukphy_status(miisc); + mii_phy_update(miisc, MII_POLLSTAT); + } + } +} + +static void +ip17x_tick(void *arg) +{ + struct ip17x_softc *sc; + + sc = arg; + ip17x_miipollstat(sc); + callout_reset(&sc->callout_tick, hz, ip17x_tick, sc); +} + +static void +ip17x_lock(device_t dev) +{ + struct ip17x_softc *sc; + + sc = device_get_softc(dev); + IP17X_LOCK_ASSERT(sc, MA_NOTOWNED); + IP17X_LOCK(sc); +} + +static void +ip17x_unlock(device_t dev) +{ + struct ip17x_softc *sc; + + sc = device_get_softc(dev); + IP17X_LOCK_ASSERT(sc, MA_OWNED); + IP17X_UNLOCK(sc); +} + +static etherswitch_info_t * +ip17x_getinfo(device_t dev) +{ + struct ip17x_softc *sc; + + sc = device_get_softc(dev); + return (&sc->info); +} + +static int +ip17x_getport(device_t dev, etherswitch_port_t *p) +{ + struct ip17x_softc *sc; + struct ifmediareq *ifmr; + struct mii_data *mii; + int err, phy; + + sc = device_get_softc(dev); + if (p->es_port < 0 || p->es_port >= sc->numports) + return (ENXIO); + + phy = sc->portphy[p->es_port]; + + /* Retrieve the PVID. */ + p->es_pvid = sc->pvid[phy]; + + /* Port flags. */ + if (sc->addtag & (1 << phy)) + p->es_flags |= ETHERSWITCH_PORT_ADDTAG; + if (sc->striptag & (1 << phy)) + p->es_flags |= ETHERSWITCH_PORT_STRIPTAG; + + ifmr = &p->es_ifmr; + + /* No media settings ? */ + if (p->es_ifmr.ifm_count == 0) + return (0); + + mii = ip17x_miiforport(sc, p->es_port); + if (mii == NULL) + return (ENXIO); + if (phy == sc->cpuport) { + /* fill in fixed values for CPU port */ + p->es_flags |= ETHERSWITCH_PORT_CPU; + ifmr->ifm_count = 0; + if (sc->media == 100) + ifmr->ifm_current = ifmr->ifm_active = + IFM_ETHER | IFM_100_TX | IFM_FDX; + else + ifmr->ifm_current = ifmr->ifm_active = + IFM_ETHER | IFM_1000_T | IFM_FDX; + ifmr->ifm_mask = 0; + ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID; + } else { + err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr, + &mii->mii_media, SIOCGIFMEDIA); + if (err) + return (err); + } + return (0); +} + +static int +ip17x_setport(device_t dev, etherswitch_port_t *p) +{ + struct ip17x_softc *sc; + struct ifmedia *ifm; + struct ifnet *ifp; + struct mii_data *mii; + int phy; + + sc = device_get_softc(dev); + if (p->es_port < 0 || p->es_port >= sc->numports) + return (ENXIO); + + phy = sc->portphy[p->es_port]; + ifp = ip17x_ifpforport(sc, p->es_port); + mii = ip17x_miiforport(sc, p->es_port); + if (ifp == NULL || mii == NULL) + return (ENXIO); + + /* Port flags. */ + if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) { + + /* Set the PVID. */ + if (p->es_pvid != 0) { + if (IP17X_IS_SWITCH(sc, IP175C) && + p->es_pvid > IP175C_LAST_VLAN) + return (ENXIO); + sc->pvid[phy] = p->es_pvid; + } + + /* Mutually exclusive. */ + if (p->es_flags & ETHERSWITCH_PORT_ADDTAG && + p->es_flags & ETHERSWITCH_PORT_STRIPTAG) + return (EINVAL); + + /* Reset the settings for this port. */ + sc->addtag &= ~(1 << phy); + sc->striptag &= ~(1 << phy); + + /* And then set it to the new value. */ + if (p->es_flags & ETHERSWITCH_PORT_ADDTAG) + sc->addtag |= (1 << phy); + if (p->es_flags & ETHERSWITCH_PORT_STRIPTAG) + sc->striptag |= (1 << phy); + } + + /* Update the switch configuration. */ + if (sc->hal.ip17x_hw_setup(sc)) + return (ENXIO); + + /* Do not allow media changes on CPU port. */ + if (phy == sc->cpuport) + return (0); + + /* No media settings ? */ + if (p->es_ifmr.ifm_count == 0) + return (0); + + ifm = &mii->mii_media; + return (ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA)); +} + +static void +ip17x_statchg(device_t dev) +{ + + DPRINTF(dev, "%s\n", __func__); +} + +static int +ip17x_ifmedia_upd(struct ifnet *ifp) +{ + struct ip17x_softc *sc; + struct mii_data *mii; + + DPRINTF(sc->sc_dev, "%s\n", __func__); + sc = ifp->if_softc; + mii = ip17x_miiforport(sc, ifp->if_dunit); + if (mii == NULL) + return (ENXIO); + mii_mediachg(mii); + return (0); +} + +static void +ip17x_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct ip17x_softc *sc; + struct mii_data *mii; + + DPRINTF(sc->sc_dev, "%s\n", __func__); + + sc = ifp->if_softc; + mii = ip17x_miiforport(sc, ifp->if_dunit); + if (mii == NULL) + return; + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; +} + +static int +ip17x_readreg(device_t dev, int addr) +{ + struct ip17x_softc *sc; + + sc = device_get_softc(dev); + IP17X_LOCK_ASSERT(sc, MA_OWNED); + + /* Not supported. */ + return (0); +} + +static int +ip17x_writereg(device_t dev, int addr, int value) +{ + struct ip17x_softc *sc; + + sc = device_get_softc(dev); + IP17X_LOCK_ASSERT(sc, MA_OWNED); + + /* Not supported. */ + return (0); +} + +static int +ip17x_getconf(device_t dev, etherswitch_conf_t *conf) +{ + struct ip17x_softc *sc; + + sc = device_get_softc(dev); + + /* Return the VLAN mode. */ + conf->cmd = ETHERSWITCH_CONF_VLAN_MODE; + conf->vlan_mode = sc->hal.ip17x_get_vlan_mode(sc); + + return (0); +} + +static int +ip17x_setconf(device_t dev, etherswitch_conf_t *conf) +{ + struct ip17x_softc *sc; + + sc = device_get_softc(dev); + + /* Set the VLAN mode. */ + if (conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) + sc->hal.ip17x_set_vlan_mode(sc, conf->vlan_mode); + + return (0); +} + +static device_method_t ip17x_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ip17x_probe), + DEVMETHOD(device_attach, ip17x_attach), + DEVMETHOD(device_detach, ip17x_detach), + + /* bus interface */ + DEVMETHOD(bus_add_child, device_add_child_ordered), + + /* MII interface */ + DEVMETHOD(miibus_readreg, ip17x_readphy), + DEVMETHOD(miibus_writereg, ip17x_writephy), + DEVMETHOD(miibus_statchg, ip17x_statchg), + + /* MDIO interface */ + DEVMETHOD(mdio_readreg, ip17x_readphy), + DEVMETHOD(mdio_writereg, ip17x_writephy), + + /* etherswitch interface */ + DEVMETHOD(etherswitch_lock, ip17x_lock), + DEVMETHOD(etherswitch_unlock, ip17x_unlock), + DEVMETHOD(etherswitch_getinfo, ip17x_getinfo), + DEVMETHOD(etherswitch_readreg, ip17x_readreg), + DEVMETHOD(etherswitch_writereg, ip17x_writereg), + DEVMETHOD(etherswitch_readphyreg, ip17x_readphy), + DEVMETHOD(etherswitch_writephyreg, ip17x_writephy), + DEVMETHOD(etherswitch_getport, ip17x_getport), + DEVMETHOD(etherswitch_setport, ip17x_setport), + DEVMETHOD(etherswitch_getvgroup, ip17x_getvgroup), + DEVMETHOD(etherswitch_setvgroup, ip17x_setvgroup), + DEVMETHOD(etherswitch_getconf, ip17x_getconf), + DEVMETHOD(etherswitch_setconf, ip17x_setconf), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(ip17x, ip17x_driver, ip17x_methods, + sizeof(struct ip17x_softc)); +static devclass_t ip17x_devclass; + +DRIVER_MODULE(ip17x, mdio, ip17x_driver, ip17x_devclass, 0, 0); +DRIVER_MODULE(miibus, ip17x, miibus_driver, miibus_devclass, 0, 0); +DRIVER_MODULE(mdio, ip17x, mdio_driver, mdio_devclass, 0, 0); +DRIVER_MODULE(etherswitch, ip17x, etherswitch_driver, etherswitch_devclass, 0, 0); +MODULE_VERSION(ip17x, 1); +MODULE_DEPEND(ip17x, miibus, 1, 1, 1); /* XXX which versions? */ +MODULE_DEPEND(ip17x, etherswitch, 1, 1, 1); /* XXX which versions? */ diff --git a/sys/dev/etherswitch/ip17x/ip17x_phy.c b/sys/dev/etherswitch/ip17x/ip17x_phy.c new file mode 100644 index 000000000000..067b8bac7e23 --- /dev/null +++ b/sys/dev/etherswitch/ip17x/ip17x_phy.c @@ -0,0 +1,102 @@ +/*- + * Copyright (c) 2013 Luiz Otavio O Souza. + * Copyright (c) 2011-2012 Stefan Bethke. + * Copyright (c) 2012 Adrian Chadd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/errno.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/socket.h> + +#include <net/if.h> + +#include <dev/mii/mii.h> + +#include <dev/etherswitch/etherswitch.h> +#include <dev/etherswitch/ip17x/ip17x_phy.h> +#include <dev/etherswitch/ip17x/ip17x_reg.h> +#include <dev/etherswitch/ip17x/ip17x_var.h> + +#include "mdio_if.h" +#include "miibus_if.h" +#include "etherswitch_if.h" + +int +ip17x_readphy(device_t dev, int phy, int reg) +{ + struct ip17x_softc *sc; + int data; + + sc = device_get_softc(dev); + IP17X_LOCK_ASSERT(sc, MA_NOTOWNED); + + if (phy < 0 || phy >= 32) + return (ENXIO); + if (reg < 0 || reg >= 32) + return (ENXIO); + + IP17X_LOCK(sc); + data = MDIO_READREG(device_get_parent(dev), phy, reg); + IP17X_UNLOCK(sc); + + return (data); +} + +int +ip17x_writephy(device_t dev, int phy, int reg, int data) +{ + struct ip17x_softc *sc; + int err; + + sc = device_get_softc(dev); + IP17X_LOCK_ASSERT(sc, MA_NOTOWNED); + + if (phy < 0 || phy >= 32) + return (ENXIO); + if (reg < 0 || reg >= 32) + return (ENXIO); + + IP17X_LOCK(sc); + err = MDIO_WRITEREG(device_get_parent(dev), phy, reg, data); + IP17X_UNLOCK(sc); + + return (err); +} + +int +ip17x_updatephy(device_t dev, int phy, int reg, int mask, int value) +{ + int val; + + val = ip17x_readphy(dev, phy, reg); + val &= ~mask; + val |= value; + return (ip17x_writephy(dev, phy, reg, val)); +} diff --git a/sys/dev/etherswitch/ip17x/ip17x_phy.h b/sys/dev/etherswitch/ip17x/ip17x_phy.h new file mode 100644 index 000000000000..e2638aefda60 --- /dev/null +++ b/sys/dev/etherswitch/ip17x/ip17x_phy.h @@ -0,0 +1,38 @@ +/*- + * Copyright (c) 2013 Luiz Otavio O Souza. + * Copyright (c) 2011-2012 Stefan Bethke. + * Copyright (c) 2012 Adrian Chadd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef __IP17X_PHY_H__ +#define __IP17X_PHY_H__ + +int ip17x_readphy(device_t, int, int); +int ip17x_writephy(device_t, int, int, int); +int ip17x_updatephy(device_t, int, int, int, int); + +#endif /* __IP17X_PHY_H__ */ diff --git a/sys/dev/etherswitch/ip17x/ip17x_reg.h b/sys/dev/etherswitch/ip17x/ip17x_reg.h new file mode 100644 index 000000000000..09b119669c8c --- /dev/null +++ b/sys/dev/etherswitch/ip17x/ip17x_reg.h @@ -0,0 +1,44 @@ +/*- + * Copyright (c) 2013 Luiz Otavio O Souza. + * Copyright (c) 2011-2012 Stefan Bethke. + * Copyright (c) 2012 Adrian Chadd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef __IP17X_REG_H__ +#define __IP17X_REG_H__ + +/* IP175X */ +#define IP17X_OUI 0x9c3 +#define IP17X_IP175A 0x05 +#define IP17X_IP175C 0x18 + +#define IP17X_MAX_VLANS 16 + +#define IP175X_CPU_PORT 5 +#define IP175X_NUM_PORTS 6 + +#endif /* __IP17X_REG_H__ */ diff --git a/sys/dev/etherswitch/ip17x/ip17x_var.h b/sys/dev/etherswitch/ip17x/ip17x_var.h new file mode 100644 index 000000000000..2259a655eb01 --- /dev/null +++ b/sys/dev/etherswitch/ip17x/ip17x_var.h @@ -0,0 +1,95 @@ +/*- + * Copyright (c) 2013 Luiz Otavio O Souza. + * Copyright (c) 2011-2012 Stefan Bethke. + * Copyright (c) 2012 Adrian Chadd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef __IP17X_VAR_H__ +#define __IP17X_VAR_H__ + +typedef enum { + IP17X_SWITCH_NONE, + IP17X_SWITCH_IP175A, + IP17X_SWITCH_IP175C, + IP17X_SWITCH_IP175D, + IP17X_SWITCH_IP178C, +} ip17x_switch_type; + +struct ip17x_vlan { + uint32_t ports; + int vlanid; +}; + +struct ip17x_softc { + device_t sc_dev; + int media; /* cpu port media */ + int cpuport; /* which PHY is connected to the CPU */ + int phymask; /* PHYs we manage */ + int phyport[MII_NPHY]; + int numports; /* number of ports */ + int *portphy; + char **ifname; + device_t **miibus; + etherswitch_info_t info; + ip17x_switch_type sc_switchtype; + struct callout callout_tick; + struct ifnet **ifp; + struct mtx sc_mtx; /* serialize access to softc */ + + struct ip17x_vlan vlan[IP17X_MAX_VLANS]; + uint32_t *pvid; /* PVID */ + uint32_t addtag; /* per port add tag flag */ + uint32_t striptag; /* per port strip tag flag */ + uint32_t vlan_mode; /* VLAN mode */ + + struct { + int (* ip17x_reset) (struct ip17x_softc *); + int (* ip17x_hw_setup) (struct ip17x_softc *); + int (* ip17x_get_vlan_mode) (struct ip17x_softc *); + int (* ip17x_set_vlan_mode) (struct ip17x_softc *, uint32_t); + } hal; +}; + +#define IP17X_IS_SWITCH(_sc, _type) \ + (!!((_sc)->sc_switchtype == IP17X_SWITCH_ ## _type)) + +#define IP17X_LOCK(_sc) \ + mtx_lock(&(_sc)->sc_mtx) +#define IP17X_UNLOCK(_sc) \ + mtx_unlock(&(_sc)->sc_mtx) +#define IP17X_LOCK_ASSERT(_sc, _what) \ + mtx_assert(&(_sc)->sc_mtx, (_what)) +#define IP17X_TRYLOCK(_sc) \ + mtx_trylock(&(_sc)->sc_mtx) + +#if defined(DEBUG) +#define DPRINTF(dev, args...) device_printf(dev, args) +#else +#define DPRINTF(dev, args...) +#endif + +#endif /* __IP17X_VAR_H__ */ diff --git a/sys/dev/etherswitch/ip17x/ip17x_vlans.c b/sys/dev/etherswitch/ip17x/ip17x_vlans.c new file mode 100644 index 000000000000..80bfc8bee40f --- /dev/null +++ b/sys/dev/etherswitch/ip17x/ip17x_vlans.c @@ -0,0 +1,167 @@ +/*- + * Copyright (c) 2013 Luiz Otavio O Souza. + * Copyright (c) 2011-2012 Stefan Bethke. + * Copyright (c) 2012 Adrian Chadd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/errno.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/socket.h> + +#include <net/if.h> + +#include <dev/mii/mii.h> + +#include <dev/etherswitch/etherswitch.h> +#include <dev/etherswitch/ip17x/ip17x_phy.h> +#include <dev/etherswitch/ip17x/ip17x_reg.h> +#include <dev/etherswitch/ip17x/ip17x_var.h> +#include <dev/etherswitch/ip17x/ip17x_vlans.h> +#include <dev/etherswitch/ip17x/ip175c.h> + +#include "mdio_if.h" +#include "miibus_if.h" +#include "etherswitch_if.h" + +/* + * Reset vlans to default state. + */ +int +ip17x_reset_vlans(struct ip17x_softc *sc, uint32_t vlan_mode) +{ + struct ip17x_vlan *v; + int i, j, phy; + + /* Do not add or strip vlan tags on any port. */ + sc->addtag = 0; + sc->striptag = 0; + + /* Reset all vlan data. */ + memset(sc->vlan, 0, sizeof(sc->vlan)); + memset(sc->pvid, 0, sizeof(uint32_t) * sc->numports); + + if (vlan_mode == ETHERSWITCH_VLAN_PORT) { + + /* Initialize port based vlans. */ + for (i = 0, phy = 0; phy < MII_NPHY; phy++) { + if (((1 << phy) & sc->phymask) == 0) + continue; + v = &sc->vlan[i]; + v->vlanid = i++; + v->ports = (1 << sc->cpuport); + for (j = 0; j < MII_NPHY; j++) { + if (((1 << j) & sc->phymask) == 0) + continue; + v->ports |= (1 << j); + } + } + + } else if (vlan_mode == ETHERSWITCH_VLAN_DOT1Q) { + + /* + * Setup vlan 1 as PVID for all switch ports. Add all ports as + * members of vlan 1. + */ + v = &sc->vlan[0]; + v->vlanid = 1; + /* Set PVID for everyone. */ + for (i = 0; i < sc->numports; i++) + sc->pvid[i] = v->vlanid; + for (i = 0; i < MII_NPHY; i++) { + if ((sc->phymask & (1 << i)) == 0) + continue; + v->ports |= (1 << i); + } + } + + return (0); +} + +int +ip17x_getvgroup(device_t dev, etherswitch_vlangroup_t *vg) +{ + struct ip17x_softc *sc; + uint32_t port; + int i; + + sc = device_get_softc(dev); + + /* Vlan ID. */ + vg->es_vid = sc->vlan[vg->es_vlangroup].vlanid; + + /* Member Ports. */ + vg->es_member_ports = 0; + for (i = 0; i < MII_NPHY; i++) { + if ((sc->phymask & (1 << i)) == 0) + continue; + if ((sc->vlan[vg->es_vlangroup].ports & (1 << i)) == 0) + continue; + port = sc->phyport[i]; + vg->es_member_ports |= (1 << port); + } + + /* Not supported. */ + vg->es_untagged_ports = vg->es_member_ports; + vg->es_fid = 0; + + return (0); +} + +int +ip17x_setvgroup(device_t dev, etherswitch_vlangroup_t *vg) +{ + struct ip17x_softc *sc; + uint32_t phy; + int i; + + sc = device_get_softc(dev); + + /* Check VLAN mode. */ + if (sc->vlan_mode == 0) + return (EINVAL); + + /* IP175C don't support VLAN IDs > 15. */ + if (IP17X_IS_SWITCH(sc, IP175C) && vg->es_vid > IP175C_LAST_VLAN) + return (EINVAL); + + /* Vlan ID. */ + sc->vlan[vg->es_vlangroup].vlanid = vg->es_vid; + + /* Member Ports. */ + sc->vlan[vg->es_vlangroup].ports = 0; + for (i = 0; i < sc->numports; i++) { + if ((vg->es_member_ports & (1 << i)) == 0) + continue; + phy = sc->portphy[i]; + sc->vlan[vg->es_vlangroup].ports |= (1 << phy); + } + + return (sc->hal.ip17x_hw_setup(sc)); +} diff --git a/sys/dev/etherswitch/ip17x/ip17x_vlans.h b/sys/dev/etherswitch/ip17x/ip17x_vlans.h new file mode 100644 index 000000000000..705926a48610 --- /dev/null +++ b/sys/dev/etherswitch/ip17x/ip17x_vlans.h @@ -0,0 +1,36 @@ +/*- + * Copyright (c) 2013 Luiz Otavio O Souza. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef __IP17X_VLANS_H__ +#define __IP17X_VLANS_H__ + +int ip17x_reset_vlans(struct ip17x_softc *, uint32_t); +int ip17x_getvgroup(device_t, etherswitch_vlangroup_t *); +int ip17x_setvgroup(device_t, etherswitch_vlangroup_t *); + +#endif /* __IP17X_VLANS_H__ */ |
