[V3,1/3] soc: qcom: dcc: Add bootconfig support for DCC

Message ID 82087136234e84eb0e10f0f168e1edb171e6d692.1675054375.git.quic_schowdhu@quicinc.com
State New
Headers
Series Add QAD, Cti-trigger and Bootconfig support for Data Capture and Compare(DCC) |

Commit Message

Souradeep Chowdhury Jan. 30, 2023, 5:13 a.m. UTC
  Add bootconfig parser for DCC which is used to configure addresses
in DCC during boot time. This is needed to debug crashes that can happen
during boot-time. The expected format of a bootconfig file is as follows:-

dcc_config {
	link_list_<The list number to configure> {
		id = <The list number to configure>
		items =  <Address as same format as dcc separated by '_'>,
	}
}

Example:-

dcc_config {
	link_list_6 {
		id = 6
		items = R_0x1781005c_1_apb,
			R_0x1782005c_1_apb
	}
}

Signed-off-by: Souradeep Chowdhury <quic_schowdhu@quicinc.com>
---
 drivers/soc/qcom/Kconfig |  3 +-
 drivers/soc/qcom/dcc.c   | 74 ++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 73 insertions(+), 4 deletions(-)
  

Patch

diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 21c4ce2..d11bda2 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -71,8 +71,9 @@  config QCOM_LLCC
 	  Say yes here to enable LLCC slice driver.
 
 config QCOM_DCC
-	tristate "Qualcomm Technologies, Inc. Data Capture and Compare(DCC) engine driver"
+	bool "Qualcomm Technologies, Inc. Data Capture and Compare(DCC) engine driver"
 	depends on ARCH_QCOM || COMPILE_TEST
+	select BOOT_CONFIG
 	help
 	  This option enables driver for Data Capture and Compare engine. DCC
 	  driver provides interface to configure DCC block and read back
diff --git a/drivers/soc/qcom/dcc.c b/drivers/soc/qcom/dcc.c
index 5b50d63..93e8f86 100644
--- a/drivers/soc/qcom/dcc.c
+++ b/drivers/soc/qcom/dcc.c
@@ -6,6 +6,7 @@ 
 
 #include <linux/bitfield.h>
 #include <linux/bitops.h>
+#include <linux/bootconfig.h>
 #include <linux/debugfs.h>
 #include <linux/delay.h>
 #include <linux/fs.h>
@@ -148,6 +149,18 @@  struct dcc_cfg_loop_attr {
 	bool	loop_start;
 };
 
+static char *replace_char(char *str, char find, char replace)
+{
+	char *current_pos = strchr(str, find);
+
+	while (current_pos) {
+		*current_pos = replace;
+		current_pos = strchr(current_pos, find);
+	}
+
+	return str;
+}
+
 static inline u32 dcc_list_offset(int version)
 {
 	return version == 1 ? 0x1c : version == 2 ? 0x2c : 0x34;
@@ -1185,13 +1198,62 @@  static void dcc_sram_dev_exit(struct dcc_drvdata *drvdata)
 	misc_deregister(&drvdata->sram_dev);
 }
 
-static int dcc_probe(struct platform_device *pdev)
+static int __init dcc_bootconfig_parse(struct dcc_drvdata *drvdata, struct  xbc_node  *dcc_node)
+{
+	struct xbc_node *linked_list, *node;
+	int curr_list, ret;
+	const char *p;
+	char *input, *token;
+	char val[30];
+
+	xbc_node_for_each_subkey(dcc_node, linked_list) {
+		p = xbc_node_find_value(linked_list, "id", &node);
+		if (p) {
+			ret = kstrtoint(p, 0, &curr_list);
+			if (ret)
+				return ret;
+		} else {
+			dev_err(drvdata->dev, "List number not specified\n");
+			continue;
+		}
+
+		p = xbc_node_find_value(linked_list, "items", &node);
+		if (!p)
+			continue;
+		xbc_array_for_each_value(node, p) {
+			strcpy(val, p);
+			input = replace_char(val, '_', ' ');
+			token = strsep(&input, " ");
+
+			if (!strcmp("R", token)) {
+				ret = dcc_config_add_read(drvdata, input, curr_list);
+			} else if (!strcmp("W", token)) {
+				ret = dcc_config_add_write(drvdata, input, curr_list);
+			} else if (!strcmp("RW", token)) {
+				ret = dcc_config_add_read_write(drvdata, input, curr_list);
+			} else if (!strcmp("L", token)) {
+				ret = dcc_config_add_loop(drvdata, input, curr_list);
+			} else {
+				dev_err(drvdata->dev, "%s is not a correct input\n", token);
+				return -EINVAL;
+			}
+			if (ret)
+				return ret;
+		}
+		dcc_enable(drvdata, curr_list);
+	}
+
+	return 0;
+}
+
+static int __init dcc_probe(struct platform_device *pdev)
 {
 	u32 val;
 	int ret = 0, i;
 	struct device *dev = &pdev->dev;
 	struct dcc_drvdata *drvdata;
 	struct resource *res;
+	struct xbc_node *dcc_node;
 
 	drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
 	if (!drvdata)
@@ -1263,6 +1325,13 @@  static int dcc_probe(struct platform_device *pdev)
 
 	dcc_create_debug_dir(drvdata);
 
+	dcc_node = xbc_find_node("dcc_config");
+	if (dcc_node) {
+		ret = dcc_bootconfig_parse(drvdata, dcc_node);
+		if (ret)
+			dev_err(drvdata->dev, "DCC: Bootconfig parse error.\n");
+	}
+
 	return 0;
 }
 
@@ -1287,14 +1356,13 @@  static const struct of_device_id dcc_match_table[] = {
 MODULE_DEVICE_TABLE(of, dcc_match_table);
 
 static struct platform_driver dcc_driver = {
-	.probe = dcc_probe,
 	.remove	= dcc_remove,
 	.driver	= {
 		.name = "qcom-dcc",
 		.of_match_table	= dcc_match_table,
 	},
 };
-module_platform_driver(dcc_driver);
+module_platform_driver_probe(dcc_driver, dcc_probe);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Qualcomm Technologies Inc. DCC driver");