Home | History | Annotate | Download | only in net
      1 /**************************************************************************
      2 ETHERBOOT -  BOOTP/TFTP Bootstrap Program
      3 
      4 Author: Martin Renters.
      5   Date: Mar 22 1995
      6 
      7  This code is based heavily on David Greenman's if_ed.c driver and
      8   Andres Vega Garcia's if_ep.c driver.
      9 
     10  Copyright (C) 1993-1994, David Greenman, Martin Renters.
     11  Copyright (C) 1993-1995, Andres Vega Garcia.
     12  Copyright (C) 1995, Serge Babkin.
     13   This software may be used, modified, copied, distributed, and sold, in
     14   both source and binary form provided that the above copyright and these
     15   terms are retained. Under no circumstances are the authors responsible for
     16   the proper functioning of this software, nor do the authors assume any
     17   responsibility for damages incurred with its use.
     18 
     19 3c509 support added by Serge Babkin (babkin (at) hq.icb.chel.su)
     20 
     21 $Id$
     22 
     23 ***************************************************************************/
     24 
     25 FILE_LICENCE ( BSD2 );
     26 
     27 /* #define EDEBUG */
     28 
     29 #include <gpxe/ethernet.h>
     30 #include "etherboot.h"
     31 #include "nic.h"
     32 #include <gpxe/isa.h>
     33 #include "3c509.h"
     34 
     35 static enum { none, bnc, utp } connector = none;	/* for 3C509 */
     36 
     37 /**************************************************************************
     38 ETH_RESET - Reset adapter
     39 ***************************************************************************/
     40 void t5x9_disable ( struct nic *nic ) {
     41 	/* stop card */
     42 	outw(RX_DISABLE, nic->ioaddr + EP_COMMAND);
     43 	outw(RX_DISCARD_TOP_PACK, nic->ioaddr + EP_COMMAND);
     44 	while (inw(nic->ioaddr + EP_STATUS) & S_COMMAND_IN_PROGRESS)
     45 		;
     46 	outw(TX_DISABLE, nic->ioaddr + EP_COMMAND);
     47 	outw(STOP_TRANSCEIVER, nic->ioaddr + EP_COMMAND);
     48 	udelay(1000);
     49 	outw(RX_RESET, nic->ioaddr + EP_COMMAND);
     50 	outw(TX_RESET, nic->ioaddr + EP_COMMAND);
     51 	outw(C_INTR_LATCH, nic->ioaddr + EP_COMMAND);
     52 	outw(SET_RD_0_MASK, nic->ioaddr + EP_COMMAND);
     53 	outw(SET_INTR_MASK, nic->ioaddr + EP_COMMAND);
     54 	outw(SET_RX_FILTER, nic->ioaddr + EP_COMMAND);
     55 
     56 	/*
     57 	 * wait for reset to complete
     58 	 */
     59 	while (inw(nic->ioaddr + EP_STATUS) & S_COMMAND_IN_PROGRESS)
     60 		;
     61 
     62 	GO_WINDOW(nic->ioaddr,0);
     63 
     64 	/* Disable the card */
     65 	outw(0, nic->ioaddr + EP_W0_CONFIG_CTRL);
     66 
     67 	/* Configure IRQ to none */
     68 	outw(SET_IRQ(0), nic->ioaddr + EP_W0_RESOURCE_CFG);
     69 }
     70 
     71 static void t509_enable ( struct nic *nic ) {
     72 	int i;
     73 
     74 	/* Enable the card */
     75 	GO_WINDOW(nic->ioaddr,0);
     76 	outw(ENABLE_DRQ_IRQ, nic->ioaddr + EP_W0_CONFIG_CTRL);
     77 
     78 	GO_WINDOW(nic->ioaddr,2);
     79 
     80 	/* Reload the ether_addr. */
     81 	for (i = 0; i < ETH_ALEN; i++)
     82 		outb(nic->node_addr[i], nic->ioaddr + EP_W2_ADDR_0 + i);
     83 
     84 	outw(RX_RESET, nic->ioaddr + EP_COMMAND);
     85 	outw(TX_RESET, nic->ioaddr + EP_COMMAND);
     86 
     87 	/* Window 1 is operating window */
     88 	GO_WINDOW(nic->ioaddr,1);
     89 	for (i = 0; i < 31; i++)
     90 		inb(nic->ioaddr + EP_W1_TX_STATUS);
     91 
     92 	/* get rid of stray intr's */
     93 	outw(ACK_INTR | 0xff, nic->ioaddr + EP_COMMAND);
     94 
     95 	outw(SET_RD_0_MASK | S_5_INTS, nic->ioaddr + EP_COMMAND);
     96 
     97 	outw(SET_INTR_MASK, nic->ioaddr + EP_COMMAND);
     98 
     99 	outw(SET_RX_FILTER | FIL_GROUP | FIL_INDIVIDUAL | FIL_BRDCST,
    100 	     nic->ioaddr + EP_COMMAND);
    101 
    102 	/* configure BNC */
    103 	if (connector == bnc) {
    104 		outw(START_TRANSCEIVER, nic->ioaddr + EP_COMMAND);
    105 		udelay(1000);
    106 	}
    107 	/* configure UTP */
    108 	else if (connector == utp) {
    109 		GO_WINDOW(nic->ioaddr,4);
    110 		outw(ENABLE_UTP, nic->ioaddr + EP_W4_MEDIA_TYPE);
    111 		sleep(2);	/* Give time for media to negotiate */
    112 		GO_WINDOW(nic->ioaddr,1);
    113 	}
    114 
    115 	/* start transceiver and receiver */
    116 	outw(RX_ENABLE, nic->ioaddr + EP_COMMAND);
    117 	outw(TX_ENABLE, nic->ioaddr + EP_COMMAND);
    118 
    119 	/* set early threshold for minimal packet length */
    120 	outw(SET_RX_EARLY_THRESH | ETH_ZLEN, nic->ioaddr + EP_COMMAND);
    121 	outw(SET_TX_START_THRESH | 16, nic->ioaddr + EP_COMMAND);
    122 }
    123 
    124 static void t509_reset ( struct nic *nic ) {
    125 	t5x9_disable ( nic );
    126 	t509_enable ( nic );
    127 }
    128 
    129 /**************************************************************************
    130 ETH_TRANSMIT - Transmit a frame
    131 ***************************************************************************/
    132 static char padmap[] = {
    133 	0, 3, 2, 1};
    134 
    135 static void t509_transmit(
    136 struct nic *nic,
    137 const char *d,			/* Destination */
    138 unsigned int t,			/* Type */
    139 unsigned int s,			/* size */
    140 const char *p)			/* Packet */
    141 {
    142 	register unsigned int len;
    143 	int pad;
    144 	int status;
    145 
    146 #ifdef	EDEBUG
    147 	printf("{l=%d,t=%hX}",s+ETH_HLEN,t);
    148 #endif
    149 
    150 	/* swap bytes of type */
    151 	t= htons(t);
    152 
    153 	len=s+ETH_HLEN; /* actual length of packet */
    154 	pad = padmap[len & 3];
    155 
    156 	/*
    157 	* The 3c509 automatically pads short packets to minimum ethernet length,
    158 	* but we drop packets that are too large. Perhaps we should truncate
    159 	* them instead?
    160 	*/
    161 	if (len + pad > ETH_FRAME_LEN) {
    162 		return;
    163 	}
    164 
    165 	/* drop acknowledgements */
    166 	while ((status=inb(nic->ioaddr + EP_W1_TX_STATUS)) & TXS_COMPLETE ) {
    167 		if (status & (TXS_UNDERRUN|TXS_MAX_COLLISION|TXS_STATUS_OVERFLOW)) {
    168 			outw(TX_RESET, nic->ioaddr + EP_COMMAND);
    169 			outw(TX_ENABLE, nic->ioaddr + EP_COMMAND);
    170 		}
    171 		outb(0x0, nic->ioaddr + EP_W1_TX_STATUS);
    172 	}
    173 
    174 	while (inw(nic->ioaddr + EP_W1_FREE_TX) < (unsigned short)len + pad + 4)
    175 		; /* no room in FIFO */
    176 
    177 	outw(len, nic->ioaddr + EP_W1_TX_PIO_WR_1);
    178 	outw(0x0, nic->ioaddr + EP_W1_TX_PIO_WR_1);	/* Second dword meaningless */
    179 
    180 	/* write packet */
    181 	outsw(nic->ioaddr + EP_W1_TX_PIO_WR_1, d, ETH_ALEN/2);
    182 	outsw(nic->ioaddr + EP_W1_TX_PIO_WR_1, nic->node_addr, ETH_ALEN/2);
    183 	outw(t, nic->ioaddr + EP_W1_TX_PIO_WR_1);
    184 	outsw(nic->ioaddr + EP_W1_TX_PIO_WR_1, p, s / 2);
    185 	if (s & 1)
    186 		outb(*(p+s - 1), nic->ioaddr + EP_W1_TX_PIO_WR_1);
    187 
    188 	while (pad--)
    189 		outb(0, nic->ioaddr + EP_W1_TX_PIO_WR_1);	/* Padding */
    190 
    191 	/* wait for Tx complete */
    192 	while((inw(nic->ioaddr + EP_STATUS) & S_COMMAND_IN_PROGRESS) != 0)
    193 		;
    194 }
    195 
    196 /**************************************************************************
    197 ETH_POLL - Wait for a frame
    198 ***************************************************************************/
    199 static int t509_poll(struct nic *nic, int retrieve)
    200 {
    201 	/* common variables */
    202 	/* variables for 3C509 */
    203 	short status, cst;
    204 	register short rx_fifo;
    205 
    206 	cst=inw(nic->ioaddr + EP_STATUS);
    207 
    208 #ifdef	EDEBUG
    209 	if(cst & 0x1FFF)
    210 		printf("-%hX-",cst);
    211 #endif
    212 
    213 	if( (cst & S_RX_COMPLETE)==0 ) {
    214 		/* acknowledge  everything */
    215 		outw(ACK_INTR| (cst & S_5_INTS), nic->ioaddr + EP_COMMAND);
    216 		outw(C_INTR_LATCH, nic->ioaddr + EP_COMMAND);
    217 
    218 		return 0;
    219 	}
    220 
    221 	status = inw(nic->ioaddr + EP_W1_RX_STATUS);
    222 #ifdef	EDEBUG
    223 	printf("*%hX*",status);
    224 #endif
    225 
    226 	if (status & ERR_RX) {
    227 		outw(RX_DISCARD_TOP_PACK, nic->ioaddr + EP_COMMAND);
    228 		return 0;
    229 	}
    230 
    231 	rx_fifo = status & RX_BYTES_MASK;
    232 	if (rx_fifo==0)
    233 		return 0;
    234 
    235 	if ( ! retrieve ) return 1;
    236 
    237 		/* read packet */
    238 #ifdef	EDEBUG
    239 	printf("[l=%d",rx_fifo);
    240 #endif
    241 	insw(nic->ioaddr + EP_W1_RX_PIO_RD_1, nic->packet, rx_fifo / 2);
    242 	if(rx_fifo & 1)
    243 		nic->packet[rx_fifo-1]=inb(nic->ioaddr + EP_W1_RX_PIO_RD_1);
    244 	nic->packetlen=rx_fifo;
    245 
    246 	while(1) {
    247 		status = inw(nic->ioaddr + EP_W1_RX_STATUS);
    248 #ifdef	EDEBUG
    249 		printf("*%hX*",status);
    250 #endif
    251 		rx_fifo = status & RX_BYTES_MASK;
    252 		if(rx_fifo>0) {
    253 			insw(nic->ioaddr + EP_W1_RX_PIO_RD_1, nic->packet+nic->packetlen, rx_fifo / 2);
    254 			if(rx_fifo & 1)
    255 				nic->packet[nic->packetlen+rx_fifo-1]=inb(nic->ioaddr + EP_W1_RX_PIO_RD_1);
    256 			nic->packetlen+=rx_fifo;
    257 #ifdef	EDEBUG
    258 			printf("+%d",rx_fifo);
    259 #endif
    260 		}
    261 		if(( status & RX_INCOMPLETE )==0) {
    262 #ifdef	EDEBUG
    263 			printf("=%d",nic->packetlen);
    264 #endif
    265 			break;
    266 		}
    267 		udelay(1000);	/* if incomplete wait 1 ms */
    268 	}
    269 	/* acknowledge reception of packet */
    270 	outw(RX_DISCARD_TOP_PACK, nic->ioaddr + EP_COMMAND);
    271 	while (inw(nic->ioaddr + EP_STATUS) & S_COMMAND_IN_PROGRESS)
    272 		;
    273 #ifdef	EDEBUG
    274 {
    275 	unsigned short type = 0;	/* used by EDEBUG */
    276 	type = (nic->packet[12]<<8) | nic->packet[13];
    277 	if(nic->packet[0]+nic->packet[1]+nic->packet[2]+nic->packet[3]+nic->packet[4]+
    278 	    nic->packet[5] == 0xFF*ETH_ALEN)
    279 		printf(",t=%hX,b]",type);
    280 	else
    281 		printf(",t=%hX]",type);
    282 }
    283 #endif
    284 	return (1);
    285 }
    286 
    287 /**************************************************************************
    288 ETH_IRQ - interrupt handling
    289 ***************************************************************************/
    290 static void t509_irq(struct nic *nic __unused, irq_action_t action __unused)
    291 {
    292   switch ( action ) {
    293   case DISABLE :
    294     break;
    295   case ENABLE :
    296     break;
    297   case FORCE :
    298     break;
    299   }
    300 }
    301 
    302 /*************************************************************************
    303 	3Com 509 - specific routines
    304 **************************************************************************/
    305 
    306 static int eeprom_rdy ( uint16_t ioaddr ) {
    307 	int i;
    308 
    309 	for (i = 0; is_eeprom_busy(ioaddr) && i < MAX_EEPROMBUSY; i++);
    310 	if (i >= MAX_EEPROMBUSY) {
    311 		/* printf("3c509: eeprom failed to come ready.\n"); */
    312 		/* memory in EPROM is tight */
    313 		/* printf("3c509: eeprom busy.\n"); */
    314 		return (0);
    315 	}
    316 	return (1);
    317 }
    318 
    319 /*
    320  * get_e: gets a 16 bits word from the EEPROM.
    321  */
    322 static int get_e ( uint16_t ioaddr, int offset ) {
    323 	GO_WINDOW(ioaddr,0);
    324 	if (!eeprom_rdy(ioaddr))
    325 		return (0xffff);
    326 	outw(EEPROM_CMD_RD | offset, ioaddr + EP_W0_EEPROM_COMMAND);
    327 	if (!eeprom_rdy(ioaddr))
    328 		return (0xffff);
    329 	return (inw(ioaddr + EP_W0_EEPROM_DATA));
    330 }
    331 
    332 static struct nic_operations t509_operations = {
    333 	.connect	= dummy_connect,
    334 	.poll		= t509_poll,
    335 	.transmit	= t509_transmit,
    336 	.irq		= t509_irq,
    337 };
    338 
    339 /**************************************************************************
    340 ETH_PROBE - Look for an adapter
    341 ***************************************************************************/
    342 int t5x9_probe ( struct nic *nic,
    343 		 uint16_t prod_id_check, uint16_t prod_id_mask ) {
    344 	uint16_t prod_id;
    345 	int i,j;
    346 	unsigned short *p;
    347 
    348 	/* Check product ID */
    349 	prod_id = get_e ( nic->ioaddr, EEPROM_PROD_ID );
    350 	if ( ( prod_id & prod_id_mask ) != prod_id_check ) {
    351 		printf ( "EEPROM Product ID is incorrect (%hx & %hx != %hx)\n",
    352 			 prod_id, prod_id_mask, prod_id_check );
    353 		return 0;
    354 	}
    355 
    356 	/* test for presence of connectors */
    357 	GO_WINDOW(nic->ioaddr,0);
    358 	i = inw(nic->ioaddr + EP_W0_CONFIG_CTRL);
    359 	j = (inw(nic->ioaddr + EP_W0_ADDRESS_CFG) >> 14) & 0x3;
    360 
    361 	switch(j) {
    362 	case 0:
    363 		if (i & IS_UTP) {
    364 			printf("10baseT\n");
    365 			connector = utp;
    366 		} else {
    367 			printf("10baseT not present\n");
    368 			return 0;
    369 		}
    370 		break;
    371 	case 1:
    372 		if (i & IS_AUI) {
    373 			printf("10base5\n");
    374 		} else {
    375 			printf("10base5 not present\n");
    376 			return 0;
    377 		}
    378 		break;
    379 	case 3:
    380 		if (i & IS_BNC) {
    381 			printf("10base2\n");
    382 			connector = bnc;
    383 		} else {
    384 			printf("10base2 not present\n");
    385 			return 0;
    386 		}
    387 		break;
    388 	default:
    389 		printf("unknown connector\n");
    390 		return 0;
    391 	}
    392 
    393 	/*
    394 	* Read the station address from the eeprom
    395 	*/
    396 	p = (unsigned short *) nic->node_addr;
    397 	for (i = 0; i < ETH_ALEN / 2; i++) {
    398 		p[i] = htons(get_e(nic->ioaddr,i));
    399 		GO_WINDOW(nic->ioaddr,2);
    400 		outw(ntohs(p[i]), nic->ioaddr + EP_W2_ADDR_0 + (i * 2));
    401 	}
    402 
    403 	DBG ( "Ethernet Address: %s\n", eth_ntoa ( nic->node_addr ) );
    404 
    405 	t509_reset(nic);
    406 
    407 	nic->nic_op = &t509_operations;
    408 	return 1;
    409 
    410 }
    411 
    412 /*
    413  * Local variables:
    414  *  c-basic-offset: 8
    415  * End:
    416  */
    417