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: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
db067ef342
|
@ -613,8 +613,13 @@ struct rtl8169_private {
|
|||
struct work_struct work;
|
||||
} wk;
|
||||
|
||||
spinlock_t config25_lock;
|
||||
spinlock_t mac_ocp_lock;
|
||||
|
||||
spinlock_t cfg9346_usage_lock;
|
||||
int cfg9346_usage_count;
|
||||
|
||||
unsigned supports_gmii:1;
|
||||
unsigned aspm_manageable:1;
|
||||
dma_addr_t counters_phys_addr;
|
||||
struct rtl8169_counters *counters;
|
||||
struct rtl8169_tc_offsets tc_offset;
|
||||
|
@ -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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
|
@ -675,6 +690,28 @@ static void rtl_pci_commit(struct rtl8169_private *tp)
|
|||
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)
|
||||
{
|
||||
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)
|
|||
(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))
|
||||
return;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
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))
|
||||
return 0;
|
||||
|
@ -865,12 +911,28 @@ static u16 r8168_mac_ocp_read(struct rtl8169_private *tp, u32 reg)
|
|||
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,
|
||||
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
|
||||
|
@ -1336,6 +1398,7 @@ static void __rtl8169_set_wol(struct rtl8169_private *tp, u32 wolopts)
|
|||
{ WAKE_MAGIC, Config3, MagicPacket }
|
||||
};
|
||||
unsigned int i, tmp = ARRAY_SIZE(cfg);
|
||||
unsigned long flags;
|
||||
u8 options;
|
||||
|
||||
rtl_unlock_config_regs(tp);
|
||||
|
@ -1354,12 +1417,14 @@ static void __rtl8169_set_wol(struct rtl8169_private *tp, u32 wolopts)
|
|||
r8168_mac_ocp_modify(tp, 0xc0b6, BIT(0), 0);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&tp->config25_lock, flags);
|
||||
for (i = 0; i < tmp; i++) {
|
||||
options = RTL_R8(tp, cfg[i].reg) & ~cfg[i].mask;
|
||||
if (wolopts & cfg[i].opt)
|
||||
options |= cfg[i].mask;
|
||||
RTL_W8(tp, cfg[i].reg, options);
|
||||
}
|
||||
spin_unlock_irqrestore(&tp->config25_lock, flags);
|
||||
|
||||
switch (tp->mac_version) {
|
||||
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)
|
|||
case RTL_GIGA_MAC_VER_34:
|
||||
case RTL_GIGA_MAC_VER_37:
|
||||
case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_63:
|
||||
options = RTL_R8(tp, Config2) & ~PME_SIGNAL;
|
||||
if (wolopts)
|
||||
options |= PME_SIGNAL;
|
||||
RTL_W8(tp, Config2, options);
|
||||
rtl_mod_config2(tp, 0, PME_SIGNAL);
|
||||
else
|
||||
rtl_mod_config2(tp, PME_SIGNAL, 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -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)
|
||||
{
|
||||
/* Don't enable ASPM in the chip if OS can't control ASPM */
|
||||
if (enable && tp->aspm_manageable) {
|
||||
RTL_W8(tp, Config5, RTL_R8(tp, Config5) | ASPM_en);
|
||||
RTL_W8(tp, Config2, RTL_R8(tp, Config2) | ClkReqEn);
|
||||
if (tp->mac_version < RTL_GIGA_MAC_VER_32)
|
||||
return;
|
||||
|
||||
if (enable) {
|
||||
rtl_mod_config5(tp, 0, ASPM_en);
|
||||
rtl_mod_config2(tp, 0, ClkReqEn);
|
||||
|
||||
switch (tp->mac_version) {
|
||||
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)
|
|||
break;
|
||||
}
|
||||
|
||||
RTL_W8(tp, Config2, RTL_R8(tp, Config2) & ~ClkReqEn);
|
||||
RTL_W8(tp, Config5, RTL_R8(tp, Config5) & ~ASPM_en);
|
||||
rtl_mod_config2(tp, ClkReqEn, 0);
|
||||
rtl_mod_config5(tp, ASPM_en, 0);
|
||||
}
|
||||
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
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)
|
|||
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)
|
||||
|
@ -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_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);
|
||||
}
|
||||
|
@ -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, DLLPR, RTL_R8(tp, DLLPR) | PFM_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);
|
||||
}
|
||||
|
@ -4510,6 +4575,10 @@ static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance)
|
|||
}
|
||||
|
||||
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);
|
||||
__napi_schedule(&tp->napi);
|
||||
}
|
||||
|
@ -4569,9 +4638,14 @@ static int rtl8169_poll(struct napi_struct *napi, int 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_unlock_config_regs(tp);
|
||||
rtl_hw_aspm_clkreq_enable(tp, true);
|
||||
rtl_lock_config_regs(tp);
|
||||
}
|
||||
|
||||
return work_done;
|
||||
}
|
||||
|
||||
|
@ -5145,16 +5219,6 @@ done:
|
|||
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)
|
||||
{
|
||||
struct rtl8169_private *tp;
|
||||
|
@ -5176,6 +5240,10 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||
tp->eee_adv = -1;
|
||||
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,
|
||||
struct pcpu_sw_netstats);
|
||||
if (!dev->tstats)
|
||||
|
@ -5222,19 +5290,6 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||
|
||||
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->cp_cmd = RTL_R16(tp, CPlusCmd) & CPCMD_MASK;
|
||||
|
|
Loading…
Reference in New Issue