blob: 168aedbffa7e72e746d88afc2d4381119f1fdb81 [file] [log] [blame]
Alex Deucher66229b22013-06-26 00:11:19 -04001/*
2 * Copyright 2011 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 * Authors: Alex Deucher
23 */
24
25#include <linux/firmware.h>
26#include "drmP.h"
27#include "radeon.h"
28#include "rv770d.h"
29#include "rv770_dpm.h"
30#include "rv770_smc.h"
31#include "atom.h"
32#include "radeon_ucode.h"
33
34#define FIRST_SMC_INT_VECT_REG 0xFFD8
35#define FIRST_INT_VECT_S19 0xFFC0
36
37static const u8 rv770_smc_int_vectors[] =
38{
39 0x08, 0x10, 0x08, 0x10,
40 0x08, 0x10, 0x08, 0x10,
41 0x08, 0x10, 0x08, 0x10,
42 0x08, 0x10, 0x08, 0x10,
43 0x08, 0x10, 0x08, 0x10,
44 0x08, 0x10, 0x08, 0x10,
45 0x08, 0x10, 0x08, 0x10,
46 0x08, 0x10, 0x08, 0x10,
47 0x08, 0x10, 0x08, 0x10,
48 0x08, 0x10, 0x08, 0x10,
49 0x08, 0x10, 0x08, 0x10,
50 0x08, 0x10, 0x08, 0x10,
51 0x08, 0x10, 0x0C, 0xD7,
52 0x08, 0x2B, 0x08, 0x10,
53 0x03, 0x51, 0x03, 0x51,
54 0x03, 0x51, 0x03, 0x51
55};
56
57static const u8 rv730_smc_int_vectors[] =
58{
59 0x08, 0x15, 0x08, 0x15,
60 0x08, 0x15, 0x08, 0x15,
61 0x08, 0x15, 0x08, 0x15,
62 0x08, 0x15, 0x08, 0x15,
63 0x08, 0x15, 0x08, 0x15,
64 0x08, 0x15, 0x08, 0x15,
65 0x08, 0x15, 0x08, 0x15,
66 0x08, 0x15, 0x08, 0x15,
67 0x08, 0x15, 0x08, 0x15,
68 0x08, 0x15, 0x08, 0x15,
69 0x08, 0x15, 0x08, 0x15,
70 0x08, 0x15, 0x08, 0x15,
71 0x08, 0x15, 0x0C, 0xBB,
72 0x08, 0x30, 0x08, 0x15,
73 0x03, 0x56, 0x03, 0x56,
74 0x03, 0x56, 0x03, 0x56
75};
76
77static const u8 rv710_smc_int_vectors[] =
78{
79 0x08, 0x04, 0x08, 0x04,
80 0x08, 0x04, 0x08, 0x04,
81 0x08, 0x04, 0x08, 0x04,
82 0x08, 0x04, 0x08, 0x04,
83 0x08, 0x04, 0x08, 0x04,
84 0x08, 0x04, 0x08, 0x04,
85 0x08, 0x04, 0x08, 0x04,
86 0x08, 0x04, 0x08, 0x04,
87 0x08, 0x04, 0x08, 0x04,
88 0x08, 0x04, 0x08, 0x04,
89 0x08, 0x04, 0x08, 0x04,
90 0x08, 0x04, 0x08, 0x04,
91 0x08, 0x04, 0x0C, 0xCB,
92 0x08, 0x1F, 0x08, 0x04,
93 0x03, 0x51, 0x03, 0x51,
94 0x03, 0x51, 0x03, 0x51
95};
96
97static const u8 rv740_smc_int_vectors[] =
98{
99 0x08, 0x10, 0x08, 0x10,
100 0x08, 0x10, 0x08, 0x10,
101 0x08, 0x10, 0x08, 0x10,
102 0x08, 0x10, 0x08, 0x10,
103 0x08, 0x10, 0x08, 0x10,
104 0x08, 0x10, 0x08, 0x10,
105 0x08, 0x10, 0x08, 0x10,
106 0x08, 0x10, 0x08, 0x10,
107 0x08, 0x10, 0x08, 0x10,
108 0x08, 0x10, 0x08, 0x10,
109 0x08, 0x10, 0x08, 0x10,
110 0x08, 0x10, 0x08, 0x10,
111 0x08, 0x10, 0x0C, 0xD7,
112 0x08, 0x2B, 0x08, 0x10,
113 0x03, 0x51, 0x03, 0x51,
114 0x03, 0x51, 0x03, 0x51
115};
116
Alex Deucherdc50ba72013-06-26 00:33:35 -0400117static const u8 cedar_smc_int_vectors[] =
118{
119 0x0B, 0x05, 0x0B, 0x05,
120 0x0B, 0x05, 0x0B, 0x05,
121 0x0B, 0x05, 0x0B, 0x05,
122 0x0B, 0x05, 0x0B, 0x05,
123 0x0B, 0x05, 0x0B, 0x05,
124 0x0B, 0x05, 0x0B, 0x05,
125 0x0B, 0x05, 0x0B, 0x05,
126 0x0B, 0x05, 0x0B, 0x05,
127 0x0B, 0x05, 0x0B, 0x05,
128 0x0B, 0x05, 0x0B, 0x05,
129 0x0B, 0x05, 0x0B, 0x05,
130 0x0B, 0x05, 0x0B, 0x05,
131 0x0B, 0x05, 0x11, 0x8B,
132 0x0B, 0x20, 0x0B, 0x05,
133 0x04, 0xF6, 0x04, 0xF6,
134 0x04, 0xF6, 0x04, 0xF6
135};
136
137static const u8 redwood_smc_int_vectors[] =
138{
139 0x0B, 0x05, 0x0B, 0x05,
140 0x0B, 0x05, 0x0B, 0x05,
141 0x0B, 0x05, 0x0B, 0x05,
142 0x0B, 0x05, 0x0B, 0x05,
143 0x0B, 0x05, 0x0B, 0x05,
144 0x0B, 0x05, 0x0B, 0x05,
145 0x0B, 0x05, 0x0B, 0x05,
146 0x0B, 0x05, 0x0B, 0x05,
147 0x0B, 0x05, 0x0B, 0x05,
148 0x0B, 0x05, 0x0B, 0x05,
149 0x0B, 0x05, 0x0B, 0x05,
150 0x0B, 0x05, 0x0B, 0x05,
151 0x0B, 0x05, 0x11, 0x8B,
152 0x0B, 0x20, 0x0B, 0x05,
153 0x04, 0xF6, 0x04, 0xF6,
154 0x04, 0xF6, 0x04, 0xF6
155};
156
157static const u8 juniper_smc_int_vectors[] =
158{
159 0x0B, 0x05, 0x0B, 0x05,
160 0x0B, 0x05, 0x0B, 0x05,
161 0x0B, 0x05, 0x0B, 0x05,
162 0x0B, 0x05, 0x0B, 0x05,
163 0x0B, 0x05, 0x0B, 0x05,
164 0x0B, 0x05, 0x0B, 0x05,
165 0x0B, 0x05, 0x0B, 0x05,
166 0x0B, 0x05, 0x0B, 0x05,
167 0x0B, 0x05, 0x0B, 0x05,
168 0x0B, 0x05, 0x0B, 0x05,
169 0x0B, 0x05, 0x0B, 0x05,
170 0x0B, 0x05, 0x0B, 0x05,
171 0x0B, 0x05, 0x11, 0x8B,
172 0x0B, 0x20, 0x0B, 0x05,
173 0x04, 0xF6, 0x04, 0xF6,
174 0x04, 0xF6, 0x04, 0xF6
175};
176
177static const u8 cypress_smc_int_vectors[] =
178{
179 0x0B, 0x05, 0x0B, 0x05,
180 0x0B, 0x05, 0x0B, 0x05,
181 0x0B, 0x05, 0x0B, 0x05,
182 0x0B, 0x05, 0x0B, 0x05,
183 0x0B, 0x05, 0x0B, 0x05,
184 0x0B, 0x05, 0x0B, 0x05,
185 0x0B, 0x05, 0x0B, 0x05,
186 0x0B, 0x05, 0x0B, 0x05,
187 0x0B, 0x05, 0x0B, 0x05,
188 0x0B, 0x05, 0x0B, 0x05,
189 0x0B, 0x05, 0x0B, 0x05,
190 0x0B, 0x05, 0x0B, 0x05,
191 0x0B, 0x05, 0x11, 0x8B,
192 0x0B, 0x20, 0x0B, 0x05,
193 0x04, 0xF6, 0x04, 0xF6,
194 0x04, 0xF6, 0x04, 0xF6
195};
196
Alex Deucher66229b22013-06-26 00:11:19 -0400197int rv770_set_smc_sram_address(struct radeon_device *rdev,
198 u16 smc_address, u16 limit)
199{
200 u32 addr;
201
202 if (smc_address & 3)
203 return -EINVAL;
204 if ((smc_address + 3) > limit)
205 return -EINVAL;
206
207 addr = smc_address;
208 addr |= SMC_SRAM_AUTO_INC_DIS;
209
210 WREG32(SMC_SRAM_ADDR, addr);
211
212 return 0;
213}
214
215int rv770_copy_bytes_to_smc(struct radeon_device *rdev,
216 u16 smc_start_address, const u8 *src,
217 u16 byte_count, u16 limit)
218{
219 u32 data, original_data, extra_shift;
220 u16 addr;
221 int ret;
222
223 if (smc_start_address & 3)
224 return -EINVAL;
225 if ((smc_start_address + byte_count) > limit)
226 return -EINVAL;
227
228 addr = smc_start_address;
229
230 while (byte_count >= 4) {
231 /* SMC address space is BE */
232 data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
233
234 ret = rv770_set_smc_sram_address(rdev, addr, limit);
235 if (ret)
236 return ret;
237
238 WREG32(SMC_SRAM_DATA, data);
239
240 src += 4;
241 byte_count -= 4;
242 addr += 4;
243 }
244
245 /* RMW for final bytes */
246 if (byte_count > 0) {
247 data = 0;
248
249 ret = rv770_set_smc_sram_address(rdev, addr, limit);
250 if (ret)
251 return ret;
252
253 original_data = RREG32(SMC_SRAM_DATA);
254
255 extra_shift = 8 * (4 - byte_count);
256
257 while (byte_count > 0) {
258 /* SMC address space is BE */
259 data = (data << 8) + *src++;
260 byte_count--;
261 }
262
263 data <<= extra_shift;
264
265 data |= (original_data & ~((~0UL) << extra_shift));
266
267 ret = rv770_set_smc_sram_address(rdev, addr, limit);
268 if (ret)
269 return ret;
270
271 WREG32(SMC_SRAM_DATA, data);
272 }
273
274 return 0;
275}
276
277static int rv770_program_interrupt_vectors(struct radeon_device *rdev,
278 u32 smc_first_vector, const u8 *src,
279 u32 byte_count)
280{
281 u32 tmp, i;
282
283 if (byte_count % 4)
284 return -EINVAL;
285
286 if (smc_first_vector < FIRST_SMC_INT_VECT_REG) {
287 tmp = FIRST_SMC_INT_VECT_REG - smc_first_vector;
288
289 if (tmp > byte_count)
290 return 0;
291
292 byte_count -= tmp;
293 src += tmp;
294 smc_first_vector = FIRST_SMC_INT_VECT_REG;
295 }
296
297 for (i = 0; i < byte_count; i += 4) {
298 /* SMC address space is BE */
299 tmp = (src[i] << 24) | (src[i + 1] << 16) | (src[i + 2] << 8) | src[i + 3];
300
301 WREG32(SMC_ISR_FFD8_FFDB + i, tmp);
302 }
303
304 return 0;
305}
306
307void rv770_start_smc(struct radeon_device *rdev)
308{
309 WREG32_P(SMC_IO, SMC_RST_N, ~SMC_RST_N);
310}
311
312void rv770_reset_smc(struct radeon_device *rdev)
313{
314 WREG32_P(SMC_IO, 0, ~SMC_RST_N);
315}
316
317void rv770_stop_smc_clock(struct radeon_device *rdev)
318{
319 WREG32_P(SMC_IO, 0, ~SMC_CLK_EN);
320}
321
322void rv770_start_smc_clock(struct radeon_device *rdev)
323{
324 WREG32_P(SMC_IO, SMC_CLK_EN, ~SMC_CLK_EN);
325}
326
327bool rv770_is_smc_running(struct radeon_device *rdev)
328{
329 u32 tmp;
330
331 tmp = RREG32(SMC_IO);
332
333 if ((tmp & SMC_RST_N) && (tmp & SMC_CLK_EN))
334 return true;
335 else
336 return false;
337}
338
339PPSMC_Result rv770_send_msg_to_smc(struct radeon_device *rdev, PPSMC_Msg msg)
340{
341 u32 tmp;
342 int i;
343 PPSMC_Result result;
344
345 if (!rv770_is_smc_running(rdev))
346 return PPSMC_Result_Failed;
347
348 WREG32_P(SMC_MSG, HOST_SMC_MSG(msg), ~HOST_SMC_MSG_MASK);
349
350 for (i = 0; i < rdev->usec_timeout; i++) {
351 tmp = RREG32(SMC_MSG) & HOST_SMC_RESP_MASK;
352 tmp >>= HOST_SMC_RESP_SHIFT;
353 if (tmp != 0)
354 break;
355 udelay(1);
356 }
357
358 tmp = RREG32(SMC_MSG) & HOST_SMC_RESP_MASK;
359 tmp >>= HOST_SMC_RESP_SHIFT;
360
361 result = (PPSMC_Result)tmp;
362 return result;
363}
364
365PPSMC_Result rv770_wait_for_smc_inactive(struct radeon_device *rdev)
366{
367 int i;
368 PPSMC_Result result = PPSMC_Result_OK;
369
370 if (!rv770_is_smc_running(rdev))
371 return result;
372
373 for (i = 0; i < rdev->usec_timeout; i++) {
374 if (RREG32(SMC_IO) & SMC_STOP_MODE)
375 break;
376 udelay(1);
377 }
378
379 return result;
380}
381
382static void rv770_clear_smc_sram(struct radeon_device *rdev, u16 limit)
383{
384 u16 i;
385
386 for (i = 0; i < limit; i += 4) {
387 rv770_set_smc_sram_address(rdev, i, limit);
388 WREG32(SMC_SRAM_DATA, 0);
389 }
390}
391
392int rv770_load_smc_ucode(struct radeon_device *rdev,
393 u16 limit)
394{
395 int ret;
396 const u8 *int_vect;
397 u16 int_vect_start_address;
398 u16 int_vect_size;
399 const u8 *ucode_data;
400 u16 ucode_start_address;
401 u16 ucode_size;
402
403 if (!rdev->smc_fw)
404 return -EINVAL;
405
406 rv770_clear_smc_sram(rdev, limit);
407
408 switch (rdev->family) {
409 case CHIP_RV770:
410 ucode_start_address = RV770_SMC_UCODE_START;
411 ucode_size = RV770_SMC_UCODE_SIZE;
412 int_vect = (const u8 *)&rv770_smc_int_vectors;
413 int_vect_start_address = RV770_SMC_INT_VECTOR_START;
414 int_vect_size = RV770_SMC_INT_VECTOR_SIZE;
415 break;
416 case CHIP_RV730:
417 ucode_start_address = RV730_SMC_UCODE_START;
418 ucode_size = RV730_SMC_UCODE_SIZE;
419 int_vect = (const u8 *)&rv730_smc_int_vectors;
420 int_vect_start_address = RV730_SMC_INT_VECTOR_START;
421 int_vect_size = RV730_SMC_INT_VECTOR_SIZE;
422 break;
423 case CHIP_RV710:
424 ucode_start_address = RV710_SMC_UCODE_START;
425 ucode_size = RV710_SMC_UCODE_SIZE;
426 int_vect = (const u8 *)&rv710_smc_int_vectors;
427 int_vect_start_address = RV710_SMC_INT_VECTOR_START;
428 int_vect_size = RV710_SMC_INT_VECTOR_SIZE;
429 break;
430 case CHIP_RV740:
431 ucode_start_address = RV740_SMC_UCODE_START;
432 ucode_size = RV740_SMC_UCODE_SIZE;
433 int_vect = (const u8 *)&rv740_smc_int_vectors;
434 int_vect_start_address = RV740_SMC_INT_VECTOR_START;
435 int_vect_size = RV740_SMC_INT_VECTOR_SIZE;
436 break;
Alex Deucherdc50ba72013-06-26 00:33:35 -0400437 case CHIP_CEDAR:
438 ucode_start_address = CEDAR_SMC_UCODE_START;
439 ucode_size = CEDAR_SMC_UCODE_SIZE;
440 int_vect = (const u8 *)&cedar_smc_int_vectors;
441 int_vect_start_address = CEDAR_SMC_INT_VECTOR_START;
442 int_vect_size = CEDAR_SMC_INT_VECTOR_SIZE;
443 break;
444 case CHIP_REDWOOD:
445 ucode_start_address = REDWOOD_SMC_UCODE_START;
446 ucode_size = REDWOOD_SMC_UCODE_SIZE;
447 int_vect = (const u8 *)&redwood_smc_int_vectors;
448 int_vect_start_address = REDWOOD_SMC_INT_VECTOR_START;
449 int_vect_size = REDWOOD_SMC_INT_VECTOR_SIZE;
450 break;
451 case CHIP_JUNIPER:
452 ucode_start_address = JUNIPER_SMC_UCODE_START;
453 ucode_size = JUNIPER_SMC_UCODE_SIZE;
454 int_vect = (const u8 *)&juniper_smc_int_vectors;
455 int_vect_start_address = JUNIPER_SMC_INT_VECTOR_START;
456 int_vect_size = JUNIPER_SMC_INT_VECTOR_SIZE;
457 break;
458 case CHIP_CYPRESS:
459 case CHIP_HEMLOCK:
460 ucode_start_address = CYPRESS_SMC_UCODE_START;
461 ucode_size = CYPRESS_SMC_UCODE_SIZE;
462 int_vect = (const u8 *)&cypress_smc_int_vectors;
463 int_vect_start_address = CYPRESS_SMC_INT_VECTOR_START;
464 int_vect_size = CYPRESS_SMC_INT_VECTOR_SIZE;
465 break;
Alex Deucher66229b22013-06-26 00:11:19 -0400466 default:
467 DRM_ERROR("unknown asic in smc ucode loader\n");
468 BUG();
469 }
470
471 /* load the ucode */
472 ucode_data = (const u8 *)rdev->smc_fw->data;
473 ret = rv770_copy_bytes_to_smc(rdev, ucode_start_address,
474 ucode_data, ucode_size, limit);
475 if (ret)
476 return ret;
477
478 /* set up the int vectors */
479 ret = rv770_program_interrupt_vectors(rdev, int_vect_start_address,
480 int_vect, int_vect_size);
481 if (ret)
482 return ret;
483
484 return 0;
485}
486
487int rv770_read_smc_sram_dword(struct radeon_device *rdev,
488 u16 smc_address, u32 *value, u16 limit)
489{
490 int ret;
491
492 ret = rv770_set_smc_sram_address(rdev, smc_address, limit);
493 if (ret)
494 return ret;
495
496 *value = RREG32(SMC_SRAM_DATA);
497
498 return 0;
499}
500
501int rv770_write_smc_sram_dword(struct radeon_device *rdev,
502 u16 smc_address, u32 value, u16 limit)
503{
504 int ret;
505
506 ret = rv770_set_smc_sram_address(rdev, smc_address, limit);
507 if (ret)
508 return ret;
509
510 WREG32(SMC_SRAM_DATA, value);
511
512 return 0;
513}