1 /****************************************************************************** 2 * 3 * This file is provided under a dual BSD/GPLv2 license. When using or 4 * redistributing this file, you may do so under either license. 5 * 6 * GPL LICENSE SUMMARY 7 * 8 * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. 9 * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH 10 * 11 * This program is free software; you can redistribute it and/or modify 12 * it under the terms of version 2 of the GNU General Public License as 13 * published by the Free Software Foundation. 14 * 15 * This program is distributed in the hope that it will be useful, but 16 * WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program; if not, write to the Free Software 22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, 23 * USA 24 * 25 * The full GNU General Public License is included in this distribution 26 * in the file called COPYING. 27 * 28 * Contact Information: 29 * Intel Linux Wireless <linuxwifi@intel.com> 30 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 31 * 32 * BSD LICENSE 33 * 34 * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. 35 * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH 36 * Copyright(c) 2015 Intel Deutschland GmbH 37 * All rights reserved. 38 * 39 * Redistribution and use in source and binary forms, with or without 40 * modification, are permitted provided that the following conditions 41 * are met: 42 * 43 * * Redistributions of source code must retain the above copyright 44 * notice, this list of conditions and the following disclaimer. 45 * * Redistributions in binary form must reproduce the above copyright 46 * notice, this list of conditions and the following disclaimer in 47 * the documentation and/or other materials provided with the 48 * distribution. 49 * * Neither the name Intel Corporation nor the names of its 50 * contributors may be used to endorse or promote products derived 51 * from this software without specific prior written permission. 52 * 53 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 54 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 55 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 56 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 57 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 58 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 59 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 60 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 61 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 62 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 63 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 64 * 65 *****************************************************************************/ 66 67 #include "mvm.h" 68 69 #define IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT HZ 70 71 static void iwl_mvm_enter_ctkill(struct iwl_mvm *mvm) 72 { 73 struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle; 74 u32 duration = tt->params.ct_kill_duration; 75 76 if (test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status)) 77 return; 78 79 IWL_ERR(mvm, "Enter CT Kill\n"); 80 iwl_mvm_set_hw_ctkill_state(mvm, true); 81 82 tt->throttle = false; 83 tt->dynamic_smps = false; 84 85 /* Don't schedule an exit work if we're in test mode, since 86 * the temperature will not change unless we manually set it 87 * again (or disable testing). 88 */ 89 if (!mvm->temperature_test) 90 schedule_delayed_work(&tt->ct_kill_exit, 91 round_jiffies_relative(duration * HZ)); 92 } 93 94 static void iwl_mvm_exit_ctkill(struct iwl_mvm *mvm) 95 { 96 if (!test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status)) 97 return; 98 99 IWL_ERR(mvm, "Exit CT Kill\n"); 100 iwl_mvm_set_hw_ctkill_state(mvm, false); 101 } 102 103 void iwl_mvm_tt_temp_changed(struct iwl_mvm *mvm, u32 temp) 104 { 105 /* ignore the notification if we are in test mode */ 106 if (mvm->temperature_test) 107 return; 108 109 if (mvm->temperature == temp) 110 return; 111 112 mvm->temperature = temp; 113 iwl_mvm_tt_handler(mvm); 114 } 115 116 static int iwl_mvm_temp_notif_parse(struct iwl_mvm *mvm, 117 struct iwl_rx_packet *pkt) 118 { 119 struct iwl_dts_measurement_notif *notif; 120 int len = iwl_rx_packet_payload_len(pkt); 121 int temp; 122 123 if (WARN_ON_ONCE(len < sizeof(*notif))) { 124 IWL_ERR(mvm, "Invalid DTS_MEASUREMENT_NOTIFICATION\n"); 125 return -EINVAL; 126 } 127 128 notif = (void *)pkt->data; 129 130 temp = le32_to_cpu(notif->temp); 131 132 /* shouldn't be negative, but since it's s32, make sure it isn't */ 133 if (WARN_ON_ONCE(temp < 0)) 134 temp = 0; 135 136 IWL_DEBUG_TEMP(mvm, "DTS_MEASUREMENT_NOTIFICATION - %d\n", temp); 137 138 return temp; 139 } 140 141 static bool iwl_mvm_temp_notif_wait(struct iwl_notif_wait_data *notif_wait, 142 struct iwl_rx_packet *pkt, void *data) 143 { 144 struct iwl_mvm *mvm = 145 container_of(notif_wait, struct iwl_mvm, notif_wait); 146 int *temp = data; 147 int ret; 148 149 ret = iwl_mvm_temp_notif_parse(mvm, pkt); 150 if (ret < 0) 151 return true; 152 153 *temp = ret; 154 155 return true; 156 } 157 158 void iwl_mvm_temp_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) 159 { 160 struct iwl_rx_packet *pkt = rxb_addr(rxb); 161 int temp; 162 163 /* the notification is handled synchronously in ctkill, so skip here */ 164 if (test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status)) 165 return; 166 167 temp = iwl_mvm_temp_notif_parse(mvm, pkt); 168 if (temp < 0) 169 return; 170 171 iwl_mvm_tt_temp_changed(mvm, temp); 172 } 173 174 static int iwl_mvm_get_temp_cmd(struct iwl_mvm *mvm) 175 { 176 struct iwl_dts_measurement_cmd cmd = { 177 .flags = cpu_to_le32(DTS_TRIGGER_CMD_FLAGS_TEMP), 178 }; 179 struct iwl_ext_dts_measurement_cmd extcmd = { 180 .control_mode = cpu_to_le32(DTS_AUTOMATIC), 181 }; 182 u32 cmdid; 183 184 if (fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_WIDE_CMD_HDR)) 185 cmdid = iwl_cmd_id(CMD_DTS_MEASUREMENT_TRIGGER_WIDE, 186 PHY_OPS_GROUP, 0); 187 else 188 cmdid = CMD_DTS_MEASUREMENT_TRIGGER; 189 190 if (!fw_has_capa(&mvm->fw->ucode_capa, 191 IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE)) 192 return iwl_mvm_send_cmd_pdu(mvm, cmdid, 0, sizeof(cmd), &cmd); 193 194 return iwl_mvm_send_cmd_pdu(mvm, cmdid, 0, sizeof(extcmd), &extcmd); 195 } 196 197 int iwl_mvm_get_temp(struct iwl_mvm *mvm) 198 { 199 struct iwl_notification_wait wait_temp_notif; 200 static u16 temp_notif[] = { WIDE_ID(PHY_OPS_GROUP, 201 DTS_MEASUREMENT_NOTIF_WIDE) }; 202 int ret, temp; 203 204 if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_WIDE_CMD_HDR)) 205 temp_notif[0] = DTS_MEASUREMENT_NOTIFICATION; 206 207 lockdep_assert_held(&mvm->mutex); 208 209 iwl_init_notification_wait(&mvm->notif_wait, &wait_temp_notif, 210 temp_notif, ARRAY_SIZE(temp_notif), 211 iwl_mvm_temp_notif_wait, &temp); 212 213 ret = iwl_mvm_get_temp_cmd(mvm); 214 if (ret) { 215 IWL_ERR(mvm, "Failed to get the temperature (err=%d)\n", ret); 216 iwl_remove_notification(&mvm->notif_wait, &wait_temp_notif); 217 return ret; 218 } 219 220 ret = iwl_wait_notification(&mvm->notif_wait, &wait_temp_notif, 221 IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT); 222 if (ret) { 223 IWL_ERR(mvm, "Getting the temperature timed out\n"); 224 return ret; 225 } 226 227 return temp; 228 } 229 230 static void check_exit_ctkill(struct work_struct *work) 231 { 232 struct iwl_mvm_tt_mgmt *tt; 233 struct iwl_mvm *mvm; 234 u32 duration; 235 s32 temp; 236 237 tt = container_of(work, struct iwl_mvm_tt_mgmt, ct_kill_exit.work); 238 mvm = container_of(tt, struct iwl_mvm, thermal_throttle); 239 240 duration = tt->params.ct_kill_duration; 241 242 mutex_lock(&mvm->mutex); 243 244 if (__iwl_mvm_mac_start(mvm)) 245 goto reschedule; 246 247 /* make sure the device is available for direct read/writes */ 248 if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_CHECK_CTKILL)) { 249 __iwl_mvm_mac_stop(mvm); 250 goto reschedule; 251 } 252 253 temp = iwl_mvm_get_temp(mvm); 254 255 iwl_mvm_unref(mvm, IWL_MVM_REF_CHECK_CTKILL); 256 257 __iwl_mvm_mac_stop(mvm); 258 259 if (temp < 0) 260 goto reschedule; 261 262 IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", temp); 263 264 if (temp <= tt->params.ct_kill_exit) { 265 mutex_unlock(&mvm->mutex); 266 iwl_mvm_exit_ctkill(mvm); 267 return; 268 } 269 270 reschedule: 271 mutex_unlock(&mvm->mutex); 272 schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit, 273 round_jiffies(duration * HZ)); 274 } 275 276 static void iwl_mvm_tt_smps_iterator(void *_data, u8 *mac, 277 struct ieee80211_vif *vif) 278 { 279 struct iwl_mvm *mvm = _data; 280 enum ieee80211_smps_mode smps_mode; 281 282 lockdep_assert_held(&mvm->mutex); 283 284 if (mvm->thermal_throttle.dynamic_smps) 285 smps_mode = IEEE80211_SMPS_DYNAMIC; 286 else 287 smps_mode = IEEE80211_SMPS_AUTOMATIC; 288 289 if (vif->type != NL80211_IFTYPE_STATION) 290 return; 291 292 iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT, smps_mode); 293 } 294 295 static void iwl_mvm_tt_tx_protection(struct iwl_mvm *mvm, bool enable) 296 { 297 struct ieee80211_sta *sta; 298 struct iwl_mvm_sta *mvmsta; 299 int i, err; 300 301 for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { 302 sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], 303 lockdep_is_held(&mvm->mutex)); 304 if (IS_ERR_OR_NULL(sta)) 305 continue; 306 mvmsta = iwl_mvm_sta_from_mac80211(sta); 307 if (enable == mvmsta->tt_tx_protection) 308 continue; 309 err = iwl_mvm_tx_protection(mvm, mvmsta, enable); 310 if (err) { 311 IWL_ERR(mvm, "Failed to %s Tx protection\n", 312 enable ? "enable" : "disable"); 313 } else { 314 IWL_DEBUG_TEMP(mvm, "%s Tx protection\n", 315 enable ? "Enable" : "Disable"); 316 mvmsta->tt_tx_protection = enable; 317 } 318 } 319 } 320 321 void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff) 322 { 323 struct iwl_host_cmd cmd = { 324 .id = REPLY_THERMAL_MNG_BACKOFF, 325 .len = { sizeof(u32), }, 326 .data = { &backoff, }, 327 }; 328 329 backoff = max(backoff, mvm->thermal_throttle.min_backoff); 330 331 if (iwl_mvm_send_cmd(mvm, &cmd) == 0) { 332 IWL_DEBUG_TEMP(mvm, "Set Thermal Tx backoff to: %u\n", 333 backoff); 334 mvm->thermal_throttle.tx_backoff = backoff; 335 } else { 336 IWL_ERR(mvm, "Failed to change Thermal Tx backoff\n"); 337 } 338 } 339 340 void iwl_mvm_tt_handler(struct iwl_mvm *mvm) 341 { 342 struct iwl_tt_params *params = &mvm->thermal_throttle.params; 343 struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle; 344 s32 temperature = mvm->temperature; 345 bool throttle_enable = false; 346 int i; 347 u32 tx_backoff; 348 349 IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", mvm->temperature); 350 351 if (params->support_ct_kill && temperature >= params->ct_kill_entry) { 352 iwl_mvm_enter_ctkill(mvm); 353 return; 354 } 355 356 if (params->support_ct_kill && 357 temperature <= params->ct_kill_exit) { 358 iwl_mvm_exit_ctkill(mvm); 359 return; 360 } 361 362 if (params->support_dynamic_smps) { 363 if (!tt->dynamic_smps && 364 temperature >= params->dynamic_smps_entry) { 365 IWL_DEBUG_TEMP(mvm, "Enable dynamic SMPS\n"); 366 tt->dynamic_smps = true; 367 ieee80211_iterate_active_interfaces_atomic( 368 mvm->hw, IEEE80211_IFACE_ITER_NORMAL, 369 iwl_mvm_tt_smps_iterator, mvm); 370 throttle_enable = true; 371 } else if (tt->dynamic_smps && 372 temperature <= params->dynamic_smps_exit) { 373 IWL_DEBUG_TEMP(mvm, "Disable dynamic SMPS\n"); 374 tt->dynamic_smps = false; 375 ieee80211_iterate_active_interfaces_atomic( 376 mvm->hw, IEEE80211_IFACE_ITER_NORMAL, 377 iwl_mvm_tt_smps_iterator, mvm); 378 } 379 } 380 381 if (params->support_tx_protection) { 382 if (temperature >= params->tx_protection_entry) { 383 iwl_mvm_tt_tx_protection(mvm, true); 384 throttle_enable = true; 385 } else if (temperature <= params->tx_protection_exit) { 386 iwl_mvm_tt_tx_protection(mvm, false); 387 } 388 } 389 390 if (params->support_tx_backoff) { 391 tx_backoff = tt->min_backoff; 392 for (i = 0; i < TT_TX_BACKOFF_SIZE; i++) { 393 if (temperature < params->tx_backoff[i].temperature) 394 break; 395 tx_backoff = max(tt->min_backoff, 396 params->tx_backoff[i].backoff); 397 } 398 if (tx_backoff != tt->min_backoff) 399 throttle_enable = true; 400 if (tt->tx_backoff != tx_backoff) 401 iwl_mvm_tt_tx_backoff(mvm, tx_backoff); 402 } 403 404 if (!tt->throttle && throttle_enable) { 405 IWL_WARN(mvm, 406 "Due to high temperature thermal throttling initiated\n"); 407 tt->throttle = true; 408 } else if (tt->throttle && !tt->dynamic_smps && 409 tt->tx_backoff == tt->min_backoff && 410 temperature <= params->tx_protection_exit) { 411 IWL_WARN(mvm, 412 "Temperature is back to normal thermal throttling stopped\n"); 413 tt->throttle = false; 414 } 415 } 416 417 static const struct iwl_tt_params iwl_mvm_default_tt_params = { 418 .ct_kill_entry = 118, 419 .ct_kill_exit = 96, 420 .ct_kill_duration = 5, 421 .dynamic_smps_entry = 114, 422 .dynamic_smps_exit = 110, 423 .tx_protection_entry = 114, 424 .tx_protection_exit = 108, 425 .tx_backoff = { 426 {.temperature = 112, .backoff = 200}, 427 {.temperature = 113, .backoff = 600}, 428 {.temperature = 114, .backoff = 1200}, 429 {.temperature = 115, .backoff = 2000}, 430 {.temperature = 116, .backoff = 4000}, 431 {.temperature = 117, .backoff = 10000}, 432 }, 433 .support_ct_kill = true, 434 .support_dynamic_smps = true, 435 .support_tx_protection = true, 436 .support_tx_backoff = true, 437 }; 438 439 void iwl_mvm_tt_initialize(struct iwl_mvm *mvm, u32 min_backoff) 440 { 441 struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle; 442 443 IWL_DEBUG_TEMP(mvm, "Initialize Thermal Throttling\n"); 444 445 if (mvm->cfg->thermal_params) 446 tt->params = *mvm->cfg->thermal_params; 447 else 448 tt->params = iwl_mvm_default_tt_params; 449 450 tt->throttle = false; 451 tt->dynamic_smps = false; 452 tt->min_backoff = min_backoff; 453 INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill); 454 } 455 456 void iwl_mvm_tt_exit(struct iwl_mvm *mvm) 457 { 458 cancel_delayed_work_sync(&mvm->thermal_throttle.ct_kill_exit); 459 IWL_DEBUG_TEMP(mvm, "Exit Thermal Throttling\n"); 460 } 461