1213762Sjmallett/*- 2213762Sjmallett * Copyright (c) 2010 Juli Mallett <jmallett@FreeBSD.org> 3213762Sjmallett * All rights reserved. 4213762Sjmallett * 5213762Sjmallett * Redistribution and use in source and binary forms, with or without 6213762Sjmallett * modification, are permitted provided that the following conditions 7213762Sjmallett * are met: 8213762Sjmallett * 1. Redistributions of source code must retain the above copyright 9213762Sjmallett * notice, this list of conditions and the following disclaimer. 10213762Sjmallett * 2. Redistributions in binary form must reproduce the above copyright 11213762Sjmallett * notice, this list of conditions and the following disclaimer in the 12213762Sjmallett * documentation and/or other materials provided with the distribution. 13213762Sjmallett * 14213762Sjmallett * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15213762Sjmallett * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16213762Sjmallett * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17213762Sjmallett * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18213762Sjmallett * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19213762Sjmallett * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20213762Sjmallett * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21213762Sjmallett * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22213762Sjmallett * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23213762Sjmallett * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24213762Sjmallett * SUCH DAMAGE. 25213762Sjmallett * 26213762Sjmallett * $FreeBSD$ 27213762Sjmallett */ 28213762Sjmallett 29213762Sjmallett#include <sys/cdefs.h> 30213762Sjmallett__FBSDID("$FreeBSD$"); 31213762Sjmallett 32213762Sjmallett/* 33213762Sjmallett * Driver for the Marvell 88E61xx family of switch PHYs 34213762Sjmallett */ 35213762Sjmallett 36213762Sjmallett#include <sys/param.h> 37213762Sjmallett#include <sys/systm.h> 38213762Sjmallett#include <sys/kernel.h> 39213762Sjmallett#include <sys/socket.h> 40213762Sjmallett#include <sys/errno.h> 41213762Sjmallett#include <sys/module.h> 42213762Sjmallett#include <sys/bus.h> 43213762Sjmallett#include <sys/sysctl.h> 44213762Sjmallett 45213762Sjmallett#include <net/ethernet.h> 46213762Sjmallett#include <net/if.h> 47213762Sjmallett#include <net/if_media.h> 48213762Sjmallett 49213762Sjmallett#include "miibus_if.h" 50213762Sjmallett 51213762Sjmallett#include "mv88e61xxphyreg.h" 52213762Sjmallett 53213762Sjmallettstruct mv88e61xxphy_softc; 54213762Sjmallett 55213762Sjmallettstruct mv88e61xxphy_port_softc { 56213762Sjmallett struct mv88e61xxphy_softc *sc_switch; 57213762Sjmallett unsigned sc_port; 58213762Sjmallett unsigned sc_domain; 59213762Sjmallett unsigned sc_vlan; 60213762Sjmallett unsigned sc_priority; 61213762Sjmallett unsigned sc_flags; 62213762Sjmallett}; 63213762Sjmallett 64213762Sjmallett#define MV88E61XXPHY_PORT_FLAG_VTU_UPDATE (0x0001) 65213762Sjmallett 66213762Sjmallettstruct mv88e61xxphy_softc { 67213762Sjmallett device_t sc_dev; 68213762Sjmallett struct mv88e61xxphy_port_softc sc_ports[MV88E61XX_PORTS]; 69213762Sjmallett}; 70213762Sjmallett 71213762Sjmallettenum mv88e61xxphy_vtu_membership_type { 72213762Sjmallett MV88E61XXPHY_VTU_UNMODIFIED, 73213762Sjmallett MV88E61XXPHY_VTU_UNTAGGED, 74213762Sjmallett MV88E61XXPHY_VTU_TAGGED, 75213762Sjmallett MV88E61XXPHY_VTU_DISCARDED, 76213762Sjmallett}; 77213762Sjmallett 78213762Sjmallettenum mv88e61xxphy_sysctl_link_type { 79213762Sjmallett MV88E61XXPHY_LINK_SYSCTL_DUPLEX, 80213762Sjmallett MV88E61XXPHY_LINK_SYSCTL_LINK, 81213762Sjmallett MV88E61XXPHY_LINK_SYSCTL_MEDIA, 82213762Sjmallett}; 83213762Sjmallett 84213762Sjmallettenum mv88e61xxphy_sysctl_port_type { 85213762Sjmallett MV88E61XXPHY_PORT_SYSCTL_DOMAIN, 86213762Sjmallett MV88E61XXPHY_PORT_SYSCTL_VLAN, 87213762Sjmallett MV88E61XXPHY_PORT_SYSCTL_PRIORITY, 88213762Sjmallett}; 89213762Sjmallett 90213762Sjmallett/* 91213762Sjmallett * Register access macros. 92213762Sjmallett */ 93213762Sjmallett#define MV88E61XX_READ(sc, phy, reg) \ 94213762Sjmallett MIIBUS_READREG(device_get_parent((sc)->sc_dev), (phy), (reg)) 95213762Sjmallett 96213762Sjmallett#define MV88E61XX_WRITE(sc, phy, reg, val) \ 97213762Sjmallett MIIBUS_WRITEREG(device_get_parent((sc)->sc_dev), (phy), (reg), (val)) 98213762Sjmallett 99213762Sjmallett#define MV88E61XX_READ_PORT(psc, reg) \ 100213762Sjmallett MV88E61XX_READ((psc)->sc_switch, MV88E61XX_PORT((psc)->sc_port), (reg)) 101213762Sjmallett 102213762Sjmallett#define MV88E61XX_WRITE_PORT(psc, reg, val) \ 103213762Sjmallett MV88E61XX_WRITE((psc)->sc_switch, MV88E61XX_PORT((psc)->sc_port), (reg), (val)) 104213762Sjmallett 105213762Sjmallettstatic int mv88e61xxphy_probe(device_t); 106213762Sjmallettstatic int mv88e61xxphy_attach(device_t); 107213762Sjmallett 108213762Sjmallettstatic void mv88e61xxphy_init(struct mv88e61xxphy_softc *); 109213762Sjmallettstatic void mv88e61xxphy_init_port(struct mv88e61xxphy_port_softc *); 110213762Sjmallettstatic void mv88e61xxphy_init_vtu(struct mv88e61xxphy_softc *); 111213762Sjmallettstatic int mv88e61xxphy_sysctl_link_proc(SYSCTL_HANDLER_ARGS); 112213762Sjmallettstatic int mv88e61xxphy_sysctl_port_proc(SYSCTL_HANDLER_ARGS); 113213762Sjmallettstatic void mv88e61xxphy_vtu_load(struct mv88e61xxphy_softc *, uint16_t); 114213762Sjmallettstatic void mv88e61xxphy_vtu_set_membership(struct mv88e61xxphy_softc *, unsigned, enum mv88e61xxphy_vtu_membership_type); 115213762Sjmallettstatic void mv88e61xxphy_vtu_wait(struct mv88e61xxphy_softc *); 116213762Sjmallett 117213762Sjmallettstatic int 118213762Sjmallettmv88e61xxphy_probe(device_t dev) 119213762Sjmallett{ 120213762Sjmallett uint16_t val; 121213762Sjmallett 122213762Sjmallett val = MIIBUS_READREG(device_get_parent(dev), MV88E61XX_PORT(0), 123213762Sjmallett MV88E61XX_PORT_REVISION); 124213762Sjmallett switch (val >> 4) { 125213762Sjmallett case 0x121: 126213762Sjmallett device_set_desc(dev, "Marvell Link Street 88E6123 3-Port Gigabit Switch"); 127213762Sjmallett return (0); 128213762Sjmallett case 0x161: 129213762Sjmallett device_set_desc(dev, "Marvell Link Street 88E6161 6-Port Gigabit Switch"); 130213762Sjmallett return (0); 131213762Sjmallett case 0x165: 132213762Sjmallett device_set_desc(dev, "Marvell Link Street 88E6161 6-Port Advanced Gigabit Switch"); 133213762Sjmallett return (0); 134213762Sjmallett default: 135213762Sjmallett return (ENXIO); 136213762Sjmallett } 137213762Sjmallett} 138213762Sjmallett 139213762Sjmallettstatic int 140213762Sjmallettmv88e61xxphy_attach(device_t dev) 141213762Sjmallett{ 142213762Sjmallett char portbuf[] = "N"; 143213762Sjmallett struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev); 144213762Sjmallett struct sysctl_oid *tree = device_get_sysctl_tree(dev); 145213762Sjmallett struct sysctl_oid_list *child = SYSCTL_CHILDREN(tree); 146213762Sjmallett struct sysctl_oid *port_node, *portN_node; 147213762Sjmallett struct sysctl_oid_list *port_tree, *portN_tree; 148213762Sjmallett struct mv88e61xxphy_softc *sc; 149213762Sjmallett unsigned port; 150213762Sjmallett 151213762Sjmallett sc = device_get_softc(dev); 152213762Sjmallett sc->sc_dev = dev; 153213762Sjmallett 154213762Sjmallett /* 155213762Sjmallett * Initialize port softcs. 156213762Sjmallett */ 157213762Sjmallett for (port = 0; port < MV88E61XX_PORTS; port++) { 158213762Sjmallett struct mv88e61xxphy_port_softc *psc; 159213762Sjmallett 160213762Sjmallett psc = &sc->sc_ports[port]; 161213762Sjmallett psc->sc_switch = sc; 162213762Sjmallett psc->sc_port = port; 163213762Sjmallett psc->sc_domain = 0; /* One broadcast domain by default. */ 164213762Sjmallett psc->sc_vlan = port + 1; /* Tag VLANs by default. */ 165213762Sjmallett psc->sc_priority = 0; /* No default special priority. */ 166213762Sjmallett psc->sc_flags = 0; 167213762Sjmallett } 168213762Sjmallett 169213762Sjmallett /* 170213762Sjmallett * Add per-port sysctl tree/handlers. 171213762Sjmallett */ 172213762Sjmallett port_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "port", 173213762Sjmallett CTLFLAG_RD, NULL, "Switch Ports"); 174213762Sjmallett port_tree = SYSCTL_CHILDREN(port_node); 175213762Sjmallett for (port = 0; port < MV88E61XX_PORTS; port++) { 176213762Sjmallett struct mv88e61xxphy_port_softc *psc; 177213762Sjmallett 178213762Sjmallett psc = &sc->sc_ports[port]; 179213762Sjmallett 180213762Sjmallett portbuf[0] = '0' + port; 181213762Sjmallett portN_node = SYSCTL_ADD_NODE(ctx, port_tree, OID_AUTO, portbuf, 182213762Sjmallett CTLFLAG_RD, NULL, "Switch Port"); 183213762Sjmallett portN_tree = SYSCTL_CHILDREN(portN_node); 184213762Sjmallett 185213762Sjmallett SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "duplex", 186213762Sjmallett CTLFLAG_RD | CTLTYPE_INT, psc, 187213762Sjmallett MV88E61XXPHY_LINK_SYSCTL_DUPLEX, 188213762Sjmallett mv88e61xxphy_sysctl_link_proc, "IU", 189213762Sjmallett "Media duplex status (0 = half duplex; 1 = full duplex)"); 190213762Sjmallett 191213762Sjmallett SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "link", 192213762Sjmallett CTLFLAG_RD | CTLTYPE_INT, psc, 193213762Sjmallett MV88E61XXPHY_LINK_SYSCTL_LINK, 194213762Sjmallett mv88e61xxphy_sysctl_link_proc, "IU", 195213762Sjmallett "Link status (0 = down; 1 = up)"); 196213762Sjmallett 197213762Sjmallett SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "media", 198213762Sjmallett CTLFLAG_RD | CTLTYPE_INT, psc, 199213762Sjmallett MV88E61XXPHY_LINK_SYSCTL_MEDIA, 200213762Sjmallett mv88e61xxphy_sysctl_link_proc, "IU", 201213762Sjmallett "Media speed (0 = unknown; 10 = 10Mbps; 100 = 100Mbps; 1000 = 1Gbps)"); 202213762Sjmallett 203213762Sjmallett SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "domain", 204213762Sjmallett CTLFLAG_RW | CTLTYPE_INT, psc, 205213762Sjmallett MV88E61XXPHY_PORT_SYSCTL_DOMAIN, 206213762Sjmallett mv88e61xxphy_sysctl_port_proc, "IU", 207213762Sjmallett "Broadcast domain (ports can only talk to other ports in the same domain)"); 208213762Sjmallett 209213762Sjmallett SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "vlan", 210213762Sjmallett CTLFLAG_RW | CTLTYPE_INT, psc, 211213762Sjmallett MV88E61XXPHY_PORT_SYSCTL_VLAN, 212213762Sjmallett mv88e61xxphy_sysctl_port_proc, "IU", 213213762Sjmallett "Tag packets from/for this port with a given VLAN."); 214213762Sjmallett 215213762Sjmallett SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "priority", 216213762Sjmallett CTLFLAG_RW | CTLTYPE_INT, psc, 217213762Sjmallett MV88E61XXPHY_PORT_SYSCTL_PRIORITY, 218213762Sjmallett mv88e61xxphy_sysctl_port_proc, "IU", 219213762Sjmallett "Default packet priority for this port."); 220213762Sjmallett } 221213762Sjmallett 222213762Sjmallett mv88e61xxphy_init(sc); 223213762Sjmallett 224213762Sjmallett return (0); 225213762Sjmallett} 226213762Sjmallett 227213762Sjmallettstatic void 228213762Sjmallettmv88e61xxphy_init(struct mv88e61xxphy_softc *sc) 229213762Sjmallett{ 230213762Sjmallett unsigned port; 231213762Sjmallett uint16_t val; 232213762Sjmallett unsigned i; 233213762Sjmallett 234213762Sjmallett /* Disable all ports. */ 235213762Sjmallett for (port = 0; port < MV88E61XX_PORTS; port++) { 236213762Sjmallett struct mv88e61xxphy_port_softc *psc; 237213762Sjmallett 238213762Sjmallett psc = &sc->sc_ports[port]; 239213762Sjmallett 240213762Sjmallett val = MV88E61XX_READ_PORT(psc, MV88E61XX_PORT_CONTROL); 241213762Sjmallett val &= ~0x3; 242213762Sjmallett MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL, val); 243213762Sjmallett } 244213762Sjmallett 245213762Sjmallett DELAY(2000); 246213762Sjmallett 247213762Sjmallett /* Reset the switch. */ 248213762Sjmallett MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_CONTROL, 0xc400); 249213762Sjmallett for (i = 0; i < 100; i++) { 250213762Sjmallett val = MV88E61XX_READ(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_STATUS); 251213762Sjmallett if ((val & 0xc800) == 0xc800) 252213762Sjmallett break; 253213762Sjmallett DELAY(10); 254213762Sjmallett } 255213762Sjmallett if (i == 100) { 256213762Sjmallett device_printf(sc->sc_dev, "%s: switch reset timed out.\n", __func__); 257213762Sjmallett return; 258213762Sjmallett } 259213762Sjmallett 260213762Sjmallett /* Disable PPU. */ 261213762Sjmallett MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_CONTROL, 0x0000); 262213762Sjmallett 263213762Sjmallett /* Configure host port and send monitor frames to it. */ 264213762Sjmallett MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_MONITOR, 265213762Sjmallett (MV88E61XX_HOST_PORT << 12) | (MV88E61XX_HOST_PORT << 8) | 266213762Sjmallett (MV88E61XX_HOST_PORT << 4)); 267213762Sjmallett 268213762Sjmallett /* Disable remote management. */ 269213762Sjmallett MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_REMOTE_MGMT, 0x0000); 270213762Sjmallett 271213762Sjmallett /* Send all specifically-addressed frames to the host port. */ 272213762Sjmallett MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL2, MV88E61XX_GLOBAL2_MANAGE_2X, 0xffff); 273213762Sjmallett MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL2, MV88E61XX_GLOBAL2_MANAGE_0X, 0xffff); 274213762Sjmallett 275213762Sjmallett /* Remove provider-supplied tag and use it for switching. */ 276213762Sjmallett MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL2, MV88E61XX_GLOBAL2_CONTROL2, 277213762Sjmallett MV88E61XX_GLOBAL2_CONTROL2_REMOVE_PTAG); 278213762Sjmallett 279213762Sjmallett /* Configure all ports. */ 280213762Sjmallett for (port = 0; port < MV88E61XX_PORTS; port++) { 281213762Sjmallett struct mv88e61xxphy_port_softc *psc; 282213762Sjmallett 283213762Sjmallett psc = &sc->sc_ports[port]; 284213762Sjmallett mv88e61xxphy_init_port(psc); 285213762Sjmallett } 286213762Sjmallett 287213762Sjmallett /* Reprogram VLAN table (VTU.) */ 288213762Sjmallett mv88e61xxphy_init_vtu(sc); 289213762Sjmallett 290213762Sjmallett /* Enable all ports. */ 291213762Sjmallett for (port = 0; port < MV88E61XX_PORTS; port++) { 292213762Sjmallett struct mv88e61xxphy_port_softc *psc; 293213762Sjmallett 294213762Sjmallett psc = &sc->sc_ports[port]; 295213762Sjmallett 296213762Sjmallett val = MV88E61XX_READ_PORT(psc, MV88E61XX_PORT_CONTROL); 297213762Sjmallett val |= 0x3; 298213762Sjmallett MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL, val); 299213762Sjmallett } 300213762Sjmallett} 301213762Sjmallett 302213762Sjmallettstatic void 303213762Sjmallettmv88e61xxphy_init_port(struct mv88e61xxphy_port_softc *psc) 304213762Sjmallett{ 305213762Sjmallett struct mv88e61xxphy_softc *sc; 306213762Sjmallett unsigned allow_mask; 307213762Sjmallett 308213762Sjmallett sc = psc->sc_switch; 309213762Sjmallett 310213762Sjmallett /* Set media type and flow control. */ 311213762Sjmallett if (psc->sc_port != MV88E61XX_HOST_PORT) { 312213762Sjmallett /* Don't force any media type or flow control. */ 313213762Sjmallett MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_FORCE_MAC, 0x0003); 314213762Sjmallett } else { 315213762Sjmallett /* Make CPU port 1G FDX. */ 316213762Sjmallett MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_FORCE_MAC, 0x003e); 317213762Sjmallett } 318213762Sjmallett 319213762Sjmallett /* Don't limit flow control pauses. */ 320213762Sjmallett MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_PAUSE_CONTROL, 0x0000); 321213762Sjmallett 322213762Sjmallett /* Set various port functions per Linux. */ 323213762Sjmallett if (psc->sc_port != MV88E61XX_HOST_PORT) { 324213762Sjmallett MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL, 0x04bc); 325213762Sjmallett } else { 326213762Sjmallett /* 327213762Sjmallett * Send frames for unknown unicast and multicast groups to 328213762Sjmallett * host, too. 329213762Sjmallett */ 330213762Sjmallett MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL, 0x063f); 331213762Sjmallett } 332213762Sjmallett 333213762Sjmallett if (psc->sc_port != MV88E61XX_HOST_PORT) { 334213762Sjmallett /* Disable trunking. */ 335213762Sjmallett MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL2, 0x0000); 336213762Sjmallett } else { 337213762Sjmallett /* Disable trunking and send learn messages to host. */ 338213762Sjmallett MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL2, 0x8000); 339213762Sjmallett } 340213762Sjmallett 341213762Sjmallett /* 342213762Sjmallett * Port-based VLAN map; isolates MAC tables and forces ports to talk 343213762Sjmallett * only to the host. 344213762Sjmallett * 345213762Sjmallett * Always allow the host to send to all ports and allow all ports to 346213762Sjmallett * send to the host. 347213762Sjmallett */ 348213762Sjmallett if (psc->sc_port != MV88E61XX_HOST_PORT) { 349213762Sjmallett allow_mask = 1 << MV88E61XX_HOST_PORT; 350213762Sjmallett } else { 351213762Sjmallett allow_mask = (1 << MV88E61XX_PORTS) - 1; 352213762Sjmallett allow_mask &= ~(1 << MV88E61XX_HOST_PORT); 353213762Sjmallett } 354213762Sjmallett MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_VLAN_MAP, 355213762Sjmallett (psc->sc_domain << 12) | allow_mask); 356213762Sjmallett 357213762Sjmallett /* VLAN tagging. Set default priority and VLAN tag (or none.) */ 358213762Sjmallett MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_VLAN, 359213762Sjmallett (psc->sc_priority << 14) | psc->sc_vlan); 360213762Sjmallett 361213762Sjmallett if (psc->sc_port == MV88E61XX_HOST_PORT) { 362213762Sjmallett /* Set provider ingress tag. */ 363213762Sjmallett MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_PROVIDER_PROTO, 364213762Sjmallett ETHERTYPE_VLAN); 365213762Sjmallett 366213762Sjmallett /* Set provider egress tag. */ 367213762Sjmallett MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_ETHER_PROTO, 368213762Sjmallett ETHERTYPE_VLAN); 369213762Sjmallett 370213762Sjmallett /* Use secure 802.1q mode and accept only tagged frames. */ 371213762Sjmallett MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_FILTER, 372213762Sjmallett MV88E61XX_PORT_FILTER_MAP_DEST | 373213762Sjmallett MV88E61XX_PORT_FILTER_8021Q_SECURE | 374213762Sjmallett MV88E61XX_PORT_FILTER_DISCARD_UNTAGGED); 375213762Sjmallett } else { 376213762Sjmallett /* Don't allow tagged frames. */ 377213762Sjmallett MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_FILTER, 378213762Sjmallett MV88E61XX_PORT_FILTER_MAP_DEST | 379213762Sjmallett MV88E61XX_PORT_FILTER_DISCARD_TAGGED); 380213762Sjmallett } 381213762Sjmallett} 382213762Sjmallett 383213762Sjmallettstatic void 384213762Sjmallettmv88e61xxphy_init_vtu(struct mv88e61xxphy_softc *sc) 385213762Sjmallett{ 386213762Sjmallett unsigned port; 387213762Sjmallett 388213762Sjmallett /* 389213762Sjmallett * Start flush of the VTU. 390213762Sjmallett */ 391213762Sjmallett mv88e61xxphy_vtu_wait(sc); 392213762Sjmallett MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_VTU_OP, 393213762Sjmallett MV88E61XX_GLOBAL_VTU_OP_BUSY | MV88E61XX_GLOBAL_VTU_OP_OP_FLUSH); 394213762Sjmallett 395213762Sjmallett /* 396213762Sjmallett * Queue each port's VLAN to be programmed. 397213762Sjmallett */ 398213762Sjmallett for (port = 0; port < MV88E61XX_PORTS; port++) { 399213762Sjmallett struct mv88e61xxphy_port_softc *psc; 400213762Sjmallett 401213762Sjmallett psc = &sc->sc_ports[port]; 402213762Sjmallett psc->sc_flags &= ~MV88E61XXPHY_PORT_FLAG_VTU_UPDATE; 403213762Sjmallett if (psc->sc_vlan == 0) 404213762Sjmallett continue; 405213762Sjmallett psc->sc_flags |= MV88E61XXPHY_PORT_FLAG_VTU_UPDATE; 406213762Sjmallett } 407213762Sjmallett 408213762Sjmallett /* 409213762Sjmallett * Program each VLAN that is in use. 410213762Sjmallett */ 411213762Sjmallett for (port = 0; port < MV88E61XX_PORTS; port++) { 412213762Sjmallett struct mv88e61xxphy_port_softc *psc; 413213762Sjmallett 414213762Sjmallett psc = &sc->sc_ports[port]; 415213762Sjmallett if ((psc->sc_flags & MV88E61XXPHY_PORT_FLAG_VTU_UPDATE) == 0) 416213762Sjmallett continue; 417213762Sjmallett mv88e61xxphy_vtu_load(sc, psc->sc_vlan); 418213762Sjmallett } 419213762Sjmallett 420213762Sjmallett /* 421213762Sjmallett * Wait for last pending VTU operation to complete. 422213762Sjmallett */ 423213762Sjmallett mv88e61xxphy_vtu_wait(sc); 424213762Sjmallett} 425213762Sjmallett 426213762Sjmallettstatic int 427213762Sjmallettmv88e61xxphy_sysctl_link_proc(SYSCTL_HANDLER_ARGS) 428213762Sjmallett{ 429213762Sjmallett struct mv88e61xxphy_port_softc *psc = arg1; 430213762Sjmallett enum mv88e61xxphy_sysctl_link_type type = arg2; 431213762Sjmallett uint16_t val; 432213762Sjmallett unsigned out; 433213762Sjmallett 434213762Sjmallett val = MV88E61XX_READ_PORT(psc, MV88E61XX_PORT_STATUS); 435213762Sjmallett switch (type) { 436213762Sjmallett case MV88E61XXPHY_LINK_SYSCTL_DUPLEX: 437213762Sjmallett if ((val & MV88E61XX_PORT_STATUS_DUPLEX) != 0) 438213762Sjmallett out = 1; 439213762Sjmallett else 440213762Sjmallett out = 0; 441213762Sjmallett break; 442213762Sjmallett case MV88E61XXPHY_LINK_SYSCTL_LINK: 443213762Sjmallett if ((val & MV88E61XX_PORT_STATUS_LINK) != 0) 444213762Sjmallett out = 1; 445213762Sjmallett else 446213762Sjmallett out = 0; 447213762Sjmallett break; 448213762Sjmallett case MV88E61XXPHY_LINK_SYSCTL_MEDIA: 449213762Sjmallett switch (val & MV88E61XX_PORT_STATUS_MEDIA) { 450213762Sjmallett case MV88E61XX_PORT_STATUS_MEDIA_10M: 451213762Sjmallett out = 10; 452213762Sjmallett break; 453213762Sjmallett case MV88E61XX_PORT_STATUS_MEDIA_100M: 454213762Sjmallett out = 100; 455213762Sjmallett break; 456213762Sjmallett case MV88E61XX_PORT_STATUS_MEDIA_1G: 457213762Sjmallett out = 1000; 458213762Sjmallett break; 459213762Sjmallett default: 460213762Sjmallett out = 0; 461213762Sjmallett break; 462213762Sjmallett } 463213762Sjmallett break; 464213762Sjmallett default: 465213762Sjmallett return (EINVAL); 466213762Sjmallett } 467213762Sjmallett return (sysctl_handle_int(oidp, NULL, out, req)); 468213762Sjmallett} 469213762Sjmallett 470213762Sjmallettstatic int 471213762Sjmallettmv88e61xxphy_sysctl_port_proc(SYSCTL_HANDLER_ARGS) 472213762Sjmallett{ 473213762Sjmallett struct mv88e61xxphy_port_softc *psc = arg1; 474213762Sjmallett enum mv88e61xxphy_sysctl_port_type type = arg2; 475213762Sjmallett struct mv88e61xxphy_softc *sc = psc->sc_switch; 476213762Sjmallett unsigned max, val, *valp; 477213762Sjmallett int error; 478213762Sjmallett 479213762Sjmallett switch (type) { 480213762Sjmallett case MV88E61XXPHY_PORT_SYSCTL_DOMAIN: 481213762Sjmallett valp = &psc->sc_domain; 482213762Sjmallett max = 0xf; 483213762Sjmallett break; 484213762Sjmallett case MV88E61XXPHY_PORT_SYSCTL_VLAN: 485213762Sjmallett valp = &psc->sc_vlan; 486213762Sjmallett max = 0x1000; 487213762Sjmallett break; 488213762Sjmallett case MV88E61XXPHY_PORT_SYSCTL_PRIORITY: 489213762Sjmallett valp = &psc->sc_priority; 490213762Sjmallett max = 3; 491213762Sjmallett break; 492213762Sjmallett default: 493213762Sjmallett return (EINVAL); 494213762Sjmallett } 495213762Sjmallett 496213762Sjmallett val = *valp; 497213762Sjmallett error = sysctl_handle_int(oidp, &val, 0, req); 498213762Sjmallett if (error != 0 || req->newptr == NULL) 499213762Sjmallett return (error); 500213762Sjmallett 501213762Sjmallett /* Bounds check value. */ 502213762Sjmallett if (val >= max) 503213762Sjmallett return (EINVAL); 504213762Sjmallett 505213762Sjmallett /* Reinitialize switch with new value. */ 506213762Sjmallett *valp = val; 507213762Sjmallett mv88e61xxphy_init(sc); 508213762Sjmallett 509213762Sjmallett return (0); 510213762Sjmallett} 511213762Sjmallett 512213762Sjmallettstatic void 513213762Sjmallettmv88e61xxphy_vtu_load(struct mv88e61xxphy_softc *sc, uint16_t vid) 514213762Sjmallett{ 515213762Sjmallett unsigned port; 516213762Sjmallett 517213762Sjmallett /* 518213762Sjmallett * Wait for previous operation to complete. 519213762Sjmallett */ 520213762Sjmallett mv88e61xxphy_vtu_wait(sc); 521213762Sjmallett 522213762Sjmallett /* 523213762Sjmallett * Set VID. 524213762Sjmallett */ 525213762Sjmallett MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_VTU_VID, 526213762Sjmallett MV88E61XX_GLOBAL_VTU_VID_VALID | vid); 527213762Sjmallett 528213762Sjmallett /* 529213762Sjmallett * Add ports to this VTU. 530213762Sjmallett */ 531213762Sjmallett for (port = 0; port < MV88E61XX_PORTS; port++) { 532213762Sjmallett struct mv88e61xxphy_port_softc *psc; 533213762Sjmallett 534213762Sjmallett psc = &sc->sc_ports[port]; 535213762Sjmallett if (psc->sc_vlan == vid) { 536213762Sjmallett /* 537213762Sjmallett * Send this port its VLAN traffic untagged. 538213762Sjmallett */ 539213762Sjmallett psc->sc_flags &= ~MV88E61XXPHY_PORT_FLAG_VTU_UPDATE; 540213762Sjmallett mv88e61xxphy_vtu_set_membership(sc, port, MV88E61XXPHY_VTU_UNTAGGED); 541213762Sjmallett } else if (psc->sc_port == MV88E61XX_HOST_PORT) { 542213762Sjmallett /* 543213762Sjmallett * The host sees all VLANs tagged. 544213762Sjmallett */ 545213762Sjmallett mv88e61xxphy_vtu_set_membership(sc, port, MV88E61XXPHY_VTU_TAGGED); 546213762Sjmallett } else { 547213762Sjmallett /* 548213762Sjmallett * This port isn't on this VLAN. 549213762Sjmallett */ 550213762Sjmallett mv88e61xxphy_vtu_set_membership(sc, port, MV88E61XXPHY_VTU_DISCARDED); 551213762Sjmallett } 552213762Sjmallett } 553213762Sjmallett 554213762Sjmallett /* 555213762Sjmallett * Start adding this entry. 556213762Sjmallett */ 557213762Sjmallett MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_VTU_OP, 558213762Sjmallett MV88E61XX_GLOBAL_VTU_OP_BUSY | 559213762Sjmallett MV88E61XX_GLOBAL_VTU_OP_OP_VTU_LOAD); 560213762Sjmallett} 561213762Sjmallett 562213762Sjmallettstatic void 563213762Sjmallettmv88e61xxphy_vtu_set_membership(struct mv88e61xxphy_softc *sc, unsigned port, 564213762Sjmallett enum mv88e61xxphy_vtu_membership_type type) 565213762Sjmallett{ 566213762Sjmallett unsigned shift, reg; 567213762Sjmallett uint16_t bits; 568213762Sjmallett uint16_t val; 569213762Sjmallett 570213762Sjmallett switch (type) { 571213762Sjmallett case MV88E61XXPHY_VTU_UNMODIFIED: 572213762Sjmallett bits = 0; 573213762Sjmallett break; 574213762Sjmallett case MV88E61XXPHY_VTU_UNTAGGED: 575213762Sjmallett bits = 1; 576213762Sjmallett break; 577213762Sjmallett case MV88E61XXPHY_VTU_TAGGED: 578213762Sjmallett bits = 2; 579213762Sjmallett break; 580213762Sjmallett case MV88E61XXPHY_VTU_DISCARDED: 581213762Sjmallett bits = 3; 582213762Sjmallett break; 583213762Sjmallett default: 584213762Sjmallett return; 585213762Sjmallett } 586213762Sjmallett 587213762Sjmallett if (port < 4) { 588213762Sjmallett reg = MV88E61XX_GLOBAL_VTU_DATA_P0P3; 589213762Sjmallett shift = port * 4; 590213762Sjmallett } else { 591213762Sjmallett reg = MV88E61XX_GLOBAL_VTU_DATA_P4P5; 592213762Sjmallett shift = (port - 4) * 4; 593213762Sjmallett } 594213762Sjmallett 595213762Sjmallett val = MV88E61XX_READ(sc, MV88E61XX_GLOBAL, reg); 596213762Sjmallett val |= bits << shift; 597213762Sjmallett MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, reg, val); 598213762Sjmallett} 599213762Sjmallett 600213762Sjmallettstatic void 601213762Sjmallettmv88e61xxphy_vtu_wait(struct mv88e61xxphy_softc *sc) 602213762Sjmallett{ 603213762Sjmallett uint16_t val; 604213762Sjmallett 605213762Sjmallett for (;;) { 606213762Sjmallett val = MV88E61XX_READ(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_VTU_OP); 607213762Sjmallett if ((val & MV88E61XX_GLOBAL_VTU_OP_BUSY) == 0) 608213762Sjmallett return; 609213762Sjmallett } 610213762Sjmallett} 611213762Sjmallett 612213762Sjmallettstatic device_method_t mv88e61xxphy_methods[] = { 613213762Sjmallett /* device interface */ 614213762Sjmallett DEVMETHOD(device_probe, mv88e61xxphy_probe), 615213762Sjmallett DEVMETHOD(device_attach, mv88e61xxphy_attach), 616213762Sjmallett DEVMETHOD(device_detach, bus_generic_detach), 617213762Sjmallett DEVMETHOD(device_shutdown, bus_generic_shutdown), 618213762Sjmallett 619213762Sjmallett { 0, 0 } 620213762Sjmallett}; 621213762Sjmallett 622213762Sjmallettstatic devclass_t mv88e61xxphy_devclass; 623213762Sjmallett 624213762Sjmallettstatic driver_t mv88e61xxphy_driver = { 625213762Sjmallett "mv88e61xxphy", 626213762Sjmallett mv88e61xxphy_methods, 627213762Sjmallett sizeof(struct mv88e61xxphy_softc) 628213762Sjmallett}; 629213762Sjmallett 630213762SjmallettDRIVER_MODULE(mv88e61xxphy, octe, mv88e61xxphy_driver, mv88e61xxphy_devclass, 0, 0); 631