Commit 90fb1a36 authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'pci/controller/vmd'

- Add pci_enable_link_state() to allow drivers to enable ASPM link state
  (Michael Bottini)

- Add quirk to enable all ASPM link states and program LTR for devices
  below VMD (David E. Box)

* pci/controller/vmd:
  PCI: vmd: Add quirk to configure PCIe ASPM and LTR
  PCI: vmd: Create feature grouping for client products
  PCI: vmd: Use PCI_VDEVICE in device list
  PCI/ASPM: Add pci_enable_link_state()
parents 69ed52be f492edb4
Loading
Loading
Loading
Loading
+71 −26
Original line number Diff line number Diff line
@@ -66,8 +66,23 @@ enum vmd_features {
	 * interrupt handling.
	 */
	VMD_FEAT_CAN_BYPASS_MSI_REMAP		= (1 << 4),

	/*
	 * Enable ASPM on the PCIE root ports and set the default LTR of the
	 * storage devices on platforms where these values are not configured by
	 * BIOS. This is needed for laptops, which require these settings for
	 * proper power management of the SoC.
	 */
	VMD_FEAT_BIOS_PM_QUIRK		= (1 << 5),
};

#define VMD_BIOS_PM_QUIRK_LTR	0x1003	/* 3145728 ns */

#define VMD_FEATS_CLIENT	(VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP |	\
				 VMD_FEAT_HAS_BUS_RESTRICTIONS |	\
				 VMD_FEAT_OFFSET_FIRST_VECTOR |		\
				 VMD_FEAT_BIOS_PM_QUIRK)

static DEFINE_IDA(vmd_instance_ida);

/*
@@ -709,6 +724,46 @@ static void vmd_copy_host_bridge_flags(struct pci_host_bridge *root_bridge,
	vmd_bridge->native_dpc = root_bridge->native_dpc;
}

/*
 * Enable ASPM and LTR settings on devices that aren't configured by BIOS.
 */
static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata)
{
	unsigned long features = *(unsigned long *)userdata;
	u16 ltr = VMD_BIOS_PM_QUIRK_LTR;
	u32 ltr_reg;
	int pos;

	if (!(features & VMD_FEAT_BIOS_PM_QUIRK))
		return 0;

	pci_enable_link_state(pdev, PCIE_LINK_STATE_ALL);

	pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_LTR);
	if (!pos)
		return 0;

	/*
	 * Skip if the max snoop LTR is non-zero, indicating BIOS has set it
	 * so the LTR quirk is not needed.
	 */
	pci_read_config_dword(pdev, pos + PCI_LTR_MAX_SNOOP_LAT, &ltr_reg);
	if (!!(ltr_reg & (PCI_LTR_VALUE_MASK | PCI_LTR_SCALE_MASK)))
		return 0;

	/*
	 * Set the default values to the maximum required by the platform to
	 * allow the deepest power management savings. Write as a DWORD where
	 * the lower word is the max snoop latency and the upper word is the
	 * max non-snoop latency.
	 */
	ltr_reg = (ltr << 16) | ltr;
	pci_write_config_dword(pdev, pos + PCI_LTR_MAX_SNOOP_LAT, ltr_reg);
	pci_info(pdev, "VMD: Default LTR value set by driver\n");

	return 0;
}

static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
{
	struct pci_sysdata *sd = &vmd->sysdata;
@@ -881,6 +936,8 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)

	pci_assign_unassigned_bus_resources(vmd->bus);

	pci_walk_bus(vmd->bus, vmd_pm_enable_quirk, &features);

	/*
	 * VMD root buses are virtual and don't return true on pci_is_pcie()
	 * and will fail pcie_bus_configure_settings() early. It can instead be
@@ -1017,36 +1074,24 @@ static int vmd_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(vmd_dev_pm_ops, vmd_suspend, vmd_resume);

static const struct pci_device_id vmd_ids[] = {
	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_VMD_201D),
	{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_VMD_201D),
		.driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP,},
	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_VMD_28C0),
	{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_VMD_28C0),
		.driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW |
				VMD_FEAT_HAS_BUS_RESTRICTIONS |
				VMD_FEAT_CAN_BYPASS_MSI_REMAP,},
	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x467f),
		.driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP |
				VMD_FEAT_HAS_BUS_RESTRICTIONS |
				VMD_FEAT_OFFSET_FIRST_VECTOR,},
	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x4c3d),
		.driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP |
				VMD_FEAT_HAS_BUS_RESTRICTIONS |
				VMD_FEAT_OFFSET_FIRST_VECTOR,},
	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa77f),
		.driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP |
				VMD_FEAT_HAS_BUS_RESTRICTIONS |
				VMD_FEAT_OFFSET_FIRST_VECTOR,},
	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x7d0b),
		.driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP |
				VMD_FEAT_HAS_BUS_RESTRICTIONS |
				VMD_FEAT_OFFSET_FIRST_VECTOR,},
	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xad0b),
		.driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP |
				VMD_FEAT_HAS_BUS_RESTRICTIONS |
				VMD_FEAT_OFFSET_FIRST_VECTOR,},
	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_VMD_9A0B),
		.driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP |
				VMD_FEAT_HAS_BUS_RESTRICTIONS |
				VMD_FEAT_OFFSET_FIRST_VECTOR,},
	{PCI_VDEVICE(INTEL, 0x467f),
		.driver_data = VMD_FEATS_CLIENT,},
	{PCI_VDEVICE(INTEL, 0x4c3d),
		.driver_data = VMD_FEATS_CLIENT,},
	{PCI_VDEVICE(INTEL, 0xa77f),
		.driver_data = VMD_FEATS_CLIENT,},
	{PCI_VDEVICE(INTEL, 0x7d0b),
		.driver_data = VMD_FEATS_CLIENT,},
	{PCI_VDEVICE(INTEL, 0xad0b),
		.driver_data = VMD_FEATS_CLIENT,},
	{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_VMD_9A0B),
		.driver_data = VMD_FEATS_CLIENT,},
	{0,}
};
MODULE_DEVICE_TABLE(pci, vmd_ids);
+54 −0
Original line number Diff line number Diff line
@@ -1181,6 +1181,60 @@ int pci_disable_link_state(struct pci_dev *pdev, int state)
}
EXPORT_SYMBOL(pci_disable_link_state);

/**
 * pci_enable_link_state - Clear and set the default device link state so that
 * the link may be allowed to enter the specified states. Note that if the
 * BIOS didn't grant ASPM control to the OS, this does nothing because we can't
 * touch the LNKCTL register. Also note that this does not enable states
 * disabled by pci_disable_link_state(). Return 0 or a negative errno.
 *
 * @pdev: PCI device
 * @state: Mask of ASPM link states to enable
 */
