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