summary refs log tree commit diff
path: root/arch/arm/mach-omap2
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@g5.osdl.org>2006-07-02 15:04:12 -0700
committerLinus Torvalds <torvalds@g5.osdl.org>2006-07-02 15:04:12 -0700
commita8c4c20dfa8b28a3c99e33c639d9c2ea5657741e (patch)
tree887b64d29b5a46d9ab2ca1267d8a2f05b5845561 /arch/arm/mach-omap2
parent168d04b3b4de7723eb73b3cffc9cb75224e0f393 (diff)
parent2dc7667b9d0674db6572723356fe3857031101a4 (diff)
downloadlinux-a8c4c20dfa8b28a3c99e33c639d9c2ea5657741e.tar.gz
Merge branch 'devel' of master.kernel.org:/home/rmk/linux-2.6-arm
* 'devel' of master.kernel.org:/home/rmk/linux-2.6-arm: (44 commits)
  [ARM] 3541/2: workaround for PXA27x erratum E7
  [ARM] nommu: provide a way for correct control register value selection
  [ARM] 3705/1: add supersection support to ioremap()
  [ARM] 3707/1: iwmmxt: use the generic thread notifier infrastructure
  [ARM] 3706/2: ep93xx: add cirrus logic edb9315a support
  [ARM] 3704/1: format IOP Kconfig with tabs, create more consistency
  [ARM] 3703/1: Add help description for ARCH_EP80219
  [ARM] 3678/1: MMC: Make OMAP MMC work
  [ARM] 3677/1: OMAP: Update H2 defconfig
  [ARM] 3676/1: ARM: OMAP: Fix dmtimers and timer32k to compile on OMAP1
  [ARM] Add section support to ioremap
  [ARM] Fix sa11x0 SDRAM selection
  [ARM] Set bit 4 on section mappings correctly depending on CPU
  [ARM] 3666/1: TRIZEPS4 [1/5] core
  ARM: OMAP: Multiplexing for 24xx GPMC wait pin monitoring
  ARM: OMAP: Fix SRAM to use MT_MEMORY instead of MT_DEVICE
  ARM: OMAP: Update dmtimers
  ARM: OMAP: Make clock variables static
  ARM: OMAP: Fix GPMC compilation when DEBUG is defined
  ARM: OMAP: Mux updates for external DMA and GPIO
  ...
Diffstat (limited to 'arch/arm/mach-omap2')
-rw-r--r--arch/arm/mach-omap2/Kconfig1
-rw-r--r--arch/arm/mach-omap2/Makefile5
-rw-r--r--arch/arm/mach-omap2/clock.c37
-rw-r--r--arch/arm/mach-omap2/clock.h2
-rw-r--r--arch/arm/mach-omap2/devices.c46
-rw-r--r--arch/arm/mach-omap2/gpmc.c209
-rw-r--r--arch/arm/mach-omap2/io.c2
-rw-r--r--arch/arm/mach-omap2/mux.c38
-rw-r--r--arch/arm/mach-omap2/pm-domain.c300
-rw-r--r--arch/arm/mach-omap2/pm.c271
-rw-r--r--arch/arm/mach-omap2/timer-gp.c86
11 files changed, 900 insertions, 97 deletions
diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index 537dd2e6d380..aab97ccf1e63 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -8,6 +8,7 @@ config ARCH_OMAP24XX
 config ARCH_OMAP2420
 	bool "OMAP2420 support"
 	depends on ARCH_OMAP24XX
+	select OMAP_DM_TIMER
 
 comment "OMAP Board Type"
 	depends on ARCH_OMAP2
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index 111eaa64258f..266d88e77bdc 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -3,12 +3,13 @@
 #
 
 # Common support
-obj-y := irq.o id.o io.o sram-fn.o memory.o prcm.o clock.o mux.o devices.o serial.o
+obj-y := irq.o id.o io.o sram-fn.o memory.o prcm.o clock.o mux.o devices.o \
+	 serial.o gpmc.o
 
 obj-$(CONFIG_OMAP_MPU_TIMER)		+= timer-gp.o
 
 # Power Management
-obj-$(CONFIG_PM) += pm.o sleep.o
+obj-$(CONFIG_PM) += pm.o pm-domain.o sleep.o
 
 # Specific board support
 obj-$(CONFIG_MACH_OMAP_GENERIC)		+= board-generic.o
diff --git a/arch/arm/mach-omap2/clock.c b/arch/arm/mach-omap2/clock.c
index 7edf0f69da1e..d1b648a4efbf 100644
--- a/arch/arm/mach-omap2/clock.c
+++ b/arch/arm/mach-omap2/clock.c
@@ -659,26 +659,35 @@ static int omap2_clk_set_rate(struct clk *clk, unsigned long rate)
 
 		/* Isolate control register */
 		div_sel = (SRC_RATE_SEL_MASK & clk->flags);
-		div_off = clk->src_offset;
+		div_off = clk->rate_offset;
 
 		validrate = omap2_clksel_round_rate(clk, rate, &new_div);
-		if(validrate != rate)
+		if (validrate != rate)
 			return(ret);
 
 		field_val = omap2_get_clksel(&div_sel, &field_mask, clk);
 		if (div_sel == 0)
 			return ret;
 
