]>
Commit | Line | Data |
---|---|---|
156ec473 SS |
1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* | |
3 | * AMD SoC Power Management Controller Driver | |
4 | * | |
5 | * Copyright (c) 2020, Advanced Micro Devices, Inc. | |
6 | * All Rights Reserved. | |
7 | * | |
8 | * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com> | |
9 | */ | |
10 | ||
11 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
12 | ||
310e782a | 13 | #include <asm/amd_nb.h> |
156ec473 SS |
14 | #include <linux/acpi.h> |
15 | #include <linux/bitfield.h> | |
16 | #include <linux/bits.h> | |
17 | #include <linux/debugfs.h> | |
18 | #include <linux/delay.h> | |
19 | #include <linux/io.h> | |
20 | #include <linux/iopoll.h> | |
59348401 | 21 | #include <linux/limits.h> |
156ec473 SS |
22 | #include <linux/module.h> |
23 | #include <linux/pci.h> | |
24 | #include <linux/platform_device.h> | |
59348401 | 25 | #include <linux/rtc.h> |
8e60615e | 26 | #include <linux/serio.h> |
156ec473 SS |
27 | #include <linux/suspend.h> |
28 | #include <linux/seq_file.h> | |
29 | #include <linux/uaccess.h> | |
30 | ||
e8ef8dd2 ML |
31 | #include "pmc.h" |
32 | ||
156ec473 SS |
33 | /* SMU communication registers */ |
34 | #define AMD_PMC_REGISTER_MESSAGE 0x538 | |
35 | #define AMD_PMC_REGISTER_RESPONSE 0x980 | |
36 | #define AMD_PMC_REGISTER_ARGUMENT 0x9BC | |
37 | ||
f6045de1 SG |
38 | /* PMC Scratch Registers */ |
39 | #define AMD_PMC_SCRATCH_REG_CZN 0x94 | |
40 | #define AMD_PMC_SCRATCH_REG_YC 0xD14 | |
41 | ||
426c0ff2 | 42 | /* STB Registers */ |
426c0ff2 | 43 | #define AMD_PMC_STB_PMI_0 0x03E30600 |
e24faabf ML |
44 | #define AMD_PMC_STB_S2IDLE_PREPARE 0xC6000001 |
45 | #define AMD_PMC_STB_S2IDLE_RESTORE 0xC6000002 | |
db55fb8a | 46 | #define AMD_PMC_STB_S2IDLE_CHECK 0xC6000003 |
b0d4bb97 | 47 | #define AMD_PMC_STB_DUMMY_PC 0xC6000007 |
426c0ff2 | 48 | |
3d7d407d | 49 | /* STB S2D(Spill to DRAM) has different message port offset */ |
3d7d407d SG |
50 | #define AMD_S2D_REGISTER_MESSAGE 0xA20 |
51 | #define AMD_S2D_REGISTER_RESPONSE 0xA80 | |
52 | #define AMD_S2D_REGISTER_ARGUMENT 0xA88 | |
53 | ||
54 | /* STB Spill to DRAM Parameters */ | |
d9f421da | 55 | #define S2D_TELEMETRY_BYTES_MAX 0x100000U |
3f720b21 | 56 | #define S2D_RSVD_RAM_SPACE 0x100000 |
3d7d407d SG |
57 | #define S2D_TELEMETRY_DRAMBYTES_MAX 0x1000000 |
58 | ||
cdb10138 SS |
59 | /* STB Spill to DRAM Message Definition */ |
60 | #define STB_FORCE_FLUSH_DATA 0xCF | |
61 | ||
156ec473 | 62 | /* Base address of SMU for mapping physical address to virtual address */ |
156ec473 SS |
63 | #define AMD_PMC_MAPPING_SIZE 0x01000 |
64 | #define AMD_PMC_BASE_ADDR_OFFSET 0x10000 | |
65 | #define AMD_PMC_BASE_ADDR_LO 0x13B102E8 | |
66 | #define AMD_PMC_BASE_ADDR_HI 0x13B102EC | |
67 | #define AMD_PMC_BASE_ADDR_LO_MASK GENMASK(15, 0) | |
68 | #define AMD_PMC_BASE_ADDR_HI_MASK GENMASK(31, 20) | |
69 | ||
70 | /* SMU Response Codes */ | |
71 | #define AMD_PMC_RESULT_OK 0x01 | |
72 | #define AMD_PMC_RESULT_CMD_REJECT_BUSY 0xFC | |
73 | #define AMD_PMC_RESULT_CMD_REJECT_PREREQ 0xFD | |
74 | #define AMD_PMC_RESULT_CMD_UNKNOWN 0xFE | |
75 | #define AMD_PMC_RESULT_FAILED 0xFF | |
76 | ||
b9a4fa69 SS |
77 | /* FCH SSC Registers */ |
78 | #define FCH_S0I3_ENTRY_TIME_L_OFFSET 0x30 | |
79 | #define FCH_S0I3_ENTRY_TIME_H_OFFSET 0x34 | |
80 | #define FCH_S0I3_EXIT_TIME_L_OFFSET 0x38 | |
81 | #define FCH_S0I3_EXIT_TIME_H_OFFSET 0x3C | |
82 | #define FCH_SSC_MAPPING_SIZE 0x800 | |
83 | #define FCH_BASE_PHY_ADDR_LOW 0xFED81100 | |
84 | #define FCH_BASE_PHY_ADDR_HIGH 0x00000000 | |
85 | ||
76620567 SS |
86 | /* SMU Message Definations */ |
87 | #define SMU_MSG_GETSMUVERSION 0x02 | |
88 | #define SMU_MSG_LOG_GETDRAM_ADDR_HI 0x04 | |
89 | #define SMU_MSG_LOG_GETDRAM_ADDR_LO 0x05 | |
90 | #define SMU_MSG_LOG_START 0x06 | |
91 | #define SMU_MSG_LOG_RESET 0x07 | |
92 | #define SMU_MSG_LOG_DUMP_DATA 0x08 | |
93 | #define SMU_MSG_GET_SUP_CONSTRAINTS 0x09 | |
156ec473 SS |
94 | /* List of supported CPU ids */ |
95 | #define AMD_CPU_ID_RV 0x15D0 | |
96 | #define AMD_CPU_ID_RN 0x1630 | |
97 | #define AMD_CPU_ID_PCO AMD_CPU_ID_RV | |
98 | #define AMD_CPU_ID_CZN AMD_CPU_ID_RN | |
83cbaf14 | 99 | #define AMD_CPU_ID_YC 0x14B5 |
221b8b21 | 100 | #define AMD_CPU_ID_CB 0x14D8 |
035c8a91 | 101 | #define AMD_CPU_ID_PS 0x14E8 |
efebfa80 | 102 | #define AMD_CPU_ID_SP 0x14A4 |
822507ca | 103 | #define PCI_DEVICE_ID_AMD_1AH_M20H_ROOT 0x1507 |
156ec473 | 104 | |
a602f511 | 105 | #define PMC_MSG_DELAY_MIN_US 50 |
3c3c8e88 | 106 | #define RESPONSE_REGISTER_LOOP_MAX 20000 |
156ec473 | 107 | |
76620567 SS |
108 | #define DELAY_MIN_US 2000 |
109 | #define DELAY_MAX_US 3000 | |
426c0ff2 | 110 | #define FIFO_SIZE 4096 |
be1ca8ae | 111 | |
156ec473 SS |
112 | enum amd_pmc_def { |
113 | MSG_TEST = 0x01, | |
114 | MSG_OS_HINT_PCO, | |
115 | MSG_OS_HINT_RN, | |
116 | }; | |
117 | ||
3d7d407d SG |
118 | enum s2d_arg { |
119 | S2D_TELEMETRY_SIZE = 0x01, | |
120 | S2D_PHYS_ADDR_LOW, | |
121 | S2D_PHYS_ADDR_HIGH, | |
1ecfd309 | 122 | S2D_NUM_SAMPLES, |
be8325fb | 123 | S2D_DRAM_SIZE, |
3d7d407d SG |
124 | }; |
125 | ||
d9f421da SS |
126 | struct amd_pmc_stb_v2_data { |
127 | size_t size; | |
128 | u8 data[] __counted_by(size); | |
129 | }; | |
130 | ||
76620567 SS |
131 | struct amd_pmc_bit_map { |
132 | const char *name; | |
133 | u32 bit_mask; | |
134 | }; | |
135 | ||
136 | static const struct amd_pmc_bit_map soc15_ip_blk[] = { | |
137 | {"DISPLAY", BIT(0)}, | |
138 | {"CPU", BIT(1)}, | |
139 | {"GFX", BIT(2)}, | |
140 | {"VDD", BIT(3)}, | |
141 | {"ACP", BIT(4)}, | |
142 | {"VCN", BIT(5)}, | |
143 | {"ISP", BIT(6)}, | |
144 | {"NBIO", BIT(7)}, | |
145 | {"DF", BIT(8)}, | |
139332e2 SS |
146 | {"USB3_0", BIT(9)}, |
147 | {"USB3_1", BIT(10)}, | |
76620567 | 148 | {"LAPIC", BIT(11)}, |
139332e2 SS |
149 | {"USB3_2", BIT(12)}, |
150 | {"USB3_3", BIT(13)}, | |
151 | {"USB3_4", BIT(14)}, | |
152 | {"USB4_0", BIT(15)}, | |
153 | {"USB4_1", BIT(16)}, | |
154 | {"MPM", BIT(17)}, | |
155 | {"JPEG", BIT(18)}, | |
156 | {"IPU", BIT(19)}, | |
157 | {"UMSCH", BIT(20)}, | |
76620567 SS |
158 | {} |
159 | }; | |
160 | ||
426c0ff2 SG |
161 | static bool enable_stb; |
162 | module_param(enable_stb, bool, 0644); | |
163 | MODULE_PARM_DESC(enable_stb, "Enable the STB debug mechanism"); | |
164 | ||
062c1394 ML |
165 | static bool disable_workarounds; |
166 | module_param(disable_workarounds, bool, 0644); | |
167 | MODULE_PARM_DESC(disable_workarounds, "Disable workarounds for platform bugs"); | |
168 | ||
3f720b21 SS |
169 | static bool dump_custom_stb; |
170 | module_param(dump_custom_stb, bool, 0644); | |
171 | MODULE_PARM_DESC(dump_custom_stb, "Enable to dump full STB buffer"); | |
172 | ||
156ec473 | 173 | static struct amd_pmc_dev pmc; |
4c9dbf86 | 174 | static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool ret); |
426c0ff2 | 175 | static int amd_pmc_read_stb(struct amd_pmc_dev *dev, u32 *buf); |
753ee989 | 176 | static int amd_pmc_write_stb(struct amd_pmc_dev *dev, u32 data); |
156ec473 SS |
177 | |
178 | static inline u32 amd_pmc_reg_read(struct amd_pmc_dev *dev, int reg_offset) | |
179 | { | |
180 | return ioread32(dev->regbase + reg_offset); | |
181 | } | |
182 | ||
183 | static inline void amd_pmc_reg_write(struct amd_pmc_dev *dev, int reg_offset, u32 val) | |
184 | { | |
185 | iowrite32(val, dev->regbase + reg_offset); | |
186 | } | |
187 | ||
76620567 SS |
188 | struct smu_metrics { |
189 | u32 table_version; | |
190 | u32 hint_count; | |
9cfe0202 | 191 | u32 s0i3_last_entry_status; |
76620567 SS |
192 | u32 timein_s0i2; |
193 | u64 timeentering_s0i3_lastcapture; | |
194 | u64 timeentering_s0i3_totaltime; | |
195 | u64 timeto_resume_to_os_lastcapture; | |
196 | u64 timeto_resume_to_os_totaltime; | |
197 | u64 timein_s0i3_lastcapture; | |
198 | u64 timein_s0i3_totaltime; | |
199 | u64 timein_swdrips_lastcapture; | |
200 | u64 timein_swdrips_totaltime; | |
139332e2 SS |
201 | u64 timecondition_notmet_lastcapture[32]; |
202 | u64 timecondition_notmet_totaltime[32]; | |
76620567 SS |
203 | } __packed; |
204 | ||
426c0ff2 SG |
205 | static int amd_pmc_stb_debugfs_open(struct inode *inode, struct file *filp) |
206 | { | |
207 | struct amd_pmc_dev *dev = filp->f_inode->i_private; | |
208 | u32 size = FIFO_SIZE * sizeof(u32); | |
209 | u32 *buf; | |
210 | int rc; | |
211 | ||
212 | buf = kzalloc(size, GFP_KERNEL); | |
213 | if (!buf) | |
214 | return -ENOMEM; | |
215 | ||
216 | rc = amd_pmc_read_stb(dev, buf); | |
217 | if (rc) { | |
218 | kfree(buf); | |
219 | return rc; | |
220 | } | |
221 | ||
222 | filp->private_data = buf; | |
223 | return rc; | |
224 | } | |
225 | ||
226 | static ssize_t amd_pmc_stb_debugfs_read(struct file *filp, char __user *buf, size_t size, | |
227 | loff_t *pos) | |
228 | { | |
229 | if (!filp->private_data) | |
230 | return -EINVAL; | |
231 | ||
232 | return simple_read_from_buffer(buf, size, pos, filp->private_data, | |
233 | FIFO_SIZE * sizeof(u32)); | |
234 | } | |
235 | ||
236 | static int amd_pmc_stb_debugfs_release(struct inode *inode, struct file *filp) | |
237 | { | |
238 | kfree(filp->private_data); | |
239 | return 0; | |
240 | } | |
241 | ||
f7086daa | 242 | static const struct file_operations amd_pmc_stb_debugfs_fops = { |
426c0ff2 SG |
243 | .owner = THIS_MODULE, |
244 | .open = amd_pmc_stb_debugfs_open, | |
245 | .read = amd_pmc_stb_debugfs_read, | |
246 | .release = amd_pmc_stb_debugfs_release, | |
247 | }; | |
248 | ||
3f720b21 SS |
249 | /* Enhanced STB Firmware Reporting Mechanism */ |
250 | static int amd_pmc_stb_handle_efr(struct file *filp) | |
251 | { | |
252 | struct amd_pmc_dev *dev = filp->f_inode->i_private; | |
253 | struct amd_pmc_stb_v2_data *stb_data_arr; | |
254 | u32 fsize; | |
255 | ||
256 | fsize = dev->dram_size - S2D_RSVD_RAM_SPACE; | |
257 | stb_data_arr = kmalloc(struct_size(stb_data_arr, data, fsize), GFP_KERNEL); | |
258 | if (!stb_data_arr) | |
259 | return -ENOMEM; | |
260 | ||
261 | stb_data_arr->size = fsize; | |
262 | memcpy_fromio(stb_data_arr->data, dev->stb_virt_addr, fsize); | |
263 | filp->private_data = stb_data_arr; | |
264 | ||
265 | return 0; | |
266 | } | |
267 | ||
3d7d407d SG |
268 | static int amd_pmc_stb_debugfs_open_v2(struct inode *inode, struct file *filp) |
269 | { | |
270 | struct amd_pmc_dev *dev = filp->f_inode->i_private; | |
d9f421da SS |
271 | u32 fsize, num_samples, val, stb_rdptr_offset = 0; |
272 | struct amd_pmc_stb_v2_data *stb_data_arr; | |
1ecfd309 | 273 | int ret; |
3d7d407d | 274 | |
b0d4bb97 SS |
275 | /* Write dummy postcode while reading the STB buffer */ |
276 | ret = amd_pmc_write_stb(dev, AMD_PMC_STB_DUMMY_PC); | |
277 | if (ret) | |
278 | dev_err(dev->dev, "error writing to STB: %d\n", ret); | |
279 | ||
1ecfd309 SS |
280 | /* Spill to DRAM num_samples uses separate SMU message port */ |
281 | dev->msg_port = 1; | |
282 | ||
cdb10138 SS |
283 | ret = amd_pmc_send_cmd(dev, 0, &val, STB_FORCE_FLUSH_DATA, 1); |
284 | if (ret) | |
285 | dev_dbg_once(dev->dev, "S2D force flush not supported: %d\n", ret); | |
286 | ||
3f720b21 SS |
287 | /* |
288 | * We have a custom stb size and the PMFW is supposed to give | |
289 | * the enhanced dram size. Note that we land here only for the | |
290 | * platforms that support enhanced dram size reporting. | |
291 | */ | |
292 | if (dump_custom_stb) | |
293 | return amd_pmc_stb_handle_efr(filp); | |
294 | ||
1ecfd309 | 295 | /* Get the num_samples to calculate the last push location */ |
139332e2 | 296 | ret = amd_pmc_send_cmd(dev, S2D_NUM_SAMPLES, &num_samples, dev->s2d_msg_id, true); |
1ecfd309 SS |
297 | /* Clear msg_port for other SMU operation */ |
298 | dev->msg_port = 0; | |
299 | if (ret) { | |
300 | dev_err(dev->dev, "error: S2D_NUM_SAMPLES not supported : %d\n", ret); | |
301 | return ret; | |
302 | } | |
303 | ||
d9f421da SS |
304 | fsize = min(num_samples, S2D_TELEMETRY_BYTES_MAX); |
305 | stb_data_arr = kmalloc(struct_size(stb_data_arr, data, fsize), GFP_KERNEL); | |
306 | if (!stb_data_arr) | |
307 | return -ENOMEM; | |
308 | ||
309 | stb_data_arr->size = fsize; | |
310 | ||
b1362257 SS |
311 | /* |
312 | * Start capturing data from the last push location. | |
313 | * This is for general cases, where the stb limits | |
314 | * are meant for standard usage. | |
315 | */ | |
1ecfd309 | 316 | if (num_samples > S2D_TELEMETRY_BYTES_MAX) { |
b1362257 SS |
317 | /* First read oldest data starting 1 behind last write till end of ringbuffer */ |
318 | stb_rdptr_offset = num_samples % S2D_TELEMETRY_BYTES_MAX; | |
319 | fsize = S2D_TELEMETRY_BYTES_MAX - stb_rdptr_offset; | |
320 | ||
321 | memcpy_fromio(stb_data_arr->data, dev->stb_virt_addr + stb_rdptr_offset, fsize); | |
322 | /* Second copy the newer samples from offset 0 - last write */ | |
323 | memcpy_fromio(stb_data_arr->data + fsize, dev->stb_virt_addr, stb_rdptr_offset); | |
1ecfd309 | 324 | } else { |
b1362257 | 325 | memcpy_fromio(stb_data_arr->data, dev->stb_virt_addr, fsize); |
1ecfd309 SS |
326 | } |
327 | ||
d9f421da | 328 | filp->private_data = stb_data_arr; |
3d7d407d SG |
329 | |
330 | return 0; | |
331 | } | |
332 | ||
333 | static ssize_t amd_pmc_stb_debugfs_read_v2(struct file *filp, char __user *buf, size_t size, | |
334 | loff_t *pos) | |
335 | { | |
d9f421da | 336 | struct amd_pmc_stb_v2_data *data = filp->private_data; |
3d7d407d | 337 | |
d9f421da | 338 | return simple_read_from_buffer(buf, size, pos, data->data, data->size); |
3d7d407d SG |
339 | } |
340 | ||
341 | static int amd_pmc_stb_debugfs_release_v2(struct inode *inode, struct file *filp) | |
342 | { | |
343 | kfree(filp->private_data); | |
344 | return 0; | |
345 | } | |
346 | ||
347 | static const struct file_operations amd_pmc_stb_debugfs_fops_v2 = { | |
348 | .owner = THIS_MODULE, | |
349 | .open = amd_pmc_stb_debugfs_open_v2, | |
350 | .read = amd_pmc_stb_debugfs_read_v2, | |
351 | .release = amd_pmc_stb_debugfs_release_v2, | |
352 | }; | |
353 | ||
139332e2 SS |
354 | static void amd_pmc_get_ip_info(struct amd_pmc_dev *dev) |
355 | { | |
356 | switch (dev->cpu_id) { | |
357 | case AMD_CPU_ID_PCO: | |
358 | case AMD_CPU_ID_RN: | |
359 | case AMD_CPU_ID_YC: | |
360 | case AMD_CPU_ID_CB: | |
361 | dev->num_ips = 12; | |
362 | dev->s2d_msg_id = 0xBE; | |
363 | break; | |
364 | case AMD_CPU_ID_PS: | |
365 | dev->num_ips = 21; | |
366 | dev->s2d_msg_id = 0x85; | |
367 | break; | |
368 | } | |
369 | } | |
370 | ||
17faaaca RZ |
371 | static int amd_pmc_setup_smu_logging(struct amd_pmc_dev *dev) |
372 | { | |
373 | if (dev->cpu_id == AMD_CPU_ID_PCO) { | |
374 | dev_warn_once(dev->dev, "SMU debugging info not supported on this platform\n"); | |
375 | return -EINVAL; | |
376 | } | |
377 | ||
378 | /* Get Active devices list from SMU */ | |
379 | if (!dev->active_ips) | |
a0d61b07 | 380 | amd_pmc_send_cmd(dev, 0, &dev->active_ips, SMU_MSG_GET_SUP_CONSTRAINTS, true); |
17faaaca RZ |
381 | |
382 | /* Get dram address */ | |
383 | if (!dev->smu_virt_addr) { | |
384 | u32 phys_addr_low, phys_addr_hi; | |
385 | u64 smu_phys_addr; | |
386 | ||
a0d61b07 SS |
387 | amd_pmc_send_cmd(dev, 0, &phys_addr_low, SMU_MSG_LOG_GETDRAM_ADDR_LO, true); |
388 | amd_pmc_send_cmd(dev, 0, &phys_addr_hi, SMU_MSG_LOG_GETDRAM_ADDR_HI, true); | |
17faaaca RZ |
389 | smu_phys_addr = ((u64)phys_addr_hi << 32 | phys_addr_low); |
390 | ||
391 | dev->smu_virt_addr = devm_ioremap(dev->dev, smu_phys_addr, | |
392 | sizeof(struct smu_metrics)); | |
393 | if (!dev->smu_virt_addr) | |
394 | return -ENOMEM; | |
395 | } | |
396 | ||
397 | /* Start the logging */ | |
a0d61b07 SS |
398 | amd_pmc_send_cmd(dev, 0, NULL, SMU_MSG_LOG_RESET, false); |
399 | amd_pmc_send_cmd(dev, 0, NULL, SMU_MSG_LOG_START, false); | |
17faaaca RZ |
400 | |
401 | return 0; | |
402 | } | |
403 | ||
854abe25 ML |
404 | static int get_metrics_table(struct amd_pmc_dev *pdev, struct smu_metrics *table) |
405 | { | |
0c211cec ML |
406 | if (!pdev->smu_virt_addr) { |
407 | int ret = amd_pmc_setup_smu_logging(pdev); | |
408 | ||
409 | if (ret) | |
410 | return ret; | |
411 | } | |
412 | ||
854abe25 ML |
413 | if (pdev->cpu_id == AMD_CPU_ID_PCO) |
414 | return -ENODEV; | |
415 | memcpy_fromio(table, pdev->smu_virt_addr, sizeof(struct smu_metrics)); | |
416 | return 0; | |
417 | } | |
418 | ||
419 | static void amd_pmc_validate_deepest(struct amd_pmc_dev *pdev) | |
420 | { | |
421 | struct smu_metrics table; | |
422 | ||
423 | if (get_metrics_table(pdev, &table)) | |
424 | return; | |
425 | ||
426 | if (!table.s0i3_last_entry_status) | |
427 | dev_warn(pdev->dev, "Last suspend didn't reach deepest state\n"); | |
09f5df3f ML |
428 | pm_report_hw_sleep_time(table.s0i3_last_entry_status ? |
429 | table.timein_s0i3_lastcapture : 0); | |
854abe25 ML |
430 | } |
431 | ||
9af48b26 NC |
432 | static int amd_pmc_get_smu_version(struct amd_pmc_dev *dev) |
433 | { | |
434 | int rc; | |
435 | u32 val; | |
436 | ||
b8457726 ML |
437 | if (dev->cpu_id == AMD_CPU_ID_PCO) |
438 | return -ENODEV; | |
439 | ||
a0d61b07 | 440 | rc = amd_pmc_send_cmd(dev, 0, &val, SMU_MSG_GETSMUVERSION, true); |
9af48b26 NC |
441 | if (rc) |
442 | return rc; | |
443 | ||
444 | dev->smu_program = (val >> 24) & GENMASK(7, 0); | |
445 | dev->major = (val >> 16) & GENMASK(7, 0); | |
446 | dev->minor = (val >> 8) & GENMASK(7, 0); | |
447 | dev->rev = (val >> 0) & GENMASK(7, 0); | |
448 | ||
449 | dev_dbg(dev->dev, "SMU program %u version is %u.%u.%u\n", | |
450 | dev->smu_program, dev->major, dev->minor, dev->rev); | |
451 | ||
452 | return 0; | |
453 | } | |
454 | ||
455 | static ssize_t smu_fw_version_show(struct device *d, struct device_attribute *attr, | |
456 | char *buf) | |
457 | { | |
458 | struct amd_pmc_dev *dev = dev_get_drvdata(d); | |
459 | ||
460 | if (!dev->major) { | |
461 | int rc = amd_pmc_get_smu_version(dev); | |
462 | ||
463 | if (rc) | |
464 | return rc; | |
465 | } | |
466 | return sysfs_emit(buf, "%u.%u.%u\n", dev->major, dev->minor, dev->rev); | |
467 | } | |
468 | ||
469 | static ssize_t smu_program_show(struct device *d, struct device_attribute *attr, | |
470 | char *buf) | |
471 | { | |
472 | struct amd_pmc_dev *dev = dev_get_drvdata(d); | |
473 | ||
474 | if (!dev->major) { | |
475 | int rc = amd_pmc_get_smu_version(dev); | |
476 | ||
477 | if (rc) | |
478 | return rc; | |
479 | } | |
480 | return sysfs_emit(buf, "%u\n", dev->smu_program); | |
481 | } | |
482 | ||
483 | static DEVICE_ATTR_RO(smu_fw_version); | |
484 | static DEVICE_ATTR_RO(smu_program); | |
485 | ||
5ec9ee0d ML |
486 | static umode_t pmc_attr_is_visible(struct kobject *kobj, struct attribute *attr, int idx) |
487 | { | |
488 | struct device *dev = kobj_to_dev(kobj); | |
489 | struct amd_pmc_dev *pdev = dev_get_drvdata(dev); | |
490 | ||
491 | if (pdev->cpu_id == AMD_CPU_ID_PCO) | |
492 | return 0; | |
493 | return 0444; | |
494 | } | |
495 | ||
9af48b26 NC |
496 | static struct attribute *pmc_attrs[] = { |
497 | &dev_attr_smu_fw_version.attr, | |
498 | &dev_attr_smu_program.attr, | |
499 | NULL, | |
500 | }; | |
5ec9ee0d ML |
501 | |
502 | static struct attribute_group pmc_attr_group = { | |
503 | .attrs = pmc_attrs, | |
504 | .is_visible = pmc_attr_is_visible, | |
505 | }; | |
506 | ||
507 | static const struct attribute_group *pmc_groups[] = { | |
508 | &pmc_attr_group, | |
509 | NULL, | |
510 | }; | |
9af48b26 | 511 | |
156ec473 SS |
512 | static int smu_fw_info_show(struct seq_file *s, void *unused) |
513 | { | |
76620567 SS |
514 | struct amd_pmc_dev *dev = s->private; |
515 | struct smu_metrics table; | |
516 | int idx; | |
517 | ||
854abe25 | 518 | if (get_metrics_table(dev, &table)) |
76620567 SS |
519 | return -EINVAL; |
520 | ||
76620567 SS |
521 | seq_puts(s, "\n=== SMU Statistics ===\n"); |
522 | seq_printf(s, "Table Version: %d\n", table.table_version); | |
523 | seq_printf(s, "Hint Count: %d\n", table.hint_count); | |
9cfe0202 SG |
524 | seq_printf(s, "Last S0i3 Status: %s\n", table.s0i3_last_entry_status ? "Success" : |
525 | "Unknown/Fail"); | |
76620567 SS |
526 | seq_printf(s, "Time (in us) to S0i3: %lld\n", table.timeentering_s0i3_lastcapture); |
527 | seq_printf(s, "Time (in us) in S0i3: %lld\n", table.timein_s0i3_lastcapture); | |
7dbcaf74 SG |
528 | seq_printf(s, "Time (in us) to resume from S0i3: %lld\n", |
529 | table.timeto_resume_to_os_lastcapture); | |
76620567 SS |
530 | |
531 | seq_puts(s, "\n=== Active time (in us) ===\n"); | |
139332e2 | 532 | for (idx = 0 ; idx < dev->num_ips ; idx++) { |
76620567 SS |
533 | if (soc15_ip_blk[idx].bit_mask & dev->active_ips) |
534 | seq_printf(s, "%-8s : %lld\n", soc15_ip_blk[idx].name, | |
535 | table.timecondition_notmet_lastcapture[idx]); | |
536 | } | |
537 | ||
156ec473 SS |
538 | return 0; |
539 | } | |
540 | DEFINE_SHOW_ATTRIBUTE(smu_fw_info); | |
541 | ||
b9a4fa69 SS |
542 | static int s0ix_stats_show(struct seq_file *s, void *unused) |
543 | { | |
544 | struct amd_pmc_dev *dev = s->private; | |
545 | u64 entry_time, exit_time, residency; | |
546 | ||
63585d59 ML |
547 | /* Use FCH registers to get the S0ix stats */ |
548 | if (!dev->fch_virt_addr) { | |
549 | u32 base_addr_lo = FCH_BASE_PHY_ADDR_LOW; | |
550 | u32 base_addr_hi = FCH_BASE_PHY_ADDR_HIGH; | |
551 | u64 fch_phys_addr = ((u64)base_addr_hi << 32 | base_addr_lo); | |
552 | ||
553 | dev->fch_virt_addr = devm_ioremap(dev->dev, fch_phys_addr, FCH_SSC_MAPPING_SIZE); | |
554 | if (!dev->fch_virt_addr) | |
555 | return -ENOMEM; | |
556 | } | |
557 | ||
b9a4fa69 SS |
558 | entry_time = ioread32(dev->fch_virt_addr + FCH_S0I3_ENTRY_TIME_H_OFFSET); |
559 | entry_time = entry_time << 32 | ioread32(dev->fch_virt_addr + FCH_S0I3_ENTRY_TIME_L_OFFSET); | |
560 | ||
561 | exit_time = ioread32(dev->fch_virt_addr + FCH_S0I3_EXIT_TIME_H_OFFSET); | |
562 | exit_time = exit_time << 32 | ioread32(dev->fch_virt_addr + FCH_S0I3_EXIT_TIME_L_OFFSET); | |
563 | ||
564 | /* It's in 48MHz. We need to convert it */ | |
7f5231b1 SS |
565 | residency = exit_time - entry_time; |
566 | do_div(residency, 48); | |
b9a4fa69 SS |
567 | |
568 | seq_puts(s, "=== S0ix statistics ===\n"); | |
569 | seq_printf(s, "S0ix Entry Time: %lld\n", entry_time); | |
570 | seq_printf(s, "S0ix Exit Time: %lld\n", exit_time); | |
571 | seq_printf(s, "Residency Time: %lld\n", residency); | |
572 | ||
573 | return 0; | |
574 | } | |
575 | DEFINE_SHOW_ATTRIBUTE(s0ix_stats); | |
576 | ||
9217bd1d ML |
577 | static int amd_pmc_idlemask_read(struct amd_pmc_dev *pdev, struct device *dev, |
578 | struct seq_file *s) | |
f6045de1 | 579 | { |
9217bd1d | 580 | u32 val; |
f6045de1 SG |
581 | int rc; |
582 | ||
9217bd1d ML |
583 | switch (pdev->cpu_id) { |
584 | case AMD_CPU_ID_CZN: | |
585 | /* we haven't yet read SMU version */ | |
586 | if (!pdev->major) { | |
587 | rc = amd_pmc_get_smu_version(pdev); | |
588 | if (rc) | |
589 | return rc; | |
590 | } | |
591 | if (pdev->major > 56 || (pdev->major >= 55 && pdev->minor >= 37)) | |
592 | val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_CZN); | |
593 | else | |
594 | return -EINVAL; | |
595 | break; | |
596 | case AMD_CPU_ID_YC: | |
597 | case AMD_CPU_ID_CB: | |
598 | case AMD_CPU_ID_PS: | |
599 | val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_YC); | |
600 | break; | |
601 | default: | |
602 | return -EINVAL; | |
b0c07116 ML |
603 | } |
604 | ||
9217bd1d | 605 | if (dev) |
b77505ed | 606 | pm_pr_dbg("SMU idlemask s0i3: 0x%x\n", val); |
9217bd1d ML |
607 | |
608 | if (s) | |
609 | seq_printf(s, "SMU idlemask : 0x%x\n", val); | |
f6045de1 SG |
610 | |
611 | return 0; | |
612 | } | |
9217bd1d ML |
613 | |
614 | static int amd_pmc_idlemask_show(struct seq_file *s, void *unused) | |
615 | { | |
616 | return amd_pmc_idlemask_read(s->private, NULL, s); | |
617 | } | |
f6045de1 SG |
618 | DEFINE_SHOW_ATTRIBUTE(amd_pmc_idlemask); |
619 | ||
156ec473 SS |
620 | static void amd_pmc_dbgfs_unregister(struct amd_pmc_dev *dev) |
621 | { | |
622 | debugfs_remove_recursive(dev->dbgfs_dir); | |
623 | } | |
624 | ||
5d50eef3 SS |
625 | static bool amd_pmc_is_stb_supported(struct amd_pmc_dev *dev) |
626 | { | |
627 | switch (dev->cpu_id) { | |
628 | case AMD_CPU_ID_YC: | |
629 | case AMD_CPU_ID_CB: | |
630 | case AMD_CPU_ID_PS: | |
631 | return true; | |
632 | default: | |
633 | return false; | |
634 | } | |
635 | } | |
636 | ||
156ec473 SS |
637 | static void amd_pmc_dbgfs_register(struct amd_pmc_dev *dev) |
638 | { | |
639 | dev->dbgfs_dir = debugfs_create_dir("amd_pmc", NULL); | |
640 | debugfs_create_file("smu_fw_info", 0644, dev->dbgfs_dir, dev, | |
641 | &smu_fw_info_fops); | |
b9a4fa69 SS |
642 | debugfs_create_file("s0ix_stats", 0644, dev->dbgfs_dir, dev, |
643 | &s0ix_stats_fops); | |
f6045de1 SG |
644 | debugfs_create_file("amd_pmc_idlemask", 0644, dev->dbgfs_dir, dev, |
645 | &amd_pmc_idlemask_fops); | |
426c0ff2 | 646 | /* Enable STB only when the module_param is set */ |
3d7d407d | 647 | if (enable_stb) { |
5d50eef3 | 648 | if (amd_pmc_is_stb_supported(dev)) |
3d7d407d SG |
649 | debugfs_create_file("stb_read", 0644, dev->dbgfs_dir, dev, |
650 | &amd_pmc_stb_debugfs_fops_v2); | |
651 | else | |
652 | debugfs_create_file("stb_read", 0644, dev->dbgfs_dir, dev, | |
653 | &amd_pmc_stb_debugfs_fops); | |
654 | } | |
156ec473 | 655 | } |
156ec473 SS |
656 | |
657 | static void amd_pmc_dump_registers(struct amd_pmc_dev *dev) | |
658 | { | |
3d7d407d SG |
659 | u32 value, message, argument, response; |
660 | ||
661 | if (dev->msg_port) { | |
662 | message = AMD_S2D_REGISTER_MESSAGE; | |
663 | argument = AMD_S2D_REGISTER_ARGUMENT; | |
664 | response = AMD_S2D_REGISTER_RESPONSE; | |
665 | } else { | |
666 | message = AMD_PMC_REGISTER_MESSAGE; | |
667 | argument = AMD_PMC_REGISTER_ARGUMENT; | |
668 | response = AMD_PMC_REGISTER_RESPONSE; | |
669 | } | |
156ec473 | 670 | |
3d7d407d | 671 | value = amd_pmc_reg_read(dev, response); |
1ac252a5 | 672 | dev_dbg(dev->dev, "AMD_%s_REGISTER_RESPONSE:%x\n", dev->msg_port ? "S2D" : "PMC", value); |
156ec473 | 673 | |
3d7d407d | 674 | value = amd_pmc_reg_read(dev, argument); |
1ac252a5 | 675 | dev_dbg(dev->dev, "AMD_%s_REGISTER_ARGUMENT:%x\n", dev->msg_port ? "S2D" : "PMC", value); |
156ec473 | 676 | |
3d7d407d | 677 | value = amd_pmc_reg_read(dev, message); |
1ac252a5 | 678 | dev_dbg(dev->dev, "AMD_%s_REGISTER_MESSAGE:%x\n", dev->msg_port ? "S2D" : "PMC", value); |
156ec473 SS |
679 | } |
680 | ||
4c9dbf86 | 681 | static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool ret) |
156ec473 SS |
682 | { |
683 | int rc; | |
3d7d407d | 684 | u32 val, message, argument, response; |
156ec473 | 685 | |
95e1b60f | 686 | mutex_lock(&dev->lock); |
3d7d407d SG |
687 | |
688 | if (dev->msg_port) { | |
689 | message = AMD_S2D_REGISTER_MESSAGE; | |
690 | argument = AMD_S2D_REGISTER_ARGUMENT; | |
691 | response = AMD_S2D_REGISTER_RESPONSE; | |
692 | } else { | |
693 | message = AMD_PMC_REGISTER_MESSAGE; | |
694 | argument = AMD_PMC_REGISTER_ARGUMENT; | |
695 | response = AMD_PMC_REGISTER_RESPONSE; | |
696 | } | |
697 | ||
156ec473 | 698 | /* Wait until we get a valid response */ |
3d7d407d | 699 | rc = readx_poll_timeout(ioread32, dev->regbase + response, |
95e1b60f | 700 | val, val != 0, PMC_MSG_DELAY_MIN_US, |
156ec473 SS |
701 | PMC_MSG_DELAY_MIN_US * RESPONSE_REGISTER_LOOP_MAX); |
702 | if (rc) { | |
703 | dev_err(dev->dev, "failed to talk to SMU\n"); | |
95edbbf7 | 704 | goto out_unlock; |
156ec473 SS |
705 | } |
706 | ||
707 | /* Write zero to response register */ | |
3d7d407d | 708 | amd_pmc_reg_write(dev, response, 0); |
156ec473 SS |
709 | |
710 | /* Write argument into response register */ | |
3d7d407d | 711 | amd_pmc_reg_write(dev, argument, arg); |
156ec473 SS |
712 | |
713 | /* Write message ID to message ID register */ | |
3d7d407d | 714 | amd_pmc_reg_write(dev, message, msg); |
76620567 | 715 | |
95e1b60f | 716 | /* Wait until we get a valid response */ |
3d7d407d | 717 | rc = readx_poll_timeout(ioread32, dev->regbase + response, |
95e1b60f SS |
718 | val, val != 0, PMC_MSG_DELAY_MIN_US, |
719 | PMC_MSG_DELAY_MIN_US * RESPONSE_REGISTER_LOOP_MAX); | |
720 | if (rc) { | |
721 | dev_err(dev->dev, "SMU response timed out\n"); | |
722 | goto out_unlock; | |
723 | } | |
724 | ||
725 | switch (val) { | |
726 | case AMD_PMC_RESULT_OK: | |
76620567 SS |
727 | if (ret) { |
728 | /* PMFW may take longer time to return back the data */ | |
729 | usleep_range(DELAY_MIN_US, 10 * DELAY_MAX_US); | |
3d7d407d | 730 | *data = amd_pmc_reg_read(dev, argument); |
76620567 | 731 | } |
95e1b60f SS |
732 | break; |
733 | case AMD_PMC_RESULT_CMD_REJECT_BUSY: | |
734 | dev_err(dev->dev, "SMU not ready. err: 0x%x\n", val); | |
735 | rc = -EBUSY; | |
736 | goto out_unlock; | |
737 | case AMD_PMC_RESULT_CMD_UNKNOWN: | |
738 | dev_err(dev->dev, "SMU cmd unknown. err: 0x%x\n", val); | |
739 | rc = -EINVAL; | |
740 | goto out_unlock; | |
741 | case AMD_PMC_RESULT_CMD_REJECT_PREREQ: | |
742 | case AMD_PMC_RESULT_FAILED: | |
743 | default: | |
744 | dev_err(dev->dev, "SMU cmd failed. err: 0x%x\n", val); | |
745 | rc = -EIO; | |
746 | goto out_unlock; | |
747 | } | |
748 | ||
749 | out_unlock: | |
750 | mutex_unlock(&dev->lock); | |
162b937a | 751 | amd_pmc_dump_registers(dev); |
95e1b60f | 752 | return rc; |
156ec473 SS |
753 | } |
754 | ||
76620567 SS |
755 | static int amd_pmc_get_os_hint(struct amd_pmc_dev *dev) |
756 | { | |
757 | switch (dev->cpu_id) { | |
758 | case AMD_CPU_ID_PCO: | |
759 | return MSG_OS_HINT_PCO; | |
760 | case AMD_CPU_ID_RN: | |
83cbaf14 | 761 | case AMD_CPU_ID_YC: |
221b8b21 | 762 | case AMD_CPU_ID_CB: |
035c8a91 | 763 | case AMD_CPU_ID_PS: |
76620567 SS |
764 | return MSG_OS_HINT_RN; |
765 | } | |
766 | return -EINVAL; | |
767 | } | |
768 | ||
8e60615e ML |
769 | static int amd_pmc_czn_wa_irq1(struct amd_pmc_dev *pdev) |
770 | { | |
771 | struct device *d; | |
772 | int rc; | |
773 | ||
774 | if (!pdev->major) { | |
775 | rc = amd_pmc_get_smu_version(pdev); | |
776 | if (rc) | |
777 | return rc; | |
778 | } | |
779 | ||
780 | if (pdev->major > 64 || (pdev->major == 64 && pdev->minor > 65)) | |
781 | return 0; | |
782 | ||
783 | d = bus_find_device_by_name(&serio_bus, NULL, "serio0"); | |
784 | if (!d) | |
785 | return 0; | |
786 | if (device_may_wakeup(d)) { | |
787 | dev_info_once(d, "Disabling IRQ1 wakeup source to avoid platform firmware bug\n"); | |
788 | disable_irq_wake(1); | |
789 | device_set_wakeup_enable(d, false); | |
790 | } | |
791 | put_device(d); | |
792 | ||
793 | return 0; | |
794 | } | |
795 | ||
59348401 ML |
796 | static int amd_pmc_verify_czn_rtc(struct amd_pmc_dev *pdev, u32 *arg) |
797 | { | |
798 | struct rtc_device *rtc_device; | |
799 | time64_t then, now, duration; | |
800 | struct rtc_wkalrm alarm; | |
801 | struct rtc_time tm; | |
802 | int rc; | |
803 | ||
0b6e6e14 ML |
804 | /* we haven't yet read SMU version */ |
805 | if (!pdev->major) { | |
806 | rc = amd_pmc_get_smu_version(pdev); | |
807 | if (rc) | |
808 | return rc; | |
809 | } | |
810 | ||
59348401 ML |
811 | if (pdev->major < 64 || (pdev->major == 64 && pdev->minor < 53)) |
812 | return 0; | |
813 | ||
2978891a | 814 | rtc_device = rtc_class_open("rtc0"); |
59348401 ML |
815 | if (!rtc_device) |
816 | return 0; | |
817 | rc = rtc_read_alarm(rtc_device, &alarm); | |
818 | if (rc) | |
819 | return rc; | |
820 | if (!alarm.enabled) { | |
821 | dev_dbg(pdev->dev, "alarm not enabled\n"); | |
822 | return 0; | |
823 | } | |
59348401 ML |
824 | rc = rtc_read_time(rtc_device, &tm); |
825 | if (rc) | |
826 | return rc; | |
827 | then = rtc_tm_to_time64(&alarm.time); | |
828 | now = rtc_tm_to_time64(&tm); | |
829 | duration = then-now; | |
830 | ||
831 | /* in the past */ | |
832 | if (then < now) | |
833 | return 0; | |
834 | ||
835 | /* will be stored in upper 16 bits of s0i3 hint argument, | |
836 | * so timer wakeup from s0i3 is limited to ~18 hours or less | |
837 | */ | |
838 | if (duration <= 4 || duration > U16_MAX) | |
839 | return -EINVAL; | |
840 | ||
841 | *arg |= (duration << 16); | |
842 | rc = rtc_alarm_irq_enable(rtc_device, 0); | |
b77505ed | 843 | pm_pr_dbg("wakeup timer programmed for %lld seconds\n", duration); |
59348401 ML |
844 | |
845 | return rc; | |
846 | } | |
847 | ||
b1f66033 | 848 | static void amd_pmc_s2idle_prepare(void) |
156ec473 | 849 | { |
b1f66033 | 850 | struct amd_pmc_dev *pdev = &pmc; |
156ec473 | 851 | int rc; |
76620567 | 852 | u8 msg; |
59348401 | 853 | u32 arg = 1; |
156ec473 | 854 | |
76620567 | 855 | /* Reset and Start SMU logging - to monitor the s0i3 stats */ |
0c211cec | 856 | amd_pmc_setup_smu_logging(pdev); |
76620567 | 857 | |
062c1394 ML |
858 | /* Activate CZN specific platform bug workarounds */ |
859 | if (pdev->cpu_id == AMD_CPU_ID_CZN && !disable_workarounds) { | |
59348401 | 860 | rc = amd_pmc_verify_czn_rtc(pdev, &arg); |
23f5f700 ML |
861 | if (rc) { |
862 | dev_err(pdev->dev, "failed to set RTC: %d\n", rc); | |
0d64787e | 863 | return; |
23f5f700 | 864 | } |
59348401 ML |
865 | } |
866 | ||
76620567 | 867 | msg = amd_pmc_get_os_hint(pdev); |
a0d61b07 | 868 | rc = amd_pmc_send_cmd(pdev, arg, NULL, msg, false); |
32370191 | 869 | if (rc) { |
23f5f700 | 870 | dev_err(pdev->dev, "suspend failed: %d\n", rc); |
0d64787e | 871 | return; |
32370191 | 872 | } |
156ec473 | 873 | |
90bec285 ML |
874 | rc = amd_pmc_write_stb(pdev, AMD_PMC_STB_S2IDLE_PREPARE); |
875 | if (rc) | |
876 | dev_err(pdev->dev, "error writing to STB: %d\n", rc); | |
156ec473 SS |
877 | } |
878 | ||
db55fb8a ML |
879 | static void amd_pmc_s2idle_check(void) |
880 | { | |
881 | struct amd_pmc_dev *pdev = &pmc; | |
e4678483 | 882 | struct smu_metrics table; |
db55fb8a ML |
883 | int rc; |
884 | ||
e4678483 ML |
885 | /* CZN: Ensure that future s0i3 entry attempts at least 10ms passed */ |
886 | if (pdev->cpu_id == AMD_CPU_ID_CZN && !get_metrics_table(pdev, &table) && | |
887 | table.s0i3_last_entry_status) | |
888 | usleep_range(10000, 20000); | |
889 | ||
c928df03 ML |
890 | /* Dump the IdleMask before we add to the STB */ |
891 | amd_pmc_idlemask_read(pdev, pdev->dev, NULL); | |
892 | ||
db55fb8a ML |
893 | rc = amd_pmc_write_stb(pdev, AMD_PMC_STB_S2IDLE_CHECK); |
894 | if (rc) | |
895 | dev_err(pdev->dev, "error writing to STB: %d\n", rc); | |
896 | } | |
897 | ||
7abc3618 ML |
898 | static int amd_pmc_dump_data(struct amd_pmc_dev *pdev) |
899 | { | |
900 | if (pdev->cpu_id == AMD_CPU_ID_PCO) | |
901 | return -ENODEV; | |
902 | ||
a0d61b07 | 903 | return amd_pmc_send_cmd(pdev, 0, NULL, SMU_MSG_LOG_DUMP_DATA, false); |
7abc3618 ML |
904 | } |
905 | ||
b1f66033 | 906 | static void amd_pmc_s2idle_restore(void) |
156ec473 | 907 | { |
b1f66033 | 908 | struct amd_pmc_dev *pdev = &pmc; |
156ec473 | 909 | int rc; |
76620567 | 910 | u8 msg; |
156ec473 | 911 | |
76620567 | 912 | msg = amd_pmc_get_os_hint(pdev); |
a0d61b07 | 913 | rc = amd_pmc_send_cmd(pdev, 0, NULL, msg, false); |
156ec473 | 914 | if (rc) |
23f5f700 | 915 | dev_err(pdev->dev, "resume failed: %d\n", rc); |
156ec473 | 916 | |
9c93f8f4 | 917 | /* Let SMU know that we are looking for stats */ |
7abc3618 | 918 | amd_pmc_dump_data(pdev); |
9c93f8f4 | 919 | |
90bec285 ML |
920 | rc = amd_pmc_write_stb(pdev, AMD_PMC_STB_S2IDLE_RESTORE); |
921 | if (rc) | |
922 | dev_err(pdev->dev, "error writing to STB: %d\n", rc); | |
426c0ff2 | 923 | |
854abe25 ML |
924 | /* Notify on failed entry */ |
925 | amd_pmc_validate_deepest(pdev); | |
e8ef8dd2 ML |
926 | |
927 | amd_pmc_process_restore_quirks(pdev); | |
156ec473 SS |
928 | } |
929 | ||
b1f66033 ML |
930 | static struct acpi_s2idle_dev_ops amd_pmc_s2idle_dev_ops = { |
931 | .prepare = amd_pmc_s2idle_prepare, | |
db55fb8a | 932 | .check = amd_pmc_s2idle_check, |
b1f66033 | 933 | .restore = amd_pmc_s2idle_restore, |
156ec473 | 934 | }; |
8e60615e | 935 | |
5b309e80 | 936 | static int amd_pmc_suspend_handler(struct device *dev) |
8e60615e ML |
937 | { |
938 | struct amd_pmc_dev *pdev = dev_get_drvdata(dev); | |
939 | ||
062c1394 | 940 | if (pdev->cpu_id == AMD_CPU_ID_CZN && !disable_workarounds) { |
8e60615e ML |
941 | int rc = amd_pmc_czn_wa_irq1(pdev); |
942 | ||
943 | if (rc) { | |
944 | dev_err(pdev->dev, "failed to adjust keyboard wakeup: %d\n", rc); | |
945 | return rc; | |
946 | } | |
947 | } | |
948 | ||
949 | return 0; | |
950 | } | |
951 | ||
24efcdf0 | 952 | static DEFINE_SIMPLE_DEV_PM_OPS(amd_pmc_pm, amd_pmc_suspend_handler, NULL); |
156ec473 SS |
953 | |
954 | static const struct pci_device_id pmc_pci_ids[] = { | |
035c8a91 | 955 | { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_PS) }, |
221b8b21 | 956 | { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_CB) }, |
83cbaf14 | 957 | { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_YC) }, |
156ec473 SS |
958 | { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_CZN) }, |
959 | { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_RN) }, | |
960 | { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_PCO) }, | |
961 | { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_RV) }, | |
efebfa80 | 962 | { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_SP) }, |
822507ca | 963 | { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_1AH_M20H_ROOT) }, |
156ec473 SS |
964 | { } |
965 | }; | |
966 | ||
3d7d407d SG |
967 | static int amd_pmc_s2d_init(struct amd_pmc_dev *dev) |
968 | { | |
d713b8d2 | 969 | u32 phys_addr_low, phys_addr_hi; |
3d7d407d | 970 | u64 stb_phys_addr; |
d713b8d2 | 971 | u32 size = 0; |
be8325fb | 972 | int ret; |
3d7d407d SG |
973 | |
974 | /* Spill to DRAM feature uses separate SMU message port */ | |
975 | dev->msg_port = 1; | |
976 | ||
139332e2 SS |
977 | /* Get num of IP blocks within the SoC */ |
978 | amd_pmc_get_ip_info(dev); | |
979 | ||
980 | amd_pmc_send_cmd(dev, S2D_TELEMETRY_SIZE, &size, dev->s2d_msg_id, true); | |
3d7d407d SG |
981 | if (size != S2D_TELEMETRY_BYTES_MAX) |
982 | return -EIO; | |
983 | ||
be8325fb | 984 | /* Get DRAM size */ |
c6ea14d5 SS |
985 | ret = amd_pmc_send_cmd(dev, S2D_DRAM_SIZE, &dev->dram_size, dev->s2d_msg_id, true); |
986 | if (ret || !dev->dram_size) | |
be8325fb SS |
987 | dev->dram_size = S2D_TELEMETRY_DRAMBYTES_MAX; |
988 | ||
3d7d407d | 989 | /* Get STB DRAM address */ |
139332e2 SS |
990 | amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_LOW, &phys_addr_low, dev->s2d_msg_id, true); |
991 | amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_HIGH, &phys_addr_hi, dev->s2d_msg_id, true); | |
3d7d407d SG |
992 | |
993 | stb_phys_addr = ((u64)phys_addr_hi << 32 | phys_addr_low); | |
994 | ||
995 | /* Clear msg_port for other SMU operation */ | |
996 | dev->msg_port = 0; | |
997 | ||
be8325fb | 998 | dev->stb_virt_addr = devm_ioremap(dev->dev, stb_phys_addr, dev->dram_size); |
3d7d407d SG |
999 | if (!dev->stb_virt_addr) |
1000 | return -ENOMEM; | |
1001 | ||
1002 | return 0; | |
1003 | } | |
1004 | ||
426c0ff2 SG |
1005 | static int amd_pmc_write_stb(struct amd_pmc_dev *dev, u32 data) |
1006 | { | |
1007 | int err; | |
1008 | ||
8d99129e | 1009 | err = amd_smn_write(0, AMD_PMC_STB_PMI_0, data); |
426c0ff2 | 1010 | if (err) { |
8d99129e | 1011 | dev_err(dev->dev, "failed to write data in stb: 0x%X\n", AMD_PMC_STB_PMI_0); |
426c0ff2 SG |
1012 | return pcibios_err_to_errno(err); |
1013 | } | |
1014 | ||
1015 | return 0; | |
1016 | } | |
1017 | ||
1018 | static int amd_pmc_read_stb(struct amd_pmc_dev *dev, u32 *buf) | |
1019 | { | |
1020 | int i, err; | |
1021 | ||
426c0ff2 | 1022 | for (i = 0; i < FIFO_SIZE; i++) { |
8d99129e | 1023 | err = amd_smn_read(0, AMD_PMC_STB_PMI_0, buf++); |
426c0ff2 | 1024 | if (err) { |
8d99129e | 1025 | dev_err(dev->dev, "error reading data from stb: 0x%X\n", AMD_PMC_STB_PMI_0); |
426c0ff2 SG |
1026 | return pcibios_err_to_errno(err); |
1027 | } | |
1028 | } | |
1029 | ||
1030 | return 0; | |
1031 | } | |
1032 | ||
156ec473 SS |
1033 | static int amd_pmc_probe(struct platform_device *pdev) |
1034 | { | |
1035 | struct amd_pmc_dev *dev = &pmc; | |
1036 | struct pci_dev *rdev; | |
76620567 | 1037 | u32 base_addr_lo, base_addr_hi; |
63585d59 | 1038 | u64 base_addr; |
156ec473 SS |
1039 | int err; |
1040 | u32 val; | |
1041 | ||
1042 | dev->dev = &pdev->dev; | |
1043 | ||
1044 | rdev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0, 0)); | |
745ed17a | 1045 | if (!rdev || !pci_match_id(pmc_pci_ids, rdev)) { |
6a5a14b1 SG |
1046 | err = -ENODEV; |
1047 | goto err_pci_dev_put; | |
745ed17a | 1048 | } |
156ec473 SS |
1049 | |
1050 | dev->cpu_id = rdev->device; | |
efebfa80 SS |
1051 | |
1052 | if (dev->cpu_id == AMD_CPU_ID_SP) { | |
1053 | dev_warn_once(dev->dev, "S0i3 is not supported on this hardware\n"); | |
1054 | err = -ENODEV; | |
1055 | goto err_pci_dev_put; | |
1056 | } | |
1057 | ||
6a5a14b1 | 1058 | dev->rdev = rdev; |
310e782a | 1059 | err = amd_smn_read(0, AMD_PMC_BASE_ADDR_LO, &val); |
745ed17a | 1060 | if (err) { |
310e782a | 1061 | dev_err(dev->dev, "error reading 0x%x\n", AMD_PMC_BASE_ADDR_LO); |
6a5a14b1 SG |
1062 | err = pcibios_err_to_errno(err); |
1063 | goto err_pci_dev_put; | |
745ed17a | 1064 | } |
156ec473 SS |
1065 | |
1066 | base_addr_lo = val & AMD_PMC_BASE_ADDR_HI_MASK; | |
1067 | ||
310e782a | 1068 | err = amd_smn_read(0, AMD_PMC_BASE_ADDR_HI, &val); |
745ed17a | 1069 | if (err) { |
310e782a | 1070 | dev_err(dev->dev, "error reading 0x%x\n", AMD_PMC_BASE_ADDR_HI); |
6a5a14b1 SG |
1071 | err = pcibios_err_to_errno(err); |
1072 | goto err_pci_dev_put; | |
745ed17a | 1073 | } |
156ec473 SS |
1074 | |
1075 | base_addr_hi = val & AMD_PMC_BASE_ADDR_LO_MASK; | |
156ec473 SS |
1076 | base_addr = ((u64)base_addr_hi << 32 | base_addr_lo); |
1077 | ||
156ec473 SS |
1078 | dev->regbase = devm_ioremap(dev->dev, base_addr + AMD_PMC_BASE_ADDR_OFFSET, |
1079 | AMD_PMC_MAPPING_SIZE); | |
6a5a14b1 SG |
1080 | if (!dev->regbase) { |
1081 | err = -ENOMEM; | |
1082 | goto err_pci_dev_put; | |
1083 | } | |
156ec473 | 1084 | |
95e1b60f | 1085 | mutex_init(&dev->lock); |
76620567 | 1086 | |
5d50eef3 | 1087 | if (enable_stb && amd_pmc_is_stb_supported(dev)) { |
3d7d407d SG |
1088 | err = amd_pmc_s2d_init(dev); |
1089 | if (err) | |
ccb32e2b | 1090 | goto err_pci_dev_put; |
3d7d407d SG |
1091 | } |
1092 | ||
156ec473 | 1093 | platform_set_drvdata(pdev, dev); |
24efcdf0 AB |
1094 | if (IS_ENABLED(CONFIG_SUSPEND)) { |
1095 | err = acpi_register_lps0_dev(&amd_pmc_s2idle_dev_ops); | |
1096 | if (err) | |
1097 | dev_warn(dev->dev, "failed to register LPS0 sleep handler, expect increased power consumption\n"); | |
e8ef8dd2 ML |
1098 | if (!disable_workarounds) |
1099 | amd_pmc_quirks_init(dev); | |
24efcdf0 | 1100 | } |
b1f66033 | 1101 | |
156ec473 | 1102 | amd_pmc_dbgfs_register(dev); |
09f5df3f | 1103 | pm_report_max_hw_sleep(U64_MAX); |
156ec473 | 1104 | return 0; |
6a5a14b1 SG |
1105 | |
1106 | err_pci_dev_put: | |
1107 | pci_dev_put(rdev); | |
1108 | return err; | |
156ec473 SS |
1109 | } |
1110 | ||
3359d99e | 1111 | static void amd_pmc_remove(struct platform_device *pdev) |
156ec473 SS |
1112 | { |
1113 | struct amd_pmc_dev *dev = platform_get_drvdata(pdev); | |
1114 | ||
24efcdf0 AB |
1115 | if (IS_ENABLED(CONFIG_SUSPEND)) |
1116 | acpi_unregister_lps0_dev(&amd_pmc_s2idle_dev_ops); | |
156ec473 | 1117 | amd_pmc_dbgfs_unregister(dev); |
6a5a14b1 | 1118 | pci_dev_put(dev->rdev); |
95e1b60f | 1119 | mutex_destroy(&dev->lock); |
156ec473 SS |
1120 | } |
1121 | ||
1122 | static const struct acpi_device_id amd_pmc_acpi_ids[] = { | |
1123 | {"AMDI0005", 0}, | |
9422584a | 1124 | {"AMDI0006", 0}, |
83cbaf14 | 1125 | {"AMDI0007", 0}, |
221b8b21 | 1126 | {"AMDI0008", 0}, |
6412518f | 1127 | {"AMDI0009", 0}, |
822507ca | 1128 | {"AMDI000A", 0}, |
156ec473 | 1129 | {"AMD0004", 0}, |
432cce21 | 1130 | {"AMD0005", 0}, |
156ec473 SS |
1131 | { } |
1132 | }; | |
1133 | MODULE_DEVICE_TABLE(acpi, amd_pmc_acpi_ids); | |
1134 | ||
1135 | static struct platform_driver amd_pmc_driver = { | |
1136 | .driver = { | |
1137 | .name = "amd_pmc", | |
1138 | .acpi_match_table = amd_pmc_acpi_ids, | |
7f1ea75d | 1139 | .dev_groups = pmc_groups, |
24efcdf0 | 1140 | .pm = pm_sleep_ptr(&amd_pmc_pm), |
156ec473 SS |
1141 | }, |
1142 | .probe = amd_pmc_probe, | |
3359d99e | 1143 | .remove_new = amd_pmc_remove, |
156ec473 SS |
1144 | }; |
1145 | module_platform_driver(amd_pmc_driver); | |
1146 | ||
1147 | MODULE_LICENSE("GPL v2"); | |
1148 | MODULE_DESCRIPTION("AMD PMC Driver"); |