Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8f417ca9 authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'pci/aspm' into next

* pci/aspm:
  PCI/ASPM: Add comment about L1 substate latency
  PCI/ASPM: Configure L1 substate settings
  PCI/ASPM: Calculate and save the L1.2 timing parameters
  PCI/ASPM: Read and set up L1 substate capabilities
  PCI/ASPM: Add support for L1 substates
  PCI/ASPM: Add L1 substate capability structure register definitions
parents ca0ef7fe a142f4d3
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -71,6 +71,14 @@ config PCIEASPM_POWERSAVE
	  Enable PCI Express ASPM L0s and L1 where possible, even if the
	  BIOS did not.

config PCIEASPM_POWER_SUPERSAVE
	bool "Power Supersave"
	depends on PCIEASPM
	help
	  Same as PCIEASPM_POWERSAVE, except it also enables L1 substates where
	  possible. This would result in higher power savings while staying in L1
	  where the components support it.

config PCIEASPM_PERFORMANCE
	bool "Performance"
	depends on PCIEASPM
+278 −13
Original line number Diff line number Diff line
@@ -30,8 +30,29 @@
#define ASPM_STATE_L0S_UP	(1)	/* Upstream direction L0s state */
#define ASPM_STATE_L0S_DW	(2)	/* Downstream direction L0s state */
#define ASPM_STATE_L1		(4)	/* L1 state */
#define ASPM_STATE_L1_1		(8)	/* ASPM L1.1 state */
#define ASPM_STATE_L1_2		(0x10)	/* ASPM L1.2 state */
#define ASPM_STATE_L1_1_PCIPM	(0x20)	/* PCI PM L1.1 state */
#define ASPM_STATE_L1_2_PCIPM	(0x40)	/* PCI PM L1.2 state */
#define ASPM_STATE_L1_SS_PCIPM	(ASPM_STATE_L1_1_PCIPM | ASPM_STATE_L1_2_PCIPM)
#define ASPM_STATE_L1_2_MASK	(ASPM_STATE_L1_2 | ASPM_STATE_L1_2_PCIPM)
#define ASPM_STATE_L1SS		(ASPM_STATE_L1_1 | ASPM_STATE_L1_1_PCIPM |\
				 ASPM_STATE_L1_2_MASK)
#define ASPM_STATE_L0S		(ASPM_STATE_L0S_UP | ASPM_STATE_L0S_DW)
#define ASPM_STATE_ALL		(ASPM_STATE_L0S | ASPM_STATE_L1)
#define ASPM_STATE_ALL		(ASPM_STATE_L0S | ASPM_STATE_L1 |	\
				 ASPM_STATE_L1SS)

/*
 * When L1 substates are enabled, the LTR L1.2 threshold is a timing parameter
 * that decides whether L1.1 or L1.2 is entered (Refer PCIe spec for details).
 * Not sure is there is a way to "calculate" this on the fly, but maybe we
 * could turn it into a parameter in future.  This value has been taken from
 * the following files from Intel's coreboot (which is the only code I found
 * to have used this):
 * https://www.coreboot.org/pipermail/coreboot-gerrit/2015-March/021134.html
 * https://review.coreboot.org/#/c/8832/
 */
#define LTR_L1_2_THRESHOLD_BITS	((1 << 21) | (1 << 23) | (1 << 30))

