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 ¤t_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