Skip to content

Commit 726fb6b

Browse files
spandruvadarafaeljw
authored andcommitted
ACPI / PM: Check low power idle constraints for debug only
For SoC to achieve its lowest power platform idle state a set of hardware preconditions must be met. These preconditions or constraints can be obtained by issuing a device specific method (_DSM) with function "1". Refer to the document provided in the link below. Here during initialization (from attach() callback of LPS0 device), invoke function 1 to get the device constraints. Each enabled constraint is stored in a table. The devices in this table are used to check whether they were in required minimum state, while entering suspend. This check is done from platform freeze wake() callback, only when /sys/power/pm_debug_messages attribute is non zero. If any constraint is not met and device is ACPI power managed then it prints the device information to kernel logs. Also if debug is enabled in acpi/sleep.c, the constraint table and state of each device on wake is dumped in kernel logs. Since pm_debug_messages_on setting is used as condition to check constraints outside kernel/power/main.c, pm_debug_messages_on is changed to a global variable. Link: http://www.uefi.org/sites/default/files/resources/Intel_ACPI_Low_Power_S0_Idle.pdf Signed-off-by: Srinivas Pandruvada <[email protected]> Signed-off-by: Rafael J. Wysocki <[email protected]>
1 parent 23d5855 commit 726fb6b

File tree

3 files changed

+171
-1
lines changed

3 files changed

+171
-1
lines changed

drivers/acpi/sleep.c

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,7 @@ static const struct acpi_device_id lps0_device_ids[] = {
669669

670670
#define ACPI_LPS0_DSM_UUID "c4eb40a0-6cd2-11e2-bcfd-0800200c9a66"
671671

672+
#define ACPI_LPS0_GET_DEVICE_CONSTRAINTS 1
672673
#define ACPI_LPS0_SCREEN_OFF 3
673674
#define ACPI_LPS0_SCREEN_ON 4
674675
#define ACPI_LPS0_ENTRY 5
@@ -680,6 +681,166 @@ static acpi_handle lps0_device_handle;
680681
static guid_t lps0_dsm_guid;
681682
static char lps0_dsm_func_mask;
682683

684+
/* Device constraint entry structure */
685+
struct lpi_device_info {
686+
char *name;
687+
int enabled;
688+
union acpi_object *package;
689+
};
690+
691+
/* Constraint package structure */
692+
struct lpi_device_constraint {
693+
int uid;
694+
int min_dstate;
695+
int function_states;
696+
};
697+
698+
struct lpi_constraints {
699+
acpi_handle handle;
700+
int min_dstate;
701+
};
702+
703+
static struct lpi_constraints *lpi_constraints_table;
704+
static int lpi_constraints_table_size;
705+
706+
static void lpi_device_get_constraints(void)
707+
{
708+
union acpi_object *out_obj;
709+
int i;
710+
711+
out_obj = acpi_evaluate_dsm_typed(lps0_device_handle, &lps0_dsm_guid,
712+
1, ACPI_LPS0_GET_DEVICE_CONSTRAINTS,
713+
NULL, ACPI_TYPE_PACKAGE);
714+
715+
acpi_handle_debug(lps0_device_handle, "_DSM function 1 eval %s\n",
716+
out_obj ? "successful" : "failed");
717+
718+
if (!out_obj)
719+
return;
720+
721+
lpi_constraints_table = kcalloc(out_obj->package.count,
722+
sizeof(*lpi_constraints_table),
723+
GFP_KERNEL);
724+
if (!lpi_constraints_table)
725+
goto free_acpi_buffer;
726+
727+
acpi_handle_debug(lps0_device_handle, "LPI: constraints list begin:\n");
728+
729+
for (i = 0; i < out_obj->package.count; i++) {
730+
struct lpi_constraints *constraint;
731+
acpi_status status;
732+
union acpi_object *package = &out_obj->package.elements[i];
733+
struct lpi_device_info info = { };
734+
int package_count = 0, j;
735+
736+
if (!package)
737+
continue;
738+
739+
for (j = 0; j < package->package.count; ++j) {
740+
union acpi_object *element =
741+
&(package->package.elements[j]);
742+
743+
switch (element->type) {
744+
case ACPI_TYPE_INTEGER:
745+
info.enabled = element->integer.value;
746+
break;
747+
case ACPI_TYPE_STRING:
748+
info.name = element->string.pointer;
749+
break;
750+
case ACPI_TYPE_PACKAGE:
751+
package_count = element->package.count;
752+
info.package = element->package.elements;
753+
break;
754+
}
755+
}
756+
757+
if (!info.enabled || !info.package || !info.name)
758+
continue;
759+
760+
constraint = &lpi_constraints_table[lpi_constraints_table_size];
761+
762+
status = acpi_get_handle(NULL, info.name, &constraint->handle);
763+
if (ACPI_FAILURE(status))
764+
continue;
765+
766+
acpi_handle_debug(lps0_device_handle,
767+
"index:%d Name:%s\n", i, info.name);
768+
769+
constraint->min_dstate = -1;
770+
771+
for (j = 0; j < package_count; ++j) {
772+
union acpi_object *info_obj = &info.package[j];
773+
union acpi_object *cnstr_pkg;
774+
union acpi_object *obj;
775+
struct lpi_device_constraint dev_info;
776+
777+
switch (info_obj->type) {
778+
case ACPI_TYPE_INTEGER:
779+
/* version */
780+
break;
781+
case ACPI_TYPE_PACKAGE:
782+
if (info_obj->package.count < 2)
783+
break;
784+
785+
cnstr_pkg = info_obj->package.elements;
786+
obj = &cnstr_pkg[0];
787+
dev_info.uid = obj->integer.value;
788+
obj = &cnstr_pkg[1];
789+
dev_info.min_dstate = obj->integer.value;
790+
791+
acpi_handle_debug(lps0_device_handle,
792+
"uid:%d min_dstate:%s\n",
793+
dev_info.uid,
794+
acpi_power_state_string(dev_info.min_dstate));
795+
796+
constraint->min_dstate = dev_info.min_dstate;
797+
break;
798+
}
799+
}
800+
801+
if (constraint->min_dstate < 0) {
802+
acpi_handle_debug(lps0_device_handle,
803+
"Incomplete constraint defined\n");
804+
continue;
805+
}
806+
807+
lpi_constraints_table_size++;
808+
}
809+
810+
acpi_handle_debug(lps0_device_handle, "LPI: constraints list end\n");
811+
812+
free_acpi_buffer:
813+
ACPI_FREE(out_obj);
814+
}
815+
816+
static void lpi_check_constraints(void)
817+
{
818+
int i;
819+
820+
for (i = 0; i < lpi_constraints_table_size; ++i) {
821+
struct acpi_device *adev;
822+
823+
if (acpi_bus_get_device(lpi_constraints_table[i].handle, &adev))
824+
continue;
825+
826+
acpi_handle_debug(adev->handle,
827+
"LPI: required min power state:%s current power state:%s\n",
828+
acpi_power_state_string(lpi_constraints_table[i].min_dstate),
829+
acpi_power_state_string(adev->power.state));
830+
831+
if (!adev->flags.power_manageable) {
832+
acpi_handle_info(adev->handle, "LPI: Device not power manageble\n");
833+
continue;
834+
}
835+
836+
if (adev->power.state < lpi_constraints_table[i].min_dstate)
837+
acpi_handle_info(adev->handle,
838+
"LPI: Constraint not met; min power state:%s current power state:%s\n",
839+
acpi_power_state_string(lpi_constraints_table[i].min_dstate),
840+
acpi_power_state_string(adev->power.state));
841+
}
842+
}
843+
683844
static void acpi_sleep_run_lps0_dsm(unsigned int func)
684845
{
685846
union acpi_object *out_obj;
@@ -729,6 +890,9 @@ static int lps0_device_attach(struct acpi_device *adev,
729890
"_DSM function 0 evaluation failed\n");
730891
}
731892
ACPI_FREE(out_obj);
893+
894+
lpi_device_get_constraints();
895+
732896
return 0;
733897
}
734898

