iwlwifi: mvm: optionally store D3 SRAM after resume
authorJohannes Berg <johannes.berg@intel.com>
Fri, 3 May 2013 09:44:16 +0000 (11:44 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Tue, 14 May 2013 08:42:37 +0000 (10:42 +0200)
The D3 image SRAM is overwritten by the runtime image, so
it can't be accessed after resume. However, it can be very
useful to look at it to know what happened during D3, so
add the ability to store the image and make it available
in debugfs.

Reviewed-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
drivers/net/wireless/iwlwifi/mvm/d3.c
drivers/net/wireless/iwlwifi/mvm/debugfs.c
drivers/net/wireless/iwlwifi/mvm/mvm.h
drivers/net/wireless/iwlwifi/mvm/ops.c

index 16bbdcc8627ae568db4734cf5c61458397162b1e..63d788aef3f4675a201ffe7c178359ec143bb79c 100644 (file)
@@ -1214,6 +1214,26 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
        iwl_free_resp(&cmd);
 }
 
+static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm)
+{
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       const struct fw_img *img = &mvm->fw->img[IWL_UCODE_WOWLAN];
+       u32 len = img->sec[IWL_UCODE_SECTION_DATA].len;
+       u32 offs = img->sec[IWL_UCODE_SECTION_DATA].offset;
+
+       if (!mvm->store_d3_resume_sram)
+               return;
+
+       if (!mvm->d3_resume_sram) {
+               mvm->d3_resume_sram = kzalloc(len, GFP_KERNEL);
+               if (!mvm->d3_resume_sram)
+                       return;
+       }
+
+       iwl_trans_read_mem_bytes(mvm->trans, offs, mvm->d3_resume_sram, len);
+#endif
+}
+
 int iwl_mvm_resume(struct ieee80211_hw *hw)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
@@ -1245,6 +1265,9 @@ int iwl_mvm_resume(struct ieee80211_hw *hw)
                goto out_unlock;
        }
 
+       /* query SRAM first in case we want event logging */
+       iwl_mvm_read_d3_sram(mvm);
+
        iwl_mvm_query_wakeup_reasons(mvm, vif);
 
  out_unlock:
index a12c98be8616a6b0b9dc26255e21eb717be41646..0765827d87a6538a5b549bc8584004a3826e8138 100644 (file)
@@ -482,6 +482,70 @@ static ssize_t iwl_dbgfs_fw_restart_write(struct file *file,
        return count;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static ssize_t iwl_dbgfs_d3_sram_write(struct file *file,
+                                      const char __user *user_buf,
+                                      size_t count, loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+       char buf[8] = {};
+       int store;
+
+       if (copy_from_user(buf, user_buf, sizeof(buf)))
+               return -EFAULT;
+
+       if (sscanf(buf, "%d", &store) != 1)
+               return -EINVAL;
+
+       mvm->store_d3_resume_sram = store;
+
+       return count;
+}
+
+static ssize_t iwl_dbgfs_d3_sram_read(struct file *file, char __user *user_buf,
+                                     size_t count, loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+       const struct fw_img *img;
+       int ofs, len, pos = 0;
+       size_t bufsz, ret;
+       char *buf;
+       u8 *ptr = mvm->d3_resume_sram;
+
+       img = &mvm->fw->img[IWL_UCODE_WOWLAN];
+       len = img->sec[IWL_UCODE_SECTION_DATA].len;
+
+       bufsz = len * 4 + 256;
+       buf = kzalloc(bufsz, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       pos += scnprintf(buf, bufsz, "D3 SRAM capture: %sabled\n",
+                        mvm->store_d3_resume_sram ? "en" : "dis");
+
+       if (ptr) {
+               for (ofs = 0; ofs < len; ofs += 16) {
+                       pos += scnprintf(buf + pos, bufsz - pos,
+                                        "0x%.4x ", ofs);
+                       hex_dump_to_buffer(ptr + ofs, 16, 16, 1, buf + pos,
+                                          bufsz - pos, false);
+                       pos += strlen(buf + pos);
+                       if (bufsz - pos > 0)
+                               buf[pos++] = '\n';
+               }
+       } else {
+               pos += scnprintf(buf + pos, bufsz - pos,
+                                "(no data captured)\n");
+       }
+
+       ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+
+       kfree(buf);
+
+       return ret;
+}
+#endif
+
 #define MVM_DEBUGFS_READ_FILE_OPS(name)                                        \
 static const struct file_operations iwl_dbgfs_##name##_ops = { \
        .read = iwl_dbgfs_##name##_read,                                \
@@ -525,6 +589,9 @@ MVM_DEBUGFS_READ_FILE_OPS(bt_notif);
 MVM_DEBUGFS_WRITE_FILE_OPS(power_down_allow);
 MVM_DEBUGFS_WRITE_FILE_OPS(power_down_d3_allow);
 MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart);
+#ifdef CONFIG_PM_SLEEP
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram);
+#endif
 
 /* Interface specific debugfs entries */
 MVM_DEBUGFS_READ_FILE_OPS(mac_params);
@@ -543,6 +610,9 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
        MVM_DEBUGFS_ADD_FILE(power_down_allow, mvm->debugfs_dir, S_IWUSR);
        MVM_DEBUGFS_ADD_FILE(power_down_d3_allow, mvm->debugfs_dir, S_IWUSR);
        MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, S_IWUSR);
+#ifdef CONFIG_PM_SLEEP
+       MVM_DEBUGFS_ADD_FILE(d3_sram, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
+#endif
 
        /*
         * Create a symlink with mac80211. It will be removed when mac80211
index 3505fcafde6635cfe190dffd48deda6a9a0cb3bc..712c39ae748fb3eb6d55ea2dd2a3761758da4603 100644 (file)
@@ -344,6 +344,10 @@ struct iwl_mvm {
 
 #ifdef CONFIG_PM_SLEEP
        int gtk_ivlen, gtk_icvlen, ptk_ivlen, ptk_icvlen;
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       bool store_d3_resume_sram;
+       void *d3_resume_sram;
+#endif
 #endif
 
        /* BT-Coex */
index 5bf1eaf9046a0a8e42d3eacae197fbb7eef18c4d..bb79a8dcf6bbd081690a15c90d499d86ed028ae3 100644 (file)
@@ -443,6 +443,10 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
 
        kfree(mvm->scan_cmd);
 
+#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_IWLWIFI_DEBUGFS)
+       kfree(mvm->d3_resume_sram);
+#endif
+
        iwl_trans_stop_hw(mvm->trans, true);
 
        iwl_phy_db_free(mvm->phy_db);