#define DRV_NAME "3c509"
#define TX_TIMEOUT (400*HZ/1000)
#include <linux/module.h>
#include <linux/isa.h>
#include <linux/pnp.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/in.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/pm.h>
#include <linux/skbuff.h>
#include <linux/delay.h> /* for udelay() */
#include <linux/spinlock.h>
#include <linux/ethtool.h>
#include <linux/device.h>
#include <linux/eisa.h>
#include <linux/bitops.h>
#include <linux/uaccess.h>
#include <asm/io.h>
#include <asm/irq.h>
#ifdef EL3_DEBUG
static int el3_debug = EL3_DEBUG;
#else
static int el3_debug = 2;
#endif
static int el3_cards = 0;
#define EL3_MAX_CARDS 8
#define EL3_DATA 0x00
#define EL3_CMD 0x0e
#define EL3_STATUS 0x0e
#define EEPROM_READ 0x80
#define EL3_IO_EXTENT 16
#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
enum c509cmd {
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, StatsEnable = 21<<11,
StatsDisable = 22<<11, StopCoax = 23<<11, PowerUp = 27<<11,
PowerDown = 28<<11, PowerAuto = 29<<11};
enum c509status {
IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
IntReq = 0x0040, StatsFull = 0x0080, CmdBusy = 0x1000, };
enum RxFilter {
RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 };
#define TX_FIFO 0x00
#define RX_FIFO 0x00
#define RX_STATUS 0x08
#define TX_STATUS 0x0B
#define TX_FREE 0x0C /* Remaining free bytes in Tx buffer. */
#define WN0_CONF_CTRL 0x04 /* Window 0: Configuration control register */
#define WN0_ADDR_CONF 0x06 /* Window 0: Address configuration register */
#define WN0_IRQ 0x08 /* Window 0: Set IRQ line in bits 12-15. */
#define WN4_MEDIA 0x0A /* Window 4: Various transcvr/media bits. */
#define MEDIA_TP 0x00C0 /* Enable link beat and jabber for 10baseT. */
#define WN4_NETDIAG 0x06 /* Window 4: Net diagnostic */
#define FD_ENABLE 0x8000 /* Enable full-duplex ("external loopback") */
#define SKB_QUEUE_SIZE 64
enum el3_cardtype { EL3_ISA, EL3_PNP, EL3_EISA };
struct el3_private {
spinlock_t lock;
int head, size;
struct sk_buff *queue[SKB_QUEUE_SIZE];
enum el3_cardtype type;
};
static int id_port;
static int current_tag;
static struct net_device *el3_devs[EL3_MAX_CARDS];
static int debug = -1;
static int irq[] = {-1, -1, -1, -1, -1, -1, -1, -1};
static int max_interrupt_work = 10;
#ifdef CONFIG_PNP
static int nopnp;
#endif
static int el3_common_init(struct net_device *dev);
static void el3_common_remove(struct net_device *dev);
static ushort id_read_eeprom(int index);
static ushort read_eeprom(int ioaddr, int index);
static int el3_open(struct net_device *dev);
static netdev_tx_t el3_start_xmit(struct sk_buff *skb, struct net_device *dev);
static irqreturn_t el3_interrupt(int irq, void *dev_id);
static void update_stats(struct net_device *dev);
static struct net_device_stats *el3_get_stats(struct net_device *dev);
static int el3_rx(struct net_device *dev);
static int el3_close(struct net_device *dev);
static void set_multicast_list(struct net_device *dev);
static void el3_tx_timeout (struct net_device *dev, unsigned int txqueue);
static void el3_down(struct net_device *dev);
static void el3_up(struct net_device *dev);
static const struct ethtool_ops ethtool_ops;
#ifdef CONFIG_PM
static int el3_suspend(struct device *, pm_message_t);
static int el3_resume(struct device *);
#else
#define el3_suspend NULL
#define el3_resume NULL
#endif
static int el3_device_remove (struct device *device);
#ifdef CONFIG_NET_POLL_CONTROLLER
static void el3_poll_controller(struct net_device *dev);
#endif
static int el3_isa_id_sequence(__be16 *phys_addr)
{
short lrs_state = 0xff;
int i;
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;
}
if (current_tag == 0)
outb(0xd0, id_port);
else
outb(0xd8, id_port);
if (id_read_eeprom(7) != 0x6d50)
return 1;
for (i = 0; i < 3; i++)
phys_addr[i] = htons(id_read_eeprom(i));
#ifdef CONFIG_PNP
if (!nopnp) {
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);
outb(0xd0 + ++current_tag, id_port);
return 2;
}
}
}
#endif /* CONFIG_PNP */
return 0;
}
static void 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);
eth_hw_addr_set(dev, (u8 *)phys_addr);
dev->base_addr = ioaddr;
dev->irq = irq;
dev->if_port = if_port;
lp->type = type;
}
static int el3_isa_match(struct device *pdev, unsigned int ndev)
{
struct net_device *dev;
int ioaddr, isa_irq, if_port, err;
unsigned int iobase;
__be16 phys_addr[3];
while ((err = el3_isa_id_sequence(phys_addr)) == 2)
;
if (err == 1)
return 0;
iobase = id_read_eeprom(8);
if_port = iobase >> 14;
ioaddr = 0x200 + ((iobase & 0x1f) << 4);
if (irq[el3_cards] > 1 && irq[el3_cards] < 16)
isa_irq = irq[el3_cards];
else
isa_irq = id_read_eeprom(9) >> 12;
dev = alloc_etherdev(sizeof(struct el3_private));
if (!dev)
return -ENOMEM;
SET_NETDEV_DEV(dev, pdev);
if (!request_region(ioaddr, EL3_IO_EXTENT, "3c509-isa")) {
free_netdev(dev);
return 0;
}
outb(0xd0 + ++current_tag, id_port);
outb((ioaddr >> 4) | 0xe0, id_port);
EL3WINDOW(0);
if (inw(ioaddr) != 0x6d50) {
free_netdev(dev);
return 0;
}
outw(0x0f00, ioaddr + WN0_IRQ);
el3_dev_fill(dev, phys_addr, ioaddr, isa_irq, if_port, EL3_ISA);
dev_set_drvdata(pdev, dev);
if (el3_common_init(dev)) {
free_netdev(dev);
return 0;
}
el3_devs[el3_cards++] = dev;
return 1;
}
static void el3_isa_remove(struct device *pdev,
unsigned int ndev)
{
el3_device_remove(pdev);
dev_set_drvdata(pdev, NULL);
}
#ifdef CONFIG_PM
static int el3_isa_suspend(struct device *dev, unsigned int n,
pm_message_t state)
{
current_tag = 0;
return el3_suspend(dev, state);
}
static int el3_isa_resume(struct device *dev, unsigned int n)
{
struct net_device *ndev = dev_get_drvdata(dev);
int ioaddr = ndev->base_addr, err;
__be16 phys_addr[3];
while ((err = el3_isa_id_sequence(phys_addr)) == 2)
;
if (err == 1)
return 0;
outb(0xd0 + ++current_tag, id_port);
outb((ioaddr >> 4) | 0xe0, id_port);
EL3WINDOW(0);
if (inw(ioaddr) != 0x6d50)
return 1;
outw(0x0f00, ioaddr + WN0_IRQ);
return el3_resume(dev);
}
#endif
static struct isa_driver el3_isa_driver = {
.match = el3_isa_match,
.remove = el3_isa_remove,
#ifdef CONFIG_PM
.suspend = el3_isa_suspend,
.resume = el3_isa_resume,
#endif
.driver = {
.name = "3c509"
},
};
static int isa_registered;
#ifdef CONFIG_PNP
static const struct pnp_device_id el3_pnp_ids[] = {
{ .id = "TCM5090" },
{ .id = "TCM5091" },
{ .id = "TCM5094" },
{ .id = "TCM5095" },
{ .id = "TCM5098" },
{ .id = "PNP80f7" },
{ .id = "PNP80f8" },
{ .id = "" }
};
MODULE_DEVICE_TABLE(pnp, el3_pnp_ids);
static int el3_pnp_probe(struct pnp_dev *pdev, const struct pnp_device_id *id)
{
short i;
int ioaddr, irq, if_port;
__be16 phys_addr[3];
struct net_device *dev = NULL;
int err;
ioaddr = pnp_port_start(pdev, 0);
if (!request_region(ioaddr, EL3_IO_EXTENT, "3c509-pnp"))
return -EBUSY;
irq = pnp_irq(pdev, 0);
EL3WINDOW(0);
for (i = 0; i < 3; i++)
phys_addr[i] = htons(read_eeprom(ioaddr, i));
if_port = read_eeprom(ioaddr, 8) >> 14;
dev = alloc_etherdev(sizeof(struct el3_private));
if (!dev) {
release_region(ioaddr, EL3_IO_EXTENT);
return -ENOMEM;
}
SET_NETDEV_DEV(dev, &pdev->dev);
el3_dev_fill(dev, phys_addr, ioaddr, irq, if_port, EL3_PNP);
pnp_set_drvdata(pdev, dev);
err = el3_common_init(dev);
if (err) {
pnp_set_drvdata(pdev, NULL);
free_netdev(dev);
return err;
}
el3_devs[el3_cards++] = dev;
return 0;
}
static void el3_pnp_remove(struct pnp_dev *pdev)
{
el3_common_remove(pnp_get_drvdata(pdev));
pnp_set_drvdata(pdev, NULL);
}
#ifdef CONFIG_PM
static int el3_pnp_suspend(struct pnp_dev *pdev, pm_message_t state)
{
return el3_suspend(&pdev->dev, state);
}
static int el3_pnp_resume(struct pnp_dev *pdev)
{
return el3_resume(&pdev->dev);
}
#endif
static struct pnp_driver el3_pnp_driver = {
.name = "3c509",
.id_table = el3_pnp_ids,
.probe = el3_pnp_probe,
.remove = el3_pnp_remove,
#ifdef CONFIG_PM
.suspend = el3_pnp_suspend,
.resume = el3_pnp_resume,
#endif
};
static int pnp_registered;
#endif /* CONFIG_PNP */
#ifdef CONFIG_EISA
static const struct eisa_device_id el3_eisa_ids[] = {
{ "TCM5090" },
{ "TCM5091" },
{ "TCM5092" },
{ "TCM5093" },
{ "TCM5094" },
{ "TCM5095" },
{ "TCM5098" },
{ "" }
};
MODULE_DEVICE_TABLE(eisa, el3_eisa_ids);
static int el3_eisa_probe (struct device *device);
static struct eisa_driver el3_eisa_driver = {
.id_table = el3_eisa_ids,
.driver = {
.name = "3c579",
.probe = el3_eisa_probe,
.remove = el3_device_remove,
.suspend = el3_suspend,
.resume = el3_resume,
}
};
static int eisa_registered;
#endif
static const struct net_device_ops netdev_ops = {
.ndo_open = el3_open,
.ndo_stop = el3_close,
.ndo_start_xmit = el3_start_xmit,
.ndo_get_stats = el3_get_stats,
.ndo_set_rx_mode = set_multicast_list,
.ndo_tx_timeout = el3_tx_timeout,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = el3_poll_controller,
#endif
};
static int el3_common_init(struct net_device *dev)
{
struct el3_private *lp = netdev_priv(dev);
int err;
static const char * const if_names[] = {
"10baseT", "AUI", "undefined", "BNC"
};
spin_lock_init(&lp->lock);
if (dev->mem_start & 0x05) {
dev->if_port = (dev->mem_start & 0x0f);
} else {
dev->if_port |= (dev->mem_start & 0x08);
}
dev->netdev_ops = &netdev_ops;
dev->watchdog_timeo = TX_TIMEOUT;
dev->ethtool_ops = ðtool_ops;
err = register_netdev(dev);
if (err) {
pr_err("Failed to register 3c5x9 at %#3.3lx, IRQ %d.\n",
dev->base_addr, dev->irq);
release_region(dev->base_addr, EL3_IO_EXTENT);
return err;
}
pr_info("%s: 3c5x9 found at %#3.3lx, %s port, address %pM, IRQ %d.\n",
dev->name, dev->base_addr, if_names[(dev->if_port & 0x03)],
dev->dev_addr, dev->irq);
return 0;
}
static void el3_common_remove (struct net_device *dev)
{
unregister_netdev (dev);
release_region(dev->base_addr, EL3_IO_EXTENT);
free_netdev (dev);
}
#ifdef CONFIG_EISA
static int el3_eisa_probe(struct device *device)
{
short i;
int ioaddr, irq, if_port;
__be16 phys_addr[3];
struct net_device *dev = NULL;
struct eisa_device *edev;
int err;
edev = to_eisa_device (device);
ioaddr = edev->base_addr;
if (!request_region(ioaddr, EL3_IO_EXTENT, "3c579-eisa"))
return -EBUSY;
outw(SelectWindow | 0, ioaddr + 0xC80 + EL3_CMD);
irq = inw(ioaddr + WN0_IRQ) >> 12;
if_port = inw(ioaddr + 6)>>14;
for (i = 0; i < 3; i++)
phys_addr[i] = htons(read_eeprom(ioaddr, i));
read_eeprom(ioaddr, 3);
dev = alloc_etherdev(sizeof (struct el3_private));
if (dev == NULL) {
release_region(ioaddr, EL3_IO_EXTENT);
return -ENOMEM;
}
SET_NETDEV_DEV(dev, device);
el3_dev_fill(dev, phys_addr, ioaddr, irq, if_port, EL3_EISA);
eisa_set_drvdata (edev, dev);
err = el3_common_init(dev);
if (err) {
eisa_set_drvdata (edev, NULL);
free_netdev(dev);
return err;
}
el3_devs[el3_cards++] = dev;
return 0;
}
#endif
static int el3_device_remove(struct device *device)
{
struct net_device *dev;
dev = dev_get_drvdata(device);
el3_common_remove (dev);
return 0;
}
static ushort read_eeprom(int ioaddr, int index)
{
outw(EEPROM_READ + index, ioaddr + 10);
mdelay(2);
return inw(ioaddr + 12);
}
static ushort id_read_eeprom(int index)
{
int bit, word = 0;
outb(EEPROM_READ + index, id_port);
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;
}
static int
el3_open(struct net_device *dev)
{
int ioaddr = dev->base_addr;
int i;
outw(TxReset, ioaddr + EL3_CMD);
outw(RxReset, ioaddr + EL3_CMD);
outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
i = request_irq(dev->irq, el3_interrupt, 0, dev->name, dev);
if (i)
return i;
EL3WINDOW(0);
if (el3_debug > 3)
pr_debug("%s: Opening, IRQ %d status@%x %4.4x.\n", dev->name,
dev->irq, ioaddr + EL3_STATUS, inw(ioaddr + EL3_STATUS));
el3_up(dev);
if (el3_debug > 3)
pr_debug("%s: Opened 3c509 IRQ %d status %4.4x.\n",
dev->name, dev->irq, inw(ioaddr + EL3_STATUS));
return 0;
}
static void
el3_tx_timeout (struct net_device *dev, unsigned int txqueue)
{
int ioaddr = dev->base_addr;
pr_warn("%s: transmit timed out, Tx_status %2.2x status %4.4x Tx FIFO room %d\n",
dev->name, inb(ioaddr + TX_STATUS), inw(ioaddr + EL3_STATUS),
inw(ioaddr + TX_FREE));
dev->stats.tx_errors++;
netif_trans_update(dev);
outw(TxReset, ioaddr + EL3_CMD);
outw(TxEnable, ioaddr + EL3_CMD);
netif_wake_queue(dev);
}
static netdev_tx_t
el3_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct el3_private *lp = netdev_priv(dev);
int ioaddr = dev->base_addr;
unsigned long flags;
netif_stop_queue (dev);
dev->stats.tx_bytes += skb->len;
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));
}
spin_lock_irqsave(&lp->lock, flags);
outw(skb->len, ioaddr + TX_FIFO);
outw(0x00, ioaddr + TX_FIFO);
outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
if (inw(ioaddr + TX_FREE) > 1536)
netif_start_queue(dev);
else
outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
spin_unlock_irqrestore(&lp->lock, flags);
dev_consume_skb_any (skb);
{
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);
}
}
return NETDEV_TX_OK;
}
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);
}
while ((status = inw(ioaddr + EL3_STATUS)) &
(IntLatch | RxComplete | StatsFull)) {
if (status & RxComplete)
el3_rx(dev);
if (status & TxAvailable) {
if (el3_debug > 5)
pr_debug(" TX room bit was handled.\n");
outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
netif_wake_queue (dev);
}
if (status & (AdapterFailure | RxEarly | StatsFull | TxComplete)) {
if (status & StatsFull)
update_stats(dev);
if (status & RxEarly) {
el3_rx(dev);
outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
}
if (status & TxComplete) {
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);
}
}
if (status & AdapterFailure) {
outw(RxReset, ioaddr + EL3_CMD);
outw(SetRxFilter | RxStation | RxBroadcast
| (dev->flags & IFF_ALLMULTI ? RxMulticast : 0)
| (dev->flags & IFF_PROMISC ? RxProm : 0),
ioaddr + EL3_CMD);
outw(RxEnable, ioaddr + EL3_CMD);
outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
}
}
if (--i < 0) {
pr_err("%s: Infinite loop in interrupt, status %4.4x.\n",
dev->name, status);
outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
break;
}
outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
}
if (el3_debug > 4) {
pr_debug("%s: exiting interrupt, status %4.4x.\n", dev->name,
inw(ioaddr + EL3_STATUS));
}
spin_unlock(&lp->lock);
return IRQ_HANDLED;
}
#ifdef CONFIG_NET_POLL_CONTROLLER
static void el3_poll_controller(struct net_device *dev)
{
disable_irq(dev->irq);
el3_interrupt(dev->irq, dev);
enable_irq(dev->irq);
}
#endif
static struct net_device_stats *
el3_get_stats(struct net_device *dev)
{
struct el3_private *lp = netdev_priv(dev);
unsigned long flags;
spin_lock_irqsave(&lp->lock, flags);
update_stats(dev);
spin_unlock_irqrestore(&lp->lock, flags);
return &dev->stats;
}
static void update_stats(struct net_device *dev)
{
int ioaddr = dev->base_addr;
if (el3_debug > 5)
pr_debug(" Updating the statistics.\n");
outw(StatsDisable, ioaddr + EL3_CMD);
EL3WINDOW(6);
dev->stats.tx_carrier_errors += inb(ioaddr + 0);
dev->stats.tx_heartbeat_errors += inb(ioaddr + 1);
inb(ioaddr + 2);
dev->stats.collisions += inb(ioaddr + 3);
dev->stats.tx_window_errors += inb(ioaddr + 4);
dev->stats.rx_fifo_errors += inb(ioaddr + 5);
dev->stats.tx_packets += inb(ioaddr + 6);
inb(ioaddr + 7);
inb(ioaddr + 8);
inw(ioaddr + 10);
inw(ioaddr + 12);
EL3WINDOW(1);
outw(StatsEnable, ioaddr + EL3_CMD);
}
static int
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) {
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);
insl(ioaddr + RX_FIFO, skb_put(skb,pkt_len),
(pkt_len + 3) >> 2);
outw(RxDiscard, ioaddr + EL3_CMD);
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);
while (inw(ioaddr + EL3_STATUS) & 0x1000)
pr_debug(" Waiting for 3c509 to discard packet, status %x.\n",
inw(ioaddr + EL3_STATUS) );
}
return 0;
}
static void
set_multicast_list(struct net_device *dev)
{
unsigned long flags;
struct el3_private *lp = netdev_priv(dev);
int ioaddr = dev->base_addr;
int mc_count = netdev_mc_count(dev);
if (el3_debug > 1) {
static int old;
if (old != mc_count) {
old = mc_count;
pr_debug("%s: Setting Rx mode to %d addresses.\n",
dev->name, mc_count);
}
}
spin_lock_irqsave(&lp->lock, flags);
if (dev->flags&IFF_PROMISC) {
outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm,
ioaddr + EL3_CMD);
}
else if (mc_count || (dev->flags&IFF_ALLMULTI)) {
outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast, ioaddr + EL3_CMD);
}
else
outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
spin_unlock_irqrestore(&lp->lock, flags);
}
static int
el3_close(struct net_device *dev)
{
int ioaddr = dev->base_addr;
struct el3_private *lp = netdev_priv(dev);
if (el3_debug > 2)
pr_debug("%s: Shutting down ethercard.\n", dev->name);
el3_down(dev);
free_irq(dev->irq, dev);
EL3WINDOW(0);
if (lp->type != EL3_EISA) {
outw(0x0f00, ioaddr + WN0_IRQ);
}
return 0;
}
static int
el3_link_ok(struct net_device *dev)
{
int ioaddr = dev->base_addr;
u16 tmp;
EL3WINDOW(4);
tmp = inw(ioaddr + WN4_MEDIA);
EL3WINDOW(1);
return tmp & (1<<11);
}
static void
el3_netdev_get_ecmd(struct net_device *dev, struct ethtool_link_ksettings *cmd)
{
u16 tmp;
int ioaddr = dev->base_addr;
u32 supported;
EL3WINDOW(0);
tmp = inw(ioaddr + WN0_ADDR_CONF);
switch (tmp >> 14) {
case 0:
cmd->base.port = PORT_TP;
break;
case 1:
cmd->base.port = PORT_AUI;
break;
case 3:
cmd->base.port = PORT_BNC;
break;
default:
break;
}
cmd->base.duplex = DUPLEX_HALF;
supported = 0;
tmp = inw(ioaddr + WN0_CONF_CTRL);
if (tmp & (1<<13))
supported |= SUPPORTED_AUI;
if (tmp & (1<<12))
supported |= SUPPORTED_BNC;
if (tmp & (1<<9)) {
supported |= SUPPORTED_TP | SUPPORTED_10baseT_Half |
SUPPORTED_10baseT_Full;
EL3WINDOW(4);
tmp = inw(ioaddr + WN4_NETDIAG);
if (tmp & FD_ENABLE)
cmd->base.duplex = DUPLEX_FULL;
}
ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
supported);
cmd->base.speed = SPEED_10;
EL3WINDOW(1);
}
static int
el3_netdev_set_ecmd(struct net_device *dev,
const struct ethtool_link_ksettings *cmd)
{
u16 tmp;
int ioaddr = dev->base_addr;
if (cmd->base.speed != SPEED_10)
return -EINVAL;
if ((cmd->base.duplex != DUPLEX_HALF) &&
(cmd->base.duplex != DUPLEX_FULL))
return -EINVAL;
EL3WINDOW(0);
tmp = inw(ioaddr + WN0_ADDR_CONF);
switch (cmd->base.port) {
case PORT_TP:
tmp &= ~(3<<14);
dev->if_port = 0;
break;
case PORT_AUI:
tmp |= (1<<14);
dev->if_port = 1;
break;
case PORT_BNC:
tmp |= (3<<14);
dev->if_port = 3;
break;
default:
return -EINVAL;
}
outw(tmp, ioaddr + WN0_ADDR_CONF);
if (dev->if_port == 3) {
tmp = inw(ioaddr + WN0_ADDR_CONF);
if (tmp & (3 << 14)) {
outw(StartCoax, ioaddr + EL3_CMD);
udelay(800);
} else
return -EIO;
}
EL3WINDOW(4);
tmp = inw(ioaddr + WN4_NETDIAG);
if (cmd->base.duplex == DUPLEX_FULL)
tmp |= FD_ENABLE;
else
tmp &= ~FD_ENABLE;
outw(tmp, ioaddr + WN4_NETDIAG);
EL3WINDOW(1);
return 0;
}
static void el3_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
{
strscpy(info->driver, DRV_NAME, sizeof(info->driver));
}
static int el3_get_link_ksettings(struct net_device *dev,
struct ethtool_link_ksettings *cmd)
{
struct el3_private *lp = netdev_priv(dev);
spin_lock_irq(&lp->lock);
el3_netdev_get_ecmd(dev, cmd);
spin_unlock_irq(&lp->lock);
return 0;
}
static int el3_set_link_ksettings(struct net_device *dev,
const struct ethtool_link_ksettings *cmd)
{
struct el3_private *lp = netdev_priv(dev);
int ret;
spin_lock_irq(&lp->lock);
ret = el3_netdev_set_ecmd(dev, cmd);
spin_unlock_irq(&lp->lock);
return ret;
}
static u32 el3_get_link(struct net_device *dev)
{
struct el3_private *lp = netdev_priv(dev);
u32 ret;
spin_lock_irq(&lp->lock);
ret = el3_link_ok(dev);
spin_unlock_irq(&lp->lock);
return ret;
}
static u32 el3_get_msglevel(struct net_device *dev)
{
return el3_debug;
}
static void el3_set_msglevel(struct net_device *dev, u32 v)
{
el3_debug = v;
}
static const struct ethtool_ops ethtool_ops = {
.get_drvinfo = el3_get_drvinfo,
.get_link = el3_get_link,
.get_msglevel = el3_get_msglevel,
.set_msglevel = el3_set_msglevel,
.get_link_ksettings = el3_get_link_ksettings,
.set_link_ksettings = el3_set_link_ksettings,
};
static void
el3_down(struct net_device *dev)
{
int ioaddr = dev->base_addr;
netif_stop_queue(dev);
outw(StatsDisable, ioaddr + EL3_CMD);
outw(RxDisable, ioaddr + EL3_CMD);
outw(TxDisable, ioaddr + EL3_CMD);
if (dev->if_port == 3)
outw(StopCoax, ioaddr + EL3_CMD);
else if (dev->if_port == 0) {
EL3WINDOW(4);
outw(inw(ioaddr + WN4_MEDIA) & ~MEDIA_TP, ioaddr + WN4_MEDIA);
}
outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD);
update_stats(dev);
}
static void
el3_up(struct net_device *dev)
{
int i, sw_info, net_diag;
int ioaddr = dev->base_addr;
outw(0x0001, ioaddr + 4);
outw((dev->irq << 12) | 0x0f00, ioaddr + WN0_IRQ);
EL3WINDOW(2);
for (i = 0; i < 6; i++)
outb(dev->dev_addr[i], ioaddr + i);
if ((dev->if_port & 0x03) == 3)
outw(StartCoax, ioaddr + EL3_CMD);
else if ((dev->if_port & 0x03) == 0) {
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);
pr_info("%s: ", dev->name);
switch (dev->if_port & 0x0c) {
case 12:
if (sw_info & 0x000f) {
pr_cont("Forcing 3c5x9b full-duplex mode");
break;
}
fallthrough;
case 8:
if ((sw_info & 0x000f) && (sw_info & 0x8000)) {
pr_cont("Setting 3c5x9b full-duplex mode (from EEPROM configuration bit)");
break;
}
fallthrough;
default:
pr_cont("Setting 3c5x9/3c5x9B half-duplex mode");
net_diag = (net_diag & ~FD_ENABLE);
}
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);
outw(inw(ioaddr + WN4_MEDIA) | MEDIA_TP, ioaddr + WN4_MEDIA);
}
outw(StatsDisable, ioaddr + EL3_CMD);
EL3WINDOW(6);
for (i = 0; i < 9; i++)
inb(ioaddr + i);
inw(ioaddr + 10);
inw(ioaddr + 12);
EL3WINDOW(1);
outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
outw(StatsEnable, ioaddr + EL3_CMD);
outw(RxEnable, ioaddr + EL3_CMD);
outw(TxEnable, ioaddr + EL3_CMD);
outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
ioaddr + EL3_CMD);
outw(SetIntrEnb | IntLatch|TxAvailable|TxComplete|RxComplete|StatsFull,
ioaddr + EL3_CMD);
netif_start_queue(dev);
}
#ifdef CONFIG_PM
static int
el3_suspend(struct device *pdev, pm_message_t state)
{
unsigned long flags;
struct net_device *dev;
struct el3_private *lp;
int ioaddr;
dev = dev_get_drvdata(pdev);
lp = netdev_priv(dev);
ioaddr = dev->base_addr;
spin_lock_irqsave(&lp->lock, flags);
if (netif_running(dev))
netif_device_detach(dev);
el3_down(dev);
outw(PowerDown, ioaddr + EL3_CMD);
spin_unlock_irqrestore(&lp->lock, flags);
return 0;
}
static int
el3_resume(struct device *pdev)
{
unsigned long flags;
struct net_device *dev;
struct el3_private *lp;
int ioaddr;
dev = dev_get_drvdata(pdev);
lp = netdev_priv(dev);
ioaddr = dev->base_addr;
spin_lock_irqsave(&lp->lock, flags);
outw(PowerUp, ioaddr + EL3_CMD);
EL3WINDOW(0);
el3_up(dev);
if (netif_running(dev))
netif_device_attach(dev);
spin_unlock_irqrestore(&lp->lock, flags);
return 0;
}
#endif /* CONFIG_PM */
module_param(debug,int, 0);
module_param_hw_array(irq, int, irq, NULL, 0);
module_param(max_interrupt_work, int, 0);
MODULE_PARM_DESC(debug, "debug level (0-6)");
MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
MODULE_PARM_DESC(max_interrupt_work, "maximum events handled per interrupt");
#ifdef CONFIG_PNP
module_param(nopnp, int, 0);
MODULE_PARM_DESC(nopnp, "disable ISA PnP support (0-1)");
#endif /* CONFIG_PNP */
MODULE_DESCRIPTION("3Com Etherlink III (3c509, 3c509B, 3c529, 3c579) ethernet driver");
MODULE_LICENSE("GPL");
static int __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
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;
}
static void __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
}
module_init (el3_init_module);
module_exit