summaryrefslogtreecommitdiffstats
path: root/drivers/platform/x86/amd-pmc.c
diff options
context:
space:
mode:
authorMario Limonciello <mario.limonciello@amd.com>2021-10-20 11:29:46 -0500
committerHans de Goede <hdegoede@redhat.com>2021-10-21 20:36:15 +0200
commit59348401ebed9f0e8ffe2d5b9cf1de30ecb24dde (patch)
tree16254a3001b95028e860a63530a97ee91480d165 /drivers/platform/x86/amd-pmc.c
parent4c9dbf8622797eee44e9a4f7327d3d04aa4d1d5f (diff)
downloadlinux-59348401ebed9f0e8ffe2d5b9cf1de30ecb24dde.tar.bz2
platform/x86: amd-pmc: Add special handling for timer based S0i3 wakeup
RTC based wakeup from s0i3 doesn't work properly on some Green Sardine platforms. Because of this, a newer SMU for Green Sardine has the ability to pass wakeup time as argument of the upper 16 bits of OS_HINT message. With older firmware setting the timer value in OS_HINT will cause firmware to reject the hint, so only run this path on: 1) Green Sardine 2) Minimum SMU FW 3) RTC alarm armed during s0i3 entry Using this method has some limitations that the s0i3 wakeup will need to be between 4 seconds and 18 hours, so check those boundary conditions as well and abort the suspend if RTC is armed for too short or too long of a duration. Signed-off-by: Mario Limonciello <mario.limonciello@amd.com> Link: https://lore.kernel.org/r/20211020162946.10537-2-mario.limonciello@amd.com Reviewed-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Diffstat (limited to 'drivers/platform/x86/amd-pmc.c')
-rw-r--r--drivers/platform/x86/amd-pmc.c60
1 files changed, 59 insertions, 1 deletions
diff --git a/drivers/platform/x86/amd-pmc.c b/drivers/platform/x86/amd-pmc.c
index 99ac50616bc3..678bf6874c63 100644
--- a/drivers/platform/x86/amd-pmc.c
+++ b/drivers/platform/x86/amd-pmc.c
@@ -17,9 +17,11 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/iopoll.h>
+#include <linux/limits.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
+#include <linux/rtc.h>
#include <linux/suspend.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
@@ -412,20 +414,76 @@ static int amd_pmc_get_os_hint(struct amd_pmc_dev *dev)
return -EINVAL;
}
+static int amd_pmc_verify_czn_rtc(struct amd_pmc_dev *pdev, u32 *arg)
+{
+ struct rtc_device *rtc_device;
+ time64_t then, now, duration;
+ struct rtc_wkalrm alarm;
+ struct rtc_time tm;
+ int rc;
+
+ if (pdev->major < 64 || (pdev->major == 64 && pdev->minor < 53))
+ return 0;
+
+ rtc_device = rtc_class_open(CONFIG_RTC_SYSTOHC_DEVICE);
+ if (!rtc_device)
+ return 0;
+ rc = rtc_read_alarm(rtc_device, &alarm);
+ if (rc)
+ return rc;
+ if (!alarm.enabled) {
+ dev_dbg(pdev->dev, "alarm not enabled\n");
+ return 0;
+ }
+ rc = rtc_valid_tm(&alarm.time);
+ if (rc)
+ return rc;
+ rc = rtc_read_time(rtc_device, &tm);
+ if (rc)
+ return rc;
+ then = rtc_tm_to_time64(&alarm.time);
+ now = rtc_tm_to_time64(&tm);
+ duration = then-now;
+
+ /* in the past */
+ if (then < now)
+ return 0;
+
+ /* will be stored in upper 16 bits of s0i3 hint argument,
+ * so timer wakeup from s0i3 is limited to ~18 hours or less
+ */
+ if (duration <= 4 || duration > U16_MAX)
+ return -EINVAL;
+
+ *arg |= (duration << 16);
+ rc = rtc_alarm_irq_enable(rtc_device, 0);
+ dev_info(pdev->dev, "wakeup timer programmed for %lld seconds\n", duration);
+
+ return rc;
+}
+
static int __maybe_unused amd_pmc_suspend(struct device *dev)
{
struct amd_pmc_dev *pdev = dev_get_drvdata(dev);
int rc;
u8 msg;
+ u32 arg = 1;
/* Reset and Start SMU logging - to monitor the s0i3 stats */
amd_pmc_send_cmd(pdev, 0, NULL, SMU_MSG_LOG_RESET, 0);
amd_pmc_send_cmd(pdev, 0, NULL, SMU_MSG_LOG_START, 0);
+ /* Activate CZN specific RTC functionality */
+ if (pdev->cpu_id == AMD_CPU_ID_CZN) {
+ rc = amd_pmc_verify_czn_rtc(pdev, &arg);
+ if (rc < 0)
+ return rc;
+ }
+
/* Dump the IdleMask before we send hint to SMU */
amd_pmc_idlemask_read(pdev, dev, NULL);
msg = amd_pmc_get_os_hint(pdev);
- rc = amd_pmc_send_cmd(pdev, 1, NULL, msg, 0);
+ rc = amd_pmc_send_cmd(pdev, arg, NULL, msg, 0);
if (rc)
dev_err(pdev->dev, "suspend failed\n");