]>
Commit | Line | Data |
---|---|---|
fe8c2806 WD |
1 | /*------------------------------------------------------------------------ |
2 | . 3c589.c | |
3 | . This is a driver for 3Com's 3C589 (Etherlink III) PCMCIA Ethernet device. | |
4 | . | |
5 | . (C) Copyright 2002 | |
6 | . Sysgo Real-Time Solutions, GmbH <www.elinos.com> | |
7 | . Rolf Offermanns <rof@sysgo.de> | |
8 | . | |
9 | . This program is free software; you can redistribute it and/or modify | |
10 | . it under the terms of the GNU General Public License as published by | |
11 | . the Free Software Foundation; either version 2 of the License, or | |
12 | . (at your option) any later version. | |
13 | . | |
14 | . This program is distributed in the hope that it will be useful, | |
15 | . but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | . MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | . GNU General Public License for more details. | |
18 | . | |
19 | . You should have received a copy of the GNU General Public License | |
20 | . along with this program; if not, write to the Free Software | |
21 | . Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
22 | . | |
23 | ----------------------------------------------------------------------------*/ | |
24 | ||
25 | #include <common.h> | |
26 | #include <command.h> | |
27 | #include <net.h> | |
28 | ||
fe8c2806 WD |
29 | #include "3c589.h" |
30 | ||
31 | ||
32 | /* Use power-down feature of the chip */ | |
33 | #define POWER_DOWN 0 | |
34 | ||
35 | #define NO_AUTOPROBE | |
36 | ||
37 | static const char version[] = | |
38 | "Your ad here! :P\n"; | |
39 | ||
40 | ||
41 | #undef EL_DEBUG | |
42 | ||
43 | typedef unsigned char byte; | |
44 | typedef unsigned short word; | |
45 | typedef unsigned long int dword; | |
46 | /*------------------------------------------------------------------------ | |
47 | . | |
48 | . Configuration options, for the experienced user to change. | |
49 | . | |
50 | -------------------------------------------------------------------------*/ | |
51 | ||
52 | /* | |
53 | . Wait time for memory to be free. This probably shouldn't be | |
54 | . tuned that much, as waiting for this means nothing else happens | |
55 | . in the system | |
56 | */ | |
57 | #define MEMORY_WAIT_TIME 16 | |
58 | ||
59 | ||
60 | #if (EL_DEBUG > 2 ) | |
61 | #define PRINTK3(args...) printf(args) | |
62 | #else | |
63 | #define PRINTK3(args...) | |
64 | #endif | |
65 | ||
66 | #if EL_DEBUG > 1 | |
67 | #define PRINTK2(args...) printf(args) | |
68 | #else | |
69 | #define PRINTK2(args...) | |
70 | #endif | |
71 | ||
72 | #ifdef EL_DEBUG | |
73 | #define PRINTK(args...) printf(args) | |
74 | #else | |
75 | #define PRINTK(args...) | |
76 | #endif | |
77 | ||
78 | #define outb(args...) mmio_outb(args) | |
79 | #define mmio_outb(value, addr) (*((volatile byte *)(addr)) = value) | |
80 | ||
81 | #define inb(args...) mmio_inb(args) | |
82 | #define mmio_inb(addr) (*((volatile byte *)(addr))) | |
83 | ||
84 | #define outw(args...) mmio_outw(args) | |
85 | #define mmio_outw(value, addr) (*((volatile word *)(addr)) = value) | |
86 | ||
87 | #define inw(args...) mmio_inw(args) | |
88 | #define mmio_inw(addr) (*((volatile word *)(addr))) | |
89 | ||
90 | #define outsw(args...) mmio_outsw(args) | |
91 | #define mmio_outsw(r,b,l) ({ int __i; \ | |
92 | word *__b2; \ | |
93 | __b2 = (word *) b; \ | |
94 | for (__i = 0; __i < l; __i++) { \ | |
95 | mmio_outw( *(__b2 + __i), r); \ | |
96 | } \ | |
97 | }) | |
98 | ||
99 | #define insw(args...) mmio_insw(args) | |
53677ef1 | 100 | #define mmio_insw(r,b,l) ({ int __i ; \ |
fe8c2806 | 101 | word *__b2; \ |
8bde7f77 WD |
102 | __b2 = (word *) b; \ |
103 | for (__i = 0; __i < l; __i++) { \ | |
fe8c2806 WD |
104 | *(__b2 + __i) = mmio_inw(r); \ |
105 | mmio_inw(0); \ | |
106 | }; \ | |
107 | }) | |
108 | ||
109 | /*------------------------------------------------------------------------ | |
110 | . | |
111 | . The internal workings of the driver. If you are changing anything | |
112 | . here with the 3Com stuff, you should have the datasheet and know | |
113 | . what you are doing. | |
114 | . | |
115 | -------------------------------------------------------------------------*/ | |
116 | #define EL_BASE_ADDR 0x20000000 | |
117 | ||
118 | ||
119 | /* Offsets from base I/O address. */ | |
120 | #define EL3_DATA 0x00 | |
121 | #define EL3_TIMER 0x0a | |
122 | #define EL3_CMD 0x0e | |
123 | #define EL3_STATUS 0x0e | |
124 | ||
125 | #define EEPROM_READ 0x0080 | |
126 | ||
127 | #define EL3WINDOW(win_num) mmio_outw(SelectWindow + (win_num), EL_BASE_ADDR + EL3_CMD) | |
128 | ||
129 | /* The top five bits written to EL3_CMD are a command, the lower | |
130 | 11 bits are the parameter, if applicable. */ | |
131 | enum c509cmd { | |
132 | TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11, | |
133 | RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11, | |
134 | TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11, | |
135 | FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11, | |
136 | SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11, | |
137 | SetTxThreshold = 18<<11, SetTxStart = 19<<11, StatsEnable = 21<<11, | |
138 | StatsDisable = 22<<11, StopCoax = 23<<11, | |
139 | }; | |
140 | ||
141 | enum c509status { | |
142 | IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004, | |
143 | TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020, | |
144 | IntReq = 0x0040, StatsFull = 0x0080, CmdBusy = 0x1000 | |
145 | }; | |
146 | ||
147 | /* The SetRxFilter command accepts the following classes: */ | |
148 | enum RxFilter { | |
149 | RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 | |
150 | }; | |
151 | ||
152 | /* Register window 1 offsets, the window used in normal operation. */ | |
153 | #define TX_FIFO 0x00 | |
154 | #define RX_FIFO 0x00 | |
53677ef1 WD |
155 | #define RX_STATUS 0x08 |
156 | #define TX_STATUS 0x0B | |
fe8c2806 WD |
157 | #define TX_FREE 0x0C /* Remaining free bytes in Tx buffer. */ |
158 | ||
159 | ||
160 | /* | |
161 | Read a word from the EEPROM using the regular EEPROM access register. | |
162 | Assume that we are in register window zero. | |
163 | */ | |
164 | static word read_eeprom(dword ioaddr, int index) | |
165 | { | |
166 | int i; | |
167 | outw(EEPROM_READ + index, ioaddr + 0xa); | |
168 | /* Reading the eeprom takes 162 us */ | |
169 | for (i = 1620; i >= 0; i--) | |
170 | if ((inw(ioaddr + 10) & EEPROM_BUSY) == 0) | |
171 | break; | |
172 | return inw(ioaddr + 0xc); | |
173 | } | |
174 | ||
175 | static void el_get_mac_addr( unsigned char *mac_addr ) | |
176 | { | |
177 | int i; | |
178 | union | |
179 | { | |
180 | word w; | |
181 | unsigned char b[2]; | |
182 | } wrd; | |
183 | unsigned char old_window = inw( EL_BASE_ADDR + EL3_STATUS ) >> 13; | |
184 | GO_WINDOW(0); | |
185 | VX_BUSY_WAIT; | |
186 | for (i = 0; i < 3; i++) | |
187 | { | |
188 | wrd.w = read_eeprom(EL_BASE_ADDR, 0xa+i); | |
189 | #ifdef __BIG_ENDIAN | |
190 | mac_addr[2*i] = wrd.b[0]; | |
191 | mac_addr[2*i+1] = wrd.b[1]; | |
192 | #else | |
193 | mac_addr[2*i] = wrd.b[1]; | |
194 | mac_addr[2*i+1] = wrd.b[0]; | |
195 | #endif | |
196 | } | |
197 | GO_WINDOW(old_window); | |
198 | VX_BUSY_WAIT; | |
199 | } | |
200 | ||
201 | ||
202 | #if EL_DEBUG > 1 | |
203 | static void print_packet( byte * buf, int length ) | |
204 | { | |
8bde7f77 WD |
205 | int i; |
206 | int remainder; | |
207 | int lines; | |
208 | ||
209 | PRINTK2("Packet of length %d \n", length ); | |
210 | ||
211 | lines = length / 16; | |
212 | remainder = length % 16; | |
213 | ||
214 | for ( i = 0; i < lines ; i ++ ) { | |
215 | int cur; | |
216 | ||
217 | for ( cur = 0; cur < 8; cur ++ ) { | |
218 | byte a, b; | |
219 | ||
220 | a = *(buf ++ ); | |
221 | b = *(buf ++ ); | |
222 | PRINTK2("%02x%02x ", a, b ); | |
223 | } | |
224 | PRINTK2("\n"); | |
225 | } | |
226 | for ( i = 0; i < remainder/2 ; i++ ) { | |
227 | byte a, b; | |
228 | ||
229 | a = *(buf ++ ); | |
230 | b = *(buf ++ ); | |
231 | PRINTK2("%02x%02x ", a, b ); | |
232 | } | |
233 | PRINTK2("\n"); | |
fe8c2806 WD |
234 | } |
235 | #endif /* EL_DEBUG > 1 */ | |
236 | ||
237 | ||
fe8c2806 WD |
238 | /************************************************************************** |
239 | ETH_RESET - Reset adapter | |
240 | ***************************************************************************/ | |
241 | static void el_reset(bd_t *bd) | |
242 | { | |
243 | /*********************************************************** | |
244 | Reset 3Com 595 card | |
245 | *************************************************************/ | |
246 | /* QUICK HACK | |
247 | * - adjust timing for 3c589 | |
248 | * - enable io for PCMCIA */ | |
249 | outw(0x0004, 0xa0000018); | |
250 | udelay(100); | |
251 | outw(0x0041, 0x28010000); | |
252 | udelay(100); | |
253 | ||
254 | /* issue global reset */ | |
255 | outw(GLOBAL_RESET, BASE + VX_COMMAND); | |
256 | ||
257 | /* must wait for at least 1ms */ | |
258 | udelay(100000000); | |
259 | ||
260 | /* set mac addr */ | |
261 | { | |
d3f87148 | 262 | uchar mac_addr[6]; |
fe8c2806 WD |
263 | int i; |
264 | ||
d3f87148 MF |
265 | if (!eth_getenv_enetaddr("ethaddr", mac_addr)) { |
266 | el_get_mac_addr(mac_addr); | |
267 | eth_setenv_enetaddr("ethaddr", mac_addr); | |
268 | } | |
fe8c2806 WD |
269 | |
270 | GO_WINDOW(2); | |
271 | VX_BUSY_WAIT; | |
272 | ||
273 | printf("3C589 MAC Addr.: "); | |
274 | for (i = 0; i < 6; i++) | |
275 | { | |
276 | printf("%02x", mac_addr[i]); | |
277 | outb(mac_addr[i], BASE + VX_W2_ADDR_0 + i); | |
278 | VX_BUSY_WAIT; | |
279 | } | |
280 | printf("\n\n"); | |
281 | } | |
282 | ||
283 | /* set RX filter */ | |
284 | outw(SET_RX_FILTER | FIL_INDIVIDUAL | FIL_BRDCST, BASE + VX_COMMAND); | |
285 | VX_BUSY_WAIT; | |
286 | ||
287 | ||
288 | /* set irq mask and read_zero */ | |
289 | outw(SET_RD_0_MASK | S_CARD_FAILURE | S_RX_COMPLETE | | |
290 | S_TX_COMPLETE | S_TX_AVAIL, BASE + VX_COMMAND); | |
291 | VX_BUSY_WAIT; | |
292 | ||
293 | outw(SET_INTR_MASK | S_CARD_FAILURE | S_RX_COMPLETE | | |
294 | S_TX_COMPLETE | S_TX_AVAIL, BASE + VX_COMMAND); | |
295 | VX_BUSY_WAIT; | |
296 | ||
297 | /* enable TP Linkbeat */ | |
298 | GO_WINDOW(4); | |
299 | VX_BUSY_WAIT; | |
300 | ||
301 | outw( ENABLE_UTP, BASE + VX_W4_MEDIA_TYPE); | |
302 | VX_BUSY_WAIT; | |
303 | ||
304 | ||
305 | /* | |
306 | * Attempt to get rid of any stray interrupts that occured during | |
307 | * configuration. On the i386 this isn't possible because one may | |
308 | * already be queued. However, a single stray interrupt is | |
309 | * unimportant. | |
310 | */ | |
311 | ||
312 | outw(ACK_INTR | 0xff, BASE + VX_COMMAND); | |
313 | VX_BUSY_WAIT; | |
314 | ||
315 | /* enable TX and RX */ | |
316 | outw( RX_ENABLE, BASE + VX_COMMAND ); | |
317 | VX_BUSY_WAIT; | |
318 | ||
319 | outw( TX_ENABLE, BASE + VX_COMMAND ); | |
320 | VX_BUSY_WAIT; | |
321 | ||
322 | ||
323 | /* print the diag. regs. */ | |
324 | PRINTK2("Diag. Regs\n"); | |
325 | PRINTK2("--> MEDIA_TYPE: %04x\n", inw(BASE + VX_W4_MEDIA_TYPE)); | |
326 | PRINTK2("--> NET_DIAG: %04x\n", inw(BASE + VX_W4_NET_DIAG)); | |
327 | PRINTK2("--> FIFO_DIAG: %04x\n", inw(BASE + VX_W4_FIFO_DIAG)); | |
328 | PRINTK2("--> CTRLR_STATUS: %04x\n", inw(BASE + VX_W4_CTRLR_STATUS)); | |
329 | PRINTK2("\n\n"); | |
330 | ||
331 | /* enter working mode */ | |
332 | GO_WINDOW(1); | |
333 | VX_BUSY_WAIT; | |
334 | ||
335 | /* wait for another 1ms */ | |
336 | udelay(100000000); | |
337 | } | |
338 | ||
339 | ||
340 | /*----------------------------------------------------------------- | |
341 | . | |
342 | . The driver can be entered at any of the following entry points. | |
343 | . | |
344 | .------------------------------------------------------------------ */ | |
345 | ||
346 | extern int eth_init(bd_t *bd); | |
347 | extern void eth_halt(void); | |
348 | extern int eth_rx(void); | |
349 | extern int eth_send(volatile void *packet, int length); | |
350 | ||
351 | ||
352 | /* | |
353 | ------------------------------------------------------------ | |
354 | . | |
355 | . Internal routines | |
356 | . | |
357 | ------------------------------------------------------------ | |
358 | */ | |
359 | ||
360 | int eth_init(bd_t *bd) | |
361 | { | |
362 | el_reset(bd); | |
363 | return 0; | |
364 | } | |
365 | ||
366 | void eth_halt() { | |
367 | return; | |
368 | } | |
369 | ||
370 | #define EDEBUG 1 | |
371 | ||
372 | ||
373 | /************************************************************************** | |
374 | ETH_POLL - Wait for a frame | |
375 | ***************************************************************************/ | |
376 | ||
377 | int eth_rx() | |
378 | { | |
379 | word status, rx_status, packet_size; | |
380 | ||
381 | VX_BUSY_WAIT; | |
382 | ||
383 | status = inw( BASE + VX_STATUS ); | |
384 | ||
385 | if ( (status & S_RX_COMPLETE) == 0 ) return 0; /* nothing to do */ | |
386 | ||
387 | /* Packet waiting -> check RX_STATUS */ | |
388 | rx_status = inw( BASE + VX_W1_RX_STATUS ); | |
389 | ||
390 | if ( rx_status & ERR_RX ) | |
391 | { | |
392 | /* error in packet -> discard */ | |
393 | PRINTK("[ERROR] Invalid packet -> discarding\n"); | |
394 | PRINTK("-- error code 0x%02x\n", rx_status & ERR_MASK); | |
395 | PRINTK("-- rx bytes 0x%04d\n", rx_status & ((1<<11) - 1)); | |
396 | PRINTK("[ERROR] Invalid packet -> discarding\n"); | |
397 | outw( RX_DISCARD_TOP_PACK, BASE + VX_COMMAND ); | |
398 | return 0; | |
399 | } | |
400 | ||
401 | /* correct pack. waiting in fifo */ | |
402 | packet_size = rx_status & RX_BYTES_MASK; | |
403 | ||
404 | PRINTK("Correct packet waiting in fifo, size: %d\n", packet_size); | |
405 | ||
406 | { | |
407 | volatile word *packet_start = (word *)(BASE + VX_W1_RX_PIO_RD_1); | |
408 | word *RcvBuffer = (word *)(NetRxPackets[0]); | |
409 | int wcount = 0; | |
410 | ||
411 | for (wcount = 0; wcount < (packet_size >> 1); wcount++) | |
412 | { | |
413 | *RcvBuffer++ = *(packet_start); | |
414 | } | |
415 | ||
416 | /* handle odd packets */ | |
417 | if ( packet_size & 1 ) | |
418 | { | |
419 | *RcvBuffer++ = *(packet_start); | |
420 | } | |
421 | } | |
422 | ||
423 | /* fifo should now be empty (besides the padding bytes) */ | |
424 | if ( ((*((word *)(BASE + VX_W1_RX_STATUS))) & RX_BYTES_MASK) > 3 ) | |
425 | { | |
426 | PRINTK("[ERROR] Fifo not empty after packet read (remaining pkts: %d)\n", | |
427 | (((*(word *)(BASE + VX_W1_RX_STATUS))) & RX_BYTES_MASK)); | |
428 | } | |
429 | ||
430 | /* discard packet */ | |
431 | *((word *)(BASE + VX_COMMAND)) = RX_DISCARD_TOP_PACK; | |
432 | ||
433 | /* Pass Packets to upper Layer */ | |
434 | NetReceive(NetRxPackets[0], packet_size); | |
435 | return packet_size; | |
436 | } | |
437 | ||
438 | ||
fe8c2806 WD |
439 | /************************************************************************** |
440 | ETH_TRANSMIT - Transmit a frame | |
441 | ***************************************************************************/ | |
442 | static char padmap[] = { | |
443 | 0, 3, 2, 1}; | |
444 | ||
445 | ||
446 | int eth_send(volatile void *packet, int length) { | |
447 | int pad; | |
448 | int status; | |
449 | volatile word *buf = (word *)packet; | |
450 | int dummy = 0; | |
451 | ||
452 | /* padding stuff */ | |
453 | pad = padmap[length & 3]; | |
454 | ||
455 | PRINTK("eth_send(), length: %d\n", length); | |
456 | /* drop acknowledgements */ | |
457 | while(( status=inb(EL_BASE_ADDR + VX_W1_TX_STATUS) )& TXS_COMPLETE ) { | |
458 | if(status & (TXS_UNDERRUN|TXS_MAX_COLLISION|TXS_STATUS_OVERFLOW)) { | |
459 | outw(TX_RESET, EL_BASE_ADDR + VX_COMMAND); | |
460 | outw(TX_ENABLE, EL_BASE_ADDR + VX_COMMAND); | |
461 | PRINTK("Bad status, resetting and reenabling transmitter\n"); | |
462 | } | |
463 | ||
464 | outb(0x0, EL_BASE_ADDR + VX_W1_TX_STATUS); | |
465 | } | |
466 | ||
467 | ||
468 | while (inw(EL_BASE_ADDR + VX_W1_FREE_TX) < length + pad + 4) { | |
469 | /* no room in FIFO */ | |
470 | if (dummy == 0) | |
471 | { | |
472 | PRINTK("No room in FIFO, waiting...\n"); | |
473 | dummy++; | |
474 | } | |
475 | ||
476 | } | |
477 | ||
478 | PRINTK(" ---> FIFO ready\n"); | |
479 | ||
480 | ||
481 | outw(length, EL_BASE_ADDR + VX_W1_TX_PIO_WR_1); | |
482 | ||
483 | /* Second dword meaningless */ | |
484 | outw(0x0, EL_BASE_ADDR + VX_W1_TX_PIO_WR_1); | |
485 | ||
486 | #if EL_DEBUG > 1 | |
487 | print_packet((byte *)buf, length); | |
488 | #endif | |
489 | ||
490 | /* write packet */ | |
491 | { | |
492 | unsigned int i, totw; | |
493 | ||
494 | totw = ((length + 1) >> 1); | |
495 | PRINTK("Buffer: (totw = %d)\n", totw); | |
496 | for (i = 0; i < totw; i++) { | |
497 | outw( *(buf+i), EL_BASE_ADDR + VX_W1_TX_PIO_WR_1); | |
498 | udelay(10); | |
499 | } | |
500 | if(totw & 1) | |
501 | { /* pad to double word length */ | |
502 | outw( 0, EL_BASE_ADDR + VX_W1_TX_PIO_WR_1); | |
503 | udelay(10); | |
504 | } | |
505 | PRINTK("\n\n"); | |
506 | } | |
507 | ||
8bde7f77 | 508 | /* wait for Tx complete */ |
fe8c2806 | 509 | PRINTK("Waiting for Tx to complete...\n"); |
8bde7f77 | 510 | while(((status = inw(EL_BASE_ADDR + VX_STATUS)) & S_COMMAND_IN_PROGRESS) != 0) |
fe8c2806 WD |
511 | { |
512 | udelay(10); | |
513 | } | |
514 | PRINTK(" ---> Tx completed, status = 0x%04x\n", status); | |
515 | ||
516 | return length; | |
517 | } |