-		if(clk->flags & CM_SYSCLKOUT_SEL1){
-			switch(new_div){
-			case 16: field_val = 4; break;
-			case 8:  field_val = 3; break;
-			case 4:  field_val = 2; break;
-			case 2:  field_val = 1; break;
-			case 1:  field_val = 0; break;
+		if (clk->flags & CM_SYSCLKOUT_SEL1) {
+			switch (new_div) {
+			case 16:
+				field_val = 4;
+				break;
+			case 8:
+				field_val = 3;
+				break;
+			case 4:
+				field_val = 2;
+				break;
+			case 2:
+				field_val = 1;
+				break;
+			case 1:
+				field_val = 0;
+				break;
 			}
-		}
-		else
+		} else
 			field_val = new_div;
 
 		reg = (void __iomem *)div_sel;
@@ -743,7 +752,7 @@ static u32 omap2_get_src_field(u32 *type_to_addr, u32 reg_offset,
 			val = 0x2;
 		break;
 	case CM_WKUP_SEL1:
-		src_reg_addr = (u32)&CM_CLKSEL2_CORE;
+		src_reg_addr = (u32)&CM_CLKSEL_WKUP;
 		mask = 0x3;
 		if (src_clk == &func_32k_ck)
 			val = 0x0;
@@ -783,9 +792,9 @@ static u32 omap2_get_src_field(u32 *type_to_addr, u32 reg_offset,
 			val = 0;
 		if (src_clk == &sys_ck)
 			val = 1;
-		if (src_clk == &func_54m_ck)
-			val = 2;
 		if (src_clk == &func_96m_ck)
+			val = 2;
+		if (src_clk == &func_54m_ck)
 			val = 3;
 		break;
 	}
diff --git a/arch/arm/mach-omap2/clock.h b/arch/arm/mach-omap2/clock.h
index 6c78d471fab7..2781dfbc5164 100644
--- a/arch/arm/mach-omap2/clock.h
+++ b/arch/arm/mach-omap2/clock.h
@@ -1062,7 +1062,7 @@ static struct clk gpt2_ick = {
 	.parent		= &l4_ck,
 	.flags		= CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
 	.enable_reg	= (void __iomem *)&CM_ICLKEN1_CORE,	/* Bit4 */
-	.enable_bit	= 0,
+	.enable_bit	= 4,
 	.recalc		= &omap2_followparent_recalc,
 };
 
diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c
index 4842ffe26705..aa4322451e8b 100644
--- a/arch/arm/mach-omap2/devices.c
+++ b/arch/arm/mach-omap2/devices.c
@@ -104,6 +104,51 @@ static inline void omap_init_sti(void)
 static inline void omap_init_sti(void) {}
 #endif
 
+#if defined(CONFIG_SPI_OMAP24XX)
+
+#include <asm/arch/mcspi.h>
+
+#define OMAP2_MCSPI1_BASE		0x48098000
+#define OMAP2_MCSPI2_BASE		0x4809a000
+
+/* FIXME: use resources instead */
+
+static struct omap2_mcspi_platform_config omap2_mcspi1_config = {
+	.base		= io_p2v(OMAP2_MCSPI1_BASE),
+	.num_cs		= 4,
+};
+
+struct platform_device omap2_mcspi1 = {
+	.name		= "omap2_mcspi",
+	.id		= 1,
+	.dev		= {
+		.platform_data = &omap2_mcspi1_config,
+	},
+};
+
+static struct omap2_mcspi_platform_config omap2_mcspi2_config = {
+	.base		= io_p2v(OMAP2_MCSPI2_BASE),
+	.num_cs		= 2,
+};
+
+struct platform_device omap2_mcspi2 = {
+	.name		= "omap2_mcspi",
+	.id		= 2,
+	.dev		= {
+		.platform_data = &omap2_mcspi2_config,
+	},
+};
+
+static void omap_init_mcspi(void)
+{
+	platform_device_register(&omap2_mcspi1);
+	platform_device_register(&omap2_mcspi2);
+}
+
+#else
+static inline void omap_init_mcspi(void) {}
+#endif
+
 /*-------------------------------------------------------------------------*/
 
 static int __init omap2_init_devices(void)
@@ -112,6 +157,7 @@ static int __init omap2_init_devices(void)
 	 * in alphabetical order so they're easier to sort through.
 	 */
 	omap_init_i2c();
+	omap_init_mcspi();
 	omap_init_sti();
 
 	return 0;
diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c
new file mode 100644
index 000000000000..c7a48f921fef
--- /dev/null
+++ b/arch/arm/mach-omap2/gpmc.c
@@ -0,0 +1,209 @@
+/*
+ * GPMC support functions
+ *
+ * Copyright (C) 2005-2006 Nokia Corporation
+ *
+ * Author: Juha Yrjola
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+
+#include <asm/io.h>
+#include <asm/arch/gpmc.h>
+
+#undef DEBUG
+
+#define GPMC_BASE		0x6800a000
+#define GPMC_REVISION		0x00
+#define GPMC_SYSCONFIG		0x10
+#define GPMC_SYSSTATUS		0x14
+#define GPMC_IRQSTATUS		0x18
+#define GPMC_IRQENABLE		0x1c
+#define GPMC_TIMEOUT_CONTROL	0x40
+#define GPMC_ERR_ADDRESS	0x44
+#define GPMC_ERR_TYPE		0x48
+#define GPMC_CONFIG		0x50
+#define GPMC_STATUS		0x54
+#define GPMC_PREFETCH_CONFIG1	0x1e0
+#define GPMC_PREFETCH_CONFIG2	0x1e4
+#define GPMC_PREFETCH_CONTROL	0x1e8
+#define GPMC_PREFETCH_STATUS	0x1f0
+#define GPMC_ECC_CONFIG		0x1f4
+#define GPMC_ECC_CONTROL	0x1f8
+#define GPMC_ECC_SIZE_CONFIG	0x1fc
+
+#define GPMC_CS0		0x60
+#define GPMC_CS_SIZE		0x30
+
+static void __iomem *gpmc_base =
+	(void __iomem *) IO_ADDRESS(GPMC_BASE);
+static void __iomem *gpmc_cs_base =
+	(void __iomem *) IO_ADDRESS(GPMC_BASE) + GPMC_CS0;
+
+static struct clk *gpmc_l3_clk;
+
+static void gpmc_write_reg(int idx, u32 val)
+{
+	__raw_writel(val, gpmc_base + idx);
+}
+
+static u32 gpmc_read_reg(int idx)
+{
+	return __raw_readl(gpmc_base + idx);
+}
+
+void gpmc_cs_write_reg(int cs, int idx, u32 val)
+{
+	void __iomem *reg_addr;
+
+	reg_addr = gpmc_cs_base + (cs * GPMC_CS_SIZE) + idx;
+	__raw_writel(val, reg_addr);
+}
+
+u32 gpmc_cs_read_reg(int cs, int idx)
+{
+	return __raw_readl(gpmc_cs_base + (cs * GPMC_CS_SIZE) + idx);
+}
+
+/* TODO: Add support for gpmc_fck to clock framework and use it */
+static unsigned long gpmc_get_fclk_period(void)
+{
+	/* In picoseconds */
+	return 1000000000 / ((clk_get_rate(gpmc_l3_clk)) / 1000);
+}
+
+unsigned int gpmc_ns_to_ticks(unsigned int time_ns)
+{
+	unsigned long tick_ps;
+
+	/* Calculate in picosecs to yield more exact results */
+	tick_ps = gpmc_get_fclk_period();
+
+	return (time_ns * 1000 + tick_ps - 1) / tick_ps;
+}
+
+#ifdef DEBUG
+static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit,
+			       int time, const char *name)
+#else
+static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit,
+			       int time)
+#endif
+{
+	u32 l;
+	int ticks, mask, nr_bits;
+
+	if (time == 0)
+		ticks = 0;
+	else
+		ticks = gpmc_ns_to_ticks(time);
+	nr_bits = end_bit - st_bit + 1;
+	if (ticks >= 1 << nr_bits)
+		return -1;
+
+	mask = (1 << nr_bits) - 1;
+	l = gpmc_cs_read_reg(cs, reg);
+#ifdef DEBUG
+	printk(KERN_INFO "GPMC CS%d: %-10s: %d ticks, %3lu ns (was %i ticks)\n",
+	       cs, name, ticks, gpmc_get_fclk_period() * ticks / 1000,
+	       (l >> st_bit) & mask);
+#endif
+	l &= ~(mask << st_bit);
+	l |= ticks << st_bit;
+	gpmc_cs_write_reg(cs, reg, l);
+
+	return 0;
+}
+
+#ifdef DEBUG
+#define GPMC_SET_ONE(reg, st, end, field) \
+	if (set_gpmc_timing_reg(cs, (reg), (st), (end),		\
+			t->field, #field) < 0)			\
+		return -1
+#else
+#define GPMC_SET_ONE(reg, st, end, field) \
+	if (set_gpmc_timing_reg(cs, (reg), (st), (end), t->field) < 0) \
+		return -1
+#endif
+
+int gpmc_cs_calc_divider(int cs, unsigned int sync_clk)
+{
+	int div;
+	u32 l;
+
+	l = sync_clk * 1000 + (gpmc_get_fclk_period() - 1);
+	div = l / gpmc_get_fclk_period();
+	if (div > 4)
+		return -1;
+	if (div < 0)
+		div = 1;
+
+	return div;
+}
+
+int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t)
+{
+	int div;
+	u32 l;
+
+	div = gpmc_cs_calc_divider(cs, t->sync_clk);
+	if (div < 0)
+		return -1;
+
+	GPMC_SET_ONE(GPMC_CS_CONFIG2,  0,  3, cs_on);
+	GPMC_SET_ONE(GPMC_CS_CONFIG2,  8, 12, cs_rd_off);
+	GPMC_SET_ONE(GPMC_CS_CONFIG2, 16, 20, cs_wr_off);
+
+	GPMC_SET_ONE(GPMC_CS_CONFIG3,  0,  3, adv_on);
+	GPMC_SET_ONE(GPMC_CS_CONFIG3,  8, 12, adv_rd_off);
+	GPMC_SET_ONE(GPMC_CS_CONFIG3, 16, 20, adv_wr_off);
+
+	GPMC_SET_ONE(GPMC_CS_CONFIG4,  0,  3, oe_on);
+	GPMC_SET_ONE(GPMC_CS_CONFIG4,  8, 12, oe_off);
+	GPMC_SET_ONE(GPMC_CS_CONFIG4, 16, 19, we_on);
+	GPMC_SET_ONE(GPMC_CS_CONFIG4, 24, 28, we_off);
+
+	GPMC_SET_ONE(GPMC_CS_CONFIG5,  0,  4, rd_cycle);
+	GPMC_SET_ONE(GPMC_CS_CONFIG5,  8, 12, wr_cycle);
+	GPMC_SET_ONE(GPMC_CS_CONFIG5, 16, 20, access);
+
+	GPMC_SET_ONE(GPMC_CS_CONFIG5, 24, 27, page_burst_access);
+
+#ifdef DEBUG
+	printk(KERN_INFO "GPMC CS%d CLK period is %lu (div %d)\n",
+	       cs, gpmc_get_fclk_period(), div);
+#endif
+
+	l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);
+	l &= ~0x03;
+	l |= (div - 1);
+
+	return 0;
+}
+
+unsigned long gpmc_cs_get_base_addr(int cs)
+{
+	return (gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7) & 0x1f) << 24;
+}
+
+void __init gpmc_init(void)
+{
+	u32 l;
+
+	gpmc_l3_clk = clk_get(NULL, "core_l3_ck");
+	BUG_ON(IS_ERR(gpmc_l3_clk));
+
+	l = gpmc_read_reg(GPMC_REVISION);
+	printk(KERN_INFO "GPMC revision %d.%d\n", (l >> 4) & 0x0f, l & 0x0f);
+	/* Set smart idle mode and automatic L3 clock gating */
+	l = gpmc_read_reg(GPMC_SYSCONFIG);
+	l &= 0x03 << 3;
+	l |= (0x02 << 3) | (1 << 0);
+	gpmc_write_reg(GPMC_SYSCONFIG, l);
+}
diff --git a/arch/arm/mach-omap2/io.c b/arch/arm/mach-omap2/io.c
index 20dd6e74e91d..a0728c33e5d9 100644
--- a/arch/arm/mach-omap2/io.c
+++ b/arch/arm/mach-omap2/io.c
@@ -26,6 +26,7 @@
 extern void omap_sram_init(void);
 extern int omap2_clk_init(void);
 extern void omap2_check_revision(void);
