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