Commit 9f65ad25 authored by Yanbo Li's avatar Yanbo Li Committed by Kalle Valo

ath10k: add memory dump debugfs interface

Add mem_val debugfs file for dumping the firmware (target) memory and also for
writing to the memory. The firmware memory is accessed through one file which
uses position of the file as the firmware memory address. For example, with dd
use skip parameter for the address.

Beucase target memory width is 32 bits it's strongly recommended to use
blocksize divisable with 4 when using this interface. For example, when using
dd use bs=4 to set the block size to 4 and remember to divide both count and
skip values with four.

To read 4 kB chunk from address 0x400000:

dd if=mem_value bs=4 count=1024 skip=1048576 | xxd -g1

To write value 0x01020304 to address 0x400400:

echo 0x01020304 | xxd -r | dd of=mem_value bs=4 seek=1048832

To read 4 KB chunk of memory and then write back after edit:

dd if=mem_value of=tmp.bin bs=4 count=1024 skip=1048576
emacs tmp.bin
dd if=tmp.bin of=mem_value bs=4 count=1024 seek=1048576
Signed-off-by: default avatarYanbo Li <yanbol@qti.qualcomm.com>
Signed-off-by: default avatarKalle Valo <kvalo@qca.qualcomm.com>
parent 077a3804
......@@ -1047,6 +1047,117 @@ static const struct file_operations fops_reg_value = {
.llseek = default_llseek,
};
static ssize_t ath10k_mem_value_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath10k *ar = file->private_data;
u8 *buf;
int ret;
if (*ppos < 0)
return -EINVAL;
if (!count)
return 0;
mutex_lock(&ar->conf_mutex);
buf = vmalloc(count);
if (!buf) {
ret = -ENOMEM;
goto exit;
}
if (ar->state != ATH10K_STATE_ON &&
ar->state != ATH10K_STATE_UTF) {
ret = -ENETDOWN;
goto exit;
}
ret = ath10k_hif_diag_read(ar, *ppos, buf, count);
if (ret) {
ath10k_warn(ar, "failed to read address 0x%08x via diagnose window fnrom debugfs: %d\n",
(u32)(*ppos), ret);
goto exit;
}
ret = copy_to_user(user_buf, buf, count);
if (ret) {
ret = -EFAULT;
goto exit;
}
count -= ret;
*ppos += count;
ret = count;
exit:
vfree(buf);
mutex_unlock(&ar->conf_mutex);
return ret;
}
static ssize_t ath10k_mem_value_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath10k *ar = file->private_data;
u8 *buf;
int ret;
if (*ppos < 0)
return -EINVAL;
if (!count)
return 0;
mutex_lock(&ar->conf_mutex);
buf = vmalloc(count);
if (!buf) {
ret = -ENOMEM;
goto exit;
}
if (ar->state != ATH10K_STATE_ON &&
ar->state != ATH10K_STATE_UTF) {
ret = -ENETDOWN;
goto exit;
}
ret = copy_from_user(buf, user_buf, count);
if (ret) {
ret = -EFAULT;
goto exit;
}
ret = ath10k_hif_diag_write(ar, *ppos, buf, count);
if (ret) {
ath10k_warn(ar, "failed to write address 0x%08x via diagnose window from debugfs: %d\n",
(u32)(*ppos), ret);
goto exit;
}
*ppos += count;
ret = count;
exit:
vfree(buf);
mutex_unlock(&ar->conf_mutex);
return ret;
}
static const struct file_operations fops_mem_value = {
.read = ath10k_mem_value_read,
.write = ath10k_mem_value_write,
.open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static int ath10k_debug_htt_stats_req(struct ath10k *ar)
{
u64 cookie;
......@@ -1754,6 +1865,9 @@ int ath10k_debug_register(struct ath10k *ar)
debugfs_create_file("reg_value", S_IRUSR | S_IWUSR,
ar->debug.debugfs_phy, ar, &fops_reg_value);
debugfs_create_file("mem_value", S_IRUSR | S_IWUSR,
ar->debug.debugfs_phy, ar, &fops_mem_value);
debugfs_create_file("chip_id", S_IRUSR, ar->debug.debugfs_phy,
ar, &fops_chip_id);
......
......@@ -48,6 +48,8 @@ struct ath10k_hif_ops {
int (*diag_read)(struct ath10k *ar, u32 address, void *buf,
size_t buf_len);
int (*diag_write)(struct ath10k *ar, u32 address, const void *data,
int nbytes);
/*
* API to handle HIF-specific BMI message exchanges, this API is
* synchronous and only allowed to be called from a context that
......@@ -113,6 +115,15 @@ static inline int ath10k_hif_diag_read(struct ath10k *ar, u32 address, void *buf
return ar->hif.ops->diag_read(ar, address, buf, buf_len);
}
static inline int ath10k_hif_diag_write(struct ath10k *ar, u32 address,
const void *data, int nbytes)
{
if (!ar->hif.ops->diag_write)
return -EOPNOTSUPP;
return ar->hif.ops->diag_write(ar, address, data, nbytes);
}
static inline int ath10k_hif_exchange_bmi_msg(struct ath10k *ar,
void *request, u32 request_len,
void *response, u32 *response_len)
......
......@@ -1988,6 +1988,7 @@ static int ath10k_pci_hif_resume(struct ath10k *ar)
static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
.tx_sg = ath10k_pci_hif_tx_sg,
.diag_read = ath10k_pci_hif_diag_read,
.diag_write = ath10k_pci_diag_write_mem,
.exchange_bmi_msg = ath10k_pci_hif_exchange_bmi_msg,
.start = ath10k_pci_hif_start,
.stop = ath10k_pci_hif_stop,
......
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