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