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 355dbf5099SPratik Vishwakarma /* Microsoft platform agnostic UUID */ 365dbf5099SPratik Vishwakarma #define ACPI_LPS0_DSM_UUID_MICROSOFT "11e00d56-ce64-47ce-837b-1f898f9aa461" 375dbf5099SPratik Vishwakarma 38fef98671SRafael J. Wysocki #define ACPI_LPS0_DSM_UUID "c4eb40a0-6cd2-11e2-bcfd-0800200c9a66" 39fef98671SRafael J. Wysocki 40fef98671SRafael J. Wysocki #define ACPI_LPS0_GET_DEVICE_CONSTRAINTS 1 41fef98671SRafael J. Wysocki #define ACPI_LPS0_SCREEN_OFF 3 42fef98671SRafael J. Wysocki #define ACPI_LPS0_SCREEN_ON 4 43fef98671SRafael J. Wysocki #define ACPI_LPS0_ENTRY 5 44fef98671SRafael J. Wysocki #define ACPI_LPS0_EXIT 6 455dbf5099SPratik Vishwakarma #define ACPI_LPS0_MS_ENTRY 7 465dbf5099SPratik Vishwakarma #define ACPI_LPS0_MS_EXIT 8 47fef98671SRafael J. Wysocki 48fef98671SRafael J. Wysocki /* AMD */ 49fef98671SRafael J. Wysocki #define ACPI_LPS0_DSM_UUID_AMD "e3f32452-febc-43ce-9039-932122d37721" 50f59a905bSAlex Deucher #define ACPI_LPS0_ENTRY_AMD 2 51f59a905bSAlex Deucher #define ACPI_LPS0_EXIT_AMD 3 52fef98671SRafael J. Wysocki #define ACPI_LPS0_SCREEN_OFF_AMD 4 53fef98671SRafael J. Wysocki #define ACPI_LPS0_SCREEN_ON_AMD 5 54fef98671SRafael J. Wysocki 55fef98671SRafael J. Wysocki static acpi_handle lps0_device_handle; 56fef98671SRafael J. Wysocki static guid_t lps0_dsm_guid; 574a012dc8SPratik Vishwakarma static int lps0_dsm_func_mask; 58fef98671SRafael J. Wysocki 595dbf5099SPratik Vishwakarma static guid_t lps0_dsm_guid_microsoft; 605dbf5099SPratik Vishwakarma static int lps0_dsm_func_mask_microsoft; 615dbf5099SPratik Vishwakarma 62fef98671SRafael J. Wysocki /* Device constraint entry structure */ 63fef98671SRafael J. Wysocki struct lpi_device_info { 64fef98671SRafael J. Wysocki char *name; 65fef98671SRafael J. Wysocki int enabled; 66fef98671SRafael J. Wysocki union acpi_object *package; 67fef98671SRafael J. Wysocki }; 68fef98671SRafael J. Wysocki 69fef98671SRafael J. Wysocki /* Constraint package structure */ 70fef98671SRafael J. Wysocki struct lpi_device_constraint { 71fef98671SRafael J. Wysocki int uid; 72fef98671SRafael J. Wysocki int min_dstate; 73fef98671SRafael J. Wysocki int function_states; 74fef98671SRafael J. Wysocki }; 75fef98671SRafael J. Wysocki 76fef98671SRafael J. Wysocki struct lpi_constraints { 77fef98671SRafael J. Wysocki acpi_handle handle; 78fef98671SRafael J. Wysocki int min_dstate; 79fef98671SRafael J. Wysocki }; 80fef98671SRafael J. Wysocki 814a012dc8SPratik Vishwakarma /* AMD Constraint package structure */ 82fef98671SRafael J. Wysocki struct lpi_device_constraint_amd { 83fef98671SRafael J. Wysocki char *name; 84fef98671SRafael J. Wysocki int enabled; 85fef98671SRafael J. Wysocki int function_states; 86fef98671SRafael J. Wysocki int min_dstate; 87fef98671SRafael J. Wysocki }; 88fef98671SRafael J. Wysocki 89fef98671SRafael J. Wysocki static struct lpi_constraints *lpi_constraints_table; 90fef98671SRafael J. Wysocki static int lpi_constraints_table_size; 91fef98671SRafael J. Wysocki static int rev_id; 92fef98671SRafael J. Wysocki 93fef98671SRafael J. Wysocki static void lpi_device_get_constraints_amd(void) 94fef98671SRafael J. Wysocki { 95fef98671SRafael J. Wysocki union acpi_object *out_obj; 96fef98671SRafael J. Wysocki int i, j, k; 97fef98671SRafael J. Wysocki 98fef98671SRafael J. Wysocki out_obj = acpi_evaluate_dsm_typed(lps0_device_handle, &lps0_dsm_guid, 99904d4a6cSPratik Vishwakarma rev_id, ACPI_LPS0_GET_DEVICE_CONSTRAINTS, 100fef98671SRafael J. Wysocki NULL, ACPI_TYPE_PACKAGE); 101fef98671SRafael J. Wysocki 102fef98671SRafael J. Wysocki acpi_handle_debug(lps0_device_handle, "_DSM function 1 eval %s\n", 103fef98671SRafael J. Wysocki out_obj ? "successful" : "failed"); 104fef98671SRafael J. Wysocki 1054a012dc8SPratik Vishwakarma if (!out_obj) 1064a012dc8SPratik Vishwakarma return; 1074a012dc8SPratik Vishwakarma 108fef98671SRafael J. Wysocki for (i = 0; i < out_obj->package.count; i++) { 109fef98671SRafael J. Wysocki union acpi_object *package = &out_obj->package.elements[i]; 110fef98671SRafael J. Wysocki 111aa7a1bb0SRafael J. Wysocki if (package->type == ACPI_TYPE_PACKAGE) { 112fef98671SRafael J. Wysocki lpi_constraints_table = kcalloc(package->package.count, 113fef98671SRafael J. Wysocki sizeof(*lpi_constraints_table), 114fef98671SRafael J. Wysocki GFP_KERNEL); 115fef98671SRafael J. Wysocki 116fef98671SRafael J. Wysocki if (!lpi_constraints_table) 117fef98671SRafael J. Wysocki goto free_acpi_buffer; 118fef98671SRafael J. Wysocki 119fef98671SRafael J. Wysocki acpi_handle_debug(lps0_device_handle, 120fef98671SRafael J. Wysocki "LPI: constraints list begin:\n"); 121fef98671SRafael J. Wysocki 122fef98671SRafael J. Wysocki for (j = 0; j < package->package.count; ++j) { 123fef98671SRafael J. Wysocki union acpi_object *info_obj = &package->package.elements[j]; 124fef98671SRafael J. Wysocki struct lpi_device_constraint_amd dev_info = {}; 125fef98671SRafael J. Wysocki struct lpi_constraints *list; 126fef98671SRafael J. Wysocki acpi_status status; 127fef98671SRafael J. Wysocki 128fef98671SRafael J. Wysocki for (k = 0; k < info_obj->package.count; ++k) { 129fef98671SRafael J. Wysocki union acpi_object *obj = &info_obj->package.elements[k]; 130fef98671SRafael J. Wysocki 131fef98671SRafael J. Wysocki list = &lpi_constraints_table[lpi_constraints_table_size]; 132fef98671SRafael J. Wysocki list->min_dstate = -1; 133fef98671SRafael J. Wysocki 134fef98671SRafael J. Wysocki switch (k) { 135fef98671SRafael J. Wysocki case 0: 136fef98671SRafael J. Wysocki dev_info.enabled = obj->integer.value; 137fef98671SRafael J. Wysocki break; 138fef98671SRafael J. Wysocki case 1: 139fef98671SRafael J. Wysocki dev_info.name = obj->string.pointer; 140fef98671SRafael J. Wysocki break; 141fef98671SRafael J. Wysocki case 2: 142fef98671SRafael J. Wysocki dev_info.function_states = obj->integer.value; 143fef98671SRafael J. Wysocki break; 144fef98671SRafael J. Wysocki case 3: 145fef98671SRafael J. Wysocki dev_info.min_dstate = obj->integer.value; 146fef98671SRafael J. Wysocki break; 147fef98671SRafael J. Wysocki } 148fef98671SRafael J. Wysocki 149fef98671SRafael J. Wysocki if (!dev_info.enabled || !dev_info.name || 150fef98671SRafael J. Wysocki !dev_info.min_dstate) 151fef98671SRafael J. Wysocki continue; 152fef98671SRafael J. Wysocki 153fef98671SRafael J. Wysocki status = acpi_get_handle(NULL, dev_info.name, 154fef98671SRafael J. Wysocki &list->handle); 155fef98671SRafael J. Wysocki if (ACPI_FAILURE(status)) 156fef98671SRafael J. Wysocki continue; 157fef98671SRafael J. Wysocki 158fef98671SRafael J. Wysocki acpi_handle_debug(lps0_device_handle, 159fef98671SRafael J. Wysocki "Name:%s\n", dev_info.name); 160fef98671SRafael J. Wysocki 161fef98671SRafael J. Wysocki list->min_dstate = dev_info.min_dstate; 162fef98671SRafael J. Wysocki 163fef98671SRafael J. Wysocki if (list->min_dstate < 0) { 164fef98671SRafael J. Wysocki acpi_handle_debug(lps0_device_handle, 165fef98671SRafael J. Wysocki "Incomplete constraint defined\n"); 166fef98671SRafael J. Wysocki continue; 167fef98671SRafael J. Wysocki } 168fef98671SRafael J. Wysocki } 169fef98671SRafael J. Wysocki lpi_constraints_table_size++; 170fef98671SRafael J. Wysocki } 171fef98671SRafael J. Wysocki } 172fef98671SRafael J. Wysocki } 173fef98671SRafael J. Wysocki 174fef98671SRafael J. Wysocki acpi_handle_debug(lps0_device_handle, "LPI: constraints list end\n"); 175fef98671SRafael J. Wysocki 176fef98671SRafael J. Wysocki free_acpi_buffer: 177fef98671SRafael J. Wysocki ACPI_FREE(out_obj); 178fef98671SRafael J. Wysocki } 179fef98671SRafael J. Wysocki 180fef98671SRafael J. Wysocki static void lpi_device_get_constraints(void) 181fef98671SRafael J. Wysocki { 182fef98671SRafael J. Wysocki union acpi_object *out_obj; 183fef98671SRafael J. Wysocki int i; 184fef98671SRafael J. Wysocki 185fef98671SRafael J. Wysocki out_obj = acpi_evaluate_dsm_typed(lps0_device_handle, &lps0_dsm_guid, 186fef98671SRafael J. Wysocki 1, ACPI_LPS0_GET_DEVICE_CONSTRAINTS, 187fef98671SRafael J. Wysocki NULL, ACPI_TYPE_PACKAGE); 188fef98671SRafael J. Wysocki 189fef98671SRafael J. Wysocki acpi_handle_debug(lps0_device_handle, "_DSM function 1 eval %s\n", 190fef98671SRafael J. Wysocki out_obj ? "successful" : "failed"); 191fef98671SRafael J. Wysocki 192fef98671SRafael J. Wysocki if (!out_obj) 193fef98671SRafael J. Wysocki return; 194fef98671SRafael J. Wysocki 195fef98671SRafael J. Wysocki lpi_constraints_table = kcalloc(out_obj->package.count, 196fef98671SRafael J. Wysocki sizeof(*lpi_constraints_table), 197fef98671SRafael J. Wysocki GFP_KERNEL); 198fef98671SRafael J. Wysocki if (!lpi_constraints_table) 199fef98671SRafael J. Wysocki goto free_acpi_buffer; 200fef98671SRafael J. Wysocki 201fef98671SRafael J. Wysocki acpi_handle_debug(lps0_device_handle, "LPI: constraints list begin:\n"); 202fef98671SRafael J. Wysocki 203fef98671SRafael J. Wysocki for (i = 0; i < out_obj->package.count; i++) { 204fef98671SRafael J. Wysocki struct lpi_constraints *constraint; 205fef98671SRafael J. Wysocki acpi_status status; 206fef98671SRafael J. Wysocki union acpi_object *package = &out_obj->package.elements[i]; 207fef98671SRafael J. Wysocki struct lpi_device_info info = { }; 208fef98671SRafael J. Wysocki int package_count = 0, j; 209fef98671SRafael J. Wysocki 210fef98671SRafael J. Wysocki if (!package) 211fef98671SRafael J. Wysocki continue; 212fef98671SRafael J. Wysocki 213fef98671SRafael J. Wysocki for (j = 0; j < package->package.count; ++j) { 214fef98671SRafael J. Wysocki union acpi_object *element = 215fef98671SRafael J. Wysocki &(package->package.elements[j]); 216fef98671SRafael J. Wysocki 217fef98671SRafael J. Wysocki switch (element->type) { 218fef98671SRafael J. Wysocki case ACPI_TYPE_INTEGER: 219fef98671SRafael J. Wysocki info.enabled = element->integer.value; 220fef98671SRafael J. Wysocki break; 221fef98671SRafael J. Wysocki case ACPI_TYPE_STRING: 222fef98671SRafael J. Wysocki info.name = element->string.pointer; 223fef98671SRafael J. Wysocki break; 224fef98671SRafael J. Wysocki case ACPI_TYPE_PACKAGE: 225fef98671SRafael J. Wysocki package_count = element->package.count; 226fef98671SRafael J. Wysocki info.package = element->package.elements; 227fef98671SRafael J. Wysocki break; 228fef98671SRafael J. Wysocki } 229fef98671SRafael J. Wysocki } 230fef98671SRafael J. Wysocki 231fef98671SRafael J. Wysocki if (!info.enabled || !info.package || !info.name) 232fef98671SRafael J. Wysocki continue; 233fef98671SRafael J. Wysocki 234fef98671SRafael J. Wysocki constraint = &lpi_constraints_table[lpi_constraints_table_size]; 235fef98671SRafael J. Wysocki 236fef98671SRafael J. Wysocki status = acpi_get_handle(NULL, info.name, &constraint->handle); 237fef98671SRafael J. Wysocki if (ACPI_FAILURE(status)) 238fef98671SRafael J. Wysocki continue; 239fef98671SRafael J. Wysocki 240fef98671SRafael J. Wysocki acpi_handle_debug(lps0_device_handle, 241fef98671SRafael J. Wysocki "index:%d Name:%s\n", i, info.name); 242fef98671SRafael J. Wysocki 243fef98671SRafael J. Wysocki constraint->min_dstate = -1; 244fef98671SRafael J. Wysocki 245fef98671SRafael J. Wysocki for (j = 0; j < package_count; ++j) { 246fef98671SRafael J. Wysocki union acpi_object *info_obj = &info.package[j]; 247fef98671SRafael J. Wysocki union acpi_object *cnstr_pkg; 248fef98671SRafael J. Wysocki union acpi_object *obj; 249fef98671SRafael J. Wysocki struct lpi_device_constraint dev_info; 250fef98671SRafael J. Wysocki 251fef98671SRafael J. Wysocki switch (info_obj->type) { 252fef98671SRafael J. Wysocki case ACPI_TYPE_INTEGER: 253fef98671SRafael J. Wysocki /* version */ 254fef98671SRafael J. Wysocki break; 255fef98671SRafael J. Wysocki case ACPI_TYPE_PACKAGE: 256fef98671SRafael J. Wysocki if (info_obj->package.count < 2) 257fef98671SRafael J. Wysocki break; 258fef98671SRafael J. Wysocki 259fef98671SRafael J. Wysocki cnstr_pkg = info_obj->package.elements; 260fef98671SRafael J. Wysocki obj = &cnstr_pkg[0]; 261fef98671SRafael J. Wysocki dev_info.uid = obj->integer.value; 262fef98671SRafael J. Wysocki obj = &cnstr_pkg[1]; 263fef98671SRafael J. Wysocki dev_info.min_dstate = obj->integer.value; 264fef98671SRafael J. Wysocki 265fef98671SRafael J. Wysocki acpi_handle_debug(lps0_device_handle, 266fef98671SRafael J. Wysocki "uid:%d min_dstate:%s\n", 267fef98671SRafael J. Wysocki dev_info.uid, 268fef98671SRafael J. Wysocki acpi_power_state_string(dev_info.min_dstate)); 269fef98671SRafael J. Wysocki 270fef98671SRafael J. Wysocki constraint->min_dstate = dev_info.min_dstate; 271fef98671SRafael J. Wysocki break; 272fef98671SRafael J. Wysocki } 273fef98671SRafael J. Wysocki } 274fef98671SRafael J. Wysocki 275fef98671SRafael J. Wysocki if (constraint->min_dstate < 0) { 276fef98671SRafael J. Wysocki acpi_handle_debug(lps0_device_handle, 277fef98671SRafael J. Wysocki "Incomplete constraint defined\n"); 278fef98671SRafael J. Wysocki continue; 279fef98671SRafael J. Wysocki } 280fef98671SRafael J. Wysocki 281fef98671SRafael J. Wysocki lpi_constraints_table_size++; 282fef98671SRafael J. Wysocki } 283fef98671SRafael J. Wysocki 284fef98671SRafael J. Wysocki acpi_handle_debug(lps0_device_handle, "LPI: constraints list end\n"); 285fef98671SRafael J. Wysocki 286fef98671SRafael J. Wysocki free_acpi_buffer: 287fef98671SRafael J. Wysocki ACPI_FREE(out_obj); 288fef98671SRafael J. Wysocki } 289fef98671SRafael J. Wysocki 290fef98671SRafael J. Wysocki static void lpi_check_constraints(void) 291fef98671SRafael J. Wysocki { 292fef98671SRafael J. Wysocki int i; 293fef98671SRafael J. Wysocki 294fef98671SRafael J. Wysocki for (i = 0; i < lpi_constraints_table_size; ++i) { 295fef98671SRafael J. Wysocki acpi_handle handle = lpi_constraints_table[i].handle; 29699ece713SRafael J. Wysocki struct acpi_device *adev = acpi_fetch_acpi_dev(handle); 297fef98671SRafael J. Wysocki 29899ece713SRafael J. Wysocki if (!adev) 299fef98671SRafael J. Wysocki continue; 300fef98671SRafael J. Wysocki 301fef98671SRafael J. Wysocki acpi_handle_debug(handle, 302fef98671SRafael J. Wysocki "LPI: required min power state:%s current power state:%s\n", 303fef98671SRafael J. Wysocki acpi_power_state_string(lpi_constraints_table[i].min_dstate), 304fef98671SRafael J. Wysocki acpi_power_state_string(adev->power.state)); 305fef98671SRafael J. Wysocki 306fef98671SRafael J. Wysocki if (!adev->flags.power_manageable) { 307fef98671SRafael J. Wysocki acpi_handle_info(handle, "LPI: Device not power manageable\n"); 308fef98671SRafael J. Wysocki lpi_constraints_table[i].handle = NULL; 309fef98671SRafael J. Wysocki continue; 310fef98671SRafael J. Wysocki } 311fef98671SRafael J. Wysocki 312fef98671SRafael J. Wysocki if (adev->power.state < lpi_constraints_table[i].min_dstate) 313fef98671SRafael J. Wysocki acpi_handle_info(handle, 314fef98671SRafael J. Wysocki "LPI: Constraint not met; min power state:%s current power state:%s\n", 315fef98671SRafael J. Wysocki acpi_power_state_string(lpi_constraints_table[i].min_dstate), 316fef98671SRafael J. Wysocki acpi_power_state_string(adev->power.state)); 317fef98671SRafael J. Wysocki } 318fef98671SRafael J. Wysocki } 319fef98671SRafael J. Wysocki 3203f4b116cSPratik Vishwakarma static void acpi_sleep_run_lps0_dsm(unsigned int func, unsigned int func_mask, guid_t dsm_guid) 321fef98671SRafael J. Wysocki { 322fef98671SRafael J. Wysocki union acpi_object *out_obj; 323fef98671SRafael J. Wysocki 3243f4b116cSPratik Vishwakarma if (!(func_mask & (1 << func))) 325fef98671SRafael J. Wysocki return; 326fef98671SRafael J. Wysocki 3273f4b116cSPratik Vishwakarma out_obj = acpi_evaluate_dsm(lps0_device_handle, &dsm_guid, 3283f4b116cSPratik Vishwakarma rev_id, func, NULL); 329fef98671SRafael J. Wysocki ACPI_FREE(out_obj); 330fef98671SRafael J. Wysocki 331fef98671SRafael J. Wysocki acpi_handle_debug(lps0_device_handle, "_DSM function %u evaluation %s\n", 332fef98671SRafael J. Wysocki func, out_obj ? "successful" : "failed"); 333fef98671SRafael J. Wysocki } 334fef98671SRafael J. Wysocki 335fef98671SRafael J. Wysocki static bool acpi_s2idle_vendor_amd(void) 336fef98671SRafael J. Wysocki { 337fef98671SRafael J. Wysocki return boot_cpu_data.x86_vendor == X86_VENDOR_AMD; 338fef98671SRafael J. Wysocki } 339fef98671SRafael J. Wysocki 3404a012dc8SPratik Vishwakarma static int validate_dsm(acpi_handle handle, const char *uuid, int rev, guid_t *dsm_guid) 3414a012dc8SPratik Vishwakarma { 3424a012dc8SPratik Vishwakarma union acpi_object *obj; 3434a012dc8SPratik Vishwakarma int ret = -EINVAL; 3444a012dc8SPratik Vishwakarma 3454a012dc8SPratik Vishwakarma guid_parse(uuid, dsm_guid); 3464a012dc8SPratik Vishwakarma obj = acpi_evaluate_dsm(handle, dsm_guid, rev, 0, NULL); 3474a012dc8SPratik Vishwakarma 3484a012dc8SPratik Vishwakarma /* Check if the _DSM is present and as expected. */ 3494a012dc8SPratik Vishwakarma if (!obj || obj->type != ACPI_TYPE_BUFFER || obj->buffer.length == 0 || 3504a012dc8SPratik Vishwakarma obj->buffer.length > sizeof(u32)) { 3514a012dc8SPratik Vishwakarma acpi_handle_debug(handle, 3524a012dc8SPratik Vishwakarma "_DSM UUID %s rev %d function 0 evaluation failed\n", uuid, rev); 3534a012dc8SPratik Vishwakarma goto out; 3544a012dc8SPratik Vishwakarma } 3554a012dc8SPratik Vishwakarma 3564a012dc8SPratik Vishwakarma ret = *(int *)obj->buffer.pointer; 3574a012dc8SPratik Vishwakarma acpi_handle_debug(handle, "_DSM UUID %s rev %d function mask: 0x%x\n", uuid, rev, ret); 3584a012dc8SPratik Vishwakarma 3594a012dc8SPratik Vishwakarma out: 3604a012dc8SPratik Vishwakarma ACPI_FREE(obj); 3614a012dc8SPratik Vishwakarma return ret; 3624a012dc8SPratik Vishwakarma } 3634a012dc8SPratik Vishwakarma 364fef98671SRafael J. Wysocki static int lps0_device_attach(struct acpi_device *adev, 365fef98671SRafael J. Wysocki const struct acpi_device_id *not_used) 366fef98671SRafael J. Wysocki { 367fef98671SRafael J. Wysocki if (lps0_device_handle) 368fef98671SRafael J. Wysocki return 0; 369fef98671SRafael J. Wysocki 370fef98671SRafael J. Wysocki if (!(acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0)) 371fef98671SRafael J. Wysocki return 0; 372fef98671SRafael J. Wysocki 373fef98671SRafael J. Wysocki if (acpi_s2idle_vendor_amd()) { 3741ea1dbf1SSachi King /* AMD0004, AMD0005, AMDI0005: 3758fbd6c15SMario Limonciello * - Should use rev_id 0x0 3768fbd6c15SMario Limonciello * - function mask > 0x3: Should use AMD method, but has off by one bug 3778fbd6c15SMario Limonciello * - function mask = 0x3: Should use Microsoft method 3788fbd6c15SMario Limonciello * AMDI0006: 3798fbd6c15SMario Limonciello * - should use rev_id 0x0 3808fbd6c15SMario Limonciello * - function mask = 0x3: Should use Microsoft method 381f0c62255SMario Limonciello * AMDI0007: 382f0c62255SMario Limonciello * - Should use rev_id 0x2 383f0c62255SMario Limonciello * - Should only use AMD method 3848fbd6c15SMario Limonciello */ 3858fbd6c15SMario Limonciello const char *hid = acpi_device_hid(adev); 386f0c62255SMario Limonciello rev_id = strcmp(hid, "AMDI0007") ? 0 : 2; 3874a012dc8SPratik Vishwakarma lps0_dsm_func_mask = validate_dsm(adev->handle, 3884a012dc8SPratik Vishwakarma ACPI_LPS0_DSM_UUID_AMD, rev_id, &lps0_dsm_guid); 3895dbf5099SPratik Vishwakarma lps0_dsm_func_mask_microsoft = validate_dsm(adev->handle, 390f0c62255SMario Limonciello ACPI_LPS0_DSM_UUID_MICROSOFT, 0, 3915dbf5099SPratik Vishwakarma &lps0_dsm_guid_microsoft); 3928fbd6c15SMario Limonciello if (lps0_dsm_func_mask > 0x3 && (!strcmp(hid, "AMD0004") || 3931ea1dbf1SSachi King !strcmp(hid, "AMD0005") || 3948fbd6c15SMario Limonciello !strcmp(hid, "AMDI0005"))) { 3958fbd6c15SMario Limonciello lps0_dsm_func_mask = (lps0_dsm_func_mask << 1) | 0x1; 3968fbd6c15SMario Limonciello acpi_handle_debug(adev->handle, "_DSM UUID %s: Adjusted function mask: 0x%x\n", 3978fbd6c15SMario Limonciello ACPI_LPS0_DSM_UUID_AMD, lps0_dsm_func_mask); 398f0c62255SMario Limonciello } else if (lps0_dsm_func_mask_microsoft > 0 && !strcmp(hid, "AMDI0007")) { 399f0c62255SMario Limonciello lps0_dsm_func_mask_microsoft = -EINVAL; 400f0c62255SMario Limonciello acpi_handle_debug(adev->handle, "_DSM Using AMD method\n"); 4018fbd6c15SMario Limonciello } 402fef98671SRafael J. Wysocki } else { 403fef98671SRafael J. Wysocki rev_id = 1; 4044a012dc8SPratik Vishwakarma lps0_dsm_func_mask = validate_dsm(adev->handle, 4054a012dc8SPratik Vishwakarma ACPI_LPS0_DSM_UUID, rev_id, &lps0_dsm_guid); 4065dbf5099SPratik Vishwakarma lps0_dsm_func_mask_microsoft = -EINVAL; 407fef98671SRafael J. Wysocki } 408fef98671SRafael J. Wysocki 4095dbf5099SPratik Vishwakarma if (lps0_dsm_func_mask < 0 && lps0_dsm_func_mask_microsoft < 0) 4105dbf5099SPratik Vishwakarma return 0; //function evaluation failed 411fef98671SRafael J. Wysocki 412fef98671SRafael J. Wysocki lps0_device_handle = adev->handle; 413fef98671SRafael J. Wysocki 414fef98671SRafael J. Wysocki if (acpi_s2idle_vendor_amd()) 415fef98671SRafael J. Wysocki lpi_device_get_constraints_amd(); 416fef98671SRafael J. Wysocki else 417fef98671SRafael J. Wysocki lpi_device_get_constraints(); 418fef98671SRafael J. Wysocki 419fef98671SRafael J. Wysocki /* 420fef98671SRafael J. Wysocki * Use suspend-to-idle by default if the default suspend mode was not 421fef98671SRafael J. Wysocki * set from the command line. 422fef98671SRafael J. Wysocki */ 423fef98671SRafael J. Wysocki if (mem_sleep_default > PM_SUSPEND_MEM && !acpi_sleep_default_s3) 424fef98671SRafael J. Wysocki mem_sleep_current = PM_SUSPEND_TO_IDLE; 425fef98671SRafael J. Wysocki 426fef98671SRafael J. Wysocki /* 427*d6ebb17cSMario Limonciello * Some LPS0 systems, like ASUS Zenbook UX430UNR/i7-8550U, require the 428*d6ebb17cSMario Limonciello * EC GPE to be enabled while suspended for certain wakeup devices to 429*d6ebb17cSMario Limonciello * work, so mark it as wakeup-capable. 430fef98671SRafael J. Wysocki */ 431fef98671SRafael J. Wysocki acpi_ec_mark_gpe_for_wake(); 432fef98671SRafael J. Wysocki 433fef98671SRafael J. Wysocki return 0; 434fef98671SRafael J. Wysocki } 435fef98671SRafael J. Wysocki 436fef98671SRafael J. Wysocki static struct acpi_scan_handler lps0_handler = { 437fef98671SRafael J. Wysocki .ids = lps0_device_ids, 438fef98671SRafael J. Wysocki .attach = lps0_device_attach, 439fef98671SRafael J. Wysocki }; 440fef98671SRafael J. Wysocki 441fef98671SRafael J. Wysocki int acpi_s2idle_prepare_late(void) 442fef98671SRafael J. Wysocki { 443fef98671SRafael J. Wysocki if (!lps0_device_handle || sleep_no_lps0) 444fef98671SRafael J. Wysocki return 0; 445fef98671SRafael J. Wysocki 446fef98671SRafael J. Wysocki if (pm_debug_messages_on) 447fef98671SRafael J. Wysocki lpi_check_constraints(); 448fef98671SRafael J. Wysocki 449fa209644SMario Limonciello /* Screen off */ 450fa209644SMario Limonciello if (lps0_dsm_func_mask > 0) 451fa209644SMario Limonciello acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ? 452fa209644SMario Limonciello ACPI_LPS0_SCREEN_OFF_AMD : 453fa209644SMario Limonciello ACPI_LPS0_SCREEN_OFF, 454fa209644SMario Limonciello lps0_dsm_func_mask, lps0_dsm_guid); 455fa209644SMario Limonciello 456fa209644SMario Limonciello if (lps0_dsm_func_mask_microsoft > 0) 4575dbf5099SPratik Vishwakarma acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_OFF, 4585dbf5099SPratik Vishwakarma lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft); 459fa209644SMario Limonciello 460fa209644SMario Limonciello /* LPS0 entry */ 461fa209644SMario Limonciello if (lps0_dsm_func_mask > 0) 462fa209644SMario Limonciello acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ? 463fa209644SMario Limonciello ACPI_LPS0_ENTRY_AMD : 464fa209644SMario Limonciello ACPI_LPS0_ENTRY, 465fa209644SMario Limonciello lps0_dsm_func_mask, lps0_dsm_guid); 466fa209644SMario Limonciello if (lps0_dsm_func_mask_microsoft > 0) { 467fa209644SMario Limonciello acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY, 468fa209644SMario Limonciello lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft); 469fa209644SMario Limonciello /* modern standby entry */ 4704753b46eSMario Limonciello acpi_sleep_run_lps0_dsm(ACPI_LPS0_MS_ENTRY, 4715dbf5099SPratik Vishwakarma lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft); 472fef98671SRafael J. Wysocki } 473fef98671SRafael J. Wysocki return 0; 474fef98671SRafael J. Wysocki } 475fef98671SRafael J. Wysocki 476fef98671SRafael J. Wysocki void acpi_s2idle_restore_early(void) 477fef98671SRafael J. Wysocki { 478fef98671SRafael J. Wysocki if (!lps0_device_handle || sleep_no_lps0) 479fef98671SRafael J. Wysocki return; 480fef98671SRafael J. Wysocki 481fa209644SMario Limonciello /* Modern standby exit */ 482fa209644SMario Limonciello if (lps0_dsm_func_mask_microsoft > 0) 4834753b46eSMario Limonciello acpi_sleep_run_lps0_dsm(ACPI_LPS0_MS_EXIT, 4845dbf5099SPratik Vishwakarma lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft); 485fa209644SMario Limonciello 486fa209644SMario Limonciello /* LPS0 exit */ 487fa209644SMario Limonciello if (lps0_dsm_func_mask > 0) 488fa209644SMario Limonciello acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ? 489fa209644SMario Limonciello ACPI_LPS0_EXIT_AMD : 490fa209644SMario Limonciello ACPI_LPS0_EXIT, 491fa209644SMario Limonciello lps0_dsm_func_mask, lps0_dsm_guid); 492fa209644SMario Limonciello if (lps0_dsm_func_mask_microsoft > 0) 493fa209644SMario Limonciello acpi_sleep_run_lps0_dsm(ACPI_LPS0_EXIT, 494fa209644SMario Limonciello lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft); 495fa209644SMario Limonciello 496fa209644SMario Limonciello /* Screen on */ 497fa209644SMario Limonciello if (lps0_dsm_func_mask_microsoft > 0) 4985dbf5099SPratik Vishwakarma acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_ON, 4995dbf5099SPratik Vishwakarma lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft); 500fa209644SMario Limonciello if (lps0_dsm_func_mask > 0) 501fa209644SMario Limonciello acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ? 502fa209644SMario Limonciello ACPI_LPS0_SCREEN_ON_AMD : 503fa209644SMario Limonciello ACPI_LPS0_SCREEN_ON, 5043f4b116cSPratik Vishwakarma lps0_dsm_func_mask, lps0_dsm_guid); 505fef98671SRafael J. Wysocki } 506fef98671SRafael J. Wysocki 507fef98671SRafael J. Wysocki static const struct platform_s2idle_ops acpi_s2idle_ops_lps0 = { 508fef98671SRafael J. Wysocki .begin = acpi_s2idle_begin, 509fef98671SRafael J. Wysocki .prepare = acpi_s2idle_prepare, 510fef98671SRafael J. Wysocki .prepare_late = acpi_s2idle_prepare_late, 511fef98671SRafael J. Wysocki .wake = acpi_s2idle_wake, 512fef98671SRafael J. Wysocki .restore_early = acpi_s2idle_restore_early, 513fef98671SRafael J. Wysocki .restore = acpi_s2idle_restore, 514fef98671SRafael J. Wysocki .end = acpi_s2idle_end, 515fef98671SRafael J. Wysocki }; 516fef98671SRafael J. Wysocki 517fef98671SRafael J. Wysocki void acpi_s2idle_setup(void) 518fef98671SRafael J. Wysocki { 519fef98671SRafael J. Wysocki acpi_scan_add_handler(&lps0_handler); 520fef98671SRafael J. Wysocki s2idle_set_ops(&acpi_s2idle_ops_lps0); 521fef98671SRafael J. Wysocki } 522fef98671SRafael J. Wysocki 523fef98671SRafael J. Wysocki #endif /* CONFIG_SUSPEND */ 524