+extern void gpmc_init(void);
 
 /*
  * The machine specific code may provide the extra mapping besides the
@@ -66,4 +67,5 @@ void __init omap2_init_common_hw(void)
 {
 	omap2_mux_init();
 	omap2_clk_init();
+	gpmc_init();
 }
diff --git a/arch/arm/mach-omap2/mux.c b/arch/arm/mach-omap2/mux.c
index 4c5f2c04883e..60ef084faffd 100644
--- a/arch/arm/mach-omap2/mux.c
+++ b/arch/arm/mach-omap2/mux.c
@@ -52,6 +52,12 @@ MUX_CFG_24XX("W19_24XX_SYS_NIRQ",	0x12c,	0,	1,	1,	1)
 /* 24xx clocks */
 MUX_CFG_24XX("W14_24XX_SYS_CLKOUT",	0x137,	0,	1,	1,	1)
 
+/* 24xx GPMC wait pin monitoring */
+MUX_CFG_24XX("L3_GPMC_WAIT0",		0x09a,	0,	1,	1,	1)
+MUX_CFG_24XX("N7_GPMC_WAIT1",		0x09b,	0,	1,	1,	1)
+MUX_CFG_24XX("M1_GPMC_WAIT2",		0x09c,	0,	1,	1,	1)
+MUX_CFG_24XX("P1_GPMC_WAIT3",		0x09d,	0,	1,	1,	1)
+
 /* 24xx McBSP */
 MUX_CFG_24XX("Y15_24XX_MCBSP2_CLKX",	0x124,	1,	1,	0,	1)
 MUX_CFG_24XX("R14_24XX_MCBSP2_FSX",	0x125,	1,	1,	0,	1)
@@ -59,18 +65,38 @@ MUX_CFG_24XX("W15_24XX_MCBSP2_DR",	0x126,	1,	1,	0,	1)
 MUX_CFG_24XX("V15_24XX_MCBSP2_DX",	0x127,	1,	1,	0,	1)
 
 /* 24xx GPIO */
-MUX_CFG_24XX("M21_242X_GPIO11",	 0x0c9,  3,      1,      1,      1)
+MUX_CFG_24XX("M21_242X_GPIO11",		0x0c9,  3,      1,      1,      1)
 MUX_CFG_24XX("AA10_242X_GPIO13",	0x0e5,  3,      0,      0,      1)
-MUX_CFG_24XX("AA6_242X_GPIO14",	 0x0e6,  3,      0,      0,      1)
-MUX_CFG_24XX("AA4_242X_GPIO15",	 0x0e7,  3,      0,      0,      1)
-MUX_CFG_24XX("Y11_242X_GPIO16",	 0x0e8,  3,      0,      0,      1)
+MUX_CFG_24XX("AA6_242X_GPIO14",		0x0e6,  3,      0,      0,      1)
+MUX_CFG_24XX("AA4_242X_GPIO15",		0x0e7,  3,      0,      0,      1)
+MUX_CFG_24XX("Y11_242X_GPIO16",		0x0e8,  3,      0,      0,      1)
 MUX_CFG_24XX("AA12_242X_GPIO17",	0x0e9,  3,      0,      0,      1)
-MUX_CFG_24XX("AA8_242X_GPIO58",	 0x0ea,  3,      0,      0,      1)
+MUX_CFG_24XX("AA8_242X_GPIO58",		0x0ea,  3,      0,      0,      1)
 MUX_CFG_24XX("Y20_24XX_GPIO60",		0x12c,	3,	0,	0,	1)
-MUX_CFG_24XX("W4__24XX_GPIO74",	 0x0f2,  3,      0,      0,      1)
+MUX_CFG_24XX("W4__24XX_GPIO74",		0x0f2,  3,      0,      0,      1)
 MUX_CFG_24XX("M15_24XX_GPIO92",		0x10a,	3,	0,	0,	1)
 MUX_CFG_24XX("V14_24XX_GPIO117",	0x128,	3,	1,	0,	1)
 
