]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/net/r8169.c
r8169: confusion between hardware and IP header alignment
[linux-2.6-omap-h63xx.git] / drivers / net / r8169.c
index 45876a854f002975893d95265798797bb4bc7f1d..a6fea192ffff1ba3a283c7b55b42e3e2fe09b5f4 100644 (file)
@@ -151,16 +151,17 @@ static const int multicast_filter_limit = 32;
 #define RTL_R32(reg)           ((unsigned long) readl (ioaddr + (reg)))
 
 enum mac_version {
-       RTL_GIGA_MAC_VER_01 = 0x00,
-       RTL_GIGA_MAC_VER_02 = 0x01,
-       RTL_GIGA_MAC_VER_03 = 0x02,
-       RTL_GIGA_MAC_VER_04 = 0x03,
-       RTL_GIGA_MAC_VER_05 = 0x04,
-       RTL_GIGA_MAC_VER_11 = 0x0b,
-       RTL_GIGA_MAC_VER_12 = 0x0c,
-       RTL_GIGA_MAC_VER_13 = 0x0d,
-       RTL_GIGA_MAC_VER_14 = 0x0e,
-       RTL_GIGA_MAC_VER_15 = 0x0f
+       RTL_GIGA_MAC_VER_01 = 0x01, // 8169
+       RTL_GIGA_MAC_VER_02 = 0x02, // 8169S
+       RTL_GIGA_MAC_VER_03 = 0x03, // 8110S
+       RTL_GIGA_MAC_VER_04 = 0x04, // 8169SB
+       RTL_GIGA_MAC_VER_05 = 0x05, // 8110SCd
+       RTL_GIGA_MAC_VER_06 = 0x06, // 8110SCe
+       RTL_GIGA_MAC_VER_11 = 0x0b, // 8168Bb
+       RTL_GIGA_MAC_VER_12 = 0x0c, // 8168Be 8168Bf
+       RTL_GIGA_MAC_VER_13 = 0x0d, // 8101Eb 8101Ec
+       RTL_GIGA_MAC_VER_14 = 0x0e, // 8101
+       RTL_GIGA_MAC_VER_15 = 0x0f  // 8101
 };
 
 enum phy_version {
@@ -180,11 +181,12 @@ static const struct {
        u8 mac_version;
        u32 RxConfigMask;       /* Clears the bits supported by this chip */
 } rtl_chip_info[] = {
-       _R("RTL8169",           RTL_GIGA_MAC_VER_01, 0xff7e1880),
-       _R("RTL8169s/8110s",    RTL_GIGA_MAC_VER_02, 0xff7e1880),
-       _R("RTL8169s/8110s",    RTL_GIGA_MAC_VER_03, 0xff7e1880),
-       _R("RTL8169sb/8110sb",  RTL_GIGA_MAC_VER_04, 0xff7e1880),
-       _R("RTL8169sc/8110sc",  RTL_GIGA_MAC_VER_05, 0xff7e1880),
+       _R("RTL8169",           RTL_GIGA_MAC_VER_01, 0xff7e1880), // 8169
+       _R("RTL8169s",          RTL_GIGA_MAC_VER_02, 0xff7e1880), // 8169S
+       _R("RTL8110s",          RTL_GIGA_MAC_VER_03, 0xff7e1880), // 8110S
+       _R("RTL8169sb/8110sb",  RTL_GIGA_MAC_VER_04, 0xff7e1880), // 8169SB
+       _R("RTL8169sc/8110sc",  RTL_GIGA_MAC_VER_05, 0xff7e1880), // 8110SCd
+       _R("RTL8169sc/8110sc",  RTL_GIGA_MAC_VER_06, 0xff7e1880), // 8110SCe
        _R("RTL8168b/8111b",    RTL_GIGA_MAC_VER_11, 0xff7e1880), // PCI-E
        _R("RTL8168b/8111b",    RTL_GIGA_MAC_VER_12, 0xff7e1880), // PCI-E
        _R("RTL8101e",          RTL_GIGA_MAC_VER_13, 0xff7e1880), // PCI-E 8139
@@ -199,20 +201,15 @@ enum cfg_version {
        RTL_CFG_2
 };
 
-static const struct {
-       unsigned int region;
-       unsigned int align;
-} rtl_cfg_info[] = {
-       [RTL_CFG_0] = { 1, NET_IP_ALIGN },
-       [RTL_CFG_1] = { 2, NET_IP_ALIGN },
-       [RTL_CFG_2] = { 2, 8 }
-};
+static void rtl_hw_start_8169(struct net_device *);
+static void rtl_hw_start_8168(struct net_device *);
+static void rtl_hw_start_8101(struct net_device *);
 
 static struct pci_device_id rtl8169_pci_tbl[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_REALTEK,     0x8129), 0, 0, RTL_CFG_0 },
        { PCI_DEVICE(PCI_VENDOR_ID_REALTEK,     0x8136), 0, 0, RTL_CFG_2 },
        { PCI_DEVICE(PCI_VENDOR_ID_REALTEK,     0x8167), 0, 0, RTL_CFG_0 },
-       { PCI_DEVICE(PCI_VENDOR_ID_REALTEK,     0x8168), 0, 0, RTL_CFG_2 },
+       { PCI_DEVICE(PCI_VENDOR_ID_REALTEK,     0x8168), 0, 0, RTL_CFG_1 },
        { PCI_DEVICE(PCI_VENDOR_ID_REALTEK,     0x8169), 0, 0, RTL_CFG_0 },
        { PCI_DEVICE(PCI_VENDOR_ID_DLINK,       0x4300), 0, 0, RTL_CFG_0 },
        { PCI_DEVICE(0x1259,                    0xc107), 0, 0, RTL_CFG_0 },
@@ -323,6 +320,10 @@ enum RTL8169_register_content {
        /* Config1 register p.24 */
        PMEnable        = (1 << 0),     /* Power Management Enable */
 
+       /* Config2 register p. 25 */
+       PCI_Clock_66MHz = 0x01,
+       PCI_Clock_33MHz = 0x00,
+
        /* Config3 register p.25 */
        MagicPacket     = (1 << 5),     /* Wake up when receives a Magic Packet */
        LinkUp          = (1 << 4),     /* Wake up when the cable connection is re-established */
@@ -343,10 +344,15 @@ enum RTL8169_register_content {
        TBINwComplete   = 0x01000000,
 
        /* CPlusCmd p.31 */
+       PktCntrDisable  = (1 << 7),     // 8168
        RxVlan          = (1 << 6),
        RxChkSum        = (1 << 5),
        PCIDAC          = (1 << 4),
        PCIMulRW        = (1 << 3),
+       INTT_0          = 0x0000,       // 8168
+       INTT_1          = 0x0001,       // 8168
+       INTT_2          = 0x0002,       // 8168
+       INTT_3          = 0x0003,       // 8168
 
        /* rtl8169_PHYstatus */
        TBI_Enable = 0x80,
@@ -446,6 +452,8 @@ struct rtl8169_private {
        unsigned rx_buf_sz;
        struct timer_list timer;
        u16 cp_cmd;
+       u16 intr_event;
+       u16 napi_event;
        u16 intr_mask;
        int phy_auto_nego_reg;
        int phy_1000_ctrl_reg;
@@ -455,6 +463,7 @@ struct rtl8169_private {
        int (*set_speed)(struct net_device *, u8 autoneg, u16 speed, u8 duplex);
        void (*get_settings)(struct net_device *, struct ethtool_cmd *);
        void (*phy_reset_enable)(void __iomem *);
+       void (*hw_start)(struct net_device *);
        unsigned int (*phy_reset_pending)(void __iomem *);
        unsigned int (*link_ok)(void __iomem *);
        struct delayed_work task;
@@ -478,9 +487,9 @@ static int rtl8169_open(struct net_device *dev);
 static int rtl8169_start_xmit(struct sk_buff *skb, struct net_device *dev);
 static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance);
 static int rtl8169_init_ring(struct net_device *dev);
-static void rtl8169_hw_start(struct net_device *dev);
+static void rtl_hw_start(struct net_device *dev);
 static int rtl8169_close(struct net_device *dev);
-static void rtl8169_set_rx_mode(struct net_device *dev);
+static void rtl_set_rx_mode(struct net_device *dev);
 static void rtl8169_tx_timeout(struct net_device *dev);
 static struct net_device_stats *rtl8169_get_stats(struct net_device *dev);
 static int rtl8169_rx_interrupt(struct net_device *, struct rtl8169_private *,
@@ -493,10 +502,6 @@ static void rtl8169_rx_clear(struct rtl8169_private *tp);
 static int rtl8169_poll(struct net_device *dev, int *budget);
 #endif
 
-static const u16 rtl8169_intr_mask =
-       SYSErr | LinkChg | RxOverflow | RxFIFOOver | TxErr | TxOK | RxErr | RxOK;
-static const u16 rtl8169_napi_event =
-       RxOK | RxOverflow | RxFIFOOver | TxOK | TxErr;
 static const unsigned int rtl8169_rx_config =
        (RX_FIFO_THRESH << RxCfgFIFOShift) | (RX_DMA_BURST << RxCfgDMAShift);
 
@@ -886,16 +891,6 @@ static void rtl8169_vlan_rx_register(struct net_device *dev,
        spin_unlock_irqrestore(&tp->lock, flags);
 }
 
-static void rtl8169_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
-{
-       struct rtl8169_private *tp = netdev_priv(dev);
-       unsigned long flags;
-
-       spin_lock_irqsave(&tp->lock, flags);
-       vlan_group_set_device(tp->vlgrp, vid, NULL);
-       spin_unlock_irqrestore(&tp->lock, flags);
-}
-
 static int rtl8169_rx_vlan_skb(struct rtl8169_private *tp, struct RxDesc *desc,
                               struct sk_buff *skb)
 {
@@ -1164,6 +1159,13 @@ static void rtl8169_write_gmii_reg_bit(void __iomem *ioaddr, int reg, int bitnum
 
 static void rtl8169_get_mac_version(struct rtl8169_private *tp, void __iomem *ioaddr)
 {
+       /*
+        * The driver currently handles the 8168Bf and the 8168Be identically
+        * but they can be identified more specifically through the test below
+        * if needed:
+        *
+        * (RTL_R32(TxConfig) & 0x700000) == 0x500000 ? 8168Bf : 8168Be
+        */
        const struct {
                u32 mask;
                int mac_version;
@@ -1173,6 +1175,7 @@ static void rtl8169_get_mac_version(struct rtl8169_private *tp, void __iomem *io
                { 0x34000000,   RTL_GIGA_MAC_VER_13 },
                { 0x30800000,   RTL_GIGA_MAC_VER_14 },
                { 0x30000000,   RTL_GIGA_MAC_VER_11 },
+               { 0x98000000,   RTL_GIGA_MAC_VER_06 },
                { 0x18000000,   RTL_GIGA_MAC_VER_05 },
                { 0x10000000,   RTL_GIGA_MAC_VER_04 },
                { 0x04000000,   RTL_GIGA_MAC_VER_03 },
@@ -1181,7 +1184,7 @@ static void rtl8169_get_mac_version(struct rtl8169_private *tp, void __iomem *io
        }, *p = mac_info;
        u32 reg;
 
-       reg = RTL_R32(TxConfig) & 0x7c800000;
+       reg = RTL_R32(TxConfig) & 0xfc800000;
        while ((reg & p->mask) != p->mask)
                p++;
        tp->mac_version = p->mac_version;
@@ -1429,10 +1432,10 @@ static void rtl8169_init_phy(struct net_device *dev, struct rtl8169_private *tp)
        dprintk("Set MAC Reg C+CR Offset 0x82h = 0x01h\n");
        RTL_W8(0x82, 0x01);
 
-       if (tp->mac_version < RTL_GIGA_MAC_VER_03) {
-               dprintk("Set PCI Latency=0x40\n");
-               pci_write_config_byte(tp->pci_dev, PCI_LATENCY_TIMER, 0x40);
-       }
+       pci_write_config_byte(tp->pci_dev, PCI_LATENCY_TIMER, 0x40);
+
+       if (tp->mac_version <= RTL_GIGA_MAC_VER_06)
+               pci_write_config_byte(tp->pci_dev, PCI_CACHE_LINE_SIZE, 0x08);
 
        if (tp->mac_version == RTL_GIGA_MAC_VER_02) {
                dprintk("Set MAC Reg C+CR Offset 0x82h = 0x01h\n");
@@ -1477,10 +1480,44 @@ static int rtl8169_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
        return -EOPNOTSUPP;
 }
 
+static const struct rtl_cfg_info {
+       void (*hw_start)(struct net_device *);
+       unsigned int region;
+       unsigned int align;
+       u16 intr_event;
+       u16 napi_event;
+} rtl_cfg_infos [] = {
+       [RTL_CFG_0] = {
+               .hw_start       = rtl_hw_start_8169,
+               .region         = 1,
+               .align          = 2,
+               .intr_event     = SYSErr | LinkChg | RxOverflow |
+                                 RxFIFOOver | TxErr | TxOK | RxOK | RxErr,
+               .napi_event     = RxFIFOOver | TxErr | TxOK | RxOK | RxOverflow
+       },
+       [RTL_CFG_1] = {
+               .hw_start       = rtl_hw_start_8168,
+               .region         = 2,
+               .align          = 8,
+               .intr_event     = SYSErr | LinkChg | RxOverflow |
+                                 TxErr | TxOK | RxOK | RxErr,
+               .napi_event     = TxErr | TxOK | RxOK | RxOverflow
+       },
+       [RTL_CFG_2] = {
+               .hw_start       = rtl_hw_start_8101,
+               .region         = 2,
+               .align          = 8,
+               .intr_event     = SYSErr | LinkChg | RxOverflow | PCSTimeout |
+                                 RxFIFOOver | TxErr | TxOK | RxOK | RxErr,
+               .napi_event     = RxFIFOOver | TxErr | TxOK | RxOK | RxOverflow
+       }
+};
+
 static int __devinit
 rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
-       const unsigned int region = rtl_cfg_info[ent->driver_data].region;
+       const struct rtl_cfg_info *cfg = rtl_cfg_infos + ent->driver_data;
+       const unsigned int region = cfg->region;
        struct rtl8169_private *tp;
        struct net_device *dev;
        void __iomem *ioaddr;
@@ -1657,7 +1694,7 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        SET_ETHTOOL_OPS(dev, &rtl8169_ethtool_ops);
        dev->stop = rtl8169_close;
        dev->tx_timeout = rtl8169_tx_timeout;
-       dev->set_multicast_list = rtl8169_set_rx_mode;
+       dev->set_multicast_list = rtl_set_rx_mode;
        dev->watchdog_timeo = RTL8169_TX_TIMEOUT;
        dev->irq = pdev->irq;
        dev->base_addr = (unsigned long) ioaddr;
@@ -1671,7 +1708,6 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 #ifdef CONFIG_R8169_VLAN
        dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
        dev->vlan_rx_register = rtl8169_vlan_rx_register;
-       dev->vlan_rx_kill_vid = rtl8169_vlan_rx_kill_vid;
 #endif
 
 #ifdef CONFIG_NET_POLL_CONTROLLER
@@ -1681,7 +1717,10 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        tp->intr_mask = 0xffff;
        tp->pci_dev = pdev;
        tp->mmio_addr = ioaddr;
-       tp->align = rtl_cfg_info[ent->driver_data].align;
+       tp->align = cfg->align;
+       tp->hw_start = cfg->hw_start;
+       tp->intr_event = cfg->intr_event;
+       tp->napi_event = cfg->napi_event;
 
        init_timer(&tp->timer);
        tp->timer.data = (unsigned long) dev;
@@ -1785,7 +1824,7 @@ static int rtl8169_open(struct net_device *dev)
        if (retval < 0)
                goto err_release_ring_2;
 
-       rtl8169_hw_start(dev);
+       rtl_hw_start(dev);
 
        rtl8169_request_timer(dev);
 
@@ -1816,7 +1855,7 @@ static void rtl8169_hw_reset(void __iomem *ioaddr)
        RTL_R8(ChipCmd);
 }
 
-static void rtl8169_set_rx_tx_config_registers(struct rtl8169_private *tp)
+static void rtl_set_rx_tx_config_registers(struct rtl8169_private *tp)
 {
        void __iomem *ioaddr = tp->mmio_addr;
        u32 cfg = rtl8169_rx_config;
@@ -1829,12 +1868,10 @@ static void rtl8169_set_rx_tx_config_registers(struct rtl8169_private *tp)
                (InterFrameGap << TxInterFrameGapShift));
 }
 
-static void rtl8169_hw_start(struct net_device *dev)
+static void rtl_hw_start(struct net_device *dev)
 {
        struct rtl8169_private *tp = netdev_priv(dev);
        void __iomem *ioaddr = tp->mmio_addr;
-       struct pci_dev *pdev = tp->pci_dev;
-       u16 cmd;
        u32 i;
 
        /* Soft reset the chip. */
@@ -1847,27 +1884,74 @@ static void rtl8169_hw_start(struct net_device *dev)
                msleep_interruptible(1);
        }
 
-       if (tp->mac_version == RTL_GIGA_MAC_VER_05) {
-               RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) | PCIMulRW);
-               pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 0x08);
-       }
+       tp->hw_start(dev);
 
-       if (tp->mac_version == RTL_GIGA_MAC_VER_13) {
-               pci_write_config_word(pdev, 0x68, 0x00);
-               pci_write_config_word(pdev, 0x69, 0x08);
-       }
+       netif_start_queue(dev);
+}
 
-       /* Undocumented stuff. */
-       if (tp->mac_version == RTL_GIGA_MAC_VER_05) {
-               /* Realtek's r1000_n.c driver uses '&& 0x01' here. Well... */
-               if ((RTL_R8(Config2) & 0x07) & 0x01)
-                       RTL_W32(0x7c, 0x0007ffff);
 
-               RTL_W32(0x7c, 0x0007ff00);
+static void rtl_set_rx_tx_desc_registers(struct rtl8169_private *tp,
+                                        void __iomem *ioaddr)
+{
+       /*
+        * Magic spell: some iop3xx ARM board needs the TxDescAddrHigh
+        * register to be written before TxDescAddrLow to work.
+        * Switching from MMIO to I/O access fixes the issue as well.
+        */
+       RTL_W32(TxDescStartAddrHigh, ((u64) tp->TxPhyAddr) >> 32);
+       RTL_W32(TxDescStartAddrLow, ((u64) tp->TxPhyAddr) & DMA_32BIT_MASK);
+       RTL_W32(RxDescAddrHigh, ((u64) tp->RxPhyAddr) >> 32);
+       RTL_W32(RxDescAddrLow, ((u64) tp->RxPhyAddr) & DMA_32BIT_MASK);
+}
+
+static u16 rtl_rw_cpluscmd(void __iomem *ioaddr)
+{
+       u16 cmd;
 
-               pci_read_config_word(pdev, PCI_COMMAND, &cmd);
-               cmd = cmd & 0xef;
-               pci_write_config_word(pdev, PCI_COMMAND, cmd);
+       cmd = RTL_R16(CPlusCmd);
+       RTL_W16(CPlusCmd, cmd);
+       return cmd;
+}
+
+static void rtl_set_rx_max_size(void __iomem *ioaddr)
+{
+       /* Low hurts. Let's disable the filtering. */
+       RTL_W16(RxMaxSize, 16383);
+}
+
+static void rtl8169_set_magic_reg(void __iomem *ioaddr, unsigned mac_version)
+{
+       struct {
+               u32 mac_version;
+               u32 clk;
+               u32 val;
+       } cfg2_info [] = {
+               { RTL_GIGA_MAC_VER_05, PCI_Clock_33MHz, 0x000fff00 }, // 8110SCd
+               { RTL_GIGA_MAC_VER_05, PCI_Clock_66MHz, 0x000fffff },
+               { RTL_GIGA_MAC_VER_06, PCI_Clock_33MHz, 0x00ffff00 }, // 8110SCe
+               { RTL_GIGA_MAC_VER_06, PCI_Clock_66MHz, 0x00ffffff }
+       }, *p = cfg2_info;
+       unsigned int i;
+       u32 clk;
+
+       clk = RTL_R8(Config2) & PCI_Clock_66MHz;
+       for (i = 0; i < ARRAY_SIZE(cfg2_info); i++) {
+               if ((p->mac_version == mac_version) && (p->clk == clk)) {
+                       RTL_W32(0x7c, p->val);
+                       break;
+               }
+       }
+}
+
+static void rtl_hw_start_8169(struct net_device *dev)
+{
+       struct rtl8169_private *tp = netdev_priv(dev);
+       void __iomem *ioaddr = tp->mmio_addr;
+       struct pci_dev *pdev = tp->pci_dev;
+
+       if (tp->mac_version == RTL_GIGA_MAC_VER_05) {
+               RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) | PCIMulRW);
+               pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 0x08);
        }
 
        RTL_W8(Cfg9346, Cfg9346_Unlock);
@@ -1879,19 +1963,11 @@ static void rtl8169_hw_start(struct net_device *dev)
 
        RTL_W8(EarlyTxThres, EarlyTxThld);
 
-       /* Low hurts. Let's disable the filtering. */
-       RTL_W16(RxMaxSize, 16383);
+       rtl_set_rx_max_size(ioaddr);
 
-       if ((tp->mac_version == RTL_GIGA_MAC_VER_01) ||
-           (tp->mac_version == RTL_GIGA_MAC_VER_02) ||
-           (tp->mac_version == RTL_GIGA_MAC_VER_03) ||
-           (tp->mac_version == RTL_GIGA_MAC_VER_04))
-               rtl8169_set_rx_tx_config_registers(tp);
-
-       cmd = RTL_R16(CPlusCmd);
-       RTL_W16(CPlusCmd, cmd);
+       rtl_set_rx_tx_config_registers(tp);
 
-       tp->cp_cmd |= cmd | PCIMulRW;
+       tp->cp_cmd |= rtl_rw_cpluscmd(ioaddr) | PCIMulRW;
 
        if ((tp->mac_version == RTL_GIGA_MAC_VER_02) ||
            (tp->mac_version == RTL_GIGA_MAC_VER_03)) {
@@ -1902,29 +1978,15 @@ static void rtl8169_hw_start(struct net_device *dev)
 
        RTL_W16(CPlusCmd, tp->cp_cmd);
 
+       rtl8169_set_magic_reg(ioaddr, tp->mac_version);
+
        /*
         * Undocumented corner. Supposedly:
         * (TxTimer << 12) | (TxPackets << 8) | (RxTimer << 4) | RxPackets
         */
        RTL_W16(IntrMitigate, 0x0000);
 
-       /*
-        * Magic spell: some iop3xx ARM board needs the TxDescAddrHigh
-        * register to be written before TxDescAddrLow to work.
-        * Switching from MMIO to I/O access fixes the issue as well.
-        */
-       RTL_W32(TxDescStartAddrHigh, ((u64) tp->TxPhyAddr >> 32));
-       RTL_W32(TxDescStartAddrLow, ((u64) tp->TxPhyAddr & DMA_32BIT_MASK));
-       RTL_W32(RxDescAddrHigh, ((u64) tp->RxPhyAddr >> 32));
-       RTL_W32(RxDescAddrLow, ((u64) tp->RxPhyAddr & DMA_32BIT_MASK));
-
-       if ((tp->mac_version != RTL_GIGA_MAC_VER_01) &&
-           (tp->mac_version != RTL_GIGA_MAC_VER_02) &&
-           (tp->mac_version != RTL_GIGA_MAC_VER_03) &&
-           (tp->mac_version != RTL_GIGA_MAC_VER_04)) {
-               RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb);
-               rtl8169_set_rx_tx_config_registers(tp);
-       }
+       rtl_set_rx_tx_desc_registers(tp, ioaddr);
 
        RTL_W8(Cfg9346, Cfg9346_Lock);
 
@@ -1933,15 +1995,107 @@ static void rtl8169_hw_start(struct net_device *dev)
 
        RTL_W32(RxMissed, 0);
 
-       rtl8169_set_rx_mode(dev);
+       rtl_set_rx_mode(dev);
 
        /* no early-rx interrupts */
        RTL_W16(MultiIntr, RTL_R16(MultiIntr) & 0xF000);
 
        /* Enable all known interrupts by setting the interrupt mask. */
-       RTL_W16(IntrMask, rtl8169_intr_mask);
+       RTL_W16(IntrMask, tp->intr_event);
 
-       netif_start_queue(dev);
+       RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb);
+}
+
+static void rtl_hw_start_8168(struct net_device *dev)
+{
+       struct rtl8169_private *tp = netdev_priv(dev);
+       void __iomem *ioaddr = tp->mmio_addr;
+       struct pci_dev *pdev = tp->pci_dev;
+       u8 ctl;
+
+       RTL_W8(Cfg9346, Cfg9346_Unlock);
+
+       RTL_W8(EarlyTxThres, EarlyTxThld);
+
+       rtl_set_rx_max_size(ioaddr);
+
+       rtl_set_rx_tx_config_registers(tp);
+
+       tp->cp_cmd |= RTL_R16(CPlusCmd) | PktCntrDisable | INTT_1;
+
+       RTL_W16(CPlusCmd, tp->cp_cmd);
+
+       /* Tx performance tweak. */
+       pci_read_config_byte(pdev, 0x69, &ctl);
+       ctl = (ctl & ~0x70) | 0x50;
+       pci_write_config_byte(pdev, 0x69, ctl);
+
+       RTL_W16(IntrMitigate, 0x5151);
+
+       /* Work around for RxFIFO overflow. */
+       if (tp->mac_version == RTL_GIGA_MAC_VER_11) {
+               tp->intr_event |= RxFIFOOver | PCSTimeout;
+               tp->intr_event &= ~RxOverflow;
+       }
+
+       rtl_set_rx_tx_desc_registers(tp, ioaddr);
+
+       RTL_W8(Cfg9346, Cfg9346_Lock);
+
+       RTL_R8(IntrMask);
+
+       RTL_W32(RxMissed, 0);
+
+       rtl_set_rx_mode(dev);
+
+       RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb);
+
+       RTL_W16(MultiIntr, RTL_R16(MultiIntr) & 0xF000);
+
+       RTL_W16(IntrMask, tp->intr_event);
+}
+
+static void rtl_hw_start_8101(struct net_device *dev)
+{
+       struct rtl8169_private *tp = netdev_priv(dev);
+       void __iomem *ioaddr = tp->mmio_addr;
+       struct pci_dev *pdev = tp->pci_dev;
+
+       if (tp->mac_version == RTL_GIGA_MAC_VER_13) {
+               pci_write_config_word(pdev, 0x68, 0x00);
+               pci_write_config_word(pdev, 0x69, 0x08);
+       }
+
+       RTL_W8(Cfg9346, Cfg9346_Unlock);
+
+       RTL_W8(EarlyTxThres, EarlyTxThld);
+
+       rtl_set_rx_max_size(ioaddr);
+
+       tp->cp_cmd |= rtl_rw_cpluscmd(ioaddr) | PCIMulRW;
+
+       RTL_W16(CPlusCmd, tp->cp_cmd);
+
+       RTL_W16(IntrMitigate, 0x0000);
+
+       rtl_set_rx_tx_desc_registers(tp, ioaddr);
+
+       RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb);
+       rtl_set_rx_tx_config_registers(tp);
+
+       RTL_W8(Cfg9346, Cfg9346_Lock);
+
+       RTL_R8(IntrMask);
+
+       RTL_W32(RxMissed, 0);
+
+       rtl_set_rx_mode(dev);
+
+       RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb);
+
+       RTL_W16(MultiIntr, RTL_R16(MultiIntr) & 0xf000);
+
+       RTL_W16(IntrMask, tp->intr_event);
 }
 
 static int rtl8169_change_mtu(struct net_device *dev, int new_mtu)
