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 26 static bool sleep_no_lps0 __read_mostly; 27 module_param(sleep_no_lps0, bool, 0644); 28 MODULE_PARM_DESC(sleep_no_lps0, "Do not use the special LPS0 device interface"); 29 30 static 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 55 static acpi_handle lps0_device_handle; 56 static guid_t lps0_dsm_guid; 57 static int lps0_dsm_func_mask; 58 59 static guid_t lps0_dsm_guid_microsoft; 60 static int lps0_dsm_func_mask_microsoft; 61 62 /* Device constraint entry structure */ 63 struct lpi_device_info { 64 char *name; 65 int enabled; 66 union acpi_object *package; 67 }; 68 69 /* Constraint package structure */ 70 struct lpi_device_constraint { 71 int uid; 72 int min_dstate; 73 int function_states; 74 }; 75 76 struct lpi_constraints { 77 acpi_handle handle; 78 int min_dstate; 79 }; 80 81 /* AMD Constraint package structure */ 82 struct lpi_device_constraint_amd { 83 char *name; 84 int enabled; 85 int function_states; 86 int min_dstate; 87 }; 88 89 static LIST_HEAD(lps0_s2idle_devops_head); 90 91 static struct lpi_constraints *lpi_constraints_table; 92 static int lpi_constraints_table_size; 93 static int rev_id; 94 95 static 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 178 free_acpi_buffer: 179 ACPI_FREE(out_obj); 180 } 181 182 static 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 288 free_acpi_buffer: 289 ACPI_FREE(out_obj); 290 } 291 292 static 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 322 static 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 337 static bool acpi_s2idle_vendor_amd(void) 338 { 339 return boot_cpu_data.x86_vendor == X86_VENDOR_AMD; 340 } 341 342 static 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 361 out: 362 ACPI_FREE(obj); 363 return ret; 364 } 365 366 static 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_s2idle_vendor_amd()) { 373 /* AMD0004, AMD0005, AMDI0005: 374 * - Should use rev_id 0x0 375 * - function mask > 0x3: Should use AMD method, but has off by one bug 376 * - function mask = 0x3: Should use Microsoft method 377 * AMDI0006: 378 * - should use rev_id 0x0 379 * - function mask = 0x3: Should use Microsoft method 380 * AMDI0007: 381 * - Should use rev_id 0x2 382 * - Should only use AMD method 383 */ 384 const char *hid = acpi_device_hid(adev); 385 rev_id = strcmp(hid, "AMDI0007") ? 0 : 2; 386 lps0_dsm_func_mask = validate_dsm(adev->handle, 387 ACPI_LPS0_DSM_UUID_AMD, rev_id, &lps0_dsm_guid); 388 lps0_dsm_func_mask_microsoft = validate_dsm(adev->handle, 389 ACPI_LPS0_DSM_UUID_MICROSOFT, 0, 390 &lps0_dsm_guid_microsoft); 391 if (lps0_dsm_func_mask > 0x3 && (!strcmp(hid, "AMD0004") || 392 !strcmp(hid, "AMD0005") || 393 !strcmp(hid, "AMDI0005"))) { 394 lps0_dsm_func_mask = (lps0_dsm_func_mask << 1) | 0x1; 395 acpi_handle_debug(adev->handle, "_DSM UUID %s: Adjusted function mask: 0x%x\n", 396 ACPI_LPS0_DSM_UUID_AMD, lps0_dsm_func_mask); 397 } else if (lps0_dsm_func_mask_microsoft > 0 && 398 (!strcmp(hid, "AMDI0007") || 399 !strcmp(hid, "AMDI0008"))) { 400 lps0_dsm_func_mask_microsoft = -EINVAL; 401 acpi_handle_debug(adev->handle, "_DSM Using AMD method\n"); 402 } 403 } else { 404 rev_id = 1; 405 lps0_dsm_func_mask = validate_dsm(adev->handle, 406 ACPI_LPS0_DSM_UUID, rev_id, &lps0_dsm_guid); 407 lps0_dsm_func_mask_microsoft = -EINVAL; 408 } 409 410 if (lps0_dsm_func_mask < 0 && lps0_dsm_func_mask_microsoft < 0) 411 return 0; //function evaluation failed 412 413 lps0_device_handle = adev->handle; 414 415 if (acpi_s2idle_vendor_amd()) 416 lpi_device_get_constraints_amd(); 417 else 418 lpi_device_get_constraints(); 419 420 /* 421 * Use suspend-to-idle by default if ACPI_FADT_LOW_POWER_S0 is set in 422 * the FADT and the default suspend mode was not set from the command 423 * line. 424 */ 425 if ((acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0) && 426 mem_sleep_default > PM_SUSPEND_MEM && !acpi_sleep_default_s3) { 427 mem_sleep_current = PM_SUSPEND_TO_IDLE; 428 pr_info("Low-power S0 idle used by default for system suspend\n"); 429 } 430 431 /* 432 * Some LPS0 systems, like ASUS Zenbook UX430UNR/i7-8550U, require the 433 * EC GPE to be enabled while suspended for certain wakeup devices to 434 * work, so mark it as wakeup-capable. 435 */ 436 acpi_ec_mark_gpe_for_wake(); 437 438 return 0; 439 } 440 441 static struct acpi_scan_handler lps0_handler = { 442 .ids = lps0_device_ids, 443 .attach = lps0_device_attach, 444 }; 445 446 int acpi_s2idle_prepare_late(void) 447 { 448 struct acpi_s2idle_dev_ops *handler; 449 450 if (!lps0_device_handle || sleep_no_lps0) 451 return 0; 452 453 if (pm_debug_messages_on) 454 lpi_check_constraints(); 455 456 /* Screen off */ 457 if (lps0_dsm_func_mask > 0) 458 acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ? 459 ACPI_LPS0_SCREEN_OFF_AMD : 460 ACPI_LPS0_SCREEN_OFF, 461 lps0_dsm_func_mask, lps0_dsm_guid); 462 463 if (lps0_dsm_func_mask_microsoft > 0) 464 acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_OFF, 465 lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft); 466 467 /* LPS0 entry */ 468 if (lps0_dsm_func_mask > 0) 469 acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ? 470 ACPI_LPS0_ENTRY_AMD : 471 ACPI_LPS0_ENTRY, 472 lps0_dsm_func_mask, lps0_dsm_guid); 473 if (lps0_dsm_func_mask_microsoft > 0) { 474 acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY, 475 lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft); 476 /* modern standby entry */ 477 acpi_sleep_run_lps0_dsm(ACPI_LPS0_MS_ENTRY, 478 lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft); 479 } 480 481 list_for_each_entry(handler, &lps0_s2idle_devops_head, list_node) { 482 if (handler->prepare) 483 handler->prepare(); 484 } 485 486 return 0; 487 } 488 489 void acpi_s2idle_restore_early(void) 490 { 491 struct acpi_s2idle_dev_ops *handler; 492 493 if (!lps0_device_handle || sleep_no_lps0) 494 return; 495 496 list_for_each_entry(handler, &lps0_s2idle_devops_head, list_node) 497 if (handler->restore) 498 handler->restore(); 499 500 /* Modern standby exit */ 501 if (lps0_dsm_func_mask_microsoft > 0) 502 acpi_sleep_run_lps0_dsm(ACPI_LPS0_MS_EXIT, 503 lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft); 504 505 /* LPS0 exit */ 506 if (lps0_dsm_func_mask > 0) 507 acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ? 508 ACPI_LPS0_EXIT_AMD : 509 ACPI_LPS0_EXIT, 510 lps0_dsm_func_mask, lps0_dsm_guid); 511 if (lps0_dsm_func_mask_microsoft > 0) 512 acpi_sleep_run_lps0_dsm(ACPI_LPS0_EXIT, 513 lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft); 514 515 /* Screen on */ 516 if (lps0_dsm_func_mask_microsoft > 0) 517 acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_ON, 518 lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft); 519 if (lps0_dsm_func_mask > 0) 520 acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ? 521 ACPI_LPS0_SCREEN_ON_AMD : 522 ACPI_LPS0_SCREEN_ON, 523 lps0_dsm_func_mask, lps0_dsm_guid); 524 } 525 526 static const struct platform_s2idle_ops acpi_s2idle_ops_lps0 = { 527 .begin = acpi_s2idle_begin, 528 .prepare = acpi_s2idle_prepare, 529 .prepare_late = acpi_s2idle_prepare_late, 530 .wake = acpi_s2idle_wake, 531 .restore_early = acpi_s2idle_restore_early, 532 .restore = acpi_s2idle_restore, 533 .end = acpi_s2idle_end, 534 }; 535 536 void acpi_s2idle_setup(void) 537 { 538 acpi_scan_add_handler(&lps0_handler); 539 s2idle_set_ops(&acpi_s2idle_ops_lps0); 540 } 541 542 int acpi_register_lps0_dev(struct acpi_s2idle_dev_ops *arg) 543 { 544 if (!lps0_device_handle || sleep_no_lps0) 545 return -ENODEV; 546 547 lock_system_sleep(); 548 list_add(&arg->list_node, &lps0_s2idle_devops_head); 549 unlock_system_sleep(); 550 551 return 0; 552 } 553 EXPORT_SYMBOL_GPL(acpi_register_lps0_dev); 554 555 void acpi_unregister_lps0_dev(struct acpi_s2idle_dev_ops *arg) 556 { 557 if (!lps0_device_handle || sleep_no_lps0) 558 return; 559 560 lock_system_sleep(); 561 list_del(&arg->list_node); 562 unlock_system_sleep(); 563 } 564 EXPORT_SYMBOL_GPL(acpi_unregister_lps0_dev); 565 566 #endif /* CONFIG_SUSPEND */ 567