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 ret = iwl_mvm_get_temp(mvm, &temp);
302
303 __iwl_mvm_mac_stop(mvm);
304
305 if (ret)
306 goto reschedule;
307
308 IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", temp);
309
310 if (temp <= tt->params.ct_kill_exit) {
311 mutex_unlock(&mvm->mutex);
312 iwl_mvm_exit_ctkill(mvm);
313 return;
314 }
315
316reschedule:
317 mutex_unlock(&mvm->mutex);
318 schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit,
319 round_jiffies(duration * HZ));
320}
321
322static void iwl_mvm_tt_smps_iterator(void *_data, u8 *mac,
323 struct ieee80211_vif *vif)
324{
325 struct iwl_mvm *mvm = _data;
326 enum ieee80211_smps_mode smps_mode;
327
328 lockdep_assert_held(&mvm->mutex);
329
330 if (mvm->thermal_throttle.dynamic_smps)
331 smps_mode = IEEE80211_SMPS_DYNAMIC;
332 else
333 smps_mode = IEEE80211_SMPS_AUTOMATIC;
334
335 if (vif->type != NL80211_IFTYPE_STATION)
336 return;
337
338 iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT, smps_mode);
339}
340
341static void iwl_mvm_tt_tx_protection(struct iwl_mvm *mvm, bool enable)
342{
343 struct iwl_mvm_sta *mvmsta;
344 int i, err;
345
346 for (i = 0; i < ARRAY_SIZE(mvm->fw_id_to_mac_id); i++) {
347 mvmsta = iwl_mvm_sta_from_staid_protected(mvm, i);
348 if (!mvmsta)
349 continue;
350
351 if (enable == mvmsta->tt_tx_protection)
352 continue;
353 err = iwl_mvm_tx_protection(mvm, mvmsta, enable);
354 if (err) {
355 IWL_ERR(mvm, "Failed to %s Tx protection\n",
356 enable ? "enable" : "disable");
357 } else {
358 IWL_DEBUG_TEMP(mvm, "%s Tx protection\n",
359 enable ? "Enable" : "Disable");
360 mvmsta->tt_tx_protection = enable;
361 }
362 }
363}
364
365void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff)
366{
367 struct iwl_host_cmd cmd = {
368 .id = REPLY_THERMAL_MNG_BACKOFF,
369 .len = { sizeof(u32), },
370 .data = { &backoff, },
371 };
372
373 backoff = max(backoff, mvm->thermal_throttle.min_backoff);
374
375 if (iwl_mvm_send_cmd(mvm, &cmd) == 0) {
376 IWL_DEBUG_TEMP(mvm, "Set Thermal Tx backoff to: %u\n",
377 backoff);
378 mvm->thermal_throttle.tx_backoff = backoff;
379 } else {
380 IWL_ERR(mvm, "Failed to change Thermal Tx backoff\n");
381 }
382}
383
384void iwl_mvm_tt_handler(struct iwl_mvm *mvm)
385{
386 struct iwl_tt_params *params = &mvm->thermal_throttle.params;
387 struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
388 s32 temperature = mvm->temperature;
389 bool throttle_enable = false;
390 int i;
391 u32 tx_backoff;
392
393 IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", mvm->temperature);
394
395 if (params->support_ct_kill && temperature >= params->ct_kill_entry) {
396 iwl_mvm_enter_ctkill(mvm);
397 return;
398 }
399
400 if (params->support_ct_kill &&
401 temperature <= params->ct_kill_exit) {
402 iwl_mvm_exit_ctkill(mvm);
403 return;
404 }
405
406 if (params->support_dynamic_smps) {
407 if (!tt->dynamic_smps &&
408 temperature >= params->dynamic_smps_entry) {
409 IWL_DEBUG_TEMP(mvm, "Enable dynamic SMPS\n");
410 tt->dynamic_smps = true;
411 ieee80211_iterate_active_interfaces_atomic(
412 mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
413 iwl_mvm_tt_smps_iterator, mvm);
414 throttle_enable = true;
415 } else if (tt->dynamic_smps &&
416 temperature <= params->dynamic_smps_exit) {
417 IWL_DEBUG_TEMP(mvm, "Disable dynamic SMPS\n");
418 tt->dynamic_smps = false;
419 ieee80211_iterate_active_interfaces_atomic(
420 mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
421 iwl_mvm_tt_smps_iterator, mvm);
422 }
423 }
424
425 if (params->support_tx_protection) {
426 if (temperature >= params->tx_protection_entry) {
427 iwl_mvm_tt_tx_protection(mvm, true);
428 throttle_enable = true;
429 } else if (temperature <= params->tx_protection_exit) {
430 iwl_mvm_tt_tx_protection(mvm, false);
431 }
432 }
433
434 if (params->support_tx_backoff) {
435 tx_backoff = tt->min_backoff;
436 for (i = 0; i < TT_TX_BACKOFF_SIZE; i++) {
437 if (temperature < params->tx_backoff[i].temperature)
438 break;
439 tx_backoff = max(tt->min_backoff,
440 params->tx_backoff[i].backoff);
441 }
442 if (tx_backoff != tt->min_backoff)
443 throttle_enable = true;
444 if (tt->tx_backoff != tx_backoff)
445 iwl_mvm_tt_tx_backoff(mvm, tx_backoff);
446 }
447
448 if (!tt->throttle && throttle_enable) {
449 IWL_WARN(mvm,
450 "Due to high temperature thermal throttling initiated\n");
451 tt->throttle = true;
452 } else if (tt->throttle && !tt->dynamic_smps &&
453 tt->tx_backoff == tt->min_backoff &&
454 temperature <= params->tx_protection_exit) {
455 IWL_WARN(mvm,
456 "Temperature is back to normal thermal throttling stopped\n");
457 tt->throttle = false;
458 }
459}
460
461static const struct iwl_tt_params iwl_mvm_default_tt_params = {
462 .ct_kill_entry = 118,
463 .ct_kill_exit = 96,
464 .ct_kill_duration = 5,
465 .dynamic_smps_entry = 114,
466 .dynamic_smps_exit = 110,
467 .tx_protection_entry = 114,
468 .tx_protection_exit = 108,
469 .tx_backoff = {
470 {.temperature = 112, .backoff = 200},
471 {.temperature = 113, .backoff = 600},
472 {.temperature = 114, .backoff = 1200},
473 {.temperature = 115, .backoff = 2000},
474 {.temperature = 116, .backoff = 4000},
475 {.temperature = 117, .backoff = 10000},
476 },
477 .support_ct_kill = true,
478 .support_dynamic_smps = true,
479 .support_tx_protection = true,
480 .support_tx_backoff = true,
481};
482
483
484static const u32 iwl_mvm_cdev_budgets[] = {
485 2000,
486 1800,
487 1600,
488 1400,
489 1200,
490 1000,
491 900,
492 800,
493 700,
494 650,
495 600,
496 550,
497 500,
498 450,
499 400,
500 350,
501 300,
502 250,
503 200,
504 150,
505};
506
507int iwl_mvm_ctdp_command(struct iwl_mvm *mvm, u32 op, u32 state)
508{
509 struct iwl_mvm_ctdp_cmd cmd = {
510 .operation = cpu_to_le32(op),
511 .budget = cpu_to_le32(iwl_mvm_cdev_budgets[state]),
512 .window_size = 0,
513 };
514 int ret;
515 u32 status;
516
517 lockdep_assert_held(&mvm->mutex);
518
519 status = 0;
520 ret = iwl_mvm_send_cmd_pdu_status(mvm, WIDE_ID(PHY_OPS_GROUP,
521 CTDP_CONFIG_CMD),
522 sizeof(cmd), &cmd, &status);
523
524 if (ret) {
525 IWL_ERR(mvm, "cTDP command failed (err=%d)\n", ret);
526 return ret;
527 }
528
529 switch (op) {
530 case CTDP_CMD_OPERATION_START:
531#ifdef CONFIG_THERMAL
532 mvm->cooling_dev.cur_state = state;
533#endif
534 break;
535 case CTDP_CMD_OPERATION_REPORT:
536 IWL_DEBUG_TEMP(mvm, "cTDP avg energy in mWatt = %d\n", status);
537
538
539
540
541
542
543 return status;
544 case CTDP_CMD_OPERATION_STOP:
545 IWL_DEBUG_TEMP(mvm, "cTDP stopped successfully\n");
546 break;
547 }
548
549 return 0;
550}
551
552#ifdef CONFIG_THERMAL
553static int compare_temps(const void *a, const void *b)
554{
555 return ((s16)le16_to_cpu(*(__le16 *)a) -
556 (s16)le16_to_cpu(*(__le16 *)b));
557}
558#endif
559
560int iwl_mvm_send_temp_report_ths_cmd(struct iwl_mvm *mvm)
561{
562 struct temp_report_ths_cmd cmd = {0};
563 int ret;
564#ifdef CONFIG_THERMAL
565 int i, j, idx = 0;
566
567 lockdep_assert_held(&mvm->mutex);
568
569 if (!mvm->tz_device.tzone)
570 goto send;
571
572
573
574
575
576
577 for (i = 0; i < IWL_MAX_DTS_TRIPS; i++) {
578 if (mvm->tz_device.temp_trips[i] != S16_MIN) {
579 cmd.thresholds[idx++] =
580 cpu_to_le16(mvm->tz_device.temp_trips[i]);
581 }
582 }
583 cmd.num_temps = cpu_to_le32(idx);
584
585 if (!idx)
586 goto send;
587
588
589 sort(cmd.thresholds, idx, sizeof(s16), compare_temps, NULL);
590
591
592
593
594 for (i = 0; i < idx; i++) {
595 for (j = 0; j < IWL_MAX_DTS_TRIPS; j++) {
596 if (le16_to_cpu(cmd.thresholds[i]) ==
597 mvm->tz_device.temp_trips[j])
598 mvm->tz_device.fw_trips_index[i] = j;
599 }
600 }
601
602send:
603#endif
604 ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(PHY_OPS_GROUP,
605 TEMP_REPORTING_THRESHOLDS_CMD),
606 0, sizeof(cmd), &cmd);
607 if (ret)
608 IWL_ERR(mvm, "TEMP_REPORT_THS_CMD command failed (err=%d)\n",
609 ret);
610
611 return ret;
612}
613
614#ifdef CONFIG_THERMAL
615static int iwl_mvm_tzone_get_temp(struct thermal_zone_device *device,
616 int *temperature)
617{
618 struct iwl_mvm *mvm = (struct iwl_mvm *)device->devdata;
619 int ret;
620 int temp;
621
622 mutex_lock(&mvm->mutex);
623
624 if (!iwl_mvm_firmware_running(mvm) ||
625 mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) {
626 ret = -ENODATA;
627 goto out;
628 }
629
630 ret = iwl_mvm_get_temp(mvm, &temp);
631 if (ret)
632 goto out;
633
634 *temperature = temp * 1000;
635
636out:
637 mutex_unlock(&mvm->mutex);
638 return ret;
639}
640
641static int iwl_mvm_tzone_get_trip_temp(struct thermal_zone_device *device,
642 int trip, int *temp)
643{
644 struct iwl_mvm *mvm = (struct iwl_mvm *)device->devdata;
645
646 if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS)
647 return -EINVAL;
648
649 *temp = mvm->tz_device.temp_trips[trip] * 1000;
650
651 return 0;
652}
653
654static int iwl_mvm_tzone_get_trip_type(struct thermal_zone_device *device,
655 int trip, enum thermal_trip_type *type)
656{
657 if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS)
658 return -EINVAL;
659
660 *type = THERMAL_TRIP_PASSIVE;
661
662 return 0;
663}
664
665static int iwl_mvm_tzone_set_trip_temp(struct thermal_zone_device *device,
666 int trip, int temp)
667{
668 struct iwl_mvm *mvm = (struct iwl_mvm *)device->devdata;
669 struct iwl_mvm_thermal_device *tzone;
670 int i, ret;
671 s16 temperature;
672
673 mutex_lock(&mvm->mutex);
674
675 if (!iwl_mvm_firmware_running(mvm) ||
676 mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) {
677 ret = -EIO;
678 goto out;
679 }
680
681 if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS) {
682 ret = -EINVAL;
683 goto out;
684 }
685
686 if ((temp / 1000) > S16_MAX) {
687 ret = -EINVAL;
688 goto out;
689 }
690
691 temperature = (s16)(temp / 1000);
692 tzone = &mvm->tz_device;
693
694 if (!tzone) {
695 ret = -EIO;
696 goto out;
697 }
698
699
700 if (tzone->temp_trips[trip] == temperature) {
701 ret = 0;
702 goto out;
703 }
704
705
706 for (i = 0; i < IWL_MAX_DTS_TRIPS; i++) {
707 if (tzone->temp_trips[i] == temperature) {
708 ret = -EINVAL;
709 goto out;
710 }
711 }
712
713 tzone->temp_trips[trip] = temperature;
714
715 ret = iwl_mvm_send_temp_report_ths_cmd(mvm);
716out:
717 mutex_unlock(&mvm->mutex);
718 return ret;
719}
720
721static struct thermal_zone_device_ops tzone_ops = {
722 .get_temp = iwl_mvm_tzone_get_temp,
723 .get_trip_temp = iwl_mvm_tzone_get_trip_temp,
724 .get_trip_type = iwl_mvm_tzone_get_trip_type,
725 .set_trip_temp = iwl_mvm_tzone_set_trip_temp,
726};
727
728
729#define IWL_WRITABLE_TRIPS_MSK (BIT(IWL_MAX_DTS_TRIPS) - 1)
730
731static void iwl_mvm_thermal_zone_register(struct iwl_mvm *mvm)
732{
733 int i;
734 char name[] = "iwlwifi";
735
736 if (!iwl_mvm_is_tt_in_fw(mvm)) {
737 mvm->tz_device.tzone = NULL;
738
739 return;
740 }
741
742 BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);
743
744 mvm->tz_device.tzone = thermal_zone_device_register(name,
745 IWL_MAX_DTS_TRIPS,
746 IWL_WRITABLE_TRIPS_MSK,
747 mvm, &tzone_ops,
748 NULL, 0, 0);
749 if (IS_ERR(mvm->tz_device.tzone)) {
750 IWL_DEBUG_TEMP(mvm,
751 "Failed to register to thermal zone (err = %ld)\n",
752 PTR_ERR(mvm->tz_device.tzone));
753 mvm->tz_device.tzone = NULL;
754 return;
755 }
756
757
758
759
760 for (i = 0 ; i < IWL_MAX_DTS_TRIPS; i++)
761 mvm->tz_device.temp_trips[i] = S16_MIN;
762}
763
764static int iwl_mvm_tcool_get_max_state(struct thermal_cooling_device *cdev,
765 unsigned long *state)
766{
767 *state = ARRAY_SIZE(iwl_mvm_cdev_budgets) - 1;
768
769 return 0;
770}
771
772static int iwl_mvm_tcool_get_cur_state(struct thermal_cooling_device *cdev,
773 unsigned long *state)
774{
775 struct iwl_mvm *mvm = (struct iwl_mvm *)(cdev->devdata);
776
777 *state = mvm->cooling_dev.cur_state;
778
779 return 0;
780}
781
782static int iwl_mvm_tcool_set_cur_state(struct thermal_cooling_device *cdev,
783 unsigned long new_state)
784{
785 struct iwl_mvm *mvm = (struct iwl_mvm *)(cdev->devdata);
786 int ret;
787
788 mutex_lock(&mvm->mutex);
789
790 if (!iwl_mvm_firmware_running(mvm) ||
791 mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) {
792 ret = -EIO;
793 goto unlock;
794 }
795
796 if (new_state >= ARRAY_SIZE(iwl_mvm_cdev_budgets)) {
797 ret = -EINVAL;
798 goto unlock;
799 }
800
801 ret = iwl_mvm_ctdp_command(mvm, CTDP_CMD_OPERATION_START,
802 new_state);
803
804unlock:
805 mutex_unlock(&mvm->mutex);
806 return ret;
807}
808
809static const struct thermal_cooling_device_ops tcooling_ops = {
810 .get_max_state = iwl_mvm_tcool_get_max_state,
811 .get_cur_state = iwl_mvm_tcool_get_cur_state,
812 .set_cur_state = iwl_mvm_tcool_set_cur_state,
813};
814
815static void iwl_mvm_cooling_device_register(struct iwl_mvm *mvm)
816{
817 char name[] = "iwlwifi";
818
819 if (!iwl_mvm_is_ctdp_supported(mvm))
820 return;
821
822 BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);
823
824 mvm->cooling_dev.cdev =
825 thermal_cooling_device_register(name,
826 mvm,
827 &tcooling_ops);
828
829 if (IS_ERR(mvm->cooling_dev.cdev)) {
830 IWL_DEBUG_TEMP(mvm,
831 "Failed to register to cooling device (err = %ld)\n",
832 PTR_ERR(mvm->cooling_dev.cdev));
833 mvm->cooling_dev.cdev = NULL;
834 return;
835 }
836}
837
838static void iwl_mvm_thermal_zone_unregister(struct iwl_mvm *mvm)
839{
840 if (!iwl_mvm_is_tt_in_fw(mvm) || !mvm->tz_device.tzone)
841 return;
842
843 IWL_DEBUG_TEMP(mvm, "Thermal zone device unregister\n");
844 if (mvm->tz_device.tzone) {
845 thermal_zone_device_unregister(mvm->tz_device.tzone);
846 mvm->tz_device.tzone = NULL;
847 }
848}
849
850static void iwl_mvm_cooling_device_unregister(struct iwl_mvm *mvm)
851{
852 if (!iwl_mvm_is_ctdp_supported(mvm) || !mvm->cooling_dev.cdev)
853 return;
854
855 IWL_DEBUG_TEMP(mvm, "Cooling device unregister\n");
856 if (mvm->cooling_dev.cdev) {
857 thermal_cooling_device_unregister(mvm->cooling_dev.cdev);
858 mvm->cooling_dev.cdev = NULL;
859 }
860}
861#endif
862
863void iwl_mvm_thermal_initialize(struct iwl_mvm *mvm, u32 min_backoff)
864{
865 struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
866
867 IWL_DEBUG_TEMP(mvm, "Initialize Thermal Throttling\n");
868
869 if (mvm->cfg->thermal_params)
870 tt->params = *mvm->cfg->thermal_params;
871 else
872 tt->params = iwl_mvm_default_tt_params;
873
874 tt->throttle = false;
875 tt->dynamic_smps = false;
876 tt->min_backoff = min_backoff;
877 INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill);
878
879#ifdef CONFIG_THERMAL
880 iwl_mvm_cooling_device_register(mvm);
881 iwl_mvm_thermal_zone_register(mvm);
882#endif
883 mvm->init_status |= IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE;
884}
885
886void iwl_mvm_thermal_exit(struct iwl_mvm *mvm)
887{
888 if (!(mvm->init_status & IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE))
889 return;
890
891 cancel_delayed_work_sync(&mvm->thermal_throttle.ct_kill_exit);
892 IWL_DEBUG_TEMP(mvm, "Exit Thermal Throttling\n");
893
894#ifdef CONFIG_THERMAL
895 iwl_mvm_cooling_device_unregister(mvm);
896 iwl_mvm_thermal_zone_unregister(mvm);
897#endif
898 mvm->init_status &= ~IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE;
899}
900