arm: msm8974: Driver to add support to disable debug image

On MSM8974, XPUs do not reset for non-watchdog resets when the
watchdog debug feature is enabled. This driver is required to
provide an interface to disable debug image for a PS HOLD reset.

Change-Id: I9e4a210475d0fff686d9d88000e8d71da1b553a1
Signed-off-by: Pushkar Joshi <pushkarj@codeaurora.org>
diff --git a/Documentation/devicetree/bindings/arm/msm/qcom-wdog-debug.txt b/Documentation/devicetree/bindings/arm/msm/qcom-wdog-debug.txt
new file mode 100644
index 0000000..e5fd1b2
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/qcom-wdog-debug.txt
@@ -0,0 +1,16 @@
+* Qualcomm's Watchdog Debug Image Controller
+
+The Qualcomm's Watchdog debug image controller is used for enabling/disabling of
+watchdog debug image feature.
+
+Required properties:
+- compatible : should be "qcom,msm-wdog-debug"
+- reg : base page aligned physical base address of the controller and length of
+	memory mapped region.
+
+Example:
+
+	qcom,msm-wdog-debug@fc401000 {
+		compatible = "qcom,msm-wdogi-debug";
+		reg = <0xfc401000 0x1000>;
+	};
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index e59fb5b..75b73ec 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -2645,4 +2645,12 @@
 	 such as display backlight, vreg pin-ctrl, smps clock over the RPC
 	 interface. This support is required for MSMs on which the APPS
 	 does not have a direct access to the PMIC.
+
+config MSM_ENABLE_WDOG_DEBUG_CONTROL
+	bool "MSM Watchdog driver to disable debug Image"
+	help
+	 This driver supports the configuration of the GCC_WDOG_DEBUG register
+	 used to control debug image.
+	 This support is currently required for MSM8974 to disable debug image
+	 on PS HOLD reset
 endif
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 3c44a06..ffe8b72 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -383,6 +383,7 @@
 obj-$(CONFIG_MSM_RPC_USB) += rpc_hsusb.o rpc_fsusb.o
 obj-$(CONFIG_MSM_RPC_PMAPP) += rpc_pmapp.o
 
+obj-$(CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL) += wdog_debug.o
 
 ifdef CONFIG_MSM_CPR
 obj-$(CONFIG_DEBUG_FS) += msm_cpr-debug.o