@@ -1967,7 +2121,7 @@ static int rtl8169_change_mtu(struct net_device *dev, int new_mtu)
 
        netif_poll_enable(dev);
 
-       rtl8169_hw_start(dev);
+       rtl_hw_start(dev);
 
        rtl8169_request_timer(dev);
 
@@ -2008,31 +2162,28 @@ static inline void rtl8169_map_to_asic(struct RxDesc *desc, dma_addr_t mapping,
        rtl8169_mark_to_asic(desc, rx_buf_sz);
 }
 
-static int rtl8169_alloc_rx_skb(struct pci_dev *pdev, struct sk_buff **sk_buff,
-                               struct RxDesc *desc, int rx_buf_sz,
-                               unsigned int align)
+static struct sk_buff *rtl8169_alloc_rx_skb(struct pci_dev *pdev,
+                                           struct net_device *dev,
+                                           struct RxDesc *desc, int rx_buf_sz,
+                                           unsigned int align)
 {
        struct sk_buff *skb;
        dma_addr_t mapping;
-       int ret = 0;
 
-       skb = dev_alloc_skb(rx_buf_sz + align);
+       skb = netdev_alloc_skb(dev, rx_buf_sz + align);
        if (!skb)
                goto err_out;
 
        skb_reserve(skb, (align - 1) & (unsigned long)skb->data);
-       *sk_buff = skb;
 
        mapping = pci_map_single(pdev, skb->data, rx_buf_sz,
                                 PCI_DMA_FROMDEVICE);
 
        rtl8169_map_to_asic(desc, mapping, rx_buf_sz);
-
 out:
-       return ret;
+       return skb;
 
 err_out:
-       ret = -ENOMEM;
        rtl8169_make_unusable_by_asic(desc);
        goto out;
 }
