From owner-svn-src-all@FreeBSD.ORG Wed Oct 13 09:17:45 2010 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 317A9106566B; Wed, 13 Oct 2010 09:17:45 +0000 (UTC) (envelope-from jmallett@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 1DE7F8FC12; Wed, 13 Oct 2010 09:17:45 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id o9D9Hj3a078777; Wed, 13 Oct 2010 09:17:45 GMT (envelope-from jmallett@svn.freebsd.org) Received: (from jmallett@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id o9D9HjSh078768; Wed, 13 Oct 2010 09:17:45 GMT (envelope-from jmallett@svn.freebsd.org) Message-Id: <201010130917.o9D9HjSh078768@svn.freebsd.org> From: Juli Mallett Date: Wed, 13 Oct 2010 09:17:45 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r213762 - in head/sys/mips: cavium cavium/octe conf X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 13 Oct 2010 09:17:45 -0000 Author: jmallett Date: Wed Oct 13 09:17:44 2010 New Revision: 213762 URL: http://svn.freebsd.org/changeset/base/213762 Log: o) Make it possible to attach a PHY directly to an octe device rather than using miibus, since for some devices that use multiple addresses on the bus, going through miibus may be unclear, and for devices that are not standard MII PHYs, miibus may throw a fit, necessitating complicated interfaces to fake the interface that it expects during probe/attach. o) Make the mv88e61xx SMI interface in octe attach a PHY directly and fix some mistakes in the code that resulted from trying too hard to present a nice interface to miibus. o) Add a PHY driver for the mv88e61xx. If attached (it is optional in kernel compiles so the default behavior of having a dumb switch is preserved) it will place the switch in a VLAN-tagging mode such that each physical port has a VLAN associated with it and interfaces for the VLANs can be created to address or bridge between them. XXX It would be nice for this to be part of a single module including the SMI interface, and for it to fit into a generic switch configuration framework and for it to use DSA rather than VLANs, but this is a start and gives some sense of the parameters of such frameworks that are not currently present in FreeBSD. In lieu of a switch configuration interface, per-port media status and VLAN settings are in a sysctl tree. XXX There may be some minor nits remaining in the handling of broadcast, multicast and unknown destination traffic. It would also be nice to go through and replace the few remaining magic numbers with macros at some point in the future. XXX This has only been tested with the MV88E6161, but it should work with minimal or no modification on related switches, so support for probing them was included. Thanks to Pat Saavedra of TELoIP and Rafal Jaworowski of Semihalf for their assistance in understanding the switch chipset. Added: head/sys/mips/cavium/octe/mv88e61xxphy.c (contents, props changed) head/sys/mips/cavium/octe/mv88e61xxphyreg.h (contents, props changed) Modified: head/sys/mips/cavium/files.octeon1 head/sys/mips/cavium/octe/cavium-ethernet.h head/sys/mips/cavium/octe/ethernet-mdio.c head/sys/mips/cavium/octe/ethernet-mv88e61xx.c head/sys/mips/cavium/octe/octe.c head/sys/mips/conf/OCTEON1 Modified: head/sys/mips/cavium/files.octeon1 ============================================================================== --- head/sys/mips/cavium/files.octeon1 Wed Oct 13 06:28:40 2010 (r213761) +++ head/sys/mips/cavium/files.octeon1 Wed Oct 13 09:17:44 2010 (r213762) @@ -31,6 +31,7 @@ mips/cavium/octe/ethernet-sgmii.c optio mips/cavium/octe/ethernet-spi.c optional octe mips/cavium/octe/ethernet-tx.c optional octe mips/cavium/octe/ethernet-xaui.c optional octe +mips/cavium/octe/mv88e61xxphy.c optional octe mv88e61xxphy mips/cavium/octe/octe.c optional octe mips/cavium/octe/octebus.c optional octe Modified: head/sys/mips/cavium/octe/cavium-ethernet.h ============================================================================== --- head/sys/mips/cavium/octe/cavium-ethernet.h Wed Oct 13 06:28:40 2010 (r213761) +++ head/sys/mips/cavium/octe/cavium-ethernet.h Wed Oct 13 09:17:44 2010 (r213762) @@ -72,6 +72,7 @@ typedef struct { uint8_t mac[6]; int phy_id; + const char *phy_device; int (*mdio_read)(struct ifnet *, int, int); void (*mdio_write)(struct ifnet *, int, int, int); Modified: head/sys/mips/cavium/octe/ethernet-mdio.c ============================================================================== --- head/sys/mips/cavium/octe/ethernet-mdio.c Wed Oct 13 06:28:40 2010 (r213761) +++ head/sys/mips/cavium/octe/ethernet-mdio.c Wed Oct 13 09:17:44 2010 (r213762) @@ -132,6 +132,7 @@ int cvm_oct_mdio_setup_device(struct ifn cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc; priv->phy_id = cvmx_helper_board_get_mii_address(priv->port); + priv->phy_device = NULL; priv->mdio_read = NULL; priv->mdio_write = NULL; Modified: head/sys/mips/cavium/octe/ethernet-mv88e61xx.c ============================================================================== --- head/sys/mips/cavium/octe/ethernet-mv88e61xx.c Wed Oct 13 06:28:40 2010 (r213761) +++ head/sys/mips/cavium/octe/ethernet-mv88e61xx.c Wed Oct 13 09:17:44 2010 (r213762) @@ -49,8 +49,6 @@ __FBSDID("$FreeBSD$"); #include "wrapper-cvmx-includes.h" #include "ethernet-headers.h" -#define MV88E61XX_SMI_PHY_SW 0x10 /* Switch PHY. */ - #define MV88E61XX_SMI_REG_CMD 0x00 /* Indirect command register. */ #define MV88E61XX_SMI_CMD_BUSY 0x8000 /* Busy bit. */ #define MV88E61XX_SMI_CMD_22 0x1000 /* Clause 22 (default 45.) */ @@ -61,89 +59,67 @@ __FBSDID("$FreeBSD$"); #define MV88E61XX_SMI_REG_DAT 0x01 /* Indirect data register. */ -static int cvm_oct_mv88e61xx_mdio_read(struct ifnet *, int, int); -static void cvm_oct_mv88e61xx_mdio_write(struct ifnet *, int, int, int); static int cvm_oct_mv88e61xx_smi_read(struct ifnet *, int, int); static void cvm_oct_mv88e61xx_smi_write(struct ifnet *, int, int, int); -static int cvm_oct_mv88e61xx_smi_wait(struct ifnet *, int); +static int cvm_oct_mv88e61xx_smi_wait(struct ifnet *); int cvm_oct_mv88e61xx_setup_device(struct ifnet *ifp) { cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc; - priv->mdio_read = cvm_oct_mv88e61xx_mdio_read; - priv->mdio_write = cvm_oct_mv88e61xx_mdio_write; + priv->mdio_read = cvm_oct_mv88e61xx_smi_read; + priv->mdio_write = cvm_oct_mv88e61xx_smi_write; + priv->phy_device = "mv88e61xxphy"; return (0); } static int -cvm_oct_mv88e61xx_mdio_read(struct ifnet *ifp, int phy_id, int location) -{ - /* - * Intercept reads of MII_BMSR. The miibus uses this to determine - * PHY presence and we only want it to look for a PHY attachment - * for the switch PHY itself. The PHY driver will talk to all of - * the other ports as need be. - */ - switch (location) { - case MII_BMSR: - if (phy_id != MV88E61XX_SMI_PHY_SW) - return (0); - return (BMSR_EXTSTAT | BMSR_ACOMP | BMSR_LINK); - default: - return (cvm_oct_mv88e61xx_smi_read(ifp, phy_id, location)); - } -} - -static void -cvm_oct_mv88e61xx_mdio_write(struct ifnet *ifp, int phy_id, int location, int val) -{ - return (cvm_oct_mv88e61xx_smi_write(ifp, phy_id, location, val)); -} - -static int cvm_oct_mv88e61xx_smi_read(struct ifnet *ifp, int phy_id, int location) { + cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc; int error; - error = cvm_oct_mv88e61xx_smi_wait(ifp, phy_id); + error = cvm_oct_mv88e61xx_smi_wait(ifp); if (error != 0) return (0); - cvm_oct_mdio_write(ifp, phy_id, MV88E61XX_SMI_REG_CMD, + cvm_oct_mdio_write(ifp, priv->phy_id, MV88E61XX_SMI_REG_CMD, MV88E61XX_SMI_CMD_BUSY | MV88E61XX_SMI_CMD_22 | MV88E61XX_SMI_CMD_READ | MV88E61XX_SMI_CMD_PHY(phy_id) | MV88E61XX_SMI_CMD_REG(location)); - error = cvm_oct_mv88e61xx_smi_wait(ifp, phy_id); + error = cvm_oct_mv88e61xx_smi_wait(ifp); if (error != 0) return (0); - return (cvm_oct_mdio_read(ifp, phy_id, MV88E61XX_SMI_REG_DAT)); + return (cvm_oct_mdio_read(ifp, priv->phy_id, MV88E61XX_SMI_REG_DAT)); } static void cvm_oct_mv88e61xx_smi_write(struct ifnet *ifp, int phy_id, int location, int val) { - cvm_oct_mv88e61xx_smi_wait(ifp, phy_id); - cvm_oct_mdio_write(ifp, phy_id, MV88E61XX_SMI_REG_DAT, val); - cvm_oct_mdio_write(ifp, phy_id, MV88E61XX_SMI_REG_CMD, + cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc; + + cvm_oct_mv88e61xx_smi_wait(ifp); + cvm_oct_mdio_write(ifp, priv->phy_id, MV88E61XX_SMI_REG_DAT, val); + cvm_oct_mdio_write(ifp, priv->phy_id, MV88E61XX_SMI_REG_CMD, MV88E61XX_SMI_CMD_BUSY | MV88E61XX_SMI_CMD_22 | MV88E61XX_SMI_CMD_WRITE | MV88E61XX_SMI_CMD_PHY(phy_id) | MV88E61XX_SMI_CMD_REG(location)); - cvm_oct_mv88e61xx_smi_wait(ifp, phy_id); + cvm_oct_mv88e61xx_smi_wait(ifp); } static int -cvm_oct_mv88e61xx_smi_wait(struct ifnet *ifp, int phy_id) +cvm_oct_mv88e61xx_smi_wait(struct ifnet *ifp) { + cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc; uint16_t cmd; unsigned i; for (i = 0; i < 10000; i++) { - cmd = cvm_oct_mdio_read(ifp, phy_id, MV88E61XX_SMI_REG_CMD); + cmd = cvm_oct_mdio_read(ifp, priv->phy_id, MV88E61XX_SMI_REG_CMD); if ((cmd & MV88E61XX_SMI_CMD_BUSY) == 0) return (0); } Added: head/sys/mips/cavium/octe/mv88e61xxphy.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/mips/cavium/octe/mv88e61xxphy.c Wed Oct 13 09:17:44 2010 (r213762) @@ -0,0 +1,630 @@ +/*- + * Copyright (c) 2010 Juli Mallett + * 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 +__FBSDID("$FreeBSD$"); + +/* + * Driver for the Marvell 88E61xx family of switch PHYs + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "miibus_if.h" + +#include "mv88e61xxphyreg.h" + +struct mv88e61xxphy_softc; + +struct mv88e61xxphy_port_softc { + struct mv88e61xxphy_softc *sc_switch; + unsigned sc_port; + unsigned sc_domain; + unsigned sc_vlan; + unsigned sc_priority; + unsigned sc_flags; +}; + +#define MV88E61XXPHY_PORT_FLAG_VTU_UPDATE (0x0001) + +struct mv88e61xxphy_softc { + device_t sc_dev; + struct mv88e61xxphy_port_softc sc_ports[MV88E61XX_PORTS]; +}; + +enum mv88e61xxphy_vtu_membership_type { + MV88E61XXPHY_VTU_UNMODIFIED, + MV88E61XXPHY_VTU_UNTAGGED, + MV88E61XXPHY_VTU_TAGGED, + MV88E61XXPHY_VTU_DISCARDED, +}; + +enum mv88e61xxphy_sysctl_link_type { + MV88E61XXPHY_LINK_SYSCTL_DUPLEX, + MV88E61XXPHY_LINK_SYSCTL_LINK, + MV88E61XXPHY_LINK_SYSCTL_MEDIA, +}; + +enum mv88e61xxphy_sysctl_port_type { + MV88E61XXPHY_PORT_SYSCTL_DOMAIN, + MV88E61XXPHY_PORT_SYSCTL_VLAN, + MV88E61XXPHY_PORT_SYSCTL_PRIORITY, +}; + +/* + * Register access macros. + */ +#define MV88E61XX_READ(sc, phy, reg) \ + MIIBUS_READREG(device_get_parent((sc)->sc_dev), (phy), (reg)) + +#define MV88E61XX_WRITE(sc, phy, reg, val) \ + MIIBUS_WRITEREG(device_get_parent((sc)->sc_dev), (phy), (reg), (val)) + +#define MV88E61XX_READ_PORT(psc, reg) \ + MV88E61XX_READ((psc)->sc_switch, MV88E61XX_PORT((psc)->sc_port), (reg)) + +#define MV88E61XX_WRITE_PORT(psc, reg, val) \ + MV88E61XX_WRITE((psc)->sc_switch, MV88E61XX_PORT((psc)->sc_port), (reg), (val)) + +static int mv88e61xxphy_probe(device_t); +static int mv88e61xxphy_attach(device_t); + +static void mv88e61xxphy_init(struct mv88e61xxphy_softc *); +static void mv88e61xxphy_init_port(struct mv88e61xxphy_port_softc *); +static void mv88e61xxphy_init_vtu(struct mv88e61xxphy_softc *); +static int mv88e61xxphy_sysctl_link_proc(SYSCTL_HANDLER_ARGS); +static int mv88e61xxphy_sysctl_port_proc(SYSCTL_HANDLER_ARGS); +static void mv88e61xxphy_vtu_load(struct mv88e61xxphy_softc *, uint16_t); +static void mv88e61xxphy_vtu_set_membership(struct mv88e61xxphy_softc *, unsigned, enum mv88e61xxphy_vtu_membership_type); +static void mv88e61xxphy_vtu_wait(struct mv88e61xxphy_softc *); + +static int +mv88e61xxphy_probe(device_t dev) +{ + uint16_t val; + + val = MIIBUS_READREG(device_get_parent(dev), MV88E61XX_PORT(0), + MV88E61XX_PORT_REVISION); + switch (val >> 4) { + case 0x121: + device_set_desc(dev, "Marvell Link Street 88E6123 3-Port Gigabit Switch"); + return (0); + case 0x161: + device_set_desc(dev, "Marvell Link Street 88E6161 6-Port Gigabit Switch"); + return (0); + case 0x165: + device_set_desc(dev, "Marvell Link Street 88E6161 6-Port Advanced Gigabit Switch"); + return (0); + default: + return (ENXIO); + } +} + +static int +mv88e61xxphy_attach(device_t dev) +{ + char portbuf[] = "N"; + struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev); + struct sysctl_oid *tree = device_get_sysctl_tree(dev); + struct sysctl_oid_list *child = SYSCTL_CHILDREN(tree); + struct sysctl_oid *port_node, *portN_node; + struct sysctl_oid_list *port_tree, *portN_tree; + struct mv88e61xxphy_softc *sc; + unsigned port; + + sc = device_get_softc(dev); + sc->sc_dev = dev; + + /* + * Initialize port softcs. + */ + for (port = 0; port < MV88E61XX_PORTS; port++) { + struct mv88e61xxphy_port_softc *psc; + + psc = &sc->sc_ports[port]; + psc->sc_switch = sc; + psc->sc_port = port; + psc->sc_domain = 0; /* One broadcast domain by default. */ + psc->sc_vlan = port + 1; /* Tag VLANs by default. */ + psc->sc_priority = 0; /* No default special priority. */ + psc->sc_flags = 0; + } + + /* + * Add per-port sysctl tree/handlers. + */ + port_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "port", + CTLFLAG_RD, NULL, "Switch Ports"); + port_tree = SYSCTL_CHILDREN(port_node); + for (port = 0; port < MV88E61XX_PORTS; port++) { + struct mv88e61xxphy_port_softc *psc; + + psc = &sc->sc_ports[port]; + + portbuf[0] = '0' + port; + portN_node = SYSCTL_ADD_NODE(ctx, port_tree, OID_AUTO, portbuf, + CTLFLAG_RD, NULL, "Switch Port"); + portN_tree = SYSCTL_CHILDREN(portN_node); + + SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "duplex", + CTLFLAG_RD | CTLTYPE_INT, psc, + MV88E61XXPHY_LINK_SYSCTL_DUPLEX, + mv88e61xxphy_sysctl_link_proc, "IU", + "Media duplex status (0 = half duplex; 1 = full duplex)"); + + SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "link", + CTLFLAG_RD | CTLTYPE_INT, psc, + MV88E61XXPHY_LINK_SYSCTL_LINK, + mv88e61xxphy_sysctl_link_proc, "IU", + "Link status (0 = down; 1 = up)"); + + SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "media", + CTLFLAG_RD | CTLTYPE_INT, psc, + MV88E61XXPHY_LINK_SYSCTL_MEDIA, + mv88e61xxphy_sysctl_link_proc, "IU", + "Media speed (0 = unknown; 10 = 10Mbps; 100 = 100Mbps; 1000 = 1Gbps)"); + + SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "domain", + CTLFLAG_RW | CTLTYPE_INT, psc, + MV88E61XXPHY_PORT_SYSCTL_DOMAIN, + mv88e61xxphy_sysctl_port_proc, "IU", + "Broadcast domain (ports can only talk to other ports in the same domain)"); + + SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "vlan", + CTLFLAG_RW | CTLTYPE_INT, psc, + MV88E61XXPHY_PORT_SYSCTL_VLAN, + mv88e61xxphy_sysctl_port_proc, "IU", + "Tag packets from/for this port with a given VLAN."); + + SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "priority", + CTLFLAG_RW | CTLTYPE_INT, psc, + MV88E61XXPHY_PORT_SYSCTL_PRIORITY, + mv88e61xxphy_sysctl_port_proc, "IU", + "Default packet priority for this port."); + } + + mv88e61xxphy_init(sc); + + return (0); +} + +static void +mv88e61xxphy_init(struct mv88e61xxphy_softc *sc) +{ + unsigned port; + uint16_t val; + unsigned i; + + /* Disable all ports. */ + for (port = 0; port < MV88E61XX_PORTS; port++) { + struct mv88e61xxphy_port_softc *psc; + + psc = &sc->sc_ports[port]; + + val = MV88E61XX_READ_PORT(psc, MV88E61XX_PORT_CONTROL); + val &= ~0x3; + MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL, val); + } + + DELAY(2000); + + /* Reset the switch. */ + MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_CONTROL, 0xc400); + for (i = 0; i < 100; i++) { + val = MV88E61XX_READ(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_STATUS); + if ((val & 0xc800) == 0xc800) + break; + DELAY(10); + } + if (i == 100) { + device_printf(sc->sc_dev, "%s: switch reset timed out.\n", __func__); + return; + } + + /* Disable PPU. */ + MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_CONTROL, 0x0000); + + /* Configure host port and send monitor frames to it. */ + MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_MONITOR, + (MV88E61XX_HOST_PORT << 12) | (MV88E61XX_HOST_PORT << 8) | + (MV88E61XX_HOST_PORT << 4)); + + /* Disable remote management. */ + MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_REMOTE_MGMT, 0x0000); + + /* Send all specifically-addressed frames to the host port. */ + MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL2, MV88E61XX_GLOBAL2_MANAGE_2X, 0xffff); + MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL2, MV88E61XX_GLOBAL2_MANAGE_0X, 0xffff); + + /* Remove provider-supplied tag and use it for switching. */ + MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL2, MV88E61XX_GLOBAL2_CONTROL2, + MV88E61XX_GLOBAL2_CONTROL2_REMOVE_PTAG); + + /* Configure all ports. */ + for (port = 0; port < MV88E61XX_PORTS; port++) { + struct mv88e61xxphy_port_softc *psc; + + psc = &sc->sc_ports[port]; + mv88e61xxphy_init_port(psc); + } + + /* Reprogram VLAN table (VTU.) */ + mv88e61xxphy_init_vtu(sc); + + /* Enable all ports. */ + for (port = 0; port < MV88E61XX_PORTS; port++) { + struct mv88e61xxphy_port_softc *psc; + + psc = &sc->sc_ports[port]; + + val = MV88E61XX_READ_PORT(psc, MV88E61XX_PORT_CONTROL); + val |= 0x3; + MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL, val); + } +} + +static void +mv88e61xxphy_init_port(struct mv88e61xxphy_port_softc *psc) +{ + struct mv88e61xxphy_softc *sc; + unsigned allow_mask; + + sc = psc->sc_switch; + + /* Set media type and flow control. */ + if (psc->sc_port != MV88E61XX_HOST_PORT) { + /* Don't force any media type or flow control. */ + MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_FORCE_MAC, 0x0003); + } else { + /* Make CPU port 1G FDX. */ + MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_FORCE_MAC, 0x003e); + } + + /* Don't limit flow control pauses. */ + MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_PAUSE_CONTROL, 0x0000); + + /* Set various port functions per Linux. */ + if (psc->sc_port != MV88E61XX_HOST_PORT) { + MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL, 0x04bc); + } else { + /* + * Send frames for unknown unicast and multicast groups to + * host, too. + */ + MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL, 0x063f); + } + + if (psc->sc_port != MV88E61XX_HOST_PORT) { + /* Disable trunking. */ + MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL2, 0x0000); + } else { + /* Disable trunking and send learn messages to host. */ + MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL2, 0x8000); + } + + /* + * Port-based VLAN map; isolates MAC tables and forces ports to talk + * only to the host. + * + * Always allow the host to send to all ports and allow all ports to + * send to the host. + */ + if (psc->sc_port != MV88E61XX_HOST_PORT) { + allow_mask = 1 << MV88E61XX_HOST_PORT; + } else { + allow_mask = (1 << MV88E61XX_PORTS) - 1; + allow_mask &= ~(1 << MV88E61XX_HOST_PORT); + } + MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_VLAN_MAP, + (psc->sc_domain << 12) | allow_mask); + + /* VLAN tagging. Set default priority and VLAN tag (or none.) */ + MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_VLAN, + (psc->sc_priority << 14) | psc->sc_vlan); + + if (psc->sc_port == MV88E61XX_HOST_PORT) { + /* Set provider ingress tag. */ + MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_PROVIDER_PROTO, + ETHERTYPE_VLAN); + + /* Set provider egress tag. */ + MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_ETHER_PROTO, + ETHERTYPE_VLAN); + + /* Use secure 802.1q mode and accept only tagged frames. */ + MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_FILTER, + MV88E61XX_PORT_FILTER_MAP_DEST | + MV88E61XX_PORT_FILTER_8021Q_SECURE | + MV88E61XX_PORT_FILTER_DISCARD_UNTAGGED); + } else { + /* Don't allow tagged frames. */ + MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_FILTER, + MV88E61XX_PORT_FILTER_MAP_DEST | + MV88E61XX_PORT_FILTER_DISCARD_TAGGED); + } +} + +static void +mv88e61xxphy_init_vtu(struct mv88e61xxphy_softc *sc) +{ + unsigned port; + + /* + * Start flush of the VTU. + */ + mv88e61xxphy_vtu_wait(sc); + MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_VTU_OP, + MV88E61XX_GLOBAL_VTU_OP_BUSY | MV88E61XX_GLOBAL_VTU_OP_OP_FLUSH); + + /* + * Queue each port's VLAN to be programmed. + */ + for (port = 0; port < MV88E61XX_PORTS; port++) { + struct mv88e61xxphy_port_softc *psc; + + psc = &sc->sc_ports[port]; + psc->sc_flags &= ~MV88E61XXPHY_PORT_FLAG_VTU_UPDATE; + if (psc->sc_vlan == 0) + continue; + psc->sc_flags |= MV88E61XXPHY_PORT_FLAG_VTU_UPDATE; + } + + /* + * Program each VLAN that is in use. + */ + for (port = 0; port < MV88E61XX_PORTS; port++) { + struct mv88e61xxphy_port_softc *psc; + + psc = &sc->sc_ports[port]; + if ((psc->sc_flags & MV88E61XXPHY_PORT_FLAG_VTU_UPDATE) == 0) + continue; + mv88e61xxphy_vtu_load(sc, psc->sc_vlan); + } + + /* + * Wait for last pending VTU operation to complete. + */ + mv88e61xxphy_vtu_wait(sc); +} + +static int +mv88e61xxphy_sysctl_link_proc(SYSCTL_HANDLER_ARGS) +{ + struct mv88e61xxphy_port_softc *psc = arg1; + enum mv88e61xxphy_sysctl_link_type type = arg2; + uint16_t val; + unsigned out; + + val = MV88E61XX_READ_PORT(psc, MV88E61XX_PORT_STATUS); + switch (type) { + case MV88E61XXPHY_LINK_SYSCTL_DUPLEX: + if ((val & MV88E61XX_PORT_STATUS_DUPLEX) != 0) + out = 1; + else + out = 0; + break; + case MV88E61XXPHY_LINK_SYSCTL_LINK: + if ((val & MV88E61XX_PORT_STATUS_LINK) != 0) + out = 1; + else + out = 0; + break; + case MV88E61XXPHY_LINK_SYSCTL_MEDIA: + switch (val & MV88E61XX_PORT_STATUS_MEDIA) { + case MV88E61XX_PORT_STATUS_MEDIA_10M: + out = 10; + break; + case MV88E61XX_PORT_STATUS_MEDIA_100M: + out = 100; + break; + case MV88E61XX_PORT_STATUS_MEDIA_1G: + out = 1000; + break; + default: + out = 0; + break; + } + break; + default: + return (EINVAL); + } + return (sysctl_handle_int(oidp, NULL, out, req)); +} + +static int +mv88e61xxphy_sysctl_port_proc(SYSCTL_HANDLER_ARGS) +{ + struct mv88e61xxphy_port_softc *psc = arg1; + enum mv88e61xxphy_sysctl_port_type type = arg2; + struct mv88e61xxphy_softc *sc = psc->sc_switch; + unsigned max, val, *valp; + int error; + + switch (type) { + case MV88E61XXPHY_PORT_SYSCTL_DOMAIN: + valp = &psc->sc_domain; + max = 0xf; + break; + case MV88E61XXPHY_PORT_SYSCTL_VLAN: + valp = &psc->sc_vlan; + max = 0x1000; + break; + case MV88E61XXPHY_PORT_SYSCTL_PRIORITY: + valp = &psc->sc_priority; + max = 3; + break; + default: + return (EINVAL); + } + + val = *valp; + error = sysctl_handle_int(oidp, &val, 0, req); + if (error != 0 || req->newptr == NULL) + return (error); + + /* Bounds check value. */ + if (val >= max) + return (EINVAL); + + /* Reinitialize switch with new value. */ + *valp = val; + mv88e61xxphy_init(sc); + + return (0); +} + +static void +mv88e61xxphy_vtu_load(struct mv88e61xxphy_softc *sc, uint16_t vid) +{ + unsigned port; + + /* + * Wait for previous operation to complete. + */ + mv88e61xxphy_vtu_wait(sc); + + /* + * Set VID. + */ + MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_VTU_VID, + MV88E61XX_GLOBAL_VTU_VID_VALID | vid); + + /* + * Add ports to this VTU. + */ + for (port = 0; port < MV88E61XX_PORTS; port++) { + struct mv88e61xxphy_port_softc *psc; + + psc = &sc->sc_ports[port]; + if (psc->sc_vlan == vid) { + /* + * Send this port its VLAN traffic untagged. + */ + psc->sc_flags &= ~MV88E61XXPHY_PORT_FLAG_VTU_UPDATE; + mv88e61xxphy_vtu_set_membership(sc, port, MV88E61XXPHY_VTU_UNTAGGED); + } else if (psc->sc_port == MV88E61XX_HOST_PORT) { + /* + * The host sees all VLANs tagged. + */ + mv88e61xxphy_vtu_set_membership(sc, port, MV88E61XXPHY_VTU_TAGGED); + } else { + /* + * This port isn't on this VLAN. + */ + mv88e61xxphy_vtu_set_membership(sc, port, MV88E61XXPHY_VTU_DISCARDED); + } + } + + /* + * Start adding this entry. + */ + MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_VTU_OP, + MV88E61XX_GLOBAL_VTU_OP_BUSY | + MV88E61XX_GLOBAL_VTU_OP_OP_VTU_LOAD); +} + +static void +mv88e61xxphy_vtu_set_membership(struct mv88e61xxphy_softc *sc, unsigned port, + enum mv88e61xxphy_vtu_membership_type type) +{ + unsigned shift, reg; + uint16_t bits; + uint16_t val; + + switch (type) { + case MV88E61XXPHY_VTU_UNMODIFIED: + bits = 0; + break; + case MV88E61XXPHY_VTU_UNTAGGED: + bits = 1; + break; + case MV88E61XXPHY_VTU_TAGGED: + bits = 2; + break; + case MV88E61XXPHY_VTU_DISCARDED: + bits = 3; + break; + default: + return; + } + + if (port < 4) { + reg = MV88E61XX_GLOBAL_VTU_DATA_P0P3; + shift = port * 4; + } else { + reg = MV88E61XX_GLOBAL_VTU_DATA_P4P5; + shift = (port - 4) * 4; + } + + val = MV88E61XX_READ(sc, MV88E61XX_GLOBAL, reg); + val |= bits << shift; + MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, reg, val); +} + +static void +mv88e61xxphy_vtu_wait(struct mv88e61xxphy_softc *sc) +{ + uint16_t val; + + for (;;) { + val = MV88E61XX_READ(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_VTU_OP); + if ((val & MV88E61XX_GLOBAL_VTU_OP_BUSY) == 0) + return; + } +} + +static device_method_t mv88e61xxphy_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, mv88e61xxphy_probe), + DEVMETHOD(device_attach, mv88e61xxphy_attach), + DEVMETHOD(device_detach, bus_generic_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + { 0, 0 } +}; + +static devclass_t mv88e61xxphy_devclass; + +static driver_t mv88e61xxphy_driver = { + "mv88e61xxphy", + mv88e61xxphy_methods, + sizeof(struct mv88e61xxphy_softc) +}; + +DRIVER_MODULE(mv88e61xxphy, octe, mv88e61xxphy_driver, mv88e61xxphy_devclass, 0, 0); Added: head/sys/mips/cavium/octe/mv88e61xxphyreg.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/mips/cavium/octe/mv88e61xxphyreg.h Wed Oct 13 09:17:44 2010 (r213762) @@ -0,0 +1,149 @@ +/*- + * Copyright (c) 2010 Juli Mallett + * 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$ + */ + +/* + * Register definitions for Marvell MV88E61XX + * + * Note that names and definitions were gleaned from Linux and U-Boot patches + * released by Marvell, often by looking at contextual use of the registers + * involved, and may not be representative of the full functionality of those + * registers and are certainly not an exhaustive enumeration of registers. + * + * For an exhaustive enumeration of registers, check out the QD-DSDT package + * included in the Marvell ARM Feroceon Board Support Package for Linux. + */ + +#ifndef _MIPS_CAVIUM_OCTE_MV88E61XXPHYREG_H_ +#define _MIPS_CAVIUM_OCTE_MV88E61XXPHYREG_H_ + +/* + * Port addresses & per-port registers. + */ +#define MV88E61XX_PORT(x) (0x10 + (x)) +#define MV88E61XX_HOST_PORT (5) +#define MV88E61XX_PORTS (6) + +#define MV88E61XX_PORT_STATUS (0x00) +#define MV88E61XX_PORT_FORCE_MAC (0x01) +#define MV88E61XX_PORT_PAUSE_CONTROL (0x02) +#define MV88E61XX_PORT_REVISION (0x03) +#define MV88E61XX_PORT_CONTROL (0x04) +#define MV88E61XX_PORT_CONTROL2 (0x05) +#define MV88E61XX_PORT_VLAN_MAP (0x06) +#define MV88E61XX_PORT_VLAN (0x07) +#define MV88E61XX_PORT_FILTER (0x08) +#define MV88E61XX_PORT_EGRESS_CONTROL (0x09) +#define MV88E61XX_PORT_EGRESS_CONTROL2 (0x0a) +#define MV88E61XX_PORT_PORT_LEARN (0x0b) +#define MV88E61XX_PORT_ATU_CONTROL (0x0c) +#define MV88E61XX_PORT_PRIORITY_CONTROL (0x0d) +#define MV88E61XX_PORT_ETHER_PROTO (0x0f) +#define MV88E61XX_PORT_PROVIDER_PROTO (0x1a) +#define MV88E61XX_PORT_PRIORITY_MAP (0x18) +#define MV88E61XX_PORT_PRIORITY_MAP2 (0x19) + +/* + * Fields and values in each register. + */ +#define MV88E61XX_PORT_STATUS_MEDIA (0x0300) +#define MV88E61XX_PORT_STATUS_MEDIA_10M (0x0000) +#define MV88E61XX_PORT_STATUS_MEDIA_100M (0x0100) +#define MV88E61XX_PORT_STATUS_MEDIA_1G (0x0200) +#define MV88E61XX_PORT_STATUS_DUPLEX (0x0400) +#define MV88E61XX_PORT_STATUS_LINK (0x0800) +#define MV88E61XX_PORT_STATUS_FC (0x8000) + +#define MV88E61XX_PORT_CONTROL_DOUBLE_TAG (0x0200) + +#define MV88E61XX_PORT_FILTER_MAP_DEST (0x0080) +#define MV88E61XX_PORT_FILTER_DISCARD_UNTAGGED (0x0100) +#define MV88E61XX_PORT_FILTER_DISCARD_TAGGED (0x0200) +#define MV88E61XX_PORT_FILTER_8021Q_MODE (0x0c00) +#define MV88E61XX_PORT_FILTER_8021Q_DISABLED (0x0000) +#define MV88E61XX_PORT_FILTER_8021Q_FALLBACK (0x0400) +#define MV88E61XX_PORT_FILTER_8021Q_CHECK (0x0800) +#define MV88E61XX_PORT_FILTER_8021Q_SECURE (0x0c00) + +/* + * Global address & global registers. + */ +#define MV88E61XX_GLOBAL (0x1b) + +#define MV88E61XX_GLOBAL_STATUS (0x00) +#define MV88E61XX_GLOBAL_CONTROL (0x04) +#define MV88E61XX_GLOBAL_VTU_OP (0x05) +#define MV88E61XX_GLOBAL_VTU_VID (0x06) +#define MV88E61XX_GLOBAL_VTU_DATA_P0P3 (0x07) +#define MV88E61XX_GLOBAL_VTU_DATA_P4P5 (0x08) +#define MV88E61XX_GLOBAL_ATU_CONTROL (0x0a) +#define MV88E61XX_GLOBAL_PRIORITY_MAP (0x18) +#define MV88E61XX_GLOBAL_MONITOR (0x1a) +#define MV88E61XX_GLOBAL_REMOTE_MGMT (0x1c) +#define MV88E61XX_GLOBAL_STATS (0x1d) + +/* + * Fields and values in each register. + */ +#define MV88E61XX_GLOBAL_VTU_OP_BUSY (0x8000) +#define MV88E61XX_GLOBAL_VTU_OP_OP (0x7000) +#define MV88E61XX_GLOBAL_VTU_OP_OP_FLUSH (0x1000) +#define MV88E61XX_GLOBAL_VTU_OP_OP_VTU_LOAD (0x3000) + +#define MV88E61XX_GLOBAL_VTU_VID_VALID (0x1000) + +/* + * Second global address & second global registers. + */ +#define MV88E61XX_GLOBAL2 (0x1c) + +#define MV88E61XX_GLOBAL2_MANAGE_2X (0x02) +#define MV88E61XX_GLOBAL2_MANAGE_0X (0x03) +#define MV88E61XX_GLOBAL2_CONTROL2 (0x05) +#define MV88E61XX_GLOBAL2_TRUNK_MASK (0x07) +#define MV88E61XX_GLOBAL2_TRUNK_MAP (0x08) +#define MV88E61XX_GLOBAL2_RATELIMIT (0x09) +#define MV88E61XX_GLOBAL2_VLAN_CONTROL (0x0b) +#define MV88E61XX_GLOBAL2_MAC_ADDRESS (0x0d) + +/* + * Fields and values in each register. + */ +#define MV88E61XX_GLOBAL2_CONTROL2_DOUBLE_USE (0x8000) +#define MV88E61XX_GLOBAL2_CONTROL2_LOOP_PREVENT (0x4000) +#define MV88E61XX_GLOBAL2_CONTROL2_FLOW_MESSAGE (0x2000) +#define MV88E61XX_GLOBAL2_CONTROL2_FLOOD_BC (0x1000) +#define MV88E61XX_GLOBAL2_CONTROL2_REMOVE_PTAG (0x0800) +#define MV88E61XX_GLOBAL2_CONTROL2_AGE_INT (0x0400) +#define MV88E61XX_GLOBAL2_CONTROL2_FLOW_TAG (0x0200) +#define MV88E61XX_GLOBAL2_CONTROL2_ALWAYS_VTU (0x0100) +#define MV88E61XX_GLOBAL2_CONTROL2_FORCE_FC_PRI (0x0080) +#define MV88E61XX_GLOBAL2_CONTROL2_FC_PRI (0x0070) +#define MV88E61XX_GLOBAL2_CONTROL2_MGMT_TO_HOST (0x0008) +#define MV88E61XX_GLOBAL2_CONTROL2_MGMT_PRI (0x0007) + +#endif /* !_MIPS_CAVIUM_OCTE_MV88E61XXPHYREG_H_ */ Modified: head/sys/mips/cavium/octe/octe.c ============================================================================== --- head/sys/mips/cavium/octe/octe.c Wed Oct 13 06:28:40 2010 (r213761) +++ head/sys/mips/cavium/octe/octe.c Wed Oct 13 09:17:44 2010 (r213762) @@ -146,6 +146,7 @@ octe_attach(device_t dev) { struct ifnet *ifp; cvm_oct_private_t *priv; + device_t child; unsigned qos; int error; @@ -155,10 +156,15 @@ octe_attach(device_t dev) if_initname(ifp, device_get_name(dev), device_get_unit(dev)); if (priv->phy_id != -1) { - error = mii_phy_probe(dev, &priv->miibus, octe_mii_medchange, - octe_mii_medstat); - if (error != 0) { - device_printf(dev, "missing phy %u\n", priv->phy_id); + if (priv->phy_device == NULL) { + error = mii_phy_probe(dev, &priv->miibus, octe_mii_medchange, + octe_mii_medstat); + if (error != 0) + device_printf(dev, "missing phy %u\n", priv->phy_id); + } else { + child = device_add_child(dev, priv->phy_device, -1); + if (child == NULL) + device_printf(dev, "missing phy %u device %s\n", priv->phy_id, priv->phy_device); } } @@ -202,7 +208,7 @@ octe_attach(device_t dev) IFQ_SET_READY(&ifp->if_snd); OCTE_TX_UNLOCK(priv); - return (0); + return (bus_generic_attach(dev)); } static int Modified: head/sys/mips/conf/OCTEON1 ============================================================================== --- head/sys/mips/conf/OCTEON1 Wed Oct 13 06:28:40 2010 (r213761) +++ head/sys/mips/conf/OCTEON1 Wed Oct 13 09:17:44 2010 (r213762) @@ -180,6 +180,10 @@ device uart # Generic UART driver # NOTE: Be sure to keep the 'device miibus' line in order to use these NICs! device octe +# Switch PHY support for the octe driver. These currently present a VLAN per +# physical port, but may eventually provide support for DSA or similar instead. +#device mv88e61xxphy # Marvell 88E61XX + # PCI Ethernet NICs. device de # DEC/Intel DC21x4x (``Tulip'') device em # Intel PRO/1000 Gigabit Ethernet Family