diff options
| author | cvs2svn <cvs2svn@FreeBSD.org> | 2006-11-14 20:42:41 +0000 |
|---|---|---|
| committer | cvs2svn <cvs2svn@FreeBSD.org> | 2006-11-14 20:42:41 +0000 |
| commit | 2e696f27e8a561dcc35ddb119783263ecc5482ab (patch) | |
| tree | fa6863701dfdb582cee37a4ac16243b4dc4e7e0e /sys/dev/fe | |
| parent | ff0722cbd8a8f39ca6bf317088b8bce6496a3197 (diff) | |
| parent | d245b5915eaae1eea9ba999791a718614b8836aa (diff) | |
Notes
Diffstat (limited to 'sys/dev/fe')
| -rw-r--r-- | sys/dev/fe/if_fe.c | 2239 | ||||
| -rw-r--r-- | sys/dev/fe/if_fe_cbus.c | 1012 | ||||
| -rw-r--r-- | sys/dev/fe/if_fe_isa.c | 1053 | ||||
| -rw-r--r-- | sys/dev/fe/if_fe_pccard.c | 310 | ||||
| -rw-r--r-- | sys/dev/fe/if_fereg.h | 166 | ||||
| -rw-r--r-- | sys/dev/fe/if_fevar.h | 179 | ||||
| -rw-r--r-- | sys/dev/fe/mb86960.h | 340 |
7 files changed, 5299 insertions, 0 deletions
diff --git a/sys/dev/fe/if_fe.c b/sys/dev/fe/if_fe.c new file mode 100644 index 000000000000..0bc11979da1d --- /dev/null +++ b/sys/dev/fe/if_fe.c @@ -0,0 +1,2239 @@ +/*- + * All Rights Reserved, Copyright (C) Fujitsu Limited 1995 + * + * This software may be used, modified, copied, distributed, and sold, in + * both source and binary form provided that the above copyright, these + * terms and the following disclaimer are retained. The name of the author + * and/or the contributor may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND THE CONTRIBUTOR ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR THE CONTRIBUTOR BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION. + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * + * Device driver for Fujitsu MB86960A/MB86965A based Ethernet cards. + * Contributed by M. Sekiguchi. <seki@sysrap.cs.fujitsu.co.jp> + * + * This version is intended to be a generic template for various + * MB86960A/MB86965A based Ethernet cards. It currently supports + * Fujitsu FMV-180 series for ISA and Allied-Telesis AT1700/RE2000 + * series for ISA, as well as Fujitsu MBH10302 PC card. + * There are some currently- + * unused hooks embedded, which are primarily intended to support + * other types of Ethernet cards, but the author is not sure whether + * they are useful. + * + * This version also includes some alignments to support RE1000, + * C-NET(98)P2 and so on. These cards are not for AT-compatibles, + * but for NEC PC-98 bus -- a proprietary bus architecture available + * only in Japan. Confusingly, it is different from the Microsoft's + * PC98 architecture. :-{ + * Further work for PC-98 version will be available as a part of + * FreeBSD(98) project. + * + * This software is a derivative work of if_ed.c version 1.56 by David + * Greenman available as a part of FreeBSD 2.0 RELEASE source distribution. + * + * The following lines are retained from the original if_ed.c: + * + * Copyright (C) 1993, David Greenman. This software may be used, modified, + * copied, distributed, and sold, in both source and binary form provided + * that the above copyright and these terms are retained. Under no + * circumstances is the author responsible for the proper functioning + * of this software, nor does the author assume any responsibility + * for damages incurred with its use. + */ + +/* + * TODO: + * o To support ISA PnP auto configuration for FMV-183/184. + * o To support REX-9886/87(PC-98 only). + * o To reconsider mbuf usage. + * o To reconsider transmission buffer usage, including + * transmission buffer size (currently 4KB x 2) and pros-and- + * cons of multiple frame transmission. + * o To test IPX codes. + * o To test new-bus frontend. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/mbuf.h> + +#include <sys/bus.h> +#include <machine/bus.h> +#include <sys/rman.h> +#include <machine/resource.h> + +#include <net/ethernet.h> +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_mib.h> +#include <net/if_media.h> +#include <net/if_types.h> + +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#include <net/bpf.h> + +#include <dev/fe/mb86960.h> +#include <dev/fe/if_fereg.h> +#include <dev/fe/if_fevar.h> + +/* + * Transmit just one packet per a "send" command to 86960. + * This option is intended for performance test. An EXPERIMENTAL option. + */ +#ifndef FE_SINGLE_TRANSMISSION +#define FE_SINGLE_TRANSMISSION 0 +#endif + +/* + * Maximum loops when interrupt. + * This option prevents an infinite loop due to hardware failure. + * (Some laptops make an infinite loop after PC-Card is ejected.) + */ +#ifndef FE_MAX_LOOP +#define FE_MAX_LOOP 0x800 +#endif + +/* + * Device configuration flags. + */ + +/* DLCR6 settings. */ +#define FE_FLAGS_DLCR6_VALUE 0x007F + +/* Force DLCR6 override. */ +#define FE_FLAGS_OVERRIDE_DLCR6 0x0080 + + +devclass_t fe_devclass; + +/* + * Special filter values. + */ +static struct fe_filter const fe_filter_nothing = { FE_FILTER_NOTHING }; +static struct fe_filter const fe_filter_all = { FE_FILTER_ALL }; + +/* Standard driver entry points. These can be static. */ +static void fe_init (void *); +static driver_intr_t fe_intr; +static int fe_ioctl (struct ifnet *, u_long, caddr_t); +static void fe_start (struct ifnet *); +static void fe_watchdog (struct ifnet *); +static int fe_medchange (struct ifnet *); +static void fe_medstat (struct ifnet *, struct ifmediareq *); + +/* Local functions. Order of declaration is confused. FIXME. */ +static int fe_get_packet ( struct fe_softc *, u_short ); +static void fe_tint ( struct fe_softc *, u_char ); +static void fe_rint ( struct fe_softc *, u_char ); +static void fe_xmit ( struct fe_softc * ); +static void fe_write_mbufs ( struct fe_softc *, struct mbuf * ); +static void fe_setmode ( struct fe_softc * ); +static void fe_loadmar ( struct fe_softc * ); + +#ifdef DIAGNOSTIC +static void fe_emptybuffer ( struct fe_softc * ); +#endif + +/* + * Fe driver specific constants which relate to 86960/86965. + */ + +/* Interrupt masks */ +#define FE_TMASK ( FE_D2_COLL16 | FE_D2_TXDONE ) +#define FE_RMASK ( FE_D3_OVRFLO | FE_D3_CRCERR \ + | FE_D3_ALGERR | FE_D3_SRTPKT | FE_D3_PKTRDY ) + +/* Maximum number of iterations for a receive interrupt. */ +#define FE_MAX_RECV_COUNT ( ( 65536 - 2048 * 2 ) / 64 ) + /* + * Maximum size of SRAM is 65536, + * minimum size of transmission buffer in fe is 2x2KB, + * and minimum amount of received packet including headers + * added by the chip is 64 bytes. + * Hence FE_MAX_RECV_COUNT is the upper limit for number + * of packets in the receive buffer. + */ + +/* + * Miscellaneous definitions not directly related to hardware. + */ + +/* The following line must be delete when "net/if_media.h" support it. */ +#ifndef IFM_10_FL +#define IFM_10_FL /* 13 */ IFM_10_5 +#endif + +#if 0 +/* Mapping between media bitmap (in fe_softc.mbitmap) and ifm_media. */ +static int const bit2media [] = { + IFM_HDX | IFM_ETHER | IFM_AUTO, + IFM_HDX | IFM_ETHER | IFM_MANUAL, + IFM_HDX | IFM_ETHER | IFM_10_T, + IFM_HDX | IFM_ETHER | IFM_10_2, + IFM_HDX | IFM_ETHER | IFM_10_5, + IFM_HDX | IFM_ETHER | IFM_10_FL, + IFM_FDX | IFM_ETHER | IFM_10_T, + /* More can be come here... */ + 0 +}; +#else +/* Mapping between media bitmap (in fe_softc.mbitmap) and ifm_media. */ +static int const bit2media [] = { + IFM_ETHER | IFM_AUTO, + IFM_ETHER | IFM_MANUAL, + IFM_ETHER | IFM_10_T, + IFM_ETHER | IFM_10_2, + IFM_ETHER | IFM_10_5, + IFM_ETHER | IFM_10_FL, + IFM_ETHER | IFM_10_T, + /* More can be come here... */ + 0 +}; +#endif + +/* + * Check for specific bits in specific registers have specific values. + * A common utility function called from various sub-probe routines. + */ +int +fe_simple_probe (struct fe_softc const * sc, + struct fe_simple_probe_struct const * sp) +{ + struct fe_simple_probe_struct const *p; + int8_t bits; + + for (p = sp; p->mask != 0; p++) { + bits = fe_inb(sc, p->port); + printf("port %d, mask %x, bits %x read %x\n", p->port, + p->mask, p->bits, bits); + if ((bits & p->mask) != p->bits) + return 0; + } + return 1; +} + +/* Test if a given 6 byte value is a valid Ethernet station (MAC) + address. "Vendor" is an expected vendor code (first three bytes,) + or a zero when nothing expected. */ +int +fe_valid_Ether_p (u_char const * addr, unsigned vendor) +{ +#ifdef FE_DEBUG + printf("fe?: validating %6D against %06x\n", addr, ":", vendor); +#endif + + /* All zero is not allowed as a vendor code. */ + if (addr[0] == 0 && addr[1] == 0 && addr[2] == 0) return 0; + + switch (vendor) { + case 0x000000: + /* Legal Ethernet address (stored in ROM) must have + its Group and Local bits cleared. */ + if ((addr[0] & 0x03) != 0) return 0; + break; + case 0x020000: + /* Same as above, but a local address is allowed in + this context. */ + if (ETHER_IS_MULTICAST(addr)) return 0; + break; + default: + /* Make sure the vendor part matches if one is given. */ + if ( addr[0] != ((vendor >> 16) & 0xFF) + || addr[1] != ((vendor >> 8) & 0xFF) + || addr[2] != ((vendor ) & 0xFF)) return 0; + break; + } + + /* Host part must not be all-zeros nor all-ones. */ + if (addr[3] == 0xFF && addr[4] == 0xFF && addr[5] == 0xFF) return 0; + if (addr[3] == 0x00 && addr[4] == 0x00 && addr[5] == 0x00) return 0; + + /* Given addr looks like an Ethernet address. */ + return 1; +} + +/* Fill our softc struct with default value. */ +void +fe_softc_defaults (struct fe_softc *sc) +{ + /* Prepare for typical register prototypes. We assume a + "typical" board has <32KB> of <fast> SRAM connected with a + <byte-wide> data lines. */ + sc->proto_dlcr4 = FE_D4_LBC_DISABLE | FE_D4_CNTRL; + sc->proto_dlcr5 = 0; + sc->proto_dlcr6 = FE_D6_BUFSIZ_32KB | FE_D6_TXBSIZ_2x4KB + | FE_D6_BBW_BYTE | FE_D6_SBW_WORD | FE_D6_SRAM_100ns; + sc->proto_dlcr7 = FE_D7_BYTSWP_LH; + sc->proto_bmpr13 = 0; + + /* Assume the probe process (to be done later) is stable. */ + sc->stability = 0; + + /* A typical board needs no hooks. */ + sc->init = NULL; + sc->stop = NULL; + + /* Assume the board has no software-controllable media selection. */ + sc->mbitmap = MB_HM; + sc->defmedia = MB_HM; + sc->msel = NULL; +} + +/* Common error reporting routine used in probe routines for + "soft configured IRQ"-type boards. */ +void +fe_irq_failure (char const *name, int unit, int irq, char const *list) +{ + printf("fe%d: %s board is detected, but %s IRQ was given\n", + unit, name, (irq == NO_IRQ ? "no" : "invalid")); + if (list != NULL) { + printf("fe%d: specify an IRQ from %s in kernel config\n", + unit, list); + } +} + +/* + * Hardware (vendor) specific hooks. + */ + +/* + * Generic media selection scheme for MB86965 based boards. + */ +void +fe_msel_965 (struct fe_softc *sc) +{ + u_char b13; + + /* Find the appropriate bits for BMPR13 tranceiver control. */ + switch (IFM_SUBTYPE(sc->media.ifm_media)) { + case IFM_AUTO: b13 = FE_B13_PORT_AUTO | FE_B13_TPTYPE_UTP; break; + case IFM_10_T: b13 = FE_B13_PORT_TP | FE_B13_TPTYPE_UTP; break; + default: b13 = FE_B13_PORT_AUI; break; + } + + /* Write it into the register. It takes effect immediately. */ + fe_outb(sc, FE_BMPR13, sc->proto_bmpr13 | b13); +} + + +/* + * Fujitsu MB86965 JLI mode support routines. + */ + +/* + * Routines to read all bytes from the config EEPROM through MB86965A. + * It is a MicroWire (3-wire) serial EEPROM with 6-bit address. + * (93C06 or 93C46.) + */ +static void +fe_strobe_eeprom_jli (struct fe_softc *sc, u_short bmpr16) +{ + /* + * We must guarantee 1us (or more) interval to access slow + * EEPROMs. The following redundant code provides enough + * delay with ISA timing. (Even if the bus clock is "tuned.") + * Some modification will be needed on faster busses. + */ + fe_outb(sc, bmpr16, FE_B16_SELECT); + fe_outb(sc, bmpr16, FE_B16_SELECT | FE_B16_CLOCK); + fe_outb(sc, bmpr16, FE_B16_SELECT | FE_B16_CLOCK); + fe_outb(sc, bmpr16, FE_B16_SELECT); +} + +void +fe_read_eeprom_jli (struct fe_softc * sc, u_char * data) +{ + u_char n, val, bit; + u_char save16, save17; + + /* Save the current value of the EEPROM interface registers. */ + save16 = fe_inb(sc, FE_BMPR16); + save17 = fe_inb(sc, FE_BMPR17); + + /* Read bytes from EEPROM; two bytes per an iteration. */ + for (n = 0; n < JLI_EEPROM_SIZE / 2; n++) { + + /* Reset the EEPROM interface. */ + fe_outb(sc, FE_BMPR16, 0x00); + fe_outb(sc, FE_BMPR17, 0x00); + + /* Start EEPROM access. */ + fe_outb(sc, FE_BMPR16, FE_B16_SELECT); + fe_outb(sc, FE_BMPR17, FE_B17_DATA); + fe_strobe_eeprom_jli(sc, FE_BMPR16); + + /* Pass the iteration count as well as a READ command. */ + val = 0x80 | n; + for (bit = 0x80; bit != 0x00; bit >>= 1) { + fe_outb(sc, FE_BMPR17, (val & bit) ? FE_B17_DATA : 0); + fe_strobe_eeprom_jli(sc, FE_BMPR16); + } + fe_outb(sc, FE_BMPR17, 0x00); + + /* Read a byte. */ + val = 0; + for (bit = 0x80; bit != 0x00; bit >>= 1) { + fe_strobe_eeprom_jli(sc, FE_BMPR16); + if (fe_inb(sc, FE_BMPR17) & FE_B17_DATA) + val |= bit; + } + *data++ = val; + + /* Read one more byte. */ + val = 0; + for (bit = 0x80; bit != 0x00; bit >>= 1) { + fe_strobe_eeprom_jli(sc, FE_BMPR16); + if (fe_inb(sc, FE_BMPR17) & FE_B17_DATA) + val |= bit; + } + *data++ = val; + } + +#if 0 + /* Reset the EEPROM interface, again. */ + fe_outb(sc, FE_BMPR16, 0x00); + fe_outb(sc, FE_BMPR17, 0x00); +#else + /* Make sure to restore the original value of EEPROM interface + registers, since we are not yet sure we have MB86965A on + the address. */ + fe_outb(sc, FE_BMPR17, save17); + fe_outb(sc, FE_BMPR16, save16); +#endif + +#if 1 + /* Report what we got. */ + if (bootverbose) { + int i; + data -= JLI_EEPROM_SIZE; + for (i = 0; i < JLI_EEPROM_SIZE; i += 16) { + if_printf(sc->ifp, + "EEPROM(JLI):%3x: %16D\n", i, data + i, " "); + } + } +#endif +} + +void +fe_init_jli (struct fe_softc * sc) +{ + /* "Reset" by writing into a magic location. */ + DELAY(200); + fe_outb(sc, 0x1E, fe_inb(sc, 0x1E)); + DELAY(300); +} + + +/* + * SSi 78Q8377A support routines. + */ + +/* + * Routines to read all bytes from the config EEPROM through 78Q8377A. + * It is a MicroWire (3-wire) serial EEPROM with 8-bit address. (I.e., + * 93C56 or 93C66.) + * + * As I don't have SSi manuals, (hmm, an old song again!) I'm not exactly + * sure the following code is correct... It is just stolen from the + * C-NET(98)P2 support routine in FreeBSD(98). + */ + +void +fe_read_eeprom_ssi (struct fe_softc *sc, u_char *data) +{ + u_char val, bit; + int n; + u_char save6, save7, save12; + + /* Save the current value for the DLCR registers we are about + to destroy. */ + save6 = fe_inb(sc, FE_DLCR6); + save7 = fe_inb(sc, FE_DLCR7); + + /* Put the 78Q8377A into a state that we can access the EEPROM. */ + fe_outb(sc, FE_DLCR6, + FE_D6_BBW_WORD | FE_D6_SBW_WORD | FE_D6_DLC_DISABLE); + fe_outb(sc, FE_DLCR7, + FE_D7_BYTSWP_LH | FE_D7_RBS_BMPR | FE_D7_RDYPNS | FE_D7_POWER_UP); + + /* Save the current value for the BMPR12 register, too. */ + save12 = fe_inb(sc, FE_DLCR12); + + /* Read bytes from EEPROM; two bytes per an iteration. */ + for (n = 0; n < SSI_EEPROM_SIZE / 2; n++) { + + /* Start EEPROM access */ + fe_outb(sc, FE_DLCR12, SSI_EEP); + fe_outb(sc, FE_DLCR12, SSI_EEP | SSI_CSL); + + /* Send the following four bits to the EEPROM in the + specified order: a dummy bit, a start bit, and + command bits (10) for READ. */ + fe_outb(sc, FE_DLCR12, SSI_EEP | SSI_CSL ); + fe_outb(sc, FE_DLCR12, SSI_EEP | SSI_CSL | SSI_CLK ); /* 0 */ + fe_outb(sc, FE_DLCR12, SSI_EEP | SSI_CSL | SSI_DAT); + fe_outb(sc, FE_DLCR12, SSI_EEP | SSI_CSL | SSI_CLK | SSI_DAT); /* 1 */ + fe_outb(sc, FE_DLCR12, SSI_EEP | SSI_CSL | SSI_DAT); + fe_outb(sc, FE_DLCR12, SSI_EEP | SSI_CSL | SSI_CLK | SSI_DAT); /* 1 */ + fe_outb(sc, FE_DLCR12, SSI_EEP | SSI_CSL ); + fe_outb(sc, FE_DLCR12, SSI_EEP | SSI_CSL | SSI_CLK ); /* 0 */ + + /* Pass the iteration count to the chip. */ + for (bit = 0x80; bit != 0x00; bit >>= 1) { + val = ( n & bit ) ? SSI_DAT : 0; + fe_outb(sc, FE_DLCR12, SSI_EEP | SSI_CSL | val); + fe_outb(sc, FE_DLCR12, SSI_EEP | SSI_CSL | SSI_CLK | val); + } + + /* Read a byte. */ + val = 0; + for (bit = 0x80; bit != 0x00; bit >>= 1) { + fe_outb(sc, FE_DLCR12, SSI_EEP | SSI_CSL); + fe_outb(sc, FE_DLCR12, SSI_EEP | SSI_CSL | SSI_CLK); + if (fe_inb(sc, FE_DLCR12) & SSI_DIN) + val |= bit; + } + *data++ = val; + + /* Read one more byte. */ + val = 0; + for (bit = 0x80; bit != 0x00; bit >>= 1) { + fe_outb(sc, FE_DLCR12, SSI_EEP | SSI_CSL); + fe_outb(sc, FE_DLCR12, SSI_EEP | SSI_CSL | SSI_CLK); + if (fe_inb(sc, FE_DLCR12) & SSI_DIN) + val |= bit; + } + *data++ = val; + + fe_outb(sc, FE_DLCR12, SSI_EEP); + } + + /* Reset the EEPROM interface. (For now.) */ + fe_outb(sc, FE_DLCR12, 0x00); + + /* Restore the saved register values, for the case that we + didn't have 78Q8377A at the given address. */ + fe_outb(sc, FE_DLCR12, save12); + fe_outb(sc, FE_DLCR7, save7); + fe_outb(sc, FE_DLCR6, save6); + +#if 1 + /* Report what we got. */ + if (bootverbose) { + int i; + data -= SSI_EEPROM_SIZE; + for (i = 0; i < SSI_EEPROM_SIZE; i += 16) { + if_printf(sc->ifp, + "EEPROM(SSI):%3x: %16D\n", i, data + i, " "); + } + } +#endif +} + +/* + * TDK/LANX boards support routines. + */ + +/* It is assumed that the CLK line is low and SDA is high (float) upon entry. */ +#define LNX_PH(D,K,N) \ + ((LNX_SDA_##D | LNX_CLK_##K) << N) +#define LNX_CYCLE(D1,D2,D3,D4,K1,K2,K3,K4) \ + (LNX_PH(D1,K1,0)|LNX_PH(D2,K2,8)|LNX_PH(D3,K3,16)|LNX_PH(D4,K4,24)) + +#define LNX_CYCLE_START LNX_CYCLE(HI,LO,LO,HI, HI,HI,LO,LO) +#define LNX_CYCLE_STOP LNX_CYCLE(LO,LO,HI,HI, LO,HI,HI,LO) +#define LNX_CYCLE_HI LNX_CYCLE(HI,HI,HI,HI, LO,HI,LO,LO) +#define LNX_CYCLE_LO LNX_CYCLE(LO,LO,LO,HI, LO,HI,LO,LO) +#define LNX_CYCLE_INIT LNX_CYCLE(LO,HI,HI,HI, LO,LO,LO,LO) + +static void +fe_eeprom_cycle_lnx (struct fe_softc *sc, u_short reg20, u_long cycle) +{ + fe_outb(sc, reg20, (cycle ) & 0xFF); + DELAY(15); + fe_outb(sc, reg20, (cycle >> 8) & 0xFF); + DELAY(15); + fe_outb(sc, reg20, (cycle >> 16) & 0xFF); + DELAY(15); + fe_outb(sc, reg20, (cycle >> 24) & 0xFF); + DELAY(15); +} + +static u_char +fe_eeprom_receive_lnx (struct fe_softc *sc, u_short reg20) +{ + u_char dat; + + fe_outb(sc, reg20, LNX_CLK_HI | LNX_SDA_FL); + DELAY(15); + dat = fe_inb(sc, reg20); + fe_outb(sc, reg20, LNX_CLK_LO | LNX_SDA_FL); + DELAY(15); + return (dat & LNX_SDA_IN); +} + +void +fe_read_eeprom_lnx (struct fe_softc *sc, u_char *data) +{ + int i; + u_char n, bit, val; + u_char save20; + u_short reg20 = 0x14; + + save20 = fe_inb(sc, reg20); + + /* NOTE: DELAY() timing constants are approximately three + times longer (slower) than the required minimum. This is + to guarantee a reliable operation under some tough + conditions... Fortunately, this routine is only called + during the boot phase, so the speed is less important than + stability. */ + +#if 1 + /* Reset the X24C01's internal state machine and put it into + the IDLE state. We usually don't need this, but *if* + someone (e.g., probe routine of other driver) write some + garbage into the register at 0x14, synchronization will be + lost, and the normal EEPROM access protocol won't work. + Moreover, as there are no easy way to reset, we need a + _manoeuvre_ here. (It even lacks a reset pin, so pushing + the RESET button on the PC doesn't help!) */ + fe_eeprom_cycle_lnx(sc, reg20, LNX_CYCLE_INIT); + for (i = 0; i < 10; i++) + fe_eeprom_cycle_lnx(sc, reg20, LNX_CYCLE_START); + fe_eeprom_cycle_lnx(sc, reg20, LNX_CYCLE_STOP); + DELAY(10000); +#endif + + /* Issue a start condition. */ + fe_eeprom_cycle_lnx(sc, reg20, LNX_CYCLE_START); + + /* Send seven bits of the starting address (zero, in this + case) and a command bit for READ. */ + val = 0x01; + for (bit = 0x80; bit != 0x00; bit >>= 1) { + if (val & bit) { + fe_eeprom_cycle_lnx(sc, reg20, LNX_CYCLE_HI); + } else { + fe_eeprom_cycle_lnx(sc, reg20, LNX_CYCLE_LO); + } + } + + /* Receive an ACK bit. */ + if (fe_eeprom_receive_lnx(sc, reg20)) { + /* ACK was not received. EEPROM is not present (i.e., + this board was not a TDK/LANX) or not working + properly. */ + if (bootverbose) { + if_printf(sc->ifp, + "no ACK received from EEPROM(LNX)\n"); + } + /* Clear the given buffer to indicate we could not get + any info. and return. */ + bzero(data, LNX_EEPROM_SIZE); + goto RET; + } + + /* Read bytes from EEPROM. */ + for (n = 0; n < LNX_EEPROM_SIZE; n++) { + + /* Read a byte and store it into the buffer. */ + val = 0x00; + for (bit = 0x80; bit != 0x00; bit >>= 1) { + if (fe_eeprom_receive_lnx(sc, reg20)) + val |= bit; + } + *data++ = val; + + /* Acknowledge if we have to read more. */ + if (n < LNX_EEPROM_SIZE - 1) { + fe_eeprom_cycle_lnx(sc, reg20, LNX_CYCLE_LO); + } + } + + /* Issue a STOP condition, de-activating the clock line. + It will be safer to keep the clock line low than to leave + it high. */ + fe_eeprom_cycle_lnx(sc, reg20, LNX_CYCLE_STOP); + + RET: + fe_outb(sc, reg20, save20); + +#if 1 + /* Report what we got. */ + if (bootverbose) { + data -= LNX_EEPROM_SIZE; + for (i = 0; i < LNX_EEPROM_SIZE; i += 16) { + if_printf(sc->ifp, + "EEPROM(LNX):%3x: %16D\n", i, data + i, " "); + } + } +#endif +} + +void +fe_init_lnx (struct fe_softc * sc) +{ + /* Reset the 86960. Do we need this? FIXME. */ + fe_outb(sc, 0x12, 0x06); + DELAY(100); + fe_outb(sc, 0x12, 0x07); + DELAY(100); + + /* Setup IRQ control register on the ASIC. */ + fe_outb(sc, 0x14, sc->priv_info); +} + + +/* + * Ungermann-Bass boards support routine. + */ +void +fe_init_ubn (struct fe_softc * sc) +{ + /* Do we need this? FIXME. */ + fe_outb(sc, FE_DLCR7, + sc->proto_dlcr7 | FE_D7_RBS_BMPR | FE_D7_POWER_UP); + fe_outb(sc, 0x18, 0x00); + DELAY(200); + + /* Setup IRQ control register on the ASIC. */ + fe_outb(sc, 0x14, sc->priv_info); +} + + +/* + * Install interface into kernel networking data structures + */ +int +fe_attach (device_t dev) +{ + struct fe_softc *sc = device_get_softc(dev); + struct ifnet *ifp; + int flags = device_get_flags(dev); + int b, error; + + ifp = sc->ifp = if_alloc(IFT_ETHER); + if (ifp == NULL) { + device_printf(dev, "can not ifalloc\n"); + return (ENOSPC); + } + + error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET, + fe_intr, sc, &sc->irq_handle); + if (error) { + if_free(ifp); + fe_release_resource(dev); + return ENXIO; + } + + /* + * Initialize ifnet structure + */ + ifp->if_softc = sc; + if_initname(sc->ifp, device_get_name(dev), device_get_unit(dev)); + ifp->if_start = fe_start; + ifp->if_ioctl = fe_ioctl; + ifp->if_watchdog = fe_watchdog; + ifp->if_init = fe_init; + ifp->if_linkmib = &sc->mibdata; + ifp->if_linkmiblen = sizeof (sc->mibdata); + +#if 0 /* I'm not sure... */ + sc->mibdata.dot3Compliance = DOT3COMPLIANCE_COLLS; +#endif + + /* + * Set fixed interface flags. + */ + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST | + IFF_NEEDSGIANT; + +#if 1 + /* + * Set maximum size of output queue, if it has not been set. + * It is done here as this driver may be started after the + * system initialization (i.e., the interface is PCMCIA.) + * + * I'm not sure this is really necessary, but, even if it is, + * it should be done somewhere else, e.g., in if_attach(), + * since it must be a common workaround for all network drivers. + * FIXME. + */ + if (ifp->if_snd.ifq_maxlen == 0) + ifp->if_snd.ifq_maxlen = ifqmaxlen; +#endif + +#if FE_SINGLE_TRANSMISSION + /* Override txb config to allocate minimum. */ + sc->proto_dlcr6 &= ~FE_D6_TXBSIZ + sc->proto_dlcr6 |= FE_D6_TXBSIZ_2x2KB; +#endif + + /* Modify hardware config if it is requested. */ + if (flags & FE_FLAGS_OVERRIDE_DLCR6) + sc->proto_dlcr6 = flags & FE_FLAGS_DLCR6_VALUE; + + /* Find TX buffer size, based on the hardware dependent proto. */ + switch (sc->proto_dlcr6 & FE_D6_TXBSIZ) { + case FE_D6_TXBSIZ_2x2KB: sc->txb_size = 2048; break; + case FE_D6_TXBSIZ_2x4KB: sc->txb_size = 4096; break; + case FE_D6_TXBSIZ_2x8KB: sc->txb_size = 8192; break; + default: + /* Oops, we can't work with single buffer configuration. */ + if (bootverbose) { + if_printf(sc->ifp, + "strange TXBSIZ config; fixing\n"); + } + sc->proto_dlcr6 &= ~FE_D6_TXBSIZ; + sc->proto_dlcr6 |= FE_D6_TXBSIZ_2x2KB; + sc->txb_size = 2048; + break; + } + + /* Initialize the if_media interface. */ + ifmedia_init(&sc->media, 0, fe_medchange, fe_medstat); + for (b = 0; bit2media[b] != 0; b++) { + if (sc->mbitmap & (1 << b)) { + ifmedia_add(&sc->media, bit2media[b], 0, NULL); + } + } + for (b = 0; bit2media[b] != 0; b++) { + if (sc->defmedia & (1 << b)) { + ifmedia_set(&sc->media, bit2media[b]); + break; + } + } +#if 0 /* Turned off; this is called later, when the interface UPs. */ + fe_medchange(sc); +#endif + + /* Attach and stop the interface. */ + ether_ifattach(sc->ifp, sc->enaddr); + fe_stop(sc); + + /* Print additional info when attached. */ + device_printf(dev, "type %s%s\n", sc->typestr, + (sc->proto_dlcr4 & FE_D4_DSC) ? ", full duplex" : ""); + if (bootverbose) { + int buf, txb, bbw, sbw, ram; + + buf = txb = bbw = sbw = ram = -1; + switch ( sc->proto_dlcr6 & FE_D6_BUFSIZ ) { + case FE_D6_BUFSIZ_8KB: buf = 8; break; + case FE_D6_BUFSIZ_16KB: buf = 16; break; + case FE_D6_BUFSIZ_32KB: buf = 32; break; + case FE_D6_BUFSIZ_64KB: buf = 64; break; + } + switch ( sc->proto_dlcr6 & FE_D6_TXBSIZ ) { + case FE_D6_TXBSIZ_2x2KB: txb = 2; break; + case FE_D6_TXBSIZ_2x4KB: txb = 4; break; + case FE_D6_TXBSIZ_2x8KB: txb = 8; break; + } + switch ( sc->proto_dlcr6 & FE_D6_BBW ) { + case FE_D6_BBW_BYTE: bbw = 8; break; + case FE_D6_BBW_WORD: bbw = 16; break; + } + switch ( sc->proto_dlcr6 & FE_D6_SBW ) { + case FE_D6_SBW_BYTE: sbw = 8; break; + case FE_D6_SBW_WORD: sbw = 16; break; + } + switch ( sc->proto_dlcr6 & FE_D6_SRAM ) { + case FE_D6_SRAM_100ns: ram = 100; break; + case FE_D6_SRAM_150ns: ram = 150; break; + } + device_printf(dev, "SRAM %dKB %dbit %dns, TXB %dKBx2, %dbit I/O\n", + buf, bbw, ram, txb, sbw); + } + if (sc->stability & UNSTABLE_IRQ) + device_printf(dev, "warning: IRQ number may be incorrect\n"); + if (sc->stability & UNSTABLE_MAC) + device_printf(dev, "warning: above MAC address may be incorrect\n"); + if (sc->stability & UNSTABLE_TYPE) + device_printf(dev, "warning: hardware type was not validated\n"); + + return 0; +} + +int +fe_alloc_port(device_t dev, int size) +{ + struct fe_softc *sc = device_get_softc(dev); + struct resource *res; + int rid; + + rid = 0; + res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, + 0ul, ~0ul, size, RF_ACTIVE); + if (res) { + sc->port_used = size; + sc->port_res = res; + sc->iot = rman_get_bustag(res); + sc->ioh = rman_get_bushandle(res); + return (0); + } + + return (ENOENT); +} + +int +fe_alloc_irq(device_t dev, int flags) +{ + struct fe_softc *sc = device_get_softc(dev); + struct resource *res; + int rid; + + rid = 0; + res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | flags); + if (res) { + sc->irq_res = res; + return (0); + } + + return (ENOENT); +} + +void +fe_release_resource(device_t dev) +{ + struct fe_softc *sc = device_get_softc(dev); + + if (sc->port_res) { + bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->port_res); + sc->port_res = NULL; + } + if (sc->irq_res) { + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); + sc->irq_res = NULL; + } +} + +/* + * Reset interface, after some (hardware) trouble is deteced. + */ +static void +fe_reset (struct fe_softc *sc) +{ + /* Record how many packets are lost by this accident. */ + sc->ifp->if_oerrors += sc->txb_sched + sc->txb_count; + sc->mibdata.dot3StatsInternalMacTransmitErrors++; + + /* Put the interface into known initial state. */ + fe_stop(sc); + if (sc->ifp->if_flags & IFF_UP) + fe_init(sc); +} + +/* + * Stop everything on the interface. + * + * All buffered packets, both transmitting and receiving, + * if any, will be lost by stopping the interface. + */ +void +fe_stop (struct fe_softc *sc) +{ + int s; + + s = splimp(); + + /* Disable interrupts. */ + fe_outb(sc, FE_DLCR2, 0x00); + fe_outb(sc, FE_DLCR3, 0x00); + + /* Stop interface hardware. */ + DELAY(200); + fe_outb(sc, FE_DLCR6, sc->proto_dlcr6 | FE_D6_DLC_DISABLE); + DELAY(200); + + /* Clear all interrupt status. */ + fe_outb(sc, FE_DLCR0, 0xFF); + fe_outb(sc, FE_DLCR1, 0xFF); + + /* Put the chip in stand-by mode. */ + DELAY(200); + fe_outb(sc, FE_DLCR7, sc->proto_dlcr7 | FE_D7_POWER_DOWN); + DELAY(200); + + /* Reset transmitter variables and interface flags. */ + sc->ifp->if_drv_flags &= ~(IFF_DRV_OACTIVE | IFF_DRV_RUNNING); + sc->ifp->if_timer = 0; + sc->txb_free = sc->txb_size; + sc->txb_count = 0; + sc->txb_sched = 0; + + /* MAR loading can be delayed. */ + sc->filter_change = 0; + + /* Call a device-specific hook. */ + if (sc->stop) + sc->stop(sc); + + (void) splx(s); +} + +/* + * Device timeout/watchdog routine. Entered if the device neglects to + * generate an interrupt after a transmit has been started on it. + */ +static void +fe_watchdog ( struct ifnet *ifp ) +{ + struct fe_softc *sc = ifp->if_softc; + + /* A "debug" message. */ + if_printf(ifp, "transmission timeout (%d+%d)%s\n", + sc->txb_sched, sc->txb_count, + (ifp->if_flags & IFF_UP) ? "" : " when down"); + if (sc->ifp->if_opackets == 0 && sc->ifp->if_ipackets == 0) + if_printf(ifp, "wrong IRQ setting in config?\n"); + fe_reset(sc); +} + +/* + * Initialize device. + */ +static void +fe_init (void * xsc) +{ + struct fe_softc *sc = xsc; + int s; + + /* Start initializing 86960. */ + s = splimp(); + + /* Call a hook before we start initializing the chip. */ + if (sc->init) + sc->init(sc); + + /* + * Make sure to disable the chip, also. + * This may also help re-programming the chip after + * hot insertion of PCMCIAs. + */ + DELAY(200); + fe_outb(sc, FE_DLCR6, sc->proto_dlcr6 | FE_D6_DLC_DISABLE); + DELAY(200); + + /* Power up the chip and select register bank for DLCRs. */ + DELAY(200); + fe_outb(sc, FE_DLCR7, + sc->proto_dlcr7 | FE_D7_RBS_DLCR | FE_D7_POWER_UP); + DELAY(200); + + /* Feed the station address. */ + fe_outblk(sc, FE_DLCR8, IFP2ENADDR(sc->ifp), ETHER_ADDR_LEN); + + /* Clear multicast address filter to receive nothing. */ + fe_outb(sc, FE_DLCR7, + sc->proto_dlcr7 | FE_D7_RBS_MAR | FE_D7_POWER_UP); + fe_outblk(sc, FE_MAR8, fe_filter_nothing.data, FE_FILTER_LEN); + + /* Select the BMPR bank for runtime register access. */ + fe_outb(sc, FE_DLCR7, + sc->proto_dlcr7 | FE_D7_RBS_BMPR | FE_D7_POWER_UP); + + /* Initialize registers. */ + fe_outb(sc, FE_DLCR0, 0xFF); /* Clear all bits. */ + fe_outb(sc, FE_DLCR1, 0xFF); /* ditto. */ + fe_outb(sc, FE_DLCR2, 0x00); + fe_outb(sc, FE_DLCR3, 0x00); + fe_outb(sc, FE_DLCR4, sc->proto_dlcr4); + fe_outb(sc, FE_DLCR5, sc->proto_dlcr5); + fe_outb(sc, FE_BMPR10, 0x00); + fe_outb(sc, FE_BMPR11, FE_B11_CTRL_SKIP | FE_B11_MODE1); + fe_outb(sc, FE_BMPR12, 0x00); + fe_outb(sc, FE_BMPR13, sc->proto_bmpr13); + fe_outb(sc, FE_BMPR14, 0x00); + fe_outb(sc, FE_BMPR15, 0x00); + + /* Enable interrupts. */ + fe_outb(sc, FE_DLCR2, FE_TMASK); + fe_outb(sc, FE_DLCR3, FE_RMASK); + + /* Select requested media, just before enabling DLC. */ + if (sc->msel) + sc->msel(sc); + + /* Enable transmitter and receiver. */ + DELAY(200); + fe_outb(sc, FE_DLCR6, sc->proto_dlcr6 | FE_D6_DLC_ENABLE); + DELAY(200); + +#ifdef DIAGNOSTIC + /* + * Make sure to empty the receive buffer. + * + * This may be redundant, but *if* the receive buffer were full + * at this point, then the driver would hang. I have experienced + * some strange hang-up just after UP. I hope the following + * code solve the problem. + * + * I have changed the order of hardware initialization. + * I think the receive buffer cannot have any packets at this + * point in this version. The following code *must* be + * redundant now. FIXME. + * + * I've heard a rumore that on some PC card implementation of + * 8696x, the receive buffer can have some data at this point. + * The following message helps discovering the fact. FIXME. + */ + if (!(fe_inb(sc, FE_DLCR5) & FE_D5_BUFEMP)) { + if_printf(sc->ifp, + "receive buffer has some data after reset\n"); + fe_emptybuffer(sc); + } + + /* Do we need this here? Actually, no. I must be paranoia. */ + fe_outb(sc, FE_DLCR0, 0xFF); /* Clear all bits. */ + fe_outb(sc, FE_DLCR1, 0xFF); /* ditto. */ +#endif + + /* Set 'running' flag, because we are now running. */ + sc->ifp->if_drv_flags |= IFF_DRV_RUNNING; + + /* + * At this point, the interface is running properly, + * except that it receives *no* packets. we then call + * fe_setmode() to tell the chip what packets to be + * received, based on the if_flags and multicast group + * list. It completes the initialization process. + */ + fe_setmode(sc); + +#if 0 + /* ...and attempt to start output queued packets. */ + /* TURNED OFF, because the semi-auto media prober wants to UP + the interface keeping it idle. The upper layer will soon + start the interface anyway, and there are no significant + delay. */ + fe_start(sc->ifp); +#endif + + (void) splx(s); +} + +/* + * This routine actually starts the transmission on the interface + */ +static void +fe_xmit (struct fe_softc *sc) +{ + /* + * Set a timer just in case we never hear from the board again. + * We use longer timeout for multiple packet transmission. + * I'm not sure this timer value is appropriate. FIXME. + */ + sc->ifp->if_timer = 1 + sc->txb_count; + + /* Update txb variables. */ + sc->txb_sched = sc->txb_count; + sc->txb_count = 0; + sc->txb_free = sc->txb_size; + sc->tx_excolls = 0; + + /* Start transmitter, passing packets in TX buffer. */ + fe_outb(sc, FE_BMPR10, sc->txb_sched | FE_B10_START); +} + +/* + * Start output on interface. + * We make two assumptions here: + * 1) that the current priority is set to splimp _before_ this code + * is called *and* is returned to the appropriate priority after + * return + * 2) that the IFF_DRV_OACTIVE flag is checked before this code is called + * (i.e. that the output part of the interface is idle) + */ +static void +fe_start (struct ifnet *ifp) +{ + struct fe_softc *sc = ifp->if_softc; + struct mbuf *m; + +#ifdef DIAGNOSTIC + /* Just a sanity check. */ + if ((sc->txb_count == 0) != (sc->txb_free == sc->txb_size)) { + /* + * Txb_count and txb_free co-works to manage the + * transmission buffer. Txb_count keeps track of the + * used potion of the buffer, while txb_free does unused + * potion. So, as long as the driver runs properly, + * txb_count is zero if and only if txb_free is same + * as txb_size (which represents whole buffer.) + */ + if_printf(ifp, "inconsistent txb variables (%d, %d)\n", + sc->txb_count, sc->txb_free); + /* + * So, what should I do, then? + * + * We now know txb_count and txb_free contradicts. We + * cannot, however, tell which is wrong. More + * over, we cannot peek 86960 transmission buffer or + * reset the transmission buffer. (In fact, we can + * reset the entire interface. I don't want to do it.) + * + * If txb_count is incorrect, leaving it as-is will cause + * sending of garbage after next interrupt. We have to + * avoid it. Hence, we reset the txb_count here. If + * txb_free was incorrect, resetting txb_count just loose + * some packets. We can live with it. + */ + sc->txb_count = 0; + } +#endif + + /* + * First, see if there are buffered packets and an idle + * transmitter - should never happen at this point. + */ + if ((sc->txb_count > 0) && (sc->txb_sched == 0)) { + if_printf(ifp, "transmitter idle with %d buffered packets\n", + sc->txb_count); + fe_xmit(sc); + } + + /* + * Stop accepting more transmission packets temporarily, when + * a filter change request is delayed. Updating the MARs on + * 86960 flushes the transmission buffer, so it is delayed + * until all buffered transmission packets have been sent + * out. + */ + if (sc->filter_change) { + /* + * Filter change request is delayed only when the DLC is + * working. DLC soon raise an interrupt after finishing + * the work. + */ + goto indicate_active; + } + + for (;;) { + + /* + * See if there is room to put another packet in the buffer. + * We *could* do better job by peeking the send queue to + * know the length of the next packet. Current version just + * tests against the worst case (i.e., longest packet). FIXME. + * + * When adding the packet-peek feature, don't forget adding a + * test on txb_count against QUEUEING_MAX. + * There is a little chance the packet count exceeds + * the limit. Assume transmission buffer is 8KB (2x8KB + * configuration) and an application sends a bunch of small + * (i.e., minimum packet sized) packets rapidly. An 8KB + * buffer can hold 130 blocks of 62 bytes long... + */ + if (sc->txb_free + < ETHER_MAX_LEN - ETHER_CRC_LEN + FE_DATA_LEN_LEN) { + /* No room. */ + goto indicate_active; + } + +#if FE_SINGLE_TRANSMISSION + if (sc->txb_count > 0) { + /* Just one packet per a transmission buffer. */ + goto indicate_active; + } +#endif + + /* + * Get the next mbuf chain for a packet to send. + */ + IF_DEQUEUE(&sc->ifp->if_snd, m); + if (m == NULL) { + /* No more packets to send. */ + goto indicate_inactive; + } + + /* + * Copy the mbuf chain into the transmission buffer. + * txb_* variables are updated as necessary. + */ + fe_write_mbufs(sc, m); + + /* Start transmitter if it's idle. */ + if ((sc->txb_count > 0) && (sc->txb_sched == 0)) + fe_xmit(sc); + + /* + * Tap off here if there is a bpf listener, + * and the device is *not* in promiscuous mode. + * (86960 receives self-generated packets if + * and only if it is in "receive everything" + * mode.) + */ + if (!(sc->ifp->if_flags & IFF_PROMISC)) + BPF_MTAP(sc->ifp, m); + + m_freem(m); + } + + indicate_inactive: + /* + * We are using the !OACTIVE flag to indicate to + * the outside world that we can accept an + * additional packet rather than that the + * transmitter is _actually_ active. Indeed, the + * transmitter may be active, but if we haven't + * filled all the buffers with data then we still + * want to accept more. + */ + sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + return; + + indicate_active: + /* + * The transmitter is active, and there are no room for + * more outgoing packets in the transmission buffer. + */ + sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE; + return; +} + +/* + * Drop (skip) a packet from receive buffer in 86960 memory. + */ +static void +fe_droppacket (struct fe_softc * sc, int len) +{ + int i; + + /* + * 86960 manual says that we have to read 8 bytes from the buffer + * before skip the packets and that there must be more than 8 bytes + * remaining in the buffer when issue a skip command. + * Remember, we have already read 4 bytes before come here. + */ + if (len > 12) { + /* Read 4 more bytes, and skip the rest of the packet. */ + if ((sc->proto_dlcr6 & FE_D6_SBW) == FE_D6_SBW_BYTE) + { + (void) fe_inb(sc, FE_BMPR8); + (void) fe_inb(sc, FE_BMPR8); + (void) fe_inb(sc, FE_BMPR8); + (void) fe_inb(sc, FE_BMPR8); + } + else + { + (void) fe_inw(sc, FE_BMPR8); + (void) fe_inw(sc, FE_BMPR8); + } + fe_outb(sc, FE_BMPR14, FE_B14_SKIP); + } else { + /* We should not come here unless receiving RUNTs. */ + if ((sc->proto_dlcr6 & FE_D6_SBW) == FE_D6_SBW_BYTE) + { + for (i = 0; i < len; i++) + (void) fe_inb(sc, FE_BMPR8); + } + else + { + for (i = 0; i < len; i += 2) + (void) fe_inw(sc, FE_BMPR8); + } + } +} + +#ifdef DIAGNOSTIC +/* + * Empty receiving buffer. + */ +static void +fe_emptybuffer (struct fe_softc * sc) +{ + int i; + u_char saved_dlcr5; + +#ifdef FE_DEBUG + if_printf(sc->ifp, "emptying receive buffer\n"); +#endif + + /* + * Stop receiving packets, temporarily. + */ + saved_dlcr5 = fe_inb(sc, FE_DLCR5); + fe_outb(sc, FE_DLCR5, sc->proto_dlcr5); + DELAY(1300); + + /* + * When we come here, the receive buffer management may + * have been broken. So, we cannot use skip operation. + * Just discard everything in the buffer. + */ + if ((sc->proto_dlcr6 & FE_D6_SBW) == FE_D6_SBW_BYTE) + { + for (i = 0; i < 65536; i++) { + if (fe_inb(sc, FE_DLCR5) & FE_D5_BUFEMP) + break; + (void) fe_inb(sc, FE_BMPR8); + } + } + else + { + for (i = 0; i < 65536; i += 2) { + if (fe_inb(sc, FE_DLCR5) & FE_D5_BUFEMP) + break; + (void) fe_inw(sc, FE_BMPR8); + } + } + + /* + * Double check. + */ + if (fe_inb(sc, FE_DLCR5) & FE_D5_BUFEMP) { + if_printf(sc->ifp, + "could not empty receive buffer\n"); + /* Hmm. What should I do if this happens? FIXME. */ + } + + /* + * Restart receiving packets. + */ + fe_outb(sc, FE_DLCR5, saved_dlcr5); +} +#endif + +/* + * Transmission interrupt handler + * The control flow of this function looks silly. FIXME. + */ +static void +fe_tint (struct fe_softc * sc, u_char tstat) +{ + int left; + int col; + + /* + * Handle "excessive collision" interrupt. + */ + if (tstat & FE_D0_COLL16) { + + /* + * Find how many packets (including this collided one) + * are left unsent in transmission buffer. + */ + left = fe_inb(sc, FE_BMPR10); + if_printf(sc->ifp, "excessive collision (%d/%d)\n", + left, sc->txb_sched); + + /* + * Clear the collision flag (in 86960) here + * to avoid confusing statistics. + */ + fe_outb(sc, FE_DLCR0, FE_D0_COLLID); + + /* + * Restart transmitter, skipping the + * collided packet. + * + * We *must* skip the packet to keep network running + * properly. Excessive collision error is an + * indication of the network overload. If we + * tried sending the same packet after excessive + * collision, the network would be filled with + * out-of-time packets. Packets belonging + * to reliable transport (such as TCP) are resent + * by some upper layer. + */ + fe_outb(sc, FE_BMPR11, FE_B11_CTRL_SKIP | FE_B11_MODE1); + + /* Update statistics. */ + sc->tx_excolls++; + } + + /* + * Handle "transmission complete" interrupt. + */ + if (tstat & FE_D0_TXDONE) { + + /* + * Add in total number of collisions on last + * transmission. We also clear "collision occurred" flag + * here. + * + * 86960 has a design flaw on collision count on multiple + * packet transmission. When we send two or more packets + * with one start command (that's what we do when the + * transmission queue is crowded), 86960 informs us number + * of collisions occurred on the last packet on the + * transmission only. Number of collisions on previous + * packets are lost. I have told that the fact is clearly + * stated in the Fujitsu document. + * + * I considered not to mind it seriously. Collision + * count is not so important, anyway. Any comments? FIXME. + */ + + if (fe_inb(sc, FE_DLCR0) & FE_D0_COLLID) { + + /* Clear collision flag. */ + fe_outb(sc, FE_DLCR0, FE_D0_COLLID); + + /* Extract collision count from 86960. */ + col = fe_inb(sc, FE_DLCR4); + col = (col & FE_D4_COL) >> FE_D4_COL_SHIFT; + if (col == 0) { + /* + * Status register indicates collisions, + * while the collision count is zero. + * This can happen after multiple packet + * transmission, indicating that one or more + * previous packet(s) had been collided. + * + * Since the accurate number of collisions + * has been lost, we just guess it as 1; + * Am I too optimistic? FIXME. + */ + col = 1; + } + sc->ifp->if_collisions += col; + if (col == 1) + sc->mibdata.dot3StatsSingleCollisionFrames++; + else + sc->mibdata.dot3StatsMultipleCollisionFrames++; + sc->mibdata.dot3StatsCollFrequencies[col-1]++; + } + + /* + * Update transmission statistics. + * Be sure to reflect number of excessive collisions. + */ + col = sc->tx_excolls; + sc->ifp->if_opackets += sc->txb_sched - col; + sc->ifp->if_oerrors += col; + sc->ifp->if_collisions += col * 16; + sc->mibdata.dot3StatsExcessiveCollisions += col; + sc->mibdata.dot3StatsCollFrequencies[15] += col; + sc->txb_sched = 0; + + /* + * The transmitter is no more active. + * Reset output active flag and watchdog timer. + */ + sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + sc->ifp->if_timer = 0; + + /* + * If more data is ready to transmit in the buffer, start + * transmitting them. Otherwise keep transmitter idle, + * even if more data is queued. This gives receive + * process a slight priority. + */ + if (sc->txb_count > 0) + fe_xmit(sc); + } +} + +/* + * Ethernet interface receiver interrupt. + */ +static void +fe_rint (struct fe_softc * sc, u_char rstat) +{ + u_short len; + u_char status; + int i; + + /* + * Update statistics if this interrupt is caused by an error. + * Note that, when the system was not sufficiently fast, the + * receive interrupt might not be acknowledged immediately. If + * one or more errornous frames were received before this routine + * was scheduled, they are ignored, and the following error stats + * give less than real values. + */ + if (rstat & (FE_D1_OVRFLO | FE_D1_CRCERR | FE_D1_ALGERR | FE_D1_SRTPKT)) { + if (rstat & FE_D1_OVRFLO) + sc->mibdata.dot3StatsInternalMacReceiveErrors++; + if (rstat & FE_D1_CRCERR) + sc->mibdata.dot3StatsFCSErrors++; + if (rstat & FE_D1_ALGERR) + sc->mibdata.dot3StatsAlignmentErrors++; +#if 0 + /* The reference MAC receiver defined in 802.3 + silently ignores short frames (RUNTs) without + notifying upper layer. RFC 1650 (dot3 MIB) is + based on the 802.3, and it has no stats entry for + RUNTs... */ + if (rstat & FE_D1_SRTPKT) + sc->mibdata.dot3StatsFrameTooShorts++; /* :-) */ +#endif + sc->ifp->if_ierrors++; + } + + /* + * MB86960 has a flag indicating "receive queue empty." + * We just loop, checking the flag, to pull out all received + * packets. + * + * We limit the number of iterations to avoid infinite-loop. + * The upper bound is set to unrealistic high value. + */ + for (i = 0; i < FE_MAX_RECV_COUNT * 2; i++) { + + /* Stop the iteration if 86960 indicates no packets. */ + if (fe_inb(sc, FE_DLCR5) & FE_D5_BUFEMP) + return; + + /* + * Extract a receive status byte. + * As our 86960 is in 16 bit bus access mode, we have to + * use inw() to get the status byte. The significant + * value is returned in lower 8 bits. + */ + if ((sc->proto_dlcr6 & FE_D6_SBW) == FE_D6_SBW_BYTE) + { + status = fe_inb(sc, FE_BMPR8); + (void) fe_inb(sc, FE_BMPR8); + } + else + { + status = (u_char) fe_inw(sc, FE_BMPR8); + } + + /* + * Extract the packet length. + * It is a sum of a header (14 bytes) and a payload. + * CRC has been stripped off by the 86960. + */ + if ((sc->proto_dlcr6 & FE_D6_SBW) == FE_D6_SBW_BYTE) + { + len = fe_inb(sc, FE_BMPR8); + len |= (fe_inb(sc, FE_BMPR8) << 8); + } + else + { + len = fe_inw(sc, FE_BMPR8); + } + + /* + * AS our 86960 is programed to ignore errored frame, + * we must not see any error indication in the + * receive buffer. So, any error condition is a + * serious error, e.g., out-of-sync of the receive + * buffer pointers. + */ + if ((status & 0xF0) != 0x20 || + len > ETHER_MAX_LEN - ETHER_CRC_LEN || + len < ETHER_MIN_LEN - ETHER_CRC_LEN) { + if_printf(sc->ifp, + "RX buffer out-of-sync\n"); + sc->ifp->if_ierrors++; + sc->mibdata.dot3StatsInternalMacReceiveErrors++; + fe_reset(sc); + return; + } + + /* + * Go get a packet. + */ + if (fe_get_packet(sc, len) < 0) { + /* + * Negative return from fe_get_packet() + * indicates no available mbuf. We stop + * receiving packets, even if there are more + * in the buffer. We hope we can get more + * mbuf next time. + */ + sc->ifp->if_ierrors++; + sc->mibdata.dot3StatsMissedFrames++; + fe_droppacket(sc, len); + return; + } + + /* Successfully received a packet. Update stat. */ + sc->ifp->if_ipackets++; + } + + /* Maximum number of frames has been received. Something + strange is happening here... */ + if_printf(sc->ifp, "unusual receive flood\n"); + sc->mibdata.dot3StatsInternalMacReceiveErrors++; + fe_reset(sc); +} + +/* + * Ethernet interface interrupt processor + */ +static void +fe_intr (void *arg) +{ + struct fe_softc *sc = arg; + u_char tstat, rstat; + int loop_count = FE_MAX_LOOP; + + /* Loop until there are no more new interrupt conditions. */ + while (loop_count-- > 0) { + /* + * Get interrupt conditions, masking unneeded flags. + */ + tstat = fe_inb(sc, FE_DLCR0) & FE_TMASK; + rstat = fe_inb(sc, FE_DLCR1) & FE_RMASK; + if (tstat == 0 && rstat == 0) + return; + + /* + * Reset the conditions we are acknowledging. + */ + fe_outb(sc, FE_DLCR0, tstat); + fe_outb(sc, FE_DLCR1, rstat); + + /* + * Handle transmitter interrupts. + */ + if (tstat) + fe_tint(sc, tstat); + + /* + * Handle receiver interrupts + */ + if (rstat) + fe_rint(sc, rstat); + + /* + * Update the multicast address filter if it is + * needed and possible. We do it now, because + * we can make sure the transmission buffer is empty, + * and there is a good chance that the receive queue + * is empty. It will minimize the possibility of + * packet loss. + */ + if (sc->filter_change && + sc->txb_count == 0 && sc->txb_sched == 0) { + fe_loadmar(sc); + sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + } + + /* + * If it looks like the transmitter can take more data, + * attempt to start output on the interface. This is done + * after handling the receiver interrupt to give the + * receive operation priority. + * + * BTW, I'm not sure in what case the OACTIVE is on at + * this point. Is the following test redundant? + * + * No. This routine polls for both transmitter and + * receiver interrupts. 86960 can raise a receiver + * interrupt when the transmission buffer is full. + */ + if ((sc->ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) + fe_start(sc->ifp); + } + + if_printf(sc->ifp, "too many loops\n"); +} + +/* + * Process an ioctl request. This code needs some work - it looks + * pretty ugly. + */ +static int +fe_ioctl (struct ifnet * ifp, u_long command, caddr_t data) +{ + struct fe_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *)data; + int s, error = 0; + + s = splimp(); + + switch (command) { + + case SIOCSIFFLAGS: + /* + * Switch interface state between "running" and + * "stopped", reflecting the UP flag. + */ + if (sc->ifp->if_flags & IFF_UP) { + if ((sc->ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + fe_init(sc); + } else { + if ((sc->ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) + fe_stop(sc); + } + + /* + * Promiscuous and/or multicast flags may have changed, + * so reprogram the multicast filter and/or receive mode. + */ + fe_setmode(sc); + + /* Done. */ + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + /* + * Multicast list has changed; set the hardware filter + * accordingly. + */ + fe_setmode(sc); + break; + + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + /* Let if_media to handle these commands and to call + us back. */ + error = ifmedia_ioctl(ifp, ifr, &sc->media, command); + break; + + default: + error = ether_ioctl(ifp, command, data); + break; + } + + (void) splx(s); + return (error); +} + +/* + * Retrieve packet from receive buffer and send to the next level up via + * ether_input(). + * Returns 0 if success, -1 if error (i.e., mbuf allocation failure). + */ +static int +fe_get_packet (struct fe_softc * sc, u_short len) +{ + struct ifnet *ifp = sc->ifp; + struct ether_header *eh; + struct mbuf *m; + + /* + * NFS wants the data be aligned to the word (4 byte) + * boundary. Ethernet header has 14 bytes. There is a + * 2-byte gap. + */ +#define NFS_MAGIC_OFFSET 2 + + /* + * This function assumes that an Ethernet packet fits in an + * mbuf (with a cluster attached when necessary.) On FreeBSD + * 2.0 for x86, which is the primary target of this driver, an + * mbuf cluster has 4096 bytes, and we are happy. On ancient + * BSDs, such as vanilla 4.3 for 386, a cluster size was 1024, + * however. If the following #error message were printed upon + * compile, you need to rewrite this function. + */ +#if ( MCLBYTES < ETHER_MAX_LEN - ETHER_CRC_LEN + NFS_MAGIC_OFFSET ) +#error "Too small MCLBYTES to use fe driver." +#endif + + /* + * Our strategy has one more problem. There is a policy on + * mbuf cluster allocation. It says that we must have at + * least MINCLSIZE (208 bytes on FreeBSD 2.0 for x86) to + * allocate a cluster. For a packet of a size between + * (MHLEN - 2) to (MINCLSIZE - 2), our code violates the rule... + * On the other hand, the current code is short, simple, + * and fast, however. It does no harmful thing, just waists + * some memory. Any comments? FIXME. + */ + + /* Allocate an mbuf with packet header info. */ + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return -1; + + /* Attach a cluster if this packet doesn't fit in a normal mbuf. */ + if (len > MHLEN - NFS_MAGIC_OFFSET) { + MCLGET(m, M_DONTWAIT); + if (!(m->m_flags & M_EXT)) { + m_freem(m); + return -1; + } + } + + /* Initialize packet header info. */ + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = len; + + /* Set the length of this packet. */ + m->m_len = len; + + /* The following silliness is to make NFS happy */ + m->m_data += NFS_MAGIC_OFFSET; + + /* Get (actually just point to) the header part. */ + eh = mtod(m, struct ether_header *); + + /* Get a packet. */ + if ((sc->proto_dlcr6 & FE_D6_SBW) == FE_D6_SBW_BYTE) + { + fe_insb(sc, FE_BMPR8, (u_int8_t *)eh, len); + } + else + { + fe_insw(sc, FE_BMPR8, (u_int16_t *)eh, (len + 1) >> 1); + } + + /* Feed the packet to upper layer. */ + (*ifp->if_input)(ifp, m); + return 0; +} + +/* + * Write an mbuf chain to the transmission buffer memory using 16 bit PIO. + * Returns number of bytes actually written, including length word. + * + * If an mbuf chain is too long for an Ethernet frame, it is not sent. + * Packets shorter than Ethernet minimum are legal, and we pad them + * before sending out. An exception is "partial" packets which are + * shorter than mandatory Ethernet header. + */ +static void +fe_write_mbufs (struct fe_softc *sc, struct mbuf *m) +{ + u_short length, len; + struct mbuf *mp; + u_char *data; + u_short savebyte; /* WARNING: Architecture dependent! */ +#define NO_PENDING_BYTE 0xFFFF + + static u_char padding [ETHER_MIN_LEN - ETHER_CRC_LEN - ETHER_HDR_LEN]; + +#ifdef DIAGNOSTIC + /* First, count up the total number of bytes to copy */ + length = 0; + for (mp = m; mp != NULL; mp = mp->m_next) + length += mp->m_len; + + /* Check if this matches the one in the packet header. */ + if (length != m->m_pkthdr.len) { + if_printf(sc->ifp, + "packet length mismatch? (%d/%d)\n", + length, m->m_pkthdr.len); + } +#else + /* Just use the length value in the packet header. */ + length = m->m_pkthdr.len; +#endif + +#ifdef DIAGNOSTIC + /* + * Should never send big packets. If such a packet is passed, + * it should be a bug of upper layer. We just ignore it. + * ... Partial (too short) packets, neither. + */ + if (length < ETHER_HDR_LEN || + length > ETHER_MAX_LEN - ETHER_CRC_LEN) { + if_printf(sc->ifp, + "got an out-of-spec packet (%u bytes) to send\n", length); + sc->ifp->if_oerrors++; + sc->mibdata.dot3StatsInternalMacTransmitErrors++; + return; + } +#endif + + /* + * Put the length word for this frame. + * Does 86960 accept odd length? -- Yes. + * Do we need to pad the length to minimum size by ourselves? + * -- Generally yes. But for (or will be) the last + * packet in the transmission buffer, we can skip the + * padding process. It may gain performance slightly. FIXME. + */ + if ((sc->proto_dlcr6 & FE_D6_SBW) == FE_D6_SBW_BYTE) + { + len = max(length, ETHER_MIN_LEN - ETHER_CRC_LEN); + fe_outb(sc, FE_BMPR8, len & 0x00ff); + fe_outb(sc, FE_BMPR8, (len & 0xff00) >> 8); + } + else + { + fe_outw(sc, FE_BMPR8, + max(length, ETHER_MIN_LEN - ETHER_CRC_LEN)); + } + + /* + * Update buffer status now. + * Truncate the length up to an even number, since we use outw(). + */ + if ((sc->proto_dlcr6 & FE_D6_SBW) != FE_D6_SBW_BYTE) + { + length = (length + 1) & ~1; + } + sc->txb_free -= FE_DATA_LEN_LEN + + max(length, ETHER_MIN_LEN - ETHER_CRC_LEN); + sc->txb_count++; + + /* + * Transfer the data from mbuf chain to the transmission buffer. + * MB86960 seems to require that data be transferred as words, and + * only words. So that we require some extra code to patch + * over odd-length mbufs. + */ + if ((sc->proto_dlcr6 & FE_D6_SBW) == FE_D6_SBW_BYTE) + { + /* 8-bit cards are easy. */ + for (mp = m; mp != 0; mp = mp->m_next) { + if (mp->m_len) + fe_outsb(sc, FE_BMPR8, mtod(mp, caddr_t), + mp->m_len); + } + } + else + { + /* 16-bit cards are a pain. */ + savebyte = NO_PENDING_BYTE; + for (mp = m; mp != 0; mp = mp->m_next) { + + /* Ignore empty mbuf. */ + len = mp->m_len; + if (len == 0) + continue; + + /* Find the actual data to send. */ + data = mtod(mp, caddr_t); + + /* Finish the last byte. */ + if (savebyte != NO_PENDING_BYTE) { + fe_outw(sc, FE_BMPR8, savebyte | (*data << 8)); + data++; + len--; + savebyte = NO_PENDING_BYTE; + } + + /* output contiguous words */ + if (len > 1) { + fe_outsw(sc, FE_BMPR8, (u_int16_t *)data, + len >> 1); + data += len & ~1; + len &= 1; + } + + /* Save a remaining byte, if there is one. */ + if (len > 0) + savebyte = *data; + } + + /* Spit the last byte, if the length is odd. */ + if (savebyte != NO_PENDING_BYTE) + fe_outw(sc, FE_BMPR8, savebyte); + } + + /* Pad to the Ethernet minimum length, if the packet is too short. */ + if (length < ETHER_MIN_LEN - ETHER_CRC_LEN) { + if ((sc->proto_dlcr6 & FE_D6_SBW) == FE_D6_SBW_BYTE) + { + fe_outsb(sc, FE_BMPR8, padding, + ETHER_MIN_LEN - ETHER_CRC_LEN - length); + } + else + { + fe_outsw(sc, FE_BMPR8, (u_int16_t *)padding, + (ETHER_MIN_LEN - ETHER_CRC_LEN - length) >> 1); + } + } +} + +/* + * Compute the multicast address filter from the + * list of multicast addresses we need to listen to. + */ +static struct fe_filter +fe_mcaf ( struct fe_softc *sc ) +{ + int index; + struct fe_filter filter; + struct ifmultiaddr *ifma; + + filter = fe_filter_nothing; + IF_ADDR_LOCK(sc->ifp); + TAILQ_FOREACH(ifma, &sc->ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + index = ether_crc32_le(LLADDR((struct sockaddr_dl *) + ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; +#ifdef FE_DEBUG + if_printf(sc->ifp, "hash(%6D) == %d\n", + enm->enm_addrlo , ":", index); +#endif + + filter.data[index >> 3] |= 1 << (index & 7); + } + IF_ADDR_UNLOCK(sc->ifp); + return ( filter ); +} + +/* + * Calculate a new "multicast packet filter" and put the 86960 + * receiver in appropriate mode. + */ +static void +fe_setmode (struct fe_softc *sc) +{ + + /* + * If the interface is not running, we postpone the update + * process for receive modes and multicast address filter + * until the interface is restarted. It reduces some + * complicated job on maintaining chip states. (Earlier versions + * of this driver had a bug on that point...) + * + * To complete the trick, fe_init() calls fe_setmode() after + * restarting the interface. + */ + if (!(sc->ifp->if_drv_flags & IFF_DRV_RUNNING)) + return; + + /* + * Promiscuous mode is handled separately. + */ + if (sc->ifp->if_flags & IFF_PROMISC) { + /* + * Program 86960 to receive all packets on the segment + * including those directed to other stations. + * Multicast filter stored in MARs are ignored + * under this setting, so we don't need to update it. + * + * Promiscuous mode in FreeBSD 2 is used solely by + * BPF, and BPF only listens to valid (no error) packets. + * So, we ignore erroneous ones even in this mode. + * (Older versions of fe driver mistook the point.) + */ + fe_outb(sc, FE_DLCR5, + sc->proto_dlcr5 | FE_D5_AFM0 | FE_D5_AFM1); + sc->filter_change = 0; + return; + } + + /* + * Turn the chip to the normal (non-promiscuous) mode. + */ + fe_outb(sc, FE_DLCR5, sc->proto_dlcr5 | FE_D5_AFM1); + + /* + * Find the new multicast filter value. + */ + if (sc->ifp->if_flags & IFF_ALLMULTI) + sc->filter = fe_filter_all; + else + sc->filter = fe_mcaf(sc); + sc->filter_change = 1; + + /* + * We have to update the multicast filter in the 86960, A.S.A.P. + * + * Note that the DLC (Data Link Control unit, i.e. transmitter + * and receiver) must be stopped when feeding the filter, and + * DLC trashes all packets in both transmission and receive + * buffers when stopped. + * + * To reduce the packet loss, we delay the filter update + * process until buffers are empty. + */ + if (sc->txb_sched == 0 && sc->txb_count == 0 && + !(fe_inb(sc, FE_DLCR1) & FE_D1_PKTRDY)) { + /* + * Buffers are (apparently) empty. Load + * the new filter value into MARs now. + */ + fe_loadmar(sc); + } else { + /* + * Buffers are not empty. Mark that we have to update + * the MARs. The new filter will be loaded by feintr() + * later. + */ + } +} + +/* + * Load a new multicast address filter into MARs. + * + * The caller must have splimp'ed before fe_loadmar. + * This function starts the DLC upon return. So it can be called only + * when the chip is working, i.e., from the driver's point of view, when + * a device is RUNNING. (I mistook the point in previous versions.) + */ +static void +fe_loadmar (struct fe_softc * sc) +{ + /* Stop the DLC (transmitter and receiver). */ + DELAY(200); + fe_outb(sc, FE_DLCR6, sc->proto_dlcr6 | FE_D6_DLC_DISABLE); + DELAY(200); + + /* Select register bank 1 for MARs. */ + fe_outb(sc, FE_DLCR7, sc->proto_dlcr7 | FE_D7_RBS_MAR | FE_D7_POWER_UP); + + /* Copy filter value into the registers. */ + fe_outblk(sc, FE_MAR8, sc->filter.data, FE_FILTER_LEN); + + /* Restore the bank selection for BMPRs (i.e., runtime registers). */ + fe_outb(sc, FE_DLCR7, + sc->proto_dlcr7 | FE_D7_RBS_BMPR | FE_D7_POWER_UP); + + /* Restart the DLC. */ + DELAY(200); + fe_outb(sc, FE_DLCR6, sc->proto_dlcr6 | FE_D6_DLC_ENABLE); + DELAY(200); + + /* We have just updated the filter. */ + sc->filter_change = 0; +} + +/* Change the media selection. */ +static int +fe_medchange (struct ifnet *ifp) +{ + struct fe_softc *sc = (struct fe_softc *)ifp->if_softc; + +#ifdef DIAGNOSTIC + /* If_media should not pass any request for a media which this + interface doesn't support. */ + int b; + + for (b = 0; bit2media[b] != 0; b++) { + if (bit2media[b] == sc->media.ifm_media) break; + } + if (((1 << b) & sc->mbitmap) == 0) { + if_printf(sc->ifp, + "got an unsupported media request (0x%x)\n", + sc->media.ifm_media); + return EINVAL; + } +#endif + + /* We don't actually change media when the interface is down. + fe_init() will do the job, instead. Should we also wait + until the transmission buffer being empty? Changing the + media when we are sending a frame will cause two garbages + on wires, one on old media and another on new. FIXME */ + if (sc->ifp->if_flags & IFF_UP) { + if (sc->msel) sc->msel(sc); + } + + return 0; +} + +/* I don't know how I can support media status callback... FIXME. */ +static void +fe_medstat (struct ifnet *ifp, struct ifmediareq *ifmr) +{ + (void)ifp; + (void)ifmr; +} diff --git a/sys/dev/fe/if_fe_cbus.c b/sys/dev/fe/if_fe_cbus.c new file mode 100644 index 000000000000..7076f42268ce --- /dev/null +++ b/sys/dev/fe/if_fe_cbus.c @@ -0,0 +1,1012 @@ +/*- + * All Rights Reserved, Copyright (C) Fujitsu Limited 1995 + * + * This software may be used, modified, copied, distributed, and sold, in + * both source and binary form provided that the above copyright, these + * terms and the following disclaimer are retained. The name of the author + * and/or the contributor may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND THE CONTRIBUTOR ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR THE CONTRIBUTOR BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION. + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/module.h> + +#include <sys/bus.h> +#include <machine/bus.h> +#include <sys/rman.h> +#include <machine/resource.h> + +#include <net/ethernet.h> +#include <net/if.h> +#include <net/if_mib.h> +#include <net/if_media.h> + +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#include <dev/fe/mb86960.h> +#include <dev/fe/if_fereg.h> +#include <dev/fe/if_fevar.h> + +#include <isa/isavar.h> + +/* + * Cbus specific code. + */ +static int fe_isa_probe(device_t); +static int fe_isa_attach(device_t); + +static struct isa_pnp_id fe_ids[] = { + { 0x101ee0d, NULL }, /* CON0101 - Contec C-NET(98)P2-T */ + { 0, NULL } +}; + +static device_method_t fe_isa_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, fe_isa_probe), + DEVMETHOD(device_attach, fe_isa_attach), + + { 0, 0 } +}; + +static driver_t fe_isa_driver = { + "fe", + fe_isa_methods, + sizeof (struct fe_softc) +}; + +DRIVER_MODULE(fe, isa, fe_isa_driver, fe_devclass, 0, 0); + + +static int fe98_alloc_port(device_t, int); + +static int fe_probe_re1000(device_t); +static int fe_probe_cnet9ne(device_t); +static int fe_probe_rex(device_t); +static int fe_probe_ssi(device_t); +static int fe_probe_jli(device_t); +static int fe_probe_lnx(device_t); +static int fe_probe_gwy(device_t); +static int fe_probe_ubn(device_t); + +/* + * Determine if the device is present at a specified I/O address. The + * main entry to the driver. + */ +static int +fe_isa_probe(device_t dev) +{ + struct fe_softc *sc; + int error; + + /* Prepare for the softc struct. */ + sc = device_get_softc(dev); + sc->sc_unit = device_get_unit(dev); + + /* Check isapnp ids */ + error = ISA_PNP_PROBE(device_get_parent(dev), dev, fe_ids); + + /* If the card had a PnP ID that didn't match any we know about */ + if (error == ENXIO) + goto end; + + /* If we had some other problem. */ + if (!(error == 0 || error == ENOENT)) + goto end; + + /* Probe for supported boards. */ + if ((error = fe_probe_re1000(dev)) == 0) + goto end; + fe_release_resource(dev); + + if ((error = fe_probe_cnet9ne(dev)) == 0) + goto end; + fe_release_resource(dev); + + if ((error = fe_probe_rex(dev)) == 0) + goto end; + fe_release_resource(dev); + + if ((error = fe_probe_ssi(dev)) == 0) + goto end; + fe_release_resource(dev); + + if ((error = fe_probe_jli(dev)) == 0) + goto end; + fe_release_resource(dev); + + if ((error = fe_probe_lnx(dev)) == 0) + goto end; + fe_release_resource(dev); + + if ((error = fe_probe_ubn(dev)) == 0) + goto end; + fe_release_resource(dev); + + if ((error = fe_probe_gwy(dev)) == 0) + goto end; + fe_release_resource(dev); + +end: + if (error == 0) + error = fe_alloc_irq(dev, 0); + + fe_release_resource(dev); + return (error); +} + +static int +fe_isa_attach(device_t dev) +{ + struct fe_softc *sc = device_get_softc(dev); + + if (sc->port_used) + fe98_alloc_port(dev, sc->type); + fe_alloc_irq(dev, 0); + + return fe_attach(dev); +} + + +/* Generic I/O address table */ +static bus_addr_t ioaddr_generic[MAXREGISTERS] = { + 0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007, + 0x008, 0x009, 0x00a, 0x00b, 0x00c, 0x00d, 0x00e, 0x00f, + 0x010, 0x011, 0x012, 0x013, 0x014, 0x015, 0x016, 0x017, + 0x018, 0x019, 0x01a, 0x01b, 0x01c, 0x01d, 0x01e, 0x01f, +}; + +/* I/O address table for RE1000/1000Plus */ +static bus_addr_t ioaddr_re1000[MAXREGISTERS] = { + 0x0000, 0x0001, 0x0200, 0x0201, 0x0400, 0x0401, 0x0600, 0x0601, + 0x0800, 0x0801, 0x0a00, 0x0a01, 0x0c00, 0x0c01, 0x0e00, 0x0e01, + 0x1000, 0x1200, 0x1400, 0x1600, 0x1800, 0x1a00, 0x1c00, 0x1e00, + 0x1001, 0x1201, 0x1401, 0x1601, 0x1801, 0x1a01, 0x1c01, 0x1e01, +}; + +/* I/O address table for CNET9NE */ +static bus_addr_t ioaddr_cnet9ne[MAXREGISTERS] = { + 0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007, + 0x008, 0x009, 0x00a, 0x00b, 0x00c, 0x00d, 0x00e, 0x00f, + 0x400, 0x402, 0x404, 0x406, 0x408, 0x40a, 0x40c, 0x40e, + 0x401, 0x403, 0x405, 0x407, 0x409, 0x40b, 0x40d, 0x40f, +}; + +/* I/O address table for LAC-98 */ +static bus_addr_t ioaddr_lnx[MAXREGISTERS] = { + 0x000, 0x002, 0x004, 0x006, 0x008, 0x00a, 0x00c, 0x00e, + 0x100, 0x102, 0x104, 0x106, 0x108, 0x10a, 0x10c, 0x10e, + 0x200, 0x202, 0x204, 0x206, 0x208, 0x20a, 0x20c, 0x20e, + 0x300, 0x302, 0x304, 0x306, 0x308, 0x30a, 0x30c, 0x30e, +}; + +/* I/O address table for Access/PC N98C+ */ +static bus_addr_t ioaddr_ubn[MAXREGISTERS] = { + 0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007, + 0x008, 0x009, 0x00a, 0x00b, 0x00c, 0x00d, 0x00e, 0x00f, + 0x200, 0x201, 0x202, 0x203, 0x204, 0x205, 0x206, 0x207, + 0x208, 0x209, 0x20a, 0x20b, 0x20c, 0x20d, 0x20e, 0x20f, +}; + +/* I/O address table for REX-9880 */ +static bus_addr_t ioaddr_rex[MAXREGISTERS] = { + 0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007, + 0x008, 0x009, 0x00a, 0x00b, 0x00c, 0x00d, 0x00e, 0x00f, + 0x100, 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107, + 0x108, 0x109, 0x10a, 0x10b, 0x10c, 0x10d, 0x10e, 0x10f, +}; + +static int +fe98_alloc_port(device_t dev, int type) +{ + struct fe_softc *sc = device_get_softc(dev); + struct resource *res; + bus_addr_t *iat; + int size, rid; + + switch (type) { + case FE_TYPE_RE1000: + iat = ioaddr_re1000; + size = MAXREGISTERS; + break; + case FE_TYPE_CNET9NE: + iat = ioaddr_cnet9ne; + size = MAXREGISTERS; + break; + case FE_TYPE_SSI: + iat = ioaddr_generic; + size = MAXREGISTERS; + break; + case FE_TYPE_LNX: + iat = ioaddr_lnx; + size = MAXREGISTERS; + break; + case FE_TYPE_GWY: + iat = ioaddr_generic; + size = MAXREGISTERS; + break; + case FE_TYPE_UBN: + iat = ioaddr_ubn; + size = MAXREGISTERS; + break; + case FE_TYPE_REX: + iat = ioaddr_rex; + size = MAXREGISTERS; + break; + default: + iat = ioaddr_generic; + size = MAXREGISTERS; + break; + } + + rid = 0; + res = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid, + iat, size, RF_ACTIVE); + if (res == NULL) + return ENOENT; + + isa_load_resourcev(res, iat, size); + + sc->type = type; + sc->port_used = size; + sc->port_res = res; + sc->iot = rman_get_bustag(res); + sc->ioh = rman_get_bushandle(res); + return (0); +} + + +/* + * Probe and initialization for Allied-Telesis RE1000 series. + */ +static void +fe_init_re1000(struct fe_softc *sc) +{ + /* Setup IRQ control register on the ASIC. */ + fe_outb(sc, FE_RE1000_IRQCONF, sc->priv_info); +} + +static int +fe_probe_re1000(device_t dev) +{ + struct fe_softc *sc = device_get_softc(dev); + int i, n; + u_long iobase, irq; + u_char sum; + + static struct fe_simple_probe_struct probe_table [] = { + { FE_DLCR2, 0x58, 0x00 }, + { FE_DLCR4, 0x08, 0x00 }, + { 0 } + }; + + /* See if the specified I/O address is possible for RE1000. */ + /* [01]D[02468ACE] are allowed. */ + if (bus_get_resource(dev, SYS_RES_IOPORT, 0, &iobase, NULL) != 0) + return ENXIO; + if ((iobase & ~0x10E) != 0xD0) + return ENXIO; + + if (fe98_alloc_port(dev, FE_TYPE_RE1000)) + return ENXIO; + + /* Fill the softc struct with default values. */ + fe_softc_defaults(sc); + + /* See if the card is on its address. */ + if (!fe_simple_probe(sc, probe_table)) + return ENXIO; + + /* Get our station address from EEPROM. */ + fe_inblk(sc, 0x18, sc->enaddr, ETHER_ADDR_LEN); + + /* Make sure it is Allied-Telesis's. */ + if (!fe_valid_Ether_p(sc->enaddr, 0x0000F4)) + return ENXIO; +#if 1 + /* Calculate checksum. */ + sum = fe_inb(sc, 0x1e); + for (i = 0; i < ETHER_ADDR_LEN; i++) + sum ^= sc->enaddr[i]; + if (sum != 0) + return ENXIO; +#endif + /* Setup the board type. */ + sc->typestr = "RE1000"; + + /* This looks like an RE1000 board. It requires an + explicit IRQ setting in config. Make sure we have one, + determining an appropriate value for the IRQ control + register. */ + irq = 0; + bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, NULL); + switch (irq) { + case 3: n = 0x10; break; + case 5: n = 0x20; break; + case 6: n = 0x40; break; + case 12: n = 0x80; break; + default: + fe_irq_failure(sc->typestr, sc->sc_unit, irq, "3/5/6/12"); + return ENXIO; + } + sc->priv_info = (fe_inb(sc, FE_RE1000_IRQCONF) & 0x0f) | n; + + /* Setup hooks. We need a special initialization procedure. */ + sc->init = fe_init_re1000; + + return 0; +} + +/* JLI sub-probe for Allied-Telesis RE1000Plus/ME1500 series. */ +static u_short const * +fe_probe_jli_re1000p(struct fe_softc * sc, u_char const * eeprom) +{ + int i; + static u_short const irqmaps_re1000p [4] = { 3, 5, 6, 12 }; + + /* Make sure the EEPROM contains Allied-Telesis bit pattern. */ + if (eeprom[1] != 0xFF) return NULL; + for (i = 2; i < 8; i++) if (eeprom[i] != 0xFF) return NULL; + for (i = 14; i < 24; i++) if (eeprom[i] != 0xFF) return NULL; + + /* Get our station address from EEPROM, and make sure the + EEPROM contains Allied-Telesis's address. */ + bcopy(eeprom + 8, sc->enaddr, ETHER_ADDR_LEN); + if (!fe_valid_Ether_p(sc->enaddr, 0x0000F4)) + return NULL; + + /* I don't know any sub-model identification. */ + sc->typestr = "RE1000Plus/ME1500"; + + /* Returns the IRQ table for the RE1000Plus. */ + return irqmaps_re1000p; +} + + +/* + * Probe for Allied-Telesis RE1000Plus/ME1500 series. + */ +static int +fe_probe_jli(device_t dev) +{ + struct fe_softc *sc = device_get_softc(dev); + int i, n, xirq, error; + u_long iobase, irq; + u_char eeprom [JLI_EEPROM_SIZE]; + u_short const * irqmap; + + static u_short const baseaddr [8] = + { 0x1D6, 0x1D8, 0x1DA, 0x1D4, 0x0D4, 0x0D2, 0x0D8, 0x0D0 }; + static struct fe_simple_probe_struct const probe_table [] = { + /* { FE_DLCR1, 0x20, 0x00 }, Doesn't work. */ + { FE_DLCR2, 0x50, 0x00 }, + { FE_DLCR4, 0x08, 0x00 }, + /* { FE_DLCR5, 0x80, 0x00 }, Doesn't work. */ +#if 0 + { FE_BMPR16, 0x1B, 0x00 }, + { FE_BMPR17, 0x7F, 0x00 }, +#endif + { 0 } + }; + + /* + * See if the specified address is possible for MB86965A JLI mode. + */ + if (bus_get_resource(dev, SYS_RES_IOPORT, 0, &iobase, NULL) != 0) + return ENXIO; + for (i = 0; i < 8; i++) { + if (baseaddr[i] == iobase) + break; + } + if (i == 8) + return ENXIO; + + if (fe98_alloc_port(dev, FE_TYPE_RE1000)) + return ENXIO; + + /* Fill the softc struct with default values. */ + fe_softc_defaults(sc); + + /* + * We should test if MB86965A is on the base address now. + * Unfortunately, it is very hard to probe it reliably, since + * we have no way to reset the chip under software control. + * On cold boot, we could check the "signature" bit patterns + * described in the Fujitsu document. On warm boot, however, + * we can predict almost nothing about register values. + */ + if (!fe_simple_probe(sc, probe_table)) + return ENXIO; + + /* Check if our I/O address matches config info on 86965. */ + n = (fe_inb(sc, FE_BMPR19) & FE_B19_ADDR) >> FE_B19_ADDR_SHIFT; + if (baseaddr[n] != iobase) + return ENXIO; + + /* + * We are now almost sure we have an MB86965 at the given + * address. So, read EEPROM through it. We have to write + * into LSI registers to read from EEPROM. I want to avoid it + * at this stage, but I cannot test the presence of the chip + * any further without reading EEPROM. FIXME. + */ + fe_read_eeprom_jli(sc, eeprom); + + /* Make sure that config info in EEPROM and 86965 agree. */ + if (eeprom[FE_EEPROM_CONF] != fe_inb(sc, FE_BMPR19)) + return ENXIO; + + /* Use 86965 media selection scheme, unless othewise + specified. It is "AUTO always" and "select with BMPR13". + This behaviour covers most of the 86965 based board (as + minimum requirements.) It is backward compatible with + previous versions, also. */ + sc->mbitmap = MB_HA; + sc->defmedia = MB_HA; + sc->msel = fe_msel_965; + + /* Perform board-specific probe. */ + if ((irqmap = fe_probe_jli_re1000p(sc, eeprom)) == NULL) + return ENXIO; + + /* Find the IRQ read from EEPROM. */ + n = (fe_inb(sc, FE_BMPR19) & FE_B19_IRQ) >> FE_B19_IRQ_SHIFT; + xirq = irqmap[n]; + + /* Try to determine IRQ setting. */ + error = bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, NULL); + if (error && xirq == NO_IRQ) { + /* The device must be configured with an explicit IRQ. */ + device_printf(dev, "IRQ auto-detection does not work\n"); + return ENXIO; + } else if (error && xirq != NO_IRQ) { + /* Just use the probed IRQ value. */ + bus_set_resource(dev, SYS_RES_IRQ, 0, xirq, 1); + } else if (!error && xirq == NO_IRQ) { + /* No problem. Go ahead. */ + } else if (irq == xirq) { + /* Good. Go ahead. */ + } else { + /* User must be warned in this case. */ + sc->stability |= UNSTABLE_IRQ; + } + + /* Setup a hook, which resets te 86965 when the driver is being + initialized. This may solve a nasty bug. FIXME. */ + sc->init = fe_init_jli; + + return 0; +} + + +/* + * Probe and initialization for Contec C-NET(9N)E series. + */ + +/* TODO: Should be in "if_fereg.h" */ +#define FE_CNET9NE_INTR 0x10 /* Interrupt Mask? */ + +static void +fe_init_cnet9ne(struct fe_softc *sc) +{ + /* Enable interrupt? FIXME. */ + fe_outb(sc, FE_CNET9NE_INTR, 0x10); +} + +static int +fe_probe_cnet9ne (device_t dev) +{ + struct fe_softc *sc = device_get_softc(dev); + u_long iobase, irq; + + static struct fe_simple_probe_struct probe_table [] = { + { FE_DLCR2, 0x58, 0x00 }, + { FE_DLCR4, 0x08, 0x00 }, + { 0 } + }; + + /* See if the specified I/O address is possible for C-NET(9N)E. */ + if (bus_get_resource(dev, SYS_RES_IOPORT, 0, &iobase, NULL) != 0) + return ENXIO; + if (iobase != 0x73D0) + return ENXIO; + + if (fe98_alloc_port(dev, FE_TYPE_CNET9NE)) + return ENXIO; + + /* Fill the softc struct with default values. */ + fe_softc_defaults(sc); + + /* See if the card is on its address. */ + if (!fe_simple_probe(sc, probe_table)) + return ENXIO; + + /* Get our station address from EEPROM. */ + fe_inblk(sc, 0x18, sc->enaddr, ETHER_ADDR_LEN); + + /* Make sure it is Contec's. */ + if (!fe_valid_Ether_p(sc->enaddr, 0x00804C)) + return ENXIO; + + /* Determine the card type. */ + if (sc->enaddr[3] == 0x06) { + sc->typestr = "C-NET(9N)C"; + + /* We seems to need our own IDENT bits... FIXME. */ + sc->proto_dlcr7 = FE_D7_BYTSWP_LH | FE_D7_IDENT_NICE; + + /* C-NET(9N)C requires an explicit IRQ to work. */ + if (bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, NULL) != 0) { + fe_irq_failure(sc->typestr, sc->sc_unit, NO_IRQ, NULL); + return ENXIO; + } + } else { + sc->typestr = "C-NET(9N)E"; + + /* C-NET(9N)E works only IRQ5. */ + if (bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, NULL) != 0) + return ENXIO; + if (irq != 5) { + fe_irq_failure(sc->typestr, sc->sc_unit, irq, "5"); + return ENXIO; + } + + /* We need an init hook to initialize ASIC before we start. */ + sc->init = fe_init_cnet9ne; + } + + /* C-NET(9N)E has 64KB SRAM. */ + sc->proto_dlcr6 = FE_D6_BUFSIZ_64KB | FE_D6_TXBSIZ_2x4KB + | FE_D6_BBW_WORD | FE_D6_SBW_WORD | FE_D6_SRAM; + + return 0; +} + + +/* + * Probe for Contec C-NET(98)P2 series. + * (Logitec LAN-98TP/LAN-98T25P - parhaps) + */ +static int +fe_probe_ssi(device_t dev) +{ + struct fe_softc *sc = device_get_softc(dev); + u_long iobase, irq; + + u_char eeprom [SSI_EEPROM_SIZE]; + static struct fe_simple_probe_struct probe_table [] = { + { FE_DLCR2, 0x08, 0x00 }, + { FE_DLCR4, 0x08, 0x00 }, + { 0 } + }; + static u_short const irqmap[] = { + /* INT0 INT1 INT2 */ + NO_IRQ, NO_IRQ, NO_IRQ, 3, NO_IRQ, 5, 6, NO_IRQ, + NO_IRQ, 9, 10, NO_IRQ, 12, 13, NO_IRQ, NO_IRQ, + /* INT3 INT41 INT5 INT6 */ + }; + + /* See if the specified I/O address is possible for 78Q8377A. */ + /* [0-D]3D0 are allowed. */ + if (bus_get_resource(dev, SYS_RES_IOPORT, 0, &iobase, NULL) != 0) + return ENXIO; + if ((iobase & 0xFFF) != 0x3D0) + return ENXIO; + + if (fe98_alloc_port(dev, FE_TYPE_SSI)) + return ENXIO; + + /* Fill the softc struct with default values. */ + fe_softc_defaults(sc); + + /* See if the card is on its address. */ + if (!fe_simple_probe(sc, probe_table)) + return ENXIO; + + /* We now have to read the config EEPROM. We should be very + careful, since doing so destroys a register. (Remember, we + are not yet sure we have a C-NET(98)P2 board here.) Don't + remember to select BMPRs bofore reading EEPROM, since other + register bank may be selected before the probe() is called. */ + fe_read_eeprom_ssi(sc, eeprom); + + /* Make sure the Ethernet (MAC) station address is of Contec's. */ + if (!fe_valid_Ether_p(eeprom + FE_SSI_EEP_ADDR, 0x00804C)) + return ENXIO; + bcopy(eeprom + FE_SSI_EEP_ADDR, sc->enaddr, ETHER_ADDR_LEN); + + /* Setup the board type. */ + sc->typestr = "C-NET(98)P2"; + + /* Non-PnP mode, set static resource from eeprom. */ + if (!isa_get_vendorid(dev)) { + /* Get IRQ configuration from EEPROM. */ + irq = irqmap[eeprom[FE_SSI_EEP_IRQ]]; + if (irq == NO_IRQ) { + fe_irq_failure(sc->typestr, sc->sc_unit, irq, + "3/5/6/9/10/12/13"); + return ENXIO; + } + bus_set_resource(dev, SYS_RES_IRQ, 0, irq, 1); + } + + /* Get Duplex-mode configuration from EEPROM. */ + sc->proto_dlcr4 |= (eeprom[FE_SSI_EEP_DUPLEX] & FE_D4_DSC); + + /* Fill softc struct accordingly. */ + sc->mbitmap = MB_HT; + sc->defmedia = MB_HT; + + return 0; +} + + +/* + * Probe for TDK LAC-98012/013/025/9N011 - parhaps. + */ +static int +fe_probe_lnx(device_t dev) +{ + struct fe_softc *sc = device_get_softc(dev); + + u_long iobase, irq; + u_char eeprom [LNX_EEPROM_SIZE]; + + static struct fe_simple_probe_struct probe_table [] = { + { FE_DLCR2, 0x58, 0x00 }, + { FE_DLCR4, 0x08, 0x00 }, + { 0 } + }; + + /* See if the specified I/O address is possible for TDK/LANX boards. */ + /* 0D0, 4D0, 8D0, and CD0 are allowed. */ + if (bus_get_resource(dev, SYS_RES_IOPORT, 0, &iobase, NULL) != 0) + return ENXIO; + if ((iobase & ~0xC00) != 0xD0) + return ENXIO; + + if (fe98_alloc_port(dev, FE_TYPE_LNX)) + return ENXIO; + + /* Fill the softc struct with default values. */ + fe_softc_defaults(sc); + + /* See if the card is on its address. */ + if (!fe_simple_probe(sc, probe_table)) + return ENXIO; + + /* We now have to read the config EEPROM. We should be very + careful, since doing so destroys a register. (Remember, we + are not yet sure we have a LAC-98012/98013 board here.) */ + fe_read_eeprom_lnx(sc, eeprom); + + /* Make sure the Ethernet (MAC) station address is of TDK/LANX's. */ + if (!fe_valid_Ether_p(eeprom, 0x008098)) + return ENXIO; + bcopy(eeprom, sc->enaddr, ETHER_ADDR_LEN); + + /* Setup the board type. */ + sc->typestr = "LAC-98012/98013"; + + /* This looks like a TDK/LANX board. It requires an + explicit IRQ setting in config. Make sure we have one, + determining an appropriate value for the IRQ control + register. */ + irq = 0; + if (bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, NULL) != 0) + return ENXIO; + switch (irq) { + case 3 : sc->priv_info = 0x10 | LNX_CLK_LO | LNX_SDA_HI; break; + case 5 : sc->priv_info = 0x20 | LNX_CLK_LO | LNX_SDA_HI; break; + case 6 : sc->priv_info = 0x40 | LNX_CLK_LO | LNX_SDA_HI; break; + case 12: sc->priv_info = 0x80 | LNX_CLK_LO | LNX_SDA_HI; break; + default: + fe_irq_failure(sc->typestr, sc->sc_unit, irq, "3/5/6/12"); + return ENXIO; + } + + /* LAC-98's system bus width is 8-bit. */ + sc->proto_dlcr6 = FE_D6_BUFSIZ_32KB | FE_D6_TXBSIZ_2x2KB + | FE_D6_BBW_BYTE | FE_D6_SBW_BYTE | FE_D6_SRAM_150ns; + + /* Setup hooks. We need a special initialization procedure. */ + sc->init = fe_init_lnx; + + return 0; +} + + +/* + * Probe for Gateway Communications' old cards. + * (both as Generic MB86960 probe routine) + */ +static int +fe_probe_gwy(device_t dev) +{ + struct fe_softc *sc = device_get_softc(dev); + + static struct fe_simple_probe_struct probe_table [] = { + /* { FE_DLCR2, 0x70, 0x00 }, */ + { FE_DLCR2, 0x58, 0x00 }, + { FE_DLCR4, 0x08, 0x00 }, + { 0 } + }; + + /* + * XXX + * I'm not sure which address is possible, so accepts any. + */ + + if (fe98_alloc_port(dev, FE_TYPE_GWY)) + return ENXIO; + + /* Fill the softc struct with default values. */ + fe_softc_defaults(sc); + + /* See if the card is on its address. */ + if (!fe_simple_probe(sc, probe_table)) + return ENXIO; + + /* Get our station address from EEPROM. */ + fe_inblk(sc, 0x18, sc->enaddr, ETHER_ADDR_LEN); + if (!fe_valid_Ether_p(sc->enaddr, 0x000000)) + return ENXIO; + + /* Determine the card type. */ + sc->typestr = "Generic MB86960 Ethernet"; + if (fe_valid_Ether_p(sc->enaddr, 0x000061)) + sc->typestr = "Gateway Ethernet (Fujitsu chipset)"; + + /* Gateway's board requires an explicit IRQ to work, since it + is not possible to probe the setting of jumpers. */ + if (bus_get_resource(dev, SYS_RES_IRQ, 0, NULL, NULL) != 0) { + fe_irq_failure(sc->typestr, sc->sc_unit, NO_IRQ, NULL); + return ENXIO; + } + + return 0; +} + + +/* + * Probe for Ungermann-Bass Access/PC N98C+(Model 85152). + */ +static int +fe_probe_ubn(device_t dev) +{ + struct fe_softc *sc = device_get_softc(dev); + + u_char sum, save7; + u_long iobase, irq; + int i; + static struct fe_simple_probe_struct const probe_table [] = { + { FE_DLCR2, 0x58, 0x00 }, + { FE_DLCR4, 0x08, 0x00 }, + { 0 } + }; + + /* See if the specified I/O address is possible for Access/PC. */ + /* [01][048C]D0 are allowed. */ + if (bus_get_resource(dev, SYS_RES_IOPORT, 0, &iobase, NULL) != 0) + return ENXIO; + if ((iobase & ~0x1C00) != 0xD0) + return ENXIO; + + if (fe98_alloc_port(dev, FE_TYPE_UBN)) + return ENXIO; + + /* Fill the softc struct with default values. */ + fe_softc_defaults(sc); + + /* Simple probe. */ + if (!fe_simple_probe(sc, probe_table)) + return ENXIO; + + /* NOTE: Access/NOTE N98 sometimes freeze when reading station + address. In case of using it togather with C-NET(9N)C, + this problem usually happens. + Writing DLCR7 prevents freezing, but I don't know why. FIXME. */ + + /* Save the current value for the DLCR7 register we are about + to destroy. */ + save7 = fe_inb(sc, FE_DLCR7); + fe_outb(sc, FE_DLCR7, + sc->proto_dlcr7 | FE_D7_RBS_BMPR | FE_D7_POWER_UP); + + /* Get our station address form ID ROM and make sure it is UBN's. */ + fe_inblk(sc, 0x18, sc->enaddr, ETHER_ADDR_LEN); + if (!fe_valid_Ether_p(sc->enaddr, 0x00DD01)) + goto fail_ubn; +#if 1 + /* Calculate checksum. */ + sum = fe_inb(sc, 0x1e); + for (i = 0; i < ETHER_ADDR_LEN; i++) + sum ^= sc->enaddr[i]; + if (sum != 0) + goto fail_ubn; +#endif + + /* Setup the board type. */ + sc->typestr = "Access/PC"; + + /* This looks like an AccessPC/N98C+ board. It requires an + explicit IRQ setting in config. Make sure we have one, + determining an appropriate value for the IRQ control + register. */ + irq = 0; + bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, NULL); + switch (irq) { + case 3: sc->priv_info = 0x01; break; + case 5: sc->priv_info = 0x02; break; + case 6: sc->priv_info = 0x04; break; + case 12: sc->priv_info = 0x08; break; + default: + fe_irq_failure(sc->typestr, sc->sc_unit, irq, "3/5/6/12"); + goto fail_ubn; + } + + /* Setup hooks. We need a special initialization procedure. */ + sc->init = fe_init_ubn; + + return 0; + +fail_ubn: + fe_outb(sc, FE_DLCR7, save7); + return ENXIO; +} + + +/* + * REX boards(non-JLI type) support routine. + */ + +#define REX_EEPROM_SIZE 32 +#define REX_DAT 0x01 + +static void +fe_read_eeprom_rex(struct fe_softc *sc, u_char *data) +{ + int i; + u_char bit, val; + u_char save16; + + save16 = fe_inb(sc, 0x10); + + /* Issue a start condition. */ + val = fe_inb(sc, 0x10) & 0xf0; + fe_outb(sc, 0x10, val); + + (void) fe_inb(sc, 0x10); + (void) fe_inb(sc, 0x10); + (void) fe_inb(sc, 0x10); + (void) fe_inb(sc, 0x10); + + /* Read bytes from EEPROM. */ + for (i = 0; i < REX_EEPROM_SIZE; i++) { + /* Read a byte and store it into the buffer. */ + val = 0x00; + for (bit = 0x01; bit != 0x00; bit <<= 1) + if (fe_inb(sc, 0x10) & REX_DAT) + val |= bit; + *data++ = val; + } + + fe_outb(sc, 0x10, save16); + +#if 1 + /* Report what we got. */ + if (bootverbose) { + data -= REX_EEPROM_SIZE; + for (i = 0; i < REX_EEPROM_SIZE; i += 16) { + printf("fe%d: EEPROM(REX):%3x: %16D\n", + sc->sc_unit, i, data + i, " "); + } + } +#endif +} + + +static void +fe_init_rex(struct fe_softc *sc) +{ + /* Setup IRQ control register on the ASIC. */ + fe_outb(sc, 0x10, sc->priv_info); +} + +/* + * Probe for RATOC REX-9880/81/82/83 series. + */ +static int +fe_probe_rex(device_t dev) +{ + struct fe_softc *sc = device_get_softc(dev); + + int i; + u_long iobase, irq; + u_char eeprom [REX_EEPROM_SIZE]; + + static struct fe_simple_probe_struct probe_table [] = { + { FE_DLCR2, 0x58, 0x00 }, + { FE_DLCR4, 0x08, 0x00 }, + { 0 } + }; + + /* See if the specified I/O address is possible for REX-9880. */ + /* 6[46CE]D0 are allowed. */ + if (bus_get_resource(dev, SYS_RES_IOPORT, 0, &iobase, NULL) != 0) + return ENXIO; + if ((iobase & ~0xA00) != 0x64D0) + return ENXIO; + + if (fe98_alloc_port(dev, FE_TYPE_REX)) + return ENXIO; + + /* Fill the softc struct with default values. */ + fe_softc_defaults(sc); + + /* See if the card is on its address. */ + if (!fe_simple_probe(sc, probe_table)) + return ENXIO; + + /* We now have to read the config EEPROM. We should be very + careful, since doing so destroys a register. (Remember, we + are not yet sure we have a REX-9880 board here.) */ + fe_read_eeprom_rex(sc, eeprom); + for (i = 0; i < ETHER_ADDR_LEN; i++) + sc->enaddr[i] = eeprom[7 - i]; + + /* Make sure it is RATOC's. */ + if (!fe_valid_Ether_p(sc->enaddr, 0x00C0D0) && + !fe_valid_Ether_p(sc->enaddr, 0x00803D)) + return 0; + + /* Setup the board type. */ + sc->typestr = "REX-9880/9883"; + + /* This looks like a REX-9880 board. It requires an + explicit IRQ setting in config. Make sure we have one, + determining an appropriate value for the IRQ control + register. */ + irq = 0; + bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, NULL); + switch (irq) { + case 3: sc->priv_info = 0x10; break; + case 5: sc->priv_info = 0x20; break; + case 6: sc->priv_info = 0x40; break; + case 12: sc->priv_info = 0x80; break; + default: + fe_irq_failure(sc->typestr, sc->sc_unit, irq, "3/5/6/12"); + return ENXIO; + } + + /* Setup hooks. We need a special initialization procedure. */ + sc->init = fe_init_rex; + + /* REX-9880 has 64KB SRAM. */ + sc->proto_dlcr6 = FE_D6_BUFSIZ_64KB | FE_D6_TXBSIZ_2x4KB + | FE_D6_BBW_WORD | FE_D6_SBW_WORD | FE_D6_SRAM; +#if 1 + sc->proto_dlcr7 |= FE_D7_EOPPOL; /* XXX */ +#endif + + return 0; +} diff --git a/sys/dev/fe/if_fe_isa.c b/sys/dev/fe/if_fe_isa.c new file mode 100644 index 000000000000..3c37575a40d9 --- /dev/null +++ b/sys/dev/fe/if_fe_isa.c @@ -0,0 +1,1053 @@ +/*- + * All Rights Reserved, Copyright (C) Fujitsu Limited 1995 + * + * This software may be used, modified, copied, distributed, and sold, in + * both source and binary form provided that the above copyright, these + * terms and the following disclaimer are retained. The name of the author + * and/or the contributor may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND THE CONTRIBUTOR ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR THE CONTRIBUTOR BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION. + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/module.h> + +#include <sys/bus.h> +#include <machine/bus.h> +#include <machine/resource.h> + +#include <net/ethernet.h> +#include <net/if.h> +#include <net/if_mib.h> +#include <net/if_media.h> + +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#include <dev/fe/mb86960.h> +#include <dev/fe/if_fereg.h> +#include <dev/fe/if_fevar.h> + +#include <isa/isavar.h> + +/* + * ISA specific code. + */ +static int fe_isa_probe(device_t); +static int fe_isa_attach(device_t); + +static device_method_t fe_isa_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, fe_isa_probe), + DEVMETHOD(device_attach, fe_isa_attach), + + { 0, 0 } +}; + +static driver_t fe_isa_driver = { + "fe", + fe_isa_methods, + sizeof (struct fe_softc) +}; + +DRIVER_MODULE(fe, isa, fe_isa_driver, fe_devclass, 0, 0); + + +static int fe_probe_ssi(device_t); +static int fe_probe_jli(device_t); +static int fe_probe_fmv(device_t); +static int fe_probe_lnx(device_t); +static int fe_probe_gwy(device_t); +static int fe_probe_ubn(device_t); + +/* + * Determine if the device is present at a specified I/O address. The + * main entry to the driver. + */ +static int +fe_isa_probe(device_t dev) +{ + struct fe_softc *sc; + int error; + + /* Check isapnp ids */ + if (isa_get_vendorid(dev)) + return (ENXIO); + + /* Prepare for the softc struct. */ + sc = device_get_softc(dev); + sc->sc_unit = device_get_unit(dev); + + /* Probe for supported boards. */ + if ((error = fe_probe_ssi(dev)) == 0) + goto end; + fe_release_resource(dev); + + if ((error = fe_probe_jli(dev)) == 0) + goto end; + fe_release_resource(dev); + + if ((error = fe_probe_fmv(dev)) == 0) + goto end; + fe_release_resource(dev); + + if ((error = fe_probe_lnx(dev)) == 0) + goto end; + fe_release_resource(dev); + + if ((error = fe_probe_ubn(dev)) == 0) + goto end; + fe_release_resource(dev); + + if ((error = fe_probe_gwy(dev)) == 0) + goto end; + fe_release_resource(dev); + +end: + if (error == 0) + error = fe_alloc_irq(dev, 0); + + fe_release_resource(dev); + return (error); +} + +static int +fe_isa_attach(device_t dev) +{ + struct fe_softc *sc = device_get_softc(dev); + + if (sc->port_used) + fe_alloc_port(dev, sc->port_used); + fe_alloc_irq(dev, 0); + + return fe_attach(dev); +} + + +/* + * Probe and initialization for Fujitsu FMV-180 series boards + */ + +static void +fe_init_fmv(struct fe_softc *sc) +{ + /* Initialize ASIC. */ + fe_outb(sc, FE_FMV3, 0); + fe_outb(sc, FE_FMV10, 0); + +#if 0 + /* "Refresh" hardware configuration. FIXME. */ + fe_outb(sc, FE_FMV2, fe_inb(sc, FE_FMV2)); +#endif + + /* Turn the "master interrupt control" flag of ASIC on. */ + fe_outb(sc, FE_FMV3, FE_FMV3_IRQENB); +} + +static void +fe_msel_fmv184(struct fe_softc *sc) +{ + u_char port; + + /* FMV-184 has a special "register" to switch between AUI/BNC. + Determine the value to write into the register, based on the + user-specified media selection. */ + port = (IFM_SUBTYPE(sc->media.ifm_media) == IFM_10_2) ? 0x00 : 0x01; + + /* The register is #5 on exntesion register bank... + (Details of the register layout is not yet discovered.) */ + fe_outb(sc, 0x1B, 0x46); /* ??? */ + fe_outb(sc, 0x1E, 0x04); /* select ex-reg #4. */ + fe_outb(sc, 0x1F, 0xC8); /* ??? */ + fe_outb(sc, 0x1E, 0x05); /* select ex-reg #5. */ + fe_outb(sc, 0x1F, port); /* Switch the media. */ + fe_outb(sc, 0x1E, 0x04); /* select ex-reg #4. */ + fe_outb(sc, 0x1F, 0x00); /* ??? */ + fe_outb(sc, 0x1B, 0x00); /* ??? */ + + /* Make sure to select "external tranceiver" on MB86964. */ + fe_outb(sc, FE_BMPR13, sc->proto_bmpr13 | FE_B13_PORT_AUI); +} + +static int +fe_probe_fmv(device_t dev) +{ + struct fe_softc *sc = device_get_softc(dev); + int n; + u_long iobase, irq; + + static u_short const irqmap [ 4 ] = { 3, 7, 10, 15 }; + + static struct fe_simple_probe_struct const probe_table [] = { + { FE_DLCR2, 0x71, 0x00 }, + { FE_DLCR4, 0x08, 0x00 }, + + { FE_FMV0, 0x78, 0x50 }, /* ERRDY+PRRDY */ + { FE_FMV1, 0xB0, 0x00 }, /* FMV-183/4 has 0x48 bits. */ + { FE_FMV3, 0x7F, 0x00 }, + + { 0 } + }; + + /* Board subtypes; it lists known FMV-180 variants. */ + struct subtype { + u_short mcode; + u_short mbitmap; + u_short defmedia; + char const * str; + }; + static struct subtype const typelist [] = { + { 0x0005, MB_HA|MB_HT|MB_H5, MB_HA, "FMV-181" }, + { 0x0105, MB_HA|MB_HT|MB_H5, MB_HA, "FMV-181A" }, + { 0x0003, MB_HM, MB_HM, "FMV-182" }, + { 0x0103, MB_HM, MB_HM, "FMV-182A" }, + { 0x0804, MB_HT, MB_HT, "FMV-183" }, + { 0x0C04, MB_HT, MB_HT, "FMV-183 (on-board)" }, + { 0x0803, MB_H2|MB_H5, MB_H2, "FMV-184" }, + { 0, MB_HA, MB_HA, "unknown FMV-180 (?)" }, + }; + struct subtype const * type; + + /* Media indicator and "Hardware revision ID" */ + u_short mcode; + + /* See if the specified address is possible for FMV-180 + series. 220, 240, 260, 280, 2A0, 2C0, 300, and 340 are + allowed for all boards, and 200, 2E0, 320, 360, 380, 3A0, + 3C0, and 3E0 for PnP boards. */ + if (bus_get_resource(dev, SYS_RES_IOPORT, 0, &iobase, NULL) != 0) + return ENXIO; + if ((iobase & ~0x1E0) != 0x200) + return ENXIO; + + /* FMV-180 occupies 32 I/O addresses. */ + if (fe_alloc_port(dev, 32)) + return ENXIO; + + /* Setup an I/O address mapping table and some others. */ + fe_softc_defaults(sc); + + /* Simple probe. */ + if (!fe_simple_probe(sc, probe_table)) + return ENXIO; + + /* Get our station address from EEPROM, and make sure it is + Fujitsu's. */ + fe_inblk(sc, FE_FMV4, sc->enaddr, ETHER_ADDR_LEN); + if (!fe_valid_Ether_p(sc->enaddr, 0x00000E)) + return ENXIO; + + /* Find the supported media and "hardware revision" to know + the model identification. */ + mcode = (fe_inb(sc, FE_FMV0) & FE_FMV0_MEDIA) + | ((fe_inb(sc, FE_FMV1) & FE_FMV1_REV) << 8); + + /* Determine the card type. */ + for (type = typelist; type->mcode != 0; type++) { + if (type->mcode == mcode) + break; + } + if (type->mcode == 0) { + /* Unknown card type... Hope the driver works. */ + sc->stability |= UNSTABLE_TYPE; + if (bootverbose) { + device_printf(dev, "unknown config: %x-%x-%x-%x\n", + fe_inb(sc, FE_FMV0), + fe_inb(sc, FE_FMV1), + fe_inb(sc, FE_FMV2), + fe_inb(sc, FE_FMV3)); + } + } + + /* Setup the board type and media information. */ + sc->type = FE_TYPE_FMV; + sc->typestr = type->str; + sc->mbitmap = type->mbitmap; + sc->defmedia = type->defmedia; + sc->msel = fe_msel_965; + + if (type->mbitmap == (MB_H2 | MB_H5)) { + /* FMV184 requires a special media selection procedure. */ + sc->msel = fe_msel_fmv184; + } + + /* + * An FMV-180 has been probed. + * Determine which IRQ to be used. + * + * In this version, we give a priority to the kernel config file. + * If the EEPROM and config don't match, say it to the user for + * an attention. + */ + n = (fe_inb(sc, FE_FMV2) & FE_FMV2_IRS) >> FE_FMV2_IRS_SHIFT; + + irq = 0; + bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, NULL); + if (irq == NO_IRQ) { + /* Just use the probed value. */ + bus_set_resource(dev, SYS_RES_IRQ, 0, irqmap[n], 1); + } else if (irq != irqmap[n]) { + /* Don't match. */ + sc->stability |= UNSTABLE_IRQ; + } + + /* We need an init hook to initialize ASIC before we start. */ + sc->init = fe_init_fmv; + + return 0; +} + +/* + * Fujitsu MB86965 JLI mode probe routines. + * + * 86965 has a special operating mode called JLI (mode 0), under which + * the chip interfaces with ISA bus with a software-programmable + * configuration. (The Fujitsu document calls the feature "Plug and + * play," but it is not compatible with the ISA-PnP spec. designed by + * Intel and Microsoft.) Ethernet cards designed to use JLI are + * almost same, but there are two things which require board-specific + * probe routines: EEPROM layout and IRQ pin connection. + * + * JLI provides a handy way to access EEPROM which should contains the + * chip configuration information (such as I/O port address) as well + * as Ethernet station (MAC) address. The chip configuration info. is + * stored on a fixed location. However, the station address can be + * located anywhere in the EEPROM; it is up to the board designer to + * determine the location. (The manual just says "somewhere in the + * EEPROM.") The fe driver must somehow find out the correct + * location. + * + * Another problem resides in the IRQ pin connection. JLI provides a + * user to choose an IRQ from up to four predefined IRQs. The 86965 + * chip has a register to select one out of the four possibilities. + * However, the selection is against the four IRQ pins on the chip. + * (So-called IRQ-A, -B, -C and -D.) It is (again) up to the board + * designer to determine which pin to connect which IRQ line on the + * ISA bus. We need a vendor (or model, for some vendor) specific IRQ + * mapping table. + * + * The routine fe_probe_jli() provides all probe and initialization + * processes which are common to all JLI implementation, and sub-probe + * routines supply board-specific actions. + * + * JLI sub-probe routine has the following template: + * + * u_short const * func (struct fe_softc * sc, u_char const * eeprom); + * + * where eeprom is a pointer to an array of 32 byte data read from the + * config EEPROM on the board. It retuns an IRQ mapping table for the + * board, when the corresponding implementation is detected. It + * returns a NULL otherwise. + * + * Primary purpose of the functin is to analize the config EEPROM, + * determine if it matches with the pattern of that of supported card, + * and extract necessary information from it. One of the information + * expected to be extracted from EEPROM is the Ethernet station (MAC) + * address, which must be set to the softc table of the interface by + * the board-specific routine. + */ + +/* JLI sub-probe for Allied-Telesyn/Allied-Telesis AT1700/RE2000 series. */ +static u_short const * +fe_probe_jli_ati(struct fe_softc * sc, u_char const * eeprom) +{ + int i; + static u_short const irqmaps_ati [4][4] = + { + { 3, 4, 5, 9 }, + { 10, 11, 12, 15 }, + { 3, 11, 5, 15 }, + { 10, 11, 14, 15 }, + }; + + /* Make sure the EEPROM contains Allied-Telesis/Allied-Telesyn + bit pattern. */ + if (eeprom[1] != 0x00) return NULL; + for (i = 2; i < 8; i++) if (eeprom[i] != 0xFF) return NULL; + for (i = 14; i < 24; i++) if (eeprom[i] != 0xFF) return NULL; + + /* Get our station address from EEPROM, and make sure the + EEPROM contains ATI's address. */ + bcopy(eeprom + 8, sc->enaddr, ETHER_ADDR_LEN); + if (!fe_valid_Ether_p(sc->enaddr, 0x0000F4)) + return NULL; + + /* + * The following model identification codes are stolen + * from the NetBSD port of the fe driver. My reviewers + * suggested minor revision. + */ + + /* Determine the card type. */ + switch (eeprom[FE_ATI_EEP_MODEL]) { + case FE_ATI_MODEL_AT1700T: + sc->typestr = "AT-1700T/RE2001"; + sc->mbitmap = MB_HT; + sc->defmedia = MB_HT; + break; + case FE_ATI_MODEL_AT1700BT: + sc->typestr = "AT-1700BT/RE2003"; + sc->mbitmap = MB_HA | MB_HT | MB_H2; + break; + case FE_ATI_MODEL_AT1700FT: + sc->typestr = "AT-1700FT/RE2009"; + sc->mbitmap = MB_HA | MB_HT | MB_HF; + break; + case FE_ATI_MODEL_AT1700AT: + sc->typestr = "AT-1700AT/RE2005"; + sc->mbitmap = MB_HA | MB_HT | MB_H5; + break; + default: + sc->typestr = "unknown AT-1700/RE2000"; + sc->stability |= UNSTABLE_TYPE | UNSTABLE_IRQ; + break; + } + sc->type = FE_TYPE_JLI; + +#if 0 + /* Should we extract default media from eeprom? Linux driver + for AT1700 does it, although previous releases of FreeBSD + don't. FIXME. */ + /* Determine the default media selection from the config + EEPROM. The byte at offset EEP_MEDIA is believed to + contain BMPR13 value to be set. We just ignore STP bit or + squelch bit, since we don't support those. (It is + intentional.) */ + switch (eeprom[FE_ATI_EEP_MEDIA] & FE_B13_PORT) { + case FE_B13_AUTO: + sc->defmedia = MB_HA; + break; + case FE_B13_TP: + sc->defmedia = MB_HT; + break; + case FE_B13_AUI: + sc->defmedia = sc->mbitmap & (MB_H2|MB_H5|MB_H5); /*XXX*/ + break; + default: + sc->defmedia = MB_HA; + break; + } + + /* Make sure the default media is compatible with the supported + ones. */ + if ((sc->defmedia & sc->mbitmap) == 0) { + if (sc->defmedia == MB_HA) { + sc->defmedia = MB_HT; + } else { + sc->defmedia = MB_HA; + } + } +#endif + + /* + * Try to determine IRQ settings. + * Different models use different ranges of IRQs. + */ + switch ((eeprom[FE_ATI_EEP_REVISION] & 0xf0) + |(eeprom[FE_ATI_EEP_MAGIC] & 0x04)) { + case 0x30: case 0x34: return irqmaps_ati[3]; + case 0x10: case 0x14: + case 0x50: case 0x54: return irqmaps_ati[2]; + case 0x44: case 0x64: return irqmaps_ati[1]; + default: return irqmaps_ati[0]; + } +} + +/* JLI sub-probe and msel hook for ICL Ethernet. */ +static void +fe_msel_icl(struct fe_softc *sc) +{ + u_char d4; + + /* Switch between UTP and "external tranceiver" as always. */ + fe_msel_965(sc); + + /* The board needs one more bit (on DLCR4) be set appropriately. */ + if (IFM_SUBTYPE(sc->media.ifm_media) == IFM_10_5) { + d4 = sc->proto_dlcr4 | FE_D4_CNTRL; + } else { + d4 = sc->proto_dlcr4 & ~FE_D4_CNTRL; + } + fe_outb(sc, FE_DLCR4, d4); +} + +static u_short const * +fe_probe_jli_icl(struct fe_softc * sc, u_char const * eeprom) +{ + int i; + u_short defmedia; + u_char d6; + static u_short const irqmap_icl [4] = { 9, 10, 5, 15 }; + + /* Make sure the EEPROM contains ICL bit pattern. */ + for (i = 24; i < 39; i++) { + if (eeprom[i] != 0x20 && (eeprom[i] & 0xF0) != 0x30) return NULL; + } + for (i = 112; i < 122; i++) { + if (eeprom[i] != 0x20 && (eeprom[i] & 0xF0) != 0x30) return NULL; + } + + /* Make sure the EEPROM contains ICL's permanent station + address. If it isn't, probably this board is not an + ICL's. */ + if (!fe_valid_Ether_p(eeprom+122, 0x00004B)) + return NULL; + + /* Check if the "configured" Ethernet address in the EEPROM is + valid. Use it if it is, or use the "permanent" address instead. */ + if (fe_valid_Ether_p(eeprom+4, 0x020000)) { + /* The configured address is valid. Use it. */ + bcopy(eeprom+4, sc->enaddr, ETHER_ADDR_LEN); + } else { + /* The configured address is invalid. Use permanent. */ + bcopy(eeprom+122, sc->enaddr, ETHER_ADDR_LEN); + } + + /* Determine model and supported media. */ + switch (eeprom[0x5E]) { + case 0: + sc->typestr = "EtherTeam16i/COMBO"; + sc->mbitmap = MB_HA | MB_HT | MB_H5 | MB_H2; + break; + case 1: + sc->typestr = "EtherTeam16i/TP"; + sc->mbitmap = MB_HT; + break; + case 2: + sc->typestr = "EtherTeam16i/ErgoPro"; + sc->mbitmap = MB_HA | MB_HT | MB_H5; + break; + case 4: + sc->typestr = "EtherTeam16i/DUO"; + sc->mbitmap = MB_HA | MB_HT | MB_H2; + break; + default: + sc->typestr = "EtherTeam16i"; + sc->stability |= UNSTABLE_TYPE; + if (bootverbose) { + printf("fe%d: unknown model code %02x for EtherTeam16i\n", + sc->sc_unit, eeprom[0x5E]); + } + break; + } + sc->type = FE_TYPE_JLI; + + /* I'm not sure the following msel hook is required by all + models or COMBO only... FIXME. */ + sc->msel = fe_msel_icl; + + /* Make the configured media selection the default media. */ + switch (eeprom[0x28]) { + case 0: defmedia = MB_HA; break; + case 1: defmedia = MB_H5; break; + case 2: defmedia = MB_HT; break; + case 3: defmedia = MB_H2; break; + default: + if (bootverbose) { + printf("fe%d: unknown default media: %02x\n", + sc->sc_unit, eeprom[0x28]); + } + defmedia = MB_HA; + break; + } + + /* Make sure the default media is compatible with the + supported media. */ + if ((defmedia & sc->mbitmap) == 0) { + if (bootverbose) { + printf("fe%d: default media adjusted\n", sc->sc_unit); + } + defmedia = sc->mbitmap; + } + + /* Keep the determined default media. */ + sc->defmedia = defmedia; + + /* ICL has "fat" models. We have to program 86965 to properly + reflect the hardware. */ + d6 = sc->proto_dlcr6 & ~(FE_D6_BUFSIZ | FE_D6_BBW); + switch ((eeprom[0x61] << 8) | eeprom[0x60]) { + case 0x2008: d6 |= FE_D6_BUFSIZ_32KB | FE_D6_BBW_BYTE; break; + case 0x4010: d6 |= FE_D6_BUFSIZ_64KB | FE_D6_BBW_WORD; break; + default: + /* We can't support it, since we don't know which bits + to set in DLCR6. */ + printf("fe%d: unknown SRAM config for ICL\n", sc->sc_unit); + return NULL; + } + sc->proto_dlcr6 = d6; + + /* Returns the IRQ table for the ICL board. */ + return irqmap_icl; +} + +/* JLI sub-probe for RATOC REX-5586/5587. */ +static u_short const * +fe_probe_jli_rex(struct fe_softc * sc, u_char const * eeprom) +{ + int i; + static u_short const irqmap_rex [4] = { 3, 4, 5, NO_IRQ }; + + /* Make sure the EEPROM contains RATOC's config pattern. */ + if (eeprom[1] != eeprom[0]) return NULL; + for (i = 8; i < 32; i++) if (eeprom[i] != 0xFF) return NULL; + + /* Get our station address from EEPROM. Note that RATOC + stores it "byte-swapped" in each word. (I don't know why.) + So, we just can't use bcopy().*/ + sc->enaddr[0] = eeprom[3]; + sc->enaddr[1] = eeprom[2]; + sc->enaddr[2] = eeprom[5]; + sc->enaddr[3] = eeprom[4]; + sc->enaddr[4] = eeprom[7]; + sc->enaddr[5] = eeprom[6]; + + /* Make sure the EEPROM contains RATOC's station address. */ + if (!fe_valid_Ether_p(sc->enaddr, 0x00C0D0)) + return NULL; + + /* I don't know any sub-model identification. */ + sc->type = FE_TYPE_JLI; + sc->typestr = "REX-5586/5587"; + + /* Returns the IRQ for the RATOC board. */ + return irqmap_rex; +} + +/* JLI sub-probe for Unknown board. */ +static u_short const * +fe_probe_jli_unk(struct fe_softc * sc, u_char const * eeprom) +{ + int i, n, romsize; + static u_short const irqmap [4] = { NO_IRQ, NO_IRQ, NO_IRQ, NO_IRQ }; + + /* The generic JLI probe considered this board has an 86965 + in JLI mode, but any other board-specific routines could + not find the matching implementation. So, we "guess" the + location by looking for a bit pattern which looks like a + MAC address. */ + + /* Determine how large the EEPROM is. */ + for (romsize = JLI_EEPROM_SIZE/2; romsize > 16; romsize >>= 1) { + for (i = 0; i < romsize; i++) { + if (eeprom[i] != eeprom[i+romsize]) + break; + } + if (i < romsize) + break; + } + romsize <<= 1; + + /* Look for a bit pattern which looks like a MAC address. */ + for (n = 2; n <= romsize - ETHER_ADDR_LEN; n += 2) { + if (!fe_valid_Ether_p(eeprom + n, 0x000000)) + continue; + } + + /* If no reasonable address was found, we can't go further. */ + if (n > romsize - ETHER_ADDR_LEN) + return NULL; + + /* Extract our (guessed) station address. */ + bcopy(eeprom+n, sc->enaddr, ETHER_ADDR_LEN); + + /* We are not sure what type of board it is... */ + sc->type = FE_TYPE_JLI; + sc->typestr = "(unknown JLI)"; + sc->stability |= UNSTABLE_TYPE | UNSTABLE_MAC; + + /* Returns the totally unknown IRQ mapping table. */ + return irqmap; +} + +/* + * Probe and initialization for all JLI implementations. + */ + +static int +fe_probe_jli(device_t dev) +{ + struct fe_softc *sc = device_get_softc(dev); + int i, n, error, xirq; + u_long iobase, irq; + u_char eeprom [JLI_EEPROM_SIZE]; + u_short const * irqmap; + + static u_short const baseaddr [8] = + { 0x260, 0x280, 0x2A0, 0x240, 0x340, 0x320, 0x380, 0x300 }; + static struct fe_simple_probe_struct const probe_table [] = { + { FE_DLCR1, 0x20, 0x00 }, + { FE_DLCR2, 0x50, 0x00 }, + { FE_DLCR4, 0x08, 0x00 }, + { FE_DLCR5, 0x80, 0x00 }, +#if 0 + { FE_BMPR16, 0x1B, 0x00 }, + { FE_BMPR17, 0x7F, 0x00 }, +#endif + { 0 } + }; + + /* + * See if the specified address is possible for MB86965A JLI mode. + */ + if (bus_get_resource(dev, SYS_RES_IOPORT, 0, &iobase, NULL) != 0) + return ENXIO; + for (i = 0; i < 8; i++) { + if (baseaddr[i] == iobase) + break; + } + if (i == 8) + return ENXIO; + + /* 86965 JLI occupies 32 I/O addresses. */ + if (fe_alloc_port(dev, 32)) + return ENXIO; + + /* Fill the softc struct with reasonable default. */ + fe_softc_defaults(sc); + + /* + * We should test if MB86965A is on the base address now. + * Unfortunately, it is very hard to probe it reliably, since + * we have no way to reset the chip under software control. + * On cold boot, we could check the "signature" bit patterns + * described in the Fujitsu document. On warm boot, however, + * we can predict almost nothing about register values. + */ + if (!fe_simple_probe(sc, probe_table)) + return ENXIO; + + /* Check if our I/O address matches config info on 86965. */ + n = (fe_inb(sc, FE_BMPR19) & FE_B19_ADDR) >> FE_B19_ADDR_SHIFT; + if (baseaddr[n] != iobase) + return ENXIO; + + /* + * We are now almost sure we have an MB86965 at the given + * address. So, read EEPROM through it. We have to write + * into LSI registers to read from EEPROM. I want to avoid it + * at this stage, but I cannot test the presence of the chip + * any further without reading EEPROM. FIXME. + */ + fe_read_eeprom_jli(sc, eeprom); + + /* Make sure that config info in EEPROM and 86965 agree. */ + if (eeprom[FE_EEPROM_CONF] != fe_inb(sc, FE_BMPR19)) + return ENXIO; + + /* Use 86965 media selection scheme, unless othewise + specified. It is "AUTO always" and "select with BMPR13." + This behaviour covers most of the 86965 based board (as + minimum requirements.) It is backward compatible with + previous versions, also. */ + sc->mbitmap = MB_HA; + sc->defmedia = MB_HA; + sc->msel = fe_msel_965; + + /* Perform board-specific probe, one by one. Note that the + order of probe is important and should not be changed + arbitrarily. */ + if ((irqmap = fe_probe_jli_ati(sc, eeprom)) == NULL + && (irqmap = fe_probe_jli_rex(sc, eeprom)) == NULL + && (irqmap = fe_probe_jli_icl(sc, eeprom)) == NULL + && (irqmap = fe_probe_jli_unk(sc, eeprom)) == NULL) + return ENXIO; + + /* Find the IRQ read from EEPROM. */ + n = (fe_inb(sc, FE_BMPR19) & FE_B19_IRQ) >> FE_B19_IRQ_SHIFT; + xirq = irqmap[n]; + + /* Try to determine IRQ setting. */ + error = bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, NULL); + if (error && xirq == NO_IRQ) { + /* The device must be configured with an explicit IRQ. */ + device_printf(dev, "IRQ auto-detection does not work\n"); + return ENXIO; + } else if (error && xirq != NO_IRQ) { + /* Just use the probed IRQ value. */ + bus_set_resource(dev, SYS_RES_IRQ, 0, xirq, 1); + } else if (!error && xirq == NO_IRQ) { + /* No problem. Go ahead. */ + } else if (irq == xirq) { + /* Good. Go ahead. */ + } else { + /* User must be warned in this case. */ + sc->stability |= UNSTABLE_IRQ; + } + + /* Setup a hook, which resets te 86965 when the driver is being + initialized. This may solve a nasty bug. FIXME. */ + sc->init = fe_init_jli; + + return 0; +} + +/* Probe for TDK LAK-AX031, which is an SSi 78Q8377A based board. */ +static int +fe_probe_ssi(device_t dev) +{ + struct fe_softc *sc = device_get_softc(dev); + u_long iobase, irq; + + u_char eeprom [SSI_EEPROM_SIZE]; + static struct fe_simple_probe_struct probe_table [] = { + { FE_DLCR2, 0x08, 0x00 }, + { FE_DLCR4, 0x08, 0x00 }, + { 0 } + }; + + /* See if the specified I/O address is possible for 78Q8377A. */ + if (bus_get_resource(dev, SYS_RES_IOPORT, 0, &iobase, NULL) != 0) + return ENXIO; + if ((iobase & ~0x3F0) != 0x000) + return ENXIO; + + /* We have 16 registers. */ + if (fe_alloc_port(dev, 16)) + return ENXIO; + + /* Fill the softc struct with default values. */ + fe_softc_defaults(sc); + + /* See if the card is on its address. */ + if (!fe_simple_probe(sc, probe_table)) + return ENXIO; + + /* We now have to read the config EEPROM. We should be very + careful, since doing so destroys a register. (Remember, we + are not yet sure we have a LAK-AX031 board here.) Don't + remember to select BMPRs bofore reading EEPROM, since other + register bank may be selected before the probe() is called. */ + fe_read_eeprom_ssi(sc, eeprom); + + /* Make sure the Ethernet (MAC) station address is of TDK's. */ + if (!fe_valid_Ether_p(eeprom+FE_SSI_EEP_ADDR, 0x008098)) + return ENXIO; + bcopy(eeprom + FE_SSI_EEP_ADDR, sc->enaddr, ETHER_ADDR_LEN); + + /* This looks like a TDK-AX031 board. It requires an explicit + IRQ setting in config, since we currently don't know how we + can find the IRQ value assigned by ISA PnP manager. */ + if (bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, NULL) != 0) { + fe_irq_failure("LAK-AX031", sc->sc_unit, NO_IRQ, NULL); + return ENXIO; + } + + /* Fill softc struct accordingly. */ + sc->type = FE_TYPE_SSI; + sc->typestr = "LAK-AX031"; + sc->mbitmap = MB_HT; + sc->defmedia = MB_HT; + + return 0; +} + +/* + * Probe and initialization for TDK/LANX LAC-AX012/013 boards. + */ +static int +fe_probe_lnx(device_t dev) +{ + struct fe_softc *sc = device_get_softc(dev); + u_long iobase, irq; + + u_char eeprom [LNX_EEPROM_SIZE]; + static struct fe_simple_probe_struct probe_table [] = { + { FE_DLCR2, 0x58, 0x00 }, + { FE_DLCR4, 0x08, 0x00 }, + { 0 } + }; + + /* See if the specified I/O address is possible for TDK/LANX boards. */ + /* 300, 320, 340, and 360 are allowed. */ + if (bus_get_resource(dev, SYS_RES_IOPORT, 0, &iobase, NULL) != 0) + return ENXIO; + if ((iobase & ~0x060) != 0x300) + return ENXIO; + + /* We have 32 registers. */ + if (fe_alloc_port(dev, 32)) + return ENXIO; + + /* Fill the softc struct with default values. */ + fe_softc_defaults(sc); + + /* See if the card is on its address. */ + if (!fe_simple_probe(sc, probe_table)) + return ENXIO; + + /* We now have to read the config EEPROM. We should be very + careful, since doing so destroys a register. (Remember, we + are not yet sure we have a LAC-AX012/AX013 board here.) */ + fe_read_eeprom_lnx(sc, eeprom); + + /* Make sure the Ethernet (MAC) station address is of TDK/LANX's. */ + if (!fe_valid_Ether_p(eeprom, 0x008098)) + return ENXIO; + bcopy(eeprom, sc->enaddr, ETHER_ADDR_LEN); + + /* This looks like a TDK/LANX board. It requires an + explicit IRQ setting in config. Make sure we have one, + determining an appropriate value for the IRQ control + register. */ + irq = 0; + bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, NULL); + switch (irq) { + case 3: sc->priv_info = 0x40 | LNX_CLK_LO | LNX_SDA_HI; break; + case 4: sc->priv_info = 0x20 | LNX_CLK_LO | LNX_SDA_HI; break; + case 5: sc->priv_info = 0x10 | LNX_CLK_LO | LNX_SDA_HI; break; + case 9: sc->priv_info = 0x80 | LNX_CLK_LO | LNX_SDA_HI; break; + default: + fe_irq_failure("LAC-AX012/AX013", sc->sc_unit, irq, "3/4/5/9"); + return ENXIO; + } + + /* Fill softc struct accordingly. */ + sc->type = FE_TYPE_LNX; + sc->typestr = "LAC-AX012/AX013"; + sc->init = fe_init_lnx; + + return 0; +} + +/* + * Probe and initialization for Gateway Communications' old cards. + */ +static int +fe_probe_gwy(device_t dev) +{ + struct fe_softc *sc = device_get_softc(dev); + u_long iobase, irq; + + static struct fe_simple_probe_struct probe_table [] = { + /* { FE_DLCR2, 0x70, 0x00 }, */ + { FE_DLCR2, 0x58, 0x00 }, + { FE_DLCR4, 0x08, 0x00 }, + { 0 } + }; + + /* See if the specified I/O address is possible for Gateway boards. */ + if (bus_get_resource(dev, SYS_RES_IOPORT, 0, &iobase, NULL) != 0) + return ENXIO; + if ((iobase & ~0x1E0) != 0x200) + return ENXIO; + + /* That's all. The card occupies 32 I/O addresses, as always. */ + if (fe_alloc_port(dev, 32)) + return ENXIO; + + /* Setup an I/O address mapping table and some others. */ + fe_softc_defaults(sc); + + /* See if the card is on its address. */ + if (!fe_simple_probe(sc, probe_table)) + return ENXIO; + + /* Get our station address from EEPROM. */ + fe_inblk(sc, 0x18, sc->enaddr, ETHER_ADDR_LEN); + + /* Make sure it is Gateway Communication's. */ + if (!fe_valid_Ether_p(sc->enaddr, 0x000061)) + return ENXIO; + + /* Gateway's board requires an explicit IRQ to work, since it + is not possible to probe the setting of jumpers. */ + if (bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, NULL) != 0) { + fe_irq_failure("Gateway Ethernet", sc->sc_unit, NO_IRQ, NULL); + return ENXIO; + } + + /* Fill softc struct accordingly. */ + sc->type = FE_TYPE_GWY; + sc->typestr = "Gateway Ethernet (Fujitsu chipset)"; + + return 0; +} + +/* Probe and initialization for Ungermann-Bass Network + K.K. "Access/PC" boards. */ +static int +fe_probe_ubn(device_t dev) +{ + struct fe_softc *sc = device_get_softc(dev); + u_long iobase, irq; +#if 0 + u_char sum; +#endif + static struct fe_simple_probe_struct const probe_table [] = { + { FE_DLCR2, 0x58, 0x00 }, + { FE_DLCR4, 0x08, 0x00 }, + { 0 } + }; + + /* See if the specified I/O address is possible for AccessPC/ISA. */ + if (bus_get_resource(dev, SYS_RES_IOPORT, 0, &iobase, NULL) != 0) + return ENXIO; + if ((iobase & ~0x0E0) != 0x300) + return ENXIO; + + /* We have 32 registers. */ + if (fe_alloc_port(dev, 32)) + return ENXIO; + + /* Setup an I/O address mapping table and some others. */ + fe_softc_defaults(sc); + + /* Simple probe. */ + if (!fe_simple_probe(sc, probe_table)) + return ENXIO; + + /* Get our station address form ID ROM and make sure it is UBN's. */ + fe_inblk(sc, 0x18, sc->enaddr, ETHER_ADDR_LEN); + if (!fe_valid_Ether_p(sc->enaddr, 0x00DD01)) + return ENXIO; +#if 0 + /* Calculate checksum. */ + sum = fe_inb(sc, 0x1e); + for (i = 0; i < ETHER_ADDR_LEN; i++) { + sum ^= sc->enaddr[i]; + } + if (sum != 0) + return ENXIO; +#endif + /* This looks like an AccessPC/ISA board. It requires an + explicit IRQ setting in config. Make sure we have one, + determining an appropriate value for the IRQ control + register. */ + irq = 0; + bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, NULL); + switch (irq) { + case 3: sc->priv_info = 0x02; break; + case 4: sc->priv_info = 0x04; break; + case 5: sc->priv_info = 0x08; break; + case 10: sc->priv_info = 0x10; break; + default: + fe_irq_failure("Access/PC", sc->sc_unit, irq, "3/4/5/10"); + return ENXIO; + } + + /* Fill softc struct accordingly. */ + sc->type = FE_TYPE_UBN; + sc->typestr = "Access/PC"; + sc->init = fe_init_ubn; + + return 0; +} diff --git a/sys/dev/fe/if_fe_pccard.c b/sys/dev/fe/if_fe_pccard.c new file mode 100644 index 000000000000..a143e637893a --- /dev/null +++ b/sys/dev/fe/if_fe_pccard.c @@ -0,0 +1,310 @@ +/*- + * All Rights Reserved, Copyright (C) Fujitsu Limited 1995 + * + * This software may be used, modified, copied, distributed, and sold, in + * both source and binary form provided that the above copyright, these + * terms and the following disclaimer are retained. The name of the author + * and/or the contributor may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND THE CONTRIBUTOR ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR THE CONTRIBUTOR BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION. + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/systm.h> +#include <sys/module.h> + +#include <sys/bus.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> + +#include <net/ethernet.h> +#include <net/if.h> +#include <net/if_mib.h> +#include <net/if_media.h> + +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#include <dev/fe/mb86960.h> +#include <dev/fe/if_fereg.h> +#include <dev/fe/if_fevar.h> + +#include <dev/pccard/pccardvar.h> +#include <dev/pccard/pccard_cis.h> + +#include "card_if.h" +#include "pccarddevs.h" + +/* + * PC-Card (PCMCIA) specific code. + */ +static int fe_pccard_probe(device_t); +static int fe_pccard_attach(device_t); +static int fe_pccard_detach(device_t); +static int fe_pccard_match(device_t); + +static const struct fe_pccard_product { + struct pccard_product mpp_product; + int mpp_flags; +#define MPP_MBH10302 1 +} fe_pccard_products[] = { + /* These need to be first */ + { PCMCIA_CARD(FUJITSU2, FMV_J181), MPP_MBH10302 }, + { PCMCIA_CARD(FUJITSU2, FMV_J182), 0 }, + { PCMCIA_CARD(FUJITSU2, FMV_J182A), 0 }, + { PCMCIA_CARD(FUJITSU2, ITCFJ182A), 0 }, + /* These need to be second */ + { PCMCIA_CARD(TDK, LAK_CD021BX), 0 }, + { PCMCIA_CARD(TDK, LAK_CF010), 0 }, +#if 0 /* XXX 86960-based? */ + { PCMCIA_CARD(TDK, LAK_DFL9610), 0 }, +#endif + { PCMCIA_CARD(CONTEC, CNETPC), 0 }, + { PCMCIA_CARD(FUJITSU, LA501), 0 }, + { PCMCIA_CARD(FUJITSU, LA10S), 0 }, + { PCMCIA_CARD(FUJITSU, NE200T), MPP_MBH10302 },/* Sold by Eagle */ + { PCMCIA_CARD(RATOC, REX_R280), 0 }, + { { NULL } } +}; + +static int +fe_pccard_match(device_t dev) +{ + const struct pccard_product *pp; + int error; + uint32_t fcn = PCCARD_FUNCTION_UNSPEC; + + /* Make sure we're a network function */ + error = pccard_get_function(dev, &fcn); + if (error != 0) + return (error); + if (fcn != PCCARD_FUNCTION_NETWORK) + return (ENXIO); + + if ((pp = pccard_product_lookup(dev, + (const struct pccard_product *)fe_pccard_products, + sizeof(fe_pccard_products[0]), NULL)) != NULL) { + if (pp->pp_name != NULL) + device_set_desc(dev, pp->pp_name); + return 0; + } + return EIO; +} + +static device_method_t fe_pccard_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, pccard_compat_probe), + DEVMETHOD(device_attach, pccard_compat_attach), + DEVMETHOD(device_detach, fe_pccard_detach), + + /* Card interface */ + DEVMETHOD(card_compat_match, fe_pccard_match), + DEVMETHOD(card_compat_probe, fe_pccard_probe), + DEVMETHOD(card_compat_attach, fe_pccard_attach), + + { 0, 0 } +}; + +static driver_t fe_pccard_driver = { + "fe", + fe_pccard_methods, + sizeof (struct fe_softc) +}; + +DRIVER_MODULE(fe, pccard, fe_pccard_driver, fe_devclass, 0, 0); + +static int fe_probe_mbh(device_t, const struct fe_pccard_product *); +static int fe_probe_tdk(device_t, const struct fe_pccard_product *); +/* + * Initialize the device - called from Slot manager. + */ +static int +fe_pccard_probe(device_t dev) +{ + struct fe_softc *sc; + const struct fe_pccard_product *pp; + int error; + + /* Prepare for the device probe process. */ + sc = device_get_softc(dev); + sc->sc_unit = device_get_unit(dev); + + pp = (const struct fe_pccard_product *) pccard_product_lookup(dev, + (const struct pccard_product *)fe_pccard_products, + sizeof(fe_pccard_products[0]), NULL); + if (pp == NULL) + return (ENXIO); + + if (pp->mpp_flags & MPP_MBH10302) + error = fe_probe_mbh(dev, pp); + else + error = fe_probe_tdk(dev, pp); + if (error == 0) + error = fe_alloc_irq(dev, 0); + + fe_release_resource(dev); + return (error); +} + +static int +fe_pccard_attach(device_t dev) +{ + struct fe_softc *sc = device_get_softc(dev); + + if (sc->port_used) + fe_alloc_port(dev, sc->port_used); + fe_alloc_irq(dev, 0); + + return fe_attach(dev); +} + +/* + * feunload - unload the driver and clear the table. + */ +static int +fe_pccard_detach(device_t dev) +{ + struct fe_softc *sc = device_get_softc(dev); + struct ifnet *ifp = sc->ifp; + + fe_stop(sc); + ether_ifdetach(ifp); + if_free(ifp); + bus_teardown_intr(dev, sc->irq_res, sc->irq_handle); + fe_release_resource(dev); + + return 0; +} + + +/* + * Probe and initialization for Fujitsu MBH10302 PCMCIA Ethernet interface. + * Note that this is for 10302 only; MBH10304 is handled by fe_probe_tdk(). + */ +static void +fe_init_mbh(struct fe_softc *sc) +{ + /* Minimal initialization of 86960. */ + DELAY(200); + fe_outb(sc, FE_DLCR6, sc->proto_dlcr6 | FE_D6_DLC_DISABLE); + DELAY(200); + + /* Disable all interrupts. */ + fe_outb(sc, FE_DLCR2, 0); + fe_outb(sc, FE_DLCR3, 0); + + /* Enable master interrupt flag. */ + fe_outb(sc, FE_MBH0, FE_MBH0_MAGIC | FE_MBH0_INTR_ENABLE); +} + +static int +fe_probe_mbh(device_t dev, const struct fe_pccard_product *pp) +{ + struct fe_softc *sc = device_get_softc(dev); + + static struct fe_simple_probe_struct probe_table [] = { + { FE_DLCR2, 0x58, 0x00 }, + { FE_DLCR4, 0x08, 0x00 }, + { FE_DLCR6, 0xFF, 0xB6 }, + { 0 } + }; + + /* MBH10302 occupies 32 I/O addresses. */ + if (fe_alloc_port(dev, 32)) + return ENXIO; + + /* Fill the softc struct with default values. */ + fe_softc_defaults(sc); + + /* + * See if MBH10302 is on its address. + * I'm not sure the following probe code works. FIXME. + */ + if (!fe_simple_probe(sc, probe_table)) + return ENXIO; + + /* Get our station address from EEPROM. */ + fe_inblk(sc, FE_MBH10, sc->enaddr, ETHER_ADDR_LEN); + + /* Make sure we got a valid station address. */ + if (!fe_valid_Ether_p(sc->enaddr, 0)) + return ENXIO; + + /* Determine the card type. */ + sc->type = FE_TYPE_MBH; + sc->typestr = "MBH10302 (PCMCIA)"; + + /* We seems to need our own IDENT bits... FIXME. */ + sc->proto_dlcr7 = FE_D7_BYTSWP_LH | FE_D7_IDENT_NICE; + + /* Setup hooks. We need a special initialization procedure. */ + sc->init = fe_init_mbh; + + return 0; +} + +/* + * Probe and initialization for TDK/CONTEC PCMCIA Ethernet interface. + * by MASUI Kenji <masui@cs.titech.ac.jp> + * + * (Contec uses TDK Ethenet chip -- hosokawa) + * + * This version of fe_probe_tdk has been rewrote to handle + * *generic* PC card implementation of Fujitsu MB8696x family. The + * name _tdk is just for a historical reason. :-) + */ +static int +fe_probe_tdk (device_t dev, const struct fe_pccard_product *pp) +{ + struct fe_softc *sc = device_get_softc(dev); + + static struct fe_simple_probe_struct probe_table [] = { + { FE_DLCR2, 0x10, 0x00 }, + { FE_DLCR4, 0x08, 0x00 }, + /* { FE_DLCR5, 0x80, 0x00 }, Does not work well. */ + { 0 } + }; + + /* C-NET(PC)C occupies 16 I/O addresses. */ + if (fe_alloc_port(dev, 16)) + return ENXIO; + + /* Fill the softc struct with default values. */ + fe_softc_defaults(sc); + + /* + * See if C-NET(PC)C is on its address. + */ + if (!fe_simple_probe(sc, probe_table)) + return ENXIO; + + /* Determine the card type. */ + sc->type = FE_TYPE_TDK; + sc->typestr = "Generic MB8696x/78Q837x Ethernet (PCMCIA)"; + + pccard_get_ether(dev, sc->enaddr); + + /* Make sure we got a valid station address. */ + if (!fe_valid_Ether_p(sc->enaddr, 0)) + return ENXIO; + + return 0; +} diff --git a/sys/dev/fe/if_fereg.h b/sys/dev/fe/if_fereg.h new file mode 100644 index 000000000000..afc9c5704c58 --- /dev/null +++ b/sys/dev/fe/if_fereg.h @@ -0,0 +1,166 @@ +/*- + * Hardware specification of various 8696x based Ethernet cards. + * Contributed by M. Sekiguchi <seki@sysrap.cs.fujitsu.co.jp> + * + * All Rights Reserved, Copyright (C) Fujitsu Limited 1995 + * + * This software may be used, modified, copied, distributed, and sold, + * in both source and binary form provided that the above copyright, + * these terms and the following disclaimer are retained. The name of + * the author and/or the contributor may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND THE CONTRIBUTOR ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR THE CONTRIBUTOR BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* $FreeBSD$ */ + +/* + * Registers on FMV-180 series' ISA bus interface ASIC. + * I'm not sure the following register names are appropriate. + * Doesn't it look silly, eh? FIXME. + */ + +#define FE_FMV0 16 /* Card status register #0 */ +#define FE_FMV1 17 /* Card status register #1 */ +#define FE_FMV2 18 /* Card config register #0 */ +#define FE_FMV3 19 /* Card config register #1 */ +#define FE_FMV4 20 /* Station address #1 */ +#define FE_FMV5 21 /* Station address #2 */ +#define FE_FMV6 22 /* Station address #3 */ +#define FE_FMV7 23 /* Station address #4 */ +#define FE_FMV8 24 /* Station address #5 */ +#define FE_FMV9 25 /* Station address #6 */ +#define FE_FMV10 26 /* Buffer RAM control register */ +#define FE_FMV11 27 /* Buffer RAM data register */ + +/* + * FMV-180 series' ASIC register values. + */ + +/* FMV0: Card status register #0: Misc info? */ +#define FE_FMV0_MEDIA 0x07 /* Supported physical media. */ +#define FE_FMV0_PRRDY 0x10 /* ??? */ +#define FE_FMV0_PRERR 0x20 /* ??? */ +#define FE_FMV0_ERRDY 0x40 /* ??? */ +#define FE_FMV0_IREQ 0x80 /* ??? */ + +#define FE_FMV0_MEDIUM_5 0x01 /* 10base5/Dsub */ +#define FE_FMV0_MEDIUM_2 0x02 /* 10base2/BNC */ +#define FE_FMV0_MEDIUM_T 0x04 /* 10baseT/RJ45 */ + +/* Card status register #1: Hardware revision. */ +#define FE_FMV1_REV 0x0F /* Card revision */ +#define FE_FMV1_UPPER 0xF0 /* Usage unknown */ + +/* Card config register #0: I/O port address assignment. */ +#define FE_FMV2_IOS 0x07 /* I/O selection. */ +#define FE_FMV2_MES 0x38 /* ??? boot ROM? */ +#define FE_FMV2_IRS 0xC0 /* IRQ selection. */ + +#define FE_FMV2_IOS_SHIFT 0 +#define FE_FMV2_MES_SHIFT 3 +#define FE_FMV2_IRS_SHIFT 6 + +/* Card config register #1: IRQ enable */ +#define FE_FMV3_IRQENB 0x80 /* IRQ enable. */ + + +/* + * Register(?) specific to AT1700/RE2000. + */ + +#define FE_ATI_RESET 0x1F /* Write to reset the 86965. */ + +/* EEPROM allocation (offsets) of AT1700/RE2000. */ +#define FE_ATI_EEP_ADDR 0x08 /* Station address. (8-13) */ +#define FE_ATI_EEP_MEDIA 0x18 /* Media type. */ +#define FE_ATI_EEP_MAGIC 0x19 /* XXX Magic. */ +#define FE_ATI_EEP_MODEL 0x1e /* Hardware type. */ +#define FE_ATI_EEP_REVISION 0x1f /* Hardware revision. */ + +/* Value for FE_ATI_EEP_MODEL. */ +#define FE_ATI_MODEL_AT1700T 0x00 +#define FE_ATI_MODEL_AT1700BT 0x01 +#define FE_ATI_MODEL_AT1700FT 0x02 +#define FE_ATI_MODEL_AT1700AT 0x03 + + +/* + * Registers on MBH10302. + */ + +#define FE_MBH0 0x10 /* ??? Including interrupt. */ +#define FE_MBH1 0x11 /* ??? */ +#define FE_MBH10 0x1A /* Station address. (10 - 15) */ + +/* Values to be set in MBH0 register. */ +#define FE_MBH0_MAGIC 0x0D /* Just a magic constant? */ +#define FE_MBH0_INTR 0x10 /* Master interrupt control. */ + +#define FE_MBH0_INTR_ENABLE 0x10 /* Enable interrupts. */ +#define FE_MBH0_INTR_DISABLE 0x00 /* Disable interrupts. */ + + +/* + * Registers on RE1000. (*NOT* on RE1000 Plus.) + */ + +/* IRQ configuration. */ +#define FE_RE1000_IRQCONF 0x10 + + +/* + * Fujitsu MB86965 JLI mode support routines. + */ + +/* Datasheet for 86965 explicitly states that it only supports serial + * EEPROM with 16 words (32 bytes) capacity. (I.e., 93C06.) However, + * ones with 64 words (128 bytes) are available in the marked, namely + * 93C46, and are also fully compatible with 86965. It is known that + * some boards (e.g., ICL) actually have 93C46 on them and use extra + * storage to keep various config info. */ +#define JLI_EEPROM_SIZE 128 + + +/* + * SSi 78Q8377A support routines. + */ +#define SSI_EEPROM_SIZE 512 +#define SSI_DIN 0x01 +#define SSI_DAT 0x01 +#define SSI_CSL 0x02 +#define SSI_CLK 0x04 +#define SSI_EEP 0x10 + +#define FE_SSI_EEP_IRQ 9 /* Irq ??? */ +#define FE_SSI_EEP_ADDR 16 /* Station(MAC) address */ +#define FE_SSI_EEP_DUPLEX 25 /* Duplex mode ??? */ + + +/* + * TDK/LANX boards support routines. + */ + +/* AX012/AX013 equips an X24C01 chip, which has 128 bytes of memory cells. */ +#define LNX_EEPROM_SIZE 128 + +/* Bit assignments and command definitions for the serial EEPROM + interface register in LANX ASIC. */ +#define LNX_SDA_HI 0x08 /* Drive SDA line high (logical 1.) */ +#define LNX_SDA_LO 0x00 /* Drive SDA line low (logical 0.) */ +#define LNX_SDA_FL 0x08 /* Float (don't drive) SDA line. */ +#define LNX_SDA_IN 0x01 /* Mask for reading SDA line. */ +#define LNX_CLK_HI 0x04 /* Drive clock line high (active.) */ +#define LNX_CLK_LO 0x00 /* Drive clock line low (inactive.) */ diff --git a/sys/dev/fe/if_fevar.h b/sys/dev/fe/if_fevar.h new file mode 100644 index 000000000000..e80c9567b8a4 --- /dev/null +++ b/sys/dev/fe/if_fevar.h @@ -0,0 +1,179 @@ +/*- + * All Rights Reserved, Copyright (C) Fujitsu Limited 1995 + * + * This software may be used, modified, copied, distributed, and sold, in + * both source and binary form provided that the above copyright, these + * terms and the following disclaimer are retained. The name of the author + * and/or the contributor may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND THE CONTRIBUTOR ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR THE CONTRIBUTOR BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION. + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* How many registers does an fe-supported adapter have at maximum? */ +#define MAXREGISTERS 32 + +/* Shouldn't these be defined somewhere else such as isa_device.h? */ +#define NO_IRQ 0 + +/* Flags for stability. */ +#define UNSTABLE_IRQ 0x01 /* IRQ setting may be incorrect. */ +#define UNSTABLE_MAC 0x02 /* Probed MAC address may be incorrect. */ +#define UNSTABLE_TYPE 0x04 /* Probed vendor/model may be incorrect. */ + +/* Mapping between media bitmap (in fe_softc.mbitmap) and ifm_media. */ +#define MB_HA 0x0001 +#define MB_HM 0x0002 +#define MB_HT 0x0004 +#define MB_H2 0x0008 +#define MB_H5 0x0010 +#define MB_HF 0x0020 +#define MB_FT 0x0040 + +/* Card types. */ +#define FE_TYPE_SSI 1 +#define FE_TYPE_JLI 2 +#define FE_TYPE_FMV 3 +#define FE_TYPE_LNX 4 +#define FE_TYPE_UBN 5 +#define FE_TYPE_GWY 6 +#define FE_TYPE_MBH 7 +#define FE_TYPE_TDK 8 +#define FE_TYPE_RE1000 9 +#define FE_TYPE_CNET9NE 10 +#define FE_TYPE_REX 11 + +/* + * Data type for a multicast address filter on 8696x. + */ +struct fe_filter { + u_char data [FE_FILTER_LEN]; +}; + +/* + * fe_softc: per line info and status + */ +struct fe_softc { + + /* Used by "common" codes. */ + struct ifnet *ifp; + int sc_unit; + u_char enaddr[6]; + + /* Used by config codes. */ + int type; + int port_used; + struct resource * port_res; + bus_space_tag_t iot; + bus_space_handle_t ioh; + struct resource * irq_res; + void * irq_handle; + + /* Set by probe() and not modified in later phases. */ + char const * typestr; /* printable name of the interface. */ + u_short txb_size; /* size of TX buffer, in bytes */ + u_char proto_dlcr4; /* DLCR4 prototype. */ + u_char proto_dlcr5; /* DLCR5 prototype. */ + u_char proto_dlcr6; /* DLCR6 prototype. */ + u_char proto_dlcr7; /* DLCR7 prototype. */ + u_char proto_bmpr13; /* BMPR13 prototype. */ + u_char stability; /* How stable is this? */ + u_short priv_info; /* info specific to a vendor/model. */ + + /* Vendor/model specific hooks. */ + void (*init)(struct fe_softc *); /* Just before fe_init(). */ + void (*stop)(struct fe_softc *); /* Just after fe_stop(). */ + + /* Transmission buffer management. */ + u_short txb_free; /* free bytes in TX buffer */ + u_char txb_count; /* number of packets in TX buffer */ + u_char txb_sched; /* number of scheduled packets */ + + /* Excessive collision counter (see fe_tint() for details.) */ + u_char tx_excolls; /* # of excessive collisions. */ + + /* Multicast address filter management. */ + u_char filter_change; /* MARs must be changed ASAP. */ + struct fe_filter filter;/* new filter value. */ + + /* Network management. */ + struct ifmib_iso_8802_3 mibdata; + + /* Media information. */ + struct ifmedia media; /* used by if_media. */ + u_short mbitmap; /* bitmap for supported media; see bit2media */ + int defmedia; /* default media */ + void (* msel)(struct fe_softc *); /* media selector. */ + +}; + +struct fe_simple_probe_struct { + u_char port; /* Offset from the base I/O address. */ + u_char mask; /* Bits to be checked. */ + u_char bits; /* Values to be compared against. */ +}; + + +extern devclass_t fe_devclass; + +int fe_attach(device_t); +int fe_alloc_port(device_t, int); +int fe_alloc_irq(device_t, int); +void fe_release_resource(device_t); + +int fe_simple_probe(struct fe_softc const *, + struct fe_simple_probe_struct const *); +int fe_valid_Ether_p(u_char const *, unsigned); +void fe_softc_defaults(struct fe_softc *); +void fe_stop(struct fe_softc *sc); +void fe_irq_failure(char const *, int, int, char const *); +void fe_msel_965(struct fe_softc *); +void fe_read_eeprom_jli(struct fe_softc *, u_char *); +void fe_init_jli(struct fe_softc *); +void fe_read_eeprom_ssi(struct fe_softc *, u_char *); +void fe_read_eeprom_lnx(struct fe_softc *, u_char *); +void fe_init_lnx(struct fe_softc *); +void fe_init_ubn(struct fe_softc *); + + +#define fe_inb(sc, port) \ + bus_space_read_1((sc)->iot, (sc)->ioh, (port)) + +#define fe_outb(sc, port, value) \ + bus_space_write_1((sc)->iot, (sc)->ioh, (port), (value)) + +#define fe_inw(sc, port) \ + bus_space_read_2((sc)->iot, (sc)->ioh, (port)) + +#define fe_outw(sc, port, value) \ + bus_space_write_2((sc)->iot, (sc)->ioh, (port), (value)) + +#define fe_insb(sc, port, addr, count) \ + bus_space_read_multi_1((sc)->iot, (sc)->ioh, (port), (addr), (count)) + +#define fe_outsb(sc, port, addr, count) \ + bus_space_write_multi_1((sc)->iot, (sc)->ioh, (port), (addr), (count)) + +#define fe_insw(sc, port, addr, count) \ + bus_space_read_multi_2((sc)->iot, (sc)->ioh, (port), (addr), (count)) + +#define fe_outsw(sc, port, addr, count) \ + bus_space_write_multi_2((sc)->iot, (sc)->ioh, (port), (addr), (count)) + +#define fe_inblk(sc, port, addr, count) \ + bus_space_read_region_1((sc)->iot, (sc)->ioh, (port), (addr), (count)) + +#define fe_outblk(sc, port, addr, count) \ + bus_space_write_region_1((sc)->iot, (sc)->ioh, (port), (addr), (count)) diff --git a/sys/dev/fe/mb86960.h b/sys/dev/fe/mb86960.h new file mode 100644 index 000000000000..1272dde68331 --- /dev/null +++ b/sys/dev/fe/mb86960.h @@ -0,0 +1,340 @@ +/*- + * All Rights Reserved, Copyright (C) Fujitsu Limited 1995 + * + * This software may be used, modified, copied, distributed, and sold, in + * both source and binary form provided that the above copyright, these + * terms and the following disclaimer are retained. The name of the author + * and/or the contributor may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND THE CONTRIBUTOR ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR THE CONTRIBUTOR BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Registers of Fujitsu MB86960A/MB86965A series Ethernet controllers. + * Written and contributed by M.S. <seki@sysrap.cs.fujitsu.co.jp> + */ + +/* + * Notes on register naming: + * + * Fujitsu documents for MB86960A/MB86965A uses no mnemorable names + * for their registers. They defined only three names for 32 + * registers and appended numbers to distinguish registers of + * same name. Surprisingly, the numbers represent I/O address + * offsets of the registers from the base addresses, and their + * names correspond to the "bank" the registers are allocated. + * All this means that, for example, to say "read DLCR8" has no more + * than to say "read a register at offset 8 on bank DLCR." + * + * The following definitions may look silly, but that's what Fujitsu + * did, and it is necessary to know these names to read Fujitsu + * documents.. + */ + +/* Data Link Control Registrs, on invaliant port addresses. */ +#define FE_DLCR0 0 +#define FE_DLCR1 1 +#define FE_DLCR2 2 +#define FE_DLCR3 3 +#define FE_DLCR4 4 +#define FE_DLCR5 5 +#define FE_DLCR6 6 +#define FE_DLCR7 7 + +/* More DLCRs, on register bank #0. */ +#define FE_DLCR8 8 +#define FE_DLCR9 9 +#define FE_DLCR10 10 +#define FE_DLCR11 11 +#define FE_DLCR12 12 +#define FE_DLCR13 13 +#define FE_DLCR14 14 +#define FE_DLCR15 15 + +/* Malticast Address Registers. On register bank #1. */ +#define FE_MAR8 8 +#define FE_MAR9 9 +#define FE_MAR10 10 +#define FE_MAR11 11 +#define FE_MAR12 12 +#define FE_MAR13 13 +#define FE_MAR14 14 +#define FE_MAR15 15 + +/* Buffer Memory Port Registers. On register back #2. */ +#define FE_BMPR8 8 +#define FE_BMPR9 9 +#define FE_BMPR10 10 +#define FE_BMPR11 11 +#define FE_BMPR12 12 +#define FE_BMPR13 13 +#define FE_BMPR14 14 +#define FE_BMPR15 15 + +/* More BMPRs, only on 86965, accessible only when JLI mode. */ +#define FE_BMPR16 16 +#define FE_BMPR17 17 +#define FE_BMPR18 18 +#define FE_BMPR19 19 + +/* + * Definitions of registers. + * I don't have Fujitsu documents of MB86960A/MB86965A, so I don't + * know the official names for each flags and fields. The following + * names are assigned by me (the author of this file,) since I cannot + * mnemorize hexadecimal constants for all of these functions. + * Comments? + * + * I've got documents from Fujitsu web site, recently. However, it's + * too late. Names for some fields (bits) are kept different from + * those used in the Fujitsu documents... + */ + +/* DLCR0 -- transmitter status */ +#define FE_D0_BUSERR 0x01 /* Bus write error? */ +#define FE_D0_COLL16 0x02 /* Collision limit (16) encountered */ +#define FE_D0_COLLID 0x04 /* Collision on last transmission */ +#define FE_D0_JABBER 0x08 /* Jabber */ +#define FE_D0_CRLOST 0x10 /* Carrier lost on last transmission */ +#define FE_D0_PKTRCD 0x20 /* Last packet looped back correctly */ +#define FE_D0_NETBSY 0x40 /* Network Busy (Carrier Detected) */ +#define FE_D0_TXDONE 0x80 /* Transmission complete */ + +/* DLCR1 -- receiver status */ +#define FE_D1_OVRFLO 0x01 /* Receiver buffer overflow */ +#define FE_D1_CRCERR 0x02 /* CRC error on last packet */ +#define FE_D1_ALGERR 0x04 /* Alignment error on last packet */ +#define FE_D1_SRTPKT 0x08 /* Short (RUNT) packet is received */ +#define FE_D1_RMTRST 0x10 /* Remote reset packet (type = 0x0900) */ +#define FE_D1_DMAEOP 0x20 /* Host asserted End of DMA OPeration */ +#define FE_D1_BUSERR 0x40 /* Bus read error */ +#define FE_D1_PKTRDY 0x80 /* Packet(s) ready on receive buffer */ + +/* DLCR2 -- transmitter interrupt control; same layout as DLCR0 */ +#define FE_D2_BUSERR FE_D0_BUSERR +#define FE_D2_COLL16 FE_D0_COLL16 +#define FE_D2_COLLID FE_D0_COLLID +#define FE_D2_JABBER FE_D0_JABBER +#define FE_D2_TXDONE FE_D0_TXDONE + +#define FE_D2_RESERVED 0x70 + +/* DLCR3 -- receiver interrupt control; same layout as DLCR1 */ +#define FE_D3_OVRFLO FE_D1_OVRFLO +#define FE_D3_CRCERR FE_D1_CRCERR +#define FE_D3_ALGERR FE_D1_ALGERR +#define FE_D3_SRTPKT FE_D1_SRTPKT +#define FE_D3_RMTRST FE_D1_RMTRST +#define FE_D3_DMAEOP FE_D1_DMAEOP +#define FE_D3_BUSERR FE_D1_BUSERR +#define FE_D3_PKTRDY FE_D1_PKTRDY + +/* DLCR4 -- transmitter operation mode */ +#define FE_D4_DSC 0x01 /* Disable carrier sense on trans. */ +#define FE_D4_LBC 0x02 /* Loop back test control */ +#define FE_D4_CNTRL 0x04 /* - tied to CNTRL pin of the chip */ +#define FE_D4_TEST1 0x08 /* Test output #1 */ +#define FE_D4_COL 0xF0 /* Collision counter */ + +#define FE_D4_LBC_ENABLE 0x00 /* Perform loop back test */ +#define FE_D4_LBC_DISABLE 0x02 /* Normal operation */ + +#define FE_D4_COL_SHIFT 4 + +/* DLCR5 -- receiver operation mode */ +#define FE_D5_AFM0 0x01 /* Receive packets for other stations */ +#define FE_D5_AFM1 0x02 /* Receive packets for this station */ +#define FE_D5_RMTRST 0x04 /* Enable remote reset operation */ +#define FE_D5_SRTPKT 0x08 /* Accept short (RUNT) packets */ +#define FE_D5_SRTADR 0x10 /* Short (16 bits?) MAC address */ +#define FE_D5_BADPKT 0x20 /* Accept packets with error */ +#define FE_D5_BUFEMP 0x40 /* Receive buffer is empty */ +#define FE_D5_TEST2 0x80 /* Test output #2 */ + +/* DLCR6 -- hardware configuration #0 */ +#define FE_D6_BUFSIZ 0x03 /* Size of NIC buffer SRAM */ +#define FE_D6_TXBSIZ 0x0C /* Size (and config)of trans. buffer */ +#define FE_D6_BBW 0x10 /* Buffer SRAM bus width */ +#define FE_D6_SBW 0x20 /* System bus width */ +#define FE_D6_SRAM 0x40 /* Buffer SRAM access time */ +#define FE_D6_DLC 0x80 /* Disable DLC (recever/transmitter) */ + +#define FE_D6_BUFSIZ_8KB 0x00 /* The board has 8KB SRAM */ +#define FE_D6_BUFSIZ_16KB 0x01 /* The board has 16KB SRAM */ +#define FE_D6_BUFSIZ_32KB 0x02 /* The board has 32KB SRAM */ +#define FE_D6_BUFSIZ_64KB 0x03 /* The board has 64KB SRAM */ + +#define FE_D6_TXBSIZ_1x2KB 0x00 /* Single 2KB buffer for trans. */ +#define FE_D6_TXBSIZ_2x2KB 0x04 /* Double 2KB buffers */ +#define FE_D6_TXBSIZ_2x4KB 0x08 /* Double 4KB buffers */ +#define FE_D6_TXBSIZ_2x8KB 0x0C /* Double 8KB buffers */ + +#define FE_D6_BBW_WORD 0x00 /* SRAM has 16 bit data line */ +#define FE_D6_BBW_BYTE 0x10 /* SRAM has 8 bit data line */ + +#define FE_D6_SBW_WORD 0x00 /* Access with 16 bit (AT) bus */ +#define FE_D6_SBW_BYTE 0x20 /* Access with 8 bit (XT) bus */ + +#define FE_D6_SRAM_150ns 0x00 /* The board has slow SRAM */ +#define FE_D6_SRAM_100ns 0x40 /* The board has fast SRAM */ + +#define FE_D6_DLC_ENABLE 0x00 /* Normal operation */ +#define FE_D6_DLC_DISABLE 0x80 /* Stop sending/receiving */ + +/* DLC7 -- hardware configuration #1 */ +#define FE_D7_BYTSWP 0x01 /* Host byte order control */ +#define FE_D7_EOPPOL 0x02 /* Polarity of DMA EOP signal */ +#define FE_D7_RBS 0x0C /* Register bank select */ +#define FE_D7_RDYPNS 0x10 /* Senses RDYPNSEL input signal */ +#define FE_D7_POWER 0x20 /* Stand-by (power down) mode control */ +#define FE_D7_IDENT 0xC0 /* Chip identification */ + +#define FE_D7_BYTSWP_LH 0x00 /* DEC/Intel byte order */ +#define FE_D7_BYTSWP_HL 0x01 /* IBM/Motorolla byte order */ + +#define FE_D7_RBS_DLCR 0x00 /* Select DLCR8-15 */ +#define FE_D7_RBS_MAR 0x04 /* Select MAR8-15 */ +#define FE_D7_RBS_BMPR 0x08 /* Select BMPR8-15 */ + +#define FE_D7_POWER_DOWN 0x00 /* Power down (stand-by) mode */ +#define FE_D7_POWER_UP 0x20 /* Normal operation */ + +#define FE_D7_IDENT_TDK 0x00 /* TDK chips? */ +#define FE_D7_IDENT_NICE 0x80 /* Fujitsu NICE (86960) */ +#define FE_D7_IDENT_EC 0xC0 /* Fujitsu EtherCoupler (86965) */ + +/* DLCR8 thru DLCR13 are for Ethernet station address. */ + +/* DLCR14 and DLCR15 are for TDR. (TDR is used for cable diagnostic.) */ + +/* MAR8 thru MAR15 are for Multicast address filter. */ + +/* BMPR8 and BMPR9 are for packet data. */ + +/* BMPR10 -- transmitter start trigger */ +#define FE_B10_START 0x80 /* Start transmitter */ +#define FE_B10_COUNT 0x7F /* Packet count */ + +/* BMPR11 -- 16 collisions control */ +#define FE_B11_CTRL 0x01 /* Skip or resend errored packets */ +#define FE_B11_MODE1 0x02 /* Restart transmitter after COLL16 */ +#define FE_B11_MODE2 0x04 /* Automatic restart enable */ + +#define FE_B11_CTRL_RESEND 0x00 /* Re-send the collided packet */ +#define FE_B11_CTRL_SKIP 0x01 /* Skip the collided packet */ + +/* BMPR12 -- DMA enable */ +#define FE_B12_TXDMA 0x01 /* Enable transmitter DMA */ +#define FE_B12_RXDMA 0x02 /* Enable receiver DMA */ + +/* BMPR13 -- DMA control */ +#define FE_B13_BSTCTL 0x03 /* DMA burst mode control */ +#define FE_B13_TPTYPE 0x04 /* Twisted pair cable impedance */ +#define FE_B13_PORT 0x18 /* Port (TP/AUI) selection */ +#define FE_B13_LNKTST 0x20 /* Link test enable */ +#define FE_B13_SQTHLD 0x40 /* Lower squelch threshold */ +#define FE_B13_IOUNLK 0x80 /* Change I/O base address, on JLI mode */ + +#define FE_B13_BSTCTL_1 0x00 +#define FE_B13_BSTCTL_4 0x01 +#define FE_B13_BSTCTL_8 0x02 +#define FE_B13_BSTCLT_12 0x03 + +#define FE_B13_TPTYPE_UTP 0x00 /* Unshielded (standard) cable */ +#define FE_B13_TPTYPE_STP 0x04 /* Shielded (IBM) cable */ + +#define FE_B13_PORT_AUTO 0x00 /* Auto detected */ +#define FE_B13_PORT_TP 0x08 /* Force TP */ +#define FE_B13_PORT_AUI 0x18 /* Force AUI */ + +/* BMPR14 -- More receiver control and more transmission interrupts */ +#define FE_B14_FILTER 0x01 /* Filter out self-originated packets */ +#define FE_B14_SQE 0x02 /* SQE interrupt enable */ +#define FE_B14_SKIP 0x04 /* Skip a received packet */ +#define FE_B14_RJAB 0x20 /* RJAB interrupt enable */ +#define FE_B14_LLD 0x40 /* Local-link-down interrupt enable */ +#define FE_B14_RLD 0x80 /* Remote-link-down interrupt enable */ + +/* BMPR15 -- More transmitter status; basically same layout as BMPR14 */ +#define FE_B15_SQE FE_B14_SQE +#define FE_B15_RCVPOL 0x08 /* Reversed receive line polarity */ +#define FE_B15_RMTPRT 0x10 /* ??? */ +#define FE_B15_RAJB FE_B14_RJAB +#define FE_B15_LLD FE_B14_LLD +#define FE_B15_RLD FE_B14_RLD + +/* BMPR16 -- EEPROM control */ +#define FE_B16_DOUT 0x04 /* EEPROM Data in (CPU to EEPROM) */ +#define FE_B16_SELECT 0x20 /* EEPROM chip select */ +#define FE_B16_CLOCK 0x40 /* EEPROM shift clock */ +#define FE_B16_DIN 0x80 /* EEPROM data out (EEPROM to CPU) */ + +/* BMPR17 -- EEPROM data */ +#define FE_B17_DATA 0x80 /* EEPROM data bit */ + +/* BMPR18 -- cycle I/O address setting in JLI mode */ + +/* BMPR19 -- ISA interface configuration in JLI mode */ +#define FE_B19_IRQ 0xC0 +#define FE_B19_IRQ_SHIFT 6 + +#define FE_B19_ROM 0x38 +#define FE_B19_ROM_SHIFT 3 + +#define FE_B19_ADDR 0x07 +#define FE_B19_ADDR_SHIFT 0 + +/* + * An extra I/O port address to reset 86965. This location is called + * "ID ROM area" by Fujitsu document. + */ + +/* + * Flags in Receive Packet Header... Basically same layout as DLCR1. + */ +#define FE_RPH_OVRFLO FE_D1_OVRFLO +#define FE_RPH_CRCERR FE_D1_CRCERR +#define FE_RPH_ALGERR FE_D1_ALGERR +#define FE_RPH_SRTPKT FE_D1_SRTPKT +#define FE_RPH_RMTRST FE_D1_RMTRST +#define FE_RPH_GOOD 0x20 /* Good packet follows */ + +/* + * EEPROM specification (of JLI mode). + */ + +/* Number of bytes in an EEPROM accessible through 86965. */ +#define FE_EEPROM_SIZE 32 + +/* Offset for JLI config; automatically copied into BMPR19 at startup. */ +#define FE_EEPROM_CONF 0 + +/* + * Some 8696x specific constants. + */ + +/* Length (in bytes) of a Multicast Address Filter. */ +#define FE_FILTER_LEN 8 + +/* How many packets we can put in the transmission buffer on NIC memory. */ +#define FE_QUEUEING_MAX 127 + +/* Length (in bytes) of a "packet length" word in transmission buffer. */ +#define FE_DATA_LEN_LEN 2 + +/* Special Multicast Address Filter value. */ +#define FE_FILTER_NOTHING { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 } +#define FE_FILTER_ALL { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF } |
