xref: /openbmc/linux/drivers/platform/x86/amd/pmf/core.c (revision 4dbd6e61)
1da5ce22dSShyam Sundar S K // SPDX-License-Identifier: GPL-2.0-or-later
2da5ce22dSShyam Sundar S K /*
3da5ce22dSShyam Sundar S K  * AMD Platform Management Framework Driver
4da5ce22dSShyam Sundar S K  *
5da5ce22dSShyam Sundar S K  * Copyright (c) 2022, Advanced Micro Devices, Inc.
6da5ce22dSShyam Sundar S K  * All Rights Reserved.
7da5ce22dSShyam Sundar S K  *
8da5ce22dSShyam Sundar S K  * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
9da5ce22dSShyam Sundar S K  */
10da5ce22dSShyam Sundar S K 
11aec8298cSShyam Sundar S K #include <asm/amd_nb.h>
124dc491c8SShyam Sundar S K #include <linux/debugfs.h>
13da5ce22dSShyam Sundar S K #include <linux/iopoll.h>
14da5ce22dSShyam Sundar S K #include <linux/module.h>
15da5ce22dSShyam Sundar S K #include <linux/pci.h>
16da5ce22dSShyam Sundar S K #include <linux/platform_device.h>
174c71ae41SShyam Sundar S K #include <linux/power_supply.h>
18da5ce22dSShyam Sundar S K #include "pmf.h"
19da5ce22dSShyam Sundar S K 
20da5ce22dSShyam Sundar S K /* PMF-SMU communication registers */
21da5ce22dSShyam Sundar S K #define AMD_PMF_REGISTER_MESSAGE	0xA18
22da5ce22dSShyam Sundar S K #define AMD_PMF_REGISTER_RESPONSE	0xA78
23da5ce22dSShyam Sundar S K #define AMD_PMF_REGISTER_ARGUMENT	0xA58
24da5ce22dSShyam Sundar S K 
25da5ce22dSShyam Sundar S K /* Base address of SMU for mapping physical address to virtual address */
26da5ce22dSShyam Sundar S K #define AMD_PMF_MAPPING_SIZE		0x01000
27da5ce22dSShyam Sundar S K #define AMD_PMF_BASE_ADDR_OFFSET	0x10000
28da5ce22dSShyam Sundar S K #define AMD_PMF_BASE_ADDR_LO		0x13B102E8
29da5ce22dSShyam Sundar S K #define AMD_PMF_BASE_ADDR_HI		0x13B102EC
30da5ce22dSShyam Sundar S K #define AMD_PMF_BASE_ADDR_LO_MASK	GENMASK(15, 0)
31da5ce22dSShyam Sundar S K #define AMD_PMF_BASE_ADDR_HI_MASK	GENMASK(31, 20)
32da5ce22dSShyam Sundar S K 
33da5ce22dSShyam Sundar S K /* SMU Response Codes */
34da5ce22dSShyam Sundar S K #define AMD_PMF_RESULT_OK                    0x01
35da5ce22dSShyam Sundar S K #define AMD_PMF_RESULT_CMD_REJECT_BUSY       0xFC
36da5ce22dSShyam Sundar S K #define AMD_PMF_RESULT_CMD_REJECT_PREREQ     0xFD
37da5ce22dSShyam Sundar S K #define AMD_PMF_RESULT_CMD_UNKNOWN           0xFE
38da5ce22dSShyam Sundar S K #define AMD_PMF_RESULT_FAILED                0xFF
39da5ce22dSShyam Sundar S K 
40da5ce22dSShyam Sundar S K /* List of supported CPU ids */
419448ec9bSShyam Sundar S K #define AMD_CPU_ID_RMB			0x14b5
42da5ce22dSShyam Sundar S K #define AMD_CPU_ID_PS			0x14e8
435d3acd9dSShyam Sundar S K #define PCI_DEVICE_ID_AMD_1AH_M20H_ROOT	0x1507
44da5ce22dSShyam Sundar S K 
45da5ce22dSShyam Sundar S K #define PMF_MSG_DELAY_MIN_US		50
46da5ce22dSShyam Sundar S K #define RESPONSE_REGISTER_LOOP_MAX	20000
47da5ce22dSShyam Sundar S K 
48da5ce22dSShyam Sundar S K #define DELAY_MIN_US	2000
49da5ce22dSShyam Sundar S K #define DELAY_MAX_US	3000
50da5ce22dSShyam Sundar S K 
511a409b35SShyam Sundar S K /* override Metrics Table sample size time (in ms) */
521a409b35SShyam Sundar S K static int metrics_table_loop_ms = 1000;
531a409b35SShyam Sundar S K module_param(metrics_table_loop_ms, int, 0644);
541a409b35SShyam Sundar S K MODULE_PARM_DESC(metrics_table_loop_ms, "Metrics Table sample size time (default = 1000ms)");
551a409b35SShyam Sundar S K 
569448ec9bSShyam Sundar S K /* Force load on supported older platforms */
579448ec9bSShyam Sundar S K static bool force_load;
589448ec9bSShyam Sundar S K module_param(force_load, bool, 0444);
599448ec9bSShyam Sundar S K MODULE_PARM_DESC(force_load, "Force load this driver on supported older platforms (experimental)");
609448ec9bSShyam Sundar S K 
amd_pmf_pwr_src_notify_call(struct notifier_block * nb,unsigned long event,void * data)61f21bf622SShyam Sundar S K static int amd_pmf_pwr_src_notify_call(struct notifier_block *nb, unsigned long event, void *data)
62f21bf622SShyam Sundar S K {
63f21bf622SShyam Sundar S K 	struct amd_pmf_dev *pmf = container_of(nb, struct amd_pmf_dev, pwr_src_notifier);
64f21bf622SShyam Sundar S K 
65f21bf622SShyam Sundar S K 	if (event != PSY_EVENT_PROP_CHANGED)
66f21bf622SShyam Sundar S K 		return NOTIFY_OK;
67f21bf622SShyam Sundar S K 
68f21bf622SShyam Sundar S K 	if (is_apmf_func_supported(pmf, APMF_FUNC_AUTO_MODE) ||
69f21bf622SShyam Sundar S K 	    is_apmf_func_supported(pmf, APMF_FUNC_DYN_SLIDER_DC) ||
70f21bf622SShyam Sundar S K 	    is_apmf_func_supported(pmf, APMF_FUNC_DYN_SLIDER_AC)) {
71f21bf622SShyam Sundar S K 		if ((pmf->amt_enabled || pmf->cnqf_enabled) && is_pprof_balanced(pmf))
72f21bf622SShyam Sundar S K 			return NOTIFY_DONE;
73f21bf622SShyam Sundar S K 	}
74f21bf622SShyam Sundar S K 
7533c9ab5bSShyam Sundar S K 	if (is_apmf_func_supported(pmf, APMF_FUNC_STATIC_SLIDER_GRANULAR))
76f21bf622SShyam Sundar S K 		amd_pmf_set_sps_power_limits(pmf);
77f21bf622SShyam Sundar S K 
7833c9ab5bSShyam Sundar S K 	if (is_apmf_func_supported(pmf, APMF_FUNC_OS_POWER_SLIDER_UPDATE))
7933c9ab5bSShyam Sundar S K 		amd_pmf_power_slider_update_event(pmf);
8033c9ab5bSShyam Sundar S K 
81f21bf622SShyam Sundar S K 	return NOTIFY_OK;
82f21bf622SShyam Sundar S K }
83f21bf622SShyam Sundar S K 
current_power_limits_show(struct seq_file * seq,void * unused)844dc491c8SShyam Sundar S K static int current_power_limits_show(struct seq_file *seq, void *unused)
854dc491c8SShyam Sundar S K {
864dc491c8SShyam Sundar S K 	struct amd_pmf_dev *dev = seq->private;
874dc491c8SShyam Sundar S K 	struct amd_pmf_static_slider_granular table;
884dc491c8SShyam Sundar S K 	int mode, src = 0;
894dc491c8SShyam Sundar S K 
904dc491c8SShyam Sundar S K 	mode = amd_pmf_get_pprof_modes(dev);
91ea522b80SShyam Sundar S K 	if (mode < 0)
92ea522b80SShyam Sundar S K 		return mode;
93ea522b80SShyam Sundar S K 
944dc491c8SShyam Sundar S K 	src = amd_pmf_get_power_source();
954dc491c8SShyam Sundar S K 	amd_pmf_update_slider(dev, SLIDER_OP_GET, mode, &table);
964dc491c8SShyam Sundar S K 	seq_printf(seq, "spl:%u fppt:%u sppt:%u sppt_apu_only:%u stt_min:%u stt[APU]:%u stt[HS2]: %u\n",
974dc491c8SShyam Sundar S K 		   table.prop[src][mode].spl,
984dc491c8SShyam Sundar S K 		   table.prop[src][mode].fppt,
994dc491c8SShyam Sundar S K 		   table.prop[src][mode].sppt,
1004dc491c8SShyam Sundar S K 		   table.prop[src][mode].sppt_apu_only,
1014dc491c8SShyam Sundar S K 		   table.prop[src][mode].stt_min,
1024dc491c8SShyam Sundar S K 		   table.prop[src][mode].stt_skin_temp[STT_TEMP_APU],
1034dc491c8SShyam Sundar S K 		   table.prop[src][mode].stt_skin_temp[STT_TEMP_HS2]);
1044dc491c8SShyam Sundar S K 	return 0;
1054dc491c8SShyam Sundar S K }
1064dc491c8SShyam Sundar S K DEFINE_SHOW_ATTRIBUTE(current_power_limits);
1074dc491c8SShyam Sundar S K 
amd_pmf_dbgfs_unregister(struct amd_pmf_dev * dev)1084dc491c8SShyam Sundar S K static void amd_pmf_dbgfs_unregister(struct amd_pmf_dev *dev)
1094dc491c8SShyam Sundar S K {
1104dc491c8SShyam Sundar S K 	debugfs_remove_recursive(dev->dbgfs_dir);
1114dc491c8SShyam Sundar S K }
1124dc491c8SShyam Sundar S K 
amd_pmf_dbgfs_register(struct amd_pmf_dev * dev)1134dc491c8SShyam Sundar S K static void amd_pmf_dbgfs_register(struct amd_pmf_dev *dev)
1144dc491c8SShyam Sundar S K {
1154dc491c8SShyam Sundar S K 	dev->dbgfs_dir = debugfs_create_dir("amd_pmf", NULL);
1164dc491c8SShyam Sundar S K 	debugfs_create_file("current_power_limits", 0644, dev->dbgfs_dir, dev,
1174dc491c8SShyam Sundar S K 			    &current_power_limits_fops);
1184dc491c8SShyam Sundar S K }
1194dc491c8SShyam Sundar S K 
amd_pmf_get_power_source(void)1204c71ae41SShyam Sundar S K int amd_pmf_get_power_source(void)
1214c71ae41SShyam Sundar S K {
1224c71ae41SShyam Sundar S K 	if (power_supply_is_system_supplied() > 0)
1234c71ae41SShyam Sundar S K 		return POWER_SOURCE_AC;
1244c71ae41SShyam Sundar S K 	else
1254c71ae41SShyam Sundar S K 		return POWER_SOURCE_DC;
1264c71ae41SShyam Sundar S K }
1274c71ae41SShyam Sundar S K 
amd_pmf_get_metrics(struct work_struct * work)1281a409b35SShyam Sundar S K static void amd_pmf_get_metrics(struct work_struct *work)
1291a409b35SShyam Sundar S K {
1301a409b35SShyam Sundar S K 	struct amd_pmf_dev *dev = container_of(work, struct amd_pmf_dev, work_buffer.work);
1311a409b35SShyam Sundar S K 	ktime_t time_elapsed_ms;
1321a409b35SShyam Sundar S K 	int socket_power;
1331a409b35SShyam Sundar S K 
1347d77dcc8SShyam Sundar S K 	mutex_lock(&dev->update_mutex);
1351a409b35SShyam Sundar S K 	/* Transfer table contents */
1361a409b35SShyam Sundar S K 	memset(dev->buf, 0, sizeof(dev->m_table));
1371a409b35SShyam Sundar S K 	amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL);
1381a409b35SShyam Sundar S K 	memcpy(&dev->m_table, dev->buf, sizeof(dev->m_table));
1391a409b35SShyam Sundar S K 
1401a409b35SShyam Sundar S K 	time_elapsed_ms = ktime_to_ms(ktime_get()) - dev->start_time;
1411a409b35SShyam Sundar S K 	/* Calculate the avg SoC power consumption */
1421a409b35SShyam Sundar S K 	socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
1431a409b35SShyam Sundar S K 
1447d77dcc8SShyam Sundar S K 	if (dev->amt_enabled) {
1457d77dcc8SShyam Sundar S K 		/* Apply the Auto Mode transition */
1467d77dcc8SShyam Sundar S K 		amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
1477d77dcc8SShyam Sundar S K 	}
1487d77dcc8SShyam Sundar S K 
1491738061cSShyam Sundar S K 	if (dev->cnqf_enabled) {
1501738061cSShyam Sundar S K 		/* Apply the CnQF transition */
1511738061cSShyam Sundar S K 		amd_pmf_trans_cnqf(dev, socket_power, time_elapsed_ms);
1521738061cSShyam Sundar S K 	}
1531738061cSShyam Sundar S K 
1541a409b35SShyam Sundar S K 	dev->start_time = ktime_to_ms(ktime_get());
1551a409b35SShyam Sundar S K 	schedule_delayed_work(&dev->work_buffer, msecs_to_jiffies(metrics_table_loop_ms));
1567d77dcc8SShyam Sundar S K 	mutex_unlock(&dev->update_mutex);
1571a409b35SShyam Sundar S K }
1581a409b35SShyam Sundar S K 
amd_pmf_reg_read(struct amd_pmf_dev * dev,int reg_offset)159da5ce22dSShyam Sundar S K static inline u32 amd_pmf_reg_read(struct amd_pmf_dev *dev, int reg_offset)
160da5ce22dSShyam Sundar S K {
161da5ce22dSShyam Sundar S K 	return ioread32(dev->regbase + reg_offset);
162da5ce22dSShyam Sundar S K }
163da5ce22dSShyam Sundar S K 
amd_pmf_reg_write(struct amd_pmf_dev * dev,int reg_offset,u32 val)164da5ce22dSShyam Sundar S K static inline void amd_pmf_reg_write(struct amd_pmf_dev *dev, int reg_offset, u32 val)
165da5ce22dSShyam Sundar S K {
166da5ce22dSShyam Sundar S K 	iowrite32(val, dev->regbase + reg_offset);
167da5ce22dSShyam Sundar S K }
168da5ce22dSShyam Sundar S K 
amd_pmf_dump_registers(struct amd_pmf_dev * dev)169da5ce22dSShyam Sundar S K static void __maybe_unused amd_pmf_dump_registers(struct amd_pmf_dev *dev)
170da5ce22dSShyam Sundar S K {
171da5ce22dSShyam Sundar S K 	u32 value;
172da5ce22dSShyam Sundar S K 
173da5ce22dSShyam Sundar S K 	value = amd_pmf_reg_read(dev, AMD_PMF_REGISTER_RESPONSE);
174da5ce22dSShyam Sundar S K 	dev_dbg(dev->dev, "AMD_PMF_REGISTER_RESPONSE:%x\n", value);
175da5ce22dSShyam Sundar S K 
176da5ce22dSShyam Sundar S K 	value = amd_pmf_reg_read(dev, AMD_PMF_REGISTER_ARGUMENT);
177da5ce22dSShyam Sundar S K 	dev_dbg(dev->dev, "AMD_PMF_REGISTER_ARGUMENT:%d\n", value);
178da5ce22dSShyam Sundar S K 
179da5ce22dSShyam Sundar S K 	value = amd_pmf_reg_read(dev, AMD_PMF_REGISTER_MESSAGE);
180da5ce22dSShyam Sundar S K 	dev_dbg(dev->dev, "AMD_PMF_REGISTER_MESSAGE:%x\n", value);
181da5ce22dSShyam Sundar S K }
182da5ce22dSShyam Sundar S K 
amd_pmf_send_cmd(struct amd_pmf_dev * dev,u8 message,bool get,u32 arg,u32 * data)183da5ce22dSShyam Sundar S K int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32 *data)
184da5ce22dSShyam Sundar S K {
185da5ce22dSShyam Sundar S K 	int rc;
186da5ce22dSShyam Sundar S K 	u32 val;
187da5ce22dSShyam Sundar S K 
188da5ce22dSShyam Sundar S K 	mutex_lock(&dev->lock);
189da5ce22dSShyam Sundar S K 
190da5ce22dSShyam Sundar S K 	/* Wait until we get a valid response */
191da5ce22dSShyam Sundar S K 	rc = readx_poll_timeout(ioread32, dev->regbase + AMD_PMF_REGISTER_RESPONSE,
192da5ce22dSShyam Sundar S K 				val, val != 0, PMF_MSG_DELAY_MIN_US,
193da5ce22dSShyam Sundar S K 				PMF_MSG_DELAY_MIN_US * RESPONSE_REGISTER_LOOP_MAX);
194da5ce22dSShyam Sundar S K 	if (rc) {
195da5ce22dSShyam Sundar S K 		dev_err(dev->dev, "failed to talk to SMU\n");
196da5ce22dSShyam Sundar S K 		goto out_unlock;
197da5ce22dSShyam Sundar S K 	}
198da5ce22dSShyam Sundar S K 
199da5ce22dSShyam Sundar S K 	/* Write zero to response register */
200da5ce22dSShyam Sundar S K 	amd_pmf_reg_write(dev, AMD_PMF_REGISTER_RESPONSE, 0);
201da5ce22dSShyam Sundar S K 
202da5ce22dSShyam Sundar S K 	/* Write argument into argument register */
203da5ce22dSShyam Sundar S K 	amd_pmf_reg_write(dev, AMD_PMF_REGISTER_ARGUMENT, arg);
204da5ce22dSShyam Sundar S K 
205da5ce22dSShyam Sundar S K 	/* Write message ID to message ID register */
206da5ce22dSShyam Sundar S K 	amd_pmf_reg_write(dev, AMD_PMF_REGISTER_MESSAGE, message);
207da5ce22dSShyam Sundar S K 
208da5ce22dSShyam Sundar S K 	/* Wait until we get a valid response */
209da5ce22dSShyam Sundar S K 	rc = readx_poll_timeout(ioread32, dev->regbase + AMD_PMF_REGISTER_RESPONSE,
210da5ce22dSShyam Sundar S K 				val, val != 0, PMF_MSG_DELAY_MIN_US,
211da5ce22dSShyam Sundar S K 				PMF_MSG_DELAY_MIN_US * RESPONSE_REGISTER_LOOP_MAX);
212da5ce22dSShyam Sundar S K 	if (rc) {
213da5ce22dSShyam Sundar S K 		dev_err(dev->dev, "SMU response timed out\n");
214da5ce22dSShyam Sundar S K 		goto out_unlock;
215da5ce22dSShyam Sundar S K 	}
216da5ce22dSShyam Sundar S K 
217da5ce22dSShyam Sundar S K 	switch (val) {
218da5ce22dSShyam Sundar S K 	case AMD_PMF_RESULT_OK:
219da5ce22dSShyam Sundar S K 		if (get) {
220da5ce22dSShyam Sundar S K 			/* PMFW may take longer time to return back the data */
221da5ce22dSShyam Sundar S K 			usleep_range(DELAY_MIN_US, 10 * DELAY_MAX_US);
222da5ce22dSShyam Sundar S K 			*data = amd_pmf_reg_read(dev, AMD_PMF_REGISTER_ARGUMENT);
223da5ce22dSShyam Sundar S K 		}
224da5ce22dSShyam Sundar S K 		break;
225da5ce22dSShyam Sundar S K 	case AMD_PMF_RESULT_CMD_REJECT_BUSY:
226da5ce22dSShyam Sundar S K 		dev_err(dev->dev, "SMU not ready. err: 0x%x\n", val);
227da5ce22dSShyam Sundar S K 		rc = -EBUSY;
228da5ce22dSShyam Sundar S K 		goto out_unlock;
229da5ce22dSShyam Sundar S K 	case AMD_PMF_RESULT_CMD_UNKNOWN:
230da5ce22dSShyam Sundar S K 		dev_err(dev->dev, "SMU cmd unknown. err: 0x%x\n", val);
231da5ce22dSShyam Sundar S K 		rc = -EINVAL;
232da5ce22dSShyam Sundar S K 		goto out_unlock;
233da5ce22dSShyam Sundar S K 	case AMD_PMF_RESULT_CMD_REJECT_PREREQ:
234da5ce22dSShyam Sundar S K 	case AMD_PMF_RESULT_FAILED:
235da5ce22dSShyam Sundar S K 	default:
236da5ce22dSShyam Sundar S K 		dev_err(dev->dev, "SMU cmd failed. err: 0x%x\n", val);
237da5ce22dSShyam Sundar S K 		rc = -EIO;
238da5ce22dSShyam Sundar S K 		goto out_unlock;
239da5ce22dSShyam Sundar S K 	}
240da5ce22dSShyam Sundar S K 
241da5ce22dSShyam Sundar S K out_unlock:
242da5ce22dSShyam Sundar S K 	mutex_unlock(&dev->lock);
243da5ce22dSShyam Sundar S K 	amd_pmf_dump_registers(dev);
244da5ce22dSShyam Sundar S K 	return rc;
245da5ce22dSShyam Sundar S K }
246da5ce22dSShyam Sundar S K 
247da5ce22dSShyam Sundar S K static const struct pci_device_id pmf_pci_ids[] = {
2489448ec9bSShyam Sundar S K 	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_RMB) },
249da5ce22dSShyam Sundar S K 	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_PS) },
2505d3acd9dSShyam Sundar S K 	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_1AH_M20H_ROOT) },
251da5ce22dSShyam Sundar S K 	{ }
252da5ce22dSShyam Sundar S K };
253da5ce22dSShyam Sundar S K 
amd_pmf_set_dram_addr(struct amd_pmf_dev * dev)254b54147faSMario Limonciello static void amd_pmf_set_dram_addr(struct amd_pmf_dev *dev)
2551a409b35SShyam Sundar S K {
2561a409b35SShyam Sundar S K 	u64 phys_addr;
2571a409b35SShyam Sundar S K 	u32 hi, low;
2581a409b35SShyam Sundar S K 
2591a409b35SShyam Sundar S K 	phys_addr = virt_to_phys(dev->buf);
2601a409b35SShyam Sundar S K 	hi = phys_addr >> 32;
2611a409b35SShyam Sundar S K 	low = phys_addr & GENMASK(31, 0);
2621a409b35SShyam Sundar S K 
2631a409b35SShyam Sundar S K 	amd_pmf_send_cmd(dev, SET_DRAM_ADDR_HIGH, 0, hi, NULL);
2641a409b35SShyam Sundar S K 	amd_pmf_send_cmd(dev, SET_DRAM_ADDR_LOW, 0, low, NULL);
265b54147faSMario Limonciello }
266b54147faSMario Limonciello 
amd_pmf_init_metrics_table(struct amd_pmf_dev * dev)267b54147faSMario Limonciello int amd_pmf_init_metrics_table(struct amd_pmf_dev *dev)
268b54147faSMario Limonciello {
269b54147faSMario Limonciello 	/* Get Metrics Table Address */
270b54147faSMario Limonciello 	dev->buf = kzalloc(sizeof(dev->m_table), GFP_KERNEL);
271b54147faSMario Limonciello 	if (!dev->buf)
272b54147faSMario Limonciello 		return -ENOMEM;
273b54147faSMario Limonciello 
274b54147faSMario Limonciello 	INIT_DELAYED_WORK(&dev->work_buffer, amd_pmf_get_metrics);
275b54147faSMario Limonciello 
276b54147faSMario Limonciello 	amd_pmf_set_dram_addr(dev);
2771a409b35SShyam Sundar S K 
2781a409b35SShyam Sundar S K 	/*
2791a409b35SShyam Sundar S K 	 * Start collecting the metrics data after a small delay
2801a409b35SShyam Sundar S K 	 * or else, we might end up getting stale values from PMFW.
2811a409b35SShyam Sundar S K 	 */
2821a409b35SShyam Sundar S K 	schedule_delayed_work(&dev->work_buffer, msecs_to_jiffies(metrics_table_loop_ms * 3));
2831a409b35SShyam Sundar S K 
2841a409b35SShyam Sundar S K 	return 0;
2851a409b35SShyam Sundar S K }
2861a409b35SShyam Sundar S K 
amd_pmf_resume_handler(struct device * dev)287b54147faSMario Limonciello static int amd_pmf_resume_handler(struct device *dev)
288b54147faSMario Limonciello {
289b54147faSMario Limonciello 	struct amd_pmf_dev *pdev = dev_get_drvdata(dev);
290b54147faSMario Limonciello 
291b54147faSMario Limonciello 	if (pdev->buf)
292b54147faSMario Limonciello 		amd_pmf_set_dram_addr(pdev);
293b54147faSMario Limonciello 
294b54147faSMario Limonciello 	return 0;
295b54147faSMario Limonciello }
296b54147faSMario Limonciello 
297b54147faSMario Limonciello static DEFINE_SIMPLE_DEV_PM_OPS(amd_pmf_pm, NULL, amd_pmf_resume_handler);
298b54147faSMario Limonciello 
amd_pmf_init_features(struct amd_pmf_dev * dev)2994c71ae41SShyam Sundar S K static void amd_pmf_init_features(struct amd_pmf_dev *dev)
3004c71ae41SShyam Sundar S K {
3011738061cSShyam Sundar S K 	int ret;
3021738061cSShyam Sundar S K 
3034c71ae41SShyam Sundar S K 	/* Enable Static Slider */
30433c9ab5bSShyam Sundar S K 	if (is_apmf_func_supported(dev, APMF_FUNC_STATIC_SLIDER_GRANULAR) ||
30533c9ab5bSShyam Sundar S K 	    is_apmf_func_supported(dev, APMF_FUNC_OS_POWER_SLIDER_UPDATE)) {
3064c71ae41SShyam Sundar S K 		amd_pmf_init_sps(dev);
307146b6f68SShyam Sundar S K 		dev->pwr_src_notifier.notifier_call = amd_pmf_pwr_src_notify_call;
308146b6f68SShyam Sundar S K 		power_supply_reg_notifier(&dev->pwr_src_notifier);
3094c71ae41SShyam Sundar S K 		dev_dbg(dev->dev, "SPS enabled and Platform Profiles registered\n");
3104c71ae41SShyam Sundar S K 	}
3113f5571d9SShyam Sundar S K 
3123f5571d9SShyam Sundar S K 	/* Enable Auto Mode */
3133f5571d9SShyam Sundar S K 	if (is_apmf_func_supported(dev, APMF_FUNC_AUTO_MODE)) {
3143f5571d9SShyam Sundar S K 		amd_pmf_init_auto_mode(dev);
3153f5571d9SShyam Sundar S K 		dev_dbg(dev->dev, "Auto Mode Init done\n");
3161738061cSShyam Sundar S K 	} else if (is_apmf_func_supported(dev, APMF_FUNC_DYN_SLIDER_AC) ||
3171738061cSShyam Sundar S K 			  is_apmf_func_supported(dev, APMF_FUNC_DYN_SLIDER_DC)) {
3181738061cSShyam Sundar S K 		/* Enable Cool n Quiet Framework (CnQF) */
3191738061cSShyam Sundar S K 		ret = amd_pmf_init_cnqf(dev);
3201738061cSShyam Sundar S K 		if (ret)
3211738061cSShyam Sundar S K 			dev_warn(dev->dev, "CnQF Init failed\n");
3223f5571d9SShyam Sundar S K 	}
3234c71ae41SShyam Sundar S K }
3244c71ae41SShyam Sundar S K 
amd_pmf_deinit_features(struct amd_pmf_dev * dev)3254c71ae41SShyam Sundar S K static void amd_pmf_deinit_features(struct amd_pmf_dev *dev)
3264c71ae41SShyam Sundar S K {
327*4dbd6e61SMario Limonciello 	if (is_apmf_func_supported(dev, APMF_FUNC_STATIC_SLIDER_GRANULAR) ||
328*4dbd6e61SMario Limonciello 	    is_apmf_func_supported(dev, APMF_FUNC_OS_POWER_SLIDER_UPDATE)) {
329146b6f68SShyam Sundar S K 		power_supply_unreg_notifier(&dev->pwr_src_notifier);
3304c71ae41SShyam Sundar S K 		amd_pmf_deinit_sps(dev);
331146b6f68SShyam Sundar S K 	}
3323f5571d9SShyam Sundar S K 
3331738061cSShyam Sundar S K 	if (is_apmf_func_supported(dev, APMF_FUNC_AUTO_MODE)) {
3343f5571d9SShyam Sundar S K 		amd_pmf_deinit_auto_mode(dev);
3351738061cSShyam Sundar S K 	} else if (is_apmf_func_supported(dev, APMF_FUNC_DYN_SLIDER_AC) ||
3361738061cSShyam Sundar S K 			  is_apmf_func_supported(dev, APMF_FUNC_DYN_SLIDER_DC)) {
3371738061cSShyam Sundar S K 		amd_pmf_deinit_cnqf(dev);
3381738061cSShyam Sundar S K 	}
3394c71ae41SShyam Sundar S K }
3404c71ae41SShyam Sundar S K 
341da5ce22dSShyam Sundar S K static const struct acpi_device_id amd_pmf_acpi_ids[] = {
3429448ec9bSShyam Sundar S K 	{"AMDI0100", 0x100},
343da5ce22dSShyam Sundar S K 	{"AMDI0102", 0},
3445d3acd9dSShyam Sundar S K 	{"AMDI0103", 0},
345da5ce22dSShyam Sundar S K 	{ }
346da5ce22dSShyam Sundar S K };
347da5ce22dSShyam Sundar S K MODULE_DEVICE_TABLE(acpi, amd_pmf_acpi_ids);
348da5ce22dSShyam Sundar S K 
amd_pmf_probe(struct platform_device * pdev)349da5ce22dSShyam Sundar S K static int amd_pmf_probe(struct platform_device *pdev)
350da5ce22dSShyam Sundar S K {
3519448ec9bSShyam Sundar S K 	const struct acpi_device_id *id;
352da5ce22dSShyam Sundar S K 	struct amd_pmf_dev *dev;
353da5ce22dSShyam Sundar S K 	struct pci_dev *rdev;
354da5ce22dSShyam Sundar S K 	u32 base_addr_lo;
355da5ce22dSShyam Sundar S K 	u32 base_addr_hi;
356da5ce22dSShyam Sundar S K 	u64 base_addr;
357da5ce22dSShyam Sundar S K 	u32 val;
358da5ce22dSShyam Sundar S K 	int err;
359da5ce22dSShyam Sundar S K 
3609448ec9bSShyam Sundar S K 	id = acpi_match_device(amd_pmf_acpi_ids, &pdev->dev);
3619448ec9bSShyam Sundar S K 	if (!id)
3629448ec9bSShyam Sundar S K 		return -ENODEV;
3639448ec9bSShyam Sundar S K 
3649448ec9bSShyam Sundar S K 	if (id->driver_data == 0x100 && !force_load)
3659448ec9bSShyam Sundar S K 		return -ENODEV;
3669448ec9bSShyam Sundar S K 
367da5ce22dSShyam Sundar S K 	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
368da5ce22dSShyam Sundar S K 	if (!dev)
369da5ce22dSShyam Sundar S K 		return -ENOMEM;
370da5ce22dSShyam Sundar S K 
371da5ce22dSShyam Sundar S K 	dev->dev = &pdev->dev;
372da5ce22dSShyam Sundar S K 
373da5ce22dSShyam Sundar S K 	rdev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0, 0));
374da5ce22dSShyam Sundar S K 	if (!rdev || !pci_match_id(pmf_pci_ids, rdev)) {
375da5ce22dSShyam Sundar S K 		pci_dev_put(rdev);
376da5ce22dSShyam Sundar S K 		return -ENODEV;
377da5ce22dSShyam Sundar S K 	}
378da5ce22dSShyam Sundar S K 
379da5ce22dSShyam Sundar S K 	dev->cpu_id = rdev->device;
380da5ce22dSShyam Sundar S K 
381aec8298cSShyam Sundar S K 	err = amd_smn_read(0, AMD_PMF_BASE_ADDR_LO, &val);
382da5ce22dSShyam Sundar S K 	if (err) {
383aec8298cSShyam Sundar S K 		dev_err(dev->dev, "error in reading from 0x%x\n", AMD_PMF_BASE_ADDR_LO);
384da5ce22dSShyam Sundar S K 		pci_dev_put(rdev);
385da5ce22dSShyam Sundar S K 		return pcibios_err_to_errno(err);
386da5ce22dSShyam Sundar S K 	}
387da5ce22dSShyam Sundar S K 
388da5ce22dSShyam Sundar S K 	base_addr_lo = val & AMD_PMF_BASE_ADDR_HI_MASK;
389da5ce22dSShyam Sundar S K 
390aec8298cSShyam Sundar S K 	err = amd_smn_read(0, AMD_PMF_BASE_ADDR_HI, &val);
391da5ce22dSShyam Sundar S K 	if (err) {
392aec8298cSShyam Sundar S K 		dev_err(dev->dev, "error in reading from 0x%x\n", AMD_PMF_BASE_ADDR_HI);
393da5ce22dSShyam Sundar S K 		pci_dev_put(rdev);
394da5ce22dSShyam Sundar S K 		return pcibios_err_to_errno(err);
395da5ce22dSShyam Sundar S K 	}
396da5ce22dSShyam Sundar S K 
397da5ce22dSShyam Sundar S K 	base_addr_hi = val & AMD_PMF_BASE_ADDR_LO_MASK;
398da5ce22dSShyam Sundar S K 	pci_dev_put(rdev);
399da5ce22dSShyam Sundar S K 	base_addr = ((u64)base_addr_hi << 32 | base_addr_lo);
400da5ce22dSShyam Sundar S K 
401da5ce22dSShyam Sundar S K 	dev->regbase = devm_ioremap(dev->dev, base_addr + AMD_PMF_BASE_ADDR_OFFSET,
402da5ce22dSShyam Sundar S K 				    AMD_PMF_MAPPING_SIZE);
403da5ce22dSShyam Sundar S K 	if (!dev->regbase)
404da5ce22dSShyam Sundar S K 		return -ENOMEM;
405da5ce22dSShyam Sundar S K 
406e0c40529SHans de Goede 	mutex_init(&dev->lock);
407e0c40529SHans de Goede 	mutex_init(&dev->update_mutex);
408e0c40529SHans de Goede 
4095eb315ebSShyam Sundar S K 	apmf_acpi_init(dev);
410da5ce22dSShyam Sundar S K 	platform_set_drvdata(pdev, dev);
4114c71ae41SShyam Sundar S K 	amd_pmf_init_features(dev);
41222ee98cbSShyam Sundar S K 	apmf_install_handler(dev);
4134dc491c8SShyam Sundar S K 	amd_pmf_dbgfs_register(dev);
414da5ce22dSShyam Sundar S K 
415da5ce22dSShyam Sundar S K 	dev_info(dev->dev, "registered PMF device successfully\n");
416da5ce22dSShyam Sundar S K 
417da5ce22dSShyam Sundar S K 	return 0;
418da5ce22dSShyam Sundar S K }
419da5ce22dSShyam Sundar S K 
amd_pmf_remove(struct platform_device * pdev)420b444276bSUwe Kleine-König static void amd_pmf_remove(struct platform_device *pdev)
421da5ce22dSShyam Sundar S K {
422da5ce22dSShyam Sundar S K 	struct amd_pmf_dev *dev = platform_get_drvdata(pdev);
423da5ce22dSShyam Sundar S K 
4244c71ae41SShyam Sundar S K 	amd_pmf_deinit_features(dev);
425b9ab888bSShyam Sundar S K 	apmf_acpi_deinit(dev);
4264dc491c8SShyam Sundar S K 	amd_pmf_dbgfs_unregister(dev);
427e0c40529SHans de Goede 	mutex_destroy(&dev->lock);
428e0c40529SHans de Goede 	mutex_destroy(&dev->update_mutex);
429da5ce22dSShyam Sundar S K 	kfree(dev->buf);
430da5ce22dSShyam Sundar S K }
431da5ce22dSShyam Sundar S K 
4323dae5825SShyam Sundar S K static const struct attribute_group *amd_pmf_driver_groups[] = {
4333dae5825SShyam Sundar S K 	&cnqf_feature_attribute_group,
4343dae5825SShyam Sundar S K 	NULL,
4353dae5825SShyam Sundar S K };
4363dae5825SShyam Sundar S K 
437da5ce22dSShyam Sundar S K static struct platform_driver amd_pmf_driver = {
438da5ce22dSShyam Sundar S K 	.driver = {
439da5ce22dSShyam Sundar S K 		.name = "amd-pmf",
440da5ce22dSShyam Sundar S K 		.acpi_match_table = amd_pmf_acpi_ids,
4413dae5825SShyam Sundar S K 		.dev_groups = amd_pmf_driver_groups,
442b54147faSMario Limonciello 		.pm = pm_sleep_ptr(&amd_pmf_pm),
443da5ce22dSShyam Sundar S K 	},
444da5ce22dSShyam Sundar S K 	.probe = amd_pmf_probe,
445b444276bSUwe Kleine-König 	.remove_new = amd_pmf_remove,
446da5ce22dSShyam Sundar S K };
447da5ce22dSShyam Sundar S K module_platform_driver(amd_pmf_driver);
448da5ce22dSShyam Sundar S K 
449da5ce22dSShyam Sundar S K MODULE_LICENSE("GPL");
450da5ce22dSShyam Sundar S K MODULE_DESCRIPTION("AMD Platform Management Framework Driver");
451