PCI: Fix the ACPI power state during runtime resume
PATCH 1/1 PCI: Fix the ACPI power state during runtime resume
Consider the following sequence during PCI device runtime suspend/resume:
1. PCI device goes into runtime suspended state. The PCI state will be changed to PCI_D0 and then pci_platform_power_transition() will be called which changes the ACPI state to ACPI_STATE_D3_HOT.
2. Parent bridge goes into runtime suspended state. If parent bridge supports D3cold, then it will change the power state of all its children to D3cold state and the power will be removed.
3. During wake-up time, the bridge will be runtime resumed first and pci_power_up() will be called for the bridge. Now, the power supply will be resumed.
4. pci_resume_bus() will be called which will internally invoke pci_restore_standard_config(). pci_update_current_state() will read PCI_PM_CTRL register and the current_state will be updated to D0.
In the above process, at step 4, the ACPI device state will still be ACPI_STATE_D3_HOT since pci_platform_power_transition() is not being invoked. We need call the pci_platform_power_transition() with state D0 to change the ACPI state to ACPI_STATE_D0.
This patch calls pci_power_up() if current power state is D0 inside pci_restore_standard_config(). This pci_power_up() will change the ACPI state to ACPI_STATE_D0.
Following are the steps to confirm:
Enable the debug prints in acpi_pci_set_power_state()
0000:01:00.0 is PCI device and 0000:00:01.0 is parent bridge device
Before:
0000:01:00.0: power state changed by ACPI to D3hot 0000:00:01.0: power state changed by ACPI to D3cold 0000:00:01.0: power state changed by ACPI to D0
After:
0000:01:00.0: power state changed by ACPI to D3hot 0000:00:01.0: power state changed by ACPI to D3cold 0000:00:01.0: power state changed by ACPI to D0 0000:01:00.0: power state changed by ACPI to D0
So with this patch, the PCI device ACPI state is also being changed to D0.
--- drivers/pci/pci-driver.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-)
drivers/pci/pci-driver.c +11 -3
@@ -521,14 +521,22 @@ static void pci_device_shutdown(struct device *dev)*/ static int pci_restore_standard_config (struct pci_dev *pci_dev) { +int error =0 ;pci_update_current_state (pci_dev,PCI_UNKNOWN );if (pci_dev->current_state !=PCI_D0 ) { -int error =pci_set_power_state (pci_dev,PCI_D0 ); -if (error) -return error; + error =pci_set_power_state (pci_dev,PCI_D0 ); + }else { +/* +* The platform power state can still be non-D0, so this is +* required to change the platform power state to D0. +*/ + error =pci_power_up (pci_dev); } +if (error) +return error; +pci_restore_state (pci_dev);pci_pme_restore (pci_dev);return 0 ;
Signature · 1 line
2.17.1
[+cc Rafael, hoping for your review :)
Wonder if we should add something like this to MAINTAINERS so you get cc'd on power-related things:
MAINTAINERS +1 -0
@@ -15422,6 +15422,7 @@ F: include/linux/pm.h F: include/linux/pm_* F: include/linux/powercap.h F: kernel/configs/nopm.config +K: pci_[a-z_]*power[a-z_]*\( DYNAMIC THERMAL POWER MANAGEMENT (DTPM) M: Daniel Lezcano <[email protected]>
]
Quoted 89 lines · Abhishek Sahu
Consider the following sequence during PCI device runtime suspend/resume:
1. PCI device goes into runtime suspended state. The PCI state will be changed to
PCI_D0and thenpci_platform_power_transition()will be called which changes the ACPI state toACPI_STATE_D3_HOT.2. Parent bridge goes into runtime suspended state. If parent bridge supports D3cold, then it will change the power state of all its children to D3cold state and the power will be removed.
3. During wake-up time, the bridge will be runtime resumed first and
pci_power_up()will be called for the bridge. Now, the power supply will be resumed.4.
pci_resume_bus()will be called which will internally invokepci_restore_standard_config().pci_update_current_state()will readPCI_PM_CTRLregister and thecurrent_statewill be updated to D0.In the above process, at step 4, the ACPI device state will still be
ACPI_STATE_D3_HOTsincepci_platform_power_transition()is not being invoked. We need call thepci_platform_power_transition()with state D0 to change the ACPI state toACPI_STATE_D0.This patch calls
pci_power_up()if current power state is D0 insidepci_restore_standard_config(). Thispci_power_up()will change the ACPI state toACPI_STATE_D0.Following are the steps to confirm:
Enable the debug prints in
acpi_pci_set_power_state()0000:01:00.0 is PCI device and 0000:00:01.0 is parent bridge device
Before:
0000:01:00.0: power state changed by ACPI to D3hot 0000:00:01.0: power state changed by ACPI to D3cold 0000:00:01.0: power state changed by ACPI to D0
After:
0000:01:00.0: power state changed by ACPI to D3hot 0000:00:01.0: power state changed by ACPI to D3cold 0000:00:01.0: power state changed by ACPI to D0 0000:01:00.0: power state changed by ACPI to D0
So with this patch, the PCI device ACPI state is also being changed to D0.
Signed-off-by Abhishek Sahu <[email protected]>--- drivers/pci/pci-driver.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-)drivers/pci/pci-driver.c +11
-3@@ -521,14 +521,22 @@ static void pci_device_shutdown(struct device *dev)*/ static int pci_restore_standard_config (struct pci_dev *pci_dev) { +int error =0 ;pci_update_current_state (pci_dev,PCI_UNKNOWN );if (pci_dev->current_state !=PCI_D0 ) { -int error =pci_set_power_state (pci_dev,PCI_D0 ); -if (error) -return error; + error =pci_set_power_state (pci_dev,PCI_D0 ); + }else { +/* +* The platform power state can still be non-D0, so this is +* required to change the platform power state to D0. +*/ + error =pci_power_up (pci_dev); } +if (error) +return error; +pci_restore_state (pci_dev);pci_pme_restore (pci_dev);return 0 ;Signature · 1 line
2.17.1
Quoted 1 line · CET Bjorn Helgaas
[+cc Rafael, hoping for your review :)
+Mika
MAINTAINERS F: include/linux/pm.h · line 15425
Quoted 12 lines
Wonder if we should add something like this to
MAINTAINERSso you get cc'd on power-related things:MAINTAINERS +1
-0@@ -15422,6 +15422,7 @@ F: include/linux/pm.h F: include/linux/pm_* F: include/linux/powercap.h F: kernel/configs/nopm.config +K: pci_[a-z_]*power[a-z_]*\(
It seems so, but generally PM patches should be CCed to linux-pm anyway.
Quoted 5 lines
MAINTAINERS +0
-0DYNAMIC THERMAL POWER MANAGEMENT (DTPM) M: Daniel Lezcano <[email protected]>]
Quoted 7 lines
Quoted 6 lines · Abhishek Sahu
Consider the following sequence during PCI device runtime suspend/resume:
1. PCI device goes into runtime suspended state. The PCI state will be changed to
PCI_D0and thenpci_platform_power_transition()will be called which changes the ACPI state toACPI_STATE_D3_HOT.
You mean PCI_D3hot I suppose?
Quoted 8 lines
Quoted 8 lines
2. Parent bridge goes into runtime suspended state. If parent bridge supports D3cold, then it will change the power state of all its children to D3cold state and the power will be removed.
3. During wake-up time, the bridge will be runtime resumed first and
pci_power_up()will be called for the bridge. Now, the power supply will be resumed.
Quoted 8 lines
Quoted 8 lines
4.
pci_resume_bus()will be called which will internally invokepci_restore_standard_config().pci_update_current_state()will readPCI_PM_CTRLregister and thecurrent_statewill be updated to D0.In the above process, at step 4, the ACPI device state will still be
ACPI_STATE_D3_HOTsincepci_platform_power_transition()is not being invoked.
I'm not quite following.
I'm assuming that this description applies to the endpoint device that was previously put into D3_hot.
Since its current state is D3_hot, it is not D0 (in particular) and the pci_set_power_state() in pci_restore_standard_config() should put int into D0 proper, including the platform firmware part.
Quoted 45 lines
Quoted 45 lines
We need call the
pci_platform_power_transition()with state D0 to change the ACPI state toACPI_STATE_D0.This patch calls
pci_power_up()if current power state is D0 insidepci_restore_standard_config(). Thispci_power_up()will change the ACPI state toACPI_STATE_D0.Following are the steps to confirm:
Enable the debug prints in
acpi_pci_set_power_state()0000:01:00.0 is PCI device and 0000:00:01.0 is parent bridge device
Before:
0000:01:00.0: power state changed by ACPI to D3hot 0000:00:01.0: power state changed by ACPI to D3cold 0000:00:01.0: power state changed by ACPI to D0
After:
0000:01:00.0: power state changed by ACPI to D3hot 0000:00:01.0: power state changed by ACPI to D3cold 0000:00:01.0: power state changed by ACPI to D0 0000:01:00.0: power state changed by ACPI to D0
So with this patch, the PCI device ACPI state is also being changed to D0.
Signed-off-by Abhishek Sahu <[email protected]>--- drivers/pci/pci-driver.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-)drivers/pci/pci-driver.c +1
-0@@ -521,14 +521,22 @@ static void pci_device_shutdown(struct device *dev)*/ static int pci_restore_standard_config (struct pci_dev *pci_dev) { +int error =0 ;pci_update_current_state (pci_dev,PCI_UNKNOWN );
Quoted 10 lines
drivers/pci/pci-driver.c static void pci_device_shutdown(struct device *dev) · line 533
Quoted 10 lines
drivers/pci/pci-driver.c +6
-3if (pci_dev->current_state !=PCI_D0 ) { -int error =pci_set_power_state (pci_dev,PCI_D0 ); -if (error) -return error; + error =pci_set_power_state (pci_dev,PCI_D0 ); + }else { +/* +* The platform power state can still be non-D0, so this is +* required to change the platform power state to D0. +*/
This really isn't expected to happen.
If the device's power state has been changed to D3hot by ACPI, it is not in D0.
It looks like the state tracking is not working here.
Quoted 10 lines
Quoted 9 lines
drivers/pci/pci-driver.c +4
-0+ error =pci_power_up (pci_dev); } +if (error) +return error; +pci_restore_state (pci_dev);pci_pme_restore (pci_dev);return 0 ;
Quoted 4 lines · Rafael J. Wysocki
Quoted 3 lines · CET Bjorn Helgaas
Wonder if we should add something like this to
MAINTAINERSso you get cc'd on power-related things:
Quoted 11 lines
MAINTAINERS F: include/linux/pm.h · line 15425
Quoted 9 lines
MAINTAINERS +1
-0@@ -15422,6 +15422,7 @@ F: include/linux/pm.h F: include/linux/pm_* F: include/linux/powercap.h F: kernel/configs/nopm.config +K: pci_[a-z_]*power[a-z_]*\(It seems so, but generally PM patches should be CCed to linux-pm anyway.
Definitely. It's just that running get_maintainer.pl on this patch currently shows:
Bjorn Helgaas <[email protected]> (supporter:PCI SUBSYSTEM) [email protected] (open list:PCI SUBSYSTEM) [email protected] (open list)
so it's not as helpful as it could be. The MAINTAINERS patch above would change it to this:
Bjorn Helgaas <[email protected]> (supporter:PCI SUBSYSTEM) "Rafael J. Wysocki" <[email protected]> (supporter:POWER MANAGEMENT CORE) [email protected] (open list:PCI SUBSYSTEM) [email protected] (open list) [email protected] (open list:POWER MANAGEMENT CORE)
Bjorn
Quoted 26 lines · Rafael J. Wysocki
Quoted 1 line · CET Bjorn Helgaas
[+cc Rafael, hoping for your review :)
+Mika
MAINTAINERS F: include/linux/pm.h · line 15425
Quoted 12 lines
Wonder if we should add something like this to
MAINTAINERSso you get cc'd on power-related things:MAINTAINERS +1
-0@@ -15422,6 +15422,7 @@ F: include/linux/pm.h F: include/linux/pm_* F: include/linux/powercap.h F: kernel/configs/nopm.config +K: pci_[a-z_]*power[a-z_]*\(It seems so, but generally PM patches should be CCed to linux-pm anyway.
Quoted 6 lines
MAINTAINERS +0
-0DYNAMIC THERMAL POWER MANAGEMENT (DTPM) M: Daniel Lezcano <[email protected]>]
On Mon, Jan 24, 2022 at 05:51:07PM +0530, Abhishek Sahu wrote:
Quoted 9 lines
Quoted 6 lines
Quoted 6 lines
Consider the following sequence during PCI device runtime suspend/resume:
1. PCI device goes into runtime suspended state. The PCI state will be changed to
PCI_D0and thenpci_platform_power_transition()will be called which changes the ACPI state toACPI_STATE_D3_HOT.You mean
PCI_D3hotI suppose?
Yes. It should be PCI_D3hot here.
Quoted 13 lines
Quoted 13 lines
Quoted 13 lines
2. Parent bridge goes into runtime suspended state. If parent bridge supports D3cold, then it will change the power state of all its children to D3cold state and the power will be removed.
3. During wake-up time, the bridge will be runtime resumed first and
pci_power_up()will be called for the bridge. Now, the power supply will be resumed.4.
pci_resume_bus()will be called which will internally invokepci_restore_standard_config().pci_update_current_state()will readPCI_PM_CTRLregister and thecurrent_statewill be updated to D0.
Quoted 9 lines
Quoted 3 lines
Quoted 3 lines
In the above process, at step 4, the ACPI device state will still be
ACPI_STATE_D3_HOTsincepci_platform_power_transition()is not being invoked.I'm not quite following.
I'm assuming that this description applies to the endpoint device that was previously put into
D3_hot.
Yes. This is applicable for endpoint devices which was previously put into D3hot.
Quoted 4 lines
Since its current state is
D3_hot, it is not D0 (in particular) and thepci_set_power_state()inpci_restore_standard_config()should put int into D0 proper, including the platform firmware part.
The pci_restore_standard_config() for endpoint devices are being called internally during wake-up of upstream bridge.
pci_power_up(struct pci_dev *dev)
{
...
if (dev->runtime_d3cold) {
/*
* When powering on a bridge from D3cold, the whole hierarchy
* may be powered on into D0uninitialized state, resume them to
* give them a chance to suspend again
*/
pci_resume_bus(dev->subordinate);
}
...}
For the upstream bridge, the above code will trigger the wake-up of endpoint devices and then following code will be executed for the endpoint devices:
pci_update_current_state(struct pci_dev *dev, pci_power_t state)
{
if (platform_pci_get_power_state(dev) == PCI_D3cold ||
!pci_device_is_present(dev)) {
dev->current_state = PCI_D3cold;
} else if (dev->pm_cap) {
u16 pmcsr;
pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK);
} else {
dev->current_state = state;
}}
In the above code, the current_state will be set to D0 for the endpoint devices since it will go into second block where it will read the PM_CTRL register.
Quoted 58 lines
Quoted 45 lines
Quoted 45 lines
We need call the
pci_platform_power_transition()with state D0 to change the ACPI state toACPI_STATE_D0.This patch calls
pci_power_up()if current power state is D0 insidepci_restore_standard_config(). Thispci_power_up()will change the ACPI state toACPI_STATE_D0.Following are the steps to confirm:
Enable the debug prints in
acpi_pci_set_power_state()0000:01:00.0 is PCI device and 0000:00:01.0 is parent bridge device
Before:
0000:01:00.0: power state changed by ACPI to D3hot 0000:00:01.0: power state changed by ACPI to D3cold 0000:00:01.0: power state changed by ACPI to D0
After:
0000:01:00.0: power state changed by ACPI to D3hot 0000:00:01.0: power state changed by ACPI to D3cold 0000:00:01.0: power state changed by ACPI to D0 0000:01:00.0: power state changed by ACPI to D0
So with this patch, the PCI device ACPI state is also being changed to D0.
Signed-off-by Abhishek Sahu <[email protected]>--- drivers/pci/pci-driver.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-)drivers/pci/pci-driver.c +1
-0@@ -521,14 +521,22 @@ static void pci_device_shutdown(struct device *dev)*/ static int pci_restore_standard_config (struct pci_dev *pci_dev) { +int error =0 ;pci_update_current_state (pci_dev,PCI_UNKNOWN );Quoted 10 lines
drivers/pci/pci-driver.c static void pci_device_shutdown(struct device *dev) · line 533
Quoted 10 lines
drivers/pci/pci-driver.c +6
-3if (pci_dev->current_state !=PCI_D0 ) { -int error =pci_set_power_state (pci_dev,PCI_D0 ); -if (error) -return error; + error =pci_set_power_state (pci_dev,PCI_D0 ); + }else { +/* +* The platform power state can still be non-D0, so this is +* required to change the platform power state to D0. +*/ This really isn't expected to happen.
Quoted 4 lines
If the device's power state has been changed to D3hot by ACPI, it is not in D0.
It looks like the state tracking is not working here.
The state setting to D0 is happening due to the current logic present in pci_update_current_state(). If we can fix the logic in pci_update_current_state() to detect this condition and return state D3hot, then it should also fix the issue.
Thanks,
Abhishek
Quoted 14 lines
Quoted 10 lines
Quoted 9 lines
drivers/pci/pci-driver.c +4
-0+ error =pci_power_up (pci_dev); } +if (error) +return error; +pci_restore_state (pci_dev);pci_pme_restore (pci_dev);return 0 ;
Quoted 173 lines · Abhishek Sahu
Quoted 26 lines · Rafael J. Wysocki
Quoted 1 line · CET Bjorn Helgaas
[+cc Rafael, hoping for your review :)
+Mika
MAINTAINERS F: include/linux/pm.h · line 15425
Quoted 12 lines
Wonder if we should add something like this to
MAINTAINERSso you get cc'd on power-related things:MAINTAINERS +1
-0@@ -15422,6 +15422,7 @@ F: include/linux/pm.h F: include/linux/pm_* F: include/linux/powercap.h F: kernel/configs/nopm.config +K: pci_[a-z_]*power[a-z_]*\(It seems so, but generally PM patches should be CCed to linux-pm anyway.
Quoted 6 lines
MAINTAINERS +0
-0DYNAMIC THERMAL POWER MANAGEMENT (DTPM) M: Daniel Lezcano <[email protected]>]
On Mon, Jan 24, 2022 at 05:51:07PM +0530, Abhishek Sahu wrote:
Quoted 9 lines
Quoted 6 lines
Quoted 6 lines
Consider the following sequence during PCI device runtime suspend/resume:
1. PCI device goes into runtime suspended state. The PCI state will be changed to
PCI_D0and thenpci_platform_power_transition()will be called which changes the ACPI state toACPI_STATE_D3_HOT.You mean
PCI_D3hotI suppose?Yes. It should be
PCI_D3hothere.Quoted 13 lines
Quoted 13 lines
Quoted 13 lines
2. Parent bridge goes into runtime suspended state. If parent bridge supports D3cold, then it will change the power state of all its children to D3cold state and the power will be removed.
3. During wake-up time, the bridge will be runtime resumed first and
pci_power_up()will be called for the bridge. Now, the power supply will be resumed.4.
pci_resume_bus()will be called which will internally invokepci_restore_standard_config().pci_update_current_state()will readPCI_PM_CTRLregister and thecurrent_statewill be updated to D0.Quoted 9 lines
Quoted 3 lines
Quoted 3 lines
In the above process, at step 4, the ACPI device state will still be
ACPI_STATE_D3_HOTsincepci_platform_power_transition()is not being invoked.I'm not quite following.
I'm assuming that this description applies to the endpoint device that was previously put into
D3_hot.Yes. This is applicable for endpoint devices which was previously put into D3hot.
Quoted 4 lines
Since its current state is
D3_hot, it is not D0 (in particular) and thepci_set_power_state()inpci_restore_standard_config()should put int into D0 proper, including the platform firmware part.The
pci_restore_standard_config()for endpoint devices are being called internally during wake-up of upstream bridge.
pci_power_up(struct pci_dev *dev){ ... if (dev->runtime_d3cold) { /* * When powering on a bridge from D3cold, the whole hierarchy * may be powered on into D0uninitialized state, resume them to * give them a chance to suspend again */ pci_resume_bus(dev->subordinate); } ...}
For the upstream bridge, the above code will trigger the wake-up of endpoint devices and then following code will be executed for the endpoint devices:
pci_update_current_state(struct pci_dev *dev, pci_power_t state){ if (platform_pci_get_power_state(dev) == PCI_D3cold || !pci_device_is_present(dev)) { dev->current_state = PCI_D3cold; } else if (dev->pm_cap) { u16 pmcsr; pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr); dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK); } else { dev->current_state = state; }}
In the above code, the
current_statewill be set to D0 for the endpoint devices since it will go into second block where it will read thePM_CTRLregister.Quoted 58 lines
Quoted 45 lines
Quoted 45 lines
We need call the
pci_platform_power_transition()with state D0 to change the ACPI state toACPI_STATE_D0.This patch calls
pci_power_up()if current power state is D0 insidepci_restore_standard_config(). Thispci_power_up()will change the ACPI state toACPI_STATE_D0.Following are the steps to confirm:
Enable the debug prints in
acpi_pci_set_power_state()0000:01:00.0 is PCI device and 0000:00:01.0 is parent bridge device
Before:
0000:01:00.0: power state changed by ACPI to D3hot 0000:00:01.0: power state changed by ACPI to D3cold 0000:00:01.0: power state changed by ACPI to D0
After:
0000:01:00.0: power state changed by ACPI to D3hot 0000:00:01.0: power state changed by ACPI to D3cold 0000:00:01.0: power state changed by ACPI to D0 0000:01:00.0: power state changed by ACPI to D0
So with this patch, the PCI device ACPI state is also being changed to D0.
Signed-off-by Abhishek Sahu <[email protected]>--- drivers/pci/pci-driver.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-)drivers/pci/pci-driver.c +1
-0@@ -521,14 +521,22 @@ static void pci_device_shutdown(struct device *dev)*/ static int pci_restore_standard_config (struct pci_dev *pci_dev) { +int error =0 ;pci_update_current_state (pci_dev,PCI_UNKNOWN );Quoted 10 lines
drivers/pci/pci-driver.c static void pci_device_shutdown(struct device *dev) · line 533
Quoted 10 lines
drivers/pci/pci-driver.c +6
-3if (pci_dev->current_state !=PCI_D0 ) { -int error =pci_set_power_state (pci_dev,PCI_D0 ); -if (error) -return error; + error =pci_set_power_state (pci_dev,PCI_D0 ); + }else { +/* +* The platform power state can still be non-D0, so this is +* required to change the platform power state to D0. +*/ This really isn't expected to happen.
Quoted 4 lines
If the device's power state has been changed to D3hot by ACPI, it is not in D0.
It looks like the state tracking is not working here.
Quoted 8 lines
The state setting to D0 is happening due to the current logic present in
pci_update_current_state(). If we can fix the logic inpci_update_current_state()to detect this condition and return state D3hot, then it should also fix the issue.Thanks,
Abhishek
Hi Rafael/Mika,
Could you please help regarding the correct way to fix this issue. I can update the patch accordingly.
Thanks,
Abhishek
Quoted 15 lines
Quoted 14 lines
Quoted 10 lines
Quoted 9 lines
drivers/pci/pci-driver.c +4
-0+ error =pci_power_up (pci_dev); } +if (error) +return error; +pci_restore_state (pci_dev);pci_pme_restore (pci_dev);return 0 ;
Quoted 179 lines · CEST Abhishek Sahu
Quoted 178 lines · Abhishek Sahu
Quoted 26 lines · Rafael J. Wysocki
Quoted 1 line · CET Bjorn Helgaas
[+cc Rafael, hoping for your review :)
+Mika
MAINTAINERS F: include/linux/pm.h · line 15425
Quoted 12 lines
Wonder if we should add something like this to
MAINTAINERSso you get cc'd on power-related things:MAINTAINERS +1
-0@@ -15422,6 +15422,7 @@ F: include/linux/pm.h F: include/linux/pm_* F: include/linux/powercap.h F: kernel/configs/nopm.config +K: pci_[a-z_]*power[a-z_]*\(It seems so, but generally PM patches should be CCed to linux-pm anyway.
Quoted 6 lines
MAINTAINERS +0
-0DYNAMIC THERMAL POWER MANAGEMENT (DTPM) M: Daniel Lezcano <[email protected]>]
On Mon, Jan 24, 2022 at 05:51:07PM +0530, Abhishek Sahu wrote:
Quoted 9 lines
Quoted 6 lines
Quoted 6 lines
Consider the following sequence during PCI device runtime suspend/resume:
1. PCI device goes into runtime suspended state. The PCI state will be changed to
PCI_D0and thenpci_platform_power_transition()will be called which changes the ACPI state toACPI_STATE_D3_HOT.You mean
PCI_D3hotI suppose?Yes. It should be
PCI_D3hothere.Quoted 13 lines
Quoted 13 lines
Quoted 13 lines
2. Parent bridge goes into runtime suspended state. If parent bridge supports D3cold, then it will change the power state of all its children to D3cold state and the power will be removed.
3. During wake-up time, the bridge will be runtime resumed first and
pci_power_up()will be called for the bridge. Now, the power supply will be resumed.4.
pci_resume_bus()will be called which will internally invokepci_restore_standard_config().pci_update_current_state()will readPCI_PM_CTRLregister and thecurrent_statewill be updated to D0.Quoted 9 lines
Quoted 3 lines
Quoted 3 lines
In the above process, at step 4, the ACPI device state will still be
ACPI_STATE_D3_HOTsincepci_platform_power_transition()is not being invoked.I'm not quite following.
I'm assuming that this description applies to the endpoint device that was previously put into
D3_hot.Yes. This is applicable for endpoint devices which was previously put into D3hot.
Quoted 4 lines
Since its current state is
D3_hot, it is not D0 (in particular) and thepci_set_power_state()inpci_restore_standard_config()should put int into D0 proper, including the platform firmware part.The
pci_restore_standard_config()for endpoint devices are being called internally during wake-up of upstream bridge.
pci_power_up(struct pci_dev *dev){ ... if (dev->runtime_d3cold) { /* * When powering on a bridge from D3cold, the whole hierarchy * may be powered on into D0uninitialized state, resume them to * give them a chance to suspend again */ pci_resume_bus(dev->subordinate); } ...}
For the upstream bridge, the above code will trigger the wake-up of endpoint devices and then following code will be executed for the endpoint devices:
pci_update_current_state(struct pci_dev *dev, pci_power_t state){ if (platform_pci_get_power_state(dev) == PCI_D3cold || !pci_device_is_present(dev)) { dev->current_state = PCI_D3cold; } else if (dev->pm_cap) { u16 pmcsr; pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr); dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK); } else { dev->current_state = state; }}
In the above code, the
current_statewill be set to D0 for the endpoint devices since it will go into second block where it will read thePM_CTRLregister.Quoted 58 lines
Quoted 45 lines
Quoted 45 lines
We need call the
pci_platform_power_transition()with state D0 to change the ACPI state toACPI_STATE_D0.This patch calls
pci_power_up()if current power state is D0 insidepci_restore_standard_config(). Thispci_power_up()will change the ACPI state toACPI_STATE_D0.Following are the steps to confirm:
Enable the debug prints in
acpi_pci_set_power_state()0000:01:00.0 is PCI device and 0000:00:01.0 is parent bridge device
Before:
0000:01:00.0: power state changed by ACPI to D3hot 0000:00:01.0: power state changed by ACPI to D3cold 0000:00:01.0: power state changed by ACPI to D0
After:
0000:01:00.0: power state changed by ACPI to D3hot 0000:00:01.0: power state changed by ACPI to D3cold 0000:00:01.0: power state changed by ACPI to D0 0000:01:00.0: power state changed by ACPI to D0
So with this patch, the PCI device ACPI state is also being changed to D0.
Signed-off-by Abhishek Sahu <[email protected]>--- drivers/pci/pci-driver.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-)drivers/pci/pci-driver.c +1
-0@@ -521,14 +521,22 @@ static void pci_device_shutdown(struct device *dev)*/ static int pci_restore_standard_config (struct pci_dev *pci_dev) { +int error =0 ;pci_update_current_state (pci_dev,PCI_UNKNOWN );Quoted 10 lines
drivers/pci/pci-driver.c static void pci_device_shutdown(struct device *dev) · line 533
Quoted 10 lines
drivers/pci/pci-driver.c +6
-3if (pci_dev->current_state !=PCI_D0 ) { -int error =pci_set_power_state (pci_dev,PCI_D0 ); -if (error) -return error; + error =pci_set_power_state (pci_dev,PCI_D0 ); + }else { +/* +* The platform power state can still be non-D0, so this is +* required to change the platform power state to D0. +*/ This really isn't expected to happen.
Quoted 4 lines
If the device's power state has been changed to D3hot by ACPI, it is not in D0.
It looks like the state tracking is not working here.
The state setting to D0 is happening due to the current logic present in
pci_update_current_state(). If we can fix the logic inpci_update_current_state()to detect this condition and return state D3hot, then it should also fix the issue.
Quoted 8 lines
Quoted 3 lines
Thanks,
AbhishekHi Rafael/Mika,
Could you please help regarding the correct way to fix this issue. I can update the patch accordingly.
I think you can try one of the patches posted recently:
https://patchwork.kernel.org/project/linux-pm/patch/3623886.MHq7AAxBmi@kreacher/
Thanks!
Quoted 186 lines · Rafael J. Wysocki
Quoted 185 lines · CEST Abhishek Sahu
Quoted 173 lines · Abhishek Sahu
Quoted 26 lines · Rafael J. Wysocki
Quoted 1 line · CET Bjorn Helgaas
[+cc Rafael, hoping for your review :)
+Mika
MAINTAINERS F: include/linux/pm.h · line 15425
Quoted 12 lines
Wonder if we should add something like this to
MAINTAINERSso you get cc'd on power-related things:MAINTAINERS +1
-0@@ -15422,6 +15422,7 @@ F: include/linux/pm.h F: include/linux/pm_* F: include/linux/powercap.h F: kernel/configs/nopm.config +K: pci_[a-z_]*power[a-z_]*\(It seems so, but generally PM patches should be CCed to linux-pm anyway.
Quoted 6 lines
MAINTAINERS +0
-0DYNAMIC THERMAL POWER MANAGEMENT (DTPM) M: Daniel Lezcano <[email protected]>]
On Mon, Jan 24, 2022 at 05:51:07PM +0530, Abhishek Sahu wrote:
Quoted 9 lines
Quoted 6 lines
Quoted 6 lines
Consider the following sequence during PCI device runtime suspend/resume:
1. PCI device goes into runtime suspended state. The PCI state will be changed to
PCI_D0and thenpci_platform_power_transition()will be called which changes the ACPI state toACPI_STATE_D3_HOT.You mean
PCI_D3hotI suppose?Yes. It should be
PCI_D3hothere.Quoted 13 lines
Quoted 13 lines
Quoted 13 lines
2. Parent bridge goes into runtime suspended state. If parent bridge supports D3cold, then it will change the power state of all its children to D3cold state and the power will be removed.
3. During wake-up time, the bridge will be runtime resumed first and
pci_power_up()will be called for the bridge. Now, the power supply will be resumed.4.
pci_resume_bus()will be called which will internally invokepci_restore_standard_config().pci_update_current_state()will readPCI_PM_CTRLregister and thecurrent_statewill be updated to D0.Quoted 9 lines
Quoted 3 lines
Quoted 3 lines
In the above process, at step 4, the ACPI device state will still be
ACPI_STATE_D3_HOTsincepci_platform_power_transition()is not being invoked.I'm not quite following.
I'm assuming that this description applies to the endpoint device that was previously put into
D3_hot.Yes. This is applicable for endpoint devices which was previously put into D3hot.
Quoted 4 lines
Since its current state is
D3_hot, it is not D0 (in particular) and thepci_set_power_state()inpci_restore_standard_config()should put int into D0 proper, including the platform firmware part.The
pci_restore_standard_config()for endpoint devices are being called internally during wake-up of upstream bridge.
pci_power_up(struct pci_dev *dev){ ... if (dev->runtime_d3cold) { /* * When powering on a bridge from D3cold, the whole hierarchy * may be powered on into D0uninitialized state, resume them to * give them a chance to suspend again */ pci_resume_bus(dev->subordinate); } ...}
For the upstream bridge, the above code will trigger the wake-up of endpoint devices and then following code will be executed for the endpoint devices:
pci_update_current_state(struct pci_dev *dev, pci_power_t state){ if (platform_pci_get_power_state(dev) == PCI_D3cold || !pci_device_is_present(dev)) { dev->current_state = PCI_D3cold; } else if (dev->pm_cap) { u16 pmcsr; pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr); dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK); } else { dev->current_state = state; }}
In the above code, the
current_statewill be set to D0 for the endpoint devices since it will go into second block where it will read thePM_CTRLregister.Quoted 58 lines
Quoted 45 lines
Quoted 45 lines
We need call the
pci_platform_power_transition()with state D0 to change the ACPI state toACPI_STATE_D0.This patch calls
pci_power_up()if current power state is D0 insidepci_restore_standard_config(). Thispci_power_up()will change the ACPI state toACPI_STATE_D0.Following are the steps to confirm:
Enable the debug prints in
acpi_pci_set_power_state()0000:01:00.0 is PCI device and 0000:00:01.0 is parent bridge device
Before:
0000:01:00.0: power state changed by ACPI to D3hot 0000:00:01.0: power state changed by ACPI to D3cold 0000:00:01.0: power state changed by ACPI to D0
After:
0000:01:00.0: power state changed by ACPI to D3hot 0000:00:01.0: power state changed by ACPI to D3cold 0000:00:01.0: power state changed by ACPI to D0 0000:01:00.0: power state changed by ACPI to D0
So with this patch, the PCI device ACPI state is also being changed to D0.
Signed-off-by Abhishek Sahu <[email protected]>--- drivers/pci/pci-driver.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-)drivers/pci/pci-driver.c +1
-0@@ -521,14 +521,22 @@ static void pci_device_shutdown(struct device *dev)*/ static int pci_restore_standard_config (struct pci_dev *pci_dev) { +int error =0 ;pci_update_current_state (pci_dev,PCI_UNKNOWN );Quoted 10 lines
drivers/pci/pci-driver.c static void pci_device_shutdown(struct device *dev) · line 533
Quoted 10 lines
drivers/pci/pci-driver.c +6
-3if (pci_dev->current_state !=PCI_D0 ) { -int error =pci_set_power_state (pci_dev,PCI_D0 ); -if (error) -return error; + error =pci_set_power_state (pci_dev,PCI_D0 ); + }else { +/* +* The platform power state can still be non-D0, so this is +* required to change the platform power state to D0. +*/ This really isn't expected to happen.
Quoted 4 lines
If the device's power state has been changed to D3hot by ACPI, it is not in D0.
It looks like the state tracking is not working here.
Quoted 8 lines
The state setting to D0 is happening due to the current logic present in
pci_update_current_state(). If we can fix the logic inpci_update_current_state()to detect this condition and return state D3hot, then it should also fix the issue.Thanks,
AbhishekHi Rafael/Mika,
Quoted 11 lines
Quoted 2 lines
Could you please help regarding the correct way to fix this issue. I can update the patch accordingly.
I think you can try one of the patches posted recently:
Thanks!
Thanks Rafael.
I have applied both the changes and still the issue which I mentioned is happening.
Following are the prints:
0000:01:00.0: power state changed by ACPI to D3hot 0000:00:01.0: power state changed by ACPI to D3cold 0000:00:01.0: power state changed by ACPI to D0
So ACPI state change is still not happening for PCI endpoint devices.
Also, the I checked the code and the pci_power_up() will not be called for endpoint devices. For endpoints, pci_restore_standard_config() will be called first where the current state will come as D0.
Thanks,
Abhishek
Quoted 208 lines · CEST Abhishek Sahu
Quoted 186 lines · Rafael J. Wysocki
Quoted 185 lines · CEST Abhishek Sahu
Quoted 173 lines · Abhishek Sahu
Quoted 26 lines · Rafael J. Wysocki
Quoted 1 line · CET Bjorn Helgaas
[+cc Rafael, hoping for your review :)
+Mika
MAINTAINERS F: include/linux/pm.h · line 15425
Quoted 12 lines
Wonder if we should add something like this to
MAINTAINERSso you get cc'd on power-related things:MAINTAINERS +1
-0@@ -15422,6 +15422,7 @@ F: include/linux/pm.h F: include/linux/pm_* F: include/linux/powercap.h F: kernel/configs/nopm.config +K: pci_[a-z_]*power[a-z_]*\(It seems so, but generally PM patches should be CCed to linux-pm anyway.
Quoted 6 lines
MAINTAINERS +0
-0DYNAMIC THERMAL POWER MANAGEMENT (DTPM) M: Daniel Lezcano <[email protected]>]
On Mon, Jan 24, 2022 at 05:51:07PM +0530, Abhishek Sahu wrote:
Quoted 9 lines
Quoted 6 lines
Quoted 6 lines
Consider the following sequence during PCI device runtime suspend/resume:
1. PCI device goes into runtime suspended state. The PCI state will be changed to
PCI_D0and thenpci_platform_power_transition()will be called which changes the ACPI state toACPI_STATE_D3_HOT.You mean
PCI_D3hotI suppose?Yes. It should be
PCI_D3hothere.Quoted 13 lines
Quoted 13 lines
Quoted 13 lines
2. Parent bridge goes into runtime suspended state. If parent bridge supports D3cold, then it will change the power state of all its children to D3cold state and the power will be removed.
3. During wake-up time, the bridge will be runtime resumed first and
pci_power_up()will be called for the bridge. Now, the power supply will be resumed.4.
pci_resume_bus()will be called which will internally invokepci_restore_standard_config().pci_update_current_state()will readPCI_PM_CTRLregister and thecurrent_statewill be updated to D0.Quoted 9 lines
Quoted 3 lines
Quoted 3 lines
In the above process, at step 4, the ACPI device state will still be
ACPI_STATE_D3_HOTsincepci_platform_power_transition()is not being invoked.I'm not quite following.
I'm assuming that this description applies to the endpoint device that was previously put into
D3_hot.Yes. This is applicable for endpoint devices which was previously put into D3hot.
Quoted 4 lines
Since its current state is
D3_hot, it is not D0 (in particular) and thepci_set_power_state()inpci_restore_standard_config()should put int into D0 proper, including the platform firmware part.The
pci_restore_standard_config()for endpoint devices are being called internally during wake-up of upstream bridge.
pci_power_up(struct pci_dev *dev){ ... if (dev->runtime_d3cold) { /* * When powering on a bridge from D3cold, the whole hierarchy * may be powered on into D0uninitialized state, resume them to * give them a chance to suspend again */ pci_resume_bus(dev->subordinate); } ...}
For the upstream bridge, the above code will trigger the wake-up of endpoint devices and then following code will be executed for the endpoint devices:
pci_update_current_state(struct pci_dev *dev, pci_power_t state){ if (platform_pci_get_power_state(dev) == PCI_D3cold || !pci_device_is_present(dev)) { dev->current_state = PCI_D3cold; } else if (dev->pm_cap) { u16 pmcsr; pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr); dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK); } else { dev->current_state = state; }}
In the above code, the
current_statewill be set to D0 for the endpoint devices since it will go into second block where it will read thePM_CTRLregister.Quoted 58 lines
Quoted 45 lines
Quoted 45 lines
We need call the
pci_platform_power_transition()with state D0 to change the ACPI state toACPI_STATE_D0.This patch calls
pci_power_up()if current power state is D0 insidepci_restore_standard_config(). Thispci_power_up()will change the ACPI state toACPI_STATE_D0.Following are the steps to confirm:
Enable the debug prints in
acpi_pci_set_power_state()0000:01:00.0 is PCI device and 0000:00:01.0 is parent bridge device
Before:
0000:01:00.0: power state changed by ACPI to D3hot 0000:00:01.0: power state changed by ACPI to D3cold 0000:00:01.0: power state changed by ACPI to D0
After:
0000:01:00.0: power state changed by ACPI to D3hot 0000:00:01.0: power state changed by ACPI to D3cold 0000:00:01.0: power state changed by ACPI to D0 0000:01:00.0: power state changed by ACPI to D0
So with this patch, the PCI device ACPI state is also being changed to D0.
Signed-off-by Abhishek Sahu <[email protected]>--- drivers/pci/pci-driver.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-)drivers/pci/pci-driver.c +1
-0@@ -521,14 +521,22 @@ static void pci_device_shutdown(struct device *dev)*/ static int pci_restore_standard_config (struct pci_dev *pci_dev) { +int error =0 ;pci_update_current_state (pci_dev,PCI_UNKNOWN );Quoted 10 lines
drivers/pci/pci-driver.c static void pci_device_shutdown(struct device *dev) · line 533
Quoted 10 lines
drivers/pci/pci-driver.c +6
-3if (pci_dev->current_state !=PCI_D0 ) { -int error =pci_set_power_state (pci_dev,PCI_D0 ); -if (error) -return error; + error =pci_set_power_state (pci_dev,PCI_D0 ); + }else { +/* +* The platform power state can still be non-D0, so this is +* required to change the platform power state to D0. +*/ This really isn't expected to happen.
Quoted 4 lines
If the device's power state has been changed to D3hot by ACPI, it is not in D0.
It looks like the state tracking is not working here.
Quoted 8 lines
The state setting to D0 is happening due to the current logic present in
pci_update_current_state(). If we can fix the logic inpci_update_current_state()to detect this condition and return state D3hot, then it should also fix the issue.Thanks,
AbhishekHi Rafael/Mika,
Quoted 11 lines
Quoted 2 lines
Could you please help regarding the correct way to fix this issue. I can update the patch accordingly.
I think you can try one of the patches posted recently:
Thanks!
Thanks Rafael.
I have applied both the changes and still the issue which I mentioned is happening.Following are the prints:
0000:01:00.0: power state changed by ACPI to D3hot 0000:00:01.0: power state changed by ACPI to D3cold 0000:00:01.0: power state changed by ACPI to D0
Quoted 5 lines
So ACPI state change is still not happening for PCI endpoint devices.
Also, the I checked the code and the
pci_power_up()will not be called for endpoint devices. For endpoints,pci_restore_standard_config()will be called first where the current state will come as D0.
OK, I see.
The problem is that if the PCI device goes to D0 because of the bridge power-up, it's ACPI companion's power state may not follow, which means that we really want to do a full power-up in there.
Please test the appended patch with the patch from
https://patchwork.kernel.org/project/linux-pm/patch/3623886.MHq7AAxBmi@kreacher/
still applied.
--- drivers/pci/pci-driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/pci/pci-driver.c +1 -1
@@ -1312,7 +1312,7 @@ static int pci_pm_runtime_resume(struct* to a driver because although we left it in D0, it may have gone to * D3cold when the bridge above it runtime suspended. */ -pci_restore_standard_config (pci_dev); +pci_pm_default_resume_early (pci_dev);if (!pci_dev->driver)return 0 ;
Quoted 237 lines · Rafael J. Wysocki
Quoted 208 lines · CEST Abhishek Sahu
Quoted 186 lines · Rafael J. Wysocki
Quoted 185 lines · CEST Abhishek Sahu
Quoted 173 lines · Abhishek Sahu
Quoted 26 lines · Rafael J. Wysocki
Quoted 1 line · CET Bjorn Helgaas
[+cc Rafael, hoping for your review :)
+Mika
MAINTAINERS F: include/linux/pm.h · line 15425
Quoted 12 lines
Wonder if we should add something like this to
MAINTAINERSso you get cc'd on power-related things:MAINTAINERS +1
-0@@ -15422,6 +15422,7 @@ F: include/linux/pm.h F: include/linux/pm_* F: include/linux/powercap.h F: kernel/configs/nopm.config +K: pci_[a-z_]*power[a-z_]*\(It seems so, but generally PM patches should be CCed to linux-pm anyway.
Quoted 6 lines
MAINTAINERS +0
-0DYNAMIC THERMAL POWER MANAGEMENT (DTPM) M: Daniel Lezcano <[email protected]>]
On Mon, Jan 24, 2022 at 05:51:07PM +0530, Abhishek Sahu wrote:
Quoted 9 lines
Quoted 6 lines
Quoted 6 lines
Consider the following sequence during PCI device runtime suspend/resume:
1. PCI device goes into runtime suspended state. The PCI state will be changed to
PCI_D0and thenpci_platform_power_transition()will be called which changes the ACPI state toACPI_STATE_D3_HOT.You mean
PCI_D3hotI suppose?Yes. It should be
PCI_D3hothere.Quoted 13 lines
Quoted 13 lines
Quoted 13 lines
2. Parent bridge goes into runtime suspended state. If parent bridge supports D3cold, then it will change the power state of all its children to D3cold state and the power will be removed.
3. During wake-up time, the bridge will be runtime resumed first and
pci_power_up()will be called for the bridge. Now, the power supply will be resumed.4.
pci_resume_bus()will be called which will internally invokepci_restore_standard_config().pci_update_current_state()will readPCI_PM_CTRLregister and thecurrent_statewill be updated to D0.Quoted 9 lines
Quoted 3 lines
Quoted 3 lines
In the above process, at step 4, the ACPI device state will still be
ACPI_STATE_D3_HOTsincepci_platform_power_transition()is not being invoked.I'm not quite following.
I'm assuming that this description applies to the endpoint device that was previously put into
D3_hot.Yes. This is applicable for endpoint devices which was previously put into D3hot.
Quoted 4 lines
Since its current state is
D3_hot, it is not D0 (in particular) and thepci_set_power_state()inpci_restore_standard_config()should put int into D0 proper, including the platform firmware part.The
pci_restore_standard_config()for endpoint devices are being called internally during wake-up of upstream bridge.
pci_power_up(struct pci_dev *dev){ ... if (dev->runtime_d3cold) { /* * When powering on a bridge from D3cold, the whole hierarchy * may be powered on into D0uninitialized state, resume them to * give them a chance to suspend again */ pci_resume_bus(dev->subordinate); } ...}
For the upstream bridge, the above code will trigger the wake-up of endpoint devices and then following code will be executed for the endpoint devices:
pci_update_current_state(struct pci_dev *dev, pci_power_t state){ if (platform_pci_get_power_state(dev) == PCI_D3cold || !pci_device_is_present(dev)) { dev->current_state = PCI_D3cold; } else if (dev->pm_cap) { u16 pmcsr; pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr); dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK); } else { dev->current_state = state; }}
In the above code, the
current_statewill be set to D0 for the endpoint devices since it will go into second block where it will read thePM_CTRLregister.Quoted 58 lines
Quoted 45 lines
Quoted 45 lines
We need call the
pci_platform_power_transition()with state D0 to change the ACPI state toACPI_STATE_D0.This patch calls
pci_power_up()if current power state is D0 insidepci_restore_standard_config(). Thispci_power_up()will change the ACPI state toACPI_STATE_D0.Following are the steps to confirm:
Enable the debug prints in
acpi_pci_set_power_state()0000:01:00.0 is PCI device and 0000:00:01.0 is parent bridge device
Before:
0000:01:00.0: power state changed by ACPI to D3hot 0000:00:01.0: power state changed by ACPI to D3cold 0000:00:01.0: power state changed by ACPI to D0
After:
0000:01:00.0: power state changed by ACPI to D3hot 0000:00:01.0: power state changed by ACPI to D3cold 0000:00:01.0: power state changed by ACPI to D0 0000:01:00.0: power state changed by ACPI to D0
So with this patch, the PCI device ACPI state is also being changed to D0.
Signed-off-by Abhishek Sahu <[email protected]>--- drivers/pci/pci-driver.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-)drivers/pci/pci-driver.c +1
-0@@ -521,14 +521,22 @@ static void pci_device_shutdown(struct device *dev)*/ static int pci_restore_standard_config (struct pci_dev *pci_dev) { +int error =0 ;pci_update_current_state (pci_dev,PCI_UNKNOWN );Quoted 10 lines
drivers/pci/pci-driver.c static void pci_device_shutdown(struct device *dev) · line 533
Quoted 10 lines
drivers/pci/pci-driver.c +6
-3if (pci_dev->current_state !=PCI_D0 ) { -int error =pci_set_power_state (pci_dev,PCI_D0 ); -if (error) -return error; + error =pci_set_power_state (pci_dev,PCI_D0 ); + }else { +/* +* The platform power state can still be non-D0, so this is +* required to change the platform power state to D0. +*/ This really isn't expected to happen.
Quoted 4 lines
If the device's power state has been changed to D3hot by ACPI, it is not in D0.
It looks like the state tracking is not working here.
Quoted 8 lines
The state setting to D0 is happening due to the current logic present in
pci_update_current_state(). If we can fix the logic inpci_update_current_state()to detect this condition and return state D3hot, then it should also fix the issue.Thanks,
AbhishekHi Rafael/Mika,
Quoted 11 lines
Quoted 2 lines
Could you please help regarding the correct way to fix this issue. I can update the patch accordingly.
I think you can try one of the patches posted recently:
Thanks!
Thanks Rafael.
I have applied both the changes and still the issue which I mentioned is happening.Following are the prints:
0000:01:00.0: power state changed by ACPI to D3hot 0000:00:01.0: power state changed by ACPI to D3cold 0000:00:01.0: power state changed by ACPI to D0
Quoted 5 lines
So ACPI state change is still not happening for PCI endpoint devices.
Also, the I checked the code and the
pci_power_up()will not be called for endpoint devices. For endpoints,pci_restore_standard_config()will be called first where the current state will come as D0.OK, I see.
The problem is that if the PCI device goes to D0 because of the bridge power-up, it's ACPI companion's power state may not follow, which means that we really want to do a full power-up in there.
Please test the appended patch with the patch from
still applied.
--- drivers/pci/pci-driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)Index:
linux-pm/drivers/pci/pci-driver.c
===================================================================
drivers/pci/pci-driver.c static int pci_pm_runtime_resume(struct · line 1318
Quoted 8 lines
drivers/pci/pci-driver.c +1
-1@@ -1312,7 +1312,7 @@ static int pci_pm_runtime_resume(struct* to a driver because although we left it in D0, it may have gone to * D3cold when the bridge above it runtime suspended. */ -pci_restore_standard_config (pci_dev); +pci_pm_default_resume_early (pci_dev);if (!pci_dev->driver)return 0 ;
Thanks Rafael.
With the above code change, the issue is getting fixed and the PCI endpoint power state is also changing to D0.
0000:01:00.0: power state changed by ACPI to D3hot 0000:00:01.0: power state changed by ACPI to D3cold 0000:00:01.0: power state changed by ACPI to D0 0000:01:00.0: power state changed by ACPI to D0
Regards,
Abhishek
Quoted 251 lines · CEST Abhishek Sahu
Quoted 237 lines · Rafael J. Wysocki
Quoted 208 lines · CEST Abhishek Sahu
Quoted 186 lines · Rafael J. Wysocki
Quoted 185 lines · CEST Abhishek Sahu
Quoted 173 lines · Abhishek Sahu
Quoted 26 lines · Rafael J. Wysocki
Quoted 1 line · CET Bjorn Helgaas
[+cc Rafael, hoping for your review :)
+Mika
MAINTAINERS F: include/linux/pm.h · line 15425
Quoted 12 lines
Wonder if we should add something like this to
MAINTAINERSso you get cc'd on power-related things:MAINTAINERS +1
-0@@ -15422,6 +15422,7 @@ F: include/linux/pm.h F: include/linux/pm_* F: include/linux/powercap.h F: kernel/configs/nopm.config +K: pci_[a-z_]*power[a-z_]*\(It seems so, but generally PM patches should be CCed to linux-pm anyway.
Quoted 6 lines
MAINTAINERS +0
-0DYNAMIC THERMAL POWER MANAGEMENT (DTPM) M: Daniel Lezcano <[email protected]>]
On Mon, Jan 24, 2022 at 05:51:07PM +0530, Abhishek Sahu wrote:
Quoted 9 lines
Quoted 6 lines
Quoted 6 lines
Consider the following sequence during PCI device runtime suspend/resume:
1. PCI device goes into runtime suspended state. The PCI state will be changed to
PCI_D0and thenpci_platform_power_transition()will be called which changes the ACPI state toACPI_STATE_D3_HOT.You mean
PCI_D3hotI suppose?Yes. It should be
PCI_D3hothere.Quoted 13 lines
Quoted 13 lines
Quoted 13 lines
2. Parent bridge goes into runtime suspended state. If parent bridge supports D3cold, then it will change the power state of all its children to D3cold state and the power will be removed.
3. During wake-up time, the bridge will be runtime resumed first and
pci_power_up()will be called for the bridge. Now, the power supply will be resumed.4.
pci_resume_bus()will be called which will internally invokepci_restore_standard_config().pci_update_current_state()will readPCI_PM_CTRLregister and thecurrent_statewill be updated to D0.Quoted 9 lines
Quoted 3 lines
Quoted 3 lines
In the above process, at step 4, the ACPI device state will still be
ACPI_STATE_D3_HOTsincepci_platform_power_transition()is not being invoked.I'm not quite following.
I'm assuming that this description applies to the endpoint device that was previously put into
D3_hot.Yes. This is applicable for endpoint devices which was previously put into D3hot.
Quoted 4 lines
Since its current state is
D3_hot, it is not D0 (in particular) and thepci_set_power_state()inpci_restore_standard_config()should put int into D0 proper, including the platform firmware part.The
pci_restore_standard_config()for endpoint devices are being called internally during wake-up of upstream bridge.
pci_power_up(struct pci_dev *dev){ ... if (dev->runtime_d3cold) { /* * When powering on a bridge from D3cold, the whole hierarchy * may be powered on into D0uninitialized state, resume them to * give them a chance to suspend again */ pci_resume_bus(dev->subordinate); } ...}
For the upstream bridge, the above code will trigger the wake-up of endpoint devices and then following code will be executed for the endpoint devices:
pci_update_current_state(struct pci_dev *dev, pci_power_t state){ if (platform_pci_get_power_state(dev) == PCI_D3cold || !pci_device_is_present(dev)) { dev->current_state = PCI_D3cold; } else if (dev->pm_cap) { u16 pmcsr; pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr); dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK); } else { dev->current_state = state; }}
In the above code, the
current_statewill be set to D0 for the endpoint devices since it will go into second block where it will read thePM_CTRLregister.Quoted 58 lines
Quoted 45 lines
Quoted 45 lines
We need call the
pci_platform_power_transition()with state D0 to change the ACPI state toACPI_STATE_D0.This patch calls
pci_power_up()if current power state is D0 insidepci_restore_standard_config(). Thispci_power_up()will change the ACPI state toACPI_STATE_D0.Following are the steps to confirm:
Enable the debug prints in
acpi_pci_set_power_state()0000:01:00.0 is PCI device and 0000:00:01.0 is parent bridge device
Before:
0000:01:00.0: power state changed by ACPI to D3hot 0000:00:01.0: power state changed by ACPI to D3cold 0000:00:01.0: power state changed by ACPI to D0
After:
0000:01:00.0: power state changed by ACPI to D3hot 0000:00:01.0: power state changed by ACPI to D3cold 0000:00:01.0: power state changed by ACPI to D0 0000:01:00.0: power state changed by ACPI to D0
So with this patch, the PCI device ACPI state is also being changed to D0.
Signed-off-by Abhishek Sahu <[email protected]>--- drivers/pci/pci-driver.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-)drivers/pci/pci-driver.c +1
-0@@ -521,14 +521,22 @@ static void pci_device_shutdown(struct device *dev)*/ static int pci_restore_standard_config (struct pci_dev *pci_dev) { +int error =0 ;pci_update_current_state (pci_dev,PCI_UNKNOWN );Quoted 10 lines
drivers/pci/pci-driver.c static void pci_device_shutdown(struct device *dev) · line 533
Quoted 10 lines
drivers/pci/pci-driver.c +6
-3if (pci_dev->current_state !=PCI_D0 ) { -int error =pci_set_power_state (pci_dev,PCI_D0 ); -if (error) -return error; + error =pci_set_power_state (pci_dev,PCI_D0 ); + }else { +/* +* The platform power state can still be non-D0, so this is +* required to change the platform power state to D0. +*/ This really isn't expected to happen.
Quoted 4 lines
If the device's power state has been changed to D3hot by ACPI, it is not in D0.
It looks like the state tracking is not working here.
Quoted 8 lines
The state setting to D0 is happening due to the current logic present in
pci_update_current_state(). If we can fix the logic inpci_update_current_state()to detect this condition and return state D3hot, then it should also fix the issue.Thanks,
AbhishekHi Rafael/Mika,
Quoted 11 lines
Quoted 2 lines
Could you please help regarding the correct way to fix this issue. I can update the patch accordingly.
I think you can try one of the patches posted recently:
Thanks!
Thanks Rafael.
I have applied both the changes and still the issue which I mentioned is happening.Following are the prints:
0000:01:00.0: power state changed by ACPI to D3hot 0000:00:01.0: power state changed by ACPI to D3cold 0000:00:01.0: power state changed by ACPI to D0
Quoted 5 lines
So ACPI state change is still not happening for PCI endpoint devices.
Also, the I checked the code and the
pci_power_up()will not be called for endpoint devices. For endpoints,pci_restore_standard_config()will be called first where the current state will come as D0.OK, I see.
The problem is that if the PCI device goes to D0 because of the bridge power-up, it's ACPI companion's power state may not follow, which means that we really want to do a full power-up in there.
Please test the appended patch with the patch from
still applied.
--- drivers/pci/pci-driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)Index:
linux-pm/drivers/pci/pci-driver.c
===================================================================drivers/pci/pci-driver.c static int pci_pm_runtime_resume(struct · line 1318
Quoted 8 lines
drivers/pci/pci-driver.c +1
-1@@ -1312,7 +1312,7 @@ static int pci_pm_runtime_resume(struct* to a driver because although we left it in D0, it may have gone to * D3cold when the bridge above it runtime suspended. */ -pci_restore_standard_config (pci_dev); +pci_pm_default_resume_early (pci_dev);if (!pci_dev->driver)return 0 ;Thanks Rafael.
With the above code change, the issue is getting fixed and the PCI endpoint power state is also changing to D0.
Quoted 4 lines
0000:01:00.0: power state changed by ACPI to D3hot 0000:00:01.0: power state changed by ACPI to D3cold 0000:00:01.0: power state changed by ACPI to D0 0000:01:00.0: power state changed by ACPI to D0
Thanks for testing!