struct aspm_latency {
	u32 l0s;			/* L0s latency (nsec) */
@@ -40,6 +61,7 @@ struct aspm_latency {

struct pcie_link_state {
	struct pci_dev *pdev;		/* Upstream component of the Link */
	struct pci_dev *downstream;	/* Downstream component, function 0 */
	struct pcie_link_state *root;	/* pointer to the root port link */
	struct pcie_link_state *parent;	/* pointer to the parent Link state */
	struct list_head sibling;	/* node in link_list */
@@ -47,11 +69,11 @@ struct pcie_link_state {
	struct list_head link;		/* node in parent's children list */

	/* ASPM state */
	u32 aspm_support:3;		/* Supported ASPM state */
	u32 aspm_enabled:3;		/* Enabled ASPM state */
	u32 aspm_capable:3;		/* Capable ASPM state with latency */
	u32 aspm_default:3;		/* Default ASPM state by BIOS */
	u32 aspm_disable:3;		/* Disabled ASPM state */
	u32 aspm_support:7;		/* Supported ASPM state */
	u32 aspm_enabled:7;		/* Enabled ASPM state */
	u32 aspm_capable:7;		/* Capable ASPM state with latency */
	u32 aspm_default:7;		/* Default ASPM state by BIOS */
	u32 aspm_disable:7;		/* Disabled ASPM state */

	/* Clock PM state */
	u32 clkpm_capable:1;		/* Clock PM capable? */
@@ -66,6 +88,14 @@ struct pcie_link_state {
	 * has one slot under it, so at most there are 8 functions.
	 */
	struct aspm_latency acceptable[8];

	/* L1 PM Substate info */
	struct {
		u32 up_cap_ptr;		/* L1SS cap ptr in upstream dev */
		u32 dw_cap_ptr;		/* L1SS cap ptr in downstream dev */
		u32 ctl1;		/* value to be programmed in ctl1 */
		u32 ctl2;		/* value to be programmed in ctl2 */
	} l1ss;
};

static int aspm_disabled, aspm_force;
@@ -76,11 +106,14 @@ static LIST_HEAD(link_list);
#define POLICY_DEFAULT 0	/* BIOS default setting */
#define POLICY_PERFORMANCE 1	/* high performance */
#define POLICY_POWERSAVE 2	/* high power saving */
#define POLICY_POWER_SUPERSAVE 3 /* possibly even more power saving */

#ifdef CONFIG_PCIEASPM_PERFORMANCE
static int aspm_policy = POLICY_PERFORMANCE;
#elif defined CONFIG_PCIEASPM_POWERSAVE
static int aspm_policy = POLICY_POWERSAVE;
#elif defined CONFIG_PCIEASPM_POWER_SUPERSAVE
static int aspm_policy = POLICY_POWER_SUPERSAVE;
#else
static int aspm_policy;
#endif
@@ -88,7 +121,8 @@ static int aspm_policy;
static const char *policy_str[] = {
	[POLICY_DEFAULT] = "default",
	[POLICY_PERFORMANCE] = "performance",
	[POLICY_POWERSAVE] = "powersave"
	[POLICY_POWERSAVE] = "powersave",
	[POLICY_POWER_SUPERSAVE] = "powersupersave"
};

#define LINK_RETRAIN_TIMEOUT HZ
@@ -101,6 +135,9 @@ static int policy_to_aspm_state(struct pcie_link_state *link)
		return 0;
	case POLICY_POWERSAVE:
		/* Enable ASPM L0s/L1 */
		return (ASPM_STATE_L0S | ASPM_STATE_L1);
	case POLICY_POWER_SUPERSAVE:
		/* Enable Everything */
		return ASPM_STATE_ALL;
	case POLICY_DEFAULT:
		return link->aspm_default;
@@ -115,7 +152,8 @@ static int policy_to_clkpm_state(struct pcie_link_state *link)
		/* Disable ASPM and Clock PM */
		return 0;
	case POLICY_POWERSAVE:
		/* Disable Clock PM */
	case POLICY_POWER_SUPERSAVE:
		/* Enable Clock PM */
		return 1;
	case POLICY_DEFAULT:
		return link->clkpm_default;
@@ -278,11 +316,33 @@ static u32 calc_l1_acceptable(u32 encoding)
	return (1000 << encoding);
}

/* Convert L1SS T_pwr encoding to usec */
static u32 calc_l1ss_pwron(struct pci_dev *pdev, u32 scale, u32 val)
{
	switch (scale) {
	case 0:
		return val * 2;
	case 1:
		return val * 10;
	case 2:
		return val * 100;
	}
	dev_err(&pdev->dev, "%s: Invalid T_PwrOn scale: %u\n",
		__func__, scale);
	return 0;
}

struct aspm_register_info {
	u32 support:2;
	u32 enabled:2;
	u32 latency_encoding_l0s;
	u32 latency_encoding_l1;

	/* L1 substates */
	u32 l1ss_cap_ptr;
	u32 l1ss_cap;
	u32 l1ss_ctl1;
	u32 l1ss_ctl2;
};

static void pcie_get_aspm_reg(struct pci_dev *pdev,
@@ -297,6 +357,22 @@ static void pcie_get_aspm_reg(struct pci_dev *pdev,
	info->latency_encoding_l1  = (reg32 & PCI_EXP_LNKCAP_L1EL) >> 15;
	pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &reg16);
	info->enabled = reg16 & PCI_EXP_LNKCTL_ASPMC;

	/* Read L1 PM substate capabilities */
	info->l1ss_cap = info->l1ss_ctl1 = info->l1ss_ctl2 = 0;
	info->l1ss_cap_ptr = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_L1SS);
	if (!info->l1ss_cap_ptr)
		return;
	pci_read_config_dword(pdev, info->l1ss_cap_ptr + PCI_L1SS_CAP,
			      &info->l1ss_cap);
	if (!(info->l1ss_cap & PCI_L1SS_CAP_L1_PM_SS)) {
		info->l1ss_cap = 0;
		return;
	}
	pci_read_config_dword(pdev, info->l1ss_cap_ptr + PCI_L1SS_CTL1,
			      &info->l1ss_ctl1);
	pci_read_config_dword(pdev, info->l1ss_cap_ptr + PCI_L1SS_CTL2,
			      &info->l1ss_ctl2);
}

static void pcie_aspm_check_latency(struct pci_dev *endpoint)
@@ -327,6 +403,14 @@ static void pcie_aspm_check_latency(struct pci_dev *endpoint)
		 * Check L1 latency.
		 * Every switch on the path to root complex need 1
		 * more microsecond for L1. Spec doesn't mention L0s.
		 *
		 * The exit latencies for L1 substates are not advertised
		 * by a device.  Since the spec also doesn't mention a way
		 * to determine max latencies introduced by enabling L1
		 * substates on the components, it is not clear how to do
		 * a L1 substate exit latency check.  We assume that the
		 * L1 exit latencies advertised by a device include L1
		 * substate latencies (and hence do not do any check).
		 */
		latency = max_t(u32, link->latency_up.l1, link->latency_dw.l1);
		if ((link->aspm_capable & ASPM_STATE_L1) &&
@@ -338,6 +422,60 @@ static void pcie_aspm_check_latency(struct pci_dev *endpoint)
	}
}

/*
 * The L1 PM substate capability is only implemented in function 0 in a
 * multi function device.
 */
static struct pci_dev *pci_function_0(struct pci_bus *linkbus)
{
	struct pci_dev *child;

