David van Moolenbroek f7df02e747 libnetdriver: rewrite
This is a driver-breaking update to the netdriver library, which is
used by all network drivers.  The aim of this change is to make the
library more compatible with NetBSD, and in particular with various
features that are expected to be supported by the NetBSD userland.
The main changes made by this patch are the following:

- each network driver now has a NetBSD-style short device name;
- drivers are not expected to receive packets right after startup;
- extended support for receipt modes, including multicast lists;
- support for multiple parallel send, receive requests;
- embedding of I/O vectors in send and receive requests;
- support for capabilities, including checksum offloading;
- support for reporting link status updates to the TCP/IP stack;
- support for setting and retrieving media status;
- support for changing the hardware (MAC) address;
- support for NetBSD interface flags IFF_DEBUG, IFF_LINK[0-2];
- support for NetBSD error statistics;
- support for regular time-based ("tick") callbacks.

IMPORTANT: this patch applies a minimal update to the existing drivers
in order to make them work at all with the new netdriver library.  It
however does *not* change all drivers to make use of the new features.
In fact, strictly speaking, all drivers are now violating requirements
imposed by the new library in one way or another, most notably by
enabling packet receipt when starting the driver.  Changing all the
drivers to be compliant, and to support the newly added options, is
left to future patches.  The existing drivers should currently *not*
be taken as examples of how to implement a new network driver!

With that said, a few drivers have already been changed to make use of
some of the new features: fxp, e1000, rtl8139, and rtl8169 now report
link and media status, and the last three of those now support setting
the hardware MAC address on the fly.  In addition, dp8390 has been
changed to default to PCI autoconfiguration if no configuration is
specified through environment variables.

Change-Id: I4b3ea9c0b9bc25d5b0609c6ff256fb0db71cdc42
2017-04-30 13:15:28 +00:00

949 lines
28 KiB
C

