diff options
Diffstat (limited to 'sys/dev/fe/if_fe_cbus.c')
-rw-r--r-- | sys/dev/fe/if_fe_cbus.c | 1018 |
1 files changed, 1018 insertions, 0 deletions
diff --git a/sys/dev/fe/if_fe_cbus.c b/sys/dev/fe/if_fe_cbus.c new file mode 100644 index 0000000..7d993f2 --- /dev/null +++ b/sys/dev/fe/if_fe_cbus.c @@ -0,0 +1,1018 @@ +/* + * 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$ + */ + +#include "opt_fe.h" +#include "opt_inet.h" +#include "opt_ipx.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/mbuf.h> +#include <sys/module.h> +#include <machine/clock.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 <i386/isa/ic/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 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); + + /* Probe for supported boards. */ +#ifdef PC98 + 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); +#endif + + 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[16]; + size = 16; + 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; + + switch (type) { + case FE_TYPE_CNET9NE: + iat = ioaddr_cnet9ne; + size = MAXREGISTERS; + break; + } + + 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->sc_enaddr, ETHER_ADDR_LEN); + + /* Make sure it is Allied-Telesis's. */ + if (!valid_Ether_p(sc->sc_enaddr, 0x0000F4)) + return ENXIO; +#if 1 + /* Calculate checksum. */ + sum = fe_inb(sc, 0x1e); + for (i = 0; i < ETHER_ADDR_LEN; i++) + sum ^= sc->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->sc_enaddr, ETHER_ADDR_LEN); + if (!valid_Ether_p(sc->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->sc_enaddr, ETHER_ADDR_LEN); + + /* Make sure it is Contec's. */ + if (!valid_Ether_p(sc->sc_enaddr, 0x00804C)) + return ENXIO; + + /* Determine the card type. */ + if (sc->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 (!valid_Ether_p(eeprom + FE_SSI_EEP_ADDR, 0x00804C)) + return ENXIO; + bcopy(eeprom + FE_SSI_EEP_ADDR, sc->sc_enaddr, ETHER_ADDR_LEN); + + /* Setup the board type. */ + sc->typestr = "C-NET(98)P2"; + + /* 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) +{ +#ifndef FE_8BIT_SUPPORT + device_printf(dev, + "skip LAC-98012/013(only 16-bit cards are supported)\n"); + return ENXIO; +#else + 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 (!valid_Ether_p(eeprom, 0x008098)) + return ENXIO; + bcopy(eeprom, sc->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; +#endif /* FE_8BIT_SUPPORT */ +} + + +/* + * 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->sc_enaddr, ETHER_ADDR_LEN); + if (!valid_Ether_p(sc->sc_enaddr, 0x000000)) + return ENXIO; + + /* Determine the card type. */ + sc->typestr = "Generic MB86960 Ethernet"; + if (valid_Ether_p(sc->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->sc_enaddr, ETHER_ADDR_LEN); + if (!valid_Ether_p(sc->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->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->sc_enaddr[i] = eeprom[7 - i]; + + /* Make sure it is RATOC's. */ + if (!valid_Ether_p(sc->sc_enaddr, 0x00C0D0) && + !valid_Ether_p(sc->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; +} |