aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/etherswitch/e6000sw/e6000sw.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/etherswitch/e6000sw/e6000sw.c')
-rw-r--r--sys/dev/etherswitch/e6000sw/e6000sw.c94
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