diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-power.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-power.c | 444 |
1 files changed, 444 insertions, 0 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-power.c b/drivers/net/wireless/iwlwifi/iwl-power.c new file mode 100644 index 00000000000..cd64df05f9e --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-power.c | |||
@@ -0,0 +1,444 @@ | |||
1 | /****************************************************************************** | ||
2 | * | ||
3 | * Copyright(c) 2007 - 2011 Intel Corporation. All rights reserved. | ||
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: | ||
25 | * Intel Linux Wireless <ilw@linux.intel.com> | ||
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> | ||
32 | #include <linux/slab.h> | ||
33 | #include <linux/init.h> | ||
34 | |||
35 | #include <net/mac80211.h> | ||
36 | |||
37 | #include "iwl-eeprom.h" | ||
38 | #include "iwl-dev.h" | ||
39 | #include "iwl-agn.h" | ||
40 | #include "iwl-core.h" | ||
41 | #include "iwl-io.h" | ||
42 | #include "iwl-commands.h" | ||
43 | #include "iwl-debug.h" | ||
44 | #include "iwl-power.h" | ||
45 | #include "iwl-trans.h" | ||
46 | |||
47 | /* | ||
48 | * Setting power level allows the card to go to sleep when not busy. | ||
49 | * | ||
50 | * We calculate a sleep command based on the required latency, which | ||
51 | * we get from mac80211. In order to handle thermal throttling, we can | ||
52 | * also use pre-defined power levels. | ||
53 | */ | ||
54 | |||
55 | /* | ||
56 | * This defines the old power levels. They are still used by default | ||
57 | * (level 1) and for thermal throttle (levels 3 through 5) | ||
58 | */ | ||
59 | |||
60 | struct iwl_power_vec_entry { | ||
61 | struct iwl_powertable_cmd cmd; | ||
62 | u8 no_dtim; /* number of skip dtim */ | ||
63 | }; | ||
64 | |||
65 | #define IWL_DTIM_RANGE_0_MAX 2 | ||
66 | #define IWL_DTIM_RANGE_1_MAX 10 | ||
67 | |||
68 | #define NOSLP cpu_to_le16(0), 0, 0 | ||
69 | #define SLP IWL_POWER_DRIVER_ALLOW_SLEEP_MSK, 0, 0 | ||
70 | #define ASLP (IWL_POWER_POWER_SAVE_ENA_MSK | \ | ||
71 | IWL_POWER_POWER_MANAGEMENT_ENA_MSK | \ | ||
72 | IWL_POWER_ADVANCE_PM_ENA_MSK) | ||
73 | #define ASLP_TOUT(T) cpu_to_le32(T) | ||
74 | #define TU_TO_USEC 1024 | ||
75 | #define SLP_TOUT(T) cpu_to_le32((T) * TU_TO_USEC) | ||
76 | #define SLP_VEC(X0, X1, X2, X3, X4) {cpu_to_le32(X0), \ | ||
77 | cpu_to_le32(X1), \ | ||
78 | cpu_to_le32(X2), \ | ||
79 | cpu_to_le32(X3), \ | ||
80 | cpu_to_le32(X4)} | ||
81 | /* default power management (not Tx power) table values */ | ||
82 | /* for DTIM period 0 through IWL_DTIM_RANGE_0_MAX */ | ||
83 | /* DTIM 0 - 2 */ | ||
84 | static const struct iwl_power_vec_entry range_0[IWL_POWER_NUM] = { | ||
85 | {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 1, 2, 2, 0xFF)}, 0}, | ||
86 | {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 2, 2, 0xFF)}, 0}, | ||
87 | {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 2, 2, 2, 0xFF)}, 0}, | ||
88 | {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 2, 4, 4, 0xFF)}, 1}, | ||
89 | {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 2, 4, 6, 0xFF)}, 2} | ||
90 | }; | ||
91 | |||
92 | |||
93 | /* for DTIM period IWL_DTIM_RANGE_0_MAX + 1 through IWL_DTIM_RANGE_1_MAX */ | ||
94 | /* DTIM 3 - 10 */ | ||
95 | static const struct iwl_power_vec_entry range_1[IWL_POWER_NUM] = { | ||
96 | {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 4)}, 0}, | ||
97 | {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 3, 4, 7)}, 0}, | ||
98 | {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 4, 6, 7, 9)}, 0}, | ||
99 | {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 4, 6, 9, 10)}, 1}, | ||
100 | {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 4, 6, 10, 10)}, 2} | ||
101 | }; | ||
102 | |||
103 | /* for DTIM period > IWL_DTIM_RANGE_1_MAX */ | ||
104 | /* DTIM 11 - */ | ||
105 | static const struct iwl_power_vec_entry range_2[IWL_POWER_NUM] = { | ||
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 | |||
113 | /* advance power management */ | ||
114 | /* DTIM 0 - 2 */ | ||
115 | static const struct iwl_power_vec_entry apm_range_0[IWL_POWER_NUM] = { | ||
116 | {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), | ||
117 | SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, | ||
118 | {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), | ||
119 | SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, | ||
120 | {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), | ||
121 | SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, | ||
122 | {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), | ||
123 | SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, | ||
124 | {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), | ||
125 | SLP_VEC(1, 2, 6, 8, 0xFF), ASLP_TOUT(2)}, 2} | ||
126 | }; | ||
127 | |||
128 | |||
129 | /* for DTIM period IWL_DTIM_RANGE_0_MAX + 1 through IWL_DTIM_RANGE_1_MAX */ | ||
130 | /* DTIM 3 - 10 */ | ||
131 | static const struct iwl_power_vec_entry apm_range_1[IWL_POWER_NUM] = { | ||
132 | {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), | ||
133 | SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, | ||
134 | {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), | ||
135 | SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, | ||
136 | {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), | ||
137 | SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, | ||
138 | {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), | ||
139 | SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, | ||
140 | {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), | ||
141 | SLP_VEC(1, 2, 6, 8, 0xFF), 0}, 2} | ||
142 | }; | ||
143 | |||
144 | /* for DTIM period > IWL_DTIM_RANGE_1_MAX */ | ||
145 | /* DTIM 11 - */ | ||
146 | static const struct iwl_power_vec_entry apm_range_2[IWL_POWER_NUM] = { | ||
147 | {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), | ||
148 | SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, | ||
149 | {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), | ||
150 | SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, | ||
151 | {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), | ||
152 | SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, | ||
153 | {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), | ||
154 | SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, | ||
155 | {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), | ||
156 | SLP_VEC(1, 2, 6, 8, 0xFF), ASLP_TOUT(2)}, 2} | ||
157 | }; | ||
158 | |||
159 | static void iwl_static_sleep_cmd(struct iwl_priv *priv, | ||
160 | struct iwl_powertable_cmd *cmd, | ||
161 | enum iwl_power_level lvl, int period) | ||
162 | { | ||
163 | const struct iwl_power_vec_entry *table; | ||
164 | int max_sleep[IWL_POWER_VEC_SIZE] = { 0 }; | ||
165 | int i; | ||
166 | u8 skip; | ||
167 | u32 slp_itrvl; | ||
168 | |||
169 | if (priv->cfg->adv_pm) { | ||
170 | table = apm_range_2; | ||
171 | if (period <= IWL_DTIM_RANGE_1_MAX) | ||
172 | table = apm_range_1; | ||
173 | if (period <= IWL_DTIM_RANGE_0_MAX) | ||
174 | table = apm_range_0; | ||
175 | } else { | ||
176 | table = range_2; | ||
177 | if (period <= IWL_DTIM_RANGE_1_MAX) | ||
178 | table = range_1; | ||
179 | if (period <= IWL_DTIM_RANGE_0_MAX) | ||
180 | table = range_0; | ||
181 | } | ||
182 | |||
183 | if (WARN_ON(lvl < 0 || lvl >= IWL_POWER_NUM)) | ||
184 | memset(cmd, 0, sizeof(*cmd)); | ||
185 | else | ||
186 | *cmd = table[lvl].cmd; | ||
187 | |||
188 | if (period == 0) { | ||
189 | skip = 0; | ||
190 | period = 1; | ||
191 | for (i = 0; i < IWL_POWER_VEC_SIZE; i++) | ||
192 | max_sleep[i] = 1; | ||
193 | |||
194 | } else { | ||
195 | skip = table[lvl].no_dtim; | ||
196 | for (i = 0; i < IWL_POWER_VEC_SIZE; i++) | ||
197 | max_sleep[i] = le32_to_cpu(cmd->sleep_interval[i]); | ||
198 | max_sleep[IWL_POWER_VEC_SIZE - 1] = skip + 1; | ||
199 | } | ||
200 | |||
201 | slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]); | ||
202 | /* figure out the listen interval based on dtim period and skip */ | ||
203 | if (slp_itrvl == 0xFF) | ||
204 | cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] = | ||
205 | cpu_to_le32(period * (skip + 1)); | ||
206 | |||
207 | slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]); | ||
208 | if (slp_itrvl > period) | ||
209 | cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] = | ||
210 | cpu_to_le32((slp_itrvl / period) * period); | ||
211 | |||
212 | if (skip) | ||
213 | cmd->flags |= IWL_POWER_SLEEP_OVER_DTIM_MSK; | ||
214 | else | ||
215 | cmd->flags &= ~IWL_POWER_SLEEP_OVER_DTIM_MSK; | ||
216 | |||
217 | if (priv->cfg->base_params->shadow_reg_enable) | ||
218 | cmd->flags |= IWL_POWER_SHADOW_REG_ENA; | ||
219 | else | ||
220 | cmd->flags &= ~IWL_POWER_SHADOW_REG_ENA; | ||
221 | |||
222 | if (iwl_advanced_bt_coexist(priv)) { | ||
223 | if (!priv->cfg->bt_params->bt_sco_disable) | ||
224 | cmd->flags |= IWL_POWER_BT_SCO_ENA; | ||
225 | else | ||
226 | cmd->flags &= ~IWL_POWER_BT_SCO_ENA; | ||
227 | } | ||
228 | |||
229 | |||
230 | slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]); | ||
231 | if (slp_itrvl > IWL_CONN_MAX_LISTEN_INTERVAL) | ||
232 | cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] = | ||
233 | cpu_to_le32(IWL_CONN_MAX_LISTEN_INTERVAL); | ||
234 | |||
235 | /* enforce max sleep interval */ | ||
236 | for (i = IWL_POWER_VEC_SIZE - 1; i >= 0 ; i--) { | ||
237 | if (le32_to_cpu(cmd->sleep_interval[i]) > | ||
238 | (max_sleep[i] * period)) | ||
239 | cmd->sleep_interval[i] = | ||
240 | cpu_to_le32(max_sleep[i] * period); | ||
241 | if (i != (IWL_POWER_VEC_SIZE - 1)) { | ||
242 | if (le32_to_cpu(cmd->sleep_interval[i]) > | ||
243 | le32_to_cpu(cmd->sleep_interval[i+1])) | ||
244 | cmd->sleep_interval[i] = | ||
245 | cmd->sleep_interval[i+1]; | ||
246 | } | ||
247 | } | ||
248 | |||
249 | if (priv->power_data.bus_pm) | ||
250 | cmd->flags |= IWL_POWER_PCI_PM_MSK; | ||
251 | else | ||
252 | cmd->flags &= ~IWL_POWER_PCI_PM_MSK; | ||
253 | |||
254 | IWL_DEBUG_POWER(priv, "numSkipDtim = %u, dtimPeriod = %d\n", | ||
255 | skip, period); | ||
256 | IWL_DEBUG_POWER(priv, "Sleep command for index %d\n", lvl + 1); | ||
257 | } | ||
258 | |||
259 | static void iwl_power_sleep_cam_cmd(struct iwl_priv *priv, | ||
260 | struct iwl_powertable_cmd *cmd) | ||
261 | { | ||
262 | memset(cmd, 0, sizeof(*cmd)); | ||
263 | |||
264 | if (priv->power_data.bus_pm) | ||
265 | cmd->flags |= IWL_POWER_PCI_PM_MSK; | ||
266 | |||
267 | IWL_DEBUG_POWER(priv, "Sleep command for CAM\n"); | ||
268 | } | ||
269 | |||
270 | static void iwl_power_fill_sleep_cmd(struct iwl_priv *priv, | ||
271 | struct iwl_powertable_cmd *cmd, | ||
272 | int dynps_ms, int wakeup_period) | ||
273 | { | ||
274 | /* | ||
275 | * These are the original power level 3 sleep successions. The | ||
276 | * device may behave better with such succession and was also | ||
277 | * only tested with that. Just like the original sleep commands, | ||
278 | * also adjust the succession here to the wakeup_period below. | ||
279 | * The ranges are the same as for the sleep commands, 0-2, 3-9 | ||
280 | * and >10, which is selected based on the DTIM interval for | ||
281 | * the sleep index but here we use the wakeup period since that | ||
282 | * is what we need to do for the latency requirements. | ||
283 | */ | ||
284 | static const u8 slp_succ_r0[IWL_POWER_VEC_SIZE] = { 2, 2, 2, 2, 2 }; | ||
285 | static const u8 slp_succ_r1[IWL_POWER_VEC_SIZE] = { 2, 4, 6, 7, 9 }; | ||
286 | static const u8 slp_succ_r2[IWL_POWER_VEC_SIZE] = { 2, 7, 9, 9, 0xFF }; | ||
287 | const u8 *slp_succ = slp_succ_r0; | ||
288 | int i; | ||
289 | |||
290 | if (wakeup_period > IWL_DTIM_RANGE_0_MAX) | ||
291 | slp_succ = slp_succ_r1; | ||
292 | if (wakeup_period > IWL_DTIM_RANGE_1_MAX) | ||
293 | slp_succ = slp_succ_r2; | ||
294 | |||
295 | memset(cmd, 0, sizeof(*cmd)); | ||
296 | |||
297 | cmd->flags = IWL_POWER_DRIVER_ALLOW_SLEEP_MSK | | ||
298 | IWL_POWER_FAST_PD; /* no use seeing frames for others */ | ||
299 | |||
300 | if (priv->power_data.bus_pm) | ||
301 | cmd->flags |= IWL_POWER_PCI_PM_MSK; | ||
302 | |||
303 | if (priv->cfg->base_params->shadow_reg_enable) | ||
304 | cmd->flags |= IWL_POWER_SHADOW_REG_ENA; | ||
305 | else | ||
306 | cmd->flags &= ~IWL_POWER_SHADOW_REG_ENA; | ||
307 | |||
308 | if (iwl_advanced_bt_coexist(priv)) { | ||
309 | if (!priv->cfg->bt_params->bt_sco_disable) | ||
310 | cmd->flags |= IWL_POWER_BT_SCO_ENA; | ||
311 | else | ||
312 | cmd->flags &= ~IWL_POWER_BT_SCO_ENA; | ||
313 | } | ||
314 | |||
315 | cmd->rx_data_timeout = cpu_to_le32(1000 * dynps_ms); | ||
316 | cmd->tx_data_timeout = cpu_to_le32(1000 * dynps_ms); | ||
317 | |||
318 | for (i = 0; i < IWL_POWER_VEC_SIZE; i++) | ||
319 | cmd->sleep_interval[i] = | ||
320 | cpu_to_le32(min_t(int, slp_succ[i], wakeup_period)); | ||
321 | |||
322 | IWL_DEBUG_POWER(priv, "Automatic sleep command\n"); | ||
323 | } | ||
324 | |||
325 | static int iwl_set_power(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd) | ||
326 | { | ||
327 | IWL_DEBUG_POWER(priv, "Sending power/sleep command\n"); | ||
328 | IWL_DEBUG_POWER(priv, "Flags value = 0x%08X\n", cmd->flags); | ||
329 | IWL_DEBUG_POWER(priv, "Tx timeout = %u\n", le32_to_cpu(cmd->tx_data_timeout)); | ||
330 | IWL_DEBUG_POWER(priv, "Rx timeout = %u\n", le32_to_cpu(cmd->rx_data_timeout)); | ||
331 | IWL_DEBUG_POWER(priv, "Sleep interval vector = { %d , %d , %d , %d , %d }\n", | ||
332 | le32_to_cpu(cmd->sleep_interval[0]), | ||
333 | le32_to_cpu(cmd->sleep_interval[1]), | ||
334 | le32_to_cpu(cmd->sleep_interval[2]), | ||
335 | le32_to_cpu(cmd->sleep_interval[3]), | ||
336 | le32_to_cpu(cmd->sleep_interval[4])); | ||
337 | |||
338 | return trans_send_cmd_pdu(&priv->trans, POWER_TABLE_CMD, CMD_SYNC, | ||
339 | sizeof(struct iwl_powertable_cmd), cmd); | ||
340 | } | ||
341 | |||
342 | static void iwl_power_build_cmd(struct iwl_priv *priv, | ||
343 | struct iwl_powertable_cmd *cmd) | ||
344 | { | ||
345 | bool enabled = priv->hw->conf.flags & IEEE80211_CONF_PS; | ||
346 | int dtimper; | ||
347 | |||
348 | dtimper = priv->hw->conf.ps_dtim_period ?: 1; | ||
349 | |||
350 | if (priv->wowlan) | ||
351 | iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, dtimper); | ||
352 | else if (!priv->cfg->base_params->no_idle_support && | ||
353 | priv->hw->conf.flags & IEEE80211_CONF_IDLE) | ||
354 | iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, 20); | ||
355 | else if (iwl_tt_is_low_power_state(priv)) { | ||
356 | /* in thermal throttling low power state */ | ||
357 | iwl_static_sleep_cmd(priv, cmd, | ||
358 | iwl_tt_current_power_mode(priv), dtimper); | ||
359 | } else if (!enabled) | ||
360 | iwl_power_sleep_cam_cmd(priv, cmd); | ||
361 | else if (priv->power_data.debug_sleep_level_override >= 0) | ||
362 | iwl_static_sleep_cmd(priv, cmd, | ||
363 | priv->power_data.debug_sleep_level_override, | ||
364 | dtimper); | ||
365 | else if (iwlagn_mod_params.no_sleep_autoadjust) { | ||
366 | if (iwlagn_mod_params.power_level > IWL_POWER_INDEX_1 && | ||
367 | iwlagn_mod_params.power_level <= IWL_POWER_INDEX_5) | ||
368 | iwl_static_sleep_cmd(priv, cmd, | ||
369 | iwlagn_mod_params.power_level, dtimper); | ||
370 | else | ||
371 | iwl_static_sleep_cmd(priv, cmd, | ||
372 | IWL_POWER_INDEX_1, dtimper); | ||
373 | } else | ||
374 | iwl_power_fill_sleep_cmd(priv, cmd, | ||
375 | priv->hw->conf.dynamic_ps_timeout, | ||
376 | priv->hw->conf.max_sleep_period); | ||
377 | } | ||
378 | |||
379 | int iwl_power_set_mode(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd, | ||
380 | bool force) | ||
381 | { | ||
382 | int ret; | ||
383 | bool update_chains; | ||
384 | |||
385 | lockdep_assert_held(&priv->mutex); | ||
386 | |||
387 | /* Don't update the RX chain when chain noise calibration is running */ | ||
388 | update_chains = priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE || | ||
389 | priv->chain_noise_data.state == IWL_CHAIN_NOISE_ALIVE; | ||
390 | |||
391 | if (!memcmp(&priv->power_data.sleep_cmd, cmd, sizeof(*cmd)) && !force) | ||
392 | return 0; | ||
393 | |||
394 | if (!iwl_is_ready_rf(priv)) | ||
395 | return -EIO; | ||
396 | |||
397 | /* scan complete use sleep_power_next, need to be updated */ | ||
398 | memcpy(&priv->power_data.sleep_cmd_next, cmd, sizeof(*cmd)); | ||
399 | if (test_bit(STATUS_SCANNING, &priv->status) && !force) { | ||
400 | IWL_DEBUG_INFO(priv, "Defer power set mode while scanning\n"); | ||
401 | return 0; | ||
402 | } | ||
403 | |||
404 | if (cmd->flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK) | ||
405 | set_bit(STATUS_POWER_PMI, &priv->status); | ||
406 | |||
407 | ret = iwl_set_power(priv, cmd); | ||
408 | if (!ret) { | ||
409 | if (!(cmd->flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK)) | ||
410 | clear_bit(STATUS_POWER_PMI, &priv->status); | ||
411 | |||
412 | if (update_chains) | ||
413 | iwl_update_chain_flags(priv); | ||
414 | else | ||
415 | IWL_DEBUG_POWER(priv, | ||
416 | "Cannot update the power, chain noise " | ||
417 | "calibration running: %d\n", | ||
418 | priv->chain_noise_data.state); | ||
419 | |||
420 | memcpy(&priv->power_data.sleep_cmd, cmd, sizeof(*cmd)); | ||
421 | } else | ||
422 | IWL_ERR(priv, "set power fail, ret = %d", ret); | ||
423 | |||
424 | return ret; | ||
425 | } | ||
426 | |||
427 | int iwl_power_update_mode(struct iwl_priv *priv, bool force) | ||
428 | { | ||
429 | struct iwl_powertable_cmd cmd; | ||
430 | |||
431 | iwl_power_build_cmd(priv, &cmd); | ||
432 | return iwl_power_set_mode(priv, &cmd, force); | ||
433 | } | ||
434 | |||
435 | /* initialize to default */ | ||
436 | void iwl_power_initialize(struct iwl_priv *priv) | ||
437 | { | ||
438 | priv->power_data.bus_pm = bus_get_pm_support(priv->bus); | ||
439 | |||
440 | priv->power_data.debug_sleep_level_override = -1; | ||
441 | |||
442 | memset(&priv->power_data.sleep_cmd, 0, | ||
443 | sizeof(priv->power_data.sleep_cmd)); | ||
444 | } | ||