Add patches to detect Realtek 8102/8103 to r8101 driver.
authorArne Fitzenreiter <arne_f@ipfire.org>
Sun, 22 Nov 2009 15:01:54 +0000 (16:01 +0100)
committerArne Fitzenreiter <arne_f@ipfire.org>
Sun, 22 Nov 2009 15:01:54 +0000 (16:01 +0100)
lfs/r8101
src/r8101/r8101.c

index 94adc03..8f92a76 100644 (file)
--- a/lfs/r8101
+++ b/lfs/r8101
@@ -30,7 +30,7 @@ else
        VERSUFIX = ipfire
 endif
 
-VER        = ipfire-1
+VER        = ipfire-2
 
 THISAPP    = r8101
 DIR_APP    = $(DIR_SRC)/$(THISAPP)
index d4cebf4..57cda49 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * r8101.c: RealTek 8101 ethernet driver.
  *
- * This driver based on r8169 from Kernel 2.6.27.31
+ * This driver based on r8169 from Kernel 2.6.27.39 with SuSE patches
  * All pciids except for 8101 are removed becaue we want use
  * original realtek drivers except for 8101 because the
  * vendors r8101 produce a kernel panic.
@@ -67,6 +67,7 @@ static const int multicast_filter_limit = 32;
 /* MAC address length */
 #define MAC_ADDR_LEN   6
 
+#define MAX_READ_REQUEST_SHIFT 12
 #define RX_FIFO_THRESH 7       /* 7 means NO threshold, Rx buffer level before first PCI xfer. */
 #define RX_DMA_BURST   6       /* Maximum PCI burst, '6' is 1024 */
 #define TX_DMA_BURST   6       /* Maximum PCI burst, '6' is 1024 */
