iPXE
3c5x9.c
Go to the documentation of this file.
00001 /**************************************************************************
00002 ETHERBOOT -  BOOTP/TFTP Bootstrap Program
00003 
00004 Author: Martin Renters.
00005   Date: Mar 22 1995
00006 
00007  This code is based heavily on David Greenman's if_ed.c driver and
00008   Andres Vega Garcia's if_ep.c driver.
00009 
00010  Copyright (C) 1993-1994, David Greenman, Martin Renters.
00011  Copyright (C) 1993-1995, Andres Vega Garcia.
00012  Copyright (C) 1995, Serge Babkin.
00013   This software may be used, modified, copied, distributed, and sold, in
00014   both source and binary form provided that the above copyright and these
00015   terms are retained. Under no circumstances are the authors responsible for
00016   the proper functioning of this software, nor do the authors assume any
00017   responsibility for damages incurred with its use.
00018 
00019 3c509 support added by Serge Babkin (babkin@hq.icb.chel.su)
00020 
00021 $Id$
00022 
00023 ***************************************************************************/
00024 
00025 FILE_LICENCE ( BSD2 );
00026 
00027 /* #define EDEBUG */
00028 
00029 #include <ipxe/ethernet.h>
00030 #include "etherboot.h"
00031 #include "nic.h"
00032 #include <ipxe/isa.h>
00033 #include "3c509.h"
00034 
00035 static enum { none, bnc, utp } connector = none;        /* for 3C509 */
00036 
00037 /**************************************************************************
00038 ETH_RESET - Reset adapter
00039 ***************************************************************************/
00040 void t5x9_disable ( struct nic *nic ) {
00041         /* stop card */
00042         outw(RX_DISABLE, nic->ioaddr + EP_COMMAND);
00043         outw(RX_DISCARD_TOP_PACK, nic->ioaddr + EP_COMMAND);
00044         while (inw(nic->ioaddr + EP_STATUS) & S_COMMAND_IN_PROGRESS)
00045                 ;
00046         outw(TX_DISABLE, nic->ioaddr + EP_COMMAND);
00047         outw(STOP_TRANSCEIVER, nic->ioaddr + EP_COMMAND);
00048         udelay(1000);
00049         outw(RX_RESET, nic->ioaddr + EP_COMMAND);
00050         outw(TX_RESET, nic->ioaddr + EP_COMMAND);
00051         outw(C_INTR_LATCH, nic->ioaddr + EP_COMMAND);
00052         outw(SET_RD_0_MASK, nic->ioaddr + EP_COMMAND);
00053         outw(SET_INTR_MASK, nic->ioaddr + EP_COMMAND);
00054         outw(SET_RX_FILTER, nic->ioaddr + EP_COMMAND);
00055 
00056         /*
00057          * wait for reset to complete
00058          */
00059         while (inw(nic->ioaddr + EP_STATUS) & S_COMMAND_IN_PROGRESS)
00060                 ;
00061 
00062         GO_WINDOW(nic->ioaddr,0);
00063 
00064         /* Disable the card */
00065         outw(0, nic->ioaddr + EP_W0_CONFIG_CTRL);
00066 
00067         /* Configure IRQ to none */
00068         outw(SET_IRQ(0), nic->ioaddr + EP_W0_RESOURCE_CFG);
00069 }
00070 
00071 static void t509_enable ( struct nic *nic ) {
00072         int i;
00073 
00074         /* Enable the card */
00075         GO_WINDOW(nic->ioaddr,0);
00076         outw(ENABLE_DRQ_IRQ, nic->ioaddr + EP_W0_CONFIG_CTRL);
00077 
00078         GO_WINDOW(nic->ioaddr,2);
00079 
00080         /* Reload the ether_addr. */
00081         for (i = 0; i < ETH_ALEN; i++)
00082                 outb(nic->node_addr[i], nic->ioaddr + EP_W2_ADDR_0 + i);
00083 
00084         outw(RX_RESET, nic->ioaddr + EP_COMMAND);
00085         outw(TX_RESET, nic->ioaddr + EP_COMMAND);
00086 
00087         /* Window 1 is operating window */
00088         GO_WINDOW(nic->ioaddr,1);
00089         for (i = 0; i < 31; i++)
00090                 inb(nic->ioaddr + EP_W1_TX_STATUS);
00091 
00092         /* get rid of stray intr's */
00093         outw(ACK_INTR | 0xff, nic->ioaddr + EP_COMMAND);
00094 
00095         outw(SET_RD_0_MASK | S_5_INTS, nic->ioaddr + EP_COMMAND);
00096 
00097         outw(SET_INTR_MASK, nic->ioaddr + EP_COMMAND);
00098 
00099         outw(SET_RX_FILTER | FIL_GROUP | FIL_INDIVIDUAL | FIL_BRDCST,
00100              nic->ioaddr + EP_COMMAND);
00101 
00102         /* configure BNC */
00103         if (connector == bnc) {
00104                 outw(START_TRANSCEIVER, nic->ioaddr + EP_COMMAND);
00105                 udelay(1000);
00106         }
00107         /* configure UTP */
00108         else if (connector == utp) {
00109                 GO_WINDOW(nic->ioaddr,4);
00110                 outw(ENABLE_UTP, nic->ioaddr + EP_W4_MEDIA_TYPE);
00111                 mdelay(2000);   /* Give time for media to negotiate */
00112                 GO_WINDOW(nic->ioaddr,1);
00113         }
00114 
00115         /* start transceiver and receiver */
00116         outw(RX_ENABLE, nic->ioaddr + EP_COMMAND);
00117         outw(TX_ENABLE, nic->ioaddr + EP_COMMAND);
00118 
00119         /* set early threshold for minimal packet length */
00120         outw(SET_RX_EARLY_THRESH | ETH_ZLEN, nic->ioaddr + EP_COMMAND);
00121         outw(SET_TX_START_THRESH | 16, nic->ioaddr + EP_COMMAND);
00122 }
00123 
00124 static void t509_reset ( struct nic *nic ) {
00125         t5x9_disable ( nic );
00126         t509_enable ( nic );
00127 }    
00128 
00129 /**************************************************************************
00130 ETH_TRANSMIT - Transmit a frame
00131 ***************************************************************************/
00132 static char padmap[] = {
00133         0, 3, 2, 1};
00134 
00135 static void t509_transmit(
00136 struct nic *nic,
00137 const char *d,                  /* Destination */
00138 unsigned int t,                 /* Type */
00139 unsigned int s,                 /* size */
00140 const char *p)                  /* Packet */
00141 {
00142         register unsigned int len;
00143         int pad;
00144         int status;
00145 
00146 #ifdef  EDEBUG
00147         printf("{l=%d,t=%hX}",s+ETH_HLEN,t);
00148 #endif
00149 
00150         /* swap bytes of type */
00151         t= htons(t);
00152 
00153         len=s+ETH_HLEN; /* actual length of packet */
00154         pad = padmap[len & 3];
00155 
00156         /*
00157         * The 3c509 automatically pads short packets to minimum ethernet length,
00158         * but we drop packets that are too large. Perhaps we should truncate
00159         * them instead?
00160         */
00161         if (len + pad > ETH_FRAME_LEN) {
00162                 return;
00163         }
00164 
00165         /* drop acknowledgements */
00166         while ((status=inb(nic->ioaddr + EP_W1_TX_STATUS)) & TXS_COMPLETE ) {
00167                 if (status & (TXS_UNDERRUN|TXS_MAX_COLLISION|TXS_STATUS_OVERFLOW)) {
00168                         outw(TX_RESET, nic->ioaddr + EP_COMMAND);
00169                         outw(TX_ENABLE, nic->ioaddr + EP_COMMAND);
00170                 }
00171                 outb(0x0, nic->ioaddr + EP_W1_TX_STATUS);
00172         }
00173 
00174         while (inw(nic->ioaddr + EP_W1_FREE_TX) < (unsigned short)len + pad + 4)
00175                 ; /* no room in FIFO */
00176 
00177         outw(len, nic->ioaddr + EP_W1_TX_PIO_WR_1);
00178         outw(0x0, nic->ioaddr + EP_W1_TX_PIO_WR_1);     /* Second dword meaningless */
00179 
00180         /* write packet */
00181         outsw(nic->ioaddr + EP_W1_TX_PIO_WR_1, d, ETH_ALEN/2);
00182         outsw(nic->ioaddr + EP_W1_TX_PIO_WR_1, nic->node_addr, ETH_ALEN/2);
00183         outw(t, nic->ioaddr + EP_W1_TX_PIO_WR_1);
00184         outsw(nic->ioaddr + EP_W1_TX_PIO_WR_1, p, s / 2);
00185         if (s & 1)
00186                 outb(*(p+s - 1), nic->ioaddr + EP_W1_TX_PIO_WR_1);
00187 
00188         while (pad--)
00189                 outb(0, nic->ioaddr + EP_W1_TX_PIO_WR_1);       /* Padding */
00190 
00191         /* wait for Tx complete */
00192         while((inw(nic->ioaddr + EP_STATUS) & S_COMMAND_IN_PROGRESS) != 0)
00193                 ;
00194 }
00195 
00196 /**************************************************************************
00197 ETH_POLL - Wait for a frame
00198 ***************************************************************************/
00199 static int t509_poll(struct nic *nic, int retrieve)
00200 {
00201         /* common variables */
00202         /* variables for 3C509 */
00203         short status, cst;
00204         register short rx_fifo;
00205 
00206         cst=inw(nic->ioaddr + EP_STATUS);
00207 
00208 #ifdef  EDEBUG
00209         if(cst & 0x1FFF)
00210                 printf("-%hX-",cst);
00211 #endif
00212 
00213         if( (cst & S_RX_COMPLETE)==0 ) {
00214                 /* acknowledge  everything */
00215                 outw(ACK_INTR| (cst & S_5_INTS), nic->ioaddr + EP_COMMAND);
00216                 outw(C_INTR_LATCH, nic->ioaddr + EP_COMMAND);
00217 
00218                 return 0;
00219         }
00220 
00221         status = inw(nic->ioaddr + EP_W1_RX_STATUS);
00222 #ifdef  EDEBUG
00223         printf("*%hX*",status);
00224 #endif
00225 
00226         if (status & ERR_RX) {
00227                 outw(RX_DISCARD_TOP_PACK, nic->ioaddr + EP_COMMAND);
00228                 return 0;
00229         }
00230 
00231         rx_fifo = status & RX_BYTES_MASK;
00232         if (rx_fifo==0)
00233                 return 0;
00234 
00235         if ( ! retrieve ) return 1;
00236 
00237                 /* read packet */
00238 #ifdef  EDEBUG
00239         printf("[l=%d",rx_fifo);
00240 #endif
00241         insw(nic->ioaddr + EP_W1_RX_PIO_RD_1, nic->packet, rx_fifo / 2);
00242         if(rx_fifo & 1)
00243                 nic->packet[rx_fifo-1]=inb(nic->ioaddr + EP_W1_RX_PIO_RD_1);
00244         nic->packetlen=rx_fifo;
00245 
00246         while(1) {
00247                 status = inw(nic->ioaddr + EP_W1_RX_STATUS);
00248 #ifdef  EDEBUG
00249                 printf("*%hX*",status);
00250 #endif
00251                 rx_fifo = status & RX_BYTES_MASK;
00252                 if(rx_fifo>0) {
00253                         insw(nic->ioaddr + EP_W1_RX_PIO_RD_1, nic->packet+nic->packetlen, rx_fifo / 2);
00254                         if(rx_fifo & 1)
00255                                 nic->packet[nic->packetlen+rx_fifo-1]=inb(nic->ioaddr + EP_W1_RX_PIO_RD_1);
00256                         nic->packetlen+=rx_fifo;
00257 #ifdef  EDEBUG
00258                         printf("+%d",rx_fifo);
00259 #endif
00260                 }
00261                 if(( status & RX_INCOMPLETE )==0) {
00262 #ifdef  EDEBUG
00263                         printf("=%d",nic->packetlen);
00264 #endif
00265                         break;
00266                 }
00267                 udelay(1000);   /* if incomplete wait 1 ms */
00268         }
00269         /* acknowledge reception of packet */
00270         outw(RX_DISCARD_TOP_PACK, nic->ioaddr + EP_COMMAND);
00271         while (inw(nic->ioaddr + EP_STATUS) & S_COMMAND_IN_PROGRESS)
00272                 ;
00273 #ifdef  EDEBUG
00274 {
00275         unsigned short type = 0;        /* used by EDEBUG */
00276         type = (nic->packet[12]<<8) | nic->packet[13];
00277         if(nic->packet[0]+nic->packet[1]+nic->packet[2]+nic->packet[3]+nic->packet[4]+
00278             nic->packet[5] == 0xFF*ETH_ALEN)
00279                 printf(",t=%hX,b]",type);
00280         else
00281                 printf(",t=%hX]",type);
00282 }
00283 #endif
00284         return (1);
00285 }
00286 
00287 /**************************************************************************
00288 ETH_IRQ - interrupt handling
00289 ***************************************************************************/
00290 static void t509_irq(struct nic *nic __unused, irq_action_t action __unused)
00291 {
00292   switch ( action ) {
00293   case DISABLE :
00294     break;
00295   case ENABLE :
00296     break;
00297   case FORCE :
00298     break;
00299   }
00300 }
00301 
00302 /*************************************************************************
00303         3Com 509 - specific routines
00304 **************************************************************************/
00305 
00306 static int eeprom_rdy ( uint16_t ioaddr ) {
00307         int i;
00308 
00309         for (i = 0; is_eeprom_busy(ioaddr) && i < MAX_EEPROMBUSY; i++);
00310         if (i >= MAX_EEPROMBUSY) {
00311                 /* printf("3c509: eeprom failed to come ready.\n"); */
00312                 /* memory in EPROM is tight */
00313                 /* printf("3c509: eeprom busy.\n"); */
00314                 return (0);
00315         }
00316         return (1);
00317 }
00318 
00319 /*
00320  * get_e: gets a 16 bits word from the EEPROM.
00321  */
00322 static int get_e ( uint16_t ioaddr, int offset ) {
00323         GO_WINDOW(ioaddr,0);
00324         if (!eeprom_rdy(ioaddr))
00325                 return (0xffff);
00326         outw(EEPROM_CMD_RD | offset, ioaddr + EP_W0_EEPROM_COMMAND);
00327         if (!eeprom_rdy(ioaddr))
00328                 return (0xffff);
00329         return (inw(ioaddr + EP_W0_EEPROM_DATA));
00330 }
00331 
00332 static struct nic_operations t509_operations = {
00333         .connect        = dummy_connect,
00334         .poll           = t509_poll,
00335         .transmit       = t509_transmit,
00336         .irq            = t509_irq,
00337 };
00338 
00339 /**************************************************************************
00340 ETH_PROBE - Look for an adapter
00341 ***************************************************************************/
00342 int t5x9_probe ( struct nic *nic,
00343                  uint16_t prod_id_check, uint16_t prod_id_mask ) {
00344         uint16_t prod_id;
00345         int i,j;
00346         unsigned short *p;
00347         
00348         /* Check product ID */
00349         prod_id = get_e ( nic->ioaddr, EEPROM_PROD_ID );
00350         if ( ( prod_id & prod_id_mask ) != prod_id_check ) {
00351                 printf ( "EEPROM Product ID is incorrect (%hx & %hx != %hx)\n",
00352                          prod_id, prod_id_mask, prod_id_check );
00353                 return 0;
00354         }
00355 
00356         /* test for presence of connectors */
00357         GO_WINDOW(nic->ioaddr,0);
00358         i = inw(nic->ioaddr + EP_W0_CONFIG_CTRL);
00359         j = (inw(nic->ioaddr + EP_W0_ADDRESS_CFG) >> 14) & 0x3;
00360 
00361         switch(j) {
00362         case 0:
00363                 if (i & IS_UTP) {
00364                         printf("10baseT\n");
00365                         connector = utp;
00366                 } else {
00367                         printf("10baseT not present\n");
00368                         return 0;
00369                 }
00370                 break;
00371         case 1:
00372                 if (i & IS_AUI) {
00373                         printf("10base5\n");
00374                 } else {
00375                         printf("10base5 not present\n");
00376                         return 0;
00377                 }
00378                 break;
00379         case 3:
00380                 if (i & IS_BNC) {
00381                         printf("10base2\n");
00382                         connector = bnc;
00383                 } else {
00384                         printf("10base2 not present\n");
00385                         return 0;
00386                 }
00387                 break;
00388         default:
00389                 printf("unknown connector\n");
00390                 return 0;
00391         }
00392 
00393         /*
00394         * Read the station address from the eeprom
00395         */
00396         p = (unsigned short *) nic->node_addr;
00397         for (i = 0; i < ETH_ALEN / 2; i++) {
00398                 p[i] = htons(get_e(nic->ioaddr,i));
00399                 GO_WINDOW(nic->ioaddr,2);
00400                 outw(ntohs(p[i]), nic->ioaddr + EP_W2_ADDR_0 + (i * 2));
00401         }
00402 
00403         DBG ( "Ethernet Address: %s\n", eth_ntoa ( nic->node_addr ) );
00404 
00405         t509_reset(nic);
00406 
00407         nic->nic_op = &t509_operations;
00408         return 1;
00409 
00410 }
00411 
00412 /*
00413  * Local variables:
00414  *  c-basic-offset: 8
00415  * End:
00416  */