	list_for_each_entry(child, &linkbus->devices, bus_list)
		if (PCI_FUNC(child->devfn) == 0)
			return child;
	return NULL;
}

/* Calculate L1.2 PM substate timing parameters */
static void aspm_calc_l1ss_info(struct pcie_link_state *link,
				struct aspm_register_info *upreg,
				struct aspm_register_info *dwreg)
{
	u32 val1, val2, scale1, scale2;

	link->l1ss.up_cap_ptr = upreg->l1ss_cap_ptr;
	link->l1ss.dw_cap_ptr = dwreg->l1ss_cap_ptr;
	link->l1ss.ctl1 = link->l1ss.ctl2 = 0;

	if (!(link->aspm_support & ASPM_STATE_L1_2_MASK))
		return;

	/* Choose the greater of the two T_cmn_mode_rstr_time */
	val1 = (upreg->l1ss_cap >> 8) & 0xFF;
	val2 = (upreg->l1ss_cap >> 8) & 0xFF;
	if (val1 > val2)
		link->l1ss.ctl1 |= val1 << 8;
	else
		link->l1ss.ctl1 |= val2 << 8;
	/*
	 * We currently use LTR L1.2 threshold to be fixed constant picked from
	 * Intel's coreboot.
	 */
	link->l1ss.ctl1 |= LTR_L1_2_THRESHOLD_BITS;

