ef544f6312
bus_setup_intr() o add an int return code to all fast handlers o retire INTR_FAST/IH_FAST For more info: http://docs.freebsd.org/cgi/getmsg.cgi?fetch=465712+0+current/freebsd-current Reviewed by: many Approved by: re@
1627 lines
39 KiB
C
1627 lines
39 KiB
C
/*-
|
|
* Copyright (c) 1995, David Greenman
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice unmodified, this list of conditions, and the following
|
|
* disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 CONTRIBUTORS 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 <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/kernel.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_arp.h>
|
|
#include <net/if_media.h>
|
|
#include <net/if_mib.h>
|
|
|
|
#include <isa/isavar.h>
|
|
|
|
#include <dev/ed/if_edvar.h>
|
|
#include <dev/ed/if_edreg.h>
|
|
#include <dev/ed/if_ed98.h>
|
|
|
|
static int ed98_alloc_port(device_t, int);
|
|
static int ed98_alloc_memory(device_t, int);
|
|
static int ed_pio_testmem(struct ed_softc *, int, int, int);
|
|
static int ed_probe_CNET98(device_t, int, int);
|
|
static int ed_probe_CNET98EL(device_t, int, int);
|
|
static int ed_probe_EZ98(device_t, int, int);
|
|
static int ed_probe_NEC77(device_t, int, int);
|
|
static int ed_probe_NW98X(device_t, int, int);
|
|
static int ed_probe_SB98(device_t, int, int);
|
|
static int ed_probe_SIC98(device_t, int, int);
|
|
static int ed98_probe_Novell(device_t, int, int);
|
|
static int ed98_probe_generic8390(struct ed_softc *);
|
|
static void ed_reset_CNET98(struct ed_softc *, int);
|
|
static void ed_winsel_CNET98(struct ed_softc *, u_short);
|
|
static void ed_get_SB98(struct ed_softc *);
|
|
|
|
static int ed_cbus_probe(device_t);
|
|
static int ed_cbus_attach(device_t);
|
|
|
|
static struct isa_pnp_id ed_ids[] = {
|
|
/* TODO - list up PnP boards for PC-98 */
|
|
{ 0, NULL }
|
|
};
|
|
|
|
static int
|
|
ed_cbus_probe(device_t dev)
|
|
{
|
|
struct ed_softc *sc = device_get_softc(dev);
|
|
int flags = device_get_flags(dev);
|
|
int error = 0;
|
|
|
|
sc->type = ED_TYPE98(flags);
|
|
#ifdef ED_DEBUG
|
|
device_printf(dev, "ed_cbus_probe: sc->type=%x\n", sc->type);
|
|
#endif
|
|
|
|
/* Check isapnp ids */
|
|
error = ISA_PNP_PROBE(device_get_parent(dev), dev, ed_ids);
|
|
#ifdef ED_DEBUG
|
|
device_printf(dev, "ed_cbus_probe: ISA_PNP_PROBE returns %d\n", error);
|
|
#endif
|
|
|
|
/* 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;
|
|
|
|
/* Heuristic probes */
|
|
#ifdef ED_DEBUG
|
|
device_printf(dev, "ed_cbus_probe: Heuristic probes start\n");
|
|
#endif
|
|
switch (sc->type) {
|
|
case ED_TYPE98_GENERIC:
|
|
/*
|
|
* CAUTION!
|
|
* sc->type of these boards are overwritten by PC/AT's value.
|
|
*/
|
|
|
|
/*
|
|
* SMC EtherEZ98
|
|
*/
|
|
error = ed_probe_EZ98(dev, 0, flags);
|
|
if (error == 0)
|
|
goto end;
|
|
|
|
ed_release_resources(dev);
|
|
|
|
/*
|
|
* Allied Telesis CenterCom LA-98-T
|
|
*/
|
|
error = ed_probe_Novell(dev, 0, flags);
|
|
if (error == 0) {
|
|
ed_Novell_read_mac(sc);
|
|
goto end;
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* NE2000-like boards probe routine
|
|
*/
|
|
case ED_TYPE98_BDN:
|
|
/*
|
|
* ELECOM LANEED LD-BDN
|
|
* PLANET SMART COM 98 EN-2298
|
|
*/
|
|
case ED_TYPE98_LGY:
|
|
/*
|
|
* MELCO LGY-98, IND-SP, IND-SS
|
|
* MACNICA NE2098
|
|
*/
|
|
case ED_TYPE98_ICM:
|
|
/*
|
|
* ICM DT-ET-25, DT-ET-T5, IF-2766ET, IF-2771ET
|
|
* D-Link DE-298P, DE-298
|
|
*/
|
|
case ED_TYPE98_EGY:
|
|
/*
|
|
* MELCO EGY-98
|
|
* Contec C-NET(98)E-A, C-NET(98)L-A
|
|
*/
|
|
case ED_TYPE98_108:
|
|
/*
|
|
* NEC PC-9801-107,108
|
|
*/
|
|
case ED_TYPE98_NC5098:
|
|
/*
|
|
* NextCom NC5098
|
|
*/
|
|
error = ed98_probe_Novell(dev, 0, flags);
|
|
break;
|
|
|
|
/*
|
|
* other boards with special probe routine
|
|
*/
|
|
case ED_TYPE98_SIC:
|
|
/*
|
|
* Allied Telesis SIC-98
|
|
*/
|
|
error = ed_probe_SIC98(dev, 0, flags);
|
|
break;
|
|
|
|
case ED_TYPE98_CNET98EL:
|
|
/*
|
|
* Contec C-NET(98)E/L
|
|
*/
|
|
error = ed_probe_CNET98EL(dev, 0, flags);
|
|
break;
|
|
|
|
case ED_TYPE98_CNET98:
|
|
/*
|
|
* Contec C-NET(98)
|
|
*/
|
|
error = ed_probe_CNET98(dev, 0, flags);
|
|
break;
|
|
|
|
case ED_TYPE98_LA98:
|
|
/*
|
|
* IO-DATA LA/T-98
|
|
* NEC PC-9801-77,78
|
|
*/
|
|
error = ed_probe_NEC77(dev, 0, flags);
|
|
break;
|
|
|
|
case ED_TYPE98_NW98X:
|
|
/*
|
|
* Networld EC/EP-98X
|
|
*/
|
|
error = ed_probe_NW98X(dev, 0, flags);
|
|
break;
|
|
|
|
case ED_TYPE98_SB98:
|
|
/*
|
|
* Soliton SB-9801
|
|
* Fujikura FN-9801
|
|
*/
|
|
error = ed_probe_SB98(dev, 0, flags);
|
|
break;
|
|
}
|
|
|
|
end:
|
|
#ifdef ED_DEBUG
|
|
device_printf(dev, "ed_cbus_probe: end, error=%d\n", error);
|
|
#endif
|
|
if (error == 0)
|
|
error = ed_alloc_irq(dev, 0, 0);
|
|
|
|
ed_release_resources(dev);
|
|
return (error);
|
|
}
|
|
|
|
static int
|
|
ed_cbus_attach(dev)
|
|
device_t dev;
|
|
{
|
|
struct ed_softc *sc = device_get_softc(dev);
|
|
int flags = device_get_flags(dev);
|
|
int error;
|
|
|
|
if (sc->port_used > 0) {
|
|
if (ED_TYPE98(flags) == ED_TYPE98_GENERIC)
|
|
ed_alloc_port(dev, sc->port_rid, sc->port_used);
|
|
else
|
|
ed98_alloc_port(dev, sc->port_rid);
|
|
}
|
|
if (sc->mem_used)
|
|
ed_alloc_memory(dev, sc->mem_rid, sc->mem_used);
|
|
|
|
ed_alloc_irq(dev, sc->irq_rid, 0);
|
|
|
|
error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET | INTR_MPSAFE,
|
|
NULL, edintr, sc, &sc->irq_handle);
|
|
if (error) {
|
|
ed_release_resources(dev);
|
|
return (error);
|
|
}
|
|
|
|
return ed_attach(dev);
|
|
}
|
|
|
|
/*
|
|
* Interrupt conversion table for EtherEZ98
|
|
*/
|
|
static uint16_t ed_EZ98_intr_val[] = {
|
|
0,
|
|
3,
|
|
5,
|
|
6,
|
|
0,
|
|
9,
|
|
12,
|
|
13
|
|
};
|
|
|
|
static int
|
|
ed_probe_EZ98(device_t dev, int port_rid, int flags)
|
|
{
|
|
struct ed_softc *sc = device_get_softc(dev);
|
|
int error;
|
|
static unsigned short *intr_vals[] = {NULL, ed_EZ98_intr_val};
|
|
|
|
error = ed_alloc_port(dev, port_rid, ED_EZ98_IO_PORTS);
|
|
if (error) {
|
|
return (error);
|
|
}
|
|
|
|
sc->asic_offset = ED_EZ98_ASIC_OFFSET;
|
|
sc->nic_offset = ED_EZ98_NIC_OFFSET;
|
|
|
|
return ed_probe_WD80x3_generic(dev, flags, intr_vals);
|
|
}
|
|
|
|
/*
|
|
* I/O conversion tables
|
|
*/
|
|
|
|
/* LGY-98, ICM, C-NET(98)E/L */
|
|
static bus_addr_t ed98_ioaddr_generic[] = {
|
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
|
|
};
|
|
|
|
/*
|
|
* Definitions for Contec C-NET(98)E/L
|
|
*/
|
|
#define ED_CNET98EL_ICR 2 /* Interrupt Configuration Register */
|
|
|
|
#define ED_CNET98EL_ICR_IRQ3 0x01
|
|
#define ED_CNET98EL_ICR_IRQ5 0x02
|
|
#define ED_CNET98EL_ICR_IRQ6 0x04
|
|
#define ED_CNET98EL_ICR_IRQ12 0x20
|
|
|
|
#define ED_CNET98EL_IMR 4 /* Interrupt Mask Register */
|
|
#define ED_CNET98EL_ISR 5 /* Interrupt Status Register */
|
|
|
|
/* EGY-98 */
|
|
static bus_addr_t ed98_ioaddr_egy98[] = {
|
|
0, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e,
|
|
0x100, 0x102, 0x104, 0x106, 0x108, 0x10a, 0x10c, 0x10e
|
|
};
|
|
|
|
/* SIC-98 */
|
|
static bus_addr_t ed98_ioaddr_sic98[] = {
|
|
0x0000, 0x0200, 0x0400, 0x0600, 0x0800, 0x0a00, 0x0c00, 0x0e00,
|
|
0x1000, 0x1200, 0x1400, 0x1600, 0x1800, 0x1a00, 0x1c00, 0x1e00
|
|
};
|
|
|
|
/* LA/T-98, LD-BDN, PC-9801-77, SB-9801 */
|
|
static bus_addr_t ed98_ioaddr_la98[] = {
|
|
0x0000, 0x1000, 0x2000, 0x3000, 0x4000, 0x5000, 0x6000, 0x7000,
|
|
0x8000, 0x9000, 0xa000, 0xb000, 0xc000, 0xd000, 0xe000, 0xf000,
|
|
0x0100 /* for NEC 77(see below) */
|
|
};
|
|
|
|
/*
|
|
* Definitions for NEC PC-9801-77
|
|
*/
|
|
#define ED_NEC77_IRQ 16 /* Interrupt Configuration Register */
|
|
|
|
#define ED_NEC77_IRQ3 0x04
|
|
#define ED_NEC77_IRQ5 0x06
|
|
#define ED_NEC77_IRQ6 0x08
|
|
#define ED_NEC77_IRQ12 0x0a
|
|
#define ED_NEC77_IRQ13 0x02
|
|
|
|
/*
|
|
* Definitions for Soliton SB-9801
|
|
*/
|
|
#define ED_SB98_CFG 1 /* Board configuration */
|
|
|
|
#define ED_SB98_CFG_IRQ3 0x00
|
|
#define ED_SB98_CFG_IRQ5 0x04
|
|
#define ED_SB98_CFG_IRQ6 0x08
|
|
#define ED_SB98_CFG_IRQ12 0x0c
|
|
#define ED_SB98_CFG_ALTPORT 0x40 /* use EXTERNAL media */
|
|
#define ED_SB98_CFG_ENABLE 0xa0 /* enable configuration */
|
|
|
|
#define ED_SB98_EEPENA 2 /* EEPROM access enable */
|
|
|
|
#define ED_SB98_EEPENA_DISABLE 0x00
|
|
#define ED_SB98_EEPENA_ENABLE 0x01
|
|
|
|
#define ED_SB98_EEP 3 /* EEPROM access */
|
|
|
|
#define ED_SB98_EEP_SDA 0x01 /* Serial Data */
|
|
#define ED_SB98_EEP_SCL 0x02 /* Serial Clock */
|
|
#define ED_SB98_EEP_READ 0x01 /* Read Command */
|
|
|
|
#define ED_SB98_EEP_DELAY 300
|
|
|
|
#define ED_SB98_ADDRESS 0x01 /* Station Address(1-6) */
|
|
|
|
#define ED_SB98_POLARITY 4 /* Polarity */
|
|
|
|
/* PC-9801-108 */
|
|
static bus_addr_t ed98_ioaddr_nec108[] = {
|
|
0x0000, 0x0002, 0x0004, 0x0006, 0x0008, 0x000a, 0x000c, 0x000e,
|
|
0x1000, 0x1002, 0x1004, 0x1006, 0x1008, 0x100a, 0x100c, 0x100e
|
|
};
|
|
|
|
/* C-NET(98) */
|
|
static bus_addr_t ed98_ioaddr_cnet98[] = {
|
|
0x0000, 0x0002, 0x0004, 0x0006, 0x0008, 0x000a, 0x000c, 0x000e,
|
|
0x0400, 0x0402, 0x0404, 0x0406, 0x0408, 0x040a, 0x040c, 0x040e
|
|
};
|
|
|
|
/*
|
|
* Definitions for Contec C-NET(98)
|
|
*/
|
|
#define ED_CNET98_MAP_REG0L 0 /* MAPPING register0 Low */
|
|
#define ED_CNET98_MAP_REG1L 1 /* MAPPING register1 Low */
|
|
#define ED_CNET98_MAP_REG2L 2 /* MAPPING register2 Low */
|
|
#define ED_CNET98_MAP_REG3L 3 /* MAPPING register3 Low */
|
|
#define ED_CNET98_MAP_REG0H 4 /* MAPPING register0 Hi */
|
|
#define ED_CNET98_MAP_REG1H 5 /* MAPPING register1 Hi */
|
|
#define ED_CNET98_MAP_REG2H 6 /* MAPPING register2 Hi */
|
|
#define ED_CNET98_MAP_REG3H 7 /* MAPPING register3 Hi */
|
|
#define ED_CNET98_WIN_REG 8 /* Window register */
|
|
#define ED_CNET98_INT_LEV 9 /* Init level register */
|
|
|
|
#define ED_CNET98_INT_IRQ3 0x01 /* INT 0 */
|
|
#define ED_CNET98_INT_IRQ5 0x02 /* INT 1 */
|
|
#define ED_CNET98_INT_IRQ6 0x04 /* INT 2 */
|
|
#define ED_CNET98_INT_IRQ9 0x08 /* INT 3 */
|
|
#define ED_CNET98_INT_IRQ12 0x20 /* INT 5 */
|
|
#define ED_CNET98_INT_IRQ13 0x40 /* INT 6 */
|
|
|
|
#define ED_CNET98_INT_REQ 10 /* Init request register */
|
|
#define ED_CNET98_INT_MASK 11 /* Init mask register */
|
|
#define ED_CNET98_INT_STAT 12 /* Init status register */
|
|
#define ED_CNET98_INT_CLR 12 /* Init clear register */
|
|
#define ED_CNET98_RESERVE1 13
|
|
#define ED_CNET98_RESERVE2 14
|
|
#define ED_CNET98_RESERVE3 15
|
|
|
|
/* EC/EP-98X, NC5098 */
|
|
static bus_addr_t ed98_ioaddr_nw98x[] = {
|
|
0x0000, 0x0100, 0x0200, 0x0300, 0x0400, 0x0500, 0x0600, 0x0700,
|
|
0x0800, 0x0900, 0x0a00, 0x0b00, 0x0c00, 0x0d00, 0x0e00, 0x0f00,
|
|
0x1000 /* for EC/EP-98X(see below) */
|
|
};
|
|
|
|
/*
|
|
* Definitions for Networld EC/EP-98X
|
|
*/
|
|
#define ED_NW98X_IRQ 16 /* Interrupt Configuration Register */
|
|
|
|
#define ED_NW98X_IRQ3 0x04
|
|
#define ED_NW98X_IRQ5 0x06
|
|
#define ED_NW98X_IRQ6 0x08
|
|
#define ED_NW98X_IRQ12 0x0a
|
|
#define ED_NW98X_IRQ13 0x02
|
|
|
|
/* NC5098 ASIC */
|
|
static bus_addr_t ed98_asic_nc5098[] = {
|
|
/* DATA ENADDR RESET */
|
|
0x0000, 0x2000, 0x2100, 0x2200, 0x2300, 0x2400, 0x2500, 0x4000,
|
|
0, 0, 0, 0, 0, 0, 0, 0
|
|
};
|
|
|
|
/*
|
|
* Definitions for NextCom NC5098
|
|
*/
|
|
#define ED_NC5098_ENADDR 1 /* Station Address(1-6) */
|
|
|
|
/*
|
|
* Allocate a port resource with the given resource id.
|
|
*/
|
|
static int
|
|
ed98_alloc_port(device_t dev, int rid)
|
|
{
|
|
struct ed_softc *sc = device_get_softc(dev);
|
|
struct resource *res;
|
|
int error;
|
|
bus_addr_t *io_nic, *io_asic, adj;
|
|
static bus_addr_t io_res[ED_NOVELL_IO_PORTS + 1];
|
|
int i, n;
|
|
int offset, reset, data;
|
|
|
|
/* Set i/o table for resource manager */
|
|
io_nic = io_asic = ed98_ioaddr_generic;
|
|
offset = ED_NOVELL_ASIC_OFFSET;
|
|
reset = ED_NOVELL_RESET;
|
|
data = ED_NOVELL_DATA;
|
|
n = ED_NOVELL_IO_PORTS;
|
|
|
|
switch (sc->type) {
|
|
case ED_TYPE98_LGY:
|
|
io_asic = ed98_ioaddr_egy98; /* XXX - Yes, we use egy98 */
|
|
offset = 0x0200;
|
|
reset = 8;
|
|
break;
|
|
|
|
case ED_TYPE98_EGY:
|
|
io_nic = io_asic = ed98_ioaddr_egy98;
|
|
offset = 0x0200;
|
|
reset = 8;
|
|
break;
|
|
|
|
case ED_TYPE98_ICM:
|
|
offset = 0x0100;
|
|
break;
|
|
|
|
case ED_TYPE98_BDN:
|
|
io_nic = io_asic = ed98_ioaddr_la98;
|
|
offset = 0x0100;
|
|
reset = 0x0c;
|
|
break;
|
|
|
|
case ED_TYPE98_SIC:
|
|
io_nic = io_asic = ed98_ioaddr_sic98;
|
|
offset = 0x2000;
|
|
n = 16+1;
|
|
break;
|
|
|
|
case ED_TYPE98_108:
|
|
io_nic = io_asic = ed98_ioaddr_nec108;
|
|
offset = 0x0888; /* XXX - overwritten after */
|
|
reset = 1;
|
|
n = 16; /* XXX - does not set ASIC i/o here */
|
|
break;
|
|
|
|
case ED_TYPE98_LA98:
|
|
io_nic = io_asic = ed98_ioaddr_la98;
|
|
offset = 0x0100;
|
|
break;
|
|
|
|
case ED_TYPE98_CNET98EL:
|
|
offset = 0x0400;
|
|
data = 0x0e;
|
|
break;
|
|
|
|
case ED_TYPE98_CNET98:
|
|
/* XXX - Yes, we use generic i/o here */
|
|
offset = 0x0400;
|
|
break;
|
|
|
|
case ED_TYPE98_NW98X:
|
|
io_nic = io_asic = ed98_ioaddr_nw98x;
|
|
offset = 0x1000;
|
|
break;
|
|
|
|
case ED_TYPE98_SB98:
|
|
io_nic = io_asic = ed98_ioaddr_la98;
|
|
offset = 0x0400;
|
|
reset = 7;
|
|
break;
|
|
|
|
case ED_TYPE98_NC5098:
|
|
io_nic = ed98_ioaddr_nw98x;
|
|
io_asic = ed98_asic_nc5098;
|
|
offset = 0x2000;
|
|
reset = 7;
|
|
n = 16+8; /* XXX */
|
|
break;
|
|
}
|
|
|
|
bcopy(io_nic, io_res, sizeof(io_nic[0]) * ED_NOVELL_ASIC_OFFSET);
|
|
for (i = ED_NOVELL_ASIC_OFFSET; i < ED_NOVELL_IO_PORTS; i++)
|
|
io_res[i] = io_asic[i - ED_NOVELL_ASIC_OFFSET] + offset;
|
|
|
|
res = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid, io_res, n,
|
|
RF_ACTIVE);
|
|
if (!res)
|
|
return (ENOENT);
|
|
|
|
sc->port_rid = rid;
|
|
sc->port_res = res;
|
|
sc->port_used = n;
|
|
sc->port_bst = rman_get_bustag(res);
|
|
sc->port_bsh = rman_get_bushandle(res);
|
|
|
|
/* Re-map i/o table if needed */
|
|
switch (sc->type) {
|
|
case ED_TYPE98_LA98:
|
|
case ED_TYPE98_NW98X:
|
|
io_res[n] = io_asic[n - ED_NOVELL_ASIC_OFFSET] + offset;
|
|
n++;
|
|
break;
|
|
|
|
case ED_TYPE98_108:
|
|
adj = (rman_get_start(res) & 0xf000) / 2;
|
|
offset = (offset | adj) - rman_get_start(res);
|
|
|
|
for (n = ED_NOVELL_ASIC_OFFSET; n < ED_NOVELL_IO_PORTS; n++)
|
|
io_res[n] = io_asic[n - ED_NOVELL_ASIC_OFFSET] + offset;
|
|
break;
|
|
|
|
case ED_TYPE98_CNET98:
|
|
io_nic = io_asic = ed98_ioaddr_cnet98;
|
|
offset = 1;
|
|
|
|
bcopy(io_nic, io_res, sizeof(io_nic[0]) * ED_NOVELL_ASIC_OFFSET);
|
|
for (n = ED_NOVELL_ASIC_OFFSET; n < ED_NOVELL_IO_PORTS; n++)
|
|
io_res[n] = io_asic[n - ED_NOVELL_ASIC_OFFSET] + offset;
|
|
break;
|
|
|
|
case ED_TYPE98_NC5098:
|
|
n = ED_NOVELL_IO_PORTS;
|
|
break;
|
|
}
|
|
|
|
if (reset != ED_NOVELL_RESET)
|
|
io_res[ED_NOVELL_ASIC_OFFSET + ED_NOVELL_RESET] =
|
|
io_res[ED_NOVELL_ASIC_OFFSET + reset];
|
|
if (data != ED_NOVELL_DATA) {
|
|
io_res[ED_NOVELL_ASIC_OFFSET + ED_NOVELL_DATA] =
|
|
io_res[ED_NOVELL_ASIC_OFFSET + data];
|
|
#if 0
|
|
io_res[ED_NOVELL_ASIC_OFFSET + ED_NOVELL_DATA + 1] =
|
|
io_res[ED_NOVELL_ASIC_OFFSET + data + 1];
|
|
#endif
|
|
}
|
|
|
|
error = isa_load_resourcev(res, io_res, n);
|
|
if (error != 0)
|
|
return (ENOENT);
|
|
#ifdef ED_DEBUG
|
|
device_printf(dev, "ed98_alloc_port: i/o ports = %d\n", n);
|
|
for (i = 0; i < n; i++)
|
|
printf("%x,", io_res[i]);
|
|
printf("\n");
|
|
#endif
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
ed98_alloc_memory(dev, rid)
|
|
device_t dev;
|
|
int rid;
|
|
{
|
|
struct ed_softc *sc = device_get_softc(dev);
|
|
int error;
|
|
u_long conf_maddr, conf_msize;
|
|
|
|
error = bus_get_resource(dev, SYS_RES_MEMORY, 0, &conf_maddr,
|
|
&conf_msize);
|
|
if (error)
|
|
return (error);
|
|
|
|
if ((conf_maddr == 0) || (conf_msize == 0))
|
|
return (ENXIO);
|
|
|
|
error = ed_alloc_memory(dev, rid, (int) conf_msize);
|
|
if (error)
|
|
return (error);
|
|
|
|
sc->mem_start = 0;
|
|
sc->mem_size = conf_msize;
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Generic probe routine for testing for the existance of a DS8390.
|
|
* Must be called after the NIC has just been reset. This routine
|
|
* works by looking at certain register values that are guaranteed
|
|
* to be initialized a certain way after power-up or reset. Seems
|
|
* not to currently work on the 83C690.
|
|
*
|
|
* Specifically:
|
|
*
|
|
* Register reset bits set bits
|
|
* Command Register (CR) TXP, STA RD2, STP
|
|
* Interrupt Status (ISR) RST
|
|
* Interrupt Mask (IMR) All bits
|
|
* Data Control (DCR) LAS
|
|
* Transmit Config. (TCR) LB1, LB0
|
|
*
|
|
* XXX - We only check the CR register.
|
|
*
|
|
* Return 1 if 8390 was found, 0 if not.
|
|
*/
|
|
|
|
static int
|
|
ed98_probe_generic8390(struct ed_softc *sc)
|
|
{
|
|
u_char tmp = ed_nic_inb(sc, ED_P0_CR);
|
|
#ifdef DIAGNOSTIC
|
|
printf("ed?: inb(ED_P0_CR)=%x\n", tmp);
|
|
#endif
|
|
if ((tmp & (ED_CR_RD2 | ED_CR_TXP | ED_CR_STA | ED_CR_STP)) !=
|
|
(ED_CR_RD2 | ED_CR_STP))
|
|
return (0);
|
|
|
|
(void) ed_nic_inb(sc, ED_P0_ISR);
|
|
|
|
return (1);
|
|
}
|
|
|
|
static int
|
|
ed98_probe_Novell(device_t dev, int port_rid, int flags)
|
|
{
|
|
struct ed_softc *sc = device_get_softc(dev);
|
|
int error;
|
|
int n;
|
|
u_char romdata[ETHER_ADDR_LEN * 2], tmp;
|
|
|
|
#ifdef ED_DEBUG
|
|
device_printf(dev, "ed98_probe_Novell: start\n");
|
|
#endif
|
|
error = ed98_alloc_port(dev, port_rid);
|
|
if (error)
|
|
return (error);
|
|
|
|
sc->asic_offset = ED_NOVELL_ASIC_OFFSET;
|
|
sc->nic_offset = ED_NOVELL_NIC_OFFSET;
|
|
|
|
/* Reset the board */
|
|
#ifdef ED_DEBUG
|
|
device_printf(dev, "ed98_probe_Novell: reset\n");
|
|
#endif
|
|
switch (sc->type) {
|
|
#if 1 /* XXX - I'm not sure this is really necessary... */
|
|
case ED_TYPE98_BDN:
|
|
tmp = ed_asic_inb(sc, ED_NOVELL_RESET);
|
|
ed_asic_outb(sc, ED_NOVELL_RESET, (tmp & 0xf0) | 0x08);
|
|
ed_nic_outb(sc, 0x04, tmp);
|
|
(void) ed_asic_inb(sc, 0x08);
|
|
ed_asic_outb(sc, 0x08, tmp);
|
|
ed_asic_outb(sc, 0x08, tmp & 0x7f);
|
|
break;
|
|
#endif
|
|
case ED_TYPE98_NC5098:
|
|
ed_asic_outb(sc, ED_NOVELL_RESET, 0x00);
|
|
DELAY(5000);
|
|
ed_asic_outb(sc, ED_NOVELL_RESET, 0x01);
|
|
break;
|
|
|
|
default:
|
|
tmp = ed_asic_inb(sc, ED_NOVELL_RESET);
|
|
|
|
/*
|
|
* I don't know if this is necessary; probably cruft leftover from
|
|
* Clarkson packet driver code. Doesn't do a thing on the boards I've
|
|
* tested. -DG [note that an outb(0x84, 0) seems to work here, and is
|
|
* non-invasive...but some boards don't seem to reset and I don't have
|
|
* complete documentation on what the 'right' thing to do is...so we
|
|
* do the invasive thing for now. Yuck.]
|
|
*/
|
|
ed_asic_outb(sc, ED_NOVELL_RESET, tmp);
|
|
break;
|
|
}
|
|
DELAY(5000);
|
|
|
|
/*
|
|
* This is needed because some NE clones apparently don't reset the
|
|
* NIC properly (or the NIC chip doesn't reset fully on power-up) XXX
|
|
* - this makes the probe invasive! ...Done against my better
|
|
* judgement. -DLG
|
|
*/
|
|
ed_nic_outb(sc, ED_P0_CR, ED_CR_RD2 | ED_CR_STP);
|
|
DELAY(5000);
|
|
|
|
/* Make sure that we really have an 8390 based board */
|
|
if (!ed98_probe_generic8390(sc))
|
|
return (ENXIO);
|
|
|
|
/* Test memory via PIO */
|
|
#ifdef ED_DEBUG
|
|
device_printf(dev, "ed98_probe_Novell: test memory\n");
|
|
#endif
|
|
sc->cr_proto = ED_CR_RD2;
|
|
if (!ed_pio_testmem(sc, 8192, 0, flags) &&
|
|
!ed_pio_testmem(sc, 16384, 1, flags))
|
|
return (ENXIO);
|
|
|
|
/* Setup the board type */
|
|
#ifdef ED_DEBUG
|
|
device_printf(dev, "ed98_probe_Novell: board type\n");
|
|
#endif
|
|
switch (sc->type) {
|
|
case ED_TYPE98_BDN:
|
|
sc->type_str = "LD-BDN";
|
|
break;
|
|
case ED_TYPE98_EGY:
|
|
sc->type_str = "EGY-98";
|
|
break;
|
|
case ED_TYPE98_LGY:
|
|
sc->type_str = "LGY-98";
|
|
break;
|
|
case ED_TYPE98_ICM:
|
|
sc->type_str = "ICM";
|
|
break;
|
|
case ED_TYPE98_108:
|
|
sc->type_str = "PC-9801-108";
|
|
break;
|
|
case ED_TYPE98_LA98:
|
|
sc->type_str = "LA-98";
|
|
break;
|
|
case ED_TYPE98_NW98X:
|
|
sc->type_str = "NW98X";
|
|
break;
|
|
case ED_TYPE98_NC5098:
|
|
sc->type_str = "NC5098";
|
|
break;
|
|
default:
|
|
sc->type_str = NULL;
|
|
break;
|
|
}
|
|
|
|
/* Get station address */
|
|
switch (sc->type) {
|
|
case ED_TYPE98_NC5098:
|
|
for (n = 0; n < ETHER_ADDR_LEN; n++)
|
|
sc->enaddr[n] = ed_asic_inb(sc, ED_NC5098_ENADDR + n);
|
|
break;
|
|
|
|
default:
|
|
ed_pio_readmem(sc, 0, romdata, sizeof(romdata));
|
|
for (n = 0; n < ETHER_ADDR_LEN; n++)
|
|
sc->enaddr[n] = romdata[n * (sc->isa16bit + 1)];
|
|
break;
|
|
}
|
|
|
|
/* clear any pending interrupts that might have occurred above */
|
|
ed_nic_outb(sc, ED_P0_ISR, 0xff);
|
|
|
|
sc->sc_write_mbufs = ed_pio_write_mbufs;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Probe and vendor-specific initialization routine for SIC-98 boards
|
|
*/
|
|
static int
|
|
ed_probe_SIC98(device_t dev, int port_rid, int flags)
|
|
{
|
|
struct ed_softc *sc = device_get_softc(dev);
|
|
int error;
|
|
int i;
|
|
u_char sum;
|
|
|
|
/*
|
|
* Setup card RAM and I/O address
|
|
* Kernel Virtual to segment C0000-DFFFF????
|
|
*/
|
|
error = ed98_alloc_port(dev, port_rid);
|
|
if (error)
|
|
return (error);
|
|
|
|
sc->asic_offset = ED_SIC_ASIC_OFFSET;
|
|
sc->nic_offset = ED_SIC_NIC_OFFSET;
|
|
|
|
error = ed98_alloc_memory(dev, 0);
|
|
if (error)
|
|
return (error);
|
|
|
|
/* Reset card to force it into a known state. */
|
|
ed_asic_outb(sc, 0, 0x00);
|
|
DELAY(100);
|
|
if (ED_TYPE98SUB(flags) == 0) {
|
|
/* SIC-98/SIU-98 */
|
|
ed_asic_outb(sc, 0, 0x94);
|
|
DELAY(100);
|
|
ed_asic_outb(sc, 0, 0x94);
|
|
} else {
|
|
/* SIU-98-D */
|
|
ed_asic_outb(sc, 0, 0x80);
|
|
DELAY(100);
|
|
ed_asic_outb(sc, 0, 0x94);
|
|
DELAY(100);
|
|
ed_asic_outb(sc, 0, 0x9e);
|
|
}
|
|
DELAY(100);
|
|
|
|
/*
|
|
* Here we check the card ROM, if the checksum passes, and the
|
|
* type code and ethernet address check out, then we know we have
|
|
* an SIC card.
|
|
*/
|
|
sum = bus_space_read_1(sc->mem_bst, sc->mem_bsh, 6 * 2);
|
|
for (i = 0; i < ETHER_ADDR_LEN; i++)
|
|
sum ^= (sc->enaddr[i] =
|
|
bus_space_read_1(sc->mem_bst, sc->mem_bsh, i * 2));
|
|
#ifdef ED_DEBUG
|
|
device_printf(dev, "ed_probe_sic98: got address %6D\n",
|
|
sc->enaddr, ":");
|
|
#endif
|
|
if (sum != 0)
|
|
return (ENXIO);
|
|
if ((sc->enaddr[0] | sc->enaddr[1] | sc->enaddr[2]) == 0)
|
|
return (ENXIO);
|
|
|
|
sc->vendor = ED_VENDOR_SIC;
|
|
sc->type_str = "SIC98";
|
|
sc->isa16bit = 1;
|
|
sc->cr_proto = 0;
|
|
|
|
/*
|
|
* SIC RAM page 0x0000-0x3fff(or 0x7fff)
|
|
*/
|
|
if (ED_TYPE98SUB(flags) == 0)
|
|
ed_asic_outb(sc, 0, 0x90);
|
|
else
|
|
ed_asic_outb(sc, 0, 0x8e);
|
|
DELAY(100);
|
|
|
|
error = ed_clear_memory(dev);
|
|
if (error)
|
|
return (error);
|
|
|
|
sc->mem_shared = 1;
|
|
sc->mem_end = sc->mem_start + sc->mem_size;
|
|
|
|
/*
|
|
* allocate one xmit buffer if < 16k, two buffers otherwise
|
|
*/
|
|
if ((sc->mem_size < 16384) || (flags & ED_FLAGS_NO_MULTI_BUFFERING))
|
|
sc->txb_cnt = 1;
|
|
else
|
|
sc->txb_cnt = 2;
|
|
sc->tx_page_start = 0;
|
|
|
|
sc->rec_page_start = sc->tx_page_start + ED_TXBUF_SIZE * sc->txb_cnt;
|
|
sc->rec_page_stop = sc->tx_page_start + sc->mem_size / ED_PAGE_SIZE;
|
|
|
|
sc->mem_ring = sc->mem_start + sc->txb_cnt * ED_PAGE_SIZE * ED_TXBUF_SIZE;
|
|
|
|
sc->sc_write_mbufs = ed_shmem_write_mbufs;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Contec C-NET(98) series support routines
|
|
*/
|
|
static void
|
|
ed_reset_CNET98(struct ed_softc *sc, int flags)
|
|
{
|
|
u_int init_addr = ED_CNET98_INIT;
|
|
u_char tmp;
|
|
|
|
/* Choose initial register address */
|
|
if (ED_TYPE98SUB(flags) != 0) {
|
|
init_addr = ED_CNET98_INIT2;
|
|
}
|
|
#ifdef ED_DEBUG
|
|
printf("ed?: initial register=%x\n", init_addr);
|
|
#endif
|
|
/*
|
|
* Reset the board to force it into a known state.
|
|
*/
|
|
outb(init_addr, 0x00); /* request */
|
|
DELAY(5000);
|
|
outb(init_addr, 0x01); /* cancel */
|
|
DELAY(5000);
|
|
|
|
/*
|
|
* Set I/O address(A15-12) and cpu type
|
|
*
|
|
* AAAAIXXC(8bit)
|
|
* AAAA: A15-A12, I: I/O enable, XX: reserved, C: CPU type
|
|
*
|
|
* CPU type is 1:80286 or higher, 0:not.
|
|
* But FreeBSD runs under i386 or higher, thus it must be 1.
|
|
*/
|
|
tmp = (rman_get_start(sc->port_res) & 0xf000) >> 8;
|
|
tmp |= (0x08 | 0x01);
|
|
#ifdef ED_DEBUG
|
|
printf("ed?: outb(%x, %x)\n", init_addr + 2, tmp);
|
|
#endif
|
|
outb(init_addr + 2, tmp);
|
|
DELAY(5000);
|
|
|
|
/*
|
|
* This is needed because some NE clones apparently don't reset the
|
|
* NIC properly (or the NIC chip doesn't reset fully on power-up) XXX
|
|
* - this makes the probe invasive! ...Done against my better
|
|
* judgement. -DLG
|
|
*/
|
|
ed_nic_outb(sc, ED_P0_CR, ED_CR_RD2 | ED_CR_STP);
|
|
DELAY(5000);
|
|
}
|
|
|
|
static void
|
|
ed_winsel_CNET98(struct ed_softc *sc, u_short bank)
|
|
{
|
|
u_char mem = (rman_get_start(sc->mem_res) >> 12) & 0xff;
|
|
|
|
/*
|
|
* Disable window memory
|
|
* bit7 is 0:disable
|
|
*/
|
|
ed_asic_outb(sc, ED_CNET98_WIN_REG, mem & 0x7f);
|
|
DELAY(10);
|
|
|
|
/*
|
|
* Select window address
|
|
* FreeBSD address 0xf00xxxxx
|
|
*/
|
|
ed_asic_outb(sc, ED_CNET98_MAP_REG0L, bank & 0xff);
|
|
DELAY(10);
|
|
ed_asic_outb(sc, ED_CNET98_MAP_REG0H, (bank >> 8) & 0xff);
|
|
DELAY(10);
|
|
ed_asic_outb(sc, ED_CNET98_MAP_REG1L, 0x00);
|
|
DELAY(10);
|
|
ed_asic_outb(sc, ED_CNET98_MAP_REG1H, 0x41);
|
|
DELAY(10);
|
|
ed_asic_outb(sc, ED_CNET98_MAP_REG2L, 0x00);
|
|
DELAY(10);
|
|
ed_asic_outb(sc, ED_CNET98_MAP_REG2H, 0x42);
|
|
DELAY(10);
|
|
ed_asic_outb(sc, ED_CNET98_MAP_REG3L, 0x00);
|
|
DELAY(10);
|
|
ed_asic_outb(sc, ED_CNET98_MAP_REG3H, 0x43);
|
|
DELAY(10);
|
|
|
|
/*
|
|
* Enable window memory(16Kbyte)
|
|
* bit7 is 1:enable
|
|
*/
|
|
#ifdef ED_DEBUG
|
|
printf("ed?: window start address=%x\n", mem);
|
|
#endif
|
|
ed_asic_outb(sc, ED_CNET98_WIN_REG, mem);
|
|
DELAY(10);
|
|
}
|
|
|
|
/*
|
|
* Probe and vendor-specific initialization routine for C-NET(98) boards
|
|
*/
|
|
static int
|
|
ed_probe_CNET98(device_t dev, int port_rid, int flags)
|
|
{
|
|
struct ed_softc *sc = device_get_softc(dev);
|
|
int error;
|
|
u_char tmp;
|
|
u_long conf_irq, junk;
|
|
#ifdef DIAGNOSTIC
|
|
u_char tmp_s;
|
|
#endif
|
|
|
|
error = ed98_alloc_port(dev, port_rid);
|
|
if (error)
|
|
return (error);
|
|
|
|
sc->asic_offset = ED_NOVELL_ASIC_OFFSET;
|
|
sc->nic_offset = ED_NOVELL_NIC_OFFSET;
|
|
|
|
error = ed98_alloc_memory(dev, 0);
|
|
if (error)
|
|
return (error);
|
|
|
|
/* Check I/O address. 0x[a-f]3d0 are allowed. */
|
|
if (((rman_get_start(sc->port_res) & 0x0fff) != 0x03d0)
|
|
|| ((rman_get_start(sc->port_res) & 0xf000) < (u_short) 0xa000)) {
|
|
#ifdef DIAGNOSTIC
|
|
device_printf(dev, "Invalid i/o port configuration (0x%lx) "
|
|
"must be %s for %s\n", rman_get_start(sc->port_res),
|
|
"0x[a-f]3d0", "CNET98");
|
|
#endif
|
|
return (ENXIO);
|
|
}
|
|
|
|
#ifdef DIAGNOSTIC
|
|
/* Check window area address */
|
|
tmp_s = rman_get_start(sc->mem_res) >> 12;
|
|
if (tmp_s < 0x80) {
|
|
device_printf(dev, "Please change window address(0x%lx)\n",
|
|
rman_get_start(sc->mem_res));
|
|
return (ENXIO);
|
|
}
|
|
|
|
tmp_s &= 0x0f;
|
|
tmp = rman_get_start(sc->port_res) >> 12;
|
|
if ((tmp_s <= tmp) && (tmp < (tmp_s + 4))) {
|
|
device_printf(dev, "Please change iobase address(0x%lx) "
|
|
"or window address(0x%lx)\n",
|
|
rman_get_start(sc->port_res),
|
|
rman_get_start(sc->mem_res));
|
|
return (ENXIO);
|
|
}
|
|
#endif
|
|
/* Reset the board */
|
|
ed_reset_CNET98(sc, flags);
|
|
|
|
/*
|
|
* This is needed because some NE clones apparently don't reset the
|
|
* NIC properly (or the NIC chip doesn't reset fully on power-up) XXX
|
|
* - this makes the probe invasive! ...Done against my better
|
|
* judgement. -DLG
|
|
*/
|
|
ed_nic_outb(sc, ED_P0_CR, ED_CR_RD2 | ED_CR_STP);
|
|
DELAY(5000);
|
|
|
|
/* Make sure that we really have an 8390 based board */
|
|
if (!ed98_probe_generic8390(sc))
|
|
return (ENXIO);
|
|
|
|
/*
|
|
* Set window ethernet address area
|
|
* board memory base 0x480000 data 256byte
|
|
*/
|
|
ed_winsel_CNET98(sc, 0x4800);
|
|
|
|
/*
|
|
* Get station address from on-board ROM
|
|
*/
|
|
bus_space_read_region_1(sc->mem_bst, sc->mem_bsh, sc->mem_start,
|
|
sc->enaddr, ETHER_ADDR_LEN);
|
|
|
|
sc->vendor = ED_VENDOR_MISC;
|
|
sc->type_str = "CNET98";
|
|
sc->isa16bit = 0;
|
|
sc->cr_proto = ED_CR_RD2;
|
|
|
|
/*
|
|
* Set window buffer memory area
|
|
* board memory base 0x400000 data 16kbyte
|
|
*/
|
|
ed_winsel_CNET98(sc, 0x4000);
|
|
|
|
error = ed_clear_memory(dev);
|
|
if (error)
|
|
return (error);
|
|
|
|
sc->mem_shared = 1;
|
|
sc->mem_end = sc->mem_start + sc->mem_size;
|
|
|
|
sc->txb_cnt = 1; /* XXX */
|
|
sc->tx_page_start = 0;
|
|
|
|
sc->rec_page_start = sc->tx_page_start + ED_TXBUF_SIZE;
|
|
sc->rec_page_stop = sc->tx_page_start + sc->mem_size / ED_PAGE_SIZE;
|
|
|
|
sc->mem_ring = sc->mem_start + ED_PAGE_SIZE * ED_TXBUF_SIZE;
|
|
|
|
/*
|
|
* Set interrupt level
|
|
*/
|
|
error = bus_get_resource(dev, SYS_RES_IRQ, 0, &conf_irq, &junk);
|
|
if (error)
|
|
return (error);
|
|
|
|
switch (conf_irq) {
|
|
case 3:
|
|
tmp = ED_CNET98_INT_IRQ3;
|
|
break;
|
|
case 5:
|
|
tmp = ED_CNET98_INT_IRQ5;
|
|
break;
|
|
case 6:
|
|
tmp = ED_CNET98_INT_IRQ6;
|
|
break;
|
|
case 9:
|
|
tmp = ED_CNET98_INT_IRQ9;
|
|
break;
|
|
case 12:
|
|
tmp = ED_CNET98_INT_IRQ12;
|
|
break;
|
|
case 13:
|
|
tmp = ED_CNET98_INT_IRQ13;
|
|
break;
|
|
default:
|
|
device_printf(dev, "Invalid irq configuration (%ld) must be "
|
|
"%s for %s\n", conf_irq, "3,5,6,9,12,13", "CNET98");
|
|
return (ENXIO);
|
|
}
|
|
ed_asic_outb(sc, ED_CNET98_INT_LEV, tmp);
|
|
DELAY(1000);
|
|
/*
|
|
* Set interrupt mask.
|
|
* bit7:1 all interrupt mask
|
|
* bit1:1 timer interrupt mask
|
|
* bit0:0 NS controler interrupt enable
|
|
*/
|
|
ed_asic_outb(sc, ED_CNET98_INT_MASK, 0x7e);
|
|
DELAY(1000);
|
|
|
|
sc->sc_write_mbufs = ed_shmem_write_mbufs;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Probe and vendor-specific initialization routine for C-NET(98)E/L boards
|
|
*/
|
|
static int
|
|
ed_probe_CNET98EL(device_t dev, int port_rid, int flags)
|
|
{
|
|
struct ed_softc *sc = device_get_softc(dev);
|
|
int error;
|
|
int i;
|
|
u_char romdata[ETHER_ADDR_LEN * 2], tmp;
|
|
u_long conf_irq, junk;
|
|
|
|
error = ed98_alloc_port(dev, port_rid);
|
|
if (error)
|
|
return (error);
|
|
|
|
sc->asic_offset = ED_NOVELL_ASIC_OFFSET;
|
|
sc->nic_offset = ED_NOVELL_NIC_OFFSET;
|
|
|
|
/* Check I/O address. 0x[0-f]3d0 are allowed. */
|
|
if ((rman_get_start(sc->port_res) & 0x0fff) != 0x03d0) {
|
|
#ifdef DIAGNOSTIC
|
|
device_printf(dev, "Invalid i/o port configuration (0x%lx) "
|
|
"must be %s for %s\n", rman_get_start(sc->port_res),
|
|
"0x?3d0", "CNET98E/L");
|
|
#endif
|
|
return (ENXIO);
|
|
}
|
|
|
|
/* Reset the board */
|
|
ed_reset_CNET98(sc, flags);
|
|
|
|
/*
|
|
* This is needed because some NE clones apparently don't reset the
|
|
* NIC properly (or the NIC chip doesn't reset fully on power-up) XXX
|
|
* - this makes the probe invasive! ...Done against my better
|
|
* judgement. -DLG
|
|
*/
|
|
ed_nic_outb(sc, ED_P0_CR, ED_CR_RD2 | ED_CR_STP);
|
|
DELAY(5000);
|
|
|
|
/* Make sure that we really have an 8390 based board */
|
|
if (!ed98_probe_generic8390(sc))
|
|
return (ENXIO);
|
|
|
|
/* Test memory via PIO */
|
|
sc->cr_proto = ED_CR_RD2;
|
|
if (!ed_pio_testmem(sc, ED_CNET98EL_PAGE_OFFSET, 1, flags))
|
|
return (ENXIO);
|
|
|
|
/* This looks like a C-NET(98)E/L board. */
|
|
sc->type_str = "CNET98E/L";
|
|
|
|
/*
|
|
* Set IRQ. C-NET(98)E/L only allows a choice of irq 3,5,6.
|
|
*/
|
|
error = bus_get_resource(dev, SYS_RES_IRQ, 0, &conf_irq, &junk);
|
|
if (error)
|
|
return (error);
|
|
|
|
switch (conf_irq) {
|
|
case 3:
|
|
tmp = ED_CNET98EL_ICR_IRQ3;
|
|
break;
|
|
case 5:
|
|
tmp = ED_CNET98EL_ICR_IRQ5;
|
|
break;
|
|
case 6:
|
|
tmp = ED_CNET98EL_ICR_IRQ6;
|
|
break;
|
|
#if 0
|
|
case 12:
|
|
tmp = ED_CNET98EL_ICR_IRQ12;
|
|
break;
|
|
#endif
|
|
default:
|
|
device_printf(dev, "Invalid irq configuration (%ld) must be "
|
|
"%s for %s\n", conf_irq, "3,5,6", "CNET98E/L");
|
|
return (ENXIO);
|
|
}
|
|
ed_asic_outb(sc, ED_CNET98EL_ICR, tmp);
|
|
ed_asic_outb(sc, ED_CNET98EL_IMR, 0x7e);
|
|
|
|
/* Get station address from on-board ROM */
|
|
ed_pio_readmem(sc, 16384, romdata, sizeof(romdata));
|
|
for (i = 0; i < ETHER_ADDR_LEN; i++)
|
|
sc->enaddr[i] = romdata[i * 2];
|
|
|
|
/* clear any pending interrupts that might have occurred above */
|
|
ed_nic_outb(sc, ED_P0_ISR, 0xff);
|
|
|
|
sc->sc_write_mbufs = ed_pio_write_mbufs;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Probe and vendor-specific initialization routine for PC-9801-77 boards
|
|
*/
|
|
static int
|
|
ed_probe_NEC77(device_t dev, int port_rid, int flags)
|
|
{
|
|
struct ed_softc *sc = device_get_softc(dev);
|
|
int error;
|
|
u_char tmp;
|
|
u_long conf_irq, junk;
|
|
|
|
error = ed98_probe_Novell(dev, port_rid, flags);
|
|
if (error)
|
|
return (error);
|
|
|
|
/* LA/T-98 does not need IRQ setting. */
|
|
if (ED_TYPE98SUB(flags) == 0)
|
|
return (0);
|
|
|
|
/*
|
|
* Set IRQ. PC-9801-77 only allows a choice of irq 3,5,6,12,13.
|
|
*/
|
|
error = bus_get_resource(dev, SYS_RES_IRQ, 0, &conf_irq, &junk);
|
|
if (error)
|
|
return (error);
|
|
|
|
switch (conf_irq) {
|
|
case 3:
|
|
tmp = ED_NEC77_IRQ3;
|
|
break;
|
|
case 5:
|
|
tmp = ED_NEC77_IRQ5;
|
|
break;
|
|
case 6:
|
|
tmp = ED_NEC77_IRQ6;
|
|
break;
|
|
case 12:
|
|
tmp = ED_NEC77_IRQ12;
|
|
break;
|
|
case 13:
|
|
tmp = ED_NEC77_IRQ13;
|
|
break;
|
|
default:
|
|
device_printf(dev, "Invalid irq configuration (%ld) must be "
|
|
"%s for %s\n", conf_irq, "3,5,6,12,13", "PC-9801-77");
|
|
return (ENXIO);
|
|
}
|
|
ed_asic_outb(sc, ED_NEC77_IRQ, tmp);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Probe and vendor-specific initialization routine for EC/EP-98X boards
|
|
*/
|
|
static int
|
|
ed_probe_NW98X(device_t dev, int port_rid, int flags)
|
|
{
|
|
struct ed_softc *sc = device_get_softc(dev);
|
|
int error;
|
|
u_char tmp;
|
|
u_long conf_irq, junk;
|
|
|
|
error = ed98_probe_Novell(dev, port_rid, flags);
|
|
if (error)
|
|
return (error);
|
|
|
|
/* Networld 98X3 does not need IRQ setting. */
|
|
if (ED_TYPE98SUB(flags) == 0)
|
|
return (0);
|
|
|
|
/*
|
|
* Set IRQ. EC/EP-98X only allows a choice of irq 3,5,6,12,13.
|
|
*/
|
|
error = bus_get_resource(dev, SYS_RES_IRQ, 0, &conf_irq, &junk);
|
|
if (error)
|
|
return (error);
|
|
|
|
switch (conf_irq) {
|
|
case 3:
|
|
tmp = ED_NW98X_IRQ3;
|
|
break;
|
|
case 5:
|
|
tmp = ED_NW98X_IRQ5;
|
|
break;
|
|
case 6:
|
|
tmp = ED_NW98X_IRQ6;
|
|
break;
|
|
case 12:
|
|
tmp = ED_NW98X_IRQ12;
|
|
break;
|
|
case 13:
|
|
tmp = ED_NW98X_IRQ13;
|
|
break;
|
|
default:
|
|
device_printf(dev, "Invalid irq configuration (%ld) must be "
|
|
"%s for %s\n", conf_irq, "3,5,6,12,13", "EC/EP-98X");
|
|
return (ENXIO);
|
|
}
|
|
ed_asic_outb(sc, ED_NW98X_IRQ, tmp);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Read SB-9801 station address from Serial Two-Wire EEPROM
|
|
*/
|
|
static void
|
|
ed_get_SB98(struct ed_softc *sc)
|
|
{
|
|
int i, j;
|
|
u_char mask, val;
|
|
|
|
/* enable EEPROM acceess */
|
|
ed_asic_outb(sc, ED_SB98_EEPENA, ED_SB98_EEPENA_ENABLE);
|
|
|
|
/* output start command */
|
|
ed_asic_outb(sc, ED_SB98_EEP, ED_SB98_EEP_SDA | ED_SB98_EEP_SCL);
|
|
DELAY(ED_SB98_EEP_DELAY);
|
|
ed_asic_outb(sc, ED_SB98_EEP, ED_SB98_EEP_SCL);
|
|
DELAY(ED_SB98_EEP_DELAY);
|
|
|
|
/* output address (7bit) */
|
|
for (mask = 0x40; mask != 0; mask >>= 1) {
|
|
val = 0;
|
|
if (ED_SB98_ADDRESS & mask)
|
|
val = ED_SB98_EEP_SDA;
|
|
ed_asic_outb(sc, ED_SB98_EEP, val);
|
|
DELAY(ED_SB98_EEP_DELAY);
|
|
ed_asic_outb(sc, ED_SB98_EEP, val | ED_SB98_EEP_SCL);
|
|
DELAY(ED_SB98_EEP_DELAY);
|
|
}
|
|
|
|
/* output READ command */
|
|
ed_asic_outb(sc, ED_SB98_EEP, ED_SB98_EEP_READ);
|
|
DELAY(ED_SB98_EEP_DELAY);
|
|
ed_asic_outb(sc, ED_SB98_EEP, ED_SB98_EEP_READ | ED_SB98_EEP_SCL);
|
|
DELAY(ED_SB98_EEP_DELAY);
|
|
|
|
/* read station address */
|
|
for (i = 0; i < ETHER_ADDR_LEN; i++) {
|
|
/* output ACK */
|
|
ed_asic_outb(sc, ED_SB98_EEP, 0);
|
|
DELAY(ED_SB98_EEP_DELAY);
|
|
ed_asic_outb(sc, ED_SB98_EEP, ED_SB98_EEP_SCL);
|
|
DELAY(ED_SB98_EEP_DELAY);
|
|
|
|
val = 0;
|
|
for (j = 0; j < 8; j++) {
|
|
ed_asic_outb(sc, ED_SB98_EEP, ED_SB98_EEP_SDA);
|
|
DELAY(ED_SB98_EEP_DELAY);
|
|
ed_asic_outb(sc, ED_SB98_EEP, ED_SB98_EEP_SDA | ED_SB98_EEP_SCL);
|
|
DELAY(ED_SB98_EEP_DELAY);
|
|
val <<= 1;
|
|
val |= (ed_asic_inb(sc, ED_SB98_EEP) & ED_SB98_EEP_SDA);
|
|
DELAY(ED_SB98_EEP_DELAY);
|
|
}
|
|
sc->enaddr[i] = val;
|
|
}
|
|
|
|
/* output Last ACK */
|
|
ed_asic_outb(sc, ED_SB98_EEP, ED_SB98_EEP_SDA);
|
|
DELAY(ED_SB98_EEP_DELAY);
|
|
ed_asic_outb(sc, ED_SB98_EEP, ED_SB98_EEP_SDA | ED_SB98_EEP_SCL);
|
|
DELAY(ED_SB98_EEP_DELAY);
|
|
|
|
/* output stop command */
|
|
ed_asic_outb(sc, ED_SB98_EEP, ED_SB98_EEP_SCL);
|
|
DELAY(ED_SB98_EEP_DELAY);
|
|
ed_asic_outb(sc, ED_SB98_EEP, ED_SB98_EEP_SDA | ED_SB98_EEP_SCL);
|
|
DELAY(ED_SB98_EEP_DELAY);
|
|
|
|
/* disable EEPROM access */
|
|
ed_asic_outb(sc, ED_SB98_EEPENA, ED_SB98_EEPENA_DISABLE);
|
|
}
|
|
|
|
/*
|
|
* Probe and vendor-specific initialization routine for SB-9801 boards
|
|
*/
|
|
static int
|
|
ed_probe_SB98(device_t dev, int port_rid, int flags)
|
|
{
|
|
struct ed_softc *sc = device_get_softc(dev);
|
|
int error;
|
|
u_char tmp;
|
|
u_long conf_irq, junk;
|
|
|
|
error = ed98_alloc_port(dev, port_rid);
|
|
if (error)
|
|
return (error);
|
|
|
|
sc->asic_offset = ED_NOVELL_ASIC_OFFSET;
|
|
sc->nic_offset = ED_NOVELL_NIC_OFFSET;
|
|
|
|
/* Check I/O address. 00d[02468ace] are allowed. */
|
|
if ((rman_get_start(sc->port_res) & ~0x000e) != 0x00d0) {
|
|
#ifdef DIAGNOSTIC
|
|
device_printf(dev, "Invalid i/o port configuration (0x%lx) "
|
|
"must be %s for %s\n", rman_get_start(sc->port_res),
|
|
"0xd?", "SB9801");
|
|
#endif
|
|
return (ENXIO);
|
|
}
|
|
|
|
/* Write I/O port address and read 4 times */
|
|
outb(ED_SB98_IO_INHIBIT, rman_get_start(sc->port_res) & 0xff);
|
|
(void) inb(ED_SB98_IO_INHIBIT); DELAY(300);
|
|
(void) inb(ED_SB98_IO_INHIBIT); DELAY(300);
|
|
(void) inb(ED_SB98_IO_INHIBIT); DELAY(300);
|
|
(void) inb(ED_SB98_IO_INHIBIT); DELAY(300);
|
|
|
|
/*
|
|
* Check IRQ. Soliton SB-9801 only allows a choice of
|
|
* irq 3,5,6,12
|
|
*/
|
|
error = bus_get_resource(dev, SYS_RES_IRQ, 0, &conf_irq, &junk);
|
|
if (error)
|
|
return (error);
|
|
|
|
switch (conf_irq) {
|
|
case 3:
|
|
tmp = ED_SB98_CFG_IRQ3;
|
|
break;
|
|
case 5:
|
|
tmp = ED_SB98_CFG_IRQ5;
|
|
break;
|
|
case 6:
|
|
tmp = ED_SB98_CFG_IRQ6;
|
|
break;
|
|
case 12:
|
|
tmp = ED_SB98_CFG_IRQ12;
|
|
break;
|
|
default:
|
|
device_printf(dev, "Invalid irq configuration (%ld) must be "
|
|
"%s for %s\n", conf_irq, "3,5,6,12", "SB9801");
|
|
return (ENXIO);
|
|
}
|
|
|
|
if (flags & ED_FLAGS_DISABLE_TRANCEIVER)
|
|
tmp |= ED_SB98_CFG_ALTPORT;
|
|
ed_asic_outb(sc, ED_SB98_CFG, ED_SB98_CFG_ENABLE | tmp);
|
|
ed_asic_outb(sc, ED_SB98_POLARITY, 0x01);
|
|
|
|
/* Reset the board. */
|
|
ed_asic_outb(sc, ED_NOVELL_RESET, 0x7a);
|
|
DELAY(300);
|
|
ed_asic_outb(sc, ED_NOVELL_RESET, 0x79);
|
|
DELAY(300);
|
|
|
|
/*
|
|
* This is needed because some NE clones apparently don't reset the
|
|
* NIC properly (or the NIC chip doesn't reset fully on power-up) XXX
|
|
* - this makes the probe invasive! ...Done against my better
|
|
* judgement. -DLG
|
|
*/
|
|
ed_nic_outb(sc, ED_P0_CR, ED_CR_RD2 | ED_CR_STP);
|
|
DELAY(5000);
|
|
|
|
/* Make sure that we really have an 8390 based board */
|
|
if (!ed98_probe_generic8390(sc))
|
|
return (ENXIO);
|
|
|
|
/* Test memory via PIO */
|
|
sc->cr_proto = ED_CR_RD2;
|
|
if (!ed_pio_testmem(sc, 16384, 1, flags))
|
|
return (ENXIO);
|
|
|
|
/* This looks like an SB9801 board. */
|
|
sc->type_str = "SB9801";
|
|
|
|
/* Get station address */
|
|
ed_get_SB98(sc);
|
|
|
|
/* clear any pending interrupts that might have occurred above */
|
|
ed_nic_outb(sc, ED_P0_ISR, 0xff);
|
|
|
|
sc->sc_write_mbufs = ed_pio_write_mbufs;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Test the ability to read and write to the NIC memory.
|
|
*/
|
|
static int
|
|
ed_pio_testmem(struct ed_softc *sc, int page_offset, int isa16bit, int flags)
|
|
{
|
|
u_long memsize;
|
|
static char test_pattern[32] = "THIS is A memory TEST pattern";
|
|
char test_buffer[32];
|
|
#ifdef DIAGNOSTIC
|
|
int page_end;
|
|
#endif
|
|
|
|
sc->vendor = ED_VENDOR_NOVELL;
|
|
sc->mem_shared = 0;
|
|
sc->isa16bit = isa16bit;
|
|
|
|
/* 8k of memory plus an additional 8k if 16bit */
|
|
memsize = (isa16bit ? 16384 : 8192);
|
|
|
|
/*
|
|
* This prevents packets from being stored in the NIC memory when the
|
|
* readmem routine turns on the start bit in the CR.
|
|
*/
|
|
ed_nic_outb(sc, ED_P0_RCR, ED_RCR_MON);
|
|
|
|
/* Initialize DCR for byte/word operations */
|
|
if (isa16bit)
|
|
ed_nic_outb(sc, ED_P0_DCR, ED_DCR_WTS | ED_DCR_FT1 | ED_DCR_LS);
|
|
else
|
|
ed_nic_outb(sc, ED_P0_DCR, ED_DCR_FT1 | ED_DCR_LS);
|
|
ed_nic_outb(sc, ED_P0_PSTART, page_offset / ED_PAGE_SIZE);
|
|
ed_nic_outb(sc, ED_P0_PSTOP, (page_offset + memsize) / ED_PAGE_SIZE);
|
|
#ifdef ED_DEBUG
|
|
printf("ed?: ed_pio_testmem: page start=%x, end=%lx",
|
|
page_offset, page_offset + memsize);
|
|
#endif
|
|
|
|
/*
|
|
* Write a test pattern. If this fails, then we don't know
|
|
* what this board is.
|
|
*/
|
|
ed_pio_writemem(sc, test_pattern, page_offset, sizeof(test_pattern));
|
|
ed_pio_readmem(sc, page_offset, test_buffer, sizeof(test_pattern));
|
|
|
|
if (bcmp(test_pattern, test_buffer, sizeof(test_pattern))) {
|
|
#ifdef ED_DEBUG
|
|
printf("ed?: ed_pio_testmem: bcmp(page %x) NG", page_offset);
|
|
#endif
|
|
return (0);
|
|
}
|
|
|
|
#ifdef DIAGNOSTIC
|
|
/* Check the bottom. */
|
|
page_end = page_offset + memsize - ED_PAGE_SIZE;
|
|
ed_pio_writemem(sc, test_pattern, page_end, sizeof(test_pattern));
|
|
ed_pio_readmem(sc, page_end, test_buffer, sizeof(test_pattern));
|
|
|
|
if (bcmp(test_pattern, test_buffer, sizeof(test_pattern))) {
|
|
#ifdef ED_DEBUG
|
|
printf("ed?: ed_pio_testmem: bcmp(page %x) NG", page_end);
|
|
#endif
|
|
return (0);
|
|
}
|
|
#endif
|
|
sc->mem_size = memsize;
|
|
sc->mem_start = page_offset;
|
|
sc->mem_end = sc->mem_start + memsize;
|
|
sc->tx_page_start = page_offset / ED_PAGE_SIZE;
|
|
|
|
/*
|
|
* Use one xmit buffer if < 16k, two buffers otherwise (if not told
|
|
* otherwise).
|
|
*/
|
|
if ((memsize < 16384) || (flags & ED_FLAGS_NO_MULTI_BUFFERING))
|
|
sc->txb_cnt = 1;
|
|
else
|
|
sc->txb_cnt = 2;
|
|
|
|
sc->rec_page_start = sc->tx_page_start + sc->txb_cnt * ED_TXBUF_SIZE;
|
|
sc->rec_page_stop = sc->tx_page_start + memsize / ED_PAGE_SIZE;
|
|
|
|
sc->mem_ring = sc->mem_start + sc->txb_cnt * ED_PAGE_SIZE * ED_TXBUF_SIZE;
|
|
|
|
return (1);
|
|
}
|
|
|
|
static device_method_t ed_cbus_methods[] = {
|
|
/* Device interface */
|
|
DEVMETHOD(device_probe, ed_cbus_probe),
|
|
DEVMETHOD(device_attach, ed_cbus_attach),
|
|
DEVMETHOD(device_detach, ed_detach),
|
|
|
|
{ 0, 0 }
|
|
};
|
|
|
|
static driver_t ed_cbus_driver = {
|
|
"ed",
|
|
ed_cbus_methods,
|
|
sizeof(struct ed_softc)
|
|
};
|
|
|
|
DRIVER_MODULE(ed, isa, ed_cbus_driver, ed_devclass, 0, 0);
|
|
MODULE_DEPEND(ed, isa, 1, 1, 1);
|
|
MODULE_DEPEND(ed, ether, 1, 1, 1);
|