/* ====================================================================== * * A PCMCIA ethernet driver for the 3com 3c589 card. * * Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net * * 3c589_cs.c 1.162 2001/10/13 00:08:50 * * The network driver code is based on Donald Becker's 3c589 code: * * Written 1994 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. * Donald Becker may be reached at becker@scyld.com * * Updated for 2.5.x by Alan Cox <alan@lxorguk.ukuu.org.uk> * * ======================================================================
*/
/* To minimize the size of the driver source I only define operating * constants if they are used several times. You'll need the manual * if you want to understand driver details.
*/
/* Offsets from base I/O address. */ #define EL3_DATA 0x00 #define EL3_TIMER 0x0a #define EL3_CMD 0x0e #define EL3_STATUS 0x0e
/* Is this a 3c562? */ if (link->manf_id != MANFID_3COM)
dev_info(&link->dev, "hmmm, is this really a 3Com card??\n");
multi = (link->card_id == PRODID_3COM_3C562);
link->io_lines = 16;
/* For the 3c562, the base address must be xx00-xx7f */ for (i = j = 0; j < 0x400; j += 0x10) { if (multi && (j & 0x80)) continue;
link->resource[0]->start = j ^ 0x300;
i = pcmcia_request_io(link); if (i == 0) break;
} if (i != 0) goto failed;
ret = pcmcia_request_irq(link, el3_interrupt); if (ret) goto failed;
ret = pcmcia_enable_device(link); if (ret) goto failed;
/* The 3c589 has an extra EEPROM for configuration info, including * the hardware address. The 3c562 puts the address in the CIS.
*/
len = pcmcia_get_tuple(link, 0x88, &buf); if (buf && len >= 6) { for (i = 0; i < 3; i++)
addr[i] = htons(le16_to_cpu(buf[i*2]));
kfree(buf);
} else {
kfree(buf); /* 0 < len < 6 */ for (i = 0; i < 3; i++)
addr[i] = htons(read_eeprom(ioaddr, i)); if (addr[0] == htons(0x6060)) {
dev_err(&link->dev, "IO port conflict at 0x%03lx-0x%03lx\n",
dev->base_addr, dev->base_addr+15); goto failed;
}
}
eth_hw_addr_set(dev, (u8 *)addr);
/* The address and resource configuration register aren't loaded from * the EEPROM and *must* be set to 0 and IRQ3 for the PCMCIA version.
*/
outw(0x3f00, ioaddr + 8);
fifo = inl(ioaddr);
/* The if_port symbol can be set when the module is loaded */ if ((if_port >= 0) && (if_port <= 3))
dev->if_port = if_port; else
dev_err(&link->dev, "invalid if_port requested\n");
/* Use this for commands that may take time to finish */
staticvoid tc589_wait_for_completion(struct net_device *dev, int cmd)
{ int i = 100;
outw(cmd, dev->base_addr + EL3_CMD); while (--i > 0) if (!(inw(dev->base_addr + EL3_STATUS) & 0x1000)) break; if (i == 0)
netdev_warn(dev, "command 0x%04x did not complete!\n", cmd);
}
/* Read a word from the EEPROM using the regular EEPROM access register. * Assume that we are in register window zero.
*/
static u16 read_eeprom(unsignedint ioaddr, int index)
{ int i;
outw(EEPROM_READ + index, ioaddr + 10); /* Reading the eeprom takes 162 us */ for (i = 1620; i >= 0; i--) if ((inw(ioaddr + 10) & EEPROM_BUSY) == 0) break; return inw(ioaddr + 12);
}
/* Set transceiver type, perhaps to something other than what the user * specified in dev->if_port.
*/
EL3WINDOW(0); switch (if_port) { case 0: case 1:
outw(0, ioaddr + 6); break; case 2:
outw(3<<14, ioaddr + 6); break; case 3:
outw(1<<14, ioaddr + 6); break;
} /* On PCMCIA, this just turns on the LED */
outw((if_port == 2) ? StartCoax : StopCoax, ioaddr + EL3_CMD); /* 10baseT interface, enable link beat and jabber check. */
EL3WINDOW(4);
outw(MEDIA_LED | ((if_port < 2) ? MEDIA_TP : 0), ioaddr + WN4_MEDIA);
EL3WINDOW(1); if (if_port == 2)
lp->media_status = ((dev->if_port == 0) ? 0x8000 : 0x4000); else
lp->media_status = ((dev->if_port == 0) ? 0x4010 : 0x8800);
}
staticvoid dump_status(struct net_device *dev)
{ unsignedint ioaddr = dev->base_addr;
EL3WINDOW(1);
netdev_info(dev, " irq status %04x, rx status %04x, tx status %02x tx free %04x\n",
inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS),
inb(ioaddr+TX_STATUS), inw(ioaddr+TX_FREE));
EL3WINDOW(4);
netdev_info(dev, " diagnostics: fifo %04x net %04x ethernet %04x media %04x\n",
inw(ioaddr+0x04), inw(ioaddr+0x06), inw(ioaddr+0x08),
inw(ioaddr+0x0a));
EL3WINDOW(1);
}
/* Reset and restore all of the 3c589 registers. */ staticvoid tc589_reset(struct net_device *dev)
{ unsignedint ioaddr = dev->base_addr; int i;
EL3WINDOW(0);
outw(0x0001, ioaddr + 4); /* Activate board. */
outw(0x3f00, ioaddr + 8); /* Set the IRQ line. */
/* Set the station address in window 2. */
EL3WINDOW(2); for (i = 0; i < 6; i++)
outb(dev->dev_addr[i], ioaddr + i);
tc589_set_xcvr(dev, dev->if_port);
/* 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);
set_rx_mode(dev);
outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */ /* Allow status bits to be seen. */
outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD); /* Ack all pending events, and set active indicator mask. */
outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
ioaddr + EL3_CMD);
outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
| AdapterFailure, ioaddr + EL3_CMD);
}
/* 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_stop_queue(dev); /* Interrupt us when the FIFO has room for max-sized packet. */
outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
}
/* Check for pending interrupt with expired latency timer: with * this, we can limp along even if the interrupt is blocked
*/ if ((inw(ioaddr + EL3_STATUS) & IntLatch) &&
(inb(ioaddr + EL3_TIMER) == 0xff)) { if (!lp->fast_poll)
netdev_warn(dev, "interrupt(s) dropped!\n");
/* 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. * * Caller must hold the lock for this
*/
dev_dbg(&link->dev, "%s: shutting down ethercard.\n", dev->name);
if (pcmcia_dev_present(link)) { /* Turn off statistics ASAP. We update dev->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 == 2) /* Turn off thinnet power. Green! */
outw(StopCoax, ioaddr + EL3_CMD); elseif (dev->if_port == 1) { /* Disable link beat and jabber */
EL3WINDOW(4);
outw(0, ioaddr + WN4_MEDIA);
}
/* Switching back to window 0 disables the IRQ. */
EL3WINDOW(0); /* But we explicitly zero the IRQ line select anyway. */
outw(0x0f00, ioaddr + WN0_IRQ);
/* Check if the card still exists */ if ((inw(ioaddr+EL3_STATUS) & 0xe000) == 0x2000)
update_stats(dev);
}
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.