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

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
Loading
Loading
Loading
Loading
+100 −45
Original line number Diff line number Diff line
@@ -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)
{
	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)
{
	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 @@ static void rtl_init_mac_address(struct rtl8169_private *tp)
	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;