Skip to content

Commit 0467ed8

Browse files
maciejsszmigierorafaeljw
authored andcommitted
ACPI: PM: Add HP EliteBook 855 G7 WWAN modem power resource quirk
This laptop (and possibly similar models too) has power resource called "GP12.PXP_" for its Intel XMM7360 WWAN modem. For this power resource to turn ON power for the modem it needs certain internal flag called "ONEN" to be set: Method (_ON, 0, NotSerialized) // _ON_: Power On { If (^^^LPCB.EC0.ECRG) { If ((ONEN == Zero)) { Return (Zero) } (..) } } This flag only gets set from this power resource _OFF method, while the actual modem power gets turned off during suspend by "GP12.PTS" method called from the global _PTS (Prepare To Sleep) method. In fact, this power resource _OFF method implementation just sets the aforementioned flag: Method (_OFF, 0, NotSerialized) // _OFF: Power Off { OFEN = Zero ONEN = One } Upon hibernation finish, the kernel tries to set this power resource back ON since its _STA method returns 0 and the resource is still considered in use as it is declared as required for D0 for both the modem ACPI device (GP12.PWAN) and its parent PCIe port ACPI device object (GP12). But the _ON method won't do anything since that "ONEN" flag is not set. Overall, this means the modem is dead after hibernation finish until the laptop is rebooted since the modem power has been cut by _PTS and its PCI configuration was lost and not able to be restored. The easiest way to workaround this issue is to call this power resource _OFF method before calling the _ON method to make sure the "ONEN" flag gets properly set. This makes the modem alive once again after hibernation finish - with properly restored PCI configuration space. Since this platform does *not* support S3 the fact that acpi_resume_power_resources() is also called during resume from S3 is not a problem there. Do the DMI based quirk matching and quirk flag initialization just once - in acpi_power_resources_init() function. This way the whole resume path overhead of this change on other systems amounts to simple hp_eb_gp12pxp_quirk flag comparison. Signed-off-by: Maciej S. Szmigiero <mail@maciej.szmigiero.name> Link: https://patch.msgid.link/c6ff6931c5d27592052f30339de1b9cc298c43f0.1754243159.git.mail@maciej.szmigiero.name [ rjw: Changelog edits ] Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
1 parent 3bc3dc1 commit 0467ed8

File tree

1 file changed

+78
-2
lines changed

1 file changed

+78
-2
lines changed

drivers/acpi/power.c

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
#define pr_fmt(fmt) "ACPI: PM: " fmt
2525

26+
#include <linux/delay.h>
2627
#include <linux/dmi.h>
2728
#include <linux/kernel.h>
2829
#include <linux/module.h>
@@ -63,6 +64,7 @@ struct acpi_power_resource_entry {
6364
struct acpi_power_resource *resource;
6465
};
6566

67+
static bool hp_eb_gp12pxp_quirk;
6668
static bool unused_power_resources_quirk;
6769

6870
static LIST_HEAD(acpi_power_resource_list);
@@ -994,6 +996,38 @@ struct acpi_device *acpi_add_power_resource(acpi_handle handle)
994996
}
995997