+/* 242x DBG GPIO */
+MUX_CFG_24XX("V4_242X_GPIO49",		0xd3,	3,	0,	0,	1)
+MUX_CFG_24XX("W2_242X_GPIO50",		0xd4,	3,	0,	0,	1)
+MUX_CFG_24XX("U4_242X_GPIO51",		0xd5,	3,	0,	0,	1)
+MUX_CFG_24XX("V3_242X_GPIO52",		0xd6,	3,	0,	0,	1)
+MUX_CFG_24XX("V2_242X_GPIO53",		0xd7,	3,	0,	0,	1)
+MUX_CFG_24XX("V6_242X_GPIO53",		0xcf,	3,	0,	0,	1)
+MUX_CFG_24XX("T4_242X_GPIO54",		0xd8,	3,	0,	0,	1)
+MUX_CFG_24XX("Y4_242X_GPIO54",		0xd0,	3,	0,	0,	1)
+MUX_CFG_24XX("T3_242X_GPIO55",		0xd9,	3,	0,	0,	1)
+MUX_CFG_24XX("U2_242X_GPIO56",		0xda,	3,	0,	0,	1)
+
+/* 24xx external DMA requests */
+MUX_CFG_24XX("AA10_242X_DMAREQ0",	0x0e5,  2,      0,      0,      1)
+MUX_CFG_24XX("AA6_242X_DMAREQ1",	0x0e6,  2,      0,      0,      1)
+MUX_CFG_24XX("E4_242X_DMAREQ2",		0x074,  2,      0,      0,      1)
+MUX_CFG_24XX("G4_242X_DMAREQ3",		0x073,  2,      0,      0,      1)
+MUX_CFG_24XX("D3_242X_DMAREQ4",		0x072,  2,      0,      0,      1)
+MUX_CFG_24XX("E3_242X_DMAREQ5",		0x071,  2,      0,      0,      1)
+
 /* TSC IRQ */
 MUX_CFG_24XX("P20_24XX_TSC_IRQ",	0x108,	0,	0,	0,	1)
 
