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*5cc62108SMaciej S. Szmigiero /*
882*5cc62108SMaciej S. Szmigiero * Must be called only from the same set of dev_pm_ops handlers
883*5cc62108SMaciej S. Szmigiero * as i8042_pm_suspend() is called: currently just from .suspend.
884*5cc62108SMaciej S. Szmigiero */
8855b235bbcSMario Limonciello if (pdev->disable_8042_wakeup && !disable_workarounds) {
886a7edb661SMario Limonciello int rc = amd_pmc_wa_irq1(pdev);
88792c2fb8fSShyam Sundar S K
88892c2fb8fSShyam Sundar S K if (rc) {
88992c2fb8fSShyam Sundar S K dev_err(pdev->dev, "failed to adjust keyboard wakeup: %d\n", rc);
89092c2fb8fSShyam Sundar S K return rc;
89192c2fb8fSShyam Sundar S K }
89292c2fb8fSShyam Sundar S K }
89392c2fb8fSShyam Sundar S K
89492c2fb8fSShyam Sundar S K return 0;
89592c2fb8fSShyam Sundar S K }
89692c2fb8fSShyam Sundar S K
897*5cc62108SMaciej S. Szmigiero static const struct dev_pm_ops amd_pmc_pm = {
898*5cc62108SMaciej S. Szmigiero .suspend = amd_pmc_suspend_handler,
899*5cc62108SMaciej S. Szmigiero };
90092c2fb8fSShyam Sundar S K
90192c2fb8fSShyam Sundar S K static const struct pci_device_id pmc_pci_ids[] = {
90292c2fb8fSShyam Sundar S K { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_PS) },
90392c2fb8fSShyam Sundar S K { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_CB) },
90492c2fb8fSShyam Sundar S K { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_YC) },
90592c2fb8fSShyam Sundar S K { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_CZN) },
90692c2fb8fSShyam Sundar S K { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_RN) },
90792c2fb8fSShyam Sundar S K { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_PCO) },
90892c2fb8fSShyam Sundar S K { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_RV) },
90992c2fb8fSShyam Sundar S K { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_SP) },
91092c2fb8fSShyam Sundar S K { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_1AH_M20H_ROOT) },
91192c2fb8fSShyam Sundar S K { }
91292c2fb8fSShyam Sundar S K };
91392c2fb8fSShyam Sundar S K
amd_pmc_s2d_init(struct amd_pmc_dev * dev)91492c2fb8fSShyam Sundar S K static int amd_pmc_s2d_init(struct amd_pmc_dev *dev)
91592c2fb8fSShyam Sundar S K {
91692c2fb8fSShyam Sundar S K u32 phys_addr_low, phys_addr_hi;
91792c2fb8fSShyam Sundar S K u64 stb_phys_addr;
91892c2fb8fSShyam Sundar S K u32 size = 0;
91992c2fb8fSShyam Sundar S K int ret;
92092c2fb8fSShyam Sundar S K
92192c2fb8fSShyam Sundar S K /* Spill to DRAM feature uses separate SMU message port */
92292c2fb8fSShyam Sundar S K dev->msg_port = 1;
92392c2fb8fSShyam Sundar S K
92492c2fb8fSShyam Sundar S K /* Get num of IP blocks within the SoC */
92592c2fb8fSShyam Sundar S K amd_pmc_get_ip_info(dev);
92692c2fb8fSShyam Sundar S K
92792c2fb8fSShyam Sundar S K amd_pmc_send_cmd(dev, S2D_TELEMETRY_SIZE, &size, dev->s2d_msg_id, true);
92892c2fb8fSShyam Sundar S K if (size != S2D_TELEMETRY_BYTES_MAX)
92992c2fb8fSShyam Sundar S K return -EIO;
93092c2fb8fSShyam Sundar S K
93192c2fb8fSShyam Sundar S K /* Get DRAM size */
9321cceb482SShyam Sundar S K ret = amd_pmc_send_cmd(dev, S2D_DRAM_SIZE, &dev->dram_size, dev->s2d_msg_id, true);
9331cceb482SShyam Sundar S K if (ret || !dev->dram_size)
93492c2fb8fSShyam Sundar S K dev->dram_size = S2D_TELEMETRY_DRAMBYTES_MAX;
93592c2fb8fSShyam Sundar S K
93692c2fb8fSShyam Sundar S K /* Get STB DRAM address */
93792c2fb8fSShyam Sundar S K amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_LOW, &phys_addr_low, dev->s2d_msg_id, true);
93892c2fb8fSShyam Sundar S K amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_HIGH, &phys_addr_hi, dev->s2d_msg_id, true);
93992c2fb8fSShyam Sundar S K
9407a3ed3f1SCorey Hickey if (!phys_addr_hi && !phys_addr_low) {
9417a3ed3f1SCorey Hickey dev_err(dev->dev, "STB is not enabled on the system; disable enable_stb or contact system vendor\n");
9427a3ed3f1SCorey Hickey return -EINVAL;
9437a3ed3f1SCorey Hickey }
9447a3ed3f1SCorey Hickey
94592c2fb8fSShyam Sundar S K stb_phys_addr = ((u64)phys_addr_hi << 32 | phys_addr_low);
94692c2fb8fSShyam Sundar S K
94792c2fb8fSShyam Sundar S K /* Clear msg_port for other SMU operation */
94892c2fb8fSShyam Sundar S K dev->msg_port = 0;
94992c2fb8fSShyam Sundar S K
95092c2fb8fSShyam Sundar S K dev->stb_virt_addr = devm_ioremap(dev->dev, stb_phys_addr, dev->dram_size);
95192c2fb8fSShyam Sundar S K if (!dev->stb_virt_addr)
95292c2fb8fSShyam Sundar S K return -ENOMEM;
95392c2fb8fSShyam Sundar S K
95492c2fb8fSShyam Sundar S K return 0;
95592c2fb8fSShyam Sundar S K }
95692c2fb8fSShyam Sundar S K
amd_pmc_write_stb(struct amd_pmc_dev * dev,u32 data)95792c2fb8fSShyam Sundar S K static int amd_pmc_write_stb(struct amd_pmc_dev *dev, u32 data)
95892c2fb8fSShyam Sundar S K {
95992c2fb8fSShyam Sundar S K int err;
96092c2fb8fSShyam Sundar S K
96192c2fb8fSShyam Sundar S K err = amd_smn_write(0, AMD_PMC_STB_PMI_0, data);
96292c2fb8fSShyam Sundar S K if (err) {
96392c2fb8fSShyam Sundar S K dev_err(dev->dev, "failed to write data in stb: 0x%X\n", AMD_PMC_STB_PMI_0);
96492c2fb8fSShyam Sundar S K return pcibios_err_to_errno(err);
96592c2fb8fSShyam Sundar S K }
96692c2fb8fSShyam Sundar S K
96792c2fb8fSShyam Sundar S K return 0;
96892c2fb8fSShyam Sundar S K }
96992c2fb8fSShyam Sundar S K
amd_pmc_read_stb(struct amd_pmc_dev * dev,u32 * buf)97092c2fb8fSShyam Sundar S K static int amd_pmc_read_stb(struct amd_pmc_dev *dev, u32 *buf)
97192c2fb8fSShyam Sundar S K {
97292c2fb8fSShyam Sundar S K int i, err;
97392c2fb8fSShyam Sundar S K
97492c2fb8fSShyam Sundar S K for (i = 0; i < FIFO_SIZE; i++) {
97592c2fb8fSShyam Sundar S K err = amd_smn_read(0, AMD_PMC_STB_PMI_0, buf++);
97692c2fb8fSShyam Sundar S K if (err) {
97792c2fb8fSShyam Sundar S K dev_err(dev->dev, "error reading data from stb: 0x%X\n", AMD_PMC_STB_PMI_0);
97892c2fb8fSShyam Sundar S K return pcibios_err_to_errno(err);
97992c2fb8fSShyam Sundar S K }
98092c2fb8fSShyam Sundar S K }
98192c2fb8fSShyam Sundar S K
98292c2fb8fSShyam Sundar S K return 0;
98392c2fb8fSShyam Sundar S K }
98492c2fb8fSShyam Sundar S K
amd_pmc_probe(struct platform_device * pdev)98592c2fb8fSShyam Sundar S K static int amd_pmc_probe(struct platform_device *pdev)
98692c2fb8fSShyam Sundar S K {
98792c2fb8fSShyam Sundar S K struct amd_pmc_dev *dev = &pmc;
98892c2fb8fSShyam Sundar S K struct pci_dev *rdev;
98992c2fb8fSShyam Sundar S K u32 base_addr_lo, base_addr_hi;
99092c2fb8fSShyam Sundar S K u64 base_addr;
99192c2fb8fSShyam Sundar S K int err;
99292c2fb8fSShyam Sundar S K u32 val;
99392c2fb8fSShyam Sundar S K
99492c2fb8fSShyam Sundar S K dev->dev = &pdev->dev;
99592c2fb8fSShyam Sundar S K
99692c2fb8fSShyam Sundar S K rdev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0, 0));
99792c2fb8fSShyam Sundar S K if (!rdev || !pci_match_id(pmc_pci_ids, rdev)) {
99892c2fb8fSShyam Sundar S K err = -ENODEV;
99992c2fb8fSShyam Sundar S K goto err_pci_dev_put;
100092c2fb8fSShyam Sundar S K }
100192c2fb8fSShyam Sundar S K
100292c2fb8fSShyam Sundar S K dev->cpu_id = rdev->device;
100392c2fb8fSShyam Sundar S K
100492c2fb8fSShyam Sundar S K if (dev->cpu_id == AMD_CPU_ID_SP) {
100592c2fb8fSShyam Sundar S K dev_warn_once(dev->dev, "S0i3 is not supported on this hardware\n");
100692c2fb8fSShyam Sundar S K err = -ENODEV;
100792c2fb8fSShyam Sundar S K goto err_pci_dev_put;
100892c2fb8fSShyam Sundar S K }
100992c2fb8fSShyam Sundar S K
101092c2fb8fSShyam Sundar S K dev->rdev = rdev;
101192c2fb8fSShyam Sundar S K err = amd_smn_read(0, AMD_PMC_BASE_ADDR_LO, &val);
101292c2fb8fSShyam Sundar S K if (err) {
101392c2fb8fSShyam Sundar S K dev_err(dev->dev, "error reading 0x%x\n", AMD_PMC_BASE_ADDR_LO);
101492c2fb8fSShyam Sundar S K err = pcibios_err_to_errno(err);
101592c2fb8fSShyam Sundar S K goto err_pci_dev_put;
101692c2fb8fSShyam Sundar S K }
101792c2fb8fSShyam Sundar S K
101892c2fb8fSShyam Sundar S K base_addr_lo = val & AMD_PMC_BASE_ADDR_HI_MASK;
101992c2fb8fSShyam Sundar S K
102092c2fb8fSShyam Sundar S K err = amd_smn_read(0, AMD_PMC_BASE_ADDR_HI, &val);
102192c2fb8fSShyam Sundar S K if (err) {
102292c2fb8fSShyam Sundar S K dev_err(dev->dev, "error reading 0x%x\n", AMD_PMC_BASE_ADDR_HI);
102392c2fb8fSShyam Sundar S K err = pcibios_err_to_errno(err);
102492c2fb8fSShyam Sundar S K goto err_pci_dev_put;
102592c2fb8fSShyam Sundar S K }
102692c2fb8fSShyam Sundar S K
102792c2fb8fSShyam Sundar S K base_addr_hi = val & AMD_PMC_BASE_ADDR_LO_MASK;
102892c2fb8fSShyam Sundar S K base_addr = ((u64)base_addr_hi << 32 | base_addr_lo);
102992c2fb8fSShyam Sundar S K
103092c2fb8fSShyam Sundar S K dev->regbase = devm_ioremap(dev->dev, base_addr + AMD_PMC_BASE_ADDR_OFFSET,
103192c2fb8fSShyam Sundar S K AMD_PMC_MAPPING_SIZE);
103292c2fb8fSShyam Sundar S K if (!dev->regbase) {
103392c2fb8fSShyam Sundar S K err = -ENOMEM;
103492c2fb8fSShyam Sundar S K goto err_pci_dev_put;
103592c2fb8fSShyam Sundar S K }
103692c2fb8fSShyam Sundar S K
103792c2fb8fSShyam Sundar S K mutex_init(&dev->lock);
103892c2fb8fSShyam Sundar S K
103992c2fb8fSShyam Sundar S K if (enable_stb && amd_pmc_is_stb_supported(dev)) {
104092c2fb8fSShyam Sundar S K err = amd_pmc_s2d_init(dev);
104192c2fb8fSShyam Sundar S K if (err)
104292c2fb8fSShyam Sundar S K goto err_pci_dev_put;
104392c2fb8fSShyam Sundar S K }
104492c2fb8fSShyam Sundar S K
104592c2fb8fSShyam Sundar S K platform_set_drvdata(pdev, dev);
104692c2fb8fSShyam Sundar S K if (IS_ENABLED(CONFIG_SUSPEND)) {
104792c2fb8fSShyam Sundar S K err = acpi_register_lps0_dev(&amd_pmc_s2idle_dev_ops);
104892c2fb8fSShyam Sundar S K if (err)
104992c2fb8fSShyam Sundar S K dev_warn(dev->dev, "failed to register LPS0 sleep handler, expect increased power consumption\n");
105092c2fb8fSShyam Sundar S K if (!disable_workarounds)
105192c2fb8fSShyam Sundar S K amd_pmc_quirks_init(dev);
105292c2fb8fSShyam Sundar S K }
105392c2fb8fSShyam Sundar S K
105492c2fb8fSShyam Sundar S K amd_pmc_dbgfs_register(dev);
105592c2fb8fSShyam Sundar S K pm_report_max_hw_sleep(U64_MAX);
105692c2fb8fSShyam Sundar S K return 0;
105792c2fb8fSShyam Sundar S K
105892c2fb8fSShyam Sundar S K err_pci_dev_put:
105992c2fb8fSShyam Sundar S K pci_dev_put(rdev);
106092c2fb8fSShyam Sundar S K return err;
106192c2fb8fSShyam Sundar S K }
106292c2fb8fSShyam Sundar S K
amd_pmc_remove(struct platform_device * pdev)106392c2fb8fSShyam Sundar S K static void amd_pmc_remove(struct platform_device *pdev)
106492c2fb8fSShyam Sundar S K {
106592c2fb8fSShyam Sundar S K struct amd_pmc_dev *dev = platform_get_drvdata(pdev);
106692c2fb8fSShyam Sundar S K
106792c2fb8fSShyam Sundar S K if (IS_ENABLED(CONFIG_SUSPEND))
106892c2fb8fSShyam Sundar S K acpi_unregister_lps0_dev(&amd_pmc_s2idle_dev_ops);
106992c2fb8fSShyam Sundar S K amd_pmc_dbgfs_unregister(dev);
107092c2fb8fSShyam Sundar S K pci_dev_put(dev->rdev);
107192c2fb8fSShyam Sundar S K mutex_destroy(&dev->lock);
107292c2fb8fSShyam Sundar S K }
107392c2fb8fSShyam Sundar S K
107492c2fb8fSShyam Sundar S K static const struct acpi_device_id amd_pmc_acpi_ids[] = {
107592c2fb8fSShyam Sundar S K {"AMDI0005", 0},
107692c2fb8fSShyam Sundar S K {"AMDI0006", 0},
107792c2fb8fSShyam Sundar S K {"AMDI0007", 0},
107892c2fb8fSShyam Sundar S K {"AMDI0008", 0},
107992c2fb8fSShyam Sundar S K {"AMDI0009", 0},
108092c2fb8fSShyam Sundar S K {"AMDI000A", 0},
108192c2fb8fSShyam Sundar S K {"AMD0004", 0},
108292c2fb8fSShyam Sundar S K {"AMD0005", 0},
108392c2fb8fSShyam Sundar S K { }
108492c2fb8fSShyam Sundar S K };
108592c2fb8fSShyam Sundar S K MODULE_DEVICE_TABLE(acpi, amd_pmc_acpi_ids);
108692c2fb8fSShyam Sundar S K
108792c2fb8fSShyam Sundar S K static struct platform_driver amd_pmc_driver = {
108892c2fb8fSShyam Sundar S K .driver = {
108992c2fb8fSShyam Sundar S K .name = "amd_pmc",
109092c2fb8fSShyam Sundar S K .acpi_match_table = amd_pmc_acpi_ids,
109192c2fb8fSShyam Sundar S K .dev_groups = pmc_groups,
109292c2fb8fSShyam Sundar S K .pm = pm_sleep_ptr(&amd_pmc_pm),
109392c2fb8fSShyam Sundar S K },
109492c2fb8fSShyam Sundar S K .probe = amd_pmc_probe,
109592c2fb8fSShyam Sundar S K .remove_new = amd_pmc_remove,
109692c2fb8fSShyam Sundar S K };
109792c2fb8fSShyam Sundar S K module_platform_driver(amd_pmc_driver);
109892c2fb8fSShyam Sundar S K
109992c2fb8fSShyam Sundar S K MODULE_LICENSE("GPL v2");
110092c2fb8fSShyam Sundar S K MODULE_DESCRIPTION("AMD PMC Driver");
1101