1 /*
2  * Copyright (c) 2020-2021 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for
6  * any purpose with or without fee is hereby granted, provided that the
7  * above copyright notice and this permission notice appear in all
8  * copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17  * PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 /**
21  * DOC: wlan_hdd_thermal.c
22  *
23  * WLAN Host Device Driver implementation for thermal mitigation handling
24  */
25 
26 #include <wlan_hdd_includes.h>
27 #include <net/cfg80211.h>
28 #include "wlan_osif_priv.h"
29 #include "qdf_trace.h"
30 #include "wlan_hdd_main.h"
31 #include "osif_sync.h"
32 #include <linux/limits.h>
33 #include <wlan_hdd_object_manager.h>
34 #include "sme_api.h"
35 #include "wlan_hdd_thermal.h"
36 #include "wlan_hdd_cfg80211.h"
37 #include <qca_vendor.h>
38 #include "wlan_fwol_ucfg_api.h"
39 #include <pld_common.h>
40 #include "wlan_hdd_stats.h"
41 #include "os_if_fwol.h"
42 #include "wlan_osif_request_manager.h"
43 #include "wlan_fwol_public_structs.h"
44 
45 #define DC_OFF_PERCENT_WPPS 50
46 #define WLAN_WAIT_TIME_GET_THERM_LVL 1000
47 
48 const struct nla_policy
49 	wlan_hdd_thermal_mitigation_policy
50 	[QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_MAX + 1] = {
51 		[QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_VALUE] = {.type = NLA_U32},
52 		[QCA_WLAN_VENDOR_ATTR_THERMAL_LEVEL] = {
53 						.type = NLA_U32},
54 		[QCA_WLAN_VENDOR_ATTR_THERMAL_COMPLETION_WINDOW] = {
55 						.type = NLA_U32},
56 		[QCA_WLAN_VENDOR_ATTR_THERMAL_STATS] = {.type = NLA_NESTED},
57 };
58 
59 #ifdef FEATURE_WPSS_THERMAL_MITIGATION
60 void
hdd_thermal_fill_clientid_priority(struct hdd_context * hdd_ctx,uint8_t mon_id,uint8_t priority_apps,uint8_t priority_wpps,struct thermal_mitigation_params * params)61 hdd_thermal_fill_clientid_priority(struct hdd_context *hdd_ctx, uint8_t mon_id,
62 				   uint8_t priority_apps, uint8_t priority_wpps,
63 				   struct thermal_mitigation_params *params)
64 {
65 	if (hdd_ctx->multi_client_thermal_mitigation) {
66 		if (mon_id == THERMAL_MONITOR_APPS) {
67 			params->priority  = priority_apps;
68 			params->client_id = mon_id;
69 			hdd_debug("Thermal client:%d priority_apps: %d", mon_id,
70 				  priority_apps);
71 		} else if (mon_id == THERMAL_MONITOR_WPSS) {
72 			params->priority = priority_wpps;
73 			params->client_id = mon_id;
74 			/* currently hardcoded,
75 			 * can be changed based on requirement.
76 			 */
77 			params->levelconf[0].dcoffpercent = DC_OFF_PERCENT_WPPS;
78 			hdd_debug("Thermal client:%d priority_wpps: %d", mon_id,
79 				  priority_wpps);
80 		}
81 	}
82 }
83 #endif
84 
85 QDF_STATUS
hdd_send_thermal_mitigation_val(struct hdd_context * hdd_ctx,uint32_t level,uint8_t mon_id)86 hdd_send_thermal_mitigation_val(struct hdd_context *hdd_ctx, uint32_t level,
87 				uint8_t mon_id)
88 {
89 	uint32_t dc, dc_off_percent;
90 	uint32_t prio = 0, target_temp = 0;
91 	struct wlan_fwol_thermal_temp thermal_temp = {0};
92 	QDF_STATUS status;
93 	bool enable = true;
94 	struct thermal_mitigation_params therm_cfg_params = {0};
95 
96 	status = ucfg_fwol_get_thermal_temp(hdd_ctx->psoc, &thermal_temp);
97 	if (QDF_IS_STATUS_ERROR(status)) {
98 		hdd_err_rl("Failed to get fwol thermal obj");
99 		return status;
100 	}
101 
102 	switch (level) {
103 	case QCA_WLAN_VENDOR_THERMAL_LEVEL_EMERGENCY:
104 		dc_off_percent = thermal_temp.throttle_dutycycle_level[5];
105 		break;
106 	case QCA_WLAN_VENDOR_THERMAL_LEVEL_CRITICAL:
107 		dc_off_percent = thermal_temp.throttle_dutycycle_level[4];
108 		break;
109 	case QCA_WLAN_VENDOR_THERMAL_LEVEL_SEVERE:
110 		dc_off_percent = thermal_temp.throttle_dutycycle_level[3];
111 		break;
112 	case QCA_WLAN_VENDOR_THERMAL_LEVEL_MODERATE:
113 		dc_off_percent = thermal_temp.throttle_dutycycle_level[2];
114 		break;
115 	case QCA_WLAN_VENDOR_THERMAL_LEVEL_LIGHT:
116 		dc_off_percent = thermal_temp.throttle_dutycycle_level[1];
117 		break;
118 	case QCA_WLAN_VENDOR_THERMAL_LEVEL_NONE:
119 		enable = false;
120 		dc_off_percent = thermal_temp.throttle_dutycycle_level[0];
121 		break;
122 	default:
123 		hdd_debug("Invalid thermal state");
124 		return QDF_STATUS_E_INVAL;
125 	}
126 
127 	dc = thermal_temp.thermal_sampling_time;
128 	therm_cfg_params.enable = enable;
129 	therm_cfg_params.dc = dc;
130 	therm_cfg_params.levelconf[0].dcoffpercent = dc_off_percent;
131 	therm_cfg_params.levelconf[0].priority = prio;
132 	therm_cfg_params.levelconf[0].tmplwm = target_temp;
133 	therm_cfg_params.num_thermal_conf = 1;
134 	therm_cfg_params.pdev_id = 0;
135 
136 	hdd_thermal_fill_clientid_priority(hdd_ctx, mon_id,
137 					   thermal_temp.priority_apps,
138 					   thermal_temp.priority_wpps,
139 					   &therm_cfg_params);
140 
141 	hdd_debug("dc %d dc_off_per %d", dc, dc_off_percent);
142 
143 	status = sme_set_thermal_throttle_cfg(hdd_ctx->mac_handle,
144 					      &therm_cfg_params);
145 	if (QDF_IS_STATUS_ERROR(status))
146 		hdd_err_rl("Failed to set throttle configuration %d", status);
147 
148 	else
149 		/*
150 		 * After SSR, the thermal mitigation level is lost.
151 		 * As SSR is hidden from userland, this command will not come
152 		 * from userspace after a SSR. To restore this configuration,
153 		 * save this in hdd context and restore after re-init.
154 		 */
155 		hdd_ctx->dutycycle_off_percent = dc_off_percent;
156 
157 	return QDF_STATUS_SUCCESS;
158 }
159 
160 /**
161  * convert_level_to_vendor_thermal_level() - convert internal thermal level
162  *  to vendor command attribute enum qca_wlan_vendor_thermal_level
163  * @level: driver internal thermal level
164  *
165  * Return: vendor thermal level
166  */
167 static enum qca_wlan_vendor_thermal_level
convert_level_to_vendor_thermal_level(enum thermal_throttle_level level)168 convert_level_to_vendor_thermal_level(enum thermal_throttle_level level)
169 {
170 	if (level == THERMAL_FULLPERF)
171 		return QCA_WLAN_VENDOR_THERMAL_LEVEL_NONE;
172 	else if (level == THERMAL_MITIGATION)
173 		return QCA_WLAN_VENDOR_THERMAL_LEVEL_MODERATE;
174 	else if (level == THERMAL_SHUTOFF)
175 		return QCA_WLAN_VENDOR_THERMAL_LEVEL_CRITICAL;
176 	else
177 		return QCA_WLAN_VENDOR_THERMAL_LEVEL_EMERGENCY;
178 }
179 
180 /**
181  * hdd_get_curr_thermal_throttle_level_val() - Indicate current target
182  *  thermal throttle level to upper layer upon query level
183  * @hdd_ctx: hdd context
184  *
185  * Return: 0 for success
186  */
187 static int
hdd_get_curr_thermal_throttle_level_val(struct hdd_context * hdd_ctx)188 hdd_get_curr_thermal_throttle_level_val(struct hdd_context *hdd_ctx)
189 {
190 	struct sk_buff *reply_skb;
191 	uint32_t data_len;
192 	enum thermal_throttle_level level = THERMAL_FULLPERF;
193 	enum qca_wlan_vendor_thermal_level vendor_level;
194 	QDF_STATUS status;
195 
196 	status = ucfg_fwol_thermal_get_target_level(hdd_ctx->psoc, &level);
197 	if (QDF_IS_STATUS_ERROR(status)) {
198 		hdd_err("get_thermal level: fail get target level");
199 		return -EINVAL;
200 	}
201 	vendor_level = convert_level_to_vendor_thermal_level(level);
202 	data_len = NLMSG_HDRLEN + nla_total_size(sizeof(uint32_t));
203 	reply_skb = wlan_cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy,
204 							     data_len);
205 	if (!reply_skb) {
206 		hdd_err("get_thermal level: buffer alloc fail");
207 		return -ENOMEM;
208 	}
209 	if (nla_put_u32(reply_skb, QCA_WLAN_VENDOR_ATTR_THERMAL_LEVEL,
210 			vendor_level)) {
211 		hdd_err("get_thermal level: nla put fail");
212 		wlan_cfg80211_vendor_free_skb(reply_skb);
213 		return -EINVAL;
214 	}
215 	hdd_debug("get_thermal level: %d vendor level %d", level,
216 		  vendor_level);
217 
218 	return wlan_cfg80211_vendor_cmd_reply(reply_skb);
219 }
220 
221 /**
222  * hdd_get_curr_thermal_temperature_val() - Indicate current target
223  *  thermal temperature to upper layer when handing temperature
224  *  query vendor command
225  * @hdd_ctx: hdd context
226  * @adapter: adapter context
227  *
228  * Return: 0 for success
229  */
230 static int
hdd_get_curr_thermal_temperature_val(struct hdd_context * hdd_ctx,struct hdd_adapter * adapter)231 hdd_get_curr_thermal_temperature_val(struct hdd_context *hdd_ctx,
232 				     struct hdd_adapter *adapter)
233 {
234 	struct sk_buff *reply_skb;
235 	int ret;
236 	uint32_t data_len;
237 	int temperature = 0;
238 
239 	ret = wlan_hdd_get_temperature(adapter, &temperature);
240 	if (ret)
241 		return ret;
242 	data_len = NLMSG_HDRLEN + nla_total_size(sizeof(uint32_t));
243 	reply_skb = wlan_cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy,
244 							     data_len);
245 	if (!reply_skb) {
246 		hdd_err("get_thermal temperature: buffer alloc fail");
247 		return -ENOMEM;
248 	}
249 	if (nla_put_u32(reply_skb,
250 			QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_DATA,
251 			temperature)) {
252 		hdd_err("get_thermal temperature: nla put fail");
253 		wlan_cfg80211_vendor_free_skb(reply_skb);
254 		return -EINVAL;
255 	}
256 	hdd_debug("get_thermal temperature: %d", temperature);
257 
258 	return wlan_cfg80211_vendor_cmd_reply(reply_skb);
259 }
260 
261 #ifdef THERMAL_STATS_SUPPORT
262 QDF_STATUS
hdd_send_get_thermal_stats_cmd(struct hdd_context * hdd_ctx,enum thermal_stats_request_type request_type,void (* callback)(void * context,struct thermal_throttle_info * response),void * context)263 hdd_send_get_thermal_stats_cmd(struct hdd_context *hdd_ctx,
264 			       enum thermal_stats_request_type request_type,
265 			       void (*callback)(void *context,
266 			       struct thermal_throttle_info *response),
267 			       void *context)
268 {
269 	int ret;
270 
271 	if (!hdd_ctx->psoc) {
272 		hdd_err_rl("NULL pointer for psoc");
273 		return QDF_STATUS_E_INVAL;
274 	}
275 
276 
277 	/* Send Get Thermal Stats cmd to FW */
278 	ret = os_if_fwol_get_thermal_stats_req(hdd_ctx->psoc, request_type,
279 					       callback, context);
280 	if (ret)
281 		return QDF_STATUS_E_FAILURE;
282 
283 	return QDF_STATUS_SUCCESS;
284 }
285 
286 /**
287  * hdd_get_thermal_stats_cb() - Get thermal stats callback
288  * @context: Call context
289  * @response: Pointer to response structure
290  *
291  * Return: void
292  */
293 static void
hdd_get_thermal_stats_cb(void * context,struct thermal_throttle_info * response)294 hdd_get_thermal_stats_cb(void *context,
295 			 struct thermal_throttle_info *response)
296 {
297 	struct osif_request *request;
298 	struct thermal_throttle_info *priv;
299 
300 	request = osif_request_get(context);
301 	if (!request) {
302 		osif_err("Obsolete request");
303 		return;
304 	}
305 
306 	priv = osif_request_priv(request);
307 	qdf_mem_copy(priv, response, sizeof(struct thermal_throttle_info));
308 
309 	osif_request_complete(request);
310 	osif_request_put(request);
311 }
312 
313 #define THERMAL_MIN_TEMP QCA_WLAN_VENDOR_ATTR_THERMAL_STATS_MIN_TEMPERATURE
314 #define THERMAL_MAX_TEMP QCA_WLAN_VENDOR_ATTR_THERMAL_STATS_MAX_TEMPERATURE
315 #define THERMAL_DWELL_TIME QCA_WLAN_VENDOR_ATTR_THERMAL_STATS_DWELL_TIME
316 #define THERMAL_LVL_COUNT QCA_WLAN_VENDOR_ATTR_THERMAL_STATS_TEMP_LEVEL_COUNTER
317 
318 /**
319  * hdd_get_curr_thermal_stats_val() - Indicate thermal stats
320  *  to upper layer when query vendor command
321  * @wiphy: Pointer to wireless phy
322  * @hdd_ctx: hdd context
323  *
324  * Return: 0 for success
325  */
326 static int
hdd_get_curr_thermal_stats_val(struct wiphy * wiphy,struct hdd_context * hdd_ctx)327 hdd_get_curr_thermal_stats_val(struct wiphy *wiphy,
328 			       struct hdd_context *hdd_ctx)
329 {
330 	int ret = 0;
331 	uint8_t i = 0;
332 	struct osif_request *request = NULL;
333 	int skb_len = 0;
334 	struct thermal_throttle_info *priv;
335 	struct thermal_throttle_info *get_tt_stats = NULL;
336 	struct sk_buff *skb = NULL;
337 	void *cookie;
338 	struct nlattr *therm_attr;
339 	struct nlattr *tt_levels;
340 	static const struct osif_request_params params = {
341 		.priv_size = sizeof(*priv),
342 		.timeout_ms = WLAN_WAIT_TIME_GET_THERM_LVL,
343 		.dealloc = NULL,
344 	};
345 
346 	if (hdd_ctx->is_therm_stats_in_progress) {
347 		hdd_err("request already in progress");
348 		return -EINVAL;
349 	}
350 
351 	request = osif_request_alloc(&params);
352 	if (!request) {
353 		hdd_err("request allocation failure");
354 		return -ENOMEM;
355 	}
356 	cookie = osif_request_cookie(request);
357 	hdd_ctx->is_therm_stats_in_progress = true;
358 	ret = hdd_send_get_thermal_stats_cmd(hdd_ctx, thermal_stats_req,
359 					     hdd_get_thermal_stats_cb,
360 					     cookie);
361 	if (QDF_IS_STATUS_ERROR(ret)) {
362 		hdd_err("Failure while sending command to fw");
363 		ret = -EAGAIN;
364 		goto completed;
365 	}
366 
367 	ret = osif_request_wait_for_response(request);
368 	if (ret) {
369 		hdd_err("Timed out while retrieving thermal stats");
370 		ret = -EAGAIN;
371 		goto completed;
372 	}
373 
374 	get_tt_stats = osif_request_priv(request);
375 	if (!get_tt_stats) {
376 		hdd_err("invalid get_tt_stats");
377 		ret = -EINVAL;
378 		goto completed;
379 	}
380 
381 	skb_len = NLMSG_HDRLEN + (get_tt_stats->therm_throt_levels) *
382 		  (NLA_HDRLEN + (NLA_HDRLEN +
383 		     sizeof(get_tt_stats->level_info[i].start_temp_level) +
384 		     NLA_HDRLEN +
385 		     sizeof(get_tt_stats->level_info[i].end_temp_level) +
386 		     NLA_HDRLEN +
387 		     sizeof(get_tt_stats->level_info[i].total_time_ms_lo) +
388 		     NLA_HDRLEN +
389 		     sizeof(get_tt_stats->level_info[i].num_entry)));
390 
391 	skb = wlan_cfg80211_vendor_cmd_alloc_reply_skb(wiphy,
392 						       skb_len);
393 	if (!skb) {
394 		hdd_err_rl("wlan_cfg80211_vendor_cmd_alloc_reply_skb failed");
395 		ret = -ENOMEM;
396 		goto completed;
397 	}
398 
399 	therm_attr = nla_nest_start(skb, QCA_WLAN_VENDOR_ATTR_THERMAL_STATS);
400 	if (!therm_attr) {
401 		hdd_err_rl("nla_nest_start failed for attr failed");
402 		ret = -EINVAL;
403 		goto nla_failed;
404 	}
405 
406 	for (i = 0; i < get_tt_stats->therm_throt_levels; i++) {
407 		tt_levels = nla_nest_start(skb, i);
408 		if (!tt_levels) {
409 			hdd_err_rl("nla_nest_start failed for thermal level %d",
410 				   i);
411 			ret = -EINVAL;
412 			goto nla_failed;
413 		}
414 
415 		hdd_debug("level %d, Temp Range: %d - %d, Dwell time %d, Counter %d",
416 			  i, get_tt_stats->level_info[i].start_temp_level,
417 			  get_tt_stats->level_info[i].end_temp_level,
418 			  get_tt_stats->level_info[i].total_time_ms_lo,
419 			  get_tt_stats->level_info[i].num_entry);
420 
421 		if (nla_put_u32(skb, THERMAL_MIN_TEMP,
422 				get_tt_stats->level_info[i].start_temp_level) ||
423 		    nla_put_u32(skb, THERMAL_MAX_TEMP,
424 				get_tt_stats->level_info[i].end_temp_level) ||
425 		    nla_put_u32(skb, THERMAL_DWELL_TIME,
426 				(get_tt_stats->level_info[i].total_time_ms_lo)) ||
427 		    nla_put_u32(skb, THERMAL_LVL_COUNT,
428 				get_tt_stats->level_info[i].num_entry)) {
429 			hdd_err("nla put failure");
430 			ret =  -EINVAL;
431 			goto nla_failed;
432 		}
433 		nla_nest_end(skb, tt_levels);
434 	}
435 	nla_nest_end(skb, therm_attr);
436 	wlan_cfg80211_vendor_cmd_reply(skb);
437 	goto completed;
438 
439 nla_failed:
440 	wlan_cfg80211_vendor_free_skb(skb);
441 completed:
442 	hdd_ctx->is_therm_stats_in_progress = false;
443 	osif_request_put(request);
444 
445 	return ret;
446 }
447 
448 #undef THERMAL_MIN_TEMP
449 #undef THERMAL_MAX_TEMP
450 #undef THERMAL_DWELL_TIME
451 #undef THERMAL_LVL_COUNT
452 
453 static QDF_STATUS
hdd_send_thermal_stats_clear_cmd(struct hdd_context * hdd_ctx)454 hdd_send_thermal_stats_clear_cmd(struct hdd_context *hdd_ctx)
455 {
456 	QDF_STATUS status;
457 
458 	status = hdd_send_get_thermal_stats_cmd(hdd_ctx,
459 					     thermal_stats_clear, NULL,
460 					     NULL);
461 
462 	return status;
463 }
464 #else
465 static int
hdd_get_curr_thermal_stats_val(struct wiphy * wiphy,struct hdd_context * hdd_ctx)466 hdd_get_curr_thermal_stats_val(struct wiphy *wiphy,
467 			       struct hdd_context *hdd_ctx)
468 {
469 	return -EINVAL;
470 }
471 
472 static QDF_STATUS
hdd_send_thermal_stats_clear_cmd(struct hdd_context * hdd_ctx)473 hdd_send_thermal_stats_clear_cmd(struct hdd_context *hdd_ctx)
474 {
475 	return QDF_STATUS_E_NOSUPPORT;
476 }
477 #endif /* THERMAL_STATS_SUPPORT */
478 
479 /**
480  * __wlan_hdd_cfg80211_set_thermal_mitigation_policy() - Set the thermal policy
481  * @wiphy: Pointer to wireless phy
482  * @wdev: Pointer to wireless device
483  * @data: Pointer to data
484  * @data_len: Length of @data
485  *
486  * Return: 0 on success, negative errno on failure
487  */
488 static int
__wlan_hdd_cfg80211_set_thermal_mitigation_policy(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)489 __wlan_hdd_cfg80211_set_thermal_mitigation_policy(struct wiphy *wiphy,
490 						  struct wireless_dev *wdev,
491 						  const void *data,
492 						  int data_len)
493 {
494 	struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
495 	struct net_device *dev = wdev->netdev;
496 	struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
497 	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_MAX + 1];
498 	uint32_t level, cmd_type;
499 	QDF_STATUS status;
500 	int ret;
501 
502 	hdd_enter();
503 
504 	ret = wlan_hdd_validate_context(hdd_ctx);
505 	if (ret)
506 		return -EINVAL;
507 
508 	if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
509 		hdd_err_rl("Command not allowed in FTM mode");
510 		return -EPERM;
511 	}
512 
513 	if (wlan_cfg80211_nla_parse(tb,
514 				    QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_MAX,
515 				    (struct nlattr *)data, data_len,
516 				    wlan_hdd_thermal_mitigation_policy)) {
517 		hdd_err_rl("Invalid attribute");
518 		return -EINVAL;
519 	}
520 
521 	if (!tb[QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_VALUE]) {
522 		hdd_err_rl("attr thermal cmd value failed");
523 		return -EINVAL;
524 	}
525 
526 	cmd_type = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_VALUE]);
527 	switch (cmd_type) {
528 	case QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_SET_LEVEL:
529 		if (!tb[QCA_WLAN_VENDOR_ATTR_THERMAL_LEVEL]) {
530 			hdd_err_rl("attr thermal throttle set failed");
531 			return -EINVAL;
532 		}
533 		level =
534 		    nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_THERMAL_LEVEL]);
535 
536 		hdd_debug("thermal mitigation level from userspace %d", level);
537 		status = hdd_send_thermal_mitigation_val(hdd_ctx, level,
538 							 THERMAL_MONITOR_APPS);
539 		ret = qdf_status_to_os_return(status);
540 		break;
541 	case QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_LEVEL:
542 		ret = hdd_get_curr_thermal_throttle_level_val(hdd_ctx);
543 		break;
544 	case QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_TEMPERATURE:
545 		ret = hdd_get_curr_thermal_temperature_val(hdd_ctx, adapter);
546 		break;
547 	case QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_THERMAL_STATS:
548 		ret = hdd_get_curr_thermal_stats_val(wiphy, hdd_ctx);
549 		break;
550 	case QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_CLEAR_THERMAL_STATS:
551 		status = hdd_send_thermal_stats_clear_cmd(hdd_ctx);
552 		if (QDF_IS_STATUS_ERROR(status)) {
553 			hdd_err("Failure while sending command to fw");
554 			ret = -EINVAL;
555 		}
556 		break;
557 	default:
558 		ret = -EINVAL;
559 	}
560 
561 	hdd_exit();
562 	return ret;
563 }
564 
565 /**
566  * wlan_hdd_cfg80211_set_thermal_mitigation_policy() - set thermal
567  * mitigation policy
568  * @wiphy: wiphy pointer
569  * @wdev: pointer to struct wireless_dev
570  * @data: pointer to incoming NL vendor data
571  * @data_len: length of @data
572  *
573  * Return: 0 on success; error number otherwise.
574  */
575 int
wlan_hdd_cfg80211_set_thermal_mitigation_policy(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)576 wlan_hdd_cfg80211_set_thermal_mitigation_policy(struct wiphy *wiphy,
577 						struct wireless_dev *wdev,
578 						const void *data, int data_len)
579 {
580 	struct osif_psoc_sync *psoc_sync;
581 	int errno;
582 
583 	errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync);
584 	if (errno)
585 		return errno;
586 
587 	errno = __wlan_hdd_cfg80211_set_thermal_mitigation_policy(wiphy, wdev,
588 								  data,
589 								  data_len);
590 
591 	osif_psoc_sync_op_stop(psoc_sync);
592 
593 	return errno;
594 }
595 
wlan_hdd_thermal_config_support(void)596 bool wlan_hdd_thermal_config_support(void)
597 {
598 	return true;
599 }
600 
hdd_restore_thermal_mitigation_config(struct hdd_context * hdd_ctx)601 QDF_STATUS hdd_restore_thermal_mitigation_config(struct hdd_context *hdd_ctx)
602 {
603 	bool enable = true;
604 	uint32_t dc, dc_off_percent = 0;
605 	uint32_t prio = 0, target_temp = 0;
606 	struct wlan_fwol_thermal_temp thermal_temp = {0};
607 	QDF_STATUS status;
608 	struct thermal_mitigation_params therm_cfg_params = {0};
609 
610 	status = ucfg_fwol_get_thermal_temp(hdd_ctx->psoc, &thermal_temp);
611 	if (QDF_IS_STATUS_ERROR(status)) {
612 		hdd_err_rl("Failed to get fwol thermal obj");
613 		return status;
614 	}
615 
616 	dc_off_percent = hdd_ctx->dutycycle_off_percent;
617 	dc = thermal_temp.thermal_sampling_time;
618 
619 	if (!dc_off_percent)
620 		enable = false;
621 
622 	therm_cfg_params.enable = enable;
623 	therm_cfg_params.dc = dc;
624 	therm_cfg_params.levelconf[0].dcoffpercent = dc_off_percent;
625 	therm_cfg_params.levelconf[0].priority = prio;
626 	therm_cfg_params.levelconf[0].tmplwm = target_temp;
627 	therm_cfg_params.num_thermal_conf = 1;
628 	therm_cfg_params.client_id = THERMAL_MONITOR_APPS;
629 	therm_cfg_params.priority = 0;
630 
631 	hdd_debug("dc %d dc_off_per %d enable %d", dc, dc_off_percent, enable);
632 
633 	status = sme_set_thermal_throttle_cfg(hdd_ctx->mac_handle,
634 					      &therm_cfg_params);
635 	if (QDF_IS_STATUS_ERROR(status))
636 		hdd_err_rl("Failed to set throttle configuration %d", status);
637 
638 	return status;
639 }
640 
641 static int
__wlan_hdd_pld_set_thermal_mitigation(struct device * dev,unsigned long state,int mon_id)642 __wlan_hdd_pld_set_thermal_mitigation(struct device *dev, unsigned long state,
643 				      int mon_id)
644 {
645 	struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
646 	QDF_STATUS status;
647 	int ret;
648 
649 	ret = wlan_hdd_validate_context(hdd_ctx);
650 	if (ret)
651 		return ret;
652 
653 	if (hdd_ctx->driver_status == DRIVER_MODULES_CLOSED)
654 		return -EINVAL;
655 
656 	status = hdd_send_thermal_mitigation_val(hdd_ctx, state, mon_id);
657 
658 	return qdf_status_to_os_return(status);
659 }
660 
wlan_hdd_pld_set_thermal_mitigation(struct device * dev,unsigned long state,int mon_id)661 int wlan_hdd_pld_set_thermal_mitigation(struct device *dev, unsigned long state,
662 					int mon_id)
663 {
664 	struct osif_psoc_sync *psoc_sync;
665 	int ret;
666 
667 	hdd_enter();
668 
669 	ret = osif_psoc_sync_op_start(dev, &psoc_sync);
670 	if (ret)
671 		return ret;
672 
673 	ret =  __wlan_hdd_pld_set_thermal_mitigation(dev, state, mon_id);
674 
675 	osif_psoc_sync_op_stop(psoc_sync);
676 	hdd_exit();
677 
678 	return ret;
679 }
680 
681 #ifdef FEATURE_WPSS_THERMAL_MITIGATION
hdd_thermal_mitigation_register_wpps(struct hdd_context * hdd_ctx,struct device * dev)682 inline void hdd_thermal_mitigation_register_wpps(struct hdd_context *hdd_ctx,
683 						 struct device *dev)
684 {
685 	if (hdd_ctx->multi_client_thermal_mitigation)
686 		pld_thermal_register(dev, HDD_THERMAL_STATE_LIGHT,
687 				     THERMAL_MONITOR_WPSS);
688 }
689 
hdd_thermal_mitigation_unregister_wpps(struct hdd_context * hdd_ctx,struct device * dev)690 inline void hdd_thermal_mitigation_unregister_wpps(struct hdd_context *hdd_ctx,
691 						   struct device *dev)
692 {
693 	if (hdd_ctx->multi_client_thermal_mitigation)
694 		pld_thermal_unregister(dev, THERMAL_MONITOR_WPSS);
695 }
696 #else
697 static inline
hdd_thermal_mitigation_register_wpps(struct hdd_context * hdd_ctx,struct device * dev)698 void hdd_thermal_mitigation_register_wpps(struct hdd_context *hdd_ctx,
699 					  struct device *dev)
700 {
701 }
702 
703 static inline
hdd_thermal_mitigation_unregister_wpps(struct hdd_context * hdd_ctx,struct device * dev)704 void hdd_thermal_mitigation_unregister_wpps(struct hdd_context *hdd_ctx,
705 					    struct device *dev)
706 {
707 }
708 #endif
hdd_thermal_mitigation_register(struct hdd_context * hdd_ctx,struct device * dev)709 void hdd_thermal_mitigation_register(struct hdd_context *hdd_ctx,
710 				     struct device *dev)
711 {
712 	pld_thermal_register(dev, HDD_THERMAL_STATE_EMERGENCY,
713 			     THERMAL_MONITOR_APPS);
714 	hdd_thermal_mitigation_register_wpps(hdd_ctx, dev);
715 }
716 
hdd_thermal_mitigation_unregister(struct hdd_context * hdd_ctx,struct device * dev)717 void hdd_thermal_mitigation_unregister(struct hdd_context *hdd_ctx,
718 				       struct device *dev)
719 {
720 	hdd_thermal_mitigation_unregister_wpps(hdd_ctx, dev);
721 	pld_thermal_unregister(dev, THERMAL_MONITOR_APPS);
722 }
723 
724 #ifdef FW_THERMAL_THROTTLE_SUPPORT
725 /**
726  * hdd_notify_thermal_throttle_handler() - Thermal throttle event handler
727  * @psoc: psoc object
728  * @info: thermal throttle information from target
729  *
730  * Return: QDF_STATUS_SUCCESS for success.
731  */
732 static QDF_STATUS
hdd_notify_thermal_throttle_handler(struct wlan_objmgr_psoc * psoc,struct thermal_throttle_info * info)733 hdd_notify_thermal_throttle_handler(struct wlan_objmgr_psoc *psoc,
734 				    struct thermal_throttle_info *info)
735 {
736 	uint32_t data_len;
737 	struct sk_buff *vendor_event;
738 	struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
739 	int ret;
740 	enum qca_wlan_vendor_thermal_level level;
741 
742 	ret = wlan_hdd_validate_context(hdd_ctx);
743 	if (ret)
744 		return QDF_STATUS_E_FAILURE;
745 
746 	/* TX will be throttled completely if above MITIGATION level.
747 	 * So report additional DIAG event to notify user-space explicitly.
748 	 */
749 	if (info->level == THERMAL_SHUTOFF ||
750 	    info->level == THERMAL_SHUTDOWN_TARGET)
751 		host_log_device_status(WLAN_STATUS_DEVICE_TEMPERATURE_HIGH);
752 
753 	data_len = NLMSG_HDRLEN + nla_total_size(sizeof(uint32_t));
754 	vendor_event = wlan_cfg80211_vendor_event_alloc(
755 				hdd_ctx->wiphy, NULL, data_len,
756 				QCA_NL80211_VENDOR_SUBCMD_THERMAL_INDEX,
757 				GFP_KERNEL);
758 	if (!vendor_event) {
759 		hdd_err("wlan_cfg80211_vendor_event_alloc failed");
760 		return QDF_STATUS_E_NOMEM;
761 	}
762 	level = convert_level_to_vendor_thermal_level(info->level);
763 	if (nla_put_u32(vendor_event,
764 			QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_LEVEL,
765 			level)) {
766 		wlan_cfg80211_vendor_free_skb(vendor_event);
767 		return QDF_STATUS_E_INVAL;
768 	}
769 	hdd_debug("thermal_throttle:level %d vendor level %d", info->level,
770 		  level);
771 	wlan_cfg80211_vendor_event(vendor_event, GFP_KERNEL);
772 
773 	return QDF_STATUS_SUCCESS;
774 }
775 
hdd_thermal_register_callbacks(struct hdd_context * hdd_ctx)776 void hdd_thermal_register_callbacks(struct hdd_context *hdd_ctx)
777 {
778 	struct fwol_thermal_callbacks cb_obj = {0};
779 
780 	cb_obj.notify_thermal_throttle_handler =
781 		hdd_notify_thermal_throttle_handler;
782 	ucfg_fwol_thermal_register_callbacks(hdd_ctx->psoc, &cb_obj);
783 }
784 
hdd_thermal_unregister_callbacks(struct hdd_context * hdd_ctx)785 void hdd_thermal_unregister_callbacks(struct hdd_context *hdd_ctx)
786 {
787 	ucfg_fwol_thermal_unregister_callbacks(hdd_ctx->psoc);
788 }
789 #endif
790