mirror of
https://git.savannah.gnu.org/git/grub.git
synced 2026-10-22 15:22:39 +00:00
970 lines
29 KiB
C
970 lines
29 KiB
C
/**************************************************************************
|
|
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"
|
|
|
|
/* 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;
|
|
#ifdef ETHERBOOT32
|
|
memcpy((char *)write_ptr, (char *)rx_cmd, sizeof(rx_cmd));
|
|
#endif
|
|
#ifdef ETHERBOOT16
|
|
memcpyf(write_ptr, (char *)rx_cmd, sizeof(rx_cmd));
|
|
#endif
|
|
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;
|
|
#ifdef ETHERBOOT32
|
|
memcpy((char *)write_ptr, (char *)rx_cmd, sizeof(unsigned short) * 3);
|
|
#endif
|
|
#ifdef ETHERBOOT16
|
|
memcpyf(write_ptr, (char *)rx_cmd, sizeof(unsigned short) * 3);
|
|
#endif
|
|
}
|
|
|
|
static void ack_status(void)
|
|
{
|
|
unsigned short cmd, status;
|
|
#ifdef ETHERBOOT32
|
|
unsigned short *shmem = (short *)mem_start;
|
|
#endif
|
|
#ifdef ETHERBOOT16
|
|
unsigned short shmem[CONFIG_CMD>>1];
|
|
#endif
|
|
|
|
#ifdef ETHERBOOT16
|
|
read_mem(mem_start, shmem);
|
|
#endif
|
|
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;
|
|
#ifdef ETHERBOOT16
|
|
memcpyf(mem_start, shmem, sizeof(shmem));
|
|
#endif
|
|
#if defined(DEBUG)
|
|
printf("Status %x Command %x\n", status, cmd);
|
|
#endif
|
|
outb(0, ioaddr + I82586_ATTN);
|
|
}
|
|
|
|
/**************************************************************************
|
|
RESET - Reset adapter
|
|
***************************************************************************/
|
|
|
|
static void i82586_reset(struct nic *nic)
|
|
{
|
|
unsigned long time;
|
|
#ifdef ETHERBOOT32
|
|
unsigned short *shmem = (short *)mem_start;
|
|
#endif
|
|
#ifdef ETHERBOOT16
|
|
unsigned short shmem[CONFIG_CMD>>1];
|
|
#endif
|
|
|
|
/* 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. */
|
|
#ifdef ETHERBOOT32
|
|
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, ETHER_ADDR_SIZE);
|
|
#endif
|
|
#ifdef ETHERBOOT16
|
|
memcpyf(mem_end - 10, (char *)init_words, 10);
|
|
memcpyf(mem_start, (char *)&init_words[5], sizeof(init_words) - 10);
|
|
memcpyf(mem_start + SA_OFFSET, nic->node_addr, ETHER_ADDR_SIZE);
|
|
#endif
|
|
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 (
|
|
#ifdef ETHERBOOT16
|
|
read_mem(mem_start, shmem),
|
|
#endif
|
|
shmem[iSCB_STATUS>>1] == 0)
|
|
{
|
|
if (currticks() > time)
|
|
{
|
|
printf("i82586 initialisation timed out with status %x, cmd %x\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)
|
|
#ifdef ETHERBOOT16
|
|
read_mem(mem_start, shmem);
|
|
#endif
|
|
printf("i82586 status %x, cmd %x\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;
|
|
#ifdef ETHERBOOT32
|
|
unsigned short *shmem = (short *)mem_start + rx_head;
|
|
#endif
|
|
#ifdef ETHERBOOT16
|
|
unsigned short shmem[16];
|
|
#endif
|
|
|
|
/* return true if there's an ethernet packet ready to read */
|
|
if (
|
|
#ifdef ETHERBOOT16
|
|
read_mem(mem_start + rx_head, shmem),
|
|
#endif
|
|
((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;
|
|
#ifdef ETHERBOOT32
|
|
memcpy(nic->packet, (char *)mem_start + rx_head + 0x20, pkt_len);
|
|
#endif
|
|
#ifdef ETHERBOOT16
|
|
fmemcpy(nic->packet, mem_start + rx_head + 0x20, pkt_len);
|
|
#endif
|
|
/* Only packets not from ourself */
|
|
if (memcmp(nic->packet + ETHER_ADDR_SIZE, nic->node_addr, ETHER_ADDR_SIZE) != 0)
|
|
{
|
|
nic->packetlen = pkt_len;
|
|
status = 1;
|
|
}
|
|
}
|
|
/* Clear the status word and set EOL on Rx frame */
|
|
shmem[0] = 0;
|
|
shmem[1] = 0xC000;
|
|
#ifdef ETHERBOOT16
|
|
memcpyf(mem_start + rx_head, shmem, sizeof(unsigned short) * 2);
|
|
/* Clear the EOL on previous RFD */
|
|
memcpyf(mem_start + rx_tail + 2, shmem, sizeof(unsigned short));
|
|
#endif
|
|
#ifdef ETHERBOOT32
|
|
*(short *)(mem_start + rx_tail + 2) = 0;
|
|
#endif
|
|
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 */
|
|
};
|
|
#ifdef ETHERBOOT32
|
|
unsigned short *shmem = (short *)mem_start + TX_BUF_START;
|
|
#endif
|
|
#ifdef ETHERBOOT16
|
|
unsigned short shmem[11];
|
|
#endif
|
|
|
|
/* send the packet to destination */
|
|
/* adjust some contents */
|
|
type = htons(t);
|
|
if (s < ETH_MIN_PACKET)
|
|
s = ETH_MIN_PACKET;
|
|
tx_cmd[4] = (s + ETHER_HDR_SIZE) | 0x8000;
|
|
tx_cmd[6] = TX_BUF_START + 22 + scb_base;
|
|
bptr = mem_start + TX_BUF_START;
|
|
#ifdef ETHERBOOT32
|
|
memcpy((char *)bptr, (char *)tx_cmd, sizeof(tx_cmd));
|
|
bptr += sizeof(tx_cmd);
|
|
memcpy((char *)bptr, d, ETHER_ADDR_SIZE);
|
|
bptr += ETHER_ADDR_SIZE;
|
|
memcpy((char *)bptr, nic->node_addr, ETHER_ADDR_SIZE);
|
|
bptr += ETHER_ADDR_SIZE;
|
|
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;
|
|
#endif
|
|
#ifdef ETHERBOOT16
|
|
memcpyf(bptr, (char *)tx_cmd, sizeof(tx_cmd));
|
|
bptr += sizeof(tx_cmd);
|
|
memcpyf(bptr, d, ETHER_ADDR_SIZE);
|
|
bptr += ETHER_ADDR_SIZE;
|
|
memcpyf(bptr, nic->node_addr, ETHER_ADDR_SIZE);
|
|
bptr += ETHER_ADDR_SIZE;
|
|
memcpyf(bptr, (char *)&type, sizeof(type));
|
|
bptr += sizeof(type);
|
|
memcpyf(bptr, p, s);
|
|
/* Change the offset in the IDLELOOP */
|
|
z = TX_BUF_START;
|
|
memcpyf(mem_start + IDLELOOP + 4, (char *)&z, sizeof(z));
|
|
#endif
|
|
/* Wait for transmit completion */
|
|
while (
|
|
#ifdef ETHERBOOT16
|
|
read_mem(mem_start + TX_BUF_START, shmem),
|
|
#endif
|
|
(shmem[0] & 0x2000) == 0)
|
|
;
|
|
/* Change the offset in the IDLELOOP back and
|
|
change the final loop to point here */
|
|
#ifdef ETHERBOOT32
|
|
*(unsigned short *)(mem_start + IDLELOOP + 4) = IDLELOOP;
|
|
*(unsigned short *)(mem_start + TX_BUF_START + 20) = IDLELOOP;
|
|
#endif
|
|
#ifdef ETHERBOOT16
|
|
z = IDLELOOP;
|
|
memcpyf(mem_start + IDLELOOP + 4, (char *)&z, sizeof(z));
|
|
memcpyf(mem_start + TX_BUF_START + 20, (char *)&z, sizeof(z));
|
|
#endif
|
|
ack_status();
|
|
}
|
|
|
|
/**************************************************************************
|
|
DISABLE - Turn off ethernet interface
|
|
***************************************************************************/
|
|
static void i82586_disable(struct nic *nic)
|
|
{
|
|
#ifdef ETHERBOOT32
|
|
unsigned short *shmem = (short *)mem_start;
|
|
#endif
|
|
#ifdef ETHERBOOT16
|
|
unsigned short shmem[CONFIG_CMD>>1];
|
|
#endif
|
|
|
|
#if 0
|
|
/* Flush the Tx and disable Rx. */
|
|
#ifdef ETHERBOOT16
|
|
read_mem(mem_start, shmem);
|
|
#endif
|
|
shmem[iSCB_CMD>>1] = RX_SUSPEND | CUC_SUSPEND;
|
|
#ifdef ETHERBOOT16
|
|
memcpyf(mem_start, shmem, sizeof(shmem));
|
|
#endif
|
|
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;
|
|
printf("\n3c507 ioaddr 0x%x, IRQ %d, mem [0x%X-0x%X], %sternal xcvr, addr ",
|
|
ioaddr, irq, mem_start, mem_end, if_port ? "in" : "ex");
|
|
/* Get station address */
|
|
outb(0x01, ioaddr + MISC_CTRL);
|
|
for (i = 0; i < ETHER_ADDR_SIZE; ++i)
|
|
{
|
|
printf("%b", nic->node_addr[i] = inb(ioaddr+i));
|
|
if (i < ETHER_ADDR_SIZE -1)
|
|
printf(":");
|
|
}
|
|
putchar('\n');
|
|
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. */
|
|
#ifdef ETHERBOOT32
|
|
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);
|
|
#endif
|
|
#ifdef ETHERBOOT16
|
|
memcpyf(mem_end - 10, (char *)init_words, 10);
|
|
memcpyf(mem_start, (char *)&init_words[5], sizeof(init_words) - 10);
|
|
fmemcpy((char *)&i, mem_start, sizeof(unsigned short));
|
|
if (i != 1)
|
|
return (0);
|
|
#endif
|
|
outb(0, ioaddr + NI52_RESET);
|
|
outb(0, ioaddr + I82586_ATTN);
|
|
i = 50;
|
|
while (
|
|
#ifdef ETHERBOOT16
|
|
read_mem(mem_start, shmem),
|
|
#endif
|
|
shmem[iSCB_STATUS>>1] == 0)
|
|
{
|
|
if (--i == 0)
|
|
{
|
|
printf("i82586 initialisation timed out with status %x, cmd %x\n",
|
|
shmem[iSCB_STATUS>>1], shmem[iSCB_CMD>>1]);
|
|
break;
|
|
}
|
|
}
|
|
/* Issue channel-attn -- the 82586 won't start. */
|
|
outb(0, ioaddr + I82586_ATTN);
|
|
#ifdef ETHERBOOT32
|
|
if (*(unsigned short *)mem_start != 0)
|
|
return (0);
|
|
#endif
|
|
#ifdef ETHERBOOT16
|
|
fmemcpy((char *)&i, mem_start, sizeof(unsigned short));
|
|
if (i != 0)
|
|
return (0);
|
|
#endif
|
|
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);
|
|
printf("\nNI5210 ioaddr 0x%x, mem [0x%X-0x%X], addr ",
|
|
ioaddr, mem_start, mem_end);
|
|
/* Get station address */
|
|
for (i = 0; i < ETHER_ADDR_SIZE; ++i)
|
|
{
|
|
printf("%b", nic->node_addr[i] = inb(ioaddr+i));
|
|
if (i < ETHER_ADDR_SIZE -1)
|
|
printf(":");
|
|
}
|
|
putchar('\n');
|
|
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. */
|
|
#ifdef ETHERBOOT32
|
|
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);
|
|
#endif
|
|
#ifdef ETHERBOOT16
|
|
memcpyf(mem_end - 10, (char *)init_words, 10);
|
|
memcpyf(mem_start, (char *)&init_words[5], sizeof(init_words) - 10);
|
|
fmemcpy((char *)&i, mem_start, sizeof(unsigned short));
|
|
if (i != 1)
|
|
return (0);
|
|
#endif
|
|
outb(0, ioaddr + EXOS205_RESET);
|
|
outb(0, ioaddr + I82586_ATTN);
|
|
i = 50;
|
|
while (
|
|
#ifdef ETHERBOOT16
|
|
read_mem(mem_start, shmem),
|
|
#endif
|
|
shmem[iSCB_STATUS>>1] == 0)
|
|
{
|
|
if (--i == 0)
|
|
{
|
|
printf("i82586 initialisation timed out with status %x, cmd %x\n",
|
|
shmem[iSCB_STATUS>>1], shmem[iSCB_CMD>>1]);
|
|
break;
|
|
}
|
|
}
|
|
/* Issue channel-attn -- the 82586 won't start. */
|
|
outb(0, ioaddr + I82586_ATTN);
|
|
#ifdef ETHERBOOT32
|
|
if (*(unsigned short *)mem_start != 0)
|
|
return (0);
|
|
#endif
|
|
#ifdef ETHERBOOT16
|
|
fmemcpy((char *)&i, mem_start, sizeof(unsigned short));
|
|
if (i != 0)
|
|
return (0);
|
|
#endif
|
|
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);
|
|
printf("\nEXOS205 ioaddr 0x%x, mem [0x%X-0x%X], addr ",
|
|
ioaddr, mem_start, mem_end);
|
|
/* Get station address */
|
|
for (i = 0; i < ETHER_ADDR_SIZE; ++i)
|
|
{
|
|
printf("%b", nic->node_addr[i] = inb(ioaddr+i));
|
|
if (i < ETHER_ADDR_SIZE -1)
|
|
printf(":");
|
|
}
|
|
putchar('\n');
|
|
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
|