	/* Choose the greater of the two T_pwr_on */
	val1 = (upreg->l1ss_cap >> 19) & 0x1F;
	scale1 = (upreg->l1ss_cap >> 16) & 0x03;
	val2 = (dwreg->l1ss_cap >> 19) & 0x1F;
	scale2 = (dwreg->l1ss_cap >> 16) & 0x03;

	if (calc_l1ss_pwron(link->pdev, scale1, val1) >
	    calc_l1ss_pwron(link->downstream, scale2, val2))
		link->l1ss.ctl2 |= scale1 | (val1 << 3);
	else
		link->l1ss.ctl2 |= scale2 | (val2 << 3);
}

static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
{
	struct pci_dev *child, *parent = link->pdev;
@@ -353,8 +491,9 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)

	/* Get upstream/downstream components' register state */
	pcie_get_aspm_reg(parent, &upreg);
	child = list_entry(linkbus->devices.next, struct pci_dev, bus_list);
	child = pci_function_0(linkbus);
	pcie_get_aspm_reg(child, &dwreg);
	link->downstream = child;

	/*
	 * If ASPM not supported, don't mess with the clocks and link,
@@ -397,6 +536,28 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
	link->latency_up.l1 = calc_l1_latency(upreg.latency_encoding_l1);
	link->latency_dw.l1 = calc_l1_latency(dwreg.latency_encoding_l1);

	/* Setup L1 substate */
	if (upreg.l1ss_cap & dwreg.l1ss_cap & PCI_L1SS_CAP_ASPM_L1_1)
		link->aspm_support |= ASPM_STATE_L1_1;
	if (upreg.l1ss_cap & dwreg.l1ss_cap & PCI_L1SS_CAP_ASPM_L1_2)
		link->aspm_support |= ASPM_STATE_L1_2;
	if (upreg.l1ss_cap & dwreg.l1ss_cap & PCI_L1SS_CAP_PCIPM_L1_1)
		link->aspm_support |= ASPM_STATE_L1_1_PCIPM;
	if (upreg.l1ss_cap & dwreg.l1ss_cap & PCI_L1SS_CAP_PCIPM_L1_2)
		link->aspm_support |= ASPM_STATE_L1_2_PCIPM;

	if (upreg.l1ss_ctl1 & dwreg.l1ss_ctl1 & PCI_L1SS_CTL1_ASPM_L1_1)
		link->aspm_enabled |= ASPM_STATE_L1_1;
	if (upreg.l1ss_ctl1 & dwreg.l1ss_ctl1 & PCI_L1SS_CTL1_ASPM_L1_2)
		link->aspm_enabled |= ASPM_STATE_L1_2;
	if (upreg.l1ss_ctl1 & dwreg.l1ss_ctl1 & PCI_L1SS_CTL1_PCIPM_L1_1)
		link->aspm_enabled |= ASPM_STATE_L1_1_PCIPM;
	if (upreg.l1ss_ctl1 & dwreg.l1ss_ctl1 & PCI_L1SS_CTL1_PCIPM_L1_2)
		link->aspm_enabled |= ASPM_STATE_L1_2_PCIPM;

	if (link->aspm_support & ASPM_STATE_L1SS)
		aspm_calc_l1ss_info(link, &upreg, &dwreg);

	/* Save default state */
	link->aspm_default = link->aspm_enabled;

@@ -435,6 +596,92 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
	}
}

static void pci_clear_and_set_dword(struct pci_dev *pdev, int pos,
				    u32 clear, u32 set)
{
	u32 val;

	pci_read_config_dword(pdev, pos, &val);
	val &= ~clear;
	val |= set;
	pci_write_config_dword(pdev, pos, val);
}

