iPXE
3c595.c
Go to the documentation of this file.
00001 /*
00002 * 3c595.c -- 3COM 3C595 Fast Etherlink III PCI driver for etherboot
00003 *
00004 * Copyright (C) 2000 Shusuke Nisiyama <shu@athena.qe.eng.hokudai.ac.jp>
00005 * All rights reserved.
00006 * Mar. 14, 2000
00007 *
00008 *  This software may be used, modified, copied, distributed, and sold, in
00009 *  both source and binary form provided that the above copyright and these
00010 *  terms are retained. Under no circumstances are the authors responsible for
00011 *  the proper functioning of this software, nor do the authors assume any
00012 *  responsibility for damages incurred with its use.
00013 *
00014 * This code is based on Martin Renters' etherboot-4.4.3 3c509.c and 
00015 * Herb Peyerl's FreeBSD 3.4-RELEASE if_vx.c driver.
00016 *
00017 *  Copyright (C) 1993-1994, David Greenman, Martin Renters.
00018 *  Copyright (C) 1993-1995, Andres Vega Garcia.
00019 *  Copyright (C) 1995, Serge Babkin.
00020 *
00021 *  Copyright (c) 1994 Herb Peyerl <hpeyerl@novatel.ca>
00022 *
00023 * timlegge      08-24-2003      Add Multicast Support
00024 */
00025 
00026 FILE_LICENCE ( BSD2 );
00027 
00028 /* #define EDEBUG */
00029 
00030 #include "etherboot.h"
00031 #include "nic.h"
00032 #include <ipxe/pci.h>
00033 #include <ipxe/ethernet.h>
00034 #include "3c595.h"
00035 
00036 static struct nic_operations t595_operations;
00037 
00038 static unsigned short   eth_nic_base;
00039 static unsigned short   vx_connector, vx_connectors;
00040 
00041 static struct connector_entry {
00042   int bit;
00043   char *name;
00044 } conn_tab[VX_CONNECTORS] = {
00045 #define CONNECTOR_UTP   0
00046   { 0x08, "utp"},
00047 #define CONNECTOR_AUI   1
00048   { 0x20, "aui"},
00049 /* dummy */
00050   { 0, "???"},
00051 #define CONNECTOR_BNC   3
00052   { 0x10, "bnc"},
00053 #define CONNECTOR_TX    4
00054   { 0x02, "tx"},
00055 #define CONNECTOR_FX    5
00056   { 0x04, "fx"},
00057 #define CONNECTOR_MII   6
00058   { 0x40, "mii"},
00059   { 0, "???"}
00060 };
00061 
00062 static void vxgetlink(void);
00063 static void vxsetlink(void);
00064 
00065 /**************************************************************************
00066 ETH_RESET - Reset adapter
00067 ***************************************************************************/
00068 static void t595_reset(struct nic *nic)
00069 {
00070         int i;
00071 
00072         /***********************************************************
00073                         Reset 3Com 595 card
00074         *************************************************************/
00075 
00076         /* stop card */
00077         outw(RX_DISABLE, BASE + VX_COMMAND);
00078         outw(RX_DISCARD_TOP_PACK, BASE + VX_COMMAND);
00079         VX_BUSY_WAIT;
00080         outw(TX_DISABLE, BASE + VX_COMMAND);
00081         outw(STOP_TRANSCEIVER, BASE + VX_COMMAND);
00082         udelay(8000);
00083         outw(RX_RESET, BASE + VX_COMMAND);
00084         VX_BUSY_WAIT;
00085         outw(TX_RESET, BASE + VX_COMMAND);
00086         VX_BUSY_WAIT;
00087         outw(C_INTR_LATCH, BASE + VX_COMMAND);
00088         outw(SET_RD_0_MASK, BASE + VX_COMMAND);
00089         outw(SET_INTR_MASK, BASE + VX_COMMAND);
00090         outw(SET_RX_FILTER, BASE + VX_COMMAND);
00091 
00092         /*
00093         * initialize card
00094         */
00095         VX_BUSY_WAIT;
00096 
00097         GO_WINDOW(0);
00098 
00099         /* Disable the card */
00100 /*      outw(0, BASE + VX_W0_CONFIG_CTRL); */
00101 
00102         /* Configure IRQ to none */
00103 /*      outw(SET_IRQ(0), BASE + VX_W0_RESOURCE_CFG); */
00104 
00105         /* Enable the card */
00106 /*      outw(ENABLE_DRQ_IRQ, BASE + VX_W0_CONFIG_CTRL); */
00107 
00108         GO_WINDOW(2);
00109 
00110         /* Reload the ether_addr. */
00111         for (i = 0; i < ETH_ALEN; i++)
00112                 outb(nic->node_addr[i], BASE + VX_W2_ADDR_0 + i);
00113 
00114         outw(RX_RESET, BASE + VX_COMMAND);
00115         VX_BUSY_WAIT;
00116         outw(TX_RESET, BASE + VX_COMMAND);
00117         VX_BUSY_WAIT;
00118 
00119         /* Window 1 is operating window */
00120         GO_WINDOW(1);
00121         for (i = 0; i < 31; i++)
00122                 inb(BASE + VX_W1_TX_STATUS);
00123 
00124         outw(SET_RD_0_MASK | S_CARD_FAILURE | S_RX_COMPLETE |
00125                 S_TX_COMPLETE | S_TX_AVAIL, BASE + VX_COMMAND);
00126         outw(SET_INTR_MASK | S_CARD_FAILURE | S_RX_COMPLETE |
00127                 S_TX_COMPLETE | S_TX_AVAIL, BASE + VX_COMMAND);
00128 
00129 /*
00130  * Attempt to get rid of any stray interrupts that occurred during
00131  * configuration.  On the i386 this isn't possible because one may
00132  * already be queued.  However, a single stray interrupt is
00133  * unimportant.
00134  */
00135 
00136         outw(ACK_INTR | 0xff, BASE + VX_COMMAND);
00137 
00138         outw(SET_RX_FILTER | FIL_INDIVIDUAL |
00139             FIL_BRDCST|FIL_MULTICAST, BASE + VX_COMMAND);
00140 
00141         vxsetlink();
00142 /*{
00143         int i,j;
00144         i = CONNECTOR_TX;
00145         GO_WINDOW(3);
00146         j = inl(BASE + VX_W3_INTERNAL_CFG) & ~INTERNAL_CONNECTOR_MASK;
00147         outl(BASE + VX_W3_INTERNAL_CFG, j | (i <<INTERNAL_CONNECTOR_BITS));
00148         GO_WINDOW(4);
00149         outw(LINKBEAT_ENABLE, BASE + VX_W4_MEDIA_TYPE);
00150         GO_WINDOW(1);
00151 }*/
00152 
00153         /* start tranciever and receiver */
00154         outw(RX_ENABLE, BASE + VX_COMMAND);
00155         outw(TX_ENABLE, BASE + VX_COMMAND);
00156 
00157 }
00158 
00159 /**************************************************************************
00160 ETH_TRANSMIT - Transmit a frame
00161 ***************************************************************************/
00162 static char padmap[] = {
00163         0, 3, 2, 1};
00164 
00165 static void t595_transmit(
00166 struct nic *nic,
00167 const char *d,                  /* Destination */
00168 unsigned int t,                 /* Type */
00169 unsigned int s,                 /* size */
00170 const char *p)                  /* Packet */
00171 {
00172         register int len;
00173         int pad;
00174         int status;
00175 
00176 #ifdef EDEBUG
00177         printf("{l=%d,t=%hX}",s+ETH_HLEN,t);
00178 #endif
00179 
00180         /* swap bytes of type */
00181         t= htons(t);
00182 
00183         len=s+ETH_HLEN; /* actual length of packet */
00184         pad = padmap[len & 3];
00185 
00186         /*
00187         * The 3c595 automatically pads short packets to minimum ethernet length,
00188         * but we drop packets that are too large. Perhaps we should truncate
00189         * them instead?
00190         */
00191         if (len + pad > ETH_FRAME_LEN) {
00192                 return;
00193         }
00194 
00195         /* drop acknowledgements */
00196         while(( status=inb(BASE + VX_W1_TX_STATUS) )& TXS_COMPLETE ) {
00197                 if(status & (TXS_UNDERRUN|TXS_MAX_COLLISION|TXS_STATUS_OVERFLOW)) {
00198                         outw(TX_RESET, BASE + VX_COMMAND);
00199                         outw(TX_ENABLE, BASE + VX_COMMAND);
00200                 }
00201 
00202                 outb(0x0, BASE + VX_W1_TX_STATUS);
00203         }
00204 
00205         while (inw(BASE + VX_W1_FREE_TX) < len + pad + 4) {
00206                 /* no room in FIFO */
00207         }
00208 
00209         outw(len, BASE + VX_W1_TX_PIO_WR_1);
00210         outw(0x0, BASE + VX_W1_TX_PIO_WR_1);    /* Second dword meaningless */
00211 
00212         /* write packet */
00213         outsw(BASE + VX_W1_TX_PIO_WR_1, d, ETH_ALEN/2);
00214         outsw(BASE + VX_W1_TX_PIO_WR_1, nic->node_addr, ETH_ALEN/2);
00215         outw(t, BASE + VX_W1_TX_PIO_WR_1);
00216         outsw(BASE + VX_W1_TX_PIO_WR_1, p, s / 2);
00217         if (s & 1)
00218                 outb(*(p+s - 1), BASE + VX_W1_TX_PIO_WR_1);
00219 
00220         while (pad--)
00221                 outb(0, BASE + VX_W1_TX_PIO_WR_1);      /* Padding */
00222 
00223         /* wait for Tx complete */
00224         while((inw(BASE + VX_STATUS) & S_COMMAND_IN_PROGRESS) != 0)
00225                 ;
00226 }
00227 
00228 /**************************************************************************
00229 ETH_POLL - Wait for a frame
00230 ***************************************************************************/
00231 static int t595_poll(struct nic *nic, int retrieve)
00232 {
00233         /* common variables */
00234         /* variables for 3C595 */
00235         short status, cst;
00236         register short rx_fifo;
00237 
00238         cst=inw(BASE + VX_STATUS);
00239 
00240 #ifdef EDEBUG
00241         if(cst & 0x1FFF)
00242                 printf("-%hX-",cst);
00243 #endif
00244 
00245         if( (cst & S_RX_COMPLETE)==0 ) {
00246                 /* acknowledge  everything */
00247                 outw(ACK_INTR | cst, BASE + VX_COMMAND);
00248                 outw(C_INTR_LATCH, BASE + VX_COMMAND);
00249 
00250                 return 0;
00251         }
00252 
00253         status = inw(BASE + VX_W1_RX_STATUS);
00254 #ifdef EDEBUG
00255         printf("*%hX*",status);
00256 #endif
00257 
00258         if (status & ERR_RX) {
00259                 outw(RX_DISCARD_TOP_PACK, BASE + VX_COMMAND);
00260                 return 0;
00261         }
00262 
00263         rx_fifo = status & RX_BYTES_MASK;
00264         if (rx_fifo==0)
00265                 return 0;
00266 
00267         if ( ! retrieve ) return 1;
00268 
00269                 /* read packet */
00270 #ifdef EDEBUG
00271         printf("[l=%d",rx_fifo);
00272 #endif
00273         insw(BASE + VX_W1_RX_PIO_RD_1, nic->packet, rx_fifo / 2);
00274         if(rx_fifo & 1)
00275                 nic->packet[rx_fifo-1]=inb(BASE + VX_W1_RX_PIO_RD_1);
00276         nic->packetlen=rx_fifo;
00277 
00278         while(1) {
00279                 status = inw(BASE + VX_W1_RX_STATUS);
00280 #ifdef EDEBUG
00281                 printf("*%hX*",status);
00282 #endif
00283                 rx_fifo = status & RX_BYTES_MASK;
00284 
00285                 if(rx_fifo>0) {
00286                         insw(BASE + VX_W1_RX_PIO_RD_1, nic->packet+nic->packetlen, rx_fifo / 2);
00287                         if(rx_fifo & 1)
00288                                 nic->packet[nic->packetlen+rx_fifo-1]=inb(BASE + VX_W1_RX_PIO_RD_1);
00289                         nic->packetlen+=rx_fifo;
00290 #ifdef EDEBUG
00291                         printf("+%d",rx_fifo);
00292 #endif
00293                 }
00294                 if(( status & RX_INCOMPLETE )==0) {
00295 #ifdef EDEBUG
00296                         printf("=%d",nic->packetlen);
00297 #endif
00298                         break;
00299                 }
00300                 udelay(1000);
00301         }
00302 
00303         /* acknowledge reception of packet */
00304         outw(RX_DISCARD_TOP_PACK, BASE + VX_COMMAND);
00305         while (inw(BASE + VX_STATUS) & S_COMMAND_IN_PROGRESS);
00306 #ifdef EDEBUG
00307 {
00308         unsigned short type = 0;        /* used by EDEBUG */
00309         type = (nic->packet[12]<<8) | nic->packet[13];
00310         if(nic->packet[0]+nic->packet[1]+nic->packet[2]+nic->packet[3]+nic->packet[4]+
00311             nic->packet[5] == 0xFF*ETH_ALEN)
00312                 printf(",t=%hX,b]",type);
00313         else
00314                 printf(",t=%hX]",type);
00315 }
00316 #endif
00317         return 1;
00318 }
00319 
00320 
00321 /*************************************************************************
00322         3Com 595 - specific routines
00323 **************************************************************************/
00324 
00325 static int
00326 eeprom_rdy()
00327 {
00328         int i;
00329 
00330         for (i = 0; is_eeprom_busy(BASE) && i < MAX_EEPROMBUSY; i++)
00331                 udelay(1000);
00332         if (i >= MAX_EEPROMBUSY) {
00333                 /* printf("3c595: eeprom failed to come ready.\n"); */
00334                 printf("3c595: eeprom is busy.\n"); /* memory in EPROM is tight */
00335                 return (0);
00336         }
00337         return (1);
00338 }
00339 
00340 /*
00341  * get_e: gets a 16 bits word from the EEPROM. we must have set the window
00342  * before
00343  */
00344 static int
00345 get_e(offset)
00346 int offset;
00347 {
00348         if (!eeprom_rdy())
00349                 return (0xffff);
00350         outw(EEPROM_CMD_RD | offset, BASE + VX_W0_EEPROM_COMMAND);
00351         if (!eeprom_rdy())
00352                 return (0xffff);
00353         return (inw(BASE + VX_W0_EEPROM_DATA));
00354 }
00355 
00356 static void            
00357 vxgetlink(void)
00358 {
00359     int n, k;
00360 
00361     GO_WINDOW(3);
00362     vx_connectors = inw(BASE + VX_W3_RESET_OPT) & 0x7f;
00363     for (n = 0, k = 0; k < VX_CONNECTORS; k++) {
00364       if (vx_connectors & conn_tab[k].bit) {
00365         if (n > 0) {
00366           printf("/");
00367         }
00368         printf("%s", conn_tab[k].name );
00369         n++;
00370       }
00371     }
00372     if (vx_connectors == 0) {
00373         printf("no connectors!");
00374         return;
00375     }
00376     GO_WINDOW(3);
00377     vx_connector = (inl(BASE + VX_W3_INTERNAL_CFG) 
00378                         & INTERNAL_CONNECTOR_MASK) 
00379                         >> INTERNAL_CONNECTOR_BITS;
00380     if (vx_connector & 0x10) {
00381         vx_connector &= 0x0f;
00382         printf("[*%s*]", conn_tab[vx_connector].name);
00383         printf(": disable 'auto select' with DOS util!");
00384     } else {
00385         printf("[*%s*]", conn_tab[vx_connector].name);
00386     }
00387 }
00388 
00389 static void            
00390 vxsetlink(void)
00391 {       
00392     int i, j;
00393     char *reason, *warning;
00394     static signed char prev_conn = -1;
00395 
00396     if (prev_conn == -1) {
00397         prev_conn = vx_connector;
00398     }
00399 
00400     i = vx_connector;       /* default in EEPROM */
00401     reason = "default";
00402     warning = NULL;
00403 
00404     if ((vx_connectors & conn_tab[vx_connector].bit) == 0) {
00405         warning = "strange connector type in EEPROM.";
00406         reason = "forced";
00407         i = CONNECTOR_UTP;
00408     }
00409 
00410         if (warning) {
00411             printf("warning: %s\n", warning);
00412         }
00413         printf("selected %s. (%s)\n", conn_tab[i].name, reason);
00414 
00415     /* Set the selected connector. */
00416     GO_WINDOW(3);
00417     j = inl(BASE + VX_W3_INTERNAL_CFG) & ~INTERNAL_CONNECTOR_MASK;
00418     outl(j | (i <<INTERNAL_CONNECTOR_BITS), BASE + VX_W3_INTERNAL_CFG);
00419 
00420     /* First, disable all. */
00421     outw(STOP_TRANSCEIVER, BASE + VX_COMMAND);
00422     udelay(8000);
00423     GO_WINDOW(4);
00424     outw(0, BASE + VX_W4_MEDIA_TYPE);
00425 
00426     /* Second, enable the selected one. */
00427     switch(i) {
00428       case CONNECTOR_UTP:
00429         GO_WINDOW(4);
00430         outw(ENABLE_UTP, BASE + VX_W4_MEDIA_TYPE);
00431         break;
00432       case CONNECTOR_BNC:
00433         outw(START_TRANSCEIVER,BASE + VX_COMMAND);
00434         udelay(8000);
00435         break;
00436       case CONNECTOR_TX:
00437       case CONNECTOR_FX:
00438         GO_WINDOW(4);
00439         outw(LINKBEAT_ENABLE, BASE + VX_W4_MEDIA_TYPE);
00440         break;
00441       default:  /* AUI and MII fall here */
00442         break;
00443     }
00444     GO_WINDOW(1); 
00445 }
00446 
00447 static void t595_disable ( struct nic *nic ) {
00448 
00449         t595_reset(nic);
00450 
00451         outw(STOP_TRANSCEIVER, BASE + VX_COMMAND);
00452         udelay(8000);
00453         GO_WINDOW(4);
00454         outw(0, BASE + VX_W4_MEDIA_TYPE);
00455         GO_WINDOW(1);
00456 }
00457 
00458 static void t595_irq(struct nic *nic __unused, irq_action_t action __unused)
00459 {
00460   switch ( action ) {
00461   case DISABLE :
00462     break;
00463   case ENABLE :
00464     break;
00465   case FORCE :
00466     break;
00467   }
00468 }
00469 
00470 /**************************************************************************
00471 ETH_PROBE - Look for an adapter
00472 ***************************************************************************/
00473 static int t595_probe ( struct nic *nic, struct pci_device *pci ) {
00474 
00475         int i;
00476         unsigned short *p;
00477 
00478         if (pci->ioaddr == 0)
00479                 return 0;
00480         eth_nic_base = pci->ioaddr;
00481 
00482         nic->irqno  = 0;
00483         nic->ioaddr = pci->ioaddr;
00484 
00485         GO_WINDOW(0);
00486         outw(GLOBAL_RESET, BASE + VX_COMMAND);
00487         VX_BUSY_WAIT;
00488 
00489         vxgetlink();
00490 
00491 /*
00492         printf("\nEEPROM:");
00493         for (i = 0; i < (EEPROMSIZE/2); i++) {
00494           printf("%hX:", get_e(i));
00495         }
00496         printf("\n");
00497 */
00498         /*
00499         * Read the station address from the eeprom
00500         */
00501         p = (unsigned short *) nic->node_addr;
00502         for (i = 0; i < 3; i++) {
00503                 GO_WINDOW(0);
00504                 p[i] = htons(get_e(EEPROM_OEM_ADDR_0 + i));
00505                 GO_WINDOW(2);
00506                 outw(ntohs(p[i]), BASE + VX_W2_ADDR_0 + (i * 2));
00507         }
00508 
00509         DBG ( "Ethernet address: %s\n", eth_ntoa (nic->node_addr) );
00510 
00511         t595_reset(nic);
00512         nic->nic_op     = &t595_operations;
00513         return 1;
00514 
00515 }
00516 
00517 static struct nic_operations t595_operations = {
00518         .connect        = dummy_connect,
00519         .poll           = t595_poll,
00520         .transmit       = t595_transmit,
00521         .irq            = t595_irq,
00522 
00523 };
00524 
00525 static struct pci_device_id t595_nics[] = {
00526 PCI_ROM(0x10b7, 0x5900, "3c590",           "3Com590", 0),               /* Vortex 10Mbps */
00527 PCI_ROM(0x10b7, 0x5950, "3c595",           "3Com595", 0),               /* Vortex 100baseTx */
00528 PCI_ROM(0x10b7, 0x5951, "3c595-1",         "3Com595", 0),               /* Vortex 100baseT4 */
00529 PCI_ROM(0x10b7, 0x5952, "3c595-2",         "3Com595", 0),               /* Vortex 100base-MII */
00530 PCI_ROM(0x10b7, 0x9000, "3c900-tpo",       "3Com900-TPO", 0),   /* 10 Base TPO */
00531 PCI_ROM(0x10b7, 0x9001, "3c900-t4",        "3Com900-Combo", 0), /* 10/100 T4 */
00532 PCI_ROM(0x10b7, 0x9004, "3c900b-tpo",      "3Com900B-TPO", 0),  /* 10 Base TPO */
00533 PCI_ROM(0x10b7, 0x9005, "3c900b-combo",    "3Com900B-Combo", 0),        /* 10 Base Combo */
00534 PCI_ROM(0x10b7, 0x9006, "3c900b-tpb2",     "3Com900B-2/T", 0),  /* 10 Base TP and Base2 */
00535 PCI_ROM(0x10b7, 0x900a, "3c900b-fl",       "3Com900B-FL", 0),   /* 10 Base F */
00536 PCI_ROM(0x10b7, 0x9800, "3c980-cyclone-1", "3Com980-Cyclone", 0),       /* Cyclone */
00537 PCI_ROM(0x10b7, 0x9805, "3c9805-1",        "3Com9805", 0),              /* Dual Port Server Cyclone */
00538 PCI_ROM(0x10b7, 0x7646, "3csoho100-tx-1",  "3CSOHO100-TX", 0),  /* Hurricane */
00539 PCI_ROM(0x10b7, 0x4500, "3c450-1",         "3Com450 HomePNA Tornado", 0),
00540 };
00541 
00542 PCI_DRIVER ( t595_driver, t595_nics, PCI_NO_CLASS );
00543 
00544 DRIVER ( "3C595", nic_driver, pci_driver, t595_driver,
00545          t595_probe, t595_disable );
00546 
00547 /*
00548  * Local variables:
00549  *  c-basic-offset: 8
00550  *  c-indent-level: 8
00551  *  tab-width: 8
00552  * End:
00553  */