@@ -2054,16 +2205,22 @@ static u32 rtl8169_rx_fill(struct rtl8169_private *tp, struct net_device *dev,
 {
        u32 cur;
 
-       for (cur = start; end - cur > 0; cur++) {
-               int ret, i = cur % NUM_RX_DESC;
+       for (cur = start; end - cur != 0; cur++) {
+               struct sk_buff *skb;
+               unsigned int i = cur % NUM_RX_DESC;
+
+               WARN_ON((s32)(end - cur) < 0);
 
                if (tp->Rx_skbuff[i])
                        continue;
 
-               ret = rtl8169_alloc_rx_skb(tp->pci_dev, tp->Rx_skbuff + i,
-                       tp->RxDescArray + i, tp->rx_buf_sz, tp->align);
-               if (ret < 0)
+               skb = rtl8169_alloc_rx_skb(tp->pci_dev, dev,
+                                          tp->RxDescArray + i,
+                                          tp->rx_buf_sz, tp->align);
+               if (!skb)
                        break;
+
+               tp->Rx_skbuff[i] = skb;
        }
        return cur - start;
 }
@@ -2209,7 +2366,7 @@ static void rtl8169_reset_task(struct work_struct *work)
 
        if (tp->dirty_rx == tp->cur_rx) {
                rtl8169_init_ring_indexes(tp);
-               rtl8169_hw_start(dev);
+               rtl_hw_start(dev);
                netif_wake_queue(dev);
        } else {
                if (net_ratelimit()) {
@@ -2491,25 +2648,26 @@ static inline void rtl8169_rx_csum(struct sk_buff *skb, struct RxDesc *desc)
                skb->ip_summed = CHECKSUM_NONE;
 }
 
-static inline int rtl8169_try_rx_copy(struct sk_buff **sk_buff, int pkt_size,
-                                     struct RxDesc *desc, int rx_buf_sz,
-                                     unsigned int align)
+static inline bool rtl8169_try_rx_copy(struct sk_buff **sk_buff, int pkt_size,
+                                      struct pci_dev *pdev, dma_addr_t addr)
 {
-       int ret = -1;
+       struct sk_buff *skb;
+       bool done = false;
 
-       if (pkt_size < rx_copybreak) {
-               struct sk_buff *skb;
+       if (pkt_size >= rx_copybreak)
+               goto out;
 
-               skb = dev_alloc_skb(pkt_size + align);
-               if (skb) {
-                       skb_reserve(skb, (align - 1) & (unsigned long)skb->data);
-                       eth_copy_and_sum(skb, sk_buff[0]->data, pkt_size, 0);
-                       *sk_buff = skb;
-                       rtl8169_mark_to_asic(desc, rx_buf_sz);
-                       ret = 0;
-               }
-       }
-       return ret;
+       skb = dev_alloc_skb(pkt_size + NET_IP_ALIGN);
+       if (!skb)
+               goto out;
+
+       pci_dma_sync_single_for_cpu(pdev, addr, pkt_size, PCI_DMA_FROMDEVICE);
+       skb_reserve(skb, NET_IP_ALIGN);
+       skb_copy_from_linear_data(*sk_buff, skb->data, pkt_size);
+       *sk_buff = skb;
+       done = true;
+out:
+       return done;
 }
 
 static int
@@ -2555,9 +2713,9 @@ rtl8169_rx_interrupt(struct net_device *dev, struct rtl8169_private *tp,
                        rtl8169_mark_to_asic(desc, tp->rx_buf_sz);
                } else {
                        struct sk_buff *skb = tp->Rx_skbuff[entry];
+                       dma_addr_t addr = le64_to_cpu(desc->addr);
                        int pkt_size = (status & 0x00001FFF) - 4;
-                       void (*pci_action)(struct pci_dev *, dma_addr_t,
-                               size_t, int) = pci_dma_sync_single_for_device;
+                       struct pci_dev *pdev = tp->pci_dev;
 
                        /*
                         * The driver does not support incoming fragmented
@@ -2573,19 +2731,16 @@ rtl8169_rx_interrupt(struct net_device *dev, struct rtl8169_private *tp,
 
                        rtl8169_rx_csum(skb, desc);
 
-                       pci_dma_sync_single_for_cpu(tp->pci_dev,
-                               le64_to_cpu(desc->addr), tp->rx_buf_sz,
-                               PCI_DMA_FROMDEVICE);
-
-                       if (rtl8169_try_rx_copy(&skb, pkt_size, desc,
-                                               tp->rx_buf_sz, tp->align)) {
-                               pci_action = pci_unmap_single;
+                       if (rtl8169_try_rx_copy(&skb, pkt_size, pdev, addr)) {
+                               pci_dma_sync_single_for_device(pdev, addr,
+                                       pkt_size, PCI_DMA_FROMDEVICE);
+                               rtl8169_mark_to_asic(desc, tp->rx_buf_sz);
+                       } else {
+                               pci_unmap_single(pdev, addr, pkt_size,
+                                                PCI_DMA_FROMDEVICE);
                                tp->Rx_skbuff[entry] = NULL;
                        }
 
-                       pci_action(tp->pci_dev, le64_to_cpu(desc->addr),
-                                  tp->rx_buf_sz, PCI_DMA_FROMDEVICE);
-
                        skb_put(skb, pkt_size);
                        skb->protocol = eth_type_trans(skb, dev);
 
@@ -2596,6 +2751,13 @@ rtl8169_rx_interrupt(struct net_device *dev, struct rtl8169_private *tp,
                        tp->stats.rx_bytes += pkt_size;
                        tp->stats.rx_packets++;
                }
+
+               /* Work around for AMD plateform. */
+               if ((desc->opts2 & 0xfffe000) &&
+                   (tp->mac_version == RTL_GIGA_MAC_VER_05)) {
+                       desc->opts2 = 0;
+                       cur_rx++;
+               }
        }
 
        count = cur_rx - tp->cur_rx;
@@ -2648,9 +2810,17 @@ rtl8169_interrupt(int irq, void *dev_instance)
                RTL_W16(IntrStatus,
                        (status & RxFIFOOver) ? (status | RxOverflow) : status);
 
-               if (!(status & rtl8169_intr_mask))
+               if (!(status & tp->intr_event))
                        break;
 
+                /* Work around for rx fifo overflow */
+                if (unlikely(status & RxFIFOOver) &&
+                   (tp->mac_version == RTL_GIGA_MAC_VER_11)) {
+                       netif_stop_queue(dev);
+                       rtl8169_tx_timeout(dev);
+                       break;
+               }
+
                if (unlikely(status & SYSErr)) {
                        rtl8169_pcierr_interrupt(dev);
                        break;
@@ -2660,8 +2830,8 @@ rtl8169_interrupt(int irq, void *dev_instance)
                        rtl8169_check_link_status(dev, tp, ioaddr);
 
 #ifdef CONFIG_R8169_NAPI
-               RTL_W16(IntrMask, rtl8169_intr_mask & ~rtl8169_napi_event);
-               tp->intr_mask = ~rtl8169_napi_event;
+               RTL_W16(IntrMask, tp->intr_event & ~tp->napi_event);
+               tp->intr_mask = ~tp->napi_event;
 
                if (likely(netif_rx_schedule_prep(dev)))
                        __netif_rx_schedule(dev);
@@ -2718,7 +2888,7 @@ static int rtl8169_poll(struct net_device *dev, int *budget)
                 * write is safe - FR
                 */
                smp_wmb();
-               RTL_W16(IntrMask, rtl8169_intr_mask);
+               RTL_W16(IntrMask, tp->intr_event);
        }
 
        return (work_done >= work_to_do);
@@ -2800,8 +2970,7 @@ static int rtl8169_close(struct net_device *dev)
        return 0;
 }
 
-static void
-rtl8169_set_rx_mode(struct net_device *dev)
+static void rtl_set_rx_mode(struct net_device *dev)
 {
        struct rtl8169_private *tp = netdev_priv(dev);
        void __iomem *ioaddr = tp->mmio_addr;