A number of Cezanne systems report IRQ1 as a wakeup source when it's not actually a wakeup. This can cause problems for certain ACPI events. The following fix went upstream that fixed it:
commit 8e60615e8932 ("platform/x86/amd: pmc: Disable IRQ1 wakeup for RN/CZN")
It was reported that this fix actually helped here with older kernels too: Link: https://gitlab.freedesktop.org/drm/amd/-/issues/1921#note_1770257
So backport this fix to 5.15.y as well. This backport is dependent upon being able to read the SMU version which happened in a different commit. So backport that commit and follow up fixes as well.
v1->v2: * Split into multiple commits * Catch some fixes for reading SMU version too
Hans de Goede (1): platform/x86: amd-pmc: Fix compilation when CONFIG_DEBUGFS is disabled
Mario Limonciello (2): platform/x86: amd-pmc: Correct usage of SMU version platform/x86/amd: pmc: Disable IRQ1 wakeup for RN/CZN
Sanket Goswami (1): platform/x86: amd-pmc: Export Idlemask values based on the APU
drivers/platform/x86/amd-pmc.c | 116 +++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+)
From: Sanket Goswami Sanket.Goswami@amd.com
IdleMask is the metric used by the PM firmware to know the status of each of the Hardware IP blocks monitored by the PM firmware.
Knowing this value is key to get the information of s2idle suspend/resume status. This value is mapped to PMC scratch registers, retrieve them accordingly based on the CPU family and the underlying firmware support.
Co-developed-by: Shyam Sundar S K Shyam-sundar.S-k@amd.com Signed-off-by: Shyam Sundar S K Shyam-sundar.S-k@amd.com Signed-off-by: Sanket Goswami Sanket.Goswami@amd.com Reviewed-by: Mario Limonciello mario.limonciello@amd.com Link: https://lore.kernel.org/r/20210916124002.2529-1-Sanket.Goswami@amd.com Reviewed-by: Hans de Goede hdegoede@redhat.com Signed-off-by: Hans de Goede hdegoede@redhat.com (cherry picked from commit f6045de1f53268131ea75a99b210b869dcc150b2) Although only part of the commit is required, full commit is backported to allow for clearer backports in the future if needed. Signed-off-by: Mario Limonciello mario.limonciello@amd.com --- drivers/platform/x86/amd-pmc.c | 76 ++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+)
diff --git a/drivers/platform/x86/amd-pmc.c b/drivers/platform/x86/amd-pmc.c index d3efd514614c..98283d8bef94 100644 --- a/drivers/platform/x86/amd-pmc.c +++ b/drivers/platform/x86/amd-pmc.c @@ -29,6 +29,10 @@ #define AMD_PMC_REGISTER_RESPONSE 0x980 #define AMD_PMC_REGISTER_ARGUMENT 0x9BC
+/* PMC Scratch Registers */ +#define AMD_PMC_SCRATCH_REG_CZN 0x94 +#define AMD_PMC_SCRATCH_REG_YC 0xD14 + /* Base address of SMU for mapping physical address to virtual address */ #define AMD_PMC_SMU_INDEX_ADDRESS 0xB8 #define AMD_PMC_SMU_INDEX_DATA 0xBC @@ -110,6 +114,10 @@ struct amd_pmc_dev { u32 base_addr; u32 cpu_id; u32 active_ips; +/* SMU version information */ + u16 major; + u16 minor; + u16 rev; struct device *dev; struct mutex lock; /* generic mutex lock */ #if IS_ENABLED(CONFIG_DEBUG_FS) @@ -201,6 +209,66 @@ static int s0ix_stats_show(struct seq_file *s, void *unused) } DEFINE_SHOW_ATTRIBUTE(s0ix_stats);
+static int amd_pmc_get_smu_version(struct amd_pmc_dev *dev) +{ + int rc; + u32 val; + + rc = amd_pmc_send_cmd(dev, 0, &val, SMU_MSG_GETSMUVERSION, 1); + if (rc) + return rc; + + dev->major = (val >> 16) & GENMASK(15, 0); + dev->minor = (val >> 8) & GENMASK(7, 0); + dev->rev = (val >> 0) & GENMASK(7, 0); + + dev_dbg(dev->dev, "SMU version is %u.%u.%u\n", dev->major, dev->minor, dev->rev); + + return 0; +} + +static int amd_pmc_idlemask_read(struct amd_pmc_dev *pdev, struct device *dev, + struct seq_file *s) +{ + u32 val; + + switch (pdev->cpu_id) { + case AMD_CPU_ID_CZN: + val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_CZN); + break; + case AMD_CPU_ID_YC: + val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_YC); + break; + default: + return -EINVAL; + } + + if (dev) + dev_dbg(pdev->dev, "SMU idlemask s0i3: 0x%x\n", val); + + if (s) + seq_printf(s, "SMU idlemask : 0x%x\n", val); + + return 0; +} + +static int amd_pmc_idlemask_show(struct seq_file *s, void *unused) +{ + struct amd_pmc_dev *dev = s->private; + int rc; + + if (dev->major > 56 || (dev->major >= 55 && dev->minor >= 37)) { + rc = amd_pmc_idlemask_read(dev, NULL, s); + if (rc) + return rc; + } else { + seq_puts(s, "Unsupported SMU version for Idlemask\n"); + } + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(amd_pmc_idlemask); + static void amd_pmc_dbgfs_unregister(struct amd_pmc_dev *dev) { debugfs_remove_recursive(dev->dbgfs_dir); @@ -213,6 +281,8 @@ static void amd_pmc_dbgfs_register(struct amd_pmc_dev *dev) &smu_fw_info_fops); debugfs_create_file("s0ix_stats", 0644, dev->dbgfs_dir, dev, &s0ix_stats_fops); + debugfs_create_file("amd_pmc_idlemask", 0644, dev->dbgfs_dir, dev, + &amd_pmc_idlemask_fops); } #else static inline void amd_pmc_dbgfs_register(struct amd_pmc_dev *dev) @@ -349,6 +419,8 @@ static int __maybe_unused amd_pmc_suspend(struct device *dev) amd_pmc_send_cmd(pdev, 0, NULL, SMU_MSG_LOG_RESET, 0); amd_pmc_send_cmd(pdev, 0, NULL, SMU_MSG_LOG_START, 0);
+ /* 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); if (rc) @@ -371,6 +443,9 @@ static int __maybe_unused amd_pmc_resume(struct device *dev) if (rc) dev_err(pdev->dev, "resume failed\n");
+ /* Dump the IdleMask to see the blockers */ + amd_pmc_idlemask_read(pdev, dev, NULL); + return 0; }
@@ -458,6 +533,7 @@ static int amd_pmc_probe(struct platform_device *pdev) if (err) dev_err(dev->dev, "SMU debugging info not supported on this platform\n");
+ amd_pmc_get_smu_version(dev); platform_set_drvdata(pdev, dev); amd_pmc_dbgfs_register(dev); return 0;
From: Hans de Goede hdegoede@redhat.com
The amd_pmc_get_smu_version() and amd_pmc_idlemask_read() functions are used in the probe / suspend/resume code, so they are also used when CONFIG_DEBUGFS is disabled, move them outside of the #ifdef CONFIG_DEBUGFS block.
Note this purely moves the code to above the #ifdef CONFIG_DEBUGFS, the code is completely unchanged.
Fixes: f6045de1f532 ("platform/x86: amd-pmc: Export Idlemask values based on the APU") Cc: Shyam Sundar S K Shyam-sundar.S-k@amd.com Cc: Sanket Goswami Sanket.Goswami@amd.com Reported-by: Nathan Chancellor nathan@kernel.org Signed-off-by: Hans de Goede hdegoede@redhat.com (cherry picked from commit 40635cd32f0d83573a558dc30e9ba3469e769249) Signed-off-by: Mario Limonciello mario.limonciello@amd.com --- drivers/platform/x86/amd-pmc.c | 86 +++++++++++++++++----------------- 1 file changed, 43 insertions(+), 43 deletions(-)
diff --git a/drivers/platform/x86/amd-pmc.c b/drivers/platform/x86/amd-pmc.c index 98283d8bef94..67bc3079cdce 100644 --- a/drivers/platform/x86/amd-pmc.c +++ b/drivers/platform/x86/amd-pmc.c @@ -155,6 +155,49 @@ struct smu_metrics { u64 timecondition_notmet_totaltime[SOC_SUBSYSTEM_IP_MAX]; } __packed;
+static int amd_pmc_get_smu_version(struct amd_pmc_dev *dev) +{ + int rc; + u32 val; + + rc = amd_pmc_send_cmd(dev, 0, &val, SMU_MSG_GETSMUVERSION, 1); + if (rc) + return rc; + + dev->major = (val >> 16) & GENMASK(15, 0); + dev->minor = (val >> 8) & GENMASK(7, 0); + dev->rev = (val >> 0) & GENMASK(7, 0); + + dev_dbg(dev->dev, "SMU version is %u.%u.%u\n", dev->major, dev->minor, dev->rev); + + return 0; +} + +static int amd_pmc_idlemask_read(struct amd_pmc_dev *pdev, struct device *dev, + struct seq_file *s) +{ + u32 val; + + switch (pdev->cpu_id) { + case AMD_CPU_ID_CZN: + val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_CZN); + break; + case AMD_CPU_ID_YC: + val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_YC); + break; + default: + return -EINVAL; + } + + if (dev) + dev_dbg(pdev->dev, "SMU idlemask s0i3: 0x%x\n", val); + + if (s) + seq_printf(s, "SMU idlemask : 0x%x\n", val); + + return 0; +} + #ifdef CONFIG_DEBUG_FS static int smu_fw_info_show(struct seq_file *s, void *unused) { @@ -209,49 +252,6 @@ static int s0ix_stats_show(struct seq_file *s, void *unused) } DEFINE_SHOW_ATTRIBUTE(s0ix_stats);
-static int amd_pmc_get_smu_version(struct amd_pmc_dev *dev) -{ - int rc; - u32 val; - - rc = amd_pmc_send_cmd(dev, 0, &val, SMU_MSG_GETSMUVERSION, 1); - if (rc) - return rc; - - dev->major = (val >> 16) & GENMASK(15, 0); - dev->minor = (val >> 8) & GENMASK(7, 0); - dev->rev = (val >> 0) & GENMASK(7, 0); - - dev_dbg(dev->dev, "SMU version is %u.%u.%u\n", dev->major, dev->minor, dev->rev); - - return 0; -} - -static int amd_pmc_idlemask_read(struct amd_pmc_dev *pdev, struct device *dev, - struct seq_file *s) -{ - u32 val; - - switch (pdev->cpu_id) { - case AMD_CPU_ID_CZN: - val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_CZN); - break; - case AMD_CPU_ID_YC: - val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_YC); - break; - default: - return -EINVAL; - } - - if (dev) - dev_dbg(pdev->dev, "SMU idlemask s0i3: 0x%x\n", val); - - if (s) - seq_printf(s, "SMU idlemask : 0x%x\n", val); - - return 0; -} - static int amd_pmc_idlemask_show(struct seq_file *s, void *unused) { struct amd_pmc_dev *dev = s->private;
Yellow carp has been outputting versions like `1093.24.0`, but this is supposed to be 69.24.0. That is the MSB is being interpreted incorrectly.
The MSB is not part of the major version, but has generally been treated that way thus far. It's actually the program, and used to distinguish between two programs from a similar family but different codebase.
Link: https://patchwork.freedesktop.org/patch/469993/ Signed-off-by: Mario Limonciello mario.limonciello@amd.com Link: https://lore.kernel.org/r/20220120174439.12770-1-mario.limonciello@amd.com Reviewed-by: Hans de Goede hdegoede@redhat.com Signed-off-by: Hans de Goede hdegoede@redhat.com (cherry picked from commit b8fb0d9b47660ddb8a8256412784aad7cee9f21a) Signed-off-by: Mario Limonciello mario.limonciello@amd.com --- drivers/platform/x86/amd-pmc.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/drivers/platform/x86/amd-pmc.c b/drivers/platform/x86/amd-pmc.c index 67bc3079cdce..e6158d0f2ac3 100644 --- a/drivers/platform/x86/amd-pmc.c +++ b/drivers/platform/x86/amd-pmc.c @@ -115,9 +115,10 @@ struct amd_pmc_dev { u32 cpu_id; u32 active_ips; /* SMU version information */ - u16 major; - u16 minor; - u16 rev; + u8 smu_program; + u8 major; + u8 minor; + u8 rev; struct device *dev; struct mutex lock; /* generic mutex lock */ #if IS_ENABLED(CONFIG_DEBUG_FS) @@ -164,11 +165,13 @@ static int amd_pmc_get_smu_version(struct amd_pmc_dev *dev) if (rc) return rc;
- dev->major = (val >> 16) & GENMASK(15, 0); + dev->smu_program = (val >> 24) & GENMASK(7, 0); + dev->major = (val >> 16) & GENMASK(7, 0); dev->minor = (val >> 8) & GENMASK(7, 0); dev->rev = (val >> 0) & GENMASK(7, 0);
- dev_dbg(dev->dev, "SMU version is %u.%u.%u\n", dev->major, dev->minor, dev->rev); + dev_dbg(dev->dev, "SMU program %u version is %u.%u.%u\n", + dev->smu_program, dev->major, dev->minor, dev->rev);
return 0; }
By default when the system is configured for low power idle in the FADT the keyboard is set up as a wake source. This matches the behavior that Windows uses for Modern Standby as well.
It has been reported that a variety of AMD based designs there are spurious wakeups are happening where two IRQ sources are active.
For example: ``` PM: Triggering wakeup from IRQ 9 PM: Triggering wakeup from IRQ 1 ```
In these designs IRQ 9 is the ACPI SCI and IRQ 1 is the keyboard. One way to trigger this problem is to suspend the laptop and then unplug the AC adapter. The SOC will be in a hardware sleep state and plugging in the AC adapter returns control to the kernel's s2idle loop.
Normally if just IRQ 9 was active the s2idle loop would advance any EC transactions and no other IRQ being active would cause the s2idle loop to put the SOC back into hardware sleep state.
When this bug occurred IRQ 1 is also active even if no keyboard activity occurred. This causes the s2idle loop to break and the system to wake.
This is a platform firmware bug triggering IRQ1 without keyboard activity. This occurs in Windows as well, but Windows will enter "SW DRIPS" and then with no activity enters back into "HW DRIPS" (hardware sleep state).
This issue affects Renoir, Lucienne, Cezanne, and Barcelo platforms. It does not happen on newer systems such as Mendocino or Rembrandt.
It's been fixed in newer platform firmware. To avoid triggering the bug on older systems check the SMU F/W version and adjust the policy at suspend time for s2idle wakeup from keyboard on these systems. A lot of thought and experimentation has been given around the timing of disabling IRQ1, and to make it work the "suspend" PM callback is restored.
Reported-by: Kai-Heng Feng kai.heng.feng@canonical.com Reported-by: Xaver Hugl xaver.hugl@gmail.com Link: https://gitlab.freedesktop.org/drm/amd/-/issues/2115 Link: https://gitlab.freedesktop.org/drm/amd/-/issues/1951 Signed-off-by: Mario Limonciello mario.limonciello@amd.com Link: https://lore.kernel.org/r/20230120191519.15926-1-mario.limonciello@amd.com Reviewed-by: Hans de Goede hdegoede@redhat.com Signed-off-by: Hans de Goede hdegoede@redhat.com (cherry picked from commit 8e60615e8932167057b363c11a7835da7f007106) This has been hand modified for missing dependency commits. Link: https://gitlab.freedesktop.org/drm/amd/-/issues/1921#note_1770257 Signed-off-by: Mario Limonciello mario.limonciello@amd.com --- drivers/platform/x86/amd-pmc.c | 37 ++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+)
diff --git a/drivers/platform/x86/amd-pmc.c b/drivers/platform/x86/amd-pmc.c index e6158d0f2ac3..83fea28bbb4f 100644 --- a/drivers/platform/x86/amd-pmc.c +++ b/drivers/platform/x86/amd-pmc.c @@ -20,6 +20,7 @@ #include <linux/module.h> #include <linux/pci.h> #include <linux/platform_device.h> +#include <linux/serio.h> #include <linux/suspend.h> #include <linux/seq_file.h> #include <linux/uaccess.h> @@ -412,12 +413,48 @@ static int amd_pmc_get_os_hint(struct amd_pmc_dev *dev) return -EINVAL; }
+static int amd_pmc_czn_wa_irq1(struct amd_pmc_dev *pdev) +{ + struct device *d; + int rc; + + if (!pdev->major) { + rc = amd_pmc_get_smu_version(pdev); + if (rc) + return rc; + } + + if (pdev->major > 64 || (pdev->major == 64 && pdev->minor > 65)) + return 0; + + d = bus_find_device_by_name(&serio_bus, NULL, "serio0"); + if (!d) + return 0; + if (device_may_wakeup(d)) { + dev_info_once(d, "Disabling IRQ1 wakeup source to avoid platform firmware bug\n"); + disable_irq_wake(1); + device_set_wakeup_enable(d, false); + } + put_device(d); + + return 0; +} + static int __maybe_unused amd_pmc_suspend(struct device *dev) { struct amd_pmc_dev *pdev = dev_get_drvdata(dev); int rc; u8 msg;
+ if (pdev->cpu_id == AMD_CPU_ID_CZN) { + int rc = amd_pmc_czn_wa_irq1(pdev); + + if (rc) { + dev_err(pdev->dev, "failed to adjust keyboard wakeup: %d\n", rc); + return rc; + } + } + /* 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);
On Mon, Feb 13, 2023 at 04:05:33PM -0600, Mario Limonciello wrote:
A number of Cezanne systems report IRQ1 as a wakeup source when it's not actually a wakeup. This can cause problems for certain ACPI events. The following fix went upstream that fixed it:
commit 8e60615e8932 ("platform/x86/amd: pmc: Disable IRQ1 wakeup for RN/CZN")
It was reported that this fix actually helped here with older kernels too: Link: https://gitlab.freedesktop.org/drm/amd/-/issues/1921#note_1770257
So backport this fix to 5.15.y as well. This backport is dependent upon being able to read the SMU version which happened in a different commit. So backport that commit and follow up fixes as well.
v1->v2:
- Split into multiple commits
- Catch some fixes for reading SMU version too
Hans de Goede (1): platform/x86: amd-pmc: Fix compilation when CONFIG_DEBUGFS is disabled
Mario Limonciello (2): platform/x86: amd-pmc: Correct usage of SMU version platform/x86/amd: pmc: Disable IRQ1 wakeup for RN/CZN
Sanket Goswami (1): platform/x86: amd-pmc: Export Idlemask values based on the APU
drivers/platform/x86/amd-pmc.c | 116 +++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+)
-- 2.34.1
All now queued up, thanks.
greg k-h
linux-stable-mirror@lists.linaro.org