Commit afc66bb7 authored by Johannes Berg's avatar Johannes Berg

iwlwifi: mvm: optionally store D3 SRAM after resume

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: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 93fc6411
...@@ -1214,6 +1214,26 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, ...@@ -1214,6 +1214,26 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
iwl_free_resp(&cmd); 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) int iwl_mvm_resume(struct ieee80211_hw *hw)
{ {
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
...@@ -1245,6 +1265,9 @@ int iwl_mvm_resume(struct ieee80211_hw *hw) ...@@ -1245,6 +1265,9 @@ int iwl_mvm_resume(struct ieee80211_hw *hw)
goto out_unlock; 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); iwl_mvm_query_wakeup_reasons(mvm, vif);
out_unlock: out_unlock:
......
...@@ -482,6 +482,70 @@ static ssize_t iwl_dbgfs_fw_restart_write(struct file *file, ...@@ -482,6 +482,70 @@ static ssize_t iwl_dbgfs_fw_restart_write(struct file *file,
return count; 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) \ #define MVM_DEBUGFS_READ_FILE_OPS(name) \
static const struct file_operations iwl_dbgfs_##name##_ops = { \ static const struct file_operations iwl_dbgfs_##name##_ops = { \
.read = iwl_dbgfs_##name##_read, \ .read = iwl_dbgfs_##name##_read, \
...@@ -525,6 +589,9 @@ MVM_DEBUGFS_READ_FILE_OPS(bt_notif); ...@@ -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_allow);
MVM_DEBUGFS_WRITE_FILE_OPS(power_down_d3_allow); MVM_DEBUGFS_WRITE_FILE_OPS(power_down_d3_allow);
MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart); MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart);
#ifdef CONFIG_PM_SLEEP
MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram);
#endif
/* Interface specific debugfs entries */ /* Interface specific debugfs entries */
MVM_DEBUGFS_READ_FILE_OPS(mac_params); MVM_DEBUGFS_READ_FILE_OPS(mac_params);
...@@ -543,6 +610,9 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) ...@@ -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_allow, mvm->debugfs_dir, S_IWUSR);
MVM_DEBUGFS_ADD_FILE(power_down_d3_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); 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 * Create a symlink with mac80211. It will be removed when mac80211
......
...@@ -344,6 +344,10 @@ struct iwl_mvm { ...@@ -344,6 +344,10 @@ struct iwl_mvm {
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
int gtk_ivlen, gtk_icvlen, ptk_ivlen, ptk_icvlen; int gtk_ivlen, gtk_icvlen, ptk_ivlen, ptk_icvlen;
#ifdef CONFIG_IWLWIFI_DEBUGFS
bool store_d3_resume_sram;
void *d3_resume_sram;
#endif
#endif #endif
/* BT-Coex */ /* BT-Coex */
......
...@@ -443,6 +443,10 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode) ...@@ -443,6 +443,10 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
kfree(mvm->scan_cmd); 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_trans_stop_hw(mvm->trans, true);
iwl_phy_db_free(mvm->phy_db); iwl_phy_db_free(mvm->phy_db);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment