1 /* $NetBSD: elink3.c,v 1.4 2008/12/14 18:46:33 christos 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 44 epstop(void) 45 { 46 47 /* stop card */ 48 outw(BASE + EP_COMMAND, RX_DISABLE); 49 outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); 50 while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); 51 52 outw(BASE + EP_COMMAND, TX_DISABLE); 53 outw(BASE + EP_COMMAND, STOP_TRANSCEIVER); 54 55 outw(BASE + EP_COMMAND, RX_RESET); 56 outw(BASE + EP_COMMAND, TX_RESET); 57 58 outw(BASE + EP_COMMAND, C_INTR_LATCH); 59 outw(BASE + EP_COMMAND, SET_RD_0_MASK); 60 outw(BASE + EP_COMMAND, SET_INTR_MASK); 61 outw(BASE + EP_COMMAND, SET_RX_FILTER); 62 } 63 64 void 65 EtherStop(void) 66 { 67 68 epstop(); 69 outw(BASE + EP_COMMAND, GLOBAL_RESET); 70 delay(100000); 71 } 72 73 /************************************************************************** 74 ETH_RESET - Reset adapter 75 ***************************************************************************/ 76 void 77 epreset(void) 78 { 79 int i; 80 81 /*********************************************************** 82 Reset 3Com 509 card 83 *************************************************************/ 84 85 epstop(); 86 87 /* 88 * initialize card 89 */ 90 while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) 91 continue; 92 93 GO_WINDOW(0); 94 95 /* Disable the card */ 96 outw(BASE + EP_W0_CONFIG_CTRL, 0); 97 98 /* Configure IRQ to none */ 99 outw(BASE + EP_W0_RESOURCE_CFG, SET_IRQ(0)); 100 101 /* Enable the card */ 102 outw(BASE + EP_W0_CONFIG_CTRL, ENABLE_DRQ_IRQ); 103 104 GO_WINDOW(2); 105 106 /* Reload the ether_addr. */ 107 for (i = 0; i < 6; i++) 108 outb(BASE + EP_W2_ADDR_0 + i, eth_myaddr[i]); 109 110 outw(BASE + EP_COMMAND, RX_RESET); 111 outw(BASE + EP_COMMAND, TX_RESET); 112 113 /* Window 1 is operating window */ 114 GO_WINDOW(1); 115 for (i = 0; i < 31; i++) 116 inb(BASE + EP_W1_TX_STATUS); 117 118 /* get rid of stray intr's */ 119 outw(BASE + EP_COMMAND, ACK_INTR | 0xff); 120 121 outw(BASE + EP_COMMAND, SET_RD_0_MASK | S_5_INTS); 122 123 outw(BASE + EP_COMMAND, SET_INTR_MASK); 124 125 outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL | 126 FIL_BRDCST); 127 128 /* configure BNC */ 129 if (ether_medium == ETHERMEDIUM_BNC) { 130 outw(BASE + EP_COMMAND, START_TRANSCEIVER); 131 delay(1000); 132 } 133 /* configure UTP */ 134 if (ether_medium == ETHERMEDIUM_UTP) { 135 GO_WINDOW(4); 136 outw(BASE + EP_W4_MEDIA_TYPE, ENABLE_UTP); 137 GO_WINDOW(1); 138 } 139 140 /* start tranciever and receiver */ 141 outw(BASE + EP_COMMAND, RX_ENABLE); 142 outw(BASE + EP_COMMAND, TX_ENABLE); 143 144 /* set early threshold for minimal packet length */ 145 outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | 64); 146 147 outw(BASE + EP_COMMAND, SET_TX_START_THRESH | 16); 148 } 149 150 /************************************************************************** 151 ETH_TRANSMIT - Transmit a frame 152 ***************************************************************************/ 153 static const char padmap[] = { 154 0, 3, 2, 1}; 155 156 int 157 EtherSend(char *pkt, int len) 158 { 159 int pad; 160 int status; 161 162 #ifdef EDEBUG 163 printf("{l=%d}", len); 164 #endif 165 166 pad = padmap[len & 3]; 167 168 /* 169 * The 3c509 automatically pads short packets to minimum ethernet length, 170 * but we drop packets that are too large. Perhaps we should truncate 171 * them instead? 172 */ 173 if (len + pad > ETHER_MAX_LEN) { 174 return -1; 175 } 176 177 /* drop acknowledgements */ 178 while ((status = inb(BASE + EP_W1_TX_STATUS)) & TXS_COMPLETE ) { 179 if (status & (TXS_UNDERRUN | TXS_MAX_COLLISION | 180 TXS_STATUS_OVERFLOW)) { 181 outw(BASE + EP_COMMAND, TX_RESET); 182 outw(BASE + EP_COMMAND, TX_ENABLE); 183 } 184 185 outb(BASE + EP_W1_TX_STATUS, 0x0); 186 } 187 188 while (inw(BASE + EP_W1_FREE_TX) < len + pad + 4) { 189 /* no room in FIFO */ 190 continue; 191 } 192 193 outw(BASE + EP_W1_TX_PIO_WR_1, len); 194 outw(BASE + EP_W1_TX_PIO_WR_1, 0x0); /* Second dword meaningless */ 195 196 /* write packet */ 197 outsw(BASE + EP_W1_TX_PIO_WR_1, pkt, len / 2); 198 if (len & 1) 199 outb(BASE + EP_W1_TX_PIO_WR_1, *(pkt + len - 1)); 200 201 while (pad--) 202 outb(BASE + EP_W1_TX_PIO_WR_1, 0); /* Padding */ 203 204 /* timeout after sending */ 205 delay(1000); 206 return len; 207 } 208 209 /************************************************************************** 210 ETH_POLL - Wait for a frame 211 ***************************************************************************/ 212 int 213 EtherReceive(char *pkt, int maxlen) 214 { 215 /* common variables */ 216 int len; 217 /* variables for 3C509 */ 218 short status, cst; 219 register short rx_fifo; 220 221 cst = inw(BASE + EP_STATUS); 222 223 #ifdef EDEBUG 224 if (cst & 0x1FFF) 225 printf("-%x-",cst); 226 #endif 227 228 if ((cst & (S_RX_COMPLETE|S_RX_EARLY)) == 0) { 229 /* acknowledge everything */ 230 outw(BASE + EP_COMMAND, ACK_INTR| (cst & S_5_INTS)); 231 outw(BASE + EP_COMMAND, C_INTR_LATCH); 232 233 return 0; 234 } 235 236 status = inw(BASE + EP_W1_RX_STATUS); 237 #ifdef EDEBUG 238 printf("*%x*",status); 239 #endif 240 241 if (status & ERR_RX) { 242 outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); 243 return 0; 244 } 245 246 rx_fifo = status & RX_BYTES_MASK; 247 if (rx_fifo == 0) 248 return 0; 249 250 if (rx_fifo > maxlen) 251 goto zulang; 252 253 /* read packet */ 254 #ifdef EDEBUG 255 printf("[l=%d",rx_fifo); 256 #endif 257 insw(BASE + EP_W1_RX_PIO_RD_1, pkt, rx_fifo / 2); 258 if (rx_fifo & 1) 259 pkt[rx_fifo-1] = inb(BASE + EP_W1_RX_PIO_RD_1); 260 len = rx_fifo; 261 262 for (;;) { 263 status = inw(BASE + EP_W1_RX_STATUS); 264 #ifdef EDEBUG 265 printf("*%x*",status); 266 #endif 267 rx_fifo = status & RX_BYTES_MASK; 268 269 if (rx_fifo > 0) { 270 if ((len + rx_fifo) > maxlen) 271 goto zulang; 272 273 insw(BASE + EP_W1_RX_PIO_RD_1, pkt + len, rx_fifo / 2); 274 if (rx_fifo & 1) 275 pkt[len + rx_fifo-1] = inb(BASE + EP_W1_RX_PIO_RD_1); 276 len += rx_fifo; 277 #ifdef EDEBUG 278 printf("+%d",rx_fifo); 279 #endif 280 } 281 282 if ((status & RX_INCOMPLETE) == 0) { 283 #ifdef EDEBUG 284 printf("=%d",len); 285 #endif 286 break; 287 } 288 289 delay(1000); 290 } 291 292 /* acknowledge reception of packet */ 293 outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); 294 while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) 295 continue; 296 297 return len; 298 299 zulang: 300 outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); 301 while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) 302 continue; 303 return 0; 304 } 305 306 /************************************************************************* 307 3Com 509 - specific routines 308 **************************************************************************/ 309 310 static int 311 eeprom_rdy(void) 312 { 313 int i; 314 315 for (i = 0; is_eeprom_busy(IS_BASE) && i < MAX_EEPROMBUSY; i++); 316 if (i >= MAX_EEPROMBUSY) { 317 printf("3c509: eeprom failed to come ready.\r\n"); 318 return 0; 319 } 320 return 1; 321 } 322 323 /* 324 * get_e: gets a 16 bits word from the EEPROM. we must have set the window 325 * before 326 */ 327 int 328 ep_get_e(int offset) 329 { 330 if (!eeprom_rdy()) 331 return 0xffff; 332 outw(IS_BASE + EP_W0_EEPROM_COMMAND, EEPROM_CMD_RD | offset); 333 if (!eeprom_rdy()) 334 return 0xffff; 335 return inw(IS_BASE + EP_W0_EEPROM_DATA); 336 } 337