834 lines · c
1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause2/*3 * Copyright (C) 2012-2014, 2019-2022, 2024 Intel Corporation4 * Copyright (C) 2013-2014 Intel Mobile Communications GmbH5 * Copyright (C) 2015-2016 Intel Deutschland GmbH6 */7#include <linux/sort.h>8 9#include "mvm.h"10 11#define IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT HZ12 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 /* Don't schedule an exit work if we're in test mode, since30 * the temperature will not change unless we manually set it31 * again (or disable testing).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 /* ignore the notification if we are in test mode */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 /* we can use notif_v1 only, because v2 only adds an additional68 * parameter, which is not used in this function.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 /* shouldn't be negative, but since it's s32, make sure it isn't */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 /* the notification is handled synchronously in ctkill, so skip here */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 /* 0xFF in ths_crossed means the notification is not related134 * to a trip, so we can ignore it here.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_THERMAL143 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 /* CONFIG_THERMAL */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 if (iwl_fw_lookup_notif_ver(mvm->fw, PHY_OPS_GROUP,164 CT_KILL_NOTIFICATION, 0) > 1)165 IWL_DEBUG_TEMP(mvm,166 "CT kill notification DTS bitmap = 0x%x, Scheme = %d\n",167 notif->dts, notif->scheme);168 169 iwl_mvm_enter_ctkill(mvm);170}171 172/*173 * send the DTS_MEASUREMENT_TRIGGER command with or without waiting for a174 * response. If we get a response then the measurement is stored in 'temp'175 */176static int iwl_mvm_send_temp_cmd(struct iwl_mvm *mvm, bool response, s32 *temp)177{178 struct iwl_host_cmd cmd = {};179 struct iwl_dts_measurement_cmd dts_cmd = {180 .flags = cpu_to_le32(DTS_TRIGGER_CMD_FLAGS_TEMP),181 };182 struct iwl_ext_dts_measurement_cmd ext_cmd = {183 .control_mode = cpu_to_le32(DTS_DIRECT_WITHOUT_MEASURE),184 };185 struct iwl_dts_measurement_resp *resp;186 void *cmd_ptr;187 int ret;188 u32 cmd_flags = 0;189 u16 len;190 191 /* Check which command format is used (regular/extended) */192 if (fw_has_capa(&mvm->fw->ucode_capa,193 IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE)) {194 len = sizeof(ext_cmd);195 cmd_ptr = &ext_cmd;196 } else {197 len = sizeof(dts_cmd);198 cmd_ptr = &dts_cmd;199 }200 /* The command version where we get a response is zero length */201 if (response) {202 cmd_flags = CMD_WANT_SKB;203 len = 0;204 }205 206 cmd.id = WIDE_ID(PHY_OPS_GROUP, CMD_DTS_MEASUREMENT_TRIGGER_WIDE);207 cmd.len[0] = len;208 cmd.flags = cmd_flags;209 cmd.data[0] = cmd_ptr;210 211 IWL_DEBUG_TEMP(mvm,212 "Sending temperature measurement command - %s response\n",213 response ? "with" : "without");214 ret = iwl_mvm_send_cmd(mvm, &cmd);215 216 if (ret) {217 IWL_ERR(mvm,218 "Failed to send the temperature measurement command (err=%d)\n",219 ret);220 return ret;221 }222 223 if (response) {224 resp = (void *)cmd.resp_pkt->data;225 *temp = le32_to_cpu(resp->temp);226 IWL_DEBUG_TEMP(mvm,227 "Got temperature measurement response: temp=%d\n",228 *temp);229 iwl_free_resp(&cmd);230 }231 232 return ret;233}234 235int iwl_mvm_get_temp(struct iwl_mvm *mvm, s32 *temp)236{237 struct iwl_notification_wait wait_temp_notif;238 static u16 temp_notif[] = { WIDE_ID(PHY_OPS_GROUP,239 DTS_MEASUREMENT_NOTIF_WIDE) };240 int ret;241 u8 cmd_ver;242 243 /*244 * If command version is 1 we send the command and immediately get245 * a response. For older versions we send the command and wait for a246 * notification (no command TLV for previous versions).247 */248 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw,249 WIDE_ID(PHY_OPS_GROUP, CMD_DTS_MEASUREMENT_TRIGGER_WIDE),250 IWL_FW_CMD_VER_UNKNOWN);251 if (cmd_ver == 1)252 return iwl_mvm_send_temp_cmd(mvm, true, temp);253 254 lockdep_assert_held(&mvm->mutex);255 256 iwl_init_notification_wait(&mvm->notif_wait, &wait_temp_notif,257 temp_notif, ARRAY_SIZE(temp_notif),258 iwl_mvm_temp_notif_wait, temp);259 260 ret = iwl_mvm_send_temp_cmd(mvm, false, temp);261 if (ret) {262 iwl_remove_notification(&mvm->notif_wait, &wait_temp_notif);263 return ret;264 }265 266 ret = iwl_wait_notification(&mvm->notif_wait, &wait_temp_notif,267 IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT);268 if (ret)269 IWL_WARN(mvm, "Getting the temperature timed out\n");270 271 return ret;272}273 274static void check_exit_ctkill(struct work_struct *work)275{276 struct iwl_mvm_tt_mgmt *tt;277 struct iwl_mvm *mvm;278 u32 duration;279 s32 temp;280 int ret;281 282 tt = container_of(work, struct iwl_mvm_tt_mgmt, ct_kill_exit.work);283 mvm = container_of(tt, struct iwl_mvm, thermal_throttle);284 285 if (iwl_mvm_is_tt_in_fw(mvm)) {286 iwl_mvm_exit_ctkill(mvm);287 288 return;289 }290 291 duration = tt->params.ct_kill_duration;292 293 flush_work(&mvm->roc_done_wk);294 295 mutex_lock(&mvm->mutex);296 297 if (__iwl_mvm_mac_start(mvm))298 goto reschedule;299 300 ret = iwl_mvm_get_temp(mvm, &temp);301 302 __iwl_mvm_mac_stop(mvm, false);303 304 if (ret)305 goto reschedule;306 307 IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", temp);308 309 if (temp <= tt->params.ct_kill_exit) {310 mutex_unlock(&mvm->mutex);311 iwl_mvm_exit_ctkill(mvm);312 return;313 }314 315reschedule:316 mutex_unlock(&mvm->mutex);317 schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit,318 round_jiffies(duration * HZ));319}320 321static void iwl_mvm_tt_smps_iterator(void *_data, u8 *mac,322 struct ieee80211_vif *vif)323{324 struct iwl_mvm *mvm = _data;325 enum ieee80211_smps_mode smps_mode;326 327 lockdep_assert_held(&mvm->mutex);328 329 if (mvm->thermal_throttle.dynamic_smps)330 smps_mode = IEEE80211_SMPS_DYNAMIC;331 else332 smps_mode = IEEE80211_SMPS_AUTOMATIC;333 334 if (vif->type != NL80211_IFTYPE_STATION)335 return;336 337 iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT, smps_mode, 0);338}339 340static void iwl_mvm_tt_tx_protection(struct iwl_mvm *mvm, bool enable)341{342 struct iwl_mvm_sta *mvmsta;343 int i, err;344 345 for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) {346 mvmsta = iwl_mvm_sta_from_staid_protected(mvm, i);347 if (!mvmsta)348 continue;349 350 if (enable == mvmsta->tt_tx_protection)351 continue;352 err = iwl_mvm_tx_protection(mvm, mvmsta, enable);353 if (err) {354 IWL_ERR(mvm, "Failed to %s Tx protection\n",355 enable ? "enable" : "disable");356 } else {357 IWL_DEBUG_TEMP(mvm, "%s Tx protection\n",358 enable ? "Enable" : "Disable");359 mvmsta->tt_tx_protection = enable;360 }361 }362}363 364void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff)365{366 struct iwl_host_cmd cmd = {367 .id = REPLY_THERMAL_MNG_BACKOFF,368 .len = { sizeof(u32), },369 .data = { &backoff, },370 };371 372 backoff = max(backoff, mvm->thermal_throttle.min_backoff);373 374 if (iwl_mvm_send_cmd(mvm, &cmd) == 0) {375 IWL_DEBUG_TEMP(mvm, "Set Thermal Tx backoff to: %u\n",376 backoff);377 mvm->thermal_throttle.tx_backoff = backoff;378 } else {379 IWL_ERR(mvm, "Failed to change Thermal Tx backoff\n");380 }381}382 383void iwl_mvm_tt_handler(struct iwl_mvm *mvm)384{385 struct iwl_tt_params *params = &mvm->thermal_throttle.params;386 struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;387 s32 temperature = mvm->temperature;388 bool throttle_enable = false;389 int i;390 u32 tx_backoff;391 392 IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", mvm->temperature);393 394 if (params->support_ct_kill && temperature >= params->ct_kill_entry) {395 iwl_mvm_enter_ctkill(mvm);396 return;397 }398 399 if (params->support_ct_kill &&400 temperature <= params->ct_kill_exit) {401 iwl_mvm_exit_ctkill(mvm);402 return;403 }404 405 if (params->support_dynamic_smps) {406 if (!tt->dynamic_smps &&407 temperature >= params->dynamic_smps_entry) {408 IWL_DEBUG_TEMP(mvm, "Enable dynamic SMPS\n");409 tt->dynamic_smps = true;410 ieee80211_iterate_active_interfaces_atomic(411 mvm->hw, IEEE80211_IFACE_ITER_NORMAL,412 iwl_mvm_tt_smps_iterator, mvm);413 throttle_enable = true;414 } else if (tt->dynamic_smps &&415 temperature <= params->dynamic_smps_exit) {416 IWL_DEBUG_TEMP(mvm, "Disable dynamic SMPS\n");417 tt->dynamic_smps = false;418 ieee80211_iterate_active_interfaces_atomic(419 mvm->hw, IEEE80211_IFACE_ITER_NORMAL,420 iwl_mvm_tt_smps_iterator, mvm);421 }422 }423 424 if (params->support_tx_protection) {425 if (temperature >= params->tx_protection_entry) {426 iwl_mvm_tt_tx_protection(mvm, true);427 throttle_enable = true;428 } else if (temperature <= params->tx_protection_exit) {429 iwl_mvm_tt_tx_protection(mvm, false);430 }431 }432 433 if (params->support_tx_backoff) {434 tx_backoff = tt->min_backoff;435 for (i = 0; i < TT_TX_BACKOFF_SIZE; i++) {436 if (temperature < params->tx_backoff[i].temperature)437 break;438 tx_backoff = max(tt->min_backoff,439 params->tx_backoff[i].backoff);440 }441 if (tx_backoff != tt->min_backoff)442 throttle_enable = true;443 if (tt->tx_backoff != tx_backoff)444 iwl_mvm_tt_tx_backoff(mvm, tx_backoff);445 }446 447 if (!tt->throttle && throttle_enable) {448 IWL_WARN(mvm,449 "Due to high temperature thermal throttling initiated\n");450 tt->throttle = true;451 } else if (tt->throttle && !tt->dynamic_smps &&452 tt->tx_backoff == tt->min_backoff &&453 temperature <= params->tx_protection_exit) {454 IWL_WARN(mvm,455 "Temperature is back to normal thermal throttling stopped\n");456 tt->throttle = false;457 }458}459 460static const struct iwl_tt_params iwl_mvm_default_tt_params = {461 .ct_kill_entry = 118,462 .ct_kill_exit = 96,463 .ct_kill_duration = 5,464 .dynamic_smps_entry = 114,465 .dynamic_smps_exit = 110,466 .tx_protection_entry = 114,467 .tx_protection_exit = 108,468 .tx_backoff = {469 {.temperature = 112, .backoff = 200},470 {.temperature = 113, .backoff = 600},471 {.temperature = 114, .backoff = 1200},472 {.temperature = 115, .backoff = 2000},473 {.temperature = 116, .backoff = 4000},474 {.temperature = 117, .backoff = 10000},475 },476 .support_ct_kill = true,477 .support_dynamic_smps = true,478 .support_tx_protection = true,479 .support_tx_backoff = true,480};481 482/* budget in mWatt */483static const u32 iwl_mvm_cdev_budgets[] = {484 2400, /* cooling state 0 */485 2000, /* cooling state 1 */486 1800, /* cooling state 2 */487 1600, /* cooling state 3 */488 1400, /* cooling state 4 */489 1200, /* cooling state 5 */490 1000, /* cooling state 6 */491 900, /* cooling state 7 */492 800, /* cooling state 8 */493 700, /* cooling state 9 */494 650, /* cooling state 10 */495 600, /* cooling state 11 */496 550, /* cooling state 12 */497 500, /* cooling state 13 */498 450, /* cooling state 14 */499 400, /* cooling state 15 */500 350, /* cooling state 16 */501 300, /* cooling state 17 */502 250, /* cooling state 18 */503 200, /* cooling state 19 */504 150, /* cooling state 20 */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_THERMAL532 mvm->cooling_dev.cur_state = state;533#endif /* CONFIG_THERMAL */534 break;535 case CTDP_CMD_OPERATION_REPORT:536 IWL_DEBUG_TEMP(mvm, "cTDP avg energy in mWatt = %d\n", status);537 /* when the function is called with CTDP_CMD_OPERATION_REPORT538 * option the function should return the average budget value539 * that is received from the FW.540 * The budget can't be less or equal to 0, so it's possible541 * to distinguish between error values and budgets.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_THERMAL553static 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 559struct iwl_trip_walk_data {560 __le16 *thresholds;561 int count;562};563 564static int iwl_trip_temp_cb(struct thermal_trip *trip, void *arg)565{566 struct iwl_trip_walk_data *twd = arg;567 568 if (trip->temperature == THERMAL_TEMP_INVALID)569 return 0;570 571 twd->thresholds[twd->count++] = cpu_to_le16((s16)(trip->temperature / 1000));572 return 0;573}574#endif575 576int iwl_mvm_send_temp_report_ths_cmd(struct iwl_mvm *mvm)577{578 struct temp_report_ths_cmd cmd = {0};579 int ret;580#ifdef CONFIG_THERMAL581 struct iwl_trip_walk_data twd = { .thresholds = cmd.thresholds, .count = 0 };582 583 lockdep_assert_held(&mvm->mutex);584 585 if (!mvm->tz_device.tzone)586 goto send;587 588 /*589 * The thermal core holds an array of temperature trips that are590 * unsorted and uncompressed, the FW should get it compressed and591 * sorted.592 */593 594 /* compress trips to cmd array, remove uninitialized values*/595 for_each_thermal_trip(mvm->tz_device.tzone, iwl_trip_temp_cb, &twd);596 597 cmd.num_temps = cpu_to_le32(twd.count);598 if (twd.count)599 sort(cmd.thresholds, twd.count, sizeof(s16), compare_temps, NULL);600 601send:602#endif603 ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(PHY_OPS_GROUP,604 TEMP_REPORTING_THRESHOLDS_CMD),605 0, sizeof(cmd), &cmd);606 if (ret)607 IWL_ERR(mvm, "TEMP_REPORT_THS_CMD command failed (err=%d)\n",608 ret);609 610 return ret;611}612 613#ifdef CONFIG_THERMAL614static int iwl_mvm_tzone_get_temp(struct thermal_zone_device *device,615 int *temperature)616{617 struct iwl_mvm *mvm = thermal_zone_device_priv(device);618 int ret;619 int temp;620 621 guard(mvm)(mvm);622 623 if (!iwl_mvm_firmware_running(mvm) ||624 mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) {625 /*626 * Tell the core that there is no valid temperature value to627 * return, but it need not worry about this.628 */629 *temperature = THERMAL_TEMP_INVALID;630 return 0;631 }632 633 ret = iwl_mvm_get_temp(mvm, &temp);634 if (ret)635 return ret;636 637 *temperature = temp * 1000;638 return 0;639}640 641static int iwl_mvm_tzone_set_trip_temp(struct thermal_zone_device *device,642 const struct thermal_trip *trip, int temp)643{644 struct iwl_mvm *mvm = thermal_zone_device_priv(device);645 646 guard(mvm)(mvm);647 648 if (!iwl_mvm_firmware_running(mvm) ||649 mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR)650 return -EIO;651 652 if ((temp / 1000) > S16_MAX)653 return -EINVAL;654 655 return iwl_mvm_send_temp_report_ths_cmd(mvm);656}657 658static struct thermal_zone_device_ops tzone_ops = {659 .get_temp = iwl_mvm_tzone_get_temp,660 .set_trip_temp = iwl_mvm_tzone_set_trip_temp,661};662 663static void iwl_mvm_thermal_zone_register(struct iwl_mvm *mvm)664{665 int i, ret;666 char name[16];667 static atomic_t counter = ATOMIC_INIT(0);668 669 if (!iwl_mvm_is_tt_in_fw(mvm)) {670 mvm->tz_device.tzone = NULL;671 672 return;673 }674 675 BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);676 677 sprintf(name, "iwlwifi_%u", atomic_inc_return(&counter) & 0xFF);678 /*679 * 0 is a valid temperature,680 * so initialize the array with S16_MIN which invalid temperature681 */682 for (i = 0 ; i < IWL_MAX_DTS_TRIPS; i++) {683 mvm->tz_device.trips[i].temperature = THERMAL_TEMP_INVALID;684 mvm->tz_device.trips[i].type = THERMAL_TRIP_PASSIVE;685 mvm->tz_device.trips[i].flags = THERMAL_TRIP_FLAG_RW_TEMP;686 }687 mvm->tz_device.tzone = thermal_zone_device_register_with_trips(name,688 mvm->tz_device.trips,689 IWL_MAX_DTS_TRIPS,690 mvm, &tzone_ops,691 NULL, 0, 0);692 if (IS_ERR(mvm->tz_device.tzone)) {693 IWL_DEBUG_TEMP(mvm,694 "Failed to register to thermal zone (err = %ld)\n",695 PTR_ERR(mvm->tz_device.tzone));696 mvm->tz_device.tzone = NULL;697 return;698 }699 700 ret = thermal_zone_device_enable(mvm->tz_device.tzone);701 if (ret) {702 IWL_DEBUG_TEMP(mvm, "Failed to enable thermal zone\n");703 thermal_zone_device_unregister(mvm->tz_device.tzone);704 }705}706 707static int iwl_mvm_tcool_get_max_state(struct thermal_cooling_device *cdev,708 unsigned long *state)709{710 *state = ARRAY_SIZE(iwl_mvm_cdev_budgets) - 1;711 712 return 0;713}714 715static int iwl_mvm_tcool_get_cur_state(struct thermal_cooling_device *cdev,716 unsigned long *state)717{718 struct iwl_mvm *mvm = (struct iwl_mvm *)(cdev->devdata);719 720 *state = mvm->cooling_dev.cur_state;721 722 return 0;723}724 725static int iwl_mvm_tcool_set_cur_state(struct thermal_cooling_device *cdev,726 unsigned long new_state)727{728 struct iwl_mvm *mvm = (struct iwl_mvm *)(cdev->devdata);729 730 guard(mvm)(mvm);731 732 if (!iwl_mvm_firmware_running(mvm) ||733 mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR)734 return -EIO;735 736 if (new_state >= ARRAY_SIZE(iwl_mvm_cdev_budgets))737 return -EINVAL;738 739 return iwl_mvm_ctdp_command(mvm, CTDP_CMD_OPERATION_START,740 new_state);741}742 743static const struct thermal_cooling_device_ops tcooling_ops = {744 .get_max_state = iwl_mvm_tcool_get_max_state,745 .get_cur_state = iwl_mvm_tcool_get_cur_state,746 .set_cur_state = iwl_mvm_tcool_set_cur_state,747};748 749static void iwl_mvm_cooling_device_register(struct iwl_mvm *mvm)750{751 char name[] = "iwlwifi";752 753 if (!iwl_mvm_is_ctdp_supported(mvm))754 return;755 756 BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);757 758 mvm->cooling_dev.cdev =759 thermal_cooling_device_register(name,760 mvm,761 &tcooling_ops);762 763 if (IS_ERR(mvm->cooling_dev.cdev)) {764 IWL_DEBUG_TEMP(mvm,765 "Failed to register to cooling device (err = %ld)\n",766 PTR_ERR(mvm->cooling_dev.cdev));767 mvm->cooling_dev.cdev = NULL;768 return;769 }770}771 772static void iwl_mvm_thermal_zone_unregister(struct iwl_mvm *mvm)773{774 if (!iwl_mvm_is_tt_in_fw(mvm) || !mvm->tz_device.tzone)775 return;776 777 IWL_DEBUG_TEMP(mvm, "Thermal zone device unregister\n");778 if (mvm->tz_device.tzone) {779 thermal_zone_device_unregister(mvm->tz_device.tzone);780 mvm->tz_device.tzone = NULL;781 }782}783 784static void iwl_mvm_cooling_device_unregister(struct iwl_mvm *mvm)785{786 if (!iwl_mvm_is_ctdp_supported(mvm) || !mvm->cooling_dev.cdev)787 return;788 789 IWL_DEBUG_TEMP(mvm, "Cooling device unregister\n");790 if (mvm->cooling_dev.cdev) {791 thermal_cooling_device_unregister(mvm->cooling_dev.cdev);792 mvm->cooling_dev.cdev = NULL;793 }794}795#endif /* CONFIG_THERMAL */796 797void iwl_mvm_thermal_initialize(struct iwl_mvm *mvm, u32 min_backoff)798{799 struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;800 801 IWL_DEBUG_TEMP(mvm, "Initialize Thermal Throttling\n");802 803 if (mvm->cfg->thermal_params)804 tt->params = *mvm->cfg->thermal_params;805 else806 tt->params = iwl_mvm_default_tt_params;807 808 tt->throttle = false;809 tt->dynamic_smps = false;810 tt->min_backoff = min_backoff;811 INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill);812 813#ifdef CONFIG_THERMAL814 iwl_mvm_cooling_device_register(mvm);815 iwl_mvm_thermal_zone_register(mvm);816#endif817 mvm->init_status |= IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE;818}819 820void iwl_mvm_thermal_exit(struct iwl_mvm *mvm)821{822 if (!(mvm->init_status & IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE))823 return;824 825 cancel_delayed_work_sync(&mvm->thermal_throttle.ct_kill_exit);826 IWL_DEBUG_TEMP(mvm, "Exit Thermal Throttling\n");827 828#ifdef CONFIG_THERMAL829 iwl_mvm_cooling_device_unregister(mvm);830 iwl_mvm_thermal_zone_unregister(mvm);831#endif832 mvm->init_status &= ~IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE;833}834