/* $NetBSD: elink3.c,v 1.4 2008/12/14 18:46:33 christos Exp $ */ /* stripped down from freebsd:sys/i386/netboot/3c509.c */ /************************************************************************** NETBOOT - BOOTP/TFTP Bootstrap Program Author: Martin Renters. Date: Mar 22 1995 This code is based heavily on David Greenman's if_ed.c driver and Andres Vega Garcia's if_ep.c driver. Copyright (C) 1993-1994, David Greenman, Martin Renters. Copyright (C) 1993-1995, Andres Vega Garcia. Copyright (C) 1995, Serge Babkin. This software may be used, modified, copied, distributed, and sold, in both source and binary form provided that the above copyright and these terms are retained. Under no circumstances are the authors responsible for the proper functioning of this software, nor do the authors assume any responsibility for damages incurred with its use. 3c509 support added by Serge Babkin (babkin@hq.icb.chel.su) 3c509.c,v 1.2 1995/05/30 07:58:52 rgrimes Exp ***************************************************************************/ #include #include #include #include #include "etherdrv.h" #include "3c509.h" extern unsigned short eth_base; extern u_char eth_myaddr[6]; void epstop(void) { /* stop card */ outw(BASE + EP_COMMAND, RX_DISABLE); outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); outw(BASE + EP_COMMAND, TX_DISABLE); outw(BASE + EP_COMMAND, STOP_TRANSCEIVER); outw(BASE + EP_COMMAND, RX_RESET); outw(BASE + EP_COMMAND, TX_RESET); outw(BASE + EP_COMMAND, C_INTR_LATCH); outw(BASE + EP_COMMAND, SET_RD_0_MASK); outw(BASE + EP_COMMAND, SET_INTR_MASK); outw(BASE + EP_COMMAND, SET_RX_FILTER); } void EtherStop(void) { epstop(); outw(BASE + EP_COMMAND, GLOBAL_RESET); delay(100000); } /************************************************************************** ETH_RESET - Reset adapter ***************************************************************************/ void epreset(void) { int i; /*********************************************************** Reset 3Com 509 card *************************************************************/ epstop(); /* * initialize card */ while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) continue; GO_WINDOW(0); /* Disable the card */ outw(BASE + EP_W0_CONFIG_CTRL, 0); /* Configure IRQ to none */ outw(BASE + EP_W0_RESOURCE_CFG, SET_IRQ(0)); /* Enable the card */ outw(BASE + EP_W0_CONFIG_CTRL, ENABLE_DRQ_IRQ); GO_WINDOW(2); /* Reload the ether_addr. */ for (i = 0; i < 6; i++) outb(BASE + EP_W2_ADDR_0 + i, eth_myaddr[i]); outw(BASE + EP_COMMAND, RX_RESET); outw(BASE + EP_COMMAND, TX_RESET); /* Window 1 is operating window */ GO_WINDOW(1); for (i = 0; i < 31; i++) inb(BASE + EP_W1_TX_STATUS); /* get rid of stray intr's */ outw(BASE + EP_COMMAND, ACK_INTR | 0xff); outw(BASE + EP_COMMAND, SET_RD_0_MASK | S_5_INTS); outw(BASE + EP_COMMAND, SET_INTR_MASK); outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL | FIL_BRDCST); /* configure BNC */ if (ether_medium == ETHERMEDIUM_BNC) { outw(BASE + EP_COMMAND, START_TRANSCEIVER); delay(1000); } /* configure UTP */ if (ether_medium == ETHERMEDIUM_UTP) { GO_WINDOW(4); outw(BASE + EP_W4_MEDIA_TYPE, ENABLE_UTP); GO_WINDOW(1); } /* start tranciever and receiver */ outw(BASE + EP_COMMAND, RX_ENABLE); outw(BASE + EP_COMMAND, TX_ENABLE); /* set early threshold for minimal packet length */ outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | 64); outw(BASE + EP_COMMAND, SET_TX_START_THRESH | 16); } /************************************************************************** ETH_TRANSMIT - Transmit a frame ***************************************************************************/ static const char padmap[] = { 0, 3, 2, 1}; int EtherSend(char *pkt, int len) { int pad; int status; #ifdef EDEBUG printf("{l=%d}", len); #endif pad = padmap[len & 3]; /* * The 3c509 automatically pads short packets to minimum ethernet length, * but we drop packets that are too large. Perhaps we should truncate * them instead? */ if (len + pad > ETHER_MAX_LEN) { return -1; } /* drop acknowledgements */ while ((status = inb(BASE + EP_W1_TX_STATUS)) & TXS_COMPLETE ) { if (status & (TXS_UNDERRUN | TXS_MAX_COLLISION | TXS_STATUS_OVERFLOW)) { outw(BASE + EP_COMMAND, TX_RESET); outw(BASE + EP_COMMAND, TX_ENABLE); } outb(BASE + EP_W1_TX_STATUS, 0x0); } while (inw(BASE + EP_W1_FREE_TX) < len + pad + 4) { /* no room in FIFO */ continue; } outw(BASE + EP_W1_TX_PIO_WR_1, len); outw(BASE + EP_W1_TX_PIO_WR_1, 0x0); /* Second dword meaningless */ /* write packet */ outsw(BASE + EP_W1_TX_PIO_WR_1, pkt, len / 2); if (len & 1) outb(BASE + EP_W1_TX_PIO_WR_1, *(pkt + len - 1)); while (pad--) outb(BASE + EP_W1_TX_PIO_WR_1, 0); /* Padding */ /* timeout after sending */ delay(1000); return len; } /************************************************************************** ETH_POLL - Wait for a frame ***************************************************************************/ int EtherReceive(char *pkt, int maxlen) { /* common variables */ int len; /* variables for 3C509 */ short status, cst; register short rx_fifo; cst = inw(BASE + EP_STATUS); #ifdef EDEBUG if (cst & 0x1FFF) printf("-%x-",cst); #endif if ((cst & (S_RX_COMPLETE|S_RX_EARLY)) == 0) { /* acknowledge everything */ outw(BASE + EP_COMMAND, ACK_INTR| (cst & S_5_INTS)); outw(BASE + EP_COMMAND, C_INTR_LATCH); return 0; } status = inw(BASE + EP_W1_RX_STATUS); #ifdef EDEBUG printf("*%x*",status); #endif if (status & ERR_RX) { outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); return 0; } rx_fifo = status & RX_BYTES_MASK; if (rx_fifo == 0) return 0; if (rx_fifo > maxlen) goto zulang; /* read packet */ #ifdef EDEBUG printf("[l=%d",rx_fifo); #endif insw(BASE + EP_W1_RX_PIO_RD_1, pkt, rx_fifo / 2); if (rx_fifo & 1) pkt[rx_fifo-1] = inb(BASE + EP_W1_RX_PIO_RD_1); len = rx_fifo; for (;;) { status = inw(BASE + EP_W1_RX_STATUS); #ifdef EDEBUG printf("*%x*",status); #endif rx_fifo = status & RX_BYTES_MASK; if (rx_fifo > 0) { if ((len + rx_fifo) > maxlen) goto zulang; insw(BASE + EP_W1_RX_PIO_RD_1, pkt + len, rx_fifo / 2); if (rx_fifo & 1) pkt[len + rx_fifo-1] = inb(BASE + EP_W1_RX_PIO_RD_1); len += rx_fifo; #ifdef EDEBUG printf("+%d",rx_fifo); #endif } if ((status & RX_INCOMPLETE) == 0) { #ifdef EDEBUG printf("=%d",len); #endif break; } delay(1000); } /* acknowledge reception of packet */ outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) continue; return len; zulang: outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) continue; return 0; } /************************************************************************* 3Com 509 - specific routines **************************************************************************/ static int eeprom_rdy(void) { int i; for (i = 0; is_eeprom_busy(IS_BASE) && i < MAX_EEPROMBUSY; i++); if (i >= MAX_EEPROMBUSY) { printf("3c509: eeprom failed to come ready.\r\n"); return 0; } return 1; } /* * get_e: gets a 16 bits word from the EEPROM. we must have set the window * before */ int ep_get_e(int offset) { if (!eeprom_rdy()) return 0xffff; outw(IS_BASE + EP_W0_EEPROM_COMMAND, EEPROM_CMD_RD | offset); if (!eeprom_rdy()) return 0xffff; return inw(IS_BASE + EP_W0_EEPROM_DATA); }