Most LoongArch 64 machines are using custom "SADR" ACPI extension
to perform ACPI S3 sleep. However the standard ACPI way to perform
sleep is to write a value to ACPI PM1/SLEEP_CTL register, and this
is never supported properly in kernel.
Fix standard S3 sleep by providing a fallback DoSuspend function
which calls ACPI's acpi_enter_sleep_state routine when SADR is
not provided by the firmware.
Also fix suspend assembly code so that ra is set properly before
go into sleep routine. (Previously linked address of jirl was set
to a0, some firmware do require return address in a0 but it's
already set with la.pcrel before).
Cc: stable@vger.kernel.org
Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
---
arch/loongarch/power/platform.c | 24 ++++++++++++++++++------
arch/loongarch/power/suspend_asm.S | 2 +-
2 files changed, 19 insertions(+), 7 deletions(-)
diff --git a/arch/loongarch/power/platform.c b/arch/loongarch/power/platform.c
index 3ea8e07aa225..2aea41f8e3ff 100644
--- a/arch/loongarch/power/platform.c
+++ b/arch/loongarch/power/platform.c
@@ -34,22 +34,34 @@ void enable_pci_wakeup(void)
acpi_write_bit_register(ACPI_BITREG_PCIEXP_WAKE_DISABLE, 0);
}
+static void acpi_suspend_register_fallback(void)
+{
+ acpi_enter_sleep_state(ACPI_STATE_S3);
+}
+
static int __init loongson3_acpi_suspend_init(void)
{
#ifdef CONFIG_ACPI
acpi_status status;
uint64_t suspend_addr = 0;
- if (acpi_disabled || acpi_gbl_reduced_hardware)
+ if (acpi_disabled)
return 0;
- acpi_write_bit_register(ACPI_BITREG_SCI_ENABLE, 1);
+ if (!acpi_sleep_state_supported(ACPI_STATE_S3))
+ return 0;
+
+ if (!acpi_gbl_reduced_hardware)
+ acpi_write_bit_register(ACPI_BITREG_SCI_ENABLE, 1);
+
status = acpi_evaluate_integer(NULL, "\\SADR", NULL, &suspend_addr);
- if (ACPI_FAILURE(status) || !suspend_addr) {
- pr_err("ACPI S3 is not support!\n");
- return -1;
+ if (!ACPI_FAILURE(status) && suspend_addr) {
+ loongson_sysconf.suspend_addr = (u64)phys_to_virt(PHYSADDR(suspend_addr));
+ return 0;
}
- loongson_sysconf.suspend_addr = (u64)phys_to_virt(PHYSADDR(suspend_addr));
+
+ pr_info("ACPI S3 supported with hw register fallback\n");
+ loongson_sysconf.suspend_addr = (u64)acpi_suspend_register_fallback;
#endif
return 0;
}
diff --git a/arch/loongarch/power/suspend_asm.S b/arch/loongarch/power/suspend_asm.S
index 6fdd74eb219b..fe08dbb73c87 100644
--- a/arch/loongarch/power/suspend_asm.S
+++ b/arch/loongarch/power/suspend_asm.S
@@ -66,7 +66,7 @@ SYM_FUNC_START(loongarch_suspend_enter)
la.pcrel a0, loongarch_wakeup_start
la.pcrel t0, loongarch_suspend_addr
ld.d t0, t0, 0
- jirl a0, t0, 0 /* Call BIOS's STR sleep routine */
+ jirl ra, t0, 0 /* Call BIOS's STR sleep routine */
/*
* This is where we return upon wakeup.
--
2.43.0
Hi, Jiaxun,
On Fri, Jun 14, 2024 at 12:41 AM Jiaxun Yang <jiaxun.yang@flygoat.com> wrote:
>
> Most LoongArch 64 machines are using custom "SADR" ACPI extension
> to perform ACPI S3 sleep. However the standard ACPI way to perform
> sleep is to write a value to ACPI PM1/SLEEP_CTL register, and this
> is never supported properly in kernel.
Maybe our hardware is insane so we need "SADR", if so, this patch may
break real hardware. What's your opinion, Jianmin?
Huacai
>
> Fix standard S3 sleep by providing a fallback DoSuspend function
> which calls ACPI's acpi_enter_sleep_state routine when SADR is
> not provided by the firmware.
>
> Also fix suspend assembly code so that ra is set properly before
> go into sleep routine. (Previously linked address of jirl was set
> to a0, some firmware do require return address in a0 but it's
> already set with la.pcrel before).
>
> Cc: stable@vger.kernel.org
> Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
> ---
> arch/loongarch/power/platform.c | 24 ++++++++++++++++++------
> arch/loongarch/power/suspend_asm.S | 2 +-
> 2 files changed, 19 insertions(+), 7 deletions(-)
>
> diff --git a/arch/loongarch/power/platform.c b/arch/loongarch/power/platform.c
> index 3ea8e07aa225..2aea41f8e3ff 100644
> --- a/arch/loongarch/power/platform.c
> +++ b/arch/loongarch/power/platform.c
> @@ -34,22 +34,34 @@ void enable_pci_wakeup(void)
> acpi_write_bit_register(ACPI_BITREG_PCIEXP_WAKE_DISABLE, 0);
> }
>
> +static void acpi_suspend_register_fallback(void)
> +{
> + acpi_enter_sleep_state(ACPI_STATE_S3);
> +}
> +
> static int __init loongson3_acpi_suspend_init(void)
> {
> #ifdef CONFIG_ACPI
> acpi_status status;
> uint64_t suspend_addr = 0;
>
> - if (acpi_disabled || acpi_gbl_reduced_hardware)
> + if (acpi_disabled)
> return 0;
>
> - acpi_write_bit_register(ACPI_BITREG_SCI_ENABLE, 1);
> + if (!acpi_sleep_state_supported(ACPI_STATE_S3))
> + return 0;
> +
> + if (!acpi_gbl_reduced_hardware)
> + acpi_write_bit_register(ACPI_BITREG_SCI_ENABLE, 1);
> +
> status = acpi_evaluate_integer(NULL, "\\SADR", NULL, &suspend_addr);
> - if (ACPI_FAILURE(status) || !suspend_addr) {
> - pr_err("ACPI S3 is not support!\n");
> - return -1;
> + if (!ACPI_FAILURE(status) && suspend_addr) {
> + loongson_sysconf.suspend_addr = (u64)phys_to_virt(PHYSADDR(suspend_addr));
> + return 0;
> }
> - loongson_sysconf.suspend_addr = (u64)phys_to_virt(PHYSADDR(suspend_addr));
> +
> + pr_info("ACPI S3 supported with hw register fallback\n");
> + loongson_sysconf.suspend_addr = (u64)acpi_suspend_register_fallback;
> #endif
> return 0;
> }
> diff --git a/arch/loongarch/power/suspend_asm.S b/arch/loongarch/power/suspend_asm.S
> index 6fdd74eb219b..fe08dbb73c87 100644
> --- a/arch/loongarch/power/suspend_asm.S
> +++ b/arch/loongarch/power/suspend_asm.S
> @@ -66,7 +66,7 @@ SYM_FUNC_START(loongarch_suspend_enter)
> la.pcrel a0, loongarch_wakeup_start
> la.pcrel t0, loongarch_suspend_addr
> ld.d t0, t0, 0
> - jirl a0, t0, 0 /* Call BIOS's STR sleep routine */
> + jirl ra, t0, 0 /* Call BIOS's STR sleep routine */
>
> /*
> * This is where we return upon wakeup.
>
> --
> 2.43.0
>
>
© 2016 - 2026 Red Hat, Inc.