/* Configure the ASPM L1 substates */
static void pcie_config_aspm_l1ss(struct pcie_link_state *link, u32 state)
{
	u32 val, enable_req;
	struct pci_dev *child = link->downstream, *parent = link->pdev;
	u32 up_cap_ptr = link->l1ss.up_cap_ptr;
	u32 dw_cap_ptr = link->l1ss.dw_cap_ptr;

	enable_req = (link->aspm_enabled ^ state) & state;

	/*
	 * Here are the rules specified in the PCIe spec for enabling L1SS:
	 * - When enabling L1.x, enable bit at parent first, then at child
	 * - When disabling L1.x, disable bit at child first, then at parent
	 * - When enabling ASPM L1.x, need to disable L1
	 *   (at child followed by parent).
	 * - The ASPM/PCIPM L1.2 must be disabled while programming timing
	 *   parameters
	 *
	 * To keep it simple, disable all L1SS bits first, and later enable
	 * what is needed.
	 */

	/* Disable all L1 substates */
	pci_clear_and_set_dword(child, dw_cap_ptr + PCI_L1SS_CTL1,
				PCI_L1SS_CTL1_L1SS_MASK, 0);
	pci_clear_and_set_dword(parent, up_cap_ptr + PCI_L1SS_CTL1,
				PCI_L1SS_CTL1_L1SS_MASK, 0);
	/*
	 * If needed, disable L1, and it gets enabled later
	 * in pcie_config_aspm_link().
	 */
	if (enable_req & (ASPM_STATE_L1_1 | ASPM_STATE_L1_2)) {
		pcie_capability_clear_and_set_word(child, PCI_EXP_LNKCTL,
						   PCI_EXP_LNKCTL_ASPM_L1, 0);
		pcie_capability_clear_and_set_word(parent, PCI_EXP_LNKCTL,
						   PCI_EXP_LNKCTL_ASPM_L1, 0);
	}

	if (enable_req & ASPM_STATE_L1_2_MASK) {

		/* Program T_pwr_on in both ports */
		pci_write_config_dword(parent, up_cap_ptr + PCI_L1SS_CTL2,
				       link->l1ss.ctl2);
		pci_write_config_dword(child, dw_cap_ptr + PCI_L1SS_CTL2,
				       link->l1ss.ctl2);

		/* Program T_cmn_mode in parent */
		pci_clear_and_set_dword(parent, up_cap_ptr + PCI_L1SS_CTL1,
					0xFF00, link->l1ss.ctl1);

		/* Program LTR L1.2 threshold in both ports */
		pci_clear_and_set_dword(parent,	dw_cap_ptr + PCI_L1SS_CTL1,
					0xE3FF0000, link->l1ss.ctl1);
		pci_clear_and_set_dword(child, dw_cap_ptr + PCI_L1SS_CTL1,
					0xE3FF0000, link->l1ss.ctl1);
	}

	val = 0;
	if (state & ASPM_STATE_L1_1)
		val |= PCI_L1SS_CTL1_ASPM_L1_1;
	if (state & ASPM_STATE_L1_2)
		val |= PCI_L1SS_CTL1_ASPM_L1_2;
	if (state & ASPM_STATE_L1_1_PCIPM)
		val |= PCI_L1SS_CTL1_PCIPM_L1_1;
	if (state & ASPM_STATE_L1_2_PCIPM)
		val |= PCI_L1SS_CTL1_PCIPM_L1_2;

	/* Enable what we need to enable */
	pci_clear_and_set_dword(parent, up_cap_ptr + PCI_L1SS_CTL1,
				PCI_L1SS_CAP_L1_PM_SS, val);
	pci_clear_and_set_dword(child, dw_cap_ptr + PCI_L1SS_CTL1,
				PCI_L1SS_CAP_L1_PM_SS, val);
}