diff --git a/arch/arm/mach-omap2/pm-domain.c b/arch/arm/mach-omap2/pm-domain.c
new file mode 100644
index 000000000000..5e20e740cde5
--- /dev/null
+++ b/arch/arm/mach-omap2/pm-domain.c
@@ -0,0 +1,300 @@
+/*
+ * linux/arch/arm/mach-omap2/pm-domain.c
+ *
+ * Power domain functions for OMAP2
+ *
+ * Copyright (C) 2006 Nokia Corporation
+ * Tony Lindgren <tony@atomide.com>
+ *
+ * Some code based on earlier OMAP2 sample PM code
+ * Copyright (C) 2005 Texas Instruments, Inc.
+ * Richard Woodruff <r-woodruff2@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+
+#include <asm/io.h>
+
+#include "prcm-regs.h"
+
+/* Power domain offsets */
+#define PM_MPU_OFFSET			0x100
+#define PM_CORE_OFFSET			0x200
+#define PM_GFX_OFFSET			0x300
+#define PM_WKUP_OFFSET			0x400		/* Autoidle only */
+#define PM_PLL_OFFSET			0x500		/* Autoidle only */
+#define PM_DSP_OFFSET			0x800
+#define PM_MDM_OFFSET			0xc00
+
+/* Power domain wake-up dependency control register */
+#define PM_WKDEP_OFFSET			0xc8
+#define		EN_MDM			(1 << 5)
+#define		EN_WKUP			(1 << 4)
+#define		EN_GFX			(1 << 3)
+#define		EN_DSP			(1 << 2)
+#define		EN_MPU			(1 << 1)
+#define		EN_CORE			(1 << 0)
+
+/* Core power domain state transition control register */
+#define PM_PWSTCTRL_OFFSET		0xe0
+#define		FORCESTATE		(1 << 18)	/* Only for DSP & GFX */
+#define		MEM4RETSTATE		(1 << 6)
+#define		MEM3RETSTATE		(1 << 5)
+#define		MEM2RETSTATE		(1 << 4)
+#define		MEM1RETSTATE		(1 << 3)
+#define		LOGICRETSTATE		(1 << 2)	/* Logic is retained */
+#define		POWERSTATE_OFF		0x3
+#define		POWERSTATE_RETENTION	0x1
+#define		POWERSTATE_ON		0x0
+
+/* Power domain state register */
+#define PM_PWSTST_OFFSET		0xe4
+
+/* Hardware supervised state transition control register */
+#define CM_CLKSTCTRL_OFFSET		0x48
+#define		AUTOSTAT_MPU		(1 << 0)	/* MPU */
+#define		AUTOSTAT_DSS		(1 << 2)	/* Core */
+#define		AUTOSTAT_L4		(1 << 1)	/* Core */
+#define		AUTOSTAT_L3		(1 << 0)	/* Core */
+#define		AUTOSTAT_GFX		(1 << 0)	/* GFX */
+#define		AUTOSTAT_IVA		(1 << 8)	/* 2420 IVA in DSP domain */
+#define		AUTOSTAT_DSP		(1 << 0)	/* DSP */
+#define		AUTOSTAT_MDM		(1 << 0)	/* MDM */
+
+/* Automatic control of interface clock idling */
+#define CM_AUTOIDLE1_OFFSET		0x30
+#define CM_AUTOIDLE2_OFFSET		0x34		/* Core only */
+#define CM_AUTOIDLE3_OFFSET		0x38		/* Core only */
+#define CM_AUTOIDLE4_OFFSET		0x3c		/* Core only */
+#define		AUTO_54M(x)		(((x) & 0x3) << 6)
+#define		AUTO_96M(x)		(((x) & 0x3) << 2)
+#define		AUTO_DPLL(x)		(((x) & 0x3) << 0)
+#define		AUTO_STOPPED		0x3
+#define		AUTO_BYPASS_FAST	0x2		/* DPLL only */
+#define		AUTO_BYPASS_LOW_POWER	0x1		/* DPLL only */
+#define		AUTO_DISABLED		0x0
+
+/* Voltage control PRCM_VOLTCTRL bits */
+#define		AUTO_EXTVOLT		(1 << 15)
+#define		FORCE_EXTVOLT		(1 << 14)
+#define		SETOFF_LEVEL(x)		(((x) & 0x3) << 12)
+#define		MEMRETCTRL		(1 << 8)
+#define		SETRET_LEVEL(x)		(((x) & 0x3) << 6)
+#define		VOLT_LEVEL(x)		(((x) & 0x3) << 0)
+
+#define OMAP24XX_PRCM_VBASE	IO_ADDRESS(OMAP24XX_PRCM_BASE)
+#define prcm_readl(r)		__raw_readl(OMAP24XX_PRCM_VBASE + (r))
+#define prcm_writel(v, r)	__raw_writel((v), OMAP24XX_PRCM_VBASE + (r))
+
+static u32 pmdomain_get_wakeup_dependencies(int domain_offset)
+{
+	return prcm_readl(domain_offset + PM_WKDEP_OFFSET);
+}
+
+static void pmdomain_set_wakeup_dependencies(u32 state, int domain_offset)
+{
+	prcm_writel(state, domain_offset + PM_WKDEP_OFFSET);
+}
+
+static u32 pmdomain_get_powerstate(int domain_offset)
+{
+	return prcm_readl(domain_offset + PM_PWSTCTRL_OFFSET);
+}
+
+static void pmdomain_set_powerstate(u32 state, int domain_offset)
+{
+	prcm_writel(state, domain_offset + PM_PWSTCTRL_OFFSET);
+}
+
+static u32 pmdomain_get_clock_autocontrol(int domain_offset)
+{
+	return prcm_readl(domain_offset + CM_CLKSTCTRL_OFFSET);
+}
+
+static void pmdomain_set_clock_autocontrol(u32 state, int domain_offset)
+{
+	prcm_writel(state, domain_offset + CM_CLKSTCTRL_OFFSET);
+}
+
+static u32 pmdomain_get_clock_autoidle1(int domain_offset)
+{
+	return prcm_readl(domain_offset + CM_AUTOIDLE1_OFFSET);
+}
+
+/* Core domain only */
+static u32 pmdomain_get_clock_autoidle2(int domain_offset)
+{
+	return prcm_readl(domain_offset + CM_AUTOIDLE2_OFFSET);
+}
+
+/* Core domain only */
+static u32 pmdomain_get_clock_autoidle3(int domain_offset)
+{
+	return prcm_readl(domain_offset + CM_AUTOIDLE3_OFFSET);
+}
+
+/* Core domain only */
+static u32 pmdomain_get_clock_autoidle4(int domain_offset)
+{
+	return prcm_readl(domain_offset + CM_AUTOIDLE4_OFFSET);
+}
+
+static void pmdomain_set_clock_autoidle1(u32 state, int domain_offset)
+{
+	prcm_writel(state, CM_AUTOIDLE1_OFFSET + domain_offset);
+}
+
+/* Core domain only */
+static void pmdomain_set_clock_autoidle2(u32 state, int domain_offset)
+{
+	prcm_writel(state, CM_AUTOIDLE2_OFFSET + domain_offset);
+}
+
+/* Core domain only */
+static void pmdomain_set_clock_autoidle3(u32 state, int domain_offset)
+{
+	prcm_writel(state, CM_AUTOIDLE3_OFFSET + domain_offset);
+}
+
+/* Core domain only */
+static void pmdomain_set_clock_autoidle4(u32 state, int domain_offset)
+{
+	prcm_writel(state, CM_AUTOIDLE4_OFFSET + domain_offset);
+}
+
+/*
+ * Configures power management domains to idle clocks automatically.
+ */
+void pmdomain_set_autoidle(void)
+{
+	u32 val;
+
+	/* Set PLL auto stop for 54M, 96M & DPLL */
+	pmdomain_set_clock_autoidle1(AUTO_54M(AUTO_STOPPED) |
+				     AUTO_96M(AUTO_STOPPED) |
+				     AUTO_DPLL(AUTO_STOPPED), PM_PLL_OFFSET);
+
+	/* External clock input control
+	 * REVISIT: Should this be in clock framework?
+	 */
+	PRCM_CLKSRC_CTRL |= (0x3 << 3);
+
+	/* Configure number of 32KHz clock cycles for sys_clk */
+	PRCM_CLKSSETUP = 0x00ff;
+
+	/* Configure automatic voltage transition */
+	PRCM_VOLTSETUP = 0;
+	val = PRCM_VOLTCTRL;
+	val &= ~(SETOFF_LEVEL(0x3) | VOLT_LEVEL(0x3));
+	val |= SETOFF_LEVEL(1) | VOLT_LEVEL(1) | AUTO_EXTVOLT;
+	PRCM_VOLTCTRL = val;
+
+	/* Disable emulation tools functional clock */
+	PRCM_CLKEMUL_CTRL = 0x0;
+
+	/* Set core memory retention state */
+	val = pmdomain_get_powerstate(PM_CORE_OFFSET);
+	if (cpu_is_omap2420()) {
+		val &= ~(0x7 << 3);
+		val |= (MEM3RETSTATE | MEM2RETSTATE | MEM1RETSTATE);
+	} else {
+		val &= ~(0xf << 3);
+		val |= (MEM4RETSTATE | MEM3RETSTATE | MEM2RETSTATE |
+			MEM1RETSTATE);
+	}
+	pmdomain_set_powerstate(val, PM_CORE_OFFSET);
+
+	/* OCP interface smart idle. REVISIT: Enable autoidle bit0 ? */
+	val = SMS_SYSCONFIG;
+	val &= ~(0x3 << 3);
+	val |= (0x2 << 3) | (1 << 0);
+	SMS_SYSCONFIG |= val;
+
+	val = SDRC_SYSCONFIG;
+	val &= ~(0x3 << 3);
+	val |= (0x2 << 3);
+	SDRC_SYSCONFIG = val;
+
+	/* Configure L3 interface for smart idle.
+	 * REVISIT: Enable autoidle bit0 ?
+	 */
+	val = GPMC_SYSCONFIG;
+	val &= ~(0x3 << 3);
+	val |= (0x2 << 3) | (1 << 0);
+	GPMC_SYSCONFIG = val;
+
+	pmdomain_set_powerstate(LOGICRETSTATE | POWERSTATE_RETENTION,
+				PM_MPU_OFFSET);
+	pmdomain_set_powerstate(POWERSTATE_RETENTION, PM_CORE_OFFSET);
+	if (!cpu_is_omap2420())
+		pmdomain_set_powerstate(POWERSTATE_RETENTION, PM_MDM_OFFSET);
+
+	/* Assume suspend function has saved the state for DSP and GFX */
+	pmdomain_set_powerstate(FORCESTATE | POWERSTATE_OFF, PM_DSP_OFFSET);
+	pmdomain_set_powerstate(FORCESTATE | POWERSTATE_OFF, PM_GFX_OFFSET);
+
+#if 0
+	/* REVISIT: Internal USB needs special handling */
+	force_standby_usb();
+	if (cpu_is_omap2430())
+		force_hsmmc();
+	sdram_self_refresh_on_idle_req(1);
+#endif
+
+	/* Enable clock auto control for all domains.
+	 * Note that CORE domain includes also DSS, L4 & L3.
+	 */
+	pmdomain_set_clock_autocontrol(AUTOSTAT_MPU, PM_MPU_OFFSET);
+	pmdomain_set_clock_autocontrol(AUTOSTAT_GFX, PM_GFX_OFFSET);
+	pmdomain_set_clock_autocontrol(AUTOSTAT_DSS | AUTOSTAT_L4 | AUTOSTAT_L3,
+				       PM_CORE_OFFSET);
+	if (cpu_is_omap2420())
+		pmdomain_set_clock_autocontrol(AUTOSTAT_IVA | AUTOSTAT_DSP,
+					       PM_DSP_OFFSET);
+	else {
+		pmdomain_set_clock_autocontrol(AUTOSTAT_DSP, PM_DSP_OFFSET);
+		pmdomain_set_clock_autocontrol(AUTOSTAT_MDM, PM_MDM_OFFSET);
+	}
+
+	/* Enable clock autoidle for all domains */
+	pmdomain_set_clock_autoidle1(0x2, PM_DSP_OFFSET);
+	if (cpu_is_omap2420()) {
+		pmdomain_set_clock_autoidle1(0xfffffff9, PM_CORE_OFFSET);
+		pmdomain_set_clock_autoidle2(0x7, PM_CORE_OFFSET);
+		pmdomain_set_clock_autoidle1(0x3f, PM_WKUP_OFFSET);
+	} else {
+		pmdomain_set_clock_autoidle1(0xeafffff1, PM_CORE_OFFSET);
+		pmdomain_set_clock_autoidle2(0xfff, PM_CORE_OFFSET);
+		pmdomain_set_clock_autoidle1(0x7f, PM_WKUP_OFFSET);
+		pmdomain_set_clock_autoidle1(0x3, PM_MDM_OFFSET);
+	}
+	pmdomain_set_clock_autoidle3(0x7, PM_CORE_OFFSET);
+	pmdomain_set_clock_autoidle4(0x1f, PM_CORE_OFFSET);
+}
+
+/*
+ * Initializes power domains by removing wake-up dependencies and powering
+ * down DSP and GFX. Gets called from PM init. Note that DSP and IVA code
+ * must re-enable DSP and GFX when used.
+ */
+void __init pmdomain_init(void)
+{
+	/* Remove all domain wakeup dependencies */
+	pmdomain_set_wakeup_dependencies(EN_WKUP | EN_CORE, PM_MPU_OFFSET);
+	pmdomain_set_wakeup_dependencies(0, PM_DSP_OFFSET);
+	pmdomain_set_wakeup_dependencies(0, PM_GFX_OFFSET);
+	pmdomain_set_wakeup_dependencies(EN_WKUP | EN_MPU, PM_CORE_OFFSET);
+	if (cpu_is_omap2430())
+		pmdomain_set_wakeup_dependencies(0, PM_MDM_OFFSET);
+
+	/* Power down DSP and GFX */
+	pmdomain_set_powerstate(POWERSTATE_OFF | FORCESTATE, PM_DSP_OFFSET);
+	pmdomain_set_powerstate(POWERSTATE_OFF | FORCESTATE, PM_GFX_OFFSET);
+}
diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c
index 562168fa2b16..d7eee99b7e3f 100644
--- a/arch/arm/mach-omap2/pm.c
+++ b/arch/arm/mach-omap2/pm.c
@@ -23,6 +23,7 @@
 #include <linux/interrupt.h>
 #include <linux/sysfs.h>
 #include <linux/module.h>
