Commit | Line | Data |
---|---|---|
5da4b55f MA |
1 | /****************************************************************************** |
2 | * | |
01f8162a | 3 | * Copyright(c) 2007 - 2009 Intel Corporation. All rights reserved. |
5da4b55f MA |
4 | * |
5 | * Portions of this file are derived from the ipw3945 project, as well | |
6 | * as portions of the ieee80211 subsystem header files. | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify it | |
9 | * under the terms of version 2 of the GNU General Public License as | |
10 | * published by the Free Software Foundation. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
15 | * more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License along with | |
18 | * this program; if not, write to the Free Software Foundation, Inc., | |
19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA | |
20 | * | |
21 | * The full GNU General Public License is included in this distribution in the | |
22 | * file called LICENSE. | |
23 | * | |
24 | * Contact Information: | |
759ef89f | 25 | * Intel Linux Wireless <ilw@linux.intel.com> |
5da4b55f MA |
26 | * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 |
27 | *****************************************************************************/ | |
28 | ||
29 | ||
30 | #include <linux/kernel.h> | |
31 | #include <linux/module.h> | |
5da4b55f MA |
32 | #include <linux/init.h> |
33 | ||
34 | #include <net/mac80211.h> | |
35 | ||
36 | #include "iwl-eeprom.h" | |
3e0d4cb1 | 37 | #include "iwl-dev.h" |
5da4b55f | 38 | #include "iwl-core.h" |
39b73fb1 | 39 | #include "iwl-io.h" |
5a36ba0e | 40 | #include "iwl-commands.h" |
5da4b55f MA |
41 | #include "iwl-debug.h" |
42 | #include "iwl-power.h" | |
5da4b55f MA |
43 | |
44 | /* | |
e312c24c | 45 | * Setting power level allows the card to go to sleep when not busy. |
5da4b55f | 46 | * |
e312c24c JB |
47 | * We calculate a sleep command based on the required latency, which |
48 | * we get from mac80211. In order to handle thermal throttling, we can | |
49 | * also use pre-defined power levels. | |
5da4b55f MA |
50 | */ |
51 | ||
e312c24c JB |
52 | /* |
53 | * For now, keep using power level 1 instead of automatically | |
54 | * adjusting ... | |
55 | */ | |
56 | bool no_sleep_autoadjust = true; | |
57 | module_param(no_sleep_autoadjust, bool, S_IRUGO); | |
58 | MODULE_PARM_DESC(no_sleep_autoadjust, | |
59 | "don't automatically adjust sleep level " | |
60 | "according to maximum network latency"); | |
5da4b55f | 61 | |
e312c24c JB |
62 | /* |
63 | * This defines the old power levels. They are still used by default | |
64 | * (level 1) and for thermal throttle (levels 3 through 5) | |
65 | */ | |
66 | ||
67 | struct iwl_power_vec_entry { | |
68 | struct iwl_powertable_cmd cmd; | |
69 | u8 no_dtim; | |
70 | }; | |
71 | ||
72 | #define IWL_DTIM_RANGE_0_MAX 2 | |
73 | #define IWL_DTIM_RANGE_1_MAX 10 | |
5da4b55f | 74 | |
7af2c460 JB |
75 | #define NOSLP cpu_to_le16(0), 0, 0 |
76 | #define SLP IWL_POWER_DRIVER_ALLOW_SLEEP_MSK, 0, 0 | |
77 | #define TU_TO_USEC 1024 | |
78 | #define SLP_TOUT(T) cpu_to_le32((T) * TU_TO_USEC) | |
79 | #define SLP_VEC(X0, X1, X2, X3, X4) {cpu_to_le32(X0), \ | |
80 | cpu_to_le32(X1), \ | |
81 | cpu_to_le32(X2), \ | |
82 | cpu_to_le32(X3), \ | |
83 | cpu_to_le32(X4)} | |
5da4b55f | 84 | /* default power management (not Tx power) table values */ |
e312c24c | 85 | /* for DTIM period 0 through IWL_DTIM_RANGE_0_MAX */ |
7af2c460 | 86 | static const struct iwl_power_vec_entry range_0[IWL_POWER_NUM] = { |
5da4b55f MA |
87 | {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 2, 2, 0xFF)}, 0}, |
88 | {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 2, 2, 0xFF)}, 0}, | |
89 | {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 2, 2, 2, 0xFF)}, 0}, | |
90 | {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 2, 4, 4, 0xFF)}, 1}, | |
91 | {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 2, 4, 6, 0xFF)}, 2} | |
92 | }; | |
93 | ||
94 | ||
e312c24c | 95 | /* for DTIM period IWL_DTIM_RANGE_0_MAX + 1 through IWL_DTIM_RANGE_1_MAX */ |
7af2c460 | 96 | static const struct iwl_power_vec_entry range_1[IWL_POWER_NUM] = { |
5da4b55f MA |
97 | {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 4)}, 0}, |
98 | {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 3, 4, 7)}, 0}, | |
99 | {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 4, 6, 7, 9)}, 0}, | |
100 | {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 4, 6, 9, 10)}, 1}, | |
101 | {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 4, 7, 10, 10)}, 2} | |
102 | }; | |
103 | ||
e312c24c | 104 | /* for DTIM period > IWL_DTIM_RANGE_1_MAX */ |
7af2c460 | 105 | static const struct iwl_power_vec_entry range_2[IWL_POWER_NUM] = { |
5da4b55f MA |
106 | {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 0xFF)}, 0}, |
107 | {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(2, 4, 6, 7, 0xFF)}, 0}, | |
108 | {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 7, 9, 9, 0xFF)}, 0}, | |
109 | {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 7, 9, 9, 0xFF)}, 0}, | |
110 | {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(4, 7, 10, 10, 0xFF)}, 0} | |
111 | }; | |
112 | ||
e312c24c JB |
113 | static void iwl_static_sleep_cmd(struct iwl_priv *priv, |
114 | struct iwl_powertable_cmd *cmd, | |
115 | enum iwl_power_level lvl, int period) | |
116 | { | |
117 | const struct iwl_power_vec_entry *table; | |
118 | int max_sleep, i; | |
119 | bool skip; | |
120 | ||
121 | table = range_2; | |
122 | if (period < IWL_DTIM_RANGE_1_MAX) | |
123 | table = range_1; | |
124 | if (period < IWL_DTIM_RANGE_0_MAX) | |
125 | table = range_0; | |
126 | ||
127 | BUG_ON(lvl < 0 || lvl >= IWL_POWER_NUM); | |
128 | ||
129 | *cmd = table[lvl].cmd; | |
130 | ||
131 | if (period == 0) { | |
132 | skip = false; | |
133 | period = 1; | |
134 | } else { | |
135 | skip = !!table[lvl].no_dtim; | |
136 | } | |
137 | ||
138 | if (skip) { | |
139 | __le32 slp_itrvl = cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]; | |
140 | max_sleep = le32_to_cpu(slp_itrvl); | |
141 | if (max_sleep == 0xFF) | |
142 | max_sleep = period * (skip + 1); | |
143 | else if (max_sleep > period) | |
144 | max_sleep = (le32_to_cpu(slp_itrvl) / period) * period; | |
145 | cmd->flags |= IWL_POWER_SLEEP_OVER_DTIM_MSK; | |
146 | } else { | |
147 | max_sleep = period; | |
148 | cmd->flags &= ~IWL_POWER_SLEEP_OVER_DTIM_MSK; | |
149 | } | |
150 | ||
151 | for (i = 0; i < IWL_POWER_VEC_SIZE; i++) | |
152 | if (le32_to_cpu(cmd->sleep_interval[i]) > max_sleep) | |
153 | cmd->sleep_interval[i] = cpu_to_le32(max_sleep); | |
154 | ||
155 | if (priv->power_data.pci_pm) | |
156 | cmd->flags |= IWL_POWER_PCI_PM_MSK; | |
157 | else | |
158 | cmd->flags &= ~IWL_POWER_PCI_PM_MSK; | |
159 | ||
160 | IWL_DEBUG_POWER(priv, "Sleep command for index %d\n", lvl + 1); | |
161 | } | |
162 | ||
46f9381a WYG |
163 | /* default Thermal Throttling transaction table |
164 | * Current state | Throttling Down | Throttling Up | |
165 | *============================================================================= | |
166 | * Condition Nxt State Condition Nxt State Condition Nxt State | |
167 | *----------------------------------------------------------------------------- | |
168 | * IWL_TI_0 T >= 115 CT_KILL 115>T>=105 TI_1 N/A N/A | |
169 | * IWL_TI_1 T >= 115 CT_KILL 115>T>=110 TI_2 T<=95 TI_0 | |
170 | * IWL_TI_2 T >= 115 CT_KILL T<=100 TI_1 | |
171 | * IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0 | |
172 | *============================================================================= | |
173 | */ | |
174 | static const struct iwl_tt_trans tt_range_0[IWL_TI_STATE_MAX - 1] = { | |
175 | {IWL_TI_0, IWL_ABSOLUTE_ZERO, 104}, | |
176 | {IWL_TI_1, 105, CT_KILL_THRESHOLD}, | |
177 | {IWL_TI_CT_KILL, CT_KILL_THRESHOLD + 1, IWL_ABSOLUTE_MAX} | |
178 | }; | |
179 | static const struct iwl_tt_trans tt_range_1[IWL_TI_STATE_MAX - 1] = { | |
180 | {IWL_TI_0, IWL_ABSOLUTE_ZERO, 95}, | |
181 | {IWL_TI_2, 110, CT_KILL_THRESHOLD}, | |
182 | {IWL_TI_CT_KILL, CT_KILL_THRESHOLD + 1, IWL_ABSOLUTE_MAX} | |
183 | }; | |
184 | static const struct iwl_tt_trans tt_range_2[IWL_TI_STATE_MAX - 1] = { | |
185 | {IWL_TI_1, IWL_ABSOLUTE_ZERO, 100}, | |
186 | {IWL_TI_CT_KILL, CT_KILL_THRESHOLD + 1, IWL_ABSOLUTE_MAX}, | |
187 | {IWL_TI_CT_KILL, CT_KILL_THRESHOLD + 1, IWL_ABSOLUTE_MAX} | |
188 | }; | |
189 | static const struct iwl_tt_trans tt_range_3[IWL_TI_STATE_MAX - 1] = { | |
190 | {IWL_TI_0, IWL_ABSOLUTE_ZERO, CT_KILL_EXIT_THRESHOLD}, | |
191 | {IWL_TI_CT_KILL, CT_KILL_EXIT_THRESHOLD + 1, IWL_ABSOLUTE_MAX}, | |
192 | {IWL_TI_CT_KILL, CT_KILL_EXIT_THRESHOLD + 1, IWL_ABSOLUTE_MAX} | |
193 | }; | |
194 | ||
195 | /* Advance Thermal Throttling default restriction table */ | |
196 | static const struct iwl_tt_restriction restriction_range[IWL_TI_STATE_MAX] = { | |
3ad3b92a JB |
197 | {IWL_ANT_OK_MULTI, IWL_ANT_OK_MULTI, true }, |
198 | {IWL_ANT_OK_SINGLE, IWL_ANT_OK_MULTI, true }, | |
199 | {IWL_ANT_OK_SINGLE, IWL_ANT_OK_SINGLE, false }, | |
200 | {IWL_ANT_OK_NONE, IWL_ANT_OK_NONE, false } | |
46f9381a | 201 | }; |
d25aabb0 | 202 | |
5da4b55f | 203 | |
e312c24c JB |
204 | static void iwl_power_sleep_cam_cmd(struct iwl_priv *priv, |
205 | struct iwl_powertable_cmd *cmd) | |
5da4b55f | 206 | { |
e312c24c | 207 | memset(cmd, 0, sizeof(*cmd)); |
5da4b55f | 208 | |
e312c24c JB |
209 | if (priv->power_data.pci_pm) |
210 | cmd->flags |= IWL_POWER_PCI_PM_MSK; | |
5da4b55f | 211 | |
e312c24c | 212 | IWL_DEBUG_POWER(priv, "Sleep command for CAM\n"); |
5da4b55f MA |
213 | } |
214 | ||
e312c24c JB |
215 | static void iwl_power_fill_sleep_cmd(struct iwl_priv *priv, |
216 | struct iwl_powertable_cmd *cmd, | |
217 | int dynps_ms, int wakeup_period) | |
5da4b55f | 218 | { |
4c561a02 JB |
219 | /* |
220 | * These are the original power level 3 sleep successions. The | |
221 | * device may behave better with such succession and was also | |
222 | * only tested with that. Just like the original sleep commands, | |
223 | * also adjust the succession here to the wakeup_period below. | |
224 | * The ranges are the same as for the sleep commands, 0-2, 3-9 | |
225 | * and >10, which is selected based on the DTIM interval for | |
226 | * the sleep index but here we use the wakeup period since that | |
227 | * is what we need to do for the latency requirements. | |
228 | */ | |
229 | static const u8 slp_succ_r0[IWL_POWER_VEC_SIZE] = { 2, 2, 2, 2, 2 }; | |
230 | static const u8 slp_succ_r1[IWL_POWER_VEC_SIZE] = { 2, 4, 6, 7, 9 }; | |
231 | static const u8 slp_succ_r2[IWL_POWER_VEC_SIZE] = { 2, 7, 9, 9, 0xFF }; | |
232 | const u8 *slp_succ = slp_succ_r0; | |
5cd19c5f | 233 | int i; |
5da4b55f | 234 | |
4c561a02 JB |
235 | if (wakeup_period > IWL_DTIM_RANGE_0_MAX) |
236 | slp_succ = slp_succ_r1; | |
237 | if (wakeup_period > IWL_DTIM_RANGE_1_MAX) | |
238 | slp_succ = slp_succ_r2; | |
239 | ||
e312c24c | 240 | memset(cmd, 0, sizeof(*cmd)); |
5cd19c5f | 241 | |
e312c24c JB |
242 | cmd->flags = IWL_POWER_DRIVER_ALLOW_SLEEP_MSK | |
243 | IWL_POWER_FAST_PD; /* no use seeing frames for others */ | |
5da4b55f | 244 | |
e312c24c JB |
245 | if (priv->power_data.pci_pm) |
246 | cmd->flags |= IWL_POWER_PCI_PM_MSK; | |
5da4b55f | 247 | |
e312c24c JB |
248 | cmd->rx_data_timeout = cpu_to_le32(1000 * dynps_ms); |
249 | cmd->tx_data_timeout = cpu_to_le32(1000 * dynps_ms); | |
5da4b55f | 250 | |
5cd19c5f | 251 | for (i = 0; i < IWL_POWER_VEC_SIZE; i++) |
4c561a02 JB |
252 | cmd->sleep_interval[i] = |
253 | cpu_to_le32(min_t(int, slp_succ[i], wakeup_period)); | |
e312c24c JB |
254 | |
255 | IWL_DEBUG_POWER(priv, "Automatic sleep command\n"); | |
256 | } | |
5da4b55f | 257 | |
e312c24c JB |
258 | static int iwl_set_power(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd) |
259 | { | |
260 | IWL_DEBUG_POWER(priv, "Sending power/sleep command\n"); | |
e1623446 TW |
261 | IWL_DEBUG_POWER(priv, "Flags value = 0x%08X\n", cmd->flags); |
262 | IWL_DEBUG_POWER(priv, "Tx timeout = %u\n", le32_to_cpu(cmd->tx_data_timeout)); | |
263 | IWL_DEBUG_POWER(priv, "Rx timeout = %u\n", le32_to_cpu(cmd->rx_data_timeout)); | |
264 | IWL_DEBUG_POWER(priv, "Sleep interval vector = { %d , %d , %d , %d , %d }\n", | |
5da4b55f MA |
265 | le32_to_cpu(cmd->sleep_interval[0]), |
266 | le32_to_cpu(cmd->sleep_interval[1]), | |
267 | le32_to_cpu(cmd->sleep_interval[2]), | |
268 | le32_to_cpu(cmd->sleep_interval[3]), | |
269 | le32_to_cpu(cmd->sleep_interval[4])); | |
270 | ||
e312c24c JB |
271 | return iwl_send_cmd_pdu(priv, POWER_TABLE_CMD, |
272 | sizeof(struct iwl_powertable_cmd), cmd); | |
5da4b55f MA |
273 | } |
274 | ||
275 | ||
04816448 | 276 | int iwl_power_update_mode(struct iwl_priv *priv, bool force) |
5da4b55f | 277 | { |
5da4b55f | 278 | int ret = 0; |
3ad3b92a | 279 | struct iwl_tt_mgmt *tt = &priv->thermal_throttle; |
e312c24c JB |
280 | bool enabled = (priv->iw_mode == NL80211_IFTYPE_STATION) && |
281 | (priv->hw->conf.flags & IEEE80211_CONF_PS); | |
a71c8f62 | 282 | bool update_chains; |
e312c24c JB |
283 | struct iwl_powertable_cmd cmd; |
284 | int dtimper; | |
5da4b55f | 285 | |
04816448 | 286 | /* Don't update the RX chain when chain noise calibration is running */ |
a71c8f62 WT |
287 | update_chains = priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE || |
288 | priv->chain_noise_data.state == IWL_CHAIN_NOISE_ALIVE; | |
04816448 | 289 | |
e312c24c JB |
290 | if (priv->vif) |
291 | dtimper = priv->vif->bss_conf.dtim_period; | |
292 | else | |
293 | dtimper = 1; | |
294 | ||
295 | /* TT power setting overwrites everything */ | |
296 | if (tt->state >= IWL_TI_1) | |
297 | iwl_static_sleep_cmd(priv, &cmd, tt->tt_power_mode, dtimper); | |
298 | else if (!enabled) | |
299 | iwl_power_sleep_cam_cmd(priv, &cmd); | |
300 | else if (priv->power_data.debug_sleep_level_override >= 0) | |
301 | iwl_static_sleep_cmd(priv, &cmd, | |
302 | priv->power_data.debug_sleep_level_override, | |
303 | dtimper); | |
304 | else if (no_sleep_autoadjust) | |
305 | iwl_static_sleep_cmd(priv, &cmd, IWL_POWER_INDEX_1, dtimper); | |
306 | else | |
307 | iwl_power_fill_sleep_cmd(priv, &cmd, | |
308 | priv->hw->conf.dynamic_ps_timeout, | |
309 | priv->hw->conf.max_sleep_period); | |
5da4b55f | 310 | |
7af2c460 | 311 | if (iwl_is_ready_rf(priv) && |
e312c24c JB |
312 | (memcmp(&priv->power_data.sleep_cmd, &cmd, sizeof(cmd)) || force)) { |
313 | if (cmd.flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK) | |
5da4b55f MA |
314 | set_bit(STATUS_POWER_PMI, &priv->status); |
315 | ||
ca579617 | 316 | ret = iwl_set_power(priv, &cmd); |
3a780d25 | 317 | if (!ret) { |
e312c24c | 318 | if (!(cmd.flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK)) |
3a780d25 WYG |
319 | clear_bit(STATUS_POWER_PMI, &priv->status); |
320 | ||
321 | if (priv->cfg->ops->lib->update_chain_flags && | |
322 | update_chains) | |
323 | priv->cfg->ops->lib->update_chain_flags(priv); | |
b57d46aa | 324 | else if (priv->cfg->ops->lib->update_chain_flags) |
3a780d25 WYG |
325 | IWL_DEBUG_POWER(priv, |
326 | "Cannot update the power, chain noise " | |
a71c8f62 WT |
327 | "calibration running: %d\n", |
328 | priv->chain_noise_data.state); | |
e312c24c | 329 | memcpy(&priv->power_data.sleep_cmd, &cmd, sizeof(cmd)); |
3a780d25 WYG |
330 | } else |
331 | IWL_ERR(priv, "set power fail, ret = %d", ret); | |
5da4b55f MA |
332 | } |
333 | ||
334 | return ret; | |
335 | } | |
336 | EXPORT_SYMBOL(iwl_power_update_mode); | |
337 | ||
46f9381a WYG |
338 | bool iwl_ht_enabled(struct iwl_priv *priv) |
339 | { | |
3ad3b92a | 340 | struct iwl_tt_mgmt *tt = &priv->thermal_throttle; |
46f9381a WYG |
341 | struct iwl_tt_restriction *restriction; |
342 | ||
3ad3b92a | 343 | if (!priv->thermal_throttle.advanced_tt) |
46f9381a WYG |
344 | return true; |
345 | restriction = tt->restriction + tt->state; | |
346 | return restriction->is_ht; | |
347 | } | |
348 | EXPORT_SYMBOL(iwl_ht_enabled); | |
349 | ||
3ad3b92a | 350 | enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv) |
46f9381a | 351 | { |
3ad3b92a | 352 | struct iwl_tt_mgmt *tt = &priv->thermal_throttle; |
46f9381a WYG |
353 | struct iwl_tt_restriction *restriction; |
354 | ||
3ad3b92a JB |
355 | if (!priv->thermal_throttle.advanced_tt) |
356 | return IWL_ANT_OK_MULTI; | |
46f9381a WYG |
357 | restriction = tt->restriction + tt->state; |
358 | return restriction->tx_stream; | |
359 | } | |
360 | EXPORT_SYMBOL(iwl_tx_ant_restriction); | |
361 | ||
3ad3b92a | 362 | enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv) |
46f9381a | 363 | { |
3ad3b92a | 364 | struct iwl_tt_mgmt *tt = &priv->thermal_throttle; |
46f9381a WYG |
365 | struct iwl_tt_restriction *restriction; |
366 | ||
3ad3b92a JB |
367 | if (!priv->thermal_throttle.advanced_tt) |
368 | return IWL_ANT_OK_MULTI; | |
46f9381a WYG |
369 | restriction = tt->restriction + tt->state; |
370 | return restriction->rx_stream; | |
371 | } | |
46f9381a | 372 | |
39b73fb1 WYG |
373 | #define CT_KILL_EXIT_DURATION (5) /* 5 seconds duration */ |
374 | ||
375 | /* | |
376 | * toggle the bit to wake up uCode and check the temperature | |
377 | * if the temperature is below CT, uCode will stay awake and send card | |
378 | * state notification with CT_KILL bit clear to inform Thermal Throttling | |
379 | * Management to change state. Otherwise, uCode will go back to sleep | |
380 | * without doing anything, driver should continue the 5 seconds timer | |
381 | * to wake up uCode for temperature check until temperature drop below CT | |
382 | */ | |
383 | static void iwl_tt_check_exit_ct_kill(unsigned long data) | |
384 | { | |
385 | struct iwl_priv *priv = (struct iwl_priv *)data; | |
3ad3b92a | 386 | struct iwl_tt_mgmt *tt = &priv->thermal_throttle; |
39b73fb1 WYG |
387 | unsigned long flags; |
388 | ||
389 | if (test_bit(STATUS_EXIT_PENDING, &priv->status)) | |
390 | return; | |
391 | ||
392 | if (tt->state == IWL_TI_CT_KILL) { | |
3ad3b92a | 393 | if (priv->thermal_throttle.ct_kill_toggle) { |
39b73fb1 WYG |
394 | iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, |
395 | CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); | |
3ad3b92a | 396 | priv->thermal_throttle.ct_kill_toggle = false; |
39b73fb1 WYG |
397 | } else { |
398 | iwl_write32(priv, CSR_UCODE_DRV_GP1_SET, | |
399 | CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); | |
3ad3b92a | 400 | priv->thermal_throttle.ct_kill_toggle = true; |
39b73fb1 WYG |
401 | } |
402 | iwl_read32(priv, CSR_UCODE_DRV_GP1); | |
403 | spin_lock_irqsave(&priv->reg_lock, flags); | |
404 | if (!iwl_grab_nic_access(priv)) | |
405 | iwl_release_nic_access(priv); | |
406 | spin_unlock_irqrestore(&priv->reg_lock, flags); | |
407 | ||
408 | /* Reschedule the ct_kill timer to occur in | |
409 | * CT_KILL_EXIT_DURATION seconds to ensure we get a | |
410 | * thermal update */ | |
3ad3b92a | 411 | mod_timer(&priv->thermal_throttle.ct_kill_exit_tm, jiffies + |
39b73fb1 WYG |
412 | CT_KILL_EXIT_DURATION * HZ); |
413 | } | |
414 | } | |
415 | ||
416 | static void iwl_perform_ct_kill_task(struct iwl_priv *priv, | |
417 | bool stop) | |
418 | { | |
419 | if (stop) { | |
420 | IWL_DEBUG_POWER(priv, "Stop all queues\n"); | |
421 | if (priv->mac80211_registered) | |
422 | ieee80211_stop_queues(priv->hw); | |
423 | IWL_DEBUG_POWER(priv, | |
424 | "Schedule 5 seconds CT_KILL Timer\n"); | |
3ad3b92a | 425 | mod_timer(&priv->thermal_throttle.ct_kill_exit_tm, jiffies + |
39b73fb1 WYG |
426 | CT_KILL_EXIT_DURATION * HZ); |
427 | } else { | |
428 | IWL_DEBUG_POWER(priv, "Wake all queues\n"); | |
429 | if (priv->mac80211_registered) | |
430 | ieee80211_wake_queues(priv->hw); | |
431 | } | |
432 | } | |
433 | ||
434 | #define IWL_MINIMAL_POWER_THRESHOLD (CT_KILL_THRESHOLD_LEGACY) | |
435 | #define IWL_REDUCED_PERFORMANCE_THRESHOLD_2 (100) | |
436 | #define IWL_REDUCED_PERFORMANCE_THRESHOLD_1 (90) | |
437 | ||
438 | /* | |
439 | * Legacy thermal throttling | |
440 | * 1) Avoid NIC destruction due to high temperatures | |
441 | * Chip will identify dangerously high temperatures that can | |
442 | * harm the device and will power down | |
443 | * 2) Avoid the NIC power down due to high temperature | |
444 | * Throttle early enough to lower the power consumption before | |
445 | * drastic steps are needed | |
446 | */ | |
447 | static void iwl_legacy_tt_handler(struct iwl_priv *priv, s32 temp) | |
448 | { | |
3ad3b92a | 449 | struct iwl_tt_mgmt *tt = &priv->thermal_throttle; |
ee9f2989 | 450 | enum iwl_tt_state old_state; |
39b73fb1 WYG |
451 | |
452 | #ifdef CONFIG_IWLWIFI_DEBUG | |
453 | if ((tt->tt_previous_temp) && | |
454 | (temp > tt->tt_previous_temp) && | |
455 | ((temp - tt->tt_previous_temp) > | |
456 | IWL_TT_INCREASE_MARGIN)) { | |
457 | IWL_DEBUG_POWER(priv, | |
458 | "Temperature increase %d degree Celsius\n", | |
459 | (temp - tt->tt_previous_temp)); | |
460 | } | |
461 | #endif | |
ee9f2989 | 462 | old_state = tt->state; |
39b73fb1 WYG |
463 | /* in Celsius */ |
464 | if (temp >= IWL_MINIMAL_POWER_THRESHOLD) | |
ee9f2989 | 465 | tt->state = IWL_TI_CT_KILL; |
39b73fb1 | 466 | else if (temp >= IWL_REDUCED_PERFORMANCE_THRESHOLD_2) |
ee9f2989 | 467 | tt->state = IWL_TI_2; |
39b73fb1 | 468 | else if (temp >= IWL_REDUCED_PERFORMANCE_THRESHOLD_1) |
ee9f2989 | 469 | tt->state = IWL_TI_1; |
39b73fb1 | 470 | else |
ee9f2989 | 471 | tt->state = IWL_TI_0; |
39b73fb1 WYG |
472 | |
473 | #ifdef CONFIG_IWLWIFI_DEBUG | |
474 | tt->tt_previous_temp = temp; | |
475 | #endif | |
ee9f2989 | 476 | if (tt->state != old_state) { |
ee9f2989 | 477 | switch (tt->state) { |
39b73fb1 | 478 | case IWL_TI_0: |
e312c24c JB |
479 | /* |
480 | * When the system is ready to go back to IWL_TI_0 | |
481 | * we only have to call iwl_power_update_mode() to | |
482 | * do so. | |
39b73fb1 | 483 | */ |
39b73fb1 WYG |
484 | break; |
485 | case IWL_TI_1: | |
486 | tt->tt_power_mode = IWL_POWER_INDEX_3; | |
487 | break; | |
488 | case IWL_TI_2: | |
489 | tt->tt_power_mode = IWL_POWER_INDEX_4; | |
490 | break; | |
491 | default: | |
492 | tt->tt_power_mode = IWL_POWER_INDEX_5; | |
493 | break; | |
494 | } | |
a28027cd | 495 | mutex_lock(&priv->mutex); |
39b73fb1 WYG |
496 | if (iwl_power_update_mode(priv, true)) { |
497 | /* TT state not updated | |
498 | * try again during next temperature read | |
499 | */ | |
ee9f2989 | 500 | tt->state = old_state; |
39b73fb1 WYG |
501 | IWL_ERR(priv, "Cannot update power mode, " |
502 | "TT state not updated\n"); | |
503 | } else { | |
ee9f2989 | 504 | if (tt->state == IWL_TI_CT_KILL) |
39b73fb1 | 505 | iwl_perform_ct_kill_task(priv, true); |
ee9f2989 WYG |
506 | else if (old_state == IWL_TI_CT_KILL && |
507 | tt->state != IWL_TI_CT_KILL) | |
39b73fb1 | 508 | iwl_perform_ct_kill_task(priv, false); |
39b73fb1 WYG |
509 | IWL_DEBUG_POWER(priv, "Temperature state changed %u\n", |
510 | tt->state); | |
511 | IWL_DEBUG_POWER(priv, "Power Index change to %u\n", | |
512 | tt->tt_power_mode); | |
513 | } | |
a28027cd | 514 | mutex_unlock(&priv->mutex); |
39b73fb1 WYG |
515 | } |
516 | } | |
517 | ||
46f9381a WYG |
518 | /* |
519 | * Advance thermal throttling | |
520 | * 1) Avoid NIC destruction due to high temperatures | |
521 | * Chip will identify dangerously high temperatures that can | |
522 | * harm the device and will power down | |
523 | * 2) Avoid the NIC power down due to high temperature | |
524 | * Throttle early enough to lower the power consumption before | |
525 | * drastic steps are needed | |
526 | * Actions include relaxing the power down sleep thresholds and | |
527 | * decreasing the number of TX streams | |
528 | * 3) Avoid throughput performance impact as much as possible | |
529 | * | |
530 | *============================================================================= | |
531 | * Condition Nxt State Condition Nxt State Condition Nxt State | |
532 | *----------------------------------------------------------------------------- | |
533 | * IWL_TI_0 T >= 115 CT_KILL 115>T>=105 TI_1 N/A N/A | |
534 | * IWL_TI_1 T >= 115 CT_KILL 115>T>=110 TI_2 T<=95 TI_0 | |
535 | * IWL_TI_2 T >= 115 CT_KILL T<=100 TI_1 | |
536 | * IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0 | |
537 | *============================================================================= | |
538 | */ | |
539 | static void iwl_advance_tt_handler(struct iwl_priv *priv, s32 temp) | |
540 | { | |
3ad3b92a | 541 | struct iwl_tt_mgmt *tt = &priv->thermal_throttle; |
46f9381a WYG |
542 | int i; |
543 | bool changed = false; | |
544 | enum iwl_tt_state old_state; | |
545 | struct iwl_tt_trans *transaction; | |
546 | ||
547 | old_state = tt->state; | |
548 | for (i = 0; i < IWL_TI_STATE_MAX - 1; i++) { | |
549 | /* based on the current TT state, | |
550 | * find the curresponding transaction table | |
551 | * each table has (IWL_TI_STATE_MAX - 1) entries | |
552 | * tt->transaction + ((old_state * (IWL_TI_STATE_MAX - 1)) | |
553 | * will advance to the correct table. | |
554 | * then based on the current temperature | |
555 | * find the next state need to transaction to | |
556 | * go through all the possible (IWL_TI_STATE_MAX - 1) entries | |
557 | * in the current table to see if transaction is needed | |
558 | */ | |
559 | transaction = tt->transaction + | |
560 | ((old_state * (IWL_TI_STATE_MAX - 1)) + i); | |
561 | if (temp >= transaction->tt_low && | |
562 | temp <= transaction->tt_high) { | |
563 | #ifdef CONFIG_IWLWIFI_DEBUG | |
564 | if ((tt->tt_previous_temp) && | |
565 | (temp > tt->tt_previous_temp) && | |
566 | ((temp - tt->tt_previous_temp) > | |
567 | IWL_TT_INCREASE_MARGIN)) { | |
568 | IWL_DEBUG_POWER(priv, | |
569 | "Temperature increase %d " | |
570 | "degree Celsius\n", | |
571 | (temp - tt->tt_previous_temp)); | |
572 | } | |
573 | tt->tt_previous_temp = temp; | |
574 | #endif | |
575 | if (old_state != | |
576 | transaction->next_state) { | |
577 | changed = true; | |
578 | tt->state = | |
579 | transaction->next_state; | |
580 | } | |
581 | break; | |
582 | } | |
583 | } | |
584 | if (changed) { | |
585 | struct iwl_rxon_cmd *rxon = &priv->staging_rxon; | |
46f9381a WYG |
586 | |
587 | if (tt->state >= IWL_TI_1) { | |
46f9381a WYG |
588 | /* force PI = IWL_POWER_INDEX_5 in the case of TI > 0 */ |
589 | tt->tt_power_mode = IWL_POWER_INDEX_5; | |
590 | if (!iwl_ht_enabled(priv)) | |
591 | /* disable HT */ | |
592 | rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK | | |
593 | RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK | | |
7aafef1c | 594 | RXON_FLG_HT40_PROT_MSK | |
46f9381a WYG |
595 | RXON_FLG_HT_PROT_MSK); |
596 | else { | |
597 | /* check HT capability and set | |
598 | * according to the system HT capability | |
599 | * in case get disabled before */ | |
600 | iwl_set_rxon_ht(priv, &priv->current_ht_config); | |
601 | } | |
602 | ||
603 | } else { | |
e312c24c JB |
604 | /* |
605 | * restore system power setting -- it will be | |
606 | * recalculated automatically. | |
46f9381a | 607 | */ |
e312c24c | 608 | |
46f9381a WYG |
609 | /* check HT capability and set |
610 | * according to the system HT capability | |
611 | * in case get disabled before */ | |
612 | iwl_set_rxon_ht(priv, &priv->current_ht_config); | |
613 | } | |
a28027cd | 614 | mutex_lock(&priv->mutex); |
46f9381a WYG |
615 | if (iwl_power_update_mode(priv, true)) { |
616 | /* TT state not updated | |
617 | * try again during next temperature read | |
618 | */ | |
619 | IWL_ERR(priv, "Cannot update power mode, " | |
620 | "TT state not updated\n"); | |
621 | tt->state = old_state; | |
622 | } else { | |
623 | IWL_DEBUG_POWER(priv, | |
624 | "Thermal Throttling to new state: %u\n", | |
625 | tt->state); | |
626 | if (old_state != IWL_TI_CT_KILL && | |
627 | tt->state == IWL_TI_CT_KILL) { | |
628 | IWL_DEBUG_POWER(priv, "Enter IWL_TI_CT_KILL\n"); | |
629 | iwl_perform_ct_kill_task(priv, true); | |
630 | ||
631 | } else if (old_state == IWL_TI_CT_KILL && | |
632 | tt->state != IWL_TI_CT_KILL) { | |
633 | IWL_DEBUG_POWER(priv, "Exit IWL_TI_CT_KILL\n"); | |
634 | iwl_perform_ct_kill_task(priv, false); | |
635 | } | |
636 | } | |
a28027cd | 637 | mutex_unlock(&priv->mutex); |
46f9381a WYG |
638 | } |
639 | } | |
640 | ||
39b73fb1 WYG |
641 | /* Card State Notification indicated reach critical temperature |
642 | * if PSP not enable, no Thermal Throttling function will be performed | |
643 | * just set the GP1 bit to acknowledge the event | |
644 | * otherwise, go into IWL_TI_CT_KILL state | |
645 | * since Card State Notification will not provide any temperature reading | |
46f9381a | 646 | * for Legacy mode |
39b73fb1 | 647 | * so just pass the CT_KILL temperature to iwl_legacy_tt_handler() |
46f9381a WYG |
648 | * for advance mode |
649 | * pass CT_KILL_THRESHOLD+1 to make sure move into IWL_TI_CT_KILL state | |
39b73fb1 | 650 | */ |
a28027cd | 651 | static void iwl_bg_ct_enter(struct work_struct *work) |
39b73fb1 | 652 | { |
a28027cd | 653 | struct iwl_priv *priv = container_of(work, struct iwl_priv, ct_enter); |
3ad3b92a | 654 | struct iwl_tt_mgmt *tt = &priv->thermal_throttle; |
39b73fb1 WYG |
655 | |
656 | if (test_bit(STATUS_EXIT_PENDING, &priv->status)) | |
657 | return; | |
658 | ||
a28027cd WYG |
659 | if (!iwl_is_ready(priv)) |
660 | return; | |
661 | ||
39b73fb1 WYG |
662 | if (tt->state != IWL_TI_CT_KILL) { |
663 | IWL_ERR(priv, "Device reached critical temperature " | |
664 | "- ucode going to sleep!\n"); | |
3ad3b92a | 665 | if (!priv->thermal_throttle.advanced_tt) |
46f9381a WYG |
666 | iwl_legacy_tt_handler(priv, |
667 | IWL_MINIMAL_POWER_THRESHOLD); | |
668 | else | |
669 | iwl_advance_tt_handler(priv, | |
670 | CT_KILL_THRESHOLD + 1); | |
39b73fb1 WYG |
671 | } |
672 | } | |
39b73fb1 WYG |
673 | |
674 | /* Card State Notification indicated out of critical temperature | |
675 | * since Card State Notification will not provide any temperature reading | |
676 | * so pass the IWL_REDUCED_PERFORMANCE_THRESHOLD_2 temperature | |
677 | * to iwl_legacy_tt_handler() to get out of IWL_CT_KILL state | |
678 | */ | |
a28027cd | 679 | static void iwl_bg_ct_exit(struct work_struct *work) |
39b73fb1 | 680 | { |
a28027cd | 681 | struct iwl_priv *priv = container_of(work, struct iwl_priv, ct_exit); |
3ad3b92a | 682 | struct iwl_tt_mgmt *tt = &priv->thermal_throttle; |
39b73fb1 WYG |
683 | |
684 | if (test_bit(STATUS_EXIT_PENDING, &priv->status)) | |
685 | return; | |
686 | ||
a28027cd WYG |
687 | if (!iwl_is_ready(priv)) |
688 | return; | |
689 | ||
39b73fb1 | 690 | /* stop ct_kill_exit_tm timer */ |
3ad3b92a | 691 | del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm); |
39b73fb1 WYG |
692 | |
693 | if (tt->state == IWL_TI_CT_KILL) { | |
694 | IWL_ERR(priv, | |
695 | "Device temperature below critical" | |
696 | "- ucode awake!\n"); | |
3ad3b92a | 697 | if (!priv->thermal_throttle.advanced_tt) |
46f9381a WYG |
698 | iwl_legacy_tt_handler(priv, |
699 | IWL_REDUCED_PERFORMANCE_THRESHOLD_2); | |
700 | else | |
701 | iwl_advance_tt_handler(priv, CT_KILL_EXIT_THRESHOLD); | |
39b73fb1 WYG |
702 | } |
703 | } | |
a28027cd WYG |
704 | |
705 | void iwl_tt_enter_ct_kill(struct iwl_priv *priv) | |
706 | { | |
707 | if (test_bit(STATUS_EXIT_PENDING, &priv->status)) | |
708 | return; | |
709 | ||
710 | IWL_DEBUG_POWER(priv, "Queueing critical temperature enter.\n"); | |
711 | queue_work(priv->workqueue, &priv->ct_enter); | |
712 | } | |
713 | EXPORT_SYMBOL(iwl_tt_enter_ct_kill); | |
714 | ||
715 | void iwl_tt_exit_ct_kill(struct iwl_priv *priv) | |
716 | { | |
717 | if (test_bit(STATUS_EXIT_PENDING, &priv->status)) | |
718 | return; | |
719 | ||
720 | IWL_DEBUG_POWER(priv, "Queueing critical temperature exit.\n"); | |
721 | queue_work(priv->workqueue, &priv->ct_exit); | |
722 | } | |
39b73fb1 WYG |
723 | EXPORT_SYMBOL(iwl_tt_exit_ct_kill); |
724 | ||
a28027cd | 725 | static void iwl_bg_tt_work(struct work_struct *work) |
39b73fb1 | 726 | { |
a28027cd | 727 | struct iwl_priv *priv = container_of(work, struct iwl_priv, tt_work); |
39b73fb1 WYG |
728 | s32 temp = priv->temperature; /* degrees CELSIUS except 4965 */ |
729 | ||
730 | if (test_bit(STATUS_EXIT_PENDING, &priv->status)) | |
731 | return; | |
732 | ||
733 | if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_4965) | |
734 | temp = KELVIN_TO_CELSIUS(priv->temperature); | |
735 | ||
3ad3b92a | 736 | if (!priv->thermal_throttle.advanced_tt) |
46f9381a WYG |
737 | iwl_legacy_tt_handler(priv, temp); |
738 | else | |
739 | iwl_advance_tt_handler(priv, temp); | |
39b73fb1 | 740 | } |
a28027cd WYG |
741 | |
742 | void iwl_tt_handler(struct iwl_priv *priv) | |
743 | { | |
744 | if (test_bit(STATUS_EXIT_PENDING, &priv->status)) | |
745 | return; | |
746 | ||
747 | IWL_DEBUG_POWER(priv, "Queueing thermal throttling work.\n"); | |
748 | queue_work(priv->workqueue, &priv->tt_work); | |
749 | } | |
39b73fb1 WYG |
750 | EXPORT_SYMBOL(iwl_tt_handler); |
751 | ||
752 | /* Thermal throttling initialization | |
46f9381a WYG |
753 | * For advance thermal throttling: |
754 | * Initialize Thermal Index and temperature threshold table | |
755 | * Initialize thermal throttling restriction table | |
39b73fb1 WYG |
756 | */ |
757 | void iwl_tt_initialize(struct iwl_priv *priv) | |
758 | { | |
3ad3b92a | 759 | struct iwl_tt_mgmt *tt = &priv->thermal_throttle; |
46f9381a WYG |
760 | int size = sizeof(struct iwl_tt_trans) * (IWL_TI_STATE_MAX - 1); |
761 | struct iwl_tt_trans *transaction; | |
39b73fb1 WYG |
762 | |
763 | IWL_DEBUG_POWER(priv, "Initialize Thermal Throttling \n"); | |
764 | ||
765 | memset(tt, 0, sizeof(struct iwl_tt_mgmt)); | |
766 | ||
767 | tt->state = IWL_TI_0; | |
3ad3b92a JB |
768 | init_timer(&priv->thermal_throttle.ct_kill_exit_tm); |
769 | priv->thermal_throttle.ct_kill_exit_tm.data = (unsigned long)priv; | |
770 | priv->thermal_throttle.ct_kill_exit_tm.function = iwl_tt_check_exit_ct_kill; | |
a28027cd WYG |
771 | |
772 | /* setup deferred ct kill work */ | |
773 | INIT_WORK(&priv->tt_work, iwl_bg_tt_work); | |
774 | INIT_WORK(&priv->ct_enter, iwl_bg_ct_enter); | |
775 | INIT_WORK(&priv->ct_exit, iwl_bg_ct_exit); | |
776 | ||
46f9381a WYG |
777 | switch (priv->hw_rev & CSR_HW_REV_TYPE_MSK) { |
778 | case CSR_HW_REV_TYPE_6x00: | |
779 | case CSR_HW_REV_TYPE_6x50: | |
780 | IWL_DEBUG_POWER(priv, "Advanced Thermal Throttling\n"); | |
781 | tt->restriction = kzalloc(sizeof(struct iwl_tt_restriction) * | |
782 | IWL_TI_STATE_MAX, GFP_KERNEL); | |
783 | tt->transaction = kzalloc(sizeof(struct iwl_tt_trans) * | |
784 | IWL_TI_STATE_MAX * (IWL_TI_STATE_MAX - 1), | |
785 | GFP_KERNEL); | |
786 | if (!tt->restriction || !tt->transaction) { | |
787 | IWL_ERR(priv, "Fallback to Legacy Throttling\n"); | |
3ad3b92a | 788 | priv->thermal_throttle.advanced_tt = false; |
46f9381a WYG |
789 | kfree(tt->restriction); |
790 | tt->restriction = NULL; | |
791 | kfree(tt->transaction); | |
792 | tt->transaction = NULL; | |
793 | } else { | |
794 | transaction = tt->transaction + | |
795 | (IWL_TI_0 * (IWL_TI_STATE_MAX - 1)); | |
796 | memcpy(transaction, &tt_range_0[0], size); | |
797 | transaction = tt->transaction + | |
798 | (IWL_TI_1 * (IWL_TI_STATE_MAX - 1)); | |
799 | memcpy(transaction, &tt_range_1[0], size); | |
800 | transaction = tt->transaction + | |
801 | (IWL_TI_2 * (IWL_TI_STATE_MAX - 1)); | |
802 | memcpy(transaction, &tt_range_2[0], size); | |
803 | transaction = tt->transaction + | |
804 | (IWL_TI_CT_KILL * (IWL_TI_STATE_MAX - 1)); | |
805 | memcpy(transaction, &tt_range_3[0], size); | |
806 | size = sizeof(struct iwl_tt_restriction) * | |
807 | IWL_TI_STATE_MAX; | |
808 | memcpy(tt->restriction, | |
809 | &restriction_range[0], size); | |
3ad3b92a | 810 | priv->thermal_throttle.advanced_tt = true; |
46f9381a WYG |
811 | } |
812 | break; | |
813 | default: | |
814 | IWL_DEBUG_POWER(priv, "Legacy Thermal Throttling\n"); | |
3ad3b92a | 815 | priv->thermal_throttle.advanced_tt = false; |
46f9381a WYG |
816 | break; |
817 | } | |
39b73fb1 WYG |
818 | } |
819 | EXPORT_SYMBOL(iwl_tt_initialize); | |
820 | ||
821 | /* cleanup thermal throttling management related memory and timer */ | |
822 | void iwl_tt_exit(struct iwl_priv *priv) | |
823 | { | |
3ad3b92a | 824 | struct iwl_tt_mgmt *tt = &priv->thermal_throttle; |
46f9381a | 825 | |
39b73fb1 | 826 | /* stop ct_kill_exit_tm timer if activated */ |
3ad3b92a | 827 | del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm); |
a28027cd WYG |
828 | cancel_work_sync(&priv->tt_work); |
829 | cancel_work_sync(&priv->ct_enter); | |
830 | cancel_work_sync(&priv->ct_exit); | |
46f9381a | 831 | |
3ad3b92a | 832 | if (priv->thermal_throttle.advanced_tt) { |
46f9381a WYG |
833 | /* free advance thermal throttling memory */ |
834 | kfree(tt->restriction); | |
835 | tt->restriction = NULL; | |
836 | kfree(tt->transaction); | |
837 | tt->transaction = NULL; | |
838 | } | |
39b73fb1 WYG |
839 | } |
840 | EXPORT_SYMBOL(iwl_tt_exit); | |
841 | ||
a96a27f9 | 842 | /* initialize to default */ |
5da4b55f MA |
843 | void iwl_power_initialize(struct iwl_priv *priv) |
844 | { | |
e312c24c JB |
845 | u16 lctl = iwl_pcie_link_ctl(priv); |
846 | ||
847 | priv->power_data.pci_pm = !(lctl & PCI_CFG_LINK_CTRL_VAL_L0S_EN); | |
848 | ||
849 | priv->power_data.debug_sleep_level_override = -1; | |
850 | ||
851 | memset(&priv->power_data.sleep_cmd, 0, | |
852 | sizeof(priv->power_data.sleep_cmd)); | |
5da4b55f MA |
853 | } |
854 | EXPORT_SYMBOL(iwl_power_initialize); |