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 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 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        /* make sure the device is available for direct read/writes */
 302        if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_CHECK_CTKILL)) {
 303                __iwl_mvm_mac_stop(mvm);
 304                goto reschedule;
 305        }
 306
 307        ret = iwl_mvm_get_temp(mvm, &temp);
 308
 309        iwl_mvm_unref(mvm, IWL_MVM_REF_CHECK_CTKILL);
 310
 311        __iwl_mvm_mac_stop(mvm);
 312
 313        if (ret)
 314                goto reschedule;
 315
 316        IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", temp);
 317
 318        if (temp <= tt->params.ct_kill_exit) {
 319                mutex_unlock(&mvm->mutex);
 320                iwl_mvm_exit_ctkill(mvm);
 321                return;
 322        }
 323
 324reschedule:
 325        mutex_unlock(&mvm->mutex);
 326        schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit,
 327                              round_jiffies(duration * HZ));
 328}
 329
 330static void iwl_mvm_tt_smps_iterator(void *_data, u8 *mac,
 331                                     struct ieee80211_vif *vif)
 332{
 333        struct iwl_mvm *mvm = _data;
 334        enum ieee80211_smps_mode smps_mode;
 335
 336        lockdep_assert_held(&mvm->mutex);
 337
 338        if (mvm->thermal_throttle.dynamic_smps)
 339                smps_mode = IEEE80211_SMPS_DYNAMIC;
 340        else
 341                smps_mode = IEEE80211_SMPS_AUTOMATIC;
 342
 343        if (vif->type != NL80211_IFTYPE_STATION)
 344                return;
 345
 346        iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT, smps_mode);
 347}
 348
 349static void iwl_mvm_tt_tx_protection(struct iwl_mvm *mvm, bool enable)
 350{
 351        struct iwl_mvm_sta *mvmsta;
 352        int i, err;
 353
 354        for (i = 0; i < ARRAY_SIZE(mvm->fw_id_to_mac_id); i++) {
 355                mvmsta = iwl_mvm_sta_from_staid_protected(mvm, i);
 356                if (!mvmsta)
 357                        continue;
 358
 359                if (enable == mvmsta->tt_tx_protection)
 360                        continue;
 361                err = iwl_mvm_tx_protection(mvm, mvmsta, enable);
 362                if (err) {
 363                        IWL_ERR(mvm, "Failed to %s Tx protection\n",
 364                                enable ? "enable" : "disable");
 365                } else {
 366                        IWL_DEBUG_TEMP(mvm, "%s Tx protection\n",
 367                                       enable ? "Enable" : "Disable");
 368                        mvmsta->tt_tx_protection = enable;
 369                }
 370        }
 371}
 372
 373void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff)
 374{
 375        struct iwl_host_cmd cmd = {
 376                .id = REPLY_THERMAL_MNG_BACKOFF,
 377                .len = { sizeof(u32), },
 378                .data = { &backoff, },
 379        };
 380
 381        backoff = max(backoff, mvm->thermal_throttle.min_backoff);
 382
 383        if (iwl_mvm_send_cmd(mvm, &cmd) == 0) {
 384                IWL_DEBUG_TEMP(mvm, "Set Thermal Tx backoff to: %u\n",
 385                               backoff);
 386                mvm->thermal_throttle.tx_backoff = backoff;
 387        } else {
 388                IWL_ERR(mvm, "Failed to change Thermal Tx backoff\n");
 389        }
 390}
 391
 392void iwl_mvm_tt_handler(struct iwl_mvm *mvm)
 393{
 394        struct iwl_tt_params *params = &mvm->thermal_throttle.params;
 395        struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
 396        s32 temperature = mvm->temperature;
 397        bool throttle_enable = false;
 398        int i;
 399        u32 tx_backoff;
 400
 401        IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", mvm->temperature);
 402
 403        if (params->support_ct_kill && temperature >= params->ct_kill_entry) {
 404                iwl_mvm_enter_ctkill(mvm);
 405                return;
 406        }
 407
 408        if (params->support_ct_kill &&
 409            temperature <= params->ct_kill_exit) {
 410                iwl_mvm_exit_ctkill(mvm);
 411                return;
 412        }
 413
 414        if (params->support_dynamic_smps) {
 415                if (!tt->dynamic_smps &&
 416                    temperature >= params->dynamic_smps_entry) {
 417                        IWL_DEBUG_TEMP(mvm, "Enable dynamic SMPS\n");
 418                        tt->dynamic_smps = true;
 419                        ieee80211_iterate_active_interfaces_atomic(
 420                                        mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
 421                                        iwl_mvm_tt_smps_iterator, mvm);
 422                        throttle_enable = true;
 423                } else if (tt->dynamic_smps &&
 424                           temperature <= params->dynamic_smps_exit) {
 425                        IWL_DEBUG_TEMP(mvm, "Disable dynamic SMPS\n");
 426                        tt->dynamic_smps = false;
 427                        ieee80211_iterate_active_interfaces_atomic(
 428                                        mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
 429                                        iwl_mvm_tt_smps_iterator, mvm);
 430                }
 431        }
 432
 433        if (params->support_tx_protection) {
 434                if (temperature >= params->tx_protection_entry) {
 435                        iwl_mvm_tt_tx_protection(mvm, true);
 436                        throttle_enable = true;
 437                } else if (temperature <= params->tx_protection_exit) {
 438                        iwl_mvm_tt_tx_protection(mvm, false);
 439                }
 440        }
 441
 442        if (params->support_tx_backoff) {
 443                tx_backoff = tt->min_backoff;
 444                for (i = 0; i < TT_TX_BACKOFF_SIZE; i++) {
 445                        if (temperature < params->tx_backoff[i].temperature)
 446                                break;
 447                        tx_backoff = max(tt->min_backoff,
 448                                         params->tx_backoff[i].backoff);
 449                }
 450                if (tx_backoff != tt->min_backoff)
 451                        throttle_enable = true;
 452                if (tt->tx_backoff != tx_backoff)
 453                        iwl_mvm_tt_tx_backoff(mvm, tx_backoff);
 454        }
 455
 456        if (!tt->throttle && throttle_enable) {
 457                IWL_WARN(mvm,
 458                         "Due to high temperature thermal throttling initiated\n");
 459                tt->throttle = true;
 460        } else if (tt->throttle && !tt->dynamic_smps &&
 461                   tt->tx_backoff == tt->min_backoff &&
 462                   temperature <= params->tx_protection_exit) {
 463                IWL_WARN(mvm,
 464                         "Temperature is back to normal thermal throttling stopped\n");
 465                tt->throttle = false;
 466        }
 467}
 468
 469static const struct iwl_tt_params iwl_mvm_default_tt_params = {
 470        .ct_kill_entry = 118,
 471        .ct_kill_exit = 96,
 472        .ct_kill_duration = 5,
 473        .dynamic_smps_entry = 114,
 474        .dynamic_smps_exit = 110,
 475        .tx_protection_entry = 114,
 476        .tx_protection_exit = 108,
 477        .tx_backoff = {
 478                {.temperature = 112, .backoff = 200},
 479                {.temperature = 113, .backoff = 600},
 480                {.temperature = 114, .backoff = 1200},
 481                {.temperature = 115, .backoff = 2000},
 482                {.temperature = 116, .backoff = 4000},
 483                {.temperature = 117, .backoff = 10000},
 484        },
 485        .support_ct_kill = true,
 486        .support_dynamic_smps = true,
 487        .support_tx_protection = true,
 488        .support_tx_backoff = true,
 489};
 490
 491/* budget in mWatt */
 492static const u32 iwl_mvm_cdev_budgets[] = {
 493        2000,   /* cooling state 0 */
 494        1800,   /* cooling state 1 */
 495        1600,   /* cooling state 2 */
 496        1400,   /* cooling state 3 */
 497        1200,   /* cooling state 4 */
 498        1000,   /* cooling state 5 */
 499        900,    /* cooling state 6 */
 500        800,    /* cooling state 7 */
 501        700,    /* cooling state 8 */
 502        650,    /* cooling state 9 */
 503        600,    /* cooling state 10 */
 504        550,    /* cooling state 11 */
 505        500,    /* cooling state 12 */
 506        450,    /* cooling state 13 */
 507        400,    /* cooling state 14 */
 508        350,    /* cooling state 15 */
 509        300,    /* cooling state 16 */
 510        250,    /* cooling state 17 */
 511        200,    /* cooling state 18 */
 512        150,    /* cooling state 19 */
 513};
 514
 515int iwl_mvm_ctdp_command(struct iwl_mvm *mvm, u32 op, u32 state)
 516{
 517        struct iwl_mvm_ctdp_cmd cmd = {
 518                .operation = cpu_to_le32(op),
 519                .budget = cpu_to_le32(iwl_mvm_cdev_budgets[state]),
 520                .window_size = 0,
 521        };
 522        int ret;
 523        u32 status;
 524
 525        lockdep_assert_held(&mvm->mutex);
 526
 527        status = 0;
 528        ret = iwl_mvm_send_cmd_pdu_status(mvm, WIDE_ID(PHY_OPS_GROUP,
 529                                                       CTDP_CONFIG_CMD),
 530                                          sizeof(cmd), &cmd, &status);
 531
 532        if (ret) {
 533                IWL_ERR(mvm, "cTDP command failed (err=%d)\n", ret);
 534                return ret;
 535        }
 536
 537        switch (op) {
 538        case CTDP_CMD_OPERATION_START:
 539#ifdef CONFIG_THERMAL
 540                mvm->cooling_dev.cur_state = state;
 541#endif /* CONFIG_THERMAL */
 542                break;
 543        case CTDP_CMD_OPERATION_REPORT:
 544                IWL_DEBUG_TEMP(mvm, "cTDP avg energy in mWatt = %d\n", status);
 545                /* when the function is called with CTDP_CMD_OPERATION_REPORT
 546                 * option the function should return the average budget value
 547                 * that is received from the FW.
 548                 * The budget can't be less or equal to 0, so it's possible
 549                 * to distinguish between error values and budgets.
 550                 */
 551                return status;
 552        case CTDP_CMD_OPERATION_STOP:
 553                IWL_DEBUG_TEMP(mvm, "cTDP stopped successfully\n");
 554                break;
 555        }
 556
 557        return 0;
 558}
 559
 560#ifdef CONFIG_THERMAL
 561static int compare_temps(const void *a, const void *b)
 562{
 563        return ((s16)le16_to_cpu(*(__le16 *)a) -
 564                (s16)le16_to_cpu(*(__le16 *)b));
 565}
 566
 567int iwl_mvm_send_temp_report_ths_cmd(struct iwl_mvm *mvm)
 568{
 569        struct temp_report_ths_cmd cmd = {0};
 570        int ret, i, j, idx = 0;
 571
 572        lockdep_assert_held(&mvm->mutex);
 573
 574        if (!mvm->tz_device.tzone)
 575                return -EINVAL;
 576
 577        /* The driver holds array of temperature trips that are unsorted
 578         * and uncompressed, the FW should get it compressed and sorted
 579         */
 580
 581        /* compress temp_trips to cmd array, remove uninitialized values*/
 582        for (i = 0; i < IWL_MAX_DTS_TRIPS; i++) {
 583                if (mvm->tz_device.temp_trips[i] != S16_MIN) {
 584                        cmd.thresholds[idx++] =
 585                                cpu_to_le16(mvm->tz_device.temp_trips[i]);
 586                }
 587        }
 588        cmd.num_temps = cpu_to_le32(idx);
 589
 590        if (!idx)
 591                goto send;
 592
 593        /*sort cmd array*/
 594        sort(cmd.thresholds, idx, sizeof(s16), compare_temps, NULL);
 595
 596        /* we should save the indexes of trips because we sort
 597         * and compress the orginal array
 598         */
 599        for (i = 0; i < idx; i++) {
 600                for (j = 0; j < IWL_MAX_DTS_TRIPS; j++) {
 601                        if (le16_to_cpu(cmd.thresholds[i]) ==
 602                                mvm->tz_device.temp_trips[j])
 603                                mvm->tz_device.fw_trips_index[i] = j;
 604                }
 605        }
 606
 607send:
 608        ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(PHY_OPS_GROUP,
 609                                                TEMP_REPORTING_THRESHOLDS_CMD),
 610                                   0, sizeof(cmd), &cmd);
 611        if (ret)
 612                IWL_ERR(mvm, "TEMP_REPORT_THS_CMD command failed (err=%d)\n",
 613                        ret);
 614
 615        return ret;
 616}
 617
 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[] = "iwlwifi";
 738
 739        if (!iwl_mvm_is_tt_in_fw(mvm)) {
 740                mvm->tz_device.tzone = NULL;
 741
 742                return;
 743        }
 744
 745        BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);
 746
 747        mvm->tz_device.tzone = thermal_zone_device_register(name,
 748                                                        IWL_MAX_DTS_TRIPS,
 749                                                        IWL_WRITABLE_TRIPS_MSK,
 750                                                        mvm, &tzone_ops,
 751                                                        NULL, 0, 0);
 752        if (IS_ERR(mvm->tz_device.tzone)) {
 753                IWL_DEBUG_TEMP(mvm,
 754                               "Failed to register to thermal zone (err = %ld)\n",
 755                               PTR_ERR(mvm->tz_device.tzone));
 756                mvm->tz_device.tzone = NULL;
 757                return;
 758        }
 759
 760        /* 0 is a valid temperature,
 761         * so initialize the array with S16_MIN which invalid temperature
 762         */
 763        for (i = 0 ; i < IWL_MAX_DTS_TRIPS; i++)
 764                mvm->tz_device.temp_trips[i] = S16_MIN;
 765}
 766
 767static int iwl_mvm_tcool_get_max_state(struct thermal_cooling_device *cdev,
 768                                       unsigned long *state)
 769{
 770        *state = ARRAY_SIZE(iwl_mvm_cdev_budgets) - 1;
 771
 772        return 0;
 773}
 774
 775static int iwl_mvm_tcool_get_cur_state(struct thermal_cooling_device *cdev,
 776                                       unsigned long *state)
 777{
 778        struct iwl_mvm *mvm = (struct iwl_mvm *)(cdev->devdata);
 779
 780        *state = mvm->cooling_dev.cur_state;
 781
 782        return 0;
 783}
 784
 785static int iwl_mvm_tcool_set_cur_state(struct thermal_cooling_device *cdev,
 786                                       unsigned long new_state)
 787{
 788        struct iwl_mvm *mvm = (struct iwl_mvm *)(cdev->devdata);
 789        int ret;
 790
 791        mutex_lock(&mvm->mutex);
 792
 793        if (!iwl_mvm_firmware_running(mvm) ||
 794            mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) {
 795                ret = -EIO;
 796                goto unlock;
 797        }
 798
 799        if (new_state >= ARRAY_SIZE(iwl_mvm_cdev_budgets)) {
 800                ret = -EINVAL;
 801                goto unlock;
 802        }
 803
 804        ret = iwl_mvm_ctdp_command(mvm, CTDP_CMD_OPERATION_START,
 805                                   new_state);
 806
 807unlock:
 808        mutex_unlock(&mvm->mutex);
 809        return ret;
 810}
 811
 812static const struct thermal_cooling_device_ops tcooling_ops = {
 813        .get_max_state = iwl_mvm_tcool_get_max_state,
 814        .get_cur_state = iwl_mvm_tcool_get_cur_state,
 815        .set_cur_state = iwl_mvm_tcool_set_cur_state,
 816};
 817
 818static void iwl_mvm_cooling_device_register(struct iwl_mvm *mvm)
 819{
 820        char name[] = "iwlwifi";
 821
 822        if (!iwl_mvm_is_ctdp_supported(mvm))
 823                return;
 824
 825        BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);
 826
 827        mvm->cooling_dev.cdev =
 828                thermal_cooling_device_register(name,
 829                                                mvm,
 830                                                &tcooling_ops);
 831
 832        if (IS_ERR(mvm->cooling_dev.cdev)) {
 833                IWL_DEBUG_TEMP(mvm,
 834                               "Failed to register to cooling device (err = %ld)\n",
 835                               PTR_ERR(mvm->cooling_dev.cdev));
 836                mvm->cooling_dev.cdev = NULL;
 837                return;
 838        }
 839}
 840
 841static void iwl_mvm_thermal_zone_unregister(struct iwl_mvm *mvm)
 842{
 843        if (!iwl_mvm_is_tt_in_fw(mvm) || !mvm->tz_device.tzone)
 844                return;
 845
 846        IWL_DEBUG_TEMP(mvm, "Thermal zone device unregister\n");
 847        if (mvm->tz_device.tzone) {
 848                thermal_zone_device_unregister(mvm->tz_device.tzone);
 849                mvm->tz_device.tzone = NULL;
 850        }
 851}
 852
 853static void iwl_mvm_cooling_device_unregister(struct iwl_mvm *mvm)
 854{
 855        if (!iwl_mvm_is_ctdp_supported(mvm) || !mvm->cooling_dev.cdev)
 856                return;
 857
 858        IWL_DEBUG_TEMP(mvm, "Cooling device unregister\n");
 859        if (mvm->cooling_dev.cdev) {
 860                thermal_cooling_device_unregister(mvm->cooling_dev.cdev);
 861                mvm->cooling_dev.cdev = NULL;
 862        }
 863}
 864#endif /* CONFIG_THERMAL */
 865
 866void iwl_mvm_thermal_initialize(struct iwl_mvm *mvm, u32 min_backoff)
 867{
 868        struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
 869
 870        IWL_DEBUG_TEMP(mvm, "Initialize Thermal Throttling\n");
 871
 872        if (mvm->cfg->thermal_params)
 873                tt->params = *mvm->cfg->thermal_params;
 874        else
 875                tt->params = iwl_mvm_default_tt_params;
 876
 877        tt->throttle = false;
 878        tt->dynamic_smps = false;
 879        tt->min_backoff = min_backoff;
 880        INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill);
 881
 882#ifdef CONFIG_THERMAL
 883        iwl_mvm_cooling_device_register(mvm);
 884        iwl_mvm_thermal_zone_register(mvm);
 885#endif
 886        mvm->init_status |= IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE;
 887}
 888
 889void iwl_mvm_thermal_exit(struct iwl_mvm *mvm)
 890{
 891        if (!(mvm->init_status & IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE))
 892                return;
 893
 894        cancel_delayed_work_sync(&mvm->thermal_throttle.ct_kill_exit);
 895        IWL_DEBUG_TEMP(mvm, "Exit Thermal Throttling\n");
 896
 897#ifdef CONFIG_THERMAL
 898        iwl_mvm_cooling_device_unregister(mvm);
 899        iwl_mvm_thermal_zone_unregister(mvm);
 900#endif
 901        mvm->init_status &= ~IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE;
 902}
 903