+#include <linux/delay.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
@@ -36,11 +37,18 @@
 #include <asm/arch/sram.h>
 #include <asm/arch/pm.h>
 
+#include "prcm-regs.h"
+
 static struct clk *vclk;
 static void (*omap2_sram_idle)(void);
 static void (*omap2_sram_suspend)(int dllctrl, int cpu_rev);
 static void (*saved_idle)(void);
 
+extern void __init pmdomain_init(void);
+extern void pmdomain_set_autoidle(void);
+
+static unsigned int omap24xx_sleep_save[OMAP24XX_SLEEP_SAVE_SIZE];
+
 void omap2_pm_idle(void)
 {
 	local_irq_disable();
@@ -87,23 +95,272 @@ static int omap2_pm_prepare(suspend_state_t state)
 	return error;
 }
 
+#define INT0_WAKE_MASK	(OMAP_IRQ_BIT(INT_24XX_GPIO_BANK1) |	\
+			OMAP_IRQ_BIT(INT_24XX_GPIO_BANK2) |	\
+			OMAP_IRQ_BIT(INT_24XX_GPIO_BANK3))
+
+#define INT1_WAKE_MASK	(OMAP_IRQ_BIT(INT_24XX_GPIO_BANK4))
+
+#define INT2_WAKE_MASK	(OMAP_IRQ_BIT(INT_24XX_UART1_IRQ) |	\
+			OMAP_IRQ_BIT(INT_24XX_UART2_IRQ) |	\
+			OMAP_IRQ_BIT(INT_24XX_UART3_IRQ))
+
+#define preg(reg)	printk("%s\t(0x%p):\t0x%08x\n", #reg, &reg, reg);
+
+static void omap2_pm_debug(char * desc)
+{
+	printk("%s:\n", desc);
+
+	preg(CM_CLKSTCTRL_MPU);
+	preg(CM_CLKSTCTRL_CORE);
+	preg(CM_CLKSTCTRL_GFX);
+	preg(CM_CLKSTCTRL_DSP);
+	preg(CM_CLKSTCTRL_MDM);
+
+	preg(PM_PWSTCTRL_MPU);
+	preg(PM_PWSTCTRL_CORE);
+	preg(PM_PWSTCTRL_GFX);
+	preg(PM_PWSTCTRL_DSP);
+	preg(PM_PWSTCTRL_MDM);
+
+	preg(PM_PWSTST_MPU);
+	preg(PM_PWSTST_CORE);
+	preg(PM_PWSTST_GFX);
+	preg(PM_PWSTST_DSP);
+	preg(PM_PWSTST_MDM);
+
+	preg(CM_AUTOIDLE1_CORE);
+	preg(CM_AUTOIDLE2_CORE);
+	preg(CM_AUTOIDLE3_CORE);
+	preg(CM_AUTOIDLE4_CORE);
+	preg(CM_AUTOIDLE_WKUP);
+	preg(CM_AUTOIDLE_PLL);
+	preg(CM_AUTOIDLE_DSP);
+	preg(CM_AUTOIDLE_MDM);
+
+	preg(CM_ICLKEN1_CORE);
+	preg(CM_ICLKEN2_CORE);
+	preg(CM_ICLKEN3_CORE);
+	preg(CM_ICLKEN4_CORE);
+	preg(CM_ICLKEN_GFX);
+	preg(CM_ICLKEN_WKUP);
+	preg(CM_ICLKEN_DSP);
+	preg(CM_ICLKEN_MDM);
+
+	preg(CM_IDLEST1_CORE);
+	preg(CM_IDLEST2_CORE);
+	preg(CM_IDLEST3_CORE);
+	preg(CM_IDLEST4_CORE);
+	preg(CM_IDLEST_GFX);
+	preg(CM_IDLEST_WKUP);
+	preg(CM_IDLEST_CKGEN);
+	preg(CM_IDLEST_DSP);
+	preg(CM_IDLEST_MDM);
+
+	preg(RM_RSTST_MPU);
+	preg(RM_RSTST_GFX);
+	preg(RM_RSTST_WKUP);
+	preg(RM_RSTST_DSP);
+	preg(RM_RSTST_MDM);
+
+	preg(PM_WKDEP_MPU);
+	preg(PM_WKDEP_CORE);
+	preg(PM_WKDEP_GFX);
+	preg(PM_WKDEP_DSP);
+	preg(PM_WKDEP_MDM);
+
+	preg(CM_FCLKEN_WKUP);
+	preg(CM_ICLKEN_WKUP);
+	preg(CM_IDLEST_WKUP);
+	preg(CM_AUTOIDLE_WKUP);
+	preg(CM_CLKSEL_WKUP);
+
+	preg(PM_WKEN_WKUP);
+	preg(PM_WKST_WKUP);
+}
+
+static inline void omap2_pm_save_registers(void)
+{
+	/* Save interrupt registers */
+	OMAP24XX_SAVE(INTC_MIR0);
+	OMAP24XX_SAVE(INTC_MIR1);
+	OMAP24XX_SAVE(INTC_MIR2);
+
+	/* Save power control registers */
+	OMAP24XX_SAVE(CM_CLKSTCTRL_MPU);
+	OMAP24XX_SAVE(CM_CLKSTCTRL_CORE);
+	OMAP24XX_SAVE(CM_CLKSTCTRL_GFX);
+	OMAP24XX_SAVE(CM_CLKSTCTRL_DSP);
+	OMAP24XX_SAVE(CM_CLKSTCTRL_MDM);
+
+	/* Save power state registers */
+	OMAP24XX_SAVE(PM_PWSTCTRL_MPU);
+	OMAP24XX_SAVE(PM_PWSTCTRL_CORE);
+	OMAP24XX_SAVE(PM_PWSTCTRL_GFX);
+	OMAP24XX_SAVE(PM_PWSTCTRL_DSP);
+	OMAP24XX_SAVE(PM_PWSTCTRL_MDM);
+
+	/* Save autoidle registers */
+	OMAP24XX_SAVE(CM_AUTOIDLE1_CORE);
+	OMAP24XX_SAVE(CM_AUTOIDLE2_CORE);
+	OMAP24XX_SAVE(CM_AUTOIDLE3_CORE);
+	OMAP24XX_SAVE(CM_AUTOIDLE4_CORE);
+	OMAP24XX_SAVE(CM_AUTOIDLE_WKUP);
+	OMAP24XX_SAVE(CM_AUTOIDLE_PLL);
+	OMAP24XX_SAVE(CM_AUTOIDLE_DSP);
+	OMAP24XX_SAVE(CM_AUTOIDLE_MDM);
+
+	/* Save idle state registers */
+	OMAP24XX_SAVE(CM_IDLEST1_CORE);
+	OMAP24XX_SAVE(CM_IDLEST2_CORE);
+	OMAP24XX_SAVE(CM_IDLEST3_CORE);
+	OMAP24XX_SAVE(CM_IDLEST4_CORE);
+	OMAP24XX_SAVE(CM_IDLEST_GFX);
+	OMAP24XX_SAVE(CM_IDLEST_WKUP);
+	OMAP24XX_SAVE(CM_IDLEST_CKGEN);
+	OMAP24XX_SAVE(CM_IDLEST_DSP);
+	OMAP24XX_SAVE(CM_IDLEST_MDM);
+
+	/* Save clock registers */
+	OMAP24XX_SAVE(CM_FCLKEN1_CORE);
+	OMAP24XX_SAVE(CM_FCLKEN2_CORE);
+	OMAP24XX_SAVE(CM_ICLKEN1_CORE);
+	OMAP24XX_SAVE(CM_ICLKEN2_CORE);
+	OMAP24XX_SAVE(CM_ICLKEN3_CORE);
+	OMAP24XX_SAVE(CM_ICLKEN4_CORE);
+}
+
+static inline void omap2_pm_restore_registers(void)
+{
+	/* Restore clock state registers */
+	OMAP24XX_RESTORE(CM_CLKSTCTRL_MPU);
+	OMAP24XX_RESTORE(CM_CLKSTCTRL_CORE);
+	OMAP24XX_RESTORE(CM_CLKSTCTRL_GFX);
+	OMAP24XX_RESTORE(CM_CLKSTCTRL_DSP);
+	OMAP24XX_RESTORE(CM_CLKSTCTRL_MDM);
+
+	/* Restore power state registers */
+	OMAP24XX_RESTORE(PM_PWSTCTRL_MPU);
+	OMAP24XX_RESTORE(PM_PWSTCTRL_CORE);
+	OMAP24XX_RESTORE(PM_PWSTCTRL_GFX);
+	OMAP24XX_RESTORE(PM_PWSTCTRL_DSP);
+	OMAP24XX_RESTORE(PM_PWSTCTRL_MDM);
+
+	/* Restore idle state registers */
+	OMAP24XX_RESTORE(CM_IDLEST1_CORE);
+	OMAP24XX_RESTORE(CM_IDLEST2_CORE);
+	OMAP24XX_RESTORE(CM_IDLEST3_CORE);
+	OMAP24XX_RESTORE(CM_IDLEST4_CORE);
+	OMAP24XX_RESTORE(CM_IDLEST_GFX);
+	OMAP24XX_RESTORE(CM_IDLEST_WKUP);
+	OMAP24XX_RESTORE(CM_IDLEST_CKGEN);
+	OMAP24XX_RESTORE(CM_IDLEST_DSP);
+	OMAP24XX_RESTORE(CM_IDLEST_MDM);
+
+	/* Restore autoidle registers */
+	OMAP24XX_RESTORE(CM_AUTOIDLE1_CORE);
+	OMAP24XX_RESTORE(CM_AUTOIDLE2_CORE);
+	OMAP24XX_RESTORE(CM_AUTOIDLE3_CORE);
+	OMAP24XX_RESTORE(CM_AUTOIDLE4_CORE);
+	OMAP24XX_RESTORE(CM_AUTOIDLE_WKUP);
+	OMAP24XX_RESTORE(CM_AUTOIDLE_PLL);
+	OMAP24XX_RESTORE(CM_AUTOIDLE_DSP);
+	OMAP24XX_RESTORE(CM_AUTOIDLE_MDM);
+
+	/* Restore clock registers */
+	OMAP24XX_RESTORE(CM_FCLKEN1_CORE);
+	OMAP24XX_RESTORE(CM_FCLKEN2_CORE);
+	OMAP24XX_RESTORE(CM_ICLKEN1_CORE);
+	OMAP24XX_RESTORE(CM_ICLKEN2_CORE);
+	OMAP24XX_RESTORE(CM_ICLKEN3_CORE);
+	OMAP24XX_RESTORE(CM_ICLKEN4_CORE);
+
+	/* REVISIT: Clear interrupts here */
+
+	/* Restore interrupt registers */
+	OMAP24XX_RESTORE(INTC_MIR0);
+	OMAP24XX_RESTORE(INTC_MIR1);
+	OMAP24XX_RESTORE(INTC_MIR2);
+}
+
+static int omap2_pm_suspend(void)
+{
+	int processor_type = 0;
+
+	/* REVISIT: 0x21 or 0x26? */
+	if (cpu_is_omap2420())
+		processor_type = 0x21;
+
+	if (!processor_type)
+		return -ENOTSUPP;
+
+	local_irq_disable();
+	local_fiq_disable();
+
+	omap2_pm_save_registers();
+
+	/* Disable interrupts except for the wake events */
+	INTC_MIR_SET0 = 0xffffffff & ~INT0_WAKE_MASK;
+	INTC_MIR_SET1 = 0xffffffff & ~INT1_WAKE_MASK;
+	INTC_MIR_SET2 = 0xffffffff & ~INT2_WAKE_MASK;
+
+	pmdomain_set_autoidle();
+
+	/* Clear old wake-up events */
+	PM_WKST1_CORE = 0;
+	PM_WKST2_CORE = 0;
+	PM_WKST_WKUP = 0;
+
+	/* Enable wake-up events */
+	PM_WKEN1_CORE = (1 << 22) | (1 << 21);	/* UART1 & 2 */
+	PM_WKEN2_CORE = (1 << 2);		/* UART3 */
+	PM_WKEN_WKUP = (1 << 2) | (1 << 0);	/* GPIO & GPT1 */
+
+	/* Disable clocks except for CM_ICLKEN2_CORE. It gets disabled
+	 * in the SRAM suspend code */
+	CM_FCLKEN1_CORE = 0;
+	CM_FCLKEN2_CORE = 0;
+	CM_ICLKEN1_CORE = 0;
+	CM_ICLKEN3_CORE = 0;
+	CM_ICLKEN4_CORE = 0;
+
+	omap2_pm_debug("Status before suspend");
+
+	/* Must wait for serial buffers to clear */
+	mdelay(200);
+
+	/* Jump to SRAM suspend code
+	 * REVISIT: When is this SDRC_DLLB_CTRL?
+	 */
+	omap2_sram_suspend(SDRC_DLLA_CTRL, processor_type);
+
+	/* Back from sleep */
+	omap2_pm_restore_registers();
+
+	local_fiq_enable();
+	local_irq_enable();
+
+	return 0;
+}
+
 static int omap2_pm_enter(suspend_state_t state)
 {
+	int ret = 0;
+
 	switch (state)
 	{
 	case PM_SUSPEND_STANDBY:
 	case PM_SUSPEND_MEM:
-		/* FIXME: Add suspend */
+		ret = omap2_pm_suspend();
 		break;
-
 	case PM_SUSPEND_DISK:
-		return -ENOTSUPP;
-
+		ret = -ENOTSUPP;
+		break;
 	default:
-		return -EINVAL;
+		ret = -EINVAL;
 	}
 
-	return 0;
+	return ret;
 }
 
 static int omap2_pm_finish(suspend_state_t state)
