summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJim Wylder <jwylder@motorola.com>2014-04-15 12:08:38 -0500
committerDoug Zobel <dzobel1@motorola.com>2014-04-29 13:26:31 -0500
commit302f70450dd87df2d7daef1a37f38ff413c6593e (patch)
tree7f7ca9ddb60a1ab5457a1309ead8c26387da8cd9
parent45f47718c757cab219371cd24610a0a0cbc1797a (diff)
downloadolio-linux-3.10-302f70450dd87df2d7daef1a37f38ff413c6593e.tar.xz
olio-linux-3.10-302f70450dd87df2d7daef1a37f38ff413c6593e.zip
IKXCLOCK-938 arm: omap2: pm debug register dump
Reworking of earlier data reporting module. Register values can be read from <debugfs>/pm_debug/registers/current,1,2,3,4 Currently 1 is recorded before the last suspend and 2 is recorded after the last suspend. Change-Id: I061ec369a59c647c662879f30f4830f8d47e8996 Signed-off-by: Jim Wylder <jwylder@motorola.com>
-rw-r--r--arch/arm/mach-omap2/Makefile2
-rw-r--r--arch/arm/mach-omap2/pm-debug-regs.c478
-rw-r--r--arch/arm/mach-omap2/pm-debug-regs.h31
-rw-r--r--arch/arm/mach-omap2/pm-debug.c4
-rw-r--r--arch/arm/mach-omap2/pm34xx.c19
-rw-r--r--arch/arm/mach-omap2/prm2xxx.h3
6 files changed, 533 insertions, 4 deletions
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index 50daf4181a7..731e039bef0 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -85,7 +85,7 @@ obj-$(CONFIG_ARCH_OMAP2) += sleep24xx.o
obj-$(CONFIG_ARCH_OMAP3) += pm34xx.o sleep34xx.o
obj-$(CONFIG_ARCH_OMAP4) += pm44xx.o omap-mpuss-lowpower.o
obj-$(CONFIG_SOC_OMAP5) += omap-mpuss-lowpower.o
-obj-$(CONFIG_PM_DEBUG) += pm-debug.o
+obj-$(CONFIG_PM_DEBUG) += pm-debug.o pm-debug-regs.o
obj-$(CONFIG_POWER_AVS_OMAP) += sr_device.o
obj-$(CONFIG_POWER_AVS_OMAP_CLASS3) += smartreflex-class3.o
diff --git a/arch/arm/mach-omap2/pm-debug-regs.c b/arch/arm/mach-omap2/pm-debug-regs.c
new file mode 100644
index 00000000000..0997fe8138d
--- /dev/null
+++ b/arch/arm/mach-omap2/pm-debug-regs.c
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) 2014 Motorola Mobility LLC
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ * Work based on OMAP Power Management debug routines
+ * Copyright (C) 2005 Texas Instruments, Inc.
+ * Copyright (C) 2006-2008 Nokia Corporation
+ *
+ * Written by:
+ * Richard Woodruff <r-woodruff2@ti.com>
+ * Tony Lindgren
+ * Juha Yrjola
+ * Amit Kucheria <amit.kucheria@nokia.com>
+ * Igor Stoppa <igor.stoppa@nokia.com>
+ * Jouni Hogander
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "iomap.h"
+#include "clock.h"
+#include "powerdomain.h"
+#include "clockdomain.h"
+#include "omap-pm.h"
+
+#include "soc.h"
+#include "cm2xxx_3xxx.h"
+#include "prm3xxx.h"
+#include "prm2xxx_3xxx.h"
+#include "pm.h"
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+static struct dentry *pm_dbg_dir;
+#endif
+
+struct pm_module_def {
+ char name[8]; /* Name of the module */
+ short type; /* CM or PRM */
+ unsigned short offset;
+ int low; /* First register address on this module */
+ int high; /* Last register address on this module */
+};
+
+#define MOD_CM 0
+#define MOD_PRM 1
+
+
+static const struct pm_module_def pm_dbg_reg_modules[] = {
+ { "IVA2", MOD_CM, OMAP3430_IVA2_MOD, 0, 0x4c },
+ { "OCP", MOD_CM, OCP_MOD, 0, 0x10 },
+ { "MPU", MOD_CM, MPU_MOD, 4, 0x4c },
+ { "CORE", MOD_CM, CORE_MOD, 0, 0x4c },
+ { "SGX", MOD_CM, OMAP3430ES2_SGX_MOD, 0, 0x4c },
+ { "WKUP", MOD_CM, WKUP_MOD, 0, 0x40 },
+ { "CCR", MOD_CM, PLL_MOD, 0, 0x70 },
+ { "DSS", MOD_CM, OMAP3430_DSS_MOD, 0, 0x4c },
+ { "CAM", MOD_CM, OMAP3430_CAM_MOD, 0, 0x4c },
+ { "PER", MOD_CM, OMAP3430_PER_MOD, 0, 0x4c },
+ { "EMU", MOD_CM, OMAP3430_EMU_MOD, 0x40, 0x54 },
+ { "NEON", MOD_CM, OMAP3430_NEON_MOD, 0x20, 0x48 },
+ { "USB", MOD_CM, OMAP3430ES2_USBHOST_MOD, 0, 0x4c },
+
+ { "IVA2", MOD_PRM, OMAP3430_IVA2_MOD, 0x50, 0xfc },
+ { "OCP", MOD_PRM, OCP_MOD, 4, 0x1c },
+ { "MPU", MOD_PRM, MPU_MOD, 0x58, 0xe8 },
+ { "CORE", MOD_PRM, CORE_MOD, 0x58, 0xf8 },
+ { "SGX", MOD_PRM, OMAP3430ES2_SGX_MOD, 0x58, 0xe8 },
+ { "WKUP", MOD_PRM, WKUP_MOD, 0xa0, 0xb0 },
+ { "CCR", MOD_PRM, PLL_MOD, 0x40, 0x70 },
+ { "DSS", MOD_PRM, OMAP3430_DSS_MOD, 0x58, 0xe8 },
+ { "CAM", MOD_PRM, OMAP3430_CAM_MOD, 0x58, 0xe8 },
+ { "PER", MOD_PRM, OMAP3430_PER_MOD, 0x58, 0xe8 },
+ { "EMU", MOD_PRM, OMAP3430_EMU_MOD, 0x58, 0xe4 },
+ { "GLBL", MOD_PRM, OMAP3430_GR_MOD, 0x20, 0xe4 },
+ { "NEON", MOD_PRM, OMAP3430_NEON_MOD, 0x58, 0xe8 },
+ { "USB", MOD_PRM, OMAP3430ES2_USBHOST_MOD, 0x58, 0xe8 },
+ { "", 0, 0, 0, 0 },
+};
+
+#define PM_DBG_MAX_REG_SETS 4
+
+static void *pm_dbg_reg_set[PM_DBG_MAX_REG_SETS] = {0};
+
+static int pm_dbg_get_regset_size(void)
+{
+ static int regset_size = 0;
+
+ if (regset_size == 0) {
+ int i = 0;
+ while (pm_dbg_reg_modules[i].name[0] != 0) {
+ regset_size += pm_dbg_reg_modules[i].high +
+ 4 - pm_dbg_reg_modules[i].low;
+ i++;
+ }
+ }
+ return regset_size;
+}
+
+static void pm_dbg_regset_store(u32 *ptr)
+{
+ int i = 0;
+ int j;
+ u32 val;
+
+ while (pm_dbg_reg_modules[i].name[0] != 0) {
+ for (j = pm_dbg_reg_modules[i].low;
+ j <= pm_dbg_reg_modules[i].high; j += 4) {
+ if (pm_dbg_reg_modules[i].type == MOD_CM)
+ val = omap2_cm_read_mod_reg(
+ pm_dbg_reg_modules[i].offset, j);
+ else
+ val = omap2_prm_read_mod_reg(
+ pm_dbg_reg_modules[i].offset, j);
+ *(ptr++) = val;
+ }
+ i++;
+ }
+}
+
+void pm_dbg_regs_save(int reg_set)
+{
+ if (pm_dbg_reg_set[reg_set - 1] == NULL)
+ return;
+
+ pm_dbg_regset_store(pm_dbg_reg_set[reg_set - 1]);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int pm_dbg_show_regs(struct seq_file *s, void *unused)
+{
+ int i, j;
+ unsigned long val;
+ int reg_set = (int)s->private;
+ u32 *ptr;
+ void *store = NULL;
+ int regs;
+ int linefeed;
+
+ if (!cpu_is_omap34xx())
+ return -EINVAL;
+
+ if (reg_set == 0) {
+ store = kmalloc(pm_dbg_get_regset_size(), GFP_KERNEL);
+ if (store == NULL)
+ return -ENOMEM;
+ ptr = store;
+ pm_dbg_regset_store(ptr);
+ } else {
+ ptr = pm_dbg_reg_set[reg_set - 1];
+ }
+
+ i = 0;
+
+ while (pm_dbg_reg_modules[i].name[0] != 0) {
+ regs = 0;
+ linefeed = 0;
+ if (pm_dbg_reg_modules[i].type == MOD_CM)
+ seq_printf(s, "MOD: CM_%s (%08x)\n",
+ pm_dbg_reg_modules[i].name,
+ (u32)(OMAP3430_CM_BASE +
+ pm_dbg_reg_modules[i].offset));
+ else
+ seq_printf(s, "MOD: PRM_%s (%08x)\n",
+ pm_dbg_reg_modules[i].name,
+ (u32)(OMAP3430_PRM_BASE +
+ pm_dbg_reg_modules[i].offset));
+
+ for (j = pm_dbg_reg_modules[i].low;
+ j <= pm_dbg_reg_modules[i].high; j += 4) {
+ val = *(ptr++);
+ if (val != 0) {
+ regs++;
+ if (linefeed) {
+ seq_printf(s, "\n");
+ linefeed = 0;
+ }
+ seq_printf(s, " %02x => %08lx", j, val);
+ if (regs % 4 == 0)
+ linefeed = 1;
+ }
+ }
+ seq_printf(s, "\n");
+ i++;
+ }
+
+ if (store != NULL)
+ kfree(store);
+
+ return 0;
+}
+
+#define WAKEUP_SOURCE_LEN 512
+void pm_dbg_show_wakeup_source(void)
+{
+ u32 val = 0;
+ int len = 0;
+ static char buf[WAKEUP_SOURCE_LEN];
+ char *pbuf;
+ u32 gpio_bit = 0;
+
+ /* print the real wkup sources */
+ memset(buf, 0, WAKEUP_SOURCE_LEN);
+ pbuf = buf;
+ len = WAKEUP_SOURCE_LEN - (pbuf - buf);
+ if (len > 16)
+ pbuf += snprintf(pbuf, len, "WAKEDUP BY: ");
+
+ val = omap2_prm_read_mod_reg(WKUP_MOD, PM_WKST);
+ val &= omap2_prm_read_mod_reg(WKUP_MOD, PM_WKEN);
+ len = WAKEUP_SOURCE_LEN - (pbuf - buf);
+ if (val && len > 30)
+ pbuf += snprintf(pbuf, len, "WKUP_MOD(0x%x), ", val);
+
+ val = omap2_prm_read_mod_reg(CORE_MOD, PM_WKST1);
+ val &= omap2_prm_read_mod_reg(CORE_MOD, PM_WKEN1);
+ len = WAKEUP_SOURCE_LEN - (pbuf - buf);
+ if (val && len > 30)
+ pbuf += snprintf(pbuf, len, "CORE_MOD(0x%x), ", val);
+
+ val = omap2_prm_read_mod_reg(CORE_MOD, OMAP3430ES2_PM_WKST3);
+ val &= omap2_prm_read_mod_reg(CORE_MOD, OMAP3430ES2_PM_WKEN3);
+ len = WAKEUP_SOURCE_LEN - (pbuf - buf);
+ if (val && len > 30)
+ pbuf += snprintf(pbuf, len, "CORE3_MOD(0x%x), ", val);
+
+ val = omap2_prm_read_mod_reg(OMAP3430_PER_MOD, PM_WKST);
+ val &= omap2_prm_read_mod_reg(OMAP3430_PER_MOD, PM_WKEN);
+ len = WAKEUP_SOURCE_LEN - (pbuf - buf);
+ if (val && len > 30)
+ pbuf += snprintf(pbuf, len, "PER_MOD(0x%x), ", val);
+
+ val = omap2_prm_read_mod_reg(OMAP3430ES2_USBHOST_MOD, PM_WKST);
+ val &= omap2_prm_read_mod_reg(OMAP3430ES2_USBHOST_MOD, PM_WKEN);
+ len = WAKEUP_SOURCE_LEN - (pbuf - buf);
+ if (val && len > 30)
+ pbuf += snprintf(pbuf, len, "USBHOST(0x%x), ", val);
+
+ val = omap2_prm_read_mod_reg(OMAP3430_DSS_MOD, PM_WKST);
+ val &= omap2_prm_read_mod_reg(OMAP3430_DSS_MOD, PM_WKEN);
+ len = WAKEUP_SOURCE_LEN - (pbuf - buf);
+ if (val && len > 30)
+ pbuf += snprintf(pbuf, len, "DSS(0x%x), ", val);
+
+ val = omap2_prm_read_mod_reg(OCP_MOD, OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
+ val &= omap2_prm_read_mod_reg(OCP_MOD, OMAP3_PRM_IRQENABLE_MPU_OFFSET);
+ len = WAKEUP_SOURCE_LEN - (pbuf - buf);
+ if (val && len > 30)
+ pbuf += snprintf(pbuf, len, "MPU_IRQSTATUS(0x%x), ", val);
+
+ val = omap2_prm_read_mod_reg(OMAP3430_IVA2_MOD, OMAP3430_PRM_IRQSTATUS_IVA2);
+ val &= omap2_prm_read_mod_reg(OMAP3430_IVA2_MOD, OMAP3430_PRM_IRQENABLE_IVA2);
+ len = WAKEUP_SOURCE_LEN - (pbuf - buf);
+ if (val && len > 30)
+ pbuf += snprintf(pbuf, len, "IVA2_IRQSTATUS(0x%x), ", val);
+
+ val = __raw_readl(OMAP2_L4_IO_ADDRESS(OMAP34XX_IC_BASE + (0x009C)));
+ len = WAKEUP_SOURCE_LEN - (pbuf - buf);
+ if (val && len > 30)
+ pbuf += snprintf(pbuf, len, "INTC_FIQ0(0x%x), ", val);
+
+ val = __raw_readl(OMAP2_L4_IO_ADDRESS(OMAP34XX_IC_BASE + (0x00bC)));
+ len = WAKEUP_SOURCE_LEN - (pbuf - buf);
+ if (val && len > 30)
+ pbuf += snprintf(pbuf, len, "INTC_FIQ1(0x%x), ", val);
+
+ val = __raw_readl(OMAP2_L4_IO_ADDRESS(OMAP34XX_IC_BASE + (0x00dC)));
+ len = WAKEUP_SOURCE_LEN - (pbuf - buf);
+ if (val && len > 30)
+ pbuf += snprintf(pbuf, len, "INTC_FIQ2(0x%x), ", val);
+
+
+ val = __raw_readl(OMAP2_L4_IO_ADDRESS(OMAP34XX_IC_BASE + (0x0098)));
+ len = WAKEUP_SOURCE_LEN - (pbuf - buf);
+ if (val && len > 30)
+ pbuf += snprintf(pbuf, len, "INTC_IRQ0(0x%x), ", val);
+
+ val = __raw_readl(OMAP2_L4_IO_ADDRESS(OMAP34XX_IC_BASE + (0x00B8)));
+ len = WAKEUP_SOURCE_LEN - (pbuf - buf);
+ if (val && len > 30)
+ pbuf += snprintf(pbuf, len, "INTC_IRQ1(0x%x), ", val);
+
+ val = __raw_readl(OMAP2_L4_IO_ADDRESS(OMAP34XX_IC_BASE + (0x00D8)));
+ len = WAKEUP_SOURCE_LEN - (pbuf - buf);
+ if (val && len > 30)
+ pbuf += snprintf(pbuf, len, "INTC_IRQ2(0x%x), ", val);
+
+ val = __raw_readl(OMAP2_L4_IO_ADDRESS(OMAP34XX_IC_BASE + (0x009C)));
+ len = WAKEUP_SOURCE_LEN - (pbuf - buf);
+ if (val && len > 30)
+ pbuf += snprintf(pbuf, len, "INTC_FIQ0(0x%x), ", val);
+
+ val = __raw_readl(OMAP2_L4_IO_ADDRESS(OMAP34XX_IC_BASE + (0x00BC)));
+ len = WAKEUP_SOURCE_LEN - (pbuf - buf);
+ if (val && len > 30)
+ pbuf += snprintf(pbuf, len, "INTC_FIQ1(0x%x), ", val);
+
+ val = __raw_readl(OMAP2_L4_IO_ADDRESS(OMAP34XX_IC_BASE + (0x00DC)));
+ len = WAKEUP_SOURCE_LEN - (pbuf - buf);
+ if (val && len > 30)
+ pbuf += snprintf(pbuf, len, "INTC_FIQ2(0x%x), ", val);
+
+ len = WAKEUP_SOURCE_LEN - (pbuf - buf);
+ if ((val & (1<<29)) && len > 20) {
+ gpio_bit = __raw_readl(OMAP2_L4_IO_ADDRESS(0x48310018)) &
+ __raw_readl(OMAP2_L4_IO_ADDRESS(0x4831001C));
+ pbuf += snprintf(pbuf, len, "GPIO1(0x%x), ", gpio_bit);
+ }
+ len = WAKEUP_SOURCE_LEN - (pbuf - buf);
+ if ((val & (1<<30)) && len > 20) {
+ gpio_bit = __raw_readl(OMAP2_L4_IO_ADDRESS(0x49050018)) &
+ __raw_readl(OMAP2_L4_IO_ADDRESS(0x4905001C));
+ pbuf += snprintf(pbuf, len, "GPIO2(0x%x), ", gpio_bit);
+ }
+ len = WAKEUP_SOURCE_LEN - (pbuf - buf);
+ if ((val & (1<<31)) && len > 20) {
+ gpio_bit = __raw_readl(OMAP2_L4_IO_ADDRESS(0x49052018)) &
+ __raw_readl(OMAP2_L4_IO_ADDRESS(0x4905201C));
+ pbuf += snprintf(pbuf, len, "GPIO3(0x%x), ", gpio_bit);
+ }
+
+ val = __raw_readl(OMAP2_L4_IO_ADDRESS(OMAP34XX_IC_BASE + (0x00b8)));
+ len = WAKEUP_SOURCE_LEN - (pbuf - buf);
+ if (val && len > 30)
+ pbuf += snprintf(pbuf, len, "INTC_IRQ1(0x%x), ", val);
+
+ len = WAKEUP_SOURCE_LEN - (pbuf - buf);
+ if ((val & (1<<0)) && len > 20) {
+ gpio_bit = __raw_readl(OMAP2_L4_IO_ADDRESS(0x49054018)) &
+ __raw_readl(OMAP2_L4_IO_ADDRESS(0x4905401C));
+ pbuf += snprintf(pbuf, len, "GPIO4(0x%x), ", gpio_bit);
+ }
+ len = WAKEUP_SOURCE_LEN - (pbuf - buf);
+ if ((val & (1<<1)) && len > 20) {
+ gpio_bit = __raw_readl(OMAP2_L4_IO_ADDRESS(0x49056018)) &
+ __raw_readl(OMAP2_L4_IO_ADDRESS(0x4905601C));
+ pbuf += snprintf(pbuf, len, "GPIO5(0x%x), ", gpio_bit);
+ }
+ len = WAKEUP_SOURCE_LEN - (pbuf - buf);
+ if ((val & (1<<2)) && len > 20) {
+ gpio_bit = __raw_readl(OMAP2_L4_IO_ADDRESS(0x49058018)) &
+ __raw_readl(OMAP2_L4_IO_ADDRESS(0x4905801C));
+ pbuf += snprintf(pbuf, len, "GPIO6(0x%x), ", gpio_bit);
+ }
+
+ val = __raw_readl(OMAP2_L4_IO_ADDRESS(OMAP34XX_IC_BASE + (0x00d8)));
+ len = WAKEUP_SOURCE_LEN - (pbuf - buf);
+ if (val && len > 30)
+ pbuf += snprintf(pbuf, len, "INTC_IRQ2(0x%x)", val);
+
+ pr_debug("%s\n", buf);
+
+}
+EXPORT_SYMBOL(pm_dbg_show_wakeup_source);
+
+static int pm_dbg_reg_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pm_dbg_show_regs, inode->i_private);
+}
+
+static const struct file_operations debug_reg_fops = {
+ .open = pm_dbg_reg_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+#endif
+
+int pm_dbg_regs_dump(int reg_set)
+{
+ int i, j;
+ unsigned long val;
+ u32 *ptr;
+ int regs;
+
+ if ((reg_set <= 0) || (reg_set > PM_DBG_MAX_REG_SETS))
+ return -EINVAL;
+
+ ptr = pm_dbg_reg_set[reg_set - 1];
+
+ i = 0;
+
+ while (pm_dbg_reg_modules[i].name[0] != 0) {
+ regs = 0;
+ if (pm_dbg_reg_modules[i].type == MOD_CM)
+ pr_debug("MOD: CM_%s (%08x)\n",
+ pm_dbg_reg_modules[i].name,
+ (u32)(OMAP3430_CM_BASE +
+ pm_dbg_reg_modules[i].offset));
+ else
+ pr_debug("MOD: PRM_%s (%08x)\n",
+ pm_dbg_reg_modules[i].name,
+ (u32)(OMAP3430_PRM_BASE +
+ pm_dbg_reg_modules[i].offset));
+
+ for (j = pm_dbg_reg_modules[i].low;
+ j <= pm_dbg_reg_modules[i].high; j += 4) {
+ val = *(ptr++);
+ if (val != 0) {
+ regs++;
+ pr_debug(" %02x => %08lx\n", j, val);
+ }
+ }
+ i++;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(pm_dbg_regs_dump);
+
+static void __init pm_dbg_regset_init(void)
+{
+ int i;
+
+ for (i = 0; i < PM_DBG_MAX_REG_SETS; i++) {
+ pm_dbg_reg_set[i] =
+ kmalloc(pm_dbg_get_regset_size(), GFP_KERNEL);
+ }
+}
+
+#ifdef CONFIG_DEBUG_FS
+int __init pm_dbg_regs_init(struct dentry *d)
+{
+ int i;
+ char name[2];
+
+ if (!cpu_is_omap34xx()) {
+ pr_err("%s: only OMAP3 supported\n", __func__);
+ return -ENODEV;
+ }
+
+ pm_dbg_dir = debugfs_create_dir("registers", d);
+ if (IS_ERR(pm_dbg_dir))
+ return PTR_ERR(pm_dbg_dir);
+
+ (void) debugfs_create_file("current", S_IRUGO,
+ pm_dbg_dir, (void *)0, &debug_reg_fops);
+
+ pm_dbg_regset_init();
+ for (i = 0; i < PM_DBG_MAX_REG_SETS; i++) {
+ if (pm_dbg_reg_set[i] != NULL) {
+ sprintf(name, "%d", i + 1);
+ (void) debugfs_create_file(name, S_IRUGO,
+ pm_dbg_dir, (void *)(i+1), &debug_reg_fops);
+ }
+ }
+
+ return 0;
+}
+#else
+static int __init pm_dbg_regs_init(void)
+{
+ if (!cpu_is_omap34xx()) {
+ pr_err("%s: only OMAP3 supported\n", __func__);
+ return -ENODEV;
+ }
+
+ pm_dbg_regset_init();
+ return 0;
+}
+omap_arch_initcall(pm_dbg_regs_init);
+#endif
diff --git a/arch/arm/mach-omap2/pm-debug-regs.h b/arch/arm/mach-omap2/pm-debug-regs.h
new file mode 100644
index 00000000000..e403977affa
--- /dev/null
+++ b/arch/arm/mach-omap2/pm-debug-regs.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2013 Motorola Mobility LLC
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 __PM_DEBUG_REGS_H__
+#define __PM_DEBUG_REGS_H__
+
+#include <linux/dcache.h>
+
+#ifdef CONFIG_PM_DEBUG
+extern int pm_dbg_regs_init(struct dentry *d);
+extern void pm_dbg_regs_save(int reg_set);
+extern void pm_dbg_regs_dump(int reg_set);
+extern void pm_dbg_show_wakeup_source(void);
+#else
+static inline int pm_dbg_regs_init(struct dentry *d) { return 0; }
+static inline void pm_dbg_regs_save(int reg_set) {};
+static inline void pm_dbg_regs_dump(int reg_set) {};
+static inline void pm_dbg_show_wakeup_source(void) {};
+#endif
+
+#endif
diff --git a/arch/arm/mach-omap2/pm-debug.c b/arch/arm/mach-omap2/pm-debug.c
index 0b339861d75..cc38db9edf0 100644
--- a/arch/arm/mach-omap2/pm-debug.c
+++ b/arch/arm/mach-omap2/pm-debug.c
@@ -36,6 +36,7 @@
#include "cm2xxx_3xxx.h"
#include "prm2xxx_3xxx.h"
#include "pm.h"
+#include "pm-debug-regs.h"
u32 enable_off_mode;
@@ -273,6 +274,9 @@ static int __init pm_dbg_init(void)
(void) debugfs_create_file("enable_off_mode", S_IRUGO | S_IWUSR, d,
&enable_off_mode, &pm_dbg_option_fops);
+
+ pm_dbg_regs_init(d);
+
pm_dbg_init_done = 1;
return 0;
diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c
index a52cfb7fb48..54cd309e790 100644
--- a/arch/arm/mach-omap2/pm34xx.c
+++ b/arch/arm/mach-omap2/pm34xx.c
@@ -50,6 +50,7 @@
#include "sdrc.h"
#include "sram.h"
#include "control.h"
+#include "pm-debug-regs.h"
/* pm34xx errata defined in pm.h */
u16 pm34xx_errata;
@@ -71,6 +72,7 @@ void (*omap3_do_wfi_sram)(void);
static struct powerdomain *mpu_pwrdm, *neon_pwrdm;
static struct powerdomain *core_pwrdm, *per_pwrdm;
+static struct powerdomain *dss_pwrdm;
static void omap3_core_save_context(void)
{
@@ -295,7 +297,6 @@ void omap_sram_idle(void)
}
omap3_intc_prepare_idle();
-
/*
* On EMU/HS devices ROM code restores a SRDC value
* from scratchpad which has automatic self refresh on timeout
@@ -308,6 +309,9 @@ void omap_sram_idle(void)
core_next_state == PWRDM_POWER_OFF)
sdrc_pwr = sdrc_read_reg(SDRC_POWER);
+ if (suspend_debug)
+ pm_dbg_regs_save(1);
+
/*
* omap3_arm_context is the location where some ARM context
* get saved. The rest is placed on the stack, and restored
@@ -320,6 +324,9 @@ void omap_sram_idle(void)
else
omap34xx_do_sram_idle(save_state);
+ if (suspend_debug)
+ pm_dbg_regs_save(2);
+
/* Restore normal SDRC POWER settings */
if (cpu_is_omap3430() && omap_rev() >= OMAP3430_REV_ES3_0 &&
(omap_type() == OMAP2_DEVICE_TYPE_EMU ||
@@ -396,11 +403,15 @@ restore:
}
omap_set_pwrdm_state(pwrst->pwrdm, pwrst->saved_state);
}
- if (ret)
+ if (ret) {
pr_err("Could not enter target state in pm_suspend\n");
- else
+ pm_dbg_regs_dump(1);
+ pm_dbg_regs_dump(2);
+ } else
pr_info("Successfully put all powerdomains to target state\n");
+ pm_dbg_show_wakeup_source();
+
suspend_debug = false;
return ret;
}
@@ -653,6 +664,7 @@ static void __init pm_errata_configure(void)
pm34xx_errata |= PM_RTA_ERRATUM_i608;
/* Enable the l2 cache toggling in sleep logic */
enable_omap3630_toggle_l2_on_restore();
+
if (omap_rev() < OMAP3630_REV_ES1_2)
pm34xx_errata |= (PM_SDRC_WAKEUP_ERRATUM_i583 |
PM_PER_MEMORIES_ERRATUM_i582);
@@ -713,6 +725,7 @@ int __init omap3_pm_init(void)
neon_pwrdm = pwrdm_lookup("neon_pwrdm");
per_pwrdm = pwrdm_lookup("per_pwrdm");
core_pwrdm = pwrdm_lookup("core_pwrdm");
+ dss_pwrdm = pwrdm_lookup("dss_pwrdm");
neon_clkdm = clkdm_lookup("neon_clkdm");
mpu_clkdm = clkdm_lookup("mpu_clkdm");
diff --git a/arch/arm/mach-omap2/prm2xxx.h b/arch/arm/mach-omap2/prm2xxx.h
index 3194dd87e0e..8f16aecf92f 100644
--- a/arch/arm/mach-omap2/prm2xxx.h
+++ b/arch/arm/mach-omap2/prm2xxx.h
@@ -94,6 +94,9 @@
*/
/* Register offsets appearing on both OMAP2 and OMAP3 */
+#define INTC_PENDING_IRQ0 0x0098
+#define INTC_PENDING_IRQ1 0x00b8
+#define INTC_PENDING_IRQ2 0x00d8
#define OMAP2_RM_RSTCTRL 0x0050
#define OMAP2_RM_RSTTIME 0x0054