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