@@ -100,6 +101,10 @@ enum mac_version {
        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_07 = 0x07, // 8102e
+       RTL_GIGA_MAC_VER_08 = 0x08, // 8102e
+       RTL_GIGA_MAC_VER_09 = 0x09, // 8102e
+       RTL_GIGA_MAC_VER_10 = 0x0a, // 8101e
        RTL_GIGA_MAC_VER_11 = 0x0b, // 8168Bb
        RTL_GIGA_MAC_VER_12 = 0x0c, // 8168Be
        RTL_GIGA_MAC_VER_13 = 0x0d, // 8101Eb
@@ -124,8 +129,12 @@ static const struct {
        _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), // 8169SCd
-       _R("RTL8169sc/8110sc",  RTL_GIGA_MAC_VER_06, 0xff7e1880), // 8169SCe
+       _R("RTL8169sc/8110sc",  RTL_GIGA_MAC_VER_05, 0xff7e1880), // 8110SCd
+       _R("RTL8169sc/8110sc",  RTL_GIGA_MAC_VER_06, 0xff7e1880), // 8110SCe
+       _R("RTL8102e",          RTL_GIGA_MAC_VER_07, 0xff7e1880), // PCI-E
+       _R("RTL8102e",          RTL_GIGA_MAC_VER_08, 0xff7e1880), // PCI-E
+       _R("RTL8102e",          RTL_GIGA_MAC_VER_09, 0xff7e1880), // PCI-E
+       _R("RTL8101e",          RTL_GIGA_MAC_VER_10, 0xff7e1880), // PCI-E
        _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
@@ -201,9 +210,6 @@ enum rtl_registers {
        Config5         = 0x56,
        MultiIntr       = 0x5c,
        PHYAR           = 0x60,
-       TBICSR          = 0x64,
-       TBI_ANAR        = 0x68,
-       TBI_LPAR        = 0x6a,
        PHYstatus       = 0x6c,
        RxMaxSize       = 0xda,
        CPlusCmd        = 0xe0,
@@ -217,6 +223,32 @@ enum rtl_registers {
        FuncForceEvent  = 0xfc,
 };
 
+enum rtl8110_registers {
+       TBICSR                  = 0x64,
+       TBI_ANAR                = 0x68,
+       TBI_LPAR                = 0x6a,
+};
+
+enum rtl8168_8101_registers {
+       CSIDR                   = 0x64,
+       CSIAR                   = 0x68,
+#define        CSIAR_FLAG                      0x80000000
+#define        CSIAR_WRITE_CMD                 0x80000000
+#define        CSIAR_BYTE_ENABLE               0x0f
+#define        CSIAR_BYTE_ENABLE_SHIFT         12
+#define        CSIAR_ADDR_MASK                 0x0fff
+
+       EPHYAR                  = 0x80,
+#define        EPHYAR_FLAG                     0x80000000
+#define        EPHYAR_WRITE_CMD                0x80000000
+#define        EPHYAR_REG_MASK                 0x1f
+#define        EPHYAR_REG_SHIFT                16
+#define        EPHYAR_DATA_MASK                0xffff
+       DBG_REG                 = 0xd1,
+#define        FIX_NAK_1                       (1 << 4)
+#define        FIX_NAK_2                       (1 << 3)
+};
+
 enum rtl_register_content {
        /* InterruptStatusBits */
        SYSErr          = 0x8000,
@@ -270,7 +302,13 @@ enum rtl_register_content {
        TxDMAShift = 8, /* DMA burst value (0-7) is shift this many bits */
 
        /* Config1 register p.24 */
+       LEDS1           = (1 << 7),
+       LEDS0           = (1 << 6),
        MSIEnable       = (1 << 5),     /* Enable Message Signaled Interrupt */
+       Speed_down      = (1 << 4),
+       MEMMAP          = (1 << 3),
+       IOMAP           = (1 << 2),
+       VPD             = (1 << 1),
        PMEnable        = (1 << 0),     /* Power Management Enable */
 
        /* Config2 register p. 25 */
@@ -280,6 +318,7 @@ enum rtl_register_content {
        /* 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 */
+       Beacon_en       = (1 << 0),     /* 8168 only. Reserved in the 8168b */
 
        /* Config5 register p.27 */
        BWF             = (1 << 6),     /* Accept Broadcast wakeup frame */
@@ -297,7 +336,16 @@ enum rtl_register_content {
        TBINwComplete   = 0x01000000,
 
        /* CPlusCmd p.31 */
-       PktCntrDisable  = (1 << 7),     // 8168
+       EnableBist      = (1 << 15),    // 8168 8101
+       Mac_dbgo_oe     = (1 << 14),    // 8168 8101
+       Normal_mode     = (1 << 13),    // unused
+       Force_half_dup  = (1 << 12),    // 8168 8101
+       Force_rxflow_en = (1 << 11),    // 8168 8101
+       Force_txflow_en = (1 << 10),    // 8168 8101
+       Cxpl_dbg_sel    = (1 << 9),     // 8168 8101
+       ASF             = (1 << 8),     // 8168 8101
+       PktCntrDisable  = (1 << 7),     // 8168 8101
+       Mac_dbgo_sel    = 0x001c,       // 8168
        RxVlan          = (1 << 6),
        RxChkSum        = (1 << 5),
        PCIDAC          = (1 << 4),
@@ -433,6 +481,7 @@ struct rtl8101_private {
        void (*hw_start)(struct net_device *);
        unsigned int (*phy_reset_pending)(void __iomem *);
        unsigned int (*link_ok)(void __iomem *);
+       int pcie_cap;
        struct delayed_work task;
        unsigned features;
 
@@ -507,6 +556,11 @@ static int mdio_read(void __iomem *ioaddr, int reg_addr)
        return value;
 }
 
+static void mdio_patch(void __iomem *ioaddr, int reg_addr, int value)
+{
+       mdio_write(ioaddr, reg_addr, mdio_read(ioaddr, reg_addr) | value);
+}
+
 static void rtl_mdio_write(struct net_device *dev, int phy_id, int location,
                           int val)
 {
@@ -524,6 +578,72 @@ static int rtl_mdio_read(struct net_device *dev, int phy_id, int location)
        return mdio_read(ioaddr, location);
 }
 
+static void rtl_ephy_write(void __iomem *ioaddr, int reg_addr, int value)
+{
+       unsigned int i;
+
+       RTL_W32(EPHYAR, EPHYAR_WRITE_CMD | (value & EPHYAR_DATA_MASK) |
+               (reg_addr & EPHYAR_REG_MASK) << EPHYAR_REG_SHIFT);
+
+       for (i = 0; i < 100; i++) {
+               if (!(RTL_R32(EPHYAR) & EPHYAR_FLAG))
+                       break;
+               udelay(10);
+       }
+}
+
+static u16 rtl_ephy_read(void __iomem *ioaddr, int reg_addr)
+{
+       u16 value = 0xffff;
+       unsigned int i;
+
+       RTL_W32(EPHYAR, (reg_addr & EPHYAR_REG_MASK) << EPHYAR_REG_SHIFT);
+
+       for (i = 0; i < 100; i++) {
+               if (RTL_R32(EPHYAR) & EPHYAR_FLAG) {
+                       value = RTL_R32(EPHYAR) & EPHYAR_DATA_MASK;
+                       break;
+               }
+               udelay(10);
+       }
+
+       return value;
+}
+
+static void rtl_csi_write(void __iomem *ioaddr, int addr, int value)
+{
+       unsigned int i;
+
+       RTL_W32(CSIDR, value);
+       RTL_W32(CSIAR, CSIAR_WRITE_CMD | (addr & CSIAR_ADDR_MASK) |
+               CSIAR_BYTE_ENABLE << CSIAR_BYTE_ENABLE_SHIFT);
+
+       for (i = 0; i < 100; i++) {
+               if (!(RTL_R32(CSIAR) & CSIAR_FLAG))
+                       break;
+               udelay(10);
+       }
+}
+
+static u32 rtl_csi_read(void __iomem *ioaddr, int addr)
+{
+       u32 value = ~0x00;
+       unsigned int i;
+
+       RTL_W32(CSIAR, (addr & CSIAR_ADDR_MASK) |
+               CSIAR_BYTE_ENABLE << CSIAR_BYTE_ENABLE_SHIFT);
+
+       for (i = 0; i < 100; i++) {
+               if (RTL_R32(CSIAR) & CSIAR_FLAG) {
+                       value = RTL_R32(CSIDR);
+                       break;
+               }
+               udelay(10);
+       }
+
+       return value;
+}
+
 static void rtl8101_irq_mask_and_ack(void __iomem *ioaddr)
 {
        RTL_W16(IntrMask, 0x0000);
@@ -713,72 +833,83 @@ static int rtl8101_set_speed_xmii(struct net_device *dev,
 {
        struct rtl8101_private *tp = netdev_priv(dev);
        void __iomem *ioaddr = tp->mmio_addr;
-       int auto_nego, giga_ctrl;
-
-       auto_nego = mdio_read(ioaddr, MII_ADVERTISE);
-       auto_nego &= ~(ADVERTISE_10HALF | ADVERTISE_10FULL |
-                      ADVERTISE_100HALF | ADVERTISE_100FULL);
-       giga_ctrl = mdio_read(ioaddr, MII_CTRL1000);
-       giga_ctrl &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF);
+       int giga_ctrl, bmcr;
 
        if (autoneg == AUTONEG_ENABLE) {
+               int auto_nego;
+
+               auto_nego = mdio_read(ioaddr, MII_ADVERTISE);
                auto_nego |= (ADVERTISE_10HALF | ADVERTISE_10FULL |
                              ADVERTISE_100HALF | ADVERTISE_100FULL);
-               giga_ctrl |= ADVERTISE_1000FULL | ADVERTISE_1000HALF;
-       } else {
-               if (speed == SPEED_10)
-                       auto_nego |= ADVERTISE_10HALF | ADVERTISE_10FULL;
-               else if (speed == SPEED_100)
-                       auto_nego |= ADVERTISE_100HALF | ADVERTISE_100FULL;
-               else if (speed == SPEED_1000)
-                       giga_ctrl |= ADVERTISE_1000FULL | ADVERTISE_1000HALF;
-
-               if (duplex == DUPLEX_HALF)
-                       auto_nego &= ~(ADVERTISE_10FULL | ADVERTISE_100FULL);
+               auto_nego |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
 
-               if (duplex == DUPLEX_FULL)
-                       auto_nego &= ~(ADVERTISE_10HALF | ADVERTISE_100HALF);
-
-               /* This tweak comes straight from Realtek's driver. */
-               if ((speed == SPEED_100) && (duplex == DUPLEX_HALF) &&
-                   ((tp->mac_version == RTL_GIGA_MAC_VER_13) ||
-                    (tp->mac_version == RTL_GIGA_MAC_VER_16))) {
-                       auto_nego = ADVERTISE_100HALF | ADVERTISE_CSMA;
-               }
-       }
+               giga_ctrl = mdio_read(ioaddr, MII_CTRL1000);
+               giga_ctrl &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF);
 
-       /* The 8100e/8101e do Fast Ethernet only. */
-       if ((tp->mac_version == RTL_GIGA_MAC_VER_13) ||
-           (tp->mac_version == RTL_GIGA_MAC_VER_14) ||
-           (tp->mac_version == RTL_GIGA_MAC_VER_15) ||
-           (tp->mac_version == RTL_GIGA_MAC_VER_16)) {
-               if ((giga_ctrl & (ADVERTISE_1000FULL | ADVERTISE_1000HALF)) &&
-                   netif_msg_link(tp)) {
+               /* The 8100e/8101e/8102e do Fast Ethernet only. */
+               if ((tp->mac_version != RTL_GIGA_MAC_VER_07) &&
+                   (tp->mac_version != RTL_GIGA_MAC_VER_08) &&
+                   (tp->mac_version != RTL_GIGA_MAC_VER_09) &&
+                   (tp->mac_version != RTL_GIGA_MAC_VER_10) &&
+                   (tp->mac_version != RTL_GIGA_MAC_VER_13) &&
+                   (tp->mac_version != RTL_GIGA_MAC_VER_14) &&
+                   (tp->mac_version != RTL_GIGA_MAC_VER_15) &&
+                   (tp->mac_version != RTL_GIGA_MAC_VER_16)) {
+                       giga_ctrl |= ADVERTISE_1000FULL | ADVERTISE_1000HALF;
+               } else if (netif_msg_link(tp)) {
                        printk(KERN_INFO "%s: PHY does not support 1000Mbps.\n",
                               dev->name);
                }
-               giga_ctrl &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF);
-       }
 
-       auto_nego |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
+               bmcr = BMCR_ANENABLE | BMCR_ANRESTART;
+
+               if ((tp->mac_version == RTL_GIGA_MAC_VER_11) ||
+                   (tp->mac_version == RTL_GIGA_MAC_VER_12) ||
+                   (tp->mac_version >= RTL_GIGA_MAC_VER_17)) {
+                       /*
+                        * Wake up the PHY.
+                        * Vendor specific (0x1f) and reserved (0x0e) MII
+                        * registers.
+                        */
+                       mdio_write(ioaddr, 0x1f, 0x0000);
+                       mdio_write(ioaddr, 0x0e, 0x0000);
+               }
+
+               tp->phy_auto_nego_reg = auto_nego;
+
+               mdio_write(ioaddr, MII_ADVERTISE, auto_nego);
+               mdio_write(ioaddr, MII_CTRL1000, giga_ctrl);
+       } else {
+               giga_ctrl = 0;
+
+               if (speed == SPEED_10)
+                       bmcr = 0;
+               else if (speed == SPEED_100)
+                       bmcr = BMCR_SPEED100;
+               else
+                       return -EINVAL;
+
+               if (duplex == DUPLEX_FULL)
+                       bmcr |= BMCR_FULLDPLX;
 
-       if ((tp->mac_version == RTL_GIGA_MAC_VER_11) ||
-           (tp->mac_version == RTL_GIGA_MAC_VER_12) ||
-           (tp->mac_version >= RTL_GIGA_MAC_VER_17)) {
-               /*
-                * Wake up the PHY.
-                * Vendor specific (0x1f) and reserved (0x0e) MII registers.
-                */
                mdio_write(ioaddr, 0x1f, 0x0000);
-               mdio_write(ioaddr, 0x0e, 0x0000);
        }
 
-       tp->phy_auto_nego_reg = auto_nego;
        tp->phy_1000_ctrl_reg = giga_ctrl;
 
-       mdio_write(ioaddr, MII_ADVERTISE, auto_nego);
-       mdio_write(ioaddr, MII_CTRL1000, giga_ctrl);
-       mdio_write(ioaddr, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART);
+       mdio_write(ioaddr, MII_BMCR, bmcr);
+
+       if ((tp->mac_version == RTL_GIGA_MAC_VER_02) ||
+           (tp->mac_version == RTL_GIGA_MAC_VER_03)) {
+               if ((speed == SPEED_100) && (autoneg != AUTONEG_ENABLE)) {
+                       mdio_write(ioaddr, 0x17, 0x2138);
+                       mdio_write(ioaddr, 0x0e, 0x0260);
+               } else {
+                       mdio_write(ioaddr, 0x17, 0x2108);
+                       mdio_write(ioaddr, 0x0e, 0x0000);
+               }
+       }
+
        return 0;
 }
 
@@ -1129,8 +1260,17 @@ static void rtl8101_get_mac_version(struct rtl8101_private *tp,
                { 0x7c800000, 0x30000000,       RTL_GIGA_MAC_VER_11 },
 
                /* 8101 family. */
+               { 0x7cf00000, 0x34a00000,       RTL_GIGA_MAC_VER_09 },
+               { 0x7cf00000, 0x24a00000,       RTL_GIGA_MAC_VER_09 },
+               { 0x7cf00000, 0x34900000,       RTL_GIGA_MAC_VER_08 },
+               { 0x7cf00000, 0x24900000,       RTL_GIGA_MAC_VER_08 },
+               { 0x7cf00000, 0x34800000,       RTL_GIGA_MAC_VER_07 },
+               { 0x7cf00000, 0x24800000,       RTL_GIGA_MAC_VER_07 },
                { 0x7cf00000, 0x34000000,       RTL_GIGA_MAC_VER_13 },
+               { 0x7cf00000, 0x34300000,       RTL_GIGA_MAC_VER_10 },
                { 0x7cf00000, 0x34200000,       RTL_GIGA_MAC_VER_16 },
+               { 0x7c800000, 0x34800000,       RTL_GIGA_MAC_VER_09 },
+               { 0x7c800000, 0x24800000,       RTL_GIGA_MAC_VER_09 },
                { 0x7c800000, 0x34000000,       RTL_GIGA_MAC_VER_16 },
                /* FIXME: where did these entries come from ? -- FR */
                { 0xfc800000, 0x38800000,       RTL_GIGA_MAC_VER_15 },
@@ -1292,6 +1432,22 @@ static void rtl8168cx_hw_phy_config(void __iomem *ioaddr)
        rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init));
 }
 
+static void rtl8102e_hw_phy_config(void __iomem *ioaddr)
+{
+       struct phy_reg phy_reg_init[] = {
+               { 0x1f, 0x0003 },
+               { 0x08, 0x441d },
+               { 0x01, 0x9100 },
+               { 0x1f, 0x0000 }
+       };
+
+       mdio_write(ioaddr, 0x1f, 0x0000);
+       mdio_patch(ioaddr, 0x11, 1 << 12);
+       mdio_patch(ioaddr, 0x19, 1 << 13);
+
+       rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init));
+}
+
 static void rtl_hw_phy_config(struct net_device *dev)
 {
        struct rtl8101_private *tp = netdev_priv(dev);
@@ -1309,6 +1465,11 @@ static void rtl_hw_phy_config(struct net_device *dev)
        case RTL_GIGA_MAC_VER_04:
                rtl8101sb_hw_phy_config(ioaddr);
                break;
+       case RTL_GIGA_MAC_VER_07:
+       case RTL_GIGA_MAC_VER_08:
+       case RTL_GIGA_MAC_VER_09:
+               rtl8102e_hw_phy_config(ioaddr);
+               break;
        case RTL_GIGA_MAC_VER_18:
                rtl8168cp_hw_phy_config(ioaddr);
                break;
@@ -1692,6 +1853,10 @@ rtl8101_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                goto err_out_free_res_4;
        }
 
+       tp->pcie_cap = pci_find_capability(pdev, PCI_CAP_ID_EXP);
+       if (!tp->pcie_cap && netif_msg_probe(tp))
+               dev_info(&pdev->dev, "no PCI Express capability\n");
+
        RTL_W16(IntrMask, 0x0000);
 
        /* Soft reset the chip. */
@@ -2084,12 +2249,51 @@ static void rtl_hw_start_8169(struct net_device *dev)
        RTL_W16(IntrMask, tp->intr_event);
 }
 
+static void rtl_tx_performance_tweak(struct pci_dev *pdev, u16 force)
+{
+       struct net_device *dev = pci_get_drvdata(pdev);
+       struct rtl8101_private *tp = netdev_priv(dev);
+       int cap = tp->pcie_cap;
+
+       if (cap) {
+               u16 ctl;
+
+               pci_read_config_word(pdev, cap + PCI_EXP_DEVCTL, &ctl);
+               ctl = (ctl & ~PCI_EXP_DEVCTL_READRQ) | force;
+               pci_write_config_word(pdev, cap + PCI_EXP_DEVCTL, ctl);
+       }
+}
+
+static void rtl_csi_access_enable(void __iomem *ioaddr)
+{
+       u32 csi;
+
+       csi = rtl_csi_read(ioaddr, 0x070c) & 0x00ffffff;
+       rtl_csi_write(ioaddr, 0x070c, csi | 0x27000000);
+}
+
+struct ephy_info {
+       unsigned int offset;
+       u16 mask;
+       u16 bits;
+};
+
+static void rtl_ephy_init(void __iomem *ioaddr, struct ephy_info *e, int len)
+{
+       u16 w;
+
+       while (len-- > 0) {
+               w = (rtl_ephy_read(ioaddr, e->offset) & ~e->mask) | e->bits;
+               rtl_ephy_write(ioaddr, e->offset, w);
+               e++;
+       }
+}
+
 static void rtl_hw_start_8168(struct net_device *dev)
 {
        struct rtl8101_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);
 
@@ -2103,10 +2307,7 @@ static void rtl_hw_start_8168(struct net_device *dev)
 
        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_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT);
 
        RTL_W16(IntrMitigate, 0x5151);
 
