1/**************************************************************************
2 ETHERBOOT -  BOOTP/TFTP Bootstrap Program
3
4 Author: Martin Renters
5 Date: May/94
6
7 This code is based heavily on David Greenman's if_ed.c driver
8
9 Copyright (C) 1993-1994, David Greenman, Martin Renters.
10 This software may be used, modified, copied, distributed, and sold, in
11 both source and binary form provided that the above copyright and these
12 terms are retained. Under no circumstances are the authors responsible for
13 the proper functioning of this software, nor do the authors assume any
14 responsibility for damages incurred with its use.
15
16 Multicast support added by Timothy Legge (timlegge@users.sourceforge.net) 09/28/2003
17 Relocation support added by Ken Yap (ken_yap@users.sourceforge.net) 28/12/02
18 Card Detect support adapted from the eCos driver (Christian Plessl <cplessl@ee.ethz.ch>)
19 Extracted from ns8390.c and adapted by Pantelis Koukousoulas <pktoss@gmail.com>
20 **************************************************************************/
21
22FILE_LICENCE ( BSD2 );
23
24#include "ns8390.h"
25#include "etherboot.h"
26#include "nic.h"
27#include <gpxe/ethernet.h>
28#include <gpxe/isa.h>
29#include <errno.h>
30
31#define ASIC_PIO NE_DATA
32
33static unsigned char eth_vendor, eth_flags;
34static unsigned short eth_nic_base, eth_asic_base;
35static unsigned char eth_memsize, eth_rx_start, eth_tx_start;
36static Address eth_bmem, eth_rmem;
37static unsigned char eth_drain_receiver;
38
39static struct nic_operations ne_operations;
40static void ne_reset(struct nic *nic, struct isa_device *isa);
41
42static isa_probe_addr_t ne_probe_addrs[] = { 0x300, 0x280, 0x320, 0x340, 0x380, 0x220, };
43
44/**************************************************************************
45 ETH_PIO_READ - Read a frame via Programmed I/O
46 **************************************************************************/
47static void eth_pio_read(unsigned int src, unsigned char *dst, unsigned int cnt) {
48	outb(D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
49	outb(cnt, eth_nic_base + D8390_P0_RBCR0);
50	outb(cnt >> 8, eth_nic_base + D8390_P0_RBCR1);
51	outb(src, eth_nic_base + D8390_P0_RSAR0);
52	outb(src >> 8, eth_nic_base + D8390_P0_RSAR1);
53	outb(D8390_COMMAND_RD0 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
54	if (eth_flags & FLAG_16BIT)
55		cnt = (cnt + 1) >> 1;
56
57	while (cnt--) {
58		if (eth_flags & FLAG_16BIT) {
59			*((unsigned short *) dst) = inw(eth_asic_base + ASIC_PIO);
60			dst += 2;
61		} else
62			*(dst++) = inb(eth_asic_base + ASIC_PIO);
63	}
64}
65
66/**************************************************************************
67 ETH_PIO_WRITE - Write a frame via Programmed I/O
68 **************************************************************************/
69static void eth_pio_write(const unsigned char *src, unsigned int dst,
70		unsigned int cnt) {
71	outb(D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
72	outb(D8390_ISR_RDC, eth_nic_base + D8390_P0_ISR);
73	outb(cnt, eth_nic_base + D8390_P0_RBCR0);
74	outb(cnt >> 8, eth_nic_base + D8390_P0_RBCR1);
75	outb(dst, eth_nic_base + D8390_P0_RSAR0);
76	outb(dst >> 8, eth_nic_base + D8390_P0_RSAR1);
77	outb(D8390_COMMAND_RD1 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
78	if (eth_flags & FLAG_16BIT)
79		cnt = (cnt + 1) >> 1;
80
81	while (cnt--) {
82
83		if (eth_flags & FLAG_16BIT) {
84			outw(*((unsigned short *) src), eth_asic_base + ASIC_PIO);
85			src += 2;
86		} else
87			outb(*(src++), eth_asic_base + ASIC_PIO);
88	}
89}
90
91/**************************************************************************
92 enable_multicast - Enable Multicast
93 **************************************************************************/
94static void enable_multicast(unsigned short eth_nic_base) {
95	unsigned char mcfilter[8];
96	int i;
97
98	memset(mcfilter, 0xFF, 8);
99	outb(4, eth_nic_base + D8390_P0_RCR);
100	outb(D8390_COMMAND_RD2 + D8390_COMMAND_PS1, eth_nic_base + D8390_P0_COMMAND);
101	for (i = 0; i < 8; i++) {
102		outb(mcfilter[i], eth_nic_base + 8 + i);
103		if (inb(eth_nic_base + 8 + i) != mcfilter[i])
104			DBG("Error SMC 83C690 Multicast filter read/write mishap %d\n",
105					i);
106	}
107	outb(D8390_COMMAND_RD2 + D8390_COMMAND_PS0, eth_nic_base + D8390_P0_COMMAND);
108	outb(4 | 0x08, eth_nic_base + D8390_P0_RCR);
109}
110
111/**************************************************************************
112 NE_PROBE1 - Look for an adapter on the ISA bus
113 **************************************************************************/
114static int ne_probe1(isa_probe_addr_t ioaddr) {
115	//From the eCos driver
116	unsigned int regd;
117	unsigned int state;
118
119	state = inb(ioaddr);
120	outb(ioaddr, D8390_COMMAND_RD2 | D8390_COMMAND_PS1 | D8390_COMMAND_STP);
121	regd = inb(ioaddr + D8390_P0_TCR);
122
123	if (inb(ioaddr + D8390_P0_TCR)) {
124		outb(ioaddr, state);
125		outb(ioaddr + 0x0d, regd);
126		return 0;
127	}
128
129	return 1;
130}
131
132/**************************************************************************
133 NE_PROBE - Initialize an adapter ???
134 **************************************************************************/
135static int ne_probe(struct nic *nic, struct isa_device *isa) {
136	int i;
137	unsigned char c;
138	unsigned char romdata[16];
139	unsigned char testbuf[32];
140
141	eth_vendor = VENDOR_NONE;
142	eth_drain_receiver = 0;
143
144	nic->irqno = 0;
145	nic->ioaddr = isa->ioaddr;
146	eth_nic_base = isa->ioaddr;
147
148	/******************************************************************
149	 Search for NE1000/2000 if no WD/SMC or 3com cards
150	 ******************************************************************/
151	if (eth_vendor == VENDOR_NONE) {
152
153		static unsigned char test[] = "NE*000 memory";
154
155		eth_bmem = 0; /* No shared memory */
156
157		eth_flags = FLAG_PIO;
158		eth_asic_base = eth_nic_base + NE_ASIC_OFFSET;
159		eth_memsize = MEM_16384;
160		eth_tx_start = 32;
161		eth_rx_start = 32 + D8390_TXBUF_SIZE;
162		c = inb(eth_asic_base + NE_RESET);
163		outb(c, eth_asic_base + NE_RESET);
164		(void) inb(0x84);
165		outb(D8390_COMMAND_STP | D8390_COMMAND_RD2, eth_nic_base
166				+ D8390_P0_COMMAND);
167		outb(D8390_RCR_MON, eth_nic_base + D8390_P0_RCR);
168		outb(D8390_DCR_FT1 | D8390_DCR_LS, eth_nic_base + D8390_P0_DCR);
169		outb(MEM_8192, eth_nic_base + D8390_P0_PSTART);
170		outb(MEM_16384, eth_nic_base + D8390_P0_PSTOP);
171		eth_pio_write((unsigned char *) test, 8192, sizeof(test));
172		eth_pio_read(8192, testbuf, sizeof(test));
173		if (!memcmp(test, testbuf, sizeof(test)))
174			goto out;
175		eth_flags |= FLAG_16BIT;
176		eth_memsize = MEM_32768;
177		eth_tx_start = 64;
178		eth_rx_start = 64 + D8390_TXBUF_SIZE;
179		outb(D8390_DCR_WTS | D8390_DCR_FT1 | D8390_DCR_LS, eth_nic_base
180				+ D8390_P0_DCR);
181		outb(MEM_16384, eth_nic_base + D8390_P0_PSTART);
182		outb(MEM_32768, eth_nic_base + D8390_P0_PSTOP);
183		eth_pio_write((unsigned char *) test, 16384, sizeof(test));
184		eth_pio_read(16384, testbuf, sizeof(test));
185		if (!memcmp(testbuf, test, sizeof(test)))
186			goto out;
187
188
189out:
190		if (eth_nic_base == 0)
191			return (0);
192		if (eth_nic_base > ISA_MAX_ADDR) /* PCI probably */
193			eth_flags |= FLAG_16BIT;
194		eth_vendor = VENDOR_NOVELL;
195		eth_pio_read(0, romdata, sizeof(romdata));
196		for (i = 0; i < ETH_ALEN; i++) {
197			nic->node_addr[i] = romdata[i + ((eth_flags & FLAG_16BIT) ? i : 0)];
198		}
199		nic->ioaddr = eth_nic_base;
200		DBG("\nNE%c000 base %4.4x, MAC Addr %s\n",
201				(eth_flags & FLAG_16BIT) ? '2' : '1', eth_nic_base, eth_ntoa(
202						nic->node_addr));
203	}
204
205	if (eth_vendor == VENDOR_NONE)
206		return (0);
207
208	if (eth_vendor != VENDOR_3COM)
209		eth_rmem = eth_bmem;
210
211	ne_reset(nic, isa);
212	nic->nic_op = &ne_operations;
213	return 1;
214}
215
216
217/**************************************************************************
218 NE_DISABLE - Turn off adapter
219 **************************************************************************/
220static void ne_disable(struct nic *nic, struct isa_device *isa) {
221	ne_reset(nic, isa);
222}
223
224
225/**************************************************************************
226 NE_RESET - Reset adapter
227 **************************************************************************/
228static void ne_reset(struct nic *nic, struct isa_device *isa __unused)
229{
230	int i;
231
232	eth_drain_receiver = 0;
233	outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 |
234			D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
235	if (eth_flags & FLAG_16BIT)
236	outb(0x49, eth_nic_base+D8390_P0_DCR);
237	else
238	outb(0x48, eth_nic_base+D8390_P0_DCR);
239	outb(0, eth_nic_base+D8390_P0_RBCR0);
240	outb(0, eth_nic_base+D8390_P0_RBCR1);
241	outb(0x20, eth_nic_base+D8390_P0_RCR); /* monitor mode */
242	outb(2, eth_nic_base+D8390_P0_TCR);
243	outb(eth_tx_start, eth_nic_base+D8390_P0_TPSR);
244	outb(eth_rx_start, eth_nic_base+D8390_P0_PSTART);
245
246	outb(eth_memsize, eth_nic_base+D8390_P0_PSTOP);
247	outb(eth_memsize - 1, eth_nic_base+D8390_P0_BOUND);
248	outb(0xFF, eth_nic_base+D8390_P0_ISR);
249	outb(0, eth_nic_base+D8390_P0_IMR);
250	outb(D8390_COMMAND_PS1 |
251			D8390_COMMAND_RD2 | D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
252
253	for (i=0; i<ETH_ALEN; i++)
254	outb(nic->node_addr[i], eth_nic_base+D8390_P1_PAR0+i);
255	for (i=0; i<ETH_ALEN; i++)
256	outb(0xFF, eth_nic_base+D8390_P1_MAR0+i);
257	outb(eth_rx_start, eth_nic_base+D8390_P1_CURR);
258	outb(D8390_COMMAND_PS0 |
259			D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
260	outb(0xFF, eth_nic_base+D8390_P0_ISR);
261	outb(0, eth_nic_base+D8390_P0_TCR); /* transmitter on */
262	outb(4, eth_nic_base+D8390_P0_RCR); /* allow rx broadcast frames */
263
264	enable_multicast(eth_nic_base);
265}
266
267
268/**************************************************************************
269 NE_POLL - Wait for a frame
270 **************************************************************************/
271static int ne_poll(struct nic *nic __unused, int retrieve __unused)
272{
273	int ret = 0;
274	unsigned char rstat, curr, next;
275	unsigned short len, frag;
276	unsigned short pktoff;
277	unsigned char *p;
278	struct ringbuffer pkthdr;
279
280	rstat = inb(eth_nic_base+D8390_P0_RSR);
281	if (!(rstat & D8390_RSTAT_PRX)) return(0);
282	next = inb(eth_nic_base+D8390_P0_BOUND)+1;
283	if (next >= eth_memsize) next = eth_rx_start;
284	outb(D8390_COMMAND_PS1, eth_nic_base+D8390_P0_COMMAND);
285	curr = inb(eth_nic_base+D8390_P1_CURR);
286	outb(D8390_COMMAND_PS0, eth_nic_base+D8390_P0_COMMAND);
287	if (curr >= eth_memsize) curr=eth_rx_start;
288	if (curr == next) return(0);
289
290	if ( ! retrieve ) return 1;
291
292	pktoff = next << 8;
293	if (eth_flags & FLAG_PIO)
294	eth_pio_read(pktoff, (unsigned char *)&pkthdr, 4);
295	else
296	memcpy(&pkthdr, bus_to_virt(eth_rmem + pktoff), 4);
297	pktoff += sizeof(pkthdr);
298	/* incoming length includes FCS so must sub 4 */
299	len = pkthdr.len - 4;
300	if ((pkthdr.status & D8390_RSTAT_PRX) == 0 || len < ETH_ZLEN
301			|| len> ETH_FRAME_LEN) {
302		DBG("Bogus packet, ignoring\n");
303		return (0);
304	}
305	else {
306		p = nic->packet;
307		nic->packetlen = len; /* available to caller */
308		frag = (eth_memsize << 8) - pktoff;
309		if (len> frag) { /* We have a wrap-around */
310			/* read first part */
311			if (eth_flags & FLAG_PIO)
312			eth_pio_read(pktoff, p, frag);
313			else
314			memcpy(p, bus_to_virt(eth_rmem + pktoff), frag);
315			pktoff = eth_rx_start << 8;
316			p += frag;
317			len -= frag;
318		}
319		/* read second part */
320		if (eth_flags & FLAG_PIO)
321		eth_pio_read(pktoff, p, len);
322		else
323		memcpy(p, bus_to_virt(eth_rmem + pktoff), len);
324		ret = 1;
325	}
326	next = pkthdr.next; /* frame number of next packet */
327	if (next == eth_rx_start)
328	next = eth_memsize;
329	outb(next-1, eth_nic_base+D8390_P0_BOUND);
330	return(ret);
331}
332
333
334/**************************************************************************
335 NE_TRANSMIT - Transmit a frame
336 **************************************************************************/
337static void ne_transmit(struct nic *nic, const char *d, /* Destination */
338unsigned int t, /* Type */
339unsigned int s, /* size */
340const char *p) { /* Packet */
341
342	/* Programmed I/O */
343	unsigned short type;
344	type = (t >> 8) | (t << 8);
345	eth_pio_write((unsigned char *) d, eth_tx_start << 8, ETH_ALEN);
346	eth_pio_write(nic->node_addr, (eth_tx_start << 8) + ETH_ALEN, ETH_ALEN);
347	/* bcc generates worse code without (const+const) below */
348	eth_pio_write((unsigned char *) &type, (eth_tx_start << 8) + (ETH_ALEN
349			+ ETH_ALEN), 2);
350	eth_pio_write((unsigned char *) p, (eth_tx_start << 8) + ETH_HLEN, s);
351	s += ETH_HLEN;
352	if (s < ETH_ZLEN)
353		s = ETH_ZLEN;
354
355	outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 | D8390_COMMAND_STA,
356			eth_nic_base + D8390_P0_COMMAND);
357	outb(eth_tx_start, eth_nic_base + D8390_P0_TPSR);
358	outb(s, eth_nic_base + D8390_P0_TBCR0);
359	outb(s >> 8, eth_nic_base + D8390_P0_TBCR1);
360
361	outb(D8390_COMMAND_PS0 | D8390_COMMAND_TXP | D8390_COMMAND_RD2
362			| D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
363}
364
365static struct nic_operations ne_operations = { .connect = dummy_connect,
366		.poll = ne_poll, .transmit = ne_transmit, .irq = dummy_irq,
367};
368
369ISA_DRIVER ( ne_driver, ne_probe_addrs, ne_probe1,
370		GENERIC_ISAPNP_VENDOR, 0x0600 );
371
372DRIVER ( "ne", nic_driver, isapnp_driver, ne_driver,
373		ne_probe, ne_disable );
374
375ISA_ROM("ne","NE1000/2000 and clones");
376