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 #define ACPI_LPS0_DSM_UUID "c4eb40a0-6cd2-11e2-bcfd-0800200c9a66" 36 37 #define ACPI_LPS0_GET_DEVICE_CONSTRAINTS 1 38 #define ACPI_LPS0_SCREEN_OFF 3 39 #define ACPI_LPS0_SCREEN_ON 4 40 #define ACPI_LPS0_ENTRY 5 41 #define ACPI_LPS0_EXIT 6 42 43 /* AMD */ 44 #define ACPI_LPS0_DSM_UUID_AMD "e3f32452-febc-43ce-9039-932122d37721" 45 #define ACPI_LPS0_SCREEN_OFF_AMD 4 46 #define ACPI_LPS0_SCREEN_ON_AMD 5 47 48 static acpi_handle lps0_device_handle; 49 static guid_t lps0_dsm_guid; 50 static char lps0_dsm_func_mask; 51 52 /* Device constraint entry structure */ 53 struct lpi_device_info { 54 char *name; 55 int enabled; 56 union acpi_object *package; 57 }; 58 59 /* Constraint package structure */ 60 struct lpi_device_constraint { 61 int uid; 62 int min_dstate; 63 int function_states; 64 }; 65 66 struct lpi_constraints { 67 acpi_handle handle; 68 int min_dstate; 69 }; 70 71 /* AMD */ 72 /* Device constraint entry structure */ 73 struct lpi_device_info_amd { 74 int revision; 75 int count; 76 union acpi_object *package; 77 }; 78 79 /* Constraint package structure */ 80 struct lpi_device_constraint_amd { 81 char *name; 82 int enabled; 83 int function_states; 84 int min_dstate; 85 }; 86 87 static struct lpi_constraints *lpi_constraints_table; 88 static int lpi_constraints_table_size; 89 static int rev_id; 90 91 static void lpi_device_get_constraints_amd(void) 92 { 93 union acpi_object *out_obj; 94 int i, j, k; 95 96 out_obj = acpi_evaluate_dsm_typed(lps0_device_handle, &lps0_dsm_guid, 97 1, ACPI_LPS0_GET_DEVICE_CONSTRAINTS, 98 NULL, ACPI_TYPE_PACKAGE); 99 100 if (!out_obj) 101 return; 102 103 acpi_handle_debug(lps0_device_handle, "_DSM function 1 eval %s\n", 104 out_obj ? "successful" : "failed"); 105 106 for (i = 0; i < out_obj->package.count; i++) { 107 union acpi_object *package = &out_obj->package.elements[i]; 108 struct lpi_device_info_amd info = { }; 109 110 if (package->type == ACPI_TYPE_INTEGER) { 111 switch (i) { 112 case 0: 113 info.revision = package->integer.value; 114 break; 115 case 1: 116 info.count = package->integer.value; 117 break; 118 } 119 } else if (package->type == ACPI_TYPE_PACKAGE) { 120 lpi_constraints_table = kcalloc(package->package.count, 121 sizeof(*lpi_constraints_table), 122 GFP_KERNEL); 123 124 if (!lpi_constraints_table) 125 goto free_acpi_buffer; 126 127 acpi_handle_debug(lps0_device_handle, 128 "LPI: constraints list begin:\n"); 129 130 for (j = 0; j < package->package.count; ++j) { 131 union acpi_object *info_obj = &package->package.elements[j]; 132 struct lpi_device_constraint_amd dev_info = {}; 133 struct lpi_constraints *list; 134 acpi_status status; 135 136 for (k = 0; k < info_obj->package.count; ++k) { 137 union acpi_object *obj = &info_obj->package.elements[k]; 138 union acpi_object *obj_new; 139 140 list = &lpi_constraints_table[lpi_constraints_table_size]; 141 list->min_dstate = -1; 142 143 obj_new = &obj[k]; 144 switch (k) { 145 case 0: 146 dev_info.enabled = obj->integer.value; 147 break; 148 case 1: 149 dev_info.name = obj->string.pointer; 150 break; 151 case 2: 152 dev_info.function_states = obj->integer.value; 153 break; 154 case 3: 155 dev_info.min_dstate = obj->integer.value; 156 break; 157 } 158 159 if (!dev_info.enabled || !dev_info.name || 160 !dev_info.min_dstate) 161 continue; 162 163 status = acpi_get_handle(NULL, dev_info.name, 164 &list->handle); 165 if (ACPI_FAILURE(status)) 166 continue; 167 168 acpi_handle_debug(lps0_device_handle, 169 "Name:%s\n", dev_info.name); 170 171 list->min_dstate = dev_info.min_dstate; 172 173 if (list->min_dstate < 0) { 174 acpi_handle_debug(lps0_device_handle, 175 "Incomplete constraint defined\n"); 176 continue; 177 } 178 } 179 lpi_constraints_table_size++; 180 } 181 } 182 } 183 184 acpi_handle_debug(lps0_device_handle, "LPI: constraints list end\n"); 185 186 free_acpi_buffer: 187 ACPI_FREE(out_obj); 188 } 189 190 static void lpi_device_get_constraints(void) 191 { 192 union acpi_object *out_obj; 193 int i; 194 195 out_obj = acpi_evaluate_dsm_typed(lps0_device_handle, &lps0_dsm_guid, 196 1, ACPI_LPS0_GET_DEVICE_CONSTRAINTS, 197 NULL, ACPI_TYPE_PACKAGE); 198 199 acpi_handle_debug(lps0_device_handle, "_DSM function 1 eval %s\n", 200 out_obj ? "successful" : "failed"); 201 202 if (!out_obj) 203 return; 204 205 lpi_constraints_table = kcalloc(out_obj->package.count, 206 sizeof(*lpi_constraints_table), 207 GFP_KERNEL); 208 if (!lpi_constraints_table) 209 goto free_acpi_buffer; 210 211 acpi_handle_debug(lps0_device_handle, "LPI: constraints list begin:\n"); 212 213 for (i = 0; i < out_obj->package.count; i++) { 214 struct lpi_constraints *constraint; 215 acpi_status status; 216 union acpi_object *package = &out_obj->package.elements[i]; 217 struct lpi_device_info info = { }; 218 int package_count = 0, j; 219 220 if (!package) 221 continue; 222 223 for (j = 0; j < package->package.count; ++j) { 224 union acpi_object *element = 225 &(package->package.elements[j]); 226 227 switch (element->type) { 228 case ACPI_TYPE_INTEGER: 229 info.enabled = element->integer.value; 230 break; 231 case ACPI_TYPE_STRING: 232 info.name = element->string.pointer; 233 break; 234 case ACPI_TYPE_PACKAGE: 235 package_count = element->package.count; 236 info.package = element->package.elements; 237 break; 238 } 239 } 240 241 if (!info.enabled || !info.package || !info.name) 242 continue; 243 244 constraint = &lpi_constraints_table[lpi_constraints_table_size]; 245 246 status = acpi_get_handle(NULL, info.name, &constraint->handle); 247 if (ACPI_FAILURE(status)) 248 continue; 249 250 acpi_handle_debug(lps0_device_handle, 251 "index:%d Name:%s\n", i, info.name); 252 253 constraint->min_dstate = -1; 254 255 for (j = 0; j < package_count; ++j) { 256 union acpi_object *info_obj = &info.package[j]; 257 union acpi_object *cnstr_pkg; 258 union acpi_object *obj; 259 struct lpi_device_constraint dev_info; 260 261 switch (info_obj->type) { 262 case ACPI_TYPE_INTEGER: 263 /* version */ 264 break; 265 case ACPI_TYPE_PACKAGE: 266 if (info_obj->package.count < 2) 267 break; 268 269 cnstr_pkg = info_obj->package.elements; 270 obj = &cnstr_pkg[0]; 271 dev_info.uid = obj->integer.value; 272 obj = &cnstr_pkg[1]; 273 dev_info.min_dstate = obj->integer.value; 274 275 acpi_handle_debug(lps0_device_handle, 276 "uid:%d min_dstate:%s\n", 277 dev_info.uid, 278 acpi_power_state_string(dev_info.min_dstate)); 279 280 constraint->min_dstate = dev_info.min_dstate; 281 break; 282 } 283 } 284 285 if (constraint->min_dstate < 0) { 286 acpi_handle_debug(lps0_device_handle, 287 "Incomplete constraint defined\n"); 288 continue; 289 } 290 291 lpi_constraints_table_size++; 292 } 293 294 acpi_handle_debug(lps0_device_handle, "LPI: constraints list end\n"); 295 296 free_acpi_buffer: 297 ACPI_FREE(out_obj); 298 } 299 300 static void lpi_check_constraints(void) 301 { 302 int i; 303 304 for (i = 0; i < lpi_constraints_table_size; ++i) { 305 acpi_handle handle = lpi_constraints_table[i].handle; 306 struct acpi_device *adev; 307 308 if (!handle || acpi_bus_get_device(handle, &adev)) 309 continue; 310 311 acpi_handle_debug(handle, 312 "LPI: required min power state:%s current power state:%s\n", 313 acpi_power_state_string(lpi_constraints_table[i].min_dstate), 314 acpi_power_state_string(adev->power.state)); 315 316 if (!adev->flags.power_manageable) { 317 acpi_handle_info(handle, "LPI: Device not power manageable\n"); 318 lpi_constraints_table[i].handle = NULL; 319 continue; 320 } 321 322 if (adev->power.state < lpi_constraints_table[i].min_dstate) 323 acpi_handle_info(handle, 324 "LPI: Constraint not met; min power state:%s current power state:%s\n", 325 acpi_power_state_string(lpi_constraints_table[i].min_dstate), 326 acpi_power_state_string(adev->power.state)); 327 } 328 } 329 330 static void acpi_sleep_run_lps0_dsm(unsigned int func) 331 { 332 union acpi_object *out_obj; 333 334 if (!(lps0_dsm_func_mask & (1 << func))) 335 return; 336 337 out_obj = acpi_evaluate_dsm(lps0_device_handle, &lps0_dsm_guid, rev_id, func, NULL); 338 ACPI_FREE(out_obj); 339 340 acpi_handle_debug(lps0_device_handle, "_DSM function %u evaluation %s\n", 341 func, out_obj ? "successful" : "failed"); 342 } 343 344 static bool acpi_s2idle_vendor_amd(void) 345 { 346 return boot_cpu_data.x86_vendor == X86_VENDOR_AMD; 347 } 348 349 static int lps0_device_attach(struct acpi_device *adev, 350 const struct acpi_device_id *not_used) 351 { 352 union acpi_object *out_obj; 353 354 if (lps0_device_handle) 355 return 0; 356 357 if (!(acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0)) 358 return 0; 359 360 if (acpi_s2idle_vendor_amd()) { 361 guid_parse(ACPI_LPS0_DSM_UUID_AMD, &lps0_dsm_guid); 362 out_obj = acpi_evaluate_dsm(adev->handle, &lps0_dsm_guid, 0, 0, NULL); 363 rev_id = 0; 364 } else { 365 guid_parse(ACPI_LPS0_DSM_UUID, &lps0_dsm_guid); 366 out_obj = acpi_evaluate_dsm(adev->handle, &lps0_dsm_guid, 1, 0, NULL); 367 rev_id = 1; 368 } 369 370 /* Check if the _DSM is present and as expected. */ 371 if (!out_obj || out_obj->type != ACPI_TYPE_BUFFER) { 372 acpi_handle_debug(adev->handle, 373 "_DSM function 0 evaluation failed\n"); 374 return 0; 375 } 376 377 lps0_dsm_func_mask = *(char *)out_obj->buffer.pointer; 378 379 ACPI_FREE(out_obj); 380 381 acpi_handle_debug(adev->handle, "_DSM function mask: 0x%x\n", 382 lps0_dsm_func_mask); 383 384 lps0_device_handle = adev->handle; 385 386 if (acpi_s2idle_vendor_amd()) 387 lpi_device_get_constraints_amd(); 388 else 389 lpi_device_get_constraints(); 390 391 /* 392 * Use suspend-to-idle by default if the default suspend mode was not 393 * set from the command line. 394 */ 395 if (mem_sleep_default > PM_SUSPEND_MEM && !acpi_sleep_default_s3) 396 mem_sleep_current = PM_SUSPEND_TO_IDLE; 397 398 /* 399 * Some LPS0 systems, like ASUS Zenbook UX430UNR/i7-8550U, require the 400 * EC GPE to be enabled while suspended for certain wakeup devices to 401 * work, so mark it as wakeup-capable. 402 */ 403 acpi_ec_mark_gpe_for_wake(); 404 405 return 0; 406 } 407 408 static struct acpi_scan_handler lps0_handler = { 409 .ids = lps0_device_ids, 410 .attach = lps0_device_attach, 411 }; 412 413 int acpi_s2idle_prepare_late(void) 414 { 415 if (!lps0_device_handle || sleep_no_lps0) 416 return 0; 417 418 if (pm_debug_messages_on) 419 lpi_check_constraints(); 420 421 if (acpi_s2idle_vendor_amd()) { 422 acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_OFF_AMD); 423 } else { 424 acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_OFF); 425 acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY); 426 } 427 428 return 0; 429 } 430 431 void acpi_s2idle_restore_early(void) 432 { 433 if (!lps0_device_handle || sleep_no_lps0) 434 return; 435 436 if (acpi_s2idle_vendor_amd()) { 437 acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_ON_AMD); 438 } else { 439 acpi_sleep_run_lps0_dsm(ACPI_LPS0_EXIT); 440 acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_ON); 441 } 442 } 443 444 static const struct platform_s2idle_ops acpi_s2idle_ops_lps0 = { 445 .begin = acpi_s2idle_begin, 446 .prepare = acpi_s2idle_prepare, 447 .prepare_late = acpi_s2idle_prepare_late, 448 .wake = acpi_s2idle_wake, 449 .restore_early = acpi_s2idle_restore_early, 450 .restore = acpi_s2idle_restore, 451 .end = acpi_s2idle_end, 452 }; 453 454 void acpi_s2idle_setup(void) 455 { 456 acpi_scan_add_handler(&lps0_handler); 457 s2idle_set_ops(&acpi_s2idle_ops_lps0); 458 } 459 460 #endif /* CONFIG_SUSPEND */ 461