• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
22 FILE_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 
33 static unsigned char eth_vendor, eth_flags;
34 static unsigned short eth_nic_base, eth_asic_base;
35 static unsigned char eth_memsize, eth_rx_start, eth_tx_start;
36 static Address eth_bmem, eth_rmem;
37 static unsigned char eth_drain_receiver;
38 
39 static struct nic_operations ne_operations;
40 static void ne_reset(struct nic *nic, struct isa_device *isa);
41 
42 static 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  **************************************************************************/
eth_pio_read(unsigned int src,unsigned char * dst,unsigned int cnt)47 static 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  **************************************************************************/
eth_pio_write(const unsigned char * src,unsigned int dst,unsigned int cnt)69 static 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  **************************************************************************/
enable_multicast(unsigned short eth_nic_base)94 static 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  **************************************************************************/
ne_probe1(isa_probe_addr_t ioaddr)114 static 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  **************************************************************************/
ne_probe(struct nic * nic,struct isa_device * isa)135 static 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 
189 out:
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  **************************************************************************/
ne_disable(struct nic * nic,struct isa_device * isa)220 static 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  **************************************************************************/
ne_reset(struct nic * nic,struct isa_device * isa __unused)228 static 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  **************************************************************************/
ne_poll(struct nic * nic __unused,int retrieve __unused)271 static 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  **************************************************************************/
ne_transmit(struct nic * nic,const char * d,unsigned int t,unsigned int s,const char * p)337 static void ne_transmit(struct nic *nic, const char *d, /* Destination */
338 unsigned int t, /* Type */
339 unsigned int s, /* size */
340 const 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 
365 static struct nic_operations ne_operations = { .connect = dummy_connect,
366 		.poll = ne_poll, .transmit = ne_transmit, .irq = dummy_irq,
367 };
368 
369 ISA_DRIVER ( ne_driver, ne_probe_addrs, ne_probe1,
370 		GENERIC_ISAPNP_VENDOR, 0x0600 );
371 
372 DRIVER ( "ne", nic_driver, isapnp_driver, ne_driver,
373 		ne_probe, ne_disable );
374 
375 ISA_ROM("ne","NE1000/2000 and clones");
376