d4fcf3cba5
and amd64. The optimization is a trivial on recent machines. Reviewed by: -arch (imp, marcel, dfr)
886 lines
20 KiB
C
886 lines
20 KiB
C
/*-
|
|
* Copyright (c) 2003 Matthew N. Dodd
|
|
* 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, 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.
|
|
*
|
|
* Portions:
|
|
* Copyright (c) 1992, 1993, University of Vermont and State
|
|
* Agricultural College.
|
|
* Copyright (c) 1992, 1993, Garrett A. Wollman.
|
|
* Copyright (c) 1990, 1991, William F. Jolitz
|
|
* Copyright (c) 1990, The Regents of the University of California
|
|
* Copyright (c) 1993, 1994, Charles M. Hannum
|
|
* Copyright (c) 1993, 1994, 1995, Rodney W. Grimes
|
|
* Copyright (c) 1997, Aaron C. Smith
|
|
*
|
|
* See if_ie.c for applicable license.
|
|
*/
|
|
|
|
#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 <sys/rman.h>
|
|
|
|
#include <machine/clock.h>
|
|
#include <machine/md_var.h>
|
|
|
|
#include <net/if.h>
|
|
#include <net/if_arp.h>
|
|
#include <net/if_media.h>
|
|
|
|
#include <isa/isavar.h>
|
|
#include <isa/pnpvar.h>
|
|
|
|
#include <i386/isa/elink.h>
|
|
|
|
#include <dev/ic/i82586.h>
|
|
#include <dev/ie/if_ie507.h>
|
|
#include <dev/ie/if_iee16.h>
|
|
#include <dev/ie/if_iereg.h>
|
|
#include <dev/ie/if_ievar.h>
|
|
|
|
static int ie_modevent (module_t, int, void *);
|
|
|
|
static void ie_isa_3C507_identify (driver_t *, device_t);
|
|
static int ie_isa_3C507_probe (device_t);
|
|
static int ie_isa_3C507_attach (device_t);
|
|
static int ie_3C507_port_check (u_int32_t);
|
|
|
|
static void ie_isa_ee16_identify (driver_t *, device_t);
|
|
static int ie_isa_ee16_probe (device_t);
|
|
static int ie_isa_ee16_attach (device_t);
|
|
static int ie_ee16_port_check (u_int32_t port);
|
|
static u_int16_t ie_ee16_hw_read_eeprom (u_int32_t port, int loc);
|
|
|
|
static int ie_isa_sl_probe (device_t);
|
|
static int ie_isa_sl_attach (device_t);
|
|
static enum ie_hardware ie_isa_sl_get_hard_type (u_int32_t);
|
|
|
|
/*
|
|
* 3Com 3C507 Etherlink 16
|
|
*/
|
|
#define IE_3C507_IOBASE_LOW 0x200
|
|
#define IE_3C507_IOBASE_HIGH 0x3e0
|
|
#define IE_3C507_IOSIZE 16
|
|
|
|
#define IE_3C507_IRQ_MASK 0x0f
|
|
|
|
#define IE_3C507_MADDR_HIGH 0x20
|
|
#define IE_3C507_MADDR_MASK 0x1c
|
|
#define IE_3C507_MADDR_BASE 0xc0000
|
|
#define IE_3C507_MADDR_SHIFT 12
|
|
|
|
#define IE_3C507_MSIZE_MASK 3
|
|
#define IE_3C507_MSIZE_SHIFT 14
|
|
|
|
static void
|
|
ie_isa_3C507_identify (driver_t *driver, device_t parent)
|
|
{
|
|
char * desc = "3Com 3C507 Etherlink 16";
|
|
device_t child;
|
|
u_int32_t port, maddr, msize;
|
|
u_int8_t irq, data;
|
|
int error;
|
|
|
|
/* Reset and put card in CONFIG state without changing address. */
|
|
elink_reset();
|
|
elink_idseq(ELINK_507_POLY);
|
|
elink_idseq(ELINK_507_POLY);
|
|
outb(ELINK_ID_PORT, 0xff);
|
|
|
|
for (port = IE_3C507_IOBASE_LOW;
|
|
port <= IE_3C507_IOBASE_HIGH;
|
|
port += IE_3C507_IOSIZE) {
|
|
|
|
if (ie_3C507_port_check(port)) {
|
|
#if DEBUG
|
|
if (bootverbose) {
|
|
device_printf(parent,
|
|
"(if_ie) (3C507) not found at port %#x\n",
|
|
port);
|
|
}
|
|
#endif
|
|
continue;
|
|
}
|
|
|
|
outb(port + IE507_CTRL, EL_CTRL_NRST);
|
|
|
|
data = inb(port + IE507_IRQ);
|
|
irq = data & IE_3C507_IRQ_MASK;
|
|
|
|
data = inb(port + IE507_MADDR);
|
|
|
|
if (data & IE_3C507_MADDR_HIGH) {
|
|
if (bootverbose) {
|
|
device_printf(parent,
|
|
"(if_ie) can't map 3C507 RAM in high memory\n");
|
|
}
|
|
continue;
|
|
}
|
|
|
|
maddr = IE_3C507_MADDR_BASE +
|
|
((data & IE_3C507_MADDR_MASK)
|
|
<< IE_3C507_MADDR_SHIFT);
|
|
msize = ((data & IE_3C507_MSIZE_MASK) + 1)
|
|
<< IE_3C507_MSIZE_SHIFT;
|
|
|
|
child = BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, "ie", -1);
|
|
device_set_desc_copy(child, desc);
|
|
device_set_driver(child, driver);
|
|
|
|
error = bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1);
|
|
if (error) {
|
|
device_printf(parent, "(if_ie) Unable to set IRQ resource %d.\n",
|
|
irq);
|
|
error = device_delete_child(parent, child);
|
|
continue;
|
|
}
|
|
|
|
error = bus_set_resource(child, SYS_RES_IOPORT, 0, port, IE_3C507_IOSIZE);
|
|
if (error) {
|
|
device_printf(parent, "(if_ie) Unable to set IOPORT resource %#x-%#x.\n",
|
|
port, port+IE_3C507_IOSIZE);
|
|
error = device_delete_child(parent, child);
|
|
continue;
|
|
}
|
|
|
|
error = bus_set_resource(child, SYS_RES_MEMORY, 0, maddr, msize);
|
|
if (error) {
|
|
device_printf(parent, "(if_ie) Unable to set MEMORY resource %#x-%#x.\n",
|
|
maddr, maddr+msize);
|
|
error = device_delete_child(parent, child);
|
|
continue;
|
|
}
|
|
|
|
if (bootverbose) {
|
|
device_printf(parent,
|
|
"(if_ie) <%s> at port %#x-%#x irq %d iomem %#lx-%#lx (%dKB)\n",
|
|
desc,
|
|
port, (port + IE_3C507_IOSIZE) - 1,
|
|
irq,
|
|
(u_long)maddr, (u_long)(maddr + msize) - 1,
|
|
(msize / 1024));
|
|
}
|
|
}
|
|
|
|
/* go to RUN state */
|
|
outb(ELINK_ID_PORT, 0x00);
|
|
elink_idseq(ELINK_507_POLY);
|
|
outb(ELINK_ID_PORT, 0x00);
|
|
|
|
return;
|
|
}
|
|
|
|
static int
|
|
ie_isa_3C507_probe (device_t dev)
|
|
{
|
|
u_int32_t iobase;
|
|
|
|
/* No ISA-PnP support */
|
|
if (isa_get_vendorid(dev)) {
|
|
return (ENXIO);
|
|
}
|
|
|
|
/* No ISA-HINT support */
|
|
if (!device_get_desc(dev)) {
|
|
return (EBUSY);
|
|
}
|
|
|
|
/* Have we at least an ioport? */
|
|
if ((iobase = bus_get_resource_start(dev, SYS_RES_IOPORT, 0)) == 0) {
|
|
return (ENXIO);
|
|
}
|
|
|
|
/* Is this thing really a 3c507? */
|
|
if (ie_3C507_port_check(iobase)) {
|
|
return (ENXIO);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
ie_isa_3C507_attach (device_t dev)
|
|
{
|
|
struct ie_softc * sc;
|
|
int error;
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
sc->io_rid = 0;
|
|
sc->irq_rid = 0;
|
|
sc->mem_rid = 0;
|
|
|
|
error = ie_alloc_resources(dev);
|
|
if (error) {
|
|
goto bad;
|
|
}
|
|
|
|
sc->bus_use = 0;
|
|
sc->ie_reset_586 = el_reset_586;
|
|
sc->ie_chan_attn = el_chan_attn;
|
|
sc->hard_type = IE_3C507;
|
|
sc->hard_vers = 0;
|
|
|
|
outb(PORT(sc) + IE507_CTRL, EL_CTRL_NORMAL);
|
|
|
|
if (!check_ie_present(sc)) {
|
|
error = ENXIO;
|
|
goto bad;
|
|
}
|
|
|
|
sl_read_ether(sc, sc->arpcom.ac_enaddr);
|
|
|
|
/* Clear the interrupt latch just in case. */
|
|
outb(PORT(sc) + IE507_ICTRL, 1);
|
|
|
|
error = ie_attach(dev);
|
|
if (error) {
|
|
device_printf(dev, "ie_attach() failed.\n");
|
|
goto bad;
|
|
}
|
|
|
|
error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET,
|
|
ie_intr, sc, &sc->irq_ih);
|
|
if (error) {
|
|
device_printf(dev, "Unable to register interrupt handler\n");
|
|
goto bad;
|
|
}
|
|
|
|
return (0);
|
|
bad:
|
|
ie_release_resources(dev);
|
|
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* If a 3c507 is present, return 0
|
|
* else, return 1.
|
|
*/
|
|
static int
|
|
ie_3C507_port_check (u_int32_t port)
|
|
{
|
|
u_char * signature = "*3COM*";
|
|
int i;
|
|
|
|
for (i = 0; i < 6; i++)
|
|
if (inb(port + i) != signature[i])
|
|
return (ENXIO);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Intel EtherExpress 16
|
|
*/
|
|
#define IE_EE16_ID_PORT 0x0f
|
|
#define IE_EE16_ID 0xbaba
|
|
#define IE_EE16_EEPROM_CONFIG1 0x00
|
|
#define IE_EE16_EEPROM_IRQ_MASK 0xe000
|
|
#define IE_EE16_EEPROM_IRQ_SHIFT 13
|
|
#define IE_EE16_EEPROM_MEMCFG 0x06
|
|
#define IE_EE16_IOSIZE 16
|
|
|
|
/*
|
|
* TODO:
|
|
* Test for 8/16 bit mode.
|
|
* Test for invalid mem sizes.
|
|
*/
|
|
static void
|
|
ie_isa_ee16_identify (driver_t *driver, device_t parent)
|
|
{
|
|
char * desc = "Intel EtherExpress 16";
|
|
device_t child;
|
|
u_int16_t ports[] = {
|
|
0x300, 0x310, 0x320, 0x330,
|
|
0x340, 0x350, 0x360, 0x370,
|
|
0x200, 0x210, 0x220, 0x230,
|
|
0x240, 0x250, 0x260, 0x270,
|
|
0
|
|
};
|
|
u_int16_t irqs[] = { 0, 0x09, 0x03, 0x04, 0x05, 0x0a, 0x0b, 0 };
|
|
u_int32_t port, maddr, msize;
|
|
u_int8_t irq;
|
|
u_int16_t data;
|
|
int i, error;
|
|
|
|
for (i = 0; ports[i]; i++) {
|
|
port = ports[i];
|
|
|
|
if (ie_ee16_port_check(port)) {
|
|
#if DEBUG
|
|
if (bootverbose) {
|
|
device_printf(parent,
|
|
"if_ie: (EE16) not found at port %#x\n",
|
|
port);
|
|
}
|
|
#endif
|
|
continue;
|
|
}
|
|
|
|
/* reset any ee16 at the current iobase */
|
|
outb(port + IEE16_ECTRL, IEE16_RESET_ASIC);
|
|
outb(port + IEE16_ECTRL, 0);
|
|
DELAY(240);
|
|
|
|
data = ie_ee16_hw_read_eeprom(port, IE_EE16_EEPROM_CONFIG1);
|
|
irq = irqs[((data & IE_EE16_EEPROM_IRQ_MASK)
|
|
>> IE_EE16_EEPROM_IRQ_SHIFT)];
|
|
|
|
data = ie_ee16_hw_read_eeprom(port, IE_EE16_EEPROM_MEMCFG);
|
|
maddr = 0xc0000 + ((ffs(data & 0x00ff) - 1) * 0x4000);
|
|
msize = (fls((data & 0x00ff) >> (ffs(data & 0x00ff) - 1)))
|
|
* 0x4000;
|
|
|
|
child = BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, "ie", -1);
|
|
device_set_desc_copy(child, desc);
|
|
device_set_driver(child, driver);
|
|
|
|
error = bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1);
|
|
if (error) {
|
|
device_printf(parent, "(if_ie) Unable to set IRQ resource %d.\n",
|
|
irq);
|
|
error = device_delete_child(parent, child);
|
|
continue;
|
|
}
|
|
|
|
error = bus_set_resource(child, SYS_RES_IOPORT, 0, port, IE_EE16_IOSIZE);
|
|
if (error) {
|
|
device_printf(parent, "(if_ie) Unable to set IOPORT resource %#x-%#x.\n",
|
|
port, port+IE_EE16_IOSIZE);
|
|
error = device_delete_child(parent, child);
|
|
continue;
|
|
}
|
|
|
|
error = bus_set_resource(child, SYS_RES_MEMORY, 0, maddr, msize);
|
|
if (error) {
|
|
device_printf(parent, "(if_ie) Unable to set MEMORY resource %#x-%#x.\n",
|
|
maddr, maddr+msize);
|
|
error = device_delete_child(parent, child);
|
|
continue;
|
|
}
|
|
|
|
if (bootverbose) {
|
|
device_printf(parent,
|
|
"if_ie: <%s> at port %#x-%#x irq %d iomem %#lx-%#lx (%dKB)\n",
|
|
desc,
|
|
port, (port + IE_EE16_IOSIZE) - 1,
|
|
irq,
|
|
(u_long)maddr, (u_long)(maddr + msize) - 1,
|
|
(msize / 1024));
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static int
|
|
ie_isa_ee16_probe (device_t dev)
|
|
{
|
|
u_int32_t iobase;
|
|
|
|
/* No ISA-PnP support */
|
|
if (isa_get_vendorid(dev))
|
|
return (ENXIO);
|
|
|
|
/* No ISA-HINT support */
|
|
if (!device_get_desc(dev))
|
|
return (EBUSY);
|
|
|
|
/* Have we at least an ioport? */
|
|
if ((iobase = bus_get_resource_start(dev, SYS_RES_IOPORT, 0)) == 0)
|
|
return (ENXIO);
|
|
|
|
/* Is this really an EE16? */
|
|
if (ie_ee16_port_check(iobase))
|
|
return (ENXIO);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
ie_isa_ee16_attach (device_t dev)
|
|
{
|
|
struct ie_softc * sc;
|
|
int i, error;
|
|
u_int16_t checksum;
|
|
u_short eaddrtemp, pg, adjust, decode, edecode;
|
|
u_char bart_config;
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
sc->io_rid = 0;
|
|
sc->irq_rid = 0;
|
|
sc->mem_rid = 0;
|
|
|
|
error = ie_alloc_resources(dev);
|
|
if (error) {
|
|
goto bad;
|
|
}
|
|
|
|
sc->bus_use = 0;
|
|
sc->ie_reset_586 = ee16_reset_586;
|
|
sc->ie_chan_attn = ee16_chan_attn;
|
|
sc->hard_type = IE_EE16;
|
|
sc->hard_vers = 0;
|
|
sc->iomem = 0;
|
|
|
|
/* reset any ee16 at the current iobase */
|
|
outb(PORT(sc) + IEE16_ECTRL, IEE16_RESET_ASIC);
|
|
outb(PORT(sc) + IEE16_ECTRL, 0);
|
|
DELAY(240);
|
|
|
|
/* Is this really an EE16? */
|
|
if (ie_ee16_port_check(PORT(sc))) {
|
|
device_printf(dev, "ie_ee16_port_check() failed\n");
|
|
error = ENXIO;
|
|
goto bad;
|
|
}
|
|
|
|
/* need to put the 586 in RESET while we access the eeprom. */
|
|
outb(PORT(sc) + IEE16_ECTRL, IEE16_RESET_586);
|
|
|
|
/* read the eeprom and checksum it, should == IE_E16_ID */
|
|
checksum = 0;
|
|
for (i = 0; i < 0x40; i++)
|
|
checksum += ie_ee16_hw_read_eeprom(PORT(sc), i);
|
|
|
|
if (checksum != IE_EE16_ID) {
|
|
device_printf(dev, "invalid eeprom checksum: %x\n", checksum);
|
|
error = ENXIO;
|
|
goto bad;
|
|
}
|
|
|
|
if ((kvtop(sc->iomembot) < 0xC0000) ||
|
|
(kvtop(sc->iomembot) + sc->iosize > 0xF0000)) {
|
|
device_printf(sc->dev, "mapped memory location %p out of range\n",
|
|
(void *)sc->iomembot);
|
|
error = ENXIO;
|
|
goto bad;
|
|
}
|
|
|
|
pg = ((kvtop(sc->iomembot)) & 0x3C000) >> 14;
|
|
adjust = IEE16_MCTRL_FMCS16 | (pg & 0x3) << 2;
|
|
decode = ((1 << (sc->iosize / 16384)) - 1) << pg;
|
|
edecode = ((~decode >> 4) & 0xF0) | (decode >> 8);
|
|
|
|
/* ZZZ This should be checked against eeprom location 6, low byte */
|
|
outb(PORT(sc) + IEE16_MEMDEC, decode & 0xFF);
|
|
/* ZZZ This should be checked against eeprom location 1, low byte */
|
|
outb(PORT(sc) + IEE16_MCTRL, adjust);
|
|
/* ZZZ Now if I could find this one I would have it made */
|
|
outb(PORT(sc) + IEE16_MPCTRL, (~decode & 0xFF));
|
|
/* ZZZ I think this is location 6, high byte */
|
|
outb(PORT(sc) + IEE16_MECTRL, edecode); /* XXX disable Exxx */
|
|
|
|
#if 0
|
|
(void) kvtop(sc->iomembot);
|
|
#endif
|
|
|
|
/*
|
|
* first prime the stupid bart DRAM controller so that it works,
|
|
* then zero out all of memory.
|
|
*/
|
|
bzero(sc->iomembot, 32);
|
|
bzero(sc->iomembot, sc->iosize);
|
|
|
|
/* Get the encoded interrupt number from the EEPROM */
|
|
sc->irq_encoded = ie_ee16_hw_read_eeprom(PORT(sc),
|
|
IE_EE16_EEPROM_CONFIG1);
|
|
sc->irq_encoded = (sc->irq_encoded & IE_EE16_EEPROM_IRQ_MASK) >>
|
|
IE_EE16_EEPROM_IRQ_SHIFT;
|
|
|
|
/*
|
|
* Get the hardware ethernet address from the EEPROM and save it in
|
|
* the softc for use by the 586 setup code.
|
|
*/
|
|
eaddrtemp = ie_ee16_hw_read_eeprom(PORT(sc), IEE16_EEPROM_ENET_HIGH);
|
|
sc->arpcom.ac_enaddr[1] = eaddrtemp & 0xFF;
|
|
sc->arpcom.ac_enaddr[0] = eaddrtemp >> 8;
|
|
eaddrtemp = ie_ee16_hw_read_eeprom(PORT(sc), IEE16_EEPROM_ENET_MID);
|
|
sc->arpcom.ac_enaddr[3] = eaddrtemp & 0xFF;
|
|
sc->arpcom.ac_enaddr[2] = eaddrtemp >> 8;
|
|
eaddrtemp = ie_ee16_hw_read_eeprom(PORT(sc), IEE16_EEPROM_ENET_LOW);
|
|
sc->arpcom.ac_enaddr[5] = eaddrtemp & 0xFF;
|
|
sc->arpcom.ac_enaddr[4] = eaddrtemp >> 8;
|
|
|
|
/* disable the board interrupts */
|
|
outb(PORT(sc) + IEE16_IRQ, sc->irq_encoded);
|
|
|
|
/* enable loopback to keep bad packets off the wire */
|
|
bart_config = inb(PORT(sc) + IEE16_CONFIG);
|
|
bart_config |= IEE16_BART_LOOPBACK;
|
|
bart_config |= IEE16_BART_MCS16_TEST;/* inb doesn't get bit! */
|
|
outb(PORT(sc) + IEE16_CONFIG, bart_config);
|
|
bart_config = inb(PORT(sc) + IEE16_CONFIG);
|
|
|
|
/* take the board out of reset state */
|
|
outb(PORT(sc) + IEE16_ECTRL, 0);
|
|
DELAY(100);
|
|
|
|
if (!check_ie_present(sc)) {
|
|
device_printf(dev, "check_ie_present() returned false.\n");
|
|
error = ENXIO;
|
|
goto bad;
|
|
}
|
|
|
|
error = ie_attach(dev);
|
|
if (error) {
|
|
device_printf(dev, "ie_attach() failed.\n");
|
|
goto bad;
|
|
}
|
|
|
|
error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET,
|
|
ie_intr, sc, &sc->irq_ih);
|
|
if (error) {
|
|
device_printf(dev, "Unable to register interrupt handler\n");
|
|
goto bad;
|
|
}
|
|
|
|
return (0);
|
|
bad:
|
|
ie_release_resources(dev);
|
|
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* If an EE16 is present, return 0
|
|
* else, return 1.
|
|
*/
|
|
static int
|
|
ie_ee16_port_check (u_int32_t port)
|
|
{
|
|
int i;
|
|
u_int16_t board_id;
|
|
u_int8_t data;
|
|
|
|
board_id = 0;
|
|
for (i = 0; i < 4; i++) {
|
|
data = inb(port + IE_EE16_ID_PORT);
|
|
board_id |= ((data >> 4) << ((data & 0x03) << 2));
|
|
}
|
|
|
|
if (board_id != IE_EE16_ID)
|
|
return (1);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
ie_ee16_hw_eeprom_clock (u_int32_t port, int state)
|
|
{
|
|
u_int8_t ectrl;
|
|
|
|
ectrl = inb(port + IEE16_ECTRL);
|
|
ectrl &= ~(IEE16_RESET_ASIC | IEE16_ECTRL_EESK);
|
|
|
|
if (state) {
|
|
ectrl |= IEE16_ECTRL_EESK;
|
|
}
|
|
outb(port + IEE16_ECTRL, ectrl);
|
|
DELAY(9); /* EESK must be stable for 8.38 uSec */
|
|
}
|
|
|
|
static void
|
|
ie_ee16_hw_eeprom_out (u_int32_t port, u_int16_t edata, int count)
|
|
{
|
|
u_int8_t ectrl;
|
|
int i;
|
|
|
|
ectrl = inb(port + IEE16_ECTRL);
|
|
ectrl &= ~IEE16_RESET_ASIC;
|
|
|
|
for (i = count - 1; i >= 0; i--) {
|
|
ectrl &= ~IEE16_ECTRL_EEDI;
|
|
if (edata & (1 << i)) {
|
|
ectrl |= IEE16_ECTRL_EEDI;
|
|
}
|
|
outb(port + IEE16_ECTRL, ectrl);
|
|
DELAY(1); /* eeprom data must be setup for 0.4 uSec */
|
|
ie_ee16_hw_eeprom_clock(port, 1);
|
|
ie_ee16_hw_eeprom_clock(port, 0);
|
|
}
|
|
ectrl &= ~IEE16_ECTRL_EEDI;
|
|
outb(port + IEE16_ECTRL, ectrl);
|
|
DELAY(1); /* eeprom data must be held for 0.4 uSec */
|
|
|
|
return;
|
|
}
|
|
|
|
static u_int16_t
|
|
ie_ee16_hw_eeprom_in (u_int32_t port)
|
|
{
|
|
u_int8_t ectrl;
|
|
u_int16_t edata;
|
|
int i;
|
|
|
|
ectrl = inb(port + IEE16_ECTRL);
|
|
ectrl &= ~IEE16_RESET_ASIC;
|
|
|
|
for (edata = 0, i = 0; i < 16; i++) {
|
|
edata = edata << 1;
|
|
ie_ee16_hw_eeprom_clock(port, 1);
|
|
ectrl = inb(port + IEE16_ECTRL);
|
|
if (ectrl & IEE16_ECTRL_EEDO) {
|
|
edata |= 1;
|
|
}
|
|
ie_ee16_hw_eeprom_clock(port, 0);
|
|
}
|
|
return (edata);
|
|
}
|
|
|
|
static u_int16_t
|
|
ie_ee16_hw_read_eeprom (u_int32_t port, int loc)
|
|
{
|
|
u_int8_t ectrl;
|
|
u_int16_t edata;
|
|
|
|
ectrl = inb(port + IEE16_ECTRL);
|
|
ectrl &= IEE16_ECTRL_MASK;
|
|
ectrl |= IEE16_ECTRL_EECS;
|
|
outb(port + IEE16_ECTRL, ectrl);
|
|
|
|
ie_ee16_hw_eeprom_out(port, IEE16_EEPROM_READ, IEE16_EEPROM_OPSIZE1);
|
|
ie_ee16_hw_eeprom_out(port, loc, IEE16_EEPROM_ADDR_SIZE);
|
|
edata = ie_ee16_hw_eeprom_in(port);
|
|
|
|
ectrl = inb(port + IEE16_ECTRL);
|
|
ectrl &= ~(IEE16_RESET_ASIC | IEE16_ECTRL_EEDI | IEE16_ECTRL_EECS);
|
|
outb(port + IEE16_ECTRL, ectrl);
|
|
|
|
ie_ee16_hw_eeprom_clock(port, 1);
|
|
ie_ee16_hw_eeprom_clock(port, 0);
|
|
|
|
return (edata);
|
|
}
|
|
|
|
/*
|
|
* AT&T StarLan/
|
|
*/
|
|
|
|
static int
|
|
ie_isa_sl_probe (device_t dev)
|
|
{
|
|
u_int32_t iobase;
|
|
|
|
/* No ISA-PnP support */
|
|
if (isa_get_vendorid(dev))
|
|
return (ENXIO);
|
|
|
|
/* ISA-HINT support only! */
|
|
if (device_get_desc(dev))
|
|
return (EBUSY);
|
|
|
|
/* Have we at least an ioport? */
|
|
if ((iobase = bus_get_resource_start(dev, SYS_RES_IOPORT, 0)) == 0)
|
|
return (ENXIO);
|
|
|
|
/* Is this really an SL board? */
|
|
if (ie_isa_sl_get_hard_type(iobase) == IE_NONE)
|
|
return (ENXIO);
|
|
|
|
return (ENXIO);
|
|
}
|
|
|
|
static int
|
|
ie_isa_sl_attach (device_t dev)
|
|
{
|
|
struct ie_softc * sc;
|
|
int error;
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
sc->io_rid = 0;
|
|
sc->irq_rid = 0;
|
|
sc->mem_rid = 0;
|
|
|
|
error = ie_alloc_resources(dev);
|
|
if (error) {
|
|
goto bad;
|
|
}
|
|
|
|
/* Is this really an SL board? */
|
|
if ((sc->hard_type = ie_isa_sl_get_hard_type(PORT(sc))) == IE_NONE) {
|
|
error = ENXIO;
|
|
goto bad;
|
|
}
|
|
|
|
sc->hard_vers = SL_REV(inb(PORT(sc) + IEATT_REVISION));
|
|
if (sc->hard_type == IE_NI5210) {
|
|
sc->bus_use = 1;
|
|
} else {
|
|
sc->bus_use = 0;
|
|
}
|
|
|
|
sc->ie_reset_586 = sl_reset_586;
|
|
sc->ie_chan_attn = sl_chan_attn;
|
|
|
|
if (!check_ie_present(sc)) {
|
|
error = ENXIO;
|
|
goto bad;
|
|
}
|
|
|
|
switch (sc->hard_type) {
|
|
case IE_EN100:
|
|
case IE_STARLAN10:
|
|
case IE_SLFIBER:
|
|
case IE_NI5210:
|
|
sl_read_ether(sc, sc->arpcom.ac_enaddr);
|
|
break;
|
|
default:
|
|
if (bootverbose)
|
|
device_printf(sc->dev, "unknown AT&T board type code %d\n", sc->hard_type);
|
|
error = ENXIO;
|
|
goto bad;
|
|
break;
|
|
}
|
|
|
|
error = ie_attach(dev);
|
|
if (error) {
|
|
device_printf(dev, "ie_attach() failed.\n");
|
|
goto bad;
|
|
}
|
|
|
|
error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET,
|
|
ie_intr, sc, &sc->irq_ih);
|
|
if (error) {
|
|
device_printf(dev, "Unable to register interrupt handler\n");
|
|
goto bad;
|
|
}
|
|
|
|
return (0);
|
|
bad:
|
|
ie_release_resources(dev);
|
|
|
|
return (error);
|
|
}
|
|
|
|
static enum ie_hardware
|
|
ie_isa_sl_get_hard_type (u_int32_t port)
|
|
{
|
|
u_char c;
|
|
enum ie_hardware retval;
|
|
|
|
c = inb(port + IEATT_REVISION);
|
|
switch (SL_BOARD(c)) {
|
|
case SL1_BOARD:
|
|
if (inb(port + IEATT_ATTRIB) != NI5210_BOARD)
|
|
retval = IE_NONE;
|
|
retval = IE_NI5210;
|
|
break;
|
|
case SL10_BOARD:
|
|
retval = IE_STARLAN10;
|
|
break;
|
|
case EN100_BOARD:
|
|
retval = IE_EN100;
|
|
break;
|
|
case SLFIBER_BOARD:
|
|
retval = IE_SLFIBER;
|
|
break;
|
|
default:
|
|
retval = IE_NONE;
|
|
}
|
|
return (retval);
|
|
}
|
|
|
|
static devclass_t ie_devclass;
|
|
|
|
static device_method_t ie_isa_3C507_methods[] = {
|
|
DEVMETHOD(device_identify, ie_isa_3C507_identify),
|
|
DEVMETHOD(device_probe, ie_isa_3C507_probe),
|
|
DEVMETHOD(device_attach, ie_isa_3C507_attach),
|
|
DEVMETHOD(device_detach, ie_detach),
|
|
{ 0, 0 }
|
|
};
|
|
static driver_t ie_isa_3C507_driver = {
|
|
"ie",
|
|
ie_isa_3C507_methods,
|
|
sizeof(struct ie_softc),
|
|
};
|
|
DRIVER_MODULE(ie_3C507, isa, ie_isa_3C507_driver, ie_devclass, ie_modevent, 0);
|
|
MODULE_DEPEND(ie_3C507, elink, 1, 1, 1);
|
|
|
|
static device_method_t ie_isa_ee16_methods[] = {
|
|
DEVMETHOD(device_identify, ie_isa_ee16_identify),
|
|
DEVMETHOD(device_probe, ie_isa_ee16_probe),
|
|
DEVMETHOD(device_attach, ie_isa_ee16_attach),
|
|
DEVMETHOD(device_detach, ie_detach),
|
|
{ 0, 0 }
|
|
};
|
|
static driver_t ie_isa_ee16_driver = {
|
|
"ie",
|
|
ie_isa_ee16_methods,
|
|
sizeof(struct ie_softc),
|
|
};
|
|
DRIVER_MODULE(ie_EE16, isa, ie_isa_ee16_driver, ie_devclass, ie_modevent, 0);
|
|
|
|
static device_method_t ie_isa_sl_methods[] = {
|
|
DEVMETHOD(device_probe, ie_isa_sl_probe),
|
|
DEVMETHOD(device_attach, ie_isa_sl_attach),
|
|
DEVMETHOD(device_detach, ie_detach),
|
|
{ 0, 0 }
|
|
};
|
|
static driver_t ie_isa_sl_driver = {
|
|
"ie",
|
|
ie_isa_sl_methods,
|
|
sizeof(struct ie_softc),
|
|
};
|
|
DRIVER_MODULE(ie_SL, isa, ie_isa_sl_driver, ie_devclass, ie_modevent, 0);
|
|
|
|
static int
|
|
ie_modevent (mod, what, arg)
|
|
module_t mod;
|
|
int what;
|
|
void * arg;
|
|
{
|
|
device_t * devs;
|
|
int count;
|
|
int i;
|
|
|
|
switch (what) {
|
|
case MOD_LOAD:
|
|
break;
|
|
case MOD_UNLOAD:
|
|
devclass_get_devices(ie_devclass, &devs, &count);
|
|
for (i = 0; i < count; i++)
|
|
device_delete_child(device_get_parent(devs[i]), devs[i]);
|
|
break;
|
|
default:
|
|
break;
|
|
};
|
|
|
|
return (0);
|
|
}
|