Line data Source code
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 <dev/pci/drm/drmP.h>
26 : #include "radeon.h"
27 : #include "cikd.h"
28 : #include "ppsmc.h"
29 : #include "radeon_ucode.h"
30 : #include "ci_dpm.h"
31 :
32 0 : static int ci_set_smc_sram_address(struct radeon_device *rdev,
33 : u32 smc_address, u32 limit)
34 : {
35 0 : if (smc_address & 3)
36 0 : return -EINVAL;
37 0 : if ((smc_address + 3) > limit)
38 0 : return -EINVAL;
39 :
40 0 : WREG32(SMC_IND_INDEX_0, smc_address);
41 0 : WREG32_P(SMC_IND_ACCESS_CNTL, 0, ~AUTO_INCREMENT_IND_0);
42 :
43 0 : return 0;
44 0 : }
45 :
46 0 : int ci_copy_bytes_to_smc(struct radeon_device *rdev,
47 : u32 smc_start_address,
48 : const u8 *src, u32 byte_count, u32 limit)
49 : {
50 : unsigned long flags;
51 : u32 data, original_data;
52 : u32 addr;
53 : u32 extra_shift;
54 : int ret = 0;
55 :
56 0 : if (smc_start_address & 3)
57 0 : return -EINVAL;
58 0 : if ((smc_start_address + byte_count) > limit)
59 0 : return -EINVAL;
60 :
61 : addr = smc_start_address;
62 :
63 0 : spin_lock_irqsave(&rdev->smc_idx_lock, flags);
64 0 : while (byte_count >= 4) {
65 : /* SMC address space is BE */
66 0 : data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
67 :
68 0 : ret = ci_set_smc_sram_address(rdev, addr, limit);
69 0 : if (ret)
70 : goto done;
71 :
72 0 : WREG32(SMC_IND_DATA_0, data);
73 :
74 0 : src += 4;
75 0 : byte_count -= 4;
76 0 : addr += 4;
77 : }
78 :
79 : /* RMW for the final bytes */
80 0 : if (byte_count > 0) {
81 : data = 0;
82 :
83 0 : ret = ci_set_smc_sram_address(rdev, addr, limit);
84 0 : if (ret)
85 : goto done;
86 :
87 0 : original_data = RREG32(SMC_IND_DATA_0);
88 :
89 0 : extra_shift = 8 * (4 - byte_count);
90 :
91 0 : while (byte_count > 0) {
92 0 : data = (data << 8) + *src++;
93 0 : byte_count--;
94 : }
95 :
96 0 : data <<= extra_shift;
97 :
98 0 : data |= (original_data & ~((~0UL) << extra_shift));
99 :
100 0 : ret = ci_set_smc_sram_address(rdev, addr, limit);
101 0 : if (ret)
102 : goto done;
103 :
104 0 : WREG32(SMC_IND_DATA_0, data);
105 0 : }
106 :
107 : done:
108 0 : spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
109 :
110 0 : return ret;
111 0 : }
112 :
113 0 : void ci_start_smc(struct radeon_device *rdev)
114 : {
115 0 : u32 tmp = RREG32_SMC(SMC_SYSCON_RESET_CNTL);
116 :
117 0 : tmp &= ~RST_REG;
118 0 : WREG32_SMC(SMC_SYSCON_RESET_CNTL, tmp);
119 0 : }
120 :
121 0 : void ci_reset_smc(struct radeon_device *rdev)
122 : {
123 0 : u32 tmp = RREG32_SMC(SMC_SYSCON_RESET_CNTL);
124 :
125 0 : tmp |= RST_REG;
126 0 : WREG32_SMC(SMC_SYSCON_RESET_CNTL, tmp);
127 0 : }
128 :
129 0 : int ci_program_jump_on_start(struct radeon_device *rdev)
130 : {
131 : static const u8 data[] = { 0xE0, 0x00, 0x80, 0x40 };
132 :
133 0 : return ci_copy_bytes_to_smc(rdev, 0x0, data, 4, sizeof(data)+1);
134 : }
135 :
136 0 : void ci_stop_smc_clock(struct radeon_device *rdev)
137 : {
138 0 : u32 tmp = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
139 :
140 0 : tmp |= CK_DISABLE;
141 :
142 0 : WREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0, tmp);
143 0 : }
144 :
145 0 : void ci_start_smc_clock(struct radeon_device *rdev)
146 : {
147 0 : u32 tmp = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
148 :
149 0 : tmp &= ~CK_DISABLE;
150 :
151 0 : WREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0, tmp);
152 0 : }
153 :
154 0 : bool ci_is_smc_running(struct radeon_device *rdev)
155 : {
156 0 : u32 clk = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
157 0 : u32 pc_c = RREG32_SMC(SMC_PC_C);
158 :
159 0 : if (!(clk & CK_DISABLE) && (0x20100 <= pc_c))
160 0 : return true;
161 :
162 0 : return false;
163 0 : }
164 :
165 0 : PPSMC_Result ci_send_msg_to_smc(struct radeon_device *rdev, PPSMC_Msg msg)
166 : {
167 : u32 tmp;
168 : int i;
169 :
170 0 : if (!ci_is_smc_running(rdev))
171 0 : return PPSMC_Result_Failed;
172 :
173 0 : WREG32(SMC_MESSAGE_0, msg);
174 :
175 0 : for (i = 0; i < rdev->usec_timeout; i++) {
176 0 : tmp = RREG32(SMC_RESP_0);
177 0 : if (tmp != 0)
178 : break;
179 0 : udelay(1);
180 : }
181 0 : tmp = RREG32(SMC_RESP_0);
182 :
183 0 : return (PPSMC_Result)tmp;
184 0 : }
185 :
186 : #if 0
187 : PPSMC_Result ci_wait_for_smc_inactive(struct radeon_device *rdev)
188 : {
189 : u32 tmp;
190 : int i;
191 :
192 : if (!ci_is_smc_running(rdev))
193 : return PPSMC_Result_OK;
194 :
195 : for (i = 0; i < rdev->usec_timeout; i++) {
196 : tmp = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
197 : if ((tmp & CKEN) == 0)
198 : break;
199 : udelay(1);
200 : }
201 :
202 : return PPSMC_Result_OK;
203 : }
204 : #endif
205 :
206 0 : int ci_load_smc_ucode(struct radeon_device *rdev, u32 limit)
207 : {
208 : unsigned long flags;
209 : u32 ucode_start_address;
210 : u32 ucode_size;
211 : const u8 *src;
212 : u32 data;
213 :
214 0 : if (!rdev->smc_fw)
215 0 : return -EINVAL;
216 :
217 0 : if (rdev->new_fw) {
218 : const struct smc_firmware_header_v1_0 *hdr =
219 0 : (const struct smc_firmware_header_v1_0 *)rdev->smc_fw->data;
220 :
221 0 : radeon_ucode_print_smc_hdr(&hdr->header);
222 :
223 0 : ucode_start_address = le32_to_cpu(hdr->ucode_start_addr);
224 0 : ucode_size = le32_to_cpu(hdr->header.ucode_size_bytes);
225 : src = (const u8 *)
226 0 : (rdev->smc_fw->data + le32_to_cpu(hdr->header.ucode_array_offset_bytes));
227 0 : } else {
228 0 : switch (rdev->family) {
229 : case CHIP_BONAIRE:
230 : ucode_start_address = BONAIRE_SMC_UCODE_START;
231 : ucode_size = BONAIRE_SMC_UCODE_SIZE;
232 0 : break;
233 : case CHIP_HAWAII:
234 : ucode_start_address = HAWAII_SMC_UCODE_START;
235 : ucode_size = HAWAII_SMC_UCODE_SIZE;
236 0 : break;
237 : default:
238 0 : DRM_ERROR("unknown asic in smc ucode loader\n");
239 0 : BUG();
240 : }
241 :
242 0 : src = (const u8 *)rdev->smc_fw->data;
243 : }
244 :
245 0 : if (ucode_size & 3)
246 0 : return -EINVAL;
247 :
248 0 : spin_lock_irqsave(&rdev->smc_idx_lock, flags);
249 0 : WREG32(SMC_IND_INDEX_0, ucode_start_address);
250 0 : WREG32_P(SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, ~AUTO_INCREMENT_IND_0);
251 0 : while (ucode_size >= 4) {
252 : /* SMC address space is BE */
253 0 : data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
254 :
255 0 : WREG32(SMC_IND_DATA_0, data);
256 :
257 0 : src += 4;
258 0 : ucode_size -= 4;
259 : }
260 0 : WREG32_P(SMC_IND_ACCESS_CNTL, 0, ~AUTO_INCREMENT_IND_0);
261 0 : spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
262 :
263 0 : return 0;
264 0 : }
265 :
266 0 : int ci_read_smc_sram_dword(struct radeon_device *rdev,
267 : u32 smc_address, u32 *value, u32 limit)
268 : {
269 : unsigned long flags;
270 : int ret;
271 :
272 0 : spin_lock_irqsave(&rdev->smc_idx_lock, flags);
273 0 : ret = ci_set_smc_sram_address(rdev, smc_address, limit);
274 0 : if (ret == 0)
275 0 : *value = RREG32(SMC_IND_DATA_0);
276 0 : spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
277 :
278 0 : return ret;
279 : }
280 :
281 0 : int ci_write_smc_sram_dword(struct radeon_device *rdev,
282 : u32 smc_address, u32 value, u32 limit)
283 : {
284 : unsigned long flags;
285 : int ret;
286 :
287 0 : spin_lock_irqsave(&rdev->smc_idx_lock, flags);
288 0 : ret = ci_set_smc_sram_address(rdev, smc_address, limit);
289 0 : if (ret == 0)
290 0 : WREG32(SMC_IND_DATA_0, value);
291 0 : spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
292 :
293 0 : return ret;
294 : }
|