1 /* $NetBSD: dp8390.c,v 1.6 2008/12/14 18:46:33 christos Exp $ */
4 * Polling driver for National Semiconductor DS8390/WD83C690 based
7 * Copyright (c) 1998 Matthias Drochner. All rights reserved.
9 * Copyright (c) 1994, 1995 Charles M. Hannum. All rights reserved.
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.
18 #include <sys/types.h>
19 #include <machine/pio.h>
21 #include <lib/libsa/stand.h>
24 #include <dev/ic/dp8390reg.h>
32 int dp8390_iobase
, dp8390_membase
, dp8390_memsize
;
33 #if defined(SUPPORT_WD80X3) && defined(SUPPORT_SMC_ULTRA)
36 uint8_t dp8390_cr_proto
;
37 uint8_t dp8390_dcr_reg
;
39 #define WE_IOBASE dp8390_iobase
41 static u_short rec_page_start
;
42 static u_short rec_page_stop
;
43 static u_short next_packet
;
45 extern u_char eth_myaddr
[6];
48 static void *vmembase
;
49 extern void *mapmem(int, int);
50 extern void unmapmem(void *, int);
51 extern int mapio(void);
54 bbcopy(void *src
, void *dst
, int len
)
56 char *s
= (char *)src
;
57 char *d
= (char *)dst
;
64 static void dp8390_read(int, char *, u_short
);
66 #define NIC_GET(reg) inb(WE_IOBASE + reg)
67 #define NIC_PUT(reg, val) outb(WE_IOBASE + reg, val)
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
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
);
83 if (dp8390_dcr_reg
& ED_DCR_LS
) {
84 NIC_PUT(ED_P0_DCR
, dp8390_dcr_reg
);
87 * Set FIFO threshold to 8, No auto-init Remote DMA, byte
88 * order=80x86, byte-wide DMA xfers,
90 NIC_PUT(ED_P0_DCR
, ED_DCR_FT1
| ED_DCR_LS
);
93 /* Clear remote byte count registers. */
94 NIC_PUT(ED_P0_RBCR0
, 0);
95 NIC_PUT(ED_P0_RBCR1
, 0);
97 /* Tell RCR to do nothing for now. */
98 NIC_PUT(ED_P0_RCR
, ED_RCR_MON
);
100 /* Place NIC in internal loopback mode. */
101 NIC_PUT(ED_P0_TCR
, ED_TCR_LB0
);
103 /* Set lower bits of byte addressable framing to 0. */
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
);
113 * Clear all interrupts. A '1' in each bit position clears the
114 * corresponding flag.
116 NIC_PUT(ED_P0_ISR
, 0xff);
119 * Disable all interrupts.
121 NIC_PUT(ED_P0_IMR
, 0);
123 /* Program command register for page 1. */
124 NIC_PUT(ED_P0_CR
, dp8390_cr_proto
| ED_CR_PAGE_1
| ED_CR_STP
);
126 /* Copy out our station address. */
127 for (i
= 0; i
< 6; ++i
)
128 NIC_PUT(ED_P1_PAR0
+ i
, eth_myaddr
[i
]);
131 * Set current page pointer to one page after the boundary pointer, as
132 * recommended in the National manual.
134 next_packet
= rec_page_start
+ 1;
135 NIC_PUT(ED_P1_CURR
, next_packet
);
137 /* Program command register for page 0. */
138 NIC_PUT(ED_P1_CR
, dp8390_cr_proto
| ED_CR_PAGE_0
| ED_CR_STP
);
140 /* directed and broadcast */
141 NIC_PUT(ED_P0_RCR
, ED_RCR_AB
);
143 /* Take interface out of loopback. */
144 NIC_PUT(ED_P0_TCR
, 0);
146 /* Fire up the interface. */
147 NIC_PUT(ED_P0_CR
, dp8390_cr_proto
| ED_CR_PAGE_0
| ED_CR_STA
);
155 printf("no IO access\n");
158 vmembase
= mapmem(dp8390_membase
, dp8390_memsize
);
160 printf("no memory access\n");
165 rec_page_start
= TX_PAGE_START
+ ED_TXBUF_SIZE
;
166 rec_page_stop
= TX_PAGE_START
+ (dp8390_memsize
>> ED_PAGE_SHIFT
);
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
);
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.
186 while (((NIC_GET(ED_P0_ISR
) & ED_ISR_RST
) == 0) && --n
)
190 unmapmem(vmembase
, dp8390_memsize
);
195 EtherSend(char *pkt
, int len
)
197 #ifdef SUPPORT_NE2000
198 ne2000_writemem(pkt
, dp8390_membase
, len
);
201 vpbcopy(pkt
, (void *)dp8390_membase
, len
);
203 bbcopy(pkt
, vmembase
, len
);
207 /* Set TX buffer start page. */
208 NIC_PUT(ED_P0_TPSR
, TX_PAGE_START
);
211 NIC_PUT(ED_P0_TBCR0
, len
< 60 ? 60 : len
);
212 NIC_PUT(ED_P0_TBCR1
, len
>> 8);
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
);
221 dp8390_read(int buf
, char *dest
, u_short len
)
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
;
229 /* Copy amount up to end of NIC memory. */
230 #ifdef SUPPORT_NE2000
231 ne2000_readmem(buf
, dest
, tmp_amount
);
234 pvbcopy((void *)buf
, dest
, tmp_amount
);
236 bbcopy(vmembase
+ buf
- dp8390_membase
, dest
, tmp_amount
);
241 buf
= RX_BUFBASE
+ (rec_page_start
<< ED_PAGE_SHIFT
);
244 #ifdef SUPPORT_NE2000
245 ne2000_readmem(buf
, dest
, len
);
248 pvbcopy((void *)buf
, dest
, len
);
250 bbcopy(vmembase
+ buf
- dp8390_membase
, dest
, len
);
256 EtherReceive(char *pkt
, int maxlen
)
258 struct dp8390_ring packet_hdr
;
261 u_char boundary
, current
;
262 #ifdef DP8390_OLDCHIPS
266 if (!(NIC_GET(ED_P0_RSR
) & ED_RSR_PRX
))
267 return 0; /* XXX error handling */
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
);
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).
280 current
= NIC_GET(ED_P1_CURR
);
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
);
285 if (next_packet
== current
)
288 /* Get pointer to this buffer's header structure. */
289 packet_ptr
= RX_BUFBASE
+ (next_packet
<< ED_PAGE_SHIFT
);
292 * The byte count includes a 4 byte header that was added by
295 #ifdef SUPPORT_NE2000
296 ne2000_readmem(packet_ptr
, (void *)&packet_hdr
, 4);
299 pvbcopy((void *)packet_ptr
, &packet_hdr
, 4);
301 bbcopy(vmembase
+ packet_ptr
- dp8390_membase
, &packet_hdr
, 4);
305 len
= packet_hdr
.count
;
307 #ifdef DP8390_OLDCHIPS
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
314 * NOTE: sc->next_packet is pointing at the current packet.
316 if (packet_hdr
.next_packet
>= next_packet
)
317 nlen
= (packet_hdr
.next_packet
- next_packet
);
319 nlen
= ((packet_hdr
.next_packet
- rec_page_start
) +
320 (rec_page_stop
- next_packet
));
322 if ((len
& ED_PAGE_MASK
) + sizeof(packet_hdr
) > ED_PAGE_SIZE
)
324 len
= (len
& ED_PAGE_MASK
) | (nlen
<< ED_PAGE_SHIFT
);
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
);
337 if (packet_hdr
.next_packet
< rec_page_start
||
338 packet_hdr
.next_packet
>= rec_page_stop
)
339 panic(IFNAME
": RAM corrupt");
341 len
-= sizeof(struct dp8390_ring
);
344 dp8390_read(packet_ptr
+ sizeof(struct dp8390_ring
),
349 /* Update next packet pointer. */
350 next_packet
= packet_hdr
.next_packet
;
353 * Update NIC boundary pointer - being careful to keep it one
354 * buffer behind (as recommended by NS databook).
356 boundary
= next_packet
- 1;
357 if (boundary
< rec_page_start
)
358 boundary
= rec_page_stop
- 1;
359 NIC_PUT(ED_P0_BNRY
, boundary
);