summaryrefslogtreecommitdiffstats
path: root/sys/dev/hme/if_hme.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/hme/if_hme.c')
-rw-r--r--sys/dev/hme/if_hme.c136
1 files changed, 91 insertions, 45 deletions
diff --git a/sys/dev/hme/if_hme.c b/sys/dev/hme/if_hme.c
index c281e1b..61b021f 100644
--- a/sys/dev/hme/if_hme.c
+++ b/sys/dev/hme/if_hme.c
@@ -34,7 +34,7 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
- * from: NetBSD: hme.c,v 1.29 2002/05/05 03:02:38 thorpej Exp
+ * from: NetBSD: hme.c,v 1.35 2003/02/27 14:58:22 pk Exp
*/
#include <sys/cdefs.h>
@@ -115,6 +115,7 @@ static void hme_mifinit(struct hme_softc *);
static void hme_setladrf(struct hme_softc *, int);
static int hme_mediachange(struct ifnet *);
+static int hme_mediachange_locked(struct hme_softc *);
static void hme_mediastatus(struct ifnet *, struct ifmediareq *);
static int hme_load_txmbuf(struct hme_softc *, struct mbuf **);
@@ -310,19 +311,21 @@ hme_config(struct hme_softc *sc)
/*
* Walk along the list of attached MII devices and
- * establish an `MII instance' to `phy number'
- * mapping. We'll use this mapping in media change
- * requests to determine which phy to use to program
- * the MIF configuration register.
+ * establish an `MII instance' to `PHY number'
+ * mapping. We'll use this mapping to enable the MII
+ * drivers of the external transceiver according to
+ * the currently selected media.
*/
- for (child = LIST_FIRST(&sc->sc_mii->mii_phys); child != NULL;
- child = LIST_NEXT(child, mii_list)) {
+ sc->sc_phys[0] = sc->sc_phys[1] = -1;
+ LIST_FOREACH(child, &sc->sc_mii->mii_phys, mii_list) {
/*
* Note: we support just two PHYs: the built-in
* internal device and an external on the MII
* connector.
*/
- if (child->mii_phy > 1 || child->mii_inst > 1) {
+ if ((child->mii_phy != HME_PHYAD_EXTERNAL &&
+ child->mii_phy != HME_PHYAD_INTERNAL) ||
+ child->mii_inst > 1) {
device_printf(sc->sc_dev, "cannot accommodate "
"MII device %s at phy %d, instance %d\n",
device_get_name(child->mii_dev),
@@ -476,6 +479,9 @@ hme_stop(struct hme_softc *sc)
callout_stop(&sc->sc_tick_ch);
sc->sc_ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+ /* Mask all interrupts */
+ HME_SEB_WRITE_4(sc, HME_SEBI_IMASK, 0xffffffff);
+
/* Reset transmitter and receiver */
HME_SEB_WRITE_4(sc, HME_SEBI_RESET, HME_SEB_RESET_ETX |
HME_SEB_RESET_ERX);
@@ -741,7 +747,6 @@ hme_init_locked(struct hme_softc *sc)
v = ((ea[4] << 8) | ea[5]) & 0x3fff;
HME_MAC_WRITE_4(sc, HME_MACI_RANDSEED, v);
-
/* Note: Accepting power-on default for other MAC registers here.. */
/* step 5. RX MAC registers & counters */
@@ -833,9 +838,6 @@ hme_init_locked(struct hme_softc *sc)
/* step 11. XIF Configuration */
v = HME_MAC_READ_4(sc, HME_MACI_XIF);
v |= HME_MAC_XIF_OE;
- /* If an external transceiver is connected, enable its MII drivers */
- if ((HME_MIF_READ_4(sc, HME_MIFI_CFG) & HME_MIF_CFG_MDI1) != 0)
- v |= HME_MAC_XIF_MIIENABLE;
CTR1(KTR_HME, "hme_init: programming XIF to %x", (u_int)v);
HME_MAC_WRITE_4(sc, HME_MACI_XIF, v);
@@ -871,9 +873,7 @@ hme_init_locked(struct hme_softc *sc)
#endif
/* Set the current media. */
- /*
- * mii_mediachg(sc->sc_mii);
- */
+ hme_mediachange_locked(sc);
/* Start the one second timer. */
callout_reset(&sc->sc_tick_ch, hz, hme_tick, sc);
@@ -1290,7 +1290,11 @@ hme_eint(struct hme_softc *sc, u_int status)
{
if ((status & HME_SEB_STAT_MIFIRQ) != 0) {
- device_printf(sc->sc_dev, "XXXlink status changed\n");
+ device_printf(sc->sc_dev, "XXXlink status changed: "
+ "cfg=%#x, stat=%#x, sm=%#x\n",
+ HME_MIF_READ_4(sc, HME_MIFI_CFG),
+ HME_MIF_READ_4(sc, HME_MIFI_STAT),
+ HME_MIF_READ_4(sc, HME_MIFI_SM));
return;
}
@@ -1322,7 +1326,6 @@ hme_intr(void *v)
HME_UNLOCK(sc);
}
-
static void
hme_watchdog(struct ifnet *ifp)
{
@@ -1351,10 +1354,27 @@ hme_mifinit(struct hme_softc *sc)
{
u_int32_t v;
- /* Configure the MIF in frame mode */
- v = HME_MIF_READ_4(sc, HME_MIFI_CFG);
- v &= ~HME_MIF_CFG_BBMODE;
- HME_MIF_WRITE_4(sc, HME_MIFI_CFG, v);
+ HME_LOCK_ASSERT(sc, MA_OWNED);
+ /*
+ * Configure the MIF in frame mode, polling disabled, internal PHY
+ * selected.
+ */
+ HME_MIF_WRITE_4(sc, HME_MIFI_CFG, 0);
+
+ /*
+ * If the currently selected media uses the external transceiver,
+ * enable its MII drivers (which basically isolates the internal
+ * one and vice versa). In case the current media hasn't been set,
+ * yet, we default to the internal transceiver.
+ */
+ v = HME_MAC_READ_4(sc, HME_MACI_XIF);
+ if (sc->sc_mii != NULL && sc->sc_mii->mii_media.ifm_cur != NULL &&
+ sc->sc_phys[IFM_INST(sc->sc_mii->mii_media.ifm_cur->ifm_media)] ==
+ HME_PHYAD_EXTERNAL)
+ v |= HME_MAC_XIF_MIIENABLE;
+ else
+ v &= ~HME_MAC_XIF_MIIENABLE;
+ HME_MAC_WRITE_4(sc, HME_MACI_XIF, v);
}
/*
@@ -1363,17 +1383,21 @@ hme_mifinit(struct hme_softc *sc)
int
hme_mii_readreg(device_t dev, int phy, int reg)
{
- struct hme_softc *sc = device_get_softc(dev);
+ struct hme_softc *sc;
int n;
u_int32_t v;
+ /* We can at most have two PHYs. */
+ if (phy != HME_PHYAD_EXTERNAL && phy != HME_PHYAD_INTERNAL)
+ return (0);
+
+ sc = device_get_softc(dev);
/* Select the desired PHY in the MIF configuration register */
v = HME_MIF_READ_4(sc, HME_MIFI_CFG);
- /* Clear PHY select bit */
- v &= ~HME_MIF_CFG_PHY;
if (phy == HME_PHYAD_EXTERNAL)
- /* Set PHY select bit to get at external device */
v |= HME_MIF_CFG_PHY;
+ else
+ v &= ~HME_MIF_CFG_PHY;
HME_MIF_WRITE_4(sc, HME_MIFI_CFG, v);
/* Construct the frame command */
@@ -1387,9 +1411,8 @@ hme_mii_readreg(device_t dev, int phy, int reg)
for (n = 0; n < 100; n++) {
DELAY(1);
v = HME_MIF_READ_4(sc, HME_MIFI_FO);
- if (v & HME_MIF_FO_TALSB) {
+ if (v & HME_MIF_FO_TALSB)
return (v & HME_MIF_FO_DATA);
- }
}
device_printf(sc->sc_dev, "mii_read timeout\n");
@@ -1399,17 +1422,21 @@ hme_mii_readreg(device_t dev, int phy, int reg)
int
hme_mii_writereg(device_t dev, int phy, int reg, int val)
{
- struct hme_softc *sc = device_get_softc(dev);
+ struct hme_softc *sc;
int n;
u_int32_t v;
+ /* We can at most have two PHYs. */
+ if (phy != HME_PHYAD_EXTERNAL && phy != HME_PHYAD_INTERNAL)
+ return (0);
+
+ sc = device_get_softc(dev);
/* Select the desired PHY in the MIF configuration register */
v = HME_MIF_READ_4(sc, HME_MIFI_CFG);
- /* Clear PHY select bit */
- v &= ~HME_MIF_CFG_PHY;
if (phy == HME_PHYAD_EXTERNAL)
- /* Set PHY select bit to get at external device */
v |= HME_MIF_CFG_PHY;
+ else
+ v &= ~HME_MIF_CFG_PHY;
HME_MIF_WRITE_4(sc, HME_MIFI_CFG, v);
/* Construct the frame command */
@@ -1435,25 +1462,16 @@ hme_mii_writereg(device_t dev, int phy, int reg, int val)
void
hme_mii_statchg(device_t dev)
{
- struct hme_softc *sc = device_get_softc(dev);
- int instance;
- int phy;
+ struct hme_softc *sc;
u_int32_t v;
- instance = IFM_INST(sc->sc_mii->mii_media.ifm_cur->ifm_media);
- phy = sc->sc_phys[instance];
+ sc = device_get_softc(dev);
+
#ifdef HMEDEBUG
if (sc->sc_debug)
- printf("hme_mii_statchg: status change: phy = %d\n", phy);
+ device_printf(sc->sc_dev, "hme_mii_statchg: status change\n");
#endif
- /* Select the current PHY in the MIF configuration register */
- v = HME_MIF_READ_4(sc, HME_MIFI_CFG);
- v &= ~HME_MIF_CFG_PHY;
- if (phy == HME_PHYAD_EXTERNAL)
- v |= HME_MIF_CFG_PHY;
- HME_MIF_WRITE_4(sc, HME_MIFI_CFG, v);
-
/* Set the MAC Full Duplex bit appropriately */
v = HME_MAC_READ_4(sc, HME_MACI_TXCFG);
if (!hme_mac_bitflip(sc, HME_MACI_TXCFG, v, HME_MAC_TXCFG_ENABLE, 0))
@@ -1474,11 +1492,39 @@ hme_mediachange(struct ifnet *ifp)
int error;
HME_LOCK(sc);
- error = mii_mediachg(sc->sc_mii);
+ error = hme_mediachange_locked(sc);
HME_UNLOCK(sc);
return (error);
}
+static int
+hme_mediachange_locked(struct hme_softc *sc)
+{
+ struct mii_softc *child;
+
+ HME_LOCK_ASSERT(sc, MA_OWNED);
+#ifdef HMEDEBUG
+ if (sc->sc_debug)
+ device_printf(sc->sc_dev, "hme_mediachange_locked");
+#endif
+
+ hme_mifinit(sc);
+
+ /*
+ * If both PHYs are present reset them. This is required for
+ * unisolating the previously isolated PHY when switching PHYs.
+ * As the above hme_mifinit() call will set the MII drivers in
+ * the XIF configuration register accoring to the currently
+ * selected media, there should be no window during which the
+ * data paths of both transceivers are open at the same time,
+ * even if the PHY device drivers use MIIF_NOISOLATE.
+ */
+ if (sc->sc_phys[0] != -1 && sc->sc_phys[1] != -1)
+ LIST_FOREACH(child, &sc->sc_mii->mii_phys, mii_list)
+ mii_phy_reset(child);
+ return (mii_mediachg(sc->sc_mii));
+}
+
static void
hme_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr)
{
OpenPOWER on IntegriCloud