diff options
| author | Dmitry Torokhov <[email protected]> | 2023-05-01 15:20:08 -0700 | 
|---|---|---|
| committer | Dmitry Torokhov <[email protected]> | 2023-05-01 15:20:08 -0700 | 
| commit | 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e (patch) | |
| tree | d57f3a63479a07b4e0cece029886e76e04feb984 /drivers/platform/x86/amd/pmc.c | |
| parent | 5dc63e56a9cf8df0b59c234a505a1653f1bdf885 (diff) | |
| parent | 53bea86b5712c7491bb3dae12e271666df0a308c (diff) | |
Merge branch 'next' into for-linus
Prepare input updates for 6.4 merge window.
Diffstat (limited to 'drivers/platform/x86/amd/pmc.c')
| -rw-r--r-- | drivers/platform/x86/amd/pmc.c | 120 | 
1 files changed, 96 insertions, 24 deletions
diff --git a/drivers/platform/x86/amd/pmc.c b/drivers/platform/x86/amd/pmc.c index 439d282aafd1..2edaae04a691 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,12 +164,14 @@ 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); -#ifdef CONFIG_SUSPEND  static int amd_pmc_write_stb(struct amd_pmc_dev *dev, u32 data); -#endif  static inline u32 amd_pmc_reg_read(struct amd_pmc_dev *dev, int reg_offset)  { @@ -241,13 +247,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; @@ -351,7 +384,6 @@ static int get_metrics_table(struct amd_pmc_dev *pdev, struct smu_metrics *table  	return 0;  } -#ifdef CONFIG_SUSPEND  static void amd_pmc_validate_deepest(struct amd_pmc_dev *pdev)  {  	struct smu_metrics table; @@ -365,7 +397,6 @@ static void amd_pmc_validate_deepest(struct amd_pmc_dev *pdev)  		dev_dbg(pdev->dev, "Last suspend in deepest state for %lluus\n",  			 table.timein_s0i3_lastcapture);  } -#endif  static int amd_pmc_get_smu_version(struct amd_pmc_dev *dev)  { @@ -555,13 +586,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) @@ -638,7 +669,6 @@ out_unlock:  	return rc;  } -#ifdef CONFIG_SUSPEND  static int amd_pmc_get_os_hint(struct amd_pmc_dev *dev)  {  	switch (dev->cpu_id) { @@ -653,6 +683,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 +772,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,7 +839,24 @@ static struct acpi_s2idle_dev_ops amd_pmc_s2idle_dev_ops = {  	.check = amd_pmc_s2idle_check,  	.restore = amd_pmc_s2idle_restore,  }; -#endif + +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 DEFINE_SIMPLE_DEV_PM_OPS(amd_pmc_pm, amd_pmc_suspend_handler, NULL);  static const struct pci_device_id pmc_pci_ids[] = {  	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_PS) }, @@ -824,7 +898,6 @@ static int amd_pmc_s2d_init(struct amd_pmc_dev *dev)  	return 0;  } -#ifdef CONFIG_SUSPEND  static int amd_pmc_write_stb(struct amd_pmc_dev *dev, u32 data)  {  	int err; @@ -845,7 +918,6 @@ static int amd_pmc_write_stb(struct amd_pmc_dev *dev, u32 data)  	return 0;  } -#endif  static int amd_pmc_read_stb(struct amd_pmc_dev *dev, u32 *buf)  { @@ -932,15 +1004,15 @@ static int amd_pmc_probe(struct platform_device *pdev)  	if (enable_stb && (dev->cpu_id == AMD_CPU_ID_YC || dev->cpu_id == AMD_CPU_ID_CB)) {  		err = amd_pmc_s2d_init(dev);  		if (err) -			return err; +			goto err_pci_dev_put;  	}  	platform_set_drvdata(pdev, dev); -#ifdef CONFIG_SUSPEND -	err = acpi_register_lps0_dev(&amd_pmc_s2idle_dev_ops); -	if (err) -		dev_warn(dev->dev, "failed to register LPS0 sleep handler, expect increased power consumption\n"); -#endif +	if (IS_ENABLED(CONFIG_SUSPEND)) { +		err = acpi_register_lps0_dev(&amd_pmc_s2idle_dev_ops); +		if (err) +			dev_warn(dev->dev, "failed to register LPS0 sleep handler, expect increased power consumption\n"); +	}  	amd_pmc_dbgfs_register(dev);  	return 0; @@ -954,9 +1026,8 @@ static int amd_pmc_remove(struct platform_device *pdev)  {  	struct amd_pmc_dev *dev = platform_get_drvdata(pdev); -#ifdef CONFIG_SUSPEND -	acpi_unregister_lps0_dev(&amd_pmc_s2idle_dev_ops); -#endif +	if (IS_ENABLED(CONFIG_SUSPEND)) +		acpi_unregister_lps0_dev(&amd_pmc_s2idle_dev_ops);  	amd_pmc_dbgfs_unregister(dev);  	pci_dev_put(dev->rdev);  	mutex_destroy(&dev->lock); @@ -980,6 +1051,7 @@ static struct platform_driver amd_pmc_driver = {  		.name = "amd_pmc",  		.acpi_match_table = amd_pmc_acpi_ids,  		.dev_groups = pmc_groups, +		.pm = pm_sleep_ptr(&amd_pmc_pm),  	},  	.probe = amd_pmc_probe,  	.remove = amd_pmc_remove,  |