*************************************************************************/
#define DRV_NAME "pcnet32"
-#define DRV_VERSION "1.29"
-#define DRV_RELDATE "04.06.2004"
+#define DRV_VERSION "1.30i"
+#define DRV_RELDATE "06.28.2004"
#define PFX DRV_NAME ": "
static const char *version =
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
+#include <linux/moduleparam.h>
+#include <linux/bitops.h>
-#include <asm/bitops.h>
#include <asm/dma.h>
#include <asm/io.h>
#include <asm/uaccess.h>
static struct net_device *pcnet32_dev;
-static int max_interrupt_work = 80;
+static int max_interrupt_work = 2;
static int rx_copybreak = 200;
#define PCNET32_PORT_AUI 0x00
};
#define PCNET32_TEST_LEN (sizeof(pcnet32_gstrings_test) / ETH_GSTRING_LEN)
+#define PCNET32_NUM_REGS 168
+
#define MAX_UNITS 8 /* More are supported, limit only on options */
static int options[MAX_UNITS];
static int full_duplex[MAX_UNITS];
+static int homepna[MAX_UNITS];
/*
* Theory of Operation
* Jim Lewis <jklewis@us.ibm.com> added ethernet loopback test.
* Thomas Munck Steenholdt <tmus@tmus.dk> non-mii ioctl corrections.
* v1.29 6 Apr 2004 Jim Lewis <jklewis@us.ibm.com> added physical
- * identification code (blink led's).
+ * identification code (blink led's) and register dump.
+ * Don Fry added timer for 971/972 so skbufs don't remain on tx ring
+ * forever.
+ * v1.30 18 May 2004 Don Fry removed timer and Last Transmit Interrupt
+ * (ltint) as they added complexity and didn't give good throughput.
+ * v1.30a 22 May 2004 Don Fry limit frames received during interrupt.
+ * v1.30b 24 May 2004 Don Fry fix bogus tx carrier errors with 79c973,
+ * assisted by Bruce Penrod <bmpenrod@endruntechnologies.com>.
+ * v1.30c 25 May 2004 Don Fry added netif_wake_queue after pcnet32_restart.
+ * v1.30d 01 Jun 2004 Don Fry discard oversize rx packets.
+ * v1.30e 11 Jun 2004 Don Fry recover after fifo error and rx hang.
+ * v1.30f 16 Jun 2004 Don Fry cleanup IRQ to allow 0 and 1 for PCI,
+ * expanding on suggestions from Ralf Baechle <ralf@linux-mips.org>,
+ * and Brian Murphy <brian@murphy.dk>.
+ * v1.30g 22 Jun 2004 Patrick Simmons <psimmons@flash.net> added option
+ * homepna for selecting HomePNA mode for PCNet/Home 79C978.
+ * v1.30h 24 Jun 2004 Don Fry correctly select auto, speed, duplex in bcr32.
+ * v1.30i 28 Jun 2004 Don Fry change to use module_param.
*/
struct net_device_stats stats;
char tx_full;
int options;
- int shared_irq:1, /* shared irq possible */
- ltint:1, /* enable TxDone-intr inhibitor */
- dxsuflo:1, /* disable transmit stop on uflo */
- mii:1; /* mii port available */
+ unsigned int shared_irq:1, /* shared irq possible */
+ dxsuflo:1, /* disable transmit stop on uflo */
+ mii:1; /* mii port available */
struct net_device *next;
struct mii_if_info mii_if;
struct timer_list watchdog_timer;
static void pcnet32_probe_vlbus(void);
static int pcnet32_probe_pci(struct pci_dev *, const struct pci_device_id *);
-static int pcnet32_probe1(unsigned long, unsigned int, int, struct pci_dev *);
+static int pcnet32_probe1(unsigned long, int, struct pci_dev *);
static int pcnet32_open(struct net_device *);
static int pcnet32_init_ring(struct net_device *);
static int pcnet32_start_xmit(struct sk_buff *, struct net_device *);
static int pcnet32_loopback_test(struct net_device *dev, uint64_t *data1);
static int pcnet32_phys_id(struct net_device *dev, u32 data);
static void pcnet32_led_blink_callback(struct net_device *dev);
+static int pcnet32_get_regs_len(struct net_device *dev);
+static void pcnet32_get_regs(struct net_device *dev, struct ethtool_regs *regs,
+ void *ptr);
enum pci_flags_bit {
PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
#ifdef CONFIG_NET_POLL_CONTROLLER
static void pcnet32_poll_controller(struct net_device *dev)
{
- disable_irq(dev->irq);
- pcnet32_interrupt(0, dev, NULL);
- enable_irq(dev->irq);
+ disable_irq(dev->irq);
+ pcnet32_interrupt(0, dev, NULL);
+ enable_irq(dev->irq);
}
#endif
for (i=0; i<6; i++)
*packet++ = dev->dev_addr[i];
for (i=0; i<6; i++)
- *packet++ = dev->dev_addr[i];
+ *packet++ = dev->dev_addr[i];
/* type */
*packet++ = 0x08;
*packet++ = 0x06;
return 0;
}
+static int pcnet32_get_regs_len(struct net_device *dev)
+{
+ return(PCNET32_NUM_REGS * sizeof(u16));
+}
+
+static void pcnet32_get_regs(struct net_device *dev, struct ethtool_regs *regs,
+ void *ptr)
+{
+ int i, csr0;
+ u16 *buff = ptr;
+ struct pcnet32_private *lp = dev->priv;
+ struct pcnet32_access *a = &lp->a;
+ ulong ioaddr = dev->base_addr;
+ int ticks;
+ unsigned long flags;
+
+ spin_lock_irqsave(&lp->lock, flags);
+
+ csr0 = a->read_csr(ioaddr, 0);
+ if (!(csr0 & 0x0004)) { /* If not stopped */
+ /* set SUSPEND (SPND) - CSR5 bit 0 */
+ a->write_csr(ioaddr, 5, 0x0001);
+
+ /* poll waiting for bit to be set */
+ ticks = 0;
+ while (!(a->read_csr(ioaddr, 5) & 0x0001)) {
+ spin_unlock_irqrestore(&lp->lock, flags);
+ mdelay(1);
+ spin_lock_irqsave(&lp->lock, flags);
+ ticks++;
+ if (ticks > 200) {
+ if (netif_msg_hw(lp))
+ printk(KERN_DEBUG "%s: Error getting into suspend!\n",
+ dev->name);
+ break;
+ }
+ }
+ }
+
+ /* read address PROM */
+ for (i=0; i<16; i += 2)
+ *buff++ = inw(ioaddr + i);
+
+ /* read control and status registers */
+ for (i=0; i<90; i++) {
+ *buff++ = a->read_csr(ioaddr, i);
+ }
+
+ *buff++ = a->read_csr(ioaddr, 112);
+ *buff++ = a->read_csr(ioaddr, 114);
+
+ /* read bus configuration registers */
+ for (i=0; i<36; i++) {
+ *buff++ = a->read_bcr(ioaddr, i);
+ }
+
+ /* read mii phy registers */
+ if (lp->mii) {
+ for (i=0; i<32; i++) {
+ lp->a.write_bcr(ioaddr, 33, ((lp->mii_if.phy_id) << 5) | i);
+ *buff++ = lp->a.read_bcr(ioaddr, 34);
+ }
+ }
+
+ if (!(csr0 & 0x0004)) { /* If not stopped */
+ /* clear SUSPEND (SPND) - CSR5 bit 0 */
+ a->write_csr(ioaddr, 5, 0x0000);
+ }
+
+ i = buff - (u16 *)ptr;
+ for (; i < PCNET32_NUM_REGS; i++)
+ *buff++ = 0;
+
+ spin_unlock_irqrestore(&lp->lock, flags);
+}
+
static struct ethtool_ops pcnet32_ethtool_ops = {
.get_settings = pcnet32_get_settings,
.set_settings = pcnet32_set_settings,
.self_test_count = pcnet32_self_test_count,
.self_test = pcnet32_ethtool_test,
.phys_id = pcnet32_phys_id,
+ .get_regs_len = pcnet32_get_regs_len,
+ .get_regs = pcnet32_get_regs,
};
/* only probes for non-PCI devices, the rest are handled by
if (request_region(ioaddr, PCNET32_TOTAL_SIZE, "pcnet32_probe_vlbus")) {
/* check if there is really a pcnet chip on that ioaddr */
if ((inb(ioaddr + 14) == 0x57) && (inb(ioaddr + 15) == 0x57)) {
- pcnet32_probe1(ioaddr, 0, 0, NULL);
+ pcnet32_probe1(ioaddr, 0, NULL);
} else {
release_region(ioaddr, PCNET32_TOTAL_SIZE);
}
return -EBUSY;
}
- return pcnet32_probe1(ioaddr, pdev->irq, 1, pdev);
+ err = pcnet32_probe1(ioaddr, 1, pdev);
+ if (err < 0) {
+ pci_disable_device(pdev);
+ }
+ return err;
}
* pdev will be NULL when called from pcnet32_probe_vlbus.
*/
static int __devinit
-pcnet32_probe1(unsigned long ioaddr, unsigned int irq_line, int shared,
- struct pci_dev *pdev)
+pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev)
{
struct pcnet32_private *lp;
dma_addr_t lp_dma_addr;
int i, media;
- int fdx, mii, fset, dxsuflo, ltint;
+ int fdx, mii, fset, dxsuflo;
int chip_version;
char *chipname;
struct net_device *dev;
}
/* initialize variables */
- fdx = mii = fset = dxsuflo = ltint = 0;
+ fdx = mii = fset = dxsuflo = 0;
chip_version = (chip_version >> 12) & 0xffff;
switch (chip_version) {
case 0x2623:
chipname = "PCnet/FAST 79C971"; /* PCI */
fdx = 1; mii = 1; fset = 1;
- ltint = 1;
break;
case 0x2624:
chipname = "PCnet/FAST+ 79C972"; /* PCI */
fdx = 1;
/*
* This is based on specs published at www.amd.com. This section
- * assumes that a card with a 79C978 wants to go into 1Mb HomePNA
- * mode. The 79C978 can also go into standard ethernet, and there
- * probably should be some sort of module option to select the
- * mode by which the card should operate
+ * assumes that a card with a 79C978 wants to go into standard
+ * ethernet mode. The 79C978 can also go into 1Mb HomePNA mode,
+ * and the module option homepna=1 can select this instead.
*/
- /* switch to home wiring mode */
media = a->read_bcr(ioaddr, 49);
+ media &= ~3; /* default to 10Mb ethernet */
+ if (cards_found < MAX_UNITS && homepna[cards_found])
+ media |= 1; /* switch to home wiring mode */
if (pcnet32_debug & NETIF_MSG_PROBE)
- printk(KERN_DEBUG PFX "media reset to %#x.\n", media);
+ printk(KERN_DEBUG PFX "media set to %sMbit mode.\n",
+ (media & 1) ? "1" : "10");
a->write_bcr(ioaddr, 49, media);
break;
case 0x2627:
fdx = 1; mii = 1;
break;
case 0x2628:
- chipname = "PCnet/FAST III 79C976";
+ chipname = "PCnet/PRO 79C976";
fdx = 1; mii = 1;
break;
default:
a->write_bcr(ioaddr, 18, (a->read_bcr(ioaddr, 18) | 0x0860));
a->write_csr(ioaddr, 80, (a->read_csr(ioaddr, 80) & 0x0C00) | 0x0c00);
dxsuflo = 1;
- ltint = 1;
}
dev = alloc_etherdev(0);
lp->mii_if.phy_id_mask = 0x1f;
lp->mii_if.reg_num_mask = 0x1f;
lp->dxsuflo = dxsuflo;
- lp->ltint = ltint;
lp->mii = mii;
lp->msg_enable = pcnet32_debug;
if ((cards_found >= MAX_UNITS) || (options[cards_found] > sizeof(options_mapping)))
a->write_csr(ioaddr, 2, (lp->dma_addr + offsetof(struct pcnet32_private,
init_block)) >> 16);
- if (irq_line) {
- dev->irq = irq_line;
- }
-
- if (dev->irq >= 2) {
+ if (pdev) { /* use the IRQ provided by PCI */
+ dev->irq = pdev->irq;
if (pcnet32_debug & NETIF_MSG_PROBE)
printk(" assigned IRQ %d.\n", dev->irq);
} else {
int rc;
unsigned long flags;
- if (dev->irq == 0 ||
- request_irq(dev->irq, &pcnet32_interrupt,
+ if (request_irq(dev->irq, &pcnet32_interrupt,
lp->shared_irq ? SA_SHIRQ : 0, dev->name, (void *)dev)) {
return -EAGAIN;
}
val |= 0x10;
lp->a.write_csr (ioaddr, 124, val);
+ /* 24 Jun 2004 according AMD, in order to change the PHY,
+ * DANAS (or DISPM for 79C976) must be set; then select the speed,
+ * duplex, and/or enable auto negotiation, and clear DANAS */
if (lp->mii && !(lp->options & PCNET32_PORT_ASEL)) {
+ lp->a.write_bcr(ioaddr, 32, lp->a.read_bcr(ioaddr, 32) | 0x0080);
/* disable Auto Negotiation, set 10Mpbs, HD */
- val = lp->a.read_bcr (ioaddr, 32) & ~0x38;
+ val = lp->a.read_bcr(ioaddr, 32) & ~0xb8;
if (lp->options & PCNET32_PORT_FD)
val |= 0x10;
if (lp->options & PCNET32_PORT_100)
lp->a.write_bcr (ioaddr, 32, val);
} else {
if (lp->options & PCNET32_PORT_ASEL) {
+ lp->a.write_bcr(ioaddr, 32, lp->a.read_bcr(ioaddr, 32) | 0x0080);
/* enable auto negotiate, setup, disable fd */
val = lp->a.read_bcr(ioaddr, 32) & ~0x98;
val |= 0x20;
}
#endif
- if (lp->ltint) { /* Enable TxDone-intr inhibitor */
- val = lp->a.read_csr (ioaddr, 5);
- val |= (1<<14);
- lp->a.write_csr (ioaddr, 5, val);
- }
-
lp->init_block.mode = le16_to_cpu((lp->options & PCNET32_PORT_PORTSEL) << 7);
pcnet32_load_multicast(dev);
/* Re-initialize the PCNET32, and start it when done. */
lp->a.write_csr (ioaddr, 1, (lp->dma_addr +
- offsetof(struct pcnet32_private, init_block)) & 0xffff);
+ offsetof(struct pcnet32_private, init_block)) & 0xffff);
lp->a.write_csr (ioaddr, 2, (lp->dma_addr +
offsetof(struct pcnet32_private, init_block)) >> 16);
int i;
for (i = 0; i < TX_RING_SIZE; i++) {
+ lp->tx_ring[i].status = 0; /* CPU owns buffer */
+ wmb(); /* Make sure adapter sees owner change */
if (lp->tx_skbuff[i]) {
pci_unmap_single(lp->pci_dev, lp->tx_dma_addr[i],
lp->tx_skbuff[i]->len, PCI_DMA_TODEVICE);
dev_kfree_skb_any(lp->tx_skbuff[i]);
- lp->tx_skbuff[i] = NULL;
- lp->tx_dma_addr[i] = 0;
}
+ lp->tx_skbuff[i] = NULL;
+ lp->tx_dma_addr[i] = 0;
}
}
skb_reserve (rx_skbuff, 2);
}
+ rmb();
if (lp->rx_dma_addr[i] == 0)
lp->rx_dma_addr[i] = pci_map_single(lp->pci_dev, rx_skbuff->tail,
PKT_BUF_SZ-2, PCI_DMA_FROMDEVICE);
lp->rx_ring[i].base = (u32)le32_to_cpu(lp->rx_dma_addr[i]);
lp->rx_ring[i].buf_length = le16_to_cpu(2-PKT_BUF_SZ);
+ wmb(); /* Make sure owner changes after all others are visible */
lp->rx_ring[i].status = le16_to_cpu(0x8000);
}
/* The Tx buffer address is filled in as needed, but we do need to clear
* the upper ownership bit. */
for (i = 0; i < TX_RING_SIZE; i++) {
+ lp->tx_ring[i].status = 0; /* CPU owns buffer */
+ wmb(); /* Make sure adapter sees owner change */
lp->tx_ring[i].base = 0;
- lp->tx_ring[i].status = 0;
lp->tx_dma_addr[i] = 0;
}
- wmb(); /* Make sure all changes are visible */
lp->init_block.tlen_rlen = le16_to_cpu(TX_RING_LEN_BITS | RX_RING_LEN_BITS);
for (i = 0; i < 6; i++)
offsetof(struct pcnet32_private, rx_ring));
lp->init_block.tx_ring = (u32)le32_to_cpu(lp->dma_addr +
offsetof(struct pcnet32_private, tx_ring));
+ wmb(); /* Make sure all changes are visible */
return 0;
}
+/* the pcnet32 has been issued a stop or reset. Wait for the stop bit
+ * then flush the pending transmit operations, re-initialize the ring,
+ * and tell the chip to initialize.
+ */
static void
pcnet32_restart(struct net_device *dev, unsigned int csr0_bits)
{
unsigned long ioaddr = dev->base_addr;
int i;
+ /* wait for stop */
+ for (i=0; i<100; i++)
+ if (lp->a.read_csr(ioaddr, 0) & 0x0004)
+ break;
+
+ if (i >= 100 && netif_msg_drv(lp))
+ printk(KERN_ERR "%s: pcnet32_restart timed out waiting for stop.\n",
+ dev->name);
+
pcnet32_purge_tx_ring(dev);
if (pcnet32_init_ring(dev))
return;
lp->cur_rx);
for (i = 0 ; i < RX_RING_SIZE; i++)
printk("%s %08x %04x %08x %04x", i & 1 ? "" : "\n ",
- lp->rx_ring[i].base, -lp->rx_ring[i].buf_length,
- lp->rx_ring[i].msg_length, (unsigned)lp->rx_ring[i].status);
+ le32_to_cpu(lp->rx_ring[i].base),
+ (-le16_to_cpu(lp->rx_ring[i].buf_length)) & 0xffff,
+ le32_to_cpu(lp->rx_ring[i].msg_length),
+ le16_to_cpu(lp->rx_ring[i].status));
for (i = 0 ; i < TX_RING_SIZE; i++)
printk("%s %08x %04x %08x %04x", i & 1 ? "" : "\n ",
- lp->tx_ring[i].base, -lp->tx_ring[i].length,
- lp->tx_ring[i].misc, (unsigned)lp->tx_ring[i].status);
+ le32_to_cpu(lp->tx_ring[i].base),
+ (-le16_to_cpu(lp->tx_ring[i].length)) & 0xffff,
+ le32_to_cpu(lp->tx_ring[i].misc),
+ le16_to_cpu(lp->tx_ring[i].status));
printk("\n");
}
pcnet32_restart(dev, 0x0042);
int entry;
unsigned long flags;
+ spin_lock_irqsave(&lp->lock, flags);
+
if (netif_msg_tx_queued(lp)) {
printk(KERN_DEBUG "%s: pcnet32_start_xmit() called, csr0 %4.4x.\n",
dev->name, lp->a.read_csr(ioaddr, 0));
}
- spin_lock_irqsave(&lp->lock, flags);
-
/* Default status -- will not enable Successful-TxDone
* interrupt when that option is available to us.
*/
status = 0x8300;
- entry = (lp->cur_tx - lp->dirty_tx) & TX_RING_MOD_MASK;
- if ((lp->ltint) &&
- ((entry == TX_RING_SIZE/3) ||
- (entry == (TX_RING_SIZE*2)/3) ||
- (entry >= TX_RING_SIZE-2)))
- {
- /* Enable Successful-TxDone interrupt if we have
- * 1/3, 2/3 or nearly all of, our ring buffer Tx'd
- * but not yet cleaned up. Thus, most of the time,
- * we will not enable Successful-TxDone interrupts.
- */
- status = 0x9300;
- }
/* Fill in a Tx ring entry */
spin_lock(&lp->lock);
rap = lp->a.read_rap(ioaddr);
- while ((csr0 = lp->a.read_csr (ioaddr, 0)) & 0x8600 && --boguscnt >= 0) {
+ while ((csr0 = lp->a.read_csr (ioaddr, 0)) & 0x8f00 && --boguscnt >= 0) {
if (csr0 == 0xffff) {
break; /* PCMCIA remove happened */
}
/* There was an major error, log it. */
int err_status = le32_to_cpu(lp->tx_ring[entry].misc);
lp->stats.tx_errors++;
+ if (netif_msg_tx_err(lp))
+ printk(KERN_ERR "%s: Tx error status=%04x err_status=%08x\n",
+ dev->name, status, err_status);
if (err_status & 0x04000000) lp->stats.tx_aborted_errors++;
if (err_status & 0x08000000) lp->stats.tx_carrier_errors++;
if (err_status & 0x10000000) lp->stats.tx_window_errors++;
pci_unmap_single(lp->pci_dev, lp->tx_dma_addr[entry],
lp->tx_skbuff[entry]->len, PCI_DMA_TODEVICE);
dev_kfree_skb_irq(lp->tx_skbuff[entry]);
- lp->tx_skbuff[entry] = 0;
+ lp->tx_skbuff[entry] = NULL;
lp->tx_dma_addr[entry] = 0;
}
dirty_tx++;
}
delta = (lp->cur_tx - dirty_tx) & (TX_RING_MOD_MASK + TX_RING_SIZE);
- if (delta >= TX_RING_SIZE) {
+ if (delta > TX_RING_SIZE) {
if (netif_msg_drv(lp))
printk(KERN_ERR "%s: out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
dev->name, dirty_tx, lp->cur_tx, lp->tx_full);
}
if (must_restart) {
- /* stop the chip to clear the error condition, then restart */
- lp->a.write_csr (ioaddr, 0, 0x0004);
+ /* reset the chip to clear the error condition, then restart */
+ lp->a.reset(ioaddr);
+ lp->a.write_csr(ioaddr, 4, 0x0915);
pcnet32_restart(dev, 0x0002);
+ netif_wake_queue(dev);
}
}
- /* Clear any other interrupt, and set interrupt enable. */
- lp->a.write_csr (ioaddr, 0, 0x7940);
+ /* Set interrupt enable. */
+ lp->a.write_csr (ioaddr, 0, 0x0040);
lp->a.write_rap (ioaddr,rap);
if (netif_msg_intr(lp))
{
struct pcnet32_private *lp = dev->priv;
int entry = lp->cur_rx & RX_RING_MOD_MASK;
+ int boguscnt = RX_RING_SIZE / 2;
/* If we own the next entry, it's a new packet. Send it up. */
while ((short)le16_to_cpu(lp->rx_ring[entry].status) >= 0) {
short pkt_len = (le32_to_cpu(lp->rx_ring[entry].msg_length) & 0xfff)-4;
struct sk_buff *skb;
- if (pkt_len < 60) {
+ /* Discard oversize frames. */
+ if (unlikely(pkt_len > PKT_BUF_SZ - 2)) {
+ if (netif_msg_drv(lp))
+ printk(KERN_ERR "%s: Impossible packet size %d!\n",
+ dev->name, pkt_len);
+ lp->stats.rx_errors++;
+ } else if (pkt_len < 60) {
if (netif_msg_rx_err(lp))
printk(KERN_ERR "%s: Runt packet!\n", dev->name);
lp->stats.rx_errors++;
if (i > RX_RING_SIZE -2) {
lp->stats.rx_dropped++;
lp->rx_ring[entry].status |= le16_to_cpu(0x8000);
+ wmb(); /* Make sure adapter sees owner change */
lp->cur_rx++;
}
break;
wmb(); /* Make sure owner changes after all others are visible */
lp->rx_ring[entry].status |= le16_to_cpu(0x8000);
entry = (++lp->cur_rx) & RX_RING_MOD_MASK;
+ if (--boguscnt <= 0) break; /* don't stay in loop forever */
}
return 0;
free_irq(dev->irq, dev);
+ spin_lock_irqsave(&lp->lock, flags);
+
/* free all allocated skbuffs */
for (i = 0; i < RX_RING_SIZE; i++) {
lp->rx_ring[i].status = 0;
+ wmb(); /* Make sure adapter sees owner change */
if (lp->rx_skbuff[i]) {
pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[i], PKT_BUF_SZ-2,
PCI_DMA_FROMDEVICE);
}
for (i = 0; i < TX_RING_SIZE; i++) {
+ lp->tx_ring[i].status = 0; /* CPU owns buffer */
+ wmb(); /* Make sure adapter sees owner change */
if (lp->tx_skbuff[i]) {
pci_unmap_single(lp->pci_dev, lp->tx_dma_addr[i],
lp->tx_skbuff[i]->len, PCI_DMA_TODEVICE);
lp->tx_dma_addr[i] = 0;
}
+ spin_unlock_irqrestore(&lp->lock, flags);
+
return 0;
}
}
lp->a.write_csr (ioaddr, 0, 0x0004); /* Temporarily stop the lance. */
-
pcnet32_restart(dev, 0x0042); /* Resume normal operation */
+ netif_wake_queue(dev);
+
spin_unlock_irqrestore(&lp->lock, flags);
}
+/* This routine assumes that the lp->lock is held */
static int mdio_read(struct net_device *dev, int phy_id, int reg_num)
{
struct pcnet32_private *lp = dev->priv;
unsigned long ioaddr = dev->base_addr;
u16 val_out;
- int phyaddr;
if (!lp->mii)
return 0;
- phyaddr = lp->a.read_bcr(ioaddr, 33);
-
lp->a.write_bcr(ioaddr, 33, ((phy_id & 0x1f) << 5) | (reg_num & 0x1f));
val_out = lp->a.read_bcr(ioaddr, 34);
- lp->a.write_bcr(ioaddr, 33, phyaddr);
return val_out;
}
+/* This routine assumes that the lp->lock is held */
static void mdio_write(struct net_device *dev, int phy_id, int reg_num, int val)
{
struct pcnet32_private *lp = dev->priv;
unsigned long ioaddr = dev->base_addr;
- int phyaddr;
if (!lp->mii)
return;
- phyaddr = lp->a.read_bcr(ioaddr, 33);
-
lp->a.write_bcr(ioaddr, 33, ((phy_id & 0x1f) << 5) | (reg_num & 0x1f));
lp->a.write_bcr(ioaddr, 34, val);
- lp->a.write_bcr(ioaddr, 33, phyaddr);
}
static int pcnet32_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
struct pcnet32_private *lp = dev->priv;
- struct mii_ioctl_data *data = (struct mii_ioctl_data *)&rq->ifr_data;
int rc;
unsigned long flags;
/* SIOC[GS]MIIxxx ioctls */
if (lp->mii) {
spin_lock_irqsave(&lp->lock, flags);
- rc = generic_mii_ioctl(&lp->mii_if, data, cmd, NULL);
+ rc = generic_mii_ioctl(&lp->mii_if, if_mii(rq), cmd, NULL);
spin_unlock_irqrestore(&lp->lock, flags);
} else {
rc = -EOPNOTSUPP;
release_region(dev->base_addr, PCNET32_TOTAL_SIZE);
pci_free_consistent(lp->pci_dev, sizeof(*lp), lp, lp->dma_addr);
free_netdev(dev);
+ pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
}
}
.id_table = pcnet32_pci_tbl,
};
-MODULE_PARM(debug, "i");
+/* An additional parameter that may be passed in... */
+static int debug = -1;
+static int tx_start_pt = -1;
+static int pcnet32_have_pci;
+
+module_param(debug, int, 0);
MODULE_PARM_DESC(debug, DRV_NAME " debug level");
-MODULE_PARM(max_interrupt_work, "i");
+module_param(max_interrupt_work, int, 0);
MODULE_PARM_DESC(max_interrupt_work, DRV_NAME " maximum events handled per interrupt");
-MODULE_PARM(rx_copybreak, "i");
+module_param(rx_copybreak, int, 0);
MODULE_PARM_DESC(rx_copybreak, DRV_NAME " copy breakpoint for copy-only-tiny-frames");
-MODULE_PARM(tx_start_pt, "i");
+module_param(tx_start_pt, int, 0);
MODULE_PARM_DESC(tx_start_pt, DRV_NAME " transmit start point (0-3)");
-MODULE_PARM(pcnet32vlb, "i");
+module_param(pcnet32vlb, int, 0);
MODULE_PARM_DESC(pcnet32vlb, DRV_NAME " Vesa local bus (VLB) support (0/1)");
-MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
+module_param_array(options, int, NULL, 0);
MODULE_PARM_DESC(options, DRV_NAME " initial option setting(s) (0-15)");
-MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
+module_param_array(full_duplex, int, NULL, 0);
MODULE_PARM_DESC(full_duplex, DRV_NAME " full duplex setting(s) (1)");
+/* Module Parameter for HomePNA cards added by Patrick Simmons, 2004 */
+module_param_array(homepna, int, NULL, 0);
+MODULE_PARM_DESC(homepna, DRV_NAME " mode for 79C978 cards (1 for HomePNA, 0 for Ethernet, default Ethernet");
MODULE_AUTHOR("Thomas Bogendoerfer");
MODULE_DESCRIPTION("Driver for PCnet32 and PCnetPCI based ethercards");
#define PCNET32_MSG_DEFAULT (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK)
-/* An additional parameter that may be passed in... */
-static int debug = -1;
-static int tx_start_pt = -1;
-static int pcnet32_have_pci;
-
static int __init pcnet32_init_module(void)
{
printk(KERN_INFO "%s", version);