I have noticed, for a while, that my O2 systems were horribly slow
during installs or upgrades, when fetching sets from the network (28
*minutes* to fetch base64.tgz).

At first, I thought this was a bsd.rd specific bug, but couldn't find
anything obvious. After gathering enough data, I found out that the
problem only occurs on a cold boot. After a reboot, the network
performance is as good as it can be. That would explain why I would only
notice it during upgrades.

I also noticed that, on a warm boot, the dmesg would show:

mec0 at macebus0 base 0x00280000 irq 3: MAC-110 rev 1, address 08:00:69:0e:bf:a1
nsphy0 at mec0 phy 8: DP83840 10/100 PHY, rev. 1

but on cold boots, it would show:

mec0 at macebus0 base 0x00280000 irq 3: MAC-110 rev 1, address 08:00:69:0e:bf:a1
nsphy0 at mec0 phy 10: DP83840 10/100 PHY, rev. 1

Note that, in these cases, the phy seems to attach to a different
address. In these cases, after booting, "ifconfig mec0" would show:

mec0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
        lladdr 08:00:69:0e:bf:a1
        llprio 3
        media: Ethernet autoselect
        status: active
        inet 10.0.1.193 netmask 0xff000000 broadcast 10.255.255.255

while one would expect the "media" line to be similar to:

        media: Ethernet autoselect (100baseTX full-duplex)

Investigating further, it seems that, after a cold boot, the MII bus
takes some time to initialize; the phy does not answer to address 8 but
to a larger address (10 or 11), then, after being reset, to its correct
address of 8.

So the kernel would discover the phy at a wrong address, attach it, and
after it gets reset, reading from the phy at the wrong address would
return either all bits clear or all bits set, confusing the link speed
logic without any way to recover.

What I tried but did not work:
- invoking mii_attach() twice in the mec driver. This would attach nsphy
  twice, once at the wrong address, then once at the correct address,
  but the first (wrong) attachment would be preferred.
- adding a one second delay between the Ethernet interface reset and
  mii_attach(). This would work most of the time, but not always.

What I tried and works:
- the first time the interface is reset, the mii bus is walked and all
  phys found on it are reset. Thus, by the time mii_attach() runs and
  walks the bus again, the phy will answer at the right address.

The diff below implements this (last chunk of if_mec.c), and also cleans
the mii read/write routines a bit (all the other chunks).

Tested on three different R5K family O2 systems, which have all been
exposing that problem on cold boot.

Index: dev/if_mec.c
===================================================================
RCS file: /OpenBSD/src/sys/arch/sgi/dev/if_mec.c,v
retrieving revision 1.37
diff -u -p -r1.37 if_mec.c
--- dev/if_mec.c        22 Jan 2017 10:17:37 -0000      1.37
+++ dev/if_mec.c        8 Dec 2018 15:00:04 -0000
@@ -264,7 +264,6 @@ struct mec_softc {
        bus_dma_tag_t sc_dmat;          /* bus_dma tag. */
 
        struct mii_data sc_mii;         /* MII/media information. */
-       int sc_phyaddr;                 /* MII address. */
        struct timeout sc_tick_ch;      /* Tick timeout. */
 
        bus_dmamap_t sc_cddmamap;       /* bus_dma map for control data. */
@@ -329,7 +328,7 @@ void        mec_start(struct ifnet *);
 void   mec_watchdog(struct ifnet *);
 void   mec_tick(void *);
 int    mec_ioctl(struct ifnet *, u_long, caddr_t);
-void   mec_reset(struct mec_softc *);
+void   mec_reset(struct mec_softc *, int);
 void   mec_iff(struct mec_softc *);
 int    mec_intr(void *arg);
 void   mec_stop(struct ifnet *);
@@ -422,7 +421,7 @@ mec_attach(struct device *parent, struct
        enaddr_aton(bios_enaddr, sc->sc_ac.ac_enaddr);
 
        /* Reset device. */
-       mec_reset(sc);
+       mec_reset(sc, 1);
 
        command = bus_space_read_8(sc->sc_st, sc->sc_sh, MEC_MAC_CONTROL);
 
@@ -451,7 +450,6 @@ mec_attach(struct device *parent, struct
                ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER | IFM_MANUAL);
        } else {
                ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER | IFM_AUTO);
-               sc->sc_phyaddr = child->mii_phy;
        }
 
        bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ);
