Commit db067ef3 authored by David S. Miller's avatar David S. Miller

Merge branch 'r8169-disable-ASPM-during-NAPI-poll'

Heiner Kallweit says:

====================
r8169: disable ASPM during NAPI poll

This is a rework of ideas from Kai-Heng on how to avoid the known
ASPM issues whilst still allowing for a maximum of ASPM-related power
savings. As a prerequisite some locking is added first.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 8ca5a579 2ab19de6
...@@ -613,8 +613,13 @@ struct rtl8169_private { ...@@ -613,8 +613,13 @@ struct rtl8169_private {
struct work_struct work; struct work_struct work;
} wk; } wk;
spinlock_t config25_lock;
spinlock_t mac_ocp_lock;
spinlock_t cfg9346_usage_lock;
int cfg9346_usage_count;
unsigned supports_gmii:1; unsigned supports_gmii:1;
unsigned aspm_manageable:1;
dma_addr_t counters_phys_addr; dma_addr_t counters_phys_addr;
struct rtl8169_counters *counters; struct rtl8169_counters *counters;
struct rtl8169_tc_offsets tc_offset; struct rtl8169_tc_offsets tc_offset;
...@@ -661,12 +666,22 @@ static inline struct device *tp_to_dev(struct rtl8169_private *tp) ...@@ -661,12 +666,22 @@ static inline struct device *tp_to_dev(struct rtl8169_private *tp)
static void rtl_lock_config_regs(struct rtl8169_private *tp) static void rtl_lock_config_regs(struct rtl8169_private *tp)
{ {
RTL_W8(tp, Cfg9346, Cfg9346_Lock); unsigned long flags;
spin_lock_irqsave(&tp->cfg9346_usage_lock, flags);
if (!--tp->cfg9346_usage_count)
RTL_W8(tp, Cfg9346, Cfg9346_Lock);
spin_unlock_irqrestore(&tp->cfg9346_usage_lock, flags);
} }
static void rtl_unlock_config_regs(struct rtl8169_private *tp) static void rtl_unlock_config_regs(struct rtl8169_private *tp)
{ {
RTL_W8(tp, Cfg9346, Cfg9346_Unlock); unsigned long flags;
spin_lock_irqsave(&tp->cfg9346_usage_lock, flags);
if (!tp->cfg9346_usage_count++)
RTL_W8(tp, Cfg9346, Cfg9346_Unlock);
spin_unlock_irqrestore(&tp->cfg9346_usage_lock, flags);
} }
static void rtl_pci_commit(struct rtl8169_private *tp) static void rtl_pci_commit(struct rtl8169_private *tp)
...@@ -675,6 +690,28 @@ static void rtl_pci_commit(struct rtl8169_private *tp) ...@@ -675,6 +690,28 @@ static void rtl_pci_commit(struct rtl8169_private *tp)
RTL_R8(tp, ChipCmd); RTL_R8(tp, ChipCmd);
} }
static void rtl_mod_config2(struct rtl8169_private *tp, u8 clear, u8 set)
{
unsigned long flags;
u8 val;
spin_lock_irqsave(&tp->config25_lock, flags);
val = RTL_R8(tp, Config2);
RTL_W8(tp, Config2, (val & ~clear) | set);
spin_unlock_irqrestore(&tp->config25_lock, flags);
}
static void rtl_mod_config5(struct rtl8169_private *tp, u8 clear, u8 set)
{
unsigned long flags;
u8 val;
spin_lock_irqsave(&tp->config25_lock, flags);
val = RTL_R8(tp, Config5);
RTL_W8(tp, Config5, (val & ~clear) | set);
spin_unlock_irqrestore(&tp->config25_lock, flags);
}
static bool rtl_is_8125(struct rtl8169_private *tp) static bool rtl_is_8125(struct rtl8169_private *tp)
{ {
return tp->mac_version >= RTL_GIGA_MAC_VER_61; return tp->mac_version >= RTL_GIGA_MAC_VER_61;
...@@ -847,7 +884,7 @@ static int r8168_phy_ocp_read(struct rtl8169_private *tp, u32 reg) ...@@ -847,7 +884,7 @@ static int r8168_phy_ocp_read(struct rtl8169_private *tp, u32 reg)
(RTL_R32(tp, GPHY_OCP) & 0xffff) : -ETIMEDOUT; (RTL_R32(tp, GPHY_OCP) & 0xffff) : -ETIMEDOUT;
} }
static void r8168_mac_ocp_write(struct rtl8169_private *tp, u32 reg, u32 data) static void __r8168_mac_ocp_write(struct rtl8169_private *tp, u32 reg, u32 data)
{ {
if (rtl_ocp_reg_failure(reg)) if (rtl_ocp_reg_failure(reg))
return; return;
...@@ -855,7 +892,16 @@ static void r8168_mac_ocp_write(struct rtl8169_private *tp, u32 reg, u32 data) ...@@ -855,7 +892,16 @@ static void r8168_mac_ocp_write(struct rtl8169_private *tp, u32 reg, u32 data)
RTL_W32(tp, OCPDR, OCPAR_FLAG | (reg << 15) | data); RTL_W32(tp, OCPDR, OCPAR_FLAG | (reg << 15) | data);
} }
static u16 r8168_mac_ocp_read(struct rtl8169_private *tp, u32 reg) static void r8168_mac_ocp_write(struct rtl8169_private *tp, u32 reg, u32 data)
{
unsigned long flags;
spin_lock_irqsave(&tp->mac_ocp_lock, flags);
__r8168_mac_ocp_write(tp, reg, data);
spin_unlock_irqrestore(&tp->mac_ocp_lock, flags);
}
static u16 __r8168_mac_ocp_read(struct rtl8169_private *tp, u32 reg)
{ {
if (rtl_ocp_reg_failure(reg)) if (rtl_ocp_reg_failure(reg))
return 0; return 0;
...@@ -865,12 +911,28 @@ static u16 r8168_mac_ocp_read(struct rtl8169_private *tp, u32 reg) ...@@ -865,12 +911,28 @@ static u16 r8168_mac_ocp_read(struct rtl8169_private *tp, u32 reg)
return RTL_R32(tp, OCPDR); return RTL_R32(tp, OCPDR);
} }
static u16 r8168_mac_ocp_read(struct rtl8169_private *tp, u32 reg)
{
unsigned long flags;
u16 val;
spin_lock_irqsave(&tp->mac_ocp_lock, flags);
val = __r8168_mac_ocp_read(tp, reg);
spin_unlock_irqrestore(&tp->mac_ocp_lock, flags);
return val;
}
static void r8168_mac_ocp_modify(struct rtl8169_private *tp, u32 reg, u16 mask, static void r8168_mac_ocp_modify(struct rtl8169_private *tp, u32 reg, u16 mask,
u16 set) u16 set)
{ {
u16 data = r8168_mac_ocp_read(tp, reg); unsigned long flags;
u16 data;
r8168_mac_ocp_write(tp, reg, (data & ~mask) | set); spin_lock_irqsave(&tp->mac_ocp_lock, flags);
data = __r8168_mac_ocp_read(tp, reg);
__r8168_mac_ocp_write(tp, reg, (data & ~mask) | set);
spin_unlock_irqrestore(&tp->mac_ocp_lock, flags);
} }
/* Work around a hw issue with RTL8168g PHY, the quirk disables /* Work around a hw issue with RTL8168g PHY, the quirk disables
...@@ -1336,6 +1398,7 @@ static void __rtl8169_set_wol(struct rtl8169_private *tp, u32 wolopts) ...@@ -1336,6 +1398,7 @@ static void __rtl8169_set_wol(struct rtl8169_private *tp, u32 wolopts)
{ WAKE_MAGIC, Config3, MagicPacket } { WAKE_MAGIC, Config3, MagicPacket }
}; };
unsigned int i, tmp = ARRAY_SIZE(cfg); unsigned int i, tmp = ARRAY_SIZE(cfg);
unsigned long flags;
u8 options; u8 options;
rtl_unlock_config_regs(tp); rtl_unlock_config_regs(tp);
...@@ -1354,12 +1417,14 @@ static void __rtl8169_set_wol(struct rtl8169_private *tp, u32 wolopts) ...@@ -1354,12 +1417,14 @@ static void __rtl8169_set_wol(struct rtl8169_private *tp, u32 wolopts)
r8168_mac_ocp_modify(tp, 0xc0b6, BIT(0), 0); r8168_mac_ocp_modify(tp, 0xc0b6, BIT(0), 0);
} }
spin_lock_irqsave(&tp->config25_lock, flags);
for (i = 0; i < tmp; i++) { for (i = 0; i < tmp; i++) {
options = RTL_R8(tp, cfg[i].reg) & ~cfg[i].mask; options = RTL_R8(tp, cfg[i].reg) & ~cfg[i].mask;
if (wolopts & cfg[i].opt) if (wolopts & cfg[i].opt)
options |= cfg[i].mask; options |= cfg[i].mask;
RTL_W8(tp, cfg[i].reg, options); RTL_W8(tp, cfg[i].reg, options);
} }
spin_unlock_irqrestore(&tp->config25_lock, flags);
switch (tp->mac_version) { switch (tp->mac_version) {
case RTL_GIGA_MAC_VER_02 ... RTL_GIGA_MAC_VER_06: case RTL_GIGA_MAC_VER_02 ... RTL_GIGA_MAC_VER_06:
...@@ -1371,10 +1436,10 @@ static void __rtl8169_set_wol(struct rtl8169_private *tp, u32 wolopts) ...@@ -1371,10 +1436,10 @@ static void __rtl8169_set_wol(struct rtl8169_private *tp, u32 wolopts)
case RTL_GIGA_MAC_VER_34: case RTL_GIGA_MAC_VER_34:
case RTL_GIGA_MAC_VER_37: case RTL_GIGA_MAC_VER_37:
case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_63: case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_63:
options = RTL_R8(tp, Config2) & ~PME_SIGNAL;
if (wolopts) if (wolopts)
options |= PME_SIGNAL; rtl_mod_config2(tp, 0, PME_SIGNAL);
RTL_W8(tp, Config2, options); else
rtl_mod_config2(tp, PME_SIGNAL, 0);
break; break;
default: default:
break; break;
...@@ -2675,10 +2740,12 @@ static void rtl_disable_exit_l1(struct rtl8169_private *tp) ...@@ -2675,10 +2740,12 @@ static void rtl_disable_exit_l1(struct rtl8169_private *tp)
static void rtl_hw_aspm_clkreq_enable(struct rtl8169_private *tp, bool enable) static void rtl_hw_aspm_clkreq_enable(struct rtl8169_private *tp, bool enable)
{ {
/* Don't enable ASPM in the chip if OS can't control ASPM */ if (tp->mac_version < RTL_GIGA_MAC_VER_32)
if (enable && tp->aspm_manageable) { return;
RTL_W8(tp, Config5, RTL_R8(tp, Config5) | ASPM_en);
RTL_W8(tp, Config2, RTL_R8(tp, Config2) | ClkReqEn); if (enable) {
rtl_mod_config5(tp, 0, ASPM_en);
rtl_mod_config2(tp, 0, ClkReqEn);
switch (tp->mac_version) { switch (tp->mac_version) {
case RTL_GIGA_MAC_VER_46 ... RTL_GIGA_MAC_VER_48: case RTL_GIGA_MAC_VER_46 ... RTL_GIGA_MAC_VER_48:
...@@ -2701,11 +2768,9 @@ static void rtl_hw_aspm_clkreq_enable(struct rtl8169_private *tp, bool enable) ...@@ -2701,11 +2768,9 @@ static void rtl_hw_aspm_clkreq_enable(struct rtl8169_private *tp, bool enable)
break; break;
} }
RTL_W8(tp, Config2, RTL_R8(tp, Config2) & ~ClkReqEn); rtl_mod_config2(tp, ClkReqEn, 0);
RTL_W8(tp, Config5, RTL_R8(tp, Config5) & ~ASPM_en); rtl_mod_config5(tp, ASPM_en, 0);
} }
udelay(10);
} }
static void rtl_set_fifo_size(struct rtl8169_private *tp, u16 rx_stat, static void rtl_set_fifo_size(struct rtl8169_private *tp, u16 rx_stat,
...@@ -2863,7 +2928,7 @@ static void rtl_hw_start_8168e_1(struct rtl8169_private *tp) ...@@ -2863,7 +2928,7 @@ static void rtl_hw_start_8168e_1(struct rtl8169_private *tp)
RTL_W32(tp, MISC, RTL_R32(tp, MISC) | TXPLA_RST); RTL_W32(tp, MISC, RTL_R32(tp, MISC) | TXPLA_RST);
RTL_W32(tp, MISC, RTL_R32(tp, MISC) & ~TXPLA_RST); RTL_W32(tp, MISC, RTL_R32(tp, MISC) & ~TXPLA_RST);
RTL_W8(tp, Config5, RTL_R8(tp, Config5) & ~Spi_en); rtl_mod_config5(tp, Spi_en, 0);
} }
static void rtl_hw_start_8168e_2(struct rtl8169_private *tp) static void rtl_hw_start_8168e_2(struct rtl8169_private *tp)
...@@ -2896,7 +2961,7 @@ static void rtl_hw_start_8168e_2(struct rtl8169_private *tp) ...@@ -2896,7 +2961,7 @@ static void rtl_hw_start_8168e_2(struct rtl8169_private *tp)
RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) | PFM_EN); RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) | PFM_EN);
RTL_W32(tp, MISC, RTL_R32(tp, MISC) | PWM_EN); RTL_W32(tp, MISC, RTL_R32(tp, MISC) | PWM_EN);
RTL_W8(tp, Config5, RTL_R8(tp, Config5) & ~Spi_en); rtl_mod_config5(tp, Spi_en, 0);
rtl_hw_aspm_clkreq_enable(tp, true); rtl_hw_aspm_clkreq_enable(tp, true);
} }
...@@ -2919,7 +2984,7 @@ static void rtl_hw_start_8168f(struct rtl8169_private *tp) ...@@ -2919,7 +2984,7 @@ static void rtl_hw_start_8168f(struct rtl8169_private *tp)
RTL_W8(tp, MCU, RTL_R8(tp, MCU) & ~NOW_IS_OOB); RTL_W8(tp, MCU, RTL_R8(tp, MCU) & ~NOW_IS_OOB);
RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) | PFM_EN); RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) | PFM_EN);
RTL_W32(tp, MISC, RTL_R32(tp, MISC) | PWM_EN); RTL_W32(tp, MISC, RTL_R32(tp, MISC) | PWM_EN);
RTL_W8(tp, Config5, RTL_R8(tp, Config5) & ~Spi_en); rtl_mod_config5(tp, Spi_en, 0);
rtl8168_config_eee_mac(tp); rtl8168_config_eee_mac(tp);
} }
...@@ -4510,6 +4575,10 @@ static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance) ...@@ -4510,6 +4575,10 @@ static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance)
} }
if (napi_schedule_prep(&tp->napi)) { if (napi_schedule_prep(&tp->napi)) {
rtl_unlock_config_regs(tp);
rtl_hw_aspm_clkreq_enable(tp, false);
rtl_lock_config_regs(tp);
rtl_irq_disable(tp); rtl_irq_disable(tp);
__napi_schedule(&tp->napi); __napi_schedule(&tp->napi);
} }
...@@ -4569,9 +4638,14 @@ static int rtl8169_poll(struct napi_struct *napi, int budget) ...@@ -4569,9 +4638,14 @@ static int rtl8169_poll(struct napi_struct *napi, int budget)
work_done = rtl_rx(dev, tp, budget); work_done = rtl_rx(dev, tp, budget);
if (work_done < budget && napi_complete_done(napi, work_done)) if (work_done < budget && napi_complete_done(napi, work_done)) {
rtl_irq_enable(tp); rtl_irq_enable(tp);
rtl_unlock_config_regs(tp);
rtl_hw_aspm_clkreq_enable(tp, true);
rtl_lock_config_regs(tp);
}
return work_done; return work_done;
} }
...@@ -5145,16 +5219,6 @@ static void rtl_init_mac_address(struct rtl8169_private *tp) ...@@ -5145,16 +5219,6 @@ static void rtl_init_mac_address(struct rtl8169_private *tp)
rtl_rar_set(tp, mac_addr); rtl_rar_set(tp, mac_addr);
} }
/* register is set if system vendor successfully tested ASPM 1.2 */
static bool rtl_aspm_is_safe(struct rtl8169_private *tp)
{
if (tp->mac_version >= RTL_GIGA_MAC_VER_61 &&
r8168_mac_ocp_read(tp, 0xc0b2) & 0xf)
return true;
return false;
}
static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{ {
struct rtl8169_private *tp; struct rtl8169_private *tp;
...@@ -5176,6 +5240,10 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) ...@@ -5176,6 +5240,10 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
tp->eee_adv = -1; tp->eee_adv = -1;
tp->ocp_base = OCP_STD_PHY_BASE; tp->ocp_base = OCP_STD_PHY_BASE;
spin_lock_init(&tp->cfg9346_usage_lock);
spin_lock_init(&tp->config25_lock);
spin_lock_init(&tp->mac_ocp_lock);
dev->tstats = devm_netdev_alloc_pcpu_stats(&pdev->dev, dev->tstats = devm_netdev_alloc_pcpu_stats(&pdev->dev,
struct pcpu_sw_netstats); struct pcpu_sw_netstats);
if (!dev->tstats) if (!dev->tstats)
...@@ -5222,19 +5290,6 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) ...@@ -5222,19 +5290,6 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
tp->mac_version = chipset; tp->mac_version = chipset;
/* Disable ASPM L1 as that cause random device stop working
* problems as well as full system hangs for some PCIe devices users.
* Chips from RTL8168h partially have issues with L1.2, but seem
* to work fine with L1 and L1.1.
*/
if (rtl_aspm_is_safe(tp))
rc = 0;
else if (tp->mac_version >= RTL_GIGA_MAC_VER_46)
rc = pci_disable_link_state(pdev, PCIE_LINK_STATE_L1_2);
else
rc = pci_disable_link_state(pdev, PCIE_LINK_STATE_L1);
tp->aspm_manageable = !rc;
tp->dash_type = rtl_check_dash(tp); tp->dash_type = rtl_check_dash(tp);
tp->cp_cmd = RTL_R16(tp, CPlusCmd) & CPCMD_MASK; tp->cp_cmd = RTL_R16(tp, CPlusCmd) & CPCMD_MASK;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment