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