1 | /* |
2 | * Copyright 2021 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: AMD |
23 | * |
24 | */ |
25 | |
26 | #include "dcn32_clk_mgr_smu_msg.h" |
27 | |
28 | #include "clk_mgr_internal.h" |
29 | #include "reg_helper.h" |
30 | #include "dalsmc.h" |
31 | #include "smu13_driver_if.h" |
32 | |
33 | #define mmDAL_MSG_REG 0x1628A |
34 | #define mmDAL_ARG_REG 0x16273 |
35 | #define mmDAL_RESP_REG 0x16274 |
36 | |
37 | #define REG(reg_name) \ |
38 | mm ## reg_name |
39 | |
40 | #include "logger_types.h" |
41 | |
42 | #define smu_print(str, ...) {DC_LOG_SMU(str, ##__VA_ARGS__); } |
43 | |
44 | |
45 | /* |
46 | * Function to be used instead of REG_WAIT macro because the wait ends when |
47 | * the register is NOT EQUAL to zero, and because the translation in msg_if.h |
48 | * won't work with REG_WAIT. |
49 | */ |
50 | static uint32_t dcn32_smu_wait_for_response(struct clk_mgr_internal *clk_mgr, unsigned int delay_us, unsigned int max_retries) |
51 | { |
52 | uint32_t reg = 0; |
53 | |
54 | do { |
55 | reg = REG_READ(DAL_RESP_REG); |
56 | if (reg) |
57 | break; |
58 | |
59 | if (delay_us >= 1000) |
60 | msleep(msecs: delay_us/1000); |
61 | else if (delay_us > 0) |
62 | udelay(delay_us); |
63 | } while (max_retries--); |
64 | |
65 | return reg; |
66 | } |
67 | |
68 | static bool dcn32_smu_send_msg_with_param(struct clk_mgr_internal *clk_mgr, uint32_t msg_id, uint32_t param_in, uint32_t *param_out) |
69 | { |
70 | /* Wait for response register to be ready */ |
71 | dcn32_smu_wait_for_response(clk_mgr, delay_us: 10, max_retries: 200000); |
72 | |
73 | /* Clear response register */ |
74 | REG_WRITE(DAL_RESP_REG, 0); |
75 | |
76 | /* Set the parameter register for the SMU message */ |
77 | REG_WRITE(DAL_ARG_REG, param_in); |
78 | |
79 | /* Trigger the message transaction by writing the message ID */ |
80 | REG_WRITE(DAL_MSG_REG, msg_id); |
81 | |
82 | /* Wait for response */ |
83 | if (dcn32_smu_wait_for_response(clk_mgr, delay_us: 10, max_retries: 200000) == DALSMC_Result_OK) { |
84 | if (param_out) |
85 | *param_out = REG_READ(DAL_ARG_REG); |
86 | |
87 | return true; |
88 | } |
89 | |
90 | return false; |
91 | } |
92 | |
93 | /* |
94 | * Use these functions to return back delay information so we can aggregate the total |
95 | * delay when requesting hardmin clk |
96 | * |
97 | * dcn32_smu_wait_for_response_delay |
98 | * dcn32_smu_send_msg_with_param_delay |
99 | * |
100 | */ |
101 | static uint32_t dcn32_smu_wait_for_response_delay(struct clk_mgr_internal *clk_mgr, unsigned int delay_us, unsigned int max_retries, unsigned int *total_delay_us) |
102 | { |
103 | uint32_t reg = 0; |
104 | *total_delay_us = 0; |
105 | |
106 | do { |
107 | reg = REG_READ(DAL_RESP_REG); |
108 | if (reg) |
109 | break; |
110 | |
111 | if (delay_us >= 1000) |
112 | msleep(msecs: delay_us/1000); |
113 | else if (delay_us > 0) |
114 | udelay(delay_us); |
115 | *total_delay_us += delay_us; |
116 | } while (max_retries--); |
117 | |
118 | return reg; |
119 | } |
120 | |
121 | static bool dcn32_smu_send_msg_with_param_delay(struct clk_mgr_internal *clk_mgr, uint32_t msg_id, uint32_t param_in, uint32_t *param_out, unsigned int *total_delay_us) |
122 | { |
123 | unsigned int delay1_us, delay2_us; |
124 | *total_delay_us = 0; |
125 | |
126 | /* Wait for response register to be ready */ |
127 | dcn32_smu_wait_for_response_delay(clk_mgr, delay_us: 10, max_retries: 200000, total_delay_us: &delay1_us); |
128 | |
129 | /* Clear response register */ |
130 | REG_WRITE(DAL_RESP_REG, 0); |
131 | |
132 | /* Set the parameter register for the SMU message */ |
133 | REG_WRITE(DAL_ARG_REG, param_in); |
134 | |
135 | /* Trigger the message transaction by writing the message ID */ |
136 | REG_WRITE(DAL_MSG_REG, msg_id); |
137 | |
138 | /* Wait for response */ |
139 | if (dcn32_smu_wait_for_response_delay(clk_mgr, delay_us: 10, max_retries: 200000, total_delay_us: &delay2_us) == DALSMC_Result_OK) { |
140 | if (param_out) |
141 | *param_out = REG_READ(DAL_ARG_REG); |
142 | |
143 | *total_delay_us = delay1_us + delay2_us; |
144 | return true; |
145 | } |
146 | |
147 | *total_delay_us = delay1_us + 2000000; |
148 | return false; |
149 | } |
150 | |
151 | void dcn32_smu_send_fclk_pstate_message(struct clk_mgr_internal *clk_mgr, bool enable) |
152 | { |
153 | smu_print("FCLK P-state support value is : %d\n" , enable); |
154 | |
155 | dcn32_smu_send_msg_with_param(clk_mgr, |
156 | DALSMC_MSG_SetFclkSwitchAllow, param_in: enable ? FCLK_PSTATE_SUPPORTED : FCLK_PSTATE_NOTSUPPORTED, NULL); |
157 | } |
158 | |
159 | void dcn32_smu_send_cab_for_uclk_message(struct clk_mgr_internal *clk_mgr, unsigned int num_ways) |
160 | { |
161 | uint32_t param = (num_ways << 1) | (num_ways > 0); |
162 | |
163 | dcn32_smu_send_msg_with_param(clk_mgr, DALSMC_MSG_SetCabForUclkPstate, param_in: param, NULL); |
164 | smu_print("Numways for SubVP : %d\n" , num_ways); |
165 | } |
166 | |
167 | void dcn32_smu_transfer_wm_table_dram_2_smu(struct clk_mgr_internal *clk_mgr) |
168 | { |
169 | smu_print("SMU Transfer WM table DRAM 2 SMU\n" ); |
170 | |
171 | dcn32_smu_send_msg_with_param(clk_mgr, |
172 | DALSMC_MSG_TransferTableDram2Smu, TABLE_WATERMARKS, NULL); |
173 | } |
174 | |
175 | void dcn32_smu_set_pme_workaround(struct clk_mgr_internal *clk_mgr) |
176 | { |
177 | smu_print("SMU Set PME workaround\n" ); |
178 | |
179 | dcn32_smu_send_msg_with_param(clk_mgr, |
180 | DALSMC_MSG_BacoAudioD3PME, param_in: 0, NULL); |
181 | } |
182 | |
183 | /* Check PMFW version if it supports ReturnHardMinStatus message */ |
184 | static bool dcn32_get_hard_min_status_supported(struct clk_mgr_internal *clk_mgr) |
185 | { |
186 | if (ASICREV_IS_GC_11_0_0(clk_mgr->base.ctx->asic_id.hw_internal_rev)) { |
187 | if (clk_mgr->smu_ver >= 0x4e6a00) |
188 | return true; |
189 | } else if (ASICREV_IS_GC_11_0_2(clk_mgr->base.ctx->asic_id.hw_internal_rev)) { |
190 | if (clk_mgr->smu_ver >= 0x524e00) |
191 | return true; |
192 | } else { /* ASICREV_IS_GC_11_0_3 */ |
193 | if (clk_mgr->smu_ver >= 0x503900) |
194 | return true; |
195 | } |
196 | return false; |
197 | } |
198 | |
199 | /* Returns the clocks which were fulfilled by the DAL hard min arbiter in PMFW */ |
200 | static unsigned int dcn32_smu_get_hard_min_status(struct clk_mgr_internal *clk_mgr, bool *no_timeout, unsigned int *total_delay_us) |
201 | { |
202 | uint32_t response = 0; |
203 | |
204 | /* bits 23:16 for clock type, lower 16 bits for frequency in MHz */ |
205 | uint32_t param = 0; |
206 | |
207 | *no_timeout = dcn32_smu_send_msg_with_param_delay(clk_mgr, |
208 | DALSMC_MSG_ReturnHardMinStatus, param_in: param, param_out: &response, total_delay_us); |
209 | |
210 | smu_print("SMU Get hard min status: no_timeout %d delay %d us clk bits %x\n" , |
211 | *no_timeout, *total_delay_us, response); |
212 | |
213 | return response; |
214 | } |
215 | |
216 | static bool dcn32_smu_wait_get_hard_min_status(struct clk_mgr_internal *clk_mgr, |
217 | uint32_t clk) |
218 | { |
219 | int readDalHardMinClkBits, checkDalHardMinClkBits; |
220 | unsigned int total_delay_us, read_total_delay_us; |
221 | bool no_timeout, hard_min_done; |
222 | |
223 | static unsigned int cur_wait_get_hard_min_max_us; |
224 | static unsigned int cur_wait_get_hard_min_max_timeouts; |
225 | |
226 | checkDalHardMinClkBits = CHECK_HARD_MIN_CLK_DPREFCLK; |
227 | if (clk == PPCLK_DISPCLK) |
228 | checkDalHardMinClkBits |= CHECK_HARD_MIN_CLK_DISPCLK; |
229 | if (clk == PPCLK_DPPCLK) |
230 | checkDalHardMinClkBits |= CHECK_HARD_MIN_CLK_DPPCLK; |
231 | if (clk == PPCLK_DCFCLK) |
232 | checkDalHardMinClkBits |= CHECK_HARD_MIN_CLK_DCFCLK; |
233 | if (clk == PPCLK_DTBCLK) |
234 | checkDalHardMinClkBits |= CHECK_HARD_MIN_CLK_DTBCLK; |
235 | if (clk == PPCLK_UCLK) |
236 | checkDalHardMinClkBits |= CHECK_HARD_MIN_CLK_UCLK; |
237 | |
238 | if (checkDalHardMinClkBits == CHECK_HARD_MIN_CLK_DPREFCLK) |
239 | return 0; |
240 | |
241 | total_delay_us = 0; |
242 | hard_min_done = false; |
243 | while (1) { |
244 | readDalHardMinClkBits = dcn32_smu_get_hard_min_status(clk_mgr, no_timeout: &no_timeout, total_delay_us: &read_total_delay_us); |
245 | total_delay_us += read_total_delay_us; |
246 | if (checkDalHardMinClkBits == (readDalHardMinClkBits & checkDalHardMinClkBits)) { |
247 | hard_min_done = true; |
248 | break; |
249 | } |
250 | |
251 | |
252 | if (total_delay_us >= 2000000) { |
253 | cur_wait_get_hard_min_max_timeouts++; |
254 | smu_print("SMU Wait get hard min status: %d timeouts\n" , cur_wait_get_hard_min_max_timeouts); |
255 | break; |
256 | } |
257 | msleep(msecs: 1); |
258 | total_delay_us += 1000; |
259 | } |
260 | |
261 | if (total_delay_us > cur_wait_get_hard_min_max_us) |
262 | cur_wait_get_hard_min_max_us = total_delay_us; |
263 | |
264 | smu_print("SMU Wait get hard min status: no_timeout %d, delay %d us, max %d us, read %x, check %x\n" , |
265 | no_timeout, total_delay_us, cur_wait_get_hard_min_max_us, readDalHardMinClkBits, checkDalHardMinClkBits); |
266 | |
267 | return hard_min_done; |
268 | } |
269 | |
270 | /* Returns the actual frequency that was set in MHz, 0 on failure */ |
271 | unsigned int dcn32_smu_set_hard_min_by_freq(struct clk_mgr_internal *clk_mgr, uint32_t clk, uint16_t freq_mhz) |
272 | { |
273 | uint32_t response = 0; |
274 | bool hard_min_done = false; |
275 | |
276 | /* bits 23:16 for clock type, lower 16 bits for frequency in MHz */ |
277 | uint32_t param = (clk << 16) | freq_mhz; |
278 | |
279 | smu_print("SMU Set hard min by freq: clk = %d, freq_mhz = %d MHz\n" , clk, freq_mhz); |
280 | |
281 | dcn32_smu_send_msg_with_param(clk_mgr, |
282 | DALSMC_MSG_SetHardMinByFreq, param_in: param, param_out: &response); |
283 | |
284 | if (dcn32_get_hard_min_status_supported(clk_mgr)) { |
285 | hard_min_done = dcn32_smu_wait_get_hard_min_status(clk_mgr, clk); |
286 | smu_print("SMU Frequency set = %d KHz hard_min_done %d\n" , response, hard_min_done); |
287 | } else |
288 | smu_print("SMU Frequency set = %d KHz\n" , response); |
289 | |
290 | return response; |
291 | } |
292 | |
293 | void dcn32_smu_wait_for_dmub_ack_mclk(struct clk_mgr_internal *clk_mgr, bool enable) |
294 | { |
295 | smu_print("PMFW to wait for DMCUB ack for MCLK : %d\n" , enable); |
296 | |
297 | dcn32_smu_send_msg_with_param(clk_mgr, msg_id: 0x14, param_in: enable ? 1 : 0, NULL); |
298 | } |
299 | |