1 | /* |
2 | * Copyright 2017 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 | */ |
23 | /* |
24 | * dc_helper.c |
25 | * |
26 | * Created on: Aug 30, 2016 |
27 | * Author: agrodzov |
28 | */ |
29 | |
30 | #include <linux/delay.h> |
31 | #include <linux/stdarg.h> |
32 | |
33 | #include "dm_services.h" |
34 | |
35 | #include "dc.h" |
36 | #include "dc_dmub_srv.h" |
37 | #include "reg_helper.h" |
38 | |
39 | #define DC_LOGGER \ |
40 | ctx->logger |
41 | |
42 | static inline void submit_dmub_read_modify_write( |
43 | struct dc_reg_helper_state *offload, |
44 | const struct dc_context *ctx) |
45 | { |
46 | struct dmub_rb_cmd_read_modify_write *cmd_buf = &offload->cmd_data.read_modify_write; |
47 | |
48 | offload->should_burst_write = |
49 | (offload->same_addr_count == (DMUB_READ_MODIFY_WRITE_SEQ__MAX - 1)); |
50 | cmd_buf->header.payload_bytes = |
51 | sizeof(struct dmub_cmd_read_modify_write_sequence) * offload->reg_seq_count; |
52 | |
53 | dm_execute_dmub_cmd(ctx, cmd: &offload->cmd_data, wait_type: DM_DMUB_WAIT_TYPE_NO_WAIT); |
54 | |
55 | memset(cmd_buf, 0, sizeof(*cmd_buf)); |
56 | |
57 | offload->reg_seq_count = 0; |
58 | offload->same_addr_count = 0; |
59 | } |
60 | |
61 | static inline void submit_dmub_burst_write( |
62 | struct dc_reg_helper_state *offload, |
63 | const struct dc_context *ctx) |
64 | { |
65 | struct dmub_rb_cmd_burst_write *cmd_buf = &offload->cmd_data.burst_write; |
66 | |
67 | cmd_buf->header.payload_bytes = |
68 | sizeof(uint32_t) * offload->reg_seq_count; |
69 | |
70 | dm_execute_dmub_cmd(ctx, cmd: &offload->cmd_data, wait_type: DM_DMUB_WAIT_TYPE_NO_WAIT); |
71 | |
72 | memset(cmd_buf, 0, sizeof(*cmd_buf)); |
73 | |
74 | offload->reg_seq_count = 0; |
75 | } |
76 | |
77 | static inline void submit_dmub_reg_wait( |
78 | struct dc_reg_helper_state *offload, |
79 | const struct dc_context *ctx) |
80 | { |
81 | struct dmub_rb_cmd_reg_wait *cmd_buf = &offload->cmd_data.reg_wait; |
82 | |
83 | dm_execute_dmub_cmd(ctx, cmd: &offload->cmd_data, wait_type: DM_DMUB_WAIT_TYPE_NO_WAIT); |
84 | |
85 | memset(cmd_buf, 0, sizeof(*cmd_buf)); |
86 | offload->reg_seq_count = 0; |
87 | } |
88 | |
89 | struct dc_reg_value_masks { |
90 | uint32_t value; |
91 | uint32_t mask; |
92 | }; |
93 | |
94 | struct dc_reg_sequence { |
95 | uint32_t addr; |
96 | struct dc_reg_value_masks value_masks; |
97 | }; |
98 | |
99 | static inline void set_reg_field_value_masks( |
100 | struct dc_reg_value_masks *field_value_mask, |
101 | uint32_t value, |
102 | uint32_t mask, |
103 | uint8_t shift) |
104 | { |
105 | ASSERT(mask != 0); |
106 | |
107 | field_value_mask->value = (field_value_mask->value & ~mask) | (mask & (value << shift)); |
108 | field_value_mask->mask = field_value_mask->mask | mask; |
109 | } |
110 | |
111 | static void set_reg_field_values(struct dc_reg_value_masks *field_value_mask, |
112 | uint32_t addr, int n, |
113 | uint8_t shift1, uint32_t mask1, uint32_t field_value1, |
114 | va_list ap) |
115 | { |
116 | uint32_t shift, mask, field_value; |
117 | int i = 1; |
118 | |
119 | /* gather all bits value/mask getting updated in this register */ |
120 | set_reg_field_value_masks(field_value_mask, |
121 | value: field_value1, mask: mask1, shift: shift1); |
122 | |
123 | while (i < n) { |
124 | shift = va_arg(ap, uint32_t); |
125 | mask = va_arg(ap, uint32_t); |
126 | field_value = va_arg(ap, uint32_t); |
127 | |
128 | set_reg_field_value_masks(field_value_mask, |
129 | value: field_value, mask, shift); |
130 | i++; |
131 | } |
132 | } |
133 | |
134 | static void dmub_flush_buffer_execute( |
135 | struct dc_reg_helper_state *offload, |
136 | const struct dc_context *ctx) |
137 | { |
138 | submit_dmub_read_modify_write(offload, ctx); |
139 | } |
140 | |
141 | static void dmub_flush_burst_write_buffer_execute( |
142 | struct dc_reg_helper_state *offload, |
143 | const struct dc_context *ctx) |
144 | { |
145 | submit_dmub_burst_write(offload, ctx); |
146 | } |
147 | |
148 | static bool dmub_reg_value_burst_set_pack(const struct dc_context *ctx, uint32_t addr, |
149 | uint32_t reg_val) |
150 | { |
151 | struct dc_reg_helper_state *offload = &ctx->dmub_srv->reg_helper_offload; |
152 | struct dmub_rb_cmd_burst_write *cmd_buf = &offload->cmd_data.burst_write; |
153 | |
154 | /* flush command if buffer is full */ |
155 | if (offload->reg_seq_count == DMUB_BURST_WRITE_VALUES__MAX) |
156 | dmub_flush_burst_write_buffer_execute(offload, ctx); |
157 | |
158 | if (offload->cmd_data.cmd_common.header.type == DMUB_CMD__REG_SEQ_BURST_WRITE && |
159 | addr != cmd_buf->addr) { |
160 | dmub_flush_burst_write_buffer_execute(offload, ctx); |
161 | return false; |
162 | } |
163 | |
164 | cmd_buf->header.type = DMUB_CMD__REG_SEQ_BURST_WRITE; |
165 | cmd_buf->header.sub_type = 0; |
166 | cmd_buf->addr = addr; |
167 | cmd_buf->write_values[offload->reg_seq_count] = reg_val; |
168 | offload->reg_seq_count++; |
169 | |
170 | return true; |
171 | } |
172 | |
173 | static uint32_t dmub_reg_value_pack(const struct dc_context *ctx, uint32_t addr, |
174 | struct dc_reg_value_masks *field_value_mask) |
175 | { |
176 | struct dc_reg_helper_state *offload = &ctx->dmub_srv->reg_helper_offload; |
177 | struct dmub_rb_cmd_read_modify_write *cmd_buf = &offload->cmd_data.read_modify_write; |
178 | struct dmub_cmd_read_modify_write_sequence *seq; |
179 | |
180 | /* flush command if buffer is full */ |
181 | if (offload->cmd_data.cmd_common.header.type != DMUB_CMD__REG_SEQ_BURST_WRITE && |
182 | offload->reg_seq_count == DMUB_READ_MODIFY_WRITE_SEQ__MAX) |
183 | dmub_flush_buffer_execute(offload, ctx); |
184 | |
185 | if (offload->should_burst_write) { |
186 | if (dmub_reg_value_burst_set_pack(ctx, addr, reg_val: field_value_mask->value)) |
187 | return field_value_mask->value; |
188 | else |
189 | offload->should_burst_write = false; |
190 | } |
191 | |
192 | /* pack commands */ |
193 | cmd_buf->header.type = DMUB_CMD__REG_SEQ_READ_MODIFY_WRITE; |
194 | cmd_buf->header.sub_type = 0; |
195 | seq = &cmd_buf->seq[offload->reg_seq_count]; |
196 | |
197 | if (offload->reg_seq_count) { |
198 | if (cmd_buf->seq[offload->reg_seq_count - 1].addr == addr) |
199 | offload->same_addr_count++; |
200 | else |
201 | offload->same_addr_count = 0; |
202 | } |
203 | |
204 | seq->addr = addr; |
205 | seq->modify_mask = field_value_mask->mask; |
206 | seq->modify_value = field_value_mask->value; |
207 | offload->reg_seq_count++; |
208 | |
209 | return field_value_mask->value; |
210 | } |
211 | |
212 | static void dmub_reg_wait_done_pack(const struct dc_context *ctx, uint32_t addr, |
213 | uint32_t mask, uint32_t shift, uint32_t condition_value, uint32_t time_out_us) |
214 | { |
215 | struct dc_reg_helper_state *offload = &ctx->dmub_srv->reg_helper_offload; |
216 | struct dmub_rb_cmd_reg_wait *cmd_buf = &offload->cmd_data.reg_wait; |
217 | |
218 | cmd_buf->header.type = DMUB_CMD__REG_REG_WAIT; |
219 | cmd_buf->header.sub_type = 0; |
220 | cmd_buf->reg_wait.addr = addr; |
221 | cmd_buf->reg_wait.condition_field_value = mask & (condition_value << shift); |
222 | cmd_buf->reg_wait.mask = mask; |
223 | cmd_buf->reg_wait.time_out_us = time_out_us; |
224 | } |
225 | |
226 | uint32_t generic_reg_update_ex(const struct dc_context *ctx, |
227 | uint32_t addr, int n, |
228 | uint8_t shift1, uint32_t mask1, uint32_t field_value1, |
229 | ...) |
230 | { |
231 | struct dc_reg_value_masks field_value_mask = {0}; |
232 | uint32_t reg_val; |
233 | va_list ap; |
234 | |
235 | va_start(ap, field_value1); |
236 | |
237 | set_reg_field_values(field_value_mask: &field_value_mask, addr, n, shift1, mask1, |
238 | field_value1, ap); |
239 | |
240 | va_end(ap); |
241 | |
242 | if (ctx->dmub_srv && |
243 | ctx->dmub_srv->reg_helper_offload.gather_in_progress) |
244 | return dmub_reg_value_pack(ctx, addr, field_value_mask: &field_value_mask); |
245 | /* todo: return void so we can decouple code running in driver from register states */ |
246 | |
247 | /* mmio write directly */ |
248 | reg_val = dm_read_reg(ctx, addr); |
249 | reg_val = (reg_val & ~field_value_mask.mask) | field_value_mask.value; |
250 | dm_write_reg(ctx, addr, reg_val); |
251 | return reg_val; |
252 | } |
253 | |
254 | uint32_t generic_reg_set_ex(const struct dc_context *ctx, |
255 | uint32_t addr, uint32_t reg_val, int n, |
256 | uint8_t shift1, uint32_t mask1, uint32_t field_value1, |
257 | ...) |
258 | { |
259 | struct dc_reg_value_masks field_value_mask = {0}; |
260 | va_list ap; |
261 | |
262 | va_start(ap, field_value1); |
263 | |
264 | set_reg_field_values(field_value_mask: &field_value_mask, addr, n, shift1, mask1, |
265 | field_value1, ap); |
266 | |
267 | va_end(ap); |
268 | |
269 | |
270 | /* mmio write directly */ |
271 | reg_val = (reg_val & ~field_value_mask.mask) | field_value_mask.value; |
272 | |
273 | if (ctx->dmub_srv && |
274 | ctx->dmub_srv->reg_helper_offload.gather_in_progress) { |
275 | return dmub_reg_value_burst_set_pack(ctx, addr, reg_val); |
276 | /* todo: return void so we can decouple code running in driver from register states */ |
277 | } |
278 | |
279 | dm_write_reg(ctx, addr, reg_val); |
280 | return reg_val; |
281 | } |
282 | |
283 | uint32_t generic_reg_get(const struct dc_context *ctx, uint32_t addr, |
284 | uint8_t shift, uint32_t mask, uint32_t *field_value) |
285 | { |
286 | uint32_t reg_val = dm_read_reg(ctx, addr); |
287 | *field_value = get_reg_field_value_ex(reg_value: reg_val, mask, shift); |
288 | return reg_val; |
289 | } |
290 | |
291 | uint32_t generic_reg_get2(const struct dc_context *ctx, uint32_t addr, |
292 | uint8_t shift1, uint32_t mask1, uint32_t *field_value1, |
293 | uint8_t shift2, uint32_t mask2, uint32_t *field_value2) |
294 | { |
295 | uint32_t reg_val = dm_read_reg(ctx, addr); |
296 | *field_value1 = get_reg_field_value_ex(reg_value: reg_val, mask: mask1, shift: shift1); |
297 | *field_value2 = get_reg_field_value_ex(reg_value: reg_val, mask: mask2, shift: shift2); |
298 | return reg_val; |
299 | } |
300 | |
301 | uint32_t generic_reg_get3(const struct dc_context *ctx, uint32_t addr, |
302 | uint8_t shift1, uint32_t mask1, uint32_t *field_value1, |
303 | uint8_t shift2, uint32_t mask2, uint32_t *field_value2, |
304 | uint8_t shift3, uint32_t mask3, uint32_t *field_value3) |
305 | { |
306 | uint32_t reg_val = dm_read_reg(ctx, addr); |
307 | *field_value1 = get_reg_field_value_ex(reg_value: reg_val, mask: mask1, shift: shift1); |
308 | *field_value2 = get_reg_field_value_ex(reg_value: reg_val, mask: mask2, shift: shift2); |
309 | *field_value3 = get_reg_field_value_ex(reg_value: reg_val, mask: mask3, shift: shift3); |
310 | return reg_val; |
311 | } |
312 | |
313 | uint32_t generic_reg_get4(const struct dc_context *ctx, uint32_t addr, |
314 | uint8_t shift1, uint32_t mask1, uint32_t *field_value1, |
315 | uint8_t shift2, uint32_t mask2, uint32_t *field_value2, |
316 | uint8_t shift3, uint32_t mask3, uint32_t *field_value3, |
317 | uint8_t shift4, uint32_t mask4, uint32_t *field_value4) |
318 | { |
319 | uint32_t reg_val = dm_read_reg(ctx, addr); |
320 | *field_value1 = get_reg_field_value_ex(reg_value: reg_val, mask: mask1, shift: shift1); |
321 | *field_value2 = get_reg_field_value_ex(reg_value: reg_val, mask: mask2, shift: shift2); |
322 | *field_value3 = get_reg_field_value_ex(reg_value: reg_val, mask: mask3, shift: shift3); |
323 | *field_value4 = get_reg_field_value_ex(reg_value: reg_val, mask: mask4, shift: shift4); |
324 | return reg_val; |
325 | } |
326 | |
327 | uint32_t generic_reg_get5(const struct dc_context *ctx, uint32_t addr, |
328 | uint8_t shift1, uint32_t mask1, uint32_t *field_value1, |
329 | uint8_t shift2, uint32_t mask2, uint32_t *field_value2, |
330 | uint8_t shift3, uint32_t mask3, uint32_t *field_value3, |
331 | uint8_t shift4, uint32_t mask4, uint32_t *field_value4, |
332 | uint8_t shift5, uint32_t mask5, uint32_t *field_value5) |
333 | { |
334 | uint32_t reg_val = dm_read_reg(ctx, addr); |
335 | *field_value1 = get_reg_field_value_ex(reg_value: reg_val, mask: mask1, shift: shift1); |
336 | *field_value2 = get_reg_field_value_ex(reg_value: reg_val, mask: mask2, shift: shift2); |
337 | *field_value3 = get_reg_field_value_ex(reg_value: reg_val, mask: mask3, shift: shift3); |
338 | *field_value4 = get_reg_field_value_ex(reg_value: reg_val, mask: mask4, shift: shift4); |
339 | *field_value5 = get_reg_field_value_ex(reg_value: reg_val, mask: mask5, shift: shift5); |
340 | return reg_val; |
341 | } |
342 | |
343 | uint32_t generic_reg_get6(const struct dc_context *ctx, uint32_t addr, |
344 | uint8_t shift1, uint32_t mask1, uint32_t *field_value1, |
345 | uint8_t shift2, uint32_t mask2, uint32_t *field_value2, |
346 | uint8_t shift3, uint32_t mask3, uint32_t *field_value3, |
347 | uint8_t shift4, uint32_t mask4, uint32_t *field_value4, |
348 | uint8_t shift5, uint32_t mask5, uint32_t *field_value5, |
349 | uint8_t shift6, uint32_t mask6, uint32_t *field_value6) |
350 | { |
351 | uint32_t reg_val = dm_read_reg(ctx, addr); |
352 | *field_value1 = get_reg_field_value_ex(reg_value: reg_val, mask: mask1, shift: shift1); |
353 | *field_value2 = get_reg_field_value_ex(reg_value: reg_val, mask: mask2, shift: shift2); |
354 | *field_value3 = get_reg_field_value_ex(reg_value: reg_val, mask: mask3, shift: shift3); |
355 | *field_value4 = get_reg_field_value_ex(reg_value: reg_val, mask: mask4, shift: shift4); |
356 | *field_value5 = get_reg_field_value_ex(reg_value: reg_val, mask: mask5, shift: shift5); |
357 | *field_value6 = get_reg_field_value_ex(reg_value: reg_val, mask: mask6, shift: shift6); |
358 | return reg_val; |
359 | } |
360 | |
361 | uint32_t generic_reg_get7(const struct dc_context *ctx, uint32_t addr, |
362 | uint8_t shift1, uint32_t mask1, uint32_t *field_value1, |
363 | uint8_t shift2, uint32_t mask2, uint32_t *field_value2, |
364 | uint8_t shift3, uint32_t mask3, uint32_t *field_value3, |
365 | uint8_t shift4, uint32_t mask4, uint32_t *field_value4, |
366 | uint8_t shift5, uint32_t mask5, uint32_t *field_value5, |
367 | uint8_t shift6, uint32_t mask6, uint32_t *field_value6, |
368 | uint8_t shift7, uint32_t mask7, uint32_t *field_value7) |
369 | { |
370 | uint32_t reg_val = dm_read_reg(ctx, addr); |
371 | *field_value1 = get_reg_field_value_ex(reg_value: reg_val, mask: mask1, shift: shift1); |
372 | *field_value2 = get_reg_field_value_ex(reg_value: reg_val, mask: mask2, shift: shift2); |
373 | *field_value3 = get_reg_field_value_ex(reg_value: reg_val, mask: mask3, shift: shift3); |
374 | *field_value4 = get_reg_field_value_ex(reg_value: reg_val, mask: mask4, shift: shift4); |
375 | *field_value5 = get_reg_field_value_ex(reg_value: reg_val, mask: mask5, shift: shift5); |
376 | *field_value6 = get_reg_field_value_ex(reg_value: reg_val, mask: mask6, shift: shift6); |
377 | *field_value7 = get_reg_field_value_ex(reg_value: reg_val, mask: mask7, shift: shift7); |
378 | return reg_val; |
379 | } |
380 | |
381 | uint32_t generic_reg_get8(const struct dc_context *ctx, uint32_t addr, |
382 | uint8_t shift1, uint32_t mask1, uint32_t *field_value1, |
383 | uint8_t shift2, uint32_t mask2, uint32_t *field_value2, |
384 | uint8_t shift3, uint32_t mask3, uint32_t *field_value3, |
385 | uint8_t shift4, uint32_t mask4, uint32_t *field_value4, |
386 | uint8_t shift5, uint32_t mask5, uint32_t *field_value5, |
387 | uint8_t shift6, uint32_t mask6, uint32_t *field_value6, |
388 | uint8_t shift7, uint32_t mask7, uint32_t *field_value7, |
389 | uint8_t shift8, uint32_t mask8, uint32_t *field_value8) |
390 | { |
391 | uint32_t reg_val = dm_read_reg(ctx, addr); |
392 | *field_value1 = get_reg_field_value_ex(reg_value: reg_val, mask: mask1, shift: shift1); |
393 | *field_value2 = get_reg_field_value_ex(reg_value: reg_val, mask: mask2, shift: shift2); |
394 | *field_value3 = get_reg_field_value_ex(reg_value: reg_val, mask: mask3, shift: shift3); |
395 | *field_value4 = get_reg_field_value_ex(reg_value: reg_val, mask: mask4, shift: shift4); |
396 | *field_value5 = get_reg_field_value_ex(reg_value: reg_val, mask: mask5, shift: shift5); |
397 | *field_value6 = get_reg_field_value_ex(reg_value: reg_val, mask: mask6, shift: shift6); |
398 | *field_value7 = get_reg_field_value_ex(reg_value: reg_val, mask: mask7, shift: shift7); |
399 | *field_value8 = get_reg_field_value_ex(reg_value: reg_val, mask: mask8, shift: shift8); |
400 | return reg_val; |
401 | } |
402 | /* note: va version of this is pretty bad idea, since there is a output parameter pass by pointer |
403 | * compiler won't be able to check for size match and is prone to stack corruption type of bugs |
404 | |
405 | uint32_t generic_reg_get(const struct dc_context *ctx, |
406 | uint32_t addr, int n, ...) |
407 | { |
408 | uint32_t shift, mask; |
409 | uint32_t *field_value; |
410 | uint32_t reg_val; |
411 | int i = 0; |
412 | |
413 | reg_val = dm_read_reg(ctx, addr); |
414 | |
415 | va_list ap; |
416 | va_start(ap, n); |
417 | |
418 | while (i < n) { |
419 | shift = va_arg(ap, uint32_t); |
420 | mask = va_arg(ap, uint32_t); |
421 | field_value = va_arg(ap, uint32_t *); |
422 | |
423 | *field_value = get_reg_field_value_ex(reg_val, mask, shift); |
424 | i++; |
425 | } |
426 | |
427 | va_end(ap); |
428 | |
429 | return reg_val; |
430 | } |
431 | */ |
432 | |
433 | void generic_reg_wait(const struct dc_context *ctx, |
434 | uint32_t addr, uint32_t shift, uint32_t mask, uint32_t condition_value, |
435 | unsigned int delay_between_poll_us, unsigned int time_out_num_tries, |
436 | const char *func_name, int line) |
437 | { |
438 | uint32_t field_value; |
439 | uint32_t reg_val; |
440 | int i; |
441 | |
442 | if (ctx->dmub_srv && |
443 | ctx->dmub_srv->reg_helper_offload.gather_in_progress) { |
444 | dmub_reg_wait_done_pack(ctx, addr, mask, shift, condition_value, |
445 | time_out_us: delay_between_poll_us * time_out_num_tries); |
446 | return; |
447 | } |
448 | |
449 | /* |
450 | * Something is terribly wrong if time out is > 3000ms. |
451 | * 3000ms is the maximum time needed for SMU to pass values back. |
452 | * This value comes from experiments. |
453 | * |
454 | */ |
455 | ASSERT(delay_between_poll_us * time_out_num_tries <= 3000000); |
456 | |
457 | for (i = 0; i <= time_out_num_tries; i++) { |
458 | if (i) { |
459 | if (delay_between_poll_us >= 1000) |
460 | msleep(msecs: delay_between_poll_us/1000); |
461 | else if (delay_between_poll_us > 0) |
462 | udelay(delay_between_poll_us); |
463 | } |
464 | |
465 | reg_val = dm_read_reg(ctx, addr); |
466 | |
467 | field_value = get_reg_field_value_ex(reg_value: reg_val, mask, shift); |
468 | |
469 | if (field_value == condition_value) { |
470 | if (i * delay_between_poll_us > 1000) |
471 | DC_LOG_DC("REG_WAIT taking a while: %dms in %s line:%d\n" , |
472 | delay_between_poll_us * i / 1000, |
473 | func_name, line); |
474 | return; |
475 | } |
476 | } |
477 | |
478 | DC_LOG_WARNING("REG_WAIT timeout %dus * %d tries - %s line:%d\n" , |
479 | delay_between_poll_us, time_out_num_tries, |
480 | func_name, line); |
481 | |
482 | BREAK_TO_DEBUGGER(); |
483 | } |
484 | |
485 | void generic_write_indirect_reg(const struct dc_context *ctx, |
486 | uint32_t addr_index, uint32_t addr_data, |
487 | uint32_t index, uint32_t data) |
488 | { |
489 | dm_write_reg(ctx, addr_index, index); |
490 | dm_write_reg(ctx, addr_data, data); |
491 | } |
492 | |
493 | uint32_t generic_read_indirect_reg(const struct dc_context *ctx, |
494 | uint32_t addr_index, uint32_t addr_data, |
495 | uint32_t index) |
496 | { |
497 | uint32_t value = 0; |
498 | |
499 | // when reg read, there should not be any offload. |
500 | if (ctx->dmub_srv && |
501 | ctx->dmub_srv->reg_helper_offload.gather_in_progress) { |
502 | ASSERT(false); |
503 | } |
504 | |
505 | dm_write_reg(ctx, addr_index, index); |
506 | value = dm_read_reg(ctx, addr_data); |
507 | |
508 | return value; |
509 | } |
510 | |
511 | uint32_t generic_indirect_reg_get(const struct dc_context *ctx, |
512 | uint32_t addr_index, uint32_t addr_data, |
513 | uint32_t index, int n, |
514 | uint8_t shift1, uint32_t mask1, uint32_t *field_value1, |
515 | ...) |
516 | { |
517 | uint32_t shift, mask, *field_value; |
518 | uint32_t value = 0; |
519 | int i = 1; |
520 | |
521 | va_list ap; |
522 | |
523 | va_start(ap, field_value1); |
524 | |
525 | value = generic_read_indirect_reg(ctx, addr_index, addr_data, index); |
526 | *field_value1 = get_reg_field_value_ex(reg_value: value, mask: mask1, shift: shift1); |
527 | |
528 | while (i < n) { |
529 | shift = va_arg(ap, uint32_t); |
530 | mask = va_arg(ap, uint32_t); |
531 | field_value = va_arg(ap, uint32_t *); |
532 | |
533 | *field_value = get_reg_field_value_ex(reg_value: value, mask, shift); |
534 | i++; |
535 | } |
536 | |
537 | va_end(ap); |
538 | |
539 | return value; |
540 | } |
541 | |
542 | uint32_t generic_indirect_reg_update_ex(const struct dc_context *ctx, |
543 | uint32_t addr_index, uint32_t addr_data, |
544 | uint32_t index, uint32_t reg_val, int n, |
545 | uint8_t shift1, uint32_t mask1, uint32_t field_value1, |
546 | ...) |
547 | { |
548 | uint32_t shift, mask, field_value; |
549 | int i = 1; |
550 | |
551 | va_list ap; |
552 | |
553 | va_start(ap, field_value1); |
554 | |
555 | reg_val = set_reg_field_value_ex(reg_value: reg_val, value: field_value1, mask: mask1, shift: shift1); |
556 | |
557 | while (i < n) { |
558 | shift = va_arg(ap, uint32_t); |
559 | mask = va_arg(ap, uint32_t); |
560 | field_value = va_arg(ap, uint32_t); |
561 | |
562 | reg_val = set_reg_field_value_ex(reg_value: reg_val, value: field_value, mask, shift); |
563 | i++; |
564 | } |
565 | |
566 | generic_write_indirect_reg(ctx, addr_index, addr_data, index, data: reg_val); |
567 | va_end(ap); |
568 | |
569 | return reg_val; |
570 | } |
571 | |
572 | |
573 | uint32_t generic_indirect_reg_update_ex_sync(const struct dc_context *ctx, |
574 | uint32_t index, uint32_t reg_val, int n, |
575 | uint8_t shift1, uint32_t mask1, uint32_t field_value1, |
576 | ...) |
577 | { |
578 | uint32_t shift, mask, field_value; |
579 | int i = 1; |
580 | |
581 | va_list ap; |
582 | |
583 | va_start(ap, field_value1); |
584 | |
585 | reg_val = set_reg_field_value_ex(reg_value: reg_val, value: field_value1, mask: mask1, shift: shift1); |
586 | |
587 | while (i < n) { |
588 | shift = va_arg(ap, uint32_t); |
589 | mask = va_arg(ap, uint32_t); |
590 | field_value = va_arg(ap, uint32_t); |
591 | |
592 | reg_val = set_reg_field_value_ex(reg_value: reg_val, value: field_value, mask, shift); |
593 | i++; |
594 | } |
595 | |
596 | dm_write_index_reg(ctx, addr_space: CGS_IND_REG__PCIE, index, value: reg_val); |
597 | va_end(ap); |
598 | |
599 | return reg_val; |
600 | } |
601 | |
602 | uint32_t generic_indirect_reg_get_sync(const struct dc_context *ctx, |
603 | uint32_t index, int n, |
604 | uint8_t shift1, uint32_t mask1, uint32_t *field_value1, |
605 | ...) |
606 | { |
607 | uint32_t shift, mask, *field_value; |
608 | uint32_t value = 0; |
609 | int i = 1; |
610 | |
611 | va_list ap; |
612 | |
613 | va_start(ap, field_value1); |
614 | |
615 | value = dm_read_index_reg(ctx, addr_space: CGS_IND_REG__PCIE, index); |
616 | *field_value1 = get_reg_field_value_ex(reg_value: value, mask: mask1, shift: shift1); |
617 | |
618 | while (i < n) { |
619 | shift = va_arg(ap, uint32_t); |
620 | mask = va_arg(ap, uint32_t); |
621 | field_value = va_arg(ap, uint32_t *); |
622 | |
623 | *field_value = get_reg_field_value_ex(reg_value: value, mask, shift); |
624 | i++; |
625 | } |
626 | |
627 | va_end(ap); |
628 | |
629 | return value; |
630 | } |
631 | |
632 | void reg_sequence_start_gather(const struct dc_context *ctx) |
633 | { |
634 | /* if reg sequence is supported and enabled, set flag to |
635 | * indicate we want to have REG_SET, REG_UPDATE macro build |
636 | * reg sequence command buffer rather than MMIO directly. |
637 | */ |
638 | |
639 | if (ctx->dmub_srv && ctx->dc->debug.dmub_offload_enabled) { |
640 | struct dc_reg_helper_state *offload = |
641 | &ctx->dmub_srv->reg_helper_offload; |
642 | |
643 | /* caller sequence mismatch. need to debug caller. offload will not work!!! */ |
644 | ASSERT(!offload->gather_in_progress); |
645 | |
646 | offload->gather_in_progress = true; |
647 | } |
648 | } |
649 | |
650 | void reg_sequence_start_execute(const struct dc_context *ctx) |
651 | { |
652 | struct dc_reg_helper_state *offload; |
653 | |
654 | if (!ctx->dmub_srv) |
655 | return; |
656 | |
657 | offload = &ctx->dmub_srv->reg_helper_offload; |
658 | |
659 | if (offload && offload->gather_in_progress) { |
660 | offload->gather_in_progress = false; |
661 | offload->should_burst_write = false; |
662 | switch (offload->cmd_data.cmd_common.header.type) { |
663 | case DMUB_CMD__REG_SEQ_READ_MODIFY_WRITE: |
664 | submit_dmub_read_modify_write(offload, ctx); |
665 | break; |
666 | case DMUB_CMD__REG_REG_WAIT: |
667 | submit_dmub_reg_wait(offload, ctx); |
668 | break; |
669 | case DMUB_CMD__REG_SEQ_BURST_WRITE: |
670 | submit_dmub_burst_write(offload, ctx); |
671 | break; |
672 | default: |
673 | return; |
674 | } |
675 | } |
676 | } |
677 | |
678 | void reg_sequence_wait_done(const struct dc_context *ctx) |
679 | { |
680 | /* callback to DM to poll for last submission done*/ |
681 | struct dc_reg_helper_state *offload; |
682 | |
683 | if (!ctx->dmub_srv) |
684 | return; |
685 | |
686 | offload = &ctx->dmub_srv->reg_helper_offload; |
687 | |
688 | if (offload && |
689 | ctx->dc->debug.dmub_offload_enabled && |
690 | !ctx->dc->debug.dmcub_emulation) { |
691 | dc_dmub_srv_wait_idle(dc_dmub_srv: ctx->dmub_srv); |
692 | } |
693 | } |
694 | |
695 | char *dce_version_to_string(const int version) |
696 | { |
697 | switch (version) { |
698 | case DCE_VERSION_8_0: |
699 | return "DCE 8.0" ; |
700 | case DCE_VERSION_8_1: |
701 | return "DCE 8.1" ; |
702 | case DCE_VERSION_8_3: |
703 | return "DCE 8.3" ; |
704 | case DCE_VERSION_10_0: |
705 | return "DCE 10.0" ; |
706 | case DCE_VERSION_11_0: |
707 | return "DCE 11.0" ; |
708 | case DCE_VERSION_11_2: |
709 | return "DCE 11.2" ; |
710 | case DCE_VERSION_11_22: |
711 | return "DCE 11.22" ; |
712 | case DCE_VERSION_12_0: |
713 | return "DCE 12.0" ; |
714 | case DCE_VERSION_12_1: |
715 | return "DCE 12.1" ; |
716 | case DCN_VERSION_1_0: |
717 | return "DCN 1.0" ; |
718 | case DCN_VERSION_1_01: |
719 | return "DCN 1.0.1" ; |
720 | case DCN_VERSION_2_0: |
721 | return "DCN 2.0" ; |
722 | case DCN_VERSION_2_1: |
723 | return "DCN 2.1" ; |
724 | case DCN_VERSION_2_01: |
725 | return "DCN 2.0.1" ; |
726 | case DCN_VERSION_3_0: |
727 | return "DCN 3.0" ; |
728 | case DCN_VERSION_3_01: |
729 | return "DCN 3.0.1" ; |
730 | case DCN_VERSION_3_02: |
731 | return "DCN 3.0.2" ; |
732 | case DCN_VERSION_3_03: |
733 | return "DCN 3.0.3" ; |
734 | case DCN_VERSION_3_1: |
735 | return "DCN 3.1" ; |
736 | case DCN_VERSION_3_14: |
737 | return "DCN 3.1.4" ; |
738 | case DCN_VERSION_3_15: |
739 | return "DCN 3.1.5" ; |
740 | case DCN_VERSION_3_16: |
741 | return "DCN 3.1.6" ; |
742 | case DCN_VERSION_3_2: |
743 | return "DCN 3.2" ; |
744 | case DCN_VERSION_3_21: |
745 | return "DCN 3.2.1" ; |
746 | case DCN_VERSION_3_5: |
747 | return "DCN 3.5" ; |
748 | case DCN_VERSION_3_51: |
749 | return "DCN 3.5.1" ; |
750 | default: |
751 | return "Unknown" ; |
752 | } |
753 | } |
754 | |