2 #error multicast support is not yet implemented
6 Per an email message from Russ Nelson <nelson@crynwr.com> on
7 18 March 2008 this file is now licensed under GPL Version 2.
9 From: Russ Nelson <nelson@crynwr.com>
10 Date: Tue, 18 Mar 2008 12:42:00 -0400
11 Subject: Re: [Etherboot-developers] cs89x0 driver in etherboot
13 As copyright holder, if I say it doesn't conflict with the GPL,
14 then it doesn't conflict with the GPL.
16 However, there's no point in causing people's brains to overheat,
17 so yes, I grant permission for the code to be relicensed under the
18 GPLv2. Please make sure that this change in licensing makes its
23 FILE_LICENCE ( GPL2_ONLY );
25 /* cs89x0.c: A Crystal Semiconductor CS89[02]0 driver for etherboot. */
27 Permission is granted to distribute the enclosed cs89x0.[ch] driver
28 only in conjunction with the Etherboot package. The code is
29 ordinarily distributed under the GPL.
31 Russ Nelson, January 2000
35 Thu Dec 6 22:40:00 1996 Markus Gutschke <gutschk@math.uni-muenster.de>
37 * disabled all "advanced" features; this should make the code more reliable
39 * reorganized the reset function
41 * always reset the address port, so that autoprobing will continue working
43 * some cosmetic changes
47 Thu Dec 5 21:00:00 1996 Markus Gutschke <gutschk@math.uni-muenster.de>
49 * tested the code against a CS8900 card
51 * lots of minor bug fixes and adjustments
53 * this is the first release, that actually works! it still requires some
54 changes in order to be more tolerant to different environments
58 Fri Nov 22 23:00:00 1996 Markus Gutschke <gutschk@math.uni-muenster.de>
60 * read the manuals for the CS89x0 chipsets and took note of all the
61 changes that will be necessary in order to adapt Russel Nelson's code
62 to the requirements of a BOOT-Prom
66 Thu Nov 19 22:00:00 1996 Markus Gutschke <gutschk@math.uni-muenster.de>
68 * Synched with Russel Nelson's current code (v1.00)
72 Thu Nov 12 18:00:00 1996 Markus Gutschke <gutschk@math.uni-muenster.de>
74 * Cleaned up some of the code and tried to optimize the code size.
78 Sun Nov 10 16:30:00 1996 Markus Gutschke <gutschk@math.uni-muenster.de>
80 * First experimental release. This code compiles fine, but I
81 have no way of testing whether it actually works.
83 * I did not (yet) bother to make the code 16bit aware, so for
84 the time being, it will only work for Etherboot/32.
91 #include <ipxe/ethernet.h>
92 #include "etherboot.h"
97 static unsigned short eth_nic_base;
98 static unsigned long eth_mem_start;
99 static unsigned short eth_irqno;
100 static unsigned short eth_cs_type; /* one of: CS8900, CS8920, CS8920M */
101 static unsigned short eth_auto_neg_cnf;
102 static unsigned short eth_adapter_cnf;
103 static unsigned short eth_linectl;
105 /*************************************************************************
106 CS89x0 - specific routines
107 **************************************************************************/
109 static inline int readreg(int portno)
111 outw(portno, eth_nic_base + ADD_PORT);
112 return inw(eth_nic_base + DATA_PORT);
115 static inline void writereg(int portno, int value)
117 outw(portno, eth_nic_base + ADD_PORT);
118 outw(value, eth_nic_base + DATA_PORT);
122 /*************************************************************************
124 **************************************************************************/
126 static int wait_eeprom_ready(void)
128 unsigned long tmo = currticks() + 4*TICKS_PER_SEC;
130 /* check to see if the EEPROM is ready, a timeout is used -
131 just in case EEPROM is ready when SI_BUSY in the
132 PP_SelfST is clear */
133 while(readreg(PP_SelfST) & SI_BUSY) {
134 if (currticks() >= tmo)
139 static int get_eeprom_data(int off, int len, unsigned short *buffer)
144 printf("\ncs: EEPROM data from %hX for %hX:",off,len);
146 for (i = 0; i < len; i++) {
147 if (wait_eeprom_ready() < 0)
149 /* Now send the EEPROM read command and EEPROM location
151 writereg(PP_EECMD, (off + i) | EEPROM_READ_CMD);
152 if (wait_eeprom_ready() < 0)
154 buffer[i] = readreg(PP_EEData);
158 printf("%hX ", buffer[i]);
168 static int get_eeprom_chksum(int off __unused, int len, unsigned short *buffer)
173 for (i = 0; i < len; i++)
181 /*************************************************************************
182 Activate all of the available media and probe for network
183 **************************************************************************/
185 static void clrline(void)
190 for (i = 79; i--; ) putchar(' ');
195 static void control_dc_dc(int on_not_off)
197 unsigned int selfcontrol;
198 unsigned long tmo = currticks() + TICKS_PER_SEC;
200 /* control the DC to DC convertor in the SelfControl register. */
201 selfcontrol = HCB1_ENBL; /* Enable the HCB1 bit as an output */
202 if (((eth_adapter_cnf & A_CNF_DC_DC_POLARITY) != 0) ^ on_not_off)
205 selfcontrol &= ~HCB1;
206 writereg(PP_SelfCTL, selfcontrol);
208 /* Wait for the DC/DC converter to power up - 1000ms */
209 while (currticks() < tmo);
214 static int detect_tp(void)
218 /* Turn on the chip auto detection of 10BT/ AUI */
220 clrline(); printf("attempting %s:","TP");
222 /* If connected to another full duplex capable 10-Base-T card
223 the link pulses seem to be lost when the auto detect bit in
224 the LineCTL is set. To overcome this the auto detect bit
225 will be cleared whilst testing the 10-Base-T interface.
226 This would not be necessary for the sparrow chip but is
227 simpler to do it anyway. */
228 writereg(PP_LineCTL, eth_linectl &~ AUI_ONLY);
231 /* Delay for the hardware to work out if the TP cable is
233 for (tmo = currticks() + 4; currticks() < tmo; );
235 if ((readreg(PP_LineST) & LINK_OK) == 0)
238 if (eth_cs_type != CS8900) {
240 writereg(PP_AutoNegCTL, eth_auto_neg_cnf & AUTO_NEG_MASK);
242 if ((eth_auto_neg_cnf & AUTO_NEG_BITS) == AUTO_NEG_ENABLE) {
243 printf(" negotiating duplex... ");
244 while (readreg(PP_AutoNegST) & AUTO_NEG_BUSY) {
245 if (currticks() - tmo > 40*TICKS_PER_SEC) {
251 if (readreg(PP_AutoNegST) & FDX_ACTIVE)
252 printf("using full duplex");
254 printf("using half duplex");
257 return A_CNF_MEDIA_10B_T;
260 /* send a test packet - return true if carrier bits are ok */
261 static int send_test_pkt(struct nic *nic)
263 static unsigned char testpacket[] = { 0,0,0,0,0,0, 0,0,0,0,0,0,
264 0, 46, /*A 46 in network order */
265 0, 0, /*DSAP=0 & SSAP=0 fields */
266 0xf3,0 /*Control (Test Req+P bit set)*/ };
269 writereg(PP_LineCTL, readreg(PP_LineCTL) | SERIAL_TX_ON);
271 memcpy(testpacket, nic->node_addr, ETH_ALEN);
272 memcpy(testpacket+ETH_ALEN, nic->node_addr, ETH_ALEN);
274 outw(TX_AFTER_ALL, eth_nic_base + TX_CMD_PORT);
275 outw(ETH_ZLEN, eth_nic_base + TX_LEN_PORT);
277 /* Test to see if the chip has allocated memory for the packet */
278 for (tmo = currticks() + 2;
279 (readreg(PP_BusST) & READY_FOR_TX_NOW) == 0; )
280 if (currticks() >= tmo)
283 /* Write the contents of the packet */
284 outsw(eth_nic_base + TX_FRAME_PORT, testpacket,
287 printf(" sending test packet ");
288 /* wait a couple of timer ticks for packet to be received */
289 for (tmo = currticks() + 2; currticks() < tmo; );
291 if ((readreg(PP_TxEvent) & TX_SEND_OK_BITS) == TX_OK) {
300 static int detect_aui(struct nic *nic)
302 clrline(); printf("attempting %s:","AUI");
305 writereg(PP_LineCTL, (eth_linectl & ~AUTO_AUI_10BASET) | AUI_ONLY);
307 if (send_test_pkt(nic)) {
308 return A_CNF_MEDIA_AUI; }
313 static int detect_bnc(struct nic *nic)
315 clrline(); printf("attempting %s:","BNC");
318 writereg(PP_LineCTL, (eth_linectl & ~AUTO_AUI_10BASET) | AUI_ONLY);
320 if (send_test_pkt(nic)) {
321 return A_CNF_MEDIA_10B_2; }
326 /**************************************************************************
327 ETH_RESET - Reset adapter
328 ***************************************************************************/
330 static void cs89x0_reset(struct nic *nic)
333 unsigned long reset_tmo;
335 writereg(PP_SelfCTL, readreg(PP_SelfCTL) | POWER_ON_RESET);
337 /* wait for two ticks; that is 2*55ms */
338 for (reset_tmo = currticks() + 2; currticks() < reset_tmo; );
340 if (eth_cs_type != CS8900) {
341 /* Hardware problem requires PNP registers to be reconfigured
343 if (eth_irqno != 0xFFFF) {
344 outw(PP_CS8920_ISAINT, eth_nic_base + ADD_PORT);
345 outb(eth_irqno, eth_nic_base + DATA_PORT);
346 outb(0, eth_nic_base + DATA_PORT + 1); }
349 outw(PP_CS8920_ISAMemB, eth_nic_base + ADD_PORT);
350 outb((eth_mem_start >> 8) & 0xff, eth_nic_base + DATA_PORT);
351 outb((eth_mem_start >> 24) & 0xff, eth_nic_base + DATA_PORT + 1); } }
353 /* Wait until the chip is reset */
354 for (reset_tmo = currticks() + 2;
355 (readreg(PP_SelfST) & INIT_DONE) == 0 &&
356 currticks() < reset_tmo; );
358 /* disable interrupts and memory accesses */
359 writereg(PP_BusCTL, 0);
361 /* set the ethernet address */
362 for (i=0; i < ETH_ALEN/2; i++)
364 nic->node_addr[i*2] |
365 (nic->node_addr[i*2+1] << 8));
367 /* receive only error free packets addressed to this card */
368 writereg(PP_RxCTL, DEF_RX_ACCEPT);
370 /* do not generate any interrupts on receive operations */
371 writereg(PP_RxCFG, 0);
373 /* do not generate any interrupts on transmit operations */
374 writereg(PP_TxCFG, 0);
376 /* do not generate any interrupts on buffer operations */
377 writereg(PP_BufCFG, 0);
379 /* reset address port, so that autoprobing will keep working */
380 outw(PP_ChipID, eth_nic_base + ADD_PORT);
385 /**************************************************************************
386 ETH_TRANSMIT - Transmit a frame
387 ***************************************************************************/
389 static void cs89x0_transmit(
391 const char *d, /* Destination */
392 unsigned int t, /* Type */
393 unsigned int s, /* size */
394 const char *p) /* Packet */
399 /* does this size have to be rounded??? please,
400 somebody have a look in the specs */
401 if ((sr = ((s + ETH_HLEN + 1)&~1)) < ETH_ZLEN)
405 /* initiate a transmit sequence */
406 outw(TX_AFTER_ALL, eth_nic_base + TX_CMD_PORT);
407 outw(sr, eth_nic_base + TX_LEN_PORT);
409 /* Test to see if the chip has allocated memory for the packet */
410 if ((readreg(PP_BusST) & READY_FOR_TX_NOW) == 0) {
411 /* Oops... this should not happen! */
412 printf("cs: unable to send packet; retrying...\n");
413 for (tmo = currticks() + 5*TICKS_PER_SEC; currticks() < tmo; );
417 /* Write the contents of the packet */
418 outsw(eth_nic_base + TX_FRAME_PORT, d, ETH_ALEN/2);
419 outsw(eth_nic_base + TX_FRAME_PORT, nic->node_addr,
421 outw(((t >> 8)&0xFF)|(t << 8), eth_nic_base + TX_FRAME_PORT);
422 outsw(eth_nic_base + TX_FRAME_PORT, p, (s+1)/2);
423 for (sr = sr/2 - (s+1)/2 - ETH_ALEN - 1; sr > 0; sr--)
424 outw(0, eth_nic_base + TX_FRAME_PORT);
426 /* wait for transfer to succeed */
427 for (tmo = currticks()+5*TICKS_PER_SEC;
428 (s = readreg(PP_TxEvent)&~0x1F) == 0 && currticks() < tmo;)
430 if ((s & TX_SEND_OK_BITS) != TX_OK) {
431 printf("\ntransmission error %#hX\n", s);
437 /**************************************************************************
438 ETH_POLL - Wait for a frame
439 ***************************************************************************/
441 static int cs89x0_poll(struct nic *nic, int retrieve)
445 status = readreg(PP_RxEvent);
447 if ((status & RX_OK) == 0)
450 if ( ! retrieve ) return 1;
452 status = inw(eth_nic_base + RX_FRAME_PORT);
453 nic->packetlen = inw(eth_nic_base + RX_FRAME_PORT);
454 insw(eth_nic_base + RX_FRAME_PORT, nic->packet, nic->packetlen >> 1);
455 if (nic->packetlen & 1)
456 nic->packet[nic->packetlen-1] = inw(eth_nic_base + RX_FRAME_PORT);
460 static void cs89x0_irq(struct nic *nic __unused, irq_action_t action __unused)
472 static struct nic_operations cs89x0_operations = {
473 .connect = dummy_connect,
475 .transmit = cs89x0_transmit,
479 /**************************************************************************
480 ETH_PROBE - Look for an adapter
481 ***************************************************************************/
483 static int cs89x0_probe_addr ( isa_probe_addr_t ioaddr ) {
484 /* if they give us an odd I/O address, then do ONE write to
485 the address port, to get it back to address zero, where we
486 expect to find the EISA signature word. */
489 if ((inw(ioaddr + ADD_PORT) & ADD_MASK) != ADD_SIG)
491 outw(PP_ChipID, ioaddr + ADD_PORT);
494 if (inw(ioaddr + DATA_PORT) != CHIP_EISA_ID_SIG)
500 static int cs89x0_probe ( struct nic *nic, struct isa_device *isa __unused ) {
502 unsigned rev_type = 0, isa_cnf, cs_revision;
503 unsigned short eeprom_buff[CHKSUM_LEN];
505 nic->ioaddr &= ~1; /* LSB = 1 indicates a more aggressive probe */
506 eth_nic_base = nic->ioaddr;
508 /* get the chip type */
509 rev_type = readreg(PRODUCT_ID_ADD);
510 eth_cs_type = rev_type &~ REVISON_BITS;
511 cs_revision = ((rev_type & REVISON_BITS) >> 8) + 'A';
513 printf("\ncs: cs89%c0%s rev %c, base %#hX",
514 eth_cs_type==CS8900?'0':'2',
515 eth_cs_type==CS8920M?"M":"",
519 /* First check to see if an EEPROM is attached*/
520 if ((readreg(PP_SelfST) & EEPROM_PRESENT) == 0) {
521 printf("\ncs: no EEPROM...\n");
522 outw(PP_ChipID, eth_nic_base + ADD_PORT);
524 } else if (get_eeprom_data(START_EEPROM_DATA,CHKSUM_LEN,
526 printf("\ncs: EEPROM read failed...\n");
527 outw(PP_ChipID, eth_nic_base + ADD_PORT);
529 } else if (get_eeprom_chksum(START_EEPROM_DATA,CHKSUM_LEN,
531 printf("\ncs: EEPROM checksum bad...\n");
532 outw(PP_ChipID, eth_nic_base + ADD_PORT);
536 /* get transmission control word but keep the
537 autonegotiation bits */
538 eth_auto_neg_cnf = eeprom_buff[AUTO_NEG_CNF_OFFSET/2];
539 /* Store adapter configuration */
540 eth_adapter_cnf = eeprom_buff[ADAPTER_CNF_OFFSET/2];
541 /* Store ISA configuration */
542 isa_cnf = eeprom_buff[ISA_CNF_OFFSET/2];
544 /* store the initial memory base address */
545 eth_mem_start = eeprom_buff[PACKET_PAGE_OFFSET/2] << 8;
547 printf("%s%s%s, addr ",
548 (eth_adapter_cnf & A_CNF_10B_T)?", RJ-45":"",
549 (eth_adapter_cnf & A_CNF_AUI)?", AUI":"",
550 (eth_adapter_cnf & A_CNF_10B_2)?", BNC":"");
552 /* If this is a CS8900 then no pnp soft */
553 if (eth_cs_type != CS8900 &&
554 /* Check if the ISA IRQ has been set */
555 (i = readreg(PP_CS8920_ISAINT) & 0xff,
556 (i != 0 && i < CS8920_NO_INTS)))
559 i = isa_cnf & INT_NO_MASK;
560 if (eth_cs_type == CS8900) {
561 /* the table that follows is dependent
562 upon how you wired up your cs8900
563 in your system. The table is the
564 same as the cs8900 engineering demo
565 board. irq_map also depends on the
566 contents of the table. Also see
567 write_irq, which is the reverse
568 mapping of the table below. */
569 if (i < 4) i = "\012\013\014\005"[i];
570 else printf("\ncs: BUG: isa_config is %d\n", i); }
573 nic->irqno = eth_irqno;
575 /* Retrieve and print the ethernet address. */
576 for (i=0; i<ETH_ALEN; i++) {
577 nic->node_addr[i] = ((unsigned char *)eeprom_buff)[i];
580 DBG ( "%s\n", eth_ntoa ( nic->node_addr ) );
584 /* Retrieve and print the ethernet address. */
586 unsigned char MAC_HW_ADDR[6]={MAC_HW_ADDR_DRV};
587 memcpy(nic->node_addr, MAC_HW_ADDR, 6);
590 DBG ( "%s\n", eth_ntoa ( nic->node_addr ) );
592 eth_adapter_cnf = A_CNF_10B_T | A_CNF_MEDIA_10B_T;
593 eth_auto_neg_cnf = EE_AUTO_NEG_ENABLE | IMM_BIT;
596 /* Set the LineCTL quintuplet based on adapter
597 configuration read from EEPROM */
598 if ((eth_adapter_cnf & A_CNF_EXTND_10B_2) &&
599 (eth_adapter_cnf & A_CNF_LOW_RX_SQUELCH))
600 eth_linectl = LOW_RX_SQUELCH;
604 /* check to make sure that they have the "right"
605 hardware available */
606 switch(eth_adapter_cnf & A_CNF_MEDIA_TYPE) {
607 case A_CNF_MEDIA_10B_T: result = eth_adapter_cnf & A_CNF_10B_T;
609 case A_CNF_MEDIA_AUI: result = eth_adapter_cnf & A_CNF_AUI;
611 case A_CNF_MEDIA_10B_2: result = eth_adapter_cnf & A_CNF_10B_2;
613 default: result = eth_adapter_cnf & (A_CNF_10B_T | A_CNF_AUI |
617 printf("cs: EEPROM is configured for unavailable media\n");
619 writereg(PP_LineCTL, readreg(PP_LineCTL) &
620 ~(SERIAL_TX_ON | SERIAL_RX_ON));
621 outw(PP_ChipID, eth_nic_base + ADD_PORT);
625 /* Initialize the card for probing of the attached media */
628 /* set the hardware to the configured choice */
629 switch(eth_adapter_cnf & A_CNF_MEDIA_TYPE) {
630 case A_CNF_MEDIA_10B_T:
631 result = detect_tp();
634 printf("10Base-T (RJ-45%s",
635 ") has no cable\n"); }
636 /* check "ignore missing media" bit */
637 if (eth_auto_neg_cnf & IMM_BIT)
638 /* Yes! I don't care if I see a link pulse */
639 result = A_CNF_MEDIA_10B_T;
641 case A_CNF_MEDIA_AUI:
642 result = detect_aui(nic);
645 printf("10Base-5 (AUI%s",
646 ") has no cable\n"); }
647 /* check "ignore missing media" bit */
648 if (eth_auto_neg_cnf & IMM_BIT)
649 /* Yes! I don't care if I see a carrrier */
650 result = A_CNF_MEDIA_AUI;
652 case A_CNF_MEDIA_10B_2:
653 result = detect_bnc(nic);
656 printf("10Base-2 (BNC%s",
657 ") has no cable\n"); }
658 /* check "ignore missing media" bit */
659 if (eth_auto_neg_cnf & IMM_BIT)
660 /* Yes! I don't care if I can xmit a packet */
661 result = A_CNF_MEDIA_10B_2;
663 case A_CNF_MEDIA_AUTO:
664 writereg(PP_LineCTL, eth_linectl | AUTO_AUI_10BASET);
665 if (eth_adapter_cnf & A_CNF_10B_T)
666 if ((result = detect_tp()) != 0)
668 if (eth_adapter_cnf & A_CNF_AUI)
669 if ((result = detect_aui(nic)) != 0)
671 if (eth_adapter_cnf & A_CNF_10B_2)
672 if ((result = detect_bnc(nic)) != 0)
674 clrline(); printf("no media detected\n");
679 case 0: printf("no network cable attached to configured media\n");
681 case A_CNF_MEDIA_10B_T: printf("using 10Base-T (RJ-45)\n");
683 case A_CNF_MEDIA_AUI: printf("using 10Base-5 (AUI)\n");
685 case A_CNF_MEDIA_10B_2: printf("using 10Base-2 (BNC)\n");
689 /* Turn on both receive and transmit operations */
690 writereg(PP_LineCTL, readreg(PP_LineCTL) | SERIAL_RX_ON |
696 writereg(PP_LineCTL, readreg(PP_LineCTL) &
697 ~(SERIAL_TX_ON | SERIAL_RX_ON));
698 outw(PP_ChipID, eth_nic_base + ADD_PORT);
702 nic->nic_op = &cs89x0_operations;
706 static void cs89x0_disable ( struct nic *nic,
707 struct isa_device *isa __unused ) {
711 static isa_probe_addr_t cs89x0_probe_addrs[] = {
713 /* use "conservative" default values for autoprobing */
714 0x300, 0x320, 0x340, 0x200, 0x220, 0x240,
715 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0,
716 /* if that did not work, then be more aggressive */
717 0x301, 0x321, 0x341, 0x201, 0x221, 0x241,
718 0x261, 0x281, 0x2a1, 0x2c1, 0x2e1,
724 ISA_DRIVER ( cs89x0_driver, cs89x0_probe_addrs, cs89x0_probe_addr,
725 ISAPNP_VENDOR('C','S','C'), 0x0007 );
727 DRIVER ( "cs89x0", nic_driver, isa_driver, cs89x0_driver,
728 cs89x0_probe, cs89x0_disable );
730 ISA_ROM ( "cs89x0", "Crystal Semiconductor CS89x0" );