0001
0002
0003
0004
0005
0006
0007 #include <linux/sort.h>
0008
0009 #include "mvm.h"
0010
0011 #define IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT HZ
0012
0013 void iwl_mvm_enter_ctkill(struct iwl_mvm *mvm)
0014 {
0015 struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
0016 u32 duration = tt->params.ct_kill_duration;
0017
0018 if (test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status))
0019 return;
0020
0021 IWL_ERR(mvm, "Enter CT Kill\n");
0022 iwl_mvm_set_hw_ctkill_state(mvm, true);
0023
0024 if (!iwl_mvm_is_tt_in_fw(mvm)) {
0025 tt->throttle = false;
0026 tt->dynamic_smps = false;
0027 }
0028
0029
0030
0031
0032
0033 if (!mvm->temperature_test)
0034 schedule_delayed_work(&tt->ct_kill_exit,
0035 round_jiffies_relative(duration * HZ));
0036 }
0037
0038 static void iwl_mvm_exit_ctkill(struct iwl_mvm *mvm)
0039 {
0040 if (!test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status))
0041 return;
0042
0043 IWL_ERR(mvm, "Exit CT Kill\n");
0044 iwl_mvm_set_hw_ctkill_state(mvm, false);
0045 }
0046
0047 static void iwl_mvm_tt_temp_changed(struct iwl_mvm *mvm, u32 temp)
0048 {
0049
0050 if (mvm->temperature_test)
0051 return;
0052
0053 if (mvm->temperature == temp)
0054 return;
0055
0056 mvm->temperature = temp;
0057 iwl_mvm_tt_handler(mvm);
0058 }
0059
0060 static int iwl_mvm_temp_notif_parse(struct iwl_mvm *mvm,
0061 struct iwl_rx_packet *pkt)
0062 {
0063 struct iwl_dts_measurement_notif_v1 *notif_v1;
0064 int len = iwl_rx_packet_payload_len(pkt);
0065 int temp;
0066
0067
0068
0069
0070 if (WARN_ON_ONCE(len < sizeof(*notif_v1))) {
0071 IWL_ERR(mvm, "Invalid DTS_MEASUREMENT_NOTIFICATION\n");
0072 return -EINVAL;
0073 }
0074
0075 notif_v1 = (void *)pkt->data;
0076
0077 temp = le32_to_cpu(notif_v1->temp);
0078
0079
0080 if (WARN_ON_ONCE(temp < 0))
0081 temp = 0;
0082
0083 IWL_DEBUG_TEMP(mvm, "DTS_MEASUREMENT_NOTIFICATION - %d\n", temp);
0084
0085 return temp;
0086 }
0087
0088 static bool iwl_mvm_temp_notif_wait(struct iwl_notif_wait_data *notif_wait,
0089 struct iwl_rx_packet *pkt, void *data)
0090 {
0091 struct iwl_mvm *mvm =
0092 container_of(notif_wait, struct iwl_mvm, notif_wait);
0093 int *temp = data;
0094 int ret;
0095
0096 ret = iwl_mvm_temp_notif_parse(mvm, pkt);
0097 if (ret < 0)
0098 return true;
0099
0100 *temp = ret;
0101
0102 return true;
0103 }
0104
0105 void iwl_mvm_temp_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
0106 {
0107 struct iwl_rx_packet *pkt = rxb_addr(rxb);
0108 struct iwl_dts_measurement_notif_v2 *notif_v2;
0109 int len = iwl_rx_packet_payload_len(pkt);
0110 int temp;
0111 u32 ths_crossed;
0112
0113
0114 if (test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status))
0115 return;
0116
0117 temp = iwl_mvm_temp_notif_parse(mvm, pkt);
0118
0119 if (!iwl_mvm_is_tt_in_fw(mvm)) {
0120 if (temp >= 0)
0121 iwl_mvm_tt_temp_changed(mvm, temp);
0122 return;
0123 }
0124
0125 if (WARN_ON_ONCE(len < sizeof(*notif_v2))) {
0126 IWL_ERR(mvm, "Invalid DTS_MEASUREMENT_NOTIFICATION\n");
0127 return;
0128 }
0129
0130 notif_v2 = (void *)pkt->data;
0131 ths_crossed = le32_to_cpu(notif_v2->threshold_idx);
0132
0133
0134
0135
0136 if (ths_crossed == 0xFF)
0137 return;
0138
0139 IWL_DEBUG_TEMP(mvm, "Temp = %d Threshold crossed = %d\n",
0140 temp, ths_crossed);
0141
0142 #ifdef CONFIG_THERMAL
0143 if (WARN_ON(ths_crossed >= IWL_MAX_DTS_TRIPS))
0144 return;
0145
0146 if (mvm->tz_device.tzone) {
0147 struct iwl_mvm_thermal_device *tz_dev = &mvm->tz_device;
0148
0149 thermal_zone_device_update(tz_dev->tzone,
0150 THERMAL_TRIP_VIOLATED);
0151 }
0152 #endif
0153 }
0154
0155 void iwl_mvm_ct_kill_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
0156 {
0157 struct iwl_rx_packet *pkt = rxb_addr(rxb);
0158 struct ct_kill_notif *notif;
0159
0160 notif = (struct ct_kill_notif *)pkt->data;
0161 IWL_DEBUG_TEMP(mvm, "CT Kill notification temperature = %d\n",
0162 notif->temperature);
0163 if (iwl_fw_lookup_notif_ver(mvm->fw, PHY_OPS_GROUP,
0164 CT_KILL_NOTIFICATION, 0) > 1)
0165 IWL_DEBUG_TEMP(mvm,
0166 "CT kill notification DTS bitmap = 0x%x, Scheme = %d\n",
0167 notif->dts, notif->scheme);
0168
0169 iwl_mvm_enter_ctkill(mvm);
0170 }
0171
0172
0173
0174
0175
0176 static int iwl_mvm_send_temp_cmd(struct iwl_mvm *mvm, bool response, s32 *temp)
0177 {
0178 struct iwl_host_cmd cmd = {};
0179 struct iwl_dts_measurement_cmd dts_cmd = {
0180 .flags = cpu_to_le32(DTS_TRIGGER_CMD_FLAGS_TEMP),
0181 };
0182 struct iwl_ext_dts_measurement_cmd ext_cmd = {
0183 .control_mode = cpu_to_le32(DTS_DIRECT_WITHOUT_MEASURE),
0184 };
0185 struct iwl_dts_measurement_resp *resp;
0186 void *cmd_ptr;
0187 int ret;
0188 u32 cmd_flags = 0;
0189 u16 len;
0190
0191
0192 if (fw_has_capa(&mvm->fw->ucode_capa,
0193 IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE)) {
0194 len = sizeof(ext_cmd);
0195 cmd_ptr = &ext_cmd;
0196 } else {
0197 len = sizeof(dts_cmd);
0198 cmd_ptr = &dts_cmd;
0199 }
0200
0201 if (response) {
0202 cmd_flags = CMD_WANT_SKB;
0203 len = 0;
0204 }
0205
0206 cmd.id = WIDE_ID(PHY_OPS_GROUP, CMD_DTS_MEASUREMENT_TRIGGER_WIDE);
0207 cmd.len[0] = len;
0208 cmd.flags = cmd_flags;
0209 cmd.data[0] = cmd_ptr;
0210
0211 IWL_DEBUG_TEMP(mvm,
0212 "Sending temperature measurement command - %s response\n",
0213 response ? "with" : "without");
0214 ret = iwl_mvm_send_cmd(mvm, &cmd);
0215
0216 if (ret) {
0217 IWL_ERR(mvm,
0218 "Failed to send the temperature measurement command (err=%d)\n",
0219 ret);
0220 return ret;
0221 }
0222
0223 if (response) {
0224 resp = (void *)cmd.resp_pkt->data;
0225 *temp = le32_to_cpu(resp->temp);
0226 IWL_DEBUG_TEMP(mvm,
0227 "Got temperature measurement response: temp=%d\n",
0228 *temp);
0229 iwl_free_resp(&cmd);
0230 }
0231
0232 return ret;
0233 }
0234
0235 int iwl_mvm_get_temp(struct iwl_mvm *mvm, s32 *temp)
0236 {
0237 struct iwl_notification_wait wait_temp_notif;
0238 static u16 temp_notif[] = { WIDE_ID(PHY_OPS_GROUP,
0239 DTS_MEASUREMENT_NOTIF_WIDE) };
0240 int ret;
0241 u8 cmd_ver;
0242
0243
0244
0245
0246
0247
0248 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw,
0249 WIDE_ID(PHY_OPS_GROUP, CMD_DTS_MEASUREMENT_TRIGGER_WIDE),
0250 IWL_FW_CMD_VER_UNKNOWN);
0251 if (cmd_ver == 1)
0252 return iwl_mvm_send_temp_cmd(mvm, true, temp);
0253
0254 lockdep_assert_held(&mvm->mutex);
0255
0256 iwl_init_notification_wait(&mvm->notif_wait, &wait_temp_notif,
0257 temp_notif, ARRAY_SIZE(temp_notif),
0258 iwl_mvm_temp_notif_wait, temp);
0259
0260 ret = iwl_mvm_send_temp_cmd(mvm, false, temp);
0261 if (ret) {
0262 iwl_remove_notification(&mvm->notif_wait, &wait_temp_notif);
0263 return ret;
0264 }
0265
0266 ret = iwl_wait_notification(&mvm->notif_wait, &wait_temp_notif,
0267 IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT);
0268 if (ret)
0269 IWL_WARN(mvm, "Getting the temperature timed out\n");
0270
0271 return ret;
0272 }
0273
0274 static void check_exit_ctkill(struct work_struct *work)
0275 {
0276 struct iwl_mvm_tt_mgmt *tt;
0277 struct iwl_mvm *mvm;
0278 u32 duration;
0279 s32 temp;
0280 int ret;
0281
0282 tt = container_of(work, struct iwl_mvm_tt_mgmt, ct_kill_exit.work);
0283 mvm = container_of(tt, struct iwl_mvm, thermal_throttle);
0284
0285 if (iwl_mvm_is_tt_in_fw(mvm)) {
0286 iwl_mvm_exit_ctkill(mvm);
0287
0288 return;
0289 }
0290
0291 duration = tt->params.ct_kill_duration;
0292
0293 flush_work(&mvm->roc_done_wk);
0294
0295 mutex_lock(&mvm->mutex);
0296
0297 if (__iwl_mvm_mac_start(mvm))
0298 goto reschedule;
0299
0300 ret = iwl_mvm_get_temp(mvm, &temp);
0301
0302 __iwl_mvm_mac_stop(mvm);
0303
0304 if (ret)
0305 goto reschedule;
0306
0307 IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", temp);
0308
0309 if (temp <= tt->params.ct_kill_exit) {
0310 mutex_unlock(&mvm->mutex);
0311 iwl_mvm_exit_ctkill(mvm);
0312 return;
0313 }
0314
0315 reschedule:
0316 mutex_unlock(&mvm->mutex);
0317 schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit,
0318 round_jiffies(duration * HZ));
0319 }
0320
0321 static void iwl_mvm_tt_smps_iterator(void *_data, u8 *mac,
0322 struct ieee80211_vif *vif)
0323 {
0324 struct iwl_mvm *mvm = _data;
0325 enum ieee80211_smps_mode smps_mode;
0326
0327 lockdep_assert_held(&mvm->mutex);
0328
0329 if (mvm->thermal_throttle.dynamic_smps)
0330 smps_mode = IEEE80211_SMPS_DYNAMIC;
0331 else
0332 smps_mode = IEEE80211_SMPS_AUTOMATIC;
0333
0334 if (vif->type != NL80211_IFTYPE_STATION)
0335 return;
0336
0337 iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT, smps_mode);
0338 }
0339
0340 static void iwl_mvm_tt_tx_protection(struct iwl_mvm *mvm, bool enable)
0341 {
0342 struct iwl_mvm_sta *mvmsta;
0343 int i, err;
0344
0345 for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) {
0346 mvmsta = iwl_mvm_sta_from_staid_protected(mvm, i);
0347 if (!mvmsta)
0348 continue;
0349
0350 if (enable == mvmsta->tt_tx_protection)
0351 continue;
0352 err = iwl_mvm_tx_protection(mvm, mvmsta, enable);
0353 if (err) {
0354 IWL_ERR(mvm, "Failed to %s Tx protection\n",
0355 enable ? "enable" : "disable");
0356 } else {
0357 IWL_DEBUG_TEMP(mvm, "%s Tx protection\n",
0358 enable ? "Enable" : "Disable");
0359 mvmsta->tt_tx_protection = enable;
0360 }
0361 }
0362 }
0363
0364 void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff)
0365 {
0366 struct iwl_host_cmd cmd = {
0367 .id = REPLY_THERMAL_MNG_BACKOFF,
0368 .len = { sizeof(u32), },
0369 .data = { &backoff, },
0370 };
0371
0372 backoff = max(backoff, mvm->thermal_throttle.min_backoff);
0373
0374 if (iwl_mvm_send_cmd(mvm, &cmd) == 0) {
0375 IWL_DEBUG_TEMP(mvm, "Set Thermal Tx backoff to: %u\n",
0376 backoff);
0377 mvm->thermal_throttle.tx_backoff = backoff;
0378 } else {
0379 IWL_ERR(mvm, "Failed to change Thermal Tx backoff\n");
0380 }
0381 }
0382
0383 void iwl_mvm_tt_handler(struct iwl_mvm *mvm)
0384 {
0385 struct iwl_tt_params *params = &mvm->thermal_throttle.params;
0386 struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
0387 s32 temperature = mvm->temperature;
0388 bool throttle_enable = false;
0389 int i;
0390 u32 tx_backoff;
0391
0392 IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", mvm->temperature);
0393
0394 if (params->support_ct_kill && temperature >= params->ct_kill_entry) {
0395 iwl_mvm_enter_ctkill(mvm);
0396 return;
0397 }
0398
0399 if (params->support_ct_kill &&
0400 temperature <= params->ct_kill_exit) {
0401 iwl_mvm_exit_ctkill(mvm);
0402 return;
0403 }
0404
0405 if (params->support_dynamic_smps) {
0406 if (!tt->dynamic_smps &&
0407 temperature >= params->dynamic_smps_entry) {
0408 IWL_DEBUG_TEMP(mvm, "Enable dynamic SMPS\n");
0409 tt->dynamic_smps = true;
0410 ieee80211_iterate_active_interfaces_atomic(
0411 mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
0412 iwl_mvm_tt_smps_iterator, mvm);
0413 throttle_enable = true;
0414 } else if (tt->dynamic_smps &&
0415 temperature <= params->dynamic_smps_exit) {
0416 IWL_DEBUG_TEMP(mvm, "Disable dynamic SMPS\n");
0417 tt->dynamic_smps = false;
0418 ieee80211_iterate_active_interfaces_atomic(
0419 mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
0420 iwl_mvm_tt_smps_iterator, mvm);
0421 }
0422 }
0423
0424 if (params->support_tx_protection) {
0425 if (temperature >= params->tx_protection_entry) {
0426 iwl_mvm_tt_tx_protection(mvm, true);
0427 throttle_enable = true;
0428 } else if (temperature <= params->tx_protection_exit) {
0429 iwl_mvm_tt_tx_protection(mvm, false);
0430 }
0431 }
0432
0433 if (params->support_tx_backoff) {
0434 tx_backoff = tt->min_backoff;
0435 for (i = 0; i < TT_TX_BACKOFF_SIZE; i++) {
0436 if (temperature < params->tx_backoff[i].temperature)
0437 break;
0438 tx_backoff = max(tt->min_backoff,
0439 params->tx_backoff[i].backoff);
0440 }
0441 if (tx_backoff != tt->min_backoff)
0442 throttle_enable = true;
0443 if (tt->tx_backoff != tx_backoff)
0444 iwl_mvm_tt_tx_backoff(mvm, tx_backoff);
0445 }
0446
0447 if (!tt->throttle && throttle_enable) {
0448 IWL_WARN(mvm,
0449 "Due to high temperature thermal throttling initiated\n");
0450 tt->throttle = true;
0451 } else if (tt->throttle && !tt->dynamic_smps &&
0452 tt->tx_backoff == tt->min_backoff &&
0453 temperature <= params->tx_protection_exit) {
0454 IWL_WARN(mvm,
0455 "Temperature is back to normal thermal throttling stopped\n");
0456 tt->throttle = false;
0457 }
0458 }
0459
0460 static const struct iwl_tt_params iwl_mvm_default_tt_params = {
0461 .ct_kill_entry = 118,
0462 .ct_kill_exit = 96,
0463 .ct_kill_duration = 5,
0464 .dynamic_smps_entry = 114,
0465 .dynamic_smps_exit = 110,
0466 .tx_protection_entry = 114,
0467 .tx_protection_exit = 108,
0468 .tx_backoff = {
0469 {.temperature = 112, .backoff = 200},
0470 {.temperature = 113, .backoff = 600},
0471 {.temperature = 114, .backoff = 1200},
0472 {.temperature = 115, .backoff = 2000},
0473 {.temperature = 116, .backoff = 4000},
0474 {.temperature = 117, .backoff = 10000},
0475 },
0476 .support_ct_kill = true,
0477 .support_dynamic_smps = true,
0478 .support_tx_protection = true,
0479 .support_tx_backoff = true,
0480 };
0481
0482
0483 static const u32 iwl_mvm_cdev_budgets[] = {
0484 2400,
0485 2000,
0486 1800,
0487 1600,
0488 1400,
0489 1200,
0490 1000,
0491 900,
0492 800,
0493 700,
0494 650,
0495 600,
0496 550,
0497 500,
0498 450,
0499 400,
0500 350,
0501 300,
0502 250,
0503 200,
0504 150,
0505 };
0506
0507 int iwl_mvm_ctdp_command(struct iwl_mvm *mvm, u32 op, u32 state)
0508 {
0509 struct iwl_mvm_ctdp_cmd cmd = {
0510 .operation = cpu_to_le32(op),
0511 .budget = cpu_to_le32(iwl_mvm_cdev_budgets[state]),
0512 .window_size = 0,
0513 };
0514 int ret;
0515 u32 status;
0516
0517 lockdep_assert_held(&mvm->mutex);
0518
0519 status = 0;
0520 ret = iwl_mvm_send_cmd_pdu_status(mvm, WIDE_ID(PHY_OPS_GROUP,
0521 CTDP_CONFIG_CMD),
0522 sizeof(cmd), &cmd, &status);
0523
0524 if (ret) {
0525 IWL_ERR(mvm, "cTDP command failed (err=%d)\n", ret);
0526 return ret;
0527 }
0528
0529 switch (op) {
0530 case CTDP_CMD_OPERATION_START:
0531 #ifdef CONFIG_THERMAL
0532 mvm->cooling_dev.cur_state = state;
0533 #endif
0534 break;
0535 case CTDP_CMD_OPERATION_REPORT:
0536 IWL_DEBUG_TEMP(mvm, "cTDP avg energy in mWatt = %d\n", status);
0537
0538
0539
0540
0541
0542
0543 return status;
0544 case CTDP_CMD_OPERATION_STOP:
0545 IWL_DEBUG_TEMP(mvm, "cTDP stopped successfully\n");
0546 break;
0547 }
0548
0549 return 0;
0550 }
0551
0552 #ifdef CONFIG_THERMAL
0553 static int compare_temps(const void *a, const void *b)
0554 {
0555 return ((s16)le16_to_cpu(*(__le16 *)a) -
0556 (s16)le16_to_cpu(*(__le16 *)b));
0557 }
0558 #endif
0559
0560 int iwl_mvm_send_temp_report_ths_cmd(struct iwl_mvm *mvm)
0561 {
0562 struct temp_report_ths_cmd cmd = {0};
0563 int ret;
0564 #ifdef CONFIG_THERMAL
0565 int i, j, idx = 0;
0566
0567 lockdep_assert_held(&mvm->mutex);
0568
0569 if (!mvm->tz_device.tzone)
0570 goto send;
0571
0572
0573
0574
0575
0576
0577 for (i = 0; i < IWL_MAX_DTS_TRIPS; i++) {
0578 if (mvm->tz_device.temp_trips[i] != S16_MIN) {
0579 cmd.thresholds[idx++] =
0580 cpu_to_le16(mvm->tz_device.temp_trips[i]);
0581 }
0582 }
0583 cmd.num_temps = cpu_to_le32(idx);
0584
0585 if (!idx)
0586 goto send;
0587
0588
0589 sort(cmd.thresholds, idx, sizeof(s16), compare_temps, NULL);
0590
0591
0592
0593
0594 for (i = 0; i < idx; i++) {
0595 for (j = 0; j < IWL_MAX_DTS_TRIPS; j++) {
0596 if (le16_to_cpu(cmd.thresholds[i]) ==
0597 mvm->tz_device.temp_trips[j])
0598 mvm->tz_device.fw_trips_index[i] = j;
0599 }
0600 }
0601
0602 send:
0603 #endif
0604 ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(PHY_OPS_GROUP,
0605 TEMP_REPORTING_THRESHOLDS_CMD),
0606 0, sizeof(cmd), &cmd);
0607 if (ret)
0608 IWL_ERR(mvm, "TEMP_REPORT_THS_CMD command failed (err=%d)\n",
0609 ret);
0610
0611 return ret;
0612 }
0613
0614 #ifdef CONFIG_THERMAL
0615 static int iwl_mvm_tzone_get_temp(struct thermal_zone_device *device,
0616 int *temperature)
0617 {
0618 struct iwl_mvm *mvm = (struct iwl_mvm *)device->devdata;
0619 int ret;
0620 int temp;
0621
0622 mutex_lock(&mvm->mutex);
0623
0624 if (!iwl_mvm_firmware_running(mvm) ||
0625 mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) {
0626 ret = -ENODATA;
0627 goto out;
0628 }
0629
0630 ret = iwl_mvm_get_temp(mvm, &temp);
0631 if (ret)
0632 goto out;
0633
0634 *temperature = temp * 1000;
0635
0636 out:
0637 mutex_unlock(&mvm->mutex);
0638 return ret;
0639 }
0640
0641 static int iwl_mvm_tzone_get_trip_temp(struct thermal_zone_device *device,
0642 int trip, int *temp)
0643 {
0644 struct iwl_mvm *mvm = (struct iwl_mvm *)device->devdata;
0645
0646 if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS)
0647 return -EINVAL;
0648
0649 *temp = mvm->tz_device.temp_trips[trip] * 1000;
0650
0651 return 0;
0652 }
0653
0654 static int iwl_mvm_tzone_get_trip_type(struct thermal_zone_device *device,
0655 int trip, enum thermal_trip_type *type)
0656 {
0657 if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS)
0658 return -EINVAL;
0659
0660 *type = THERMAL_TRIP_PASSIVE;
0661
0662 return 0;
0663 }
0664
0665 static int iwl_mvm_tzone_set_trip_temp(struct thermal_zone_device *device,
0666 int trip, int temp)
0667 {
0668 struct iwl_mvm *mvm = (struct iwl_mvm *)device->devdata;
0669 struct iwl_mvm_thermal_device *tzone;
0670 int i, ret;
0671 s16 temperature;
0672
0673 mutex_lock(&mvm->mutex);
0674
0675 if (!iwl_mvm_firmware_running(mvm) ||
0676 mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) {
0677 ret = -EIO;
0678 goto out;
0679 }
0680
0681 if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS) {
0682 ret = -EINVAL;
0683 goto out;
0684 }
0685
0686 if ((temp / 1000) > S16_MAX) {
0687 ret = -EINVAL;
0688 goto out;
0689 }
0690
0691 temperature = (s16)(temp / 1000);
0692 tzone = &mvm->tz_device;
0693
0694 if (!tzone) {
0695 ret = -EIO;
0696 goto out;
0697 }
0698
0699
0700 if (tzone->temp_trips[trip] == temperature) {
0701 ret = 0;
0702 goto out;
0703 }
0704
0705
0706 for (i = 0; i < IWL_MAX_DTS_TRIPS; i++) {
0707 if (tzone->temp_trips[i] == temperature) {
0708 ret = -EINVAL;
0709 goto out;
0710 }
0711 }
0712
0713 tzone->temp_trips[trip] = temperature;
0714
0715 ret = iwl_mvm_send_temp_report_ths_cmd(mvm);
0716 out:
0717 mutex_unlock(&mvm->mutex);
0718 return ret;
0719 }
0720
0721 static struct thermal_zone_device_ops tzone_ops = {
0722 .get_temp = iwl_mvm_tzone_get_temp,
0723 .get_trip_temp = iwl_mvm_tzone_get_trip_temp,
0724 .get_trip_type = iwl_mvm_tzone_get_trip_type,
0725 .set_trip_temp = iwl_mvm_tzone_set_trip_temp,
0726 };
0727
0728
0729 #define IWL_WRITABLE_TRIPS_MSK (BIT(IWL_MAX_DTS_TRIPS) - 1)
0730
0731 static void iwl_mvm_thermal_zone_register(struct iwl_mvm *mvm)
0732 {
0733 int i, ret;
0734 char name[16];
0735 static atomic_t counter = ATOMIC_INIT(0);
0736
0737 if (!iwl_mvm_is_tt_in_fw(mvm)) {
0738 mvm->tz_device.tzone = NULL;
0739
0740 return;
0741 }
0742
0743 BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);
0744
0745 sprintf(name, "iwlwifi_%u", atomic_inc_return(&counter) & 0xFF);
0746 mvm->tz_device.tzone = thermal_zone_device_register(name,
0747 IWL_MAX_DTS_TRIPS,
0748 IWL_WRITABLE_TRIPS_MSK,
0749 mvm, &tzone_ops,
0750 NULL, 0, 0);
0751 if (IS_ERR(mvm->tz_device.tzone)) {
0752 IWL_DEBUG_TEMP(mvm,
0753 "Failed to register to thermal zone (err = %ld)\n",
0754 PTR_ERR(mvm->tz_device.tzone));
0755 mvm->tz_device.tzone = NULL;
0756 return;
0757 }
0758
0759 ret = thermal_zone_device_enable(mvm->tz_device.tzone);
0760 if (ret) {
0761 IWL_DEBUG_TEMP(mvm, "Failed to enable thermal zone\n");
0762 thermal_zone_device_unregister(mvm->tz_device.tzone);
0763 return;
0764 }
0765
0766
0767
0768
0769 for (i = 0 ; i < IWL_MAX_DTS_TRIPS; i++)
0770 mvm->tz_device.temp_trips[i] = S16_MIN;
0771 }
0772
0773 static int iwl_mvm_tcool_get_max_state(struct thermal_cooling_device *cdev,
0774 unsigned long *state)
0775 {
0776 *state = ARRAY_SIZE(iwl_mvm_cdev_budgets) - 1;
0777
0778 return 0;
0779 }
0780
0781 static int iwl_mvm_tcool_get_cur_state(struct thermal_cooling_device *cdev,
0782 unsigned long *state)
0783 {
0784 struct iwl_mvm *mvm = (struct iwl_mvm *)(cdev->devdata);
0785
0786 *state = mvm->cooling_dev.cur_state;
0787
0788 return 0;
0789 }
0790
0791 static int iwl_mvm_tcool_set_cur_state(struct thermal_cooling_device *cdev,
0792 unsigned long new_state)
0793 {
0794 struct iwl_mvm *mvm = (struct iwl_mvm *)(cdev->devdata);
0795 int ret;
0796
0797 mutex_lock(&mvm->mutex);
0798
0799 if (!iwl_mvm_firmware_running(mvm) ||
0800 mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) {
0801 ret = -EIO;
0802 goto unlock;
0803 }
0804
0805 if (new_state >= ARRAY_SIZE(iwl_mvm_cdev_budgets)) {
0806 ret = -EINVAL;
0807 goto unlock;
0808 }
0809
0810 ret = iwl_mvm_ctdp_command(mvm, CTDP_CMD_OPERATION_START,
0811 new_state);
0812
0813 unlock:
0814 mutex_unlock(&mvm->mutex);
0815 return ret;
0816 }
0817
0818 static const struct thermal_cooling_device_ops tcooling_ops = {
0819 .get_max_state = iwl_mvm_tcool_get_max_state,
0820 .get_cur_state = iwl_mvm_tcool_get_cur_state,
0821 .set_cur_state = iwl_mvm_tcool_set_cur_state,
0822 };
0823
0824 static void iwl_mvm_cooling_device_register(struct iwl_mvm *mvm)
0825 {
0826 char name[] = "iwlwifi";
0827
0828 if (!iwl_mvm_is_ctdp_supported(mvm))
0829 return;
0830
0831 BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);
0832
0833 mvm->cooling_dev.cdev =
0834 thermal_cooling_device_register(name,
0835 mvm,
0836 &tcooling_ops);
0837
0838 if (IS_ERR(mvm->cooling_dev.cdev)) {
0839 IWL_DEBUG_TEMP(mvm,
0840 "Failed to register to cooling device (err = %ld)\n",
0841 PTR_ERR(mvm->cooling_dev.cdev));
0842 mvm->cooling_dev.cdev = NULL;
0843 return;
0844 }
0845 }
0846
0847 static void iwl_mvm_thermal_zone_unregister(struct iwl_mvm *mvm)
0848 {
0849 if (!iwl_mvm_is_tt_in_fw(mvm) || !mvm->tz_device.tzone)
0850 return;
0851
0852 IWL_DEBUG_TEMP(mvm, "Thermal zone device unregister\n");
0853 if (mvm->tz_device.tzone) {
0854 thermal_zone_device_unregister(mvm->tz_device.tzone);
0855 mvm->tz_device.tzone = NULL;
0856 }
0857 }
0858
0859 static void iwl_mvm_cooling_device_unregister(struct iwl_mvm *mvm)
0860 {
0861 if (!iwl_mvm_is_ctdp_supported(mvm) || !mvm->cooling_dev.cdev)
0862 return;
0863
0864 IWL_DEBUG_TEMP(mvm, "Cooling device unregister\n");
0865 if (mvm->cooling_dev.cdev) {
0866 thermal_cooling_device_unregister(mvm->cooling_dev.cdev);
0867 mvm->cooling_dev.cdev = NULL;
0868 }
0869 }
0870 #endif
0871
0872 void iwl_mvm_thermal_initialize(struct iwl_mvm *mvm, u32 min_backoff)
0873 {
0874 struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
0875
0876 IWL_DEBUG_TEMP(mvm, "Initialize Thermal Throttling\n");
0877
0878 if (mvm->cfg->thermal_params)
0879 tt->params = *mvm->cfg->thermal_params;
0880 else
0881 tt->params = iwl_mvm_default_tt_params;
0882
0883 tt->throttle = false;
0884 tt->dynamic_smps = false;
0885 tt->min_backoff = min_backoff;
0886 INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill);
0887
0888 #ifdef CONFIG_THERMAL
0889 iwl_mvm_cooling_device_register(mvm);
0890 iwl_mvm_thermal_zone_register(mvm);
0891 #endif
0892 mvm->init_status |= IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE;
0893 }
0894
0895 void iwl_mvm_thermal_exit(struct iwl_mvm *mvm)
0896 {
0897 if (!(mvm->init_status & IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE))
0898 return;
0899
0900 cancel_delayed_work_sync(&mvm->thermal_throttle.ct_kill_exit);
0901 IWL_DEBUG_TEMP(mvm, "Exit Thermal Throttling\n");
0902
0903 #ifdef CONFIG_THERMAL
0904 iwl_mvm_cooling_device_unregister(mvm);
0905 iwl_mvm_thermal_zone_unregister(mvm);
0906 #endif
0907 mvm->init_status &= ~IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE;
0908 }