diff options
| author | Jim Wylder <jwylder@motorola.com> | 2014-04-15 12:08:38 -0500 | 
|---|---|---|
| committer | Doug Zobel <dzobel1@motorola.com> | 2014-04-29 13:26:31 -0500 | 
| commit | 302f70450dd87df2d7daef1a37f38ff413c6593e (patch) | |
| tree | 7f7ca9ddb60a1ab5457a1309ead8c26387da8cd9 | |
| parent | 45f47718c757cab219371cd24610a0a0cbc1797a (diff) | |
| download | olio-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/Makefile | 2 | ||||
| -rw-r--r-- | arch/arm/mach-omap2/pm-debug-regs.c | 478 | ||||
| -rw-r--r-- | arch/arm/mach-omap2/pm-debug-regs.h | 31 | ||||
| -rw-r--r-- | arch/arm/mach-omap2/pm-debug.c | 4 | ||||
| -rw-r--r-- | arch/arm/mach-omap2/pm34xx.c | 19 | ||||
| -rw-r--r-- | arch/arm/mach-omap2/prm2xxx.h | 3 | 
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 |