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 struct lpi_constraints *lpi_constraints_table; 90 static int lpi_constraints_table_size; 91 static int rev_id; 92 93 static void lpi_device_get_constraints_amd(void) 94 { 95 union acpi_object *out_obj; 96 int i, j, k; 97 98 out_obj = acpi_evaluate_dsm_typed(lps0_device_handle, &lps0_dsm_guid, 99 rev_id, ACPI_LPS0_GET_DEVICE_CONSTRAINTS, 100 NULL, ACPI_TYPE_PACKAGE); 101 102 acpi_handle_debug(lps0_device_handle, "_DSM function 1 eval %s\n", 103 out_obj ? "successful" : "failed"); 104 105 if (!out_obj) 106 return; 107 108 for (i = 0; i < out_obj->package.count; i++) { 109 union acpi_object *package = &out_obj->package.elements[i]; 110 111 if (package->type == ACPI_TYPE_PACKAGE) { 112 lpi_constraints_table = kcalloc(package->package.count, 113 sizeof(*lpi_constraints_table), 114 GFP_KERNEL); 115 116 if (!lpi_constraints_table) 117 goto free_acpi_buffer; 118 119 acpi_handle_debug(lps0_device_handle, 120 "LPI: constraints list begin:\n"); 121 122 for (j = 0; j < package->package.count; ++j) { 123 union acpi_object *info_obj = &package->package.elements[j]; 124 struct lpi_device_constraint_amd dev_info = {}; 125 struct lpi_constraints *list; 126 acpi_status status; 127 128 for (k = 0; k < info_obj->package.count; ++k) { 129 union acpi_object *obj = &info_obj->package.elements[k]; 130 131 list = &lpi_constraints_table[lpi_constraints_table_size]; 132 list->min_dstate = -1; 133 134 switch (k) { 135 case 0: 136 dev_info.enabled = obj->integer.value; 137 break; 138 case 1: 139 dev_info.name = obj->string.pointer; 140 break; 141 case 2: 142 dev_info.function_states = obj->integer.value; 143 break; 144 case 3: 145 dev_info.min_dstate = obj->integer.value; 146 break; 147 } 148 149 if (!dev_info.enabled || !dev_info.name || 150 !dev_info.min_dstate) 151 continue; 152 153 status = acpi_get_handle(NULL, dev_info.name, 154 &list->handle); 155 if (ACPI_FAILURE(status)) 156 continue; 157 158 acpi_handle_debug(lps0_device_handle, 159 "Name:%s\n", dev_info.name); 160 161 list->min_dstate = dev_info.min_dstate; 162 163 if (list->min_dstate < 0) { 164 acpi_handle_debug(lps0_device_handle, 165 "Incomplete constraint defined\n"); 166 continue; 167 } 168 } 169 lpi_constraints_table_size++; 170 } 171 } 172 } 173 174 acpi_handle_debug(lps0_device_handle, "LPI: constraints list end\n"); 175 176 free_acpi_buffer: 177 ACPI_FREE(out_obj); 178 } 179 180 static void lpi_device_get_constraints(void) 181 { 182 union acpi_object *out_obj; 183 int i; 184 185 out_obj = acpi_evaluate_dsm_typed(lps0_device_handle, &lps0_dsm_guid, 186 1, ACPI_LPS0_GET_DEVICE_CONSTRAINTS, 187 NULL, ACPI_TYPE_PACKAGE); 188 189 acpi_handle_debug(lps0_device_handle, "_DSM function 1 eval %s\n", 190 out_obj ? "successful" : "failed"); 191 192 if (!out_obj) 193 return; 194 195 lpi_constraints_table = kcalloc(out_obj->package.count, 196 sizeof(*lpi_constraints_table), 197 GFP_KERNEL); 198 if (!lpi_constraints_table) 199 goto free_acpi_buffer; 200 201 acpi_handle_debug(lps0_device_handle, "LPI: constraints list begin:\n"); 202 203 for (i = 0; i < out_obj->package.count; i++) { 204 struct lpi_constraints *constraint; 205 acpi_status status; 206 union acpi_object *package = &out_obj->package.elements[i]; 207 struct lpi_device_info info = { }; 208 int package_count = 0, j; 209 210 if (!package) 211 continue; 212 213 for (j = 0; j < package->package.count; ++j) { 214 union acpi_object *element = 215 &(package->package.elements[j]); 216 217 switch (element->type) { 218 case ACPI_TYPE_INTEGER: 219 info.enabled = element->integer.value; 220 break; 221 case ACPI_TYPE_STRING: 222 info.name = element->string.pointer; 223 break; 224 case ACPI_TYPE_PACKAGE: 225 package_count = element->package.count; 226 info.package = element->package.elements; 227 break; 228 } 229 } 230 231 if (!info.enabled || !info.package || !info.name) 232 continue; 233 234 constraint = &lpi_constraints_table[lpi_constraints_table_size]; 235 236 status = acpi_get_handle(NULL, info.name, &constraint->handle); 237 if (ACPI_FAILURE(status)) 238 continue; 239 240 acpi_handle_debug(lps0_device_handle, 241 "index:%d Name:%s\n", i, info.name); 242 243 constraint->min_dstate = -1; 244 245 for (j = 0; j < package_count; ++j) { 246 union acpi_object *info_obj = &info.package[j]; 247 union acpi_object *cnstr_pkg; 248 union acpi_object *obj; 249 struct lpi_device_constraint dev_info; 250 251 switch (info_obj->type) { 252 case ACPI_TYPE_INTEGER: 253 /* version */ 254 break; 255 case ACPI_TYPE_PACKAGE: 256 if (info_obj->package.count < 2) 257 break; 258 259 cnstr_pkg = info_obj->package.elements; 260 obj = &cnstr_pkg[0]; 261 dev_info.uid = obj->integer.value; 262 obj = &cnstr_pkg[1]; 263 dev_info.min_dstate = obj->integer.value; 264 265 acpi_handle_debug(lps0_device_handle, 266 "uid:%d min_dstate:%s\n", 267 dev_info.uid, 268 acpi_power_state_string(dev_info.min_dstate)); 269 270 constraint->min_dstate = dev_info.min_dstate; 271 break; 272 } 273 } 274 275 if (constraint->min_dstate < 0) { 276 acpi_handle_debug(lps0_device_handle, 277 "Incomplete constraint defined\n"); 278 continue; 279 } 280 281 lpi_constraints_table_size++; 282 } 283 284 acpi_handle_debug(lps0_device_handle, "LPI: constraints list end\n"); 285 286 free_acpi_buffer: 287 ACPI_FREE(out_obj); 288 } 289 290 static void lpi_check_constraints(void) 291 { 292 int i; 293 294 for (i = 0; i < lpi_constraints_table_size; ++i) { 295 acpi_handle handle = lpi_constraints_table[i].handle; 296 struct acpi_device *adev = acpi_fetch_acpi_dev(handle); 297 298 if (!adev) 299 continue; 300 301 acpi_handle_debug(handle, 302 "LPI: required min power state:%s current power state:%s\n", 303 acpi_power_state_string(lpi_constraints_table[i].min_dstate), 304 acpi_power_state_string(adev->power.state)); 305 306 if (!adev->flags.power_manageable) { 307 acpi_handle_info(handle, "LPI: Device not power manageable\n"); 308 lpi_constraints_table[i].handle = NULL; 309 continue; 310 } 311 312 if (adev->power.state < lpi_constraints_table[i].min_dstate) 313 acpi_handle_info(handle, 314 "LPI: Constraint not met; min power state:%s current power state:%s\n", 315 acpi_power_state_string(lpi_constraints_table[i].min_dstate), 316 acpi_power_state_string(adev->power.state)); 317 } 318 } 319 320 static void acpi_sleep_run_lps0_dsm(unsigned int func, unsigned int func_mask, guid_t dsm_guid) 321 { 322 union acpi_object *out_obj; 323 324 if (!(func_mask & (1 << func))) 325 return; 326 327 out_obj = acpi_evaluate_dsm(lps0_device_handle, &dsm_guid, 328 rev_id, func, NULL); 329 ACPI_FREE(out_obj); 330 331 acpi_handle_debug(lps0_device_handle, "_DSM function %u evaluation %s\n", 332 func, out_obj ? "successful" : "failed"); 333 } 334 335 static bool acpi_s2idle_vendor_amd(void) 336 { 337 return boot_cpu_data.x86_vendor == X86_VENDOR_AMD; 338 } 339 340 static int validate_dsm(acpi_handle handle, const char *uuid, int rev, guid_t *dsm_guid) 341 { 342 union acpi_object *obj; 343 int ret = -EINVAL; 344 345 guid_parse(uuid, dsm_guid); 346 obj = acpi_evaluate_dsm(handle, dsm_guid, rev, 0, NULL); 347 348 /* Check if the _DSM is present and as expected. */ 349 if (!obj || obj->type != ACPI_TYPE_BUFFER || obj->buffer.length == 0 || 350 obj->buffer.length > sizeof(u32)) { 351 acpi_handle_debug(handle, 352 "_DSM UUID %s rev %d function 0 evaluation failed\n", uuid, rev); 353 goto out; 354 } 355 356 ret = *(int *)obj->buffer.pointer; 357 acpi_handle_debug(handle, "_DSM UUID %s rev %d function mask: 0x%x\n", uuid, rev, ret); 358 359 out: 360 ACPI_FREE(obj); 361 return ret; 362 } 363 364 static int lps0_device_attach(struct acpi_device *adev, 365 const struct acpi_device_id *not_used) 366 { 367 if (lps0_device_handle) 368 return 0; 369 370 if (!(acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0)) 371 return 0; 372 373 if (acpi_s2idle_vendor_amd()) { 374 /* AMD0004, AMD0005, AMDI0005: 375 * - Should use rev_id 0x0 376 * - function mask > 0x3: Should use AMD method, but has off by one bug 377 * - function mask = 0x3: Should use Microsoft method 378 * AMDI0006: 379 * - should use rev_id 0x0 380 * - function mask = 0x3: Should use Microsoft method 381 * AMDI0007: 382 * - Should use rev_id 0x2 383 * - Should only use AMD method 384 */ 385 const char *hid = acpi_device_hid(adev); 386 rev_id = strcmp(hid, "AMDI0007") ? 0 : 2; 387 lps0_dsm_func_mask = validate_dsm(adev->handle, 388 ACPI_LPS0_DSM_UUID_AMD, rev_id, &lps0_dsm_guid); 389 lps0_dsm_func_mask_microsoft = validate_dsm(adev->handle, 390 ACPI_LPS0_DSM_UUID_MICROSOFT, 0, 391 &lps0_dsm_guid_microsoft); 392 if (lps0_dsm_func_mask > 0x3 && (!strcmp(hid, "AMD0004") || 393 !strcmp(hid, "AMD0005") || 394 !strcmp(hid, "AMDI0005"))) { 395 lps0_dsm_func_mask = (lps0_dsm_func_mask << 1) | 0x1; 396 acpi_handle_debug(adev->handle, "_DSM UUID %s: Adjusted function mask: 0x%x\n", 397 ACPI_LPS0_DSM_UUID_AMD, lps0_dsm_func_mask); 398 } else if (lps0_dsm_func_mask_microsoft > 0 && !strcmp(hid, "AMDI0007")) { 399 lps0_dsm_func_mask_microsoft = -EINVAL; 400 acpi_handle_debug(adev->handle, "_DSM Using AMD method\n"); 401 } 402 } else { 403 rev_id = 1; 404 lps0_dsm_func_mask = validate_dsm(adev->handle, 405 ACPI_LPS0_DSM_UUID, rev_id, &lps0_dsm_guid); 406 lps0_dsm_func_mask_microsoft = -EINVAL; 407 } 408 409 if (lps0_dsm_func_mask < 0 && lps0_dsm_func_mask_microsoft < 0) 410 return 0; //function evaluation failed 411 412 lps0_device_handle = adev->handle; 413 414 if (acpi_s2idle_vendor_amd()) 415 lpi_device_get_constraints_amd(); 416 else 417 lpi_device_get_constraints(); 418 419 /* 420 * Use suspend-to-idle by default if the default suspend mode was not 421 * set from the command line. 422 */ 423 if (mem_sleep_default > PM_SUSPEND_MEM && !acpi_sleep_default_s3) 424 mem_sleep_current = PM_SUSPEND_TO_IDLE; 425 426 /* 427 * Some Intel based LPS0 systems, like ASUS Zenbook UX430UNR/i7-8550U don't 428 * use intel-hid or intel-vbtn but require the EC GPE to be enabled while 429 * suspended for certain wakeup devices to work, so mark it as wakeup-capable. 430 * 431 * Only enable on !AMD as enabling this universally causes problems for a number 432 * of AMD based systems. 433 */ 434 if (!acpi_s2idle_vendor_amd()) 435 acpi_ec_mark_gpe_for_wake(); 436 437 return 0; 438 } 439 440 static struct acpi_scan_handler lps0_handler = { 441 .ids = lps0_device_ids, 442 .attach = lps0_device_attach, 443 }; 444 445 int acpi_s2idle_prepare_late(void) 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 return 0; 478 } 479 480 void acpi_s2idle_restore_early(void) 481 { 482 if (!lps0_device_handle || sleep_no_lps0) 483 return; 484 485 /* Modern standby exit */ 486 if (lps0_dsm_func_mask_microsoft > 0) 487 acpi_sleep_run_lps0_dsm(ACPI_LPS0_MS_EXIT, 488 lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft); 489 490 /* LPS0 exit */ 491 if (lps0_dsm_func_mask > 0) 492 acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ? 493 ACPI_LPS0_EXIT_AMD : 494 ACPI_LPS0_EXIT, 495 lps0_dsm_func_mask, lps0_dsm_guid); 496 if (lps0_dsm_func_mask_microsoft > 0) 497 acpi_sleep_run_lps0_dsm(ACPI_LPS0_EXIT, 498 lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft); 499 500 /* Screen on */ 501 if (lps0_dsm_func_mask_microsoft > 0) 502 acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_ON, 503 lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft); 504 if (lps0_dsm_func_mask > 0) 505 acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ? 506 ACPI_LPS0_SCREEN_ON_AMD : 507 ACPI_LPS0_SCREEN_ON, 508 lps0_dsm_func_mask, lps0_dsm_guid); 509 } 510 511 static const struct platform_s2idle_ops acpi_s2idle_ops_lps0 = { 512 .begin = acpi_s2idle_begin, 513 .prepare = acpi_s2idle_prepare, 514 .prepare_late = acpi_s2idle_prepare_late, 515 .wake = acpi_s2idle_wake, 516 .restore_early = acpi_s2idle_restore_early, 517 .restore = acpi_s2idle_restore, 518 .end = acpi_s2idle_end, 519 }; 520 521 void acpi_s2idle_setup(void) 522 { 523 acpi_scan_add_handler(&lps0_handler); 524 s2idle_set_ops(&acpi_s2idle_ops_lps0); 525 } 526 527 #endif /* CONFIG_SUSPEND */ 528