xref: /netbsd/sys/arch/i386/stand/lib/netif/dp8390.c (revision bf9ec67e)
1 /*	$NetBSD: dp8390.c,v 1.3 2001/11/01 09:37:17 yamt Exp $	*/
2 
3 /*
4  * Polling driver for National Semiconductor DS8390/WD83C690 based
5  * ethernet adapters.
6  *
7  * Copyright (c) 1998 Matthias Drochner.  All rights reserved.
8  *
9  * Copyright (c) 1994, 1995 Charles M. Hannum.  All rights reserved.
10  *
11  * Copyright (C) 1993, David Greenman.  This software may be used, modified,
12  * copied, distributed, and sold, in both source and binary form provided that
13  * the above copyright and these terms are retained.  Under no circumstances is
14  * the author responsible for the proper functioning of this software, nor does
15  * the author assume any responsibility for damages incurred with its use.
16  */
17 
18 #include <sys/types.h>
19 #include <machine/pio.h>
20 
21 #include <lib/libsa/stand.h>
22 #include <libi386.h>
23 
24 #include <dev/ic/dp8390reg.h>
25 #include "dp8390.h"
26 #ifdef SUPPORT_NE2000
27 #include "ne.h"
28 #endif
29 
30 #include "etherdrv.h"
31 
32 int dp8390_iobase, dp8390_membase, dp8390_memsize;
33 #if defined(SUPPORT_WD80X3) && defined(SUPPORT_SMC_ULTRA)
34 int dp8390_is790;
35 #endif
36 u_int8_t dp8390_cr_proto;
37 u_int8_t dp8390_dcr_reg;
38 
39 #define WE_IOBASE dp8390_iobase
40 
41 static u_short rec_page_start;
42 static u_short rec_page_stop;
43 static u_short next_packet;
44 
45 extern u_char eth_myaddr[6];
46 
47 #ifndef _STANDALONE
48 static caddr_t vmembase;
49 extern caddr_t mapmem __P((int, int));
50 extern void unmapmem __P((caddr_t, int));
51 extern int mapio __P((void));
52 
53 static void bbcopy(src, dst, len)
54 caddr_t src, dst;
55 int len;
56 {
57 	char *s = (char *)src, *d = (char *)dst;
58 
59 	while(len--)
60 		*d++ = *s++;
61 }
62 #endif
63 
64 static void dp8390_read __P((int, char *, u_short));
65 
66 #define NIC_GET(reg) inb(WE_IOBASE + reg)
67 #define NIC_PUT(reg, val) outb(WE_IOBASE + reg, val)
68 
69 static void
70 dp8390_init()
71 {
72 	int i;
73 
74 	/*
75 	 * Initialize the NIC in the exact order outlined in the NS manual.
76 	 * This init procedure is "mandatory"...don't change what or when
77 	 * things happen.
78 	 */
79 
80 	/* Set interface for page 0, remote DMA complete, stopped. */
81 	NIC_PUT(ED_P0_CR, dp8390_cr_proto | ED_CR_PAGE_0 | ED_CR_STP);
82 
83 	if (dp8390_dcr_reg & ED_DCR_LS) {
84 		NIC_PUT(ED_P0_DCR, dp8390_dcr_reg);
85 	} else {
86 		/*
87 		 * Set FIFO threshold to 8, No auto-init Remote DMA, byte
88 		 * order=80x86, byte-wide DMA xfers,
89 		 */
90 		NIC_PUT(ED_P0_DCR, ED_DCR_FT1 | ED_DCR_LS);
91 	}
92 
93 	/* Clear remote byte count registers. */
94 	NIC_PUT(ED_P0_RBCR0, 0);
95 	NIC_PUT(ED_P0_RBCR1, 0);
96 
97 	/* Tell RCR to do nothing for now. */
98 	NIC_PUT(ED_P0_RCR, ED_RCR_MON);
99 
100 	/* Place NIC in internal loopback mode. */
101 	NIC_PUT(ED_P0_TCR, ED_TCR_LB0);
102 
103 	/* Set lower bits of byte addressable framing to 0. */
104 	if (dp8390_is790)
105 		NIC_PUT(0x09, 0);
106 
107 	/* Initialize receive buffer ring. */
108 	NIC_PUT(ED_P0_BNRY, rec_page_start);
109 	NIC_PUT(ED_P0_PSTART, rec_page_start);
110 	NIC_PUT(ED_P0_PSTOP, rec_page_stop);
111 
112 	/*
113 	 * Clear all interrupts.  A '1' in each bit position clears the
114 	 * corresponding flag.
115 	 */
116 	NIC_PUT(ED_P0_ISR, 0xff);
117 
118 	/*
119 	 * Disable all interrupts.
120 	 */
121 	NIC_PUT(ED_P0_IMR, 0);
122 
123 	/* Program command register for page 1. */
124 	NIC_PUT(ED_P0_CR, dp8390_cr_proto | ED_CR_PAGE_1 | ED_CR_STP);
125 
126 	/* Copy out our station address. */
127 	for (i = 0; i < 6; ++i)
128 		NIC_PUT(ED_P1_PAR0 + i, eth_myaddr[i]);
129 
130 	/*
131 	 * Set current page pointer to one page after the boundary pointer, as
132 	 * recommended in the National manual.
133 	 */
134 	next_packet = rec_page_start + 1;
135 	NIC_PUT(ED_P1_CURR, next_packet);
136 
137 	/* Program command register for page 0. */
138 	NIC_PUT(ED_P1_CR, dp8390_cr_proto | ED_CR_PAGE_0 | ED_CR_STP);
139 
140 	/* directed and broadcast */
141 	NIC_PUT(ED_P0_RCR, ED_RCR_AB);
142 
143 	/* Take interface out of loopback. */
144 	NIC_PUT(ED_P0_TCR, 0);
145 
146 	/* Fire up the interface. */
147 	NIC_PUT(ED_P0_CR, dp8390_cr_proto | ED_CR_PAGE_0 | ED_CR_STA);
148 }
149 
150 int
151 dp8390_config()
152 {
153 #ifndef _STANDALONE
154 	if (mapio()) {
155 		printf("no IO access\n");
156 		return(-1);
157 	}
158 	vmembase = mapmem(dp8390_membase, dp8390_memsize);
159 	if (!vmembase) {
160 		printf("no memory access\n");
161 		return(-1);
162 	}
163 #endif
164 
165 	rec_page_start = TX_PAGE_START + ED_TXBUF_SIZE;
166 	rec_page_stop = TX_PAGE_START + (dp8390_memsize >> ED_PAGE_SHIFT);
167 
168 	dp8390_init();
169 
170 	return(0);
171 }
172 
173 void
174 dp8390_stop()
175 {
176 	int n = 5000;
177 
178 	/* Stop everything on the interface, and select page 0 registers. */
179 	NIC_PUT(ED_P0_CR, dp8390_cr_proto | ED_CR_PAGE_0 | ED_CR_STP);
180 
181 	/*
182 	 * Wait for interface to enter stopped state, but limit # of checks to
183 	 * 'n' (about 5ms).  It shouldn't even take 5us on modern DS8390's, but
184 	 * just in case it's an old one.
185 	 */
186 	while (((NIC_GET(ED_P0_ISR) & ED_ISR_RST) == 0) && --n);
187 
188 #ifndef _STANDALONE
189 	unmapmem(vmembase, dp8390_memsize);
190 #endif
191 }
192 
193 int
194 EtherSend(pkt, len)
195 char *pkt;
196 int len;
197 {
198 #ifdef SUPPORT_NE2000
199 	ne2000_writemem(pkt, dp8390_membase, len);
200 #else
201 #ifdef _STANDALONE
202 	vpbcopy(pkt, (void *)dp8390_membase, len);
203 #else
204 	bbcopy(pkt, vmembase, len);
205 #endif
206 #endif
207 
208 	/* Set TX buffer start page. */
209 	NIC_PUT(ED_P0_TPSR, TX_PAGE_START);
210 
211 	/* Set TX length. */
212 	NIC_PUT(ED_P0_TBCR0, len < 60 ? 60 : len);
213 	NIC_PUT(ED_P0_TBCR1, len >> 8);
214 
215 	/* Set page 0, remote DMA complete, transmit packet, and *start*. */
216 	NIC_PUT(ED_P0_CR, dp8390_cr_proto | ED_CR_PAGE_0 | ED_CR_TXP | ED_CR_STA);
217 
218 	return(len);
219 }
220 
221 static void
222 dp8390_read(buf, dest, len)
223 	int buf;
224 	char *dest;
225 	u_short len;
226 {
227 	u_short tmp_amount;
228 
229 	/* Does copy wrap to lower addr in ring buffer? */
230 	if (buf + len > dp8390_membase + dp8390_memsize) {
231 		tmp_amount = dp8390_membase + dp8390_memsize - buf;
232 
233 		/* Copy amount up to end of NIC memory. */
234 #ifdef SUPPORT_NE2000
235 		ne2000_readmem(buf, dest, tmp_amount);
236 #else
237 #ifdef _STANDALONE
238 		pvbcopy((void *)buf, dest, tmp_amount);
239 #else
240 		bbcopy(vmembase + buf - dp8390_membase, dest, tmp_amount);
241 #endif
242 #endif
243 
244 		len -= tmp_amount;
245 		buf = RX_BUFBASE + (rec_page_start << ED_PAGE_SHIFT);
246 		dest += tmp_amount;
247 	}
248 #ifdef SUPPORT_NE2000
249 	ne2000_readmem(buf, dest, len);
250 #else
251 #ifdef _STANDALONE
252 	pvbcopy((void *)buf, dest, len);
253 #else
254 	bbcopy(vmembase + buf - dp8390_membase, dest, len);
255 #endif
256 #endif
257 }
258 
259 int
260 EtherReceive(pkt, maxlen)
261 char *pkt;
262 int maxlen;
263 {
264 	struct dp8390_ring packet_hdr;
265 	int packet_ptr;
266 	u_short len;
267 	u_char boundary, current;
268 #ifdef DP8390_OLDCHIPS
269 	u_char nlen;
270 #endif
271 
272 	if (!(NIC_GET(ED_P0_RSR) & ED_RSR_PRX))
273 		return(0); /* XXX error handling */
274 
275 	/* Set NIC to page 1 registers to get 'current' pointer. */
276 	NIC_PUT(ED_P0_CR, dp8390_cr_proto | ED_CR_PAGE_1 | ED_CR_STA);
277 
278 	/*
279 	 * 'sc->next_packet' is the logical beginning of the ring-buffer - i.e.
280 	 * it points to where new data has been buffered.  The 'CURR' (current)
281 	 * register points to the logical end of the ring-buffer - i.e. it
282 	 * points to where additional new data will be added.  We loop here
283 	 * until the logical beginning equals the logical end (or in other
284 	 * words, until the ring-buffer is empty).
285 	 */
286 	current = NIC_GET(ED_P1_CURR);
287 
288 	/* Set NIC to page 0 registers to update boundary register. */
289 	NIC_PUT(ED_P1_CR, dp8390_cr_proto | ED_CR_PAGE_0 | ED_CR_STA);
290 
291 	if (next_packet == current)
292 		return(0);
293 
294 	/* Get pointer to this buffer's header structure. */
295 	packet_ptr = RX_BUFBASE + (next_packet << ED_PAGE_SHIFT);
296 
297 	/*
298 	 * The byte count includes a 4 byte header that was added by
299 	 * the NIC.
300 	 */
301 #ifdef SUPPORT_NE2000
302 	ne2000_readmem(packet_ptr, (void *)&packet_hdr, 4);
303 #else
304 #ifdef _STANDALONE
305 	pvbcopy((void *)packet_ptr, &packet_hdr, 4);
306 #else
307 	bbcopy(vmembase + packet_ptr - dp8390_membase, &packet_hdr, 4);
308 #endif
309 #endif
310 
311 	len = packet_hdr.count;
312 
313 #ifdef DP8390_OLDCHIPS
314 	/*
315 	 * Try do deal with old, buggy chips that sometimes duplicate
316 	 * the low byte of the length into the high byte.  We do this
317 	 * by simply ignoring the high byte of the length and always
318 	 * recalculating it.
319 	 *
320 	 * NOTE: sc->next_packet is pointing at the current packet.
321 	 */
322 	if (packet_hdr.next_packet >= next_packet)
323 		nlen = (packet_hdr.next_packet - next_packet);
324 	else
325 		nlen = ((packet_hdr.next_packet - rec_page_start) +
326 			(rec_page_stop - next_packet));
327 	--nlen;
328 	if ((len & ED_PAGE_MASK) + sizeof(packet_hdr) > ED_PAGE_SIZE)
329 		--nlen;
330 	len = (len & ED_PAGE_MASK) | (nlen << ED_PAGE_SHIFT);
331 #ifdef DIAGNOSTIC
332 	if (len != packet_hdr.count) {
333 		printf(IFNAME ": length does not match next packet pointer\n");
334 		printf(IFNAME ": len %04x nlen %04x start %02x "
335 		       "first %02x curr %02x next %02x stop %02x\n",
336 		       packet_hdr.count, len,
337 		       rec_page_start, next_packet, current,
338 		       packet_hdr.next_packet, rec_page_stop);
339 	}
340 #endif
341 #endif
342 
343 	if (packet_hdr.next_packet < rec_page_start ||
344 	    packet_hdr.next_packet >= rec_page_stop)
345 		panic(IFNAME ": RAM corrupt");
346 
347 	len -= sizeof(struct dp8390_ring);
348 	if (len < maxlen) {
349 		/* Go get packet. */
350 		dp8390_read(packet_ptr + sizeof(struct dp8390_ring),
351 			    pkt, len);
352 	} else
353 		len = 0;
354 
355 	/* Update next packet pointer. */
356 	next_packet = packet_hdr.next_packet;
357 
358 	/*
359 	 * Update NIC boundary pointer - being careful to keep it one
360 	 * buffer behind (as recommended by NS databook).
361 	 */
362 	boundary = next_packet - 1;
363 	if (boundary < rec_page_start)
364 		boundary = rec_page_stop - 1;
365 	NIC_PUT(ED_P0_BNRY, boundary);
366 
367 	return(len);
368 }
369