/*
* lance.c
*
* This file contains a ethernet device driver for AMD LANCE based ethernet
* cards.
*
* Created: Jul 27, 2002 by Kazuya Kodama <kazuya@nii.ac.jp>
* Adapted for Minix 3: Sep 05, 2005 by Joren l'Ami <jwlami@cs.vu.nl>
*/
#define VERBOSE 0 /* Verbose debugging output */
#define LANCE_FKEY 0 /* Use function key to dump Lance stats */
#include <minix/drivers.h>
#include <minix/netdriver.h>
#include <assert.h>
#include <minix/syslib.h>
#include <minix/endpoint.h>
#include <machine/pci.h>
#include <minix/ds.h>
#include "lance.h"
static int do_init(unsigned int instance, netdriver_addr_t *addr,
uint32_t *caps, unsigned int *ticks);
static void ec_confaddr(netdriver_addr_t *addr, unsigned int instance);
static void ec_reinit(ether_card_t *ec);
static void ec_reset(ether_card_t *ec);
static void do_intr(unsigned int mask);
static void do_set_mode(unsigned int mode, const netdriver_addr_t *mcast_list,
unsigned int mcast_count);
static int do_send(struct netdriver_data *data, size_t size);
static ssize_t do_recv(struct netdriver_data *data, size_t max);
static void do_stop(void);
static void lance_dump(void);
static void do_other(const message *m_ptr, int ipc_status);
static void get_addressing(int devind, ether_card_t *ec);
static int lance_probe(ether_card_t *ec, unsigned int skip);
static void lance_init_hw(ether_card_t *ec, netdriver_addr_t *addr,
unsigned int instance);
/* Accesses Lance Control and Status Registers */
static u8_t in_byte(port_t port);
static u16_t in_word(port_t port);
static void out_word(port_t port, u16_t value);
static u16_t read_csr(port_t ioaddr, u16_t csrno);
static void write_csr(port_t ioaddr, u16_t csrno, u16_t value);
static ether_card_t ec_state;
/* --- LANCE --- */
/* General */
typedef uint32_t Address;
#define ETH_FRAME_LEN 1518
#define LANCE_MUST_PAD 0x00000001
#define LANCE_ENABLE_AUTOSELECT 0x00000002
#define LANCE_SELECT_PHONELINE 0x00000004
#define LANCE_MUST_UNRESET 0x00000008
static const struct lance_chip_type
{
int id_number;
const char *name;
int flags;
} chip_table[] = {
{0x0000, "LANCE 7990", /* Ancient lance chip. */
LANCE_MUST_PAD + LANCE_MUST_UNRESET},
{0x0003, "PCnet/ISA 79C960", /* 79C960 PCnet/ISA. */
LANCE_ENABLE_AUTOSELECT},
{0x2260, "PCnet/ISA+ 79C961", /* 79C961 PCnet/ISA+, Plug-n-Play. */
LANCE_ENABLE_AUTOSELECT},
{0x2420, "PCnet/PCI 79C970", /* 79C970 or 79C974 PCnet-SCSI, PCI. */
LANCE_ENABLE_AUTOSELECT},
{0x2430, "PCnet32", /* 79C965 PCnet for VL bus. */
LANCE_ENABLE_AUTOSELECT},
{0x2621, "PCnet/PCI-II 79C970A", /* 79C970A PCInetPCI II. */
LANCE_ENABLE_AUTOSELECT},
{0x2625, "PCnet-FAST III 79C973",/* 79C973 PCInet-FAST III. */
LANCE_ENABLE_AUTOSELECT},
{0x2626, "PCnet/HomePNA 79C978",
LANCE_ENABLE_AUTOSELECT|LANCE_SELECT_PHONELINE},
{0x0, "PCnet (unknown)",
LANCE_ENABLE_AUTOSELECT},
};
/* ############## for LANCE device ############## */
#define LANCE_ETH_ADDR 0x0
#define LANCE_DATA 0x10
#define LANCE_ADDR 0x12
#define LANCE_RESET 0x14
#define LANCE_BUS_IF 0x16
#define LANCE_TOTAL_SIZE 0x18
/* Use 2^4=16 {Rx,Tx} buffers */
#define LANCE_LOG_RX_BUFFERS 4
#define RX_RING_SIZE (1 << (LANCE_LOG_RX_BUFFERS))
#define RX_RING_MOD_MASK (RX_RING_SIZE - 1)
#define RX_RING_LEN_BITS ((LANCE_LOG_RX_BUFFERS) << 29)
#define LANCE_LOG_TX_BUFFERS 4
#define TX_RING_SIZE (1 << (LANCE_LOG_TX_BUFFERS))
#define TX_RING_MOD_MASK (TX_RING_SIZE - 1)
#define TX_RING_LEN_BITS ((LANCE_LOG_TX_BUFFERS) << 29)
/* for lance_interface */
struct lance_init_block
{
unsigned short mode;
unsigned char phys_addr[6];
unsigned long filter[2];
Address rx_ring;
Address tx_ring;
};
struct lance_rx_head
{
union {
Address base;
unsigned char addr[4];
} u;
short buf_length; /* 2s complement */
short msg_length;
};
struct lance_tx_head
{
union {
Address base;
unsigned char addr[4];
} u;
short buf_length; /* 2s complement */
short misc;
};
struct lance_interface
{
struct lance_init_block init_block;
struct lance_rx_head rx_ring[RX_RING_SIZE];
struct lance_tx_head tx_ring[TX_RING_SIZE];
unsigned char rbuf[RX_RING_SIZE][ETH_FRAME_LEN];
unsigned char tbuf[TX_RING_SIZE][ETH_FRAME_LEN];
};
/* =============== global variables =============== */
/* AKA the stuff that really should have been in ether_card_t */
static struct lance_interface *lp;
#define LANCE_BUF_SIZE (sizeof(struct lance_interface))
static char *lance_buf = NULL;
static int rx_slot_nr = 0; /* Rx-slot number */
static int tx_slot_nr = 0; /* Tx-slot number */
static int cur_tx_slot_nr = 0; /* Tx-slot number */
static phys_bytes tx_ring_base[TX_RING_SIZE]; /* Tx-slot physical address */
static char isstored[TX_RING_SIZE]; /* Tx-slot in-use */
static const struct netdriver lance_table = {
.ndr_name = "le",
.ndr_init = do_init,
.ndr_stop = do_stop,
.ndr_set_mode = do_set_mode,
.ndr_recv = do_recv,
.ndr_send = do_send,
.ndr_intr = do_intr,
.ndr_other = do_other,
};
/*===========================================================================*
* main *
*===========================================================================*/
int main(int argc, char **argv)
{
env_setargs(argc, argv);
netdriver_task(&lance_table);
return 0;
}
/*===========================================================================*
* lance_dump *
*===========================================================================*/
static void lance_dump()
{
ether_card_t *ec;
int isr, csr;
unsigned short ioaddr;
printf("\n");
ec = &ec_state;
printf("lance driver %s:\n", netdriver_name());
ioaddr = ec->ec_port;
isr = read_csr(ioaddr, LANCE_CSR0);
printf("isr = 0x%x, mode = 0x%x\n", isr, ec->ec_mode);
printf("irq = %d\tioadr = 0x%x\n", ec->ec_irq, ec->ec_port);
csr = read_csr(ioaddr, LANCE_CSR0);
printf("CSR0: 0x%x\n", csr);
csr = read_csr(ioaddr, LANCE_CSR3);
printf("CSR3: 0x%x\n", csr);
csr = read_csr(ioaddr, LANCE_CSR4);
printf("CSR4: 0x%x\n", csr);
csr = read_csr(ioaddr, LANCE_CSR5);
printf("CSR5: 0x%x\n", csr);
csr = read_csr(ioaddr, LANCE_CSR15);
printf("CSR15: 0x%x\n", csr);
}
/*===========================================================================*
* do_other *
*===========================================================================*/
static void do_other(const message *m_ptr, int ipc_status)
{
if (is_ipc_notify(ipc_status) && m_ptr->m_source == TTY_PROC_NR)
lance_dump();
}
/*===========================================================================*
* ec_confaddr *
*===========================================================================*/
static void ec_confaddr(netdriver_addr_t *addr, unsigned int instance)
{
int i;
char eakey[16];
static char eafmt[]= "x:x:x:x:x:x";
long v;
/* User defined ethernet address? */
strlcpy(eakey, "LANCE0_EA", sizeof(eakey));
eakey[5] += instance;
for (i = 0; i < 6; i++)
{
v= addr->na_addr[i];
if (env_parse(eakey, eafmt, i, &v, 0x00L, 0xFFL) != EP_SET)
break;
addr->na_addr[i]= v;
}
if (i != 0 && i != 6)
{
/* It's all or nothing; force a panic. */
panic("invalid ethernet address supplied");
}
}
/*===========================================================================*
* do_init *
*===========================================================================*/
static int do_init(unsigned int instance, netdriver_addr_t *addr,
uint32_t *caps, unsigned int *ticks __unused)
{
/* Initialize the lance driver. */
ether_card_t *ec;
#if VERBOSE
int i;
#endif
#if LANCE_FKEY
int r, fkeys, sfkeys;
#endif
#if LANCE_FKEY
fkeys = sfkeys = 0;
bit_set( sfkeys, 7 );
if ( (r = fkey_map(&fkeys, &sfkeys)) != OK )
printf("Warning: lance couldn't observe Shift+F7 key: %d\n",r);
#endif
/* Initialize the driver state. */
ec= &ec_state;
memset(ec, 0, sizeof(*ec));
/* See if there is a matching card. */
if (!lance_probe(ec, instance))
return ENXIO;
/* Initialize the hardware. */
lance_init_hw(ec, addr, instance);
#if VERBOSE
printf("%s: Ethernet address ", netdriver_name());
for (i= 0; i < 6; i++)
printf("%x%c", addr->na_addr[i], i < 5 ? ':' : '\n');
#endif
*caps = NDEV_CAP_MCAST | NDEV_CAP_BCAST;
return OK;
}
/*===========================================================================*
* ec_reinit *
*===========================================================================*/
static void ec_reinit(ether_card_t *ec)
{
int i;
unsigned short ioaddr = ec->ec_port;
/* stop */
write_csr(ioaddr, LANCE_CSR0, LANCE_CSR0_STOP);
/* purge Tx-ring */
tx_slot_nr = cur_tx_slot_nr = 0;
for (i=0; i<TX_RING_SIZE; i++)
{
lp->tx_ring[i].u.base = 0;
isstored[i]=0;
}
/* re-init Rx-ring */
rx_slot_nr = 0;
for (i=0; i<RX_RING_SIZE; i++)
{
lp->rx_ring[i].buf_length = -ETH_FRAME_LEN;
lp->rx_ring[i].u.addr[3] |= 0x80;
}
/* Set 'Receive Mode' */
if (ec->ec_mode & NDEV_MODE_PROMISC)
{
write_csr(ioaddr, LANCE_CSR15, LANCE_CSR15_PROM);
}
else
{
if (ec->ec_mode &
(NDEV_MODE_BCAST | NDEV_MODE_MCAST_LIST | NDEV_MODE_MCAST_ALL))
{
write_csr(ioaddr, LANCE_CSR15, 0x0000);
}
else
{
write_csr(ioaddr, LANCE_CSR15, LANCE_CSR15_DRCVBC);
}
}
/* start && enable interrupt */
write_csr(ioaddr, LANCE_CSR0,
LANCE_CSR0_IDON|LANCE_CSR0_IENA|LANCE_CSR0_STRT);
return;
}
/*===========================================================================*
* do_set_mode *
*===========================================================================*/
static void do_set_mode(unsigned int mode,
const netdriver_addr_t *mcast_list __unused,
unsigned int mcast_count __unused)
{
ether_card_t *ec;
ec = &ec_state;
ec->ec_mode = mode;
ec_reinit(ec);
}
/*===========================================================================*
* do_intr *
*===========================================================================*/
static void do_intr(unsigned int __unused mask)
{
ether_card_t *ec;
int must_restart = 0;
int r, check, status;
int isr = 0x0000;
unsigned short ioaddr;
ec = &ec_state;
ioaddr = ec->ec_port;
for (;;)
{
#if VERBOSE
printf("ETH: Reading ISR...");
#endif
isr = read_csr(ioaddr, LANCE_CSR0);
if (isr & (LANCE_CSR0_ERR|LANCE_CSR0_RINT|LANCE_CSR0_TINT)) {
write_csr(ioaddr, LANCE_CSR0,
isr & ~(LANCE_CSR0_IENA|LANCE_CSR0_TDMD|LANCE_CSR0_STOP
|LANCE_CSR0_STRT|LANCE_CSR0_INIT) );
}
write_csr(ioaddr, LANCE_CSR0,
LANCE_CSR0_BABL|LANCE_CSR0_CERR|LANCE_CSR0_MISS|LANCE_CSR0_MERR
|LANCE_CSR0_IDON|LANCE_CSR0_IENA);
if ((isr & (LANCE_CSR0_TINT|LANCE_CSR0_RINT|LANCE_CSR0_MISS
|LANCE_CSR0_BABL|LANCE_CSR0_ERR)) == 0x0000)
{
#if VERBOSE
printf("OK\n");
#endif
break;
}
if (isr & LANCE_CSR0_MISS)
{
#if VERBOSE
printf("RX Missed Frame\n");
#endif
netdriver_stat_ierror(1);
}
if ((isr & LANCE_CSR0_BABL) || (isr & LANCE_CSR0_TINT))
{
if (isr & LANCE_CSR0_BABL)
{
#if VERBOSE
printf("TX Timeout\n");
#endif
netdriver_stat_oerror(1);
}
if (isr & LANCE_CSR0_TINT)
{
#if VERBOSE
printf("TX INT\n");
#endif
/* status check: restart if needed. */
status = lp->tx_ring[cur_tx_slot_nr].u.base;
/* did an error (UFLO, LCOL, LCAR, RTRY) occur? */
if (status & 0x40000000)
{
status = lp->tx_ring[cur_tx_slot_nr].misc;
netdriver_stat_oerror(1);
if (status & 0x4000) /* UFLO */
{
must_restart=1;
}
}
else
{
if (status & 0x18000000)
netdriver_stat_coll(1);
}
}
/* transmit a packet on the next slot if it exists. */
check = 0;
if (isstored[cur_tx_slot_nr]==1)
{
/* free the tx-slot just transmitted */
isstored[cur_tx_slot_nr]=0;
cur_tx_slot_nr = (cur_tx_slot_nr + 1) & TX_RING_MOD_MASK;
/* next tx-slot is ready? */
if (isstored[cur_tx_slot_nr]==1)
check=1;
else
check=0;
}
else
{
panic("got premature TX INT..");
}
if (check==1)
{
lp->tx_ring[cur_tx_slot_nr].u.addr[3] = 0x83;
write_csr(ioaddr, LANCE_CSR0, LANCE_CSR0_IENA|LANCE_CSR0_TDMD);
}
/* we set a buffered message in the slot if it exists. */
/* and transmit it, if needed. */
if (!must_restart)
netdriver_send();
}
if (isr & LANCE_CSR0_RINT)
{
#if VERBOSE
printf("RX INT\n");
#endif
netdriver_recv();
}
if (must_restart == 1)
{
#if VERBOSE
printf("ETH: restarting...\n");
#endif
ec_reset(ec);
}
}
/* reenable interrupts */
if ((r = sys_irqenable(&ec->ec_hook)) != OK)
panic("couldn't enable interrupt: %d", r);
}
/*===========================================================================*
* ec_reset *
*===========================================================================*/
static void ec_reset(ether_card_t *ec)
{
/* Stop/start the chip, and clear all RX,TX-slots. The spec says this type
* of reset should be done on MERR, UFLO, and TX BUFF errors. For now it is
* called only for UFLO and TX BUFF (which causes UFLO). MERR is not (yet?)
* handled. Also note that the PCnet spec says that the LANCE chip does not
* require resetting Rx/Tx, but PCnet does. We intend to support both here.
*/
unsigned short ioaddr = ec->ec_port;
int i;
/* stop */
write_csr(ioaddr, LANCE_CSR0, LANCE_CSR0_STOP);
/* start */
write_csr(ioaddr, LANCE_CSR0, LANCE_CSR0_STRT);
/* purge Tx-ring */
tx_slot_nr = cur_tx_slot_nr = 0;
for (i=0; i<TX_RING_SIZE; i++)
{
lp->tx_ring[i].u.base = 0;
isstored[i]=0;
}
/* re-init Rx-ring */
rx_slot_nr = 0;
for (i=0; i<RX_RING_SIZE; i++)
{
lp->rx_ring[i].buf_length = -ETH_FRAME_LEN;
lp->rx_ring[i].u.addr[3] |= 0x80;
}
/* store a buffered message on the slot if it exists */
netdriver_send();
}
/*===========================================================================*
* do_recv *
*===========================================================================*/
static ssize_t do_recv(struct netdriver_data *data, size_t max)
{
ether_card_t *ec;
vir_bytes length;
int packet_processed;
int status;
unsigned short ioaddr;
ec = &ec_state;
ioaddr = ec->ec_port;
/* we check all the received slots until find a properly received packet */
packet_processed = FALSE;
while (!packet_processed)
{
status = lp->rx_ring[rx_slot_nr].u.base >> 24;
/* is the slot marked as ready? */
if ( (status & 0x80) != 0x00 )
return SUSPEND; /* no */
/* did an error occur? */
if (status != 0x03)
{
if (status & 0x01)
netdriver_stat_ierror(1);
length = 0;
}
else
{
length = lp->rx_ring[rx_slot_nr].msg_length;
}
/* do we now have a valid packet? */
if (length > 0)
{
if (length > max)
length = max;
netdriver_copyout(data, 0, lp->rbuf[rx_slot_nr], length);
packet_processed = TRUE;
}
/* set up this slot again, and we move to the next slot */
lp->rx_ring[rx_slot_nr].buf_length = -ETH_FRAME_LEN;
lp->rx_ring[rx_slot_nr].u.addr[3] |= 0x80;
write_csr(ioaddr, LANCE_CSR0,
LANCE_CSR0_BABL|LANCE_CSR0_CERR|LANCE_CSR0_MISS
|LANCE_CSR0_MERR|LANCE_CSR0_IDON|LANCE_CSR0_IENA);
rx_slot_nr = (rx_slot_nr + 1) & RX_RING_MOD_MASK;
}
/* return the length of the packet we have received */
return length;
}
/*===========================================================================*
* do_send *
*===========================================================================*/
static int do_send(struct netdriver_data *data, size_t size)
{
int check;
ether_card_t *ec;
unsigned short ioaddr;
ec = &ec_state;
/* if all slots are used, this request must be deferred */
if (isstored[tx_slot_nr]==1)
return SUSPEND;
/* copy the packet to the slot on DMA address */
netdriver_copyin(data, 0, lp->tbuf[tx_slot_nr], size);
/* set-up for transmitting, and transmit it if needed. */
lp->tx_ring[tx_slot_nr].buf_length = -size;
lp->tx_ring[tx_slot_nr].misc = 0x0;
lp->tx_ring[tx_slot_nr].u.base = tx_ring_base[tx_slot_nr];
isstored[tx_slot_nr]=1;
if (cur_tx_slot_nr == tx_slot_nr)
check=1;
else
check=0;
tx_slot_nr = (tx_slot_nr + 1) & TX_RING_MOD_MASK;
if (check == 1)
{
ioaddr = ec->ec_port;
lp->tx_ring[cur_tx_slot_nr].u.addr[3] = 0x83;
write_csr(ioaddr, LANCE_CSR0, LANCE_CSR0_IENA|LANCE_CSR0_TDMD);
}
return OK;
}
/*===========================================================================*
* do_stop *
*===========================================================================*/
static void do_stop(void)
{
ether_card_t *ec;
unsigned short ioaddr;
ec = &ec_state;
ioaddr = ec->ec_port;
/* stop */
write_csr(ioaddr, LANCE_CSR0, LANCE_CSR0_STOP);
/* Reset */
in_word(ioaddr+LANCE_RESET);
}
/*===========================================================================*
* get_addressing *
*===========================================================================*/
static void get_addressing(int devind, ether_card_t *ec)
{
unsigned int ioaddr;
int reg, irq;
for (reg = PCI_BAR; reg <= PCI_BAR_6; reg += 4)
{
ioaddr = pci_attr_r32(devind, reg);
if ((ioaddr & PCI_BAR_IO_MASK) == 0 || (ioaddr & PCI_BAR_IO) == 0)
continue;
/* Strip the I/O address out of the returned value */
ioaddr &= PCI_BAR_IO_MASK;
ec->ec_port = ioaddr;
}
/* KK: Get the IRQ number */
irq = pci_attr_r8(devind, PCI_IPR);
if (irq)
irq = pci_attr_r8(devind, PCI_ILR);
ec->ec_irq = irq;
}
/*===========================================================================*
* lance_probe *
*===========================================================================*/
static int lance_probe(ether_card_t *ec, unsigned int skip)
{
unsigned short pci_cmd;
unsigned short ioaddr;
int lance_version, chip_version;
int devind, r;
u16_t vid, did;
pci_init();
r= pci_first_dev(&devind, &vid, &did);
if (r == 0)
return 0;
while (skip--)
{
r= pci_next_dev(&devind, &vid, &did);
if (!r)
return 0;
}
pci_reserve(devind);
get_addressing(devind, ec);
/* ===== Bus Master ? ===== */
pci_cmd = pci_attr_r32(devind, PCI_CR);
if (!(pci_cmd & PCI_CR_MAST_EN)) {
pci_cmd |= PCI_CR_MAST_EN;
pci_attr_w32(devind, PCI_CR, pci_cmd);
}
/* ===== Probe Details ===== */
ioaddr = ec->ec_port;
/* Reset */
in_word(ioaddr+LANCE_RESET);
if (read_csr(ioaddr, LANCE_CSR0) != LANCE_CSR0_STOP)
{
return 0;
}
/* Probe Chip Version */
out_word(ioaddr+LANCE_ADDR, 88); /* Get the version of the chip */
if (in_word(ioaddr+LANCE_ADDR) != 88)
lance_version = 0;
else
{
chip_version = read_csr(ioaddr, LANCE_CSR88);
chip_version |= read_csr(ioaddr, LANCE_CSR89) << 16;
if ((chip_version & 0xfff) != 0x3)
{
return 0;
}
chip_version = (chip_version >> 12) & 0xffff;
for (lance_version = 1; chip_table[lance_version].id_number != 0;
++lance_version)
if (chip_table[lance_version].id_number == chip_version)
break;
}
#if VERBOSE
printf("%s: %s at %X:%d\n",
netdriver_name(), chip_table[lance_version].name,
ec->ec_port, ec->ec_irq);
#endif
return lance_version;
}
/*===========================================================================*
* virt_to_bus *
*===========================================================================*/
static phys_bytes virt_to_bus(void *ptr)
{
phys_bytes value;
int r;
if ((r = sys_umap(SELF, VM_D, (vir_bytes)ptr, 4, &value)) != OK)
panic("sys_umap failed: %d", r);
return value;
}
/*===========================================================================*
* lance_init_hw *
*===========================================================================*/
static void lance_init_hw(ether_card_t *ec, netdriver_addr_t *addr,
unsigned int instance)
{
phys_bytes lance_buf_phys;
int i, r;
Address l;
unsigned short ioaddr = ec->ec_port;
/* ============= setup init_block(cf. lance_probe1) ================ */
/* make sure data structure is 8-byte aligned and below 16MB (for DMA) */
/* Allocate memory */
if ((lance_buf = alloc_contig(LANCE_BUF_SIZE, AC_ALIGN4K|AC_LOWER16M,
&lance_buf_phys)) == NULL)
panic("alloc_contig failed: %d", LANCE_BUF_SIZE);
l = (vir_bytes)lance_buf;
lp = (struct lance_interface *)l;
/* disable Tx and Rx */
lp->init_block.mode = LANCE_CSR15_DTX|LANCE_CSR15_DRX;
lp->init_block.filter[0] = lp->init_block.filter[1] = 0x0;
/* using multiple Rx/Tx buffer */
lp->init_block.rx_ring
= (virt_to_bus(&lp->rx_ring) & 0xffffff) | RX_RING_LEN_BITS;
lp->init_block.tx_ring
= (virt_to_bus(&lp->tx_ring) & 0xffffff) | TX_RING_LEN_BITS;
l = virt_to_bus(&lp->init_block);
write_csr(ioaddr, LANCE_CSR1, (unsigned short)l);
write_csr(ioaddr, LANCE_CSR2, (unsigned short)(l >> 16));
write_csr(ioaddr, LANCE_CSR4,
LANCE_CSR4_APAD_XMT|LANCE_CSR4_MFCOM|LANCE_CSR4_RCVCCOM
|LANCE_CSR4_TXSTRTM|LANCE_CSR4_JABM);
/* ============= Get MAC address (cf. lance_probe1) ================ */
for (i = 0; i < 6; ++i)
addr->na_addr[i]=in_byte(ioaddr+LANCE_ETH_ADDR+i);
/* Allow the user to override the hardware address. */
ec_confaddr(addr, instance);
/* ============ (re)start init_block(cf. lance_reset) =============== */
/* Reset the LANCE */
(void)in_word(ioaddr+LANCE_RESET);
/* ----- Re-initialize the LANCE ----- */
/* Set station address */
for (i = 0; i < 6; ++i)
lp->init_block.phys_addr[i] = addr->na_addr[i];
/* Preset the receive ring headers */
for (i=0; i<RX_RING_SIZE; i++)
{
lp->rx_ring[i].buf_length = -ETH_FRAME_LEN;
/* OWN */
lp->rx_ring[i].u.base = virt_to_bus(lp->rbuf[i]) & 0xffffff;
/* we set the top byte as the very last thing */
lp->rx_ring[i].u.addr[3] = 0x80;
}
/* Preset the transmitting ring headers */
for (i=0; i<TX_RING_SIZE; i++)
{
lp->tx_ring[i].u.base = 0;
tx_ring_base[i] = virt_to_bus(lp->tbuf[i]) & 0xffffff;
isstored[i] = 0;
}
/* enable Rx and Tx */
lp->init_block.mode = 0x0;
l = (Address)virt_to_bus(&lp->init_block);
write_csr(ioaddr, LANCE_CSR1, (short)l);
write_csr(ioaddr, LANCE_CSR2, (short)(l >> 16));
write_csr(ioaddr, LANCE_CSR4,
LANCE_CSR4_APAD_XMT|LANCE_CSR4_MFCOM|LANCE_CSR4_RCVCCOM
|LANCE_CSR4_TXSTRTM|LANCE_CSR4_JABM);
/* ----- start when init done. ----- */
/* stop */
write_csr(ioaddr, LANCE_CSR0, LANCE_CSR0_STOP);
/* init */
write_csr(ioaddr, LANCE_CSR0, LANCE_CSR0_INIT);
/* poll for IDON */
for (i = 10000; i > 0; --i)
if (read_csr(ioaddr, LANCE_CSR0) & LANCE_CSR0_IDON)
break;
/* Set 'Multicast Table' */
for (i=0;i<4;++i)
{
write_csr(ioaddr, LANCE_CSR8 + i, 0xffff);
}
/* Set 'Receive Mode' */
if (ec->ec_mode & NDEV_MODE_PROMISC)
{
write_csr(ioaddr, LANCE_CSR15, LANCE_CSR15_PROM);
}
else
{
if (ec->ec_mode &
(NDEV_MODE_BCAST | NDEV_MODE_MCAST_LIST | NDEV_MODE_MCAST_ALL))
{
write_csr(ioaddr, LANCE_CSR15, 0x0000);
}
else
{
write_csr(ioaddr, LANCE_CSR15, LANCE_CSR15_DRCVBC);
}
}
/* Set the interrupt handler */
ec->ec_hook = ec->ec_irq;
if ((r=sys_irqsetpolicy(ec->ec_irq, 0, &ec->ec_hook)) != OK)
panic("couldn't set IRQ policy: %d", r);
if ((r = sys_irqenable(&ec->ec_hook)) != OK)
panic("couldn't enable interrupt: %d", r);
/* start && enable interrupt */
write_csr(ioaddr, LANCE_CSR0,
LANCE_CSR0_IDON|LANCE_CSR0_IENA|LANCE_CSR0_STRT);
}
/*===========================================================================*
* in_byte *
*===========================================================================*/
static u8_t in_byte(port_t port)
{
int r;
u32_t value;
r= sys_inb(port, &value);
if (r != OK)
panic("sys_inb failed: %d", r);
return value;
}
/*===========================================================================*
* in_word *
*===========================================================================*/
static u16_t in_word(port_t port)
{
int r;
u32_t value;
r= sys_inw(port, &value);
if (r != OK)
panic("sys_inw failed: %d", r);
return value;
}
/*===========================================================================*
* out_word *
*===========================================================================*/
static void out_word(port_t port, u16_t value)
{
int r;
r= sys_outw(port, value);
if (r != OK)
panic("sys_outw failed: %d", r);
}
/*===========================================================================*
* read_csr *
*===========================================================================*/
static u16_t read_csr(port_t ioaddr, u16_t csrno)
{
out_word(ioaddr+LANCE_ADDR, csrno);
return in_word(ioaddr+LANCE_DATA);
}
/*===========================================================================*
* write_csr *
*===========================================================================*/
static void write_csr(port_t ioaddr, u16_t csrno, u16_t value)
{
out_word(ioaddr+LANCE_ADDR, csrno);
out_word(ioaddr+LANCE_DATA, value);
}