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: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Johannes Berg 2013-05-03 11:44:16 +02:00
parent 93fc64114b
commit afc66bb734
4 changed files with 101 additions and 0 deletions

View file

@ -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)
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:

View 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);
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)
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

View file

@ -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 */

View file

@ -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);