aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/iwlwifi/mvm
diff options
context:
space:
mode:
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};