mirror of
https://git.savannah.gnu.org/git/grub.git
synced 2026-10-21 06:49:35 +00:00
1173 lines
36 KiB
C
1173 lines
36 KiB
C
/* 3c59x.c: A 3Com 3c59x/3c90x "Vortex/Boomerang" ethernet driver for linux. */
|
|
/*
|
|
Written 1995 by Donald Becker.
|
|
|
|
This software may be used and distributed according to the terms
|
|
of the GNU Public License, incorporated herein by reference.
|
|
|
|
This driver is for the 3Com "Vortex" series ethercards. Members of
|
|
the series include the 3c590 PCI EtherLink III and 3c595-Tx PCI Fast
|
|
EtherLink.
|
|
|
|
The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
|
|
Center of Excellence in Space Data and Information Sciences
|
|
Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
|
|
*/
|
|
|
|
#ifdef INCLUDE_3C59X
|
|
|
|
|
|
#define lx_outb(a,b) outb(b,a)
|
|
#define lx_outw(a,b) outw(b,a)
|
|
#define lx_outl(a,b) outl(b,a)
|
|
|
|
|
|
static int __jiffies__;
|
|
|
|
#define jiffies ((__jiffies__+=1000))
|
|
|
|
void cli ()
|
|
{
|
|
__asm__ __volatile__("cli");
|
|
}
|
|
#define HZ 1
|
|
|
|
void memset (char *add, int v, int s)
|
|
{
|
|
while (s--)
|
|
*add++ = (char )v;
|
|
}
|
|
|
|
static char *version = "3c59x.c:v0.30-all 12/23/96 becker@cesdis.gsfc.nasa.gov\n";
|
|
|
|
/* "Knobs" that turn on special features. */
|
|
/* Enable the automatic media selection code. */
|
|
#define AUTOMEDIA 1
|
|
|
|
/* Allow the use of bus master transfers instead of programmed-I/O for the
|
|
Tx process. Bus master transfers are always disabled by default, but
|
|
iff this is set they may be turned on using 'options'. */
|
|
|
|
#define NO_VORTEX_BUS_MASTER
|
|
|
|
|
|
#include "netboot.h"
|
|
#include "nic.h"
|
|
#include "3c509.h"
|
|
#include "pci.h"
|
|
|
|
#include "if.h"
|
|
|
|
#include "netdevice.h"
|
|
|
|
struct device card;
|
|
|
|
/* "Knobs" for adjusting internal parameters. */
|
|
/* Put out somewhat more debugging messages. (0 - no msg, 1 minimal msgs). */
|
|
#define VORTEX_DEBUG 2
|
|
|
|
/* Number of times to check to see if the Tx FIFO has space, used in some
|
|
limited cases. */
|
|
#define WAIT_TX_AVAIL 200
|
|
|
|
/* Operational parameter that usually are not changed. */
|
|
/* Time in jiffies before concluding Tx hung */
|
|
#define TX_TIMEOUT ((400*HZ)/1000)
|
|
|
|
/* The total size is twice that of the original EtherLinkIII series: the
|
|
runtime register window, window 1, is now always mapped in. */
|
|
#define VORTEX_TOTAL_SIZE 0x20
|
|
|
|
#ifdef HAVE_DEVLIST
|
|
struct netdev_entry tc59x_drv =
|
|
{"Vortex", vortex_pci_probe, VORTEX_TOTAL_SIZE, NULL};
|
|
#endif
|
|
|
|
#ifdef VORTEX_DEBUG
|
|
int vortex_debug = VORTEX_DEBUG;
|
|
#else
|
|
int vortex_debug = 1;
|
|
#endif
|
|
|
|
/* Caution! These entries must be consistent, with the EISA ones last. */
|
|
static int product_ids[] = {0x5900, 0x5950, 0x5951, 0x5952, 0x9000,
|
|
0x9001, 0x9050, 0x9051, 0, 0};
|
|
static const char *product_names[] = {
|
|
"3c590 Vortex 10Mbps",
|
|
"3c595 Vortex 100baseTX",
|
|
"3c595 Vortex 100baseT4",
|
|
"3c595 Vortex 100base-MII",
|
|
"3c900 Boomerang 10baseT",
|
|
"3c900 Boomerang 10Mbps/Combo",
|
|
"3c905 Boomerang 100baseTx",
|
|
"3c905 Boomerang 100baseT4",
|
|
"3c592 EISA 10mbps Demon/Vortex",
|
|
"3c597 EISA Fast Demon/Vortex",
|
|
};
|
|
#define DEMON10_INDEX 8
|
|
#define DEMON100_INDEX 9
|
|
|
|
/*
|
|
Theory of Operation
|
|
|
|
I. Board Compatibility
|
|
|
|
This device driver is designed for the 3Com FastEtherLink, 3Com's PCI to
|
|
10/100baseT adapter. It also works with the 3c590, a similar product
|
|
with only a 10Mbs interface.
|
|
|
|
II. Board-specific settings
|
|
|
|
PCI bus devices are configured by the system at boot time, so no jumpers
|
|
need to be set on the board. The system BIOS should be set to assign the
|
|
PCI INTA signal to an otherwise unused system IRQ line. While it's
|
|
physically possible to shared PCI interrupt lines, the 1.2.0 kernel doesn't
|
|
support it.
|
|
|
|
III. Driver operation
|
|
|
|
The 3c59x series use an interface that's very similar to the previous 3c5x9
|
|
series. The primary interface is two programmed-I/O FIFOs, with an
|
|
alternate single-contiguous-region bus-master transfer (see next).
|
|
|
|
One extension that is advertised in a very large font is that the adapters
|
|
are capable of being bus masters. Unfortunately this capability is only for
|
|
a single contiguous region making it less useful than the list of transfer
|
|
regions available with the DEC Tulip or AMD PCnet. Given the significant
|
|
performance impact of taking an extra interrupt for each transfer, using
|
|
DMA transfers is a win only with large blocks.
|
|
|
|
IIIC. Synchronization
|
|
The driver runs as two independent, single-threaded flows of control. One
|
|
is the send-packet routine, which enforces single-threaded use by the
|
|
dev->tbusy flag. The other thread is the interrupt handler, which is single
|
|
threaded by the hardware and other software.
|
|
|
|
IV. Notes
|
|
|
|
Thanks to Cameron Spitzer and Terry Murphy of 3Com for providing both
|
|
3c590 and 3c595 boards.
|
|
The name "Vortex" is the internal 3Com project name for the PCI ASIC, and
|
|
the EISA version is called "Demon". According to Terry these names come
|
|
from rides at the local amusement park.
|
|
|
|
The new chips support both ethernet (1.5K) and FDDI (4.5K) packet sizes!
|
|
This driver only supports ethernet packets because of the skbuff allocation
|
|
limit of 4K.
|
|
*/
|
|
|
|
#define TCOM_VENDOR_ID 0x10B7 /* 3Com's manufacturer's ID. */
|
|
|
|
/* Operational definitions.
|
|
These are not used by other compilation units and thus are not
|
|
exported in a ".h" file.
|
|
|
|
First the windows. There are eight register windows, with the command
|
|
and status registers available in each.
|
|
*/
|
|
#define EL3WINDOW(win_num) lx_outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
|
|
#define EL3_CMD 0x0e
|
|
#define EL3_STATUS 0x0e
|
|
|
|
/* The top five bits written to EL3_CMD are a command, the lower
|
|
11 bits are the parameter, if applicable.
|
|
Note that 11 parameters bits was fine for ethernet, but the new chip
|
|
can handle FDDI length frames (~4500 octets) and now parameters count
|
|
32-bit 'Dwords' rather than octets. */
|
|
|
|
enum vortex_cmd {
|
|
TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11,
|
|
RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11,
|
|
TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11,
|
|
FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11,
|
|
SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11,
|
|
SetTxThreshold = 18<<11, SetTxStart = 19<<11,
|
|
StartDMAUp = 20<<11, StartDMADown = (20<<11)+1, StatsEnable = 21<<11,
|
|
StatsDisable = 22<<11, StopCoax = 23<<11,};
|
|
|
|
/* The SetRxFilter command accepts the following classes: */
|
|
enum RxFilter {
|
|
RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 };
|
|
|
|
/* Bits in the general status register. */
|
|
enum vortex_status {
|
|
IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
|
|
TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
|
|
IntReq = 0x0040, StatsFull = 0x0080, DMADone = 1<<8,
|
|
DMAInProgress = 1<<11, /* DMA controller is still busy.*/
|
|
CmdInProgress = 1<<12, /* EL3_CMD is still busy.*/
|
|
};
|
|
|
|
/* Register window 1 offsets, the window used in normal operation.
|
|
On the Vortex this window is always mapped at offsets 0x10-0x1f. */
|
|
enum Window1 {
|
|
TX_FIFO = 0x10, RX_FIFO = 0x10, RxErrors = 0x14,
|
|
RxStatus = 0x18, Timer=0x1A, TxStatus = 0x1B,
|
|
TxFree = 0x1C, /* Remaining free bytes in Tx buffer. */
|
|
};
|
|
enum Window0 {
|
|
Wn0EepromCmd = 10, /* Window 0: EEPROM command register. */
|
|
Wn0EepromData = 12, /* Window 0: EEPROM results register. */
|
|
};
|
|
enum Win0_EEPROM_bits {
|
|
EEPROM_Read = 0x80, EEPROM_WRITE = 0x40, EEPROM_ERASE = 0xC0,
|
|
EEPROM_EWENB = 0x30, /* Enable erasing/writing for 10 msec. */
|
|
EEPROM_EWDIS = 0x00, /* Disable EWENB before 10 msec timeout. */
|
|
};
|
|
/* EEPROM locations. */
|
|
enum eeprom_offset {
|
|
PhysAddr01=0, PhysAddr23=1, PhysAddr45=2, ModelID=3,
|
|
EtherLink3ID=7, IFXcvrIO=8, IRQLine=9,
|
|
NodeAddr01=10, NodeAddr23=11, NodeAddr45=12,
|
|
DriverTune=13, Checksum=15};
|
|
|
|
enum Window3 { /* Window 3: MAC/config bits. */
|
|
Wn3_Config=0, Wn3_MAC_Ctrl=6, Wn3_Options=8,
|
|
};
|
|
union wn3_config {
|
|
int i;
|
|
struct w3_config_fields {
|
|
unsigned int ram_size:3, ram_width:1, ram_speed:2, rom_size:2;
|
|
int pad8:8;
|
|
unsigned int ram_split:2, pad18:2, xcvr:3, pad21:1, autoselect:1;
|
|
int pad24:7;
|
|
} u;
|
|
};
|
|
|
|
enum Window4 {
|
|
Wn4_Media = 0x0A, /* Window 4: Various transcvr/media bits. */
|
|
};
|
|
enum Win4_Media_bits {
|
|
Media_SQE = 0x0008, /* Enable SQE error counting for AUI. */
|
|
Media_10TP = 0x00C0, /* Enable link beat and jabber for 10baseT. */
|
|
Media_Lnk = 0x0080, /* Enable just link beat for 100TX/100FX. */
|
|
Media_LnkBeat = 0x0800,
|
|
};
|
|
enum Window7 { /* Window 7: Bus Master control. */
|
|
Wn7_MasterAddr = 0, Wn7_MasterLen = 6, Wn7_MasterStatus = 12,
|
|
};
|
|
|
|
struct vortex_private {
|
|
char devname[8]; /* "ethN" string, also for kernel debug. */
|
|
const char *product_name;
|
|
int options; /* User-settable misc. driver options. */
|
|
int last_rx_packets; /* For media autoselection. */
|
|
unsigned int available_media:8, /* From Wn3_Options */
|
|
media_override:3, /* Passed-in media type. */
|
|
default_media:3, /* Read from the EEPROM. */
|
|
full_duplex:1, bus_master:1, autoselect:1;
|
|
};
|
|
|
|
/* The action to take with a media selection timer tick.
|
|
Note that we deviate from the 3Com order by checking 10base2 before AUI.
|
|
*/
|
|
static struct media_table {
|
|
char *name;
|
|
unsigned int media_bits:16, /* Bits to set in Wn4_Media register. */
|
|
mask:8, /* The transceiver-present bit in Wn3_Config.*/
|
|
next:8; /* The media type to try next. */
|
|
short wait; /* Time before we check media status. */
|
|
} media_tbl[] = {
|
|
{ "10baseT", Media_10TP,0x08, 3 /* 10baseT->10base2 */, (14*HZ)/10},
|
|
{ "10Mbs AUI", Media_SQE, 0x20, 8 /* AUI->default */, (1*HZ)/10},
|
|
{ "undefined", 0, 0x80, 0 /* Undefined */, 0},
|
|
{ "10base2", 0, 0x10, 1 /* 10base2->AUI. */, (1*HZ)/10},
|
|
{ "100baseTX", Media_Lnk, 0x02, 5 /* 100baseTX->100baseFX */, (14*HZ)/10},
|
|
{ "100baseFX", Media_Lnk, 0x04, 6 /* 100baseFX->MII */, (14*HZ)/10},
|
|
{ "MII", 0, 0x40, 0 /* MII->10baseT */, (14*HZ)/10},
|
|
{ "undefined", 0, 0x01, 0 /* Undefined/100baseT4 */, 0},
|
|
{ "Default", 0, 0xFF, 0 /* Use default */, 0},
|
|
};
|
|
|
|
static int vortex_scan(struct device *dev);
|
|
static int vortex_found_device(struct device *dev, int ioaddr, int irq,
|
|
int product_index, int options);
|
|
static int vortex_probe1(struct device *dev);
|
|
static int vortex_open(struct device *dev);
|
|
static void vortex_timer(unsigned long arg);
|
|
static int vortex_start_xmit(struct device *dev,char *data, int len);
|
|
static int vortex_rx(struct device *dev, char *data, int len);
|
|
|
|
static int vortex_close(struct device *dev);
|
|
static void update_stats(int addr, struct device *dev);
|
|
static struct enet_statistics *vortex_get_stats(struct device *dev);
|
|
static void set_rx_mode(struct device *dev);
|
|
#ifndef NEW_MULTICAST
|
|
static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
|
|
#endif
|
|
|
|
|
|
/* Unlike the other PCI cards the 59x cards don't need a large contiguous
|
|
memory region, so making the driver a loadable module is feasible.
|
|
|
|
Unfortunately maximizing the shared code between the integrated and
|
|
module version of the driver results in a complicated set of initialization
|
|
procedures.
|
|
init_module() -- modules / tc59x_init() -- built-in
|
|
The wrappers for vortex_scan()
|
|
vortex_scan() The common routine that scans for PCI and EISA cards
|
|
vortex_found_device() Allocate a device structure when we find a card.
|
|
Different versions exist for modules and built-in.
|
|
vortex_probe1() Fill in the device structure -- this is separated
|
|
so that the modules code can put it in dev->init.
|
|
*/
|
|
/* This driver uses 'options' to pass the media type, full-duplex flag, etc. */
|
|
/* Note: this is the only limit on the number of cards supported!! */
|
|
static int options[8] = { -1, -1, -1, -1, -1, -1, -1, -1,};
|
|
/* Variables to work-around the Compaq PCI BIOS32 problem. */
|
|
static int compaq_ioaddr = 0, compaq_irq = 0, compaq_prod_id = 0;
|
|
|
|
int tc59x_probe(struct device *dev)
|
|
{
|
|
int cards_found = 0;
|
|
|
|
cards_found = vortex_scan(dev);
|
|
|
|
if (vortex_debug > 0 && cards_found)
|
|
printf(version);
|
|
|
|
return cards_found ? 0 : -1;
|
|
}
|
|
|
|
static int vortex_scan(struct device *dev)
|
|
{
|
|
int cards_found = 0;
|
|
|
|
#ifndef NO_PCI
|
|
/* if (pcibios_present()) { */
|
|
if (1) {
|
|
static int pci_index = 0;
|
|
static int board_index = 0;
|
|
for (; product_ids[board_index]; board_index++, pci_index = 0) {
|
|
for (; pci_index < 16; pci_index++) {
|
|
unsigned char pci_bus, pci_device_fn, pci_irq_line;
|
|
unsigned char pci_latency;
|
|
unsigned int pci_ioaddr;
|
|
unsigned short pci_command;
|
|
|
|
struct pci_device pcidev;
|
|
|
|
eth_pci_init (&pcidev, &pci_ioaddr);
|
|
/* Remove I/O space marker in bit 0. */
|
|
|
|
#ifdef VORTEX_BUS_MASTER
|
|
/* Get and check the bus-master and latency values.
|
|
Some PCI BIOSes fail to set the master-enable bit, and
|
|
the latency timer must be set to the maximum value to avoid
|
|
data corruption that occurs when the timer expires during
|
|
a transfer. Yes, it's a bug. */
|
|
pcibios_read_config_word(pci_bus, pci_device_fn,
|
|
PCI_COMMAND, &pci_command);
|
|
if ( ! (pci_command & PCI_COMMAND_MASTER)) {
|
|
printf(" PCI Master Bit has not been set! Setting...\n");
|
|
pci_command |= PCI_COMMAND_MASTER;
|
|
pcibios_write_config_word(pci_bus, pci_device_fn,
|
|
PCI_COMMAND, pci_command);
|
|
}
|
|
pcibios_read_config_byte(pci_bus, pci_device_fn,
|
|
PCI_LATENCY_TIMER, &pci_latency);
|
|
if (pci_latency != 255) {
|
|
printf(" 3Com EtherLink III: Overriding PCI latency"
|
|
" timer (CFLT) setting of %d, new value is 255.\n",
|
|
pci_latency);
|
|
pcibios_write_config_byte(pci_bus, pci_device_fn,
|
|
PCI_LATENCY_TIMER, 255);
|
|
}
|
|
#endif /* VORTEX_BUS_MASTER */
|
|
vortex_found_device(dev, pci_ioaddr, pci_irq_line, board_index,
|
|
dev && dev->mem_start ? dev->mem_start
|
|
: options[cards_found]);
|
|
dev = 0;
|
|
cards_found++;
|
|
}
|
|
}
|
|
}
|
|
#endif /* NO_PCI */
|
|
|
|
|
|
/* Special code to work-around the Compaq PCI BIOS32 problem. */
|
|
if (compaq_ioaddr) {
|
|
vortex_found_device(dev, compaq_ioaddr, compaq_irq, compaq_prod_id,
|
|
dev && dev->mem_start ? dev->mem_start : options[cards_found]);
|
|
cards_found++;
|
|
dev = 0;
|
|
}
|
|
|
|
return cards_found;
|
|
}
|
|
|
|
static int vortex_found_device(struct device *dev, int ioaddr, int irq,
|
|
int product_index, int options)
|
|
{
|
|
struct vortex_private *vp;
|
|
static struct vortex_private vps;
|
|
|
|
if (dev) {
|
|
/*dev->priv = kmalloc(sizeof (struct vortex_private), GFP_KERNEL); */
|
|
dev->priv = &vps;
|
|
memset(dev->priv, 0, sizeof (struct vortex_private));
|
|
}
|
|
#if 0
|
|
dev = init_etherdev(dev, sizeof(struct vortex_private));
|
|
#endif
|
|
dev->base_addr = ioaddr;
|
|
dev->irq = irq;
|
|
vp = (struct vortex_private *)dev->priv;
|
|
vp->product_name = product_names[product_index];
|
|
vp->options = options;
|
|
if (options >= 0) {
|
|
vp->media_override = ((options & 7) == 2) ? 0 : options & 7;
|
|
vp->full_duplex = (options & 8) ? 1 : 0;
|
|
vp->bus_master = (options & 16) ? 1 : 0;
|
|
} else {
|
|
vp->media_override = 7;
|
|
vp->full_duplex = 0;
|
|
vp->bus_master = 0;
|
|
}
|
|
|
|
vortex_probe1(dev);
|
|
return 0;
|
|
}
|
|
|
|
static int vortex_probe1(struct device *dev)
|
|
{
|
|
int ioaddr = dev->base_addr;
|
|
struct vortex_private *vp = (struct vortex_private *)dev->priv;
|
|
int i;
|
|
|
|
printf("%s: 3Com %s at %#3x,", dev->name,
|
|
vp->product_name, ioaddr);
|
|
|
|
/* Read the station address from the EEPROM. */
|
|
EL3WINDOW(0);
|
|
for (i = 0; i < 3; i++) {
|
|
short *phys_addr = (short *)dev->dev_addr;
|
|
int timer;
|
|
lx_outw(EEPROM_Read + PhysAddr01 + i, ioaddr + Wn0EepromCmd);
|
|
/* Pause for at least 162 us. for the read to take place. */
|
|
for (timer = 162*4 + 400; timer >= 0; timer--) {
|
|
SLOW_DOWN_IO;
|
|
if ((inw(ioaddr + Wn0EepromCmd) & 0x8000) == 0)
|
|
break;
|
|
}
|
|
phys_addr[i] = htons(inw(ioaddr + Wn0EepromData));
|
|
}
|
|
for (i = 0; i < 6; i++)
|
|
printf("%c%x", i ? ':' : ' ', dev->dev_addr[i]);
|
|
printf(", IRQ %d\n", dev->irq);
|
|
/* Tell them about an invalid IRQ. */
|
|
if (vortex_debug && (dev->irq <= 0 || dev->irq > 15))
|
|
printf(" *** Warning: this IRQ is unlikely to work!\n");
|
|
|
|
{
|
|
char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
|
|
union wn3_config config;
|
|
EL3WINDOW(3);
|
|
vp->available_media = inw(ioaddr + Wn3_Options);
|
|
config.i = inl(ioaddr + Wn3_Config);
|
|
if (vortex_debug > 1)
|
|
printf(" Internal config register is %4.4x, transceivers %#x.\n",
|
|
config.i, inw(ioaddr + Wn3_Options));
|
|
printf(" %dK %s-wide RAM %s Rx:Tx split, %s%s interface.\n",
|
|
8 << config.u.ram_size,
|
|
config.u.ram_width ? "word" : "byte",
|
|
ram_split[config.u.ram_split],
|
|
config.u.autoselect ? "autoselect/" : "",
|
|
media_tbl[config.u.xcvr].name);
|
|
dev->if_port = config.u.xcvr;
|
|
vp->default_media = config.u.xcvr;
|
|
vp->autoselect = config.u.autoselect;
|
|
}
|
|
|
|
#if 0
|
|
/* We do a request_region() only to register /proc/ioports info. */
|
|
request_region(ioaddr, VORTEX_TOTAL_SIZE, vp->product_name);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
vortex_open(struct device *dev)
|
|
{
|
|
int ioaddr = dev->base_addr;
|
|
struct vortex_private *vp = (struct vortex_private *)dev->priv;
|
|
union wn3_config config;
|
|
int i;
|
|
|
|
/* Before initializing select the active media port. */
|
|
EL3WINDOW(3);
|
|
if (vp->full_duplex)
|
|
lx_outb(0x20, ioaddr + Wn3_MAC_Ctrl); /* Set the full-duplex bit. */
|
|
config.i = inl(ioaddr + Wn3_Config);
|
|
|
|
if (vp->media_override != 7) {
|
|
if (vortex_debug > 1)
|
|
printf("%s: Media override to transceiver %d (%s).\n",
|
|
dev->name, vp->media_override,
|
|
media_tbl[vp->media_override].name);
|
|
dev->if_port = vp->media_override;
|
|
} else if (vp->autoselect) {
|
|
/* Find first available media type, starting with 100baseTx. */
|
|
dev->if_port = 4;
|
|
while (! (vp->available_media & media_tbl[dev->if_port].mask))
|
|
dev->if_port = media_tbl[dev->if_port].next;
|
|
|
|
if (vortex_debug > 1)
|
|
printf("%s: Initial media type %s.\n",
|
|
dev->name, media_tbl[dev->if_port].name);
|
|
|
|
#if 0
|
|
vp->timer.expires = RUN_AT(media_tbl[dev->if_port].wait);
|
|
vp->timer.data = (unsigned long)dev;
|
|
vp->timer.function = &vortex_timer; /* timer handler */
|
|
add_timer(&vp->timer);
|
|
#endif
|
|
} else
|
|
dev->if_port = vp->default_media;
|
|
|
|
config.u.xcvr = dev->if_port;
|
|
lx_outl(config.i, ioaddr + Wn3_Config);
|
|
|
|
if (vortex_debug > 1) {
|
|
printf("%s: vortex_open() InternalConfig %8.8x.\n",
|
|
dev->name, config.i);
|
|
}
|
|
|
|
lx_outw(TxReset, ioaddr + EL3_CMD);
|
|
for (i = 20; i >= 0 ; i--)
|
|
if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
|
|
break;
|
|
|
|
lx_outw(RxReset, ioaddr + EL3_CMD);
|
|
/* Wait a few ticks for the RxReset command to complete. */
|
|
for (i = 20; i >= 0 ; i--)
|
|
if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
|
|
break;
|
|
|
|
lx_outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
|
|
|
|
#if 0
|
|
#ifdef SA_SHIRQ /* Use the now-standard shared IRQ implementation. */
|
|
if (request_irq(dev->irq, &vortex_interrupt, SA_SHIRQ,
|
|
vp->product_name, dev)) {
|
|
return -EAGAIN;
|
|
}
|
|
#else
|
|
#ifdef USE_SHARED_IRQ /* Use my shared IRQ implementation. */
|
|
i = request_shared_irq(dev->irq, &vortex_interrupt, dev, vp->product_name);
|
|
if (i) /* Error */
|
|
return i;
|
|
#else
|
|
if (dev->irq == 0 || irq2dev_map[dev->irq] != NULL)
|
|
return -EAGAIN;
|
|
irq2dev_map[dev->irq] = dev;
|
|
if (REQUEST_IRQ(dev->irq, &vortex_interrupt, 0, vp->product_name, NULL)) {
|
|
irq2dev_map[dev->irq] = NULL;
|
|
return -EAGAIN;
|
|
}
|
|
#endif /* USE_SHARED_IRQ */
|
|
#endif /* SA_SHIRQ */
|
|
#endif
|
|
if (vortex_debug > 1) {
|
|
EL3WINDOW(4);
|
|
printf("%s: vortex_open() irq %d media status %4.4x.\n",
|
|
dev->name, dev->irq, inw(ioaddr + Wn4_Media));
|
|
}
|
|
|
|
/* Set the station address and mask in window 2 each time opened. */
|
|
EL3WINDOW(2);
|
|
for (i = 0; i < 6; i++)
|
|
lx_outb(dev->dev_addr[i], ioaddr + i);
|
|
for (; i < 12; i+=2)
|
|
lx_outw(0, ioaddr + i);
|
|
|
|
if (dev->if_port == 3)
|
|
/* Start the thinnet transceiver. We should really wait 50ms...*/
|
|
lx_outw(StartCoax, ioaddr + EL3_CMD);
|
|
EL3WINDOW(4);
|
|
lx_outw((inw(ioaddr + Wn4_Media) & ~(Media_10TP|Media_SQE)) |
|
|
media_tbl[dev->if_port].media_bits, ioaddr + Wn4_Media);
|
|
|
|
/* Switch to the stats window, and clear all stats by reading. */
|
|
lx_outw(StatsDisable, ioaddr + EL3_CMD);
|
|
EL3WINDOW(6);
|
|
for (i = 0; i < 10; i++)
|
|
inb(ioaddr + i);
|
|
inw(ioaddr + 10);
|
|
inw(ioaddr + 12);
|
|
/* New: On the Vortex we must also clear the BadSSD counter. */
|
|
EL3WINDOW(4);
|
|
inb(ioaddr + 12);
|
|
|
|
/* Switch to register set 7 for normal use. */
|
|
EL3WINDOW(7);
|
|
|
|
/* Set reciever mode: presumably accept b-case and phys addr only. */
|
|
set_rx_mode(dev);
|
|
lx_outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
|
|
|
|
dev->tbusy = 0;
|
|
dev->interrupt = 0;
|
|
dev->start = 1;
|
|
|
|
lx_outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
|
|
lx_outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
|
|
/* Allow status bits to be seen. */
|
|
lx_outw(SetStatusEnb | 0x1ff, ioaddr + EL3_CMD);
|
|
/* Ack all pending events, and set active indicator mask. */
|
|
lx_outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
|
|
ioaddr + EL3_CMD);
|
|
lx_outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
|
|
| DMADone, ioaddr + EL3_CMD);
|
|
|
|
#ifdef MODULE
|
|
MOD_INC_USE_COUNT;
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void vortex_timer(unsigned long data)
|
|
{
|
|
#ifdef AUTOMEDIA
|
|
struct device *dev = (struct device *)data;
|
|
struct vortex_private *vp = (struct vortex_private *)dev->priv;
|
|
int ioaddr = dev->base_addr;
|
|
unsigned long flags;
|
|
int ok = 0;
|
|
|
|
if (vortex_debug > 1)
|
|
printf("%s: Media selection timer tick happened, %s.\n",
|
|
dev->name, media_tbl[dev->if_port].name);
|
|
|
|
save_flags(flags); cli(); {
|
|
int old_window = inw(ioaddr + EL3_CMD) >> 13;
|
|
int media_status;
|
|
EL3WINDOW(4);
|
|
media_status = inw(ioaddr + Wn4_Media);
|
|
switch (dev->if_port) {
|
|
case 0: case 4: case 5: /* 10baseT, 100baseTX, 100baseFX */
|
|
if (media_status & Media_LnkBeat) {
|
|
ok = 1;
|
|
if (vortex_debug > 1)
|
|
printf("%s: Media %s has link beat, %x.\n",
|
|
dev->name, media_tbl[dev->if_port].name, media_status);
|
|
} else if (vortex_debug > 1)
|
|
printf("%s: Media %s is has no link beat, %x.\n",
|
|
dev->name, media_tbl[dev->if_port].name, media_status);
|
|
|
|
break;
|
|
default: /* Other media types handled by Tx timeouts. */
|
|
if (vortex_debug > 1)
|
|
printf("%s: Media %s is has no indication, %x.\n",
|
|
dev->name, media_tbl[dev->if_port].name, media_status);
|
|
ok = 1;
|
|
}
|
|
if ( ! ok) {
|
|
union wn3_config config;
|
|
|
|
do {
|
|
dev->if_port = media_tbl[dev->if_port].next;
|
|
} while ( ! (vp->available_media & media_tbl[dev->if_port].mask));
|
|
if (dev->if_port == 8) { /* Go back to default. */
|
|
dev->if_port = vp->default_media;
|
|
if (vortex_debug > 1)
|
|
printf("%s: Media selection failing, using default %s port.\n",
|
|
dev->name, media_tbl[dev->if_port].name);
|
|
} else {
|
|
if (vortex_debug > 1)
|
|
printf("%s: Media selection failed, now trying %s port.\n",
|
|
dev->name, media_tbl[dev->if_port].name);
|
|
#if 0
|
|
vp->timer.expires = RUN_AT(media_tbl[dev->if_port].wait);
|
|
add_timer(&vp->timer);
|
|
#endif
|
|
}
|
|
lx_outw((media_status & ~(Media_10TP|Media_SQE)) |
|
|
media_tbl[dev->if_port].media_bits, ioaddr + Wn4_Media);
|
|
|
|
EL3WINDOW(3);
|
|
config.i = inl(ioaddr + Wn3_Config);
|
|
config.u.xcvr = dev->if_port;
|
|
lx_outl(config.i, ioaddr + Wn3_Config);
|
|
|
|
lx_outw(dev->if_port == 3 ? StartCoax : StopCoax, ioaddr + EL3_CMD);
|
|
}
|
|
EL3WINDOW(old_window);
|
|
} restore_flags(flags);
|
|
if (vortex_debug > 1)
|
|
printf("%s: Media selection timer finished, %s.\n",
|
|
dev->name, media_tbl[dev->if_port].name);
|
|
|
|
#endif /* AUTOMEDIA*/
|
|
return;
|
|
}
|
|
|
|
static int
|
|
vortex_start_xmit(struct device *dev, char *data, int len)
|
|
{
|
|
struct vortex_private *vp = (struct vortex_private *)dev->priv;
|
|
int ioaddr = dev->base_addr;
|
|
|
|
/* Part of the following code is inspired by code from Giuseppe Ciaccio,
|
|
ciaccio@disi.unige.it.
|
|
It works around a ?bug? in the 8K Vortex that only occurs on some
|
|
systems: the TxAvailable interrupt seems to be lost.
|
|
The ugly work-around is to busy-wait for room available in the Tx
|
|
buffer before deciding the transmitter is actually hung.
|
|
This busy-wait should never really occur, since the problem is that
|
|
there actually *is* room in the Tx FIFO.
|
|
|
|
This pointed out an optimization -- we can ignore dev->tbusy if
|
|
we actually have room for this packet.
|
|
*/
|
|
|
|
if (dev->tbusy) {
|
|
/* Transmitter timeout, serious problems. */
|
|
int tickssofar = jiffies - dev->trans_start;
|
|
int i;
|
|
|
|
if (tickssofar < TX_TIMEOUT-2) /* We probably aren't empty. */
|
|
return 1;
|
|
/* Wait a while to see if there really is room. */
|
|
for (i = WAIT_TX_AVAIL; i >= 0; i--)
|
|
if (inw(ioaddr + TxFree) > len)
|
|
break;
|
|
if ( i < 0) {
|
|
if (tickssofar < TX_TIMEOUT)
|
|
return 1;
|
|
printf("%s: transmit timed out, tx_status %2.2x status %4.4x.\n",
|
|
dev->name, inb(ioaddr + TxStatus), inw(ioaddr + EL3_STATUS));
|
|
/* Issue TX_RESET and TX_START commands. */
|
|
lx_outw(TxReset, ioaddr + EL3_CMD);
|
|
for (i = 20; i >= 0 ; i--)
|
|
if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
|
|
break;
|
|
lx_outw(TxEnable, ioaddr + EL3_CMD);
|
|
dev->trans_start = jiffies;
|
|
dev->tbusy = 0;
|
|
#if 0
|
|
vp->stats.tx_errors++;
|
|
vp->stats.tx_dropped++;
|
|
#endif
|
|
return 0; /* Yes, silently *drop* the packet! */
|
|
}
|
|
}
|
|
|
|
/* Block a timer-based transmit from overlapping. This could better be
|
|
done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
|
|
If this ever occurs the queue layer is doing something evil! */
|
|
#if 0
|
|
if (set_bit(0, (void*)&dev->tbusy) != 0) {
|
|
printf("%s: Transmitter access conflict.\n", dev->name);
|
|
return 1;
|
|
}
|
|
#endif
|
|
/* Put out the doubleword header... */
|
|
lx_outl(len, ioaddr + TX_FIFO);
|
|
#ifdef VORTEX_BUS_MASTER
|
|
if (vp->bus_master) {
|
|
/* Set the bus-master controller to transfer the packet. */
|
|
lx_outl((int)(data), ioaddr + Wn7_MasterAddr);
|
|
lx_outw((len + 3) & ~3, ioaddr + Wn7_MasterLen);
|
|
/* vp->tx_skb = skb; */
|
|
lx_outw(StartDMADown, ioaddr + EL3_CMD);
|
|
/* dev->tbusy will be cleared at the DMADone interrupt. */
|
|
} else {
|
|
/* ... and the packet rounded to a doubleword. */
|
|
outsl(ioaddr + TX_FIFO, data, (len + 3) >> 2);
|
|
/* dev_kfree_skb (skb, FREE_WRITE); */
|
|
if (inw(ioaddr + TxFree) > 1536) {
|
|
dev->tbusy = 0;
|
|
} else
|
|
/* Interrupt us when the FIFO has room for max-sized packet. */
|
|
lx_outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD);
|
|
}
|
|
#else
|
|
/* ... and the packet rounded to a doubleword. */
|
|
outsl(ioaddr + TX_FIFO, data, (len + 3) >> 2);
|
|
/* dev_kfree_skb (skb, FREE_WRITE); */
|
|
if (inw(ioaddr + TxFree) > 1536) {
|
|
dev->tbusy = 0;
|
|
} else
|
|
/* Interrupt us when the FIFO has room for max-sized packet. */
|
|
lx_outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD);
|
|
#endif /* bus master */
|
|
|
|
dev->trans_start = jiffies;
|
|
|
|
/* Clear the Tx status stack. */
|
|
{
|
|
short tx_status;
|
|
int i = 4;
|
|
|
|
while (--i > 0 && (tx_status = inb(ioaddr + TxStatus)) > 0) {
|
|
if (tx_status & 0x3C) { /* A Tx-disabling error occurred. */
|
|
if (vortex_debug > 2)
|
|
printf("%s: Tx error, status %2.2x.\n",
|
|
dev->name, tx_status);
|
|
#if 0
|
|
if (tx_status & 0x04) vp->stats.tx_fifo_errors++;
|
|
if (tx_status & 0x38) vp->stats.tx_aborted_errors++;
|
|
#endif
|
|
if (tx_status & 0x30) {
|
|
int j;
|
|
lx_outw(TxReset, ioaddr + EL3_CMD);
|
|
for (j = 20; j >= 0 ; j--)
|
|
if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
|
|
break;
|
|
}
|
|
lx_outw(TxEnable, ioaddr + EL3_CMD);
|
|
}
|
|
lx_outb(0x00, ioaddr + TxStatus); /* Pop the status stack. */
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#if 0
|
|
|
|
/* The interrupt handler does all of the Rx thread work and cleans up
|
|
after the Tx thread. */
|
|
static void vortex_interrupt IRQ(int irq, void *dev_id, struct pt_regs *regs)
|
|
{
|
|
#ifdef SA_SHIRQ /* Use the now-standard shared IRQ implementation. */
|
|
struct device *dev = dev_id;
|
|
#else
|
|
#ifdef USE_SHARED_IRQ
|
|
struct device *dev = (struct device *)(irq == 0 ? regs : irq2dev_map[irq]);
|
|
#else
|
|
struct device *dev = (struct device *)(irq2dev_map[irq]);
|
|
#endif
|
|
#endif
|
|
struct vortex_private *lp;
|
|
int ioaddr, status;
|
|
int latency;
|
|
int i = 0;
|
|
|
|
if (dev->interrupt)
|
|
printf("%s: Re-entering the interrupt handler.\n", dev->name);
|
|
dev->interrupt = 1;
|
|
|
|
ioaddr = dev->base_addr;
|
|
latency = inb(ioaddr + Timer);
|
|
lp = (struct vortex_private *)dev->priv;
|
|
|
|
status = inw(ioaddr + EL3_STATUS);
|
|
|
|
if (vortex_debug > 4)
|
|
printf("%s: interrupt, status %4.4x, timer %d.\n", dev->name,
|
|
status, latency);
|
|
if ((status & 0xE000) != 0xE000) {
|
|
static int donedidthis=0;
|
|
/* Some interrupt controllers store a bogus interrupt from boot-time.
|
|
Ignore a single early interrupt, but don't hang the machine for
|
|
other interrupt problems. */
|
|
if (donedidthis++ > 1) {
|
|
printf("%s: Bogus interrupt, bailing. Status %4.4x, start=%d.\n",
|
|
dev->name, status, dev->start);
|
|
FREE_IRQ(dev->irq, dev);
|
|
}
|
|
}
|
|
|
|
do {
|
|
if (vortex_debug > 5)
|
|
printf("%s: In interrupt loop, status %4.4x.\n",
|
|
dev->name, status);
|
|
if (status & RxComplete)
|
|
vortex_rx(dev);
|
|
|
|
if (status & TxAvailable) {
|
|
if (vortex_debug > 5)
|
|
printf(" TX room bit was handled.\n");
|
|
/* There's room in the FIFO for a full-sized packet. */
|
|
lx_outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
|
|
dev->tbusy = 0;
|
|
mark_bh(NET_BH);
|
|
}
|
|
#ifdef VORTEX_BUS_MASTER
|
|
if (status & DMADone) {
|
|
lx_outw(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */
|
|
dev->tbusy = 0;
|
|
/* dev_kfree_skb (lp->tx_skb, FREE_WRITE); Release the transfered buffer */
|
|
/* mark_bh(NET_BH); */
|
|
}
|
|
#endif
|
|
if (status & (AdapterFailure | RxEarly | StatsFull)) {
|
|
/* Handle all uncommon interrupts at once. */
|
|
if (status & RxEarly) { /* Rx early is unused. */
|
|
vortex_rx(dev);
|
|
lx_outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
|
|
}
|
|
if (status & StatsFull) { /* Empty statistics. */
|
|
static int DoneDidThat = 0;
|
|
if (vortex_debug > 4)
|
|
printf("%s: Updating stats.\n", dev->name);
|
|
update_stats(ioaddr, dev);
|
|
/* DEBUG HACK: Disable statistics as an interrupt source. */
|
|
/* This occurs when we have the wrong media type! */
|
|
if (DoneDidThat == 0 &&
|
|
inw(ioaddr + EL3_STATUS) & StatsFull) {
|
|
int win, reg;
|
|
printf("%s: Updating stats failed, disabling stats as an"
|
|
" interrupt source.\n", dev->name);
|
|
for (win = 0; win < 8; win++) {
|
|
EL3WINDOW(win);
|
|
printf("\n Vortex window %d:", win);
|
|
for (reg = 0; reg < 16; reg++)
|
|
printf(" %2.2x", inb(ioaddr+reg));
|
|
}
|
|
EL3WINDOW(7);
|
|
lx_outw(SetIntrEnb | 0x18, ioaddr + EL3_CMD);
|
|
DoneDidThat++;
|
|
}
|
|
}
|
|
if (status & AdapterFailure) {
|
|
/* Adapter failure requires Rx reset and reinit. */
|
|
lx_outw(RxReset, ioaddr + EL3_CMD);
|
|
/* Set the Rx filter to the current state. */
|
|
set_rx_mode(dev);
|
|
lx_outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */
|
|
lx_outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
|
|
}
|
|
}
|
|
|
|
if (++i > 10) {
|
|
printf("%s: Infinite loop in interrupt, status %4.4x. "
|
|
"Disabling functions (%4.4x).\n",
|
|
dev->name, status, SetStatusEnb | ((~status) & 0xFE));
|
|
/* Disable all pending interrupts. */
|
|
lx_outw(SetStatusEnb | ((~status) & 0xFE), ioaddr + EL3_CMD);
|
|
lx_outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
|
|
break;
|
|
}
|
|
/* Acknowledge the IRQ. */
|
|
lx_outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
|
|
|
|
} while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete));
|
|
|
|
if (vortex_debug > 4)
|
|
printf("%s: exiting interrupt, status %4.4x.\n", dev->name, status);
|
|
|
|
dev->interrupt = 0;
|
|
return;
|
|
}
|
|
|
|
#endif
|
|
|
|
static int
|
|
vortex_rx(struct device *dev, char *data, int len)
|
|
{
|
|
struct vortex_private *vp = (struct vortex_private *)dev->priv;
|
|
int ioaddr = dev->base_addr;
|
|
int i;
|
|
short rx_status;
|
|
|
|
if (vortex_debug > 5)
|
|
printf(" In rx_packet(), status %4.4x, rx_status %4.4x.\n",
|
|
inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus));
|
|
while ((rx_status = inw(ioaddr + RxStatus)) > 0) {
|
|
if (rx_status & 0x4000) { /* Error, update stats. */
|
|
unsigned char rx_error = inb(ioaddr + RxErrors);
|
|
if (vortex_debug > 4)
|
|
printf(" Rx error: status %2.2x.\n", rx_error);
|
|
#if 0
|
|
vp->stats.rx_errors++;
|
|
if (rx_error & 0x01) vp->stats.rx_over_errors++;
|
|
if (rx_error & 0x02) vp->stats.rx_length_errors++;
|
|
if (rx_error & 0x04) vp->stats.rx_frame_errors++;
|
|
if (rx_error & 0x08) vp->stats.rx_crc_errors++;
|
|
if (rx_error & 0x10) vp->stats.rx_length_errors++;
|
|
#endif
|
|
} else {
|
|
/* The packet length: up to 4.5K!. */
|
|
short pkt_len = rx_status & 0x1fff;
|
|
/* struct sk_buff *skb; */
|
|
|
|
/* skb = DEV_ALLOC_SKB(pkt_len + 5); */
|
|
if (vortex_debug > 4)
|
|
printf("Receiving packet size %d status %4.4x.\n",
|
|
pkt_len, rx_status);
|
|
|
|
if (1) {
|
|
insl(ioaddr + RX_FIFO, data, (pkt_len + 3) >> 2);
|
|
lx_outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
|
|
|
|
for (i = 200; i >= 0; i--)
|
|
if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
|
|
break;
|
|
}
|
|
|
|
#if 0
|
|
if (skb != NULL) {
|
|
skb->dev = dev;
|
|
#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE >= 0x10300
|
|
skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
|
|
/* 'skb_put()' points to the start of sk_buff data area. */
|
|
insl(ioaddr + RX_FIFO, skb_put(skb, pkt_len),
|
|
(pkt_len + 3) >> 2);
|
|
skb->protocol = eth_type_trans(skb, dev);
|
|
#else
|
|
skb->len = pkt_len;
|
|
/* 'skb->data' points to the start of sk_buff data area. */
|
|
insl(ioaddr + RX_FIFO, skb->data, (pkt_len + 3) >> 2);
|
|
#endif /* KERNEL_1_3_0 */
|
|
netif_rx(skb);
|
|
lx_outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
|
|
/* Wait a limited time to go to next packet. */
|
|
for (i = 200; i >= 0; i--)
|
|
if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
|
|
break;
|
|
vp->stats.rx_packets++;
|
|
continue;
|
|
} else if (vortex_debug)
|
|
printf("%s: Couldn't allocate a sk_buff of size %d.\n",
|
|
dev->name, pkt_len);
|
|
#endif
|
|
}
|
|
#if 0
|
|
vp->stats.rx_dropped++;
|
|
#endif
|
|
lx_outw(RxDiscard, ioaddr + EL3_CMD);
|
|
/* Wait a limited time to skip this packet. */
|
|
for (i = 200; i >= 0; i--)
|
|
if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
vortex_close(struct device *dev)
|
|
{
|
|
struct vortex_private *vp = (struct vortex_private *)dev->priv;
|
|
int ioaddr = dev->base_addr;
|
|
|
|
dev->start = 0;
|
|
dev->tbusy = 1;
|
|
|
|
if (vortex_debug > 1)
|
|
printf("%s: vortex_close() status %4.4x, Tx status %2.2x.\n",
|
|
dev->name, inw(ioaddr + EL3_STATUS), inb(ioaddr + TxStatus));
|
|
|
|
#if 0
|
|
del_timer(&vp->timer);
|
|
#endif
|
|
/* Turn off statistics ASAP. We update lp->stats below. */
|
|
lx_outw(StatsDisable, ioaddr + EL3_CMD);
|
|
|
|
/* Disable the receiver and transmitter. */
|
|
lx_outw(RxDisable, ioaddr + EL3_CMD);
|
|
lx_outw(TxDisable, ioaddr + EL3_CMD);
|
|
|
|
if (dev->if_port == 3)
|
|
/* Turn off thinnet power. Green! */
|
|
lx_outw(StopCoax, ioaddr + EL3_CMD);
|
|
#if 0
|
|
|
|
#ifdef SA_SHIRQ
|
|
FREE_IRQ(dev->irq, dev);
|
|
#else
|
|
#ifdef USE_SHARED_IRQ
|
|
free_shared_irq(dev->irq, dev);
|
|
#else
|
|
FREE_IRQ(dev->irq, NULL);
|
|
/* Mmmm, we should disable all interrupt sources here. */
|
|
irq2dev_map[dev->irq] = 0;
|
|
#endif
|
|
#endif
|
|
|
|
update_stats(ioaddr, dev);
|
|
#ifdef MODULE
|
|
MOD_DEC_USE_COUNT;
|
|
#endif
|
|
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* This new version of set_rx_mode() supports v1.4 kernels.
|
|
The Vortex chip has no documented multicast filter, so the only
|
|
multicast setting is to receive all multicast frames. At least
|
|
the chip has a very clean way to set the mode, unlike many others. */
|
|
static void
|
|
set_rx_mode(struct device *dev)
|
|
{
|
|
short ioaddr = dev->base_addr;
|
|
short new_mode;
|
|
|
|
if (dev->flags & IFF_PROMISC) {
|
|
if (vortex_debug > 3)
|
|
printf("%s: Setting promiscuous mode.\n", dev->name);
|
|
new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast|RxProm;
|
|
} else if (/*(dev->mc_list) || */ (dev->flags & IFF_ALLMULTI)) {
|
|
new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast;
|
|
} else
|
|
new_mode = SetRxFilter | RxStation | RxBroadcast;
|
|
|
|
lx_outw(new_mode, ioaddr + EL3_CMD);
|
|
}
|
|
|
|
|
|
/*
|
|
* Local variables:
|
|
* compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c 3c59x.c -o ../../modules/3c59x.o"
|
|
* c-indent-level: 4
|
|
* tab-width: 4
|
|
* End:
|
|
*/
|
|
void VX_reset (struct nic *nic)
|
|
{
|
|
printf ("reset");
|
|
}
|
|
|
|
int VX_poll (struct nic *nic)
|
|
{
|
|
// printf ("Polling %x %d\n", nic->packet, nic->packetlen);
|
|
}
|
|
|
|
void VX_transmit (struct nic *nic, char *d,
|
|
unsigned int t, unsigned int s, char *p)
|
|
{
|
|
int i;
|
|
|
|
printf ("Transmit type %d, %x %d\n", t, p, s);
|
|
printf ("SRCAddr : ");
|
|
for (i = 0; i < 6; i++)
|
|
printf ("%x:", ((int)nic->node_addr[i]) & 0xff);
|
|
printf ("\nDest : ");
|
|
for (i = 0; i < 6; i++)
|
|
printf ("%x:", ((int)d[i]) & 0xff);
|
|
printf ("\n");
|
|
|
|
}
|
|
|
|
void VX_disable (struct nic *nic)
|
|
{
|
|
printf ("Disable\n");
|
|
}
|
|
|
|
struct nic *VX_probe(struct nic *nic, unsigned short *probe_addrs)
|
|
{
|
|
|
|
if (probe_addrs) {
|
|
card.mem_start = probe_addrs[0];
|
|
vortex_probe1(&card);
|
|
|
|
printf ("Probe 3c93 at %x\n", card.mem_start);
|
|
nic->priv_data = &card;
|
|
nic->reset = VX_reset;
|
|
nic->poll = VX_poll;
|
|
nic->transmit = VX_transmit;
|
|
nic->disable = VX_disable;
|
|
return 1;
|
|
}
|
|
printf ("Failed : No ProbeAddrs\n");
|
|
return 0;
|
|
}
|
|
|
|
#endif /* INCLUDE_3C59X */
|