1/*
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
117static 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
197static const u8 barts_smc_int_vectors[] =
198{
199	0x0C, 0x14, 0x0C, 0x14,
200	0x0C, 0x14, 0x0C, 0x14,
201	0x0C, 0x14, 0x0C, 0x14,
202	0x0C, 0x14, 0x0C, 0x14,
203	0x0C, 0x14, 0x0C, 0x14,
204	0x0C, 0x14, 0x0C, 0x14,
205	0x0C, 0x14, 0x0C, 0x14,
206	0x0C, 0x14, 0x0C, 0x14,
207	0x0C, 0x14, 0x0C, 0x14,
208	0x0C, 0x14, 0x0C, 0x14,
209	0x0C, 0x14, 0x0C, 0x14,
210	0x0C, 0x14, 0x0C, 0x14,
211	0x0C, 0x14, 0x12, 0xAA,
212	0x0C, 0x2F, 0x15, 0xF6,
213	0x15, 0xF6, 0x05, 0x0A,
214	0x05, 0x0A, 0x05, 0x0A
215};
216
217static const u8 turks_smc_int_vectors[] =
218{
219	0x0C, 0x14, 0x0C, 0x14,
220	0x0C, 0x14, 0x0C, 0x14,
221	0x0C, 0x14, 0x0C, 0x14,
222	0x0C, 0x14, 0x0C, 0x14,
223	0x0C, 0x14, 0x0C, 0x14,
224	0x0C, 0x14, 0x0C, 0x14,
225	0x0C, 0x14, 0x0C, 0x14,
226	0x0C, 0x14, 0x0C, 0x14,
227	0x0C, 0x14, 0x0C, 0x14,
228	0x0C, 0x14, 0x0C, 0x14,
229	0x0C, 0x14, 0x0C, 0x14,
230	0x0C, 0x14, 0x0C, 0x14,
231	0x0C, 0x14, 0x12, 0xAA,
232	0x0C, 0x2F, 0x15, 0xF6,
233	0x15, 0xF6, 0x05, 0x0A,
234	0x05, 0x0A, 0x05, 0x0A
235};
236
237static const u8 caicos_smc_int_vectors[] =
238{
239	0x0C, 0x14, 0x0C, 0x14,
240	0x0C, 0x14, 0x0C, 0x14,
241	0x0C, 0x14, 0x0C, 0x14,
242	0x0C, 0x14, 0x0C, 0x14,
243	0x0C, 0x14, 0x0C, 0x14,
244	0x0C, 0x14, 0x0C, 0x14,
245	0x0C, 0x14, 0x0C, 0x14,
246	0x0C, 0x14, 0x0C, 0x14,
247	0x0C, 0x14, 0x0C, 0x14,
248	0x0C, 0x14, 0x0C, 0x14,
249	0x0C, 0x14, 0x0C, 0x14,
250	0x0C, 0x14, 0x0C, 0x14,
251	0x0C, 0x14, 0x12, 0xAA,
252	0x0C, 0x2F, 0x15, 0xF6,
253	0x15, 0xF6, 0x05, 0x0A,
254	0x05, 0x0A, 0x05, 0x0A
255};
256
257static const u8 cayman_smc_int_vectors[] =
258{
259	0x12, 0x05, 0x12, 0x05,
260	0x12, 0x05, 0x12, 0x05,
261	0x12, 0x05, 0x12, 0x05,
262	0x12, 0x05, 0x12, 0x05,
263	0x12, 0x05, 0x12, 0x05,
264	0x12, 0x05, 0x12, 0x05,
265	0x12, 0x05, 0x12, 0x05,
266	0x12, 0x05, 0x12, 0x05,
267	0x12, 0x05, 0x12, 0x05,
268	0x12, 0x05, 0x12, 0x05,
269	0x12, 0x05, 0x12, 0x05,
270	0x12, 0x05, 0x12, 0x05,
271	0x12, 0x05, 0x18, 0xEA,
272	0x12, 0x20, 0x1C, 0x34,
273	0x1C, 0x34, 0x08, 0x72,
274	0x08, 0x72, 0x08, 0x72
275};
276
277static int rv770_set_smc_sram_address(struct radeon_device *rdev,
278				      u16 smc_address, u16 limit)
279{
280	u32 addr;
281
282	if (smc_address & 3)
283		return -EINVAL;
284	if ((smc_address + 3) > limit)
285		return -EINVAL;
286
287	addr = smc_address;
288	addr |= SMC_SRAM_AUTO_INC_DIS;
289
290	WREG32(SMC_SRAM_ADDR, addr);
291
292	return 0;
293}
294
295int rv770_copy_bytes_to_smc(struct radeon_device *rdev,
296			    u16 smc_start_address, const u8 *src,
297			    u16 byte_count, u16 limit)
298{
299	unsigned long flags;
300	u32 data, original_data, extra_shift;
301	u16 addr;
302	int ret = 0;
303
304	if (smc_start_address & 3)
305		return -EINVAL;
306	if ((smc_start_address + byte_count) > limit)
307		return -EINVAL;
308
309	addr = smc_start_address;
310
311	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
312	while (byte_count >= 4) {
313		/* SMC address space is BE */
314		data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
315
316		ret = rv770_set_smc_sram_address(rdev, addr, limit);
317		if (ret)
318			goto done;
319
320		WREG32(SMC_SRAM_DATA, data);
321
322		src += 4;
323		byte_count -= 4;
324		addr += 4;
325	}
326
327	/* RMW for final bytes */
328	if (byte_count > 0) {
329		data = 0;
330
331		ret = rv770_set_smc_sram_address(rdev, addr, limit);
332		if (ret)
333			goto done;
334
335		original_data = RREG32(SMC_SRAM_DATA);
336
337		extra_shift = 8 * (4 - byte_count);
338
339		while (byte_count > 0) {
340			/* SMC address space is BE */
341			data = (data << 8) + *src++;
342			byte_count--;
343		}
344
345		data <<= extra_shift;
346
347		data |= (original_data & ~((~0UL) << extra_shift));
348
349		ret = rv770_set_smc_sram_address(rdev, addr, limit);
350		if (ret)
351			goto done;
352
353		WREG32(SMC_SRAM_DATA, data);
354	}
355
356done:
357	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
358
359	return ret;
360}
361
362static int rv770_program_interrupt_vectors(struct radeon_device *rdev,
363					   u32 smc_first_vector, const u8 *src,
364					   u32 byte_count)
365{
366	u32 tmp, i;
367
368	if (byte_count % 4)
369		return -EINVAL;
370
371	if (smc_first_vector < FIRST_SMC_INT_VECT_REG) {
372		tmp = FIRST_SMC_INT_VECT_REG - smc_first_vector;
373
374		if (tmp > byte_count)
375			return 0;
376
377		byte_count -= tmp;
378		src += tmp;
379		smc_first_vector = FIRST_SMC_INT_VECT_REG;
380	}
381
382	for (i = 0; i < byte_count; i += 4) {
383		/* SMC address space is BE */
384		tmp = (src[i] << 24) | (src[i + 1] << 16) | (src[i + 2] << 8) | src[i + 3];
385
386		WREG32(SMC_ISR_FFD8_FFDB + i, tmp);
387	}
388
389	return 0;
390}
391
392void rv770_start_smc(struct radeon_device *rdev)
393{
394	WREG32_P(SMC_IO, SMC_RST_N, ~SMC_RST_N);
395}
396
397void rv770_reset_smc(struct radeon_device *rdev)
398{
399	WREG32_P(SMC_IO, 0, ~SMC_RST_N);
400}
401
402void rv770_stop_smc_clock(struct radeon_device *rdev)
403{
404	WREG32_P(SMC_IO, 0, ~SMC_CLK_EN);
405}
406
407void rv770_start_smc_clock(struct radeon_device *rdev)
408{
409	WREG32_P(SMC_IO, SMC_CLK_EN, ~SMC_CLK_EN);
410}
411
412bool rv770_is_smc_running(struct radeon_device *rdev)
413{
414	u32 tmp;
415
416	tmp = RREG32(SMC_IO);
417
418	if ((tmp & SMC_RST_N) && (tmp & SMC_CLK_EN))
419		return true;
420	else
421		return false;
422}
423
424PPSMC_Result rv770_send_msg_to_smc(struct radeon_device *rdev, PPSMC_Msg msg)
425{
426	u32 tmp;
427	int i;
428	PPSMC_Result result;
429
430	if (!rv770_is_smc_running(rdev))
431		return PPSMC_Result_Failed;
432
433	WREG32_P(SMC_MSG, HOST_SMC_MSG(msg), ~HOST_SMC_MSG_MASK);
434
435	for (i = 0; i < rdev->usec_timeout; i++) {
436		tmp = RREG32(SMC_MSG) & HOST_SMC_RESP_MASK;
437		tmp >>= HOST_SMC_RESP_SHIFT;
438		if (tmp != 0)
439			break;
440		udelay(1);
441	}
442
443	tmp = RREG32(SMC_MSG) & HOST_SMC_RESP_MASK;
444	tmp >>= HOST_SMC_RESP_SHIFT;
445
446	result = (PPSMC_Result)tmp;
447	return result;
448}
449
450PPSMC_Result rv770_wait_for_smc_inactive(struct radeon_device *rdev)
451{
452	int i;
453	PPSMC_Result result = PPSMC_Result_OK;
454
455	if (!rv770_is_smc_running(rdev))
456		return result;
457
458	for (i = 0; i < rdev->usec_timeout; i++) {
459		if (RREG32(SMC_IO) & SMC_STOP_MODE)
460			break;
461		udelay(1);
462	}
463
464	return result;
465}
466
467static void rv770_clear_smc_sram(struct radeon_device *rdev, u16 limit)
468{
469	unsigned long flags;
470	u16 i;
471
472	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
473	for (i = 0;  i < limit; i += 4) {
474		rv770_set_smc_sram_address(rdev, i, limit);
475		WREG32(SMC_SRAM_DATA, 0);
476	}
477	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
478}
479
480int rv770_load_smc_ucode(struct radeon_device *rdev,
481			 u16 limit)
482{
483	int ret;
484	const u8 *int_vect;
485	u16 int_vect_start_address;
486	u16 int_vect_size;
487	const u8 *ucode_data;
488	u16 ucode_start_address;
489	u16 ucode_size;
490
491	if (!rdev->smc_fw)
492		return -EINVAL;
493
494	rv770_clear_smc_sram(rdev, limit);
495
496	switch (rdev->family) {
497	case CHIP_RV770:
498		ucode_start_address = RV770_SMC_UCODE_START;
499		ucode_size = RV770_SMC_UCODE_SIZE;
500		int_vect = (const u8 *)&rv770_smc_int_vectors;
501		int_vect_start_address = RV770_SMC_INT_VECTOR_START;
502		int_vect_size = RV770_SMC_INT_VECTOR_SIZE;
503		break;
504	case CHIP_RV730:
505		ucode_start_address = RV730_SMC_UCODE_START;
506		ucode_size = RV730_SMC_UCODE_SIZE;
507		int_vect = (const u8 *)&rv730_smc_int_vectors;
508		int_vect_start_address = RV730_SMC_INT_VECTOR_START;
509		int_vect_size = RV730_SMC_INT_VECTOR_SIZE;
510		break;
511	case CHIP_RV710:
512		ucode_start_address = RV710_SMC_UCODE_START;
513		ucode_size = RV710_SMC_UCODE_SIZE;
514		int_vect = (const u8 *)&rv710_smc_int_vectors;
515		int_vect_start_address = RV710_SMC_INT_VECTOR_START;
516		int_vect_size = RV710_SMC_INT_VECTOR_SIZE;
517		break;
518	case CHIP_RV740:
519		ucode_start_address = RV740_SMC_UCODE_START;
520		ucode_size = RV740_SMC_UCODE_SIZE;
521		int_vect = (const u8 *)&rv740_smc_int_vectors;
522		int_vect_start_address = RV740_SMC_INT_VECTOR_START;
523		int_vect_size = RV740_SMC_INT_VECTOR_SIZE;
524		break;
525	case CHIP_CEDAR:
526		ucode_start_address = CEDAR_SMC_UCODE_START;
527		ucode_size = CEDAR_SMC_UCODE_SIZE;
528		int_vect = (const u8 *)&cedar_smc_int_vectors;
529		int_vect_start_address = CEDAR_SMC_INT_VECTOR_START;
530		int_vect_size = CEDAR_SMC_INT_VECTOR_SIZE;
531		break;
532	case CHIP_REDWOOD:
533		ucode_start_address = REDWOOD_SMC_UCODE_START;
534		ucode_size = REDWOOD_SMC_UCODE_SIZE;
535		int_vect = (const u8 *)&redwood_smc_int_vectors;
536		int_vect_start_address = REDWOOD_SMC_INT_VECTOR_START;
537		int_vect_size = REDWOOD_SMC_INT_VECTOR_SIZE;
538		break;
539	case CHIP_JUNIPER:
540		ucode_start_address = JUNIPER_SMC_UCODE_START;
541		ucode_size = JUNIPER_SMC_UCODE_SIZE;
542		int_vect = (const u8 *)&juniper_smc_int_vectors;
543		int_vect_start_address = JUNIPER_SMC_INT_VECTOR_START;
544		int_vect_size = JUNIPER_SMC_INT_VECTOR_SIZE;
545		break;
546	case CHIP_CYPRESS:
547	case CHIP_HEMLOCK:
548		ucode_start_address = CYPRESS_SMC_UCODE_START;
549		ucode_size = CYPRESS_SMC_UCODE_SIZE;
550		int_vect = (const u8 *)&cypress_smc_int_vectors;
551		int_vect_start_address = CYPRESS_SMC_INT_VECTOR_START;
552		int_vect_size = CYPRESS_SMC_INT_VECTOR_SIZE;
553		break;
554	case CHIP_BARTS:
555		ucode_start_address = BARTS_SMC_UCODE_START;
556		ucode_size = BARTS_SMC_UCODE_SIZE;
557		int_vect = (const u8 *)&barts_smc_int_vectors;
558		int_vect_start_address = BARTS_SMC_INT_VECTOR_START;
559		int_vect_size = BARTS_SMC_INT_VECTOR_SIZE;
560		break;
561	case CHIP_TURKS:
562		ucode_start_address = TURKS_SMC_UCODE_START;
563		ucode_size = TURKS_SMC_UCODE_SIZE;
564		int_vect = (const u8 *)&turks_smc_int_vectors;
565		int_vect_start_address = TURKS_SMC_INT_VECTOR_START;
566		int_vect_size = TURKS_SMC_INT_VECTOR_SIZE;
567		break;
568	case CHIP_CAICOS:
569		ucode_start_address = CAICOS_SMC_UCODE_START;
570		ucode_size = CAICOS_SMC_UCODE_SIZE;
571		int_vect = (const u8 *)&caicos_smc_int_vectors;
572		int_vect_start_address = CAICOS_SMC_INT_VECTOR_START;
573		int_vect_size = CAICOS_SMC_INT_VECTOR_SIZE;
574		break;
575	case CHIP_CAYMAN:
576		ucode_start_address = CAYMAN_SMC_UCODE_START;
577		ucode_size = CAYMAN_SMC_UCODE_SIZE;
578		int_vect = (const u8 *)&cayman_smc_int_vectors;
579		int_vect_start_address = CAYMAN_SMC_INT_VECTOR_START;
580		int_vect_size = CAYMAN_SMC_INT_VECTOR_SIZE;
581		break;
582	default:
583		DRM_ERROR("unknown asic in smc ucode loader\n");
584		BUG();
585	}
586
587	/* load the ucode */
588	ucode_data = (const u8 *)rdev->smc_fw->data;
589	ret = rv770_copy_bytes_to_smc(rdev, ucode_start_address,
590				      ucode_data, ucode_size, limit);
591	if (ret)
592		return ret;
593
594	/* set up the int vectors */
595	ret = rv770_program_interrupt_vectors(rdev, int_vect_start_address,
596					      int_vect, int_vect_size);
597	if (ret)
598		return ret;
599
600	return 0;
601}
602
603int rv770_read_smc_sram_dword(struct radeon_device *rdev,
604			      u16 smc_address, u32 *value, u16 limit)
605{
606	unsigned long flags;
607	int ret;
608
609	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
610	ret = rv770_set_smc_sram_address(rdev, smc_address, limit);
611	if (ret == 0)
612		*value = RREG32(SMC_SRAM_DATA);
613	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
614
615	return ret;
616}
617
618int rv770_write_smc_sram_dword(struct radeon_device *rdev,
619			       u16 smc_address, u32 value, u16 limit)
620{
621	unsigned long flags;
622	int ret;
623
624	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
625	ret = rv770_set_smc_sram_address(rdev, smc_address, limit);
626	if (ret == 0)
627		WREG32(SMC_SRAM_DATA, value);
628	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
629
630	return ret;
631}
632