@@ -2131,6 +2332,70 @@ static void rtl_hw_start_8168(struct net_device *dev)
        RTL_W16(IntrMask, tp->intr_event);
 }
 
+#define R810X_CPCMD_QUIRK_MASK (\
+       EnableBist | \
+       Mac_dbgo_oe | \
+       Force_half_dup | \
+       Force_half_dup | \
+       Force_txflow_en | \
+       Cxpl_dbg_sel | \
+       ASF | \
+       PktCntrDisable | \
+       PCIDAC | \
+       PCIMulRW)
+
+static void rtl_hw_start_8102e_1(void __iomem *ioaddr, struct pci_dev *pdev)
+{
+       static struct ephy_info e_info_8102e_1[] = {
+               { 0x01, 0, 0x6e65 },
+               { 0x02, 0, 0x091f },
+               { 0x03, 0, 0xc2f9 },
+               { 0x06, 0, 0xafb5 },
+               { 0x07, 0, 0x0e00 },
+               { 0x19, 0, 0xec80 },
+               { 0x01, 0, 0x2e65 },
+               { 0x01, 0, 0x6e65 }
+       };
+       u8 cfg1;
+
+       rtl_csi_access_enable(ioaddr);
+
+       RTL_W8(DBG_REG, FIX_NAK_1);
+
+       rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT);
+
+       RTL_W8(Config1,
+              LEDS1 | LEDS0 | Speed_down | MEMMAP | IOMAP | VPD | PMEnable);
+       RTL_W8(Config3, RTL_R8(Config3) & ~Beacon_en);
+
+       cfg1 = RTL_R8(Config1);
+       if ((cfg1 & LEDS0) && (cfg1 & LEDS1))
+               RTL_W8(Config1, cfg1 & ~LEDS0);
+
+       RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) & ~R810X_CPCMD_QUIRK_MASK);
+
+       rtl_ephy_init(ioaddr, e_info_8102e_1, ARRAY_SIZE(e_info_8102e_1));
+}
+
+static void rtl_hw_start_8102e_2(void __iomem *ioaddr, struct pci_dev *pdev)
+{
+       rtl_csi_access_enable(ioaddr);
+
+       rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT);
+
+       RTL_W8(Config1, MEMMAP | IOMAP | VPD | PMEnable);
+       RTL_W8(Config3, RTL_R8(Config3) & ~Beacon_en);
+
+       RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) & ~R810X_CPCMD_QUIRK_MASK);
+}
+
+static void rtl_hw_start_8102e_3(void __iomem *ioaddr, struct pci_dev *pdev)
+{
+       rtl_hw_start_8102e_2(ioaddr, pdev);
+
+       rtl_ephy_write(ioaddr, 0x03, 0xc2f9);
+}
+
 static void rtl_hw_start_8101(struct net_device *dev)
 {
        struct rtl8101_private *tp = netdev_priv(dev);
@@ -2139,8 +2404,26 @@ static void rtl_hw_start_8101(struct net_device *dev)
 
        if ((tp->mac_version == RTL_GIGA_MAC_VER_13) ||
            (tp->mac_version == RTL_GIGA_MAC_VER_16)) {
-               pci_write_config_word(pdev, 0x68, 0x00);
-               pci_write_config_word(pdev, 0x69, 0x08);
+               int cap = tp->pcie_cap;
+
+               if (cap) {
+                       pci_write_config_word(pdev, cap + PCI_EXP_DEVCTL,
+                                             PCI_EXP_DEVCTL_NOSNOOP_EN);
+               }
+       }
+
+       switch (tp->mac_version) {
+       case RTL_GIGA_MAC_VER_07:
+               rtl_hw_start_8102e_1(ioaddr, pdev);
+               break;
+
+       case RTL_GIGA_MAC_VER_08:
+               rtl_hw_start_8102e_3(ioaddr, pdev);
+               break;
+
+       case RTL_GIGA_MAC_VER_09:
+               rtl_hw_start_8102e_2(ioaddr, pdev);
+               break;
        }
 
        RTL_W8(Cfg9346, Cfg9346_Unlock);