aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/iwlwifi/mvm/d3.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2013-05-14 07:53:45 -0400
committerJohannes Berg <johannes.berg@intel.com>2013-05-29 15:56:57 -0400
commitdebff6184c32149bd08cfecfafbebb96201be37d (patch)
tree25feb83b8dd4cc4c119b00245cdebf36435d5698 /drivers/net/wireless/iwlwifi/mvm/d3.c
parent774439518ac01049e9bf44f2fa4c604981f39dbf (diff)
iwlwifi: mvm: implement D3 testing
For testing the D3 (WoWLAN) firmware, it is useful to be able to run the firmware with instrumentation while the host isn't sleeping and can poke at the firmware debug logging etc. Implement this by a debugfs file. When the file is opened the D3 firmware is loaded and all regular commands are blocked. While the file is being read, poll the firmware's PME status flag and report EOF once it changes to non-zero. When it is closed, do (most of) the resume processing. This lets a user just "cat" the file. Pressing Ctrl-C to kill the cat process will resume the firwmare as though the platform resumed for non-wireless reason and when the firmware wants to wake up reading from the file automatically completes. Unlike in real suspend, only disable interrupts and don't reset the TX/RX hardware while in the test mode. This is a workaround for some interrupt problems that happen only when the PCIe link isn't fully reset (presumably by changing the PCI config space registers which the core PCI code does.) Note that while regular operations are blocked from sending commands to the firmware, they could still be made and cause strange mac80211 issues. Therefore, while using this testing feature you need to be careful to not try to disconnect, roam or similar, and will see warnings for such attempts. Als note that this requires an upcoming firmware change to tell the driver the location of the PME status flag in SRAM. D3 test will fail if the firmware doesn't report the pointer. 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/d3.c')
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/d3.c149
1 files changed, 139 insertions, 10 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c
index 4d3c978b5c76..7a2ef3f013fd 100644
--- a/drivers/net/wireless/iwlwifi/mvm/d3.c
+++ b/drivers/net/wireless/iwlwifi/mvm/d3.c
@@ -63,6 +63,7 @@
63 63
64#include <linux/etherdevice.h> 64#include <linux/etherdevice.h>
65#include <linux/ip.h> 65#include <linux/ip.h>
66#include <linux/fs.h>
66#include <net/cfg80211.h> 67#include <net/cfg80211.h>
67#include <net/ipv6.h> 68#include <net/ipv6.h>
68#include <net/tcp.h> 69#include <net/tcp.h>
@@ -756,7 +757,9 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
756 return 0; 757 return 0;
757} 758}
758 759
759int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) 760static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
761 struct cfg80211_wowlan *wowlan,
762 bool test)
760{ 763{
761 struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); 764 struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
762 struct iwl_d3_iter_data suspend_iter_data = { 765 struct iwl_d3_iter_data suspend_iter_data = {
@@ -769,7 +772,7 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
769 struct iwl_wowlan_config_cmd wowlan_config_cmd = {}; 772 struct iwl_wowlan_config_cmd wowlan_config_cmd = {};
770 struct iwl_wowlan_kek_kck_material_cmd kek_kck_cmd = {}; 773 struct iwl_wowlan_kek_kck_material_cmd kek_kck_cmd = {};
771 struct iwl_wowlan_tkip_params_cmd tkip_cmd = {}; 774 struct iwl_wowlan_tkip_params_cmd tkip_cmd = {};
772 struct iwl_d3_manager_config d3_cfg_cmd = { 775 struct iwl_d3_manager_config d3_cfg_cmd_data = {
773 /* 776 /*
774 * Program the minimum sleep time to 10 seconds, as many 777 * Program the minimum sleep time to 10 seconds, as many
775 * platforms have issues processing a wakeup signal while 778 * platforms have issues processing a wakeup signal while
@@ -777,17 +780,30 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
777 */ 780 */
778 .min_sleep_time = cpu_to_le32(10 * 1000 * 1000), 781 .min_sleep_time = cpu_to_le32(10 * 1000 * 1000),
779 }; 782 };
783 struct iwl_host_cmd d3_cfg_cmd = {
784 .id = D3_CONFIG_CMD,
785 .flags = CMD_SYNC | CMD_WANT_SKB,
786 .data[0] = &d3_cfg_cmd_data,
787 .len[0] = sizeof(d3_cfg_cmd_data),
788 };
780 struct wowlan_key_data key_data = { 789 struct wowlan_key_data key_data = {
781 .use_rsc_tsc = false, 790 .use_rsc_tsc = false,
782 .tkip = &tkip_cmd, 791 .tkip = &tkip_cmd,
783 .use_tkip = false, 792 .use_tkip = false,
784 }; 793 };
785 int ret, i; 794 int ret, i;
795 int len __maybe_unused;
786 u16 seq; 796 u16 seq;
787 u8 old_aux_sta_id, old_ap_sta_id = IWL_MVM_STATION_COUNT; 797 u8 old_aux_sta_id, old_ap_sta_id = IWL_MVM_STATION_COUNT;
788 798
789 if (WARN_ON(!wowlan)) 799 if (!wowlan) {
800 /*
801 * mac80211 shouldn't get here, but for D3 test
802 * it doesn't warrant a warning
803 */
804 WARN_ON(!test);
790 return -EINVAL; 805 return -EINVAL;
806 }
791 807
792 key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL); 808 key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL);
793 if (!key_data.rsc_tsc) 809 if (!key_data.rsc_tsc)
@@ -1012,14 +1028,26 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
1012 goto out; 1028 goto out;
1013 1029
1014 /* must be last -- this switches firmware state */ 1030 /* must be last -- this switches firmware state */
1015 ret = iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD, CMD_SYNC, 1031 ret = iwl_mvm_send_cmd(mvm, &d3_cfg_cmd);
1016 sizeof(d3_cfg_cmd), &d3_cfg_cmd);
1017 if (ret) 1032 if (ret)
1018 goto out; 1033 goto out;
1034#ifdef CONFIG_IWLWIFI_DEBUGFS
1035 len = le32_to_cpu(d3_cfg_cmd.resp_pkt->len_n_flags) &
1036 FH_RSCSR_FRAME_SIZE_MSK;
1037 if (len >= sizeof(u32) * 2) {
1038 mvm->d3_test_pme_ptr =
1039 le32_to_cpup((__le32 *)d3_cfg_cmd.resp_pkt->data);
1040 } else if (test) {
1041 /* in test mode we require the pointer */
1042 ret = -EIO;
1043 goto out;
1044 }
1045#endif
1046 iwl_free_resp(&d3_cfg_cmd);
1019 1047
1020 clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); 1048 clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
1021 1049
1022 iwl_trans_d3_suspend(mvm->trans); 1050 iwl_trans_d3_suspend(mvm->trans, test);
1023 out: 1051 out:
1024 mvm->aux_sta.sta_id = old_aux_sta_id; 1052 mvm->aux_sta.sta_id = old_aux_sta_id;
1025 mvm_ap_sta->sta_id = old_ap_sta_id; 1053 mvm_ap_sta->sta_id = old_ap_sta_id;
@@ -1034,6 +1062,11 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
1034 return ret; 1062 return ret;
1035} 1063}
1036 1064
1065int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
1066{
1067 return __iwl_mvm_suspend(hw, wowlan, false);
1068}
1069
1037static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, 1070static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
1038 struct ieee80211_vif *vif) 1071 struct ieee80211_vif *vif)
1039{ 1072{
@@ -1238,9 +1271,8 @@ static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm)
1238#endif 1271#endif
1239} 1272}
1240 1273
1241int iwl_mvm_resume(struct ieee80211_hw *hw) 1274static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
1242{ 1275{
1243 struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
1244 struct iwl_d3_iter_data resume_iter_data = { 1276 struct iwl_d3_iter_data resume_iter_data = {
1245 .mvm = mvm, 1277 .mvm = mvm,
1246 }; 1278 };
@@ -1260,7 +1292,7 @@ int iwl_mvm_resume(struct ieee80211_hw *hw)
1260 1292
1261 vif = resume_iter_data.vif; 1293 vif = resume_iter_data.vif;
1262 1294
1263 ret = iwl_trans_d3_resume(mvm->trans, &d3_status); 1295 ret = iwl_trans_d3_resume(mvm->trans, &d3_status, test);
1264 if (ret) 1296 if (ret)
1265 goto out_unlock; 1297 goto out_unlock;
1266 1298
@@ -1277,7 +1309,7 @@ int iwl_mvm_resume(struct ieee80211_hw *hw)
1277 out_unlock: 1309 out_unlock:
1278 mutex_unlock(&mvm->mutex); 1310 mutex_unlock(&mvm->mutex);
1279 1311
1280 if (vif) 1312 if (!test && vif)
1281 ieee80211_resume_disconnect(vif); 1313 ieee80211_resume_disconnect(vif);
1282 1314
1283 /* return 1 to reconfigure the device */ 1315 /* return 1 to reconfigure the device */
@@ -1285,9 +1317,106 @@ int iwl_mvm_resume(struct ieee80211_hw *hw)
1285 return 1; 1317 return 1;
1286} 1318}
1287 1319
1320int iwl_mvm_resume(struct ieee80211_hw *hw)
1321{
1322 struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
1323
1324 return __iwl_mvm_resume(mvm, false);
1325}
1326
1288void iwl_mvm_set_wakeup(struct ieee80211_hw *hw, bool enabled) 1327void iwl_mvm_set_wakeup(struct ieee80211_hw *hw, bool enabled)
1289{ 1328{
1290 struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); 1329 struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
1291 1330
1292 device_set_wakeup_enable(mvm->trans->dev, enabled); 1331 device_set_wakeup_enable(mvm->trans->dev, enabled);
1293} 1332}
1333
1334#ifdef CONFIG_IWLWIFI_DEBUGFS
1335static int iwl_mvm_d3_test_open(struct inode *inode, struct file *file)
1336{
1337 struct iwl_mvm *mvm = inode->i_private;
1338 int err;
1339
1340 if (mvm->d3_test_active)
1341 return -EBUSY;
1342
1343 file->private_data = inode->i_private;
1344
1345 ieee80211_stop_queues(mvm->hw);
1346 synchronize_net();
1347
1348 /* start pseudo D3 */
1349 rtnl_lock();
1350 err = __iwl_mvm_suspend(mvm->hw, mvm->hw->wiphy->wowlan_config, true);
1351 rtnl_unlock();
1352 if (err > 0)
1353 err = -EINVAL;
1354 if (err) {
1355 ieee80211_wake_queues(mvm->hw);
1356 return err;
1357 }
1358 mvm->d3_test_active = true;
1359 return 0;
1360}
1361
1362static ssize_t iwl_mvm_d3_test_read(struct file *file, char __user *user_buf,
1363 size_t count, loff_t *ppos)
1364{
1365 struct iwl_mvm *mvm = file->private_data;
1366 u32 pme_asserted;
1367
1368 while (true) {
1369 pme_asserted = iwl_trans_read_mem32(mvm->trans,
1370 mvm->d3_test_pme_ptr);
1371 if (pme_asserted)
1372 break;
1373 if (msleep_interruptible(100))
1374 break;
1375 }
1376
1377 return 0;
1378}
1379
1380static void iwl_mvm_d3_test_disconn_work_iter(void *_data, u8 *mac,
1381 struct ieee80211_vif *vif)
1382{
1383 if (vif->type == NL80211_IFTYPE_STATION)
1384 ieee80211_connection_loss(vif);
1385}
1386
1387static int iwl_mvm_d3_test_release(struct inode *inode, struct file *file)
1388{
1389 struct iwl_mvm *mvm = inode->i_private;
1390 int remaining_time = 10;
1391
1392 mvm->d3_test_active = false;
1393 __iwl_mvm_resume(mvm, true);
1394 iwl_abort_notification_waits(&mvm->notif_wait);
1395 ieee80211_restart_hw(mvm->hw);
1396
1397 /* wait for restart and disconnect all interfaces */
1398 while (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
1399 remaining_time > 0) {
1400 remaining_time--;
1401 msleep(1000);
1402 }
1403
1404 if (remaining_time == 0)
1405 IWL_ERR(mvm, "Timed out waiting for HW restart to finish!\n");
1406
1407 ieee80211_iterate_active_interfaces_atomic(
1408 mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
1409 iwl_mvm_d3_test_disconn_work_iter, NULL);
1410
1411 ieee80211_wake_queues(mvm->hw);
1412
1413 return 0;
1414}
1415
1416const struct file_operations iwl_dbgfs_d3_test_ops = {
1417 .llseek = no_llseek,
1418 .open = iwl_mvm_d3_test_open,
1419 .read = iwl_mvm_d3_test_read,
1420 .release = iwl_mvm_d3_test_release,
1421};
1422#endif