xref: /openbmc/linux/drivers/platform/x86/amd/pmf/core.c (revision f21bf622)
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 
114dc491c8SShyam Sundar S K #include <linux/debugfs.h>
12da5ce22dSShyam Sundar S K #include <linux/iopoll.h>
13da5ce22dSShyam Sundar S K #include <linux/module.h>
14da5ce22dSShyam Sundar S K #include <linux/pci.h>
15da5ce22dSShyam Sundar S K #include <linux/platform_device.h>
164c71ae41SShyam Sundar S K #include <linux/power_supply.h>
17da5ce22dSShyam Sundar S K #include "pmf.h"
18da5ce22dSShyam Sundar S K 
19da5ce22dSShyam Sundar S K /* PMF-SMU communication registers */
20da5ce22dSShyam Sundar S K #define AMD_PMF_REGISTER_MESSAGE	0xA18
21da5ce22dSShyam Sundar S K #define AMD_PMF_REGISTER_RESPONSE	0xA78
22da5ce22dSShyam Sundar S K #define AMD_PMF_REGISTER_ARGUMENT	0xA58
23da5ce22dSShyam Sundar S K 
24da5ce22dSShyam Sundar S K /* Base address of SMU for mapping physical address to virtual address */
25da5ce22dSShyam Sundar S K #define AMD_PMF_SMU_INDEX_ADDRESS	0xB8
26da5ce22dSShyam Sundar S K #define AMD_PMF_SMU_INDEX_DATA		0xBC
27da5ce22dSShyam Sundar S K #define AMD_PMF_MAPPING_SIZE		0x01000
28da5ce22dSShyam Sundar S K #define AMD_PMF_BASE_ADDR_OFFSET	0x10000
29da5ce22dSShyam Sundar S K #define AMD_PMF_BASE_ADDR_LO		0x13B102E8
30da5ce22dSShyam Sundar S K #define AMD_PMF_BASE_ADDR_HI		0x13B102EC
31da5ce22dSShyam Sundar S K #define AMD_PMF_BASE_ADDR_LO_MASK	GENMASK(15, 0)
32da5ce22dSShyam Sundar S K #define AMD_PMF_BASE_ADDR_HI_MASK	GENMASK(31, 20)
33da5ce22dSShyam Sundar S K 
34da5ce22dSShyam Sundar S K /* SMU Response Codes */
35da5ce22dSShyam Sundar S K #define AMD_PMF_RESULT_OK                    0x01
36da5ce22dSShyam Sundar S K #define AMD_PMF_RESULT_CMD_REJECT_BUSY       0xFC
37da5ce22dSShyam Sundar S K #define AMD_PMF_RESULT_CMD_REJECT_PREREQ     0xFD
38da5ce22dSShyam Sundar S K #define AMD_PMF_RESULT_CMD_UNKNOWN           0xFE
39da5ce22dSShyam Sundar S K #define AMD_PMF_RESULT_FAILED                0xFF
40da5ce22dSShyam Sundar S K 
41da5ce22dSShyam Sundar S K /* List of supported CPU ids */
429448ec9bSShyam Sundar S K #define AMD_CPU_ID_RMB			0x14b5
43da5ce22dSShyam Sundar S K #define AMD_CPU_ID_PS			0x14e8
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 
61*f21bf622SShyam Sundar S K static int amd_pmf_pwr_src_notify_call(struct notifier_block *nb, unsigned long event, void *data)
62*f21bf622SShyam Sundar S K {
63*f21bf622SShyam Sundar S K 	struct amd_pmf_dev *pmf = container_of(nb, struct amd_pmf_dev, pwr_src_notifier);
64*f21bf622SShyam Sundar S K 
65*f21bf622SShyam Sundar S K 	if (event != PSY_EVENT_PROP_CHANGED)
66*f21bf622SShyam Sundar S K 		return NOTIFY_OK;
67*f21bf622SShyam Sundar S K 
68*f21bf622SShyam Sundar S K 	if (is_apmf_func_supported(pmf, APMF_FUNC_AUTO_MODE) ||
69*f21bf622SShyam Sundar S K 	    is_apmf_func_supported(pmf, APMF_FUNC_DYN_SLIDER_DC) ||
70*f21bf622SShyam Sundar S K 	    is_apmf_func_supported(pmf, APMF_FUNC_DYN_SLIDER_AC)) {
71*f21bf622SShyam Sundar S K 		if ((pmf->amt_enabled || pmf->cnqf_enabled) && is_pprof_balanced(pmf))
72*f21bf622SShyam Sundar S K 			return NOTIFY_DONE;
73*f21bf622SShyam Sundar S K 	}
74*f21bf622SShyam Sundar S K 
75*f21bf622SShyam Sundar S K 	amd_pmf_set_sps_power_limits(pmf);
76*f21bf622SShyam Sundar S K 
77*f21bf622SShyam Sundar S K 	return NOTIFY_OK;
78*f21bf622SShyam Sundar S K }
79*f21bf622SShyam Sundar S K 
804dc491c8SShyam Sundar S K static int current_power_limits_show(struct seq_file *seq, void *unused)
814dc491c8SShyam Sundar S K {
824dc491c8SShyam Sundar S K 	struct amd_pmf_dev *dev = seq->private;
834dc491c8SShyam Sundar S K 	struct amd_pmf_static_slider_granular table;
844dc491c8SShyam Sundar S K 	int mode, src = 0;
854dc491c8SShyam Sundar S K 
864dc491c8SShyam Sundar S K 	mode = amd_pmf_get_pprof_modes(dev);
87ea522b80SShyam Sundar S K 	if (mode < 0)
88ea522b80SShyam Sundar S K 		return mode;
89ea522b80SShyam Sundar S K 
904dc491c8SShyam Sundar S K 	src = amd_pmf_get_power_source();
914dc491c8SShyam Sundar S K 	amd_pmf_update_slider(dev, SLIDER_OP_GET, mode, &table);
924dc491c8SShyam 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",
934dc491c8SShyam Sundar S K 		   table.prop[src][mode].spl,
944dc491c8SShyam Sundar S K 		   table.prop[src][mode].fppt,
954dc491c8SShyam Sundar S K 		   table.prop[src][mode].sppt,
964dc491c8SShyam Sundar S K 		   table.prop[src][mode].sppt_apu_only,
974dc491c8SShyam Sundar S K 		   table.prop[src][mode].stt_min,
984dc491c8SShyam Sundar S K 		   table.prop[src][mode].stt_skin_temp[STT_TEMP_APU],
994dc491c8SShyam Sundar S K 		   table.prop[src][mode].stt_skin_temp[STT_TEMP_HS2]);
1004dc491c8SShyam Sundar S K 	return 0;
1014dc491c8SShyam Sundar S K }
1024dc491c8SShyam Sundar S K DEFINE_SHOW_ATTRIBUTE(current_power_limits);
1034dc491c8SShyam Sundar S K 
1044dc491c8SShyam Sundar S K static void amd_pmf_dbgfs_unregister(struct amd_pmf_dev *dev)
1054dc491c8SShyam Sundar S K {
1064dc491c8SShyam Sundar S K 	debugfs_remove_recursive(dev->dbgfs_dir);
1074dc491c8SShyam Sundar S K }
1084dc491c8SShyam Sundar S K 
1094dc491c8SShyam Sundar S K static void amd_pmf_dbgfs_register(struct amd_pmf_dev *dev)
1104dc491c8SShyam Sundar S K {
1114dc491c8SShyam Sundar S K 	dev->dbgfs_dir = debugfs_create_dir("amd_pmf", NULL);
1124dc491c8SShyam Sundar S K 	debugfs_create_file("current_power_limits", 0644, dev->dbgfs_dir, dev,
1134dc491c8SShyam Sundar S K 			    &current_power_limits_fops);
1144dc491c8SShyam Sundar S K }
1154dc491c8SShyam Sundar S K 
1164c71ae41SShyam Sundar S K int amd_pmf_get_power_source(void)
1174c71ae41SShyam Sundar S K {
1184c71ae41SShyam Sundar S K 	if (power_supply_is_system_supplied() > 0)
1194c71ae41SShyam Sundar S K 		return POWER_SOURCE_AC;
1204c71ae41SShyam Sundar S K 	else
1214c71ae41SShyam Sundar S K 		return POWER_SOURCE_DC;
1224c71ae41SShyam Sundar S K }
1234c71ae41SShyam Sundar S K 
1241a409b35SShyam Sundar S K static void amd_pmf_get_metrics(struct work_struct *work)
1251a409b35SShyam Sundar S K {
1261a409b35SShyam Sundar S K 	struct amd_pmf_dev *dev = container_of(work, struct amd_pmf_dev, work_buffer.work);
1271a409b35SShyam Sundar S K 	ktime_t time_elapsed_ms;
1281a409b35SShyam Sundar S K 	int socket_power;
1291a409b35SShyam Sundar S K 
1307d77dcc8SShyam Sundar S K 	mutex_lock(&dev->update_mutex);
1311a409b35SShyam Sundar S K 	/* Transfer table contents */
1321a409b35SShyam Sundar S K 	memset(dev->buf, 0, sizeof(dev->m_table));
1331a409b35SShyam Sundar S K 	amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL);
1341a409b35SShyam Sundar S K 	memcpy(&dev->m_table, dev->buf, sizeof(dev->m_table));
1351a409b35SShyam Sundar S K 
1361a409b35SShyam Sundar S K 	time_elapsed_ms = ktime_to_ms(ktime_get()) - dev->start_time;
1371a409b35SShyam Sundar S K 	/* Calculate the avg SoC power consumption */
1381a409b35SShyam Sundar S K 	socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
1391a409b35SShyam Sundar S K 
1407d77dcc8SShyam Sundar S K 	if (dev->amt_enabled) {
1417d77dcc8SShyam Sundar S K 		/* Apply the Auto Mode transition */
1427d77dcc8SShyam Sundar S K 		amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
1437d77dcc8SShyam Sundar S K 	}
1447d77dcc8SShyam Sundar S K 
1451738061cSShyam Sundar S K 	if (dev->cnqf_enabled) {
1461738061cSShyam Sundar S K 		/* Apply the CnQF transition */
1471738061cSShyam Sundar S K 		amd_pmf_trans_cnqf(dev, socket_power, time_elapsed_ms);
1481738061cSShyam Sundar S K 	}
1491738061cSShyam Sundar S K 
1501a409b35SShyam Sundar S K 	dev->start_time = ktime_to_ms(ktime_get());
1511a409b35SShyam Sundar S K 	schedule_delayed_work(&dev->work_buffer, msecs_to_jiffies(metrics_table_loop_ms));
1527d77dcc8SShyam Sundar S K 	mutex_unlock(&dev->update_mutex);
1531a409b35SShyam Sundar S K }
1541a409b35SShyam Sundar S K 
155da5ce22dSShyam Sundar S K static inline u32 amd_pmf_reg_read(struct amd_pmf_dev *dev, int reg_offset)
156da5ce22dSShyam Sundar S K {
157da5ce22dSShyam Sundar S K 	return ioread32(dev->regbase + reg_offset);
158da5ce22dSShyam Sundar S K }
159da5ce22dSShyam Sundar S K 
160da5ce22dSShyam Sundar S K static inline void amd_pmf_reg_write(struct amd_pmf_dev *dev, int reg_offset, u32 val)
161da5ce22dSShyam Sundar S K {
162da5ce22dSShyam Sundar S K 	iowrite32(val, dev->regbase + reg_offset);
163da5ce22dSShyam Sundar S K }
164da5ce22dSShyam Sundar S K 
165da5ce22dSShyam Sundar S K static void __maybe_unused amd_pmf_dump_registers(struct amd_pmf_dev *dev)
166da5ce22dSShyam Sundar S K {
167da5ce22dSShyam Sundar S K 	u32 value;
168da5ce22dSShyam Sundar S K 
169da5ce22dSShyam Sundar S K 	value = amd_pmf_reg_read(dev, AMD_PMF_REGISTER_RESPONSE);
170da5ce22dSShyam Sundar S K 	dev_dbg(dev->dev, "AMD_PMF_REGISTER_RESPONSE:%x\n", value);
171da5ce22dSShyam Sundar S K 
172da5ce22dSShyam Sundar S K 	value = amd_pmf_reg_read(dev, AMD_PMF_REGISTER_ARGUMENT);
173da5ce22dSShyam Sundar S K 	dev_dbg(dev->dev, "AMD_PMF_REGISTER_ARGUMENT:%d\n", value);
174da5ce22dSShyam Sundar S K 
175da5ce22dSShyam Sundar S K 	value = amd_pmf_reg_read(dev, AMD_PMF_REGISTER_MESSAGE);
176da5ce22dSShyam Sundar S K 	dev_dbg(dev->dev, "AMD_PMF_REGISTER_MESSAGE:%x\n", value);
177da5ce22dSShyam Sundar S K }
178da5ce22dSShyam Sundar S K 
179da5ce22dSShyam Sundar S K int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32 *data)
180da5ce22dSShyam Sundar S K {
181da5ce22dSShyam Sundar S K 	int rc;
182da5ce22dSShyam Sundar S K 	u32 val;
183da5ce22dSShyam Sundar S K 
184da5ce22dSShyam Sundar S K 	mutex_lock(&dev->lock);
185da5ce22dSShyam Sundar S K 
186da5ce22dSShyam Sundar S K 	/* Wait until we get a valid response */
187da5ce22dSShyam Sundar S K 	rc = readx_poll_timeout(ioread32, dev->regbase + AMD_PMF_REGISTER_RESPONSE,
188da5ce22dSShyam Sundar S K 				val, val != 0, PMF_MSG_DELAY_MIN_US,
189da5ce22dSShyam Sundar S K 				PMF_MSG_DELAY_MIN_US * RESPONSE_REGISTER_LOOP_MAX);
190da5ce22dSShyam Sundar S K 	if (rc) {
191da5ce22dSShyam Sundar S K 		dev_err(dev->dev, "failed to talk to SMU\n");
192da5ce22dSShyam Sundar S K 		goto out_unlock;
193da5ce22dSShyam Sundar S K 	}
194da5ce22dSShyam Sundar S K 
195da5ce22dSShyam Sundar S K 	/* Write zero to response register */
196da5ce22dSShyam Sundar S K 	amd_pmf_reg_write(dev, AMD_PMF_REGISTER_RESPONSE, 0);
197da5ce22dSShyam Sundar S K 
198da5ce22dSShyam Sundar S K 	/* Write argument into argument register */
199da5ce22dSShyam Sundar S K 	amd_pmf_reg_write(dev, AMD_PMF_REGISTER_ARGUMENT, arg);
200da5ce22dSShyam Sundar S K 
201da5ce22dSShyam Sundar S K 	/* Write message ID to message ID register */
202da5ce22dSShyam Sundar S K 	amd_pmf_reg_write(dev, AMD_PMF_REGISTER_MESSAGE, message);
203da5ce22dSShyam Sundar S K 
204da5ce22dSShyam Sundar S K 	/* Wait until we get a valid response */
205da5ce22dSShyam Sundar S K 	rc = readx_poll_timeout(ioread32, dev->regbase + AMD_PMF_REGISTER_RESPONSE,
206da5ce22dSShyam Sundar S K 				val, val != 0, PMF_MSG_DELAY_MIN_US,
207da5ce22dSShyam Sundar S K 				PMF_MSG_DELAY_MIN_US * RESPONSE_REGISTER_LOOP_MAX);
208da5ce22dSShyam Sundar S K 	if (rc) {
209da5ce22dSShyam Sundar S K 		dev_err(dev->dev, "SMU response timed out\n");
210da5ce22dSShyam Sundar S K 		goto out_unlock;
211da5ce22dSShyam Sundar S K 	}
212da5ce22dSShyam Sundar S K 
213da5ce22dSShyam Sundar S K 	switch (val) {
214da5ce22dSShyam Sundar S K 	case AMD_PMF_RESULT_OK:
215da5ce22dSShyam Sundar S K 		if (get) {
216da5ce22dSShyam Sundar S K 			/* PMFW may take longer time to return back the data */
217da5ce22dSShyam Sundar S K 			usleep_range(DELAY_MIN_US, 10 * DELAY_MAX_US);
218da5ce22dSShyam Sundar S K 			*data = amd_pmf_reg_read(dev, AMD_PMF_REGISTER_ARGUMENT);
219da5ce22dSShyam Sundar S K 		}
220da5ce22dSShyam Sundar S K 		break;
221da5ce22dSShyam Sundar S K 	case AMD_PMF_RESULT_CMD_REJECT_BUSY:
222da5ce22dSShyam Sundar S K 		dev_err(dev->dev, "SMU not ready. err: 0x%x\n", val);
223da5ce22dSShyam Sundar S K 		rc = -EBUSY;
224da5ce22dSShyam Sundar S K 		goto out_unlock;
225da5ce22dSShyam Sundar S K 	case AMD_PMF_RESULT_CMD_UNKNOWN:
226da5ce22dSShyam Sundar S K 		dev_err(dev->dev, "SMU cmd unknown. err: 0x%x\n", val);
227da5ce22dSShyam Sundar S K 		rc = -EINVAL;
228da5ce22dSShyam Sundar S K 		goto out_unlock;
229da5ce22dSShyam Sundar S K 	case AMD_PMF_RESULT_CMD_REJECT_PREREQ:
230da5ce22dSShyam Sundar S K 	case AMD_PMF_RESULT_FAILED:
231da5ce22dSShyam Sundar S K 	default:
232da5ce22dSShyam Sundar S K 		dev_err(dev->dev, "SMU cmd failed. err: 0x%x\n", val);
233da5ce22dSShyam Sundar S K 		rc = -EIO;
234da5ce22dSShyam Sundar S K 		goto out_unlock;
235da5ce22dSShyam Sundar S K 	}
236da5ce22dSShyam Sundar S K 
237da5ce22dSShyam Sundar S K out_unlock:
238da5ce22dSShyam Sundar S K 	mutex_unlock(&dev->lock);
239da5ce22dSShyam Sundar S K 	amd_pmf_dump_registers(dev);
240da5ce22dSShyam Sundar S K 	return rc;
241da5ce22dSShyam Sundar S K }
242da5ce22dSShyam Sundar S K 
243da5ce22dSShyam Sundar S K static const struct pci_device_id pmf_pci_ids[] = {
2449448ec9bSShyam Sundar S K 	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_RMB) },
245da5ce22dSShyam Sundar S K 	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_PS) },
246da5ce22dSShyam Sundar S K 	{ }
247da5ce22dSShyam Sundar S K };
248da5ce22dSShyam Sundar S K 
2491a409b35SShyam Sundar S K int amd_pmf_init_metrics_table(struct amd_pmf_dev *dev)
2501a409b35SShyam Sundar S K {
2511a409b35SShyam Sundar S K 	u64 phys_addr;
2521a409b35SShyam Sundar S K 	u32 hi, low;
2531a409b35SShyam Sundar S K 
2541a409b35SShyam Sundar S K 	INIT_DELAYED_WORK(&dev->work_buffer, amd_pmf_get_metrics);
2551a409b35SShyam Sundar S K 
2561a409b35SShyam Sundar S K 	/* Get Metrics Table Address */
2571a409b35SShyam Sundar S K 	dev->buf = kzalloc(sizeof(dev->m_table), GFP_KERNEL);
2581a409b35SShyam Sundar S K 	if (!dev->buf)
2591a409b35SShyam Sundar S K 		return -ENOMEM;
2601a409b35SShyam Sundar S K 
2611a409b35SShyam Sundar S K 	phys_addr = virt_to_phys(dev->buf);
2621a409b35SShyam Sundar S K 	hi = phys_addr >> 32;
2631a409b35SShyam Sundar S K 	low = phys_addr & GENMASK(31, 0);
2641a409b35SShyam Sundar S K 
2651a409b35SShyam Sundar S K 	amd_pmf_send_cmd(dev, SET_DRAM_ADDR_HIGH, 0, hi, NULL);
2661a409b35SShyam Sundar S K 	amd_pmf_send_cmd(dev, SET_DRAM_ADDR_LOW, 0, low, NULL);
2671a409b35SShyam Sundar S K 
2681a409b35SShyam Sundar S K 	/*
2691a409b35SShyam Sundar S K 	 * Start collecting the metrics data after a small delay
2701a409b35SShyam Sundar S K 	 * or else, we might end up getting stale values from PMFW.
2711a409b35SShyam Sundar S K 	 */
2721a409b35SShyam Sundar S K 	schedule_delayed_work(&dev->work_buffer, msecs_to_jiffies(metrics_table_loop_ms * 3));
2731a409b35SShyam Sundar S K 
2741a409b35SShyam Sundar S K 	return 0;
2751a409b35SShyam Sundar S K }
2761a409b35SShyam Sundar S K 
2774c71ae41SShyam Sundar S K static void amd_pmf_init_features(struct amd_pmf_dev *dev)
2784c71ae41SShyam Sundar S K {
2791738061cSShyam Sundar S K 	int ret;
2801738061cSShyam Sundar S K 
2814c71ae41SShyam Sundar S K 	/* Enable Static Slider */
2824c71ae41SShyam Sundar S K 	if (is_apmf_func_supported(dev, APMF_FUNC_STATIC_SLIDER_GRANULAR)) {
2834c71ae41SShyam Sundar S K 		amd_pmf_init_sps(dev);
2844c71ae41SShyam Sundar S K 		dev_dbg(dev->dev, "SPS enabled and Platform Profiles registered\n");
2854c71ae41SShyam Sundar S K 	}
2863f5571d9SShyam Sundar S K 
2873f5571d9SShyam Sundar S K 	/* Enable Auto Mode */
2883f5571d9SShyam Sundar S K 	if (is_apmf_func_supported(dev, APMF_FUNC_AUTO_MODE)) {
2893f5571d9SShyam Sundar S K 		amd_pmf_init_auto_mode(dev);
2903f5571d9SShyam Sundar S K 		dev_dbg(dev->dev, "Auto Mode Init done\n");
2911738061cSShyam Sundar S K 	} else if (is_apmf_func_supported(dev, APMF_FUNC_DYN_SLIDER_AC) ||
2921738061cSShyam Sundar S K 			  is_apmf_func_supported(dev, APMF_FUNC_DYN_SLIDER_DC)) {
2931738061cSShyam Sundar S K 		/* Enable Cool n Quiet Framework (CnQF) */
2941738061cSShyam Sundar S K 		ret = amd_pmf_init_cnqf(dev);
2951738061cSShyam Sundar S K 		if (ret)
2961738061cSShyam Sundar S K 			dev_warn(dev->dev, "CnQF Init failed\n");
2973f5571d9SShyam Sundar S K 	}
2984c71ae41SShyam Sundar S K }
2994c71ae41SShyam Sundar S K 
3004c71ae41SShyam Sundar S K static void amd_pmf_deinit_features(struct amd_pmf_dev *dev)
3014c71ae41SShyam Sundar S K {
3024c71ae41SShyam Sundar S K 	if (is_apmf_func_supported(dev, APMF_FUNC_STATIC_SLIDER_GRANULAR))
3034c71ae41SShyam Sundar S K 		amd_pmf_deinit_sps(dev);
3043f5571d9SShyam Sundar S K 
3051738061cSShyam Sundar S K 	if (is_apmf_func_supported(dev, APMF_FUNC_AUTO_MODE)) {
3063f5571d9SShyam Sundar S K 		amd_pmf_deinit_auto_mode(dev);
3071738061cSShyam Sundar S K 	} else if (is_apmf_func_supported(dev, APMF_FUNC_DYN_SLIDER_AC) ||
3081738061cSShyam Sundar S K 			  is_apmf_func_supported(dev, APMF_FUNC_DYN_SLIDER_DC)) {
3091738061cSShyam Sundar S K 		amd_pmf_deinit_cnqf(dev);
3101738061cSShyam Sundar S K 	}
3114c71ae41SShyam Sundar S K }
3124c71ae41SShyam Sundar S K 
313da5ce22dSShyam Sundar S K static const struct acpi_device_id amd_pmf_acpi_ids[] = {
3149448ec9bSShyam Sundar S K 	{"AMDI0100", 0x100},
315da5ce22dSShyam Sundar S K 	{"AMDI0102", 0},
316da5ce22dSShyam Sundar S K 	{ }
317da5ce22dSShyam Sundar S K };
318da5ce22dSShyam Sundar S K MODULE_DEVICE_TABLE(acpi, amd_pmf_acpi_ids);
319da5ce22dSShyam Sundar S K 
320da5ce22dSShyam Sundar S K static int amd_pmf_probe(struct platform_device *pdev)
321da5ce22dSShyam Sundar S K {
3229448ec9bSShyam Sundar S K 	const struct acpi_device_id *id;
323da5ce22dSShyam Sundar S K 	struct amd_pmf_dev *dev;
324da5ce22dSShyam Sundar S K 	struct pci_dev *rdev;
325da5ce22dSShyam Sundar S K 	u32 base_addr_lo;
326da5ce22dSShyam Sundar S K 	u32 base_addr_hi;
327da5ce22dSShyam Sundar S K 	u64 base_addr;
328da5ce22dSShyam Sundar S K 	u32 val;
329da5ce22dSShyam Sundar S K 	int err;
330da5ce22dSShyam Sundar S K 
3319448ec9bSShyam Sundar S K 	id = acpi_match_device(amd_pmf_acpi_ids, &pdev->dev);
3329448ec9bSShyam Sundar S K 	if (!id)
3339448ec9bSShyam Sundar S K 		return -ENODEV;
3349448ec9bSShyam Sundar S K 
3359448ec9bSShyam Sundar S K 	if (id->driver_data == 0x100 && !force_load)
3369448ec9bSShyam Sundar S K 		return -ENODEV;
3379448ec9bSShyam Sundar S K 
338da5ce22dSShyam Sundar S K 	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
339da5ce22dSShyam Sundar S K 	if (!dev)
340da5ce22dSShyam Sundar S K 		return -ENOMEM;
341da5ce22dSShyam Sundar S K 
342da5ce22dSShyam Sundar S K 	dev->dev = &pdev->dev;
343da5ce22dSShyam Sundar S K 
344da5ce22dSShyam Sundar S K 	rdev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0, 0));
345da5ce22dSShyam Sundar S K 	if (!rdev || !pci_match_id(pmf_pci_ids, rdev)) {
346da5ce22dSShyam Sundar S K 		pci_dev_put(rdev);
347da5ce22dSShyam Sundar S K 		return -ENODEV;
348da5ce22dSShyam Sundar S K 	}
349da5ce22dSShyam Sundar S K 
350da5ce22dSShyam Sundar S K 	dev->cpu_id = rdev->device;
351da5ce22dSShyam Sundar S K 	err = pci_write_config_dword(rdev, AMD_PMF_SMU_INDEX_ADDRESS, AMD_PMF_BASE_ADDR_LO);
352da5ce22dSShyam Sundar S K 	if (err) {
353da5ce22dSShyam Sundar S K 		dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMF_SMU_INDEX_ADDRESS);
354da5ce22dSShyam Sundar S K 		pci_dev_put(rdev);
355da5ce22dSShyam Sundar S K 		return pcibios_err_to_errno(err);
356da5ce22dSShyam Sundar S K 	}
357da5ce22dSShyam Sundar S K 
358da5ce22dSShyam Sundar S K 	err = pci_read_config_dword(rdev, AMD_PMF_SMU_INDEX_DATA, &val);
359da5ce22dSShyam Sundar S K 	if (err) {
360da5ce22dSShyam Sundar S K 		pci_dev_put(rdev);
361da5ce22dSShyam Sundar S K 		return pcibios_err_to_errno(err);
362da5ce22dSShyam Sundar S K 	}
363da5ce22dSShyam Sundar S K 
364da5ce22dSShyam Sundar S K 	base_addr_lo = val & AMD_PMF_BASE_ADDR_HI_MASK;
365da5ce22dSShyam Sundar S K 
366da5ce22dSShyam Sundar S K 	err = pci_write_config_dword(rdev, AMD_PMF_SMU_INDEX_ADDRESS, AMD_PMF_BASE_ADDR_HI);
367da5ce22dSShyam Sundar S K 	if (err) {
368da5ce22dSShyam Sundar S K 		dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMF_SMU_INDEX_ADDRESS);
369da5ce22dSShyam Sundar S K 		pci_dev_put(rdev);
370da5ce22dSShyam Sundar S K 		return pcibios_err_to_errno(err);
371da5ce22dSShyam Sundar S K 	}
372da5ce22dSShyam Sundar S K 
373da5ce22dSShyam Sundar S K 	err = pci_read_config_dword(rdev, AMD_PMF_SMU_INDEX_DATA, &val);
374da5ce22dSShyam Sundar S K 	if (err) {
375da5ce22dSShyam Sundar S K 		pci_dev_put(rdev);
376da5ce22dSShyam Sundar S K 		return pcibios_err_to_errno(err);
377da5ce22dSShyam Sundar S K 	}
378da5ce22dSShyam Sundar S K 
379da5ce22dSShyam Sundar S K 	base_addr_hi = val & AMD_PMF_BASE_ADDR_LO_MASK;
380da5ce22dSShyam Sundar S K 	pci_dev_put(rdev);
381da5ce22dSShyam Sundar S K 	base_addr = ((u64)base_addr_hi << 32 | base_addr_lo);
382da5ce22dSShyam Sundar S K 
383da5ce22dSShyam Sundar S K 	dev->regbase = devm_ioremap(dev->dev, base_addr + AMD_PMF_BASE_ADDR_OFFSET,
384da5ce22dSShyam Sundar S K 				    AMD_PMF_MAPPING_SIZE);
385da5ce22dSShyam Sundar S K 	if (!dev->regbase)
386da5ce22dSShyam Sundar S K 		return -ENOMEM;
387da5ce22dSShyam Sundar S K 
3885eb315ebSShyam Sundar S K 	apmf_acpi_init(dev);
389da5ce22dSShyam Sundar S K 	platform_set_drvdata(pdev, dev);
3904c71ae41SShyam Sundar S K 	amd_pmf_init_features(dev);
39122ee98cbSShyam Sundar S K 	apmf_install_handler(dev);
3924dc491c8SShyam Sundar S K 	amd_pmf_dbgfs_register(dev);
393da5ce22dSShyam Sundar S K 
394*f21bf622SShyam Sundar S K 	dev->pwr_src_notifier.notifier_call = amd_pmf_pwr_src_notify_call;
395*f21bf622SShyam Sundar S K 	power_supply_reg_notifier(&dev->pwr_src_notifier);
396*f21bf622SShyam Sundar S K 
397da5ce22dSShyam Sundar S K 	mutex_init(&dev->lock);
3987d77dcc8SShyam Sundar S K 	mutex_init(&dev->update_mutex);
399da5ce22dSShyam Sundar S K 	dev_info(dev->dev, "registered PMF device successfully\n");
400da5ce22dSShyam Sundar S K 
401da5ce22dSShyam Sundar S K 	return 0;
402da5ce22dSShyam Sundar S K }
403da5ce22dSShyam Sundar S K 
404da5ce22dSShyam Sundar S K static int amd_pmf_remove(struct platform_device *pdev)
405da5ce22dSShyam Sundar S K {
406da5ce22dSShyam Sundar S K 	struct amd_pmf_dev *dev = platform_get_drvdata(pdev);
407da5ce22dSShyam Sundar S K 
408*f21bf622SShyam Sundar S K 	power_supply_unreg_notifier(&dev->pwr_src_notifier);
409da5ce22dSShyam Sundar S K 	mutex_destroy(&dev->lock);
4107d77dcc8SShyam Sundar S K 	mutex_destroy(&dev->update_mutex);
4114c71ae41SShyam Sundar S K 	amd_pmf_deinit_features(dev);
412b9ab888bSShyam Sundar S K 	apmf_acpi_deinit(dev);
4134dc491c8SShyam Sundar S K 	amd_pmf_dbgfs_unregister(dev);
414da5ce22dSShyam Sundar S K 	kfree(dev->buf);
415da5ce22dSShyam Sundar S K 	return 0;
416da5ce22dSShyam Sundar S K }
417da5ce22dSShyam Sundar S K 
4183dae5825SShyam Sundar S K static const struct attribute_group *amd_pmf_driver_groups[] = {
4193dae5825SShyam Sundar S K 	&cnqf_feature_attribute_group,
4203dae5825SShyam Sundar S K 	NULL,
4213dae5825SShyam Sundar S K };
4223dae5825SShyam Sundar S K 
423da5ce22dSShyam Sundar S K static struct platform_driver amd_pmf_driver = {
424da5ce22dSShyam Sundar S K 	.driver = {
425da5ce22dSShyam Sundar S K 		.name = "amd-pmf",
426da5ce22dSShyam Sundar S K 		.acpi_match_table = amd_pmf_acpi_ids,
4273dae5825SShyam Sundar S K 		.dev_groups = amd_pmf_driver_groups,
428da5ce22dSShyam Sundar S K 	},
429da5ce22dSShyam Sundar S K 	.probe = amd_pmf_probe,
430da5ce22dSShyam Sundar S K 	.remove = amd_pmf_remove,
431da5ce22dSShyam Sundar S K };
432da5ce22dSShyam Sundar S K module_platform_driver(amd_pmf_driver);
433da5ce22dSShyam Sundar S K 
434da5ce22dSShyam Sundar S K MODULE_LICENSE("GPL");
435da5ce22dSShyam Sundar S K MODULE_DESCRIPTION("AMD Platform Management Framework Driver");
436