diff --git a/sys/pci/if_pn.c b/sys/pci/if_pn.c index ee81b37b7d97..24a3c5bb99da 100644 --- a/sys/pci/if_pn.c +++ b/sys/pci/if_pn.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_pn.c,v 1.42 1999/03/30 19:29:25 wpaul Exp $ + * $Id: if_pn.c,v 1.45 1999/04/10 18:22:22 wpaul Exp $ */ /* @@ -97,7 +97,7 @@ #ifndef lint static const char rcsid[] = - "$Id: if_pn.c,v 1.42 1999/03/30 19:29:25 wpaul Exp $"; + "$Id: if_pn.c,v 1.45 1999/04/10 18:22:22 wpaul Exp $"; #endif /* @@ -105,7 +105,9 @@ static const char rcsid[] = */ static struct pn_type pn_devs[] = { { PN_VENDORID, PN_DEVICEID_PNIC, - "82c168/82c169 PNIC 10/100BaseTX" }, + "82c168 PNIC 10/100BaseTX" }, + { PN_VENDORID, PN_DEVICEID_PNIC, + "82c169 PNIC 10/100BaseTX" }, { PN_VENDORID, PN_DEVICEID_PNIC_II, "82c115 PNIC II 10/100BaseTX" }, { 0, 0, NULL } @@ -164,7 +166,9 @@ static void pn_autoneg_xmit __P((struct pn_softc *)); static void pn_autoneg_mii __P((struct pn_softc *, int, int)); static void pn_setmode_mii __P((struct pn_softc *, int)); static void pn_getmode_mii __P((struct pn_softc *)); -static void pn_setcfg __P((struct pn_softc *, u_int16_t)); +static void pn_autoneg __P((struct pn_softc *, int, int)); +static void pn_setmode __P((struct pn_softc *, int)); +static void pn_setcfg __P((struct pn_softc *, u_int32_t)); static u_int32_t pn_calchash __P((u_int8_t *)); static void pn_setfilt __P((struct pn_softc *)); static void pn_reset __P((struct pn_softc *)); @@ -441,7 +445,7 @@ static void pn_autoneg_mii(sc, flag, verbose) media &= ~PHY_BMCR_AUTONEGENBL; /* Set ASIC's duplex mode to match the PHY. */ - pn_setcfg(sc, media); + pn_setcfg(sc, ifm->ifm_media); pn_phy_writereg(sc, PHY_BMCR, media); } else { if (verbose) @@ -539,9 +543,166 @@ static void pn_getmode_mii(sc) return; } -/* - * Set speed and duplex mode. - */ +static void pn_autoneg(sc, flag, verbose) + struct pn_softc *sc; + int flag; + int verbose; +{ + u_int32_t nway = 0, ability; + struct ifnet *ifp; + struct ifmedia *ifm; + + ifm = &sc->ifmedia; + ifp = &sc->arpcom.ac_if; + + ifm->ifm_media = IFM_ETHER | IFM_AUTO; + + switch (flag) { + case PN_FLAG_FORCEDELAY: + /* + * XXX Never use this option anywhere but in the probe + * routine: making the kernel stop dead in its tracks + * for three whole seconds after we've gone multi-user + * is really bad manners. + */ + CSR_WRITE_4(sc, PN_GEN, + PN_GEN_MUSTBEONE|PN_GEN_100TX_LOOP); + PN_CLRBIT(sc, PN_NWAY, PN_NWAY_AUTONEGRSTR); + PN_SETBIT(sc, PN_NWAY, PN_NWAY_AUTOENB); + DELAY(5000000); + break; + case PN_FLAG_SCHEDDELAY: + /* + * Wait for the transmitter to go idle before starting + * an autoneg session, otherwise pn_start() may clobber + * our timeout, and we don't want to allow transmission + * during an autoneg session since that can screw it up. + */ + if (sc->pn_cdata.pn_tx_head != NULL) { + sc->pn_want_auto = 1; + return; + } + CSR_WRITE_4(sc, PN_GEN, + PN_GEN_MUSTBEONE|PN_GEN_100TX_LOOP); + PN_CLRBIT(sc, PN_NWAY, PN_NWAY_AUTONEGRSTR); + PN_SETBIT(sc, PN_NWAY, PN_NWAY_AUTOENB); + ifp->if_timer = 5; + sc->pn_autoneg = 1; + sc->pn_want_auto = 0; + return; + break; + case PN_FLAG_DELAYTIMEO: + ifp->if_timer = 0; + sc->pn_autoneg = 0; + break; + default: + printf("pn%d: invalid autoneg flag: %d\n", sc->pn_unit, flag); + return; + } + + if (CSR_READ_4(sc, PN_NWAY) & PN_NWAY_LPAR) { + if (verbose) + printf("pn%d: autoneg complete, ", sc->pn_unit); + } else { + if (verbose) + printf("pn%d: autoneg not complete, ", sc->pn_unit); + } + + /* Link is good. Report modes and set duplex mode. */ + if (CSR_READ_4(sc, PN_ISR) & PN_ISR_LINKPASS) { + if (verbose) + printf("link status good "); + + ability = CSR_READ_4(sc, PN_NWAY); + if (ability & PN_NWAY_LPAR100T4) { + ifm->ifm_media = IFM_ETHER|IFM_100_T4; + nway = PN_NWAY_MODE_100T4; + printf("(100baseT4)\n"); + } else if (ability & PN_NWAY_LPAR100FULL) { + ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_FDX; + nway = PN_NWAY_MODE_100FD; + printf("(full-duplex, 100Mbps)\n"); + } else if (ability & PN_NWAY_LPAR100HALF) { + ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_HDX; + nway = PN_NWAY_MODE_100HD; + printf("(half-duplex, 100Mbps)\n"); + } else if (ability & PN_NWAY_LPAR10FULL) { + ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_FDX; + nway = PN_NWAY_MODE_10FD; + printf("(full-duplex, 10Mbps)\n"); + } else if (ability & PN_NWAY_LPAR10HALF) { + ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX; + nway = PN_NWAY_MODE_10HD; + printf("(half-duplex, 10Mbps)\n"); + } + + /* Set ASIC's duplex mode to match the PHY. */ + pn_setcfg(sc, ifm->ifm_media); + CSR_WRITE_4(sc, PN_NWAY, nway); + } else { + if (verbose) + printf("no carrier\n"); + } + + pn_init(sc); + + if (sc->pn_tx_pend) { + sc->pn_autoneg = 0; + sc->pn_tx_pend = 0; + pn_start(ifp); + } + + return; +} + +static void pn_setmode(sc, media) + struct pn_softc *sc; + int media; +{ + u_int32_t nway = 0; + struct ifnet *ifp; + + ifp = &sc->arpcom.ac_if; + + /* + * If an autoneg session is in progress, stop it. + */ + if (sc->pn_autoneg) { + printf("pn%d: canceling autoneg session\n", sc->pn_unit); + ifp->if_timer = sc->pn_autoneg = sc->pn_want_auto = 0; + PN_CLRBIT(sc, PN_NWAY, PN_NWAY_AUTONEGRSTR); + } + + printf("pn%d: selecting NWAY, ", sc->pn_unit); + + if (IFM_SUBTYPE(media) == IFM_100_T4) { + printf("100Mbps/T4, half-duplex\n"); + nway = PN_NWAY_MODE_100T4; + } + + if (IFM_SUBTYPE(media) == IFM_100_TX) { + printf("100Mbps, "); + nway = PN_NWAY_MODE_100HD; + } + + if (IFM_SUBTYPE(media) == IFM_10_T) { + printf("10Mbps, "); + nway = PN_NWAY_MODE_10HD; + } + + if ((media & IFM_GMASK) == IFM_FDX) { + printf("full duplex\n"); + nway |= PN_NWAY_DUPLEX; + } else { + printf("half duplex\n"); + } + + pn_setcfg(sc, media); + CSR_WRITE_4(sc, PN_NWAY, nway); + + return; +} + static void pn_setmode_mii(sc, media) struct pn_softc *sc; int media; @@ -593,7 +754,7 @@ static void pn_setmode_mii(sc, media) bmcr &= ~PHY_BMCR_DUPLEX; } - pn_setcfg(sc, bmcr); + pn_setcfg(sc, media); pn_phy_writereg(sc, PHY_BMCR, bmcr); return; @@ -688,9 +849,9 @@ void pn_setfilt(sc) * 'full-duplex' and '100Mbps' bits in the netconfig register, we * first have to put the transmit and/or receive logic in the idle state. */ -static void pn_setcfg(sc, bmcr) +static void pn_setcfg(sc, media) struct pn_softc *sc; - u_int16_t bmcr; + u_int32_t media; { int i, restart = 0; @@ -711,12 +872,17 @@ static void pn_setcfg(sc, bmcr) } - if (bmcr & PHY_BMCR_SPEEDSEL) + if (IFM_SUBTYPE(media) == IFM_100_TX) { PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_SPEEDSEL); - else + CSR_WRITE_4(sc, PN_GEN, PN_GEN_MUSTBEONE| + PN_GEN_SPEEDSEL|PN_GEN_100TX_LOOP); + } else { PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_SPEEDSEL); + CSR_WRITE_4(sc, PN_GEN, + PN_GEN_MUSTBEONE|PN_GEN_100TX_LOOP); + } - if (bmcr & PHY_BMCR_DUPLEX) + if ((media & IFM_GMASK) == IFM_FDX) PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_FULLDUPLEX); else PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_FULLDUPLEX); @@ -757,12 +923,28 @@ pn_probe(config_id, device_id) pcidi_t device_id; { struct pn_type *t; + u_int32_t rev; t = pn_devs; while(t->pn_name != NULL) { if ((device_id & 0xFFFF) == t->pn_vid && ((device_id >> 16) & 0xFFFF) == t->pn_did) { + if (t->pn_did == PN_DEVICEID_PNIC) { + rev = pci_conf_read(config_id, + PN_PCI_REVISION) & 0xFF; + switch(rev & PN_REVMASK) { + case PN_REVID_82C168: + return(t->pn_name); + break; + case PN_REVID_82C169: + t++; + return(t->pn_name); + default: + printf("unknown PNIC rev: %x\n", rev); + break; + } + } return(t->pn_name); } t++; @@ -886,6 +1068,9 @@ pn_attach(config_id, unit) goto fail; } + /* Save the cache line size. */ + sc->pn_cachesize = pci_conf_read(config_id, PN_PCI_CACHELEN) & 0xFF; + /* Reset the adapter. */ pn_reset(sc); @@ -951,6 +1136,8 @@ pn_attach(config_id, unit) ifp->if_baudrate = 10000000; ifp->if_snd.ifq_maxlen = PN_TX_LIST_CNT - 1; + ifmedia_init(&sc->ifmedia, 0, pn_ifmedia_upd, pn_ifmedia_sts); + if (bootverbose) printf("pn%d: probing for a PHY\n", sc->pn_unit); for (i = PN_PHYADDR_MIN; i < PN_PHYADDR_MAX + 1; i++) { @@ -988,21 +1175,25 @@ pn_attach(config_id, unit) if (bootverbose) printf("pn%d: PHY type: %s\n", sc->pn_unit, sc->pn_pinfo->pn_name); + + pn_getmode_mii(sc); + pn_autoneg_mii(sc, PN_FLAG_FORCEDELAY, 1); } else { - printf("pn%d: MII without any phy!\n", sc->pn_unit); - goto fail; + ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); + ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); + ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); + ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX, 0, NULL); + ifmedia_add(&sc->ifmedia, + IFM_ETHER|IFM_100_TX|IFM_HDX, 0, NULL); + ifmedia_add(&sc->ifmedia, + IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL); + ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_T4, 0, NULL); + ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL); + pn_autoneg(sc, PN_FLAG_FORCEDELAY, 1); } - /* - * Do ifmedia setup. - */ - ifmedia_init(&sc->ifmedia, 0, pn_ifmedia_upd, pn_ifmedia_sts); - - pn_getmode_mii(sc); - pn_autoneg_mii(sc, PN_FLAG_FORCEDELAY, 1); media = sc->ifmedia.ifm_media; pn_stop(sc); - ifmedia_set(&sc->ifmedia, media); /* @@ -1477,8 +1668,13 @@ static void pn_txeoc(sc) if (sc->pn_cdata.pn_tx_head == NULL) { ifp->if_flags &= ~IFF_OACTIVE; sc->pn_cdata.pn_tx_tail = NULL; - if (sc->pn_want_auto) - pn_autoneg_mii(sc, PN_FLAG_SCHEDDELAY, 1); + if (sc->pn_want_auto) { + if (sc->pn_pinfo == NULL) + pn_autoneg(sc, PN_FLAG_SCHEDDELAY, 1); + else + pn_autoneg_mii(sc, PN_FLAG_SCHEDDELAY, 1); + } + } return; @@ -1746,7 +1942,22 @@ static void pn_init(xsc) /* * Set cache alignment and burst length. */ - CSR_WRITE_4(sc, PN_BUSCTL, PN_BUSCTL_CONFIG); + switch(sc->pn_cachesize) { + case 32: + PN_SETBIT(sc, PN_BUSCTL, PN_CACHEALIGN_32LONG); + break; + case 16: + PN_SETBIT(sc, PN_BUSCTL, PN_CACHEALIGN_16LONG); + break; + case 8: + PN_SETBIT(sc, PN_BUSCTL, PN_CACHEALIGN_8LONG); + break; + case 0: + default: + PN_SETBIT(sc, PN_BUSCTL, PN_CACHEALIGN_NONE); + break; + } + CSR_WRITE_4(sc, PN_BUSCTL, PN_BURSTLEN_USECA); PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_TX_IMMEDIATE); PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_NO_RXCRC); @@ -1757,13 +1968,16 @@ static void pn_init(xsc) PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_TX_THRESH); PN_SETBIT(sc, PN_NETCFG, PN_TXTHRESH_72BYTES); - pn_setcfg(sc, pn_phy_readreg(sc, PHY_BMCR)); - - if (sc->pn_pinfo != NULL) { + if (sc->pn_pinfo == NULL) { + PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_MIIENB); + PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_TX_BACKOFF); + } else { PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_MIIENB); PN_SETBIT(sc, PN_ENDEC, PN_ENDEC_JABBERDIS); } + pn_setcfg(sc, sc->ifmedia.ifm_media); + /* Init circular RX list. */ if (pn_list_rx_init(sc) == ENOBUFS) { printf("pn%d: initialization failed: no " @@ -1825,10 +2039,17 @@ static int pn_ifmedia_upd(ifp) if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) return(EINVAL); - if (IFM_SUBTYPE(ifm->ifm_media) == IFM_AUTO) - pn_autoneg_mii(sc, PN_FLAG_SCHEDDELAY, 1); - else - pn_setmode_mii(sc, ifm->ifm_media); + if (IFM_SUBTYPE(ifm->ifm_media) == IFM_AUTO) { + if (sc->pn_pinfo == NULL) + pn_autoneg(sc, PN_FLAG_SCHEDDELAY, 1); + else + pn_autoneg_mii(sc, PN_FLAG_SCHEDDELAY, 1); + } else { + if (sc->pn_pinfo == NULL) + pn_setmode(sc, ifm->ifm_media); + else + pn_setmode_mii(sc, ifm->ifm_media); + } return(0); } @@ -1934,7 +2155,10 @@ static void pn_watchdog(ifp) sc = ifp->if_softc; if (sc->pn_autoneg) { - pn_autoneg_mii(sc, PN_FLAG_DELAYTIMEO, 1); + if (sc->pn_pinfo == NULL) + pn_autoneg(sc, PN_FLAG_DELAYTIMEO, 1); + else + pn_autoneg_mii(sc, PN_FLAG_DELAYTIMEO, 1); return; } diff --git a/sys/pci/if_pnreg.h b/sys/pci/if_pnreg.h index f97e04408abd..1e901b966fb3 100644 --- a/sys/pci/if_pnreg.h +++ b/sys/pci/if_pnreg.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_pnreg.h,v 1.21 1999/03/27 20:08:53 wpaul Exp $ + * $Id: if_pnreg.h,v 1.23 1999/04/10 18:22:22 wpaul Exp $ */ /* @@ -53,7 +53,6 @@ #define PN_MII 0xA0 /* MII access register */ #define PN_NWAY 0xB8 /* Internal NWAY register */ - /* * Bus control bits. */ @@ -71,6 +70,7 @@ #define PN_SKIPLEN_4LONG 0x00000020 #define PN_SKIPLEN_5LONG 0x00000040 +#define PN_CACHEALIGN_NONE 0x00000000 #define PN_CACHEALIGN_8LONG 0x00004000 #define PN_CACHEALIGN_16LONG 0x00008000 #define PN_CACHEALIGN_32LONG 0x0000C000 @@ -109,6 +109,7 @@ #define PN_ISR_RX_IDLE 0x00000100 /* rx stopped */ #define PN_ISR_RX_WATCHDOG 0x00000200 /* rx watchdog timeo */ #define PN_ISR_TX_EARLY 0x00000400 /* rx watchdog timeo */ +#define PN_ISR_LINKFAIL 0x00001000 #define PN_ISR_BUS_ERR 0x00002000 #define PN_ISR_ABNORMAL 0x00008000 #define PN_ISR_NORMAL 0x00010000 @@ -253,7 +254,7 @@ #define PN_NWAY_TP 0x00000040 /* 1 == tp, 0 == AUI */ #define PN_NWAY_AUIVOLT 0x00000080 /* 1 == full, 0 == half */ #define PN_NWAY_DUPLEX 0x00000100 /* 1 == full, 0 == half */ -#define PN_NWAY_LINKTEST 0x00000200 /* 1 == on, 0 == off */ +#define PN_NWAY_LINKTEST 0x00000200 /* 0 == on, 1 == off */ #define PN_NWAY_AUTODETECT 0x00000400 /* 1 == off, 0 == on */ #define PN_NWAY_SPEEDSEL 0x00000800 /* 0 == 10, 1 == 100 */ #define PN_NWAY_NWAY_ENB 0x00001000 /* 0 == off, 1 == on */ @@ -270,6 +271,46 @@ #define PN_NWAY_LPAR100HALF 0x40000000 #define PN_NWAY_LPAR100T4 0x80000000 +/* + * Nway register bits that must be set to turn on to initiate + * an autoneg session with all modes advertized and AUI disabled. + */ +#define PN_NWAY_AUTOENB \ + (PN_NWAY_AUILOWCUR|PN_NWAY_TPEXTEND|PN_NWAY_POLARITY|PN_NWAY_TP \ + |PN_NWAY_NWAY_ENB|PN_NWAY_CAP10HALF|PN_NWAY_CAP10FULL| \ + PN_NWAY_CAP100FULL|PN_NWAY_CAP100HALF|PN_NWAY_CAP100T4| \ + PN_NWAY_AUTONEGRSTR) + +#define PN_NWAY_MODE_10HD \ + (PN_NWAY_CAP10HALF|PN_NWAY_CAP10FULL| \ + PN_NWAY_CAP100FULL|PN_NWAY_CAP100HALF|PN_NWAY_CAP100T4| \ + PN_NWAY_AUILOWCUR|PN_NWAY_TPEXTEND|PN_NWAY_POLARITY| \ + PN_NWAY_TP) + +#define PN_NWAY_MODE_10FD \ + (PN_NWAY_CAP10HALF|PN_NWAY_CAP10FULL| \ + PN_NWAY_CAP100FULL|PN_NWAY_CAP100HALF|PN_NWAY_CAP100T4| \ + PN_NWAY_AUILOWCUR|PN_NWAY_TPEXTEND|PN_NWAY_POLARITY| \ + PN_NWAY_TP|PN_NWAY_DUPLEX) + +#define PN_NWAY_MODE_100HD \ + (PN_NWAY_CAP10HALF|PN_NWAY_CAP10FULL| \ + PN_NWAY_CAP100FULL|PN_NWAY_CAP100HALF|PN_NWAY_CAP100T4| \ + PN_NWAY_AUILOWCUR|PN_NWAY_TPEXTEND|PN_NWAY_POLARITY| \ + PN_NWAY_TP|PN_NWAY_SPEEDSEL) + +#define PN_NWAY_MODE_100FD \ + (PN_NWAY_CAP10HALF|PN_NWAY_CAP10FULL| \ + PN_NWAY_CAP100FULL|PN_NWAY_CAP100HALF|PN_NWAY_CAP100T4| \ + PN_NWAY_AUILOWCUR|PN_NWAY_TPEXTEND|PN_NWAY_POLARITY| \ + PN_NWAY_TP|PN_NWAY_SPEEDSEL|PN_NWAY_DUPLEX) + +#define PN_NWAY_MODE_100T4 PN_NWAY_MODE_100HD + +#define PN_NWAY_LPAR \ + (PN_NWAY_LPAR10HALF|PN_NWAY_LPAR10FULL|PN_NWAY_LPAR100HALF| \ + PN_NWAY_LPAR100FULL|PN_NWAY_LPAR100T4) + /* * Size of a setup frame. */ @@ -443,6 +484,7 @@ struct pn_softc { #define PN_169_REV 32 #define PN_169B_REV 33 u_int8_t pn_promisc_war; + u_int8_t pn_cachesize; struct pn_chain_onefrag *pn_promisc_bug_save; unsigned char *pn_promisc_buf; #endif @@ -482,6 +524,15 @@ struct pn_softc { #define PN_DEVICEID_PNIC 0x0002 #define PN_DEVICEID_PNIC_II 0xc115 +/* + * The 82c168 chip has the same PCI vendor/device ID as the + * 82c169, but a different revision. Assume that any revision + * between 0x10 an 0x1F is an 82c168. + */ +#define PN_REVMASK 0xF0 +#define PN_REVID_82C168 0x10 +#define PN_REVID_82C169 0x20 + /* * Texas Instruments PHY identifiers */ @@ -525,6 +576,7 @@ struct pn_softc { #define PN_PCI_STATUS 0x06 #define PN_PCI_REVISION 0x08 #define PN_PCI_CLASSCODE 0x09 +#define PN_PCI_CACHELEN 0x0C #define PN_PCI_LATENCY_TIMER 0x0D #define PN_PCI_HEADER_TYPE 0x0E #define PN_PCI_LOIO 0x10