| /************************************************************************** |
| 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 |