s2idle.c (15271B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Architecture-specific ACPI-based support for suspend-to-idle. 4 * 5 * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com> 6 * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> 7 * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com> 8 * 9 * On platforms supporting the Low Power S0 Idle interface there is an ACPI 10 * device object with the PNP0D80 compatible device ID (System Power Management 11 * Controller) and a specific _DSM method under it. That method, if present, 12 * can be used to indicate to the platform that the OS is transitioning into a 13 * low-power state in which certain types of activity are not desirable or that 14 * it is leaving such a state, which allows the platform to adjust its operation 15 * mode accordingly. 16 */ 17 18#include <linux/acpi.h> 19#include <linux/device.h> 20#include <linux/suspend.h> 21 22#include "../sleep.h" 23 24#ifdef CONFIG_SUSPEND 25 26static bool sleep_no_lps0 __read_mostly; 27module_param(sleep_no_lps0, bool, 0644); 28MODULE_PARM_DESC(sleep_no_lps0, "Do not use the special LPS0 device interface"); 29 30static const struct acpi_device_id lps0_device_ids[] = { 31 {"PNP0D80", }, 32 {"", }, 33}; 34 35/* Microsoft platform agnostic UUID */ 36#define ACPI_LPS0_DSM_UUID_MICROSOFT "11e00d56-ce64-47ce-837b-1f898f9aa461" 37 38#define ACPI_LPS0_DSM_UUID "c4eb40a0-6cd2-11e2-bcfd-0800200c9a66" 39 40#define ACPI_LPS0_GET_DEVICE_CONSTRAINTS 1 41#define ACPI_LPS0_SCREEN_OFF 3 42#define ACPI_LPS0_SCREEN_ON 4 43#define ACPI_LPS0_ENTRY 5 44#define ACPI_LPS0_EXIT 6 45#define ACPI_LPS0_MS_ENTRY 7 46#define ACPI_LPS0_MS_EXIT 8 47 48/* AMD */ 49#define ACPI_LPS0_DSM_UUID_AMD "e3f32452-febc-43ce-9039-932122d37721" 50#define ACPI_LPS0_ENTRY_AMD 2 51#define ACPI_LPS0_EXIT_AMD 3 52#define ACPI_LPS0_SCREEN_OFF_AMD 4 53#define ACPI_LPS0_SCREEN_ON_AMD 5 54 55static acpi_handle lps0_device_handle; 56static guid_t lps0_dsm_guid; 57static int lps0_dsm_func_mask; 58 59static guid_t lps0_dsm_guid_microsoft; 60static int lps0_dsm_func_mask_microsoft; 61 62/* Device constraint entry structure */ 63struct lpi_device_info { 64 char *name; 65 int enabled; 66 union acpi_object *package; 67}; 68 69/* Constraint package structure */ 70struct lpi_device_constraint { 71 int uid; 72 int min_dstate; 73 int function_states; 74}; 75 76struct lpi_constraints { 77 acpi_handle handle; 78 int min_dstate; 79}; 80 81/* AMD Constraint package structure */ 82struct lpi_device_constraint_amd { 83 char *name; 84 int enabled; 85 int function_states; 86 int min_dstate; 87}; 88 89static LIST_HEAD(lps0_s2idle_devops_head); 90 91static struct lpi_constraints *lpi_constraints_table; 92static int lpi_constraints_table_size; 93static int rev_id; 94 95static void lpi_device_get_constraints_amd(void) 96{ 97 union acpi_object *out_obj; 98 int i, j, k; 99 100 out_obj = acpi_evaluate_dsm_typed(lps0_device_handle, &lps0_dsm_guid, 101 rev_id, ACPI_LPS0_GET_DEVICE_CONSTRAINTS, 102 NULL, ACPI_TYPE_PACKAGE); 103 104 acpi_handle_debug(lps0_device_handle, "_DSM function 1 eval %s\n", 105 out_obj ? "successful" : "failed"); 106 107 if (!out_obj) 108 return; 109 110 for (i = 0; i < out_obj->package.count; i++) { 111 union acpi_object *package = &out_obj->package.elements[i]; 112 113 if (package->type == ACPI_TYPE_PACKAGE) { 114 lpi_constraints_table = kcalloc(package->package.count, 115 sizeof(*lpi_constraints_table), 116 GFP_KERNEL); 117 118 if (!lpi_constraints_table) 119 goto free_acpi_buffer; 120 121 acpi_handle_debug(lps0_device_handle, 122 "LPI: constraints list begin:\n"); 123 124 for (j = 0; j < package->package.count; ++j) { 125 union acpi_object *info_obj = &package->package.elements[j]; 126 struct lpi_device_constraint_amd dev_info = {}; 127 struct lpi_constraints *list; 128 acpi_status status; 129 130 for (k = 0; k < info_obj->package.count; ++k) { 131 union acpi_object *obj = &info_obj->package.elements[k]; 132 133 list = &lpi_constraints_table[lpi_constraints_table_size]; 134 list->min_dstate = -1; 135 136 switch (k) { 137 case 0: 138 dev_info.enabled = obj->integer.value; 139 break; 140 case 1: 141 dev_info.name = obj->string.pointer; 142 break; 143 case 2: 144 dev_info.function_states = obj->integer.value; 145 break; 146 case 3: 147 dev_info.min_dstate = obj->integer.value; 148 break; 149 } 150 151 if (!dev_info.enabled || !dev_info.name || 152 !dev_info.min_dstate) 153 continue; 154 155 status = acpi_get_handle(NULL, dev_info.name, 156 &list->handle); 157 if (ACPI_FAILURE(status)) 158 continue; 159 160 acpi_handle_debug(lps0_device_handle, 161 "Name:%s\n", dev_info.name); 162 163 list->min_dstate = dev_info.min_dstate; 164 165 if (list->min_dstate < 0) { 166 acpi_handle_debug(lps0_device_handle, 167 "Incomplete constraint defined\n"); 168 continue; 169 } 170 } 171 lpi_constraints_table_size++; 172 } 173 } 174 } 175 176 acpi_handle_debug(lps0_device_handle, "LPI: constraints list end\n"); 177 178free_acpi_buffer: 179 ACPI_FREE(out_obj); 180} 181 182static void lpi_device_get_constraints(void) 183{ 184 union acpi_object *out_obj; 185 int i; 186 187 out_obj = acpi_evaluate_dsm_typed(lps0_device_handle, &lps0_dsm_guid, 188 1, ACPI_LPS0_GET_DEVICE_CONSTRAINTS, 189 NULL, ACPI_TYPE_PACKAGE); 190 191 acpi_handle_debug(lps0_device_handle, "_DSM function 1 eval %s\n", 192 out_obj ? "successful" : "failed"); 193 194 if (!out_obj) 195 return; 196 197 lpi_constraints_table = kcalloc(out_obj->package.count, 198 sizeof(*lpi_constraints_table), 199 GFP_KERNEL); 200 if (!lpi_constraints_table) 201 goto free_acpi_buffer; 202 203 acpi_handle_debug(lps0_device_handle, "LPI: constraints list begin:\n"); 204 205 for (i = 0; i < out_obj->package.count; i++) { 206 struct lpi_constraints *constraint; 207 acpi_status status; 208 union acpi_object *package = &out_obj->package.elements[i]; 209 struct lpi_device_info info = { }; 210 int package_count = 0, j; 211 212 if (!package) 213 continue; 214 215 for (j = 0; j < package->package.count; ++j) { 216 union acpi_object *element = 217 &(package->package.elements[j]); 218 219 switch (element->type) { 220 case ACPI_TYPE_INTEGER: 221 info.enabled = element->integer.value; 222 break; 223 case ACPI_TYPE_STRING: 224 info.name = element->string.pointer; 225 break; 226 case ACPI_TYPE_PACKAGE: 227 package_count = element->package.count; 228 info.package = element->package.elements; 229 break; 230 } 231 } 232 233 if (!info.enabled || !info.package || !info.name) 234 continue; 235 236 constraint = &lpi_constraints_table[lpi_constraints_table_size]; 237 238 status = acpi_get_handle(NULL, info.name, &constraint->handle); 239 if (ACPI_FAILURE(status)) 240 continue; 241 242 acpi_handle_debug(lps0_device_handle, 243 "index:%d Name:%s\n", i, info.name); 244 245 constraint->min_dstate = -1; 246 247 for (j = 0; j < package_count; ++j) { 248 union acpi_object *info_obj = &info.package[j]; 249 union acpi_object *cnstr_pkg; 250 union acpi_object *obj; 251 struct lpi_device_constraint dev_info; 252 253 switch (info_obj->type) { 254 case ACPI_TYPE_INTEGER: 255 /* version */ 256 break; 257 case ACPI_TYPE_PACKAGE: 258 if (info_obj->package.count < 2) 259 break; 260 261 cnstr_pkg = info_obj->package.elements; 262 obj = &cnstr_pkg[0]; 263 dev_info.uid = obj->integer.value; 264 obj = &cnstr_pkg[1]; 265 dev_info.min_dstate = obj->integer.value; 266 267 acpi_handle_debug(lps0_device_handle, 268 "uid:%d min_dstate:%s\n", 269 dev_info.uid, 270 acpi_power_state_string(dev_info.min_dstate)); 271 272 constraint->min_dstate = dev_info.min_dstate; 273 break; 274 } 275 } 276 277 if (constraint->min_dstate < 0) { 278 acpi_handle_debug(lps0_device_handle, 279 "Incomplete constraint defined\n"); 280 continue; 281 } 282 283 lpi_constraints_table_size++; 284 } 285 286 acpi_handle_debug(lps0_device_handle, "LPI: constraints list end\n"); 287 288free_acpi_buffer: 289 ACPI_FREE(out_obj); 290} 291 292static void lpi_check_constraints(void) 293{ 294 int i; 295 296 for (i = 0; i < lpi_constraints_table_size; ++i) { 297 acpi_handle handle = lpi_constraints_table[i].handle; 298 struct acpi_device *adev = acpi_fetch_acpi_dev(handle); 299 300 if (!adev) 301 continue; 302 303 acpi_handle_debug(handle, 304 "LPI: required min power state:%s current power state:%s\n", 305 acpi_power_state_string(lpi_constraints_table[i].min_dstate), 306 acpi_power_state_string(adev->power.state)); 307 308 if (!adev->flags.power_manageable) { 309 acpi_handle_info(handle, "LPI: Device not power manageable\n"); 310 lpi_constraints_table[i].handle = NULL; 311 continue; 312 } 313 314 if (adev->power.state < lpi_constraints_table[i].min_dstate) 315 acpi_handle_info(handle, 316 "LPI: Constraint not met; min power state:%s current power state:%s\n", 317 acpi_power_state_string(lpi_constraints_table[i].min_dstate), 318 acpi_power_state_string(adev->power.state)); 319 } 320} 321 322static void acpi_sleep_run_lps0_dsm(unsigned int func, unsigned int func_mask, guid_t dsm_guid) 323{ 324 union acpi_object *out_obj; 325 326 if (!(func_mask & (1 << func))) 327 return; 328 329 out_obj = acpi_evaluate_dsm(lps0_device_handle, &dsm_guid, 330 rev_id, func, NULL); 331 ACPI_FREE(out_obj); 332 333 acpi_handle_debug(lps0_device_handle, "_DSM function %u evaluation %s\n", 334 func, out_obj ? "successful" : "failed"); 335} 336 337static bool acpi_s2idle_vendor_amd(void) 338{ 339 return boot_cpu_data.x86_vendor == X86_VENDOR_AMD; 340} 341 342static int validate_dsm(acpi_handle handle, const char *uuid, int rev, guid_t *dsm_guid) 343{ 344 union acpi_object *obj; 345 int ret = -EINVAL; 346 347 guid_parse(uuid, dsm_guid); 348 obj = acpi_evaluate_dsm(handle, dsm_guid, rev, 0, NULL); 349 350 /* Check if the _DSM is present and as expected. */ 351 if (!obj || obj->type != ACPI_TYPE_BUFFER || obj->buffer.length == 0 || 352 obj->buffer.length > sizeof(u32)) { 353 acpi_handle_debug(handle, 354 "_DSM UUID %s rev %d function 0 evaluation failed\n", uuid, rev); 355 goto out; 356 } 357 358 ret = *(int *)obj->buffer.pointer; 359 acpi_handle_debug(handle, "_DSM UUID %s rev %d function mask: 0x%x\n", uuid, rev, ret); 360 361out: 362 ACPI_FREE(obj); 363 return ret; 364} 365 366static int lps0_device_attach(struct acpi_device *adev, 367 const struct acpi_device_id *not_used) 368{ 369 if (lps0_device_handle) 370 return 0; 371 372 if (!(acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0)) 373 return 0; 374 375 if (acpi_s2idle_vendor_amd()) { 376 /* AMD0004, AMD0005, AMDI0005: 377 * - Should use rev_id 0x0 378 * - function mask > 0x3: Should use AMD method, but has off by one bug 379 * - function mask = 0x3: Should use Microsoft method 380 * AMDI0006: 381 * - should use rev_id 0x0 382 * - function mask = 0x3: Should use Microsoft method 383 * AMDI0007: 384 * - Should use rev_id 0x2 385 * - Should only use AMD method 386 */ 387 const char *hid = acpi_device_hid(adev); 388 rev_id = strcmp(hid, "AMDI0007") ? 0 : 2; 389 lps0_dsm_func_mask = validate_dsm(adev->handle, 390 ACPI_LPS0_DSM_UUID_AMD, rev_id, &lps0_dsm_guid); 391 lps0_dsm_func_mask_microsoft = validate_dsm(adev->handle, 392 ACPI_LPS0_DSM_UUID_MICROSOFT, 0, 393 &lps0_dsm_guid_microsoft); 394 if (lps0_dsm_func_mask > 0x3 && (!strcmp(hid, "AMD0004") || 395 !strcmp(hid, "AMD0005") || 396 !strcmp(hid, "AMDI0005"))) { 397 lps0_dsm_func_mask = (lps0_dsm_func_mask << 1) | 0x1; 398 acpi_handle_debug(adev->handle, "_DSM UUID %s: Adjusted function mask: 0x%x\n", 399 ACPI_LPS0_DSM_UUID_AMD, lps0_dsm_func_mask); 400 } else if (lps0_dsm_func_mask_microsoft > 0 && !strcmp(hid, "AMDI0007")) { 401 lps0_dsm_func_mask_microsoft = -EINVAL; 402 acpi_handle_debug(adev->handle, "_DSM Using AMD method\n"); 403 } 404 } else { 405 rev_id = 1; 406 lps0_dsm_func_mask = validate_dsm(adev->handle, 407 ACPI_LPS0_DSM_UUID, rev_id, &lps0_dsm_guid); 408 lps0_dsm_func_mask_microsoft = -EINVAL; 409 } 410 411 if (lps0_dsm_func_mask < 0 && lps0_dsm_func_mask_microsoft < 0) 412 return 0; //function evaluation failed 413 414 lps0_device_handle = adev->handle; 415 416 if (acpi_s2idle_vendor_amd()) 417 lpi_device_get_constraints_amd(); 418 else 419 lpi_device_get_constraints(); 420 421 /* 422 * Use suspend-to-idle by default if the default suspend mode was not 423 * set from the command line. 424 */ 425 if (mem_sleep_default > PM_SUSPEND_MEM && !acpi_sleep_default_s3) 426 mem_sleep_current = PM_SUSPEND_TO_IDLE; 427 428 /* 429 * Some LPS0 systems, like ASUS Zenbook UX430UNR/i7-8550U, require the 430 * EC GPE to be enabled while suspended for certain wakeup devices to 431 * work, so mark it as wakeup-capable. 432 */ 433 acpi_ec_mark_gpe_for_wake(); 434 435 return 0; 436} 437 438static struct acpi_scan_handler lps0_handler = { 439 .ids = lps0_device_ids, 440 .attach = lps0_device_attach, 441}; 442 443int acpi_s2idle_prepare_late(void) 444{ 445 struct acpi_s2idle_dev_ops *handler; 446 447 if (!lps0_device_handle || sleep_no_lps0) 448 return 0; 449 450 if (pm_debug_messages_on) 451 lpi_check_constraints(); 452 453 /* Screen off */ 454 if (lps0_dsm_func_mask > 0) 455 acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ? 456 ACPI_LPS0_SCREEN_OFF_AMD : 457 ACPI_LPS0_SCREEN_OFF, 458 lps0_dsm_func_mask, lps0_dsm_guid); 459 460 if (lps0_dsm_func_mask_microsoft > 0) 461 acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_OFF, 462 lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft); 463 464 /* LPS0 entry */ 465 if (lps0_dsm_func_mask > 0) 466 acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ? 467 ACPI_LPS0_ENTRY_AMD : 468 ACPI_LPS0_ENTRY, 469 lps0_dsm_func_mask, lps0_dsm_guid); 470 if (lps0_dsm_func_mask_microsoft > 0) { 471 acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY, 472 lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft); 473 /* modern standby entry */ 474 acpi_sleep_run_lps0_dsm(ACPI_LPS0_MS_ENTRY, 475 lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft); 476 } 477 478 list_for_each_entry(handler, &lps0_s2idle_devops_head, list_node) { 479 if (handler->prepare) 480 handler->prepare(); 481 } 482 483 return 0; 484} 485 486void acpi_s2idle_restore_early(void) 487{ 488 struct acpi_s2idle_dev_ops *handler; 489 490 if (!lps0_device_handle || sleep_no_lps0) 491 return; 492 493 list_for_each_entry(handler, &lps0_s2idle_devops_head, list_node) 494 if (handler->restore) 495 handler->restore(); 496 497 /* Modern standby exit */ 498 if (lps0_dsm_func_mask_microsoft > 0) 499 acpi_sleep_run_lps0_dsm(ACPI_LPS0_MS_EXIT, 500 lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft); 501 502 /* LPS0 exit */ 503 if (lps0_dsm_func_mask > 0) 504 acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ? 505 ACPI_LPS0_EXIT_AMD : 506 ACPI_LPS0_EXIT, 507 lps0_dsm_func_mask, lps0_dsm_guid); 508 if (lps0_dsm_func_mask_microsoft > 0) 509 acpi_sleep_run_lps0_dsm(ACPI_LPS0_EXIT, 510 lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft); 511 512 /* Screen on */ 513 if (lps0_dsm_func_mask_microsoft > 0) 514 acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_ON, 515 lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft); 516 if (lps0_dsm_func_mask > 0) 517 acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ? 518 ACPI_LPS0_SCREEN_ON_AMD : 519 ACPI_LPS0_SCREEN_ON, 520 lps0_dsm_func_mask, lps0_dsm_guid); 521} 522 523static const struct platform_s2idle_ops acpi_s2idle_ops_lps0 = { 524 .begin = acpi_s2idle_begin, 525 .prepare = acpi_s2idle_prepare, 526 .prepare_late = acpi_s2idle_prepare_late, 527 .wake = acpi_s2idle_wake, 528 .restore_early = acpi_s2idle_restore_early, 529 .restore = acpi_s2idle_restore, 530 .end = acpi_s2idle_end, 531}; 532 533void acpi_s2idle_setup(void) 534{ 535 acpi_scan_add_handler(&lps0_handler); 536 s2idle_set_ops(&acpi_s2idle_ops_lps0); 537} 538 539int acpi_register_lps0_dev(struct acpi_s2idle_dev_ops *arg) 540{ 541 if (!lps0_device_handle || sleep_no_lps0) 542 return -ENODEV; 543 544 lock_system_sleep(); 545 list_add(&arg->list_node, &lps0_s2idle_devops_head); 546 unlock_system_sleep(); 547 548 return 0; 549} 550EXPORT_SYMBOL_GPL(acpi_register_lps0_dev); 551 552void acpi_unregister_lps0_dev(struct acpi_s2idle_dev_ops *arg) 553{ 554 if (!lps0_device_handle || sleep_no_lps0) 555 return; 556 557 lock_system_sleep(); 558 list_del(&arg->list_node); 559 unlock_system_sleep(); 560} 561EXPORT_SYMBOL_GPL(acpi_unregister_lps0_dev); 562 563#endif /* CONFIG_SUSPEND */