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 *
  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
  69void 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
  94static 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
 103void 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
 116static 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
 144static 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
 161void 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
 211void 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
 229static 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
 249int 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
 277static 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
 316reschedule:
 317        mutex_unlock(&mvm->mutex);
 318        schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit,
 319                              round_jiffies(duration * HZ));
 320}
 321
 322static void iwl_mvm_tt_smps_iterator(void *_data, u8 *mac,
 323                                     struct ieee80211_vif *vif)
 324{
 325        struct iwl_mvm *mvm = _data;
 326        enum ieee80211_smps_mode smps_mode;
 327
 328        lockdep_assert_held(&mvm->mutex);
 329
 330        if (mvm->thermal_throttle.dynamic_smps)
 331                smps_mode = IEEE80211_SMPS_DYNAMIC;
 332        else
 333                smps_mode = IEEE80211_SMPS_AUTOMATIC;
 334
 335        if (vif->type != NL80211_IFTYPE_STATION)
 336                return;
 337
 338        iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT, smps_mode);
 339}
 340
 341static void iwl_mvm_tt_tx_protection(struct iwl_mvm *mvm, bool enable)
 342{
 343        struct iwl_mvm_sta *mvmsta;
 344        int i, err;
 345
 346        for (i = 0; i < 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
 365void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff)
 366{
 367        struct iwl_host_cmd cmd = {
 368                .id = REPLY_THERMAL_MNG_BACKOFF,
 369                .len = { sizeof(u32), },
 370                .data = { &backoff, },
 371        };
 372
 373        backoff = max(backoff, mvm->thermal_throttle.min_backoff);
 374
 375        if (iwl_mvm_send_cmd(mvm, &cmd) == 0) {
 376                IWL_DEBUG_TEMP(mvm, "Set Thermal Tx backoff to: %u\n",
 377                               backoff);
 378                mvm->thermal_throttle.tx_backoff = backoff;
 379        } else {
 380                IWL_ERR(mvm, "Failed to change Thermal Tx backoff\n");
 381        }
 382}
 383
 384void iwl_mvm_tt_handler(struct iwl_mvm *mvm)
 385{
 386        struct iwl_tt_params *params = &mvm->thermal_throttle.params;
 387        struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
 388        s32 temperature = mvm->temperature;
 389        bool throttle_enable = false;
 390        int i;
 391        u32 tx_backoff;
 392
 393        IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", mvm->temperature);
 394
 395        if (params->support_ct_kill && temperature >= params->ct_kill_entry) {
 396                iwl_mvm_enter_ctkill(mvm);
 397                return;
 398        }
 399
 400        if (params->support_ct_kill &&
 401            temperature <= params->ct_kill_exit) {
 402                iwl_mvm_exit_ctkill(mvm);
 403                return;
 404        }
 405
 406        if (params->support_dynamic_smps) {
 407                if (!tt->dynamic_smps &&
 408                    temperature >= params->dynamic_smps_entry) {
 409                        IWL_DEBUG_TEMP(mvm, "Enable dynamic SMPS\n");
 410                        tt->dynamic_smps = true;
 411                        ieee80211_iterate_active_interfaces_atomic(
 412                                        mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
 413                                        iwl_mvm_tt_smps_iterator, mvm);
 414                        throttle_enable = true;
 415                } else if (tt->dynamic_smps &&
 416                           temperature <= params->dynamic_smps_exit) {
 417                        IWL_DEBUG_TEMP(mvm, "Disable dynamic SMPS\n");
 418                        tt->dynamic_smps = false;
 419                        ieee80211_iterate_active_interfaces_atomic(
 420                                        mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
 421                                        iwl_mvm_tt_smps_iterator, mvm);
 422                }
 423        }
 424
 425        if (params->support_tx_protection) {
 426                if (temperature >= params->tx_protection_entry) {
 427                        iwl_mvm_tt_tx_protection(mvm, true);
 428                        throttle_enable = true;
 429                } else if (temperature <= params->tx_protection_exit) {
 430                        iwl_mvm_tt_tx_protection(mvm, false);
 431                }
 432        }
 433
 434        if (params->support_tx_backoff) {
 435                tx_backoff = tt->min_backoff;
 436                for (i = 0; i < TT_TX_BACKOFF_SIZE; i++) {
 437                        if (temperature < params->tx_backoff[i].temperature)
 438                                break;
 439                        tx_backoff = max(tt->min_backoff,
 440                                         params->tx_backoff[i].backoff);
 441                }
 442                if (tx_backoff != tt->min_backoff)
 443                        throttle_enable = true;
 444                if (tt->tx_backoff != tx_backoff)
 445                        iwl_mvm_tt_tx_backoff(mvm, tx_backoff);
 446        }
 447
 448        if (!tt->throttle && throttle_enable) {
 449                IWL_WARN(mvm,
 450                         "Due to high temperature thermal throttling initiated\n");
 451                tt->throttle = true;
 452        } else if (tt->throttle && !tt->dynamic_smps &&
 453                   tt->tx_backoff == tt->min_backoff &&
 454                   temperature <= params->tx_protection_exit) {
 455                IWL_WARN(mvm,
 456                         "Temperature is back to normal thermal throttling stopped\n");
 457                tt->throttle = false;
 458        }
 459}
 460
 461static const struct iwl_tt_params iwl_mvm_default_tt_params = {
 462        .ct_kill_entry = 118,
 463        .ct_kill_exit = 96,
 464        .ct_kill_duration = 5,
 465        .dynamic_smps_entry = 114,
 466        .dynamic_smps_exit = 110,
 467        .tx_protection_entry = 114,
 468        .tx_protection_exit = 108,
 469        .tx_backoff = {
 470                {.temperature = 112, .backoff = 200},
 471                {.temperature = 113, .backoff = 600},
 472                {.temperature = 114, .backoff = 1200},
 473                {.temperature = 115, .backoff = 2000},
 474                {.temperature = 116, .backoff = 4000},
 475                {.temperature = 117, .backoff = 10000},
 476        },
 477        .support_ct_kill = true,
 478        .support_dynamic_smps = true,
 479        .support_tx_protection = true,
 480        .support_tx_backoff = true,
 481};
 482
 483/* budget in mWatt */
 484static 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
 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_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
 553static 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
 560int 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
 602send:
 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
 615static 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
 636out:
 637        mutex_unlock(&mvm->mutex);
 638        return ret;
 639}
 640
 641static 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
 654static 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
 665static 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);
 716out:
 717        mutex_unlock(&mvm->mutex);
 718        return ret;
 719}
 720
 721static  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
 731static void iwl_mvm_thermal_zone_register(struct iwl_mvm *mvm)
 732{
 733        int i;
 734        char name[] = "iwlwifi";
 735
 736        if (!iwl_mvm_is_tt_in_fw(mvm)) {
 737                mvm->tz_device.tzone = NULL;
 738
 739                return;
 740        }
 741
 742        BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);
 743
 744        mvm->tz_device.tzone = thermal_zone_device_register(name,
 745                                                        IWL_MAX_DTS_TRIPS,
 746                                                        IWL_WRITABLE_TRIPS_MSK,
 747                                                        mvm, &tzone_ops,
 748                                                        NULL, 0, 0);
 749        if (IS_ERR(mvm->tz_device.tzone)) {
 750                IWL_DEBUG_TEMP(mvm,
 751                               "Failed to register to thermal zone (err = %ld)\n",
 752                               PTR_ERR(mvm->tz_device.tzone));
 753                mvm->tz_device.tzone = NULL;
 754                return;
 755        }
 756
 757        /* 0 is a valid temperature,
 758         * so initialize the array with S16_MIN which invalid temperature
 759         */
 760        for (i = 0 ; i < IWL_MAX_DTS_TRIPS; i++)
 761                mvm->tz_device.temp_trips[i] = S16_MIN;
 762}
 763
 764static int iwl_mvm_tcool_get_max_state(struct thermal_cooling_device *cdev,
 765                                       unsigned long *state)
 766{
 767        *state = ARRAY_SIZE(iwl_mvm_cdev_budgets) - 1;
 768
 769        return 0;
 770}
 771
 772static int iwl_mvm_tcool_get_cur_state(struct thermal_cooling_device *cdev,
 773                                       unsigned long *state)
 774{
 775        struct iwl_mvm *mvm = (struct iwl_mvm *)(cdev->devdata);
 776
 777        *state = mvm->cooling_dev.cur_state;
 778
 779        return 0;
 780}
 781
 782static int iwl_mvm_tcool_set_cur_state(struct thermal_cooling_device *cdev,
 783                                       unsigned long new_state)
 784{
 785        struct iwl_mvm *mvm = (struct iwl_mvm *)(cdev->devdata);
 786        int ret;
 787
 788        mutex_lock(&mvm->mutex);
 789
 790        if (!iwl_mvm_firmware_running(mvm) ||
 791            mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) {
 792                ret = -EIO;
 793                goto unlock;
 794        }
 795
 796        if (new_state >= ARRAY_SIZE(iwl_mvm_cdev_budgets)) {
 797                ret = -EINVAL;
 798                goto unlock;
 799        }
 800
 801        ret = iwl_mvm_ctdp_command(mvm, CTDP_CMD_OPERATION_START,
 802                                   new_state);
 803
 804unlock:
 805        mutex_unlock(&mvm->mutex);
 806        return ret;
 807}
 808
 809static const struct thermal_cooling_device_ops tcooling_ops = {
 810        .get_max_state = iwl_mvm_tcool_get_max_state,
 811        .get_cur_state = iwl_mvm_tcool_get_cur_state,
 812        .set_cur_state = iwl_mvm_tcool_set_cur_state,
 813};
 814
 815static void iwl_mvm_cooling_device_register(struct iwl_mvm *mvm)
 816{
 817        char name[] = "iwlwifi";
 818
 819        if (!iwl_mvm_is_ctdp_supported(mvm))
 820                return;
 821
 822        BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);
 823
 824        mvm->cooling_dev.cdev =
 825                thermal_cooling_device_register(name,
 826                                                mvm,
 827                                                &tcooling_ops);
 828
 829        if (IS_ERR(mvm->cooling_dev.cdev)) {
 830                IWL_DEBUG_TEMP(mvm,
 831                               "Failed to register to cooling device (err = %ld)\n",
 832                               PTR_ERR(mvm->cooling_dev.cdev));
 833                mvm->cooling_dev.cdev = NULL;
 834                return;
 835        }
 836}
 837
 838static void iwl_mvm_thermal_zone_unregister(struct iwl_mvm *mvm)
 839{
 840        if (!iwl_mvm_is_tt_in_fw(mvm) || !mvm->tz_device.tzone)
 841                return;
 842
 843        IWL_DEBUG_TEMP(mvm, "Thermal zone device unregister\n");
 844        if (mvm->tz_device.tzone) {
 845                thermal_zone_device_unregister(mvm->tz_device.tzone);
 846                mvm->tz_device.tzone = NULL;
 847        }
 848}
 849
 850static void iwl_mvm_cooling_device_unregister(struct iwl_mvm *mvm)
 851{
 852        if (!iwl_mvm_is_ctdp_supported(mvm) || !mvm->cooling_dev.cdev)
 853                return;
 854
 855        IWL_DEBUG_TEMP(mvm, "Cooling device unregister\n");
 856        if (mvm->cooling_dev.cdev) {
 857                thermal_cooling_device_unregister(mvm->cooling_dev.cdev);
 858                mvm->cooling_dev.cdev = NULL;
 859        }
 860}
 861#endif /* CONFIG_THERMAL */
 862
 863void iwl_mvm_thermal_initialize(struct iwl_mvm *mvm, u32 min_backoff)
 864{
 865        struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
 866
 867        IWL_DEBUG_TEMP(mvm, "Initialize Thermal Throttling\n");
 868
 869        if (mvm->cfg->thermal_params)
 870                tt->params = *mvm->cfg->thermal_params;
 871        else
 872                tt->params = iwl_mvm_default_tt_params;
 873
 874        tt->throttle = false;
 875        tt->dynamic_smps = false;
 876        tt->min_backoff = min_backoff;
 877        INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill);
 878
 879#ifdef CONFIG_THERMAL
 880        iwl_mvm_cooling_device_register(mvm);
 881        iwl_mvm_thermal_zone_register(mvm);
 882#endif
 883        mvm->init_status |= IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE;
 884}
 885
 886void iwl_mvm_thermal_exit(struct iwl_mvm *mvm)
 887{
 888        if (!(mvm->init_status & IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE))
 889                return;
 890
 891        cancel_delayed_work_sync(&mvm->thermal_throttle.ct_kill_exit);
 892        IWL_DEBUG_TEMP(mvm, "Exit Thermal Throttling\n");
 893
 894#ifdef CONFIG_THERMAL
 895        iwl_mvm_cooling_device_unregister(mvm);
 896        iwl_mvm_thermal_zone_unregister(mvm);
 897#endif
 898        mvm->init_status &= ~IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE;
 899}
 900