summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/dev/mii/nsphy.c126
1 files changed, 77 insertions, 49 deletions
diff --git a/sys/dev/mii/nsphy.c b/sys/dev/mii/nsphy.c
index d6c4726..c15a814 100644
--- a/sys/dev/mii/nsphy.c
+++ b/sys/dev/mii/nsphy.c
@@ -117,6 +117,7 @@ DRIVER_MODULE(nsphy, miibus, nsphy_driver, nsphy_devclass, 0, 0);
static int nsphy_service(struct mii_softc *, struct mii_data *, int);
static void nsphy_status(struct mii_softc *);
+static void nsphy_reset(struct mii_softc *);
static int
nsphy_probe(device_t dev)
@@ -128,7 +129,7 @@ nsphy_probe(device_t dev)
if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_NATSEMI &&
MII_MODEL(ma->mii_id2) == MII_MODEL_NATSEMI_DP83840) {
device_set_desc(dev, MII_STR_NATSEMI_DP83840);
- } else
+ } else
return (ENXIO);
return (BUS_PROBE_DEFAULT);
@@ -140,6 +141,7 @@ nsphy_attach(device_t dev)
struct mii_softc *sc;
struct mii_attach_args *ma;
struct mii_data *mii;
+ const char *nic;
sc = device_get_softc(dev);
ma = device_get_ivars(dev);
@@ -152,38 +154,51 @@ nsphy_attach(device_t dev)
sc->mii_service = nsphy_service;
sc->mii_pdata = mii;
-#ifdef foo
+ mii->mii_instance++;
+
+ nic = device_get_name(device_get_parent(sc->mii_dev));
/*
- * i82557 wedges if all of its PHYs are isolated!
+ * Am79C971 and i82557 wedge when isolating all of their
+ * (external) PHYs.
*/
- if (strcmp(device_get_name(device_get_parent(sc->mii_dev)),
- "fxp") == 0 && mii->mii_instance == 0)
-#endif
+ if (strcmp(nic, "fxp") == 0 || strcmp(nic, "pcn") == 0)
+ sc->mii_flags |= MIIF_NOISOLATE;
- sc->mii_flags |= MIIF_NOISOLATE;
- mii->mii_instance++;
+ /*
+ * DP83840A used with HME chips don't advertise their media
+ * capabilities themselves properly so force writing the ANAR
+ * according to the BMSR in mii_phy_setmedia().
+ */
+ if (strcmp(nic, "hme") == 0)
+ sc->mii_flags |= MIIF_FORCEANEG;
#define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL)
-#if 0
- /* Can't do this on the i82557! */
- ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst),
- BMCR_ISO);
+ /*
+ * In order for MII loopback to work Am79C971 and greater PCnet
+ * chips additionally need to be placed into external loopback
+ * mode which pcn(4) doesn't do so far.
+ */
+ if (strcmp(nic, "pcn") != 0)
+#if 1
+ ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP,
+ sc->mii_inst), BMCR_LOOP|BMCR_S100);
+#else
+ if (strcmp(nic, "pcn") == 0)
+ sc->mii_flags |= MIIF_NOLOOP;
#endif
- ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, sc->mii_inst),
- BMCR_LOOP|BMCR_S100);
- mii_phy_reset(sc);
+ nsphy_reset(sc);
sc->mii_capabilities =
PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
device_printf(dev, " ");
- mii_add_media(sc);
+ mii_phy_add_media(sc);
printf("\n");
#undef ADD
MIIBUS_MEDIAINIT(sc->mii_dev);
- return(0);
+ return (0);
}
static int
@@ -191,7 +206,6 @@ nsphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
{
struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
int reg;
- device_t parent;
switch (cmd) {
case MII_POLLSTAT:
@@ -219,8 +233,6 @@ nsphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
break;
- parent = device_get_parent(sc->mii_dev);
-
reg = PHY_READ(sc, MII_NSPHY_PCR);
/*
@@ -254,36 +266,10 @@ nsphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
*/
reg |= 0x0100 | 0x0400;
- if (strcmp(device_get_name(parent), "fxp") == 0)
+ if (strcmp(mii->mii_ifp->if_dname, "fxp") == 0)
PHY_WRITE(sc, MII_NSPHY_PCR, reg);
- switch (IFM_SUBTYPE(ife->ifm_media)) {
- case IFM_AUTO:
- /*
- * If we're already in auto mode and don't hang off
- * of a hme(4), just return. DP83840A on hme(4) don't
- * advertise their media capabilities themselves
- * properly so force writing the ANAR according to
- * the BMSR by mii_phy_auto() every time.
- */
- if ((PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) == 0 &&
- strcmp(device_get_name(parent), "hme") != 0)
- return (0);
- (void) mii_phy_auto(sc);
- break;
- case IFM_100_T4:
- /*
- * XXX Not supported as a manual setting right now.
- */
- return (EINVAL);
- default:
- /*
- * BMCR data is stored in the ifmedia entry.
- */
- PHY_WRITE(sc, MII_ANAR,
- mii_anar(ife->ifm_media));
- PHY_WRITE(sc, MII_BMCR, ife->ifm_data);
- }
+ mii_phy_setmedia(sc);
break;
case MII_TICK:
@@ -309,6 +295,7 @@ static void
nsphy_status(struct mii_softc *sc)
{
struct mii_data *mii = sc->mii_pdata;
+ struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
int bmsr, bmcr, par, anlpar;
mii->mii_media_status = IFM_AVALID;
@@ -378,5 +365,46 @@ nsphy_status(struct mii_softc *sc)
mii->mii_media_active |= IFM_FDX;
#endif
} else
- mii->mii_media_active |= mii_media_from_bmcr(bmcr);
+ mii->mii_media_active = ife->ifm_media;
+}
+
+static void
+nsphy_reset(struct mii_softc *sc)
+{
+ struct ifmedia_entry *ife = sc->mii_pdata->mii_media.ifm_cur;
+ int reg, i;
+
+ if (sc->mii_flags & MIIF_NOISOLATE)
+ reg = BMCR_RESET;
+ else
+ reg = BMCR_RESET | BMCR_ISO;
+ PHY_WRITE(sc, MII_BMCR, reg);
+
+ /*
+ * Give it a little time to settle in case we just got power.
+ * The DP83840A data sheet suggests that a soft reset should not
+ * happen within 500us of power being applied. Be conservative.
+ */
+ DELAY(1000);
+
+ /*
+ * Wait another 2s for it to complete.
+ * This is only a little overkill as under normal circumstances
+ * the PHY can take up to 1s to complete reset.
+ * This is also a bit odd because after a reset, the BMCR will
+ * clear the reset bit and simply reports 0 even though the reset
+ * is not yet complete.
+ */
+ for (i = 0; i < 1000; i++) {
+ reg = PHY_READ(sc, MII_BMCR);
+ if (reg != 0 && (reg & BMCR_RESET) == 0)
+ break;
+ DELAY(2000);
+ }
+
+ if ((sc->mii_flags & MIIF_NOISOLATE) == 0) {
+ if ((ife == NULL && sc->mii_inst != 0) ||
+ (ife != NULL && IFM_INST(ife->ifm_media) != sc->mii_inst))
+ PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
+ }
}
OpenPOWER on IntegriCloud