PCI: vmd: Enable PCI PM's L1 substates of remapped PCIe Root Port and NVMe
Subsystems (3)
v7 cover PCI: vmd: Enable PCI PM's L1 substates of remapped PCIe Root Port and NVMe
Notice the VMD remapped PCIe Root Port and NVMe have PCI PM L1 substates capability, but they are disabled originally.
Here is a failed example on ASUS B1400CEAE with enabled VMD:
10000:e0:06.0 PCI bridge: Intel Corporation 11th Gen Core Processor PCIe Controller (rev 01) (prog-if 00 [Normal decode])
...
Capabilities: [200 v1] L1 PM Substates
L1SubCap: PCI-PM_L1.2+ PCI-PM_L1.1+ ASPM_L1.2+ ASPM_L1.1+ L1_PM_Substates+
PortCommonModeRestoreTime=45us PortTPowerOnTime=50us
L1SubCtl1: PCI-PM_L1.2- PCI-PM_L1.1- ASPM_L1.2+ ASPM_L1.1-
T_CommonMode=45us LTR1.2_Threshold=101376ns
L1SubCtl2: T_PwrOn=50us10000:e1:00.0 Non-Volatile memory controller: Sandisk Corp WD Blue SN550 NVMe SSD (rev 01) (prog-if 02 [NVM Express])
...
Capabilities: [900 v1] L1 PM Substates
L1SubCap: PCI-PM_L1.2+ PCI-PM_L1.1- ASPM_L1.2+ ASPM_L1.1- L1_PM_Substates+
PortCommonModeRestoreTime=32us PortTPowerOnTime=10us
L1SubCtl1: PCI-PM_L1.2- PCI-PM_L1.1- ASPM_L1.2+ ASPM_L1.1-
T_CommonMode=0us LTR1.2_Threshold=0ns
L1SubCtl2: T_PwrOn=10usAccording to "PCIe r6.0, sec 5.5.4", to config the link between the PCIe Root Port and the child device correctly:
* Ensure both devices are in D0 before enabling PCI-PM L1 PM Substates.
* Ensure L1.2 parameters: Common_Mode_Restore_Times, T_POWER_ON and LTR_L1.2_THRESHOLD are programmed properly on both devices before enable bits for L1.2.
Prepare this series to fix that.
Jian-Hong Pan (4):
PCI: vmd: Set PCI devices to D0 before enable PCI PM's L1 substates PCI/ASPM: Add notes about enabling PCI-PM L1SS to
pci_enable_link_state(_locked)
PCI/ASPM: Introduce aspm_get_l1ss_cap()
PCI/ASPM: Fix L1.2 parameters when enable link state
drivers/pci/controller/vmd.c | 13 ++++++++---- drivers/pci/pcie/aspm.c | 40 ++++++++++++++++++++++++++++-------- 2 files changed, 40 insertions(+), 13 deletions(-)
Signature · 1 line
2.45.2
PATCH v7 1/4 PCI: vmd: Set PCI devices to D0 before enable PCI PM's L1 substates
The remapped PCIe Root Port and the child device have PCI PM L1 substates capability, but they are disabled originally.
Here is a failed example on ASUS B1400CEAE:
Capabilities: [900 v1] L1 PM Substates
L1SubCap: PCI-PM_L1.2+ PCI-PM_L1.1- ASPM_L1.2+ ASPM_L1.1- L1_PM_Substates+
PortCommonModeRestoreTime=32us PortTPowerOnTime=10us
L1SubCtl1: PCI-PM_L1.2- PCI-PM_L1.1- ASPM_L1.2+ ASPM_L1.1-
T_CommonMode=0us LTR1.2_Threshold=0ns
L1SubCtl2: T_PwrOn=10usPower on all of the VMD remapped PCI devices to D0 before enable PCI-PM L1 PM Substates by following "PCIe r6.0, sec 5.5.4".
---
v2:
- Power on the VMD remapped devices with pci_set_power_state_locked()
- Prepare the PCIe LTR parameters before enable L1 Substates
- Add note into the comments of both pci_enable_link_state() and pci_enable_link_state_locked() for kernel-doc.
- The original patch set can be split as individual patches.
v3:
- Re-send for the missed version information.
- Split drivers/pci/pcie/aspm.c modification into following patches.
- Fix the comment for enasuring the PCI devices in D0.
v4:
- The same
v5:
- Tweak the commit title and message
- Change the goto label from out_enable_link_state to out_state_change
v6, v7:
- The same
drivers/pci/controller/vmd.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-)
drivers/pci/controller/vmd.c +9 -4
@@ -751,11 +751,9 @@ static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata)if (!(features &VMD_FEAT_BIOS_PM_QUIRK ))return 0 ; -pci_enable_link_state_locked (pdev,PCIE_LINK_STATE_ALL ); - pos =pci_find_ext_capability (pdev,PCI_EXT_CAP_ID_LTR );if (!pos) -return 0 ; + goto out_state_change;/* * Skip if the max snoop LTR is non-zero, indicating BIOS has set it
@@ -763,7 +761,7 @@ static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata)*/ 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 ; + goto out_state_change;/* * Set the default values to the maximum required by the platform to
@@ -775,6 +773,13 @@ static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata)pci_write_config_dword (pdev, pos +PCI_LTR_MAX_SNOOP_LAT , ltr_reg);pci_info (pdev,"VMD: Default LTR value set by driver\n" ); +out_state_change: +/* +* Ensure devices are in D0 before enabling PCI-PM L1 PM Substates, per +* PCIe r6.0, sec 5.5.4. +*/ +pci_set_power_state_locked (pdev,PCI_D0 ); +pci_enable_link_state_locked (pdev,PCIE_LINK_STATE_ALL );return 0 ; }
Signature · 1 line
2.45.2
PATCH v7 2/4 PCI/ASPM: Add notes about enabling PCI-PM L1SS to pci_enable_link_state(_locked)
In reply to Jian-Hong PanAccording to "PCIe r6.0, sec 5.5.4", add note about D0 requirement in pci_enable_link_state() kernel-doc.
---
v3:
- Fix as readable comments
v4:
- The same
v5:
- Tweak and simplify the commit message
v6, v7:
- The same
drivers/pci/pcie/aspm.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/pci/pcie/aspm.c +6 -0
@@ -1442,6 +1442,9 @@ static int __pci_enable_link_state(struct pci_dev *pdev, int state, bool locked)* touch the LNKCTL register. Also note that this does not enable states * disabled by pci_disable_link_state(). Return 0 or a negative errno. * +* Note: Ensure devices are in D0 before enabling PCI-PM L1 PM Substates, per +* PCIe r6.0, sec 5.5.4. +* * @pdev: PCI device * @state: Mask of ASPM link states to enable */
@@ -1458,6 +1461,9 @@ EXPORT_SYMBOL(pci_enable_link_state);* 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. * +* Note: Ensure devices are in D0 before enabling PCI-PM L1 PM Substates, per +* PCIe r6.0, sec 5.5.4. +* * @pdev: PCI device * @state: Mask of ASPM link states to enable *
Signature · 1 line
2.45.2
PATCH v7 3/4 PCI/ASPM: Introduce aspm_get_l1ss_cap()
In reply to Jian-Hong PanIntroduce aspm_get_l1ss_cap() which is extracted from aspm_l1ss_init() to get the PCIe's L1SS capability. This does not change any behavior, but aspm_get_l1ss_cap() can be reused later.
---
v6:
- Skipped
v7:
- Pick back
drivers/pci/pcie/aspm.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-)
drivers/pci/pcie/aspm.c +14 -9
@@ -611,6 +611,18 @@ static void pcie_aspm_check_latency(struct pci_dev *endpoint) } } +static u32 aspm_get_l1ss_cap (struct pci_dev *pdev) +{ +u32 l1ss_cap; + +pci_read_config_dword (pdev, pdev->l1ss +PCI_L1SS_CAP , &l1ss_cap); + +if (!(l1ss_cap &PCI_L1SS_CAP_L1_PM_SS )) + l1ss_cap =0 ; + +return l1ss_cap; +} +/* Calculate L1.2 PM substate timing parameters */ static void aspm_calc_l12_info (struct pcie_link_state *link,u32 parent_l1ss_cap,u32 child_l1ss_cap)
@@ -721,15 +733,8 @@ static void aspm_l1ss_init(struct pcie_link_state *link)return ;/* Setup L1 substate */ -pci_read_config_dword (parent, parent->l1ss +PCI_L1SS_CAP , - &parent_l1ss_cap); -pci_read_config_dword (child, child->l1ss +PCI_L1SS_CAP , - &child_l1ss_cap); - -if (!(parent_l1ss_cap &PCI_L1SS_CAP_L1_PM_SS )) - parent_l1ss_cap =0 ; -if (!(child_l1ss_cap &PCI_L1SS_CAP_L1_PM_SS )) - child_l1ss_cap =0 ; + parent_l1ss_cap =aspm_get_l1ss_cap (parent); + child_l1ss_cap =aspm_get_l1ss_cap (child);/* * If we don't have LTR for the entire path from the Root Complex
Signature · 1 line
2.45.2
PATCH v7 4/4 PCI/ASPM: Fix L1.2 parameters when enable link state
In reply to Jian-Hong PanCurrently, when enable link's L1.2 features with __pci_enable_link_state(), it configs the link directly without ensuring related L1.2 parameters, such as T_POWER_ON, Common_Mode_Restore_Time, and LTR_L1.2_THRESHOLD have been programmed.
This leads the link's L1.2 between PCIe Root Port and child device gets wrong configs when a caller tries to enabled it.
Here is a failed example on ASUS B1400CEAE with enabled VMD:
10000:e0:06.0 PCI bridge: Intel Corporation 11th Gen Core Processor PCIe Controller (rev 01) (prog-if 00 [Normal decode])
...
Capabilities: [200 v1] L1 PM Substates
L1SubCap: PCI-PM_L1.2+ PCI-PM_L1.1+ ASPM_L1.2+ ASPM_L1.1+ L1_PM_Substates+
PortCommonModeRestoreTime=45us PortTPowerOnTime=50us
L1SubCtl1: PCI-PM_L1.2- PCI-PM_L1.1- ASPM_L1.2+ ASPM_L1.1-
T_CommonMode=45us LTR1.2_Threshold=101376ns
L1SubCtl2: T_PwrOn=50us10000:e1:00.0 Non-Volatile memory controller: Sandisk Corp WD Blue SN550 NVMe SSD (rev 01) (prog-if 02 [NVM Express])
...
Capabilities: [900 v1] L1 PM Substates
L1SubCap: PCI-PM_L1.2+ PCI-PM_L1.1- ASPM_L1.2+ ASPM_L1.1- L1_PM_Substates+
PortCommonModeRestoreTime=32us PortTPowerOnTime=10us
L1SubCtl1: PCI-PM_L1.2- PCI-PM_L1.1- ASPM_L1.2+ ASPM_L1.1-
T_CommonMode=0us LTR1.2_Threshold=0ns
L1SubCtl2: T_PwrOn=10usAccording to "PCIe r6.0, sec 5.5.4", before enabling ASPM L1.2 on the PCIe Root Port and the child NVMe, they should be programmed with the same LTR1.2_Threshold value. However, they have different values in this case.
Invoke aspm_calc_l12_info() to program the L1.2 parameters properly before enable L1.2 bits of L1 PM Substates Control Register in __pci_enable_link_state().
---
v2:
- Prepare the PCIe LTR parameters before enable L1 Substates
v3:
- Only enable supported features for the L1 Substates part
v4:
- Focus on fixing L1.2 parameters, instead of re-initializing whole L1SS
v5:
- Fix typo and commit message
- Split introducing aspm_get_l1ss_cap() to "PCI/ASPM: Introduce aspm_get_l1ss_cap()"
v6:
- Skipped
v7:
- Pick back and rebase on the new version kernel
- Drop the link state flag check. And, always config link state's timing parameters
drivers/pci/pcie/aspm.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
drivers/pci/pcie/aspm.c +11 -0
@@ -1411,6 +1411,8 @@ EXPORT_SYMBOL(pci_disable_link_state);static int __pci_enable_link_state (struct pci_dev *pdev,int state,bool locked) {struct pcie_link_state *link =pcie_aspm_get_link (pdev); +struct pci_dev *child = link->downstream, *parent = link->pdev; +u32 parent_l1ss_cap, child_l1ss_cap;if (!link)return -EINVAL ;
@@ -1428,6 +1430,15 @@ static int __pci_enable_link_state(struct pci_dev *pdev, int state, bool locked)if (!locked)down_read (&pci_bus_sem);mutex_lock (&aspm_lock); +/* +* Ensure L1.2 parameters: Common_Mode_Restore_Times, T_POWER_ON and +* LTR_L1.2_THRESHOLD are programmed properly before enable bits for +* L1.2, per PCIe r6.0, sec 5.5.4. +*/ + parent_l1ss_cap =aspm_get_l1ss_cap (parent); + child_l1ss_cap =aspm_get_l1ss_cap (child); +aspm_calc_l12_info (link, parent_l1ss_cap, child_l1ss_cap); + link->aspm_default =pci_calc_aspm_enable_mask (state);pcie_config_aspm_link (link,policy_to_aspm_state (link));
Signature · 1 line
2.45.2