aboutsummarylogtreecommitdiffstats
path: root/9001-v5.14.13-s0ix-patch-2021-10-17.patch
diff options
context:
space:
mode:
Diffstat (limited to '9001-v5.14.13-s0ix-patch-2021-10-17.patch')
-rw-r--r--9001-v5.14.13-s0ix-patch-2021-10-17.patch803
1 files changed, 803 insertions, 0 deletions
diff --git a/9001-v5.14.13-s0ix-patch-2021-10-17.patch b/9001-v5.14.13-s0ix-patch-2021-10-17.patch
new file mode 100644
index 000000000000..b768c9891b7f
--- /dev/null
+++ b/9001-v5.14.13-s0ix-patch-2021-10-17.patch
@@ -0,0 +1,803 @@
+From 96d064ca676e6f62cc4e8850bebc3ce56498c2a5 Mon Sep 17 00:00:00 2001
+From: Scott B <arglebargle@arglebargle.dev>
+Date: Sun, 17 Oct 2021 07:39:02 -0700
+Subject: [PATCH] v5.14.13 s0ix patch 2021-10-17
+
+Squashed commit of the following:
+
+commit 88230ab44d6f9fa6f76b6f24dc4e2ce6636715c5
+Author: Scott B <arglebargle@arglebargle.dev>
+Date: Sun Oct 17 07:03:00 2021 -0700
+
+ pinctrl: amd: Fix wakeups when IRQ is shared with SCI (FIXUP)
+
+commit 71ec0b27fda84c20b2051f33d2830be749a21257
+Author: Mario Limonciello <mario.limonciello@amd.com>
+Date: Fri Oct 15 09:43:32 2021 -0500
+
+ pinctrl: amd: Fix wakeups when IRQ is shared with SCI
+
+ 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
+ Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
+
+commit 5de6a0019bc8c145249a581c7f9a9d39a37e769c
+Author: Sachi King <nakato@nakato.io>
+Date: Sat Oct 9 14:32:40 2021 +1100
+
+ pinctrl: amd: disable and mask interrupts on probe
+
+ Some systems such as the Microsoft Surface Laptop 4 leave interrupts
+ enabled and configured for use in sleep states on boot, which cause
+ unexpected behaviour such as spurious wakes and failed resumes in
+ s2idle states.
+
+ As interrupts should not be enabled until they are claimed and
+ explicitly enabled, disabling any interrupts mistakenly left enabled by
+ firmware should be safe.
+
+ Signed-off-by: Sachi King <nakato@nakato.io>
+
+commit f7ebc28774dacd2005465491b477f1f4c22eb0e2
+Author: Sanket Goswami <Sanket.Goswami@amd.com>
+Date: Tue Oct 5 21:26:41 2021 +0530
+
+ platform/x86: amd-pmc: Add support for AMD Smart Trace Buffer
+
+ 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>
+ Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
+
+commit eac3a5ace2bf72169c1b603f925cb31b064ec1fd
+Author: Sachi King <nakato@nakato.io>
+Date: Sat Oct 2 14:18:40 2021 +1000
+
+ ACPI: PM: Include alternate AMDI0005 id in special behaviour
+
+ The Surface Laptop 4 AMD has used the AMD0005 to identify this
+ controller instead of using the appropriate ACPI ID AMDI0005. The
+ AMD0005 needs the same special casing as AMDI0005.
+
+ Cc: <stable@vger.kernel.org> # 5.14+
+ Signed-off-by: Sachi King <nakato@nakato.io>
+
+commit cee45b2ead71565f22f31594d55addbeb1ac9f5e
+Author: Sachi King <nakato@nakato.io>
+Date: Sat Oct 2 14:18:39 2021 +1000
+
+ platform/x86: amd-pmc: Add alternative acpi id for PMC controller
+
+ The Surface Laptop 4 AMD has used the AMD0005 to identify this
+ controller instead of using the appropriate ACPI ID AMDI0005. Include
+ AMD0005 in the acpi id list.
+
+ Cc: <stable@vger.kernel.org> # 5.14+
+ Signed-off-by: Sachi King <nakato@nakato.io>
+
+commit b8ca017aaf1ccaf765d665b70033f31faae702cc
+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 1b5d54074071b787f5f000e690bbbffe300039fe
+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 43ad349462f59bec501a1204b7701d3dc2dd7b73
+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 4711b9421ba64c864fdfd9b52c1425216559cc37
+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 0bf64dbc15381fd63cef11035e54970c19052058
+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 6738be9a1e6a13dfc050482e6289e8b62df59e5d
+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 9e06b4819516c194ed762b59ff345fde96835c5a
+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>
+
+commit c2690a5827ca0da39ebc5d52386e71d6e427f3a9
+Author: Basavaraj Natikar <Basavaraj.Natikar@amd.com>
+Date: Tue Aug 31 17:36:12 2021 +0530
+
+ pinctrl: amd: Add irq field data
+
+ pinctrl_amd use gpiochip_get_data() to get their local state containers
+ back from the gpiochip passed as amd_gpio chip data.
+
+ Hence added irq field data to get directly using amd_gpio chip data.
+
+ Signed-off-by: Basavaraj Natikar <Basavaraj.Natikar@amd.com>
+
+commit ce18e7758d09249f615318a663c061c7cd56c036
+Author: Basavaraj Natikar <Basavaraj.Natikar@amd.com>
+Date: Tue Aug 31 17:36:13 2021 +0530
+
+ pinctrl: amd: Handle wake-up interrupt
+
+ Enable/disable power management wakeup mode, which is disabled by
+ default. enable_irq_wake enables wakes the system from sleep.
+
+ Hence added enable/disable irq_wake to handle wake-up interrupt.
+
+ Signed-off-by: Basavaraj Natikar <Basavaraj.Natikar@amd.com>
+---
+ drivers/acpi/processor_idle.c | 3 +-
+ drivers/acpi/x86/s2idle.c | 9 +-
+ drivers/pinctrl/Kconfig | 2 +-
+ drivers/pinctrl/pinctrl-amd.c | 77 +++++++++++--
+ drivers/pinctrl/pinctrl-amd.h | 1 +
+ drivers/platform/x86/amd-pmc.c | 200 ++++++++++++++++++++++++++++++++-
+ 6 files changed, 276 insertions(+), 16 deletions(-)
+
+diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
+index 095c8aca141e..1b6529396371 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 bd92b549fd5a..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",
+@@ -371,7 +377,7 @@ static int lps0_device_attach(struct acpi_device *adev,
+ return 0;
+
+ if (acpi_s2idle_vendor_amd()) {
+- /* AMD0004, AMDI0005:
++ /* AMD0004, AMD0005, AMDI0005:
+ * - Should use rev_id 0x0
+ * - function mask > 0x3: Should use AMD method, but has off by one bug
+ * - function mask = 0x3: Should use Microsoft method
+@@ -390,6 +396,7 @@ static int lps0_device_attach(struct acpi_device *adev,
+ ACPI_LPS0_DSM_UUID_MICROSOFT, 0,
+ &lps0_dsm_guid_microsoft);
+ if (lps0_dsm_func_mask > 0x3 && (!strcmp(hid, "AMD0004") ||
++ !strcmp(hid, "AMD0005") ||
+ !strcmp(hid, "AMDI0005"))) {
+ lps0_dsm_func_mask = (lps0_dsm_func_mask << 1) | 0x1;
+ acpi_handle_debug(adev->handle, "_DSM UUID %s: Adjusted function mask: 0x%x\n",
+diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
+index f38f12801f18..b225e28e433c 100644
+--- a/drivers/pinctrl/Kconfig
++++ b/drivers/pinctrl/Kconfig
+@@ -96,7 +96,7 @@ config PINCTRL_AT91PIO4
+ config PINCTRL_AMD
+ tristate "AMD GPIO pin control"
+ depends on HAS_IOMEM
+- depends on ACPI || COMPILE_TEST
++ depends on ACPI
+ select GPIOLIB
+ select GPIOLIB_IRQCHIP
+ select PINMUX
+diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c
+index 5b764740b829..0a9360ee6be1 100644
+--- a/drivers/pinctrl/pinctrl-amd.c
++++ b/drivers/pinctrl/pinctrl-amd.c
+@@ -445,6 +445,7 @@ static int amd_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct amd_gpio *gpio_dev = gpiochip_get_data(gc);
+ u32 wake_mask = BIT(WAKE_CNTRL_OFF_S0I3) | BIT(WAKE_CNTRL_OFF_S3);
++ int err;
+
+ raw_spin_lock_irqsave(&gpio_dev->lock, flags);
+ pin_reg = readl(gpio_dev->base + (d->hwirq)*4);
+@@ -457,6 +458,15 @@ static int amd_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
+ writel(pin_reg, gpio_dev->base + (d->hwirq)*4);
+ raw_spin_unlock_irqrestore(&gpio_dev->lock, flags);
+
++ if (on)
++ err = enable_irq_wake(gpio_dev->irq);
++ else
++ err = disable_irq_wake(gpio_dev->irq);
++
++ if (err)
++ dev_err(&gpio_dev->pdev->dev, "failed to %s wake-up interrupt\n",
++ on ? "enable" : "disable");
++
+ return 0;
+ }
+
+@@ -588,16 +598,16 @@ 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 _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;
+ u32 regval;
+ u64 status, mask;
++ bool ret = false;
+
+ /* Read the wake status */
+ raw_spin_lock_irqsave(&gpio_dev->lock, flags);
+@@ -617,6 +627,12 @@ 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);
++ /* called from resume context on a shared IRQ, just
++ * checking wake source.
++ */
++ if (irq < 0 && (regval & BIT(WAKE_STS_OFF)))
++ return true;
++
+ if (!(regval & PIN_IRQ_PENDING) ||
+ !(regval & BIT(INTERRUPT_MASK_OFF)))
+ continue;
+@@ -642,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;
+ }
+ }
++ /* called from resume context on shared IRQ but didn't cause wake */
++ if (irq < 0)
++ return ret;
+
+ /* Signal EOI to the GPIO unit */
+ raw_spin_lock_irqsave(&gpio_dev->lock, flags);
+@@ -656,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 _amd_gpio_irq_handler(irq, dev_id) ? IRQ_HANDLED : IRQ_NONE;
++}
++
++static bool amd_gpio_check_wake(void *dev_id)
++{
++ return _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);
+@@ -832,6 +861,34 @@ static const struct pinconf_ops amd_pinconf_ops = {
+ .pin_config_group_set = amd_pinconf_group_set,
+ };
+
++static void amd_gpio_irq_init(struct amd_gpio *gpio_dev)
++{
++ struct pinctrl_desc *desc = gpio_dev->pctrl->desc;
++ unsigned long flags;
++ u32 pin_reg, mask;
++ int i;
++
++ mask = BIT(WAKE_CNTRL_OFF_S0I3) | BIT(WAKE_CNTRL_OFF_S3) |
++ BIT(INTERRUPT_MASK_OFF) | BIT(INTERRUPT_ENABLE_OFF) |
++ BIT(WAKE_CNTRL_OFF_S4);
++
++ for (i = 0; i < desc->npins; i++) {
++ int pin = desc->pins[i].number;
++ const struct pin_desc *pd = pin_desc_get(gpio_dev->pctrl, pin);
++
++ if (!pd)
++ continue;
++
++ raw_spin_lock_irqsave(&gpio_dev->lock, flags);
++
++ pin_reg = readl(gpio_dev->base + i * 4);
++ pin_reg &= ~mask;
++ writel(pin_reg, gpio_dev->base + i * 4);
++
++ raw_spin_unlock_irqrestore(&gpio_dev->lock, flags);
++ }
++}
++
+ #ifdef CONFIG_PM_SLEEP
+ static bool amd_gpio_should_save(struct amd_gpio *gpio_dev, unsigned int pin)
+ {
+@@ -904,7 +961,6 @@ static struct pinctrl_desc amd_pinctrl_desc = {
+ static int amd_gpio_probe(struct platform_device *pdev)
+ {
+ int ret = 0;
+- int irq_base;
+ struct resource *res;
+ struct amd_gpio *gpio_dev;
+ struct gpio_irq_chip *girq;
+@@ -927,9 +983,9 @@ static int amd_gpio_probe(struct platform_device *pdev)
+ if (!gpio_dev->base)
+ return -ENOMEM;
+
+- irq_base = platform_get_irq(pdev, 0);
+- if (irq_base < 0)
+- return irq_base;
++ gpio_dev->irq = platform_get_irq(pdev, 0);
++ if (gpio_dev->irq < 0)
++ return gpio_dev->irq;
+
+ #ifdef CONFIG_PM_SLEEP
+ gpio_dev->saved_regs = devm_kcalloc(&pdev->dev, amd_pinctrl_desc.npins,
+@@ -969,6 +1025,9 @@ static int amd_gpio_probe(struct platform_device *pdev)
+ return PTR_ERR(gpio_dev->pctrl);
+ }
+
++ /* Disable and mask interrupts */
++ amd_gpio_irq_init(gpio_dev);
++
+ girq = &gpio_dev->gc.irq;
+ girq->chip = &amd_gpio_irqchip;
+ /* This will let us handle the parent IRQ in the driver */
+@@ -989,12 +1048,13 @@ static int amd_gpio_probe(struct platform_device *pdev)
+ goto out2;
+ }
+
+- ret = devm_request_irq(&pdev->dev, irq_base, amd_gpio_irq_handler,
++ ret = devm_request_irq(&pdev->dev, gpio_dev->irq, amd_gpio_irq_handler,
+ IRQF_SHARED, KBUILD_MODNAME, gpio_dev);
+ if (ret)
+ 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;
+@@ -1012,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/pinctrl/pinctrl-amd.h b/drivers/pinctrl/pinctrl-amd.h
+index 95e763424042..1d4317073654 100644
+--- a/drivers/pinctrl/pinctrl-amd.h
++++ b/drivers/pinctrl/pinctrl-amd.h
+@@ -98,6 +98,7 @@ struct amd_gpio {
+ struct resource *res;
+ struct platform_device *pdev;
+ u32 *saved_regs;
++ int irq;
+ };
+
+ /* KERNCZ configuration*/
+diff --git a/drivers/platform/x86/amd-pmc.c b/drivers/platform/x86/amd-pmc.c
+index d6a7c896ac86..dc851c2c4d1c 100644
+--- a/drivers/platform/x86/amd-pmc.c
++++ b/drivers/platform/x86/amd-pmc.c
+@@ -29,6 +29,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 +86,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,14 +121,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 u32 stb_data[FIFO_SIZE];
++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_write_stb(struct amd_pmc_dev *dev, u32 data);
++static int amd_pmc_read_stb(struct amd_pmc_dev *dev);
+ static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, bool set, u32 *data, u8 msg, bool ret);
+
+ static inline u32 amd_pmc_reg_read(struct amd_pmc_dev *dev, int reg_offset)
+@@ -133,7 +156,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 +170,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)
+ {
+@@ -162,9 +228,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 +270,37 @@ 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 int stb_read_show(struct seq_file *seq, void *unused)
++{
++ struct amd_pmc_dev *pdev = seq->private;
++ int i;
++
++ amd_pmc_read_stb(pdev);
++
++ for (i = 0; i < FIFO_SIZE; i += 4)
++ seq_hex_dump(seq, "", DUMP_PREFIX_NONE, 16, 1, &stb_data[i], 16, true);
++
++ return 0;
++}
++DEFINE_SHOW_ATTRIBUTE(stb_read);
++
+ static void amd_pmc_dbgfs_unregister(struct amd_pmc_dev *dev)
+ {
+ debugfs_remove_recursive(dev->dbgfs_dir);
+@@ -213,6 +313,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,
++ &stb_read_fops);
+ }
+ #else
+ static inline void amd_pmc_dbgfs_register(struct amd_pmc_dev *dev)
+@@ -349,11 +455,16 @@ 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)
+ dev_err(pdev->dev, "suspend failed\n");
+
++ if (enable_stb)
++ amd_pmc_write_stb(pdev, AMD_PMC_STB_PREDEF);
++
+ return rc;
+ }
+
+@@ -363,14 +474,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 +505,76 @@ static const struct pci_device_id pmc_pci_ids[] = {
+ { }
+ };
+
++static int amd_pmc_get_root_port(struct amd_pmc_dev *dev)
++{
++ dev->rdev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0, 0));
++ if (!dev->rdev || !pci_match_id(pmc_pci_ids, dev->rdev)) {
++ pci_dev_put(dev->rdev);
++ return -ENODEV;
++ }
++
++ return 0;
++}
++
++static int amd_pmc_write_stb(struct amd_pmc_dev *dev, u32 data)
++{
++ int rc;
++
++ rc = amd_pmc_get_root_port(dev);
++ if (rc)
++ return 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 cnt = 0, value;
++ int i, err;
++
++ err = amd_pmc_get_root_port(dev);
++ if (err)
++ return err;
++
++ 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);
++ }
++
++ stb_data[cnt++] = value;
++ }
++
++ return 0;
++}
++
+ static int amd_pmc_probe(struct platform_device *pdev)
+ {
+ struct amd_pmc_dev *dev = &pmc;
+@@ -457,6 +645,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;
+@@ -476,6 +665,7 @@ static const struct acpi_device_id amd_pmc_acpi_ids[] = {
+ {"AMDI0006", 0},
+ {"AMDI0007", 0},
+ {"AMD0004", 0},
++ {"AMD0005", 0},
+ { }
+ };
+ MODULE_DEVICE_TABLE(acpi, amd_pmc_acpi_ids);
+--
+2.33.1
+