@@ -766,6 +930,10 @@ static int acpi_s2idle_prepare(void)
766930

767931
static void acpi_s2idle_wake(void)
768932
{
933+
934+
if (pm_debug_messages_on)
935+
lpi_check_constraints();
936+
769937
/*
770938
* If IRQD_WAKEUP_ARMED is not set for the SCI at this point, it means
771939
* that the SCI has triggered while suspended, so cancel the wakeup in

include/linux/suspend.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,9 +495,11 @@ static inline void unlock_system_sleep(void) {}
495495

496496
#ifdef CONFIG_PM_SLEEP_DEBUG
497497
extern bool pm_print_times_enabled;
498+
extern bool pm_debug_messages_on;
498499
extern __printf(2, 3) void __pm_pr_dbg(bool defer, const char *fmt, ...);
499500
#else
500501
#define pm_print_times_enabled (false)
502+
#define pm_debug_messages_on (false)
501503

502504
#include <linux/printk.h>
503505

kernel/power/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ static ssize_t pm_wakeup_irq_show(struct kobject *kobj,
361361

362362
power_attr_ro(pm_wakeup_irq);
363363

364-
static bool pm_debug_messages_on __read_mostly;
364+
bool pm_debug_messages_on __read_mostly;
365365

366366
static ssize_t pm_debug_messages_show(struct kobject *kobj,
367367
struct kobj_attribute *attr, char *buf)

0 commit comments

Comments
 (0)