int pci_enable_link_state(struct pci_dev *pdev, int state)
{
	struct pcie_link_state *link = pcie_aspm_get_link(pdev);

	if (!link)
		return -EINVAL;
	/*
	 * A driver requested that ASPM be enabled on this device, but
	 * if we don't have permission to manage ASPM (e.g., on ACPI
	 * systems we have to observe the FADT ACPI_FADT_NO_ASPM bit and
	 * the _OSC method), we can't honor that request.
	 */
	if (aspm_disabled) {
		pci_warn(pdev, "can't override BIOS ASPM; OS doesn't have ASPM control\n");
		return -EPERM;
	}

	down_read(&pci_bus_sem);
	mutex_lock(&aspm_lock);
	link->aspm_default = 0;
	if (state & PCIE_LINK_STATE_L0S)
		link->aspm_default |= ASPM_STATE_L0S;
	if (state & PCIE_LINK_STATE_L1)
		/* L1 PM substates require L1 */
		link->aspm_default |= ASPM_STATE_L1 | ASPM_STATE_L1SS;
	if (state & PCIE_LINK_STATE_L1_1)
		link->aspm_default |= ASPM_STATE_L1_1;
	if (state & PCIE_LINK_STATE_L1_2)
		link->aspm_default |= ASPM_STATE_L1_2;
	if (state & PCIE_LINK_STATE_L1_1_PCIPM)
		link->aspm_default |= ASPM_STATE_L1_1_PCIPM;
	if (state & PCIE_LINK_STATE_L1_2_PCIPM)
		link->aspm_default |= ASPM_STATE_L1_2_PCIPM;
	pcie_config_aspm_link(link, policy_to_aspm_state(link));

	link->clkpm_default = (state & PCIE_LINK_STATE_CLKPM) ? 1 : 0;
	pcie_set_clkpm(link, policy_to_clkpm_state(link));
	mutex_unlock(&aspm_lock);
	up_read(&pci_bus_sem);

	return 0;
}
EXPORT_SYMBOL(pci_enable_link_state);

static int pcie_aspm_set_policy(const char *val,
				const struct kernel_param *kp)
{
+7 −0
Original line number Diff line number Diff line
@@ -1686,10 +1686,15 @@ extern bool pcie_ports_native;
#define PCIE_LINK_STATE_L1_2		BIT(4)
#define PCIE_LINK_STATE_L1_1_PCIPM	BIT(5)
#define PCIE_LINK_STATE_L1_2_PCIPM	BIT(6)
#define PCIE_LINK_STATE_ALL		(PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 |\
					 PCIE_LINK_STATE_CLKPM | PCIE_LINK_STATE_L1_1 |\
					 PCIE_LINK_STATE_L1_2 | PCIE_LINK_STATE_L1_1_PCIPM |\
					 PCIE_LINK_STATE_L1_2_PCIPM)

#ifdef CONFIG_PCIEASPM
int pci_disable_link_state(struct pci_dev *pdev, int state);
int pci_disable_link_state_locked(struct pci_dev *pdev, int state);
int pci_enable_link_state(struct pci_dev *pdev, int state);
void pcie_no_aspm(void);
bool pcie_aspm_support_enabled(void);
bool pcie_aspm_enabled(struct pci_dev *pdev);
@@ -1698,6 +1703,8 @@ static inline int pci_disable_link_state(struct pci_dev *pdev, int state)
{ return 0; }
static inline int pci_disable_link_state_locked(struct pci_dev *pdev, int state)
{ return 0; }
static inline int pci_enable_link_state(struct pci_dev *pdev, int state)
{ return 0; }
static inline void pcie_no_aspm(void) { }
static inline bool pcie_aspm_support_enabled(void) { return false; }
static inline bool pcie_aspm_enabled(struct pci_dev *pdev) { return false; }