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