diff options
Diffstat (limited to 'sys/dev/etherswitch/e6000sw/e6000sw.c')
-rw-r--r-- | sys/dev/etherswitch/e6000sw/e6000sw.c | 94 |
1 files changed, 72 insertions, 22 deletions
diff --git a/sys/dev/etherswitch/e6000sw/e6000sw.c b/sys/dev/etherswitch/e6000sw/e6000sw.c index 95f1a2e96db6..7e9193f4ba47 100644 --- a/sys/dev/etherswitch/e6000sw/e6000sw.c +++ b/sys/dev/etherswitch/e6000sw/e6000sw.c @@ -51,7 +51,7 @@ #include <dev/ofw/ofw_bus.h> #include <dev/ofw/ofw_bus_subr.h> #else -#include <machine/stdarg.h> +#include <sys/stdarg.h> #endif #include "e6000swreg.h" @@ -89,6 +89,7 @@ typedef struct e6000sw_softc { device_t miibus[E6000SW_MAX_PORTS]; struct taskqueue *sc_tq; struct timeout_task sc_tt; + bool is_shutdown; int vlans[E6000SW_NUM_VLANS]; uint32_t swid; @@ -195,17 +196,17 @@ DEFINE_CLASS_0(e6000sw, e6000sw_driver, e6000sw_methods, sizeof(e6000sw_softc_t)); DRIVER_MODULE(e6000sw, mdio, e6000sw_driver, 0, 0); -DRIVER_MODULE(etherswitch, e6000sw, etherswitch_driver, 0, 0); DRIVER_MODULE(miibus, e6000sw, miibus_driver, 0, 0); +DRIVER_MODULE_ORDERED(etherswitch, e6000sw, etherswitch_driver, 0, 0, SI_ORDER_ANY); MODULE_DEPEND(e6000sw, mdio, 1, 1, 1); - +MODULE_DEPEND(e6000sw, etherswitch, 1, 1, 1); static void e6000sw_identify(driver_t *driver, device_t parent) { - if (device_find_child(parent, "e6000sw", -1) == NULL) - BUS_ADD_CHILD(parent, 0, "e6000sw", -1); + if (device_find_child(parent, "e6000sw", DEVICE_UNIT_ANY) == NULL) + BUS_ADD_CHILD(parent, 0, "e6000sw", DEVICE_UNIT_ANY); } static int @@ -216,7 +217,8 @@ e6000sw_probe(device_t dev) #ifdef FDT phandle_t switch_node; #else - int is_6190; + int is_6190 = 0; + int is_6190x = 0; #endif sc = device_get_softc(dev); @@ -252,15 +254,25 @@ e6000sw_probe(device_t dev) device_get_unit(sc->dev), "addr", &sc->sw_addr) != 0) return (ENXIO); if (resource_int_value(device_get_name(sc->dev), - device_get_unit(sc->dev), "is6190", &is_6190) != 0) + device_get_unit(sc->dev), "is6190", &is_6190) != 0) { /* * Check "is8190" to keep backward compatibility with * older setups. */ resource_int_value(device_get_name(sc->dev), device_get_unit(sc->dev), "is8190", &is_6190); + } + resource_int_value(device_get_name(sc->dev), + device_get_unit(sc->dev), "is6190x", &is_6190x); + if (is_6190 != 0 && is_6190x != 0) { + device_printf(dev, + "Cannot configure conflicting variants (6190 / 6190x)\n"); + return (ENXIO); + } if (is_6190 != 0) sc->swid = MV88E6190; + else if (is_6190x != 0) + sc->swid = MV88E6190X; #endif if (sc->sw_addr < 0 || sc->sw_addr > 32) return (ENXIO); @@ -302,6 +314,10 @@ e6000sw_probe(device_t dev) description = "Marvell 88E6190"; sc->num_ports = 11; break; + case MV88E6190X: + description = "Marvell 88E6190X"; + sc->num_ports = 11; + break; default: device_printf(dev, "Unrecognized device, id 0x%x.\n", sc->swid); return (ENXIO); @@ -332,7 +348,7 @@ e6000sw_parse_fixed_link(e6000sw_softc_t *sc, phandle_t node, uint32_t port) return (ENXIO); } if (speed == 2500 && (MVSWITCH(sc, MV88E6141) || - MVSWITCH(sc, MV88E6341) || MVSWITCH(sc, MV88E6190))) + MVSWITCH(sc, MV88E6341) || MVSWITCH(sc, MV88E6190) || MVSWITCH(sc, MV88E6190X))) sc->fixed25_mask |= (1 << port); } @@ -454,8 +470,6 @@ e6000sw_init_interface(e6000sw_softc_t *sc, int port) snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->dev)); sc->ifp[port] = if_alloc(IFT_ETHER); - if (sc->ifp[port] == NULL) - return (ENOMEM); if_setsoftc(sc->ifp[port], sc); if_setflagbits(sc->ifp[port], IFF_UP | IFF_BROADCAST | IFF_DRV_RUNNING | IFF_SIMPLEX, 0); @@ -598,22 +612,26 @@ e6000sw_attach(device_t dev) reg |= PSC_CONTROL_SPD2500; else reg |= PSC_CONTROL_SPD1000; - if (MVSWITCH(sc, MV88E6190) && + if ((MVSWITCH(sc, MV88E6190) || + MVSWITCH(sc, MV88E6190X)) && e6000sw_is_fixed25port(sc, port)) reg |= PSC_CONTROL_ALT_SPD; reg |= PSC_CONTROL_FORCED_DPX | PSC_CONTROL_FULLDPX | PSC_CONTROL_FORCED_LINK | PSC_CONTROL_LINK_UP | PSC_CONTROL_FORCED_SPD; - if (!MVSWITCH(sc, MV88E6190)) + if (!MVSWITCH(sc, MV88E6190) && + !MVSWITCH(sc, MV88E6190X)) reg |= PSC_CONTROL_FORCED_FC | PSC_CONTROL_FC_ON; if (MVSWITCH(sc, MV88E6141) || MVSWITCH(sc, MV88E6341) || - MVSWITCH(sc, MV88E6190)) + MVSWITCH(sc, MV88E6190) || + MVSWITCH(sc, MV88E6190X)) reg |= PSC_CONTROL_FORCED_EEE; e6000sw_writereg(sc, REG_PORT(sc, port), PSC_CONTROL, reg); /* Power on the SERDES interfaces. */ - if (MVSWITCH(sc, MV88E6190) && + if ((MVSWITCH(sc, MV88E6190) || + MVSWITCH(sc, MV88E6190X)) && (port == 9 || port == 10)) { if (e6000sw_is_fixed25port(sc, port)) sgmii = false; @@ -644,14 +662,15 @@ e6000sw_attach(device_t dev) device_printf(dev, "switch is ready.\n"); E6000SW_UNLOCK(sc); - bus_generic_probe(dev); - bus_generic_attach(dev); + bus_identify_children(dev); + bus_attach_children(dev); taskqueue_enqueue_timeout(sc->sc_tq, &sc->sc_tt, hz); return (0); out_fail: + E6000SW_UNLOCK(sc); e6000sw_detach(dev); return (err); @@ -847,19 +866,26 @@ e6000sw_writephy_locked(device_t dev, int phy, int reg, int data) static int e6000sw_detach(device_t dev) { - int phy; + int error, phy; e6000sw_softc_t *sc; sc = device_get_softc(dev); - if (device_is_attached(dev)) - taskqueue_drain_timeout(sc->sc_tq, &sc->sc_tt); + E6000SW_LOCK(sc); + sc->is_shutdown = true; + if (sc->sc_tq != NULL) { + while (taskqueue_cancel_timeout(sc->sc_tq, &sc->sc_tt, NULL) != 0) + taskqueue_drain_timeout(sc->sc_tq, &sc->sc_tt); + } + E6000SW_UNLOCK(sc); + + error = bus_generic_detach(dev); + if (error != 0) + return (error); if (sc->sc_tq != NULL) taskqueue_free(sc->sc_tq); - device_delete_children(dev); - sx_destroy(&sc->sx); for (phy = 0; phy < sc->num_ports; phy++) { if (sc->ifp[phy] != NULL) @@ -1376,11 +1402,17 @@ e6000sw_getvgroup(device_t dev, etherswitch_vlangroup_t *vg) static __inline struct mii_data* e6000sw_miiforphy(e6000sw_softc_t *sc, unsigned int phy) { + device_t mii_dev; if (!e6000sw_is_phyport(sc, phy)) return (NULL); + mii_dev = sc->miibus[phy]; + if (mii_dev == NULL) + return (NULL); + if (device_get_state(mii_dev) != DS_ATTACHED) + return (NULL); - return (device_get_softc(sc->miibus[phy])); + return (device_get_softc(mii_dev)); } static int @@ -1583,6 +1615,12 @@ e6000sw_tick(void *arg, int p __unused) E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED); E6000SW_LOCK(sc); + + if (sc->is_shutdown) { + E6000SW_UNLOCK(sc); + return; + } + for (port = 0; port < sc->num_ports; port++) { /* Tick only on PHY ports */ if (!e6000sw_is_portenabled(sc, port) || @@ -1600,6 +1638,17 @@ e6000sw_tick(void *arg, int p __unused) &mii->mii_media_status, &mii->mii_media_active); LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { + /* + * Note: this is sometimes NULL during PHY + * enumeration, although that shouldn't be + * happening /after/ tick runs. To work + * around this whilst the problem is being + * debugged, just do a NULL check here and + * continue. + */ + if (mii->mii_media.ifm_cur == NULL) + continue; + if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) != miisc->mii_inst) continue; @@ -1607,6 +1656,7 @@ e6000sw_tick(void *arg, int p __unused) } } E6000SW_UNLOCK(sc); + taskqueue_enqueue_timeout(sc->sc_tq, &sc->sc_tt, hz); } static void |