static int mlphy_service(struct mii_softc *xsc, struct mii_data *mii, int cmd) { struct ifmedia_entry *ife = mii->mii_media.ifm_cur; int reg; struct mii_softc *other = NULL; struct mlphy_softc *msc = (struct mlphy_softc *)xsc; struct mii_softc *sc = &msc->ml_mii; device_t *devlist; int devs, i; const struct mii_media *mm; /* * See if there's another PHY on this bus with us. * If so, we may need it for 10Mbps modes. */ device_get_children(msc->ml_mii.mii_dev, &devlist, &devs); for (i = 0; i < devs; i++) { if (strcmp(device_get_name(devlist[i]), "mlphy")) { other = device_get_softc(devlist[i]); break; } } kfree(devlist, M_TEMP); KKASSERT(ife->ifm_data >= 0 && ife->ifm_data < MII_NMEDIA); mm = &mii_media_table[ife->ifm_data]; switch (cmd) { case MII_POLLSTAT: /* * If we're not polling our PHY instance, just return. */ if (IFM_INST(ife->ifm_media) != sc->mii_inst) return (0); break; case MII_MEDIACHG: /* * If the media indicates a different PHY instance, * isolate ourselves. */ if (IFM_INST(ife->ifm_media) != sc->mii_inst) { reg = PHY_READ(sc, MII_BMCR); PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO); return (0); } /* * If the interface is not up, don't do anything. */ if ((mii->mii_ifp->if_flags & IFF_UP) == 0) break; switch (IFM_SUBTYPE(ife->ifm_media)) { case IFM_AUTO: /* * For autonegotiation, reset and isolate the * companion PHY (if any) and then do NWAY * autonegotiation ourselves. */ msc->ml_state = ML_STATE_AUTO_SELF; if (other != NULL) { mii_phy_reset(other); PHY_WRITE(other, MII_BMCR, BMCR_ISO); } mii_phy_auto(sc, 1); msc->ml_linked = 0; return(0); case IFM_10_T: /* * For 10baseT modes, reset and program the * companion PHY (of any), then program ourselves * to match. This will put us in pass-through * mode and let the companion PHY do all the * work. */ if (other != NULL) { mii_phy_reset(other); PHY_WRITE(other, MII_BMCR, mm->mm_bmcr); } PHY_WRITE(sc, MII_ANAR, mm->mm_anar); PHY_WRITE(sc, MII_BMCR, mm->mm_bmcr); msc->ml_state = 0; break; case IFM_100_TX: /* * For 100baseTX modes, reset and isolate the * companion PHY (if any), then program ourselves * accordingly. */ if (other != NULL) { mii_phy_reset(other); PHY_WRITE(other, MII_BMCR, BMCR_ISO); } PHY_WRITE(sc, MII_ANAR, mm->mm_anar); PHY_WRITE(sc, MII_BMCR, mm->mm_bmcr); msc->ml_state = 0; break; case IFM_100_T4: /* * XXX Not supported as a manual setting right now. */ return (EINVAL); default: break; } break; case MII_TICK: /* * If we're not currently selected, just return. */ if (IFM_INST(ife->ifm_media) != sc->mii_inst) return (0); /* * Is the interface even up? */ if ((mii->mii_ifp->if_flags & IFF_UP) == 0) return (0); /* * Only used for autonegotiation. */ if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) break; /* * Check to see if we have link. If we do, we don't * need to restart the autonegotiation process. Read * the BMSR twice in case it's latched. * If we're in a 10Mbps mode, check the link of the * 10Mbps PHY. Sometimes the Micro Linear PHY's * linkstat bit will clear while the linkstat bit of * the companion PHY will remain set. */ if (msc->ml_state == ML_STATE_AUTO_OTHER) { reg = PHY_READ(other, MII_BMSR) | PHY_READ(other, MII_BMSR); } else { reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); } if (reg & BMSR_LINK) { if (!msc->ml_linked) { msc->ml_linked = 1; mlphy_status(sc); } sc->mii_ticks = 0; break; } /* * Only retry autonegotiation every 5 seconds. */ if (++sc->mii_ticks <= sc->mii_anegticks) return (0); sc->mii_ticks = 0; msc->ml_linked = 0; mii->mii_media_active = IFM_NONE; mii_phy_reset(sc); msc->ml_state = ML_STATE_AUTO_SELF; if (other != NULL) { mii_phy_reset(other); PHY_WRITE(other, MII_BMCR, BMCR_ISO); } if (mii_phy_auto(sc, 0) == EJUSTRETURN) return(0); break; } /* Update the media status. */ if (msc->ml_state == ML_STATE_AUTO_OTHER) { int other_inst; other_inst = other->mii_inst; other->mii_inst = sc->mii_inst; other->mii_service(other, mii, MII_POLLSTAT); other->mii_inst = other_inst; sc->mii_media_active = other->mii_media_active; sc->mii_media_status = other->mii_media_status; } else { ukphy_status(sc); } /* Callback if something changed. */ mii_phy_update(sc, cmd); return (0); }
int mlphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) { struct ifmedia_entry *ife = mii->mii_media.ifm_cur; struct mii_softc *other = NULL; struct mlphy_softc *msc = (struct mlphy_softc *)sc; int other_inst, reg; LIST_FOREACH(other, &mii->mii_phys, mii_list) if (other != sc) break; if ((sc->mii_dev.dv_flags & DVF_ACTIVE) == 0) return (ENXIO); switch (cmd) { case MII_POLLSTAT: /* * If we're not polling our PHY instance, just return. */ if (IFM_INST(ife->ifm_media) != sc->mii_inst) return (0); break; case MII_MEDIACHG: /* * If the interface is not up, don't do anything. */ if ((mii->mii_ifp->if_flags & IFF_UP) == 0) break; switch (IFM_SUBTYPE(ife->ifm_media)) { case IFM_AUTO: msc->ml_state = ML_STATE_AUTO_SELF; if (other != NULL) { mii_phy_reset(other); PHY_WRITE(other, MII_BMCR, BMCR_ISO); } (void)mii_phy_auto(sc, 0); msc->ml_linked = 0; break; case IFM_10_T: /* * For 10baseT modes, reset and program the * companion PHY (of any), then setup ourselves * to match. This will put us in pass-through * mode and let the companion PHY do all the * work. * BMCR data is stored in the ifmedia entry. */ if (other != NULL) { mii_phy_reset(other); PHY_WRITE(other, MII_BMCR, ife->ifm_data); } mii_phy_setmedia(sc); msc->ml_state = 0; break; case IFM_100_TX: /* * For 100baseTX modes, reset and isolate the * companion PHY (if any), then setup ourselves * accordingly. * * BMCR data is stored in the ifmedia entry. */ if (other != NULL) { mii_phy_reset(other); PHY_WRITE(other, MII_BMCR, BMCR_ISO); } mii_phy_setmedia(sc); msc->ml_state = 0; break; default: return (EINVAL); } break; case MII_TICK: /* * If interface is not up, don't do anything */ if ((mii->mii_ifp->if_flags & IFF_UP) == 0) return (0); /* * Only used for autonegotiation. */ if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) break; /* * Check to see if we have link. If we do, we don't * need to restart the autonegotiation process. Read * the BMSR twice in case it's latched. * If we're in a 10Mbps mode, check the link of the * 10Mbps PHY. Sometimes the Micro Linear PHY's * linkstat bit will clear while the linkstat bit of * the companion PHY will remain set. */ if (msc->ml_state == ML_STATE_AUTO_OTHER) { reg = PHY_READ(other, MII_BMSR) | PHY_READ(other, MII_BMSR); } else { reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); } if (reg & BMSR_LINK) { if (!msc->ml_linked) { msc->ml_linked = 1; mlphy_status(sc); } sc->mii_ticks = 0; break; } /* * Only retry autonegotiation every 5 seconds. */ if (++sc->mii_ticks <= MII_ANEGTICKS) break; sc->mii_ticks = 0; msc->ml_linked = 0; mii->mii_media_active = IFM_NONE; mii_phy_reset(sc); msc->ml_state = ML_STATE_AUTO_SELF; if (other != NULL) { mii_phy_reset(other); PHY_WRITE(other, MII_BMCR, BMCR_ISO); } mii_phy_auto(sc, 0); break; case MII_DOWN: mii_phy_down(sc); return (0); } /* Update the media status. */ if (msc->ml_state == ML_STATE_AUTO_OTHER && other != NULL) { other_inst = other->mii_inst; other->mii_inst = sc->mii_inst; if (IFM_INST(ife->ifm_media) == other->mii_inst) (void) PHY_SERVICE(other, mii, MII_POLLSTAT); other->mii_inst = other_inst; sc->mii_media_active = other->mii_media_active; sc->mii_media_status = other->mii_media_status; } else ukphy_status(sc); /* Callback if something changed. */ mii_phy_update(sc, cmd); return (0); }