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