1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24#define SWSMU_CODE_LAYER_L2
25
26#include "amdgpu.h"
27#include "amdgpu_smu.h"
28#include "smu_v11_0.h"
29#include "smu11_driver_if_cyan_skillfish.h"
30#include "cyan_skillfish_ppt.h"
31#include "smu_v11_8_ppsmc.h"
32#include "smu_v11_8_pmfw.h"
33#include "smu_cmn.h"
34#include "soc15_common.h"
35
36
37
38
39
40
41
42#undef pr_err
43#undef pr_warn
44#undef pr_info
45#undef pr_debug
46
47
48#define CYAN_SKILLFISH_SCLK_MIN 1000
49#define CYAN_SKILLFISH_SCLK_MAX 2000
50
51
52#define CYAN_SKILLFISH_VDDC_MIN 700
53#define CYAN_SKILLFISH_VDDC_MAX 1129
54#define CYAN_SKILLFISH_VDDC_MAGIC 5118
55
56static struct gfx_user_settings {
57 uint32_t sclk;
58 uint32_t vddc;
59} cyan_skillfish_user_settings;
60
61static uint32_t cyan_skillfish_sclk_default;
62
63#define FEATURE_MASK(feature) (1ULL << feature)
64#define SMC_DPM_FEATURE ( \
65 FEATURE_MASK(FEATURE_FCLK_DPM_BIT) | \
66 FEATURE_MASK(FEATURE_SOC_DPM_BIT) | \
67 FEATURE_MASK(FEATURE_GFX_DPM_BIT))
68
69static struct cmn2asic_msg_mapping cyan_skillfish_message_map[SMU_MSG_MAX_COUNT] = {
70 MSG_MAP(TestMessage, PPSMC_MSG_TestMessage, 0),
71 MSG_MAP(GetSmuVersion, PPSMC_MSG_GetSmuVersion, 0),
72 MSG_MAP(GetDriverIfVersion, PPSMC_MSG_GetDriverIfVersion, 0),
73 MSG_MAP(SetDriverDramAddrHigh, PPSMC_MSG_SetDriverTableDramAddrHigh, 0),
74 MSG_MAP(SetDriverDramAddrLow, PPSMC_MSG_SetDriverTableDramAddrLow, 0),
75 MSG_MAP(TransferTableSmu2Dram, PPSMC_MSG_TransferTableSmu2Dram, 0),
76 MSG_MAP(TransferTableDram2Smu, PPSMC_MSG_TransferTableDram2Smu, 0),
77 MSG_MAP(GetEnabledSmuFeatures, PPSMC_MSG_GetEnabledSmuFeatures, 0),
78 MSG_MAP(RequestGfxclk, PPSMC_MSG_RequestGfxclk, 0),
79 MSG_MAP(ForceGfxVid, PPSMC_MSG_ForceGfxVid, 0),
80 MSG_MAP(UnforceGfxVid, PPSMC_MSG_UnforceGfxVid, 0),
81};
82
83static struct cmn2asic_mapping cyan_skillfish_table_map[SMU_TABLE_COUNT] = {
84 TAB_MAP_VALID(SMU_METRICS),
85};
86
87static int cyan_skillfish_tables_init(struct smu_context *smu)
88{
89 struct smu_table_context *smu_table = &smu->smu_table;
90 struct smu_table *tables = smu_table->tables;
91
92 SMU_TABLE_INIT(tables, SMU_TABLE_SMU_METRICS,
93 sizeof(SmuMetrics_t),
94 PAGE_SIZE,
95 AMDGPU_GEM_DOMAIN_VRAM);
96
97 smu_table->metrics_table = kzalloc(sizeof(SmuMetrics_t), GFP_KERNEL);
98 if (!smu_table->metrics_table)
99 goto err0_out;
100
101 smu_table->gpu_metrics_table_size = sizeof(struct gpu_metrics_v2_2);
102 smu_table->gpu_metrics_table = kzalloc(smu_table->gpu_metrics_table_size, GFP_KERNEL);
103 if (!smu_table->gpu_metrics_table)
104 goto err1_out;
105
106 smu_table->metrics_time = 0;
107
108 return 0;
109
110err1_out:
111 smu_table->gpu_metrics_table_size = 0;
112 kfree(smu_table->metrics_table);
113err0_out:
114 return -ENOMEM;
115}
116
117static int cyan_skillfish_init_smc_tables(struct smu_context *smu)
118{
119 int ret = 0;
120
121 ret = cyan_skillfish_tables_init(smu);
122 if (ret)
123 return ret;
124
125 return smu_v11_0_init_smc_tables(smu);
126}
127
128static int cyan_skillfish_finit_smc_tables(struct smu_context *smu)
129{
130 struct smu_table_context *smu_table = &smu->smu_table;
131
132 kfree(smu_table->metrics_table);
133 smu_table->metrics_table = NULL;
134
135 kfree(smu_table->gpu_metrics_table);
136 smu_table->gpu_metrics_table = NULL;
137 smu_table->gpu_metrics_table_size = 0;
138
139 smu_table->metrics_time = 0;
140
141 return 0;
142}
143
144static int
145cyan_skillfish_get_smu_metrics_data(struct smu_context *smu,
146 MetricsMember_t member,
147 uint32_t *value)
148{
149 struct smu_table_context *smu_table = &smu->smu_table;
150 SmuMetrics_t *metrics = (SmuMetrics_t *)smu_table->metrics_table;
151 int ret = 0;
152
153 mutex_lock(&smu->metrics_lock);
154
155 ret = smu_cmn_get_metrics_table_locked(smu, NULL, false);
156 if (ret) {
157 mutex_unlock(&smu->metrics_lock);
158 return ret;
159 }
160
161 switch (member) {
162 case METRICS_CURR_GFXCLK:
163 *value = metrics->Current.GfxclkFrequency;
164 break;
165 case METRICS_CURR_SOCCLK:
166 *value = metrics->Current.SocclkFrequency;
167 break;
168 case METRICS_CURR_VCLK:
169 *value = metrics->Current.VclkFrequency;
170 break;
171 case METRICS_CURR_DCLK:
172 *value = metrics->Current.DclkFrequency;
173 break;
174 case METRICS_CURR_UCLK:
175 *value = metrics->Current.MemclkFrequency;
176 break;
177 case METRICS_AVERAGE_SOCKETPOWER:
178 *value = (metrics->Current.CurrentSocketPower << 8) /
179 1000;
180 break;
181 case METRICS_TEMPERATURE_EDGE:
182 *value = metrics->Current.GfxTemperature / 100 *
183 SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
184 break;
185 case METRICS_TEMPERATURE_HOTSPOT:
186 *value = metrics->Current.SocTemperature / 100 *
187 SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
188 break;
189 case METRICS_VOLTAGE_VDDSOC:
190 *value = metrics->Current.Voltage[0];
191 break;
192 case METRICS_VOLTAGE_VDDGFX:
193 *value = metrics->Current.Voltage[1];
194 break;
195 case METRICS_THROTTLER_STATUS:
196 *value = metrics->Current.ThrottlerStatus;
197 break;
198 default:
199 *value = UINT_MAX;
200 break;
201 }
202
203 mutex_unlock(&smu->metrics_lock);
204
205 return ret;
206}
207
208static int cyan_skillfish_read_sensor(struct smu_context *smu,
209 enum amd_pp_sensors sensor,
210 void *data,
211 uint32_t *size)
212{
213 int ret = 0;
214
215 if (!data || !size)
216 return -EINVAL;
217
218 mutex_lock(&smu->sensor_lock);
219
220 switch (sensor) {
221 case AMDGPU_PP_SENSOR_GFX_SCLK:
222 ret = cyan_skillfish_get_smu_metrics_data(smu,
223 METRICS_CURR_GFXCLK,
224 (uint32_t *)data);
225 *(uint32_t *)data *= 100;
226 *size = 4;
227 break;
228 case AMDGPU_PP_SENSOR_GFX_MCLK:
229 ret = cyan_skillfish_get_smu_metrics_data(smu,
230 METRICS_CURR_UCLK,
231 (uint32_t *)data);
232 *(uint32_t *)data *= 100;
233 *size = 4;
234 break;
235 case AMDGPU_PP_SENSOR_GPU_POWER:
236 ret = cyan_skillfish_get_smu_metrics_data(smu,
237 METRICS_AVERAGE_SOCKETPOWER,
238 (uint32_t *)data);
239 *size = 4;
240 break;
241 case AMDGPU_PP_SENSOR_HOTSPOT_TEMP:
242 ret = cyan_skillfish_get_smu_metrics_data(smu,
243 METRICS_TEMPERATURE_HOTSPOT,
244 (uint32_t *)data);
245 *size = 4;
246 break;
247 case AMDGPU_PP_SENSOR_EDGE_TEMP:
248 ret = cyan_skillfish_get_smu_metrics_data(smu,
249 METRICS_TEMPERATURE_EDGE,
250 (uint32_t *)data);
251 *size = 4;
252 break;
253 case AMDGPU_PP_SENSOR_VDDNB:
254 ret = cyan_skillfish_get_smu_metrics_data(smu,
255 METRICS_VOLTAGE_VDDSOC,
256 (uint32_t *)data);
257 *size = 4;
258 break;
259 case AMDGPU_PP_SENSOR_VDDGFX:
260 ret = cyan_skillfish_get_smu_metrics_data(smu,
261 METRICS_VOLTAGE_VDDGFX,
262 (uint32_t *)data);
263 *size = 4;
264 break;
265 default:
266 ret = -EOPNOTSUPP;
267 break;
268 }
269
270 mutex_unlock(&smu->sensor_lock);
271
272 return ret;
273}
274
275static int cyan_skillfish_get_current_clk_freq(struct smu_context *smu,
276 enum smu_clk_type clk_type,
277 uint32_t *value)
278{
279 MetricsMember_t member_type;
280
281 switch (clk_type) {
282 case SMU_GFXCLK:
283 case SMU_SCLK:
284 member_type = METRICS_CURR_GFXCLK;
285 break;
286 case SMU_FCLK:
287 case SMU_MCLK:
288 member_type = METRICS_CURR_UCLK;
289 break;
290 case SMU_SOCCLK:
291 member_type = METRICS_CURR_SOCCLK;
292 break;
293 case SMU_VCLK:
294 member_type = METRICS_CURR_VCLK;
295 break;
296 case SMU_DCLK:
297 member_type = METRICS_CURR_DCLK;
298 break;
299 default:
300 return -EINVAL;
301 }
302
303 return cyan_skillfish_get_smu_metrics_data(smu, member_type, value);
304}
305
306static int cyan_skillfish_print_clk_levels(struct smu_context *smu,
307 enum smu_clk_type clk_type,
308 char *buf)
309{
310 int ret = 0, size = 0;
311 uint32_t cur_value = 0;
312 int i;
313
314 smu_cmn_get_sysfs_buf(&buf, &size);
315
316 switch (clk_type) {
317 case SMU_OD_SCLK:
318 ret = cyan_skillfish_get_smu_metrics_data(smu, METRICS_CURR_GFXCLK, &cur_value);
319 if (ret)
320 return ret;
321 size += sysfs_emit_at(buf, size,"%s:\n", "OD_SCLK");
322 size += sysfs_emit_at(buf, size, "0: %uMhz *\n", cur_value);
323 break;
324 case SMU_OD_VDDC_CURVE:
325 ret = cyan_skillfish_get_smu_metrics_data(smu, METRICS_VOLTAGE_VDDGFX, &cur_value);
326 if (ret)
327 return ret;
328 size += sysfs_emit_at(buf, size,"%s:\n", "OD_VDDC");
329 size += sysfs_emit_at(buf, size, "0: %umV *\n", cur_value);
330 break;
331 case SMU_OD_RANGE:
332 size += sysfs_emit_at(buf, size, "%s:\n", "OD_RANGE");
333 size += sysfs_emit_at(buf, size, "SCLK: %7uMhz %10uMhz\n",
334 CYAN_SKILLFISH_SCLK_MIN, CYAN_SKILLFISH_SCLK_MAX);
335 size += sysfs_emit_at(buf, size, "VDDC: %7umV %10umV\n",
336 CYAN_SKILLFISH_VDDC_MIN, CYAN_SKILLFISH_VDDC_MAX);
337 break;
338 case SMU_FCLK:
339 case SMU_MCLK:
340 case SMU_SOCCLK:
341 case SMU_VCLK:
342 case SMU_DCLK:
343 ret = cyan_skillfish_get_current_clk_freq(smu, clk_type, &cur_value);
344 if (ret)
345 return ret;
346 size += sysfs_emit_at(buf, size, "0: %uMhz *\n", cur_value);
347 break;
348 case SMU_SCLK:
349 case SMU_GFXCLK:
350 ret = cyan_skillfish_get_current_clk_freq(smu, clk_type, &cur_value);
351 if (ret)
352 return ret;
353 if (cur_value == CYAN_SKILLFISH_SCLK_MAX)
354 i = 2;
355 else if (cur_value == CYAN_SKILLFISH_SCLK_MIN)
356 i = 0;
357 else
358 i = 1;
359 size += sysfs_emit_at(buf, size, "0: %uMhz %s\n", CYAN_SKILLFISH_SCLK_MIN,
360 i == 0 ? "*" : "");
361 size += sysfs_emit_at(buf, size, "1: %uMhz %s\n",
362 i == 1 ? cur_value : cyan_skillfish_sclk_default,
363 i == 1 ? "*" : "");
364 size += sysfs_emit_at(buf, size, "2: %uMhz %s\n", CYAN_SKILLFISH_SCLK_MAX,
365 i == 2 ? "*" : "");
366 break;
367 default:
368 dev_warn(smu->adev->dev, "Unsupported clock type\n");
369 return ret;
370 }
371
372 return size;
373}
374
375static bool cyan_skillfish_is_dpm_running(struct smu_context *smu)
376{
377 struct amdgpu_device *adev = smu->adev;
378 int ret = 0;
379 uint32_t feature_mask[2];
380 uint64_t feature_enabled;
381
382
383 if (adev->in_suspend)
384 return false;
385
386 ret = smu_cmn_get_enabled_32_bits_mask(smu, feature_mask, 2);
387 if (ret)
388 return false;
389
390 feature_enabled = (uint64_t)feature_mask[0] |
391 ((uint64_t)feature_mask[1] << 32);
392
393
394
395
396 if (!cyan_skillfish_sclk_default)
397 cyan_skillfish_get_smu_metrics_data(smu, METRICS_CURR_GFXCLK,
398 &cyan_skillfish_sclk_default);
399
400 return !!(feature_enabled & SMC_DPM_FEATURE);
401}
402
403static ssize_t cyan_skillfish_get_gpu_metrics(struct smu_context *smu,
404 void **table)
405{
406 struct smu_table_context *smu_table = &smu->smu_table;
407 struct gpu_metrics_v2_2 *gpu_metrics =
408 (struct gpu_metrics_v2_2 *)smu_table->gpu_metrics_table;
409 SmuMetrics_t metrics;
410 int i, ret = 0;
411
412 ret = smu_cmn_get_metrics_table(smu, &metrics, true);
413 if (ret)
414 return ret;
415
416 smu_cmn_init_soft_gpu_metrics(gpu_metrics, 2, 2);
417
418 gpu_metrics->temperature_gfx = metrics.Current.GfxTemperature;
419 gpu_metrics->temperature_soc = metrics.Current.SocTemperature;
420
421 gpu_metrics->average_socket_power = metrics.Current.CurrentSocketPower;
422 gpu_metrics->average_soc_power = metrics.Current.Power[0];
423 gpu_metrics->average_gfx_power = metrics.Current.Power[1];
424
425 gpu_metrics->average_gfxclk_frequency = metrics.Average.GfxclkFrequency;
426 gpu_metrics->average_socclk_frequency = metrics.Average.SocclkFrequency;
427 gpu_metrics->average_uclk_frequency = metrics.Average.MemclkFrequency;
428 gpu_metrics->average_fclk_frequency = metrics.Average.MemclkFrequency;
429 gpu_metrics->average_vclk_frequency = metrics.Average.VclkFrequency;
430 gpu_metrics->average_dclk_frequency = metrics.Average.DclkFrequency;
431
432 gpu_metrics->current_gfxclk = metrics.Current.GfxclkFrequency;
433 gpu_metrics->current_socclk = metrics.Current.SocclkFrequency;
434 gpu_metrics->current_uclk = metrics.Current.MemclkFrequency;
435 gpu_metrics->current_fclk = metrics.Current.MemclkFrequency;
436 gpu_metrics->current_vclk = metrics.Current.VclkFrequency;
437 gpu_metrics->current_dclk = metrics.Current.DclkFrequency;
438
439 for (i = 0; i < 6; i++) {
440 gpu_metrics->temperature_core[i] = metrics.Current.CoreTemperature[i];
441 gpu_metrics->average_core_power[i] = metrics.Average.CorePower[i];
442 gpu_metrics->current_coreclk[i] = metrics.Current.CoreFrequency[i];
443 }
444
445 for (i = 0; i < 2; i++) {
446 gpu_metrics->temperature_l3[i] = metrics.Current.L3Temperature[i];
447 gpu_metrics->current_l3clk[i] = metrics.Current.L3Frequency[i];
448 }
449
450 gpu_metrics->throttle_status = metrics.Current.ThrottlerStatus;
451 gpu_metrics->system_clock_counter = ktime_get_boottime_ns();
452
453 *table = (void *)gpu_metrics;
454
455 return sizeof(struct gpu_metrics_v2_2);
456}
457
458static int cyan_skillfish_od_edit_dpm_table(struct smu_context *smu,
459 enum PP_OD_DPM_TABLE_COMMAND type,
460 long input[], uint32_t size)
461{
462 int ret = 0;
463 uint32_t vid;
464
465 switch (type) {
466 case PP_OD_EDIT_VDDC_CURVE:
467 if (size != 3 || input[0] != 0) {
468 dev_err(smu->adev->dev, "Invalid parameter!\n");
469 return -EINVAL;
470 }
471
472 if (input[1] < CYAN_SKILLFISH_SCLK_MIN ||
473 input[1] > CYAN_SKILLFISH_SCLK_MAX) {
474 dev_err(smu->adev->dev, "Invalid sclk! Valid sclk range: %uMHz - %uMhz\n",
475 CYAN_SKILLFISH_SCLK_MIN, CYAN_SKILLFISH_SCLK_MAX);
476 return -EINVAL;
477 }
478
479 if (input[2] < CYAN_SKILLFISH_VDDC_MIN ||
480 input[2] > CYAN_SKILLFISH_VDDC_MAX) {
481 dev_err(smu->adev->dev, "Invalid vddc! Valid vddc range: %umV - %umV\n",
482 CYAN_SKILLFISH_VDDC_MIN, CYAN_SKILLFISH_VDDC_MAX);
483 return -EINVAL;
484 }
485
486 cyan_skillfish_user_settings.sclk = input[1];
487 cyan_skillfish_user_settings.vddc = input[2];
488
489 break;
490 case PP_OD_RESTORE_DEFAULT_TABLE:
491 if (size != 0) {
492 dev_err(smu->adev->dev, "Invalid parameter!\n");
493 return -EINVAL;
494 }
495
496 cyan_skillfish_user_settings.sclk = cyan_skillfish_sclk_default;
497 cyan_skillfish_user_settings.vddc = CYAN_SKILLFISH_VDDC_MAGIC;
498
499 break;
500 case PP_OD_COMMIT_DPM_TABLE:
501 if (size != 0) {
502 dev_err(smu->adev->dev, "Invalid parameter!\n");
503 return -EINVAL;
504 }
505
506 if (cyan_skillfish_user_settings.sclk < CYAN_SKILLFISH_SCLK_MIN ||
507 cyan_skillfish_user_settings.sclk > CYAN_SKILLFISH_SCLK_MAX) {
508 dev_err(smu->adev->dev, "Invalid sclk! Valid sclk range: %uMHz - %uMhz\n",
509 CYAN_SKILLFISH_SCLK_MIN, CYAN_SKILLFISH_SCLK_MAX);
510 return -EINVAL;
511 }
512
513 if ((cyan_skillfish_user_settings.vddc != CYAN_SKILLFISH_VDDC_MAGIC) &&
514 (cyan_skillfish_user_settings.vddc < CYAN_SKILLFISH_VDDC_MIN ||
515 cyan_skillfish_user_settings.vddc > CYAN_SKILLFISH_VDDC_MAX)) {
516 dev_err(smu->adev->dev, "Invalid vddc! Valid vddc range: %umV - %umV\n",
517 CYAN_SKILLFISH_VDDC_MIN, CYAN_SKILLFISH_VDDC_MAX);
518 return -EINVAL;
519 }
520
521 ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_RequestGfxclk,
522 cyan_skillfish_user_settings.sclk, NULL);
523 if (ret) {
524 dev_err(smu->adev->dev, "Set sclk failed!\n");
525 return ret;
526 }
527
528 if (cyan_skillfish_user_settings.vddc == CYAN_SKILLFISH_VDDC_MAGIC) {
529 ret = smu_cmn_send_smc_msg(smu, SMU_MSG_UnforceGfxVid, NULL);
530 if (ret) {
531 dev_err(smu->adev->dev, "Unforce vddc failed!\n");
532 return ret;
533 }
534 } else {
535
536
537
538
539 vid = (1550 - cyan_skillfish_user_settings.vddc) * 160 / 1000;
540 ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_ForceGfxVid, vid, NULL);
541 if (ret) {
542 dev_err(smu->adev->dev, "Force vddc failed!\n");
543 return ret;
544 }
545 }
546
547 break;
548 default:
549 return -EOPNOTSUPP;
550 }
551
552 return ret;
553}
554
555static const struct pptable_funcs cyan_skillfish_ppt_funcs = {
556
557 .check_fw_status = smu_v11_0_check_fw_status,
558 .check_fw_version = smu_v11_0_check_fw_version,
559 .init_power = smu_v11_0_init_power,
560 .fini_power = smu_v11_0_fini_power,
561 .init_smc_tables = cyan_skillfish_init_smc_tables,
562 .fini_smc_tables = cyan_skillfish_finit_smc_tables,
563 .read_sensor = cyan_skillfish_read_sensor,
564 .print_clk_levels = cyan_skillfish_print_clk_levels,
565 .is_dpm_running = cyan_skillfish_is_dpm_running,
566 .get_gpu_metrics = cyan_skillfish_get_gpu_metrics,
567 .od_edit_dpm_table = cyan_skillfish_od_edit_dpm_table,
568 .register_irq_handler = smu_v11_0_register_irq_handler,
569 .notify_memory_pool_location = smu_v11_0_notify_memory_pool_location,
570 .send_smc_msg_with_param = smu_cmn_send_smc_msg_with_param,
571 .send_smc_msg = smu_cmn_send_smc_msg,
572 .set_driver_table_location = smu_v11_0_set_driver_table_location,
573 .interrupt_work = smu_v11_0_interrupt_work,
574};
575
576void cyan_skillfish_set_ppt_funcs(struct smu_context *smu)
577{
578 smu->ppt_funcs = &cyan_skillfish_ppt_funcs;
579 smu->message_map = cyan_skillfish_message_map;
580 smu->table_map = cyan_skillfish_table_map;
581 smu->is_apu = true;
582}
583