// SPDX-License-Identifier: GPL-2.0 /* * Architecture-specific ACPI-based support for suspend-to-idle. * * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com> * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com> * * On platforms supporting the Low Power S0 Idle interface there is an ACPI * device object with the PNP0D80 compatible device ID (System Power Management * Controller) and a specific _DSM method under it. That method, if present, * can be used to indicate to the platform that the OS is transitioning into a * low-power state in which certain types of activity are not desirable or that * it is leaving such a state, which allows the platform to adjust its operation * mode accordingly.
*/
staticbool sleep_no_lps0 __read_mostly;
module_param(sleep_no_lps0, bool, 0644);
MODULE_PARM_DESC(sleep_no_lps0, "Do not use the special LPS0 device interface");
lpi_constraints_table = kcalloc(out_obj->package.count, sizeof(*lpi_constraints_table),
GFP_KERNEL); if (!lpi_constraints_table) goto free_acpi_buffer;
acpi_handle_debug(lps0_device_handle, "LPI: constraints list begin:\n");
for (i = 0; i < out_obj->package.count; i++) { struct lpi_constraints *constraint;
acpi_status status; union acpi_object *package = &out_obj->package.elements[i]; struct lpi_device_info info = { }; int package_count = 0, j;
if (!package) continue;
for (j = 0; j < package->package.count; j++) { union acpi_object *element =
&(package->package.elements[j]);
switch (element->type) { case ACPI_TYPE_INTEGER:
info.enabled = element->integer.value; break; case ACPI_TYPE_STRING:
info.name = element->string.pointer; break; case ACPI_TYPE_PACKAGE:
package_count = element->package.count;
info.package = element->package.elements; break;
}
}
if (!info.enabled || !info.package || !info.name) continue;
acpi_handle_debug(lps0_device_handle, "LPI: constraints list end\n");
free_acpi_buffer:
ACPI_FREE(out_obj);
}
/** * acpi_get_lps0_constraint - Get the LPS0 constraint for a device. * @adev: Device to get the constraint for. * * The LPS0 constraint is the shallowest (minimum) power state in which the * device can be so as to allow the platform as a whole to achieve additional * energy conservation by utilizing a system-wide low-power state. * * Returns: * - ACPI power state value of the constraint for @adev on success. * - Otherwise, ACPI_STATE_UNKNOWN.
*/ int acpi_get_lps0_constraint(struct acpi_device *adev)
{ struct lpi_constraints *entry;
for_each_lpi_constraint(entry) { if (adev->handle == entry->handle) return entry->min_dstate;
}
acpi_handle_debug(entry->handle, "LPI: required min power state:%s current power state:%s\n",
acpi_power_state_string(entry->min_dstate),
acpi_power_state_string(adev->power.state));
if (!adev->flags.power_manageable) {
acpi_handle_info(entry->handle, "LPI: Device not power manageable\n");
entry->handle = NULL; continue;
}
if (adev->power.state < entry->min_dstate)
acpi_handle_info(entry->handle, "LPI: Constraint not met; min power state:%s current power state:%s\n",
acpi_power_state_string(entry->min_dstate),
acpi_power_state_string(adev->power.state));
}
}
staticconstchar *acpi_sleep_dsm_state_to_str(unsignedint state)
{ if (lps0_dsm_func_mask_microsoft || !acpi_s2idle_vendor_amd()) { switch (state) { case ACPI_LPS0_SCREEN_OFF: return"screen off"; case ACPI_LPS0_SCREEN_ON: return"screen on"; case ACPI_LPS0_ENTRY: return"lps0 entry"; case ACPI_LPS0_EXIT: return"lps0 exit"; case ACPI_LPS0_MS_ENTRY: return"lps0 ms entry"; case ACPI_LPS0_MS_EXIT: return"lps0 ms exit";
}
} else { switch (state) { case ACPI_LPS0_SCREEN_ON_AMD: return"screen on"; case ACPI_LPS0_SCREEN_OFF_AMD: return"screen off"; case ACPI_LPS0_ENTRY_AMD: return"lps0 entry"; case ACPI_LPS0_EXIT_AMD: return"lps0 exit";
}
}
/* * Log a message if the _DSM function sets for two * different UUIDs overlap.
*/
func_mask = lps0_dsm_func_mask & lps0_dsm_func_mask_microsoft; if (func_mask)
acpi_handle_info(adev->handle, "Duplicate LPS0 _DSM functions (mask: 0x%x)\n",
func_mask);
}
}
if (acpi_s2idle_vendor_amd())
lpi_device_get_constraints_amd(); else
lpi_device_get_constraints();
/* * Use suspend-to-idle by default if ACPI_FADT_LOW_POWER_S0 is set in * the FADT and the default suspend mode was not set from the command * line.
*/ if ((acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0) &&
mem_sleep_default > PM_SUSPEND_MEM && !acpi_sleep_default_s3) {
mem_sleep_current = PM_SUSPEND_TO_IDLE;
pr_info("Low-power S0 idle used by default for system suspend\n");
}
/* * Some LPS0 systems, like ASUS Zenbook UX430UNR/i7-8550U, require the * EC GPE to be enabled while suspended for certain wakeup devices to * work, so mark it as wakeup-capable.
*/
acpi_ec_mark_gpe_for_wake();