static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 val)
{
	pcie_capability_clear_and_set_word(pdev, PCI_EXP_LNKCTL,
@@ -444,11 +691,23 @@ static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 val)
static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state)
{
	u32 upstream = 0, dwstream = 0;
	struct pci_dev *child, *parent = link->pdev;
	struct pci_dev *child = link->downstream, *parent = link->pdev;
	struct pci_bus *linkbus = parent->subordinate;

	/* Nothing to do if the link is already in the requested state */
	/* Enable only the states that were not explicitly disabled */
	state &= (link->aspm_capable & ~link->aspm_disable);

	/* Can't enable any substates if L1 is not enabled */
	if (!(state & ASPM_STATE_L1))
		state &= ~ASPM_STATE_L1SS;

	/* Spec says both ports must be in D0 before enabling PCI PM substates*/
	if (parent->current_state != PCI_D0 || child->current_state != PCI_D0) {
		state &= ~ASPM_STATE_L1_SS_PCIPM;
		state |= (link->aspm_enabled & ASPM_STATE_L1_SS_PCIPM);
	}

	/* Nothing to do if the link is already in the requested state */
	if (link->aspm_enabled == state)
		return;
	/* Convert ASPM state to upstream/downstream ASPM register state */
@@ -460,6 +719,10 @@ static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state)
		upstream |= PCI_EXP_LNKCTL_ASPM_L1;
		dwstream |= PCI_EXP_LNKCTL_ASPM_L1;
	}

	if (link->aspm_capable & ASPM_STATE_L1SS)
		pcie_config_aspm_l1ss(link, state);

	/*
	 * Spec 2.0 suggests all functions should be configured the
	 * same setting for ASPM. Enabling ASPM L1 should be done in
@@ -612,7 +875,8 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev)
	 * the BIOS's expectation, we'll do so once pci_enable_device() is
	 * called.
	 */
	if (aspm_policy != POLICY_POWERSAVE) {
	if (aspm_policy != POLICY_POWERSAVE &&
	    aspm_policy != POLICY_POWER_SUPERSAVE) {
		pcie_config_aspm_path(link);
		pcie_set_clkpm(link, policy_to_clkpm_state(link));
	}
@@ -712,7 +976,8 @@ void pcie_aspm_powersave_config_link(struct pci_dev *pdev)
	if (aspm_disabled || !link)
		return;

	if (aspm_policy != POLICY_POWERSAVE)
	if (aspm_policy != POLICY_POWERSAVE &&
	    aspm_policy != POLICY_POWER_SUPERSAVE)
		return;

	down_read(&pci_bus_sem);
+16 −0
Original line number Diff line number Diff line
@@ -682,6 +682,7 @@
#define PCI_EXT_CAP_ID_PMUX	0x1A	/* Protocol Multiplexing */
#define PCI_EXT_CAP_ID_PASID	0x1B	/* Process Address Space ID */
#define PCI_EXT_CAP_ID_DPC	0x1D	/* Downstream Port Containment */
#define PCI_EXT_CAP_ID_L1SS	0x1E	/* L1 PM Substates */
#define PCI_EXT_CAP_ID_PTM	0x1F	/* Precision Time Measurement */
#define PCI_EXT_CAP_ID_MAX	PCI_EXT_CAP_ID_PTM

@@ -985,4 +986,19 @@
#define  PCI_PTM_CTRL_ENABLE		0x00000001  /* PTM enable */
#define  PCI_PTM_CTRL_ROOT		0x00000002  /* Root select */

/* L1 PM Substates */
#define PCI_L1SS_CAP		    4	/* capability register */
#define  PCI_L1SS_CAP_PCIPM_L1_2	 1	/* PCI PM L1.2 Support */
#define  PCI_L1SS_CAP_PCIPM_L1_1	 2	/* PCI PM L1.1 Support */
#define  PCI_L1SS_CAP_ASPM_L1_2		 4	/* ASPM L1.2 Support */
#define  PCI_L1SS_CAP_ASPM_L1_1		 8	/* ASPM L1.1 Support */
#define  PCI_L1SS_CAP_L1_PM_SS		16	/* L1 PM Substates Support */
#define PCI_L1SS_CTL1		    8	/* Control Register 1 */
#define  PCI_L1SS_CTL1_PCIPM_L1_2	1	/* PCI PM L1.2 Enable */
#define  PCI_L1SS_CTL1_PCIPM_L1_1	2	/* PCI PM L1.1 Support */
#define  PCI_L1SS_CTL1_ASPM_L1_2	4	/* ASPM L1.2 Support */
#define  PCI_L1SS_CTL1_ASPM_L1_1	8	/* ASPM L1.1 Support */
#define  PCI_L1SS_CTL1_L1SS_MASK	0x0000000F
#define PCI_L1SS_CTL2		    0xC	/* Control Register 2 */

#endif /* LINUX_PCI_REGS_H */