[e16e8f2] | 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@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 | */ |
---|