diff options
Diffstat (limited to 'drivers/platform/x86/amd/pmc.c')
-rw-r--r-- | drivers/platform/x86/amd/pmc.c | 98 |
1 files changed, 91 insertions, 7 deletions
diff --git a/drivers/platform/x86/amd/pmc.c b/drivers/platform/x86/amd/pmc.c index 8d924986381b..ab05b9ee6655 100644 --- a/drivers/platform/x86/amd/pmc.c +++ b/drivers/platform/x86/amd/pmc.c @@ -22,6 +22,7 @@ #include <linux/pci.h> #include <linux/platform_device.h> #include <linux/rtc.h> +#include <linux/serio.h> #include <linux/suspend.h> #include <linux/seq_file.h> #include <linux/uaccess.h> @@ -42,6 +43,7 @@ #define AMD_PMC_STB_S2IDLE_PREPARE 0xC6000001 #define AMD_PMC_STB_S2IDLE_RESTORE 0xC6000002 #define AMD_PMC_STB_S2IDLE_CHECK 0xC6000003 +#define AMD_PMC_STB_DUMMY_PC 0xC6000007 /* STB S2D(Spill to DRAM) has different message port offset */ #define STB_SPILL_TO_DRAM 0xBE @@ -103,6 +105,7 @@ #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, @@ -113,6 +116,7 @@ enum s2d_arg { S2D_TELEMETRY_SIZE = 0x01, S2D_PHYS_ADDR_LOW, S2D_PHYS_ADDR_HIGH, + S2D_NUM_SAMPLES, }; struct amd_pmc_bit_map { @@ -160,6 +164,10 @@ static bool enable_stb; module_param(enable_stb, bool, 0644); MODULE_PARM_DESC(enable_stb, "Enable the STB debug mechanism"); +static bool disable_workarounds; +module_param(disable_workarounds, bool, 0644); +MODULE_PARM_DESC(disable_workarounds, "Disable workarounds for platform bugs"); + static struct amd_pmc_dev pmc; static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool ret); static int amd_pmc_read_stb(struct amd_pmc_dev *dev, u32 *buf); @@ -241,13 +249,40 @@ static const struct file_operations amd_pmc_stb_debugfs_fops = { static int amd_pmc_stb_debugfs_open_v2(struct inode *inode, struct file *filp) { struct amd_pmc_dev *dev = filp->f_inode->i_private; - u32 *buf; + u32 *buf, fsize, num_samples, stb_rdptr_offset = 0; + int ret; + + /* Write dummy postcode while reading the STB buffer */ + ret = amd_pmc_write_stb(dev, AMD_PMC_STB_DUMMY_PC); + if (ret) + dev_err(dev->dev, "error writing to STB: %d\n", ret); buf = kzalloc(S2D_TELEMETRY_BYTES_MAX, GFP_KERNEL); if (!buf) return -ENOMEM; - memcpy_fromio(buf, dev->stb_virt_addr, S2D_TELEMETRY_BYTES_MAX); + /* Spill to DRAM num_samples uses separate SMU message port */ + dev->msg_port = 1; + + /* Get the num_samples to calculate the last push location */ + ret = amd_pmc_send_cmd(dev, S2D_NUM_SAMPLES, &num_samples, STB_SPILL_TO_DRAM, 1); + /* Clear msg_port for other SMU operation */ + dev->msg_port = 0; + if (ret) { + dev_err(dev->dev, "error: S2D_NUM_SAMPLES not supported : %d\n", ret); + return ret; + } + + /* Start capturing data from the last push location */ + if (num_samples > S2D_TELEMETRY_BYTES_MAX) { + fsize = S2D_TELEMETRY_BYTES_MAX; + stb_rdptr_offset = num_samples - fsize; + } else { + fsize = num_samples; + stb_rdptr_offset = 0; + } + + memcpy_fromio(buf, dev->stb_virt_addr + stb_rdptr_offset, fsize); filp->private_data = buf; return 0; @@ -555,13 +590,13 @@ static void amd_pmc_dump_registers(struct amd_pmc_dev *dev) } value = amd_pmc_reg_read(dev, response); - dev_dbg(dev->dev, "AMD_PMC_REGISTER_RESPONSE:%x\n", value); + dev_dbg(dev->dev, "AMD_%s_REGISTER_RESPONSE:%x\n", dev->msg_port ? "S2D" : "PMC", value); value = amd_pmc_reg_read(dev, argument); - dev_dbg(dev->dev, "AMD_PMC_REGISTER_ARGUMENT:%x\n", value); + dev_dbg(dev->dev, "AMD_%s_REGISTER_ARGUMENT:%x\n", dev->msg_port ? "S2D" : "PMC", value); value = amd_pmc_reg_read(dev, message); - dev_dbg(dev->dev, "AMD_PMC_REGISTER_MESSAGE:%x\n", value); + dev_dbg(dev->dev, "AMD_%s_REGISTER_MESSAGE:%x\n", dev->msg_port ? "S2D" : "PMC", value); } static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool ret) @@ -653,6 +688,33 @@ 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 amd_pmc_verify_czn_rtc(struct amd_pmc_dev *pdev, u32 *arg) { struct rtc_device *rtc_device; @@ -715,8 +777,8 @@ static void amd_pmc_s2idle_prepare(void) /* Reset and Start SMU logging - to monitor the s0i3 stats */ amd_pmc_setup_smu_logging(pdev); - /* Activate CZN specific RTC functionality */ - if (pdev->cpu_id == AMD_CPU_ID_CZN) { + /* Activate CZN specific platform bug workarounds */ + if (pdev->cpu_id == AMD_CPU_ID_CZN && !disable_workarounds) { rc = amd_pmc_verify_czn_rtc(pdev, &arg); if (rc) { dev_err(pdev->dev, "failed to set RTC: %d\n", rc); @@ -782,6 +844,25 @@ static struct acpi_s2idle_dev_ops amd_pmc_s2idle_dev_ops = { .check = amd_pmc_s2idle_check, .restore = amd_pmc_s2idle_restore, }; + +static int __maybe_unused amd_pmc_suspend_handler(struct device *dev) +{ + struct amd_pmc_dev *pdev = dev_get_drvdata(dev); + + if (pdev->cpu_id == AMD_CPU_ID_CZN && !disable_workarounds) { + int rc = amd_pmc_czn_wa_irq1(pdev); + + if (rc) { + dev_err(pdev->dev, "failed to adjust keyboard wakeup: %d\n", rc); + return rc; + } + } + + return 0; +} + +static SIMPLE_DEV_PM_OPS(amd_pmc_pm, amd_pmc_suspend_handler, NULL); + #endif static const struct pci_device_id pmc_pci_ids[] = { @@ -980,6 +1061,9 @@ static struct platform_driver amd_pmc_driver = { .name = "amd_pmc", .acpi_match_table = amd_pmc_acpi_ids, .dev_groups = pmc_groups, +#ifdef CONFIG_SUSPEND + .pm = &amd_pmc_pm, +#endif }, .probe = amd_pmc_probe, .remove = amd_pmc_remove, |