1 /*
2  * Copyright 2012-16 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 <linux/delay.h>
27 #include "core_types.h"
28 #include "clk_mgr_internal.h"
29 #include "reg_helper.h"
30 #include "dm_helpers.h"
31 #include "dcn31_smu.h"
32 
33 #include "yellow_carp_offset.h"
34 #include "mp/mp_13_0_2_offset.h"
35 #include "mp/mp_13_0_2_sh_mask.h"
36 
37 #define REG(reg_name) \
38 	(MP0_BASE.instance[0].segment[reg ## reg_name ## _BASE_IDX] + reg ## reg_name)
39 
40 #define FN(reg_name, field) \
41 	FD(reg_name##__##field)
42 
43 #include "logger_types.h"
44 #undef DC_LOGGER
45 #define DC_LOGGER \
46 	CTX->logger
47 #define smu_print(str, ...) {DC_LOG_SMU(str, ##__VA_ARGS__); }
48 
49 #define VBIOSSMC_MSG_TestMessage                  0x1
50 #define VBIOSSMC_MSG_GetSmuVersion                0x2
51 #define VBIOSSMC_MSG_PowerUpGfx                   0x3
52 #define VBIOSSMC_MSG_SetDispclkFreq               0x4
53 #define VBIOSSMC_MSG_SetDprefclkFreq              0x5   //Not used. DPRef is constant
54 #define VBIOSSMC_MSG_SetDppclkFreq                0x6
55 #define VBIOSSMC_MSG_SetHardMinDcfclkByFreq       0x7
56 #define VBIOSSMC_MSG_SetMinDeepSleepDcfclk        0x8
57 #define VBIOSSMC_MSG_SetPhyclkVoltageByFreq       0x9	//Keep it in case VMIN dees not support phy clk
58 #define VBIOSSMC_MSG_GetFclkFrequency             0xA
59 #define VBIOSSMC_MSG_SetDisplayCount              0xB   //Not used anymore
60 #define VBIOSSMC_MSG_EnableTmdp48MHzRefclkPwrDown 0xC   //Not used anymore
61 #define VBIOSSMC_MSG_UpdatePmeRestore             0xD
62 #define VBIOSSMC_MSG_SetVbiosDramAddrHigh         0xE   //Used for WM table txfr
63 #define VBIOSSMC_MSG_SetVbiosDramAddrLow          0xF
64 #define VBIOSSMC_MSG_TransferTableSmu2Dram        0x10
65 #define VBIOSSMC_MSG_TransferTableDram2Smu        0x11
66 #define VBIOSSMC_MSG_SetDisplayIdleOptimizations  0x12
67 #define VBIOSSMC_MSG_GetDprefclkFreq              0x13
68 #define VBIOSSMC_MSG_GetDtbclkFreq                0x14
69 #define VBIOSSMC_MSG_AllowZstatesEntry            0x15
70 #define VBIOSSMC_MSG_DisallowZstatesEntry     	  0x16
71 #define VBIOSSMC_MSG_SetDtbClk                    0x17
72 #define VBIOSSMC_Message_Count                    0x18
73 
74 #define VBIOSSMC_Status_BUSY                      0x0
75 #define VBIOSSMC_Result_OK                        0x1
76 #define VBIOSSMC_Result_Failed                    0xFF
77 #define VBIOSSMC_Result_UnknownCmd                0xFE
78 #define VBIOSSMC_Result_CmdRejectedPrereq         0xFD
79 #define VBIOSSMC_Result_CmdRejectedBusy           0xFC
80 
81 /*
82  * Function to be used instead of REG_WAIT macro because the wait ends when
83  * the register is NOT EQUAL to zero, and because the translation in msg_if.h
84  * won't work with REG_WAIT.
85  */
86 static uint32_t dcn31_smu_wait_for_response(struct clk_mgr_internal *clk_mgr, unsigned int delay_us, unsigned int max_retries)
87 {
88 	uint32_t res_val = VBIOSSMC_Status_BUSY;
89 
90 	do {
91 		res_val = REG_READ(MP1_SMN_C2PMSG_91);
92 		if (res_val != VBIOSSMC_Status_BUSY)
93 			break;
94 
95 		if (delay_us >= 1000)
96 			msleep(delay_us/1000);
97 		else if (delay_us > 0)
98 			udelay(delay_us);
99 	} while (max_retries--);
100 
101 	return res_val;
102 }
103 
104 static int dcn31_smu_send_msg_with_param(struct clk_mgr_internal *clk_mgr,
105 					 unsigned int msg_id,
106 					 unsigned int param)
107 {
108 	uint32_t result;
109 
110 	result = dcn31_smu_wait_for_response(clk_mgr, 10, 200000);
111 
112 	if (result != VBIOSSMC_Result_OK)
113 		smu_print("SMU Response was not OK. SMU response after wait received is: %d\n", result);
114 
115 	if (result == VBIOSSMC_Status_BUSY) {
116 		return -1;
117 	}
118 
119 	/* First clear response register */
120 	REG_WRITE(MP1_SMN_C2PMSG_91, VBIOSSMC_Status_BUSY);
121 
122 	/* Set the parameter register for the SMU message, unit is Mhz */
123 	REG_WRITE(MP1_SMN_C2PMSG_83, param);
124 
125 	/* Trigger the message transaction by writing the message ID */
126 	REG_WRITE(MP1_SMN_C2PMSG_67, msg_id);
127 
128 	result = dcn31_smu_wait_for_response(clk_mgr, 10, 200000);
129 
130 	if (result == VBIOSSMC_Result_Failed) {
131 		if (msg_id == VBIOSSMC_MSG_TransferTableDram2Smu &&
132 		    param == TABLE_WATERMARKS)
133 			DC_LOG_WARNING("Watermarks table not configured properly by SMU");
134 		else
135 			ASSERT(0);
136 		REG_WRITE(MP1_SMN_C2PMSG_91, VBIOSSMC_Result_OK);
137 		return -1;
138 	}
139 
140 	if (IS_SMU_TIMEOUT(result)) {
141 		ASSERT(0);
142 		dm_helpers_smu_timeout(CTX, msg_id, param, 10 * 200000);
143 	}
144 
145 	return REG_READ(MP1_SMN_C2PMSG_83);
146 }
147 
148 int dcn31_smu_get_smu_version(struct clk_mgr_internal *clk_mgr)
149 {
150 	return dcn31_smu_send_msg_with_param(
151 			clk_mgr,
152 			VBIOSSMC_MSG_GetSmuVersion,
153 			0);
154 }
155 
156 
157 int dcn31_smu_set_dispclk(struct clk_mgr_internal *clk_mgr, int requested_dispclk_khz)
158 {
159 	int actual_dispclk_set_mhz = -1;
160 
161 	if (!clk_mgr->smu_present)
162 		return requested_dispclk_khz;
163 
164 	/*  Unit of SMU msg parameter is Mhz */
165 	actual_dispclk_set_mhz = dcn31_smu_send_msg_with_param(
166 			clk_mgr,
167 			VBIOSSMC_MSG_SetDispclkFreq,
168 			khz_to_mhz_ceil(requested_dispclk_khz));
169 
170 	return actual_dispclk_set_mhz * 1000;
171 }
172 
173 int dcn31_smu_set_dprefclk(struct clk_mgr_internal *clk_mgr)
174 {
175 	int actual_dprefclk_set_mhz = -1;
176 
177 	if (!clk_mgr->smu_present)
178 		return clk_mgr->base.dprefclk_khz;
179 
180 	actual_dprefclk_set_mhz = dcn31_smu_send_msg_with_param(
181 			clk_mgr,
182 			VBIOSSMC_MSG_SetDprefclkFreq,
183 			khz_to_mhz_ceil(clk_mgr->base.dprefclk_khz));
184 
185 	/* TODO: add code for programing DP DTO, currently this is down by command table */
186 
187 	return actual_dprefclk_set_mhz * 1000;
188 }
189 
190 int dcn31_smu_set_hard_min_dcfclk(struct clk_mgr_internal *clk_mgr, int requested_dcfclk_khz)
191 {
192 	int actual_dcfclk_set_mhz = -1;
193 
194 	if (!clk_mgr->base.ctx->dc->debug.pstate_enabled)
195 		return -1;
196 
197 	if (!clk_mgr->smu_present)
198 		return requested_dcfclk_khz;
199 
200 	actual_dcfclk_set_mhz = dcn31_smu_send_msg_with_param(
201 			clk_mgr,
202 			VBIOSSMC_MSG_SetHardMinDcfclkByFreq,
203 			khz_to_mhz_ceil(requested_dcfclk_khz));
204 
205 #ifdef DBG
206 	smu_print("actual_dcfclk_set_mhz %d is set to : %d\n", actual_dcfclk_set_mhz, actual_dcfclk_set_mhz * 1000);
207 #endif
208 
209 	return actual_dcfclk_set_mhz * 1000;
210 }
211 
212 int dcn31_smu_set_min_deep_sleep_dcfclk(struct clk_mgr_internal *clk_mgr, int requested_min_ds_dcfclk_khz)
213 {
214 	int actual_min_ds_dcfclk_mhz = -1;
215 
216 	if (!clk_mgr->base.ctx->dc->debug.pstate_enabled)
217 		return -1;
218 
219 	if (!clk_mgr->smu_present)
220 		return requested_min_ds_dcfclk_khz;
221 
222 	actual_min_ds_dcfclk_mhz = dcn31_smu_send_msg_with_param(
223 			clk_mgr,
224 			VBIOSSMC_MSG_SetMinDeepSleepDcfclk,
225 			khz_to_mhz_ceil(requested_min_ds_dcfclk_khz));
226 
227 	return actual_min_ds_dcfclk_mhz * 1000;
228 }
229 
230 int dcn31_smu_set_dppclk(struct clk_mgr_internal *clk_mgr, int requested_dpp_khz)
231 {
232 	int actual_dppclk_set_mhz = -1;
233 
234 	if (!clk_mgr->smu_present)
235 		return requested_dpp_khz;
236 
237 	actual_dppclk_set_mhz = dcn31_smu_send_msg_with_param(
238 			clk_mgr,
239 			VBIOSSMC_MSG_SetDppclkFreq,
240 			khz_to_mhz_ceil(requested_dpp_khz));
241 
242 	return actual_dppclk_set_mhz * 1000;
243 }
244 
245 void dcn31_smu_set_display_idle_optimization(struct clk_mgr_internal *clk_mgr, uint32_t idle_info)
246 {
247 	if (!clk_mgr->base.ctx->dc->debug.pstate_enabled)
248 		return;
249 
250 	if (!clk_mgr->smu_present)
251 		return;
252 
253 	//TODO: Work with smu team to define optimization options.
254 	dcn31_smu_send_msg_with_param(
255 		clk_mgr,
256 		VBIOSSMC_MSG_SetDisplayIdleOptimizations,
257 		idle_info);
258 }
259 
260 void dcn31_smu_enable_phy_refclk_pwrdwn(struct clk_mgr_internal *clk_mgr, bool enable)
261 {
262 	union display_idle_optimization_u idle_info = { 0 };
263 
264 	if (!clk_mgr->smu_present)
265 		return;
266 
267 	if (enable) {
268 		idle_info.idle_info.df_request_disabled = 1;
269 		idle_info.idle_info.phy_ref_clk_off = 1;
270 	}
271 
272 	dcn31_smu_send_msg_with_param(
273 			clk_mgr,
274 			VBIOSSMC_MSG_SetDisplayIdleOptimizations,
275 			idle_info.data);
276 }
277 
278 void dcn31_smu_enable_pme_wa(struct clk_mgr_internal *clk_mgr)
279 {
280 	if (!clk_mgr->smu_present)
281 		return;
282 
283 	dcn31_smu_send_msg_with_param(
284 			clk_mgr,
285 			VBIOSSMC_MSG_UpdatePmeRestore,
286 			0);
287 }
288 
289 void dcn31_smu_set_dram_addr_high(struct clk_mgr_internal *clk_mgr, uint32_t addr_high)
290 {
291 	if (!clk_mgr->smu_present)
292 		return;
293 
294 	dcn31_smu_send_msg_with_param(clk_mgr,
295 			VBIOSSMC_MSG_SetVbiosDramAddrHigh, addr_high);
296 }
297 
298 void dcn31_smu_set_dram_addr_low(struct clk_mgr_internal *clk_mgr, uint32_t addr_low)
299 {
300 	if (!clk_mgr->smu_present)
301 		return;
302 
303 	dcn31_smu_send_msg_with_param(clk_mgr,
304 			VBIOSSMC_MSG_SetVbiosDramAddrLow, addr_low);
305 }
306 
307 void dcn31_smu_transfer_dpm_table_smu_2_dram(struct clk_mgr_internal *clk_mgr)
308 {
309 	if (!clk_mgr->smu_present)
310 		return;
311 
312 	dcn31_smu_send_msg_with_param(clk_mgr,
313 			VBIOSSMC_MSG_TransferTableSmu2Dram, TABLE_DPMCLOCKS);
314 }
315 
316 void dcn31_smu_transfer_wm_table_dram_2_smu(struct clk_mgr_internal *clk_mgr)
317 {
318 	if (!clk_mgr->smu_present)
319 		return;
320 
321 	dcn31_smu_send_msg_with_param(clk_mgr,
322 			VBIOSSMC_MSG_TransferTableDram2Smu, TABLE_WATERMARKS);
323 }
324 
325 void dcn31_smu_set_zstate_support(struct clk_mgr_internal *clk_mgr, enum dcn_zstate_support_state support)
326 {
327 	unsigned int msg_id, param;
328 
329 	if (!clk_mgr->smu_present)
330 		return;
331 
332 	if (!clk_mgr->base.ctx->dc->debug.enable_z9_disable_interface &&
333 			(support == DCN_ZSTATE_SUPPORT_ALLOW_Z10_ONLY))
334 		support = DCN_ZSTATE_SUPPORT_DISALLOW;
335 
336 
337 	if (support == DCN_ZSTATE_SUPPORT_ALLOW_Z10_ONLY)
338 		param = 1;
339 	else
340 		param = 0;
341 
342 	if (support == DCN_ZSTATE_SUPPORT_DISALLOW)
343 		msg_id = VBIOSSMC_MSG_DisallowZstatesEntry;
344 	else
345 		msg_id = VBIOSSMC_MSG_AllowZstatesEntry;
346 
347 	dcn31_smu_send_msg_with_param(
348 		clk_mgr,
349 		msg_id,
350 		param);
351 
352 }
353 
354 /* Arg = 1: Turn DTB on; 0: Turn DTB CLK OFF. when it is on, it is 600MHZ */
355 void dcn31_smu_set_dtbclk(struct clk_mgr_internal *clk_mgr, bool enable)
356 {
357 	if (!clk_mgr->smu_present)
358 		return;
359 
360 	dcn31_smu_send_msg_with_param(
361 			clk_mgr,
362 			VBIOSSMC_MSG_SetDtbClk,
363 			enable);
364 }
365