1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63#include <linux/sort.h>
64
65#include "mvm.h"
66
67#define IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT HZ
68
69void iwl_mvm_enter_ctkill(struct iwl_mvm *mvm)
70{
71 struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
72 u32 duration = tt->params.ct_kill_duration;
73
74 if (test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status))
75 return;
76
77 IWL_ERR(mvm, "Enter CT Kill\n");
78 iwl_mvm_set_hw_ctkill_state(mvm, true);
79
80 if (!iwl_mvm_is_tt_in_fw(mvm)) {
81 tt->throttle = false;
82 tt->dynamic_smps = false;
83 }
84
85
86
87
88
89 if (!mvm->temperature_test)
90 schedule_delayed_work(&tt->ct_kill_exit,
91 round_jiffies_relative(duration * HZ));
92}
93
94static void iwl_mvm_exit_ctkill(struct iwl_mvm *mvm)
95{
96 if (!test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status))
97 return;
98
99 IWL_ERR(mvm, "Exit CT Kill\n");
100 iwl_mvm_set_hw_ctkill_state(mvm, false);
101}
102
103void iwl_mvm_tt_temp_changed(struct iwl_mvm *mvm, u32 temp)
104{
105
106 if (mvm->temperature_test)
107 return;
108
109 if (mvm->temperature == temp)
110 return;
111
112 mvm->temperature = temp;
113 iwl_mvm_tt_handler(mvm);
114}
115
116static int iwl_mvm_temp_notif_parse(struct iwl_mvm *mvm,
117 struct iwl_rx_packet *pkt)
118{
119 struct iwl_dts_measurement_notif_v1 *notif_v1;
120 int len = iwl_rx_packet_payload_len(pkt);
121 int temp;
122
123
124
125
126 if (WARN_ON_ONCE(len < sizeof(*notif_v1))) {
127 IWL_ERR(mvm, "Invalid DTS_MEASUREMENT_NOTIFICATION\n");
128 return -EINVAL;
129 }
130
131 notif_v1 = (void *)pkt->data;
132
133 temp = le32_to_cpu(notif_v1->temp);
134
135
136 if (WARN_ON_ONCE(temp < 0))
137 temp = 0;
138
139 IWL_DEBUG_TEMP(mvm, "DTS_MEASUREMENT_NOTIFICATION - %d\n", temp);
140
141 return temp;
142}
143
144static bool iwl_mvm_temp_notif_wait(struct iwl_notif_wait_data *notif_wait,
145 struct iwl_rx_packet *pkt, void *data)
146{
147 struct iwl_mvm *mvm =
148 container_of(notif_wait, struct iwl_mvm, notif_wait);
149 int *temp = data;
150 int ret;
151
152 ret = iwl_mvm_temp_notif_parse(mvm, pkt);
153 if (ret < 0)
154 return true;
155
156 *temp = ret;
157
158 return true;
159}
160
161void iwl_mvm_temp_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
162{
163 struct iwl_rx_packet *pkt = rxb_addr(rxb);
164 struct iwl_dts_measurement_notif_v2 *notif_v2;
165 int len = iwl_rx_packet_payload_len(pkt);
166 int temp;
167 u32 ths_crossed;
168
169
170 if (test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status))
171 return;
172
173 temp = iwl_mvm_temp_notif_parse(mvm, pkt);
174
175 if (!iwl_mvm_is_tt_in_fw(mvm)) {
176 if (temp >= 0)
177 iwl_mvm_tt_temp_changed(mvm, temp);
178 return;
179 }
180
181 if (WARN_ON_ONCE(len < sizeof(*notif_v2))) {
182 IWL_ERR(mvm, "Invalid DTS_MEASUREMENT_NOTIFICATION\n");
183 return;
184 }
185
186 notif_v2 = (void *)pkt->data;
187 ths_crossed = le32_to_cpu(notif_v2->threshold_idx);
188
189
190
191
192 if (ths_crossed == 0xFF)
193 return;
194
195 IWL_DEBUG_TEMP(mvm, "Temp = %d Threshold crossed = %d\n",
196 temp, ths_crossed);
197
198#ifdef CONFIG_THERMAL
199 if (WARN_ON(ths_crossed >= IWL_MAX_DTS_TRIPS))
200 return;
201
202 if (mvm->tz_device.tzone) {
203 struct iwl_mvm_thermal_device *tz_dev = &mvm->tz_device;
204
205 thermal_notify_framework(tz_dev->tzone,
206 tz_dev->fw_trips_index[ths_crossed]);
207 }
208#endif
209}
210
211void iwl_mvm_ct_kill_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
212{
213 struct iwl_rx_packet *pkt = rxb_addr(rxb);
214 struct ct_kill_notif *notif;
215 int len = iwl_rx_packet_payload_len(pkt);
216
217 if (WARN_ON_ONCE(len != sizeof(*notif))) {
218 IWL_ERR(mvm, "Invalid CT_KILL_NOTIFICATION\n");
219 return;
220 }
221
222 notif = (struct ct_kill_notif *)pkt->data;
223 IWL_DEBUG_TEMP(mvm, "CT Kill notification temperature = %d\n",
224 notif->temperature);
225
226 iwl_mvm_enter_ctkill(mvm);
227}
228
229static int iwl_mvm_get_temp_cmd(struct iwl_mvm *mvm)
230{
231 struct iwl_dts_measurement_cmd cmd = {
232 .flags = cpu_to_le32(DTS_TRIGGER_CMD_FLAGS_TEMP),
233 };
234 struct iwl_ext_dts_measurement_cmd extcmd = {
235 .control_mode = cpu_to_le32(DTS_AUTOMATIC),
236 };
237 u32 cmdid;
238
239 cmdid = iwl_cmd_id(CMD_DTS_MEASUREMENT_TRIGGER_WIDE,
240 PHY_OPS_GROUP, 0);
241
242 if (!fw_has_capa(&mvm->fw->ucode_capa,
243 IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE))
244 return iwl_mvm_send_cmd_pdu(mvm, cmdid, 0, sizeof(cmd), &cmd);
245
246 return iwl_mvm_send_cmd_pdu(mvm, cmdid, 0, sizeof(extcmd), &extcmd);
247}
248
249int iwl_mvm_get_temp(struct iwl_mvm *mvm, s32 *temp)
250{
251 struct iwl_notification_wait wait_temp_notif;
252 static u16 temp_notif[] = { WIDE_ID(PHY_OPS_GROUP,
253 DTS_MEASUREMENT_NOTIF_WIDE) };
254 int ret;
255
256 lockdep_assert_held(&mvm->mutex);
257
258 iwl_init_notification_wait(&mvm->notif_wait, &wait_temp_notif,
259 temp_notif, ARRAY_SIZE(temp_notif),
260 iwl_mvm_temp_notif_wait, temp);
261
262 ret = iwl_mvm_get_temp_cmd(mvm);
263 if (ret) {
264 IWL_ERR(mvm, "Failed to get the temperature (err=%d)\n", ret);
265 iwl_remove_notification(&mvm->notif_wait, &wait_temp_notif);
266 return ret;
267 }
268
269 ret = iwl_wait_notification(&mvm->notif_wait, &wait_temp_notif,
270 IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT);
271 if (ret)
272 IWL_ERR(mvm, "Getting the temperature timed out\n");
273
274 return ret;
275}
276
277static void check_exit_ctkill(struct work_struct *work)
278{
279 struct iwl_mvm_tt_mgmt *tt;
280 struct iwl_mvm *mvm;
281 u32 duration;
282 s32 temp;
283 int ret;
284
285 tt = container_of(work, struct iwl_mvm_tt_mgmt, ct_kill_exit.work);
286 mvm = container_of(tt, struct iwl_mvm, thermal_throttle);
287
288 if (iwl_mvm_is_tt_in_fw(mvm)) {
289 iwl_mvm_exit_ctkill(mvm);
290
291 return;
292 }
293
294 duration = tt->params.ct_kill_duration;
295
296 mutex_lock(&mvm->mutex);
297
298 if (__iwl_mvm_mac_start(mvm))
299 goto reschedule;
300
301
302 if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_CHECK_CTKILL)) {
303 __iwl_mvm_mac_stop(mvm);
304 goto reschedule;
305 }
306
307 ret = iwl_mvm_get_temp(mvm, &temp);
308
309 iwl_mvm_unref(mvm, IWL_MVM_REF_CHECK_CTKILL);
310
311 __iwl_mvm_mac_stop(mvm);
312
313 if (ret)
314 goto reschedule;
315
316 IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", temp);
317
318 if (temp <= tt->params.ct_kill_exit) {
319 mutex_unlock(&mvm->mutex);
320 iwl_mvm_exit_ctkill(mvm);
321 return;
322 }
323
324reschedule:
325 mutex_unlock(&mvm->mutex);
326 schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit,
327 round_jiffies(duration * HZ));
328}
329
330static void iwl_mvm_tt_smps_iterator(void *_data, u8 *mac,
331 struct ieee80211_vif *vif)
332{
333 struct iwl_mvm *mvm = _data;
334 enum ieee80211_smps_mode smps_mode;
335
336 lockdep_assert_held(&mvm->mutex);
337
338 if (mvm->thermal_throttle.dynamic_smps)
339 smps_mode = IEEE80211_SMPS_DYNAMIC;
340 else
341 smps_mode = IEEE80211_SMPS_AUTOMATIC;
342
343 if (vif->type != NL80211_IFTYPE_STATION)
344 return;
345
346 iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT, smps_mode);
347}
348
349static void iwl_mvm_tt_tx_protection(struct iwl_mvm *mvm, bool enable)
350{
351 struct iwl_mvm_sta *mvmsta;
352 int i, err;
353
354 for (i = 0; i < ARRAY_SIZE(mvm->fw_id_to_mac_id); i++) {
355 mvmsta = iwl_mvm_sta_from_staid_protected(mvm, i);
356 if (!mvmsta)
357 continue;
358
359 if (enable == mvmsta->tt_tx_protection)
360 continue;
361 err = iwl_mvm_tx_protection(mvm, mvmsta, enable);
362 if (err) {
363 IWL_ERR(mvm, "Failed to %s Tx protection\n",
364 enable ? "enable" : "disable");
365 } else {
366 IWL_DEBUG_TEMP(mvm, "%s Tx protection\n",
367 enable ? "Enable" : "Disable");
368 mvmsta->tt_tx_protection = enable;
369 }
370 }
371}
372
373void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff)
374{
375 struct iwl_host_cmd cmd = {
376 .id = REPLY_THERMAL_MNG_BACKOFF,
377 .len = { sizeof(u32), },
378 .data = { &backoff, },
379 };
380
381 backoff = max(backoff, mvm->thermal_throttle.min_backoff);
382
383 if (iwl_mvm_send_cmd(mvm, &cmd) == 0) {
384 IWL_DEBUG_TEMP(mvm, "Set Thermal Tx backoff to: %u\n",
385 backoff);
386 mvm->thermal_throttle.tx_backoff = backoff;
387 } else {
388 IWL_ERR(mvm, "Failed to change Thermal Tx backoff\n");
389 }
390}
391
392void iwl_mvm_tt_handler(struct iwl_mvm *mvm)
393{
394 struct iwl_tt_params *params = &mvm->thermal_throttle.params;
395 struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
396 s32 temperature = mvm->temperature;
397 bool throttle_enable = false;
398 int i;
399 u32 tx_backoff;
400
401 IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", mvm->temperature);
402
403 if (params->support_ct_kill && temperature >= params->ct_kill_entry) {
404 iwl_mvm_enter_ctkill(mvm);
405 return;
406 }
407
408 if (params->support_ct_kill &&
409 temperature <= params->ct_kill_exit) {
410 iwl_mvm_exit_ctkill(mvm);
411 return;
412 }
413
414 if (params->support_dynamic_smps) {
415 if (!tt->dynamic_smps &&
416 temperature >= params->dynamic_smps_entry) {
417 IWL_DEBUG_TEMP(mvm, "Enable dynamic SMPS\n");
418 tt->dynamic_smps = true;
419 ieee80211_iterate_active_interfaces_atomic(
420 mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
421 iwl_mvm_tt_smps_iterator, mvm);
422 throttle_enable = true;
423 } else if (tt->dynamic_smps &&
424 temperature <= params->dynamic_smps_exit) {
425 IWL_DEBUG_TEMP(mvm, "Disable dynamic SMPS\n");
426 tt->dynamic_smps = false;
427 ieee80211_iterate_active_interfaces_atomic(
428 mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
429 iwl_mvm_tt_smps_iterator, mvm);
430 }
431 }
432
433 if (params->support_tx_protection) {
434 if (temperature >= params->tx_protection_entry) {
435 iwl_mvm_tt_tx_protection(mvm, true);
436 throttle_enable = true;
437 } else if (temperature <= params->tx_protection_exit) {
438 iwl_mvm_tt_tx_protection(mvm, false);
439 }
440 }
441
442 if (params->support_tx_backoff) {
443 tx_backoff = tt->min_backoff;
444 for (i = 0; i < TT_TX_BACKOFF_SIZE; i++) {
445 if (temperature < params->tx_backoff[i].temperature)
446 break;
447 tx_backoff = max(tt->min_backoff,
448 params->tx_backoff[i].backoff);
449 }
450 if (tx_backoff != tt->min_backoff)
451 throttle_enable = true;
452 if (tt->tx_backoff != tx_backoff)
453 iwl_mvm_tt_tx_backoff(mvm, tx_backoff);
454 }
455
456 if (!tt->throttle && throttle_enable) {
457 IWL_WARN(mvm,
458 "Due to high temperature thermal throttling initiated\n");
459 tt->throttle = true;
460 } else if (tt->throttle && !tt->dynamic_smps &&
461 tt->tx_backoff == tt->min_backoff &&
462 temperature <= params->tx_protection_exit) {
463 IWL_WARN(mvm,
464 "Temperature is back to normal thermal throttling stopped\n");
465 tt->throttle = false;
466 }
467}
468
469static const struct iwl_tt_params iwl_mvm_default_tt_params = {
470 .ct_kill_entry = 118,
471 .ct_kill_exit = 96,
472 .ct_kill_duration = 5,
473 .dynamic_smps_entry = 114,
474 .dynamic_smps_exit = 110,
475 .tx_protection_entry = 114,
476 .tx_protection_exit = 108,
477 .tx_backoff = {
478 {.temperature = 112, .backoff = 200},
479 {.temperature = 113, .backoff = 600},
480 {.temperature = 114, .backoff = 1200},
481 {.temperature = 115, .backoff = 2000},
482 {.temperature = 116, .backoff = 4000},
483 {.temperature = 117, .backoff = 10000},
484 },
485 .support_ct_kill = true,
486 .support_dynamic_smps = true,
487 .support_tx_protection = true,
488 .support_tx_backoff = true,
489};
490
491
492static const u32 iwl_mvm_cdev_budgets[] = {
493 2000,
494 1800,
495 1600,
496 1400,
497 1200,
498 1000,
499 900,
500 800,
501 700,
502 650,
503 600,
504 550,
505 500,
506 450,
507 400,
508 350,
509 300,
510 250,
511 200,
512 150,
513};
514
515int iwl_mvm_ctdp_command(struct iwl_mvm *mvm, u32 op, u32 state)
516{
517 struct iwl_mvm_ctdp_cmd cmd = {
518 .operation = cpu_to_le32(op),
519 .budget = cpu_to_le32(iwl_mvm_cdev_budgets[state]),
520 .window_size = 0,
521 };
522 int ret;
523 u32 status;
524
525 lockdep_assert_held(&mvm->mutex);
526
527 status = 0;
528 ret = iwl_mvm_send_cmd_pdu_status(mvm, WIDE_ID(PHY_OPS_GROUP,
529 CTDP_CONFIG_CMD),
530 sizeof(cmd), &cmd, &status);
531
532 if (ret) {
533 IWL_ERR(mvm, "cTDP command failed (err=%d)\n", ret);
534 return ret;
535 }
536
537 switch (op) {
538 case CTDP_CMD_OPERATION_START:
539#ifdef CONFIG_THERMAL
540 mvm->cooling_dev.cur_state = state;
541#endif
542 break;
543 case CTDP_CMD_OPERATION_REPORT:
544 IWL_DEBUG_TEMP(mvm, "cTDP avg energy in mWatt = %d\n", status);
545
546
547
548
549
550
551 return status;
552 case CTDP_CMD_OPERATION_STOP:
553 IWL_DEBUG_TEMP(mvm, "cTDP stopped successfully\n");
554 break;
555 }
556
557 return 0;
558}
559
560#ifdef CONFIG_THERMAL
561static int compare_temps(const void *a, const void *b)
562{
563 return ((s16)le16_to_cpu(*(__le16 *)a) -
564 (s16)le16_to_cpu(*(__le16 *)b));
565}
566
567int iwl_mvm_send_temp_report_ths_cmd(struct iwl_mvm *mvm)
568{
569 struct temp_report_ths_cmd cmd = {0};
570 int ret, i, j, idx = 0;
571
572 lockdep_assert_held(&mvm->mutex);
573
574 if (!mvm->tz_device.tzone)
575 return -EINVAL;
576
577
578
579
580
581
582 for (i = 0; i < IWL_MAX_DTS_TRIPS; i++) {
583 if (mvm->tz_device.temp_trips[i] != S16_MIN) {
584 cmd.thresholds[idx++] =
585 cpu_to_le16(mvm->tz_device.temp_trips[i]);
586 }
587 }
588 cmd.num_temps = cpu_to_le32(idx);
589
590 if (!idx)
591 goto send;
592
593
594 sort(cmd.thresholds, idx, sizeof(s16), compare_temps, NULL);
595
596
597
598
599 for (i = 0; i < idx; i++) {
600 for (j = 0; j < IWL_MAX_DTS_TRIPS; j++) {
601 if (le16_to_cpu(cmd.thresholds[i]) ==
602 mvm->tz_device.temp_trips[j])
603 mvm->tz_device.fw_trips_index[i] = j;
604 }
605 }
606
607send:
608 ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(PHY_OPS_GROUP,
609 TEMP_REPORTING_THRESHOLDS_CMD),
610 0, sizeof(cmd), &cmd);
611 if (ret)
612 IWL_ERR(mvm, "TEMP_REPORT_THS_CMD command failed (err=%d)\n",
613 ret);
614
615 return ret;
616}
617
618static int iwl_mvm_tzone_get_temp(struct thermal_zone_device *device,
619 int *temperature)
620{
621 struct iwl_mvm *mvm = (struct iwl_mvm *)device->devdata;
622 int ret;
623 int temp;
624
625 mutex_lock(&mvm->mutex);
626
627 if (!iwl_mvm_firmware_running(mvm) ||
628 mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) {
629 ret = -ENODATA;
630 goto out;
631 }
632
633 ret = iwl_mvm_get_temp(mvm, &temp);
634 if (ret)
635 goto out;
636
637 *temperature = temp * 1000;
638
639out:
640 mutex_unlock(&mvm->mutex);
641 return ret;
642}
643
644static int iwl_mvm_tzone_get_trip_temp(struct thermal_zone_device *device,
645 int trip, int *temp)
646{
647 struct iwl_mvm *mvm = (struct iwl_mvm *)device->devdata;
648
649 if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS)
650 return -EINVAL;
651
652 *temp = mvm->tz_device.temp_trips[trip] * 1000;
653
654 return 0;
655}
656
657static int iwl_mvm_tzone_get_trip_type(struct thermal_zone_device *device,
658 int trip, enum thermal_trip_type *type)
659{
660 if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS)
661 return -EINVAL;
662
663 *type = THERMAL_TRIP_PASSIVE;
664
665 return 0;
666}
667
668static int iwl_mvm_tzone_set_trip_temp(struct thermal_zone_device *device,
669 int trip, int temp)
670{
671 struct iwl_mvm *mvm = (struct iwl_mvm *)device->devdata;
672 struct iwl_mvm_thermal_device *tzone;
673 int i, ret;
674 s16 temperature;
675
676 mutex_lock(&mvm->mutex);
677
678 if (!iwl_mvm_firmware_running(mvm) ||
679 mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) {
680 ret = -EIO;
681 goto out;
682 }
683
684 if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS) {
685 ret = -EINVAL;
686 goto out;
687 }
688
689 if ((temp / 1000) > S16_MAX) {
690 ret = -EINVAL;
691 goto out;
692 }
693
694 temperature = (s16)(temp / 1000);
695 tzone = &mvm->tz_device;
696
697 if (!tzone) {
698 ret = -EIO;
699 goto out;
700 }
701
702
703 if (tzone->temp_trips[trip] == temperature) {
704 ret = 0;
705 goto out;
706 }
707
708
709 for (i = 0; i < IWL_MAX_DTS_TRIPS; i++) {
710 if (tzone->temp_trips[i] == temperature) {
711 ret = -EINVAL;
712 goto out;
713 }
714 }
715
716 tzone->temp_trips[trip] = temperature;
717
718 ret = iwl_mvm_send_temp_report_ths_cmd(mvm);
719out:
720 mutex_unlock(&mvm->mutex);
721 return ret;
722}
723
724static struct thermal_zone_device_ops tzone_ops = {
725 .get_temp = iwl_mvm_tzone_get_temp,
726 .get_trip_temp = iwl_mvm_tzone_get_trip_temp,
727 .get_trip_type = iwl_mvm_tzone_get_trip_type,
728 .set_trip_temp = iwl_mvm_tzone_set_trip_temp,
729};
730
731
732#define IWL_WRITABLE_TRIPS_MSK (BIT(IWL_MAX_DTS_TRIPS) - 1)
733
734static void iwl_mvm_thermal_zone_register(struct iwl_mvm *mvm)
735{
736 int i;
737 char name[] = "iwlwifi";
738
739 if (!iwl_mvm_is_tt_in_fw(mvm)) {
740 mvm->tz_device.tzone = NULL;
741
742 return;
743 }
744
745 BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);
746
747 mvm->tz_device.tzone = thermal_zone_device_register(name,
748 IWL_MAX_DTS_TRIPS,
749 IWL_WRITABLE_TRIPS_MSK,
750 mvm, &tzone_ops,
751 NULL, 0, 0);
752 if (IS_ERR(mvm->tz_device.tzone)) {
753 IWL_DEBUG_TEMP(mvm,
754 "Failed to register to thermal zone (err = %ld)\n",
755 PTR_ERR(mvm->tz_device.tzone));
756 mvm->tz_device.tzone = NULL;
757 return;
758 }
759
760
761
762
763 for (i = 0 ; i < IWL_MAX_DTS_TRIPS; i++)
764 mvm->tz_device.temp_trips[i] = S16_MIN;
765}
766
767static int iwl_mvm_tcool_get_max_state(struct thermal_cooling_device *cdev,
768 unsigned long *state)
769{
770 *state = ARRAY_SIZE(iwl_mvm_cdev_budgets) - 1;
771
772 return 0;
773}
774
775static int iwl_mvm_tcool_get_cur_state(struct thermal_cooling_device *cdev,
776 unsigned long *state)
777{
778 struct iwl_mvm *mvm = (struct iwl_mvm *)(cdev->devdata);
779
780 *state = mvm->cooling_dev.cur_state;
781
782 return 0;
783}
784
785static int iwl_mvm_tcool_set_cur_state(struct thermal_cooling_device *cdev,
786 unsigned long new_state)
787{
788 struct iwl_mvm *mvm = (struct iwl_mvm *)(cdev->devdata);
789 int ret;
790
791 mutex_lock(&mvm->mutex);
792
793 if (!iwl_mvm_firmware_running(mvm) ||
794 mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) {
795 ret = -EIO;
796 goto unlock;
797 }
798
799 if (new_state >= ARRAY_SIZE(iwl_mvm_cdev_budgets)) {
800 ret = -EINVAL;
801 goto unlock;
802 }
803
804 ret = iwl_mvm_ctdp_command(mvm, CTDP_CMD_OPERATION_START,
805 new_state);
806
807unlock:
808 mutex_unlock(&mvm->mutex);
809 return ret;
810}
811
812static const struct thermal_cooling_device_ops tcooling_ops = {
813 .get_max_state = iwl_mvm_tcool_get_max_state,
814 .get_cur_state = iwl_mvm_tcool_get_cur_state,
815 .set_cur_state = iwl_mvm_tcool_set_cur_state,
816};
817
818static void iwl_mvm_cooling_device_register(struct iwl_mvm *mvm)
819{
820 char name[] = "iwlwifi";
821
822 if (!iwl_mvm_is_ctdp_supported(mvm))
823 return;
824
825 BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);
826
827 mvm->cooling_dev.cdev =
828 thermal_cooling_device_register(name,
829 mvm,
830 &tcooling_ops);
831
832 if (IS_ERR(mvm->cooling_dev.cdev)) {
833 IWL_DEBUG_TEMP(mvm,
834 "Failed to register to cooling device (err = %ld)\n",
835 PTR_ERR(mvm->cooling_dev.cdev));
836 mvm->cooling_dev.cdev = NULL;
837 return;
838 }
839}
840
841static void iwl_mvm_thermal_zone_unregister(struct iwl_mvm *mvm)
842{
843 if (!iwl_mvm_is_tt_in_fw(mvm) || !mvm->tz_device.tzone)
844 return;
845
846 IWL_DEBUG_TEMP(mvm, "Thermal zone device unregister\n");
847 if (mvm->tz_device.tzone) {
848 thermal_zone_device_unregister(mvm->tz_device.tzone);
849 mvm->tz_device.tzone = NULL;
850 }
851}
852
853static void iwl_mvm_cooling_device_unregister(struct iwl_mvm *mvm)
854{
855 if (!iwl_mvm_is_ctdp_supported(mvm) || !mvm->cooling_dev.cdev)
856 return;
857
858 IWL_DEBUG_TEMP(mvm, "Cooling device unregister\n");
859 if (mvm->cooling_dev.cdev) {
860 thermal_cooling_device_unregister(mvm->cooling_dev.cdev);
861 mvm->cooling_dev.cdev = NULL;
862 }
863}
864#endif
865
866void iwl_mvm_thermal_initialize(struct iwl_mvm *mvm, u32 min_backoff)
867{
868 struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
869
870 IWL_DEBUG_TEMP(mvm, "Initialize Thermal Throttling\n");
871
872 if (mvm->cfg->thermal_params)
873 tt->params = *mvm->cfg->thermal_params;
874 else
875 tt->params = iwl_mvm_default_tt_params;
876
877 tt->throttle = false;
878 tt->dynamic_smps = false;
879 tt->min_backoff = min_backoff;
880 INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill);
881
882#ifdef CONFIG_THERMAL
883 iwl_mvm_cooling_device_register(mvm);
884 iwl_mvm_thermal_zone_register(mvm);
885#endif
886 mvm->init_status |= IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE;
887}
888
889void iwl_mvm_thermal_exit(struct iwl_mvm *mvm)
890{
891 if (!(mvm->init_status & IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE))
892 return;
893
894 cancel_delayed_work_sync(&mvm->thermal_throttle.ct_kill_exit);
895 IWL_DEBUG_TEMP(mvm, "Exit Thermal Throttling\n");
896
897#ifdef CONFIG_THERMAL
898 iwl_mvm_cooling_device_unregister(mvm);
899 iwl_mvm_thermal_zone_unregister(mvm);
900#endif
901 mvm->init_status &= ~IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE;
902}
903