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