]>
Commit | Line | Data |
---|---|---|
8ed96046 WD |
1 | /* |
2 | Ported to U-Boot by Christian Pellegrin <chri@ascensit.com> | |
3 | ||
4 | Based on sources from the Linux kernel (pcnet_cs.c, 8390.h) and | |
5 | eCOS(if_dp83902a.c, if_dp83902a.h). Both of these 2 wonderful world | |
6 | are GPL, so this is, of course, GPL. | |
7 | ||
8 | ||
9 | ========================================================================== | |
10 | ||
11 | dev/if_dp83902a.c | |
12 | ||
13 | Ethernet device driver for NS DP83902a ethernet controller | |
14 | ||
15 | ========================================================================== | |
16 | ####ECOSGPLCOPYRIGHTBEGIN#### | |
17 | ------------------------------------------- | |
18 | This file is part of eCos, the Embedded Configurable Operating System. | |
19 | Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. | |
20 | ||
21 | eCos is free software; you can redistribute it and/or modify it under | |
22 | the terms of the GNU General Public License as published by the Free | |
23 | Software Foundation; either version 2 or (at your option) any later version. | |
24 | ||
25 | eCos is distributed in the hope that it will be useful, but WITHOUT ANY | |
26 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
27 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
28 | for more details. | |
29 | ||
30 | You should have received a copy of the GNU General Public License along | |
31 | with eCos; if not, write to the Free Software Foundation, Inc., | |
32 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | |
33 | ||
34 | As a special exception, if other files instantiate templates or use macros | |
35 | or inline functions from this file, or you compile this file and link it | |
36 | with other works to produce a work based on this file, this file does not | |
37 | by itself cause the resulting work to be covered by the GNU General Public | |
38 | License. However the source code for this file must still be made available | |
39 | in accordance with section (3) of the GNU General Public License. | |
40 | ||
41 | This exception does not invalidate any other reasons why a work based on | |
42 | this file might be covered by the GNU General Public License. | |
43 | ||
44 | Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. | |
45 | at http://sources.redhat.com/ecos/ecos-license/ | |
46 | ------------------------------------------- | |
47 | ####ECOSGPLCOPYRIGHTEND#### | |
48 | ####BSDCOPYRIGHTBEGIN#### | |
49 | ||
50 | ------------------------------------------- | |
51 | ||
52 | Portions of this software may have been derived from OpenBSD or other sources, | |
53 | and are covered by the appropriate copyright disclaimers included herein. | |
54 | ||
55 | ------------------------------------------- | |
56 | ||
57 | ####BSDCOPYRIGHTEND#### | |
58 | ========================================================================== | |
59 | #####DESCRIPTIONBEGIN#### | |
60 | ||
61 | Author(s): gthomas | |
62 | Contributors: gthomas, jskov, rsandifo | |
63 | Date: 2001-06-13 | |
64 | Purpose: | |
65 | Description: | |
66 | ||
67 | FIXME: Will fail if pinged with large packets (1520 bytes) | |
68 | Add promisc config | |
69 | Add SNMP | |
70 | ||
71 | ####DESCRIPTIONEND#### | |
72 | ||
73 | ||
74 | ========================================================================== | |
75 | ||
76 | */ | |
77 | ||
78 | #include <common.h> | |
79 | #include <command.h> | |
80 | #include <net.h> | |
81 | #include <malloc.h> | |
82 | ||
83 | #ifdef CONFIG_DRIVER_NE2000 | |
84 | ||
85 | /* wor around udelay resetting OCR */ | |
86 | static void my_udelay(long us) { | |
87 | long tmo; | |
88 | ||
89 | tmo = get_timer (0) + us * CFG_HZ / 1000000; /* will this be much greater than 0 ? */ | |
90 | while (get_timer (0) < tmo); | |
91 | } | |
92 | ||
93 | #define mdelay(n) my_udelay((n)*1000) | |
94 | ||
95 | /* forward definition of function used for the uboot interface */ | |
96 | void uboot_push_packet_len(int len); | |
97 | void uboot_push_tx_done(int key, int val); | |
98 | ||
99 | /* timeout for tx/rx in s */ | |
100 | #define TOUT 5 | |
101 | ||
102 | #define ETHER_ADDR_LEN 6 | |
103 | ||
104 | /* | |
105 | ------------------------------------------------------------------------ | |
106 | Debugging details | |
107 | ||
108 | Set to perms of: | |
109 | 0 disables all debug output | |
110 | 1 for process debug output | |
111 | 2 for added data IO output: get_reg, put_reg | |
112 | 4 for packet allocation/free output | |
113 | 8 for only startup status, so we can tell we're installed OK | |
114 | */ | |
115 | /*#define DEBUG 0xf*/ | |
116 | #define DEBUG 0 | |
117 | ||
118 | #if DEBUG & 1 | |
119 | #define DEBUG_FUNCTION() do { printf("%s\n", __FUNCTION__); } while (0) | |
120 | #define DEBUG_LINE() do { printf("%d\n", __LINE__); } while (0) | |
121 | #else | |
122 | #define DEBUG_FUNCTION() do {} while(0) | |
123 | #define DEBUG_LINE() do {} while(0) | |
124 | #endif | |
125 | ||
126 | #include "ne2000.h" | |
127 | ||
128 | #if DEBUG & 1 | |
129 | #define PRINTK(args...) printf(args) | |
130 | #else | |
131 | #define PRINTK(args...) | |
132 | #endif | |
133 | ||
134 | static dp83902a_priv_data_t nic; /* just one instance of the card supported */ | |
135 | ||
136 | static bool | |
137 | dp83902a_init(void) | |
138 | { | |
139 | dp83902a_priv_data_t *dp = &nic; | |
140 | cyg_uint8* base; | |
141 | int i; | |
142 | ||
143 | DEBUG_FUNCTION(); | |
144 | ||
145 | base = dp->base; | |
146 | if (!base) return false; /* No device found */ | |
147 | ||
148 | DEBUG_LINE(); | |
149 | ||
150 | /* Prepare ESA */ | |
151 | DP_OUT(base, DP_CR, DP_CR_NODMA | DP_CR_PAGE1); /* Select page 1 */ | |
152 | /* Use the address from the serial EEPROM */ | |
153 | for (i = 0; i < 6; i++) | |
154 | DP_IN(base, DP_P1_PAR0+i, dp->esa[i]); | |
155 | DP_OUT(base, DP_CR, DP_CR_NODMA | DP_CR_PAGE0); /* Select page 0 */ | |
156 | ||
157 | printf("NE2000 - %s ESA: %02x:%02x:%02x:%02x:%02x:%02x\n", | |
158 | "eeprom", | |
159 | dp->esa[0], | |
160 | dp->esa[1], | |
161 | dp->esa[2], | |
162 | dp->esa[3], | |
163 | dp->esa[4], | |
164 | dp->esa[5] ); | |
165 | ||
166 | return true; | |
167 | } | |
168 | ||
169 | static void | |
170 | dp83902a_stop(void) | |
171 | { | |
172 | dp83902a_priv_data_t *dp = &nic; | |
173 | cyg_uint8 *base = dp->base; | |
174 | ||
175 | DEBUG_FUNCTION(); | |
176 | ||
177 | DP_OUT(base, DP_CR, DP_CR_PAGE0 | DP_CR_NODMA | DP_CR_STOP); /* Brutal */ | |
178 | DP_OUT(base, DP_ISR, 0xFF); /* Clear any pending interrupts */ | |
179 | DP_OUT(base, DP_IMR, 0x00); /* Disable all interrupts */ | |
180 | ||
181 | dp->running = false; | |
182 | } | |
183 | ||
184 | /* | |
185 | This function is called to "start up" the interface. It may be called | |
186 | multiple times, even when the hardware is already running. It will be | |
187 | called whenever something "hardware oriented" changes and should leave | |
188 | the hardware ready to send/receive packets. | |
189 | */ | |
190 | static void | |
191 | dp83902a_start(unsigned char * enaddr) | |
192 | { | |
193 | dp83902a_priv_data_t *dp = &nic; | |
194 | cyg_uint8 *base = dp->base; | |
195 | int i; | |
196 | ||
197 | DEBUG_FUNCTION(); | |
198 | ||
199 | DP_OUT(base, DP_CR, DP_CR_PAGE0 | DP_CR_NODMA | DP_CR_STOP); /* Brutal */ | |
200 | DP_OUT(base, DP_DCR, DP_DCR_INIT); | |
201 | DP_OUT(base, DP_RBCH, 0); /* Remote byte count */ | |
202 | DP_OUT(base, DP_RBCL, 0); | |
203 | DP_OUT(base, DP_RCR, DP_RCR_MON); /* Accept no packets */ | |
204 | DP_OUT(base, DP_TCR, DP_TCR_LOCAL); /* Transmitter [virtually] off */ | |
205 | DP_OUT(base, DP_TPSR, dp->tx_buf1); /* Transmitter start page */ | |
206 | dp->tx1 = dp->tx2 = 0; | |
207 | dp->tx_next = dp->tx_buf1; | |
208 | dp->tx_started = false; | |
209 | DP_OUT(base, DP_PSTART, dp->rx_buf_start); /* Receive ring start page */ | |
210 | DP_OUT(base, DP_BNDRY, dp->rx_buf_end-1); /* Receive ring boundary */ | |
211 | DP_OUT(base, DP_PSTOP, dp->rx_buf_end); /* Receive ring end page */ | |
212 | dp->rx_next = dp->rx_buf_start-1; | |
213 | DP_OUT(base, DP_ISR, 0xFF); /* Clear any pending interrupts */ | |
214 | DP_OUT(base, DP_IMR, DP_IMR_All); /* Enable all interrupts */ | |
215 | DP_OUT(base, DP_CR, DP_CR_NODMA | DP_CR_PAGE1 | DP_CR_STOP); /* Select page 1 */ | |
216 | DP_OUT(base, DP_P1_CURP, dp->rx_buf_start); /* Current page - next free page for Rx */ | |
217 | for (i = 0; i < ETHER_ADDR_LEN; i++) { | |
218 | DP_OUT(base, DP_P1_PAR0+i, enaddr[i]); | |
219 | } | |
220 | /* Enable and start device */ | |
221 | DP_OUT(base, DP_CR, DP_CR_PAGE0 | DP_CR_NODMA | DP_CR_START); | |
222 | DP_OUT(base, DP_TCR, DP_TCR_NORMAL); /* Normal transmit operations */ | |
223 | DP_OUT(base, DP_RCR, DP_RCR_AB); /* Accept broadcast, no errors, no multicast */ | |
224 | dp->running = true; | |
225 | } | |
226 | ||
227 | /* | |
228 | This routine is called to start the transmitter. It is split out from the | |
229 | data handling routine so it may be called either when data becomes first | |
230 | available or when an Tx interrupt occurs | |
231 | */ | |
232 | ||
233 | static void | |
234 | dp83902a_start_xmit(int start_page, int len) | |
235 | { | |
236 | dp83902a_priv_data_t *dp = (dp83902a_priv_data_t *) &nic; | |
237 | cyg_uint8 *base = dp->base; | |
238 | ||
239 | DEBUG_FUNCTION(); | |
240 | ||
241 | #if DEBUG & 1 | |
242 | printf("Tx pkt %d len %d\n", start_page, len); | |
243 | if (dp->tx_started) | |
244 | printf("TX already started?!?\n"); | |
245 | #endif | |
246 | ||
247 | DP_OUT(base, DP_ISR, (DP_ISR_TxP | DP_ISR_TxE)); | |
248 | DP_OUT(base, DP_CR, DP_CR_PAGE0 | DP_CR_NODMA | DP_CR_START); | |
249 | DP_OUT(base, DP_TBCL, len & 0xFF); | |
250 | DP_OUT(base, DP_TBCH, len >> 8); | |
251 | DP_OUT(base, DP_TPSR, start_page); | |
252 | DP_OUT(base, DP_CR, DP_CR_NODMA | DP_CR_TXPKT | DP_CR_START); | |
253 | ||
254 | dp->tx_started = true; | |
255 | } | |
256 | ||
257 | /* | |
258 | This routine is called to send data to the hardware. It is known a-priori | |
259 | that there is free buffer space (dp->tx_next). | |
260 | */ | |
261 | static void | |
262 | dp83902a_send(unsigned char *data, int total_len, unsigned long key) | |
263 | { | |
264 | struct dp83902a_priv_data *dp = (struct dp83902a_priv_data *) &nic; | |
265 | cyg_uint8 *base = dp->base; | |
266 | int len, start_page, pkt_len, i, isr; | |
267 | #if DEBUG & 4 | |
268 | int dx; | |
269 | #endif | |
270 | ||
271 | DEBUG_FUNCTION(); | |
272 | ||
273 | len = pkt_len = total_len; | |
274 | if (pkt_len < IEEE_8023_MIN_FRAME) pkt_len = IEEE_8023_MIN_FRAME; | |
275 | ||
276 | start_page = dp->tx_next; | |
277 | if (dp->tx_next == dp->tx_buf1) { | |
278 | dp->tx1 = start_page; | |
279 | dp->tx1_len = pkt_len; | |
280 | dp->tx1_key = key; | |
281 | dp->tx_next = dp->tx_buf2; | |
282 | } else { | |
283 | dp->tx2 = start_page; | |
284 | dp->tx2_len = pkt_len; | |
285 | dp->tx2_key = key; | |
286 | dp->tx_next = dp->tx_buf1; | |
287 | } | |
288 | ||
289 | #if DEBUG & 5 | |
290 | printf("TX prep page %d len %d\n", start_page, pkt_len); | |
291 | #endif | |
292 | ||
293 | DP_OUT(base, DP_ISR, DP_ISR_RDC); /* Clear end of DMA */ | |
294 | { | |
295 | /* Dummy read. The manual sez something slightly different, */ | |
296 | /* but the code is extended a bit to do what Hitachi's monitor */ | |
297 | /* does (i.e., also read data). */ | |
298 | ||
299 | cyg_uint16 tmp; | |
300 | int len = 1; | |
301 | ||
302 | DP_OUT(base, DP_RSAL, 0x100-len); | |
303 | DP_OUT(base, DP_RSAH, (start_page-1) & 0xff); | |
304 | DP_OUT(base, DP_RBCL, len); | |
305 | DP_OUT(base, DP_RBCH, 0); | |
306 | DP_OUT(base, DP_CR, DP_CR_PAGE0 | DP_CR_RDMA | DP_CR_START); | |
307 | DP_IN_DATA(dp->data, tmp); | |
308 | } | |
309 | ||
310 | #ifdef CYGHWR_NS_DP83902A_PLF_BROKEN_TX_DMA | |
311 | /* Stall for a bit before continuing to work around random data */ | |
312 | /* corruption problems on some platforms. */ | |
313 | CYGACC_CALL_IF_DELAY_US(1); | |
314 | #endif | |
315 | ||
316 | /* Send data to device buffer(s) */ | |
317 | DP_OUT(base, DP_RSAL, 0); | |
318 | DP_OUT(base, DP_RSAH, start_page); | |
319 | DP_OUT(base, DP_RBCL, pkt_len & 0xFF); | |
320 | DP_OUT(base, DP_RBCH, pkt_len >> 8); | |
321 | DP_OUT(base, DP_CR, DP_CR_WDMA | DP_CR_START); | |
322 | ||
323 | /* Put data into buffer */ | |
324 | #if DEBUG & 4 | |
325 | printf(" sg buf %08lx len %08x\n ", (unsigned long) data, len); | |
326 | dx = 0; | |
327 | #endif | |
328 | while (len > 0) { | |
329 | #if DEBUG & 4 | |
330 | printf(" %02x", *data); | |
331 | if (0 == (++dx % 16)) printf("\n "); | |
332 | #endif | |
333 | DP_OUT_DATA(dp->data, *data++); | |
334 | len--; | |
335 | } | |
336 | #if DEBUG & 4 | |
337 | printf("\n"); | |
338 | #endif | |
339 | if (total_len < pkt_len) { | |
340 | #if DEBUG & 4 | |
341 | printf(" + %d bytes of padding\n", pkt_len - total_len); | |
342 | #endif | |
343 | /* Padding to 802.3 length was required */ | |
344 | for (i = total_len; i < pkt_len;) { | |
345 | i++; | |
346 | DP_OUT_DATA(dp->data, 0); | |
347 | } | |
348 | } | |
349 | ||
350 | #ifdef CYGHWR_NS_DP83902A_PLF_BROKEN_TX_DMA | |
351 | /* After last data write, delay for a bit before accessing the */ | |
352 | /* device again, or we may get random data corruption in the last */ | |
353 | /* datum (on some platforms). */ | |
354 | CYGACC_CALL_IF_DELAY_US(1); | |
355 | #endif | |
356 | ||
357 | /* Wait for DMA to complete */ | |
358 | do { | |
359 | DP_IN(base, DP_ISR, isr); | |
360 | } while ((isr & DP_ISR_RDC) == 0); | |
361 | /* Then disable DMA */ | |
362 | DP_OUT(base, DP_CR, DP_CR_PAGE0 | DP_CR_NODMA | DP_CR_START); | |
363 | ||
364 | /* Start transmit if not already going */ | |
365 | if (!dp->tx_started) { | |
366 | if (start_page == dp->tx1) { | |
367 | dp->tx_int = 1; /* Expecting interrupt from BUF1 */ | |
368 | } else { | |
369 | dp->tx_int = 2; /* Expecting interrupt from BUF2 */ | |
370 | } | |
371 | dp83902a_start_xmit(start_page, pkt_len); | |
372 | } | |
373 | } | |
374 | ||
375 | /* | |
376 | This function is called when a packet has been received. It's job is | |
377 | to prepare to unload the packet from the hardware. Once the length of | |
378 | the packet is known, the upper layer of the driver can be told. When | |
379 | the upper layer is ready to unload the packet, the internal function | |
380 | 'dp83902a_recv' will be called to actually fetch it from the hardware. | |
381 | */ | |
382 | static void | |
383 | dp83902a_RxEvent(void) | |
384 | { | |
385 | struct dp83902a_priv_data *dp = (struct dp83902a_priv_data *) &nic; | |
386 | cyg_uint8 *base = dp->base; | |
387 | unsigned char rsr; | |
388 | unsigned char rcv_hdr[4]; | |
389 | int i, len, pkt, cur; | |
390 | ||
391 | DEBUG_FUNCTION(); | |
392 | ||
393 | DP_IN(base, DP_RSR, rsr); | |
394 | while (true) { | |
395 | /* Read incoming packet header */ | |
396 | DP_OUT(base, DP_CR, DP_CR_PAGE1 | DP_CR_NODMA | DP_CR_START); | |
397 | DP_IN(base, DP_P1_CURP, cur); | |
398 | DP_OUT(base, DP_P1_CR, DP_CR_PAGE0 | DP_CR_NODMA | DP_CR_START); | |
399 | DP_IN(base, DP_BNDRY, pkt); | |
400 | ||
401 | pkt += 1; | |
402 | if (pkt == dp->rx_buf_end) | |
403 | pkt = dp->rx_buf_start; | |
404 | ||
405 | if (pkt == cur) { | |
406 | break; | |
407 | } | |
408 | DP_OUT(base, DP_RBCL, sizeof(rcv_hdr)); | |
409 | DP_OUT(base, DP_RBCH, 0); | |
410 | DP_OUT(base, DP_RSAL, 0); | |
411 | DP_OUT(base, DP_RSAH, pkt); | |
412 | if (dp->rx_next == pkt) { | |
413 | if (cur == dp->rx_buf_start) | |
414 | DP_OUT(base, DP_BNDRY, dp->rx_buf_end-1); | |
415 | else | |
416 | DP_OUT(base, DP_BNDRY, cur-1); /* Update pointer */ | |
417 | return; | |
418 | } | |
419 | dp->rx_next = pkt; | |
420 | DP_OUT(base, DP_ISR, DP_ISR_RDC); /* Clear end of DMA */ | |
421 | DP_OUT(base, DP_CR, DP_CR_RDMA | DP_CR_START); | |
422 | #ifdef CYGHWR_NS_DP83902A_PLF_BROKEN_RX_DMA | |
423 | CYGACC_CALL_IF_DELAY_US(10); | |
424 | #endif | |
425 | ||
426 | for (i = 0; i < sizeof(rcv_hdr);) { | |
427 | DP_IN_DATA(dp->data, rcv_hdr[i++]); | |
428 | } | |
429 | ||
430 | #if DEBUG & 5 | |
431 | printf("rx hdr %02x %02x %02x %02x\n", | |
432 | rcv_hdr[0], rcv_hdr[1], rcv_hdr[2], rcv_hdr[3]); | |
433 | #endif | |
434 | len = ((rcv_hdr[3] << 8) | rcv_hdr[2]) - sizeof(rcv_hdr); | |
435 | uboot_push_packet_len(len); | |
436 | if (rcv_hdr[1] == dp->rx_buf_start) | |
437 | DP_OUT(base, DP_BNDRY, dp->rx_buf_end-1); | |
438 | else | |
439 | DP_OUT(base, DP_BNDRY, rcv_hdr[1]-1); /* Update pointer */ | |
440 | } | |
441 | } | |
442 | ||
443 | /* | |
444 | This function is called as a result of the "eth_drv_recv()" call above. | |
445 | It's job is to actually fetch data for a packet from the hardware once | |
446 | memory buffers have been allocated for the packet. Note that the buffers | |
447 | may come in pieces, using a scatter-gather list. This allows for more | |
448 | efficient processing in the upper layers of the stack. | |
449 | */ | |
450 | static void | |
451 | dp83902a_recv(unsigned char *data, int len) | |
452 | { | |
453 | struct dp83902a_priv_data *dp = (struct dp83902a_priv_data *) &nic; | |
454 | cyg_uint8 *base = dp->base; | |
455 | int i, mlen; | |
456 | cyg_uint8 saved_char = 0; | |
457 | bool saved; | |
458 | #if DEBUG & 4 | |
459 | int dx; | |
460 | #endif | |
461 | ||
462 | DEBUG_FUNCTION(); | |
463 | ||
464 | #if DEBUG & 5 | |
465 | printf("Rx packet %d length %d\n", dp->rx_next, len); | |
466 | #endif | |
467 | ||
468 | /* Read incoming packet data */ | |
469 | DP_OUT(base, DP_CR, DP_CR_PAGE0 | DP_CR_NODMA | DP_CR_START); | |
470 | DP_OUT(base, DP_RBCL, len & 0xFF); | |
471 | DP_OUT(base, DP_RBCH, len >> 8); | |
472 | DP_OUT(base, DP_RSAL, 4); /* Past header */ | |
473 | DP_OUT(base, DP_RSAH, dp->rx_next); | |
474 | DP_OUT(base, DP_ISR, DP_ISR_RDC); /* Clear end of DMA */ | |
475 | DP_OUT(base, DP_CR, DP_CR_RDMA | DP_CR_START); | |
476 | #ifdef CYGHWR_NS_DP83902A_PLF_BROKEN_RX_DMA | |
477 | CYGACC_CALL_IF_DELAY_US(10); | |
478 | #endif | |
479 | ||
480 | saved = false; | |
481 | for (i = 0; i < 1; i++) { | |
482 | if (data) { | |
483 | mlen = len; | |
484 | #if DEBUG & 4 | |
485 | printf(" sg buf %08lx len %08x \n", (unsigned long) data, mlen); | |
486 | dx = 0; | |
487 | #endif | |
488 | while (0 < mlen) { | |
489 | /* Saved byte from previous loop? */ | |
490 | if (saved) { | |
491 | *data++ = saved_char; | |
492 | mlen--; | |
493 | saved = false; | |
494 | continue; | |
495 | } | |
496 | ||
497 | { | |
498 | cyg_uint8 tmp; | |
499 | DP_IN_DATA(dp->data, tmp); | |
500 | #if DEBUG & 4 | |
501 | printf(" %02x", tmp); | |
502 | if (0 == (++dx % 16)) printf("\n "); | |
503 | #endif | |
504 | *data++ = tmp;; | |
505 | mlen--; | |
506 | } | |
507 | } | |
508 | #if DEBUG & 4 | |
509 | printf("\n"); | |
510 | #endif | |
511 | } | |
512 | } | |
513 | } | |
514 | ||
515 | static void | |
516 | dp83902a_TxEvent(void) | |
517 | { | |
518 | struct dp83902a_priv_data *dp = (struct dp83902a_priv_data *) &nic; | |
519 | cyg_uint8 *base = dp->base; | |
520 | unsigned char tsr; | |
521 | unsigned long key; | |
522 | ||
523 | DEBUG_FUNCTION(); | |
524 | ||
525 | DP_IN(base, DP_TSR, tsr); | |
526 | if (dp->tx_int == 1) { | |
527 | key = dp->tx1_key; | |
528 | dp->tx1 = 0; | |
529 | } else { | |
530 | key = dp->tx2_key; | |
531 | dp->tx2 = 0; | |
532 | } | |
533 | /* Start next packet if one is ready */ | |
534 | dp->tx_started = false; | |
535 | if (dp->tx1) { | |
536 | dp83902a_start_xmit(dp->tx1, dp->tx1_len); | |
537 | dp->tx_int = 1; | |
538 | } else if (dp->tx2) { | |
539 | dp83902a_start_xmit(dp->tx2, dp->tx2_len); | |
540 | dp->tx_int = 2; | |
541 | } else { | |
542 | dp->tx_int = 0; | |
543 | } | |
544 | /* Tell higher level we sent this packet */ | |
545 | uboot_push_tx_done(key, 0); | |
546 | } | |
547 | ||
548 | /* Read the tally counters to clear them. Called in response to a CNT */ | |
549 | /* interrupt. */ | |
550 | static void | |
551 | dp83902a_ClearCounters(void) | |
552 | { | |
553 | struct dp83902a_priv_data *dp = (struct dp83902a_priv_data *) &nic; | |
554 | cyg_uint8 *base = dp->base; | |
555 | cyg_uint8 cnt1, cnt2, cnt3; | |
556 | ||
557 | DP_IN(base, DP_FER, cnt1); | |
558 | DP_IN(base, DP_CER, cnt2); | |
559 | DP_IN(base, DP_MISSED, cnt3); | |
560 | DP_OUT(base, DP_ISR, DP_ISR_CNT); | |
561 | } | |
562 | ||
563 | /* Deal with an overflow condition. This code follows the procedure set */ | |
564 | /* out in section 7.0 of the datasheet. */ | |
565 | static void | |
566 | dp83902a_Overflow(void) | |
567 | { | |
568 | struct dp83902a_priv_data *dp = (struct dp83902a_priv_data *)&nic; | |
569 | cyg_uint8 *base = dp->base; | |
570 | cyg_uint8 isr; | |
571 | ||
572 | /* Issue a stop command and wait 1.6ms for it to complete. */ | |
573 | DP_OUT(base, DP_CR, DP_CR_STOP | DP_CR_NODMA); | |
574 | CYGACC_CALL_IF_DELAY_US(1600); | |
575 | ||
576 | /* Clear the remote byte counter registers. */ | |
577 | DP_OUT(base, DP_RBCL, 0); | |
578 | DP_OUT(base, DP_RBCH, 0); | |
579 | ||
580 | /* Enter loopback mode while we clear the buffer. */ | |
581 | DP_OUT(base, DP_TCR, DP_TCR_LOCAL); | |
582 | DP_OUT(base, DP_CR, DP_CR_START | DP_CR_NODMA); | |
583 | ||
584 | /* Read in as many packets as we can and acknowledge any and receive */ | |
585 | /* interrupts. Since the buffer has overflowed, a receive event of */ | |
586 | /* some kind will have occured. */ | |
587 | dp83902a_RxEvent(); | |
588 | DP_OUT(base, DP_ISR, DP_ISR_RxP|DP_ISR_RxE); | |
589 | ||
590 | /* Clear the overflow condition and leave loopback mode. */ | |
591 | DP_OUT(base, DP_ISR, DP_ISR_OFLW); | |
592 | DP_OUT(base, DP_TCR, DP_TCR_NORMAL); | |
593 | ||
594 | /* If a transmit command was issued, but no transmit event has occured, */ | |
595 | /* restart it here. */ | |
596 | DP_IN(base, DP_ISR, isr); | |
597 | if (dp->tx_started && !(isr & (DP_ISR_TxP|DP_ISR_TxE))) { | |
598 | DP_OUT(base, DP_CR, DP_CR_NODMA | DP_CR_TXPKT | DP_CR_START); | |
599 | } | |
600 | } | |
601 | ||
602 | static void | |
603 | dp83902a_poll(void) | |
604 | { | |
605 | struct dp83902a_priv_data *dp = (struct dp83902a_priv_data *) &nic; | |
606 | cyg_uint8 *base = dp->base; | |
607 | unsigned char isr; | |
608 | ||
609 | DP_OUT(base, DP_CR, DP_CR_NODMA | DP_CR_PAGE0 | DP_CR_START); | |
610 | DP_IN(base, DP_ISR, isr); | |
611 | while (0 != isr) { | |
612 | /* The CNT interrupt triggers when the MSB of one of the error */ | |
613 | /* counters is set. We don't much care about these counters, but */ | |
614 | /* we should read their values to reset them. */ | |
615 | if (isr & DP_ISR_CNT) { | |
616 | dp83902a_ClearCounters(); | |
617 | } | |
618 | /* Check for overflow. It's a special case, since there's a */ | |
619 | /* particular procedure that must be followed to get back into */ | |
620 | /* a running state.a */ | |
621 | if (isr & DP_ISR_OFLW) { | |
622 | dp83902a_Overflow(); | |
623 | } else { | |
624 | /* Other kinds of interrupts can be acknowledged simply by */ | |
625 | /* clearing the relevant bits of the ISR. Do that now, then */ | |
626 | /* handle the interrupts we care about. */ | |
627 | DP_OUT(base, DP_ISR, isr); /* Clear set bits */ | |
628 | if (!dp->running) break; /* Is this necessary? */ | |
629 | /* Check for tx_started on TX event since these may happen */ | |
630 | /* spuriously it seems. */ | |
631 | if (isr & (DP_ISR_TxP|DP_ISR_TxE) && dp->tx_started) { | |
632 | dp83902a_TxEvent(); | |
633 | } | |
634 | if (isr & (DP_ISR_RxP|DP_ISR_RxE)) { | |
635 | dp83902a_RxEvent(); | |
636 | } | |
637 | } | |
638 | DP_IN(base, DP_ISR, isr); | |
639 | } | |
640 | } | |
641 | ||
642 | /* find prom (taken from pc_net_cs.c from Linux) */ | |
643 | ||
644 | #include "8390.h" | |
645 | ||
646 | typedef struct hw_info_t { | |
647 | u_int offset; | |
648 | u_char a0, a1, a2; | |
649 | u_int flags; | |
650 | } hw_info_t; | |
651 | ||
652 | #define DELAY_OUTPUT 0x01 | |
653 | #define HAS_MISC_REG 0x02 | |
654 | #define USE_BIG_BUF 0x04 | |
655 | #define HAS_IBM_MISC 0x08 | |
656 | #define IS_DL10019 0x10 | |
657 | #define IS_DL10022 0x20 | |
658 | #define HAS_MII 0x40 | |
659 | #define USE_SHMEM 0x80 /* autodetected */ | |
660 | ||
661 | #define AM79C9XX_HOME_PHY 0x00006B90 /* HomePNA PHY */ | |
662 | #define AM79C9XX_ETH_PHY 0x00006B70 /* 10baseT PHY */ | |
663 | #define MII_PHYID_REV_MASK 0xfffffff0 | |
664 | #define MII_PHYID_REG1 0x02 | |
665 | #define MII_PHYID_REG2 0x03 | |
666 | ||
667 | static hw_info_t hw_info[] = { | |
668 | { /* Accton EN2212 */ 0x0ff0, 0x00, 0x00, 0xe8, DELAY_OUTPUT }, | |
669 | { /* Allied Telesis LA-PCM */ 0x0ff0, 0x00, 0x00, 0xf4, 0 }, | |
670 | { /* APEX MultiCard */ 0x03f4, 0x00, 0x20, 0xe5, 0 }, | |
671 | { /* ASANTE FriendlyNet */ 0x4910, 0x00, 0x00, 0x94, | |
672 | DELAY_OUTPUT | HAS_IBM_MISC }, | |
673 | { /* Danpex EN-6200P2 */ 0x0110, 0x00, 0x40, 0xc7, 0 }, | |
674 | { /* DataTrek NetCard */ 0x0ff0, 0x00, 0x20, 0xe8, 0 }, | |
675 | { /* Dayna CommuniCard E */ 0x0110, 0x00, 0x80, 0x19, 0 }, | |
676 | { /* D-Link DE-650 */ 0x0040, 0x00, 0x80, 0xc8, 0 }, | |
677 | { /* EP-210 Ethernet */ 0x0110, 0x00, 0x40, 0x33, 0 }, | |
678 | { /* EP4000 Ethernet */ 0x01c0, 0x00, 0x00, 0xb4, 0 }, | |
679 | { /* Epson EEN10B */ 0x0ff0, 0x00, 0x00, 0x48, | |
680 | HAS_MISC_REG | HAS_IBM_MISC }, | |
681 | { /* ELECOM Laneed LD-CDWA */ 0xb8, 0x08, 0x00, 0x42, 0 }, | |
682 | { /* Hypertec Ethernet */ 0x01c0, 0x00, 0x40, 0x4c, 0 }, | |
683 | { /* IBM CCAE */ 0x0ff0, 0x08, 0x00, 0x5a, | |
684 | HAS_MISC_REG | HAS_IBM_MISC }, | |
685 | { /* IBM CCAE */ 0x0ff0, 0x00, 0x04, 0xac, | |
686 | HAS_MISC_REG | HAS_IBM_MISC }, | |
687 | { /* IBM CCAE */ 0x0ff0, 0x00, 0x06, 0x29, | |
688 | HAS_MISC_REG | HAS_IBM_MISC }, | |
689 | { /* IBM FME */ 0x0374, 0x08, 0x00, 0x5a, | |
690 | HAS_MISC_REG | HAS_IBM_MISC }, | |
691 | { /* IBM FME */ 0x0374, 0x00, 0x04, 0xac, | |
692 | HAS_MISC_REG | HAS_IBM_MISC }, | |
693 | { /* Kansai KLA-PCM/T */ 0x0ff0, 0x00, 0x60, 0x87, | |
694 | HAS_MISC_REG | HAS_IBM_MISC }, | |
695 | { /* NSC DP83903 */ 0x0374, 0x08, 0x00, 0x17, | |
696 | HAS_MISC_REG | HAS_IBM_MISC }, | |
697 | { /* NSC DP83903 */ 0x0374, 0x00, 0xc0, 0xa8, | |
698 | HAS_MISC_REG | HAS_IBM_MISC }, | |
699 | { /* NSC DP83903 */ 0x0374, 0x00, 0xa0, 0xb0, | |
700 | HAS_MISC_REG | HAS_IBM_MISC }, | |
701 | { /* NSC DP83903 */ 0x0198, 0x00, 0x20, 0xe0, | |
702 | HAS_MISC_REG | HAS_IBM_MISC }, | |
703 | { /* I-O DATA PCLA/T */ 0x0ff0, 0x00, 0xa0, 0xb0, 0 }, | |
704 | { /* Katron PE-520 */ 0x0110, 0x00, 0x40, 0xf6, 0 }, | |
705 | { /* Kingston KNE-PCM/x */ 0x0ff0, 0x00, 0xc0, 0xf0, | |
706 | HAS_MISC_REG | HAS_IBM_MISC }, | |
707 | { /* Kingston KNE-PCM/x */ 0x0ff0, 0xe2, 0x0c, 0x0f, | |
708 | HAS_MISC_REG | HAS_IBM_MISC }, | |
709 | { /* Kingston KNE-PC2 */ 0x0180, 0x00, 0xc0, 0xf0, 0 }, | |
710 | { /* Maxtech PCN2000 */ 0x5000, 0x00, 0x00, 0xe8, 0 }, | |
711 | { /* NDC Instant-Link */ 0x003a, 0x00, 0x80, 0xc6, 0 }, | |
712 | { /* NE2000 Compatible */ 0x0ff0, 0x00, 0xa0, 0x0c, 0 }, | |
713 | { /* Network General Sniffer */ 0x0ff0, 0x00, 0x00, 0x65, | |
714 | HAS_MISC_REG | HAS_IBM_MISC }, | |
715 | { /* Panasonic VEL211 */ 0x0ff0, 0x00, 0x80, 0x45, | |
716 | HAS_MISC_REG | HAS_IBM_MISC }, | |
717 | { /* PreMax PE-200 */ 0x07f0, 0x00, 0x20, 0xe0, 0 }, | |
718 | { /* RPTI EP400 */ 0x0110, 0x00, 0x40, 0x95, 0 }, | |
719 | { /* SCM Ethernet */ 0x0ff0, 0x00, 0x20, 0xcb, 0 }, | |
720 | { /* Socket EA */ 0x4000, 0x00, 0xc0, 0x1b, | |
721 | DELAY_OUTPUT | HAS_MISC_REG | USE_BIG_BUF }, | |
722 | { /* Socket LP-E CF+ */ 0x01c0, 0x00, 0xc0, 0x1b, 0 }, | |
723 | { /* SuperSocket RE450T */ 0x0110, 0x00, 0xe0, 0x98, 0 }, | |
724 | { /* Volktek NPL-402CT */ 0x0060, 0x00, 0x40, 0x05, 0 }, | |
725 | { /* NEC PC-9801N-J12 */ 0x0ff0, 0x00, 0x00, 0x4c, 0 }, | |
726 | { /* PCMCIA Technology OEM */ 0x01c8, 0x00, 0xa0, 0x0c, 0 } | |
727 | }; | |
728 | ||
729 | #define NR_INFO (sizeof(hw_info)/sizeof(hw_info_t)) | |
730 | ||
731 | static hw_info_t default_info = { 0, 0, 0, 0, 0 }; | |
732 | ||
733 | unsigned char dev_addr[6]; | |
734 | ||
735 | #define PCNET_CMD 0x00 | |
736 | #define PCNET_DATAPORT 0x10 /* NatSemi-defined port window offset. */ | |
737 | #define PCNET_RESET 0x1f /* Issue a read to reset, a write to clear. */ | |
738 | #define PCNET_MISC 0x18 /* For IBM CCAE and Socket EA cards */ | |
739 | ||
740 | unsigned long nic_base; | |
741 | ||
742 | static void pcnet_reset_8390(void) | |
743 | { | |
744 | int i, r; | |
745 | ||
746 | PRINTK("nic base is %lx\n", nic_base); | |
747 | ||
748 | #if 1 | |
749 | n2k_outb(E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD); | |
750 | PRINTK("cmd (at %lx) is %x\n", nic_base+ E8390_CMD, n2k_inb(E8390_CMD)); | |
751 | n2k_outb(E8390_NODMA+E8390_PAGE1+E8390_STOP, E8390_CMD); | |
752 | PRINTK("cmd (at %lx) is %x\n", nic_base+ E8390_CMD, n2k_inb(E8390_CMD)); | |
753 | n2k_outb(E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD); | |
754 | PRINTK("cmd (at %lx) is %x\n", nic_base+ E8390_CMD, n2k_inb(E8390_CMD)); | |
755 | #endif | |
756 | n2k_outb(E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD); | |
757 | ||
758 | n2k_outb(n2k_inb(nic_base + PCNET_RESET), PCNET_RESET); | |
759 | ||
760 | for (i = 0; i < 100; i++) { | |
761 | if ((r = (n2k_inb(EN0_ISR) & ENISR_RESET)) != 0) | |
762 | break; | |
763 | PRINTK("got %x in reset\n", r); | |
764 | my_udelay(100); | |
765 | } | |
766 | n2k_outb(ENISR_RESET, EN0_ISR); /* Ack intr. */ | |
767 | ||
768 | if (i == 100) | |
769 | printf("pcnet_reset_8390() did not complete.\n"); | |
770 | } /* pcnet_reset_8390 */ | |
771 | ||
772 | static hw_info_t * get_prom(void ) { | |
773 | unsigned char prom[32]; | |
774 | int i, j; | |
775 | struct { | |
776 | u_char value, offset; | |
777 | } program_seq[] = { | |
778 | {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/ | |
779 | {0x48, EN0_DCFG}, /* Set byte-wide (0x48) access. */ | |
780 | {0x00, EN0_RCNTLO}, /* Clear the count regs. */ | |
781 | {0x00, EN0_RCNTHI}, | |
782 | {0x00, EN0_IMR}, /* Mask completion irq. */ | |
783 | {0xFF, EN0_ISR}, | |
784 | {E8390_RXOFF, EN0_RXCR}, /* 0x20 Set to monitor */ | |
785 | {E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */ | |
786 | {32, EN0_RCNTLO}, | |
787 | {0x00, EN0_RCNTHI}, | |
788 | {0x00, EN0_RSARLO}, /* DMA starting at 0x0000. */ | |
789 | {0x00, EN0_RSARHI}, | |
790 | {E8390_RREAD+E8390_START, E8390_CMD}, | |
791 | }; | |
792 | ||
793 | PRINTK("trying to get MAC via prom reading\n"); | |
794 | ||
795 | pcnet_reset_8390(); | |
796 | ||
797 | mdelay(10); | |
798 | ||
799 | for (i = 0; i < sizeof(program_seq)/sizeof(program_seq[0]); i++) | |
800 | n2k_outb(program_seq[i].value, program_seq[i].offset); | |
801 | ||
802 | PRINTK("PROM:"); | |
803 | for (i = 0; i < 32; i++) { | |
804 | prom[i] = n2k_inb(PCNET_DATAPORT); | |
805 | PRINTK(" %02x", prom[i]); | |
806 | } | |
807 | PRINTK("\n"); | |
808 | for (i = 0; i < NR_INFO; i++) { | |
809 | if ((prom[0] == hw_info[i].a0) && | |
810 | (prom[2] == hw_info[i].a1) && | |
811 | (prom[4] == hw_info[i].a2)) { | |
812 | PRINTK("matched board %d\n", i); | |
813 | break; | |
814 | } | |
815 | } | |
816 | if ((i < NR_INFO) || ((prom[28] == 0x57) && (prom[30] == 0x57))) { | |
817 | for (j = 0; j < 6; j++) | |
818 | dev_addr[j] = prom[j<<1]; | |
819 | PRINTK("on exit i is %d/%ld\n", i, NR_INFO); | |
820 | PRINTK("MAC address is %02x:%02x:%02x:%02x:%02x:%02x\n", | |
821 | dev_addr[0],dev_addr[1],dev_addr[2],dev_addr[3],dev_addr[4],dev_addr[5]); | |
822 | return (i < NR_INFO) ? hw_info+i : &default_info; | |
823 | } | |
824 | return NULL; | |
825 | } | |
826 | ||
827 | /* U-boot specific routines */ | |
828 | ||
829 | #define NB 5 | |
830 | ||
831 | static unsigned char *pbuf = NULL; | |
832 | static int plen[NB]; | |
833 | static int nrx = 0; | |
834 | ||
835 | static int pkey = -1; | |
836 | ||
837 | void uboot_push_packet_len(int len) { | |
838 | PRINTK("pushed len = %d, nrx = %d\n", len, nrx); | |
839 | if (len>=2000) { | |
840 | printf("NE2000: packet too big\n"); | |
841 | return; | |
842 | } | |
843 | if (nrx >= NB) { | |
844 | printf("losing packets in rx\n"); | |
845 | return; | |
846 | } | |
847 | plen[nrx] = len; | |
848 | dp83902a_recv(&pbuf[nrx*2000], len); | |
849 | nrx++; | |
850 | } | |
851 | ||
852 | void uboot_push_tx_done(int key, int val) { | |
853 | PRINTK("pushed key = %d\n", key); | |
854 | pkey = key; | |
855 | } | |
856 | ||
857 | int eth_init(bd_t *bd) { | |
858 | static hw_info_t * r; | |
859 | char ethaddr[20]; | |
860 | ||
861 | PRINTK("### eth_init\n"); | |
862 | ||
863 | if (!pbuf) { | |
864 | pbuf = malloc(NB*2000); | |
865 | if (!pbuf) { | |
866 | printf("Cannot allocate rx buffers\n"); | |
867 | return -1; | |
868 | } | |
869 | } | |
870 | ||
871 | #ifdef CONFIG_DRIVER_NE2000_CCR | |
872 | { | |
873 | volatile unsigned char *p = (volatile unsigned char *) CONFIG_DRIVER_NE2000_CCR; | |
874 | ||
875 | PRINTK("CCR before is %x\n", *p); | |
876 | *p = CONFIG_DRIVER_NE2000_VAL; | |
877 | PRINTK("CCR after is %x\n", *p); | |
878 | } | |
879 | #endif | |
880 | ||
881 | nic_base = CONFIG_DRIVER_NE2000_BASE; | |
882 | nic.base = (cyg_uint8 *) CONFIG_DRIVER_NE2000_BASE; | |
883 | ||
884 | r = get_prom(); | |
885 | if (!r) | |
886 | return -1; | |
887 | ||
888 | sprintf (ethaddr, "%02X:%02X:%02X:%02X:%02X:%02X", | |
889 | dev_addr[0], dev_addr[1], | |
890 | dev_addr[2], dev_addr[3], | |
891 | dev_addr[4], dev_addr[5]) ; | |
892 | PRINTK("Set environment from HW MAC addr = \"%s\"\n", ethaddr); | |
893 | setenv ("ethaddr", ethaddr); | |
894 | ||
895 | ||
896 | #define DP_DATA 0x10 | |
897 | nic.data = nic.base + DP_DATA; | |
898 | nic.tx_buf1 = 0x40; | |
899 | nic.tx_buf2 = 0x48; | |
900 | nic.rx_buf_start = 0x50; | |
901 | nic.rx_buf_end = 0x80; | |
902 | ||
903 | if (dp83902a_init() == false) | |
904 | return -1; | |
905 | dp83902a_start(dev_addr); | |
906 | return 0; | |
907 | } | |
908 | ||
909 | void eth_halt() { | |
910 | ||
911 | PRINTK("### eth_halt\n"); | |
912 | ||
913 | dp83902a_stop(); | |
914 | } | |
915 | ||
916 | int eth_rx() { | |
917 | int j, tmo; | |
918 | ||
919 | PRINTK("### eth_rx\n"); | |
920 | ||
921 | tmo = get_timer (0) + TOUT * CFG_HZ; | |
922 | while(1) { | |
923 | dp83902a_poll(); | |
924 | if (nrx > 0) { | |
925 | for(j=0; j<nrx; j++) { | |
926 | NetReceive(&pbuf[j*2000], plen[j]); | |
927 | } | |
928 | nrx = 0; | |
929 | return 1; | |
930 | } | |
931 | if (get_timer (0) >= tmo) { | |
932 | printf("timeout during rx\n"); | |
933 | return 0; | |
934 | } | |
935 | } | |
936 | return 0; | |
937 | } | |
938 | ||
939 | int eth_send(volatile void *packet, int length) { | |
940 | int tmo; | |
941 | ||
942 | PRINTK("### eth_send\n"); | |
943 | ||
944 | pkey = -1; | |
945 | ||
946 | dp83902a_send((unsigned char *) packet, length, 666); | |
947 | tmo = get_timer (0) + TOUT * CFG_HZ; | |
948 | while(1) { | |
949 | dp83902a_poll(); | |
950 | if (pkey != -1) { | |
951 | PRINTK("Packet sucesfully sent\n"); | |
952 | return 0; | |
953 | } | |
954 | if (get_timer (0) >= tmo) { | |
955 | printf("transmission error (timoeut)\n"); | |
956 | return 0; | |
957 | } | |
958 | ||
959 | } | |
960 | return 0; | |
961 | } | |
962 | ||
963 | #endif |