diff options
Diffstat (limited to 'drivers/platform/x86/amd')
| -rw-r--r-- | drivers/platform/x86/amd/Kconfig | 16 | ||||
| -rw-r--r-- | drivers/platform/x86/amd/Makefile | 3 | ||||
| -rw-r--r-- | drivers/platform/x86/amd/hsmp.c | 241 | ||||
| -rw-r--r-- | drivers/platform/x86/amd/pmc/Kconfig | 20 | ||||
| -rw-r--r-- | drivers/platform/x86/amd/pmc/Makefile | 8 | ||||
| -rw-r--r-- | drivers/platform/x86/amd/pmc/pmc-quirks.c (renamed from drivers/platform/x86/amd/pmc-quirks.c) | 93 | ||||
| -rw-r--r-- | drivers/platform/x86/amd/pmc/pmc.c (renamed from drivers/platform/x86/amd/pmc.c) | 152 | ||||
| -rw-r--r-- | drivers/platform/x86/amd/pmc/pmc.h (renamed from drivers/platform/x86/amd/pmc.h) | 12 | ||||
| -rw-r--r-- | drivers/platform/x86/amd/pmf/cnqf.c | 5 | ||||
| -rw-r--r-- | drivers/platform/x86/amd/pmf/core.c | 3 | 
10 files changed, 438 insertions, 115 deletions
| diff --git a/drivers/platform/x86/amd/Kconfig b/drivers/platform/x86/amd/Kconfig index d9685aef0887..55f3a2fc6aec 100644 --- a/drivers/platform/x86/amd/Kconfig +++ b/drivers/platform/x86/amd/Kconfig @@ -4,21 +4,7 @@  #  source "drivers/platform/x86/amd/pmf/Kconfig" - -config AMD_PMC -	tristate "AMD SoC PMC driver" -	depends on ACPI && PCI && RTC_CLASS && AMD_NB -	select SERIO -	help -	  The driver provides support for AMD Power Management Controller -	  primarily responsible for S2Idle transactions that are driven from -	  a platform firmware running on SMU. This driver also provides a debug -	  mechanism to investigate the S2Idle transactions and failures. - -	  Say Y or M here if you have a notebook powered by AMD RYZEN CPU/APU. - -	  If you choose to compile this driver as a module the module will be -	  called amd-pmc. +source "drivers/platform/x86/amd/pmc/Kconfig"  config AMD_HSMP  	tristate "AMD HSMP Driver" diff --git a/drivers/platform/x86/amd/Makefile b/drivers/platform/x86/amd/Makefile index 65732f0a3913..f04932b7a7d1 100644 --- a/drivers/platform/x86/amd/Makefile +++ b/drivers/platform/x86/amd/Makefile @@ -4,8 +4,7 @@  # AMD x86 Platform-Specific Drivers  # -amd-pmc-y			:= pmc.o pmc-quirks.o -obj-$(CONFIG_AMD_PMC)		+= amd-pmc.o +obj-$(CONFIG_AMD_PMC)		+= pmc/  amd_hsmp-y			:= hsmp.o  obj-$(CONFIG_AMD_HSMP)		+= amd_hsmp.o  obj-$(CONFIG_AMD_PMF)		+= pmf/ diff --git a/drivers/platform/x86/amd/hsmp.c b/drivers/platform/x86/amd/hsmp.c index 31382ef52efb..b55d80e29139 100644 --- a/drivers/platform/x86/amd/hsmp.c +++ b/drivers/platform/x86/amd/hsmp.c @@ -20,7 +20,7 @@  #include <linux/semaphore.h>  #define DRIVER_NAME		"amd_hsmp" -#define DRIVER_VERSION		"1.0" +#define DRIVER_VERSION		"2.0"  /* HSMP Status / Error codes */  #define HSMP_STATUS_NOT_READY	0x00 @@ -47,9 +47,29 @@  #define HSMP_INDEX_REG		0xc4  #define HSMP_DATA_REG		0xc8 -static struct semaphore *hsmp_sem; +#define HSMP_CDEV_NAME		"hsmp_cdev" +#define HSMP_DEVNODE_NAME	"hsmp" +#define HSMP_METRICS_TABLE_NAME	"metrics_bin" -static struct miscdevice hsmp_device; +#define HSMP_ATTR_GRP_NAME_SIZE	10 + +struct hsmp_socket { +	struct bin_attribute hsmp_attr; +	void __iomem *metric_tbl_addr; +	struct semaphore hsmp_sem; +	char name[HSMP_ATTR_GRP_NAME_SIZE]; +	u16 sock_ind; +}; + +struct hsmp_plat_device { +	struct miscdevice hsmp_device; +	struct hsmp_socket *sock; +	struct device *dev; +	u32 proto_ver; +	u16 num_sockets; +}; + +static struct hsmp_plat_device plat_dev;  static int amd_hsmp_rdwr(struct pci_dev *root, u32 address,  			 u32 *value, bool write) @@ -188,6 +208,7 @@ static int validate_message(struct hsmp_message *msg)  int hsmp_send_message(struct hsmp_message *msg)  { +	struct hsmp_socket *sock = &plat_dev.sock[msg->sock_ind];  	struct amd_northbridge *nb;  	int ret; @@ -208,14 +229,13 @@ int hsmp_send_message(struct hsmp_message *msg)  	 * In SMP system timeout of 100 millisecs should  	 * be enough for the previous thread to finish the operation  	 */ -	ret = down_timeout(&hsmp_sem[msg->sock_ind], -			   msecs_to_jiffies(HSMP_MSG_TIMEOUT)); +	ret = down_timeout(&sock->hsmp_sem, msecs_to_jiffies(HSMP_MSG_TIMEOUT));  	if (ret < 0)  		return ret;  	ret = __hsmp_send_message(nb->root, msg); -	up(&hsmp_sem[msg->sock_ind]); +	up(&sock->hsmp_sem);  	return ret;  } @@ -317,32 +337,198 @@ static const struct file_operations hsmp_fops = {  	.compat_ioctl	= hsmp_ioctl,  }; +static ssize_t hsmp_metric_tbl_read(struct file *filp, struct kobject *kobj, +				    struct bin_attribute *bin_attr, char *buf, +				    loff_t off, size_t count) +{ +	struct hsmp_socket *sock = bin_attr->private; +	struct hsmp_message msg = { 0 }; +	int ret; + +	/* Do not support lseek(), reads entire metric table */ +	if (count < bin_attr->size) { +		dev_err(plat_dev.dev, "Wrong buffer size\n"); +		return -EINVAL; +	} + +	if (!sock) { +		dev_err(plat_dev.dev, "Failed to read attribute private data\n"); +		return -EINVAL; +	} + +	msg.msg_id	= HSMP_GET_METRIC_TABLE; +	msg.sock_ind	= sock->sock_ind; + +	ret = hsmp_send_message(&msg); +	if (ret) +		return ret; +	memcpy_fromio(buf, sock->metric_tbl_addr, bin_attr->size); + +	return bin_attr->size; +} + +static int hsmp_get_tbl_dram_base(u16 sock_ind) +{ +	struct hsmp_socket *sock = &plat_dev.sock[sock_ind]; +	struct hsmp_message msg = { 0 }; +	phys_addr_t dram_addr; +	int ret; + +	msg.sock_ind	= sock_ind; +	msg.response_sz	= hsmp_msg_desc_table[HSMP_GET_METRIC_TABLE_DRAM_ADDR].response_sz; +	msg.msg_id	= HSMP_GET_METRIC_TABLE_DRAM_ADDR; + +	ret = hsmp_send_message(&msg); +	if (ret) +		return ret; + +	/* +	 * calculate the metric table DRAM address from lower and upper 32 bits +	 * sent from SMU and ioremap it to virtual address. +	 */ +	dram_addr = msg.args[0] | ((u64)(msg.args[1]) << 32); +	if (!dram_addr) { +		dev_err(plat_dev.dev, "Invalid DRAM address for metric table\n"); +		return -ENOMEM; +	} +	sock->metric_tbl_addr = devm_ioremap(plat_dev.dev, dram_addr, +					     sizeof(struct hsmp_metric_table)); +	if (!sock->metric_tbl_addr) { +		dev_err(plat_dev.dev, "Failed to ioremap metric table addr\n"); +		return -ENOMEM; +	} +	return 0; +} + +static umode_t hsmp_is_sock_attr_visible(struct kobject *kobj, +					 struct bin_attribute *battr, int id) +{ +	if (plat_dev.proto_ver == HSMP_PROTO_VER6) +		return battr->attr.mode; +	else +		return 0; +} + +static int hsmp_init_metric_tbl_bin_attr(struct bin_attribute **hattrs, u16 sock_ind) +{ +	struct bin_attribute *hattr = &plat_dev.sock[sock_ind].hsmp_attr; + +	sysfs_bin_attr_init(hattr); +	hattr->attr.name	= HSMP_METRICS_TABLE_NAME; +	hattr->attr.mode	= 0444; +	hattr->read		= hsmp_metric_tbl_read; +	hattr->size		= sizeof(struct hsmp_metric_table); +	hattr->private		= &plat_dev.sock[sock_ind]; +	hattrs[0]		= hattr; + +	if (plat_dev.proto_ver == HSMP_PROTO_VER6) +		return (hsmp_get_tbl_dram_base(sock_ind)); +	else +		return 0; +} + +/* One bin sysfs for metrics table*/ +#define NUM_HSMP_ATTRS		1 + +static int hsmp_create_sysfs_interface(void) +{ +	const struct attribute_group **hsmp_attr_grps; +	struct bin_attribute **hsmp_bin_attrs; +	struct attribute_group *attr_grp; +	int ret; +	u16 i; + +	/* String formatting is currently limited to u8 sockets */ +	if (WARN_ON(plat_dev.num_sockets > U8_MAX)) +		return -ERANGE; + +	hsmp_attr_grps = devm_kzalloc(plat_dev.dev, sizeof(struct attribute_group *) * +				      (plat_dev.num_sockets + 1), GFP_KERNEL); +	if (!hsmp_attr_grps) +		return -ENOMEM; + +	/* Create a sysfs directory for each socket */ +	for (i = 0; i < plat_dev.num_sockets; i++) { +		attr_grp = devm_kzalloc(plat_dev.dev, sizeof(struct attribute_group), GFP_KERNEL); +		if (!attr_grp) +			return -ENOMEM; + +		snprintf(plat_dev.sock[i].name, HSMP_ATTR_GRP_NAME_SIZE, "socket%u", (u8)i); +		attr_grp->name = plat_dev.sock[i].name; + +		/* Null terminated list of attributes */ +		hsmp_bin_attrs = devm_kzalloc(plat_dev.dev, sizeof(struct bin_attribute *) * +					      (NUM_HSMP_ATTRS + 1), GFP_KERNEL); +		if (!hsmp_bin_attrs) +			return -ENOMEM; + +		attr_grp->bin_attrs		= hsmp_bin_attrs; +		attr_grp->is_bin_visible	= hsmp_is_sock_attr_visible; +		hsmp_attr_grps[i]		= attr_grp; + +		/* Now create the leaf nodes */ +		ret = hsmp_init_metric_tbl_bin_attr(hsmp_bin_attrs, i); +		if (ret) +			return ret; +	} +	return devm_device_add_groups(plat_dev.dev, hsmp_attr_grps); +} + +static int hsmp_cache_proto_ver(void) +{ +	struct hsmp_message msg = { 0 }; +	int ret; + +	msg.msg_id	= HSMP_GET_PROTO_VER; +	msg.sock_ind	= 0; +	msg.response_sz = hsmp_msg_desc_table[HSMP_GET_PROTO_VER].response_sz; + +	ret = hsmp_send_message(&msg); +	if (!ret) +		plat_dev.proto_ver = msg.args[0]; + +	return ret; +} +  static int hsmp_pltdrv_probe(struct platform_device *pdev)  { -	int i; +	int ret, i; -	hsmp_sem = devm_kzalloc(&pdev->dev, -				(amd_nb_num() * sizeof(struct semaphore)), -				GFP_KERNEL); -	if (!hsmp_sem) +	plat_dev.sock = devm_kzalloc(&pdev->dev, +				     (plat_dev.num_sockets * sizeof(struct hsmp_socket)), +				     GFP_KERNEL); +	if (!plat_dev.sock)  		return -ENOMEM; +	plat_dev.dev = &pdev->dev; + +	for (i = 0; i < plat_dev.num_sockets; i++) { +		sema_init(&plat_dev.sock[i].hsmp_sem, 1); +		plat_dev.sock[i].sock_ind = i; +	} -	for (i = 0; i < amd_nb_num(); i++) -		sema_init(&hsmp_sem[i], 1); +	plat_dev.hsmp_device.name	= HSMP_CDEV_NAME; +	plat_dev.hsmp_device.minor	= MISC_DYNAMIC_MINOR; +	plat_dev.hsmp_device.fops	= &hsmp_fops; +	plat_dev.hsmp_device.parent	= &pdev->dev; +	plat_dev.hsmp_device.nodename	= HSMP_DEVNODE_NAME; +	plat_dev.hsmp_device.mode	= 0644; + +	ret = hsmp_cache_proto_ver(); +	if (ret) { +		dev_err(plat_dev.dev, "Failed to read HSMP protocol version\n"); +		return ret; +	} -	hsmp_device.name	= "hsmp_cdev"; -	hsmp_device.minor	= MISC_DYNAMIC_MINOR; -	hsmp_device.fops	= &hsmp_fops; -	hsmp_device.parent	= &pdev->dev; -	hsmp_device.nodename	= "hsmp"; -	hsmp_device.mode	= 0644; +	ret = hsmp_create_sysfs_interface(); +	if (ret) +		dev_err(plat_dev.dev, "Failed to create HSMP sysfs interface\n"); -	return misc_register(&hsmp_device); +	return misc_register(&plat_dev.hsmp_device);  }  static void hsmp_pltdrv_remove(struct platform_device *pdev)  { -	misc_deregister(&hsmp_device); +	misc_deregister(&plat_dev.hsmp_device);  }  static struct platform_driver amd_hsmp_driver = { @@ -358,7 +544,6 @@ static struct platform_device *amd_hsmp_platdev;  static int __init hsmp_plt_init(void)  {  	int ret = -ENODEV; -	u16 num_sockets;  	int i;  	if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD || boot_cpu_data.x86 < 0x19) { @@ -371,18 +556,18 @@ static int __init hsmp_plt_init(void)  	 * amd_nb_num() returns number of SMN/DF interfaces present in the system  	 * if we have N SMN/DF interfaces that ideally means N sockets  	 */ -	num_sockets = amd_nb_num(); -	if (num_sockets == 0) +	plat_dev.num_sockets = amd_nb_num(); +	if (plat_dev.num_sockets == 0)  		return ret;  	/* Test the hsmp interface on each socket */ -	for (i = 0; i < num_sockets; i++) { +	for (i = 0; i < plat_dev.num_sockets; i++) {  		ret = hsmp_test(i, 0xDEADBEEF);  		if (ret) { -			pr_err("HSMP is not supported on Fam:%x model:%x\n", +			pr_err("HSMP test message failed on Fam:%x model:%x\n",  			       boot_cpu_data.x86, boot_cpu_data.x86_model); -			pr_err("Or Is HSMP disabled in BIOS ?\n"); -			return -EOPNOTSUPP; +			pr_err("Is HSMP disabled in BIOS ?\n"); +			return ret;  		}  	} diff --git a/drivers/platform/x86/amd/pmc/Kconfig b/drivers/platform/x86/amd/pmc/Kconfig new file mode 100644 index 000000000000..883c0a95ac0c --- /dev/null +++ b/drivers/platform/x86/amd/pmc/Kconfig @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# AMD PMC Driver +# + +config AMD_PMC +	tristate "AMD SoC PMC driver" +	depends on ACPI && PCI && RTC_CLASS && AMD_NB +	depends on SUSPEND +	select SERIO +	help +	  The driver provides support for AMD Power Management Controller +	  primarily responsible for S2Idle transactions that are driven from +	  a platform firmware running on SMU. This driver also provides a debug +	  mechanism to investigate the S2Idle transactions and failures. + +	  Say Y or M here if you have a notebook powered by AMD RYZEN CPU/APU. + +	  If you choose to compile this driver as a module the module will be +	  called amd-pmc. diff --git a/drivers/platform/x86/amd/pmc/Makefile b/drivers/platform/x86/amd/pmc/Makefile new file mode 100644 index 000000000000..4aaa29d351c9 --- /dev/null +++ b/drivers/platform/x86/amd/pmc/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for linux/drivers/platform/x86/amd/pmc +# AMD Power Management Controller Driver +# + +amd-pmc-objs := pmc.o pmc-quirks.o +obj-$(CONFIG_AMD_PMC) += amd-pmc.o diff --git a/drivers/platform/x86/amd/pmc-quirks.c b/drivers/platform/x86/amd/pmc/pmc-quirks.c index ad702463a65d..b456370166b6 100644 --- a/drivers/platform/x86/amd/pmc-quirks.c +++ b/drivers/platform/x86/amd/pmc/pmc-quirks.c @@ -16,12 +16,17 @@  struct quirk_entry {  	u32 s2idle_bug_mmio; +	bool spurious_8042;  };  static struct quirk_entry quirk_s2idle_bug = {  	.s2idle_bug_mmio = 0xfed80380,  }; +static struct quirk_entry quirk_spurious_8042 = { +	.spurious_8042 = true, +}; +  static const struct dmi_system_id fwbug_list[] = {  	{  		.ident = "L14 Gen2 AMD", @@ -111,6 +116,79 @@ static const struct dmi_system_id fwbug_list[] = {  			DMI_MATCH(DMI_PRODUCT_NAME, "21A1"),  		}  	}, +	/* https://bugzilla.kernel.org/show_bug.cgi?id=218024 */ +	{ +		.ident = "V14 G4 AMN", +		.driver_data = &quirk_s2idle_bug, +		.matches = { +			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), +			DMI_MATCH(DMI_PRODUCT_NAME, "82YT"), +		} +	}, +	{ +		.ident = "V14 G4 AMN", +		.driver_data = &quirk_s2idle_bug, +		.matches = { +			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), +			DMI_MATCH(DMI_PRODUCT_NAME, "83GE"), +		} +	}, +	{ +		.ident = "V15 G4 AMN", +		.driver_data = &quirk_s2idle_bug, +		.matches = { +			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), +			DMI_MATCH(DMI_PRODUCT_NAME, "82YU"), +		} +	}, +	{ +		.ident = "V15 G4 AMN", +		.driver_data = &quirk_s2idle_bug, +		.matches = { +			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), +			DMI_MATCH(DMI_PRODUCT_NAME, "83CQ"), +		} +	}, +	{ +		.ident = "IdeaPad 1 14AMN7", +		.driver_data = &quirk_s2idle_bug, +		.matches = { +			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), +			DMI_MATCH(DMI_PRODUCT_NAME, "82VF"), +		} +	}, +	{ +		.ident = "IdeaPad 1 15AMN7", +		.driver_data = &quirk_s2idle_bug, +		.matches = { +			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), +			DMI_MATCH(DMI_PRODUCT_NAME, "82VG"), +		} +	}, +	{ +		.ident = "IdeaPad 1 15AMN7", +		.driver_data = &quirk_s2idle_bug, +		.matches = { +			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), +			DMI_MATCH(DMI_PRODUCT_NAME, "82X5"), +		} +	}, +	{ +		.ident = "IdeaPad Slim 3 14AMN8", +		.driver_data = &quirk_s2idle_bug, +		.matches = { +			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), +			DMI_MATCH(DMI_PRODUCT_NAME, "82XN"), +		} +	}, +	{ +		.ident = "IdeaPad Slim 3 15AMN8", +		.driver_data = &quirk_s2idle_bug, +		.matches = { +			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), +			DMI_MATCH(DMI_PRODUCT_NAME, "82XQ"), +		} +	},  	/* https://gitlab.freedesktop.org/drm/amd/-/issues/2684 */  	{  		.ident = "HP Laptop 15s-eq2xxx", @@ -120,6 +198,16 @@ static const struct dmi_system_id fwbug_list[] = {  			DMI_MATCH(DMI_PRODUCT_NAME, "HP Laptop 15s-eq2xxx"),  		}  	}, +	/* https://community.frame.work/t/tracking-framework-amd-ryzen-7040-series-lid-wakeup-behavior-feedback/39128 */ +	{ +		.ident = "Framework Laptop 13 (Phoenix)", +		.driver_data = &quirk_spurious_8042, +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "Framework"), +			DMI_MATCH(DMI_PRODUCT_NAME, "Laptop 13 (AMD Ryzen 7040Series)"), +			DMI_MATCH(DMI_BIOS_VERSION, "03.03"), +		} +	},  	{}  }; @@ -162,6 +250,9 @@ void amd_pmc_quirks_init(struct amd_pmc_dev *dev)  {  	const struct dmi_system_id *dmi_id; +	if (dev->cpu_id == AMD_CPU_ID_CZN) +		dev->disable_8042_wakeup = true; +  	dmi_id = dmi_first_match(fwbug_list);  	if (!dmi_id)  		return; @@ -169,4 +260,6 @@ void amd_pmc_quirks_init(struct amd_pmc_dev *dev)  	if (dev->quirks->s2idle_bug_mmio)  		pr_info("Using s2idle quirk to avoid %s platform firmware bug\n",  			dmi_id->ident); +	if (dev->quirks->spurious_8042) +		dev->disable_8042_wakeup = true;  } diff --git a/drivers/platform/x86/amd/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index c1e788b67a74..864c8cc2f8a3 100644 --- a/drivers/platform/x86/amd/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -52,9 +52,13 @@  #define AMD_S2D_REGISTER_ARGUMENT	0xA88  /* STB Spill to DRAM Parameters */ -#define S2D_TELEMETRY_BYTES_MAX		0x100000 +#define S2D_TELEMETRY_BYTES_MAX		0x100000U +#define S2D_RSVD_RAM_SPACE		0x100000  #define S2D_TELEMETRY_DRAMBYTES_MAX	0x1000000 +/* STB Spill to DRAM Message Definition */ +#define STB_FORCE_FLUSH_DATA		0xCF +  /* Base address of SMU for mapping physical address to virtual address */  #define AMD_PMC_MAPPING_SIZE		0x01000  #define AMD_PMC_BASE_ADDR_OFFSET	0x10000 @@ -87,16 +91,6 @@  #define SMU_MSG_LOG_RESET		0x07  #define SMU_MSG_LOG_DUMP_DATA		0x08  #define SMU_MSG_GET_SUP_CONSTRAINTS	0x09 -/* List of supported CPU ids */ -#define AMD_CPU_ID_RV			0x15D0 -#define AMD_CPU_ID_RN			0x1630 -#define AMD_CPU_ID_PCO			AMD_CPU_ID_RV -#define AMD_CPU_ID_CZN			AMD_CPU_ID_RN -#define AMD_CPU_ID_YC			0x14B5 -#define AMD_CPU_ID_CB			0x14D8 -#define AMD_CPU_ID_PS			0x14E8 -#define AMD_CPU_ID_SP			0x14A4 -#define PCI_DEVICE_ID_AMD_1AH_M20H_ROOT 0x1507  #define PMC_MSG_DELAY_MIN_US		50  #define RESPONSE_REGISTER_LOOP_MAX	20000 @@ -119,6 +113,11 @@ enum s2d_arg {  	S2D_DRAM_SIZE,  }; +struct amd_pmc_stb_v2_data { +	size_t size; +	u8 data[] __counted_by(size); +}; +  struct amd_pmc_bit_map {  	const char *name;  	u32 bit_mask; @@ -157,6 +156,10 @@ static bool disable_workarounds;  module_param(disable_workarounds, bool, 0644);  MODULE_PARM_DESC(disable_workarounds, "Disable workarounds for platform bugs"); +static bool dump_custom_stb; +module_param(dump_custom_stb, bool, 0644); +MODULE_PARM_DESC(dump_custom_stb, "Enable to dump full STB buffer"); +  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); @@ -233,10 +236,30 @@ static const struct file_operations amd_pmc_stb_debugfs_fops = {  	.release = amd_pmc_stb_debugfs_release,  }; +/* Enhanced STB Firmware Reporting Mechanism */ +static int amd_pmc_stb_handle_efr(struct file *filp) +{ +	struct amd_pmc_dev *dev = filp->f_inode->i_private; +	struct amd_pmc_stb_v2_data *stb_data_arr; +	u32 fsize; + +	fsize = dev->dram_size - S2D_RSVD_RAM_SPACE; +	stb_data_arr = kmalloc(struct_size(stb_data_arr, data, fsize), GFP_KERNEL); +	if (!stb_data_arr) +		return -ENOMEM; + +	stb_data_arr->size = fsize; +	memcpy_fromio(stb_data_arr->data, dev->stb_virt_addr, fsize); +	filp->private_data = stb_data_arr; + +	return 0; +} +  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, fsize, num_samples, stb_rdptr_offset = 0; +	u32 fsize, num_samples, val, stb_rdptr_offset = 0; +	struct amd_pmc_stb_v2_data *stb_data_arr;  	int ret;  	/* Write dummy postcode while reading the STB buffer */ @@ -244,34 +267,55 @@ static int amd_pmc_stb_debugfs_open_v2(struct inode *inode, struct file *filp)  	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; -  	/* Spill to DRAM num_samples uses separate SMU message port */  	dev->msg_port = 1; +	ret = amd_pmc_send_cmd(dev, 0, &val, STB_FORCE_FLUSH_DATA, 1); +	if (ret) +		dev_dbg_once(dev->dev, "S2D force flush not supported: %d\n", ret); + +	/* +	 * We have a custom stb size and the PMFW is supposed to give +	 * the enhanced dram size. Note that we land here only for the +	 * platforms that support enhanced dram size reporting. +	 */ +	if (dump_custom_stb) +		return amd_pmc_stb_handle_efr(filp); +  	/* Get the num_samples to calculate the last push location */  	ret = amd_pmc_send_cmd(dev, S2D_NUM_SAMPLES, &num_samples, dev->s2d_msg_id, true);  	/* 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); -		kfree(buf);  		return ret;  	} -	/* Start capturing data from the last push location */ +	fsize = min(num_samples, S2D_TELEMETRY_BYTES_MAX); +	stb_data_arr = kmalloc(struct_size(stb_data_arr, data, fsize), GFP_KERNEL); +	if (!stb_data_arr) +		return -ENOMEM; + +	stb_data_arr->size = fsize; + +	/* +	 * Start capturing data from the last push location. +	 * This is for general cases, where the stb limits +	 * are meant for standard usage. +	 */  	if (num_samples > S2D_TELEMETRY_BYTES_MAX) { -		fsize  = S2D_TELEMETRY_BYTES_MAX; -		stb_rdptr_offset = num_samples - fsize; +		/* First read oldest data starting 1 behind last write till end of ringbuffer */ +		stb_rdptr_offset = num_samples % S2D_TELEMETRY_BYTES_MAX; +		fsize = S2D_TELEMETRY_BYTES_MAX - stb_rdptr_offset; + +		memcpy_fromio(stb_data_arr->data, dev->stb_virt_addr + stb_rdptr_offset, fsize); +		/* Second copy the newer samples from offset 0 - last write */ +		memcpy_fromio(stb_data_arr->data + fsize, dev->stb_virt_addr, stb_rdptr_offset);  	} else { -		fsize = num_samples; -		stb_rdptr_offset = 0; +		memcpy_fromio(stb_data_arr->data, dev->stb_virt_addr, fsize);  	} -	memcpy_fromio(buf, dev->stb_virt_addr + stb_rdptr_offset, fsize); -	filp->private_data = buf; +	filp->private_data = stb_data_arr;  	return 0;  } @@ -279,11 +323,9 @@ static int amd_pmc_stb_debugfs_open_v2(struct inode *inode, struct file *filp)  static ssize_t amd_pmc_stb_debugfs_read_v2(struct file *filp, char __user *buf, size_t size,  					   loff_t *pos)  { -	if (!filp->private_data) -		return -EINVAL; +	struct amd_pmc_stb_v2_data *data = filp->private_data; -	return simple_read_from_buffer(buf, size, pos, filp->private_data, -					S2D_TELEMETRY_BYTES_MAX); +	return simple_read_from_buffer(buf, size, pos, data->data, data->size);  }  static int amd_pmc_stb_debugfs_release_v2(struct inode *inode, struct file *filp) @@ -714,19 +756,22 @@ 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) +static int amd_pmc_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; -	} +	/* cezanne platform firmware has a fix in 64.66.0 */ +	if (pdev->cpu_id == AMD_CPU_ID_CZN) { +		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; +		if (pdev->major > 64 || (pdev->major == 64 && pdev->minor > 65)) +			return 0; +	}  	d = bus_find_device_by_name(&serio_bus, NULL, "serio0");  	if (!d) @@ -885,8 +930,8 @@ static int 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 (pdev->disable_8042_wakeup && !disable_workarounds) { +		int rc = amd_pmc_wa_irq1(pdev);  		if (rc) {  			dev_err(pdev->dev, "failed to adjust keyboard wakeup: %d\n", rc); @@ -912,33 +957,6 @@ static const struct pci_device_id pmc_pci_ids[] = {  	{ }  }; -static int amd_pmc_get_dram_size(struct amd_pmc_dev *dev) -{ -	int ret; - -	switch (dev->cpu_id) { -	case AMD_CPU_ID_YC: -		if (!(dev->major > 90 || (dev->major == 90 && dev->minor > 39))) { -			ret = -EINVAL; -			goto err_dram_size; -		} -		break; -	default: -		ret = -EINVAL; -		goto err_dram_size; -	} - -	ret = amd_pmc_send_cmd(dev, S2D_DRAM_SIZE, &dev->dram_size, dev->s2d_msg_id, true); -	if (ret || !dev->dram_size) -		goto err_dram_size; - -	return 0; - -err_dram_size: -	dev_err(dev->dev, "DRAM size command not supported for this platform\n"); -	return ret; -} -  static int amd_pmc_s2d_init(struct amd_pmc_dev *dev)  {  	u32 phys_addr_low, phys_addr_hi; @@ -957,8 +975,8 @@ static int amd_pmc_s2d_init(struct amd_pmc_dev *dev)  		return -EIO;  	/* Get DRAM size */ -	ret = amd_pmc_get_dram_size(dev); -	if (ret) +	ret = amd_pmc_send_cmd(dev, S2D_DRAM_SIZE, &dev->dram_size, dev->s2d_msg_id, true); +	if (ret || !dev->dram_size)  		dev->dram_size = S2D_TELEMETRY_DRAMBYTES_MAX;  	/* Get STB DRAM address */ diff --git a/drivers/platform/x86/amd/pmc.h b/drivers/platform/x86/amd/pmc/pmc.h index c27bd6a5642f..b4794f118739 100644 --- a/drivers/platform/x86/amd/pmc.h +++ b/drivers/platform/x86/amd/pmc/pmc.h @@ -36,9 +36,21 @@ struct amd_pmc_dev {  	struct mutex lock; /* generic mutex lock */  	struct dentry *dbgfs_dir;  	struct quirk_entry *quirks; +	bool disable_8042_wakeup;  };  void amd_pmc_process_restore_quirks(struct amd_pmc_dev *dev);  void amd_pmc_quirks_init(struct amd_pmc_dev *dev); +/* List of supported CPU ids */ +#define AMD_CPU_ID_RV			0x15D0 +#define AMD_CPU_ID_RN			0x1630 +#define AMD_CPU_ID_PCO			AMD_CPU_ID_RV +#define AMD_CPU_ID_CZN			AMD_CPU_ID_RN +#define AMD_CPU_ID_YC			0x14B5 +#define AMD_CPU_ID_CB			0x14D8 +#define AMD_CPU_ID_PS			0x14E8 +#define AMD_CPU_ID_SP			0x14A4 +#define PCI_DEVICE_ID_AMD_1AH_M20H_ROOT 0x1507 +  #endif /* PMC_H */ diff --git a/drivers/platform/x86/amd/pmf/cnqf.c b/drivers/platform/x86/amd/pmf/cnqf.c index 539b186e9027..bc8899e15c91 100644 --- a/drivers/platform/x86/amd/pmf/cnqf.c +++ b/drivers/platform/x86/amd/pmf/cnqf.c @@ -8,6 +8,7 @@   * Author: Shyam Sundar S K <[email protected]>   */ +#include <linux/string_choices.h>  #include <linux/workqueue.h>  #include "pmf.h" @@ -399,7 +400,7 @@ static ssize_t cnqf_enable_store(struct device *dev,  			amd_pmf_set_sps_power_limits(pdev);  	} -	dev_dbg(pdev->dev, "Received CnQF %s\n", input ? "on" : "off"); +	dev_dbg(pdev->dev, "Received CnQF %s\n", str_on_off(input));  	return count;  } @@ -409,7 +410,7 @@ static ssize_t cnqf_enable_show(struct device *dev,  {  	struct amd_pmf_dev *pdev = dev_get_drvdata(dev); -	return sysfs_emit(buf, "%s\n", pdev->cnqf_enabled ? "on" : "off"); +	return sysfs_emit(buf, "%s\n", str_on_off(pdev->cnqf_enabled));  }  static DEVICE_ATTR_RW(cnqf_enable); diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c index 57bf1a9f0e76..78ed3ee22555 100644 --- a/drivers/platform/x86/amd/pmf/core.c +++ b/drivers/platform/x86/amd/pmf/core.c @@ -324,7 +324,8 @@ static void amd_pmf_init_features(struct amd_pmf_dev *dev)  static void amd_pmf_deinit_features(struct amd_pmf_dev *dev)  { -	if (is_apmf_func_supported(dev, APMF_FUNC_STATIC_SLIDER_GRANULAR)) { +	if (is_apmf_func_supported(dev, APMF_FUNC_STATIC_SLIDER_GRANULAR) || +	    is_apmf_func_supported(dev, APMF_FUNC_OS_POWER_SLIDER_UPDATE)) {  		power_supply_unreg_notifier(&dev->pwr_src_notifier);  		amd_pmf_deinit_sps(dev);  	} |