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(¶ms);
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