linux/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
   2/*
   3 * Copyright (C) 2012-2014, 2019-2021 Intel Corporation
   4 * Copyright (C) 2013-2014 Intel Mobile Communications GmbH
   5 * Copyright (C) 2015-2016 Intel Deutschland GmbH
   6 */
   7#include <linux/sort.h>
   8
   9#include "mvm.h"
  10
  11#define IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT HZ
  12
  13void iwl_mvm_enter_ctkill(struct iwl_mvm *mvm)
  14{
  15        struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
  16        u32 duration = tt->params.ct_kill_duration;
  17
  18        if (test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status))
  19                return;
  20
  21        IWL_ERR(mvm, "Enter CT Kill\n");
  22        iwl_mvm_set_hw_ctkill_state(mvm, true);
  23
  24        if (!iwl_mvm_is_tt_in_fw(mvm)) {
  25                tt->throttle = false;
  26                tt->dynamic_smps = false;
  27        }
  28
  29        /* Don't schedule an exit work if we're in test mode, since
  30         * the temperature will not change unless we manually set it
  31         * again (or disable testing).
  32         */
  33        if (!mvm->temperature_test)
  34                schedule_delayed_work(&tt->ct_kill_exit,
  35                                      round_jiffies_relative(duration * HZ));
  36}
  37
  38static void iwl_mvm_exit_ctkill(struct iwl_mvm *mvm)
  39{
  40        if (!test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status))
  41                return;
  42
  43        IWL_ERR(mvm, "Exit CT Kill\n");
  44        iwl_mvm_set_hw_ctkill_state(mvm, false);
  45}
  46
  47static void iwl_mvm_tt_temp_changed(struct iwl_mvm *mvm, u32 temp)
  48{
  49        /* ignore the notification if we are in test mode */
  50        if (mvm->temperature_test)
  51                return;
  52
  53        if (mvm->temperature == temp)
  54                return;
  55
  56        mvm->temperature = temp;
  57        iwl_mvm_tt_handler(mvm);
  58}
  59
  60static int iwl_mvm_temp_notif_parse(struct iwl_mvm *mvm,
  61                                    struct iwl_rx_packet *pkt)
  62{
  63        struct iwl_dts_measurement_notif_v1 *notif_v1;
  64        int len = iwl_rx_packet_payload_len(pkt);
  65        int temp;
  66
  67        /* we can use notif_v1 only, because v2 only adds an additional
  68         * parameter, which is not used in this function.
  69        */
  70        if (WARN_ON_ONCE(len < sizeof(*notif_v1))) {
  71                IWL_ERR(mvm, "Invalid DTS_MEASUREMENT_NOTIFICATION\n");
  72                return -EINVAL;
  73        }
  74
  75        notif_v1 = (void *)pkt->data;
  76
  77        temp = le32_to_cpu(notif_v1->temp);
  78
  79        /* shouldn't be negative, but since it's s32, make sure it isn't */
  80        if (WARN_ON_ONCE(temp < 0))
  81                temp = 0;
  82
  83        IWL_DEBUG_TEMP(mvm, "DTS_MEASUREMENT_NOTIFICATION - %d\n", temp);
  84
  85        return temp;
  86}
  87
  88static bool iwl_mvm_temp_notif_wait(struct iwl_notif_wait_data *notif_wait,
  89                                    struct iwl_rx_packet *pkt, void *data)
  90{
  91        struct iwl_mvm *mvm =
  92                container_of(notif_wait, struct iwl_mvm, notif_wait);
  93        int *temp = data;
  94        int ret;
  95
  96        ret = iwl_mvm_temp_notif_parse(mvm, pkt);
  97        if (ret < 0)
  98                return true;
  99
 100        *temp = ret;
 101
 102        return true;
 103}
 104
 105void iwl_mvm_temp_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
 106{
 107        struct iwl_rx_packet *pkt = rxb_addr(rxb);
 108        struct iwl_dts_measurement_notif_v2 *notif_v2;
 109        int len = iwl_rx_packet_payload_len(pkt);
 110        int temp;
 111        u32 ths_crossed;
 112
 113        /* the notification is handled synchronously in ctkill, so skip here */
 114        if (test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status))
 115                return;
 116
 117        temp = iwl_mvm_temp_notif_parse(mvm, pkt);
 118
 119        if (!iwl_mvm_is_tt_in_fw(mvm)) {
 120                if (temp >= 0)
 121                        iwl_mvm_tt_temp_changed(mvm, temp);
 122                return;
 123        }
 124
 125        if (WARN_ON_ONCE(len < sizeof(*notif_v2))) {
 126                IWL_ERR(mvm, "Invalid DTS_MEASUREMENT_NOTIFICATION\n");
 127                return;
 128        }
 129
 130        notif_v2 = (void *)pkt->data;
 131        ths_crossed = le32_to_cpu(notif_v2->threshold_idx);
 132
 133        /* 0xFF in ths_crossed means the notification is not related
 134         * to a trip, so we can ignore it here.
 135         */
 136        if (ths_crossed == 0xFF)
 137                return;
 138
 139        IWL_DEBUG_TEMP(mvm, "Temp = %d Threshold crossed = %d\n",
 140                       temp, ths_crossed);
 141
 142#ifdef CONFIG_THERMAL
 143        if (WARN_ON(ths_crossed >= IWL_MAX_DTS_TRIPS))
 144                return;
 145
 146        if (mvm->tz_device.tzone) {
 147                struct iwl_mvm_thermal_device *tz_dev = &mvm->tz_device;
 148
 149                thermal_zone_device_update(tz_dev->tzone,
 150                                           THERMAL_TRIP_VIOLATED);
 151        }
 152#endif /* CONFIG_THERMAL */
 153}
 154
 155void iwl_mvm_ct_kill_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
 156{
 157        struct iwl_rx_packet *pkt = rxb_addr(rxb);
 158        struct ct_kill_notif *notif;
 159
 160        notif = (struct ct_kill_notif *)pkt->data;
 161        IWL_DEBUG_TEMP(mvm, "CT Kill notification temperature = %d\n",
 162                       notif->temperature);
 163        if (iwl_fw_lookup_notif_ver(mvm->fw, PHY_OPS_GROUP,
 164                                    CT_KILL_NOTIFICATION, 0) > 1)
 165                IWL_DEBUG_TEMP(mvm,
 166                               "CT kill notification DTS bitmap = 0x%x, Scheme = %d\n",
 167                               notif->dts, notif->scheme);
 168
 169        iwl_mvm_enter_ctkill(mvm);
 170}
 171
 172/*
 173 * send the DTS_MEASUREMENT_TRIGGER command with or without waiting for a
 174 * response. If we get a response then the measurement is stored in 'temp'
 175 */
 176static int iwl_mvm_send_temp_cmd(struct iwl_mvm *mvm, bool response, s32 *temp)
 177{
 178        struct iwl_host_cmd cmd = {};
 179        struct iwl_dts_measurement_cmd dts_cmd = {
 180                .flags = cpu_to_le32(DTS_TRIGGER_CMD_FLAGS_TEMP),
 181        };
 182        struct iwl_ext_dts_measurement_cmd ext_cmd = {
 183                .control_mode = cpu_to_le32(DTS_DIRECT_WITHOUT_MEASURE),
 184        };
 185        struct iwl_dts_measurement_resp *resp;
 186        void *cmd_ptr;
 187        int ret;
 188        u32 cmd_flags = 0;
 189        u16 len;
 190
 191        /* Check which command format is used (regular/extended) */
 192        if (fw_has_capa(&mvm->fw->ucode_capa,
 193                        IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE)) {
 194                len = sizeof(ext_cmd);
 195                cmd_ptr = &ext_cmd;
 196        } else {
 197                len = sizeof(dts_cmd);
 198                cmd_ptr = &dts_cmd;
 199        }
 200        /* The command version where we get a response is zero length */
 201        if (response) {
 202                cmd_flags = CMD_WANT_SKB;
 203                len = 0;
 204        }
 205
 206        cmd.id =  WIDE_ID(PHY_OPS_GROUP, CMD_DTS_MEASUREMENT_TRIGGER_WIDE);
 207        cmd.len[0] = len;
 208        cmd.flags = cmd_flags;
 209        cmd.data[0] = cmd_ptr;
 210
 211        IWL_DEBUG_TEMP(mvm,
 212                       "Sending temperature measurement command - %s response\n",
 213                       response ? "with" : "without");
 214        ret = iwl_mvm_send_cmd(mvm, &cmd);
 215
 216        if (ret) {
 217                IWL_ERR(mvm,
 218                        "Failed to send the temperature measurement command (err=%d)\n",
 219                        ret);
 220                return ret;
 221        }
 222
 223        if (response) {
 224                resp = (void *)cmd.resp_pkt->data;
 225                *temp = le32_to_cpu(resp->temp);
 226                IWL_DEBUG_TEMP(mvm,
 227                               "Got temperature measurement response: temp=%d\n",
 228                               *temp);
 229                iwl_free_resp(&cmd);
 230        }
 231
 232        return ret;
 233}
 234
 235int iwl_mvm_get_temp(struct iwl_mvm *mvm, s32 *temp)
 236{
 237        struct iwl_notification_wait wait_temp_notif;
 238        static u16 temp_notif[] = { WIDE_ID(PHY_OPS_GROUP,
 239                                            DTS_MEASUREMENT_NOTIF_WIDE) };
 240        int ret;
 241        u8 cmd_ver;
 242
 243        /*
 244         * If command version is 1 we send the command and immediately get
 245         * a response. For older versions we send the command and wait for a
 246         * notification (no command TLV for previous versions).
 247         */
 248        cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw,
 249                                        WIDE_ID(PHY_OPS_GROUP, CMD_DTS_MEASUREMENT_TRIGGER_WIDE),
 250                                        IWL_FW_CMD_VER_UNKNOWN);
 251        if (cmd_ver == 1)
 252                return iwl_mvm_send_temp_cmd(mvm, true, temp);
 253
 254        lockdep_assert_held(&mvm->mutex);
 255
 256        iwl_init_notification_wait(&mvm->notif_wait, &wait_temp_notif,
 257                                   temp_notif, ARRAY_SIZE(temp_notif),
 258                                   iwl_mvm_temp_notif_wait, temp);
 259
 260        ret = iwl_mvm_send_temp_cmd(mvm, false, temp);
 261        if (ret) {
 262                iwl_remove_notification(&mvm->notif_wait, &wait_temp_notif);
 263                return ret;
 264        }
 265
 266        ret = iwl_wait_notification(&mvm->notif_wait, &wait_temp_notif,
 267                                    IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT);
 268        if (ret)
 269                IWL_WARN(mvm, "Getting the temperature timed out\n");
 270
 271        return ret;
 272}
 273
 274static void check_exit_ctkill(struct work_struct *work)
 275{
 276        struct iwl_mvm_tt_mgmt *tt;
 277        struct iwl_mvm *mvm;
 278        u32 duration;
 279        s32 temp;
 280        int ret;
 281
 282        tt = container_of(work, struct iwl_mvm_tt_mgmt, ct_kill_exit.work);
 283        mvm = container_of(tt, struct iwl_mvm, thermal_throttle);
 284
 285        if (iwl_mvm_is_tt_in_fw(mvm)) {
 286                iwl_mvm_exit_ctkill(mvm);
 287
 288                return;
 289        }
 290
 291        duration = tt->params.ct_kill_duration;
 292
 293        flush_work(&mvm->roc_done_wk);
 294
 295        mutex_lock(&mvm->mutex);
 296
 297        if (__iwl_mvm_mac_start(mvm))
 298                goto reschedule;
 299
 300        ret = iwl_mvm_get_temp(mvm, &temp);
 301
 302        __iwl_mvm_mac_stop(mvm);
 303
 304        if (ret)
 305                goto reschedule;
 306
 307        IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", temp);
 308
 309        if (temp <= tt->params.ct_kill_exit) {
 310                mutex_unlock(&mvm->mutex);
 311                iwl_mvm_exit_ctkill(mvm);
 312                return;
 313        }
 314
 315reschedule:
 316        mutex_unlock(&mvm->mutex);
 317        schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit,
 318                              round_jiffies(duration * HZ));
 319}
 320
 321static void iwl_mvm_tt_smps_iterator(void *_data, u8 *mac,
 322                                     struct ieee80211_vif *vif)
 323{
 324        struct iwl_mvm *mvm = _data;
 325        enum ieee80211_smps_mode smps_mode;
 326
 327        lockdep_assert_held(&mvm->mutex);
 328
 329        if (mvm->thermal_throttle.dynamic_smps)
 330                smps_mode = IEEE80211_SMPS_DYNAMIC;
 331        else
 332                smps_mode = IEEE80211_SMPS_AUTOMATIC;
 333
 334        if (vif->type != NL80211_IFTYPE_STATION)
 335                return;
 336
 337        iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT, smps_mode);
 338}
 339
 340static void iwl_mvm_tt_tx_protection(struct iwl_mvm *mvm, bool enable)
 341{
 342        struct iwl_mvm_sta *mvmsta;
 343        int i, err;
 344
 345        for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) {
 346                mvmsta = iwl_mvm_sta_from_staid_protected(mvm, i);
 347                if (!mvmsta)
 348                        continue;
 349
 350                if (enable == mvmsta->tt_tx_protection)
 351                        continue;
 352                err = iwl_mvm_tx_protection(mvm, mvmsta, enable);
 353                if (err) {
 354                        IWL_ERR(mvm, "Failed to %s Tx protection\n",
 355                                enable ? "enable" : "disable");
 356                } else {
 357                        IWL_DEBUG_TEMP(mvm, "%s Tx protection\n",
 358                                       enable ? "Enable" : "Disable");
 359                        mvmsta->tt_tx_protection = enable;
 360                }
 361        }
 362}
 363
 364void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff)
 365{
 366        struct iwl_host_cmd cmd = {
 367                .id = REPLY_THERMAL_MNG_BACKOFF,
 368                .len = { sizeof(u32), },
 369                .data = { &backoff, },
 370        };
 371
 372        backoff = max(backoff, mvm->thermal_throttle.min_backoff);
 373
 374        if (iwl_mvm_send_cmd(mvm, &cmd) == 0) {
 375                IWL_DEBUG_TEMP(mvm, "Set Thermal Tx backoff to: %u\n",
 376                               backoff);
 377                mvm->thermal_throttle.tx_backoff = backoff;
 378        } else {
 379                IWL_ERR(mvm, "Failed to change Thermal Tx backoff\n");
 380        }
 381}
 382
 383void iwl_mvm_tt_handler(struct iwl_mvm *mvm)
 384{
 385        struct iwl_tt_params *params = &mvm->thermal_throttle.params;
 386        struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
 387        s32 temperature = mvm->temperature;
 388        bool throttle_enable = false;
 389        int i;
 390        u32 tx_backoff;
 391
 392        IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", mvm->temperature);
 393
 394        if (params->support_ct_kill && temperature >= params->ct_kill_entry) {
 395                iwl_mvm_enter_ctkill(mvm);
 396                return;
 397        }
 398
 399        if (params->support_ct_kill &&
 400            temperature <= params->ct_kill_exit) {
 401                iwl_mvm_exit_ctkill(mvm);
 402                return;
 403        }
 404
 405        if (params->support_dynamic_smps) {
 406                if (!tt->dynamic_smps &&
 407                    temperature >= params->dynamic_smps_entry) {
 408                        IWL_DEBUG_TEMP(mvm, "Enable dynamic SMPS\n");
 409                        tt->dynamic_smps = true;
 410                        ieee80211_iterate_active_interfaces_atomic(
 411                                        mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
 412                                        iwl_mvm_tt_smps_iterator, mvm);
 413                        throttle_enable = true;
 414                } else if (tt->dynamic_smps &&
 415                           temperature <= params->dynamic_smps_exit) {
 416                        IWL_DEBUG_TEMP(mvm, "Disable dynamic SMPS\n");
 417                        tt->dynamic_smps = false;
 418                        ieee80211_iterate_active_interfaces_atomic(
 419                                        mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
 420                                        iwl_mvm_tt_smps_iterator, mvm);
 421                }
 422        }
 423
 424        if (params->support_tx_protection) {
 425                if (temperature >= params->tx_protection_entry) {
 426                        iwl_mvm_tt_tx_protection(mvm, true);
 427                        throttle_enable = true;
 428                } else if (temperature <= params->tx_protection_exit) {
 429                        iwl_mvm_tt_tx_protection(mvm, false);
 430                }
 431        }
 432
 433        if (params->support_tx_backoff) {
 434                tx_backoff = tt->min_backoff;
 435                for (i = 0; i < TT_TX_BACKOFF_SIZE; i++) {
 436                        if (temperature < params->tx_backoff[i].temperature)
 437                                break;
 438                        tx_backoff = max(tt->min_backoff,
 439                                         params->tx_backoff[i].backoff);
 440                }
 441                if (tx_backoff != tt->min_backoff)
 442                        throttle_enable = true;
 443                if (tt->tx_backoff != tx_backoff)
 444                        iwl_mvm_tt_tx_backoff(mvm, tx_backoff);
 445        }
 446
 447        if (!tt->throttle && throttle_enable) {
 448                IWL_WARN(mvm,
 449                         "Due to high temperature thermal throttling initiated\n");
 450                tt->throttle = true;
 451        } else if (tt->throttle && !tt->dynamic_smps &&
 452                   tt->tx_backoff == tt->min_backoff &&
 453                   temperature <= params->tx_protection_exit) {
 454                IWL_WARN(mvm,
 455                         "Temperature is back to normal thermal throttling stopped\n");
 456                tt->throttle = false;
 457        }
 458}
 459
 460static const struct iwl_tt_params iwl_mvm_default_tt_params = {
 461        .ct_kill_entry = 118,
 462        .ct_kill_exit = 96,
 463        .ct_kill_duration = 5,
 464        .dynamic_smps_entry = 114,
 465        .dynamic_smps_exit = 110,
 466        .tx_protection_entry = 114,
 467        .tx_protection_exit = 108,
 468        .tx_backoff = {
 469                {.temperature = 112, .backoff = 200},
 470                {.temperature = 113, .backoff = 600},
 471                {.temperature = 114, .backoff = 1200},
 472                {.temperature = 115, .backoff = 2000},
 473                {.temperature = 116, .backoff = 4000},
 474                {.temperature = 117, .backoff = 10000},
 475        },
 476        .support_ct_kill = true,
 477        .support_dynamic_smps = true,
 478        .support_tx_protection = true,
 479        .support_tx_backoff = true,
 480};
 481
 482/* budget in mWatt */
 483static const u32 iwl_mvm_cdev_budgets[] = {
 484        2400,   /* cooling state 0 */
 485        2000,   /* cooling state 1 */
 486        1800,   /* cooling state 2 */
 487        1600,   /* cooling state 3 */
 488        1400,   /* cooling state 4 */
 489        1200,   /* cooling state 5 */
 490        1000,   /* cooling state 6 */
 491        900,    /* cooling state 7 */
 492        800,    /* cooling state 8 */
 493        700,    /* cooling state 9 */
 494        650,    /* cooling state 10 */
 495        600,    /* cooling state 11 */
 496        550,    /* cooling state 12 */
 497        500,    /* cooling state 13 */
 498        450,    /* cooling state 14 */
 499        400,    /* cooling state 15 */
 500        350,    /* cooling state 16 */
 501        300,    /* cooling state 17 */
 502        250,    /* cooling state 18 */
 503        200,    /* cooling state 19 */
 504        150,    /* cooling state 20 */
 505};
 506
 507int iwl_mvm_ctdp_command(struct iwl_mvm *mvm, u32 op, u32 state)
 508{
 509        struct iwl_mvm_ctdp_cmd cmd = {
 510                .operation = cpu_to_le32(op),
 511                .budget = cpu_to_le32(iwl_mvm_cdev_budgets[state]),
 512                .window_size = 0,
 513        };
 514        int ret;
 515        u32 status;
 516
 517        lockdep_assert_held(&mvm->mutex);
 518
 519        status = 0;
 520        ret = iwl_mvm_send_cmd_pdu_status(mvm, WIDE_ID(PHY_OPS_GROUP,
 521                                                       CTDP_CONFIG_CMD),
 522                                          sizeof(cmd), &cmd, &status);
 523
 524        if (ret) {
 525                IWL_ERR(mvm, "cTDP command failed (err=%d)\n", ret);
 526                return ret;
 527        }
 528
 529        switch (op) {
 530        case CTDP_CMD_OPERATION_START:
 531#ifdef CONFIG_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, ret;
 734        char name[16];
 735        static atomic_t counter = ATOMIC_INIT(0);
 736
 737        if (!iwl_mvm_is_tt_in_fw(mvm)) {
 738                mvm->tz_device.tzone = NULL;
 739
 740                return;
 741        }
 742
 743        BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);
 744
 745        sprintf(name, "iwlwifi_%u", atomic_inc_return(&counter) & 0xFF);
 746        mvm->tz_device.tzone = thermal_zone_device_register(name,
 747                                                        IWL_MAX_DTS_TRIPS,
 748                                                        IWL_WRITABLE_TRIPS_MSK,
 749                                                        mvm, &tzone_ops,
 750                                                        NULL, 0, 0);
 751        if (IS_ERR(mvm->tz_device.tzone)) {
 752                IWL_DEBUG_TEMP(mvm,
 753                               "Failed to register to thermal zone (err = %ld)\n",
 754                               PTR_ERR(mvm->tz_device.tzone));
 755                mvm->tz_device.tzone = NULL;
 756                return;
 757        }
 758
 759        ret = thermal_zone_device_enable(mvm->tz_device.tzone);
 760        if (ret) {
 761                IWL_DEBUG_TEMP(mvm, "Failed to enable thermal zone\n");
 762                thermal_zone_device_unregister(mvm->tz_device.tzone);
 763                return;
 764        }
 765
 766        /* 0 is a valid temperature,
 767         * so initialize the array with S16_MIN which invalid temperature
 768         */
 769        for (i = 0 ; i < IWL_MAX_DTS_TRIPS; i++)
 770                mvm->tz_device.temp_trips[i] = S16_MIN;
 771}
 772
 773static int iwl_mvm_tcool_get_max_state(struct thermal_cooling_device *cdev,
 774                                       unsigned long *state)
 775{
 776        *state = ARRAY_SIZE(iwl_mvm_cdev_budgets) - 1;
 777
 778        return 0;
 779}
 780
 781static int iwl_mvm_tcool_get_cur_state(struct thermal_cooling_device *cdev,
 782                                       unsigned long *state)
 783{
 784        struct iwl_mvm *mvm = (struct iwl_mvm *)(cdev->devdata);
 785
 786        *state = mvm->cooling_dev.cur_state;
 787
 788        return 0;
 789}
 790
 791static int iwl_mvm_tcool_set_cur_state(struct thermal_cooling_device *cdev,
 792                                       unsigned long new_state)
 793{
 794        struct iwl_mvm *mvm = (struct iwl_mvm *)(cdev->devdata);
 795        int ret;
 796
 797        mutex_lock(&mvm->mutex);
 798
 799        if (!iwl_mvm_firmware_running(mvm) ||
 800            mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) {
 801                ret = -EIO;
 802                goto unlock;
 803        }
 804
 805        if (new_state >= ARRAY_SIZE(iwl_mvm_cdev_budgets)) {
 806                ret = -EINVAL;
 807                goto unlock;
 808        }
 809
 810        ret = iwl_mvm_ctdp_command(mvm, CTDP_CMD_OPERATION_START,
 811                                   new_state);
 812
 813unlock:
 814        mutex_unlock(&mvm->mutex);
 815        return ret;
 816}
 817
 818static const struct thermal_cooling_device_ops tcooling_ops = {
 819        .get_max_state = iwl_mvm_tcool_get_max_state,
 820        .get_cur_state = iwl_mvm_tcool_get_cur_state,
 821        .set_cur_state = iwl_mvm_tcool_set_cur_state,
 822};
 823
 824static void iwl_mvm_cooling_device_register(struct iwl_mvm *mvm)
 825{
 826        char name[] = "iwlwifi";
 827
 828        if (!iwl_mvm_is_ctdp_supported(mvm))
 829                return;
 830
 831        BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);
 832
 833        mvm->cooling_dev.cdev =
 834                thermal_cooling_device_register(name,
 835                                                mvm,
 836                                                &tcooling_ops);
 837
 838        if (IS_ERR(mvm->cooling_dev.cdev)) {
 839                IWL_DEBUG_TEMP(mvm,
 840                               "Failed to register to cooling device (err = %ld)\n",
 841                               PTR_ERR(mvm->cooling_dev.cdev));
 842                mvm->cooling_dev.cdev = NULL;
 843                return;
 844        }
 845}
 846
 847static void iwl_mvm_thermal_zone_unregister(struct iwl_mvm *mvm)
 848{
 849        if (!iwl_mvm_is_tt_in_fw(mvm) || !mvm->tz_device.tzone)
 850                return;
 851
 852        IWL_DEBUG_TEMP(mvm, "Thermal zone device unregister\n");
 853        if (mvm->tz_device.tzone) {
 854                thermal_zone_device_unregister(mvm->tz_device.tzone);
 855                mvm->tz_device.tzone = NULL;
 856        }
 857}
 858
 859static void iwl_mvm_cooling_device_unregister(struct iwl_mvm *mvm)
 860{
 861        if (!iwl_mvm_is_ctdp_supported(mvm) || !mvm->cooling_dev.cdev)
 862                return;
 863
 864        IWL_DEBUG_TEMP(mvm, "Cooling device unregister\n");
 865        if (mvm->cooling_dev.cdev) {
 866                thermal_cooling_device_unregister(mvm->cooling_dev.cdev);
 867                mvm->cooling_dev.cdev = NULL;
 868        }
 869}
 870#endif /* CONFIG_THERMAL */
 871
 872void iwl_mvm_thermal_initialize(struct iwl_mvm *mvm, u32 min_backoff)
 873{
 874        struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
 875
 876        IWL_DEBUG_TEMP(mvm, "Initialize Thermal Throttling\n");
 877
 878        if (mvm->cfg->thermal_params)
 879                tt->params = *mvm->cfg->thermal_params;
 880        else
 881                tt->params = iwl_mvm_default_tt_params;
 882
 883        tt->throttle = false;
 884        tt->dynamic_smps = false;
 885        tt->min_backoff = min_backoff;
 886        INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill);
 887
 888#ifdef CONFIG_THERMAL
 889        iwl_mvm_cooling_device_register(mvm);
 890        iwl_mvm_thermal_zone_register(mvm);
 891#endif
 892        mvm->init_status |= IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE;
 893}
 894
 895void iwl_mvm_thermal_exit(struct iwl_mvm *mvm)
 896{
 897        if (!(mvm->init_status & IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE))
 898                return;
 899
 900        cancel_delayed_work_sync(&mvm->thermal_throttle.ct_kill_exit);
 901        IWL_DEBUG_TEMP(mvm, "Exit Thermal Throttling\n");
 902
 903#ifdef CONFIG_THERMAL
 904        iwl_mvm_cooling_device_unregister(mvm);
 905        iwl_mvm_thermal_zone_unregister(mvm);
 906#endif
 907        mvm->init_status &= ~IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE;
 908}
 909