Loading...
Note: File does not exist in v6.9.4.
1/*
2 * Copyright 2019 Advanced Micro Devices, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 */
22
23#include "pp_debug.h"
24#include <linux/firmware.h>
25#include "amdgpu.h"
26#include "amdgpu_smu.h"
27#include "atomfirmware.h"
28#include "amdgpu_atomfirmware.h"
29#include "smu_v12_0.h"
30#include "soc15_common.h"
31#include "atom.h"
32#include "renoir_ppt.h"
33
34#include "asic_reg/mp/mp_12_0_0_offset.h"
35#include "asic_reg/mp/mp_12_0_0_sh_mask.h"
36
37#define smnMP1_FIRMWARE_FLAGS 0x3010024
38
39#define mmSMUIO_GFX_MISC_CNTL 0x00c8
40#define mmSMUIO_GFX_MISC_CNTL_BASE_IDX 0
41#define SMUIO_GFX_MISC_CNTL__PWR_GFXOFF_STATUS_MASK 0x00000006L
42#define SMUIO_GFX_MISC_CNTL__PWR_GFXOFF_STATUS__SHIFT 0x1
43
44static int smu_v12_0_send_msg_without_waiting(struct smu_context *smu,
45 uint16_t msg)
46{
47 struct amdgpu_device *adev = smu->adev;
48
49 WREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_66, msg);
50 return 0;
51}
52
53static int smu_v12_0_read_arg(struct smu_context *smu, uint32_t *arg)
54{
55 struct amdgpu_device *adev = smu->adev;
56
57 *arg = RREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_82);
58 return 0;
59}
60
61static int smu_v12_0_wait_for_response(struct smu_context *smu)
62{
63 struct amdgpu_device *adev = smu->adev;
64 uint32_t cur_value, i;
65
66 for (i = 0; i < adev->usec_timeout; i++) {
67 cur_value = RREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_90);
68 if ((cur_value & MP1_C2PMSG_90__CONTENT_MASK) != 0)
69 break;
70 udelay(1);
71 }
72
73 /* timeout means wrong logic */
74 if (i == adev->usec_timeout)
75 return -ETIME;
76
77 return RREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_90) == 0x1 ? 0 : -EIO;
78}
79
80static int smu_v12_0_send_msg(struct smu_context *smu, uint16_t msg)
81{
82 struct amdgpu_device *adev = smu->adev;
83 int ret = 0, index = 0;
84
85 index = smu_msg_get_index(smu, msg);
86 if (index < 0)
87 return index;
88
89 smu_v12_0_wait_for_response(smu);
90
91 WREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_90, 0);
92
93 smu_v12_0_send_msg_without_waiting(smu, (uint16_t)index);
94
95 ret = smu_v12_0_wait_for_response(smu);
96
97 if (ret)
98 pr_err("Failed to send message 0x%x, response 0x%x\n", index,
99 ret);
100
101 return ret;
102
103}
104
105static int
106smu_v12_0_send_msg_with_param(struct smu_context *smu, uint16_t msg,
107 uint32_t param)
108{
109 struct amdgpu_device *adev = smu->adev;
110 int ret = 0, index = 0;
111
112 index = smu_msg_get_index(smu, msg);
113 if (index < 0)
114 return index;
115
116 ret = smu_v12_0_wait_for_response(smu);
117 if (ret)
118 pr_err("Failed to send message 0x%x, response 0x%x, param 0x%x\n",
119 index, ret, param);
120
121 WREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_90, 0);
122
123 WREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_82, param);
124
125 smu_v12_0_send_msg_without_waiting(smu, (uint16_t)index);
126
127 ret = smu_v12_0_wait_for_response(smu);
128 if (ret)
129 pr_err("Failed to send message 0x%x, response 0x%x param 0x%x\n",
130 index, ret, param);
131
132 return ret;
133}
134
135static int smu_v12_0_check_fw_status(struct smu_context *smu)
136{
137 struct amdgpu_device *adev = smu->adev;
138 uint32_t mp1_fw_flags;
139
140 mp1_fw_flags = RREG32_PCIE(MP1_Public |
141 (smnMP1_FIRMWARE_FLAGS & 0xffffffff));
142
143 if ((mp1_fw_flags & MP1_FIRMWARE_FLAGS__INTERRUPTS_ENABLED_MASK) >>
144 MP1_FIRMWARE_FLAGS__INTERRUPTS_ENABLED__SHIFT)
145 return 0;
146
147 return -EIO;
148}
149
150static int smu_v12_0_check_fw_version(struct smu_context *smu)
151{
152 uint32_t if_version = 0xff, smu_version = 0xff;
153 uint16_t smu_major;
154 uint8_t smu_minor, smu_debug;
155 int ret = 0;
156
157 ret = smu_get_smc_version(smu, &if_version, &smu_version);
158 if (ret)
159 return ret;
160
161 smu_major = (smu_version >> 16) & 0xffff;
162 smu_minor = (smu_version >> 8) & 0xff;
163 smu_debug = (smu_version >> 0) & 0xff;
164
165 /*
166 * 1. if_version mismatch is not critical as our fw is designed
167 * to be backward compatible.
168 * 2. New fw usually brings some optimizations. But that's visible
169 * only on the paired driver.
170 * Considering above, we just leave user a warning message instead
171 * of halt driver loading.
172 */
173 if (if_version != smu->smc_if_version) {
174 pr_info("smu driver if version = 0x%08x, smu fw if version = 0x%08x, "
175 "smu fw version = 0x%08x (%d.%d.%d)\n",
176 smu->smc_if_version, if_version,
177 smu_version, smu_major, smu_minor, smu_debug);
178 pr_warn("SMU driver if version not matched\n");
179 }
180
181 return ret;
182}
183
184static int smu_v12_0_powergate_sdma(struct smu_context *smu, bool gate)
185{
186 if (!(smu->adev->flags & AMD_IS_APU))
187 return 0;
188
189 if (gate)
190 return smu_send_smc_msg(smu, SMU_MSG_PowerDownSdma);
191 else
192 return smu_send_smc_msg(smu, SMU_MSG_PowerUpSdma);
193}
194
195static int smu_v12_0_powergate_vcn(struct smu_context *smu, bool gate)
196{
197 if (!(smu->adev->flags & AMD_IS_APU))
198 return 0;
199
200 if (gate)
201 return smu_send_smc_msg(smu, SMU_MSG_PowerDownVcn);
202 else
203 return smu_send_smc_msg(smu, SMU_MSG_PowerUpVcn);
204}
205
206static int smu_v12_0_set_gfx_cgpg(struct smu_context *smu, bool enable)
207{
208 if (!(smu->adev->pg_flags & AMD_PG_SUPPORT_GFX_PG))
209 return 0;
210
211 return smu_v12_0_send_msg_with_param(smu,
212 SMU_MSG_SetGfxCGPG, enable ? 1 : 0);
213}
214
215/**
216 * smu_v12_0_get_gfxoff_status - get gfxoff status
217 *
218 * @smu: amdgpu_device pointer
219 *
220 * This function will be used to get gfxoff status
221 *
222 * Returns 0=GFXOFF(default).
223 * Returns 1=Transition out of GFX State.
224 * Returns 2=Not in GFXOFF.
225 * Returns 3=Transition into GFXOFF.
226 */
227static uint32_t smu_v12_0_get_gfxoff_status(struct smu_context *smu)
228{
229 uint32_t reg;
230 uint32_t gfxOff_Status = 0;
231 struct amdgpu_device *adev = smu->adev;
232
233 reg = RREG32_SOC15(SMUIO, 0, mmSMUIO_GFX_MISC_CNTL);
234 gfxOff_Status = (reg & SMUIO_GFX_MISC_CNTL__PWR_GFXOFF_STATUS_MASK)
235 >> SMUIO_GFX_MISC_CNTL__PWR_GFXOFF_STATUS__SHIFT;
236
237 return gfxOff_Status;
238}
239
240static int smu_v12_0_gfx_off_control(struct smu_context *smu, bool enable)
241{
242 int ret = 0, timeout = 500;
243
244 if (enable) {
245 ret = smu_send_smc_msg(smu, SMU_MSG_AllowGfxOff);
246
247 /* confirm gfx is back to "off" state, timeout is 5 seconds */
248 while (!(smu_v12_0_get_gfxoff_status(smu) == 0)) {
249 msleep(10);
250 timeout--;
251 if (timeout == 0) {
252 DRM_ERROR("enable gfxoff timeout and failed!\n");
253 break;
254 }
255 }
256 } else {
257 ret = smu_send_smc_msg(smu, SMU_MSG_DisallowGfxOff);
258
259 /* confirm gfx is back to "on" state, timeout is 0.5 second */
260 while (!(smu_v12_0_get_gfxoff_status(smu) == 2)) {
261 msleep(1);
262 timeout--;
263 if (timeout == 0) {
264 DRM_ERROR("disable gfxoff timeout and failed!\n");
265 break;
266 }
267 }
268 }
269
270 return ret;
271}
272
273static int smu_v12_0_init_smc_tables(struct smu_context *smu)
274{
275 struct smu_table_context *smu_table = &smu->smu_table;
276 struct smu_table *tables = NULL;
277
278 if (smu_table->tables || smu_table->table_count == 0)
279 return -EINVAL;
280
281 tables = kcalloc(SMU_TABLE_COUNT, sizeof(struct smu_table),
282 GFP_KERNEL);
283 if (!tables)
284 return -ENOMEM;
285
286 smu_table->tables = tables;
287
288 return smu_tables_init(smu, tables);
289}
290
291static int smu_v12_0_fini_smc_tables(struct smu_context *smu)
292{
293 struct smu_table_context *smu_table = &smu->smu_table;
294
295 if (!smu_table->tables || smu_table->table_count == 0)
296 return -EINVAL;
297
298 kfree(smu_table->clocks_table);
299 kfree(smu_table->tables);
300
301 smu_table->clocks_table = NULL;
302 smu_table->tables = NULL;
303
304 return 0;
305}
306
307static int smu_v12_0_populate_smc_tables(struct smu_context *smu)
308{
309 struct smu_table_context *smu_table = &smu->smu_table;
310 struct smu_table *table = NULL;
311
312 table = &smu_table->tables[SMU_TABLE_DPMCLOCKS];
313 if (!table)
314 return -EINVAL;
315
316 if (!table->cpu_addr)
317 return -EINVAL;
318
319 return smu_update_table(smu, SMU_TABLE_DPMCLOCKS, 0, smu_table->clocks_table, false);
320}
321
322static int smu_v12_0_get_dpm_ultimate_freq(struct smu_context *smu, enum smu_clk_type clk_type,
323 uint32_t *min, uint32_t *max)
324{
325 int ret = 0;
326
327 mutex_lock(&smu->mutex);
328
329 if (max) {
330 switch (clk_type) {
331 case SMU_GFXCLK:
332 case SMU_SCLK:
333 ret = smu_send_smc_msg(smu, SMU_MSG_GetMaxGfxclkFrequency);
334 if (ret) {
335 pr_err("Attempt to get max GX frequency from SMC Failed !\n");
336 goto failed;
337 }
338 ret = smu_read_smc_arg(smu, max);
339 if (ret)
340 goto failed;
341 break;
342 case SMU_UCLK:
343 ret = smu_get_dpm_uclk_limited(smu, max, true);
344 if (ret)
345 goto failed;
346 break;
347 default:
348 ret = -EINVAL;
349 goto failed;
350
351 }
352 }
353
354 if (min) {
355 switch (clk_type) {
356 case SMU_GFXCLK:
357 case SMU_SCLK:
358 ret = smu_send_smc_msg(smu, SMU_MSG_GetMinGfxclkFrequency);
359 if (ret) {
360 pr_err("Attempt to get min GX frequency from SMC Failed !\n");
361 goto failed;
362 }
363 ret = smu_read_smc_arg(smu, min);
364 if (ret)
365 goto failed;
366 break;
367 case SMU_UCLK:
368 ret = smu_get_dpm_uclk_limited(smu, min, false);
369 if (ret)
370 goto failed;
371 break;
372 default:
373 ret = -EINVAL;
374 goto failed;
375 }
376
377 }
378failed:
379 mutex_unlock(&smu->mutex);
380 return ret;
381}
382
383static const struct smu_funcs smu_v12_0_funcs = {
384 .check_fw_status = smu_v12_0_check_fw_status,
385 .check_fw_version = smu_v12_0_check_fw_version,
386 .powergate_sdma = smu_v12_0_powergate_sdma,
387 .powergate_vcn = smu_v12_0_powergate_vcn,
388 .send_smc_msg = smu_v12_0_send_msg,
389 .send_smc_msg_with_param = smu_v12_0_send_msg_with_param,
390 .read_smc_arg = smu_v12_0_read_arg,
391 .set_gfx_cgpg = smu_v12_0_set_gfx_cgpg,
392 .gfx_off_control = smu_v12_0_gfx_off_control,
393 .init_smc_tables = smu_v12_0_init_smc_tables,
394 .fini_smc_tables = smu_v12_0_fini_smc_tables,
395 .populate_smc_tables = smu_v12_0_populate_smc_tables,
396 .get_dpm_ultimate_freq = smu_v12_0_get_dpm_ultimate_freq,
397};
398
399void smu_v12_0_set_smu_funcs(struct smu_context *smu)
400{
401 struct amdgpu_device *adev = smu->adev;
402
403 smu->funcs = &smu_v12_0_funcs;
404
405 switch (adev->asic_type) {
406 case CHIP_RENOIR:
407 renoir_set_ppt_funcs(smu);
408 break;
409 default:
410 pr_warn("Unknown asic for smu12\n");
411 }
412}