mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 12:28:41 +08:00
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()
This commit is contained in:
commit
90fb1a3652
@ -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, <r_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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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; }
|
||||
|
Loading…
Reference in New Issue
Block a user