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