aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless
diff options
context:
space:
mode:
authorLuciano Coelho <luciano.coelho@intel.com>2014-09-04 05:29:15 -0400
committerEmmanuel Grumbach <emmanuel.grumbach@intel.com>2014-09-14 06:06:40 -0400
commita0a09243e02e0ad6a366139093d80b870ec9a16e (patch)
treea5027bca0554298925f89297ae1c0814cf54d71d /drivers/net/wireless
parent34e611ea2a9d88ccf77b2a073ab9b0c53e58e6f8 (diff)
iwlwifi: mvm: use the firmware to get the temperature during CT kill
Reading the temperature directly from the hardware, without the help of the firmware, is a complex process and is not entirely the same for different hardware. Also, some NICs don't easily allow access to the sensors when the firmware is not running, which would add even more complexity to the code. To reduce the code complexity and to avoid code duplication between the firmware and the driver, boot the firmware briefly to read the current temperature while in CT kill mode. Signed-off-by: Luciano Coelho <luciano.coelho@intel.com> Reviewed-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api.h32
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac80211.c34
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mvm.h3
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/ops.c2
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/tt.c326
5 files changed, 140 insertions, 257 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
index b599b5288982..fbcc036b1e44 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
@@ -205,6 +205,10 @@ enum {
205 REPLY_SF_CFG_CMD = 0xd1, 205 REPLY_SF_CFG_CMD = 0xd1,
206 REPLY_BEACON_FILTERING_CMD = 0xd2, 206 REPLY_BEACON_FILTERING_CMD = 0xd2,
207 207
208 /* DTS measurements */
209 CMD_DTS_MEASUREMENT_TRIGGER = 0xdc,
210 DTS_MEASUREMENT_NOTIFICATION = 0xdd,
211
208 REPLY_DEBUG_CMD = 0xf0, 212 REPLY_DEBUG_CMD = 0xf0,
209 DEBUG_LOG_MSG = 0xf7, 213 DEBUG_LOG_MSG = 0xf7,
210 214
@@ -1618,4 +1622,32 @@ struct iwl_sf_cfg_cmd {
1618 __le32 full_on_timeouts[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES]; 1622 __le32 full_on_timeouts[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES];
1619} __packed; /* SF_CFG_API_S_VER_2 */ 1623} __packed; /* SF_CFG_API_S_VER_2 */
1620 1624
1625/* DTS measurements */
1626
1627enum iwl_dts_measurement_flags {
1628 DTS_TRIGGER_CMD_FLAGS_TEMP = BIT(0),
1629 DTS_TRIGGER_CMD_FLAGS_VOLT = BIT(1),
1630};
1631
1632/**
1633 * iwl_dts_measurement_cmd - request DTS temperature and/or voltage measurements
1634 *
1635 * @flags: indicates which measurements we want as specified in &enum
1636 * iwl_dts_measurement_flags
1637 */
1638struct iwl_dts_measurement_cmd {
1639 __le32 flags;
1640} __packed; /* TEMPERATURE_MEASUREMENT_TRIGGER_CMD_S */
1641
1642/**
1643 * iwl_dts_measurement_notif - notification received with the measurements
1644 *
1645 * @temp: the measured temperature
1646 * @voltage: the measured voltage
1647 */
1648struct iwl_dts_measurement_notif {
1649 __le32 temp;
1650 __le32 voltage;
1651} __packed; /* TEMPERATURE_MEASUREMENT_TRIGGER_NTFY_S */
1652
1621#endif /* __fw_api_h__ */ 1653#endif /* __fw_api_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index 089d7b37cf90..5c33d2d15488 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -815,12 +815,11 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
815 mvm->rx_ba_sessions = 0; 815 mvm->rx_ba_sessions = 0;
816} 816}
817 817
818static int iwl_mvm_mac_start(struct ieee80211_hw *hw) 818int __iwl_mvm_mac_start(struct iwl_mvm *mvm)
819{ 819{
820 struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
821 int ret; 820 int ret;
822 821
823 mutex_lock(&mvm->mutex); 822 lockdep_assert_held(&mvm->mutex);
824 823
825 /* Clean up some internal and mac80211 state on restart */ 824 /* Clean up some internal and mac80211 state on restart */
826 if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) 825 if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
@@ -837,6 +836,16 @@ static int iwl_mvm_mac_start(struct ieee80211_hw *hw)
837 iwl_mvm_d0i3_enable_tx(mvm, NULL); 836 iwl_mvm_d0i3_enable_tx(mvm, NULL);
838 } 837 }
839 838
839 return ret;
840}
841
842static int iwl_mvm_mac_start(struct ieee80211_hw *hw)
843{
844 struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
845 int ret;
846
847 mutex_lock(&mvm->mutex);
848 ret = __iwl_mvm_mac_start(mvm);
840 mutex_unlock(&mvm->mutex); 849 mutex_unlock(&mvm->mutex);
841 850
842 return ret; 851 return ret;
@@ -862,14 +871,9 @@ static void iwl_mvm_mac_restart_complete(struct ieee80211_hw *hw)
862 mutex_unlock(&mvm->mutex); 871 mutex_unlock(&mvm->mutex);
863} 872}
864 873
865static void iwl_mvm_mac_stop(struct ieee80211_hw *hw) 874void __iwl_mvm_mac_stop(struct iwl_mvm *mvm)
866{ 875{
867 struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); 876 lockdep_assert_held(&mvm->mutex);
868
869 flush_work(&mvm->d0i3_exit_work);
870 flush_work(&mvm->async_handlers_wk);
871
872 mutex_lock(&mvm->mutex);
873 877
874 /* disallow low power states when the FW is down */ 878 /* disallow low power states when the FW is down */
875 iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN); 879 iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
@@ -891,7 +895,17 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)
891 iwl_mvm_del_aux_sta(mvm); 895 iwl_mvm_del_aux_sta(mvm);
892 896
893 mvm->ucode_loaded = false; 897 mvm->ucode_loaded = false;
898}
894 899
900static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)
901{
902 struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
903
904 flush_work(&mvm->d0i3_exit_work);
905 flush_work(&mvm->async_handlers_wk);
906
907 mutex_lock(&mvm->mutex);
908 __iwl_mvm_mac_stop(mvm);
895 mutex_unlock(&mvm->mutex); 909 mutex_unlock(&mvm->mutex);
896 910
897 /* 911 /*
diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h
index 4b1db9a3e65c..a36fa6c658d3 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h
@@ -792,6 +792,9 @@ struct iwl_rate_info {
792 u8 ieee; /* MAC header: IWL_RATE_6M_IEEE, etc. */ 792 u8 ieee; /* MAC header: IWL_RATE_6M_IEEE, etc. */
793}; 793};
794 794
795void __iwl_mvm_mac_stop(struct iwl_mvm *mvm);
796int __iwl_mvm_mac_start(struct iwl_mvm *mvm);
797
795/****************** 798/******************
796 * MVM Methods 799 * MVM Methods
797 ******************/ 800 ******************/
diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c
index 5d8c562d1a78..9710084ad286 100644
--- a/drivers/net/wireless/iwlwifi/mvm/ops.c
+++ b/drivers/net/wireless/iwlwifi/mvm/ops.c
@@ -332,6 +332,8 @@ static const char *const iwl_mvm_cmd_strings[REPLY_MAX] = {
332 CMD(BCAST_FILTER_CMD), 332 CMD(BCAST_FILTER_CMD),
333 CMD(REPLY_SF_CFG_CMD), 333 CMD(REPLY_SF_CFG_CMD),
334 CMD(REPLY_BEACON_FILTERING_CMD), 334 CMD(REPLY_BEACON_FILTERING_CMD),
335 CMD(CMD_DTS_MEASUREMENT_TRIGGER),
336 CMD(DTS_MEASUREMENT_NOTIFICATION),
335 CMD(REPLY_THERMAL_MNG_BACKOFF), 337 CMD(REPLY_THERMAL_MNG_BACKOFF),
336 CMD(MAC_PM_POWER_TABLE), 338 CMD(MAC_PM_POWER_TABLE),
337 CMD(BT_COEX_CI), 339 CMD(BT_COEX_CI),
diff --git a/drivers/net/wireless/iwlwifi/mvm/tt.c b/drivers/net/wireless/iwlwifi/mvm/tt.c
index c3e1fe4282f1..c750ca7b8269 100644
--- a/drivers/net/wireless/iwlwifi/mvm/tt.c
+++ b/drivers/net/wireless/iwlwifi/mvm/tt.c
@@ -69,275 +69,99 @@
69#include "iwl-csr.h" 69#include "iwl-csr.h"
70#include "iwl-prph.h" 70#include "iwl-prph.h"
71 71
72#define OTP_DTS_DIODE_DEVIATION 96 /*in words*/ 72#define IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT HZ
73/* VBG - Voltage Band Gap error data (temperature offset) */
74#define OTP_WP_DTS_VBG (OTP_DTS_DIODE_DEVIATION + 2)
75#define MEAS_VBG_MIN_VAL 2300
76#define MEAS_VBG_MAX_VAL 3000
77#define MEAS_VBG_DEFAULT_VAL 2700
78#define DTS_DIODE_VALID(flags) (flags & DTS_DIODE_REG_FLAGS_PASS_ONCE)
79#define MIN_TEMPERATURE 0
80#define MAX_TEMPERATURE 125
81#define TEMPERATURE_ERROR (MAX_TEMPERATURE + 1)
82#define PTAT_DIGITAL_VALUE_MIN_VALUE 0
83#define PTAT_DIGITAL_VALUE_MAX_VALUE 0xFF
84#define DTS_VREFS_NUM 5
85static inline u32 DTS_DIODE_GET_VREFS_ID(u32 flags)
86{
87 return (flags & DTS_DIODE_REG_FLAGS_VREFS_ID) >>
88 DTS_DIODE_REG_FLAGS_VREFS_ID_POS;
89}
90 73
91#define CALC_VREFS_MIN_DIFF 43 74static void iwl_mvm_enter_ctkill(struct iwl_mvm *mvm)
92#define CALC_VREFS_MAX_DIFF 51
93#define CALC_LUT_SIZE (1 + CALC_VREFS_MAX_DIFF - CALC_VREFS_MIN_DIFF)
94#define CALC_LUT_INDEX_OFFSET CALC_VREFS_MIN_DIFF
95#define CALC_TEMPERATURE_RESULT_SHIFT_OFFSET 23
96
97/*
98 * @digital_value: The diode's digital-value sampled (temperature/voltage)
99 * @vref_low: The lower voltage-reference (the vref just below the diode's
100 * sampled digital-value)
101 * @vref_high: The higher voltage-reference (the vref just above the diode's
102 * sampled digital-value)
103 * @flags: bits[1:0]: The ID of the Vrefs pair (lowVref,highVref)
104 * bits[6:2]: Reserved.
105 * bits[7:7]: Indicates completion of at least 1 successful sample
106 * since last DTS reset.
107 */
108struct iwl_mvm_dts_diode_bits {
109 u8 digital_value;
110 u8 vref_low;
111 u8 vref_high;
112 u8 flags;
113} __packed;
114
115union dts_diode_results {
116 u32 reg_value;
117 struct iwl_mvm_dts_diode_bits bits;
118} __packed;
119
120static s16 iwl_mvm_dts_get_volt_band_gap(struct iwl_mvm *mvm)
121{ 75{
122 struct iwl_nvm_section calib_sec; 76 u32 duration = mvm->thermal_throttle.params->ct_kill_duration;
123 const __le16 *calib;
124 u16 vbg;
125
126 /* TODO: move parsing to NVM code */
127 calib_sec = mvm->nvm_sections[NVM_SECTION_TYPE_CALIBRATION];
128 calib = (__le16 *)calib_sec.data;
129 77
130 vbg = le16_to_cpu(calib[OTP_WP_DTS_VBG]); 78 if (test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status))
79 return;
131 80
132 if (vbg < MEAS_VBG_MIN_VAL || vbg > MEAS_VBG_MAX_VAL) 81 IWL_ERR(mvm, "Enter CT Kill\n");
133 vbg = MEAS_VBG_DEFAULT_VAL; 82 iwl_mvm_set_hw_ctkill_state(mvm, true);
134 83
135 return vbg; 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(&mvm->thermal_throttle.ct_kill_exit,
90 round_jiffies_relative(duration * HZ));
136} 91}
137 92
138static u16 iwl_mvm_dts_get_ptat_deviation_offset(struct iwl_mvm *mvm) 93static void iwl_mvm_exit_ctkill(struct iwl_mvm *mvm)
139{ 94{
140 const u8 *calib; 95 if (!test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status))
141 u8 ptat, pa1, pa2, median; 96 return;
142
143 /* TODO: move parsing to NVM code */
144 calib = mvm->nvm_sections[NVM_SECTION_TYPE_CALIBRATION].data;
145 ptat = calib[OTP_DTS_DIODE_DEVIATION * 2];
146 pa1 = calib[OTP_DTS_DIODE_DEVIATION * 2 + 1];
147 pa2 = calib[OTP_DTS_DIODE_DEVIATION * 2 + 2];
148
149 /* get the median: */
150 if (ptat > pa1) {
151 if (ptat > pa2)
152 median = (pa1 > pa2) ? pa1 : pa2;
153 else
154 median = ptat;
155 } else {
156 if (pa1 > pa2)
157 median = (ptat > pa2) ? ptat : pa2;
158 else
159 median = pa1;
160 }
161 97
162 return ptat - median; 98 IWL_ERR(mvm, "Exit CT Kill\n");
99 iwl_mvm_set_hw_ctkill_state(mvm, false);
163} 100}
164 101
165static u8 iwl_mvm_dts_calibrate_ptat_deviation(struct iwl_mvm *mvm, u8 value) 102static bool iwl_mvm_temp_notif(struct iwl_notif_wait_data *notif_wait,
103 struct iwl_rx_packet *pkt, void *data)
166{ 104{
167 /* Calibrate the PTAT digital value, based on PTAT deviation data: */ 105 struct iwl_mvm *mvm =
168 s16 new_val = value - iwl_mvm_dts_get_ptat_deviation_offset(mvm); 106 container_of(notif_wait, struct iwl_mvm, notif_wait);
107 int *temp = data;
108 struct iwl_dts_measurement_notif *notif;
109 int len = iwl_rx_packet_payload_len(pkt);
110
111 if (WARN_ON_ONCE(len != sizeof(*notif))) {
112 IWL_ERR(mvm, "Invalid DTS_MEASUREMENT_NOTIFICATION\n");
113 return true;
114 }
169 115
170 if (new_val > PTAT_DIGITAL_VALUE_MAX_VALUE) 116 notif = (void *)pkt->data;
171 new_val = PTAT_DIGITAL_VALUE_MAX_VALUE;
172 else if (new_val < PTAT_DIGITAL_VALUE_MIN_VALUE)
173 new_val = PTAT_DIGITAL_VALUE_MIN_VALUE;
174 117
175 return new_val; 118 *temp = le32_to_cpu(notif->temp);
176}
177 119
178static bool dts_get_adjacent_vrefs(struct iwl_mvm *mvm, 120 /* shouldn't be negative, but since it's s32, make sure it isn't */
179 union dts_diode_results *avg_ptat) 121 if (WARN_ON_ONCE(*temp < 0))
180{ 122 *temp = 0;
181 u8 vrefs_results[DTS_VREFS_NUM];
182 u8 low_vref_index = 0, flags;
183 u32 reg;
184
185 reg = iwl_read_prph(mvm->trans, DTSC_VREF_AVG);
186 memcpy(vrefs_results, &reg, sizeof(reg));
187 reg = iwl_read_prph(mvm->trans, DTSC_VREF5_AVG);
188 vrefs_results[4] = reg & 0xff;
189
190 if (avg_ptat->bits.digital_value < vrefs_results[0] ||
191 avg_ptat->bits.digital_value > vrefs_results[4])
192 return false;
193
194 if (avg_ptat->bits.digital_value > vrefs_results[3])
195 low_vref_index = 3;
196 else if (avg_ptat->bits.digital_value > vrefs_results[2])
197 low_vref_index = 2;
198 else if (avg_ptat->bits.digital_value > vrefs_results[1])
199 low_vref_index = 1;
200
201 avg_ptat->bits.vref_low = vrefs_results[low_vref_index];
202 avg_ptat->bits.vref_high = vrefs_results[low_vref_index + 1];
203 flags = avg_ptat->bits.flags;
204 avg_ptat->bits.flags =
205 (flags & ~DTS_DIODE_REG_FLAGS_VREFS_ID) |
206 (low_vref_index & DTS_DIODE_REG_FLAGS_VREFS_ID);
207 return true;
208}
209 123
210/* 124 IWL_DEBUG_TEMP(mvm, "DTS_MEASUREMENT_NOTIFICATION - %d\n", *temp);
211 * return true it the results are valid, and false otherwise. 125 return true;
212 */
213static bool dts_read_ptat_avg_results(struct iwl_mvm *mvm,
214 union dts_diode_results *avg_ptat)
215{
216 u32 reg;
217 u8 tmp;
218
219 /* fill the diode value and pass_once with avg-reg results */
220 reg = iwl_read_prph(mvm->trans, DTSC_PTAT_AVG);
221 reg &= DTS_DIODE_REG_DIG_VAL | DTS_DIODE_REG_PASS_ONCE;
222 avg_ptat->reg_value = reg;
223
224 /* calibrate the PTAT digital value */
225 tmp = avg_ptat->bits.digital_value;
226 tmp = iwl_mvm_dts_calibrate_ptat_deviation(mvm, tmp);
227 avg_ptat->bits.digital_value = tmp;
228
229 /*
230 * fill vrefs fields, based on the avgVrefs results
231 * and the diode value
232 */
233 return dts_get_adjacent_vrefs(mvm, avg_ptat) &&
234 DTS_DIODE_VALID(avg_ptat->bits.flags);
235} 126}
236 127
237static s32 calculate_nic_temperature(union dts_diode_results avg_ptat, 128static int iwl_mvm_get_temp_cmd(struct iwl_mvm *mvm)
238 u16 volt_band_gap)
239{ 129{
240 u32 tmp_result; 130 struct iwl_dts_measurement_cmd cmd = {
241 u8 vrefs_diff; 131 .flags = cpu_to_le32(DTS_TRIGGER_CMD_FLAGS_TEMP),
242 /*
243 * For temperature calculation (at the end, shift right by 23)
244 * LUT[(D2-D1)] = ROUND{ 2^23 / ((D2-D1)*9*10) }
245 * (D2-D1) == 43 44 45 46 47 48 49 50 51
246 */
247 static const u16 calc_lut[CALC_LUT_SIZE] = {
248 2168, 2118, 2071, 2026, 1983, 1942, 1902, 1864, 1828,
249 }; 132 };
250 133
251 /* 134 return iwl_mvm_send_cmd_pdu(mvm, CMD_DTS_MEASUREMENT_TRIGGER, 0,
252 * The diff between the high and low voltage-references is assumed 135 sizeof(cmd), &cmd);
253 * to be strictly be in range of [60,68]
254 */
255 vrefs_diff = avg_ptat.bits.vref_high - avg_ptat.bits.vref_low;
256
257 if (vrefs_diff < CALC_VREFS_MIN_DIFF ||
258 vrefs_diff > CALC_VREFS_MAX_DIFF)
259 return TEMPERATURE_ERROR;
260
261 /* calculate the result: */
262 tmp_result =
263 vrefs_diff * (DTS_DIODE_GET_VREFS_ID(avg_ptat.bits.flags) + 9);
264 tmp_result += avg_ptat.bits.digital_value;
265 tmp_result -= avg_ptat.bits.vref_high;
266
267 /* multiply by the LUT value (based on the diff) */
268 tmp_result *= calc_lut[vrefs_diff - CALC_LUT_INDEX_OFFSET];
269
270 /*
271 * Get the BandGap (the voltage refereces source) error data
272 * (temperature offset)
273 */
274 tmp_result *= volt_band_gap;
275
276 /*
277 * here, tmp_result value can be up to 32-bits. We want to right-shift
278 * it *without* sign-extend.
279 */
280 tmp_result = tmp_result >> CALC_TEMPERATURE_RESULT_SHIFT_OFFSET;
281
282 /*
283 * at this point, tmp_result should be in the range:
284 * 200 <= tmp_result <= 365
285 */
286 return (s16)tmp_result - 240;
287}
288
289static s32 check_nic_temperature(struct iwl_mvm *mvm)
290{
291 u16 volt_band_gap;
292 union dts_diode_results avg_ptat;
293
294 volt_band_gap = iwl_mvm_dts_get_volt_band_gap(mvm);
295
296 /* disable DTS */
297 iwl_write_prph(mvm->trans, SHR_MISC_WFM_DTS_EN, 0);
298
299 /* SV initialization */
300 iwl_write_prph(mvm->trans, SHR_MISC_WFM_DTS_EN, 1);
301 iwl_write_prph(mvm->trans, DTSC_CFG_MODE,
302 DTSC_CFG_MODE_PERIODIC);
303
304 /* wait for results */
305 msleep(100);
306 if (!dts_read_ptat_avg_results(mvm, &avg_ptat))
307 return TEMPERATURE_ERROR;
308
309 /* disable DTS */
310 iwl_write_prph(mvm->trans, SHR_MISC_WFM_DTS_EN, 0);
311
312 return calculate_nic_temperature(avg_ptat, volt_band_gap);
313} 136}
314 137
315static void iwl_mvm_enter_ctkill(struct iwl_mvm *mvm) 138static int iwl_mvm_get_temp(struct iwl_mvm *mvm)
316{ 139{
317 u32 duration = mvm->thermal_throttle.params->ct_kill_duration; 140 struct iwl_notification_wait wait_temp_notif;
141 static const u8 temp_notif[] = { DTS_MEASUREMENT_NOTIFICATION };
142 int ret, temp;
318 143
319 if (test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status)) 144 lockdep_assert_held(&mvm->mutex);
320 return;
321 145
322 IWL_ERR(mvm, "Enter CT Kill\n"); 146 iwl_init_notification_wait(&mvm->notif_wait, &wait_temp_notif,
323 iwl_mvm_set_hw_ctkill_state(mvm, true); 147 temp_notif, ARRAY_SIZE(temp_notif),
148 iwl_mvm_temp_notif, &temp);
324 149
325 /* Don't schedule an exit work if we're in test mode, since 150 ret = iwl_mvm_get_temp_cmd(mvm);
326 * the temperature will not change unless we manually set it 151 if (ret) {
327 * again (or disable testing). 152 IWL_ERR(mvm, "Failed to get the temperature (err=%d)\n", ret);
328 */ 153 iwl_remove_notification(&mvm->notif_wait, &wait_temp_notif);
329 if (!mvm->temperature_test) 154 return ret;
330 schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit, 155 }
331 round_jiffies_relative(duration * HZ));
332}
333 156
334static void iwl_mvm_exit_ctkill(struct iwl_mvm *mvm) 157 ret = iwl_wait_notification(&mvm->notif_wait, &wait_temp_notif,
335{ 158 IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT);
336 if (!test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status)) 159 if (ret) {
337 return; 160 IWL_ERR(mvm, "Getting the temperature timed out\n");
161 return ret;
162 }
338 163
339 IWL_ERR(mvm, "Exit CT Kill\n"); 164 return temp;
340 iwl_mvm_set_hw_ctkill_state(mvm, false);
341} 165}
342 166
343static void check_exit_ctkill(struct work_struct *work) 167static void check_exit_ctkill(struct work_struct *work)
@@ -352,28 +176,36 @@ static void check_exit_ctkill(struct work_struct *work)
352 176
353 duration = tt->params->ct_kill_duration; 177 duration = tt->params->ct_kill_duration;
354 178
179 mutex_lock(&mvm->mutex);
180
181 if (__iwl_mvm_mac_start(mvm))
182 goto reschedule;
183
355 /* make sure the device is available for direct read/writes */ 184 /* make sure the device is available for direct read/writes */
356 if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_CHECK_CTKILL)) 185 if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_CHECK_CTKILL)) {
186 __iwl_mvm_mac_stop(mvm);
357 goto reschedule; 187 goto reschedule;
188 }
358 189
359 iwl_trans_start_hw(mvm->trans); 190 temp = iwl_mvm_get_temp(mvm);
360 temp = check_nic_temperature(mvm);
361 iwl_trans_stop_device(mvm->trans);
362 191
363 iwl_mvm_unref(mvm, IWL_MVM_REF_CHECK_CTKILL); 192 iwl_mvm_unref(mvm, IWL_MVM_REF_CHECK_CTKILL);
364 193
365 if (temp < MIN_TEMPERATURE || temp > MAX_TEMPERATURE) { 194 __iwl_mvm_mac_stop(mvm);
366 IWL_DEBUG_TEMP(mvm, "Failed to measure NIC temperature\n"); 195
196 if (temp < 0)
367 goto reschedule; 197 goto reschedule;
368 } 198
369 IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", temp); 199 IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", temp);
370 200
371 if (temp <= tt->params->ct_kill_exit) { 201 if (temp <= tt->params->ct_kill_exit) {
202 mutex_unlock(&mvm->mutex);
372 iwl_mvm_exit_ctkill(mvm); 203 iwl_mvm_exit_ctkill(mvm);
373 return; 204 return;
374 } 205 }
375 206
376reschedule: 207reschedule:
208 mutex_unlock(&mvm->mutex);
377 schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit, 209 schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit,
378 round_jiffies(duration * HZ)); 210 round_jiffies(duration * HZ));
379} 211}