1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * Copyright 2011 Advanced Micro Devices, Inc.
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * Permission is hereby granted, free of charge, to any person obtaining a
5*4882a593Smuzhiyun * copy of this software and associated documentation files (the "Software"),
6*4882a593Smuzhiyun * to deal in the Software without restriction, including without limitation
7*4882a593Smuzhiyun * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8*4882a593Smuzhiyun * and/or sell copies of the Software, and to permit persons to whom the
9*4882a593Smuzhiyun * Software is furnished to do so, subject to the following conditions:
10*4882a593Smuzhiyun *
11*4882a593Smuzhiyun * The above copyright notice and this permission notice shall be included in
12*4882a593Smuzhiyun * all copies or substantial portions of the Software.
13*4882a593Smuzhiyun *
14*4882a593Smuzhiyun * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15*4882a593Smuzhiyun * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16*4882a593Smuzhiyun * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17*4882a593Smuzhiyun * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18*4882a593Smuzhiyun * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19*4882a593Smuzhiyun * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20*4882a593Smuzhiyun * OTHER DEALINGS IN THE SOFTWARE.
21*4882a593Smuzhiyun *
22*4882a593Smuzhiyun * Authors: Alex Deucher
23*4882a593Smuzhiyun */
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun #include <linux/firmware.h>
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun #include "radeon.h"
28*4882a593Smuzhiyun #include "sid.h"
29*4882a593Smuzhiyun #include "ppsmc.h"
30*4882a593Smuzhiyun #include "radeon_ucode.h"
31*4882a593Smuzhiyun #include "sislands_smc.h"
32*4882a593Smuzhiyun
si_set_smc_sram_address(struct radeon_device * rdev,u32 smc_address,u32 limit)33*4882a593Smuzhiyun static int si_set_smc_sram_address(struct radeon_device *rdev,
34*4882a593Smuzhiyun u32 smc_address, u32 limit)
35*4882a593Smuzhiyun {
36*4882a593Smuzhiyun if (smc_address & 3)
37*4882a593Smuzhiyun return -EINVAL;
38*4882a593Smuzhiyun if ((smc_address + 3) > limit)
39*4882a593Smuzhiyun return -EINVAL;
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun WREG32(SMC_IND_INDEX_0, smc_address);
42*4882a593Smuzhiyun WREG32_P(SMC_IND_ACCESS_CNTL, 0, ~AUTO_INCREMENT_IND_0);
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun return 0;
45*4882a593Smuzhiyun }
46*4882a593Smuzhiyun
si_copy_bytes_to_smc(struct radeon_device * rdev,u32 smc_start_address,const u8 * src,u32 byte_count,u32 limit)47*4882a593Smuzhiyun int si_copy_bytes_to_smc(struct radeon_device *rdev,
48*4882a593Smuzhiyun u32 smc_start_address,
49*4882a593Smuzhiyun const u8 *src, u32 byte_count, u32 limit)
50*4882a593Smuzhiyun {
51*4882a593Smuzhiyun unsigned long flags;
52*4882a593Smuzhiyun int ret = 0;
53*4882a593Smuzhiyun u32 data, original_data, addr, extra_shift;
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun if (smc_start_address & 3)
56*4882a593Smuzhiyun return -EINVAL;
57*4882a593Smuzhiyun if ((smc_start_address + byte_count) > limit)
58*4882a593Smuzhiyun return -EINVAL;
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun addr = smc_start_address;
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun spin_lock_irqsave(&rdev->smc_idx_lock, flags);
63*4882a593Smuzhiyun while (byte_count >= 4) {
64*4882a593Smuzhiyun /* SMC address space is BE */
65*4882a593Smuzhiyun data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun ret = si_set_smc_sram_address(rdev, addr, limit);
68*4882a593Smuzhiyun if (ret)
69*4882a593Smuzhiyun goto done;
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun WREG32(SMC_IND_DATA_0, data);
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun src += 4;
74*4882a593Smuzhiyun byte_count -= 4;
75*4882a593Smuzhiyun addr += 4;
76*4882a593Smuzhiyun }
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun /* RMW for the final bytes */
79*4882a593Smuzhiyun if (byte_count > 0) {
80*4882a593Smuzhiyun data = 0;
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun ret = si_set_smc_sram_address(rdev, addr, limit);
83*4882a593Smuzhiyun if (ret)
84*4882a593Smuzhiyun goto done;
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun original_data = RREG32(SMC_IND_DATA_0);
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun extra_shift = 8 * (4 - byte_count);
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun while (byte_count > 0) {
91*4882a593Smuzhiyun /* SMC address space is BE */
92*4882a593Smuzhiyun data = (data << 8) + *src++;
93*4882a593Smuzhiyun byte_count--;
94*4882a593Smuzhiyun }
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun data <<= extra_shift;
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun data |= (original_data & ~((~0UL) << extra_shift));
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun ret = si_set_smc_sram_address(rdev, addr, limit);
101*4882a593Smuzhiyun if (ret)
102*4882a593Smuzhiyun goto done;
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun WREG32(SMC_IND_DATA_0, data);
105*4882a593Smuzhiyun }
106*4882a593Smuzhiyun
107*4882a593Smuzhiyun done:
108*4882a593Smuzhiyun spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
109*4882a593Smuzhiyun
110*4882a593Smuzhiyun return ret;
111*4882a593Smuzhiyun }
112*4882a593Smuzhiyun
si_start_smc(struct radeon_device * rdev)113*4882a593Smuzhiyun void si_start_smc(struct radeon_device *rdev)
114*4882a593Smuzhiyun {
115*4882a593Smuzhiyun u32 tmp = RREG32_SMC(SMC_SYSCON_RESET_CNTL);
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun tmp &= ~RST_REG;
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun WREG32_SMC(SMC_SYSCON_RESET_CNTL, tmp);
120*4882a593Smuzhiyun }
121*4882a593Smuzhiyun
si_reset_smc(struct radeon_device * rdev)122*4882a593Smuzhiyun void si_reset_smc(struct radeon_device *rdev)
123*4882a593Smuzhiyun {
124*4882a593Smuzhiyun u32 tmp;
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun RREG32(CB_CGTT_SCLK_CTRL);
127*4882a593Smuzhiyun RREG32(CB_CGTT_SCLK_CTRL);
128*4882a593Smuzhiyun RREG32(CB_CGTT_SCLK_CTRL);
129*4882a593Smuzhiyun RREG32(CB_CGTT_SCLK_CTRL);
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun tmp = RREG32_SMC(SMC_SYSCON_RESET_CNTL);
132*4882a593Smuzhiyun tmp |= RST_REG;
133*4882a593Smuzhiyun WREG32_SMC(SMC_SYSCON_RESET_CNTL, tmp);
134*4882a593Smuzhiyun }
135*4882a593Smuzhiyun
si_program_jump_on_start(struct radeon_device * rdev)136*4882a593Smuzhiyun int si_program_jump_on_start(struct radeon_device *rdev)
137*4882a593Smuzhiyun {
138*4882a593Smuzhiyun static const u8 data[] = { 0x0E, 0x00, 0x40, 0x40 };
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun return si_copy_bytes_to_smc(rdev, 0x0, data, 4, sizeof(data)+1);
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun
si_stop_smc_clock(struct radeon_device * rdev)143*4882a593Smuzhiyun void si_stop_smc_clock(struct radeon_device *rdev)
144*4882a593Smuzhiyun {
145*4882a593Smuzhiyun u32 tmp = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun tmp |= CK_DISABLE;
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun WREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0, tmp);
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun
si_start_smc_clock(struct radeon_device * rdev)152*4882a593Smuzhiyun void si_start_smc_clock(struct radeon_device *rdev)
153*4882a593Smuzhiyun {
154*4882a593Smuzhiyun u32 tmp = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun tmp &= ~CK_DISABLE;
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun WREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0, tmp);
159*4882a593Smuzhiyun }
160*4882a593Smuzhiyun
si_is_smc_running(struct radeon_device * rdev)161*4882a593Smuzhiyun bool si_is_smc_running(struct radeon_device *rdev)
162*4882a593Smuzhiyun {
163*4882a593Smuzhiyun u32 rst = RREG32_SMC(SMC_SYSCON_RESET_CNTL);
164*4882a593Smuzhiyun u32 clk = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun if (!(rst & RST_REG) && !(clk & CK_DISABLE))
167*4882a593Smuzhiyun return true;
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun return false;
170*4882a593Smuzhiyun }
171*4882a593Smuzhiyun
si_send_msg_to_smc(struct radeon_device * rdev,PPSMC_Msg msg)172*4882a593Smuzhiyun PPSMC_Result si_send_msg_to_smc(struct radeon_device *rdev, PPSMC_Msg msg)
173*4882a593Smuzhiyun {
174*4882a593Smuzhiyun u32 tmp;
175*4882a593Smuzhiyun int i;
176*4882a593Smuzhiyun
177*4882a593Smuzhiyun if (!si_is_smc_running(rdev))
178*4882a593Smuzhiyun return PPSMC_Result_Failed;
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun WREG32(SMC_MESSAGE_0, msg);
181*4882a593Smuzhiyun
182*4882a593Smuzhiyun for (i = 0; i < rdev->usec_timeout; i++) {
183*4882a593Smuzhiyun tmp = RREG32(SMC_RESP_0);
184*4882a593Smuzhiyun if (tmp != 0)
185*4882a593Smuzhiyun break;
186*4882a593Smuzhiyun udelay(1);
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun tmp = RREG32(SMC_RESP_0);
189*4882a593Smuzhiyun
190*4882a593Smuzhiyun return (PPSMC_Result)tmp;
191*4882a593Smuzhiyun }
192*4882a593Smuzhiyun
si_wait_for_smc_inactive(struct radeon_device * rdev)193*4882a593Smuzhiyun PPSMC_Result si_wait_for_smc_inactive(struct radeon_device *rdev)
194*4882a593Smuzhiyun {
195*4882a593Smuzhiyun u32 tmp;
196*4882a593Smuzhiyun int i;
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun if (!si_is_smc_running(rdev))
199*4882a593Smuzhiyun return PPSMC_Result_OK;
200*4882a593Smuzhiyun
201*4882a593Smuzhiyun for (i = 0; i < rdev->usec_timeout; i++) {
202*4882a593Smuzhiyun tmp = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
203*4882a593Smuzhiyun if ((tmp & CKEN) == 0)
204*4882a593Smuzhiyun break;
205*4882a593Smuzhiyun udelay(1);
206*4882a593Smuzhiyun }
207*4882a593Smuzhiyun
208*4882a593Smuzhiyun return PPSMC_Result_OK;
209*4882a593Smuzhiyun }
210*4882a593Smuzhiyun
si_load_smc_ucode(struct radeon_device * rdev,u32 limit)211*4882a593Smuzhiyun int si_load_smc_ucode(struct radeon_device *rdev, u32 limit)
212*4882a593Smuzhiyun {
213*4882a593Smuzhiyun unsigned long flags;
214*4882a593Smuzhiyun u32 ucode_start_address;
215*4882a593Smuzhiyun u32 ucode_size;
216*4882a593Smuzhiyun const u8 *src;
217*4882a593Smuzhiyun u32 data;
218*4882a593Smuzhiyun
219*4882a593Smuzhiyun if (!rdev->smc_fw)
220*4882a593Smuzhiyun return -EINVAL;
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun if (rdev->new_fw) {
223*4882a593Smuzhiyun const struct smc_firmware_header_v1_0 *hdr =
224*4882a593Smuzhiyun (const struct smc_firmware_header_v1_0 *)rdev->smc_fw->data;
225*4882a593Smuzhiyun
226*4882a593Smuzhiyun radeon_ucode_print_smc_hdr(&hdr->header);
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun ucode_start_address = le32_to_cpu(hdr->ucode_start_addr);
229*4882a593Smuzhiyun ucode_size = le32_to_cpu(hdr->header.ucode_size_bytes);
230*4882a593Smuzhiyun src = (const u8 *)
231*4882a593Smuzhiyun (rdev->smc_fw->data + le32_to_cpu(hdr->header.ucode_array_offset_bytes));
232*4882a593Smuzhiyun } else {
233*4882a593Smuzhiyun switch (rdev->family) {
234*4882a593Smuzhiyun case CHIP_TAHITI:
235*4882a593Smuzhiyun ucode_start_address = TAHITI_SMC_UCODE_START;
236*4882a593Smuzhiyun ucode_size = TAHITI_SMC_UCODE_SIZE;
237*4882a593Smuzhiyun break;
238*4882a593Smuzhiyun case CHIP_PITCAIRN:
239*4882a593Smuzhiyun ucode_start_address = PITCAIRN_SMC_UCODE_START;
240*4882a593Smuzhiyun ucode_size = PITCAIRN_SMC_UCODE_SIZE;
241*4882a593Smuzhiyun break;
242*4882a593Smuzhiyun case CHIP_VERDE:
243*4882a593Smuzhiyun ucode_start_address = VERDE_SMC_UCODE_START;
244*4882a593Smuzhiyun ucode_size = VERDE_SMC_UCODE_SIZE;
245*4882a593Smuzhiyun break;
246*4882a593Smuzhiyun case CHIP_OLAND:
247*4882a593Smuzhiyun ucode_start_address = OLAND_SMC_UCODE_START;
248*4882a593Smuzhiyun ucode_size = OLAND_SMC_UCODE_SIZE;
249*4882a593Smuzhiyun break;
250*4882a593Smuzhiyun case CHIP_HAINAN:
251*4882a593Smuzhiyun ucode_start_address = HAINAN_SMC_UCODE_START;
252*4882a593Smuzhiyun ucode_size = HAINAN_SMC_UCODE_SIZE;
253*4882a593Smuzhiyun break;
254*4882a593Smuzhiyun default:
255*4882a593Smuzhiyun DRM_ERROR("unknown asic in smc ucode loader\n");
256*4882a593Smuzhiyun BUG();
257*4882a593Smuzhiyun }
258*4882a593Smuzhiyun src = (const u8 *)rdev->smc_fw->data;
259*4882a593Smuzhiyun }
260*4882a593Smuzhiyun
261*4882a593Smuzhiyun if (ucode_size & 3)
262*4882a593Smuzhiyun return -EINVAL;
263*4882a593Smuzhiyun
264*4882a593Smuzhiyun spin_lock_irqsave(&rdev->smc_idx_lock, flags);
265*4882a593Smuzhiyun WREG32(SMC_IND_INDEX_0, ucode_start_address);
266*4882a593Smuzhiyun WREG32_P(SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, ~AUTO_INCREMENT_IND_0);
267*4882a593Smuzhiyun while (ucode_size >= 4) {
268*4882a593Smuzhiyun /* SMC address space is BE */
269*4882a593Smuzhiyun data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
270*4882a593Smuzhiyun
271*4882a593Smuzhiyun WREG32(SMC_IND_DATA_0, data);
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun src += 4;
274*4882a593Smuzhiyun ucode_size -= 4;
275*4882a593Smuzhiyun }
276*4882a593Smuzhiyun WREG32_P(SMC_IND_ACCESS_CNTL, 0, ~AUTO_INCREMENT_IND_0);
277*4882a593Smuzhiyun spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
278*4882a593Smuzhiyun
279*4882a593Smuzhiyun return 0;
280*4882a593Smuzhiyun }
281*4882a593Smuzhiyun
si_read_smc_sram_dword(struct radeon_device * rdev,u32 smc_address,u32 * value,u32 limit)282*4882a593Smuzhiyun int si_read_smc_sram_dword(struct radeon_device *rdev, u32 smc_address,
283*4882a593Smuzhiyun u32 *value, u32 limit)
284*4882a593Smuzhiyun {
285*4882a593Smuzhiyun unsigned long flags;
286*4882a593Smuzhiyun int ret;
287*4882a593Smuzhiyun
288*4882a593Smuzhiyun spin_lock_irqsave(&rdev->smc_idx_lock, flags);
289*4882a593Smuzhiyun ret = si_set_smc_sram_address(rdev, smc_address, limit);
290*4882a593Smuzhiyun if (ret == 0)
291*4882a593Smuzhiyun *value = RREG32(SMC_IND_DATA_0);
292*4882a593Smuzhiyun spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun return ret;
295*4882a593Smuzhiyun }
296*4882a593Smuzhiyun
si_write_smc_sram_dword(struct radeon_device * rdev,u32 smc_address,u32 value,u32 limit)297*4882a593Smuzhiyun int si_write_smc_sram_dword(struct radeon_device *rdev, u32 smc_address,
298*4882a593Smuzhiyun u32 value, u32 limit)
299*4882a593Smuzhiyun {
300*4882a593Smuzhiyun unsigned long flags;
301*4882a593Smuzhiyun int ret;
302*4882a593Smuzhiyun
303*4882a593Smuzhiyun spin_lock_irqsave(&rdev->smc_idx_lock, flags);
304*4882a593Smuzhiyun ret = si_set_smc_sram_address(rdev, smc_address, limit);
305*4882a593Smuzhiyun if (ret == 0)
306*4882a593Smuzhiyun WREG32(SMC_IND_DATA_0, value);
307*4882a593Smuzhiyun spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
308*4882a593Smuzhiyun
309*4882a593Smuzhiyun return ret;
310*4882a593Smuzhiyun }
311