Commit 5bdd670e authored by Anisetti Avinash Krishna's avatar Anisetti Avinash Krishna Committed by Chris Friedt
Browse files

lib: acpi: Enable poweroff feature



Enable system poweroff feature using ACPI.

Signed-off-by: default avatarAnisetti Avinash Krishna <anisetti.avinash.krishna@intel.com>
parent 5905fa86
Loading
Loading
Loading
Loading
+16 −1
Original line number Diff line number Diff line
@@ -299,4 +299,19 @@ ACPI_MADT_LOCAL_APIC *acpi_local_apic_get(int cpu_num);
 */
int acpi_invoke_method(char *path, ACPI_OBJECT_LIST *arg_list, ACPI_OBJECT *ret_obj);

#endif
#if defined(CONFIG_ACPI_POWEROFF) || defined(__DOXYGEN__)
/**
 * @brief system level poweroff by setting it to soft off.
 * Requires @kconfig{CONFIG_ACPI_POWEROFF} to be enabled.
 *
 * @return -EINVAL if the pm1_cnt address is not available.
 */
int acpi_poweroff(void);
#else
static inline int acpi_poweroff(void)
{
	return -ENOTSUP;
}
#endif /* CONFIG_ACPI_POWEROFF */

#endif /* ZEPHYR_INCLUDE_DRIVERS_ACPI_H_ */
+5 −0
Original line number Diff line number Diff line
@@ -34,6 +34,11 @@ config ACPI_SHELL
	help
	  Enable commands for debugging ACPI using the built-in shell.

config ACPI_POWEROFF
	bool "Power off functionality by setting system to S5 power state"
	help
	  Enable support for system power off through ACPI.

config ACPI_DEV_MAX
	int "maximum child devices"
	default 1000
+48 −0
Original line number Diff line number Diff line
@@ -14,6 +14,23 @@
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(ACPI, CONFIG_ACPI_LOG_LEVEL);

#if defined(CONFIG_ACPI_POWEROFF)

/* PM1_CNT register */
#if (defined(CONFIG_BOARD_QEMU_X86_64) || defined(CONFIG_BOARD_QEMU_X86))
#define PM1_CNT_SLP_TYP_S5 0x00 /* S5 SLP_TYP is 0 in QEMU */
#elif defined(CONFIG_ACRN_COMMON)
#define PM1_CNT_SLP_TYP_S5 0x05 /* S5 SLP_TYP is 5 in ACRN */
#else
#define PM1_CNT_SLP_TYP_S5 0x07 /* S5 SLP_TYP is 7 in other platforms*/
#endif

#define PM1_CNT_SLP_TYP_SHFT 0x0A /* SLP_TYP bits(10-12) in PM1_CNT */

#define PM1_CNT_SLP_EN BIT(13) /* Sets SLP_EN bit13 */

#endif /* CONFIG_ACPI_POWEROFF */

static struct {
	struct acpi_dev child_dev[CONFIG_ACPI_DEV_MAX];
	int num_dev;
@@ -974,3 +991,34 @@ static int acpi_init(void)
exit:
	return status;
}

#if defined(CONFIG_ACPI_POWEROFF)

int acpi_poweroff(void)
{
	ACPI_STATUS status;
	uintptr_t pm1_cnt_addr;
	uint32_t pm1_cnt;

	if (!acpi.early_init) {
		status = acpi_early_init();
		if (status) {
			LOG_ERR("ACPI early init failed");
			return -ENODEV;
		}
	}

	if (!AcpiGbl_FADT.Pm1aControlBlock) {
		return -EINVAL;
	}

	pm1_cnt_addr = AcpiGbl_FADT.Pm1aControlBlock;

	pm1_cnt = sys_in16(pm1_cnt_addr);
	pm1_cnt |= ((PM1_CNT_SLP_TYP_S5 << PM1_CNT_SLP_TYP_SHFT) | PM1_CNT_SLP_EN);
	sys_out16(pm1_cnt, pm1_cnt_addr);

	return 0;
}

#endif /* CONFIG_ACPI_POWEROFF */
+13 −0
Original line number Diff line number Diff line
@@ -346,6 +346,16 @@ static int read_table(const struct shell *sh, size_t argc, char **argv)
	return 0;
}

#if defined(CONFIG_ACPI_POWEROFF)

static void cmd_acpi_poweroff(const struct shell *sh)
{
	if (acpi_poweroff()) {
		shell_print(sh, "ACPI poweroff failed due to invalid PM1_CNT address");
	}
}
#endif

SHELL_STATIC_SUBCMD_SET_CREATE(
	sub_acpi,
	SHELL_CMD(crs, NULL,
@@ -366,6 +376,9 @@ SHELL_STATIC_SUBCMD_SET_CREATE(
		  get_acpi_dev_resource),
	SHELL_CMD(rd_table, NULL, "read ACPI table (eg: acpi read_table APIC)",
		  read_table),
#if defined(CONFIG_ACPI_POWEROFF)
	SHELL_CMD(poweroff, NULL, "poweroff the platform", cmd_acpi_poweroff),
#endif
	SHELL_SUBCMD_SET_END /* Array terminated. */
);