162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Enable PCIe link L0s/L1 state and Clock Power Management 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2007 Intel 662306a36Sopenharmony_ci * Copyright (C) Zhang Yanmin (yanmin.zhang@intel.com) 762306a36Sopenharmony_ci * Copyright (C) Shaohua Li (shaohua.li@intel.com) 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/math.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/moduleparam.h> 1462306a36Sopenharmony_ci#include <linux/pci.h> 1562306a36Sopenharmony_ci#include <linux/pci_regs.h> 1662306a36Sopenharmony_ci#include <linux/errno.h> 1762306a36Sopenharmony_ci#include <linux/pm.h> 1862306a36Sopenharmony_ci#include <linux/init.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/jiffies.h> 2162306a36Sopenharmony_ci#include <linux/delay.h> 2262306a36Sopenharmony_ci#include "../pci.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#ifdef MODULE_PARAM_PREFIX 2562306a36Sopenharmony_ci#undef MODULE_PARAM_PREFIX 2662306a36Sopenharmony_ci#endif 2762306a36Sopenharmony_ci#define MODULE_PARAM_PREFIX "pcie_aspm." 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* Note: those are not register definitions */ 3062306a36Sopenharmony_ci#define ASPM_STATE_L0S_UP (1) /* Upstream direction L0s state */ 3162306a36Sopenharmony_ci#define ASPM_STATE_L0S_DW (2) /* Downstream direction L0s state */ 3262306a36Sopenharmony_ci#define ASPM_STATE_L1 (4) /* L1 state */ 3362306a36Sopenharmony_ci#define ASPM_STATE_L1_1 (8) /* ASPM L1.1 state */ 3462306a36Sopenharmony_ci#define ASPM_STATE_L1_2 (0x10) /* ASPM L1.2 state */ 3562306a36Sopenharmony_ci#define ASPM_STATE_L1_1_PCIPM (0x20) /* PCI PM L1.1 state */ 3662306a36Sopenharmony_ci#define ASPM_STATE_L1_2_PCIPM (0x40) /* PCI PM L1.2 state */ 3762306a36Sopenharmony_ci#define ASPM_STATE_L1_SS_PCIPM (ASPM_STATE_L1_1_PCIPM | ASPM_STATE_L1_2_PCIPM) 3862306a36Sopenharmony_ci#define ASPM_STATE_L1_2_MASK (ASPM_STATE_L1_2 | ASPM_STATE_L1_2_PCIPM) 3962306a36Sopenharmony_ci#define ASPM_STATE_L1SS (ASPM_STATE_L1_1 | ASPM_STATE_L1_1_PCIPM |\ 4062306a36Sopenharmony_ci ASPM_STATE_L1_2_MASK) 4162306a36Sopenharmony_ci#define ASPM_STATE_L0S (ASPM_STATE_L0S_UP | ASPM_STATE_L0S_DW) 4262306a36Sopenharmony_ci#define ASPM_STATE_ALL (ASPM_STATE_L0S | ASPM_STATE_L1 | \ 4362306a36Sopenharmony_ci ASPM_STATE_L1SS) 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistruct pcie_link_state { 4662306a36Sopenharmony_ci struct pci_dev *pdev; /* Upstream component of the Link */ 4762306a36Sopenharmony_ci struct pci_dev *downstream; /* Downstream component, function 0 */ 4862306a36Sopenharmony_ci struct pcie_link_state *root; /* pointer to the root port link */ 4962306a36Sopenharmony_ci struct pcie_link_state *parent; /* pointer to the parent Link state */ 5062306a36Sopenharmony_ci struct list_head sibling; /* node in link_list */ 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci /* ASPM state */ 5362306a36Sopenharmony_ci u32 aspm_support:7; /* Supported ASPM state */ 5462306a36Sopenharmony_ci u32 aspm_enabled:7; /* Enabled ASPM state */ 5562306a36Sopenharmony_ci u32 aspm_capable:7; /* Capable ASPM state with latency */ 5662306a36Sopenharmony_ci u32 aspm_default:7; /* Default ASPM state by BIOS */ 5762306a36Sopenharmony_ci u32 aspm_disable:7; /* Disabled ASPM state */ 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci /* Clock PM state */ 6062306a36Sopenharmony_ci u32 clkpm_capable:1; /* Clock PM capable? */ 6162306a36Sopenharmony_ci u32 clkpm_enabled:1; /* Current Clock PM state */ 6262306a36Sopenharmony_ci u32 clkpm_default:1; /* Default Clock PM state by BIOS */ 6362306a36Sopenharmony_ci u32 clkpm_disable:1; /* Clock PM disabled */ 6462306a36Sopenharmony_ci}; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic int aspm_disabled, aspm_force; 6762306a36Sopenharmony_cistatic bool aspm_support_enabled = true; 6862306a36Sopenharmony_cistatic DEFINE_MUTEX(aspm_lock); 6962306a36Sopenharmony_cistatic LIST_HEAD(link_list); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci#define POLICY_DEFAULT 0 /* BIOS default setting */ 7262306a36Sopenharmony_ci#define POLICY_PERFORMANCE 1 /* high performance */ 7362306a36Sopenharmony_ci#define POLICY_POWERSAVE 2 /* high power saving */ 7462306a36Sopenharmony_ci#define POLICY_POWER_SUPERSAVE 3 /* possibly even more power saving */ 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci#ifdef CONFIG_PCIEASPM_PERFORMANCE 7762306a36Sopenharmony_cistatic int aspm_policy = POLICY_PERFORMANCE; 7862306a36Sopenharmony_ci#elif defined CONFIG_PCIEASPM_POWERSAVE 7962306a36Sopenharmony_cistatic int aspm_policy = POLICY_POWERSAVE; 8062306a36Sopenharmony_ci#elif defined CONFIG_PCIEASPM_POWER_SUPERSAVE 8162306a36Sopenharmony_cistatic int aspm_policy = POLICY_POWER_SUPERSAVE; 8262306a36Sopenharmony_ci#else 8362306a36Sopenharmony_cistatic int aspm_policy; 8462306a36Sopenharmony_ci#endif 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic const char *policy_str[] = { 8762306a36Sopenharmony_ci [POLICY_DEFAULT] = "default", 8862306a36Sopenharmony_ci [POLICY_PERFORMANCE] = "performance", 8962306a36Sopenharmony_ci [POLICY_POWERSAVE] = "powersave", 9062306a36Sopenharmony_ci [POLICY_POWER_SUPERSAVE] = "powersupersave" 9162306a36Sopenharmony_ci}; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* 9462306a36Sopenharmony_ci * The L1 PM substate capability is only implemented in function 0 in a 9562306a36Sopenharmony_ci * multi function device. 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_cistatic struct pci_dev *pci_function_0(struct pci_bus *linkbus) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct pci_dev *child; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci list_for_each_entry(child, &linkbus->devices, bus_list) 10262306a36Sopenharmony_ci if (PCI_FUNC(child->devfn) == 0) 10362306a36Sopenharmony_ci return child; 10462306a36Sopenharmony_ci return NULL; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic int policy_to_aspm_state(struct pcie_link_state *link) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci switch (aspm_policy) { 11062306a36Sopenharmony_ci case POLICY_PERFORMANCE: 11162306a36Sopenharmony_ci /* Disable ASPM and Clock PM */ 11262306a36Sopenharmony_ci return 0; 11362306a36Sopenharmony_ci case POLICY_POWERSAVE: 11462306a36Sopenharmony_ci /* Enable ASPM L0s/L1 */ 11562306a36Sopenharmony_ci return (ASPM_STATE_L0S | ASPM_STATE_L1); 11662306a36Sopenharmony_ci case POLICY_POWER_SUPERSAVE: 11762306a36Sopenharmony_ci /* Enable Everything */ 11862306a36Sopenharmony_ci return ASPM_STATE_ALL; 11962306a36Sopenharmony_ci case POLICY_DEFAULT: 12062306a36Sopenharmony_ci return link->aspm_default; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci return 0; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic int policy_to_clkpm_state(struct pcie_link_state *link) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci switch (aspm_policy) { 12862306a36Sopenharmony_ci case POLICY_PERFORMANCE: 12962306a36Sopenharmony_ci /* Disable ASPM and Clock PM */ 13062306a36Sopenharmony_ci return 0; 13162306a36Sopenharmony_ci case POLICY_POWERSAVE: 13262306a36Sopenharmony_ci case POLICY_POWER_SUPERSAVE: 13362306a36Sopenharmony_ci /* Enable Clock PM */ 13462306a36Sopenharmony_ci return 1; 13562306a36Sopenharmony_ci case POLICY_DEFAULT: 13662306a36Sopenharmony_ci return link->clkpm_default; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci return 0; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic void pcie_set_clkpm_nocheck(struct pcie_link_state *link, int enable) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct pci_dev *child; 14462306a36Sopenharmony_ci struct pci_bus *linkbus = link->pdev->subordinate; 14562306a36Sopenharmony_ci u32 val = enable ? PCI_EXP_LNKCTL_CLKREQ_EN : 0; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci list_for_each_entry(child, &linkbus->devices, bus_list) 14862306a36Sopenharmony_ci pcie_capability_clear_and_set_word(child, PCI_EXP_LNKCTL, 14962306a36Sopenharmony_ci PCI_EXP_LNKCTL_CLKREQ_EN, 15062306a36Sopenharmony_ci val); 15162306a36Sopenharmony_ci link->clkpm_enabled = !!enable; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic void pcie_set_clkpm(struct pcie_link_state *link, int enable) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci /* 15762306a36Sopenharmony_ci * Don't enable Clock PM if the link is not Clock PM capable 15862306a36Sopenharmony_ci * or Clock PM is disabled 15962306a36Sopenharmony_ci */ 16062306a36Sopenharmony_ci if (!link->clkpm_capable || link->clkpm_disable) 16162306a36Sopenharmony_ci enable = 0; 16262306a36Sopenharmony_ci /* Need nothing if the specified equals to current state */ 16362306a36Sopenharmony_ci if (link->clkpm_enabled == enable) 16462306a36Sopenharmony_ci return; 16562306a36Sopenharmony_ci pcie_set_clkpm_nocheck(link, enable); 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci int capable = 1, enabled = 1; 17162306a36Sopenharmony_ci u32 reg32; 17262306a36Sopenharmony_ci u16 reg16; 17362306a36Sopenharmony_ci struct pci_dev *child; 17462306a36Sopenharmony_ci struct pci_bus *linkbus = link->pdev->subordinate; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* All functions should have the same cap and state, take the worst */ 17762306a36Sopenharmony_ci list_for_each_entry(child, &linkbus->devices, bus_list) { 17862306a36Sopenharmony_ci pcie_capability_read_dword(child, PCI_EXP_LNKCAP, ®32); 17962306a36Sopenharmony_ci if (!(reg32 & PCI_EXP_LNKCAP_CLKPM)) { 18062306a36Sopenharmony_ci capable = 0; 18162306a36Sopenharmony_ci enabled = 0; 18262306a36Sopenharmony_ci break; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci pcie_capability_read_word(child, PCI_EXP_LNKCTL, ®16); 18562306a36Sopenharmony_ci if (!(reg16 & PCI_EXP_LNKCTL_CLKREQ_EN)) 18662306a36Sopenharmony_ci enabled = 0; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci link->clkpm_enabled = enabled; 18962306a36Sopenharmony_ci link->clkpm_default = enabled; 19062306a36Sopenharmony_ci link->clkpm_capable = capable; 19162306a36Sopenharmony_ci link->clkpm_disable = blacklist ? 1 : 0; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci/* 19562306a36Sopenharmony_ci * pcie_aspm_configure_common_clock: check if the 2 ends of a link 19662306a36Sopenharmony_ci * could use common clock. If they are, configure them to use the 19762306a36Sopenharmony_ci * common clock. That will reduce the ASPM state exit latency. 19862306a36Sopenharmony_ci */ 19962306a36Sopenharmony_cistatic void pcie_aspm_configure_common_clock(struct pcie_link_state *link) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci int same_clock = 1; 20262306a36Sopenharmony_ci u16 reg16, ccc, parent_old_ccc, child_old_ccc[8]; 20362306a36Sopenharmony_ci struct pci_dev *child, *parent = link->pdev; 20462306a36Sopenharmony_ci struct pci_bus *linkbus = parent->subordinate; 20562306a36Sopenharmony_ci /* 20662306a36Sopenharmony_ci * All functions of a slot should have the same Slot Clock 20762306a36Sopenharmony_ci * Configuration, so just check one function 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_ci child = list_entry(linkbus->devices.next, struct pci_dev, bus_list); 21062306a36Sopenharmony_ci BUG_ON(!pci_is_pcie(child)); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* Check downstream component if bit Slot Clock Configuration is 1 */ 21362306a36Sopenharmony_ci pcie_capability_read_word(child, PCI_EXP_LNKSTA, ®16); 21462306a36Sopenharmony_ci if (!(reg16 & PCI_EXP_LNKSTA_SLC)) 21562306a36Sopenharmony_ci same_clock = 0; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* Check upstream component if bit Slot Clock Configuration is 1 */ 21862306a36Sopenharmony_ci pcie_capability_read_word(parent, PCI_EXP_LNKSTA, ®16); 21962306a36Sopenharmony_ci if (!(reg16 & PCI_EXP_LNKSTA_SLC)) 22062306a36Sopenharmony_ci same_clock = 0; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* Port might be already in common clock mode */ 22362306a36Sopenharmony_ci pcie_capability_read_word(parent, PCI_EXP_LNKCTL, ®16); 22462306a36Sopenharmony_ci parent_old_ccc = reg16 & PCI_EXP_LNKCTL_CCC; 22562306a36Sopenharmony_ci if (same_clock && (reg16 & PCI_EXP_LNKCTL_CCC)) { 22662306a36Sopenharmony_ci bool consistent = true; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci list_for_each_entry(child, &linkbus->devices, bus_list) { 22962306a36Sopenharmony_ci pcie_capability_read_word(child, PCI_EXP_LNKCTL, 23062306a36Sopenharmony_ci ®16); 23162306a36Sopenharmony_ci if (!(reg16 & PCI_EXP_LNKCTL_CCC)) { 23262306a36Sopenharmony_ci consistent = false; 23362306a36Sopenharmony_ci break; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci if (consistent) 23762306a36Sopenharmony_ci return; 23862306a36Sopenharmony_ci pci_info(parent, "ASPM: current common clock configuration is inconsistent, reconfiguring\n"); 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci ccc = same_clock ? PCI_EXP_LNKCTL_CCC : 0; 24262306a36Sopenharmony_ci /* Configure downstream component, all functions */ 24362306a36Sopenharmony_ci list_for_each_entry(child, &linkbus->devices, bus_list) { 24462306a36Sopenharmony_ci pcie_capability_read_word(child, PCI_EXP_LNKCTL, ®16); 24562306a36Sopenharmony_ci child_old_ccc[PCI_FUNC(child->devfn)] = reg16 & PCI_EXP_LNKCTL_CCC; 24662306a36Sopenharmony_ci pcie_capability_clear_and_set_word(child, PCI_EXP_LNKCTL, 24762306a36Sopenharmony_ci PCI_EXP_LNKCTL_CCC, ccc); 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* Configure upstream component */ 25162306a36Sopenharmony_ci pcie_capability_clear_and_set_word(parent, PCI_EXP_LNKCTL, 25262306a36Sopenharmony_ci PCI_EXP_LNKCTL_CCC, ccc); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (pcie_retrain_link(link->pdev, true)) { 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* Training failed. Restore common clock configurations */ 25762306a36Sopenharmony_ci pci_err(parent, "ASPM: Could not configure common clock\n"); 25862306a36Sopenharmony_ci list_for_each_entry(child, &linkbus->devices, bus_list) 25962306a36Sopenharmony_ci pcie_capability_clear_and_set_word(child, PCI_EXP_LNKCTL, 26062306a36Sopenharmony_ci PCI_EXP_LNKCTL_CCC, 26162306a36Sopenharmony_ci child_old_ccc[PCI_FUNC(child->devfn)]); 26262306a36Sopenharmony_ci pcie_capability_clear_and_set_word(parent, PCI_EXP_LNKCTL, 26362306a36Sopenharmony_ci PCI_EXP_LNKCTL_CCC, parent_old_ccc); 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci/* Convert L0s latency encoding to ns */ 26862306a36Sopenharmony_cistatic u32 calc_l0s_latency(u32 lnkcap) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci u32 encoding = (lnkcap & PCI_EXP_LNKCAP_L0SEL) >> 12; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (encoding == 0x7) 27362306a36Sopenharmony_ci return (5 * 1000); /* > 4us */ 27462306a36Sopenharmony_ci return (64 << encoding); 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci/* Convert L0s acceptable latency encoding to ns */ 27862306a36Sopenharmony_cistatic u32 calc_l0s_acceptable(u32 encoding) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci if (encoding == 0x7) 28162306a36Sopenharmony_ci return -1U; 28262306a36Sopenharmony_ci return (64 << encoding); 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci/* Convert L1 latency encoding to ns */ 28662306a36Sopenharmony_cistatic u32 calc_l1_latency(u32 lnkcap) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci u32 encoding = (lnkcap & PCI_EXP_LNKCAP_L1EL) >> 15; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci if (encoding == 0x7) 29162306a36Sopenharmony_ci return (65 * 1000); /* > 64us */ 29262306a36Sopenharmony_ci return (1000 << encoding); 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci/* Convert L1 acceptable latency encoding to ns */ 29662306a36Sopenharmony_cistatic u32 calc_l1_acceptable(u32 encoding) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci if (encoding == 0x7) 29962306a36Sopenharmony_ci return -1U; 30062306a36Sopenharmony_ci return (1000 << encoding); 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci/* Convert L1SS T_pwr encoding to usec */ 30462306a36Sopenharmony_cistatic u32 calc_l12_pwron(struct pci_dev *pdev, u32 scale, u32 val) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci switch (scale) { 30762306a36Sopenharmony_ci case 0: 30862306a36Sopenharmony_ci return val * 2; 30962306a36Sopenharmony_ci case 1: 31062306a36Sopenharmony_ci return val * 10; 31162306a36Sopenharmony_ci case 2: 31262306a36Sopenharmony_ci return val * 100; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci pci_err(pdev, "%s: Invalid T_PwrOn scale: %u\n", __func__, scale); 31562306a36Sopenharmony_ci return 0; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci/* 31962306a36Sopenharmony_ci * Encode an LTR_L1.2_THRESHOLD value for the L1 PM Substates Control 1 32062306a36Sopenharmony_ci * register. Ports enter L1.2 when the most recent LTR value is greater 32162306a36Sopenharmony_ci * than or equal to LTR_L1.2_THRESHOLD, so we round up to make sure we 32262306a36Sopenharmony_ci * don't enter L1.2 too aggressively. 32362306a36Sopenharmony_ci * 32462306a36Sopenharmony_ci * See PCIe r6.0, sec 5.5.1, 6.18, 7.8.3.3. 32562306a36Sopenharmony_ci */ 32662306a36Sopenharmony_cistatic void encode_l12_threshold(u32 threshold_us, u32 *scale, u32 *value) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci u64 threshold_ns = (u64) threshold_us * 1000; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* 33162306a36Sopenharmony_ci * LTR_L1.2_THRESHOLD_Value ("value") is a 10-bit field with max 33262306a36Sopenharmony_ci * value of 0x3ff. 33362306a36Sopenharmony_ci */ 33462306a36Sopenharmony_ci if (threshold_ns <= 0x3ff * 1) { 33562306a36Sopenharmony_ci *scale = 0; /* Value times 1ns */ 33662306a36Sopenharmony_ci *value = threshold_ns; 33762306a36Sopenharmony_ci } else if (threshold_ns <= 0x3ff * 32) { 33862306a36Sopenharmony_ci *scale = 1; /* Value times 32ns */ 33962306a36Sopenharmony_ci *value = roundup(threshold_ns, 32) / 32; 34062306a36Sopenharmony_ci } else if (threshold_ns <= 0x3ff * 1024) { 34162306a36Sopenharmony_ci *scale = 2; /* Value times 1024ns */ 34262306a36Sopenharmony_ci *value = roundup(threshold_ns, 1024) / 1024; 34362306a36Sopenharmony_ci } else if (threshold_ns <= 0x3ff * 32768) { 34462306a36Sopenharmony_ci *scale = 3; /* Value times 32768ns */ 34562306a36Sopenharmony_ci *value = roundup(threshold_ns, 32768) / 32768; 34662306a36Sopenharmony_ci } else if (threshold_ns <= 0x3ff * 1048576) { 34762306a36Sopenharmony_ci *scale = 4; /* Value times 1048576ns */ 34862306a36Sopenharmony_ci *value = roundup(threshold_ns, 1048576) / 1048576; 34962306a36Sopenharmony_ci } else if (threshold_ns <= 0x3ff * (u64) 33554432) { 35062306a36Sopenharmony_ci *scale = 5; /* Value times 33554432ns */ 35162306a36Sopenharmony_ci *value = roundup(threshold_ns, 33554432) / 33554432; 35262306a36Sopenharmony_ci } else { 35362306a36Sopenharmony_ci *scale = 5; 35462306a36Sopenharmony_ci *value = 0x3ff; /* Max representable value */ 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic void pcie_aspm_check_latency(struct pci_dev *endpoint) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci u32 latency, encoding, lnkcap_up, lnkcap_dw; 36162306a36Sopenharmony_ci u32 l1_switch_latency = 0, latency_up_l0s; 36262306a36Sopenharmony_ci u32 latency_up_l1, latency_dw_l0s, latency_dw_l1; 36362306a36Sopenharmony_ci u32 acceptable_l0s, acceptable_l1; 36462306a36Sopenharmony_ci struct pcie_link_state *link; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci /* Device not in D0 doesn't need latency check */ 36762306a36Sopenharmony_ci if ((endpoint->current_state != PCI_D0) && 36862306a36Sopenharmony_ci (endpoint->current_state != PCI_UNKNOWN)) 36962306a36Sopenharmony_ci return; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci link = endpoint->bus->self->link_state; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* Calculate endpoint L0s acceptable latency */ 37462306a36Sopenharmony_ci encoding = (endpoint->devcap & PCI_EXP_DEVCAP_L0S) >> 6; 37562306a36Sopenharmony_ci acceptable_l0s = calc_l0s_acceptable(encoding); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci /* Calculate endpoint L1 acceptable latency */ 37862306a36Sopenharmony_ci encoding = (endpoint->devcap & PCI_EXP_DEVCAP_L1) >> 9; 37962306a36Sopenharmony_ci acceptable_l1 = calc_l1_acceptable(encoding); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci while (link) { 38262306a36Sopenharmony_ci struct pci_dev *dev = pci_function_0(link->pdev->subordinate); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* Read direction exit latencies */ 38562306a36Sopenharmony_ci pcie_capability_read_dword(link->pdev, PCI_EXP_LNKCAP, 38662306a36Sopenharmony_ci &lnkcap_up); 38762306a36Sopenharmony_ci pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, 38862306a36Sopenharmony_ci &lnkcap_dw); 38962306a36Sopenharmony_ci latency_up_l0s = calc_l0s_latency(lnkcap_up); 39062306a36Sopenharmony_ci latency_up_l1 = calc_l1_latency(lnkcap_up); 39162306a36Sopenharmony_ci latency_dw_l0s = calc_l0s_latency(lnkcap_dw); 39262306a36Sopenharmony_ci latency_dw_l1 = calc_l1_latency(lnkcap_dw); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci /* Check upstream direction L0s latency */ 39562306a36Sopenharmony_ci if ((link->aspm_capable & ASPM_STATE_L0S_UP) && 39662306a36Sopenharmony_ci (latency_up_l0s > acceptable_l0s)) 39762306a36Sopenharmony_ci link->aspm_capable &= ~ASPM_STATE_L0S_UP; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci /* Check downstream direction L0s latency */ 40062306a36Sopenharmony_ci if ((link->aspm_capable & ASPM_STATE_L0S_DW) && 40162306a36Sopenharmony_ci (latency_dw_l0s > acceptable_l0s)) 40262306a36Sopenharmony_ci link->aspm_capable &= ~ASPM_STATE_L0S_DW; 40362306a36Sopenharmony_ci /* 40462306a36Sopenharmony_ci * Check L1 latency. 40562306a36Sopenharmony_ci * Every switch on the path to root complex need 1 40662306a36Sopenharmony_ci * more microsecond for L1. Spec doesn't mention L0s. 40762306a36Sopenharmony_ci * 40862306a36Sopenharmony_ci * The exit latencies for L1 substates are not advertised 40962306a36Sopenharmony_ci * by a device. Since the spec also doesn't mention a way 41062306a36Sopenharmony_ci * to determine max latencies introduced by enabling L1 41162306a36Sopenharmony_ci * substates on the components, it is not clear how to do 41262306a36Sopenharmony_ci * a L1 substate exit latency check. We assume that the 41362306a36Sopenharmony_ci * L1 exit latencies advertised by a device include L1 41462306a36Sopenharmony_ci * substate latencies (and hence do not do any check). 41562306a36Sopenharmony_ci */ 41662306a36Sopenharmony_ci latency = max_t(u32, latency_up_l1, latency_dw_l1); 41762306a36Sopenharmony_ci if ((link->aspm_capable & ASPM_STATE_L1) && 41862306a36Sopenharmony_ci (latency + l1_switch_latency > acceptable_l1)) 41962306a36Sopenharmony_ci link->aspm_capable &= ~ASPM_STATE_L1; 42062306a36Sopenharmony_ci l1_switch_latency += 1000; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci link = link->parent; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic void pci_clear_and_set_dword(struct pci_dev *pdev, int pos, 42762306a36Sopenharmony_ci u32 clear, u32 set) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci u32 val; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci pci_read_config_dword(pdev, pos, &val); 43262306a36Sopenharmony_ci val &= ~clear; 43362306a36Sopenharmony_ci val |= set; 43462306a36Sopenharmony_ci pci_write_config_dword(pdev, pos, val); 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci/* Calculate L1.2 PM substate timing parameters */ 43862306a36Sopenharmony_cistatic void aspm_calc_l12_info(struct pcie_link_state *link, 43962306a36Sopenharmony_ci u32 parent_l1ss_cap, u32 child_l1ss_cap) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci struct pci_dev *child = link->downstream, *parent = link->pdev; 44262306a36Sopenharmony_ci u32 val1, val2, scale1, scale2; 44362306a36Sopenharmony_ci u32 t_common_mode, t_power_on, l1_2_threshold, scale, value; 44462306a36Sopenharmony_ci u32 ctl1 = 0, ctl2 = 0; 44562306a36Sopenharmony_ci u32 pctl1, pctl2, cctl1, cctl2; 44662306a36Sopenharmony_ci u32 pl1_2_enables, cl1_2_enables; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci /* Choose the greater of the two Port Common_Mode_Restore_Times */ 44962306a36Sopenharmony_ci val1 = (parent_l1ss_cap & PCI_L1SS_CAP_CM_RESTORE_TIME) >> 8; 45062306a36Sopenharmony_ci val2 = (child_l1ss_cap & PCI_L1SS_CAP_CM_RESTORE_TIME) >> 8; 45162306a36Sopenharmony_ci t_common_mode = max(val1, val2); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci /* Choose the greater of the two Port T_POWER_ON times */ 45462306a36Sopenharmony_ci val1 = (parent_l1ss_cap & PCI_L1SS_CAP_P_PWR_ON_VALUE) >> 19; 45562306a36Sopenharmony_ci scale1 = (parent_l1ss_cap & PCI_L1SS_CAP_P_PWR_ON_SCALE) >> 16; 45662306a36Sopenharmony_ci val2 = (child_l1ss_cap & PCI_L1SS_CAP_P_PWR_ON_VALUE) >> 19; 45762306a36Sopenharmony_ci scale2 = (child_l1ss_cap & PCI_L1SS_CAP_P_PWR_ON_SCALE) >> 16; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (calc_l12_pwron(parent, scale1, val1) > 46062306a36Sopenharmony_ci calc_l12_pwron(child, scale2, val2)) { 46162306a36Sopenharmony_ci ctl2 |= scale1 | (val1 << 3); 46262306a36Sopenharmony_ci t_power_on = calc_l12_pwron(parent, scale1, val1); 46362306a36Sopenharmony_ci } else { 46462306a36Sopenharmony_ci ctl2 |= scale2 | (val2 << 3); 46562306a36Sopenharmony_ci t_power_on = calc_l12_pwron(child, scale2, val2); 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci /* 46962306a36Sopenharmony_ci * Set LTR_L1.2_THRESHOLD to the time required to transition the 47062306a36Sopenharmony_ci * Link from L0 to L1.2 and back to L0 so we enter L1.2 only if 47162306a36Sopenharmony_ci * downstream devices report (via LTR) that they can tolerate at 47262306a36Sopenharmony_ci * least that much latency. 47362306a36Sopenharmony_ci * 47462306a36Sopenharmony_ci * Based on PCIe r3.1, sec 5.5.3.3.1, Figures 5-16 and 5-17, and 47562306a36Sopenharmony_ci * Table 5-11. T(POWER_OFF) is at most 2us and T(L1.2) is at 47662306a36Sopenharmony_ci * least 4us. 47762306a36Sopenharmony_ci */ 47862306a36Sopenharmony_ci l1_2_threshold = 2 + 4 + t_common_mode + t_power_on; 47962306a36Sopenharmony_ci encode_l12_threshold(l1_2_threshold, &scale, &value); 48062306a36Sopenharmony_ci ctl1 |= t_common_mode << 8 | scale << 29 | value << 16; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci /* Some broken devices only support dword access to L1 SS */ 48362306a36Sopenharmony_ci pci_read_config_dword(parent, parent->l1ss + PCI_L1SS_CTL1, &pctl1); 48462306a36Sopenharmony_ci pci_read_config_dword(parent, parent->l1ss + PCI_L1SS_CTL2, &pctl2); 48562306a36Sopenharmony_ci pci_read_config_dword(child, child->l1ss + PCI_L1SS_CTL1, &cctl1); 48662306a36Sopenharmony_ci pci_read_config_dword(child, child->l1ss + PCI_L1SS_CTL2, &cctl2); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci if (ctl1 == pctl1 && ctl1 == cctl1 && 48962306a36Sopenharmony_ci ctl2 == pctl2 && ctl2 == cctl2) 49062306a36Sopenharmony_ci return; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci /* Disable L1.2 while updating. See PCIe r5.0, sec 5.5.4, 7.8.3.3 */ 49362306a36Sopenharmony_ci pl1_2_enables = pctl1 & PCI_L1SS_CTL1_L1_2_MASK; 49462306a36Sopenharmony_ci cl1_2_enables = cctl1 & PCI_L1SS_CTL1_L1_2_MASK; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci if (pl1_2_enables || cl1_2_enables) { 49762306a36Sopenharmony_ci pci_clear_and_set_dword(child, child->l1ss + PCI_L1SS_CTL1, 49862306a36Sopenharmony_ci PCI_L1SS_CTL1_L1_2_MASK, 0); 49962306a36Sopenharmony_ci pci_clear_and_set_dword(parent, parent->l1ss + PCI_L1SS_CTL1, 50062306a36Sopenharmony_ci PCI_L1SS_CTL1_L1_2_MASK, 0); 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci /* Program T_POWER_ON times in both ports */ 50462306a36Sopenharmony_ci pci_write_config_dword(parent, parent->l1ss + PCI_L1SS_CTL2, ctl2); 50562306a36Sopenharmony_ci pci_write_config_dword(child, child->l1ss + PCI_L1SS_CTL2, ctl2); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci /* Program Common_Mode_Restore_Time in upstream device */ 50862306a36Sopenharmony_ci pci_clear_and_set_dword(parent, parent->l1ss + PCI_L1SS_CTL1, 50962306a36Sopenharmony_ci PCI_L1SS_CTL1_CM_RESTORE_TIME, ctl1); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci /* Program LTR_L1.2_THRESHOLD time in both ports */ 51262306a36Sopenharmony_ci pci_clear_and_set_dword(parent, parent->l1ss + PCI_L1SS_CTL1, 51362306a36Sopenharmony_ci PCI_L1SS_CTL1_LTR_L12_TH_VALUE | 51462306a36Sopenharmony_ci PCI_L1SS_CTL1_LTR_L12_TH_SCALE, ctl1); 51562306a36Sopenharmony_ci pci_clear_and_set_dword(child, child->l1ss + PCI_L1SS_CTL1, 51662306a36Sopenharmony_ci PCI_L1SS_CTL1_LTR_L12_TH_VALUE | 51762306a36Sopenharmony_ci PCI_L1SS_CTL1_LTR_L12_TH_SCALE, ctl1); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci if (pl1_2_enables || cl1_2_enables) { 52062306a36Sopenharmony_ci pci_clear_and_set_dword(parent, parent->l1ss + PCI_L1SS_CTL1, 0, 52162306a36Sopenharmony_ci pl1_2_enables); 52262306a36Sopenharmony_ci pci_clear_and_set_dword(child, child->l1ss + PCI_L1SS_CTL1, 0, 52362306a36Sopenharmony_ci cl1_2_enables); 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistatic void aspm_l1ss_init(struct pcie_link_state *link) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci struct pci_dev *child = link->downstream, *parent = link->pdev; 53062306a36Sopenharmony_ci u32 parent_l1ss_cap, child_l1ss_cap; 53162306a36Sopenharmony_ci u32 parent_l1ss_ctl1 = 0, child_l1ss_ctl1 = 0; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci if (!parent->l1ss || !child->l1ss) 53462306a36Sopenharmony_ci return; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci /* Setup L1 substate */ 53762306a36Sopenharmony_ci pci_read_config_dword(parent, parent->l1ss + PCI_L1SS_CAP, 53862306a36Sopenharmony_ci &parent_l1ss_cap); 53962306a36Sopenharmony_ci pci_read_config_dword(child, child->l1ss + PCI_L1SS_CAP, 54062306a36Sopenharmony_ci &child_l1ss_cap); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci if (!(parent_l1ss_cap & PCI_L1SS_CAP_L1_PM_SS)) 54362306a36Sopenharmony_ci parent_l1ss_cap = 0; 54462306a36Sopenharmony_ci if (!(child_l1ss_cap & PCI_L1SS_CAP_L1_PM_SS)) 54562306a36Sopenharmony_ci child_l1ss_cap = 0; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci /* 54862306a36Sopenharmony_ci * If we don't have LTR for the entire path from the Root Complex 54962306a36Sopenharmony_ci * to this device, we can't use ASPM L1.2 because it relies on the 55062306a36Sopenharmony_ci * LTR_L1.2_THRESHOLD. See PCIe r4.0, secs 5.5.4, 6.18. 55162306a36Sopenharmony_ci */ 55262306a36Sopenharmony_ci if (!child->ltr_path) 55362306a36Sopenharmony_ci child_l1ss_cap &= ~PCI_L1SS_CAP_ASPM_L1_2; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci if (parent_l1ss_cap & child_l1ss_cap & PCI_L1SS_CAP_ASPM_L1_1) 55662306a36Sopenharmony_ci link->aspm_support |= ASPM_STATE_L1_1; 55762306a36Sopenharmony_ci if (parent_l1ss_cap & child_l1ss_cap & PCI_L1SS_CAP_ASPM_L1_2) 55862306a36Sopenharmony_ci link->aspm_support |= ASPM_STATE_L1_2; 55962306a36Sopenharmony_ci if (parent_l1ss_cap & child_l1ss_cap & PCI_L1SS_CAP_PCIPM_L1_1) 56062306a36Sopenharmony_ci link->aspm_support |= ASPM_STATE_L1_1_PCIPM; 56162306a36Sopenharmony_ci if (parent_l1ss_cap & child_l1ss_cap & PCI_L1SS_CAP_PCIPM_L1_2) 56262306a36Sopenharmony_ci link->aspm_support |= ASPM_STATE_L1_2_PCIPM; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci if (parent_l1ss_cap) 56562306a36Sopenharmony_ci pci_read_config_dword(parent, parent->l1ss + PCI_L1SS_CTL1, 56662306a36Sopenharmony_ci &parent_l1ss_ctl1); 56762306a36Sopenharmony_ci if (child_l1ss_cap) 56862306a36Sopenharmony_ci pci_read_config_dword(child, child->l1ss + PCI_L1SS_CTL1, 56962306a36Sopenharmony_ci &child_l1ss_ctl1); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci if (parent_l1ss_ctl1 & child_l1ss_ctl1 & PCI_L1SS_CTL1_ASPM_L1_1) 57262306a36Sopenharmony_ci link->aspm_enabled |= ASPM_STATE_L1_1; 57362306a36Sopenharmony_ci if (parent_l1ss_ctl1 & child_l1ss_ctl1 & PCI_L1SS_CTL1_ASPM_L1_2) 57462306a36Sopenharmony_ci link->aspm_enabled |= ASPM_STATE_L1_2; 57562306a36Sopenharmony_ci if (parent_l1ss_ctl1 & child_l1ss_ctl1 & PCI_L1SS_CTL1_PCIPM_L1_1) 57662306a36Sopenharmony_ci link->aspm_enabled |= ASPM_STATE_L1_1_PCIPM; 57762306a36Sopenharmony_ci if (parent_l1ss_ctl1 & child_l1ss_ctl1 & PCI_L1SS_CTL1_PCIPM_L1_2) 57862306a36Sopenharmony_ci link->aspm_enabled |= ASPM_STATE_L1_2_PCIPM; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci if (link->aspm_support & ASPM_STATE_L1_2_MASK) 58162306a36Sopenharmony_ci aspm_calc_l12_info(link, parent_l1ss_cap, child_l1ss_cap); 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_cistatic void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) 58562306a36Sopenharmony_ci{ 58662306a36Sopenharmony_ci struct pci_dev *child = link->downstream, *parent = link->pdev; 58762306a36Sopenharmony_ci u32 parent_lnkcap, child_lnkcap; 58862306a36Sopenharmony_ci u16 parent_lnkctl, child_lnkctl; 58962306a36Sopenharmony_ci struct pci_bus *linkbus = parent->subordinate; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci if (blacklist) { 59262306a36Sopenharmony_ci /* Set enabled/disable so that we will disable ASPM later */ 59362306a36Sopenharmony_ci link->aspm_enabled = ASPM_STATE_ALL; 59462306a36Sopenharmony_ci link->aspm_disable = ASPM_STATE_ALL; 59562306a36Sopenharmony_ci return; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci /* 59962306a36Sopenharmony_ci * If ASPM not supported, don't mess with the clocks and link, 60062306a36Sopenharmony_ci * bail out now. 60162306a36Sopenharmony_ci */ 60262306a36Sopenharmony_ci pcie_capability_read_dword(parent, PCI_EXP_LNKCAP, &parent_lnkcap); 60362306a36Sopenharmony_ci pcie_capability_read_dword(child, PCI_EXP_LNKCAP, &child_lnkcap); 60462306a36Sopenharmony_ci if (!(parent_lnkcap & child_lnkcap & PCI_EXP_LNKCAP_ASPMS)) 60562306a36Sopenharmony_ci return; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci /* Configure common clock before checking latencies */ 60862306a36Sopenharmony_ci pcie_aspm_configure_common_clock(link); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci /* 61162306a36Sopenharmony_ci * Re-read upstream/downstream components' register state after 61262306a36Sopenharmony_ci * clock configuration. L0s & L1 exit latencies in the otherwise 61362306a36Sopenharmony_ci * read-only Link Capabilities may change depending on common clock 61462306a36Sopenharmony_ci * configuration (PCIe r5.0, sec 7.5.3.6). 61562306a36Sopenharmony_ci */ 61662306a36Sopenharmony_ci pcie_capability_read_dword(parent, PCI_EXP_LNKCAP, &parent_lnkcap); 61762306a36Sopenharmony_ci pcie_capability_read_dword(child, PCI_EXP_LNKCAP, &child_lnkcap); 61862306a36Sopenharmony_ci pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &parent_lnkctl); 61962306a36Sopenharmony_ci pcie_capability_read_word(child, PCI_EXP_LNKCTL, &child_lnkctl); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci /* 62262306a36Sopenharmony_ci * Setup L0s state 62362306a36Sopenharmony_ci * 62462306a36Sopenharmony_ci * Note that we must not enable L0s in either direction on a 62562306a36Sopenharmony_ci * given link unless components on both sides of the link each 62662306a36Sopenharmony_ci * support L0s. 62762306a36Sopenharmony_ci */ 62862306a36Sopenharmony_ci if (parent_lnkcap & child_lnkcap & PCI_EXP_LNKCAP_ASPM_L0S) 62962306a36Sopenharmony_ci link->aspm_support |= ASPM_STATE_L0S; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci if (child_lnkctl & PCI_EXP_LNKCTL_ASPM_L0S) 63262306a36Sopenharmony_ci link->aspm_enabled |= ASPM_STATE_L0S_UP; 63362306a36Sopenharmony_ci if (parent_lnkctl & PCI_EXP_LNKCTL_ASPM_L0S) 63462306a36Sopenharmony_ci link->aspm_enabled |= ASPM_STATE_L0S_DW; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci /* Setup L1 state */ 63762306a36Sopenharmony_ci if (parent_lnkcap & child_lnkcap & PCI_EXP_LNKCAP_ASPM_L1) 63862306a36Sopenharmony_ci link->aspm_support |= ASPM_STATE_L1; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci if (parent_lnkctl & child_lnkctl & PCI_EXP_LNKCTL_ASPM_L1) 64162306a36Sopenharmony_ci link->aspm_enabled |= ASPM_STATE_L1; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci aspm_l1ss_init(link); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci /* Save default state */ 64662306a36Sopenharmony_ci link->aspm_default = link->aspm_enabled; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci /* Setup initial capable state. Will be updated later */ 64962306a36Sopenharmony_ci link->aspm_capable = link->aspm_support; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci /* Get and check endpoint acceptable latencies */ 65262306a36Sopenharmony_ci list_for_each_entry(child, &linkbus->devices, bus_list) { 65362306a36Sopenharmony_ci if (pci_pcie_type(child) != PCI_EXP_TYPE_ENDPOINT && 65462306a36Sopenharmony_ci pci_pcie_type(child) != PCI_EXP_TYPE_LEG_END) 65562306a36Sopenharmony_ci continue; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci pcie_aspm_check_latency(child); 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci/* Configure the ASPM L1 substates */ 66262306a36Sopenharmony_cistatic void pcie_config_aspm_l1ss(struct pcie_link_state *link, u32 state) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci u32 val, enable_req; 66562306a36Sopenharmony_ci struct pci_dev *child = link->downstream, *parent = link->pdev; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci enable_req = (link->aspm_enabled ^ state) & state; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci /* 67062306a36Sopenharmony_ci * Here are the rules specified in the PCIe spec for enabling L1SS: 67162306a36Sopenharmony_ci * - When enabling L1.x, enable bit at parent first, then at child 67262306a36Sopenharmony_ci * - When disabling L1.x, disable bit at child first, then at parent 67362306a36Sopenharmony_ci * - When enabling ASPM L1.x, need to disable L1 67462306a36Sopenharmony_ci * (at child followed by parent). 67562306a36Sopenharmony_ci * - The ASPM/PCIPM L1.2 must be disabled while programming timing 67662306a36Sopenharmony_ci * parameters 67762306a36Sopenharmony_ci * 67862306a36Sopenharmony_ci * To keep it simple, disable all L1SS bits first, and later enable 67962306a36Sopenharmony_ci * what is needed. 68062306a36Sopenharmony_ci */ 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci /* Disable all L1 substates */ 68362306a36Sopenharmony_ci pci_clear_and_set_dword(child, child->l1ss + PCI_L1SS_CTL1, 68462306a36Sopenharmony_ci PCI_L1SS_CTL1_L1SS_MASK, 0); 68562306a36Sopenharmony_ci pci_clear_and_set_dword(parent, parent->l1ss + PCI_L1SS_CTL1, 68662306a36Sopenharmony_ci PCI_L1SS_CTL1_L1SS_MASK, 0); 68762306a36Sopenharmony_ci /* 68862306a36Sopenharmony_ci * If needed, disable L1, and it gets enabled later 68962306a36Sopenharmony_ci * in pcie_config_aspm_link(). 69062306a36Sopenharmony_ci */ 69162306a36Sopenharmony_ci if (enable_req & (ASPM_STATE_L1_1 | ASPM_STATE_L1_2)) { 69262306a36Sopenharmony_ci pcie_capability_clear_and_set_word(child, PCI_EXP_LNKCTL, 69362306a36Sopenharmony_ci PCI_EXP_LNKCTL_ASPM_L1, 0); 69462306a36Sopenharmony_ci pcie_capability_clear_and_set_word(parent, PCI_EXP_LNKCTL, 69562306a36Sopenharmony_ci PCI_EXP_LNKCTL_ASPM_L1, 0); 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci val = 0; 69962306a36Sopenharmony_ci if (state & ASPM_STATE_L1_1) 70062306a36Sopenharmony_ci val |= PCI_L1SS_CTL1_ASPM_L1_1; 70162306a36Sopenharmony_ci if (state & ASPM_STATE_L1_2) 70262306a36Sopenharmony_ci val |= PCI_L1SS_CTL1_ASPM_L1_2; 70362306a36Sopenharmony_ci if (state & ASPM_STATE_L1_1_PCIPM) 70462306a36Sopenharmony_ci val |= PCI_L1SS_CTL1_PCIPM_L1_1; 70562306a36Sopenharmony_ci if (state & ASPM_STATE_L1_2_PCIPM) 70662306a36Sopenharmony_ci val |= PCI_L1SS_CTL1_PCIPM_L1_2; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci /* Enable what we need to enable */ 70962306a36Sopenharmony_ci pci_clear_and_set_dword(parent, parent->l1ss + PCI_L1SS_CTL1, 71062306a36Sopenharmony_ci PCI_L1SS_CTL1_L1SS_MASK, val); 71162306a36Sopenharmony_ci pci_clear_and_set_dword(child, child->l1ss + PCI_L1SS_CTL1, 71262306a36Sopenharmony_ci PCI_L1SS_CTL1_L1SS_MASK, val); 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_cistatic void pcie_config_aspm_dev(struct pci_dev *pdev, u32 val) 71662306a36Sopenharmony_ci{ 71762306a36Sopenharmony_ci pcie_capability_clear_and_set_word(pdev, PCI_EXP_LNKCTL, 71862306a36Sopenharmony_ci PCI_EXP_LNKCTL_ASPMC, val); 71962306a36Sopenharmony_ci} 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_cistatic void pcie_config_aspm_link(struct pcie_link_state *link, u32 state) 72262306a36Sopenharmony_ci{ 72362306a36Sopenharmony_ci u32 upstream = 0, dwstream = 0; 72462306a36Sopenharmony_ci struct pci_dev *child = link->downstream, *parent = link->pdev; 72562306a36Sopenharmony_ci struct pci_bus *linkbus = parent->subordinate; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci /* Enable only the states that were not explicitly disabled */ 72862306a36Sopenharmony_ci state &= (link->aspm_capable & ~link->aspm_disable); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci /* Can't enable any substates if L1 is not enabled */ 73162306a36Sopenharmony_ci if (!(state & ASPM_STATE_L1)) 73262306a36Sopenharmony_ci state &= ~ASPM_STATE_L1SS; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci /* Spec says both ports must be in D0 before enabling PCI PM substates*/ 73562306a36Sopenharmony_ci if (parent->current_state != PCI_D0 || child->current_state != PCI_D0) { 73662306a36Sopenharmony_ci state &= ~ASPM_STATE_L1_SS_PCIPM; 73762306a36Sopenharmony_ci state |= (link->aspm_enabled & ASPM_STATE_L1_SS_PCIPM); 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci /* Nothing to do if the link is already in the requested state */ 74162306a36Sopenharmony_ci if (link->aspm_enabled == state) 74262306a36Sopenharmony_ci return; 74362306a36Sopenharmony_ci /* Convert ASPM state to upstream/downstream ASPM register state */ 74462306a36Sopenharmony_ci if (state & ASPM_STATE_L0S_UP) 74562306a36Sopenharmony_ci dwstream |= PCI_EXP_LNKCTL_ASPM_L0S; 74662306a36Sopenharmony_ci if (state & ASPM_STATE_L0S_DW) 74762306a36Sopenharmony_ci upstream |= PCI_EXP_LNKCTL_ASPM_L0S; 74862306a36Sopenharmony_ci if (state & ASPM_STATE_L1) { 74962306a36Sopenharmony_ci upstream |= PCI_EXP_LNKCTL_ASPM_L1; 75062306a36Sopenharmony_ci dwstream |= PCI_EXP_LNKCTL_ASPM_L1; 75162306a36Sopenharmony_ci } 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci if (link->aspm_capable & ASPM_STATE_L1SS) 75462306a36Sopenharmony_ci pcie_config_aspm_l1ss(link, state); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci /* 75762306a36Sopenharmony_ci * Spec 2.0 suggests all functions should be configured the 75862306a36Sopenharmony_ci * same setting for ASPM. Enabling ASPM L1 should be done in 75962306a36Sopenharmony_ci * upstream component first and then downstream, and vice 76062306a36Sopenharmony_ci * versa for disabling ASPM L1. Spec doesn't mention L0S. 76162306a36Sopenharmony_ci */ 76262306a36Sopenharmony_ci if (state & ASPM_STATE_L1) 76362306a36Sopenharmony_ci pcie_config_aspm_dev(parent, upstream); 76462306a36Sopenharmony_ci list_for_each_entry(child, &linkbus->devices, bus_list) 76562306a36Sopenharmony_ci pcie_config_aspm_dev(child, dwstream); 76662306a36Sopenharmony_ci if (!(state & ASPM_STATE_L1)) 76762306a36Sopenharmony_ci pcie_config_aspm_dev(parent, upstream); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci link->aspm_enabled = state; 77062306a36Sopenharmony_ci} 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_cistatic void pcie_config_aspm_path(struct pcie_link_state *link) 77362306a36Sopenharmony_ci{ 77462306a36Sopenharmony_ci while (link) { 77562306a36Sopenharmony_ci pcie_config_aspm_link(link, policy_to_aspm_state(link)); 77662306a36Sopenharmony_ci link = link->parent; 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci} 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_cistatic void free_link_state(struct pcie_link_state *link) 78162306a36Sopenharmony_ci{ 78262306a36Sopenharmony_ci link->pdev->link_state = NULL; 78362306a36Sopenharmony_ci kfree(link); 78462306a36Sopenharmony_ci} 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_cistatic int pcie_aspm_sanity_check(struct pci_dev *pdev) 78762306a36Sopenharmony_ci{ 78862306a36Sopenharmony_ci struct pci_dev *child; 78962306a36Sopenharmony_ci u32 reg32; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci /* 79262306a36Sopenharmony_ci * Some functions in a slot might not all be PCIe functions, 79362306a36Sopenharmony_ci * very strange. Disable ASPM for the whole slot 79462306a36Sopenharmony_ci */ 79562306a36Sopenharmony_ci list_for_each_entry(child, &pdev->subordinate->devices, bus_list) { 79662306a36Sopenharmony_ci if (!pci_is_pcie(child)) 79762306a36Sopenharmony_ci return -EINVAL; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci /* 80062306a36Sopenharmony_ci * If ASPM is disabled then we're not going to change 80162306a36Sopenharmony_ci * the BIOS state. It's safe to continue even if it's a 80262306a36Sopenharmony_ci * pre-1.1 device 80362306a36Sopenharmony_ci */ 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci if (aspm_disabled) 80662306a36Sopenharmony_ci continue; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci /* 80962306a36Sopenharmony_ci * Disable ASPM for pre-1.1 PCIe device, we follow MS to use 81062306a36Sopenharmony_ci * RBER bit to determine if a function is 1.1 version device 81162306a36Sopenharmony_ci */ 81262306a36Sopenharmony_ci pcie_capability_read_dword(child, PCI_EXP_DEVCAP, ®32); 81362306a36Sopenharmony_ci if (!(reg32 & PCI_EXP_DEVCAP_RBER) && !aspm_force) { 81462306a36Sopenharmony_ci pci_info(child, "disabling ASPM on pre-1.1 PCIe device. You can enable it with 'pcie_aspm=force'\n"); 81562306a36Sopenharmony_ci return -EINVAL; 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci return 0; 81962306a36Sopenharmony_ci} 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_cistatic struct pcie_link_state *alloc_pcie_link_state(struct pci_dev *pdev) 82262306a36Sopenharmony_ci{ 82362306a36Sopenharmony_ci struct pcie_link_state *link; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci link = kzalloc(sizeof(*link), GFP_KERNEL); 82662306a36Sopenharmony_ci if (!link) 82762306a36Sopenharmony_ci return NULL; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci INIT_LIST_HEAD(&link->sibling); 83062306a36Sopenharmony_ci link->pdev = pdev; 83162306a36Sopenharmony_ci link->downstream = pci_function_0(pdev->subordinate); 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci /* 83462306a36Sopenharmony_ci * Root Ports and PCI/PCI-X to PCIe Bridges are roots of PCIe 83562306a36Sopenharmony_ci * hierarchies. Note that some PCIe host implementations omit 83662306a36Sopenharmony_ci * the root ports entirely, in which case a downstream port on 83762306a36Sopenharmony_ci * a switch may become the root of the link state chain for all 83862306a36Sopenharmony_ci * its subordinate endpoints. 83962306a36Sopenharmony_ci */ 84062306a36Sopenharmony_ci if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT || 84162306a36Sopenharmony_ci pci_pcie_type(pdev) == PCI_EXP_TYPE_PCIE_BRIDGE || 84262306a36Sopenharmony_ci !pdev->bus->parent->self) { 84362306a36Sopenharmony_ci link->root = link; 84462306a36Sopenharmony_ci } else { 84562306a36Sopenharmony_ci struct pcie_link_state *parent; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci parent = pdev->bus->parent->self->link_state; 84862306a36Sopenharmony_ci if (!parent) { 84962306a36Sopenharmony_ci kfree(link); 85062306a36Sopenharmony_ci return NULL; 85162306a36Sopenharmony_ci } 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci link->parent = parent; 85462306a36Sopenharmony_ci link->root = link->parent->root; 85562306a36Sopenharmony_ci } 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci list_add(&link->sibling, &link_list); 85862306a36Sopenharmony_ci pdev->link_state = link; 85962306a36Sopenharmony_ci return link; 86062306a36Sopenharmony_ci} 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_cistatic void pcie_aspm_update_sysfs_visibility(struct pci_dev *pdev) 86362306a36Sopenharmony_ci{ 86462306a36Sopenharmony_ci struct pci_dev *child; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci list_for_each_entry(child, &pdev->subordinate->devices, bus_list) 86762306a36Sopenharmony_ci sysfs_update_group(&child->dev.kobj, &aspm_ctrl_attr_group); 86862306a36Sopenharmony_ci} 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci/* 87162306a36Sopenharmony_ci * pcie_aspm_init_link_state: Initiate PCI express link state. 87262306a36Sopenharmony_ci * It is called after the pcie and its children devices are scanned. 87362306a36Sopenharmony_ci * @pdev: the root port or switch downstream port 87462306a36Sopenharmony_ci */ 87562306a36Sopenharmony_civoid pcie_aspm_init_link_state(struct pci_dev *pdev) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci struct pcie_link_state *link; 87862306a36Sopenharmony_ci int blacklist = !!pcie_aspm_sanity_check(pdev); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci if (!aspm_support_enabled) 88162306a36Sopenharmony_ci return; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci if (pdev->link_state) 88462306a36Sopenharmony_ci return; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci /* 88762306a36Sopenharmony_ci * We allocate pcie_link_state for the component on the upstream 88862306a36Sopenharmony_ci * end of a Link, so there's nothing to do unless this device is 88962306a36Sopenharmony_ci * downstream port. 89062306a36Sopenharmony_ci */ 89162306a36Sopenharmony_ci if (!pcie_downstream_port(pdev)) 89262306a36Sopenharmony_ci return; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci /* VIA has a strange chipset, root port is under a bridge */ 89562306a36Sopenharmony_ci if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT && 89662306a36Sopenharmony_ci pdev->bus->self) 89762306a36Sopenharmony_ci return; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci down_read(&pci_bus_sem); 90062306a36Sopenharmony_ci if (list_empty(&pdev->subordinate->devices)) 90162306a36Sopenharmony_ci goto out; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci mutex_lock(&aspm_lock); 90462306a36Sopenharmony_ci link = alloc_pcie_link_state(pdev); 90562306a36Sopenharmony_ci if (!link) 90662306a36Sopenharmony_ci goto unlock; 90762306a36Sopenharmony_ci /* 90862306a36Sopenharmony_ci * Setup initial ASPM state. Note that we need to configure 90962306a36Sopenharmony_ci * upstream links also because capable state of them can be 91062306a36Sopenharmony_ci * update through pcie_aspm_cap_init(). 91162306a36Sopenharmony_ci */ 91262306a36Sopenharmony_ci pcie_aspm_cap_init(link, blacklist); 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci /* Setup initial Clock PM state */ 91562306a36Sopenharmony_ci pcie_clkpm_cap_init(link, blacklist); 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci /* 91862306a36Sopenharmony_ci * At this stage drivers haven't had an opportunity to change the 91962306a36Sopenharmony_ci * link policy setting. Enabling ASPM on broken hardware can cripple 92062306a36Sopenharmony_ci * it even before the driver has had a chance to disable ASPM, so 92162306a36Sopenharmony_ci * default to a safe level right now. If we're enabling ASPM beyond 92262306a36Sopenharmony_ci * the BIOS's expectation, we'll do so once pci_enable_device() is 92362306a36Sopenharmony_ci * called. 92462306a36Sopenharmony_ci */ 92562306a36Sopenharmony_ci if (aspm_policy != POLICY_POWERSAVE && 92662306a36Sopenharmony_ci aspm_policy != POLICY_POWER_SUPERSAVE) { 92762306a36Sopenharmony_ci pcie_config_aspm_path(link); 92862306a36Sopenharmony_ci pcie_set_clkpm(link, policy_to_clkpm_state(link)); 92962306a36Sopenharmony_ci } 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci pcie_aspm_update_sysfs_visibility(pdev); 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ciunlock: 93462306a36Sopenharmony_ci mutex_unlock(&aspm_lock); 93562306a36Sopenharmony_ciout: 93662306a36Sopenharmony_ci up_read(&pci_bus_sem); 93762306a36Sopenharmony_ci} 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci/* Recheck latencies and update aspm_capable for links under the root */ 94062306a36Sopenharmony_cistatic void pcie_update_aspm_capable(struct pcie_link_state *root) 94162306a36Sopenharmony_ci{ 94262306a36Sopenharmony_ci struct pcie_link_state *link; 94362306a36Sopenharmony_ci BUG_ON(root->parent); 94462306a36Sopenharmony_ci list_for_each_entry(link, &link_list, sibling) { 94562306a36Sopenharmony_ci if (link->root != root) 94662306a36Sopenharmony_ci continue; 94762306a36Sopenharmony_ci link->aspm_capable = link->aspm_support; 94862306a36Sopenharmony_ci } 94962306a36Sopenharmony_ci list_for_each_entry(link, &link_list, sibling) { 95062306a36Sopenharmony_ci struct pci_dev *child; 95162306a36Sopenharmony_ci struct pci_bus *linkbus = link->pdev->subordinate; 95262306a36Sopenharmony_ci if (link->root != root) 95362306a36Sopenharmony_ci continue; 95462306a36Sopenharmony_ci list_for_each_entry(child, &linkbus->devices, bus_list) { 95562306a36Sopenharmony_ci if ((pci_pcie_type(child) != PCI_EXP_TYPE_ENDPOINT) && 95662306a36Sopenharmony_ci (pci_pcie_type(child) != PCI_EXP_TYPE_LEG_END)) 95762306a36Sopenharmony_ci continue; 95862306a36Sopenharmony_ci pcie_aspm_check_latency(child); 95962306a36Sopenharmony_ci } 96062306a36Sopenharmony_ci } 96162306a36Sopenharmony_ci} 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci/* @pdev: the endpoint device */ 96462306a36Sopenharmony_civoid pcie_aspm_exit_link_state(struct pci_dev *pdev) 96562306a36Sopenharmony_ci{ 96662306a36Sopenharmony_ci struct pci_dev *parent = pdev->bus->self; 96762306a36Sopenharmony_ci struct pcie_link_state *link, *root, *parent_link; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci if (!parent || !parent->link_state) 97062306a36Sopenharmony_ci return; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci down_read(&pci_bus_sem); 97362306a36Sopenharmony_ci mutex_lock(&aspm_lock); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci link = parent->link_state; 97662306a36Sopenharmony_ci root = link->root; 97762306a36Sopenharmony_ci parent_link = link->parent; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci /* 98062306a36Sopenharmony_ci * link->downstream is a pointer to the pci_dev of function 0. If 98162306a36Sopenharmony_ci * we remove that function, the pci_dev is about to be deallocated, 98262306a36Sopenharmony_ci * so we can't use link->downstream again. Free the link state to 98362306a36Sopenharmony_ci * avoid this. 98462306a36Sopenharmony_ci * 98562306a36Sopenharmony_ci * If we're removing a non-0 function, it's possible we could 98662306a36Sopenharmony_ci * retain the link state, but PCIe r6.0, sec 7.5.3.7, recommends 98762306a36Sopenharmony_ci * programming the same ASPM Control value for all functions of 98862306a36Sopenharmony_ci * multi-function devices, so disable ASPM for all of them. 98962306a36Sopenharmony_ci */ 99062306a36Sopenharmony_ci pcie_config_aspm_link(link, 0); 99162306a36Sopenharmony_ci list_del(&link->sibling); 99262306a36Sopenharmony_ci free_link_state(link); 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci /* Recheck latencies and configure upstream links */ 99562306a36Sopenharmony_ci if (parent_link) { 99662306a36Sopenharmony_ci pcie_update_aspm_capable(root); 99762306a36Sopenharmony_ci pcie_config_aspm_path(parent_link); 99862306a36Sopenharmony_ci } 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci mutex_unlock(&aspm_lock); 100162306a36Sopenharmony_ci up_read(&pci_bus_sem); 100262306a36Sopenharmony_ci} 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci/* @pdev: the root port or switch downstream port */ 100562306a36Sopenharmony_civoid pcie_aspm_pm_state_change(struct pci_dev *pdev) 100662306a36Sopenharmony_ci{ 100762306a36Sopenharmony_ci struct pcie_link_state *link = pdev->link_state; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci if (aspm_disabled || !link) 101062306a36Sopenharmony_ci return; 101162306a36Sopenharmony_ci /* 101262306a36Sopenharmony_ci * Devices changed PM state, we should recheck if latency 101362306a36Sopenharmony_ci * meets all functions' requirement 101462306a36Sopenharmony_ci */ 101562306a36Sopenharmony_ci down_read(&pci_bus_sem); 101662306a36Sopenharmony_ci mutex_lock(&aspm_lock); 101762306a36Sopenharmony_ci pcie_update_aspm_capable(link->root); 101862306a36Sopenharmony_ci pcie_config_aspm_path(link); 101962306a36Sopenharmony_ci mutex_unlock(&aspm_lock); 102062306a36Sopenharmony_ci up_read(&pci_bus_sem); 102162306a36Sopenharmony_ci} 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_civoid pcie_aspm_powersave_config_link(struct pci_dev *pdev) 102462306a36Sopenharmony_ci{ 102562306a36Sopenharmony_ci struct pcie_link_state *link = pdev->link_state; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci if (aspm_disabled || !link) 102862306a36Sopenharmony_ci return; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci if (aspm_policy != POLICY_POWERSAVE && 103162306a36Sopenharmony_ci aspm_policy != POLICY_POWER_SUPERSAVE) 103262306a36Sopenharmony_ci return; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci down_read(&pci_bus_sem); 103562306a36Sopenharmony_ci mutex_lock(&aspm_lock); 103662306a36Sopenharmony_ci pcie_config_aspm_path(link); 103762306a36Sopenharmony_ci pcie_set_clkpm(link, policy_to_clkpm_state(link)); 103862306a36Sopenharmony_ci mutex_unlock(&aspm_lock); 103962306a36Sopenharmony_ci up_read(&pci_bus_sem); 104062306a36Sopenharmony_ci} 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_cistatic struct pcie_link_state *pcie_aspm_get_link(struct pci_dev *pdev) 104362306a36Sopenharmony_ci{ 104462306a36Sopenharmony_ci struct pci_dev *bridge; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci if (!pci_is_pcie(pdev)) 104762306a36Sopenharmony_ci return NULL; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci bridge = pci_upstream_bridge(pdev); 105062306a36Sopenharmony_ci if (!bridge || !pci_is_pcie(bridge)) 105162306a36Sopenharmony_ci return NULL; 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci return bridge->link_state; 105462306a36Sopenharmony_ci} 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_cistatic int __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem) 105762306a36Sopenharmony_ci{ 105862306a36Sopenharmony_ci struct pcie_link_state *link = pcie_aspm_get_link(pdev); 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci if (!link) 106162306a36Sopenharmony_ci return -EINVAL; 106262306a36Sopenharmony_ci /* 106362306a36Sopenharmony_ci * A driver requested that ASPM be disabled on this device, but 106462306a36Sopenharmony_ci * if we don't have permission to manage ASPM (e.g., on ACPI 106562306a36Sopenharmony_ci * systems we have to observe the FADT ACPI_FADT_NO_ASPM bit and 106662306a36Sopenharmony_ci * the _OSC method), we can't honor that request. Windows has 106762306a36Sopenharmony_ci * a similar mechanism using "PciASPMOptOut", which is also 106862306a36Sopenharmony_ci * ignored in this situation. 106962306a36Sopenharmony_ci */ 107062306a36Sopenharmony_ci if (aspm_disabled) { 107162306a36Sopenharmony_ci pci_warn(pdev, "can't disable ASPM; OS doesn't have ASPM control\n"); 107262306a36Sopenharmony_ci return -EPERM; 107362306a36Sopenharmony_ci } 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci if (sem) 107662306a36Sopenharmony_ci down_read(&pci_bus_sem); 107762306a36Sopenharmony_ci mutex_lock(&aspm_lock); 107862306a36Sopenharmony_ci if (state & PCIE_LINK_STATE_L0S) 107962306a36Sopenharmony_ci link->aspm_disable |= ASPM_STATE_L0S; 108062306a36Sopenharmony_ci if (state & PCIE_LINK_STATE_L1) 108162306a36Sopenharmony_ci /* L1 PM substates require L1 */ 108262306a36Sopenharmony_ci link->aspm_disable |= ASPM_STATE_L1 | ASPM_STATE_L1SS; 108362306a36Sopenharmony_ci if (state & PCIE_LINK_STATE_L1_1) 108462306a36Sopenharmony_ci link->aspm_disable |= ASPM_STATE_L1_1; 108562306a36Sopenharmony_ci if (state & PCIE_LINK_STATE_L1_2) 108662306a36Sopenharmony_ci link->aspm_disable |= ASPM_STATE_L1_2; 108762306a36Sopenharmony_ci if (state & PCIE_LINK_STATE_L1_1_PCIPM) 108862306a36Sopenharmony_ci link->aspm_disable |= ASPM_STATE_L1_1_PCIPM; 108962306a36Sopenharmony_ci if (state & PCIE_LINK_STATE_L1_2_PCIPM) 109062306a36Sopenharmony_ci link->aspm_disable |= ASPM_STATE_L1_2_PCIPM; 109162306a36Sopenharmony_ci pcie_config_aspm_link(link, policy_to_aspm_state(link)); 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci if (state & PCIE_LINK_STATE_CLKPM) 109462306a36Sopenharmony_ci link->clkpm_disable = 1; 109562306a36Sopenharmony_ci pcie_set_clkpm(link, policy_to_clkpm_state(link)); 109662306a36Sopenharmony_ci mutex_unlock(&aspm_lock); 109762306a36Sopenharmony_ci if (sem) 109862306a36Sopenharmony_ci up_read(&pci_bus_sem); 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci return 0; 110162306a36Sopenharmony_ci} 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ciint pci_disable_link_state_locked(struct pci_dev *pdev, int state) 110462306a36Sopenharmony_ci{ 110562306a36Sopenharmony_ci return __pci_disable_link_state(pdev, state, false); 110662306a36Sopenharmony_ci} 110762306a36Sopenharmony_ciEXPORT_SYMBOL(pci_disable_link_state_locked); 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci/** 111062306a36Sopenharmony_ci * pci_disable_link_state - Disable device's link state, so the link will 111162306a36Sopenharmony_ci * never enter specific states. Note that if the BIOS didn't grant ASPM 111262306a36Sopenharmony_ci * control to the OS, this does nothing because we can't touch the LNKCTL 111362306a36Sopenharmony_ci * register. Returns 0 or a negative errno. 111462306a36Sopenharmony_ci * 111562306a36Sopenharmony_ci * @pdev: PCI device 111662306a36Sopenharmony_ci * @state: ASPM link state to disable 111762306a36Sopenharmony_ci */ 111862306a36Sopenharmony_ciint pci_disable_link_state(struct pci_dev *pdev, int state) 111962306a36Sopenharmony_ci{ 112062306a36Sopenharmony_ci return __pci_disable_link_state(pdev, state, true); 112162306a36Sopenharmony_ci} 112262306a36Sopenharmony_ciEXPORT_SYMBOL(pci_disable_link_state); 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_cistatic int __pci_enable_link_state(struct pci_dev *pdev, int state, bool locked) 112562306a36Sopenharmony_ci{ 112662306a36Sopenharmony_ci struct pcie_link_state *link = pcie_aspm_get_link(pdev); 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci if (!link) 112962306a36Sopenharmony_ci return -EINVAL; 113062306a36Sopenharmony_ci /* 113162306a36Sopenharmony_ci * A driver requested that ASPM be enabled on this device, but 113262306a36Sopenharmony_ci * if we don't have permission to manage ASPM (e.g., on ACPI 113362306a36Sopenharmony_ci * systems we have to observe the FADT ACPI_FADT_NO_ASPM bit and 113462306a36Sopenharmony_ci * the _OSC method), we can't honor that request. 113562306a36Sopenharmony_ci */ 113662306a36Sopenharmony_ci if (aspm_disabled) { 113762306a36Sopenharmony_ci pci_warn(pdev, "can't override BIOS ASPM; OS doesn't have ASPM control\n"); 113862306a36Sopenharmony_ci return -EPERM; 113962306a36Sopenharmony_ci } 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci if (!locked) 114262306a36Sopenharmony_ci down_read(&pci_bus_sem); 114362306a36Sopenharmony_ci mutex_lock(&aspm_lock); 114462306a36Sopenharmony_ci link->aspm_default = 0; 114562306a36Sopenharmony_ci if (state & PCIE_LINK_STATE_L0S) 114662306a36Sopenharmony_ci link->aspm_default |= ASPM_STATE_L0S; 114762306a36Sopenharmony_ci if (state & PCIE_LINK_STATE_L1) 114862306a36Sopenharmony_ci link->aspm_default |= ASPM_STATE_L1; 114962306a36Sopenharmony_ci /* L1 PM substates require L1 */ 115062306a36Sopenharmony_ci if (state & PCIE_LINK_STATE_L1_1) 115162306a36Sopenharmony_ci link->aspm_default |= ASPM_STATE_L1_1 | ASPM_STATE_L1; 115262306a36Sopenharmony_ci if (state & PCIE_LINK_STATE_L1_2) 115362306a36Sopenharmony_ci link->aspm_default |= ASPM_STATE_L1_2 | ASPM_STATE_L1; 115462306a36Sopenharmony_ci if (state & PCIE_LINK_STATE_L1_1_PCIPM) 115562306a36Sopenharmony_ci link->aspm_default |= ASPM_STATE_L1_1_PCIPM | ASPM_STATE_L1; 115662306a36Sopenharmony_ci if (state & PCIE_LINK_STATE_L1_2_PCIPM) 115762306a36Sopenharmony_ci link->aspm_default |= ASPM_STATE_L1_2_PCIPM | ASPM_STATE_L1; 115862306a36Sopenharmony_ci pcie_config_aspm_link(link, policy_to_aspm_state(link)); 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci link->clkpm_default = (state & PCIE_LINK_STATE_CLKPM) ? 1 : 0; 116162306a36Sopenharmony_ci pcie_set_clkpm(link, policy_to_clkpm_state(link)); 116262306a36Sopenharmony_ci mutex_unlock(&aspm_lock); 116362306a36Sopenharmony_ci if (!locked) 116462306a36Sopenharmony_ci up_read(&pci_bus_sem); 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci return 0; 116762306a36Sopenharmony_ci} 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci/** 117062306a36Sopenharmony_ci * pci_enable_link_state - Clear and set the default device link state so that 117162306a36Sopenharmony_ci * the link may be allowed to enter the specified states. Note that if the 117262306a36Sopenharmony_ci * BIOS didn't grant ASPM control to the OS, this does nothing because we can't 117362306a36Sopenharmony_ci * touch the LNKCTL register. Also note that this does not enable states 117462306a36Sopenharmony_ci * disabled by pci_disable_link_state(). Return 0 or a negative errno. 117562306a36Sopenharmony_ci * 117662306a36Sopenharmony_ci * @pdev: PCI device 117762306a36Sopenharmony_ci * @state: Mask of ASPM link states to enable 117862306a36Sopenharmony_ci */ 117962306a36Sopenharmony_ciint pci_enable_link_state(struct pci_dev *pdev, int state) 118062306a36Sopenharmony_ci{ 118162306a36Sopenharmony_ci return __pci_enable_link_state(pdev, state, false); 118262306a36Sopenharmony_ci} 118362306a36Sopenharmony_ciEXPORT_SYMBOL(pci_enable_link_state); 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci/** 118662306a36Sopenharmony_ci * pci_enable_link_state_locked - Clear and set the default device link state 118762306a36Sopenharmony_ci * so that the link may be allowed to enter the specified states. Note that if 118862306a36Sopenharmony_ci * the BIOS didn't grant ASPM control to the OS, this does nothing because we 118962306a36Sopenharmony_ci * can't touch the LNKCTL register. Also note that this does not enable states 119062306a36Sopenharmony_ci * disabled by pci_disable_link_state(). Return 0 or a negative errno. 119162306a36Sopenharmony_ci * 119262306a36Sopenharmony_ci * @pdev: PCI device 119362306a36Sopenharmony_ci * @state: Mask of ASPM link states to enable 119462306a36Sopenharmony_ci * 119562306a36Sopenharmony_ci * Context: Caller holds pci_bus_sem read lock. 119662306a36Sopenharmony_ci */ 119762306a36Sopenharmony_ciint pci_enable_link_state_locked(struct pci_dev *pdev, int state) 119862306a36Sopenharmony_ci{ 119962306a36Sopenharmony_ci lockdep_assert_held_read(&pci_bus_sem); 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci return __pci_enable_link_state(pdev, state, true); 120262306a36Sopenharmony_ci} 120362306a36Sopenharmony_ciEXPORT_SYMBOL(pci_enable_link_state_locked); 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_cistatic int pcie_aspm_set_policy(const char *val, 120662306a36Sopenharmony_ci const struct kernel_param *kp) 120762306a36Sopenharmony_ci{ 120862306a36Sopenharmony_ci int i; 120962306a36Sopenharmony_ci struct pcie_link_state *link; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci if (aspm_disabled) 121262306a36Sopenharmony_ci return -EPERM; 121362306a36Sopenharmony_ci i = sysfs_match_string(policy_str, val); 121462306a36Sopenharmony_ci if (i < 0) 121562306a36Sopenharmony_ci return i; 121662306a36Sopenharmony_ci if (i == aspm_policy) 121762306a36Sopenharmony_ci return 0; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci down_read(&pci_bus_sem); 122062306a36Sopenharmony_ci mutex_lock(&aspm_lock); 122162306a36Sopenharmony_ci aspm_policy = i; 122262306a36Sopenharmony_ci list_for_each_entry(link, &link_list, sibling) { 122362306a36Sopenharmony_ci pcie_config_aspm_link(link, policy_to_aspm_state(link)); 122462306a36Sopenharmony_ci pcie_set_clkpm(link, policy_to_clkpm_state(link)); 122562306a36Sopenharmony_ci } 122662306a36Sopenharmony_ci mutex_unlock(&aspm_lock); 122762306a36Sopenharmony_ci up_read(&pci_bus_sem); 122862306a36Sopenharmony_ci return 0; 122962306a36Sopenharmony_ci} 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_cistatic int pcie_aspm_get_policy(char *buffer, const struct kernel_param *kp) 123262306a36Sopenharmony_ci{ 123362306a36Sopenharmony_ci int i, cnt = 0; 123462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(policy_str); i++) 123562306a36Sopenharmony_ci if (i == aspm_policy) 123662306a36Sopenharmony_ci cnt += sprintf(buffer + cnt, "[%s] ", policy_str[i]); 123762306a36Sopenharmony_ci else 123862306a36Sopenharmony_ci cnt += sprintf(buffer + cnt, "%s ", policy_str[i]); 123962306a36Sopenharmony_ci cnt += sprintf(buffer + cnt, "\n"); 124062306a36Sopenharmony_ci return cnt; 124162306a36Sopenharmony_ci} 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_cimodule_param_call(policy, pcie_aspm_set_policy, pcie_aspm_get_policy, 124462306a36Sopenharmony_ci NULL, 0644); 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci/** 124762306a36Sopenharmony_ci * pcie_aspm_enabled - Check if PCIe ASPM has been enabled for a device. 124862306a36Sopenharmony_ci * @pdev: Target device. 124962306a36Sopenharmony_ci * 125062306a36Sopenharmony_ci * Relies on the upstream bridge's link_state being valid. The link_state 125162306a36Sopenharmony_ci * is deallocated only when the last child of the bridge (i.e., @pdev or a 125262306a36Sopenharmony_ci * sibling) is removed, and the caller should be holding a reference to 125362306a36Sopenharmony_ci * @pdev, so this should be safe. 125462306a36Sopenharmony_ci */ 125562306a36Sopenharmony_cibool pcie_aspm_enabled(struct pci_dev *pdev) 125662306a36Sopenharmony_ci{ 125762306a36Sopenharmony_ci struct pcie_link_state *link = pcie_aspm_get_link(pdev); 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci if (!link) 126062306a36Sopenharmony_ci return false; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci return link->aspm_enabled; 126362306a36Sopenharmony_ci} 126462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pcie_aspm_enabled); 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_cistatic ssize_t aspm_attr_show_common(struct device *dev, 126762306a36Sopenharmony_ci struct device_attribute *attr, 126862306a36Sopenharmony_ci char *buf, u8 state) 126962306a36Sopenharmony_ci{ 127062306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 127162306a36Sopenharmony_ci struct pcie_link_state *link = pcie_aspm_get_link(pdev); 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", (link->aspm_enabled & state) ? 1 : 0); 127462306a36Sopenharmony_ci} 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_cistatic ssize_t aspm_attr_store_common(struct device *dev, 127762306a36Sopenharmony_ci struct device_attribute *attr, 127862306a36Sopenharmony_ci const char *buf, size_t len, u8 state) 127962306a36Sopenharmony_ci{ 128062306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 128162306a36Sopenharmony_ci struct pcie_link_state *link = pcie_aspm_get_link(pdev); 128262306a36Sopenharmony_ci bool state_enable; 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci if (kstrtobool(buf, &state_enable) < 0) 128562306a36Sopenharmony_ci return -EINVAL; 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci down_read(&pci_bus_sem); 128862306a36Sopenharmony_ci mutex_lock(&aspm_lock); 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci if (state_enable) { 129162306a36Sopenharmony_ci link->aspm_disable &= ~state; 129262306a36Sopenharmony_ci /* need to enable L1 for substates */ 129362306a36Sopenharmony_ci if (state & ASPM_STATE_L1SS) 129462306a36Sopenharmony_ci link->aspm_disable &= ~ASPM_STATE_L1; 129562306a36Sopenharmony_ci } else { 129662306a36Sopenharmony_ci link->aspm_disable |= state; 129762306a36Sopenharmony_ci if (state & ASPM_STATE_L1) 129862306a36Sopenharmony_ci link->aspm_disable |= ASPM_STATE_L1SS; 129962306a36Sopenharmony_ci } 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci pcie_config_aspm_link(link, policy_to_aspm_state(link)); 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci mutex_unlock(&aspm_lock); 130462306a36Sopenharmony_ci up_read(&pci_bus_sem); 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci return len; 130762306a36Sopenharmony_ci} 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci#define ASPM_ATTR(_f, _s) \ 131062306a36Sopenharmony_cistatic ssize_t _f##_show(struct device *dev, \ 131162306a36Sopenharmony_ci struct device_attribute *attr, char *buf) \ 131262306a36Sopenharmony_ci{ return aspm_attr_show_common(dev, attr, buf, ASPM_STATE_##_s); } \ 131362306a36Sopenharmony_ci \ 131462306a36Sopenharmony_cistatic ssize_t _f##_store(struct device *dev, \ 131562306a36Sopenharmony_ci struct device_attribute *attr, \ 131662306a36Sopenharmony_ci const char *buf, size_t len) \ 131762306a36Sopenharmony_ci{ return aspm_attr_store_common(dev, attr, buf, len, ASPM_STATE_##_s); } 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ciASPM_ATTR(l0s_aspm, L0S) 132062306a36Sopenharmony_ciASPM_ATTR(l1_aspm, L1) 132162306a36Sopenharmony_ciASPM_ATTR(l1_1_aspm, L1_1) 132262306a36Sopenharmony_ciASPM_ATTR(l1_2_aspm, L1_2) 132362306a36Sopenharmony_ciASPM_ATTR(l1_1_pcipm, L1_1_PCIPM) 132462306a36Sopenharmony_ciASPM_ATTR(l1_2_pcipm, L1_2_PCIPM) 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_cistatic ssize_t clkpm_show(struct device *dev, 132762306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 132862306a36Sopenharmony_ci{ 132962306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 133062306a36Sopenharmony_ci struct pcie_link_state *link = pcie_aspm_get_link(pdev); 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", link->clkpm_enabled); 133362306a36Sopenharmony_ci} 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_cistatic ssize_t clkpm_store(struct device *dev, 133662306a36Sopenharmony_ci struct device_attribute *attr, 133762306a36Sopenharmony_ci const char *buf, size_t len) 133862306a36Sopenharmony_ci{ 133962306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 134062306a36Sopenharmony_ci struct pcie_link_state *link = pcie_aspm_get_link(pdev); 134162306a36Sopenharmony_ci bool state_enable; 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci if (kstrtobool(buf, &state_enable) < 0) 134462306a36Sopenharmony_ci return -EINVAL; 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci down_read(&pci_bus_sem); 134762306a36Sopenharmony_ci mutex_lock(&aspm_lock); 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci link->clkpm_disable = !state_enable; 135062306a36Sopenharmony_ci pcie_set_clkpm(link, policy_to_clkpm_state(link)); 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci mutex_unlock(&aspm_lock); 135362306a36Sopenharmony_ci up_read(&pci_bus_sem); 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci return len; 135662306a36Sopenharmony_ci} 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_cistatic DEVICE_ATTR_RW(clkpm); 135962306a36Sopenharmony_cistatic DEVICE_ATTR_RW(l0s_aspm); 136062306a36Sopenharmony_cistatic DEVICE_ATTR_RW(l1_aspm); 136162306a36Sopenharmony_cistatic DEVICE_ATTR_RW(l1_1_aspm); 136262306a36Sopenharmony_cistatic DEVICE_ATTR_RW(l1_2_aspm); 136362306a36Sopenharmony_cistatic DEVICE_ATTR_RW(l1_1_pcipm); 136462306a36Sopenharmony_cistatic DEVICE_ATTR_RW(l1_2_pcipm); 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_cistatic struct attribute *aspm_ctrl_attrs[] = { 136762306a36Sopenharmony_ci &dev_attr_clkpm.attr, 136862306a36Sopenharmony_ci &dev_attr_l0s_aspm.attr, 136962306a36Sopenharmony_ci &dev_attr_l1_aspm.attr, 137062306a36Sopenharmony_ci &dev_attr_l1_1_aspm.attr, 137162306a36Sopenharmony_ci &dev_attr_l1_2_aspm.attr, 137262306a36Sopenharmony_ci &dev_attr_l1_1_pcipm.attr, 137362306a36Sopenharmony_ci &dev_attr_l1_2_pcipm.attr, 137462306a36Sopenharmony_ci NULL 137562306a36Sopenharmony_ci}; 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_cistatic umode_t aspm_ctrl_attrs_are_visible(struct kobject *kobj, 137862306a36Sopenharmony_ci struct attribute *a, int n) 137962306a36Sopenharmony_ci{ 138062306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 138162306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 138262306a36Sopenharmony_ci struct pcie_link_state *link = pcie_aspm_get_link(pdev); 138362306a36Sopenharmony_ci static const u8 aspm_state_map[] = { 138462306a36Sopenharmony_ci ASPM_STATE_L0S, 138562306a36Sopenharmony_ci ASPM_STATE_L1, 138662306a36Sopenharmony_ci ASPM_STATE_L1_1, 138762306a36Sopenharmony_ci ASPM_STATE_L1_2, 138862306a36Sopenharmony_ci ASPM_STATE_L1_1_PCIPM, 138962306a36Sopenharmony_ci ASPM_STATE_L1_2_PCIPM, 139062306a36Sopenharmony_ci }; 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci if (aspm_disabled || !link) 139362306a36Sopenharmony_ci return 0; 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci if (n == 0) 139662306a36Sopenharmony_ci return link->clkpm_capable ? a->mode : 0; 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci return link->aspm_capable & aspm_state_map[n - 1] ? a->mode : 0; 139962306a36Sopenharmony_ci} 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ciconst struct attribute_group aspm_ctrl_attr_group = { 140262306a36Sopenharmony_ci .name = "link", 140362306a36Sopenharmony_ci .attrs = aspm_ctrl_attrs, 140462306a36Sopenharmony_ci .is_visible = aspm_ctrl_attrs_are_visible, 140562306a36Sopenharmony_ci}; 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_cistatic int __init pcie_aspm_disable(char *str) 140862306a36Sopenharmony_ci{ 140962306a36Sopenharmony_ci if (!strcmp(str, "off")) { 141062306a36Sopenharmony_ci aspm_policy = POLICY_DEFAULT; 141162306a36Sopenharmony_ci aspm_disabled = 1; 141262306a36Sopenharmony_ci aspm_support_enabled = false; 141362306a36Sopenharmony_ci printk(KERN_INFO "PCIe ASPM is disabled\n"); 141462306a36Sopenharmony_ci } else if (!strcmp(str, "force")) { 141562306a36Sopenharmony_ci aspm_force = 1; 141662306a36Sopenharmony_ci printk(KERN_INFO "PCIe ASPM is forcibly enabled\n"); 141762306a36Sopenharmony_ci } 141862306a36Sopenharmony_ci return 1; 141962306a36Sopenharmony_ci} 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci__setup("pcie_aspm=", pcie_aspm_disable); 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_civoid pcie_no_aspm(void) 142462306a36Sopenharmony_ci{ 142562306a36Sopenharmony_ci /* 142662306a36Sopenharmony_ci * Disabling ASPM is intended to prevent the kernel from modifying 142762306a36Sopenharmony_ci * existing hardware state, not to clear existing state. To that end: 142862306a36Sopenharmony_ci * (a) set policy to POLICY_DEFAULT in order to avoid changing state 142962306a36Sopenharmony_ci * (b) prevent userspace from changing policy 143062306a36Sopenharmony_ci */ 143162306a36Sopenharmony_ci if (!aspm_force) { 143262306a36Sopenharmony_ci aspm_policy = POLICY_DEFAULT; 143362306a36Sopenharmony_ci aspm_disabled = 1; 143462306a36Sopenharmony_ci } 143562306a36Sopenharmony_ci} 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_cibool pcie_aspm_support_enabled(void) 143862306a36Sopenharmony_ci{ 143962306a36Sopenharmony_ci return aspm_support_enabled; 144062306a36Sopenharmony_ci} 1441