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); 177248dd603SAdrian Chadd sc->ifp[port]->if_softc = sc; 178248dd603SAdrian Chadd sc->ifp[port]->if_flags |= IFF_UP | IFF_BROADCAST | 179248dd603SAdrian Chadd IFF_DRV_RUNNING | IFF_SIMPLEX; 1808237ba8aSLuiz Otavio O Souza if_initname(sc->ifp[port], name, port); 181248dd603SAdrian Chadd sc->miibus[port] = malloc(sizeof(device_t), M_IP17X, 182248dd603SAdrian Chadd M_WAITOK | M_ZERO); 183248dd603SAdrian Chadd err = mii_attach(sc->sc_dev, sc->miibus[port], sc->ifp[port], 184248dd603SAdrian Chadd ip17x_ifmedia_upd, ip17x_ifmedia_sts, \ 185248dd603SAdrian Chadd BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0); 186248dd603SAdrian Chadd DPRINTF(sc->sc_dev, "%s attached to pseudo interface %s\n", 187248dd603SAdrian Chadd device_get_nameunit(*sc->miibus[port]), 188248dd603SAdrian Chadd sc->ifp[port]->if_xname); 189248dd603SAdrian Chadd if (err != 0) { 190248dd603SAdrian Chadd device_printf(sc->sc_dev, 191248dd603SAdrian Chadd "attaching PHY %d failed\n", 192248dd603SAdrian Chadd phy); 193248dd603SAdrian Chadd break; 194248dd603SAdrian Chadd } 195248dd603SAdrian Chadd sc->info.es_nports = port + 1; 196248dd603SAdrian Chadd if (++port >= sc->numports) 197248dd603SAdrian Chadd break; 198248dd603SAdrian Chadd } 199248dd603SAdrian Chadd return (err); 200248dd603SAdrian Chadd } 201248dd603SAdrian Chadd 202248dd603SAdrian Chadd static int 203248dd603SAdrian Chadd ip17x_attach(device_t dev) 204248dd603SAdrian Chadd { 205248dd603SAdrian Chadd struct ip17x_softc *sc; 206248dd603SAdrian Chadd int err; 207248dd603SAdrian Chadd 208248dd603SAdrian Chadd sc = device_get_softc(dev); 209248dd603SAdrian Chadd 210248dd603SAdrian Chadd sc->sc_dev = dev; 211248dd603SAdrian Chadd mtx_init(&sc->sc_mtx, "ip17x", NULL, MTX_DEF); 212248dd603SAdrian Chadd strlcpy(sc->info.es_name, device_get_desc(dev), 213248dd603SAdrian Chadd sizeof(sc->info.es_name)); 214248dd603SAdrian Chadd 215248dd603SAdrian Chadd /* XXX Defaults */ 216248dd603SAdrian Chadd sc->phymask = 0x0f; 217248dd603SAdrian Chadd sc->media = 100; 218248dd603SAdrian Chadd 219248dd603SAdrian Chadd (void) resource_int_value(device_get_name(dev), device_get_unit(dev), 220248dd603SAdrian Chadd "phymask", &sc->phymask); 221248dd603SAdrian Chadd 222248dd603SAdrian Chadd /* Number of vlans supported by the switch. */ 223248dd603SAdrian Chadd sc->info.es_nvlangroups = IP17X_MAX_VLANS; 224248dd603SAdrian Chadd 225248dd603SAdrian Chadd /* Attach the switch related functions. */ 226248dd603SAdrian Chadd if (IP17X_IS_SWITCH(sc, IP175C)) 227248dd603SAdrian Chadd ip175c_attach(sc); 228248dd603SAdrian Chadd else if (IP17X_IS_SWITCH(sc, IP175D)) 229248dd603SAdrian Chadd ip175d_attach(sc); 230248dd603SAdrian Chadd else 231248dd603SAdrian Chadd /* We don't have support to all the models yet :-/ */ 232248dd603SAdrian Chadd return (ENXIO); 233248dd603SAdrian Chadd 234248dd603SAdrian Chadd /* Always attach the cpu port. */ 235248dd603SAdrian Chadd sc->phymask |= (1 << sc->cpuport); 236248dd603SAdrian Chadd 237248dd603SAdrian Chadd sc->ifp = malloc(sizeof(struct ifnet *) * sc->numports, M_IP17X, 238248dd603SAdrian Chadd M_WAITOK | M_ZERO); 239248dd603SAdrian Chadd sc->pvid = malloc(sizeof(uint32_t) * sc->numports, M_IP17X, 240248dd603SAdrian Chadd M_WAITOK | M_ZERO); 241248dd603SAdrian Chadd sc->miibus = malloc(sizeof(device_t *) * sc->numports, M_IP17X, 242248dd603SAdrian Chadd M_WAITOK | M_ZERO); 243248dd603SAdrian Chadd sc->portphy = malloc(sizeof(int) * sc->numports, M_IP17X, 244248dd603SAdrian Chadd M_WAITOK | M_ZERO); 245248dd603SAdrian Chadd 246248dd603SAdrian Chadd /* Initialize the switch. */ 247248dd603SAdrian Chadd sc->hal.ip17x_reset(sc); 248248dd603SAdrian Chadd 249248dd603SAdrian Chadd /* 250248dd603SAdrian Chadd * Attach the PHYs and complete the bus enumeration. 251248dd603SAdrian Chadd */ 252248dd603SAdrian Chadd err = ip17x_attach_phys(sc); 253248dd603SAdrian Chadd if (err != 0) 254248dd603SAdrian Chadd return (err); 255248dd603SAdrian Chadd 256248dd603SAdrian Chadd /* 257248dd603SAdrian Chadd * Set the switch to port based vlans or disabled (if not supported 258248dd603SAdrian Chadd * on this model). 259248dd603SAdrian Chadd */ 260248dd603SAdrian Chadd sc->hal.ip17x_set_vlan_mode(sc, ETHERSWITCH_VLAN_PORT); 261248dd603SAdrian Chadd 262248dd603SAdrian Chadd bus_generic_probe(dev); 263248dd603SAdrian Chadd bus_enumerate_hinted_children(dev); 264248dd603SAdrian Chadd err = bus_generic_attach(dev); 265248dd603SAdrian Chadd if (err != 0) 266248dd603SAdrian Chadd return (err); 267248dd603SAdrian Chadd 268a99badc2SAdrian Chadd if (sc->miipoll) { 269248dd603SAdrian Chadd callout_init(&sc->callout_tick, 0); 270248dd603SAdrian Chadd 271248dd603SAdrian Chadd ip17x_tick(sc); 272a99badc2SAdrian Chadd } 273248dd603SAdrian Chadd 274248dd603SAdrian Chadd return (0); 275248dd603SAdrian Chadd } 276248dd603SAdrian Chadd 277248dd603SAdrian Chadd static int 278248dd603SAdrian Chadd ip17x_detach(device_t dev) 279248dd603SAdrian Chadd { 280248dd603SAdrian Chadd struct ip17x_softc *sc; 281248dd603SAdrian Chadd int i, port; 282248dd603SAdrian Chadd 283248dd603SAdrian Chadd sc = device_get_softc(dev); 284a99badc2SAdrian Chadd if (sc->miipoll) 285248dd603SAdrian Chadd callout_drain(&sc->callout_tick); 286248dd603SAdrian Chadd 287248dd603SAdrian Chadd for (i=0; i < MII_NPHY; i++) { 288248dd603SAdrian Chadd if (((1 << i) & sc->phymask) == 0) 289248dd603SAdrian Chadd continue; 290248dd603SAdrian Chadd port = sc->phyport[i]; 291248dd603SAdrian Chadd if (sc->miibus[port] != NULL) 292248dd603SAdrian Chadd device_delete_child(dev, (*sc->miibus[port])); 293248dd603SAdrian Chadd if (sc->ifp[port] != NULL) 294248dd603SAdrian Chadd if_free(sc->ifp[port]); 295248dd603SAdrian Chadd free(sc->miibus[port], M_IP17X); 296248dd603SAdrian Chadd } 297248dd603SAdrian Chadd 298248dd603SAdrian Chadd free(sc->portphy, M_IP17X); 299248dd603SAdrian Chadd free(sc->miibus, M_IP17X); 300248dd603SAdrian Chadd free(sc->pvid, M_IP17X); 301248dd603SAdrian Chadd free(sc->ifp, M_IP17X); 302248dd603SAdrian Chadd 303248dd603SAdrian Chadd /* Reset the switch. */ 304248dd603SAdrian Chadd sc->hal.ip17x_reset(sc); 305248dd603SAdrian Chadd 306248dd603SAdrian Chadd bus_generic_detach(dev); 307248dd603SAdrian Chadd mtx_destroy(&sc->sc_mtx); 308248dd603SAdrian Chadd 309248dd603SAdrian Chadd return (0); 310248dd603SAdrian Chadd } 311248dd603SAdrian Chadd 312248dd603SAdrian Chadd static inline struct mii_data * 313248dd603SAdrian Chadd ip17x_miiforport(struct ip17x_softc *sc, int port) 314248dd603SAdrian Chadd { 315248dd603SAdrian Chadd 316248dd603SAdrian Chadd if (port < 0 || port > sc->numports) 317248dd603SAdrian Chadd return (NULL); 318248dd603SAdrian Chadd return (device_get_softc(*sc->miibus[port])); 319248dd603SAdrian Chadd } 320248dd603SAdrian Chadd 321248dd603SAdrian Chadd static inline struct ifnet * 322248dd603SAdrian Chadd ip17x_ifpforport(struct ip17x_softc *sc, int port) 323248dd603SAdrian Chadd { 324248dd603SAdrian Chadd 325248dd603SAdrian Chadd if (port < 0 || port > sc->numports) 326248dd603SAdrian Chadd return (NULL); 327248dd603SAdrian Chadd return (sc->ifp[port]); 328248dd603SAdrian Chadd } 329248dd603SAdrian Chadd 330248dd603SAdrian Chadd /* 331248dd603SAdrian Chadd * Poll the status for all PHYs. 332248dd603SAdrian Chadd */ 333248dd603SAdrian Chadd static void 334248dd603SAdrian Chadd ip17x_miipollstat(struct ip17x_softc *sc) 335248dd603SAdrian Chadd { 336248dd603SAdrian Chadd struct mii_softc *miisc; 337248dd603SAdrian Chadd struct mii_data *mii; 338248dd603SAdrian Chadd int i, port; 339248dd603SAdrian Chadd 340248dd603SAdrian Chadd IP17X_LOCK_ASSERT(sc, MA_NOTOWNED); 341248dd603SAdrian Chadd 342248dd603SAdrian Chadd for (i = 0; i < MII_NPHY; i++) { 343248dd603SAdrian Chadd if (((1 << i) & sc->phymask) == 0) 344248dd603SAdrian Chadd continue; 345248dd603SAdrian Chadd port = sc->phyport[i]; 346248dd603SAdrian Chadd if ((*sc->miibus[port]) == NULL) 347248dd603SAdrian Chadd continue; 348248dd603SAdrian Chadd mii = device_get_softc(*sc->miibus[port]); 349248dd603SAdrian Chadd LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { 350248dd603SAdrian Chadd if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) != 351248dd603SAdrian Chadd miisc->mii_inst) 352248dd603SAdrian Chadd continue; 353248dd603SAdrian Chadd ukphy_status(miisc); 354248dd603SAdrian Chadd mii_phy_update(miisc, MII_POLLSTAT); 355248dd603SAdrian Chadd } 356248dd603SAdrian Chadd } 357248dd603SAdrian Chadd } 358248dd603SAdrian Chadd 359248dd603SAdrian Chadd static void 360248dd603SAdrian Chadd ip17x_tick(void *arg) 361248dd603SAdrian Chadd { 362248dd603SAdrian Chadd struct ip17x_softc *sc; 363248dd603SAdrian Chadd 364248dd603SAdrian Chadd sc = arg; 365248dd603SAdrian Chadd ip17x_miipollstat(sc); 366248dd603SAdrian Chadd callout_reset(&sc->callout_tick, hz, ip17x_tick, sc); 367248dd603SAdrian Chadd } 368248dd603SAdrian Chadd 369248dd603SAdrian Chadd static void 370248dd603SAdrian Chadd ip17x_lock(device_t dev) 371248dd603SAdrian Chadd { 372248dd603SAdrian Chadd struct ip17x_softc *sc; 373248dd603SAdrian Chadd 374248dd603SAdrian Chadd sc = device_get_softc(dev); 375248dd603SAdrian Chadd IP17X_LOCK_ASSERT(sc, MA_NOTOWNED); 376248dd603SAdrian Chadd IP17X_LOCK(sc); 377248dd603SAdrian Chadd } 378248dd603SAdrian Chadd 379248dd603SAdrian Chadd static void 380248dd603SAdrian Chadd ip17x_unlock(device_t dev) 381248dd603SAdrian Chadd { 382248dd603SAdrian Chadd struct ip17x_softc *sc; 383248dd603SAdrian Chadd 384248dd603SAdrian Chadd sc = device_get_softc(dev); 385248dd603SAdrian Chadd IP17X_LOCK_ASSERT(sc, MA_OWNED); 386248dd603SAdrian Chadd IP17X_UNLOCK(sc); 387248dd603SAdrian Chadd } 388248dd603SAdrian Chadd 389248dd603SAdrian Chadd static etherswitch_info_t * 390248dd603SAdrian Chadd ip17x_getinfo(device_t dev) 391248dd603SAdrian Chadd { 392248dd603SAdrian Chadd struct ip17x_softc *sc; 393248dd603SAdrian Chadd 394248dd603SAdrian Chadd sc = device_get_softc(dev); 395248dd603SAdrian Chadd return (&sc->info); 396248dd603SAdrian Chadd } 397248dd603SAdrian Chadd 398248dd603SAdrian Chadd static int 399248dd603SAdrian Chadd ip17x_getport(device_t dev, etherswitch_port_t *p) 400248dd603SAdrian Chadd { 401248dd603SAdrian Chadd struct ip17x_softc *sc; 402248dd603SAdrian Chadd struct ifmediareq *ifmr; 403248dd603SAdrian Chadd struct mii_data *mii; 404248dd603SAdrian Chadd int err, phy; 405248dd603SAdrian Chadd 406248dd603SAdrian Chadd sc = device_get_softc(dev); 407248dd603SAdrian Chadd if (p->es_port < 0 || p->es_port >= sc->numports) 408248dd603SAdrian Chadd return (ENXIO); 409248dd603SAdrian Chadd 410248dd603SAdrian Chadd phy = sc->portphy[p->es_port]; 411248dd603SAdrian Chadd 412248dd603SAdrian Chadd /* Retrieve the PVID. */ 413248dd603SAdrian Chadd p->es_pvid = sc->pvid[phy]; 414248dd603SAdrian Chadd 415248dd603SAdrian Chadd /* Port flags. */ 416248dd603SAdrian Chadd if (sc->addtag & (1 << phy)) 417248dd603SAdrian Chadd p->es_flags |= ETHERSWITCH_PORT_ADDTAG; 418248dd603SAdrian Chadd if (sc->striptag & (1 << phy)) 419248dd603SAdrian Chadd p->es_flags |= ETHERSWITCH_PORT_STRIPTAG; 420248dd603SAdrian Chadd 421248dd603SAdrian Chadd ifmr = &p->es_ifmr; 422248dd603SAdrian Chadd 423248dd603SAdrian Chadd /* No media settings ? */ 424248dd603SAdrian Chadd if (p->es_ifmr.ifm_count == 0) 425248dd603SAdrian Chadd return (0); 426248dd603SAdrian Chadd 427248dd603SAdrian Chadd mii = ip17x_miiforport(sc, p->es_port); 428248dd603SAdrian Chadd if (mii == NULL) 429248dd603SAdrian Chadd return (ENXIO); 430248dd603SAdrian Chadd if (phy == sc->cpuport) { 431248dd603SAdrian Chadd /* fill in fixed values for CPU port */ 432248dd603SAdrian Chadd p->es_flags |= ETHERSWITCH_PORT_CPU; 433248dd603SAdrian Chadd ifmr->ifm_count = 0; 434248dd603SAdrian Chadd if (sc->media == 100) 435248dd603SAdrian Chadd ifmr->ifm_current = ifmr->ifm_active = 436248dd603SAdrian Chadd IFM_ETHER | IFM_100_TX | IFM_FDX; 437248dd603SAdrian Chadd else 438248dd603SAdrian Chadd ifmr->ifm_current = ifmr->ifm_active = 439248dd603SAdrian Chadd IFM_ETHER | IFM_1000_T | IFM_FDX; 440248dd603SAdrian Chadd ifmr->ifm_mask = 0; 441248dd603SAdrian Chadd ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID; 442248dd603SAdrian Chadd } else { 443248dd603SAdrian Chadd err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr, 444248dd603SAdrian Chadd &mii->mii_media, SIOCGIFMEDIA); 445248dd603SAdrian Chadd if (err) 446248dd603SAdrian Chadd return (err); 447248dd603SAdrian Chadd } 448248dd603SAdrian Chadd return (0); 449248dd603SAdrian Chadd } 450248dd603SAdrian Chadd 451248dd603SAdrian Chadd static int 452248dd603SAdrian Chadd ip17x_setport(device_t dev, etherswitch_port_t *p) 453248dd603SAdrian Chadd { 454248dd603SAdrian Chadd struct ip17x_softc *sc; 455248dd603SAdrian Chadd struct ifmedia *ifm; 456248dd603SAdrian Chadd struct ifnet *ifp; 457248dd603SAdrian Chadd struct mii_data *mii; 458248dd603SAdrian Chadd int phy; 459248dd603SAdrian Chadd 460248dd603SAdrian Chadd sc = device_get_softc(dev); 461248dd603SAdrian Chadd if (p->es_port < 0 || p->es_port >= sc->numports) 462248dd603SAdrian Chadd return (ENXIO); 463248dd603SAdrian Chadd 464248dd603SAdrian Chadd phy = sc->portphy[p->es_port]; 465248dd603SAdrian Chadd ifp = ip17x_ifpforport(sc, p->es_port); 466248dd603SAdrian Chadd mii = ip17x_miiforport(sc, p->es_port); 467248dd603SAdrian Chadd if (ifp == NULL || mii == NULL) 468248dd603SAdrian Chadd return (ENXIO); 469248dd603SAdrian Chadd 470248dd603SAdrian Chadd /* Port flags. */ 471248dd603SAdrian Chadd if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) { 472248dd603SAdrian Chadd 473248dd603SAdrian Chadd /* Set the PVID. */ 474248dd603SAdrian Chadd if (p->es_pvid != 0) { 475248dd603SAdrian Chadd if (IP17X_IS_SWITCH(sc, IP175C) && 476248dd603SAdrian Chadd p->es_pvid > IP175C_LAST_VLAN) 477248dd603SAdrian Chadd return (ENXIO); 478248dd603SAdrian Chadd sc->pvid[phy] = p->es_pvid; 479248dd603SAdrian Chadd } 480248dd603SAdrian Chadd 481248dd603SAdrian Chadd /* Mutually exclusive. */ 482248dd603SAdrian Chadd if (p->es_flags & ETHERSWITCH_PORT_ADDTAG && 483248dd603SAdrian Chadd p->es_flags & ETHERSWITCH_PORT_STRIPTAG) 484248dd603SAdrian Chadd return (EINVAL); 485248dd603SAdrian Chadd 486248dd603SAdrian Chadd /* Reset the settings for this port. */ 487248dd603SAdrian Chadd sc->addtag &= ~(1 << phy); 488248dd603SAdrian Chadd sc->striptag &= ~(1 << phy); 489248dd603SAdrian Chadd 490248dd603SAdrian Chadd /* And then set it to the new value. */ 491248dd603SAdrian Chadd if (p->es_flags & ETHERSWITCH_PORT_ADDTAG) 492248dd603SAdrian Chadd sc->addtag |= (1 << phy); 493248dd603SAdrian Chadd if (p->es_flags & ETHERSWITCH_PORT_STRIPTAG) 494248dd603SAdrian Chadd sc->striptag |= (1 << phy); 495248dd603SAdrian Chadd } 496248dd603SAdrian Chadd 497248dd603SAdrian Chadd /* Update the switch configuration. */ 498248dd603SAdrian Chadd if (sc->hal.ip17x_hw_setup(sc)) 499248dd603SAdrian Chadd return (ENXIO); 500248dd603SAdrian Chadd 501248dd603SAdrian Chadd /* Do not allow media changes on CPU port. */ 502248dd603SAdrian Chadd if (phy == sc->cpuport) 503248dd603SAdrian Chadd return (0); 504248dd603SAdrian Chadd 505248dd603SAdrian Chadd /* No media settings ? */ 506248dd603SAdrian Chadd if (p->es_ifmr.ifm_count == 0) 507248dd603SAdrian Chadd return (0); 508248dd603SAdrian Chadd 509248dd603SAdrian Chadd ifm = &mii->mii_media; 510248dd603SAdrian Chadd return (ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA)); 511248dd603SAdrian Chadd } 512248dd603SAdrian Chadd 513248dd603SAdrian Chadd static void 514248dd603SAdrian Chadd ip17x_statchg(device_t dev) 515248dd603SAdrian Chadd { 516248dd603SAdrian Chadd 517248dd603SAdrian Chadd DPRINTF(dev, "%s\n", __func__); 518248dd603SAdrian Chadd } 519248dd603SAdrian Chadd 520248dd603SAdrian Chadd static int 521248dd603SAdrian Chadd ip17x_ifmedia_upd(struct ifnet *ifp) 522248dd603SAdrian Chadd { 523248dd603SAdrian Chadd struct ip17x_softc *sc; 524248dd603SAdrian Chadd struct mii_data *mii; 525248dd603SAdrian Chadd 526248dd603SAdrian Chadd sc = ifp->if_softc; 5278237ba8aSLuiz Otavio O Souza DPRINTF(sc->sc_dev, "%s\n", __func__); 528248dd603SAdrian Chadd mii = ip17x_miiforport(sc, ifp->if_dunit); 529248dd603SAdrian Chadd if (mii == NULL) 530248dd603SAdrian Chadd return (ENXIO); 531248dd603SAdrian Chadd mii_mediachg(mii); 5328237ba8aSLuiz Otavio O Souza 533248dd603SAdrian Chadd return (0); 534248dd603SAdrian Chadd } 535248dd603SAdrian Chadd 536248dd603SAdrian Chadd static void 537248dd603SAdrian Chadd ip17x_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) 538248dd603SAdrian Chadd { 539248dd603SAdrian Chadd struct ip17x_softc *sc; 540248dd603SAdrian Chadd struct mii_data *mii; 541248dd603SAdrian Chadd 542248dd603SAdrian Chadd sc = ifp->if_softc; 5438237ba8aSLuiz Otavio O Souza DPRINTF(sc->sc_dev, "%s\n", __func__); 544248dd603SAdrian Chadd mii = ip17x_miiforport(sc, ifp->if_dunit); 545248dd603SAdrian Chadd if (mii == NULL) 546248dd603SAdrian Chadd return; 547248dd603SAdrian Chadd mii_pollstat(mii); 548248dd603SAdrian Chadd ifmr->ifm_active = mii->mii_media_active; 549248dd603SAdrian Chadd ifmr->ifm_status = mii->mii_media_status; 550248dd603SAdrian Chadd } 551248dd603SAdrian Chadd 552248dd603SAdrian Chadd static int 553248dd603SAdrian Chadd ip17x_readreg(device_t dev, int addr) 554248dd603SAdrian Chadd { 555248dd603SAdrian Chadd struct ip17x_softc *sc; 556248dd603SAdrian Chadd 557248dd603SAdrian Chadd sc = device_get_softc(dev); 558248dd603SAdrian Chadd IP17X_LOCK_ASSERT(sc, MA_OWNED); 559248dd603SAdrian Chadd 560248dd603SAdrian Chadd /* Not supported. */ 561248dd603SAdrian Chadd return (0); 562248dd603SAdrian Chadd } 563248dd603SAdrian Chadd 564248dd603SAdrian Chadd static int 565248dd603SAdrian Chadd ip17x_writereg(device_t dev, int addr, int value) 566248dd603SAdrian Chadd { 567248dd603SAdrian Chadd struct ip17x_softc *sc; 568248dd603SAdrian Chadd 569248dd603SAdrian Chadd sc = device_get_softc(dev); 570248dd603SAdrian Chadd IP17X_LOCK_ASSERT(sc, MA_OWNED); 571248dd603SAdrian Chadd 572248dd603SAdrian Chadd /* Not supported. */ 573248dd603SAdrian Chadd return (0); 574248dd603SAdrian Chadd } 575248dd603SAdrian Chadd 576248dd603SAdrian Chadd static int 577248dd603SAdrian Chadd ip17x_getconf(device_t dev, etherswitch_conf_t *conf) 578248dd603SAdrian Chadd { 579248dd603SAdrian Chadd struct ip17x_softc *sc; 580248dd603SAdrian Chadd 581248dd603SAdrian Chadd sc = device_get_softc(dev); 582248dd603SAdrian Chadd 583248dd603SAdrian Chadd /* Return the VLAN mode. */ 584248dd603SAdrian Chadd conf->cmd = ETHERSWITCH_CONF_VLAN_MODE; 585248dd603SAdrian Chadd conf->vlan_mode = sc->hal.ip17x_get_vlan_mode(sc); 586248dd603SAdrian Chadd 587248dd603SAdrian Chadd return (0); 588248dd603SAdrian Chadd } 589248dd603SAdrian Chadd 590248dd603SAdrian Chadd static int 591248dd603SAdrian Chadd ip17x_setconf(device_t dev, etherswitch_conf_t *conf) 592248dd603SAdrian Chadd { 593248dd603SAdrian Chadd struct ip17x_softc *sc; 594248dd603SAdrian Chadd 595248dd603SAdrian Chadd sc = device_get_softc(dev); 596248dd603SAdrian Chadd 597248dd603SAdrian Chadd /* Set the VLAN mode. */ 598248dd603SAdrian Chadd if (conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) 599248dd603SAdrian Chadd sc->hal.ip17x_set_vlan_mode(sc, conf->vlan_mode); 600248dd603SAdrian Chadd 601248dd603SAdrian Chadd return (0); 602248dd603SAdrian Chadd } 603248dd603SAdrian Chadd 604248dd603SAdrian Chadd static device_method_t ip17x_methods[] = { 605248dd603SAdrian Chadd /* Device interface */ 606a99badc2SAdrian Chadd DEVMETHOD(device_identify, ip17x_identify), 607248dd603SAdrian Chadd DEVMETHOD(device_probe, ip17x_probe), 608248dd603SAdrian Chadd DEVMETHOD(device_attach, ip17x_attach), 609248dd603SAdrian Chadd DEVMETHOD(device_detach, ip17x_detach), 610248dd603SAdrian Chadd 611248dd603SAdrian Chadd /* bus interface */ 612248dd603SAdrian Chadd DEVMETHOD(bus_add_child, device_add_child_ordered), 613248dd603SAdrian Chadd 614248dd603SAdrian Chadd /* MII interface */ 615248dd603SAdrian Chadd DEVMETHOD(miibus_readreg, ip17x_readphy), 616248dd603SAdrian Chadd DEVMETHOD(miibus_writereg, ip17x_writephy), 617248dd603SAdrian Chadd DEVMETHOD(miibus_statchg, ip17x_statchg), 618248dd603SAdrian Chadd 619248dd603SAdrian Chadd /* MDIO interface */ 620248dd603SAdrian Chadd DEVMETHOD(mdio_readreg, ip17x_readphy), 621248dd603SAdrian Chadd DEVMETHOD(mdio_writereg, ip17x_writephy), 622248dd603SAdrian Chadd 623248dd603SAdrian Chadd /* etherswitch interface */ 624248dd603SAdrian Chadd DEVMETHOD(etherswitch_lock, ip17x_lock), 625248dd603SAdrian Chadd DEVMETHOD(etherswitch_unlock, ip17x_unlock), 626248dd603SAdrian Chadd DEVMETHOD(etherswitch_getinfo, ip17x_getinfo), 627248dd603SAdrian Chadd DEVMETHOD(etherswitch_readreg, ip17x_readreg), 628248dd603SAdrian Chadd DEVMETHOD(etherswitch_writereg, ip17x_writereg), 629248dd603SAdrian Chadd DEVMETHOD(etherswitch_readphyreg, ip17x_readphy), 630248dd603SAdrian Chadd DEVMETHOD(etherswitch_writephyreg, ip17x_writephy), 631248dd603SAdrian Chadd DEVMETHOD(etherswitch_getport, ip17x_getport), 632248dd603SAdrian Chadd DEVMETHOD(etherswitch_setport, ip17x_setport), 633248dd603SAdrian Chadd DEVMETHOD(etherswitch_getvgroup, ip17x_getvgroup), 634248dd603SAdrian Chadd DEVMETHOD(etherswitch_setvgroup, ip17x_setvgroup), 635248dd603SAdrian Chadd DEVMETHOD(etherswitch_getconf, ip17x_getconf), 636248dd603SAdrian Chadd DEVMETHOD(etherswitch_setconf, ip17x_setconf), 637248dd603SAdrian Chadd 638248dd603SAdrian Chadd DEVMETHOD_END 639248dd603SAdrian Chadd }; 640248dd603SAdrian Chadd 641248dd603SAdrian Chadd DEFINE_CLASS_0(ip17x, ip17x_driver, ip17x_methods, 642248dd603SAdrian Chadd sizeof(struct ip17x_softc)); 643248dd603SAdrian Chadd static devclass_t ip17x_devclass; 644248dd603SAdrian Chadd 645248dd603SAdrian Chadd DRIVER_MODULE(ip17x, mdio, ip17x_driver, ip17x_devclass, 0, 0); 646248dd603SAdrian Chadd DRIVER_MODULE(miibus, ip17x, miibus_driver, miibus_devclass, 0, 0); 647248dd603SAdrian Chadd DRIVER_MODULE(etherswitch, ip17x, etherswitch_driver, etherswitch_devclass, 0, 0); 648248dd603SAdrian Chadd MODULE_VERSION(ip17x, 1); 649a99badc2SAdrian Chadd 650a99badc2SAdrian Chadd #ifdef FDT 651a99badc2SAdrian Chadd MODULE_DEPEND(ip17x, mdio, 1, 1, 1); /* XXX which versions? */ 652a99badc2SAdrian Chadd #else 653a99badc2SAdrian Chadd DRIVER_MODULE(mdio, ip17x, mdio_driver, mdio_devclass, 0, 0); 654248dd603SAdrian Chadd MODULE_DEPEND(ip17x, miibus, 1, 1, 1); /* XXX which versions? */ 655248dd603SAdrian Chadd MODULE_DEPEND(ip17x, etherswitch, 1, 1, 1); /* XXX which versions? */ 656a99badc2SAdrian Chadd #endif 657