blob: 15540c24dd9507de6064909302b9daa1765d4bc7 [file] [log] [blame]
/**************************************************************************
Etherboot - BOOTP/TFTP Bootstrap Program
i82586 NIC driver for Etherboot
Ken Yap, January 1998
***************************************************************************/
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2, or (at
* your option) any later version.
*/
#include "etherboot.h"
#include "nic.h"
#include "cards.h"
#include "timer.h"
#define udelay(n) waiton_timer2(((n)*TICKS_PER_MS)/1000)
/* Sources of information:
Donald Becker's excellent 3c507 driver in Linux
Intel 82596 data sheet (yes, 82596; it has a 586 compatibility mode)
*/
/* Code below mostly stolen wholesale from 3c507.c driver in Linux */
/*
Details of the i82586.
You'll really need the databook to understand the details of this part,
but the outline is that the i82586 has two separate processing units.
Both are started from a list of three configuration tables, of which only
the last, the System Control Block (SCB), is used after reset-time. The SCB
has the following fields:
Status word
Command word
Tx/Command block addr.
Rx block addr.
The command word accepts the following controls for the Tx and Rx units:
*/
#define CUC_START 0x0100
#define CUC_RESUME 0x0200
#define CUC_SUSPEND 0x0300
#define RX_START 0x0010
#define RX_RESUME 0x0020
#define RX_SUSPEND 0x0030
/* The Rx unit uses a list of frame descriptors and a list of data buffer
descriptors. We use full-sized (1518 byte) data buffers, so there is
a one-to-one pairing of frame descriptors to buffer descriptors.
The Tx ("command") unit executes a list of commands that look like:
Status word Written by the 82586 when the command is done.
Command word Command in lower 3 bits, post-command action in upper 3
Link word The address of the next command.
Parameters (as needed).
Some definitions related to the Command Word are:
*/
#define CMD_EOL 0x8000 /* The last command of the list, stop. */
#define CMD_SUSP 0x4000 /* Suspend after doing cmd. */
#define CMD_INTR 0x2000 /* Interrupt after doing cmd. */
enum commands {
CmdNOp = 0, CmdSASetup = 1, CmdConfigure = 2, CmdMulticastList = 3,
CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7};
/*
Details of the EtherLink16 Implementation
The 3c507 and NI5210 are generic shared-memory i82586 implementations.
3c507: The host can map 16K, 32K, 48K, or 64K of the 64K memory into
0x0[CD][08]0000, or all 64K into 0xF[02468]0000.
NI5210: The host can map 8k or 16k at 0x[CDE][048C]000 but we
assume 8k because to have 16k you cannot put a ROM on the NIC.
*/
/* Offsets from the base I/O address. */
#ifdef INCLUDE_3C507
#define SA_DATA 0 /* Station address data, or 3Com signature. */
#define MISC_CTRL 6 /* Switch the SA_DATA banks, and bus config bits. */
#define RESET_IRQ 10 /* Reset the latched IRQ line. */
#define I82586_ATTN 11 /* Frob the 82586 Channel Attention line. */
#define ROM_CONFIG 13
#define MEM_CONFIG 14
#define IRQ_CONFIG 15
#define EL16_IO_EXTENT 16
/* The ID port is used at boot-time to locate the ethercard. */
#define ID_PORT 0x100
#endif
#ifdef INCLUDE_NI5210
#define NI52_RESET 0 /* writing to this address, resets the i82586 */
#define I82586_ATTN 1 /* channel attention, kick the 586 */
#endif
#ifdef INCLUDE_EXOS205
#define EXOS205_RESET 0 /* writing to this address, resets the i82586 */
#define I82586_ATTN 1 /* channel attention, kick the 586 */
#endif
/* Offsets to registers in the mailbox (SCB). */
#define iSCB_STATUS 0x8
#define iSCB_CMD 0xA
#define iSCB_CBL 0xC /* Command BLock offset. */
#define iSCB_RFA 0xE /* Rx Frame Area offset. */
/* Since the 3c507 maps the shared memory window so that the last byte is
at 82586 address FFFF, the first byte is at 82586 address 0, 16K, 32K, or
48K corresponding to window sizes of 64K, 48K, 32K and 16K respectively.
We can account for this be setting the 'SBC Base' entry in the ISCP table
below for all the 16 bit offset addresses, and also adding the 'SCB Base'
value to all 24 bit physical addresses (in the SCP table and the TX and RX
Buffer Descriptors).
-Mark
*/
/*
What follows in 'init_words[]' is the "program" that is downloaded to the
82586 memory. It's mostly tables and command blocks, and starts at the
reset address 0xfffff6. This is designed to be similar to the EtherExpress,
thus the unusual location of the SCB at 0x0008.
Even with the additional "don't care" values, doing it this way takes less
program space than initializing the individual tables, and I feel it's much
cleaner.
The databook is particularly useless for the first two structures, I had
to use the Crynwr driver as an example.
The memory setup is as follows:
*/
#define CONFIG_CMD 0x18
#define SET_SA_CMD 0x24
#define SA_OFFSET 0x2A
#define IDLELOOP 0x30
#define TDR_CMD 0x38
#define TDR_TIME 0x3C
#define DUMP_CMD 0x40
#define DIAG_CMD 0x48
#define SET_MC_CMD 0x4E
#define DUMP_DATA 0x56 /* A 170 byte buffer for dump and Set-MC into. */
#define TX_BUF_START 0x0100
#define TX_BUF_SIZE (1518+14+20+16) /* packet+header+TBD */
#define RX_BUF_START 0x1000
#define RX_BUF_SIZE (1518+14+18) /* packet+header+RBD */
#define RX_BUF_END (mem_end - mem_start - 20)
/*
That's it: only 86 bytes to set up the beast, including every extra
command available. The 170 byte buffer at DUMP_DATA is shared between the
Dump command (called only by the diagnostic program) and the SetMulticastList
command.
To complete the memory setup you only have to write the station address at
SA_OFFSET and create the Tx & Rx buffer lists.
The Tx command chain and buffer list is setup as follows:
A Tx command table, with the data buffer pointing to...
A Tx data buffer descriptor. The packet is in a single buffer, rather than
chaining together several smaller buffers.
A NoOp command, which initially points to itself,
And the packet data.
A transmit is done by filling in the Tx command table and data buffer,
re-writing the NoOp command, and finally changing the offset of the last
command to point to the current Tx command. When the Tx command is finished,
it jumps to the NoOp, when it loops until the next Tx command changes the
"link offset" in the NoOp. This way the 82586 never has to go through the
slow restart sequence.
The Rx buffer list is set up in the obvious ring structure. We have enough
memory (and low enough interrupt latency) that we can avoid the complicated
Rx buffer linked lists by alway associating a full-size Rx data buffer with
each Rx data frame.
I currently use one transmit buffer starting at TX_BUF_START (0x0100), and
use the rest of memory, from RX_BUF_START to RX_BUF_END, for Rx buffers.
*/
static unsigned short init_words[] = {
/* System Configuration Pointer (SCP). */
#if defined(INCLUDE_3C507)
0x0000, /* Set bus size to 16 bits. */
#else
0x0001, /* Set bus size to 8 bits */
#endif
0,0, /* pad words. */
0x0000,0x0000, /* ISCP phys addr, set in init_82586_mem(). */
/* Intermediate System Configuration Pointer (ISCP). */
0x0001, /* Status word that's cleared when init is done. */
0x0008,0,0, /* SCB offset, (skip, skip) */
/* System Control Block (SCB). */
0,0xf000|RX_START|CUC_START, /* SCB status and cmd. */
CONFIG_CMD, /* Command list pointer, points to Configure. */
RX_BUF_START, /* Rx block list. */
0,0,0,0, /* Error count: CRC, align, buffer, overrun. */
/* 0x0018: Configure command. Change to put MAC data with packet. */
0, CmdConfigure, /* Status, command. */
SET_SA_CMD, /* Next command is Set Station Addr. */
0x0804, /* "4" bytes of config data, 8 byte FIFO. */
0x2e40, /* Magic values, including MAC data location. */
0, /* Unused pad word. */
/* 0x0024: Setup station address command. */
0, CmdSASetup,
SET_MC_CMD, /* Next command. */
0xaa00,0xb000,0x0bad, /* Station address (to be filled in) */
/* 0x0030: NOP, looping back to itself. Point to first Tx buffer to Tx. */
0, CmdNOp, IDLELOOP, 0 /* pad */,
/* 0x0038: A unused Time-Domain Reflectometer command. */
0, CmdTDR, IDLELOOP, 0,
/* 0x0040: An unused Dump State command. */
0, CmdDump, IDLELOOP, DUMP_DATA,
/* 0x0048: An unused Diagnose command. */
0, CmdDiagnose, IDLELOOP,
/* 0x004E: An empty set-multicast-list command. */
0, CmdMulticastList, IDLELOOP, 0,
};
/* NIC specific static variables go here */
static unsigned short ioaddr, irq, scb_base;
static Address mem_start, mem_end;
static unsigned short rx_head, rx_tail;
#define read_mem(m,s) fmemcpy((char *)s, m, sizeof(s))
static void setup_rx_buffers(struct nic *nic)
{
Address write_ptr;
unsigned short cur_rx_buf;
static unsigned short rx_cmd[16] = {
0x0000, /* Rx status */
0x0000, /* Rx command, only and last */
RX_BUF_START, /* Link (will be adjusted) */
RX_BUF_START + 22, /* Buffer offset (will be adjusted) */
0x0000, 0x0000, 0x0000, /* Pad for dest addr */
0x0000, 0x0000, 0x0000, /* Pad for source addr */
0x0000, /* Pad for protocol */
0x0000, /* Buffer: Actual count */
-1, /* Buffer: Next (none) */
RX_BUF_START + 0x20, /* Buffer: Address low (+ scb_base) (will be adjusted) */
0x0000, /* Buffer: Address high */
0x8000 | (RX_BUF_SIZE - 0x20)
};
cur_rx_buf = rx_head = RX_BUF_START;
do { /* While there is room for one more buffer */
write_ptr = mem_start + cur_rx_buf;
/* adjust some contents */
rx_cmd[1] = 0x0000;
rx_cmd[2] = cur_rx_buf + RX_BUF_SIZE;
rx_cmd[3] = cur_rx_buf + 22;
rx_cmd[13] = cur_rx_buf + 0x20 + scb_base;
memcpy((char *)write_ptr, (char *)rx_cmd, sizeof(rx_cmd));
rx_tail = cur_rx_buf;
cur_rx_buf += RX_BUF_SIZE;
} while (cur_rx_buf <= RX_BUF_END - RX_BUF_SIZE);
/* Terminate the list by setting the EOL bit and wrap ther pointer
to make the list a ring. */
write_ptr = mem_start + rx_tail;
rx_cmd[1] = 0xC000;
rx_cmd[2] = rx_head;
memcpy((char *)write_ptr, (char *)rx_cmd, sizeof(unsigned short) * 3);
}
static void ack_status(void)
{
unsigned short cmd, status;
unsigned short *shmem = (short *)mem_start;
cmd = (status = shmem[iSCB_STATUS>>1]) & 0xf000;
if (status & 0x100) /* CU suspended? */
cmd |= CUC_RESUME;
if ((status & 0x200) == 0) /* CU not active? */
cmd |= CUC_START;
if (status & 0x010) /* RU suspended? */
cmd |= RX_RESUME;
else if ((status & 0x040) == 0) /* RU not active? */
cmd |= RX_START;
if (cmd == 0) /* Nothing to do */
return;
shmem[iSCB_CMD>>1] = cmd;
#if defined(DEBUG)
printf("Status %hX Command %hX\n", status, cmd);
#endif
outb(0, ioaddr + I82586_ATTN);
}
/**************************************************************************
RESET - Reset adapter
***************************************************************************/
static void i82586_reset(struct nic *nic)
{
unsigned long time;
unsigned short *shmem = (short *)mem_start;
/* put the card in its initial state */
#ifdef INCLUDE_3C507
/* Enable loopback to protect the wire while starting up,
and hold the 586 in reset during the memory initialisation. */
outb(0x20, ioaddr + MISC_CTRL);
#endif
/* Fix the ISCP address and base. */
init_words[3] = scb_base;
init_words[7] = scb_base;
/* Write the words at 0xfff6. */
/* Write the words at 0x0000. */
/* Fill in the station address. */
memcpy((char *)(mem_end - 10), (char *)init_words, 10);
memcpy((char *)mem_start, (char *)&init_words[5], sizeof(init_words) - 10);
memcpy((char *)mem_start + SA_OFFSET, nic->node_addr, ETH_ALEN);
setup_rx_buffers(nic);
#ifdef INCLUDE_3C507
/* Start the 586 by releasing the reset line, but leave loopback. */
outb(0xA0, ioaddr + MISC_CTRL);
#endif
/* This was time consuming to track down; you need to give two channel
attention signals to reliably start up the i82586. */
outb(0, ioaddr + I82586_ATTN);
time = currticks() + TICKS_PER_SEC; /* allow 1 second to init */
while (
shmem[iSCB_STATUS>>1] == 0)
{
if (currticks() > time)
{
printf("i82586 initialisation timed out with status %hX, cmd %hX\n",
shmem[iSCB_STATUS>>1], shmem[iSCB_CMD>>1]);
break;
}
}
/* Issue channel-attn -- the 82586 won't start. */
outb(0, ioaddr + I82586_ATTN);
#ifdef INCLUDE_3C507
/* Disable loopback. */
outb(0x80, ioaddr + MISC_CTRL);
#endif
#if defined(DEBUG)
printf("i82586 status %hX, cmd %hX\n",
shmem[iSCB_STATUS>>1], shmem[iSCB_CMD>>1]);
#endif
}
/**************************************************************************
POLL - Wait for a frame
***************************************************************************/
static int i82586_poll(struct nic *nic)
{
int status;
unsigned short rfd_cmd, next_rx_frame, data_buffer_addr,
frame_status, pkt_len;
unsigned short *shmem = (short *)mem_start + rx_head;
/* return true if there's an ethernet packet ready to read */
if (
((frame_status = shmem[0]) & 0x8000) == 0)
return (0); /* nope */
rfd_cmd = shmem[1];
next_rx_frame = shmem[2];
data_buffer_addr = shmem[3];
pkt_len = shmem[11];
status = 0;
if (rfd_cmd != 0 || data_buffer_addr != rx_head + 22
|| (pkt_len & 0xC000) != 0xC000)
printf("\nRx frame corrupt, discarded");
else if ((frame_status & 0x2000) == 0)
printf("\nRx frame had error");
else
{
/* We have a frame, copy it to our buffer */
pkt_len &= 0x3FFF;
memcpy(nic->packet, (char *)mem_start + rx_head + 0x20, pkt_len);
/* Only packets not from ourself */
if (memcmp(nic->packet + ETH_ALEN, nic->node_addr, ETH_ALEN) != 0)
{
nic->packetlen = pkt_len;
status = 1;
}
}
/* Clear the status word and set EOL on Rx frame */
shmem[0] = 0;
shmem[1] = 0xC000;
*(short *)(mem_start + rx_tail + 2) = 0;
rx_tail = rx_head;
rx_head = next_rx_frame;
ack_status();
return (status);
}
/**************************************************************************
TRANSMIT - Transmit a frame
***************************************************************************/
static void i82586_transmit(
struct nic *nic,
const char *d, /* Destination */
unsigned int t, /* Type */
unsigned int s, /* size */
const char *p) /* Packet */
{
Address bptr;
unsigned short type, z;
static unsigned short tx_cmd[11] = {
0x0, /* Tx status */
CmdTx, /* Tx command */
TX_BUF_START+16, /* Next command is a NoOp */
TX_BUF_START+8, /* Data Buffer offset */
0x8000, /* | with size */
0xffff, /* No next data buffer */
TX_BUF_START+22, /* + scb_base */
0x0, /* Buffer address high bits (always zero) */
0x0, /* Nop status */
CmdNOp, /* Nop command */
TX_BUF_START+16 /* Next is myself */
};
unsigned short *shmem = (short *)mem_start + TX_BUF_START;
/* send the packet to destination */
/* adjust some contents */
type = htons(t);
if (s < ETH_ZLEN)
s = ETH_ZLEN;
tx_cmd[4] = (s + ETH_HLEN) | 0x8000;
tx_cmd[6] = TX_BUF_START + 22 + scb_base;
bptr = mem_start + TX_BUF_START;
memcpy((char *)bptr, (char *)tx_cmd, sizeof(tx_cmd));
bptr += sizeof(tx_cmd);
memcpy((char *)bptr, d, ETH_ALEN);
bptr += ETH_ALEN;
memcpy((char *)bptr, nic->node_addr, ETH_ALEN);
bptr += ETH_ALEN;
memcpy((char *)bptr, (char *)&type, sizeof(type));
bptr += sizeof(type);
memcpy((char *)bptr, p, s);
/* Change the offset in the IDLELOOP */
*(unsigned short *)(mem_start + IDLELOOP + 4) = TX_BUF_START;
/* Wait for transmit completion */
while (
(shmem[0] & 0x2000) == 0)
;
/* Change the offset in the IDLELOOP back and
change the final loop to point here */
*(unsigned short *)(mem_start + IDLELOOP + 4) = IDLELOOP;
*(unsigned short *)(mem_start + TX_BUF_START + 20) = IDLELOOP;
ack_status();
}
/**************************************************************************
DISABLE - Turn off ethernet interface
***************************************************************************/
static void i82586_disable(struct nic *nic)
{
unsigned short *shmem = (short *)mem_start;
#if 0
/* Flush the Tx and disable Rx. */
shmem[iSCB_CMD>>1] = RX_SUSPEND | CUC_SUSPEND;
outb(0, ioaddr + I82586_ATTN);
#ifdef INCLUDE_NI5210
outb(0, ioaddr + NI52_RESET);
#endif
#endif /* 0 */
}
#ifdef INCLUDE_3C507
static int t507_probe1(struct nic *nic, unsigned short ioaddr)
{
int i;
Address size;
char mem_config;
char if_port;
if (inb(ioaddr) != '*' || inb(ioaddr+1) != '3'
|| inb(ioaddr+2) != 'C' || inb(ioaddr+3) != 'O')
return (0);
irq = inb(ioaddr + IRQ_CONFIG) & 0x0f;
mem_config = inb(ioaddr + MEM_CONFIG);
if (mem_config & 0x20)
{
size = 65536L;
mem_start = 0xf00000L + (mem_config & 0x08 ? 0x080000L
: (((Address)mem_config & 0x3) << 17));
}
else
{
size = ((((Address)mem_config & 0x3) + 1) << 14);
mem_start = 0x0c0000L + (((Address)mem_config & 0x18) << 12);
}
mem_end = mem_start + size;
scb_base = 65536L - size;
if_port = inb(ioaddr + ROM_CONFIG) & 0x80;
/* Get station address */
outb(0x01, ioaddr + MISC_CTRL);
for (i = 0; i < ETH_ALEN; ++i)
{
nic->node_addr[i] = inb(ioaddr+i);
}
printf("\n3c507 ioaddr %#hX, IRQ %d, mem [%#X-%#X], %sternal xcvr, addr %!\n",
ioaddr, irq, mem_start, mem_end, if_port ? "in" : "ex", nic->node_addr);
return (1);
}
/**************************************************************************
PROBE - Look for an adapter, this routine's visible to the outside
***************************************************************************/
struct nic *t507_probe(struct nic *nic, unsigned short *probe_addrs)
{
static unsigned char init_ID_done = 0;
unsigned short lrs_state = 0xff;
static unsigned short io_addrs[] = { 0x300, 0x320, 0x340, 0x280, 0 };
unsigned short *p;
int i;
if (init_ID_done == 0)
{
/* Send the ID sequence to the ID_PORT to enable the board */
outb(0x00, ID_PORT);
for (i = 0; i < 255; ++i)
{
outb(lrs_state, ID_PORT);
lrs_state <<= 1;
if (lrs_state & 0x100)
lrs_state ^= 0xe7;
}
outb(0x00, ID_PORT);
init_ID_done = 1;
}
/* if probe_addrs is 0, then routine can use a hardwired default */
if (probe_addrs == 0)
probe_addrs = io_addrs;
for (p = probe_addrs; (ioaddr = *p) != 0; ++p)
if (t507_probe1(nic, ioaddr))
break;
if (ioaddr != 0)
{
/* point to NIC specific routines */
i82586_reset(nic);
nic->reset = i82586_reset;
nic->poll = i82586_poll;
nic->transmit = i82586_transmit;
nic->disable = i82586_disable;
return nic;
}
/* else */
{
return 0;
}
}
#endif
#ifdef INCLUDE_NI5210
static int ni5210_probe2(void)
{
unsigned short i;
unsigned short shmem[10];
/* Fix the ISCP address and base. */
init_words[3] = scb_base;
init_words[7] = scb_base;
/* Write the words at 0xfff6. */
/* Write the words at 0x0000. */
memcpy((char *)(mem_end - 10), (char *)init_words, 10);
memcpy((char *)mem_start, (char *)&init_words[5], sizeof(init_words) - 10);
if (*(unsigned short *)mem_start != 1)
return (0);
outb(0, ioaddr + NI52_RESET);
outb(0, ioaddr + I82586_ATTN);
udelay(32);
i = 50;
while (
shmem[iSCB_STATUS>>1] == 0)
{
if (--i == 0)
{
printf("i82586 initialisation timed out with status %hX, cmd %hX\n",
shmem[iSCB_STATUS>>1], shmem[iSCB_CMD>>1]);
break;
}
}
/* Issue channel-attn -- the 82586 won't start. */
outb(0, ioaddr + I82586_ATTN);
if (*(unsigned short *)mem_start != 0)
return (0);
return (1);
}
static int ni5210_probe1(struct nic *nic)
{
int i;
static Address mem_addrs[] = {
0xc0000, 0xc4000, 0xc8000, 0xcc000,
0xd0000, 0xd4000, 0xd8000, 0xdc000,
0xe0000, 0xe4000, 0xe8000, 0xec000,
0 };
Address *p;
if (inb(ioaddr + 6) != 0x0 || inb(ioaddr + 7) != 0x55)
return (0);
scb_base = -8192; /* assume 8k memory */
for (p = mem_addrs; (mem_start = *p) != 0; ++p)
if (mem_end = mem_start + 8192, ni5210_probe2())
break;
if (mem_start == 0)
return (0);
/* Get station address */
for (i = 0; i < ETH_ALEN; ++i)
{
nic->node_addr[i] = inb(ioaddr+i);
}
printf("\nNI5210 ioaddr %#hX, mem [%#X-%#X], addr %!\n",
ioaddr, mem_start, mem_end, nic->node_addr);
return (1);
}
struct nic *ni5210_probe(struct nic *nic, unsigned short *probe_addrs)
{
/* missing entries are addresses usually already used */
static unsigned short io_addrs[] = {
0x200, 0x208, 0x210, 0x218, 0x220, 0x228, 0x230, 0x238,
0x240, 0x248, 0x250, 0x258, 0x260, 0x268, 0x270, /*Par*/
0x280, 0x288, 0x290, 0x298, 0x2A0, 0x2A8, 0x2B0, 0x2B8,
0x2C0, 0x2C8, 0x2D0, 0x2D8, 0x2E0, 0x2E8, 0x2F0, /*Ser*/
0x300, 0x308, 0x310, 0x318, 0x320, 0x328, 0x330, 0x338,
0x340, 0x348, 0x350, 0x358, 0x360, 0x368, 0x370, /*Par*/
0x380, 0x388, 0x390, 0x398, 0x3A0, 0x3A8, /*Vid,Par*/
0x3C0, 0x3C8, 0x3D0, 0x3D8, 0x3E0, 0x3E8, /*Ser*/
0x0
};
unsigned short *p;
int i;
/* if probe_addrs is 0, then routine can use a hardwired default */
if (probe_addrs == 0)
probe_addrs = io_addrs;
for (p = probe_addrs; (ioaddr = *p) != 0; ++p)
if (ni5210_probe1(nic))
break;
if (ioaddr != 0)
{
/* point to NIC specific routines */
i82586_reset(nic);
nic->reset = i82586_reset;
nic->poll = i82586_poll;
nic->transmit = i82586_transmit;
nic->disable = i82586_disable;
return nic;
}
/* else */
{
return 0;
}
}
#endif
#ifdef INCLUDE_EXOS205
/*
* Code to download to I186 in EXOS205
*/
static unsigned char exos_i186_init[] =
{
0x08,0x00,0x14,0x00,0x00,0x00,0xaa,0xfa,0x33,0xc0,0xba,0xfe,0xff,0xef,0xb8,0xf8,
0xff,0xe7,0xa0,0xb8,0x7c,0x00,0xe7,0xa4,0xb8,0xbc,0x80,0xe7,0xa8,0x8c,0xc8,0x8e,
0xd8,0xbb,0x2f,0x0e,0xc6,0x07,0xa5,0x33,0xc9,0xeb,0x00,0xeb,0x00,0xeb,0x00,0xe2,
0xf8,0xbe,0x2c,0x0e,0xba,0x02,0x05,0x33,0xdb,0xb9,0x03,0x00,0xec,0x24,0x0f,0x8a,
0xe0,0x02,0xd8,0x42,0x42,0xec,0x02,0xd8,0xd0,0xe0,0xd0,0xe0,0xd0,0xe0,0xd0,0xe0,
0x0a,0xc4,0x88,0x04,0x42,0x42,0x46,0xe2,0xe3,0x8a,0xe3,0xd0,0xec,0xd0,0xec,0xd0,
0xec,0xd0,0xec,0x80,0xe3,0x0f,0x02,0xe3,0x80,0xf4,0x05,0xec,0x3a,0xe0,0x74,0x05,
0xc6,0x04,0x5a,0xeb,0xfe,0xc6,0x04,0x55,0x33,0xc0,0x8e,0xd8,0xbe,0x38,0x00,0xc7,
0x04,0xce,0x0e,0x46,0x46,0xc7,0x04,0x00,0xff,0xfb,0xba,0x3c,0x00,0xb8,0x03,0x00,
0xef,0x33,0xdb,0x33,0xc9,0xbd,0x04,0x0f,0x90,0x90,0x90,0x90,0xe2,0xfa,0x43,0x2e,
0x89,0x5e,0x00,0xeb,0xf3,0x52,0xba,0x00,0x06,0xef,0x50,0x53,0x55,0xbd,0xf8,0x0e,
0x2e,0x8b,0x5e,0x00,0x43,0x2e,0x89,0x5e,0x00,0xba,0x22,0x00,0xb8,0x00,0x80,0xef,
0x5d,0x5b,0x58,0x5a,0xcf,0x49,0x4e,0x54,0x52,0x20,0x63,0x6e,0x74,0x2d,0x3e,0x00,
0x00,0x4c,0x4f,0x4f,0x50,0x20,0x63,0x6e,0x74,0x2d,0x3e,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xea,0x30,0x0e,0x00,0xff,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,00
};
/* These offsets are from the end of the i186 download code */
#define OFFSET_SEMA 0x1D1
#define OFFSET_ADDR 0x1D7
static int exos205_probe2(void)
{
unsigned short i;
unsigned short shmem[10];
/* Fix the ISCP address and base. */
init_words[3] = scb_base;
init_words[7] = scb_base;
/* Write the words at 0xfff6. */
/* Write the words at 0x0000. */
memcpy((char *)(mem_end - 10), (char *)init_words, 10);
memcpy((char *)mem_start, (char *)&init_words[5], sizeof(init_words) - 10);
if (*(unsigned short *)mem_start != 1)
return (0);
outb(0, ioaddr + EXOS205_RESET);
outb(0, ioaddr + I82586_ATTN);
i = 50;
while (
shmem[iSCB_STATUS>>1] == 0)
{
if (--i == 0)
{
printf("i82586 initialisation timed out with status %hX, cmd %hX\n",
shmem[iSCB_STATUS>>1], shmem[iSCB_CMD>>1]);
break;
}
}
/* Issue channel-attn -- the 82586 won't start. */
outb(0, ioaddr + I82586_ATTN);
if (*(unsigned short *)mem_start != 0)
return (0);
return (1);
}
static int exos205_probe1(struct nic *nic)
{
int i;
/* If you know the other addresses please let me know */
static Address mem_addrs[] = {
0xcc000, 0 };
Address *p;
scb_base = -16384; /* assume 8k memory */
for (p = mem_addrs; (mem_start = *p) != 0; ++p)
if (mem_end = mem_start + 16384, exos205_probe2())
break;
if (mem_start == 0)
return (0);
/* Get station address */
for (i = 0; i < ETH_ALEN; ++i)
{
nic->node_addr[i] = inb(ioaddr+i);
}
printf("\nEXOS205 ioaddr %#hX, mem [%#X-%#X], addr %!\n",
ioaddr, mem_start, mem_end, nic->node_addr);
return (1);
}
struct nic *exos205_probe(struct nic *nic, unsigned short *probe_addrs)
{
/* If you know the other addresses, please let me know */
static unsigned short io_addrs[] = {
0x310, 0x0
};
unsigned short *p;
int i;
/* if probe_addrs is 0, then routine can use a hardwired default */
if (probe_addrs == 0)
probe_addrs = io_addrs;
for (p = probe_addrs; (ioaddr = *p) != 0; ++p)
if (exos205_probe1(nic))
break;
if (ioaddr != 0)
{
/* point to NIC specific routines */
i82586_reset(nic);
nic->reset = i82586_reset;
nic->poll = i82586_poll;
nic->transmit = i82586_transmit;
nic->disable = i82586_disable;
return nic;
}
/* else */
{
return 0;
}
}
#endif