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
47void 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_notify_framework(tz_dev->tzone,
150 tz_dev->fw_trips_index[ths_crossed]);
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 int len = iwl_rx_packet_payload_len(pkt);
160
161 if (WARN_ON_ONCE(len != sizeof(*notif))) {
162 IWL_ERR(mvm, "Invalid CT_KILL_NOTIFICATION\n");
163 return;
164 }
165
166 notif = (struct ct_kill_notif *)pkt->data;
167 IWL_DEBUG_TEMP(mvm, "CT Kill notification temperature = %d\n",
168 notif->temperature);
169
170 iwl_mvm_enter_ctkill(mvm);
171}
172
173
174
175
176
177static int iwl_mvm_send_temp_cmd(struct iwl_mvm *mvm, bool response, s32 *temp)
178{
179 struct iwl_host_cmd cmd = {};
180 struct iwl_dts_measurement_cmd dts_cmd = {
181 .flags = cpu_to_le32(DTS_TRIGGER_CMD_FLAGS_TEMP),
182 };
183 struct iwl_ext_dts_measurement_cmd ext_cmd = {
184 .control_mode = cpu_to_le32(DTS_DIRECT_WITHOUT_MEASURE),
185 };
186 struct iwl_dts_measurement_resp *resp;
187 void *cmd_ptr;
188 int ret;
189 u32 cmd_flags = 0;
190 u16 len;
191
192
193 if (fw_has_capa(&mvm->fw->ucode_capa,
194 IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE)) {
195 len = sizeof(ext_cmd);
196 cmd_ptr = &ext_cmd;
197 } else {
198 len = sizeof(dts_cmd);
199 cmd_ptr = &dts_cmd;
200 }
201
202 if (response) {
203 cmd_flags = CMD_WANT_SKB;
204 len = 0;
205 }
206
207 cmd.id = WIDE_ID(PHY_OPS_GROUP, CMD_DTS_MEASUREMENT_TRIGGER_WIDE);
208 cmd.len[0] = len;
209 cmd.flags = cmd_flags;
210 cmd.data[0] = cmd_ptr;
211
212 IWL_DEBUG_TEMP(mvm,
213 "Sending temperature measurement command - %s response\n",
214 response ? "with" : "without");
215 ret = iwl_mvm_send_cmd(mvm, &cmd);
216
217 if (ret) {
218 IWL_ERR(mvm,
219 "Failed to send the temperature measurement command (err=%d)\n",
220 ret);
221 return ret;
222 }
223
224 if (response) {
225 resp = (void *)cmd.resp_pkt->data;
226 *temp = le32_to_cpu(resp->temp);
227 IWL_DEBUG_TEMP(mvm,
228 "Got temperature measurement response: temp=%d\n",
229 *temp);
230 iwl_free_resp(&cmd);
231 }
232
233 return ret;
234}
235
236int iwl_mvm_get_temp(struct iwl_mvm *mvm, s32 *temp)
237{
238 struct iwl_notification_wait wait_temp_notif;
239 static u16 temp_notif[] = { WIDE_ID(PHY_OPS_GROUP,
240 DTS_MEASUREMENT_NOTIF_WIDE) };
241 int ret;
242 u8 cmd_ver;
243
244
245
246
247
248
249 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, PHY_OPS_GROUP,
250 CMD_DTS_MEASUREMENT_TRIGGER_WIDE,
251 IWL_FW_CMD_VER_UNKNOWN);
252 if (cmd_ver == 1)
253 return iwl_mvm_send_temp_cmd(mvm, true, temp);
254
255 lockdep_assert_held(&mvm->mutex);
256
257 iwl_init_notification_wait(&mvm->notif_wait, &wait_temp_notif,
258 temp_notif, ARRAY_SIZE(temp_notif),
259 iwl_mvm_temp_notif_wait, temp);
260
261 ret = iwl_mvm_send_temp_cmd(mvm, false, temp);
262 if (ret) {
263 iwl_remove_notification(&mvm->notif_wait, &wait_temp_notif);
264 return ret;
265 }
266
267 ret = iwl_wait_notification(&mvm->notif_wait, &wait_temp_notif,
268 IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT);
269 if (ret)
270 IWL_ERR(mvm, "Getting the temperature timed out\n");
271
272 return ret;
273}
274
275static void check_exit_ctkill(struct work_struct *work)
276{
277 struct iwl_mvm_tt_mgmt *tt;
278 struct iwl_mvm *mvm;
279 u32 duration;
280 s32 temp;
281 int ret;
282
283 tt = container_of(work, struct iwl_mvm_tt_mgmt, ct_kill_exit.work);
284 mvm = container_of(tt, struct iwl_mvm, thermal_throttle);
285
286 if (iwl_mvm_is_tt_in_fw(mvm)) {
287 iwl_mvm_exit_ctkill(mvm);
288
289 return;
290 }
291
292 duration = tt->params.ct_kill_duration;
293
294 flush_work(&mvm->roc_done_wk);
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 < mvm->fw->ucode_capa.num_stations; 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 2400,
486 2000,
487 1800,
488 1600,
489 1400,
490 1200,
491 1000,
492 900,
493 800,
494 700,
495 650,
496 600,
497 550,
498 500,
499 450,
500 400,
501 350,
502 300,
503 250,
504 200,
505 150,
506};
507
508int iwl_mvm_ctdp_command(struct iwl_mvm *mvm, u32 op, u32 state)
509{
510 struct iwl_mvm_ctdp_cmd cmd = {
511 .operation = cpu_to_le32(op),
512 .budget = cpu_to_le32(iwl_mvm_cdev_budgets[state]),
513 .window_size = 0,
514 };
515 int ret;
516 u32 status;
517
518 lockdep_assert_held(&mvm->mutex);
519
520 status = 0;
521 ret = iwl_mvm_send_cmd_pdu_status(mvm, WIDE_ID(PHY_OPS_GROUP,
522 CTDP_CONFIG_CMD),
523 sizeof(cmd), &cmd, &status);
524
525 if (ret) {
526 IWL_ERR(mvm, "cTDP command failed (err=%d)\n", ret);
527 return ret;
528 }
529
530 switch (op) {
531 case CTDP_CMD_OPERATION_START:
532#ifdef CONFIG_THERMAL
533 mvm->cooling_dev.cur_state = state;
534#endif
535 break;
536 case CTDP_CMD_OPERATION_REPORT:
537 IWL_DEBUG_TEMP(mvm, "cTDP avg energy in mWatt = %d\n", status);
538
539
540
541
542
543
544 return status;
545 case CTDP_CMD_OPERATION_STOP:
546 IWL_DEBUG_TEMP(mvm, "cTDP stopped successfully\n");
547 break;
548 }
549
550 return 0;
551}
552
553#ifdef CONFIG_THERMAL
554static int compare_temps(const void *a, const void *b)
555{
556 return ((s16)le16_to_cpu(*(__le16 *)a) -
557 (s16)le16_to_cpu(*(__le16 *)b));
558}
559#endif
560
561int iwl_mvm_send_temp_report_ths_cmd(struct iwl_mvm *mvm)
562{
563 struct temp_report_ths_cmd cmd = {0};
564 int ret;
565#ifdef CONFIG_THERMAL
566 int i, j, idx = 0;
567
568 lockdep_assert_held(&mvm->mutex);
569
570 if (!mvm->tz_device.tzone)
571 goto send;
572
573
574
575
576
577
578 for (i = 0; i < IWL_MAX_DTS_TRIPS; i++) {
579 if (mvm->tz_device.temp_trips[i] != S16_MIN) {
580 cmd.thresholds[idx++] =
581 cpu_to_le16(mvm->tz_device.temp_trips[i]);
582 }
583 }
584 cmd.num_temps = cpu_to_le32(idx);
585
586 if (!idx)
587 goto send;
588
589
590 sort(cmd.thresholds, idx, sizeof(s16), compare_temps, NULL);
591
592
593
594
595 for (i = 0; i < idx; i++) {
596 for (j = 0; j < IWL_MAX_DTS_TRIPS; j++) {
597 if (le16_to_cpu(cmd.thresholds[i]) ==
598 mvm->tz_device.temp_trips[j])
599 mvm->tz_device.fw_trips_index[i] = j;
600 }
601 }
602
603send:
604#endif
605 ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(PHY_OPS_GROUP,
606 TEMP_REPORTING_THRESHOLDS_CMD),
607 0, sizeof(cmd), &cmd);
608 if (ret)
609 IWL_ERR(mvm, "TEMP_REPORT_THS_CMD command failed (err=%d)\n",
610 ret);
611
612 return ret;
613}
614
615#ifdef CONFIG_THERMAL
616static int iwl_mvm_tzone_get_temp(struct thermal_zone_device *device,
617 int *temperature)
618{
619 struct iwl_mvm *mvm = (struct iwl_mvm *)device->devdata;
620 int ret;
621 int temp;
622
623 mutex_lock(&mvm->mutex);
624
625 if (!iwl_mvm_firmware_running(mvm) ||
626 mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) {
627 ret = -ENODATA;
628 goto out;
629 }
630
631 ret = iwl_mvm_get_temp(mvm, &temp);
632 if (ret)
633 goto out;
634
635 *temperature = temp * 1000;
636
637out:
638 mutex_unlock(&mvm->mutex);
639 return ret;
640}
641
642static int iwl_mvm_tzone_get_trip_temp(struct thermal_zone_device *device,
643 int trip, int *temp)
644{
645 struct iwl_mvm *mvm = (struct iwl_mvm *)device->devdata;
646
647 if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS)
648 return -EINVAL;
649
650 *temp = mvm->tz_device.temp_trips[trip] * 1000;
651
652 return 0;
653}
654
655static int iwl_mvm_tzone_get_trip_type(struct thermal_zone_device *device,
656 int trip, enum thermal_trip_type *type)
657{
658 if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS)
659 return -EINVAL;
660
661 *type = THERMAL_TRIP_PASSIVE;
662
663 return 0;
664}
665
666static int iwl_mvm_tzone_set_trip_temp(struct thermal_zone_device *device,
667 int trip, int temp)
668{
669 struct iwl_mvm *mvm = (struct iwl_mvm *)device->devdata;
670 struct iwl_mvm_thermal_device *tzone;
671 int i, ret;
672 s16 temperature;
673
674 mutex_lock(&mvm->mutex);
675
676 if (!iwl_mvm_firmware_running(mvm) ||
677 mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) {
678 ret = -EIO;
679 goto out;
680 }
681
682 if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS) {
683 ret = -EINVAL;
684 goto out;
685 }
686
687 if ((temp / 1000) > S16_MAX) {
688 ret = -EINVAL;
689 goto out;
690 }
691
692 temperature = (s16)(temp / 1000);
693 tzone = &mvm->tz_device;
694
695 if (!tzone) {
696 ret = -EIO;
697 goto out;
698 }
699
700
701 if (tzone->temp_trips[trip] == temperature) {
702 ret = 0;
703 goto out;
704 }
705
706
707 for (i = 0; i < IWL_MAX_DTS_TRIPS; i++) {
708 if (tzone->temp_trips[i] == temperature) {
709 ret = -EINVAL;
710 goto out;
711 }
712 }
713
714 tzone->temp_trips[trip] = temperature;
715
716 ret = iwl_mvm_send_temp_report_ths_cmd(mvm);
717out:
718 mutex_unlock(&mvm->mutex);
719 return ret;
720}
721
722static struct thermal_zone_device_ops tzone_ops = {
723 .get_temp = iwl_mvm_tzone_get_temp,
724 .get_trip_temp = iwl_mvm_tzone_get_trip_temp,
725 .get_trip_type = iwl_mvm_tzone_get_trip_type,
726 .set_trip_temp = iwl_mvm_tzone_set_trip_temp,
727};
728
729
730#define IWL_WRITABLE_TRIPS_MSK (BIT(IWL_MAX_DTS_TRIPS) - 1)
731
732static void iwl_mvm_thermal_zone_register(struct iwl_mvm *mvm)
733{
734 int i, ret;
735 char name[16];
736 static atomic_t counter = ATOMIC_INIT(0);
737
738 if (!iwl_mvm_is_tt_in_fw(mvm)) {
739 mvm->tz_device.tzone = NULL;
740
741 return;
742 }
743
744 BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);
745
746 sprintf(name, "iwlwifi_%u", atomic_inc_return(&counter) & 0xFF);
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 ret = thermal_zone_device_enable(mvm->tz_device.tzone);
761 if (ret) {
762 IWL_DEBUG_TEMP(mvm, "Failed to enable thermal zone\n");
763 thermal_zone_device_unregister(mvm->tz_device.tzone);
764 return;
765 }
766
767
768
769
770 for (i = 0 ; i < IWL_MAX_DTS_TRIPS; i++)
771 mvm->tz_device.temp_trips[i] = S16_MIN;
772}
773
774static int iwl_mvm_tcool_get_max_state(struct thermal_cooling_device *cdev,
775 unsigned long *state)
776{
777 *state = ARRAY_SIZE(iwl_mvm_cdev_budgets) - 1;
778
779 return 0;
780}
781
782static int iwl_mvm_tcool_get_cur_state(struct thermal_cooling_device *cdev,
783 unsigned long *state)
784{
785 struct iwl_mvm *mvm = (struct iwl_mvm *)(cdev->devdata);
786
787 *state = mvm->cooling_dev.cur_state;
788
789 return 0;
790}
791
792static int iwl_mvm_tcool_set_cur_state(struct thermal_cooling_device *cdev,
793 unsigned long new_state)
794{
795 struct iwl_mvm *mvm = (struct iwl_mvm *)(cdev->devdata);
796 int ret;
797
798 mutex_lock(&mvm->mutex);
799
800 if (!iwl_mvm_firmware_running(mvm) ||
801 mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) {
802 ret = -EIO;
803 goto unlock;
804 }
805
806 if (new_state >= ARRAY_SIZE(iwl_mvm_cdev_budgets)) {
807 ret = -EINVAL;
808 goto unlock;
809 }
810
811 ret = iwl_mvm_ctdp_command(mvm, CTDP_CMD_OPERATION_START,
812 new_state);
813
814unlock:
815 mutex_unlock(&mvm->mutex);
816 return ret;
817}
818
819static const struct thermal_cooling_device_ops tcooling_ops = {
820 .get_max_state = iwl_mvm_tcool_get_max_state,
821 .get_cur_state = iwl_mvm_tcool_get_cur_state,
822 .set_cur_state = iwl_mvm_tcool_set_cur_state,
823};
824
825static void iwl_mvm_cooling_device_register(struct iwl_mvm *mvm)
826{
827 char name[] = "iwlwifi";
828
829 if (!iwl_mvm_is_ctdp_supported(mvm))
830 return;
831
832 BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);
833
834 mvm->cooling_dev.cdev =
835 thermal_cooling_device_register(name,
836 mvm,
837 &tcooling_ops);
838
839 if (IS_ERR(mvm->cooling_dev.cdev)) {
840 IWL_DEBUG_TEMP(mvm,
841 "Failed to register to cooling device (err = %ld)\n",
842 PTR_ERR(mvm->cooling_dev.cdev));
843 mvm->cooling_dev.cdev = NULL;
844 return;
845 }
846}
847
848static void iwl_mvm_thermal_zone_unregister(struct iwl_mvm *mvm)
849{
850 if (!iwl_mvm_is_tt_in_fw(mvm) || !mvm->tz_device.tzone)
851 return;
852
853 IWL_DEBUG_TEMP(mvm, "Thermal zone device unregister\n");
854 if (mvm->tz_device.tzone) {
855 thermal_zone_device_unregister(mvm->tz_device.tzone);
856 mvm->tz_device.tzone = NULL;
857 }
858}
859
860static void iwl_mvm_cooling_device_unregister(struct iwl_mvm *mvm)
861{
862 if (!iwl_mvm_is_ctdp_supported(mvm) || !mvm->cooling_dev.cdev)
863 return;
864
865 IWL_DEBUG_TEMP(mvm, "Cooling device unregister\n");
866 if (mvm->cooling_dev.cdev) {
867 thermal_cooling_device_unregister(mvm->cooling_dev.cdev);
868 mvm->cooling_dev.cdev = NULL;
869 }
870}
871#endif
872
873void iwl_mvm_thermal_initialize(struct iwl_mvm *mvm, u32 min_backoff)
874{
875 struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
876
877 IWL_DEBUG_TEMP(mvm, "Initialize Thermal Throttling\n");
878
879 if (mvm->cfg->thermal_params)
880 tt->params = *mvm->cfg->thermal_params;
881 else
882 tt->params = iwl_mvm_default_tt_params;
883
884 tt->throttle = false;
885 tt->dynamic_smps = false;
886 tt->min_backoff = min_backoff;
887 INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill);
888
889#ifdef CONFIG_THERMAL
890 iwl_mvm_cooling_device_register(mvm);
891 iwl_mvm_thermal_zone_register(mvm);
892#endif
893 mvm->init_status |= IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE;
894}
895
896void iwl_mvm_thermal_exit(struct iwl_mvm *mvm)
897{
898 if (!(mvm->init_status & IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE))
899 return;
900
901 cancel_delayed_work_sync(&mvm->thermal_throttle.ct_kill_exit);
902 IWL_DEBUG_TEMP(mvm, "Exit Thermal Throttling\n");
903
904#ifdef CONFIG_THERMAL
905 iwl_mvm_cooling_device_unregister(mvm);
906 iwl_mvm_thermal_zone_unregister(mvm);
907#endif
908 mvm->init_status &= ~IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE;
909}
910