+static void rtl_tx_performance_tweak(struct pci_dev *pdev, u16 force)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct rtl8169_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_disable_clock_request(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct rtl8169_private *tp = netdev_priv(dev);
+ int cap = tp->pcie_cap;
+
+ if (cap) {
+ u16 ctl;
+
+ pci_read_config_word(pdev, cap + PCI_EXP_LNKCTL, &ctl);
+ ctl &= ~PCI_EXP_LNKCTL_CLKREQ_EN;
+ pci_write_config_word(pdev, cap + PCI_EXP_LNKCTL, ctl);
+ }
+}
+
+#define R8168_CPCMD_QUIRK_MASK (\
+ EnableBist | \
+ Mac_dbgo_oe | \
+ Force_half_dup | \
+ Force_rxflow_en | \
+ Force_txflow_en | \
+ Cxpl_dbg_sel | \
+ ASF | \
+ PktCntrDisable | \
+ Mac_dbgo_sel)
+
+static void rtl_hw_start_8168bb(void __iomem *ioaddr, struct pci_dev *pdev)
+{
+ RTL_W8(Config3, RTL_R8(Config3) & ~Beacon_en);
+
+ RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) & ~R8168_CPCMD_QUIRK_MASK);
+
+ rtl_tx_performance_tweak(pdev,
+ (0x5 << MAX_READ_REQUEST_SHIFT) | PCI_EXP_DEVCTL_NOSNOOP_EN);
+}
+
+static void rtl_hw_start_8168bef(void __iomem *ioaddr, struct pci_dev *pdev)
+{
+ rtl_hw_start_8168bb(ioaddr, pdev);
+
+ RTL_W8(EarlyTxThres, EarlyTxThld);
+
+ RTL_W8(Config4, RTL_R8(Config4) & ~(1 << 0));
+}
+
+static void __rtl_hw_start_8168cp(void __iomem *ioaddr, struct pci_dev *pdev)
+{
+ RTL_W8(Config1, RTL_R8(Config1) | Speed_down);
+
+ RTL_W8(Config3, RTL_R8(Config3) & ~Beacon_en);
+
+ rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT);
+
+ rtl_disable_clock_request(pdev);
+
+ RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) & ~R8168_CPCMD_QUIRK_MASK);
+}
+
+static void rtl_hw_start_8168cp_1(void __iomem *ioaddr, struct pci_dev *pdev)
+{
+ static struct ephy_info e_info_8168cp[] = {
+ { 0x01, 0, 0x0001 },
+ { 0x02, 0x0800, 0x1000 },
+ { 0x03, 0, 0x0042 },
+ { 0x06, 0x0080, 0x0000 },
+ { 0x07, 0, 0x2000 }
+ };
+
+ rtl_csi_access_enable(ioaddr);
+
+ rtl_ephy_init(ioaddr, e_info_8168cp, ARRAY_SIZE(e_info_8168cp));
+
+ __rtl_hw_start_8168cp(ioaddr, pdev);
+}
+
+static void rtl_hw_start_8168cp_2(void __iomem *ioaddr, struct pci_dev *pdev)
+{
+ rtl_csi_access_enable(ioaddr);
+
+ RTL_W8(Config3, RTL_R8(Config3) & ~Beacon_en);
+
+ rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT);
+
+ RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) & ~R8168_CPCMD_QUIRK_MASK);
+}
+
+static void rtl_hw_start_8168cp_3(void __iomem *ioaddr, struct pci_dev *pdev)
+{
+ rtl_csi_access_enable(ioaddr);
+
+ RTL_W8(Config3, RTL_R8(Config3) & ~Beacon_en);
+
+ /* Magic. */
+ RTL_W8(DBG_REG, 0x20);
+
+ RTL_W8(EarlyTxThres, EarlyTxThld);
+
+ rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT);
+
+ RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) & ~R8168_CPCMD_QUIRK_MASK);
+}
+
+static void rtl_hw_start_8168c_1(void __iomem *ioaddr, struct pci_dev *pdev)
+{
+ static struct ephy_info e_info_8168c_1[] = {
+ { 0x02, 0x0800, 0x1000 },
+ { 0x03, 0, 0x0002 },
+ { 0x06, 0x0080, 0x0000 }
+ };
+
+ rtl_csi_access_enable(ioaddr);
+
+ RTL_W8(DBG_REG, 0x06 | FIX_NAK_1 | FIX_NAK_2);
+
+ rtl_ephy_init(ioaddr, e_info_8168c_1, ARRAY_SIZE(e_info_8168c_1));
+
+ __rtl_hw_start_8168cp(ioaddr, pdev);
+}
+
+static void rtl_hw_start_8168c_2(void __iomem *ioaddr, struct pci_dev *pdev)
+{
+ static struct ephy_info e_info_8168c_2[] = {
+ { 0x01, 0, 0x0001 },
+ { 0x03, 0x0400, 0x0220 }
+ };
+
+ rtl_csi_access_enable(ioaddr);
+
+ rtl_ephy_init(ioaddr, e_info_8168c_2, ARRAY_SIZE(e_info_8168c_2));
+
+ __rtl_hw_start_8168cp(ioaddr, pdev);
+}
+
+static void rtl_hw_start_8168c_3(void __iomem *ioaddr, struct pci_dev *pdev)
+{
+ rtl_hw_start_8168c_2(ioaddr, pdev);
+}
+
+static void rtl_hw_start_8168c_4(void __iomem *ioaddr, struct pci_dev *pdev)
+{
+ rtl_csi_access_enable(ioaddr);
+
+ __rtl_hw_start_8168cp(ioaddr, pdev);
+}
+
+static void rtl_hw_start_8168d(void __iomem *ioaddr, struct pci_dev *pdev)
+{
+ rtl_csi_access_enable(ioaddr);
+
+ rtl_disable_clock_request(pdev);
+
+ RTL_W8(EarlyTxThres, EarlyTxThld);
+
+ rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT);
+
+ RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) & ~R8168_CPCMD_QUIRK_MASK);
+}
+
+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;
+
+ RTL_W8(Cfg9346, Cfg9346_Unlock);
+
+ RTL_W8(EarlyTxThres, EarlyTxThld);
+
+ rtl_set_rx_max_size(ioaddr);
+
+ tp->cp_cmd |= RTL_R16(CPlusCmd) | PktCntrDisable | INTT_1;
+
+ RTL_W16(CPlusCmd, tp->cp_cmd);
+
+ 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_set_rx_mode(dev);
+
+ RTL_W32(TxConfig, (TX_DMA_BURST << TxDMAShift) |
+ (InterFrameGap << TxInterFrameGapShift));
+
+ RTL_R8(IntrMask);
+
+ switch (tp->mac_version) {
+ case RTL_GIGA_MAC_VER_11:
+ rtl_hw_start_8168bb(ioaddr, pdev);
+ break;
+
+ case RTL_GIGA_MAC_VER_12:
+ case RTL_GIGA_MAC_VER_17:
+ rtl_hw_start_8168bef(ioaddr, pdev);
+ break;
+
+ case RTL_GIGA_MAC_VER_18:
+ rtl_hw_start_8168cp_1(ioaddr, pdev);
+ break;
+
+ case RTL_GIGA_MAC_VER_19:
+ rtl_hw_start_8168c_1(ioaddr, pdev);
+ break;
+
+ case RTL_GIGA_MAC_VER_20:
+ rtl_hw_start_8168c_2(ioaddr, pdev);
+ break;
+
+ case RTL_GIGA_MAC_VER_21:
+ rtl_hw_start_8168c_3(ioaddr, pdev);
+ break;
+
+ case RTL_GIGA_MAC_VER_22:
+ rtl_hw_start_8168c_4(ioaddr, pdev);
+ break;
+
+ case RTL_GIGA_MAC_VER_23:
+ rtl_hw_start_8168cp_2(ioaddr, pdev);
+ break;
+
+ case RTL_GIGA_MAC_VER_24:
+ rtl_hw_start_8168cp_3(ioaddr, pdev);
+ break;
+
+ case RTL_GIGA_MAC_VER_25:
+ rtl_hw_start_8168d(ioaddr, pdev);
+ break;
+
+ default:
+ printk(KERN_ERR PFX "%s: unknown chipset (mac_version = %d).\n",
+ dev->name, tp->mac_version);
+ break;
+ }
+
+ RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb);
+
+ RTL_W8(Cfg9346, Cfg9346_Lock);
+
+ RTL_W16(MultiIntr, RTL_R16(MultiIntr) & 0xF000);
+
+ 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 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) ||
+ (tp->mac_version == RTL_GIGA_MAC_VER_16)) {
+ 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);
+
+ 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_set_rx_mode(dev);
+
+ RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb);
+
+ RTL_W16(MultiIntr, RTL_R16(MultiIntr) & 0xf000);
+
+ RTL_W16(IntrMask, tp->intr_event);