996998
#ifdef CONFIG_ACPI_SLEEP
999+
static bool resource_is_gp12pxp(acpi_handle handle)
1000+
{
1001+
const char *path;
1002+
bool ret;
1003+
1004+
path = acpi_handle_path(handle);
1005+
ret = path && strcmp(path, "\\_SB_.PCI0.GP12.PXP_") == 0;
1006+
kfree(path);
1007+
1008+
return ret;
1009+
}
1010+
1011+
static void acpi_resume_on_eb_gp12pxp(struct acpi_power_resource *resource)
1012+
{
1013+
acpi_handle_notice(resource->device.handle,
1014+
"HP EB quirk - turning OFF then ON\n");
1015+
1016+
__acpi_power_off(resource);
1017+
__acpi_power_on(resource);
1018+
1019+
/*
1020+
* Use the same delay as DSDT uses in modem _RST method.
1021+
*
1022+
* Otherwise we get "Unable to change power state from unknown to D0,
1023+
* device inaccessible" error for the modem PCI device after thaw.
1024+
*
1025+
* This power resource is normally being enabled only during thaw (once)
1026+
* so this wait is not a performance issue.
1027+
*/
1028+
msleep(200);
1029+
}
1030+
9971031
void acpi_resume_power_resources(void)
9981032
{
9991033
struct acpi_power_resource *resource;
@@ -1015,8 +1049,14 @@ void acpi_resume_power_resources(void)
10151049

10161050
if (state == ACPI_POWER_RESOURCE_STATE_OFF
10171051
&& resource->ref_count) {
1018-
acpi_handle_debug(resource->device.handle, "Turning ON\n");
1019-
__acpi_power_on(resource);
1052+
if (hp_eb_gp12pxp_quirk &&
1053+
resource_is_gp12pxp(resource->device.handle)) {
1054+
acpi_resume_on_eb_gp12pxp(resource);
1055+
} else {
1056+
acpi_handle_debug(resource->device.handle,
1057+
"Turning ON\n");
1058+
__acpi_power_on(resource);
1059+
}
10201060
}
10211061

10221062
mutex_unlock(&resource->resource_lock);
@@ -1026,6 +1066,41 @@ void acpi_resume_power_resources(void)
10261066
}
10271067
#endif
10281068

1069+
static const struct dmi_system_id dmi_hp_elitebook_gp12pxp_quirk[] = {
1070+
/*
1071+
* This laptop (and possibly similar models too) has power resource called
1072+
* "GP12.PXP_" for its WWAN modem.
1073+
*
1074+
* For this power resource to turn ON power for the modem it needs certain
1075+
* internal flag called "ONEN" to be set.
1076+
* This flag only gets set from this power resource "_OFF" method, while the
1077+
* actual modem power gets turned off during suspend by "GP12.PTS" method
1078+
* called from the global "_PTS" (Prepare To Sleep) method.
1079+
* On the other hand, this power resource "_OFF" method implementation just
1080+
* sets the aforementioned flag without actually doing anything else (it
1081+
* doesn't contain any code to actually turn off power).
1082+
*
1083+
* The above means that when upon hibernation finish we try to set this
1084+
* power resource back ON since its "_STA" method returns 0 (while the resource
1085+
* is still considered in use) its "_ON" method won't do anything since
1086+
* that "ONEN" flag is not set.
1087+
* Overall, this means the modem is dead until laptop is rebooted since its
1088+
* power has been cut by "_PTS" and its PCI configuration was lost and not able
1089+
* to be restored.
1090+
*
1091+
* The easiest way to workaround the issue is to call this power resource
1092+
* "_OFF" method before calling the "_ON" method to make sure the "ONEN"
1093+
* flag gets properly set.
1094+
*/
1095+
{
1096+
.matches = {
1097+
DMI_MATCH(DMI_SYS_VENDOR, "HP"),
1098+
DMI_MATCH(DMI_PRODUCT_NAME, "HP EliteBook 855 G7 Notebook PC"),
1099+
},
1100+
},
1101+
{}
1102+
};
1103+
10291104
static const struct dmi_system_id dmi_leave_unused_power_resources_on[] = {
10301105
{
10311106
/*
@@ -1070,6 +1145,7 @@ void acpi_turn_off_unused_power_resources(void)
10701145

10711146
void __init acpi_power_resources_init(void)
10721147
{
1148+
hp_eb_gp12pxp_quirk = dmi_check_system(dmi_hp_elitebook_gp12pxp_quirk);
10731149
unused_power_resources_quirk =
10741150
dmi_check_system(dmi_leave_unused_power_resources_on);
10751151
}

0 commit comments

Comments
 (0)