diff options
author | wpaul <wpaul@FreeBSD.org> | 2000-10-05 17:36:14 +0000 |
---|---|---|
committer | wpaul <wpaul@FreeBSD.org> | 2000-10-05 17:36:14 +0000 |
commit | 094e009b7bf2f3229f20c0e9598169d008e309b1 (patch) | |
tree | 198194ff98d817ad854b208274fb8f632f9154ce | |
parent | 7e5aee1a7c10c28ef486189e52d18db3426fc94a (diff) | |
download | FreeBSD-src-094e009b7bf2f3229f20c0e9598169d008e309b1.zip FreeBSD-src-094e009b7bf2f3229f20c0e9598169d008e309b1.tar.gz |
Add support for parsing the media blocks from the SROM on 21143
adapters. This is necessary in order to make this driver work with
the built-in ethernet on the alpha Miata machines. These systems
have a 21143-PC chip on-board and optional daughtercards with either
a 10/100 MII transceiver or a 10baseT/10base2 transceiver. In both
cases, you need to twiddle the GPIO bits on the controller in order
to turn the transceivers on, and you have to read the media info
from the SROM in order to find out what bits to twiddle.
-rw-r--r-- | sys/dev/dc/dcphy.c | 24 | ||||
-rw-r--r-- | sys/dev/dc/if_dc.c | 278 | ||||
-rw-r--r-- | sys/dev/dc/if_dcreg.h | 120 | ||||
-rw-r--r-- | sys/dev/mii/dcphy.c | 24 | ||||
-rw-r--r-- | sys/pci/if_dc.c | 278 | ||||
-rw-r--r-- | sys/pci/if_dcreg.h | 120 |
6 files changed, 712 insertions, 132 deletions
diff --git a/sys/dev/dc/dcphy.c b/sys/dev/dc/dcphy.c index 73e241e..ad2599c 100644 --- a/sys/dev/dc/dcphy.c +++ b/sys/dev/dc/dcphy.c @@ -179,12 +179,17 @@ static int dcphy_attach(dev) sc->mii_capabilities = BMSR_ANEG|BMSR_10TFDX|BMSR_10THDX; break; default: - ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, - sc->mii_inst), BMCR_LOOP|BMCR_S100); - - sc->mii_capabilities = - BMSR_ANEG|BMSR_100TXFDX|BMSR_100TXHDX| - BMSR_10TFDX|BMSR_10THDX; + if (dc_sc->dc_pmode == DC_PMODE_SIA) { + sc->mii_capabilities = + BMSR_ANEG|BMSR_10TFDX|BMSR_10THDX; + } else { + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, + sc->mii_inst), BMCR_LOOP|BMCR_S100); + + sc->mii_capabilities = + BMSR_ANEG|BMSR_100TXFDX|BMSR_100TXHDX| + BMSR_10TFDX|BMSR_10THDX; + } break; } @@ -429,10 +434,11 @@ dcphy_status(sc) } skip: - if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_SCRAMBLER) - mii->mii_media_active |= IFM_100_TX; - else + + if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_SPEEDSEL) mii->mii_media_active |= IFM_10_T; + else + mii->mii_media_active |= IFM_100_TX; if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_FULLDUPLEX) mii->mii_media_active |= IFM_FDX; diff --git a/sys/dev/dc/if_dc.c b/sys/dev/dc/if_dc.c index 039f835..695b019 100644 --- a/sys/dev/dc/if_dc.c +++ b/sys/dev/dc/if_dc.c @@ -73,27 +73,6 @@ * combination with the internal NWAY support to create a 10/100 * autosensing configuration. * - * Knowing which media is available on a given card is tough: you're - * supposed to go slogging through the EEPROM looking for media - * description structures. Unfortunately, some card vendors that use - * the 21143 don't obey the DEC SROM spec correctly, which means that - * what you find in the EEPROM may not agree with reality. Fortunately, - * the 21143 provides us a way to get around this issue: lurking in - * PCI configuration space is the Configuration Wake-Up Command Register. - * This register is loaded with a value from the EEPROM when wake on LAN - * mode is enabled; this value tells us quite clearly what kind of media - * is attached to the NIC. The main purpose of this register is to tell - * the NIC what media to scan when in wake on LAN mode, however by - * forcibly enabling wake on LAN mode, we can use to learn what kind of - * media a given NIC has available and adapt ourselves accordingly. - * - * Of course, if the media description blocks in the EEPROM are bogus. - * what are the odds that the CWUC aren't bogus as well, right? Well, - * the CWUC value is more likely to be correct since wake on LAN mode - * won't work correctly without it, and wake on LAN is a big selling - * point these days. It's also harder to screw up a single byte than - * a whole media descriptor block. - * * Note that not all tulip workalikes are handled in this driver: we only * deal with those which are relatively well behaved. The Winbond is * handled separately due to its different register offsets and the @@ -140,6 +119,9 @@ #include <pci/pcivar.h> #define DC_USEIOSPACE +#ifdef __alpha__ +#define SRM_MEDIA +#endif #include <pci/if_dcreg.h> @@ -257,6 +239,15 @@ static void dc_reset __P((struct dc_softc *)); static int dc_list_rx_init __P((struct dc_softc *)); static int dc_list_tx_init __P((struct dc_softc *)); +static void dc_parse_21143_srom __P((struct dc_softc *)); +static void dc_decode_leaf_sia __P((struct dc_softc *, + struct dc_eblock_sia *)); +static void dc_decode_leaf_mii __P((struct dc_softc *, + struct dc_eblock_mii *)); +static void dc_decode_leaf_sym __P((struct dc_softc *, + struct dc_eblock_sym *)); +static void dc_apply_fixup __P((struct dc_softc *, int)); + #ifdef DC_USEIOSPACE #define DC_RES SYS_RES_IOPORT #define DC_RID DC_PCI_CFBIO @@ -691,7 +682,7 @@ static int dc_miibus_readreg(dev, phy, reg) if (DC_IS_ADMTEK(sc) && phy != DC_ADMTEK_PHYADDR) return(0); - if (sc->dc_pmode == DC_PMODE_SYM) { + if (sc->dc_pmode != DC_PMODE_MII) { if (phy == (MII_NPHY - 1)) { switch(reg) { case MII_BMSR: @@ -860,6 +851,7 @@ static void dc_miibus_statchg(dev) sc = device_get_softc(dev); if (DC_IS_ADMTEK(sc)) return; + mii = device_get_softc(sc->dc_miibus); ifm = &mii->mii_media; if (DC_IS_DAVICOM(sc) && @@ -1248,6 +1240,10 @@ static void dc_setcfg(sc, media) DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PCS); DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_SCRAMBLER); + if (DC_IS_INTEL(sc)) + dc_apply_fixup(sc, + (media & IFM_GMASK) == IFM_FDX ? + IFM_100_TX|IFM_FDX : IFM_100_TX); } } @@ -1278,6 +1274,21 @@ static void dc_setcfg(sc, media) DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PCS); DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_SCRAMBLER); + if (DC_IS_INTEL(sc)) { + DC_CLRBIT(sc, DC_SIARESET, DC_SIA_RESET); + DC_CLRBIT(sc, DC_10BTCTRL, 0xFFFF); + if ((media & IFM_GMASK) == IFM_FDX) + DC_SETBIT(sc, DC_10BTCTRL, 0x7F3D); + else + DC_SETBIT(sc, DC_10BTCTRL, 0x7F3F); + DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET); + DC_CLRBIT(sc, DC_10BTCTRL, + DC_TCTL_AUTONEGENBL); + dc_apply_fixup(sc, + (media & IFM_GMASK) == IFM_FDX ? + IFM_10_T|IFM_FDX : IFM_10_T); + DELAY(20000); + } } } @@ -1346,8 +1357,11 @@ static void dc_reset(sc) * into a state where it will never come out of reset * until we reset the whole chip again. */ - if (DC_IS_INTEL(sc)) + if (DC_IS_INTEL(sc)) { DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET); + CSR_WRITE_4(sc, DC_10BTCTRL, 0); + CSR_WRITE_4(sc, DC_WATCHDOG, 0); + } return; } @@ -1456,6 +1470,155 @@ static void dc_acpi(dev) return; } +static void dc_apply_fixup(sc, media) + struct dc_softc *sc; + int media; +{ + struct dc_mediainfo *m; + u_int8_t *p; + int i; + u_int8_t reg; + + m = sc->dc_mi; + + while (m != NULL) { + if (m->dc_media == media) + break; + m = m->dc_next; + } + + if (m == NULL) + return; + + for (i = 0, p = m->dc_reset_ptr; i < m->dc_reset_len; i++, p += 2) { + reg = (p[0] | (p[1] << 8)) << 16; + CSR_WRITE_4(sc, DC_WATCHDOG, reg); + } + + for (i = 0, p = m->dc_gp_ptr; i < m->dc_gp_len; i++, p += 2) { + reg = (p[0] | (p[1] << 8)) << 16; + CSR_WRITE_4(sc, DC_WATCHDOG, reg); + } + + return; +} + +static void dc_decode_leaf_sia(sc, l) + struct dc_softc *sc; + struct dc_eblock_sia *l; +{ + struct dc_mediainfo *m; + + m = malloc(sizeof(struct dc_mediainfo), M_DEVBUF, M_NOWAIT); + if (l->dc_sia_code == DC_SIA_CODE_10BT) + m->dc_media = IFM_10_T; + + if (l->dc_sia_code == DC_SIA_CODE_10BT_FDX) + m->dc_media = IFM_10_T|IFM_FDX; + + if (l->dc_sia_code == DC_SIA_CODE_10B2) + m->dc_media = IFM_10_2; + + if (l->dc_sia_code == DC_SIA_CODE_10B5) + m->dc_media = IFM_10_5; + + m->dc_gp_len = 2; + m->dc_gp_ptr = (u_int8_t *)&l->dc_sia_gpio_ctl; + + m->dc_next = sc->dc_mi; + sc->dc_mi = m; + + sc->dc_pmode = DC_PMODE_SIA; + + return; +} + +static void dc_decode_leaf_sym(sc, l) + struct dc_softc *sc; + struct dc_eblock_sym *l; +{ + struct dc_mediainfo *m; + + m = malloc(sizeof(struct dc_mediainfo), M_DEVBUF, M_NOWAIT); + if (l->dc_sym_code == DC_SYM_CODE_100BT) + m->dc_media = IFM_100_TX; + + if (l->dc_sym_code == DC_SYM_CODE_100BT_FDX) + m->dc_media = IFM_100_TX|IFM_FDX; + + m->dc_gp_len = 2; + m->dc_gp_ptr = (u_int8_t *)&l->dc_sym_gpio_ctl; + + m->dc_next = sc->dc_mi; + sc->dc_mi = m; + + sc->dc_pmode = DC_PMODE_SYM; + + return; +} + +static void dc_decode_leaf_mii(sc, l) + struct dc_softc *sc; + struct dc_eblock_mii *l; +{ + u_int8_t *p; + struct dc_mediainfo *m; + + m = malloc(sizeof(struct dc_mediainfo), M_DEVBUF, M_NOWAIT); + /* We abuse IFM_AUTO to represent MII. */ + m->dc_media = IFM_AUTO; + m->dc_gp_len = l->dc_gpr_len; + + p = (u_int8_t *)l; + p += sizeof(struct dc_eblock_mii); + m->dc_gp_ptr = p; + p += 2 * l->dc_gpr_len; + m->dc_reset_len = *p; + p++; + m->dc_reset_ptr = p; + + m->dc_next = sc->dc_mi; + sc->dc_mi = m; + + return; +} + +static void dc_parse_21143_srom(sc) + struct dc_softc *sc; +{ + struct dc_leaf_hdr *lhdr; + struct dc_eblock_hdr *hdr; + int i, loff; + char *ptr; + + loff = sc->dc_srom[27]; + lhdr = (struct dc_leaf_hdr *)&(sc->dc_srom[loff]); + + ptr = (char *)lhdr; + ptr += sizeof(struct dc_leaf_hdr) - 1; + for (i = 0; i < lhdr->dc_mcnt; i++) { + hdr = (struct dc_eblock_hdr *)ptr; + switch(hdr->dc_type) { + case DC_EBLOCK_MII: + dc_decode_leaf_mii(sc, (struct dc_eblock_mii *)hdr); + break; + case DC_EBLOCK_SIA: + dc_decode_leaf_sia(sc, (struct dc_eblock_sia *)hdr); + break; + case DC_EBLOCK_SYM: + dc_decode_leaf_sym(sc, (struct dc_eblock_sym *)hdr); + break; + default: + /* Don't care. Yet. */ + break; + } + ptr += (hdr->dc_len & 0x7F); + ptr++; + } + + return; +} + /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. @@ -1463,7 +1626,7 @@ static void dc_acpi(dev) static int dc_attach(dev) device_t dev; { - int s; + int s, tmp = 0; u_char eaddr[ETHER_ADDR_LEN]; u_int32_t command; struct dc_softc *sc; @@ -1548,12 +1711,14 @@ static int dc_attach(dev) sc->dc_type = DC_TYPE_21143; sc->dc_flags |= DC_TX_POLL|DC_TX_USE_TX_INTR; sc->dc_flags |= DC_REDUCED_MII_POLL; + /* Save EEPROM contents so we can parse them later. */ + dc_read_eeprom(sc, (caddr_t)&sc->dc_srom, 0, 512, 0); break; case DC_DEVICEID_DM9100: case DC_DEVICEID_DM9102: sc->dc_type = DC_TYPE_DM9102; sc->dc_flags |= DC_TX_COALESCE|DC_TX_USE_TX_INTR; - sc->dc_flags |= DC_REDUCED_MII_POLL; + sc->dc_flags |= DC_REDUCED_MII_POLL|DC_TX_ONE; sc->dc_pmode = DC_PMODE_MII; break; case DC_DEVICEID_AL981: @@ -1650,32 +1815,9 @@ static int dc_attach(dev) * The tricky ones are the Macronix/PNIC II and the * Intel 21143. */ - if (DC_IS_INTEL(sc)) { - u_int32_t media, cwuc; - cwuc = pci_read_config(dev, DC_PCI_CWUC, 4); - cwuc |= DC_CWUC_FORCE_WUL; - pci_write_config(dev, DC_PCI_CWUC, cwuc, 4); - DELAY(10000); - media = pci_read_config(dev, DC_PCI_CWUC, 4); - cwuc &= ~DC_CWUC_FORCE_WUL; - pci_write_config(dev, DC_PCI_CWUC, cwuc, 4); - DELAY(10000); - if (media & DC_CWUC_MII_ABILITY) - sc->dc_pmode = DC_PMODE_MII; - if (media & DC_CWUC_SYM_ABILITY) { - sc->dc_pmode = DC_PMODE_SYM; - sc->dc_flags |= DC_21143_NWAY; - } - /* - * If none of the bits are set, then this NIC - * isn't meant to support 'wake up LAN' mode. - * This is usually only the case on multiport - * cards, and these cards almost always have - * MII transceivers. - */ - if (media == 0) - sc->dc_pmode = DC_PMODE_MII; - } else if (DC_IS_MACRONIX(sc) || DC_IS_PNICII(sc)) { + if (DC_IS_INTEL(sc)) + dc_parse_21143_srom(sc); + else if (DC_IS_MACRONIX(sc) || DC_IS_PNICII(sc)) { if (sc->dc_type == DC_TYPE_98713) sc->dc_pmode = DC_PMODE_MII; else @@ -1749,13 +1891,25 @@ static int dc_attach(dev) ifp->if_snd.ifq_maxlen = DC_TX_LIST_CNT - 1; /* - * Do MII setup. + * Do MII setup. If this is a 21143, check for a PHY on the + * MII bus after applying any necessary fixups to twiddle the + * GPIO bits. If we don't end up finding a PHY, restore the + * old selection (SIA only or SIA/SYM) and attach the dcphy + * driver instead. */ + if (DC_IS_INTEL(sc)) { + dc_apply_fixup(sc, IFM_AUTO); + tmp = sc->dc_pmode; + sc->dc_pmode = DC_PMODE_MII; + } + error = mii_phy_probe(dev, &sc->dc_miibus, dc_ifmedia_upd, dc_ifmedia_sts); if (error && DC_IS_INTEL(sc)) { - sc->dc_pmode = DC_PMODE_SYM; + sc->dc_pmode = tmp; + if (sc->dc_pmode != DC_PMODE_SIA) + sc->dc_pmode = DC_PMODE_SYM; sc->dc_flags |= DC_21143_NWAY; mii_phy_probe(dev, &sc->dc_miibus, dc_ifmedia_upd, dc_ifmedia_sts); @@ -1786,7 +1940,7 @@ static int dc_attach(dev) ether_ifattach(ifp, ETHER_BPF_SUPPORTED); callout_handle_init(&sc->dc_stat_ch); -#ifdef __alpha__ +#ifdef SRM_MEDIA sc->dc_srm_media = 0; /* Remember the SRM console media setting */ @@ -1825,6 +1979,7 @@ static int dc_detach(dev) struct dc_softc *sc; struct ifnet *ifp; int s; + struct dc_mediainfo *m; s = splimp(); @@ -1845,6 +2000,12 @@ static int dc_detach(dev) if (sc->dc_pnic_rx_buf != NULL) free(sc->dc_pnic_rx_buf, M_DEVBUF); + while(sc->dc_mi != NULL) { + m = sc->dc_mi->dc_next; + free(sc->dc_mi, M_DEVBUF); + sc->dc_mi = m; + } + splx(s); return(0); @@ -2609,6 +2770,11 @@ static void dc_start(ifp) */ if (ifp->if_bpf) bpf_mtap(ifp, m_head); + + if (sc->dc_flags & DC_TX_ONE) { + ifp->if_flags |= IFF_OACTIVE; + break; + } } /* Transmit */ @@ -2775,7 +2941,7 @@ static void dc_init(xsc) sc->dc_stat_ch = timeout(dc_tick, sc, hz); } -#ifdef __alpha__ +#ifdef SRM_MEDIA if(sc->dc_srm_media) { struct ifreq ifr; @@ -2887,7 +3053,7 @@ static int dc_ioctl(ifp, command, data) case SIOCSIFMEDIA: mii = device_get_softc(sc->dc_miibus); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); -#ifdef __alpha__ +#ifdef SRM_MEDIA if (sc->dc_srm_media) sc->dc_srm_media = 0; #endif diff --git a/sys/dev/dc/if_dcreg.h b/sys/dev/dc/if_dcreg.h index a0a2444..9c6cfc4 100644 --- a/sys/dev/dc/if_dcreg.h +++ b/sys/dev/dc/if_dcreg.h @@ -96,6 +96,7 @@ /* MII/symbol mode port types */ #define DC_PMODE_MII 0x1 #define DC_PMODE_SYM 0x2 +#define DC_PMODE_SIA 0x3 /* * Bus control bits. @@ -445,6 +446,16 @@ struct dc_chain_data { int dc_rx_prod; }; +struct dc_mediainfo { + int dc_media; + u_int8_t *dc_gp_ptr; + u_int8_t dc_gp_len; + u_int8_t *dc_reset_ptr; + u_int8_t dc_reset_len; + struct dc_mediainfo *dc_next; +}; + + struct dc_type { u_int16_t dc_vid; u_int16_t dc_did; @@ -657,10 +668,12 @@ struct dc_softc { int dc_if_media; u_int32_t dc_flags; u_int32_t dc_txthresh; + u_int8_t dc_srom[1024]; + struct dc_mediainfo *dc_mi; struct dc_list_data *dc_ldata; struct dc_chain_data dc_cdata; struct callout_handle dc_stat_ch; -#ifdef __alpha__ +#ifdef SRM_MEDIA int dc_srm_media; #endif }; @@ -680,6 +693,7 @@ struct dc_softc { #define DC_128BIT_HASH 0x00001000 #define DC_64BIT_HASH 0x00002000 #define DC_TULIP_LEDS 0x00004000 +#define DC_TX_ONE 0x00008000 /* * register space access macros @@ -930,6 +944,110 @@ struct dc_softc { #define DC_CWUC_SYM_ABILITY 0x00000080 #define DC_CWUC_LOCK 0x00000100 +/* + * SROM nonsense. + */ + +#define DC_IB_CTLRCNT 0x13 +#define DC_IB_LEAF0_CNUM 0x1A +#define DC_IB_LEAF0_OFFSET 0x1B + +struct dc_info_leaf { + u_int16_t dc_conntype; + u_int8_t dc_blkcnt; + u_int8_t dc_rsvd; + u_int16_t dc_infoblk; +}; + +#define DC_CTYPE_10BT 0x0000 +#define DC_CTYPE_10BT_NWAY 0x0100 +#define DC_CTYPE_10BT_FDX 0x0204 +#define DC_CTYPE_10B2 0x0001 +#define DC_CTYPE_10B5 0x0002 +#define DC_CTYPE_100BT 0x0003 +#define DC_CTYPE_100BT_FDX 0x0205 +#define DC_CTYPE_100T4 0x0006 +#define DC_CTYPE_100FX 0x0007 +#define DC_CTYPE_100FX_FDX 0x0208 +#define DC_CTYPE_MII_10BT 0x0009 +#define DC_CTYPE_MII_10BT_FDX 0x020A +#define DC_CTYPE_MII_100BT 0x000D +#define DC_CTYPE_MII_100BT_FDX 0x020E +#define DC_CTYPE_MII_100T4 0x000F +#define DC_CTYPE_MII_100FX 0x0010 +#define DC_CTYPE_MII_100FX_FDX 0x0211 +#define DC_CTYPE_DYN_PUP_AUTOSENSE 0x0800 +#define DC_CTYPE_PUP_AUTOSENSe 0x8800 +#define DC_CTYPE_NOMEDIA 0xFFFF + +#define DC_EBLOCK_SIA 0x0002 +#define DC_EBLOCK_MII 0x0003 +#define DC_EBLOCK_SYM 0x0004 +#define DC_EBLOCK_RESET 0x0005 +#define DC_EBLOCK_PHY_SHUTDOWN 0x0006 + +struct dc_leaf_hdr { + u_int16_t dc_mtype; + u_int8_t dc_mcnt; + u_int8_t dc_rsvd; +}; + +struct dc_eblock_hdr { + u_int8_t dc_len; + u_int8_t dc_type; +}; + +struct dc_eblock_sia { + struct dc_eblock_hdr dc_sia_hdr; + u_int8_t dc_sia_code; + u_int8_t dc_sia_mediaspec[6]; /* CSR13, CSR14, CSR15 */ + u_int8_t dc_sia_gpio_ctl[2]; + u_int8_t dc_sia_gpio_dat[2]; +}; + +#define DC_SIA_CODE_10BT 0x00 +#define DC_SIA_CODE_10B2 0x01 +#define DC_SIA_CODE_10B5 0x02 +#define DC_SIA_CODE_10BT_FDX 0x04 +#define DC_SIA_CODE_EXT 0x40 + +/* + * Note that the first word in the gpr and reset + * sequences is always a control word. + */ +struct dc_eblock_mii { + struct dc_eblock_hdr dc_mii_hdr; + u_int8_t dc_mii_phynum; + u_int8_t dc_gpr_len; +/* u_int16_t dc_gpr_dat[n]; */ +/* u_int8_t dc_reset_len; */ +/* u_int16_t dc_reset_dat[n]; */ +/* There are other fields after these, but we don't + * care about them since they can be determined by looking + * at the PHY. + */ +}; + +struct dc_eblock_sym { + struct dc_eblock_hdr dc_sym_hdr; + u_int8_t dc_sym_code; + u_int8_t dc_sym_gpio_ctl[2]; + u_int8_t dc_sym_gpio_dat[2]; + u_int8_t dc_sym_cmd[2]; +}; + +#define DC_SYM_CODE_100BT 0x03 +#define DC_SYM_CODE_100BT_FDX 0x05 +#define DC_SYM_CODE_100T4 0x06 +#define DC_SYM_CODE_100FX 0x07 +#define DC_SYM_CODE_100FX_FDX 0x08 + +struct dc_eblock_reset { + struct dc_eblock_hdr dc_reset_hdr; + u_int8_t dc_reset_len; +/* u_int16_t dc_reset_dat[n]; */ +}; + #ifdef __alpha__ #undef vtophys #define vtophys(va) alpha_XXX_dmamap((vm_offset_t)va) diff --git a/sys/dev/mii/dcphy.c b/sys/dev/mii/dcphy.c index 73e241e..ad2599c 100644 --- a/sys/dev/mii/dcphy.c +++ b/sys/dev/mii/dcphy.c @@ -179,12 +179,17 @@ static int dcphy_attach(dev) sc->mii_capabilities = BMSR_ANEG|BMSR_10TFDX|BMSR_10THDX; break; default: - ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, - sc->mii_inst), BMCR_LOOP|BMCR_S100); - - sc->mii_capabilities = - BMSR_ANEG|BMSR_100TXFDX|BMSR_100TXHDX| - BMSR_10TFDX|BMSR_10THDX; + if (dc_sc->dc_pmode == DC_PMODE_SIA) { + sc->mii_capabilities = + BMSR_ANEG|BMSR_10TFDX|BMSR_10THDX; + } else { + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, + sc->mii_inst), BMCR_LOOP|BMCR_S100); + + sc->mii_capabilities = + BMSR_ANEG|BMSR_100TXFDX|BMSR_100TXHDX| + BMSR_10TFDX|BMSR_10THDX; + } break; } @@ -429,10 +434,11 @@ dcphy_status(sc) } skip: - if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_SCRAMBLER) - mii->mii_media_active |= IFM_100_TX; - else + + if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_SPEEDSEL) mii->mii_media_active |= IFM_10_T; + else + mii->mii_media_active |= IFM_100_TX; if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_FULLDUPLEX) mii->mii_media_active |= IFM_FDX; diff --git a/sys/pci/if_dc.c b/sys/pci/if_dc.c index 039f835..695b019 100644 --- a/sys/pci/if_dc.c +++ b/sys/pci/if_dc.c @@ -73,27 +73,6 @@ * combination with the internal NWAY support to create a 10/100 * autosensing configuration. * - * Knowing which media is available on a given card is tough: you're - * supposed to go slogging through the EEPROM looking for media - * description structures. Unfortunately, some card vendors that use - * the 21143 don't obey the DEC SROM spec correctly, which means that - * what you find in the EEPROM may not agree with reality. Fortunately, - * the 21143 provides us a way to get around this issue: lurking in - * PCI configuration space is the Configuration Wake-Up Command Register. - * This register is loaded with a value from the EEPROM when wake on LAN - * mode is enabled; this value tells us quite clearly what kind of media - * is attached to the NIC. The main purpose of this register is to tell - * the NIC what media to scan when in wake on LAN mode, however by - * forcibly enabling wake on LAN mode, we can use to learn what kind of - * media a given NIC has available and adapt ourselves accordingly. - * - * Of course, if the media description blocks in the EEPROM are bogus. - * what are the odds that the CWUC aren't bogus as well, right? Well, - * the CWUC value is more likely to be correct since wake on LAN mode - * won't work correctly without it, and wake on LAN is a big selling - * point these days. It's also harder to screw up a single byte than - * a whole media descriptor block. - * * Note that not all tulip workalikes are handled in this driver: we only * deal with those which are relatively well behaved. The Winbond is * handled separately due to its different register offsets and the @@ -140,6 +119,9 @@ #include <pci/pcivar.h> #define DC_USEIOSPACE +#ifdef __alpha__ +#define SRM_MEDIA +#endif #include <pci/if_dcreg.h> @@ -257,6 +239,15 @@ static void dc_reset __P((struct dc_softc *)); static int dc_list_rx_init __P((struct dc_softc *)); static int dc_list_tx_init __P((struct dc_softc *)); +static void dc_parse_21143_srom __P((struct dc_softc *)); +static void dc_decode_leaf_sia __P((struct dc_softc *, + struct dc_eblock_sia *)); +static void dc_decode_leaf_mii __P((struct dc_softc *, + struct dc_eblock_mii *)); +static void dc_decode_leaf_sym __P((struct dc_softc *, + struct dc_eblock_sym *)); +static void dc_apply_fixup __P((struct dc_softc *, int)); + #ifdef DC_USEIOSPACE #define DC_RES SYS_RES_IOPORT #define DC_RID DC_PCI_CFBIO @@ -691,7 +682,7 @@ static int dc_miibus_readreg(dev, phy, reg) if (DC_IS_ADMTEK(sc) && phy != DC_ADMTEK_PHYADDR) return(0); - if (sc->dc_pmode == DC_PMODE_SYM) { + if (sc->dc_pmode != DC_PMODE_MII) { if (phy == (MII_NPHY - 1)) { switch(reg) { case MII_BMSR: @@ -860,6 +851,7 @@ static void dc_miibus_statchg(dev) sc = device_get_softc(dev); if (DC_IS_ADMTEK(sc)) return; + mii = device_get_softc(sc->dc_miibus); ifm = &mii->mii_media; if (DC_IS_DAVICOM(sc) && @@ -1248,6 +1240,10 @@ static void dc_setcfg(sc, media) DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PCS); DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_SCRAMBLER); + if (DC_IS_INTEL(sc)) + dc_apply_fixup(sc, + (media & IFM_GMASK) == IFM_FDX ? + IFM_100_TX|IFM_FDX : IFM_100_TX); } } @@ -1278,6 +1274,21 @@ static void dc_setcfg(sc, media) DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PCS); DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_SCRAMBLER); + if (DC_IS_INTEL(sc)) { + DC_CLRBIT(sc, DC_SIARESET, DC_SIA_RESET); + DC_CLRBIT(sc, DC_10BTCTRL, 0xFFFF); + if ((media & IFM_GMASK) == IFM_FDX) + DC_SETBIT(sc, DC_10BTCTRL, 0x7F3D); + else + DC_SETBIT(sc, DC_10BTCTRL, 0x7F3F); + DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET); + DC_CLRBIT(sc, DC_10BTCTRL, + DC_TCTL_AUTONEGENBL); + dc_apply_fixup(sc, + (media & IFM_GMASK) == IFM_FDX ? + IFM_10_T|IFM_FDX : IFM_10_T); + DELAY(20000); + } } } @@ -1346,8 +1357,11 @@ static void dc_reset(sc) * into a state where it will never come out of reset * until we reset the whole chip again. */ - if (DC_IS_INTEL(sc)) + if (DC_IS_INTEL(sc)) { DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET); + CSR_WRITE_4(sc, DC_10BTCTRL, 0); + CSR_WRITE_4(sc, DC_WATCHDOG, 0); + } return; } @@ -1456,6 +1470,155 @@ static void dc_acpi(dev) return; } +static void dc_apply_fixup(sc, media) + struct dc_softc *sc; + int media; +{ + struct dc_mediainfo *m; + u_int8_t *p; + int i; + u_int8_t reg; + + m = sc->dc_mi; + + while (m != NULL) { + if (m->dc_media == media) + break; + m = m->dc_next; + } + + if (m == NULL) + return; + + for (i = 0, p = m->dc_reset_ptr; i < m->dc_reset_len; i++, p += 2) { + reg = (p[0] | (p[1] << 8)) << 16; + CSR_WRITE_4(sc, DC_WATCHDOG, reg); + } + + for (i = 0, p = m->dc_gp_ptr; i < m->dc_gp_len; i++, p += 2) { + reg = (p[0] | (p[1] << 8)) << 16; + CSR_WRITE_4(sc, DC_WATCHDOG, reg); + } + + return; +} + +static void dc_decode_leaf_sia(sc, l) + struct dc_softc *sc; + struct dc_eblock_sia *l; +{ + struct dc_mediainfo *m; + + m = malloc(sizeof(struct dc_mediainfo), M_DEVBUF, M_NOWAIT); + if (l->dc_sia_code == DC_SIA_CODE_10BT) + m->dc_media = IFM_10_T; + + if (l->dc_sia_code == DC_SIA_CODE_10BT_FDX) + m->dc_media = IFM_10_T|IFM_FDX; + + if (l->dc_sia_code == DC_SIA_CODE_10B2) + m->dc_media = IFM_10_2; + + if (l->dc_sia_code == DC_SIA_CODE_10B5) + m->dc_media = IFM_10_5; + + m->dc_gp_len = 2; + m->dc_gp_ptr = (u_int8_t *)&l->dc_sia_gpio_ctl; + + m->dc_next = sc->dc_mi; + sc->dc_mi = m; + + sc->dc_pmode = DC_PMODE_SIA; + + return; +} + +static void dc_decode_leaf_sym(sc, l) + struct dc_softc *sc; + struct dc_eblock_sym *l; +{ + struct dc_mediainfo *m; + + m = malloc(sizeof(struct dc_mediainfo), M_DEVBUF, M_NOWAIT); + if (l->dc_sym_code == DC_SYM_CODE_100BT) + m->dc_media = IFM_100_TX; + + if (l->dc_sym_code == DC_SYM_CODE_100BT_FDX) + m->dc_media = IFM_100_TX|IFM_FDX; + + m->dc_gp_len = 2; + m->dc_gp_ptr = (u_int8_t *)&l->dc_sym_gpio_ctl; + + m->dc_next = sc->dc_mi; + sc->dc_mi = m; + + sc->dc_pmode = DC_PMODE_SYM; + + return; +} + +static void dc_decode_leaf_mii(sc, l) + struct dc_softc *sc; + struct dc_eblock_mii *l; +{ + u_int8_t *p; + struct dc_mediainfo *m; + + m = malloc(sizeof(struct dc_mediainfo), M_DEVBUF, M_NOWAIT); + /* We abuse IFM_AUTO to represent MII. */ + m->dc_media = IFM_AUTO; + m->dc_gp_len = l->dc_gpr_len; + + p = (u_int8_t *)l; + p += sizeof(struct dc_eblock_mii); + m->dc_gp_ptr = p; + p += 2 * l->dc_gpr_len; + m->dc_reset_len = *p; + p++; + m->dc_reset_ptr = p; + + m->dc_next = sc->dc_mi; + sc->dc_mi = m; + + return; +} + +static void dc_parse_21143_srom(sc) + struct dc_softc *sc; +{ + struct dc_leaf_hdr *lhdr; + struct dc_eblock_hdr *hdr; + int i, loff; + char *ptr; + + loff = sc->dc_srom[27]; + lhdr = (struct dc_leaf_hdr *)&(sc->dc_srom[loff]); + + ptr = (char *)lhdr; + ptr += sizeof(struct dc_leaf_hdr) - 1; + for (i = 0; i < lhdr->dc_mcnt; i++) { + hdr = (struct dc_eblock_hdr *)ptr; + switch(hdr->dc_type) { + case DC_EBLOCK_MII: + dc_decode_leaf_mii(sc, (struct dc_eblock_mii *)hdr); + break; + case DC_EBLOCK_SIA: + dc_decode_leaf_sia(sc, (struct dc_eblock_sia *)hdr); + break; + case DC_EBLOCK_SYM: + dc_decode_leaf_sym(sc, (struct dc_eblock_sym *)hdr); + break; + default: + /* Don't care. Yet. */ + break; + } + ptr += (hdr->dc_len & 0x7F); + ptr++; + } + + return; +} + /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. @@ -1463,7 +1626,7 @@ static void dc_acpi(dev) static int dc_attach(dev) device_t dev; { - int s; + int s, tmp = 0; u_char eaddr[ETHER_ADDR_LEN]; u_int32_t command; struct dc_softc *sc; @@ -1548,12 +1711,14 @@ static int dc_attach(dev) sc->dc_type = DC_TYPE_21143; sc->dc_flags |= DC_TX_POLL|DC_TX_USE_TX_INTR; sc->dc_flags |= DC_REDUCED_MII_POLL; + /* Save EEPROM contents so we can parse them later. */ + dc_read_eeprom(sc, (caddr_t)&sc->dc_srom, 0, 512, 0); break; case DC_DEVICEID_DM9100: case DC_DEVICEID_DM9102: sc->dc_type = DC_TYPE_DM9102; sc->dc_flags |= DC_TX_COALESCE|DC_TX_USE_TX_INTR; - sc->dc_flags |= DC_REDUCED_MII_POLL; + sc->dc_flags |= DC_REDUCED_MII_POLL|DC_TX_ONE; sc->dc_pmode = DC_PMODE_MII; break; case DC_DEVICEID_AL981: @@ -1650,32 +1815,9 @@ static int dc_attach(dev) * The tricky ones are the Macronix/PNIC II and the * Intel 21143. */ - if (DC_IS_INTEL(sc)) { - u_int32_t media, cwuc; - cwuc = pci_read_config(dev, DC_PCI_CWUC, 4); - cwuc |= DC_CWUC_FORCE_WUL; - pci_write_config(dev, DC_PCI_CWUC, cwuc, 4); - DELAY(10000); - media = pci_read_config(dev, DC_PCI_CWUC, 4); - cwuc &= ~DC_CWUC_FORCE_WUL; - pci_write_config(dev, DC_PCI_CWUC, cwuc, 4); - DELAY(10000); - if (media & DC_CWUC_MII_ABILITY) - sc->dc_pmode = DC_PMODE_MII; - if (media & DC_CWUC_SYM_ABILITY) { - sc->dc_pmode = DC_PMODE_SYM; - sc->dc_flags |= DC_21143_NWAY; - } - /* - * If none of the bits are set, then this NIC - * isn't meant to support 'wake up LAN' mode. - * This is usually only the case on multiport - * cards, and these cards almost always have - * MII transceivers. - */ - if (media == 0) - sc->dc_pmode = DC_PMODE_MII; - } else if (DC_IS_MACRONIX(sc) || DC_IS_PNICII(sc)) { + if (DC_IS_INTEL(sc)) + dc_parse_21143_srom(sc); + else if (DC_IS_MACRONIX(sc) || DC_IS_PNICII(sc)) { if (sc->dc_type == DC_TYPE_98713) sc->dc_pmode = DC_PMODE_MII; else @@ -1749,13 +1891,25 @@ static int dc_attach(dev) ifp->if_snd.ifq_maxlen = DC_TX_LIST_CNT - 1; /* - * Do MII setup. + * Do MII setup. If this is a 21143, check for a PHY on the + * MII bus after applying any necessary fixups to twiddle the + * GPIO bits. If we don't end up finding a PHY, restore the + * old selection (SIA only or SIA/SYM) and attach the dcphy + * driver instead. */ + if (DC_IS_INTEL(sc)) { + dc_apply_fixup(sc, IFM_AUTO); + tmp = sc->dc_pmode; + sc->dc_pmode = DC_PMODE_MII; + } + error = mii_phy_probe(dev, &sc->dc_miibus, dc_ifmedia_upd, dc_ifmedia_sts); if (error && DC_IS_INTEL(sc)) { - sc->dc_pmode = DC_PMODE_SYM; + sc->dc_pmode = tmp; + if (sc->dc_pmode != DC_PMODE_SIA) + sc->dc_pmode = DC_PMODE_SYM; sc->dc_flags |= DC_21143_NWAY; mii_phy_probe(dev, &sc->dc_miibus, dc_ifmedia_upd, dc_ifmedia_sts); @@ -1786,7 +1940,7 @@ static int dc_attach(dev) ether_ifattach(ifp, ETHER_BPF_SUPPORTED); callout_handle_init(&sc->dc_stat_ch); -#ifdef __alpha__ +#ifdef SRM_MEDIA sc->dc_srm_media = 0; /* Remember the SRM console media setting */ @@ -1825,6 +1979,7 @@ static int dc_detach(dev) struct dc_softc *sc; struct ifnet *ifp; int s; + struct dc_mediainfo *m; s = splimp(); @@ -1845,6 +2000,12 @@ static int dc_detach(dev) if (sc->dc_pnic_rx_buf != NULL) free(sc->dc_pnic_rx_buf, M_DEVBUF); + while(sc->dc_mi != NULL) { + m = sc->dc_mi->dc_next; + free(sc->dc_mi, M_DEVBUF); + sc->dc_mi = m; + } + splx(s); return(0); @@ -2609,6 +2770,11 @@ static void dc_start(ifp) */ if (ifp->if_bpf) bpf_mtap(ifp, m_head); + + if (sc->dc_flags & DC_TX_ONE) { + ifp->if_flags |= IFF_OACTIVE; + break; + } } /* Transmit */ @@ -2775,7 +2941,7 @@ static void dc_init(xsc) sc->dc_stat_ch = timeout(dc_tick, sc, hz); } -#ifdef __alpha__ +#ifdef SRM_MEDIA if(sc->dc_srm_media) { struct ifreq ifr; @@ -2887,7 +3053,7 @@ static int dc_ioctl(ifp, command, data) case SIOCSIFMEDIA: mii = device_get_softc(sc->dc_miibus); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); -#ifdef __alpha__ +#ifdef SRM_MEDIA if (sc->dc_srm_media) sc->dc_srm_media = 0; #endif diff --git a/sys/pci/if_dcreg.h b/sys/pci/if_dcreg.h index a0a2444..9c6cfc4 100644 --- a/sys/pci/if_dcreg.h +++ b/sys/pci/if_dcreg.h @@ -96,6 +96,7 @@ /* MII/symbol mode port types */ #define DC_PMODE_MII 0x1 #define DC_PMODE_SYM 0x2 +#define DC_PMODE_SIA 0x3 /* * Bus control bits. @@ -445,6 +446,16 @@ struct dc_chain_data { int dc_rx_prod; }; +struct dc_mediainfo { + int dc_media; + u_int8_t *dc_gp_ptr; + u_int8_t dc_gp_len; + u_int8_t *dc_reset_ptr; + u_int8_t dc_reset_len; + struct dc_mediainfo *dc_next; +}; + + struct dc_type { u_int16_t dc_vid; u_int16_t dc_did; @@ -657,10 +668,12 @@ struct dc_softc { int dc_if_media; u_int32_t dc_flags; u_int32_t dc_txthresh; + u_int8_t dc_srom[1024]; + struct dc_mediainfo *dc_mi; struct dc_list_data *dc_ldata; struct dc_chain_data dc_cdata; struct callout_handle dc_stat_ch; -#ifdef __alpha__ +#ifdef SRM_MEDIA int dc_srm_media; #endif }; @@ -680,6 +693,7 @@ struct dc_softc { #define DC_128BIT_HASH 0x00001000 #define DC_64BIT_HASH 0x00002000 #define DC_TULIP_LEDS 0x00004000 +#define DC_TX_ONE 0x00008000 /* * register space access macros @@ -930,6 +944,110 @@ struct dc_softc { #define DC_CWUC_SYM_ABILITY 0x00000080 #define DC_CWUC_LOCK 0x00000100 +/* + * SROM nonsense. + */ + +#define DC_IB_CTLRCNT 0x13 +#define DC_IB_LEAF0_CNUM 0x1A +#define DC_IB_LEAF0_OFFSET 0x1B + +struct dc_info_leaf { + u_int16_t dc_conntype; + u_int8_t dc_blkcnt; + u_int8_t dc_rsvd; + u_int16_t dc_infoblk; +}; + +#define DC_CTYPE_10BT 0x0000 +#define DC_CTYPE_10BT_NWAY 0x0100 +#define DC_CTYPE_10BT_FDX 0x0204 +#define DC_CTYPE_10B2 0x0001 +#define DC_CTYPE_10B5 0x0002 +#define DC_CTYPE_100BT 0x0003 +#define DC_CTYPE_100BT_FDX 0x0205 +#define DC_CTYPE_100T4 0x0006 +#define DC_CTYPE_100FX 0x0007 +#define DC_CTYPE_100FX_FDX 0x0208 +#define DC_CTYPE_MII_10BT 0x0009 +#define DC_CTYPE_MII_10BT_FDX 0x020A +#define DC_CTYPE_MII_100BT 0x000D +#define DC_CTYPE_MII_100BT_FDX 0x020E +#define DC_CTYPE_MII_100T4 0x000F +#define DC_CTYPE_MII_100FX 0x0010 +#define DC_CTYPE_MII_100FX_FDX 0x0211 +#define DC_CTYPE_DYN_PUP_AUTOSENSE 0x0800 +#define DC_CTYPE_PUP_AUTOSENSe 0x8800 +#define DC_CTYPE_NOMEDIA 0xFFFF + +#define DC_EBLOCK_SIA 0x0002 +#define DC_EBLOCK_MII 0x0003 +#define DC_EBLOCK_SYM 0x0004 +#define DC_EBLOCK_RESET 0x0005 +#define DC_EBLOCK_PHY_SHUTDOWN 0x0006 + +struct dc_leaf_hdr { + u_int16_t dc_mtype; + u_int8_t dc_mcnt; + u_int8_t dc_rsvd; +}; + +struct dc_eblock_hdr { + u_int8_t dc_len; + u_int8_t dc_type; +}; + +struct dc_eblock_sia { + struct dc_eblock_hdr dc_sia_hdr; + u_int8_t dc_sia_code; + u_int8_t dc_sia_mediaspec[6]; /* CSR13, CSR14, CSR15 */ + u_int8_t dc_sia_gpio_ctl[2]; + u_int8_t dc_sia_gpio_dat[2]; +}; + +#define DC_SIA_CODE_10BT 0x00 +#define DC_SIA_CODE_10B2 0x01 +#define DC_SIA_CODE_10B5 0x02 +#define DC_SIA_CODE_10BT_FDX 0x04 +#define DC_SIA_CODE_EXT 0x40 + +/* + * Note that the first word in the gpr and reset + * sequences is always a control word. + */ +struct dc_eblock_mii { + struct dc_eblock_hdr dc_mii_hdr; + u_int8_t dc_mii_phynum; + u_int8_t dc_gpr_len; +/* u_int16_t dc_gpr_dat[n]; */ +/* u_int8_t dc_reset_len; */ +/* u_int16_t dc_reset_dat[n]; */ +/* There are other fields after these, but we don't + * care about them since they can be determined by looking + * at the PHY. + */ +}; + +struct dc_eblock_sym { + struct dc_eblock_hdr dc_sym_hdr; + u_int8_t dc_sym_code; + u_int8_t dc_sym_gpio_ctl[2]; + u_int8_t dc_sym_gpio_dat[2]; + u_int8_t dc_sym_cmd[2]; +}; + +#define DC_SYM_CODE_100BT 0x03 +#define DC_SYM_CODE_100BT_FDX 0x05 +#define DC_SYM_CODE_100T4 0x06 +#define DC_SYM_CODE_100FX 0x07 +#define DC_SYM_CODE_100FX_FDX 0x08 + +struct dc_eblock_reset { + struct dc_eblock_hdr dc_reset_hdr; + u_int8_t dc_reset_len; +/* u_int16_t dc_reset_dat[n]; */ +}; + #ifdef __alpha__ #undef vtophys #define vtophys(va) alpha_XXX_dmamap((vm_offset_t)va) |