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