xref: /netbsd/sys/arch/i386/stand/lib/netif/dp8390.c (revision 6550d01e)
1 /*	$NetBSD: dp8390.c,v 1.6 2008/12/14 18:46:33 christos 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 uint8_t dp8390_cr_proto;
37 uint8_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 void *vmembase;
49 extern void *mapmem(int, int);
50 extern void unmapmem(void *, int);
51 extern int mapio(void);
52 
53 static void
54 bbcopy(void *src, void *dst, int len)
55 {
56 	char *s = (char *)src;
57 	char *d = (char *)dst;
58 
59 	while (len--)
60 		*d++ = *s++;
61 }
62 #endif
63 
64 static void dp8390_read(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(void)
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(void)
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(void)
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 		continue;
188 
189 #ifndef _STANDALONE
190 	unmapmem(vmembase, dp8390_memsize);
191 #endif
192 }
193 
194 int
195 EtherSend(char *pkt, int len)
196 {
197 #ifdef SUPPORT_NE2000
198 	ne2000_writemem(pkt, dp8390_membase, len);
199 #else
200 #ifdef _STANDALONE
201 	vpbcopy(pkt, (void *)dp8390_membase, len);
202 #else
203 	bbcopy(pkt, vmembase, len);
204 #endif
205 #endif
206 
207 	/* Set TX buffer start page. */
208 	NIC_PUT(ED_P0_TPSR, TX_PAGE_START);
209 
210 	/* Set TX length. */
211 	NIC_PUT(ED_P0_TBCR0, len < 60 ? 60 : len);
212 	NIC_PUT(ED_P0_TBCR1, len >> 8);
213 
214 	/* Set page 0, remote DMA complete, transmit packet, and *start*. */
215 	NIC_PUT(ED_P0_CR, dp8390_cr_proto | ED_CR_PAGE_0 | ED_CR_TXP | ED_CR_STA);
216 
217 	return len;
218 }
219 
220 static void
221 dp8390_read(int buf, char *dest, u_short len)
222 {
223 	u_short tmp_amount;
224 
225 	/* Does copy wrap to lower addr in ring buffer? */
226 	if (buf + len > dp8390_membase + dp8390_memsize) {
227 		tmp_amount = dp8390_membase + dp8390_memsize - buf;
228 
229 		/* Copy amount up to end of NIC memory. */
230 #ifdef SUPPORT_NE2000
231 		ne2000_readmem(buf, dest, tmp_amount);
232 #else
233 #ifdef _STANDALONE
234 		pvbcopy((void *)buf, dest, tmp_amount);
235 #else
236 		bbcopy(vmembase + buf - dp8390_membase, dest, tmp_amount);
237 #endif
238 #endif
239 
240 		len -= tmp_amount;
241 		buf = RX_BUFBASE + (rec_page_start << ED_PAGE_SHIFT);
242 		dest += tmp_amount;
243 	}
244 #ifdef SUPPORT_NE2000
245 	ne2000_readmem(buf, dest, len);
246 #else
247 #ifdef _STANDALONE
248 	pvbcopy((void *)buf, dest, len);
249 #else
250 	bbcopy(vmembase + buf - dp8390_membase, dest, len);
251 #endif
252 #endif
253 }
254 
255 int
256 EtherReceive(char *pkt, int maxlen)
257 {
258 	struct dp8390_ring packet_hdr;
259 	int packet_ptr;
260 	u_short len;
261 	u_char boundary, current;
262 #ifdef DP8390_OLDCHIPS
263 	u_char nlen;
264 #endif
265 
266 	if (!(NIC_GET(ED_P0_RSR) & ED_RSR_PRX))
267 		return 0; /* XXX error handling */
268 
269 	/* Set NIC to page 1 registers to get 'current' pointer. */
270 	NIC_PUT(ED_P0_CR, dp8390_cr_proto | ED_CR_PAGE_1 | ED_CR_STA);
271 
272 	/*
273 	 * 'sc->next_packet' is the logical beginning of the ring-buffer - i.e.
274 	 * it points to where new data has been buffered.  The 'CURR' (current)
275 	 * register points to the logical end of the ring-buffer - i.e. it
276 	 * points to where additional new data will be added.  We loop here
277 	 * until the logical beginning equals the logical end (or in other
278 	 * words, until the ring-buffer is empty).
279 	 */
280 	current = NIC_GET(ED_P1_CURR);
281 
282 	/* Set NIC to page 0 registers to update boundary register. */
283 	NIC_PUT(ED_P1_CR, dp8390_cr_proto | ED_CR_PAGE_0 | ED_CR_STA);
284 
285 	if (next_packet == current)
286 		return 0;
287 
288 	/* Get pointer to this buffer's header structure. */
289 	packet_ptr = RX_BUFBASE + (next_packet << ED_PAGE_SHIFT);
290 
291 	/*
292 	 * The byte count includes a 4 byte header that was added by
293 	 * the NIC.
294 	 */
295 #ifdef SUPPORT_NE2000
296 	ne2000_readmem(packet_ptr, (void *)&packet_hdr, 4);
297 #else
298 #ifdef _STANDALONE
299 	pvbcopy((void *)packet_ptr, &packet_hdr, 4);
300 #else
301 	bbcopy(vmembase + packet_ptr - dp8390_membase, &packet_hdr, 4);
302 #endif
303 #endif
304 
305 	len = packet_hdr.count;
306 
307 #ifdef DP8390_OLDCHIPS
308 	/*
309 	 * Try do deal with old, buggy chips that sometimes duplicate
310 	 * the low byte of the length into the high byte.  We do this
311 	 * by simply ignoring the high byte of the length and always
312 	 * recalculating it.
313 	 *
314 	 * NOTE: sc->next_packet is pointing at the current packet.
315 	 */
316 	if (packet_hdr.next_packet >= next_packet)
317 		nlen = (packet_hdr.next_packet - next_packet);
318 	else
319 		nlen = ((packet_hdr.next_packet - rec_page_start) +
320 			(rec_page_stop - next_packet));
321 	--nlen;
322 	if ((len & ED_PAGE_MASK) + sizeof(packet_hdr) > ED_PAGE_SIZE)
323 		--nlen;
324 	len = (len & ED_PAGE_MASK) | (nlen << ED_PAGE_SHIFT);
325 #ifdef DIAGNOSTIC
326 	if (len != packet_hdr.count) {
327 		printf(IFNAME ": length does not match next packet pointer\n");
328 		printf(IFNAME ": len %04x nlen %04x start %02x "
329 		       "first %02x curr %02x next %02x stop %02x\n",
330 		       packet_hdr.count, len,
331 		       rec_page_start, next_packet, current,
332 		       packet_hdr.next_packet, rec_page_stop);
333 	}
334 #endif
335 #endif
336 
337 	if (packet_hdr.next_packet < rec_page_start ||
338 	    packet_hdr.next_packet >= rec_page_stop)
339 		panic(IFNAME ": RAM corrupt");
340 
341 	len -= sizeof(struct dp8390_ring);
342 	if (len < maxlen) {
343 		/* Go get packet. */
344 		dp8390_read(packet_ptr + sizeof(struct dp8390_ring),
345 			    pkt, len);
346 	} else
347 		len = 0;
348 
349 	/* Update next packet pointer. */
350 	next_packet = packet_hdr.next_packet;
351 
352 	/*
353 	 * Update NIC boundary pointer - being careful to keep it one
354 	 * buffer behind (as recommended by NS databook).
355 	 */
356 	boundary = next_packet - 1;
357 	if (boundary < rec_page_start)
358 		boundary = rec_page_stop - 1;
359 	NIC_PUT(ED_P0_BNRY, boundary);
360 
361 	return len;
362 }
363