@@ -499,21 +497,19 @@ mec_mii_readreg(struct device *self, int
        struct mec_softc *sc = (void *)self;
        bus_space_tag_t st = sc->sc_st;
        bus_space_handle_t sh = sc->sc_sh;
-       uint32_t val;
+       uint64_t val;
        int i;
 
        if (mec_mii_wait(sc) != 0)
                return 0;
 
-       bus_space_write_4(st, sh, MEC_PHY_ADDRESS,
+       bus_space_write_8(st, sh, MEC_PHY_ADDRESS,
            (phy << MEC_PHY_ADDR_DEVSHIFT) | (reg & MEC_PHY_ADDR_REGISTER));
        bus_space_write_8(st, sh, MEC_PHY_READ_INITIATE, 1);
-       delay(25);
 
        for (i = 0; i < 20; i++) {
-               delay(30);
-
-               val = bus_space_read_4(st, sh, MEC_PHY_DATA);
+               delay(25);
+               val = bus_space_read_8(st, sh, MEC_PHY_DATA);
 
                if ((val & MEC_PHY_DATA_BUSY) == 0)
                        return val & MEC_PHY_DATA_VALUE;
@@ -529,18 +525,13 @@ mec_mii_writereg(struct device *self, in
        bus_space_handle_t sh = sc->sc_sh;
 
        if (mec_mii_wait(sc) != 0) {
-               printf("timed out writing %x: %x\n", reg, val);
+               printf("MII timed out writing %x: %x\n", reg, val);
                return;
        }
 
-       bus_space_write_4(st, sh, MEC_PHY_ADDRESS,
+       bus_space_write_8(st, sh, MEC_PHY_ADDRESS,
            (phy << MEC_PHY_ADDR_DEVSHIFT) | (reg & MEC_PHY_ADDR_REGISTER));
-
-       delay(60);
-
-       bus_space_write_4(st, sh, MEC_PHY_DATA, val & MEC_PHY_DATA_VALUE);
-
-       delay(60);
+       bus_space_write_8(st, sh, MEC_PHY_DATA, val & MEC_PHY_DATA_VALUE);
 
        mec_mii_wait(sc);
 }
@@ -548,20 +539,14 @@ mec_mii_writereg(struct device *self, in
 int
 mec_mii_wait(struct mec_softc *sc)
 {
-       uint32_t busy;
-       int i, s;
+       uint64_t busy;
+       int i;
 
        for (i = 0; i < 100; i++) {
-               delay(30);
-
-               s = splhigh();
-               busy = bus_space_read_4(sc->sc_st, sc->sc_sh, MEC_PHY_DATA);
-               splx(s);
-
+               busy = bus_space_read_8(sc->sc_st, sc->sc_sh, MEC_PHY_DATA);
                if ((busy & MEC_PHY_DATA_BUSY) == 0)
                        return 0;
-               if (busy == 0xffff) /* XXX ? */
-                       return 0;
+               delay(30);
        }
 
        printf("%s: MII timed out\n", sc->sc_dev.dv_xname);
@@ -628,7 +613,7 @@ mec_init(struct ifnet *ifp)
        mec_stop(ifp);
 
        /* Reset device. */
-       mec_reset(sc);
+       mec_reset(sc, 0);
 
        /* Setup filter for multicast or promisc mode. */
        mec_iff(sc);
@@ -676,7 +661,7 @@ mec_init(struct ifnet *ifp)
 }
 
 void
-mec_reset(struct mec_softc *sc)
+mec_reset(struct mec_softc *sc, int firsttime)
 {
        bus_space_tag_t st = sc->sc_st;
        bus_space_handle_t sh = sc->sc_sh;
@@ -705,6 +690,41 @@ mec_reset(struct mec_softc *sc)
 
        DPRINTF(MEC_DEBUG_RESET, ("mec: control now %llx\n",
            bus_space_read_8(st, sh, MEC_MAC_CONTROL)));
+
+       if (firsttime) {
+               /*
+                * After a cold boot, during the initial MII probe, the
+                * PHY would sometimes answer to addresses 11 or 10, only
+                * to settle to address 8 shortly after.
+                *
+                * Because of this, the PHY driver would attach to the wrong
+                * address and further link configuration would fail (with PHY
+                * register reads returning either 0 or FFFF), leading to
+                * horrible performance and no way to select a proper media.
+                */
+               int i, reg, phyno;
+               for (phyno = 0; phyno < MII_NPHY; phyno++) {
+                       reg = mec_mii_readreg(&sc->sc_dev, phyno, MII_BMSR);
+                       /* same logic as in mii_attach() */
+                       if (reg == 0 || reg == 0xffff ||
+                           (reg & (BMSR_MEDIAMASK | BMSR_EXTSTAT)) == 0)
+                               continue;
+                       /* inline mii_phy_reset() */
+                       mec_mii_writereg(&sc->sc_dev, phyno, MII_BMCR,
+                           BMCR_RESET | BMCR_ISO);
+                       delay(500);
+                       for (i = 0; i < 100; i++) {
+                               reg = mec_mii_readreg(&sc->sc_dev, phyno,
+                                   MII_BMCR);
+                               if ((reg & BMCR_RESET) == 0) {
+                                       mec_mii_writereg(&sc->sc_dev, phyno,
+                                           MII_BMCR, reg | BMCR_ISO);
+                                       break;
+                               }
+                               delay(1000);
+                       }
+               }
+       }
 }
 
 void
Index: dev/if_mecreg.h
===================================================================
RCS file: /OpenBSD/src/sys/arch/sgi/dev/if_mecreg.h,v
retrieving revision 1.2
diff -u -p -r1.2 if_mecreg.h
--- dev/if_mecreg.h     31 Jul 2007 19:10:22 -0000      1.2
+++ dev/if_mecreg.h     8 Dec 2018 15:00:04 -0000
@@ -117,13 +117,11 @@
 #define MEC_TX_VECTOR                  0x58
 #define MEC_IRQ_VECTOR                 0x58
 
-#define MEC_PHY_DATA_PAD               0x60 /* XXX ? */
-#define MEC_PHY_DATA                   0x64
+#define MEC_PHY_DATA                   0x60
 #define  MEC_PHY_DATA_BUSY             0x00010000
 #define  MEC_PHY_DATA_VALUE            0x0000ffff
 
-#define MEC_PHY_ADDRESS_PAD            0x68 /* XXX ? */
-#define MEC_PHY_ADDRESS                        0x6c
+#define MEC_PHY_ADDRESS                        0x68
 #define  MEC_PHY_ADDR_REGISTER         0x0000001f
 #define  MEC_PHY_ADDR_DEVICE           0x000003e0
 #define  MEC_PHY_ADDR_DEVSHIFT         5

Reply via email to