msm: qdss: save and restore debug registers across PC
Debug OS lock register is on the core (Krait) power domain and
so looses its contents on a power collapse. It's power on reset
state is locked and external debugger accesses are not allowed
while the debug OS lock is set.
This prevents JTag users from setting breakpoints after the first
Krait power collapse.
Saving and restoring debug registers across power collapse helps
preserve the breakpoints as well as clear the debug OS lock as
part of this procedure while coming out of power collapse.
CRs-Fixed: 310982
Change-Id: Icfccea17e556474241bf495885d9bb575d7571bf
Signed-off-by: Pratik Patel <pratikp@codeaurora.org>
diff --git a/arch/arm/mach-msm/qdss-debug.c b/arch/arm/mach-msm/qdss-debug.c
new file mode 100644
index 0000000..b2d38b1
--- /dev/null
+++ b/arch/arm/mach-msm/qdss-debug.c
@@ -0,0 +1,285 @@
+/* Copyright (c) 2011, Code Aurora Forum. 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/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+
+#include "qdss.h"
+
+#define debug_writel(debug, cpu, val, off) \
+ __raw_writel((val), debug.base[cpu] + off)
+#define debug_readl(debug, cpu, off) \
+ __raw_readl(debug.base[cpu] + off)
+
+#define DBGDIDR (0x000)
+#define DBGWFAR (0x018)
+#define DBGVCR (0x01C)
+#define DBGECR (0x024)
+#define DBGDTRRX (0x080)
+#define DBGITR (0x084)
+#define DBGDSCR (0x088)
+#define DBGDTRTX (0x08C)
+#define DBGDRCR (0x090)
+#define DBGEACR (0x094)
+#define DBGPCSR (0x0A0)
+#define DBGCIDSR (0x0A4)
+#define DBGVIDSR (0x0A8)
+#define DBGBVRm(n) (0x100 + (n * 4))
+#define DBGBCRm(n) (0x140 + (n * 4))
+#define DBGWVRm(n) (0x180 + (n * 4))
+#define DBGWCRm(n) (0x1C0 + (n * 4))
+#define DBGBXVRm(n) (0x240 + (n * 4))
+#define DBGOSLAR (0x300)
+#define DBGOSLSR (0x304)
+#define DBGPRCR (0x310)
+#define DBGPRSR (0x314)
+
+
+#define DEBUG_LOCK(cpu) \
+do { \
+ mb(); \
+ debug_writel(debug, cpu, MAGIC2, CS_LAR); \
+} while (0)
+#define DEBUG_UNLOCK(cpu) \
+do { \
+ debug_writel(debug, cpu, MAGIC1, CS_LAR); \
+ mb(); \
+} while (0)
+
+#define DEBUG_OS_LOCK(cpu) \
+do { \
+ debug_writel(debug, cpu, MAGIC1, DBGOSLAR); \
+ mb(); \
+} while (0)
+#define DEBUG_OS_UNLOCK(cpu) \
+do { \
+ mb(); \
+ debug_writel(debug, cpu, MAGIC2, DBGOSLAR); \
+ mb(); \
+} while (0)
+
+#define MAX_DEBUG_REGS (90)
+#define MAX_STATE_SIZE (MAX_DEBUG_REGS * num_possible_cpus())
+#define DBGDSCR_MASK (0x6C30FC3C)
+
+struct debug_config {
+ /* read only config register */
+ uint32_t dbg_id;
+ /* derived values */
+ uint8_t nr_watch_pts;
+ uint8_t nr_brk_pts;
+ uint8_t nr_ctx_comp;
+};
+
+struct debug_ctx {
+ struct debug_config cfg;
+ void __iomem **base;
+ uint32_t *state;
+ struct device *dev;
+};
+
+static struct debug_ctx debug;
+
+static void debug_save_reg(int cpu)
+{
+ uint32_t i;
+ int j;
+
+ DEBUG_UNLOCK(cpu);
+ DEBUG_OS_LOCK(cpu);
+
+ i = cpu * MAX_DEBUG_REGS;
+
+ debug.state[i++] = debug_readl(debug, cpu, DBGWFAR);
+ for (j = 0; j < debug.cfg.nr_brk_pts; j++) {
+ debug.state[i++] = debug_readl(debug, cpu, DBGBCRm(j));
+ debug.state[i++] = debug_readl(debug, cpu, DBGBVRm(j));
+ }
+ for (j = 0; j < debug.cfg.nr_ctx_comp; j++)
+ debug.state[i++] = debug_readl(debug, cpu, DBGBXVRm(j));
+ for (j = 0; j < debug.cfg.nr_watch_pts; j++) {
+ debug.state[i++] = debug_readl(debug, cpu, DBGWVRm(j));
+ debug.state[i++] = debug_readl(debug, cpu, DBGWCRm(j));
+ }
+ debug.state[i++] = debug_readl(debug, cpu, DBGVCR);
+ debug.state[i++] = debug_readl(debug, cpu, CS_CLAIMSET);
+ debug.state[i++] = debug_readl(debug, cpu, CS_CLAIMCLR);
+ debug.state[i++] = debug_readl(debug, cpu, DBGDTRTX);
+ debug.state[i++] = debug_readl(debug, cpu, DBGDTRRX);
+ debug.state[i++] = debug_readl(debug, cpu, DBGDSCR);
+
+ DEBUG_LOCK(cpu);
+}
+
+static void debug_restore_reg(int cpu)
+{
+ uint32_t i;
+ int j;
+
+ DEBUG_UNLOCK(cpu);
+ DEBUG_OS_LOCK(cpu);
+
+ i = cpu * MAX_DEBUG_REGS;
+
+ debug_writel(debug, cpu, debug.state[i++], DBGWFAR);
+ for (j = 0; j < debug.cfg.nr_brk_pts; j++) {
+ debug_writel(debug, cpu, debug.state[i++], DBGBCRm(j));
+ debug_writel(debug, cpu, debug.state[i++], DBGBVRm(j));
+ }
+ for (j = 0; j < debug.cfg.nr_ctx_comp; j++)
+ debug_writel(debug, cpu, debug.state[i++], DBGBXVRm(j));
+ for (j = 0; j < debug.cfg.nr_watch_pts; j++) {
+ debug_writel(debug, cpu, debug.state[i++], DBGWVRm(j));
+ debug_writel(debug, cpu, debug.state[i++], DBGWCRm(j));
+ }
+ debug_writel(debug, cpu, debug.state[i++], DBGVCR);
+ debug_writel(debug, cpu, debug.state[i++], CS_CLAIMSET);
+ debug_writel(debug, cpu, debug.state[i++], CS_CLAIMCLR);
+ debug_writel(debug, cpu, debug.state[i++], DBGDTRTX);
+ debug_writel(debug, cpu, debug.state[i++], DBGDTRRX);
+ debug_writel(debug, cpu, debug.state[i++] & DBGDSCR_MASK, DBGDSCR);
+
+ DEBUG_OS_UNLOCK(cpu);
+ DEBUG_LOCK(cpu);
+}
+
+/* msm_save_jtag_debug and msm_restore_jtag_debug should be fast
+ *
+ * These functions will be called either from:
+ * 1. per_cpu idle thread context for idle power collapses.
+ * 2. per_cpu idle thread context for hotplug/suspend power collapse for
+ * nonboot cpus.
+ * 3. suspend thread context for core0.
+ *
+ * In all cases we are guaranteed to be running on the same cpu for the
+ * entire duration.
+ */
+void msm_save_jtag_debug(void)
+{
+ int cpu = smp_processor_id();
+ debug_save_reg(cpu);
+}
+
+void msm_restore_jtag_debug(void)
+{
+ int cpu = smp_processor_id();
+ debug_restore_reg(cpu);
+}
+
+static void debug_cfg_ro_init(void)
+{
+ /* use cpu 0 for setup */
+ int cpu = 0;
+
+ DEBUG_UNLOCK(cpu);
+
+ debug.cfg.dbg_id = debug_readl(debug, cpu, DBGDIDR);
+ debug.cfg.nr_ctx_comp = BMVAL(debug.cfg.dbg_id, 20, 23) + 1;
+ debug.cfg.nr_brk_pts = BMVAL(debug.cfg.dbg_id, 24, 27) + 1;
+ debug.cfg.nr_watch_pts = BMVAL(debug.cfg.dbg_id, 28, 31) + 1;
+
+ DEBUG_LOCK(cpu);
+}
+
+static int __devinit debug_probe(struct platform_device *pdev)
+{
+ int i, ret;
+ struct resource *res;
+
+ debug.base = kzalloc(pdev->num_resources * sizeof(void *), GFP_KERNEL);
+ if (!debug.base) {
+ ret = -ENOMEM;
+ goto err_base_kzalloc;
+ }
+
+ for (i = 0; i < pdev->num_resources; i++) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+ if (!res) {
+ ret = -EINVAL;
+ goto err_res;
+ }
+
+ debug.base[i] = ioremap_nocache(res->start, resource_size(res));
+ if (!debug.base[i]) {
+ ret = -EINVAL;
+ goto err_ioremap;
+ }
+ }
+
+ debug.dev = &pdev->dev;
+
+ debug.state = kzalloc(MAX_STATE_SIZE * sizeof(uint32_t), GFP_KERNEL);
+ if (!debug.state) {
+ ret = -ENOMEM;
+ goto err_state_kzalloc;
+ }
+
+ debug_cfg_ro_init();
+
+ dev_info(debug.dev, "Debug intialized.\n");
+
+ return 0;
+
+err_state_kzalloc:
+err_ioremap:
+err_res:
+ while (i) {
+ iounmap(debug.base[i-1]);
+ i--;
+ }
+ kfree(debug.base);
+err_base_kzalloc:
+ return ret;
+}
+
+static int __devexit debug_remove(struct platform_device *pdev)
+{
+ int i;
+
+ kfree(debug.state);
+ for (i = pdev->num_resources; i > 0; i--)
+ iounmap(debug.base[i-1]);
+ kfree(debug.base);
+
+ return 0;
+}
+
+static struct platform_driver debug_driver = {
+ .probe = debug_probe,
+ .remove = __devexit_p(debug_remove),
+ .driver = {
+ .name = "msm_debug",
+ },
+};
+
+static int __init debug_init(void)
+{
+ return platform_driver_register(&debug_driver);
+}
+module_init(debug_init);
+
+static void __exit debug_exit(void)
+{
+ platform_driver_unregister(&debug_driver);
+}
+module_exit(debug_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Coresight Debug driver");