aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/iwlwifi/mvm
diff options
context:
space:
mode:
authorAlexander Bondar <alexander.bondar@intel.com>2013-03-10 09:29:44 -0400
committerJohannes Berg <johannes.berg@intel.com>2013-07-16 09:13:23 -0400
commite811ada7a6a3f720c178ba29998ce9f9685f9df3 (patch)
tree726f979761d68443e4a90dd86d10dbf2231e8aee /drivers/net/wireless/iwlwifi/mvm
parent0c393d4eac31912ad6ea362d4f9bf78aa1fe9a69 (diff)
iwlwifi: mvm: Upgrade to a new power management uAPSD API
Change power management implementation to support new host-device API containing uAPSD parameters. Verify FW support for this new API. Use the new power table command (0xA9) to configure power management. Use the legacy command (0x77) if FW does not support the new API. New file power_legacy.c is introduced for legacy implementation. Signed-off-by: Alexander Bondar <alexander.bondar@intel.com> Reviewed-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'drivers/net/wireless/iwlwifi/mvm')
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/Makefile2
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/debugfs.c33
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-power.h75
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api.h5
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac80211.c11
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mvm.h46
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/ops.c6
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/power.c128
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/power_legacy.c319
9 files changed, 544 insertions, 81 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/Makefile b/drivers/net/wireless/iwlwifi/mvm/Makefile
index ff856e543ae8..6d73817850ce 100644
--- a/drivers/net/wireless/iwlwifi/mvm/Makefile
+++ b/drivers/net/wireless/iwlwifi/mvm/Makefile
@@ -2,7 +2,7 @@ obj-$(CONFIG_IWLMVM) += iwlmvm.o
2iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o 2iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o
3iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o 3iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o
4iwlmvm-y += scan.o time-event.o rs.o 4iwlmvm-y += scan.o time-event.o rs.o
5iwlmvm-y += power.o bt-coex.o 5iwlmvm-y += power.o power_legacy.o bt-coex.o
6iwlmvm-y += led.o tt.o 6iwlmvm-y += led.o tt.o
7iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o 7iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o
8iwlmvm-$(CONFIG_PM_SLEEP) += d3.o 8iwlmvm-$(CONFIG_PM_SLEEP) += d3.o
diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c
index e56ed2a84888..5d669da09afe 100644
--- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c
+++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c
@@ -424,40 +424,11 @@ static ssize_t iwl_dbgfs_pm_params_read(struct file *file,
424 struct ieee80211_vif *vif = file->private_data; 424 struct ieee80211_vif *vif = file->private_data;
425 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 425 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
426 struct iwl_mvm *mvm = mvmvif->dbgfs_data; 426 struct iwl_mvm *mvm = mvmvif->dbgfs_data;
427 struct iwl_powertable_cmd cmd = {};
428 char buf[256]; 427 char buf[256];
429 int bufsz = sizeof(buf); 428 int bufsz = sizeof(buf);
430 int pos = 0; 429 int pos;
431 430
432 iwl_mvm_power_build_cmd(mvm, vif, &cmd); 431 pos = iwl_mvm_power_dbgfs_read(mvm, vif, buf, bufsz);
433
434 pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off = %d\n",
435 (cmd.flags &
436 cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK)) ?
437 0 : 1);
438 pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n",
439 le32_to_cpu(cmd.skip_dtim_periods));
440 pos += scnprintf(buf+pos, bufsz-pos, "power_scheme = %d\n",
441 iwlmvm_mod_params.power_scheme);
442 pos += scnprintf(buf+pos, bufsz-pos, "flags = 0x%x\n",
443 le16_to_cpu(cmd.flags));
444 pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n",
445 cmd.keep_alive_seconds);
446
447 if (cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) {
448 pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n",
449 (cmd.flags &
450 cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ?
451 1 : 0);
452 pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout = %d\n",
453 le32_to_cpu(cmd.rx_data_timeout));
454 pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout = %d\n",
455 le32_to_cpu(cmd.tx_data_timeout));
456 if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
457 pos += scnprintf(buf+pos, bufsz-pos,
458 "lprx_rssi_threshold = %d\n",
459 le32_to_cpu(cmd.lprx_rssi_threshold));
460 }
461 432
462 return simple_read_from_buffer(user_buf, count, ppos, buf, pos); 433 return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
463} 434}
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
index a6da359a80c3..300211d79f2a 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
@@ -79,6 +79,10 @@
79 * '1' Driver enables PM (use rest of parameters) 79 * '1' Driver enables PM (use rest of parameters)
80 * @POWER_FLAGS_SKIP_OVER_DTIM_MSK: '0' PM have to walk up every DTIM, 80 * @POWER_FLAGS_SKIP_OVER_DTIM_MSK: '0' PM have to walk up every DTIM,
81 * '1' PM could sleep over DTIM till listen Interval. 81 * '1' PM could sleep over DTIM till listen Interval.
82 * @POWER_FLAGS_SNOOZE_ENA_MSK: Enable snoozing only if uAPSD is enabled and all
83 * access categories are both delivery and trigger enabled.
84 * @POWER_FLAGS_BT_SCO_ENA: Enable BT SCO coex only if uAPSD and
85 * PBW Snoozing enabled
82 * @POWER_FLAGS_ADVANCE_PM_ENA_MSK: Advanced PM (uAPSD) enable mask 86 * @POWER_FLAGS_ADVANCE_PM_ENA_MSK: Advanced PM (uAPSD) enable mask
83 * @POWER_FLAGS_LPRX_ENA_MSK: Low Power RX enable. 87 * @POWER_FLAGS_LPRX_ENA_MSK: Low Power RX enable.
84*/ 88*/
@@ -86,6 +90,8 @@ enum iwl_power_flags {
86 POWER_FLAGS_POWER_SAVE_ENA_MSK = BIT(0), 90 POWER_FLAGS_POWER_SAVE_ENA_MSK = BIT(0),
87 POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK = BIT(1), 91 POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK = BIT(1),
88 POWER_FLAGS_SKIP_OVER_DTIM_MSK = BIT(2), 92 POWER_FLAGS_SKIP_OVER_DTIM_MSK = BIT(2),
93 POWER_FLAGS_SNOOZE_ENA_MSK = BIT(5),
94 POWER_FLAGS_BT_SCO_ENA = BIT(8),
89 POWER_FLAGS_ADVANCE_PM_ENA_MSK = BIT(9), 95 POWER_FLAGS_ADVANCE_PM_ENA_MSK = BIT(9),
90 POWER_FLAGS_LPRX_ENA_MSK = BIT(11), 96 POWER_FLAGS_LPRX_ENA_MSK = BIT(11),
91}; 97};
@@ -93,7 +99,8 @@ enum iwl_power_flags {
93#define IWL_POWER_VEC_SIZE 5 99#define IWL_POWER_VEC_SIZE 5
94 100
95/** 101/**
96 * struct iwl_powertable_cmd - Power Table Command 102 * struct iwl_powertable_cmd - legacy power command. Beside old API support this
103 * is used also with a new power API for device wide power settings.
97 * POWER_TABLE_CMD = 0x77 (command, has simple generic response) 104 * POWER_TABLE_CMD = 0x77 (command, has simple generic response)
98 * 105 *
99 * @flags: Power table command flags from POWER_FLAGS_* 106 * @flags: Power table command flags from POWER_FLAGS_*
@@ -125,6 +132,72 @@ struct iwl_powertable_cmd {
125} __packed; 132} __packed;
126 133
127/** 134/**
135 * struct iwl_mac_power_cmd - New power command containing uAPSD support
136 * MAC_PM_POWER_TABLE = 0xA9 (command, has simple generic response)
137 * @id_and_color: MAC contex identifier
138 * @flags: Power table command flags from POWER_FLAGS_*
139 * @keep_alive_seconds: Keep alive period in seconds. Default - 25 sec.
140 * Minimum allowed:- 3 * DTIM. Keep alive period must be
141 * set regardless of power scheme or current power state.
142 * FW use this value also when PM is disabled.
143 * @rx_data_timeout: Minimum time (usec) from last Rx packet for AM to
144 * PSM transition - legacy PM
145 * @tx_data_timeout: Minimum time (usec) from last Tx packet for AM to
146 * PSM transition - legacy PM
147 * @sleep_interval: not in use
148 * @skip_dtim_periods: Number of DTIM periods to skip if Skip over DTIM flag
149 * is set. For example, if it is required to skip over
150 * one DTIM, this value need to be set to 2 (DTIM periods).
151 * @rx_data_timeout_uapsd: Minimum time (usec) from last Rx packet for AM to
152 * PSM transition - uAPSD
153 * @tx_data_timeout_uapsd: Minimum time (usec) from last Tx packet for AM to
154 * PSM transition - uAPSD
155 * @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled.
156 * Default: 80dbm
157 * @num_skip_dtim: Number of DTIMs to skip if Skip over DTIM flag is set
158 * @snooze_interval: TBD
159 * @snooze_window: TBD
160 * @snooze_step: TBD
161 * @qndp_tid: TID client shall use for uAPSD QNDP triggers
162 * @uapsd_ac_flags: Set trigger-enabled and delivery-enabled indication for
163 * each corresponding AC.
164 * Use IEEE80211_WMM_IE_STA_QOSINFO_AC* for correct values.
165 * @uapsd_max_sp: Use IEEE80211_WMM_IE_STA_QOSINFO_SP_* for correct
166 * values.
167 * @heavy_traffic_thr_tx_pkts: TX threshold measured in number of packets
168 * @heavy_traffic_thr_rx_pkts: RX threshold measured in number of packets
169 * @heavy_traffic_thr_tx_load: TX threshold measured in load's percentage
170 * @heavy_traffic_thr_rx_load: RX threshold measured in load's percentage
171 * @limited_ps_threshold:
172*/
173struct iwl_mac_power_cmd {
174 /* CONTEXT_DESC_API_T_VER_1 */
175 __le32 id_and_color;
176
177 /* CLIENT_PM_POWER_TABLE_S_VER_1 */
178 __le16 flags;
179 __le16 keep_alive_seconds;
180 __le32 rx_data_timeout;
181 __le32 tx_data_timeout;
182 __le32 rx_data_timeout_uapsd;
183 __le32 tx_data_timeout_uapsd;
184 u8 lprx_rssi_threshold;
185 u8 skip_dtim_periods;
186 __le16 snooze_interval;
187 __le16 snooze_window;
188 u8 snooze_step;
189 u8 qndp_tid;
190 u8 uapsd_ac_flags;
191 u8 uapsd_max_sp;
192 u8 heavy_traffic_threshold_tx_packets;
193 u8 heavy_traffic_threshold_rx_packets;
194 u8 heavy_traffic_threshold_tx_percentage;
195 u8 heavy_traffic_threshold_rx_percentage;
196 u8 limited_ps_threshold;
197 u8 reserved;
198} __packed;
199
200/**
128 * struct iwl_beacon_filter_cmd 201 * struct iwl_beacon_filter_cmd
129 * REPLY_BEACON_FILTERING_CMD = 0xd2 (command) 202 * REPLY_BEACON_FILTERING_CMD = 0xd2 (command)
130 * @id_and_color: MAC contex identifier 203 * @id_and_color: MAC contex identifier
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
index cbfb3beae783..44614f5e4485 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
@@ -136,7 +136,7 @@ enum {
136 CALIB_RES_NOTIF_PHY_DB = 0x6b, 136 CALIB_RES_NOTIF_PHY_DB = 0x6b,
137 /* PHY_DB_CMD = 0x6c, */ 137 /* PHY_DB_CMD = 0x6c, */
138 138
139 /* Power */ 139 /* Power - legacy power table command */
140 POWER_TABLE_CMD = 0x77, 140 POWER_TABLE_CMD = 0x77,
141 141
142 /* Thermal Throttling*/ 142 /* Thermal Throttling*/
@@ -166,6 +166,9 @@ enum {
166 166
167 MISSED_BEACONS_NOTIFICATION = 0xa2, 167 MISSED_BEACONS_NOTIFICATION = 0xa2,
168 168
169 /* Power - new power table command */
170 MAC_PM_POWER_TABLE = 0xa9,
171
169 REPLY_RX_PHY_CMD = 0xc0, 172 REPLY_RX_PHY_CMD = 0xc0,
170 REPLY_RX_MPDU_CMD = 0xc1, 173 REPLY_RX_MPDU_CMD = 0xc1,
171 BA_NOTIF = 0xc5, 174 BA_NOTIF = 0xc5,
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index e08683b20531..2d07605eaabf 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -774,9 +774,14 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
774 if (ret) 774 if (ret)
775 IWL_ERR(mvm, "failed to update quotas\n"); 775 IWL_ERR(mvm, "failed to update quotas\n");
776 } 776 }
777 ret = iwl_mvm_power_update_mode(mvm, vif); 777 if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD)) {
778 if (ret) 778 /* Workaround for FW bug, otherwise FW disables device
779 IWL_ERR(mvm, "failed to update power mode\n"); 779 * power save upon disassociation
780 */
781 ret = iwl_mvm_power_update_mode(mvm, vif);
782 if (ret)
783 IWL_ERR(mvm, "failed to update power mode\n");
784 }
780 } else if (changes & BSS_CHANGED_BEACON_INFO) { 785 } else if (changes & BSS_CHANGED_BEACON_INFO) {
781 /* 786 /*
782 * We received a beacon _after_ association so 787 * We received a beacon _after_ association so
diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h
index 3aaecbcdc551..caa6a1758172 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h
@@ -91,6 +91,9 @@ enum iwl_mvm_tx_fifo {
91}; 91};
92 92
93extern struct ieee80211_ops iwl_mvm_hw_ops; 93extern struct ieee80211_ops iwl_mvm_hw_ops;
94extern const struct iwl_mvm_power_ops pm_legacy_ops;
95extern const struct iwl_mvm_power_ops pm_mac_ops;
96
94/** 97/**
95 * struct iwl_mvm_mod_params - module parameters for iwlmvm 98 * struct iwl_mvm_mod_params - module parameters for iwlmvm
96 * @init_dbg: if true, then the NIC won't be stopped if the INIT fw asserted. 99 * @init_dbg: if true, then the NIC won't be stopped if the INIT fw asserted.
@@ -150,6 +153,17 @@ enum iwl_power_scheme {
150 153
151#define IWL_CONN_MAX_LISTEN_INTERVAL 70 154#define IWL_CONN_MAX_LISTEN_INTERVAL 70
152 155
156struct iwl_mvm_power_ops {
157 int (*power_update_mode)(struct iwl_mvm *mvm,
158 struct ieee80211_vif *vif);
159 int (*power_disable)(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
160#ifdef CONFIG_IWLWIFI_DEBUGFS
161 int (*power_dbgfs_read)(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
162 char *buf, int bufsz);
163#endif
164};
165
166
153#ifdef CONFIG_IWLWIFI_DEBUGFS 167#ifdef CONFIG_IWLWIFI_DEBUGFS
154enum iwl_dbgfs_pm_mask { 168enum iwl_dbgfs_pm_mask {
155 MVM_DEBUGFS_PM_KEEP_ALIVE = BIT(0), 169 MVM_DEBUGFS_PM_KEEP_ALIVE = BIT(0),
@@ -163,7 +177,7 @@ enum iwl_dbgfs_pm_mask {
163}; 177};
164 178
165struct iwl_dbgfs_pm { 179struct iwl_dbgfs_pm {
166 u8 keep_alive_seconds; 180 u16 keep_alive_seconds;
167 u32 rx_data_timeout; 181 u32 rx_data_timeout;
168 u32 tx_data_timeout; 182 u32 tx_data_timeout;
169 bool skip_over_dtim; 183 bool skip_over_dtim;
@@ -481,6 +495,8 @@ struct iwl_mvm {
481 /* Thermal Throttling and CTkill */ 495 /* Thermal Throttling and CTkill */
482 struct iwl_mvm_tt_mgmt thermal_throttle; 496 struct iwl_mvm_tt_mgmt thermal_throttle;
483 s32 temperature; /* Celsius */ 497 s32 temperature; /* Celsius */
498
499 const struct iwl_mvm_power_ops *pm_ops;
484}; 500};
485 501
486/* Extract MVM priv from op_mode and _hw */ 502/* Extract MVM priv from op_mode and _hw */
@@ -660,10 +676,26 @@ int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq,
660 u8 flags, bool init); 676 u8 flags, bool init);
661 677
662/* power managment */ 678/* power managment */
663int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif); 679static inline int iwl_mvm_power_update_mode(struct iwl_mvm *mvm,
664int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif); 680 struct ieee80211_vif *vif)
665void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 681{
666 struct iwl_powertable_cmd *cmd); 682 return mvm->pm_ops->power_update_mode(mvm, vif);
683}
684
685static inline int iwl_mvm_power_disable(struct iwl_mvm *mvm,
686 struct ieee80211_vif *vif)
687{
688 return mvm->pm_ops->power_disable(mvm, vif);
689}
690
691#ifdef CONFIG_IWLWIFI_DEBUGFS
692static inline int iwl_mvm_power_dbgfs_read(struct iwl_mvm *mvm,
693 struct ieee80211_vif *vif,
694 char *buf, int bufsz)
695{
696 return mvm->pm_ops->power_dbgfs_read(mvm, vif, buf, bufsz);
697}
698#endif
667 699
668int iwl_mvm_leds_init(struct iwl_mvm *mvm); 700int iwl_mvm_leds_init(struct iwl_mvm *mvm);
669void iwl_mvm_leds_exit(struct iwl_mvm *mvm); 701void iwl_mvm_leds_exit(struct iwl_mvm *mvm);
@@ -707,6 +739,10 @@ int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
707 struct ieee80211_vif *vif); 739 struct ieee80211_vif *vif);
708int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, 740int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
709 struct ieee80211_vif *vif); 741 struct ieee80211_vif *vif);
742int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm,
743 struct iwl_beacon_filter_cmd *cmd);
744int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm,
745 struct ieee80211_vif *vif, bool enable);
710 746
711/* SMPS */ 747/* SMPS */
712void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 748void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c
index 0a4eb278f789..fa1e1ce9f2be 100644
--- a/drivers/net/wireless/iwlwifi/mvm/ops.c
+++ b/drivers/net/wireless/iwlwifi/mvm/ops.c
@@ -301,6 +301,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
301 CMD(MCAST_FILTER_CMD), 301 CMD(MCAST_FILTER_CMD),
302 CMD(REPLY_BEACON_FILTERING_CMD), 302 CMD(REPLY_BEACON_FILTERING_CMD),
303 CMD(REPLY_THERMAL_MNG_BACKOFF), 303 CMD(REPLY_THERMAL_MNG_BACKOFF),
304 CMD(MAC_PM_POWER_TABLE),
304}; 305};
305#undef CMD 306#undef CMD
306 307
@@ -431,6 +432,11 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
431 if (err) 432 if (err)
432 goto out_unregister; 433 goto out_unregister;
433 434
435 if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD)
436 mvm->pm_ops = &pm_mac_ops;
437 else
438 mvm->pm_ops = &pm_legacy_ops;
439
434 return op_mode; 440 return op_mode;
435 441
436 out_unregister: 442 out_unregister:
diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c
index e7ca965a89b8..644bf476921a 100644
--- a/drivers/net/wireless/iwlwifi/mvm/power.c
+++ b/drivers/net/wireless/iwlwifi/mvm/power.c
@@ -75,8 +75,8 @@
75 75
76#define POWER_KEEP_ALIVE_PERIOD_SEC 25 76#define POWER_KEEP_ALIVE_PERIOD_SEC 25
77 77
78static int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm, 78int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm,
79 struct iwl_beacon_filter_cmd *cmd) 79 struct iwl_beacon_filter_cmd *cmd)
80{ 80{
81 int ret; 81 int ret;
82 82
@@ -106,8 +106,8 @@ static int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm,
106 return ret; 106 return ret;
107} 107}
108 108
109static int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm, 109int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm,
110 struct ieee80211_vif *vif, bool enable) 110 struct ieee80211_vif *vif, bool enable)
111{ 111{
112 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 112 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
113 struct iwl_beacon_filter_cmd cmd = { 113 struct iwl_beacon_filter_cmd cmd = {
@@ -124,13 +124,14 @@ static int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm,
124} 124}
125 125
126static void iwl_mvm_power_log(struct iwl_mvm *mvm, 126static void iwl_mvm_power_log(struct iwl_mvm *mvm,
127 struct iwl_powertable_cmd *cmd) 127 struct iwl_mac_power_cmd *cmd)
128{ 128{
129 IWL_DEBUG_POWER(mvm, 129 IWL_DEBUG_POWER(mvm,
130 "Sending power table command for power level %d, flags = 0x%X\n", 130 "Sending power table command on mac id 0x%X for power level %d, flags = 0x%X\n",
131 iwlmvm_mod_params.power_scheme, 131 cmd->id_and_color, iwlmvm_mod_params.power_scheme,
132 le16_to_cpu(cmd->flags)); 132 le16_to_cpu(cmd->flags));
133 IWL_DEBUG_POWER(mvm, "Keep alive = %u sec\n", cmd->keep_alive_seconds); 133 IWL_DEBUG_POWER(mvm, "Keep alive = %u sec\n",
134 le16_to_cpu(cmd->keep_alive_seconds));
134 135
135 if (cmd->flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) { 136 if (cmd->flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) {
136 IWL_DEBUG_POWER(mvm, "Rx timeout = %u usec\n", 137 IWL_DEBUG_POWER(mvm, "Rx timeout = %u usec\n",
@@ -139,15 +140,16 @@ static void iwl_mvm_power_log(struct iwl_mvm *mvm,
139 le32_to_cpu(cmd->tx_data_timeout)); 140 le32_to_cpu(cmd->tx_data_timeout));
140 if (cmd->flags & cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) 141 if (cmd->flags & cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK))
141 IWL_DEBUG_POWER(mvm, "DTIM periods to skip = %u\n", 142 IWL_DEBUG_POWER(mvm, "DTIM periods to skip = %u\n",
142 le32_to_cpu(cmd->skip_dtim_periods)); 143 cmd->skip_dtim_periods);
143 if (cmd->flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK)) 144 if (cmd->flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
144 IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n", 145 IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n",
145 le32_to_cpu(cmd->lprx_rssi_threshold)); 146 cmd->lprx_rssi_threshold);
146 } 147 }
147} 148}
148 149
149void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 150static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
150 struct iwl_powertable_cmd *cmd) 151 struct ieee80211_vif *vif,
152 struct iwl_mac_power_cmd *cmd)
151{ 153{
152 struct ieee80211_hw *hw = mvm->hw; 154 struct ieee80211_hw *hw = mvm->hw;
153 struct ieee80211_chanctx_conf *chanctx_conf; 155 struct ieee80211_chanctx_conf *chanctx_conf;
@@ -158,19 +160,26 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
158 struct iwl_mvm_vif *mvmvif __maybe_unused = 160 struct iwl_mvm_vif *mvmvif __maybe_unused =
159 iwl_mvm_vif_from_mac80211(vif); 161 iwl_mvm_vif_from_mac80211(vif);
160 162
163 cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
164 mvmvif->color));
165 dtimper = hw->conf.ps_dtim_period ?: 1;
166
161 /* 167 /*
162 * Regardless of power management state the driver must set 168 * Regardless of power management state the driver must set
163 * keep alive period. FW will use it for sending keep alive NDPs 169 * keep alive period. FW will use it for sending keep alive NDPs
164 * immediately after association. 170 * immediately after association. Check that keep alive period
171 * is at least 3 * DTIM
165 */ 172 */
166 cmd->keep_alive_seconds = POWER_KEEP_ALIVE_PERIOD_SEC; 173 dtimper_msec = dtimper * vif->bss_conf.beacon_int;
174 keep_alive = max_t(int, 3 * dtimper_msec,
175 MSEC_PER_SEC * POWER_KEEP_ALIVE_PERIOD_SEC);
176 keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC);
177 cmd->keep_alive_seconds = cpu_to_le16(keep_alive);
167 178
168 if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM) 179 if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)
169 return; 180 return;
170 181
171 cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK); 182 cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
172 if (!vif->bss_conf.assoc)
173 cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
174 183
175#ifdef CONFIG_IWLWIFI_DEBUGFS 184#ifdef CONFIG_IWLWIFI_DEBUGFS
176 if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF && 185 if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF &&
@@ -186,12 +195,9 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
186 (vif->bss_conf.beacon_rate->bitrate == 10 || 195 (vif->bss_conf.beacon_rate->bitrate == 10 ||
187 vif->bss_conf.beacon_rate->bitrate == 60)) { 196 vif->bss_conf.beacon_rate->bitrate == 60)) {
188 cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK); 197 cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
189 cmd->lprx_rssi_threshold = 198 cmd->lprx_rssi_threshold = POWER_LPRX_RSSI_THRESHOLD;
190 cpu_to_le32(POWER_LPRX_RSSI_THRESHOLD);
191 } 199 }
192 200
193 dtimper = hw->conf.ps_dtim_period ?: 1;
194
195 /* Check if radar detection is required on current channel */ 201 /* Check if radar detection is required on current channel */
196 rcu_read_lock(); 202 rcu_read_lock();
197 chanctx_conf = rcu_dereference(vif->chanctx_conf); 203 chanctx_conf = rcu_dereference(vif->chanctx_conf);
@@ -207,16 +213,9 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
207 (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP || 213 (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP ||
208 mvm->cur_ucode == IWL_UCODE_WOWLAN)) { 214 mvm->cur_ucode == IWL_UCODE_WOWLAN)) {
209 cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); 215 cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
210 cmd->skip_dtim_periods = cpu_to_le32(3); 216 cmd->skip_dtim_periods = 3;
211 } 217 }
212 218
213 /* Check that keep alive period is at least 3 * DTIM */
214 dtimper_msec = dtimper * vif->bss_conf.beacon_int;
215 keep_alive = max_t(int, 3 * dtimper_msec,
216 MSEC_PER_SEC * cmd->keep_alive_seconds);
217 keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC);
218 cmd->keep_alive_seconds = keep_alive;
219
220 if (mvm->cur_ucode != IWL_UCODE_WOWLAN) { 219 if (mvm->cur_ucode != IWL_UCODE_WOWLAN) {
221 cmd->rx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC); 220 cmd->rx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC);
222 cmd->tx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC); 221 cmd->tx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC);
@@ -227,7 +226,8 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
227 226
228#ifdef CONFIG_IWLWIFI_DEBUGFS 227#ifdef CONFIG_IWLWIFI_DEBUGFS
229 if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE) 228 if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE)
230 cmd->keep_alive_seconds = mvmvif->dbgfs_pm.keep_alive_seconds; 229 cmd->keep_alive_seconds =
230 cpu_to_le16(mvmvif->dbgfs_pm.keep_alive_seconds);
231 if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_OVER_DTIM) { 231 if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_OVER_DTIM) {
232 if (mvmvif->dbgfs_pm.skip_over_dtim) 232 if (mvmvif->dbgfs_pm.skip_over_dtim)
233 cmd->flags |= 233 cmd->flags |=
@@ -243,8 +243,7 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
243 cmd->tx_data_timeout = 243 cmd->tx_data_timeout =
244 cpu_to_le32(mvmvif->dbgfs_pm.tx_data_timeout); 244 cpu_to_le32(mvmvif->dbgfs_pm.tx_data_timeout);
245 if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS) 245 if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS)
246 cmd->skip_dtim_periods = 246 cmd->skip_dtim_periods = mvmvif->dbgfs_pm.skip_dtim_periods;
247 cpu_to_le32(mvmvif->dbgfs_pm.skip_dtim_periods);
248 if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_ENA) { 247 if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_ENA) {
249 if (mvmvif->dbgfs_pm.lprx_ena) 248 if (mvmvif->dbgfs_pm.lprx_ena)
250 cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK); 249 cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
@@ -252,16 +251,16 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
252 cmd->flags &= cpu_to_le16(~POWER_FLAGS_LPRX_ENA_MSK); 251 cmd->flags &= cpu_to_le16(~POWER_FLAGS_LPRX_ENA_MSK);
253 } 252 }
254 if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD) 253 if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD)
255 cmd->lprx_rssi_threshold = 254 cmd->lprx_rssi_threshold = mvmvif->dbgfs_pm.lprx_rssi_threshold;
256 cpu_to_le32(mvmvif->dbgfs_pm.lprx_rssi_threshold);
257#endif /* CONFIG_IWLWIFI_DEBUGFS */ 255#endif /* CONFIG_IWLWIFI_DEBUGFS */
258} 256}
259 257
260int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif) 258static int iwl_mvm_power_mac_update_mode(struct iwl_mvm *mvm,
259 struct ieee80211_vif *vif)
261{ 260{
262 int ret; 261 int ret;
263 bool ba_enable; 262 bool ba_enable;
264 struct iwl_powertable_cmd cmd = {}; 263 struct iwl_mac_power_cmd cmd = {};
265 264
266 if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) 265 if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
267 return 0; 266 return 0;
@@ -280,7 +279,7 @@ int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
280 iwl_mvm_power_build_cmd(mvm, vif, &cmd); 279 iwl_mvm_power_build_cmd(mvm, vif, &cmd);
281 iwl_mvm_power_log(mvm, &cmd); 280 iwl_mvm_power_log(mvm, &cmd);
282 281
283 ret = iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC, 282 ret = iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, CMD_SYNC,
284 sizeof(cmd), &cmd); 283 sizeof(cmd), &cmd);
285 if (ret) 284 if (ret)
286 return ret; 285 return ret;
@@ -291,15 +290,19 @@ int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
291 return iwl_mvm_update_beacon_abort(mvm, vif, ba_enable); 290 return iwl_mvm_update_beacon_abort(mvm, vif, ba_enable);
292} 291}
293 292
294int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif) 293static int iwl_mvm_power_mac_disable(struct iwl_mvm *mvm,
294 struct ieee80211_vif *vif)
295{ 295{
296 struct iwl_powertable_cmd cmd = {}; 296 struct iwl_mac_power_cmd cmd = {};
297 struct iwl_mvm_vif *mvmvif __maybe_unused = 297 struct iwl_mvm_vif *mvmvif __maybe_unused =
298 iwl_mvm_vif_from_mac80211(vif); 298 iwl_mvm_vif_from_mac80211(vif);
299 299
300 if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) 300 if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
301 return 0; 301 return 0;
302 302
303 cmd.id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
304 mvmvif->color));
305
303 if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM) 306 if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
304 cmd.flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK); 307 cmd.flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
305 308
@@ -310,11 +313,50 @@ int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
310#endif 313#endif
311 iwl_mvm_power_log(mvm, &cmd); 314 iwl_mvm_power_log(mvm, &cmd);
312 315
313 return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_ASYNC, 316 return iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, CMD_ASYNC,
314 sizeof(cmd), &cmd); 317 sizeof(cmd), &cmd);
315} 318}
316 319
317#ifdef CONFIG_IWLWIFI_DEBUGFS 320#ifdef CONFIG_IWLWIFI_DEBUGFS
321static int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm,
322 struct ieee80211_vif *vif, char *buf,
323 int bufsz)
324{
325 struct iwl_mac_power_cmd cmd = {};
326 int pos = 0;
327
328 iwl_mvm_power_build_cmd(mvm, vif, &cmd);
329
330 pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off = %d\n",
331 (cmd.flags &
332 cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK)) ?
333 0 : 1);
334 pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n",
335 cmd.skip_dtim_periods);
336 pos += scnprintf(buf+pos, bufsz-pos, "power_scheme = %d\n",
337 iwlmvm_mod_params.power_scheme);
338 pos += scnprintf(buf+pos, bufsz-pos, "flags = 0x%x\n",
339 le16_to_cpu(cmd.flags));
340 pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n",
341 le16_to_cpu(cmd.keep_alive_seconds));
342
343 if (cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) {
344 pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n",
345 (cmd.flags &
346 cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ?
347 1 : 0);
348 pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout = %d\n",
349 le32_to_cpu(cmd.rx_data_timeout));
350 pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout = %d\n",
351 le32_to_cpu(cmd.tx_data_timeout));
352 if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
353 pos += scnprintf(buf+pos, bufsz-pos,
354 "lprx_rssi_threshold = %d\n",
355 cmd.lprx_rssi_threshold);
356 }
357 return pos;
358}
359
318void 360void
319iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif, 361iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif,
320 struct iwl_beacon_filter_cmd *cmd) 362 struct iwl_beacon_filter_cmd *cmd)
@@ -382,3 +424,11 @@ int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
382 424
383 return ret; 425 return ret;
384} 426}
427
428const struct iwl_mvm_power_ops pm_mac_ops = {
429 .power_update_mode = iwl_mvm_power_mac_update_mode,
430 .power_disable = iwl_mvm_power_mac_disable,
431#ifdef CONFIG_IWLWIFI_DEBUGFS
432 .power_dbgfs_read = iwl_mvm_power_mac_dbgfs_read,
433#endif
434};
diff --git a/drivers/net/wireless/iwlwifi/mvm/power_legacy.c b/drivers/net/wireless/iwlwifi/mvm/power_legacy.c
new file mode 100644
index 000000000000..2ce79bad5845
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/power_legacy.c
@@ -0,0 +1,319 @@
1/******************************************************************************
2 *
3 * This file is provided under a dual BSD/GPLv2 license. When using or
4 * redistributing this file, you may do so under either license.
5 *
6 * GPL LICENSE SUMMARY
7 *
8 * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of version 2 of the GNU General Public License as
12 * published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
22 * USA
23 *
24 * The full GNU General Public License is included in this distribution
25 * in the file called COPYING.
26 *
27 * Contact Information:
28 * Intel Linux Wireless <ilw@linux.intel.com>
29 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
30 *
31 * BSD LICENSE
32 *
33 * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
34 * All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 *
40 * * Redistributions of source code must retain the above copyright
41 * notice, this list of conditions and the following disclaimer.
42 * * Redistributions in binary form must reproduce the above copyright
43 * notice, this list of conditions and the following disclaimer in
44 * the documentation and/or other materials provided with the
45 * distribution.
46 * * Neither the name Intel Corporation nor the names of its
47 * contributors may be used to endorse or promote products derived
48 * from this software without specific prior written permission.
49 *
50 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
51 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
52 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
53 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
54 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
55 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
56 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
57 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
58 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
59 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
60 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
61 *
62 *****************************************************************************/
63
64#include <linux/kernel.h>
65#include <linux/module.h>
66#include <linux/slab.h>
67#include <linux/init.h>
68
69#include <net/mac80211.h>
70
71#include "iwl-debug.h"
72#include "mvm.h"
73#include "iwl-modparams.h"
74#include "fw-api-power.h"
75
76#define POWER_KEEP_ALIVE_PERIOD_SEC 25
77
78static void iwl_mvm_power_log(struct iwl_mvm *mvm,
79 struct iwl_powertable_cmd *cmd)
80{
81 IWL_DEBUG_POWER(mvm,
82 "Sending power table command for power level %d, flags = 0x%X\n",
83 iwlmvm_mod_params.power_scheme,
84 le16_to_cpu(cmd->flags));
85 IWL_DEBUG_POWER(mvm, "Keep alive = %u sec\n", cmd->keep_alive_seconds);
86
87 if (cmd->flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) {
88 IWL_DEBUG_POWER(mvm, "Rx timeout = %u usec\n",
89 le32_to_cpu(cmd->rx_data_timeout));
90 IWL_DEBUG_POWER(mvm, "Tx timeout = %u usec\n",
91 le32_to_cpu(cmd->tx_data_timeout));
92 if (cmd->flags & cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK))
93 IWL_DEBUG_POWER(mvm, "DTIM periods to skip = %u\n",
94 le32_to_cpu(cmd->skip_dtim_periods));
95 if (cmd->flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
96 IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n",
97 le32_to_cpu(cmd->lprx_rssi_threshold));
98 }
99}
100
101static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
102 struct ieee80211_vif *vif,
103 struct iwl_powertable_cmd *cmd)
104{
105 struct ieee80211_hw *hw = mvm->hw;
106 struct ieee80211_chanctx_conf *chanctx_conf;
107 struct ieee80211_channel *chan;
108 int dtimper, dtimper_msec;
109 int keep_alive;
110 bool radar_detect = false;
111 struct iwl_mvm_vif *mvmvif __maybe_unused =
112 iwl_mvm_vif_from_mac80211(vif);
113
114 /*
115 * Regardless of power management state the driver must set
116 * keep alive period. FW will use it for sending keep alive NDPs
117 * immediately after association.
118 */
119 cmd->keep_alive_seconds = POWER_KEEP_ALIVE_PERIOD_SEC;
120
121 if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)
122 return;
123
124 cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
125 if (!vif->bss_conf.assoc)
126 cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
127
128#ifdef CONFIG_IWLWIFI_DEBUGFS
129 if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF &&
130 mvmvif->dbgfs_pm.disable_power_off)
131 cmd->flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK);
132#endif
133 if (!vif->bss_conf.ps)
134 return;
135
136 cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
137
138 if (vif->bss_conf.beacon_rate &&
139 (vif->bss_conf.beacon_rate->bitrate == 10 ||
140 vif->bss_conf.beacon_rate->bitrate == 60)) {
141 cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
142 cmd->lprx_rssi_threshold =
143 cpu_to_le32(POWER_LPRX_RSSI_THRESHOLD);
144 }
145
146 dtimper = hw->conf.ps_dtim_period ?: 1;
147
148 /* Check if radar detection is required on current channel */
149 rcu_read_lock();
150 chanctx_conf = rcu_dereference(vif->chanctx_conf);
151 WARN_ON(!chanctx_conf);
152 if (chanctx_conf) {
153 chan = chanctx_conf->def.chan;
154 radar_detect = chan->flags & IEEE80211_CHAN_RADAR;
155 }
156 rcu_read_unlock();
157
158 /* Check skip over DTIM conditions */
159 if (!radar_detect && (dtimper <= 10) &&
160 (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP ||
161 mvm->cur_ucode == IWL_UCODE_WOWLAN)) {
162 cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
163 cmd->skip_dtim_periods = cpu_to_le32(3);
164 }
165
166 /* Check that keep alive period is at least 3 * DTIM */
167 dtimper_msec = dtimper * vif->bss_conf.beacon_int;
168 keep_alive = max_t(int, 3 * dtimper_msec,
169 MSEC_PER_SEC * cmd->keep_alive_seconds);
170 keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC);
171 cmd->keep_alive_seconds = keep_alive;
172
173 if (mvm->cur_ucode != IWL_UCODE_WOWLAN) {
174 cmd->rx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC);
175 cmd->tx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC);
176 } else {
177 cmd->rx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC);
178 cmd->tx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC);
179 }
180
181#ifdef CONFIG_IWLWIFI_DEBUGFS
182 if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE)
183 cmd->keep_alive_seconds = mvmvif->dbgfs_pm.keep_alive_seconds;
184 if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_OVER_DTIM) {
185 if (mvmvif->dbgfs_pm.skip_over_dtim)
186 cmd->flags |=
187 cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
188 else
189 cmd->flags &=
190 cpu_to_le16(~POWER_FLAGS_SKIP_OVER_DTIM_MSK);
191 }
192 if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_RX_DATA_TIMEOUT)
193 cmd->rx_data_timeout =
194 cpu_to_le32(mvmvif->dbgfs_pm.rx_data_timeout);
195 if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_TX_DATA_TIMEOUT)
196 cmd->tx_data_timeout =
197 cpu_to_le32(mvmvif->dbgfs_pm.tx_data_timeout);
198 if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS)
199 cmd->skip_dtim_periods =
200 cpu_to_le32(mvmvif->dbgfs_pm.skip_dtim_periods);
201 if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_ENA) {
202 if (mvmvif->dbgfs_pm.lprx_ena)
203 cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
204 else
205 cmd->flags &= cpu_to_le16(~POWER_FLAGS_LPRX_ENA_MSK);
206 }
207 if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD)
208 cmd->lprx_rssi_threshold =
209 cpu_to_le32(mvmvif->dbgfs_pm.lprx_rssi_threshold);
210#endif /* CONFIG_IWLWIFI_DEBUGFS */
211}
212
213static int iwl_mvm_power_legacy_update_mode(struct iwl_mvm *mvm,
214 struct ieee80211_vif *vif)
215{
216 int ret;
217 bool ba_enable;
218 struct iwl_powertable_cmd cmd = {};
219
220 if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
221 return 0;
222
223 /*
224 * TODO: The following vif_count verification is temporary condition.
225 * Avoid power mode update if more than one interface is currently
226 * active. Remove this condition when FW will support power management
227 * on multiple MACs.
228 */
229 IWL_DEBUG_POWER(mvm, "Currently %d interfaces active\n",
230 mvm->vif_count);
231 if (mvm->vif_count > 1)
232 return 0;
233
234 iwl_mvm_power_build_cmd(mvm, vif, &cmd);
235 iwl_mvm_power_log(mvm, &cmd);
236
237 ret = iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC,
238 sizeof(cmd), &cmd);
239 if (ret)
240 return ret;
241
242 ba_enable = !!(cmd.flags &
243 cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK));
244
245 return iwl_mvm_update_beacon_abort(mvm, vif, ba_enable);
246}
247
248static int iwl_mvm_power_legacy_disable(struct iwl_mvm *mvm,
249 struct ieee80211_vif *vif)
250{
251 struct iwl_powertable_cmd cmd = {};
252 struct iwl_mvm_vif *mvmvif __maybe_unused =
253 iwl_mvm_vif_from_mac80211(vif);
254
255 if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
256 return 0;
257
258 if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
259 cmd.flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
260
261#ifdef CONFIG_IWLWIFI_DEBUGFS
262 if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF &&
263 mvmvif->dbgfs_pm.disable_power_off)
264 cmd.flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK);
265#endif
266 iwl_mvm_power_log(mvm, &cmd);
267
268 return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_ASYNC,
269 sizeof(cmd), &cmd);
270}
271
272#ifdef CONFIG_IWLWIFI_DEBUGFS
273static int iwl_mvm_power_legacy_dbgfs_read(struct iwl_mvm *mvm,
274 struct ieee80211_vif *vif, char *buf,
275 int bufsz)
276{
277 struct iwl_powertable_cmd cmd = {};
278 int pos = 0;
279
280 iwl_mvm_power_build_cmd(mvm, vif, &cmd);
281
282 pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off = %d\n",
283 (cmd.flags &
284 cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK)) ?
285 0 : 1);
286 pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n",
287 le32_to_cpu(cmd.skip_dtim_periods));
288 pos += scnprintf(buf+pos, bufsz-pos, "power_scheme = %d\n",
289 iwlmvm_mod_params.power_scheme);
290 pos += scnprintf(buf+pos, bufsz-pos, "flags = 0x%x\n",
291 le16_to_cpu(cmd.flags));
292 pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n",
293 cmd.keep_alive_seconds);
294
295 if (cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) {
296 pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n",
297 (cmd.flags &
298 cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ?
299 1 : 0);
300 pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout = %d\n",
301 le32_to_cpu(cmd.rx_data_timeout));
302 pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout = %d\n",
303 le32_to_cpu(cmd.tx_data_timeout));
304 if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
305 pos += scnprintf(buf+pos, bufsz-pos,
306 "lprx_rssi_threshold = %d\n",
307 le32_to_cpu(cmd.lprx_rssi_threshold));
308 }
309 return pos;
310}
311#endif
312
313const struct iwl_mvm_power_ops pm_legacy_ops = {
314 .power_update_mode = iwl_mvm_power_legacy_update_mode,
315 .power_disable = iwl_mvm_power_legacy_disable,
316#ifdef CONFIG_IWLWIFI_DEBUGFS
317 .power_dbgfs_read = iwl_mvm_power_legacy_dbgfs_read,
318#endif
319};