1 /* $NetBSD: elink3.c,v 1.3 1998/02/16 11:26:36 drochner Exp $ */ 2 3 /* stripped down from freebsd:sys/i386/netboot/3c509.c */ 4 5 /************************************************************************** 6 NETBOOT - BOOTP/TFTP Bootstrap Program 7 8 Author: Martin Renters. 9 Date: Mar 22 1995 10 11 This code is based heavily on David Greenman's if_ed.c driver and 12 Andres Vega Garcia's if_ep.c driver. 13 14 Copyright (C) 1993-1994, David Greenman, Martin Renters. 15 Copyright (C) 1993-1995, Andres Vega Garcia. 16 Copyright (C) 1995, Serge Babkin. 17 This software may be used, modified, copied, distributed, and sold, in 18 both source and binary form provided that the above copyright and these 19 terms are retained. Under no circumstances are the authors responsible for 20 the proper functioning of this software, nor do the authors assume any 21 responsibility for damages incurred with its use. 22 23 3c509 support added by Serge Babkin (babkin@hq.icb.chel.su) 24 25 3c509.c,v 1.2 1995/05/30 07:58:52 rgrimes Exp 26 27 ***************************************************************************/ 28 29 #include <sys/types.h> 30 #include <machine/pio.h> 31 32 #include <lib/libsa/stand.h> 33 34 #include <libi386.h> 35 36 #include "etherdrv.h" 37 #include "3c509.h" 38 39 extern unsigned short eth_base; 40 41 extern u_char eth_myaddr[6]; 42 43 void epstop() 44 { 45 /* stop card */ 46 outw(BASE + EP_COMMAND, RX_DISABLE); 47 outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); 48 while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); 49 50 outw(BASE + EP_COMMAND, TX_DISABLE); 51 outw(BASE + EP_COMMAND, STOP_TRANSCEIVER); 52 53 outw(BASE + EP_COMMAND, RX_RESET); 54 outw(BASE + EP_COMMAND, TX_RESET); 55 56 outw(BASE + EP_COMMAND, C_INTR_LATCH); 57 outw(BASE + EP_COMMAND, SET_RD_0_MASK); 58 outw(BASE + EP_COMMAND, SET_INTR_MASK); 59 outw(BASE + EP_COMMAND, SET_RX_FILTER); 60 } 61 62 void EtherStop() 63 { 64 epstop(); 65 outw(BASE + EP_COMMAND, GLOBAL_RESET); 66 delay(100000); 67 } 68 69 /************************************************************************** 70 ETH_RESET - Reset adapter 71 ***************************************************************************/ 72 void epreset() 73 { 74 int i; 75 76 /*********************************************************** 77 Reset 3Com 509 card 78 *************************************************************/ 79 80 epstop(); 81 82 /* 83 * initialize card 84 */ 85 while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); 86 87 GO_WINDOW(0); 88 89 /* Disable the card */ 90 outw(BASE + EP_W0_CONFIG_CTRL, 0); 91 92 /* Configure IRQ to none */ 93 outw(BASE + EP_W0_RESOURCE_CFG, SET_IRQ(0)); 94 95 /* Enable the card */ 96 outw(BASE + EP_W0_CONFIG_CTRL, ENABLE_DRQ_IRQ); 97 98 GO_WINDOW(2); 99 100 /* Reload the ether_addr. */ 101 for (i = 0; i < 6; i++) 102 outb(BASE + EP_W2_ADDR_0 + i, eth_myaddr[i]); 103 104 outw(BASE + EP_COMMAND, RX_RESET); 105 outw(BASE + EP_COMMAND, TX_RESET); 106 107 /* Window 1 is operating window */ 108 GO_WINDOW(1); 109 for (i = 0; i < 31; i++) 110 inb(BASE + EP_W1_TX_STATUS); 111 112 /* get rid of stray intr's */ 113 outw(BASE + EP_COMMAND, ACK_INTR | 0xff); 114 115 outw(BASE + EP_COMMAND, SET_RD_0_MASK | S_5_INTS); 116 117 outw(BASE + EP_COMMAND, SET_INTR_MASK); 118 119 outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL | 120 FIL_BRDCST); 121 122 /* configure BNC */ 123 if(ether_medium == ETHERMEDIUM_BNC) { 124 outw(BASE + EP_COMMAND, START_TRANSCEIVER); 125 delay(1000); 126 } 127 /* configure UTP */ 128 if(ether_medium == ETHERMEDIUM_UTP) { 129 GO_WINDOW(4); 130 outw(BASE + EP_W4_MEDIA_TYPE, ENABLE_UTP); 131 GO_WINDOW(1); 132 } 133 134 /* start tranciever and receiver */ 135 outw(BASE + EP_COMMAND, RX_ENABLE); 136 outw(BASE + EP_COMMAND, TX_ENABLE); 137 138 /* set early threshold for minimal packet length */ 139 outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | 64); 140 141 outw(BASE + EP_COMMAND, SET_TX_START_THRESH | 16); 142 } 143 144 /************************************************************************** 145 ETH_TRANSMIT - Transmit a frame 146 ***************************************************************************/ 147 static const char padmap[] = { 148 0, 3, 2, 1}; 149 150 int EtherSend(pkt, len) 151 char *pkt; 152 int len; 153 { 154 int pad; 155 int status; 156 157 #ifdef EDEBUG 158 printf("{l=%d}", len); 159 #endif 160 161 pad = padmap[len & 3]; 162 163 /* 164 * The 3c509 automatically pads short packets to minimum ethernet length, 165 * but we drop packets that are too large. Perhaps we should truncate 166 * them instead? 167 */ 168 if (len + pad > ETHER_MAX_LEN) { 169 return -1; 170 } 171 172 /* drop acknowledgements */ 173 while(( status=inb(BASE + EP_W1_TX_STATUS) )& TXS_COMPLETE ) { 174 if(status & (TXS_UNDERRUN|TXS_MAX_COLLISION|TXS_STATUS_OVERFLOW)) { 175 outw(BASE + EP_COMMAND, TX_RESET); 176 outw(BASE + EP_COMMAND, TX_ENABLE); 177 } 178 179 outb(BASE + EP_W1_TX_STATUS, 0x0); 180 } 181 182 while (inw(BASE + EP_W1_FREE_TX) < len + pad + 4) { 183 /* no room in FIFO */ 184 } 185 186 outw(BASE + EP_W1_TX_PIO_WR_1, len); 187 outw(BASE + EP_W1_TX_PIO_WR_1, 0x0); /* Second dword meaningless */ 188 189 /* write packet */ 190 outsw(BASE + EP_W1_TX_PIO_WR_1, pkt, len / 2); 191 if (len & 1) 192 outb(BASE + EP_W1_TX_PIO_WR_1, *(pkt + len - 1)); 193 194 while (pad--) 195 outb(BASE + EP_W1_TX_PIO_WR_1, 0); /* Padding */ 196 197 /* timeout after sending */ 198 delay(1000); 199 return(len); 200 } 201 202 /************************************************************************** 203 ETH_POLL - Wait for a frame 204 ***************************************************************************/ 205 int EtherReceive(pkt, maxlen) 206 char *pkt; 207 int maxlen; 208 { 209 /* common variables */ 210 int len; 211 /* variables for 3C509 */ 212 short status, cst; 213 register short rx_fifo; 214 215 cst=inw(BASE + EP_STATUS); 216 217 #ifdef EDEBUG 218 if(cst & 0x1FFF) 219 printf("-%x-",cst); 220 #endif 221 222 if( (cst & (S_RX_COMPLETE|S_RX_EARLY) )==0 ) { 223 /* acknowledge everything */ 224 outw(BASE + EP_COMMAND, ACK_INTR| (cst & S_5_INTS)); 225 outw(BASE + EP_COMMAND, C_INTR_LATCH); 226 227 return 0; 228 } 229 230 status = inw(BASE + EP_W1_RX_STATUS); 231 #ifdef EDEBUG 232 printf("*%x*",status); 233 #endif 234 235 if (status & ERR_RX) { 236 outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); 237 return 0; 238 } 239 240 rx_fifo = status & RX_BYTES_MASK; 241 if (rx_fifo==0) 242 return 0; 243 244 if (rx_fifo > maxlen) goto zulang; 245 246 /* read packet */ 247 #ifdef EDEBUG 248 printf("[l=%d",rx_fifo); 249 #endif 250 insw(BASE + EP_W1_RX_PIO_RD_1, pkt, rx_fifo / 2); 251 if(rx_fifo & 1) 252 pkt[rx_fifo-1]=inb(BASE + EP_W1_RX_PIO_RD_1); 253 len=rx_fifo; 254 255 while(1) { 256 status = inw(BASE + EP_W1_RX_STATUS); 257 #ifdef EDEBUG 258 printf("*%x*",status); 259 #endif 260 rx_fifo = status & RX_BYTES_MASK; 261 262 if(rx_fifo>0) { 263 if((len + rx_fifo) > maxlen) goto zulang; 264 265 insw(BASE + EP_W1_RX_PIO_RD_1, pkt+len, rx_fifo / 2); 266 if(rx_fifo & 1) 267 pkt[len+rx_fifo-1]=inb(BASE + EP_W1_RX_PIO_RD_1); 268 len+=rx_fifo; 269 #ifdef EDEBUG 270 printf("+%d",rx_fifo); 271 #endif 272 } 273 274 if(( status & RX_INCOMPLETE )==0) { 275 #ifdef EDEBUG 276 printf("=%d",len); 277 #endif 278 break; 279 } 280 281 delay(1000); 282 } 283 284 /* acknowledge reception of packet */ 285 outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); 286 while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); 287 288 return len; 289 290 zulang: 291 outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); 292 while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); 293 return 0; 294 } 295 296 /************************************************************************* 297 3Com 509 - specific routines 298 **************************************************************************/ 299 300 static int 301 eeprom_rdy() 302 { 303 int i; 304 305 for (i = 0; is_eeprom_busy(IS_BASE) && i < MAX_EEPROMBUSY; i++); 306 if (i >= MAX_EEPROMBUSY) { 307 printf("3c509: eeprom failed to come ready.\r\n"); 308 return (0); 309 } 310 return (1); 311 } 312 313 /* 314 * get_e: gets a 16 bits word from the EEPROM. we must have set the window 315 * before 316 */ 317 int 318 ep_get_e(offset) 319 int offset; 320 { 321 if (!eeprom_rdy()) 322 return (0xffff); 323 outw(IS_BASE + EP_W0_EEPROM_COMMAND, EEPROM_CMD_RD | offset); 324 if (!eeprom_rdy()) 325 return (0xffff); 326 return (inw(IS_BASE + EP_W0_EEPROM_DATA)); 327 } 328