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