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
  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
 164        iwl_mvm_enter_ctkill(mvm);
 165}
 166
 167/*
 168 * send the DTS_MEASUREMENT_TRIGGER command with or without waiting for a
 169 * response. If we get a response then the measurement is stored in 'temp'
 170 */
 171static int iwl_mvm_send_temp_cmd(struct iwl_mvm *mvm, bool response, s32 *temp)
 172{
 173        struct iwl_host_cmd cmd = {};
 174        struct iwl_dts_measurement_cmd dts_cmd = {
 175                .flags = cpu_to_le32(DTS_TRIGGER_CMD_FLAGS_TEMP),
 176        };
 177        struct iwl_ext_dts_measurement_cmd ext_cmd = {
 178                .control_mode = cpu_to_le32(DTS_DIRECT_WITHOUT_MEASURE),
 179        };
 180        struct iwl_dts_measurement_resp *resp;
 181        void *cmd_ptr;
 182        int ret;
 183        u32 cmd_flags = 0;
 184        u16 len;
 185
 186        /* Check which command format is used (regular/extended) */
 187        if (fw_has_capa(&mvm->fw->ucode_capa,
 188                        IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE)) {
 189                len = sizeof(ext_cmd);
 190                cmd_ptr = &ext_cmd;
 191        } else {
 192                len = sizeof(dts_cmd);
 193                cmd_ptr = &dts_cmd;
 194        }
 195        /* The command version where we get a response is zero length */
 196        if (response) {
 197                cmd_flags = CMD_WANT_SKB;
 198                len = 0;
 199        }
 200
 201        cmd.id =  WIDE_ID(PHY_OPS_GROUP, CMD_DTS_MEASUREMENT_TRIGGER_WIDE);
 202        cmd.len[0] = len;
 203        cmd.flags = cmd_flags;
 204        cmd.data[0] = cmd_ptr;
 205
 206        IWL_DEBUG_TEMP(mvm,
 207                       "Sending temperature measurement command - %s response\n",
 208                       response ? "with" : "without");
 209        ret = iwl_mvm_send_cmd(mvm, &cmd);
 210
 211        if (ret) {
 212                IWL_ERR(mvm,
 213                        "Failed to send the temperature measurement command (err=%d)\n",
 214                        ret);
 215                return ret;
 216        }
 217
 218        if (response) {
 219                resp = (void *)cmd.resp_pkt->data;
 220                *temp = le32_to_cpu(resp->temp);
 221                IWL_DEBUG_TEMP(mvm,
 222                               "Got temperature measurement response: temp=%d\n",
 223                               *temp);
 224                iwl_free_resp(&cmd);
 225        }
 226
 227        return ret;
 228}
 229
 230int iwl_mvm_get_temp(struct iwl_mvm *mvm, s32 *temp)
 231{
 232        struct iwl_notification_wait wait_temp_notif;
 233        static u16 temp_notif[] = { WIDE_ID(PHY_OPS_GROUP,
 234                                            DTS_MEASUREMENT_NOTIF_WIDE) };
 235        int ret;
 236        u8 cmd_ver;
 237
 238        /*
 239         * If command version is 1 we send the command and immediately get
 240         * a response. For older versions we send the command and wait for a
 241         * notification (no command TLV for previous versions).
 242         */
 243        cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, PHY_OPS_GROUP,
 244                                        CMD_DTS_MEASUREMENT_TRIGGER_WIDE,
 245                                        IWL_FW_CMD_VER_UNKNOWN);
 246        if (cmd_ver == 1)
 247                return iwl_mvm_send_temp_cmd(mvm, true, temp);
 248
 249        lockdep_assert_held(&mvm->mutex);
 250
 251        iwl_init_notification_wait(&mvm->notif_wait, &wait_temp_notif,
 252                                   temp_notif, ARRAY_SIZE(temp_notif),
 253                                   iwl_mvm_temp_notif_wait, temp);
 254
 255        ret = iwl_mvm_send_temp_cmd(mvm, false, temp);
 256        if (ret) {
 257                iwl_remove_notification(&mvm->notif_wait, &wait_temp_notif);
 258                return ret;
 259        }
 260
 261        ret = iwl_wait_notification(&mvm->notif_wait, &wait_temp_notif,
 262                                    IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT);
 263        if (ret)
 264                IWL_WARN(mvm, "Getting the temperature timed out\n");
 265
 266        return ret;
 267}
 268
 269static void check_exit_ctkill(struct work_struct *work)
 270{
 271        struct iwl_mvm_tt_mgmt *tt;
 272        struct iwl_mvm *mvm;
 273        u32 duration;
 274        s32 temp;
 275        int ret;
 276
 277        tt = container_of(work, struct iwl_mvm_tt_mgmt, ct_kill_exit.work);
 278        mvm = container_of(tt, struct iwl_mvm, thermal_throttle);
 279
 280        if (iwl_mvm_is_tt_in_fw(mvm)) {
 281                iwl_mvm_exit_ctkill(mvm);
 282
 283                return;
 284        }
 285
 286        duration = tt->params.ct_kill_duration;
 287
 288        flush_work(&mvm->roc_done_wk);
 289
 290        mutex_lock(&mvm->mutex);
 291
 292        if (__iwl_mvm_mac_start(mvm))
 293                goto reschedule;
 294
 295        ret = iwl_mvm_get_temp(mvm, &temp);
 296
 297        __iwl_mvm_mac_stop(mvm);
 298
 299        if (ret)
 300                goto reschedule;
 301
 302        IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", temp);
 303
 304        if (temp <= tt->params.ct_kill_exit) {
 305                mutex_unlock(&mvm->mutex);
 306                iwl_mvm_exit_ctkill(mvm);
 307                return;
 308        }
 309
 310reschedule:
 311        mutex_unlock(&mvm->mutex);
 312        schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit,
 313                              round_jiffies(duration * HZ));
 314}
 315
 316static void iwl_mvm_tt_smps_iterator(void *_data, u8 *mac,
 317                                     struct ieee80211_vif *vif)
 318{
 319        struct iwl_mvm *mvm = _data;
 320        enum ieee80211_smps_mode smps_mode;
 321
 322        lockdep_assert_held(&mvm->mutex);
 323
 324        if (mvm->thermal_throttle.dynamic_smps)
 325                smps_mode = IEEE80211_SMPS_DYNAMIC;
 326        else
 327                smps_mode = IEEE80211_SMPS_AUTOMATIC;
 328
 329        if (vif->type != NL80211_IFTYPE_STATION)
 330                return;
 331
 332        iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT, smps_mode);
 333}
 334
 335static void iwl_mvm_tt_tx_protection(struct iwl_mvm *mvm, bool enable)
 336{
 337        struct iwl_mvm_sta *mvmsta;
 338        int i, err;
 339
 340        for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) {
 341                mvmsta = iwl_mvm_sta_from_staid_protected(mvm, i);
 342                if (!mvmsta)
 343                        continue;
 344
 345                if (enable == mvmsta->tt_tx_protection)
 346                        continue;
 347                err = iwl_mvm_tx_protection(mvm, mvmsta, enable);
 348                if (err) {
 349                        IWL_ERR(mvm, "Failed to %s Tx protection\n",
 350                                enable ? "enable" : "disable");
 351                } else {
 352                        IWL_DEBUG_TEMP(mvm, "%s Tx protection\n",
 353                                       enable ? "Enable" : "Disable");
 354                        mvmsta->tt_tx_protection = enable;
 355                }
 356        }
 357}
 358
 359void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff)
 360{
 361        struct iwl_host_cmd cmd = {
 362                .id = REPLY_THERMAL_MNG_BACKOFF,
 363                .len = { sizeof(u32), },
 364                .data = { &backoff, },
 365        };
 366
 367        backoff = max(backoff, mvm->thermal_throttle.min_backoff);
 368
 369        if (iwl_mvm_send_cmd(mvm, &cmd) == 0) {
 370                IWL_DEBUG_TEMP(mvm, "Set Thermal Tx backoff to: %u\n",
 371                               backoff);
 372                mvm->thermal_throttle.tx_backoff = backoff;
 373        } else {
 374                IWL_ERR(mvm, "Failed to change Thermal Tx backoff\n");
 375        }
 376}
 377
 378void iwl_mvm_tt_handler(struct iwl_mvm *mvm)
 379{
 380        struct iwl_tt_params *params = &mvm->thermal_throttle.params;
 381        struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
 382        s32 temperature = mvm->temperature;
 383        bool throttle_enable = false;
 384        int i;
 385        u32 tx_backoff;
 386
 387        IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", mvm->temperature);
 388
 389        if (params->support_ct_kill && temperature >= params->ct_kill_entry) {
 390                iwl_mvm_enter_ctkill(mvm);
 391                return;
 392        }
 393
 394        if (params->support_ct_kill &&
 395            temperature <= params->ct_kill_exit) {
 396                iwl_mvm_exit_ctkill(mvm);
 397                return;
 398        }
 399
 400        if (params->support_dynamic_smps) {
 401                if (!tt->dynamic_smps &&
 402                    temperature >= params->dynamic_smps_entry) {
 403                        IWL_DEBUG_TEMP(mvm, "Enable dynamic SMPS\n");
 404                        tt->dynamic_smps = true;
 405                        ieee80211_iterate_active_interfaces_atomic(
 406                                        mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
 407                                        iwl_mvm_tt_smps_iterator, mvm);
 408                        throttle_enable = true;
 409                } else if (tt->dynamic_smps &&
 410                           temperature <= params->dynamic_smps_exit) {
 411                        IWL_DEBUG_TEMP(mvm, "Disable dynamic SMPS\n");
 412                        tt->dynamic_smps = false;
 413                        ieee80211_iterate_active_interfaces_atomic(
 414                                        mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
 415                                        iwl_mvm_tt_smps_iterator, mvm);
 416                }
 417        }
 418
 419        if (params->support_tx_protection) {
 420                if (temperature >= params->tx_protection_entry) {
 421                        iwl_mvm_tt_tx_protection(mvm, true);
 422                        throttle_enable = true;
 423                } else if (temperature <= params->tx_protection_exit) {
 424                        iwl_mvm_tt_tx_protection(mvm, false);
 425                }
 426        }
 427
 428        if (params->support_tx_backoff) {
 429                tx_backoff = tt->min_backoff;
 430                for (i = 0; i < TT_TX_BACKOFF_SIZE; i++) {
 431                        if (temperature < params->tx_backoff[i].temperature)
 432                                break;
 433                        tx_backoff = max(tt->min_backoff,
 434                                         params->tx_backoff[i].backoff);
 435                }
 436                if (tx_backoff != tt->min_backoff)
 437                        throttle_enable = true;
 438                if (tt->tx_backoff != tx_backoff)
 439                        iwl_mvm_tt_tx_backoff(mvm, tx_backoff);
 440        }
 441
 442        if (!tt->throttle && throttle_enable) {
 443                IWL_WARN(mvm,
 444                         "Due to high temperature thermal throttling initiated\n");
 445                tt->throttle = true;
 446        } else if (tt->throttle && !tt->dynamic_smps &&
 447                   tt->tx_backoff == tt->min_backoff &&
 448                   temperature <= params->tx_protection_exit) {
 449                IWL_WARN(mvm,
 450                         "Temperature is back to normal thermal throttling stopped\n");
 451                tt->throttle = false;
 452        }
 453}
 454
 455static const struct iwl_tt_params iwl_mvm_default_tt_params = {
 456        .ct_kill_entry = 118,
 457        .ct_kill_exit = 96,
 458        .ct_kill_duration = 5,
 459        .dynamic_smps_entry = 114,
 460        .dynamic_smps_exit = 110,
 461        .tx_protection_entry = 114,
 462        .tx_protection_exit = 108,
 463        .tx_backoff = {
 464                {.temperature = 112, .backoff = 200},
 465                {.temperature = 113, .backoff = 600},
 466                {.temperature = 114, .backoff = 1200},
 467                {.temperature = 115, .backoff = 2000},
 468                {.temperature = 116, .backoff = 4000},
 469                {.temperature = 117, .backoff = 10000},
 470        },
 471        .support_ct_kill = true,
 472        .support_dynamic_smps = true,
 473        .support_tx_protection = true,
 474        .support_tx_backoff = true,
 475};
 476
 477/* budget in mWatt */
 478static const u32 iwl_mvm_cdev_budgets[] = {
 479        2400,   /* cooling state 0 */
 480        2000,   /* cooling state 1 */
 481        1800,   /* cooling state 2 */
 482        1600,   /* cooling state 3 */
 483        1400,   /* cooling state 4 */
 484        1200,   /* cooling state 5 */
 485        1000,   /* cooling state 6 */
 486        900,    /* cooling state 7 */
 487        800,    /* cooling state 8 */
 488        700,    /* cooling state 9 */
 489        650,    /* cooling state 10 */
 490        600,    /* cooling state 11 */
 491        550,    /* cooling state 12 */
 492        500,    /* cooling state 13 */
 493        450,    /* cooling state 14 */
 494        400,    /* cooling state 15 */
 495        350,    /* cooling state 16 */
 496        300,    /* cooling state 17 */
 497        250,    /* cooling state 18 */
 498        200,    /* cooling state 19 */
 499        150,    /* cooling state 20 */
 500};
 501
 502int iwl_mvm_ctdp_command(struct iwl_mvm *mvm, u32 op, u32 state)
 503{
 504        struct iwl_mvm_ctdp_cmd cmd = {
 505                .operation = cpu_to_le32(op),
 506                .budget = cpu_to_le32(iwl_mvm_cdev_budgets[state]),
 507                .window_size = 0,
 508        };
 509        int ret;
 510        u32 status;
 511
 512        lockdep_assert_held(&mvm->mutex);
 513
 514        status = 0;
 515        ret = iwl_mvm_send_cmd_pdu_status(mvm, WIDE_ID(PHY_OPS_GROUP,
 516                                                       CTDP_CONFIG_CMD),
 517                                          sizeof(cmd), &cmd, &status);
 518
 519        if (ret) {
 520                IWL_ERR(mvm, "cTDP command failed (err=%d)\n", ret);
 521                return ret;
 522        }
 523
 524        switch (op) {
 525        case CTDP_CMD_OPERATION_START:
 526#ifdef CONFIG_THERMAL
 527                mvm->cooling_dev.cur_state = state;
 528#endif /* CONFIG_THERMAL */
 529                break;
 530        case CTDP_CMD_OPERATION_REPORT:
 531                IWL_DEBUG_TEMP(mvm, "cTDP avg energy in mWatt = %d\n", status);
 532                /* when the function is called with CTDP_CMD_OPERATION_REPORT
 533                 * option the function should return the average budget value
 534                 * that is received from the FW.
 535                 * The budget can't be less or equal to 0, so it's possible
 536                 * to distinguish between error values and budgets.
 537                 */
 538                return status;
 539        case CTDP_CMD_OPERATION_STOP:
 540                IWL_DEBUG_TEMP(mvm, "cTDP stopped successfully\n");
 541                break;
 542        }
 543
 544        return 0;
 545}
 546
 547#ifdef CONFIG_THERMAL
 548static int compare_temps(const void *a, const void *b)
 549{
 550        return ((s16)le16_to_cpu(*(__le16 *)a) -
 551                (s16)le16_to_cpu(*(__le16 *)b));
 552}
 553#endif
 554
 555int iwl_mvm_send_temp_report_ths_cmd(struct iwl_mvm *mvm)
 556{
 557        struct temp_report_ths_cmd cmd = {0};
 558        int ret;
 559#ifdef CONFIG_THERMAL
 560        int i, j, idx = 0;
 561
 562        lockdep_assert_held(&mvm->mutex);
 563
 564        if (!mvm->tz_device.tzone)
 565                goto send;
 566
 567        /* The driver holds array of temperature trips that are unsorted
 568         * and uncompressed, the FW should get it compressed and sorted
 569         */
 570
 571        /* compress temp_trips to cmd array, remove uninitialized values*/
 572        for (i = 0; i < IWL_MAX_DTS_TRIPS; i++) {
 573                if (mvm->tz_device.temp_trips[i] != S16_MIN) {
 574                        cmd.thresholds[idx++] =
 575                                cpu_to_le16(mvm->tz_device.temp_trips[i]);
 576                }
 577        }
 578        cmd.num_temps = cpu_to_le32(idx);
 579
 580        if (!idx)
 581                goto send;
 582
 583        /*sort cmd array*/
 584        sort(cmd.thresholds, idx, sizeof(s16), compare_temps, NULL);
 585
 586        /* we should save the indexes of trips because we sort
 587         * and compress the orginal array
 588         */
 589        for (i = 0; i < idx; i++) {
 590                for (j = 0; j < IWL_MAX_DTS_TRIPS; j++) {
 591                        if (le16_to_cpu(cmd.thresholds[i]) ==
 592                                mvm->tz_device.temp_trips[j])
 593                                mvm->tz_device.fw_trips_index[i] = j;
 594                }
 595        }
 596
 597send:
 598#endif
 599        ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(PHY_OPS_GROUP,
 600                                                TEMP_REPORTING_THRESHOLDS_CMD),
 601                                   0, sizeof(cmd), &cmd);
 602        if (ret)
 603                IWL_ERR(mvm, "TEMP_REPORT_THS_CMD command failed (err=%d)\n",
 604                        ret);
 605
 606        return ret;
 607}
 608
 609#ifdef CONFIG_THERMAL
 610static int iwl_mvm_tzone_get_temp(struct thermal_zone_device *device,
 611                                  int *temperature)
 612{
 613        struct iwl_mvm *mvm = (struct iwl_mvm *)device->devdata;
 614        int ret;
 615        int temp;
 616
 617        mutex_lock(&mvm->mutex);
 618
 619        if (!iwl_mvm_firmware_running(mvm) ||
 620            mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) {
 621                ret = -ENODATA;
 622                goto out;
 623        }
 624
 625        ret = iwl_mvm_get_temp(mvm, &temp);
 626        if (ret)
 627                goto out;
 628
 629        *temperature = temp * 1000;
 630
 631out:
 632        mutex_unlock(&mvm->mutex);
 633        return ret;
 634}
 635
 636static int iwl_mvm_tzone_get_trip_temp(struct thermal_zone_device *device,
 637                                       int trip, int *temp)
 638{
 639        struct iwl_mvm *mvm = (struct iwl_mvm *)device->devdata;
 640
 641        if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS)
 642                return -EINVAL;
 643
 644        *temp = mvm->tz_device.temp_trips[trip] * 1000;
 645
 646        return 0;
 647}
 648
 649static int iwl_mvm_tzone_get_trip_type(struct thermal_zone_device *device,
 650                                       int trip, enum thermal_trip_type *type)
 651{
 652        if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS)
 653                return -EINVAL;
 654
 655        *type = THERMAL_TRIP_PASSIVE;
 656
 657        return 0;
 658}
 659
 660static int iwl_mvm_tzone_set_trip_temp(struct thermal_zone_device *device,
 661                                       int trip, int temp)
 662{
 663        struct iwl_mvm *mvm = (struct iwl_mvm *)device->devdata;
 664        struct iwl_mvm_thermal_device *tzone;
 665        int i, ret;
 666        s16 temperature;
 667
 668        mutex_lock(&mvm->mutex);
 669
 670        if (!iwl_mvm_firmware_running(mvm) ||
 671            mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) {
 672                ret = -EIO;
 673                goto out;
 674        }
 675
 676        if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS) {
 677                ret = -EINVAL;
 678                goto out;
 679        }
 680
 681        if ((temp / 1000) > S16_MAX) {
 682                ret = -EINVAL;
 683                goto out;
 684        }
 685
 686        temperature = (s16)(temp / 1000);
 687        tzone = &mvm->tz_device;
 688
 689        if (!tzone) {
 690                ret = -EIO;
 691                goto out;
 692        }
 693
 694        /* no updates*/
 695        if (tzone->temp_trips[trip] == temperature) {
 696                ret = 0;
 697                goto out;
 698        }
 699
 700        /* already existing temperature */
 701        for (i = 0; i < IWL_MAX_DTS_TRIPS; i++) {
 702                if (tzone->temp_trips[i] == temperature) {
 703                        ret = -EINVAL;
 704                        goto out;
 705                }
 706        }
 707
 708        tzone->temp_trips[trip] = temperature;
 709
 710        ret = iwl_mvm_send_temp_report_ths_cmd(mvm);
 711out:
 712        mutex_unlock(&mvm->mutex);
 713        return ret;
 714}
 715
 716static  struct thermal_zone_device_ops tzone_ops = {
 717        .get_temp = iwl_mvm_tzone_get_temp,
 718        .get_trip_temp = iwl_mvm_tzone_get_trip_temp,
 719        .get_trip_type = iwl_mvm_tzone_get_trip_type,
 720        .set_trip_temp = iwl_mvm_tzone_set_trip_temp,
 721};
 722
 723/* make all trips writable */
 724#define IWL_WRITABLE_TRIPS_MSK (BIT(IWL_MAX_DTS_TRIPS) - 1)
 725
 726static void iwl_mvm_thermal_zone_register(struct iwl_mvm *mvm)
 727{
 728        int i, ret;
 729        char name[16];
 730        static atomic_t counter = ATOMIC_INIT(0);
 731
 732        if (!iwl_mvm_is_tt_in_fw(mvm)) {
 733                mvm->tz_device.tzone = NULL;
 734
 735                return;
 736        }
 737
 738        BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);
 739
 740        sprintf(name, "iwlwifi_%u", atomic_inc_return(&counter) & 0xFF);
 741        mvm->tz_device.tzone = thermal_zone_device_register(name,
 742                                                        IWL_MAX_DTS_TRIPS,
 743                                                        IWL_WRITABLE_TRIPS_MSK,
 744                                                        mvm, &tzone_ops,
 745                                                        NULL, 0, 0);
 746        if (IS_ERR(mvm->tz_device.tzone)) {
 747                IWL_DEBUG_TEMP(mvm,
 748                               "Failed to register to thermal zone (err = %ld)\n",
 749                               PTR_ERR(mvm->tz_device.tzone));
 750                mvm->tz_device.tzone = NULL;
 751                return;
 752        }
 753
 754        ret = thermal_zone_device_enable(mvm->tz_device.tzone);
 755        if (ret) {
 756                IWL_DEBUG_TEMP(mvm, "Failed to enable thermal zone\n");
 757                thermal_zone_device_unregister(mvm->tz_device.tzone);
 758                return;
 759        }
 760
 761        /* 0 is a valid temperature,
 762         * so initialize the array with S16_MIN which invalid temperature
 763         */
 764        for (i = 0 ; i < IWL_MAX_DTS_TRIPS; i++)
 765                mvm->tz_device.temp_trips[i] = S16_MIN;
 766}
 767
 768static int iwl_mvm_tcool_get_max_state(struct thermal_cooling_device *cdev,
 769                                       unsigned long *state)
 770{
 771        *state = ARRAY_SIZE(iwl_mvm_cdev_budgets) - 1;
 772
 773        return 0;
 774}
 775
 776static int iwl_mvm_tcool_get_cur_state(struct thermal_cooling_device *cdev,
 777                                       unsigned long *state)
 778{
 779        struct iwl_mvm *mvm = (struct iwl_mvm *)(cdev->devdata);
 780
 781        *state = mvm->cooling_dev.cur_state;
 782
 783        return 0;
 784}
 785
 786static int iwl_mvm_tcool_set_cur_state(struct thermal_cooling_device *cdev,
 787                                       unsigned long new_state)
 788{
 789        struct iwl_mvm *mvm = (struct iwl_mvm *)(cdev->devdata);
 790        int ret;
 791
 792        mutex_lock(&mvm->mutex);
 793
 794        if (!iwl_mvm_firmware_running(mvm) ||
 795            mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) {
 796                ret = -EIO;
 797                goto unlock;
 798        }
 799
 800        if (new_state >= ARRAY_SIZE(iwl_mvm_cdev_budgets)) {
 801                ret = -EINVAL;
 802                goto unlock;
 803        }
 804
 805        ret = iwl_mvm_ctdp_command(mvm, CTDP_CMD_OPERATION_START,
 806                                   new_state);
 807
 808unlock:
 809        mutex_unlock(&mvm->mutex);
 810        return ret;
 811}
 812
 813static const struct thermal_cooling_device_ops tcooling_ops = {
 814        .get_max_state = iwl_mvm_tcool_get_max_state,
 815        .get_cur_state = iwl_mvm_tcool_get_cur_state,
 816        .set_cur_state = iwl_mvm_tcool_set_cur_state,
 817};
 818
 819static void iwl_mvm_cooling_device_register(struct iwl_mvm *mvm)
 820{
 821        char name[] = "iwlwifi";
 822
 823        if (!iwl_mvm_is_ctdp_supported(mvm))
 824                return;
 825
 826        BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);
 827
 828        mvm->cooling_dev.cdev =
 829                thermal_cooling_device_register(name,
 830                                                mvm,
 831                                                &tcooling_ops);
 832
 833        if (IS_ERR(mvm->cooling_dev.cdev)) {
 834                IWL_DEBUG_TEMP(mvm,
 835                               "Failed to register to cooling device (err = %ld)\n",
 836                               PTR_ERR(mvm->cooling_dev.cdev));
 837                mvm->cooling_dev.cdev = NULL;
 838                return;
 839        }
 840}
 841
 842static void iwl_mvm_thermal_zone_unregister(struct iwl_mvm *mvm)
 843{
 844        if (!iwl_mvm_is_tt_in_fw(mvm) || !mvm->tz_device.tzone)
 845                return;
 846
 847        IWL_DEBUG_TEMP(mvm, "Thermal zone device unregister\n");
 848        if (mvm->tz_device.tzone) {
 849                thermal_zone_device_unregister(mvm->tz_device.tzone);
 850                mvm->tz_device.tzone = NULL;
 851        }
 852}
 853
 854static void iwl_mvm_cooling_device_unregister(struct iwl_mvm *mvm)
 855{
 856        if (!iwl_mvm_is_ctdp_supported(mvm) || !mvm->cooling_dev.cdev)
 857                return;
 858
 859        IWL_DEBUG_TEMP(mvm, "Cooling device unregister\n");
 860        if (mvm->cooling_dev.cdev) {
 861                thermal_cooling_device_unregister(mvm->cooling_dev.cdev);
 862                mvm->cooling_dev.cdev = NULL;
 863        }
 864}
 865#endif /* CONFIG_THERMAL */
 866
 867void iwl_mvm_thermal_initialize(struct iwl_mvm *mvm, u32 min_backoff)
 868{
 869        struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
 870
 871        IWL_DEBUG_TEMP(mvm, "Initialize Thermal Throttling\n");
 872
 873        if (mvm->cfg->thermal_params)
 874                tt->params = *mvm->cfg->thermal_params;
 875        else
 876                tt->params = iwl_mvm_default_tt_params;
 877
 878        tt->throttle = false;
 879        tt->dynamic_smps = false;
 880        tt->min_backoff = min_backoff;
 881        INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill);
 882
 883#ifdef CONFIG_THERMAL
 884        iwl_mvm_cooling_device_register(mvm);
 885        iwl_mvm_thermal_zone_register(mvm);
 886#endif
 887        mvm->init_status |= IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE;
 888}
 889
 890void iwl_mvm_thermal_exit(struct iwl_mvm *mvm)
 891{
 892        if (!(mvm->init_status & IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE))
 893                return;
 894
 895        cancel_delayed_work_sync(&mvm->thermal_throttle.ct_kill_exit);
 896        IWL_DEBUG_TEMP(mvm, "Exit Thermal Throttling\n");
 897
 898#ifdef CONFIG_THERMAL
 899        iwl_mvm_cooling_device_unregister(mvm);
 900        iwl_mvm_thermal_zone_unregister(mvm);
 901#endif
 902        mvm->init_status &= ~IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE;
 903}
 904