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