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