svn commit: r213762 - in head/sys/mips: cavium cavium/octe conf

Juli Mallett jmallett at FreeBSD.org
Wed Oct 13 09:17:45 UTC 2010


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 <jmallett at FreeBSD.org>
+ * 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Driver for the Marvell 88E61xx family of switch PHYs
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/sysctl.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_media.h>
+
+#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 <jmallett at FreeBSD.org>
+ * 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


More information about the svn-src-all mailing list