1/*	$NetBSD: dp8390.c,v 1.5 2007/03/04 06:00:02 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
32int dp8390_iobase, dp8390_membase, dp8390_memsize;
33#if defined(SUPPORT_WD80X3) && defined(SUPPORT_SMC_ULTRA)
34int dp8390_is790;
35#endif
36uint8_t dp8390_cr_proto;
37uint8_t dp8390_dcr_reg;
38
39#define WE_IOBASE dp8390_iobase
40
41static u_short rec_page_start;
42static u_short rec_page_stop;
43static u_short next_packet;
44
45extern u_char eth_myaddr[6];
46
47#ifndef _STANDALONE
48static void *vmembase;
49extern void *mapmem(int, int);
50extern void unmapmem(void *, int);
51extern int mapio(void);
52
53static void
54bbcopy(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
64static 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
69static void
70dp8390_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
150int
151dp8390_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
173void
174dp8390_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
194int
195EtherSend(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
220static void
221dp8390_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
255int
256EtherReceive(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