icnss: Provide test mode through debugfs
Provide a test mode mechanism through debugfs interface for WLAN firmware CCPM module so that WLAN enable and disable can be tested without loading WLAN functional driver. Change-Id: I8d411e067690443eefea645f4ff8130cf786c32f Signed-off-by: Prashanth Bhatta <bhattap@codeaurora.org>
This commit is contained in:
parent
ee5af4bf4c
commit
e4e34e9e8a
1 changed files with 195 additions and 47 deletions
|
@ -20,6 +20,8 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
@ -60,7 +62,8 @@ struct icnss_driver_event {
|
|||
enum cnss_driver_state {
|
||||
ICNSS_WLFW_QMI_CONNECTED,
|
||||
ICNSS_FW_READY,
|
||||
ICNSS_DRIVER_PROBED
|
||||
ICNSS_DRIVER_PROBED,
|
||||
ICNSS_FW_TEST_MODE,
|
||||
};
|
||||
|
||||
#ifdef ICNSS_PANIC
|
||||
|
@ -131,6 +134,7 @@ static struct icnss_data {
|
|||
icnss_mem_region[QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01];
|
||||
bool skip_qmi;
|
||||
struct completion driver_unregister;
|
||||
struct dentry *root_dentry;
|
||||
} *penv;
|
||||
|
||||
static char *icnss_driver_event_to_str(enum icnss_driver_event_type type)
|
||||
|
@ -1380,38 +1384,6 @@ int icnss_get_ce_id(int irq)
|
|||
}
|
||||
EXPORT_SYMBOL(icnss_get_ce_id);
|
||||
|
||||
static ssize_t icnss_wlan_mode_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
int val;
|
||||
int ret;
|
||||
|
||||
if (!penv)
|
||||
return -ENODEV;
|
||||
|
||||
ret = kstrtoint(buf, 0, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (val == ICNSS_WALTEST || val == ICNSS_CCPM) {
|
||||
pr_debug("%s: WLAN Test Mode -> %d\n", __func__, val);
|
||||
ret = icnss_wlan_enable(NULL, val, NULL);
|
||||
if (ret)
|
||||
pr_err("%s: WLAN Test Mode %d failed with %d\n",
|
||||
__func__, val, ret);
|
||||
} else {
|
||||
pr_err("%s: Mode %d is not supported from command line\n",
|
||||
__func__, val);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(icnss_wlan_mode, S_IWUSR, NULL, icnss_wlan_mode_store);
|
||||
|
||||
static struct clk *icnss_clock_init(struct device *dev, const char *cname)
|
||||
{
|
||||
struct clk *c;
|
||||
|
@ -1594,6 +1566,191 @@ static int icnss_release_resources(void)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int icnss_test_mode_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct icnss_data *priv = s->private;
|
||||
|
||||
seq_puts(s, "0 : Test mode disable\n");
|
||||
seq_puts(s, "1 : WLAN Firmware test\n");
|
||||
seq_puts(s, "2 : CCPM test\n");
|
||||
|
||||
seq_puts(s, "\n");
|
||||
|
||||
if (!test_bit(ICNSS_FW_READY, &priv->state)) {
|
||||
seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
|
||||
seq_puts(s, "Machine mode is running, can't run test mode!\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
|
||||
seq_puts(s, "Test mode is running!\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
seq_puts(s, "Test can be run, Have fun!\n");
|
||||
|
||||
out:
|
||||
seq_puts(s, "\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int icnss_test_mode_fw_test_off(struct icnss_data *priv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!test_bit(ICNSS_FW_READY, &priv->state)) {
|
||||
pr_err("Firmware is not ready yet!, wait for FW READY: state: 0x%lx\n",
|
||||
priv->state);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
|
||||
pr_err("Machine mode is running, can't run test mode: state: 0x%lx\n",
|
||||
priv->state);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
|
||||
pr_err("Test mode not started, state: 0x%lx\n", priv->state);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
icnss_wlan_disable(ICNSS_OFF);
|
||||
|
||||
ret = icnss_hw_power_off(priv);
|
||||
|
||||
clear_bit(ICNSS_FW_TEST_MODE, &priv->state);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
static int icnss_test_mode_fw_test(struct icnss_data *priv,
|
||||
enum icnss_driver_mode mode)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!test_bit(ICNSS_FW_READY, &priv->state)) {
|
||||
pr_err("Firmware is not ready yet!, wait for FW READY, state: 0x%lx\n",
|
||||
priv->state);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
|
||||
pr_err("Machine mode is running, can't run test mode, state: 0x%lx\n",
|
||||
priv->state);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
|
||||
pr_err("Test mode already started, state: 0x%lx\n",
|
||||
priv->state);
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = icnss_hw_power_on(priv);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
set_bit(ICNSS_FW_TEST_MODE, &priv->state);
|
||||
|
||||
ret = icnss_wlan_enable(NULL, mode, NULL);
|
||||
if (ret)
|
||||
goto power_off;
|
||||
|
||||
return 0;
|
||||
|
||||
power_off:
|
||||
icnss_hw_power_off(priv);
|
||||
clear_bit(ICNSS_FW_TEST_MODE, &priv->state);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t icnss_test_mode_write(struct file *fp, const char __user *buf,
|
||||
size_t count, loff_t *off)
|
||||
{
|
||||
struct icnss_data *priv =
|
||||
((struct seq_file *)fp->private_data)->private;
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
ret = kstrtou32_from_user(buf, count, 0, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (val) {
|
||||
case 0:
|
||||
ret = icnss_test_mode_fw_test_off(priv);
|
||||
break;
|
||||
case 1:
|
||||
ret = icnss_test_mode_fw_test(priv, ICNSS_WALTEST);
|
||||
break;
|
||||
case 2:
|
||||
ret = icnss_test_mode_fw_test(priv, ICNSS_CCPM);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int icnss_test_mode_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, icnss_test_mode_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations icnss_test_mode_fops = {
|
||||
.read = seq_read,
|
||||
.write = icnss_test_mode_write,
|
||||
.release = single_release,
|
||||
.open = icnss_test_mode_open,
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = seq_lseek,
|
||||
};
|
||||
|
||||
static int icnss_debugfs_create(struct icnss_data *priv)
|
||||
{
|
||||
int ret = 0;
|
||||
struct dentry *root_dentry;
|
||||
|
||||
root_dentry = debugfs_create_dir("icnss", 0);
|
||||
|
||||
if (IS_ERR(root_dentry)) {
|
||||
ret = PTR_ERR(root_dentry);
|
||||
pr_err("Unable to create debugfs %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
priv->root_dentry = root_dentry;
|
||||
|
||||
debugfs_create_file("test_mode", S_IRUSR | S_IWUSR,
|
||||
root_dentry, priv, &icnss_test_mode_fops);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void icnss_debugfs_destroy(struct icnss_data *priv)
|
||||
{
|
||||
debugfs_remove_recursive(priv->root_dentry);
|
||||
}
|
||||
|
||||
static int icnss_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
@ -1713,20 +1870,13 @@ static int icnss_probe(struct platform_device *pdev)
|
|||
penv->skip_qmi = of_property_read_bool(dev->of_node,
|
||||
"qcom,skip-qmi");
|
||||
|
||||
ret = device_create_file(dev, &dev_attr_icnss_wlan_mode);
|
||||
if (ret) {
|
||||
pr_err("%s: wlan_mode sys file creation failed\n",
|
||||
__func__);
|
||||
goto err_wlan_mode;
|
||||
}
|
||||
|
||||
spin_lock_init(&penv->event_lock);
|
||||
|
||||
penv->event_wq = alloc_workqueue("icnss_driver_event", 0, 0);
|
||||
if (!penv->event_wq) {
|
||||
pr_err("%s: workqueue creation failed\n", __func__);
|
||||
ret = -EFAULT;
|
||||
goto err_workqueue;
|
||||
goto err_smmu_clock_enable;
|
||||
}
|
||||
|
||||
INIT_WORK(&penv->event_work, icnss_driver_event_work);
|
||||
|
@ -1742,6 +1892,8 @@ static int icnss_probe(struct platform_device *pdev)
|
|||
goto err_qmi;
|
||||
}
|
||||
|
||||
icnss_debugfs_create(penv);
|
||||
|
||||
pr_info("icnss: Platform driver probed successfully\n");
|
||||
|
||||
return ret;
|
||||
|
@ -1749,11 +1901,6 @@ static int icnss_probe(struct platform_device *pdev)
|
|||
err_qmi:
|
||||
if (penv->event_wq)
|
||||
destroy_workqueue(penv->event_wq);
|
||||
err_workqueue:
|
||||
device_remove_file(&pdev->dev, &dev_attr_icnss_wlan_mode);
|
||||
err_wlan_mode:
|
||||
if (penv->smmu_clk)
|
||||
icnss_clock_disable(penv->smmu_clk);
|
||||
err_smmu_clock_enable:
|
||||
if (penv->smmu_mapping)
|
||||
icnss_smmu_remove(&pdev->dev);
|
||||
|
@ -1782,13 +1929,14 @@ static int icnss_remove(struct platform_device *pdev)
|
|||
{
|
||||
int ret = 0;
|
||||
|
||||
icnss_debugfs_destroy(penv);
|
||||
|
||||
qmi_svc_event_notifier_unregister(WLFW_SERVICE_ID_V01,
|
||||
WLFW_SERVICE_VERS_V01,
|
||||
WLFW_SERVICE_INS_ID_V01,
|
||||
&wlfw_clnt_nb);
|
||||
if (penv->event_wq)
|
||||
destroy_workqueue(penv->event_wq);
|
||||
device_remove_file(&pdev->dev, &dev_attr_icnss_wlan_mode);
|
||||
|
||||
if (penv->smmu_mapping) {
|
||||
if (penv->smmu_clk)
|
||||
|
|
Loading…
Add table
Reference in a new issue