summaryrefslogtreecommitdiffstats
path: root/sys/pci
diff options
context:
space:
mode:
authorwpaul <wpaul@FreeBSD.org>1998-08-03 01:33:12 +0000
committerwpaul <wpaul@FreeBSD.org>1998-08-03 01:33:12 +0000
commit86f0f2b26f1fccb96ef47d8dfe99bff26e6b8174 (patch)
treee84949d16de92ff077a7223c14a53940065737bf /sys/pci
parent5d69d2598bbfb0adafe1ac98710aab14ab1d8c2e (diff)
downloadFreeBSD-src-86f0f2b26f1fccb96ef47d8dfe99bff26e6b8174.zip
FreeBSD-src-86f0f2b26f1fccb96ef47d8dfe99bff26e6b8174.tar.gz
Updates for the ThunderLAN driver:
- probe for PHYs by checking the BMSR (phy status) register instead of the vendor ID register. - fix the autonegotiation routine so that it figures out the autonegotiated modes correctly. - add tweaks to support the Olicom OC-2326 now that I've actually had a chance to test one o Olicom appears to encode the ethernet address in the EEPROM in 16-bit chunks in network byte order. If we detect an Olicom card (based on the PCI vendor ID), byte-swap the station address accordingly. XXX The Linux driver does not do this. I find this odd since the README from the Linux driver indicates that patches to support the Olicom cards came from somebody at Olicom; you'd think if anyone would get that right, it'd be them. Regardless, I accepted the word of the disgnoatic program that came bundled with the card as gospel and fixed the attach routine to make the station address match what it says. o The version of the 2326 card that I got for testing is a strange beast: the card does not look like the picture on the box in which it was packed. For one thing, the picture shows what looks like an external NS 83840A PHY, but the actual card doesn't have one. The card has a TNETE100APCM chip, which appears to have not only the usual internal tlan 10Mbps PHY at MII address 32, but also a 10/100 PHY at MII address 0. Curiously, this PHY's vendor and device ID registers always return 0x0000. I suspect that this is a mutant version of the ThunderLAN chip with 100Mbps support. This combination behaves a little strangely and required the following changes: - The internal PHY has to be enabled in tl_softreset(). - The internal PHY doesn't seem to come to life after detecting the 100Mbps PHY unless it's reset twice. - If you want to use 100Mbps modes, you have to isolate the internal PHY. - If you want to use 10Mbps modes, you have to un-isolate the internal PHY. The latter two changes are handled at the end of tl_init(): if the PHY vendor ID is 0x0000 (which should not be possible if we have a real external PHY), then tl_init() forces the internal PHY's BMCR register to the proper values.
Diffstat (limited to 'sys/pci')
-rw-r--r--sys/pci/if_tl.c166
-rw-r--r--sys/pci/if_tlreg.h2
2 files changed, 136 insertions, 32 deletions
diff --git a/sys/pci/if_tl.c b/sys/pci/if_tl.c
index 78571c1..7b56025 100644
--- a/sys/pci/if_tl.c
+++ b/sys/pci/if_tl.c
@@ -29,7 +29,7 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*
- * $Id: if_tl.c,v 1.1 1998/07/11 00:34:45 wpaul Exp wpaul $
+ * $Id: if_tl.c,v 1.4 1998/08/03 00:44:48 root Exp $
*/
/*
@@ -217,7 +217,7 @@
#ifndef lint
static char rcsid[] =
- "$Id: if_tl.c,v 1.1 1998/07/11 00:34:45 wpaul Exp wpaul $";
+ "$Id: if_tl.c,v 1.4 1998/08/03 00:44:48 root Exp $";
#endif
/*
@@ -734,14 +734,14 @@ static void tl_autoneg(sc, flag, verbose)
int flag;
int verbose;
{
- u_int16_t phy_sts = 0, media = 0;
+ u_int16_t phy_sts = 0, media = 0, advert, ability;
struct ifnet *ifp;
struct ifmedia *ifm;
volatile struct tl_csr *csr;
+ csr = sc->csr;
ifm = &sc->ifmedia;
ifp = &sc->arpcom.ac_if;
- csr = sc->csr;
/*
* First, see if autoneg is supported. If not, there's
@@ -763,6 +763,8 @@ static void tl_autoneg(sc, flag, verbose)
* for three whole seconds after we've gone multi-user
* is really bad manners.
*/
+ tl_phy_writereg(sc, PHY_BMCR, PHY_BMCR_RESET);
+ DELAY(500);
phy_sts = tl_phy_readreg(sc, PHY_BMCR);
phy_sts |= PHY_BMCR_AUTONEGENBL|PHY_BMCR_AUTONEGRSTR;
tl_phy_writereg(sc, PHY_BMCR, phy_sts);
@@ -779,6 +781,8 @@ static void tl_autoneg(sc, flag, verbose)
sc->tl_want_auto = 1;
return;
}
+ tl_phy_writereg(sc, PHY_BMCR, PHY_BMCR_RESET);
+ DELAY(500);
phy_sts = tl_phy_readreg(sc, PHY_BMCR);
phy_sts |= PHY_BMCR_AUTONEGENBL|PHY_BMCR_AUTONEGRSTR;
tl_phy_writereg(sc, PHY_BMCR, phy_sts);
@@ -814,38 +818,60 @@ static void tl_autoneg(sc, flag, verbose)
if (phy_sts & PHY_BMSR_LINKSTAT) {
if (verbose)
printf("link status good ");
+
+ advert = tl_phy_readreg(sc, TL_PHY_ANAR);
+ ability = tl_phy_readreg(sc, TL_PHY_LPAR);
media = tl_phy_readreg(sc, PHY_BMCR);
+ /*
+ * Be sure to turn off the ISOLATE and
+ * LOOPBACK bits in the control register,
+ * otherwise we may not be able to communicate.
+ */
+ media &= ~(PHY_BMCR_LOOPBK|PHY_BMCR_ISOLATE);
/* Set the DUPLEX bit in the NetCmd register accordingly. */
- if (media & PHY_BMCR_DUPLEX) {
+ if (advert & PHY_ANAR_100BT4 && ability & PHY_ANAR_100BT4) {
+ ifm->ifm_media = IFM_ETHER|IFM_100_T4;
+ DIO_SEL(TL_NETCMD);
+ DIO_BYTE0_CLR(TL_CMD_DUPLEX);
if (verbose)
- printf("(full-duplex, ");
- ifm->ifm_media |= IFM_FDX;
- ifm->ifm_media &= ~IFM_HDX;
+ printf("(100baseT4)\n");
+ } else if (advert & PHY_ANAR_100BTXFULL &&
+ ability & PHY_ANAR_100BTXFULL) {
+ ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_FDX;
+ media |= PHY_BMCR_SPEEDSEL;
+ media |= PHY_BMCR_DUPLEX;
DIO_SEL(TL_NETCMD);
DIO_BYTE0_SET(TL_CMD_DUPLEX);
- } else {
if (verbose)
- printf("(half-duplex, ");
- ifm->ifm_media &= ~IFM_FDX;
- ifm->ifm_media |= IFM_HDX;
+ printf("(full-duplex, 100Mbps)\n");
+ } else if (advert & PHY_ANAR_100BTXHALF &&
+ ability & PHY_ANAR_100BTXHALF) {
+ ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_HDX;
+ media |= PHY_BMCR_SPEEDSEL;
+ media &= ~PHY_BMCR_DUPLEX;
DIO_SEL(TL_NETCMD);
DIO_BYTE0_CLR(TL_CMD_DUPLEX);
- }
-
- if (media & PHY_BMCR_SPEEDSEL) {
if (verbose)
- printf("100Mb/s)\n");
- ifm->ifm_media |= IFM_100_TX;
- ifm->ifm_media &= ~IFM_10_T;
+ printf("(half-duplex, 100Mbps)\n");
+ } else if (advert & PHY_ANAR_10BTFULL &&
+ ability & PHY_ANAR_10BTFULL) {
+ ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_FDX;
+ media &= ~PHY_BMCR_SPEEDSEL;
+ media |= PHY_BMCR_DUPLEX;
+ DIO_SEL(TL_NETCMD);
+ DIO_BYTE0_SET(TL_CMD_DUPLEX);
+ if (verbose)
+ printf("(full-duplex, 10Mbps)\n");
} else {
+ ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX;
+ media &= ~PHY_BMCR_SPEEDSEL;
+ media &= ~PHY_BMCR_DUPLEX;
+ DIO_SEL(TL_NETCMD);
+ DIO_BYTE0_CLR(TL_CMD_DUPLEX);
if (verbose)
- printf("10Mb/s)\n");
- ifm->ifm_media &= ~IFM_100_TX;
- ifm->ifm_media |= IFM_10_T;
+ printf("(half-duplex, 10Mbps)\n");
}
-
- /* Turn off autoneg */
media &= ~PHY_BMCR_AUTONEGENBL;
tl_phy_writereg(sc, PHY_BMCR, media);
} else {
@@ -853,6 +879,8 @@ static void tl_autoneg(sc, flag, verbose)
printf("no carrier\n");
}
+ tl_init(sc);
+
if (sc->tl_tx_pend) {
sc->tl_autoneg = 0;
sc->tl_tx_pend = 0;
@@ -874,13 +902,14 @@ static void tl_setmode(sc, media)
volatile struct tl_csr *csr;
csr = sc->csr;
+
bmcr = tl_phy_readreg(sc, PHY_BMCR);
anar = tl_phy_readreg(sc, PHY_ANAR);
ctl = tl_phy_readreg(sc, TL_PHY_CTL);
DIO_SEL(TL_NETCMD);
bmcr &= ~(PHY_BMCR_SPEEDSEL|PHY_BMCR_DUPLEX|PHY_BMCR_AUTONEGENBL|
- PHY_BMCR_LOOPBK);
+ PHY_BMCR_LOOPBK|PHY_BMCR_ISOLATE);
anar &= ~(PHY_ANAR_100BT4|PHY_ANAR_100BTXFULL|PHY_ANAR_100BTXHALF|
PHY_ANAR_10BTFULL|PHY_ANAR_10BTHALF);
@@ -892,22 +921,27 @@ static void tl_setmode(sc, media)
if (IFM_SUBTYPE(media) == IFM_AUTO)
bmcr |= PHY_BMCR_AUTONEGENBL;
- if (IFM_SUBTYPE(media) == IFM_10_5)
+ if (IFM_SUBTYPE(media) == IFM_10_5) {
ctl |= PHY_CTL_AUISEL;
+ tl_phy_writereg(sc, TL_PHY_CTL, ctl);
+ }
if (IFM_SUBTYPE(media) == IFM_100_TX) {
bmcr |= PHY_BMCR_SPEEDSEL;
if ((media & IFM_GMASK) == IFM_FDX) {
bmcr |= PHY_BMCR_DUPLEX;
anar |= PHY_ANAR_100BTXFULL;
+ DIO_SEL(TL_NETCMD);
DIO_BYTE0_SET(TL_CMD_DUPLEX);
} else if ((media & IFM_GMASK) == IFM_HDX) {
bmcr &= ~PHY_BMCR_DUPLEX;
anar |= PHY_ANAR_100BTXHALF;
+ DIO_SEL(TL_NETCMD);
DIO_BYTE0_CLR(TL_CMD_DUPLEX);
} else {
bmcr &= ~PHY_BMCR_DUPLEX;
anar |= PHY_ANAR_100BTXHALF;
+ DIO_SEL(TL_NETCMD);
DIO_BYTE0_CLR(TL_CMD_DUPLEX);
}
}
@@ -917,14 +951,17 @@ static void tl_setmode(sc, media)
if ((media & IFM_GMASK) == IFM_FDX) {
bmcr |= PHY_BMCR_DUPLEX;
anar |= PHY_ANAR_10BTFULL;
+ DIO_SEL(TL_NETCMD);
DIO_BYTE0_SET(TL_CMD_DUPLEX);
} else if ((media & IFM_GMASK) == IFM_HDX) {
bmcr &= ~PHY_BMCR_DUPLEX;
anar |= PHY_ANAR_10BTHALF;
+ DIO_SEL(TL_NETCMD);
DIO_BYTE0_CLR(TL_CMD_DUPLEX);
} else {
bmcr &= ~PHY_BMCR_DUPLEX;
anar |= PHY_ANAR_10BTHALF;
+ DIO_SEL(TL_NETCMD);
DIO_BYTE0_CLR(TL_CMD_DUPLEX);
}
}
@@ -933,7 +970,8 @@ static void tl_setmode(sc, media)
#ifdef notyet
tl_phy_writereg(sc, PHY_ANAR, anar);
#endif
- tl_phy_writereg(sc, TL_PHY_CTL, ctl);
+
+ tl_init(sc);
return;
}
@@ -1160,7 +1198,7 @@ static int tl_attach_phy(csr, tl_unit, eaddr, tl_phy, ilist)
caddr_t roundptr;
if (tl_phy != TL_PHYADDR_MAX)
- tl_softreset(csr, 0);
+ tl_softreset(csr, 1);
/* Reset the PHY again, just in case. */
bzero((char *)&frame, sizeof(frame));
@@ -1365,8 +1403,10 @@ static int tl_attach_phy(csr, tl_unit, eaddr, tl_phy, ilist)
* ThunderLAN chip defaults to half-duplex and stays there unless
* told otherwise.
*/
- if (sc->tl_phy_sts & PHY_BMSR_CANAUTONEG)
- tl_autoneg(sc, TL_FLAG_FORCEDELAY, 0);
+ if (sc->tl_phy_sts & PHY_BMSR_CANAUTONEG) {
+ tl_init(sc);
+ tl_autoneg(sc, TL_FLAG_SCHEDDELAY, 1);
+ }
/*
* Call MI attach routines.
@@ -1448,6 +1488,28 @@ tl_attach_ctlr(config_id, unit)
}
/*
+ * XXX Olicom, in its desire to be different from the
+ * rest of the world, has done strange things with the
+ * encoding of the station address in the EEPROM. First
+ * of all, they store the address at offset 0xF8 rather
+ * than at 0x83 like the ThunderLAN manual suggests.
+ * Second, they store the address in 16-bit chunks in
+ * network byte order, as opposed to storing it sequentially
+ * like all the other ThunderLAN cards. In order to get
+ * the station address in a form that matches what the Olicom
+ * diagnostic utility specifies, we have to byte-swap each
+ * word. To make things even more confusing, neither 00:00:28
+ * nor 00:00:24 appear in the IEEE OUI database.
+ */
+ if (ilist->tl_dinfo->tl_vid == OLICOM_VENDORID) {
+ for (i = 0; i < ETHER_ADDR_LEN; i += 2) {
+ u_int16_t *p;
+ p = (u_int16_t *)&eaddr[i];
+ *p = ntohs(*p);
+ }
+ }
+
+ /*
* A ThunderLAN chip was detected. Inform the world.
*/
printf("tlc%d: Ethernet address: %6D\n", unit, eaddr, ":");
@@ -1470,7 +1532,7 @@ tl_attach_ctlr(config_id, unit)
DELAY(500);
while(frame.mii_data & PHY_BMCR_RESET)
tl_mii_readreg(csr, &frame);
- frame.mii_regaddr = TL_PHY_VENID;
+ frame.mii_regaddr = PHY_BMSR;
frame.mii_data = 0;
tl_mii_readreg(csr, &frame);
if (!frame.mii_data)
@@ -1490,8 +1552,20 @@ tl_attach_ctlr(config_id, unit)
goto fail;
}
- at_shutdown(tl_shutdown, ilist, SHUTDOWN_POST_SYNC);
+ /* Reset internal PHY. */
+ frame.mii_phyaddr = TL_PHYADDR_MAX;
+ frame.mii_regaddr = TL_PHY_GENCTL;
+ frame.mii_data = PHY_BMCR_RESET;
+ tl_mii_writereg(csr, &frame);
+ DELAY(500);
+ /* Do it again. */
+ tl_mii_writereg(csr, &frame);
+ DELAY(500);
+ /* Now isolate it. */
+ frame.mii_data = PHY_BMCR_ISOLATE;
+ tl_mii_writereg(csr, &frame);
+ at_shutdown(tl_shutdown, ilist, SHUTDOWN_POST_SYNC);
fail:
splx(s);
return;
@@ -2318,6 +2392,36 @@ static void tl_init(xsc)
CMD_SET(sc->csr, TL_CMD_RT);
sc->csr->tl_ch_parm = vtophys(&sc->tl_ldata->tl_rx_list[0]);
+ /*
+ * XXX The following is a kludge. I've recently encountered
+ * a version of the Olicom OC-2326 with what looks like a
+ * mutant version of the ThunderLAN chip with a high-speed
+ * PHY at MII address 0, but whose vendor and device ID
+ * registers return 0. The tricky thing about these cards is
+ * that while you can autonegotiate 10/100 half/full duplex
+ * modes using just the PHY at address 0, you need to isolate
+ * the internal PHY at address 31 in order to use 100Mbps modes
+ * and un-isolate it if you want to use 10Mbps modes.
+ * I'm not sure if this nonsense is Olicom's doing or if this
+ * really is a special version of the ThunderLAN with built-in
+ * 100Mbps support, but I don't have a simple way to 'spot the
+ * looney' other than checking the PHY vendor ID.
+ */
+ if (!sc->tl_phy_vid) {
+ u_int8_t addr = 0;
+ u_int16_t bmcr;
+
+ bmcr = tl_phy_readreg(sc, PHY_BMCR);
+ addr = sc->tl_phy_addr;
+ sc->tl_phy_addr = TL_PHYADDR_MAX;
+ tl_phy_writereg(sc, PHY_BMCR, PHY_BMCR_RESET);
+ if (bmcr & PHY_BMCR_SPEEDSEL)
+ tl_phy_writereg(sc, PHY_BMCR, PHY_BMCR_ISOLATE);
+ else
+ tl_phy_writereg(sc, PHY_BMCR, bmcr);
+ sc->tl_phy_addr = addr;
+ }
+
/* Send the RX go command */
CMD_SET(sc->csr, (TL_CMD_GO|TL_CMD_RT));
sc->tl_iflist->tl_active_phy = sc->tl_phy_addr;
diff --git a/sys/pci/if_tlreg.h b/sys/pci/if_tlreg.h
index 319498a..8e19016 100644
--- a/sys/pci/if_tlreg.h
+++ b/sys/pci/if_tlreg.h
@@ -29,7 +29,7 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*
- * $Id: if_tlreg.h,v 1.20 1998/07/12 14:59:25 wpaul Exp wpaul $
+ * $Id: if_tlreg.h,v 1.3 1998/08/02 23:54:37 root Exp $
*/
OpenPOWER on IntegriCloud