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-2020 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
  47void 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_notify_framework(tz_dev->tzone,
 150                                         tz_dev->fw_trips_index[ths_crossed]);
 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        int len = iwl_rx_packet_payload_len(pkt);
 160
 161        if (WARN_ON_ONCE(len != sizeof(*notif))) {
 162                IWL_ERR(mvm, "Invalid CT_KILL_NOTIFICATION\n");
 163                return;
 164        }
 165
 166        notif = (struct ct_kill_notif *)pkt->data;
 167        IWL_DEBUG_TEMP(mvm, "CT Kill notification temperature = %d\n",
 168                       notif->temperature);
 169
 170        iwl_mvm_enter_ctkill(mvm);
 171}
 172
 173/*
 174 * send the DTS_MEASUREMENT_TRIGGER command with or without waiting for a
 175 * response. If we get a response then the measurement is stored in 'temp'
 176 */
 177static int iwl_mvm_send_temp_cmd(struct iwl_mvm *mvm, bool response, s32 *temp)
 178{
 179        struct iwl_host_cmd cmd = {};
 180        struct iwl_dts_measurement_cmd dts_cmd = {
 181                .flags = cpu_to_le32(DTS_TRIGGER_CMD_FLAGS_TEMP),
 182        };
 183        struct iwl_ext_dts_measurement_cmd ext_cmd = {
 184                .control_mode = cpu_to_le32(DTS_DIRECT_WITHOUT_MEASURE),
 185        };
 186        struct iwl_dts_measurement_resp *resp;
 187        void *cmd_ptr;
 188        int ret;
 189        u32 cmd_flags = 0;
 190        u16 len;
 191
 192        /* Check which command format is used (regular/extended) */
 193        if (fw_has_capa(&mvm->fw->ucode_capa,
 194                        IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE)) {
 195                len = sizeof(ext_cmd);
 196                cmd_ptr = &ext_cmd;
 197        } else {
 198                len = sizeof(dts_cmd);
 199                cmd_ptr = &dts_cmd;
 200        }
 201        /* The command version where we get a response is zero length */
 202        if (response) {
 203                cmd_flags = CMD_WANT_SKB;
 204                len = 0;
 205        }
 206
 207        cmd.id =  WIDE_ID(PHY_OPS_GROUP, CMD_DTS_MEASUREMENT_TRIGGER_WIDE);
 208        cmd.len[0] = len;
 209        cmd.flags = cmd_flags;
 210        cmd.data[0] = cmd_ptr;
 211
 212        IWL_DEBUG_TEMP(mvm,
 213                       "Sending temperature measurement command - %s response\n",
 214                       response ? "with" : "without");
 215        ret = iwl_mvm_send_cmd(mvm, &cmd);
 216
 217        if (ret) {
 218                IWL_ERR(mvm,
 219                        "Failed to send the temperature measurement command (err=%d)\n",
 220                        ret);
 221                return ret;
 222        }
 223
 224        if (response) {
 225                resp = (void *)cmd.resp_pkt->data;
 226                *temp = le32_to_cpu(resp->temp);
 227                IWL_DEBUG_TEMP(mvm,
 228                               "Got temperature measurement response: temp=%d\n",
 229                               *temp);
 230                iwl_free_resp(&cmd);
 231        }
 232
 233        return ret;
 234}
 235
 236int iwl_mvm_get_temp(struct iwl_mvm *mvm, s32 *temp)
 237{
 238        struct iwl_notification_wait wait_temp_notif;
 239        static u16 temp_notif[] = { WIDE_ID(PHY_OPS_GROUP,
 240                                            DTS_MEASUREMENT_NOTIF_WIDE) };
 241        int ret;
 242        u8 cmd_ver;
 243
 244        /*
 245         * If command version is 1 we send the command and immediately get
 246         * a response. For older versions we send the command and wait for a
 247         * notification (no command TLV for previous versions).
 248         */
 249        cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, PHY_OPS_GROUP,
 250                                        CMD_DTS_MEASUREMENT_TRIGGER_WIDE,
 251                                        IWL_FW_CMD_VER_UNKNOWN);
 252        if (cmd_ver == 1)
 253                return iwl_mvm_send_temp_cmd(mvm, true, temp);
 254
 255        lockdep_assert_held(&mvm->mutex);
 256
 257        iwl_init_notification_wait(&mvm->notif_wait, &wait_temp_notif,
 258                                   temp_notif, ARRAY_SIZE(temp_notif),
 259                                   iwl_mvm_temp_notif_wait, temp);
 260
 261        ret = iwl_mvm_send_temp_cmd(mvm, false, temp);
 262        if (ret) {
 263                iwl_remove_notification(&mvm->notif_wait, &wait_temp_notif);
 264                return ret;
 265        }
 266
 267        ret = iwl_wait_notification(&mvm->notif_wait, &wait_temp_notif,
 268                                    IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT);
 269        if (ret)
 270                IWL_ERR(mvm, "Getting the temperature timed out\n");
 271
 272        return ret;
 273}
 274
 275static void check_exit_ctkill(struct work_struct *work)
 276{
 277        struct iwl_mvm_tt_mgmt *tt;
 278        struct iwl_mvm *mvm;
 279        u32 duration;
 280        s32 temp;
 281        int ret;
 282
 283        tt = container_of(work, struct iwl_mvm_tt_mgmt, ct_kill_exit.work);
 284        mvm = container_of(tt, struct iwl_mvm, thermal_throttle);
 285
 286        if (iwl_mvm_is_tt_in_fw(mvm)) {
 287                iwl_mvm_exit_ctkill(mvm);
 288
 289                return;
 290        }
 291
 292        duration = tt->params.ct_kill_duration;
 293
 294        flush_work(&mvm->roc_done_wk);
 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 < mvm->fw->ucode_capa.num_stations; 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        2400,   /* cooling state 0 */
 486        2000,   /* cooling state 1 */
 487        1800,   /* cooling state 2 */
 488        1600,   /* cooling state 3 */
 489        1400,   /* cooling state 4 */
 490        1200,   /* cooling state 5 */
 491        1000,   /* cooling state 6 */
 492        900,    /* cooling state 7 */
 493        800,    /* cooling state 8 */
 494        700,    /* cooling state 9 */
 495        650,    /* cooling state 10 */
 496        600,    /* cooling state 11 */
 497        550,    /* cooling state 12 */
 498        500,    /* cooling state 13 */
 499        450,    /* cooling state 14 */
 500        400,    /* cooling state 15 */
 501        350,    /* cooling state 16 */
 502        300,    /* cooling state 17 */
 503        250,    /* cooling state 18 */
 504        200,    /* cooling state 19 */
 505        150,    /* cooling state 20 */
 506};
 507
 508int iwl_mvm_ctdp_command(struct iwl_mvm *mvm, u32 op, u32 state)
 509{
 510        struct iwl_mvm_ctdp_cmd cmd = {
 511                .operation = cpu_to_le32(op),
 512                .budget = cpu_to_le32(iwl_mvm_cdev_budgets[state]),
 513                .window_size = 0,
 514        };
 515        int ret;
 516        u32 status;
 517
 518        lockdep_assert_held(&mvm->mutex);
 519
 520        status = 0;
 521        ret = iwl_mvm_send_cmd_pdu_status(mvm, WIDE_ID(PHY_OPS_GROUP,
 522                                                       CTDP_CONFIG_CMD),
 523                                          sizeof(cmd), &cmd, &status);
 524
 525        if (ret) {
 526                IWL_ERR(mvm, "cTDP command failed (err=%d)\n", ret);
 527                return ret;
 528        }
 529
 530        switch (op) {
 531        case CTDP_CMD_OPERATION_START:
 532#ifdef CONFIG_THERMAL
 533                mvm->cooling_dev.cur_state = state;
 534#endif /* CONFIG_THERMAL */
 535                break;
 536        case CTDP_CMD_OPERATION_REPORT:
 537                IWL_DEBUG_TEMP(mvm, "cTDP avg energy in mWatt = %d\n", status);
 538                /* when the function is called with CTDP_CMD_OPERATION_REPORT
 539                 * option the function should return the average budget value
 540                 * that is received from the FW.
 541                 * The budget can't be less or equal to 0, so it's possible
 542                 * to distinguish between error values and budgets.
 543                 */
 544                return status;
 545        case CTDP_CMD_OPERATION_STOP:
 546                IWL_DEBUG_TEMP(mvm, "cTDP stopped successfully\n");
 547                break;
 548        }
 549
 550        return 0;
 551}
 552
 553#ifdef CONFIG_THERMAL
 554static int compare_temps(const void *a, const void *b)
 555{
 556        return ((s16)le16_to_cpu(*(__le16 *)a) -
 557                (s16)le16_to_cpu(*(__le16 *)b));
 558}
 559#endif
 560
 561int iwl_mvm_send_temp_report_ths_cmd(struct iwl_mvm *mvm)
 562{
 563        struct temp_report_ths_cmd cmd = {0};
 564        int ret;
 565#ifdef CONFIG_THERMAL
 566        int i, j, idx = 0;
 567
 568        lockdep_assert_held(&mvm->mutex);
 569
 570        if (!mvm->tz_device.tzone)
 571                goto send;
 572
 573        /* The driver holds array of temperature trips that are unsorted
 574         * and uncompressed, the FW should get it compressed and sorted
 575         */
 576
 577        /* compress temp_trips to cmd array, remove uninitialized values*/
 578        for (i = 0; i < IWL_MAX_DTS_TRIPS; i++) {
 579                if (mvm->tz_device.temp_trips[i] != S16_MIN) {
 580                        cmd.thresholds[idx++] =
 581                                cpu_to_le16(mvm->tz_device.temp_trips[i]);
 582                }
 583        }
 584        cmd.num_temps = cpu_to_le32(idx);
 585
 586        if (!idx)
 587                goto send;
 588
 589        /*sort cmd array*/
 590        sort(cmd.thresholds, idx, sizeof(s16), compare_temps, NULL);
 591
 592        /* we should save the indexes of trips because we sort
 593         * and compress the orginal array
 594         */
 595        for (i = 0; i < idx; i++) {
 596                for (j = 0; j < IWL_MAX_DTS_TRIPS; j++) {
 597                        if (le16_to_cpu(cmd.thresholds[i]) ==
 598                                mvm->tz_device.temp_trips[j])
 599                                mvm->tz_device.fw_trips_index[i] = j;
 600                }
 601        }
 602
 603send:
 604#endif
 605        ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(PHY_OPS_GROUP,
 606                                                TEMP_REPORTING_THRESHOLDS_CMD),
 607                                   0, sizeof(cmd), &cmd);
 608        if (ret)
 609                IWL_ERR(mvm, "TEMP_REPORT_THS_CMD command failed (err=%d)\n",
 610                        ret);
 611
 612        return ret;
 613}
 614
 615#ifdef CONFIG_THERMAL
 616static int iwl_mvm_tzone_get_temp(struct thermal_zone_device *device,
 617                                  int *temperature)
 618{
 619        struct iwl_mvm *mvm = (struct iwl_mvm *)device->devdata;
 620        int ret;
 621        int temp;
 622
 623        mutex_lock(&mvm->mutex);
 624
 625        if (!iwl_mvm_firmware_running(mvm) ||
 626            mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) {
 627                ret = -ENODATA;
 628                goto out;
 629        }
 630
 631        ret = iwl_mvm_get_temp(mvm, &temp);
 632        if (ret)
 633                goto out;
 634
 635        *temperature = temp * 1000;
 636
 637out:
 638        mutex_unlock(&mvm->mutex);
 639        return ret;
 640}
 641
 642static int iwl_mvm_tzone_get_trip_temp(struct thermal_zone_device *device,
 643                                       int trip, int *temp)
 644{
 645        struct iwl_mvm *mvm = (struct iwl_mvm *)device->devdata;
 646
 647        if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS)
 648                return -EINVAL;
 649
 650        *temp = mvm->tz_device.temp_trips[trip] * 1000;
 651
 652        return 0;
 653}
 654
 655static int iwl_mvm_tzone_get_trip_type(struct thermal_zone_device *device,
 656                                       int trip, enum thermal_trip_type *type)
 657{
 658        if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS)
 659                return -EINVAL;
 660
 661        *type = THERMAL_TRIP_PASSIVE;
 662
 663        return 0;
 664}
 665
 666static int iwl_mvm_tzone_set_trip_temp(struct thermal_zone_device *device,
 667                                       int trip, int temp)
 668{
 669        struct iwl_mvm *mvm = (struct iwl_mvm *)device->devdata;
 670        struct iwl_mvm_thermal_device *tzone;
 671        int i, ret;
 672        s16 temperature;
 673
 674        mutex_lock(&mvm->mutex);
 675
 676        if (!iwl_mvm_firmware_running(mvm) ||
 677            mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) {
 678                ret = -EIO;
 679                goto out;
 680        }
 681
 682        if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS) {
 683                ret = -EINVAL;
 684                goto out;
 685        }
 686
 687        if ((temp / 1000) > S16_MAX) {
 688                ret = -EINVAL;
 689                goto out;
 690        }
 691
 692        temperature = (s16)(temp / 1000);
 693        tzone = &mvm->tz_device;
 694
 695        if (!tzone) {
 696                ret = -EIO;
 697                goto out;
 698        }
 699
 700        /* no updates*/
 701        if (tzone->temp_trips[trip] == temperature) {
 702                ret = 0;
 703                goto out;
 704        }
 705
 706        /* already existing temperature */
 707        for (i = 0; i < IWL_MAX_DTS_TRIPS; i++) {
 708                if (tzone->temp_trips[i] == temperature) {
 709                        ret = -EINVAL;
 710                        goto out;
 711                }
 712        }
 713
 714        tzone->temp_trips[trip] = temperature;
 715
 716        ret = iwl_mvm_send_temp_report_ths_cmd(mvm);
 717out:
 718        mutex_unlock(&mvm->mutex);
 719        return ret;
 720}
 721
 722static  struct thermal_zone_device_ops tzone_ops = {
 723        .get_temp = iwl_mvm_tzone_get_temp,
 724        .get_trip_temp = iwl_mvm_tzone_get_trip_temp,
 725        .get_trip_type = iwl_mvm_tzone_get_trip_type,
 726        .set_trip_temp = iwl_mvm_tzone_set_trip_temp,
 727};
 728
 729/* make all trips writable */
 730#define IWL_WRITABLE_TRIPS_MSK (BIT(IWL_MAX_DTS_TRIPS) - 1)
 731
 732static void iwl_mvm_thermal_zone_register(struct iwl_mvm *mvm)
 733{
 734        int i, ret;
 735        char name[16];
 736        static atomic_t counter = ATOMIC_INIT(0);
 737
 738        if (!iwl_mvm_is_tt_in_fw(mvm)) {
 739                mvm->tz_device.tzone = NULL;
 740
 741                return;
 742        }
 743
 744        BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);
 745
 746        sprintf(name, "iwlwifi_%u", atomic_inc_return(&counter) & 0xFF);
 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        ret = thermal_zone_device_enable(mvm->tz_device.tzone);
 761        if (ret) {
 762                IWL_DEBUG_TEMP(mvm, "Failed to enable thermal zone\n");
 763                thermal_zone_device_unregister(mvm->tz_device.tzone);
 764                return;
 765        }
 766
 767        /* 0 is a valid temperature,
 768         * so initialize the array with S16_MIN which invalid temperature
 769         */
 770        for (i = 0 ; i < IWL_MAX_DTS_TRIPS; i++)
 771                mvm->tz_device.temp_trips[i] = S16_MIN;
 772}
 773
 774static int iwl_mvm_tcool_get_max_state(struct thermal_cooling_device *cdev,
 775                                       unsigned long *state)
 776{
 777        *state = ARRAY_SIZE(iwl_mvm_cdev_budgets) - 1;
 778
 779        return 0;
 780}
 781
 782static int iwl_mvm_tcool_get_cur_state(struct thermal_cooling_device *cdev,
 783                                       unsigned long *state)
 784{
 785        struct iwl_mvm *mvm = (struct iwl_mvm *)(cdev->devdata);
 786
 787        *state = mvm->cooling_dev.cur_state;
 788
 789        return 0;
 790}
 791
 792static int iwl_mvm_tcool_set_cur_state(struct thermal_cooling_device *cdev,
 793                                       unsigned long new_state)
 794{
 795        struct iwl_mvm *mvm = (struct iwl_mvm *)(cdev->devdata);
 796        int ret;
 797
 798        mutex_lock(&mvm->mutex);
 799
 800        if (!iwl_mvm_firmware_running(mvm) ||
 801            mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) {
 802                ret = -EIO;
 803                goto unlock;
 804        }
 805
 806        if (new_state >= ARRAY_SIZE(iwl_mvm_cdev_budgets)) {
 807                ret = -EINVAL;
 808                goto unlock;
 809        }
 810
 811        ret = iwl_mvm_ctdp_command(mvm, CTDP_CMD_OPERATION_START,
 812                                   new_state);
 813
 814unlock:
 815        mutex_unlock(&mvm->mutex);
 816        return ret;
 817}
 818
 819static const struct thermal_cooling_device_ops tcooling_ops = {
 820        .get_max_state = iwl_mvm_tcool_get_max_state,
 821        .get_cur_state = iwl_mvm_tcool_get_cur_state,
 822        .set_cur_state = iwl_mvm_tcool_set_cur_state,
 823};
 824
 825static void iwl_mvm_cooling_device_register(struct iwl_mvm *mvm)
 826{
 827        char name[] = "iwlwifi";
 828
 829        if (!iwl_mvm_is_ctdp_supported(mvm))
 830                return;
 831
 832        BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);
 833
 834        mvm->cooling_dev.cdev =
 835                thermal_cooling_device_register(name,
 836                                                mvm,
 837                                                &tcooling_ops);
 838
 839        if (IS_ERR(mvm->cooling_dev.cdev)) {
 840                IWL_DEBUG_TEMP(mvm,
 841                               "Failed to register to cooling device (err = %ld)\n",
 842                               PTR_ERR(mvm->cooling_dev.cdev));
 843                mvm->cooling_dev.cdev = NULL;
 844                return;
 845        }
 846}
 847
 848static void iwl_mvm_thermal_zone_unregister(struct iwl_mvm *mvm)
 849{
 850        if (!iwl_mvm_is_tt_in_fw(mvm) || !mvm->tz_device.tzone)
 851                return;
 852
 853        IWL_DEBUG_TEMP(mvm, "Thermal zone device unregister\n");
 854        if (mvm->tz_device.tzone) {
 855                thermal_zone_device_unregister(mvm->tz_device.tzone);
 856                mvm->tz_device.tzone = NULL;
 857        }
 858}
 859
 860static void iwl_mvm_cooling_device_unregister(struct iwl_mvm *mvm)
 861{
 862        if (!iwl_mvm_is_ctdp_supported(mvm) || !mvm->cooling_dev.cdev)
 863                return;
 864
 865        IWL_DEBUG_TEMP(mvm, "Cooling device unregister\n");
 866        if (mvm->cooling_dev.cdev) {
 867                thermal_cooling_device_unregister(mvm->cooling_dev.cdev);
 868                mvm->cooling_dev.cdev = NULL;
 869        }
 870}
 871#endif /* CONFIG_THERMAL */
 872
 873void iwl_mvm_thermal_initialize(struct iwl_mvm *mvm, u32 min_backoff)
 874{
 875        struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
 876
 877        IWL_DEBUG_TEMP(mvm, "Initialize Thermal Throttling\n");
 878
 879        if (mvm->cfg->thermal_params)
 880                tt->params = *mvm->cfg->thermal_params;
 881        else
 882                tt->params = iwl_mvm_default_tt_params;
 883
 884        tt->throttle = false;
 885        tt->dynamic_smps = false;
 886        tt->min_backoff = min_backoff;
 887        INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill);
 888
 889#ifdef CONFIG_THERMAL
 890        iwl_mvm_cooling_device_register(mvm);
 891        iwl_mvm_thermal_zone_register(mvm);
 892#endif
 893        mvm->init_status |= IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE;
 894}
 895
 896void iwl_mvm_thermal_exit(struct iwl_mvm *mvm)
 897{
 898        if (!(mvm->init_status & IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE))
 899                return;
 900
 901        cancel_delayed_work_sync(&mvm->thermal_throttle.ct_kill_exit);
 902        IWL_DEBUG_TEMP(mvm, "Exit Thermal Throttling\n");
 903
 904#ifdef CONFIG_THERMAL
 905        iwl_mvm_cooling_device_unregister(mvm);
 906        iwl_mvm_thermal_zone_unregister(mvm);
 907#endif
 908        mvm->init_status &= ~IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE;
 909}
 910