summaryrefslogtreecommitdiff
path: root/drivers/platform
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@localhost>2016-08-13 04:51:53 -0700
committerGerrit - the friendly Code Review server <code-review@localhost>2016-08-13 04:51:52 -0700
commit27d0d7b45ce87c971ba62b41c0d2fb8fa220f25e (patch)
tree69063a206cadfe99217f2b00124f30790a27447e /drivers/platform
parentab067aa67f21251f35bafb7e784c233e13c10ea6 (diff)
parent699fd2ab7f1d10fdbce7b932bf52fdfcb3b6ac11 (diff)
Merge "msm: ipa: Add support for IPA unit-test framework"
Diffstat (limited to 'drivers/platform')
-rw-r--r--drivers/platform/msm/Kconfig11
-rw-r--r--drivers/platform/msm/ipa/Makefile1
-rw-r--r--drivers/platform/msm/ipa/ipa_api.c44
-rw-r--r--drivers/platform/msm/ipa/ipa_common_i.h1
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c6
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_i.h1
-rw-r--r--drivers/platform/msm/ipa/test/Makefile2
-rw-r--r--drivers/platform/msm/ipa/test/ipa_test_example.c99
-rw-r--r--drivers/platform/msm/ipa/test/ipa_ut_framework.c969
-rw-r--r--drivers/platform/msm/ipa/test/ipa_ut_framework.h222
-rw-r--r--drivers/platform/msm/ipa/test/ipa_ut_i.h86
-rw-r--r--drivers/platform/msm/ipa/test/ipa_ut_suite_list.h35
12 files changed, 1476 insertions, 1 deletions
diff --git a/drivers/platform/msm/Kconfig b/drivers/platform/msm/Kconfig
index 18ae7fa5454b..9c2697dde76d 100644
--- a/drivers/platform/msm/Kconfig
+++ b/drivers/platform/msm/Kconfig
@@ -114,7 +114,6 @@ config IPA3
Kernel and user-space processes can call the IPA driver
to configure IPA core.
-
config RMNET_IPA3
tristate "IPA3 RMNET WWAN Network Device"
depends on IPA3 && MSM_QMI_INTERFACE
@@ -124,6 +123,16 @@ config RMNET_IPA3
for RmNet Data Driver and also exchange of QMI messages between
A7 and Q6 IPA-driver.
+config IPA_UT
+ tristate "IPA Unit-Test Framework and Test Suites"
+ depends on IPA3 && DEBUG_FS
+ help
+ This Module implements IPA in-kernel test framework.
+ The framework supports defining and running tests, grouped
+ into suites according to the sub-unit of the IPA being tested.
+ The user interface to run and control the tests is debugfs file
+ system.
+
config SSM
tristate "QTI Secure Service Module"
depends on QSEECOM
diff --git a/drivers/platform/msm/ipa/Makefile b/drivers/platform/msm/ipa/Makefile
index 704dd0abfefa..15ed471f383c 100644
--- a/drivers/platform/msm/ipa/Makefile
+++ b/drivers/platform/msm/ipa/Makefile
@@ -1,4 +1,5 @@
obj-$(CONFIG_IPA) += ipa_v2/ ipa_clients/ ipa_common
obj-$(CONFIG_IPA3) += ipa_v3/ ipa_clients/ ipa_common
+obj-$(CONFIG_IPA_UT) += test/
ipa_common += ipa_api.o ipa_rm.o ipa_rm_dependency_graph.o ipa_rm_peers_list.o ipa_rm_resource.o ipa_rm_inactivity_timer.o
diff --git a/drivers/platform/msm/ipa/ipa_api.c b/drivers/platform/msm/ipa/ipa_api.c
index 09d1166e29a6..a08d157e2b0f 100644
--- a/drivers/platform/msm/ipa/ipa_api.c
+++ b/drivers/platform/msm/ipa/ipa_api.c
@@ -2533,6 +2533,50 @@ int ipa_stop_gsi_channel(u32 clnt_hdl)
}
EXPORT_SYMBOL(ipa_stop_gsi_channel);
+/**
+ * ipa_get_version_string() - Get string representation of IPA version
+ * @ver: IPA version
+ *
+ * Return: Constant string representation
+ */
+const char *ipa_get_version_string(enum ipa_hw_type ver)
+{
+ const char *str;
+
+ switch (ver) {
+ case IPA_HW_v1_0:
+ str = "1.0";
+ break;
+ case IPA_HW_v1_1:
+ str = "1.1";
+ break;
+ case IPA_HW_v2_0:
+ str = "2.0";
+ break;
+ case IPA_HW_v2_1:
+ str = "2.1";
+ break;
+ case IPA_HW_v2_5:
+ str = "2.5/2.6";
+ break;
+ case IPA_HW_v2_6L:
+ str = "2.6L";
+ break;
+ case IPA_HW_v3_0:
+ str = "3.0";
+ break;
+ case IPA_HW_v3_1:
+ str = "3.1";
+ break;
+ default:
+ str = "Invalid version";
+ break;
+ }
+
+ return str;
+}
+EXPORT_SYMBOL(ipa_get_version_string);
+
static struct of_device_id ipa_plat_drv_match[] = {
{ .compatible = "qcom,ipa", },
{ .compatible = "qcom,ipa-smmu-ap-cb", },
diff --git a/drivers/platform/msm/ipa/ipa_common_i.h b/drivers/platform/msm/ipa/ipa_common_i.h
index 115348251d17..c5f75046cd2c 100644
--- a/drivers/platform/msm/ipa/ipa_common_i.h
+++ b/drivers/platform/msm/ipa/ipa_common_i.h
@@ -355,5 +355,6 @@ u8 *ipa_write_16(u16 hw, u8 *dest);
u8 *ipa_write_8(u8 b, u8 *dest);
u8 *ipa_pad_to_64(u8 *dest);
u8 *ipa_pad_to_32(u8 *dest);
+const char *ipa_get_version_string(enum ipa_hw_type ver);
#endif /* _IPA_COMMON_I_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
index c3c5ae38ec14..95ef9afbbd3e 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
@@ -2131,6 +2131,12 @@ void ipa3_debugfs_remove(void)
debugfs_remove_recursive(dent);
}
+struct dentry *ipa_debugfs_get_root(void)
+{
+ return dent;
+}
+EXPORT_SYMBOL(ipa_debugfs_get_root);
+
#else /* !CONFIG_DEBUG_FS */
void ipa3_debugfs_init(void) {}
void ipa3_debugfs_remove(void) {}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index 806510ea8867..a7587b2b9675 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -2012,4 +2012,5 @@ int ipa3_smmu_map_peer_buff(u64 iova, phys_addr_t phys_addr,
u32 size, bool map);
int ipa3_ntn_init(void);
int ipa3_get_ntn_stats(struct Ipa3HwStatsNTNInfoData_t *stats);
+struct dentry *ipa_debugfs_get_root(void);
#endif /* _IPA3_I_H_ */
diff --git a/drivers/platform/msm/ipa/test/Makefile b/drivers/platform/msm/ipa/test/Makefile
new file mode 100644
index 000000000000..62bb9a783c89
--- /dev/null
+++ b/drivers/platform/msm/ipa/test/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_IPA_UT) += ipa_ut_mod.o
+ipa_ut_mod-y := ipa_ut_framework.o ipa_test_example.o
diff --git a/drivers/platform/msm/ipa/test/ipa_test_example.c b/drivers/platform/msm/ipa/test/ipa_test_example.c
new file mode 100644
index 000000000000..0313375ec3d3
--- /dev/null
+++ b/drivers/platform/msm/ipa/test/ipa_test_example.c
@@ -0,0 +1,99 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "ipa_ut_framework.h"
+
+/**
+ * Example IPA Unit-test suite
+ * To be a reference for writing new suites and tests.
+ * This suite is also used as unit-test for the testing framework itself.
+ * Structure:
+ * 1- Define the setup and teardown functions
+ * Not Mandatory. Null may be used as well
+ * 2- For each test, define its Run() function
+ * 3- Use IPA_UT_DEFINE_SUITE_START() to start defining the suite
+ * 4- use IPA_UT_ADD_TEST() for adding tests within
+ * the suite definition block
+ * 5- IPA_UT_DEFINE_SUITE_END() close the suite definition
+ */
+
+static int ipa_test_example_dummy;
+
+static int ipa_test_example_suite_setup(void **ppriv)
+{
+ IPA_UT_DBG("Start Setup - set 0x1234F\n");
+
+ ipa_test_example_dummy = 0x1234F;
+ *ppriv = (void *)&ipa_test_example_dummy;
+
+ return 0;
+}
+
+static int ipa_test_example_teardown(void *priv)
+{
+ IPA_UT_DBG("Start Teardown\n");
+ IPA_UT_DBG("priv=0x%p - value=0x%x\n", priv, *((int *)priv));
+
+ return 0;
+}
+
+static int ipa_test_example_test1(void *priv)
+{
+ IPA_UT_LOG("priv=0x%p - value=0x%x\n", priv, *((int *)priv));
+ ipa_test_example_dummy++;
+
+ return 0;
+}
+
+static int ipa_test_example_test2(void *priv)
+{
+ IPA_UT_LOG("priv=0x%p - value=0x%x\n", priv, *((int *)priv));
+ ipa_test_example_dummy++;
+
+ return 0;
+}
+
+static int ipa_test_example_test3(void *priv)
+{
+ IPA_UT_LOG("priv=0x%p - value=0x%x\n", priv, *((int *)priv));
+ ipa_test_example_dummy++;
+
+ return 0;
+}
+
+static int ipa_test_example_test4(void *priv)
+{
+ IPA_UT_LOG("priv=0x%p - value=0x%x\n", priv, *((int *)priv));
+ ipa_test_example_dummy++;
+
+ IPA_UT_TEST_FAIL_REPORT("failed on test");
+
+ return -EFAULT;
+}
+
+/* Suite definition block */
+IPA_UT_DEFINE_SUITE_START(example, "Example suite",
+ ipa_test_example_suite_setup, ipa_test_example_teardown)
+{
+ IPA_UT_ADD_TEST(test1, "This is test number 1",
+ ipa_test_example_test1, false, IPA_HW_v1_0, IPA_HW_MAX),
+
+ IPA_UT_ADD_TEST(test2, "This is test number 2",
+ ipa_test_example_test2, false, IPA_HW_v1_0, IPA_HW_MAX),
+
+ IPA_UT_ADD_TEST(test3, "This is test number 3",
+ ipa_test_example_test3, false, IPA_HW_v1_1, IPA_HW_v2_6),
+
+ IPA_UT_ADD_TEST(test4, "This is test number 4",
+ ipa_test_example_test4, false, IPA_HW_v1_1, IPA_HW_MAX),
+
+} IPA_UT_DEFINE_SUITE_END(example);
diff --git a/drivers/platform/msm/ipa/test/ipa_ut_framework.c b/drivers/platform/msm/ipa/test/ipa_ut_framework.c
new file mode 100644
index 000000000000..8816fc37c2e2
--- /dev/null
+++ b/drivers/platform/msm/ipa/test/ipa_ut_framework.c
@@ -0,0 +1,969 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/ipa.h>
+#include "../ipa_v3/ipa_i.h"
+#include "ipa_ut_framework.h"
+#include "ipa_ut_suite_list.h"
+#include "ipa_ut_i.h"
+
+
+#define IPA_UT_DEBUG_WRITE_BUF_SIZE 256
+#define IPA_UT_DEBUG_READ_BUF_SIZE 1024
+
+#define IPA_UT_READ_WRITE_DBG_FILE_MODE \
+ (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP)
+
+/**
+ * struct ipa_ut_context - I/S context
+ * @inited: Will wait till IPA is ready. Will create the enable file
+ * @enabled: All tests and suite debugfs files are created
+ * @lock: Lock for mutual exclustion
+ * @ipa_dbgfs_root: IPA root debugfs folder
+ * @test_dbgfs_root: UT root debugfs folder. Sub-folder of IPA root
+ * @test_dbgfs_suites: Suites root debugfs folder. Sub-folder of UT root
+ */
+struct ipa_ut_context {
+ bool inited;
+ bool enabled;
+ struct mutex lock;
+ struct dentry *ipa_dbgfs_root;
+ struct dentry *test_dbgfs_root;
+ struct dentry *test_dbgfs_suites;
+};
+
+static ssize_t ipa_ut_dbgfs_enable_read(struct file *file,
+ char __user *ubuf, size_t count, loff_t *ppos);
+static ssize_t ipa_ut_dbgfs_enable_write(struct file *file,
+ const char __user *buf, size_t count, loff_t *ppos);
+static ssize_t ipa_ut_dbgfs_test_read(struct file *file,
+ char __user *ubuf, size_t count, loff_t *ppos);
+static ssize_t ipa_ut_dbgfs_test_write(struct file *file,
+ const char __user *buf, size_t count, loff_t *ppos);
+static int ipa_ut_dbgfs_all_test_open(struct inode *inode,
+ struct file *filp);
+static int ipa_ut_dbgfs_regression_test_open(struct inode *inode,
+ struct file *filp);
+static ssize_t ipa_ut_dbgfs_meta_test_read(struct file *file,
+ char __user *ubuf, size_t count, loff_t *ppos);
+static ssize_t ipa_ut_dbgfs_meta_test_write(struct file *file,
+ const char __user *buf, size_t count, loff_t *ppos);
+
+
+static const struct file_operations ipa_ut_dbgfs_enable_fops = {
+ .read = ipa_ut_dbgfs_enable_read,
+ .write = ipa_ut_dbgfs_enable_write,
+};
+static const struct file_operations ipa_ut_dbgfs_test_fops = {
+ .read = ipa_ut_dbgfs_test_read,
+ .write = ipa_ut_dbgfs_test_write,
+};
+static const struct file_operations ipa_ut_dbgfs_all_test_fops = {
+ .open = ipa_ut_dbgfs_all_test_open,
+ .read = ipa_ut_dbgfs_meta_test_read,
+ .write = ipa_ut_dbgfs_meta_test_write,
+};
+static const struct file_operations ipa_ut_dbgfs_regression_test_fops = {
+ .open = ipa_ut_dbgfs_regression_test_open,
+ .read = ipa_ut_dbgfs_meta_test_read,
+ .write = ipa_ut_dbgfs_meta_test_write,
+};
+
+static struct ipa_ut_context *ipa_ut_ctx;
+char *_IPA_UT_TEST_LOG_BUF_NAME;
+struct ipa_ut_tst_fail_report _IPA_UT_TEST_FAIL_REPORT_DATA;
+
+
+/**
+ * ipa_ut_show_suite_exec_summary() - Show tests run summary
+ * @suite: suite to print its running summary
+ *
+ * Print list of succeeded tests, failed tests and skipped tests
+ *
+ * Note: Assumes lock acquired
+ */
+static void ipa_ut_show_suite_exec_summary(const struct ipa_ut_suite *suite)
+{
+ int i;
+
+ IPA_UT_DBG("Entry\n");
+
+ ipa_assert_on(!suite);
+
+ pr_info("\n\n");
+ pr_info("\t Suite '%s' summary\n", suite->meta_data->name);
+ pr_info("===========================\n");
+ pr_info("Successful tests\n");
+ pr_info("----------------\n");
+ for (i = 0 ; i < suite->tests_cnt ; i++) {
+ if (suite->tests[i].res != IPA_UT_TEST_RES_SUCCESS)
+ continue;
+ pr_info("\t%s\n", suite->tests[i].name);
+ }
+ pr_info("\nFailed tests\n");
+ pr_info("------------\n");
+ for (i = 0 ; i < suite->tests_cnt ; i++) {
+ if (suite->tests[i].res != IPA_UT_TEST_RES_FAIL)
+ continue;
+ pr_info("\t%s\n", suite->tests[i].name);
+ }
+ pr_info("\nSkipped tests\n");
+ pr_info("-------------\n");
+ for (i = 0 ; i < suite->tests_cnt ; i++) {
+ if (suite->tests[i].res != IPA_UT_TEST_RES_SKIP)
+ continue;
+ pr_info("\t%s\n", suite->tests[i].name);
+ }
+ pr_info("\n");
+}
+
+/**
+ * ipa_ut_dbgfs_meta_test_write() - Debugfs write func for a for a meta test
+ * @params: write fops
+ *
+ * Used to run all/regression tests in a suite
+ * Create log buffer that the test can use to store ongoing logs
+ * IPA clocks need to be voted.
+ * Run setup() once before running the tests and teardown() once after
+ * If no such call-backs then ignore it; if failed then fail the suite
+ * Print tests progress during running
+ * Test log and fail report will be showed only if the test failed.
+ * Finally show Summary of the suite tests running
+ *
+ * Note: If test supported IPA H/W version mismatch, skip it
+ * If a test lack run function, skip it
+ * If test doesn't belong to regression and it is regression run, skip it
+ * Note: Running mode: Do not stop running on failure
+ *
+ * Return: Negative in failure, given characters amount in success
+ */
+static ssize_t ipa_ut_dbgfs_meta_test_write(struct file *file,
+ const char __user *buf, size_t count, loff_t *ppos)
+{
+ struct ipa_ut_suite *suite;
+ int i;
+ enum ipa_hw_type ipa_ver;
+ int rc = 0;
+ long meta_type;
+ bool tst_fail = false;
+
+ IPA_UT_DBG("Entry\n");
+
+ mutex_lock(&ipa_ut_ctx->lock);
+ suite = file->f_inode->i_private;
+ ipa_assert_on(!suite);
+ meta_type = (long)(file->private_data);
+ IPA_UT_DBG("Meta test type %ld\n", meta_type);
+
+ _IPA_UT_TEST_LOG_BUF_NAME = kzalloc(_IPA_UT_TEST_LOG_BUF_SIZE,
+ GFP_KERNEL);
+ if (!_IPA_UT_TEST_LOG_BUF_NAME) {
+ IPA_UT_ERR("failed to allocate %d bytes\n",
+ _IPA_UT_TEST_LOG_BUF_SIZE);
+ rc = -ENOMEM;
+ goto unlock_mutex;
+ }
+
+ if (!suite->tests_cnt || !suite->tests) {
+ pr_info("No tests for suite '%s'\n", suite->meta_data->name);
+ goto free_mem;
+ }
+
+ ipa_ver = ipa_get_hw_type();
+
+ IPA_ACTIVE_CLIENTS_INC_SPECIAL("IPA_UT");
+
+ if (suite->meta_data->setup) {
+ pr_info("*** Suite '%s': Run setup ***\n",
+ suite->meta_data->name);
+ rc = suite->meta_data->setup(&suite->meta_data->priv);
+ if (rc) {
+ IPA_UT_ERR("Setup failed for suite %s\n",
+ suite->meta_data->name);
+ rc = -EFAULT;
+ goto release_clock;
+ }
+ } else {
+ pr_info("*** Suite '%s': No Setup ***\n",
+ suite->meta_data->name);
+ }
+
+ pr_info("*** Suite '%s': Run %s tests ***\n\n",
+ suite->meta_data->name,
+ meta_type == IPA_UT_META_TEST_REGRESSION ? "regression" : "all"
+ );
+ for (i = 0 ; i < suite->tests_cnt ; i++) {
+ if (meta_type == IPA_UT_META_TEST_REGRESSION &&
+ !suite->tests[i].run_in_regression) {
+ pr_info(
+ "*** Test '%s': Skip - Not in regression ***\n\n"
+ , suite->tests[i].name);
+ suite->tests[i].res = IPA_UT_TEST_RES_SKIP;
+ continue;
+ }
+ if (suite->tests[i].min_ipa_hw_ver > ipa_ver ||
+ suite->tests[i].max_ipa_hw_ver < ipa_ver) {
+ pr_info(
+ "*** Test '%s': Skip - IPA VER mismatch ***\n\n"
+ , suite->tests[i].name);
+ suite->tests[i].res = IPA_UT_TEST_RES_SKIP;
+ continue;
+ }
+ if (!suite->tests[i].run) {
+ pr_info(
+ "*** Test '%s': Skip - No Run function ***\n\n"
+ , suite->tests[i].name);
+ suite->tests[i].res = IPA_UT_TEST_RES_SKIP;
+ continue;
+ }
+
+ _IPA_UT_TEST_LOG_BUF_NAME[0] = '\0';
+ _IPA_UT_TEST_FAIL_REPORT_DATA.valid = false;
+ pr_info("*** Test '%s': Running... ***\n",
+ suite->tests[i].name);
+ rc = suite->tests[i].run(suite->meta_data->priv);
+ if (rc) {
+ tst_fail = true;
+ suite->tests[i].res = IPA_UT_TEST_RES_FAIL;
+ pr_info("%s", _IPA_UT_TEST_LOG_BUF_NAME);
+ } else {
+ suite->tests[i].res = IPA_UT_TEST_RES_SUCCESS;
+ }
+
+ pr_info(">>>>>>**** TEST '%s': %s ****<<<<<<\n",
+ suite->tests[i].name, tst_fail ? "FAIL" : "SUCCESS");
+
+ if (tst_fail && _IPA_UT_TEST_FAIL_REPORT_DATA.valid) {
+ pr_info("*** FAIL INFO:\n");
+ pr_info("\tFILE = %s\n\tFUNC = %s()\n\tLINE = %d\n",
+ _IPA_UT_TEST_FAIL_REPORT_DATA.file,
+ _IPA_UT_TEST_FAIL_REPORT_DATA.func,
+ _IPA_UT_TEST_FAIL_REPORT_DATA.line);
+ pr_info("\t%s\n", _IPA_UT_TEST_FAIL_REPORT_DATA.info);
+ }
+
+ pr_info("\n");
+ }
+
+ if (suite->meta_data->teardown) {
+ pr_info("*** Suite '%s': Run Teardown ***\n",
+ suite->meta_data->name);
+ rc = suite->meta_data->teardown(suite->meta_data->priv);
+ if (rc) {
+ IPA_UT_ERR("Teardown failed for suite %s\n",
+ suite->meta_data->name);
+ rc = -EFAULT;
+ goto release_clock;
+ }
+ } else {
+ pr_info("*** Suite '%s': No Teardown ***\n",
+ suite->meta_data->name);
+ }
+
+ ipa_ut_show_suite_exec_summary(suite);
+
+release_clock:
+ IPA_ACTIVE_CLIENTS_DEC_SPECIAL("IPA_UT");
+free_mem:
+ kfree(_IPA_UT_TEST_LOG_BUF_NAME);
+unlock_mutex:
+ mutex_unlock(&ipa_ut_ctx->lock);
+ return ((!rc && !tst_fail) ? count : -EFAULT);
+}
+
+/**
+ * ipa_ut_dbgfs_meta_test_read() - Debugfs read func for a meta test
+ * @params: read fops
+ *
+ * Meta test, is a test that describes other test or bunch of tests.
+ * for example, the 'all' test. Running this test will run all
+ * the tests in the suite.
+ *
+ * Show information regard the suite. E.g. name and description
+ * If regression - List the regression tests names
+ *
+ * Return: Amount of characters written to user space buffer
+ */
+static ssize_t ipa_ut_dbgfs_meta_test_read(struct file *file,
+ char __user *ubuf, size_t count, loff_t *ppos)
+{
+ char *buf;
+ struct ipa_ut_suite *suite;
+ int nbytes;
+ ssize_t cnt;
+ long meta_type;
+ int i;
+
+ IPA_UT_DBG("Entry\n");
+
+ mutex_lock(&ipa_ut_ctx->lock);
+ suite = file->f_inode->i_private;
+ ipa_assert_on(!suite);
+ meta_type = (long)(file->private_data);
+ IPA_UT_DBG("Meta test type %ld\n", meta_type);
+
+ buf = kmalloc(IPA_UT_DEBUG_READ_BUF_SIZE, GFP_KERNEL);
+ if (!buf) {
+ IPA_UT_ERR("failed to allocate %d bytes\n",
+ IPA_UT_DEBUG_READ_BUF_SIZE);
+ cnt = 0;
+ goto unlock_mutex;
+ }
+
+ if (meta_type == IPA_UT_META_TEST_ALL) {
+ nbytes = scnprintf(buf, IPA_UT_DEBUG_READ_BUF_SIZE,
+ "\tMeta-test running all the tests in the suite:\n"
+ "\tSuite Name: %s\n"
+ "\tDescription: %s\n"
+ "\tNumber of test in suite: %zu\n",
+ suite->meta_data->name,
+ suite->meta_data->desc ?: "",
+ suite->tests_cnt);
+ } else {
+ nbytes = scnprintf(buf, IPA_UT_DEBUG_READ_BUF_SIZE,
+ "\tMeta-test running regression tests in the suite:\n"
+ "\tSuite Name: %s\n"
+ "\tDescription: %s\n"
+ "\tRegression tests:\n",
+ suite->meta_data->name,
+ suite->meta_data->desc ?: "");
+ for (i = 0 ; i < suite->tests_cnt ; i++) {
+ if (!suite->tests[i].run_in_regression)
+ continue;
+ nbytes += scnprintf(buf + nbytes,
+ IPA_UT_DEBUG_READ_BUF_SIZE - nbytes,
+ "\t\t%s\n", suite->tests[i].name);
+ }
+ }
+
+ cnt = simple_read_from_buffer(ubuf, count, ppos, buf, nbytes);
+ kfree(buf);
+
+unlock_mutex:
+ mutex_unlock(&ipa_ut_ctx->lock);
+ return cnt;
+}
+
+/**
+ * ipa_ut_dbgfs_regression_test_open() - Debugfs open function for
+ * 'regression' tests
+ * @params: open fops
+ *
+ * Mark "Regression tests" for meta-tests later operations.
+ *
+ * Return: Zero (always success).
+ */
+static int ipa_ut_dbgfs_regression_test_open(struct inode *inode,
+ struct file *filp)
+{
+ IPA_UT_DBG("Entry\n");
+
+ filp->private_data = (void *)(IPA_UT_META_TEST_REGRESSION);
+
+ return 0;
+}
+
+/**
+ * ipa_ut_dbgfs_all_test_open() - Debugfs open function for 'all' tests
+ * @params: open fops
+ *
+ * Mark "All tests" for meta-tests later operations.
+ *
+ * Return: Zero (always success).
+ */
+static int ipa_ut_dbgfs_all_test_open(struct inode *inode,
+ struct file *filp)
+{
+ IPA_UT_DBG("Entry\n");
+
+ filp->private_data = (void *)(IPA_UT_META_TEST_ALL);
+
+ return 0;
+}
+
+/**
+ * ipa_ut_dbgfs_test_write() - Debugfs write function for a test
+ * @params: write fops
+ *
+ * Used to run a test.
+ * Create log buffer that the test can use to store ongoing logs
+ * IPA clocks need to be voted.
+ * Run setup() before the test and teardown() after the tests.
+ * If no such call-backs then ignore it; if failed then fail the test
+ * If all succeeds, no printing to user
+ * If failed, test logs and failure report will be printed to user
+ *
+ * Note: Test must has run function and it's supported IPA H/W version
+ * must be matching. Otherwise test will fail.
+ *
+ * Return: Negative in failure, given characters amount in success
+ */
+static ssize_t ipa_ut_dbgfs_test_write(struct file *file,
+ const char __user *buf, size_t count, loff_t *ppos)
+{
+ struct ipa_ut_test *test;
+ struct ipa_ut_suite *suite;
+ bool tst_fail = false;
+ int rc = 0;
+ enum ipa_hw_type ipa_ver;
+
+ IPA_UT_DBG("Entry\n");
+
+ mutex_lock(&ipa_ut_ctx->lock);
+ test = file->f_inode->i_private;
+ ipa_assert_on(!test);
+
+ _IPA_UT_TEST_LOG_BUF_NAME = kzalloc(_IPA_UT_TEST_LOG_BUF_SIZE,
+ GFP_KERNEL);
+ if (!_IPA_UT_TEST_LOG_BUF_NAME) {
+ IPA_UT_ERR("failed to allocate %d bytes\n",
+ _IPA_UT_TEST_LOG_BUF_SIZE);
+ rc = -ENOMEM;
+ goto unlock_mutex;
+ }
+
+ if (!test->run) {
+ IPA_UT_ERR("*** Test %s - No run func ***\n",
+ test->name);
+ rc = -EFAULT;
+ goto free_mem;
+ }
+
+ ipa_ver = ipa_get_hw_type();
+ if (test->min_ipa_hw_ver > ipa_ver ||
+ test->max_ipa_hw_ver < ipa_ver) {
+ IPA_UT_ERR("Cannot run test %s on IPA HW Ver %s\n",
+ test->name, ipa_get_version_string(ipa_ver));
+ rc = -EFAULT;
+ goto free_mem;
+ }
+
+ IPA_ACTIVE_CLIENTS_INC_SPECIAL("IPA_UT");
+
+ suite = test->suite;
+ if (suite && suite->meta_data->setup) {
+ IPA_UT_DBG("*** Suite '%s': Run setup ***\n",
+ suite->meta_data->name);
+ rc = suite->meta_data->setup(&suite->meta_data->priv);
+ if (rc) {
+ IPA_UT_ERR("Setup failed for suite %s\n",
+ suite->meta_data->name);
+ rc = -EFAULT;
+ goto release_clock;
+ }
+ } else {
+ IPA_UT_DBG("*** Suite '%s': No Setup ***\n",
+ suite->meta_data->name);
+ }
+
+ IPA_UT_DBG("*** Test '%s': Running... ***\n", test->name);
+ _IPA_UT_TEST_FAIL_REPORT_DATA.valid = false;
+ rc = test->run(suite->meta_data->priv);
+ if (rc)
+ tst_fail = true;
+ IPA_UT_DBG("*** Test %s - ***\n", tst_fail ? "FAIL" : "SUCCESS");
+ if (tst_fail) {
+ pr_info("=================>>>>>>>>>>>\n");
+ pr_info("%s\n", _IPA_UT_TEST_LOG_BUF_NAME);
+ pr_info("**** TEST %s FAILED ****\n", test->name);
+ if (_IPA_UT_TEST_FAIL_REPORT_DATA.valid) {
+ pr_info("*** FAIL INFO:\n");
+ pr_info("\tFILE = %s\n\tFUNC = %s()\n\tLINE = %d\n",
+ _IPA_UT_TEST_FAIL_REPORT_DATA.file,
+ _IPA_UT_TEST_FAIL_REPORT_DATA.func,
+ _IPA_UT_TEST_FAIL_REPORT_DATA.line);
+ pr_info("\t%s\n", _IPA_UT_TEST_FAIL_REPORT_DATA.info);
+ }
+ pr_info("<<<<<<<<<<<=================\n");
+ }
+
+ if (suite && suite->meta_data->teardown) {
+ IPA_UT_DBG("*** Suite '%s': Run Teardown ***\n",
+ suite->meta_data->name);
+ rc = suite->meta_data->teardown(suite->meta_data->priv);
+ if (rc) {
+ IPA_UT_ERR("Teardown failed for suite %s\n",
+ suite->meta_data->name);
+ rc = -EFAULT;
+ goto release_clock;
+ }
+ } else {
+ IPA_UT_DBG("*** Suite '%s': No Teardown ***\n",
+ suite->meta_data->name);
+ }
+
+release_clock:
+ IPA_ACTIVE_CLIENTS_DEC_SPECIAL("IPA_UT");
+free_mem:
+ kfree(_IPA_UT_TEST_LOG_BUF_NAME);
+unlock_mutex:
+ mutex_unlock(&ipa_ut_ctx->lock);
+ return ((!rc && !tst_fail) ? count : -EFAULT);
+}
+
+/**
+ * ipa_ut_dbgfs_test_read() - Debugfs read function for a test
+ * @params: read fops
+ *
+ * print information regard the test. E.g. name and description
+ *
+ * Return: Amount of characters written to user space buffer
+ */
+static ssize_t ipa_ut_dbgfs_test_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ char *buf;
+ struct ipa_ut_test *test;
+ int nbytes;
+ ssize_t cnt;
+
+ IPA_UT_DBG("Entry\n");
+
+ mutex_lock(&ipa_ut_ctx->lock);
+ test = file->f_inode->i_private;
+ ipa_assert_on(!test);
+
+ buf = kmalloc(IPA_UT_DEBUG_READ_BUF_SIZE, GFP_KERNEL);
+ if (!buf) {
+ IPA_UT_ERR("failed to allocate %d bytes\n",
+ IPA_UT_DEBUG_READ_BUF_SIZE);
+ cnt = 0;
+ goto unlock_mutex;
+ }
+
+ nbytes = scnprintf(buf, IPA_UT_DEBUG_READ_BUF_SIZE,
+ "\t Test Name: %s\n"
+ "\t Description: %s\n"
+ "\t Suite Name: %s\n"
+ "\t Run In Regression: %s\n"
+ "\t Supported IPA versions: [%s -> %s]\n",
+ test->name, test->desc ?: "", test->suite->meta_data->name,
+ test->run_in_regression ? "Yes" : "No",
+ ipa_get_version_string(test->min_ipa_hw_ver),
+ test->max_ipa_hw_ver == IPA_HW_MAX ? "MAX" :
+ ipa_get_version_string(test->max_ipa_hw_ver));
+
+ if (nbytes > count)
+ IPA_UT_ERR("User buf too small - return partial info\n");
+
+ cnt = simple_read_from_buffer(ubuf, count, ppos, buf, nbytes);
+ kfree(buf);
+
+unlock_mutex:
+ mutex_unlock(&ipa_ut_ctx->lock);
+ return cnt;
+}
+
+/**
+ * ipa_ut_framework_load_suites() - Load tests and expose them to user space
+ *
+ * Creates debugfs folder for each suite and then file for each test in it.
+ * Create debugfs "all" file for each suite for meta-test to run all tests.
+ *
+ * Note: Assumes lock acquired
+ *
+ * Return: Zero in success, otherwise in failure
+ */
+int ipa_ut_framework_load_suites(void)
+{
+ int suite_idx;
+ int tst_idx;
+ struct ipa_ut_suite *suite;
+ struct dentry *s_dent;
+ struct dentry *f_dent;
+
+ IPA_UT_DBG("Entry\n");
+
+ for (suite_idx = IPA_UT_SUITE_FIRST_INDEX;
+ suite_idx < IPA_UT_SUITES_COUNT; suite_idx++) {
+ suite = IPA_UT_GET_SUITE(suite_idx);
+
+ if (!suite->meta_data->name) {
+ IPA_UT_ERR("No suite name\n");
+ return -EFAULT;
+ }
+
+ s_dent = debugfs_create_dir(suite->meta_data->name,
+ ipa_ut_ctx->test_dbgfs_suites);
+
+ if (!s_dent || IS_ERR(s_dent)) {
+ IPA_UT_ERR("fail create dbg entry - suite %s\n",
+ suite->meta_data->name);
+ return -EFAULT;
+ }
+
+ for (tst_idx = 0; tst_idx < suite->tests_cnt ; tst_idx++) {
+ if (!suite->tests[tst_idx].name) {
+ IPA_UT_ERR("No test name on suite %s\n",
+ suite->meta_data->name);
+ return -EFAULT;
+ }
+ f_dent = debugfs_create_file(
+ suite->tests[tst_idx].name,
+ IPA_UT_READ_WRITE_DBG_FILE_MODE, s_dent,
+ &suite->tests[tst_idx],
+ &ipa_ut_dbgfs_test_fops);
+ if (!f_dent || IS_ERR(f_dent)) {
+ IPA_UT_ERR("fail create dbg entry - tst %s\n",
+ suite->tests[tst_idx].name);
+ return -EFAULT;
+ }
+ }
+
+ /* entry for meta-test all to run all tests in suites */
+ f_dent = debugfs_create_file(_IPA_UT_RUN_ALL_TEST_NAME,
+ IPA_UT_READ_WRITE_DBG_FILE_MODE, s_dent,
+ suite, &ipa_ut_dbgfs_all_test_fops);
+ if (!f_dent || IS_ERR(f_dent)) {
+ IPA_UT_ERR("fail to create dbg entry - %s\n",
+ _IPA_UT_RUN_ALL_TEST_NAME);
+ return -EFAULT;
+ }
+
+ /*
+ * entry for meta-test regression to run all regression
+ * tests in suites
+ */
+ f_dent = debugfs_create_file(_IPA_UT_RUN_REGRESSION_TEST_NAME,
+ IPA_UT_READ_WRITE_DBG_FILE_MODE, s_dent,
+ suite, &ipa_ut_dbgfs_regression_test_fops);
+ if (!f_dent || IS_ERR(f_dent)) {
+ IPA_UT_ERR("fail to create dbg entry - %s\n",
+ _IPA_UT_RUN_ALL_TEST_NAME);
+ return -EFAULT;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * ipa_ut_framework_enable() - Enable the framework
+ *
+ * Creates the tests and suites debugfs entries and load them.
+ * This will expose the tests to user space.
+ *
+ * Return: Zero in success, otherwise in failure
+ */
+static int ipa_ut_framework_enable(void)
+{
+ int ret = 0;
+
+ IPA_UT_DBG("Entry\n");
+
+ mutex_lock(&ipa_ut_ctx->lock);
+
+ if (ipa_ut_ctx->enabled) {
+ IPA_UT_ERR("Already enabled\n");
+ goto unlock_mutex;
+ }
+
+ ipa_ut_ctx->test_dbgfs_suites = debugfs_create_dir("suites",
+ ipa_ut_ctx->test_dbgfs_root);
+ if (!ipa_ut_ctx->test_dbgfs_suites ||
+ IS_ERR(ipa_ut_ctx->test_dbgfs_suites)) {
+ IPA_UT_ERR("failed to create suites debugfs dir\n");
+ ret = -EFAULT;
+ goto unlock_mutex;
+ }
+
+ if (ipa_ut_framework_load_suites()) {
+ IPA_UT_ERR("failed to load the suites into debugfs\n");
+ ret = -EFAULT;
+ goto fail_clean_suites_dbgfs;
+ }
+
+ ipa_ut_ctx->enabled = true;
+ goto unlock_mutex;
+
+fail_clean_suites_dbgfs:
+ debugfs_remove_recursive(ipa_ut_ctx->test_dbgfs_suites);
+unlock_mutex:
+ mutex_unlock(&ipa_ut_ctx->lock);
+ return ret;
+}
+
+/**
+ * ipa_ut_framework_disable() - Disable the framework
+ *
+ * Remove the tests and suites debugfs exposure.
+ *
+ * Return: Zero in success, otherwise in failure
+ */
+static int ipa_ut_framework_disable(void)
+{
+ int ret = 0;
+
+ IPA_UT_DBG("Entry\n");
+
+ mutex_lock(&ipa_ut_ctx->lock);
+
+ if (!ipa_ut_ctx->enabled) {
+ IPA_UT_ERR("Already disabled\n");
+ goto unlock_mutex;
+ }
+
+ debugfs_remove_recursive(ipa_ut_ctx->test_dbgfs_suites);
+
+ ipa_ut_ctx->enabled = false;
+
+unlock_mutex:
+ mutex_unlock(&ipa_ut_ctx->lock);
+ return ret;
+}
+
+/**
+ * ipa_ut_dbgfs_enable_write() - Debugfs enable file write fops
+ * @params: write fops
+ *
+ * Input should be number. If 0, then disable. Otherwise enable.
+ *
+ * Return: if failed then negative value, if succeeds, amount of given chars
+ */
+static ssize_t ipa_ut_dbgfs_enable_write(struct file *file,
+ const char __user *buf, size_t count, loff_t *ppos)
+{
+ char lcl_buf[IPA_UT_DEBUG_WRITE_BUF_SIZE];
+ s8 option = 0;
+ int ret;
+
+ IPA_UT_DBG("Entry\n");
+
+ if (sizeof(lcl_buf) < count + 1) {
+ IPA_UT_ERR("No enough space\n");
+ return -E2BIG;
+ }
+
+ if (copy_from_user(lcl_buf, buf, count)) {
+ IPA_UT_ERR("fail to copy buf from user space\n");
+ return -EFAULT;
+ }
+
+ lcl_buf[count] = '\0';
+ if (kstrtos8(lcl_buf, 0, &option)) {
+ IPA_UT_ERR("fail convert str to s8\n");
+ return -EINVAL;
+ }
+
+ if (option == 0)
+ ret = ipa_ut_framework_disable();
+ else
+ ret = ipa_ut_framework_enable();
+
+ return ret ?: count;
+}
+
+/**
+ * ipa_ut_dbgfs_enable_read() - Debugfs enable file read fops
+ * @params: read fops
+ *
+ * To show to user space if the I/S is enabled or disabled.
+ *
+ * Return: amount of characters returned to user space
+ */
+static ssize_t ipa_ut_dbgfs_enable_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ const char *status;
+
+ IPA_UT_DBG("Entry\n");
+
+ mutex_lock(&ipa_ut_ctx->lock);
+ status = ipa_ut_ctx->enabled ?
+ "Enabled - Write 0 to disable\n" :
+ "Disabled - Write 1 to enable\n";
+ mutex_unlock(&ipa_ut_ctx->lock);
+ return simple_read_from_buffer(ubuf, count, ppos,
+ status, strlen(status));
+}
+
+/**
+ * ipa_ut_framework_init() - Unit-tests framework initialization
+ *
+ * Complete tests initialization: Each tests needs to point to it's
+ * corresponing suite.
+ * Creates the framework debugfs root directory under IPA directory.
+ * Create enable debugfs file - to enable/disable the framework.
+ *
+ * Return: Zero in success, otherwise in failure
+ */
+static int ipa_ut_framework_init(void)
+{
+ struct dentry *dfile_enable;
+ int ret;
+ int suite_idx;
+ int test_idx;
+ struct ipa_ut_suite *suite;
+
+ IPA_UT_DBG("Entry\n");
+
+ ipa_assert_on(!ipa_ut_ctx);
+
+ ipa_ut_ctx->ipa_dbgfs_root = ipa_debugfs_get_root();
+ if (!ipa_ut_ctx->ipa_dbgfs_root) {
+ IPA_UT_ERR("No IPA debugfs root entry\n");
+ return -EFAULT;
+ }
+
+ mutex_lock(&ipa_ut_ctx->lock);
+
+ /* tests needs to point to their corresponding suites structures */
+ for (suite_idx = IPA_UT_SUITE_FIRST_INDEX;
+ suite_idx < IPA_UT_SUITES_COUNT; suite_idx++) {
+ suite = IPA_UT_GET_SUITE(suite_idx);
+ ipa_assert_on(!suite);
+ if (!suite->tests) {
+ IPA_UT_DBG("No tests for suite %s\n",
+ suite->meta_data->name);
+ continue;
+ }
+ for (test_idx = 0; test_idx < suite->tests_cnt; test_idx++) {
+ suite->tests[test_idx].suite = suite;
+ IPA_UT_DBG("Updating test %s info for suite %s\n",
+ suite->tests[test_idx].name,
+ suite->meta_data->name);
+ }
+ }
+
+ ipa_ut_ctx->test_dbgfs_root = debugfs_create_dir("test",
+ ipa_ut_ctx->ipa_dbgfs_root);
+ if (!ipa_ut_ctx->test_dbgfs_root ||
+ IS_ERR(ipa_ut_ctx->test_dbgfs_root)) {
+ IPA_UT_ERR("failed to create test debugfs dir\n");
+ ret = -EFAULT;
+ goto unlock_mutex;
+ }
+
+ dfile_enable = debugfs_create_file("enable",
+ IPA_UT_READ_WRITE_DBG_FILE_MODE,
+ ipa_ut_ctx->test_dbgfs_root, 0, &ipa_ut_dbgfs_enable_fops);
+ if (!dfile_enable || IS_ERR(dfile_enable)) {
+ IPA_UT_ERR("failed to create enable debugfs file\n");
+ ret = -EFAULT;
+ goto fail_clean_dbgfs;
+ }
+
+ ipa_ut_ctx->inited = true;
+ IPA_UT_DBG("Done\n");
+ ret = 0;
+ goto unlock_mutex;
+
+fail_clean_dbgfs:
+ debugfs_remove_recursive(ipa_ut_ctx->test_dbgfs_root);
+unlock_mutex:
+ mutex_unlock(&ipa_ut_ctx->lock);
+ return ret;
+}
+
+/**
+ * ipa_ut_framework_destroy() - Destroy the UT framework info
+ *
+ * Disable it if enabled.
+ * Remove the debugfs entries using the root entry
+ */
+static void ipa_ut_framework_destroy(void)
+{
+ IPA_UT_DBG("Entry\n");
+
+ mutex_lock(&ipa_ut_ctx->lock);
+ if (ipa_ut_ctx->enabled)
+ ipa_ut_framework_disable();
+ if (ipa_ut_ctx->inited)
+ debugfs_remove_recursive(ipa_ut_ctx->test_dbgfs_root);
+ mutex_unlock(&ipa_ut_ctx->lock);
+}
+
+/**
+ * ipa_ut_ipa_ready_cb() - IPA ready CB
+ *
+ * Once IPA is ready starting initializing the unit-test framework
+ */
+static void ipa_ut_ipa_ready_cb(void *user_data)
+{
+ IPA_UT_DBG("Entry\n");
+ (void)ipa_ut_framework_init();
+}
+
+/**
+ * ipa_ut_module_init() - Module init
+ *
+ * Create the framework context, wait for IPA driver readiness
+ * and Initialize it.
+ * If IPA driver already ready, continue initialization immediately.
+ * if not, wait for IPA ready notification by IPA driver context
+ */
+static int __init ipa_ut_module_init(void)
+{
+ int ret;
+
+ IPA_UT_INFO("Loading IPA test module...\n");
+
+ ipa_ut_ctx = kzalloc(sizeof(struct ipa_ut_context), GFP_KERNEL);
+ if (!ipa_ut_ctx) {
+ IPA_UT_ERR("Failed to allocate ctx\n");
+ return -ENOMEM;
+ }
+ mutex_init(&ipa_ut_ctx->lock);
+
+ if (!ipa_is_ready()) {
+ IPA_UT_DBG("IPA driver not ready, registering callback\n");
+ ret = ipa_register_ipa_ready_cb(ipa_ut_ipa_ready_cb, NULL);
+
+ /*
+ * If we received -EEXIST, IPA has initialized. So we need
+ * to continue the initing process.
+ */
+ if (ret != -EEXIST) {
+ if (ret) {
+ IPA_UT_ERR("IPA CB reg failed - %d\n", ret);
+ kfree(ipa_ut_ctx);
+ ipa_ut_ctx = NULL;
+ }
+ return ret;
+ }
+ }
+
+ ret = ipa_ut_framework_init();
+ if (ret) {
+ IPA_UT_ERR("framework init failed\n");
+ kfree(ipa_ut_ctx);
+ ipa_ut_ctx = NULL;
+ }
+ return ret;
+}
+
+/**
+ * ipa_ut_module_exit() - Module exit function
+ *
+ * Destroys the Framework and removes its context
+ */
+static void ipa_ut_module_exit(void)
+{
+ IPA_UT_DBG("Entry\n");
+
+ if (!ipa_ut_ctx)
+ return;
+
+ ipa_ut_framework_destroy();
+ kfree(ipa_ut_ctx);
+ ipa_ut_ctx = NULL;
+}
+
+module_init(ipa_ut_module_init);
+module_exit(ipa_ut_module_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("IPA Unit Test module");
diff --git a/drivers/platform/msm/ipa/test/ipa_ut_framework.h b/drivers/platform/msm/ipa/test/ipa_ut_framework.h
new file mode 100644
index 000000000000..177be51bfe7d
--- /dev/null
+++ b/drivers/platform/msm/ipa/test/ipa_ut_framework.h
@@ -0,0 +1,222 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _IPA_UT_FRAMEWORK_H_
+#define _IPA_UT_FRAMEWORK_H_
+
+#include <linux/kernel.h>
+#include "../ipa_common_i.h"
+#include "ipa_ut_i.h"
+
+#define IPA_UT_DRV_NAME "ipa_ut"
+
+#define IPA_UT_DBG(fmt, args...) \
+ do { \
+ pr_debug(IPA_UT_DRV_NAME " %s:%d " fmt, \
+ __func__, __LINE__, ## args); \
+ IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
+ IPA_UT_DRV_NAME " %s:%d " fmt, ## args); \
+ IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+ IPA_UT_DRV_NAME " %s:%d " fmt, ## args); \
+ } while (0)
+
+#define IPA_UT_DBG_LOW(fmt, args...) \
+ do { \
+ pr_debug(IPA_UT_DRV_NAME " %s:%d " fmt, \
+ __func__, __LINE__, ## args); \
+ IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+ IPA_UT_DRV_NAME " %s:%d " fmt, ## args); \
+ } while (0)
+
+#define IPA_UT_ERR(fmt, args...) \
+ do { \
+ pr_err(IPA_UT_DRV_NAME " %s:%d " fmt, \
+ __func__, __LINE__, ## args); \
+ IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
+ IPA_UT_DRV_NAME " %s:%d " fmt, ## args); \
+ IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+ IPA_UT_DRV_NAME " %s:%d " fmt, ## args); \
+ } while (0)
+
+#define IPA_UT_INFO(fmt, args...) \
+ do { \
+ pr_info(IPA_UT_DRV_NAME " %s:%d " fmt, \
+ __func__, __LINE__, ## args); \
+ IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
+ IPA_UT_DRV_NAME " %s:%d " fmt, ## args); \
+ IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+ IPA_UT_DRV_NAME " %s:%d " fmt, ## args); \
+ } while (0)
+
+/**
+ * struct ipa_ut_tst_fail_report - Information on test failure
+ * @valid: When a test posts a report, valid will be marked true
+ * @file: File name containing the failed test.
+ * @line: Number of line in the file where the test failed.
+ * @func: Function where the test failed in.
+ * @info: Information about the failure.
+ */
+struct ipa_ut_tst_fail_report {
+ bool valid;
+ const char *file;
+ int line;
+ const char *func;
+ const char *info;
+};
+
+/**
+ * Report on test failure
+ * To be used by tests.
+ */
+#define IPA_UT_TEST_FAIL_REPORT(__info) \
+ do { \
+ extern struct ipa_ut_tst_fail_report \
+ _IPA_UT_TEST_FAIL_REPORT_DATA; \
+ _IPA_UT_TEST_FAIL_REPORT_DATA.valid = true; \
+ _IPA_UT_TEST_FAIL_REPORT_DATA.file = __FILENAME__; \
+ _IPA_UT_TEST_FAIL_REPORT_DATA.line = __LINE__; \
+ _IPA_UT_TEST_FAIL_REPORT_DATA.func = __func__; \
+ if (__info) \
+ _IPA_UT_TEST_FAIL_REPORT_DATA.info = __info; \
+ else \
+ _IPA_UT_TEST_FAIL_REPORT_DATA.info = ""; \
+ } while (0)
+
+/**
+ * To be used by tests to log progress and ongoing information
+ * Logs are not printed to user, but saved to a buffer.
+ * I/S shall print the buffer at different occasions - e.g. in test failure
+ */
+#define IPA_UT_LOG(fmt, args...) \
+ do { \
+ extern char *_IPA_UT_TEST_LOG_BUF_NAME; \
+ char __buf[512]; \
+ IPA_UT_DBG(fmt, args); \
+ scnprintf(__buf, sizeof(__buf), \
+ fmt, args); \
+ strlcat(_IPA_UT_TEST_LOG_BUF_NAME, __buf, sizeof(__buf)); \
+ } while (0)
+
+/**
+ * struct ipa_ut_suite_meta - Suite meta-data
+ * @name: Suite unique name
+ * @desc: Suite description
+ * @setup: Setup Call-back of the suite
+ * @teardown: Teardown Call-back of the suite
+ * @priv: Private pointer of the suite
+ *
+ * Setup/Teardown will be called once for the suite when running a tests of it.
+ * priv field is shared between the Setup/Teardown and the tests
+ */
+struct ipa_ut_suite_meta {
+ char *name;
+ char *desc;
+ int (*setup)(void **ppriv);
+ int (*teardown)(void *priv);
+ void *priv;
+};
+
+/* Test suite data structure declaration */
+struct ipa_ut_suite;
+
+/**
+ * struct ipa_ut_test - Test information
+ * @name: Test name
+ * @desc: Test description
+ * @run: Test execution call-back
+ * @run_in_regression: To run this test as part of regression?
+ * @min_ipa_hw_ver: Minimum IPA H/W version where the test is supported?
+ * @max_ipa_hw_ver: Maximum IPA H/W version where the test is supported?
+ * @suite: Pointer to suite containing this test
+ * @res: Test execution result. Will be updated after running a test as part
+ * of suite tests run
+ */
+struct ipa_ut_test {
+ char *name;
+ char *desc;
+ int (*run)(void *priv);
+ bool run_in_regression;
+ int min_ipa_hw_ver;
+ int max_ipa_hw_ver;
+ struct ipa_ut_suite *suite;
+ enum ipa_ut_test_result res;
+};
+
+/**
+ * struct ipa_ut_suite - Suite information
+ * @meta_data: Pointer to meta-data structure of the suite
+ * @tests: Pointer to array of tests belongs to the suite
+ * @tests_cnt: Number of tests
+ */
+struct ipa_ut_suite {
+ struct ipa_ut_suite_meta *meta_data;
+ struct ipa_ut_test *tests;
+ size_t tests_cnt;
+};
+
+
+/**
+ * Add a test to a suite.
+ * Will add entry to tests array and update its info with
+ * the given info, thus adding new test.
+ */
+#define IPA_UT_ADD_TEST(__name, __desc, __run, __run_in_regression, \
+ __min_ipa_hw_ver, __max_ipa__hw_ver) \
+ { \
+ .name = #__name, \
+ .desc = __desc, \
+ .run = __run, \
+ .run_in_regression = __run_in_regression, \
+ .min_ipa_hw_ver = __min_ipa_hw_ver, \
+ .max_ipa_hw_ver = __max_ipa__hw_ver, \
+ .suite = NULL, \
+ }
+
+/**
+ * Declare a suite
+ * Every suite need to be declared before it is registered.
+ */
+#define IPA_UT_DECLARE_SUITE(__name) \
+ extern struct ipa_ut_suite _IPA_UT_SUITE_DATA(__name)
+
+/**
+ * Register a suite
+ * Registering a suite is mandatory so it will be considered.
+ */
+#define IPA_UT_REGISTER_SUITE(__name) \
+ (&_IPA_UT_SUITE_DATA(__name))
+
+/**
+ * Start/End suite definition
+ * Will create the suite global structures and adds adding tests to it.
+ * Use IPA_UT_ADD_TEST() with these macros to add tests when defining
+ * a suite
+ */
+#define IPA_UT_DEFINE_SUITE_START(__name, __desc, __setup, __teardown) \
+ static struct ipa_ut_suite_meta _IPA_UT_SUITE_META_DATA(__name) = \
+ { \
+ .name = #__name, \
+ .desc = __desc, \
+ .setup = __setup, \
+ .teardown = __teardown, \
+ }; \
+ static struct ipa_ut_test _IPA_UT_SUITE_TESTS(__name)[] =
+#define IPA_UT_DEFINE_SUITE_END(__name) \
+ ; \
+ struct ipa_ut_suite _IPA_UT_SUITE_DATA(__name) = \
+ { \
+ .meta_data = &_IPA_UT_SUITE_META_DATA(__name), \
+ .tests = _IPA_UT_SUITE_TESTS(__name), \
+ .tests_cnt = ARRAY_SIZE(_IPA_UT_SUITE_TESTS(__name)), \
+ }
+
+#endif /* _IPA_UT_FRAMEWORK_H_ */
diff --git a/drivers/platform/msm/ipa/test/ipa_ut_i.h b/drivers/platform/msm/ipa/test/ipa_ut_i.h
new file mode 100644
index 000000000000..7cf5e53d0af1
--- /dev/null
+++ b/drivers/platform/msm/ipa/test/ipa_ut_i.h
@@ -0,0 +1,86 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _IPA_UT_I_H_
+#define _IPA_UT_I_H_
+
+/* Suite data global structure name */
+#define _IPA_UT_SUITE_DATA(__name) ipa_ut_ ##__name ##_data
+
+/* Suite meta-data global structure name */
+#define _IPA_UT_SUITE_META_DATA(__name) ipa_ut_ ##__name ##_meta_data
+
+/* Suite global array of tests */
+#define _IPA_UT_SUITE_TESTS(__name) ipa_ut_ ##__name ##_tests
+
+/* Global array of all suites */
+#define _IPA_UT_ALL_SUITES ipa_ut_all_suites_data
+
+/* Meta-test "all" name - test to run all tests in given suite */
+#define _IPA_UT_RUN_ALL_TEST_NAME "all"
+
+/**
+ * Meta-test "regression" name -
+ * test to run all regression tests in given suite
+ */
+#define _IPA_UT_RUN_REGRESSION_TEST_NAME "regression"
+
+
+/* Test Log buffer name and size */
+#define _IPA_UT_TEST_LOG_BUF_NAME ipa_ut_tst_log_buf
+#define _IPA_UT_TEST_LOG_BUF_SIZE 2048
+
+/* Global structure for test fail execution result information */
+#define _IPA_UT_TEST_FAIL_REPORT_DATA ipa_ut_tst_fail_report_data
+
+/* Start/End definitions of the array of suites */
+#define IPA_UT_DEFINE_ALL_SUITES_START \
+ static struct ipa_ut_suite *_IPA_UT_ALL_SUITES[] =
+#define IPA_UT_DEFINE_ALL_SUITES_END
+
+/**
+ * Suites iterator - Array-like container
+ * First index, number of elements and element fetcher
+ */
+#define IPA_UT_SUITE_FIRST_INDEX 0
+#define IPA_UT_SUITES_COUNT \
+ ARRAY_SIZE(_IPA_UT_ALL_SUITES)
+#define IPA_UT_GET_SUITE(__index) \
+ _IPA_UT_ALL_SUITES[__index]
+
+/**
+ * enum ipa_ut_test_result - Test execution result
+ * @IPA_UT_TEST_RES_FAIL: Test executed and failed
+ * @IPA_UT_TEST_RES_SUCCESS: Test executed and succeeded
+ * @IPA_UT_TEST_RES_SKIP: Test was not executed.
+ *
+ * When running all tests in a suite, a specific test could
+ * be skipped and not executed. For example due to mismatch of
+ * IPA H/W version.
+ */
+enum ipa_ut_test_result {
+ IPA_UT_TEST_RES_FAIL,
+ IPA_UT_TEST_RES_SUCCESS,
+ IPA_UT_TEST_RES_SKIP,
+};
+
+/**
+ * enum ipa_ut_meta_test_type - Type of suite meta-test
+ * @IPA_UT_META_TEST_ALL: Represents all tests in suite
+ * @IPA_UT_META_TEST_REGRESSION: Represents all regression tests in suite
+ */
+enum ipa_ut_meta_test_type {
+ IPA_UT_META_TEST_ALL,
+ IPA_UT_META_TEST_REGRESSION,
+};
+
+#endif /* _IPA_UT_I_H_ */
diff --git a/drivers/platform/msm/ipa/test/ipa_ut_suite_list.h b/drivers/platform/msm/ipa/test/ipa_ut_suite_list.h
new file mode 100644
index 000000000000..615ba671ebaa
--- /dev/null
+++ b/drivers/platform/msm/ipa/test/ipa_ut_suite_list.h
@@ -0,0 +1,35 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _IPA_UT_SUITE_LIST_H_
+#define _IPA_UT_SUITE_LIST_H_
+
+#include "ipa_ut_framework.h"
+#include "ipa_ut_i.h"
+
+/**
+ * Declare every suite here so that it will be found later below
+ * No importance for order.
+ */
+IPA_UT_DECLARE_SUITE(example);
+
+
+/**
+ * Register every suite inside the below block.
+ * Unregistered suites will be ignored
+ */
+IPA_UT_DEFINE_ALL_SUITES_START
+{
+ IPA_UT_REGISTER_SUITE(example),
+} IPA_UT_DEFINE_ALL_SUITES_END;
+
+#endif /* _IPA_UT_SUITE_LIST_H_ */