diff options
Diffstat (limited to '9001-v5.15-s0ix-patch-2021-11-19.patch')
-rw-r--r-- | 9001-v5.15-s0ix-patch-2021-11-19.patch | 1051 |
1 files changed, 1051 insertions, 0 deletions
diff --git a/9001-v5.15-s0ix-patch-2021-11-19.patch b/9001-v5.15-s0ix-patch-2021-11-19.patch new file mode 100644 index 000000000000..13beb6dbb834 --- /dev/null +++ b/9001-v5.15-s0ix-patch-2021-11-19.patch @@ -0,0 +1,1051 @@ +From 62f3bba24251ed9351a6f90413f196b52d4475fe Mon Sep 17 00:00:00 2001 +From: Scott B <arglebargle@arglebargle.dev> +Date: Fri, 19 Nov 2021 16:46:04 -0800 +Subject: [PATCH] v5.15 s0ix patch 2021-11-19 + +Squashed commit of the following: + +commit ba41527a12621cb4e7124ce4afcf6aa4f991a769 +Author: Alex Deucher <alexander.deucher@amd.com> +Date: Thu Nov 18 14:54:53 2021 -0500 + + drm/amdgpu/gfx9: switch to golden tsc registers for renoir+ + + Renoir and newer gfx9 APUs have new TSC register that is + not part of the gfxoff tile, so it can be read without + needing to disable gfx off. + + Signed-off-by: Alex Deucher <alexander.deucher@amd.com> + +commit 8e2d97445c1620f1b9cd5283b9cb654afa3e4b91 +Author: Alex Deucher <alexander.deucher@amd.com> +Date: Thu Nov 18 14:54:52 2021 -0500 + + drm/amdgpu/gfx10: add wraparound gpu counter check for APUs as well + + Apply the same check we do for dGPUs for APUs as well. + + Signed-off-by: Alex Deucher <alexander.deucher@amd.com> + +commit 462c4b0a294a94d38c3ea9e32f09eafe42777616 +Author: Julian Sikorski <belegdol+github@gmail.com> +Date: Fri Nov 19 17:52:36 2021 +0100 + + GFXOFF check patch by Lijo Lazar + +commit 8baca2bf29ff89df4d7310caa156f49b85eac9c4 +Author: Sanket Goswami <Sanket.Goswami@amd.com> +Date: Thu Oct 28 17:09:35 2021 +0530 + + platform/x86: amd-pmc: Add support for AMD Smart Trace Buffer (v5) + + STB (Smart Trace Buffer), is a debug trace buffer which is used to help + isolate failures by analyzing the last feature that a system was running + before hitting a failure. This nonintrusive way is always running in the + background and trace is stored into the SoC. + + This patch provides mechanism to access the STB buffer using the read + and write routines. + + 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> + +commit 1aa4adac827e28d33352e47988c3d3311bfeab85 +Author: Sanket Goswami <Sanket.Goswami@amd.com> +Date: Thu Oct 28 17:09:34 2021 +0530 + + platform/x86: amd-pmc: Store the pci_dev instance inside struct amd_pmc_dev + + Store the root port information in amd_pmc_probe() so that the + information can be used across multiple routines. + + Signed-off-by: Sanket Goswami <Sanket.Goswami@amd.com> + +commit 0e7044e4961c1c735e16f9c8033637cdcef839b3 +Author: Sanket Goswami <Sanket.Goswami@amd.com> +Date: Thu Oct 28 17:09:33 2021 +0530 + + platform/x86: amd-pmc: Simplify error handling path + + Handle error-exits in the amd_pmc_probe() so that the code duplication + is reduced. + + Signed-off-by: Sanket Goswami <Sanket.Goswami@amd.com> + +commit 989a04253b89ace8343625e79b9a6682b615f922 +Author: Mario Limonciello <mario.limonciello@amd.com> +Date: Sun Oct 31 20:48:53 2021 -0500 + + pinctrl: amd: Fix wakeups when IRQ is shared with SCI (v7) + + On some Lenovo AMD Gen2 platforms the IRQ for the SCI and pinctrl drivers + are shared. Due to how the s2idle loop handling works, this case needs + an extra explicit check whether the interrupt was caused by SCI or by + the GPIO controller. + + To fix this rework the existing IRQ handler function to function as a + checker and an IRQ handler depending on the calling arguments. + + BugLink: https://gitlab.freedesktop.org/drm/amd/-/issues/1738 + Reported-by: Joerie de Gram <j.de.gram@gmail.com> + Signed-off-by: Mario Limonciello <mario.limonciello@amd.com> + Acked-by: Basavaraj Natikar <Basavaraj.Natikar@amd.com> + +commit 926f0d2773e3d42a0863c05c3700e0665251db87 +Author: Mario Limonciello <mario.limonciello@amd.com> +Date: Fri Oct 29 15:40:16 2021 -0500 + + ACPI: Add stubs for wakeup handler functions + + The commit ddfd9dcf270c ("ACPI: PM: Add acpi_[un]register_wakeup_handler()") + added new functions for drivers to use during the s2idle wakeup path, but + didn't add stubs for when CONFIG_ACPI wasn't set. + + Add those stubs in for other drivers to be able to use. + + Fixes: ddfd9dcf270c ("ACPI: PM: Add acpi_[un]register_wakeup_handler()") + Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> + Signed-off-by: Mario Limonciello <mario.limonciello@amd.com> + +commit e284369442c61048b06e31ece7282b032de09c29 +Author: Hans de Goede <hdegoede@redhat.com> +Date: Tue Nov 2 16:32:56 2021 +0100 + + platform/x86: amd-pmc: Make CONFIG_AMD_PMC depend on RTC_CLASS + + Since the "Add special handling for timer based S0i3 wakeup" changes + the amd-pmc code now relies on symbols from the RTC-class code, + add a dependency for this to Kconfig. + + Fixes: 59348401ebed ("platform/x86: amd-pmc: Add special handling for timer based S0i3 wakeup") + Cc: Mario Limonciello <mario.limonciello@amd.com> + Reported-by: Randy Dunlap <rdunlap@infradead.org> + Signed-off-by: Hans de Goede <hdegoede@redhat.com> + Acked-by: Mario Limonciello <mario.limonciello@amd.com> + +commit 7d269824781acfca610b81e3747833423a4b314a +Author: Mario Limonciello <mario.limonciello@amd.com> +Date: Tue Oct 26 12:14:43 2021 -0500 + + platform/x86: amd-pmc: Drop check for valid alarm time + + This is already checked when calling rtc_read_alarm. + + Suggested-by: Alexandre Belloni <alexandre.belloni@bootlin.com> + Signed-off-by: Mario Limonciello <mario.limonciello@amd.com> + +commit f62a21feb82e82bbdda66ac7b0ce04dab4b43e0b +Author: Mario Limonciello <mario.limonciello@amd.com> +Date: Tue Oct 26 12:14:42 2021 -0500 + + platform/x86: amd-pmc: Downgrade dev_info message to dev_dbg + + For the majority of users this information will not be informative + as they've chosen to program the RTC before going to sleep. + + Suggested-by: Alexandre Belloni <alexandre.belloni@bootlin.com> + Signed-off-by: Mario Limonciello <mario.limonciello@amd.com> + +commit c01d560f2b53e1519676b8c412802e1ce4ed6b02 +Author: Mario Limonciello <mario.limonciello@amd.com> +Date: Tue Oct 26 12:14:41 2021 -0500 + + platform/x86: amd-pmc: fix compilation without CONFIG_RTC_SYSTOHC_DEVICE + + If the configuration hasn't specified this parameter the rest of the new + RTC functionality should just be ignored. + + Cc: Alexandre Belloni <alexandre.belloni@bootlin.com> + Suggested-by: Hans de Goede <hdegoede@redhat.com> + Fixes: 59348401ebed ("platform/x86: amd-pmc: Add special handling for timer based S0i3 wakeup") + Signed-off-by: Mario Limonciello <mario.limonciello@amd.com> + +commit 0f8c1535f8317ef99ec51f11c00584a85bdf49fd +Author: Mario Limonciello <mario.limonciello@amd.com> +Date: Wed Oct 20 11:29:46 2021 -0500 + + 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> + +commit 13232abac47845187d59e20409f888899f0b36d3 +Author: Mario Limonciello <mario.limonciello@amd.com> +Date: Wed Oct 20 11:29:45 2021 -0500 + + platform/x86: amd-pmc: adjust arguments for `amd_pmc_send_cmd` + + Currently the "argument" for the "message" is listed as a boolean + value. This works well for the commands used currently, but an + additional upcoming command will pass more data in the message. + + Expand it to be a full 32 bit value. + + Signed-off-by: Mario Limonciello <mario.limonciello@amd.com> + +commit 6d42af9d4af7b3e53c8d8c853effbcc84623b414 +Author: Hans de Goede <hdegoede@redhat.com> +Date: Tue Sep 28 16:21:22 2021 +0200 + + platform/x86: amd-pmc: Fix compilation when CONFIG_DEBUGFS is disabled + + 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. + + 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> + +commit 26f5620d051d1c3d9824da3df5ad6f7865154d56 +Author: Mario Limonciello <mario.limonciello@amd.com> +Date: Fri Sep 24 12:32:06 2021 -0500 + + ACPI: PM: s2idle: Don't report missing devices as failing constraints + + ACPI tables may have entries for devices that are not physically + present but that can be connected. These devices shouldn't cause + constraints checking to fail. + + Signed-off-by: Mario Limonciello <mario.limonciello@amd.com> + Change-Id: I34f5ca978aab69ff0a0906191eec21649b19fe27 + +commit 9b45888587b66d06c38c47958e7e503d0a367ad6 +Author: Sanket Goswami <Sanket.Goswami@amd.com> +Date: Tue Sep 21 17:30:20 2021 +0530 + + platform/x86: amd-pmc: Add a message to print resume time info + + Add a message to print the resume time information obtained from the + smu_metrics structure. + + Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com> + Signed-off-by: Sanket Goswami <Sanket.Goswami@amd.com> + +commit 65e81196616f652d9b202f7af689129c1feb1212 +Author: Sanket Goswami <Sanket.Goswami@amd.com> +Date: Tue Sep 21 17:29:10 2021 +0530 + + platform/x86: amd-pmc: Send command to dump data after clearing OS_HINT + + It was reported that the resume stats received from the firmware are + always zero. This happens because the SMU expects the driver to send the + command to dump the log data after clearing the OS_HINT. + + Adjust the order of the commands sent to SMU. + + Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com> + Signed-off-by: Sanket Goswami <Sanket.Goswami@amd.com> + +commit 8bc5014976796c839be445928f13c9338dd49add +Author: Sanket Goswami <Sanket.Goswami@amd.com> +Date: Thu Sep 16 18:11:30 2021 +0530 + + platform/x86: amd-pmc: Check s0i3 cycle status + + As the PM firmware returns the status of the last s0i3 in the smu_metrics + structure, the existing name "s0i3_cyclecount" seems to be a misnomer. + Change it accordingly to "s0i3_last_entry_status". + + Signed-off-by: Sanket Goswami <Sanket.Goswami@amd.com> + Acked-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com> + +commit e47fa03b6683ef26e4fe6a74432920d0f71b2795 +Author: Sanket Goswami <Sanket.Goswami@amd.com> +Date: Thu Sep 16 18:10:02 2021 +0530 + + platform/x86: amd-pmc: Export Idlemask values based on the APU + + 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> + Acked-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com> + Reviewed-by: Mario Limonciello <mario.limonciello@amd.com> + +commit b10ad23222811cf6fd409228990eaa651f54e1b8 +Author: Mario Limonciello <mario.limonciello@amd.com> +Date: Wed Sep 15 16:52:16 2021 -0500 + + ACPI: processor idle: Allow playing dead in C3 + + commit 1a022e3f1be1 ("idle, x86: Allow off-lined CPU to enter + deeper C states") originally allowed offlined CPUs to play dead + up to ACPI C2. Although this improves power consumption of offlined + CPUs, it does not allow the CPUs to get into the deepest state + on AMD platforms blocking s0i3 entry. + + BugLink: https://gitlab.freedesktop.org/drm/amd/-/issues/1708 + Signed-off-by: Mario Limonciello <mario.limonciello@amd.com> +--- + drivers/acpi/processor_idle.c | 3 +- + drivers/acpi/x86/s2idle.c | 6 + + drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c | 15 +- + drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c | 49 +++- + drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c | 5 +- + drivers/pinctrl/pinctrl-amd.c | 29 ++- + drivers/platform/x86/Kconfig | 2 +- + drivers/platform/x86/amd-pmc.c | 301 ++++++++++++++++++++-- + include/linux/acpi.h | 9 + + 9 files changed, 380 insertions(+), 39 deletions(-) + +diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c +index f37fba9e5ba0..9d378dc9e928 100644 +--- a/drivers/acpi/processor_idle.c ++++ b/drivers/acpi/processor_idle.c +@@ -789,7 +789,8 @@ static int acpi_processor_setup_cstates(struct acpi_processor *pr) + state->enter = acpi_idle_enter; + + state->flags = 0; +- if (cx->type == ACPI_STATE_C1 || cx->type == ACPI_STATE_C2) { ++ if (cx->type == ACPI_STATE_C1 || cx->type == ACPI_STATE_C2 ++ || cx->type == ACPI_STATE_C3) { + state->enter_dead = acpi_idle_play_dead; + drv->safe_state_index = count; + } +diff --git a/drivers/acpi/x86/s2idle.c b/drivers/acpi/x86/s2idle.c +index 1c48358b43ba..0b65d4623214 100644 +--- a/drivers/acpi/x86/s2idle.c ++++ b/drivers/acpi/x86/s2idle.c +@@ -309,6 +309,12 @@ static void lpi_check_constraints(void) + continue; + } + ++ if (!acpi_get_first_physical_node(adev)) { ++ acpi_handle_debug(handle, "LPI: Device is not physically present\n"); ++ lpi_constraints_table[i].handle = NULL; ++ continue; ++ } ++ + if (adev->power.state < lpi_constraints_table[i].min_dstate) + acpi_handle_info(handle, + "LPI: Constraint not met; min power state:%s current power state:%s\n", +diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c +index 16dbe593cba2..970d59a21005 100644 +--- a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c ++++ b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c +@@ -7729,8 +7729,19 @@ static uint64_t gfx_v10_0_get_gpu_clock_counter(struct amdgpu_device *adev) + switch (adev->asic_type) { + case CHIP_VANGOGH: + case CHIP_YELLOW_CARP: +- clock = (uint64_t)RREG32_SOC15(SMUIO, 0, mmGOLDEN_TSC_COUNT_LOWER_Vangogh) | +- ((uint64_t)RREG32_SOC15(SMUIO, 0, mmGOLDEN_TSC_COUNT_UPPER_Vangogh) << 32ULL); ++ preempt_disable(); ++ clock_hi = RREG32_SOC15_NO_KIQ(SMUIO, 0, mmGOLDEN_TSC_COUNT_UPPER_Vangogh); ++ clock_lo = RREG32_SOC15_NO_KIQ(SMUIO, 0, mmGOLDEN_TSC_COUNT_LOWER_Vangogh); ++ hi_check = RREG32_SOC15_NO_KIQ(SMUIO, 0, mmGOLDEN_TSC_COUNT_UPPER_Vangogh); ++ /* The SMUIO TSC clock frequency is 100MHz, which sets 32-bit carry over ++ * roughly every 42 seconds. ++ */ ++ if (hi_check != clock_hi) { ++ clock_lo = RREG32_SOC15_NO_KIQ(SMUIO, 0, mmGOLDEN_TSC_COUNT_LOWER_Vangogh); ++ clock_hi = hi_check; ++ } ++ preempt_enable(); ++ clock = clock_lo | (clock_hi << 32ULL); + break; + default: + preempt_disable(); +diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c +index 025184a556ee..3fee7ea7f05e 100644 +--- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c ++++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c +@@ -140,6 +140,11 @@ MODULE_FIRMWARE("amdgpu/aldebaran_rlc.bin"); + #define mmTCP_CHAN_STEER_5_ARCT 0x0b0c + #define mmTCP_CHAN_STEER_5_ARCT_BASE_IDX 0 + ++#define mmGOLDEN_TSC_COUNT_UPPER_Renoir 0x0025 ++#define mmGOLDEN_TSC_COUNT_UPPER_Renoir_BASE_IDX 1 ++#define mmGOLDEN_TSC_COUNT_LOWER_Renoir 0x0026 ++#define mmGOLDEN_TSC_COUNT_LOWER_Renoir_BASE_IDX 1 ++ + enum ta_ras_gfx_subblock { + /*CPC*/ + TA_RAS_BLOCK__GFX_CPC_INDEX_START = 0, +@@ -4228,19 +4233,39 @@ static uint64_t gfx_v9_0_kiq_read_clock(struct amdgpu_device *adev) + + static uint64_t gfx_v9_0_get_gpu_clock_counter(struct amdgpu_device *adev) + { +- uint64_t clock; +- +- amdgpu_gfx_off_ctrl(adev, false); +- mutex_lock(&adev->gfx.gpu_clock_mutex); +- if (adev->asic_type == CHIP_VEGA10 && amdgpu_sriov_runtime(adev)) { +- clock = gfx_v9_0_kiq_read_clock(adev); +- } else { +- WREG32_SOC15(GC, 0, mmRLC_CAPTURE_GPU_CLOCK_COUNT, 1); +- clock = (uint64_t)RREG32_SOC15(GC, 0, mmRLC_GPU_CLOCK_COUNT_LSB) | +- ((uint64_t)RREG32_SOC15(GC, 0, mmRLC_GPU_CLOCK_COUNT_MSB) << 32ULL); ++ uint64_t clock, clock_lo, clock_hi, hi_check; ++ ++ switch (adev->asic_type) { ++ case CHIP_RENOIR: ++ preempt_disable(); ++ clock_hi = RREG32_SOC15_NO_KIQ(SMUIO, 0, mmGOLDEN_TSC_COUNT_UPPER_Renoir); ++ clock_lo = RREG32_SOC15_NO_KIQ(SMUIO, 0, mmGOLDEN_TSC_COUNT_LOWER_Renoir); ++ hi_check = RREG32_SOC15_NO_KIQ(SMUIO, 0, mmGOLDEN_TSC_COUNT_UPPER_Renoir); ++ /* The SMUIO TSC clock frequency is 100MHz, which sets 32-bit carry over ++ * roughly every 42 seconds. ++ */ ++ if (hi_check != clock_hi) { ++ clock_lo = RREG32_SOC15_NO_KIQ(SMUIO, 0, mmGOLDEN_TSC_COUNT_LOWER_Renoir); ++ clock_hi = hi_check; ++ } ++ preempt_enable(); ++ clock = clock_lo | (clock_hi << 32ULL); ++ break; ++ default: ++ amdgpu_gfx_off_ctrl(adev, false); ++ mutex_lock(&adev->gfx.gpu_clock_mutex); ++ if (adev->asic_type == CHIP_VEGA10 && amdgpu_sriov_runtime(adev)) { ++ clock = gfx_v9_0_kiq_read_clock(adev); ++ } else { ++ WREG32_SOC15(GC, 0, mmRLC_CAPTURE_GPU_CLOCK_COUNT, 1); ++ clock = (uint64_t)RREG32_SOC15(GC, 0, mmRLC_GPU_CLOCK_COUNT_LSB) | ++ ((uint64_t)RREG32_SOC15(GC, 0, mmRLC_GPU_CLOCK_COUNT_MSB) << 32ULL); ++ } ++ mutex_unlock(&adev->gfx.gpu_clock_mutex); ++ amdgpu_gfx_off_ctrl(adev, true); ++ break; ++ ((uint64_t)RREG32_SOC15(GC, 0, mmRLC_GPU_CLOCK_COUNT_MSB) << 32ULL); + } +- mutex_unlock(&adev->gfx.gpu_clock_mutex); +- amdgpu_gfx_off_ctrl(adev, true); + return clock; + } + +diff --git a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c +index 04863a797115..8c54041e6c6b 100644 +--- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c ++++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c +@@ -277,8 +277,11 @@ static int smu_dpm_set_power_gate(void *handle, + struct smu_context *smu = handle; + int ret = 0; + +- if (!smu->pm_enabled || !smu->adev->pm.dpm_enabled) ++ if (!smu->pm_enabled || !smu->adev->pm.dpm_enabled) { ++ WARN(true, "SMU uninitialized but power %s requested for %u!\n", ++ gate ? "gate" : "ungate", block_type); + return -EOPNOTSUPP; ++ } + + switch (block_type) { + /* +diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c +index bae9d429b813..ecab9064a845 100644 +--- a/drivers/pinctrl/pinctrl-amd.c ++++ b/drivers/pinctrl/pinctrl-amd.c +@@ -598,14 +598,14 @@ static struct irq_chip amd_gpio_irqchip = { + + #define PIN_IRQ_PENDING (BIT(INTERRUPT_STS_OFF) | BIT(WAKE_STS_OFF)) + +-static irqreturn_t amd_gpio_irq_handler(int irq, void *dev_id) ++static bool do_amd_gpio_irq_handler(int irq, void *dev_id) + { + struct amd_gpio *gpio_dev = dev_id; + struct gpio_chip *gc = &gpio_dev->gc; +- irqreturn_t ret = IRQ_NONE; + unsigned int i, irqnr; + unsigned long flags; + u32 __iomem *regs; ++ bool ret = false; + u32 regval; + u64 status, mask; + +@@ -627,6 +627,14 @@ static irqreturn_t amd_gpio_irq_handler(int irq, void *dev_id) + /* Each status bit covers four pins */ + for (i = 0; i < 4; i++) { + regval = readl(regs + i); ++ /* caused wake on resume context for shared IRQ */ ++ if (irq < 0 && (regval & BIT(WAKE_STS_OFF))) { ++ dev_dbg(&gpio_dev->pdev->dev, ++ "Waking due to GPIO %d: 0x%x", ++ irqnr + i, regval); ++ return true; ++ } ++ + if (!(regval & PIN_IRQ_PENDING) || + !(regval & BIT(INTERRUPT_MASK_OFF))) + continue; +@@ -650,9 +658,12 @@ static irqreturn_t amd_gpio_irq_handler(int irq, void *dev_id) + } + writel(regval, regs + i); + raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); +- ret = IRQ_HANDLED; ++ ret = true; + } + } ++ /* did not cause wake on resume context for shared IRQ */ ++ if (irq < 0) ++ return false; + + /* Signal EOI to the GPIO unit */ + raw_spin_lock_irqsave(&gpio_dev->lock, flags); +@@ -664,6 +675,16 @@ static irqreturn_t amd_gpio_irq_handler(int irq, void *dev_id) + return ret; + } + ++static irqreturn_t amd_gpio_irq_handler(int irq, void *dev_id) ++{ ++ return IRQ_RETVAL(do_amd_gpio_irq_handler(irq, dev_id)); ++} ++ ++static bool __maybe_unused amd_gpio_check_wake(void *dev_id) ++{ ++ return do_amd_gpio_irq_handler(-1, dev_id); ++} ++ + static int amd_get_groups_count(struct pinctrl_dev *pctldev) + { + struct amd_gpio *gpio_dev = pinctrl_dev_get_drvdata(pctldev); +@@ -1033,6 +1054,7 @@ static int amd_gpio_probe(struct platform_device *pdev) + goto out2; + + platform_set_drvdata(pdev, gpio_dev); ++ acpi_register_wakeup_handler(gpio_dev->irq, amd_gpio_check_wake, gpio_dev); + + dev_dbg(&pdev->dev, "amd gpio driver loaded\n"); + return ret; +@@ -1050,6 +1072,7 @@ static int amd_gpio_remove(struct platform_device *pdev) + gpio_dev = platform_get_drvdata(pdev); + + gpiochip_remove(&gpio_dev->gc); ++ acpi_unregister_wakeup_handler(amd_gpio_check_wake, gpio_dev); + + return 0; + } +diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig +index e21ea3d23e6f..4cc2782635ec 100644 +--- a/drivers/platform/x86/Kconfig ++++ b/drivers/platform/x86/Kconfig +@@ -170,7 +170,7 @@ config ACER_WMI + + config AMD_PMC + tristate "AMD SoC PMC driver" +- depends on ACPI && PCI ++ depends on ACPI && PCI && RTC_CLASS + help + The driver provides support for AMD Power Management Controller + primarily responsible for S2Idle transactions that are driven from +diff --git a/drivers/platform/x86/amd-pmc.c b/drivers/platform/x86/amd-pmc.c +index fc95620101e8..5d1ff04152de 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> +@@ -29,6 +31,16 @@ + #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 ++ ++/* STB Registers */ ++#define AMD_PMC_STB_INDEX_ADDRESS 0xF8 ++#define AMD_PMC_STB_INDEX_DATA 0xFC ++#define AMD_PMC_STB_PMI_0 0x03E30600 ++#define AMD_PMC_STB_PREDEF 0xC6000001 ++ + /* 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 +@@ -76,6 +88,7 @@ + #define SOC_SUBSYSTEM_IP_MAX 12 + #define DELAY_MIN_US 2000 + #define DELAY_MAX_US 3000 ++#define FIFO_SIZE 4096 + enum amd_pmc_def { + MSG_TEST = 0x01, + MSG_OS_HINT_PCO, +@@ -110,15 +123,26 @@ 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 pci_dev *rdev; + struct mutex lock; /* generic mutex lock */ + #if IS_ENABLED(CONFIG_DEBUG_FS) + struct dentry *dbgfs_dir; + #endif /* CONFIG_DEBUG_FS */ + }; + ++static bool enable_stb; ++module_param(enable_stb, bool, 0644); ++MODULE_PARM_DESC(enable_stb, "Enable the STB debug mechanism"); ++ + static struct amd_pmc_dev pmc; +-static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, bool set, u32 *data, u8 msg, bool ret); ++static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool ret); ++static int amd_pmc_write_stb(struct amd_pmc_dev *dev, u32 data); ++static int amd_pmc_read_stb(struct amd_pmc_dev *dev, u32 *buf); + + static inline u32 amd_pmc_reg_read(struct amd_pmc_dev *dev, int reg_offset) + { +@@ -133,7 +157,7 @@ static inline void amd_pmc_reg_write(struct amd_pmc_dev *dev, int reg_offset, u3 + struct smu_metrics { + u32 table_version; + u32 hint_count; +- u32 s0i3_cyclecount; ++ u32 s0i3_last_entry_status; + u32 timein_s0i2; + u64 timeentering_s0i3_lastcapture; + u64 timeentering_s0i3_totaltime; +@@ -147,6 +171,97 @@ 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_stb_debugfs_open(struct inode *inode, struct file *filp) ++{ ++ struct amd_pmc_dev *dev = filp->f_inode->i_private; ++ u32 size = FIFO_SIZE * sizeof(u32); ++ u32 *buf; ++ int rc; ++ ++ buf = kzalloc(size, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ rc = amd_pmc_read_stb(dev, buf); ++ if (rc) ++ goto out; ++ ++ filp->private_data = buf; ++ return 0; ++ ++out: ++ kfree(buf); ++ return rc; ++} ++ ++static ssize_t amd_pmc_stb_debugfs_read(struct file *filp, char __user *buf, size_t size, ++ loff_t *pos) ++{ ++ if (!filp->private_data) ++ return -EINVAL; ++ ++ return simple_read_from_buffer(buf, size, pos, filp->private_data, ++ FIFO_SIZE * sizeof(u32)); ++} ++ ++static int amd_pmc_stb_debugfs_release(struct inode *inode, struct file *filp) ++{ ++ kfree(filp->private_data); ++ filp->private_data = NULL; ++ ++ return 0; ++} ++ ++const struct file_operations amd_pmc_stb_debugfs_fops = { ++ .owner = THIS_MODULE, ++ .open = amd_pmc_stb_debugfs_open, ++ .read = amd_pmc_stb_debugfs_read, ++ .release = amd_pmc_stb_debugfs_release, ++}; ++ ++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) + { +@@ -162,9 +277,12 @@ static int smu_fw_info_show(struct seq_file *s, void *unused) + seq_puts(s, "\n=== SMU Statistics ===\n"); + seq_printf(s, "Table Version: %d\n", table.table_version); + seq_printf(s, "Hint Count: %d\n", table.hint_count); +- seq_printf(s, "S0i3 Cycle Count: %d\n", table.s0i3_cyclecount); ++ seq_printf(s, "Last S0i3 Status: %s\n", table.s0i3_last_entry_status ? "Success" : ++ "Unknown/Fail"); + seq_printf(s, "Time (in us) to S0i3: %lld\n", table.timeentering_s0i3_lastcapture); + seq_printf(s, "Time (in us) in S0i3: %lld\n", table.timein_s0i3_lastcapture); ++ seq_printf(s, "Time (in us) to resume from S0i3: %lld\n", ++ table.timeto_resume_to_os_lastcapture); + + seq_puts(s, "\n=== Active time (in us) ===\n"); + for (idx = 0 ; idx < SOC_SUBSYSTEM_IP_MAX ; idx++) { +@@ -201,6 +319,23 @@ static int s0ix_stats_show(struct seq_file *s, void *unused) + } + DEFINE_SHOW_ATTRIBUTE(s0ix_stats); + ++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 +348,12 @@ 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); ++ /* Enable STB only when the module_param is set */ ++ if (enable_stb) ++ debugfs_create_file("stb_read", 0644, dev->dbgfs_dir, dev, ++ &amd_pmc_stb_debugfs_fops); + } + #else + static inline void amd_pmc_dbgfs_register(struct amd_pmc_dev *dev) +@@ -264,7 +405,7 @@ static void amd_pmc_dump_registers(struct amd_pmc_dev *dev) + dev_dbg(dev->dev, "AMD_PMC_REGISTER_MESSAGE:%x\n", value); + } + +-static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, bool set, u32 *data, u8 msg, bool ret) ++static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool ret) + { + int rc; + u32 val; +@@ -283,7 +424,7 @@ static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, bool set, u32 *data, u8 msg + amd_pmc_reg_write(dev, AMD_PMC_REGISTER_RESPONSE, 0); + + /* Write argument into response register */ +- amd_pmc_reg_write(dev, AMD_PMC_REGISTER_ARGUMENT, set); ++ amd_pmc_reg_write(dev, AMD_PMC_REGISTER_ARGUMENT, arg); + + /* Write message ID to message ID register */ + amd_pmc_reg_write(dev, AMD_PMC_REGISTER_MESSAGE, msg); +@@ -339,21 +480,79 @@ 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("rtc0"); ++ 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_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_dbg(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"); + ++ if (enable_stb) ++ amd_pmc_write_stb(pdev, AMD_PMC_STB_PREDEF); ++ + return rc; + } + +@@ -363,14 +562,21 @@ static int __maybe_unused amd_pmc_resume(struct device *dev) + int rc; + u8 msg; + +- /* Let SMU know that we are looking for stats */ +- amd_pmc_send_cmd(pdev, 0, NULL, SMU_MSG_LOG_DUMP_DATA, 0); +- + msg = amd_pmc_get_os_hint(pdev); + rc = amd_pmc_send_cmd(pdev, 0, NULL, msg, 0); + if (rc) + dev_err(pdev->dev, "resume failed\n"); + ++ /* Let SMU know that we are looking for stats */ ++ amd_pmc_send_cmd(pdev, 0, NULL, SMU_MSG_LOG_DUMP_DATA, 0); ++ ++ /* Dump the IdleMask to see the blockers */ ++ amd_pmc_idlemask_read(pdev, dev, NULL); ++ ++ /* Write data incremented by 1 to distinguish in stb_read */ ++ if (enable_stb) ++ amd_pmc_write_stb(pdev, AMD_PMC_STB_PREDEF + 1); ++ + return 0; + } + +@@ -387,6 +593,57 @@ static const struct pci_device_id pmc_pci_ids[] = { + { } + }; + ++static int amd_pmc_write_stb(struct amd_pmc_dev *dev, u32 data) ++{ ++ int rc; ++ ++ rc = pci_write_config_dword(dev->rdev, AMD_PMC_STB_INDEX_ADDRESS, AMD_PMC_STB_PMI_0); ++ if (rc) { ++ dev_err(dev->dev, "failed to write addr in stb: 0x%X\n", ++ AMD_PMC_STB_INDEX_ADDRESS); ++ pci_dev_put(dev->rdev); ++ return pcibios_err_to_errno(rc); ++ } ++ ++ rc = pci_write_config_dword(dev->rdev, AMD_PMC_STB_INDEX_DATA, data); ++ if (rc) { ++ dev_err(dev->dev, "failed to write data in stb: 0x%X\n", ++ AMD_PMC_STB_INDEX_DATA); ++ pci_dev_put(dev->rdev); ++ return pcibios_err_to_errno(rc); ++ } ++ ++ return 0; ++} ++ ++static int amd_pmc_read_stb(struct amd_pmc_dev *dev, u32 *buf) ++{ ++ int i, err; ++ u32 value; ++ ++ err = pci_write_config_dword(dev->rdev, AMD_PMC_STB_INDEX_ADDRESS, AMD_PMC_STB_PMI_0); ++ if (err) { ++ dev_err(dev->dev, "error writing addr to stb: 0x%X\n", ++ AMD_PMC_STB_INDEX_ADDRESS); ++ pci_dev_put(dev->rdev); ++ return pcibios_err_to_errno(err); ++ } ++ ++ for (i = 0; i < FIFO_SIZE; i++) { ++ err = pci_read_config_dword(dev->rdev, AMD_PMC_STB_INDEX_DATA, &value); ++ if (err) { ++ dev_err(dev->dev, "error reading data from stb: 0x%X\n", ++ AMD_PMC_STB_INDEX_DATA); ++ pci_dev_put(dev->rdev); ++ return pcibios_err_to_errno(err); ++ } ++ ++ *buf++ = value; ++ } ++ ++ return 0; ++} ++ + static int amd_pmc_probe(struct platform_device *pdev) + { + struct amd_pmc_dev *dev = &pmc; +@@ -400,22 +657,23 @@ static int amd_pmc_probe(struct platform_device *pdev) + + rdev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0, 0)); + if (!rdev || !pci_match_id(pmc_pci_ids, rdev)) { +- pci_dev_put(rdev); +- return -ENODEV; ++ err = -ENODEV; ++ goto err_pci_dev_put; + } + + dev->cpu_id = rdev->device; ++ dev->rdev = rdev; + err = pci_write_config_dword(rdev, AMD_PMC_SMU_INDEX_ADDRESS, AMD_PMC_BASE_ADDR_LO); + if (err) { + dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMC_SMU_INDEX_ADDRESS); +- pci_dev_put(rdev); +- return pcibios_err_to_errno(err); ++ err = pcibios_err_to_errno(err); ++ goto err_pci_dev_put; + } + + err = pci_read_config_dword(rdev, AMD_PMC_SMU_INDEX_DATA, &val); + if (err) { +- pci_dev_put(rdev); +- return pcibios_err_to_errno(err); ++ err = pcibios_err_to_errno(err); ++ goto err_pci_dev_put; + } + + base_addr_lo = val & AMD_PMC_BASE_ADDR_HI_MASK; +@@ -423,14 +681,14 @@ static int amd_pmc_probe(struct platform_device *pdev) + err = pci_write_config_dword(rdev, AMD_PMC_SMU_INDEX_ADDRESS, AMD_PMC_BASE_ADDR_HI); + if (err) { + dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMC_SMU_INDEX_ADDRESS); +- pci_dev_put(rdev); +- return pcibios_err_to_errno(err); ++ err = pcibios_err_to_errno(err); ++ goto err_pci_dev_put; + } + + err = pci_read_config_dword(rdev, AMD_PMC_SMU_INDEX_DATA, &val); + if (err) { +- pci_dev_put(rdev); +- return pcibios_err_to_errno(err); ++ err = pcibios_err_to_errno(err); ++ goto err_pci_dev_put; + } + + base_addr_hi = val & AMD_PMC_BASE_ADDR_LO_MASK; +@@ -457,9 +715,14 @@ 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; ++ ++err_pci_dev_put: ++ pci_dev_put(rdev); ++ return err; + } + + static int amd_pmc_remove(struct platform_device *pdev) +diff --git a/include/linux/acpi.h b/include/linux/acpi.h +index 974d497a897d..6224b1e32681 100644 +--- a/include/linux/acpi.h ++++ b/include/linux/acpi.h +@@ -976,6 +976,15 @@ static inline int acpi_get_local_address(acpi_handle handle, u32 *addr) + return -ENODEV; + } + ++static inline int acpi_register_wakeup_handler(int wake_irq, ++ bool (*wakeup)(void *context), void *context) ++{ ++ return -ENXIO; ++} ++ ++static inline void acpi_unregister_wakeup_handler( ++ bool (*wakeup)(void *context), void *context) { } ++ + #endif /* !CONFIG_ACPI */ + + #ifdef CONFIG_ACPI_HOTPLUG_IOAPIC +-- +2.34.0 + |