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