337 lines
7.4 KiB
C
337 lines
7.4 KiB
C
|
/* $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 <sys/types.h>
|
||
|
#include <machine/pio.h>
|
||
|
|
||
|
#include <lib/libsa/stand.h>
|
||
|
|
||
|
#include <libi386.h>
|
||
|
|
||
|
#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);
|
||
|
}
|