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