162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * AMD SoC Power Management Controller Driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2020, Advanced Micro Devices, Inc.
662306a36Sopenharmony_ci * All Rights Reserved.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <asm/amd_nb.h>
1462306a36Sopenharmony_ci#include <linux/acpi.h>
1562306a36Sopenharmony_ci#include <linux/bitfield.h>
1662306a36Sopenharmony_ci#include <linux/bits.h>
1762306a36Sopenharmony_ci#include <linux/debugfs.h>
1862306a36Sopenharmony_ci#include <linux/delay.h>
1962306a36Sopenharmony_ci#include <linux/io.h>
2062306a36Sopenharmony_ci#include <linux/iopoll.h>
2162306a36Sopenharmony_ci#include <linux/limits.h>
2262306a36Sopenharmony_ci#include <linux/module.h>
2362306a36Sopenharmony_ci#include <linux/pci.h>
2462306a36Sopenharmony_ci#include <linux/platform_device.h>
2562306a36Sopenharmony_ci#include <linux/rtc.h>
2662306a36Sopenharmony_ci#include <linux/serio.h>
2762306a36Sopenharmony_ci#include <linux/suspend.h>
2862306a36Sopenharmony_ci#include <linux/seq_file.h>
2962306a36Sopenharmony_ci#include <linux/uaccess.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include "pmc.h"
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/* SMU communication registers */
3462306a36Sopenharmony_ci#define AMD_PMC_REGISTER_MESSAGE	0x538
3562306a36Sopenharmony_ci#define AMD_PMC_REGISTER_RESPONSE	0x980
3662306a36Sopenharmony_ci#define AMD_PMC_REGISTER_ARGUMENT	0x9BC
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/* PMC Scratch Registers */
3962306a36Sopenharmony_ci#define AMD_PMC_SCRATCH_REG_CZN		0x94
4062306a36Sopenharmony_ci#define AMD_PMC_SCRATCH_REG_YC		0xD14
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/* STB Registers */
4362306a36Sopenharmony_ci#define AMD_PMC_STB_PMI_0		0x03E30600
4462306a36Sopenharmony_ci#define AMD_PMC_STB_S2IDLE_PREPARE	0xC6000001
4562306a36Sopenharmony_ci#define AMD_PMC_STB_S2IDLE_RESTORE	0xC6000002
4662306a36Sopenharmony_ci#define AMD_PMC_STB_S2IDLE_CHECK	0xC6000003
4762306a36Sopenharmony_ci#define AMD_PMC_STB_DUMMY_PC		0xC6000007
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/* STB S2D(Spill to DRAM) has different message port offset */
5062306a36Sopenharmony_ci#define AMD_S2D_REGISTER_MESSAGE	0xA20
5162306a36Sopenharmony_ci#define AMD_S2D_REGISTER_RESPONSE	0xA80
5262306a36Sopenharmony_ci#define AMD_S2D_REGISTER_ARGUMENT	0xA88
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci/* STB Spill to DRAM Parameters */
5562306a36Sopenharmony_ci#define S2D_TELEMETRY_BYTES_MAX		0x100000
5662306a36Sopenharmony_ci#define S2D_TELEMETRY_DRAMBYTES_MAX	0x1000000
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/* Base address of SMU for mapping physical address to virtual address */
5962306a36Sopenharmony_ci#define AMD_PMC_MAPPING_SIZE		0x01000
6062306a36Sopenharmony_ci#define AMD_PMC_BASE_ADDR_OFFSET	0x10000
6162306a36Sopenharmony_ci#define AMD_PMC_BASE_ADDR_LO		0x13B102E8
6262306a36Sopenharmony_ci#define AMD_PMC_BASE_ADDR_HI		0x13B102EC
6362306a36Sopenharmony_ci#define AMD_PMC_BASE_ADDR_LO_MASK	GENMASK(15, 0)
6462306a36Sopenharmony_ci#define AMD_PMC_BASE_ADDR_HI_MASK	GENMASK(31, 20)
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/* SMU Response Codes */
6762306a36Sopenharmony_ci#define AMD_PMC_RESULT_OK                    0x01
6862306a36Sopenharmony_ci#define AMD_PMC_RESULT_CMD_REJECT_BUSY       0xFC
6962306a36Sopenharmony_ci#define AMD_PMC_RESULT_CMD_REJECT_PREREQ     0xFD
7062306a36Sopenharmony_ci#define AMD_PMC_RESULT_CMD_UNKNOWN           0xFE
7162306a36Sopenharmony_ci#define AMD_PMC_RESULT_FAILED                0xFF
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci/* FCH SSC Registers */
7462306a36Sopenharmony_ci#define FCH_S0I3_ENTRY_TIME_L_OFFSET	0x30
7562306a36Sopenharmony_ci#define FCH_S0I3_ENTRY_TIME_H_OFFSET	0x34
7662306a36Sopenharmony_ci#define FCH_S0I3_EXIT_TIME_L_OFFSET	0x38
7762306a36Sopenharmony_ci#define FCH_S0I3_EXIT_TIME_H_OFFSET	0x3C
7862306a36Sopenharmony_ci#define FCH_SSC_MAPPING_SIZE		0x800
7962306a36Sopenharmony_ci#define FCH_BASE_PHY_ADDR_LOW		0xFED81100
8062306a36Sopenharmony_ci#define FCH_BASE_PHY_ADDR_HIGH		0x00000000
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci/* SMU Message Definations */
8362306a36Sopenharmony_ci#define SMU_MSG_GETSMUVERSION		0x02
8462306a36Sopenharmony_ci#define SMU_MSG_LOG_GETDRAM_ADDR_HI	0x04
8562306a36Sopenharmony_ci#define SMU_MSG_LOG_GETDRAM_ADDR_LO	0x05
8662306a36Sopenharmony_ci#define SMU_MSG_LOG_START		0x06
8762306a36Sopenharmony_ci#define SMU_MSG_LOG_RESET		0x07
8862306a36Sopenharmony_ci#define SMU_MSG_LOG_DUMP_DATA		0x08
8962306a36Sopenharmony_ci#define SMU_MSG_GET_SUP_CONSTRAINTS	0x09
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci#define PMC_MSG_DELAY_MIN_US		50
9262306a36Sopenharmony_ci#define RESPONSE_REGISTER_LOOP_MAX	20000
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci#define DELAY_MIN_US		2000
9562306a36Sopenharmony_ci#define DELAY_MAX_US		3000
9662306a36Sopenharmony_ci#define FIFO_SIZE		4096
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cienum amd_pmc_def {
9962306a36Sopenharmony_ci	MSG_TEST = 0x01,
10062306a36Sopenharmony_ci	MSG_OS_HINT_PCO,
10162306a36Sopenharmony_ci	MSG_OS_HINT_RN,
10262306a36Sopenharmony_ci};
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cienum s2d_arg {
10562306a36Sopenharmony_ci	S2D_TELEMETRY_SIZE = 0x01,
10662306a36Sopenharmony_ci	S2D_PHYS_ADDR_LOW,
10762306a36Sopenharmony_ci	S2D_PHYS_ADDR_HIGH,
10862306a36Sopenharmony_ci	S2D_NUM_SAMPLES,
10962306a36Sopenharmony_ci	S2D_DRAM_SIZE,
11062306a36Sopenharmony_ci};
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistruct amd_pmc_bit_map {
11362306a36Sopenharmony_ci	const char *name;
11462306a36Sopenharmony_ci	u32 bit_mask;
11562306a36Sopenharmony_ci};
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic const struct amd_pmc_bit_map soc15_ip_blk[] = {
11862306a36Sopenharmony_ci	{"DISPLAY",	BIT(0)},
11962306a36Sopenharmony_ci	{"CPU",		BIT(1)},
12062306a36Sopenharmony_ci	{"GFX",		BIT(2)},
12162306a36Sopenharmony_ci	{"VDD",		BIT(3)},
12262306a36Sopenharmony_ci	{"ACP",		BIT(4)},
12362306a36Sopenharmony_ci	{"VCN",		BIT(5)},
12462306a36Sopenharmony_ci	{"ISP",		BIT(6)},
12562306a36Sopenharmony_ci	{"NBIO",	BIT(7)},
12662306a36Sopenharmony_ci	{"DF",		BIT(8)},
12762306a36Sopenharmony_ci	{"USB3_0",	BIT(9)},
12862306a36Sopenharmony_ci	{"USB3_1",	BIT(10)},
12962306a36Sopenharmony_ci	{"LAPIC",	BIT(11)},
13062306a36Sopenharmony_ci	{"USB3_2",	BIT(12)},
13162306a36Sopenharmony_ci	{"USB3_3",	BIT(13)},
13262306a36Sopenharmony_ci	{"USB3_4",	BIT(14)},
13362306a36Sopenharmony_ci	{"USB4_0",	BIT(15)},
13462306a36Sopenharmony_ci	{"USB4_1",	BIT(16)},
13562306a36Sopenharmony_ci	{"MPM",		BIT(17)},
13662306a36Sopenharmony_ci	{"JPEG",	BIT(18)},
13762306a36Sopenharmony_ci	{"IPU",		BIT(19)},
13862306a36Sopenharmony_ci	{"UMSCH",	BIT(20)},
13962306a36Sopenharmony_ci	{}
14062306a36Sopenharmony_ci};
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic bool enable_stb;
14362306a36Sopenharmony_cimodule_param(enable_stb, bool, 0644);
14462306a36Sopenharmony_ciMODULE_PARM_DESC(enable_stb, "Enable the STB debug mechanism");
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic bool disable_workarounds;
14762306a36Sopenharmony_cimodule_param(disable_workarounds, bool, 0644);
14862306a36Sopenharmony_ciMODULE_PARM_DESC(disable_workarounds, "Disable workarounds for platform bugs");
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic struct amd_pmc_dev pmc;
15162306a36Sopenharmony_cistatic int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool ret);
15262306a36Sopenharmony_cistatic int amd_pmc_read_stb(struct amd_pmc_dev *dev, u32 *buf);
15362306a36Sopenharmony_cistatic int amd_pmc_write_stb(struct amd_pmc_dev *dev, u32 data);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic inline u32 amd_pmc_reg_read(struct amd_pmc_dev *dev, int reg_offset)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	return ioread32(dev->regbase + reg_offset);
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic inline void amd_pmc_reg_write(struct amd_pmc_dev *dev, int reg_offset, u32 val)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	iowrite32(val, dev->regbase + reg_offset);
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistruct smu_metrics {
16662306a36Sopenharmony_ci	u32 table_version;
16762306a36Sopenharmony_ci	u32 hint_count;
16862306a36Sopenharmony_ci	u32 s0i3_last_entry_status;
16962306a36Sopenharmony_ci	u32 timein_s0i2;
17062306a36Sopenharmony_ci	u64 timeentering_s0i3_lastcapture;
17162306a36Sopenharmony_ci	u64 timeentering_s0i3_totaltime;
17262306a36Sopenharmony_ci	u64 timeto_resume_to_os_lastcapture;
17362306a36Sopenharmony_ci	u64 timeto_resume_to_os_totaltime;
17462306a36Sopenharmony_ci	u64 timein_s0i3_lastcapture;
17562306a36Sopenharmony_ci	u64 timein_s0i3_totaltime;
17662306a36Sopenharmony_ci	u64 timein_swdrips_lastcapture;
17762306a36Sopenharmony_ci	u64 timein_swdrips_totaltime;
17862306a36Sopenharmony_ci	u64 timecondition_notmet_lastcapture[32];
17962306a36Sopenharmony_ci	u64 timecondition_notmet_totaltime[32];
18062306a36Sopenharmony_ci} __packed;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic int amd_pmc_stb_debugfs_open(struct inode *inode, struct file *filp)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	struct amd_pmc_dev *dev = filp->f_inode->i_private;
18562306a36Sopenharmony_ci	u32 size = FIFO_SIZE * sizeof(u32);
18662306a36Sopenharmony_ci	u32 *buf;
18762306a36Sopenharmony_ci	int rc;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	buf = kzalloc(size, GFP_KERNEL);
19062306a36Sopenharmony_ci	if (!buf)
19162306a36Sopenharmony_ci		return -ENOMEM;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	rc = amd_pmc_read_stb(dev, buf);
19462306a36Sopenharmony_ci	if (rc) {
19562306a36Sopenharmony_ci		kfree(buf);
19662306a36Sopenharmony_ci		return rc;
19762306a36Sopenharmony_ci	}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	filp->private_data = buf;
20062306a36Sopenharmony_ci	return rc;
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic ssize_t amd_pmc_stb_debugfs_read(struct file *filp, char __user *buf, size_t size,
20462306a36Sopenharmony_ci					loff_t *pos)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	if (!filp->private_data)
20762306a36Sopenharmony_ci		return -EINVAL;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	return simple_read_from_buffer(buf, size, pos, filp->private_data,
21062306a36Sopenharmony_ci				       FIFO_SIZE * sizeof(u32));
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic int amd_pmc_stb_debugfs_release(struct inode *inode, struct file *filp)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	kfree(filp->private_data);
21662306a36Sopenharmony_ci	return 0;
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic const struct file_operations amd_pmc_stb_debugfs_fops = {
22062306a36Sopenharmony_ci	.owner = THIS_MODULE,
22162306a36Sopenharmony_ci	.open = amd_pmc_stb_debugfs_open,
22262306a36Sopenharmony_ci	.read = amd_pmc_stb_debugfs_read,
22362306a36Sopenharmony_ci	.release = amd_pmc_stb_debugfs_release,
22462306a36Sopenharmony_ci};
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic int amd_pmc_stb_debugfs_open_v2(struct inode *inode, struct file *filp)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	struct amd_pmc_dev *dev = filp->f_inode->i_private;
22962306a36Sopenharmony_ci	u32 *buf, fsize, num_samples, stb_rdptr_offset = 0;
23062306a36Sopenharmony_ci	int ret;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	/* Write dummy postcode while reading the STB buffer */
23362306a36Sopenharmony_ci	ret = amd_pmc_write_stb(dev, AMD_PMC_STB_DUMMY_PC);
23462306a36Sopenharmony_ci	if (ret)
23562306a36Sopenharmony_ci		dev_err(dev->dev, "error writing to STB: %d\n", ret);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	buf = kzalloc(S2D_TELEMETRY_BYTES_MAX, GFP_KERNEL);
23862306a36Sopenharmony_ci	if (!buf)
23962306a36Sopenharmony_ci		return -ENOMEM;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	/* Spill to DRAM num_samples uses separate SMU message port */
24262306a36Sopenharmony_ci	dev->msg_port = 1;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	/* Get the num_samples to calculate the last push location */
24562306a36Sopenharmony_ci	ret = amd_pmc_send_cmd(dev, S2D_NUM_SAMPLES, &num_samples, dev->s2d_msg_id, true);
24662306a36Sopenharmony_ci	/* Clear msg_port for other SMU operation */
24762306a36Sopenharmony_ci	dev->msg_port = 0;
24862306a36Sopenharmony_ci	if (ret) {
24962306a36Sopenharmony_ci		dev_err(dev->dev, "error: S2D_NUM_SAMPLES not supported : %d\n", ret);
25062306a36Sopenharmony_ci		kfree(buf);
25162306a36Sopenharmony_ci		return ret;
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	/* Start capturing data from the last push location */
25562306a36Sopenharmony_ci	if (num_samples > S2D_TELEMETRY_BYTES_MAX) {
25662306a36Sopenharmony_ci		fsize  = S2D_TELEMETRY_BYTES_MAX;
25762306a36Sopenharmony_ci		stb_rdptr_offset = num_samples - fsize;
25862306a36Sopenharmony_ci	} else {
25962306a36Sopenharmony_ci		fsize = num_samples;
26062306a36Sopenharmony_ci		stb_rdptr_offset = 0;
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	memcpy_fromio(buf, dev->stb_virt_addr + stb_rdptr_offset, fsize);
26462306a36Sopenharmony_ci	filp->private_data = buf;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	return 0;
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic ssize_t amd_pmc_stb_debugfs_read_v2(struct file *filp, char __user *buf, size_t size,
27062306a36Sopenharmony_ci					   loff_t *pos)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	if (!filp->private_data)
27362306a36Sopenharmony_ci		return -EINVAL;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	return simple_read_from_buffer(buf, size, pos, filp->private_data,
27662306a36Sopenharmony_ci					S2D_TELEMETRY_BYTES_MAX);
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cistatic int amd_pmc_stb_debugfs_release_v2(struct inode *inode, struct file *filp)
28062306a36Sopenharmony_ci{
28162306a36Sopenharmony_ci	kfree(filp->private_data);
28262306a36Sopenharmony_ci	return 0;
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic const struct file_operations amd_pmc_stb_debugfs_fops_v2 = {
28662306a36Sopenharmony_ci	.owner = THIS_MODULE,
28762306a36Sopenharmony_ci	.open = amd_pmc_stb_debugfs_open_v2,
28862306a36Sopenharmony_ci	.read = amd_pmc_stb_debugfs_read_v2,
28962306a36Sopenharmony_ci	.release = amd_pmc_stb_debugfs_release_v2,
29062306a36Sopenharmony_ci};
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_cistatic void amd_pmc_get_ip_info(struct amd_pmc_dev *dev)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	switch (dev->cpu_id) {
29562306a36Sopenharmony_ci	case AMD_CPU_ID_PCO:
29662306a36Sopenharmony_ci	case AMD_CPU_ID_RN:
29762306a36Sopenharmony_ci	case AMD_CPU_ID_YC:
29862306a36Sopenharmony_ci	case AMD_CPU_ID_CB:
29962306a36Sopenharmony_ci		dev->num_ips = 12;
30062306a36Sopenharmony_ci		dev->s2d_msg_id = 0xBE;
30162306a36Sopenharmony_ci		break;
30262306a36Sopenharmony_ci	case AMD_CPU_ID_PS:
30362306a36Sopenharmony_ci		dev->num_ips = 21;
30462306a36Sopenharmony_ci		dev->s2d_msg_id = 0x85;
30562306a36Sopenharmony_ci		break;
30662306a36Sopenharmony_ci	}
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_cistatic int amd_pmc_setup_smu_logging(struct amd_pmc_dev *dev)
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci	if (dev->cpu_id == AMD_CPU_ID_PCO) {
31262306a36Sopenharmony_ci		dev_warn_once(dev->dev, "SMU debugging info not supported on this platform\n");
31362306a36Sopenharmony_ci		return -EINVAL;
31462306a36Sopenharmony_ci	}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	/* Get Active devices list from SMU */
31762306a36Sopenharmony_ci	if (!dev->active_ips)
31862306a36Sopenharmony_ci		amd_pmc_send_cmd(dev, 0, &dev->active_ips, SMU_MSG_GET_SUP_CONSTRAINTS, true);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	/* Get dram address */
32162306a36Sopenharmony_ci	if (!dev->smu_virt_addr) {
32262306a36Sopenharmony_ci		u32 phys_addr_low, phys_addr_hi;
32362306a36Sopenharmony_ci		u64 smu_phys_addr;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci		amd_pmc_send_cmd(dev, 0, &phys_addr_low, SMU_MSG_LOG_GETDRAM_ADDR_LO, true);
32662306a36Sopenharmony_ci		amd_pmc_send_cmd(dev, 0, &phys_addr_hi, SMU_MSG_LOG_GETDRAM_ADDR_HI, true);
32762306a36Sopenharmony_ci		smu_phys_addr = ((u64)phys_addr_hi << 32 | phys_addr_low);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci		dev->smu_virt_addr = devm_ioremap(dev->dev, smu_phys_addr,
33062306a36Sopenharmony_ci						  sizeof(struct smu_metrics));
33162306a36Sopenharmony_ci		if (!dev->smu_virt_addr)
33262306a36Sopenharmony_ci			return -ENOMEM;
33362306a36Sopenharmony_ci	}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	/* Start the logging */
33662306a36Sopenharmony_ci	amd_pmc_send_cmd(dev, 0, NULL, SMU_MSG_LOG_RESET, false);
33762306a36Sopenharmony_ci	amd_pmc_send_cmd(dev, 0, NULL, SMU_MSG_LOG_START, false);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	return 0;
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cistatic int get_metrics_table(struct amd_pmc_dev *pdev, struct smu_metrics *table)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	if (!pdev->smu_virt_addr) {
34562306a36Sopenharmony_ci		int ret = amd_pmc_setup_smu_logging(pdev);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci		if (ret)
34862306a36Sopenharmony_ci			return ret;
34962306a36Sopenharmony_ci	}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	if (pdev->cpu_id == AMD_CPU_ID_PCO)
35262306a36Sopenharmony_ci		return -ENODEV;
35362306a36Sopenharmony_ci	memcpy_fromio(table, pdev->smu_virt_addr, sizeof(struct smu_metrics));
35462306a36Sopenharmony_ci	return 0;
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic void amd_pmc_validate_deepest(struct amd_pmc_dev *pdev)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	struct smu_metrics table;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	if (get_metrics_table(pdev, &table))
36262306a36Sopenharmony_ci		return;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	if (!table.s0i3_last_entry_status)
36562306a36Sopenharmony_ci		dev_warn(pdev->dev, "Last suspend didn't reach deepest state\n");
36662306a36Sopenharmony_ci	pm_report_hw_sleep_time(table.s0i3_last_entry_status ?
36762306a36Sopenharmony_ci				table.timein_s0i3_lastcapture : 0);
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic int amd_pmc_get_smu_version(struct amd_pmc_dev *dev)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	int rc;
37362306a36Sopenharmony_ci	u32 val;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	if (dev->cpu_id == AMD_CPU_ID_PCO)
37662306a36Sopenharmony_ci		return -ENODEV;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	rc = amd_pmc_send_cmd(dev, 0, &val, SMU_MSG_GETSMUVERSION, true);
37962306a36Sopenharmony_ci	if (rc)
38062306a36Sopenharmony_ci		return rc;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	dev->smu_program = (val >> 24) & GENMASK(7, 0);
38362306a36Sopenharmony_ci	dev->major = (val >> 16) & GENMASK(7, 0);
38462306a36Sopenharmony_ci	dev->minor = (val >> 8) & GENMASK(7, 0);
38562306a36Sopenharmony_ci	dev->rev = (val >> 0) & GENMASK(7, 0);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	dev_dbg(dev->dev, "SMU program %u version is %u.%u.%u\n",
38862306a36Sopenharmony_ci		dev->smu_program, dev->major, dev->minor, dev->rev);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	return 0;
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_cistatic ssize_t smu_fw_version_show(struct device *d, struct device_attribute *attr,
39462306a36Sopenharmony_ci				   char *buf)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci	struct amd_pmc_dev *dev = dev_get_drvdata(d);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	if (!dev->major) {
39962306a36Sopenharmony_ci		int rc = amd_pmc_get_smu_version(dev);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci		if (rc)
40262306a36Sopenharmony_ci			return rc;
40362306a36Sopenharmony_ci	}
40462306a36Sopenharmony_ci	return sysfs_emit(buf, "%u.%u.%u\n", dev->major, dev->minor, dev->rev);
40562306a36Sopenharmony_ci}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_cistatic ssize_t smu_program_show(struct device *d, struct device_attribute *attr,
40862306a36Sopenharmony_ci				   char *buf)
40962306a36Sopenharmony_ci{
41062306a36Sopenharmony_ci	struct amd_pmc_dev *dev = dev_get_drvdata(d);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	if (!dev->major) {
41362306a36Sopenharmony_ci		int rc = amd_pmc_get_smu_version(dev);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci		if (rc)
41662306a36Sopenharmony_ci			return rc;
41762306a36Sopenharmony_ci	}
41862306a36Sopenharmony_ci	return sysfs_emit(buf, "%u\n", dev->smu_program);
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(smu_fw_version);
42262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(smu_program);
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic umode_t pmc_attr_is_visible(struct kobject *kobj, struct attribute *attr, int idx)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	struct device *dev = kobj_to_dev(kobj);
42762306a36Sopenharmony_ci	struct amd_pmc_dev *pdev = dev_get_drvdata(dev);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	if (pdev->cpu_id == AMD_CPU_ID_PCO)
43062306a36Sopenharmony_ci		return 0;
43162306a36Sopenharmony_ci	return 0444;
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_cistatic struct attribute *pmc_attrs[] = {
43562306a36Sopenharmony_ci	&dev_attr_smu_fw_version.attr,
43662306a36Sopenharmony_ci	&dev_attr_smu_program.attr,
43762306a36Sopenharmony_ci	NULL,
43862306a36Sopenharmony_ci};
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_cistatic struct attribute_group pmc_attr_group = {
44162306a36Sopenharmony_ci	.attrs = pmc_attrs,
44262306a36Sopenharmony_ci	.is_visible = pmc_attr_is_visible,
44362306a36Sopenharmony_ci};
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_cistatic const struct attribute_group *pmc_groups[] = {
44662306a36Sopenharmony_ci	&pmc_attr_group,
44762306a36Sopenharmony_ci	NULL,
44862306a36Sopenharmony_ci};
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_cistatic int smu_fw_info_show(struct seq_file *s, void *unused)
45162306a36Sopenharmony_ci{
45262306a36Sopenharmony_ci	struct amd_pmc_dev *dev = s->private;
45362306a36Sopenharmony_ci	struct smu_metrics table;
45462306a36Sopenharmony_ci	int idx;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	if (get_metrics_table(dev, &table))
45762306a36Sopenharmony_ci		return -EINVAL;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	seq_puts(s, "\n=== SMU Statistics ===\n");
46062306a36Sopenharmony_ci	seq_printf(s, "Table Version: %d\n", table.table_version);
46162306a36Sopenharmony_ci	seq_printf(s, "Hint Count: %d\n", table.hint_count);
46262306a36Sopenharmony_ci	seq_printf(s, "Last S0i3 Status: %s\n", table.s0i3_last_entry_status ? "Success" :
46362306a36Sopenharmony_ci		   "Unknown/Fail");
46462306a36Sopenharmony_ci	seq_printf(s, "Time (in us) to S0i3: %lld\n", table.timeentering_s0i3_lastcapture);
46562306a36Sopenharmony_ci	seq_printf(s, "Time (in us) in S0i3: %lld\n", table.timein_s0i3_lastcapture);
46662306a36Sopenharmony_ci	seq_printf(s, "Time (in us) to resume from S0i3: %lld\n",
46762306a36Sopenharmony_ci		   table.timeto_resume_to_os_lastcapture);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	seq_puts(s, "\n=== Active time (in us) ===\n");
47062306a36Sopenharmony_ci	for (idx = 0 ; idx < dev->num_ips ; idx++) {
47162306a36Sopenharmony_ci		if (soc15_ip_blk[idx].bit_mask & dev->active_ips)
47262306a36Sopenharmony_ci			seq_printf(s, "%-8s : %lld\n", soc15_ip_blk[idx].name,
47362306a36Sopenharmony_ci				   table.timecondition_notmet_lastcapture[idx]);
47462306a36Sopenharmony_ci	}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	return 0;
47762306a36Sopenharmony_ci}
47862306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(smu_fw_info);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_cistatic int s0ix_stats_show(struct seq_file *s, void *unused)
48162306a36Sopenharmony_ci{
48262306a36Sopenharmony_ci	struct amd_pmc_dev *dev = s->private;
48362306a36Sopenharmony_ci	u64 entry_time, exit_time, residency;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	/* Use FCH registers to get the S0ix stats */
48662306a36Sopenharmony_ci	if (!dev->fch_virt_addr) {
48762306a36Sopenharmony_ci		u32 base_addr_lo = FCH_BASE_PHY_ADDR_LOW;
48862306a36Sopenharmony_ci		u32 base_addr_hi = FCH_BASE_PHY_ADDR_HIGH;
48962306a36Sopenharmony_ci		u64 fch_phys_addr = ((u64)base_addr_hi << 32 | base_addr_lo);
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci		dev->fch_virt_addr = devm_ioremap(dev->dev, fch_phys_addr, FCH_SSC_MAPPING_SIZE);
49262306a36Sopenharmony_ci		if (!dev->fch_virt_addr)
49362306a36Sopenharmony_ci			return -ENOMEM;
49462306a36Sopenharmony_ci	}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	entry_time = ioread32(dev->fch_virt_addr + FCH_S0I3_ENTRY_TIME_H_OFFSET);
49762306a36Sopenharmony_ci	entry_time = entry_time << 32 | ioread32(dev->fch_virt_addr + FCH_S0I3_ENTRY_TIME_L_OFFSET);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	exit_time = ioread32(dev->fch_virt_addr + FCH_S0I3_EXIT_TIME_H_OFFSET);
50062306a36Sopenharmony_ci	exit_time = exit_time << 32 | ioread32(dev->fch_virt_addr + FCH_S0I3_EXIT_TIME_L_OFFSET);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	/* It's in 48MHz. We need to convert it */
50362306a36Sopenharmony_ci	residency = exit_time - entry_time;
50462306a36Sopenharmony_ci	do_div(residency, 48);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	seq_puts(s, "=== S0ix statistics ===\n");
50762306a36Sopenharmony_ci	seq_printf(s, "S0ix Entry Time: %lld\n", entry_time);
50862306a36Sopenharmony_ci	seq_printf(s, "S0ix Exit Time: %lld\n", exit_time);
50962306a36Sopenharmony_ci	seq_printf(s, "Residency Time: %lld\n", residency);
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	return 0;
51262306a36Sopenharmony_ci}
51362306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(s0ix_stats);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_cistatic int amd_pmc_idlemask_read(struct amd_pmc_dev *pdev, struct device *dev,
51662306a36Sopenharmony_ci				 struct seq_file *s)
51762306a36Sopenharmony_ci{
51862306a36Sopenharmony_ci	u32 val;
51962306a36Sopenharmony_ci	int rc;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	switch (pdev->cpu_id) {
52262306a36Sopenharmony_ci	case AMD_CPU_ID_CZN:
52362306a36Sopenharmony_ci		/* we haven't yet read SMU version */
52462306a36Sopenharmony_ci		if (!pdev->major) {
52562306a36Sopenharmony_ci			rc = amd_pmc_get_smu_version(pdev);
52662306a36Sopenharmony_ci			if (rc)
52762306a36Sopenharmony_ci				return rc;
52862306a36Sopenharmony_ci		}
52962306a36Sopenharmony_ci		if (pdev->major > 56 || (pdev->major >= 55 && pdev->minor >= 37))
53062306a36Sopenharmony_ci			val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_CZN);
53162306a36Sopenharmony_ci		else
53262306a36Sopenharmony_ci			return -EINVAL;
53362306a36Sopenharmony_ci		break;
53462306a36Sopenharmony_ci	case AMD_CPU_ID_YC:
53562306a36Sopenharmony_ci	case AMD_CPU_ID_CB:
53662306a36Sopenharmony_ci	case AMD_CPU_ID_PS:
53762306a36Sopenharmony_ci		val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_YC);
53862306a36Sopenharmony_ci		break;
53962306a36Sopenharmony_ci	default:
54062306a36Sopenharmony_ci		return -EINVAL;
54162306a36Sopenharmony_ci	}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	if (dev)
54462306a36Sopenharmony_ci		pm_pr_dbg("SMU idlemask s0i3: 0x%x\n", val);
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	if (s)
54762306a36Sopenharmony_ci		seq_printf(s, "SMU idlemask : 0x%x\n", val);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	return 0;
55062306a36Sopenharmony_ci}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_cistatic int amd_pmc_idlemask_show(struct seq_file *s, void *unused)
55362306a36Sopenharmony_ci{
55462306a36Sopenharmony_ci	return amd_pmc_idlemask_read(s->private, NULL, s);
55562306a36Sopenharmony_ci}
55662306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(amd_pmc_idlemask);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_cistatic void amd_pmc_dbgfs_unregister(struct amd_pmc_dev *dev)
55962306a36Sopenharmony_ci{
56062306a36Sopenharmony_ci	debugfs_remove_recursive(dev->dbgfs_dir);
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_cistatic bool amd_pmc_is_stb_supported(struct amd_pmc_dev *dev)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	switch (dev->cpu_id) {
56662306a36Sopenharmony_ci	case AMD_CPU_ID_YC:
56762306a36Sopenharmony_ci	case AMD_CPU_ID_CB:
56862306a36Sopenharmony_ci	case AMD_CPU_ID_PS:
56962306a36Sopenharmony_ci		return true;
57062306a36Sopenharmony_ci	default:
57162306a36Sopenharmony_ci		return false;
57262306a36Sopenharmony_ci	}
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_cistatic void amd_pmc_dbgfs_register(struct amd_pmc_dev *dev)
57662306a36Sopenharmony_ci{
57762306a36Sopenharmony_ci	dev->dbgfs_dir = debugfs_create_dir("amd_pmc", NULL);
57862306a36Sopenharmony_ci	debugfs_create_file("smu_fw_info", 0644, dev->dbgfs_dir, dev,
57962306a36Sopenharmony_ci			    &smu_fw_info_fops);
58062306a36Sopenharmony_ci	debugfs_create_file("s0ix_stats", 0644, dev->dbgfs_dir, dev,
58162306a36Sopenharmony_ci			    &s0ix_stats_fops);
58262306a36Sopenharmony_ci	debugfs_create_file("amd_pmc_idlemask", 0644, dev->dbgfs_dir, dev,
58362306a36Sopenharmony_ci			    &amd_pmc_idlemask_fops);
58462306a36Sopenharmony_ci	/* Enable STB only when the module_param is set */
58562306a36Sopenharmony_ci	if (enable_stb) {
58662306a36Sopenharmony_ci		if (amd_pmc_is_stb_supported(dev))
58762306a36Sopenharmony_ci			debugfs_create_file("stb_read", 0644, dev->dbgfs_dir, dev,
58862306a36Sopenharmony_ci					    &amd_pmc_stb_debugfs_fops_v2);
58962306a36Sopenharmony_ci		else
59062306a36Sopenharmony_ci			debugfs_create_file("stb_read", 0644, dev->dbgfs_dir, dev,
59162306a36Sopenharmony_ci					    &amd_pmc_stb_debugfs_fops);
59262306a36Sopenharmony_ci	}
59362306a36Sopenharmony_ci}
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_cistatic void amd_pmc_dump_registers(struct amd_pmc_dev *dev)
59662306a36Sopenharmony_ci{
59762306a36Sopenharmony_ci	u32 value, message, argument, response;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	if (dev->msg_port) {
60062306a36Sopenharmony_ci		message = AMD_S2D_REGISTER_MESSAGE;
60162306a36Sopenharmony_ci		argument = AMD_S2D_REGISTER_ARGUMENT;
60262306a36Sopenharmony_ci		response = AMD_S2D_REGISTER_RESPONSE;
60362306a36Sopenharmony_ci	} else {
60462306a36Sopenharmony_ci		message = AMD_PMC_REGISTER_MESSAGE;
60562306a36Sopenharmony_ci		argument = AMD_PMC_REGISTER_ARGUMENT;
60662306a36Sopenharmony_ci		response = AMD_PMC_REGISTER_RESPONSE;
60762306a36Sopenharmony_ci	}
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	value = amd_pmc_reg_read(dev, response);
61062306a36Sopenharmony_ci	dev_dbg(dev->dev, "AMD_%s_REGISTER_RESPONSE:%x\n", dev->msg_port ? "S2D" : "PMC", value);
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	value = amd_pmc_reg_read(dev, argument);
61362306a36Sopenharmony_ci	dev_dbg(dev->dev, "AMD_%s_REGISTER_ARGUMENT:%x\n", dev->msg_port ? "S2D" : "PMC", value);
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	value = amd_pmc_reg_read(dev, message);
61662306a36Sopenharmony_ci	dev_dbg(dev->dev, "AMD_%s_REGISTER_MESSAGE:%x\n", dev->msg_port ? "S2D" : "PMC", value);
61762306a36Sopenharmony_ci}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_cistatic int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool ret)
62062306a36Sopenharmony_ci{
62162306a36Sopenharmony_ci	int rc;
62262306a36Sopenharmony_ci	u32 val, message, argument, response;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	mutex_lock(&dev->lock);
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	if (dev->msg_port) {
62762306a36Sopenharmony_ci		message = AMD_S2D_REGISTER_MESSAGE;
62862306a36Sopenharmony_ci		argument = AMD_S2D_REGISTER_ARGUMENT;
62962306a36Sopenharmony_ci		response = AMD_S2D_REGISTER_RESPONSE;
63062306a36Sopenharmony_ci	} else {
63162306a36Sopenharmony_ci		message = AMD_PMC_REGISTER_MESSAGE;
63262306a36Sopenharmony_ci		argument = AMD_PMC_REGISTER_ARGUMENT;
63362306a36Sopenharmony_ci		response = AMD_PMC_REGISTER_RESPONSE;
63462306a36Sopenharmony_ci	}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	/* Wait until we get a valid response */
63762306a36Sopenharmony_ci	rc = readx_poll_timeout(ioread32, dev->regbase + response,
63862306a36Sopenharmony_ci				val, val != 0, PMC_MSG_DELAY_MIN_US,
63962306a36Sopenharmony_ci				PMC_MSG_DELAY_MIN_US * RESPONSE_REGISTER_LOOP_MAX);
64062306a36Sopenharmony_ci	if (rc) {
64162306a36Sopenharmony_ci		dev_err(dev->dev, "failed to talk to SMU\n");
64262306a36Sopenharmony_ci		goto out_unlock;
64362306a36Sopenharmony_ci	}
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	/* Write zero to response register */
64662306a36Sopenharmony_ci	amd_pmc_reg_write(dev, response, 0);
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	/* Write argument into response register */
64962306a36Sopenharmony_ci	amd_pmc_reg_write(dev, argument, arg);
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	/* Write message ID to message ID register */
65262306a36Sopenharmony_ci	amd_pmc_reg_write(dev, message, msg);
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	/* Wait until we get a valid response */
65562306a36Sopenharmony_ci	rc = readx_poll_timeout(ioread32, dev->regbase + response,
65662306a36Sopenharmony_ci				val, val != 0, PMC_MSG_DELAY_MIN_US,
65762306a36Sopenharmony_ci				PMC_MSG_DELAY_MIN_US * RESPONSE_REGISTER_LOOP_MAX);
65862306a36Sopenharmony_ci	if (rc) {
65962306a36Sopenharmony_ci		dev_err(dev->dev, "SMU response timed out\n");
66062306a36Sopenharmony_ci		goto out_unlock;
66162306a36Sopenharmony_ci	}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	switch (val) {
66462306a36Sopenharmony_ci	case AMD_PMC_RESULT_OK:
66562306a36Sopenharmony_ci		if (ret) {
66662306a36Sopenharmony_ci			/* PMFW may take longer time to return back the data */
66762306a36Sopenharmony_ci			usleep_range(DELAY_MIN_US, 10 * DELAY_MAX_US);
66862306a36Sopenharmony_ci			*data = amd_pmc_reg_read(dev, argument);
66962306a36Sopenharmony_ci		}
67062306a36Sopenharmony_ci		break;
67162306a36Sopenharmony_ci	case AMD_PMC_RESULT_CMD_REJECT_BUSY:
67262306a36Sopenharmony_ci		dev_err(dev->dev, "SMU not ready. err: 0x%x\n", val);
67362306a36Sopenharmony_ci		rc = -EBUSY;
67462306a36Sopenharmony_ci		goto out_unlock;
67562306a36Sopenharmony_ci	case AMD_PMC_RESULT_CMD_UNKNOWN:
67662306a36Sopenharmony_ci		dev_err(dev->dev, "SMU cmd unknown. err: 0x%x\n", val);
67762306a36Sopenharmony_ci		rc = -EINVAL;
67862306a36Sopenharmony_ci		goto out_unlock;
67962306a36Sopenharmony_ci	case AMD_PMC_RESULT_CMD_REJECT_PREREQ:
68062306a36Sopenharmony_ci	case AMD_PMC_RESULT_FAILED:
68162306a36Sopenharmony_ci	default:
68262306a36Sopenharmony_ci		dev_err(dev->dev, "SMU cmd failed. err: 0x%x\n", val);
68362306a36Sopenharmony_ci		rc = -EIO;
68462306a36Sopenharmony_ci		goto out_unlock;
68562306a36Sopenharmony_ci	}
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ciout_unlock:
68862306a36Sopenharmony_ci	mutex_unlock(&dev->lock);
68962306a36Sopenharmony_ci	amd_pmc_dump_registers(dev);
69062306a36Sopenharmony_ci	return rc;
69162306a36Sopenharmony_ci}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_cistatic int amd_pmc_get_os_hint(struct amd_pmc_dev *dev)
69462306a36Sopenharmony_ci{
69562306a36Sopenharmony_ci	switch (dev->cpu_id) {
69662306a36Sopenharmony_ci	case AMD_CPU_ID_PCO:
69762306a36Sopenharmony_ci		return MSG_OS_HINT_PCO;
69862306a36Sopenharmony_ci	case AMD_CPU_ID_RN:
69962306a36Sopenharmony_ci	case AMD_CPU_ID_YC:
70062306a36Sopenharmony_ci	case AMD_CPU_ID_CB:
70162306a36Sopenharmony_ci	case AMD_CPU_ID_PS:
70262306a36Sopenharmony_ci		return MSG_OS_HINT_RN;
70362306a36Sopenharmony_ci	}
70462306a36Sopenharmony_ci	return -EINVAL;
70562306a36Sopenharmony_ci}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_cistatic int amd_pmc_wa_irq1(struct amd_pmc_dev *pdev)
70862306a36Sopenharmony_ci{
70962306a36Sopenharmony_ci	struct device *d;
71062306a36Sopenharmony_ci	int rc;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	/* cezanne platform firmware has a fix in 64.66.0 */
71362306a36Sopenharmony_ci	if (pdev->cpu_id == AMD_CPU_ID_CZN) {
71462306a36Sopenharmony_ci		if (!pdev->major) {
71562306a36Sopenharmony_ci			rc = amd_pmc_get_smu_version(pdev);
71662306a36Sopenharmony_ci			if (rc)
71762306a36Sopenharmony_ci				return rc;
71862306a36Sopenharmony_ci		}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci		if (pdev->major > 64 || (pdev->major == 64 && pdev->minor > 65))
72162306a36Sopenharmony_ci			return 0;
72262306a36Sopenharmony_ci	}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	d = bus_find_device_by_name(&serio_bus, NULL, "serio0");
72562306a36Sopenharmony_ci	if (!d)
72662306a36Sopenharmony_ci		return 0;
72762306a36Sopenharmony_ci	if (device_may_wakeup(d)) {
72862306a36Sopenharmony_ci		dev_info_once(d, "Disabling IRQ1 wakeup source to avoid platform firmware bug\n");
72962306a36Sopenharmony_ci		disable_irq_wake(1);
73062306a36Sopenharmony_ci		device_set_wakeup_enable(d, false);
73162306a36Sopenharmony_ci	}
73262306a36Sopenharmony_ci	put_device(d);
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	return 0;
73562306a36Sopenharmony_ci}
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_cistatic int amd_pmc_verify_czn_rtc(struct amd_pmc_dev *pdev, u32 *arg)
73862306a36Sopenharmony_ci{
73962306a36Sopenharmony_ci	struct rtc_device *rtc_device;
74062306a36Sopenharmony_ci	time64_t then, now, duration;
74162306a36Sopenharmony_ci	struct rtc_wkalrm alarm;
74262306a36Sopenharmony_ci	struct rtc_time tm;
74362306a36Sopenharmony_ci	int rc;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	/* we haven't yet read SMU version */
74662306a36Sopenharmony_ci	if (!pdev->major) {
74762306a36Sopenharmony_ci		rc = amd_pmc_get_smu_version(pdev);
74862306a36Sopenharmony_ci		if (rc)
74962306a36Sopenharmony_ci			return rc;
75062306a36Sopenharmony_ci	}
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	if (pdev->major < 64 || (pdev->major == 64 && pdev->minor < 53))
75362306a36Sopenharmony_ci		return 0;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	rtc_device = rtc_class_open("rtc0");
75662306a36Sopenharmony_ci	if (!rtc_device)
75762306a36Sopenharmony_ci		return 0;
75862306a36Sopenharmony_ci	rc = rtc_read_alarm(rtc_device, &alarm);
75962306a36Sopenharmony_ci	if (rc)
76062306a36Sopenharmony_ci		return rc;
76162306a36Sopenharmony_ci	if (!alarm.enabled) {
76262306a36Sopenharmony_ci		dev_dbg(pdev->dev, "alarm not enabled\n");
76362306a36Sopenharmony_ci		return 0;
76462306a36Sopenharmony_ci	}
76562306a36Sopenharmony_ci	rc = rtc_read_time(rtc_device, &tm);
76662306a36Sopenharmony_ci	if (rc)
76762306a36Sopenharmony_ci		return rc;
76862306a36Sopenharmony_ci	then = rtc_tm_to_time64(&alarm.time);
76962306a36Sopenharmony_ci	now = rtc_tm_to_time64(&tm);
77062306a36Sopenharmony_ci	duration = then-now;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	/* in the past */
77362306a36Sopenharmony_ci	if (then < now)
77462306a36Sopenharmony_ci		return 0;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	/* will be stored in upper 16 bits of s0i3 hint argument,
77762306a36Sopenharmony_ci	 * so timer wakeup from s0i3 is limited to ~18 hours or less
77862306a36Sopenharmony_ci	 */
77962306a36Sopenharmony_ci	if (duration <= 4 || duration > U16_MAX)
78062306a36Sopenharmony_ci		return -EINVAL;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	*arg |= (duration << 16);
78362306a36Sopenharmony_ci	rc = rtc_alarm_irq_enable(rtc_device, 0);
78462306a36Sopenharmony_ci	pm_pr_dbg("wakeup timer programmed for %lld seconds\n", duration);
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	return rc;
78762306a36Sopenharmony_ci}
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_cistatic void amd_pmc_s2idle_prepare(void)
79062306a36Sopenharmony_ci{
79162306a36Sopenharmony_ci	struct amd_pmc_dev *pdev = &pmc;
79262306a36Sopenharmony_ci	int rc;
79362306a36Sopenharmony_ci	u8 msg;
79462306a36Sopenharmony_ci	u32 arg = 1;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	/* Reset and Start SMU logging - to monitor the s0i3 stats */
79762306a36Sopenharmony_ci	amd_pmc_setup_smu_logging(pdev);
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	/* Activate CZN specific platform bug workarounds */
80062306a36Sopenharmony_ci	if (pdev->cpu_id == AMD_CPU_ID_CZN && !disable_workarounds) {
80162306a36Sopenharmony_ci		rc = amd_pmc_verify_czn_rtc(pdev, &arg);
80262306a36Sopenharmony_ci		if (rc) {
80362306a36Sopenharmony_ci			dev_err(pdev->dev, "failed to set RTC: %d\n", rc);
80462306a36Sopenharmony_ci			return;
80562306a36Sopenharmony_ci		}
80662306a36Sopenharmony_ci	}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	msg = amd_pmc_get_os_hint(pdev);
80962306a36Sopenharmony_ci	rc = amd_pmc_send_cmd(pdev, arg, NULL, msg, false);
81062306a36Sopenharmony_ci	if (rc) {
81162306a36Sopenharmony_ci		dev_err(pdev->dev, "suspend failed: %d\n", rc);
81262306a36Sopenharmony_ci		return;
81362306a36Sopenharmony_ci	}
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	rc = amd_pmc_write_stb(pdev, AMD_PMC_STB_S2IDLE_PREPARE);
81662306a36Sopenharmony_ci	if (rc)
81762306a36Sopenharmony_ci		dev_err(pdev->dev, "error writing to STB: %d\n", rc);
81862306a36Sopenharmony_ci}
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_cistatic void amd_pmc_s2idle_check(void)
82162306a36Sopenharmony_ci{
82262306a36Sopenharmony_ci	struct amd_pmc_dev *pdev = &pmc;
82362306a36Sopenharmony_ci	struct smu_metrics table;
82462306a36Sopenharmony_ci	int rc;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	/* CZN: Ensure that future s0i3 entry attempts at least 10ms passed */
82762306a36Sopenharmony_ci	if (pdev->cpu_id == AMD_CPU_ID_CZN && !get_metrics_table(pdev, &table) &&
82862306a36Sopenharmony_ci	    table.s0i3_last_entry_status)
82962306a36Sopenharmony_ci		usleep_range(10000, 20000);
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	/* Dump the IdleMask before we add to the STB */
83262306a36Sopenharmony_ci	amd_pmc_idlemask_read(pdev, pdev->dev, NULL);
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	rc = amd_pmc_write_stb(pdev, AMD_PMC_STB_S2IDLE_CHECK);
83562306a36Sopenharmony_ci	if (rc)
83662306a36Sopenharmony_ci		dev_err(pdev->dev, "error writing to STB: %d\n", rc);
83762306a36Sopenharmony_ci}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_cistatic int amd_pmc_dump_data(struct amd_pmc_dev *pdev)
84062306a36Sopenharmony_ci{
84162306a36Sopenharmony_ci	if (pdev->cpu_id == AMD_CPU_ID_PCO)
84262306a36Sopenharmony_ci		return -ENODEV;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	return amd_pmc_send_cmd(pdev, 0, NULL, SMU_MSG_LOG_DUMP_DATA, false);
84562306a36Sopenharmony_ci}
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_cistatic void amd_pmc_s2idle_restore(void)
84862306a36Sopenharmony_ci{
84962306a36Sopenharmony_ci	struct amd_pmc_dev *pdev = &pmc;
85062306a36Sopenharmony_ci	int rc;
85162306a36Sopenharmony_ci	u8 msg;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	msg = amd_pmc_get_os_hint(pdev);
85462306a36Sopenharmony_ci	rc = amd_pmc_send_cmd(pdev, 0, NULL, msg, false);
85562306a36Sopenharmony_ci	if (rc)
85662306a36Sopenharmony_ci		dev_err(pdev->dev, "resume failed: %d\n", rc);
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	/* Let SMU know that we are looking for stats */
85962306a36Sopenharmony_ci	amd_pmc_dump_data(pdev);
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	rc = amd_pmc_write_stb(pdev, AMD_PMC_STB_S2IDLE_RESTORE);
86262306a36Sopenharmony_ci	if (rc)
86362306a36Sopenharmony_ci		dev_err(pdev->dev, "error writing to STB: %d\n", rc);
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	/* Notify on failed entry */
86662306a36Sopenharmony_ci	amd_pmc_validate_deepest(pdev);
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	amd_pmc_process_restore_quirks(pdev);
86962306a36Sopenharmony_ci}
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_cistatic struct acpi_s2idle_dev_ops amd_pmc_s2idle_dev_ops = {
87262306a36Sopenharmony_ci	.prepare = amd_pmc_s2idle_prepare,
87362306a36Sopenharmony_ci	.check = amd_pmc_s2idle_check,
87462306a36Sopenharmony_ci	.restore = amd_pmc_s2idle_restore,
87562306a36Sopenharmony_ci};
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_cistatic int amd_pmc_suspend_handler(struct device *dev)
87862306a36Sopenharmony_ci{
87962306a36Sopenharmony_ci	struct amd_pmc_dev *pdev = dev_get_drvdata(dev);
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	if (pdev->disable_8042_wakeup && !disable_workarounds) {
88262306a36Sopenharmony_ci		int rc = amd_pmc_wa_irq1(pdev);
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci		if (rc) {
88562306a36Sopenharmony_ci			dev_err(pdev->dev, "failed to adjust keyboard wakeup: %d\n", rc);
88662306a36Sopenharmony_ci			return rc;
88762306a36Sopenharmony_ci		}
88862306a36Sopenharmony_ci	}
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	return 0;
89162306a36Sopenharmony_ci}
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(amd_pmc_pm, amd_pmc_suspend_handler, NULL);
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_cistatic const struct pci_device_id pmc_pci_ids[] = {
89662306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_PS) },
89762306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_CB) },
89862306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_YC) },
89962306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_CZN) },
90062306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_RN) },
90162306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_PCO) },
90262306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_RV) },
90362306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_SP) },
90462306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_1AH_M20H_ROOT) },
90562306a36Sopenharmony_ci	{ }
90662306a36Sopenharmony_ci};
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_cistatic int amd_pmc_s2d_init(struct amd_pmc_dev *dev)
90962306a36Sopenharmony_ci{
91062306a36Sopenharmony_ci	u32 phys_addr_low, phys_addr_hi;
91162306a36Sopenharmony_ci	u64 stb_phys_addr;
91262306a36Sopenharmony_ci	u32 size = 0;
91362306a36Sopenharmony_ci	int ret;
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	/* Spill to DRAM feature uses separate SMU message port */
91662306a36Sopenharmony_ci	dev->msg_port = 1;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	/* Get num of IP blocks within the SoC */
91962306a36Sopenharmony_ci	amd_pmc_get_ip_info(dev);
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	amd_pmc_send_cmd(dev, S2D_TELEMETRY_SIZE, &size, dev->s2d_msg_id, true);
92262306a36Sopenharmony_ci	if (size != S2D_TELEMETRY_BYTES_MAX)
92362306a36Sopenharmony_ci		return -EIO;
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	/* Get DRAM size */
92662306a36Sopenharmony_ci	ret = amd_pmc_send_cmd(dev, S2D_DRAM_SIZE, &dev->dram_size, dev->s2d_msg_id, true);
92762306a36Sopenharmony_ci	if (ret || !dev->dram_size)
92862306a36Sopenharmony_ci		dev->dram_size = S2D_TELEMETRY_DRAMBYTES_MAX;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	/* Get STB DRAM address */
93162306a36Sopenharmony_ci	amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_LOW, &phys_addr_low, dev->s2d_msg_id, true);
93262306a36Sopenharmony_ci	amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_HIGH, &phys_addr_hi, dev->s2d_msg_id, true);
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	stb_phys_addr = ((u64)phys_addr_hi << 32 | phys_addr_low);
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	/* Clear msg_port for other SMU operation */
93762306a36Sopenharmony_ci	dev->msg_port = 0;
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	dev->stb_virt_addr = devm_ioremap(dev->dev, stb_phys_addr, dev->dram_size);
94062306a36Sopenharmony_ci	if (!dev->stb_virt_addr)
94162306a36Sopenharmony_ci		return -ENOMEM;
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	return 0;
94462306a36Sopenharmony_ci}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_cistatic int amd_pmc_write_stb(struct amd_pmc_dev *dev, u32 data)
94762306a36Sopenharmony_ci{
94862306a36Sopenharmony_ci	int err;
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	err = amd_smn_write(0, AMD_PMC_STB_PMI_0, data);
95162306a36Sopenharmony_ci	if (err) {
95262306a36Sopenharmony_ci		dev_err(dev->dev, "failed to write data in stb: 0x%X\n", AMD_PMC_STB_PMI_0);
95362306a36Sopenharmony_ci		return pcibios_err_to_errno(err);
95462306a36Sopenharmony_ci	}
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	return 0;
95762306a36Sopenharmony_ci}
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_cistatic int amd_pmc_read_stb(struct amd_pmc_dev *dev, u32 *buf)
96062306a36Sopenharmony_ci{
96162306a36Sopenharmony_ci	int i, err;
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	for (i = 0; i < FIFO_SIZE; i++) {
96462306a36Sopenharmony_ci		err = amd_smn_read(0, AMD_PMC_STB_PMI_0, buf++);
96562306a36Sopenharmony_ci		if (err) {
96662306a36Sopenharmony_ci			dev_err(dev->dev, "error reading data from stb: 0x%X\n", AMD_PMC_STB_PMI_0);
96762306a36Sopenharmony_ci			return pcibios_err_to_errno(err);
96862306a36Sopenharmony_ci		}
96962306a36Sopenharmony_ci	}
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	return 0;
97262306a36Sopenharmony_ci}
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_cistatic int amd_pmc_probe(struct platform_device *pdev)
97562306a36Sopenharmony_ci{
97662306a36Sopenharmony_ci	struct amd_pmc_dev *dev = &pmc;
97762306a36Sopenharmony_ci	struct pci_dev *rdev;
97862306a36Sopenharmony_ci	u32 base_addr_lo, base_addr_hi;
97962306a36Sopenharmony_ci	u64 base_addr;
98062306a36Sopenharmony_ci	int err;
98162306a36Sopenharmony_ci	u32 val;
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	dev->dev = &pdev->dev;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	rdev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0, 0));
98662306a36Sopenharmony_ci	if (!rdev || !pci_match_id(pmc_pci_ids, rdev)) {
98762306a36Sopenharmony_ci		err = -ENODEV;
98862306a36Sopenharmony_ci		goto err_pci_dev_put;
98962306a36Sopenharmony_ci	}
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	dev->cpu_id = rdev->device;
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	if (dev->cpu_id == AMD_CPU_ID_SP) {
99462306a36Sopenharmony_ci		dev_warn_once(dev->dev, "S0i3 is not supported on this hardware\n");
99562306a36Sopenharmony_ci		err = -ENODEV;
99662306a36Sopenharmony_ci		goto err_pci_dev_put;
99762306a36Sopenharmony_ci	}
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	dev->rdev = rdev;
100062306a36Sopenharmony_ci	err = amd_smn_read(0, AMD_PMC_BASE_ADDR_LO, &val);
100162306a36Sopenharmony_ci	if (err) {
100262306a36Sopenharmony_ci		dev_err(dev->dev, "error reading 0x%x\n", AMD_PMC_BASE_ADDR_LO);
100362306a36Sopenharmony_ci		err = pcibios_err_to_errno(err);
100462306a36Sopenharmony_ci		goto err_pci_dev_put;
100562306a36Sopenharmony_ci	}
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	base_addr_lo = val & AMD_PMC_BASE_ADDR_HI_MASK;
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	err = amd_smn_read(0, AMD_PMC_BASE_ADDR_HI, &val);
101062306a36Sopenharmony_ci	if (err) {
101162306a36Sopenharmony_ci		dev_err(dev->dev, "error reading 0x%x\n", AMD_PMC_BASE_ADDR_HI);
101262306a36Sopenharmony_ci		err = pcibios_err_to_errno(err);
101362306a36Sopenharmony_ci		goto err_pci_dev_put;
101462306a36Sopenharmony_ci	}
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	base_addr_hi = val & AMD_PMC_BASE_ADDR_LO_MASK;
101762306a36Sopenharmony_ci	base_addr = ((u64)base_addr_hi << 32 | base_addr_lo);
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	dev->regbase = devm_ioremap(dev->dev, base_addr + AMD_PMC_BASE_ADDR_OFFSET,
102062306a36Sopenharmony_ci				    AMD_PMC_MAPPING_SIZE);
102162306a36Sopenharmony_ci	if (!dev->regbase) {
102262306a36Sopenharmony_ci		err = -ENOMEM;
102362306a36Sopenharmony_ci		goto err_pci_dev_put;
102462306a36Sopenharmony_ci	}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	mutex_init(&dev->lock);
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	if (enable_stb && amd_pmc_is_stb_supported(dev)) {
102962306a36Sopenharmony_ci		err = amd_pmc_s2d_init(dev);
103062306a36Sopenharmony_ci		if (err)
103162306a36Sopenharmony_ci			goto err_pci_dev_put;
103262306a36Sopenharmony_ci	}
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	platform_set_drvdata(pdev, dev);
103562306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_SUSPEND)) {
103662306a36Sopenharmony_ci		err = acpi_register_lps0_dev(&amd_pmc_s2idle_dev_ops);
103762306a36Sopenharmony_ci		if (err)
103862306a36Sopenharmony_ci			dev_warn(dev->dev, "failed to register LPS0 sleep handler, expect increased power consumption\n");
103962306a36Sopenharmony_ci		if (!disable_workarounds)
104062306a36Sopenharmony_ci			amd_pmc_quirks_init(dev);
104162306a36Sopenharmony_ci	}
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	amd_pmc_dbgfs_register(dev);
104462306a36Sopenharmony_ci	pm_report_max_hw_sleep(U64_MAX);
104562306a36Sopenharmony_ci	return 0;
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_cierr_pci_dev_put:
104862306a36Sopenharmony_ci	pci_dev_put(rdev);
104962306a36Sopenharmony_ci	return err;
105062306a36Sopenharmony_ci}
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_cistatic void amd_pmc_remove(struct platform_device *pdev)
105362306a36Sopenharmony_ci{
105462306a36Sopenharmony_ci	struct amd_pmc_dev *dev = platform_get_drvdata(pdev);
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_SUSPEND))
105762306a36Sopenharmony_ci		acpi_unregister_lps0_dev(&amd_pmc_s2idle_dev_ops);
105862306a36Sopenharmony_ci	amd_pmc_dbgfs_unregister(dev);
105962306a36Sopenharmony_ci	pci_dev_put(dev->rdev);
106062306a36Sopenharmony_ci	mutex_destroy(&dev->lock);
106162306a36Sopenharmony_ci}
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_cistatic const struct acpi_device_id amd_pmc_acpi_ids[] = {
106462306a36Sopenharmony_ci	{"AMDI0005", 0},
106562306a36Sopenharmony_ci	{"AMDI0006", 0},
106662306a36Sopenharmony_ci	{"AMDI0007", 0},
106762306a36Sopenharmony_ci	{"AMDI0008", 0},
106862306a36Sopenharmony_ci	{"AMDI0009", 0},
106962306a36Sopenharmony_ci	{"AMDI000A", 0},
107062306a36Sopenharmony_ci	{"AMD0004", 0},
107162306a36Sopenharmony_ci	{"AMD0005", 0},
107262306a36Sopenharmony_ci	{ }
107362306a36Sopenharmony_ci};
107462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, amd_pmc_acpi_ids);
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_cistatic struct platform_driver amd_pmc_driver = {
107762306a36Sopenharmony_ci	.driver = {
107862306a36Sopenharmony_ci		.name = "amd_pmc",
107962306a36Sopenharmony_ci		.acpi_match_table = amd_pmc_acpi_ids,
108062306a36Sopenharmony_ci		.dev_groups = pmc_groups,
108162306a36Sopenharmony_ci		.pm = pm_sleep_ptr(&amd_pmc_pm),
108262306a36Sopenharmony_ci	},
108362306a36Sopenharmony_ci	.probe = amd_pmc_probe,
108462306a36Sopenharmony_ci	.remove_new = amd_pmc_remove,
108562306a36Sopenharmony_ci};
108662306a36Sopenharmony_cimodule_platform_driver(amd_pmc_driver);
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
108962306a36Sopenharmony_ciMODULE_DESCRIPTION("AMD PMC Driver");
1090