@@ -143,6 +400,8 @@ int __init omap2_pm_init(void)
 	pm_set_ops(&omap_pm_ops);
 	pm_idle = omap2_pm_idle;
 
+	pmdomain_init();
+
 	return 0;
 }
 
diff --git a/arch/arm/mach-omap2/timer-gp.c b/arch/arm/mach-omap2/timer-gp.c
index 1d2f5ac2f69b..cf78e6c5a277 100644
--- a/arch/arm/mach-omap2/timer-gp.c
+++ b/arch/arm/mach-omap2/timer-gp.c
@@ -6,6 +6,7 @@
  * Copyright (C) 2005 Nokia Corporation
  * Author: Paul Mundt <paul.mundt@nokia.com>
  *         Juha Yrjölä <juha.yrjola@nokia.com>
+ * OMAP Dual-mode timer framework support by Timo Teras
  *
  * Some parts based off of TI's 24xx code:
  *
@@ -22,54 +23,18 @@
 #include <linux/interrupt.h>
 #include <linux/err.h>
 #include <linux/clk.h>
+#include <linux/delay.h>
 
 #include <asm/mach/time.h>
-#include <asm/delay.h>
-#include <asm/io.h>
+#include <asm/arch/dmtimer.h>
 
-#define OMAP2_GP_TIMER1_BASE	0x48028000
-#define OMAP2_GP_TIMER2_BASE	0x4802a000
-#define OMAP2_GP_TIMER3_BASE	0x48078000
-#define OMAP2_GP_TIMER4_BASE	0x4807a000
+static struct omap_dm_timer *gptimer;
 
