Linux Audio

Check our new training course

Loading...
v6.13.7
  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}
v4.6
  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}