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