-#define GP_TIMER_TIDR		0x00
-#define GP_TIMER_TISR		0x18
-#define GP_TIMER_TIER		0x1c
-#define GP_TIMER_TCLR		0x24
-#define GP_TIMER_TCRR		0x28
-#define GP_TIMER_TLDR		0x2c
-#define GP_TIMER_TSICR		0x40
-
-#define OS_TIMER_NR		1  /* GP timer 2 */
-
-static unsigned long timer_base[] = {
-	IO_ADDRESS(OMAP2_GP_TIMER1_BASE),
-	IO_ADDRESS(OMAP2_GP_TIMER2_BASE),
-	IO_ADDRESS(OMAP2_GP_TIMER3_BASE),
-	IO_ADDRESS(OMAP2_GP_TIMER4_BASE),
-};
-
-static inline unsigned int timer_read_reg(int nr, unsigned int reg)
-{
-	return __raw_readl(timer_base[nr] + reg);
-}
-
-static inline void timer_write_reg(int nr, unsigned int reg, unsigned int val)
-{
-	__raw_writel(val, timer_base[nr] + reg);
-}
-
-/* Note that we always enable the clock prescale divider bit */
-static inline void omap2_gp_timer_start(int nr, unsigned long load_val)
+static inline void omap2_gp_timer_start(unsigned long load_val)
 {
-	unsigned int tmp;
-
-	tmp = 0xffffffff - load_val;
-
-	timer_write_reg(nr, GP_TIMER_TLDR, tmp);
-	timer_write_reg(nr, GP_TIMER_TCRR, tmp);
-	timer_write_reg(nr, GP_TIMER_TIER, 1 << 1);
-	timer_write_reg(nr, GP_TIMER_TCLR, (1 << 5) | (1 << 1) | 1);
+	omap_dm_timer_set_load(gptimer, 1, 0xffffffff - load_val);
+	omap_dm_timer_set_int_enable(gptimer, OMAP_TIMER_INT_OVERFLOW);
+	omap_dm_timer_start(gptimer);
 }
 
 static irqreturn_t omap2_gp_timer_interrupt(int irq, void *dev_id,
@@ -77,7 +42,7 @@ static irqreturn_t omap2_gp_timer_interrupt(int irq, void *dev_id,
 {
 	write_seqlock(&xtime_lock);
 
-	timer_write_reg(OS_TIMER_NR, GP_TIMER_TISR, 1 << 1);
+	omap_dm_timer_write_status(gptimer, OMAP_TIMER_INT_OVERFLOW);
 	timer_tick(regs);
 
 	write_sequnlock(&xtime_lock);
@@ -87,41 +52,26 @@ static irqreturn_t omap2_gp_timer_interrupt(int irq, void *dev_id,
 
 static struct irqaction omap2_gp_timer_irq = {
 	.name		= "gp timer",
-	.flags		= SA_INTERRUPT,
+	.flags		= SA_INTERRUPT | SA_TIMER,
 	.handler	= omap2_gp_timer_interrupt,
 };
 
 static void __init omap2_gp_timer_init(void)
 {
-	struct clk * sys_ck;
-	u32 tick_period = 120000;
-	u32 l;
+	u32 tick_period;
 
-	/* Reset clock and prescale value */
-	timer_write_reg(OS_TIMER_NR, GP_TIMER_TCLR, 0);
+	omap_dm_timer_init();
+	gptimer = omap_dm_timer_request_specific(1);
+	BUG_ON(gptimer == NULL);
 
-	sys_ck = clk_get(NULL, "sys_ck");
-	if (IS_ERR(sys_ck))
-		printk(KERN_ERR "Could not get sys_ck\n");
-	else {
-		clk_enable(sys_ck);
-		tick_period = clk_get_rate(sys_ck) / 100;
-		clk_put(sys_ck);
-	}
-
-	tick_period /= 2;	/* Minimum prescale divider is 2 */
+	omap_dm_timer_set_source(gptimer, OMAP_TIMER_SRC_SYS_CLK);
+	tick_period = clk_get_rate(omap_dm_timer_get_fclk(gptimer)) / 100;
 	tick_period -= 1;
 
-	l = timer_read_reg(OS_TIMER_NR, GP_TIMER_TIDR);
-	printk(KERN_INFO "OMAP2 GP timer (HW version %d.%d)\n",
-	       (l >> 4) & 0x0f, l & 0x0f);
-
-	setup_irq(38, &omap2_gp_timer_irq);
-
-	omap2_gp_timer_start(OS_TIMER_NR, tick_period);
+	setup_irq(omap_dm_timer_get_irq(gptimer), &omap2_gp_timer_irq);
+	omap2_gp_timer_start(tick_period);
 }
 
 struct sys_timer omap_timer = {
 	.init	= omap2_gp_timer_init,
 };
-