1 /* cs89x0.c: A Crystal Semiconductor CS89[02]0 driver for etherboot. */
3 Permission is granted to distribute the enclosed cs89x0.[ch] driver
4 only in conjunction with the Etherboot package. The code is
5 ordinarily distributed under the GPL.
7 Russ Nelson, January 2000
11 Thu Dec 6 22:40:00 1996 Markus Gutschke <gutschk@math.uni-muenster.de>
13 * disabled all "advanced" features; this should make the code more reliable
15 * reorganized the reset function
17 * always reset the address port, so that autoprobing will continue working
19 * some cosmetic changes
23 Thu Dec 5 21:00:00 1996 Markus Gutschke <gutschk@math.uni-muenster.de>
25 * tested the code against a CS8900 card
27 * lots of minor bug fixes and adjustments
29 * this is the first release, that actually works! it still requires some
30 changes in order to be more tolerant to different environments
34 Fri Nov 22 23:00:00 1996 Markus Gutschke <gutschk@math.uni-muenster.de>
36 * read the manuals for the CS89x0 chipsets and took note of all the
37 changes that will be neccessary in order to adapt Russel Nelson's code
38 to the requirements of a BOOT-Prom
42 Thu Nov 19 22:00:00 1996 Markus Gutschke <gutschk@math.uni-muenster.de>
44 * Synched with Russel Nelson's current code (v1.00)
48 Thu Nov 12 18:00:00 1996 Markus Gutschke <gutschk@math.uni-muenster.de>
50 * Cleaned up some of the code and tried to optimize the code size.
54 Sun Nov 10 16:30:00 1996 Markus Gutschke <gutschk@math.uni-muenster.de>
56 * First experimental release. This code compiles fine, but I
57 have no way of testing whether it actually works.
59 * I did not (yet) bother to make the code 16bit aware, so for
60 the time being, it will only work for Etherboot/32.
66 #include "etherboot.h"
71 static unsigned short eth_nic_base
;
72 static unsigned long eth_mem_start
;
73 static unsigned short eth_irq
;
74 static unsigned short eth_cs_type
; /* one of: CS8900, CS8920, CS8920M */
75 static unsigned short eth_auto_neg_cnf
;
76 static unsigned short eth_adapter_cnf
;
77 static unsigned short eth_linectl
;
79 /*************************************************************************
80 CS89x0 - specific routines
81 **************************************************************************/
83 static inline int readreg(int portno
)
85 outw(portno
, eth_nic_base
+ ADD_PORT
);
86 return inw(eth_nic_base
+ DATA_PORT
);
89 static inline void writereg(int portno
, int value
)
91 outw(portno
, eth_nic_base
+ ADD_PORT
);
92 outw(value
, eth_nic_base
+ DATA_PORT
);
96 /*************************************************************************
98 **************************************************************************/
100 static int wait_eeprom_ready(void)
102 unsigned long tmo
= currticks() + 4*TICKS_PER_SEC
;
104 /* check to see if the EEPROM is ready, a timeout is used -
105 just in case EEPROM is ready when SI_BUSY in the
106 PP_SelfST is clear */
107 while(readreg(PP_SelfST
) & SI_BUSY
) {
108 if (currticks() >= tmo
)
113 static int get_eeprom_data(int off
, int len
, unsigned short *buffer
)
118 printf("\ncs: EEPROM data from %hX for %hX:",off
,len
);
120 for (i
= 0; i
< len
; i
++) {
121 if (wait_eeprom_ready() < 0)
123 /* Now send the EEPROM read command and EEPROM location
125 writereg(PP_EECMD
, (off
+ i
) | EEPROM_READ_CMD
);
126 if (wait_eeprom_ready() < 0)
128 buffer
[i
] = readreg(PP_EEData
);
132 printf("%hX ", buffer
[i
]);
142 static int get_eeprom_chksum(int off
, int len
, unsigned short *buffer
)
147 for (i
= 0; i
< len
; i
++)
155 /*************************************************************************
156 Activate all of the available media and probe for network
157 **************************************************************************/
159 static void clrline(void)
164 for (i
= 79; i
--; ) putchar(' ');
169 static void control_dc_dc(int on_not_off
)
171 unsigned int selfcontrol
;
172 unsigned long tmo
= currticks() + TICKS_PER_SEC
;
174 /* control the DC to DC convertor in the SelfControl register. */
175 selfcontrol
= HCB1_ENBL
; /* Enable the HCB1 bit as an output */
176 if (((eth_adapter_cnf
& A_CNF_DC_DC_POLARITY
) != 0) ^ on_not_off
)
179 selfcontrol
&= ~HCB1
;
180 writereg(PP_SelfCTL
, selfcontrol
);
182 /* Wait for the DC/DC converter to power up - 1000ms */
183 while (currticks() < tmo
);
188 static int detect_tp(void)
192 /* Turn on the chip auto detection of 10BT/ AUI */
194 clrline(); printf("attempting %s:","TP");
196 /* If connected to another full duplex capable 10-Base-T card
197 the link pulses seem to be lost when the auto detect bit in
198 the LineCTL is set. To overcome this the auto detect bit
199 will be cleared whilst testing the 10-Base-T interface.
200 This would not be necessary for the sparrow chip but is
201 simpler to do it anyway. */
202 writereg(PP_LineCTL
, eth_linectl
&~ AUI_ONLY
);
205 /* Delay for the hardware to work out if the TP cable is
207 for (tmo
= currticks() + 4; currticks() < tmo
; );
209 if ((readreg(PP_LineST
) & LINK_OK
) == 0)
212 if (eth_cs_type
!= CS8900
) {
214 writereg(PP_AutoNegCTL
, eth_auto_neg_cnf
& AUTO_NEG_MASK
);
216 if ((eth_auto_neg_cnf
& AUTO_NEG_BITS
) == AUTO_NEG_ENABLE
) {
217 printf(" negotiating duplex... ");
218 while (readreg(PP_AutoNegST
) & AUTO_NEG_BUSY
) {
219 if (currticks() - tmo
> 40*TICKS_PER_SEC
) {
225 if (readreg(PP_AutoNegST
) & FDX_ACTIVE
)
226 printf("using full duplex");
228 printf("using half duplex");
231 return A_CNF_MEDIA_10B_T
;
234 /* send a test packet - return true if carrier bits are ok */
235 static int send_test_pkt(struct nic
*nic
)
237 static unsigned char testpacket
[] = { 0,0,0,0,0,0, 0,0,0,0,0,0,
238 0, 46, /*A 46 in network order */
239 0, 0, /*DSAP=0 & SSAP=0 fields */
240 0xf3,0 /*Control (Test Req+P bit set)*/ };
243 writereg(PP_LineCTL
, readreg(PP_LineCTL
) | SERIAL_TX_ON
);
245 memcpy(testpacket
, nic
->node_addr
, ETH_ALEN
);
246 memcpy(testpacket
+ETH_ALEN
, nic
->node_addr
, ETH_ALEN
);
248 outw(TX_AFTER_ALL
, eth_nic_base
+ TX_CMD_PORT
);
249 outw(ETH_ZLEN
, eth_nic_base
+ TX_LEN_PORT
);
251 /* Test to see if the chip has allocated memory for the packet */
252 for (tmo
= currticks() + 2;
253 (readreg(PP_BusST
) & READY_FOR_TX_NOW
) == 0; )
254 if (currticks() >= tmo
)
257 /* Write the contents of the packet */
258 outsw(eth_nic_base
+ TX_FRAME_PORT
, testpacket
,
261 printf(" sending test packet ");
262 /* wait a couple of timer ticks for packet to be received */
263 for (tmo
= currticks() + 2; currticks() < tmo
; );
265 if ((readreg(PP_TxEvent
) & TX_SEND_OK_BITS
) == TX_OK
) {
274 static int detect_aui(struct nic
*nic
)
276 clrline(); printf("attempting %s:","AUI");
279 writereg(PP_LineCTL
, (eth_linectl
& ~AUTO_AUI_10BASET
) | AUI_ONLY
);
281 if (send_test_pkt(nic
)) {
282 return A_CNF_MEDIA_AUI
; }
287 static int detect_bnc(struct nic
*nic
)
289 clrline(); printf("attempting %s:","BNC");
292 writereg(PP_LineCTL
, (eth_linectl
& ~AUTO_AUI_10BASET
) | AUI_ONLY
);
294 if (send_test_pkt(nic
)) {
295 return A_CNF_MEDIA_10B_2
; }
300 /**************************************************************************
301 ETH_RESET - Reset adapter
302 ***************************************************************************/
304 static void cs89x0_reset(struct nic
*nic
)
307 unsigned long reset_tmo
;
309 writereg(PP_SelfCTL
, readreg(PP_SelfCTL
) | POWER_ON_RESET
);
311 /* wait for two ticks; that is 2*55ms */
312 for (reset_tmo
= currticks() + 2; currticks() < reset_tmo
; );
314 if (eth_cs_type
!= CS8900
) {
315 /* Hardware problem requires PNP registers to be reconfigured
317 if (eth_irq
!= 0xFFFF) {
318 outw(PP_CS8920_ISAINT
, eth_nic_base
+ ADD_PORT
);
319 outb(eth_irq
, eth_nic_base
+ DATA_PORT
);
320 outb(0, eth_nic_base
+ DATA_PORT
+ 1); }
323 outw(PP_CS8920_ISAMemB
, eth_nic_base
+ ADD_PORT
);
324 outb((eth_mem_start
>> 8) & 0xff, eth_nic_base
+ DATA_PORT
);
325 outb((eth_mem_start
>> 24) & 0xff, eth_nic_base
+ DATA_PORT
+ 1); } }
327 /* Wait until the chip is reset */
328 for (reset_tmo
= currticks() + 2;
329 (readreg(PP_SelfST
) & INIT_DONE
) == 0 &&
330 currticks() < reset_tmo
; );
332 /* disable interrupts and memory accesses */
333 writereg(PP_BusCTL
, 0);
335 /* set the ethernet address */
336 for (i
=0; i
< ETH_ALEN
/2; i
++)
338 nic
->node_addr
[i
*2] |
339 (nic
->node_addr
[i
*2+1] << 8));
341 /* receive only error free packets addressed to this card */
342 writereg(PP_RxCTL
, DEF_RX_ACCEPT
);
344 /* do not generate any interrupts on receive operations */
345 writereg(PP_RxCFG
, 0);
347 /* do not generate any interrupts on transmit operations */
348 writereg(PP_TxCFG
, 0);
350 /* do not generate any interrupts on buffer operations */
351 writereg(PP_BufCFG
, 0);
353 /* reset address port, so that autoprobing will keep working */
354 outw(PP_ChipID
, eth_nic_base
+ ADD_PORT
);
359 /**************************************************************************
360 ETH_TRANSMIT - Transmit a frame
361 ***************************************************************************/
363 static void cs89x0_transmit(
365 const char *d
, /* Destination */
366 unsigned int t
, /* Type */
367 unsigned int s
, /* size */
368 const char *p
) /* Packet */
373 /* does this size have to be rounded??? please,
374 somebody have a look in the specs */
375 if ((sr
= ((s
+ ETH_HLEN
+ 1)&~1)) < ETH_ZLEN
)
379 /* initiate a transmit sequence */
380 outw(TX_AFTER_ALL
, eth_nic_base
+ TX_CMD_PORT
);
381 outw(sr
, eth_nic_base
+ TX_LEN_PORT
);
383 /* Test to see if the chip has allocated memory for the packet */
384 if ((readreg(PP_BusST
) & READY_FOR_TX_NOW
) == 0) {
385 /* Oops... this should not happen! */
386 printf("cs: unable to send packet; retrying...\n");
387 for (tmo
= currticks() + 5*TICKS_PER_SEC
; currticks() < tmo
; );
391 /* Write the contents of the packet */
392 outsw(eth_nic_base
+ TX_FRAME_PORT
, d
, ETH_ALEN
/2);
393 outsw(eth_nic_base
+ TX_FRAME_PORT
, nic
->node_addr
,
395 outw(((t
>> 8)&0xFF)|(t
<< 8), eth_nic_base
+ TX_FRAME_PORT
);
396 outsw(eth_nic_base
+ TX_FRAME_PORT
, p
, (s
+1)/2);
397 for (sr
= sr
/2 - (s
+1)/2 - ETH_ALEN
- 1; sr
-- > 0;
398 outw(0, eth_nic_base
+ TX_FRAME_PORT
));
400 /* wait for transfer to succeed */
401 for (tmo
= currticks()+5*TICKS_PER_SEC
;
402 (s
= readreg(PP_TxEvent
)&~0x1F) == 0 && currticks() < tmo
;)
404 if ((s
& TX_SEND_OK_BITS
) != TX_OK
) {
405 printf("\ntransmission error %#hX\n", s
);
411 /**************************************************************************
412 ETH_POLL - Wait for a frame
413 ***************************************************************************/
415 static int cs89x0_poll(struct nic
*nic
)
419 status
= readreg(PP_RxEvent
);
421 if ((status
& RX_OK
) == 0)
424 status
= inw(eth_nic_base
+ RX_FRAME_PORT
);
425 nic
->packetlen
= inw(eth_nic_base
+ RX_FRAME_PORT
);
426 insw(eth_nic_base
+ RX_FRAME_PORT
, nic
->packet
, nic
->packetlen
>> 1);
427 if (nic
->packetlen
& 1)
428 nic
->packet
[nic
->packetlen
-1] = inw(eth_nic_base
+ RX_FRAME_PORT
);
432 static void cs89x0_disable(struct nic
*nic
)
437 /**************************************************************************
438 ETH_PROBE - Look for an adapter
439 ***************************************************************************/
441 struct nic
*cs89x0_probe(struct nic
*nic
, unsigned short *probe_addrs
)
443 static const unsigned int netcard_portlist
[] = {
446 #else /* use "conservative" default values for autoprobing */
447 0x300,0x320,0x340,0x200,0x220,0x240,
448 0x260,0x280,0x2a0,0x2c0,0x2e0,
449 /* if that did not work, then be more aggressive */
450 0x301,0x321,0x341,0x201,0x221,0x241,
451 0x261,0x281,0x2a1,0x2c1,0x2e1,
456 unsigned rev_type
= 0, ioaddr
, ioidx
, isa_cnf
, cs_revision
;
457 unsigned short eeprom_buff
[CHKSUM_LEN
];
460 for (ioidx
= 0; (ioaddr
=netcard_portlist
[ioidx
++]) != 0; ) {
461 /* if they give us an odd I/O address, then do ONE write to
462 the address port, to get it back to address zero, where we
463 expect to find the EISA signature word. */
466 if ((inw(ioaddr
+ ADD_PORT
) & ADD_MASK
) != ADD_SIG
)
468 outw(PP_ChipID
, ioaddr
+ ADD_PORT
);
471 if (inw(ioaddr
+ DATA_PORT
) != CHIP_EISA_ID_SIG
)
473 eth_nic_base
= ioaddr
;
475 /* get the chip type */
476 rev_type
= readreg(PRODUCT_ID_ADD
);
477 eth_cs_type
= rev_type
&~ REVISON_BITS
;
478 cs_revision
= ((rev_type
& REVISON_BITS
) >> 8) + 'A';
480 printf("\ncs: cs89%c0%s rev %c, base %#hX",
481 eth_cs_type
==CS8900
?'0':'2',
482 eth_cs_type
==CS8920M
?"M":"",
486 /* First check to see if an EEPROM is attached*/
487 if ((readreg(PP_SelfST
) & EEPROM_PRESENT
) == 0) {
488 printf("\ncs: no EEPROM...\n");
489 outw(PP_ChipID
, eth_nic_base
+ ADD_PORT
);
491 else if (get_eeprom_data(START_EEPROM_DATA
,CHKSUM_LEN
,
493 printf("\ncs: EEPROM read failed...\n");
494 outw(PP_ChipID
, eth_nic_base
+ ADD_PORT
);
496 else if (get_eeprom_chksum(START_EEPROM_DATA
,CHKSUM_LEN
,
498 printf("\ncs: EEPROM checksum bad...\n");
499 outw(PP_ChipID
, eth_nic_base
+ ADD_PORT
);
502 /* get transmission control word but keep the
503 autonegotiation bits */
504 eth_auto_neg_cnf
= eeprom_buff
[AUTO_NEG_CNF_OFFSET
/2];
505 /* Store adapter configuration */
506 eth_adapter_cnf
= eeprom_buff
[ADAPTER_CNF_OFFSET
/2];
507 /* Store ISA configuration */
508 isa_cnf
= eeprom_buff
[ISA_CNF_OFFSET
/2];
510 /* store the initial memory base address */
511 eth_mem_start
= eeprom_buff
[PACKET_PAGE_OFFSET
/2] << 8;
513 printf("%s%s%s, addr ",
514 (eth_adapter_cnf
& A_CNF_10B_T
)?", RJ-45":"",
515 (eth_adapter_cnf
& A_CNF_AUI
)?", AUI":"",
516 (eth_adapter_cnf
& A_CNF_10B_2
)?", BNC":"");
518 /* If this is a CS8900 then no pnp soft */
519 if (eth_cs_type
!= CS8900
&&
520 /* Check if the ISA IRQ has been set */
521 (i
= readreg(PP_CS8920_ISAINT
) & 0xff,
522 (i
!= 0 && i
< CS8920_NO_INTS
)))
525 i
= isa_cnf
& INT_NO_MASK
;
526 if (eth_cs_type
== CS8900
) {
527 /* the table that follows is dependent
528 upon how you wired up your cs8900
529 in your system. The table is the
530 same as the cs8900 engineering demo
531 board. irq_map also depends on the
532 contents of the table. Also see
533 write_irq, which is the reverse
534 mapping of the table below. */
535 if (i
< 4) i
= "\012\013\014\005"[i
];
536 else printf("\ncs: BUG: isa_config is %d\n", i
); }
539 /* Retrieve and print the ethernet address. */
540 for (i
=0; i
<ETH_ALEN
; i
++) {
541 nic
->node_addr
[i
] = ((unsigned char *)eeprom_buff
)[i
];
543 printf("%!\n", nic
->node_addr
);
545 /* Set the LineCTL quintuplet based on adapter
546 configuration read from EEPROM */
547 if ((eth_adapter_cnf
& A_CNF_EXTND_10B_2
) &&
548 (eth_adapter_cnf
& A_CNF_LOW_RX_SQUELCH
))
549 eth_linectl
= LOW_RX_SQUELCH
;
553 /* check to make sure that they have the "right"
554 hardware available */
555 switch(eth_adapter_cnf
& A_CNF_MEDIA_TYPE
) {
556 case A_CNF_MEDIA_10B_T
: result
= eth_adapter_cnf
& A_CNF_10B_T
;
558 case A_CNF_MEDIA_AUI
: result
= eth_adapter_cnf
& A_CNF_AUI
;
560 case A_CNF_MEDIA_10B_2
: result
= eth_adapter_cnf
& A_CNF_10B_2
;
562 default: result
= eth_adapter_cnf
& (A_CNF_10B_T
| A_CNF_AUI
|
566 printf("cs: EEPROM is configured for unavailable media\n");
568 writereg(PP_LineCTL
, readreg(PP_LineCTL
) &
569 ~(SERIAL_TX_ON
| SERIAL_RX_ON
));
570 outw(PP_ChipID
, eth_nic_base
+ ADD_PORT
);
574 /* Initialize the card for probing of the attached media */
577 /* set the hardware to the configured choice */
578 switch(eth_adapter_cnf
& A_CNF_MEDIA_TYPE
) {
579 case A_CNF_MEDIA_10B_T
:
580 result
= detect_tp();
583 printf("10Base-T (RJ-45%s",
584 ") has no cable\n"); }
585 /* check "ignore missing media" bit */
586 if (eth_auto_neg_cnf
& IMM_BIT
)
587 /* Yes! I don't care if I see a link pulse */
588 result
= A_CNF_MEDIA_10B_T
;
590 case A_CNF_MEDIA_AUI
:
591 result
= detect_aui(nic
);
594 printf("10Base-5 (AUI%s",
595 ") has no cable\n"); }
596 /* check "ignore missing media" bit */
597 if (eth_auto_neg_cnf
& IMM_BIT
)
598 /* Yes! I don't care if I see a carrrier */
599 result
= A_CNF_MEDIA_AUI
;
601 case A_CNF_MEDIA_10B_2
:
602 result
= detect_bnc(nic
);
605 printf("10Base-2 (BNC%s",
606 ") has no cable\n"); }
607 /* check "ignore missing media" bit */
608 if (eth_auto_neg_cnf
& IMM_BIT
)
609 /* Yes! I don't care if I can xmit a packet */
610 result
= A_CNF_MEDIA_10B_2
;
612 case A_CNF_MEDIA_AUTO
:
613 writereg(PP_LineCTL
, eth_linectl
| AUTO_AUI_10BASET
);
614 if (eth_adapter_cnf
& A_CNF_10B_T
)
615 if ((result
= detect_tp()) != 0)
617 if (eth_adapter_cnf
& A_CNF_AUI
)
618 if ((result
= detect_aui(nic
)) != 0)
620 if (eth_adapter_cnf
& A_CNF_10B_2
)
621 if ((result
= detect_bnc(nic
)) != 0)
623 clrline(); printf("no media detected\n");
628 case 0: printf("no network cable attached to configured media\n");
630 case A_CNF_MEDIA_10B_T
: printf("using 10Base-T (RJ-45)\n");
632 case A_CNF_MEDIA_AUI
: printf("using 10Base-5 (AUI)\n");
634 case A_CNF_MEDIA_10B_2
: printf("using 10Base-2 (BNC)\n");
638 /* Turn on both receive and transmit operations */
639 writereg(PP_LineCTL
, readreg(PP_LineCTL
) | SERIAL_RX_ON
|
647 nic
->reset
= cs89x0_reset
;
648 nic
->poll
= cs89x0_poll
;
649 nic
->transmit
= cs89x0_transmit
;
650 nic
->disable
= cs89x0_disable
;