/* 3c509.c: A 3c509 EtherLink3 ethernet driver for linux. */ /* Written 1993-2000 by Donald Becker.
Copyright 1994-2000 by Donald Becker. Copyright 1993 United States Government as represented by the Director, National Security Agency. This software may be used and distributed according to the terms of the GNU General Public License, incorporated herein by reference.
This driver is for the 3Com EtherLinkIII series.
The author may be reached as becker@scyld.com, or C/O Scyld Computing Corporation 410 Severn Ave., Suite 210 Annapolis MD 21403
Known limitations: Because of the way 3c509 ISA detection works it's difficult to predict a priori which of several ISA-mode cards will be detected first.
This driver does not use predictive interrupt mode, resulting in higher packet latency but lower overhead. If interrupts are disabled for an unusually long time it could also result in missed packets, but in practice this rarely happens.
FIXES: Alan Cox: Removed the 'Unexpected interrupt' bug. Michael Meskes: Upgraded to Donald Becker's version 1.07. Alan Cox: Increased the eeprom delay. Regardless of what the docs say some people definitely get problems with lower (but in card spec) delays v1.10 4/21/97 Fixed module code so that multiple cards may be detected, other cleanups. -djb Andrea Arcangeli: Upgraded to Donald Becker's version 1.12. Rick Payne: Fixed SMP race condition v1.13 9/8/97 Made 'max_interrupt_work' an insmod-settable variable -djb v1.14 10/15/97 Avoided waiting..discard message for fast machines -djb v1.15 1/31/98 Faster recovery for Tx errors. -djb v1.16 2/3/98 Different ID port handling to avoid sound cards. -djb v1.18 12Mar2001 Andrew Morton - Avoid bogus detect of 3c590's (Andrzej Krzysztofowicz) - Reviewed against 1.18 from scyld.com v1.18a 17Nov2001 Jeff Garzik <jgarzik@pobox.com> - ethtool support v1.18b 1Mar2002 Zwane Mwaikambo <zwane@commfireservices.com> - Power Management support v1.18c 1Mar2002 David Ruggiero <jdr@farfalle.com> - Full duplex support v1.19 16Oct2002 Zwane Mwaikambo <zwane@linuxpower.ca> - Additional ethtool features v1.19a 28Oct2002 Davud Ruggiero <jdr@farfalle.com> - Increase *read_eeprom udelay to workaround oops with 2 cards. v1.19b 08Nov2002 Marc Zyngier <maz@wild-wind.fr.eu.org> - Introduce driver model for EISA cards. v1.20 04Feb2008 Ondrej Zary <linux@rainbow-software.org> - convert to isa_driver and pnp_driver and some cleanups
*/
#define DRV_NAME "3c509"
/* A few values that may be tweaked. */
/* Time in jiffies before concluding the transmitter is hung. */ #define TX_TIMEOUT (400*HZ/1000)
/* Used to do a global count of all the cards in the system. Must be * a global variable so that the eisa probe routines can increment
* it */ staticint el3_cards = 0; #define EL3_MAX_CARDS 8
/* To minimize the size of the driver source I only define operating constants if they are used several times. You'll need the manual
anyway if you want to understand driver details. */ /* Offsets from base I/O address. */ #define EL3_DATA 0x00 #define EL3_CMD 0x0e #define EL3_STATUS 0x0e #define EEPROM_READ 0x80
/* Return 0 on success, 1 on error, 2 when found already detected PnP card */ staticint el3_isa_id_sequence(__be16 *phys_addr)
{ short lrs_state = 0xff; int i;
/* ISA boards are detected by sending the ID sequence to the ID_PORT. We find cards past the first by setting the 'current_tag' on cards as they are found. Cards with their tag set will not
respond to subsequent ID sequences. */
outb(0x00, id_port);
outb(0x00, id_port); for (i = 0; i < 255; i++) {
outb(lrs_state, id_port);
lrs_state <<= 1;
lrs_state = lrs_state & 0x100 ? lrs_state ^ 0xcf : lrs_state;
} /* For the first probe, clear all board's tag registers. */ if (current_tag == 0)
outb(0xd0, id_port); else/* Otherwise kill off already-found boards. */
outb(0xd8, id_port); if (id_read_eeprom(7) != 0x6d50) return 1; /* Read in EEPROM data, which does contention-select. Only the lowest address board will stay "on-line".
3Com got the byte order backwards. */ for (i = 0; i < 3; i++)
phys_addr[i] = htons(id_read_eeprom(i)); #ifdef CONFIG_PNP if (!nopnp) { /* The ISA PnP 3c509 cards respond to the ID sequence too.
This check is needed in order not to register them twice. */ for (i = 0; i < el3_cards; i++) { struct el3_private *lp = netdev_priv(el3_devs[i]); if (lp->type == EL3_PNP &&
ether_addr_equal((u8 *)phys_addr, el3_devs[i]->dev_addr)) { if (el3_debug > 3)
pr_debug("3c509 with address %02x %02x %02x %02x %02x %02x was found by ISAPnP\n",
phys_addr[0] & 0xff, phys_addr[0] >> 8,
phys_addr[1] & 0xff, phys_addr[1] >> 8,
phys_addr[2] & 0xff, phys_addr[2] >> 8); /* Set the adaptor tag so that the next card can be found. */
outb(0xd0 + ++current_tag, id_port); return 2;
}
}
} #endif/* CONFIG_PNP */ return 0;
}
staticvoid el3_dev_fill(struct net_device *dev, __be16 *phys_addr, int ioaddr, int irq, int if_port, enum el3_cardtype type)
{ struct el3_private *lp = netdev_priv(dev);
while ((err = el3_isa_id_sequence(phys_addr)) == 2)
; /* Skip to next card when PnP card found */ if (err == 1) return 0; /* Set the adaptor tag so that the next card can be found. */
outb(0xd0 + ++current_tag, id_port); /* Enable the card */
outb((ioaddr >> 4) | 0xe0, id_port);
EL3WINDOW(0); if (inw(ioaddr) != 0x6d50) return 1; /* Free the interrupt so that some other card can use it. */
outw(0x0f00, ioaddr + WN0_IRQ); return el3_resume(dev);
} #endif
if (err) {
eisa_set_drvdata (edev, NULL);
free_netdev(dev); return err;
}
el3_devs[el3_cards++] = dev; return 0;
} #endif
/* This remove works for all device types. *
* The net dev must be stored in the driver data field */ staticint el3_device_remove(struct device *device)
{ struct net_device *dev;
dev = dev_get_drvdata(device);
el3_common_remove (dev); return 0;
}
/* Read a word from the EEPROM using the regular EEPROM access register. Assume that we are in register window zero.
*/ static ushort read_eeprom(int ioaddr, int index)
{
outw(EEPROM_READ + index, ioaddr + 10); /* Pause for at least 162 us. for the read to take place.
Some chips seem to require much longer */
mdelay(2); return inw(ioaddr + 12);
}
/* Read a word from the EEPROM when in the ISA ID probe state. */ static ushort id_read_eeprom(int index)
{ int bit, word = 0;
/* Issue read command, and pause for at least 162 us. for it to complete.
Assume extra-fast 16Mhz bus. */
outb(EEPROM_READ + index, id_port);
/* Pause for at least 162 us. for the read to take place. */ /* Some chips seem to require much longer */
mdelay(4);
for (bit = 15; bit >= 0; bit--)
word = (word << 1) + (inb(id_port) & 0x01);
if (el3_debug > 3)
pr_debug(" 3c509 EEPROM word %d %#4.4x.\n", index, word);
return word;
}
staticint
el3_open(struct net_device *dev)
{ int ioaddr = dev->base_addr; int i;
if (el3_debug > 4) {
pr_debug("%s: el3_start_xmit(length = %u) called, status %4.4x.\n",
dev->name, skb->len, inw(ioaddr + EL3_STATUS));
} /* * We lock the driver against other processors. Note * we don't need to lock versus the IRQ as we suspended * that. This means that we lose the ability to take * an RX during a TX upload. That sucks a bit with SMP * on an original 3c509 (2K buffer) * * Using disable_irq stops us crapping on other * time sensitive devices.
*/
spin_lock_irqsave(&lp->lock, flags);
/* Put out the doubleword header... */
outw(skb->len, ioaddr + TX_FIFO);
outw(0x00, ioaddr + TX_FIFO); /* ... and the packet rounded to a doubleword. */
outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
if (inw(ioaddr + TX_FREE) > 1536)
netif_start_queue(dev); else /* Interrupt us when the FIFO has room for max-sized packet. */
outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
spin_unlock_irqrestore(&lp->lock, flags);
dev_consume_skb_any (skb);
/* Clear the Tx status stack. */
{ short tx_status; int i = 4;
while (--i > 0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) { if (tx_status & 0x38) dev->stats.tx_aborted_errors++; if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD); if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD);
outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
}
} return NETDEV_TX_OK;
}
/* The EL3 interrupt handler. */ static irqreturn_t
el3_interrupt(int irq, void *dev_id)
{ struct net_device *dev = dev_id; struct el3_private *lp; int ioaddr, status; int i = max_interrupt_work;
lp = netdev_priv(dev);
spin_lock(&lp->lock);
ioaddr = dev->base_addr;
if (el3_debug > 4) {
status = inw(ioaddr + EL3_STATUS);
pr_debug("%s: interrupt, status %4.4x.\n", dev->name, status);
}
/* Update statistics. We change to register window 6, so this should be run single-threaded if the device is active. This is expected to be a rare operation, and it's simpler for the rest of the driver to assume that window 1 is always valid rather than use a special window-state variable.
*/ staticvoid update_stats(struct net_device *dev)
{ int ioaddr = dev->base_addr;
/* Back to window 1, and turn statistics back on. */
EL3WINDOW(1);
outw(StatsEnable, ioaddr + EL3_CMD);
}
staticint
el3_rx(struct net_device *dev)
{ int ioaddr = dev->base_addr; short rx_status;
if (el3_debug > 5)
pr_debug(" In rx_packet(), status %4.4x, rx_status %4.4x.\n",
inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS)); while ((rx_status = inw(ioaddr + RX_STATUS)) > 0) { if (rx_status & 0x4000) { /* Error, update stats. */ short error = rx_status & 0x3800;
outw(RxDiscard, ioaddr + EL3_CMD);
dev->stats.rx_errors++; switch (error) { case 0x0000: dev->stats.rx_over_errors++; break; case 0x0800: dev->stats.rx_length_errors++; break; case 0x1000: dev->stats.rx_frame_errors++; break; case 0x1800: dev->stats.rx_length_errors++; break; case 0x2000: dev->stats.rx_frame_errors++; break; case 0x2800: dev->stats.rx_crc_errors++; break;
}
} else { short pkt_len = rx_status & 0x7ff; struct sk_buff *skb;
skb = netdev_alloc_skb(dev, pkt_len + 5); if (el3_debug > 4)
pr_debug("Receiving packet size %d status %4.4x.\n",
pkt_len, rx_status); if (skb != NULL) {
skb_reserve(skb, 2); /* Align IP on 16 byte */
/* 'skb->data' points to the start of sk_buff data area. */
insl(ioaddr + RX_FIFO, skb_put(skb,pkt_len),
(pkt_len + 3) >> 2);
outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
skb->protocol = eth_type_trans(skb,dev);
netif_rx(skb);
dev->stats.rx_bytes += pkt_len;
dev->stats.rx_packets++; continue;
}
outw(RxDiscard, ioaddr + EL3_CMD);
dev->stats.rx_dropped++; if (el3_debug)
pr_debug("%s: Couldn't allocate a sk_buff of size %d.\n",
dev->name, pkt_len);
}
inw(ioaddr + EL3_STATUS); /* Delay. */ while (inw(ioaddr + EL3_STATUS) & 0x1000)
pr_debug(" Waiting for 3c509 to discard packet, status %x.\n",
inw(ioaddr + EL3_STATUS) );
}
return 0;
}
/* * Set or clear the multicast filter for this adaptor.
*/ staticvoid
set_multicast_list(struct net_device *dev)
{ unsignedlong flags; struct el3_private *lp = netdev_priv(dev); int ioaddr = dev->base_addr; int mc_count = netdev_mc_count(dev);
if (el3_debug > 2)
pr_debug("%s: Shutting down ethercard.\n", dev->name);
el3_down(dev);
free_irq(dev->irq, dev); /* Switching back to window 0 disables the IRQ. */
EL3WINDOW(0); if (lp->type != EL3_EISA) { /* But we explicitly zero the IRQ line select anyway. Don't do * it on EISA cards, it prevents the module from getting an
* IRQ after unload+reload... */
outw(0x0f00, ioaddr + WN0_IRQ);
}
staticvoid
el3_down(struct net_device *dev)
{ int ioaddr = dev->base_addr;
netif_stop_queue(dev);
/* Turn off statistics ASAP. We update lp->stats below. */
outw(StatsDisable, ioaddr + EL3_CMD);
/* Disable the receiver and transmitter. */
outw(RxDisable, ioaddr + EL3_CMD);
outw(TxDisable, ioaddr + EL3_CMD);
if (dev->if_port == 3) /* Turn off thinnet power. Green! */
outw(StopCoax, ioaddr + EL3_CMD); elseif (dev->if_port == 0) { /* Disable link beat and jabber, if_port may change here next open(). */
EL3WINDOW(4);
outw(inw(ioaddr + WN4_MEDIA) & ~MEDIA_TP, ioaddr + WN4_MEDIA);
}
outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD);
update_stats(dev);
}
staticvoid
el3_up(struct net_device *dev)
{ int i, sw_info, net_diag; int ioaddr = dev->base_addr;
/* Activating the board required and does no harm otherwise */
outw(0x0001, ioaddr + 4);
/* Set the IRQ line. */
outw((dev->irq << 12) | 0x0f00, ioaddr + WN0_IRQ);
/* Set the station address in window 2 each time opened. */
EL3WINDOW(2);
for (i = 0; i < 6; i++)
outb(dev->dev_addr[i], ioaddr + i);
if ((dev->if_port & 0x03) == 3) /* BNC interface */ /* Start the thinnet transceiver. We should really wait 50ms...*/
outw(StartCoax, ioaddr + EL3_CMD); elseif ((dev->if_port & 0x03) == 0) { /* 10baseT interface */ /* Combine secondary sw_info word (the adapter level) and primary
sw_info word (duplex setting plus other useless bits) */
EL3WINDOW(0);
sw_info = (read_eeprom(ioaddr, 0x14) & 0x400f) |
(read_eeprom(ioaddr, 0x0d) & 0xBff0);
EL3WINDOW(4);
net_diag = inw(ioaddr + WN4_NETDIAG);
net_diag = (net_diag | FD_ENABLE); /* temporarily assume full-duplex will be set */
pr_info("%s: ", dev->name); switch (dev->if_port & 0x0c) { case 12: /* force full-duplex mode if 3c5x9b */ if (sw_info & 0x000f) {
pr_cont("Forcing 3c5x9b full-duplex mode"); break;
}
fallthrough; case 8: /* set full-duplex mode based on eeprom config setting */ if ((sw_info & 0x000f) && (sw_info & 0x8000)) {
pr_cont("Setting 3c5x9b full-duplex mode (from EEPROM configuration bit)"); break;
}
fallthrough; default: /* xcvr=(0 || 4) OR user has an old 3c5x9 non "B" model */
pr_cont("Setting 3c5x9/3c5x9B half-duplex mode");
net_diag = (net_diag & ~FD_ENABLE); /* disable full duplex */
}
outw(net_diag, ioaddr + WN4_NETDIAG);
pr_cont(" if_port: %d, sw_info: %4.4x\n", dev->if_port, sw_info); if (el3_debug > 3)
pr_debug("%s: 3c5x9 net diag word is now: %4.4x.\n", dev->name, net_diag); /* Enable link beat and jabber check. */
outw(inw(ioaddr + WN4_MEDIA) | MEDIA_TP, ioaddr + WN4_MEDIA);
}
/* Switch to the stats window, and clear all stats by reading. */
outw(StatsDisable, ioaddr + EL3_CMD);
EL3WINDOW(6); for (i = 0; i < 9; i++)
inb(ioaddr + i);
inw(ioaddr + 10);
inw(ioaddr + 12);
/* Switch to register set 1 for normal use. */
EL3WINDOW(1);
staticint __init el3_init_module(void)
{ int ret = 0;
if (debug >= 0)
el3_debug = debug;
#ifdef CONFIG_PNP if (!nopnp) {
ret = pnp_register_driver(&el3_pnp_driver); if (!ret)
pnp_registered = 1;
} #endif /* Select an open I/O location at 0x1*0 to do ISA contention select. */ /* Start with 0x110 to avoid some sound cards.*/ for (id_port = 0x110 ; id_port < 0x200; id_port += 0x10) { if (!request_region(id_port, 1, "3c509-control")) continue;
outb(0x00, id_port);
outb(0xff, id_port); if (inb(id_port) & 0x01) break; else
release_region(id_port, 1);
} if (id_port >= 0x200) {
id_port = 0;
pr_err("No I/O port available for 3c509 activation.\n");
} else {
ret = isa_register_driver(&el3_isa_driver, EL3_MAX_CARDS); if (!ret)
isa_registered = 1;
} #ifdef CONFIG_EISA
ret = eisa_driver_register(&el3_eisa_driver); if (!ret)
eisa_registered = 1; #endif
#ifdef CONFIG_PNP if (pnp_registered)
ret = 0; #endif if (isa_registered)
ret = 0; #ifdef CONFIG_EISA if (eisa_registered)
ret = 0; #endif return ret;
}
staticvoid __exit el3_cleanup_module(void)
{ #ifdef CONFIG_PNP if (pnp_registered)
pnp_unregister_driver(&el3_pnp_driver); #endif if (isa_registered)
isa_unregister_driver(&el3_isa_driver); if (id_port)
release_region(id_port, 1); #ifdef CONFIG_EISA if (eisa_registered)
eisa_driver_unregister(&el3_eisa_driver); #endif
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.