xref: /openbmc/linux/drivers/platform/x86/amd/pmc/pmc.c (revision 5b235bbc)
192c2fb8fSShyam Sundar S K // SPDX-License-Identifier: GPL-2.0-or-later
292c2fb8fSShyam Sundar S K /*
392c2fb8fSShyam Sundar S K  * AMD SoC Power Management Controller Driver
492c2fb8fSShyam Sundar S K  *
592c2fb8fSShyam Sundar S K  * Copyright (c) 2020, Advanced Micro Devices, Inc.
692c2fb8fSShyam Sundar S K  * All Rights Reserved.
792c2fb8fSShyam Sundar S K  *
892c2fb8fSShyam Sundar S K  * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
992c2fb8fSShyam Sundar S K  */
1092c2fb8fSShyam Sundar S K 
1192c2fb8fSShyam Sundar S K #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1292c2fb8fSShyam Sundar S K 
1392c2fb8fSShyam Sundar S K #include <asm/amd_nb.h>
1492c2fb8fSShyam Sundar S K #include <linux/acpi.h>
1592c2fb8fSShyam Sundar S K #include <linux/bitfield.h>
1692c2fb8fSShyam Sundar S K #include <linux/bits.h>
1792c2fb8fSShyam Sundar S K #include <linux/debugfs.h>
1892c2fb8fSShyam Sundar S K #include <linux/delay.h>
1992c2fb8fSShyam Sundar S K #include <linux/io.h>
2092c2fb8fSShyam Sundar S K #include <linux/iopoll.h>
2192c2fb8fSShyam Sundar S K #include <linux/limits.h>
2292c2fb8fSShyam Sundar S K #include <linux/module.h>
2392c2fb8fSShyam Sundar S K #include <linux/pci.h>
2492c2fb8fSShyam Sundar S K #include <linux/platform_device.h>
2592c2fb8fSShyam Sundar S K #include <linux/rtc.h>
2692c2fb8fSShyam Sundar S K #include <linux/serio.h>
2792c2fb8fSShyam Sundar S K #include <linux/suspend.h>
2892c2fb8fSShyam Sundar S K #include <linux/seq_file.h>
2992c2fb8fSShyam Sundar S K #include <linux/uaccess.h>
3092c2fb8fSShyam Sundar S K 
3192c2fb8fSShyam Sundar S K #include "pmc.h"
3292c2fb8fSShyam Sundar S K 
3392c2fb8fSShyam Sundar S K /* SMU communication registers */
3492c2fb8fSShyam Sundar S K #define AMD_PMC_REGISTER_MESSAGE	0x538
3592c2fb8fSShyam Sundar S K #define AMD_PMC_REGISTER_RESPONSE	0x980
3692c2fb8fSShyam Sundar S K #define AMD_PMC_REGISTER_ARGUMENT	0x9BC
3792c2fb8fSShyam Sundar S K 
3892c2fb8fSShyam Sundar S K /* PMC Scratch Registers */
3992c2fb8fSShyam Sundar S K #define AMD_PMC_SCRATCH_REG_CZN		0x94
4092c2fb8fSShyam Sundar S K #define AMD_PMC_SCRATCH_REG_YC		0xD14
4192c2fb8fSShyam Sundar S K 
4292c2fb8fSShyam Sundar S K /* STB Registers */
4392c2fb8fSShyam Sundar S K #define AMD_PMC_STB_PMI_0		0x03E30600
4492c2fb8fSShyam Sundar S K #define AMD_PMC_STB_S2IDLE_PREPARE	0xC6000001
4592c2fb8fSShyam Sundar S K #define AMD_PMC_STB_S2IDLE_RESTORE	0xC6000002
4692c2fb8fSShyam Sundar S K #define AMD_PMC_STB_S2IDLE_CHECK	0xC6000003
4792c2fb8fSShyam Sundar S K #define AMD_PMC_STB_DUMMY_PC		0xC6000007
4892c2fb8fSShyam Sundar S K 
4992c2fb8fSShyam Sundar S K /* STB S2D(Spill to DRAM) has different message port offset */
5092c2fb8fSShyam Sundar S K #define AMD_S2D_REGISTER_MESSAGE	0xA20
5192c2fb8fSShyam Sundar S K #define AMD_S2D_REGISTER_RESPONSE	0xA80
5292c2fb8fSShyam Sundar S K #define AMD_S2D_REGISTER_ARGUMENT	0xA88
5392c2fb8fSShyam Sundar S K 
5492c2fb8fSShyam Sundar S K /* STB Spill to DRAM Parameters */
5592c2fb8fSShyam Sundar S K #define S2D_TELEMETRY_BYTES_MAX		0x100000
5692c2fb8fSShyam Sundar S K #define S2D_TELEMETRY_DRAMBYTES_MAX	0x1000000
5792c2fb8fSShyam Sundar S K 
5892c2fb8fSShyam Sundar S K /* Base address of SMU for mapping physical address to virtual address */
5992c2fb8fSShyam Sundar S K #define AMD_PMC_MAPPING_SIZE		0x01000
6092c2fb8fSShyam Sundar S K #define AMD_PMC_BASE_ADDR_OFFSET	0x10000
6192c2fb8fSShyam Sundar S K #define AMD_PMC_BASE_ADDR_LO		0x13B102E8
6292c2fb8fSShyam Sundar S K #define AMD_PMC_BASE_ADDR_HI		0x13B102EC
6392c2fb8fSShyam Sundar S K #define AMD_PMC_BASE_ADDR_LO_MASK	GENMASK(15, 0)
6492c2fb8fSShyam Sundar S K #define AMD_PMC_BASE_ADDR_HI_MASK	GENMASK(31, 20)
6592c2fb8fSShyam Sundar S K 
6692c2fb8fSShyam Sundar S K /* SMU Response Codes */
6792c2fb8fSShyam Sundar S K #define AMD_PMC_RESULT_OK                    0x01
6892c2fb8fSShyam Sundar S K #define AMD_PMC_RESULT_CMD_REJECT_BUSY       0xFC
6992c2fb8fSShyam Sundar S K #define AMD_PMC_RESULT_CMD_REJECT_PREREQ     0xFD
7092c2fb8fSShyam Sundar S K #define AMD_PMC_RESULT_CMD_UNKNOWN           0xFE
7192c2fb8fSShyam Sundar S K #define AMD_PMC_RESULT_FAILED                0xFF
7292c2fb8fSShyam Sundar S K 
7392c2fb8fSShyam Sundar S K /* FCH SSC Registers */
7492c2fb8fSShyam Sundar S K #define FCH_S0I3_ENTRY_TIME_L_OFFSET	0x30
7592c2fb8fSShyam Sundar S K #define FCH_S0I3_ENTRY_TIME_H_OFFSET	0x34
7692c2fb8fSShyam Sundar S K #define FCH_S0I3_EXIT_TIME_L_OFFSET	0x38
7792c2fb8fSShyam Sundar S K #define FCH_S0I3_EXIT_TIME_H_OFFSET	0x3C
7892c2fb8fSShyam Sundar S K #define FCH_SSC_MAPPING_SIZE		0x800
7992c2fb8fSShyam Sundar S K #define FCH_BASE_PHY_ADDR_LOW		0xFED81100
8092c2fb8fSShyam Sundar S K #define FCH_BASE_PHY_ADDR_HIGH		0x00000000
8192c2fb8fSShyam Sundar S K 
8292c2fb8fSShyam Sundar S K /* SMU Message Definations */
8392c2fb8fSShyam Sundar S K #define SMU_MSG_GETSMUVERSION		0x02
8492c2fb8fSShyam Sundar S K #define SMU_MSG_LOG_GETDRAM_ADDR_HI	0x04
8592c2fb8fSShyam Sundar S K #define SMU_MSG_LOG_GETDRAM_ADDR_LO	0x05
8692c2fb8fSShyam Sundar S K #define SMU_MSG_LOG_START		0x06
8792c2fb8fSShyam Sundar S K #define SMU_MSG_LOG_RESET		0x07
8892c2fb8fSShyam Sundar S K #define SMU_MSG_LOG_DUMP_DATA		0x08
8992c2fb8fSShyam Sundar S K #define SMU_MSG_GET_SUP_CONSTRAINTS	0x09
9092c2fb8fSShyam Sundar S K 
9192c2fb8fSShyam Sundar S K #define PMC_MSG_DELAY_MIN_US		50
9292c2fb8fSShyam Sundar S K #define RESPONSE_REGISTER_LOOP_MAX	20000
9392c2fb8fSShyam Sundar S K 
9492c2fb8fSShyam Sundar S K #define DELAY_MIN_US		2000
9592c2fb8fSShyam Sundar S K #define DELAY_MAX_US		3000
9692c2fb8fSShyam Sundar S K #define FIFO_SIZE		4096
9792c2fb8fSShyam Sundar S K 
9892c2fb8fSShyam Sundar S K enum amd_pmc_def {
9992c2fb8fSShyam Sundar S K 	MSG_TEST = 0x01,
10092c2fb8fSShyam Sundar S K 	MSG_OS_HINT_PCO,
10192c2fb8fSShyam Sundar S K 	MSG_OS_HINT_RN,
10292c2fb8fSShyam Sundar S K };
10392c2fb8fSShyam Sundar S K 
10492c2fb8fSShyam Sundar S K enum s2d_arg {
10592c2fb8fSShyam Sundar S K 	S2D_TELEMETRY_SIZE = 0x01,
10692c2fb8fSShyam Sundar S K 	S2D_PHYS_ADDR_LOW,
10792c2fb8fSShyam Sundar S K 	S2D_PHYS_ADDR_HIGH,
10892c2fb8fSShyam Sundar S K 	S2D_NUM_SAMPLES,
10992c2fb8fSShyam Sundar S K 	S2D_DRAM_SIZE,
11092c2fb8fSShyam Sundar S K };
11192c2fb8fSShyam Sundar S K 
11292c2fb8fSShyam Sundar S K struct amd_pmc_bit_map {
11392c2fb8fSShyam Sundar S K 	const char *name;
11492c2fb8fSShyam Sundar S K 	u32 bit_mask;
11592c2fb8fSShyam Sundar S K };
11692c2fb8fSShyam Sundar S K 
11792c2fb8fSShyam Sundar S K static const struct amd_pmc_bit_map soc15_ip_blk[] = {
11892c2fb8fSShyam Sundar S K 	{"DISPLAY",	BIT(0)},
11992c2fb8fSShyam Sundar S K 	{"CPU",		BIT(1)},
12092c2fb8fSShyam Sundar S K 	{"GFX",		BIT(2)},
12192c2fb8fSShyam Sundar S K 	{"VDD",		BIT(3)},
12292c2fb8fSShyam Sundar S K 	{"ACP",		BIT(4)},
12392c2fb8fSShyam Sundar S K 	{"VCN",		BIT(5)},
12492c2fb8fSShyam Sundar S K 	{"ISP",		BIT(6)},
12592c2fb8fSShyam Sundar S K 	{"NBIO",	BIT(7)},
12692c2fb8fSShyam Sundar S K 	{"DF",		BIT(8)},
12792c2fb8fSShyam Sundar S K 	{"USB3_0",	BIT(9)},
12892c2fb8fSShyam Sundar S K 	{"USB3_1",	BIT(10)},
12992c2fb8fSShyam Sundar S K 	{"LAPIC",	BIT(11)},
13092c2fb8fSShyam Sundar S K 	{"USB3_2",	BIT(12)},
13192c2fb8fSShyam Sundar S K 	{"USB3_3",	BIT(13)},
13292c2fb8fSShyam Sundar S K 	{"USB3_4",	BIT(14)},
13392c2fb8fSShyam Sundar S K 	{"USB4_0",	BIT(15)},
13492c2fb8fSShyam Sundar S K 	{"USB4_1",	BIT(16)},
13592c2fb8fSShyam Sundar S K 	{"MPM",		BIT(17)},
13692c2fb8fSShyam Sundar S K 	{"JPEG",	BIT(18)},
13792c2fb8fSShyam Sundar S K 	{"IPU",		BIT(19)},
13892c2fb8fSShyam Sundar S K 	{"UMSCH",	BIT(20)},
13992c2fb8fSShyam Sundar S K 	{}
14092c2fb8fSShyam Sundar S K };
14192c2fb8fSShyam Sundar S K 
14292c2fb8fSShyam Sundar S K static bool enable_stb;
14392c2fb8fSShyam Sundar S K module_param(enable_stb, bool, 0644);
14492c2fb8fSShyam Sundar S K MODULE_PARM_DESC(enable_stb, "Enable the STB debug mechanism");
14592c2fb8fSShyam Sundar S K 
14692c2fb8fSShyam Sundar S K static bool disable_workarounds;
14792c2fb8fSShyam Sundar S K module_param(disable_workarounds, bool, 0644);
14892c2fb8fSShyam Sundar S K MODULE_PARM_DESC(disable_workarounds, "Disable workarounds for platform bugs");
14992c2fb8fSShyam Sundar S K 
15092c2fb8fSShyam Sundar S K static struct amd_pmc_dev pmc;
15192c2fb8fSShyam Sundar S K static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool ret);
15292c2fb8fSShyam Sundar S K static int amd_pmc_read_stb(struct amd_pmc_dev *dev, u32 *buf);
15392c2fb8fSShyam Sundar S K static int amd_pmc_write_stb(struct amd_pmc_dev *dev, u32 data);
15492c2fb8fSShyam Sundar S K 
amd_pmc_reg_read(struct amd_pmc_dev * dev,int reg_offset)15592c2fb8fSShyam Sundar S K static inline u32 amd_pmc_reg_read(struct amd_pmc_dev *dev, int reg_offset)
15692c2fb8fSShyam Sundar S K {
15792c2fb8fSShyam Sundar S K 	return ioread32(dev->regbase + reg_offset);
15892c2fb8fSShyam Sundar S K }
15992c2fb8fSShyam Sundar S K 
amd_pmc_reg_write(struct amd_pmc_dev * dev,int reg_offset,u32 val)16092c2fb8fSShyam Sundar S K static inline void amd_pmc_reg_write(struct amd_pmc_dev *dev, int reg_offset, u32 val)
16192c2fb8fSShyam Sundar S K {
16292c2fb8fSShyam Sundar S K 	iowrite32(val, dev->regbase + reg_offset);
16392c2fb8fSShyam Sundar S K }
16492c2fb8fSShyam Sundar S K 
16592c2fb8fSShyam Sundar S K struct smu_metrics {
16692c2fb8fSShyam Sundar S K 	u32 table_version;
16792c2fb8fSShyam Sundar S K 	u32 hint_count;
16892c2fb8fSShyam Sundar S K 	u32 s0i3_last_entry_status;
16992c2fb8fSShyam Sundar S K 	u32 timein_s0i2;
17092c2fb8fSShyam Sundar S K 	u64 timeentering_s0i3_lastcapture;
17192c2fb8fSShyam Sundar S K 	u64 timeentering_s0i3_totaltime;
17292c2fb8fSShyam Sundar S K 	u64 timeto_resume_to_os_lastcapture;
17392c2fb8fSShyam Sundar S K 	u64 timeto_resume_to_os_totaltime;
17492c2fb8fSShyam Sundar S K 	u64 timein_s0i3_lastcapture;
17592c2fb8fSShyam Sundar S K 	u64 timein_s0i3_totaltime;
17692c2fb8fSShyam Sundar S K 	u64 timein_swdrips_lastcapture;
17792c2fb8fSShyam Sundar S K 	u64 timein_swdrips_totaltime;
17892c2fb8fSShyam Sundar S K 	u64 timecondition_notmet_lastcapture[32];
17992c2fb8fSShyam Sundar S K 	u64 timecondition_notmet_totaltime[32];
18092c2fb8fSShyam Sundar S K } __packed;
18192c2fb8fSShyam Sundar S K 
amd_pmc_stb_debugfs_open(struct inode * inode,struct file * filp)18292c2fb8fSShyam Sundar S K static int amd_pmc_stb_debugfs_open(struct inode *inode, struct file *filp)
18392c2fb8fSShyam Sundar S K {
18492c2fb8fSShyam Sundar S K 	struct amd_pmc_dev *dev = filp->f_inode->i_private;
18592c2fb8fSShyam Sundar S K 	u32 size = FIFO_SIZE * sizeof(u32);
18692c2fb8fSShyam Sundar S K 	u32 *buf;
18792c2fb8fSShyam Sundar S K 	int rc;
18892c2fb8fSShyam Sundar S K 
18992c2fb8fSShyam Sundar S K 	buf = kzalloc(size, GFP_KERNEL);
19092c2fb8fSShyam Sundar S K 	if (!buf)
19192c2fb8fSShyam Sundar S K 		return -ENOMEM;
19292c2fb8fSShyam Sundar S K 
19392c2fb8fSShyam Sundar S K 	rc = amd_pmc_read_stb(dev, buf);
19492c2fb8fSShyam Sundar S K 	if (rc) {
19592c2fb8fSShyam Sundar S K 		kfree(buf);
19692c2fb8fSShyam Sundar S K 		return rc;
19792c2fb8fSShyam Sundar S K 	}
19892c2fb8fSShyam Sundar S K 
19992c2fb8fSShyam Sundar S K 	filp->private_data = buf;
20092c2fb8fSShyam Sundar S K 	return rc;
20192c2fb8fSShyam Sundar S K }
20292c2fb8fSShyam Sundar S K 
amd_pmc_stb_debugfs_read(struct file * filp,char __user * buf,size_t size,loff_t * pos)20392c2fb8fSShyam Sundar S K static ssize_t amd_pmc_stb_debugfs_read(struct file *filp, char __user *buf, size_t size,
20492c2fb8fSShyam Sundar S K 					loff_t *pos)
20592c2fb8fSShyam Sundar S K {
20692c2fb8fSShyam Sundar S K 	if (!filp->private_data)
20792c2fb8fSShyam Sundar S K 		return -EINVAL;
20892c2fb8fSShyam Sundar S K 
20992c2fb8fSShyam Sundar S K 	return simple_read_from_buffer(buf, size, pos, filp->private_data,
21092c2fb8fSShyam Sundar S K 				       FIFO_SIZE * sizeof(u32));
21192c2fb8fSShyam Sundar S K }
21292c2fb8fSShyam Sundar S K 
amd_pmc_stb_debugfs_release(struct inode * inode,struct file * filp)21392c2fb8fSShyam Sundar S K static int amd_pmc_stb_debugfs_release(struct inode *inode, struct file *filp)
21492c2fb8fSShyam Sundar S K {
21592c2fb8fSShyam Sundar S K 	kfree(filp->private_data);
21692c2fb8fSShyam Sundar S K 	return 0;
21792c2fb8fSShyam Sundar S K }
21892c2fb8fSShyam Sundar S K 
21992c2fb8fSShyam Sundar S K static const struct file_operations amd_pmc_stb_debugfs_fops = {
22092c2fb8fSShyam Sundar S K 	.owner = THIS_MODULE,
22192c2fb8fSShyam Sundar S K 	.open = amd_pmc_stb_debugfs_open,
22292c2fb8fSShyam Sundar S K 	.read = amd_pmc_stb_debugfs_read,
22392c2fb8fSShyam Sundar S K 	.release = amd_pmc_stb_debugfs_release,
22492c2fb8fSShyam Sundar S K };
22592c2fb8fSShyam Sundar S K 
amd_pmc_stb_debugfs_open_v2(struct inode * inode,struct file * filp)22692c2fb8fSShyam Sundar S K static int amd_pmc_stb_debugfs_open_v2(struct inode *inode, struct file *filp)
22792c2fb8fSShyam Sundar S K {
22892c2fb8fSShyam Sundar S K 	struct amd_pmc_dev *dev = filp->f_inode->i_private;
22992c2fb8fSShyam Sundar S K 	u32 *buf, fsize, num_samples, stb_rdptr_offset = 0;
23092c2fb8fSShyam Sundar S K 	int ret;
23192c2fb8fSShyam Sundar S K 
23292c2fb8fSShyam Sundar S K 	/* Write dummy postcode while reading the STB buffer */
23392c2fb8fSShyam Sundar S K 	ret = amd_pmc_write_stb(dev, AMD_PMC_STB_DUMMY_PC);
23492c2fb8fSShyam Sundar S K 	if (ret)
23592c2fb8fSShyam Sundar S K 		dev_err(dev->dev, "error writing to STB: %d\n", ret);
23692c2fb8fSShyam Sundar S K 
23792c2fb8fSShyam Sundar S K 	buf = kzalloc(S2D_TELEMETRY_BYTES_MAX, GFP_KERNEL);
23892c2fb8fSShyam Sundar S K 	if (!buf)
23992c2fb8fSShyam Sundar S K 		return -ENOMEM;
24092c2fb8fSShyam Sundar S K 
24192c2fb8fSShyam Sundar S K 	/* Spill to DRAM num_samples uses separate SMU message port */
24292c2fb8fSShyam Sundar S K 	dev->msg_port = 1;
24392c2fb8fSShyam Sundar S K 
24492c2fb8fSShyam Sundar S K 	/* Get the num_samples to calculate the last push location */
24592c2fb8fSShyam Sundar S K 	ret = amd_pmc_send_cmd(dev, S2D_NUM_SAMPLES, &num_samples, dev->s2d_msg_id, true);
24692c2fb8fSShyam Sundar S K 	/* Clear msg_port for other SMU operation */
24792c2fb8fSShyam Sundar S K 	dev->msg_port = 0;
24892c2fb8fSShyam Sundar S K 	if (ret) {
24992c2fb8fSShyam Sundar S K 		dev_err(dev->dev, "error: S2D_NUM_SAMPLES not supported : %d\n", ret);
25092c2fb8fSShyam Sundar S K 		kfree(buf);
25192c2fb8fSShyam Sundar S K 		return ret;
25292c2fb8fSShyam Sundar S K 	}
25392c2fb8fSShyam Sundar S K 
25492c2fb8fSShyam Sundar S K 	/* Start capturing data from the last push location */
25592c2fb8fSShyam Sundar S K 	if (num_samples > S2D_TELEMETRY_BYTES_MAX) {
25692c2fb8fSShyam Sundar S K 		fsize  = S2D_TELEMETRY_BYTES_MAX;
25792c2fb8fSShyam Sundar S K 		stb_rdptr_offset = num_samples - fsize;
25892c2fb8fSShyam Sundar S K 	} else {
25992c2fb8fSShyam Sundar S K 		fsize = num_samples;
26092c2fb8fSShyam Sundar S K 		stb_rdptr_offset = 0;
26192c2fb8fSShyam Sundar S K 	}
26292c2fb8fSShyam Sundar S K 
26392c2fb8fSShyam Sundar S K 	memcpy_fromio(buf, dev->stb_virt_addr + stb_rdptr_offset, fsize);
26492c2fb8fSShyam Sundar S K 	filp->private_data = buf;
26592c2fb8fSShyam Sundar S K 
26692c2fb8fSShyam Sundar S K 	return 0;
26792c2fb8fSShyam Sundar S K }
26892c2fb8fSShyam Sundar S K 
amd_pmc_stb_debugfs_read_v2(struct file * filp,char __user * buf,size_t size,loff_t * pos)26992c2fb8fSShyam Sundar S K static ssize_t amd_pmc_stb_debugfs_read_v2(struct file *filp, char __user *buf, size_t size,
27092c2fb8fSShyam Sundar S K 					   loff_t *pos)
27192c2fb8fSShyam Sundar S K {
27292c2fb8fSShyam Sundar S K 	if (!filp->private_data)
27392c2fb8fSShyam Sundar S K 		return -EINVAL;
27492c2fb8fSShyam Sundar S K 
27592c2fb8fSShyam Sundar S K 	return simple_read_from_buffer(buf, size, pos, filp->private_data,
27692c2fb8fSShyam Sundar S K 					S2D_TELEMETRY_BYTES_MAX);
27792c2fb8fSShyam Sundar S K }
27892c2fb8fSShyam Sundar S K 
amd_pmc_stb_debugfs_release_v2(struct inode * inode,struct file * filp)27992c2fb8fSShyam Sundar S K static int amd_pmc_stb_debugfs_release_v2(struct inode *inode, struct file *filp)
28092c2fb8fSShyam Sundar S K {
28192c2fb8fSShyam Sundar S K 	kfree(filp->private_data);
28292c2fb8fSShyam Sundar S K 	return 0;
28392c2fb8fSShyam Sundar S K }
28492c2fb8fSShyam Sundar S K 
28592c2fb8fSShyam Sundar S K static const struct file_operations amd_pmc_stb_debugfs_fops_v2 = {
28692c2fb8fSShyam Sundar S K 	.owner = THIS_MODULE,
28792c2fb8fSShyam Sundar S K 	.open = amd_pmc_stb_debugfs_open_v2,
28892c2fb8fSShyam Sundar S K 	.read = amd_pmc_stb_debugfs_read_v2,
28992c2fb8fSShyam Sundar S K 	.release = amd_pmc_stb_debugfs_release_v2,
29092c2fb8fSShyam Sundar S K };
29192c2fb8fSShyam Sundar S K 
amd_pmc_get_ip_info(struct amd_pmc_dev * dev)29292c2fb8fSShyam Sundar S K static void amd_pmc_get_ip_info(struct amd_pmc_dev *dev)
29392c2fb8fSShyam Sundar S K {
29492c2fb8fSShyam Sundar S K 	switch (dev->cpu_id) {
29592c2fb8fSShyam Sundar S K 	case AMD_CPU_ID_PCO:
29692c2fb8fSShyam Sundar S K 	case AMD_CPU_ID_RN:
29792c2fb8fSShyam Sundar S K 	case AMD_CPU_ID_YC:
29892c2fb8fSShyam Sundar S K 	case AMD_CPU_ID_CB:
29992c2fb8fSShyam Sundar S K 		dev->num_ips = 12;
30092c2fb8fSShyam Sundar S K 		dev->s2d_msg_id = 0xBE;
30192c2fb8fSShyam Sundar S K 		break;
30292c2fb8fSShyam Sundar S K 	case AMD_CPU_ID_PS:
30392c2fb8fSShyam Sundar S K 		dev->num_ips = 21;
30492c2fb8fSShyam Sundar S K 		dev->s2d_msg_id = 0x85;
30592c2fb8fSShyam Sundar S K 		break;
30692c2fb8fSShyam Sundar S K 	}
30792c2fb8fSShyam Sundar S K }
30892c2fb8fSShyam Sundar S K 
amd_pmc_setup_smu_logging(struct amd_pmc_dev * dev)30992c2fb8fSShyam Sundar S K static int amd_pmc_setup_smu_logging(struct amd_pmc_dev *dev)
31092c2fb8fSShyam Sundar S K {
31192c2fb8fSShyam Sundar S K 	if (dev->cpu_id == AMD_CPU_ID_PCO) {
31292c2fb8fSShyam Sundar S K 		dev_warn_once(dev->dev, "SMU debugging info not supported on this platform\n");
31392c2fb8fSShyam Sundar S K 		return -EINVAL;
31492c2fb8fSShyam Sundar S K 	}
31592c2fb8fSShyam Sundar S K 
31692c2fb8fSShyam Sundar S K 	/* Get Active devices list from SMU */
31792c2fb8fSShyam Sundar S K 	if (!dev->active_ips)
31892c2fb8fSShyam Sundar S K 		amd_pmc_send_cmd(dev, 0, &dev->active_ips, SMU_MSG_GET_SUP_CONSTRAINTS, true);
31992c2fb8fSShyam Sundar S K 
32092c2fb8fSShyam Sundar S K 	/* Get dram address */
32192c2fb8fSShyam Sundar S K 	if (!dev->smu_virt_addr) {
32292c2fb8fSShyam Sundar S K 		u32 phys_addr_low, phys_addr_hi;
32392c2fb8fSShyam Sundar S K 		u64 smu_phys_addr;
32492c2fb8fSShyam Sundar S K 
32592c2fb8fSShyam Sundar S K 		amd_pmc_send_cmd(dev, 0, &phys_addr_low, SMU_MSG_LOG_GETDRAM_ADDR_LO, true);
32692c2fb8fSShyam Sundar S K 		amd_pmc_send_cmd(dev, 0, &phys_addr_hi, SMU_MSG_LOG_GETDRAM_ADDR_HI, true);
32792c2fb8fSShyam Sundar S K 		smu_phys_addr = ((u64)phys_addr_hi << 32 | phys_addr_low);
32892c2fb8fSShyam Sundar S K 
32992c2fb8fSShyam Sundar S K 		dev->smu_virt_addr = devm_ioremap(dev->dev, smu_phys_addr,
33092c2fb8fSShyam Sundar S K 						  sizeof(struct smu_metrics));
33192c2fb8fSShyam Sundar S K 		if (!dev->smu_virt_addr)
33292c2fb8fSShyam Sundar S K 			return -ENOMEM;
33392c2fb8fSShyam Sundar S K 	}
33492c2fb8fSShyam Sundar S K 
33592c2fb8fSShyam Sundar S K 	/* Start the logging */
33692c2fb8fSShyam Sundar S K 	amd_pmc_send_cmd(dev, 0, NULL, SMU_MSG_LOG_RESET, false);
33792c2fb8fSShyam Sundar S K 	amd_pmc_send_cmd(dev, 0, NULL, SMU_MSG_LOG_START, false);
33892c2fb8fSShyam Sundar S K 
33992c2fb8fSShyam Sundar S K 	return 0;
34092c2fb8fSShyam Sundar S K }
34192c2fb8fSShyam Sundar S K 
get_metrics_table(struct amd_pmc_dev * pdev,struct smu_metrics * table)34292c2fb8fSShyam Sundar S K static int get_metrics_table(struct amd_pmc_dev *pdev, struct smu_metrics *table)
34392c2fb8fSShyam Sundar S K {
34492c2fb8fSShyam Sundar S K 	if (!pdev->smu_virt_addr) {
34592c2fb8fSShyam Sundar S K 		int ret = amd_pmc_setup_smu_logging(pdev);
34692c2fb8fSShyam Sundar S K 
34792c2fb8fSShyam Sundar S K 		if (ret)
34892c2fb8fSShyam Sundar S K 			return ret;
34992c2fb8fSShyam Sundar S K 	}
35092c2fb8fSShyam Sundar S K 
35192c2fb8fSShyam Sundar S K 	if (pdev->cpu_id == AMD_CPU_ID_PCO)
35292c2fb8fSShyam Sundar S K 		return -ENODEV;
35392c2fb8fSShyam Sundar S K 	memcpy_fromio(table, pdev->smu_virt_addr, sizeof(struct smu_metrics));
35492c2fb8fSShyam Sundar S K 	return 0;
35592c2fb8fSShyam Sundar S K }
35692c2fb8fSShyam Sundar S K 
amd_pmc_validate_deepest(struct amd_pmc_dev * pdev)35792c2fb8fSShyam Sundar S K static void amd_pmc_validate_deepest(struct amd_pmc_dev *pdev)
35892c2fb8fSShyam Sundar S K {
35992c2fb8fSShyam Sundar S K 	struct smu_metrics table;
36092c2fb8fSShyam Sundar S K 
36192c2fb8fSShyam Sundar S K 	if (get_metrics_table(pdev, &table))
36292c2fb8fSShyam Sundar S K 		return;
36392c2fb8fSShyam Sundar S K 
36492c2fb8fSShyam Sundar S K 	if (!table.s0i3_last_entry_status)
36592c2fb8fSShyam Sundar S K 		dev_warn(pdev->dev, "Last suspend didn't reach deepest state\n");
36692c2fb8fSShyam Sundar S K 	pm_report_hw_sleep_time(table.s0i3_last_entry_status ?
36792c2fb8fSShyam Sundar S K 				table.timein_s0i3_lastcapture : 0);
36892c2fb8fSShyam Sundar S K }
36992c2fb8fSShyam Sundar S K 
amd_pmc_get_smu_version(struct amd_pmc_dev * dev)37092c2fb8fSShyam Sundar S K static int amd_pmc_get_smu_version(struct amd_pmc_dev *dev)
37192c2fb8fSShyam Sundar S K {
37292c2fb8fSShyam Sundar S K 	int rc;
37392c2fb8fSShyam Sundar S K 	u32 val;
37492c2fb8fSShyam Sundar S K 
37592c2fb8fSShyam Sundar S K 	if (dev->cpu_id == AMD_CPU_ID_PCO)
37692c2fb8fSShyam Sundar S K 		return -ENODEV;
37792c2fb8fSShyam Sundar S K 
37892c2fb8fSShyam Sundar S K 	rc = amd_pmc_send_cmd(dev, 0, &val, SMU_MSG_GETSMUVERSION, true);
37992c2fb8fSShyam Sundar S K 	if (rc)
38092c2fb8fSShyam Sundar S K 		return rc;
38192c2fb8fSShyam Sundar S K 
38292c2fb8fSShyam Sundar S K 	dev->smu_program = (val >> 24) & GENMASK(7, 0);
38392c2fb8fSShyam Sundar S K 	dev->major = (val >> 16) & GENMASK(7, 0);
38492c2fb8fSShyam Sundar S K 	dev->minor = (val >> 8) & GENMASK(7, 0);
38592c2fb8fSShyam Sundar S K 	dev->rev = (val >> 0) & GENMASK(7, 0);
38692c2fb8fSShyam Sundar S K 
38792c2fb8fSShyam Sundar S K 	dev_dbg(dev->dev, "SMU program %u version is %u.%u.%u\n",
38892c2fb8fSShyam Sundar S K 		dev->smu_program, dev->major, dev->minor, dev->rev);
38992c2fb8fSShyam Sundar S K 
39092c2fb8fSShyam Sundar S K 	return 0;
39192c2fb8fSShyam Sundar S K }
39292c2fb8fSShyam Sundar S K 
smu_fw_version_show(struct device * d,struct device_attribute * attr,char * buf)39392c2fb8fSShyam Sundar S K static ssize_t smu_fw_version_show(struct device *d, struct device_attribute *attr,
39492c2fb8fSShyam Sundar S K 				   char *buf)
39592c2fb8fSShyam Sundar S K {
39692c2fb8fSShyam Sundar S K 	struct amd_pmc_dev *dev = dev_get_drvdata(d);
39792c2fb8fSShyam Sundar S K 
39892c2fb8fSShyam Sundar S K 	if (!dev->major) {
39992c2fb8fSShyam Sundar S K 		int rc = amd_pmc_get_smu_version(dev);
40092c2fb8fSShyam Sundar S K 
40192c2fb8fSShyam Sundar S K 		if (rc)
40292c2fb8fSShyam Sundar S K 			return rc;
40392c2fb8fSShyam Sundar S K 	}
40492c2fb8fSShyam Sundar S K 	return sysfs_emit(buf, "%u.%u.%u\n", dev->major, dev->minor, dev->rev);
40592c2fb8fSShyam Sundar S K }
40692c2fb8fSShyam Sundar S K 
smu_program_show(struct device * d,struct device_attribute * attr,char * buf)40792c2fb8fSShyam Sundar S K static ssize_t smu_program_show(struct device *d, struct device_attribute *attr,
40892c2fb8fSShyam Sundar S K 				   char *buf)
40992c2fb8fSShyam Sundar S K {
41092c2fb8fSShyam Sundar S K 	struct amd_pmc_dev *dev = dev_get_drvdata(d);
41192c2fb8fSShyam Sundar S K 
41292c2fb8fSShyam Sundar S K 	if (!dev->major) {
41392c2fb8fSShyam Sundar S K 		int rc = amd_pmc_get_smu_version(dev);
41492c2fb8fSShyam Sundar S K 
41592c2fb8fSShyam Sundar S K 		if (rc)
41692c2fb8fSShyam Sundar S K 			return rc;
41792c2fb8fSShyam Sundar S K 	}
41892c2fb8fSShyam Sundar S K 	return sysfs_emit(buf, "%u\n", dev->smu_program);
41992c2fb8fSShyam Sundar S K }
42092c2fb8fSShyam Sundar S K 
42192c2fb8fSShyam Sundar S K static DEVICE_ATTR_RO(smu_fw_version);
42292c2fb8fSShyam Sundar S K static DEVICE_ATTR_RO(smu_program);
42392c2fb8fSShyam Sundar S K 
pmc_attr_is_visible(struct kobject * kobj,struct attribute * attr,int idx)42492c2fb8fSShyam Sundar S K static umode_t pmc_attr_is_visible(struct kobject *kobj, struct attribute *attr, int idx)
42592c2fb8fSShyam Sundar S K {
42692c2fb8fSShyam Sundar S K 	struct device *dev = kobj_to_dev(kobj);
42792c2fb8fSShyam Sundar S K 	struct amd_pmc_dev *pdev = dev_get_drvdata(dev);
42892c2fb8fSShyam Sundar S K 
42992c2fb8fSShyam Sundar S K 	if (pdev->cpu_id == AMD_CPU_ID_PCO)
43092c2fb8fSShyam Sundar S K 		return 0;
43192c2fb8fSShyam Sundar S K 	return 0444;
43292c2fb8fSShyam Sundar S K }
43392c2fb8fSShyam Sundar S K 
43492c2fb8fSShyam Sundar S K static struct attribute *pmc_attrs[] = {
43592c2fb8fSShyam Sundar S K 	&dev_attr_smu_fw_version.attr,
43692c2fb8fSShyam Sundar S K 	&dev_attr_smu_program.attr,
43792c2fb8fSShyam Sundar S K 	NULL,
43892c2fb8fSShyam Sundar S K };
43992c2fb8fSShyam Sundar S K 
44092c2fb8fSShyam Sundar S K static struct attribute_group pmc_attr_group = {
44192c2fb8fSShyam Sundar S K 	.attrs = pmc_attrs,
44292c2fb8fSShyam Sundar S K 	.is_visible = pmc_attr_is_visible,
44392c2fb8fSShyam Sundar S K };
44492c2fb8fSShyam Sundar S K 
44592c2fb8fSShyam Sundar S K static const struct attribute_group *pmc_groups[] = {
44692c2fb8fSShyam Sundar S K 	&pmc_attr_group,
44792c2fb8fSShyam Sundar S K 	NULL,
44892c2fb8fSShyam Sundar S K };
44992c2fb8fSShyam Sundar S K 
smu_fw_info_show(struct seq_file * s,void * unused)45092c2fb8fSShyam Sundar S K static int smu_fw_info_show(struct seq_file *s, void *unused)
45192c2fb8fSShyam Sundar S K {
45292c2fb8fSShyam Sundar S K 	struct amd_pmc_dev *dev = s->private;
45392c2fb8fSShyam Sundar S K 	struct smu_metrics table;
45492c2fb8fSShyam Sundar S K 	int idx;
45592c2fb8fSShyam Sundar S K 
45692c2fb8fSShyam Sundar S K 	if (get_metrics_table(dev, &table))
45792c2fb8fSShyam Sundar S K 		return -EINVAL;
45892c2fb8fSShyam Sundar S K 
45992c2fb8fSShyam Sundar S K 	seq_puts(s, "\n=== SMU Statistics ===\n");
46092c2fb8fSShyam Sundar S K 	seq_printf(s, "Table Version: %d\n", table.table_version);
46192c2fb8fSShyam Sundar S K 	seq_printf(s, "Hint Count: %d\n", table.hint_count);
46292c2fb8fSShyam Sundar S K 	seq_printf(s, "Last S0i3 Status: %s\n", table.s0i3_last_entry_status ? "Success" :
46392c2fb8fSShyam Sundar S K 		   "Unknown/Fail");
46492c2fb8fSShyam Sundar S K 	seq_printf(s, "Time (in us) to S0i3: %lld\n", table.timeentering_s0i3_lastcapture);
46592c2fb8fSShyam Sundar S K 	seq_printf(s, "Time (in us) in S0i3: %lld\n", table.timein_s0i3_lastcapture);
46692c2fb8fSShyam Sundar S K 	seq_printf(s, "Time (in us) to resume from S0i3: %lld\n",
46792c2fb8fSShyam Sundar S K 		   table.timeto_resume_to_os_lastcapture);
46892c2fb8fSShyam Sundar S K 
46992c2fb8fSShyam Sundar S K 	seq_puts(s, "\n=== Active time (in us) ===\n");
47092c2fb8fSShyam Sundar S K 	for (idx = 0 ; idx < dev->num_ips ; idx++) {
47192c2fb8fSShyam Sundar S K 		if (soc15_ip_blk[idx].bit_mask & dev->active_ips)
47292c2fb8fSShyam Sundar S K 			seq_printf(s, "%-8s : %lld\n", soc15_ip_blk[idx].name,
47392c2fb8fSShyam Sundar S K 				   table.timecondition_notmet_lastcapture[idx]);
47492c2fb8fSShyam Sundar S K 	}
47592c2fb8fSShyam Sundar S K 
47692c2fb8fSShyam Sundar S K 	return 0;
47792c2fb8fSShyam Sundar S K }
47892c2fb8fSShyam Sundar S K DEFINE_SHOW_ATTRIBUTE(smu_fw_info);
47992c2fb8fSShyam Sundar S K 
s0ix_stats_show(struct seq_file * s,void * unused)48092c2fb8fSShyam Sundar S K static int s0ix_stats_show(struct seq_file *s, void *unused)
48192c2fb8fSShyam Sundar S K {
48292c2fb8fSShyam Sundar S K 	struct amd_pmc_dev *dev = s->private;
48392c2fb8fSShyam Sundar S K 	u64 entry_time, exit_time, residency;
48492c2fb8fSShyam Sundar S K 
48592c2fb8fSShyam Sundar S K 	/* Use FCH registers to get the S0ix stats */
48692c2fb8fSShyam Sundar S K 	if (!dev->fch_virt_addr) {
48792c2fb8fSShyam Sundar S K 		u32 base_addr_lo = FCH_BASE_PHY_ADDR_LOW;
48892c2fb8fSShyam Sundar S K 		u32 base_addr_hi = FCH_BASE_PHY_ADDR_HIGH;
48992c2fb8fSShyam Sundar S K 		u64 fch_phys_addr = ((u64)base_addr_hi << 32 | base_addr_lo);
49092c2fb8fSShyam Sundar S K 
49192c2fb8fSShyam Sundar S K 		dev->fch_virt_addr = devm_ioremap(dev->dev, fch_phys_addr, FCH_SSC_MAPPING_SIZE);
49292c2fb8fSShyam Sundar S K 		if (!dev->fch_virt_addr)
49392c2fb8fSShyam Sundar S K 			return -ENOMEM;
49492c2fb8fSShyam Sundar S K 	}
49592c2fb8fSShyam Sundar S K 
49692c2fb8fSShyam Sundar S K 	entry_time = ioread32(dev->fch_virt_addr + FCH_S0I3_ENTRY_TIME_H_OFFSET);
49792c2fb8fSShyam Sundar S K 	entry_time = entry_time << 32 | ioread32(dev->fch_virt_addr + FCH_S0I3_ENTRY_TIME_L_OFFSET);
49892c2fb8fSShyam Sundar S K 
49992c2fb8fSShyam Sundar S K 	exit_time = ioread32(dev->fch_virt_addr + FCH_S0I3_EXIT_TIME_H_OFFSET);
50092c2fb8fSShyam Sundar S K 	exit_time = exit_time << 32 | ioread32(dev->fch_virt_addr + FCH_S0I3_EXIT_TIME_L_OFFSET);
50192c2fb8fSShyam Sundar S K 
50292c2fb8fSShyam Sundar S K 	/* It's in 48MHz. We need to convert it */
50392c2fb8fSShyam Sundar S K 	residency = exit_time - entry_time;
50492c2fb8fSShyam Sundar S K 	do_div(residency, 48);
50592c2fb8fSShyam Sundar S K 
50692c2fb8fSShyam Sundar S K 	seq_puts(s, "=== S0ix statistics ===\n");
50792c2fb8fSShyam Sundar S K 	seq_printf(s, "S0ix Entry Time: %lld\n", entry_time);
50892c2fb8fSShyam Sundar S K 	seq_printf(s, "S0ix Exit Time: %lld\n", exit_time);
50992c2fb8fSShyam Sundar S K 	seq_printf(s, "Residency Time: %lld\n", residency);
51092c2fb8fSShyam Sundar S K 
51192c2fb8fSShyam Sundar S K 	return 0;
51292c2fb8fSShyam Sundar S K }
51392c2fb8fSShyam Sundar S K DEFINE_SHOW_ATTRIBUTE(s0ix_stats);
51492c2fb8fSShyam Sundar S K 
amd_pmc_idlemask_read(struct amd_pmc_dev * pdev,struct device * dev,struct seq_file * s)51592c2fb8fSShyam Sundar S K static int amd_pmc_idlemask_read(struct amd_pmc_dev *pdev, struct device *dev,
51692c2fb8fSShyam Sundar S K 				 struct seq_file *s)
51792c2fb8fSShyam Sundar S K {
51892c2fb8fSShyam Sundar S K 	u32 val;
51992c2fb8fSShyam Sundar S K 	int rc;
52092c2fb8fSShyam Sundar S K 
52192c2fb8fSShyam Sundar S K 	switch (pdev->cpu_id) {
52292c2fb8fSShyam Sundar S K 	case AMD_CPU_ID_CZN:
52392c2fb8fSShyam Sundar S K 		/* we haven't yet read SMU version */
52492c2fb8fSShyam Sundar S K 		if (!pdev->major) {
52592c2fb8fSShyam Sundar S K 			rc = amd_pmc_get_smu_version(pdev);
52692c2fb8fSShyam Sundar S K 			if (rc)
52792c2fb8fSShyam Sundar S K 				return rc;
52892c2fb8fSShyam Sundar S K 		}
52992c2fb8fSShyam Sundar S K 		if (pdev->major > 56 || (pdev->major >= 55 && pdev->minor >= 37))
53092c2fb8fSShyam Sundar S K 			val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_CZN);
53192c2fb8fSShyam Sundar S K 		else
53292c2fb8fSShyam Sundar S K 			return -EINVAL;
53392c2fb8fSShyam Sundar S K 		break;
53492c2fb8fSShyam Sundar S K 	case AMD_CPU_ID_YC:
53592c2fb8fSShyam Sundar S K 	case AMD_CPU_ID_CB:
53692c2fb8fSShyam Sundar S K 	case AMD_CPU_ID_PS:
53792c2fb8fSShyam Sundar S K 		val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_YC);
53892c2fb8fSShyam Sundar S K 		break;
53992c2fb8fSShyam Sundar S K 	default:
54092c2fb8fSShyam Sundar S K 		return -EINVAL;
54192c2fb8fSShyam Sundar S K 	}
54292c2fb8fSShyam Sundar S K 
54392c2fb8fSShyam Sundar S K 	if (dev)
54492c2fb8fSShyam Sundar S K 		pm_pr_dbg("SMU idlemask s0i3: 0x%x\n", val);
54592c2fb8fSShyam Sundar S K 
54692c2fb8fSShyam Sundar S K 	if (s)
54792c2fb8fSShyam Sundar S K 		seq_printf(s, "SMU idlemask : 0x%x\n", val);
54892c2fb8fSShyam Sundar S K 
54992c2fb8fSShyam Sundar S K 	return 0;
55092c2fb8fSShyam Sundar S K }
55192c2fb8fSShyam Sundar S K 
amd_pmc_idlemask_show(struct seq_file * s,void * unused)55292c2fb8fSShyam Sundar S K static int amd_pmc_idlemask_show(struct seq_file *s, void *unused)
55392c2fb8fSShyam Sundar S K {
55492c2fb8fSShyam Sundar S K 	return amd_pmc_idlemask_read(s->private, NULL, s);
55592c2fb8fSShyam Sundar S K }
55692c2fb8fSShyam Sundar S K DEFINE_SHOW_ATTRIBUTE(amd_pmc_idlemask);
55792c2fb8fSShyam Sundar S K 
amd_pmc_dbgfs_unregister(struct amd_pmc_dev * dev)55892c2fb8fSShyam Sundar S K static void amd_pmc_dbgfs_unregister(struct amd_pmc_dev *dev)
55992c2fb8fSShyam Sundar S K {
56092c2fb8fSShyam Sundar S K 	debugfs_remove_recursive(dev->dbgfs_dir);
56192c2fb8fSShyam Sundar S K }
56292c2fb8fSShyam Sundar S K 
amd_pmc_is_stb_supported(struct amd_pmc_dev * dev)56392c2fb8fSShyam Sundar S K static bool amd_pmc_is_stb_supported(struct amd_pmc_dev *dev)
56492c2fb8fSShyam Sundar S K {
56592c2fb8fSShyam Sundar S K 	switch (dev->cpu_id) {
56692c2fb8fSShyam Sundar S K 	case AMD_CPU_ID_YC:
56792c2fb8fSShyam Sundar S K 	case AMD_CPU_ID_CB:
56892c2fb8fSShyam Sundar S K 	case AMD_CPU_ID_PS:
56992c2fb8fSShyam Sundar S K 		return true;
57092c2fb8fSShyam Sundar S K 	default:
57192c2fb8fSShyam Sundar S K 		return false;
57292c2fb8fSShyam Sundar S K 	}
57392c2fb8fSShyam Sundar S K }
57492c2fb8fSShyam Sundar S K 
amd_pmc_dbgfs_register(struct amd_pmc_dev * dev)57592c2fb8fSShyam Sundar S K static void amd_pmc_dbgfs_register(struct amd_pmc_dev *dev)
57692c2fb8fSShyam Sundar S K {
57792c2fb8fSShyam Sundar S K 	dev->dbgfs_dir = debugfs_create_dir("amd_pmc", NULL);
57892c2fb8fSShyam Sundar S K 	debugfs_create_file("smu_fw_info", 0644, dev->dbgfs_dir, dev,
57992c2fb8fSShyam Sundar S K 			    &smu_fw_info_fops);
58092c2fb8fSShyam Sundar S K 	debugfs_create_file("s0ix_stats", 0644, dev->dbgfs_dir, dev,
58192c2fb8fSShyam Sundar S K 			    &s0ix_stats_fops);
58292c2fb8fSShyam Sundar S K 	debugfs_create_file("amd_pmc_idlemask", 0644, dev->dbgfs_dir, dev,
58392c2fb8fSShyam Sundar S K 			    &amd_pmc_idlemask_fops);
58492c2fb8fSShyam Sundar S K 	/* Enable STB only when the module_param is set */
58592c2fb8fSShyam Sundar S K 	if (enable_stb) {
58692c2fb8fSShyam Sundar S K 		if (amd_pmc_is_stb_supported(dev))
58792c2fb8fSShyam Sundar S K 			debugfs_create_file("stb_read", 0644, dev->dbgfs_dir, dev,
58892c2fb8fSShyam Sundar S K 					    &amd_pmc_stb_debugfs_fops_v2);
58992c2fb8fSShyam Sundar S K 		else
59092c2fb8fSShyam Sundar S K 			debugfs_create_file("stb_read", 0644, dev->dbgfs_dir, dev,
59192c2fb8fSShyam Sundar S K 					    &amd_pmc_stb_debugfs_fops);
59292c2fb8fSShyam Sundar S K 	}
59392c2fb8fSShyam Sundar S K }
59492c2fb8fSShyam Sundar S K 
amd_pmc_dump_registers(struct amd_pmc_dev * dev)59592c2fb8fSShyam Sundar S K static void amd_pmc_dump_registers(struct amd_pmc_dev *dev)
59692c2fb8fSShyam Sundar S K {
59792c2fb8fSShyam Sundar S K 	u32 value, message, argument, response;
59892c2fb8fSShyam Sundar S K 
59992c2fb8fSShyam Sundar S K 	if (dev->msg_port) {
60092c2fb8fSShyam Sundar S K 		message = AMD_S2D_REGISTER_MESSAGE;
60192c2fb8fSShyam Sundar S K 		argument = AMD_S2D_REGISTER_ARGUMENT;
60292c2fb8fSShyam Sundar S K 		response = AMD_S2D_REGISTER_RESPONSE;
60392c2fb8fSShyam Sundar S K 	} else {
60492c2fb8fSShyam Sundar S K 		message = AMD_PMC_REGISTER_MESSAGE;
60592c2fb8fSShyam Sundar S K 		argument = AMD_PMC_REGISTER_ARGUMENT;
60692c2fb8fSShyam Sundar S K 		response = AMD_PMC_REGISTER_RESPONSE;
60792c2fb8fSShyam Sundar S K 	}
60892c2fb8fSShyam Sundar S K 
60992c2fb8fSShyam Sundar S K 	value = amd_pmc_reg_read(dev, response);
61092c2fb8fSShyam Sundar S K 	dev_dbg(dev->dev, "AMD_%s_REGISTER_RESPONSE:%x\n", dev->msg_port ? "S2D" : "PMC", value);
61192c2fb8fSShyam Sundar S K 
61292c2fb8fSShyam Sundar S K 	value = amd_pmc_reg_read(dev, argument);
61392c2fb8fSShyam Sundar S K 	dev_dbg(dev->dev, "AMD_%s_REGISTER_ARGUMENT:%x\n", dev->msg_port ? "S2D" : "PMC", value);
61492c2fb8fSShyam Sundar S K 
61592c2fb8fSShyam Sundar S K 	value = amd_pmc_reg_read(dev, message);
61692c2fb8fSShyam Sundar S K 	dev_dbg(dev->dev, "AMD_%s_REGISTER_MESSAGE:%x\n", dev->msg_port ? "S2D" : "PMC", value);
61792c2fb8fSShyam Sundar S K }
61892c2fb8fSShyam Sundar S K 
amd_pmc_send_cmd(struct amd_pmc_dev * dev,u32 arg,u32 * data,u8 msg,bool ret)61992c2fb8fSShyam Sundar S K static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool ret)
62092c2fb8fSShyam Sundar S K {
62192c2fb8fSShyam Sundar S K 	int rc;
62292c2fb8fSShyam Sundar S K 	u32 val, message, argument, response;
62392c2fb8fSShyam Sundar S K 
62492c2fb8fSShyam Sundar S K 	mutex_lock(&dev->lock);
62592c2fb8fSShyam Sundar S K 
62692c2fb8fSShyam Sundar S K 	if (dev->msg_port) {
62792c2fb8fSShyam Sundar S K 		message = AMD_S2D_REGISTER_MESSAGE;
62892c2fb8fSShyam Sundar S K 		argument = AMD_S2D_REGISTER_ARGUMENT;
62992c2fb8fSShyam Sundar S K 		response = AMD_S2D_REGISTER_RESPONSE;
63092c2fb8fSShyam Sundar S K 	} else {
63192c2fb8fSShyam Sundar S K 		message = AMD_PMC_REGISTER_MESSAGE;
63292c2fb8fSShyam Sundar S K 		argument = AMD_PMC_REGISTER_ARGUMENT;
63392c2fb8fSShyam Sundar S K 		response = AMD_PMC_REGISTER_RESPONSE;
63492c2fb8fSShyam Sundar S K 	}
63592c2fb8fSShyam Sundar S K 
63692c2fb8fSShyam Sundar S K 	/* Wait until we get a valid response */
63792c2fb8fSShyam Sundar S K 	rc = readx_poll_timeout(ioread32, dev->regbase + response,
63892c2fb8fSShyam Sundar S K 				val, val != 0, PMC_MSG_DELAY_MIN_US,
63992c2fb8fSShyam Sundar S K 				PMC_MSG_DELAY_MIN_US * RESPONSE_REGISTER_LOOP_MAX);
64092c2fb8fSShyam Sundar S K 	if (rc) {
64192c2fb8fSShyam Sundar S K 		dev_err(dev->dev, "failed to talk to SMU\n");
64292c2fb8fSShyam Sundar S K 		goto out_unlock;
64392c2fb8fSShyam Sundar S K 	}
64492c2fb8fSShyam Sundar S K 
64592c2fb8fSShyam Sundar S K 	/* Write zero to response register */
64692c2fb8fSShyam Sundar S K 	amd_pmc_reg_write(dev, response, 0);
64792c2fb8fSShyam Sundar S K 
64892c2fb8fSShyam Sundar S K 	/* Write argument into response register */
64992c2fb8fSShyam Sundar S K 	amd_pmc_reg_write(dev, argument, arg);
65092c2fb8fSShyam Sundar S K 
65192c2fb8fSShyam Sundar S K 	/* Write message ID to message ID register */
65292c2fb8fSShyam Sundar S K 	amd_pmc_reg_write(dev, message, msg);
65392c2fb8fSShyam Sundar S K 
65492c2fb8fSShyam Sundar S K 	/* Wait until we get a valid response */
65592c2fb8fSShyam Sundar S K 	rc = readx_poll_timeout(ioread32, dev->regbase + response,
65692c2fb8fSShyam Sundar S K 				val, val != 0, PMC_MSG_DELAY_MIN_US,
65792c2fb8fSShyam Sundar S K 				PMC_MSG_DELAY_MIN_US * RESPONSE_REGISTER_LOOP_MAX);
65892c2fb8fSShyam Sundar S K 	if (rc) {
65992c2fb8fSShyam Sundar S K 		dev_err(dev->dev, "SMU response timed out\n");
66092c2fb8fSShyam Sundar S K 		goto out_unlock;
66192c2fb8fSShyam Sundar S K 	}
66292c2fb8fSShyam Sundar S K 
66392c2fb8fSShyam Sundar S K 	switch (val) {
66492c2fb8fSShyam Sundar S K 	case AMD_PMC_RESULT_OK:
66592c2fb8fSShyam Sundar S K 		if (ret) {
66692c2fb8fSShyam Sundar S K 			/* PMFW may take longer time to return back the data */
66792c2fb8fSShyam Sundar S K 			usleep_range(DELAY_MIN_US, 10 * DELAY_MAX_US);
66892c2fb8fSShyam Sundar S K 			*data = amd_pmc_reg_read(dev, argument);
66992c2fb8fSShyam Sundar S K 		}
67092c2fb8fSShyam Sundar S K 		break;
67192c2fb8fSShyam Sundar S K 	case AMD_PMC_RESULT_CMD_REJECT_BUSY:
67292c2fb8fSShyam Sundar S K 		dev_err(dev->dev, "SMU not ready. err: 0x%x\n", val);
67392c2fb8fSShyam Sundar S K 		rc = -EBUSY;
67492c2fb8fSShyam Sundar S K 		goto out_unlock;
67592c2fb8fSShyam Sundar S K 	case AMD_PMC_RESULT_CMD_UNKNOWN:
67692c2fb8fSShyam Sundar S K 		dev_err(dev->dev, "SMU cmd unknown. err: 0x%x\n", val);
67792c2fb8fSShyam Sundar S K 		rc = -EINVAL;
67892c2fb8fSShyam Sundar S K 		goto out_unlock;
67992c2fb8fSShyam Sundar S K 	case AMD_PMC_RESULT_CMD_REJECT_PREREQ:
68092c2fb8fSShyam Sundar S K 	case AMD_PMC_RESULT_FAILED:
68192c2fb8fSShyam Sundar S K 	default:
68292c2fb8fSShyam Sundar S K 		dev_err(dev->dev, "SMU cmd failed. err: 0x%x\n", val);
68392c2fb8fSShyam Sundar S K 		rc = -EIO;
68492c2fb8fSShyam Sundar S K 		goto out_unlock;
68592c2fb8fSShyam Sundar S K 	}
68692c2fb8fSShyam Sundar S K 
68792c2fb8fSShyam Sundar S K out_unlock:
68892c2fb8fSShyam Sundar S K 	mutex_unlock(&dev->lock);
68992c2fb8fSShyam Sundar S K 	amd_pmc_dump_registers(dev);
69092c2fb8fSShyam Sundar S K 	return rc;
69192c2fb8fSShyam Sundar S K }
69292c2fb8fSShyam Sundar S K 
amd_pmc_get_os_hint(struct amd_pmc_dev * dev)69392c2fb8fSShyam Sundar S K static int amd_pmc_get_os_hint(struct amd_pmc_dev *dev)
69492c2fb8fSShyam Sundar S K {
69592c2fb8fSShyam Sundar S K 	switch (dev->cpu_id) {
69692c2fb8fSShyam Sundar S K 	case AMD_CPU_ID_PCO:
69792c2fb8fSShyam Sundar S K 		return MSG_OS_HINT_PCO;
69892c2fb8fSShyam Sundar S K 	case AMD_CPU_ID_RN:
69992c2fb8fSShyam Sundar S K 	case AMD_CPU_ID_YC:
70092c2fb8fSShyam Sundar S K 	case AMD_CPU_ID_CB:
70192c2fb8fSShyam Sundar S K 	case AMD_CPU_ID_PS:
70292c2fb8fSShyam Sundar S K 		return MSG_OS_HINT_RN;
70392c2fb8fSShyam Sundar S K 	}
70492c2fb8fSShyam Sundar S K 	return -EINVAL;
70592c2fb8fSShyam Sundar S K }
70692c2fb8fSShyam Sundar S K 
amd_pmc_wa_irq1(struct amd_pmc_dev * pdev)707a7edb661SMario Limonciello static int amd_pmc_wa_irq1(struct amd_pmc_dev *pdev)
70892c2fb8fSShyam Sundar S K {
70992c2fb8fSShyam Sundar S K 	struct device *d;
71092c2fb8fSShyam Sundar S K 	int rc;
71192c2fb8fSShyam Sundar S K 
712a7edb661SMario Limonciello 	/* cezanne platform firmware has a fix in 64.66.0 */
713a7edb661SMario Limonciello 	if (pdev->cpu_id == AMD_CPU_ID_CZN) {
71492c2fb8fSShyam Sundar S K 		if (!pdev->major) {
71592c2fb8fSShyam Sundar S K 			rc = amd_pmc_get_smu_version(pdev);
71692c2fb8fSShyam Sundar S K 			if (rc)
71792c2fb8fSShyam Sundar S K 				return rc;
71892c2fb8fSShyam Sundar S K 		}
71992c2fb8fSShyam Sundar S K 
72092c2fb8fSShyam Sundar S K 		if (pdev->major > 64 || (pdev->major == 64 && pdev->minor > 65))
72192c2fb8fSShyam Sundar S K 			return 0;
722a7edb661SMario Limonciello 	}
72392c2fb8fSShyam Sundar S K 
72492c2fb8fSShyam Sundar S K 	d = bus_find_device_by_name(&serio_bus, NULL, "serio0");
72592c2fb8fSShyam Sundar S K 	if (!d)
72692c2fb8fSShyam Sundar S K 		return 0;
72792c2fb8fSShyam Sundar S K 	if (device_may_wakeup(d)) {
72892c2fb8fSShyam Sundar S K 		dev_info_once(d, "Disabling IRQ1 wakeup source to avoid platform firmware bug\n");
72992c2fb8fSShyam Sundar S K 		disable_irq_wake(1);
73092c2fb8fSShyam Sundar S K 		device_set_wakeup_enable(d, false);
73192c2fb8fSShyam Sundar S K 	}
73292c2fb8fSShyam Sundar S K 	put_device(d);
73392c2fb8fSShyam Sundar S K 
73492c2fb8fSShyam Sundar S K 	return 0;
73592c2fb8fSShyam Sundar S K }
73692c2fb8fSShyam Sundar S K 
amd_pmc_verify_czn_rtc(struct amd_pmc_dev * pdev,u32 * arg)73792c2fb8fSShyam Sundar S K static int amd_pmc_verify_czn_rtc(struct amd_pmc_dev *pdev, u32 *arg)
73892c2fb8fSShyam Sundar S K {
73992c2fb8fSShyam Sundar S K 	struct rtc_device *rtc_device;
74092c2fb8fSShyam Sundar S K 	time64_t then, now, duration;
74192c2fb8fSShyam Sundar S K 	struct rtc_wkalrm alarm;
74292c2fb8fSShyam Sundar S K 	struct rtc_time tm;
74392c2fb8fSShyam Sundar S K 	int rc;
74492c2fb8fSShyam Sundar S K 
74592c2fb8fSShyam Sundar S K 	/* we haven't yet read SMU version */
74692c2fb8fSShyam Sundar S K 	if (!pdev->major) {
74792c2fb8fSShyam Sundar S K 		rc = amd_pmc_get_smu_version(pdev);
74892c2fb8fSShyam Sundar S K 		if (rc)
74992c2fb8fSShyam Sundar S K 			return rc;
75092c2fb8fSShyam Sundar S K 	}
75192c2fb8fSShyam Sundar S K 
75292c2fb8fSShyam Sundar S K 	if (pdev->major < 64 || (pdev->major == 64 && pdev->minor < 53))
75392c2fb8fSShyam Sundar S K 		return 0;
75492c2fb8fSShyam Sundar S K 
75592c2fb8fSShyam Sundar S K 	rtc_device = rtc_class_open("rtc0");
75692c2fb8fSShyam Sundar S K 	if (!rtc_device)
75792c2fb8fSShyam Sundar S K 		return 0;
75892c2fb8fSShyam Sundar S K 	rc = rtc_read_alarm(rtc_device, &alarm);
75992c2fb8fSShyam Sundar S K 	if (rc)
76092c2fb8fSShyam Sundar S K 		return rc;
76192c2fb8fSShyam Sundar S K 	if (!alarm.enabled) {
76292c2fb8fSShyam Sundar S K 		dev_dbg(pdev->dev, "alarm not enabled\n");
76392c2fb8fSShyam Sundar S K 		return 0;
76492c2fb8fSShyam Sundar S K 	}
76592c2fb8fSShyam Sundar S K 	rc = rtc_read_time(rtc_device, &tm);
76692c2fb8fSShyam Sundar S K 	if (rc)
76792c2fb8fSShyam Sundar S K 		return rc;
76892c2fb8fSShyam Sundar S K 	then = rtc_tm_to_time64(&alarm.time);
76992c2fb8fSShyam Sundar S K 	now = rtc_tm_to_time64(&tm);
77092c2fb8fSShyam Sundar S K 	duration = then-now;
77192c2fb8fSShyam Sundar S K 
77292c2fb8fSShyam Sundar S K 	/* in the past */
77392c2fb8fSShyam Sundar S K 	if (then < now)
77492c2fb8fSShyam Sundar S K 		return 0;
77592c2fb8fSShyam Sundar S K 
77692c2fb8fSShyam Sundar S K 	/* will be stored in upper 16 bits of s0i3 hint argument,
77792c2fb8fSShyam Sundar S K 	 * so timer wakeup from s0i3 is limited to ~18 hours or less
77892c2fb8fSShyam Sundar S K 	 */
77992c2fb8fSShyam Sundar S K 	if (duration <= 4 || duration > U16_MAX)
78092c2fb8fSShyam Sundar S K 		return -EINVAL;
78192c2fb8fSShyam Sundar S K 
78292c2fb8fSShyam Sundar S K 	*arg |= (duration << 16);
78392c2fb8fSShyam Sundar S K 	rc = rtc_alarm_irq_enable(rtc_device, 0);
78492c2fb8fSShyam Sundar S K 	pm_pr_dbg("wakeup timer programmed for %lld seconds\n", duration);
78592c2fb8fSShyam Sundar S K 
78692c2fb8fSShyam Sundar S K 	return rc;
78792c2fb8fSShyam Sundar S K }
78892c2fb8fSShyam Sundar S K 
amd_pmc_s2idle_prepare(void)78992c2fb8fSShyam Sundar S K static void amd_pmc_s2idle_prepare(void)
79092c2fb8fSShyam Sundar S K {
79192c2fb8fSShyam Sundar S K 	struct amd_pmc_dev *pdev = &pmc;
79292c2fb8fSShyam Sundar S K 	int rc;
79392c2fb8fSShyam Sundar S K 	u8 msg;
79492c2fb8fSShyam Sundar S K 	u32 arg = 1;
79592c2fb8fSShyam Sundar S K 
79692c2fb8fSShyam Sundar S K 	/* Reset and Start SMU logging - to monitor the s0i3 stats */
79792c2fb8fSShyam Sundar S K 	amd_pmc_setup_smu_logging(pdev);
79892c2fb8fSShyam Sundar S K 
79992c2fb8fSShyam Sundar S K 	/* Activate CZN specific platform bug workarounds */
80092c2fb8fSShyam Sundar S K 	if (pdev->cpu_id == AMD_CPU_ID_CZN && !disable_workarounds) {
80192c2fb8fSShyam Sundar S K 		rc = amd_pmc_verify_czn_rtc(pdev, &arg);
80292c2fb8fSShyam Sundar S K 		if (rc) {
80392c2fb8fSShyam Sundar S K 			dev_err(pdev->dev, "failed to set RTC: %d\n", rc);
80492c2fb8fSShyam Sundar S K 			return;
80592c2fb8fSShyam Sundar S K 		}
80692c2fb8fSShyam Sundar S K 	}
80792c2fb8fSShyam Sundar S K 
80892c2fb8fSShyam Sundar S K 	msg = amd_pmc_get_os_hint(pdev);
80992c2fb8fSShyam Sundar S K 	rc = amd_pmc_send_cmd(pdev, arg, NULL, msg, false);
81092c2fb8fSShyam Sundar S K 	if (rc) {
81192c2fb8fSShyam Sundar S K 		dev_err(pdev->dev, "suspend failed: %d\n", rc);
81292c2fb8fSShyam Sundar S K 		return;
81392c2fb8fSShyam Sundar S K 	}
81492c2fb8fSShyam Sundar S K 
81592c2fb8fSShyam Sundar S K 	rc = amd_pmc_write_stb(pdev, AMD_PMC_STB_S2IDLE_PREPARE);
81692c2fb8fSShyam Sundar S K 	if (rc)
81792c2fb8fSShyam Sundar S K 		dev_err(pdev->dev, "error writing to STB: %d\n", rc);
81892c2fb8fSShyam Sundar S K }
81992c2fb8fSShyam Sundar S K 
amd_pmc_s2idle_check(void)82092c2fb8fSShyam Sundar S K static void amd_pmc_s2idle_check(void)
82192c2fb8fSShyam Sundar S K {
82292c2fb8fSShyam Sundar S K 	struct amd_pmc_dev *pdev = &pmc;
82392c2fb8fSShyam Sundar S K 	struct smu_metrics table;
82492c2fb8fSShyam Sundar S K 	int rc;
82592c2fb8fSShyam Sundar S K 
82692c2fb8fSShyam Sundar S K 	/* CZN: Ensure that future s0i3 entry attempts at least 10ms passed */
82792c2fb8fSShyam Sundar S K 	if (pdev->cpu_id == AMD_CPU_ID_CZN && !get_metrics_table(pdev, &table) &&
82892c2fb8fSShyam Sundar S K 	    table.s0i3_last_entry_status)
82992c2fb8fSShyam Sundar S K 		usleep_range(10000, 20000);
83092c2fb8fSShyam Sundar S K 
83192c2fb8fSShyam Sundar S K 	/* Dump the IdleMask before we add to the STB */
83292c2fb8fSShyam Sundar S K 	amd_pmc_idlemask_read(pdev, pdev->dev, NULL);
83392c2fb8fSShyam Sundar S K 
83492c2fb8fSShyam Sundar S K 	rc = amd_pmc_write_stb(pdev, AMD_PMC_STB_S2IDLE_CHECK);
83592c2fb8fSShyam Sundar S K 	if (rc)
83692c2fb8fSShyam Sundar S K 		dev_err(pdev->dev, "error writing to STB: %d\n", rc);
83792c2fb8fSShyam Sundar S K }
83892c2fb8fSShyam Sundar S K 
amd_pmc_dump_data(struct amd_pmc_dev * pdev)83992c2fb8fSShyam Sundar S K static int amd_pmc_dump_data(struct amd_pmc_dev *pdev)
84092c2fb8fSShyam Sundar S K {
84192c2fb8fSShyam Sundar S K 	if (pdev->cpu_id == AMD_CPU_ID_PCO)
84292c2fb8fSShyam Sundar S K 		return -ENODEV;
84392c2fb8fSShyam Sundar S K 
84492c2fb8fSShyam Sundar S K 	return amd_pmc_send_cmd(pdev, 0, NULL, SMU_MSG_LOG_DUMP_DATA, false);
84592c2fb8fSShyam Sundar S K }
84692c2fb8fSShyam Sundar S K 
amd_pmc_s2idle_restore(void)84792c2fb8fSShyam Sundar S K static void amd_pmc_s2idle_restore(void)
84892c2fb8fSShyam Sundar S K {
84992c2fb8fSShyam Sundar S K 	struct amd_pmc_dev *pdev = &pmc;
85092c2fb8fSShyam Sundar S K 	int rc;
85192c2fb8fSShyam Sundar S K 	u8 msg;
85292c2fb8fSShyam Sundar S K 
85392c2fb8fSShyam Sundar S K 	msg = amd_pmc_get_os_hint(pdev);
85492c2fb8fSShyam Sundar S K 	rc = amd_pmc_send_cmd(pdev, 0, NULL, msg, false);
85592c2fb8fSShyam Sundar S K 	if (rc)
85692c2fb8fSShyam Sundar S K 		dev_err(pdev->dev, "resume failed: %d\n", rc);
85792c2fb8fSShyam Sundar S K 
85892c2fb8fSShyam Sundar S K 	/* Let SMU know that we are looking for stats */
85992c2fb8fSShyam Sundar S K 	amd_pmc_dump_data(pdev);
86092c2fb8fSShyam Sundar S K 
86192c2fb8fSShyam Sundar S K 	rc = amd_pmc_write_stb(pdev, AMD_PMC_STB_S2IDLE_RESTORE);
86292c2fb8fSShyam Sundar S K 	if (rc)
86392c2fb8fSShyam Sundar S K 		dev_err(pdev->dev, "error writing to STB: %d\n", rc);
86492c2fb8fSShyam Sundar S K 
86592c2fb8fSShyam Sundar S K 	/* Notify on failed entry */
86692c2fb8fSShyam Sundar S K 	amd_pmc_validate_deepest(pdev);
86792c2fb8fSShyam Sundar S K 
86892c2fb8fSShyam Sundar S K 	amd_pmc_process_restore_quirks(pdev);
86992c2fb8fSShyam Sundar S K }
87092c2fb8fSShyam Sundar S K 
87192c2fb8fSShyam Sundar S K static struct acpi_s2idle_dev_ops amd_pmc_s2idle_dev_ops = {
87292c2fb8fSShyam Sundar S K 	.prepare = amd_pmc_s2idle_prepare,
87392c2fb8fSShyam Sundar S K 	.check = amd_pmc_s2idle_check,
87492c2fb8fSShyam Sundar S K 	.restore = amd_pmc_s2idle_restore,
87592c2fb8fSShyam Sundar S K };
87692c2fb8fSShyam Sundar S K 
amd_pmc_suspend_handler(struct device * dev)87792c2fb8fSShyam Sundar S K static int amd_pmc_suspend_handler(struct device *dev)
87892c2fb8fSShyam Sundar S K {
87992c2fb8fSShyam Sundar S K 	struct amd_pmc_dev *pdev = dev_get_drvdata(dev);
88092c2fb8fSShyam Sundar S K 
881*5b235bbcSMario Limonciello 	if (pdev->disable_8042_wakeup && !disable_workarounds) {
882a7edb661SMario Limonciello 		int rc = amd_pmc_wa_irq1(pdev);
88392c2fb8fSShyam Sundar S K 
88492c2fb8fSShyam Sundar S K 		if (rc) {
88592c2fb8fSShyam Sundar S K 			dev_err(pdev->dev, "failed to adjust keyboard wakeup: %d\n", rc);
88692c2fb8fSShyam Sundar S K 			return rc;
88792c2fb8fSShyam Sundar S K 		}
88892c2fb8fSShyam Sundar S K 	}
88992c2fb8fSShyam Sundar S K 
89092c2fb8fSShyam Sundar S K 	return 0;
89192c2fb8fSShyam Sundar S K }
89292c2fb8fSShyam Sundar S K 
89392c2fb8fSShyam Sundar S K static DEFINE_SIMPLE_DEV_PM_OPS(amd_pmc_pm, amd_pmc_suspend_handler, NULL);
89492c2fb8fSShyam Sundar S K 
89592c2fb8fSShyam Sundar S K static const struct pci_device_id pmc_pci_ids[] = {
89692c2fb8fSShyam Sundar S K 	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_PS) },
89792c2fb8fSShyam Sundar S K 	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_CB) },
89892c2fb8fSShyam Sundar S K 	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_YC) },
89992c2fb8fSShyam Sundar S K 	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_CZN) },
90092c2fb8fSShyam Sundar S K 	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_RN) },
90192c2fb8fSShyam Sundar S K 	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_PCO) },
90292c2fb8fSShyam Sundar S K 	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_RV) },
90392c2fb8fSShyam Sundar S K 	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_SP) },
90492c2fb8fSShyam Sundar S K 	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_1AH_M20H_ROOT) },
90592c2fb8fSShyam Sundar S K 	{ }
90692c2fb8fSShyam Sundar S K };
90792c2fb8fSShyam Sundar S K 
amd_pmc_s2d_init(struct amd_pmc_dev * dev)90892c2fb8fSShyam Sundar S K static int amd_pmc_s2d_init(struct amd_pmc_dev *dev)
90992c2fb8fSShyam Sundar S K {
91092c2fb8fSShyam Sundar S K 	u32 phys_addr_low, phys_addr_hi;
91192c2fb8fSShyam Sundar S K 	u64 stb_phys_addr;
91292c2fb8fSShyam Sundar S K 	u32 size = 0;
91392c2fb8fSShyam Sundar S K 	int ret;
91492c2fb8fSShyam Sundar S K 
91592c2fb8fSShyam Sundar S K 	/* Spill to DRAM feature uses separate SMU message port */
91692c2fb8fSShyam Sundar S K 	dev->msg_port = 1;
91792c2fb8fSShyam Sundar S K 
91892c2fb8fSShyam Sundar S K 	/* Get num of IP blocks within the SoC */
91992c2fb8fSShyam Sundar S K 	amd_pmc_get_ip_info(dev);
92092c2fb8fSShyam Sundar S K 
92192c2fb8fSShyam Sundar S K 	amd_pmc_send_cmd(dev, S2D_TELEMETRY_SIZE, &size, dev->s2d_msg_id, true);
92292c2fb8fSShyam Sundar S K 	if (size != S2D_TELEMETRY_BYTES_MAX)
92392c2fb8fSShyam Sundar S K 		return -EIO;
92492c2fb8fSShyam Sundar S K 
92592c2fb8fSShyam Sundar S K 	/* Get DRAM size */
9261cceb482SShyam Sundar S K 	ret = amd_pmc_send_cmd(dev, S2D_DRAM_SIZE, &dev->dram_size, dev->s2d_msg_id, true);
9271cceb482SShyam Sundar S K 	if (ret || !dev->dram_size)
92892c2fb8fSShyam Sundar S K 		dev->dram_size = S2D_TELEMETRY_DRAMBYTES_MAX;
92992c2fb8fSShyam Sundar S K 
93092c2fb8fSShyam Sundar S K 	/* Get STB DRAM address */
93192c2fb8fSShyam Sundar S K 	amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_LOW, &phys_addr_low, dev->s2d_msg_id, true);
93292c2fb8fSShyam Sundar S K 	amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_HIGH, &phys_addr_hi, dev->s2d_msg_id, true);
93392c2fb8fSShyam Sundar S K 
93492c2fb8fSShyam Sundar S K 	stb_phys_addr = ((u64)phys_addr_hi << 32 | phys_addr_low);
93592c2fb8fSShyam Sundar S K 
93692c2fb8fSShyam Sundar S K 	/* Clear msg_port for other SMU operation */
93792c2fb8fSShyam Sundar S K 	dev->msg_port = 0;
93892c2fb8fSShyam Sundar S K 
93992c2fb8fSShyam Sundar S K 	dev->stb_virt_addr = devm_ioremap(dev->dev, stb_phys_addr, dev->dram_size);
94092c2fb8fSShyam Sundar S K 	if (!dev->stb_virt_addr)
94192c2fb8fSShyam Sundar S K 		return -ENOMEM;
94292c2fb8fSShyam Sundar S K 
94392c2fb8fSShyam Sundar S K 	return 0;
94492c2fb8fSShyam Sundar S K }
94592c2fb8fSShyam Sundar S K 
amd_pmc_write_stb(struct amd_pmc_dev * dev,u32 data)94692c2fb8fSShyam Sundar S K static int amd_pmc_write_stb(struct amd_pmc_dev *dev, u32 data)
94792c2fb8fSShyam Sundar S K {
94892c2fb8fSShyam Sundar S K 	int err;
94992c2fb8fSShyam Sundar S K 
95092c2fb8fSShyam Sundar S K 	err = amd_smn_write(0, AMD_PMC_STB_PMI_0, data);
95192c2fb8fSShyam Sundar S K 	if (err) {
95292c2fb8fSShyam Sundar S K 		dev_err(dev->dev, "failed to write data in stb: 0x%X\n", AMD_PMC_STB_PMI_0);
95392c2fb8fSShyam Sundar S K 		return pcibios_err_to_errno(err);
95492c2fb8fSShyam Sundar S K 	}
95592c2fb8fSShyam Sundar S K 
95692c2fb8fSShyam Sundar S K 	return 0;
95792c2fb8fSShyam Sundar S K }
95892c2fb8fSShyam Sundar S K 
amd_pmc_read_stb(struct amd_pmc_dev * dev,u32 * buf)95992c2fb8fSShyam Sundar S K static int amd_pmc_read_stb(struct amd_pmc_dev *dev, u32 *buf)
96092c2fb8fSShyam Sundar S K {
96192c2fb8fSShyam Sundar S K 	int i, err;
96292c2fb8fSShyam Sundar S K 
96392c2fb8fSShyam Sundar S K 	for (i = 0; i < FIFO_SIZE; i++) {
96492c2fb8fSShyam Sundar S K 		err = amd_smn_read(0, AMD_PMC_STB_PMI_0, buf++);
96592c2fb8fSShyam Sundar S K 		if (err) {
96692c2fb8fSShyam Sundar S K 			dev_err(dev->dev, "error reading data from stb: 0x%X\n", AMD_PMC_STB_PMI_0);
96792c2fb8fSShyam Sundar S K 			return pcibios_err_to_errno(err);
96892c2fb8fSShyam Sundar S K 		}
96992c2fb8fSShyam Sundar S K 	}
97092c2fb8fSShyam Sundar S K 
97192c2fb8fSShyam Sundar S K 	return 0;
97292c2fb8fSShyam Sundar S K }
97392c2fb8fSShyam Sundar S K 
amd_pmc_probe(struct platform_device * pdev)97492c2fb8fSShyam Sundar S K static int amd_pmc_probe(struct platform_device *pdev)
97592c2fb8fSShyam Sundar S K {
97692c2fb8fSShyam Sundar S K 	struct amd_pmc_dev *dev = &pmc;
97792c2fb8fSShyam Sundar S K 	struct pci_dev *rdev;
97892c2fb8fSShyam Sundar S K 	u32 base_addr_lo, base_addr_hi;
97992c2fb8fSShyam Sundar S K 	u64 base_addr;
98092c2fb8fSShyam Sundar S K 	int err;
98192c2fb8fSShyam Sundar S K 	u32 val;
98292c2fb8fSShyam Sundar S K 
98392c2fb8fSShyam Sundar S K 	dev->dev = &pdev->dev;
98492c2fb8fSShyam Sundar S K 
98592c2fb8fSShyam Sundar S K 	rdev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0, 0));
98692c2fb8fSShyam Sundar S K 	if (!rdev || !pci_match_id(pmc_pci_ids, rdev)) {
98792c2fb8fSShyam Sundar S K 		err = -ENODEV;
98892c2fb8fSShyam Sundar S K 		goto err_pci_dev_put;
98992c2fb8fSShyam Sundar S K 	}
99092c2fb8fSShyam Sundar S K 
99192c2fb8fSShyam Sundar S K 	dev->cpu_id = rdev->device;
99292c2fb8fSShyam Sundar S K 
99392c2fb8fSShyam Sundar S K 	if (dev->cpu_id == AMD_CPU_ID_SP) {
99492c2fb8fSShyam Sundar S K 		dev_warn_once(dev->dev, "S0i3 is not supported on this hardware\n");
99592c2fb8fSShyam Sundar S K 		err = -ENODEV;
99692c2fb8fSShyam Sundar S K 		goto err_pci_dev_put;
99792c2fb8fSShyam Sundar S K 	}
99892c2fb8fSShyam Sundar S K 
99992c2fb8fSShyam Sundar S K 	dev->rdev = rdev;
100092c2fb8fSShyam Sundar S K 	err = amd_smn_read(0, AMD_PMC_BASE_ADDR_LO, &val);
100192c2fb8fSShyam Sundar S K 	if (err) {
100292c2fb8fSShyam Sundar S K 		dev_err(dev->dev, "error reading 0x%x\n", AMD_PMC_BASE_ADDR_LO);
100392c2fb8fSShyam Sundar S K 		err = pcibios_err_to_errno(err);
100492c2fb8fSShyam Sundar S K 		goto err_pci_dev_put;
100592c2fb8fSShyam Sundar S K 	}
100692c2fb8fSShyam Sundar S K 
100792c2fb8fSShyam Sundar S K 	base_addr_lo = val & AMD_PMC_BASE_ADDR_HI_MASK;
100892c2fb8fSShyam Sundar S K 
100992c2fb8fSShyam Sundar S K 	err = amd_smn_read(0, AMD_PMC_BASE_ADDR_HI, &val);
101092c2fb8fSShyam Sundar S K 	if (err) {
101192c2fb8fSShyam Sundar S K 		dev_err(dev->dev, "error reading 0x%x\n", AMD_PMC_BASE_ADDR_HI);
101292c2fb8fSShyam Sundar S K 		err = pcibios_err_to_errno(err);
101392c2fb8fSShyam Sundar S K 		goto err_pci_dev_put;
101492c2fb8fSShyam Sundar S K 	}
101592c2fb8fSShyam Sundar S K 
101692c2fb8fSShyam Sundar S K 	base_addr_hi = val & AMD_PMC_BASE_ADDR_LO_MASK;
101792c2fb8fSShyam Sundar S K 	base_addr = ((u64)base_addr_hi << 32 | base_addr_lo);
101892c2fb8fSShyam Sundar S K 
101992c2fb8fSShyam Sundar S K 	dev->regbase = devm_ioremap(dev->dev, base_addr + AMD_PMC_BASE_ADDR_OFFSET,
102092c2fb8fSShyam Sundar S K 				    AMD_PMC_MAPPING_SIZE);
102192c2fb8fSShyam Sundar S K 	if (!dev->regbase) {
102292c2fb8fSShyam Sundar S K 		err = -ENOMEM;
102392c2fb8fSShyam Sundar S K 		goto err_pci_dev_put;
102492c2fb8fSShyam Sundar S K 	}
102592c2fb8fSShyam Sundar S K 
102692c2fb8fSShyam Sundar S K 	mutex_init(&dev->lock);
102792c2fb8fSShyam Sundar S K 
102892c2fb8fSShyam Sundar S K 	if (enable_stb && amd_pmc_is_stb_supported(dev)) {
102992c2fb8fSShyam Sundar S K 		err = amd_pmc_s2d_init(dev);
103092c2fb8fSShyam Sundar S K 		if (err)
103192c2fb8fSShyam Sundar S K 			goto err_pci_dev_put;
103292c2fb8fSShyam Sundar S K 	}
103392c2fb8fSShyam Sundar S K 
103492c2fb8fSShyam Sundar S K 	platform_set_drvdata(pdev, dev);
103592c2fb8fSShyam Sundar S K 	if (IS_ENABLED(CONFIG_SUSPEND)) {
103692c2fb8fSShyam Sundar S K 		err = acpi_register_lps0_dev(&amd_pmc_s2idle_dev_ops);
103792c2fb8fSShyam Sundar S K 		if (err)
103892c2fb8fSShyam Sundar S K 			dev_warn(dev->dev, "failed to register LPS0 sleep handler, expect increased power consumption\n");
103992c2fb8fSShyam Sundar S K 		if (!disable_workarounds)
104092c2fb8fSShyam Sundar S K 			amd_pmc_quirks_init(dev);
104192c2fb8fSShyam Sundar S K 	}
104292c2fb8fSShyam Sundar S K 
104392c2fb8fSShyam Sundar S K 	amd_pmc_dbgfs_register(dev);
104492c2fb8fSShyam Sundar S K 	pm_report_max_hw_sleep(U64_MAX);
104592c2fb8fSShyam Sundar S K 	return 0;
104692c2fb8fSShyam Sundar S K 
104792c2fb8fSShyam Sundar S K err_pci_dev_put:
104892c2fb8fSShyam Sundar S K 	pci_dev_put(rdev);
104992c2fb8fSShyam Sundar S K 	return err;
105092c2fb8fSShyam Sundar S K }
105192c2fb8fSShyam Sundar S K 
amd_pmc_remove(struct platform_device * pdev)105292c2fb8fSShyam Sundar S K static void amd_pmc_remove(struct platform_device *pdev)
105392c2fb8fSShyam Sundar S K {
105492c2fb8fSShyam Sundar S K 	struct amd_pmc_dev *dev = platform_get_drvdata(pdev);
105592c2fb8fSShyam Sundar S K 
105692c2fb8fSShyam Sundar S K 	if (IS_ENABLED(CONFIG_SUSPEND))
105792c2fb8fSShyam Sundar S K 		acpi_unregister_lps0_dev(&amd_pmc_s2idle_dev_ops);
105892c2fb8fSShyam Sundar S K 	amd_pmc_dbgfs_unregister(dev);
105992c2fb8fSShyam Sundar S K 	pci_dev_put(dev->rdev);
106092c2fb8fSShyam Sundar S K 	mutex_destroy(&dev->lock);
106192c2fb8fSShyam Sundar S K }
106292c2fb8fSShyam Sundar S K 
106392c2fb8fSShyam Sundar S K static const struct acpi_device_id amd_pmc_acpi_ids[] = {
106492c2fb8fSShyam Sundar S K 	{"AMDI0005", 0},
106592c2fb8fSShyam Sundar S K 	{"AMDI0006", 0},
106692c2fb8fSShyam Sundar S K 	{"AMDI0007", 0},
106792c2fb8fSShyam Sundar S K 	{"AMDI0008", 0},
106892c2fb8fSShyam Sundar S K 	{"AMDI0009", 0},
106992c2fb8fSShyam Sundar S K 	{"AMDI000A", 0},
107092c2fb8fSShyam Sundar S K 	{"AMD0004", 0},
107192c2fb8fSShyam Sundar S K 	{"AMD0005", 0},
107292c2fb8fSShyam Sundar S K 	{ }
107392c2fb8fSShyam Sundar S K };
107492c2fb8fSShyam Sundar S K MODULE_DEVICE_TABLE(acpi, amd_pmc_acpi_ids);
107592c2fb8fSShyam Sundar S K 
107692c2fb8fSShyam Sundar S K static struct platform_driver amd_pmc_driver = {
107792c2fb8fSShyam Sundar S K 	.driver = {
107892c2fb8fSShyam Sundar S K 		.name = "amd_pmc",
107992c2fb8fSShyam Sundar S K 		.acpi_match_table = amd_pmc_acpi_ids,
108092c2fb8fSShyam Sundar S K 		.dev_groups = pmc_groups,
108192c2fb8fSShyam Sundar S K 		.pm = pm_sleep_ptr(&amd_pmc_pm),
108292c2fb8fSShyam Sundar S K 	},
108392c2fb8fSShyam Sundar S K 	.probe = amd_pmc_probe,
108492c2fb8fSShyam Sundar S K 	.remove_new = amd_pmc_remove,
108592c2fb8fSShyam Sundar S K };
108692c2fb8fSShyam Sundar S K module_platform_driver(amd_pmc_driver);
108792c2fb8fSShyam Sundar S K 
108892c2fb8fSShyam Sundar S K MODULE_LICENSE("GPL v2");
108992c2fb8fSShyam Sundar S K MODULE_DESCRIPTION("AMD PMC Driver");
1090