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