summaryrefslogtreecommitdiffstats
path: root/drivers/net/8139too.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/8139too.c')
-rw-r--r--drivers/net/8139too.c475
1 files changed, 271 insertions, 204 deletions
diff --git a/drivers/net/8139too.c b/drivers/net/8139too.c
index c2eceb8db..6169eb742 100644
--- a/drivers/net/8139too.c
+++ b/drivers/net/8139too.c
@@ -87,7 +87,7 @@ an MMIO register read.
#include <asm/io.h>
-#define RTL8139_VERSION "0.9.3"
+#define RTL8139_VERSION "0.9.4"
#define RTL8139_MODULE_NAME "8139too"
#define RTL8139_DRIVER_NAME RTL8139_MODULE_NAME " Fast Ethernet driver " RTL8139_VERSION
#define PFX RTL8139_MODULE_NAME ": "
@@ -101,8 +101,8 @@ an MMIO register read.
#define DPRINTK(fmt, args...)
#endif
-#define RTL8139_NDEBUG 0 /* define to 1 to disable lightweight runtime checks */
-#if RTL8139_NDEBUG
+#undef RTL8139_NDEBUG /* define to 1 to disable lightweight runtime checks */
+#ifdef RTL8139_NDEBUG
#define assert(expr)
#else
#define assert(expr) \
@@ -144,6 +144,7 @@ static int multicast_filter_limit = 32;
#define RX_DMA_BURST 4 /* Maximum PCI burst, '4' is 256 bytes */
#define TX_DMA_BURST 4 /* Calculate as 16<<val. */
+
/* Operational parameters that usually are not changed. */
/* Time in jiffies before concluding the transmitter is hung. */
#define TX_TIMEOUT (6*HZ)
@@ -154,12 +155,13 @@ enum {
HAS_LNK_CHNG = 0x040000,
};
-#define RTL_IO_SIZE 0x80
+#define RTL_MIN_IO_SIZE 0x80
+#define RTL8139B_IO_SIZE 0xFF
-#define RTL8139_CAPS HAS_CHIP_XCVR|HAS_LNK_CHNG
+#define RTL8139_CAPS HAS_CHIP_XCVR|HAS_LNK_CHNG
typedef enum {
- RTL8139,
+ RTL8139 = 0,
RTL8139_CB,
SMC1211TX,
/*MPX5030,*/
@@ -168,17 +170,16 @@ typedef enum {
} chip_t;
+/* indexed by chip_t, above */
static struct {
- chip_t chip;
const char *name;
} chip_info[] __devinitdata = {
- { RTL8139, "RealTek RTL8139 Fast Ethernet"},
- { RTL8139_CB, "RealTek RTL8139B PCI/CardBus"},
- { SMC1211TX, "SMC1211TX EZCard 10/100 (RealTek RTL8139)"},
-/* { MPX5030, "Accton MPX5030 (RealTek RTL8139)"},*/
- { DELTA8139, "Delta Electronics 8139 10/100BaseTX"},
- { ADDTRON8139, "Addtron Technolgy 8139 10/100BaseTX"},
- {0,},
+ { "RealTek RTL8139 Fast Ethernet" },
+ { "RealTek RTL8139B PCI/CardBus" },
+ { "SMC1211TX EZCard 10/100 (RealTek RTL8139)" },
+/* { MPX5030, "Accton MPX5030 (RealTek RTL8139)" },*/
+ { "Delta Electronics 8139 10/100BaseTX" },
+ { "Addtron Technolgy 8139 10/100BaseTX" },
};
@@ -298,6 +299,14 @@ enum Config1Bits {
Cfg1_LED1 = 0x80,
};
+enum RxConfigBits {
+ RxCfgRcv8K = 0,
+ RxCfgRcv16K = (1 << 11),
+ RxCfgRcv32K = (1 << 12),
+ RxCfgRcv64K = (1 << 11) | (1 << 12),
+};
+
+
/* Twister tuning parameters from RealTek.
Completely undocumented, but required to tune bad links. */
enum CSCRBits {
@@ -307,6 +316,14 @@ enum CSCRBits {
CSCR_LinkDownOffCmd = 0x003c0,
CSCR_LinkDownCmd = 0x0f3c0,
};
+
+
+enum Cfg9346Bits {
+ Cfg9346_Lock = 0x00,
+ Cfg9346_Unlock = 0xC0,
+};
+
+
#define PARA78_default 0x78fa8388
#define PARA7c_default 0xcb38de43 /* param[0][3] */
#define PARA7c_xxx 0xcb38de43
@@ -327,7 +344,6 @@ struct ring_info {
struct rtl8139_private {
chip_t chip;
void *mmio_addr;
- spinlock_t lock;
int drv_flags;
struct pci_dev *pci_dev;
struct net_device_stats stats;
@@ -349,6 +365,8 @@ struct rtl8139_private {
unsigned int media2:4; /* Secondary monitored media port. */
unsigned int medialock:1; /* Don't sense media type. */
unsigned int mediasense:1; /* Media sensing in progress. */
+ int extended_regs; /* bool: supports regs > 0x80 ? */
+ spinlock_t lock;
};
MODULE_AUTHOR ("Jeff Garzik <jgarzik@mandrakesoft.com>");
@@ -374,6 +392,8 @@ static int mii_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
static struct net_device_stats *rtl8139_get_stats (struct net_device *dev);
static inline u32 ether_crc (int length, unsigned char *data);
static void rtl8139_set_rx_mode (struct net_device *dev);
+static void rtl8139_hw_start (struct net_device *dev);
+
/* write MMIO register, with flush */
/* Flush avoids rtl8139 bug w/ posted MMIO writes */
@@ -409,30 +429,17 @@ static const u16 rtl8139_intr_mask =
TxErr | TxOK | RxErr | RxOK;
static const unsigned int rtl8139_rx_config =
- (RX_FIFO_THRESH << 13) |
- (RX_BUF_LEN_IDX << 11) |
+ (RX_FIFO_THRESH << 13) | (RxCfgRcv32K) |
(RX_DMA_BURST << 8);
-static const char * __devinit rtl8139_name_from_chip (chip_t chip)
-{
- int i;
-
- for (i = 0; i < arraysize (chip_info); i++)
- if (chip == chip_info[i].chip)
- return chip_info[i].name;
-
- return "unknown";
-}
-
-
static int __devinit rtl8139_init_pci (struct pci_dev *pdev, void **ioaddr_out)
{
void *ioaddr = NULL;
u8 tmp8;
int rc;
- u32 pio_start, pio_end, pio_flags;
- u32 mmio_start, mmio_end, mmio_flags;
+ u32 pio_start, pio_end, pio_flags, pio_len;
+ u32 mmio_start, mmio_end, mmio_flags, mmio_len;
DPRINTK ("ENTER\n");
@@ -444,48 +451,67 @@ static int __devinit rtl8139_init_pci (struct pci_dev *pdev, void **ioaddr_out)
pio_start = pci_resource_start (pdev, 0);
pio_end = pci_resource_end (pdev, 0);
pio_flags = pci_resource_flags (pdev, 0);
+ pio_len = pci_resource_len (pdev, 0);
mmio_start = pci_resource_start (pdev, 1);
mmio_end = pci_resource_end (pdev, 1);
mmio_flags = pci_resource_flags (pdev, 1);
+ mmio_len = pci_resource_len (pdev, 1);
/* make sure PCI base addr 0 is PIO */
- if (pio_start == 0 || pio_end <= pio_start ||
- (!(pio_flags & IORESOURCE_IO))) {
- printk (KERN_ERR PFX "no PIO resource, aborting\n");
- return -ENODEV;
+ if (!(pio_flags & IORESOURCE_IO)) {
+ printk (KERN_ERR PFX "region #0 not a PIO resource, aborting\n");
+ rc = -ENODEV;
+ goto err_out;
}
/* make sure PCI base addr 1 is MMIO */
- if (mmio_start == 0 || mmio_end <= mmio_start ||
- (!(mmio_flags & IORESOURCE_MEM))) {
- printk (KERN_ERR PFX "no MMIO resource, aborting\n");
- return -ENODEV;
+ if (!(mmio_flags & IORESOURCE_MEM)) {
+ printk (KERN_ERR PFX "region #1 not an MMIO resource, aborting\n");
+ rc = -ENODEV;
+ goto err_out;
+ }
+
+ /* check for weird/broken PCI region reporting */
+ if ((pio_len != mmio_len) ||
+ (pio_len < RTL_MIN_IO_SIZE) ||
+ (mmio_len < RTL_MIN_IO_SIZE)) {
+ printk (KERN_ERR PFX "Invalid PCI region size(s), aborting\n");
+ rc = -ENODEV;
+ goto err_out;
}
/* make sure our PIO region in PCI space is available */
- if (!request_region (pio_start, RTL_IO_SIZE, RTL8139_MODULE_NAME)) {
+ if (!request_region (pio_start, pio_len, RTL8139_MODULE_NAME)) {
printk (KERN_ERR PFX "no I/O resource available, aborting\n");
- return -EBUSY;
+ rc = -EBUSY;
+ goto err_out;
}
/* make sure our MMIO region in PCI space is available */
- if (!request_mem_region (mmio_start, RTL_IO_SIZE, RTL8139_MODULE_NAME)) {
- release_region (pio_start, RTL_IO_SIZE);
+ if (!request_mem_region (mmio_start, mmio_len, RTL8139_MODULE_NAME)) {
printk (KERN_ERR PFX "no mem resource available, aborting\n");
- return -EBUSY;
+ rc = -EBUSY;
+ goto err_out_free_pio;
}
/* enable device (incl. PCI PM wakeup), and bus-mastering */
- pci_enable_device (pdev);
+ rc = pci_enable_device (pdev);
+ if (rc) {
+ printk (KERN_ERR PFX "cannot enable PCI device (bus %d, "
+ "devfn %d), aborting\n",
+ pdev->bus->number, pdev->devfn);
+ goto err_out_free_mmio;
+ }
+
pci_set_master (pdev);
/* ioremap MMIO region */
- ioaddr = ioremap (mmio_start, RTL_IO_SIZE);
+ ioaddr = ioremap (mmio_start, mmio_len);
if (ioaddr == NULL) {
printk (KERN_ERR PFX "cannot remap MMIO, aborting\n");
rc = -EIO;
- goto err_out;
+ goto err_out_free_mmio;
}
/* Bring the chip out of low-power mode. */
@@ -496,12 +522,12 @@ static int __devinit rtl8139_init_pci (struct pci_dev *pdev, void **ioaddr_out)
if ((tmp8 & Cfg1_PIO) == 0) {
printk (KERN_ERR PFX "PIO not enabled, Cfg1=%02X, aborting\n", tmp8);
rc = -EIO;
- goto err_out;
+ goto err_out_iounmap;
}
if ((tmp8 & Cfg1_MMIO) == 0) {
printk (KERN_ERR PFX "MMIO not enabled, Cfg1=%02X, aborting\n", tmp8);
rc = -EIO;
- goto err_out;
+ goto err_out_iounmap;
}
/* sanity checks -- ensure PIO and MMIO registers agree */
@@ -514,28 +540,43 @@ static int __devinit rtl8139_init_pci (struct pci_dev *pdev, void **ioaddr_out)
*ioaddr_out = ioaddr;
return 0;
+err_out_iounmap:
+ assert (ioaddr > 0);
+ iounmap (ioaddr);
+err_out_free_mmio:
+ release_mem_region (mmio_start, mmio_len);
+err_out_free_pio:
+ release_region (pio_start, pio_len);
err_out:
- if (ioaddr)
- iounmap (ioaddr);
- release_region (pio_start, RTL_IO_SIZE);
- release_mem_region (mmio_start, RTL_IO_SIZE);
DPRINTK ("EXIT, returning %d\n", rc);
return rc;
}
-static int __devinit rtl8139_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
+static int __devinit rtl8139_init_one (struct pci_dev *pdev,
+ const struct pci_device_id *ent)
{
struct net_device *dev;
struct rtl8139_private *tp;
int i, addr_len, option = -1;
void *ioaddr = NULL;
+#ifndef RTL8139_NDEBUG
+ static int printed_version = 0;
+#endif /* RTL8139_NDEBUG */
+
DPRINTK ("ENTER\n");
assert (pdev != NULL);
assert (ent != NULL);
-
+
+#ifndef RTL8139_NDEBUG
+ if (!printed_version) {
+ printk (KERN_INFO RTL8139_DRIVER_NAME " loaded\n");
+ printed_version = 1;
+ }
+#endif /* RTL8139_NDEBUG */
+
i = rtl8139_init_pci (pdev, &ioaddr);
if (i < 0) {
DPRINTK ("EXIT, returning %d\n", i);
@@ -571,31 +612,34 @@ static int __devinit rtl8139_init_one (struct pci_dev *pdev, const struct pci_de
dev->irq = pdev->irq;
dev->base_addr = pci_resource_start (pdev, 1);
+ /* dev->priv/tp zeroed in init_etherdev */
dev->priv = tp = (void *)
(((long)dev->priv + PRIV_ALIGN) & ~PRIV_ALIGN);
- printk (KERN_INFO "%s: %s at 0x%lx, IRQ %d, "
- "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x.\n",
- dev->name, rtl8139_name_from_chip(ent->driver_data),
- dev->base_addr, dev->irq,
- dev->dev_addr[0], dev->dev_addr[1],
- dev->dev_addr[2], dev->dev_addr[3],
- dev->dev_addr[4], dev->dev_addr[5]);
-
- /* tp zeroed in init_etherdev */
tp->drv_flags = PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
PCI_COMMAND_MASTER | RTL8139_CAPS;
tp->pci_dev = pdev;
tp->chip = ent->driver_data;
tp->mmio_addr = ioaddr;
+ tp->extended_regs =
+ (pci_resource_len (pdev, 0) == RTL8139B_IO_SIZE) ? 1 : 0;
tp->lock = SPIN_LOCK_UNLOCKED;
PCI_SET_DRIVER_DATA (pdev, dev);
tp->phys[0] = 32;
+ printk (KERN_INFO "%s: %s at 0x%lx, IRQ %d,%s "
+ "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x.\n",
+ dev->name, chip_info[ent->driver_data].name,
+ dev->base_addr, dev->irq,
+ tp->extended_regs ? " 8139B regs," : "",
+ dev->dev_addr[0], dev->dev_addr[1],
+ dev->dev_addr[2], dev->dev_addr[3],
+ dev->dev_addr[4], dev->dev_addr[5]);
+
/* Put the chip into low-power mode. */
- RTL_W8 (Cfg9346, 0xC0);
+ RTL_W8 (Cfg9346, Cfg9346_Unlock);
RTL_W8 (Config1, 0x03); /* Enable PM & PCI VPD */
RTL_W8 (HltClk, 'H'); /* 'R' would leave the clock running. */
@@ -635,8 +679,10 @@ static void __devexit rtl8139_remove_one (struct pci_dev *pdev)
unregister_netdev (dev);
iounmap (np->mmio_addr);
- release_region (pci_resource_start (pdev, 0), RTL_IO_SIZE);
- release_mem_region (pci_resource_start (pdev, 1), RTL_IO_SIZE);
+ release_region (pci_resource_start (pdev, 0),
+ pci_resource_len (pdev, 0));
+ release_mem_region (pci_resource_start (pdev, 1),
+ pci_resource_len (pdev, 1));
#ifndef RTL8139_NDEBUG
/* poison memory before freeing */
@@ -644,7 +690,7 @@ static void __devexit rtl8139_remove_one (struct pci_dev *pdev)
sizeof (struct net_device) +
sizeof (struct rtl8139_private) +
PRIV_ALIGN);
-#endif
+#endif /* RTL8139_NDEBUG */
kfree (dev);
@@ -851,16 +897,14 @@ static void mdio_write (struct net_device *dev, int phy_id, int location,
static int rtl8139_open (struct net_device *dev)
{
struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv;
+#ifdef RTL8139_DEBUG
void *ioaddr = tp->mmio_addr;
- int i;
+#endif
DPRINTK ("ENTER\n");
MOD_INC_USE_COUNT;
- /* Soft reset the chip. */
- RTL_W8 (ChipCmd, CmdReset);
-
if (request_irq (dev->irq, &rtl8139_interrupt, SA_SHIRQ, dev->name, dev)) {
DPRINTK ("EXIT, returning -EBUSY\n");
MOD_DEC_USE_COUNT;
@@ -891,44 +935,7 @@ static int rtl8139_open (struct net_device *dev)
tp->full_duplex = tp->duplex_lock;
tp->tx_flag = (TX_FIFO_THRESH << 11) & 0x003f0000;
- /* Check that the chip has finished the reset. */
- for (i = 1000; i > 0; i--)
- if ((RTL_R8 (ChipCmd) & CmdReset) == 0)
- break;
-
- RTL_W32_F (MAC0 + 0, cpu_to_le32 (*(u32 *) (dev->dev_addr + 0)));
- RTL_W32_F (MAC0 + 4, cpu_to_le32 (*(u32 *) (dev->dev_addr + 4)));
-
- /* Must enable Tx/Rx before setting transfer thresholds! */
- RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb);
- RTL_W32 (RxConfig, rtl8139_rx_config);
- RTL_W32 (TxConfig, (TX_DMA_BURST << 8) | 0x00000000);
-
- /* Reset N-Way to chipset defaults */
- RTL_W16 (BasicModeCtrl, (1<<15)|(1<<12)|(1<<9));
- for (i = 1000; i > 0; i--)
- if ((RTL_R8 (BasicModeCtrl) & (1<<15)) == 0)
- break;
-
- /* Set N-Way to sane defaults */
- RTL_W16 (FIFOTMS, 0x0000);
- RTL_W16 (NWayAdvert, (1<<13)|(1<<8)|(1<<7)|(1<<6)|(1<<5)|0x1);
- RTL_W16 (BasicModeCtrl, (1<<13)|(1<<12)|(1<<9)|(1<<8));
-
- RTL_W8 (Cfg9346, 0xC0);
- RTL_W8 (Config1, tp->full_duplex ? 0x60 : 0x20);
- RTL_W8 (Cfg9346, 0x00);
-
- RTL_W32 (RxBuf, tp->rx_ring_dma);
-
- /* Start the chip's Tx and Rx process. */
- RTL_W32 (RxMissed, 0);
- rtl8139_set_rx_mode (dev);
-
- RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb);
-
- /* Enable all known interrupts by setting the interrupt mask. */
- RTL_W16 (IntrMask, rtl8139_intr_mask);
+ rtl8139_hw_start (dev);
DPRINTK ("%s: rtl8139_open() ioaddr %#lx IRQ %d"
" GP Pins %2.2x %s-duplex.\n",
@@ -944,23 +951,26 @@ static int rtl8139_open (struct net_device *dev)
tp->timer.function = &rtl8139_timer;
add_timer (&tp->timer);
- netif_start_queue (dev);
-
DPRINTK ("EXIT, returning 0\n");
return 0;
}
+
/* Start the hardware at open or resume. */
static void rtl8139_hw_start (struct net_device *dev)
{
struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv;
void *ioaddr = tp->mmio_addr;
int i;
+ unsigned long flags;
DPRINTK ("ENTER\n");
+
+ spin_lock_irqsave (&tp->lock, flags);
/* Soft reset the chip. */
RTL_W8 (ChipCmd, CmdReset);
+
/* Check that the chip has finished the reset. */
for (i = 1000; i > 0; i--)
if ((RTL_R8 (ChipCmd) & CmdReset) == 0)
@@ -970,8 +980,9 @@ static void rtl8139_hw_start (struct net_device *dev)
RTL_W32_F (MAC0 + 0, cpu_to_le32 (*(u32 *) (dev->dev_addr + 0)));
RTL_W32_F (MAC0 + 4, cpu_to_le32 (*(u32 *) (dev->dev_addr + 4)));
- /* Hmmm, do these belong here? */
- RTL_W8 (Cfg9346, 0x00);
+ /* unlock Config[01234] and BMCR register writes */
+ RTL_W8 (Cfg9346, Cfg9346_Unlock);
+
tp->cur_rx = 0;
/* Must enable Tx/Rx before setting transfer thresholds! */
@@ -993,19 +1004,30 @@ static void rtl8139_hw_start (struct net_device *dev)
RTL_W16 (BasicModeCtrl, (1<<13)|(1<<12)|(1<<9)|(1<<8));
/* check_duplex() here. */
- RTL_W8 (Cfg9346, 0xC0);
RTL_W8 (Config1, tp->full_duplex ? 0x60 : 0x20);
- RTL_W8 (Cfg9346, 0x00);
+
+ /* lock Config[01234] and BMCR register writes */
+ RTL_W8 (Cfg9346, Cfg9346_Lock);
RTL_W32 (RxBuf, tp->rx_ring_dma);
+
/* Start the chip's Tx and Rx process. */
RTL_W32 (RxMissed, 0);
+
+ /* release lock cuz set_rx_mode wants it */
+ spin_unlock_irqrestore (&tp->lock, flags);
rtl8139_set_rx_mode (dev);
+ spin_lock_irqsave (&tp->lock, flags);
+
RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb);
+
/* Enable all known interrupts by setting the interrupt mask. */
RTL_W16 (IntrMask, rtl8139_intr_mask);
- netif_start_queue (dev);
+ if (netif_queue_stopped (dev))
+ netif_start_queue (dev);
+
+ spin_unlock_irqrestore (&tp->lock, flags);
DPRINTK ("EXIT\n");
}
@@ -1111,9 +1133,14 @@ static void rtl8139_timer (unsigned long data)
struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv;
void *ioaddr = tp->mmio_addr;
int next_tick = 60 * HZ;
- int mii_reg5 = mdio_read (dev, tp->phys[0], 5);
+ int mii_reg5;
+ unsigned long flags;
DPRINTK ("ENTER\n");
+
+ spin_lock_irqsave (&tp->lock, flags);
+
+ mii_reg5 = mdio_read (dev, tp->phys[0], 5);
if (!tp->duplex_lock && mii_reg5 != 0xffff) {
int duplex = (mii_reg5 & 0x0100)
@@ -1125,9 +1152,9 @@ static void rtl8139_timer (unsigned long data)
" partner ability of %4.4x.\n", dev->name,
tp->full_duplex ? "full" : "half",
tp->phys[0], mii_reg5);
- RTL_W8 (Cfg9346, 0xC0);
+ RTL_W8 (Cfg9346, Cfg9346_Unlock);
RTL_W8 (Config1, tp->full_duplex ? 0x60 : 0x20);
- RTL_W8 (Cfg9346, 0x00);
+ RTL_W8 (Cfg9346, Cfg9346_Lock);
}
}
@@ -1144,6 +1171,8 @@ static void rtl8139_timer (unsigned long data)
dev->name, RTL_R8 (Config0),
RTL_R8 (Config1));
+ spin_unlock_irqrestore (&tp->lock, flags);
+
tp->timer.expires = jiffies + next_tick;
add_timer (&tp->timer);
@@ -1156,10 +1185,11 @@ static void rtl8139_tx_timeout (struct net_device *dev)
struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv;
void *ioaddr = tp->mmio_addr;
int mii_reg, i;
+ unsigned long flags;
DPRINTK ("ENTER\n");
- netif_stop_queue (dev);
+ spin_lock_irqsave (&tp->lock, flags);
DPRINTK ("%s: Transmit timeout, status %2.2x %4.4x "
"media %2.2x.\n", dev->name,
@@ -1201,6 +1231,8 @@ static void rtl8139_tx_timeout (struct net_device *dev)
}
}
+ spin_unlock_irqrestore (&tp->lock, flags);
+
rtl8139_hw_start (dev);
DPRINTK ("EXIT\n");
@@ -1227,6 +1259,7 @@ static void rtl8139_init_ring (struct net_device *dev)
DPRINTK ("EXIT\n");
}
+
static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev)
{
struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv;
@@ -1236,13 +1269,11 @@ static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev)
DPRINTK ("ENTER\n");
- netif_stop_queue (dev);
+ spin_lock_irqsave (&tp->lock, flags);
/* Calculate the next Tx descriptor entry. */
entry = tp->cur_tx % NUM_TX_DESC;
- spin_lock_irqsave (&tp->lock, flags);
-
tp->tx_info[entry].skb = skb;
if ((long) skb->data & 3) { /* Must use alignment buffer. */
tp->tx_info[entry].mapping = 0;
@@ -1263,12 +1294,12 @@ static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev)
tp->tx_flag | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN));
dev->trans_start = jiffies;
- if (++tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) /* Typical path */
- netif_start_queue (dev);
+ if (++tp->cur_tx - tp->dirty_tx >= NUM_TX_DESC)
+ netif_stop_queue (dev);
spin_unlock_irqrestore (&tp->lock, flags);
-
- DPRINTK ("%s: Queued Tx packet at %p size %lu to slot %d.\n",
+
+ DPRINTK ("%s: Queued Tx packet at %p size %u to slot %d.\n",
dev->name, skb->data, skb->len, entry);
DPRINTK ("EXIT\n");
@@ -1277,16 +1308,16 @@ static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev)
static inline void rtl8139_tx_interrupt (struct net_device *dev,
- struct rtl8139_private *tp)
+ struct rtl8139_private *tp,
+ void *ioaddr)
{
- void *ioaddr;
unsigned int dirty_tx;
assert (dev != NULL);
assert (tp != NULL);
-
+ assert (ioaddr != NULL);
+
dirty_tx = tp->dirty_tx;
- ioaddr = tp->mmio_addr;
while (tp->cur_tx - dirty_tx > 0) {
int entry = dirty_tx % NUM_TX_DESC;
@@ -1338,8 +1369,6 @@ static inline void rtl8139_tx_interrupt (struct net_device *dev,
dirty_tx++;
if (tp->cur_tx - dirty_tx < NUM_TX_DESC)
netif_wake_queue (dev);
- else
- netif_stop_queue (dev);
}
#ifndef RTL8139_NDEBUG
@@ -1349,7 +1378,7 @@ static inline void rtl8139_tx_interrupt (struct net_device *dev,
dev->name, dirty_tx, tp->cur_tx);
dirty_tx += NUM_TX_DESC;
}
-#endif
+#endif /* RTL8139_NDEBUG */
tp->dirty_tx = dirty_tx;
}
@@ -1358,11 +1387,18 @@ static inline void rtl8139_tx_interrupt (struct net_device *dev,
/* The data sheet doesn't describe the Rx ring at all, so I'm guessing at the
field alignments and semantics. */
static inline void rtl8139_rx_interrupt (struct net_device *dev,
- struct rtl8139_private *tp)
+ struct rtl8139_private *tp,
+ void *ioaddr)
{
- void *ioaddr = tp->mmio_addr;
- unsigned char *rx_ring = tp->rx_ring;
- u16 cur_rx = tp->cur_rx;
+ unsigned char *rx_ring;
+ u16 cur_rx;
+
+ assert (dev != NULL);
+ assert (tp != NULL);
+ assert (ioaddr != NULL);
+
+ rx_ring = tp->rx_ring;
+ cur_rx = tp->cur_rx;
DPRINTK ("%s: In rtl8139_rx(), current %4.4x BufAddr %4.4x,"
" free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx,
@@ -1376,15 +1412,17 @@ static inline void rtl8139_rx_interrupt (struct net_device *dev,
le32_to_cpu (*(u32 *) (rx_ring + ring_offset));
int rx_size = rx_status >> 16;
-#ifdef RTL8139_DEBUG
- int i;
DPRINTK ("%s: rtl8139_rx() status %4.4x, size %4.4x,"
" cur %4.4x.\n", dev->name, rx_status,
rx_size, cur_rx);
+#if RTL8139_DEBUG > 2
+ {
+ int i;
DPRINTK ("%s: Frame contents ", dev->name);
for (i = 0; i < 70; i++)
printk (" %2.2x", rx_ring[ring_offset + i]);
printk (".\n");
+ }
#endif
/* E. Gill */
@@ -1490,23 +1528,16 @@ static inline void rtl8139_rx_interrupt (struct net_device *dev,
static inline int rtl8139_weird_interrupt (struct net_device *dev,
struct rtl8139_private *tp,
+ void *ioaddr,
int status, int link_changed)
{
- void *ioaddr;
-
DPRINTK ("%s: Abnormal interrupt, status %8.8x.\n",
dev->name, status);
assert (dev != NULL);
assert (tp != NULL);
-
- ioaddr = tp->mmio_addr;
-
- if (status == 0xffffffff) {
- printk (KERN_WARNING PFX "abnormal interrupt, card ejected? (ok to ignore)\n");
- return -1;
- }
-
+ assert (ioaddr != NULL);
+
/* Update the error count. */
tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
RTL_W32 (RxMissed, 0);
@@ -1519,9 +1550,9 @@ static inline int rtl8139_weird_interrupt (struct net_device *dev,
|| tp->duplex_lock;
if (tp->full_duplex != duplex) {
tp->full_duplex = duplex;
- RTL_W8 (Cfg9346, 0xC0);
+ RTL_W8 (Cfg9346, Cfg9346_Unlock);
RTL_W8 (Config1, tp->full_duplex ? 0x60 : 0x20);
- RTL_W8 (Cfg9346, 0x00);
+ RTL_W8 (Cfg9346, Cfg9346_Lock);
}
status &= ~RxUnderrun;
}
@@ -1559,15 +1590,20 @@ static void rtl8139_interrupt (int irq, void *dev_instance,
struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv;
int boguscnt = max_interrupt_work;
void *ioaddr = tp->mmio_addr;
- int link_changed = 0; /* Grrr, avoid bogus "uninitialized" warning */
-
- spin_lock_irq (&tp->lock);
+ int status = 0, link_changed = 0; /* avoid bogus "uninit" warning */
+ spin_lock (&tp->lock);
+
/* disable interrupt generation while handling this interrupt */
RTL_W16 (IntrMask, 0x0000);
do {
- int status = RTL_R16 (IntrStatus);
+ status = RTL_R16 (IntrStatus);
+
+ /* h/w no longer present (hotplug?) or major error, bail */
+ if (status == 0xFFFFFFFF)
+ break;
+
/* Acknowledge all of the current interrupt sources ASAP, but
an first get an additional status bit from CSCR. */
if (status & RxUnderrun)
@@ -1606,42 +1642,45 @@ static void rtl8139_interrupt (int irq, void *dev_instance,
/* Check uncommon events with one test. */
if (status & (PCIErr | PCSTimeout | RxUnderrun | RxOverflow |
RxFIFOOver | TxErr | RxErr))
- if (rtl8139_weird_interrupt (dev, tp, status,
- link_changed) == -1)
- break;
+ rtl8139_weird_interrupt (dev, tp, ioaddr,
+ status, link_changed);
if (status & (RxOK | RxUnderrun | RxOverflow | RxFIFOOver)) /* Rx interrupt */
- rtl8139_rx_interrupt (dev, tp);
+ rtl8139_rx_interrupt (dev, tp, ioaddr);
if (status & (TxOK | TxErr))
- rtl8139_tx_interrupt (dev, tp);
-
- if (--boguscnt < 0) {
- printk (KERN_WARNING
- "%s: Too much work at interrupt, "
- "IntrStatus=0x%4.4x.\n", dev->name,
- status);
- /* Clear all interrupt sources. */
- RTL_W16 (IntrStatus, 0xffff);
- break;
- }
- } while (1);
+ rtl8139_tx_interrupt (dev, tp, ioaddr);
+
+ boguscnt--;
+ } while (boguscnt > 0);
+
+ if (boguscnt <= 0) {
+ printk (KERN_WARNING
+ "%s: Too much work at interrupt, "
+ "IntrStatus=0x%4.4x.\n", dev->name,
+ status);
+
+ /* Clear all interrupt sources. */
+ RTL_W16 (IntrStatus, 0xffff);
+ }
/* Enable all known interrupts by setting the interrupt mask. */
RTL_W16 (IntrMask, rtl8139_intr_mask);
- spin_unlock_irq (&tp->lock);
-
+ spin_unlock (&tp->lock);
+
DPRINTK ("%s: exiting interrupt, intr_status=%#4.4x.\n",
- dev->name, RTL_R16 (IntrStatus));
+ dev->name, RTL_R16 (IntrStatus));
}
+
static int rtl8139_close (struct net_device *dev)
{
struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv;
void *ioaddr = tp->mmio_addr;
int i;
+ unsigned long flags;
DPRINTK ("ENTER\n");
@@ -1650,6 +1689,8 @@ static int rtl8139_close (struct net_device *dev)
DPRINTK ("%s: Shutting down ethercard, status was 0x%4.4x.\n",
dev->name, RTL_R16 (IntrStatus));
+ spin_lock_irqsave (&tp->lock, flags);
+
/* Disable interrupts by clearing the interrupt mask. */
RTL_W16 (IntrMask, 0x0000);
@@ -1660,7 +1701,13 @@ static int rtl8139_close (struct net_device *dev)
tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
RTL_W32 (RxMissed, 0);
+ spin_unlock_irqrestore (&tp->lock, flags);
+
del_timer (&tp->timer);
+
+ /* snooze for a small bit */
+ if (current->need_resched)
+ schedule ();
free_irq (dev->irq, dev);
@@ -1685,7 +1732,7 @@ static int rtl8139_close (struct net_device *dev)
tp->tx_bufs = NULL;
/* Green! Put the chip in low-power mode. */
- RTL_W8 (Cfg9346, 0xC0);
+ RTL_W8 (Cfg9346, Cfg9346_Unlock);
RTL_W8 (Config1, 0x03);
RTL_W8 (HltClk, 'H'); /* 'R' would leave the clock running. */
@@ -1695,10 +1742,13 @@ static int rtl8139_close (struct net_device *dev)
return 0;
}
+
static int mii_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
{
struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv;
u16 *data = (u16 *) & rq->ifr_data;
+ unsigned long flags;
+ int rc = 0;
DPRINTK ("ENTER\n");
@@ -1706,24 +1756,34 @@ static int mii_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */
data[0] = tp->phys[0] & 0x3f;
/* Fall Through */
+
case SIOCDEVPRIVATE + 1: /* Read the specified MII register. */
+ spin_lock_irqsave (&tp->lock, flags);
data[3] = mdio_read (dev, data[0], data[1] & 0x1f);
- DPRINTK ("EXIT\n");
- return 0;
+ spin_unlock_irqrestore (&tp->lock, flags);
+ break;
+
case SIOCDEVPRIVATE + 2: /* Write the specified MII register */
- if (!capable (CAP_NET_ADMIN))
- return -EPERM;
+ if (!capable (CAP_NET_ADMIN)) {
+ rc = -EPERM;
+ break;
+ }
+
+ spin_lock_irqsave (&tp->lock, flags);
mdio_write (dev, data[0], data[1] & 0x1f, data[2]);
- DPRINTK ("EXIT\n");
- return 0;
+ spin_unlock_irqrestore (&tp->lock, flags);
+ break;
+
default:
- DPRINTK ("EXIT\n");
- return -EOPNOTSUPP;
+ rc = -EOPNOTSUPP;
+ break;
}
- DPRINTK ("EXIT\n");
+ DPRINTK ("EXIT, returning %d\n", rc);
+ return rc;
}
+
static struct net_device_stats *rtl8139_get_stats (struct net_device *dev)
{
struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv;
@@ -1734,8 +1794,14 @@ static struct net_device_stats *rtl8139_get_stats (struct net_device *dev)
assert (tp != NULL);
if (netif_running(dev)) {
+ unsigned long flags;
+
+ spin_lock_irqsave (&tp->lock, flags);
+
tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
RTL_W32 (RxMissed, 0);
+
+ spin_unlock_irqrestore (&tp->lock, flags);
}
DPRINTK ("EXIT\n");
@@ -1765,12 +1831,14 @@ static inline u32 ether_crc (int length, unsigned char *data)
return crc;
}
+
static void rtl8139_set_rx_mode (struct net_device *dev)
{
struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv;
void *ioaddr = tp->mmio_addr;
u32 mc_filter[2]; /* Multicast hash filter */
int i, rx_mode;
+ unsigned long flags;
DPRINTK ("ENTER\n");
@@ -1803,11 +1871,19 @@ static void rtl8139_set_rx_mode (struct net_device *dev)
dmi_addr) >> 26,
mc_filter);
}
+
+ /* if called from irq handler, lock already acquired */
+ if (!in_irq ())
+ spin_lock_irqsave (&tp->lock, flags);
+
/* We can safely update without stopping the chip. */
RTL_W32 (RxConfig, rtl8139_rx_config | rx_mode);
RTL_W32_F (MAR0 + 0, mc_filter[0]);
RTL_W32_F (MAR0 + 4, mc_filter[1]);
+ if (!in_irq ())
+ spin_unlock_irqrestore (&tp->lock, flags);
+
DPRINTK ("EXIT\n");
}
@@ -1817,9 +1893,12 @@ static void rtl8139_suspend (struct pci_dev *pdev)
struct net_device *dev = PCI_GET_DRIVER_DATA (pdev);
struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv;
void *ioaddr = tp->mmio_addr;
+ unsigned long flags;
- netif_stop_queue (dev);
+ netif_device_detach (dev);
+ spin_lock_irqsave (&tp->lock, flags);
+
/* Disable interrupts, stop Tx and Rx. */
RTL_W16 (IntrMask, 0x0000);
RTL_W8 (ChipCmd, 0x00);
@@ -1827,6 +1906,8 @@ static void rtl8139_suspend (struct pci_dev *pdev)
/* Update the error counts. */
tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
RTL_W32 (RxMissed, 0);
+
+ spin_unlock_irqrestore (&tp->lock, flags);
}
@@ -1834,7 +1915,8 @@ static void rtl8139_resume (struct pci_dev *pdev)
{
struct net_device *dev = PCI_GET_DRIVER_DATA (pdev);
- rtl8139_hw_start(dev);
+ netif_device_attach (dev);
+ rtl8139_hw_start (dev);
}
@@ -1850,22 +1932,7 @@ static struct pci_driver rtl8139_pci_driver = {
static int __init rtl8139_init_module (void)
{
- int rc;
-
- DPRINTK ("ENTER\n");
-
- rc = pci_register_driver (&rtl8139_pci_driver);
-
- if (rc > 0) {
- printk (KERN_INFO RTL8139_DRIVER_NAME
- " loaded (%d device%s registered)\n",
- rc, rc > 1 ? "s" : "");
- } else {
- pci_unregister_driver (&rtl8139_pci_driver);
- }
-
- DPRINTK ("EXIT\n");
- return rc > 0 ? 0 : -ENODEV;
+ return pci_module_init (&rtl8139_pci_driver);
}