1248dd603SAdrian Chadd /*- 2248dd603SAdrian Chadd * Copyright (c) 2013 Luiz Otavio O Souza. 3248dd603SAdrian Chadd * Copyright (c) 2011-2012 Stefan Bethke. 4248dd603SAdrian Chadd * Copyright (c) 2012 Adrian Chadd. 5248dd603SAdrian Chadd * All rights reserved. 6248dd603SAdrian Chadd * 7248dd603SAdrian Chadd * Redistribution and use in source and binary forms, with or without 8248dd603SAdrian Chadd * modification, are permitted provided that the following conditions 9248dd603SAdrian Chadd * are met: 10248dd603SAdrian Chadd * 1. Redistributions of source code must retain the above copyright 11248dd603SAdrian Chadd * notice, this list of conditions and the following disclaimer. 12248dd603SAdrian Chadd * 2. Redistributions in binary form must reproduce the above copyright 13248dd603SAdrian Chadd * notice, this list of conditions and the following disclaimer in the 14248dd603SAdrian Chadd * documentation and/or other materials provided with the distribution. 15248dd603SAdrian Chadd * 16248dd603SAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17248dd603SAdrian Chadd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18248dd603SAdrian Chadd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19248dd603SAdrian Chadd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20248dd603SAdrian Chadd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21248dd603SAdrian Chadd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22248dd603SAdrian Chadd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23248dd603SAdrian Chadd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24248dd603SAdrian Chadd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25248dd603SAdrian Chadd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26248dd603SAdrian Chadd * SUCH DAMAGE. 27248dd603SAdrian Chadd * 28248dd603SAdrian Chadd * $FreeBSD$ 29248dd603SAdrian Chadd */ 30248dd603SAdrian Chadd 31248dd603SAdrian Chadd #include <sys/param.h> 32248dd603SAdrian Chadd #include <sys/bus.h> 33248dd603SAdrian Chadd #include <sys/errno.h> 34248dd603SAdrian Chadd #include <sys/kernel.h> 354170452dSChristian Brueffer #include <sys/lock.h> 364170452dSChristian Brueffer #include <sys/malloc.h> 37248dd603SAdrian Chadd #include <sys/module.h> 384170452dSChristian Brueffer #include <sys/mutex.h> 39248dd603SAdrian Chadd #include <sys/socket.h> 40248dd603SAdrian Chadd #include <sys/sockio.h> 41248dd603SAdrian Chadd #include <sys/sysctl.h> 42248dd603SAdrian Chadd #include <sys/systm.h> 434170452dSChristian Brueffer #include <sys/types.h> 44248dd603SAdrian Chadd 45248dd603SAdrian Chadd #include <net/if.h> 46248dd603SAdrian Chadd #include <net/ethernet.h> 47248dd603SAdrian Chadd #include <net/if_media.h> 48248dd603SAdrian Chadd #include <net/if_types.h> 494170452dSChristian Brueffer #include <net/if_var.h> 50248dd603SAdrian Chadd 51248dd603SAdrian Chadd #include <machine/bus.h> 52248dd603SAdrian Chadd #include <dev/mii/mii.h> 53248dd603SAdrian Chadd #include <dev/mii/miivar.h> 54248dd603SAdrian Chadd #include <dev/etherswitch/mdio.h> 55248dd603SAdrian Chadd 56248dd603SAdrian Chadd #include <dev/etherswitch/etherswitch.h> 57248dd603SAdrian Chadd #include <dev/etherswitch/ip17x/ip17x_phy.h> 58248dd603SAdrian Chadd #include <dev/etherswitch/ip17x/ip17x_reg.h> 59248dd603SAdrian Chadd #include <dev/etherswitch/ip17x/ip17x_var.h> 60248dd603SAdrian Chadd #include <dev/etherswitch/ip17x/ip17x_vlans.h> 61248dd603SAdrian Chadd #include <dev/etherswitch/ip17x/ip175c.h> 62248dd603SAdrian Chadd #include <dev/etherswitch/ip17x/ip175d.h> 63248dd603SAdrian Chadd 64248dd603SAdrian Chadd #include "mdio_if.h" 65248dd603SAdrian Chadd #include "miibus_if.h" 66248dd603SAdrian Chadd #include "etherswitch_if.h" 67248dd603SAdrian Chadd 68248dd603SAdrian Chadd MALLOC_DECLARE(M_IP17X); 69248dd603SAdrian Chadd MALLOC_DEFINE(M_IP17X, "ip17x", "ip17x data structures"); 70248dd603SAdrian Chadd 71248dd603SAdrian Chadd static void ip17x_tick(void *); 72248dd603SAdrian Chadd static int ip17x_ifmedia_upd(struct ifnet *); 73248dd603SAdrian Chadd static void ip17x_ifmedia_sts(struct ifnet *, struct ifmediareq *); 74248dd603SAdrian Chadd 75248dd603SAdrian Chadd static int 76248dd603SAdrian Chadd ip17x_probe(device_t dev) 77248dd603SAdrian Chadd { 78248dd603SAdrian Chadd struct ip17x_softc *sc; 79248dd603SAdrian Chadd uint32_t oui, model, phy_id1, phy_id2; 80248dd603SAdrian Chadd 81248dd603SAdrian Chadd sc = device_get_softc(dev); 82248dd603SAdrian Chadd 83248dd603SAdrian Chadd /* Read ID from PHY 0. */ 84248dd603SAdrian Chadd phy_id1 = MDIO_READREG(device_get_parent(dev), 0, MII_PHYIDR1); 85248dd603SAdrian Chadd phy_id2 = MDIO_READREG(device_get_parent(dev), 0, MII_PHYIDR2); 86248dd603SAdrian Chadd 87248dd603SAdrian Chadd oui = MII_OUI(phy_id1, phy_id2), 88248dd603SAdrian Chadd model = MII_MODEL(phy_id2); 89248dd603SAdrian Chadd /* We only care about IC+ devices. */ 90248dd603SAdrian Chadd if (oui != IP17X_OUI) { 91248dd603SAdrian Chadd device_printf(dev, 92248dd603SAdrian Chadd "Unsupported IC+ switch. Unknown OUI: %#x\n", oui); 93248dd603SAdrian Chadd return (ENXIO); 94248dd603SAdrian Chadd } 95248dd603SAdrian Chadd 96248dd603SAdrian Chadd switch (model) { 97248dd603SAdrian Chadd case IP17X_IP175A: 98248dd603SAdrian Chadd sc->sc_switchtype = IP17X_SWITCH_IP175A; 99248dd603SAdrian Chadd break; 100248dd603SAdrian Chadd case IP17X_IP175C: 101248dd603SAdrian Chadd sc->sc_switchtype = IP17X_SWITCH_IP175C; 102248dd603SAdrian Chadd break; 103248dd603SAdrian Chadd default: 104248dd603SAdrian Chadd device_printf(dev, "Unsupported IC+ switch model: %#x\n", 105248dd603SAdrian Chadd model); 106248dd603SAdrian Chadd return (ENXIO); 107248dd603SAdrian Chadd } 108248dd603SAdrian Chadd 109248dd603SAdrian Chadd /* IP175D has a specific ID register. */ 110248dd603SAdrian Chadd model = MDIO_READREG(device_get_parent(dev), IP175D_ID_PHY, 111248dd603SAdrian Chadd IP175D_ID_REG); 112248dd603SAdrian Chadd if (model == 0x175d) 113248dd603SAdrian Chadd sc->sc_switchtype = IP17X_SWITCH_IP175D; 114248dd603SAdrian Chadd else { 115248dd603SAdrian Chadd /* IP178 has more PHYs. Try it. */ 116248dd603SAdrian Chadd model = MDIO_READREG(device_get_parent(dev), 5, MII_PHYIDR1); 117248dd603SAdrian Chadd if (phy_id1 == model) 118248dd603SAdrian Chadd sc->sc_switchtype = IP17X_SWITCH_IP178C; 119248dd603SAdrian Chadd } 120248dd603SAdrian Chadd 121248dd603SAdrian Chadd device_set_desc_copy(dev, "IC+ IP17x switch driver"); 122248dd603SAdrian Chadd return (BUS_PROBE_DEFAULT); 123248dd603SAdrian Chadd } 124248dd603SAdrian Chadd 125248dd603SAdrian Chadd static int 126248dd603SAdrian Chadd ip17x_attach_phys(struct ip17x_softc *sc) 127248dd603SAdrian Chadd { 128248dd603SAdrian Chadd int err, phy, port; 129248dd603SAdrian Chadd char name[IFNAMSIZ]; 130248dd603SAdrian Chadd 131248dd603SAdrian Chadd port = err = 0; 132248dd603SAdrian Chadd 133248dd603SAdrian Chadd /* PHYs need an interface, so we generate a dummy one */ 134248dd603SAdrian Chadd snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->sc_dev)); 135248dd603SAdrian Chadd for (phy = 0; phy < MII_NPHY; phy++) { 136248dd603SAdrian Chadd if (((1 << phy) & sc->phymask) == 0) 137248dd603SAdrian Chadd continue; 138248dd603SAdrian Chadd sc->phyport[phy] = port; 139248dd603SAdrian Chadd sc->portphy[port] = phy; 140248dd603SAdrian Chadd sc->ifp[port] = if_alloc(IFT_ETHER); 141248dd603SAdrian Chadd sc->ifp[port]->if_softc = sc; 142248dd603SAdrian Chadd sc->ifp[port]->if_flags |= IFF_UP | IFF_BROADCAST | 143248dd603SAdrian Chadd IFF_DRV_RUNNING | IFF_SIMPLEX; 144248dd603SAdrian Chadd sc->ifname[port] = malloc(strlen(name)+1, M_IP17X, M_WAITOK); 145248dd603SAdrian Chadd bcopy(name, sc->ifname[port], strlen(name)+1); 146248dd603SAdrian Chadd if_initname(sc->ifp[port], sc->ifname[port], port); 147248dd603SAdrian Chadd sc->miibus[port] = malloc(sizeof(device_t), M_IP17X, 148248dd603SAdrian Chadd M_WAITOK | M_ZERO); 149248dd603SAdrian Chadd err = mii_attach(sc->sc_dev, sc->miibus[port], sc->ifp[port], 150248dd603SAdrian Chadd ip17x_ifmedia_upd, ip17x_ifmedia_sts, \ 151248dd603SAdrian Chadd BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0); 152248dd603SAdrian Chadd DPRINTF(sc->sc_dev, "%s attached to pseudo interface %s\n", 153248dd603SAdrian Chadd device_get_nameunit(*sc->miibus[port]), 154248dd603SAdrian Chadd sc->ifp[port]->if_xname); 155248dd603SAdrian Chadd if (err != 0) { 156248dd603SAdrian Chadd device_printf(sc->sc_dev, 157248dd603SAdrian Chadd "attaching PHY %d failed\n", 158248dd603SAdrian Chadd phy); 159248dd603SAdrian Chadd break; 160248dd603SAdrian Chadd } 161248dd603SAdrian Chadd sc->info.es_nports = port + 1; 162248dd603SAdrian Chadd if (++port >= sc->numports) 163248dd603SAdrian Chadd break; 164248dd603SAdrian Chadd } 165248dd603SAdrian Chadd return (err); 166248dd603SAdrian Chadd } 167248dd603SAdrian Chadd 168248dd603SAdrian Chadd static int 169248dd603SAdrian Chadd ip17x_attach(device_t dev) 170248dd603SAdrian Chadd { 171248dd603SAdrian Chadd struct ip17x_softc *sc; 172248dd603SAdrian Chadd int err; 173248dd603SAdrian Chadd 174248dd603SAdrian Chadd sc = device_get_softc(dev); 175248dd603SAdrian Chadd 176248dd603SAdrian Chadd sc->sc_dev = dev; 177248dd603SAdrian Chadd mtx_init(&sc->sc_mtx, "ip17x", NULL, MTX_DEF); 178248dd603SAdrian Chadd strlcpy(sc->info.es_name, device_get_desc(dev), 179248dd603SAdrian Chadd sizeof(sc->info.es_name)); 180248dd603SAdrian Chadd 181248dd603SAdrian Chadd /* XXX Defaults */ 182248dd603SAdrian Chadd sc->phymask = 0x0f; 183248dd603SAdrian Chadd sc->media = 100; 184248dd603SAdrian Chadd 185248dd603SAdrian Chadd (void) resource_int_value(device_get_name(dev), device_get_unit(dev), 186248dd603SAdrian Chadd "phymask", &sc->phymask); 187248dd603SAdrian Chadd 188248dd603SAdrian Chadd /* Number of vlans supported by the switch. */ 189248dd603SAdrian Chadd sc->info.es_nvlangroups = IP17X_MAX_VLANS; 190248dd603SAdrian Chadd 191248dd603SAdrian Chadd /* Attach the switch related functions. */ 192248dd603SAdrian Chadd if (IP17X_IS_SWITCH(sc, IP175C)) 193248dd603SAdrian Chadd ip175c_attach(sc); 194248dd603SAdrian Chadd else if (IP17X_IS_SWITCH(sc, IP175D)) 195248dd603SAdrian Chadd ip175d_attach(sc); 196248dd603SAdrian Chadd else 197248dd603SAdrian Chadd /* We don't have support to all the models yet :-/ */ 198248dd603SAdrian Chadd return (ENXIO); 199248dd603SAdrian Chadd 200248dd603SAdrian Chadd /* Always attach the cpu port. */ 201248dd603SAdrian Chadd sc->phymask |= (1 << sc->cpuport); 202248dd603SAdrian Chadd 203248dd603SAdrian Chadd sc->ifp = malloc(sizeof(struct ifnet *) * sc->numports, M_IP17X, 204248dd603SAdrian Chadd M_WAITOK | M_ZERO); 205248dd603SAdrian Chadd sc->pvid = malloc(sizeof(uint32_t) * sc->numports, M_IP17X, 206248dd603SAdrian Chadd M_WAITOK | M_ZERO); 207248dd603SAdrian Chadd sc->ifname = malloc(sizeof(char *) * sc->numports, M_IP17X, 208248dd603SAdrian Chadd M_WAITOK | M_ZERO); 209248dd603SAdrian Chadd sc->miibus = malloc(sizeof(device_t *) * sc->numports, M_IP17X, 210248dd603SAdrian Chadd M_WAITOK | M_ZERO); 211248dd603SAdrian Chadd sc->portphy = malloc(sizeof(int) * sc->numports, M_IP17X, 212248dd603SAdrian Chadd M_WAITOK | M_ZERO); 213248dd603SAdrian Chadd 214248dd603SAdrian Chadd /* Initialize the switch. */ 215248dd603SAdrian Chadd sc->hal.ip17x_reset(sc); 216248dd603SAdrian Chadd 217248dd603SAdrian Chadd /* 218248dd603SAdrian Chadd * Attach the PHYs and complete the bus enumeration. 219248dd603SAdrian Chadd */ 220248dd603SAdrian Chadd err = ip17x_attach_phys(sc); 221248dd603SAdrian Chadd if (err != 0) 222248dd603SAdrian Chadd return (err); 223248dd603SAdrian Chadd 224248dd603SAdrian Chadd /* 225248dd603SAdrian Chadd * Set the switch to port based vlans or disabled (if not supported 226248dd603SAdrian Chadd * on this model). 227248dd603SAdrian Chadd */ 228248dd603SAdrian Chadd sc->hal.ip17x_set_vlan_mode(sc, ETHERSWITCH_VLAN_PORT); 229248dd603SAdrian Chadd 230248dd603SAdrian Chadd bus_generic_probe(dev); 231248dd603SAdrian Chadd bus_enumerate_hinted_children(dev); 232248dd603SAdrian Chadd err = bus_generic_attach(dev); 233248dd603SAdrian Chadd if (err != 0) 234248dd603SAdrian Chadd return (err); 235248dd603SAdrian Chadd 236248dd603SAdrian Chadd callout_init(&sc->callout_tick, 0); 237248dd603SAdrian Chadd 238248dd603SAdrian Chadd ip17x_tick(sc); 239248dd603SAdrian Chadd 240248dd603SAdrian Chadd return (0); 241248dd603SAdrian Chadd } 242248dd603SAdrian Chadd 243248dd603SAdrian Chadd static int 244248dd603SAdrian Chadd ip17x_detach(device_t dev) 245248dd603SAdrian Chadd { 246248dd603SAdrian Chadd struct ip17x_softc *sc; 247248dd603SAdrian Chadd int i, port; 248248dd603SAdrian Chadd 249248dd603SAdrian Chadd sc = device_get_softc(dev); 250248dd603SAdrian Chadd callout_drain(&sc->callout_tick); 251248dd603SAdrian Chadd 252248dd603SAdrian Chadd for (i=0; i < MII_NPHY; i++) { 253248dd603SAdrian Chadd if (((1 << i) & sc->phymask) == 0) 254248dd603SAdrian Chadd continue; 255248dd603SAdrian Chadd port = sc->phyport[i]; 256248dd603SAdrian Chadd if (sc->miibus[port] != NULL) 257248dd603SAdrian Chadd device_delete_child(dev, (*sc->miibus[port])); 258248dd603SAdrian Chadd if (sc->ifp[port] != NULL) 259248dd603SAdrian Chadd if_free(sc->ifp[port]); 260248dd603SAdrian Chadd free(sc->ifname[port], M_IP17X); 261248dd603SAdrian Chadd free(sc->miibus[port], M_IP17X); 262248dd603SAdrian Chadd } 263248dd603SAdrian Chadd 264248dd603SAdrian Chadd free(sc->portphy, M_IP17X); 265248dd603SAdrian Chadd free(sc->miibus, M_IP17X); 266248dd603SAdrian Chadd free(sc->ifname, M_IP17X); 267248dd603SAdrian Chadd free(sc->pvid, M_IP17X); 268248dd603SAdrian Chadd free(sc->ifp, M_IP17X); 269248dd603SAdrian Chadd 270248dd603SAdrian Chadd /* Reset the switch. */ 271248dd603SAdrian Chadd sc->hal.ip17x_reset(sc); 272248dd603SAdrian Chadd 273248dd603SAdrian Chadd bus_generic_detach(dev); 274248dd603SAdrian Chadd mtx_destroy(&sc->sc_mtx); 275248dd603SAdrian Chadd 276248dd603SAdrian Chadd return (0); 277248dd603SAdrian Chadd } 278248dd603SAdrian Chadd 279248dd603SAdrian Chadd static inline struct mii_data * 280248dd603SAdrian Chadd ip17x_miiforport(struct ip17x_softc *sc, int port) 281248dd603SAdrian Chadd { 282248dd603SAdrian Chadd 283248dd603SAdrian Chadd if (port < 0 || port > sc->numports) 284248dd603SAdrian Chadd return (NULL); 285248dd603SAdrian Chadd return (device_get_softc(*sc->miibus[port])); 286248dd603SAdrian Chadd } 287248dd603SAdrian Chadd 288248dd603SAdrian Chadd static inline struct ifnet * 289248dd603SAdrian Chadd ip17x_ifpforport(struct ip17x_softc *sc, int port) 290248dd603SAdrian Chadd { 291248dd603SAdrian Chadd 292248dd603SAdrian Chadd if (port < 0 || port > sc->numports) 293248dd603SAdrian Chadd return (NULL); 294248dd603SAdrian Chadd return (sc->ifp[port]); 295248dd603SAdrian Chadd } 296248dd603SAdrian Chadd 297248dd603SAdrian Chadd /* 298248dd603SAdrian Chadd * Poll the status for all PHYs. 299248dd603SAdrian Chadd */ 300248dd603SAdrian Chadd static void 301248dd603SAdrian Chadd ip17x_miipollstat(struct ip17x_softc *sc) 302248dd603SAdrian Chadd { 303248dd603SAdrian Chadd struct mii_softc *miisc; 304248dd603SAdrian Chadd struct mii_data *mii; 305248dd603SAdrian Chadd int i, port; 306248dd603SAdrian Chadd 307248dd603SAdrian Chadd IP17X_LOCK_ASSERT(sc, MA_NOTOWNED); 308248dd603SAdrian Chadd 309248dd603SAdrian Chadd for (i = 0; i < MII_NPHY; i++) { 310248dd603SAdrian Chadd if (((1 << i) & sc->phymask) == 0) 311248dd603SAdrian Chadd continue; 312248dd603SAdrian Chadd port = sc->phyport[i]; 313248dd603SAdrian Chadd if ((*sc->miibus[port]) == NULL) 314248dd603SAdrian Chadd continue; 315248dd603SAdrian Chadd mii = device_get_softc(*sc->miibus[port]); 316248dd603SAdrian Chadd LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { 317248dd603SAdrian Chadd if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) != 318248dd603SAdrian Chadd miisc->mii_inst) 319248dd603SAdrian Chadd continue; 320248dd603SAdrian Chadd ukphy_status(miisc); 321248dd603SAdrian Chadd mii_phy_update(miisc, MII_POLLSTAT); 322248dd603SAdrian Chadd } 323248dd603SAdrian Chadd } 324248dd603SAdrian Chadd } 325248dd603SAdrian Chadd 326248dd603SAdrian Chadd static void 327248dd603SAdrian Chadd ip17x_tick(void *arg) 328248dd603SAdrian Chadd { 329248dd603SAdrian Chadd struct ip17x_softc *sc; 330248dd603SAdrian Chadd 331248dd603SAdrian Chadd sc = arg; 332248dd603SAdrian Chadd ip17x_miipollstat(sc); 333248dd603SAdrian Chadd callout_reset(&sc->callout_tick, hz, ip17x_tick, sc); 334248dd603SAdrian Chadd } 335248dd603SAdrian Chadd 336248dd603SAdrian Chadd static void 337248dd603SAdrian Chadd ip17x_lock(device_t dev) 338248dd603SAdrian Chadd { 339248dd603SAdrian Chadd struct ip17x_softc *sc; 340248dd603SAdrian Chadd 341248dd603SAdrian Chadd sc = device_get_softc(dev); 342248dd603SAdrian Chadd IP17X_LOCK_ASSERT(sc, MA_NOTOWNED); 343248dd603SAdrian Chadd IP17X_LOCK(sc); 344248dd603SAdrian Chadd } 345248dd603SAdrian Chadd 346248dd603SAdrian Chadd static void 347248dd603SAdrian Chadd ip17x_unlock(device_t dev) 348248dd603SAdrian Chadd { 349248dd603SAdrian Chadd struct ip17x_softc *sc; 350248dd603SAdrian Chadd 351248dd603SAdrian Chadd sc = device_get_softc(dev); 352248dd603SAdrian Chadd IP17X_LOCK_ASSERT(sc, MA_OWNED); 353248dd603SAdrian Chadd IP17X_UNLOCK(sc); 354248dd603SAdrian Chadd } 355248dd603SAdrian Chadd 356248dd603SAdrian Chadd static etherswitch_info_t * 357248dd603SAdrian Chadd ip17x_getinfo(device_t dev) 358248dd603SAdrian Chadd { 359248dd603SAdrian Chadd struct ip17x_softc *sc; 360248dd603SAdrian Chadd 361248dd603SAdrian Chadd sc = device_get_softc(dev); 362248dd603SAdrian Chadd return (&sc->info); 363248dd603SAdrian Chadd } 364248dd603SAdrian Chadd 365248dd603SAdrian Chadd static int 366248dd603SAdrian Chadd ip17x_getport(device_t dev, etherswitch_port_t *p) 367248dd603SAdrian Chadd { 368248dd603SAdrian Chadd struct ip17x_softc *sc; 369248dd603SAdrian Chadd struct ifmediareq *ifmr; 370248dd603SAdrian Chadd struct mii_data *mii; 371248dd603SAdrian Chadd int err, phy; 372248dd603SAdrian Chadd 373248dd603SAdrian Chadd sc = device_get_softc(dev); 374248dd603SAdrian Chadd if (p->es_port < 0 || p->es_port >= sc->numports) 375248dd603SAdrian Chadd return (ENXIO); 376248dd603SAdrian Chadd 377248dd603SAdrian Chadd phy = sc->portphy[p->es_port]; 378248dd603SAdrian Chadd 379248dd603SAdrian Chadd /* Retrieve the PVID. */ 380248dd603SAdrian Chadd p->es_pvid = sc->pvid[phy]; 381248dd603SAdrian Chadd 382248dd603SAdrian Chadd /* Port flags. */ 383248dd603SAdrian Chadd if (sc->addtag & (1 << phy)) 384248dd603SAdrian Chadd p->es_flags |= ETHERSWITCH_PORT_ADDTAG; 385248dd603SAdrian Chadd if (sc->striptag & (1 << phy)) 386248dd603SAdrian Chadd p->es_flags |= ETHERSWITCH_PORT_STRIPTAG; 387248dd603SAdrian Chadd 388248dd603SAdrian Chadd ifmr = &p->es_ifmr; 389248dd603SAdrian Chadd 390248dd603SAdrian Chadd /* No media settings ? */ 391248dd603SAdrian Chadd if (p->es_ifmr.ifm_count == 0) 392248dd603SAdrian Chadd return (0); 393248dd603SAdrian Chadd 394248dd603SAdrian Chadd mii = ip17x_miiforport(sc, p->es_port); 395248dd603SAdrian Chadd if (mii == NULL) 396248dd603SAdrian Chadd return (ENXIO); 397248dd603SAdrian Chadd if (phy == sc->cpuport) { 398248dd603SAdrian Chadd /* fill in fixed values for CPU port */ 399248dd603SAdrian Chadd p->es_flags |= ETHERSWITCH_PORT_CPU; 400248dd603SAdrian Chadd ifmr->ifm_count = 0; 401248dd603SAdrian Chadd if (sc->media == 100) 402248dd603SAdrian Chadd ifmr->ifm_current = ifmr->ifm_active = 403248dd603SAdrian Chadd IFM_ETHER | IFM_100_TX | IFM_FDX; 404248dd603SAdrian Chadd else 405248dd603SAdrian Chadd ifmr->ifm_current = ifmr->ifm_active = 406248dd603SAdrian Chadd IFM_ETHER | IFM_1000_T | IFM_FDX; 407248dd603SAdrian Chadd ifmr->ifm_mask = 0; 408248dd603SAdrian Chadd ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID; 409248dd603SAdrian Chadd } else { 410248dd603SAdrian Chadd err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr, 411248dd603SAdrian Chadd &mii->mii_media, SIOCGIFMEDIA); 412248dd603SAdrian Chadd if (err) 413248dd603SAdrian Chadd return (err); 414248dd603SAdrian Chadd } 415248dd603SAdrian Chadd return (0); 416248dd603SAdrian Chadd } 417248dd603SAdrian Chadd 418248dd603SAdrian Chadd static int 419248dd603SAdrian Chadd ip17x_setport(device_t dev, etherswitch_port_t *p) 420248dd603SAdrian Chadd { 421248dd603SAdrian Chadd struct ip17x_softc *sc; 422248dd603SAdrian Chadd struct ifmedia *ifm; 423248dd603SAdrian Chadd struct ifnet *ifp; 424248dd603SAdrian Chadd struct mii_data *mii; 425248dd603SAdrian Chadd int phy; 426248dd603SAdrian Chadd 427248dd603SAdrian Chadd sc = device_get_softc(dev); 428248dd603SAdrian Chadd if (p->es_port < 0 || p->es_port >= sc->numports) 429248dd603SAdrian Chadd return (ENXIO); 430248dd603SAdrian Chadd 431248dd603SAdrian Chadd phy = sc->portphy[p->es_port]; 432248dd603SAdrian Chadd ifp = ip17x_ifpforport(sc, p->es_port); 433248dd603SAdrian Chadd mii = ip17x_miiforport(sc, p->es_port); 434248dd603SAdrian Chadd if (ifp == NULL || mii == NULL) 435248dd603SAdrian Chadd return (ENXIO); 436248dd603SAdrian Chadd 437248dd603SAdrian Chadd /* Port flags. */ 438248dd603SAdrian Chadd if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) { 439248dd603SAdrian Chadd 440248dd603SAdrian Chadd /* Set the PVID. */ 441248dd603SAdrian Chadd if (p->es_pvid != 0) { 442248dd603SAdrian Chadd if (IP17X_IS_SWITCH(sc, IP175C) && 443248dd603SAdrian Chadd p->es_pvid > IP175C_LAST_VLAN) 444248dd603SAdrian Chadd return (ENXIO); 445248dd603SAdrian Chadd sc->pvid[phy] = p->es_pvid; 446248dd603SAdrian Chadd } 447248dd603SAdrian Chadd 448248dd603SAdrian Chadd /* Mutually exclusive. */ 449248dd603SAdrian Chadd if (p->es_flags & ETHERSWITCH_PORT_ADDTAG && 450248dd603SAdrian Chadd p->es_flags & ETHERSWITCH_PORT_STRIPTAG) 451248dd603SAdrian Chadd return (EINVAL); 452248dd603SAdrian Chadd 453248dd603SAdrian Chadd /* Reset the settings for this port. */ 454248dd603SAdrian Chadd sc->addtag &= ~(1 << phy); 455248dd603SAdrian Chadd sc->striptag &= ~(1 << phy); 456248dd603SAdrian Chadd 457248dd603SAdrian Chadd /* And then set it to the new value. */ 458248dd603SAdrian Chadd if (p->es_flags & ETHERSWITCH_PORT_ADDTAG) 459248dd603SAdrian Chadd sc->addtag |= (1 << phy); 460248dd603SAdrian Chadd if (p->es_flags & ETHERSWITCH_PORT_STRIPTAG) 461248dd603SAdrian Chadd sc->striptag |= (1 << phy); 462248dd603SAdrian Chadd } 463248dd603SAdrian Chadd 464248dd603SAdrian Chadd /* Update the switch configuration. */ 465248dd603SAdrian Chadd if (sc->hal.ip17x_hw_setup(sc)) 466248dd603SAdrian Chadd return (ENXIO); 467248dd603SAdrian Chadd 468248dd603SAdrian Chadd /* Do not allow media changes on CPU port. */ 469248dd603SAdrian Chadd if (phy == sc->cpuport) 470248dd603SAdrian Chadd return (0); 471248dd603SAdrian Chadd 472248dd603SAdrian Chadd /* No media settings ? */ 473248dd603SAdrian Chadd if (p->es_ifmr.ifm_count == 0) 474248dd603SAdrian Chadd return (0); 475248dd603SAdrian Chadd 476248dd603SAdrian Chadd ifm = &mii->mii_media; 477248dd603SAdrian Chadd return (ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA)); 478248dd603SAdrian Chadd } 479248dd603SAdrian Chadd 480248dd603SAdrian Chadd static void 481248dd603SAdrian Chadd ip17x_statchg(device_t dev) 482248dd603SAdrian Chadd { 483248dd603SAdrian Chadd 484248dd603SAdrian Chadd DPRINTF(dev, "%s\n", __func__); 485248dd603SAdrian Chadd } 486248dd603SAdrian Chadd 487248dd603SAdrian Chadd static int 488248dd603SAdrian Chadd ip17x_ifmedia_upd(struct ifnet *ifp) 489248dd603SAdrian Chadd { 490248dd603SAdrian Chadd struct ip17x_softc *sc; 491248dd603SAdrian Chadd struct mii_data *mii; 492248dd603SAdrian Chadd 493248dd603SAdrian Chadd DPRINTF(sc->sc_dev, "%s\n", __func__); 494248dd603SAdrian Chadd sc = ifp->if_softc; 495248dd603SAdrian Chadd mii = ip17x_miiforport(sc, ifp->if_dunit); 496248dd603SAdrian Chadd if (mii == NULL) 497248dd603SAdrian Chadd return (ENXIO); 498248dd603SAdrian Chadd mii_mediachg(mii); 499248dd603SAdrian Chadd return (0); 500248dd603SAdrian Chadd } 501248dd603SAdrian Chadd 502248dd603SAdrian Chadd static void 503248dd603SAdrian Chadd ip17x_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) 504248dd603SAdrian Chadd { 505248dd603SAdrian Chadd struct ip17x_softc *sc; 506248dd603SAdrian Chadd struct mii_data *mii; 507248dd603SAdrian Chadd 508248dd603SAdrian Chadd DPRINTF(sc->sc_dev, "%s\n", __func__); 509248dd603SAdrian Chadd 510248dd603SAdrian Chadd sc = ifp->if_softc; 511248dd603SAdrian Chadd mii = ip17x_miiforport(sc, ifp->if_dunit); 512248dd603SAdrian Chadd if (mii == NULL) 513248dd603SAdrian Chadd return; 514248dd603SAdrian Chadd mii_pollstat(mii); 515248dd603SAdrian Chadd ifmr->ifm_active = mii->mii_media_active; 516248dd603SAdrian Chadd ifmr->ifm_status = mii->mii_media_status; 517248dd603SAdrian Chadd } 518248dd603SAdrian Chadd 519248dd603SAdrian Chadd static int 520248dd603SAdrian Chadd ip17x_readreg(device_t dev, int addr) 521248dd603SAdrian Chadd { 522248dd603SAdrian Chadd struct ip17x_softc *sc; 523248dd603SAdrian Chadd 524248dd603SAdrian Chadd sc = device_get_softc(dev); 525248dd603SAdrian Chadd IP17X_LOCK_ASSERT(sc, MA_OWNED); 526248dd603SAdrian Chadd 527248dd603SAdrian Chadd /* Not supported. */ 528248dd603SAdrian Chadd return (0); 529248dd603SAdrian Chadd } 530248dd603SAdrian Chadd 531248dd603SAdrian Chadd static int 532248dd603SAdrian Chadd ip17x_writereg(device_t dev, int addr, int value) 533248dd603SAdrian Chadd { 534248dd603SAdrian Chadd struct ip17x_softc *sc; 535248dd603SAdrian Chadd 536248dd603SAdrian Chadd sc = device_get_softc(dev); 537248dd603SAdrian Chadd IP17X_LOCK_ASSERT(sc, MA_OWNED); 538248dd603SAdrian Chadd 539248dd603SAdrian Chadd /* Not supported. */ 540248dd603SAdrian Chadd return (0); 541248dd603SAdrian Chadd } 542248dd603SAdrian Chadd 543248dd603SAdrian Chadd static int 544248dd603SAdrian Chadd ip17x_getconf(device_t dev, etherswitch_conf_t *conf) 545248dd603SAdrian Chadd { 546248dd603SAdrian Chadd struct ip17x_softc *sc; 547248dd603SAdrian Chadd 548248dd603SAdrian Chadd sc = device_get_softc(dev); 549248dd603SAdrian Chadd 550248dd603SAdrian Chadd /* Return the VLAN mode. */ 551248dd603SAdrian Chadd conf->cmd = ETHERSWITCH_CONF_VLAN_MODE; 552248dd603SAdrian Chadd conf->vlan_mode = sc->hal.ip17x_get_vlan_mode(sc); 553248dd603SAdrian Chadd 554248dd603SAdrian Chadd return (0); 555248dd603SAdrian Chadd } 556248dd603SAdrian Chadd 557248dd603SAdrian Chadd static int 558248dd603SAdrian Chadd ip17x_setconf(device_t dev, etherswitch_conf_t *conf) 559248dd603SAdrian Chadd { 560248dd603SAdrian Chadd struct ip17x_softc *sc; 561248dd603SAdrian Chadd 562248dd603SAdrian Chadd sc = device_get_softc(dev); 563248dd603SAdrian Chadd 564248dd603SAdrian Chadd /* Set the VLAN mode. */ 565248dd603SAdrian Chadd if (conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) 566248dd603SAdrian Chadd sc->hal.ip17x_set_vlan_mode(sc, conf->vlan_mode); 567248dd603SAdrian Chadd 568248dd603SAdrian Chadd return (0); 569248dd603SAdrian Chadd } 570248dd603SAdrian Chadd 571248dd603SAdrian Chadd static device_method_t ip17x_methods[] = { 572248dd603SAdrian Chadd /* Device interface */ 573248dd603SAdrian Chadd DEVMETHOD(device_probe, ip17x_probe), 574248dd603SAdrian Chadd DEVMETHOD(device_attach, ip17x_attach), 575248dd603SAdrian Chadd DEVMETHOD(device_detach, ip17x_detach), 576248dd603SAdrian Chadd 577248dd603SAdrian Chadd /* bus interface */ 578248dd603SAdrian Chadd DEVMETHOD(bus_add_child, device_add_child_ordered), 579248dd603SAdrian Chadd 580248dd603SAdrian Chadd /* MII interface */ 581248dd603SAdrian Chadd DEVMETHOD(miibus_readreg, ip17x_readphy), 582248dd603SAdrian Chadd DEVMETHOD(miibus_writereg, ip17x_writephy), 583248dd603SAdrian Chadd DEVMETHOD(miibus_statchg, ip17x_statchg), 584248dd603SAdrian Chadd 585248dd603SAdrian Chadd /* MDIO interface */ 586248dd603SAdrian Chadd DEVMETHOD(mdio_readreg, ip17x_readphy), 587248dd603SAdrian Chadd DEVMETHOD(mdio_writereg, ip17x_writephy), 588248dd603SAdrian Chadd 589248dd603SAdrian Chadd /* etherswitch interface */ 590248dd603SAdrian Chadd DEVMETHOD(etherswitch_lock, ip17x_lock), 591248dd603SAdrian Chadd DEVMETHOD(etherswitch_unlock, ip17x_unlock), 592248dd603SAdrian Chadd DEVMETHOD(etherswitch_getinfo, ip17x_getinfo), 593248dd603SAdrian Chadd DEVMETHOD(etherswitch_readreg, ip17x_readreg), 594248dd603SAdrian Chadd DEVMETHOD(etherswitch_writereg, ip17x_writereg), 595248dd603SAdrian Chadd DEVMETHOD(etherswitch_readphyreg, ip17x_readphy), 596248dd603SAdrian Chadd DEVMETHOD(etherswitch_writephyreg, ip17x_writephy), 597248dd603SAdrian Chadd DEVMETHOD(etherswitch_getport, ip17x_getport), 598248dd603SAdrian Chadd DEVMETHOD(etherswitch_setport, ip17x_setport), 599248dd603SAdrian Chadd DEVMETHOD(etherswitch_getvgroup, ip17x_getvgroup), 600248dd603SAdrian Chadd DEVMETHOD(etherswitch_setvgroup, ip17x_setvgroup), 601248dd603SAdrian Chadd DEVMETHOD(etherswitch_getconf, ip17x_getconf), 602248dd603SAdrian Chadd DEVMETHOD(etherswitch_setconf, ip17x_setconf), 603248dd603SAdrian Chadd 604248dd603SAdrian Chadd DEVMETHOD_END 605248dd603SAdrian Chadd }; 606248dd603SAdrian Chadd 607248dd603SAdrian Chadd DEFINE_CLASS_0(ip17x, ip17x_driver, ip17x_methods, 608248dd603SAdrian Chadd sizeof(struct ip17x_softc)); 609248dd603SAdrian Chadd static devclass_t ip17x_devclass; 610248dd603SAdrian Chadd 611248dd603SAdrian Chadd DRIVER_MODULE(ip17x, mdio, ip17x_driver, ip17x_devclass, 0, 0); 612248dd603SAdrian Chadd DRIVER_MODULE(miibus, ip17x, miibus_driver, miibus_devclass, 0, 0); 613248dd603SAdrian Chadd DRIVER_MODULE(mdio, ip17x, mdio_driver, mdio_devclass, 0, 0); 614248dd603SAdrian Chadd DRIVER_MODULE(etherswitch, ip17x, etherswitch_driver, etherswitch_devclass, 0, 0); 615248dd603SAdrian Chadd MODULE_VERSION(ip17x, 1); 616248dd603SAdrian Chadd MODULE_DEPEND(ip17x, miibus, 1, 1, 1); /* XXX which versions? */ 617248dd603SAdrian Chadd MODULE_DEPEND(ip17x, etherswitch, 1, 1, 1); /* XXX which versions? */ 618