root/drivers/net/wireless/intel/iwlwifi/mvm/tt.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. iwl_mvm_enter_ctkill
  2. iwl_mvm_exit_ctkill
  3. iwl_mvm_tt_temp_changed
  4. iwl_mvm_temp_notif_parse
  5. iwl_mvm_temp_notif_wait
  6. iwl_mvm_temp_notif
  7. iwl_mvm_ct_kill_notif
  8. iwl_mvm_get_temp_cmd
  9. iwl_mvm_get_temp
  10. check_exit_ctkill
  11. iwl_mvm_tt_smps_iterator
  12. iwl_mvm_tt_tx_protection
  13. iwl_mvm_tt_tx_backoff
  14. iwl_mvm_tt_handler
  15. iwl_mvm_ctdp_command
  16. compare_temps
  17. iwl_mvm_send_temp_report_ths_cmd
  18. iwl_mvm_tzone_get_temp
  19. iwl_mvm_tzone_get_trip_temp
  20. iwl_mvm_tzone_get_trip_type
  21. iwl_mvm_tzone_set_trip_temp
  22. iwl_mvm_thermal_zone_register
  23. iwl_mvm_tcool_get_max_state
  24. iwl_mvm_tcool_get_cur_state
  25. iwl_mvm_tcool_set_cur_state
  26. iwl_mvm_cooling_device_register
  27. iwl_mvm_thermal_zone_unregister
  28. iwl_mvm_cooling_device_unregister
  29. iwl_mvm_thermal_initialize
  30. iwl_mvm_thermal_exit

   1 /******************************************************************************
   2  *
   3  * This file is provided under a dual BSD/GPLv2 license.  When using or
   4  * redistributing this file, you may do so under either license.
   5  *
   6  * GPL LICENSE SUMMARY
   7  *
   8  * Copyright(c) 2013 - 2014, 2019 Intel Corporation. All rights reserved.
   9  * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
  10  * Copyright(c) 2015 - 2016 Intel Deutschland GmbH
  11  *
  12  * This program is free software; you can redistribute it and/or modify
  13  * it under the terms of version 2 of the GNU General Public License as
  14  * published by the Free Software Foundation.
  15  *
  16  * This program is distributed in the hope that it will be useful, but
  17  * WITHOUT ANY WARRANTY; without even the implied warranty of
  18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  19  * General Public License for more details.
  20  *
  21  * The full GNU General Public License is included in this distribution
  22  * in the file called COPYING.
  23  *
  24  * Contact Information:
  25  *  Intel Linux Wireless <linuxwifi@intel.com>
  26  * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
  27  *
  28  * BSD LICENSE
  29  *
  30  * Copyright(c) 2012 - 2014, 2019 Intel Corporation. All rights reserved.
  31  * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
  32  * Copyright(c) 2015 - 2016 Intel Deutschland GmbH
  33  * All rights reserved.
  34  *
  35  * Redistribution and use in source and binary forms, with or without
  36  * modification, are permitted provided that the following conditions
  37  * are met:
  38  *
  39  *  * Redistributions of source code must retain the above copyright
  40  *    notice, this list of conditions and the following disclaimer.
  41  *  * Redistributions in binary form must reproduce the above copyright
  42  *    notice, this list of conditions and the following disclaimer in
  43  *    the documentation and/or other materials provided with the
  44  *    distribution.
  45  *  * Neither the name Intel Corporation nor the names of its
  46  *    contributors may be used to endorse or promote products derived
  47  *    from this software without specific prior written permission.
  48  *
  49  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  50  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  51  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  52  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  53  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  54  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  55  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  56  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  57  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  58  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  59  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  60  *
  61  *****************************************************************************/
  62 
  63 #include <linux/sort.h>
  64 
  65 #include "mvm.h"
  66 
  67 #define IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT HZ
  68 
  69 void 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         /* Don't schedule an exit work if we're in test mode, since
  86          * the temperature will not change unless we manually set it
  87          * again (or disable testing).
  88          */
  89         if (!mvm->temperature_test)
  90                 schedule_delayed_work(&tt->ct_kill_exit,
  91                                       round_jiffies_relative(duration * HZ));
  92 }
  93 
  94 static 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 
 103 void iwl_mvm_tt_temp_changed(struct iwl_mvm *mvm, u32 temp)
 104 {
 105         /* ignore the notification if we are in test mode */
 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 
 116 static 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         /* we can use notif_v1 only, because v2 only adds an additional
 124          * parameter, which is not used in this function.
 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         /* shouldn't be negative, but since it's s32, make sure it isn't */
 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 
 144 static 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 
 161 void 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         /* the notification is handled synchronously in ctkill, so skip here */
 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         /* 0xFF in ths_crossed means the notification is not related
 190          * to a trip, so we can ignore it here.
 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 /* CONFIG_THERMAL */
 209 }
 210 
 211 void 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 
 229 static 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 
 249 int 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 
 277 static 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 
 316 reschedule:
 317         mutex_unlock(&mvm->mutex);
 318         schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit,
 319                               round_jiffies(duration * HZ));
 320 }
 321 
 322 static 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 
 341 static 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 
 365 void 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 
 384 void 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 
 461 static 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 /* budget in mWatt */
 484 static const u32 iwl_mvm_cdev_budgets[] = {
 485         2000,   /* cooling state 0 */
 486         1800,   /* cooling state 1 */
 487         1600,   /* cooling state 2 */
 488         1400,   /* cooling state 3 */
 489         1200,   /* cooling state 4 */
 490         1000,   /* cooling state 5 */
 491         900,    /* cooling state 6 */
 492         800,    /* cooling state 7 */
 493         700,    /* cooling state 8 */
 494         650,    /* cooling state 9 */
 495         600,    /* cooling state 10 */
 496         550,    /* cooling state 11 */
 497         500,    /* cooling state 12 */
 498         450,    /* cooling state 13 */
 499         400,    /* cooling state 14 */
 500         350,    /* cooling state 15 */
 501         300,    /* cooling state 16 */
 502         250,    /* cooling state 17 */
 503         200,    /* cooling state 18 */
 504         150,    /* cooling state 19 */
 505 };
 506 
 507 int 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 /* 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_REPORT
 538                  * option the function should return the average budget value
 539                  * that is received from the FW.
 540                  * The budget can't be less or equal to 0, so it's possible
 541                  * 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_THERMAL
 553 static 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 
 560 int 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         /* The driver holds array of temperature trips that are unsorted
 573          * and uncompressed, the FW should get it compressed and sorted
 574          */
 575 
 576         /* compress temp_trips to cmd array, remove uninitialized values*/
 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         /*sort cmd array*/
 589         sort(cmd.thresholds, idx, sizeof(s16), compare_temps, NULL);
 590 
 591         /* we should save the indexes of trips because we sort
 592          * and compress the orginal array
 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 
 602 send:
 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
 615 static 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 
 636 out:
 637         mutex_unlock(&mvm->mutex);
 638         return ret;
 639 }
 640 
 641 static 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 
 654 static 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 
 665 static 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         /* no updates*/
 700         if (tzone->temp_trips[trip] == temperature) {
 701                 ret = 0;
 702                 goto out;
 703         }
 704 
 705         /* already existing temperature */
 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);
 716 out:
 717         mutex_unlock(&mvm->mutex);
 718         return ret;
 719 }
 720 
 721 static  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 /* make all trips writable */
 729 #define IWL_WRITABLE_TRIPS_MSK (BIT(IWL_MAX_DTS_TRIPS) - 1)
 730 
 731 static void iwl_mvm_thermal_zone_register(struct iwl_mvm *mvm)
 732 {
 733         int i;
 734         char name[16];
 735         static atomic_t counter = ATOMIC_INIT(0);
 736 
 737         if (!iwl_mvm_is_tt_in_fw(mvm)) {
 738                 mvm->tz_device.tzone = NULL;
 739 
 740                 return;
 741         }
 742 
 743         BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);
 744 
 745         sprintf(name, "iwlwifi_%u", atomic_inc_return(&counter) & 0xFF);
 746         mvm->tz_device.tzone = thermal_zone_device_register(name,
 747                                                         IWL_MAX_DTS_TRIPS,
 748                                                         IWL_WRITABLE_TRIPS_MSK,
 749                                                         mvm, &tzone_ops,
 750                                                         NULL, 0, 0);
 751         if (IS_ERR(mvm->tz_device.tzone)) {
 752                 IWL_DEBUG_TEMP(mvm,
 753                                "Failed to register to thermal zone (err = %ld)\n",
 754                                PTR_ERR(mvm->tz_device.tzone));
 755                 mvm->tz_device.tzone = NULL;
 756                 return;
 757         }
 758 
 759         /* 0 is a valid temperature,
 760          * so initialize the array with S16_MIN which invalid temperature
 761          */
 762         for (i = 0 ; i < IWL_MAX_DTS_TRIPS; i++)
 763                 mvm->tz_device.temp_trips[i] = S16_MIN;
 764 }
 765 
 766 static int iwl_mvm_tcool_get_max_state(struct thermal_cooling_device *cdev,
 767                                        unsigned long *state)
 768 {
 769         *state = ARRAY_SIZE(iwl_mvm_cdev_budgets) - 1;
 770 
 771         return 0;
 772 }
 773 
 774 static int iwl_mvm_tcool_get_cur_state(struct thermal_cooling_device *cdev,
 775                                        unsigned long *state)
 776 {
 777         struct iwl_mvm *mvm = (struct iwl_mvm *)(cdev->devdata);
 778 
 779         *state = mvm->cooling_dev.cur_state;
 780 
 781         return 0;
 782 }
 783 
 784 static int iwl_mvm_tcool_set_cur_state(struct thermal_cooling_device *cdev,
 785                                        unsigned long new_state)
 786 {
 787         struct iwl_mvm *mvm = (struct iwl_mvm *)(cdev->devdata);
 788         int ret;
 789 
 790         mutex_lock(&mvm->mutex);
 791 
 792         if (!iwl_mvm_firmware_running(mvm) ||
 793             mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) {
 794                 ret = -EIO;
 795                 goto unlock;
 796         }
 797 
 798         if (new_state >= ARRAY_SIZE(iwl_mvm_cdev_budgets)) {
 799                 ret = -EINVAL;
 800                 goto unlock;
 801         }
 802 
 803         ret = iwl_mvm_ctdp_command(mvm, CTDP_CMD_OPERATION_START,
 804                                    new_state);
 805 
 806 unlock:
 807         mutex_unlock(&mvm->mutex);
 808         return ret;
 809 }
 810 
 811 static const struct thermal_cooling_device_ops tcooling_ops = {
 812         .get_max_state = iwl_mvm_tcool_get_max_state,
 813         .get_cur_state = iwl_mvm_tcool_get_cur_state,
 814         .set_cur_state = iwl_mvm_tcool_set_cur_state,
 815 };
 816 
 817 static void iwl_mvm_cooling_device_register(struct iwl_mvm *mvm)
 818 {
 819         char name[] = "iwlwifi";
 820 
 821         if (!iwl_mvm_is_ctdp_supported(mvm))
 822                 return;
 823 
 824         BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);
 825 
 826         mvm->cooling_dev.cdev =
 827                 thermal_cooling_device_register(name,
 828                                                 mvm,
 829                                                 &tcooling_ops);
 830 
 831         if (IS_ERR(mvm->cooling_dev.cdev)) {
 832                 IWL_DEBUG_TEMP(mvm,
 833                                "Failed to register to cooling device (err = %ld)\n",
 834                                PTR_ERR(mvm->cooling_dev.cdev));
 835                 mvm->cooling_dev.cdev = NULL;
 836                 return;
 837         }
 838 }
 839 
 840 static void iwl_mvm_thermal_zone_unregister(struct iwl_mvm *mvm)
 841 {
 842         if (!iwl_mvm_is_tt_in_fw(mvm) || !mvm->tz_device.tzone)
 843                 return;
 844 
 845         IWL_DEBUG_TEMP(mvm, "Thermal zone device unregister\n");
 846         if (mvm->tz_device.tzone) {
 847                 thermal_zone_device_unregister(mvm->tz_device.tzone);
 848                 mvm->tz_device.tzone = NULL;
 849         }
 850 }
 851 
 852 static void iwl_mvm_cooling_device_unregister(struct iwl_mvm *mvm)
 853 {
 854         if (!iwl_mvm_is_ctdp_supported(mvm) || !mvm->cooling_dev.cdev)
 855                 return;
 856 
 857         IWL_DEBUG_TEMP(mvm, "Cooling device unregister\n");
 858         if (mvm->cooling_dev.cdev) {
 859                 thermal_cooling_device_unregister(mvm->cooling_dev.cdev);
 860                 mvm->cooling_dev.cdev = NULL;
 861         }
 862 }
 863 #endif /* CONFIG_THERMAL */
 864 
 865 void iwl_mvm_thermal_initialize(struct iwl_mvm *mvm, u32 min_backoff)
 866 {
 867         struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
 868 
 869         IWL_DEBUG_TEMP(mvm, "Initialize Thermal Throttling\n");
 870 
 871         if (mvm->cfg->thermal_params)
 872                 tt->params = *mvm->cfg->thermal_params;
 873         else
 874                 tt->params = iwl_mvm_default_tt_params;
 875 
 876         tt->throttle = false;
 877         tt->dynamic_smps = false;
 878         tt->min_backoff = min_backoff;
 879         INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill);
 880 
 881 #ifdef CONFIG_THERMAL
 882         iwl_mvm_cooling_device_register(mvm);
 883         iwl_mvm_thermal_zone_register(mvm);
 884 #endif
 885         mvm->init_status |= IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE;
 886 }
 887 
 888 void iwl_mvm_thermal_exit(struct iwl_mvm *mvm)
 889 {
 890         if (!(mvm->init_status & IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE))
 891                 return;
 892 
 893         cancel_delayed_work_sync(&mvm->thermal_throttle.ct_kill_exit);
 894         IWL_DEBUG_TEMP(mvm, "Exit Thermal Throttling\n");
 895 
 896 #ifdef CONFIG_THERMAL
 897         iwl_mvm_cooling_device_unregister(mvm);
 898         iwl_mvm_thermal_zone_unregister(mvm);
 899 #endif
 900         mvm->init_status &= ~IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE;
 901 }

/* [<][>][^][v][top][bottom][index][help] */