diff options
author | Mohamed Abbas <mabbas@linux.intel.com> | 2008-04-21 18:41:51 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-05-07 15:02:15 -0400 |
commit | 5da4b55f78fb2ed40926b775d4f7c791594ecbd7 (patch) | |
tree | a12ecf0d26552869151738998e88e03e65bcb7c3 | |
parent | 7eafd25d9559bd0f652449c222d38d63412e3d4a (diff) |
iwlwifi: Add power level support
Add power level support
Signed-off-by: Mohamed Abbas <mabbas@linux.intel.com>
Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | drivers/net/wireless/iwlwifi/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-4965-commands.h | 1 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-4965.c | 15 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-4965.h | 29 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-core.c | 3 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-core.h | 3 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-power.c | 423 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-power.h | 76 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl4965-base.c | 217 |
9 files changed, 543 insertions, 226 deletions
diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile index 7bc569be4ba7..e2c05f5346ef 100644 --- a/drivers/net/wireless/iwlwifi/Makefile +++ b/drivers/net/wireless/iwlwifi/Makefile | |||
@@ -1,5 +1,5 @@ | |||
1 | obj-$(CONFIG_IWLCORE) += iwlcore.o | 1 | obj-$(CONFIG_IWLCORE) += iwlcore.o |
2 | iwlcore-objs := iwl-core.o iwl-eeprom.o iwl-hcmd.o | 2 | iwlcore-objs := iwl-core.o iwl-eeprom.o iwl-hcmd.o iwl-power.o |
3 | iwlcore-$(CONFIG_IWLWIFI_DEBUGFS) += iwl-debugfs.o | 3 | iwlcore-$(CONFIG_IWLWIFI_DEBUGFS) += iwl-debugfs.o |
4 | iwlcore-$(CONFIG_IWLWIFI_LEDS) += iwl-led.o | 4 | iwlcore-$(CONFIG_IWLWIFI_LEDS) += iwl-led.o |
5 | iwlcore-$(CONFIG_IWLWIFI_RFKILL) += iwl-rfkill.o | 5 | iwlcore-$(CONFIG_IWLWIFI_RFKILL) += iwl-rfkill.o |
diff --git a/drivers/net/wireless/iwlwifi/iwl-4965-commands.h b/drivers/net/wireless/iwlwifi/iwl-4965-commands.h index c03c04fe14c7..b2fa58d2d174 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965-commands.h +++ b/drivers/net/wireless/iwlwifi/iwl-4965-commands.h | |||
@@ -1853,6 +1853,7 @@ struct iwl4965_spectrum_notification { | |||
1853 | #define IWL_POWER_DRIVER_ALLOW_SLEEP_MSK __constant_cpu_to_le16(1 << 0) | 1853 | #define IWL_POWER_DRIVER_ALLOW_SLEEP_MSK __constant_cpu_to_le16(1 << 0) |
1854 | #define IWL_POWER_SLEEP_OVER_DTIM_MSK __constant_cpu_to_le16(1 << 2) | 1854 | #define IWL_POWER_SLEEP_OVER_DTIM_MSK __constant_cpu_to_le16(1 << 2) |
1855 | #define IWL_POWER_PCI_PM_MSK __constant_cpu_to_le16(1 << 3) | 1855 | #define IWL_POWER_PCI_PM_MSK __constant_cpu_to_le16(1 << 3) |
1856 | #define IWL_POWER_FAST_PD __constant_cpu_to_le16(1 << 4) | ||
1856 | 1857 | ||
1857 | struct iwl4965_powertable_cmd { | 1858 | struct iwl4965_powertable_cmd { |
1858 | __le16 flags; | 1859 | __le16 flags; |
diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index 2b9471f16862..d051aac558f2 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c | |||
@@ -702,8 +702,6 @@ int iwl4965_hw_nic_init(struct iwl_priv *priv) | |||
702 | u32 val; | 702 | u32 val; |
703 | u8 val_link; | 703 | u8 val_link; |
704 | 704 | ||
705 | iwl4965_power_init_handle(priv); | ||
706 | |||
707 | /* nic_init */ | 705 | /* nic_init */ |
708 | spin_lock_irqsave(&priv->lock, flags); | 706 | spin_lock_irqsave(&priv->lock, flags); |
709 | 707 | ||
@@ -1433,6 +1431,17 @@ int iwl4965_hw_txq_free_tfd(struct iwl_priv *priv, struct iwl4965_tx_queue *txq) | |||
1433 | return 0; | 1431 | return 0; |
1434 | } | 1432 | } |
1435 | 1433 | ||
1434 | /* set card power command */ | ||
1435 | static int iwl4965_set_power(struct iwl_priv *priv, | ||
1436 | void *cmd) | ||
1437 | { | ||
1438 | int ret = 0; | ||
1439 | |||
1440 | ret = iwl_send_cmd_pdu_async(priv, POWER_TABLE_CMD, | ||
1441 | sizeof(struct iwl4965_powertable_cmd), | ||
1442 | cmd, NULL); | ||
1443 | return ret; | ||
1444 | } | ||
1436 | int iwl4965_hw_reg_set_txpower(struct iwl_priv *priv, s8 power) | 1445 | int iwl4965_hw_reg_set_txpower(struct iwl_priv *priv, s8 power) |
1437 | { | 1446 | { |
1438 | IWL_ERROR("TODO: Implement iwl4965_hw_reg_set_txpower!\n"); | 1447 | IWL_ERROR("TODO: Implement iwl4965_hw_reg_set_txpower!\n"); |
@@ -4336,6 +4345,8 @@ static struct iwl_lib_ops iwl4965_lib = { | |||
4336 | .release_semaphore = iwlcore_eeprom_release_semaphore, | 4345 | .release_semaphore = iwlcore_eeprom_release_semaphore, |
4337 | }, | 4346 | }, |
4338 | .radio_kill_sw = iwl4965_radio_kill_sw, | 4347 | .radio_kill_sw = iwl4965_radio_kill_sw, |
4348 | .set_power = iwl4965_set_power, | ||
4349 | .update_chain_flags = iwl4965_update_chain_flags, | ||
4339 | }; | 4350 | }; |
4340 | 4351 | ||
4341 | static struct iwl_ops iwl4965_ops = { | 4352 | static struct iwl_ops iwl4965_ops = { |
diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.h b/drivers/net/wireless/iwlwifi/iwl-4965.h index 78381f287de8..0550d12e5c60 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.h +++ b/drivers/net/wireless/iwlwifi/iwl-4965.h | |||
@@ -44,6 +44,7 @@ | |||
44 | #include "iwl-prph.h" | 44 | #include "iwl-prph.h" |
45 | #include "iwl-debug.h" | 45 | #include "iwl-debug.h" |
46 | #include "iwl-led.h" | 46 | #include "iwl-led.h" |
47 | #include "iwl-power.h" | ||
47 | 48 | ||
48 | /* configuration for the iwl4965 */ | 49 | /* configuration for the iwl4965 */ |
49 | extern struct iwl_cfg iwl4965_agn_cfg; | 50 | extern struct iwl_cfg iwl4965_agn_cfg; |
@@ -257,31 +258,6 @@ enum iwl_pwr_src { | |||
257 | IWL_PWR_SRC_VAUX, | 258 | IWL_PWR_SRC_VAUX, |
258 | }; | 259 | }; |
259 | 260 | ||
260 | struct iwl4965_power_vec_entry { | ||
261 | struct iwl4965_powertable_cmd cmd; | ||
262 | u8 no_dtim; | ||
263 | }; | ||
264 | #define IWL_POWER_RANGE_0 (0) | ||
265 | #define IWL_POWER_RANGE_1 (1) | ||
266 | |||
267 | #define IWL_POWER_MODE_CAM 0x00 /* Continuously Aware Mode, always on */ | ||
268 | #define IWL_POWER_INDEX_3 0x03 | ||
269 | #define IWL_POWER_INDEX_5 0x05 | ||
270 | #define IWL_POWER_AC 0x06 | ||
271 | #define IWL_POWER_BATTERY 0x07 | ||
272 | #define IWL_POWER_LIMIT 0x07 | ||
273 | #define IWL_POWER_MASK 0x0F | ||
274 | #define IWL_POWER_ENABLED 0x10 | ||
275 | #define IWL_POWER_LEVEL(x) ((x) & IWL_POWER_MASK) | ||
276 | |||
277 | struct iwl4965_power_mgr { | ||
278 | spinlock_t lock; | ||
279 | struct iwl4965_power_vec_entry pwr_range_0[IWL_POWER_AC]; | ||
280 | struct iwl4965_power_vec_entry pwr_range_1[IWL_POWER_AC]; | ||
281 | u8 active_index; | ||
282 | u32 dtim_val; | ||
283 | }; | ||
284 | |||
285 | #define IEEE80211_DATA_LEN 2304 | 261 | #define IEEE80211_DATA_LEN 2304 |
286 | #define IEEE80211_4ADDR_LEN 30 | 262 | #define IEEE80211_4ADDR_LEN 30 |
287 | #define IEEE80211_HLEN (IEEE80211_4ADDR_LEN) | 263 | #define IEEE80211_HLEN (IEEE80211_4ADDR_LEN) |
@@ -674,6 +650,7 @@ extern unsigned int iwl4965_fill_beacon_frame(struct iwl_priv *priv, | |||
674 | extern int iwl4965_rx_queue_update_write_ptr(struct iwl_priv *priv, | 650 | extern int iwl4965_rx_queue_update_write_ptr(struct iwl_priv *priv, |
675 | struct iwl4965_rx_queue *q); | 651 | struct iwl4965_rx_queue *q); |
676 | extern __le16 *ieee80211_get_qos_ctrl(struct ieee80211_hdr *hdr); | 652 | extern __le16 *ieee80211_get_qos_ctrl(struct ieee80211_hdr *hdr); |
653 | extern void iwl4965_update_chain_flags(struct iwl_priv *priv); | ||
677 | int iwl4965_set_pwr_src(struct iwl_priv *priv, enum iwl_pwr_src src); | 654 | int iwl4965_set_pwr_src(struct iwl_priv *priv, enum iwl_pwr_src src); |
678 | 655 | ||
679 | int iwl4965_init_geos(struct iwl_priv *priv); | 656 | int iwl4965_init_geos(struct iwl_priv *priv); |
@@ -1100,7 +1077,7 @@ struct iwl_priv { | |||
1100 | u64 bytes; | 1077 | u64 bytes; |
1101 | } tx_stats[3], rx_stats[3]; | 1078 | } tx_stats[3], rx_stats[3]; |
1102 | 1079 | ||
1103 | struct iwl4965_power_mgr power_data; | 1080 | struct iwl_power_mgr power_data; |
1104 | 1081 | ||
1105 | struct iwl4965_notif_statistics statistics; | 1082 | struct iwl4965_notif_statistics statistics; |
1106 | unsigned long last_statistics_time; | 1083 | unsigned long last_statistics_time; |
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index 2dfd982d7d1f..c336b1991f1a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c | |||
@@ -37,6 +37,7 @@ struct iwl_priv; /* FIXME: remove */ | |||
37 | #include "iwl-4965.h" /* FIXME: remove */ | 37 | #include "iwl-4965.h" /* FIXME: remove */ |
38 | #include "iwl-core.h" | 38 | #include "iwl-core.h" |
39 | #include "iwl-rfkill.h" | 39 | #include "iwl-rfkill.h" |
40 | #include "iwl-power.h" | ||
40 | 41 | ||
41 | 42 | ||
42 | MODULE_DESCRIPTION("iwl core"); | 43 | MODULE_DESCRIPTION("iwl core"); |
@@ -263,8 +264,10 @@ int iwlcore_low_level_notify(struct iwl_priv *priv, | |||
263 | if (ret) | 264 | if (ret) |
264 | IWL_ERROR("Unable to initialize RFKILL system. " | 265 | IWL_ERROR("Unable to initialize RFKILL system. " |
265 | "Ignoring error: %d\n", ret); | 266 | "Ignoring error: %d\n", ret); |
267 | iwl_power_initialize(priv); | ||
266 | break; | 268 | break; |
267 | case IWLCORE_START_EVT: | 269 | case IWLCORE_START_EVT: |
270 | iwl_power_update_mode(priv, 1); | ||
268 | break; | 271 | break; |
269 | case IWLCORE_STOP_EVT: | 272 | case IWLCORE_STOP_EVT: |
270 | break; | 273 | break; |
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h index 51f61fb844f0..3d113dce8994 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.h +++ b/drivers/net/wireless/iwlwifi/iwl-core.h | |||
@@ -118,6 +118,9 @@ struct iwl_lib_ops { | |||
118 | struct { | 118 | struct { |
119 | int (*set_pwr_src)(struct iwl_priv *priv, enum iwl_pwr_src src); | 119 | int (*set_pwr_src)(struct iwl_priv *priv, enum iwl_pwr_src src); |
120 | } apm_ops; | 120 | } apm_ops; |
121 | /* power */ | ||
122 | int (*set_power)(struct iwl_priv *priv, void *cmd); | ||
123 | void (*update_chain_flags)(struct iwl_priv *priv); | ||
121 | /* eeprom operations (as defined in iwl-eeprom.h) */ | 124 | /* eeprom operations (as defined in iwl-eeprom.h) */ |
122 | struct iwl_eeprom_ops eeprom_ops; | 125 | struct iwl_eeprom_ops eeprom_ops; |
123 | }; | 126 | }; |
diff --git a/drivers/net/wireless/iwlwifi/iwl-power.c b/drivers/net/wireless/iwlwifi/iwl-power.c new file mode 100644 index 000000000000..a242628465a7 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-power.c | |||
@@ -0,0 +1,423 @@ | |||
1 | /****************************************************************************** | ||
2 | * | ||
3 | * Copyright(c) 2007 - 2008 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 | * James P. Ketrenos <ipw2100-admin@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/version.h> | ||
33 | #include <linux/init.h> | ||
34 | |||
35 | #include <net/mac80211.h> | ||
36 | |||
37 | #include "iwl-eeprom.h" | ||
38 | #include "iwl-4965.h" | ||
39 | #include "iwl-core.h" | ||
40 | #include "iwl-4965-commands.h" | ||
41 | #include "iwl-debug.h" | ||
42 | #include "iwl-power.h" | ||
43 | #include "iwl-helpers.h" | ||
44 | |||
45 | /* | ||
46 | * Setting power level allow the card to go to sleep when not busy | ||
47 | * there are three factor that decide the power level to go to, they | ||
48 | * are list here with its priority | ||
49 | * 1- critical_power_setting this will be set according to card temperature. | ||
50 | * 2- system_power_setting this will be set by system PM manager. | ||
51 | * 3- user_power_setting this will be set by user either by writing to sys or | ||
52 | * mac80211 | ||
53 | * | ||
54 | * if system_power_setting and user_power_setting is set to auto | ||
55 | * the power level will be decided according to association status and battery | ||
56 | * status. | ||
57 | * | ||
58 | */ | ||
59 | |||
60 | #define MSEC_TO_USEC 1024 | ||
61 | #define IWL_POWER_RANGE_0_MAX (2) | ||
62 | #define IWL_POWER_RANGE_1_MAX (10) | ||
63 | |||
64 | |||
65 | #define NOSLP __constant_cpu_to_le16(0), 0, 0 | ||
66 | #define SLP IWL_POWER_DRIVER_ALLOW_SLEEP_MSK, 0, 0 | ||
67 | #define SLP_TOUT(T) __constant_cpu_to_le32((T) * MSEC_TO_USEC) | ||
68 | #define SLP_VEC(X0, X1, X2, X3, X4) {__constant_cpu_to_le32(X0), \ | ||
69 | __constant_cpu_to_le32(X1), \ | ||
70 | __constant_cpu_to_le32(X2), \ | ||
71 | __constant_cpu_to_le32(X3), \ | ||
72 | __constant_cpu_to_le32(X4)} | ||
73 | |||
74 | #define IWL_POWER_ON_BATTERY IWL_POWER_INDEX_5 | ||
75 | #define IWL_POWER_ON_AC_DISASSOC IWL_POWER_MODE_CAM | ||
76 | #define IWL_POWER_ON_AC_ASSOC IWL_POWER_MODE_CAM | ||
77 | |||
78 | |||
79 | #define IWL_CT_KILL_TEMPERATURE 110 | ||
80 | #define IWL_MIN_POWER_TEMPERATURE 100 | ||
81 | #define IWL_REDUCED_POWER_TEMPERATURE 95 | ||
82 | |||
83 | /* default power management (not Tx power) table values */ | ||
84 | /* for tim 0-10 */ | ||
85 | static struct iwl_power_vec_entry range_0[IWL_POWER_AC] = { | ||
86 | {{NOSLP, SLP_TOUT(0), SLP_TOUT(0), SLP_VEC(0, 0, 0, 0, 0)}, 0}, | ||
87 | {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 2, 2, 0xFF)}, 0}, | ||
88 | {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 2, 2, 0xFF)}, 0}, | ||
89 | {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 2, 2, 2, 0xFF)}, 0}, | ||
90 | {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 2, 4, 4, 0xFF)}, 1}, | ||
91 | {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 2, 4, 6, 0xFF)}, 2} | ||
92 | }; | ||
93 | |||
94 | |||
95 | /* for tim = 3-10 */ | ||
96 | static struct iwl_power_vec_entry range_1[IWL_POWER_AC] = { | ||
97 | {{NOSLP, SLP_TOUT(0), SLP_TOUT(0), SLP_VEC(0, 0, 0, 0, 0)}, 0}, | ||
98 | {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 4)}, 0}, | ||
99 | {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 3, 4, 7)}, 0}, | ||
100 | {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 4, 6, 7, 9)}, 0}, | ||
101 | {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 4, 6, 9, 10)}, 1}, | ||
102 | {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 4, 7, 10, 10)}, 2} | ||
103 | }; | ||
104 | |||
105 | /* for tim > 11 */ | ||
106 | static struct iwl_power_vec_entry range_2[IWL_POWER_AC] = { | ||
107 | {{NOSLP, SLP_TOUT(0), SLP_TOUT(0), SLP_VEC(0, 0, 0, 0, 0)}, 0}, | ||
108 | {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 0xFF)}, 0}, | ||
109 | {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(2, 4, 6, 7, 0xFF)}, 0}, | ||
110 | {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 7, 9, 9, 0xFF)}, 0}, | ||
111 | {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 7, 9, 9, 0xFF)}, 0}, | ||
112 | {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(4, 7, 10, 10, 0xFF)}, 0} | ||
113 | }; | ||
114 | |||
115 | /* decide the right power level according to association status | ||
116 | * and battery status | ||
117 | */ | ||
118 | static u16 iwl_get_auto_power_mode(struct iwl_priv *priv) | ||
119 | { | ||
120 | u16 mode = priv->power_data.user_power_setting; | ||
121 | |||
122 | switch (priv->power_data.user_power_setting) { | ||
123 | case IWL_POWER_AUTO: | ||
124 | /* if running on battery */ | ||
125 | if (priv->power_data.is_battery_active) | ||
126 | mode = IWL_POWER_ON_BATTERY; | ||
127 | else if (iwl_is_associated(priv)) | ||
128 | mode = IWL_POWER_ON_AC_ASSOC; | ||
129 | else | ||
130 | mode = IWL_POWER_ON_AC_DISASSOC; | ||
131 | break; | ||
132 | case IWL_POWER_BATTERY: | ||
133 | mode = IWL_POWER_INDEX_3; | ||
134 | break; | ||
135 | case IWL_POWER_AC: | ||
136 | mode = IWL_POWER_MODE_CAM; | ||
137 | break; | ||
138 | } | ||
139 | return mode; | ||
140 | } | ||
141 | |||
142 | /* initialize to default */ | ||
143 | static int iwl_power_init_handle(struct iwl_priv *priv) | ||
144 | { | ||
145 | int ret = 0, i; | ||
146 | struct iwl_power_mgr *pow_data; | ||
147 | int size = sizeof(struct iwl_power_vec_entry) * IWL_POWER_AC; | ||
148 | u16 pci_pm; | ||
149 | |||
150 | IWL_DEBUG_POWER("Initialize power \n"); | ||
151 | |||
152 | pow_data = &(priv->power_data); | ||
153 | |||
154 | memset(pow_data, 0, sizeof(*pow_data)); | ||
155 | |||
156 | memcpy(&pow_data->pwr_range_0[0], &range_0[0], size); | ||
157 | memcpy(&pow_data->pwr_range_1[0], &range_1[0], size); | ||
158 | memcpy(&pow_data->pwr_range_2[0], &range_2[0], size); | ||
159 | |||
160 | ret = pci_read_config_word(priv->pci_dev, | ||
161 | PCI_LINK_CTRL, &pci_pm); | ||
162 | if (ret != 0) | ||
163 | return 0; | ||
164 | else { | ||
165 | struct iwl4965_powertable_cmd *cmd; | ||
166 | |||
167 | IWL_DEBUG_POWER("adjust power command flags\n"); | ||
168 | |||
169 | for (i = 0; i < IWL_POWER_AC; i++) { | ||
170 | cmd = &pow_data->pwr_range_0[i].cmd; | ||
171 | |||
172 | if (pci_pm & 0x1) | ||
173 | cmd->flags &= ~IWL_POWER_PCI_PM_MSK; | ||
174 | else | ||
175 | cmd->flags |= IWL_POWER_PCI_PM_MSK; | ||
176 | } | ||
177 | } | ||
178 | return ret; | ||
179 | } | ||
180 | |||
181 | /* adjust power command according to dtim period and power level*/ | ||
182 | static int iwl_update_power_command(struct iwl_priv *priv, | ||
183 | struct iwl4965_powertable_cmd *cmd, | ||
184 | u16 mode) | ||
185 | { | ||
186 | int ret = 0, i; | ||
187 | u8 skip; | ||
188 | u32 max_sleep = 0; | ||
189 | struct iwl_power_vec_entry *range; | ||
190 | u8 period = 0; | ||
191 | struct iwl_power_mgr *pow_data; | ||
192 | |||
193 | if (mode > IWL_POWER_INDEX_5) { | ||
194 | IWL_DEBUG_POWER("Error invalid power mode \n"); | ||
195 | return -1; | ||
196 | } | ||
197 | pow_data = &(priv->power_data); | ||
198 | |||
199 | if (pow_data->dtim_period <= IWL_POWER_RANGE_0_MAX) | ||
200 | range = &pow_data->pwr_range_0[0]; | ||
201 | else if (pow_data->dtim_period <= IWL_POWER_RANGE_1_MAX) | ||
202 | range = &pow_data->pwr_range_1[0]; | ||
203 | else | ||
204 | range = &pow_data->pwr_range_2[0]; | ||
205 | |||
206 | period = pow_data->dtim_period; | ||
207 | memcpy(cmd, &range[mode].cmd, sizeof(struct iwl4965_powertable_cmd)); | ||
208 | |||
209 | if (period == 0) { | ||
210 | period = 1; | ||
211 | skip = 0; | ||
212 | } else | ||
213 | skip = range[mode].no_dtim; | ||
214 | |||
215 | if (skip == 0) { | ||
216 | max_sleep = period; | ||
217 | cmd->flags &= ~IWL_POWER_SLEEP_OVER_DTIM_MSK; | ||
218 | } else { | ||
219 | __le32 slp_itrvl = cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]; | ||
220 | max_sleep = le32_to_cpu(slp_itrvl); | ||
221 | if (max_sleep == 0xFF) | ||
222 | max_sleep = period * (skip + 1); | ||
223 | else if (max_sleep > period) | ||
224 | max_sleep = (le32_to_cpu(slp_itrvl) / period) * period; | ||
225 | cmd->flags |= IWL_POWER_SLEEP_OVER_DTIM_MSK; | ||
226 | } | ||
227 | |||
228 | for (i = 0; i < IWL_POWER_VEC_SIZE; i++) { | ||
229 | if (le32_to_cpu(cmd->sleep_interval[i]) > max_sleep) | ||
230 | cmd->sleep_interval[i] = cpu_to_le32(max_sleep); | ||
231 | } | ||
232 | |||
233 | IWL_DEBUG_POWER("Flags value = 0x%08X\n", cmd->flags); | ||
234 | IWL_DEBUG_POWER("Tx timeout = %u\n", le32_to_cpu(cmd->tx_data_timeout)); | ||
235 | IWL_DEBUG_POWER("Rx timeout = %u\n", le32_to_cpu(cmd->rx_data_timeout)); | ||
236 | IWL_DEBUG_POWER("Sleep interval vector = { %d , %d , %d , %d , %d }\n", | ||
237 | le32_to_cpu(cmd->sleep_interval[0]), | ||
238 | le32_to_cpu(cmd->sleep_interval[1]), | ||
239 | le32_to_cpu(cmd->sleep_interval[2]), | ||
240 | le32_to_cpu(cmd->sleep_interval[3]), | ||
241 | le32_to_cpu(cmd->sleep_interval[4])); | ||
242 | |||
243 | return ret; | ||
244 | } | ||
245 | |||
246 | |||
247 | /* | ||
248 | * calucaute the final power mode index | ||
249 | */ | ||
250 | int iwl_power_update_mode(struct iwl_priv *priv, u8 refresh) | ||
251 | { | ||
252 | struct iwl_power_mgr *setting = &(priv->power_data); | ||
253 | int ret = 0; | ||
254 | u16 uninitialized_var(final_mode); | ||
255 | |||
256 | /* If on battery, set to 3, | ||
257 | * if plugged into AC power, set to CAM ("continuously aware mode"), | ||
258 | * else user level */ | ||
259 | |||
260 | switch (setting->system_power_setting) { | ||
261 | case IWL_POWER_AUTO: | ||
262 | final_mode = iwl_get_auto_power_mode(priv); | ||
263 | break; | ||
264 | case IWL_POWER_BATTERY: | ||
265 | final_mode = IWL_POWER_INDEX_3; | ||
266 | break; | ||
267 | case IWL_POWER_AC: | ||
268 | final_mode = IWL_POWER_MODE_CAM; | ||
269 | break; | ||
270 | default: | ||
271 | final_mode = setting->system_power_setting; | ||
272 | } | ||
273 | |||
274 | if (setting->critical_power_setting > final_mode) | ||
275 | final_mode = setting->critical_power_setting; | ||
276 | |||
277 | /* driver only support CAM for non STA network */ | ||
278 | if (priv->iw_mode != IEEE80211_IF_TYPE_STA) | ||
279 | final_mode = IWL_POWER_MODE_CAM; | ||
280 | |||
281 | if (!iwl_is_rfkill(priv) && !setting->power_disabled && | ||
282 | ((setting->power_mode != final_mode) || refresh)) { | ||
283 | struct iwl4965_powertable_cmd cmd; | ||
284 | |||
285 | if (final_mode != IWL_POWER_MODE_CAM) | ||
286 | set_bit(STATUS_POWER_PMI, &priv->status); | ||
287 | |||
288 | iwl_update_power_command(priv, &cmd, final_mode); | ||
289 | cmd.keep_alive_beacons = 0; | ||
290 | |||
291 | if (final_mode == IWL_POWER_INDEX_5) | ||
292 | cmd.flags |= IWL_POWER_FAST_PD; | ||
293 | |||
294 | if (priv->cfg->ops->lib->set_power) | ||
295 | ret = priv->cfg->ops->lib->set_power(priv, &cmd); | ||
296 | |||
297 | if (final_mode == IWL_POWER_MODE_CAM) | ||
298 | clear_bit(STATUS_POWER_PMI, &priv->status); | ||
299 | else | ||
300 | set_bit(STATUS_POWER_PMI, &priv->status); | ||
301 | |||
302 | if (priv->cfg->ops->lib->update_chain_flags) | ||
303 | priv->cfg->ops->lib->update_chain_flags(priv); | ||
304 | |||
305 | if (!ret) | ||
306 | setting->power_mode = final_mode; | ||
307 | } | ||
308 | |||
309 | return ret; | ||
310 | } | ||
311 | EXPORT_SYMBOL(iwl_power_update_mode); | ||
312 | |||
313 | /* Allow other iwl code to disable/enable power management active | ||
314 | * this will be usefull for rate scale to disable PM during heavy | ||
315 | * Tx/Rx activities | ||
316 | */ | ||
317 | int iwl_power_disable_management(struct iwl_priv *priv) | ||
318 | { | ||
319 | u16 prev_mode; | ||
320 | int ret = 0; | ||
321 | |||
322 | if (priv->power_data.power_disabled) | ||
323 | return -EBUSY; | ||
324 | |||
325 | prev_mode = priv->power_data.user_power_setting; | ||
326 | priv->power_data.user_power_setting = IWL_POWER_MODE_CAM; | ||
327 | ret = iwl_power_update_mode(priv, 0); | ||
328 | priv->power_data.power_disabled = 1; | ||
329 | priv->power_data.user_power_setting = prev_mode; | ||
330 | |||
331 | return ret; | ||
332 | } | ||
333 | EXPORT_SYMBOL(iwl_power_disable_management); | ||
334 | |||
335 | /* Allow other iwl code to disable/enable power management active | ||
336 | * this will be usefull for rate scale to disable PM during hight | ||
337 | * valume activities | ||
338 | */ | ||
339 | int iwl_power_enable_management(struct iwl_priv *priv) | ||
340 | { | ||
341 | int ret = 0; | ||
342 | |||
343 | priv->power_data.power_disabled = 0; | ||
344 | ret = iwl_power_update_mode(priv, 0); | ||
345 | return ret; | ||
346 | } | ||
347 | EXPORT_SYMBOL(iwl_power_enable_management); | ||
348 | |||
349 | /* set user_power_setting */ | ||
350 | int iwl_power_set_user_mode(struct iwl_priv *priv, u16 mode) | ||
351 | { | ||
352 | int ret = 0; | ||
353 | |||
354 | if (mode > IWL_POWER_LIMIT) | ||
355 | return -EINVAL; | ||
356 | |||
357 | priv->power_data.user_power_setting = mode; | ||
358 | |||
359 | ret = iwl_power_update_mode(priv, 0); | ||
360 | |||
361 | return ret; | ||
362 | } | ||
363 | EXPORT_SYMBOL(iwl_power_set_user_mode); | ||
364 | |||
365 | |||
366 | /* set system_power_setting. This should be set by over all | ||
367 | * PM application. | ||
368 | */ | ||
369 | int iwl_power_set_system_mode(struct iwl_priv *priv, u16 mode) | ||
370 | { | ||
371 | int ret = 0; | ||
372 | |||
373 | if (mode > IWL_POWER_LIMIT) | ||
374 | return -EINVAL; | ||
375 | |||
376 | priv->power_data.system_power_setting = mode; | ||
377 | |||
378 | ret = iwl_power_update_mode(priv, 0); | ||
379 | |||
380 | return ret; | ||
381 | } | ||
382 | EXPORT_SYMBOL(iwl_power_set_system_mode); | ||
383 | |||
384 | /* initilize to default */ | ||
385 | void iwl_power_initialize(struct iwl_priv *priv) | ||
386 | { | ||
387 | |||
388 | iwl_power_init_handle(priv); | ||
389 | priv->power_data.user_power_setting = IWL_POWER_AUTO; | ||
390 | priv->power_data.power_disabled = 0; | ||
391 | priv->power_data.system_power_setting = IWL_POWER_AUTO; | ||
392 | priv->power_data.is_battery_active = 0; | ||
393 | priv->power_data.power_disabled = 0; | ||
394 | priv->power_data.critical_power_setting = 0; | ||
395 | } | ||
396 | EXPORT_SYMBOL(iwl_power_initialize); | ||
397 | |||
398 | /* set critical_power_setting according to temperature value */ | ||
399 | int iwl_power_temperature_change(struct iwl_priv *priv) | ||
400 | { | ||
401 | int ret = 0; | ||
402 | u16 new_critical = priv->power_data.critical_power_setting; | ||
403 | s32 temperature = KELVIN_TO_CELSIUS(priv->last_temperature); | ||
404 | |||
405 | if (temperature > IWL_CT_KILL_TEMPERATURE) | ||
406 | return 0; | ||
407 | else if (temperature > IWL_MIN_POWER_TEMPERATURE) | ||
408 | new_critical = IWL_POWER_INDEX_5; | ||
409 | else if (temperature > IWL_REDUCED_POWER_TEMPERATURE) | ||
410 | new_critical = IWL_POWER_INDEX_3; | ||
411 | else | ||
412 | new_critical = IWL_POWER_MODE_CAM; | ||
413 | |||
414 | if (new_critical != priv->power_data.critical_power_setting) | ||
415 | priv->power_data.critical_power_setting = new_critical; | ||
416 | |||
417 | if (priv->power_data.critical_power_setting > | ||
418 | priv->power_data.power_mode) | ||
419 | ret = iwl_power_update_mode(priv, 0); | ||
420 | |||
421 | return ret; | ||
422 | } | ||
423 | EXPORT_SYMBOL(iwl_power_temperature_change); | ||
diff --git a/drivers/net/wireless/iwlwifi/iwl-power.h b/drivers/net/wireless/iwlwifi/iwl-power.h new file mode 100644 index 000000000000..aad2784ca2ea --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-power.h | |||
@@ -0,0 +1,76 @@ | |||
1 | /****************************************************************************** | ||
2 | * | ||
3 | * Copyright(c) 2007 - 2008 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 | * James P. Ketrenos <ipw2100-admin@linux.intel.com> | ||
26 | * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 | ||
27 | *****************************************************************************/ | ||
28 | #ifndef __iwl_power_setting_h__ | ||
29 | #define __iwl_power_setting_h__ | ||
30 | |||
31 | #include <net/mac80211.h> | ||
32 | #include "iwl-4965-commands.h" | ||
33 | |||
34 | struct iwl_priv; | ||
35 | |||
36 | #define IWL_POWER_MODE_CAM 0x00 /* Continuously Aware Mode, always on */ | ||
37 | #define IWL_POWER_INDEX_3 0x03 | ||
38 | #define IWL_POWER_INDEX_5 0x05 | ||
39 | #define IWL_POWER_AC 0x06 | ||
40 | #define IWL_POWER_BATTERY 0x07 | ||
41 | #define IWL_POWER_AUTO 0x08 | ||
42 | #define IWL_POWER_LIMIT 0x08 | ||
43 | #define IWL_POWER_MASK 0x0F | ||
44 | #define IWL_POWER_ENABLED 0x10 | ||
45 | |||
46 | /* Power management (not Tx power) structures */ | ||
47 | |||
48 | struct iwl_power_vec_entry { | ||
49 | struct iwl4965_powertable_cmd cmd; | ||
50 | u8 no_dtim; | ||
51 | }; | ||
52 | |||
53 | struct iwl_power_mgr { | ||
54 | spinlock_t lock; | ||
55 | struct iwl_power_vec_entry pwr_range_0[IWL_POWER_AC]; | ||
56 | struct iwl_power_vec_entry pwr_range_1[IWL_POWER_AC]; | ||
57 | struct iwl_power_vec_entry pwr_range_2[IWL_POWER_AC]; | ||
58 | u32 dtim_period; | ||
59 | /* final power level that used to calculate final power command */ | ||
60 | u8 power_mode; | ||
61 | u8 user_power_setting; /* set by user through mac80211 or sysfs */ | ||
62 | u8 system_power_setting; /* set by kernel syatem tools */ | ||
63 | u8 critical_power_setting; /* set if driver over heated */ | ||
64 | u8 is_battery_active; /* DC/AC power */ | ||
65 | u8 power_disabled; /* flag to disable using power saving level */ | ||
66 | }; | ||
67 | |||
68 | int iwl_power_update_mode(struct iwl_priv *priv, u8 refresh); | ||
69 | int iwl_power_disable_management(struct iwl_priv *priv); | ||
70 | int iwl_power_enable_management(struct iwl_priv *priv); | ||
71 | int iwl_power_set_user_mode(struct iwl_priv *priv, u16 mode); | ||
72 | int iwl_power_set_system_mode(struct iwl_priv *priv, u16 mode); | ||
73 | void iwl_power_initialize(struct iwl_priv *priv); | ||
74 | int iwl_power_temperature_change(struct iwl_priv *priv); | ||
75 | |||
76 | #endif /* __iwl_power_setting_h__ */ | ||
diff --git a/drivers/net/wireless/iwlwifi/iwl4965-base.c b/drivers/net/wireless/iwlwifi/iwl4965-base.c index 50f12a6133e8..6cb54580fe6b 100644 --- a/drivers/net/wireless/iwlwifi/iwl4965-base.c +++ b/drivers/net/wireless/iwlwifi/iwl4965-base.c | |||
@@ -879,6 +879,13 @@ static int iwl4965_commit_rxon(struct iwl_priv *priv) | |||
879 | return 0; | 879 | return 0; |
880 | } | 880 | } |
881 | 881 | ||
882 | void iwl4965_update_chain_flags(struct iwl_priv *priv) | ||
883 | { | ||
884 | |||
885 | iwl4965_set_rxon_chain(priv); | ||
886 | iwl4965_commit_rxon(priv); | ||
887 | } | ||
888 | |||
882 | static int iwl4965_send_bt_config(struct iwl_priv *priv) | 889 | static int iwl4965_send_bt_config(struct iwl_priv *priv) |
883 | { | 890 | { |
884 | struct iwl4965_bt_cmd bt_cmd = { | 891 | struct iwl4965_bt_cmd bt_cmd = { |
@@ -1366,184 +1373,6 @@ static void iwl4965_activate_qos(struct iwl_priv *priv, u8 force) | |||
1366 | } | 1373 | } |
1367 | } | 1374 | } |
1368 | 1375 | ||
1369 | /* | ||
1370 | * Power management (not Tx power!) functions | ||
1371 | */ | ||
1372 | #define MSEC_TO_USEC 1024 | ||
1373 | |||
1374 | #define NOSLP __constant_cpu_to_le16(0), 0, 0 | ||
1375 | #define SLP IWL_POWER_DRIVER_ALLOW_SLEEP_MSK, 0, 0 | ||
1376 | #define SLP_TIMEOUT(T) __constant_cpu_to_le32((T) * MSEC_TO_USEC) | ||
1377 | #define SLP_VEC(X0, X1, X2, X3, X4) {__constant_cpu_to_le32(X0), \ | ||
1378 | __constant_cpu_to_le32(X1), \ | ||
1379 | __constant_cpu_to_le32(X2), \ | ||
1380 | __constant_cpu_to_le32(X3), \ | ||
1381 | __constant_cpu_to_le32(X4)} | ||
1382 | |||
1383 | |||
1384 | /* default power management (not Tx power) table values */ | ||
1385 | /* for tim 0-10 */ | ||
1386 | static struct iwl4965_power_vec_entry range_0[IWL_POWER_AC] = { | ||
1387 | {{NOSLP, SLP_TIMEOUT(0), SLP_TIMEOUT(0), SLP_VEC(0, 0, 0, 0, 0)}, 0}, | ||
1388 | {{SLP, SLP_TIMEOUT(200), SLP_TIMEOUT(500), SLP_VEC(1, 2, 3, 4, 4)}, 0}, | ||
1389 | {{SLP, SLP_TIMEOUT(200), SLP_TIMEOUT(300), SLP_VEC(2, 4, 6, 7, 7)}, 0}, | ||
1390 | {{SLP, SLP_TIMEOUT(50), SLP_TIMEOUT(100), SLP_VEC(2, 6, 9, 9, 10)}, 0}, | ||
1391 | {{SLP, SLP_TIMEOUT(50), SLP_TIMEOUT(25), SLP_VEC(2, 7, 9, 9, 10)}, 1}, | ||
1392 | {{SLP, SLP_TIMEOUT(25), SLP_TIMEOUT(25), SLP_VEC(4, 7, 10, 10, 10)}, 1} | ||
1393 | }; | ||
1394 | |||
1395 | /* for tim > 10 */ | ||
1396 | static struct iwl4965_power_vec_entry range_1[IWL_POWER_AC] = { | ||
1397 | {{NOSLP, SLP_TIMEOUT(0), SLP_TIMEOUT(0), SLP_VEC(0, 0, 0, 0, 0)}, 0}, | ||
1398 | {{SLP, SLP_TIMEOUT(200), SLP_TIMEOUT(500), | ||
1399 | SLP_VEC(1, 2, 3, 4, 0xFF)}, 0}, | ||
1400 | {{SLP, SLP_TIMEOUT(200), SLP_TIMEOUT(300), | ||
1401 | SLP_VEC(2, 4, 6, 7, 0xFF)}, 0}, | ||
1402 | {{SLP, SLP_TIMEOUT(50), SLP_TIMEOUT(100), | ||
1403 | SLP_VEC(2, 6, 9, 9, 0xFF)}, 0}, | ||
1404 | {{SLP, SLP_TIMEOUT(50), SLP_TIMEOUT(25), SLP_VEC(2, 7, 9, 9, 0xFF)}, 0}, | ||
1405 | {{SLP, SLP_TIMEOUT(25), SLP_TIMEOUT(25), | ||
1406 | SLP_VEC(4, 7, 10, 10, 0xFF)}, 0} | ||
1407 | }; | ||
1408 | |||
1409 | int iwl4965_power_init_handle(struct iwl_priv *priv) | ||
1410 | { | ||
1411 | int rc = 0, i; | ||
1412 | struct iwl4965_power_mgr *pow_data; | ||
1413 | int size = sizeof(struct iwl4965_power_vec_entry) * IWL_POWER_AC; | ||
1414 | u16 pci_pm; | ||
1415 | |||
1416 | IWL_DEBUG_POWER("Initialize power \n"); | ||
1417 | |||
1418 | pow_data = &(priv->power_data); | ||
1419 | |||
1420 | memset(pow_data, 0, sizeof(*pow_data)); | ||
1421 | |||
1422 | pow_data->active_index = IWL_POWER_RANGE_0; | ||
1423 | pow_data->dtim_val = 0xffff; | ||
1424 | |||
1425 | memcpy(&pow_data->pwr_range_0[0], &range_0[0], size); | ||
1426 | memcpy(&pow_data->pwr_range_1[0], &range_1[0], size); | ||
1427 | |||
1428 | rc = pci_read_config_word(priv->pci_dev, PCI_LINK_CTRL, &pci_pm); | ||
1429 | if (rc != 0) | ||
1430 | return 0; | ||
1431 | else { | ||
1432 | struct iwl4965_powertable_cmd *cmd; | ||
1433 | |||
1434 | IWL_DEBUG_POWER("adjust power command flags\n"); | ||
1435 | |||
1436 | for (i = 0; i < IWL_POWER_AC; i++) { | ||
1437 | cmd = &pow_data->pwr_range_0[i].cmd; | ||
1438 | |||
1439 | if (pci_pm & 0x1) | ||
1440 | cmd->flags &= ~IWL_POWER_PCI_PM_MSK; | ||
1441 | else | ||
1442 | cmd->flags |= IWL_POWER_PCI_PM_MSK; | ||
1443 | } | ||
1444 | } | ||
1445 | return rc; | ||
1446 | } | ||
1447 | |||
1448 | static int iwl4965_update_power_cmd(struct iwl_priv *priv, | ||
1449 | struct iwl4965_powertable_cmd *cmd, u32 mode) | ||
1450 | { | ||
1451 | int rc = 0, i; | ||
1452 | u8 skip; | ||
1453 | u32 max_sleep = 0; | ||
1454 | struct iwl4965_power_vec_entry *range; | ||
1455 | u8 period = 0; | ||
1456 | struct iwl4965_power_mgr *pow_data; | ||
1457 | |||
1458 | if (mode > IWL_POWER_INDEX_5) { | ||
1459 | IWL_DEBUG_POWER("Error invalid power mode \n"); | ||
1460 | return -1; | ||
1461 | } | ||
1462 | pow_data = &(priv->power_data); | ||
1463 | |||
1464 | if (pow_data->active_index == IWL_POWER_RANGE_0) | ||
1465 | range = &pow_data->pwr_range_0[0]; | ||
1466 | else | ||
1467 | range = &pow_data->pwr_range_1[1]; | ||
1468 | |||
1469 | memcpy(cmd, &range[mode].cmd, sizeof(struct iwl4965_powertable_cmd)); | ||
1470 | |||
1471 | #ifdef IWL_MAC80211_DISABLE | ||
1472 | if (priv->assoc_network != NULL) { | ||
1473 | unsigned long flags; | ||
1474 | |||
1475 | period = priv->assoc_network->tim.tim_period; | ||
1476 | } | ||
1477 | #endif /*IWL_MAC80211_DISABLE */ | ||
1478 | skip = range[mode].no_dtim; | ||
1479 | |||
1480 | if (period == 0) { | ||
1481 | period = 1; | ||
1482 | skip = 0; | ||
1483 | } | ||
1484 | |||
1485 | if (skip == 0) { | ||
1486 | max_sleep = period; | ||
1487 | cmd->flags &= ~IWL_POWER_SLEEP_OVER_DTIM_MSK; | ||
1488 | } else { | ||
1489 | __le32 slp_itrvl = cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]; | ||
1490 | max_sleep = (le32_to_cpu(slp_itrvl) / period) * period; | ||
1491 | cmd->flags |= IWL_POWER_SLEEP_OVER_DTIM_MSK; | ||
1492 | } | ||
1493 | |||
1494 | for (i = 0; i < IWL_POWER_VEC_SIZE; i++) { | ||
1495 | if (le32_to_cpu(cmd->sleep_interval[i]) > max_sleep) | ||
1496 | cmd->sleep_interval[i] = cpu_to_le32(max_sleep); | ||
1497 | } | ||
1498 | |||
1499 | IWL_DEBUG_POWER("Flags value = 0x%08X\n", cmd->flags); | ||
1500 | IWL_DEBUG_POWER("Tx timeout = %u\n", le32_to_cpu(cmd->tx_data_timeout)); | ||
1501 | IWL_DEBUG_POWER("Rx timeout = %u\n", le32_to_cpu(cmd->rx_data_timeout)); | ||
1502 | IWL_DEBUG_POWER("Sleep interval vector = { %d , %d , %d , %d , %d }\n", | ||
1503 | le32_to_cpu(cmd->sleep_interval[0]), | ||
1504 | le32_to_cpu(cmd->sleep_interval[1]), | ||
1505 | le32_to_cpu(cmd->sleep_interval[2]), | ||
1506 | le32_to_cpu(cmd->sleep_interval[3]), | ||
1507 | le32_to_cpu(cmd->sleep_interval[4])); | ||
1508 | |||
1509 | return rc; | ||
1510 | } | ||
1511 | |||
1512 | static int iwl4965_send_power_mode(struct iwl_priv *priv, u32 mode) | ||
1513 | { | ||
1514 | u32 uninitialized_var(final_mode); | ||
1515 | int rc; | ||
1516 | struct iwl4965_powertable_cmd cmd; | ||
1517 | |||
1518 | /* If on battery, set to 3, | ||
1519 | * if plugged into AC power, set to CAM ("continuously aware mode"), | ||
1520 | * else user level */ | ||
1521 | switch (mode) { | ||
1522 | case IWL_POWER_BATTERY: | ||
1523 | final_mode = IWL_POWER_INDEX_3; | ||
1524 | break; | ||
1525 | case IWL_POWER_AC: | ||
1526 | final_mode = IWL_POWER_MODE_CAM; | ||
1527 | break; | ||
1528 | default: | ||
1529 | final_mode = mode; | ||
1530 | break; | ||
1531 | } | ||
1532 | |||
1533 | cmd.keep_alive_beacons = 0; | ||
1534 | |||
1535 | iwl4965_update_power_cmd(priv, &cmd, final_mode); | ||
1536 | |||
1537 | rc = iwl_send_cmd_pdu(priv, POWER_TABLE_CMD, sizeof(cmd), &cmd); | ||
1538 | |||
1539 | if (final_mode == IWL_POWER_MODE_CAM) | ||
1540 | clear_bit(STATUS_POWER_PMI, &priv->status); | ||
1541 | else | ||
1542 | set_bit(STATUS_POWER_PMI, &priv->status); | ||
1543 | |||
1544 | return rc; | ||
1545 | } | ||
1546 | |||
1547 | int iwl4965_is_network_packet(struct iwl_priv *priv, struct ieee80211_hdr *header) | 1376 | int iwl4965_is_network_packet(struct iwl_priv *priv, struct ieee80211_hdr *header) |
1548 | { | 1377 | { |
1549 | /* Filter incoming packets to determine if they are targeted toward | 1378 | /* Filter incoming packets to determine if they are targeted toward |
@@ -5304,8 +5133,6 @@ static void iwl4965_alive_start(struct iwl_priv *priv) | |||
5304 | priv->active_rate = priv->rates_mask; | 5133 | priv->active_rate = priv->rates_mask; |
5305 | priv->active_rate_basic = priv->rates_mask & IWL_BASIC_RATES_MASK; | 5134 | priv->active_rate_basic = priv->rates_mask & IWL_BASIC_RATES_MASK; |
5306 | 5135 | ||
5307 | iwl4965_send_power_mode(priv, IWL_POWER_LEVEL(priv->power_mode)); | ||
5308 | |||
5309 | if (iwl_is_associated(priv)) { | 5136 | if (iwl_is_associated(priv)) { |
5310 | struct iwl4965_rxon_cmd *active_rxon = | 5137 | struct iwl4965_rxon_cmd *active_rxon = |
5311 | (struct iwl4965_rxon_cmd *)(&priv->active_rxon); | 5138 | (struct iwl4965_rxon_cmd *)(&priv->active_rxon); |
@@ -5838,6 +5665,8 @@ static void iwl4965_bg_request_scan(struct work_struct *data) | |||
5838 | direct_mask, | 5665 | direct_mask, |
5839 | (void *)&scan->data[le16_to_cpu(scan->tx_cmd.len)]); | 5666 | (void *)&scan->data[le16_to_cpu(scan->tx_cmd.len)]); |
5840 | 5667 | ||
5668 | scan->filter_flags |= (RXON_FILTER_ACCEPT_GRP_MSK | | ||
5669 | RXON_FILTER_BCON_AWARE_MSK); | ||
5841 | cmd.len += le16_to_cpu(scan->tx_cmd.len) + | 5670 | cmd.len += le16_to_cpu(scan->tx_cmd.len) + |
5842 | scan->channel_count * sizeof(struct iwl4965_scan_channel); | 5671 | scan->channel_count * sizeof(struct iwl4965_scan_channel); |
5843 | cmd.data = scan; | 5672 | cmd.data = scan; |
@@ -6000,6 +5829,7 @@ static void iwl4965_post_associate(struct iwl_priv *priv) | |||
6000 | 5829 | ||
6001 | iwl4965_activate_qos(priv, 0); | 5830 | iwl4965_activate_qos(priv, 0); |
6002 | 5831 | ||
5832 | iwl_power_update_mode(priv, 0); | ||
6003 | /* we have just associated, don't start scan too early */ | 5833 | /* we have just associated, don't start scan too early */ |
6004 | priv->next_scan_jiffies = jiffies + IWL_DELAY_NEXT_SCAN; | 5834 | priv->next_scan_jiffies = jiffies + IWL_DELAY_NEXT_SCAN; |
6005 | } | 5835 | } |
@@ -6990,6 +6820,8 @@ static void iwl4965_mac_reset_tsf(struct ieee80211_hw *hw) | |||
6990 | iwl4965_commit_rxon(priv); | 6820 | iwl4965_commit_rxon(priv); |
6991 | } | 6821 | } |
6992 | 6822 | ||
6823 | iwl_power_update_mode(priv, 0); | ||
6824 | |||
6993 | /* Per mac80211.h: This is only used in IBSS mode... */ | 6825 | /* Per mac80211.h: This is only used in IBSS mode... */ |
6994 | if (priv->iw_mode != IEEE80211_IF_TYPE_IBSS) { | 6826 | if (priv->iw_mode != IEEE80211_IF_TYPE_IBSS) { |
6995 | 6827 | ||
@@ -7321,20 +7153,11 @@ static ssize_t store_power_level(struct device *d, | |||
7321 | goto out; | 7153 | goto out; |
7322 | } | 7154 | } |
7323 | 7155 | ||
7324 | if ((mode < 1) || (mode > IWL_POWER_LIMIT) || (mode == IWL_POWER_AC)) | 7156 | rc = iwl_power_set_user_mode(priv, mode); |
7325 | mode = IWL_POWER_AC; | 7157 | if (rc) { |
7326 | else | 7158 | IWL_DEBUG_MAC80211("failed setting power mode.\n"); |
7327 | mode |= IWL_POWER_ENABLED; | 7159 | goto out; |
7328 | |||
7329 | if (mode != priv->power_mode) { | ||
7330 | rc = iwl4965_send_power_mode(priv, IWL_POWER_LEVEL(mode)); | ||
7331 | if (rc) { | ||
7332 | IWL_DEBUG_MAC80211("failed setting power mode.\n"); | ||
7333 | goto out; | ||
7334 | } | ||
7335 | priv->power_mode = mode; | ||
7336 | } | 7160 | } |
7337 | |||
7338 | rc = count; | 7161 | rc = count; |
7339 | 7162 | ||
7340 | out: | 7163 | out: |
@@ -7364,7 +7187,7 @@ static ssize_t show_power_level(struct device *d, | |||
7364 | struct device_attribute *attr, char *buf) | 7187 | struct device_attribute *attr, char *buf) |
7365 | { | 7188 | { |
7366 | struct iwl_priv *priv = dev_get_drvdata(d); | 7189 | struct iwl_priv *priv = dev_get_drvdata(d); |
7367 | int level = IWL_POWER_LEVEL(priv->power_mode); | 7190 | int level = priv->power_data.power_mode; |
7368 | char *p = buf; | 7191 | char *p = buf; |
7369 | 7192 | ||
7370 | p += sprintf(p, "%d ", level); | 7193 | p += sprintf(p, "%d ", level); |
@@ -7382,14 +7205,14 @@ static ssize_t show_power_level(struct device *d, | |||
7382 | timeout_duration[level - 1] / 1000, | 7205 | timeout_duration[level - 1] / 1000, |
7383 | period_duration[level - 1] / 1000); | 7206 | period_duration[level - 1] / 1000); |
7384 | } | 7207 | } |
7385 | 7208 | /* | |
7386 | if (!(priv->power_mode & IWL_POWER_ENABLED)) | 7209 | if (!(priv->power_mode & IWL_POWER_ENABLED)) |
7387 | p += sprintf(p, " OFF\n"); | 7210 | p += sprintf(p, " OFF\n"); |
7388 | else | 7211 | else |
7389 | p += sprintf(p, " \n"); | 7212 | p += sprintf(p, " \n"); |
7390 | 7213 | */ | |
7214 | p += sprintf(p, " \n"); | ||
7391 | return (p - buf + 1); | 7215 | return (p - buf + 1); |
7392 | |||
7393 | } | 7216 | } |
7394 | 7217 | ||
7395 | static DEVICE_ATTR(power_level, S_IWUSR | S_IRUSR, show_power_level, | 7218 | static DEVICE_ATTR(power_level, S_IWUSR | S_IRUSR, show_power_level, |