patch-2.4.27 linux-2.4.27/drivers/net/r8169.c
Next file: linux-2.4.27/drivers/net/sis900.c
Previous file: linux-2.4.27/drivers/net/pcnet32.c
Back to the patch index
Back to the overall index
- Lines: 1450
- Date:
2004-08-07 16:26:05.131366377 -0700
- Orig file:
linux-2.4.26/drivers/net/r8169.c
- Orig date:
2004-04-14 06:05:30.000000000 -0700
diff -urN linux-2.4.26/drivers/net/r8169.c linux-2.4.27/drivers/net/r8169.c
@@ -33,6 +33,9 @@
- Copy mc_filter setup code from 8139cp
(includes an optimization, and avoids set_bit use)
+ <2003/11/30>
+
+ - Add new rtl8169_{suspend/resume}() support
*/
#include <linux/module.h>
@@ -40,11 +43,15 @@
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/delay.h>
+#include <linux/ethtool.h>
#include <linux/crc32.h>
#include <linux/init.h>
#include <asm/io.h>
+#define DMA_64BIT_MASK 0xffffffffffffffffULL
+#define DMA_32BIT_MASK 0x00000000ffffffffULL
+
#define RTL8169_VERSION "1.2"
#define MODULENAME "r8169"
#define RTL8169_DRIVER_NAME MODULENAME " Gigabit Ethernet driver " RTL8169_VERSION
@@ -56,9 +63,11 @@
printk( "Assertion failed! %s,%s,%s,line=%d\n", \
#expr,__FILE__,__FUNCTION__,__LINE__); \
}
+#define dprintk(fmt, args...) do { printk(PFX fmt, ## args) } while (0)
#else
#define assert(expr) do {} while (0)
-#endif
+#define dprintk(fmt, args...) do {} while (0)
+#endif /* RTL8169_DEBUG */
/* media options */
#define MAX_UNITS 8
@@ -89,9 +98,12 @@
#define NUM_TX_DESC 64 /* Number of Tx descriptor registers */
#define NUM_RX_DESC 64 /* Number of Rx descriptor registers */
#define RX_BUF_SIZE 1536 /* Rx Buffer size */
+#define R8169_TX_RING_BYTES (NUM_TX_DESC * sizeof(struct TxDesc))
+#define R8169_RX_RING_BYTES (NUM_RX_DESC * sizeof(struct RxDesc))
#define RTL_MIN_IO_SIZE 0x80
-#define TX_TIMEOUT (6*HZ)
+#define RTL8169_TX_TIMEOUT (6*HZ)
+#define RTL8169_PHY_TIMEOUT (HZ)
/* write/read MMIO register */
#define RTL_W8(reg, val8) writeb ((val8), ioaddr + (reg))
@@ -101,24 +113,52 @@
#define RTL_R16(reg) readw (ioaddr + (reg))
#define RTL_R32(reg) ((unsigned long) readl (ioaddr + (reg)))
-static struct {
+enum mac_version {
+ RTL_GIGA_MAC_VER_B = 0x00,
+ /* RTL_GIGA_MAC_VER_C = 0x03, */
+ RTL_GIGA_MAC_VER_D = 0x01,
+ RTL_GIGA_MAC_VER_E = 0x02
+};
+
+enum phy_version {
+ RTL_GIGA_PHY_VER_C = 0x03, /* PHY Reg 0x03 bit0-3 == 0x0000 */
+ RTL_GIGA_PHY_VER_D = 0x04, /* PHY Reg 0x03 bit0-3 == 0x0000 */
+ RTL_GIGA_PHY_VER_E = 0x05, /* PHY Reg 0x03 bit0-3 == 0x0000 */
+ RTL_GIGA_PHY_VER_F = 0x06, /* PHY Reg 0x03 bit0-3 == 0x0001 */
+ RTL_GIGA_PHY_VER_G = 0x07, /* PHY Reg 0x03 bit0-3 == 0x0002 */
+};
+
+
+#define _R(NAME,MAC,MASK) \
+ { .name = NAME, .mac_version = MAC, .RxConfigMask = MASK }
+
+const static struct {
const char *name;
-} board_info[] __devinitdata = {
- {
-"RealTek RTL8169 Gigabit Ethernet"},};
+ u8 mac_version;
+ u32 RxConfigMask; /* Clears the bits supported by this chip */
+} rtl_chip_info[] = {
+ _R("RTL8169", RTL_GIGA_MAC_VER_B, 0xff7e1880),
+ _R("RTL8169s/8110s", RTL_GIGA_MAC_VER_D, 0xff7e1880),
+ _R("RTL8169s/8110s", RTL_GIGA_MAC_VER_E, 0xff7e1880)
+};
+#undef _R
-static struct pci_device_id rtl8169_pci_tbl[] __devinitdata = {
+static struct pci_device_id rtl8169_pci_tbl[] = {
{0x10ec, 0x8169, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{0,},
};
MODULE_DEVICE_TABLE(pci, rtl8169_pci_tbl);
+static int rx_copybreak = 200;
+
enum RTL8169_registers {
MAC0 = 0, /* Ethernet hardware address. */
MAR0 = 8, /* Multicast filter. */
- TxDescStartAddr = 0x20,
- TxHDescStartAddr = 0x28,
+ TxDescStartAddrLow = 0x20,
+ TxDescStartAddrHigh = 0x24,
+ TxHDescStartAddrLow = 0x28,
+ TxHDescStartAddrHigh = 0x2c,
FLASH = 0x30,
ERSR = 0x36,
ChipCmd = 0x37,
@@ -143,7 +183,8 @@
PHYstatus = 0x6C,
RxMaxSize = 0xDA,
CPlusCmd = 0xE0,
- RxDescStartAddr = 0xE4,
+ RxDescAddrLow = 0xE4,
+ RxDescAddrHigh = 0xE8,
EarlyTxThres = 0xEC,
FuncEvent = 0xF0,
FuncEventMask = 0xF4,
@@ -195,7 +236,13 @@
/*TxConfigBits */
TxInterFrameGapShift = 24,
- TxDMAShift = 8, /* DMA burst value (0-7) is shift this many bits */
+ TxDMAShift = 8, /* DMA burst value (0-7) is shift this many bits */
+
+ /* CPlusCmd p.31 */
+ RxVlan = (1 << 6),
+ RxChkSum = (1 << 5),
+ PCIDAC = (1 << 4),
+ PCIMulRW = (1 << 3),
/*rtl8169_PHYstatus */
TBI_Enable = 0x80,
@@ -242,14 +289,6 @@
TBILinkOK = 0x02000000,
};
-const static struct {
- const char *name;
- u8 version; /* depend on RTL8169 docs */
- u32 RxConfigMask; /* should clear the bits supported by this chip */
-} rtl_chip_info[] = {
- {
-"RTL-8169", 0x00, 0xff7e1880,},};
-
enum _DescStatusBit {
OWNbit = 0x80000000,
EORbit = 0x40000000,
@@ -257,18 +296,18 @@
LSbit = 0x10000000,
};
+#define RsvdMask 0x3fffc000
+
struct TxDesc {
u32 status;
u32 vlan_tag;
- u32 buf_addr;
- u32 buf_Haddr;
+ u64 addr;
};
struct RxDesc {
u32 status;
u32 vlan_tag;
- u32 buf_addr;
- u32 buf_Haddr;
+ u64 addr;
};
struct rtl8169_private {
@@ -277,28 +316,34 @@
struct net_device_stats stats; /* statistics of net device */
spinlock_t lock; /* spin lock flag */
int chipset;
- unsigned long cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */
- unsigned long cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */
- unsigned long dirty_tx;
- unsigned char *TxDescArrays; /* Index of Tx Descriptor buffer */
- unsigned char *RxDescArrays; /* Index of Rx Descriptor buffer */
+ int mac_version;
+ int phy_version;
+ u32 cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */
+ u32 cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */
+ u32 dirty_rx;
+ u32 dirty_tx;
struct TxDesc *TxDescArray; /* Index of 256-alignment Tx Descriptor buffer */
struct RxDesc *RxDescArray; /* Index of 256-alignment Rx Descriptor buffer */
- unsigned char *RxBufferRings; /* Index of Rx Buffer */
- unsigned char *RxBufferRing[NUM_RX_DESC]; /* Index of Rx Buffer array */
+ dma_addr_t TxPhyAddr;
+ dma_addr_t RxPhyAddr;
+ struct sk_buff *Rx_skbuff[NUM_RX_DESC]; /* Rx data buffers */
struct sk_buff *Tx_skbuff[NUM_TX_DESC]; /* Index of Transmit data buffer */
+ struct timer_list timer;
+ unsigned long phy_link_down_cnt;
+ u16 cp_cmd;
};
MODULE_AUTHOR("Realtek");
MODULE_DESCRIPTION("RealTek RTL-8169 Gigabit Ethernet driver");
MODULE_PARM(media, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(rx_copybreak, "i");
MODULE_LICENSE("GPL");
static int rtl8169_open(struct net_device *dev);
static int rtl8169_start_xmit(struct sk_buff *skb, struct net_device *dev);
-static void rtl8169_interrupt(int irq, void *dev_instance,
+static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance,
struct pt_regs *regs);
-static void rtl8169_init_ring(struct net_device *dev);
+static int rtl8169_init_ring(struct net_device *dev);
static void rtl8169_hw_start(struct net_device *dev);
static int rtl8169_close(struct net_device *dev);
static void rtl8169_set_rx_mode(struct net_device *dev);
@@ -306,13 +351,16 @@
static struct net_device_stats *rtl8169_get_stats(struct net_device *netdev);
static const u16 rtl8169_intr_mask =
- SYSErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver | TxErr | TxOK |
- RxErr | RxOK;
+ RxUnderrun | RxOverflow | RxFIFOOver | TxErr | TxOK | RxErr | RxOK;
static const unsigned int rtl8169_rx_config =
(RX_FIFO_THRESH << RxCfgFIFOShift) | (RX_DMA_BURST << RxCfgDMAShift);
-void
-mdio_write(void *ioaddr, int RegAddr, int value)
+#define PHY_Cap_10_Half_Or_Less PHY_Cap_10_Half
+#define PHY_Cap_10_Full_Or_Less PHY_Cap_10_Full | PHY_Cap_10_Half_Or_Less
+#define PHY_Cap_100_Half_Or_Less PHY_Cap_100_Half | PHY_Cap_10_Full_Or_Less
+#define PHY_Cap_100_Full_Or_Less PHY_Cap_100_Full | PHY_Cap_100_Half_Or_Less
+
+static void mdio_write(void *ioaddr, int RegAddr, int value)
{
int i;
@@ -329,8 +377,7 @@
}
}
-int
-mdio_read(void *ioaddr, int RegAddr)
+static int mdio_read(void *ioaddr, int RegAddr)
{
int i, value = -1;
@@ -342,13 +389,272 @@
if (RTL_R32(PHYAR) & 0x80000000) {
value = (int) (RTL_R32(PHYAR) & 0xFFFF);
break;
- } else {
- udelay(100);
}
+ udelay(100);
}
return value;
}
+static void rtl8169_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ struct rtl8169_private *tp = dev->priv;
+
+ strcpy(info->driver, RTL8169_DRIVER_NAME);
+ strcpy(info->version, RTL8169_VERSION );
+ strcpy(info->bus_info, pci_name(tp->pci_dev));
+}
+
+static struct ethtool_ops rtl8169_ethtool_ops = {
+ .get_drvinfo = rtl8169_get_drvinfo,
+};
+
+static void rtl8169_write_gmii_reg_bit(void *ioaddr, int reg, int bitnum,
+ int bitval)
+{
+ int val;
+
+ val = mdio_read(ioaddr, reg);
+ val = (bitval == 1) ?
+ val | (bitval << bitnum) : val & ~(0x0001 << bitnum);
+ mdio_write(ioaddr, reg, val & 0xffff);
+}
+
+static void rtl8169_get_mac_version(struct rtl8169_private *tp, void *ioaddr)
+{
+ const struct {
+ u32 mask;
+ int mac_version;
+ } mac_info[] = {
+ { 0x1 << 26, RTL_GIGA_MAC_VER_E },
+ { 0x1 << 23, RTL_GIGA_MAC_VER_D },
+ { 0x00000000, RTL_GIGA_MAC_VER_B } /* Catch-all */
+ }, *p = mac_info;
+ u32 reg;
+
+ reg = RTL_R32(TxConfig) & 0x7c800000;
+ while ((reg & p->mask) != p->mask)
+ p++;
+ tp->mac_version = p->mac_version;
+}
+
+static void rtl8169_print_mac_version(struct rtl8169_private *tp)
+{
+ struct {
+ int version;
+ char *msg;
+ } mac_print[] = {
+ { RTL_GIGA_MAC_VER_E, "RTL_GIGA_MAC_VER_E" },
+ { RTL_GIGA_MAC_VER_D, "RTL_GIGA_MAC_VER_D" },
+ { RTL_GIGA_MAC_VER_B, "RTL_GIGA_MAC_VER_B" },
+ { 0, NULL }
+ }, *p;
+
+ for (p = mac_print; p->msg; p++) {
+ if (tp->mac_version == p->version) {
+ dprintk("mac_version == %s (%04d)\n", p->msg,
+ p->version);
+ return;
+ }
+ }
+ dprintk("mac_version == Unknown\n");
+}
+
+static void rtl8169_get_phy_version(struct rtl8169_private *tp, void *ioaddr)
+{
+ const struct {
+ u16 mask;
+ u16 set;
+ int phy_version;
+ } phy_info[] = {
+ { 0x000f, 0x0002, RTL_GIGA_PHY_VER_G },
+ { 0x000f, 0x0001, RTL_GIGA_PHY_VER_F },
+ { 0x000f, 0x0000, RTL_GIGA_PHY_VER_E },
+ { 0x0000, 0x0000, RTL_GIGA_PHY_VER_D } /* Catch-all */
+ }, *p = phy_info;
+ u16 reg;
+
+ reg = mdio_read(ioaddr, 3) & 0xffff;
+ while ((reg & p->mask) != p->set)
+ p++;
+ tp->phy_version = p->phy_version;
+}
+
+static void rtl8169_print_phy_version(struct rtl8169_private *tp)
+{
+ struct {
+ int version;
+ char *msg;
+ u32 reg;
+ } phy_print[] = {
+ { RTL_GIGA_PHY_VER_G, "RTL_GIGA_PHY_VER_G", 0x0002 },
+ { RTL_GIGA_PHY_VER_F, "RTL_GIGA_PHY_VER_F", 0x0001 },
+ { RTL_GIGA_PHY_VER_E, "RTL_GIGA_PHY_VER_E", 0x0000 },
+ { RTL_GIGA_PHY_VER_D, "RTL_GIGA_PHY_VER_D", 0x0000 },
+ { 0, NULL, 0x0000 }
+ }, *p;
+
+ for (p = phy_print; p->msg; p++) {
+ if (tp->phy_version == p->version) {
+ dprintk("phy_version == %s (%04x)\n", p->msg, p->reg);
+ return;
+ }
+ }
+ dprintk("phy_version == Unknown\n");
+}
+
+static void rtl8169_hw_phy_config(struct net_device *dev)
+{
+ struct rtl8169_private *tp = dev->priv;
+ void *ioaddr = tp->mmio_addr;
+ struct {
+ u16 regs[5]; /* Beware of bit-sign propagation */
+ } phy_magic[5] = { {
+ { 0x0000, //w 4 15 12 0
+ 0x00a1, //w 3 15 0 00a1
+ 0x0008, //w 2 15 0 0008
+ 0x1020, //w 1 15 0 1020
+ 0x1000 } },{ //w 0 15 0 1000
+ { 0x7000, //w 4 15 12 7
+ 0xff41, //w 3 15 0 ff41
+ 0xde60, //w 2 15 0 de60
+ 0x0140, //w 1 15 0 0140
+ 0x0077 } },{ //w 0 15 0 0077
+ { 0xa000, //w 4 15 12 a
+ 0xdf01, //w 3 15 0 df01
+ 0xdf20, //w 2 15 0 df20
+ 0xff95, //w 1 15 0 ff95
+ 0xfa00 } },{ //w 0 15 0 fa00
+ { 0xb000, //w 4 15 12 b
+ 0xff41, //w 3 15 0 ff41
+ 0xde20, //w 2 15 0 de20
+ 0x0140, //w 1 15 0 0140
+ 0x00bb } },{ //w 0 15 0 00bb
+ { 0xf000, //w 4 15 12 f
+ 0xdf01, //w 3 15 0 df01
+ 0xdf20, //w 2 15 0 df20
+ 0xff95, //w 1 15 0 ff95
+ 0xbf00 } //w 0 15 0 bf00
+ }
+ }, *p = phy_magic;
+ int i;
+
+ rtl8169_print_mac_version(tp);
+ rtl8169_print_phy_version(tp);
+
+ if (tp->mac_version <= RTL_GIGA_MAC_VER_B)
+ return;
+ if (tp->phy_version >= RTL_GIGA_PHY_VER_F)
+ return;
+
+ dprintk("MAC version != 0 && PHY version == 0 or 1\n");
+ dprintk("Do final_reg2.cfg\n");
+
+ /* Shazam ! */
+
+ // phy config for RTL8169s mac_version C chip
+ mdio_write(ioaddr, 31, 0x0001); //w 31 2 0 1
+ mdio_write(ioaddr, 21, 0x1000); //w 21 15 0 1000
+ mdio_write(ioaddr, 24, 0x65c7); //w 24 15 0 65c7
+ rtl8169_write_gmii_reg_bit(ioaddr, 4, 11, 0); //w 4 11 11 0
+
+ for (i = 0; i < ARRAY_SIZE(phy_magic); i++, p++) {
+ int val, pos = 4;
+
+ val = (mdio_read(ioaddr, pos) & 0x0fff) | (p->regs[0] & 0xffff);
+ mdio_write(ioaddr, pos, val);
+ while (--pos >= 0)
+ mdio_write(ioaddr, pos, p->regs[4 - pos] & 0xffff);
+ rtl8169_write_gmii_reg_bit(ioaddr, 4, 11, 1); //w 4 11 11 1
+ rtl8169_write_gmii_reg_bit(ioaddr, 4, 11, 0); //w 4 11 11 0
+ }
+ mdio_write(ioaddr, 31, 0x0000); //w 31 2 0 0
+}
+
+static void rtl8169_hw_phy_reset(struct net_device *dev)
+{
+ struct rtl8169_private *tp = dev->priv;
+ void *ioaddr = tp->mmio_addr;
+ int i, val;
+
+ printk(KERN_WARNING PFX "%s: Reset RTL8169s PHY\n", dev->name);
+
+ val = (mdio_read(ioaddr, 0) | 0x8000) & 0xffff;
+ mdio_write(ioaddr, 0, val);
+
+ for (i = 50; i >= 0; i--) {
+ if (!(mdio_read(ioaddr, 0) & 0x8000))
+ break;
+ udelay(100); /* Gross */
+ }
+
+ if (i < 0) {
+ printk(KERN_WARNING PFX "%s: no PHY Reset ack. Giving up.\n",
+ dev->name);
+ }
+}
+
+static void rtl8169_phy_timer(unsigned long __opaque)
+{
+ struct net_device *dev = (struct net_device *)__opaque;
+ struct rtl8169_private *tp = dev->priv;
+ struct timer_list *timer = &tp->timer;
+ void *ioaddr = tp->mmio_addr;
+
+ assert(tp->mac_version > RTL_GIGA_MAC_VER_B);
+ assert(tp->phy_version < RTL_GIGA_PHY_VER_G);
+
+ if (RTL_R8(PHYstatus) & LinkStatus)
+ tp->phy_link_down_cnt = 0;
+ else {
+ tp->phy_link_down_cnt++;
+ if (tp->phy_link_down_cnt >= 12) {
+ int reg;
+
+ // If link on 1000, perform phy reset.
+ reg = mdio_read(ioaddr, PHY_1000_CTRL_REG);
+ if (reg & PHY_Cap_1000_Full)
+ rtl8169_hw_phy_reset(dev);
+
+ tp->phy_link_down_cnt = 0;
+ }
+ }
+
+ mod_timer(timer, jiffies + RTL8169_PHY_TIMEOUT);
+}
+
+static inline void rtl8169_delete_timer(struct net_device *dev)
+{
+ struct rtl8169_private *tp = dev->priv;
+ struct timer_list *timer = &tp->timer;
+
+ if ((tp->mac_version <= RTL_GIGA_MAC_VER_B) ||
+ (tp->phy_version >= RTL_GIGA_PHY_VER_G))
+ return;
+
+ del_timer_sync(timer);
+
+ tp->phy_link_down_cnt = 0;
+}
+
+static inline void rtl8169_request_timer(struct net_device *dev)
+{
+ struct rtl8169_private *tp = dev->priv;
+ struct timer_list *timer = &tp->timer;
+
+ if ((tp->mac_version <= RTL_GIGA_MAC_VER_B) ||
+ (tp->phy_version >= RTL_GIGA_PHY_VER_G))
+ return;
+
+ tp->phy_link_down_cnt = 0;
+
+ init_timer(timer);
+ timer->expires = jiffies + RTL8169_PHY_TIMEOUT;
+ timer->data = (unsigned long)(dev);
+ timer->function = rtl8169_phy_timer;
+ add_timer(timer);
+}
+
static int __devinit
rtl8169_init_board(struct pci_dev *pdev, struct net_device **dev_out,
void **ioaddr_out)
@@ -356,9 +662,9 @@
void *ioaddr = NULL;
struct net_device *dev;
struct rtl8169_private *tp;
- int rc, i;
unsigned long mmio_start, mmio_end, mmio_flags, mmio_len;
- u32 tmp;
+ int rc, i, acpi_idle_state = 0, pm_cap;
+
assert(pdev != NULL);
assert(ioaddr_out != NULL);
@@ -374,12 +680,27 @@
}
SET_MODULE_OWNER(dev);
+ SET_NETDEV_DEV(dev, &pdev->dev);
tp = dev->priv;
// enable device (incl. PCI PM wakeup and hotplug setup)
rc = pci_enable_device(pdev);
- if (rc)
+ if (rc) {
+ printk(KERN_ERR PFX "%s: unable to enable device\n", pdev->slot_name);
goto err_out;
+ }
+
+ /* save power state before pci_enable_device overwrites it */
+ pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM);
+ if (pm_cap) {
+ u16 pwr_command;
+
+ pci_read_config_word(pdev, pm_cap + PCI_PM_CTRL, &pwr_command);
+ acpi_idle_state = pwr_command & PCI_PM_CTRL_STATE_MASK;
+ } else {
+ printk(KERN_ERR PFX "Cannot find PowerManagement capability, aborting.\n");
+ goto err_out_free_res;
+ }
mmio_start = pci_resource_start(pdev, 1);
mmio_end = pci_resource_end(pdev, 1);
@@ -401,8 +722,24 @@
}
rc = pci_request_regions(pdev, dev->name);
- if (rc)
+ if (rc) {
+ printk(KERN_ERR PFX "%s: Could not request regions.\n", pdev->slot_name);
goto err_out_disable;
+ }
+
+ tp->cp_cmd = PCIMulRW | RxChkSum;
+
+ if ((sizeof(dma_addr_t) > 32) &&
+ !pci_set_dma_mask(pdev, DMA_64BIT_MASK))
+ tp->cp_cmd |= PCIDAC;
+ else {
+ rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+ if (rc < 0) {
+ printk(KERN_ERR PFX "DMA configuration failed.\n");
+ goto err_out_free_res;
+ }
+ }
+
// enable PCI bus-mastering
pci_set_master(pdev);
@@ -419,30 +756,32 @@
RTL_W8(ChipCmd, CmdReset);
// Check that the chip has finished the reset.
- for (i = 1000; i > 0; i--)
+ for (i = 1000; i > 0; i--) {
if ((RTL_R8(ChipCmd) & CmdReset) == 0)
break;
- else
- udelay(10);
+ udelay(10);
+ }
- // identify chip attached to board
- tmp = RTL_R32(TxConfig);
- tmp = ((tmp & 0x7c000000) + ((tmp & 0x00800000) << 2)) >> 24;
-
- for (i = ARRAY_SIZE(rtl_chip_info) - 1; i >= 0; i--)
- if (tmp == rtl_chip_info[i].version) {
- tp->chipset = i;
- goto match;
- }
- //if unknown chip, assume array element #0, original RTL-8169 in this case
- printk(KERN_DEBUG PFX
- "PCI device %s: unknown chip version, assuming RTL-8169\n",
- pdev->slot_name);
- printk(KERN_DEBUG PFX "PCI device %s: TxConfig = 0x%lx\n",
- pdev->slot_name, (unsigned long) RTL_R32(TxConfig));
- tp->chipset = 0;
+ // Identify chip attached to board
+ rtl8169_get_mac_version(tp, ioaddr);
+ rtl8169_get_phy_version(tp, ioaddr);
+
+ rtl8169_print_mac_version(tp);
+ rtl8169_print_phy_version(tp);
+
+ for (i = ARRAY_SIZE(rtl_chip_info) - 1; i >= 0; i--) {
+ if (tp->mac_version == rtl_chip_info[i].mac_version)
+ break;
+ }
+ if (i < 0) {
+ /* Unknown chip: assume array element #0, original RTL-8169 */
+ printk(KERN_DEBUG PFX
+ "PCI device %s: unknown chip version, assuming %s\n",
+ pci_name(pdev), rtl_chip_info[0].name);
+ i++;
+ }
+ tp->chipset = i;
-match:
*ioaddr_out = ioaddr;
*dev_out = dev;
return 0;
@@ -454,7 +793,7 @@
pci_disable_device(pdev);
err_out:
- kfree(dev);
+ free_netdev(dev);
return rc;
}
@@ -495,10 +834,11 @@
dev->open = rtl8169_open;
dev->hard_start_xmit = rtl8169_start_xmit;
dev->get_stats = rtl8169_get_stats;
+ dev->ethtool_ops = &rtl8169_ethtool_ops;
dev->stop = rtl8169_close;
dev->tx_timeout = rtl8169_tx_timeout;
dev->set_multicast_list = rtl8169_set_rx_mode;
- dev->watchdog_timeo = TX_TIMEOUT;
+ dev->watchdog_timeo = RTL8169_TX_TIMEOUT;
dev->irq = pdev->irq;
dev->base_addr = (unsigned long) ioaddr;
// dev->do_ioctl = mii_ioctl;
@@ -514,7 +854,7 @@
iounmap(ioaddr);
pci_release_regions(pdev);
pci_disable_device(pdev);
- kfree(dev);
+ free_netdev(dev);
return rc;
}
@@ -527,12 +867,29 @@
"%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, "
"IRQ %d\n",
dev->name,
- board_info[ent->driver_data].name,
+ rtl_chip_info[ent->driver_data].name,
dev->base_addr,
dev->dev_addr[0], dev->dev_addr[1],
dev->dev_addr[2], dev->dev_addr[3],
dev->dev_addr[4], dev->dev_addr[5], dev->irq);
+ rtl8169_hw_phy_config(dev);
+
+ dprintk("Set MAC Reg C+CR Offset 0x82h = 0x01h\n");
+ RTL_W8(0x82, 0x01);
+
+ if (tp->mac_version < RTL_GIGA_MAC_VER_E) {
+ dprintk("Set PCI Latency=0x40\n");
+ pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x40);
+ }
+
+ if (tp->mac_version == RTL_GIGA_MAC_VER_D) {
+ dprintk("Set MAC Reg C+CR Offset 0x82h = 0x01h\n");
+ RTL_W8(0x82, 0x01);
+ dprintk("Set PHY Reg 0x0bh = 0x00h\n");
+ mdio_write(ioaddr, 0x0b, 0x0000); //w 0x0b 15 0 0
+ }
+
// if TBI is not endbled
if (!(RTL_R8(PHYstatus) & TBI_Enable)) {
int val = mdio_read(ioaddr, PHY_AUTO_NEGO_REG);
@@ -545,23 +902,23 @@
Cap10_100 = 0, Cap1000 = 0;
switch (option) {
case _10_Half:
- Cap10_100 = PHY_Cap_10_Half;
+ Cap10_100 = PHY_Cap_10_Half_Or_Less;
Cap1000 = PHY_Cap_Null;
break;
case _10_Full:
- Cap10_100 = PHY_Cap_10_Full;
+ Cap10_100 = PHY_Cap_10_Full_Or_Less;
Cap1000 = PHY_Cap_Null;
break;
case _100_Half:
- Cap10_100 = PHY_Cap_100_Half;
+ Cap10_100 = PHY_Cap_100_Half_Or_Less;
Cap1000 = PHY_Cap_Null;
break;
case _100_Full:
- Cap10_100 = PHY_Cap_100_Full;
+ Cap10_100 = PHY_Cap_100_Full_Or_Less;
Cap1000 = PHY_Cap_Null;
break;
case _1000_Full:
- Cap10_100 = PHY_Cap_Null;
+ Cap10_100 = PHY_Cap_100_Full_Or_Less;
Cap1000 = PHY_Cap_1000_Full;
break;
default:
@@ -575,9 +932,7 @@
// enable 10/100 Full/Half Mode, leave PHY_AUTO_NEGO_REG bit4:0 unchanged
mdio_write(ioaddr, PHY_AUTO_NEGO_REG,
- PHY_Cap_10_Half | PHY_Cap_10_Full |
- PHY_Cap_100_Half | PHY_Cap_100_Full | (val &
- 0x1F));
+ PHY_Cap_100_Full_Or_Less | (val & 0x1f));
// enable 1000 Full Mode
mdio_write(ioaddr, PHY_1000_CTRL_REG,
@@ -641,65 +996,101 @@
iounmap(tp->mmio_addr);
pci_release_regions(pdev);
- // poison memory before freeing
- memset(dev, 0xBC,
- sizeof (struct net_device) + sizeof (struct rtl8169_private));
-
pci_disable_device(pdev);
- kfree(dev);
+ free_netdev(dev);
pci_set_drvdata(pdev, NULL);
}
+#ifdef CONFIG_PM
+
+static int rtl8169_suspend(struct pci_dev *pdev, u32 state)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct rtl8169_private *tp = dev->priv;
+ void *ioaddr = tp->mmio_addr;
+ unsigned long flags;
+
+ if (!netif_running(dev))
+ return 0;
+
+ netif_device_detach(dev);
+ netif_stop_queue(dev);
+ spin_lock_irqsave(&tp->lock, flags);
+
+ /* Disable interrupts, stop Rx and Tx */
+ RTL_W16(IntrMask, 0);
+ RTL_W8(ChipCmd, 0);
+
+ /* Update the error counts. */
+ tp->stats.rx_missed_errors += RTL_R32(RxMissed);
+ RTL_W32(RxMissed, 0);
+ spin_unlock_irqrestore(&tp->lock, flags);
+
+ return 0;
+}
+
+static int rtl8169_resume(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+
+ if (!netif_running(dev))
+ return 0;
+
+ netif_device_attach(dev);
+ rtl8169_hw_start(dev);
+
+ return 0;
+}
+
+#endif /* CONFIG_PM */
+
static int
rtl8169_open(struct net_device *dev)
{
struct rtl8169_private *tp = dev->priv;
+ struct pci_dev *pdev = tp->pci_dev;
int retval;
- u8 diff;
- u32 TxPhyAddr, RxPhyAddr;
retval =
request_irq(dev->irq, rtl8169_interrupt, SA_SHIRQ, dev->name, dev);
- if (retval) {
- return retval;
- }
+ if (retval < 0)
+ goto out;
- tp->TxDescArrays =
- kmalloc(NUM_TX_DESC * sizeof (struct TxDesc) + 256, GFP_KERNEL);
- // Tx Desscriptor needs 256 bytes alignment;
- TxPhyAddr = virt_to_bus(tp->TxDescArrays);
- diff = 256 - (TxPhyAddr - ((TxPhyAddr >> 8) << 8));
- TxPhyAddr += diff;
- tp->TxDescArray = (struct TxDesc *) (tp->TxDescArrays + diff);
-
- tp->RxDescArrays =
- kmalloc(NUM_RX_DESC * sizeof (struct RxDesc) + 256, GFP_KERNEL);
- // Rx Desscriptor needs 256 bytes alignment;
- RxPhyAddr = virt_to_bus(tp->RxDescArrays);
- diff = 256 - (RxPhyAddr - ((RxPhyAddr >> 8) << 8));
- RxPhyAddr += diff;
- tp->RxDescArray = (struct RxDesc *) (tp->RxDescArrays + diff);
+ retval = -ENOMEM;
- if (tp->TxDescArrays == NULL || tp->RxDescArrays == NULL) {
- printk(KERN_INFO
- "Allocate RxDescArray or TxDescArray failed\n");
- free_irq(dev->irq, dev);
- if (tp->TxDescArrays)
- kfree(tp->TxDescArrays);
- if (tp->RxDescArrays)
- kfree(tp->RxDescArrays);
- return -ENOMEM;
- }
- tp->RxBufferRings = kmalloc(RX_BUF_SIZE * NUM_RX_DESC, GFP_KERNEL);
- if (tp->RxBufferRings == NULL) {
- printk(KERN_INFO "Allocate RxBufferRing failed\n");
- }
+ /*
+ * Rx and Tx desscriptors needs 256 bytes alignment.
+ * pci_alloc_consistent provides more.
+ */
+ tp->TxDescArray = pci_alloc_consistent(pdev, R8169_TX_RING_BYTES,
+ &tp->TxPhyAddr);
+ if (!tp->TxDescArray)
+ goto err_free_irq;
+
+ tp->RxDescArray = pci_alloc_consistent(pdev, R8169_RX_RING_BYTES,
+ &tp->RxPhyAddr);
+ if (!tp->RxDescArray)
+ goto err_free_tx;
+
+ retval = rtl8169_init_ring(dev);
+ if (retval < 0)
+ goto err_free_rx;
- rtl8169_init_ring(dev);
rtl8169_hw_start(dev);
- return 0;
-
+ rtl8169_request_timer(dev);
+out:
+ return retval;
+
+err_free_rx:
+ pci_free_consistent(pdev, R8169_RX_RING_BYTES, tp->RxDescArray,
+ tp->RxPhyAddr);
+err_free_tx:
+ pci_free_consistent(pdev, R8169_TX_RING_BYTES, tp->TxDescArray,
+ tp->TxPhyAddr);
+err_free_irq:
+ free_irq(dev->irq, dev);
+ goto out;
}
static void
@@ -736,11 +1127,21 @@
RTL_W32(TxConfig,
(TX_DMA_BURST << TxDMAShift) | (InterFrameGap <<
TxInterFrameGapShift));
+ tp->cp_cmd |= RTL_R16(CPlusCmd);
+ RTL_W16(CPlusCmd, tp->cp_cmd);
+
+ if (tp->mac_version == RTL_GIGA_MAC_VER_D) {
+ dprintk(KERN_INFO PFX "Set MAC Reg C+CR Offset 0xE0: bit-3 and bit-14 MUST be 1\n");
+ tp->cp_cmd |= (1 << 14) | PCIMulRW;
+ RTL_W16(CPlusCmd, tp->cp_cmd);
+ }
tp->cur_rx = 0;
- RTL_W32(TxDescStartAddr, virt_to_bus(tp->TxDescArray));
- RTL_W32(RxDescStartAddr, virt_to_bus(tp->RxDescArray));
+ RTL_W32(TxDescStartAddrLow, ((u64) tp->TxPhyAddr & DMA_32BIT_MASK));
+ RTL_W32(TxDescStartAddrHigh, ((u64) tp->TxPhyAddr >> 32));
+ RTL_W32(RxDescAddrLow, ((u64) tp->RxPhyAddr & DMA_32BIT_MASK));
+ RTL_W32(RxDescAddrHigh, ((u64) tp->RxPhyAddr >> 32));
RTL_W8(Cfg9346, Cfg9346_Lock);
udelay(10);
@@ -758,31 +1159,131 @@
}
-static void
-rtl8169_init_ring(struct net_device *dev)
+static inline void rtl8169_make_unusable_by_asic(struct RxDesc *desc)
+{
+ desc->addr = 0x0badbadbadbadbad;
+ desc->status &= ~cpu_to_le32(OWNbit | RsvdMask);
+}
+
+static void rtl8169_free_rx_skb(struct pci_dev *pdev, struct sk_buff **sk_buff,
+ struct RxDesc *desc)
+{
+ pci_unmap_single(pdev, le64_to_cpu(desc->addr), RX_BUF_SIZE,
+ PCI_DMA_FROMDEVICE);
+ dev_kfree_skb(*sk_buff);
+ *sk_buff = NULL;
+ rtl8169_make_unusable_by_asic(desc);
+}
+
+static inline void rtl8169_return_to_asic(struct RxDesc *desc)
+{
+ desc->status |= cpu_to_le32(OWNbit + RX_BUF_SIZE);
+}
+
+static inline void rtl8169_give_to_asic(struct RxDesc *desc, dma_addr_t mapping)
+{
+ desc->addr = cpu_to_le64(mapping);
+ desc->status |= cpu_to_le32(OWNbit + RX_BUF_SIZE);
+}
+
+static int rtl8169_alloc_rx_skb(struct pci_dev *pdev, struct net_device *dev,
+ struct sk_buff **sk_buff, struct RxDesc *desc)
+{
+ struct sk_buff *skb;
+ dma_addr_t mapping;
+ int ret = 0;
+
+ skb = dev_alloc_skb(RX_BUF_SIZE);
+ if (!skb)
+ goto err_out;
+
+ skb->dev = dev;
+ skb_reserve(skb, 2);
+ *sk_buff = skb;
+
+ mapping = pci_map_single(pdev, skb->tail, RX_BUF_SIZE,
+ PCI_DMA_FROMDEVICE);
+
+ rtl8169_give_to_asic(desc, mapping);
+
+out:
+ return ret;
+
+err_out:
+ ret = -ENOMEM;
+ rtl8169_make_unusable_by_asic(desc);
+ goto out;
+}
+
+static void rtl8169_rx_clear(struct rtl8169_private *tp)
{
- struct rtl8169_private *tp = dev->priv;
int i;
- tp->cur_rx = 0;
- tp->cur_tx = 0;
- tp->dirty_tx = 0;
+ for (i = 0; i < NUM_RX_DESC; i++) {
+ if (tp->Rx_skbuff[i]) {
+ rtl8169_free_rx_skb(tp->pci_dev, tp->Rx_skbuff + i,
+ tp->RxDescArray + i);
+ }
+ }
+}
+
+static u32 rtl8169_rx_fill(struct rtl8169_private *tp, struct net_device *dev,
+ u32 start, u32 end)
+{
+ u32 cur;
+
+ for (cur = start; end - cur > 0; cur++) {
+ int ret, i = cur % NUM_RX_DESC;
+
+ if (tp->Rx_skbuff[i])
+ continue;
+
+ ret = rtl8169_alloc_rx_skb(tp->pci_dev, dev, tp->Rx_skbuff + i,
+ tp->RxDescArray + i);
+ if (ret < 0)
+ break;
+ }
+ return cur - start;
+}
+
+static inline void rtl8169_mark_as_last_descriptor(struct RxDesc *desc)
+{
+ desc->status |= cpu_to_le32(EORbit);
+}
+
+static int rtl8169_init_ring(struct net_device *dev)
+{
+ struct rtl8169_private *tp = dev->priv;
+
+ tp->cur_rx = tp->dirty_rx = 0;
+ tp->cur_tx = tp->dirty_tx = 0;
memset(tp->TxDescArray, 0x0, NUM_TX_DESC * sizeof (struct TxDesc));
memset(tp->RxDescArray, 0x0, NUM_RX_DESC * sizeof (struct RxDesc));
- for (i = 0; i < NUM_TX_DESC; i++) {
- tp->Tx_skbuff[i] = NULL;
- }
- for (i = 0; i < NUM_RX_DESC; i++) {
- if (i == (NUM_RX_DESC - 1))
- tp->RxDescArray[i].status =
- (OWNbit | EORbit) + RX_BUF_SIZE;
- else
- tp->RxDescArray[i].status = OWNbit + RX_BUF_SIZE;
+ memset(tp->Tx_skbuff, 0x0, NUM_TX_DESC * sizeof(struct sk_buff *));
+ memset(tp->Rx_skbuff, 0x0, NUM_RX_DESC * sizeof(struct sk_buff *));
- tp->RxBufferRing[i] = &(tp->RxBufferRings[i * RX_BUF_SIZE]);
- tp->RxDescArray[i].buf_addr = virt_to_bus(tp->RxBufferRing[i]);
- }
+ if (rtl8169_rx_fill(tp, dev, 0, NUM_RX_DESC) != NUM_RX_DESC)
+ goto err_out;
+
+ rtl8169_mark_as_last_descriptor(tp->RxDescArray + NUM_RX_DESC - 1);
+
+ return 0;
+
+err_out:
+ rtl8169_rx_clear(tp);
+ return -ENOMEM;
+}
+
+static void rtl8169_unmap_tx_skb(struct pci_dev *pdev, struct sk_buff **sk_buff,
+ struct TxDesc *desc)
+{
+ u32 len = sk_buff[0]->len;
+
+ pci_unmap_single(pdev, le64_to_cpu(desc->addr),
+ len < ETH_ZLEN ? ETH_ZLEN : len, PCI_DMA_TODEVICE);
+ desc->addr = 0x00;
+ *sk_buff = NULL;
}
static void
@@ -792,9 +1293,12 @@
tp->cur_tx = 0;
for (i = 0; i < NUM_TX_DESC; i++) {
- if (tp->Tx_skbuff[i] != NULL) {
- dev_kfree_skb(tp->Tx_skbuff[i]);
- tp->Tx_skbuff[i] = NULL;
+ struct sk_buff *skb = tp->Tx_skbuff[i];
+
+ if (skb) {
+ rtl8169_unmap_tx_skb(tp->pci_dev, tp->Tx_skbuff + i,
+ tp->TxDescArray + i);
+ dev_kfree_skb(skb);
tp->stats.tx_dropped++;
}
}
@@ -832,41 +1336,51 @@
struct rtl8169_private *tp = dev->priv;
void *ioaddr = tp->mmio_addr;
int entry = tp->cur_tx % NUM_TX_DESC;
+ u32 len = skb->len;
- if (skb->len < ETH_ZLEN) {
+ if (unlikely(skb->len < ETH_ZLEN)) {
skb = skb_padto(skb, ETH_ZLEN);
- if (skb == NULL)
- return 0;
+ if (!skb)
+ goto err_update_stats;
+ len = ETH_ZLEN;
}
spin_lock_irq(&tp->lock);
- if ((tp->TxDescArray[entry].status & OWNbit) == 0) {
+ if (!(le32_to_cpu(tp->TxDescArray[entry].status) & OWNbit)) {
+ dma_addr_t mapping;
+
+ mapping = pci_map_single(tp->pci_dev, skb->data, len,
+ PCI_DMA_TODEVICE);
+
tp->Tx_skbuff[entry] = skb;
- tp->TxDescArray[entry].buf_addr = virt_to_bus(skb->data);
- if (entry != (NUM_TX_DESC - 1))
- tp->TxDescArray[entry].status =
- (OWNbit | FSbit | LSbit) | ((skb->len > ETH_ZLEN) ?
- skb->len : ETH_ZLEN);
- else
- tp->TxDescArray[entry].status =
- (OWNbit | EORbit | FSbit | LSbit) |
- ((skb->len > ETH_ZLEN) ? skb->len : ETH_ZLEN);
+ tp->TxDescArray[entry].addr = cpu_to_le64(mapping);
+ tp->TxDescArray[entry].status = cpu_to_le32(OWNbit | FSbit |
+ LSbit | len | (EORbit * !((entry + 1) % NUM_TX_DESC)));
+
RTL_W8(TxPoll, 0x40); //set polling bit
dev->trans_start = jiffies;
tp->cur_tx++;
- }
+ } else
+ goto err_drop;
- spin_unlock_irq(&tp->lock);
if ((tp->cur_tx - NUM_TX_DESC) == tp->dirty_tx) {
netif_stop_queue(dev);
}
+out:
+ spin_unlock_irq(&tp->lock);
return 0;
+
+err_drop:
+ dev_kfree_skb(skb);
+err_update_stats:
+ tp->stats.tx_dropped++;
+ goto out;
}
static void
@@ -884,18 +1398,23 @@
while (tx_left > 0) {
int entry = dirty_tx % NUM_TX_DESC;
+ struct sk_buff *skb = tp->Tx_skbuff[entry];
+ u32 status;
- if ((tp->TxDescArray[entry].status & OWNbit) == 0) {
- struct sk_buff *skb = tp->Tx_skbuff[entry];
+ rmb();
+ status = le32_to_cpu(tp->TxDescArray[entry].status);
+ if (status & OWNbit)
+ break;
- tp->stats.tx_bytes += skb->len >= ETH_ZLEN ?
+ /* FIXME: is it really accurate for TxErr ? */
+ tp->stats.tx_bytes += skb->len >= ETH_ZLEN ?
skb->len : ETH_ZLEN;
- tp->stats.tx_packets++;
- dev_kfree_skb_irq(skb);
- tp->Tx_skbuff[entry] = NULL;
- dirty_tx++;
- tx_left--;
- }
+ tp->stats.tx_packets++;
+ rtl8169_unmap_tx_skb(tp->pci_dev, tp->Tx_skbuff + entry,
+ tp->TxDescArray + entry);
+ dev_kfree_skb_irq(skb);
+ dirty_tx++;
+ tx_left--;
}
if (tp->dirty_tx != dirty_tx) {
@@ -905,74 +1424,108 @@
}
}
+static inline int rtl8169_try_rx_copy(struct sk_buff **sk_buff, int pkt_size,
+ struct RxDesc *desc,
+ struct net_device *dev)
+{
+ int ret = -1;
+
+ if (pkt_size < rx_copybreak) {
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(pkt_size + 2);
+ if (skb) {
+ skb->dev = dev;
+ skb_reserve(skb, 2);
+ eth_copy_and_sum(skb, sk_buff[0]->tail, pkt_size, 0);
+ *sk_buff = skb;
+ rtl8169_return_to_asic(desc);
+ ret = 0;
+ }
+ }
+ return ret;
+}
+
static void
rtl8169_rx_interrupt(struct net_device *dev, struct rtl8169_private *tp,
void *ioaddr)
{
- int cur_rx;
- struct sk_buff *skb;
- int pkt_size = 0;
+ unsigned long cur_rx, rx_left;
+ int delta;
assert(dev != NULL);
assert(tp != NULL);
assert(ioaddr != NULL);
cur_rx = tp->cur_rx;
+ rx_left = NUM_RX_DESC + tp->dirty_rx - cur_rx;
- while ((tp->RxDescArray[cur_rx].status & OWNbit) == 0) {
+ while (rx_left > 0) {
+ int entry = cur_rx % NUM_RX_DESC;
+ u32 status;
- if (tp->RxDescArray[cur_rx].status & RxRES) {
+ rmb();
+ status = le32_to_cpu(tp->RxDescArray[entry].status);
+
+ if (status & OWNbit)
+ break;
+
+ if (status & RxRES) {
printk(KERN_INFO "%s: Rx ERROR!!!\n", dev->name);
tp->stats.rx_errors++;
- if (tp->RxDescArray[cur_rx].status & (RxRWT | RxRUNT))
+ if (status & (RxRWT | RxRUNT))
tp->stats.rx_length_errors++;
- if (tp->RxDescArray[cur_rx].status & RxCRC)
+ if (status & RxCRC)
tp->stats.rx_crc_errors++;
} else {
- pkt_size =
- (int) (tp->RxDescArray[cur_rx].
- status & 0x00001FFF) - 4;
- skb = dev_alloc_skb(pkt_size + 2);
- if (skb != NULL) {
- skb->dev = dev;
- skb_reserve(skb, 2); // 16 byte align the IP fields. //
- eth_copy_and_sum(skb, tp->RxBufferRing[cur_rx],
- pkt_size, 0);
- skb_put(skb, pkt_size);
- skb->protocol = eth_type_trans(skb, dev);
- netif_rx(skb);
-
- if (cur_rx == (NUM_RX_DESC - 1))
- tp->RxDescArray[cur_rx].status =
- (OWNbit | EORbit) + RX_BUF_SIZE;
- else
- tp->RxDescArray[cur_rx].status =
- OWNbit + RX_BUF_SIZE;
-
- tp->RxDescArray[cur_rx].buf_addr =
- virt_to_bus(tp->RxBufferRing[cur_rx]);
- dev->last_rx = jiffies;
- tp->stats.rx_bytes += pkt_size;
- tp->stats.rx_packets++;
- } else {
- printk(KERN_WARNING
- "%s: Memory squeeze, deferring packet.\n",
- dev->name);
- /* We should check that some rx space is free.
- If not, free one and mark stats->rx_dropped++. */
- tp->stats.rx_dropped++;
+ struct RxDesc *desc = tp->RxDescArray + entry;
+ struct sk_buff *skb = tp->Rx_skbuff[entry];
+ int pkt_size = (status & 0x00001FFF) - 4;
+
+ pci_dma_sync_single(tp->pci_dev, le64_to_cpu(desc->addr),
+ RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
+
+ if (rtl8169_try_rx_copy(&skb, pkt_size, desc, dev)) {
+ pci_unmap_single(tp->pci_dev, le64_to_cpu(desc->addr),
+ RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
+ tp->Rx_skbuff[entry] = NULL;
}
- }
- cur_rx = (cur_rx + 1) % NUM_RX_DESC;
+ skb_put(skb, pkt_size);
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+
+ dev->last_rx = jiffies;
+ tp->stats.rx_bytes += pkt_size;
+ tp->stats.rx_packets++;
+ }
+
+ cur_rx++;
+ rx_left--;
}
tp->cur_rx = cur_rx;
+
+ delta = rtl8169_rx_fill(tp, dev, tp->dirty_rx, tp->cur_rx);
+ if (delta > 0)
+ tp->dirty_rx += delta;
+ else if (delta < 0)
+ printk(KERN_INFO "%s: no Rx buffer allocated\n", dev->name);
+
+ /*
+ * FIXME: until there is periodic timer to try and refill the ring,
+ * a temporary shortage may definitely kill the Rx process.
+ * - disable the asic to try and avoid an overflow and kick it again
+ * after refill ?
+ * - how do others driver handle this condition (Uh oh...).
+ */
+ if (tp->dirty_rx + NUM_RX_DESC == tp->cur_rx)
+ printk(KERN_EMERG "%s: Rx buffers exhausted\n", dev->name);
}
/* The interrupt handler does all of the Rx thread work and cleans up after the Tx thread. */
-static void
+static irqreturn_t
rtl8169_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
{
struct net_device *dev = (struct net_device *) dev_instance;
@@ -980,14 +1533,16 @@
int boguscnt = max_interrupt_work;
void *ioaddr = tp->mmio_addr;
int status = 0;
+ int handled = 0;
do {
status = RTL_R16(IntrStatus);
- /* h/w no longer present (hotplug?) or major error, bail */
- if (status == 0xFFFF)
+ /* hotplug/major error/no more work/shared irq */
+ if ((status == 0xFFFF) || !status)
break;
+ handled = 1;
/*
if (status & RxUnderrun)
link_changed = RTL_R16 (CSCR) & CSCR_LinkChangeBit;
@@ -995,9 +1550,7 @@
RTL_W16(IntrStatus,
(status & RxFIFOOver) ? (status | RxOverflow) : status);
- if ((status &
- (SYSErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver
- | TxErr | TxOK | RxErr | RxOK)) == 0)
+ if (!(status & rtl8169_intr_mask))
break;
// Rx interrupt
@@ -1020,17 +1573,20 @@
/* Clear all interrupt sources. */
RTL_W16(IntrStatus, 0xffff);
}
+ return IRQ_RETVAL(handled);
}
static int
rtl8169_close(struct net_device *dev)
{
struct rtl8169_private *tp = dev->priv;
+ struct pci_dev *pdev = tp->pci_dev;
void *ioaddr = tp->mmio_addr;
- int i;
netif_stop_queue(dev);
+ rtl8169_delete_timer(dev);
+
spin_lock_irq(&tp->lock);
/* Stop the chip's Tx and Rx DMA processes. */
@@ -1049,16 +1605,15 @@
free_irq(dev->irq, dev);
rtl8169_tx_clear(tp);
- kfree(tp->TxDescArrays);
- kfree(tp->RxDescArrays);
- tp->TxDescArrays = NULL;
- tp->RxDescArrays = NULL;
+
+ rtl8169_rx_clear(tp);
+
+ pci_free_consistent(pdev, R8169_RX_RING_BYTES, tp->RxDescArray,
+ tp->RxPhyAddr);
+ pci_free_consistent(pdev, R8169_TX_RING_BYTES, tp->TxDescArray,
+ tp->TxPhyAddr);
tp->TxDescArray = NULL;
tp->RxDescArray = NULL;
- kfree(tp->RxBufferRings);
- for (i = 0; i < NUM_RX_DESC; i++) {
- tp->RxBufferRing[i] = NULL;
- }
return 0;
}
@@ -1112,11 +1667,25 @@
spin_unlock_irqrestore(&tp->lock, flags);
}
-struct net_device_stats *
-rtl8169_get_stats(struct net_device *dev)
+/**
+ * rtl8169_get_stats - Get rtl8169 read/write statistics
+ * @dev: The Ethernet Device to get statistics for
+ *
+ * Get TX/RX statistics for rtl8169
+ */
+static struct net_device_stats *rtl8169_get_stats(struct net_device *dev)
{
struct rtl8169_private *tp = dev->priv;
+ void *ioaddr = tp->mmio_addr;
+ unsigned long flags;
+ if (netif_running(dev)) {
+ spin_lock_irqsave(&tp->lock, flags);
+ tp->stats.rx_missed_errors += RTL_R32(RxMissed);
+ RTL_W32(RxMissed, 0);
+ spin_unlock_irqrestore(&tp->lock, flags);
+ }
+
return &tp->stats;
}
@@ -1125,8 +1694,10 @@
.id_table = rtl8169_pci_tbl,
.probe = rtl8169_init_one,
.remove = __devexit_p(rtl8169_remove_one),
- .suspend = NULL,
- .resume = NULL,
+#ifdef CONFIG_PM
+ .suspend = rtl8169_suspend,
+ .resume = rtl8169_resume,
+#endif
};
static int __init
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)