summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYue Ma <yuem@codeaurora.org>2017-08-16 17:27:36 -0700
committerYue Ma <yuem@codeaurora.org>2017-08-18 14:57:00 -0700
commit828b18a1a83de02cb53b4bc470bdbe3658ca8378 (patch)
tree65583e36d5cda3bc47f00970117c10f0e3891e05
parent30484e039ccc38055f817c9c8195f7e67c6c0c03 (diff)
cnss2: Add debugfs entries for register read/write
Add debugfs entries for reading/writing WLAN hardware registers. This is for runtime debugging enhancement. Change-Id: If7a85181858e127187906beca69870a98f4a1184 CRs-fixed: 2059087 Signed-off-by: Yue Ma <yuem@codeaurora.org>
-rw-r--r--drivers/net/wireless/cnss2/debug.c212
-rw-r--r--drivers/net/wireless/cnss2/main.c1
-rw-r--r--drivers/net/wireless/cnss2/main.h5
3 files changed, 218 insertions, 0 deletions
diff --git a/drivers/net/wireless/cnss2/debug.c b/drivers/net/wireless/cnss2/debug.c
index c69f0a488c52..35d7fe1c318c 100644
--- a/drivers/net/wireless/cnss2/debug.c
+++ b/drivers/net/wireless/cnss2/debug.c
@@ -214,6 +214,214 @@ static const struct file_operations cnss_dev_boot_debug_fops = {
.llseek = seq_lseek,
};
+static int cnss_reg_read_debug_show(struct seq_file *s, void *data)
+{
+ struct cnss_plat_data *plat_priv = s->private;
+
+ mutex_lock(&plat_priv->dev_lock);
+ if (!plat_priv->diag_reg_read_buf) {
+ seq_puts(s, "\nUsage: echo <mem_type> <offset> <data_len> > <debugfs_path>/cnss/reg_read\n");
+ mutex_unlock(&plat_priv->dev_lock);
+ return 0;
+ }
+
+ seq_printf(s, "\nRegister read, address: 0x%x memory type: 0x%x length: 0x%x\n\n",
+ plat_priv->diag_reg_read_addr,
+ plat_priv->diag_reg_read_mem_type,
+ plat_priv->diag_reg_read_len);
+
+ seq_hex_dump(s, "", DUMP_PREFIX_OFFSET, 32, 4,
+ plat_priv->diag_reg_read_buf,
+ plat_priv->diag_reg_read_len, false);
+
+ plat_priv->diag_reg_read_len = 0;
+ kfree(plat_priv->diag_reg_read_buf);
+ plat_priv->diag_reg_read_buf = NULL;
+ mutex_unlock(&plat_priv->dev_lock);
+
+ return 0;
+}
+
+static ssize_t cnss_reg_read_debug_write(struct file *fp,
+ const char __user *user_buf,
+ size_t count, loff_t *off)
+{
+ struct cnss_plat_data *plat_priv =
+ ((struct seq_file *)fp->private_data)->private;
+ char buf[64];
+ char *sptr, *token;
+ unsigned int len = 0;
+ u32 reg_offset, mem_type;
+ u32 data_len = 0;
+ u8 *reg_buf = NULL;
+ const char *delim = " ";
+ int ret = 0;
+
+ if (!test_bit(CNSS_FW_READY, &plat_priv->driver_state)) {
+ cnss_pr_err("Firmware is not ready yet\n");
+ return -EINVAL;
+ }
+
+ len = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+ sptr = buf;
+
+ token = strsep(&sptr, delim);
+ if (!token)
+ return -EINVAL;
+
+ if (!sptr)
+ return -EINVAL;
+
+ if (kstrtou32(token, 0, &mem_type))
+ return -EINVAL;
+
+ token = strsep(&sptr, delim);
+ if (!token)
+ return -EINVAL;
+
+ if (!sptr)
+ return -EINVAL;
+
+ if (kstrtou32(token, 0, &reg_offset))
+ return -EINVAL;
+
+ token = strsep(&sptr, delim);
+ if (!token)
+ return -EINVAL;
+
+ if (kstrtou32(token, 0, &data_len))
+ return -EINVAL;
+
+ if (data_len == 0 ||
+ data_len > QMI_WLFW_MAX_ATHDIAG_DATA_SIZE_V01)
+ return -EINVAL;
+
+ mutex_lock(&plat_priv->dev_lock);
+ kfree(plat_priv->diag_reg_read_buf);
+ plat_priv->diag_reg_read_buf = NULL;
+
+ reg_buf = kzalloc(data_len, GFP_KERNEL);
+ if (!reg_buf) {
+ mutex_unlock(&plat_priv->dev_lock);
+ return -ENOMEM;
+ }
+
+ ret = cnss_wlfw_athdiag_read_send_sync(plat_priv, reg_offset,
+ mem_type, data_len,
+ reg_buf);
+ if (ret) {
+ kfree(reg_buf);
+ mutex_unlock(&plat_priv->dev_lock);
+ return ret;
+ }
+
+ plat_priv->diag_reg_read_addr = reg_offset;
+ plat_priv->diag_reg_read_mem_type = mem_type;
+ plat_priv->diag_reg_read_len = data_len;
+ plat_priv->diag_reg_read_buf = reg_buf;
+ mutex_unlock(&plat_priv->dev_lock);
+
+ return count;
+}
+
+static int cnss_reg_read_debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, cnss_reg_read_debug_show, inode->i_private);
+}
+
+static const struct file_operations cnss_reg_read_debug_fops = {
+ .read = seq_read,
+ .write = cnss_reg_read_debug_write,
+ .open = cnss_reg_read_debug_open,
+ .owner = THIS_MODULE,
+ .llseek = seq_lseek,
+};
+
+static int cnss_reg_write_debug_show(struct seq_file *s, void *data)
+{
+ seq_puts(s, "\nUsage: echo <mem_type> <offset> <reg_val> > <debugfs_path>/cnss/reg_write\n");
+
+ return 0;
+}
+
+static ssize_t cnss_reg_write_debug_write(struct file *fp,
+ const char __user *user_buf,
+ size_t count, loff_t *off)
+{
+ struct cnss_plat_data *plat_priv =
+ ((struct seq_file *)fp->private_data)->private;
+ char buf[64];
+ char *sptr, *token;
+ unsigned int len = 0;
+ u32 reg_offset, mem_type, reg_val;
+ const char *delim = " ";
+ int ret = 0;
+
+ if (!test_bit(CNSS_FW_READY, &plat_priv->driver_state)) {
+ cnss_pr_err("Firmware is not ready yet\n");
+ return -EINVAL;
+ }
+
+ len = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+ sptr = buf;
+
+ token = strsep(&sptr, delim);
+ if (!token)
+ return -EINVAL;
+
+ if (!sptr)
+ return -EINVAL;
+
+ if (kstrtou32(token, 0, &mem_type))
+ return -EINVAL;
+
+ token = strsep(&sptr, delim);
+ if (!token)
+ return -EINVAL;
+
+ if (!sptr)
+ return -EINVAL;
+
+ if (kstrtou32(token, 0, &reg_offset))
+ return -EINVAL;
+
+ token = strsep(&sptr, delim);
+ if (!token)
+ return -EINVAL;
+
+ if (kstrtou32(token, 0, &reg_val))
+ return -EINVAL;
+
+ ret = cnss_wlfw_athdiag_write_send_sync(plat_priv, reg_offset, mem_type,
+ sizeof(u32),
+ (u8 *)&reg_val);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static int cnss_reg_write_debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, cnss_reg_write_debug_show, inode->i_private);
+}
+
+static const struct file_operations cnss_reg_write_debug_fops = {
+ .read = seq_read,
+ .write = cnss_reg_write_debug_write,
+ .open = cnss_reg_write_debug_open,
+ .owner = THIS_MODULE,
+ .llseek = seq_lseek,
+};
+
#ifdef CONFIG_CNSS2_DEBUG
static int cnss_create_debug_only_node(struct cnss_plat_data *plat_priv)
{
@@ -221,6 +429,10 @@ static int cnss_create_debug_only_node(struct cnss_plat_data *plat_priv)
debugfs_create_file("dev_boot", 0600, root_dentry, plat_priv,
&cnss_dev_boot_debug_fops);
+ debugfs_create_file("reg_read", 0600, root_dentry, plat_priv,
+ &cnss_reg_read_debug_fops);
+ debugfs_create_file("reg_write", 0600, root_dentry, plat_priv,
+ &cnss_reg_write_debug_fops);
return 0;
}
diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c
index 8277de861d63..23a81ff071ee 100644
--- a/drivers/net/wireless/cnss2/main.c
+++ b/drivers/net/wireless/cnss2/main.c
@@ -2283,6 +2283,7 @@ static int cnss_probe(struct platform_device *plat_dev)
ret);
init_completion(&plat_priv->power_up_complete);
+ mutex_init(&plat_priv->dev_lock);
cnss_pr_info("Platform driver probed successfully.\n");
diff --git a/drivers/net/wireless/cnss2/main.h b/drivers/net/wireless/cnss2/main.h
index 89ada0b2e1f0..a5f9ce37b0ea 100644
--- a/drivers/net/wireless/cnss2/main.h
+++ b/drivers/net/wireless/cnss2/main.h
@@ -205,6 +205,11 @@ struct cnss_plat_data {
atomic_t pm_count;
struct timer_list fw_boot_timer;
struct completion power_up_complete;
+ struct mutex dev_lock; /* mutex for register access through debugfs */
+ u32 diag_reg_read_addr;
+ u32 diag_reg_read_mem_type;
+ u32 diag_reg_read_len;
+ u8 *diag_reg_read_buf;
};
void *cnss_bus_dev_to_bus_priv(struct device *dev);