diff --git a/arch/arm/mach-msm/wdog_debug.c b/arch/arm/mach-msm/wdog_debug.c
new file mode 100644
index 0000000..82800cf
--- /dev/null
+++ b/arch/arm/mach-msm/wdog_debug.c
@@ -0,0 +1,149 @@
+/* Copyright (c) 2012, 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/module.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <mach/scm.h>
+#include <linux/slab.h>
+
+#define MODULE_NAME "wdog_debug"
+#define WDOG_DEBUG_EN 17
+#define GCC_WDOG_DEBUG_OFFSET 0x780
+
+struct msm_wdog_debug_data {
+	unsigned int __iomem phys_base;
+	size_t size;
+	void __iomem *base;
+	struct device *dev;
+};
+
+static struct msm_wdog_debug_data *wdog_data;
+
+void msm_disable_wdog_debug(void)
+{
+	unsigned long int value;
+
+	if (wdog_data == NULL)
+		return;
+	value = readl_relaxed(wdog_data->base + GCC_WDOG_DEBUG_OFFSET);
+	value &= ~BIT(WDOG_DEBUG_EN);
+	writel_relaxed(value, wdog_data->base + GCC_WDOG_DEBUG_OFFSET);
+}
+EXPORT_SYMBOL(msm_disable_wdog_debug);
+
+void msm_enable_wdog_debug(void)
+{
+	unsigned long int value;
+
+	if (wdog_data == NULL)
+		return;
+	value = readl_relaxed(wdog_data->base + GCC_WDOG_DEBUG_OFFSET);
+	value |= BIT(WDOG_DEBUG_EN);
+	writel_relaxed(value, wdog_data->base + GCC_WDOG_DEBUG_OFFSET);
+}
+EXPORT_SYMBOL(msm_enable_wdog_debug);
+
+static int __devexit msm_wdog_debug_remove(struct platform_device *pdev)
+{
+	kfree(wdog_data);
+	wdog_data = NULL;
+	pr_info("MSM wdog_debug Exit - Deactivated\n");
+	return 0;
+}
+
+static int __devinit msm_wdog_debug_dt_to_pdata(struct platform_device *pdev,
+					struct msm_wdog_debug_data *pdata)
+{
+	struct resource *wdog_resource;
+
+	wdog_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!wdog_resource) {
+		dev_err(&pdev->dev, \
+		"%s cannot allocate resource for wdog_debug\n", \
+		 __func__);
+		return -ENXIO;
+	}
+	pdata->size = resource_size(wdog_resource);
+	pdata->phys_base = wdog_resource->start;
+	if (unlikely(!(devm_request_region(&pdev->dev, pdata->phys_base,
+					pdata->size, "msm-wdog-debug")))) {
+		dev_err(&pdev->dev, "%s cannot reserve wdog_debug region\n",
+								__func__);
+		return -ENXIO;
+	}
+	pdata->base  = devm_ioremap(&pdev->dev, pdata->phys_base,
+							pdata->size);
+	if (!pdata->base) {
+		dev_err(&pdev->dev, "%s cannot map wdog register space\n",
+				__func__);
+		return -ENXIO;
+	}
+
+	return 0;
+}
+
+static int __devinit msm_wdog_debug_probe(struct platform_device *pdev)
+{
+	int ret;
+	if (!pdev->dev.of_node)
+		return -ENODEV;
+	wdog_data = kzalloc(sizeof(struct msm_wdog_debug_data), GFP_KERNEL);
+	if (!wdog_data)
+		return -ENOMEM;
+	ret = msm_wdog_debug_dt_to_pdata(pdev, wdog_data);
+	if (ret)
+		goto err;
+	wdog_data->dev = &pdev->dev;
+	platform_set_drvdata(pdev, wdog_data);
+	msm_enable_wdog_debug();
+	return 0;
+err:
+	kzfree(wdog_data);
+	wdog_data = NULL;
+	return ret;
+}
+
+static struct of_device_id msm_wdog_debug_match_table[] = {
+	{ .compatible = "qcom,msm-wdog-debug" },
+	{}
+};
+
+static struct platform_driver msm_wdog_debug_driver = {
+	.probe = msm_wdog_debug_probe,
+	.remove = msm_wdog_debug_remove,
+	.driver = {
+		.name = MODULE_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = msm_wdog_debug_match_table,
+	},
+};
+
+static int __devinit wdog_debug_init(void)
+{
+	return platform_driver_register(&msm_wdog_debug_driver);
+}
+module_init(wdog_debug_init);
+
+static void __exit wdog_debug_exit(void)
+{
+	platform_driver_unregister(&msm_wdog_debug_driver);
+}
+module_exit(wdog_debug_exit);
+
+MODULE_DESCRIPTION("MSM Driver to disable debug Image");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/wdog_debug.h b/arch/arm/mach-msm/wdog_debug.h
new file mode 100644
index 0000000..920aa89
--- /dev/null
+++ b/arch/arm/mach-msm/wdog_debug.h
@@ -0,0 +1,24 @@
+/* Copyright (c) 2012, 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 __WDOG_DEBUG_H
+#define __WDOG_DEBUG_H
+
+#ifdef CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL
+void msm_enable_wdog_debug(void);
+void msm_disable_wdog_debug(void);
+#else
+void msm_enable_wdog_debug(void) { }
+void msm_disable_wdog_debug(void) { }
+#endif
+
+#endif