All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 00/28] Updates for Tegra support in 2.6.39
@ 2011-01-24  2:01 Colin Cross
  2011-01-24  2:01   ` Colin Cross
                   ` (27 more replies)
  0 siblings, 28 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-arm-kernel

This patch set adds support for suspend, idle, and cpufreq
for the Tegra subsystem.

Patch 2 adds functions to save and restore the GIC state
during suspend and resume, and patch 3 exports the GIC irq 
chip functions to support custom IRQ chips that need to
update the GIC as well as another block during IRQ 
operations.  Patches 2 and 3 depend on patch 1, which fixes
a naming conflict in mach-tegra, so the whole stack will
need to go in through one tree.

 arch/arm/common/gic.c                          |  138 ++++-
 arch/arm/configs/tegra_defconfig               |  123 ++++
 arch/arm/include/asm/hardware/cache-l2x0.h     |    3 +-
 arch/arm/include/asm/hardware/gic.h            |   13 +
 arch/arm/mach-tegra/Makefile                   |   12 +-
 arch/arm/mach-tegra/board.h                    |    6 +
 arch/arm/mach-tegra/common.c                   |   49 ++-
 arch/arm/mach-tegra/cortex-a9.S                |  504 +++++++++++++++
 arch/arm/mach-tegra/cpu-tegra.c                |   75 ++-
 arch/arm/mach-tegra/cpuidle.c                  |  714 ++++++++++++++++++++++
 arch/arm/mach-tegra/dma.c                      |  198 ++++---
 arch/arm/mach-tegra/gpio.c                     |   15 +-
 arch/arm/mach-tegra/headsmp-t2.S               |  216 +++++++
 arch/arm/mach-tegra/headsmp.S                  |   61 --
 arch/arm/mach-tegra/hotplug.c                  |  126 ----
 arch/arm/mach-tegra/include/mach/clk.h         |    2 +
 arch/arm/mach-tegra/include/mach/clkdev.h      |    2 +
 arch/arm/mach-tegra/include/mach/debug-macro.S |   25 +-
 arch/arm/mach-tegra/include/mach/iomap.h       |   69 ++-
 arch/arm/mach-tegra/include/mach/irqs.h        |   14 +-
 arch/arm/mach-tegra/include/mach/legacy_irq.h  |    7 +-
 arch/arm/mach-tegra/include/mach/pinmux-t2.h   |   10 +
 arch/arm/mach-tegra/include/mach/powergate.h   |   40 ++
 arch/arm/mach-tegra/include/mach/suspend.h     |   80 +++
 arch/arm/mach-tegra/include/mach/system.h      |   10 +-
 arch/arm/mach-tegra/include/mach/uncompress.h  |   18 +-
 arch/arm/mach-tegra/irq.c                      |  334 ++++++++---
 arch/arm/mach-tegra/legacy_irq.c               |  109 ++++-
 arch/arm/mach-tegra/pinmux-t2-tables.c         |   26 +-
 arch/arm/mach-tegra/platsmp.c                  |   89 +++-
 arch/arm/mach-tegra/power-macros.S             |   57 ++
 arch/arm/mach-tegra/power.h                    |   64 ++
 arch/arm/mach-tegra/powergate.c                |  212 +++++++
 arch/arm/mach-tegra/suspend-t2.c               |  414 +++++++++++++
 arch/arm/mach-tegra/suspend.c                  |  776 ++++++++++++++++++++++++
 arch/arm/mach-tegra/tegra2_clocks.c            |  264 ++++++++-
 arch/arm/mach-tegra/tegra2_save.S              |  413 +++++++++++++
 arch/arm/mach-tegra/timer.c                    |   96 +++-
 arch/arm/mm/cache-l2x0.c                       |   14 +-
 39 files changed, 4898 insertions(+), 500 deletions(-)

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 01/28] ARM: tegra: irq: Rename gic pointers to avoid conflicts
  2011-01-24  2:01 [PATCH v2 00/28] Updates for Tegra support in 2.6.39 Colin Cross
@ 2011-01-24  2:01   ` Colin Cross
  2011-01-24  2:01   ` Colin Cross
                     ` (26 subsequent siblings)
  27 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-tegra
  Cc: linux-arm-kernel, olof, konkers, Colin Cross, Russell King, linux-kernel

A future patch will export gic_mask_irq and gic_unmask_irq.
Rename the pointers in arch/arm/mach-tegra/irq.c to avoid
a compile error.

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/irq.c |   18 +++++++++---------
 1 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/arch/arm/mach-tegra/irq.c b/arch/arm/mach-tegra/irq.c
index de7dfad..17c74d2 100644
--- a/arch/arm/mach-tegra/irq.c
+++ b/arch/arm/mach-tegra/irq.c
@@ -46,24 +46,24 @@
 #define ICTLR_COP_IER_CLR	0x38
 #define ICTLR_COP_IEP_CLASS	0x3c
 
-static void (*gic_mask_irq)(struct irq_data *d);
-static void (*gic_unmask_irq)(struct irq_data *d);
+static void (*tegra_gic_mask_irq)(struct irq_data *d);
+static void (*tegra_gic_unmask_irq)(struct irq_data *d);
 
-#define irq_to_ictlr(irq) (((irq)-32) >> 5)
+#define irq_to_ictlr(irq) (((irq) - 32) >> 5)
 static void __iomem *tegra_ictlr_base = IO_ADDRESS(TEGRA_PRIMARY_ICTLR_BASE);
-#define ictlr_to_virt(ictlr) (tegra_ictlr_base + (ictlr)*0x100)
+#define ictlr_to_virt(ictlr) (tegra_ictlr_base + (ictlr) * 0x100)
 
 static void tegra_mask(struct irq_data *d)
 {
 	void __iomem *addr = ictlr_to_virt(irq_to_ictlr(d->irq));
-	gic_mask_irq(d);
-	writel(1<<(d->irq&31), addr+ICTLR_CPU_IER_CLR);
+	tegra_gic_mask_irq(d);
+	writel(1 << (d->irq & 31), addr+ICTLR_CPU_IER_CLR);
 }
 
 static void tegra_unmask(struct irq_data *d)
 {
 	void __iomem *addr = ictlr_to_virt(irq_to_ictlr(d->irq));
-	gic_unmask_irq(d);
+	tegra_gic_unmask_irq(d);
 	writel(1<<(d->irq&31), addr+ICTLR_CPU_IER_SET);
 }
 
@@ -98,8 +98,8 @@ void __init tegra_init_irq(void)
 		 IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100));
 
 	gic = get_irq_chip(29);
-	gic_unmask_irq = gic->irq_unmask;
-	gic_mask_irq = gic->irq_mask;
+	tegra_gic_unmask_irq = gic->irq_unmask;
+	tegra_gic_mask_irq = gic->irq_mask;
 	tegra_irq.irq_ack = gic->irq_ack;
 #ifdef CONFIG_SMP
 	tegra_irq.irq_set_affinity = gic->irq_set_affinity;
-- 
1.7.3.1


^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 01/28] ARM: tegra: irq: Rename gic pointers to avoid conflicts
@ 2011-01-24  2:01   ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-arm-kernel

A future patch will export gic_mask_irq and gic_unmask_irq.
Rename the pointers in arch/arm/mach-tegra/irq.c to avoid
a compile error.

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/irq.c |   18 +++++++++---------
 1 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/arch/arm/mach-tegra/irq.c b/arch/arm/mach-tegra/irq.c
index de7dfad..17c74d2 100644
--- a/arch/arm/mach-tegra/irq.c
+++ b/arch/arm/mach-tegra/irq.c
@@ -46,24 +46,24 @@
 #define ICTLR_COP_IER_CLR	0x38
 #define ICTLR_COP_IEP_CLASS	0x3c
 
-static void (*gic_mask_irq)(struct irq_data *d);
-static void (*gic_unmask_irq)(struct irq_data *d);
+static void (*tegra_gic_mask_irq)(struct irq_data *d);
+static void (*tegra_gic_unmask_irq)(struct irq_data *d);
 
-#define irq_to_ictlr(irq) (((irq)-32) >> 5)
+#define irq_to_ictlr(irq) (((irq) - 32) >> 5)
 static void __iomem *tegra_ictlr_base = IO_ADDRESS(TEGRA_PRIMARY_ICTLR_BASE);
-#define ictlr_to_virt(ictlr) (tegra_ictlr_base + (ictlr)*0x100)
+#define ictlr_to_virt(ictlr) (tegra_ictlr_base + (ictlr) * 0x100)
 
 static void tegra_mask(struct irq_data *d)
 {
 	void __iomem *addr = ictlr_to_virt(irq_to_ictlr(d->irq));
-	gic_mask_irq(d);
-	writel(1<<(d->irq&31), addr+ICTLR_CPU_IER_CLR);
+	tegra_gic_mask_irq(d);
+	writel(1 << (d->irq & 31), addr+ICTLR_CPU_IER_CLR);
 }
 
 static void tegra_unmask(struct irq_data *d)
 {
 	void __iomem *addr = ictlr_to_virt(irq_to_ictlr(d->irq));
-	gic_unmask_irq(d);
+	tegra_gic_unmask_irq(d);
 	writel(1<<(d->irq&31), addr+ICTLR_CPU_IER_SET);
 }
 
@@ -98,8 +98,8 @@ void __init tegra_init_irq(void)
 		 IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100));
 
 	gic = get_irq_chip(29);
-	gic_unmask_irq = gic->irq_unmask;
-	gic_mask_irq = gic->irq_mask;
+	tegra_gic_unmask_irq = gic->irq_unmask;
+	tegra_gic_mask_irq = gic->irq_mask;
 	tegra_irq.irq_ack = gic->irq_ack;
 #ifdef CONFIG_SMP
 	tegra_irq.irq_set_affinity = gic->irq_set_affinity;
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 02/28] ARM: gic: Add functions to save and restore gic state
  2011-01-24  2:01 [PATCH v2 00/28] Updates for Tegra support in 2.6.39 Colin Cross
@ 2011-01-24  2:01   ` Colin Cross
  2011-01-24  2:01   ` Colin Cross
                     ` (26 subsequent siblings)
  27 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-tegra
  Cc: linux-arm-kernel, olof, konkers, Colin Cross, Gary King,
	Russell King, Catalin Marinas, Abhijeet Dharmapurikar,
	Linus Walleij, linux-kernel

on systems with idle states which power-gate the logic including
the gic, such as tegra, the gic distributor needs to be shut down
and restored on entry and exit from the architecture idle code

Original-author: Gary King <gking@nvidia.com>
Signed-off-by: Gary King <gking@nvidia.com>
Signed-off-by: Colin Cross <ccross@android.com>
Acked-by: Linus Walleij <linus.walleij@stericsson.com>
---
v2: Updated on top of changes to gic initialization

This patch depends on patch 1: 
ARM: tegra: irq: Rename gic pointers to avoid conflicts

 arch/arm/common/gic.c               |  127 +++++++++++++++++++++++++++++++++--
 arch/arm/include/asm/hardware/gic.h |    3 +
 2 files changed, 125 insertions(+), 5 deletions(-)

diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c
index 2243772..a6f8c58 100644
--- a/arch/arm/common/gic.c
+++ b/arch/arm/common/gic.c
@@ -42,6 +42,13 @@ struct gic_chip_data {
 	unsigned int irq_offset;
 	void __iomem *dist_base;
 	void __iomem *cpu_base;
+#ifdef CONFIG_PM
+	u32 saved_enable[DIV_ROUND_UP(1020, 32)];
+	u32 saved_conf[DIV_ROUND_UP(1020, 16)];
+	u32 saved_pri[DIV_ROUND_UP(1020, 4)];
+	u32 saved_target[DIV_ROUND_UP(1020, 4)];
+#endif
+	unsigned int gic_irqs;
 };
 
 #ifndef MAX_GIC_NR
@@ -216,10 +223,9 @@ void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq)
 	set_irq_chained_handler(irq, gic_handle_cascade_irq);
 }
 
-static void __init gic_dist_init(struct gic_chip_data *gic,
-	unsigned int irq_start)
+static unsigned int _gic_dist_init(struct gic_chip_data *gic)
 {
-	unsigned int gic_irqs, irq_limit, i;
+	unsigned int gic_irqs, i;
 	void __iomem *base = gic->dist_base;
 	u32 cpumask = 1 << smp_processor_id();
 
@@ -262,10 +268,25 @@ static void __init gic_dist_init(struct gic_chip_data *gic,
 	for (i = 32; i < gic_irqs; i += 32)
 		writel(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i * 4 / 32);
 
+	return gic_irqs;
+}
+
+static void _gic_dist_exit(unsigned int gic_nr)
+{
+	writel(0, gic_data[gic_nr].dist_base + GIC_DIST_CTRL);
+}
+
+void __init gic_dist_init(struct gic_chip_data *gic, unsigned int irq_start)
+{
+	unsigned int irq_limit;
+	unsigned int i;
+
+	gic->gic_irqs = _gic_dist_init(gic);
+
 	/*
 	 * Limit number of interrupts registered to the platform maximum
 	 */
-	irq_limit = gic->irq_offset + gic_irqs;
+	irq_limit = gic->irq_offset + gic->gic_irqs;
 	if (WARN_ON(irq_limit > NR_IRQS))
 		irq_limit = NR_IRQS;
 
@@ -279,7 +300,15 @@ static void __init gic_dist_init(struct gic_chip_data *gic,
 		set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
 	}
 
-	writel(1, base + GIC_DIST_CTRL);
+	writel(1, gic->dist_base + GIC_DIST_CTRL);
+}
+
+void gic_dist_exit(unsigned int gic_nr)
+{
+	if (gic_nr >= MAX_GIC_NR)
+		BUG();
+
+	_gic_dist_exit(gic_nr);
 }
 
 static void __cpuinit gic_cpu_init(struct gic_chip_data *gic)
@@ -305,6 +334,14 @@ static void __cpuinit gic_cpu_init(struct gic_chip_data *gic)
 	writel(1, base + GIC_CPU_CTRL);
 }
 
+void gic_cpu_exit(unsigned int gic_nr)
+{
+	if (gic_nr >= MAX_GIC_NR)
+		BUG();
+
+	writel(0, gic_data[gic_nr].cpu_base + GIC_CPU_CTRL);
+}
+
 void __init gic_init(unsigned int gic_nr, unsigned int irq_start,
 	void __iomem *dist_base, void __iomem *cpu_base)
 {
@@ -350,3 +387,83 @@ void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
 	writel(map << 16 | irq, gic_data[0].dist_base + GIC_DIST_SOFTINT);
 }
 #endif
+
+#ifdef CONFIG_PM
+/*
+ * Saves the GIC distributor registers during suspend or idle.  Must be called
+ * with interrupts disabled but before powering down the GIC.  After calling
+ * this function, no interrupts will be delivered by the GIC, and another
+ * platform-specific wakeup source must be enabled.
+ */
+void gic_dist_save(unsigned int gic_nr)
+{
+	unsigned int gic_irqs = gic_data[gic_nr].gic_irqs;
+	void __iomem *dist_base = gic_data[gic_nr].dist_base;
+	int i;
+
+	if (gic_nr >= MAX_GIC_NR)
+		BUG();
+
+	_gic_dist_exit(gic_nr);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++)
+		gic_data[gic_nr].saved_conf[i] =
+			readl(dist_base + GIC_DIST_CONFIG + i * 4);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
+		gic_data[gic_nr].saved_pri[i] =
+			readl(dist_base + GIC_DIST_PRI + i * 4);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
+		gic_data[gic_nr].saved_target[i] =
+			readl(dist_base + GIC_DIST_TARGET + i * 4);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
+		gic_data[gic_nr].saved_enable[i] =
+			readl(dist_base + GIC_DIST_ENABLE_SET + i * 4);
+}
+
+/*
+ * Restores the GIC distributor registers during resume or when coming out of
+ * idle.  Must be called before enabling interrupts.  If a level interrupt
+ * that occured while the GIC was suspended is still present, it will be
+ * handled normally, but any edge interrupts that occured will not be seen by
+ * the GIC and need to be handled by the platform-specific wakeup source.
+ */
+void gic_dist_restore(unsigned int gic_nr)
+{
+	unsigned int gic_irqs;
+	unsigned int i;
+	void __iomem *dist_base;
+	void __iomem *cpu_base;
+
+	if (gic_nr >= MAX_GIC_NR)
+		BUG();
+
+	_gic_dist_init(&gic_data[gic_nr]);
+
+	gic_irqs = gic_data[gic_nr].gic_irqs;
+	dist_base = gic_data[gic_nr].dist_base;
+	cpu_base = gic_data[gic_nr].cpu_base;
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++)
+		writel(gic_data[gic_nr].saved_conf[i],
+			dist_base + GIC_DIST_CONFIG + i * 4);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
+		writel(gic_data[gic_nr].saved_pri[i],
+			dist_base + GIC_DIST_PRI + i * 4);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
+		writel(gic_data[gic_nr].saved_target[i],
+			dist_base + GIC_DIST_TARGET + i * 4);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
+		writel(gic_data[gic_nr].saved_enable[i],
+			dist_base + GIC_DIST_ENABLE_SET + i * 4);
+
+	writel(1, dist_base + GIC_DIST_CTRL);
+	writel(0xf0, cpu_base + GIC_CPU_PRIMASK);
+	writel(1, cpu_base + GIC_CPU_CTRL);
+}
+#endif
diff --git a/arch/arm/include/asm/hardware/gic.h b/arch/arm/include/asm/hardware/gic.h
index 84557d3..d0c2ba9 100644
--- a/arch/arm/include/asm/hardware/gic.h
+++ b/arch/arm/include/asm/hardware/gic.h
@@ -37,6 +37,9 @@ extern void __iomem *gic_cpu_base_addr;
 
 void gic_init(unsigned int, unsigned int, void __iomem *, void __iomem *);
 void gic_secondary_init(unsigned int);
+void gic_dist_save(unsigned int gic_nr);
+void gic_dist_restore(unsigned int gic_nr);
+void gic_cpu_exit(unsigned int gic_nr);
 void gic_cascade_irq(unsigned int gic_nr, unsigned int irq);
 void gic_raise_softirq(const struct cpumask *mask, unsigned int irq);
 void gic_enable_ppi(unsigned int);
-- 
1.7.3.1


^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 02/28] ARM: gic: Add functions to save and restore gic state
@ 2011-01-24  2:01   ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-arm-kernel

on systems with idle states which power-gate the logic including
the gic, such as tegra, the gic distributor needs to be shut down
and restored on entry and exit from the architecture idle code

Original-author: Gary King <gking@nvidia.com>
Signed-off-by: Gary King <gking@nvidia.com>
Signed-off-by: Colin Cross <ccross@android.com>
Acked-by: Linus Walleij <linus.walleij@stericsson.com>
---
v2: Updated on top of changes to gic initialization

This patch depends on patch 1: 
ARM: tegra: irq: Rename gic pointers to avoid conflicts

 arch/arm/common/gic.c               |  127 +++++++++++++++++++++++++++++++++--
 arch/arm/include/asm/hardware/gic.h |    3 +
 2 files changed, 125 insertions(+), 5 deletions(-)

diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c
index 2243772..a6f8c58 100644
--- a/arch/arm/common/gic.c
+++ b/arch/arm/common/gic.c
@@ -42,6 +42,13 @@ struct gic_chip_data {
 	unsigned int irq_offset;
 	void __iomem *dist_base;
 	void __iomem *cpu_base;
+#ifdef CONFIG_PM
+	u32 saved_enable[DIV_ROUND_UP(1020, 32)];
+	u32 saved_conf[DIV_ROUND_UP(1020, 16)];
+	u32 saved_pri[DIV_ROUND_UP(1020, 4)];
+	u32 saved_target[DIV_ROUND_UP(1020, 4)];
+#endif
+	unsigned int gic_irqs;
 };
 
 #ifndef MAX_GIC_NR
@@ -216,10 +223,9 @@ void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq)
 	set_irq_chained_handler(irq, gic_handle_cascade_irq);
 }
 
-static void __init gic_dist_init(struct gic_chip_data *gic,
-	unsigned int irq_start)
+static unsigned int _gic_dist_init(struct gic_chip_data *gic)
 {
-	unsigned int gic_irqs, irq_limit, i;
+	unsigned int gic_irqs, i;
 	void __iomem *base = gic->dist_base;
 	u32 cpumask = 1 << smp_processor_id();
 
@@ -262,10 +268,25 @@ static void __init gic_dist_init(struct gic_chip_data *gic,
 	for (i = 32; i < gic_irqs; i += 32)
 		writel(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i * 4 / 32);
 
+	return gic_irqs;
+}
+
+static void _gic_dist_exit(unsigned int gic_nr)
+{
+	writel(0, gic_data[gic_nr].dist_base + GIC_DIST_CTRL);
+}
+
+void __init gic_dist_init(struct gic_chip_data *gic, unsigned int irq_start)
+{
+	unsigned int irq_limit;
+	unsigned int i;
+
+	gic->gic_irqs = _gic_dist_init(gic);
+
 	/*
 	 * Limit number of interrupts registered to the platform maximum
 	 */
-	irq_limit = gic->irq_offset + gic_irqs;
+	irq_limit = gic->irq_offset + gic->gic_irqs;
 	if (WARN_ON(irq_limit > NR_IRQS))
 		irq_limit = NR_IRQS;
 
@@ -279,7 +300,15 @@ static void __init gic_dist_init(struct gic_chip_data *gic,
 		set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
 	}
 
-	writel(1, base + GIC_DIST_CTRL);
+	writel(1, gic->dist_base + GIC_DIST_CTRL);
+}
+
+void gic_dist_exit(unsigned int gic_nr)
+{
+	if (gic_nr >= MAX_GIC_NR)
+		BUG();
+
+	_gic_dist_exit(gic_nr);
 }
 
 static void __cpuinit gic_cpu_init(struct gic_chip_data *gic)
@@ -305,6 +334,14 @@ static void __cpuinit gic_cpu_init(struct gic_chip_data *gic)
 	writel(1, base + GIC_CPU_CTRL);
 }
 
+void gic_cpu_exit(unsigned int gic_nr)
+{
+	if (gic_nr >= MAX_GIC_NR)
+		BUG();
+
+	writel(0, gic_data[gic_nr].cpu_base + GIC_CPU_CTRL);
+}
+
 void __init gic_init(unsigned int gic_nr, unsigned int irq_start,
 	void __iomem *dist_base, void __iomem *cpu_base)
 {
@@ -350,3 +387,83 @@ void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
 	writel(map << 16 | irq, gic_data[0].dist_base + GIC_DIST_SOFTINT);
 }
 #endif
+
+#ifdef CONFIG_PM
+/*
+ * Saves the GIC distributor registers during suspend or idle.  Must be called
+ * with interrupts disabled but before powering down the GIC.  After calling
+ * this function, no interrupts will be delivered by the GIC, and another
+ * platform-specific wakeup source must be enabled.
+ */
+void gic_dist_save(unsigned int gic_nr)
+{
+	unsigned int gic_irqs = gic_data[gic_nr].gic_irqs;
+	void __iomem *dist_base = gic_data[gic_nr].dist_base;
+	int i;
+
+	if (gic_nr >= MAX_GIC_NR)
+		BUG();
+
+	_gic_dist_exit(gic_nr);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++)
+		gic_data[gic_nr].saved_conf[i] =
+			readl(dist_base + GIC_DIST_CONFIG + i * 4);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
+		gic_data[gic_nr].saved_pri[i] =
+			readl(dist_base + GIC_DIST_PRI + i * 4);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
+		gic_data[gic_nr].saved_target[i] =
+			readl(dist_base + GIC_DIST_TARGET + i * 4);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
+		gic_data[gic_nr].saved_enable[i] =
+			readl(dist_base + GIC_DIST_ENABLE_SET + i * 4);
+}
+
+/*
+ * Restores the GIC distributor registers during resume or when coming out of
+ * idle.  Must be called before enabling interrupts.  If a level interrupt
+ * that occured while the GIC was suspended is still present, it will be
+ * handled normally, but any edge interrupts that occured will not be seen by
+ * the GIC and need to be handled by the platform-specific wakeup source.
+ */
+void gic_dist_restore(unsigned int gic_nr)
+{
+	unsigned int gic_irqs;
+	unsigned int i;
+	void __iomem *dist_base;
+	void __iomem *cpu_base;
+
+	if (gic_nr >= MAX_GIC_NR)
+		BUG();
+
+	_gic_dist_init(&gic_data[gic_nr]);
+
+	gic_irqs = gic_data[gic_nr].gic_irqs;
+	dist_base = gic_data[gic_nr].dist_base;
+	cpu_base = gic_data[gic_nr].cpu_base;
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++)
+		writel(gic_data[gic_nr].saved_conf[i],
+			dist_base + GIC_DIST_CONFIG + i * 4);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
+		writel(gic_data[gic_nr].saved_pri[i],
+			dist_base + GIC_DIST_PRI + i * 4);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
+		writel(gic_data[gic_nr].saved_target[i],
+			dist_base + GIC_DIST_TARGET + i * 4);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
+		writel(gic_data[gic_nr].saved_enable[i],
+			dist_base + GIC_DIST_ENABLE_SET + i * 4);
+
+	writel(1, dist_base + GIC_DIST_CTRL);
+	writel(0xf0, cpu_base + GIC_CPU_PRIMASK);
+	writel(1, cpu_base + GIC_CPU_CTRL);
+}
+#endif
diff --git a/arch/arm/include/asm/hardware/gic.h b/arch/arm/include/asm/hardware/gic.h
index 84557d3..d0c2ba9 100644
--- a/arch/arm/include/asm/hardware/gic.h
+++ b/arch/arm/include/asm/hardware/gic.h
@@ -37,6 +37,9 @@ extern void __iomem *gic_cpu_base_addr;
 
 void gic_init(unsigned int, unsigned int, void __iomem *, void __iomem *);
 void gic_secondary_init(unsigned int);
+void gic_dist_save(unsigned int gic_nr);
+void gic_dist_restore(unsigned int gic_nr);
+void gic_cpu_exit(unsigned int gic_nr);
 void gic_cascade_irq(unsigned int gic_nr, unsigned int irq);
 void gic_raise_softirq(const struct cpumask *mask, unsigned int irq);
 void gic_enable_ppi(unsigned int);
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 03/28] ARM: gic: Export irq chip functions
  2011-01-24  2:01 [PATCH v2 00/28] Updates for Tegra support in 2.6.39 Colin Cross
@ 2011-01-24  2:01   ` Colin Cross
  2011-01-24  2:01   ` Colin Cross
                     ` (26 subsequent siblings)
  27 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-tegra
  Cc: linux-arm-kernel, olof, konkers, Colin Cross, Russell King,
	Catalin Marinas, Abhijeet Dharmapurikar, Linus Walleij,
	Gary King, linux-kernel

Some systems combine the GIC with an external interrupt controller.
On these systems it may be necessary to update both the GIC registers
and the external controller's registers to control IRQ behavior.
Export the irq chip functions so that these systems can define a
custom irq chip that calls into the GIC handlers.

Signed-off-by: Colin Cross <ccross@android.com>
---
v2: Updated on top of irq_data changes

 arch/arm/common/gic.c               |   11 +++++------
 arch/arm/include/asm/hardware/gic.h |   10 ++++++++++
 2 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c
index a6f8c58..c11f478 100644
--- a/arch/arm/common/gic.c
+++ b/arch/arm/common/gic.c
@@ -78,14 +78,14 @@ static inline unsigned int gic_irq(struct irq_data *d)
 /*
  * Routines to acknowledge, disable and enable interrupts
  */
-static void gic_ack_irq(struct irq_data *d)
+void gic_ack_irq(struct irq_data *d)
 {
 	spin_lock(&irq_controller_lock);
 	writel(gic_irq(d), gic_cpu_base(d) + GIC_CPU_EOI);
 	spin_unlock(&irq_controller_lock);
 }
 
-static void gic_mask_irq(struct irq_data *d)
+void gic_mask_irq(struct irq_data *d)
 {
 	u32 mask = 1 << (d->irq % 32);
 
@@ -94,7 +94,7 @@ static void gic_mask_irq(struct irq_data *d)
 	spin_unlock(&irq_controller_lock);
 }
 
-static void gic_unmask_irq(struct irq_data *d)
+void gic_unmask_irq(struct irq_data *d)
 {
 	u32 mask = 1 << (d->irq % 32);
 
@@ -103,7 +103,7 @@ static void gic_unmask_irq(struct irq_data *d)
 	spin_unlock(&irq_controller_lock);
 }
 
-static int gic_set_type(struct irq_data *d, unsigned int type)
+int gic_set_type(struct irq_data *d, unsigned int type)
 {
 	void __iomem *base = gic_dist_base(d);
 	unsigned int gicirq = gic_irq(d);
@@ -149,8 +149,7 @@ static int gic_set_type(struct irq_data *d, unsigned int type)
 }
 
 #ifdef CONFIG_SMP
-static int
-gic_set_cpu(struct irq_data *d, const struct cpumask *mask_val, bool force)
+int gic_set_cpu(struct irq_data *d, const struct cpumask *mask_val, bool force)
 {
 	void __iomem *reg = gic_dist_base(d) + GIC_DIST_TARGET + (gic_irq(d) & ~3);
 	unsigned int shift = (d->irq % 4) * 8;
diff --git a/arch/arm/include/asm/hardware/gic.h b/arch/arm/include/asm/hardware/gic.h
index d0c2ba9..2ab5cf0 100644
--- a/arch/arm/include/asm/hardware/gic.h
+++ b/arch/arm/include/asm/hardware/gic.h
@@ -33,6 +33,8 @@
 #define GIC_DIST_SOFTINT		0xf00
 
 #ifndef __ASSEMBLY__
+struct irq_data;
+
 extern void __iomem *gic_cpu_base_addr;
 
 void gic_init(unsigned int, unsigned int, void __iomem *, void __iomem *);
@@ -43,6 +45,14 @@ void gic_cpu_exit(unsigned int gic_nr);
 void gic_cascade_irq(unsigned int gic_nr, unsigned int irq);
 void gic_raise_softirq(const struct cpumask *mask, unsigned int irq);
 void gic_enable_ppi(unsigned int);
+
+void gic_ack_irq(struct irq_data *d);
+void gic_mask_irq(struct irq_data *d);
+void gic_unmask_irq(struct irq_data *d);
+int gic_set_type(struct irq_data *d, unsigned int type);
+#ifdef CONFIG_SMP
+int gic_set_cpu(struct irq_data *d, const struct cpumask *mask_val, bool force);
+#endif
 #endif
 
 #endif
-- 
1.7.3.1


^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 03/28] ARM: gic: Export irq chip functions
@ 2011-01-24  2:01   ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-arm-kernel

Some systems combine the GIC with an external interrupt controller.
On these systems it may be necessary to update both the GIC registers
and the external controller's registers to control IRQ behavior.
Export the irq chip functions so that these systems can define a
custom irq chip that calls into the GIC handlers.

Signed-off-by: Colin Cross <ccross@android.com>
---
v2: Updated on top of irq_data changes

 arch/arm/common/gic.c               |   11 +++++------
 arch/arm/include/asm/hardware/gic.h |   10 ++++++++++
 2 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c
index a6f8c58..c11f478 100644
--- a/arch/arm/common/gic.c
+++ b/arch/arm/common/gic.c
@@ -78,14 +78,14 @@ static inline unsigned int gic_irq(struct irq_data *d)
 /*
  * Routines to acknowledge, disable and enable interrupts
  */
-static void gic_ack_irq(struct irq_data *d)
+void gic_ack_irq(struct irq_data *d)
 {
 	spin_lock(&irq_controller_lock);
 	writel(gic_irq(d), gic_cpu_base(d) + GIC_CPU_EOI);
 	spin_unlock(&irq_controller_lock);
 }
 
-static void gic_mask_irq(struct irq_data *d)
+void gic_mask_irq(struct irq_data *d)
 {
 	u32 mask = 1 << (d->irq % 32);
 
@@ -94,7 +94,7 @@ static void gic_mask_irq(struct irq_data *d)
 	spin_unlock(&irq_controller_lock);
 }
 
-static void gic_unmask_irq(struct irq_data *d)
+void gic_unmask_irq(struct irq_data *d)
 {
 	u32 mask = 1 << (d->irq % 32);
 
@@ -103,7 +103,7 @@ static void gic_unmask_irq(struct irq_data *d)
 	spin_unlock(&irq_controller_lock);
 }
 
-static int gic_set_type(struct irq_data *d, unsigned int type)
+int gic_set_type(struct irq_data *d, unsigned int type)
 {
 	void __iomem *base = gic_dist_base(d);
 	unsigned int gicirq = gic_irq(d);
@@ -149,8 +149,7 @@ static int gic_set_type(struct irq_data *d, unsigned int type)
 }
 
 #ifdef CONFIG_SMP
-static int
-gic_set_cpu(struct irq_data *d, const struct cpumask *mask_val, bool force)
+int gic_set_cpu(struct irq_data *d, const struct cpumask *mask_val, bool force)
 {
 	void __iomem *reg = gic_dist_base(d) + GIC_DIST_TARGET + (gic_irq(d) & ~3);
 	unsigned int shift = (d->irq % 4) * 8;
diff --git a/arch/arm/include/asm/hardware/gic.h b/arch/arm/include/asm/hardware/gic.h
index d0c2ba9..2ab5cf0 100644
--- a/arch/arm/include/asm/hardware/gic.h
+++ b/arch/arm/include/asm/hardware/gic.h
@@ -33,6 +33,8 @@
 #define GIC_DIST_SOFTINT		0xf00
 
 #ifndef __ASSEMBLY__
+struct irq_data;
+
 extern void __iomem *gic_cpu_base_addr;
 
 void gic_init(unsigned int, unsigned int, void __iomem *, void __iomem *);
@@ -43,6 +45,14 @@ void gic_cpu_exit(unsigned int gic_nr);
 void gic_cascade_irq(unsigned int gic_nr, unsigned int irq);
 void gic_raise_softirq(const struct cpumask *mask, unsigned int irq);
 void gic_enable_ppi(unsigned int);
+
+void gic_ack_irq(struct irq_data *d);
+void gic_mask_irq(struct irq_data *d);
+void gic_unmask_irq(struct irq_data *d);
+int gic_set_type(struct irq_data *d, unsigned int type);
+#ifdef CONFIG_SMP
+int gic_set_cpu(struct irq_data *d, const struct cpumask *mask_val, bool force);
+#endif
 #endif
 
 #endif
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support for re-enabling l2x0
  2011-01-24  2:01 [PATCH v2 00/28] Updates for Tegra support in 2.6.39 Colin Cross
@ 2011-01-24  2:01   ` Colin Cross
  2011-01-24  2:01   ` Colin Cross
                     ` (26 subsequent siblings)
  27 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-tegra
  Cc: linux-arm-kernel, olof, konkers, Colin Cross, Russell King,
	Catalin Marinas, Santosh Shilimkar, Linus Walleij, Tony Lindgren,
	linux-kernel

Remove __init annotation from l2x0_init so it can be used to
reinitialize the l2x0 after it has been reset during suspend.

Add l2x0_enable to re-enable the l2x0 after l2x0_disable if
the l2x0 was not reset.

l2x0_disable cannot use writel, as writel calls wmb(), and wmb()
may call outer_cache_sync, which takes the same spinlock as
l2x0_disable.

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/include/asm/hardware/cache-l2x0.h |    3 ++-
 arch/arm/mm/cache-l2x0.c                   |   14 ++++++++++++--
 2 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/arch/arm/include/asm/hardware/cache-l2x0.h b/arch/arm/include/asm/hardware/cache-l2x0.h
index 5aeec1e..2b4479f 100644
--- a/arch/arm/include/asm/hardware/cache-l2x0.h
+++ b/arch/arm/include/asm/hardware/cache-l2x0.h
@@ -72,7 +72,8 @@
 #define L2X0_AUX_CTRL_EARLY_BRESP_SHIFT		30
 
 #ifndef __ASSEMBLY__
-extern void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask);
+extern void l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask);
+extern void l2x0_enable(void);
 #endif
 
 #endif
diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c
index 170c9bb..e525449 100644
--- a/arch/arm/mm/cache-l2x0.c
+++ b/arch/arm/mm/cache-l2x0.c
@@ -252,16 +252,26 @@ static void l2x0_flush_range(unsigned long start, unsigned long end)
 	spin_unlock_irqrestore(&l2x0_lock, flags);
 }
 
+/* enables l2x0 after l2x0_disable, does not invalidate */
+void l2x0_enable(void)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&l2x0_lock, flags);
+	writel_relaxed(1, l2x0_base + L2X0_CTRL);
+	spin_unlock_irqrestore(&l2x0_lock, flags);
+}
+
 static void l2x0_disable(void)
 {
 	unsigned long flags;
 
 	spin_lock_irqsave(&l2x0_lock, flags);
-	writel(0, l2x0_base + L2X0_CTRL);
+	writel_relaxed(0, l2x0_base + L2X0_CTRL);
 	spin_unlock_irqrestore(&l2x0_lock, flags);
 }
 
-void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask)
+void l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask)
 {
 	__u32 aux;
 	__u32 cache_id;
-- 
1.7.3.1


^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support for re-enabling l2x0
@ 2011-01-24  2:01   ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-arm-kernel

Remove __init annotation from l2x0_init so it can be used to
reinitialize the l2x0 after it has been reset during suspend.

Add l2x0_enable to re-enable the l2x0 after l2x0_disable if
the l2x0 was not reset.

l2x0_disable cannot use writel, as writel calls wmb(), and wmb()
may call outer_cache_sync, which takes the same spinlock as
l2x0_disable.

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/include/asm/hardware/cache-l2x0.h |    3 ++-
 arch/arm/mm/cache-l2x0.c                   |   14 ++++++++++++--
 2 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/arch/arm/include/asm/hardware/cache-l2x0.h b/arch/arm/include/asm/hardware/cache-l2x0.h
index 5aeec1e..2b4479f 100644
--- a/arch/arm/include/asm/hardware/cache-l2x0.h
+++ b/arch/arm/include/asm/hardware/cache-l2x0.h
@@ -72,7 +72,8 @@
 #define L2X0_AUX_CTRL_EARLY_BRESP_SHIFT		30
 
 #ifndef __ASSEMBLY__
-extern void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask);
+extern void l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask);
+extern void l2x0_enable(void);
 #endif
 
 #endif
diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c
index 170c9bb..e525449 100644
--- a/arch/arm/mm/cache-l2x0.c
+++ b/arch/arm/mm/cache-l2x0.c
@@ -252,16 +252,26 @@ static void l2x0_flush_range(unsigned long start, unsigned long end)
 	spin_unlock_irqrestore(&l2x0_lock, flags);
 }
 
+/* enables l2x0 after l2x0_disable, does not invalidate */
+void l2x0_enable(void)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&l2x0_lock, flags);
+	writel_relaxed(1, l2x0_base + L2X0_CTRL);
+	spin_unlock_irqrestore(&l2x0_lock, flags);
+}
+
 static void l2x0_disable(void)
 {
 	unsigned long flags;
 
 	spin_lock_irqsave(&l2x0_lock, flags);
-	writel(0, l2x0_base + L2X0_CTRL);
+	writel_relaxed(0, l2x0_base + L2X0_CTRL);
 	spin_unlock_irqrestore(&l2x0_lock, flags);
 }
 
-void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask)
+void l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask)
 {
 	__u32 aux;
 	__u32 cache_id;
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 05/28] ARM: tegra: Centralize macros to define debug uart base
  2011-01-24  2:01 [PATCH v2 00/28] Updates for Tegra support in 2.6.39 Colin Cross
@ 2011-01-24  2:01   ` Colin Cross
  2011-01-24  2:01   ` Colin Cross
                     ` (26 subsequent siblings)
  27 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-tegra
  Cc: linux-arm-kernel, olof, konkers, Colin Cross, Russell King,
	Tony Lindgren, Lorenzo Pieralisi, Jeremy Kerr, Gary King,
	linux-kernel

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/include/mach/debug-macro.S |   25 ++++-------------------
 arch/arm/mach-tegra/include/mach/iomap.h       |   14 +++++++++++++
 arch/arm/mach-tegra/include/mach/uncompress.h  |   18 +---------------
 3 files changed, 21 insertions(+), 36 deletions(-)

diff --git a/arch/arm/mach-tegra/include/mach/debug-macro.S b/arch/arm/mach-tegra/include/mach/debug-macro.S
index a0e7c12..e0ebe65 100644
--- a/arch/arm/mach-tegra/include/mach/debug-macro.S
+++ b/arch/arm/mach-tegra/include/mach/debug-macro.S
@@ -19,30 +19,15 @@
  */
 
 #include <mach/io.h>
+#include <mach/iomap.h>
 
 	.macro  addruart, rp, rv
         ldr     \rp, =IO_APB_PHYS       @ physical
         ldr     \rv, =IO_APB_VIRT        @ virtual
-#if defined(CONFIG_TEGRA_DEBUG_UART_NONE)
-#error "A debug UART must be selected in the kernel config to use DEBUG_LL"
-#elif defined(CONFIG_TEGRA_DEBUG_UARTA)
-        orr     \rp, \rp, #0x6000
-        orr     \rv, \rv, #0x6000
-#elif defined(CONFIG_TEGRA_DEBUG_UARTB)
-        orr     \rp, \rp, #0x6000
-	orr	\rp, \rp, #0x40
-        orr     \rv, \rv, #0x6000
-	orr	\rv, \rv, #0x40
-#elif defined(CONFIG_TEGRA_DEBUG_UARTC)
-        orr     \rp, \rp, #0x6200
-        orr     \rv, \rv, #0x6200
-#elif defined(CONFIG_TEGRA_DEBUG_UARTD)
-        orr     \rp, \rp, #0x6300
-        orr     \rv, \rv, #0x6300
-#elif defined(CONFIG_TEGRA_DEBUG_UARTE)
-        orr     \rp, \rp, #0x6400
-        orr     \rv, \rv, #0x6400
-#endif
+	orr	\rp, \rp, #(TEGRA_DEBUG_UART_BASE & 0xFF)
+	orr	\rp, \rp, #(TEGRA_DEBUG_UART_BASE & 0xFF00)
+	orr	\rv, \rv, #(TEGRA_DEBUG_UART_BASE & 0xFF)
+	orr	\rv, \rv, #(TEGRA_DEBUG_UART_BASE & 0xFF00)
 	.endm
 
 #define UART_SHIFT	2
diff --git a/arch/arm/mach-tegra/include/mach/iomap.h b/arch/arm/mach-tegra/include/mach/iomap.h
index 44a4f4b..325eca3 100644
--- a/arch/arm/mach-tegra/include/mach/iomap.h
+++ b/arch/arm/mach-tegra/include/mach/iomap.h
@@ -221,4 +221,18 @@
 #define TEGRA_SDMMC4_BASE		0xC8000600
 #define TEGRA_SDMMC4_SIZE		SZ_512
 
+#if defined(CONFIG_TEGRA_DEBUG_UART_NONE)
+# define TEGRA_DEBUG_UART_BASE 0
+#elif defined(CONFIG_TEGRA_DEBUG_UARTA)
+# define TEGRA_DEBUG_UART_BASE TEGRA_UARTA_BASE
+#elif defined(CONFIG_TEGRA_DEBUG_UARTB)
+# define TEGRA_DEBUG_UART_BASE TEGRA_UARTB_BASE
+#elif defined(CONFIG_TEGRA_DEBUG_UARTC)
+# define TEGRA_DEBUG_UART_BASE TEGRA_UARTC_BASE
+#elif defined(CONFIG_TEGRA_DEBUG_UARTD)
+# define TEGRA_DEBUG_UART_BASE TEGRA_UARTD_BASE
+#elif defined(CONFIG_TEGRA_DEBUG_UARTE)
+# define TEGRA_DEBUG_UART_BASE TEGRA_UARTE_BASE
+#endif
+
 #endif
diff --git a/arch/arm/mach-tegra/include/mach/uncompress.h b/arch/arm/mach-tegra/include/mach/uncompress.h
index 6c4dd81..4e83237 100644
--- a/arch/arm/mach-tegra/include/mach/uncompress.h
+++ b/arch/arm/mach-tegra/include/mach/uncompress.h
@@ -26,23 +26,9 @@
 
 #include <mach/iomap.h>
 
-#if defined(CONFIG_TEGRA_DEBUG_UARTA)
-#define DEBUG_UART_BASE TEGRA_UARTA_BASE
-#elif defined(CONFIG_TEGRA_DEBUG_UARTB)
-#define DEBUG_UART_BASE TEGRA_UARTB_BASE
-#elif defined(CONFIG_TEGRA_DEBUG_UARTC)
-#define DEBUG_UART_BASE TEGRA_UARTC_BASE
-#elif defined(CONFIG_TEGRA_DEBUG_UARTD)
-#define DEBUG_UART_BASE TEGRA_UARTD_BASE
-#elif defined(CONFIG_TEGRA_DEBUG_UARTE)
-#define DEBUG_UART_BASE TEGRA_UARTE_BASE
-#else
-#define DEBUG_UART_BASE NULL
-#endif
-
 static void putc(int c)
 {
-	volatile u8 *uart = (volatile u8 *)DEBUG_UART_BASE;
+	volatile u8 *uart = (volatile u8 *)TEGRA_DEBUG_UART_BASE;
 	int shift = 2;
 
 	if (uart == NULL)
@@ -59,7 +45,7 @@ static inline void flush(void)
 
 static inline void arch_decomp_setup(void)
 {
-	volatile u8 *uart = (volatile u8 *)DEBUG_UART_BASE;
+	volatile u8 *uart = (volatile u8 *)TEGRA_DEBUG_UART_BASE;
 	int shift = 2;
 
 	if (uart == NULL)
-- 
1.7.3.1


^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 05/28] ARM: tegra: Centralize macros to define debug uart base
@ 2011-01-24  2:01   ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/include/mach/debug-macro.S |   25 ++++-------------------
 arch/arm/mach-tegra/include/mach/iomap.h       |   14 +++++++++++++
 arch/arm/mach-tegra/include/mach/uncompress.h  |   18 +---------------
 3 files changed, 21 insertions(+), 36 deletions(-)

diff --git a/arch/arm/mach-tegra/include/mach/debug-macro.S b/arch/arm/mach-tegra/include/mach/debug-macro.S
index a0e7c12..e0ebe65 100644
--- a/arch/arm/mach-tegra/include/mach/debug-macro.S
+++ b/arch/arm/mach-tegra/include/mach/debug-macro.S
@@ -19,30 +19,15 @@
  */
 
 #include <mach/io.h>
+#include <mach/iomap.h>
 
 	.macro  addruart, rp, rv
         ldr     \rp, =IO_APB_PHYS       @ physical
         ldr     \rv, =IO_APB_VIRT        @ virtual
-#if defined(CONFIG_TEGRA_DEBUG_UART_NONE)
-#error "A debug UART must be selected in the kernel config to use DEBUG_LL"
-#elif defined(CONFIG_TEGRA_DEBUG_UARTA)
-        orr     \rp, \rp, #0x6000
-        orr     \rv, \rv, #0x6000
-#elif defined(CONFIG_TEGRA_DEBUG_UARTB)
-        orr     \rp, \rp, #0x6000
-	orr	\rp, \rp, #0x40
-        orr     \rv, \rv, #0x6000
-	orr	\rv, \rv, #0x40
-#elif defined(CONFIG_TEGRA_DEBUG_UARTC)
-        orr     \rp, \rp, #0x6200
-        orr     \rv, \rv, #0x6200
-#elif defined(CONFIG_TEGRA_DEBUG_UARTD)
-        orr     \rp, \rp, #0x6300
-        orr     \rv, \rv, #0x6300
-#elif defined(CONFIG_TEGRA_DEBUG_UARTE)
-        orr     \rp, \rp, #0x6400
-        orr     \rv, \rv, #0x6400
-#endif
+	orr	\rp, \rp, #(TEGRA_DEBUG_UART_BASE & 0xFF)
+	orr	\rp, \rp, #(TEGRA_DEBUG_UART_BASE & 0xFF00)
+	orr	\rv, \rv, #(TEGRA_DEBUG_UART_BASE & 0xFF)
+	orr	\rv, \rv, #(TEGRA_DEBUG_UART_BASE & 0xFF00)
 	.endm
 
 #define UART_SHIFT	2
diff --git a/arch/arm/mach-tegra/include/mach/iomap.h b/arch/arm/mach-tegra/include/mach/iomap.h
index 44a4f4b..325eca3 100644
--- a/arch/arm/mach-tegra/include/mach/iomap.h
+++ b/arch/arm/mach-tegra/include/mach/iomap.h
@@ -221,4 +221,18 @@
 #define TEGRA_SDMMC4_BASE		0xC8000600
 #define TEGRA_SDMMC4_SIZE		SZ_512
 
+#if defined(CONFIG_TEGRA_DEBUG_UART_NONE)
+# define TEGRA_DEBUG_UART_BASE 0
+#elif defined(CONFIG_TEGRA_DEBUG_UARTA)
+# define TEGRA_DEBUG_UART_BASE TEGRA_UARTA_BASE
+#elif defined(CONFIG_TEGRA_DEBUG_UARTB)
+# define TEGRA_DEBUG_UART_BASE TEGRA_UARTB_BASE
+#elif defined(CONFIG_TEGRA_DEBUG_UARTC)
+# define TEGRA_DEBUG_UART_BASE TEGRA_UARTC_BASE
+#elif defined(CONFIG_TEGRA_DEBUG_UARTD)
+# define TEGRA_DEBUG_UART_BASE TEGRA_UARTD_BASE
+#elif defined(CONFIG_TEGRA_DEBUG_UARTE)
+# define TEGRA_DEBUG_UART_BASE TEGRA_UARTE_BASE
+#endif
+
 #endif
diff --git a/arch/arm/mach-tegra/include/mach/uncompress.h b/arch/arm/mach-tegra/include/mach/uncompress.h
index 6c4dd81..4e83237 100644
--- a/arch/arm/mach-tegra/include/mach/uncompress.h
+++ b/arch/arm/mach-tegra/include/mach/uncompress.h
@@ -26,23 +26,9 @@
 
 #include <mach/iomap.h>
 
-#if defined(CONFIG_TEGRA_DEBUG_UARTA)
-#define DEBUG_UART_BASE TEGRA_UARTA_BASE
-#elif defined(CONFIG_TEGRA_DEBUG_UARTB)
-#define DEBUG_UART_BASE TEGRA_UARTB_BASE
-#elif defined(CONFIG_TEGRA_DEBUG_UARTC)
-#define DEBUG_UART_BASE TEGRA_UARTC_BASE
-#elif defined(CONFIG_TEGRA_DEBUG_UARTD)
-#define DEBUG_UART_BASE TEGRA_UARTD_BASE
-#elif defined(CONFIG_TEGRA_DEBUG_UARTE)
-#define DEBUG_UART_BASE TEGRA_UARTE_BASE
-#else
-#define DEBUG_UART_BASE NULL
-#endif
-
 static void putc(int c)
 {
-	volatile u8 *uart = (volatile u8 *)DEBUG_UART_BASE;
+	volatile u8 *uart = (volatile u8 *)TEGRA_DEBUG_UART_BASE;
 	int shift = 2;
 
 	if (uart == NULL)
@@ -59,7 +45,7 @@ static inline void flush(void)
 
 static inline void arch_decomp_setup(void)
 {
-	volatile u8 *uart = (volatile u8 *)DEBUG_UART_BASE;
+	volatile u8 *uart = (volatile u8 *)TEGRA_DEBUG_UART_BASE;
 	int shift = 2;
 
 	if (uart == NULL)
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 06/28] ARM: tegra: Add api to control internal powergating
  2011-01-24  2:01 [PATCH v2 00/28] Updates for Tegra support in 2.6.39 Colin Cross
@ 2011-01-24  2:01   ` Colin Cross
  2011-01-24  2:01   ` Colin Cross
                     ` (26 subsequent siblings)
  27 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-tegra
  Cc: linux-arm-kernel, olof, konkers, Colin Cross, Russell King, linux-kernel

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/Makefile                 |    1 +
 arch/arm/mach-tegra/include/mach/powergate.h |   40 +++++
 arch/arm/mach-tegra/powergate.c              |  212 ++++++++++++++++++++++++++
 3 files changed, 253 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-tegra/include/mach/powergate.h
 create mode 100644 arch/arm/mach-tegra/powergate.c

diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index cdbc68e..00a6ba3 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -5,6 +5,7 @@ obj-y                                   += clock.o
 obj-y                                   += timer.o
 obj-y                                   += gpio.o
 obj-y                                   += pinmux.o
+obj-y                                   += powergate.o
 obj-y					+= fuse.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += clock.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += tegra2_clocks.o
diff --git a/arch/arm/mach-tegra/include/mach/powergate.h b/arch/arm/mach-tegra/include/mach/powergate.h
new file mode 100644
index 0000000..401d1b7
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/powergate.h
@@ -0,0 +1,40 @@
+/*
+ * drivers/regulator/tegra-regulator.c
+ *
+ * Copyright (c) 2010 Google, Inc
+ *
+ * Author:
+ *	Colin Cross <ccross@google.com>
+ *
+ * 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 _MACH_TEGRA_POWERGATE_H_
+#define _MACH_TEGRA_POWERGATE_H_
+
+#define TEGRA_POWERGATE_CPU	0
+#define TEGRA_POWERGATE_3D	1
+#define TEGRA_POWERGATE_VENC	2
+#define TEGRA_POWERGATE_PCIE	3
+#define TEGRA_POWERGATE_VDEC	4
+#define TEGRA_POWERGATE_L2	5
+#define TEGRA_POWERGATE_MPE	6
+#define TEGRA_NUM_POWERGATE	7
+
+int tegra_powergate_power_on(int id);
+int tegra_powergate_power_off(int id);
+bool tegra_powergate_is_powered(int id);
+int tegra_powergate_remove_clamping(int id);
+
+/* Must be called with clk disabled, and returns with clk enabled */
+int tegra_powergate_sequence_power_up(int id, struct clk *clk);
+
+#endif /* _MACH_TEGRA_POWERGATE_H_ */
diff --git a/arch/arm/mach-tegra/powergate.c b/arch/arm/mach-tegra/powergate.c
new file mode 100644
index 0000000..3cee9aa
--- /dev/null
+++ b/arch/arm/mach-tegra/powergate.c
@@ -0,0 +1,212 @@
+/*
+ * drivers/powergate/tegra-powergate.c
+ *
+ * Copyright (c) 2010 Google, Inc
+ *
+ * Author:
+ *	Colin Cross <ccross@google.com>
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/seq_file.h>
+#include <linux/spinlock.h>
+
+#include <mach/clk.h>
+#include <mach/iomap.h>
+#include <mach/powergate.h>
+
+#define PWRGATE_TOGGLE		0x30
+#define  PWRGATE_TOGGLE_START	(1 << 8)
+
+#define REMOVE_CLAMPING		0x34
+
+#define PWRGATE_STATUS		0x38
+
+static DEFINE_SPINLOCK(tegra_powergate_lock);
+
+static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
+
+static u32 pmc_read(unsigned long reg)
+{
+	return readl(pmc + reg);
+}
+
+static void pmc_write(u32 val, unsigned long reg)
+{
+	writel(val, pmc + reg);
+}
+
+static int tegra_powergate_set(int id, bool new_state)
+{
+	bool status;
+	unsigned long flags;
+
+	spin_lock_irqsave(&tegra_powergate_lock, flags);
+
+	status = pmc_read(PWRGATE_STATUS) & (1 << id);
+
+	if (status == new_state) {
+		spin_unlock_irqrestore(&tegra_powergate_lock, flags);
+		return -EINVAL;
+	}
+
+	pmc_write(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE);
+
+	spin_unlock_irqrestore(&tegra_powergate_lock, flags);
+
+	return 0;
+}
+
+int tegra_powergate_power_on(int id)
+{
+	if (id < 0 || id >= TEGRA_NUM_POWERGATE)
+		return -EINVAL;
+
+	return tegra_powergate_set(id, true);
+}
+
+int tegra_powergate_power_off(int id)
+{
+	if (id < 0 || id >= TEGRA_NUM_POWERGATE)
+		return -EINVAL;
+
+	return tegra_powergate_set(id, false);
+}
+
+bool tegra_powergate_is_powered(int id)
+{
+	u32 status;
+
+	if (id < 0 || id >= TEGRA_NUM_POWERGATE)
+		return -EINVAL;
+
+	status = pmc_read(PWRGATE_STATUS) & (1 << id);
+	return !!status;
+}
+
+int tegra_powergate_remove_clamping(int id)
+{
+	u32 mask;
+
+	if (id < 0 || id >= TEGRA_NUM_POWERGATE)
+		return -EINVAL;
+
+	/*
+	 * Tegra 2 has a bug where PCIE and VDE clamping masks are
+	 * swapped relatively to the partition ids
+	 */
+	if (id ==  TEGRA_POWERGATE_VDEC)
+		mask = (1 << TEGRA_POWERGATE_PCIE);
+	else if	(id == TEGRA_POWERGATE_PCIE)
+		mask = (1 << TEGRA_POWERGATE_VDEC);
+	else
+		mask = (1 << id);
+
+	pmc_write(mask, REMOVE_CLAMPING);
+
+	return 0;
+}
+
+/* Must be called with clk disabled, and returns with clk enabled */
+int tegra_powergate_sequence_power_up(int id, struct clk *clk)
+{
+	int ret;
+
+	tegra_periph_reset_assert(clk);
+
+	ret = tegra_powergate_power_on(id);
+	if (ret)
+		goto err_power;
+
+	ret = clk_enable(clk);
+	if (ret)
+		goto err_clk;
+
+	udelay(10);
+
+	ret = tegra_powergate_remove_clamping(id);
+	if (ret)
+		goto err_clamp;
+
+	udelay(10);
+	tegra_periph_reset_deassert(clk);
+
+	return 0;
+
+err_clamp:
+	clk_disable(clk);
+err_clk:
+	tegra_powergate_power_off(id);
+err_power:
+	return ret;
+}
+
+#ifdef CONFIG_DEBUG_FS
+
+static const char * const powergate_name[] = {
+	[TEGRA_POWERGATE_CPU]	= "cpu",
+	[TEGRA_POWERGATE_3D]	= "3d",
+	[TEGRA_POWERGATE_VENC]	= "venc",
+	[TEGRA_POWERGATE_VDEC]	= "vdec",
+	[TEGRA_POWERGATE_PCIE]	= "pcie",
+	[TEGRA_POWERGATE_L2]	= "l2",
+	[TEGRA_POWERGATE_MPE]	= "mpe",
+};
+
+static int powergate_show(struct seq_file *s, void *data)
+{
+	int i;
+
+	seq_printf(s, " powergate powered\n");
+	seq_printf(s, "------------------\n");
+
+	for (i = 0; i < TEGRA_NUM_POWERGATE; i++)
+		seq_printf(s, " %9s %7s\n", powergate_name[i],
+			tegra_powergate_is_powered(i) ? "yes" : "no");
+	return 0;
+}
+
+static int powergate_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, powergate_show, inode->i_private);
+}
+
+static const struct file_operations powergate_fops = {
+	.open		= powergate_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int __init powergate_debugfs_init(void)
+{
+	struct dentry *d;
+	int err = -ENOMEM;
+
+	d = debugfs_create_file("powergate", S_IRUGO, NULL, NULL,
+		&powergate_fops);
+	if (!d)
+		return -ENOMEM;
+
+	return err;
+}
+
+late_initcall(powergate_debugfs_init);
+
+#endif
-- 
1.7.3.1


^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 06/28] ARM: tegra: Add api to control internal powergating
@ 2011-01-24  2:01   ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/Makefile                 |    1 +
 arch/arm/mach-tegra/include/mach/powergate.h |   40 +++++
 arch/arm/mach-tegra/powergate.c              |  212 ++++++++++++++++++++++++++
 3 files changed, 253 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-tegra/include/mach/powergate.h
 create mode 100644 arch/arm/mach-tegra/powergate.c

diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index cdbc68e..00a6ba3 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -5,6 +5,7 @@ obj-y                                   += clock.o
 obj-y                                   += timer.o
 obj-y                                   += gpio.o
 obj-y                                   += pinmux.o
+obj-y                                   += powergate.o
 obj-y					+= fuse.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += clock.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += tegra2_clocks.o
diff --git a/arch/arm/mach-tegra/include/mach/powergate.h b/arch/arm/mach-tegra/include/mach/powergate.h
new file mode 100644
index 0000000..401d1b7
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/powergate.h
@@ -0,0 +1,40 @@
+/*
+ * drivers/regulator/tegra-regulator.c
+ *
+ * Copyright (c) 2010 Google, Inc
+ *
+ * Author:
+ *	Colin Cross <ccross@google.com>
+ *
+ * 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 _MACH_TEGRA_POWERGATE_H_
+#define _MACH_TEGRA_POWERGATE_H_
+
+#define TEGRA_POWERGATE_CPU	0
+#define TEGRA_POWERGATE_3D	1
+#define TEGRA_POWERGATE_VENC	2
+#define TEGRA_POWERGATE_PCIE	3
+#define TEGRA_POWERGATE_VDEC	4
+#define TEGRA_POWERGATE_L2	5
+#define TEGRA_POWERGATE_MPE	6
+#define TEGRA_NUM_POWERGATE	7
+
+int tegra_powergate_power_on(int id);
+int tegra_powergate_power_off(int id);
+bool tegra_powergate_is_powered(int id);
+int tegra_powergate_remove_clamping(int id);
+
+/* Must be called with clk disabled, and returns with clk enabled */
+int tegra_powergate_sequence_power_up(int id, struct clk *clk);
+
+#endif /* _MACH_TEGRA_POWERGATE_H_ */
diff --git a/arch/arm/mach-tegra/powergate.c b/arch/arm/mach-tegra/powergate.c
new file mode 100644
index 0000000..3cee9aa
--- /dev/null
+++ b/arch/arm/mach-tegra/powergate.c
@@ -0,0 +1,212 @@
+/*
+ * drivers/powergate/tegra-powergate.c
+ *
+ * Copyright (c) 2010 Google, Inc
+ *
+ * Author:
+ *	Colin Cross <ccross@google.com>
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/seq_file.h>
+#include <linux/spinlock.h>
+
+#include <mach/clk.h>
+#include <mach/iomap.h>
+#include <mach/powergate.h>
+
+#define PWRGATE_TOGGLE		0x30
+#define  PWRGATE_TOGGLE_START	(1 << 8)
+
+#define REMOVE_CLAMPING		0x34
+
+#define PWRGATE_STATUS		0x38
+
+static DEFINE_SPINLOCK(tegra_powergate_lock);
+
+static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
+
+static u32 pmc_read(unsigned long reg)
+{
+	return readl(pmc + reg);
+}
+
+static void pmc_write(u32 val, unsigned long reg)
+{
+	writel(val, pmc + reg);
+}
+
+static int tegra_powergate_set(int id, bool new_state)
+{
+	bool status;
+	unsigned long flags;
+
+	spin_lock_irqsave(&tegra_powergate_lock, flags);
+
+	status = pmc_read(PWRGATE_STATUS) & (1 << id);
+
+	if (status == new_state) {
+		spin_unlock_irqrestore(&tegra_powergate_lock, flags);
+		return -EINVAL;
+	}
+
+	pmc_write(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE);
+
+	spin_unlock_irqrestore(&tegra_powergate_lock, flags);
+
+	return 0;
+}
+
+int tegra_powergate_power_on(int id)
+{
+	if (id < 0 || id >= TEGRA_NUM_POWERGATE)
+		return -EINVAL;
+
+	return tegra_powergate_set(id, true);
+}
+
+int tegra_powergate_power_off(int id)
+{
+	if (id < 0 || id >= TEGRA_NUM_POWERGATE)
+		return -EINVAL;
+
+	return tegra_powergate_set(id, false);
+}
+
+bool tegra_powergate_is_powered(int id)
+{
+	u32 status;
+
+	if (id < 0 || id >= TEGRA_NUM_POWERGATE)
+		return -EINVAL;
+
+	status = pmc_read(PWRGATE_STATUS) & (1 << id);
+	return !!status;
+}
+
+int tegra_powergate_remove_clamping(int id)
+{
+	u32 mask;
+
+	if (id < 0 || id >= TEGRA_NUM_POWERGATE)
+		return -EINVAL;
+
+	/*
+	 * Tegra 2 has a bug where PCIE and VDE clamping masks are
+	 * swapped relatively to the partition ids
+	 */
+	if (id ==  TEGRA_POWERGATE_VDEC)
+		mask = (1 << TEGRA_POWERGATE_PCIE);
+	else if	(id == TEGRA_POWERGATE_PCIE)
+		mask = (1 << TEGRA_POWERGATE_VDEC);
+	else
+		mask = (1 << id);
+
+	pmc_write(mask, REMOVE_CLAMPING);
+
+	return 0;
+}
+
+/* Must be called with clk disabled, and returns with clk enabled */
+int tegra_powergate_sequence_power_up(int id, struct clk *clk)
+{
+	int ret;
+
+	tegra_periph_reset_assert(clk);
+
+	ret = tegra_powergate_power_on(id);
+	if (ret)
+		goto err_power;
+
+	ret = clk_enable(clk);
+	if (ret)
+		goto err_clk;
+
+	udelay(10);
+
+	ret = tegra_powergate_remove_clamping(id);
+	if (ret)
+		goto err_clamp;
+
+	udelay(10);
+	tegra_periph_reset_deassert(clk);
+
+	return 0;
+
+err_clamp:
+	clk_disable(clk);
+err_clk:
+	tegra_powergate_power_off(id);
+err_power:
+	return ret;
+}
+
+#ifdef CONFIG_DEBUG_FS
+
+static const char * const powergate_name[] = {
+	[TEGRA_POWERGATE_CPU]	= "cpu",
+	[TEGRA_POWERGATE_3D]	= "3d",
+	[TEGRA_POWERGATE_VENC]	= "venc",
+	[TEGRA_POWERGATE_VDEC]	= "vdec",
+	[TEGRA_POWERGATE_PCIE]	= "pcie",
+	[TEGRA_POWERGATE_L2]	= "l2",
+	[TEGRA_POWERGATE_MPE]	= "mpe",
+};
+
+static int powergate_show(struct seq_file *s, void *data)
+{
+	int i;
+
+	seq_printf(s, " powergate powered\n");
+	seq_printf(s, "------------------\n");
+
+	for (i = 0; i < TEGRA_NUM_POWERGATE; i++)
+		seq_printf(s, " %9s %7s\n", powergate_name[i],
+			tegra_powergate_is_powered(i) ? "yes" : "no");
+	return 0;
+}
+
+static int powergate_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, powergate_show, inode->i_private);
+}
+
+static const struct file_operations powergate_fops = {
+	.open		= powergate_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int __init powergate_debugfs_init(void)
+{
+	struct dentry *d;
+	int err = -ENOMEM;
+
+	d = debugfs_create_file("powergate", S_IRUGO, NULL, NULL,
+		&powergate_fops);
+	if (!d)
+		return -ENOMEM;
+
+	return err;
+}
+
+late_initcall(powergate_debugfs_init);
+
+#endif
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 07/28] ARM: tegra: irqs: Update irq list
  2011-01-24  2:01 [PATCH v2 00/28] Updates for Tegra support in 2.6.39 Colin Cross
@ 2011-01-24  2:01   ` Colin Cross
  2011-01-24  2:01   ` Colin Cross
                     ` (26 subsequent siblings)
  27 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-tegra
  Cc: linux-arm-kernel, olof, konkers, Colin Cross, Russell King,
	Gary King, linux-kernel

Fixes typo in INT_CPU1_PMU_INTR (original fix from Will Deacon)
Adds board irqs

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/include/mach/irqs.h |   14 +++++++++++---
 1 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/arch/arm/mach-tegra/include/mach/irqs.h b/arch/arm/mach-tegra/include/mach/irqs.h
index 71bbf34..73265af 100644
--- a/arch/arm/mach-tegra/include/mach/irqs.h
+++ b/arch/arm/mach-tegra/include/mach/irqs.h
@@ -88,7 +88,7 @@
 #define INT_SYS_STATS_MON		(INT_SEC_BASE + 22)
 #define INT_GPIO5			(INT_SEC_BASE + 23)
 #define INT_CPU0_PMU_INTR		(INT_SEC_BASE + 24)
-#define INT_CPU2_PMU_INTR		(INT_SEC_BASE + 25)
+#define INT_CPU1_PMU_INTR		(INT_SEC_BASE + 25)
 #define INT_SEC_RES_26			(INT_SEC_BASE + 26)
 #define INT_S_LINK1			(INT_SEC_BASE + 27)
 #define INT_APB_DMA_COP			(INT_SEC_BASE + 28)
@@ -166,10 +166,18 @@
 #define INT_QUAD_RES_30			(INT_QUAD_BASE + 30)
 #define INT_QUAD_RES_31			(INT_QUAD_BASE + 31)
 
-#define INT_GPIO_BASE			(INT_QUAD_BASE + 32)
+#define INT_MAIN_NR			(INT_QUAD_BASE + 32 - INT_PRI_BASE)
+
+#define INT_GPIO_BASE			(INT_PRI_BASE + INT_MAIN_NR)
+
 #define INT_GPIO_NR			(28 * 8)
 
-#define NR_IRQS				(INT_GPIO_BASE + INT_GPIO_NR)
+#define TEGRA_NR_IRQS			(INT_GPIO_BASE + INT_GPIO_NR)
+
+#define INT_BOARD_BASE			TEGRA_NR_IRQS
+#define NR_BOARD_IRQS			32
+
+#define NR_IRQS				(INT_BOARD_BASE + NR_BOARD_IRQS)
 #endif
 
 #endif
-- 
1.7.3.1


^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 07/28] ARM: tegra: irqs: Update irq list
@ 2011-01-24  2:01   ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-arm-kernel

Fixes typo in INT_CPU1_PMU_INTR (original fix from Will Deacon)
Adds board irqs

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/include/mach/irqs.h |   14 +++++++++++---
 1 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/arch/arm/mach-tegra/include/mach/irqs.h b/arch/arm/mach-tegra/include/mach/irqs.h
index 71bbf34..73265af 100644
--- a/arch/arm/mach-tegra/include/mach/irqs.h
+++ b/arch/arm/mach-tegra/include/mach/irqs.h
@@ -88,7 +88,7 @@
 #define INT_SYS_STATS_MON		(INT_SEC_BASE + 22)
 #define INT_GPIO5			(INT_SEC_BASE + 23)
 #define INT_CPU0_PMU_INTR		(INT_SEC_BASE + 24)
-#define INT_CPU2_PMU_INTR		(INT_SEC_BASE + 25)
+#define INT_CPU1_PMU_INTR		(INT_SEC_BASE + 25)
 #define INT_SEC_RES_26			(INT_SEC_BASE + 26)
 #define INT_S_LINK1			(INT_SEC_BASE + 27)
 #define INT_APB_DMA_COP			(INT_SEC_BASE + 28)
@@ -166,10 +166,18 @@
 #define INT_QUAD_RES_30			(INT_QUAD_BASE + 30)
 #define INT_QUAD_RES_31			(INT_QUAD_BASE + 31)
 
-#define INT_GPIO_BASE			(INT_QUAD_BASE + 32)
+#define INT_MAIN_NR			(INT_QUAD_BASE + 32 - INT_PRI_BASE)
+
+#define INT_GPIO_BASE			(INT_PRI_BASE + INT_MAIN_NR)
+
 #define INT_GPIO_NR			(28 * 8)
 
-#define NR_IRQS				(INT_GPIO_BASE + INT_GPIO_NR)
+#define TEGRA_NR_IRQS			(INT_GPIO_BASE + INT_GPIO_NR)
+
+#define INT_BOARD_BASE			TEGRA_NR_IRQS
+#define NR_BOARD_IRQS			32
+
+#define NR_IRQS				(INT_BOARD_BASE + NR_BOARD_IRQS)
 #endif
 
 #endif
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 08/28] ARM: tegra: Add prototypes for subsystem suspend functions
  2011-01-24  2:01 [PATCH v2 00/28] Updates for Tegra support in 2.6.39 Colin Cross
@ 2011-01-24  2:01   ` Colin Cross
  2011-01-24  2:01   ` Colin Cross
                     ` (26 subsequent siblings)
  27 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-tegra
  Cc: linux-arm-kernel, olof, konkers, Colin Cross, Russell King,
	Gary King, linux-kernel

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/dma.c                  |    1 +
 arch/arm/mach-tegra/gpio.c                 |    1 +
 arch/arm/mach-tegra/include/mach/suspend.h |   38 ++++++++++++++++++++++++++++
 arch/arm/mach-tegra/irq.c                  |    1 +
 arch/arm/mach-tegra/pinmux-t2-tables.c     |    1 +
 arch/arm/mach-tegra/tegra2_clocks.c        |    1 +
 arch/arm/mach-tegra/timer.c                |    1 +
 7 files changed, 44 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-tegra/include/mach/suspend.h

diff --git a/arch/arm/mach-tegra/dma.c b/arch/arm/mach-tegra/dma.c
index edda6ec..a2a252d 100644
--- a/arch/arm/mach-tegra/dma.c
+++ b/arch/arm/mach-tegra/dma.c
@@ -30,6 +30,7 @@
 #include <mach/dma.h>
 #include <mach/irqs.h>
 #include <mach/iomap.h>
+#include <mach/suspend.h>
 
 #define APB_DMA_GEN				0x000
 #define GEN_ENABLE				(1<<31)
diff --git a/arch/arm/mach-tegra/gpio.c b/arch/arm/mach-tegra/gpio.c
index ad80488..132dcd6 100644
--- a/arch/arm/mach-tegra/gpio.c
+++ b/arch/arm/mach-tegra/gpio.c
@@ -25,6 +25,7 @@
 #include <linux/gpio.h>
 
 #include <mach/iomap.h>
+#include <mach/suspend.h>
 
 #define GPIO_BANK(x)		((x) >> 5)
 #define GPIO_PORT(x)		(((x) >> 3) & 0x3)
diff --git a/arch/arm/mach-tegra/include/mach/suspend.h b/arch/arm/mach-tegra/include/mach/suspend.h
new file mode 100644
index 0000000..5af8715
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/suspend.h
@@ -0,0 +1,38 @@
+/*
+ * arch/arm/mach-tegra/include/mach/suspend.h
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ *	Colin Cross <ccross@google.com>
+ *
+ * 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 _MACH_TEGRA_SUSPEND_H_
+#define _MACH_TEGRA_SUSPEND_H_
+
+void tegra_pinmux_suspend(void);
+void tegra_irq_suspend(void);
+void tegra_gpio_suspend(void);
+void tegra_clk_suspend(void);
+void tegra_dma_suspend(void);
+void tegra_timer_suspend(void);
+
+void tegra_pinmux_resume(void);
+void tegra_irq_resume(void);
+void tegra_gpio_resume(void);
+void tegra_clk_resume(void);
+void tegra_dma_resume(void);
+void tegra_timer_resume(void);
+
+#endif /* _MACH_TEGRA_SUSPEND_H_ */
diff --git a/arch/arm/mach-tegra/irq.c b/arch/arm/mach-tegra/irq.c
index 17c74d2..5f065f9 100644
--- a/arch/arm/mach-tegra/irq.c
+++ b/arch/arm/mach-tegra/irq.c
@@ -26,6 +26,7 @@
 #include <asm/hardware/gic.h>
 
 #include <mach/iomap.h>
+#include <mach/suspend.h>
 
 #include "board.h"
 
diff --git a/arch/arm/mach-tegra/pinmux-t2-tables.c b/arch/arm/mach-tegra/pinmux-t2-tables.c
index a6ea34e..4d97d5c 100644
--- a/arch/arm/mach-tegra/pinmux-t2-tables.c
+++ b/arch/arm/mach-tegra/pinmux-t2-tables.c
@@ -29,6 +29,7 @@
 
 #include <mach/iomap.h>
 #include <mach/pinmux.h>
+#include <mach/suspend.h>
 
 #define DRIVE_PINGROUP(pg_name, r)				\
 	[TEGRA_DRIVE_PINGROUP_ ## pg_name] = {			\
diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index f0dae6d..2dd2b03 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -27,6 +27,7 @@
 #include <linux/clkdev.h>
 
 #include <mach/iomap.h>
+#include <mach/suspend.h>
 
 #include "clock.h"
 #include "fuse.h"
diff --git a/arch/arm/mach-tegra/timer.c b/arch/arm/mach-tegra/timer.c
index 7b8ad1f..36b53a7 100644
--- a/arch/arm/mach-tegra/timer.c
+++ b/arch/arm/mach-tegra/timer.c
@@ -33,6 +33,7 @@
 
 #include <mach/iomap.h>
 #include <mach/irqs.h>
+#include <mach/suspend.h>
 
 #include "board.h"
 #include "clock.h"
-- 
1.7.3.1


^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 08/28] ARM: tegra: Add prototypes for subsystem suspend functions
@ 2011-01-24  2:01   ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/dma.c                  |    1 +
 arch/arm/mach-tegra/gpio.c                 |    1 +
 arch/arm/mach-tegra/include/mach/suspend.h |   38 ++++++++++++++++++++++++++++
 arch/arm/mach-tegra/irq.c                  |    1 +
 arch/arm/mach-tegra/pinmux-t2-tables.c     |    1 +
 arch/arm/mach-tegra/tegra2_clocks.c        |    1 +
 arch/arm/mach-tegra/timer.c                |    1 +
 7 files changed, 44 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-tegra/include/mach/suspend.h

diff --git a/arch/arm/mach-tegra/dma.c b/arch/arm/mach-tegra/dma.c
index edda6ec..a2a252d 100644
--- a/arch/arm/mach-tegra/dma.c
+++ b/arch/arm/mach-tegra/dma.c
@@ -30,6 +30,7 @@
 #include <mach/dma.h>
 #include <mach/irqs.h>
 #include <mach/iomap.h>
+#include <mach/suspend.h>
 
 #define APB_DMA_GEN				0x000
 #define GEN_ENABLE				(1<<31)
diff --git a/arch/arm/mach-tegra/gpio.c b/arch/arm/mach-tegra/gpio.c
index ad80488..132dcd6 100644
--- a/arch/arm/mach-tegra/gpio.c
+++ b/arch/arm/mach-tegra/gpio.c
@@ -25,6 +25,7 @@
 #include <linux/gpio.h>
 
 #include <mach/iomap.h>
+#include <mach/suspend.h>
 
 #define GPIO_BANK(x)		((x) >> 5)
 #define GPIO_PORT(x)		(((x) >> 3) & 0x3)
diff --git a/arch/arm/mach-tegra/include/mach/suspend.h b/arch/arm/mach-tegra/include/mach/suspend.h
new file mode 100644
index 0000000..5af8715
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/suspend.h
@@ -0,0 +1,38 @@
+/*
+ * arch/arm/mach-tegra/include/mach/suspend.h
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ *	Colin Cross <ccross@google.com>
+ *
+ * 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 _MACH_TEGRA_SUSPEND_H_
+#define _MACH_TEGRA_SUSPEND_H_
+
+void tegra_pinmux_suspend(void);
+void tegra_irq_suspend(void);
+void tegra_gpio_suspend(void);
+void tegra_clk_suspend(void);
+void tegra_dma_suspend(void);
+void tegra_timer_suspend(void);
+
+void tegra_pinmux_resume(void);
+void tegra_irq_resume(void);
+void tegra_gpio_resume(void);
+void tegra_clk_resume(void);
+void tegra_dma_resume(void);
+void tegra_timer_resume(void);
+
+#endif /* _MACH_TEGRA_SUSPEND_H_ */
diff --git a/arch/arm/mach-tegra/irq.c b/arch/arm/mach-tegra/irq.c
index 17c74d2..5f065f9 100644
--- a/arch/arm/mach-tegra/irq.c
+++ b/arch/arm/mach-tegra/irq.c
@@ -26,6 +26,7 @@
 #include <asm/hardware/gic.h>
 
 #include <mach/iomap.h>
+#include <mach/suspend.h>
 
 #include "board.h"
 
diff --git a/arch/arm/mach-tegra/pinmux-t2-tables.c b/arch/arm/mach-tegra/pinmux-t2-tables.c
index a6ea34e..4d97d5c 100644
--- a/arch/arm/mach-tegra/pinmux-t2-tables.c
+++ b/arch/arm/mach-tegra/pinmux-t2-tables.c
@@ -29,6 +29,7 @@
 
 #include <mach/iomap.h>
 #include <mach/pinmux.h>
+#include <mach/suspend.h>
 
 #define DRIVE_PINGROUP(pg_name, r)				\
 	[TEGRA_DRIVE_PINGROUP_ ## pg_name] = {			\
diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index f0dae6d..2dd2b03 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -27,6 +27,7 @@
 #include <linux/clkdev.h>
 
 #include <mach/iomap.h>
+#include <mach/suspend.h>
 
 #include "clock.h"
 #include "fuse.h"
diff --git a/arch/arm/mach-tegra/timer.c b/arch/arm/mach-tegra/timer.c
index 7b8ad1f..36b53a7 100644
--- a/arch/arm/mach-tegra/timer.c
+++ b/arch/arm/mach-tegra/timer.c
@@ -33,6 +33,7 @@
 
 #include <mach/iomap.h>
 #include <mach/irqs.h>
+#include <mach/suspend.h>
 
 #include "board.h"
 #include "clock.h"
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 09/28] ARM: tegra: clock: Suspend fixes, and add new clocks
  2011-01-24  2:01 [PATCH v2 00/28] Updates for Tegra support in 2.6.39 Colin Cross
@ 2011-01-24  2:01   ` Colin Cross
  2011-01-24  2:01   ` Colin Cross
                     ` (26 subsequent siblings)
  27 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-tegra
  Cc: linux-arm-kernel, olof, konkers, Colin Cross, Russell King, linux-kernel

Save and restore pll and osc state during suspend
Add digital audio clocks
Update clk dev associations
Correct max clock frequencies
Add pll_p as additional cpu clock state
Add values to plld table
Fix register offset for sdmmc4 clock
Add blink timer to tegra2_clocks

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/tegra2_clocks.c |  263 ++++++++++++++++++++++++++++++++---
 1 files changed, 244 insertions(+), 19 deletions(-)

diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index 2dd2b03..7a2926a 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -52,7 +52,7 @@
 #define OSC_CTRL_OSC_FREQ_19_2MHZ	(1<<30)
 #define OSC_CTRL_OSC_FREQ_12MHZ		(2<<30)
 #define OSC_CTRL_OSC_FREQ_26MHZ		(3<<30)
-#define OSC_CTRL_MASK			0x3f2
+#define OSC_CTRL_MASK			(0x3f2 | OSC_CTRL_OSC_FREQ_MASK)
 
 #define OSC_FREQ_DET			0x58
 #define OSC_FREQ_DET_TRIG		(1<<31)
@@ -135,12 +135,29 @@
 #define BUS_CLK_DISABLE			(1<<3)
 #define BUS_CLK_DIV_MASK		0x3
 
+#define PMC_CTRL			0x0
+ #define PMC_CTRL_BLINK_ENB		(1 << 7)
+
+#define PMC_DPD_PADS_ORIDE		0x1c
+ #define PMC_DPD_PADS_ORIDE_BLINK_ENB	(1 << 20)
+
+#define PMC_BLINK_TIMER_DATA_ON_SHIFT	0
+#define PMC_BLINK_TIMER_DATA_ON_MASK	0x7fff
+#define PMC_BLINK_TIMER_ENB		(1 << 15)
+#define PMC_BLINK_TIMER_DATA_OFF_SHIFT	16
+#define PMC_BLINK_TIMER_DATA_OFF_MASK	0xffff
+
 static void __iomem *reg_clk_base = IO_ADDRESS(TEGRA_CLK_RESET_BASE);
+static void __iomem *reg_pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
 
 #define clk_writel(value, reg) \
 	__raw_writel(value, (u32)reg_clk_base + (reg))
 #define clk_readl(reg) \
 	__raw_readl((u32)reg_clk_base + (reg))
+#define pmc_writel(value, reg) \
+	__raw_writel(value, (u32)reg_pmc_base + (reg))
+#define pmc_readl(reg) \
+	__raw_readl((u32)reg_pmc_base + (reg))
 
 unsigned long clk_measure_input_freq(void)
 {
@@ -358,6 +375,9 @@ static int tegra2_cpu_clk_set_rate(struct clk *c, unsigned long rate)
 		return ret;
 	}
 
+	if (rate == c->backup->rate)
+		goto out;
+
 	ret = clk_set_rate_locked(c->main, rate);
 	if (ret) {
 		pr_err("Failed to change cpu pll to %lu\n", rate);
@@ -370,6 +390,7 @@ static int tegra2_cpu_clk_set_rate(struct clk *c, unsigned long rate)
 		return ret;
 	}
 
+out:
 	return 0;
 }
 
@@ -429,6 +450,87 @@ static struct clk_ops tegra_bus_ops = {
 	.set_rate		= tegra2_bus_clk_set_rate,
 };
 
+/* Blink output functions */
+
+static void tegra2_blink_clk_init(struct clk *c)
+{
+	u32 val;
+
+	val = pmc_readl(PMC_CTRL);
+	c->state = (val & PMC_CTRL_BLINK_ENB) ? ON : OFF;
+	c->mul = 1;
+	val = pmc_readl(c->reg);
+
+	if (val & PMC_BLINK_TIMER_ENB) {
+		unsigned int on_off;
+
+		on_off = (val >> PMC_BLINK_TIMER_DATA_ON_SHIFT) &
+			PMC_BLINK_TIMER_DATA_ON_MASK;
+		val >>= PMC_BLINK_TIMER_DATA_OFF_SHIFT;
+		val &= PMC_BLINK_TIMER_DATA_OFF_MASK;
+		on_off += val;
+		/* each tick in the blink timer is 4 32KHz clocks */
+		c->div = on_off * 4;
+	} else {
+		c->div = 1;
+	}
+}
+
+static int tegra2_blink_clk_enable(struct clk *c)
+{
+	u32 val;
+
+	val = pmc_readl(PMC_DPD_PADS_ORIDE);
+	pmc_writel(val | PMC_DPD_PADS_ORIDE_BLINK_ENB, PMC_DPD_PADS_ORIDE);
+
+	val = pmc_readl(PMC_CTRL);
+	pmc_writel(val | PMC_CTRL_BLINK_ENB, PMC_CTRL);
+
+	return 0;
+}
+
+static void tegra2_blink_clk_disable(struct clk *c)
+{
+	u32 val;
+
+	val = pmc_readl(PMC_CTRL);
+	pmc_writel(val & ~PMC_CTRL_BLINK_ENB, PMC_CTRL);
+
+	val = pmc_readl(PMC_DPD_PADS_ORIDE);
+	pmc_writel(val & ~PMC_DPD_PADS_ORIDE_BLINK_ENB, PMC_DPD_PADS_ORIDE);
+}
+
+static int tegra2_blink_clk_set_rate(struct clk *c, unsigned long rate)
+{
+	if (rate >= c->parent->rate) {
+		c->div = 1;
+		pmc_writel(0, c->reg);
+	} else {
+		unsigned int on_off;
+		u32 val;
+
+		on_off = DIV_ROUND_UP(c->parent->rate / 8, rate);
+		c->div = on_off * 8;
+
+		val = (on_off & PMC_BLINK_TIMER_DATA_ON_MASK) <<
+			PMC_BLINK_TIMER_DATA_ON_SHIFT;
+		on_off &= PMC_BLINK_TIMER_DATA_OFF_MASK;
+		on_off <<= PMC_BLINK_TIMER_DATA_OFF_SHIFT;
+		val |= on_off;
+		val |= PMC_BLINK_TIMER_ENB;
+		pmc_writel(val, c->reg);
+	}
+
+	return 0;
+}
+
+static struct clk_ops tegra_blink_clk_ops = {
+	.init			= &tegra2_blink_clk_init,
+	.enable			= &tegra2_blink_clk_enable,
+	.disable		= &tegra2_blink_clk_disable,
+	.set_rate		= &tegra2_blink_clk_set_rate,
+};
+
 /* PLL Functions */
 static int tegra2_pll_clk_wait_for_lock(struct clk *c)
 {
@@ -929,6 +1031,7 @@ static struct clk_ops tegra_clk_double_ops = {
 	.set_rate		= &tegra2_clk_double_set_rate,
 };
 
+/* Audio sync clock ops */
 static void tegra2_audio_sync_clk_init(struct clk *c)
 {
 	int source;
@@ -1007,6 +1110,37 @@ static struct clk_ops tegra_audio_sync_clk_ops = {
 	.set_parent = tegra2_audio_sync_clk_set_parent,
 };
 
+/* cdev1 and cdev2 (dap_mclk1 and dap_mclk2) ops */
+
+static void tegra2_cdev_clk_init(struct clk *c)
+{
+	/* We could un-tristate the cdev1 or cdev2 pingroup here; this is
+	 * currently done in the pinmux code. */
+	c->state = ON;
+	if (!(clk_readl(CLK_OUT_ENB + PERIPH_CLK_TO_ENB_REG(c)) &
+			PERIPH_CLK_TO_ENB_BIT(c)))
+		c->state = OFF;
+}
+
+static int tegra2_cdev_clk_enable(struct clk *c)
+{
+	clk_writel(PERIPH_CLK_TO_ENB_BIT(c),
+		CLK_OUT_ENB_SET + PERIPH_CLK_TO_ENB_SET_REG(c));
+	return 0;
+}
+
+static void tegra2_cdev_clk_disable(struct clk *c)
+{
+	clk_writel(PERIPH_CLK_TO_ENB_BIT(c),
+		CLK_OUT_ENB_CLR + PERIPH_CLK_TO_ENB_SET_REG(c));
+}
+
+static struct clk_ops tegra_cdev_clk_ops = {
+	.init			= &tegra2_cdev_clk_init,
+	.enable			= &tegra2_cdev_clk_enable,
+	.disable		= &tegra2_cdev_clk_disable,
+};
+
 /* Clock definitions */
 static struct clk tegra_clk_32k = {
 	.name = "clk_32k",
@@ -1227,10 +1361,21 @@ static struct clk tegra_pll_a_out0 = {
 };
 
 static struct clk_pll_table tegra_pll_d_table[] = {
+	{ 12000000, 216000000, 216, 12, 1, 4},
+	{ 13000000, 216000000, 216, 13, 1, 4},
+	{ 19200000, 216000000, 135, 12, 1, 3},
+	{ 26000000, 216000000, 216, 26, 1, 4},
+
+	{ 12000000, 594000000, 594, 12, 1, 8},
+	{ 13000000, 594000000, 594, 13, 1, 8},
+	{ 19200000, 594000000, 495, 16, 1, 8},
+	{ 26000000, 594000000, 594, 26, 1, 8},
+
 	{ 12000000, 1000000000, 1000, 12, 1, 12},
 	{ 13000000, 1000000000, 1000, 13, 1, 12},
 	{ 19200000, 1000000000, 625,  12, 1, 8},
 	{ 26000000, 1000000000, 1000, 26, 1, 12},
+
 	{ 0, 0, 0, 0, 0, 0 },
 };
 
@@ -1372,6 +1517,24 @@ static struct clk tegra_clk_d = {
 	.max_rate  = 52000000,
 };
 
+/* dap_mclk1, belongs to the cdev1 pingroup. */
+static struct clk tegra_dev1_clk = {
+	.name      = "clk_dev1",
+	.ops       = &tegra_cdev_clk_ops,
+	.clk_num   = 94,
+	.rate      = 26000000,
+	.max_rate  = 26000000,
+};
+
+/* dap_mclk2, belongs to the cdev2 pingroup. */
+static struct clk tegra_dev2_clk = {
+	.name      = "clk_dev2",
+	.ops       = &tegra_cdev_clk_ops,
+	.clk_num   = 93,
+	.rate      = 26000000,
+	.max_rate  = 26000000,
+};
+
 /* initialized before peripheral clocks */
 static struct clk_mux_sel mux_audio_sync_clk[8+1];
 static const struct audio_sources {
@@ -1486,7 +1649,7 @@ static struct clk tegra_clk_virtual_cpu = {
 	.name      = "cpu",
 	.parent    = &tegra_clk_cclk,
 	.main      = &tegra_pll_x,
-	.backup    = &tegra_clk_m,
+	.backup    = &tegra_pll_p,
 	.ops       = &tegra_cpu_ops,
 	.max_rate  = 1000000000,
 	.dvfs      = &tegra_dvfs_virtual_cpu_dvfs,
@@ -1512,6 +1675,14 @@ static struct clk tegra_clk_pclk = {
 	.max_rate       = 108000000,
 };
 
+static struct clk tegra_clk_blink = {
+	.name		= "blink",
+	.parent		= &tegra_clk_32k,
+	.reg		= 0x40,
+	.ops		= &tegra_blink_clk_ops,
+	.max_rate	= 32768,
+};
+
 static struct clk_mux_sel mux_pllm_pllc_pllp_plla[] = {
 	{ .input = &tegra_pll_m, .value = 0},
 	{ .input = &tegra_pll_c, .value = 1},
@@ -1626,7 +1797,7 @@ struct clk tegra_periph_clks[] = {
 	PERIPH_CLK("sdmmc1",	"sdhci-tegra.0",	NULL,	14,	0x150,	52000000,  mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71), /* scales with voltage */
 	PERIPH_CLK("sdmmc2",	"sdhci-tegra.1",	NULL,	9,	0x154,	52000000,  mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71), /* scales with voltage */
 	PERIPH_CLK("sdmmc3",	"sdhci-tegra.2",	NULL,	69,	0x1bc,	52000000,  mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71), /* scales with voltage */
-	PERIPH_CLK("sdmmc4",	"sdhci-tegra.3",	NULL,	15,	0x160,	52000000,  mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71), /* scales with voltage */
+	PERIPH_CLK("sdmmc4",	"sdhci-tegra.3",	NULL,	15,	0x164,	52000000,  mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71), /* scales with voltage */
 	PERIPH_CLK("vde",	"vde",			NULL,	61,	0x1c8,	250000000, mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71), /* scales with voltage and process_id */
 	PERIPH_CLK("csite",	"csite",		NULL,	73,	0x1d4,	144000000, mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71), /* max rate ??? */
 	/* FIXME: what is la? */
@@ -1642,34 +1813,34 @@ struct clk tegra_periph_clks[] = {
 	PERIPH_CLK("i2c2_i2c",	"tegra-i2c.1",		"i2c",	0,	0,	72000000,  mux_pllp_out3,			0),
 	PERIPH_CLK("i2c3_i2c",	"tegra-i2c.2",		"i2c",	0,	0,	72000000,  mux_pllp_out3,			0),
 	PERIPH_CLK("dvc_i2c",	"tegra-i2c.3",		"i2c",	0,	0,	72000000,  mux_pllp_out3,			0),
-	PERIPH_CLK("uarta",	"uart.0",		NULL,	6,	0x178,	216000000, mux_pllp_pllc_pllm_clkm,	MUX),
-	PERIPH_CLK("uartb",	"uart.1",		NULL,	7,	0x17c,	216000000, mux_pllp_pllc_pllm_clkm,	MUX),
-	PERIPH_CLK("uartc",	"uart.2",		NULL,	55,	0x1a0,	216000000, mux_pllp_pllc_pllm_clkm,	MUX),
-	PERIPH_CLK("uartd",	"uart.3",		NULL,	65,	0x1c0,	216000000, mux_pllp_pllc_pllm_clkm,	MUX),
-	PERIPH_CLK("uarte",	"uart.4",		NULL,	66,	0x1c4,	216000000, mux_pllp_pllc_pllm_clkm,	MUX),
+	PERIPH_CLK("uarta",	"uart.0",		NULL,	6,	0x178,	600000000, mux_pllp_pllc_pllm_clkm,	MUX),
+	PERIPH_CLK("uartb",	"uart.1",		NULL,	7,	0x17c,	600000000, mux_pllp_pllc_pllm_clkm,	MUX),
+	PERIPH_CLK("uartc",	"uart.2",		NULL,	55,	0x1a0,	600000000, mux_pllp_pllc_pllm_clkm,	MUX),
+	PERIPH_CLK("uartd",	"uart.3",		NULL,	65,	0x1c0,	600000000, mux_pllp_pllc_pllm_clkm,	MUX),
+	PERIPH_CLK("uarte",	"uart.4",		NULL,	66,	0x1c4,	600000000, mux_pllp_pllc_pllm_clkm,	MUX),
 	PERIPH_CLK("3d",	"3d",			NULL,	24,	0x158,	300000000, mux_pllm_pllc_pllp_plla,	MUX | DIV_U71 | PERIPH_MANUAL_RESET), /* scales with voltage and process_id */
 	PERIPH_CLK("2d",	"2d",			NULL,	21,	0x15c,	300000000, mux_pllm_pllc_pllp_plla,	MUX | DIV_U71), /* scales with voltage and process_id */
 	/* FIXME: vi and vi_sensor share an enable */
-	PERIPH_CLK("vi",	"vi",			NULL,	20,	0x148,	150000000, mux_pllm_pllc_pllp_plla,	MUX | DIV_U71), /* scales with voltage and process_id */
-	PERIPH_CLK("vi_sensor",	"vi_sensor",		NULL,	20,	0x1a8,	150000000, mux_pllm_pllc_pllp_plla,	MUX | DIV_U71 | PERIPH_NO_RESET), /* scales with voltage and process_id */
+	PERIPH_CLK("vi",	"tegra_camera",		"vi",	20,	0x148,	150000000, mux_pllm_pllc_pllp_plla,	MUX | DIV_U71), /* scales with voltage and process_id */
+	PERIPH_CLK("vi_sensor",	"tegra_camera",		"vi_sensor",	20,	0x1a8,	150000000, mux_pllm_pllc_pllp_plla,	MUX | DIV_U71 | PERIPH_NO_RESET), /* scales with voltage and process_id */
 	PERIPH_CLK("epp",	"epp",			NULL,	19,	0x16c,	300000000, mux_pllm_pllc_pllp_plla,	MUX | DIV_U71), /* scales with voltage and process_id */
 	PERIPH_CLK("mpe",	"mpe",			NULL,	60,	0x170,	250000000, mux_pllm_pllc_pllp_plla,	MUX | DIV_U71), /* scales with voltage and process_id */
 	PERIPH_CLK("host1x",	"host1x",		NULL,	28,	0x180,	166000000, mux_pllm_pllc_pllp_plla,	MUX | DIV_U71), /* scales with voltage and process_id */
 	/* FIXME: cve and tvo share an enable	*/
 	PERIPH_CLK("cve",	"cve",			NULL,	49,	0x140,	250000000, mux_pllp_plld_pllc_clkm,	MUX | DIV_U71), /* requires min voltage */
 	PERIPH_CLK("tvo",	"tvo",			NULL,	49,	0x188,	250000000, mux_pllp_plld_pllc_clkm,	MUX | DIV_U71), /* requires min voltage */
-	PERIPH_CLK("hdmi",	"hdmi",			NULL,	51,	0x18c,	148500000, mux_pllp_plld_pllc_clkm,	MUX | DIV_U71), /* requires min voltage */
+	PERIPH_CLK("hdmi",	"hdmi",			NULL,	51,	0x18c,	600000000, mux_pllp_plld_pllc_clkm,	MUX | DIV_U71), /* requires min voltage */
 	PERIPH_CLK("tvdac",	"tvdac",		NULL,	53,	0x194,	250000000, mux_pllp_plld_pllc_clkm,	MUX | DIV_U71), /* requires min voltage */
-	PERIPH_CLK("disp1",	"tegrafb.0",		NULL,	27,	0x138,	190000000, mux_pllp_plld_pllc_clkm,	MUX | DIV_U71), /* scales with voltage and process_id */
-	PERIPH_CLK("disp2",	"tegrafb.1",		NULL,	26,	0x13c,	190000000, mux_pllp_plld_pllc_clkm,	MUX | DIV_U71), /* scales with voltage and process_id */
+	PERIPH_CLK("disp1",	"tegradc.0",		NULL,	27,	0x138,	600000000, mux_pllp_plld_pllc_clkm,	MUX | DIV_U71), /* scales with voltage and process_id */
+	PERIPH_CLK("disp2",	"tegradc.1",		NULL,	26,	0x13c,	600000000, mux_pllp_plld_pllc_clkm,	MUX | DIV_U71), /* scales with voltage and process_id */
 	PERIPH_CLK("usbd",	"fsl-tegra-udc",	NULL,	22,	0,	480000000, mux_clk_m,			0), /* requires min voltage */
 	PERIPH_CLK("usb2",	"tegra-ehci.1",		NULL,	58,	0,	480000000, mux_clk_m,			0), /* requires min voltage */
 	PERIPH_CLK("usb3",	"tegra-ehci.2",		NULL,	59,	0,	480000000, mux_clk_m,			0), /* requires min voltage */
 	PERIPH_CLK("emc",	"emc",			NULL,	57,	0x19c,	800000000, mux_pllm_pllc_pllp_clkm,	MUX | DIV_U71 | PERIPH_EMC_ENB),
 	PERIPH_CLK("dsi",	"dsi",			NULL,	48,	0,	500000000, mux_plld,			0), /* scales with voltage */
-	PERIPH_CLK("csi",	"csi",			NULL,	52,	0,	72000000,  mux_pllp_out3,		0),
-	PERIPH_CLK("isp",	"isp",			NULL,	23,	0,	150000000, mux_clk_m,			0), /* same frequency as VI */
-	PERIPH_CLK("csus",	"csus",			NULL,	92,	0,	150000000, mux_clk_m,			PERIPH_NO_RESET),
+	PERIPH_CLK("csi",	"tegra_camera",		"csi",	52,	0,	72000000,  mux_pllp_out3,		0),
+	PERIPH_CLK("isp",	"tegra_camera",		"isp",	23,	0,	150000000, mux_clk_m,			0), /* same frequency as VI */
+	PERIPH_CLK("csus",	"tegra_camera",		"csus",	92,	0,	150000000, mux_clk_m,			PERIPH_NO_RESET),
 	PERIPH_CLK("pex",       NULL,			"pex",  70,     0,	26000000,  mux_clk_m,			PERIPH_MANUAL_RESET),
 	PERIPH_CLK("afi",       NULL,			"afi",  72,     0,	26000000,  mux_clk_m,			PERIPH_MANUAL_RESET),
 	PERIPH_CLK("pcie_xclk", NULL,		  "pcie_xclk",  74,     0,	26000000,  mux_clk_m,			PERIPH_MANUAL_RESET),
@@ -1694,9 +1865,15 @@ struct clk_duplicate tegra_clk_duplicates[] = {
 	CLK_DUPLICATE("uartc",	"tegra_uart.2",	NULL),
 	CLK_DUPLICATE("uartd",	"tegra_uart.3",	NULL),
 	CLK_DUPLICATE("uarte",	"tegra_uart.4",	NULL),
-	CLK_DUPLICATE("host1x", "tegrafb.0", "host1x"),
-	CLK_DUPLICATE("host1x", "tegrafb.1", "host1x"),
+	CLK_DUPLICATE("usbd", "utmip-pad", NULL),
 	CLK_DUPLICATE("usbd", "tegra-ehci.0", NULL),
+	CLK_DUPLICATE("usbd", "tegra-otg", NULL),
+	CLK_DUPLICATE("hdmi", "tegradc.0", "hdmi"),
+	CLK_DUPLICATE("hdmi", "tegradc.1", "hdmi"),
+	CLK_DUPLICATE("pwm", "tegra_pwm.0", NULL),
+	CLK_DUPLICATE("pwm", "tegra_pwm.1", NULL),
+	CLK_DUPLICATE("pwm", "tegra_pwm.2", NULL),
+	CLK_DUPLICATE("pwm", "tegra_pwm.3", NULL),
 };
 
 #define CLK(dev, con, ck)	\
@@ -1732,7 +1909,10 @@ struct clk_lookup tegra_clk_lookups[] = {
 	CLK(NULL,	"hclk",		&tegra_clk_hclk),
 	CLK(NULL,	"pclk",		&tegra_clk_pclk),
 	CLK(NULL,	"clk_d",	&tegra_clk_d),
+	CLK(NULL,	"clk_dev1",	&tegra_dev1_clk),
+	CLK(NULL,	"clk_dev2",	&tegra_dev2_clk),
 	CLK(NULL,	"cpu",		&tegra_clk_virtual_cpu),
+	CLK(NULL,	"blink",	&tegra_clk_blink),
 };
 
 void __init tegra2_init_clocks(void)
@@ -1775,14 +1955,34 @@ void __init tegra2_init_clocks(void)
 
 #ifdef CONFIG_PM
 static u32 clk_rst_suspend[RST_DEVICES_NUM + CLK_OUT_ENB_NUM +
-			   PERIPH_CLK_SOURCE_NUM + 3];
+			   PERIPH_CLK_SOURCE_NUM + 19];
 
 void tegra_clk_suspend(void)
 {
 	unsigned long off, i;
+	u32 pllx_misc;
 	u32 *ctx = clk_rst_suspend;
 
 	*ctx++ = clk_readl(OSC_CTRL) & OSC_CTRL_MASK;
+	*ctx++ = clk_readl(tegra_pll_p.reg + PLL_BASE);
+	*ctx++ = clk_readl(tegra_pll_p.reg + PLL_MISC(&tegra_pll_p));
+	*ctx++ = clk_readl(tegra_pll_c.reg + PLL_BASE);
+	*ctx++ = clk_readl(tegra_pll_c.reg + PLL_MISC(&tegra_pll_c));
+	*ctx++ = clk_readl(tegra_pll_a.reg + PLL_BASE);
+	*ctx++ = clk_readl(tegra_pll_a.reg + PLL_MISC(&tegra_pll_a));
+
+	*ctx++ = clk_readl(tegra_pll_m_out1.reg);
+	*ctx++ = clk_readl(tegra_pll_p_out1.reg);
+	*ctx++ = clk_readl(tegra_pll_p_out3.reg);
+	*ctx++ = clk_readl(tegra_pll_a_out0.reg);
+	*ctx++ = clk_readl(tegra_pll_c_out1.reg);
+
+	*ctx++ = clk_readl(tegra_clk_cclk.reg);
+	*ctx++ = clk_readl(tegra_clk_cclk.reg + SUPER_CLK_DIVIDER);
+
+	*ctx++ = clk_readl(tegra_clk_sclk.reg);
+	*ctx++ = clk_readl(tegra_clk_sclk.reg + SUPER_CLK_DIVIDER);
+	*ctx++ = clk_readl(tegra_clk_pclk.reg);
 
 	for (off = PERIPH_CLK_SOURCE_I2S1; off <= PERIPH_CLK_SOURCE_OSC;
 			off += 4) {
@@ -1801,6 +2001,10 @@ void tegra_clk_suspend(void)
 
 	*ctx++ = clk_readl(MISC_CLK_ENB);
 	*ctx++ = clk_readl(CLK_MASK_ARM);
+
+	pllx_misc = clk_readl(tegra_pll_x.reg + PLL_MISC(&tegra_pll_x));
+	pllx_misc &= ~PLL_MISC_LOCK_ENABLE(&tegra_pll_x);
+	clk_writel(pllx_misc, tegra_pll_x.reg + PLL_MISC(&tegra_pll_x));
 }
 
 void tegra_clk_resume(void)
@@ -1813,6 +2017,27 @@ void tegra_clk_resume(void)
 	val |= *ctx++;
 	clk_writel(val, OSC_CTRL);
 
+	clk_writel(*ctx++, tegra_pll_p.reg + PLL_BASE);
+	clk_writel(*ctx++, tegra_pll_p.reg + PLL_MISC(&tegra_pll_p));
+	clk_writel(*ctx++, tegra_pll_c.reg + PLL_BASE);
+	clk_writel(*ctx++, tegra_pll_c.reg + PLL_MISC(&tegra_pll_c));
+	clk_writel(*ctx++, tegra_pll_a.reg + PLL_BASE);
+	clk_writel(*ctx++, tegra_pll_a.reg + PLL_MISC(&tegra_pll_a));
+	udelay(300);
+
+	clk_writel(*ctx++, tegra_pll_m_out1.reg);
+	clk_writel(*ctx++, tegra_pll_p_out1.reg);
+	clk_writel(*ctx++, tegra_pll_p_out3.reg);
+	clk_writel(*ctx++, tegra_pll_a_out0.reg);
+	clk_writel(*ctx++, tegra_pll_c_out1.reg);
+
+	clk_writel(*ctx++, tegra_clk_cclk.reg);
+	clk_writel(*ctx++, tegra_clk_cclk.reg + SUPER_CLK_DIVIDER);
+
+	clk_writel(*ctx++, tegra_clk_sclk.reg);
+	clk_writel(*ctx++, tegra_clk_sclk.reg + SUPER_CLK_DIVIDER);
+	clk_writel(*ctx++, tegra_clk_pclk.reg);
+
 	/* enable all clocks before configuring clock sources */
 	clk_writel(0xbffffff9ul, CLK_OUT_ENB);
 	clk_writel(0xfefffff7ul, CLK_OUT_ENB + 4);
-- 
1.7.3.1


^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 09/28] ARM: tegra: clock: Suspend fixes, and add new clocks
@ 2011-01-24  2:01   ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-arm-kernel

Save and restore pll and osc state during suspend
Add digital audio clocks
Update clk dev associations
Correct max clock frequencies
Add pll_p as additional cpu clock state
Add values to plld table
Fix register offset for sdmmc4 clock
Add blink timer to tegra2_clocks

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/tegra2_clocks.c |  263 ++++++++++++++++++++++++++++++++---
 1 files changed, 244 insertions(+), 19 deletions(-)

diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index 2dd2b03..7a2926a 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -52,7 +52,7 @@
 #define OSC_CTRL_OSC_FREQ_19_2MHZ	(1<<30)
 #define OSC_CTRL_OSC_FREQ_12MHZ		(2<<30)
 #define OSC_CTRL_OSC_FREQ_26MHZ		(3<<30)
-#define OSC_CTRL_MASK			0x3f2
+#define OSC_CTRL_MASK			(0x3f2 | OSC_CTRL_OSC_FREQ_MASK)
 
 #define OSC_FREQ_DET			0x58
 #define OSC_FREQ_DET_TRIG		(1<<31)
@@ -135,12 +135,29 @@
 #define BUS_CLK_DISABLE			(1<<3)
 #define BUS_CLK_DIV_MASK		0x3
 
+#define PMC_CTRL			0x0
+ #define PMC_CTRL_BLINK_ENB		(1 << 7)
+
+#define PMC_DPD_PADS_ORIDE		0x1c
+ #define PMC_DPD_PADS_ORIDE_BLINK_ENB	(1 << 20)
+
+#define PMC_BLINK_TIMER_DATA_ON_SHIFT	0
+#define PMC_BLINK_TIMER_DATA_ON_MASK	0x7fff
+#define PMC_BLINK_TIMER_ENB		(1 << 15)
+#define PMC_BLINK_TIMER_DATA_OFF_SHIFT	16
+#define PMC_BLINK_TIMER_DATA_OFF_MASK	0xffff
+
 static void __iomem *reg_clk_base = IO_ADDRESS(TEGRA_CLK_RESET_BASE);
+static void __iomem *reg_pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
 
 #define clk_writel(value, reg) \
 	__raw_writel(value, (u32)reg_clk_base + (reg))
 #define clk_readl(reg) \
 	__raw_readl((u32)reg_clk_base + (reg))
+#define pmc_writel(value, reg) \
+	__raw_writel(value, (u32)reg_pmc_base + (reg))
+#define pmc_readl(reg) \
+	__raw_readl((u32)reg_pmc_base + (reg))
 
 unsigned long clk_measure_input_freq(void)
 {
@@ -358,6 +375,9 @@ static int tegra2_cpu_clk_set_rate(struct clk *c, unsigned long rate)
 		return ret;
 	}
 
+	if (rate == c->backup->rate)
+		goto out;
+
 	ret = clk_set_rate_locked(c->main, rate);
 	if (ret) {
 		pr_err("Failed to change cpu pll to %lu\n", rate);
@@ -370,6 +390,7 @@ static int tegra2_cpu_clk_set_rate(struct clk *c, unsigned long rate)
 		return ret;
 	}
 
+out:
 	return 0;
 }
 
@@ -429,6 +450,87 @@ static struct clk_ops tegra_bus_ops = {
 	.set_rate		= tegra2_bus_clk_set_rate,
 };
 
+/* Blink output functions */
+
+static void tegra2_blink_clk_init(struct clk *c)
+{
+	u32 val;
+
+	val = pmc_readl(PMC_CTRL);
+	c->state = (val & PMC_CTRL_BLINK_ENB) ? ON : OFF;
+	c->mul = 1;
+	val = pmc_readl(c->reg);
+
+	if (val & PMC_BLINK_TIMER_ENB) {
+		unsigned int on_off;
+
+		on_off = (val >> PMC_BLINK_TIMER_DATA_ON_SHIFT) &
+			PMC_BLINK_TIMER_DATA_ON_MASK;
+		val >>= PMC_BLINK_TIMER_DATA_OFF_SHIFT;
+		val &= PMC_BLINK_TIMER_DATA_OFF_MASK;
+		on_off += val;
+		/* each tick in the blink timer is 4 32KHz clocks */
+		c->div = on_off * 4;
+	} else {
+		c->div = 1;
+	}
+}
+
+static int tegra2_blink_clk_enable(struct clk *c)
+{
+	u32 val;
+
+	val = pmc_readl(PMC_DPD_PADS_ORIDE);
+	pmc_writel(val | PMC_DPD_PADS_ORIDE_BLINK_ENB, PMC_DPD_PADS_ORIDE);
+
+	val = pmc_readl(PMC_CTRL);
+	pmc_writel(val | PMC_CTRL_BLINK_ENB, PMC_CTRL);
+
+	return 0;
+}
+
+static void tegra2_blink_clk_disable(struct clk *c)
+{
+	u32 val;
+
+	val = pmc_readl(PMC_CTRL);
+	pmc_writel(val & ~PMC_CTRL_BLINK_ENB, PMC_CTRL);
+
+	val = pmc_readl(PMC_DPD_PADS_ORIDE);
+	pmc_writel(val & ~PMC_DPD_PADS_ORIDE_BLINK_ENB, PMC_DPD_PADS_ORIDE);
+}
+
+static int tegra2_blink_clk_set_rate(struct clk *c, unsigned long rate)
+{
+	if (rate >= c->parent->rate) {
+		c->div = 1;
+		pmc_writel(0, c->reg);
+	} else {
+		unsigned int on_off;
+		u32 val;
+
+		on_off = DIV_ROUND_UP(c->parent->rate / 8, rate);
+		c->div = on_off * 8;
+
+		val = (on_off & PMC_BLINK_TIMER_DATA_ON_MASK) <<
+			PMC_BLINK_TIMER_DATA_ON_SHIFT;
+		on_off &= PMC_BLINK_TIMER_DATA_OFF_MASK;
+		on_off <<= PMC_BLINK_TIMER_DATA_OFF_SHIFT;
+		val |= on_off;
+		val |= PMC_BLINK_TIMER_ENB;
+		pmc_writel(val, c->reg);
+	}
+
+	return 0;
+}
+
+static struct clk_ops tegra_blink_clk_ops = {
+	.init			= &tegra2_blink_clk_init,
+	.enable			= &tegra2_blink_clk_enable,
+	.disable		= &tegra2_blink_clk_disable,
+	.set_rate		= &tegra2_blink_clk_set_rate,
+};
+
 /* PLL Functions */
 static int tegra2_pll_clk_wait_for_lock(struct clk *c)
 {
@@ -929,6 +1031,7 @@ static struct clk_ops tegra_clk_double_ops = {
 	.set_rate		= &tegra2_clk_double_set_rate,
 };
 
+/* Audio sync clock ops */
 static void tegra2_audio_sync_clk_init(struct clk *c)
 {
 	int source;
@@ -1007,6 +1110,37 @@ static struct clk_ops tegra_audio_sync_clk_ops = {
 	.set_parent = tegra2_audio_sync_clk_set_parent,
 };
 
+/* cdev1 and cdev2 (dap_mclk1 and dap_mclk2) ops */
+
+static void tegra2_cdev_clk_init(struct clk *c)
+{
+	/* We could un-tristate the cdev1 or cdev2 pingroup here; this is
+	 * currently done in the pinmux code. */
+	c->state = ON;
+	if (!(clk_readl(CLK_OUT_ENB + PERIPH_CLK_TO_ENB_REG(c)) &
+			PERIPH_CLK_TO_ENB_BIT(c)))
+		c->state = OFF;
+}
+
+static int tegra2_cdev_clk_enable(struct clk *c)
+{
+	clk_writel(PERIPH_CLK_TO_ENB_BIT(c),
+		CLK_OUT_ENB_SET + PERIPH_CLK_TO_ENB_SET_REG(c));
+	return 0;
+}
+
+static void tegra2_cdev_clk_disable(struct clk *c)
+{
+	clk_writel(PERIPH_CLK_TO_ENB_BIT(c),
+		CLK_OUT_ENB_CLR + PERIPH_CLK_TO_ENB_SET_REG(c));
+}
+
+static struct clk_ops tegra_cdev_clk_ops = {
+	.init			= &tegra2_cdev_clk_init,
+	.enable			= &tegra2_cdev_clk_enable,
+	.disable		= &tegra2_cdev_clk_disable,
+};
+
 /* Clock definitions */
 static struct clk tegra_clk_32k = {
 	.name = "clk_32k",
@@ -1227,10 +1361,21 @@ static struct clk tegra_pll_a_out0 = {
 };
 
 static struct clk_pll_table tegra_pll_d_table[] = {
+	{ 12000000, 216000000, 216, 12, 1, 4},
+	{ 13000000, 216000000, 216, 13, 1, 4},
+	{ 19200000, 216000000, 135, 12, 1, 3},
+	{ 26000000, 216000000, 216, 26, 1, 4},
+
+	{ 12000000, 594000000, 594, 12, 1, 8},
+	{ 13000000, 594000000, 594, 13, 1, 8},
+	{ 19200000, 594000000, 495, 16, 1, 8},
+	{ 26000000, 594000000, 594, 26, 1, 8},
+
 	{ 12000000, 1000000000, 1000, 12, 1, 12},
 	{ 13000000, 1000000000, 1000, 13, 1, 12},
 	{ 19200000, 1000000000, 625,  12, 1, 8},
 	{ 26000000, 1000000000, 1000, 26, 1, 12},
+
 	{ 0, 0, 0, 0, 0, 0 },
 };
 
@@ -1372,6 +1517,24 @@ static struct clk tegra_clk_d = {
 	.max_rate  = 52000000,
 };
 
+/* dap_mclk1, belongs to the cdev1 pingroup. */
+static struct clk tegra_dev1_clk = {
+	.name      = "clk_dev1",
+	.ops       = &tegra_cdev_clk_ops,
+	.clk_num   = 94,
+	.rate      = 26000000,
+	.max_rate  = 26000000,
+};
+
+/* dap_mclk2, belongs to the cdev2 pingroup. */
+static struct clk tegra_dev2_clk = {
+	.name      = "clk_dev2",
+	.ops       = &tegra_cdev_clk_ops,
+	.clk_num   = 93,
+	.rate      = 26000000,
+	.max_rate  = 26000000,
+};
+
 /* initialized before peripheral clocks */
 static struct clk_mux_sel mux_audio_sync_clk[8+1];
 static const struct audio_sources {
@@ -1486,7 +1649,7 @@ static struct clk tegra_clk_virtual_cpu = {
 	.name      = "cpu",
 	.parent    = &tegra_clk_cclk,
 	.main      = &tegra_pll_x,
-	.backup    = &tegra_clk_m,
+	.backup    = &tegra_pll_p,
 	.ops       = &tegra_cpu_ops,
 	.max_rate  = 1000000000,
 	.dvfs      = &tegra_dvfs_virtual_cpu_dvfs,
@@ -1512,6 +1675,14 @@ static struct clk tegra_clk_pclk = {
 	.max_rate       = 108000000,
 };
 
+static struct clk tegra_clk_blink = {
+	.name		= "blink",
+	.parent		= &tegra_clk_32k,
+	.reg		= 0x40,
+	.ops		= &tegra_blink_clk_ops,
+	.max_rate	= 32768,
+};
+
 static struct clk_mux_sel mux_pllm_pllc_pllp_plla[] = {
 	{ .input = &tegra_pll_m, .value = 0},
 	{ .input = &tegra_pll_c, .value = 1},
@@ -1626,7 +1797,7 @@ struct clk tegra_periph_clks[] = {
 	PERIPH_CLK("sdmmc1",	"sdhci-tegra.0",	NULL,	14,	0x150,	52000000,  mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71), /* scales with voltage */
 	PERIPH_CLK("sdmmc2",	"sdhci-tegra.1",	NULL,	9,	0x154,	52000000,  mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71), /* scales with voltage */
 	PERIPH_CLK("sdmmc3",	"sdhci-tegra.2",	NULL,	69,	0x1bc,	52000000,  mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71), /* scales with voltage */
-	PERIPH_CLK("sdmmc4",	"sdhci-tegra.3",	NULL,	15,	0x160,	52000000,  mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71), /* scales with voltage */
+	PERIPH_CLK("sdmmc4",	"sdhci-tegra.3",	NULL,	15,	0x164,	52000000,  mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71), /* scales with voltage */
 	PERIPH_CLK("vde",	"vde",			NULL,	61,	0x1c8,	250000000, mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71), /* scales with voltage and process_id */
 	PERIPH_CLK("csite",	"csite",		NULL,	73,	0x1d4,	144000000, mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71), /* max rate ??? */
 	/* FIXME: what is la? */
@@ -1642,34 +1813,34 @@ struct clk tegra_periph_clks[] = {
 	PERIPH_CLK("i2c2_i2c",	"tegra-i2c.1",		"i2c",	0,	0,	72000000,  mux_pllp_out3,			0),
 	PERIPH_CLK("i2c3_i2c",	"tegra-i2c.2",		"i2c",	0,	0,	72000000,  mux_pllp_out3,			0),
 	PERIPH_CLK("dvc_i2c",	"tegra-i2c.3",		"i2c",	0,	0,	72000000,  mux_pllp_out3,			0),
-	PERIPH_CLK("uarta",	"uart.0",		NULL,	6,	0x178,	216000000, mux_pllp_pllc_pllm_clkm,	MUX),
-	PERIPH_CLK("uartb",	"uart.1",		NULL,	7,	0x17c,	216000000, mux_pllp_pllc_pllm_clkm,	MUX),
-	PERIPH_CLK("uartc",	"uart.2",		NULL,	55,	0x1a0,	216000000, mux_pllp_pllc_pllm_clkm,	MUX),
-	PERIPH_CLK("uartd",	"uart.3",		NULL,	65,	0x1c0,	216000000, mux_pllp_pllc_pllm_clkm,	MUX),
-	PERIPH_CLK("uarte",	"uart.4",		NULL,	66,	0x1c4,	216000000, mux_pllp_pllc_pllm_clkm,	MUX),
+	PERIPH_CLK("uarta",	"uart.0",		NULL,	6,	0x178,	600000000, mux_pllp_pllc_pllm_clkm,	MUX),
+	PERIPH_CLK("uartb",	"uart.1",		NULL,	7,	0x17c,	600000000, mux_pllp_pllc_pllm_clkm,	MUX),
+	PERIPH_CLK("uartc",	"uart.2",		NULL,	55,	0x1a0,	600000000, mux_pllp_pllc_pllm_clkm,	MUX),
+	PERIPH_CLK("uartd",	"uart.3",		NULL,	65,	0x1c0,	600000000, mux_pllp_pllc_pllm_clkm,	MUX),
+	PERIPH_CLK("uarte",	"uart.4",		NULL,	66,	0x1c4,	600000000, mux_pllp_pllc_pllm_clkm,	MUX),
 	PERIPH_CLK("3d",	"3d",			NULL,	24,	0x158,	300000000, mux_pllm_pllc_pllp_plla,	MUX | DIV_U71 | PERIPH_MANUAL_RESET), /* scales with voltage and process_id */
 	PERIPH_CLK("2d",	"2d",			NULL,	21,	0x15c,	300000000, mux_pllm_pllc_pllp_plla,	MUX | DIV_U71), /* scales with voltage and process_id */
 	/* FIXME: vi and vi_sensor share an enable */
-	PERIPH_CLK("vi",	"vi",			NULL,	20,	0x148,	150000000, mux_pllm_pllc_pllp_plla,	MUX | DIV_U71), /* scales with voltage and process_id */
-	PERIPH_CLK("vi_sensor",	"vi_sensor",		NULL,	20,	0x1a8,	150000000, mux_pllm_pllc_pllp_plla,	MUX | DIV_U71 | PERIPH_NO_RESET), /* scales with voltage and process_id */
+	PERIPH_CLK("vi",	"tegra_camera",		"vi",	20,	0x148,	150000000, mux_pllm_pllc_pllp_plla,	MUX | DIV_U71), /* scales with voltage and process_id */
+	PERIPH_CLK("vi_sensor",	"tegra_camera",		"vi_sensor",	20,	0x1a8,	150000000, mux_pllm_pllc_pllp_plla,	MUX | DIV_U71 | PERIPH_NO_RESET), /* scales with voltage and process_id */
 	PERIPH_CLK("epp",	"epp",			NULL,	19,	0x16c,	300000000, mux_pllm_pllc_pllp_plla,	MUX | DIV_U71), /* scales with voltage and process_id */
 	PERIPH_CLK("mpe",	"mpe",			NULL,	60,	0x170,	250000000, mux_pllm_pllc_pllp_plla,	MUX | DIV_U71), /* scales with voltage and process_id */
 	PERIPH_CLK("host1x",	"host1x",		NULL,	28,	0x180,	166000000, mux_pllm_pllc_pllp_plla,	MUX | DIV_U71), /* scales with voltage and process_id */
 	/* FIXME: cve and tvo share an enable	*/
 	PERIPH_CLK("cve",	"cve",			NULL,	49,	0x140,	250000000, mux_pllp_plld_pllc_clkm,	MUX | DIV_U71), /* requires min voltage */
 	PERIPH_CLK("tvo",	"tvo",			NULL,	49,	0x188,	250000000, mux_pllp_plld_pllc_clkm,	MUX | DIV_U71), /* requires min voltage */
-	PERIPH_CLK("hdmi",	"hdmi",			NULL,	51,	0x18c,	148500000, mux_pllp_plld_pllc_clkm,	MUX | DIV_U71), /* requires min voltage */
+	PERIPH_CLK("hdmi",	"hdmi",			NULL,	51,	0x18c,	600000000, mux_pllp_plld_pllc_clkm,	MUX | DIV_U71), /* requires min voltage */
 	PERIPH_CLK("tvdac",	"tvdac",		NULL,	53,	0x194,	250000000, mux_pllp_plld_pllc_clkm,	MUX | DIV_U71), /* requires min voltage */
-	PERIPH_CLK("disp1",	"tegrafb.0",		NULL,	27,	0x138,	190000000, mux_pllp_plld_pllc_clkm,	MUX | DIV_U71), /* scales with voltage and process_id */
-	PERIPH_CLK("disp2",	"tegrafb.1",		NULL,	26,	0x13c,	190000000, mux_pllp_plld_pllc_clkm,	MUX | DIV_U71), /* scales with voltage and process_id */
+	PERIPH_CLK("disp1",	"tegradc.0",		NULL,	27,	0x138,	600000000, mux_pllp_plld_pllc_clkm,	MUX | DIV_U71), /* scales with voltage and process_id */
+	PERIPH_CLK("disp2",	"tegradc.1",		NULL,	26,	0x13c,	600000000, mux_pllp_plld_pllc_clkm,	MUX | DIV_U71), /* scales with voltage and process_id */
 	PERIPH_CLK("usbd",	"fsl-tegra-udc",	NULL,	22,	0,	480000000, mux_clk_m,			0), /* requires min voltage */
 	PERIPH_CLK("usb2",	"tegra-ehci.1",		NULL,	58,	0,	480000000, mux_clk_m,			0), /* requires min voltage */
 	PERIPH_CLK("usb3",	"tegra-ehci.2",		NULL,	59,	0,	480000000, mux_clk_m,			0), /* requires min voltage */
 	PERIPH_CLK("emc",	"emc",			NULL,	57,	0x19c,	800000000, mux_pllm_pllc_pllp_clkm,	MUX | DIV_U71 | PERIPH_EMC_ENB),
 	PERIPH_CLK("dsi",	"dsi",			NULL,	48,	0,	500000000, mux_plld,			0), /* scales with voltage */
-	PERIPH_CLK("csi",	"csi",			NULL,	52,	0,	72000000,  mux_pllp_out3,		0),
-	PERIPH_CLK("isp",	"isp",			NULL,	23,	0,	150000000, mux_clk_m,			0), /* same frequency as VI */
-	PERIPH_CLK("csus",	"csus",			NULL,	92,	0,	150000000, mux_clk_m,			PERIPH_NO_RESET),
+	PERIPH_CLK("csi",	"tegra_camera",		"csi",	52,	0,	72000000,  mux_pllp_out3,		0),
+	PERIPH_CLK("isp",	"tegra_camera",		"isp",	23,	0,	150000000, mux_clk_m,			0), /* same frequency as VI */
+	PERIPH_CLK("csus",	"tegra_camera",		"csus",	92,	0,	150000000, mux_clk_m,			PERIPH_NO_RESET),
 	PERIPH_CLK("pex",       NULL,			"pex",  70,     0,	26000000,  mux_clk_m,			PERIPH_MANUAL_RESET),
 	PERIPH_CLK("afi",       NULL,			"afi",  72,     0,	26000000,  mux_clk_m,			PERIPH_MANUAL_RESET),
 	PERIPH_CLK("pcie_xclk", NULL,		  "pcie_xclk",  74,     0,	26000000,  mux_clk_m,			PERIPH_MANUAL_RESET),
@@ -1694,9 +1865,15 @@ struct clk_duplicate tegra_clk_duplicates[] = {
 	CLK_DUPLICATE("uartc",	"tegra_uart.2",	NULL),
 	CLK_DUPLICATE("uartd",	"tegra_uart.3",	NULL),
 	CLK_DUPLICATE("uarte",	"tegra_uart.4",	NULL),
-	CLK_DUPLICATE("host1x", "tegrafb.0", "host1x"),
-	CLK_DUPLICATE("host1x", "tegrafb.1", "host1x"),
+	CLK_DUPLICATE("usbd", "utmip-pad", NULL),
 	CLK_DUPLICATE("usbd", "tegra-ehci.0", NULL),
+	CLK_DUPLICATE("usbd", "tegra-otg", NULL),
+	CLK_DUPLICATE("hdmi", "tegradc.0", "hdmi"),
+	CLK_DUPLICATE("hdmi", "tegradc.1", "hdmi"),
+	CLK_DUPLICATE("pwm", "tegra_pwm.0", NULL),
+	CLK_DUPLICATE("pwm", "tegra_pwm.1", NULL),
+	CLK_DUPLICATE("pwm", "tegra_pwm.2", NULL),
+	CLK_DUPLICATE("pwm", "tegra_pwm.3", NULL),
 };
 
 #define CLK(dev, con, ck)	\
@@ -1732,7 +1909,10 @@ struct clk_lookup tegra_clk_lookups[] = {
 	CLK(NULL,	"hclk",		&tegra_clk_hclk),
 	CLK(NULL,	"pclk",		&tegra_clk_pclk),
 	CLK(NULL,	"clk_d",	&tegra_clk_d),
+	CLK(NULL,	"clk_dev1",	&tegra_dev1_clk),
+	CLK(NULL,	"clk_dev2",	&tegra_dev2_clk),
 	CLK(NULL,	"cpu",		&tegra_clk_virtual_cpu),
+	CLK(NULL,	"blink",	&tegra_clk_blink),
 };
 
 void __init tegra2_init_clocks(void)
@@ -1775,14 +1955,34 @@ void __init tegra2_init_clocks(void)
 
 #ifdef CONFIG_PM
 static u32 clk_rst_suspend[RST_DEVICES_NUM + CLK_OUT_ENB_NUM +
-			   PERIPH_CLK_SOURCE_NUM + 3];
+			   PERIPH_CLK_SOURCE_NUM + 19];
 
 void tegra_clk_suspend(void)
 {
 	unsigned long off, i;
+	u32 pllx_misc;
 	u32 *ctx = clk_rst_suspend;
 
 	*ctx++ = clk_readl(OSC_CTRL) & OSC_CTRL_MASK;
+	*ctx++ = clk_readl(tegra_pll_p.reg + PLL_BASE);
+	*ctx++ = clk_readl(tegra_pll_p.reg + PLL_MISC(&tegra_pll_p));
+	*ctx++ = clk_readl(tegra_pll_c.reg + PLL_BASE);
+	*ctx++ = clk_readl(tegra_pll_c.reg + PLL_MISC(&tegra_pll_c));
+	*ctx++ = clk_readl(tegra_pll_a.reg + PLL_BASE);
+	*ctx++ = clk_readl(tegra_pll_a.reg + PLL_MISC(&tegra_pll_a));
+
+	*ctx++ = clk_readl(tegra_pll_m_out1.reg);
+	*ctx++ = clk_readl(tegra_pll_p_out1.reg);
+	*ctx++ = clk_readl(tegra_pll_p_out3.reg);
+	*ctx++ = clk_readl(tegra_pll_a_out0.reg);
+	*ctx++ = clk_readl(tegra_pll_c_out1.reg);
+
+	*ctx++ = clk_readl(tegra_clk_cclk.reg);
+	*ctx++ = clk_readl(tegra_clk_cclk.reg + SUPER_CLK_DIVIDER);
+
+	*ctx++ = clk_readl(tegra_clk_sclk.reg);
+	*ctx++ = clk_readl(tegra_clk_sclk.reg + SUPER_CLK_DIVIDER);
+	*ctx++ = clk_readl(tegra_clk_pclk.reg);
 
 	for (off = PERIPH_CLK_SOURCE_I2S1; off <= PERIPH_CLK_SOURCE_OSC;
 			off += 4) {
@@ -1801,6 +2001,10 @@ void tegra_clk_suspend(void)
 
 	*ctx++ = clk_readl(MISC_CLK_ENB);
 	*ctx++ = clk_readl(CLK_MASK_ARM);
+
+	pllx_misc = clk_readl(tegra_pll_x.reg + PLL_MISC(&tegra_pll_x));
+	pllx_misc &= ~PLL_MISC_LOCK_ENABLE(&tegra_pll_x);
+	clk_writel(pllx_misc, tegra_pll_x.reg + PLL_MISC(&tegra_pll_x));
 }
 
 void tegra_clk_resume(void)
@@ -1813,6 +2017,27 @@ void tegra_clk_resume(void)
 	val |= *ctx++;
 	clk_writel(val, OSC_CTRL);
 
+	clk_writel(*ctx++, tegra_pll_p.reg + PLL_BASE);
+	clk_writel(*ctx++, tegra_pll_p.reg + PLL_MISC(&tegra_pll_p));
+	clk_writel(*ctx++, tegra_pll_c.reg + PLL_BASE);
+	clk_writel(*ctx++, tegra_pll_c.reg + PLL_MISC(&tegra_pll_c));
+	clk_writel(*ctx++, tegra_pll_a.reg + PLL_BASE);
+	clk_writel(*ctx++, tegra_pll_a.reg + PLL_MISC(&tegra_pll_a));
+	udelay(300);
+
+	clk_writel(*ctx++, tegra_pll_m_out1.reg);
+	clk_writel(*ctx++, tegra_pll_p_out1.reg);
+	clk_writel(*ctx++, tegra_pll_p_out3.reg);
+	clk_writel(*ctx++, tegra_pll_a_out0.reg);
+	clk_writel(*ctx++, tegra_pll_c_out1.reg);
+
+	clk_writel(*ctx++, tegra_clk_cclk.reg);
+	clk_writel(*ctx++, tegra_clk_cclk.reg + SUPER_CLK_DIVIDER);
+
+	clk_writel(*ctx++, tegra_clk_sclk.reg);
+	clk_writel(*ctx++, tegra_clk_sclk.reg + SUPER_CLK_DIVIDER);
+	clk_writel(*ctx++, tegra_clk_pclk.reg);
+
 	/* enable all clocks before configuring clock sources */
 	clk_writel(0xbffffff9ul, CLK_OUT_ENB);
 	clk_writel(0xfefffff7ul, CLK_OUT_ENB + 4);
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 10/28] ARM: tegra: pinmux: Add missing drive pingroups and fix suspend
  2011-01-24  2:01 [PATCH v2 00/28] Updates for Tegra support in 2.6.39 Colin Cross
@ 2011-01-24  2:01   ` Colin Cross
  2011-01-24  2:01   ` Colin Cross
                     ` (26 subsequent siblings)
  27 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-tegra
  Cc: linux-arm-kernel, olof, konkers, Gary King, Colin Cross,
	Russell King, linux-kernel

From: Gary King <gking@nvidia.com>

Adds missing drive pingroups, saves all drive pingroups in
suspend, and restores the pinmux registers in the proper order.

Signed-off-by: Gary King <gking@nvidia.com>
Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/include/mach/pinmux-t2.h |   10 ++++++++++
 arch/arm/mach-tegra/pinmux-t2-tables.c       |   25 +++++++++++++++++++++----
 2 files changed, 31 insertions(+), 4 deletions(-)

diff --git a/arch/arm/mach-tegra/include/mach/pinmux-t2.h b/arch/arm/mach-tegra/include/mach/pinmux-t2.h
index e5b9d74..4c26263 100644
--- a/arch/arm/mach-tegra/include/mach/pinmux-t2.h
+++ b/arch/arm/mach-tegra/include/mach/pinmux-t2.h
@@ -167,6 +167,16 @@ enum tegra_drive_pingroup {
 	TEGRA_DRIVE_PINGROUP_XM2D,
 	TEGRA_DRIVE_PINGROUP_XM2CLK,
 	TEGRA_DRIVE_PINGROUP_MEMCOMP,
+	TEGRA_DRIVE_PINGROUP_SDIO1,
+	TEGRA_DRIVE_PINGROUP_CRT,
+	TEGRA_DRIVE_PINGROUP_DDC,
+	TEGRA_DRIVE_PINGROUP_GMA,
+	TEGRA_DRIVE_PINGROUP_GMB,
+	TEGRA_DRIVE_PINGROUP_GMC,
+	TEGRA_DRIVE_PINGROUP_GMD,
+	TEGRA_DRIVE_PINGROUP_GME,
+	TEGRA_DRIVE_PINGROUP_OWR,
+	TEGRA_DRIVE_PINGROUP_UAD,
 	TEGRA_MAX_DRIVE_PINGROUP,
 };
 
diff --git a/arch/arm/mach-tegra/pinmux-t2-tables.c b/arch/arm/mach-tegra/pinmux-t2-tables.c
index 4d97d5c..a475367 100644
--- a/arch/arm/mach-tegra/pinmux-t2-tables.c
+++ b/arch/arm/mach-tegra/pinmux-t2-tables.c
@@ -66,6 +66,16 @@ const struct tegra_drive_pingroup_desc tegra_soc_drive_pingroups[TEGRA_MAX_DRIVE
 	DRIVE_PINGROUP(XM2D,		0x8cc),
 	DRIVE_PINGROUP(XM2CLK,		0x8d0),
 	DRIVE_PINGROUP(MEMCOMP,		0x8d4),
+	DRIVE_PINGROUP(SDIO1,		0x8e0),
+	DRIVE_PINGROUP(CRT,		0x8ec),
+	DRIVE_PINGROUP(DDC,		0x8f0),
+	DRIVE_PINGROUP(GMA,		0x8f4),
+	DRIVE_PINGROUP(GMB,		0x8f8),
+	DRIVE_PINGROUP(GMC,		0x8fc),
+	DRIVE_PINGROUP(GMD,		0x900),
+	DRIVE_PINGROUP(GME,		0x904),
+	DRIVE_PINGROUP(OWR,		0x908),
+	DRIVE_PINGROUP(UAD,		0x90c),
 };
 
 #define PINGROUP(pg_name, vdd, f0, f1, f2, f3, f_safe,		\
@@ -217,7 +227,8 @@ const struct tegra_pingroup_desc tegra_soc_pingroups[TEGRA_MAX_PINGROUP] = {
 #define PULLUPDOWN_REG_NUM     5
 
 static u32 pinmux_reg[TRISTATE_REG_NUM + PIN_MUX_CTL_REG_NUM +
-		     PULLUPDOWN_REG_NUM];
+		      PULLUPDOWN_REG_NUM +
+		      ARRAY_SIZE(tegra_soc_drive_pingroups)];
 
 static inline unsigned long pg_readl(unsigned long offset)
 {
@@ -234,14 +245,17 @@ void tegra_pinmux_suspend(void)
 	unsigned int i;
 	u32 *ctx = pinmux_reg;
 
-	for (i = 0; i < TRISTATE_REG_NUM; i++)
-		*ctx++ = pg_readl(TRISTATE_REG_A + i*4);
-
 	for (i = 0; i < PIN_MUX_CTL_REG_NUM; i++)
 		*ctx++ = pg_readl(PIN_MUX_CTL_REG_A + i*4);
 
 	for (i = 0; i < PULLUPDOWN_REG_NUM; i++)
 		*ctx++ = pg_readl(PULLUPDOWN_REG_A + i*4);
+
+	for (i = 0; i < TRISTATE_REG_NUM; i++)
+		*ctx++ = pg_readl(TRISTATE_REG_A + i*4);
+
+	for (i = 0; i < ARRAY_SIZE(tegra_soc_drive_pingroups); i++)
+		*ctx++ = pg_readl(tegra_soc_drive_pingroups[i].reg);
 }
 
 void tegra_pinmux_resume(void)
@@ -257,5 +271,8 @@ void tegra_pinmux_resume(void)
 
 	for (i = 0; i < TRISTATE_REG_NUM; i++)
 		pg_writel(*ctx++, TRISTATE_REG_A + i*4);
+
+	for (i = 0; i < ARRAY_SIZE(tegra_soc_drive_pingroups); i++)
+		pg_writel(*ctx++, tegra_soc_drive_pingroups[i].reg);
 }
 #endif
-- 
1.7.3.1


^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 10/28] ARM: tegra: pinmux: Add missing drive pingroups and fix suspend
@ 2011-01-24  2:01   ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-arm-kernel

From: Gary King <gking@nvidia.com>

Adds missing drive pingroups, saves all drive pingroups in
suspend, and restores the pinmux registers in the proper order.

Signed-off-by: Gary King <gking@nvidia.com>
Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/include/mach/pinmux-t2.h |   10 ++++++++++
 arch/arm/mach-tegra/pinmux-t2-tables.c       |   25 +++++++++++++++++++++----
 2 files changed, 31 insertions(+), 4 deletions(-)

diff --git a/arch/arm/mach-tegra/include/mach/pinmux-t2.h b/arch/arm/mach-tegra/include/mach/pinmux-t2.h
index e5b9d74..4c26263 100644
--- a/arch/arm/mach-tegra/include/mach/pinmux-t2.h
+++ b/arch/arm/mach-tegra/include/mach/pinmux-t2.h
@@ -167,6 +167,16 @@ enum tegra_drive_pingroup {
 	TEGRA_DRIVE_PINGROUP_XM2D,
 	TEGRA_DRIVE_PINGROUP_XM2CLK,
 	TEGRA_DRIVE_PINGROUP_MEMCOMP,
+	TEGRA_DRIVE_PINGROUP_SDIO1,
+	TEGRA_DRIVE_PINGROUP_CRT,
+	TEGRA_DRIVE_PINGROUP_DDC,
+	TEGRA_DRIVE_PINGROUP_GMA,
+	TEGRA_DRIVE_PINGROUP_GMB,
+	TEGRA_DRIVE_PINGROUP_GMC,
+	TEGRA_DRIVE_PINGROUP_GMD,
+	TEGRA_DRIVE_PINGROUP_GME,
+	TEGRA_DRIVE_PINGROUP_OWR,
+	TEGRA_DRIVE_PINGROUP_UAD,
 	TEGRA_MAX_DRIVE_PINGROUP,
 };
 
diff --git a/arch/arm/mach-tegra/pinmux-t2-tables.c b/arch/arm/mach-tegra/pinmux-t2-tables.c
index 4d97d5c..a475367 100644
--- a/arch/arm/mach-tegra/pinmux-t2-tables.c
+++ b/arch/arm/mach-tegra/pinmux-t2-tables.c
@@ -66,6 +66,16 @@ const struct tegra_drive_pingroup_desc tegra_soc_drive_pingroups[TEGRA_MAX_DRIVE
 	DRIVE_PINGROUP(XM2D,		0x8cc),
 	DRIVE_PINGROUP(XM2CLK,		0x8d0),
 	DRIVE_PINGROUP(MEMCOMP,		0x8d4),
+	DRIVE_PINGROUP(SDIO1,		0x8e0),
+	DRIVE_PINGROUP(CRT,		0x8ec),
+	DRIVE_PINGROUP(DDC,		0x8f0),
+	DRIVE_PINGROUP(GMA,		0x8f4),
+	DRIVE_PINGROUP(GMB,		0x8f8),
+	DRIVE_PINGROUP(GMC,		0x8fc),
+	DRIVE_PINGROUP(GMD,		0x900),
+	DRIVE_PINGROUP(GME,		0x904),
+	DRIVE_PINGROUP(OWR,		0x908),
+	DRIVE_PINGROUP(UAD,		0x90c),
 };
 
 #define PINGROUP(pg_name, vdd, f0, f1, f2, f3, f_safe,		\
@@ -217,7 +227,8 @@ const struct tegra_pingroup_desc tegra_soc_pingroups[TEGRA_MAX_PINGROUP] = {
 #define PULLUPDOWN_REG_NUM     5
 
 static u32 pinmux_reg[TRISTATE_REG_NUM + PIN_MUX_CTL_REG_NUM +
-		     PULLUPDOWN_REG_NUM];
+		      PULLUPDOWN_REG_NUM +
+		      ARRAY_SIZE(tegra_soc_drive_pingroups)];
 
 static inline unsigned long pg_readl(unsigned long offset)
 {
@@ -234,14 +245,17 @@ void tegra_pinmux_suspend(void)
 	unsigned int i;
 	u32 *ctx = pinmux_reg;
 
-	for (i = 0; i < TRISTATE_REG_NUM; i++)
-		*ctx++ = pg_readl(TRISTATE_REG_A + i*4);
-
 	for (i = 0; i < PIN_MUX_CTL_REG_NUM; i++)
 		*ctx++ = pg_readl(PIN_MUX_CTL_REG_A + i*4);
 
 	for (i = 0; i < PULLUPDOWN_REG_NUM; i++)
 		*ctx++ = pg_readl(PULLUPDOWN_REG_A + i*4);
+
+	for (i = 0; i < TRISTATE_REG_NUM; i++)
+		*ctx++ = pg_readl(TRISTATE_REG_A + i*4);
+
+	for (i = 0; i < ARRAY_SIZE(tegra_soc_drive_pingroups); i++)
+		*ctx++ = pg_readl(tegra_soc_drive_pingroups[i].reg);
 }
 
 void tegra_pinmux_resume(void)
@@ -257,5 +271,8 @@ void tegra_pinmux_resume(void)
 
 	for (i = 0; i < TRISTATE_REG_NUM; i++)
 		pg_writel(*ctx++, TRISTATE_REG_A + i*4);
+
+	for (i = 0; i < ARRAY_SIZE(tegra_soc_drive_pingroups); i++)
+		pg_writel(*ctx++, tegra_soc_drive_pingroups[i].reg);
 }
 #endif
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 11/28] ARM: tegra: timer: Add idle and suspend support to timers
  2011-01-24  2:01 [PATCH v2 00/28] Updates for Tegra support in 2.6.39 Colin Cross
@ 2011-01-24  2:01   ` Colin Cross
  2011-01-24  2:01   ` Colin Cross
                     ` (26 subsequent siblings)
  27 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-tegra
  Cc: linux-arm-kernel, olof, konkers, Colin Cross, Russell King, linux-kernel

The Tegra processor cannot be woken from idle by the internal
ARM timers.  Reserve timer 4 to be used as a wake source
during idle.

Implement read_persistent_clock by reading the Tegra RTC
registers that stay running during suspend.

Save and restore the timer configuration register and the
sched_clock value in LP0.

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/timer.c |   95 ++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 93 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-tegra/timer.c b/arch/arm/mach-tegra/timer.c
index 36b53a7..2d0b76f 100644
--- a/arch/arm/mach-tegra/timer.c
+++ b/arch/arm/mach-tegra/timer.c
@@ -38,6 +38,10 @@
 #include "board.h"
 #include "clock.h"
 
+#define RTC_SECONDS            0x08
+#define RTC_SHADOW_SECONDS     0x0c
+#define RTC_MILLISECONDS       0x10
+
 #define TIMERUS_CNTR_1US 0x10
 #define TIMERUS_USEC_CFG 0x14
 #define TIMERUS_CNTR_FREEZE 0x4c
@@ -50,9 +54,11 @@
 #define TIMER_PTV 0x0
 #define TIMER_PCR 0x4
 
-struct tegra_timer;
-
 static void __iomem *timer_reg_base = IO_ADDRESS(TEGRA_TMR1_BASE);
+static void __iomem *rtc_base = IO_ADDRESS(TEGRA_RTC_BASE);
+
+static struct timespec persistent_ts;
+static u64 persistent_ms, last_persistent_ms;
 
 #define timer_writel(value, reg) \
 	__raw_writel(value, (u32)timer_reg_base + (reg))
@@ -133,6 +139,42 @@ static void notrace tegra_update_sched_clock(void)
 	update_sched_clock(&cd, cyc, (u32)~0);
 }
 
+/*
+ * tegra_rtc_read - Reads the Tegra RTC registers
+ * Care must be taken that this funciton is not called while the
+ * tegra_rtc driver could be executing to avoid race conditions
+ * on the RTC shadow register
+ */
+u64 tegra_rtc_read_ms(void)
+{
+	u32 ms = readl(rtc_base + RTC_MILLISECONDS);
+	u32 s = readl(rtc_base + RTC_SHADOW_SECONDS);
+	return (u64)s * MSEC_PER_SEC + ms;
+}
+
+/*
+ * read_persistent_clock -  Return time from a persistent clock.
+ *
+ * Reads the time from a source which isn't disabled during PM, the
+ * 32k sync timer.  Convert the cycles elapsed since last read into
+ * nsecs and adds to a monotonically increasing timespec.
+ * Care must be taken that this funciton is not called while the
+ * tegra_rtc driver could be executing to avoid race conditions
+ * on the RTC shadow register
+ */
+void read_persistent_clock(struct timespec *ts)
+{
+	u64 delta;
+	struct timespec *tsp = &persistent_ts;
+
+	last_persistent_ms = persistent_ms;
+	persistent_ms = tegra_rtc_read_ms();
+	delta = persistent_ms - last_persistent_ms;
+
+	timespec_add_ns(tsp, delta * NSEC_PER_MSEC);
+	*ts = *tsp;
+}
+
 static irqreturn_t tegra_timer_interrupt(int irq, void *dev_id)
 {
 	struct clock_event_device *evt = (struct clock_event_device *)dev_id;
@@ -149,6 +191,20 @@ static struct irqaction tegra_timer_irq = {
 	.irq		= INT_TMR3,
 };
 
+static irqreturn_t tegra_lp2wake_interrupt(int irq, void *dev_id)
+{
+	timer_writel(1<<30, TIMER4_BASE + TIMER_PCR);
+	return IRQ_HANDLED;
+}
+
+static struct irqaction tegra_lp2wake_irq = {
+	.name		= "timer_lp2wake",
+	.flags		= IRQF_DISABLED,
+	.handler	= tegra_lp2wake_interrupt,
+	.dev_id		= NULL,
+	.irq		= INT_TMR4,
+};
+
 static void __init tegra_init_timer(void)
 {
 	unsigned long rate = clk_measure_input_freq();
@@ -189,6 +245,12 @@ static void __init tegra_init_timer(void)
 		BUG();
 	}
 
+	ret = setup_irq(tegra_lp2wake_irq.irq, &tegra_lp2wake_irq);
+	if (ret) {
+		printk(KERN_ERR "Failed to register LP2 timer IRQ: %d\n", ret);
+		BUG();
+	}
+
 	clockevents_calc_mult_shift(&tegra_clockevent, 1000000, 5);
 	tegra_clockevent.max_delta_ns =
 		clockevent_delta2ns(0x1fffffff, &tegra_clockevent);
@@ -204,3 +266,32 @@ static void __init tegra_init_timer(void)
 struct sys_timer tegra_timer = {
 	.init = tegra_init_timer,
 };
+
+void tegra_lp2_set_trigger(unsigned long cycles)
+{
+	timer_writel(0, TIMER4_BASE + TIMER_PTV);
+	if (cycles) {
+		u32 reg = 0x80000000ul | min(0x1ffffffful, cycles);
+		timer_writel(reg, TIMER4_BASE + TIMER_PTV);
+	}
+}
+EXPORT_SYMBOL(tegra_lp2_set_trigger);
+
+unsigned long tegra_lp2_timer_remain(void)
+{
+	return timer_readl(TIMER4_BASE + TIMER_PCR) & 0x1ffffffful;
+}
+
+#ifdef CONFIG_PM
+static u32 usec_config;
+
+void tegra_timer_suspend(void)
+{
+	usec_config = timer_readl(TIMERUS_USEC_CFG);
+}
+
+void tegra_timer_resume(void)
+{
+	timer_writel(usec_config, TIMERUS_USEC_CFG);
+}
+#endif
-- 
1.7.3.1


^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 11/28] ARM: tegra: timer: Add idle and suspend support to timers
@ 2011-01-24  2:01   ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-arm-kernel

The Tegra processor cannot be woken from idle by the internal
ARM timers.  Reserve timer 4 to be used as a wake source
during idle.

Implement read_persistent_clock by reading the Tegra RTC
registers that stay running during suspend.

Save and restore the timer configuration register and the
sched_clock value in LP0.

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/timer.c |   95 ++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 93 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-tegra/timer.c b/arch/arm/mach-tegra/timer.c
index 36b53a7..2d0b76f 100644
--- a/arch/arm/mach-tegra/timer.c
+++ b/arch/arm/mach-tegra/timer.c
@@ -38,6 +38,10 @@
 #include "board.h"
 #include "clock.h"
 
+#define RTC_SECONDS            0x08
+#define RTC_SHADOW_SECONDS     0x0c
+#define RTC_MILLISECONDS       0x10
+
 #define TIMERUS_CNTR_1US 0x10
 #define TIMERUS_USEC_CFG 0x14
 #define TIMERUS_CNTR_FREEZE 0x4c
@@ -50,9 +54,11 @@
 #define TIMER_PTV 0x0
 #define TIMER_PCR 0x4
 
-struct tegra_timer;
-
 static void __iomem *timer_reg_base = IO_ADDRESS(TEGRA_TMR1_BASE);
+static void __iomem *rtc_base = IO_ADDRESS(TEGRA_RTC_BASE);
+
+static struct timespec persistent_ts;
+static u64 persistent_ms, last_persistent_ms;
 
 #define timer_writel(value, reg) \
 	__raw_writel(value, (u32)timer_reg_base + (reg))
@@ -133,6 +139,42 @@ static void notrace tegra_update_sched_clock(void)
 	update_sched_clock(&cd, cyc, (u32)~0);
 }
 
+/*
+ * tegra_rtc_read - Reads the Tegra RTC registers
+ * Care must be taken that this funciton is not called while the
+ * tegra_rtc driver could be executing to avoid race conditions
+ * on the RTC shadow register
+ */
+u64 tegra_rtc_read_ms(void)
+{
+	u32 ms = readl(rtc_base + RTC_MILLISECONDS);
+	u32 s = readl(rtc_base + RTC_SHADOW_SECONDS);
+	return (u64)s * MSEC_PER_SEC + ms;
+}
+
+/*
+ * read_persistent_clock -  Return time from a persistent clock.
+ *
+ * Reads the time from a source which isn't disabled during PM, the
+ * 32k sync timer.  Convert the cycles elapsed since last read into
+ * nsecs and adds to a monotonically increasing timespec.
+ * Care must be taken that this funciton is not called while the
+ * tegra_rtc driver could be executing to avoid race conditions
+ * on the RTC shadow register
+ */
+void read_persistent_clock(struct timespec *ts)
+{
+	u64 delta;
+	struct timespec *tsp = &persistent_ts;
+
+	last_persistent_ms = persistent_ms;
+	persistent_ms = tegra_rtc_read_ms();
+	delta = persistent_ms - last_persistent_ms;
+
+	timespec_add_ns(tsp, delta * NSEC_PER_MSEC);
+	*ts = *tsp;
+}
+
 static irqreturn_t tegra_timer_interrupt(int irq, void *dev_id)
 {
 	struct clock_event_device *evt = (struct clock_event_device *)dev_id;
@@ -149,6 +191,20 @@ static struct irqaction tegra_timer_irq = {
 	.irq		= INT_TMR3,
 };
 
+static irqreturn_t tegra_lp2wake_interrupt(int irq, void *dev_id)
+{
+	timer_writel(1<<30, TIMER4_BASE + TIMER_PCR);
+	return IRQ_HANDLED;
+}
+
+static struct irqaction tegra_lp2wake_irq = {
+	.name		= "timer_lp2wake",
+	.flags		= IRQF_DISABLED,
+	.handler	= tegra_lp2wake_interrupt,
+	.dev_id		= NULL,
+	.irq		= INT_TMR4,
+};
+
 static void __init tegra_init_timer(void)
 {
 	unsigned long rate = clk_measure_input_freq();
@@ -189,6 +245,12 @@ static void __init tegra_init_timer(void)
 		BUG();
 	}
 
+	ret = setup_irq(tegra_lp2wake_irq.irq, &tegra_lp2wake_irq);
+	if (ret) {
+		printk(KERN_ERR "Failed to register LP2 timer IRQ: %d\n", ret);
+		BUG();
+	}
+
 	clockevents_calc_mult_shift(&tegra_clockevent, 1000000, 5);
 	tegra_clockevent.max_delta_ns =
 		clockevent_delta2ns(0x1fffffff, &tegra_clockevent);
@@ -204,3 +266,32 @@ static void __init tegra_init_timer(void)
 struct sys_timer tegra_timer = {
 	.init = tegra_init_timer,
 };
+
+void tegra_lp2_set_trigger(unsigned long cycles)
+{
+	timer_writel(0, TIMER4_BASE + TIMER_PTV);
+	if (cycles) {
+		u32 reg = 0x80000000ul | min(0x1ffffffful, cycles);
+		timer_writel(reg, TIMER4_BASE + TIMER_PTV);
+	}
+}
+EXPORT_SYMBOL(tegra_lp2_set_trigger);
+
+unsigned long tegra_lp2_timer_remain(void)
+{
+	return timer_readl(TIMER4_BASE + TIMER_PCR) & 0x1ffffffful;
+}
+
+#ifdef CONFIG_PM
+static u32 usec_config;
+
+void tegra_timer_suspend(void)
+{
+	usec_config = timer_readl(TIMERUS_USEC_CFG);
+}
+
+void tegra_timer_resume(void)
+{
+	timer_writel(usec_config, TIMERUS_USEC_CFG);
+}
+#endif
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 12/28] ARM: tegra: irq: Add support for suspend wake sources
  2011-01-24  2:01 [PATCH v2 00/28] Updates for Tegra support in 2.6.39 Colin Cross
@ 2011-01-24  2:01   ` Colin Cross
  2011-01-24  2:01   ` Colin Cross
                     ` (26 subsequent siblings)
  27 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-tegra
  Cc: linux-arm-kernel, olof, konkers, Colin Cross, Russell King, linux-kernel

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/include/mach/legacy_irq.h |    4 +
 arch/arm/mach-tegra/irq.c                     |  190 ++++++++++---------------
 arch/arm/mach-tegra/legacy_irq.c              |  109 ++++++++++++++-
 3 files changed, 187 insertions(+), 116 deletions(-)

diff --git a/arch/arm/mach-tegra/include/mach/legacy_irq.h b/arch/arm/mach-tegra/include/mach/legacy_irq.h
index db1eb3d..d898c0e 100644
--- a/arch/arm/mach-tegra/include/mach/legacy_irq.h
+++ b/arch/arm/mach-tegra/include/mach/legacy_irq.h
@@ -27,5 +27,9 @@ int tegra_legacy_force_irq_status(unsigned int irq);
 void tegra_legacy_select_fiq(unsigned int irq, bool fiq);
 unsigned long tegra_legacy_vfiq(int nr);
 unsigned long tegra_legacy_class(int nr);
+int tegra_legacy_irq_set_wake(int irq, int enable);
+void tegra_legacy_irq_set_lp1_wake_mask(void);
+void tegra_legacy_irq_restore_mask(void);
+void tegra_init_legacy_irq(void);
 
 #endif
diff --git a/arch/arm/mach-tegra/irq.c b/arch/arm/mach-tegra/irq.c
index 5f065f9..4892394 100644
--- a/arch/arm/mach-tegra/irq.c
+++ b/arch/arm/mach-tegra/irq.c
@@ -18,6 +18,7 @@
  */
 
 #include <linux/kernel.h>
+#include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
@@ -26,147 +27,112 @@
 #include <asm/hardware/gic.h>
 
 #include <mach/iomap.h>
+#include <mach/legacy_irq.h>
 #include <mach/suspend.h>
 
 #include "board.h"
 
-#define INT_SYS_NR	(INT_GPIO_BASE - INT_PRI_BASE)
-#define INT_SYS_SZ	(INT_SEC_BASE - INT_PRI_BASE)
-#define PPI_NR		((INT_SYS_NR+INT_SYS_SZ-1)/INT_SYS_SZ)
+#define PMC_CTRL		0x0
+#define PMC_CTRL_LATCH_WAKEUPS	(1 << 5)
+#define PMC_WAKE_MASK		0xc
+#define PMC_WAKE_LEVEL		0x10
+#define PMC_WAKE_STATUS		0x14
+#define PMC_SW_WAKE_STATUS	0x18
+#define PMC_DPD_SAMPLE		0x20
 
-#define APBDMA_IRQ_STA_CPU  0x14
-#define APBDMA_IRQ_MASK_SET 0x20
-#define APBDMA_IRQ_MASK_CLR 0x24
+static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
 
-#define ICTLR_CPU_IER		0x20
-#define ICTLR_CPU_IER_SET	0x24
-#define ICTLR_CPU_IER_CLR	0x28
-#define ICTLR_CPU_IEP_CLASS	0x2c
-#define ICTLR_COP_IER		0x30
-#define ICTLR_COP_IER_SET	0x34
-#define ICTLR_COP_IER_CLR	0x38
-#define ICTLR_COP_IEP_CLASS	0x3c
+static u32 tegra_lp0_wake_enb;
+static u32 tegra_lp0_wake_level;
+static u32 tegra_lp0_wake_level_any;
 
-static void (*tegra_gic_mask_irq)(struct irq_data *d);
-static void (*tegra_gic_unmask_irq)(struct irq_data *d);
-
-#define irq_to_ictlr(irq) (((irq) - 32) >> 5)
-static void __iomem *tegra_ictlr_base = IO_ADDRESS(TEGRA_PRIMARY_ICTLR_BASE);
-#define ictlr_to_virt(ictlr) (tegra_ictlr_base + (ictlr) * 0x100)
+/* ensures that sufficient time is passed for a register write to
+ * serialize into the 32KHz domain */
+static void pmc_32kwritel(u32 val, unsigned long offs)
+{
+	writel(val, pmc + offs);
+	udelay(130);
+}
 
-static void tegra_mask(struct irq_data *d)
+int tegra_set_lp1_wake(int irq, int enable)
 {
-	void __iomem *addr = ictlr_to_virt(irq_to_ictlr(d->irq));
-	tegra_gic_mask_irq(d);
-	writel(1 << (d->irq & 31), addr+ICTLR_CPU_IER_CLR);
+	return tegra_legacy_irq_set_wake(irq, enable);
 }
 
-static void tegra_unmask(struct irq_data *d)
+void tegra_set_lp0_wake_pads(u32 wake_enb, u32 wake_level, u32 wake_any)
 {
-	void __iomem *addr = ictlr_to_virt(irq_to_ictlr(d->irq));
-	tegra_gic_unmask_irq(d);
-	writel(1<<(d->irq&31), addr+ICTLR_CPU_IER_SET);
+	u32 temp;
+	u32 status;
+	u32 lvl;
+
+	wake_level &= wake_enb;
+	wake_any &= wake_enb;
+
+	wake_level |= (tegra_lp0_wake_level & tegra_lp0_wake_enb);
+	wake_any |= (tegra_lp0_wake_level_any & tegra_lp0_wake_enb);
+
+	wake_enb |= tegra_lp0_wake_enb;
+
+	pmc_32kwritel(0, PMC_SW_WAKE_STATUS);
+	temp = readl(pmc + PMC_CTRL);
+	temp |= PMC_CTRL_LATCH_WAKEUPS;
+	pmc_32kwritel(temp, PMC_CTRL);
+	temp &= ~PMC_CTRL_LATCH_WAKEUPS;
+	pmc_32kwritel(temp, PMC_CTRL);
+	status = readl(pmc + PMC_SW_WAKE_STATUS);
+	lvl = readl(pmc + PMC_WAKE_LEVEL);
+
+	/* flip the wakeup trigger for any-edge triggered pads
+	 * which are currently asserting as wakeups */
+	lvl ^= status;
+	lvl &= wake_any;
+
+	wake_level |= lvl;
+
+	writel(wake_level, pmc + PMC_WAKE_LEVEL);
+	/* Enable DPD sample to trigger sampling pads data and direction
+	 * in which pad will be driven during lp0 mode*/
+	writel(0x1, pmc + PMC_DPD_SAMPLE);
+
+	writel(wake_enb, pmc + PMC_WAKE_MASK);
 }
 
-#ifdef CONFIG_PM
+static void tegra_mask(struct irq_data *d)
+{
+	gic_mask_irq(d);
+	tegra_legacy_mask_irq(d->irq);
+}
 
-static int tegra_set_wake(struct irq_data *d, unsigned int on)
+static void tegra_unmask(struct irq_data *d)
 {
-	return 0;
+	gic_unmask_irq(d);
+	tegra_legacy_unmask_irq(d->irq);
 }
-#endif
 
 static struct irq_chip tegra_irq = {
-	.name		= "PPI",
-	.irq_mask	= tegra_mask,
-	.irq_unmask	= tegra_unmask,
-#ifdef CONFIG_PM
-	.irq_set_wake	= tegra_set_wake,
+	.name			= "PPI",
+	.irq_ack		= gic_ack_irq,
+	.irq_mask		= tegra_mask,
+	.irq_unmask		= tegra_unmask,
+#ifdef CONFIG_SMP
+	.irq_set_affinity	= gic_set_cpu,
 #endif
 };
 
 void __init tegra_init_irq(void)
 {
-	struct irq_chip *gic;
 	unsigned int i;
+	int irq;
 
-	for (i = 0; i < PPI_NR; i++) {
-		writel(~0, ictlr_to_virt(i) + ICTLR_CPU_IER_CLR);
-		writel(0, ictlr_to_virt(i) + ICTLR_CPU_IEP_CLASS);
-	}
+	tegra_init_legacy_irq();
 
 	gic_init(0, 29, IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE),
 		 IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100));
 
-	gic = get_irq_chip(29);
-	tegra_gic_unmask_irq = gic->irq_unmask;
-	tegra_gic_mask_irq = gic->irq_mask;
-	tegra_irq.irq_ack = gic->irq_ack;
-#ifdef CONFIG_SMP
-	tegra_irq.irq_set_affinity = gic->irq_set_affinity;
-#endif
-
-	for (i = INT_PRI_BASE; i < INT_GPIO_BASE; i++) {
-		set_irq_chip(i, &tegra_irq);
-		set_irq_handler(i, handle_level_irq);
-		set_irq_flags(i, IRQF_VALID);
-	}
-}
-
-#ifdef CONFIG_PM
-static u32 cop_ier[PPI_NR];
-static u32 cpu_ier[PPI_NR];
-static u32 cpu_iep[PPI_NR];
-
-void tegra_irq_suspend(void)
-{
-	unsigned long flags;
-	int i;
-
-	for (i = INT_PRI_BASE; i < INT_GPIO_BASE; i++) {
-		struct irq_desc *desc = irq_to_desc(i);
-		if (!desc)
-			continue;
-		if (desc->status & IRQ_WAKEUP) {
-			pr_debug("irq %d is wakeup\n", i);
-			continue;
-		}
-		disable_irq(i);
-	}
-
-	local_irq_save(flags);
-	for (i = 0; i < PPI_NR; i++) {
-		void __iomem *ictlr = ictlr_to_virt(i);
-		cpu_ier[i] = readl(ictlr + ICTLR_CPU_IER);
-		cpu_iep[i] = readl(ictlr + ICTLR_CPU_IEP_CLASS);
-		cop_ier[i] = readl(ictlr + ICTLR_COP_IER);
-		writel(~0, ictlr + ICTLR_COP_IER_CLR);
+	for (i = 0; i < INT_MAIN_NR; i++) {
+		irq = INT_PRI_BASE + i;
+		set_irq_chip(irq, &tegra_irq);
+		set_irq_handler(irq, handle_level_irq);
+		set_irq_flags(irq, IRQF_VALID);
 	}
-	local_irq_restore(flags);
 }
-
-void tegra_irq_resume(void)
-{
-	unsigned long flags;
-	int i;
-
-	local_irq_save(flags);
-	for (i = 0; i < PPI_NR; i++) {
-		void __iomem *ictlr = ictlr_to_virt(i);
-		writel(cpu_iep[i], ictlr + ICTLR_CPU_IEP_CLASS);
-		writel(~0ul, ictlr + ICTLR_CPU_IER_CLR);
-		writel(cpu_ier[i], ictlr + ICTLR_CPU_IER_SET);
-		writel(0, ictlr + ICTLR_COP_IEP_CLASS);
-		writel(~0ul, ictlr + ICTLR_COP_IER_CLR);
-		writel(cop_ier[i], ictlr + ICTLR_COP_IER_SET);
-	}
-	local_irq_restore(flags);
-
-	for (i = INT_PRI_BASE; i < INT_GPIO_BASE; i++) {
-		struct irq_desc *desc = irq_to_desc(i);
-		if (!desc || (desc->status & IRQ_WAKEUP))
-			continue;
-		enable_irq(i);
-	}
-}
-#endif
diff --git a/arch/arm/mach-tegra/legacy_irq.c b/arch/arm/mach-tegra/legacy_irq.c
index 7cc8601..38eb719 100644
--- a/arch/arm/mach-tegra/legacy_irq.c
+++ b/arch/arm/mach-tegra/legacy_irq.c
@@ -18,17 +18,30 @@
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <mach/iomap.h>
+#include <mach/irqs.h>
 #include <mach/legacy_irq.h>
 
-#define ICTLR_CPU_IER		0x20
-#define ICTLR_CPU_IER_SET	0x24
-#define ICTLR_CPU_IER_CLR	0x28
-#define ICTLR_CPU_IEP_CLASS	0x2C
+#define INT_SYS_NR	(INT_GPIO_BASE - INT_PRI_BASE)
+#define INT_SYS_SZ	(INT_SEC_BASE - INT_PRI_BASE)
+#define PPI_NR		((INT_SYS_NR+INT_SYS_SZ-1)/INT_SYS_SZ)
+
 #define ICTLR_CPU_IEP_VFIQ	0x08
 #define ICTLR_CPU_IEP_FIR	0x14
 #define ICTLR_CPU_IEP_FIR_SET	0x18
 #define ICTLR_CPU_IEP_FIR_CLR	0x1c
 
+#define ICTLR_CPU_IER		0x20
+#define ICTLR_CPU_IER_SET	0x24
+#define ICTLR_CPU_IER_CLR	0x28
+#define ICTLR_CPU_IEP_CLASS	0x2C
+
+#define ICTLR_COP_IER		0x30
+#define ICTLR_COP_IER_SET	0x34
+#define ICTLR_COP_IER_CLR	0x38
+#define ICTLR_COP_IEP_CLASS	0x3c
+
+#define NUM_ICTLRS 4
+
 static void __iomem *ictlr_reg_base[] = {
 	IO_ADDRESS(TEGRA_PRIMARY_ICTLR_BASE),
 	IO_ADDRESS(TEGRA_SECONDARY_ICTLR_BASE),
@@ -36,6 +49,9 @@ static void __iomem *ictlr_reg_base[] = {
 	IO_ADDRESS(TEGRA_QUATERNARY_ICTLR_BASE),
 };
 
+static u32 tegra_legacy_wake_mask[4];
+static u32 tegra_legacy_saved_mask[4];
+
 /* When going into deep sleep, the CPU is powered down, taking the GIC with it
    In order to wake, the wake interrupts need to be enabled in the legacy
    interrupt controller. */
@@ -112,3 +128,88 @@ unsigned long tegra_legacy_class(int nr)
 	base = ictlr_reg_base[nr];
 	return readl(base + ICTLR_CPU_IEP_CLASS);
 }
+
+int tegra_legacy_irq_set_wake(int irq, int enable)
+{
+	irq -= 32;
+	if (enable)
+		tegra_legacy_wake_mask[irq >> 5] |= 1 << (irq & 31);
+	else
+		tegra_legacy_wake_mask[irq >> 5] &= ~(1 << (irq & 31));
+
+	return 0;
+}
+
+void tegra_legacy_irq_set_lp1_wake_mask(void)
+{
+	void __iomem *base;
+	int i;
+
+	for (i = 0; i < NUM_ICTLRS; i++) {
+		base = ictlr_reg_base[i];
+		tegra_legacy_saved_mask[i] = readl(base + ICTLR_CPU_IER);
+		writel(tegra_legacy_wake_mask[i], base + ICTLR_CPU_IER);
+	}
+}
+
+void tegra_legacy_irq_restore_mask(void)
+{
+	void __iomem *base;
+	int i;
+
+	for (i = 0; i < NUM_ICTLRS; i++) {
+		base = ictlr_reg_base[i];
+		writel(tegra_legacy_saved_mask[i], base + ICTLR_CPU_IER);
+	}
+}
+
+void tegra_init_legacy_irq(void)
+{
+	int i;
+
+	for (i = 0; i < NUM_ICTLRS; i++) {
+		void __iomem *ictlr = ictlr_reg_base[i];
+		writel(~0, ictlr + ICTLR_CPU_IER_CLR);
+		writel(0, ictlr + ICTLR_CPU_IEP_CLASS);
+	}
+}
+
+#ifdef CONFIG_PM
+static u32 cop_ier[NUM_ICTLRS];
+static u32 cpu_ier[NUM_ICTLRS];
+static u32 cpu_iep[NUM_ICTLRS];
+
+void tegra_irq_suspend(void)
+{
+	unsigned long flags;
+	int i;
+
+	local_irq_save(flags);
+	for (i = 0; i < NUM_ICTLRS; i++) {
+		void __iomem *ictlr = ictlr_reg_base[i];
+		cpu_ier[i] = readl(ictlr + ICTLR_CPU_IER);
+		cpu_iep[i] = readl(ictlr + ICTLR_CPU_IEP_CLASS);
+		cop_ier[i] = readl(ictlr + ICTLR_COP_IER);
+		writel(~0, ictlr + ICTLR_COP_IER_CLR);
+	}
+	local_irq_restore(flags);
+}
+
+void tegra_irq_resume(void)
+{
+	unsigned long flags;
+	int i;
+
+	local_irq_save(flags);
+	for (i = 0; i < NUM_ICTLRS; i++) {
+		void __iomem *ictlr = ictlr_reg_base[i];
+		writel(cpu_iep[i], ictlr + ICTLR_CPU_IEP_CLASS);
+		writel(~0ul, ictlr + ICTLR_CPU_IER_CLR);
+		writel(cpu_ier[i], ictlr + ICTLR_CPU_IER_SET);
+		writel(0, ictlr + ICTLR_COP_IEP_CLASS);
+		writel(~0ul, ictlr + ICTLR_COP_IER_CLR);
+		writel(cop_ier[i], ictlr + ICTLR_COP_IER_SET);
+	}
+	local_irq_restore(flags);
+}
+#endif
-- 
1.7.3.1


^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 12/28] ARM: tegra: irq: Add support for suspend wake sources
@ 2011-01-24  2:01   ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/include/mach/legacy_irq.h |    4 +
 arch/arm/mach-tegra/irq.c                     |  190 ++++++++++---------------
 arch/arm/mach-tegra/legacy_irq.c              |  109 ++++++++++++++-
 3 files changed, 187 insertions(+), 116 deletions(-)

diff --git a/arch/arm/mach-tegra/include/mach/legacy_irq.h b/arch/arm/mach-tegra/include/mach/legacy_irq.h
index db1eb3d..d898c0e 100644
--- a/arch/arm/mach-tegra/include/mach/legacy_irq.h
+++ b/arch/arm/mach-tegra/include/mach/legacy_irq.h
@@ -27,5 +27,9 @@ int tegra_legacy_force_irq_status(unsigned int irq);
 void tegra_legacy_select_fiq(unsigned int irq, bool fiq);
 unsigned long tegra_legacy_vfiq(int nr);
 unsigned long tegra_legacy_class(int nr);
+int tegra_legacy_irq_set_wake(int irq, int enable);
+void tegra_legacy_irq_set_lp1_wake_mask(void);
+void tegra_legacy_irq_restore_mask(void);
+void tegra_init_legacy_irq(void);
 
 #endif
diff --git a/arch/arm/mach-tegra/irq.c b/arch/arm/mach-tegra/irq.c
index 5f065f9..4892394 100644
--- a/arch/arm/mach-tegra/irq.c
+++ b/arch/arm/mach-tegra/irq.c
@@ -18,6 +18,7 @@
  */
 
 #include <linux/kernel.h>
+#include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
@@ -26,147 +27,112 @@
 #include <asm/hardware/gic.h>
 
 #include <mach/iomap.h>
+#include <mach/legacy_irq.h>
 #include <mach/suspend.h>
 
 #include "board.h"
 
-#define INT_SYS_NR	(INT_GPIO_BASE - INT_PRI_BASE)
-#define INT_SYS_SZ	(INT_SEC_BASE - INT_PRI_BASE)
-#define PPI_NR		((INT_SYS_NR+INT_SYS_SZ-1)/INT_SYS_SZ)
+#define PMC_CTRL		0x0
+#define PMC_CTRL_LATCH_WAKEUPS	(1 << 5)
+#define PMC_WAKE_MASK		0xc
+#define PMC_WAKE_LEVEL		0x10
+#define PMC_WAKE_STATUS		0x14
+#define PMC_SW_WAKE_STATUS	0x18
+#define PMC_DPD_SAMPLE		0x20
 
-#define APBDMA_IRQ_STA_CPU  0x14
-#define APBDMA_IRQ_MASK_SET 0x20
-#define APBDMA_IRQ_MASK_CLR 0x24
+static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
 
-#define ICTLR_CPU_IER		0x20
-#define ICTLR_CPU_IER_SET	0x24
-#define ICTLR_CPU_IER_CLR	0x28
-#define ICTLR_CPU_IEP_CLASS	0x2c
-#define ICTLR_COP_IER		0x30
-#define ICTLR_COP_IER_SET	0x34
-#define ICTLR_COP_IER_CLR	0x38
-#define ICTLR_COP_IEP_CLASS	0x3c
+static u32 tegra_lp0_wake_enb;
+static u32 tegra_lp0_wake_level;
+static u32 tegra_lp0_wake_level_any;
 
-static void (*tegra_gic_mask_irq)(struct irq_data *d);
-static void (*tegra_gic_unmask_irq)(struct irq_data *d);
-
-#define irq_to_ictlr(irq) (((irq) - 32) >> 5)
-static void __iomem *tegra_ictlr_base = IO_ADDRESS(TEGRA_PRIMARY_ICTLR_BASE);
-#define ictlr_to_virt(ictlr) (tegra_ictlr_base + (ictlr) * 0x100)
+/* ensures that sufficient time is passed for a register write to
+ * serialize into the 32KHz domain */
+static void pmc_32kwritel(u32 val, unsigned long offs)
+{
+	writel(val, pmc + offs);
+	udelay(130);
+}
 
-static void tegra_mask(struct irq_data *d)
+int tegra_set_lp1_wake(int irq, int enable)
 {
-	void __iomem *addr = ictlr_to_virt(irq_to_ictlr(d->irq));
-	tegra_gic_mask_irq(d);
-	writel(1 << (d->irq & 31), addr+ICTLR_CPU_IER_CLR);
+	return tegra_legacy_irq_set_wake(irq, enable);
 }
 
-static void tegra_unmask(struct irq_data *d)
+void tegra_set_lp0_wake_pads(u32 wake_enb, u32 wake_level, u32 wake_any)
 {
-	void __iomem *addr = ictlr_to_virt(irq_to_ictlr(d->irq));
-	tegra_gic_unmask_irq(d);
-	writel(1<<(d->irq&31), addr+ICTLR_CPU_IER_SET);
+	u32 temp;
+	u32 status;
+	u32 lvl;
+
+	wake_level &= wake_enb;
+	wake_any &= wake_enb;
+
+	wake_level |= (tegra_lp0_wake_level & tegra_lp0_wake_enb);
+	wake_any |= (tegra_lp0_wake_level_any & tegra_lp0_wake_enb);
+
+	wake_enb |= tegra_lp0_wake_enb;
+
+	pmc_32kwritel(0, PMC_SW_WAKE_STATUS);
+	temp = readl(pmc + PMC_CTRL);
+	temp |= PMC_CTRL_LATCH_WAKEUPS;
+	pmc_32kwritel(temp, PMC_CTRL);
+	temp &= ~PMC_CTRL_LATCH_WAKEUPS;
+	pmc_32kwritel(temp, PMC_CTRL);
+	status = readl(pmc + PMC_SW_WAKE_STATUS);
+	lvl = readl(pmc + PMC_WAKE_LEVEL);
+
+	/* flip the wakeup trigger for any-edge triggered pads
+	 * which are currently asserting as wakeups */
+	lvl ^= status;
+	lvl &= wake_any;
+
+	wake_level |= lvl;
+
+	writel(wake_level, pmc + PMC_WAKE_LEVEL);
+	/* Enable DPD sample to trigger sampling pads data and direction
+	 * in which pad will be driven during lp0 mode*/
+	writel(0x1, pmc + PMC_DPD_SAMPLE);
+
+	writel(wake_enb, pmc + PMC_WAKE_MASK);
 }
 
-#ifdef CONFIG_PM
+static void tegra_mask(struct irq_data *d)
+{
+	gic_mask_irq(d);
+	tegra_legacy_mask_irq(d->irq);
+}
 
-static int tegra_set_wake(struct irq_data *d, unsigned int on)
+static void tegra_unmask(struct irq_data *d)
 {
-	return 0;
+	gic_unmask_irq(d);
+	tegra_legacy_unmask_irq(d->irq);
 }
-#endif
 
 static struct irq_chip tegra_irq = {
-	.name		= "PPI",
-	.irq_mask	= tegra_mask,
-	.irq_unmask	= tegra_unmask,
-#ifdef CONFIG_PM
-	.irq_set_wake	= tegra_set_wake,
+	.name			= "PPI",
+	.irq_ack		= gic_ack_irq,
+	.irq_mask		= tegra_mask,
+	.irq_unmask		= tegra_unmask,
+#ifdef CONFIG_SMP
+	.irq_set_affinity	= gic_set_cpu,
 #endif
 };
 
 void __init tegra_init_irq(void)
 {
-	struct irq_chip *gic;
 	unsigned int i;
+	int irq;
 
-	for (i = 0; i < PPI_NR; i++) {
-		writel(~0, ictlr_to_virt(i) + ICTLR_CPU_IER_CLR);
-		writel(0, ictlr_to_virt(i) + ICTLR_CPU_IEP_CLASS);
-	}
+	tegra_init_legacy_irq();
 
 	gic_init(0, 29, IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE),
 		 IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100));
 
-	gic = get_irq_chip(29);
-	tegra_gic_unmask_irq = gic->irq_unmask;
-	tegra_gic_mask_irq = gic->irq_mask;
-	tegra_irq.irq_ack = gic->irq_ack;
-#ifdef CONFIG_SMP
-	tegra_irq.irq_set_affinity = gic->irq_set_affinity;
-#endif
-
-	for (i = INT_PRI_BASE; i < INT_GPIO_BASE; i++) {
-		set_irq_chip(i, &tegra_irq);
-		set_irq_handler(i, handle_level_irq);
-		set_irq_flags(i, IRQF_VALID);
-	}
-}
-
-#ifdef CONFIG_PM
-static u32 cop_ier[PPI_NR];
-static u32 cpu_ier[PPI_NR];
-static u32 cpu_iep[PPI_NR];
-
-void tegra_irq_suspend(void)
-{
-	unsigned long flags;
-	int i;
-
-	for (i = INT_PRI_BASE; i < INT_GPIO_BASE; i++) {
-		struct irq_desc *desc = irq_to_desc(i);
-		if (!desc)
-			continue;
-		if (desc->status & IRQ_WAKEUP) {
-			pr_debug("irq %d is wakeup\n", i);
-			continue;
-		}
-		disable_irq(i);
-	}
-
-	local_irq_save(flags);
-	for (i = 0; i < PPI_NR; i++) {
-		void __iomem *ictlr = ictlr_to_virt(i);
-		cpu_ier[i] = readl(ictlr + ICTLR_CPU_IER);
-		cpu_iep[i] = readl(ictlr + ICTLR_CPU_IEP_CLASS);
-		cop_ier[i] = readl(ictlr + ICTLR_COP_IER);
-		writel(~0, ictlr + ICTLR_COP_IER_CLR);
+	for (i = 0; i < INT_MAIN_NR; i++) {
+		irq = INT_PRI_BASE + i;
+		set_irq_chip(irq, &tegra_irq);
+		set_irq_handler(irq, handle_level_irq);
+		set_irq_flags(irq, IRQF_VALID);
 	}
-	local_irq_restore(flags);
 }
-
-void tegra_irq_resume(void)
-{
-	unsigned long flags;
-	int i;
-
-	local_irq_save(flags);
-	for (i = 0; i < PPI_NR; i++) {
-		void __iomem *ictlr = ictlr_to_virt(i);
-		writel(cpu_iep[i], ictlr + ICTLR_CPU_IEP_CLASS);
-		writel(~0ul, ictlr + ICTLR_CPU_IER_CLR);
-		writel(cpu_ier[i], ictlr + ICTLR_CPU_IER_SET);
-		writel(0, ictlr + ICTLR_COP_IEP_CLASS);
-		writel(~0ul, ictlr + ICTLR_COP_IER_CLR);
-		writel(cop_ier[i], ictlr + ICTLR_COP_IER_SET);
-	}
-	local_irq_restore(flags);
-
-	for (i = INT_PRI_BASE; i < INT_GPIO_BASE; i++) {
-		struct irq_desc *desc = irq_to_desc(i);
-		if (!desc || (desc->status & IRQ_WAKEUP))
-			continue;
-		enable_irq(i);
-	}
-}
-#endif
diff --git a/arch/arm/mach-tegra/legacy_irq.c b/arch/arm/mach-tegra/legacy_irq.c
index 7cc8601..38eb719 100644
--- a/arch/arm/mach-tegra/legacy_irq.c
+++ b/arch/arm/mach-tegra/legacy_irq.c
@@ -18,17 +18,30 @@
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <mach/iomap.h>
+#include <mach/irqs.h>
 #include <mach/legacy_irq.h>
 
-#define ICTLR_CPU_IER		0x20
-#define ICTLR_CPU_IER_SET	0x24
-#define ICTLR_CPU_IER_CLR	0x28
-#define ICTLR_CPU_IEP_CLASS	0x2C
+#define INT_SYS_NR	(INT_GPIO_BASE - INT_PRI_BASE)
+#define INT_SYS_SZ	(INT_SEC_BASE - INT_PRI_BASE)
+#define PPI_NR		((INT_SYS_NR+INT_SYS_SZ-1)/INT_SYS_SZ)
+
 #define ICTLR_CPU_IEP_VFIQ	0x08
 #define ICTLR_CPU_IEP_FIR	0x14
 #define ICTLR_CPU_IEP_FIR_SET	0x18
 #define ICTLR_CPU_IEP_FIR_CLR	0x1c
 
+#define ICTLR_CPU_IER		0x20
+#define ICTLR_CPU_IER_SET	0x24
+#define ICTLR_CPU_IER_CLR	0x28
+#define ICTLR_CPU_IEP_CLASS	0x2C
+
+#define ICTLR_COP_IER		0x30
+#define ICTLR_COP_IER_SET	0x34
+#define ICTLR_COP_IER_CLR	0x38
+#define ICTLR_COP_IEP_CLASS	0x3c
+
+#define NUM_ICTLRS 4
+
 static void __iomem *ictlr_reg_base[] = {
 	IO_ADDRESS(TEGRA_PRIMARY_ICTLR_BASE),
 	IO_ADDRESS(TEGRA_SECONDARY_ICTLR_BASE),
@@ -36,6 +49,9 @@ static void __iomem *ictlr_reg_base[] = {
 	IO_ADDRESS(TEGRA_QUATERNARY_ICTLR_BASE),
 };
 
+static u32 tegra_legacy_wake_mask[4];
+static u32 tegra_legacy_saved_mask[4];
+
 /* When going into deep sleep, the CPU is powered down, taking the GIC with it
    In order to wake, the wake interrupts need to be enabled in the legacy
    interrupt controller. */
@@ -112,3 +128,88 @@ unsigned long tegra_legacy_class(int nr)
 	base = ictlr_reg_base[nr];
 	return readl(base + ICTLR_CPU_IEP_CLASS);
 }
+
+int tegra_legacy_irq_set_wake(int irq, int enable)
+{
+	irq -= 32;
+	if (enable)
+		tegra_legacy_wake_mask[irq >> 5] |= 1 << (irq & 31);
+	else
+		tegra_legacy_wake_mask[irq >> 5] &= ~(1 << (irq & 31));
+
+	return 0;
+}
+
+void tegra_legacy_irq_set_lp1_wake_mask(void)
+{
+	void __iomem *base;
+	int i;
+
+	for (i = 0; i < NUM_ICTLRS; i++) {
+		base = ictlr_reg_base[i];
+		tegra_legacy_saved_mask[i] = readl(base + ICTLR_CPU_IER);
+		writel(tegra_legacy_wake_mask[i], base + ICTLR_CPU_IER);
+	}
+}
+
+void tegra_legacy_irq_restore_mask(void)
+{
+	void __iomem *base;
+	int i;
+
+	for (i = 0; i < NUM_ICTLRS; i++) {
+		base = ictlr_reg_base[i];
+		writel(tegra_legacy_saved_mask[i], base + ICTLR_CPU_IER);
+	}
+}
+
+void tegra_init_legacy_irq(void)
+{
+	int i;
+
+	for (i = 0; i < NUM_ICTLRS; i++) {
+		void __iomem *ictlr = ictlr_reg_base[i];
+		writel(~0, ictlr + ICTLR_CPU_IER_CLR);
+		writel(0, ictlr + ICTLR_CPU_IEP_CLASS);
+	}
+}
+
+#ifdef CONFIG_PM
+static u32 cop_ier[NUM_ICTLRS];
+static u32 cpu_ier[NUM_ICTLRS];
+static u32 cpu_iep[NUM_ICTLRS];
+
+void tegra_irq_suspend(void)
+{
+	unsigned long flags;
+	int i;
+
+	local_irq_save(flags);
+	for (i = 0; i < NUM_ICTLRS; i++) {
+		void __iomem *ictlr = ictlr_reg_base[i];
+		cpu_ier[i] = readl(ictlr + ICTLR_CPU_IER);
+		cpu_iep[i] = readl(ictlr + ICTLR_CPU_IEP_CLASS);
+		cop_ier[i] = readl(ictlr + ICTLR_COP_IER);
+		writel(~0, ictlr + ICTLR_COP_IER_CLR);
+	}
+	local_irq_restore(flags);
+}
+
+void tegra_irq_resume(void)
+{
+	unsigned long flags;
+	int i;
+
+	local_irq_save(flags);
+	for (i = 0; i < NUM_ICTLRS; i++) {
+		void __iomem *ictlr = ictlr_reg_base[i];
+		writel(cpu_iep[i], ictlr + ICTLR_CPU_IEP_CLASS);
+		writel(~0ul, ictlr + ICTLR_CPU_IER_CLR);
+		writel(cpu_ier[i], ictlr + ICTLR_CPU_IER_SET);
+		writel(0, ictlr + ICTLR_COP_IEP_CLASS);
+		writel(~0ul, ictlr + ICTLR_COP_IER_CLR);
+		writel(cop_ier[i], ictlr + ICTLR_COP_IER_SET);
+	}
+	local_irq_restore(flags);
+}
+#endif
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 13/28] ARM: tegra: Add suspend and hotplug support
  2011-01-24  2:01 [PATCH v2 00/28] Updates for Tegra support in 2.6.39 Colin Cross
@ 2011-01-24  2:01   ` Colin Cross
  2011-01-24  2:01   ` Colin Cross
                     ` (26 subsequent siblings)
  27 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-tegra
  Cc: linux-arm-kernel, olof, konkers, Colin Cross, linux, Gary King,
	linux-kernel

Tegra supports three low power modes that involve powering down the CPU.

LP2 powers down both CPU cores and the GICs, but leaves the core
peripherals, including the memory controller and the legacy
interrupt controller, enabled.  The legacy interrupt controller
is used as the wakeup source, and any interrupt can wake the device.
LP2 can be used in idle.

LP1 is the same as LP2, but in addition turns off the memory
controller and puts the DDR memory in self-refresh.  Any interrupt
can wake the device. LP1 could be used in idle if no peripherals
are doing DMA.

LP0 turns off everything in the SoC except the RTC and a power
management controller, both of which run off a 32 kHz clock.
The power management controller has 32 wake sources, all other
interrupts can not be used to wake from LP0.

LP2 idle mode power-gates the main CPU complex, requiring a
full processor state save and restore from a reset vector.

Processor context area is allocated during platform initialization
from the kernel, and mapped into the hotplug page tables (which also
serve as the initial page tables for the LP2 main processor reset).

Platform-specific data (power good times, PMU capabilities, etc.) must be
specified when registering the suspend operations to ensure that platform
power sequencing restrictions are maintained.

Since all device interrupts (except timers) are disabled in the suspend
path, the wakeup interrupts need to be manually unmasked before entering
into a suspend state or the processor will never wake up; these forced-unmask
interrupts are re-masked immediately in the resume path to prevent the
kernel from live-locking prior to driver resume.

In both LP0 and LP1, SDRAM is placed into self-refresh. in order to safely
perform this transition, the final shutdown procedure responsible for

  * turning off the MMU and L1 data cache
  * putting memory into self-refresh
  * setting the DDR pads to the lowest power state
  * and turning off PLLs

is copied into IRAM (at the address TEGRA_IRAM_BASE + SZ_4K) at the
start of the suspend process.

In LP1 mode (like LP2), the CPU is reset and executes the code specified
at the EVP reset vector.  Since SDRAM is in self-refresh, this code must
also be located in IRAM, and it must re-enable DRAM before restoring the
full context.  In this implementation, it enables the CPU on PLLP, enables
PLLC and PLLM, restores the SCLK burst policy, and jumps to the LP2 reset
vector to restore the rest of the system (MMU, PLLX, coresite, etc.).  The
LP2 reset vector is expected to be found in PMC_SCRATCH1, and is
initialized during system-bootup.

In LP0 mode, the core voltage domain is also shutoff.  As a result, all
of the volatile state in the core voltage domain (e.g., pinmux registers,
clock registers, etc.) must be saved to memory so that it can be restored
after the system resumes.  A limited set of wakeups are available from LP0,
and the correct levels for the wakeups must be programmed into the PMC
wakepad configuration register prior to system shutdown.  On resume, the
system resets into the boot ROM, and the boot ROM restores SDRAM and other
system state using values saved during kernel initialization in the PMC
scratch registers.

Resuming from LP0 requires the boot ROM to supply a signed recovery codeblob
to the kernel; the kernel expects that the length and address of this blob
is supplied with the lp0_vec= command line argument; if not present, suspend-
to-LP0 will be disabled

For simplicity, the outer cache is shutdown for both LP0 and LP1; it
is possible to optimize the LP1 routine to bypass outer cache shutdown
and restart.

To save power, SMP tegra SoCs place non-boot CPUs in reset when they
are removed from the scheduling cluster using CPU hotplug.

Slave CPUs save their contexts (incl. CP15 and VFP state) out to a
reserved memory region, cancel SMP operation, and write to the SoC
reset controller to disable themselves.  This is done with caches and
MMU enabled, so care is taken to ensure that all the dirty context cache
lines are cleaned out to the PoC before shutting down.

When re-enabled, slave CPUs execute a hotplug boot routine which mirrors
the initial configuration performed by secondary_startup, but after
enabling the MMU "return" to __cortex_a9_restore which restores the
saved state from the context area, and returns to platform_cpu_die.

Includes fixes from:
Scott Williams <scwilliams@nvidia.com>
Aleksandr Frid <afrid@nvidia.com>
Vik Kasivajhula <tkasivajhula@nvidia.com>
Bharat Nihalani <Kbnihalani@nvidia.com>

Original-author: Gary King <gking@nvidia.com>
Signed-off-by: Gary King <gking@nvidia.com>
Signed-off-by: Colin Cross <ccross@android.com>
---
v2: Updated for comments to v1
    Use memblock_reserve to reserve LP0 boot vector
    Don't save VFP registers, they are already saved
    Don't save context switch registers, reinitialize them 
       with cpu_init
    Don't save breakpoint and watchpoint registers
    Don't save L2X0 registers, reinitialize them with
       l2x0_init
    Update for changes to ARM smp hotplug
    De-obfuscate macros in suspend-t2.c
    Use identity_mapping_add to create suspend page tables
    Fix checkpatch issues


 arch/arm/mach-tegra/Makefile               |   10 +-
 arch/arm/mach-tegra/board.h                |    4 +
 arch/arm/mach-tegra/common.c               |   32 ++-
 arch/arm/mach-tegra/cortex-a9.S            |  504 ++++++++++++++++++
 arch/arm/mach-tegra/headsmp-t2.S           |  216 ++++++++
 arch/arm/mach-tegra/headsmp.S              |   61 ---
 arch/arm/mach-tegra/hotplug.c              |  126 -----
 arch/arm/mach-tegra/include/mach/suspend.h |   42 ++
 arch/arm/mach-tegra/platsmp.c              |   89 +++-
 arch/arm/mach-tegra/power-macros.S         |   57 ++
 arch/arm/mach-tegra/power.h                |   64 +++
 arch/arm/mach-tegra/suspend-t2.c           |  414 +++++++++++++++
 arch/arm/mach-tegra/suspend.c              |  764 ++++++++++++++++++++++++++++
 arch/arm/mach-tegra/tegra2_save.S          |  413 +++++++++++++++
 14 files changed, 2596 insertions(+), 200 deletions(-)
 create mode 100644 arch/arm/mach-tegra/cortex-a9.S
 create mode 100644 arch/arm/mach-tegra/headsmp-t2.S
 delete mode 100644 arch/arm/mach-tegra/headsmp.S
 delete mode 100644 arch/arm/mach-tegra/hotplug.c
 create mode 100644 arch/arm/mach-tegra/power-macros.S
 create mode 100644 arch/arm/mach-tegra/power.h
 create mode 100644 arch/arm/mach-tegra/suspend-t2.c
 create mode 100644 arch/arm/mach-tegra/suspend.c
 create mode 100644 arch/arm/mach-tegra/tegra2_save.S

diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index 00a6ba3..0bf1441 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -6,13 +6,19 @@ obj-y                                   += timer.o
 obj-y                                   += gpio.o
 obj-y                                   += pinmux.o
 obj-y                                   += powergate.o
+obj-y					+= suspend.o
 obj-y					+= fuse.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += clock.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += tegra2_clocks.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += tegra2_dvfs.o
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += suspend-t2.o
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC)		+= tegra2_save.o
+obj-$(CONFIG_CPU_V7)			+= cortex-a9.o
+
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)		+= pinmux-t2-tables.o
-obj-$(CONFIG_SMP)                       += platsmp.o localtimer.o headsmp.o
-obj-$(CONFIG_HOTPLUG_CPU)               += hotplug.o
+obj-$(CONFIG_LOCAL_TIMER)               += localtimer.o
+obj-$(CONFIG_SMP)                       += platsmp.o
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += headsmp-t2.o
 obj-$(CONFIG_TEGRA_SYSTEM_DMA)		+= dma.o
 obj-$(CONFIG_CPU_FREQ)                  += cpu-tegra.o
 obj-$(CONFIG_TEGRA_PCI)			+= pcie.o
diff --git a/arch/arm/mach-tegra/board.h b/arch/arm/mach-tegra/board.h
index 0de565c..ae5c333 100644
--- a/arch/arm/mach-tegra/board.h
+++ b/arch/arm/mach-tegra/board.h
@@ -27,7 +27,11 @@ void __init tegra_common_init(void);
 void __init tegra_map_common_io(void);
 void __init tegra_init_irq(void);
 void __init tegra_init_clock(void);
+void __init tegra_reserve(void);
 int __init tegra_pcie_init(bool init_port0, bool init_port1);
 
+extern unsigned long tegra_lp0_vec_start;
+extern unsigned long tegra_lp0_vec_size;
+
 extern struct sys_timer tegra_timer;
 #endif
diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c
index 7c91e2b..53ddf55 100644
--- a/arch/arm/mach-tegra/common.c
+++ b/arch/arm/mach-tegra/common.c
@@ -21,6 +21,7 @@
 #include <linux/io.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
+#include <linux/memblock.h>
 
 #include <asm/hardware/cache-l2x0.h>
 
@@ -30,6 +31,10 @@
 #include "board.h"
 #include "clock.h"
 #include "fuse.h"
+#include "power.h"
+
+unsigned long tegra_lp0_vec_start;
+unsigned long tegra_lp0_vec_size;
 
 static __initdata struct tegra_clk_init_table common_clk_init_table[] = {
 	/* name		parent		rate		enabled */
@@ -45,7 +50,7 @@ static __initdata struct tegra_clk_init_table common_clk_init_table[] = {
 	{ NULL,		NULL,		0,		0},
 };
 
-void __init tegra_init_cache(void)
+void tegra_init_cache(void)
 {
 #ifdef CONFIG_CACHE_L2X0
 	void __iomem *p = IO_ADDRESS(TEGRA_ARM_PERIF_BASE) + 0x3000;
@@ -68,3 +73,28 @@ void __init tegra_common_init(void)
 	tegra_dma_init();
 #endif
 }
+
+static int __init tegra_lp0_vec_arg(char *options)
+{
+	char *p = options;
+
+	tegra_lp0_vec_size = memparse(p, &p);
+	if (*p == '@')
+		tegra_lp0_vec_start = memparse(p+1, &p);
+
+	return 0;
+}
+early_param("lp0_vec", tegra_lp0_vec_arg);
+
+void __init tegra_reserve(void)
+{
+	if (tegra_lp0_vec_size)
+		if (memblock_reserve(tegra_lp0_vec_start, tegra_lp0_vec_size))
+			pr_err("Failed to reserve lp0_vec %08lx@%08lx\n",
+				tegra_lp0_vec_size, tegra_lp0_vec_start);
+
+	pr_info("Tegra reserved memory:\n"
+		"LP0:                    %08lx - %08lx\n",
+		tegra_lp0_vec_start,
+		tegra_lp0_vec_start + tegra_lp0_vec_size - 1);
+}
diff --git a/arch/arm/mach-tegra/cortex-a9.S b/arch/arm/mach-tegra/cortex-a9.S
new file mode 100644
index 0000000..370d6c3
--- /dev/null
+++ b/arch/arm/mach-tegra/cortex-a9.S
@@ -0,0 +1,504 @@
+/*
+ * arch/arm/mach-tegra/cortex-a9.S
+ *
+ * CPU state save & restore routines for CPU hotplug
+ *
+ * Copyright (c) 2010, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/linkage.h>
+#include <linux/init.h>
+
+#include <asm/assembler.h>
+#include <asm/domain.h>
+#include <asm/ptrace.h>
+#include <asm/cache.h>
+#include <asm/vfpmacros.h>
+#include <asm/hardware/cache-l2x0.h>
+
+#include <mach/iomap.h>
+#include <mach/io.h>
+
+/*	.section ".cpuinit.text", "ax"*/
+
+#define TTB_FLAGS 0x6A	@ IRGN_WBWA, OC_RGN_WBWA, S, NOS
+
+/*
+ * spooled CPU context is 1KB / CPU
+ */
+#define CTX_SP		0
+#define CTX_CPSR	4
+#define CTX_SPSR	8
+#define CTX_CPACR	12
+#define CTX_CSSELR	16
+#define CTX_SCTLR	20
+#define CTX_ACTLR	24
+#define CTX_PCTLR	28
+
+#define CTX_FPEXC	32
+#define CTX_FPSCR	36
+#define CTX_DIAGNOSTIC	40
+
+#define CTX_TTBR0	48
+#define CTX_TTBR1	52
+#define CTX_TTBCR	56
+#define CTX_DACR	60
+#define CTX_PAR		64
+#define CTX_PRRR	68
+#define CTX_NMRR	72
+#define CTX_VBAR	76
+#define CTX_CONTEXTIDR	80
+#define CTX_TPIDRURW	84
+#define CTX_TPIDRURO	88
+#define CTX_TPIDRPRW	92
+
+#define CTX_SVC_SP	0
+#define CTX_SVC_LR	-1	@ stored on stack
+#define CTX_SVC_SPSR	8
+
+#define CTX_SYS_SP	96
+#define CTX_SYS_LR	100
+
+#define CTX_ABT_SPSR	112
+#define CTX_ABT_SP	116
+#define CTX_ABT_LR	120
+
+#define CTX_UND_SPSR	128
+#define CTX_UND_SP	132
+#define CTX_UND_LR	136
+
+#define CTX_IRQ_SPSR	144
+#define CTX_IRQ_SP	148
+#define CTX_IRQ_LR	152
+
+#define CTX_FIQ_SPSR	160
+#define CTX_FIQ_R8	164
+#define CTX_FIQ_R9	168
+#define CTX_FIQ_R10	172
+#define CTX_FIQ_R11	178
+#define CTX_FIQ_R12	180
+#define CTX_FIQ_SP	184
+#define CTX_FIQ_LR	188
+
+/* context only relevant for master cpu */
+#ifdef CONFIG_CACHE_L2X0
+#define CTX_L2_CTRL	224
+#define CTX_L2_AUX	228
+#define CTX_L2_TAG_CTRL	232
+#define CTX_L2_DAT_CTRL	236
+#define CTX_L2_PREFETCH 240
+#endif
+
+#define CTX_VFP_REGS	256
+#define CTX_VFP_SIZE	(32 * 8)
+
+#define CTX_CP14_REGS	512
+#define CTS_CP14_DSCR	512
+#define CTX_CP14_WFAR	516
+#define CTX_CP14_VCR	520
+#define CTX_CP14_CLAIM	524
+
+/* Each of the folowing is 2 32-bit registers */
+#define CTS_CP14_BKPT_0	528
+#define CTS_CP14_BKPT_1	536
+#define CTS_CP14_BKPT_2	544
+#define CTS_CP14_BKPT_3	552
+#define CTS_CP14_BKPT_4	560
+#define CTS_CP14_BKPT_5	568
+
+/* Each of the folowing is 2 32-bit registers */
+#define CTS_CP14_WPT_0	576
+#define CTS_CP14_WPT_1	584
+#define CTS_CP14_WPT_2	592
+#define CTS_CP14_WPT_3	600
+
+#include "power.h"
+#include "power-macros.S"
+
+.macro	ctx_ptr, rd, tmp
+	cpu_id	\tmp
+	mov32	\rd, tegra_context_area
+	ldr	\rd, [\rd]
+	add	\rd, \rd, \tmp, lsl #(CONTEXT_SIZE_BYTES_SHIFT)
+.endm
+
+.macro	translate, pa, va, tmp
+	mov	\tmp, #0x1000
+	sub	\tmp, \tmp, #1
+	bic	\pa, \va, \tmp
+	mcr	p15, 0, \pa, c7, c8, 1
+	mrc	p15, 0, \pa, c7, c4, 0
+	bic	\pa, \pa, \tmp
+	and	\tmp, \va, \tmp
+	orr	\pa, \pa, \tmp
+.endm
+
+/*
+ *	__cortex_a9_save(unsigned int mode)
+ *
+ *	 spools out the volatile processor state to memory, so that
+ *	 the CPU may be safely powered down. does not preserve:
+ *	 - CP15 c0 registers (except cache size select 2,c0/c0,0)
+ *	 - CP15 c1 secure registers (c1/c1, 0-3)
+ *	 - CP15 c5 fault status registers (c5/c0 0&1, c5/c1 0&1)
+ *	 - CP15 c6 fault address registers (c6/c0 0&2)
+ *	 - CP15 c9 performance monitor registers (c9/c12 0-5,
+ *	     c9/c13 0-2, c9/c14 0-2)
+ *	 - CP15 c10 TLB lockdown register (c10/c0, 0)
+ *	 - CP15 c12 MVBAR (c12/c0, 1)
+ *	 - CP15 c15 TLB lockdown registers
+ */
+	.align L1_CACHE_SHIFT
+ENTRY(__cortex_a9_save)
+	mrs	r3, cpsr
+	cps	0x13			@ save off svc registers
+	mov	r1, sp
+	stmfd	sp!, {r3-r12, lr}
+
+	bic	r2, sp, #(L1_CACHE_BYTES-1)
+
+1:	mcr	p15, 0, r2, c7, c14, 1	@ clean out dirty stack cachelines
+	add	r2, r2, #L1_CACHE_BYTES
+	cmp	r2, r1
+	ble	1b
+	dsb
+
+	ctx_ptr	r8, r9
+	mov	r12, r0
+
+	/* zero-out context area */
+	mov	r9, r8
+	add	r10, r8, #(CONTEXT_SIZE_BYTES)
+	mov	r0, #0
+	mov	r1, #0
+	mov	r2, #0
+	mov	r3, #0
+	mov	r4, #0
+	mov	r5, #0
+	mov	r6, #0
+	mov	r7, #0
+2:	stmia	r9!, {r0-r7}
+	cmp	r9, r10
+	blo	2b
+
+	mov	r0, sp
+	mov	sp, r12			@ sp holds the power mode
+	mrs	r1, cpsr
+	mrs	r2, spsr
+
+	mrc	p15, 0, r3, c1, c0, 2	@ cpacr
+	stmia	r8, {r0-r3}
+	mrc	p15, 2, r0, c0, c0, 0	@ csselr
+	mrc	p15, 0, r1, c1, c0, 0	@ sctlr
+	mrc	p15, 0, r2, c1, c0, 1	@ actlr
+	mrc	p15, 0, r4, c15, c0, 0	@ pctlr
+	add	r9, r8, #CTX_CSSELR
+	stmia	r9, {r0-r2, r4}
+
+	mrc	p15, 0, r0, c15, c0, 1	@ diag
+	str	r0, [r8, #CTX_DIAGNOSTIC]
+
+	add	r9, r8, #CTX_TTBR0
+	mrc	p15, 0, r0, c2, c0, 0	@ TTBR0
+	mrc	p15, 0, r1, c2, c0, 1	@ TTBR1
+	mrc	p15, 0, r2, c2, c0, 2	@ TTBCR
+	mrc	p15, 0, r3, c3, c0, 0	@ domain access control reg
+	mrc	p15, 0, r4, c7, c4, 0	@ PAR
+	mrc	p15, 0, r5, c10, c2, 0	@ PRRR
+	mrc	p15, 0, r6, c10, c2, 1	@ NMRR
+	mrc	p15, 0, r7, c12, c0, 0	@ VBAR
+	stmia	r9!, {r0-r7}
+	mrc	p15, 0, r0, c13, c0, 1	@ CONTEXTIDR
+	mrc	p15, 0, r1, c13, c0, 2	@ TPIDRURW
+	mrc	p15, 0, r2, c13, c0, 3	@ TPIDRURO
+	mrc	p15, 0, r3, c13, c0, 4	@ TPIDRPRW
+	stmia	r9, {r0-r3}
+
+	mov	r0, r8
+	add	r1, r8, #CTX_FIQ_SPSR
+	cps	0x11			@ FIQ mode
+	mrs	r7, spsr
+	stmia	r1, {r7-r12,sp,lr}
+
+	cps	0x13			@ back to SVC
+	mov	r8, r0
+
+__cortex_a9_save_clean_cache:
+	mov	r10, r8
+	add	r9, r10, #(CONTEXT_SIZE_BYTES)
+	add	r9, r9, #(L1_CACHE_BYTES-1)
+	bic	r10, r10, #(L1_CACHE_BYTES-1)
+	bic	r9, r9, #(L1_CACHE_BYTES-1)
+
+3:	mcr	p15, 0, r10, c7, c10, 1
+	add	r10, r10, #L1_CACHE_BYTES
+	cmp	r10, r9
+	blo	3b
+	dsb
+
+	translate r10, r8, r1
+
+	mov	r0, #0
+	mcr	p15, 0, r0, c1, c0, 1	@ exit coherency
+	isb
+	cpu_id	r0
+	mov32	r1, (TEGRA_ARM_PERIF_BASE-IO_CPU_PHYS+IO_CPU_VIRT+0xC)
+	mov	r3, r0, lsl #2
+	mov	r2, #0xf
+	mov	r2, r2, lsl r3
+	str	r2, [r1]		@ invalidate SCU tags for CPU
+
+	cmp	r0, #0
+	bne	__put_cpu_in_reset
+	mov	r8, r10
+	b	__tear_down_master
+ENDPROC(__cortex_a9_save)
+
+/*
+ *	__cortex_a9_restore
+ *
+ *	 reloads the volatile CPU state from the context area
+ *	 the MMU should already be enabled using the secondary_data
+ *	 page tables for cpu_up before this function is called, and the
+ *	 CPU should be coherent with the SMP complex
+ */
+	.align L1_CACHE_SHIFT
+ENTRY(__cortex_a9_restore)
+	cps	0x13
+	ctx_ptr	r0, r9
+
+	cps	0x11			@ FIQ mode
+	add	r1, r0, #CTX_FIQ_SPSR
+	ldmia	r1, {r7-r12,sp,lr}
+	msr	spsr_fsxc, r7
+
+	cps	0x13			@ back to SVC
+	mov	r8, r0
+
+	add	r9, r8, #CTX_CSSELR
+	ldmia	r9, {r0-r3}
+
+	mcr	p15, 2, r0, c0, c0, 0	@ csselr
+	mcr	p15, 0, r1, c1, c0, 0	@ sctlr
+	mcr	p15, 0, r2, c1, c0, 1	@ actlr
+	mcr	p15, 0, r3, c15, c0, 0	@ pctlr
+
+	add	r9, r8, #CTX_TTBR0
+	ldmia	r9!, {r0-r7}
+
+	mcr	p15, 0, r4, c7, c4, 0	@ PAR
+	mcr	p15, 0, r7, c12, c0, 0	@ VBAR
+	mcr	p15, 0, r3, c3, c0, 0	@ domain access control reg
+	isb
+	mcr	p15, 0, r2, c2, c0, 2	@ TTBCR
+	isb
+	mcr	p15, 0, r5, c10, c2, 0	@ PRRR
+	isb
+	mcr	p15, 0, r6, c10, c2, 1	@ NMRR
+	isb
+
+	ldmia	r9, {r4-r7}
+
+	mcr	p15, 0, r5, c13, c0, 2	@ TPIDRURW
+	mcr	p15, 0, r6, c13, c0, 3	@ TPIDRURO
+	mcr	p15, 0, r7, c13, c0, 4	@ TPIDRPRW
+
+	ldmia	r8, {r5-r7, lr}
+
+	/* perform context switch to previous context */
+	mov	r9, #0
+	mcr	p15, 0, r9, c13, c0, 1	@ set reserved context
+	isb
+	mcr	p15, 0, r0, c2, c0, 0	@ TTBR0
+	isb
+	mcr	p15, 0, r4, c13, c0, 1	@ CONTEXTIDR
+	isb
+	mcr	p15, 0, r1, c2, c0, 1	@ TTBR1
+	isb
+
+	mov	r4, #0
+	mcr	p15, 0, r4, c8, c3, 0	@ invalidate TLB
+	mcr	p15, 0, r4, c7, c5, 6	@ flush BTAC
+	mcr	p15, 0, r4, c7, c5, 0	@ flush instruction cache
+	dsb
+	isb
+
+	mov	sp, r5
+	msr	cpsr_cxsf, r6
+	msr	spsr_cxsf, r7
+
+	mcr	p15, 0, lr, c1, c0, 2	@ cpacr (loaded before VFP)
+
+	ldr	r9, [r8, #CTX_DIAGNOSTIC]
+	mcr	p15, 0, r9, c15, c0, 1	@ diag
+
+	/* finally, restore the stack and return */
+	ldmfd	sp!, {r3-r12, lr}
+	msr	cpsr_fsxc, r3		@ restore original processor mode
+        isb
+	mov	pc, lr
+ENDPROC(__cortex_a9_restore)
+
+	.align L1_CACHE_SHIFT
+ENTRY(__shut_off_mmu)
+	mrc	p15, 0, r3, c1, c0, 0
+	movw	r2, #(1<<12) | (1<<11) | (1<<2) | (1<<0)
+	bic	r3, r3, r2
+	dsb
+	mcr	p15, 0, r3, c1, c0, 0
+	isb
+	bx	r9
+ENDPROC(__shut_off_mmu)
+
+/*
+ *	__invalidate_l1
+ *
+ *	  Invalidates the L1 data cache (no clean) during initial boot of
+ *	  a secondary processor
+ *
+ *	  Corrupted registers: r0-r6
+ */
+__invalidate_l1:
+	mov	r0, #0
+	mcr	p15, 2, r0, c0, c0, 0
+	mrc	p15, 1, r0, c0, c0, 0
+
+	movw	r1, #0x7fff
+	and	r2, r1, r0, lsr #13
+
+	movw	r1, #0x3ff
+
+	and	r3, r1, r0, lsr #3  @ NumWays - 1
+	add	r2, r2, #1	@ NumSets
+
+	and	r0, r0, #0x7
+	add	r0, r0, #4	@ SetShift
+
+	clz	r1, r3		@ WayShift
+	add	r4, r3, #1	@ NumWays
+1:	sub	r2, r2, #1	@ NumSets--
+	mov	r3, r4		@ Temp = NumWays
+2:	subs    r3, r3, #1	@ Temp--
+	mov	r5, r3, lsl r1
+	mov	r6, r2, lsl r0
+	orr	r5, r5, r6	@ Reg = (Temp<<WayShift)|(NumSets<<SetShift)
+	mcr	p15, 0, r5, c7, c6, 2
+	bgt	2b
+	cmp	r2, #0
+	bgt	1b
+	dsb
+	isb
+	bx	lr
+ENDPROC(__invalidate_l1)
+
+/*
+ *	__invalidate_cpu_state
+ *
+ *	 Invalidates volatile CPU state (SCU tags, caches, branch address
+ *	 arrays, exclusive monitor, etc.) so that they can be safely enabled
+ *	 instruction caching and branch predicition enabled as early as
+ *	 possible to improve performance
+ */
+ENTRY(__invalidate_cpu_state)
+	clrex
+	mov	r0, #0
+	mcr	p15, 0, r0, c1, c0, 1	@ disable SMP, prefetch, broadcast
+	isb
+	mcr	p15, 0, r0, c7, c5, 0	@ invalidate BTAC, i-cache
+	mcr	p15, 0, r0, c7, c5, 6	@ invalidate branch pred array
+	mcr	p15, 0, r0, c8, c7, 0	@ invalidate unified TLB
+	dsb
+	isb
+
+	cpu_id	r0
+	cmp	r0, #0
+	mov32	r1, (TEGRA_ARM_PERIF_BASE + 0xC)
+	movne	r0, r0, lsl #2
+	movne	r2, #0xf
+	movne	r2, r2, lsl r0
+	strne	r2, [r1]		@ invalidate SCU tags for CPU
+
+	dsb
+	mov	r0, #0x1800
+	mcr	p15, 0, r0, c1, c0, 0	@ enable branch prediction, i-cache
+	isb
+	b	__invalidate_l1		@ invalidate data cache
+ENDPROC(__invalidate_cpu_state)
+
+/*
+ *	__return_to_virtual(unsigned long pgdir, void (*ctx_restore)(void))
+ *
+ *	  Restores a CPU to the world of virtual addressing, using the
+ *	  specified page tables (which must ensure that a VA=PA mapping
+ *	  exists for the __enable_mmu function), and then jumps to
+ *	  ctx_restore to restore CPU context and return control to the OS
+ */
+	.align L1_CACHE_SHIFT
+ENTRY(__return_to_virtual)
+	orr	r8, r0, #TTB_FLAGS
+	mov	lr, r1			@ "return" to ctx_restore
+	mov	r3, #0
+	mcr	p15, 0, r3, c2, c0, 2	@ TTB control register
+
+	mcr	p15, 0, r8, c2, c0, 1	@ load TTBR1
+
+	mov	r0, #0x1f
+	mcr	p15, 0, r0, c3, c0, 0	@ domain access register
+
+	mov32	r0, 0xff0a89a8
+	mov32	r1, 0x40e044e0
+	mcr	p15, 0, r0, c10, c2, 0	@ PRRR
+	mcr	p15, 0, r1, c10, c2, 1	@ NMRR
+	mrc	p15, 0, r0, c1, c0, 0
+	mov32	r1, 0x0120c302
+	bic	r0, r0, r1
+	mov32	r1, 0x10c03c7d
+	orr	r0, r0, r1
+
+#ifdef CONFIG_ALIGNMENT_TRAP
+	orr	r0, r0, #0x2
+#else
+	bic	r0, r0, #0x2
+#endif
+	mov	r1, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \
+		      domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \
+		      domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \
+		      domain_val(DOMAIN_IO, DOMAIN_CLIENT))
+	mcr	p15, 0, r1, c3, c0, 0	@ domain access register
+	mcr	p15, 0, r8, c2, c0, 0	@ TTBR0
+	b	__turn_mmu_on_again
+	andeq	r0, r0, r0
+	andeq	r0, r0, r0
+	andeq	r0, r0, r0
+	andeq	r0, r0, r0
+ENDPROC(__return_to_virtual)
+
+/*
+ *	__turn_mmu_on_again
+ *
+ *	  does exactly what it advertises: turns the MMU on, again
+ *	  jumps to the *virtual* address lr after the MMU is enabled.
+ */
+	.align	L1_CACHE_SHIFT
+__turn_mmu_on_again:
+	mov	r0, r0
+	mcr	p15, 0, r0, c1, c0, 0
+	mrc	p15, 0, r3, c0, c0, 0
+	mov	r3, r3
+	mov	r3, lr
+	bx	lr
+ENDPROC(__turn_mmu_on_again)
diff --git a/arch/arm/mach-tegra/headsmp-t2.S b/arch/arm/mach-tegra/headsmp-t2.S
new file mode 100644
index 0000000..da96ec7
--- /dev/null
+++ b/arch/arm/mach-tegra/headsmp-t2.S
@@ -0,0 +1,216 @@
+/*
+ * arch/arm/mach-tegra/headsmp.S
+ *
+ * SMP initialization routines for Tegra SoCs
+ *
+ * Copyright (c) 2009-2010, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/linkage.h>
+#include <linux/init.h>
+
+#include <asm/assembler.h>
+#include <asm/domain.h>
+#include <asm/ptrace.h>
+#include <asm/cache.h>
+
+#include <mach/iomap.h>
+#include <mach/io.h>
+
+#include "power-macros.S"
+
+#define TTB_FLAGS 0x6A	@ IRGN_WBWA, OC_RGN_WBWA, S, NOS
+
+#define PMC_DPD_SAMPLE	0x20
+#define PMC_DPD_ENABLE	0x24
+#define PMC_SCRATCH1	0x54
+#define PMC_SCRATCH39	0x138
+#define RST_DEVICES_U	0xc
+
+#define CLK_RESET_PLLX_BASE     0xe0
+#define CLK_RESET_PLLX_MISC     0xe4
+#define CLK_RESET_PLLP_BASE     0xa0
+#define CLK_RESET_PLLP_OUTA     0xa4
+#define CLK_RESET_PLLP_OUTB     0xa8
+#define CLK_RESET_PLLP_MISC     0xac
+
+/*        .section ".cpuinit.text", "ax"*/
+
+.macro	poke_ev, val, tmp
+	mov32	\tmp, (TEGRA_EXCEPTION_VECTORS_BASE + 0x100)
+	str	\val, [\tmp]
+.endm
+
+#ifdef CONFIG_SMP
+/*
+ *	tegra_secondary_startup
+ *
+ *	 Initial secondary processor boot vector; jumps to kernel's
+ *	 secondary_startup routine
+ */
+ENTRY(tegra_secondary_startup)
+	msr	cpsr_fsxc, #0xd3
+	bl	__invalidate_cpu_state
+	cpu_id	r0
+	enable_coresite r1
+	poke_ev	r0, r1
+	b	secondary_startup
+ENDPROC(tegra_secondary_startup)
+#endif
+
+/*
+ *	__restart_plls
+ *
+ *	  Loads the saved PLLX and PLLP parameters into the PLLs, to
+ *	  allow them to stabilize while the rest of the CPU state is restored.
+ *	  Should be called after the MMU is enabled. Jumps directly
+ *	  to __cortex_a9_restore
+ */
+	.align L1_CACHE_SHIFT
+__restart_plls:
+	mov32	r0, tegra_sctx
+	mov32	r3, (TEGRA_CLK_RESET_BASE-IO_PPSB_PHYS+IO_PPSB_VIRT)
+	mov32	r4, (TEGRA_TMRUS_BASE-IO_PPSB_PHYS+IO_PPSB_VIRT)
+
+	ldr	r1, [r0, #0x0]	@ pllx_misc
+	ldr	r2, [r0, #0x4]	@ pllx_base
+	str	r1, [r3, #CLK_RESET_PLLX_MISC]
+	str	r2, [r3, #CLK_RESET_PLLX_BASE]
+
+	ldr	r1, [r0, #0x8]	@ pllp_misc
+	ldr	r2, [r0, #0xc]	@ pllp_base
+	str	r1, [r3, #CLK_RESET_PLLP_MISC]
+	str	r2, [r3, #CLK_RESET_PLLP_BASE]
+
+	ldr	r1, [r0, #0x10]	@ pllp_outa
+	ldr	r2, [r0, #0x14]	@ pllp_outb
+	str	r1, [r3, #CLK_RESET_PLLP_OUTA]
+	str	r2, [r3, #CLK_RESET_PLLP_OUTB]
+
+	/* record the time that PLLX and PLLP will be stable */
+	ldr	r1, [r4]
+	add	r1, r1, #300
+	str	r1, [r0, #0x18]	@ pll_timeout
+	b	__cortex_a9_restore
+ENDPROC(__restart_pllx)
+/*
+ *	__enable_coresite_access
+ *
+ *	  Takes the coresite debug interface out of reset, enables
+ *	  access to all CPUs. Called with MMU disabled.
+ */
+	.align L1_CACHE_SHIFT
+__enable_coresite_access:
+	mov32	r0, (TEGRA_CLK_RESET_BASE + RST_DEVICES_U)
+	mov32	r2, (TEGRA_TMRUS_BASE)
+
+	/* assert reset for 2usec */
+	ldr	r1, [r0]
+	orr	r1, #(1<<9)
+	str	r1, [r0]
+	wait_for_us r3, r2, r4
+	add	r3, r3, #2
+	bic	r1, r1, #(1<<9)
+	wait_until r3, r2, r4
+	str	r1, [r0]
+	enable_coresite r3
+	bx	lr
+ENDPROC(__enable_coresite_access)
+/*
+ *	tegra_lp2_startup
+ *
+ *	  Secondary CPU boot vector when restarting the master CPU following
+ *	  an LP2 idle transition. Re-enable coresight access, re-enable
+ *	  MMU, re-start PLLX, restore processor context.
+ */
+	.align L1_CACHE_SHIFT
+ENTRY(tegra_lp2_startup)
+	setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9
+
+	mov32	r0, TEGRA_TMRUS_BASE
+	ldr	r1, [r0]
+	mov32	r0, TEGRA_PMC_BASE
+	str	r1, [r0, #PMC_SCRATCH39]	@ save off exact lp2 exit time
+	mov	r1, #0
+	str	r1, [r0, #PMC_DPD_SAMPLE]
+	str	r1, [r0, #PMC_DPD_ENABLE]
+
+	bl	__invalidate_cpu_state
+	bl	__enable_coresite_access
+
+	mrc	p15, 0, r0, c1, c0, 1
+	orr	r0, r0, #(1 << 6) | (1 << 0)	@ re-enable coherency
+	mcr	p15, 0, r0, c1, c0, 1
+
+	/* enable SCU */
+	mov32	r0, TEGRA_ARM_PERIF_BASE
+	ldr	r1, [r0]
+	orr	r1, r1, #1
+	str	r1, [r0]
+
+	adr	r4, __tegra_lp2_data
+	ldmia	r4, {r5, r7, r12}
+	mov	r1, r12			@ ctx_restore = __cortex_a9_restore
+	sub	r4, r4, r5
+	ldr	r0, [r7, r4]		@ pgdir = tegra_pgd_phys
+	b	__return_to_virtual
+ENDPROC(tegra_lp2_startup)
+	.type	__tegra_lp2_data, %object
+__tegra_lp2_data:
+	.long	.
+	.long	tegra_pgd_phys
+	.long	__restart_plls
+	.size	__tegra_lp2_data, . - __tegra_lp2_data
+
+#ifdef CONFIG_HOTPLUG_CPU
+/*
+ *	tegra_hotplug_startup
+ *
+ *	  Secondary CPU boot vector when restarting a CPU following a
+ *	  hot-unplug. Uses the page table created by smp_prepare_cpus and
+ *	  stored in tegra_pgd_phys as the safe page table for
+ *	  __return_to_virtual, and jumps directly to __cortex_a9_restore.
+ */
+	.align L1_CACHE_SHIFT
+ENTRY(tegra_hotplug_startup)
+	setmode	PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9
+	bl	__invalidate_cpu_state
+	enable_coresite r1
+
+	/* most of the below is a retread of what happens in __v7_setup and
+	 * secondary_startup, to get the MMU re-enabled and to branch
+	 * to secondary_kernel_startup */
+	mrc	p15, 0, r0, c1, c0, 1
+	orr	r0, r0, #(1 << 6) | (1 << 0)	@ re-enable coherency
+	mcr	p15, 0, r0, c1, c0, 1
+
+	adr	r4, __tegra_hotplug_data
+	ldmia	r4, {r5, r7, r12}
+	mov	r1, r12			@ ctx_restore = __cortex_a9_restore
+	sub	r4, r4, r5
+	ldr	r0, [r7, r4]		@ pgdir = secondary_data.pgdir
+	b	__return_to_virtual
+ENDPROC(tegra_hotplug_startup)
+
+
+	.type	__tegra_hotplug_data, %object
+__tegra_hotplug_data:
+	.long	.
+	.long	tegra_pgd_phys
+	.long	__cortex_a9_restore
+	.size	__tegra_hotplug_data, . - __tegra_hotplug_data
+#endif
diff --git a/arch/arm/mach-tegra/headsmp.S b/arch/arm/mach-tegra/headsmp.S
deleted file mode 100644
index b5349b2..0000000
--- a/arch/arm/mach-tegra/headsmp.S
+++ /dev/null
@@ -1,61 +0,0 @@
-#include <linux/linkage.h>
-#include <linux/init.h>
-
-        .section ".text.head", "ax"
-	__CPUINIT
-
-/*
- * Tegra specific entry point for secondary CPUs.
- *   The secondary kernel init calls v7_flush_dcache_all before it enables
- *   the L1; however, the L1 comes out of reset in an undefined state, so
- *   the clean + invalidate performed by v7_flush_dcache_all causes a bunch
- *   of cache lines with uninitialized data and uninitialized tags to get
- *   written out to memory, which does really unpleasant things to the main
- *   processor.  We fix this by performing an invalidate, rather than a
- *   clean + invalidate, before jumping into the kernel.
- */
-ENTRY(v7_invalidate_l1)
-        mov     r0, #0
-        mcr     p15, 2, r0, c0, c0, 0
-        mrc     p15, 1, r0, c0, c0, 0
-
-        ldr     r1, =0x7fff
-        and     r2, r1, r0, lsr #13
-
-        ldr     r1, =0x3ff
-
-        and     r3, r1, r0, lsr #3  @ NumWays - 1
-        add     r2, r2, #1          @ NumSets
-
-        and     r0, r0, #0x7
-        add     r0, r0, #4          @ SetShift
-
-        clz     r1, r3              @ WayShift
-        add     r4, r3, #1          @ NumWays
-1:      sub     r2, r2, #1          @ NumSets--
-        mov     r3, r4              @ Temp = NumWays
-2:      subs    r3, r3, #1          @ Temp--
-        mov     r5, r3, lsl r1
-        mov     r6, r2, lsl r0
-        orr     r5, r5, r6          @ Reg = (Temp<<WayShift)|(NumSets<<SetShift)
-        mcr     p15, 0, r5, c7, c6, 2
-        bgt     2b
-        cmp     r2, #0
-        bgt     1b
-        dsb
-        isb
-        mov     pc, lr
-ENDPROC(v7_invalidate_l1)
-
-ENTRY(tegra_secondary_startup)
-	msr	cpsr_fsxc, #0xd3
-        bl      v7_invalidate_l1
-	mrc	p15, 0, r0, c0, c0, 5
-        and	r0, r0, #15
-        ldr     r1, =0x6000f100
-        str     r0, [r1]
-1:      ldr     r2, [r1]
-        cmp     r0, r2
-        beq     1b
-        b       secondary_startup
-ENDPROC(tegra_secondary_startup)
diff --git a/arch/arm/mach-tegra/hotplug.c b/arch/arm/mach-tegra/hotplug.c
deleted file mode 100644
index f329404..0000000
--- a/arch/arm/mach-tegra/hotplug.c
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- *  linux/arch/arm/mach-realview/hotplug.c
- *
- *  Copyright (C) 2002 ARM Ltd.
- *  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 as
- * published by the Free Software Foundation.
- */
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/smp.h>
-
-#include <asm/cacheflush.h>
-
-static inline void cpu_enter_lowpower(void)
-{
-	unsigned int v;
-
-	flush_cache_all();
-	asm volatile(
-	"	mcr	p15, 0, %1, c7, c5, 0\n"
-	"	mcr	p15, 0, %1, c7, c10, 4\n"
-	/*
-	 * Turn off coherency
-	 */
-	"	mrc	p15, 0, %0, c1, c0, 1\n"
-	"	bic	%0, %0, #0x20\n"
-	"	mcr	p15, 0, %0, c1, c0, 1\n"
-	"	mrc	p15, 0, %0, c1, c0, 0\n"
-	"	bic	%0, %0, %2\n"
-	"	mcr	p15, 0, %0, c1, c0, 0\n"
-	  : "=&r" (v)
-	  : "r" (0), "Ir" (CR_C)
-	  : "cc");
-}
-
-static inline void cpu_leave_lowpower(void)
-{
-	unsigned int v;
-
-	asm volatile(
-	"mrc	p15, 0, %0, c1, c0, 0\n"
-	"	orr	%0, %0, %1\n"
-	"	mcr	p15, 0, %0, c1, c0, 0\n"
-	"	mrc	p15, 0, %0, c1, c0, 1\n"
-	"	orr	%0, %0, #0x20\n"
-	"	mcr	p15, 0, %0, c1, c0, 1\n"
-	  : "=&r" (v)
-	  : "Ir" (CR_C)
-	  : "cc");
-}
-
-static inline void platform_do_lowpower(unsigned int cpu, int *spurious)
-{
-	/*
-	 * there is no power-control hardware on this platform, so all
-	 * we can do is put the core into WFI; this is safe as the calling
-	 * code will have already disabled interrupts
-	 */
-	for (;;) {
-		/*
-		 * here's the WFI
-		 */
-		asm(".word	0xe320f003\n"
-		    :
-		    :
-		    : "memory", "cc");
-
-		/*if (pen_release == cpu) {*/
-			/*
-			 * OK, proper wakeup, we're done
-			 */
-			break;
-		/*}*/
-
-		/*
-		 * Getting here, means that we have come out of WFI without
-		 * having been woken up - this shouldn't happen
-		 *
-		 * Just note it happening - when we're woken, we can report
-		 * its occurrence.
-		 */
-		(*spurious)++;
-	}
-}
-
-int platform_cpu_kill(unsigned int cpu)
-{
-	return 1;
-}
-
-/*
- * platform-specific code to shutdown a CPU
- *
- * Called with IRQs disabled
- */
-void platform_cpu_die(unsigned int cpu)
-{
-	int spurious = 0;
-
-	/*
-	 * we're ready for shutdown now, so do it
-	 */
-	cpu_enter_lowpower();
-	platform_do_lowpower(cpu, &spurious);
-
-	/*
-	 * bring this CPU back into the world of cache
-	 * coherency, and then restore interrupts
-	 */
-	cpu_leave_lowpower();
-
-	if (spurious)
-		pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious);
-}
-
-int platform_cpu_disable(unsigned int cpu)
-{
-	/*
-	 * we don't allow CPU 0 to be shutdown (it is still too special
-	 * e.g. clock tick interrupts)
-	 */
-	return cpu == 0 ? -EPERM : 0;
-}
diff --git a/arch/arm/mach-tegra/include/mach/suspend.h b/arch/arm/mach-tegra/include/mach/suspend.h
index 5af8715..e6043ae 100644
--- a/arch/arm/mach-tegra/include/mach/suspend.h
+++ b/arch/arm/mach-tegra/include/mach/suspend.h
@@ -21,6 +21,38 @@
 #ifndef _MACH_TEGRA_SUSPEND_H_
 #define _MACH_TEGRA_SUSPEND_H_
 
+enum tegra_suspend_mode {
+	TEGRA_SUSPEND_NONE = 0,
+	TEGRA_SUSPEND_LP2,	/* CPU voltage off */
+	TEGRA_SUSPEND_LP1,	/* CPU voltage off, DRAM self-refresh */
+	TEGRA_SUSPEND_LP0,	/* CPU + core voltage off, DRAM self-refresh */
+	TEGRA_MAX_SUSPEND_MODE,
+};
+
+struct tegra_suspend_platform_data {
+	unsigned long cpu_timer;   /* CPU power good time in us,  LP2/LP1 */
+	unsigned long cpu_off_timer;	/* CPU power off time us, LP2/LP1 */
+	unsigned long core_timer;  /* core power good time in ticks,  LP0 */
+	unsigned long core_off_timer;	/* core power off time ticks, LP0 */
+	unsigned long wake_enb;    /* mask of enabled wake pads */
+	unsigned long wake_high;   /* high-level-triggered wake pads */
+	unsigned long wake_low;    /* low-level-triggered wake pads */
+	unsigned long wake_any;    /* any-edge-triggered wake pads */
+	bool corereq_high;         /* Core power request active-high */
+	bool sysclkreq_high;       /* System clock request is active-high */
+	bool separate_req;         /* Core & CPU power request are separate */
+	enum tegra_suspend_mode suspend_mode;
+};
+
+unsigned long tegra_cpu_power_good_time(void);
+unsigned long tegra_cpu_power_off_time(void);
+enum tegra_suspend_mode tegra_get_suspend_mode(void);
+
+void __tegra_lp1_reset(void);
+void __tegra_iram_end(void);
+
+void lp0_suspend_init(void);
+
 void tegra_pinmux_suspend(void);
 void tegra_irq_suspend(void);
 void tegra_gpio_suspend(void);
@@ -35,4 +67,14 @@ void tegra_clk_resume(void);
 void tegra_dma_resume(void);
 void tegra_timer_resume(void);
 
+int tegra_irq_to_wake(int irq);
+int tegra_wake_to_irq(int wake);
+
+int tegra_set_lp0_wake(int irq, int enable);
+int tegra_set_lp0_wake_type(int irq, int flow_type);
+int tegra_set_lp1_wake(int irq, int enable);
+void tegra_set_lp0_wake_pads(u32 wake_enb, u32 wake_level, u32 wake_any);
+
+void __init tegra_init_suspend(struct tegra_suspend_platform_data *plat);
+
 #endif /* _MACH_TEGRA_SUSPEND_H_ */
diff --git a/arch/arm/mach-tegra/platsmp.c b/arch/arm/mach-tegra/platsmp.c
index ec1f689..d4ac18c 100644
--- a/arch/arm/mach-tegra/platsmp.c
+++ b/arch/arm/mach-tegra/platsmp.c
@@ -7,6 +7,8 @@
  *  Copyright (C) 2009 Palm
  *  All Rights Reserved
  *
+ *  Copyright (C) 2010 NVIDIA Corporation
+ *
  * 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.
@@ -18,14 +20,24 @@
 #include <linux/jiffies.h>
 #include <linux/smp.h>
 #include <linux/io.h>
+#include <linux/completion.h>
+#include <linux/sched.h>
+#include <linux/cpu.h>
+#include <linux/slab.h>
 
 #include <asm/cacheflush.h>
 #include <mach/hardware.h>
 #include <asm/mach-types.h>
+#include <asm/tlbflush.h>
 #include <asm/smp_scu.h>
+#include <asm/cpu.h>
+#include <asm/mmu_context.h>
+#include <asm/hardware/gic.h>
 
 #include <mach/iomap.h>
 
+#include "power.h"
+
 extern void tegra_secondary_startup(void);
 
 static DEFINE_SPINLOCK(boot_lock);
@@ -35,6 +47,8 @@ static void __iomem *scu_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE);
 	(IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100)
 #define CLK_RST_CONTROLLER_CLK_CPU_CMPLX \
 	(IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x4c)
+#define CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET \
+	(IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x340)
 #define CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR \
 	(IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x344)
 
@@ -67,27 +81,25 @@ int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
 	 */
 	spin_lock(&boot_lock);
 
-
 	/* set the reset vector to point to the secondary_startup routine */
-
 	boot_vector = virt_to_phys(tegra_secondary_startup);
+
+	smp_wmb();
+
 	old_boot_vector = readl(EVP_CPU_RESET_VECTOR);
 	writel(boot_vector, EVP_CPU_RESET_VECTOR);
 
-	/* enable cpu clock on cpu1 */
+	/* enable cpu clock on cpu */
 	reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
-	writel(reg & ~(1<<9), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+	writel(reg & ~(1<<(8+cpu)), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
 
-	reg = (1<<13) | (1<<9) | (1<<5) | (1<<1);
+	reg = 0x1111<<cpu;
 	writel(reg, CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR);
 
-	smp_wmb();
-	flush_cache_all();
-
 	/* unhalt the cpu */
-	writel(0, IO_ADDRESS(TEGRA_FLOW_CTRL_BASE) + 0x14);
+	writel(0, IO_ADDRESS(TEGRA_FLOW_CTRL_BASE) + 0x14 + 0x8*(cpu-1));
 
-	timeout = jiffies + (1 * HZ);
+	timeout = jiffies + HZ;
 	while (time_before(jiffies, timeout)) {
 		if (readl(EVP_CPU_RESET_VECTOR) != boot_vector)
 			break;
@@ -137,3 +149,60 @@ void __init platform_smp_prepare_cpus(unsigned int max_cpus)
 
 	scu_enable(scu_base);
 }
+
+#ifdef CONFIG_HOTPLUG_CPU
+
+int platform_cpu_kill(unsigned int cpu)
+{
+	unsigned int reg;
+
+	do {
+		reg = readl(CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET);
+		cpu_relax();
+	} while (!(reg & (1<<cpu)));
+
+	spin_lock(&boot_lock);
+	reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+	writel(reg | (1<<(8+cpu)), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+	spin_unlock(&boot_lock);
+
+	return 1;
+}
+
+void platform_cpu_die(unsigned int cpu)
+{
+#ifdef DEBUG
+	unsigned int this_cpu = hard_smp_processor_id();
+
+	if (cpu != this_cpu) {
+		printk(KERN_CRIT "Eek! platform_cpu_die running on %u, should be %u\n",
+			   this_cpu, cpu);
+		BUG();
+	}
+#endif
+
+	gic_cpu_exit(0);
+	barrier();
+	flush_cache_all();
+	barrier();
+	__cortex_a9_save(0);
+
+	/*
+	 * __cortex_a9_save can return through __cortex_a9_restore, but that
+	 * should never happen for a hotplugged cpu
+	 */
+	BUG();
+}
+
+int platform_cpu_disable(unsigned int cpu)
+{
+	/*
+	 * we don't allow CPU 0 to be shutdown (it is still too special
+	 * e.g. clock tick interrupts)
+	 */
+	if (unlikely(!tegra_context_area))
+		return -ENXIO;
+
+	return cpu == 0 ? -EPERM : 0;
+}
+#endif
diff --git a/arch/arm/mach-tegra/power-macros.S b/arch/arm/mach-tegra/power-macros.S
new file mode 100644
index 0000000..2d18d02
--- /dev/null
+++ b/arch/arm/mach-tegra/power-macros.S
@@ -0,0 +1,57 @@
+/*
+ * arch/arm/mach-tegra/power-macros.S
+ *
+ * Assembly macros useful for power state save / restore routines
+ *
+ * Copyright (c) 2010, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+/* returns the ID of the current processor */
+.macro	cpu_id, rd
+	mrc	p15, 0, \rd, c0, c0, 5
+	and	\rd, \rd, #0xF
+.endm
+
+
+.macro mov32, reg, val
+	movw	\reg, #:lower16:\val
+	movt	\reg, #:upper16:\val
+.endm
+
+/* waits until the microsecond counter (base) ticks, for exact timing loops */
+.macro	wait_for_us, rd, base, tmp
+	ldr	\rd, [\base]
+1001:	ldr	\tmp, [\base]
+	cmp	\rd, \tmp
+	beq	1001b
+	mov	\tmp, \rd
+.endm
+
+/* waits until the microsecond counter (base) is >= rn */
+.macro	wait_until, rn, base, tmp
+1002:	ldr	\tmp, [\base]
+	sub	\tmp, \tmp, \rn
+	ands	\tmp, \tmp, #0x80000000
+	dmb
+	bne	1002b
+.endm
+
+/* Enable Coresight access on cpu */
+.macro	enable_coresite, tmp
+	mov32	\tmp, 0xC5ACCE55
+	mcr	p14, 0, \tmp, c7, c12, 6
+.endm
diff --git a/arch/arm/mach-tegra/power.h b/arch/arm/mach-tegra/power.h
new file mode 100644
index 0000000..ac53b2a
--- /dev/null
+++ b/arch/arm/mach-tegra/power.h
@@ -0,0 +1,64 @@
+/*
+ * arch/arm/mach-tegra/power.h
+ *
+ * Declarations for power state transition code
+ *
+ * Copyright (c) 2010, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __MACH_TEGRA_POWER_H
+#define __MACH_TEGRA_POWER_H
+
+#include <asm/page.h>
+
+#define TEGRA_POWER_SDRAM_SELFREFRESH	0x400 /* SDRAM is in self-refresh */
+
+#define TEGRA_POWER_PWRREQ_POLARITY	0x1   /* core power request polarity */
+#define TEGRA_POWER_PWRREQ_OE		0x2   /* core power request enable */
+#define TEGRA_POWER_SYSCLK_POLARITY	0x4   /* sys clk polarity */
+#define TEGRA_POWER_SYSCLK_OE		0x8   /* system clock enable */
+#define TEGRA_POWER_PWRGATE_DIS		0x10  /* power gate disabled */
+#define TEGRA_POWER_EFFECT_LP0		0x40  /* enter LP0 when CPU pwr gated */
+#define TEGRA_POWER_CPU_PWRREQ_POLARITY 0x80  /* CPU power request polarity */
+#define TEGRA_POWER_CPU_PWRREQ_OE	0x100 /* CPU power request enable */
+#define TEGRA_POWER_PMC_SHIFT		8
+#define TEGRA_POWER_PMC_MASK		0x1ff
+
+/* CPU Context area (1KB per CPU) */
+#define CONTEXT_SIZE_BYTES_SHIFT 10
+#define CONTEXT_SIZE_BYTES (1<<CONTEXT_SIZE_BYTES_SHIFT)
+
+/* layout of IRAM used for LP1 save & restore */
+#define TEGRA_IRAM_CODE_AREA		(TEGRA_IRAM_BASE + SZ_4K)
+#define TEGRA_IRAM_CODE_SIZE		SZ_4K
+
+#ifndef __ASSEMBLY__
+extern void *tegra_context_area;
+
+void tegra_lp2_set_trigger(unsigned long cycles);
+unsigned long tegra_lp2_timer_remain(void);
+u64 tegra_rtc_read_ms(void);
+void __cortex_a9_save(unsigned int mode);
+void __cortex_a9_restore(void);
+void __shut_off_mmu(void);
+void tegra_lp2_startup(void);
+unsigned int tegra_suspend_lp2(unsigned int us);
+void tegra_hotplug_startup(void);
+void tegra_init_cache(void);
+#endif
+
+#endif
diff --git a/arch/arm/mach-tegra/suspend-t2.c b/arch/arm/mach-tegra/suspend-t2.c
new file mode 100644
index 0000000..10079f4
--- /dev/null
+++ b/arch/arm/mach-tegra/suspend-t2.c
@@ -0,0 +1,414 @@
+/*
+ * arch/arm/mach-tegra/suspend-t2.c
+ *
+ * BootROM LP0 scratch register preservation for Tegra 2
+ *
+ * Copyright (c) 2009-2010, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/io.h>
+
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+#include <mach/suspend.h>
+
+#include "gpio-names.h"
+
+#define PMC_SCRATCH3	0x5c
+#define PMC_SCRATCH5	0x64
+#define PMC_SCRATCH6	0x68
+#define PMC_SCRATCH7	0x6c
+#define PMC_SCRATCH8	0x70
+#define PMC_SCRATCH9	0x74
+#define PMC_SCRATCH10	0x78
+#define PMC_SCRATCH11	0x7c
+#define PMC_SCRATCH12	0x80
+#define PMC_SCRATCH13	0x84
+#define PMC_SCRATCH14	0x88
+#define PMC_SCRATCH15	0x8c
+#define PMC_SCRATCH16	0x90
+#define PMC_SCRATCH17	0x94
+#define PMC_SCRATCH18	0x98
+#define PMC_SCRATCH19	0x9c
+#define PMC_SCRATCH20	0xa0
+#define PMC_SCRATCH21	0xa4
+#define PMC_SCRATCH22	0xa8
+#define PMC_SCRATCH23	0xac
+#define PMC_SCRATCH25	0x100
+#define PMC_SCRATCH35	0x128
+#define PMC_SCRATCH36	0x12c
+#define PMC_SCRATCH40	0x13c
+
+struct pmc_scratch_field {
+	unsigned long addr;
+	unsigned int mask;
+	int shift_src;
+	int shift_dst;
+};
+
+#define field(reg, start, end, dst)					\
+	{								\
+		.addr = (reg),						\
+		.mask = 0xfffffffful >> (31 - ((end) - (start))),	\
+		.shift_src = (start),					\
+		.shift_dst = (dst),					\
+	}
+
+static const struct pmc_scratch_field pllx[] __initdata = {
+	field(TEGRA_CLK_RESET_BASE + 0xe0, 20, 22, 15), /* PLLX_DIVP */
+	field(TEGRA_CLK_RESET_BASE + 0xe0, 8, 17, 5), /* PLLX_DIVN */
+	field(TEGRA_CLK_RESET_BASE + 0xe0, 0, 4, 0), /* PLLX_DIVM */
+	field(TEGRA_CLK_RESET_BASE + 0xe4, 8, 11, 22), /* PLLX_CPCON */
+	field(TEGRA_CLK_RESET_BASE + 0xe4, 4, 7, 18), /* PLLX_LFCON */
+	field(TEGRA_APB_MISC_BASE + 0x8e4, 24, 27, 27), /* XM2CFGC_VREF_DQ */
+	field(TEGRA_APB_MISC_BASE + 0x8c8, 3, 3, 26), /* XM2CFGC_SCHMT_EN */
+	field(TEGRA_APB_MISC_BASE + 0x8d0, 2, 2, 31), /* XM2CLKCFG_PREEMP_EN */
+};
+
+static const struct pmc_scratch_field emc_0[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x3c, 0, 4, 27), /* R2W */
+	field(TEGRA_EMC_BASE + 0x34, 0, 5, 15), /* RAS */
+	field(TEGRA_EMC_BASE + 0x2c, 0, 5, 0), /* RC */
+	field(TEGRA_EMC_BASE + 0x30, 0, 8, 6), /* RFC */
+	field(TEGRA_EMC_BASE + 0x38, 0, 5, 21), /* RP */
+};
+
+static const struct pmc_scratch_field emc_1[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x44, 0, 4, 5), /* R2P */
+	field(TEGRA_EMC_BASE + 0x4c, 0, 5, 15), /* RD_RCD */
+	field(TEGRA_EMC_BASE + 0x54, 0, 3, 27), /* RRD */
+	field(TEGRA_EMC_BASE + 0x48, 0, 4, 10), /* W2P */
+	field(TEGRA_EMC_BASE + 0x40, 0, 4, 0), /* W2R */
+	field(TEGRA_EMC_BASE + 0x50, 0, 5, 21), /* WR_RCD */
+};
+
+static const struct pmc_scratch_field emc_2[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x2b8, 2, 2, 31), /* CLKCHANGE_SR_ENABLE */
+	field(TEGRA_EMC_BASE + 0x2b8, 10, 10, 30), /* USE_ADDR_CLK */
+	field(TEGRA_EMC_BASE + 0x80, 0, 4, 25), /* PCHG2PDEN */
+	field(TEGRA_EMC_BASE + 0x64, 0, 3, 12), /* QRST */
+	field(TEGRA_EMC_BASE + 0x68, 0, 3, 16), /* QSAFE */
+	field(TEGRA_EMC_BASE + 0x60, 0, 3, 8), /* QUSE */
+	field(TEGRA_EMC_BASE + 0x6c, 0, 4, 20), /* RDV */
+	field(TEGRA_EMC_BASE + 0x58, 0, 3, 0), /* REXT */
+	field(TEGRA_EMC_BASE + 0x5c, 0, 3, 4), /* WDV */
+};
+
+static const struct pmc_scratch_field emc_3[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x74, 0, 3, 16), /* BURST_REFRESH_NUM */
+	field(TEGRA_EMC_BASE + 0x7c, 0, 3, 24), /* PDEX2RD */
+	field(TEGRA_EMC_BASE + 0x78, 0, 3, 20), /* PDEX2WR */
+	field(TEGRA_EMC_BASE + 0x70, 0, 4, 0), /* REFRESH_LO */
+	field(TEGRA_EMC_BASE + 0x70, 5, 15, 5), /* REFRESH */
+	field(TEGRA_EMC_BASE + 0xa0, 0, 3, 28), /* TCLKSTABLE */
+};
+
+static const struct pmc_scratch_field emc_4[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x84, 0, 4, 0), /* ACT2PDEN */
+	field(TEGRA_EMC_BASE + 0x88, 0, 4, 5), /* AR2PDEN */
+	field(TEGRA_EMC_BASE + 0x8c, 0, 5, 10), /* RW2PDEN */
+	field(TEGRA_EMC_BASE + 0x94, 0, 3, 28), /* TCKE */
+	field(TEGRA_EMC_BASE + 0x90, 0, 11, 16), /* TXSR */
+};
+
+static const struct pmc_scratch_field emc_5[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x8, 10, 10, 30), /* AP_REQ_BUSY_CTRL */
+	field(TEGRA_EMC_BASE + 0x8, 24, 24, 31), /* CFG_PRIORITY */
+	field(TEGRA_EMC_BASE + 0x8, 2, 2, 26), /* FORCE_UPDATE */
+	field(TEGRA_EMC_BASE + 0x8, 4, 4, 27), /* MRS_WAIT */
+	field(TEGRA_EMC_BASE + 0x8, 5, 5, 28), /* PERIODIC_QRST */
+	field(TEGRA_EMC_BASE + 0x8, 9, 9, 29), /* READ_DQM_CTRL */
+	field(TEGRA_EMC_BASE + 0x8, 0, 0, 24), /* READ_MUX */
+	field(TEGRA_EMC_BASE + 0x8, 1, 1, 25), /* WRITE_MUX */
+	field(TEGRA_EMC_BASE + 0xa4, 0, 3, 6), /* TCLKSTOP */
+	field(TEGRA_EMC_BASE + 0xa8, 0, 13, 10), /* TREFBW */
+	field(TEGRA_EMC_BASE + 0x9c, 0, 5, 0), /* TRPAB */
+};
+
+static const struct pmc_scratch_field emc_6[] __initdata = {
+	field(TEGRA_EMC_BASE + 0xfc, 0, 1, 0), /* DQSIB_DLY_MSB_BYTE_0 */
+	field(TEGRA_EMC_BASE + 0xfc, 8, 9, 2), /* DQSIB_DLY_MSB_BYTE_1 */
+	field(TEGRA_EMC_BASE + 0xfc, 16, 17, 4), /* DQSIB_DLY_MSB_BYTE_2 */
+	field(TEGRA_EMC_BASE + 0xfc, 24, 25, 6), /* DQSIB_DLY_MSB_BYTE_3 */
+	field(TEGRA_EMC_BASE + 0x110, 0, 1, 8), /* QUSE_DLY_MSB_BYTE_0 */
+	field(TEGRA_EMC_BASE + 0x110, 8, 9, 10), /* QUSE_DLY_MSB_BYTE_1 */
+	field(TEGRA_EMC_BASE + 0x110, 16, 17, 12), /* QUSE_DLY_MSB_BYTE_2 */
+	field(TEGRA_EMC_BASE + 0x110, 24, 25, 14), /* QUSE_DLY_MSB_BYTE_3 */
+	field(TEGRA_EMC_BASE + 0xac, 0, 3, 22), /* QUSE_EXTRA */
+	field(TEGRA_EMC_BASE + 0x98, 0, 5, 16), /* TFAW */
+	field(TEGRA_APB_MISC_BASE + 0x8e4, 5, 5, 30), /* XM2CFGC_VREF_DQ_EN */
+	field(TEGRA_APB_MISC_BASE + 0x8e4, 16, 19, 26), /* XM2CFGC_VREF_DQS */
+};
+
+static const struct pmc_scratch_field emc_dqsib_dly[] __initdata = {
+	field(TEGRA_EMC_BASE + 0xf8, 0, 31, 0), /* DQSIB_DLY_BYTE_0 - DQSIB_DLY_BYTE_3*/
+};
+
+static const struct pmc_scratch_field emc_quse_dly[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x10c, 0, 31, 0), /* QUSE_DLY_BYTE_0 - QUSE_DLY_BYTE_3*/
+};
+
+static const struct pmc_scratch_field emc_clktrim[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x2d0, 0, 29, 0), /* DATA0_CLKTRIM - DATA3_CLKTRIM +
+					* MCLK_ADDR_CLKTRIM */
+};
+
+static const struct pmc_scratch_field emc_autocal_fbio[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x2a4, 29, 29, 29), /* AUTO_CAL_ENABLE */
+	field(TEGRA_EMC_BASE + 0x2a4, 30, 30, 30), /* AUTO_CAL_OVERRIDE */
+	field(TEGRA_EMC_BASE + 0x2a4, 8, 12, 14), /* AUTO_CAL_PD_OFFSET */
+	field(TEGRA_EMC_BASE + 0x2a4, 0, 4, 9), /* AUTO_CAL_PU_OFFSET */
+	field(TEGRA_EMC_BASE + 0x2a4, 16, 25, 19), /* AUTO_CAL_STEP */
+	field(TEGRA_EMC_BASE + 0xf4, 16, 16, 0), /* CFG_DEN_EARLY */
+	field(TEGRA_EMC_BASE + 0x104, 8, 8, 8), /* CTT_TERMINATION */
+	field(TEGRA_EMC_BASE + 0x104, 7, 7, 7), /* DIFFERENTIAL_DQS */
+	field(TEGRA_EMC_BASE + 0x104, 9, 9, 31), /* DQS_PULLD */
+	field(TEGRA_EMC_BASE + 0x104, 0, 1, 4), /* DRAM_TYPE */
+	field(TEGRA_EMC_BASE + 0x104, 4, 4, 6), /* DRAM_WIDTH */
+	field(TEGRA_EMC_BASE + 0x114, 0, 2, 1), /* CFG_QUSE_LATE */
+};
+
+static const struct pmc_scratch_field emc_autocal_interval[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x2a8, 0, 27, 0), /* AUTOCAL_INTERVAL */
+	field(TEGRA_EMC_BASE + 0x2b8, 1, 1, 29), /* CLKCHANGE_PD_ENABLE */
+	field(TEGRA_EMC_BASE + 0x2b8, 0, 0, 28), /* CLKCHANGE_REQ_ENABLE */
+	field(TEGRA_EMC_BASE + 0x2b8, 8, 9, 30), /* PIN_CONFIG */
+};
+
+static const struct pmc_scratch_field emc_cfgs[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x10, 8, 9, 3), /* EMEM_BANKWIDTH */
+	field(TEGRA_EMC_BASE + 0x10, 0, 2, 0), /* EMEM_COLWIDTH */
+	field(TEGRA_EMC_BASE + 0x10, 16, 19, 5), /* EMEM_DEVSIZE */
+	field(TEGRA_EMC_BASE + 0x10, 24, 25, 9), /* EMEM_NUMDEV */
+	field(TEGRA_EMC_BASE + 0xc, 24, 24, 21), /* AUTO_PRE_RD */
+	field(TEGRA_EMC_BASE + 0xc, 25, 25, 22), /* AUTO_PRE_WR */
+	field(TEGRA_EMC_BASE + 0xc, 16, 16, 20), /* CLEAR_AP_PREV_SPREQ */
+	field(TEGRA_EMC_BASE + 0xc, 29, 29, 23), /* DRAM_ACPD */
+	field(TEGRA_EMC_BASE + 0xc, 30, 30, 24), /* DRAM_CLKSTOP_PDSR_ONLY */
+	field(TEGRA_EMC_BASE + 0xc, 31, 31, 25), /* DRAM_CLKSTOP */
+	field(TEGRA_EMC_BASE + 0xc, 8, 15, 12), /* PRE_IDLE_CYCLES */
+	field(TEGRA_EMC_BASE + 0xc, 0, 0, 11), /* PRE_IDLE_EN */
+	field(TEGRA_EMC_BASE + 0x2bc, 28, 29, 28), /* CFG_DLL_LOCK_LIMIT */
+	field(TEGRA_EMC_BASE + 0x2bc, 6, 7, 30), /* CFG_DLL_MODE */
+	field(TEGRA_MC_BASE + 0x10c, 0, 0, 26), /* LL_CTRL */
+	field(TEGRA_MC_BASE + 0x10c, 1, 1, 27), /* LL_SEND_BOTH */
+};
+
+static const struct pmc_scratch_field emc_adr_cfg1[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x14, 8, 9, 8), /* EMEM1_BANKWIDTH */
+	field(TEGRA_EMC_BASE + 0x14, 0, 2, 5), /* EMEM1_COLWIDTH */
+	field(TEGRA_EMC_BASE + 0x14, 16, 19, 10), /* EMEM1_DEVSIZE */
+	field(TEGRA_EMC_BASE + 0x2dc, 24, 28, 0), /* TERM_DRVUP */
+	field(TEGRA_APB_MISC_BASE + 0x8d4, 0, 3, 14), /* XM2COMP_VREF_SEL */
+	field(TEGRA_APB_MISC_BASE + 0x8d8, 16, 18, 21), /* XM2VTTGEN_CAL_DRVDN */
+	field(TEGRA_APB_MISC_BASE + 0x8d8, 24, 26, 18), /* XM2VTTGEN_CAL_DRVUP */
+	field(TEGRA_APB_MISC_BASE + 0x8d8, 1, 1, 30), /* XM2VTTGEN_SHORT_PWRGND */
+	field(TEGRA_APB_MISC_BASE + 0x8d8, 0, 0, 31), /* XM2VTTGEN_SHORT */
+	field(TEGRA_APB_MISC_BASE + 0x8d8, 12, 14, 24), /* XM2VTTGEN_VAUXP_LEVEL */
+	field(TEGRA_APB_MISC_BASE + 0x8d8, 8, 10, 27), /* XM2VTTGEN_VCLAMP_LEVEL */
+};
+
+static const struct pmc_scratch_field emc_digital_dll[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x2bc, 1, 1, 23), /* DLI_TRIMMER_EN */
+	field(TEGRA_EMC_BASE + 0x2bc, 0, 0, 22), /* DLL_EN */
+	field(TEGRA_EMC_BASE + 0x2bc, 5, 5, 27), /* DLL_LOWSPEED */
+	field(TEGRA_EMC_BASE + 0x2bc, 2, 2, 24), /* DLL_OVERRIDE_EN */
+	field(TEGRA_EMC_BASE + 0x2bc, 8, 11, 28), /* DLL_UDSET */
+	field(TEGRA_EMC_BASE + 0x2bc, 4, 4, 26), /* PERBYTE_TRIMMER_OVERRIDE */
+	field(TEGRA_EMC_BASE + 0x2bc, 3, 3, 25), /* USE_SINGLE_DLL */
+	field(TEGRA_MC_BASE + 0xc, 0, 21, 0), /* EMEM_SIZE_KB */
+};
+
+static const struct pmc_scratch_field emc_dqs_clktrim[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x2d4, 0, 29, 0), /* DQS0_CLKTRIM - DQS3 + MCLK*/
+	field(TEGRA_APB_MISC_BASE + 0x8e4, 3, 3, 31), /* XM2CFGC_CTT_HIZ_EN */
+	field(TEGRA_APB_MISC_BASE + 0x8e4, 4, 4, 30), /* XM2CFGC_VREF_DQS_EN */
+};
+
+static const struct pmc_scratch_field emc_dq_clktrim[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x2d8, 0, 29, 0),
+	field(TEGRA_APB_MISC_BASE + 0x8e4, 2, 2, 30), /* XM2CFGC_PREEMP_EN */
+	field(TEGRA_APB_MISC_BASE + 0x8e4, 0, 0, 31), /* XM2CFGC_RX_FT_REC_EN */
+};
+
+static const struct pmc_scratch_field emc_dll_xform_dqs[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x2bc, 16, 25, 20), /* CFG_DLL_OVERRIDE_VAL */
+	field(TEGRA_EMC_BASE + 0x2c0, 0, 4, 0), /* DQS_MULT */
+	field(TEGRA_EMC_BASE + 0x2c0, 8, 22, 5), /* DQS_OFFS */
+	field(TEGRA_MC_BASE + 0x10c, 31, 31, 30), /* LL_DRAM_INTERLEAVE */
+};
+
+static const struct pmc_scratch_field emc_odt_rw[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x2c4, 0, 4, 0), /* QUSE_MULT */
+	field(TEGRA_EMC_BASE + 0x2c4, 8, 22, 5), /* QUSE_OFF */
+	field(TEGRA_EMC_BASE + 0xb4, 31, 31, 29), /* DISABLE_ODT_DURING_READ */
+	field(TEGRA_EMC_BASE + 0xb4, 30, 30, 28), /* B4_READ */
+	field(TEGRA_EMC_BASE + 0xb4, 0, 2, 25), /* RD_DELAY */
+	field(TEGRA_EMC_BASE + 0xb0, 31, 31, 24), /* ENABLE_ODT_DURING_WRITE */
+	field(TEGRA_EMC_BASE + 0xb0, 30, 30, 23), /* B4_WRITE */
+	field(TEGRA_EMC_BASE + 0xb0, 0, 2, 20), /* WR_DELAY */
+};
+
+static const struct pmc_scratch_field arbitration_xbar[] __initdata = {
+	field(TEGRA_AHB_GIZMO_BASE + 0xdc, 0, 31, 0),
+};
+
+static const struct pmc_scratch_field emc_zcal[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x2e0, 0, 23, 0), /* ZCAL_REF_INTERVAL */
+	field(TEGRA_EMC_BASE + 0x2e4, 0, 7, 24), /* ZCAL_WAIT_CNT */
+};
+
+static const struct pmc_scratch_field emc_ctt_term[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x2dc, 15, 19, 26), /* TERM_DRVDN */
+	field(TEGRA_EMC_BASE + 0x2dc, 8, 12, 21), /* TERM_OFFSET */
+	field(TEGRA_EMC_BASE + 0x2dc, 31, 31, 31), /* TERM_OVERRIDE */
+	field(TEGRA_EMC_BASE + 0x2dc, 0, 2, 18), /* TERM_SLOPE */
+	field(TEGRA_EMC_BASE + 0x2e8, 16, 23, 8), /* ZQ_MRW_MA */
+	field(TEGRA_EMC_BASE + 0x2e8, 0, 7, 0), /* ZQ_MRW_OP */
+};
+
+static const struct pmc_scratch_field xm2_cfgd[] __initdata = {
+	field(TEGRA_APB_MISC_BASE + 0x8e8, 16, 18, 9), /* CFGD0_DLYIN_TRM */
+	field(TEGRA_APB_MISC_BASE + 0x8e8, 20, 22, 6), /* CFGD1_DLYIN_TRM */
+	field(TEGRA_APB_MISC_BASE + 0x8e8, 24, 26, 3), /* CFGD2_DLYIN_TRM */
+	field(TEGRA_APB_MISC_BASE + 0x8e8, 28, 30, 0), /* CFGD3_DLYIN_TRM */
+	field(TEGRA_APB_MISC_BASE + 0x8e8, 3, 3, 12), /* XM2CFGD_CTT_HIZ_EN */
+	field(TEGRA_APB_MISC_BASE + 0x8e8, 2, 2, 13), /* XM2CFGD_PREEMP_EN */
+	field(TEGRA_APB_MISC_BASE + 0x8e8, 0, 0, 14), /* CM2CFGD_RX_FT_REC_EN */
+};
+
+struct pmc_scratch_reg {
+	const struct pmc_scratch_field *fields;
+	void __iomem *scratch_addr;
+	int num_fields;
+};
+
+#define scratch(offs, field_list)					\
+	{								\
+		.scratch_addr = IO_ADDRESS(TEGRA_PMC_BASE) + offs,	\
+		.fields = field_list,					\
+		.num_fields = ARRAY_SIZE(field_list),			\
+	}
+
+static const struct pmc_scratch_reg scratch[] __initdata = {
+	scratch(PMC_SCRATCH3, pllx),
+	scratch(PMC_SCRATCH5, emc_0),
+	scratch(PMC_SCRATCH6, emc_1),
+	scratch(PMC_SCRATCH7, emc_2),
+	scratch(PMC_SCRATCH8, emc_3),
+	scratch(PMC_SCRATCH9, emc_4),
+	scratch(PMC_SCRATCH10, emc_5),
+	scratch(PMC_SCRATCH11, emc_6),
+	scratch(PMC_SCRATCH12, emc_dqsib_dly),
+	scratch(PMC_SCRATCH13, emc_quse_dly),
+	scratch(PMC_SCRATCH14, emc_clktrim),
+	scratch(PMC_SCRATCH15, emc_autocal_fbio),
+	scratch(PMC_SCRATCH16, emc_autocal_interval),
+	scratch(PMC_SCRATCH17, emc_cfgs),
+	scratch(PMC_SCRATCH18, emc_adr_cfg1),
+	scratch(PMC_SCRATCH19, emc_digital_dll),
+	scratch(PMC_SCRATCH20, emc_dqs_clktrim),
+	scratch(PMC_SCRATCH21, emc_dq_clktrim),
+	scratch(PMC_SCRATCH22, emc_dll_xform_dqs),
+	scratch(PMC_SCRATCH23, emc_odt_rw),
+	scratch(PMC_SCRATCH25, arbitration_xbar),
+	scratch(PMC_SCRATCH35, emc_zcal),
+	scratch(PMC_SCRATCH36, emc_ctt_term),
+	scratch(PMC_SCRATCH40, xm2_cfgd),
+};
+
+void __init lp0_suspend_init(void)
+{
+	int i;
+	int j;
+	unsigned int v;
+	unsigned int r;
+
+	for (i = 0; i < ARRAY_SIZE(scratch); i++) {
+		r = 0;
+
+		for (j = 0; j < scratch[i].num_fields; j++) {
+			v = readl(IO_ADDRESS(scratch[i].fields[j].addr));
+			v >>= scratch[i].fields[j].shift_src;
+			v &= scratch[i].fields[j].mask;
+			v <<= scratch[i].fields[j].shift_dst;
+			r |= v;
+		}
+
+		writel(r, scratch[i].scratch_addr);
+	}
+}
+
+#define NUM_WAKE_EVENTS 31
+
+static int tegra_wake_event_irq[NUM_WAKE_EVENTS] = {
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PO5),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PV3),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PL1),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PB6),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PN7),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PA0),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PU5),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PU6),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PC7),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PS2),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PAA1),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PW3),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PW2),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PY6),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PV6),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PJ7),
+	INT_RTC,
+	INT_KBC,
+	INT_EXTERNAL_PMU,
+	-EINVAL, /* TEGRA_USB1_VBUS, */
+	-EINVAL, /* TEGRA_USB3_VBUS, */
+	-EINVAL, /* TEGRA_USB1_ID, */
+	-EINVAL, /* TEGRA_USB3_ID, */
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PI5),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PV2),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PS4),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PS5),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PS0),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PQ6),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PQ7),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PN2),
+};
+
+int tegra_irq_to_wake(int irq)
+{
+	int i;
+	for (i = 0; i < NUM_WAKE_EVENTS; i++)
+		if (tegra_wake_event_irq[i] == irq)
+			return i;
+
+	return -EINVAL;
+}
+
+int tegra_wake_to_irq(int wake)
+{
+	if (wake < 0)
+		return -EINVAL;
+
+	if (wake >= NUM_WAKE_EVENTS)
+		return -EINVAL;
+
+	return tegra_wake_event_irq[wake];
+}
diff --git a/arch/arm/mach-tegra/suspend.c b/arch/arm/mach-tegra/suspend.c
new file mode 100644
index 0000000..ded2c36
--- /dev/null
+++ b/arch/arm/mach-tegra/suspend.c
@@ -0,0 +1,764 @@
+/*
+ * arch/arm/mach-tegra/suspend.c
+ *
+ * CPU complex suspend & resume functions for Tegra SoCs
+ *
+ * Copyright (c) 2009-2010, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/suspend.h>
+#include <linux/slab.h>
+#include <linux/serial_reg.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+
+#include <asm/cacheflush.h>
+#include <asm/hardware/cache-l2x0.h>
+#include <asm/hardware/gic.h>
+#include <asm/localtimer.h>
+#include <asm/pgalloc.h>
+#include <asm/tlbflush.h>
+
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+#include <mach/legacy_irq.h>
+#include <mach/suspend.h>
+
+#include "board.h"
+#include "power.h"
+
+struct suspend_context {
+	/*
+	 * The next 7 values are referenced by offset in __restart_plls
+	 * in headsmp-t2.S, and should not be moved
+	 */
+	u32 pllx_misc;
+	u32 pllx_base;
+	u32 pllp_misc;
+	u32 pllp_base;
+	u32 pllp_outa;
+	u32 pllp_outb;
+	u32 pll_timeout;
+
+	u32 cpu_burst;
+	u32 clk_csite_src;
+	u32 twd_ctrl;
+	u32 twd_load;
+	u32 cclk_divider;
+};
+
+struct suspend_context tegra_sctx;
+
+static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
+static void __iomem *clk_rst = IO_ADDRESS(TEGRA_CLK_RESET_BASE);
+static void __iomem *flow_ctrl = IO_ADDRESS(TEGRA_FLOW_CTRL_BASE);
+static void __iomem *evp_reset = IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100;
+static void __iomem *tmrus = IO_ADDRESS(TEGRA_TMRUS_BASE);
+
+#define PMC_CTRL		0x0
+#define PMC_CTRL_LATCH_WAKEUPS	(1 << 5)
+#define PMC_WAKE_MASK		0xc
+#define PMC_WAKE_LEVEL		0x10
+#define PMC_DPAD_ORIDE		0x1C
+#define PMC_WAKE_DELAY		0xe0
+#define PMC_DPD_SAMPLE		0x20
+
+#define PMC_WAKE_STATUS		0x14
+#define PMC_SW_WAKE_STATUS	0x18
+#define PMC_COREPWRGOOD_TIMER	0x3c
+#define PMC_SCRATCH0		0x50
+#define PMC_SCRATCH1		0x54
+#define PMC_CPUPWRGOOD_TIMER	0xc8
+#define PMC_CPUPWROFF_TIMER	0xcc
+#define PMC_COREPWROFF_TIMER	PMC_WAKE_DELAY
+#define PMC_SCRATCH38		0x134
+#define PMC_SCRATCH39		0x138
+#define PMC_SCRATCH41		0x140
+
+#define CLK_RESET_CCLK_BURST	0x20
+#define CLK_RESET_CCLK_DIVIDER  0x24
+#define CLK_RESET_PLLC_BASE	0x80
+#define CLK_RESET_PLLM_BASE	0x90
+#define CLK_RESET_PLLX_BASE	0xe0
+#define CLK_RESET_PLLX_MISC	0xe4
+#define CLK_RESET_PLLP_BASE	0xa0
+#define CLK_RESET_PLLP_OUTA	0xa4
+#define CLK_RESET_PLLP_OUTB	0xa8
+#define CLK_RESET_PLLP_MISC	0xac
+
+#define CLK_RESET_SOURCE_CSITE	0x1d4
+
+#define CLK_RESET_CCLK_BURST_POLICY_SHIFT 28
+#define CLK_RESET_CCLK_BURST_POLICY_PLLM   3
+#define CLK_RESET_CCLK_BURST_POLICY_PLLX   8
+
+#define FLOW_CTRL_CPU_CSR	0x8
+#define FLOW_CTRL_CPU1_CSR	0x18
+
+unsigned long tegra_pgd_phys;  /* pgd used by hotplug & LP2 bootup */
+static pgd_t *tegra_pgd;
+void *tegra_context_area;
+
+static int tegra_last_pclk;
+static struct clk *tegra_pclk;
+static const struct tegra_suspend_platform_data *pdata;
+static unsigned long wb0_restore;
+static enum tegra_suspend_mode current_suspend_mode;
+
+static unsigned int tegra_time_in_suspend[32];
+
+static inline unsigned int time_to_bin(unsigned int time)
+{
+	return fls(time);
+}
+
+unsigned long tegra_cpu_power_good_time(void)
+{
+	if (WARN_ON_ONCE(!pdata))
+		return 5000;
+
+	return pdata->cpu_timer;
+}
+
+unsigned long tegra_cpu_power_off_time(void)
+{
+	if (WARN_ON_ONCE(!pdata))
+		return 5000;
+
+	return pdata->cpu_off_timer;
+}
+
+enum tegra_suspend_mode tegra_get_suspend_mode(void)
+{
+	if (!pdata)
+		return TEGRA_SUSPEND_NONE;
+
+	return pdata->suspend_mode;
+}
+
+static void set_power_timers(unsigned long us_on, unsigned long us_off,
+			     long rate)
+{
+	unsigned long long ticks;
+	unsigned long long pclk;
+
+	if (WARN_ON_ONCE(rate <= 0))
+		pclk = 100000000;
+	else
+		pclk = rate;
+
+	if (rate != tegra_last_pclk) {
+		ticks = (us_on * pclk) + 999999ull;
+		do_div(ticks, 1000000);
+		writel((unsigned long)ticks, pmc + PMC_CPUPWRGOOD_TIMER);
+
+		ticks = (us_off * pclk) + 999999ull;
+		do_div(ticks, 1000000);
+		writel((unsigned long)ticks, pmc + PMC_CPUPWROFF_TIMER);
+		wmb();
+	}
+	tegra_last_pclk = pclk;
+}
+
+/*
+ * create_suspend_pgtable
+ *
+ * Creates a page table with identity mappings of physical memory and IRAM
+ * for use when the MMU is off, in addition to all the regular kernel mappings.
+ */
+static int create_suspend_pgtable(void)
+{
+	tegra_pgd = pgd_alloc(&init_mm);
+	if (!tegra_pgd)
+		return -ENOMEM;
+
+	identity_mapping_add(tegra_pgd, PHYS_OFFSET, IO_IRAM_PHYS);
+	identity_mapping_add(tegra_pgd, IO_IRAM_PHYS, IO_IRAM_PHYS + SECTION_SIZE);
+
+	tegra_pgd_phys = virt_to_phys(tegra_pgd);
+
+	return 0;
+}
+
+static noinline void restore_cpu_complex(void)
+{
+	unsigned int reg;
+
+	/* restore original burst policy setting; PLLX state restored
+	 * by CPU boot-up code - wait for PLL stabilization if PLLX
+	 * was enabled, or if explicitly requested by caller */
+
+	BUG_ON(readl(clk_rst + CLK_RESET_PLLX_BASE) != tegra_sctx.pllx_base);
+
+	if (tegra_sctx.pllx_base & (1<<30)) {
+		while (readl(tmrus)-tegra_sctx.pll_timeout >= 0x80000000UL)
+			cpu_relax();
+	}
+	writel(tegra_sctx.cclk_divider, clk_rst + CLK_RESET_CCLK_DIVIDER);
+	writel(tegra_sctx.cpu_burst, clk_rst + CLK_RESET_CCLK_BURST);
+	writel(tegra_sctx.clk_csite_src, clk_rst + CLK_RESET_SOURCE_CSITE);
+
+	/* do not power-gate the CPU when flow controlled */
+	reg = readl(flow_ctrl + FLOW_CTRL_CPU_CSR);
+	reg &= ~((1<<5) | (1<<4) | 1); /* clear WFE bitmask */
+	reg |= (1<<14); /* write-1-clear event flag */
+	writel(reg, flow_ctrl + FLOW_CTRL_CPU_CSR);
+	wmb();
+
+#ifdef CONFIG_HAVE_ARM_TWD
+	writel(tegra_sctx.twd_ctrl, twd_base + 0x8);
+	writel(tegra_sctx.twd_load, twd_base + 0);
+#endif
+
+	gic_dist_restore(0);
+}
+
+static noinline void suspend_cpu_complex(void)
+{
+	unsigned int reg;
+	int i;
+
+	/* switch coresite to clk_m, save off original source */
+	tegra_sctx.clk_csite_src = readl(clk_rst + CLK_RESET_SOURCE_CSITE);
+	writel(3<<30, clk_rst + CLK_RESET_SOURCE_CSITE);
+
+	tegra_sctx.cpu_burst = readl(clk_rst + CLK_RESET_CCLK_BURST);
+	tegra_sctx.pllx_base = readl(clk_rst + CLK_RESET_PLLX_BASE);
+	tegra_sctx.pllx_misc = readl(clk_rst + CLK_RESET_PLLX_MISC);
+	tegra_sctx.pllp_base = readl(clk_rst + CLK_RESET_PLLP_BASE);
+	tegra_sctx.pllp_outa = readl(clk_rst + CLK_RESET_PLLP_OUTA);
+	tegra_sctx.pllp_outb = readl(clk_rst + CLK_RESET_PLLP_OUTB);
+	tegra_sctx.pllp_misc = readl(clk_rst + CLK_RESET_PLLP_MISC);
+	tegra_sctx.cclk_divider = readl(clk_rst + CLK_RESET_CCLK_DIVIDER);
+
+#ifdef CONFIG_HAVE_ARM_TWD
+	tegra_sctx.twd_ctrl = readl(twd_base + 0x8);
+	tegra_sctx.twd_load = readl(twd_base + 0);
+#endif
+
+	reg = readl(flow_ctrl + FLOW_CTRL_CPU_CSR);
+	/* clear any pending events, set the WFE bitmap to specify just
+	 * CPU0, and clear any pending events for this CPU */
+	reg &= ~(1 << 5); /* clear CPU1 WFE */
+	reg |= (1 << 14) | (1 << 4) | 1; /* enable CPU0 WFE */
+	writel(reg, flow_ctrl + FLOW_CTRL_CPU_CSR);
+	wmb();
+
+	for (i = 1; i < num_present_cpus(); i++) {
+		unsigned int offs = FLOW_CTRL_CPU1_CSR + (i - 1) * 8;
+		reg = readl(flow_ctrl + offs);
+		writel(reg | (1 << 14), flow_ctrl + offs);
+		wmb();
+	}
+
+	gic_cpu_exit(0);
+	gic_dist_save(0);
+}
+
+unsigned int tegra_suspend_lp2(unsigned int us)
+{
+	unsigned int mode;
+	unsigned long orig, reg;
+	unsigned int remain;
+
+	reg = readl(pmc + PMC_CTRL);
+	mode = (reg >> TEGRA_POWER_PMC_SHIFT) & TEGRA_POWER_PMC_MASK;
+	mode |= TEGRA_POWER_CPU_PWRREQ_OE;
+	if (pdata->separate_req)
+		mode |= TEGRA_POWER_PWRREQ_OE;
+	else
+		mode &= ~TEGRA_POWER_PWRREQ_OE;
+	mode &= ~TEGRA_POWER_EFFECT_LP0;
+
+	orig = readl(evp_reset);
+	writel(virt_to_phys(tegra_lp2_startup), evp_reset);
+
+	set_power_timers(pdata->cpu_timer, pdata->cpu_off_timer,
+			 clk_get_rate(tegra_pclk));
+
+	if (us)
+		tegra_lp2_set_trigger(us);
+
+	suspend_cpu_complex();
+	flush_cache_all();
+	outer_flush_all();
+	outer_disable();
+	barrier();
+
+	__cortex_a9_save(mode);
+	/* return from __cortex_a9_restore */
+	barrier();
+	cpu_init();
+	restore_cpu_complex();
+	l2x0_enable();
+
+	remain = tegra_lp2_timer_remain();
+	if (us)
+		tegra_lp2_set_trigger(0);
+
+	writel(orig, evp_reset);
+
+	return remain;
+}
+
+#ifdef CONFIG_PM
+
+/* ensures that sufficient time is passed for a register write to
+ * serialize into the 32KHz domain */
+static void pmc_32kwritel(u32 val, unsigned long offs)
+{
+	writel(val, pmc + offs);
+	udelay(130);
+}
+
+static u8 *iram_save;
+static unsigned int iram_save_size;
+static void __iomem *iram_code = IO_ADDRESS(TEGRA_IRAM_CODE_AREA);
+
+static void tegra_suspend_dram(bool do_lp0)
+{
+	unsigned int mode = TEGRA_POWER_SDRAM_SELFREFRESH;
+	unsigned long orig, reg;
+
+	orig = readl(evp_reset);
+	/* copy the reset vector and SDRAM shutdown code into IRAM */
+	memcpy(iram_save, iram_code, iram_save_size);
+	memcpy(iram_code, (void *)__tegra_lp1_reset, iram_save_size);
+
+	set_power_timers(pdata->cpu_timer, pdata->cpu_off_timer, 32768);
+
+	reg = readl(pmc + PMC_CTRL);
+	mode |= ((reg >> TEGRA_POWER_PMC_SHIFT) & TEGRA_POWER_PMC_MASK);
+
+	if (!do_lp0) {
+		writel(TEGRA_IRAM_CODE_AREA, evp_reset);
+
+		mode |= TEGRA_POWER_CPU_PWRREQ_OE;
+		if (pdata->separate_req)
+			mode |= TEGRA_POWER_PWRREQ_OE;
+		else
+			mode &= ~TEGRA_POWER_PWRREQ_OE;
+		mode &= ~TEGRA_POWER_EFFECT_LP0;
+
+		tegra_legacy_irq_set_lp1_wake_mask();
+	} else {
+		u32 boot_flag = readl(pmc + PMC_SCRATCH0);
+		pmc_32kwritel(boot_flag | 1, PMC_SCRATCH0);
+		pmc_32kwritel(wb0_restore, PMC_SCRATCH1);
+		writel(0x0, pmc + PMC_SCRATCH39);
+		mode |= TEGRA_POWER_CPU_PWRREQ_OE;
+		mode |= TEGRA_POWER_PWRREQ_OE;
+		mode |= TEGRA_POWER_EFFECT_LP0;
+
+		/* for platforms where the core & CPU power requests are
+		 * combined as a single request to the PMU, transition to
+		 * LP0 state by temporarily enabling both requests
+		 */
+		if (!pdata->separate_req) {
+			reg |= ((mode & TEGRA_POWER_PMC_MASK) <<
+				TEGRA_POWER_PMC_SHIFT);
+			pmc_32kwritel(reg, PMC_CTRL);
+			mode &= ~TEGRA_POWER_CPU_PWRREQ_OE;
+		}
+
+		tegra_set_lp0_wake_pads(pdata->wake_enb, pdata->wake_high,
+			pdata->wake_any);
+	}
+
+	suspend_cpu_complex();
+	flush_cache_all();
+	outer_flush_all();
+	outer_disable();
+
+	__cortex_a9_save(mode);
+	barrier();
+	cpu_init();
+	restore_cpu_complex();
+
+	writel(orig, evp_reset);
+	tegra_init_cache();
+
+	if (!do_lp0) {
+		memcpy(iram_code, iram_save, iram_save_size);
+		tegra_legacy_irq_restore_mask();
+	} else {
+		/* for platforms where the core & CPU power requests are
+		 * combined as a single request to the PMU, transition out
+		 * of LP0 state by temporarily enabling both requests
+		 */
+		if (!pdata->separate_req) {
+			reg = readl(pmc + PMC_CTRL);
+			reg |= (TEGRA_POWER_CPU_PWRREQ_OE << TEGRA_POWER_PMC_SHIFT);
+			pmc_32kwritel(reg, PMC_CTRL);
+			reg &= ~(TEGRA_POWER_PWRREQ_OE << TEGRA_POWER_PMC_SHIFT);
+			writel(reg, pmc + PMC_CTRL);
+		}
+	}
+
+	wmb();
+}
+
+static int tegra_suspend_prepare_late(void)
+{
+	disable_irq(INT_SYS_STATS_MON);
+	return 0;
+}
+
+static void tegra_suspend_wake(void)
+{
+	enable_irq(INT_SYS_STATS_MON);
+}
+
+static u8 uart_state[5];
+
+static int tegra_debug_uart_suspend(void)
+{
+	void __iomem *uart;
+	u32 lcr;
+
+	if (TEGRA_DEBUG_UART_BASE == 0)
+		return 0;
+
+	uart = IO_ADDRESS(TEGRA_DEBUG_UART_BASE);
+
+	lcr = readb(uart + UART_LCR * 4);
+
+	uart_state[0] = lcr;
+	uart_state[1] = readb(uart + UART_MCR * 4);
+
+	/* DLAB = 0 */
+	writeb(lcr & ~UART_LCR_DLAB, uart + UART_LCR * 4);
+
+	uart_state[2] = readb(uart + UART_IER * 4);
+
+	/* DLAB = 1 */
+	writeb(lcr | UART_LCR_DLAB, uart + UART_LCR * 4);
+
+	uart_state[3] = readb(uart + UART_DLL * 4);
+	uart_state[4] = readb(uart + UART_DLM * 4);
+
+	writeb(lcr, uart + UART_LCR * 4);
+
+	return 0;
+}
+
+static void tegra_debug_uart_resume(void)
+{
+	void __iomem *uart;
+	u32 lcr;
+
+	if (TEGRA_DEBUG_UART_BASE == 0)
+		return;
+
+	uart = IO_ADDRESS(TEGRA_DEBUG_UART_BASE);
+
+	lcr = uart_state[0];
+
+	writeb(uart_state[1], uart + UART_MCR * 4);
+
+	/* DLAB = 0 */
+	writeb(lcr & ~UART_LCR_DLAB, uart + UART_LCR * 4);
+
+	writeb(uart_state[2], uart + UART_IER * 4);
+
+	/* DLAB = 1 */
+	writeb(lcr | UART_LCR_DLAB, uart + UART_LCR * 4);
+
+	writeb(uart_state[3], uart + UART_DLL * 4);
+	writeb(uart_state[4], uart + UART_DLM * 4);
+
+	writeb(lcr, uart + UART_LCR * 4);
+}
+
+#define MC_SECURITY_START	0x6c
+#define MC_SECURITY_SIZE	0x70
+
+static int tegra_suspend_enter(suspend_state_t state)
+{
+	void __iomem *mc = IO_ADDRESS(TEGRA_MC_BASE);
+	unsigned long flags;
+	u32 mc_data[2];
+	bool do_lp0 = (current_suspend_mode == TEGRA_SUSPEND_LP0);
+	bool do_lp2 = (current_suspend_mode == TEGRA_SUSPEND_LP2);
+	int lp_state;
+	u64 rtc_before;
+	u64 rtc_after;
+	u64 secs;
+	u32 ms;
+
+	if (do_lp2)
+		lp_state = 2;
+	else if (do_lp0)
+		lp_state = 0;
+	else
+		lp_state = 1;
+
+	local_irq_save(flags);
+	local_fiq_disable();
+
+	pr_info("Entering suspend state LP%d\n", lp_state);
+	if (do_lp0) {
+		tegra_irq_suspend();
+		tegra_dma_suspend();
+		tegra_debug_uart_suspend();
+		tegra_pinmux_suspend();
+		tegra_timer_suspend();
+		tegra_gpio_suspend();
+		tegra_clk_suspend();
+
+		mc_data[0] = readl(mc + MC_SECURITY_START);
+		mc_data[1] = readl(mc + MC_SECURITY_SIZE);
+	}
+
+	rtc_before = tegra_rtc_read_ms();
+
+	if (do_lp2)
+		tegra_suspend_lp2(0);
+	else
+		tegra_suspend_dram(do_lp0);
+
+	rtc_after = tegra_rtc_read_ms();
+
+	/* Clear DPD sample */
+	writel(0x0, pmc + PMC_DPD_SAMPLE);
+
+	if (do_lp0) {
+		writel(mc_data[0], mc + MC_SECURITY_START);
+		writel(mc_data[1], mc + MC_SECURITY_SIZE);
+
+		tegra_clk_resume();
+		tegra_gpio_resume();
+		tegra_timer_resume();
+		tegra_pinmux_resume();
+		tegra_debug_uart_resume();
+		tegra_dma_resume();
+		tegra_irq_resume();
+	}
+
+	secs = rtc_after - rtc_before;
+	ms = do_div(secs, 1000);
+	pr_info("Suspended for %llu.%03u seconds\n", secs, ms);
+
+	tegra_time_in_suspend[time_to_bin(secs)]++;
+
+	local_fiq_enable();
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+static const struct platform_suspend_ops tegra_suspend_ops = {
+	.valid		= suspend_valid_only_mem,
+	.prepare_late	= tegra_suspend_prepare_late,
+	.wake		= tegra_suspend_wake,
+	.enter		= tegra_suspend_enter,
+};
+#endif
+
+void __init tegra_init_suspend(struct tegra_suspend_platform_data *plat)
+{
+	u32 reg, mode;
+
+	tegra_pclk = clk_get_sys(NULL, "pclk");
+	BUG_ON(!tegra_pclk);
+	pdata = plat;
+	(void)reg;
+	(void)mode;
+
+	if (plat->suspend_mode == TEGRA_SUSPEND_LP0 && tegra_lp0_vec_size) {
+		wb0_restore = tegra_lp0_vec_start;
+	} else {
+		pr_warning("Suspend mode LP0 requested, but missing lp0_vec\n");
+		pr_warning("Disabling LP0\n");
+		plat->suspend_mode = TEGRA_SUSPEND_LP1;
+	}
+
+	tegra_context_area = kzalloc(CONTEXT_SIZE_BYTES * NR_CPUS, GFP_KERNEL);
+
+	if (tegra_context_area && create_suspend_pgtable()) {
+		kfree(tegra_context_area);
+		tegra_context_area = NULL;
+	}
+
+#ifdef CONFIG_PM
+	iram_save_size = (unsigned long)__tegra_iram_end;
+	iram_save_size -= (unsigned long)__tegra_lp1_reset;
+
+	iram_save = kmalloc(iram_save_size, GFP_KERNEL);
+	if (!iram_save) {
+		pr_err("%s: unable to allocate memory for SDRAM self-refresh "
+		       "LP0/LP1 unavailable\n", __func__);
+		plat->suspend_mode = TEGRA_SUSPEND_LP2;
+	}
+	/* CPU reset vector for LP0 and LP1 */
+	writel(virt_to_phys(tegra_lp2_startup), pmc + PMC_SCRATCH41);
+
+	/* Always enable CPU power request; just normal polarity is supported */
+	reg = readl(pmc + PMC_CTRL);
+	BUG_ON(reg & (TEGRA_POWER_CPU_PWRREQ_POLARITY << TEGRA_POWER_PMC_SHIFT));
+	reg |= (TEGRA_POWER_CPU_PWRREQ_OE << TEGRA_POWER_PMC_SHIFT);
+	pmc_32kwritel(reg, PMC_CTRL);
+
+	/* Configure core power request and system clock control if LP0
+	   is supported */
+	writel(pdata->core_timer, pmc + PMC_COREPWRGOOD_TIMER);
+	writel(pdata->core_off_timer, pmc + PMC_COREPWROFF_TIMER);
+	reg = readl(pmc + PMC_CTRL);
+	mode = (reg >> TEGRA_POWER_PMC_SHIFT) & TEGRA_POWER_PMC_MASK;
+
+	mode &= ~TEGRA_POWER_SYSCLK_POLARITY;
+	mode &= ~TEGRA_POWER_PWRREQ_POLARITY;
+
+	if (!pdata->sysclkreq_high)
+		mode |= TEGRA_POWER_SYSCLK_POLARITY;
+	if (!pdata->corereq_high)
+		mode |= TEGRA_POWER_PWRREQ_POLARITY;
+
+	/* configure output inverters while the request is tristated */
+	reg |= (mode << TEGRA_POWER_PMC_SHIFT);
+	pmc_32kwritel(reg, PMC_CTRL);
+
+	/* now enable requests */
+	reg |= (TEGRA_POWER_SYSCLK_OE << TEGRA_POWER_PMC_SHIFT);
+	if (pdata->separate_req)
+		reg |= (TEGRA_POWER_PWRREQ_OE << TEGRA_POWER_PMC_SHIFT);
+	writel(reg, pmc + PMC_CTRL);
+
+	if (pdata->suspend_mode == TEGRA_SUSPEND_LP0)
+		lp0_suspend_init();
+
+	suspend_set_ops(&tegra_suspend_ops);
+#endif
+
+	current_suspend_mode = plat->suspend_mode;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static const char *tegra_suspend_name[TEGRA_MAX_SUSPEND_MODE] = {
+	[TEGRA_SUSPEND_NONE]	= "none",
+	[TEGRA_SUSPEND_LP2]	= "lp2",
+	[TEGRA_SUSPEND_LP1]	= "lp1",
+	[TEGRA_SUSPEND_LP0]	= "lp0",
+};
+
+static int tegra_suspend_debug_show(struct seq_file *s, void *data)
+{
+	seq_printf(s, "%s\n", tegra_suspend_name[*(int *)s->private]);
+	return 0;
+}
+
+static int tegra_suspend_debug_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, tegra_suspend_debug_show, inode->i_private);
+}
+
+static int tegra_suspend_debug_write(struct file *file,
+	const char __user *user_buf, size_t count, loff_t *ppos)
+{
+	char buf[32];
+	int buf_size;
+	int i;
+	struct seq_file *s = file->private_data;
+	enum tegra_suspend_mode *val = s->private;
+
+	memset(buf, 0x00, sizeof(buf));
+	buf_size = min(count, (sizeof(buf)-1));
+	if (copy_from_user(buf, user_buf, buf_size))
+		return -EFAULT;
+
+	for (i = 0; i < TEGRA_MAX_SUSPEND_MODE; i++) {
+		if (!strnicmp(buf, tegra_suspend_name[i],
+		    strlen(tegra_suspend_name[i]))) {
+			if (i > pdata->suspend_mode)
+				return -EINVAL;
+			*val = i;
+			return count;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static const struct file_operations tegra_suspend_debug_fops = {
+	.open		= tegra_suspend_debug_open,
+	.write		= tegra_suspend_debug_write,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int tegra_suspend_time_debug_show(struct seq_file *s, void *data)
+{
+	int bin;
+	seq_printf(s, "time (secs)  count\n");
+	seq_printf(s, "------------------\n");
+	for (bin = 0; bin < 32; bin++) {
+		if (tegra_time_in_suspend[bin] == 0)
+			continue;
+		seq_printf(s, "%4d - %4d %4u\n",
+			bin ? 1 << (bin - 1) : 0, 1 << bin,
+			tegra_time_in_suspend[bin]);
+	}
+	return 0;
+}
+
+static int tegra_suspend_time_debug_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, tegra_suspend_time_debug_show, NULL);
+}
+
+static const struct file_operations tegra_suspend_time_debug_fops = {
+	.open		= tegra_suspend_time_debug_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int __init tegra_suspend_debug_init(void)
+{
+	struct dentry *d;
+
+	d = debugfs_create_file("suspend_mode", 0755, NULL,
+		(void *)&current_suspend_mode, &tegra_suspend_debug_fops);
+	if (!d) {
+		pr_info("Failed to create suspend_mode debug file\n");
+		return -ENOMEM;
+	}
+
+	d = debugfs_create_file("suspend_time", 0755, NULL, NULL,
+		&tegra_suspend_time_debug_fops);
+	if (!d) {
+		pr_info("Failed to create suspend_time debug file\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+late_initcall(tegra_suspend_debug_init);
+#endif
diff --git a/arch/arm/mach-tegra/tegra2_save.S b/arch/arm/mach-tegra/tegra2_save.S
new file mode 100644
index 0000000..91f2ba0
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra2_save.S
@@ -0,0 +1,413 @@
+/*
+ * arch/arm/mach-tegra/tegra2_save.S
+ *
+ * CPU state save & restore routines for CPU hotplug
+ *
+ * Copyright (c) 2010, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/linkage.h>
+#include <linux/init.h>
+
+#include <asm/assembler.h>
+#include <asm/domain.h>
+#include <asm/ptrace.h>
+#include <asm/cache.h>
+#include <asm/vfpmacros.h>
+#include <asm/memory.h>
+#include <asm/hardware/cache-l2x0.h>
+
+#include <mach/iomap.h>
+#include <mach/io.h>
+
+#include "power.h"
+
+/*	.section ".cpuinit.text", "ax"*/
+
+#define TTB_FLAGS 0x6A	@ IRGN_WBWA, OC_RGN_WBWA, S, NOS
+
+#define EMC_CFG				0xc
+#define EMC_ADR_CFG			0x10
+#define EMC_REFRESH			0x70
+#define EMC_NOP				0xdc
+#define EMC_SELF_REF			0xe0
+#define EMC_REQ_CTRL			0x2b0
+#define EMC_EMC_STATUS			0x2b4
+
+#define PMC_CTRL			0x0
+#define PMC_CTRL_BFI_SHIFT		8
+#define PMC_CTRL_BFI_WIDTH		9
+#define PMC_SCRATCH38			0x134
+#define PMC_SCRATCH41			0x140
+
+#define CLK_RESET_CCLK_BURST		0x20
+#define CLK_RESET_CCLK_DIVIDER		0x24
+#define CLK_RESET_SCLK_BURST		0x28
+#define CLK_RESET_SCLK_DIVIDER		0x2c
+
+#define CLK_RESET_PLLC_BASE		0x80
+#define CLK_RESET_PLLM_BASE		0x90
+#define CLK_RESET_PLLP_BASE		0xa0
+
+#define FLOW_CTRL_HALT_CPU_EVENTS	0x0
+
+#include "power-macros.S"
+
+.macro emc_device_mask, rd, base
+	ldr	\rd, [\base, #EMC_ADR_CFG]
+	tst	\rd, #(0x3<<24)
+	moveq	\rd, #(0x1<<8)		@ just 1 device
+	movne	\rd, #(0x3<<8)		@ 2 devices
+.endm
+
+/*
+ *
+ *	__tear_down_master( r8 = context_pa, sp = power state )
+ *
+ *	  Set the clock burst policy to the selected wakeup source
+ *	  Enable CPU power-request mode in the PMC
+ *	  Put the CPU in wait-for-event mode on the flow controller
+ *	  Trigger the PMC state machine to put the CPU in reset
+ */
+ENTRY(__tear_down_master)
+__tear_down_master:
+#ifdef CONFIG_CACHE_L2X0
+	/* clean out the dirtied L2 lines, since all power transitions
+	 * cause the cache state to get invalidated (although LP1 & LP2
+	 * preserve the data in the L2, the control words (L2X0_CTRL,
+	 * L2X0_AUX_CTRL, etc.) need to be cleaned to L3 so that they
+	 * will be visible on reboot.  skip this for LP0, since the L2 cache
+	 * will be shutdown before we reach this point */
+	tst	sp, #TEGRA_POWER_EFFECT_LP0
+	bne	__l2_clean_done
+	mov32	r0, (TEGRA_ARM_PL310_BASE-IO_CPU_PHYS+IO_CPU_VIRT)
+	add	r3, r8, #(CONTEXT_SIZE_BYTES)
+	bic	r8, r8, #0x1f
+	add	r3, r3, #0x1f
+11:	str	r8, [r0, #L2X0_CLEAN_LINE_PA]
+	add	r8, r8, #32
+	cmp	r8, r3
+	blo	11b
+12:	ldr	r1, [r0, #L2X0_CLEAN_LINE_PA]
+	tst	r1, #1
+	bne	12b
+	mov	r1, #0
+	str	r1, [r0, #L2X0_CACHE_SYNC]
+13:	ldr	r1, [r0, #L2X0_CACHE_SYNC]
+	tst	r1, #1
+	bne	13b
+__l2_clean_done:
+#endif
+
+	tst	sp, #TEGRA_POWER_SDRAM_SELFREFRESH
+
+	/* preload all the address literals that are needed for the
+	 * CPU power-gating process, to avoid loads from SDRAM (which are
+	 * not supported once SDRAM is put into self-refresh.
+	 * LP0 / LP1 use physical address, since the MMU needs to be
+	 * disabled before putting SDRAM into self-refresh to avoid
+	 * memory access due to page table walks */
+	mov32	r0, (IO_APB_VIRT-IO_APB_PHYS)
+	mov32	r4, TEGRA_PMC_BASE
+	mov32	r0, (IO_PPSB_VIRT-IO_PPSB_PHYS)
+	mov32	r5, TEGRA_CLK_RESET_BASE
+	mov32	r6, TEGRA_FLOW_CTRL_BASE
+	mov32	r7, TEGRA_TMRUS_BASE
+
+	/* change page table pointer to tegra_pgd_phys, so that IRAM
+	 * and MMU shut-off will be mapped virtual == physical */
+	adr	r3, __tear_down_master_data
+	ldr	r3, [r3]		@ &tegra_pgd_phys
+	ldr	r3, [r3]
+	orr	r3, r3, #TTB_FLAGS
+	mov	r2, #0
+	mcr	p15, 0, r2, c13, c0, 1	@ reserved context
+	isb
+	mcr	p15, 0, r3, c2, c0, 0	@ TTB 0
+	isb
+
+	/* Obtain LP1 information.
+	 * R10 = LP1 branch target */
+	mov32	r2, __tegra_lp1_reset
+	mov32	r3, __tear_down_master_sdram
+	sub	r2, r3, r2
+	mov32	r3, (TEGRA_IRAM_CODE_AREA)
+	add	r10, r2, r3
+
+	mov32	r3, __shut_off_mmu
+
+	/* R9 = LP2 branch target */
+	mov32	r9, __tear_down_master_pll_cpu
+
+	/* Convert the branch targets
+	 * to physical addresses */
+	sub	r3, r3, #(PAGE_OFFSET - PHYS_OFFSET)
+	sub	r9, r9, #(PAGE_OFFSET - PHYS_OFFSET)
+	movne	r9, r10
+	bx	r3
+ENDPROC(__tear_down_master)
+	.type	__tear_down_master_data, %object
+__tear_down_master_data:
+	.long	tegra_pgd_phys
+	.size	__tear_down_master_data, . - __tear_down_master_data
+
+/*  START OF ROUTINES COPIED TO IRAM  */
+/*
+ *	__tegra_lp1_reset
+ *
+ *	  reset vector for LP1 restore; copied into IRAM during suspend.
+ *	  brings the system back up to a safe starting point (SDRAM out of
+ *	  self-refresh, PLLC, PLLM and PLLP reenabled, CPU running on PLLP,
+ *	  system clock running on the same PLL that it suspended at), and
+ *	  jumps to tegra_lp2_startup to restore PLLX and virtual addressing.
+ *	  physical address of tegra_lp2_startup expected to be stored in
+ *	  PMC_SCRATCH41
+ */
+	.align L1_CACHE_SHIFT
+ENTRY(__tegra_lp1_reset)
+__tegra_lp1_reset:
+	/* the CPU and system bus are running at 32KHz and executing from
+	 * IRAM when this code is executed; immediately switch to CLKM and
+	 * enable PLLP. */
+	mov32	r0, TEGRA_CLK_RESET_BASE
+	mov	r1, #(1<<28)
+	str	r1, [r0, #CLK_RESET_SCLK_BURST]
+	str	r1, [r0, #CLK_RESET_CCLK_BURST]
+	mov	r1, #0
+	str	r1, [r0, #CLK_RESET_SCLK_DIVIDER]
+	str	r1, [r0, #CLK_RESET_CCLK_DIVIDER]
+
+	ldr	r1, [r0, #CLK_RESET_PLLM_BASE]
+	tst	r1, #(1<<30)
+	orreq	r1, r1, #(1<<30)
+	streq	r1, [r0, #CLK_RESET_PLLM_BASE]
+	ldr	r1, [r0, #CLK_RESET_PLLP_BASE]
+	tst	r1, #(1<<30)
+	orreq	r1, r1, #(1<<30)
+	streq	r1, [r0, #CLK_RESET_PLLP_BASE]
+	ldr	r1, [r0, #CLK_RESET_PLLC_BASE]
+	tst	r1, #(1<<30)
+	orreq	r1, r1, #(1<<30)
+	streq	r1, [r0, #CLK_RESET_PLLC_BASE]
+	mov32	r7, TEGRA_TMRUS_BASE
+	ldr	r1, [r7]
+
+	/* since the optimized settings are still in SDRAM, there is
+	 * no need to store them back into the IRAM-local __lp1_pad_area */
+	add	r2, pc, #__lp1_pad_area-(.+8)
+padload:ldmia	r2!, {r3-r4}
+	cmp	r3, #0
+	beq	padload_done
+	str	r4, [r3]
+	b	padload
+padload_done:
+	ldr	r2, [r7]
+	add	r2, r2, #0x4	@ 4uS delay for DRAM pad restoration
+	wait_until r2, r7, r3
+	add	r1, r1, #0xff	@ 255uS delay for PLL stabilization
+	wait_until r1, r7, r3
+
+	str	r4, [r0, #CLK_RESET_SCLK_BURST]
+	mov32	r4, ((1<<28) | (4))	@ burst policy is PLLP
+	str	r4, [r0, #CLK_RESET_CCLK_BURST]
+
+	mov32	r0, TEGRA_EMC_BASE
+	ldr	r1, [r0, #EMC_CFG]
+	bic	r1, r1, #(1<<31)	@ disable DRAM_CLK_STOP
+	str	r1, [r0, #EMC_CFG]
+
+	mov	r1, #0
+	str	r1, [r0, #EMC_SELF_REF]	@ take DRAM out of self refresh
+	mov	r1, #1
+	str	r1, [r0, #EMC_NOP]
+	str	r1, [r0, #EMC_NOP]
+	str	r1, [r0, #EMC_REFRESH]
+
+	emc_device_mask r1, r0
+
+exit_selfrefresh_loop:
+	ldr	r2, [r0, #EMC_EMC_STATUS]
+	ands	r2, r2, r1
+	bne	exit_selfrefresh_loop
+
+	mov	r1, #0
+	str	r1, [r0, #EMC_REQ_CTRL]
+
+	mov32	r0, TEGRA_PMC_BASE
+	ldr	r0, [r0, #PMC_SCRATCH41]
+	mov	pc, r0
+ENDPROC(__tegra_lp1_reset)
+
+/*
+ *	__tear_down_master_sdram
+ *
+ *	  disables MMU, data cache, and puts SDRAM into self-refresh.
+ *	  must execute from IRAM.
+ */
+	.align L1_CACHE_SHIFT
+__tear_down_master_sdram:
+	mov32	r1, TEGRA_EMC_BASE
+	mov	r2, #3
+	str	r2, [r1, #EMC_REQ_CTRL]		@ stall incoming DRAM requests
+
+emcidle:ldr	r2, [r1, #EMC_EMC_STATUS]
+	tst	r2, #4
+	beq	emcidle
+
+	mov	r2, #1
+	str	r2, [r1, #EMC_SELF_REF]
+
+	emc_device_mask r2, r1
+
+emcself:ldr	r3, [r1, #EMC_EMC_STATUS]
+	and	r3, r3, r2
+	cmp	r3, r2
+	bne	emcself				@ loop until DDR in self-refresh
+
+	add	r2, pc, #__lp1_pad_area-(.+8)
+
+padsave:ldm	r2, {r0-r1}
+	cmp	r0, #0
+	beq	padsave_done
+	ldr	r3, [r0]
+	str	r1, [r0]
+	str	r3, [r2, #4]
+	add	r2, r2, #8
+	b	padsave
+padsave_done:
+
+	ldr	r0, [r5, #CLK_RESET_SCLK_BURST]
+	str	r0, [r2, #4]
+	dsb
+	b	__tear_down_master_pll_cpu
+ENDPROC(__tear_down_master_sdram)
+
+	.align	L1_CACHE_SHIFT
+	.type	__lp1_pad_area, %object
+__lp1_pad_area:
+	.word	TEGRA_APB_MISC_BASE + 0x8c8 /* XM2CFGCPADCTRL */
+	.word	0x8
+	.word	TEGRA_APB_MISC_BASE + 0x8cc /* XM2CFGDPADCTRL */
+	.word	0x8
+	.word	TEGRA_APB_MISC_BASE + 0x8d0 /* XM2CLKCFGPADCTRL */
+	.word	0x0
+	.word	TEGRA_APB_MISC_BASE + 0x8d4 /* XM2COMPPADCTRL */
+	.word	0x8
+	.word	TEGRA_APB_MISC_BASE + 0x8d8 /* XM2VTTGENPADCTRL */
+	.word	0x5500
+	.word	TEGRA_APB_MISC_BASE + 0x8e4 /* XM2CFGCPADCTRL2 */
+	.word	0x08080040
+	.word	TEGRA_APB_MISC_BASE + 0x8e8 /* XM2CFGDPADCTRL2 */
+	.word	0x0
+	.word	0x0	/* end of list */
+	.word	0x0	/* sclk_burst_policy */
+	.size	__lp1_pad_area, . - __lp1_pad_area
+
+	.align L1_CACHE_SHIFT
+__tear_down_master_pll_cpu:
+	ldr	r0, [r4, #PMC_CTRL]
+	bfi	r0, sp, #PMC_CTRL_BFI_SHIFT, #PMC_CTRL_BFI_WIDTH
+	str	r0, [r4, #PMC_CTRL]
+	tst	sp, #TEGRA_POWER_SDRAM_SELFREFRESH
+
+	/* in LP2 idle (SDRAM active), set the CPU burst policy to PLLP */
+	moveq	r0, #(2<<28)    /* burst policy = run mode */
+	orreq	r0, r0, #(4<<4) /* use PLLP in run mode burst */
+	streq	r0, [r5, #CLK_RESET_CCLK_BURST]
+	moveq	r0, #0
+	streq	r0, [r5, #CLK_RESET_CCLK_DIVIDER]
+	beq	__cclk_burst_set
+
+	/* in other modes, set system & CPU burst policies to 32KHz.
+	 * start by jumping to CLKM to safely disable PLLs, then jump
+	 * to CLKS */
+	mov	r0, #(1<<28)
+	str	r0, [r5, #CLK_RESET_SCLK_BURST]
+	str	r0, [r5, #CLK_RESET_CCLK_BURST]
+	mov	r0, #0
+	str	r0, [r5, #CLK_RESET_CCLK_DIVIDER]
+	str	r0, [r5, #CLK_RESET_SCLK_DIVIDER]
+
+	/* 2 us delay between changing sclk and disabling PLLs */
+	wait_for_us r1, r7, r9
+	add	r1, r1, #2
+	wait_until r1, r7, r9
+
+	/* switch to CLKS */
+	mov	r0, #0	/* burst policy = 32KHz */
+	str	r0, [r5, #CLK_RESET_SCLK_BURST]
+
+	/* disable PLLP, PLLM, PLLC in LP0 and LP1 states */
+	ldr	r0, [r5, #CLK_RESET_PLLM_BASE]
+	bic	r0, r0, #(1<<30)
+	str	r0, [r5, #CLK_RESET_PLLM_BASE]
+	ldr	r0, [r5, #CLK_RESET_PLLP_BASE]
+	bic	r0, r0, #(1<<30)
+	str	r0, [r5, #CLK_RESET_PLLP_BASE]
+	ldr	r0, [r5, #CLK_RESET_PLLC_BASE]
+	bic	r0, r0, #(1<<30)
+	str	r0, [r5, #CLK_RESET_PLLC_BASE]
+
+__cclk_burst_set:
+	mov	r0, #(4<<29)			/* STOP_UNTIL_IRQ */
+	orr	r0, r0, #(1<<10) | (1<<8)	/* IRQ_0, FIQ_0	*/
+	ldr	r1, [r7]
+	str	r1, [r4, #PMC_SCRATCH38]
+	dsb
+	str	r0, [r6, #FLOW_CTRL_HALT_CPU_EVENTS]
+	dsb
+	ldr	r0, [r6, #FLOW_CTRL_HALT_CPU_EVENTS] /* memory barrier */
+
+halted:	dsb
+	wfe	/* CPU should be power gated here */
+	isb
+	b	halted
+ENDPROC(__tear_down_master_pll_cpu)
+
+/*
+ *	__put_cpu_in_reset(cpu_nr)
+ *
+ *	 puts the specified CPU in wait-for-event mode on the flow controller
+ *	 and puts the CPU in reset
+ */
+ENTRY(__put_cpu_in_reset)
+__put_cpu_in_reset:
+	cmp	r0, #0
+	subne	r1, r0, #1
+	movne	r1, r1, lsl #3
+	addne	r1, r1, #0x14
+	moveq	r1, #0			@ r1 = CPUx_HALT_EVENTS register offset
+	mov32	r7, (TEGRA_FLOW_CTRL_BASE-IO_PPSB_PHYS+IO_PPSB_VIRT)
+	mov	r2, #(0x2<<29)
+	str	r2, [r7, r1]		@ put flow controller in wait event mode
+	isb
+	dsb
+	movw	r1, 0x1011
+	mov	r1, r1, lsl r0
+	mov32	r7, (TEGRA_CLK_RESET_BASE-IO_PPSB_PHYS+IO_PPSB_VIRT)
+	str	r1, [r7, #0x340]	@ put slave CPU in reset
+	isb
+	dsb
+	b	.
+ENDPROC(__put_cpu_in_reset)
+
+/* dummy symbol for end of IRAM */
+	.align L1_CACHE_SHIFT
+ENTRY(__tegra_iram_end)
+__tegra_iram_end:
+	b	.
+ENDPROC(__tegra_iram_end)
-- 
1.7.3.1


^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 13/28] ARM: tegra: Add suspend and hotplug support
@ 2011-01-24  2:01   ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-arm-kernel

Tegra supports three low power modes that involve powering down the CPU.

LP2 powers down both CPU cores and the GICs, but leaves the core
peripherals, including the memory controller and the legacy
interrupt controller, enabled.  The legacy interrupt controller
is used as the wakeup source, and any interrupt can wake the device.
LP2 can be used in idle.

LP1 is the same as LP2, but in addition turns off the memory
controller and puts the DDR memory in self-refresh.  Any interrupt
can wake the device. LP1 could be used in idle if no peripherals
are doing DMA.

LP0 turns off everything in the SoC except the RTC and a power
management controller, both of which run off a 32 kHz clock.
The power management controller has 32 wake sources, all other
interrupts can not be used to wake from LP0.

LP2 idle mode power-gates the main CPU complex, requiring a
full processor state save and restore from a reset vector.

Processor context area is allocated during platform initialization
from the kernel, and mapped into the hotplug page tables (which also
serve as the initial page tables for the LP2 main processor reset).

Platform-specific data (power good times, PMU capabilities, etc.) must be
specified when registering the suspend operations to ensure that platform
power sequencing restrictions are maintained.

Since all device interrupts (except timers) are disabled in the suspend
path, the wakeup interrupts need to be manually unmasked before entering
into a suspend state or the processor will never wake up; these forced-unmask
interrupts are re-masked immediately in the resume path to prevent the
kernel from live-locking prior to driver resume.

In both LP0 and LP1, SDRAM is placed into self-refresh. in order to safely
perform this transition, the final shutdown procedure responsible for

  * turning off the MMU and L1 data cache
  * putting memory into self-refresh
  * setting the DDR pads to the lowest power state
  * and turning off PLLs

is copied into IRAM (at the address TEGRA_IRAM_BASE + SZ_4K) at the
start of the suspend process.

In LP1 mode (like LP2), the CPU is reset and executes the code specified
at the EVP reset vector.  Since SDRAM is in self-refresh, this code must
also be located in IRAM, and it must re-enable DRAM before restoring the
full context.  In this implementation, it enables the CPU on PLLP, enables
PLLC and PLLM, restores the SCLK burst policy, and jumps to the LP2 reset
vector to restore the rest of the system (MMU, PLLX, coresite, etc.).  The
LP2 reset vector is expected to be found in PMC_SCRATCH1, and is
initialized during system-bootup.

In LP0 mode, the core voltage domain is also shutoff.  As a result, all
of the volatile state in the core voltage domain (e.g., pinmux registers,
clock registers, etc.) must be saved to memory so that it can be restored
after the system resumes.  A limited set of wakeups are available from LP0,
and the correct levels for the wakeups must be programmed into the PMC
wakepad configuration register prior to system shutdown.  On resume, the
system resets into the boot ROM, and the boot ROM restores SDRAM and other
system state using values saved during kernel initialization in the PMC
scratch registers.

Resuming from LP0 requires the boot ROM to supply a signed recovery codeblob
to the kernel; the kernel expects that the length and address of this blob
is supplied with the lp0_vec= command line argument; if not present, suspend-
to-LP0 will be disabled

For simplicity, the outer cache is shutdown for both LP0 and LP1; it
is possible to optimize the LP1 routine to bypass outer cache shutdown
and restart.

To save power, SMP tegra SoCs place non-boot CPUs in reset when they
are removed from the scheduling cluster using CPU hotplug.

Slave CPUs save their contexts (incl. CP15 and VFP state) out to a
reserved memory region, cancel SMP operation, and write to the SoC
reset controller to disable themselves.  This is done with caches and
MMU enabled, so care is taken to ensure that all the dirty context cache
lines are cleaned out to the PoC before shutting down.

When re-enabled, slave CPUs execute a hotplug boot routine which mirrors
the initial configuration performed by secondary_startup, but after
enabling the MMU "return" to __cortex_a9_restore which restores the
saved state from the context area, and returns to platform_cpu_die.

Includes fixes from:
Scott Williams <scwilliams@nvidia.com>
Aleksandr Frid <afrid@nvidia.com>
Vik Kasivajhula <tkasivajhula@nvidia.com>
Bharat Nihalani <Kbnihalani@nvidia.com>

Original-author: Gary King <gking@nvidia.com>
Signed-off-by: Gary King <gking@nvidia.com>
Signed-off-by: Colin Cross <ccross@android.com>
---
v2: Updated for comments to v1
    Use memblock_reserve to reserve LP0 boot vector
    Don't save VFP registers, they are already saved
    Don't save context switch registers, reinitialize them 
       with cpu_init
    Don't save breakpoint and watchpoint registers
    Don't save L2X0 registers, reinitialize them with
       l2x0_init
    Update for changes to ARM smp hotplug
    De-obfuscate macros in suspend-t2.c
    Use identity_mapping_add to create suspend page tables
    Fix checkpatch issues


 arch/arm/mach-tegra/Makefile               |   10 +-
 arch/arm/mach-tegra/board.h                |    4 +
 arch/arm/mach-tegra/common.c               |   32 ++-
 arch/arm/mach-tegra/cortex-a9.S            |  504 ++++++++++++++++++
 arch/arm/mach-tegra/headsmp-t2.S           |  216 ++++++++
 arch/arm/mach-tegra/headsmp.S              |   61 ---
 arch/arm/mach-tegra/hotplug.c              |  126 -----
 arch/arm/mach-tegra/include/mach/suspend.h |   42 ++
 arch/arm/mach-tegra/platsmp.c              |   89 +++-
 arch/arm/mach-tegra/power-macros.S         |   57 ++
 arch/arm/mach-tegra/power.h                |   64 +++
 arch/arm/mach-tegra/suspend-t2.c           |  414 +++++++++++++++
 arch/arm/mach-tegra/suspend.c              |  764 ++++++++++++++++++++++++++++
 arch/arm/mach-tegra/tegra2_save.S          |  413 +++++++++++++++
 14 files changed, 2596 insertions(+), 200 deletions(-)
 create mode 100644 arch/arm/mach-tegra/cortex-a9.S
 create mode 100644 arch/arm/mach-tegra/headsmp-t2.S
 delete mode 100644 arch/arm/mach-tegra/headsmp.S
 delete mode 100644 arch/arm/mach-tegra/hotplug.c
 create mode 100644 arch/arm/mach-tegra/power-macros.S
 create mode 100644 arch/arm/mach-tegra/power.h
 create mode 100644 arch/arm/mach-tegra/suspend-t2.c
 create mode 100644 arch/arm/mach-tegra/suspend.c
 create mode 100644 arch/arm/mach-tegra/tegra2_save.S

diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index 00a6ba3..0bf1441 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -6,13 +6,19 @@ obj-y                                   += timer.o
 obj-y                                   += gpio.o
 obj-y                                   += pinmux.o
 obj-y                                   += powergate.o
+obj-y					+= suspend.o
 obj-y					+= fuse.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += clock.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += tegra2_clocks.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += tegra2_dvfs.o
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += suspend-t2.o
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC)		+= tegra2_save.o
+obj-$(CONFIG_CPU_V7)			+= cortex-a9.o
+
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)		+= pinmux-t2-tables.o
-obj-$(CONFIG_SMP)                       += platsmp.o localtimer.o headsmp.o
-obj-$(CONFIG_HOTPLUG_CPU)               += hotplug.o
+obj-$(CONFIG_LOCAL_TIMER)               += localtimer.o
+obj-$(CONFIG_SMP)                       += platsmp.o
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += headsmp-t2.o
 obj-$(CONFIG_TEGRA_SYSTEM_DMA)		+= dma.o
 obj-$(CONFIG_CPU_FREQ)                  += cpu-tegra.o
 obj-$(CONFIG_TEGRA_PCI)			+= pcie.o
diff --git a/arch/arm/mach-tegra/board.h b/arch/arm/mach-tegra/board.h
index 0de565c..ae5c333 100644
--- a/arch/arm/mach-tegra/board.h
+++ b/arch/arm/mach-tegra/board.h
@@ -27,7 +27,11 @@ void __init tegra_common_init(void);
 void __init tegra_map_common_io(void);
 void __init tegra_init_irq(void);
 void __init tegra_init_clock(void);
+void __init tegra_reserve(void);
 int __init tegra_pcie_init(bool init_port0, bool init_port1);
 
+extern unsigned long tegra_lp0_vec_start;
+extern unsigned long tegra_lp0_vec_size;
+
 extern struct sys_timer tegra_timer;
 #endif
diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c
index 7c91e2b..53ddf55 100644
--- a/arch/arm/mach-tegra/common.c
+++ b/arch/arm/mach-tegra/common.c
@@ -21,6 +21,7 @@
 #include <linux/io.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
+#include <linux/memblock.h>
 
 #include <asm/hardware/cache-l2x0.h>
 
@@ -30,6 +31,10 @@
 #include "board.h"
 #include "clock.h"
 #include "fuse.h"
+#include "power.h"
+
+unsigned long tegra_lp0_vec_start;
+unsigned long tegra_lp0_vec_size;
 
 static __initdata struct tegra_clk_init_table common_clk_init_table[] = {
 	/* name		parent		rate		enabled */
@@ -45,7 +50,7 @@ static __initdata struct tegra_clk_init_table common_clk_init_table[] = {
 	{ NULL,		NULL,		0,		0},
 };
 
-void __init tegra_init_cache(void)
+void tegra_init_cache(void)
 {
 #ifdef CONFIG_CACHE_L2X0
 	void __iomem *p = IO_ADDRESS(TEGRA_ARM_PERIF_BASE) + 0x3000;
@@ -68,3 +73,28 @@ void __init tegra_common_init(void)
 	tegra_dma_init();
 #endif
 }
+
+static int __init tegra_lp0_vec_arg(char *options)
+{
+	char *p = options;
+
+	tegra_lp0_vec_size = memparse(p, &p);
+	if (*p == '@')
+		tegra_lp0_vec_start = memparse(p+1, &p);
+
+	return 0;
+}
+early_param("lp0_vec", tegra_lp0_vec_arg);
+
+void __init tegra_reserve(void)
+{
+	if (tegra_lp0_vec_size)
+		if (memblock_reserve(tegra_lp0_vec_start, tegra_lp0_vec_size))
+			pr_err("Failed to reserve lp0_vec %08lx@%08lx\n",
+				tegra_lp0_vec_size, tegra_lp0_vec_start);
+
+	pr_info("Tegra reserved memory:\n"
+		"LP0:                    %08lx - %08lx\n",
+		tegra_lp0_vec_start,
+		tegra_lp0_vec_start + tegra_lp0_vec_size - 1);
+}
diff --git a/arch/arm/mach-tegra/cortex-a9.S b/arch/arm/mach-tegra/cortex-a9.S
new file mode 100644
index 0000000..370d6c3
--- /dev/null
+++ b/arch/arm/mach-tegra/cortex-a9.S
@@ -0,0 +1,504 @@
+/*
+ * arch/arm/mach-tegra/cortex-a9.S
+ *
+ * CPU state save & restore routines for CPU hotplug
+ *
+ * Copyright (c) 2010, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/linkage.h>
+#include <linux/init.h>
+
+#include <asm/assembler.h>
+#include <asm/domain.h>
+#include <asm/ptrace.h>
+#include <asm/cache.h>
+#include <asm/vfpmacros.h>
+#include <asm/hardware/cache-l2x0.h>
+
+#include <mach/iomap.h>
+#include <mach/io.h>
+
+/*	.section ".cpuinit.text", "ax"*/
+
+#define TTB_FLAGS 0x6A	@ IRGN_WBWA, OC_RGN_WBWA, S, NOS
+
+/*
+ * spooled CPU context is 1KB / CPU
+ */
+#define CTX_SP		0
+#define CTX_CPSR	4
+#define CTX_SPSR	8
+#define CTX_CPACR	12
+#define CTX_CSSELR	16
+#define CTX_SCTLR	20
+#define CTX_ACTLR	24
+#define CTX_PCTLR	28
+
+#define CTX_FPEXC	32
+#define CTX_FPSCR	36
+#define CTX_DIAGNOSTIC	40
+
+#define CTX_TTBR0	48
+#define CTX_TTBR1	52
+#define CTX_TTBCR	56
+#define CTX_DACR	60
+#define CTX_PAR		64
+#define CTX_PRRR	68
+#define CTX_NMRR	72
+#define CTX_VBAR	76
+#define CTX_CONTEXTIDR	80
+#define CTX_TPIDRURW	84
+#define CTX_TPIDRURO	88
+#define CTX_TPIDRPRW	92
+
+#define CTX_SVC_SP	0
+#define CTX_SVC_LR	-1	@ stored on stack
+#define CTX_SVC_SPSR	8
+
+#define CTX_SYS_SP	96
+#define CTX_SYS_LR	100
+
+#define CTX_ABT_SPSR	112
+#define CTX_ABT_SP	116
+#define CTX_ABT_LR	120
+
+#define CTX_UND_SPSR	128
+#define CTX_UND_SP	132
+#define CTX_UND_LR	136
+
+#define CTX_IRQ_SPSR	144
+#define CTX_IRQ_SP	148
+#define CTX_IRQ_LR	152
+
+#define CTX_FIQ_SPSR	160
+#define CTX_FIQ_R8	164
+#define CTX_FIQ_R9	168
+#define CTX_FIQ_R10	172
+#define CTX_FIQ_R11	178
+#define CTX_FIQ_R12	180
+#define CTX_FIQ_SP	184
+#define CTX_FIQ_LR	188
+
+/* context only relevant for master cpu */
+#ifdef CONFIG_CACHE_L2X0
+#define CTX_L2_CTRL	224
+#define CTX_L2_AUX	228
+#define CTX_L2_TAG_CTRL	232
+#define CTX_L2_DAT_CTRL	236
+#define CTX_L2_PREFETCH 240
+#endif
+
+#define CTX_VFP_REGS	256
+#define CTX_VFP_SIZE	(32 * 8)
+
+#define CTX_CP14_REGS	512
+#define CTS_CP14_DSCR	512
+#define CTX_CP14_WFAR	516
+#define CTX_CP14_VCR	520
+#define CTX_CP14_CLAIM	524
+
+/* Each of the folowing is 2 32-bit registers */
+#define CTS_CP14_BKPT_0	528
+#define CTS_CP14_BKPT_1	536
+#define CTS_CP14_BKPT_2	544
+#define CTS_CP14_BKPT_3	552
+#define CTS_CP14_BKPT_4	560
+#define CTS_CP14_BKPT_5	568
+
+/* Each of the folowing is 2 32-bit registers */
+#define CTS_CP14_WPT_0	576
+#define CTS_CP14_WPT_1	584
+#define CTS_CP14_WPT_2	592
+#define CTS_CP14_WPT_3	600
+
+#include "power.h"
+#include "power-macros.S"
+
+.macro	ctx_ptr, rd, tmp
+	cpu_id	\tmp
+	mov32	\rd, tegra_context_area
+	ldr	\rd, [\rd]
+	add	\rd, \rd, \tmp, lsl #(CONTEXT_SIZE_BYTES_SHIFT)
+.endm
+
+.macro	translate, pa, va, tmp
+	mov	\tmp, #0x1000
+	sub	\tmp, \tmp, #1
+	bic	\pa, \va, \tmp
+	mcr	p15, 0, \pa, c7, c8, 1
+	mrc	p15, 0, \pa, c7, c4, 0
+	bic	\pa, \pa, \tmp
+	and	\tmp, \va, \tmp
+	orr	\pa, \pa, \tmp
+.endm
+
+/*
+ *	__cortex_a9_save(unsigned int mode)
+ *
+ *	 spools out the volatile processor state to memory, so that
+ *	 the CPU may be safely powered down. does not preserve:
+ *	 - CP15 c0 registers (except cache size select 2,c0/c0,0)
+ *	 - CP15 c1 secure registers (c1/c1, 0-3)
+ *	 - CP15 c5 fault status registers (c5/c0 0&1, c5/c1 0&1)
+ *	 - CP15 c6 fault address registers (c6/c0 0&2)
+ *	 - CP15 c9 performance monitor registers (c9/c12 0-5,
+ *	     c9/c13 0-2, c9/c14 0-2)
+ *	 - CP15 c10 TLB lockdown register (c10/c0, 0)
+ *	 - CP15 c12 MVBAR (c12/c0, 1)
+ *	 - CP15 c15 TLB lockdown registers
+ */
+	.align L1_CACHE_SHIFT
+ENTRY(__cortex_a9_save)
+	mrs	r3, cpsr
+	cps	0x13			@ save off svc registers
+	mov	r1, sp
+	stmfd	sp!, {r3-r12, lr}
+
+	bic	r2, sp, #(L1_CACHE_BYTES-1)
+
+1:	mcr	p15, 0, r2, c7, c14, 1	@ clean out dirty stack cachelines
+	add	r2, r2, #L1_CACHE_BYTES
+	cmp	r2, r1
+	ble	1b
+	dsb
+
+	ctx_ptr	r8, r9
+	mov	r12, r0
+
+	/* zero-out context area */
+	mov	r9, r8
+	add	r10, r8, #(CONTEXT_SIZE_BYTES)
+	mov	r0, #0
+	mov	r1, #0
+	mov	r2, #0
+	mov	r3, #0
+	mov	r4, #0
+	mov	r5, #0
+	mov	r6, #0
+	mov	r7, #0
+2:	stmia	r9!, {r0-r7}
+	cmp	r9, r10
+	blo	2b
+
+	mov	r0, sp
+	mov	sp, r12			@ sp holds the power mode
+	mrs	r1, cpsr
+	mrs	r2, spsr
+
+	mrc	p15, 0, r3, c1, c0, 2	@ cpacr
+	stmia	r8, {r0-r3}
+	mrc	p15, 2, r0, c0, c0, 0	@ csselr
+	mrc	p15, 0, r1, c1, c0, 0	@ sctlr
+	mrc	p15, 0, r2, c1, c0, 1	@ actlr
+	mrc	p15, 0, r4, c15, c0, 0	@ pctlr
+	add	r9, r8, #CTX_CSSELR
+	stmia	r9, {r0-r2, r4}
+
+	mrc	p15, 0, r0, c15, c0, 1	@ diag
+	str	r0, [r8, #CTX_DIAGNOSTIC]
+
+	add	r9, r8, #CTX_TTBR0
+	mrc	p15, 0, r0, c2, c0, 0	@ TTBR0
+	mrc	p15, 0, r1, c2, c0, 1	@ TTBR1
+	mrc	p15, 0, r2, c2, c0, 2	@ TTBCR
+	mrc	p15, 0, r3, c3, c0, 0	@ domain access control reg
+	mrc	p15, 0, r4, c7, c4, 0	@ PAR
+	mrc	p15, 0, r5, c10, c2, 0	@ PRRR
+	mrc	p15, 0, r6, c10, c2, 1	@ NMRR
+	mrc	p15, 0, r7, c12, c0, 0	@ VBAR
+	stmia	r9!, {r0-r7}
+	mrc	p15, 0, r0, c13, c0, 1	@ CONTEXTIDR
+	mrc	p15, 0, r1, c13, c0, 2	@ TPIDRURW
+	mrc	p15, 0, r2, c13, c0, 3	@ TPIDRURO
+	mrc	p15, 0, r3, c13, c0, 4	@ TPIDRPRW
+	stmia	r9, {r0-r3}
+
+	mov	r0, r8
+	add	r1, r8, #CTX_FIQ_SPSR
+	cps	0x11			@ FIQ mode
+	mrs	r7, spsr
+	stmia	r1, {r7-r12,sp,lr}
+
+	cps	0x13			@ back to SVC
+	mov	r8, r0
+
+__cortex_a9_save_clean_cache:
+	mov	r10, r8
+	add	r9, r10, #(CONTEXT_SIZE_BYTES)
+	add	r9, r9, #(L1_CACHE_BYTES-1)
+	bic	r10, r10, #(L1_CACHE_BYTES-1)
+	bic	r9, r9, #(L1_CACHE_BYTES-1)
+
+3:	mcr	p15, 0, r10, c7, c10, 1
+	add	r10, r10, #L1_CACHE_BYTES
+	cmp	r10, r9
+	blo	3b
+	dsb
+
+	translate r10, r8, r1
+
+	mov	r0, #0
+	mcr	p15, 0, r0, c1, c0, 1	@ exit coherency
+	isb
+	cpu_id	r0
+	mov32	r1, (TEGRA_ARM_PERIF_BASE-IO_CPU_PHYS+IO_CPU_VIRT+0xC)
+	mov	r3, r0, lsl #2
+	mov	r2, #0xf
+	mov	r2, r2, lsl r3
+	str	r2, [r1]		@ invalidate SCU tags for CPU
+
+	cmp	r0, #0
+	bne	__put_cpu_in_reset
+	mov	r8, r10
+	b	__tear_down_master
+ENDPROC(__cortex_a9_save)
+
+/*
+ *	__cortex_a9_restore
+ *
+ *	 reloads the volatile CPU state from the context area
+ *	 the MMU should already be enabled using the secondary_data
+ *	 page tables for cpu_up before this function is called, and the
+ *	 CPU should be coherent with the SMP complex
+ */
+	.align L1_CACHE_SHIFT
+ENTRY(__cortex_a9_restore)
+	cps	0x13
+	ctx_ptr	r0, r9
+
+	cps	0x11			@ FIQ mode
+	add	r1, r0, #CTX_FIQ_SPSR
+	ldmia	r1, {r7-r12,sp,lr}
+	msr	spsr_fsxc, r7
+
+	cps	0x13			@ back to SVC
+	mov	r8, r0
+
+	add	r9, r8, #CTX_CSSELR
+	ldmia	r9, {r0-r3}
+
+	mcr	p15, 2, r0, c0, c0, 0	@ csselr
+	mcr	p15, 0, r1, c1, c0, 0	@ sctlr
+	mcr	p15, 0, r2, c1, c0, 1	@ actlr
+	mcr	p15, 0, r3, c15, c0, 0	@ pctlr
+
+	add	r9, r8, #CTX_TTBR0
+	ldmia	r9!, {r0-r7}
+
+	mcr	p15, 0, r4, c7, c4, 0	@ PAR
+	mcr	p15, 0, r7, c12, c0, 0	@ VBAR
+	mcr	p15, 0, r3, c3, c0, 0	@ domain access control reg
+	isb
+	mcr	p15, 0, r2, c2, c0, 2	@ TTBCR
+	isb
+	mcr	p15, 0, r5, c10, c2, 0	@ PRRR
+	isb
+	mcr	p15, 0, r6, c10, c2, 1	@ NMRR
+	isb
+
+	ldmia	r9, {r4-r7}
+
+	mcr	p15, 0, r5, c13, c0, 2	@ TPIDRURW
+	mcr	p15, 0, r6, c13, c0, 3	@ TPIDRURO
+	mcr	p15, 0, r7, c13, c0, 4	@ TPIDRPRW
+
+	ldmia	r8, {r5-r7, lr}
+
+	/* perform context switch to previous context */
+	mov	r9, #0
+	mcr	p15, 0, r9, c13, c0, 1	@ set reserved context
+	isb
+	mcr	p15, 0, r0, c2, c0, 0	@ TTBR0
+	isb
+	mcr	p15, 0, r4, c13, c0, 1	@ CONTEXTIDR
+	isb
+	mcr	p15, 0, r1, c2, c0, 1	@ TTBR1
+	isb
+
+	mov	r4, #0
+	mcr	p15, 0, r4, c8, c3, 0	@ invalidate TLB
+	mcr	p15, 0, r4, c7, c5, 6	@ flush BTAC
+	mcr	p15, 0, r4, c7, c5, 0	@ flush instruction cache
+	dsb
+	isb
+
+	mov	sp, r5
+	msr	cpsr_cxsf, r6
+	msr	spsr_cxsf, r7
+
+	mcr	p15, 0, lr, c1, c0, 2	@ cpacr (loaded before VFP)
+
+	ldr	r9, [r8, #CTX_DIAGNOSTIC]
+	mcr	p15, 0, r9, c15, c0, 1	@ diag
+
+	/* finally, restore the stack and return */
+	ldmfd	sp!, {r3-r12, lr}
+	msr	cpsr_fsxc, r3		@ restore original processor mode
+        isb
+	mov	pc, lr
+ENDPROC(__cortex_a9_restore)
+
+	.align L1_CACHE_SHIFT
+ENTRY(__shut_off_mmu)
+	mrc	p15, 0, r3, c1, c0, 0
+	movw	r2, #(1<<12) | (1<<11) | (1<<2) | (1<<0)
+	bic	r3, r3, r2
+	dsb
+	mcr	p15, 0, r3, c1, c0, 0
+	isb
+	bx	r9
+ENDPROC(__shut_off_mmu)
+
+/*
+ *	__invalidate_l1
+ *
+ *	  Invalidates the L1 data cache (no clean) during initial boot of
+ *	  a secondary processor
+ *
+ *	  Corrupted registers: r0-r6
+ */
+__invalidate_l1:
+	mov	r0, #0
+	mcr	p15, 2, r0, c0, c0, 0
+	mrc	p15, 1, r0, c0, c0, 0
+
+	movw	r1, #0x7fff
+	and	r2, r1, r0, lsr #13
+
+	movw	r1, #0x3ff
+
+	and	r3, r1, r0, lsr #3  @ NumWays - 1
+	add	r2, r2, #1	@ NumSets
+
+	and	r0, r0, #0x7
+	add	r0, r0, #4	@ SetShift
+
+	clz	r1, r3		@ WayShift
+	add	r4, r3, #1	@ NumWays
+1:	sub	r2, r2, #1	@ NumSets--
+	mov	r3, r4		@ Temp = NumWays
+2:	subs    r3, r3, #1	@ Temp--
+	mov	r5, r3, lsl r1
+	mov	r6, r2, lsl r0
+	orr	r5, r5, r6	@ Reg = (Temp<<WayShift)|(NumSets<<SetShift)
+	mcr	p15, 0, r5, c7, c6, 2
+	bgt	2b
+	cmp	r2, #0
+	bgt	1b
+	dsb
+	isb
+	bx	lr
+ENDPROC(__invalidate_l1)
+
+/*
+ *	__invalidate_cpu_state
+ *
+ *	 Invalidates volatile CPU state (SCU tags, caches, branch address
+ *	 arrays, exclusive monitor, etc.) so that they can be safely enabled
+ *	 instruction caching and branch predicition enabled as early as
+ *	 possible to improve performance
+ */
+ENTRY(__invalidate_cpu_state)
+	clrex
+	mov	r0, #0
+	mcr	p15, 0, r0, c1, c0, 1	@ disable SMP, prefetch, broadcast
+	isb
+	mcr	p15, 0, r0, c7, c5, 0	@ invalidate BTAC, i-cache
+	mcr	p15, 0, r0, c7, c5, 6	@ invalidate branch pred array
+	mcr	p15, 0, r0, c8, c7, 0	@ invalidate unified TLB
+	dsb
+	isb
+
+	cpu_id	r0
+	cmp	r0, #0
+	mov32	r1, (TEGRA_ARM_PERIF_BASE + 0xC)
+	movne	r0, r0, lsl #2
+	movne	r2, #0xf
+	movne	r2, r2, lsl r0
+	strne	r2, [r1]		@ invalidate SCU tags for CPU
+
+	dsb
+	mov	r0, #0x1800
+	mcr	p15, 0, r0, c1, c0, 0	@ enable branch prediction, i-cache
+	isb
+	b	__invalidate_l1		@ invalidate data cache
+ENDPROC(__invalidate_cpu_state)
+
+/*
+ *	__return_to_virtual(unsigned long pgdir, void (*ctx_restore)(void))
+ *
+ *	  Restores a CPU to the world of virtual addressing, using the
+ *	  specified page tables (which must ensure that a VA=PA mapping
+ *	  exists for the __enable_mmu function), and then jumps to
+ *	  ctx_restore to restore CPU context and return control to the OS
+ */
+	.align L1_CACHE_SHIFT
+ENTRY(__return_to_virtual)
+	orr	r8, r0, #TTB_FLAGS
+	mov	lr, r1			@ "return" to ctx_restore
+	mov	r3, #0
+	mcr	p15, 0, r3, c2, c0, 2	@ TTB control register
+
+	mcr	p15, 0, r8, c2, c0, 1	@ load TTBR1
+
+	mov	r0, #0x1f
+	mcr	p15, 0, r0, c3, c0, 0	@ domain access register
+
+	mov32	r0, 0xff0a89a8
+	mov32	r1, 0x40e044e0
+	mcr	p15, 0, r0, c10, c2, 0	@ PRRR
+	mcr	p15, 0, r1, c10, c2, 1	@ NMRR
+	mrc	p15, 0, r0, c1, c0, 0
+	mov32	r1, 0x0120c302
+	bic	r0, r0, r1
+	mov32	r1, 0x10c03c7d
+	orr	r0, r0, r1
+
+#ifdef CONFIG_ALIGNMENT_TRAP
+	orr	r0, r0, #0x2
+#else
+	bic	r0, r0, #0x2
+#endif
+	mov	r1, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \
+		      domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \
+		      domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \
+		      domain_val(DOMAIN_IO, DOMAIN_CLIENT))
+	mcr	p15, 0, r1, c3, c0, 0	@ domain access register
+	mcr	p15, 0, r8, c2, c0, 0	@ TTBR0
+	b	__turn_mmu_on_again
+	andeq	r0, r0, r0
+	andeq	r0, r0, r0
+	andeq	r0, r0, r0
+	andeq	r0, r0, r0
+ENDPROC(__return_to_virtual)
+
+/*
+ *	__turn_mmu_on_again
+ *
+ *	  does exactly what it advertises: turns the MMU on, again
+ *	  jumps to the *virtual* address lr after the MMU is enabled.
+ */
+	.align	L1_CACHE_SHIFT
+__turn_mmu_on_again:
+	mov	r0, r0
+	mcr	p15, 0, r0, c1, c0, 0
+	mrc	p15, 0, r3, c0, c0, 0
+	mov	r3, r3
+	mov	r3, lr
+	bx	lr
+ENDPROC(__turn_mmu_on_again)
diff --git a/arch/arm/mach-tegra/headsmp-t2.S b/arch/arm/mach-tegra/headsmp-t2.S
new file mode 100644
index 0000000..da96ec7
--- /dev/null
+++ b/arch/arm/mach-tegra/headsmp-t2.S
@@ -0,0 +1,216 @@
+/*
+ * arch/arm/mach-tegra/headsmp.S
+ *
+ * SMP initialization routines for Tegra SoCs
+ *
+ * Copyright (c) 2009-2010, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/linkage.h>
+#include <linux/init.h>
+
+#include <asm/assembler.h>
+#include <asm/domain.h>
+#include <asm/ptrace.h>
+#include <asm/cache.h>
+
+#include <mach/iomap.h>
+#include <mach/io.h>
+
+#include "power-macros.S"
+
+#define TTB_FLAGS 0x6A	@ IRGN_WBWA, OC_RGN_WBWA, S, NOS
+
+#define PMC_DPD_SAMPLE	0x20
+#define PMC_DPD_ENABLE	0x24
+#define PMC_SCRATCH1	0x54
+#define PMC_SCRATCH39	0x138
+#define RST_DEVICES_U	0xc
+
+#define CLK_RESET_PLLX_BASE     0xe0
+#define CLK_RESET_PLLX_MISC     0xe4
+#define CLK_RESET_PLLP_BASE     0xa0
+#define CLK_RESET_PLLP_OUTA     0xa4
+#define CLK_RESET_PLLP_OUTB     0xa8
+#define CLK_RESET_PLLP_MISC     0xac
+
+/*        .section ".cpuinit.text", "ax"*/
+
+.macro	poke_ev, val, tmp
+	mov32	\tmp, (TEGRA_EXCEPTION_VECTORS_BASE + 0x100)
+	str	\val, [\tmp]
+.endm
+
+#ifdef CONFIG_SMP
+/*
+ *	tegra_secondary_startup
+ *
+ *	 Initial secondary processor boot vector; jumps to kernel's
+ *	 secondary_startup routine
+ */
+ENTRY(tegra_secondary_startup)
+	msr	cpsr_fsxc, #0xd3
+	bl	__invalidate_cpu_state
+	cpu_id	r0
+	enable_coresite r1
+	poke_ev	r0, r1
+	b	secondary_startup
+ENDPROC(tegra_secondary_startup)
+#endif
+
+/*
+ *	__restart_plls
+ *
+ *	  Loads the saved PLLX and PLLP parameters into the PLLs, to
+ *	  allow them to stabilize while the rest of the CPU state is restored.
+ *	  Should be called after the MMU is enabled. Jumps directly
+ *	  to __cortex_a9_restore
+ */
+	.align L1_CACHE_SHIFT
+__restart_plls:
+	mov32	r0, tegra_sctx
+	mov32	r3, (TEGRA_CLK_RESET_BASE-IO_PPSB_PHYS+IO_PPSB_VIRT)
+	mov32	r4, (TEGRA_TMRUS_BASE-IO_PPSB_PHYS+IO_PPSB_VIRT)
+
+	ldr	r1, [r0, #0x0]	@ pllx_misc
+	ldr	r2, [r0, #0x4]	@ pllx_base
+	str	r1, [r3, #CLK_RESET_PLLX_MISC]
+	str	r2, [r3, #CLK_RESET_PLLX_BASE]
+
+	ldr	r1, [r0, #0x8]	@ pllp_misc
+	ldr	r2, [r0, #0xc]	@ pllp_base
+	str	r1, [r3, #CLK_RESET_PLLP_MISC]
+	str	r2, [r3, #CLK_RESET_PLLP_BASE]
+
+	ldr	r1, [r0, #0x10]	@ pllp_outa
+	ldr	r2, [r0, #0x14]	@ pllp_outb
+	str	r1, [r3, #CLK_RESET_PLLP_OUTA]
+	str	r2, [r3, #CLK_RESET_PLLP_OUTB]
+
+	/* record the time that PLLX and PLLP will be stable */
+	ldr	r1, [r4]
+	add	r1, r1, #300
+	str	r1, [r0, #0x18]	@ pll_timeout
+	b	__cortex_a9_restore
+ENDPROC(__restart_pllx)
+/*
+ *	__enable_coresite_access
+ *
+ *	  Takes the coresite debug interface out of reset, enables
+ *	  access to all CPUs. Called with MMU disabled.
+ */
+	.align L1_CACHE_SHIFT
+__enable_coresite_access:
+	mov32	r0, (TEGRA_CLK_RESET_BASE + RST_DEVICES_U)
+	mov32	r2, (TEGRA_TMRUS_BASE)
+
+	/* assert reset for 2usec */
+	ldr	r1, [r0]
+	orr	r1, #(1<<9)
+	str	r1, [r0]
+	wait_for_us r3, r2, r4
+	add	r3, r3, #2
+	bic	r1, r1, #(1<<9)
+	wait_until r3, r2, r4
+	str	r1, [r0]
+	enable_coresite r3
+	bx	lr
+ENDPROC(__enable_coresite_access)
+/*
+ *	tegra_lp2_startup
+ *
+ *	  Secondary CPU boot vector when restarting the master CPU following
+ *	  an LP2 idle transition. Re-enable coresight access, re-enable
+ *	  MMU, re-start PLLX, restore processor context.
+ */
+	.align L1_CACHE_SHIFT
+ENTRY(tegra_lp2_startup)
+	setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9
+
+	mov32	r0, TEGRA_TMRUS_BASE
+	ldr	r1, [r0]
+	mov32	r0, TEGRA_PMC_BASE
+	str	r1, [r0, #PMC_SCRATCH39]	@ save off exact lp2 exit time
+	mov	r1, #0
+	str	r1, [r0, #PMC_DPD_SAMPLE]
+	str	r1, [r0, #PMC_DPD_ENABLE]
+
+	bl	__invalidate_cpu_state
+	bl	__enable_coresite_access
+
+	mrc	p15, 0, r0, c1, c0, 1
+	orr	r0, r0, #(1 << 6) | (1 << 0)	@ re-enable coherency
+	mcr	p15, 0, r0, c1, c0, 1
+
+	/* enable SCU */
+	mov32	r0, TEGRA_ARM_PERIF_BASE
+	ldr	r1, [r0]
+	orr	r1, r1, #1
+	str	r1, [r0]
+
+	adr	r4, __tegra_lp2_data
+	ldmia	r4, {r5, r7, r12}
+	mov	r1, r12			@ ctx_restore = __cortex_a9_restore
+	sub	r4, r4, r5
+	ldr	r0, [r7, r4]		@ pgdir = tegra_pgd_phys
+	b	__return_to_virtual
+ENDPROC(tegra_lp2_startup)
+	.type	__tegra_lp2_data, %object
+__tegra_lp2_data:
+	.long	.
+	.long	tegra_pgd_phys
+	.long	__restart_plls
+	.size	__tegra_lp2_data, . - __tegra_lp2_data
+
+#ifdef CONFIG_HOTPLUG_CPU
+/*
+ *	tegra_hotplug_startup
+ *
+ *	  Secondary CPU boot vector when restarting a CPU following a
+ *	  hot-unplug. Uses the page table created by smp_prepare_cpus and
+ *	  stored in tegra_pgd_phys as the safe page table for
+ *	  __return_to_virtual, and jumps directly to __cortex_a9_restore.
+ */
+	.align L1_CACHE_SHIFT
+ENTRY(tegra_hotplug_startup)
+	setmode	PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9
+	bl	__invalidate_cpu_state
+	enable_coresite r1
+
+	/* most of the below is a retread of what happens in __v7_setup and
+	 * secondary_startup, to get the MMU re-enabled and to branch
+	 * to secondary_kernel_startup */
+	mrc	p15, 0, r0, c1, c0, 1
+	orr	r0, r0, #(1 << 6) | (1 << 0)	@ re-enable coherency
+	mcr	p15, 0, r0, c1, c0, 1
+
+	adr	r4, __tegra_hotplug_data
+	ldmia	r4, {r5, r7, r12}
+	mov	r1, r12			@ ctx_restore = __cortex_a9_restore
+	sub	r4, r4, r5
+	ldr	r0, [r7, r4]		@ pgdir = secondary_data.pgdir
+	b	__return_to_virtual
+ENDPROC(tegra_hotplug_startup)
+
+
+	.type	__tegra_hotplug_data, %object
+__tegra_hotplug_data:
+	.long	.
+	.long	tegra_pgd_phys
+	.long	__cortex_a9_restore
+	.size	__tegra_hotplug_data, . - __tegra_hotplug_data
+#endif
diff --git a/arch/arm/mach-tegra/headsmp.S b/arch/arm/mach-tegra/headsmp.S
deleted file mode 100644
index b5349b2..0000000
--- a/arch/arm/mach-tegra/headsmp.S
+++ /dev/null
@@ -1,61 +0,0 @@
-#include <linux/linkage.h>
-#include <linux/init.h>
-
-        .section ".text.head", "ax"
-	__CPUINIT
-
-/*
- * Tegra specific entry point for secondary CPUs.
- *   The secondary kernel init calls v7_flush_dcache_all before it enables
- *   the L1; however, the L1 comes out of reset in an undefined state, so
- *   the clean + invalidate performed by v7_flush_dcache_all causes a bunch
- *   of cache lines with uninitialized data and uninitialized tags to get
- *   written out to memory, which does really unpleasant things to the main
- *   processor.  We fix this by performing an invalidate, rather than a
- *   clean + invalidate, before jumping into the kernel.
- */
-ENTRY(v7_invalidate_l1)
-        mov     r0, #0
-        mcr     p15, 2, r0, c0, c0, 0
-        mrc     p15, 1, r0, c0, c0, 0
-
-        ldr     r1, =0x7fff
-        and     r2, r1, r0, lsr #13
-
-        ldr     r1, =0x3ff
-
-        and     r3, r1, r0, lsr #3  @ NumWays - 1
-        add     r2, r2, #1          @ NumSets
-
-        and     r0, r0, #0x7
-        add     r0, r0, #4          @ SetShift
-
-        clz     r1, r3              @ WayShift
-        add     r4, r3, #1          @ NumWays
-1:      sub     r2, r2, #1          @ NumSets--
-        mov     r3, r4              @ Temp = NumWays
-2:      subs    r3, r3, #1          @ Temp--
-        mov     r5, r3, lsl r1
-        mov     r6, r2, lsl r0
-        orr     r5, r5, r6          @ Reg = (Temp<<WayShift)|(NumSets<<SetShift)
-        mcr     p15, 0, r5, c7, c6, 2
-        bgt     2b
-        cmp     r2, #0
-        bgt     1b
-        dsb
-        isb
-        mov     pc, lr
-ENDPROC(v7_invalidate_l1)
-
-ENTRY(tegra_secondary_startup)
-	msr	cpsr_fsxc, #0xd3
-        bl      v7_invalidate_l1
-	mrc	p15, 0, r0, c0, c0, 5
-        and	r0, r0, #15
-        ldr     r1, =0x6000f100
-        str     r0, [r1]
-1:      ldr     r2, [r1]
-        cmp     r0, r2
-        beq     1b
-        b       secondary_startup
-ENDPROC(tegra_secondary_startup)
diff --git a/arch/arm/mach-tegra/hotplug.c b/arch/arm/mach-tegra/hotplug.c
deleted file mode 100644
index f329404..0000000
--- a/arch/arm/mach-tegra/hotplug.c
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- *  linux/arch/arm/mach-realview/hotplug.c
- *
- *  Copyright (C) 2002 ARM Ltd.
- *  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 as
- * published by the Free Software Foundation.
- */
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/smp.h>
-
-#include <asm/cacheflush.h>
-
-static inline void cpu_enter_lowpower(void)
-{
-	unsigned int v;
-
-	flush_cache_all();
-	asm volatile(
-	"	mcr	p15, 0, %1, c7, c5, 0\n"
-	"	mcr	p15, 0, %1, c7, c10, 4\n"
-	/*
-	 * Turn off coherency
-	 */
-	"	mrc	p15, 0, %0, c1, c0, 1\n"
-	"	bic	%0, %0, #0x20\n"
-	"	mcr	p15, 0, %0, c1, c0, 1\n"
-	"	mrc	p15, 0, %0, c1, c0, 0\n"
-	"	bic	%0, %0, %2\n"
-	"	mcr	p15, 0, %0, c1, c0, 0\n"
-	  : "=&r" (v)
-	  : "r" (0), "Ir" (CR_C)
-	  : "cc");
-}
-
-static inline void cpu_leave_lowpower(void)
-{
-	unsigned int v;
-
-	asm volatile(
-	"mrc	p15, 0, %0, c1, c0, 0\n"
-	"	orr	%0, %0, %1\n"
-	"	mcr	p15, 0, %0, c1, c0, 0\n"
-	"	mrc	p15, 0, %0, c1, c0, 1\n"
-	"	orr	%0, %0, #0x20\n"
-	"	mcr	p15, 0, %0, c1, c0, 1\n"
-	  : "=&r" (v)
-	  : "Ir" (CR_C)
-	  : "cc");
-}
-
-static inline void platform_do_lowpower(unsigned int cpu, int *spurious)
-{
-	/*
-	 * there is no power-control hardware on this platform, so all
-	 * we can do is put the core into WFI; this is safe as the calling
-	 * code will have already disabled interrupts
-	 */
-	for (;;) {
-		/*
-		 * here's the WFI
-		 */
-		asm(".word	0xe320f003\n"
-		    :
-		    :
-		    : "memory", "cc");
-
-		/*if (pen_release == cpu) {*/
-			/*
-			 * OK, proper wakeup, we're done
-			 */
-			break;
-		/*}*/
-
-		/*
-		 * Getting here, means that we have come out of WFI without
-		 * having been woken up - this shouldn't happen
-		 *
-		 * Just note it happening - when we're woken, we can report
-		 * its occurrence.
-		 */
-		(*spurious)++;
-	}
-}
-
-int platform_cpu_kill(unsigned int cpu)
-{
-	return 1;
-}
-
-/*
- * platform-specific code to shutdown a CPU
- *
- * Called with IRQs disabled
- */
-void platform_cpu_die(unsigned int cpu)
-{
-	int spurious = 0;
-
-	/*
-	 * we're ready for shutdown now, so do it
-	 */
-	cpu_enter_lowpower();
-	platform_do_lowpower(cpu, &spurious);
-
-	/*
-	 * bring this CPU back into the world of cache
-	 * coherency, and then restore interrupts
-	 */
-	cpu_leave_lowpower();
-
-	if (spurious)
-		pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious);
-}
-
-int platform_cpu_disable(unsigned int cpu)
-{
-	/*
-	 * we don't allow CPU 0 to be shutdown (it is still too special
-	 * e.g. clock tick interrupts)
-	 */
-	return cpu == 0 ? -EPERM : 0;
-}
diff --git a/arch/arm/mach-tegra/include/mach/suspend.h b/arch/arm/mach-tegra/include/mach/suspend.h
index 5af8715..e6043ae 100644
--- a/arch/arm/mach-tegra/include/mach/suspend.h
+++ b/arch/arm/mach-tegra/include/mach/suspend.h
@@ -21,6 +21,38 @@
 #ifndef _MACH_TEGRA_SUSPEND_H_
 #define _MACH_TEGRA_SUSPEND_H_
 
+enum tegra_suspend_mode {
+	TEGRA_SUSPEND_NONE = 0,
+	TEGRA_SUSPEND_LP2,	/* CPU voltage off */
+	TEGRA_SUSPEND_LP1,	/* CPU voltage off, DRAM self-refresh */
+	TEGRA_SUSPEND_LP0,	/* CPU + core voltage off, DRAM self-refresh */
+	TEGRA_MAX_SUSPEND_MODE,
+};
+
+struct tegra_suspend_platform_data {
+	unsigned long cpu_timer;   /* CPU power good time in us,  LP2/LP1 */
+	unsigned long cpu_off_timer;	/* CPU power off time us, LP2/LP1 */
+	unsigned long core_timer;  /* core power good time in ticks,  LP0 */
+	unsigned long core_off_timer;	/* core power off time ticks, LP0 */
+	unsigned long wake_enb;    /* mask of enabled wake pads */
+	unsigned long wake_high;   /* high-level-triggered wake pads */
+	unsigned long wake_low;    /* low-level-triggered wake pads */
+	unsigned long wake_any;    /* any-edge-triggered wake pads */
+	bool corereq_high;         /* Core power request active-high */
+	bool sysclkreq_high;       /* System clock request is active-high */
+	bool separate_req;         /* Core & CPU power request are separate */
+	enum tegra_suspend_mode suspend_mode;
+};
+
+unsigned long tegra_cpu_power_good_time(void);
+unsigned long tegra_cpu_power_off_time(void);
+enum tegra_suspend_mode tegra_get_suspend_mode(void);
+
+void __tegra_lp1_reset(void);
+void __tegra_iram_end(void);
+
+void lp0_suspend_init(void);
+
 void tegra_pinmux_suspend(void);
 void tegra_irq_suspend(void);
 void tegra_gpio_suspend(void);
@@ -35,4 +67,14 @@ void tegra_clk_resume(void);
 void tegra_dma_resume(void);
 void tegra_timer_resume(void);
 
+int tegra_irq_to_wake(int irq);
+int tegra_wake_to_irq(int wake);
+
+int tegra_set_lp0_wake(int irq, int enable);
+int tegra_set_lp0_wake_type(int irq, int flow_type);
+int tegra_set_lp1_wake(int irq, int enable);
+void tegra_set_lp0_wake_pads(u32 wake_enb, u32 wake_level, u32 wake_any);
+
+void __init tegra_init_suspend(struct tegra_suspend_platform_data *plat);
+
 #endif /* _MACH_TEGRA_SUSPEND_H_ */
diff --git a/arch/arm/mach-tegra/platsmp.c b/arch/arm/mach-tegra/platsmp.c
index ec1f689..d4ac18c 100644
--- a/arch/arm/mach-tegra/platsmp.c
+++ b/arch/arm/mach-tegra/platsmp.c
@@ -7,6 +7,8 @@
  *  Copyright (C) 2009 Palm
  *  All Rights Reserved
  *
+ *  Copyright (C) 2010 NVIDIA Corporation
+ *
  * 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.
@@ -18,14 +20,24 @@
 #include <linux/jiffies.h>
 #include <linux/smp.h>
 #include <linux/io.h>
+#include <linux/completion.h>
+#include <linux/sched.h>
+#include <linux/cpu.h>
+#include <linux/slab.h>
 
 #include <asm/cacheflush.h>
 #include <mach/hardware.h>
 #include <asm/mach-types.h>
+#include <asm/tlbflush.h>
 #include <asm/smp_scu.h>
+#include <asm/cpu.h>
+#include <asm/mmu_context.h>
+#include <asm/hardware/gic.h>
 
 #include <mach/iomap.h>
 
+#include "power.h"
+
 extern void tegra_secondary_startup(void);
 
 static DEFINE_SPINLOCK(boot_lock);
@@ -35,6 +47,8 @@ static void __iomem *scu_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE);
 	(IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100)
 #define CLK_RST_CONTROLLER_CLK_CPU_CMPLX \
 	(IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x4c)
+#define CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET \
+	(IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x340)
 #define CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR \
 	(IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x344)
 
@@ -67,27 +81,25 @@ int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
 	 */
 	spin_lock(&boot_lock);
 
-
 	/* set the reset vector to point to the secondary_startup routine */
-
 	boot_vector = virt_to_phys(tegra_secondary_startup);
+
+	smp_wmb();
+
 	old_boot_vector = readl(EVP_CPU_RESET_VECTOR);
 	writel(boot_vector, EVP_CPU_RESET_VECTOR);
 
-	/* enable cpu clock on cpu1 */
+	/* enable cpu clock on cpu */
 	reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
-	writel(reg & ~(1<<9), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+	writel(reg & ~(1<<(8+cpu)), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
 
-	reg = (1<<13) | (1<<9) | (1<<5) | (1<<1);
+	reg = 0x1111<<cpu;
 	writel(reg, CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR);
 
-	smp_wmb();
-	flush_cache_all();
-
 	/* unhalt the cpu */
-	writel(0, IO_ADDRESS(TEGRA_FLOW_CTRL_BASE) + 0x14);
+	writel(0, IO_ADDRESS(TEGRA_FLOW_CTRL_BASE) + 0x14 + 0x8*(cpu-1));
 
-	timeout = jiffies + (1 * HZ);
+	timeout = jiffies + HZ;
 	while (time_before(jiffies, timeout)) {
 		if (readl(EVP_CPU_RESET_VECTOR) != boot_vector)
 			break;
@@ -137,3 +149,60 @@ void __init platform_smp_prepare_cpus(unsigned int max_cpus)
 
 	scu_enable(scu_base);
 }
+
+#ifdef CONFIG_HOTPLUG_CPU
+
+int platform_cpu_kill(unsigned int cpu)
+{
+	unsigned int reg;
+
+	do {
+		reg = readl(CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET);
+		cpu_relax();
+	} while (!(reg & (1<<cpu)));
+
+	spin_lock(&boot_lock);
+	reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+	writel(reg | (1<<(8+cpu)), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+	spin_unlock(&boot_lock);
+
+	return 1;
+}
+
+void platform_cpu_die(unsigned int cpu)
+{
+#ifdef DEBUG
+	unsigned int this_cpu = hard_smp_processor_id();
+
+	if (cpu != this_cpu) {
+		printk(KERN_CRIT "Eek! platform_cpu_die running on %u, should be %u\n",
+			   this_cpu, cpu);
+		BUG();
+	}
+#endif
+
+	gic_cpu_exit(0);
+	barrier();
+	flush_cache_all();
+	barrier();
+	__cortex_a9_save(0);
+
+	/*
+	 * __cortex_a9_save can return through __cortex_a9_restore, but that
+	 * should never happen for a hotplugged cpu
+	 */
+	BUG();
+}
+
+int platform_cpu_disable(unsigned int cpu)
+{
+	/*
+	 * we don't allow CPU 0 to be shutdown (it is still too special
+	 * e.g. clock tick interrupts)
+	 */
+	if (unlikely(!tegra_context_area))
+		return -ENXIO;
+
+	return cpu == 0 ? -EPERM : 0;
+}
+#endif
diff --git a/arch/arm/mach-tegra/power-macros.S b/arch/arm/mach-tegra/power-macros.S
new file mode 100644
index 0000000..2d18d02
--- /dev/null
+++ b/arch/arm/mach-tegra/power-macros.S
@@ -0,0 +1,57 @@
+/*
+ * arch/arm/mach-tegra/power-macros.S
+ *
+ * Assembly macros useful for power state save / restore routines
+ *
+ * Copyright (c) 2010, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+/* returns the ID of the current processor */
+.macro	cpu_id, rd
+	mrc	p15, 0, \rd, c0, c0, 5
+	and	\rd, \rd, #0xF
+.endm
+
+
+.macro mov32, reg, val
+	movw	\reg, #:lower16:\val
+	movt	\reg, #:upper16:\val
+.endm
+
+/* waits until the microsecond counter (base) ticks, for exact timing loops */
+.macro	wait_for_us, rd, base, tmp
+	ldr	\rd, [\base]
+1001:	ldr	\tmp, [\base]
+	cmp	\rd, \tmp
+	beq	1001b
+	mov	\tmp, \rd
+.endm
+
+/* waits until the microsecond counter (base) is >= rn */
+.macro	wait_until, rn, base, tmp
+1002:	ldr	\tmp, [\base]
+	sub	\tmp, \tmp, \rn
+	ands	\tmp, \tmp, #0x80000000
+	dmb
+	bne	1002b
+.endm
+
+/* Enable Coresight access on cpu */
+.macro	enable_coresite, tmp
+	mov32	\tmp, 0xC5ACCE55
+	mcr	p14, 0, \tmp, c7, c12, 6
+.endm
diff --git a/arch/arm/mach-tegra/power.h b/arch/arm/mach-tegra/power.h
new file mode 100644
index 0000000..ac53b2a
--- /dev/null
+++ b/arch/arm/mach-tegra/power.h
@@ -0,0 +1,64 @@
+/*
+ * arch/arm/mach-tegra/power.h
+ *
+ * Declarations for power state transition code
+ *
+ * Copyright (c) 2010, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __MACH_TEGRA_POWER_H
+#define __MACH_TEGRA_POWER_H
+
+#include <asm/page.h>
+
+#define TEGRA_POWER_SDRAM_SELFREFRESH	0x400 /* SDRAM is in self-refresh */
+
+#define TEGRA_POWER_PWRREQ_POLARITY	0x1   /* core power request polarity */
+#define TEGRA_POWER_PWRREQ_OE		0x2   /* core power request enable */
+#define TEGRA_POWER_SYSCLK_POLARITY	0x4   /* sys clk polarity */
+#define TEGRA_POWER_SYSCLK_OE		0x8   /* system clock enable */
+#define TEGRA_POWER_PWRGATE_DIS		0x10  /* power gate disabled */
+#define TEGRA_POWER_EFFECT_LP0		0x40  /* enter LP0 when CPU pwr gated */
+#define TEGRA_POWER_CPU_PWRREQ_POLARITY 0x80  /* CPU power request polarity */
+#define TEGRA_POWER_CPU_PWRREQ_OE	0x100 /* CPU power request enable */
+#define TEGRA_POWER_PMC_SHIFT		8
+#define TEGRA_POWER_PMC_MASK		0x1ff
+
+/* CPU Context area (1KB per CPU) */
+#define CONTEXT_SIZE_BYTES_SHIFT 10
+#define CONTEXT_SIZE_BYTES (1<<CONTEXT_SIZE_BYTES_SHIFT)
+
+/* layout of IRAM used for LP1 save & restore */
+#define TEGRA_IRAM_CODE_AREA		(TEGRA_IRAM_BASE + SZ_4K)
+#define TEGRA_IRAM_CODE_SIZE		SZ_4K
+
+#ifndef __ASSEMBLY__
+extern void *tegra_context_area;
+
+void tegra_lp2_set_trigger(unsigned long cycles);
+unsigned long tegra_lp2_timer_remain(void);
+u64 tegra_rtc_read_ms(void);
+void __cortex_a9_save(unsigned int mode);
+void __cortex_a9_restore(void);
+void __shut_off_mmu(void);
+void tegra_lp2_startup(void);
+unsigned int tegra_suspend_lp2(unsigned int us);
+void tegra_hotplug_startup(void);
+void tegra_init_cache(void);
+#endif
+
+#endif
diff --git a/arch/arm/mach-tegra/suspend-t2.c b/arch/arm/mach-tegra/suspend-t2.c
new file mode 100644
index 0000000..10079f4
--- /dev/null
+++ b/arch/arm/mach-tegra/suspend-t2.c
@@ -0,0 +1,414 @@
+/*
+ * arch/arm/mach-tegra/suspend-t2.c
+ *
+ * BootROM LP0 scratch register preservation for Tegra 2
+ *
+ * Copyright (c) 2009-2010, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/io.h>
+
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+#include <mach/suspend.h>
+
+#include "gpio-names.h"
+
+#define PMC_SCRATCH3	0x5c
+#define PMC_SCRATCH5	0x64
+#define PMC_SCRATCH6	0x68
+#define PMC_SCRATCH7	0x6c
+#define PMC_SCRATCH8	0x70
+#define PMC_SCRATCH9	0x74
+#define PMC_SCRATCH10	0x78
+#define PMC_SCRATCH11	0x7c
+#define PMC_SCRATCH12	0x80
+#define PMC_SCRATCH13	0x84
+#define PMC_SCRATCH14	0x88
+#define PMC_SCRATCH15	0x8c
+#define PMC_SCRATCH16	0x90
+#define PMC_SCRATCH17	0x94
+#define PMC_SCRATCH18	0x98
+#define PMC_SCRATCH19	0x9c
+#define PMC_SCRATCH20	0xa0
+#define PMC_SCRATCH21	0xa4
+#define PMC_SCRATCH22	0xa8
+#define PMC_SCRATCH23	0xac
+#define PMC_SCRATCH25	0x100
+#define PMC_SCRATCH35	0x128
+#define PMC_SCRATCH36	0x12c
+#define PMC_SCRATCH40	0x13c
+
+struct pmc_scratch_field {
+	unsigned long addr;
+	unsigned int mask;
+	int shift_src;
+	int shift_dst;
+};
+
+#define field(reg, start, end, dst)					\
+	{								\
+		.addr = (reg),						\
+		.mask = 0xfffffffful >> (31 - ((end) - (start))),	\
+		.shift_src = (start),					\
+		.shift_dst = (dst),					\
+	}
+
+static const struct pmc_scratch_field pllx[] __initdata = {
+	field(TEGRA_CLK_RESET_BASE + 0xe0, 20, 22, 15), /* PLLX_DIVP */
+	field(TEGRA_CLK_RESET_BASE + 0xe0, 8, 17, 5), /* PLLX_DIVN */
+	field(TEGRA_CLK_RESET_BASE + 0xe0, 0, 4, 0), /* PLLX_DIVM */
+	field(TEGRA_CLK_RESET_BASE + 0xe4, 8, 11, 22), /* PLLX_CPCON */
+	field(TEGRA_CLK_RESET_BASE + 0xe4, 4, 7, 18), /* PLLX_LFCON */
+	field(TEGRA_APB_MISC_BASE + 0x8e4, 24, 27, 27), /* XM2CFGC_VREF_DQ */
+	field(TEGRA_APB_MISC_BASE + 0x8c8, 3, 3, 26), /* XM2CFGC_SCHMT_EN */
+	field(TEGRA_APB_MISC_BASE + 0x8d0, 2, 2, 31), /* XM2CLKCFG_PREEMP_EN */
+};
+
+static const struct pmc_scratch_field emc_0[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x3c, 0, 4, 27), /* R2W */
+	field(TEGRA_EMC_BASE + 0x34, 0, 5, 15), /* RAS */
+	field(TEGRA_EMC_BASE + 0x2c, 0, 5, 0), /* RC */
+	field(TEGRA_EMC_BASE + 0x30, 0, 8, 6), /* RFC */
+	field(TEGRA_EMC_BASE + 0x38, 0, 5, 21), /* RP */
+};
+
+static const struct pmc_scratch_field emc_1[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x44, 0, 4, 5), /* R2P */
+	field(TEGRA_EMC_BASE + 0x4c, 0, 5, 15), /* RD_RCD */
+	field(TEGRA_EMC_BASE + 0x54, 0, 3, 27), /* RRD */
+	field(TEGRA_EMC_BASE + 0x48, 0, 4, 10), /* W2P */
+	field(TEGRA_EMC_BASE + 0x40, 0, 4, 0), /* W2R */
+	field(TEGRA_EMC_BASE + 0x50, 0, 5, 21), /* WR_RCD */
+};
+
+static const struct pmc_scratch_field emc_2[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x2b8, 2, 2, 31), /* CLKCHANGE_SR_ENABLE */
+	field(TEGRA_EMC_BASE + 0x2b8, 10, 10, 30), /* USE_ADDR_CLK */
+	field(TEGRA_EMC_BASE + 0x80, 0, 4, 25), /* PCHG2PDEN */
+	field(TEGRA_EMC_BASE + 0x64, 0, 3, 12), /* QRST */
+	field(TEGRA_EMC_BASE + 0x68, 0, 3, 16), /* QSAFE */
+	field(TEGRA_EMC_BASE + 0x60, 0, 3, 8), /* QUSE */
+	field(TEGRA_EMC_BASE + 0x6c, 0, 4, 20), /* RDV */
+	field(TEGRA_EMC_BASE + 0x58, 0, 3, 0), /* REXT */
+	field(TEGRA_EMC_BASE + 0x5c, 0, 3, 4), /* WDV */
+};
+
+static const struct pmc_scratch_field emc_3[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x74, 0, 3, 16), /* BURST_REFRESH_NUM */
+	field(TEGRA_EMC_BASE + 0x7c, 0, 3, 24), /* PDEX2RD */
+	field(TEGRA_EMC_BASE + 0x78, 0, 3, 20), /* PDEX2WR */
+	field(TEGRA_EMC_BASE + 0x70, 0, 4, 0), /* REFRESH_LO */
+	field(TEGRA_EMC_BASE + 0x70, 5, 15, 5), /* REFRESH */
+	field(TEGRA_EMC_BASE + 0xa0, 0, 3, 28), /* TCLKSTABLE */
+};
+
+static const struct pmc_scratch_field emc_4[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x84, 0, 4, 0), /* ACT2PDEN */
+	field(TEGRA_EMC_BASE + 0x88, 0, 4, 5), /* AR2PDEN */
+	field(TEGRA_EMC_BASE + 0x8c, 0, 5, 10), /* RW2PDEN */
+	field(TEGRA_EMC_BASE + 0x94, 0, 3, 28), /* TCKE */
+	field(TEGRA_EMC_BASE + 0x90, 0, 11, 16), /* TXSR */
+};
+
+static const struct pmc_scratch_field emc_5[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x8, 10, 10, 30), /* AP_REQ_BUSY_CTRL */
+	field(TEGRA_EMC_BASE + 0x8, 24, 24, 31), /* CFG_PRIORITY */
+	field(TEGRA_EMC_BASE + 0x8, 2, 2, 26), /* FORCE_UPDATE */
+	field(TEGRA_EMC_BASE + 0x8, 4, 4, 27), /* MRS_WAIT */
+	field(TEGRA_EMC_BASE + 0x8, 5, 5, 28), /* PERIODIC_QRST */
+	field(TEGRA_EMC_BASE + 0x8, 9, 9, 29), /* READ_DQM_CTRL */
+	field(TEGRA_EMC_BASE + 0x8, 0, 0, 24), /* READ_MUX */
+	field(TEGRA_EMC_BASE + 0x8, 1, 1, 25), /* WRITE_MUX */
+	field(TEGRA_EMC_BASE + 0xa4, 0, 3, 6), /* TCLKSTOP */
+	field(TEGRA_EMC_BASE + 0xa8, 0, 13, 10), /* TREFBW */
+	field(TEGRA_EMC_BASE + 0x9c, 0, 5, 0), /* TRPAB */
+};
+
+static const struct pmc_scratch_field emc_6[] __initdata = {
+	field(TEGRA_EMC_BASE + 0xfc, 0, 1, 0), /* DQSIB_DLY_MSB_BYTE_0 */
+	field(TEGRA_EMC_BASE + 0xfc, 8, 9, 2), /* DQSIB_DLY_MSB_BYTE_1 */
+	field(TEGRA_EMC_BASE + 0xfc, 16, 17, 4), /* DQSIB_DLY_MSB_BYTE_2 */
+	field(TEGRA_EMC_BASE + 0xfc, 24, 25, 6), /* DQSIB_DLY_MSB_BYTE_3 */
+	field(TEGRA_EMC_BASE + 0x110, 0, 1, 8), /* QUSE_DLY_MSB_BYTE_0 */
+	field(TEGRA_EMC_BASE + 0x110, 8, 9, 10), /* QUSE_DLY_MSB_BYTE_1 */
+	field(TEGRA_EMC_BASE + 0x110, 16, 17, 12), /* QUSE_DLY_MSB_BYTE_2 */
+	field(TEGRA_EMC_BASE + 0x110, 24, 25, 14), /* QUSE_DLY_MSB_BYTE_3 */
+	field(TEGRA_EMC_BASE + 0xac, 0, 3, 22), /* QUSE_EXTRA */
+	field(TEGRA_EMC_BASE + 0x98, 0, 5, 16), /* TFAW */
+	field(TEGRA_APB_MISC_BASE + 0x8e4, 5, 5, 30), /* XM2CFGC_VREF_DQ_EN */
+	field(TEGRA_APB_MISC_BASE + 0x8e4, 16, 19, 26), /* XM2CFGC_VREF_DQS */
+};
+
+static const struct pmc_scratch_field emc_dqsib_dly[] __initdata = {
+	field(TEGRA_EMC_BASE + 0xf8, 0, 31, 0), /* DQSIB_DLY_BYTE_0 - DQSIB_DLY_BYTE_3*/
+};
+
+static const struct pmc_scratch_field emc_quse_dly[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x10c, 0, 31, 0), /* QUSE_DLY_BYTE_0 - QUSE_DLY_BYTE_3*/
+};
+
+static const struct pmc_scratch_field emc_clktrim[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x2d0, 0, 29, 0), /* DATA0_CLKTRIM - DATA3_CLKTRIM +
+					* MCLK_ADDR_CLKTRIM */
+};
+
+static const struct pmc_scratch_field emc_autocal_fbio[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x2a4, 29, 29, 29), /* AUTO_CAL_ENABLE */
+	field(TEGRA_EMC_BASE + 0x2a4, 30, 30, 30), /* AUTO_CAL_OVERRIDE */
+	field(TEGRA_EMC_BASE + 0x2a4, 8, 12, 14), /* AUTO_CAL_PD_OFFSET */
+	field(TEGRA_EMC_BASE + 0x2a4, 0, 4, 9), /* AUTO_CAL_PU_OFFSET */
+	field(TEGRA_EMC_BASE + 0x2a4, 16, 25, 19), /* AUTO_CAL_STEP */
+	field(TEGRA_EMC_BASE + 0xf4, 16, 16, 0), /* CFG_DEN_EARLY */
+	field(TEGRA_EMC_BASE + 0x104, 8, 8, 8), /* CTT_TERMINATION */
+	field(TEGRA_EMC_BASE + 0x104, 7, 7, 7), /* DIFFERENTIAL_DQS */
+	field(TEGRA_EMC_BASE + 0x104, 9, 9, 31), /* DQS_PULLD */
+	field(TEGRA_EMC_BASE + 0x104, 0, 1, 4), /* DRAM_TYPE */
+	field(TEGRA_EMC_BASE + 0x104, 4, 4, 6), /* DRAM_WIDTH */
+	field(TEGRA_EMC_BASE + 0x114, 0, 2, 1), /* CFG_QUSE_LATE */
+};
+
+static const struct pmc_scratch_field emc_autocal_interval[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x2a8, 0, 27, 0), /* AUTOCAL_INTERVAL */
+	field(TEGRA_EMC_BASE + 0x2b8, 1, 1, 29), /* CLKCHANGE_PD_ENABLE */
+	field(TEGRA_EMC_BASE + 0x2b8, 0, 0, 28), /* CLKCHANGE_REQ_ENABLE */
+	field(TEGRA_EMC_BASE + 0x2b8, 8, 9, 30), /* PIN_CONFIG */
+};
+
+static const struct pmc_scratch_field emc_cfgs[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x10, 8, 9, 3), /* EMEM_BANKWIDTH */
+	field(TEGRA_EMC_BASE + 0x10, 0, 2, 0), /* EMEM_COLWIDTH */
+	field(TEGRA_EMC_BASE + 0x10, 16, 19, 5), /* EMEM_DEVSIZE */
+	field(TEGRA_EMC_BASE + 0x10, 24, 25, 9), /* EMEM_NUMDEV */
+	field(TEGRA_EMC_BASE + 0xc, 24, 24, 21), /* AUTO_PRE_RD */
+	field(TEGRA_EMC_BASE + 0xc, 25, 25, 22), /* AUTO_PRE_WR */
+	field(TEGRA_EMC_BASE + 0xc, 16, 16, 20), /* CLEAR_AP_PREV_SPREQ */
+	field(TEGRA_EMC_BASE + 0xc, 29, 29, 23), /* DRAM_ACPD */
+	field(TEGRA_EMC_BASE + 0xc, 30, 30, 24), /* DRAM_CLKSTOP_PDSR_ONLY */
+	field(TEGRA_EMC_BASE + 0xc, 31, 31, 25), /* DRAM_CLKSTOP */
+	field(TEGRA_EMC_BASE + 0xc, 8, 15, 12), /* PRE_IDLE_CYCLES */
+	field(TEGRA_EMC_BASE + 0xc, 0, 0, 11), /* PRE_IDLE_EN */
+	field(TEGRA_EMC_BASE + 0x2bc, 28, 29, 28), /* CFG_DLL_LOCK_LIMIT */
+	field(TEGRA_EMC_BASE + 0x2bc, 6, 7, 30), /* CFG_DLL_MODE */
+	field(TEGRA_MC_BASE + 0x10c, 0, 0, 26), /* LL_CTRL */
+	field(TEGRA_MC_BASE + 0x10c, 1, 1, 27), /* LL_SEND_BOTH */
+};
+
+static const struct pmc_scratch_field emc_adr_cfg1[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x14, 8, 9, 8), /* EMEM1_BANKWIDTH */
+	field(TEGRA_EMC_BASE + 0x14, 0, 2, 5), /* EMEM1_COLWIDTH */
+	field(TEGRA_EMC_BASE + 0x14, 16, 19, 10), /* EMEM1_DEVSIZE */
+	field(TEGRA_EMC_BASE + 0x2dc, 24, 28, 0), /* TERM_DRVUP */
+	field(TEGRA_APB_MISC_BASE + 0x8d4, 0, 3, 14), /* XM2COMP_VREF_SEL */
+	field(TEGRA_APB_MISC_BASE + 0x8d8, 16, 18, 21), /* XM2VTTGEN_CAL_DRVDN */
+	field(TEGRA_APB_MISC_BASE + 0x8d8, 24, 26, 18), /* XM2VTTGEN_CAL_DRVUP */
+	field(TEGRA_APB_MISC_BASE + 0x8d8, 1, 1, 30), /* XM2VTTGEN_SHORT_PWRGND */
+	field(TEGRA_APB_MISC_BASE + 0x8d8, 0, 0, 31), /* XM2VTTGEN_SHORT */
+	field(TEGRA_APB_MISC_BASE + 0x8d8, 12, 14, 24), /* XM2VTTGEN_VAUXP_LEVEL */
+	field(TEGRA_APB_MISC_BASE + 0x8d8, 8, 10, 27), /* XM2VTTGEN_VCLAMP_LEVEL */
+};
+
+static const struct pmc_scratch_field emc_digital_dll[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x2bc, 1, 1, 23), /* DLI_TRIMMER_EN */
+	field(TEGRA_EMC_BASE + 0x2bc, 0, 0, 22), /* DLL_EN */
+	field(TEGRA_EMC_BASE + 0x2bc, 5, 5, 27), /* DLL_LOWSPEED */
+	field(TEGRA_EMC_BASE + 0x2bc, 2, 2, 24), /* DLL_OVERRIDE_EN */
+	field(TEGRA_EMC_BASE + 0x2bc, 8, 11, 28), /* DLL_UDSET */
+	field(TEGRA_EMC_BASE + 0x2bc, 4, 4, 26), /* PERBYTE_TRIMMER_OVERRIDE */
+	field(TEGRA_EMC_BASE + 0x2bc, 3, 3, 25), /* USE_SINGLE_DLL */
+	field(TEGRA_MC_BASE + 0xc, 0, 21, 0), /* EMEM_SIZE_KB */
+};
+
+static const struct pmc_scratch_field emc_dqs_clktrim[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x2d4, 0, 29, 0), /* DQS0_CLKTRIM - DQS3 + MCLK*/
+	field(TEGRA_APB_MISC_BASE + 0x8e4, 3, 3, 31), /* XM2CFGC_CTT_HIZ_EN */
+	field(TEGRA_APB_MISC_BASE + 0x8e4, 4, 4, 30), /* XM2CFGC_VREF_DQS_EN */
+};
+
+static const struct pmc_scratch_field emc_dq_clktrim[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x2d8, 0, 29, 0),
+	field(TEGRA_APB_MISC_BASE + 0x8e4, 2, 2, 30), /* XM2CFGC_PREEMP_EN */
+	field(TEGRA_APB_MISC_BASE + 0x8e4, 0, 0, 31), /* XM2CFGC_RX_FT_REC_EN */
+};
+
+static const struct pmc_scratch_field emc_dll_xform_dqs[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x2bc, 16, 25, 20), /* CFG_DLL_OVERRIDE_VAL */
+	field(TEGRA_EMC_BASE + 0x2c0, 0, 4, 0), /* DQS_MULT */
+	field(TEGRA_EMC_BASE + 0x2c0, 8, 22, 5), /* DQS_OFFS */
+	field(TEGRA_MC_BASE + 0x10c, 31, 31, 30), /* LL_DRAM_INTERLEAVE */
+};
+
+static const struct pmc_scratch_field emc_odt_rw[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x2c4, 0, 4, 0), /* QUSE_MULT */
+	field(TEGRA_EMC_BASE + 0x2c4, 8, 22, 5), /* QUSE_OFF */
+	field(TEGRA_EMC_BASE + 0xb4, 31, 31, 29), /* DISABLE_ODT_DURING_READ */
+	field(TEGRA_EMC_BASE + 0xb4, 30, 30, 28), /* B4_READ */
+	field(TEGRA_EMC_BASE + 0xb4, 0, 2, 25), /* RD_DELAY */
+	field(TEGRA_EMC_BASE + 0xb0, 31, 31, 24), /* ENABLE_ODT_DURING_WRITE */
+	field(TEGRA_EMC_BASE + 0xb0, 30, 30, 23), /* B4_WRITE */
+	field(TEGRA_EMC_BASE + 0xb0, 0, 2, 20), /* WR_DELAY */
+};
+
+static const struct pmc_scratch_field arbitration_xbar[] __initdata = {
+	field(TEGRA_AHB_GIZMO_BASE + 0xdc, 0, 31, 0),
+};
+
+static const struct pmc_scratch_field emc_zcal[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x2e0, 0, 23, 0), /* ZCAL_REF_INTERVAL */
+	field(TEGRA_EMC_BASE + 0x2e4, 0, 7, 24), /* ZCAL_WAIT_CNT */
+};
+
+static const struct pmc_scratch_field emc_ctt_term[] __initdata = {
+	field(TEGRA_EMC_BASE + 0x2dc, 15, 19, 26), /* TERM_DRVDN */
+	field(TEGRA_EMC_BASE + 0x2dc, 8, 12, 21), /* TERM_OFFSET */
+	field(TEGRA_EMC_BASE + 0x2dc, 31, 31, 31), /* TERM_OVERRIDE */
+	field(TEGRA_EMC_BASE + 0x2dc, 0, 2, 18), /* TERM_SLOPE */
+	field(TEGRA_EMC_BASE + 0x2e8, 16, 23, 8), /* ZQ_MRW_MA */
+	field(TEGRA_EMC_BASE + 0x2e8, 0, 7, 0), /* ZQ_MRW_OP */
+};
+
+static const struct pmc_scratch_field xm2_cfgd[] __initdata = {
+	field(TEGRA_APB_MISC_BASE + 0x8e8, 16, 18, 9), /* CFGD0_DLYIN_TRM */
+	field(TEGRA_APB_MISC_BASE + 0x8e8, 20, 22, 6), /* CFGD1_DLYIN_TRM */
+	field(TEGRA_APB_MISC_BASE + 0x8e8, 24, 26, 3), /* CFGD2_DLYIN_TRM */
+	field(TEGRA_APB_MISC_BASE + 0x8e8, 28, 30, 0), /* CFGD3_DLYIN_TRM */
+	field(TEGRA_APB_MISC_BASE + 0x8e8, 3, 3, 12), /* XM2CFGD_CTT_HIZ_EN */
+	field(TEGRA_APB_MISC_BASE + 0x8e8, 2, 2, 13), /* XM2CFGD_PREEMP_EN */
+	field(TEGRA_APB_MISC_BASE + 0x8e8, 0, 0, 14), /* CM2CFGD_RX_FT_REC_EN */
+};
+
+struct pmc_scratch_reg {
+	const struct pmc_scratch_field *fields;
+	void __iomem *scratch_addr;
+	int num_fields;
+};
+
+#define scratch(offs, field_list)					\
+	{								\
+		.scratch_addr = IO_ADDRESS(TEGRA_PMC_BASE) + offs,	\
+		.fields = field_list,					\
+		.num_fields = ARRAY_SIZE(field_list),			\
+	}
+
+static const struct pmc_scratch_reg scratch[] __initdata = {
+	scratch(PMC_SCRATCH3, pllx),
+	scratch(PMC_SCRATCH5, emc_0),
+	scratch(PMC_SCRATCH6, emc_1),
+	scratch(PMC_SCRATCH7, emc_2),
+	scratch(PMC_SCRATCH8, emc_3),
+	scratch(PMC_SCRATCH9, emc_4),
+	scratch(PMC_SCRATCH10, emc_5),
+	scratch(PMC_SCRATCH11, emc_6),
+	scratch(PMC_SCRATCH12, emc_dqsib_dly),
+	scratch(PMC_SCRATCH13, emc_quse_dly),
+	scratch(PMC_SCRATCH14, emc_clktrim),
+	scratch(PMC_SCRATCH15, emc_autocal_fbio),
+	scratch(PMC_SCRATCH16, emc_autocal_interval),
+	scratch(PMC_SCRATCH17, emc_cfgs),
+	scratch(PMC_SCRATCH18, emc_adr_cfg1),
+	scratch(PMC_SCRATCH19, emc_digital_dll),
+	scratch(PMC_SCRATCH20, emc_dqs_clktrim),
+	scratch(PMC_SCRATCH21, emc_dq_clktrim),
+	scratch(PMC_SCRATCH22, emc_dll_xform_dqs),
+	scratch(PMC_SCRATCH23, emc_odt_rw),
+	scratch(PMC_SCRATCH25, arbitration_xbar),
+	scratch(PMC_SCRATCH35, emc_zcal),
+	scratch(PMC_SCRATCH36, emc_ctt_term),
+	scratch(PMC_SCRATCH40, xm2_cfgd),
+};
+
+void __init lp0_suspend_init(void)
+{
+	int i;
+	int j;
+	unsigned int v;
+	unsigned int r;
+
+	for (i = 0; i < ARRAY_SIZE(scratch); i++) {
+		r = 0;
+
+		for (j = 0; j < scratch[i].num_fields; j++) {
+			v = readl(IO_ADDRESS(scratch[i].fields[j].addr));
+			v >>= scratch[i].fields[j].shift_src;
+			v &= scratch[i].fields[j].mask;
+			v <<= scratch[i].fields[j].shift_dst;
+			r |= v;
+		}
+
+		writel(r, scratch[i].scratch_addr);
+	}
+}
+
+#define NUM_WAKE_EVENTS 31
+
+static int tegra_wake_event_irq[NUM_WAKE_EVENTS] = {
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PO5),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PV3),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PL1),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PB6),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PN7),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PA0),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PU5),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PU6),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PC7),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PS2),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PAA1),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PW3),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PW2),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PY6),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PV6),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PJ7),
+	INT_RTC,
+	INT_KBC,
+	INT_EXTERNAL_PMU,
+	-EINVAL, /* TEGRA_USB1_VBUS, */
+	-EINVAL, /* TEGRA_USB3_VBUS, */
+	-EINVAL, /* TEGRA_USB1_ID, */
+	-EINVAL, /* TEGRA_USB3_ID, */
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PI5),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PV2),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PS4),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PS5),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PS0),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PQ6),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PQ7),
+	TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PN2),
+};
+
+int tegra_irq_to_wake(int irq)
+{
+	int i;
+	for (i = 0; i < NUM_WAKE_EVENTS; i++)
+		if (tegra_wake_event_irq[i] == irq)
+			return i;
+
+	return -EINVAL;
+}
+
+int tegra_wake_to_irq(int wake)
+{
+	if (wake < 0)
+		return -EINVAL;
+
+	if (wake >= NUM_WAKE_EVENTS)
+		return -EINVAL;
+
+	return tegra_wake_event_irq[wake];
+}
diff --git a/arch/arm/mach-tegra/suspend.c b/arch/arm/mach-tegra/suspend.c
new file mode 100644
index 0000000..ded2c36
--- /dev/null
+++ b/arch/arm/mach-tegra/suspend.c
@@ -0,0 +1,764 @@
+/*
+ * arch/arm/mach-tegra/suspend.c
+ *
+ * CPU complex suspend & resume functions for Tegra SoCs
+ *
+ * Copyright (c) 2009-2010, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/suspend.h>
+#include <linux/slab.h>
+#include <linux/serial_reg.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+
+#include <asm/cacheflush.h>
+#include <asm/hardware/cache-l2x0.h>
+#include <asm/hardware/gic.h>
+#include <asm/localtimer.h>
+#include <asm/pgalloc.h>
+#include <asm/tlbflush.h>
+
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+#include <mach/legacy_irq.h>
+#include <mach/suspend.h>
+
+#include "board.h"
+#include "power.h"
+
+struct suspend_context {
+	/*
+	 * The next 7 values are referenced by offset in __restart_plls
+	 * in headsmp-t2.S, and should not be moved
+	 */
+	u32 pllx_misc;
+	u32 pllx_base;
+	u32 pllp_misc;
+	u32 pllp_base;
+	u32 pllp_outa;
+	u32 pllp_outb;
+	u32 pll_timeout;
+
+	u32 cpu_burst;
+	u32 clk_csite_src;
+	u32 twd_ctrl;
+	u32 twd_load;
+	u32 cclk_divider;
+};
+
+struct suspend_context tegra_sctx;
+
+static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
+static void __iomem *clk_rst = IO_ADDRESS(TEGRA_CLK_RESET_BASE);
+static void __iomem *flow_ctrl = IO_ADDRESS(TEGRA_FLOW_CTRL_BASE);
+static void __iomem *evp_reset = IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100;
+static void __iomem *tmrus = IO_ADDRESS(TEGRA_TMRUS_BASE);
+
+#define PMC_CTRL		0x0
+#define PMC_CTRL_LATCH_WAKEUPS	(1 << 5)
+#define PMC_WAKE_MASK		0xc
+#define PMC_WAKE_LEVEL		0x10
+#define PMC_DPAD_ORIDE		0x1C
+#define PMC_WAKE_DELAY		0xe0
+#define PMC_DPD_SAMPLE		0x20
+
+#define PMC_WAKE_STATUS		0x14
+#define PMC_SW_WAKE_STATUS	0x18
+#define PMC_COREPWRGOOD_TIMER	0x3c
+#define PMC_SCRATCH0		0x50
+#define PMC_SCRATCH1		0x54
+#define PMC_CPUPWRGOOD_TIMER	0xc8
+#define PMC_CPUPWROFF_TIMER	0xcc
+#define PMC_COREPWROFF_TIMER	PMC_WAKE_DELAY
+#define PMC_SCRATCH38		0x134
+#define PMC_SCRATCH39		0x138
+#define PMC_SCRATCH41		0x140
+
+#define CLK_RESET_CCLK_BURST	0x20
+#define CLK_RESET_CCLK_DIVIDER  0x24
+#define CLK_RESET_PLLC_BASE	0x80
+#define CLK_RESET_PLLM_BASE	0x90
+#define CLK_RESET_PLLX_BASE	0xe0
+#define CLK_RESET_PLLX_MISC	0xe4
+#define CLK_RESET_PLLP_BASE	0xa0
+#define CLK_RESET_PLLP_OUTA	0xa4
+#define CLK_RESET_PLLP_OUTB	0xa8
+#define CLK_RESET_PLLP_MISC	0xac
+
+#define CLK_RESET_SOURCE_CSITE	0x1d4
+
+#define CLK_RESET_CCLK_BURST_POLICY_SHIFT 28
+#define CLK_RESET_CCLK_BURST_POLICY_PLLM   3
+#define CLK_RESET_CCLK_BURST_POLICY_PLLX   8
+
+#define FLOW_CTRL_CPU_CSR	0x8
+#define FLOW_CTRL_CPU1_CSR	0x18
+
+unsigned long tegra_pgd_phys;  /* pgd used by hotplug & LP2 bootup */
+static pgd_t *tegra_pgd;
+void *tegra_context_area;
+
+static int tegra_last_pclk;
+static struct clk *tegra_pclk;
+static const struct tegra_suspend_platform_data *pdata;
+static unsigned long wb0_restore;
+static enum tegra_suspend_mode current_suspend_mode;
+
+static unsigned int tegra_time_in_suspend[32];
+
+static inline unsigned int time_to_bin(unsigned int time)
+{
+	return fls(time);
+}
+
+unsigned long tegra_cpu_power_good_time(void)
+{
+	if (WARN_ON_ONCE(!pdata))
+		return 5000;
+
+	return pdata->cpu_timer;
+}
+
+unsigned long tegra_cpu_power_off_time(void)
+{
+	if (WARN_ON_ONCE(!pdata))
+		return 5000;
+
+	return pdata->cpu_off_timer;
+}
+
+enum tegra_suspend_mode tegra_get_suspend_mode(void)
+{
+	if (!pdata)
+		return TEGRA_SUSPEND_NONE;
+
+	return pdata->suspend_mode;
+}
+
+static void set_power_timers(unsigned long us_on, unsigned long us_off,
+			     long rate)
+{
+	unsigned long long ticks;
+	unsigned long long pclk;
+
+	if (WARN_ON_ONCE(rate <= 0))
+		pclk = 100000000;
+	else
+		pclk = rate;
+
+	if (rate != tegra_last_pclk) {
+		ticks = (us_on * pclk) + 999999ull;
+		do_div(ticks, 1000000);
+		writel((unsigned long)ticks, pmc + PMC_CPUPWRGOOD_TIMER);
+
+		ticks = (us_off * pclk) + 999999ull;
+		do_div(ticks, 1000000);
+		writel((unsigned long)ticks, pmc + PMC_CPUPWROFF_TIMER);
+		wmb();
+	}
+	tegra_last_pclk = pclk;
+}
+
+/*
+ * create_suspend_pgtable
+ *
+ * Creates a page table with identity mappings of physical memory and IRAM
+ * for use when the MMU is off, in addition to all the regular kernel mappings.
+ */
+static int create_suspend_pgtable(void)
+{
+	tegra_pgd = pgd_alloc(&init_mm);
+	if (!tegra_pgd)
+		return -ENOMEM;
+
+	identity_mapping_add(tegra_pgd, PHYS_OFFSET, IO_IRAM_PHYS);
+	identity_mapping_add(tegra_pgd, IO_IRAM_PHYS, IO_IRAM_PHYS + SECTION_SIZE);
+
+	tegra_pgd_phys = virt_to_phys(tegra_pgd);
+
+	return 0;
+}
+
+static noinline void restore_cpu_complex(void)
+{
+	unsigned int reg;
+
+	/* restore original burst policy setting; PLLX state restored
+	 * by CPU boot-up code - wait for PLL stabilization if PLLX
+	 * was enabled, or if explicitly requested by caller */
+
+	BUG_ON(readl(clk_rst + CLK_RESET_PLLX_BASE) != tegra_sctx.pllx_base);
+
+	if (tegra_sctx.pllx_base & (1<<30)) {
+		while (readl(tmrus)-tegra_sctx.pll_timeout >= 0x80000000UL)
+			cpu_relax();
+	}
+	writel(tegra_sctx.cclk_divider, clk_rst + CLK_RESET_CCLK_DIVIDER);
+	writel(tegra_sctx.cpu_burst, clk_rst + CLK_RESET_CCLK_BURST);
+	writel(tegra_sctx.clk_csite_src, clk_rst + CLK_RESET_SOURCE_CSITE);
+
+	/* do not power-gate the CPU when flow controlled */
+	reg = readl(flow_ctrl + FLOW_CTRL_CPU_CSR);
+	reg &= ~((1<<5) | (1<<4) | 1); /* clear WFE bitmask */
+	reg |= (1<<14); /* write-1-clear event flag */
+	writel(reg, flow_ctrl + FLOW_CTRL_CPU_CSR);
+	wmb();
+
+#ifdef CONFIG_HAVE_ARM_TWD
+	writel(tegra_sctx.twd_ctrl, twd_base + 0x8);
+	writel(tegra_sctx.twd_load, twd_base + 0);
+#endif
+
+	gic_dist_restore(0);
+}
+
+static noinline void suspend_cpu_complex(void)
+{
+	unsigned int reg;
+	int i;
+
+	/* switch coresite to clk_m, save off original source */
+	tegra_sctx.clk_csite_src = readl(clk_rst + CLK_RESET_SOURCE_CSITE);
+	writel(3<<30, clk_rst + CLK_RESET_SOURCE_CSITE);
+
+	tegra_sctx.cpu_burst = readl(clk_rst + CLK_RESET_CCLK_BURST);
+	tegra_sctx.pllx_base = readl(clk_rst + CLK_RESET_PLLX_BASE);
+	tegra_sctx.pllx_misc = readl(clk_rst + CLK_RESET_PLLX_MISC);
+	tegra_sctx.pllp_base = readl(clk_rst + CLK_RESET_PLLP_BASE);
+	tegra_sctx.pllp_outa = readl(clk_rst + CLK_RESET_PLLP_OUTA);
+	tegra_sctx.pllp_outb = readl(clk_rst + CLK_RESET_PLLP_OUTB);
+	tegra_sctx.pllp_misc = readl(clk_rst + CLK_RESET_PLLP_MISC);
+	tegra_sctx.cclk_divider = readl(clk_rst + CLK_RESET_CCLK_DIVIDER);
+
+#ifdef CONFIG_HAVE_ARM_TWD
+	tegra_sctx.twd_ctrl = readl(twd_base + 0x8);
+	tegra_sctx.twd_load = readl(twd_base + 0);
+#endif
+
+	reg = readl(flow_ctrl + FLOW_CTRL_CPU_CSR);
+	/* clear any pending events, set the WFE bitmap to specify just
+	 * CPU0, and clear any pending events for this CPU */
+	reg &= ~(1 << 5); /* clear CPU1 WFE */
+	reg |= (1 << 14) | (1 << 4) | 1; /* enable CPU0 WFE */
+	writel(reg, flow_ctrl + FLOW_CTRL_CPU_CSR);
+	wmb();
+
+	for (i = 1; i < num_present_cpus(); i++) {
+		unsigned int offs = FLOW_CTRL_CPU1_CSR + (i - 1) * 8;
+		reg = readl(flow_ctrl + offs);
+		writel(reg | (1 << 14), flow_ctrl + offs);
+		wmb();
+	}
+
+	gic_cpu_exit(0);
+	gic_dist_save(0);
+}
+
+unsigned int tegra_suspend_lp2(unsigned int us)
+{
+	unsigned int mode;
+	unsigned long orig, reg;
+	unsigned int remain;
+
+	reg = readl(pmc + PMC_CTRL);
+	mode = (reg >> TEGRA_POWER_PMC_SHIFT) & TEGRA_POWER_PMC_MASK;
+	mode |= TEGRA_POWER_CPU_PWRREQ_OE;
+	if (pdata->separate_req)
+		mode |= TEGRA_POWER_PWRREQ_OE;
+	else
+		mode &= ~TEGRA_POWER_PWRREQ_OE;
+	mode &= ~TEGRA_POWER_EFFECT_LP0;
+
+	orig = readl(evp_reset);
+	writel(virt_to_phys(tegra_lp2_startup), evp_reset);
+
+	set_power_timers(pdata->cpu_timer, pdata->cpu_off_timer,
+			 clk_get_rate(tegra_pclk));
+
+	if (us)
+		tegra_lp2_set_trigger(us);
+
+	suspend_cpu_complex();
+	flush_cache_all();
+	outer_flush_all();
+	outer_disable();
+	barrier();
+
+	__cortex_a9_save(mode);
+	/* return from __cortex_a9_restore */
+	barrier();
+	cpu_init();
+	restore_cpu_complex();
+	l2x0_enable();
+
+	remain = tegra_lp2_timer_remain();
+	if (us)
+		tegra_lp2_set_trigger(0);
+
+	writel(orig, evp_reset);
+
+	return remain;
+}
+
+#ifdef CONFIG_PM
+
+/* ensures that sufficient time is passed for a register write to
+ * serialize into the 32KHz domain */
+static void pmc_32kwritel(u32 val, unsigned long offs)
+{
+	writel(val, pmc + offs);
+	udelay(130);
+}
+
+static u8 *iram_save;
+static unsigned int iram_save_size;
+static void __iomem *iram_code = IO_ADDRESS(TEGRA_IRAM_CODE_AREA);
+
+static void tegra_suspend_dram(bool do_lp0)
+{
+	unsigned int mode = TEGRA_POWER_SDRAM_SELFREFRESH;
+	unsigned long orig, reg;
+
+	orig = readl(evp_reset);
+	/* copy the reset vector and SDRAM shutdown code into IRAM */
+	memcpy(iram_save, iram_code, iram_save_size);
+	memcpy(iram_code, (void *)__tegra_lp1_reset, iram_save_size);
+
+	set_power_timers(pdata->cpu_timer, pdata->cpu_off_timer, 32768);
+
+	reg = readl(pmc + PMC_CTRL);
+	mode |= ((reg >> TEGRA_POWER_PMC_SHIFT) & TEGRA_POWER_PMC_MASK);
+
+	if (!do_lp0) {
+		writel(TEGRA_IRAM_CODE_AREA, evp_reset);
+
+		mode |= TEGRA_POWER_CPU_PWRREQ_OE;
+		if (pdata->separate_req)
+			mode |= TEGRA_POWER_PWRREQ_OE;
+		else
+			mode &= ~TEGRA_POWER_PWRREQ_OE;
+		mode &= ~TEGRA_POWER_EFFECT_LP0;
+
+		tegra_legacy_irq_set_lp1_wake_mask();
+	} else {
+		u32 boot_flag = readl(pmc + PMC_SCRATCH0);
+		pmc_32kwritel(boot_flag | 1, PMC_SCRATCH0);
+		pmc_32kwritel(wb0_restore, PMC_SCRATCH1);
+		writel(0x0, pmc + PMC_SCRATCH39);
+		mode |= TEGRA_POWER_CPU_PWRREQ_OE;
+		mode |= TEGRA_POWER_PWRREQ_OE;
+		mode |= TEGRA_POWER_EFFECT_LP0;
+
+		/* for platforms where the core & CPU power requests are
+		 * combined as a single request to the PMU, transition to
+		 * LP0 state by temporarily enabling both requests
+		 */
+		if (!pdata->separate_req) {
+			reg |= ((mode & TEGRA_POWER_PMC_MASK) <<
+				TEGRA_POWER_PMC_SHIFT);
+			pmc_32kwritel(reg, PMC_CTRL);
+			mode &= ~TEGRA_POWER_CPU_PWRREQ_OE;
+		}
+
+		tegra_set_lp0_wake_pads(pdata->wake_enb, pdata->wake_high,
+			pdata->wake_any);
+	}
+
+	suspend_cpu_complex();
+	flush_cache_all();
+	outer_flush_all();
+	outer_disable();
+
+	__cortex_a9_save(mode);
+	barrier();
+	cpu_init();
+	restore_cpu_complex();
+
+	writel(orig, evp_reset);
+	tegra_init_cache();
+
+	if (!do_lp0) {
+		memcpy(iram_code, iram_save, iram_save_size);
+		tegra_legacy_irq_restore_mask();
+	} else {
+		/* for platforms where the core & CPU power requests are
+		 * combined as a single request to the PMU, transition out
+		 * of LP0 state by temporarily enabling both requests
+		 */
+		if (!pdata->separate_req) {
+			reg = readl(pmc + PMC_CTRL);
+			reg |= (TEGRA_POWER_CPU_PWRREQ_OE << TEGRA_POWER_PMC_SHIFT);
+			pmc_32kwritel(reg, PMC_CTRL);
+			reg &= ~(TEGRA_POWER_PWRREQ_OE << TEGRA_POWER_PMC_SHIFT);
+			writel(reg, pmc + PMC_CTRL);
+		}
+	}
+
+	wmb();
+}
+
+static int tegra_suspend_prepare_late(void)
+{
+	disable_irq(INT_SYS_STATS_MON);
+	return 0;
+}
+
+static void tegra_suspend_wake(void)
+{
+	enable_irq(INT_SYS_STATS_MON);
+}
+
+static u8 uart_state[5];
+
+static int tegra_debug_uart_suspend(void)
+{
+	void __iomem *uart;
+	u32 lcr;
+
+	if (TEGRA_DEBUG_UART_BASE == 0)
+		return 0;
+
+	uart = IO_ADDRESS(TEGRA_DEBUG_UART_BASE);
+
+	lcr = readb(uart + UART_LCR * 4);
+
+	uart_state[0] = lcr;
+	uart_state[1] = readb(uart + UART_MCR * 4);
+
+	/* DLAB = 0 */
+	writeb(lcr & ~UART_LCR_DLAB, uart + UART_LCR * 4);
+
+	uart_state[2] = readb(uart + UART_IER * 4);
+
+	/* DLAB = 1 */
+	writeb(lcr | UART_LCR_DLAB, uart + UART_LCR * 4);
+
+	uart_state[3] = readb(uart + UART_DLL * 4);
+	uart_state[4] = readb(uart + UART_DLM * 4);
+
+	writeb(lcr, uart + UART_LCR * 4);
+
+	return 0;
+}
+
+static void tegra_debug_uart_resume(void)
+{
+	void __iomem *uart;
+	u32 lcr;
+
+	if (TEGRA_DEBUG_UART_BASE == 0)
+		return;
+
+	uart = IO_ADDRESS(TEGRA_DEBUG_UART_BASE);
+
+	lcr = uart_state[0];
+
+	writeb(uart_state[1], uart + UART_MCR * 4);
+
+	/* DLAB = 0 */
+	writeb(lcr & ~UART_LCR_DLAB, uart + UART_LCR * 4);
+
+	writeb(uart_state[2], uart + UART_IER * 4);
+
+	/* DLAB = 1 */
+	writeb(lcr | UART_LCR_DLAB, uart + UART_LCR * 4);
+
+	writeb(uart_state[3], uart + UART_DLL * 4);
+	writeb(uart_state[4], uart + UART_DLM * 4);
+
+	writeb(lcr, uart + UART_LCR * 4);
+}
+
+#define MC_SECURITY_START	0x6c
+#define MC_SECURITY_SIZE	0x70
+
+static int tegra_suspend_enter(suspend_state_t state)
+{
+	void __iomem *mc = IO_ADDRESS(TEGRA_MC_BASE);
+	unsigned long flags;
+	u32 mc_data[2];
+	bool do_lp0 = (current_suspend_mode == TEGRA_SUSPEND_LP0);
+	bool do_lp2 = (current_suspend_mode == TEGRA_SUSPEND_LP2);
+	int lp_state;
+	u64 rtc_before;
+	u64 rtc_after;
+	u64 secs;
+	u32 ms;
+
+	if (do_lp2)
+		lp_state = 2;
+	else if (do_lp0)
+		lp_state = 0;
+	else
+		lp_state = 1;
+
+	local_irq_save(flags);
+	local_fiq_disable();
+
+	pr_info("Entering suspend state LP%d\n", lp_state);
+	if (do_lp0) {
+		tegra_irq_suspend();
+		tegra_dma_suspend();
+		tegra_debug_uart_suspend();
+		tegra_pinmux_suspend();
+		tegra_timer_suspend();
+		tegra_gpio_suspend();
+		tegra_clk_suspend();
+
+		mc_data[0] = readl(mc + MC_SECURITY_START);
+		mc_data[1] = readl(mc + MC_SECURITY_SIZE);
+	}
+
+	rtc_before = tegra_rtc_read_ms();
+
+	if (do_lp2)
+		tegra_suspend_lp2(0);
+	else
+		tegra_suspend_dram(do_lp0);
+
+	rtc_after = tegra_rtc_read_ms();
+
+	/* Clear DPD sample */
+	writel(0x0, pmc + PMC_DPD_SAMPLE);
+
+	if (do_lp0) {
+		writel(mc_data[0], mc + MC_SECURITY_START);
+		writel(mc_data[1], mc + MC_SECURITY_SIZE);
+
+		tegra_clk_resume();
+		tegra_gpio_resume();
+		tegra_timer_resume();
+		tegra_pinmux_resume();
+		tegra_debug_uart_resume();
+		tegra_dma_resume();
+		tegra_irq_resume();
+	}
+
+	secs = rtc_after - rtc_before;
+	ms = do_div(secs, 1000);
+	pr_info("Suspended for %llu.%03u seconds\n", secs, ms);
+
+	tegra_time_in_suspend[time_to_bin(secs)]++;
+
+	local_fiq_enable();
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+static const struct platform_suspend_ops tegra_suspend_ops = {
+	.valid		= suspend_valid_only_mem,
+	.prepare_late	= tegra_suspend_prepare_late,
+	.wake		= tegra_suspend_wake,
+	.enter		= tegra_suspend_enter,
+};
+#endif
+
+void __init tegra_init_suspend(struct tegra_suspend_platform_data *plat)
+{
+	u32 reg, mode;
+
+	tegra_pclk = clk_get_sys(NULL, "pclk");
+	BUG_ON(!tegra_pclk);
+	pdata = plat;
+	(void)reg;
+	(void)mode;
+
+	if (plat->suspend_mode == TEGRA_SUSPEND_LP0 && tegra_lp0_vec_size) {
+		wb0_restore = tegra_lp0_vec_start;
+	} else {
+		pr_warning("Suspend mode LP0 requested, but missing lp0_vec\n");
+		pr_warning("Disabling LP0\n");
+		plat->suspend_mode = TEGRA_SUSPEND_LP1;
+	}
+
+	tegra_context_area = kzalloc(CONTEXT_SIZE_BYTES * NR_CPUS, GFP_KERNEL);
+
+	if (tegra_context_area && create_suspend_pgtable()) {
+		kfree(tegra_context_area);
+		tegra_context_area = NULL;
+	}
+
+#ifdef CONFIG_PM
+	iram_save_size = (unsigned long)__tegra_iram_end;
+	iram_save_size -= (unsigned long)__tegra_lp1_reset;
+
+	iram_save = kmalloc(iram_save_size, GFP_KERNEL);
+	if (!iram_save) {
+		pr_err("%s: unable to allocate memory for SDRAM self-refresh "
+		       "LP0/LP1 unavailable\n", __func__);
+		plat->suspend_mode = TEGRA_SUSPEND_LP2;
+	}
+	/* CPU reset vector for LP0 and LP1 */
+	writel(virt_to_phys(tegra_lp2_startup), pmc + PMC_SCRATCH41);
+
+	/* Always enable CPU power request; just normal polarity is supported */
+	reg = readl(pmc + PMC_CTRL);
+	BUG_ON(reg & (TEGRA_POWER_CPU_PWRREQ_POLARITY << TEGRA_POWER_PMC_SHIFT));
+	reg |= (TEGRA_POWER_CPU_PWRREQ_OE << TEGRA_POWER_PMC_SHIFT);
+	pmc_32kwritel(reg, PMC_CTRL);
+
+	/* Configure core power request and system clock control if LP0
+	   is supported */
+	writel(pdata->core_timer, pmc + PMC_COREPWRGOOD_TIMER);
+	writel(pdata->core_off_timer, pmc + PMC_COREPWROFF_TIMER);
+	reg = readl(pmc + PMC_CTRL);
+	mode = (reg >> TEGRA_POWER_PMC_SHIFT) & TEGRA_POWER_PMC_MASK;
+
+	mode &= ~TEGRA_POWER_SYSCLK_POLARITY;
+	mode &= ~TEGRA_POWER_PWRREQ_POLARITY;
+
+	if (!pdata->sysclkreq_high)
+		mode |= TEGRA_POWER_SYSCLK_POLARITY;
+	if (!pdata->corereq_high)
+		mode |= TEGRA_POWER_PWRREQ_POLARITY;
+
+	/* configure output inverters while the request is tristated */
+	reg |= (mode << TEGRA_POWER_PMC_SHIFT);
+	pmc_32kwritel(reg, PMC_CTRL);
+
+	/* now enable requests */
+	reg |= (TEGRA_POWER_SYSCLK_OE << TEGRA_POWER_PMC_SHIFT);
+	if (pdata->separate_req)
+		reg |= (TEGRA_POWER_PWRREQ_OE << TEGRA_POWER_PMC_SHIFT);
+	writel(reg, pmc + PMC_CTRL);
+
+	if (pdata->suspend_mode == TEGRA_SUSPEND_LP0)
+		lp0_suspend_init();
+
+	suspend_set_ops(&tegra_suspend_ops);
+#endif
+
+	current_suspend_mode = plat->suspend_mode;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static const char *tegra_suspend_name[TEGRA_MAX_SUSPEND_MODE] = {
+	[TEGRA_SUSPEND_NONE]	= "none",
+	[TEGRA_SUSPEND_LP2]	= "lp2",
+	[TEGRA_SUSPEND_LP1]	= "lp1",
+	[TEGRA_SUSPEND_LP0]	= "lp0",
+};
+
+static int tegra_suspend_debug_show(struct seq_file *s, void *data)
+{
+	seq_printf(s, "%s\n", tegra_suspend_name[*(int *)s->private]);
+	return 0;
+}
+
+static int tegra_suspend_debug_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, tegra_suspend_debug_show, inode->i_private);
+}
+
+static int tegra_suspend_debug_write(struct file *file,
+	const char __user *user_buf, size_t count, loff_t *ppos)
+{
+	char buf[32];
+	int buf_size;
+	int i;
+	struct seq_file *s = file->private_data;
+	enum tegra_suspend_mode *val = s->private;
+
+	memset(buf, 0x00, sizeof(buf));
+	buf_size = min(count, (sizeof(buf)-1));
+	if (copy_from_user(buf, user_buf, buf_size))
+		return -EFAULT;
+
+	for (i = 0; i < TEGRA_MAX_SUSPEND_MODE; i++) {
+		if (!strnicmp(buf, tegra_suspend_name[i],
+		    strlen(tegra_suspend_name[i]))) {
+			if (i > pdata->suspend_mode)
+				return -EINVAL;
+			*val = i;
+			return count;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static const struct file_operations tegra_suspend_debug_fops = {
+	.open		= tegra_suspend_debug_open,
+	.write		= tegra_suspend_debug_write,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int tegra_suspend_time_debug_show(struct seq_file *s, void *data)
+{
+	int bin;
+	seq_printf(s, "time (secs)  count\n");
+	seq_printf(s, "------------------\n");
+	for (bin = 0; bin < 32; bin++) {
+		if (tegra_time_in_suspend[bin] == 0)
+			continue;
+		seq_printf(s, "%4d - %4d %4u\n",
+			bin ? 1 << (bin - 1) : 0, 1 << bin,
+			tegra_time_in_suspend[bin]);
+	}
+	return 0;
+}
+
+static int tegra_suspend_time_debug_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, tegra_suspend_time_debug_show, NULL);
+}
+
+static const struct file_operations tegra_suspend_time_debug_fops = {
+	.open		= tegra_suspend_time_debug_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int __init tegra_suspend_debug_init(void)
+{
+	struct dentry *d;
+
+	d = debugfs_create_file("suspend_mode", 0755, NULL,
+		(void *)&current_suspend_mode, &tegra_suspend_debug_fops);
+	if (!d) {
+		pr_info("Failed to create suspend_mode debug file\n");
+		return -ENOMEM;
+	}
+
+	d = debugfs_create_file("suspend_time", 0755, NULL, NULL,
+		&tegra_suspend_time_debug_fops);
+	if (!d) {
+		pr_info("Failed to create suspend_time debug file\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+late_initcall(tegra_suspend_debug_init);
+#endif
diff --git a/arch/arm/mach-tegra/tegra2_save.S b/arch/arm/mach-tegra/tegra2_save.S
new file mode 100644
index 0000000..91f2ba0
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra2_save.S
@@ -0,0 +1,413 @@
+/*
+ * arch/arm/mach-tegra/tegra2_save.S
+ *
+ * CPU state save & restore routines for CPU hotplug
+ *
+ * Copyright (c) 2010, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/linkage.h>
+#include <linux/init.h>
+
+#include <asm/assembler.h>
+#include <asm/domain.h>
+#include <asm/ptrace.h>
+#include <asm/cache.h>
+#include <asm/vfpmacros.h>
+#include <asm/memory.h>
+#include <asm/hardware/cache-l2x0.h>
+
+#include <mach/iomap.h>
+#include <mach/io.h>
+
+#include "power.h"
+
+/*	.section ".cpuinit.text", "ax"*/
+
+#define TTB_FLAGS 0x6A	@ IRGN_WBWA, OC_RGN_WBWA, S, NOS
+
+#define EMC_CFG				0xc
+#define EMC_ADR_CFG			0x10
+#define EMC_REFRESH			0x70
+#define EMC_NOP				0xdc
+#define EMC_SELF_REF			0xe0
+#define EMC_REQ_CTRL			0x2b0
+#define EMC_EMC_STATUS			0x2b4
+
+#define PMC_CTRL			0x0
+#define PMC_CTRL_BFI_SHIFT		8
+#define PMC_CTRL_BFI_WIDTH		9
+#define PMC_SCRATCH38			0x134
+#define PMC_SCRATCH41			0x140
+
+#define CLK_RESET_CCLK_BURST		0x20
+#define CLK_RESET_CCLK_DIVIDER		0x24
+#define CLK_RESET_SCLK_BURST		0x28
+#define CLK_RESET_SCLK_DIVIDER		0x2c
+
+#define CLK_RESET_PLLC_BASE		0x80
+#define CLK_RESET_PLLM_BASE		0x90
+#define CLK_RESET_PLLP_BASE		0xa0
+
+#define FLOW_CTRL_HALT_CPU_EVENTS	0x0
+
+#include "power-macros.S"
+
+.macro emc_device_mask, rd, base
+	ldr	\rd, [\base, #EMC_ADR_CFG]
+	tst	\rd, #(0x3<<24)
+	moveq	\rd, #(0x1<<8)		@ just 1 device
+	movne	\rd, #(0x3<<8)		@ 2 devices
+.endm
+
+/*
+ *
+ *	__tear_down_master( r8 = context_pa, sp = power state )
+ *
+ *	  Set the clock burst policy to the selected wakeup source
+ *	  Enable CPU power-request mode in the PMC
+ *	  Put the CPU in wait-for-event mode on the flow controller
+ *	  Trigger the PMC state machine to put the CPU in reset
+ */
+ENTRY(__tear_down_master)
+__tear_down_master:
+#ifdef CONFIG_CACHE_L2X0
+	/* clean out the dirtied L2 lines, since all power transitions
+	 * cause the cache state to get invalidated (although LP1 & LP2
+	 * preserve the data in the L2, the control words (L2X0_CTRL,
+	 * L2X0_AUX_CTRL, etc.) need to be cleaned to L3 so that they
+	 * will be visible on reboot.  skip this for LP0, since the L2 cache
+	 * will be shutdown before we reach this point */
+	tst	sp, #TEGRA_POWER_EFFECT_LP0
+	bne	__l2_clean_done
+	mov32	r0, (TEGRA_ARM_PL310_BASE-IO_CPU_PHYS+IO_CPU_VIRT)
+	add	r3, r8, #(CONTEXT_SIZE_BYTES)
+	bic	r8, r8, #0x1f
+	add	r3, r3, #0x1f
+11:	str	r8, [r0, #L2X0_CLEAN_LINE_PA]
+	add	r8, r8, #32
+	cmp	r8, r3
+	blo	11b
+12:	ldr	r1, [r0, #L2X0_CLEAN_LINE_PA]
+	tst	r1, #1
+	bne	12b
+	mov	r1, #0
+	str	r1, [r0, #L2X0_CACHE_SYNC]
+13:	ldr	r1, [r0, #L2X0_CACHE_SYNC]
+	tst	r1, #1
+	bne	13b
+__l2_clean_done:
+#endif
+
+	tst	sp, #TEGRA_POWER_SDRAM_SELFREFRESH
+
+	/* preload all the address literals that are needed for the
+	 * CPU power-gating process, to avoid loads from SDRAM (which are
+	 * not supported once SDRAM is put into self-refresh.
+	 * LP0 / LP1 use physical address, since the MMU needs to be
+	 * disabled before putting SDRAM into self-refresh to avoid
+	 * memory access due to page table walks */
+	mov32	r0, (IO_APB_VIRT-IO_APB_PHYS)
+	mov32	r4, TEGRA_PMC_BASE
+	mov32	r0, (IO_PPSB_VIRT-IO_PPSB_PHYS)
+	mov32	r5, TEGRA_CLK_RESET_BASE
+	mov32	r6, TEGRA_FLOW_CTRL_BASE
+	mov32	r7, TEGRA_TMRUS_BASE
+
+	/* change page table pointer to tegra_pgd_phys, so that IRAM
+	 * and MMU shut-off will be mapped virtual == physical */
+	adr	r3, __tear_down_master_data
+	ldr	r3, [r3]		@ &tegra_pgd_phys
+	ldr	r3, [r3]
+	orr	r3, r3, #TTB_FLAGS
+	mov	r2, #0
+	mcr	p15, 0, r2, c13, c0, 1	@ reserved context
+	isb
+	mcr	p15, 0, r3, c2, c0, 0	@ TTB 0
+	isb
+
+	/* Obtain LP1 information.
+	 * R10 = LP1 branch target */
+	mov32	r2, __tegra_lp1_reset
+	mov32	r3, __tear_down_master_sdram
+	sub	r2, r3, r2
+	mov32	r3, (TEGRA_IRAM_CODE_AREA)
+	add	r10, r2, r3
+
+	mov32	r3, __shut_off_mmu
+
+	/* R9 = LP2 branch target */
+	mov32	r9, __tear_down_master_pll_cpu
+
+	/* Convert the branch targets
+	 * to physical addresses */
+	sub	r3, r3, #(PAGE_OFFSET - PHYS_OFFSET)
+	sub	r9, r9, #(PAGE_OFFSET - PHYS_OFFSET)
+	movne	r9, r10
+	bx	r3
+ENDPROC(__tear_down_master)
+	.type	__tear_down_master_data, %object
+__tear_down_master_data:
+	.long	tegra_pgd_phys
+	.size	__tear_down_master_data, . - __tear_down_master_data
+
+/*  START OF ROUTINES COPIED TO IRAM  */
+/*
+ *	__tegra_lp1_reset
+ *
+ *	  reset vector for LP1 restore; copied into IRAM during suspend.
+ *	  brings the system back up to a safe starting point (SDRAM out of
+ *	  self-refresh, PLLC, PLLM and PLLP reenabled, CPU running on PLLP,
+ *	  system clock running on the same PLL that it suspended at), and
+ *	  jumps to tegra_lp2_startup to restore PLLX and virtual addressing.
+ *	  physical address of tegra_lp2_startup expected to be stored in
+ *	  PMC_SCRATCH41
+ */
+	.align L1_CACHE_SHIFT
+ENTRY(__tegra_lp1_reset)
+__tegra_lp1_reset:
+	/* the CPU and system bus are running@32KHz and executing from
+	 * IRAM when this code is executed; immediately switch to CLKM and
+	 * enable PLLP. */
+	mov32	r0, TEGRA_CLK_RESET_BASE
+	mov	r1, #(1<<28)
+	str	r1, [r0, #CLK_RESET_SCLK_BURST]
+	str	r1, [r0, #CLK_RESET_CCLK_BURST]
+	mov	r1, #0
+	str	r1, [r0, #CLK_RESET_SCLK_DIVIDER]
+	str	r1, [r0, #CLK_RESET_CCLK_DIVIDER]
+
+	ldr	r1, [r0, #CLK_RESET_PLLM_BASE]
+	tst	r1, #(1<<30)
+	orreq	r1, r1, #(1<<30)
+	streq	r1, [r0, #CLK_RESET_PLLM_BASE]
+	ldr	r1, [r0, #CLK_RESET_PLLP_BASE]
+	tst	r1, #(1<<30)
+	orreq	r1, r1, #(1<<30)
+	streq	r1, [r0, #CLK_RESET_PLLP_BASE]
+	ldr	r1, [r0, #CLK_RESET_PLLC_BASE]
+	tst	r1, #(1<<30)
+	orreq	r1, r1, #(1<<30)
+	streq	r1, [r0, #CLK_RESET_PLLC_BASE]
+	mov32	r7, TEGRA_TMRUS_BASE
+	ldr	r1, [r7]
+
+	/* since the optimized settings are still in SDRAM, there is
+	 * no need to store them back into the IRAM-local __lp1_pad_area */
+	add	r2, pc, #__lp1_pad_area-(.+8)
+padload:ldmia	r2!, {r3-r4}
+	cmp	r3, #0
+	beq	padload_done
+	str	r4, [r3]
+	b	padload
+padload_done:
+	ldr	r2, [r7]
+	add	r2, r2, #0x4	@ 4uS delay for DRAM pad restoration
+	wait_until r2, r7, r3
+	add	r1, r1, #0xff	@ 255uS delay for PLL stabilization
+	wait_until r1, r7, r3
+
+	str	r4, [r0, #CLK_RESET_SCLK_BURST]
+	mov32	r4, ((1<<28) | (4))	@ burst policy is PLLP
+	str	r4, [r0, #CLK_RESET_CCLK_BURST]
+
+	mov32	r0, TEGRA_EMC_BASE
+	ldr	r1, [r0, #EMC_CFG]
+	bic	r1, r1, #(1<<31)	@ disable DRAM_CLK_STOP
+	str	r1, [r0, #EMC_CFG]
+
+	mov	r1, #0
+	str	r1, [r0, #EMC_SELF_REF]	@ take DRAM out of self refresh
+	mov	r1, #1
+	str	r1, [r0, #EMC_NOP]
+	str	r1, [r0, #EMC_NOP]
+	str	r1, [r0, #EMC_REFRESH]
+
+	emc_device_mask r1, r0
+
+exit_selfrefresh_loop:
+	ldr	r2, [r0, #EMC_EMC_STATUS]
+	ands	r2, r2, r1
+	bne	exit_selfrefresh_loop
+
+	mov	r1, #0
+	str	r1, [r0, #EMC_REQ_CTRL]
+
+	mov32	r0, TEGRA_PMC_BASE
+	ldr	r0, [r0, #PMC_SCRATCH41]
+	mov	pc, r0
+ENDPROC(__tegra_lp1_reset)
+
+/*
+ *	__tear_down_master_sdram
+ *
+ *	  disables MMU, data cache, and puts SDRAM into self-refresh.
+ *	  must execute from IRAM.
+ */
+	.align L1_CACHE_SHIFT
+__tear_down_master_sdram:
+	mov32	r1, TEGRA_EMC_BASE
+	mov	r2, #3
+	str	r2, [r1, #EMC_REQ_CTRL]		@ stall incoming DRAM requests
+
+emcidle:ldr	r2, [r1, #EMC_EMC_STATUS]
+	tst	r2, #4
+	beq	emcidle
+
+	mov	r2, #1
+	str	r2, [r1, #EMC_SELF_REF]
+
+	emc_device_mask r2, r1
+
+emcself:ldr	r3, [r1, #EMC_EMC_STATUS]
+	and	r3, r3, r2
+	cmp	r3, r2
+	bne	emcself				@ loop until DDR in self-refresh
+
+	add	r2, pc, #__lp1_pad_area-(.+8)
+
+padsave:ldm	r2, {r0-r1}
+	cmp	r0, #0
+	beq	padsave_done
+	ldr	r3, [r0]
+	str	r1, [r0]
+	str	r3, [r2, #4]
+	add	r2, r2, #8
+	b	padsave
+padsave_done:
+
+	ldr	r0, [r5, #CLK_RESET_SCLK_BURST]
+	str	r0, [r2, #4]
+	dsb
+	b	__tear_down_master_pll_cpu
+ENDPROC(__tear_down_master_sdram)
+
+	.align	L1_CACHE_SHIFT
+	.type	__lp1_pad_area, %object
+__lp1_pad_area:
+	.word	TEGRA_APB_MISC_BASE + 0x8c8 /* XM2CFGCPADCTRL */
+	.word	0x8
+	.word	TEGRA_APB_MISC_BASE + 0x8cc /* XM2CFGDPADCTRL */
+	.word	0x8
+	.word	TEGRA_APB_MISC_BASE + 0x8d0 /* XM2CLKCFGPADCTRL */
+	.word	0x0
+	.word	TEGRA_APB_MISC_BASE + 0x8d4 /* XM2COMPPADCTRL */
+	.word	0x8
+	.word	TEGRA_APB_MISC_BASE + 0x8d8 /* XM2VTTGENPADCTRL */
+	.word	0x5500
+	.word	TEGRA_APB_MISC_BASE + 0x8e4 /* XM2CFGCPADCTRL2 */
+	.word	0x08080040
+	.word	TEGRA_APB_MISC_BASE + 0x8e8 /* XM2CFGDPADCTRL2 */
+	.word	0x0
+	.word	0x0	/* end of list */
+	.word	0x0	/* sclk_burst_policy */
+	.size	__lp1_pad_area, . - __lp1_pad_area
+
+	.align L1_CACHE_SHIFT
+__tear_down_master_pll_cpu:
+	ldr	r0, [r4, #PMC_CTRL]
+	bfi	r0, sp, #PMC_CTRL_BFI_SHIFT, #PMC_CTRL_BFI_WIDTH
+	str	r0, [r4, #PMC_CTRL]
+	tst	sp, #TEGRA_POWER_SDRAM_SELFREFRESH
+
+	/* in LP2 idle (SDRAM active), set the CPU burst policy to PLLP */
+	moveq	r0, #(2<<28)    /* burst policy = run mode */
+	orreq	r0, r0, #(4<<4) /* use PLLP in run mode burst */
+	streq	r0, [r5, #CLK_RESET_CCLK_BURST]
+	moveq	r0, #0
+	streq	r0, [r5, #CLK_RESET_CCLK_DIVIDER]
+	beq	__cclk_burst_set
+
+	/* in other modes, set system & CPU burst policies to 32KHz.
+	 * start by jumping to CLKM to safely disable PLLs, then jump
+	 * to CLKS */
+	mov	r0, #(1<<28)
+	str	r0, [r5, #CLK_RESET_SCLK_BURST]
+	str	r0, [r5, #CLK_RESET_CCLK_BURST]
+	mov	r0, #0
+	str	r0, [r5, #CLK_RESET_CCLK_DIVIDER]
+	str	r0, [r5, #CLK_RESET_SCLK_DIVIDER]
+
+	/* 2 us delay between changing sclk and disabling PLLs */
+	wait_for_us r1, r7, r9
+	add	r1, r1, #2
+	wait_until r1, r7, r9
+
+	/* switch to CLKS */
+	mov	r0, #0	/* burst policy = 32KHz */
+	str	r0, [r5, #CLK_RESET_SCLK_BURST]
+
+	/* disable PLLP, PLLM, PLLC in LP0 and LP1 states */
+	ldr	r0, [r5, #CLK_RESET_PLLM_BASE]
+	bic	r0, r0, #(1<<30)
+	str	r0, [r5, #CLK_RESET_PLLM_BASE]
+	ldr	r0, [r5, #CLK_RESET_PLLP_BASE]
+	bic	r0, r0, #(1<<30)
+	str	r0, [r5, #CLK_RESET_PLLP_BASE]
+	ldr	r0, [r5, #CLK_RESET_PLLC_BASE]
+	bic	r0, r0, #(1<<30)
+	str	r0, [r5, #CLK_RESET_PLLC_BASE]
+
+__cclk_burst_set:
+	mov	r0, #(4<<29)			/* STOP_UNTIL_IRQ */
+	orr	r0, r0, #(1<<10) | (1<<8)	/* IRQ_0, FIQ_0	*/
+	ldr	r1, [r7]
+	str	r1, [r4, #PMC_SCRATCH38]
+	dsb
+	str	r0, [r6, #FLOW_CTRL_HALT_CPU_EVENTS]
+	dsb
+	ldr	r0, [r6, #FLOW_CTRL_HALT_CPU_EVENTS] /* memory barrier */
+
+halted:	dsb
+	wfe	/* CPU should be power gated here */
+	isb
+	b	halted
+ENDPROC(__tear_down_master_pll_cpu)
+
+/*
+ *	__put_cpu_in_reset(cpu_nr)
+ *
+ *	 puts the specified CPU in wait-for-event mode on the flow controller
+ *	 and puts the CPU in reset
+ */
+ENTRY(__put_cpu_in_reset)
+__put_cpu_in_reset:
+	cmp	r0, #0
+	subne	r1, r0, #1
+	movne	r1, r1, lsl #3
+	addne	r1, r1, #0x14
+	moveq	r1, #0			@ r1 = CPUx_HALT_EVENTS register offset
+	mov32	r7, (TEGRA_FLOW_CTRL_BASE-IO_PPSB_PHYS+IO_PPSB_VIRT)
+	mov	r2, #(0x2<<29)
+	str	r2, [r7, r1]		@ put flow controller in wait event mode
+	isb
+	dsb
+	movw	r1, 0x1011
+	mov	r1, r1, lsl r0
+	mov32	r7, (TEGRA_CLK_RESET_BASE-IO_PPSB_PHYS+IO_PPSB_VIRT)
+	str	r1, [r7, #0x340]	@ put slave CPU in reset
+	isb
+	dsb
+	b	.
+ENDPROC(__put_cpu_in_reset)
+
+/* dummy symbol for end of IRAM */
+	.align L1_CACHE_SHIFT
+ENTRY(__tegra_iram_end)
+__tegra_iram_end:
+	b	.
+ENDPROC(__tegra_iram_end)
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 14/28] ARM: tegra: irq: Add set_wake and set_type support for suspend
  2011-01-24  2:01 [PATCH v2 00/28] Updates for Tegra support in 2.6.39 Colin Cross
@ 2011-01-24  2:01   ` Colin Cross
  2011-01-24  2:01   ` Colin Cross
                     ` (26 subsequent siblings)
  27 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-tegra
  Cc: linux-arm-kernel, olof, konkers, Colin Cross, Russell King, linux-kernel

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/include/mach/legacy_irq.h |    3 +-
 arch/arm/mach-tegra/irq.c                     |  106 +++++++++++++++++++++++++
 arch/arm/mach-tegra/legacy_irq.c              |    4 +-
 3 files changed, 110 insertions(+), 3 deletions(-)

diff --git a/arch/arm/mach-tegra/include/mach/legacy_irq.h b/arch/arm/mach-tegra/include/mach/legacy_irq.h
index d898c0e..3a2bfab 100644
--- a/arch/arm/mach-tegra/include/mach/legacy_irq.h
+++ b/arch/arm/mach-tegra/include/mach/legacy_irq.h
@@ -31,5 +31,6 @@ int tegra_legacy_irq_set_wake(int irq, int enable);
 void tegra_legacy_irq_set_lp1_wake_mask(void);
 void tegra_legacy_irq_restore_mask(void);
 void tegra_init_legacy_irq(void);
-
+void tegra_legacy_irq_suspend(void);
+void tegra_legacy_irq_resume(void);
 #endif
diff --git a/arch/arm/mach-tegra/irq.c b/arch/arm/mach-tegra/irq.c
index 4892394..c292d3b 100644
--- a/arch/arm/mach-tegra/irq.c
+++ b/arch/arm/mach-tegra/irq.c
@@ -54,6 +54,51 @@ static void pmc_32kwritel(u32 val, unsigned long offs)
 	udelay(130);
 }
 
+int tegra_set_lp0_wake(int irq, int enable)
+{
+	int wake = tegra_irq_to_wake(irq);
+
+	if (wake < 0)
+		return -EINVAL;
+
+	if (enable)
+		tegra_lp0_wake_enb |= 1 << wake;
+	else
+		tegra_lp0_wake_enb &= ~(1 << wake);
+
+	return 0;
+}
+
+int tegra_set_lp0_wake_type(int irq, int flow_type)
+{
+	int wake = tegra_irq_to_wake(irq);
+
+	if (wake < 0)
+		return 0;
+
+	switch (flow_type) {
+	case IRQF_TRIGGER_FALLING:
+	case IRQF_TRIGGER_LOW:
+		tegra_lp0_wake_level &= ~(1 << wake);
+		tegra_lp0_wake_level_any &= ~(1 << wake);
+		break;
+	case IRQF_TRIGGER_HIGH:
+	case IRQF_TRIGGER_RISING:
+		tegra_lp0_wake_level |= 1 << wake;
+		tegra_lp0_wake_level_any &= ~(1 << wake);
+		break;
+
+	case IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING:
+		tegra_lp0_wake_level_any |= 1 << wake;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+
 int tegra_set_lp1_wake(int irq, int enable)
 {
 	return tegra_legacy_irq_set_wake(irq, enable);
@@ -97,6 +142,33 @@ void tegra_set_lp0_wake_pads(u32 wake_enb, u32 wake_level, u32 wake_any)
 	writel(wake_enb, pmc + PMC_WAKE_MASK);
 }
 
+static void tegra_irq_handle_wake(void)
+{
+	int wake;
+	int irq;
+	struct irq_desc *desc;
+
+	unsigned long wake_status = readl(pmc + PMC_WAKE_STATUS);
+	for_each_set_bit(wake, &wake_status, sizeof(wake_status) * 8) {
+		irq = tegra_wake_to_irq(wake);
+		if (!irq) {
+			pr_info("Resume caused by WAKE%d\n", wake);
+			continue;
+		}
+
+		desc = irq_to_desc(irq);
+		if (!desc || !desc->action || !desc->action->name) {
+			pr_info("Resume caused by WAKE%d, irq %d\n", wake, irq);
+			continue;
+		}
+
+		pr_info("Resume caused by WAKE%d, %s\n", wake,
+			desc->action->name);
+
+		generic_handle_irq(irq);
+	}
+}
+
 static void tegra_mask(struct irq_data *d)
 {
 	gic_mask_irq(d);
@@ -109,11 +181,34 @@ static void tegra_unmask(struct irq_data *d)
 	tegra_legacy_unmask_irq(d->irq);
 }
 
+static int tegra_set_wake(struct irq_data *d, unsigned int enable)
+{
+	int ret;
+	ret = tegra_set_lp1_wake(d->irq, enable);
+	if (ret)
+		return ret;
+
+	if (tegra_get_suspend_mode() == TEGRA_SUSPEND_LP0)
+		return tegra_set_lp0_wake(d->irq, enable);
+
+	return 0;
+}
+
+static int tegra_set_type(struct irq_data *d, unsigned int flow_type)
+{
+	if (tegra_get_suspend_mode() == TEGRA_SUSPEND_LP0)
+		return tegra_set_lp0_wake_type(d->irq, flow_type);
+
+	return 0;
+}
+
 static struct irq_chip tegra_irq = {
 	.name			= "PPI",
 	.irq_ack		= gic_ack_irq,
 	.irq_mask		= tegra_mask,
 	.irq_unmask		= tegra_unmask,
+	.irq_set_wake		= tegra_set_wake,
+	.irq_set_type		= tegra_set_type,
 #ifdef CONFIG_SMP
 	.irq_set_affinity	= gic_set_cpu,
 #endif
@@ -136,3 +231,14 @@ void __init tegra_init_irq(void)
 		set_irq_flags(irq, IRQF_VALID);
 	}
 }
+
+void tegra_irq_suspend(void)
+{
+	tegra_legacy_irq_suspend();
+}
+
+void tegra_irq_resume(void)
+{
+	tegra_legacy_irq_resume();
+	tegra_irq_handle_wake();
+}
diff --git a/arch/arm/mach-tegra/legacy_irq.c b/arch/arm/mach-tegra/legacy_irq.c
index 38eb719..5a6197b 100644
--- a/arch/arm/mach-tegra/legacy_irq.c
+++ b/arch/arm/mach-tegra/legacy_irq.c
@@ -179,7 +179,7 @@ static u32 cop_ier[NUM_ICTLRS];
 static u32 cpu_ier[NUM_ICTLRS];
 static u32 cpu_iep[NUM_ICTLRS];
 
-void tegra_irq_suspend(void)
+void tegra_legacy_irq_suspend(void)
 {
 	unsigned long flags;
 	int i;
@@ -195,7 +195,7 @@ void tegra_irq_suspend(void)
 	local_irq_restore(flags);
 }
 
-void tegra_irq_resume(void)
+void tegra_legacy_irq_resume(void)
 {
 	unsigned long flags;
 	int i;
-- 
1.7.3.1


^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 14/28] ARM: tegra: irq: Add set_wake and set_type support for suspend
@ 2011-01-24  2:01   ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/include/mach/legacy_irq.h |    3 +-
 arch/arm/mach-tegra/irq.c                     |  106 +++++++++++++++++++++++++
 arch/arm/mach-tegra/legacy_irq.c              |    4 +-
 3 files changed, 110 insertions(+), 3 deletions(-)

diff --git a/arch/arm/mach-tegra/include/mach/legacy_irq.h b/arch/arm/mach-tegra/include/mach/legacy_irq.h
index d898c0e..3a2bfab 100644
--- a/arch/arm/mach-tegra/include/mach/legacy_irq.h
+++ b/arch/arm/mach-tegra/include/mach/legacy_irq.h
@@ -31,5 +31,6 @@ int tegra_legacy_irq_set_wake(int irq, int enable);
 void tegra_legacy_irq_set_lp1_wake_mask(void);
 void tegra_legacy_irq_restore_mask(void);
 void tegra_init_legacy_irq(void);
-
+void tegra_legacy_irq_suspend(void);
+void tegra_legacy_irq_resume(void);
 #endif
diff --git a/arch/arm/mach-tegra/irq.c b/arch/arm/mach-tegra/irq.c
index 4892394..c292d3b 100644
--- a/arch/arm/mach-tegra/irq.c
+++ b/arch/arm/mach-tegra/irq.c
@@ -54,6 +54,51 @@ static void pmc_32kwritel(u32 val, unsigned long offs)
 	udelay(130);
 }
 
+int tegra_set_lp0_wake(int irq, int enable)
+{
+	int wake = tegra_irq_to_wake(irq);
+
+	if (wake < 0)
+		return -EINVAL;
+
+	if (enable)
+		tegra_lp0_wake_enb |= 1 << wake;
+	else
+		tegra_lp0_wake_enb &= ~(1 << wake);
+
+	return 0;
+}
+
+int tegra_set_lp0_wake_type(int irq, int flow_type)
+{
+	int wake = tegra_irq_to_wake(irq);
+
+	if (wake < 0)
+		return 0;
+
+	switch (flow_type) {
+	case IRQF_TRIGGER_FALLING:
+	case IRQF_TRIGGER_LOW:
+		tegra_lp0_wake_level &= ~(1 << wake);
+		tegra_lp0_wake_level_any &= ~(1 << wake);
+		break;
+	case IRQF_TRIGGER_HIGH:
+	case IRQF_TRIGGER_RISING:
+		tegra_lp0_wake_level |= 1 << wake;
+		tegra_lp0_wake_level_any &= ~(1 << wake);
+		break;
+
+	case IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING:
+		tegra_lp0_wake_level_any |= 1 << wake;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+
 int tegra_set_lp1_wake(int irq, int enable)
 {
 	return tegra_legacy_irq_set_wake(irq, enable);
@@ -97,6 +142,33 @@ void tegra_set_lp0_wake_pads(u32 wake_enb, u32 wake_level, u32 wake_any)
 	writel(wake_enb, pmc + PMC_WAKE_MASK);
 }
 
+static void tegra_irq_handle_wake(void)
+{
+	int wake;
+	int irq;
+	struct irq_desc *desc;
+
+	unsigned long wake_status = readl(pmc + PMC_WAKE_STATUS);
+	for_each_set_bit(wake, &wake_status, sizeof(wake_status) * 8) {
+		irq = tegra_wake_to_irq(wake);
+		if (!irq) {
+			pr_info("Resume caused by WAKE%d\n", wake);
+			continue;
+		}
+
+		desc = irq_to_desc(irq);
+		if (!desc || !desc->action || !desc->action->name) {
+			pr_info("Resume caused by WAKE%d, irq %d\n", wake, irq);
+			continue;
+		}
+
+		pr_info("Resume caused by WAKE%d, %s\n", wake,
+			desc->action->name);
+
+		generic_handle_irq(irq);
+	}
+}
+
 static void tegra_mask(struct irq_data *d)
 {
 	gic_mask_irq(d);
@@ -109,11 +181,34 @@ static void tegra_unmask(struct irq_data *d)
 	tegra_legacy_unmask_irq(d->irq);
 }
 
+static int tegra_set_wake(struct irq_data *d, unsigned int enable)
+{
+	int ret;
+	ret = tegra_set_lp1_wake(d->irq, enable);
+	if (ret)
+		return ret;
+
+	if (tegra_get_suspend_mode() == TEGRA_SUSPEND_LP0)
+		return tegra_set_lp0_wake(d->irq, enable);
+
+	return 0;
+}
+
+static int tegra_set_type(struct irq_data *d, unsigned int flow_type)
+{
+	if (tegra_get_suspend_mode() == TEGRA_SUSPEND_LP0)
+		return tegra_set_lp0_wake_type(d->irq, flow_type);
+
+	return 0;
+}
+
 static struct irq_chip tegra_irq = {
 	.name			= "PPI",
 	.irq_ack		= gic_ack_irq,
 	.irq_mask		= tegra_mask,
 	.irq_unmask		= tegra_unmask,
+	.irq_set_wake		= tegra_set_wake,
+	.irq_set_type		= tegra_set_type,
 #ifdef CONFIG_SMP
 	.irq_set_affinity	= gic_set_cpu,
 #endif
@@ -136,3 +231,14 @@ void __init tegra_init_irq(void)
 		set_irq_flags(irq, IRQF_VALID);
 	}
 }
+
+void tegra_irq_suspend(void)
+{
+	tegra_legacy_irq_suspend();
+}
+
+void tegra_irq_resume(void)
+{
+	tegra_legacy_irq_resume();
+	tegra_irq_handle_wake();
+}
diff --git a/arch/arm/mach-tegra/legacy_irq.c b/arch/arm/mach-tegra/legacy_irq.c
index 38eb719..5a6197b 100644
--- a/arch/arm/mach-tegra/legacy_irq.c
+++ b/arch/arm/mach-tegra/legacy_irq.c
@@ -179,7 +179,7 @@ static u32 cop_ier[NUM_ICTLRS];
 static u32 cpu_ier[NUM_ICTLRS];
 static u32 cpu_iep[NUM_ICTLRS];
 
-void tegra_irq_suspend(void)
+void tegra_legacy_irq_suspend(void)
 {
 	unsigned long flags;
 	int i;
@@ -195,7 +195,7 @@ void tegra_irq_suspend(void)
 	local_irq_restore(flags);
 }
 
-void tegra_irq_resume(void)
+void tegra_legacy_irq_resume(void)
 {
 	unsigned long flags;
 	int i;
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 15/28] ARM: tegra: irq: Add debugfs file to show wake irqs
  2011-01-24  2:01 [PATCH v2 00/28] Updates for Tegra support in 2.6.39 Colin Cross
@ 2011-01-24  2:01   ` Colin Cross
  2011-01-24  2:01   ` Colin Cross
                     ` (26 subsequent siblings)
  27 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-tegra
  Cc: linux-arm-kernel, olof, konkers, Colin Cross, sboyd,
	Russell King, linux-kernel

Signed-off-by: Colin Cross <ccross@android.com>
---
v2: Fix permissions on debufs file

 arch/arm/mach-tegra/irq.c |   66 +++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 66 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-tegra/irq.c b/arch/arm/mach-tegra/irq.c
index c292d3b..12c62a8 100644
--- a/arch/arm/mach-tegra/irq.c
+++ b/arch/arm/mach-tegra/irq.c
@@ -19,10 +19,12 @@
 
 #include <linux/kernel.h>
 #include <linux/delay.h>
+#include <linux/debugfs.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/io.h>
+#include <linux/seq_file.h>
 
 #include <asm/hardware/gic.h>
 
@@ -46,6 +48,8 @@ static u32 tegra_lp0_wake_enb;
 static u32 tegra_lp0_wake_level;
 static u32 tegra_lp0_wake_level_any;
 
+static unsigned int tegra_wake_irq_count[32];
+
 /* ensures that sufficient time is passed for a register write to
  * serialize into the 32KHz domain */
 static void pmc_32kwritel(u32 val, unsigned long offs)
@@ -165,6 +169,8 @@ static void tegra_irq_handle_wake(void)
 		pr_info("Resume caused by WAKE%d, %s\n", wake,
 			desc->action->name);
 
+		tegra_wake_irq_count[wake]++;
+
 		generic_handle_irq(irq);
 	}
 }
@@ -242,3 +248,63 @@ void tegra_irq_resume(void)
 	tegra_legacy_irq_resume();
 	tegra_irq_handle_wake();
 }
+
+#ifdef CONFIG_DEBUG_FS
+static int tegra_wake_irq_debug_show(struct seq_file *s, void *data)
+{
+	int wake;
+	int irq;
+	struct irq_desc *desc;
+	const char *irq_name;
+
+	seq_printf(s, "wake  irq  count  name\n");
+	seq_printf(s, "----------------------\n");
+	for (wake = 0; wake < 32; wake++) {
+		irq = tegra_wake_to_irq(wake);
+		if (irq < 0)
+			continue;
+
+		desc = irq_to_desc(irq);
+		if (tegra_wake_irq_count[wake] == 0 && desc->action == NULL)
+			continue;
+
+		if (!(desc->status & IRQ_WAKEUP))
+			continue;
+
+		irq_name = (desc->action && desc->action->name) ?
+			desc->action->name : "???";
+
+		seq_printf(s, "%4d  %3d  %5d  %s\n",
+			wake, irq, tegra_wake_irq_count[wake], irq_name);
+	}
+	return 0;
+}
+
+static int tegra_wake_irq_debug_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, tegra_wake_irq_debug_show, NULL);
+}
+
+static const struct file_operations tegra_wake_irq_debug_fops = {
+	.open		= tegra_wake_irq_debug_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int __init tegra_irq_debug_init(void)
+{
+	struct dentry *d;
+
+	d = debugfs_create_file("wake_irq", S_IRUGO, NULL, NULL,
+		&tegra_wake_irq_debug_fops);
+	if (!d) {
+		pr_info("Failed to create suspend_mode debug file\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+late_initcall(tegra_irq_debug_init);
+#endif
-- 
1.7.3.1


^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 15/28] ARM: tegra: irq: Add debugfs file to show wake irqs
@ 2011-01-24  2:01   ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Colin Cross <ccross@android.com>
---
v2: Fix permissions on debufs file

 arch/arm/mach-tegra/irq.c |   66 +++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 66 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-tegra/irq.c b/arch/arm/mach-tegra/irq.c
index c292d3b..12c62a8 100644
--- a/arch/arm/mach-tegra/irq.c
+++ b/arch/arm/mach-tegra/irq.c
@@ -19,10 +19,12 @@
 
 #include <linux/kernel.h>
 #include <linux/delay.h>
+#include <linux/debugfs.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/io.h>
+#include <linux/seq_file.h>
 
 #include <asm/hardware/gic.h>
 
@@ -46,6 +48,8 @@ static u32 tegra_lp0_wake_enb;
 static u32 tegra_lp0_wake_level;
 static u32 tegra_lp0_wake_level_any;
 
+static unsigned int tegra_wake_irq_count[32];
+
 /* ensures that sufficient time is passed for a register write to
  * serialize into the 32KHz domain */
 static void pmc_32kwritel(u32 val, unsigned long offs)
@@ -165,6 +169,8 @@ static void tegra_irq_handle_wake(void)
 		pr_info("Resume caused by WAKE%d, %s\n", wake,
 			desc->action->name);
 
+		tegra_wake_irq_count[wake]++;
+
 		generic_handle_irq(irq);
 	}
 }
@@ -242,3 +248,63 @@ void tegra_irq_resume(void)
 	tegra_legacy_irq_resume();
 	tegra_irq_handle_wake();
 }
+
+#ifdef CONFIG_DEBUG_FS
+static int tegra_wake_irq_debug_show(struct seq_file *s, void *data)
+{
+	int wake;
+	int irq;
+	struct irq_desc *desc;
+	const char *irq_name;
+
+	seq_printf(s, "wake  irq  count  name\n");
+	seq_printf(s, "----------------------\n");
+	for (wake = 0; wake < 32; wake++) {
+		irq = tegra_wake_to_irq(wake);
+		if (irq < 0)
+			continue;
+
+		desc = irq_to_desc(irq);
+		if (tegra_wake_irq_count[wake] == 0 && desc->action == NULL)
+			continue;
+
+		if (!(desc->status & IRQ_WAKEUP))
+			continue;
+
+		irq_name = (desc->action && desc->action->name) ?
+			desc->action->name : "???";
+
+		seq_printf(s, "%4d  %3d  %5d  %s\n",
+			wake, irq, tegra_wake_irq_count[wake], irq_name);
+	}
+	return 0;
+}
+
+static int tegra_wake_irq_debug_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, tegra_wake_irq_debug_show, NULL);
+}
+
+static const struct file_operations tegra_wake_irq_debug_fops = {
+	.open		= tegra_wake_irq_debug_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int __init tegra_irq_debug_init(void)
+{
+	struct dentry *d;
+
+	d = debugfs_create_file("wake_irq", S_IRUGO, NULL, NULL,
+		&tegra_wake_irq_debug_fops);
+	if (!d) {
+		pr_info("Failed to create suspend_mode debug file\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+late_initcall(tegra_irq_debug_init);
+#endif
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 16/28] ARM: tegra: irq: Implement retrigger
  2011-01-24  2:01 [PATCH v2 00/28] Updates for Tegra support in 2.6.39 Colin Cross
@ 2011-01-24  2:01   ` Colin Cross
  2011-01-24  2:01   ` Colin Cross
                     ` (26 subsequent siblings)
  27 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-tegra
  Cc: linux-arm-kernel, olof, konkers, Colin Cross, Russell King, linux-kernel

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/irq.c |   15 ++++++++++++++-
 1 files changed, 14 insertions(+), 1 deletions(-)

diff --git a/arch/arm/mach-tegra/irq.c b/arch/arm/mach-tegra/irq.c
index 12c62a8..2bd5be5 100644
--- a/arch/arm/mach-tegra/irq.c
+++ b/arch/arm/mach-tegra/irq.c
@@ -208,9 +208,21 @@ static int tegra_set_type(struct irq_data *d, unsigned int flow_type)
 	return 0;
 }
 
+static void tegra_ack(struct irq_data *d)
+{
+	tegra_legacy_force_irq_clr(d->irq);
+	gic_ack_irq(d);
+}
+
+static int tegra_retrigger(struct irq_data *d)
+{
+	tegra_legacy_force_irq_set(d->irq);
+	return 1;
+}
+
 static struct irq_chip tegra_irq = {
 	.name			= "PPI",
-	.irq_ack		= gic_ack_irq,
+	.irq_ack		= tegra_ack,
 	.irq_mask		= tegra_mask,
 	.irq_unmask		= tegra_unmask,
 	.irq_set_wake		= tegra_set_wake,
@@ -218,6 +230,7 @@ static struct irq_chip tegra_irq = {
 #ifdef CONFIG_SMP
 	.irq_set_affinity	= gic_set_cpu,
 #endif
+	.irq_retrigger		= tegra_retrigger,
 };
 
 void __init tegra_init_irq(void)
-- 
1.7.3.1


^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 16/28] ARM: tegra: irq: Implement retrigger
@ 2011-01-24  2:01   ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/irq.c |   15 ++++++++++++++-
 1 files changed, 14 insertions(+), 1 deletions(-)

diff --git a/arch/arm/mach-tegra/irq.c b/arch/arm/mach-tegra/irq.c
index 12c62a8..2bd5be5 100644
--- a/arch/arm/mach-tegra/irq.c
+++ b/arch/arm/mach-tegra/irq.c
@@ -208,9 +208,21 @@ static int tegra_set_type(struct irq_data *d, unsigned int flow_type)
 	return 0;
 }
 
+static void tegra_ack(struct irq_data *d)
+{
+	tegra_legacy_force_irq_clr(d->irq);
+	gic_ack_irq(d);
+}
+
+static int tegra_retrigger(struct irq_data *d)
+{
+	tegra_legacy_force_irq_set(d->irq);
+	return 1;
+}
+
 static struct irq_chip tegra_irq = {
 	.name			= "PPI",
-	.irq_ack		= gic_ack_irq,
+	.irq_ack		= tegra_ack,
 	.irq_mask		= tegra_mask,
 	.irq_unmask		= tegra_unmask,
 	.irq_set_wake		= tegra_set_wake,
@@ -218,6 +230,7 @@ static struct irq_chip tegra_irq = {
 #ifdef CONFIG_SMP
 	.irq_set_affinity	= gic_set_cpu,
 #endif
+	.irq_retrigger		= tegra_retrigger,
 };
 
 void __init tegra_init_irq(void)
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 17/28] ARM: tegra: gpio: Add support for waking from suspend
  2011-01-24  2:01 [PATCH v2 00/28] Updates for Tegra support in 2.6.39 Colin Cross
@ 2011-01-24  2:01   ` Colin Cross
  2011-01-24  2:01   ` Colin Cross
                     ` (26 subsequent siblings)
  27 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-tegra
  Cc: linux-arm-kernel, olof, konkers, Colin Cross, Russell King, linux-kernel

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/gpio.c |   14 +++++++++++++-
 1 files changed, 13 insertions(+), 1 deletions(-)

diff --git a/arch/arm/mach-tegra/gpio.c b/arch/arm/mach-tegra/gpio.c
index 132dcd6..5d14ccc 100644
--- a/arch/arm/mach-tegra/gpio.c
+++ b/arch/arm/mach-tegra/gpio.c
@@ -212,6 +212,9 @@ static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type)
 	else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
 		__set_irq_handler_unlocked(d->irq, handle_edge_irq);
 
+	if (tegra_get_suspend_mode() == TEGRA_SUSPEND_LP0)
+		tegra_set_lp0_wake_type(d->irq, type);
+
 	return 0;
 }
 
@@ -319,8 +322,17 @@ void tegra_gpio_suspend(void)
 
 static int tegra_gpio_wake_enable(struct irq_data *d, unsigned int enable)
 {
+	int ret;
 	struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d);
-	return set_irq_wake(bank->irq, enable);
+
+	ret = tegra_set_lp1_wake(bank->irq, enable);
+	if (ret)
+		return ret;
+
+	if (tegra_get_suspend_mode() == TEGRA_SUSPEND_LP0)
+		return tegra_set_lp0_wake(d->irq, enable);
+
+	return 0;
 }
 #endif
 
-- 
1.7.3.1


^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 17/28] ARM: tegra: gpio: Add support for waking from suspend
@ 2011-01-24  2:01   ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/gpio.c |   14 +++++++++++++-
 1 files changed, 13 insertions(+), 1 deletions(-)

diff --git a/arch/arm/mach-tegra/gpio.c b/arch/arm/mach-tegra/gpio.c
index 132dcd6..5d14ccc 100644
--- a/arch/arm/mach-tegra/gpio.c
+++ b/arch/arm/mach-tegra/gpio.c
@@ -212,6 +212,9 @@ static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type)
 	else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
 		__set_irq_handler_unlocked(d->irq, handle_edge_irq);
 
+	if (tegra_get_suspend_mode() == TEGRA_SUSPEND_LP0)
+		tegra_set_lp0_wake_type(d->irq, type);
+
 	return 0;
 }
 
@@ -319,8 +322,17 @@ void tegra_gpio_suspend(void)
 
 static int tegra_gpio_wake_enable(struct irq_data *d, unsigned int enable)
 {
+	int ret;
 	struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d);
-	return set_irq_wake(bank->irq, enable);
+
+	ret = tegra_set_lp1_wake(bank->irq, enable);
+	if (ret)
+		return ret;
+
+	if (tegra_get_suspend_mode() == TEGRA_SUSPEND_LP0)
+		return tegra_set_lp0_wake(d->irq, enable);
+
+	return 0;
 }
 #endif
 
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 18/28] ARM: tegra: add CPU_IDLE driver
  2011-01-24  2:01 [PATCH v2 00/28] Updates for Tegra support in 2.6.39 Colin Cross
@ 2011-01-24  2:01   ` Colin Cross
  2011-01-24  2:01   ` Colin Cross
                     ` (26 subsequent siblings)
  27 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-tegra
  Cc: linux-arm-kernel, olof, konkers, Colin Cross, Gary King,
	Russell King, linux-kernel

Supports clock-gated (LP3) SMP idle mode, and power-gated (LP2) idle.

Latency for LP2 idle state is calculated as a 2-sample weighted moving
average, to allow for future variations due to (e.g.) CPU frequency
scaling.

LP3 idle gates a single CPU core, but LP2 requires power gating both
CPU cores.  An interrupt is used to handshake between the two cores
to ensure both are ready to enter LP2, and another interrupt is used
to manage the process of entering LP2.

Signed-off-by: Gary King <gking@nvidia.com>
Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/Makefile  |    1 +
 arch/arm/mach-tegra/cpuidle.c |  714 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 715 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-tegra/cpuidle.c

diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index 0bf1441..75b1afa 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += headsmp-t2.o
 obj-$(CONFIG_TEGRA_SYSTEM_DMA)		+= dma.o
 obj-$(CONFIG_CPU_FREQ)                  += cpu-tegra.o
 obj-$(CONFIG_TEGRA_PCI)			+= pcie.o
+obj-$(CONFIG_CPU_IDLE)			+= cpuidle.o
 
 obj-${CONFIG_MACH_HARMONY}              += board-harmony.o
 obj-${CONFIG_MACH_HARMONY}              += board-harmony-pinmux.o
diff --git a/arch/arm/mach-tegra/cpuidle.c b/arch/arm/mach-tegra/cpuidle.c
new file mode 100644
index 0000000..232e767
--- /dev/null
+++ b/arch/arm/mach-tegra/cpuidle.c
@@ -0,0 +1,714 @@
+/*
+ * arch/arm/mach-tegra/cpuidle.c
+ *
+ * CPU idle driver for Tegra CPUs
+ *
+ * Copyright (c) 2010, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/cpu.h>
+#include <linux/cpuidle.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/hrtimer.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+#include <linux/suspend.h>
+#include <linux/tick.h>
+
+#include <asm/cacheflush.h>
+#include <asm/hardware/gic.h>
+#include <asm/localtimer.h>
+
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+#include <mach/legacy_irq.h>
+#include <mach/suspend.h>
+
+#include "power.h"
+
+#define TEGRA_CPUIDLE_BOTH_IDLE		INT_QUAD_RES_24
+#define TEGRA_CPUIDLE_TEAR_DOWN		INT_QUAD_RES_25
+
+#define EVP_CPU_RESET_VECTOR \
+	(IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100)
+#define CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET \
+	(IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x340)
+#define CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR \
+	(IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x344)
+#define CLK_RST_CONTROLLER_CLK_CPU_CMPLX \
+	(IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x4c)
+
+static bool lp2_in_idle __read_mostly = true;
+static bool lp2_disabled_by_suspend;
+module_param(lp2_in_idle, bool, 0644);
+
+static s64 tegra_cpu1_idle_time = LLONG_MAX;
+static int tegra_lp2_exit_latency;
+static int tegra_lp2_power_off_time;
+
+static struct {
+	unsigned int cpu_ready_count[2];
+	unsigned long long cpu_wants_lp2_time[2];
+	unsigned long long in_lp2_time;
+	unsigned int both_idle_count;
+	unsigned int tear_down_count;
+	unsigned int lp2_count;
+	unsigned int lp2_completed_count;
+	unsigned int lp2_count_bin[32];
+	unsigned int lp2_completed_count_bin[32];
+	unsigned int lp2_int_count[NR_IRQS];
+	unsigned int last_lp2_int_count[NR_IRQS];
+} idle_stats;
+
+struct cpuidle_driver tegra_idle = {
+	.name = "tegra_idle",
+	.owner = THIS_MODULE,
+};
+
+static DEFINE_PER_CPU(struct cpuidle_device *, idle_devices);
+
+#define FLOW_CTRL_WAITEVENT   (2<<29)
+#define FLOW_CTRL_JTAG_RESUME (1<<28)
+#define FLOW_CTRL_HALT_CPUx_EVENTS(cpu) ((cpu) ? ((cpu - 1) * 0x8 + 0x14) : 0x0)
+
+#define PMC_SCRATCH_38 0x134
+#define PMC_SCRATCH_39 0x138
+
+#define CLK_RESET_CLK_MASK_ARM 0x44
+
+static inline unsigned int time_to_bin(unsigned int time)
+{
+	return fls(time);
+}
+
+static inline void tegra_unmask_irq(int irq)
+{
+	struct irq_chip *chip = get_irq_chip(irq);
+	struct irq_data *data = irq_get_irq_data(irq);
+	chip->irq_unmask(data);
+}
+
+static inline void tegra_mask_irq(int irq)
+{
+	struct irq_chip *chip = get_irq_chip(irq);
+	struct irq_data *data = irq_get_irq_data(irq);
+	chip->irq_mask(data);
+}
+
+static inline int tegra_pending_interrupt(void)
+{
+	void __iomem *gic_cpu = IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100);
+	u32 reg = readl(gic_cpu + 0x18);
+	reg &= 0x3FF;
+
+	return reg;
+}
+
+static inline void tegra_flow_wfi(struct cpuidle_device *dev)
+{
+	void __iomem *flow_ctrl = IO_ADDRESS(TEGRA_FLOW_CTRL_BASE);
+	u32 reg = FLOW_CTRL_WAITEVENT | FLOW_CTRL_JTAG_RESUME;
+
+	flow_ctrl = flow_ctrl + FLOW_CTRL_HALT_CPUx_EVENTS(dev->cpu);
+
+	dsb();
+	__raw_writel(reg, flow_ctrl);
+	reg = __raw_readl(flow_ctrl);
+	__asm__ volatile ("wfi");
+	__raw_writel(0, flow_ctrl);
+	reg = __raw_readl(flow_ctrl);
+}
+
+#ifdef CONFIG_SMP
+static inline bool tegra_wait_for_both_idle(struct cpuidle_device *dev)
+{
+	int wake_int;
+
+	tegra_unmask_irq(TEGRA_CPUIDLE_BOTH_IDLE);
+
+	tegra_flow_wfi(dev);
+
+	wake_int = tegra_pending_interrupt();
+
+	tegra_mask_irq(TEGRA_CPUIDLE_BOTH_IDLE);
+
+	return wake_int == TEGRA_CPUIDLE_BOTH_IDLE &&
+		tegra_pending_interrupt() == 1023;
+}
+
+static inline bool tegra_wait_for_tear_down(struct cpuidle_device *dev)
+{
+	int wake_int;
+	irq_set_affinity(TEGRA_CPUIDLE_TEAR_DOWN, cpumask_of(1));
+	tegra_unmask_irq(TEGRA_CPUIDLE_TEAR_DOWN);
+
+	tegra_flow_wfi(dev);
+
+	wake_int = tegra_pending_interrupt();
+
+	tegra_mask_irq(TEGRA_CPUIDLE_TEAR_DOWN);
+
+	return wake_int == TEGRA_CPUIDLE_TEAR_DOWN &&
+		tegra_pending_interrupt() == 1023;
+}
+
+static inline bool tegra_cpu_in_reset(int cpu)
+{
+	return !!(readl(CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET) & (1 << cpu));
+}
+
+static int tegra_tear_down_cpu1(void)
+{
+	u32 reg;
+
+	/* Signal to CPU1 to tear down */
+	tegra_legacy_force_irq_set(TEGRA_CPUIDLE_TEAR_DOWN);
+
+	/* At this point, CPU0 can no longer abort LP2, but CP1 can */
+	/* TODO: any way not to poll here? Use the LP2 timer to wfi? */
+	/* takes ~80 us */
+	while (!tegra_cpu_in_reset(1) &&
+		tegra_legacy_force_irq_status(TEGRA_CPUIDLE_BOTH_IDLE))
+		cpu_relax();
+
+	tegra_legacy_force_irq_clr(TEGRA_CPUIDLE_TEAR_DOWN);
+
+	/* If CPU1 aborted LP2, restart the process */
+	if (!tegra_legacy_force_irq_status(TEGRA_CPUIDLE_BOTH_IDLE))
+		return -EAGAIN;
+
+	/* CPU1 is ready for LP2, clock gate it */
+	reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+	writel(reg | (1<<9), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+
+	return 0;
+}
+
+static void tegra_wake_cpu1(void)
+{
+	unsigned long boot_vector;
+	unsigned long old_boot_vector;
+	unsigned long timeout;
+	u32 reg;
+
+	boot_vector = virt_to_phys(tegra_hotplug_startup);
+	old_boot_vector = readl(EVP_CPU_RESET_VECTOR);
+	writel(boot_vector, EVP_CPU_RESET_VECTOR);
+
+	/* enable cpu clock on cpu */
+	reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+	writel(reg & ~(1 << (8 + 1)), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+
+	reg = 0x1111 << 1;
+	writel(reg, CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR);
+
+	/* unhalt the cpu */
+	writel(0, IO_ADDRESS(TEGRA_FLOW_CTRL_BASE) + 0x14);
+
+	timeout = jiffies + msecs_to_jiffies(1000);
+	while (time_before(jiffies, timeout)) {
+		if (readl(EVP_CPU_RESET_VECTOR) != boot_vector)
+			break;
+		udelay(10);
+	}
+
+	/* put the old boot vector back */
+	writel(old_boot_vector, EVP_CPU_RESET_VECTOR);
+
+	/* CPU1 is now started */
+}
+#else
+static inline bool tegra_wait_for_both_idle(struct cpuidle_device *dev)
+{
+	return true;
+}
+
+static inline int tegra_tear_down_cpu1(void)
+{
+	return 0;
+}
+
+static inline void tegra_wake_cpu1(void)
+{
+}
+#endif
+
+static void tegra_idle_enter_lp2_cpu0(struct cpuidle_device *dev,
+	struct cpuidle_state *state)
+{
+	s64 request;
+	ktime_t enter;
+	ktime_t exit;
+	bool sleep_completed = false;
+	int bin;
+
+restart:
+	if (!tegra_wait_for_both_idle(dev))
+		return;
+
+	idle_stats.both_idle_count++;
+
+	if (need_resched())
+		return;
+
+	/* CPU1 woke CPU0 because both are idle */
+
+	request = ktime_to_us(tick_nohz_get_sleep_length());
+	if (request < state->target_residency) {
+		/* Not enough time left to enter LP2 */
+		tegra_flow_wfi(dev);
+		return;
+	}
+
+	idle_stats.tear_down_count++;
+
+	if (tegra_tear_down_cpu1())
+		goto restart;
+
+	/* Enter LP2 */
+	request = ktime_to_us(tick_nohz_get_sleep_length());
+	smp_rmb();
+	request = min_t(s64, request, tegra_cpu1_idle_time);
+
+	enter = ktime_get();
+	if (request > state->target_residency) {
+		s64 sleep_time = request - tegra_lp2_exit_latency;
+
+		bin = time_to_bin((u32)request / 1000);
+		idle_stats.lp2_count++;
+		idle_stats.lp2_count_bin[bin]++;
+
+		if (tegra_suspend_lp2(sleep_time) == 0)
+			sleep_completed = true;
+		else
+			idle_stats.lp2_int_count[tegra_pending_interrupt()]++;
+	}
+
+	/* Bring CPU1 out of LP2 */
+	/* TODO: polls for CPU1 to boot, wfi would be better */
+	/* takes ~80 us */
+
+	/* set the reset vector to point to the secondary_startup routine */
+	smp_wmb();
+
+	tegra_wake_cpu1();
+
+	/*
+	 * TODO: is it worth going back to wfi if no interrupt is pending
+	 * and the requested sleep time has not passed?
+	 */
+
+	exit = ktime_get();
+	if (sleep_completed) {
+		/*
+		 * Stayed in LP2 for the full time until the next tick,
+		 * adjust the exit latency based on measurement
+		 */
+		int offset = ktime_to_us(ktime_sub(exit, enter)) - request;
+		int latency = tegra_lp2_exit_latency + offset / 16;
+		latency = clamp(latency, 0, 10000);
+		tegra_lp2_exit_latency = latency;
+		smp_wmb();
+
+		idle_stats.lp2_completed_count++;
+		idle_stats.lp2_completed_count_bin[bin]++;
+		idle_stats.in_lp2_time += ktime_to_us(ktime_sub(exit, enter));
+
+		pr_debug("%lld %lld %d %d\n", request,
+			ktime_to_us(ktime_sub(exit, enter)),
+			offset, bin);
+	}
+}
+
+#ifdef CONFIG_SMP
+static void tegra_idle_enter_lp2_cpu1(struct cpuidle_device *dev,
+	struct cpuidle_state *state)
+{
+	u32 twd_ctrl;
+	u32 twd_load;
+	s64 request;
+
+	tegra_legacy_force_irq_set(TEGRA_CPUIDLE_BOTH_IDLE);
+
+	if (!tegra_wait_for_tear_down(dev))
+		goto out;
+
+	if (need_resched())
+		goto out;
+
+	/*
+	 * CPU1 woke CPU0 because both were idle
+	 * CPU0 responded by waking CPU1 to tell it to disable itself
+	 */
+
+	request = ktime_to_us(tick_nohz_get_sleep_length());
+	if (request < tegra_lp2_exit_latency) {
+		/*
+		 * Not enough time left to enter LP2
+		 * Signal to CPU0 that CPU1 rejects LP2, and stay in
+		 */
+		tegra_legacy_force_irq_clr(TEGRA_CPUIDLE_BOTH_IDLE);
+		tegra_flow_wfi(dev);
+		goto out;
+	}
+
+	tegra_cpu1_idle_time = request;
+	smp_wmb();
+
+	/* Prepare CPU1 for LP2 by putting it in reset */
+
+	gic_cpu_exit(0);
+	barrier();
+	twd_ctrl = readl(twd_base + 0x8);
+	twd_load = readl(twd_base + 0);
+
+	flush_cache_all();
+	barrier();
+	__cortex_a9_save(0);
+	/* CPU1 is in reset, waiting for CPU0 to boot it, possibly after LP2 */
+
+
+	/* CPU0 booted CPU1 out of reset */
+	barrier();
+	cpu_init();
+	writel(twd_ctrl, twd_base + 0x8);
+	writel(twd_load, twd_base + 0);
+	gic_secondary_init(0);
+	tegra_unmask_irq(IRQ_LOCALTIMER);
+
+	tegra_legacy_force_irq_clr(TEGRA_CPUIDLE_BOTH_IDLE);
+
+	writel(smp_processor_id(), EVP_CPU_RESET_VECTOR);
+
+	/*
+	 * TODO: is it worth going back to wfi if no interrupt is pending
+	 * and the requested sleep time has not passed?
+	 */
+
+	return;
+
+out:
+	tegra_legacy_force_irq_clr(TEGRA_CPUIDLE_BOTH_IDLE);
+}
+#endif
+
+static int tegra_idle_enter_lp3(struct cpuidle_device *dev,
+	struct cpuidle_state *state)
+{
+	ktime_t enter, exit;
+	s64 us;
+
+	local_irq_disable();
+	local_fiq_disable();
+
+	enter = ktime_get();
+	if (!need_resched())
+		tegra_flow_wfi(dev);
+	exit = ktime_sub(ktime_get(), enter);
+	us = ktime_to_us(exit);
+
+	local_fiq_enable();
+	local_irq_enable();
+	return (int)us;
+}
+
+static int tegra_idle_enter_lp2(struct cpuidle_device *dev,
+	struct cpuidle_state *state)
+{
+	ktime_t enter, exit;
+	s64 us;
+
+	if (!lp2_in_idle || lp2_disabled_by_suspend)
+		return tegra_idle_enter_lp3(dev, state);
+
+	local_irq_disable();
+	local_fiq_disable();
+	enter = ktime_get();
+
+	idle_stats.cpu_ready_count[dev->cpu]++;
+
+#ifdef CONFIG_SMP
+	if (dev->cpu == 0)
+		tegra_idle_enter_lp2_cpu0(dev, state);
+	else
+		tegra_idle_enter_lp2_cpu1(dev, state);
+#else
+	tegra_idle_enter_lp2_cpu0(dev, state);
+#endif
+
+	exit = ktime_sub(ktime_get(), enter);
+	us = ktime_to_us(exit);
+
+	local_fiq_enable();
+	local_irq_enable();
+
+	/* cpu clockevents may have been reset by powerdown */
+	hrtimer_peek_ahead_timers();
+
+	smp_rmb();
+	state->exit_latency = tegra_lp2_exit_latency;
+	state->target_residency = tegra_lp2_exit_latency +
+		tegra_lp2_power_off_time;
+
+	idle_stats.cpu_wants_lp2_time[dev->cpu] += us;
+
+	return (int)us;
+}
+
+static int tegra_idle_enter(unsigned int cpu)
+{
+	struct cpuidle_device *dev;
+	struct cpuidle_state *state;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	dev->state_count = 0;
+	dev->cpu = cpu;
+
+	tegra_lp2_power_off_time = tegra_cpu_power_off_time();
+
+	state = &dev->states[0];
+	snprintf(state->name, CPUIDLE_NAME_LEN, "LP3");
+	snprintf(state->desc, CPUIDLE_DESC_LEN, "CPU flow-controlled");
+	state->exit_latency = 10;
+	state->target_residency = 10;
+	state->power_usage = 600;
+	state->flags = CPUIDLE_FLAG_TIME_VALID;
+	state->enter = tegra_idle_enter_lp3;
+	dev->safe_state = state;
+	dev->state_count++;
+
+	state = &dev->states[1];
+	snprintf(state->name, CPUIDLE_NAME_LEN, "LP2");
+	snprintf(state->desc, CPUIDLE_DESC_LEN, "CPU power-gate");
+	state->exit_latency = tegra_cpu_power_good_time();
+
+	state->target_residency = tegra_cpu_power_off_time() +
+		tegra_cpu_power_good_time();
+	state->power_usage = 0;
+	state->flags = CPUIDLE_FLAG_TIME_VALID;
+	state->enter = tegra_idle_enter_lp2;
+
+	dev->power_specified = 1;
+	dev->safe_state = state;
+	dev->state_count++;
+
+	if (cpuidle_register_device(dev)) {
+		pr_err("CPU%u: failed to register idle device\n", cpu);
+		kfree(dev);
+		return -EIO;
+	}
+	per_cpu(idle_devices, cpu) = dev;
+	return 0;
+}
+
+/* The IRQs that are used for communication between the cpus to agree on the
+ * cpuidle state should never get handled
+ */
+static irqreturn_t tegra_cpuidle_irq(int irq, void *dev)
+{
+	pr_err("%s: unexpected interrupt %d on cpu %d\n", __func__, irq,
+		smp_processor_id());
+	BUG();
+}
+
+static int tegra_cpuidle_pm_notify(struct notifier_block *nb,
+	unsigned long event, void *dummy)
+{
+	if (event == PM_SUSPEND_PREPARE)
+		lp2_disabled_by_suspend = true;
+	else if (event == PM_POST_SUSPEND)
+		lp2_disabled_by_suspend = false;
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block tegra_cpuidle_pm_notifier = {
+	.notifier_call = tegra_cpuidle_pm_notify,
+};
+
+static int __init tegra_cpuidle_init(void)
+{
+	unsigned int cpu;
+	void __iomem *mask_arm;
+	unsigned int reg;
+	int ret;
+
+	irq_set_affinity(TEGRA_CPUIDLE_BOTH_IDLE, cpumask_of(0));
+	irq_set_affinity(TEGRA_CPUIDLE_TEAR_DOWN, cpumask_of(1));
+
+	ret = request_irq(TEGRA_CPUIDLE_BOTH_IDLE, tegra_cpuidle_irq,
+		IRQF_NOAUTOEN, "tegra_cpuidle_both_idle", NULL);
+	if (ret) {
+		pr_err("%s: Failed to request cpuidle irq\n", __func__);
+		return ret;
+	}
+
+	ret = request_irq(TEGRA_CPUIDLE_TEAR_DOWN, tegra_cpuidle_irq,
+		IRQF_NOAUTOEN, "tegra_cpuidle_tear_down_cpu1", NULL);
+	if (ret) {
+		pr_err("%s: Failed to request cpuidle irq\n", __func__);
+		return ret;
+	}
+
+
+	disable_irq(TEGRA_CPUIDLE_BOTH_IDLE);
+	disable_irq(TEGRA_CPUIDLE_TEAR_DOWN);
+	tegra_mask_irq(TEGRA_CPUIDLE_BOTH_IDLE);
+	tegra_mask_irq(TEGRA_CPUIDLE_TEAR_DOWN);
+
+	mask_arm = IO_ADDRESS(TEGRA_CLK_RESET_BASE) + CLK_RESET_CLK_MASK_ARM;
+
+	reg = readl(mask_arm);
+	writel(reg | (1<<31), mask_arm);
+
+	ret = cpuidle_register_driver(&tegra_idle);
+
+	if (ret)
+		return ret;
+
+	for_each_possible_cpu(cpu) {
+		if (tegra_idle_enter(cpu))
+			pr_err("CPU%u: error initializing idle loop\n", cpu);
+	}
+
+	tegra_lp2_exit_latency = tegra_cpu_power_good_time();
+
+	register_pm_notifier(&tegra_cpuidle_pm_notifier);
+
+	return 0;
+}
+
+static void __exit tegra_cpuidle_exit(void)
+{
+	cpuidle_unregister_driver(&tegra_idle);
+}
+
+module_init(tegra_cpuidle_init);
+module_exit(tegra_cpuidle_exit);
+
+#ifdef CONFIG_DEBUG_FS
+static int tegra_lp2_debug_show(struct seq_file *s, void *data)
+{
+	int bin;
+	int i;
+	seq_printf(s, "                                    cpu0     cpu1\n");
+	seq_printf(s, "-------------------------------------------------\n");
+	seq_printf(s, "cpu ready:                      %8u %8u\n",
+		idle_stats.cpu_ready_count[0],
+		idle_stats.cpu_ready_count[1]);
+	seq_printf(s, "both idle:      %8u        %7u%% %7u%%\n",
+		idle_stats.both_idle_count,
+		idle_stats.both_idle_count * 100 /
+			(idle_stats.cpu_ready_count[0] ?: 1),
+		idle_stats.both_idle_count * 100 /
+			(idle_stats.cpu_ready_count[1] ?: 1));
+	seq_printf(s, "tear down:      %8u %7u%%\n", idle_stats.tear_down_count,
+		idle_stats.tear_down_count * 100 /
+			(idle_stats.both_idle_count ?: 1));
+	seq_printf(s, "lp2:            %8u %7u%%\n", idle_stats.lp2_count,
+		idle_stats.lp2_count * 100 /
+			(idle_stats.both_idle_count ?: 1));
+	seq_printf(s, "lp2 completed:  %8u %7u%%\n",
+		idle_stats.lp2_completed_count,
+		idle_stats.lp2_completed_count * 100 /
+			(idle_stats.lp2_count ?: 1));
+
+	seq_printf(s, "\n");
+	seq_printf(s, "cpu ready time:                 %8llu %8llu ms\n",
+		div64_u64(idle_stats.cpu_wants_lp2_time[0], 1000),
+		div64_u64(idle_stats.cpu_wants_lp2_time[1], 1000));
+	seq_printf(s, "lp2 time:       %8llu ms     %7d%% %7d%%\n",
+		div64_u64(idle_stats.in_lp2_time, 1000),
+		(int)div64_u64(idle_stats.in_lp2_time * 100,
+			idle_stats.cpu_wants_lp2_time[0] ?: 1),
+		(int)div64_u64(idle_stats.in_lp2_time * 100,
+			idle_stats.cpu_wants_lp2_time[1] ?: 1));
+
+	seq_printf(s, "\n");
+	seq_printf(s, "%19s %8s %8s %8s\n", "", "lp2", "comp", "%");
+	seq_printf(s, "-------------------------------------------------\n");
+	for (bin = 0; bin < 32; bin++) {
+		if (idle_stats.lp2_count_bin[bin] == 0)
+			continue;
+		seq_printf(s, "%6u - %6u ms: %8u %8u %7u%%\n",
+			1 << (bin - 1), 1 << bin,
+			idle_stats.lp2_count_bin[bin],
+			idle_stats.lp2_completed_count_bin[bin],
+			idle_stats.lp2_completed_count_bin[bin] * 100 /
+				idle_stats.lp2_count_bin[bin]);
+	}
+
+	seq_printf(s, "\n");
+	seq_printf(s, "%3s %20s %6s %10s\n",
+		"int", "name", "count", "last count");
+	seq_printf(s, "--------------------------------------------\n");
+	for (i = 0; i < NR_IRQS; i++) {
+		if (idle_stats.lp2_int_count[i] == 0)
+			continue;
+		seq_printf(s, "%3d %20s %6d %10d\n",
+			i, irq_to_desc(i)->action ?
+				irq_to_desc(i)->action->name ?: "???" : "???",
+			idle_stats.lp2_int_count[i],
+			idle_stats.lp2_int_count[i] -
+				idle_stats.last_lp2_int_count[i]);
+		idle_stats.last_lp2_int_count[i] = idle_stats.lp2_int_count[i];
+	};
+	return 0;
+}
+
+static int tegra_lp2_debug_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, tegra_lp2_debug_show, inode->i_private);
+}
+
+static const struct file_operations tegra_lp2_debug_ops = {
+	.open		= tegra_lp2_debug_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int __init tegra_cpuidle_debug_init(void)
+{
+	struct dentry *dir;
+	struct dentry *d;
+
+	dir = debugfs_create_dir("cpuidle", NULL);
+	if (!dir)
+		return -ENOMEM;
+
+	d = debugfs_create_file("lp2", S_IRUGO, dir, NULL,
+		&tegra_lp2_debug_ops);
+	if (!d)
+		return -ENOMEM;
+
+	return 0;
+}
+#endif
+
+late_initcall(tegra_cpuidle_debug_init);
-- 
1.7.3.1


^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 18/28] ARM: tegra: add CPU_IDLE driver
@ 2011-01-24  2:01   ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-arm-kernel

Supports clock-gated (LP3) SMP idle mode, and power-gated (LP2) idle.

Latency for LP2 idle state is calculated as a 2-sample weighted moving
average, to allow for future variations due to (e.g.) CPU frequency
scaling.

LP3 idle gates a single CPU core, but LP2 requires power gating both
CPU cores.  An interrupt is used to handshake between the two cores
to ensure both are ready to enter LP2, and another interrupt is used
to manage the process of entering LP2.

Signed-off-by: Gary King <gking@nvidia.com>
Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/Makefile  |    1 +
 arch/arm/mach-tegra/cpuidle.c |  714 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 715 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-tegra/cpuidle.c

diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index 0bf1441..75b1afa 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += headsmp-t2.o
 obj-$(CONFIG_TEGRA_SYSTEM_DMA)		+= dma.o
 obj-$(CONFIG_CPU_FREQ)                  += cpu-tegra.o
 obj-$(CONFIG_TEGRA_PCI)			+= pcie.o
+obj-$(CONFIG_CPU_IDLE)			+= cpuidle.o
 
 obj-${CONFIG_MACH_HARMONY}              += board-harmony.o
 obj-${CONFIG_MACH_HARMONY}              += board-harmony-pinmux.o
diff --git a/arch/arm/mach-tegra/cpuidle.c b/arch/arm/mach-tegra/cpuidle.c
new file mode 100644
index 0000000..232e767
--- /dev/null
+++ b/arch/arm/mach-tegra/cpuidle.c
@@ -0,0 +1,714 @@
+/*
+ * arch/arm/mach-tegra/cpuidle.c
+ *
+ * CPU idle driver for Tegra CPUs
+ *
+ * Copyright (c) 2010, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/cpu.h>
+#include <linux/cpuidle.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/hrtimer.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+#include <linux/suspend.h>
+#include <linux/tick.h>
+
+#include <asm/cacheflush.h>
+#include <asm/hardware/gic.h>
+#include <asm/localtimer.h>
+
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+#include <mach/legacy_irq.h>
+#include <mach/suspend.h>
+
+#include "power.h"
+
+#define TEGRA_CPUIDLE_BOTH_IDLE		INT_QUAD_RES_24
+#define TEGRA_CPUIDLE_TEAR_DOWN		INT_QUAD_RES_25
+
+#define EVP_CPU_RESET_VECTOR \
+	(IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100)
+#define CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET \
+	(IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x340)
+#define CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR \
+	(IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x344)
+#define CLK_RST_CONTROLLER_CLK_CPU_CMPLX \
+	(IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x4c)
+
+static bool lp2_in_idle __read_mostly = true;
+static bool lp2_disabled_by_suspend;
+module_param(lp2_in_idle, bool, 0644);
+
+static s64 tegra_cpu1_idle_time = LLONG_MAX;
+static int tegra_lp2_exit_latency;
+static int tegra_lp2_power_off_time;
+
+static struct {
+	unsigned int cpu_ready_count[2];
+	unsigned long long cpu_wants_lp2_time[2];
+	unsigned long long in_lp2_time;
+	unsigned int both_idle_count;
+	unsigned int tear_down_count;
+	unsigned int lp2_count;
+	unsigned int lp2_completed_count;
+	unsigned int lp2_count_bin[32];
+	unsigned int lp2_completed_count_bin[32];
+	unsigned int lp2_int_count[NR_IRQS];
+	unsigned int last_lp2_int_count[NR_IRQS];
+} idle_stats;
+
+struct cpuidle_driver tegra_idle = {
+	.name = "tegra_idle",
+	.owner = THIS_MODULE,
+};
+
+static DEFINE_PER_CPU(struct cpuidle_device *, idle_devices);
+
+#define FLOW_CTRL_WAITEVENT   (2<<29)
+#define FLOW_CTRL_JTAG_RESUME (1<<28)
+#define FLOW_CTRL_HALT_CPUx_EVENTS(cpu) ((cpu) ? ((cpu - 1) * 0x8 + 0x14) : 0x0)
+
+#define PMC_SCRATCH_38 0x134
+#define PMC_SCRATCH_39 0x138
+
+#define CLK_RESET_CLK_MASK_ARM 0x44
+
+static inline unsigned int time_to_bin(unsigned int time)
+{
+	return fls(time);
+}
+
+static inline void tegra_unmask_irq(int irq)
+{
+	struct irq_chip *chip = get_irq_chip(irq);
+	struct irq_data *data = irq_get_irq_data(irq);
+	chip->irq_unmask(data);
+}
+
+static inline void tegra_mask_irq(int irq)
+{
+	struct irq_chip *chip = get_irq_chip(irq);
+	struct irq_data *data = irq_get_irq_data(irq);
+	chip->irq_mask(data);
+}
+
+static inline int tegra_pending_interrupt(void)
+{
+	void __iomem *gic_cpu = IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100);
+	u32 reg = readl(gic_cpu + 0x18);
+	reg &= 0x3FF;
+
+	return reg;
+}
+
+static inline void tegra_flow_wfi(struct cpuidle_device *dev)
+{
+	void __iomem *flow_ctrl = IO_ADDRESS(TEGRA_FLOW_CTRL_BASE);
+	u32 reg = FLOW_CTRL_WAITEVENT | FLOW_CTRL_JTAG_RESUME;
+
+	flow_ctrl = flow_ctrl + FLOW_CTRL_HALT_CPUx_EVENTS(dev->cpu);
+
+	dsb();
+	__raw_writel(reg, flow_ctrl);
+	reg = __raw_readl(flow_ctrl);
+	__asm__ volatile ("wfi");
+	__raw_writel(0, flow_ctrl);
+	reg = __raw_readl(flow_ctrl);
+}
+
+#ifdef CONFIG_SMP
+static inline bool tegra_wait_for_both_idle(struct cpuidle_device *dev)
+{
+	int wake_int;
+
+	tegra_unmask_irq(TEGRA_CPUIDLE_BOTH_IDLE);
+
+	tegra_flow_wfi(dev);
+
+	wake_int = tegra_pending_interrupt();
+
+	tegra_mask_irq(TEGRA_CPUIDLE_BOTH_IDLE);
+
+	return wake_int == TEGRA_CPUIDLE_BOTH_IDLE &&
+		tegra_pending_interrupt() == 1023;
+}
+
+static inline bool tegra_wait_for_tear_down(struct cpuidle_device *dev)
+{
+	int wake_int;
+	irq_set_affinity(TEGRA_CPUIDLE_TEAR_DOWN, cpumask_of(1));
+	tegra_unmask_irq(TEGRA_CPUIDLE_TEAR_DOWN);
+
+	tegra_flow_wfi(dev);
+
+	wake_int = tegra_pending_interrupt();
+
+	tegra_mask_irq(TEGRA_CPUIDLE_TEAR_DOWN);
+
+	return wake_int == TEGRA_CPUIDLE_TEAR_DOWN &&
+		tegra_pending_interrupt() == 1023;
+}
+
+static inline bool tegra_cpu_in_reset(int cpu)
+{
+	return !!(readl(CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET) & (1 << cpu));
+}
+
+static int tegra_tear_down_cpu1(void)
+{
+	u32 reg;
+
+	/* Signal to CPU1 to tear down */
+	tegra_legacy_force_irq_set(TEGRA_CPUIDLE_TEAR_DOWN);
+
+	/* At this point, CPU0 can no longer abort LP2, but CP1 can */
+	/* TODO: any way not to poll here? Use the LP2 timer to wfi? */
+	/* takes ~80 us */
+	while (!tegra_cpu_in_reset(1) &&
+		tegra_legacy_force_irq_status(TEGRA_CPUIDLE_BOTH_IDLE))
+		cpu_relax();
+
+	tegra_legacy_force_irq_clr(TEGRA_CPUIDLE_TEAR_DOWN);
+
+	/* If CPU1 aborted LP2, restart the process */
+	if (!tegra_legacy_force_irq_status(TEGRA_CPUIDLE_BOTH_IDLE))
+		return -EAGAIN;
+
+	/* CPU1 is ready for LP2, clock gate it */
+	reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+	writel(reg | (1<<9), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+
+	return 0;
+}
+
+static void tegra_wake_cpu1(void)
+{
+	unsigned long boot_vector;
+	unsigned long old_boot_vector;
+	unsigned long timeout;
+	u32 reg;
+
+	boot_vector = virt_to_phys(tegra_hotplug_startup);
+	old_boot_vector = readl(EVP_CPU_RESET_VECTOR);
+	writel(boot_vector, EVP_CPU_RESET_VECTOR);
+
+	/* enable cpu clock on cpu */
+	reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+	writel(reg & ~(1 << (8 + 1)), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+
+	reg = 0x1111 << 1;
+	writel(reg, CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR);
+
+	/* unhalt the cpu */
+	writel(0, IO_ADDRESS(TEGRA_FLOW_CTRL_BASE) + 0x14);
+
+	timeout = jiffies + msecs_to_jiffies(1000);
+	while (time_before(jiffies, timeout)) {
+		if (readl(EVP_CPU_RESET_VECTOR) != boot_vector)
+			break;
+		udelay(10);
+	}
+
+	/* put the old boot vector back */
+	writel(old_boot_vector, EVP_CPU_RESET_VECTOR);
+
+	/* CPU1 is now started */
+}
+#else
+static inline bool tegra_wait_for_both_idle(struct cpuidle_device *dev)
+{
+	return true;
+}
+
+static inline int tegra_tear_down_cpu1(void)
+{
+	return 0;
+}
+
+static inline void tegra_wake_cpu1(void)
+{
+}
+#endif
+
+static void tegra_idle_enter_lp2_cpu0(struct cpuidle_device *dev,
+	struct cpuidle_state *state)
+{
+	s64 request;
+	ktime_t enter;
+	ktime_t exit;
+	bool sleep_completed = false;
+	int bin;
+
+restart:
+	if (!tegra_wait_for_both_idle(dev))
+		return;
+
+	idle_stats.both_idle_count++;
+
+	if (need_resched())
+		return;
+
+	/* CPU1 woke CPU0 because both are idle */
+
+	request = ktime_to_us(tick_nohz_get_sleep_length());
+	if (request < state->target_residency) {
+		/* Not enough time left to enter LP2 */
+		tegra_flow_wfi(dev);
+		return;
+	}
+
+	idle_stats.tear_down_count++;
+
+	if (tegra_tear_down_cpu1())
+		goto restart;
+
+	/* Enter LP2 */
+	request = ktime_to_us(tick_nohz_get_sleep_length());
+	smp_rmb();
+	request = min_t(s64, request, tegra_cpu1_idle_time);
+
+	enter = ktime_get();
+	if (request > state->target_residency) {
+		s64 sleep_time = request - tegra_lp2_exit_latency;
+
+		bin = time_to_bin((u32)request / 1000);
+		idle_stats.lp2_count++;
+		idle_stats.lp2_count_bin[bin]++;
+
+		if (tegra_suspend_lp2(sleep_time) == 0)
+			sleep_completed = true;
+		else
+			idle_stats.lp2_int_count[tegra_pending_interrupt()]++;
+	}
+
+	/* Bring CPU1 out of LP2 */
+	/* TODO: polls for CPU1 to boot, wfi would be better */
+	/* takes ~80 us */
+
+	/* set the reset vector to point to the secondary_startup routine */
+	smp_wmb();
+
+	tegra_wake_cpu1();
+
+	/*
+	 * TODO: is it worth going back to wfi if no interrupt is pending
+	 * and the requested sleep time has not passed?
+	 */
+
+	exit = ktime_get();
+	if (sleep_completed) {
+		/*
+		 * Stayed in LP2 for the full time until the next tick,
+		 * adjust the exit latency based on measurement
+		 */
+		int offset = ktime_to_us(ktime_sub(exit, enter)) - request;
+		int latency = tegra_lp2_exit_latency + offset / 16;
+		latency = clamp(latency, 0, 10000);
+		tegra_lp2_exit_latency = latency;
+		smp_wmb();
+
+		idle_stats.lp2_completed_count++;
+		idle_stats.lp2_completed_count_bin[bin]++;
+		idle_stats.in_lp2_time += ktime_to_us(ktime_sub(exit, enter));
+
+		pr_debug("%lld %lld %d %d\n", request,
+			ktime_to_us(ktime_sub(exit, enter)),
+			offset, bin);
+	}
+}
+
+#ifdef CONFIG_SMP
+static void tegra_idle_enter_lp2_cpu1(struct cpuidle_device *dev,
+	struct cpuidle_state *state)
+{
+	u32 twd_ctrl;
+	u32 twd_load;
+	s64 request;
+
+	tegra_legacy_force_irq_set(TEGRA_CPUIDLE_BOTH_IDLE);
+
+	if (!tegra_wait_for_tear_down(dev))
+		goto out;
+
+	if (need_resched())
+		goto out;
+
+	/*
+	 * CPU1 woke CPU0 because both were idle
+	 * CPU0 responded by waking CPU1 to tell it to disable itself
+	 */
+
+	request = ktime_to_us(tick_nohz_get_sleep_length());
+	if (request < tegra_lp2_exit_latency) {
+		/*
+		 * Not enough time left to enter LP2
+		 * Signal to CPU0 that CPU1 rejects LP2, and stay in
+		 */
+		tegra_legacy_force_irq_clr(TEGRA_CPUIDLE_BOTH_IDLE);
+		tegra_flow_wfi(dev);
+		goto out;
+	}
+
+	tegra_cpu1_idle_time = request;
+	smp_wmb();
+
+	/* Prepare CPU1 for LP2 by putting it in reset */
+
+	gic_cpu_exit(0);
+	barrier();
+	twd_ctrl = readl(twd_base + 0x8);
+	twd_load = readl(twd_base + 0);
+
+	flush_cache_all();
+	barrier();
+	__cortex_a9_save(0);
+	/* CPU1 is in reset, waiting for CPU0 to boot it, possibly after LP2 */
+
+
+	/* CPU0 booted CPU1 out of reset */
+	barrier();
+	cpu_init();
+	writel(twd_ctrl, twd_base + 0x8);
+	writel(twd_load, twd_base + 0);
+	gic_secondary_init(0);
+	tegra_unmask_irq(IRQ_LOCALTIMER);
+
+	tegra_legacy_force_irq_clr(TEGRA_CPUIDLE_BOTH_IDLE);
+
+	writel(smp_processor_id(), EVP_CPU_RESET_VECTOR);
+
+	/*
+	 * TODO: is it worth going back to wfi if no interrupt is pending
+	 * and the requested sleep time has not passed?
+	 */
+
+	return;
+
+out:
+	tegra_legacy_force_irq_clr(TEGRA_CPUIDLE_BOTH_IDLE);
+}
+#endif
+
+static int tegra_idle_enter_lp3(struct cpuidle_device *dev,
+	struct cpuidle_state *state)
+{
+	ktime_t enter, exit;
+	s64 us;
+
+	local_irq_disable();
+	local_fiq_disable();
+
+	enter = ktime_get();
+	if (!need_resched())
+		tegra_flow_wfi(dev);
+	exit = ktime_sub(ktime_get(), enter);
+	us = ktime_to_us(exit);
+
+	local_fiq_enable();
+	local_irq_enable();
+	return (int)us;
+}
+
+static int tegra_idle_enter_lp2(struct cpuidle_device *dev,
+	struct cpuidle_state *state)
+{
+	ktime_t enter, exit;
+	s64 us;
+
+	if (!lp2_in_idle || lp2_disabled_by_suspend)
+		return tegra_idle_enter_lp3(dev, state);
+
+	local_irq_disable();
+	local_fiq_disable();
+	enter = ktime_get();
+
+	idle_stats.cpu_ready_count[dev->cpu]++;
+
+#ifdef CONFIG_SMP
+	if (dev->cpu == 0)
+		tegra_idle_enter_lp2_cpu0(dev, state);
+	else
+		tegra_idle_enter_lp2_cpu1(dev, state);
+#else
+	tegra_idle_enter_lp2_cpu0(dev, state);
+#endif
+
+	exit = ktime_sub(ktime_get(), enter);
+	us = ktime_to_us(exit);
+
+	local_fiq_enable();
+	local_irq_enable();
+
+	/* cpu clockevents may have been reset by powerdown */
+	hrtimer_peek_ahead_timers();
+
+	smp_rmb();
+	state->exit_latency = tegra_lp2_exit_latency;
+	state->target_residency = tegra_lp2_exit_latency +
+		tegra_lp2_power_off_time;
+
+	idle_stats.cpu_wants_lp2_time[dev->cpu] += us;
+
+	return (int)us;
+}
+
+static int tegra_idle_enter(unsigned int cpu)
+{
+	struct cpuidle_device *dev;
+	struct cpuidle_state *state;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	dev->state_count = 0;
+	dev->cpu = cpu;
+
+	tegra_lp2_power_off_time = tegra_cpu_power_off_time();
+
+	state = &dev->states[0];
+	snprintf(state->name, CPUIDLE_NAME_LEN, "LP3");
+	snprintf(state->desc, CPUIDLE_DESC_LEN, "CPU flow-controlled");
+	state->exit_latency = 10;
+	state->target_residency = 10;
+	state->power_usage = 600;
+	state->flags = CPUIDLE_FLAG_TIME_VALID;
+	state->enter = tegra_idle_enter_lp3;
+	dev->safe_state = state;
+	dev->state_count++;
+
+	state = &dev->states[1];
+	snprintf(state->name, CPUIDLE_NAME_LEN, "LP2");
+	snprintf(state->desc, CPUIDLE_DESC_LEN, "CPU power-gate");
+	state->exit_latency = tegra_cpu_power_good_time();
+
+	state->target_residency = tegra_cpu_power_off_time() +
+		tegra_cpu_power_good_time();
+	state->power_usage = 0;
+	state->flags = CPUIDLE_FLAG_TIME_VALID;
+	state->enter = tegra_idle_enter_lp2;
+
+	dev->power_specified = 1;
+	dev->safe_state = state;
+	dev->state_count++;
+
+	if (cpuidle_register_device(dev)) {
+		pr_err("CPU%u: failed to register idle device\n", cpu);
+		kfree(dev);
+		return -EIO;
+	}
+	per_cpu(idle_devices, cpu) = dev;
+	return 0;
+}
+
+/* The IRQs that are used for communication between the cpus to agree on the
+ * cpuidle state should never get handled
+ */
+static irqreturn_t tegra_cpuidle_irq(int irq, void *dev)
+{
+	pr_err("%s: unexpected interrupt %d on cpu %d\n", __func__, irq,
+		smp_processor_id());
+	BUG();
+}
+
+static int tegra_cpuidle_pm_notify(struct notifier_block *nb,
+	unsigned long event, void *dummy)
+{
+	if (event == PM_SUSPEND_PREPARE)
+		lp2_disabled_by_suspend = true;
+	else if (event == PM_POST_SUSPEND)
+		lp2_disabled_by_suspend = false;
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block tegra_cpuidle_pm_notifier = {
+	.notifier_call = tegra_cpuidle_pm_notify,
+};
+
+static int __init tegra_cpuidle_init(void)
+{
+	unsigned int cpu;
+	void __iomem *mask_arm;
+	unsigned int reg;
+	int ret;
+
+	irq_set_affinity(TEGRA_CPUIDLE_BOTH_IDLE, cpumask_of(0));
+	irq_set_affinity(TEGRA_CPUIDLE_TEAR_DOWN, cpumask_of(1));
+
+	ret = request_irq(TEGRA_CPUIDLE_BOTH_IDLE, tegra_cpuidle_irq,
+		IRQF_NOAUTOEN, "tegra_cpuidle_both_idle", NULL);
+	if (ret) {
+		pr_err("%s: Failed to request cpuidle irq\n", __func__);
+		return ret;
+	}
+
+	ret = request_irq(TEGRA_CPUIDLE_TEAR_DOWN, tegra_cpuidle_irq,
+		IRQF_NOAUTOEN, "tegra_cpuidle_tear_down_cpu1", NULL);
+	if (ret) {
+		pr_err("%s: Failed to request cpuidle irq\n", __func__);
+		return ret;
+	}
+
+
+	disable_irq(TEGRA_CPUIDLE_BOTH_IDLE);
+	disable_irq(TEGRA_CPUIDLE_TEAR_DOWN);
+	tegra_mask_irq(TEGRA_CPUIDLE_BOTH_IDLE);
+	tegra_mask_irq(TEGRA_CPUIDLE_TEAR_DOWN);
+
+	mask_arm = IO_ADDRESS(TEGRA_CLK_RESET_BASE) + CLK_RESET_CLK_MASK_ARM;
+
+	reg = readl(mask_arm);
+	writel(reg | (1<<31), mask_arm);
+
+	ret = cpuidle_register_driver(&tegra_idle);
+
+	if (ret)
+		return ret;
+
+	for_each_possible_cpu(cpu) {
+		if (tegra_idle_enter(cpu))
+			pr_err("CPU%u: error initializing idle loop\n", cpu);
+	}
+
+	tegra_lp2_exit_latency = tegra_cpu_power_good_time();
+
+	register_pm_notifier(&tegra_cpuidle_pm_notifier);
+
+	return 0;
+}
+
+static void __exit tegra_cpuidle_exit(void)
+{
+	cpuidle_unregister_driver(&tegra_idle);
+}
+
+module_init(tegra_cpuidle_init);
+module_exit(tegra_cpuidle_exit);
+
+#ifdef CONFIG_DEBUG_FS
+static int tegra_lp2_debug_show(struct seq_file *s, void *data)
+{
+	int bin;
+	int i;
+	seq_printf(s, "                                    cpu0     cpu1\n");
+	seq_printf(s, "-------------------------------------------------\n");
+	seq_printf(s, "cpu ready:                      %8u %8u\n",
+		idle_stats.cpu_ready_count[0],
+		idle_stats.cpu_ready_count[1]);
+	seq_printf(s, "both idle:      %8u        %7u%% %7u%%\n",
+		idle_stats.both_idle_count,
+		idle_stats.both_idle_count * 100 /
+			(idle_stats.cpu_ready_count[0] ?: 1),
+		idle_stats.both_idle_count * 100 /
+			(idle_stats.cpu_ready_count[1] ?: 1));
+	seq_printf(s, "tear down:      %8u %7u%%\n", idle_stats.tear_down_count,
+		idle_stats.tear_down_count * 100 /
+			(idle_stats.both_idle_count ?: 1));
+	seq_printf(s, "lp2:            %8u %7u%%\n", idle_stats.lp2_count,
+		idle_stats.lp2_count * 100 /
+			(idle_stats.both_idle_count ?: 1));
+	seq_printf(s, "lp2 completed:  %8u %7u%%\n",
+		idle_stats.lp2_completed_count,
+		idle_stats.lp2_completed_count * 100 /
+			(idle_stats.lp2_count ?: 1));
+
+	seq_printf(s, "\n");
+	seq_printf(s, "cpu ready time:                 %8llu %8llu ms\n",
+		div64_u64(idle_stats.cpu_wants_lp2_time[0], 1000),
+		div64_u64(idle_stats.cpu_wants_lp2_time[1], 1000));
+	seq_printf(s, "lp2 time:       %8llu ms     %7d%% %7d%%\n",
+		div64_u64(idle_stats.in_lp2_time, 1000),
+		(int)div64_u64(idle_stats.in_lp2_time * 100,
+			idle_stats.cpu_wants_lp2_time[0] ?: 1),
+		(int)div64_u64(idle_stats.in_lp2_time * 100,
+			idle_stats.cpu_wants_lp2_time[1] ?: 1));
+
+	seq_printf(s, "\n");
+	seq_printf(s, "%19s %8s %8s %8s\n", "", "lp2", "comp", "%");
+	seq_printf(s, "-------------------------------------------------\n");
+	for (bin = 0; bin < 32; bin++) {
+		if (idle_stats.lp2_count_bin[bin] == 0)
+			continue;
+		seq_printf(s, "%6u - %6u ms: %8u %8u %7u%%\n",
+			1 << (bin - 1), 1 << bin,
+			idle_stats.lp2_count_bin[bin],
+			idle_stats.lp2_completed_count_bin[bin],
+			idle_stats.lp2_completed_count_bin[bin] * 100 /
+				idle_stats.lp2_count_bin[bin]);
+	}
+
+	seq_printf(s, "\n");
+	seq_printf(s, "%3s %20s %6s %10s\n",
+		"int", "name", "count", "last count");
+	seq_printf(s, "--------------------------------------------\n");
+	for (i = 0; i < NR_IRQS; i++) {
+		if (idle_stats.lp2_int_count[i] == 0)
+			continue;
+		seq_printf(s, "%3d %20s %6d %10d\n",
+			i, irq_to_desc(i)->action ?
+				irq_to_desc(i)->action->name ?: "???" : "???",
+			idle_stats.lp2_int_count[i],
+			idle_stats.lp2_int_count[i] -
+				idle_stats.last_lp2_int_count[i]);
+		idle_stats.last_lp2_int_count[i] = idle_stats.lp2_int_count[i];
+	};
+	return 0;
+}
+
+static int tegra_lp2_debug_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, tegra_lp2_debug_show, inode->i_private);
+}
+
+static const struct file_operations tegra_lp2_debug_ops = {
+	.open		= tegra_lp2_debug_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int __init tegra_cpuidle_debug_init(void)
+{
+	struct dentry *dir;
+	struct dentry *d;
+
+	dir = debugfs_create_dir("cpuidle", NULL);
+	if (!dir)
+		return -ENOMEM;
+
+	d = debugfs_create_file("lp2", S_IRUGO, dir, NULL,
+		&tegra_lp2_debug_ops);
+	if (!d)
+		return -ENOMEM;
+
+	return 0;
+}
+#endif
+
+late_initcall(tegra_cpuidle_debug_init);
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 19/28] ARM: tegra: iomap: Add missing devices, fix use of SZ_8, SZ_64
  2011-01-24  2:01 [PATCH v2 00/28] Updates for Tegra support in 2.6.39 Colin Cross
@ 2011-01-24  2:01   ` Colin Cross
  2011-01-24  2:01   ` Colin Cross
                     ` (26 subsequent siblings)
  27 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-tegra
  Cc: linux-arm-kernel, olof, konkers, Gary King, Colin Cross,
	Russell King, linux-kernel

From: Gary King <gking@nvidia.com>

Adds gart, hdmi, avp, host1x, and pwm controllers to mach/iomap.h
There is no SZ_8 or SZ_64, replace them with constants

Signed-off-by: Gary King <gking@nvidia.com>
Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/include/mach/iomap.h |   55 ++++++++++++++++++++++++------
 1 files changed, 44 insertions(+), 11 deletions(-)

diff --git a/arch/arm/mach-tegra/include/mach/iomap.h b/arch/arm/mach-tegra/include/mach/iomap.h
index 325eca3..26f2363 100644
--- a/arch/arm/mach-tegra/include/mach/iomap.h
+++ b/arch/arm/mach-tegra/include/mach/iomap.h
@@ -26,6 +26,9 @@
 #define TEGRA_IRAM_BASE			0x40000000
 #define TEGRA_IRAM_SIZE			SZ_256K
 
+#define TEGRA_HOST1X_BASE		0x50000000
+#define TEGRA_HOST1X_SIZE		0x24000
+
 #define TEGRA_ARM_PERIF_BASE		0x50040000
 #define TEGRA_ARM_PERIF_SIZE		SZ_8K
 
@@ -35,38 +38,56 @@
 #define TEGRA_ARM_INT_DIST_BASE		0x50041000
 #define TEGRA_ARM_INT_DIST_SIZE		SZ_4K
 
+#define TEGRA_MPE_BASE			0x54040000
+#define TEGRA_MPE_SIZE			SZ_256K
+
+#define TEGRA_VI_BASE			0x54080000
+#define TEGRA_VI_SIZE			SZ_256K
+
+#define TEGRA_ISP_BASE			0x54100000
+#define TEGRA_ISP_SIZE			SZ_256K
+
 #define TEGRA_DISPLAY_BASE		0x54200000
 #define TEGRA_DISPLAY_SIZE		SZ_256K
 
 #define TEGRA_DISPLAY2_BASE		0x54240000
 #define TEGRA_DISPLAY2_SIZE		SZ_256K
 
+#define TEGRA_HDMI_BASE			0x54280000
+#define TEGRA_HDMI_SIZE			SZ_256K
+
+#define TEGRA_GART_BASE			0x58000000
+#define TEGRA_GART_SIZE			SZ_32M
+
+#define TEGRA_RES_SEMA_BASE		0x60001000
+#define TEGRA_RES_SEMA_SIZE		SZ_4K
+
 #define TEGRA_PRIMARY_ICTLR_BASE	0x60004000
-#define TEGRA_PRIMARY_ICTLR_SIZE	SZ_64
+#define TEGRA_PRIMARY_ICTLR_SIZE	64
 
 #define TEGRA_SECONDARY_ICTLR_BASE	0x60004100
-#define TEGRA_SECONDARY_ICTLR_SIZE	SZ_64
+#define TEGRA_SECONDARY_ICTLR_SIZE	64
 
 #define TEGRA_TERTIARY_ICTLR_BASE	0x60004200
-#define TEGRA_TERTIARY_ICTLR_SIZE	SZ_64
+#define TEGRA_TERTIARY_ICTLR_SIZE	64
 
 #define TEGRA_QUATERNARY_ICTLR_BASE	0x60004300
-#define TEGRA_QUATERNARY_ICTLR_SIZE	SZ_64
+#define TEGRA_QUATERNARY_ICTLR_SIZE	64
 
 #define TEGRA_TMR1_BASE			0x60005000
-#define TEGRA_TMR1_SIZE			SZ_8
+#define TEGRA_TMR1_SIZE			8
 
 #define TEGRA_TMR2_BASE			0x60005008
-#define TEGRA_TMR2_SIZE			SZ_8
+#define TEGRA_TMR2_SIZE			8
 
 #define TEGRA_TMRUS_BASE		0x60005010
-#define TEGRA_TMRUS_SIZE		SZ_64
+#define TEGRA_TMRUS_SIZE		64
 
 #define TEGRA_TMR3_BASE			0x60005050
-#define TEGRA_TMR3_SIZE			SZ_8
+#define TEGRA_TMR3_SIZE			8
 
 #define TEGRA_TMR4_BASE			0x60005058
-#define TEGRA_TMR4_SIZE			SZ_8
+#define TEGRA_TMR4_SIZE			8
 
 #define TEGRA_CLK_RESET_BASE		0x60006000
 #define TEGRA_CLK_RESET_SIZE		SZ_4K
@@ -114,10 +135,10 @@
 #define TEGRA_I2S2_SIZE			SZ_256
 
 #define TEGRA_UARTA_BASE		0x70006000
-#define TEGRA_UARTA_SIZE		SZ_64
+#define TEGRA_UARTA_SIZE		64
 
 #define TEGRA_UARTB_BASE		0x70006040
-#define TEGRA_UARTB_SIZE		SZ_64
+#define TEGRA_UARTB_SIZE		64
 
 #define TEGRA_UARTC_BASE		0x70006200
 #define TEGRA_UARTC_SIZE		SZ_256
@@ -140,6 +161,18 @@
 #define TEGRA_PWFM_BASE			0x7000A000
 #define TEGRA_PWFM_SIZE			SZ_256
 
+#define TEGRA_PWFM0_BASE		0x7000A000
+#define TEGRA_PWFM0_SIZE		4
+
+#define TEGRA_PWFM1_BASE		0x7000A010
+#define TEGRA_PWFM1_SIZE		4
+
+#define TEGRA_PWFM2_BASE		0x7000A020
+#define TEGRA_PWFM2_SIZE		4
+
+#define TEGRA_PWFM3_BASE		0x7000A030
+#define TEGRA_PWFM3_SIZE		4
+
 #define TEGRA_MIPI_BASE			0x7000B000
 #define TEGRA_MIPI_SIZE			SZ_256
 
-- 
1.7.3.1


^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 19/28] ARM: tegra: iomap: Add missing devices, fix use of SZ_8, SZ_64
@ 2011-01-24  2:01   ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-arm-kernel

From: Gary King <gking@nvidia.com>

Adds gart, hdmi, avp, host1x, and pwm controllers to mach/iomap.h
There is no SZ_8 or SZ_64, replace them with constants

Signed-off-by: Gary King <gking@nvidia.com>
Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/include/mach/iomap.h |   55 ++++++++++++++++++++++++------
 1 files changed, 44 insertions(+), 11 deletions(-)

diff --git a/arch/arm/mach-tegra/include/mach/iomap.h b/arch/arm/mach-tegra/include/mach/iomap.h
index 325eca3..26f2363 100644
--- a/arch/arm/mach-tegra/include/mach/iomap.h
+++ b/arch/arm/mach-tegra/include/mach/iomap.h
@@ -26,6 +26,9 @@
 #define TEGRA_IRAM_BASE			0x40000000
 #define TEGRA_IRAM_SIZE			SZ_256K
 
+#define TEGRA_HOST1X_BASE		0x50000000
+#define TEGRA_HOST1X_SIZE		0x24000
+
 #define TEGRA_ARM_PERIF_BASE		0x50040000
 #define TEGRA_ARM_PERIF_SIZE		SZ_8K
 
@@ -35,38 +38,56 @@
 #define TEGRA_ARM_INT_DIST_BASE		0x50041000
 #define TEGRA_ARM_INT_DIST_SIZE		SZ_4K
 
+#define TEGRA_MPE_BASE			0x54040000
+#define TEGRA_MPE_SIZE			SZ_256K
+
+#define TEGRA_VI_BASE			0x54080000
+#define TEGRA_VI_SIZE			SZ_256K
+
+#define TEGRA_ISP_BASE			0x54100000
+#define TEGRA_ISP_SIZE			SZ_256K
+
 #define TEGRA_DISPLAY_BASE		0x54200000
 #define TEGRA_DISPLAY_SIZE		SZ_256K
 
 #define TEGRA_DISPLAY2_BASE		0x54240000
 #define TEGRA_DISPLAY2_SIZE		SZ_256K
 
+#define TEGRA_HDMI_BASE			0x54280000
+#define TEGRA_HDMI_SIZE			SZ_256K
+
+#define TEGRA_GART_BASE			0x58000000
+#define TEGRA_GART_SIZE			SZ_32M
+
+#define TEGRA_RES_SEMA_BASE		0x60001000
+#define TEGRA_RES_SEMA_SIZE		SZ_4K
+
 #define TEGRA_PRIMARY_ICTLR_BASE	0x60004000
-#define TEGRA_PRIMARY_ICTLR_SIZE	SZ_64
+#define TEGRA_PRIMARY_ICTLR_SIZE	64
 
 #define TEGRA_SECONDARY_ICTLR_BASE	0x60004100
-#define TEGRA_SECONDARY_ICTLR_SIZE	SZ_64
+#define TEGRA_SECONDARY_ICTLR_SIZE	64
 
 #define TEGRA_TERTIARY_ICTLR_BASE	0x60004200
-#define TEGRA_TERTIARY_ICTLR_SIZE	SZ_64
+#define TEGRA_TERTIARY_ICTLR_SIZE	64
 
 #define TEGRA_QUATERNARY_ICTLR_BASE	0x60004300
-#define TEGRA_QUATERNARY_ICTLR_SIZE	SZ_64
+#define TEGRA_QUATERNARY_ICTLR_SIZE	64
 
 #define TEGRA_TMR1_BASE			0x60005000
-#define TEGRA_TMR1_SIZE			SZ_8
+#define TEGRA_TMR1_SIZE			8
 
 #define TEGRA_TMR2_BASE			0x60005008
-#define TEGRA_TMR2_SIZE			SZ_8
+#define TEGRA_TMR2_SIZE			8
 
 #define TEGRA_TMRUS_BASE		0x60005010
-#define TEGRA_TMRUS_SIZE		SZ_64
+#define TEGRA_TMRUS_SIZE		64
 
 #define TEGRA_TMR3_BASE			0x60005050
-#define TEGRA_TMR3_SIZE			SZ_8
+#define TEGRA_TMR3_SIZE			8
 
 #define TEGRA_TMR4_BASE			0x60005058
-#define TEGRA_TMR4_SIZE			SZ_8
+#define TEGRA_TMR4_SIZE			8
 
 #define TEGRA_CLK_RESET_BASE		0x60006000
 #define TEGRA_CLK_RESET_SIZE		SZ_4K
@@ -114,10 +135,10 @@
 #define TEGRA_I2S2_SIZE			SZ_256
 
 #define TEGRA_UARTA_BASE		0x70006000
-#define TEGRA_UARTA_SIZE		SZ_64
+#define TEGRA_UARTA_SIZE		64
 
 #define TEGRA_UARTB_BASE		0x70006040
-#define TEGRA_UARTB_SIZE		SZ_64
+#define TEGRA_UARTB_SIZE		64
 
 #define TEGRA_UARTC_BASE		0x70006200
 #define TEGRA_UARTC_SIZE		SZ_256
@@ -140,6 +161,18 @@
 #define TEGRA_PWFM_BASE			0x7000A000
 #define TEGRA_PWFM_SIZE			SZ_256
 
+#define TEGRA_PWFM0_BASE		0x7000A000
+#define TEGRA_PWFM0_SIZE		4
+
+#define TEGRA_PWFM1_BASE		0x7000A010
+#define TEGRA_PWFM1_SIZE		4
+
+#define TEGRA_PWFM2_BASE		0x7000A020
+#define TEGRA_PWFM2_SIZE		4
+
+#define TEGRA_PWFM3_BASE		0x7000A030
+#define TEGRA_PWFM3_SIZE		4
+
 #define TEGRA_MIPI_BASE			0x7000B000
 #define TEGRA_MIPI_SIZE			SZ_256
 
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 20/28] ARM: tegra: cpufreq: Disable cpufreq during suspend
  2011-01-24  2:01 [PATCH v2 00/28] Updates for Tegra support in 2.6.39 Colin Cross
@ 2011-01-24  2:01   ` Colin Cross
  2011-01-24  2:01   ` Colin Cross
                     ` (26 subsequent siblings)
  27 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-tegra
  Cc: linux-arm-kernel, olof, konkers, Colin Cross, Russell King, linux-kernel

Adds a SUSPEND_PREPARE notification hook to drop the frequency to
the lowest possible during suspend.  This prevents the cpufreq driver
from attempting regulator calls after suspend has started - the
regulator api can call into drivers that have already been suspended.
Also adds 216MHz (off of PLLP) as the lowest CPU frequency, which
allows PLLX to be turned off.

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/cpu-tegra.c |   75 +++++++++++++++++++++++++++++++--------
 1 files changed, 60 insertions(+), 15 deletions(-)

diff --git a/arch/arm/mach-tegra/cpu-tegra.c b/arch/arm/mach-tegra/cpu-tegra.c
index fea5719..ad26a9f 100644
--- a/arch/arm/mach-tegra/cpu-tegra.c
+++ b/arch/arm/mach-tegra/cpu-tegra.c
@@ -28,6 +28,7 @@
 #include <linux/err.h>
 #include <linux/clk.h>
 #include <linux/io.h>
+#include <linux/suspend.h>
 
 #include <asm/system.h>
 
@@ -36,14 +37,15 @@
 
 /* Frequency table index must be sequential starting at 0 */
 static struct cpufreq_frequency_table freq_table[] = {
-	{ 0, 312000 },
-	{ 1, 456000 },
-	{ 2, 608000 },
-	{ 3, 760000 },
-	{ 4, 816000 },
-	{ 5, 912000 },
-	{ 6, 1000000 },
-	{ 7, CPUFREQ_TABLE_END },
+	{ 0, 216000 },
+	{ 1, 312000 },
+	{ 2, 456000 },
+	{ 3, 608000 },
+	{ 4, 760000 },
+	{ 5, 816000 },
+	{ 6, 912000 },
+	{ 7, 1000000 },
+	{ 8, CPUFREQ_TABLE_END },
 };
 
 #define NUM_CPUS	2
@@ -51,6 +53,8 @@ static struct cpufreq_frequency_table freq_table[] = {
 static struct clk *cpu_clk;
 
 static unsigned long target_cpu_speed[NUM_CPUS];
+static DEFINE_MUTEX(tegra_cpu_lock);
+static bool is_suspended;
 
 int tegra_verify_speed(struct cpufreq_policy *policy)
 {
@@ -68,16 +72,11 @@ unsigned int tegra_getspeed(unsigned int cpu)
 	return rate;
 }
 
-static int tegra_update_cpu_speed(void)
+static int tegra_update_cpu_speed(unsigned long rate)
 {
-	int i;
-	unsigned long rate = 0;
 	int ret = 0;
 	struct cpufreq_freqs freqs;
 
-	for_each_online_cpu(i)
-		rate = max(rate, target_cpu_speed[i]);
-
 	freqs.old = tegra_getspeed(0);
 	freqs.new = rate;
 
@@ -105,12 +104,30 @@ static int tegra_update_cpu_speed(void)
 	return 0;
 }
 
+static unsigned long tegra_cpu_highest_speed(void)
+{
+	unsigned long rate = 0;
+	int i;
+
+	for_each_online_cpu(i)
+		rate = max(rate, target_cpu_speed[i]);
+	return rate;
+}
+
 static int tegra_target(struct cpufreq_policy *policy,
 		       unsigned int target_freq,
 		       unsigned int relation)
 {
 	int idx;
 	unsigned int freq;
+	int ret = 0;
+
+	mutex_lock(&tegra_cpu_lock);
+
+	if (is_suspended) {
+		ret = -EBUSY;
+		goto out;
+	}
 
 	cpufreq_frequency_table_target(policy, freq_table, target_freq,
 		relation, &idx);
@@ -119,9 +136,34 @@ static int tegra_target(struct cpufreq_policy *policy,
 
 	target_cpu_speed[policy->cpu] = freq;
 
-	return tegra_update_cpu_speed();
+	ret = tegra_update_cpu_speed(tegra_cpu_highest_speed());
+
+out:
+	mutex_unlock(&tegra_cpu_lock);
+	return ret;
 }
 
+static int tegra_pm_notify(struct notifier_block *nb, unsigned long event,
+	void *dummy)
+{
+	mutex_lock(&tegra_cpu_lock);
+	if (event == PM_SUSPEND_PREPARE) {
+		is_suspended = true;
+		pr_info("Tegra cpufreq suspend: setting frequency to %d kHz\n",
+			freq_table[0].frequency);
+		tegra_update_cpu_speed(freq_table[0].frequency);
+	} else if (event == PM_POST_SUSPEND) {
+		is_suspended = false;
+	}
+	mutex_unlock(&tegra_cpu_lock);
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block tegra_cpu_pm_notifier = {
+	.notifier_call = tegra_pm_notify,
+};
+
 static int tegra_cpu_init(struct cpufreq_policy *policy)
 {
 	if (policy->cpu >= NUM_CPUS)
@@ -142,6 +184,9 @@ static int tegra_cpu_init(struct cpufreq_policy *policy)
 	policy->shared_type = CPUFREQ_SHARED_TYPE_ALL;
 	cpumask_copy(policy->related_cpus, cpu_possible_mask);
 
+	if (policy->cpu == 0)
+		register_pm_notifier(&tegra_cpu_pm_notifier);
+
 	return 0;
 }
 
-- 
1.7.3.1


^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 20/28] ARM: tegra: cpufreq: Disable cpufreq during suspend
@ 2011-01-24  2:01   ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-arm-kernel

Adds a SUSPEND_PREPARE notification hook to drop the frequency to
the lowest possible during suspend.  This prevents the cpufreq driver
from attempting regulator calls after suspend has started - the
regulator api can call into drivers that have already been suspended.
Also adds 216MHz (off of PLLP) as the lowest CPU frequency, which
allows PLLX to be turned off.

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/cpu-tegra.c |   75 +++++++++++++++++++++++++++++++--------
 1 files changed, 60 insertions(+), 15 deletions(-)

diff --git a/arch/arm/mach-tegra/cpu-tegra.c b/arch/arm/mach-tegra/cpu-tegra.c
index fea5719..ad26a9f 100644
--- a/arch/arm/mach-tegra/cpu-tegra.c
+++ b/arch/arm/mach-tegra/cpu-tegra.c
@@ -28,6 +28,7 @@
 #include <linux/err.h>
 #include <linux/clk.h>
 #include <linux/io.h>
+#include <linux/suspend.h>
 
 #include <asm/system.h>
 
@@ -36,14 +37,15 @@
 
 /* Frequency table index must be sequential starting at 0 */
 static struct cpufreq_frequency_table freq_table[] = {
-	{ 0, 312000 },
-	{ 1, 456000 },
-	{ 2, 608000 },
-	{ 3, 760000 },
-	{ 4, 816000 },
-	{ 5, 912000 },
-	{ 6, 1000000 },
-	{ 7, CPUFREQ_TABLE_END },
+	{ 0, 216000 },
+	{ 1, 312000 },
+	{ 2, 456000 },
+	{ 3, 608000 },
+	{ 4, 760000 },
+	{ 5, 816000 },
+	{ 6, 912000 },
+	{ 7, 1000000 },
+	{ 8, CPUFREQ_TABLE_END },
 };
 
 #define NUM_CPUS	2
@@ -51,6 +53,8 @@ static struct cpufreq_frequency_table freq_table[] = {
 static struct clk *cpu_clk;
 
 static unsigned long target_cpu_speed[NUM_CPUS];
+static DEFINE_MUTEX(tegra_cpu_lock);
+static bool is_suspended;
 
 int tegra_verify_speed(struct cpufreq_policy *policy)
 {
@@ -68,16 +72,11 @@ unsigned int tegra_getspeed(unsigned int cpu)
 	return rate;
 }
 
-static int tegra_update_cpu_speed(void)
+static int tegra_update_cpu_speed(unsigned long rate)
 {
-	int i;
-	unsigned long rate = 0;
 	int ret = 0;
 	struct cpufreq_freqs freqs;
 
-	for_each_online_cpu(i)
-		rate = max(rate, target_cpu_speed[i]);
-
 	freqs.old = tegra_getspeed(0);
 	freqs.new = rate;
 
@@ -105,12 +104,30 @@ static int tegra_update_cpu_speed(void)
 	return 0;
 }
 
+static unsigned long tegra_cpu_highest_speed(void)
+{
+	unsigned long rate = 0;
+	int i;
+
+	for_each_online_cpu(i)
+		rate = max(rate, target_cpu_speed[i]);
+	return rate;
+}
+
 static int tegra_target(struct cpufreq_policy *policy,
 		       unsigned int target_freq,
 		       unsigned int relation)
 {
 	int idx;
 	unsigned int freq;
+	int ret = 0;
+
+	mutex_lock(&tegra_cpu_lock);
+
+	if (is_suspended) {
+		ret = -EBUSY;
+		goto out;
+	}
 
 	cpufreq_frequency_table_target(policy, freq_table, target_freq,
 		relation, &idx);
@@ -119,9 +136,34 @@ static int tegra_target(struct cpufreq_policy *policy,
 
 	target_cpu_speed[policy->cpu] = freq;
 
-	return tegra_update_cpu_speed();
+	ret = tegra_update_cpu_speed(tegra_cpu_highest_speed());
+
+out:
+	mutex_unlock(&tegra_cpu_lock);
+	return ret;
 }
 
+static int tegra_pm_notify(struct notifier_block *nb, unsigned long event,
+	void *dummy)
+{
+	mutex_lock(&tegra_cpu_lock);
+	if (event == PM_SUSPEND_PREPARE) {
+		is_suspended = true;
+		pr_info("Tegra cpufreq suspend: setting frequency to %d kHz\n",
+			freq_table[0].frequency);
+		tegra_update_cpu_speed(freq_table[0].frequency);
+	} else if (event == PM_POST_SUSPEND) {
+		is_suspended = false;
+	}
+	mutex_unlock(&tegra_cpu_lock);
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block tegra_cpu_pm_notifier = {
+	.notifier_call = tegra_pm_notify,
+};
+
 static int tegra_cpu_init(struct cpufreq_policy *policy)
 {
 	if (policy->cpu >= NUM_CPUS)
@@ -142,6 +184,9 @@ static int tegra_cpu_init(struct cpufreq_policy *policy)
 	policy->shared_type = CPUFREQ_SHARED_TYPE_ALL;
 	cpumask_copy(policy->related_cpus, cpu_possible_mask);
 
+	if (policy->cpu == 0)
+		register_pm_notifier(&tegra_cpu_pm_notifier);
+
 	return 0;
 }
 
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 21/28] ARM: tegra: Allow overriding arch_reset
  2011-01-24  2:01 [PATCH v2 00/28] Updates for Tegra support in 2.6.39 Colin Cross
@ 2011-01-24  2:01   ` Colin Cross
  2011-01-24  2:01   ` Colin Cross
                     ` (26 subsequent siblings)
  27 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-tegra
  Cc: linux-arm-kernel, olof, konkers, Colin Cross, linux, linux-kernel

Signed-off-by: Colin Cross <ccross@android.com>
---
v2: Move definition of tegra_assert_system_reset out of static
    inline

 arch/arm/mach-tegra/board.h               |    2 ++
 arch/arm/mach-tegra/common.c              |   13 +++++++++++++
 arch/arm/mach-tegra/include/mach/system.h |   10 ++--------
 3 files changed, 17 insertions(+), 8 deletions(-)

diff --git a/arch/arm/mach-tegra/board.h b/arch/arm/mach-tegra/board.h
index ae5c333..15d561b 100644
--- a/arch/arm/mach-tegra/board.h
+++ b/arch/arm/mach-tegra/board.h
@@ -23,6 +23,8 @@
 
 #include <linux/types.h>
 
+void tegra_assert_system_reset(char mode, const char *cmd);
+
 void __init tegra_common_init(void);
 void __init tegra_map_common_io(void);
 void __init tegra_init_irq(void);
diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c
index 53ddf55..1b32495 100644
--- a/arch/arm/mach-tegra/common.c
+++ b/arch/arm/mach-tegra/common.c
@@ -27,6 +27,7 @@
 
 #include <mach/iomap.h>
 #include <mach/dma.h>
+#include <mach/system.h>
 
 #include "board.h"
 #include "clock.h"
@@ -36,6 +37,18 @@
 unsigned long tegra_lp0_vec_start;
 unsigned long tegra_lp0_vec_size;
 
+void (*arch_reset)(char mode, const char *cmd) = tegra_assert_system_reset;
+
+void tegra_assert_system_reset(char mode, const char *cmd)
+{
+	void __iomem *reset = IO_ADDRESS(TEGRA_CLK_RESET_BASE + 0x04);
+	u32 reg;
+
+	reg = readl(reset);
+	reg |= 0x04;
+	writel(reg, reset);
+}
+
 static __initdata struct tegra_clk_init_table common_clk_init_table[] = {
 	/* name		parent		rate		enabled */
 	{ "clk_m",	NULL,		0,		true },
diff --git a/arch/arm/mach-tegra/include/mach/system.h b/arch/arm/mach-tegra/include/mach/system.h
index 84d5d46..d0183d8 100644
--- a/arch/arm/mach-tegra/include/mach/system.h
+++ b/arch/arm/mach-tegra/include/mach/system.h
@@ -24,16 +24,10 @@
 #include <mach/hardware.h>
 #include <mach/iomap.h>
 
-static inline void arch_idle(void)
-{
-}
+extern void (*arch_reset)(char mode, const char *cmd);
 
-static inline void arch_reset(char mode, const char *cmd)
+static inline void arch_idle(void)
 {
-	void __iomem *reset = IO_ADDRESS(TEGRA_CLK_RESET_BASE + 0x04);
-	u32 reg = readl(reset);
-	reg |= 0x04;
-	writel(reg, reset);
 }
 
 #endif
-- 
1.7.3.1


^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 21/28] ARM: tegra: Allow overriding arch_reset
@ 2011-01-24  2:01   ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Colin Cross <ccross@android.com>
---
v2: Move definition of tegra_assert_system_reset out of static
    inline

 arch/arm/mach-tegra/board.h               |    2 ++
 arch/arm/mach-tegra/common.c              |   13 +++++++++++++
 arch/arm/mach-tegra/include/mach/system.h |   10 ++--------
 3 files changed, 17 insertions(+), 8 deletions(-)

diff --git a/arch/arm/mach-tegra/board.h b/arch/arm/mach-tegra/board.h
index ae5c333..15d561b 100644
--- a/arch/arm/mach-tegra/board.h
+++ b/arch/arm/mach-tegra/board.h
@@ -23,6 +23,8 @@
 
 #include <linux/types.h>
 
+void tegra_assert_system_reset(char mode, const char *cmd);
+
 void __init tegra_common_init(void);
 void __init tegra_map_common_io(void);
 void __init tegra_init_irq(void);
diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c
index 53ddf55..1b32495 100644
--- a/arch/arm/mach-tegra/common.c
+++ b/arch/arm/mach-tegra/common.c
@@ -27,6 +27,7 @@
 
 #include <mach/iomap.h>
 #include <mach/dma.h>
+#include <mach/system.h>
 
 #include "board.h"
 #include "clock.h"
@@ -36,6 +37,18 @@
 unsigned long tegra_lp0_vec_start;
 unsigned long tegra_lp0_vec_size;
 
+void (*arch_reset)(char mode, const char *cmd) = tegra_assert_system_reset;
+
+void tegra_assert_system_reset(char mode, const char *cmd)
+{
+	void __iomem *reset = IO_ADDRESS(TEGRA_CLK_RESET_BASE + 0x04);
+	u32 reg;
+
+	reg = readl(reset);
+	reg |= 0x04;
+	writel(reg, reset);
+}
+
 static __initdata struct tegra_clk_init_table common_clk_init_table[] = {
 	/* name		parent		rate		enabled */
 	{ "clk_m",	NULL,		0,		true },
diff --git a/arch/arm/mach-tegra/include/mach/system.h b/arch/arm/mach-tegra/include/mach/system.h
index 84d5d46..d0183d8 100644
--- a/arch/arm/mach-tegra/include/mach/system.h
+++ b/arch/arm/mach-tegra/include/mach/system.h
@@ -24,16 +24,10 @@
 #include <mach/hardware.h>
 #include <mach/iomap.h>
 
-static inline void arch_idle(void)
-{
-}
+extern void (*arch_reset)(char mode, const char *cmd);
 
-static inline void arch_reset(char mode, const char *cmd)
+static inline void arch_idle(void)
 {
-	void __iomem *reset = IO_ADDRESS(TEGRA_CLK_RESET_BASE + 0x04);
-	u32 reg = readl(reset);
-	reg |= 0x04;
-	writel(reg, reset);
 }
 
 #endif
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 22/28] ARM: tegra: dma: Fix critical data corruption bugs
  2011-01-24  2:01 [PATCH v2 00/28] Updates for Tegra support in 2.6.39 Colin Cross
@ 2011-01-24  2:01   ` Colin Cross
  2011-01-24  2:01   ` Colin Cross
                     ` (26 subsequent siblings)
  27 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-tegra
  Cc: linux-arm-kernel, olof, konkers, Colin Cross, Russell King, linux-kernel

Sometimes, due to high interrupt latency in the continuous mode
of DMA transfer, the half buffer complete interrupt is handled
after DMA has transferred the full buffer.  When this is detected,
stop DMA immediately and restart with the next buffer if the next
buffer is ready.

originally fixed by Victor(Weiguo) Pan <wpan@nvidia.com>

In place of using the simple spin_lock()/spi_unlock() in the
interrupt thread, using the spin_lock_irqsave() and
spin_unlock_irqrestore(). The lock is shared between the normal
process context and interrupt context.

originally fixed by Laxman Dewangan (ldewangan@nvidia.com)

The use of shadow registers caused memory corruption at physical
address 0 because the enable bit was not shadowed, and assuming it
needed to be set would enable an unconfigured dma block.  Most of the
register accesses don't need to know the previous state of the
registers, and the few places that do need to modify only a few bits
in the registers are the same ones that were sometimes incorrectly
setting the enable bit.  This patch convert tegra_dma_update_hardware
to set the entire register, and the other users to read-modify-write,
and drops the shadow registers completely.

Also fixes missing locking in tegra_dma_allocate_channel

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/dma.c |  197 +++++++++++++++++++++++++--------------------
 1 files changed, 108 insertions(+), 89 deletions(-)

diff --git a/arch/arm/mach-tegra/dma.c b/arch/arm/mach-tegra/dma.c
index a2a252d..250bc7b 100644
--- a/arch/arm/mach-tegra/dma.c
+++ b/arch/arm/mach-tegra/dma.c
@@ -121,17 +121,13 @@ struct tegra_dma_channel {
 	void  __iomem		*addr;
 	int			mode;
 	int			irq;
-
-	/* Register shadow */
-	u32			csr;
-	u32			ahb_seq;
-	u32			ahb_ptr;
-	u32			apb_seq;
-	u32			apb_ptr;
+	int			req_transfer_count;
 };
 
 #define  NV_DMA_MAX_CHANNELS  32
 
+static DEFINE_MUTEX(tegra_dma_lock);
+
 static DECLARE_BITMAP(channel_usage, NV_DMA_MAX_CHANNELS);
 static struct tegra_dma_channel dma_channels[NV_DMA_MAX_CHANNELS];
 
@@ -139,7 +135,6 @@ static void tegra_dma_update_hw(struct tegra_dma_channel *ch,
 	struct tegra_dma_req *req);
 static void tegra_dma_update_hw_partial(struct tegra_dma_channel *ch,
 	struct tegra_dma_req *req);
-static void tegra_dma_init_hw(struct tegra_dma_channel *ch);
 static void tegra_dma_stop(struct tegra_dma_channel *ch);
 
 void tegra_dma_flush(struct tegra_dma_channel *ch)
@@ -151,6 +146,9 @@ void tegra_dma_dequeue(struct tegra_dma_channel *ch)
 {
 	struct tegra_dma_req *req;
 
+	if (tegra_dma_is_empty(ch))
+		return;
+
 	req = list_entry(ch->list.next, typeof(*req), node);
 
 	tegra_dma_dequeue_req(ch, req);
@@ -159,10 +157,10 @@ void tegra_dma_dequeue(struct tegra_dma_channel *ch)
 
 void tegra_dma_stop(struct tegra_dma_channel *ch)
 {
-	unsigned int csr;
-	unsigned int status;
+	u32 csr;
+	u32 status;
 
-	csr = ch->csr;
+	csr = readl(ch->addr + APB_DMA_CHAN_CSR);
 	csr &= ~CSR_IE_EOC;
 	writel(csr, ch->addr + APB_DMA_CHAN_CSR);
 
@@ -176,19 +174,16 @@ void tegra_dma_stop(struct tegra_dma_channel *ch)
 
 int tegra_dma_cancel(struct tegra_dma_channel *ch)
 {
-	unsigned int csr;
+	u32 csr;
 	unsigned long irq_flags;
 
 	spin_lock_irqsave(&ch->lock, irq_flags);
 	while (!list_empty(&ch->list))
 		list_del(ch->list.next);
 
-	csr = ch->csr;
+	csr = readl(ch->addr + APB_DMA_CHAN_CSR);
 	csr &= ~CSR_REQ_SEL_MASK;
 	csr |= CSR_REQ_SEL_INVALID;
-
-	/* Set the enable as that is not shadowed */
-	csr |= CSR_ENB;
 	writel(csr, ch->addr + APB_DMA_CHAN_CSR);
 
 	tegra_dma_stop(ch);
@@ -230,18 +225,15 @@ int tegra_dma_dequeue_req(struct tegra_dma_channel *ch,
 	 *  - Finally stop or program the DMA to the next buffer in the
 	 *    list.
 	 */
-	csr = ch->csr;
+	csr = readl(ch->addr + APB_DMA_CHAN_CSR);
 	csr &= ~CSR_REQ_SEL_MASK;
 	csr |= CSR_REQ_SEL_INVALID;
-
-	/* Set the enable as that is not shadowed */
-	csr |= CSR_ENB;
 	writel(csr, ch->addr + APB_DMA_CHAN_CSR);
 
 	/* Get the transfer count */
 	status = readl(ch->addr + APB_DMA_CHAN_STA);
 	to_transfer = (status & STA_COUNT_MASK) >> STA_COUNT_SHIFT;
-	req_transfer_count = (ch->csr & CSR_WCOUNT_MASK) >> CSR_WCOUNT_SHIFT;
+	req_transfer_count = ch->req_transfer_count;
 	req_transfer_count += 1;
 	to_transfer += 1;
 
@@ -349,7 +341,9 @@ EXPORT_SYMBOL(tegra_dma_enqueue_req);
 struct tegra_dma_channel *tegra_dma_allocate_channel(int mode)
 {
 	int channel;
-	struct tegra_dma_channel *ch;
+	struct tegra_dma_channel *ch = NULL;
+
+	mutex_lock(&tegra_dma_lock);
 
 	/* first channel is the shared channel */
 	if (mode & TEGRA_DMA_SHARED) {
@@ -358,11 +352,14 @@ struct tegra_dma_channel *tegra_dma_allocate_channel(int mode)
 		channel = find_first_zero_bit(channel_usage,
 			ARRAY_SIZE(dma_channels));
 		if (channel >= ARRAY_SIZE(dma_channels))
-			return NULL;
+			goto out;
 	}
 	__set_bit(channel, channel_usage);
 	ch = &dma_channels[channel];
 	ch->mode = mode;
+
+out:
+	mutex_unlock(&tegra_dma_lock);
 	return ch;
 }
 EXPORT_SYMBOL(tegra_dma_allocate_channel);
@@ -372,22 +369,27 @@ void tegra_dma_free_channel(struct tegra_dma_channel *ch)
 	if (ch->mode & TEGRA_DMA_SHARED)
 		return;
 	tegra_dma_cancel(ch);
+	mutex_lock(&tegra_dma_lock);
 	__clear_bit(ch->id, channel_usage);
+	mutex_unlock(&tegra_dma_lock);
 }
 EXPORT_SYMBOL(tegra_dma_free_channel);
 
 static void tegra_dma_update_hw_partial(struct tegra_dma_channel *ch,
 	struct tegra_dma_req *req)
 {
+	u32 apb_ptr;
+	u32 ahb_ptr;
+
 	if (req->to_memory) {
-		ch->apb_ptr = req->source_addr;
-		ch->ahb_ptr = req->dest_addr;
+		apb_ptr = req->source_addr;
+		ahb_ptr = req->dest_addr;
 	} else {
-		ch->apb_ptr = req->dest_addr;
-		ch->ahb_ptr = req->source_addr;
+		apb_ptr = req->dest_addr;
+		ahb_ptr = req->source_addr;
 	}
-	writel(ch->apb_ptr, ch->addr + APB_DMA_CHAN_APB_PTR);
-	writel(ch->ahb_ptr, ch->addr + APB_DMA_CHAN_AHB_PTR);
+	writel(apb_ptr, ch->addr + APB_DMA_CHAN_APB_PTR);
+	writel(ahb_ptr, ch->addr + APB_DMA_CHAN_AHB_PTR);
 
 	req->status = TEGRA_DMA_REQ_INFLIGHT;
 	return;
@@ -401,38 +403,39 @@ static void tegra_dma_update_hw(struct tegra_dma_channel *ch,
 	int ahb_bus_width;
 	int apb_bus_width;
 	int index;
-	unsigned long csr;
 
+	u32 ahb_seq;
+	u32 apb_seq;
+	u32 ahb_ptr;
+	u32 apb_ptr;
+	u32 csr;
+
+	csr = CSR_IE_EOC | CSR_FLOW;
+	ahb_seq = AHB_SEQ_INTR_ENB | AHB_SEQ_BURST_1;
+	apb_seq = 0;
 
-	ch->csr |= CSR_FLOW;
-	ch->csr &= ~CSR_REQ_SEL_MASK;
-	ch->csr |= req->req_sel << CSR_REQ_SEL_SHIFT;
-	ch->ahb_seq &= ~AHB_SEQ_BURST_MASK;
-	ch->ahb_seq |= AHB_SEQ_BURST_1;
+	csr |= req->req_sel << CSR_REQ_SEL_SHIFT;
 
 	/* One shot mode is always single buffered,
 	 * continuous mode is always double buffered
 	 * */
 	if (ch->mode & TEGRA_DMA_MODE_ONESHOT) {
-		ch->csr |= CSR_ONCE;
-		ch->ahb_seq &= ~AHB_SEQ_DBL_BUF;
-		ch->csr &= ~CSR_WCOUNT_MASK;
-		ch->csr |= ((req->size>>2) - 1) << CSR_WCOUNT_SHIFT;
+		csr |= CSR_ONCE;
+		ch->req_transfer_count = (req->size >> 2) - 1;
 	} else {
-		ch->csr &= ~CSR_ONCE;
-		ch->ahb_seq |= AHB_SEQ_DBL_BUF;
+		ahb_seq |= AHB_SEQ_DBL_BUF;
 
 		/* In double buffered mode, we set the size to half the
 		 * requested size and interrupt when half the buffer
 		 * is full */
-		ch->csr &= ~CSR_WCOUNT_MASK;
-		ch->csr |= ((req->size>>3) - 1) << CSR_WCOUNT_SHIFT;
+		ch->req_transfer_count = (req->size >> 3) - 1;
 	}
 
+	csr |= ch->req_transfer_count << CSR_WCOUNT_SHIFT;
+
 	if (req->to_memory) {
-		ch->csr &= ~CSR_DIR;
-		ch->apb_ptr = req->source_addr;
-		ch->ahb_ptr = req->dest_addr;
+		apb_ptr = req->source_addr;
+		ahb_ptr = req->dest_addr;
 
 		apb_addr_wrap = req->source_wrap;
 		ahb_addr_wrap = req->dest_wrap;
@@ -440,9 +443,9 @@ static void tegra_dma_update_hw(struct tegra_dma_channel *ch,
 		ahb_bus_width = req->dest_bus_width;
 
 	} else {
-		ch->csr |= CSR_DIR;
-		ch->apb_ptr = req->dest_addr;
-		ch->ahb_ptr = req->source_addr;
+		csr |= CSR_DIR;
+		apb_ptr = req->dest_addr;
+		ahb_ptr = req->source_addr;
 
 		apb_addr_wrap = req->dest_wrap;
 		ahb_addr_wrap = req->source_wrap;
@@ -461,8 +464,7 @@ static void tegra_dma_update_hw(struct tegra_dma_channel *ch,
 		index++;
 	} while (index < ARRAY_SIZE(apb_addr_wrap_table));
 	BUG_ON(index == ARRAY_SIZE(apb_addr_wrap_table));
-	ch->apb_seq &= ~APB_SEQ_WRAP_MASK;
-	ch->apb_seq |= index << APB_SEQ_WRAP_SHIFT;
+	apb_seq |= index << APB_SEQ_WRAP_SHIFT;
 
 	/* set address wrap for AHB size */
 	index = 0;
@@ -472,55 +474,42 @@ static void tegra_dma_update_hw(struct tegra_dma_channel *ch,
 		index++;
 	} while (index < ARRAY_SIZE(ahb_addr_wrap_table));
 	BUG_ON(index == ARRAY_SIZE(ahb_addr_wrap_table));
-	ch->ahb_seq &= ~AHB_SEQ_WRAP_MASK;
-	ch->ahb_seq |= index << AHB_SEQ_WRAP_SHIFT;
+	ahb_seq |= index << AHB_SEQ_WRAP_SHIFT;
 
 	for (index = 0; index < ARRAY_SIZE(bus_width_table); index++) {
 		if (bus_width_table[index] == ahb_bus_width)
 			break;
 	}
 	BUG_ON(index == ARRAY_SIZE(bus_width_table));
-	ch->ahb_seq &= ~AHB_SEQ_BUS_WIDTH_MASK;
-	ch->ahb_seq |= index << AHB_SEQ_BUS_WIDTH_SHIFT;
+	ahb_seq |= index << AHB_SEQ_BUS_WIDTH_SHIFT;
 
 	for (index = 0; index < ARRAY_SIZE(bus_width_table); index++) {
 		if (bus_width_table[index] == apb_bus_width)
 			break;
 	}
 	BUG_ON(index == ARRAY_SIZE(bus_width_table));
-	ch->apb_seq &= ~APB_SEQ_BUS_WIDTH_MASK;
-	ch->apb_seq |= index << APB_SEQ_BUS_WIDTH_SHIFT;
-
-	ch->csr |= CSR_IE_EOC;
+	apb_seq |= index << APB_SEQ_BUS_WIDTH_SHIFT;
 
-	/* update hw registers with the shadow */
-	writel(ch->csr, ch->addr + APB_DMA_CHAN_CSR);
-	writel(ch->apb_seq, ch->addr + APB_DMA_CHAN_APB_SEQ);
-	writel(ch->apb_ptr, ch->addr + APB_DMA_CHAN_APB_PTR);
-	writel(ch->ahb_seq, ch->addr + APB_DMA_CHAN_AHB_SEQ);
-	writel(ch->ahb_ptr, ch->addr + APB_DMA_CHAN_AHB_PTR);
+	writel(csr, ch->addr + APB_DMA_CHAN_CSR);
+	writel(apb_seq, ch->addr + APB_DMA_CHAN_APB_SEQ);
+	writel(apb_ptr, ch->addr + APB_DMA_CHAN_APB_PTR);
+	writel(ahb_seq, ch->addr + APB_DMA_CHAN_AHB_SEQ);
+	writel(ahb_ptr, ch->addr + APB_DMA_CHAN_AHB_PTR);
 
-	csr = ch->csr | CSR_ENB;
+	csr |= CSR_ENB;
 	writel(csr, ch->addr + APB_DMA_CHAN_CSR);
 
 	req->status = TEGRA_DMA_REQ_INFLIGHT;
 }
 
-static void tegra_dma_init_hw(struct tegra_dma_channel *ch)
-{
-	/* One shot with an interrupt to CPU after transfer */
-	ch->csr = CSR_ONCE | CSR_IE_EOC;
-	ch->ahb_seq = AHB_SEQ_BUS_WIDTH_32 | AHB_SEQ_INTR_ENB;
-	ch->apb_seq = APB_SEQ_BUS_WIDTH_32 | 1 << APB_SEQ_WRAP_SHIFT;
-}
-
 static void handle_oneshot_dma(struct tegra_dma_channel *ch)
 {
 	struct tegra_dma_req *req;
+	unsigned long irq_flags;
 
-	spin_lock(&ch->lock);
+	spin_lock_irqsave(&ch->lock, irq_flags);
 	if (list_empty(&ch->list)) {
-		spin_unlock(&ch->lock);
+		spin_unlock_irqrestore(&ch->lock, irq_flags);
 		return;
 	}
 
@@ -528,8 +517,7 @@ static void handle_oneshot_dma(struct tegra_dma_channel *ch)
 	if (req) {
 		int bytes_transferred;
 
-		bytes_transferred =
-			(ch->csr & CSR_WCOUNT_MASK) >> CSR_WCOUNT_SHIFT;
+		bytes_transferred = ch->req_transfer_count;
 		bytes_transferred += 1;
 		bytes_transferred <<= 2;
 
@@ -537,12 +525,12 @@ static void handle_oneshot_dma(struct tegra_dma_channel *ch)
 		req->bytes_transferred = bytes_transferred;
 		req->status = TEGRA_DMA_REQ_SUCCESS;
 
-		spin_unlock(&ch->lock);
+		spin_unlock_irqrestore(&ch->lock, irq_flags);
 		/* Callback should be called without any lock */
 		pr_debug("%s: transferred %d bytes\n", __func__,
 			req->bytes_transferred);
 		req->complete(req);
-		spin_lock(&ch->lock);
+		spin_lock_irqsave(&ch->lock, irq_flags);
 	}
 
 	if (!list_empty(&ch->list)) {
@@ -552,22 +540,55 @@ static void handle_oneshot_dma(struct tegra_dma_channel *ch)
 		if (req->status != TEGRA_DMA_REQ_INFLIGHT)
 			tegra_dma_update_hw(ch, req);
 	}
-	spin_unlock(&ch->lock);
+	spin_unlock_irqrestore(&ch->lock, irq_flags);
 }
 
 static void handle_continuous_dma(struct tegra_dma_channel *ch)
 {
 	struct tegra_dma_req *req;
+	unsigned long irq_flags;
 
-	spin_lock(&ch->lock);
+	spin_lock_irqsave(&ch->lock, irq_flags);
 	if (list_empty(&ch->list)) {
-		spin_unlock(&ch->lock);
+		spin_unlock_irqrestore(&ch->lock, irq_flags);
 		return;
 	}
 
 	req = list_entry(ch->list.next, typeof(*req), node);
 	if (req) {
 		if (req->buffer_status == TEGRA_DMA_REQ_BUF_STATUS_EMPTY) {
+			bool is_dma_ping_complete;
+			is_dma_ping_complete = (readl(ch->addr + APB_DMA_CHAN_STA)
+						& STA_PING_PONG) ? true : false;
+			if (req->to_memory)
+				is_dma_ping_complete = !is_dma_ping_complete;
+			/* Out of sync - Release current buffer */
+			if (!is_dma_ping_complete) {
+				int bytes_transferred;
+
+				bytes_transferred = ch->req_transfer_count;
+				bytes_transferred += 1;
+				bytes_transferred <<= 3;
+				req->buffer_status = TEGRA_DMA_REQ_BUF_STATUS_FULL;
+				req->bytes_transferred = bytes_transferred;
+				req->status = TEGRA_DMA_REQ_SUCCESS;
+				tegra_dma_stop(ch);
+
+				if (!list_is_last(&req->node, &ch->list)) {
+					struct tegra_dma_req *next_req;
+
+					next_req = list_entry(req->node.next,
+						typeof(*next_req), node);
+					tegra_dma_update_hw(ch, next_req);
+				}
+
+				list_del(&req->node);
+
+				/* DMA lock is NOT held when callbak is called */
+				spin_unlock_irqrestore(&ch->lock, irq_flags);
+				req->complete(req);
+				return;
+			}
 			/* Load the next request into the hardware, if available
 			 * */
 			if (!list_is_last(&req->node, &ch->list)) {
@@ -580,7 +601,7 @@ static void handle_continuous_dma(struct tegra_dma_channel *ch)
 			req->buffer_status = TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL;
 			req->status = TEGRA_DMA_REQ_SUCCESS;
 			/* DMA lock is NOT held when callback is called */
-			spin_unlock(&ch->lock);
+			spin_unlock_irqrestore(&ch->lock, irq_flags);
 			if (likely(req->threshold))
 				req->threshold(req);
 			return;
@@ -591,8 +612,7 @@ static void handle_continuous_dma(struct tegra_dma_channel *ch)
 			 * the second  interrupt */
 			int bytes_transferred;
 
-			bytes_transferred =
-				(ch->csr & CSR_WCOUNT_MASK) >> CSR_WCOUNT_SHIFT;
+			bytes_transferred = ch->req_transfer_count;
 			bytes_transferred += 1;
 			bytes_transferred <<= 3;
 
@@ -602,7 +622,7 @@ static void handle_continuous_dma(struct tegra_dma_channel *ch)
 			list_del(&req->node);
 
 			/* DMA lock is NOT held when callbak is called */
-			spin_unlock(&ch->lock);
+			spin_unlock_irqrestore(&ch->lock, irq_flags);
 			req->complete(req);
 			return;
 
@@ -610,7 +630,7 @@ static void handle_continuous_dma(struct tegra_dma_channel *ch)
 			BUG();
 		}
 	}
-	spin_unlock(&ch->lock);
+	spin_unlock_irqrestore(&ch->lock, irq_flags);
 }
 
 static irqreturn_t dma_isr(int irq, void *data)
@@ -674,7 +694,6 @@ int __init tegra_dma_init(void)
 
 		spin_lock_init(&ch->lock);
 		INIT_LIST_HEAD(&ch->list);
-		tegra_dma_init_hw(ch);
 
 		irq = INT_APB_DMA_CH0 + i;
 		ret = request_threaded_irq(irq, dma_isr, dma_thread_fn, 0,
-- 
1.7.3.1


^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 22/28] ARM: tegra: dma: Fix critical data corruption bugs
@ 2011-01-24  2:01   ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-arm-kernel

Sometimes, due to high interrupt latency in the continuous mode
of DMA transfer, the half buffer complete interrupt is handled
after DMA has transferred the full buffer.  When this is detected,
stop DMA immediately and restart with the next buffer if the next
buffer is ready.

originally fixed by Victor(Weiguo) Pan <wpan@nvidia.com>

In place of using the simple spin_lock()/spi_unlock() in the
interrupt thread, using the spin_lock_irqsave() and
spin_unlock_irqrestore(). The lock is shared between the normal
process context and interrupt context.

originally fixed by Laxman Dewangan (ldewangan at nvidia.com)

The use of shadow registers caused memory corruption at physical
address 0 because the enable bit was not shadowed, and assuming it
needed to be set would enable an unconfigured dma block.  Most of the
register accesses don't need to know the previous state of the
registers, and the few places that do need to modify only a few bits
in the registers are the same ones that were sometimes incorrectly
setting the enable bit.  This patch convert tegra_dma_update_hardware
to set the entire register, and the other users to read-modify-write,
and drops the shadow registers completely.

Also fixes missing locking in tegra_dma_allocate_channel

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/dma.c |  197 +++++++++++++++++++++++++--------------------
 1 files changed, 108 insertions(+), 89 deletions(-)

diff --git a/arch/arm/mach-tegra/dma.c b/arch/arm/mach-tegra/dma.c
index a2a252d..250bc7b 100644
--- a/arch/arm/mach-tegra/dma.c
+++ b/arch/arm/mach-tegra/dma.c
@@ -121,17 +121,13 @@ struct tegra_dma_channel {
 	void  __iomem		*addr;
 	int			mode;
 	int			irq;
-
-	/* Register shadow */
-	u32			csr;
-	u32			ahb_seq;
-	u32			ahb_ptr;
-	u32			apb_seq;
-	u32			apb_ptr;
+	int			req_transfer_count;
 };
 
 #define  NV_DMA_MAX_CHANNELS  32
 
+static DEFINE_MUTEX(tegra_dma_lock);
+
 static DECLARE_BITMAP(channel_usage, NV_DMA_MAX_CHANNELS);
 static struct tegra_dma_channel dma_channels[NV_DMA_MAX_CHANNELS];
 
@@ -139,7 +135,6 @@ static void tegra_dma_update_hw(struct tegra_dma_channel *ch,
 	struct tegra_dma_req *req);
 static void tegra_dma_update_hw_partial(struct tegra_dma_channel *ch,
 	struct tegra_dma_req *req);
-static void tegra_dma_init_hw(struct tegra_dma_channel *ch);
 static void tegra_dma_stop(struct tegra_dma_channel *ch);
 
 void tegra_dma_flush(struct tegra_dma_channel *ch)
@@ -151,6 +146,9 @@ void tegra_dma_dequeue(struct tegra_dma_channel *ch)
 {
 	struct tegra_dma_req *req;
 
+	if (tegra_dma_is_empty(ch))
+		return;
+
 	req = list_entry(ch->list.next, typeof(*req), node);
 
 	tegra_dma_dequeue_req(ch, req);
@@ -159,10 +157,10 @@ void tegra_dma_dequeue(struct tegra_dma_channel *ch)
 
 void tegra_dma_stop(struct tegra_dma_channel *ch)
 {
-	unsigned int csr;
-	unsigned int status;
+	u32 csr;
+	u32 status;
 
-	csr = ch->csr;
+	csr = readl(ch->addr + APB_DMA_CHAN_CSR);
 	csr &= ~CSR_IE_EOC;
 	writel(csr, ch->addr + APB_DMA_CHAN_CSR);
 
@@ -176,19 +174,16 @@ void tegra_dma_stop(struct tegra_dma_channel *ch)
 
 int tegra_dma_cancel(struct tegra_dma_channel *ch)
 {
-	unsigned int csr;
+	u32 csr;
 	unsigned long irq_flags;
 
 	spin_lock_irqsave(&ch->lock, irq_flags);
 	while (!list_empty(&ch->list))
 		list_del(ch->list.next);
 
-	csr = ch->csr;
+	csr = readl(ch->addr + APB_DMA_CHAN_CSR);
 	csr &= ~CSR_REQ_SEL_MASK;
 	csr |= CSR_REQ_SEL_INVALID;
-
-	/* Set the enable as that is not shadowed */
-	csr |= CSR_ENB;
 	writel(csr, ch->addr + APB_DMA_CHAN_CSR);
 
 	tegra_dma_stop(ch);
@@ -230,18 +225,15 @@ int tegra_dma_dequeue_req(struct tegra_dma_channel *ch,
 	 *  - Finally stop or program the DMA to the next buffer in the
 	 *    list.
 	 */
-	csr = ch->csr;
+	csr = readl(ch->addr + APB_DMA_CHAN_CSR);
 	csr &= ~CSR_REQ_SEL_MASK;
 	csr |= CSR_REQ_SEL_INVALID;
-
-	/* Set the enable as that is not shadowed */
-	csr |= CSR_ENB;
 	writel(csr, ch->addr + APB_DMA_CHAN_CSR);
 
 	/* Get the transfer count */
 	status = readl(ch->addr + APB_DMA_CHAN_STA);
 	to_transfer = (status & STA_COUNT_MASK) >> STA_COUNT_SHIFT;
-	req_transfer_count = (ch->csr & CSR_WCOUNT_MASK) >> CSR_WCOUNT_SHIFT;
+	req_transfer_count = ch->req_transfer_count;
 	req_transfer_count += 1;
 	to_transfer += 1;
 
@@ -349,7 +341,9 @@ EXPORT_SYMBOL(tegra_dma_enqueue_req);
 struct tegra_dma_channel *tegra_dma_allocate_channel(int mode)
 {
 	int channel;
-	struct tegra_dma_channel *ch;
+	struct tegra_dma_channel *ch = NULL;
+
+	mutex_lock(&tegra_dma_lock);
 
 	/* first channel is the shared channel */
 	if (mode & TEGRA_DMA_SHARED) {
@@ -358,11 +352,14 @@ struct tegra_dma_channel *tegra_dma_allocate_channel(int mode)
 		channel = find_first_zero_bit(channel_usage,
 			ARRAY_SIZE(dma_channels));
 		if (channel >= ARRAY_SIZE(dma_channels))
-			return NULL;
+			goto out;
 	}
 	__set_bit(channel, channel_usage);
 	ch = &dma_channels[channel];
 	ch->mode = mode;
+
+out:
+	mutex_unlock(&tegra_dma_lock);
 	return ch;
 }
 EXPORT_SYMBOL(tegra_dma_allocate_channel);
@@ -372,22 +369,27 @@ void tegra_dma_free_channel(struct tegra_dma_channel *ch)
 	if (ch->mode & TEGRA_DMA_SHARED)
 		return;
 	tegra_dma_cancel(ch);
+	mutex_lock(&tegra_dma_lock);
 	__clear_bit(ch->id, channel_usage);
+	mutex_unlock(&tegra_dma_lock);
 }
 EXPORT_SYMBOL(tegra_dma_free_channel);
 
 static void tegra_dma_update_hw_partial(struct tegra_dma_channel *ch,
 	struct tegra_dma_req *req)
 {
+	u32 apb_ptr;
+	u32 ahb_ptr;
+
 	if (req->to_memory) {
-		ch->apb_ptr = req->source_addr;
-		ch->ahb_ptr = req->dest_addr;
+		apb_ptr = req->source_addr;
+		ahb_ptr = req->dest_addr;
 	} else {
-		ch->apb_ptr = req->dest_addr;
-		ch->ahb_ptr = req->source_addr;
+		apb_ptr = req->dest_addr;
+		ahb_ptr = req->source_addr;
 	}
-	writel(ch->apb_ptr, ch->addr + APB_DMA_CHAN_APB_PTR);
-	writel(ch->ahb_ptr, ch->addr + APB_DMA_CHAN_AHB_PTR);
+	writel(apb_ptr, ch->addr + APB_DMA_CHAN_APB_PTR);
+	writel(ahb_ptr, ch->addr + APB_DMA_CHAN_AHB_PTR);
 
 	req->status = TEGRA_DMA_REQ_INFLIGHT;
 	return;
@@ -401,38 +403,39 @@ static void tegra_dma_update_hw(struct tegra_dma_channel *ch,
 	int ahb_bus_width;
 	int apb_bus_width;
 	int index;
-	unsigned long csr;
 
+	u32 ahb_seq;
+	u32 apb_seq;
+	u32 ahb_ptr;
+	u32 apb_ptr;
+	u32 csr;
+
+	csr = CSR_IE_EOC | CSR_FLOW;
+	ahb_seq = AHB_SEQ_INTR_ENB | AHB_SEQ_BURST_1;
+	apb_seq = 0;
 
-	ch->csr |= CSR_FLOW;
-	ch->csr &= ~CSR_REQ_SEL_MASK;
-	ch->csr |= req->req_sel << CSR_REQ_SEL_SHIFT;
-	ch->ahb_seq &= ~AHB_SEQ_BURST_MASK;
-	ch->ahb_seq |= AHB_SEQ_BURST_1;
+	csr |= req->req_sel << CSR_REQ_SEL_SHIFT;
 
 	/* One shot mode is always single buffered,
 	 * continuous mode is always double buffered
 	 * */
 	if (ch->mode & TEGRA_DMA_MODE_ONESHOT) {
-		ch->csr |= CSR_ONCE;
-		ch->ahb_seq &= ~AHB_SEQ_DBL_BUF;
-		ch->csr &= ~CSR_WCOUNT_MASK;
-		ch->csr |= ((req->size>>2) - 1) << CSR_WCOUNT_SHIFT;
+		csr |= CSR_ONCE;
+		ch->req_transfer_count = (req->size >> 2) - 1;
 	} else {
-		ch->csr &= ~CSR_ONCE;
-		ch->ahb_seq |= AHB_SEQ_DBL_BUF;
+		ahb_seq |= AHB_SEQ_DBL_BUF;
 
 		/* In double buffered mode, we set the size to half the
 		 * requested size and interrupt when half the buffer
 		 * is full */
-		ch->csr &= ~CSR_WCOUNT_MASK;
-		ch->csr |= ((req->size>>3) - 1) << CSR_WCOUNT_SHIFT;
+		ch->req_transfer_count = (req->size >> 3) - 1;
 	}
 
+	csr |= ch->req_transfer_count << CSR_WCOUNT_SHIFT;
+
 	if (req->to_memory) {
-		ch->csr &= ~CSR_DIR;
-		ch->apb_ptr = req->source_addr;
-		ch->ahb_ptr = req->dest_addr;
+		apb_ptr = req->source_addr;
+		ahb_ptr = req->dest_addr;
 
 		apb_addr_wrap = req->source_wrap;
 		ahb_addr_wrap = req->dest_wrap;
@@ -440,9 +443,9 @@ static void tegra_dma_update_hw(struct tegra_dma_channel *ch,
 		ahb_bus_width = req->dest_bus_width;
 
 	} else {
-		ch->csr |= CSR_DIR;
-		ch->apb_ptr = req->dest_addr;
-		ch->ahb_ptr = req->source_addr;
+		csr |= CSR_DIR;
+		apb_ptr = req->dest_addr;
+		ahb_ptr = req->source_addr;
 
 		apb_addr_wrap = req->dest_wrap;
 		ahb_addr_wrap = req->source_wrap;
@@ -461,8 +464,7 @@ static void tegra_dma_update_hw(struct tegra_dma_channel *ch,
 		index++;
 	} while (index < ARRAY_SIZE(apb_addr_wrap_table));
 	BUG_ON(index == ARRAY_SIZE(apb_addr_wrap_table));
-	ch->apb_seq &= ~APB_SEQ_WRAP_MASK;
-	ch->apb_seq |= index << APB_SEQ_WRAP_SHIFT;
+	apb_seq |= index << APB_SEQ_WRAP_SHIFT;
 
 	/* set address wrap for AHB size */
 	index = 0;
@@ -472,55 +474,42 @@ static void tegra_dma_update_hw(struct tegra_dma_channel *ch,
 		index++;
 	} while (index < ARRAY_SIZE(ahb_addr_wrap_table));
 	BUG_ON(index == ARRAY_SIZE(ahb_addr_wrap_table));
-	ch->ahb_seq &= ~AHB_SEQ_WRAP_MASK;
-	ch->ahb_seq |= index << AHB_SEQ_WRAP_SHIFT;
+	ahb_seq |= index << AHB_SEQ_WRAP_SHIFT;
 
 	for (index = 0; index < ARRAY_SIZE(bus_width_table); index++) {
 		if (bus_width_table[index] == ahb_bus_width)
 			break;
 	}
 	BUG_ON(index == ARRAY_SIZE(bus_width_table));
-	ch->ahb_seq &= ~AHB_SEQ_BUS_WIDTH_MASK;
-	ch->ahb_seq |= index << AHB_SEQ_BUS_WIDTH_SHIFT;
+	ahb_seq |= index << AHB_SEQ_BUS_WIDTH_SHIFT;
 
 	for (index = 0; index < ARRAY_SIZE(bus_width_table); index++) {
 		if (bus_width_table[index] == apb_bus_width)
 			break;
 	}
 	BUG_ON(index == ARRAY_SIZE(bus_width_table));
-	ch->apb_seq &= ~APB_SEQ_BUS_WIDTH_MASK;
-	ch->apb_seq |= index << APB_SEQ_BUS_WIDTH_SHIFT;
-
-	ch->csr |= CSR_IE_EOC;
+	apb_seq |= index << APB_SEQ_BUS_WIDTH_SHIFT;
 
-	/* update hw registers with the shadow */
-	writel(ch->csr, ch->addr + APB_DMA_CHAN_CSR);
-	writel(ch->apb_seq, ch->addr + APB_DMA_CHAN_APB_SEQ);
-	writel(ch->apb_ptr, ch->addr + APB_DMA_CHAN_APB_PTR);
-	writel(ch->ahb_seq, ch->addr + APB_DMA_CHAN_AHB_SEQ);
-	writel(ch->ahb_ptr, ch->addr + APB_DMA_CHAN_AHB_PTR);
+	writel(csr, ch->addr + APB_DMA_CHAN_CSR);
+	writel(apb_seq, ch->addr + APB_DMA_CHAN_APB_SEQ);
+	writel(apb_ptr, ch->addr + APB_DMA_CHAN_APB_PTR);
+	writel(ahb_seq, ch->addr + APB_DMA_CHAN_AHB_SEQ);
+	writel(ahb_ptr, ch->addr + APB_DMA_CHAN_AHB_PTR);
 
-	csr = ch->csr | CSR_ENB;
+	csr |= CSR_ENB;
 	writel(csr, ch->addr + APB_DMA_CHAN_CSR);
 
 	req->status = TEGRA_DMA_REQ_INFLIGHT;
 }
 
-static void tegra_dma_init_hw(struct tegra_dma_channel *ch)
-{
-	/* One shot with an interrupt to CPU after transfer */
-	ch->csr = CSR_ONCE | CSR_IE_EOC;
-	ch->ahb_seq = AHB_SEQ_BUS_WIDTH_32 | AHB_SEQ_INTR_ENB;
-	ch->apb_seq = APB_SEQ_BUS_WIDTH_32 | 1 << APB_SEQ_WRAP_SHIFT;
-}
-
 static void handle_oneshot_dma(struct tegra_dma_channel *ch)
 {
 	struct tegra_dma_req *req;
+	unsigned long irq_flags;
 
-	spin_lock(&ch->lock);
+	spin_lock_irqsave(&ch->lock, irq_flags);
 	if (list_empty(&ch->list)) {
-		spin_unlock(&ch->lock);
+		spin_unlock_irqrestore(&ch->lock, irq_flags);
 		return;
 	}
 
@@ -528,8 +517,7 @@ static void handle_oneshot_dma(struct tegra_dma_channel *ch)
 	if (req) {
 		int bytes_transferred;
 
-		bytes_transferred =
-			(ch->csr & CSR_WCOUNT_MASK) >> CSR_WCOUNT_SHIFT;
+		bytes_transferred = ch->req_transfer_count;
 		bytes_transferred += 1;
 		bytes_transferred <<= 2;
 
@@ -537,12 +525,12 @@ static void handle_oneshot_dma(struct tegra_dma_channel *ch)
 		req->bytes_transferred = bytes_transferred;
 		req->status = TEGRA_DMA_REQ_SUCCESS;
 
-		spin_unlock(&ch->lock);
+		spin_unlock_irqrestore(&ch->lock, irq_flags);
 		/* Callback should be called without any lock */
 		pr_debug("%s: transferred %d bytes\n", __func__,
 			req->bytes_transferred);
 		req->complete(req);
-		spin_lock(&ch->lock);
+		spin_lock_irqsave(&ch->lock, irq_flags);
 	}
 
 	if (!list_empty(&ch->list)) {
@@ -552,22 +540,55 @@ static void handle_oneshot_dma(struct tegra_dma_channel *ch)
 		if (req->status != TEGRA_DMA_REQ_INFLIGHT)
 			tegra_dma_update_hw(ch, req);
 	}
-	spin_unlock(&ch->lock);
+	spin_unlock_irqrestore(&ch->lock, irq_flags);
 }
 
 static void handle_continuous_dma(struct tegra_dma_channel *ch)
 {
 	struct tegra_dma_req *req;
+	unsigned long irq_flags;
 
-	spin_lock(&ch->lock);
+	spin_lock_irqsave(&ch->lock, irq_flags);
 	if (list_empty(&ch->list)) {
-		spin_unlock(&ch->lock);
+		spin_unlock_irqrestore(&ch->lock, irq_flags);
 		return;
 	}
 
 	req = list_entry(ch->list.next, typeof(*req), node);
 	if (req) {
 		if (req->buffer_status == TEGRA_DMA_REQ_BUF_STATUS_EMPTY) {
+			bool is_dma_ping_complete;
+			is_dma_ping_complete = (readl(ch->addr + APB_DMA_CHAN_STA)
+						& STA_PING_PONG) ? true : false;
+			if (req->to_memory)
+				is_dma_ping_complete = !is_dma_ping_complete;
+			/* Out of sync - Release current buffer */
+			if (!is_dma_ping_complete) {
+				int bytes_transferred;
+
+				bytes_transferred = ch->req_transfer_count;
+				bytes_transferred += 1;
+				bytes_transferred <<= 3;
+				req->buffer_status = TEGRA_DMA_REQ_BUF_STATUS_FULL;
+				req->bytes_transferred = bytes_transferred;
+				req->status = TEGRA_DMA_REQ_SUCCESS;
+				tegra_dma_stop(ch);
+
+				if (!list_is_last(&req->node, &ch->list)) {
+					struct tegra_dma_req *next_req;
+
+					next_req = list_entry(req->node.next,
+						typeof(*next_req), node);
+					tegra_dma_update_hw(ch, next_req);
+				}
+
+				list_del(&req->node);
+
+				/* DMA lock is NOT held when callbak is called */
+				spin_unlock_irqrestore(&ch->lock, irq_flags);
+				req->complete(req);
+				return;
+			}
 			/* Load the next request into the hardware, if available
 			 * */
 			if (!list_is_last(&req->node, &ch->list)) {
@@ -580,7 +601,7 @@ static void handle_continuous_dma(struct tegra_dma_channel *ch)
 			req->buffer_status = TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL;
 			req->status = TEGRA_DMA_REQ_SUCCESS;
 			/* DMA lock is NOT held when callback is called */
-			spin_unlock(&ch->lock);
+			spin_unlock_irqrestore(&ch->lock, irq_flags);
 			if (likely(req->threshold))
 				req->threshold(req);
 			return;
@@ -591,8 +612,7 @@ static void handle_continuous_dma(struct tegra_dma_channel *ch)
 			 * the second  interrupt */
 			int bytes_transferred;
 
-			bytes_transferred =
-				(ch->csr & CSR_WCOUNT_MASK) >> CSR_WCOUNT_SHIFT;
+			bytes_transferred = ch->req_transfer_count;
 			bytes_transferred += 1;
 			bytes_transferred <<= 3;
 
@@ -602,7 +622,7 @@ static void handle_continuous_dma(struct tegra_dma_channel *ch)
 			list_del(&req->node);
 
 			/* DMA lock is NOT held when callbak is called */
-			spin_unlock(&ch->lock);
+			spin_unlock_irqrestore(&ch->lock, irq_flags);
 			req->complete(req);
 			return;
 
@@ -610,7 +630,7 @@ static void handle_continuous_dma(struct tegra_dma_channel *ch)
 			BUG();
 		}
 	}
-	spin_unlock(&ch->lock);
+	spin_unlock_irqrestore(&ch->lock, irq_flags);
 }
 
 static irqreturn_t dma_isr(int irq, void *data)
@@ -674,7 +694,6 @@ int __init tegra_dma_init(void)
 
 		spin_lock_init(&ch->lock);
 		INIT_LIST_HEAD(&ch->list);
-		tegra_dma_init_hw(ch);
 
 		irq = INT_APB_DMA_CH0 + i;
 		ret = request_threaded_irq(irq, dma_isr, dma_thread_fn, 0,
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 23/28] ARM: tegra: add tegra_defconfig
  2011-01-24  2:01 [PATCH v2 00/28] Updates for Tegra support in 2.6.39 Colin Cross
@ 2011-01-24  2:01   ` Colin Cross
  2011-01-24  2:01   ` Colin Cross
                     ` (26 subsequent siblings)
  27 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-tegra
  Cc: linux-arm-kernel, olof, konkers, Colin Cross, Russell King, linux-kernel

From: Olof Johansson <olof@lixom.net>

Adding one single defconfig for the tegra family of boards, to over time
cover the superset of supported platform and drivers.

Signed-off-by: Olof Johansson <olof@lixom.net>
Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/configs/tegra_defconfig |  123 ++++++++++++++++++++++++++++++++++++++
 1 files changed, 123 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/configs/tegra_defconfig

diff --git a/arch/arm/configs/tegra_defconfig b/arch/arm/configs/tegra_defconfig
new file mode 100644
index 0000000..7a9267e
--- /dev/null
+++ b/arch/arm/configs/tegra_defconfig
@@ -0,0 +1,123 @@
+CONFIG_EXPERIMENTAL=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_RESOURCE_COUNTERS=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_EMBEDDED=y
+# CONFIG_SYSCTL_SYSCALL is not set
+# CONFIG_ELF_CORE is not set
+CONFIG_SLAB=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+CONFIG_ARCH_TEGRA=y
+CONFIG_MACH_HARMONY=y
+CONFIG_TEGRA_DEBUG_UARTD=y
+CONFIG_ARM_ERRATA_742230=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_SMP=y
+CONFIG_NR_CPUS=2
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+CONFIG_HIGHMEM=y
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_VFP=y
+CONFIG_PM=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+CONFIG_INET_ESP=y
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+# CONFIG_INET_DIAG is not set
+CONFIG_IPV6=y
+CONFIG_IPV6_PRIVACY=y
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INET6_IPCOMP=y
+CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_TUNNEL=y
+CONFIG_IPV6_MULTIPLE_TABLES=y
+# CONFIG_WIRELESS is not set
+# CONFIG_FIRMWARE_IN_KERNEL is not set
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_MISC_DEVICES=y
+CONFIG_AD525X_DPOT=y
+CONFIG_AD525X_DPOT_I2C=y
+CONFIG_ICS932S401=y
+CONFIG_APDS9802ALS=y
+CONFIG_ISL29003=y
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=y
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+# CONFIG_WLAN is not set
+# CONFIG_INPUT is not set
+# CONFIG_SERIO is not set
+# CONFIG_VT is not set
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_HW_RANDOM is not set
+CONFIG_I2C=y
+# CONFIG_HWMON is not set
+# CONFIG_MFD_SUPPORT is not set
+# CONFIG_USB_SUPPORT is not set
+CONFIG_MMC=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT2_FS_SECURITY=y
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT3_FS_SECURITY=y
+# CONFIG_DNOTIFY is not set
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_FS=y
+CONFIG_DEBUG_KERNEL=y
+CONFIG_DETECT_HUNG_TASK=y
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+CONFIG_DEBUG_SLAB=y
+# CONFIG_DEBUG_PREEMPT is not set
+CONFIG_DEBUG_MUTEXES=y
+CONFIG_DEBUG_SPINLOCK_SLEEP=y
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_VM=y
+CONFIG_DEBUG_SG=y
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+CONFIG_DEBUG_LL=y
+CONFIG_EARLY_PRINTK=y
+CONFIG_CRYPTO_ECB=y
+CONFIG_CRYPTO_AES=y
+CONFIG_CRYPTO_ARC4=y
+CONFIG_CRYPTO_TWOFISH=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_CRC_CCITT=y
+CONFIG_CRC16=y
-- 
1.7.3.1


^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 23/28] ARM: tegra: add tegra_defconfig
@ 2011-01-24  2:01   ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-arm-kernel

From: Olof Johansson <olof@lixom.net>

Adding one single defconfig for the tegra family of boards, to over time
cover the superset of supported platform and drivers.

Signed-off-by: Olof Johansson <olof@lixom.net>
Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/configs/tegra_defconfig |  123 ++++++++++++++++++++++++++++++++++++++
 1 files changed, 123 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/configs/tegra_defconfig

diff --git a/arch/arm/configs/tegra_defconfig b/arch/arm/configs/tegra_defconfig
new file mode 100644
index 0000000..7a9267e
--- /dev/null
+++ b/arch/arm/configs/tegra_defconfig
@@ -0,0 +1,123 @@
+CONFIG_EXPERIMENTAL=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_RESOURCE_COUNTERS=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_EMBEDDED=y
+# CONFIG_SYSCTL_SYSCALL is not set
+# CONFIG_ELF_CORE is not set
+CONFIG_SLAB=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+CONFIG_ARCH_TEGRA=y
+CONFIG_MACH_HARMONY=y
+CONFIG_TEGRA_DEBUG_UARTD=y
+CONFIG_ARM_ERRATA_742230=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_SMP=y
+CONFIG_NR_CPUS=2
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+CONFIG_HIGHMEM=y
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_VFP=y
+CONFIG_PM=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+CONFIG_INET_ESP=y
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+# CONFIG_INET_DIAG is not set
+CONFIG_IPV6=y
+CONFIG_IPV6_PRIVACY=y
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INET6_IPCOMP=y
+CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_TUNNEL=y
+CONFIG_IPV6_MULTIPLE_TABLES=y
+# CONFIG_WIRELESS is not set
+# CONFIG_FIRMWARE_IN_KERNEL is not set
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_MISC_DEVICES=y
+CONFIG_AD525X_DPOT=y
+CONFIG_AD525X_DPOT_I2C=y
+CONFIG_ICS932S401=y
+CONFIG_APDS9802ALS=y
+CONFIG_ISL29003=y
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=y
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+# CONFIG_WLAN is not set
+# CONFIG_INPUT is not set
+# CONFIG_SERIO is not set
+# CONFIG_VT is not set
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_HW_RANDOM is not set
+CONFIG_I2C=y
+# CONFIG_HWMON is not set
+# CONFIG_MFD_SUPPORT is not set
+# CONFIG_USB_SUPPORT is not set
+CONFIG_MMC=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT2_FS_SECURITY=y
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT3_FS_SECURITY=y
+# CONFIG_DNOTIFY is not set
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_FS=y
+CONFIG_DEBUG_KERNEL=y
+CONFIG_DETECT_HUNG_TASK=y
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+CONFIG_DEBUG_SLAB=y
+# CONFIG_DEBUG_PREEMPT is not set
+CONFIG_DEBUG_MUTEXES=y
+CONFIG_DEBUG_SPINLOCK_SLEEP=y
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_VM=y
+CONFIG_DEBUG_SG=y
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+CONFIG_DEBUG_LL=y
+CONFIG_EARLY_PRINTK=y
+CONFIG_CRYPTO_ECB=y
+CONFIG_CRYPTO_AES=y
+CONFIG_CRYPTO_ARC4=y
+CONFIG_CRYPTO_TWOFISH=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_CRC_CCITT=y
+CONFIG_CRC16=y
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 24/28] ARM: tegra: Use writel_relaxed in tegra_init_cache
  2011-01-24  2:01 [PATCH v2 00/28] Updates for Tegra support in 2.6.39 Colin Cross
@ 2011-01-24  2:01   ` Colin Cross
  2011-01-24  2:01   ` Colin Cross
                     ` (26 subsequent siblings)
  27 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-tegra
  Cc: linux-arm-kernel, olof, konkers, Colin Cross, Russell King, linux-kernel

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/common.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c
index 1b32495..dc41669 100644
--- a/arch/arm/mach-tegra/common.c
+++ b/arch/arm/mach-tegra/common.c
@@ -68,8 +68,8 @@ void tegra_init_cache(void)
 #ifdef CONFIG_CACHE_L2X0
 	void __iomem *p = IO_ADDRESS(TEGRA_ARM_PERIF_BASE) + 0x3000;
 
-	writel(0x331, p + L2X0_TAG_LATENCY_CTRL);
-	writel(0x441, p + L2X0_DATA_LATENCY_CTRL);
+	writel_relaxed(0x331, p + L2X0_TAG_LATENCY_CTRL);
+	writel_relaxed(0x441, p + L2X0_DATA_LATENCY_CTRL);
 
 	l2x0_init(p, 0x6C080001, 0x8200c3fe);
 #endif
-- 
1.7.3.1


^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 24/28] ARM: tegra: Use writel_relaxed in tegra_init_cache
@ 2011-01-24  2:01   ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/common.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c
index 1b32495..dc41669 100644
--- a/arch/arm/mach-tegra/common.c
+++ b/arch/arm/mach-tegra/common.c
@@ -68,8 +68,8 @@ void tegra_init_cache(void)
 #ifdef CONFIG_CACHE_L2X0
 	void __iomem *p = IO_ADDRESS(TEGRA_ARM_PERIF_BASE) + 0x3000;
 
-	writel(0x331, p + L2X0_TAG_LATENCY_CTRL);
-	writel(0x441, p + L2X0_DATA_LATENCY_CTRL);
+	writel_relaxed(0x331, p + L2X0_TAG_LATENCY_CTRL);
+	writel_relaxed(0x441, p + L2X0_DATA_LATENCY_CTRL);
 
 	l2x0_init(p, 0x6C080001, 0x8200c3fe);
 #endif
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 25/28] ARM: tegra: suspend: Save protected aperture across LP0
  2011-01-24  2:01 [PATCH v2 00/28] Updates for Tegra support in 2.6.39 Colin Cross
@ 2011-01-24  2:01   ` Colin Cross
  2011-01-24  2:01   ` Colin Cross
                     ` (26 subsequent siblings)
  27 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-tegra
  Cc: linux-arm-kernel, olof, konkers, Colin Cross, Russell King, linux-kernel

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/suspend.c |    5 ++++-
 1 files changed, 4 insertions(+), 1 deletions(-)

diff --git a/arch/arm/mach-tegra/suspend.c b/arch/arm/mach-tegra/suspend.c
index ded2c36..be8c78f 100644
--- a/arch/arm/mach-tegra/suspend.c
+++ b/arch/arm/mach-tegra/suspend.c
@@ -497,12 +497,13 @@ static void tegra_debug_uart_resume(void)
 
 #define MC_SECURITY_START	0x6c
 #define MC_SECURITY_SIZE	0x70
+#define MC_SECURITY_CFG2	0x7c
 
 static int tegra_suspend_enter(suspend_state_t state)
 {
 	void __iomem *mc = IO_ADDRESS(TEGRA_MC_BASE);
 	unsigned long flags;
-	u32 mc_data[2];
+	u32 mc_data[3] = {0, 0, 0};
 	bool do_lp0 = (current_suspend_mode == TEGRA_SUSPEND_LP0);
 	bool do_lp2 = (current_suspend_mode == TEGRA_SUSPEND_LP2);
 	int lp_state;
@@ -533,6 +534,7 @@ static int tegra_suspend_enter(suspend_state_t state)
 
 		mc_data[0] = readl(mc + MC_SECURITY_START);
 		mc_data[1] = readl(mc + MC_SECURITY_SIZE);
+		mc_data[2] = readl(mc + MC_SECURITY_CFG2);
 	}
 
 	rtc_before = tegra_rtc_read_ms();
@@ -550,6 +552,7 @@ static int tegra_suspend_enter(suspend_state_t state)
 	if (do_lp0) {
 		writel(mc_data[0], mc + MC_SECURITY_START);
 		writel(mc_data[1], mc + MC_SECURITY_SIZE);
+		writel(mc_data[2], mc + MC_SECURITY_CFG2);
 
 		tegra_clk_resume();
 		tegra_gpio_resume();
-- 
1.7.3.1


^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 25/28] ARM: tegra: suspend: Save protected aperture across LP0
@ 2011-01-24  2:01   ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/suspend.c |    5 ++++-
 1 files changed, 4 insertions(+), 1 deletions(-)

diff --git a/arch/arm/mach-tegra/suspend.c b/arch/arm/mach-tegra/suspend.c
index ded2c36..be8c78f 100644
--- a/arch/arm/mach-tegra/suspend.c
+++ b/arch/arm/mach-tegra/suspend.c
@@ -497,12 +497,13 @@ static void tegra_debug_uart_resume(void)
 
 #define MC_SECURITY_START	0x6c
 #define MC_SECURITY_SIZE	0x70
+#define MC_SECURITY_CFG2	0x7c
 
 static int tegra_suspend_enter(suspend_state_t state)
 {
 	void __iomem *mc = IO_ADDRESS(TEGRA_MC_BASE);
 	unsigned long flags;
-	u32 mc_data[2];
+	u32 mc_data[3] = {0, 0, 0};
 	bool do_lp0 = (current_suspend_mode == TEGRA_SUSPEND_LP0);
 	bool do_lp2 = (current_suspend_mode == TEGRA_SUSPEND_LP2);
 	int lp_state;
@@ -533,6 +534,7 @@ static int tegra_suspend_enter(suspend_state_t state)
 
 		mc_data[0] = readl(mc + MC_SECURITY_START);
 		mc_data[1] = readl(mc + MC_SECURITY_SIZE);
+		mc_data[2] = readl(mc + MC_SECURITY_CFG2);
 	}
 
 	rtc_before = tegra_rtc_read_ms();
@@ -550,6 +552,7 @@ static int tegra_suspend_enter(suspend_state_t state)
 	if (do_lp0) {
 		writel(mc_data[0], mc + MC_SECURITY_START);
 		writel(mc_data[1], mc + MC_SECURITY_SIZE);
+		writel(mc_data[2], mc + MC_SECURITY_CFG2);
 
 		tegra_clk_resume();
 		tegra_gpio_resume();
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 26/28] ARM: tegra: suspend: protect suspend functions with CONFIG_PM
  2011-01-24  2:01 [PATCH v2 00/28] Updates for Tegra support in 2.6.39 Colin Cross
@ 2011-01-24  2:01   ` Colin Cross
  2011-01-24  2:01   ` Colin Cross
                     ` (26 subsequent siblings)
  27 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-tegra
  Cc: linux-arm-kernel, olof, konkers, Allen Martin, Colin Cross,
	Russell King, linux-kernel

From: Allen Martin <amartin@nvidia.com>

Protect suspend/resume functions behind #ifdef CONFIG_PM.  This
prevents a compile error with CONFIG_PM turned off.

Signed-off-by: Allen Martin <amartin@nvidia.com>
Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/suspend.c |    5 +++--
 1 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-tegra/suspend.c b/arch/arm/mach-tegra/suspend.c
index be8c78f..8b32707 100644
--- a/arch/arm/mach-tegra/suspend.c
+++ b/arch/arm/mach-tegra/suspend.c
@@ -75,10 +75,12 @@ struct suspend_context {
 struct suspend_context tegra_sctx;
 
 static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
+#ifdef CONFIG_PM
 static void __iomem *clk_rst = IO_ADDRESS(TEGRA_CLK_RESET_BASE);
 static void __iomem *flow_ctrl = IO_ADDRESS(TEGRA_FLOW_CTRL_BASE);
 static void __iomem *evp_reset = IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100;
 static void __iomem *tmrus = IO_ADDRESS(TEGRA_TMRUS_BASE);
+#endif
 
 #define PMC_CTRL		0x0
 #define PMC_CTRL_LATCH_WAKEUPS	(1 << 5)
@@ -205,6 +207,7 @@ static int create_suspend_pgtable(void)
 	return 0;
 }
 
+#ifdef CONFIG_PM
 static noinline void restore_cpu_complex(void)
 {
 	unsigned int reg;
@@ -326,8 +329,6 @@ unsigned int tegra_suspend_lp2(unsigned int us)
 	return remain;
 }
 
-#ifdef CONFIG_PM
-
 /* ensures that sufficient time is passed for a register write to
  * serialize into the 32KHz domain */
 static void pmc_32kwritel(u32 val, unsigned long offs)
-- 
1.7.3.1


^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 26/28] ARM: tegra: suspend: protect suspend functions with CONFIG_PM
@ 2011-01-24  2:01   ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-arm-kernel

From: Allen Martin <amartin@nvidia.com>

Protect suspend/resume functions behind #ifdef CONFIG_PM.  This
prevents a compile error with CONFIG_PM turned off.

Signed-off-by: Allen Martin <amartin@nvidia.com>
Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/suspend.c |    5 +++--
 1 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-tegra/suspend.c b/arch/arm/mach-tegra/suspend.c
index be8c78f..8b32707 100644
--- a/arch/arm/mach-tegra/suspend.c
+++ b/arch/arm/mach-tegra/suspend.c
@@ -75,10 +75,12 @@ struct suspend_context {
 struct suspend_context tegra_sctx;
 
 static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
+#ifdef CONFIG_PM
 static void __iomem *clk_rst = IO_ADDRESS(TEGRA_CLK_RESET_BASE);
 static void __iomem *flow_ctrl = IO_ADDRESS(TEGRA_FLOW_CTRL_BASE);
 static void __iomem *evp_reset = IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100;
 static void __iomem *tmrus = IO_ADDRESS(TEGRA_TMRUS_BASE);
+#endif
 
 #define PMC_CTRL		0x0
 #define PMC_CTRL_LATCH_WAKEUPS	(1 << 5)
@@ -205,6 +207,7 @@ static int create_suspend_pgtable(void)
 	return 0;
 }
 
+#ifdef CONFIG_PM
 static noinline void restore_cpu_complex(void)
 {
 	unsigned int reg;
@@ -326,8 +329,6 @@ unsigned int tegra_suspend_lp2(unsigned int us)
 	return remain;
 }
 
-#ifdef CONFIG_PM
-
 /* ensures that sufficient time is passed for a register write to
  * serialize into the 32KHz domain */
 static void pmc_32kwritel(u32 val, unsigned long offs)
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 27/28] ARM: tegra: enable emc clock updates after lp0
  2011-01-24  2:01 [PATCH v2 00/28] Updates for Tegra support in 2.6.39 Colin Cross
@ 2011-01-24  2:01   ` Colin Cross
  2011-01-24  2:01   ` Colin Cross
                     ` (26 subsequent siblings)
  27 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-tegra
  Cc: linux-arm-kernel, olof, konkers, James Wylder, Colin Cross,
	Russell King, linux-kernel

From: James Wylder <james.wylder@motorola.com>

Write a dummy value to EMC_MRW_0 to allow
clock frequency changes after lp0.

Signed-off-by: James Wylder <james.wylder@motorola.com>
Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/suspend.c |    8 ++++++++
 1 files changed, 8 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-tegra/suspend.c b/arch/arm/mach-tegra/suspend.c
index 8b32707..9b851c7 100644
--- a/arch/arm/mach-tegra/suspend.c
+++ b/arch/arm/mach-tegra/suspend.c
@@ -122,6 +122,10 @@ static void __iomem *tmrus = IO_ADDRESS(TEGRA_TMRUS_BASE);
 #define FLOW_CTRL_CPU_CSR	0x8
 #define FLOW_CTRL_CPU1_CSR	0x18
 
+#define EMC_MRW_0		0x0e8
+#define EMC_MRW_DEV_SELECTN     30
+#define EMC_MRW_DEV_NONE	(3 << EMC_MRW_DEV_SELECTN)
+
 unsigned long tegra_pgd_phys;  /* pgd used by hotplug & LP2 bootup */
 static pgd_t *tegra_pgd;
 void *tegra_context_area;
@@ -503,6 +507,7 @@ static void tegra_debug_uart_resume(void)
 static int tegra_suspend_enter(suspend_state_t state)
 {
 	void __iomem *mc = IO_ADDRESS(TEGRA_MC_BASE);
+	void __iomem *emc = IO_ADDRESS(TEGRA_EMC_BASE);
 	unsigned long flags;
 	u32 mc_data[3] = {0, 0, 0};
 	bool do_lp0 = (current_suspend_mode == TEGRA_SUSPEND_LP0);
@@ -555,6 +560,9 @@ static int tegra_suspend_enter(suspend_state_t state)
 		writel(mc_data[1], mc + MC_SECURITY_SIZE);
 		writel(mc_data[2], mc + MC_SECURITY_CFG2);
 
+		/* trigger emc mode write */
+		writel(EMC_MRW_DEV_NONE, emc + EMC_MRW_0);
+
 		tegra_clk_resume();
 		tegra_gpio_resume();
 		tegra_timer_resume();
-- 
1.7.3.1


^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 27/28] ARM: tegra: enable emc clock updates after lp0
@ 2011-01-24  2:01   ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-arm-kernel

From: James Wylder <james.wylder@motorola.com>

Write a dummy value to EMC_MRW_0 to allow
clock frequency changes after lp0.

Signed-off-by: James Wylder <james.wylder@motorola.com>
Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/suspend.c |    8 ++++++++
 1 files changed, 8 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-tegra/suspend.c b/arch/arm/mach-tegra/suspend.c
index 8b32707..9b851c7 100644
--- a/arch/arm/mach-tegra/suspend.c
+++ b/arch/arm/mach-tegra/suspend.c
@@ -122,6 +122,10 @@ static void __iomem *tmrus = IO_ADDRESS(TEGRA_TMRUS_BASE);
 #define FLOW_CTRL_CPU_CSR	0x8
 #define FLOW_CTRL_CPU1_CSR	0x18
 
+#define EMC_MRW_0		0x0e8
+#define EMC_MRW_DEV_SELECTN     30
+#define EMC_MRW_DEV_NONE	(3 << EMC_MRW_DEV_SELECTN)
+
 unsigned long tegra_pgd_phys;  /* pgd used by hotplug & LP2 bootup */
 static pgd_t *tegra_pgd;
 void *tegra_context_area;
@@ -503,6 +507,7 @@ static void tegra_debug_uart_resume(void)
 static int tegra_suspend_enter(suspend_state_t state)
 {
 	void __iomem *mc = IO_ADDRESS(TEGRA_MC_BASE);
+	void __iomem *emc = IO_ADDRESS(TEGRA_EMC_BASE);
 	unsigned long flags;
 	u32 mc_data[3] = {0, 0, 0};
 	bool do_lp0 = (current_suspend_mode == TEGRA_SUSPEND_LP0);
@@ -555,6 +560,9 @@ static int tegra_suspend_enter(suspend_state_t state)
 		writel(mc_data[1], mc + MC_SECURITY_SIZE);
 		writel(mc_data[2], mc + MC_SECURITY_CFG2);
 
+		/* trigger emc mode write */
+		writel(EMC_MRW_DEV_NONE, emc + EMC_MRW_0);
+
 		tegra_clk_resume();
 		tegra_gpio_resume();
 		tegra_timer_resume();
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 28/28] ARM: tegra: clock: Add forward reference to struct clk
  2011-01-24  2:01 [PATCH v2 00/28] Updates for Tegra support in 2.6.39 Colin Cross
@ 2011-01-24  2:01   ` Colin Cross
  2011-01-24  2:01   ` Colin Cross
                     ` (26 subsequent siblings)
  27 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-tegra
  Cc: linux-arm-kernel, olof, konkers, Colin Cross, Russell King, linux-kernel

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/include/mach/clk.h    |    2 ++
 arch/arm/mach-tegra/include/mach/clkdev.h |    2 ++
 2 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-tegra/include/mach/clk.h b/arch/arm/mach-tegra/include/mach/clk.h
index d772395..a217f68 100644
--- a/arch/arm/mach-tegra/include/mach/clk.h
+++ b/arch/arm/mach-tegra/include/mach/clk.h
@@ -20,6 +20,8 @@
 #ifndef __MACH_CLK_H
 #define __MACH_CLK_H
 
+struct clk;
+
 void tegra_periph_reset_deassert(struct clk *c);
 void tegra_periph_reset_assert(struct clk *c);
 
diff --git a/arch/arm/mach-tegra/include/mach/clkdev.h b/arch/arm/mach-tegra/include/mach/clkdev.h
index 412f5c6..66cd3f4 100644
--- a/arch/arm/mach-tegra/include/mach/clkdev.h
+++ b/arch/arm/mach-tegra/include/mach/clkdev.h
@@ -20,6 +20,8 @@
 #ifndef __MACH_CLKDEV_H
 #define __MACH_CLKDEV_H
 
+struct clk;
+
 static inline int __clk_get(struct clk *clk)
 {
 	return 1;
-- 
1.7.3.1


^ permalink raw reply related	[flat|nested] 137+ messages in thread

* [PATCH v2 28/28] ARM: tegra: clock: Add forward reference to struct clk
@ 2011-01-24  2:01   ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  2:01 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/mach-tegra/include/mach/clk.h    |    2 ++
 arch/arm/mach-tegra/include/mach/clkdev.h |    2 ++
 2 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-tegra/include/mach/clk.h b/arch/arm/mach-tegra/include/mach/clk.h
index d772395..a217f68 100644
--- a/arch/arm/mach-tegra/include/mach/clk.h
+++ b/arch/arm/mach-tegra/include/mach/clk.h
@@ -20,6 +20,8 @@
 #ifndef __MACH_CLK_H
 #define __MACH_CLK_H
 
+struct clk;
+
 void tegra_periph_reset_deassert(struct clk *c);
 void tegra_periph_reset_assert(struct clk *c);
 
diff --git a/arch/arm/mach-tegra/include/mach/clkdev.h b/arch/arm/mach-tegra/include/mach/clkdev.h
index 412f5c6..66cd3f4 100644
--- a/arch/arm/mach-tegra/include/mach/clkdev.h
+++ b/arch/arm/mach-tegra/include/mach/clkdev.h
@@ -20,6 +20,8 @@
 #ifndef __MACH_CLKDEV_H
 #define __MACH_CLKDEV_H
 
+struct clk;
+
 static inline int __clk_get(struct clk *clk)
 {
 	return 1;
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 137+ messages in thread

* RE: [PATCH v2 03/28] ARM: gic: Export irq chip functions
  2011-01-24  2:01   ` Colin Cross
@ 2011-01-24  6:39     ` Santosh Shilimkar
  -1 siblings, 0 replies; 137+ messages in thread
From: Santosh Shilimkar @ 2011-01-24  6:39 UTC (permalink / raw)
  To: Colin Cross, linux-tegra
  Cc: Russell King, Linus Walleij, konkers, Gary King, Catalin Marinas,
	Abhijeet Dharmapurikar, linux-kernel, olof, linux-arm-kernel

Colin,
> -----Original Message-----
> From: linux-arm-kernel-bounces@lists.infradead.org [mailto:linux-
> arm-kernel-bounces@lists.infradead.org] On Behalf Of Colin Cross
> Sent: Monday, January 24, 2011 7:31 AM
> To: linux-tegra@vger.kernel.org
> Cc: Russell King; Linus Walleij; konkers@android.com; Gary King;
> Catalin Marinas; Abhijeet Dharmapurikar; linux-
> kernel@vger.kernel.org; Colin Cross; olof@lixom.net; linux-arm-
> kernel@lists.infradead.org
> Subject: [PATCH v2 03/28] ARM: gic: Export irq chip functions
>
> Some systems combine the GIC with an external interrupt controller.
> On these systems it may be necessary to update both the GIC
> registers
> and the external controller's registers to control IRQ behavior.
> Export the irq chip functions so that these systems can define a
> custom irq chip that calls into the GIC handlers.
>
> Signed-off-by: Colin Cross <ccross@android.com>
> ---
> v2: Updated on top of irq_data changes
>
>  arch/arm/common/gic.c               |   11 +++++------
>  arch/arm/include/asm/hardware/gic.h |   10 ++++++++++
>  2 files changed, 15 insertions(+), 6 deletions(-)
>
Instead of exporting the GIC library, I think we could add the
flexibility with slightly different approach.
Have few common arm patches mainly for PM.
Will post them shortly. Have a look at them.


Regards,
Santosh

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 03/28] ARM: gic: Export irq chip functions
@ 2011-01-24  6:39     ` Santosh Shilimkar
  0 siblings, 0 replies; 137+ messages in thread
From: Santosh Shilimkar @ 2011-01-24  6:39 UTC (permalink / raw)
  To: linux-arm-kernel

Colin,
> -----Original Message-----
> From: linux-arm-kernel-bounces at lists.infradead.org [mailto:linux-
> arm-kernel-bounces at lists.infradead.org] On Behalf Of Colin Cross
> Sent: Monday, January 24, 2011 7:31 AM
> To: linux-tegra at vger.kernel.org
> Cc: Russell King; Linus Walleij; konkers at android.com; Gary King;
> Catalin Marinas; Abhijeet Dharmapurikar; linux-
> kernel at vger.kernel.org; Colin Cross; olof at lixom.net; linux-arm-
> kernel at lists.infradead.org
> Subject: [PATCH v2 03/28] ARM: gic: Export irq chip functions
>
> Some systems combine the GIC with an external interrupt controller.
> On these systems it may be necessary to update both the GIC
> registers
> and the external controller's registers to control IRQ behavior.
> Export the irq chip functions so that these systems can define a
> custom irq chip that calls into the GIC handlers.
>
> Signed-off-by: Colin Cross <ccross@android.com>
> ---
> v2: Updated on top of irq_data changes
>
>  arch/arm/common/gic.c               |   11 +++++------
>  arch/arm/include/asm/hardware/gic.h |   10 ++++++++++
>  2 files changed, 15 insertions(+), 6 deletions(-)
>
Instead of exporting the GIC library, I think we could add the
flexibility with slightly different approach.
Have few common arm patches mainly for PM.
Will post them shortly. Have a look at them.


Regards,
Santosh

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 13/28] ARM: tegra: Add suspend and hotplug support
  2011-01-24  2:01   ` Colin Cross
@ 2011-01-24  7:31     ` Colin Cross
  -1 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  7:31 UTC (permalink / raw)
  To: linux-tegra
  Cc: linux-arm-kernel, olof, konkers, Colin Cross, linux, Gary King,
	linux-kernel

On Sun, Jan 23, 2011 at 6:01 PM, Colin Cross <ccross@android.com> wrote:

<snip>

> diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
> index 00a6ba3..0bf1441 100644
> --- a/arch/arm/mach-tegra/Makefile
<snip>

> +obj-$(CONFIG_LOCAL_TIMER)               += localtimer.o

This needs to be CONFIG_LOCAL_TIMERS

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 13/28] ARM: tegra: Add suspend and hotplug support
@ 2011-01-24  7:31     ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  7:31 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, Jan 23, 2011 at 6:01 PM, Colin Cross <ccross@android.com> wrote:

<snip>

> diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
> index 00a6ba3..0bf1441 100644
> --- a/arch/arm/mach-tegra/Makefile
<snip>

> +obj-$(CONFIG_LOCAL_TIMER) ? ? ? ? ? ? ? += localtimer.o

This needs to be CONFIG_LOCAL_TIMERS

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 13/28] ARM: tegra: Add suspend and hotplug support
  2011-01-24  2:01   ` Colin Cross
@ 2011-01-24  9:07     ` Russell King - ARM Linux
  -1 siblings, 0 replies; 137+ messages in thread
From: Russell King - ARM Linux @ 2011-01-24  9:07 UTC (permalink / raw)
  To: Colin Cross
  Cc: linux-tegra, konkers, Gary King, linux-kernel, olof, linux-arm-kernel

On Sun, Jan 23, 2011 at 06:01:18PM -0800, Colin Cross wrote:
> Tegra supports three low power modes that involve powering down the CPU.

It is a common misconception that on CPU hot-unplug you need to save the
entire universe of the CPU going down.

When CPUs are hot-plugged, they reinitialize, which includes resetting
most of the CPU state.

There is no point saving the user register state, svc register state,
abort register state, interrupt register state, undefined mode register
state, TTB base registers, TTB control register, memory type remapping
registers nor the context register.  These are all reinitialized when
the CPU is brought up again.

VFP state should already be saved - when a CPU is hot-unplugged, we can't
leave a threads VFP register set stored in that hardware as it would
become inaccessible, so there's no point saving VFP state.  Recently
code was added to invalidate the software VFP state for a CPU being
hot-unplugged and reinitialize the VFP hardware on CPU hotplug.

We also have the necessary hooks into the PM code to deal with VFP state
saving/restore on PM suspend/resume.

The breakpoint/watchdog stuff _should_ be handled by the breakpoint/
watchdog code, not by each individual platform.  Please can you work
with Will Deacon if this is not the case to ensure that the breakpoint/
watchdog code can handle CPU hotplug.

Lastly, L2 cache controller should have suspend/resume support added.

We _really_ need to stop having platforms manually saving lots of CPU
state for suspend/hot unplug.

That should reduce the amount of state you're saving down to something
more manageable.

> +
> +#ifdef CONFIG_HOTPLUG_CPU
> +
> +int platform_cpu_kill(unsigned int cpu)
> +{
> +	unsigned int reg;
> +
> +	do {
> +		reg = readl(CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET);
> +		cpu_relax();
> +	} while (!(reg & (1<<cpu)));
> +
> +	spin_lock(&boot_lock);
> +	reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
> +	writel(reg | (1<<(8+cpu)), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
> +	spin_unlock(&boot_lock);
> +
> +	return 1;
> +}
> +
> +void platform_cpu_die(unsigned int cpu)
> +{
> +#ifdef DEBUG
> +	unsigned int this_cpu = hard_smp_processor_id();
> +
> +	if (cpu != this_cpu) {
> +		printk(KERN_CRIT "Eek! platform_cpu_die running on %u, should be %u\n",
> +			   this_cpu, cpu);
> +		BUG();
> +	}
> +#endif
> +
> +	gic_cpu_exit(0);
> +	barrier();
> +	flush_cache_all();
> +	barrier();
> +	__cortex_a9_save(0);
> +
> +	/*
> +	 * __cortex_a9_save can return through __cortex_a9_restore, but that
> +	 * should never happen for a hotplugged cpu
> +	 */
> +	BUG();
> +}
> +
> +int platform_cpu_disable(unsigned int cpu)
> +{
> +	/*
> +	 * we don't allow CPU 0 to be shutdown (it is still too special
> +	 * e.g. clock tick interrupts)
> +	 */
> +	if (unlikely(!tegra_context_area))
> +		return -ENXIO;
> +
> +	return cpu == 0 ? -EPERM : 0;
> +}
> +#endif

Please leave this in hotplug.c so that it matches what other platforms
do.  Otherwise it's likely to be missed by any updates that happen
architecture-wide.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 13/28] ARM: tegra: Add suspend and hotplug support
@ 2011-01-24  9:07     ` Russell King - ARM Linux
  0 siblings, 0 replies; 137+ messages in thread
From: Russell King - ARM Linux @ 2011-01-24  9:07 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, Jan 23, 2011 at 06:01:18PM -0800, Colin Cross wrote:
> Tegra supports three low power modes that involve powering down the CPU.

It is a common misconception that on CPU hot-unplug you need to save the
entire universe of the CPU going down.

When CPUs are hot-plugged, they reinitialize, which includes resetting
most of the CPU state.

There is no point saving the user register state, svc register state,
abort register state, interrupt register state, undefined mode register
state, TTB base registers, TTB control register, memory type remapping
registers nor the context register.  These are all reinitialized when
the CPU is brought up again.

VFP state should already be saved - when a CPU is hot-unplugged, we can't
leave a threads VFP register set stored in that hardware as it would
become inaccessible, so there's no point saving VFP state.  Recently
code was added to invalidate the software VFP state for a CPU being
hot-unplugged and reinitialize the VFP hardware on CPU hotplug.

We also have the necessary hooks into the PM code to deal with VFP state
saving/restore on PM suspend/resume.

The breakpoint/watchdog stuff _should_ be handled by the breakpoint/
watchdog code, not by each individual platform.  Please can you work
with Will Deacon if this is not the case to ensure that the breakpoint/
watchdog code can handle CPU hotplug.

Lastly, L2 cache controller should have suspend/resume support added.

We _really_ need to stop having platforms manually saving lots of CPU
state for suspend/hot unplug.

That should reduce the amount of state you're saving down to something
more manageable.

> +
> +#ifdef CONFIG_HOTPLUG_CPU
> +
> +int platform_cpu_kill(unsigned int cpu)
> +{
> +	unsigned int reg;
> +
> +	do {
> +		reg = readl(CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET);
> +		cpu_relax();
> +	} while (!(reg & (1<<cpu)));
> +
> +	spin_lock(&boot_lock);
> +	reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
> +	writel(reg | (1<<(8+cpu)), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
> +	spin_unlock(&boot_lock);
> +
> +	return 1;
> +}
> +
> +void platform_cpu_die(unsigned int cpu)
> +{
> +#ifdef DEBUG
> +	unsigned int this_cpu = hard_smp_processor_id();
> +
> +	if (cpu != this_cpu) {
> +		printk(KERN_CRIT "Eek! platform_cpu_die running on %u, should be %u\n",
> +			   this_cpu, cpu);
> +		BUG();
> +	}
> +#endif
> +
> +	gic_cpu_exit(0);
> +	barrier();
> +	flush_cache_all();
> +	barrier();
> +	__cortex_a9_save(0);
> +
> +	/*
> +	 * __cortex_a9_save can return through __cortex_a9_restore, but that
> +	 * should never happen for a hotplugged cpu
> +	 */
> +	BUG();
> +}
> +
> +int platform_cpu_disable(unsigned int cpu)
> +{
> +	/*
> +	 * we don't allow CPU 0 to be shutdown (it is still too special
> +	 * e.g. clock tick interrupts)
> +	 */
> +	if (unlikely(!tegra_context_area))
> +		return -ENXIO;
> +
> +	return cpu == 0 ? -EPERM : 0;
> +}
> +#endif

Please leave this in hotplug.c so that it matches what other platforms
do.  Otherwise it's likely to be missed by any updates that happen
architecture-wide.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 13/28] ARM: tegra: Add suspend and hotplug support
  2011-01-24  9:07     ` Russell King - ARM Linux
@ 2011-01-24  9:26       ` Colin Cross
  -1 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  9:26 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: linux-tegra, konkers, Gary King, linux-kernel, olof, linux-arm-kernel

On Mon, Jan 24, 2011 at 1:07 AM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Sun, Jan 23, 2011 at 06:01:18PM -0800, Colin Cross wrote:
>> Tegra supports three low power modes that involve powering down the CPU.
>
> It is a common misconception that on CPU hot-unplug you need to save the
> entire universe of the CPU going down.
>
> When CPUs are hot-plugged, they reinitialize, which includes resetting
> most of the CPU state.

The hotplug code path on Tegra currently shares the same path as cpu
idle, because the CPU ends up in the same state for both.  This
version of the patch ensures that when the cpu is brought back online,
it goes through the normal reset vector.  In hotplug, the state ends
up getting saved on the way down, but not used on the way back up.

> There is no point saving the user register state, svc register state,
> abort register state, interrupt register state, undefined mode register
> state, TTB base registers, TTB control register, memory type remapping
> registers nor the context register.  These are all reinitialized when
> the CPU is brought up again.

The register state is fixed in this version of the patch by calling
cpu_init() in the resume and idle code.  I'll look again if there is a
better way of handling the rest of the registers, but this code is
mostly needed for the suspend and idle cases where the normal boot
process is not used.

> VFP state should already be saved - when a CPU is hot-unplugged, we can't
> leave a threads VFP register set stored in that hardware as it would
> become inaccessible, so there's no point saving VFP state.  Recently
> code was added to invalidate the software VFP state for a CPU being
> hot-unplugged and reinitialize the VFP hardware on CPU hotplug.

Despite what the patch description says, this version of the patch
does not save VFP state any more.  I will update the description.  It
looks like if CONFIG_SMP=y, the VFP code saves the VFP context on
every task switch, so nothing is needed during cpu idle.  However, if
CONFIG_SMP=n, I think some way to save the VFP context is needed.

> We also have the necessary hooks into the PM code to deal with VFP state
> saving/restore on PM suspend/resume.
>
> The breakpoint/watchdog stuff _should_ be handled by the breakpoint/
> watchdog code, not by each individual platform.  Please can you work
> with Will Deacon if this is not the case to ensure that the breakpoint/
> watchdog code can handle CPU hotplug.

This version of the patch does not save breakpoint/watchdogs, but
something will need to save them during idle when the cpu is powered
down.

> Lastly, L2 cache controller should have suspend/resume support added.

Patch 4 in this series improves the L2 controller's PM support, and
this patch uses l2x0_disable, l2x0_enable, and l2x0_init.

> We _really_ need to stop having platforms manually saving lots of CPU
> state for suspend/hot unplug.
>
> That should reduce the amount of state you're saving down to something
> more manageable.
>
>> +
>> +#ifdef CONFIG_HOTPLUG_CPU
>> +
>> +int platform_cpu_kill(unsigned int cpu)
>> +{
>> +     unsigned int reg;
>> +
>> +     do {
>> +             reg = readl(CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET);
>> +             cpu_relax();
>> +     } while (!(reg & (1<<cpu)));
>> +
>> +     spin_lock(&boot_lock);
>> +     reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
>> +     writel(reg | (1<<(8+cpu)), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
>> +     spin_unlock(&boot_lock);
>> +
>> +     return 1;
>> +}
>> +
>> +void platform_cpu_die(unsigned int cpu)
>> +{
>> +#ifdef DEBUG
>> +     unsigned int this_cpu = hard_smp_processor_id();
>> +
>> +     if (cpu != this_cpu) {
>> +             printk(KERN_CRIT "Eek! platform_cpu_die running on %u, should be %u\n",
>> +                        this_cpu, cpu);
>> +             BUG();
>> +     }
>> +#endif
>> +
>> +     gic_cpu_exit(0);
>> +     barrier();
>> +     flush_cache_all();
>> +     barrier();
>> +     __cortex_a9_save(0);
>> +
>> +     /*
>> +      * __cortex_a9_save can return through __cortex_a9_restore, but that
>> +      * should never happen for a hotplugged cpu
>> +      */
>> +     BUG();
>> +}
>> +
>> +int platform_cpu_disable(unsigned int cpu)
>> +{
>> +     /*
>> +      * we don't allow CPU 0 to be shutdown (it is still too special
>> +      * e.g. clock tick interrupts)
>> +      */
>> +     if (unlikely(!tegra_context_area))
>> +             return -ENXIO;
>> +
>> +     return cpu == 0 ? -EPERM : 0;
>> +}
>> +#endif
>
> Please leave this in hotplug.c so that it matches what other platforms
> do.  Otherwise it's likely to be missed by any updates that happen
> architecture-wide.

Will do

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 13/28] ARM: tegra: Add suspend and hotplug support
@ 2011-01-24  9:26       ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24  9:26 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Jan 24, 2011 at 1:07 AM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Sun, Jan 23, 2011 at 06:01:18PM -0800, Colin Cross wrote:
>> Tegra supports three low power modes that involve powering down the CPU.
>
> It is a common misconception that on CPU hot-unplug you need to save the
> entire universe of the CPU going down.
>
> When CPUs are hot-plugged, they reinitialize, which includes resetting
> most of the CPU state.

The hotplug code path on Tegra currently shares the same path as cpu
idle, because the CPU ends up in the same state for both.  This
version of the patch ensures that when the cpu is brought back online,
it goes through the normal reset vector.  In hotplug, the state ends
up getting saved on the way down, but not used on the way back up.

> There is no point saving the user register state, svc register state,
> abort register state, interrupt register state, undefined mode register
> state, TTB base registers, TTB control register, memory type remapping
> registers nor the context register. ?These are all reinitialized when
> the CPU is brought up again.

The register state is fixed in this version of the patch by calling
cpu_init() in the resume and idle code.  I'll look again if there is a
better way of handling the rest of the registers, but this code is
mostly needed for the suspend and idle cases where the normal boot
process is not used.

> VFP state should already be saved - when a CPU is hot-unplugged, we can't
> leave a threads VFP register set stored in that hardware as it would
> become inaccessible, so there's no point saving VFP state. ?Recently
> code was added to invalidate the software VFP state for a CPU being
> hot-unplugged and reinitialize the VFP hardware on CPU hotplug.

Despite what the patch description says, this version of the patch
does not save VFP state any more.  I will update the description.  It
looks like if CONFIG_SMP=y, the VFP code saves the VFP context on
every task switch, so nothing is needed during cpu idle.  However, if
CONFIG_SMP=n, I think some way to save the VFP context is needed.

> We also have the necessary hooks into the PM code to deal with VFP state
> saving/restore on PM suspend/resume.
>
> The breakpoint/watchdog stuff _should_ be handled by the breakpoint/
> watchdog code, not by each individual platform. ?Please can you work
> with Will Deacon if this is not the case to ensure that the breakpoint/
> watchdog code can handle CPU hotplug.

This version of the patch does not save breakpoint/watchdogs, but
something will need to save them during idle when the cpu is powered
down.

> Lastly, L2 cache controller should have suspend/resume support added.

Patch 4 in this series improves the L2 controller's PM support, and
this patch uses l2x0_disable, l2x0_enable, and l2x0_init.

> We _really_ need to stop having platforms manually saving lots of CPU
> state for suspend/hot unplug.
>
> That should reduce the amount of state you're saving down to something
> more manageable.
>
>> +
>> +#ifdef CONFIG_HOTPLUG_CPU
>> +
>> +int platform_cpu_kill(unsigned int cpu)
>> +{
>> + ? ? unsigned int reg;
>> +
>> + ? ? do {
>> + ? ? ? ? ? ? reg = readl(CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET);
>> + ? ? ? ? ? ? cpu_relax();
>> + ? ? } while (!(reg & (1<<cpu)));
>> +
>> + ? ? spin_lock(&boot_lock);
>> + ? ? reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
>> + ? ? writel(reg | (1<<(8+cpu)), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
>> + ? ? spin_unlock(&boot_lock);
>> +
>> + ? ? return 1;
>> +}
>> +
>> +void platform_cpu_die(unsigned int cpu)
>> +{
>> +#ifdef DEBUG
>> + ? ? unsigned int this_cpu = hard_smp_processor_id();
>> +
>> + ? ? if (cpu != this_cpu) {
>> + ? ? ? ? ? ? printk(KERN_CRIT "Eek! platform_cpu_die running on %u, should be %u\n",
>> + ? ? ? ? ? ? ? ? ? ? ? ?this_cpu, cpu);
>> + ? ? ? ? ? ? BUG();
>> + ? ? }
>> +#endif
>> +
>> + ? ? gic_cpu_exit(0);
>> + ? ? barrier();
>> + ? ? flush_cache_all();
>> + ? ? barrier();
>> + ? ? __cortex_a9_save(0);
>> +
>> + ? ? /*
>> + ? ? ?* __cortex_a9_save can return through __cortex_a9_restore, but that
>> + ? ? ?* should never happen for a hotplugged cpu
>> + ? ? ?*/
>> + ? ? BUG();
>> +}
>> +
>> +int platform_cpu_disable(unsigned int cpu)
>> +{
>> + ? ? /*
>> + ? ? ?* we don't allow CPU 0 to be shutdown (it is still too special
>> + ? ? ?* e.g. clock tick interrupts)
>> + ? ? ?*/
>> + ? ? if (unlikely(!tegra_context_area))
>> + ? ? ? ? ? ? return -ENXIO;
>> +
>> + ? ? return cpu == 0 ? -EPERM : 0;
>> +}
>> +#endif
>
> Please leave this in hotplug.c so that it matches what other platforms
> do. ?Otherwise it's likely to be missed by any updates that happen
> architecture-wide.

Will do

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 13/28] ARM: tegra: Add suspend and hotplug support
  2011-01-24  9:26       ` Colin Cross
@ 2011-01-24 10:15         ` Russell King - ARM Linux
  -1 siblings, 0 replies; 137+ messages in thread
From: Russell King - ARM Linux @ 2011-01-24 10:15 UTC (permalink / raw)
  To: Colin Cross
  Cc: linux-tegra, konkers, Gary King, linux-kernel, olof, linux-arm-kernel

On Mon, Jan 24, 2011 at 01:26:39AM -0800, Colin Cross wrote:
> Despite what the patch description says, this version of the patch
> does not save VFP state any more.  I will update the description.

I was going by the definitions of the context save area.

> It looks like if CONFIG_SMP=y, the VFP code saves the VFP context on
> every task switch, so nothing is needed during cpu idle.  However, if
> CONFIG_SMP=n, I think some way to save the VFP context is needed.

I think we need proper idle PM support as I think this will become
commonplace.

> This version of the patch does not save breakpoint/watchdogs, but
> something will need to save them during idle when the cpu is powered
> down.

Ditto.

> > Lastly, L2 cache controller should have suspend/resume support added.
> 
> Patch 4 in this series improves the L2 controller's PM support, and
> this patch uses l2x0_disable, l2x0_enable, and l2x0_init.

I was going by the definitions of the context save state - maybe the
ones which aren't used anymore should be removed?

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 13/28] ARM: tegra: Add suspend and hotplug support
@ 2011-01-24 10:15         ` Russell King - ARM Linux
  0 siblings, 0 replies; 137+ messages in thread
From: Russell King - ARM Linux @ 2011-01-24 10:15 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Jan 24, 2011 at 01:26:39AM -0800, Colin Cross wrote:
> Despite what the patch description says, this version of the patch
> does not save VFP state any more.  I will update the description.

I was going by the definitions of the context save area.

> It looks like if CONFIG_SMP=y, the VFP code saves the VFP context on
> every task switch, so nothing is needed during cpu idle.  However, if
> CONFIG_SMP=n, I think some way to save the VFP context is needed.

I think we need proper idle PM support as I think this will become
commonplace.

> This version of the patch does not save breakpoint/watchdogs, but
> something will need to save them during idle when the cpu is powered
> down.

Ditto.

> > Lastly, L2 cache controller should have suspend/resume support added.
> 
> Patch 4 in this series improves the L2 controller's PM support, and
> this patch uses l2x0_disable, l2x0_enable, and l2x0_init.

I was going by the definitions of the context save state - maybe the
ones which aren't used anymore should be removed?

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 28/28] ARM: tegra: clock: Add forward reference to struct clk
  2011-01-24  2:01   ` Colin Cross
@ 2011-01-24 10:26     ` Russell King - ARM Linux
  -1 siblings, 0 replies; 137+ messages in thread
From: Russell King - ARM Linux @ 2011-01-24 10:26 UTC (permalink / raw)
  To: Colin Cross; +Cc: linux-tegra, konkers, linux-kernel, olof, linux-arm-kernel

On Sun, Jan 23, 2011 at 06:01:33PM -0800, Colin Cross wrote:
> diff --git a/arch/arm/mach-tegra/include/mach/clkdev.h b/arch/arm/mach-tegra/include/mach/clkdev.h
> index 412f5c6..66cd3f4 100644
> --- a/arch/arm/mach-tegra/include/mach/clkdev.h
> +++ b/arch/arm/mach-tegra/include/mach/clkdev.h
> @@ -20,6 +20,8 @@
>  #ifndef __MACH_CLKDEV_H
>  #define __MACH_CLKDEV_H
>  
> +struct clk;
> +
>  static inline int __clk_get(struct clk *clk)
>  {
>  	return 1;

If you're having to add that, I suspect this include is being
inappropriately included by files.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 28/28] ARM: tegra: clock: Add forward reference to struct clk
@ 2011-01-24 10:26     ` Russell King - ARM Linux
  0 siblings, 0 replies; 137+ messages in thread
From: Russell King - ARM Linux @ 2011-01-24 10:26 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, Jan 23, 2011 at 06:01:33PM -0800, Colin Cross wrote:
> diff --git a/arch/arm/mach-tegra/include/mach/clkdev.h b/arch/arm/mach-tegra/include/mach/clkdev.h
> index 412f5c6..66cd3f4 100644
> --- a/arch/arm/mach-tegra/include/mach/clkdev.h
> +++ b/arch/arm/mach-tegra/include/mach/clkdev.h
> @@ -20,6 +20,8 @@
>  #ifndef __MACH_CLKDEV_H
>  #define __MACH_CLKDEV_H
>  
> +struct clk;
> +
>  static inline int __clk_get(struct clk *clk)
>  {
>  	return 1;

If you're having to add that, I suspect this include is being
inappropriately included by files.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 20/28] ARM: tegra: cpufreq: Disable cpufreq during suspend
  2011-01-24  2:01   ` Colin Cross
@ 2011-01-24 14:41     ` Mark Brown
  -1 siblings, 0 replies; 137+ messages in thread
From: Mark Brown @ 2011-01-24 14:41 UTC (permalink / raw)
  To: Colin Cross
  Cc: linux-tegra, Russell King, konkers, linux-kernel, olof, linux-arm-kernel

On Sun, Jan 23, 2011 at 06:01:25PM -0800, Colin Cross wrote:
> Adds a SUSPEND_PREPARE notification hook to drop the frequency to
> the lowest possible during suspend.  This prevents the cpufreq driver
> from attempting regulator calls after suspend has started - the
> regulator api can call into drivers that have already been suspended.

Hrm, what's the situation where that happens and why does it cause
problems?  The regulator API doesn't care if suspend is going on, and
nor do any of the current drivers for regulators.  There is an issue
with keeping things like I2C alive until the bitter end of suspend so
you've got a control bus to the regulators but that's a generic issue
which crops up with other subsystems too so a regulator-specific
workaround seems dodgy.

The patch itself seems fine, it's just that it feels like there's
something else going on.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 20/28] ARM: tegra: cpufreq: Disable cpufreq during suspend
@ 2011-01-24 14:41     ` Mark Brown
  0 siblings, 0 replies; 137+ messages in thread
From: Mark Brown @ 2011-01-24 14:41 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, Jan 23, 2011 at 06:01:25PM -0800, Colin Cross wrote:
> Adds a SUSPEND_PREPARE notification hook to drop the frequency to
> the lowest possible during suspend.  This prevents the cpufreq driver
> from attempting regulator calls after suspend has started - the
> regulator api can call into drivers that have already been suspended.

Hrm, what's the situation where that happens and why does it cause
problems?  The regulator API doesn't care if suspend is going on, and
nor do any of the current drivers for regulators.  There is an issue
with keeping things like I2C alive until the bitter end of suspend so
you've got a control bus to the regulators but that's a generic issue
which crops up with other subsystems too so a regulator-specific
workaround seems dodgy.

The patch itself seems fine, it's just that it feels like there's
something else going on.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 20/28] ARM: tegra: cpufreq: Disable cpufreq during suspend
  2011-01-24 14:41     ` Mark Brown
@ 2011-01-24 18:50       ` Colin Cross
  -1 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24 18:50 UTC (permalink / raw)
  To: Mark Brown
  Cc: linux-tegra, Russell King, konkers, linux-kernel, olof, linux-arm-kernel

On Mon, Jan 24, 2011 at 6:41 AM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
> On Sun, Jan 23, 2011 at 06:01:25PM -0800, Colin Cross wrote:
>> Adds a SUSPEND_PREPARE notification hook to drop the frequency to
>> the lowest possible during suspend.  This prevents the cpufreq driver
>> from attempting regulator calls after suspend has started - the
>> regulator api can call into drivers that have already been suspended.
>
> Hrm, what's the situation where that happens and why does it cause
> problems?  The regulator API doesn't care if suspend is going on, and
> nor do any of the current drivers for regulators.  There is an issue
> with keeping things like I2C alive until the bitter end of suspend so
> you've got a control bus to the regulators but that's a generic issue
> which crops up with other subsystems too so a regulator-specific
> workaround seems dodgy.

The end result is that the regulator driver calls the i2c driver after
the i2c driver has been suspended.  On Tegra, this happens because of
voltage scaling on the CPU regulator, which can be on the i2c bus.  A
sleep in a suspend handler after the i2c bus has been suspended can
cause the cpufreq governor to lower the frequency, and try to lower
the voltage as well.

The problem isn't limited to i2c busses, there are some regulators on
spi busses.  You would need to ensure that any driver that has a
regulator consumer suspends before the regulator driver it
communicates with, and that still wouldn't fix cpufreq, which has a
sysdev suspend handler.

> The patch itself seems fine, it's just that it feels like there's
> something else going on.
>

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 20/28] ARM: tegra: cpufreq: Disable cpufreq during suspend
@ 2011-01-24 18:50       ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24 18:50 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Jan 24, 2011 at 6:41 AM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
> On Sun, Jan 23, 2011 at 06:01:25PM -0800, Colin Cross wrote:
>> Adds a SUSPEND_PREPARE notification hook to drop the frequency to
>> the lowest possible during suspend. ?This prevents the cpufreq driver
>> from attempting regulator calls after suspend has started - the
>> regulator api can call into drivers that have already been suspended.
>
> Hrm, what's the situation where that happens and why does it cause
> problems? ?The regulator API doesn't care if suspend is going on, and
> nor do any of the current drivers for regulators. ?There is an issue
> with keeping things like I2C alive until the bitter end of suspend so
> you've got a control bus to the regulators but that's a generic issue
> which crops up with other subsystems too so a regulator-specific
> workaround seems dodgy.

The end result is that the regulator driver calls the i2c driver after
the i2c driver has been suspended.  On Tegra, this happens because of
voltage scaling on the CPU regulator, which can be on the i2c bus.  A
sleep in a suspend handler after the i2c bus has been suspended can
cause the cpufreq governor to lower the frequency, and try to lower
the voltage as well.

The problem isn't limited to i2c busses, there are some regulators on
spi busses.  You would need to ensure that any driver that has a
regulator consumer suspends before the regulator driver it
communicates with, and that still wouldn't fix cpufreq, which has a
sysdev suspend handler.

> The patch itself seems fine, it's just that it feels like there's
> something else going on.
>

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 20/28] ARM: tegra: cpufreq: Disable cpufreq during suspend
  2011-01-24 18:50       ` Colin Cross
@ 2011-01-24 19:35         ` Mark Brown
  -1 siblings, 0 replies; 137+ messages in thread
From: Mark Brown @ 2011-01-24 19:35 UTC (permalink / raw)
  To: Colin Cross
  Cc: linux-tegra, Russell King, konkers, linux-kernel, olof, linux-arm-kernel

On Mon, Jan 24, 2011 at 10:50:53AM -0800, Colin Cross wrote:
> On Mon, Jan 24, 2011 at 6:41 AM, Mark Brown

> > Hrm, what's the situation where that happens and why does it cause
> > problems?  The regulator API doesn't care if suspend is going on, and
> > nor do any of the current drivers for regulators.  There is an issue
> > with keeping things like I2C alive until the bitter end of suspend so
> > you've got a control bus to the regulators but that's a generic issue
> > which crops up with other subsystems too so a regulator-specific
> > workaround seems dodgy.

> The end result is that the regulator driver calls the i2c driver after
> the i2c driver has been suspended.  On Tegra, this happens because of
> voltage scaling on the CPU regulator, which can be on the i2c bus.  A
> sleep in a suspend handler after the i2c bus has been suspended can
> cause the cpufreq governor to lower the frequency, and try to lower
> the voltage as well.

OK, that's a general problem - you need to ensure that the I2C and SPI
controllers are suspended really late.  The OMAP guys also have this
problem for some RTCs, though in their case things are slightly
different due to their use of runtime PM.  Looking at your I2C driver
(BTW, I'd suggest reminding Ben on a more regular basis about that) I
suspect that moving your suspend and resume callbacks to the _noirq
varaints will cover a lot of this.

> The problem isn't limited to i2c busses, there are some regulators on
> spi busses.  You would need to ensure that any driver that has a

For all practical purposes in this sort of discussion you can typically
do a s/I2C/I2C and SPI/ - in terms of system integration issues they're
very similar.

> regulator consumer suspends before the regulator driver it
> communicates with, and that still wouldn't fix cpufreq, which has a
> sysdev suspend handler.

So you actively need to push the processor into low power mode during
suspend?  I'm still not 100% clear what's triggering the issues you're
seeing and why this seems to be Tegra-only.  If there were a general
cpufreq/regulator interaction here I'd expect to see all ARM cpufreq
drivers needing to do the same thing (and probably some handling of this
in cpufreq core as a result).  If it's not such an issue I'd expect
there's also entertaining suspend ordering issues elsewhere.

Typically this stuff isn't a problem for regulators themselves since if
they're active by the time I2C is off they normally get suspended by
hardware handshakes from the CPU as that enters low power mode - right
now we have no regulators at all in mainline that do anything in
software on suspend.  We can get a long way by just ignoring what
happens to regulators over suspend (there is some work to do here but
it's orthogonal to this sort of issue).

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 20/28] ARM: tegra: cpufreq: Disable cpufreq during suspend
@ 2011-01-24 19:35         ` Mark Brown
  0 siblings, 0 replies; 137+ messages in thread
From: Mark Brown @ 2011-01-24 19:35 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Jan 24, 2011 at 10:50:53AM -0800, Colin Cross wrote:
> On Mon, Jan 24, 2011 at 6:41 AM, Mark Brown

> > Hrm, what's the situation where that happens and why does it cause
> > problems? ?The regulator API doesn't care if suspend is going on, and
> > nor do any of the current drivers for regulators. ?There is an issue
> > with keeping things like I2C alive until the bitter end of suspend so
> > you've got a control bus to the regulators but that's a generic issue
> > which crops up with other subsystems too so a regulator-specific
> > workaround seems dodgy.

> The end result is that the regulator driver calls the i2c driver after
> the i2c driver has been suspended.  On Tegra, this happens because of
> voltage scaling on the CPU regulator, which can be on the i2c bus.  A
> sleep in a suspend handler after the i2c bus has been suspended can
> cause the cpufreq governor to lower the frequency, and try to lower
> the voltage as well.

OK, that's a general problem - you need to ensure that the I2C and SPI
controllers are suspended really late.  The OMAP guys also have this
problem for some RTCs, though in their case things are slightly
different due to their use of runtime PM.  Looking at your I2C driver
(BTW, I'd suggest reminding Ben on a more regular basis about that) I
suspect that moving your suspend and resume callbacks to the _noirq
varaints will cover a lot of this.

> The problem isn't limited to i2c busses, there are some regulators on
> spi busses.  You would need to ensure that any driver that has a

For all practical purposes in this sort of discussion you can typically
do a s/I2C/I2C and SPI/ - in terms of system integration issues they're
very similar.

> regulator consumer suspends before the regulator driver it
> communicates with, and that still wouldn't fix cpufreq, which has a
> sysdev suspend handler.

So you actively need to push the processor into low power mode during
suspend?  I'm still not 100% clear what's triggering the issues you're
seeing and why this seems to be Tegra-only.  If there were a general
cpufreq/regulator interaction here I'd expect to see all ARM cpufreq
drivers needing to do the same thing (and probably some handling of this
in cpufreq core as a result).  If it's not such an issue I'd expect
there's also entertaining suspend ordering issues elsewhere.

Typically this stuff isn't a problem for regulators themselves since if
they're active by the time I2C is off they normally get suspended by
hardware handshakes from the CPU as that enters low power mode - right
now we have no regulators at all in mainline that do anything in
software on suspend.  We can get a long way by just ignoring what
happens to regulators over suspend (there is some work to do here but
it's orthogonal to this sort of issue).

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 20/28] ARM: tegra: cpufreq: Disable cpufreq during suspend
  2011-01-24 19:35         ` Mark Brown
@ 2011-01-24 19:52           ` Colin Cross
  -1 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24 19:52 UTC (permalink / raw)
  To: Mark Brown
  Cc: linux-tegra, Russell King, konkers, linux-kernel, olof, linux-arm-kernel

On Mon, Jan 24, 2011 at 11:35 AM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
> On Mon, Jan 24, 2011 at 10:50:53AM -0800, Colin Cross wrote:
>> On Mon, Jan 24, 2011 at 6:41 AM, Mark Brown
>
>> > Hrm, what's the situation where that happens and why does it cause
>> > problems?  The regulator API doesn't care if suspend is going on, and
>> > nor do any of the current drivers for regulators.  There is an issue
>> > with keeping things like I2C alive until the bitter end of suspend so
>> > you've got a control bus to the regulators but that's a generic issue
>> > which crops up with other subsystems too so a regulator-specific
>> > workaround seems dodgy.
>
>> The end result is that the regulator driver calls the i2c driver after
>> the i2c driver has been suspended.  On Tegra, this happens because of
>> voltage scaling on the CPU regulator, which can be on the i2c bus.  A
>> sleep in a suspend handler after the i2c bus has been suspended can
>> cause the cpufreq governor to lower the frequency, and try to lower
>> the voltage as well.
>
> OK, that's a general problem - you need to ensure that the I2C and SPI
> controllers are suspended really late.  The OMAP guys also have this
> problem for some RTCs, though in their case things are slightly
> different due to their use of runtime PM.  Looking at your I2C driver
> (BTW, I'd suggest reminding Ben on a more regular basis about that) I
> suspect that moving your suspend and resume callbacks to the _noirq
> varaints will cover a lot of this.

Even _noirq isn't late enough, if cpufreq keeps trying to change the
frequency (and thus voltage) until sysdev suspend.

>> The problem isn't limited to i2c busses, there are some regulators on
>> spi busses.  You would need to ensure that any driver that has a
>
> For all practical purposes in this sort of discussion you can typically
> do a s/I2C/I2C and SPI/ - in terms of system integration issues they're
> very similar.

True

>> regulator consumer suspends before the regulator driver it
>> communicates with, and that still wouldn't fix cpufreq, which has a
>> sysdev suspend handler.
>
> So you actively need to push the processor into low power mode during
> suspend?  I'm still not 100% clear what's triggering the issues you're
> seeing and why this seems to be Tegra-only.  If there were a general
> cpufreq/regulator interaction here I'd expect to see all ARM cpufreq
> drivers needing to do the same thing (and probably some handling of this
> in cpufreq core as a result).  If it's not such an issue I'd expect
> there's also entertaining suspend ordering issues elsewhere.

It's more of a voltage scaling issue than a cpufreq issue directly.
Tegra requires the voltage be set to nominal during resume, and the
only time it can be set for resume is before I2C suspends.  I handle
the problem with a suspend notifier in the latest version of the clock
voltage scaling patches, but I kept this patch to avoid cpufreq trying
to go to frequencies that are not supported by the suspend voltage.

> Typically this stuff isn't a problem for regulators themselves since if
> they're active by the time I2C is off they normally get suspended by
> hardware handshakes from the CPU as that enters low power mode - right
> now we have no regulators at all in mainline that do anything in
> software on suspend.  We can get a long way by just ignoring what
> happens to regulators over suspend (there is some work to do here but
> it's orthogonal to this sort of issue).

The regulator driver itself has nothing to do, and when the CPU enters
its lowest power mode it signals the regulator to turn off, but
something needs to tell the regulator to go to the correct voltage.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 20/28] ARM: tegra: cpufreq: Disable cpufreq during suspend
@ 2011-01-24 19:52           ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24 19:52 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Jan 24, 2011 at 11:35 AM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
> On Mon, Jan 24, 2011 at 10:50:53AM -0800, Colin Cross wrote:
>> On Mon, Jan 24, 2011 at 6:41 AM, Mark Brown
>
>> > Hrm, what's the situation where that happens and why does it cause
>> > problems? ?The regulator API doesn't care if suspend is going on, and
>> > nor do any of the current drivers for regulators. ?There is an issue
>> > with keeping things like I2C alive until the bitter end of suspend so
>> > you've got a control bus to the regulators but that's a generic issue
>> > which crops up with other subsystems too so a regulator-specific
>> > workaround seems dodgy.
>
>> The end result is that the regulator driver calls the i2c driver after
>> the i2c driver has been suspended. ?On Tegra, this happens because of
>> voltage scaling on the CPU regulator, which can be on the i2c bus. ?A
>> sleep in a suspend handler after the i2c bus has been suspended can
>> cause the cpufreq governor to lower the frequency, and try to lower
>> the voltage as well.
>
> OK, that's a general problem - you need to ensure that the I2C and SPI
> controllers are suspended really late. ?The OMAP guys also have this
> problem for some RTCs, though in their case things are slightly
> different due to their use of runtime PM. ?Looking at your I2C driver
> (BTW, I'd suggest reminding Ben on a more regular basis about that) I
> suspect that moving your suspend and resume callbacks to the _noirq
> varaints will cover a lot of this.

Even _noirq isn't late enough, if cpufreq keeps trying to change the
frequency (and thus voltage) until sysdev suspend.

>> The problem isn't limited to i2c busses, there are some regulators on
>> spi busses. ?You would need to ensure that any driver that has a
>
> For all practical purposes in this sort of discussion you can typically
> do a s/I2C/I2C and SPI/ - in terms of system integration issues they're
> very similar.

True

>> regulator consumer suspends before the regulator driver it
>> communicates with, and that still wouldn't fix cpufreq, which has a
>> sysdev suspend handler.
>
> So you actively need to push the processor into low power mode during
> suspend? ?I'm still not 100% clear what's triggering the issues you're
> seeing and why this seems to be Tegra-only. ?If there were a general
> cpufreq/regulator interaction here I'd expect to see all ARM cpufreq
> drivers needing to do the same thing (and probably some handling of this
> in cpufreq core as a result). ?If it's not such an issue I'd expect
> there's also entertaining suspend ordering issues elsewhere.

It's more of a voltage scaling issue than a cpufreq issue directly.
Tegra requires the voltage be set to nominal during resume, and the
only time it can be set for resume is before I2C suspends.  I handle
the problem with a suspend notifier in the latest version of the clock
voltage scaling patches, but I kept this patch to avoid cpufreq trying
to go to frequencies that are not supported by the suspend voltage.

> Typically this stuff isn't a problem for regulators themselves since if
> they're active by the time I2C is off they normally get suspended by
> hardware handshakes from the CPU as that enters low power mode - right
> now we have no regulators at all in mainline that do anything in
> software on suspend. ?We can get a long way by just ignoring what
> happens to regulators over suspend (there is some work to do here but
> it's orthogonal to this sort of issue).

The regulator driver itself has nothing to do, and when the CPU enters
its lowest power mode it signals the regulator to turn off, but
something needs to tell the regulator to go to the correct voltage.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 20/28] ARM: tegra: cpufreq: Disable cpufreq during suspend
  2011-01-24 19:52           ` Colin Cross
@ 2011-01-24 20:26             ` Mark Brown
  -1 siblings, 0 replies; 137+ messages in thread
From: Mark Brown @ 2011-01-24 20:26 UTC (permalink / raw)
  To: Colin Cross
  Cc: linux-tegra, Russell King, konkers, linux-kernel, olof, linux-arm-kernel

On Mon, Jan 24, 2011 at 11:52:12AM -0800, Colin Cross wrote:

> Even _noirq isn't late enough, if cpufreq keeps trying to change the
> frequency (and thus voltage) until sysdev suspend.

If it were just cpufreq in isolation I'm not sure it'd be a big deal -
nothing will go hideously wrong if it's ticking away by itself providing
it's just governors working away providing the error handling is OK.
However...

> On Mon, Jan 24, 2011 at 11:35 AM, Mark Brown

> > So you actively need to push the processor into low power mode during
> > suspend?  I'm still not 100% clear what's triggering the issues you're

> It's more of a voltage scaling issue than a cpufreq issue directly.
> Tegra requires the voltage be set to nominal during resume, and the
> only time it can be set for resume is before I2C suspends.  I handle
> the problem with a suspend notifier in the latest version of the clock
> voltage scaling patches, but I kept this patch to avoid cpufreq trying
> to go to frequencies that are not supported by the suspend voltage.

...it sounds like you do actually have something of the above issue - 
but I think it's a generic-ish problem.  See below.

> > Typically this stuff isn't a problem for regulators themselves since if
> > they're active by the time I2C is off they normally get suspended by
> > hardware handshakes from the CPU as that enters low power mode - right
> > now we have no regulators at all in mainline that do anything in
> > software on suspend.  We can get a long way by just ignoring what
> > happens to regulators over suspend (there is some work to do here but
> > it's orthogonal to this sort of issue).

> The regulator driver itself has nothing to do, and when the CPU enters
> its lowest power mode it signals the regulator to turn off, but
> something needs to tell the regulator to go to the correct voltage.

Normally that's handled by the same transition logic - the core PMIC
will usually sequence a bunch of voltage and power status changes when
entering its own low power mode and have a similar transition on resume
which will ensure that the regulators power back up with a sane setup
regardless of the state they were in beforehand.

It sounds like for your systems what's happening is that the resume is
restoring the previously configured voltages for the core rails rather
than going to a known good state.  Is my understanding correct here?
While your fix is good and I don't see any reason not to do it this does
sound like something we should have a solution for in common code as I'd
not be surprised if there were other hardware out there which did a
similar thing.

Assuming I'm not completely off base with the above it'd be good if you
could clarify this in the commit message - there's enough dragons with
this stuff and it's common to refer to other implementations so it'd be
nice if we had a clear record of the issue in the log.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 20/28] ARM: tegra: cpufreq: Disable cpufreq during suspend
@ 2011-01-24 20:26             ` Mark Brown
  0 siblings, 0 replies; 137+ messages in thread
From: Mark Brown @ 2011-01-24 20:26 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Jan 24, 2011 at 11:52:12AM -0800, Colin Cross wrote:

> Even _noirq isn't late enough, if cpufreq keeps trying to change the
> frequency (and thus voltage) until sysdev suspend.

If it were just cpufreq in isolation I'm not sure it'd be a big deal -
nothing will go hideously wrong if it's ticking away by itself providing
it's just governors working away providing the error handling is OK.
However...

> On Mon, Jan 24, 2011 at 11:35 AM, Mark Brown

> > So you actively need to push the processor into low power mode during
> > suspend? ?I'm still not 100% clear what's triggering the issues you're

> It's more of a voltage scaling issue than a cpufreq issue directly.
> Tegra requires the voltage be set to nominal during resume, and the
> only time it can be set for resume is before I2C suspends.  I handle
> the problem with a suspend notifier in the latest version of the clock
> voltage scaling patches, but I kept this patch to avoid cpufreq trying
> to go to frequencies that are not supported by the suspend voltage.

...it sounds like you do actually have something of the above issue - 
but I think it's a generic-ish problem.  See below.

> > Typically this stuff isn't a problem for regulators themselves since if
> > they're active by the time I2C is off they normally get suspended by
> > hardware handshakes from the CPU as that enters low power mode - right
> > now we have no regulators at all in mainline that do anything in
> > software on suspend. ?We can get a long way by just ignoring what
> > happens to regulators over suspend (there is some work to do here but
> > it's orthogonal to this sort of issue).

> The regulator driver itself has nothing to do, and when the CPU enters
> its lowest power mode it signals the regulator to turn off, but
> something needs to tell the regulator to go to the correct voltage.

Normally that's handled by the same transition logic - the core PMIC
will usually sequence a bunch of voltage and power status changes when
entering its own low power mode and have a similar transition on resume
which will ensure that the regulators power back up with a sane setup
regardless of the state they were in beforehand.

It sounds like for your systems what's happening is that the resume is
restoring the previously configured voltages for the core rails rather
than going to a known good state.  Is my understanding correct here?
While your fix is good and I don't see any reason not to do it this does
sound like something we should have a solution for in common code as I'd
not be surprised if there were other hardware out there which did a
similar thing.

Assuming I'm not completely off base with the above it'd be good if you
could clarify this in the commit message - there's enough dragons with
this stuff and it's common to refer to other implementations so it'd be
nice if we had a clear record of the issue in the log.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 20/28] ARM: tegra: cpufreq: Disable cpufreq during suspend
  2011-01-24 20:26             ` Mark Brown
@ 2011-01-24 20:52               ` Colin Cross
  -1 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24 20:52 UTC (permalink / raw)
  To: Mark Brown
  Cc: linux-tegra, Russell King, konkers, linux-kernel, olof, linux-arm-kernel

On Mon, Jan 24, 2011 at 12:26 PM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
> On Mon, Jan 24, 2011 at 11:52:12AM -0800, Colin Cross wrote:
>
>> Even _noirq isn't late enough, if cpufreq keeps trying to change the
>> frequency (and thus voltage) until sysdev suspend.
>
> If it were just cpufreq in isolation I'm not sure it'd be a big deal -
> nothing will go hideously wrong if it's ticking away by itself providing
> it's just governors working away providing the error handling is OK.
> However...
>
>> On Mon, Jan 24, 2011 at 11:35 AM, Mark Brown
>
>> > So you actively need to push the processor into low power mode during
>> > suspend?  I'm still not 100% clear what's triggering the issues you're
>
>> It's more of a voltage scaling issue than a cpufreq issue directly.
>> Tegra requires the voltage be set to nominal during resume, and the
>> only time it can be set for resume is before I2C suspends.  I handle
>> the problem with a suspend notifier in the latest version of the clock
>> voltage scaling patches, but I kept this patch to avoid cpufreq trying
>> to go to frequencies that are not supported by the suspend voltage.
>
> ...it sounds like you do actually have something of the above issue -
> but I think it's a generic-ish problem.  See below.
>
>> > Typically this stuff isn't a problem for regulators themselves since if
>> > they're active by the time I2C is off they normally get suspended by
>> > hardware handshakes from the CPU as that enters low power mode - right
>> > now we have no regulators at all in mainline that do anything in
>> > software on suspend.  We can get a long way by just ignoring what
>> > happens to regulators over suspend (there is some work to do here but
>> > it's orthogonal to this sort of issue).
>
>> The regulator driver itself has nothing to do, and when the CPU enters
>> its lowest power mode it signals the regulator to turn off, but
>> something needs to tell the regulator to go to the correct voltage.
>
> Normally that's handled by the same transition logic - the core PMIC
> will usually sequence a bunch of voltage and power status changes when
> entering its own low power mode and have a similar transition on resume
> which will ensure that the regulators power back up with a sane setup
> regardless of the state they were in beforehand.

I believe Tegra supports this mode, where the Tegra PMC can master the
I2C bus to control an external PMIC during resume, but I've never seen
it used.

> It sounds like for your systems what's happening is that the resume is
> restoring the previously configured voltages for the core rails rather
> than going to a known good state.  Is my understanding correct here?
> While your fix is good and I don't see any reason not to do it this does
> sound like something we should have a solution for in common code as I'd
> not be surprised if there were other hardware out there which did a
> similar thing.

Yes - the core can be connected to a dumb I2C regulator, which the
core can turn on and off with a request line, but can't control the
voltage without a regulator driver controlling the I2C bus.

> Assuming I'm not completely off base with the above it'd be good if you
> could clarify this in the commit message - there's enough dragons with
> this stuff and it's common to refer to other implementations so it'd be
> nice if we had a clear record of the issue in the log.

Will do

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 20/28] ARM: tegra: cpufreq: Disable cpufreq during suspend
@ 2011-01-24 20:52               ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24 20:52 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Jan 24, 2011 at 12:26 PM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
> On Mon, Jan 24, 2011 at 11:52:12AM -0800, Colin Cross wrote:
>
>> Even _noirq isn't late enough, if cpufreq keeps trying to change the
>> frequency (and thus voltage) until sysdev suspend.
>
> If it were just cpufreq in isolation I'm not sure it'd be a big deal -
> nothing will go hideously wrong if it's ticking away by itself providing
> it's just governors working away providing the error handling is OK.
> However...
>
>> On Mon, Jan 24, 2011 at 11:35 AM, Mark Brown
>
>> > So you actively need to push the processor into low power mode during
>> > suspend? ?I'm still not 100% clear what's triggering the issues you're
>
>> It's more of a voltage scaling issue than a cpufreq issue directly.
>> Tegra requires the voltage be set to nominal during resume, and the
>> only time it can be set for resume is before I2C suspends. ?I handle
>> the problem with a suspend notifier in the latest version of the clock
>> voltage scaling patches, but I kept this patch to avoid cpufreq trying
>> to go to frequencies that are not supported by the suspend voltage.
>
> ...it sounds like you do actually have something of the above issue -
> but I think it's a generic-ish problem. ?See below.
>
>> > Typically this stuff isn't a problem for regulators themselves since if
>> > they're active by the time I2C is off they normally get suspended by
>> > hardware handshakes from the CPU as that enters low power mode - right
>> > now we have no regulators at all in mainline that do anything in
>> > software on suspend. ?We can get a long way by just ignoring what
>> > happens to regulators over suspend (there is some work to do here but
>> > it's orthogonal to this sort of issue).
>
>> The regulator driver itself has nothing to do, and when the CPU enters
>> its lowest power mode it signals the regulator to turn off, but
>> something needs to tell the regulator to go to the correct voltage.
>
> Normally that's handled by the same transition logic - the core PMIC
> will usually sequence a bunch of voltage and power status changes when
> entering its own low power mode and have a similar transition on resume
> which will ensure that the regulators power back up with a sane setup
> regardless of the state they were in beforehand.

I believe Tegra supports this mode, where the Tegra PMC can master the
I2C bus to control an external PMIC during resume, but I've never seen
it used.

> It sounds like for your systems what's happening is that the resume is
> restoring the previously configured voltages for the core rails rather
> than going to a known good state. ?Is my understanding correct here?
> While your fix is good and I don't see any reason not to do it this does
> sound like something we should have a solution for in common code as I'd
> not be surprised if there were other hardware out there which did a
> similar thing.

Yes - the core can be connected to a dumb I2C regulator, which the
core can turn on and off with a request line, but can't control the
voltage without a regulator driver controlling the I2C bus.

> Assuming I'm not completely off base with the above it'd be good if you
> could clarify this in the commit message - there's enough dragons with
> this stuff and it's common to refer to other implementations so it'd be
> nice if we had a clear record of the issue in the log.

Will do

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 20/28] ARM: tegra: cpufreq: Disable cpufreq during suspend
  2011-01-24 20:52               ` Colin Cross
@ 2011-01-24 21:08                 ` Mark Brown
  -1 siblings, 0 replies; 137+ messages in thread
From: Mark Brown @ 2011-01-24 21:08 UTC (permalink / raw)
  To: Colin Cross
  Cc: linux-tegra, Russell King, konkers, linux-kernel, olof, linux-arm-kernel

On Mon, Jan 24, 2011 at 12:52:51PM -0800, Colin Cross wrote:
> On Mon, Jan 24, 2011 at 12:26 PM, Mark Brown

> > Normally that's handled by the same transition logic - the core PMIC
> > will usually sequence a bunch of voltage and power status changes when
> > entering its own low power mode and have a similar transition on resume
> > which will ensure that the regulators power back up with a sane setup
> > regardless of the state they were in beforehand.

> I believe Tegra supports this mode, where the Tegra PMC can master the
> I2C bus to control an external PMIC during resume, but I've never seen
> it used.

In almost all cases it's implemented in the PMIC rather than the CPU -
it's essentially the same logic as implements the initial power on of
the system and the PMIC needs to be capable of starting the system boot
without external assistance anyway, including sequencing the power up of
the CPU.

With most PMICs the settings will be hard coded into the silicon so
you really need to get a PMIC specifically designed for whatever CPU
you're using (this is one of the reasons you always see the same
CPU/PMIC combinations, people are terrified of changing a known good
pairing in case they build something that won't power on).  Some more
modern PMICs make this configurable which gives system designers a lot
more flexibility.

The PMC stuff is newer and in the designs I've seen is more focused on
runtime management than this stuff.

> > It sounds like for your systems what's happening is that the resume is
> > restoring the previously configured voltages for the core rails rather
> > than going to a known good state.  Is my understanding correct here?
> > While your fix is good and I don't see any reason not to do it this does
> > sound like something we should have a solution for in common code as I'd
> > not be surprised if there were other hardware out there which did a
> > similar thing.

> Yes - the core can be connected to a dumb I2C regulator, which the
> core can turn on and off with a request line, but can't control the
> voltage without a regulator driver controlling the I2C bus.

AIUI generally they end up working because whatever puts them into
suspend actually resets them so when you release reset during resume
they just recover whatever the default voltage they have is and normally
the boot and resume conditions for the CPU are identical.  I could be
off base there, most of the regulator API stuff has been done with
integrated PMICs.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 20/28] ARM: tegra: cpufreq: Disable cpufreq during suspend
@ 2011-01-24 21:08                 ` Mark Brown
  0 siblings, 0 replies; 137+ messages in thread
From: Mark Brown @ 2011-01-24 21:08 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Jan 24, 2011 at 12:52:51PM -0800, Colin Cross wrote:
> On Mon, Jan 24, 2011 at 12:26 PM, Mark Brown

> > Normally that's handled by the same transition logic - the core PMIC
> > will usually sequence a bunch of voltage and power status changes when
> > entering its own low power mode and have a similar transition on resume
> > which will ensure that the regulators power back up with a sane setup
> > regardless of the state they were in beforehand.

> I believe Tegra supports this mode, where the Tegra PMC can master the
> I2C bus to control an external PMIC during resume, but I've never seen
> it used.

In almost all cases it's implemented in the PMIC rather than the CPU -
it's essentially the same logic as implements the initial power on of
the system and the PMIC needs to be capable of starting the system boot
without external assistance anyway, including sequencing the power up of
the CPU.

With most PMICs the settings will be hard coded into the silicon so
you really need to get a PMIC specifically designed for whatever CPU
you're using (this is one of the reasons you always see the same
CPU/PMIC combinations, people are terrified of changing a known good
pairing in case they build something that won't power on).  Some more
modern PMICs make this configurable which gives system designers a lot
more flexibility.

The PMC stuff is newer and in the designs I've seen is more focused on
runtime management than this stuff.

> > It sounds like for your systems what's happening is that the resume is
> > restoring the previously configured voltages for the core rails rather
> > than going to a known good state. ?Is my understanding correct here?
> > While your fix is good and I don't see any reason not to do it this does
> > sound like something we should have a solution for in common code as I'd
> > not be surprised if there were other hardware out there which did a
> > similar thing.

> Yes - the core can be connected to a dumb I2C regulator, which the
> core can turn on and off with a request line, but can't control the
> voltage without a regulator driver controlling the I2C bus.

AIUI generally they end up working because whatever puts them into
suspend actually resets them so when you release reset during resume
they just recover whatever the default voltage they have is and normally
the boot and resume conditions for the CPU are identical.  I could be
off base there, most of the regulator API stuff has been done with
integrated PMICs.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 20/28] ARM: tegra: cpufreq: Disable cpufreq during suspend
  2011-01-24 21:08                 ` Mark Brown
@ 2011-01-24 21:24                   ` Colin Cross
  -1 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24 21:24 UTC (permalink / raw)
  To: Mark Brown
  Cc: linux-tegra, Russell King, konkers, linux-kernel, olof, linux-arm-kernel

On Mon, Jan 24, 2011 at 1:08 PM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
> On Mon, Jan 24, 2011 at 12:52:51PM -0800, Colin Cross wrote:
>> On Mon, Jan 24, 2011 at 12:26 PM, Mark Brown
>
>> > Normally that's handled by the same transition logic - the core PMIC
>> > will usually sequence a bunch of voltage and power status changes when
>> > entering its own low power mode and have a similar transition on resume
>> > which will ensure that the regulators power back up with a sane setup
>> > regardless of the state they were in beforehand.
>
>> I believe Tegra supports this mode, where the Tegra PMC can master the
>> I2C bus to control an external PMIC during resume, but I've never seen
>> it used.
>
> In almost all cases it's implemented in the PMIC rather than the CPU -
> it's essentially the same logic as implements the initial power on of
> the system and the PMIC needs to be capable of starting the system boot
> without external assistance anyway, including sequencing the power up of
> the CPU.
>
> With most PMICs the settings will be hard coded into the silicon so
> you really need to get a PMIC specifically designed for whatever CPU
> you're using (this is one of the reasons you always see the same
> CPU/PMIC combinations, people are terrified of changing a known good
> pairing in case they build something that won't power on).  Some more
> modern PMICs make this configurable which gives system designers a lot
> more flexibility.

I'm seeing the opposite problem - changing CPUs, and keeping the PMIC.

> The PMC stuff is newer and in the designs I've seen is more focused on
> runtime management than this stuff.
>
>> > It sounds like for your systems what's happening is that the resume is
>> > restoring the previously configured voltages for the core rails rather
>> > than going to a known good state.  Is my understanding correct here?
>> > While your fix is good and I don't see any reason not to do it this does
>> > sound like something we should have a solution for in common code as I'd
>> > not be surprised if there were other hardware out there which did a
>> > similar thing.
>
>> Yes - the core can be connected to a dumb I2C regulator, which the
>> core can turn on and off with a request line, but can't control the
>> voltage without a regulator driver controlling the I2C bus.
>
> AIUI generally they end up working because whatever puts them into
> suspend actually resets them so when you release reset during resume
> they just recover whatever the default voltage they have is and normally
> the boot and resume conditions for the CPU are identical.  I could be
> off base there, most of the regulator API stuff has been done with
> integrated PMICs.

Resetting during suspend would be the sane thing to do, but that's not
what the hardware does.  The regulator also gets turned off during
idle, so it can't just be reset every time it turns off.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 20/28] ARM: tegra: cpufreq: Disable cpufreq during suspend
@ 2011-01-24 21:24                   ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-24 21:24 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Jan 24, 2011 at 1:08 PM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
> On Mon, Jan 24, 2011 at 12:52:51PM -0800, Colin Cross wrote:
>> On Mon, Jan 24, 2011 at 12:26 PM, Mark Brown
>
>> > Normally that's handled by the same transition logic - the core PMIC
>> > will usually sequence a bunch of voltage and power status changes when
>> > entering its own low power mode and have a similar transition on resume
>> > which will ensure that the regulators power back up with a sane setup
>> > regardless of the state they were in beforehand.
>
>> I believe Tegra supports this mode, where the Tegra PMC can master the
>> I2C bus to control an external PMIC during resume, but I've never seen
>> it used.
>
> In almost all cases it's implemented in the PMIC rather than the CPU -
> it's essentially the same logic as implements the initial power on of
> the system and the PMIC needs to be capable of starting the system boot
> without external assistance anyway, including sequencing the power up of
> the CPU.
>
> With most PMICs the settings will be hard coded into the silicon so
> you really need to get a PMIC specifically designed for whatever CPU
> you're using (this is one of the reasons you always see the same
> CPU/PMIC combinations, people are terrified of changing a known good
> pairing in case they build something that won't power on). ?Some more
> modern PMICs make this configurable which gives system designers a lot
> more flexibility.

I'm seeing the opposite problem - changing CPUs, and keeping the PMIC.

> The PMC stuff is newer and in the designs I've seen is more focused on
> runtime management than this stuff.
>
>> > It sounds like for your systems what's happening is that the resume is
>> > restoring the previously configured voltages for the core rails rather
>> > than going to a known good state. ?Is my understanding correct here?
>> > While your fix is good and I don't see any reason not to do it this does
>> > sound like something we should have a solution for in common code as I'd
>> > not be surprised if there were other hardware out there which did a
>> > similar thing.
>
>> Yes - the core can be connected to a dumb I2C regulator, which the
>> core can turn on and off with a request line, but can't control the
>> voltage without a regulator driver controlling the I2C bus.
>
> AIUI generally they end up working because whatever puts them into
> suspend actually resets them so when you release reset during resume
> they just recover whatever the default voltage they have is and normally
> the boot and resume conditions for the CPU are identical. ?I could be
> off base there, most of the regulator API stuff has been done with
> integrated PMICs.

Resetting during suspend would be the sane thing to do, but that's not
what the hardware does.  The regulator also gets turned off during
idle, so it can't just be reset every time it turns off.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 28/28] ARM: tegra: clock: Add forward reference to struct clk
  2011-01-24 10:26     ` Russell King - ARM Linux
@ 2011-01-25  1:23       ` Colin Cross
  -1 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-25  1:23 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: linux-tegra, konkers, linux-kernel, olof, linux-arm-kernel

On Mon, Jan 24, 2011 at 2:26 AM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Sun, Jan 23, 2011 at 06:01:33PM -0800, Colin Cross wrote:
>> diff --git a/arch/arm/mach-tegra/include/mach/clkdev.h b/arch/arm/mach-tegra/include/mach/clkdev.h
>> index 412f5c6..66cd3f4 100644
>> --- a/arch/arm/mach-tegra/include/mach/clkdev.h
>> +++ b/arch/arm/mach-tegra/include/mach/clkdev.h
>> @@ -20,6 +20,8 @@
>>  #ifndef __MACH_CLKDEV_H
>>  #define __MACH_CLKDEV_H
>>
>> +struct clk;
>> +
>>  static inline int __clk_get(struct clk *clk)
>>  {
>>       return 1;
>
> If you're having to add that, I suspect this include is being
> inappropriately included by files.
>

The Tegra clock definition file, arch/arm/mach-tegra/tegra2_clocks.c,
includes the private clock.h that defines struct clk after including
linux/clkdev.h, and does not include linux/clk.h, so the forward
declaration is necessary.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 28/28] ARM: tegra: clock: Add forward reference to struct clk
@ 2011-01-25  1:23       ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-25  1:23 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Jan 24, 2011 at 2:26 AM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Sun, Jan 23, 2011 at 06:01:33PM -0800, Colin Cross wrote:
>> diff --git a/arch/arm/mach-tegra/include/mach/clkdev.h b/arch/arm/mach-tegra/include/mach/clkdev.h
>> index 412f5c6..66cd3f4 100644
>> --- a/arch/arm/mach-tegra/include/mach/clkdev.h
>> +++ b/arch/arm/mach-tegra/include/mach/clkdev.h
>> @@ -20,6 +20,8 @@
>> ?#ifndef __MACH_CLKDEV_H
>> ?#define __MACH_CLKDEV_H
>>
>> +struct clk;
>> +
>> ?static inline int __clk_get(struct clk *clk)
>> ?{
>> ? ? ? return 1;
>
> If you're having to add that, I suspect this include is being
> inappropriately included by files.
>

The Tegra clock definition file, arch/arm/mach-tegra/tegra2_clocks.c,
includes the private clock.h that defines struct clk after including
linux/clkdev.h, and does not include linux/clk.h, so the forward
declaration is necessary.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 20/28] ARM: tegra: cpufreq: Disable cpufreq during suspend
  2011-01-24 19:35         ` Mark Brown
@ 2011-01-25  4:26           ` Kyungmin Park
  -1 siblings, 0 replies; 137+ messages in thread
From: Kyungmin Park @ 2011-01-25  4:26 UTC (permalink / raw)
  To: Mark Brown
  Cc: Colin Cross, Russell King, konkers, linux-kernel, olof,
	linux-tegra, linux-arm-kernel, 함명주

On Tue, Jan 25, 2011 at 4:35 AM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
> On Mon, Jan 24, 2011 at 10:50:53AM -0800, Colin Cross wrote:
>> On Mon, Jan 24, 2011 at 6:41 AM, Mark Brown
>
>> > Hrm, what's the situation where that happens and why does it cause
>> > problems?  The regulator API doesn't care if suspend is going on, and
>> > nor do any of the current drivers for regulators.  There is an issue
>> > with keeping things like I2C alive until the bitter end of suspend so
>> > you've got a control bus to the regulators but that's a generic issue
>> > which crops up with other subsystems too so a regulator-specific
>> > workaround seems dodgy.
>
>> The end result is that the regulator driver calls the i2c driver after
>> the i2c driver has been suspended.  On Tegra, this happens because of
>> voltage scaling on the CPU regulator, which can be on the i2c bus.  A
>> sleep in a suspend handler after the i2c bus has been suspended can
>> cause the cpufreq governor to lower the frequency, and try to lower
>> the voltage as well.
>
> OK, that's a general problem - you need to ensure that the I2C and SPI
> controllers are suspended really late.  The OMAP guys also have this
> problem for some RTCs, though in their case things are slightly
> different due to their use of runtime PM.  Looking at your I2C driver
> (BTW, I'd suggest reminding Ben on a more regular basis about that) I
> suspect that moving your suspend and resume callbacks to the _noirq
> varaints will cover a lot of this.

Right, it's same as Samsung SoCs. we also disable to access the
cpufreq when suspending.
the PMIC is connected with I2C  and I2C is suspended before PMIC.

Thank you,
Kyungmin Park

>
>> The problem isn't limited to i2c busses, there are some regulators on
>> spi busses.  You would need to ensure that any driver that has a
>
> For all practical purposes in this sort of discussion you can typically
> do a s/I2C/I2C and SPI/ - in terms of system integration issues they're
> very similar.
>
>> regulator consumer suspends before the regulator driver it
>> communicates with, and that still wouldn't fix cpufreq, which has a
>> sysdev suspend handler.
>
> So you actively need to push the processor into low power mode during
> suspend?  I'm still not 100% clear what's triggering the issues you're
> seeing and why this seems to be Tegra-only.  If there were a general
> cpufreq/regulator interaction here I'd expect to see all ARM cpufreq
> drivers needing to do the same thing (and probably some handling of this
> in cpufreq core as a result).  If it's not such an issue I'd expect
> there's also entertaining suspend ordering issues elsewhere.
>
> Typically this stuff isn't a problem for regulators themselves since if
> they're active by the time I2C is off they normally get suspended by
> hardware handshakes from the CPU as that enters low power mode - right
> now we have no regulators at all in mainline that do anything in
> software on suspend.  We can get a long way by just ignoring what
> happens to regulators over suspend (there is some work to do here but
> it's orthogonal to this sort of issue).
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 20/28] ARM: tegra: cpufreq: Disable cpufreq during suspend
@ 2011-01-25  4:26           ` Kyungmin Park
  0 siblings, 0 replies; 137+ messages in thread
From: Kyungmin Park @ 2011-01-25  4:26 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jan 25, 2011 at 4:35 AM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
> On Mon, Jan 24, 2011 at 10:50:53AM -0800, Colin Cross wrote:
>> On Mon, Jan 24, 2011 at 6:41 AM, Mark Brown
>
>> > Hrm, what's the situation where that happens and why does it cause
>> > problems? ?The regulator API doesn't care if suspend is going on, and
>> > nor do any of the current drivers for regulators. ?There is an issue
>> > with keeping things like I2C alive until the bitter end of suspend so
>> > you've got a control bus to the regulators but that's a generic issue
>> > which crops up with other subsystems too so a regulator-specific
>> > workaround seems dodgy.
>
>> The end result is that the regulator driver calls the i2c driver after
>> the i2c driver has been suspended. ?On Tegra, this happens because of
>> voltage scaling on the CPU regulator, which can be on the i2c bus. ?A
>> sleep in a suspend handler after the i2c bus has been suspended can
>> cause the cpufreq governor to lower the frequency, and try to lower
>> the voltage as well.
>
> OK, that's a general problem - you need to ensure that the I2C and SPI
> controllers are suspended really late. ?The OMAP guys also have this
> problem for some RTCs, though in their case things are slightly
> different due to their use of runtime PM. ?Looking at your I2C driver
> (BTW, I'd suggest reminding Ben on a more regular basis about that) I
> suspect that moving your suspend and resume callbacks to the _noirq
> varaints will cover a lot of this.

Right, it's same as Samsung SoCs. we also disable to access the
cpufreq when suspending.
the PMIC is connected with I2C  and I2C is suspended before PMIC.

Thank you,
Kyungmin Park

>
>> The problem isn't limited to i2c busses, there are some regulators on
>> spi busses. ?You would need to ensure that any driver that has a
>
> For all practical purposes in this sort of discussion you can typically
> do a s/I2C/I2C and SPI/ - in terms of system integration issues they're
> very similar.
>
>> regulator consumer suspends before the regulator driver it
>> communicates with, and that still wouldn't fix cpufreq, which has a
>> sysdev suspend handler.
>
> So you actively need to push the processor into low power mode during
> suspend? ?I'm still not 100% clear what's triggering the issues you're
> seeing and why this seems to be Tegra-only. ?If there were a general
> cpufreq/regulator interaction here I'd expect to see all ARM cpufreq
> drivers needing to do the same thing (and probably some handling of this
> in cpufreq core as a result). ?If it's not such an issue I'd expect
> there's also entertaining suspend ordering issues elsewhere.
>
> Typically this stuff isn't a problem for regulators themselves since if
> they're active by the time I2C is off they normally get suspended by
> hardware handshakes from the CPU as that enters low power mode - right
> now we have no regulators at all in mainline that do anything in
> software on suspend. ?We can get a long way by just ignoring what
> happens to regulators over suspend (there is some work to do here but
> it's orthogonal to this sort of issue).
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support for re-enabling l2x0
  2011-01-24  2:01   ` Colin Cross
@ 2011-01-25 15:14     ` Catalin Marinas
  -1 siblings, 0 replies; 137+ messages in thread
From: Catalin Marinas @ 2011-01-25 15:14 UTC (permalink / raw)
  To: Colin Cross
  Cc: linux-tegra, linux-arm-kernel, olof, konkers, Russell King,
	Santosh Shilimkar, Linus Walleij, Tony Lindgren, linux-kernel

On Mon, 2011-01-24 at 02:01 +0000, Colin Cross wrote:
> --- a/arch/arm/mm/cache-l2x0.c
> +++ b/arch/arm/mm/cache-l2x0.c
> @@ -252,16 +252,26 @@ static void l2x0_flush_range(unsigned long start, unsigned long end)
>         spin_unlock_irqrestore(&l2x0_lock, flags);
>  }
> 
> +/* enables l2x0 after l2x0_disable, does not invalidate */
> +void l2x0_enable(void)
> +{
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&l2x0_lock, flags);
> +       writel_relaxed(1, l2x0_base + L2X0_CTRL);
> +       spin_unlock_irqrestore(&l2x0_lock, flags);
> +}
> +
>  static void l2x0_disable(void)
>  {
>         unsigned long flags;
> 
>         spin_lock_irqsave(&l2x0_lock, flags);
> -       writel(0, l2x0_base + L2X0_CTRL);
> +       writel_relaxed(0, l2x0_base + L2X0_CTRL);
>         spin_unlock_irqrestore(&l2x0_lock, flags);
>  }
> 
> -void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask)
> +void l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask)
>  {
>         __u32 aux;
>         __u32 cache_id;

So this assumes that the L2 registers are accessible. I suspect the
platform code calling it should know this.

The patch looks fine.

Acked-by: Catalin Marinas <catalin.marinas@arm.com>



^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support for re-enabling l2x0
@ 2011-01-25 15:14     ` Catalin Marinas
  0 siblings, 0 replies; 137+ messages in thread
From: Catalin Marinas @ 2011-01-25 15:14 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, 2011-01-24 at 02:01 +0000, Colin Cross wrote:
> --- a/arch/arm/mm/cache-l2x0.c
> +++ b/arch/arm/mm/cache-l2x0.c
> @@ -252,16 +252,26 @@ static void l2x0_flush_range(unsigned long start, unsigned long end)
>         spin_unlock_irqrestore(&l2x0_lock, flags);
>  }
> 
> +/* enables l2x0 after l2x0_disable, does not invalidate */
> +void l2x0_enable(void)
> +{
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&l2x0_lock, flags);
> +       writel_relaxed(1, l2x0_base + L2X0_CTRL);
> +       spin_unlock_irqrestore(&l2x0_lock, flags);
> +}
> +
>  static void l2x0_disable(void)
>  {
>         unsigned long flags;
> 
>         spin_lock_irqsave(&l2x0_lock, flags);
> -       writel(0, l2x0_base + L2X0_CTRL);
> +       writel_relaxed(0, l2x0_base + L2X0_CTRL);
>         spin_unlock_irqrestore(&l2x0_lock, flags);
>  }
> 
> -void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask)
> +void l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask)
>  {
>         __u32 aux;
>         __u32 cache_id;

So this assumes that the L2 registers are accessible. I suspect the
platform code calling it should know this.

The patch looks fine.

Acked-by: Catalin Marinas <catalin.marinas@arm.com>

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support for re-enabling l2x0
  2011-01-25 15:14     ` Catalin Marinas
@ 2011-01-25 15:41       ` Russell King - ARM Linux
  -1 siblings, 0 replies; 137+ messages in thread
From: Russell King - ARM Linux @ 2011-01-25 15:41 UTC (permalink / raw)
  To: Catalin Marinas
  Cc: Colin Cross, linux-tegra, linux-arm-kernel, olof, konkers,
	Santosh Shilimkar, Linus Walleij, Tony Lindgren, linux-kernel

On Tue, Jan 25, 2011 at 03:14:24PM +0000, Catalin Marinas wrote:
> On Mon, 2011-01-24 at 02:01 +0000, Colin Cross wrote:
> > --- a/arch/arm/mm/cache-l2x0.c
> > +++ b/arch/arm/mm/cache-l2x0.c
> > @@ -252,16 +252,26 @@ static void l2x0_flush_range(unsigned long start, unsigned long end)
> >         spin_unlock_irqrestore(&l2x0_lock, flags);
> >  }
> > 
> > +/* enables l2x0 after l2x0_disable, does not invalidate */
> > +void l2x0_enable(void)
> > +{
> > +       unsigned long flags;
> > +
> > +       spin_lock_irqsave(&l2x0_lock, flags);
> > +       writel_relaxed(1, l2x0_base + L2X0_CTRL);
> > +       spin_unlock_irqrestore(&l2x0_lock, flags);
> > +}
> > +
> >  static void l2x0_disable(void)
> >  {
> >         unsigned long flags;
> > 
> >         spin_lock_irqsave(&l2x0_lock, flags);
> > -       writel(0, l2x0_base + L2X0_CTRL);
> > +       writel_relaxed(0, l2x0_base + L2X0_CTRL);
> >         spin_unlock_irqrestore(&l2x0_lock, flags);
> >  }
> > 
> > -void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask)
> > +void l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask)
> >  {
> >         __u32 aux;
> >         __u32 cache_id;
> 
> So this assumes that the L2 registers are accessible. I suspect the
> platform code calling it should know this.
> 
> The patch looks fine.
> 
> Acked-by: Catalin Marinas <catalin.marinas@arm.com>

I think we need to come up with some proper way to deal with cpuidle
which doesn't involve adding lots of globally visible functions to all
sorts of bits of code and having platforms call them individually,
otherwise this is going to get _really_ messy in the future.

Maybe we need a notifier list which can be told when cpuidle events
happen, so that parts of the system such as VFP and L2 cache support
can do the right thing without having platforms add lots of stuff like

	gic_secondary_init();
	gic_restore_interrupt_types();
	vfp_enable();
	l2x0_enable();
	twd_enable();
	... etc ...

in their SoC specific code.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support for re-enabling l2x0
@ 2011-01-25 15:41       ` Russell King - ARM Linux
  0 siblings, 0 replies; 137+ messages in thread
From: Russell King - ARM Linux @ 2011-01-25 15:41 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jan 25, 2011 at 03:14:24PM +0000, Catalin Marinas wrote:
> On Mon, 2011-01-24 at 02:01 +0000, Colin Cross wrote:
> > --- a/arch/arm/mm/cache-l2x0.c
> > +++ b/arch/arm/mm/cache-l2x0.c
> > @@ -252,16 +252,26 @@ static void l2x0_flush_range(unsigned long start, unsigned long end)
> >         spin_unlock_irqrestore(&l2x0_lock, flags);
> >  }
> > 
> > +/* enables l2x0 after l2x0_disable, does not invalidate */
> > +void l2x0_enable(void)
> > +{
> > +       unsigned long flags;
> > +
> > +       spin_lock_irqsave(&l2x0_lock, flags);
> > +       writel_relaxed(1, l2x0_base + L2X0_CTRL);
> > +       spin_unlock_irqrestore(&l2x0_lock, flags);
> > +}
> > +
> >  static void l2x0_disable(void)
> >  {
> >         unsigned long flags;
> > 
> >         spin_lock_irqsave(&l2x0_lock, flags);
> > -       writel(0, l2x0_base + L2X0_CTRL);
> > +       writel_relaxed(0, l2x0_base + L2X0_CTRL);
> >         spin_unlock_irqrestore(&l2x0_lock, flags);
> >  }
> > 
> > -void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask)
> > +void l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask)
> >  {
> >         __u32 aux;
> >         __u32 cache_id;
> 
> So this assumes that the L2 registers are accessible. I suspect the
> platform code calling it should know this.
> 
> The patch looks fine.
> 
> Acked-by: Catalin Marinas <catalin.marinas@arm.com>

I think we need to come up with some proper way to deal with cpuidle
which doesn't involve adding lots of globally visible functions to all
sorts of bits of code and having platforms call them individually,
otherwise this is going to get _really_ messy in the future.

Maybe we need a notifier list which can be told when cpuidle events
happen, so that parts of the system such as VFP and L2 cache support
can do the right thing without having platforms add lots of stuff like

	gic_secondary_init();
	gic_restore_interrupt_types();
	vfp_enable();
	l2x0_enable();
	twd_enable();
	... etc ...

in their SoC specific code.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support for re-enabling l2x0
  2011-01-25 15:41       ` Russell King - ARM Linux
@ 2011-01-25 18:14         ` Catalin Marinas
  -1 siblings, 0 replies; 137+ messages in thread
From: Catalin Marinas @ 2011-01-25 18:14 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Colin Cross, linux-tegra, linux-arm-kernel, olof, konkers,
	Santosh Shilimkar, Linus Walleij, Tony Lindgren, linux-kernel

On Tue, 2011-01-25 at 15:41 +0000, Russell King - ARM Linux wrote:
> I think we need to come up with some proper way to deal with cpuidle
> which doesn't involve adding lots of globally visible functions to all
> sorts of bits of code and having platforms call them individually,
> otherwise this is going to get _really_ messy in the future.
> 
> Maybe we need a notifier list which can be told when cpuidle events
> happen, so that parts of the system such as VFP and L2 cache support
> can do the right thing without having platforms add lots of stuff like
> 
>         gic_secondary_init();
>         gic_restore_interrupt_types();
>         vfp_enable();
>         l2x0_enable();
>         twd_enable();
>         ... etc ...
> 
> in their SoC specific code.

But do we need a strict order between such operations? The notifier call
chain isn't too flexible.

-- 
Catalin



^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support for re-enabling l2x0
@ 2011-01-25 18:14         ` Catalin Marinas
  0 siblings, 0 replies; 137+ messages in thread
From: Catalin Marinas @ 2011-01-25 18:14 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 2011-01-25 at 15:41 +0000, Russell King - ARM Linux wrote:
> I think we need to come up with some proper way to deal with cpuidle
> which doesn't involve adding lots of globally visible functions to all
> sorts of bits of code and having platforms call them individually,
> otherwise this is going to get _really_ messy in the future.
> 
> Maybe we need a notifier list which can be told when cpuidle events
> happen, so that parts of the system such as VFP and L2 cache support
> can do the right thing without having platforms add lots of stuff like
> 
>         gic_secondary_init();
>         gic_restore_interrupt_types();
>         vfp_enable();
>         l2x0_enable();
>         twd_enable();
>         ... etc ...
> 
> in their SoC specific code.

But do we need a strict order between such operations? The notifier call
chain isn't too flexible.

-- 
Catalin

^ permalink raw reply	[flat|nested] 137+ messages in thread

* RE: [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support for re-enabling l2x0
  2011-01-25 18:14         ` Catalin Marinas
@ 2011-01-25 18:32           ` Santosh Shilimkar
  -1 siblings, 0 replies; 137+ messages in thread
From: Santosh Shilimkar @ 2011-01-25 18:32 UTC (permalink / raw)
  To: Catalin Marinas, Russell King - ARM Linux
  Cc: Colin Cross, linux-tegra, linux-arm-kernel, olof, konkers,
	Linus Walleij, Tony Lindgren, linux-kernel

> -----Original Message-----
> From: Catalin Marinas [mailto:catalin.marinas@arm.com]
> Sent: Tuesday, January 25, 2011 11:44 PM
> To: Russell King - ARM Linux
> Cc: Colin Cross; linux-tegra@vger.kernel.org; linux-arm-
> kernel@lists.infradead.org; olof@lixom.net; konkers@android.com;
> Santosh Shilimkar; Linus Walleij; Tony Lindgren; linux-
> kernel@vger.kernel.org
> Subject: Re: [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support for
> re-enabling l2x0
>
> On Tue, 2011-01-25 at 15:41 +0000, Russell King - ARM Linux wrote:
> > I think we need to come up with some proper way to deal with
> cpuidle
> > which doesn't involve adding lots of globally visible functions to
> all
> > sorts of bits of code and having platforms call them individually,
> > otherwise this is going to get _really_ messy in the future.
> >
> > Maybe we need a notifier list which can be told when cpuidle
> events
> > happen, so that parts of the system such as VFP and L2 cache
> support
> > can do the right thing without having platforms add lots of stuff
> like
> >
> >         gic_secondary_init();
> >         gic_restore_interrupt_types();
> >         vfp_enable();
> >         l2x0_enable();
> >         twd_enable();
> >         ... etc ...
> >
> > in their SoC specific code.
>
> But do we need a strict order between such operations? The notifier
> call
> chain isn't too flexible.
>
I guess it does depends on how the archs have integrated a9. Example
on OMAP there are different power modes possible.
	1. CPU context ,TWD lost
	2. CPU context ,TWD + L1 is lost
	3. CPU context + L1 is lost + GIC lost
	4. CPU context + L1 is lost + GIC lost + L2 lost
So there is need to have flexibility of calling these function
based on power modes. I don't know how notifiers can give
this flexibility

Regards,
Santosh

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support for re-enabling l2x0
@ 2011-01-25 18:32           ` Santosh Shilimkar
  0 siblings, 0 replies; 137+ messages in thread
From: Santosh Shilimkar @ 2011-01-25 18:32 UTC (permalink / raw)
  To: linux-arm-kernel

> -----Original Message-----
> From: Catalin Marinas [mailto:catalin.marinas at arm.com]
> Sent: Tuesday, January 25, 2011 11:44 PM
> To: Russell King - ARM Linux
> Cc: Colin Cross; linux-tegra at vger.kernel.org; linux-arm-
> kernel at lists.infradead.org; olof at lixom.net; konkers at android.com;
> Santosh Shilimkar; Linus Walleij; Tony Lindgren; linux-
> kernel at vger.kernel.org
> Subject: Re: [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support for
> re-enabling l2x0
>
> On Tue, 2011-01-25 at 15:41 +0000, Russell King - ARM Linux wrote:
> > I think we need to come up with some proper way to deal with
> cpuidle
> > which doesn't involve adding lots of globally visible functions to
> all
> > sorts of bits of code and having platforms call them individually,
> > otherwise this is going to get _really_ messy in the future.
> >
> > Maybe we need a notifier list which can be told when cpuidle
> events
> > happen, so that parts of the system such as VFP and L2 cache
> support
> > can do the right thing without having platforms add lots of stuff
> like
> >
> >         gic_secondary_init();
> >         gic_restore_interrupt_types();
> >         vfp_enable();
> >         l2x0_enable();
> >         twd_enable();
> >         ... etc ...
> >
> > in their SoC specific code.
>
> But do we need a strict order between such operations? The notifier
> call
> chain isn't too flexible.
>
I guess it does depends on how the archs have integrated a9. Example
on OMAP there are different power modes possible.
	1. CPU context ,TWD lost
	2. CPU context ,TWD + L1 is lost
	3. CPU context + L1 is lost + GIC lost
	4. CPU context + L1 is lost + GIC lost + L2 lost
So there is need to have flexibility of calling these function
based on power modes. I don't know how notifiers can give
this flexibility

Regards,
Santosh

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support for re-enabling l2x0
  2011-01-25 18:32           ` Santosh Shilimkar
  (?)
@ 2011-01-25 18:39           ` Will Deacon
  -1 siblings, 0 replies; 137+ messages in thread
From: Will Deacon @ 2011-01-25 18:39 UTC (permalink / raw)
  To: linux-arm-kernel

Santosh,

> > > Maybe we need a notifier list which can be told when cpuidle
> > events
> > > happen, so that parts of the system such as VFP and L2 cache
> > support
> > > can do the right thing without having platforms add lots of stuff
> > like
> > >
> > >         gic_secondary_init();
> > >         gic_restore_interrupt_types();
> > >         vfp_enable();
> > >         l2x0_enable();
> > >         twd_enable();
> > >         ... etc ...
> > >
> > > in their SoC specific code.
> >
> > But do we need a strict order between such operations? The notifier
> > call
> > chain isn't too flexible.
> >
> I guess it does depends on how the archs have integrated a9. Example
> on OMAP there are different power modes possible.
> 	1. CPU context ,TWD lost
> 	2. CPU context ,TWD + L1 is lost
> 	3. CPU context + L1 is lost + GIC lost
> 	4. CPU context + L1 is lost + GIC lost + L2 lost
> So there is need to have flexibility of calling these function
> based on power modes. I don't know how notifiers can give
> this flexibility

Well if you set the priority fields in the notifier blocks correctly
then you can just return NOTIFY_STOP when you've saved/restored as much
as you want. This assumes of course that you can identify which power
mode you're entering/leaving and that each one is `deeper' than the previous.

Will

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 28/28] ARM: tegra: clock: Add forward reference to struct clk
  2011-01-24  2:01   ` Colin Cross
@ 2011-01-26  3:19     ` Grant Likely
  -1 siblings, 0 replies; 137+ messages in thread
From: Grant Likely @ 2011-01-26  3:19 UTC (permalink / raw)
  To: Colin Cross
  Cc: linux-tegra, linux-arm-kernel, olof, konkers, Russell King, linux-kernel

On Sun, Jan 23, 2011 at 7:01 PM, Colin Cross <ccross@android.com> wrote:
> Signed-off-by: Colin Cross <ccross@android.com>

Hi Colin,

This build warning exists in current mainline.  It should go out to
Linus now instead of waiting for 2.6.29.

Acked-by: Grant Likely <grant.likely@secretlab.ca>

g.

> ---
>  arch/arm/mach-tegra/include/mach/clk.h    |    2 ++
>  arch/arm/mach-tegra/include/mach/clkdev.h |    2 ++
>  2 files changed, 4 insertions(+), 0 deletions(-)
>
> diff --git a/arch/arm/mach-tegra/include/mach/clk.h b/arch/arm/mach-tegra/include/mach/clk.h
> index d772395..a217f68 100644
> --- a/arch/arm/mach-tegra/include/mach/clk.h
> +++ b/arch/arm/mach-tegra/include/mach/clk.h
> @@ -20,6 +20,8 @@
>  #ifndef __MACH_CLK_H
>  #define __MACH_CLK_H
>
> +struct clk;
> +
>  void tegra_periph_reset_deassert(struct clk *c);
>  void tegra_periph_reset_assert(struct clk *c);
>
> diff --git a/arch/arm/mach-tegra/include/mach/clkdev.h b/arch/arm/mach-tegra/include/mach/clkdev.h
> index 412f5c6..66cd3f4 100644
> --- a/arch/arm/mach-tegra/include/mach/clkdev.h
> +++ b/arch/arm/mach-tegra/include/mach/clkdev.h
> @@ -20,6 +20,8 @@
>  #ifndef __MACH_CLKDEV_H
>  #define __MACH_CLKDEV_H
>
> +struct clk;
> +
>  static inline int __clk_get(struct clk *clk)
>  {
>        return 1;
> --
> 1.7.3.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
>



-- 
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 28/28] ARM: tegra: clock: Add forward reference to struct clk
@ 2011-01-26  3:19     ` Grant Likely
  0 siblings, 0 replies; 137+ messages in thread
From: Grant Likely @ 2011-01-26  3:19 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, Jan 23, 2011 at 7:01 PM, Colin Cross <ccross@android.com> wrote:
> Signed-off-by: Colin Cross <ccross@android.com>

Hi Colin,

This build warning exists in current mainline.  It should go out to
Linus now instead of waiting for 2.6.29.

Acked-by: Grant Likely <grant.likely@secretlab.ca>

g.

> ---
> ?arch/arm/mach-tegra/include/mach/clk.h ? ?| ? ?2 ++
> ?arch/arm/mach-tegra/include/mach/clkdev.h | ? ?2 ++
> ?2 files changed, 4 insertions(+), 0 deletions(-)
>
> diff --git a/arch/arm/mach-tegra/include/mach/clk.h b/arch/arm/mach-tegra/include/mach/clk.h
> index d772395..a217f68 100644
> --- a/arch/arm/mach-tegra/include/mach/clk.h
> +++ b/arch/arm/mach-tegra/include/mach/clk.h
> @@ -20,6 +20,8 @@
> ?#ifndef __MACH_CLK_H
> ?#define __MACH_CLK_H
>
> +struct clk;
> +
> ?void tegra_periph_reset_deassert(struct clk *c);
> ?void tegra_periph_reset_assert(struct clk *c);
>
> diff --git a/arch/arm/mach-tegra/include/mach/clkdev.h b/arch/arm/mach-tegra/include/mach/clkdev.h
> index 412f5c6..66cd3f4 100644
> --- a/arch/arm/mach-tegra/include/mach/clkdev.h
> +++ b/arch/arm/mach-tegra/include/mach/clkdev.h
> @@ -20,6 +20,8 @@
> ?#ifndef __MACH_CLKDEV_H
> ?#define __MACH_CLKDEV_H
>
> +struct clk;
> +
> ?static inline int __clk_get(struct clk *clk)
> ?{
> ? ? ? ?return 1;
> --
> 1.7.3.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at ?http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at ?http://www.tux.org/lkml/
>



-- 
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 28/28] ARM: tegra: clock: Add forward reference to struct clk
  2011-01-26  3:19     ` Grant Likely
@ 2011-01-26  3:21       ` Colin Cross
  -1 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-26  3:21 UTC (permalink / raw)
  To: Grant Likely
  Cc: linux-tegra, linux-arm-kernel, olof, konkers, Russell King, linux-kernel

On Tue, Jan 25, 2011 at 7:19 PM, Grant Likely <grant.likely@secretlab.ca> wrote:
> On Sun, Jan 23, 2011 at 7:01 PM, Colin Cross <ccross@android.com> wrote:
>> Signed-off-by: Colin Cross <ccross@android.com>
>
> Hi Colin,
>
> This build warning exists in current mainline.  It should go out to
> Linus now instead of waiting for 2.6.29.
>
> Acked-by: Grant Likely <grant.likely@secretlab.ca>
>
> g.
>
>> ---
>>  arch/arm/mach-tegra/include/mach/clk.h    |    2 ++
>>  arch/arm/mach-tegra/include/mach/clkdev.h |    2 ++
>>  2 files changed, 4 insertions(+), 0 deletions(-)
>>
>> diff --git a/arch/arm/mach-tegra/include/mach/clk.h b/arch/arm/mach-tegra/include/mach/clk.h
>> index d772395..a217f68 100644
>> --- a/arch/arm/mach-tegra/include/mach/clk.h
>> +++ b/arch/arm/mach-tegra/include/mach/clk.h
>> @@ -20,6 +20,8 @@
>>  #ifndef __MACH_CLK_H
>>  #define __MACH_CLK_H
>>
>> +struct clk;
>> +
>>  void tegra_periph_reset_deassert(struct clk *c);
>>  void tegra_periph_reset_assert(struct clk *c);
>>
>> diff --git a/arch/arm/mach-tegra/include/mach/clkdev.h b/arch/arm/mach-tegra/include/mach/clkdev.h
>> index 412f5c6..66cd3f4 100644
>> --- a/arch/arm/mach-tegra/include/mach/clkdev.h
>> +++ b/arch/arm/mach-tegra/include/mach/clkdev.h
>> @@ -20,6 +20,8 @@
>>  #ifndef __MACH_CLKDEV_H
>>  #define __MACH_CLKDEV_H
>>
>> +struct clk;
>> +
>>  static inline int __clk_get(struct clk *clk)
>>  {
>>        return 1;
>> --
>> 1.7.3.1

It will, I just haven't sent it yet.  I have one more patch I'd like
to send with it to prevent a possible variable name conflict in
2.6.39.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 28/28] ARM: tegra: clock: Add forward reference to struct clk
@ 2011-01-26  3:21       ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-26  3:21 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jan 25, 2011 at 7:19 PM, Grant Likely <grant.likely@secretlab.ca> wrote:
> On Sun, Jan 23, 2011 at 7:01 PM, Colin Cross <ccross@android.com> wrote:
>> Signed-off-by: Colin Cross <ccross@android.com>
>
> Hi Colin,
>
> This build warning exists in current mainline. ?It should go out to
> Linus now instead of waiting for 2.6.29.
>
> Acked-by: Grant Likely <grant.likely@secretlab.ca>
>
> g.
>
>> ---
>> ?arch/arm/mach-tegra/include/mach/clk.h ? ?| ? ?2 ++
>> ?arch/arm/mach-tegra/include/mach/clkdev.h | ? ?2 ++
>> ?2 files changed, 4 insertions(+), 0 deletions(-)
>>
>> diff --git a/arch/arm/mach-tegra/include/mach/clk.h b/arch/arm/mach-tegra/include/mach/clk.h
>> index d772395..a217f68 100644
>> --- a/arch/arm/mach-tegra/include/mach/clk.h
>> +++ b/arch/arm/mach-tegra/include/mach/clk.h
>> @@ -20,6 +20,8 @@
>> ?#ifndef __MACH_CLK_H
>> ?#define __MACH_CLK_H
>>
>> +struct clk;
>> +
>> ?void tegra_periph_reset_deassert(struct clk *c);
>> ?void tegra_periph_reset_assert(struct clk *c);
>>
>> diff --git a/arch/arm/mach-tegra/include/mach/clkdev.h b/arch/arm/mach-tegra/include/mach/clkdev.h
>> index 412f5c6..66cd3f4 100644
>> --- a/arch/arm/mach-tegra/include/mach/clkdev.h
>> +++ b/arch/arm/mach-tegra/include/mach/clkdev.h
>> @@ -20,6 +20,8 @@
>> ?#ifndef __MACH_CLKDEV_H
>> ?#define __MACH_CLKDEV_H
>>
>> +struct clk;
>> +
>> ?static inline int __clk_get(struct clk *clk)
>> ?{
>> ? ? ? ?return 1;
>> --
>> 1.7.3.1

It will, I just haven't sent it yet.  I have one more patch I'd like
to send with it to prevent a possible variable name conflict in
2.6.39.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 28/28] ARM: tegra: clock: Add forward reference to struct clk
  2011-01-26  3:21       ` Colin Cross
@ 2011-01-26  4:16         ` Grant Likely
  -1 siblings, 0 replies; 137+ messages in thread
From: Grant Likely @ 2011-01-26  4:16 UTC (permalink / raw)
  To: Colin Cross
  Cc: linux-tegra, linux-arm-kernel, olof, konkers, Russell King, linux-kernel

On Tue, Jan 25, 2011 at 8:21 PM, Colin Cross <ccross@android.com> wrote:
> On Tue, Jan 25, 2011 at 7:19 PM, Grant Likely <grant.likely@secretlab.ca> wrote:
>> On Sun, Jan 23, 2011 at 7:01 PM, Colin Cross <ccross@android.com> wrote:
>>> Signed-off-by: Colin Cross <ccross@android.com>
>>
>> Hi Colin,
>>
>> This build warning exists in current mainline.  It should go out to
>> Linus now instead of waiting for 2.6.29.
>>
>> Acked-by: Grant Likely <grant.likely@secretlab.ca>
>>
>> g.
>
> It will, I just haven't sent it yet.  I have one more patch I'd like
> to send with it to prevent a possible variable name conflict in
> 2.6.39.

Cool, thx.

g.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 28/28] ARM: tegra: clock: Add forward reference to struct clk
@ 2011-01-26  4:16         ` Grant Likely
  0 siblings, 0 replies; 137+ messages in thread
From: Grant Likely @ 2011-01-26  4:16 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jan 25, 2011 at 8:21 PM, Colin Cross <ccross@android.com> wrote:
> On Tue, Jan 25, 2011 at 7:19 PM, Grant Likely <grant.likely@secretlab.ca> wrote:
>> On Sun, Jan 23, 2011 at 7:01 PM, Colin Cross <ccross@android.com> wrote:
>>> Signed-off-by: Colin Cross <ccross@android.com>
>>
>> Hi Colin,
>>
>> This build warning exists in current mainline. ?It should go out to
>> Linus now instead of waiting for 2.6.29.
>>
>> Acked-by: Grant Likely <grant.likely@secretlab.ca>
>>
>> g.
>
> It will, I just haven't sent it yet. ?I have one more patch I'd like
> to send with it to prevent a possible variable name conflict in
> 2.6.39.

Cool, thx.

g.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 01/28] ARM: tegra: irq: Rename gic pointers to avoid conflicts
  2011-01-24  2:01   ` Colin Cross
@ 2011-01-26  7:10     ` Colin Cross
  -1 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-26  7:10 UTC (permalink / raw)
  To: linux-tegra
  Cc: linux-arm-kernel, olof, konkers, Colin Cross, Russell King, linux-kernel

On Sun, Jan 23, 2011 at 6:01 PM, Colin Cross <ccross@android.com> wrote:
> A future patch will export gic_mask_irq and gic_unmask_irq.
> Rename the pointers in arch/arm/mach-tegra/irq.c to avoid
> a compile error.
>
> Signed-off-by: Colin Cross <ccross@android.com>
> ---
>  arch/arm/mach-tegra/irq.c |   18 +++++++++---------
>  1 files changed, 9 insertions(+), 9 deletions(-)
>
> diff --git a/arch/arm/mach-tegra/irq.c b/arch/arm/mach-tegra/irq.c
> index de7dfad..17c74d2 100644
> --- a/arch/arm/mach-tegra/irq.c
> +++ b/arch/arm/mach-tegra/irq.c
> @@ -46,24 +46,24 @@
>  #define ICTLR_COP_IER_CLR      0x38
>  #define ICTLR_COP_IEP_CLASS    0x3c
>
> -static void (*gic_mask_irq)(struct irq_data *d);
> -static void (*gic_unmask_irq)(struct irq_data *d);
> +static void (*tegra_gic_mask_irq)(struct irq_data *d);
> +static void (*tegra_gic_unmask_irq)(struct irq_data *d);
>
> -#define irq_to_ictlr(irq) (((irq)-32) >> 5)
> +#define irq_to_ictlr(irq) (((irq) - 32) >> 5)
>  static void __iomem *tegra_ictlr_base = IO_ADDRESS(TEGRA_PRIMARY_ICTLR_BASE);
> -#define ictlr_to_virt(ictlr) (tegra_ictlr_base + (ictlr)*0x100)
> +#define ictlr_to_virt(ictlr) (tegra_ictlr_base + (ictlr) * 0x100)
>
>  static void tegra_mask(struct irq_data *d)
>  {
>        void __iomem *addr = ictlr_to_virt(irq_to_ictlr(d->irq));
> -       gic_mask_irq(d);
> -       writel(1<<(d->irq&31), addr+ICTLR_CPU_IER_CLR);
> +       tegra_gic_mask_irq(d);
> +       writel(1 << (d->irq & 31), addr+ICTLR_CPU_IER_CLR);
>  }
>
>  static void tegra_unmask(struct irq_data *d)
>  {
>        void __iomem *addr = ictlr_to_virt(irq_to_ictlr(d->irq));
> -       gic_unmask_irq(d);
> +       tegra_gic_unmask_irq(d);
>        writel(1<<(d->irq&31), addr+ICTLR_CPU_IER_SET);
>  }
>
> @@ -98,8 +98,8 @@ void __init tegra_init_irq(void)
>                 IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100));
>
>        gic = get_irq_chip(29);
> -       gic_unmask_irq = gic->irq_unmask;
> -       gic_mask_irq = gic->irq_mask;
> +       tegra_gic_unmask_irq = gic->irq_unmask;
> +       tegra_gic_mask_irq = gic->irq_mask;
>        tegra_irq.irq_ack = gic->irq_ack;
>  #ifdef CONFIG_SMP
>        tegra_irq.irq_set_affinity = gic->irq_set_affinity;
> --
> 1.7.3.1

If there are no objections, I'm going to put this patch into
tegra/for-linus for a pull request for 2.6.38-rc3.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 01/28] ARM: tegra: irq: Rename gic pointers to avoid conflicts
@ 2011-01-26  7:10     ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-26  7:10 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, Jan 23, 2011 at 6:01 PM, Colin Cross <ccross@android.com> wrote:
> A future patch will export gic_mask_irq and gic_unmask_irq.
> Rename the pointers in arch/arm/mach-tegra/irq.c to avoid
> a compile error.
>
> Signed-off-by: Colin Cross <ccross@android.com>
> ---
> ?arch/arm/mach-tegra/irq.c | ? 18 +++++++++---------
> ?1 files changed, 9 insertions(+), 9 deletions(-)
>
> diff --git a/arch/arm/mach-tegra/irq.c b/arch/arm/mach-tegra/irq.c
> index de7dfad..17c74d2 100644
> --- a/arch/arm/mach-tegra/irq.c
> +++ b/arch/arm/mach-tegra/irq.c
> @@ -46,24 +46,24 @@
> ?#define ICTLR_COP_IER_CLR ? ? ?0x38
> ?#define ICTLR_COP_IEP_CLASS ? ?0x3c
>
> -static void (*gic_mask_irq)(struct irq_data *d);
> -static void (*gic_unmask_irq)(struct irq_data *d);
> +static void (*tegra_gic_mask_irq)(struct irq_data *d);
> +static void (*tegra_gic_unmask_irq)(struct irq_data *d);
>
> -#define irq_to_ictlr(irq) (((irq)-32) >> 5)
> +#define irq_to_ictlr(irq) (((irq) - 32) >> 5)
> ?static void __iomem *tegra_ictlr_base = IO_ADDRESS(TEGRA_PRIMARY_ICTLR_BASE);
> -#define ictlr_to_virt(ictlr) (tegra_ictlr_base + (ictlr)*0x100)
> +#define ictlr_to_virt(ictlr) (tegra_ictlr_base + (ictlr) * 0x100)
>
> ?static void tegra_mask(struct irq_data *d)
> ?{
> ? ? ? ?void __iomem *addr = ictlr_to_virt(irq_to_ictlr(d->irq));
> - ? ? ? gic_mask_irq(d);
> - ? ? ? writel(1<<(d->irq&31), addr+ICTLR_CPU_IER_CLR);
> + ? ? ? tegra_gic_mask_irq(d);
> + ? ? ? writel(1 << (d->irq & 31), addr+ICTLR_CPU_IER_CLR);
> ?}
>
> ?static void tegra_unmask(struct irq_data *d)
> ?{
> ? ? ? ?void __iomem *addr = ictlr_to_virt(irq_to_ictlr(d->irq));
> - ? ? ? gic_unmask_irq(d);
> + ? ? ? tegra_gic_unmask_irq(d);
> ? ? ? ?writel(1<<(d->irq&31), addr+ICTLR_CPU_IER_SET);
> ?}
>
> @@ -98,8 +98,8 @@ void __init tegra_init_irq(void)
> ? ? ? ? ? ? ? ? IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100));
>
> ? ? ? ?gic = get_irq_chip(29);
> - ? ? ? gic_unmask_irq = gic->irq_unmask;
> - ? ? ? gic_mask_irq = gic->irq_mask;
> + ? ? ? tegra_gic_unmask_irq = gic->irq_unmask;
> + ? ? ? tegra_gic_mask_irq = gic->irq_mask;
> ? ? ? ?tegra_irq.irq_ack = gic->irq_ack;
> ?#ifdef CONFIG_SMP
> ? ? ? ?tegra_irq.irq_set_affinity = gic->irq_set_affinity;
> --
> 1.7.3.1

If there are no objections, I'm going to put this patch into
tegra/for-linus for a pull request for 2.6.38-rc3.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 15/28] ARM: tegra: irq: Add debugfs file to show wake irqs
  2011-01-24  2:01   ` Colin Cross
@ 2011-01-27 19:45     ` Stephen Boyd
  -1 siblings, 0 replies; 137+ messages in thread
From: Stephen Boyd @ 2011-01-27 19:45 UTC (permalink / raw)
  To: Colin Cross
  Cc: linux-tegra, linux-arm-kernel, olof, konkers, Russell King, linux-kernel

On 01/23/2011 06:01 PM, Colin Cross wrote:
>
> +static int __init tegra_irq_debug_init(void)
> +{
> +	struct dentry *d;
> +
> +	d = debugfs_create_file("wake_irq", S_IRUGO, NULL, NULL,
> +		&tegra_wake_irq_debug_fops);
> +	if (!d) {
> +		pr_info("Failed to create suspend_mode debug file\n");

Does this want to be a pr_err()?

-- 
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.


^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 15/28] ARM: tegra: irq: Add debugfs file to show wake irqs
@ 2011-01-27 19:45     ` Stephen Boyd
  0 siblings, 0 replies; 137+ messages in thread
From: Stephen Boyd @ 2011-01-27 19:45 UTC (permalink / raw)
  To: linux-arm-kernel

On 01/23/2011 06:01 PM, Colin Cross wrote:
>
> +static int __init tegra_irq_debug_init(void)
> +{
> +	struct dentry *d;
> +
> +	d = debugfs_create_file("wake_irq", S_IRUGO, NULL, NULL,
> +		&tegra_wake_irq_debug_fops);
> +	if (!d) {
> +		pr_info("Failed to create suspend_mode debug file\n");

Does this want to be a pr_err()?

-- 
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 18/28] ARM: tegra: add CPU_IDLE driver
  2011-01-24  2:01   ` Colin Cross
@ 2011-01-27 20:38     ` Stephen Boyd
  -1 siblings, 0 replies; 137+ messages in thread
From: Stephen Boyd @ 2011-01-27 20:38 UTC (permalink / raw)
  To: Colin Cross
  Cc: linux-tegra, Russell King, konkers, Gary King, linux-kernel,
	olof, linux-arm-kernel

On 01/23/2011 06:01 PM, Colin Cross wrote:
>
> +static int __init tegra_cpuidle_debug_init(void)
> +{
> +	struct dentry *dir;
> +	struct dentry *d;
> +
> +	dir = debugfs_create_dir("cpuidle", NULL);
> +	if (!dir)
> +		return -ENOMEM;
> +
> +	d = debugfs_create_file("lp2", S_IRUGO, dir, NULL,
> +		&tegra_lp2_debug_ops);
> +	if (!d)
> +		return -ENOMEM;
> +
> +	return 0;
> +}
> +#endif
> +
> +late_initcall(tegra_cpuidle_debug_init);

Will this compile with CONFIG_DEBUG_FS=n?

-- 
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.


^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 18/28] ARM: tegra: add CPU_IDLE driver
@ 2011-01-27 20:38     ` Stephen Boyd
  0 siblings, 0 replies; 137+ messages in thread
From: Stephen Boyd @ 2011-01-27 20:38 UTC (permalink / raw)
  To: linux-arm-kernel

On 01/23/2011 06:01 PM, Colin Cross wrote:
>
> +static int __init tegra_cpuidle_debug_init(void)
> +{
> +	struct dentry *dir;
> +	struct dentry *d;
> +
> +	dir = debugfs_create_dir("cpuidle", NULL);
> +	if (!dir)
> +		return -ENOMEM;
> +
> +	d = debugfs_create_file("lp2", S_IRUGO, dir, NULL,
> +		&tegra_lp2_debug_ops);
> +	if (!d)
> +		return -ENOMEM;
> +
> +	return 0;
> +}
> +#endif
> +
> +late_initcall(tegra_cpuidle_debug_init);

Will this compile with CONFIG_DEBUG_FS=n?

-- 
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 18/28] ARM: tegra: add CPU_IDLE driver
  2011-01-27 20:38     ` Stephen Boyd
@ 2011-01-27 21:51       ` Colin Cross
  -1 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-27 21:51 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: linux-tegra, Russell King, konkers, Gary King, linux-kernel,
	olof, linux-arm-kernel

On Thu, Jan 27, 2011 at 12:38 PM, Stephen Boyd <sboyd@codeaurora.org> wrote:
> On 01/23/2011 06:01 PM, Colin Cross wrote:
>>
>> +static int __init tegra_cpuidle_debug_init(void)
>> +{
>> +     struct dentry *dir;
>> +     struct dentry *d;
>> +
>> +     dir = debugfs_create_dir("cpuidle", NULL);
>> +     if (!dir)
>> +             return -ENOMEM;
>> +
>> +     d = debugfs_create_file("lp2", S_IRUGO, dir, NULL,
>> +             &tegra_lp2_debug_ops);
>> +     if (!d)
>> +             return -ENOMEM;
>> +
>> +     return 0;
>> +}
>> +#endif
>> +
>> +late_initcall(tegra_cpuidle_debug_init);
>
> Will this compile with CONFIG_DEBUG_FS=n?

Yes.  All debugfs ops return ERR_PTR(-ENODEV) if CONFIG_DEBUG_FS=n, so
tegra_cpuidle_debug_init will do nothing and return 0.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 18/28] ARM: tegra: add CPU_IDLE driver
@ 2011-01-27 21:51       ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-27 21:51 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Jan 27, 2011 at 12:38 PM, Stephen Boyd <sboyd@codeaurora.org> wrote:
> On 01/23/2011 06:01 PM, Colin Cross wrote:
>>
>> +static int __init tegra_cpuidle_debug_init(void)
>> +{
>> + ? ? struct dentry *dir;
>> + ? ? struct dentry *d;
>> +
>> + ? ? dir = debugfs_create_dir("cpuidle", NULL);
>> + ? ? if (!dir)
>> + ? ? ? ? ? ? return -ENOMEM;
>> +
>> + ? ? d = debugfs_create_file("lp2", S_IRUGO, dir, NULL,
>> + ? ? ? ? ? ? &tegra_lp2_debug_ops);
>> + ? ? if (!d)
>> + ? ? ? ? ? ? return -ENOMEM;
>> +
>> + ? ? return 0;
>> +}
>> +#endif
>> +
>> +late_initcall(tegra_cpuidle_debug_init);
>
> Will this compile with CONFIG_DEBUG_FS=n?

Yes.  All debugfs ops return ERR_PTR(-ENODEV) if CONFIG_DEBUG_FS=n, so
tegra_cpuidle_debug_init will do nothing and return 0.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 18/28] ARM: tegra: add CPU_IDLE driver
  2011-01-27 21:51       ` Colin Cross
@ 2011-01-27 22:07         ` Colin Cross
  -1 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-27 22:07 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: linux-tegra, Russell King, konkers, Gary King, linux-kernel,
	olof, linux-arm-kernel

On Thu, Jan 27, 2011 at 1:51 PM, Colin Cross <ccross@android.com> wrote:
> On Thu, Jan 27, 2011 at 12:38 PM, Stephen Boyd <sboyd@codeaurora.org> wrote:
>> On 01/23/2011 06:01 PM, Colin Cross wrote:
>>>
>>> +static int __init tegra_cpuidle_debug_init(void)
>>> +{
>>> +     struct dentry *dir;
>>> +     struct dentry *d;
>>> +
>>> +     dir = debugfs_create_dir("cpuidle", NULL);
>>> +     if (!dir)
>>> +             return -ENOMEM;
>>> +
>>> +     d = debugfs_create_file("lp2", S_IRUGO, dir, NULL,
>>> +             &tegra_lp2_debug_ops);
>>> +     if (!d)
>>> +             return -ENOMEM;
>>> +
>>> +     return 0;
>>> +}
>>> +#endif
>>> +
>>> +late_initcall(tegra_cpuidle_debug_init);
>>
>> Will this compile with CONFIG_DEBUG_FS=n?
>
> Yes.  All debugfs ops return ERR_PTR(-ENODEV) if CONFIG_DEBUG_FS=n, so
> tegra_cpuidle_debug_init will do nothing and return 0.
>

Oops no, it will not compile.  Will fix.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 18/28] ARM: tegra: add CPU_IDLE driver
@ 2011-01-27 22:07         ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-01-27 22:07 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Jan 27, 2011 at 1:51 PM, Colin Cross <ccross@android.com> wrote:
> On Thu, Jan 27, 2011 at 12:38 PM, Stephen Boyd <sboyd@codeaurora.org> wrote:
>> On 01/23/2011 06:01 PM, Colin Cross wrote:
>>>
>>> +static int __init tegra_cpuidle_debug_init(void)
>>> +{
>>> + ? ? struct dentry *dir;
>>> + ? ? struct dentry *d;
>>> +
>>> + ? ? dir = debugfs_create_dir("cpuidle", NULL);
>>> + ? ? if (!dir)
>>> + ? ? ? ? ? ? return -ENOMEM;
>>> +
>>> + ? ? d = debugfs_create_file("lp2", S_IRUGO, dir, NULL,
>>> + ? ? ? ? ? ? &tegra_lp2_debug_ops);
>>> + ? ? if (!d)
>>> + ? ? ? ? ? ? return -ENOMEM;
>>> +
>>> + ? ? return 0;
>>> +}
>>> +#endif
>>> +
>>> +late_initcall(tegra_cpuidle_debug_init);
>>
>> Will this compile with CONFIG_DEBUG_FS=n?
>
> Yes. ?All debugfs ops return ERR_PTR(-ENODEV) if CONFIG_DEBUG_FS=n, so
> tegra_cpuidle_debug_init will do nothing and return 0.
>

Oops no, it will not compile.  Will fix.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 02/28] ARM: gic: Add functions to save and restore gic state
  2011-01-24  2:01   ` Colin Cross
@ 2011-02-01 13:03     ` Russell King - ARM Linux
  -1 siblings, 0 replies; 137+ messages in thread
From: Russell King - ARM Linux @ 2011-02-01 13:03 UTC (permalink / raw)
  To: Colin Cross
  Cc: linux-tegra, konkers, Gary King, Catalin Marinas,
	Abhijeet Dharmapurikar, linux-kernel, olof, linux-arm-kernel,
	Linus Walleij

On Sun, Jan 23, 2011 at 06:01:07PM -0800, Colin Cross wrote:
> on systems with idle states which power-gate the logic including
> the gic, such as tegra, the gic distributor needs to be shut down
> and restored on entry and exit from the architecture idle code

I'd rather not add this and then have to weed it back out when we have a
proper interface for dealign with idle shutdown stuff.

Can some effort please be put into coming up with a proper interface
such as an idle notifier list rather than calling directly into random
parts of the kernel on idle.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 02/28] ARM: gic: Add functions to save and restore gic state
@ 2011-02-01 13:03     ` Russell King - ARM Linux
  0 siblings, 0 replies; 137+ messages in thread
From: Russell King - ARM Linux @ 2011-02-01 13:03 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, Jan 23, 2011 at 06:01:07PM -0800, Colin Cross wrote:
> on systems with idle states which power-gate the logic including
> the gic, such as tegra, the gic distributor needs to be shut down
> and restored on entry and exit from the architecture idle code

I'd rather not add this and then have to weed it back out when we have a
proper interface for dealign with idle shutdown stuff.

Can some effort please be put into coming up with a proper interface
such as an idle notifier list rather than calling directly into random
parts of the kernel on idle.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support for re-enabling l2x0
       [not found]           ` <-8932138696981683633@unknownmsgid>
  2011-02-04 23:32               ` Colin Cross
@ 2011-02-04 23:32               ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-02-04 23:32 UTC (permalink / raw)
  To: Will Deacon
  Cc: Santosh Shilimkar, Catalin Marinas, Russell King - ARM Linux,
	Linus Walleij, konkers-z5hGa2qSFaRBDgjK7y7TUQ, Tony Lindgren,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, olof-nZhT3qVonbNeoWH0uzbU5w,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On Tue, Jan 25, 2011 at 12:39 PM, Will Deacon <will.deacon-5wv7dgnIgG8@public.gmane.org> wrote:
> Santosh,
>
>> > > Maybe we need a notifier list which can be told when cpuidle
>> > events
>> > > happen, so that parts of the system such as VFP and L2 cache
>> > support
>> > > can do the right thing without having platforms add lots of stuff
>> > like
>> > >
>> > >         gic_secondary_init();
>> > >         gic_restore_interrupt_types();
>> > >         vfp_enable();
>> > >         l2x0_enable();
>> > >         twd_enable();
>> > >         ... etc ...
>> > >
>> > > in their SoC specific code.
>> >
>> > But do we need a strict order between such operations? The notifier
>> > call
>> > chain isn't too flexible.
>> >
>> I guess it does depends on how the archs have integrated a9. Example
>> on OMAP there are different power modes possible.
>>       1. CPU context ,TWD lost
>>       2. CPU context ,TWD + L1 is lost
>>       3. CPU context + L1 is lost + GIC lost
>>       4. CPU context + L1 is lost + GIC lost + L2 lost
>> So there is need to have flexibility of calling these function
>> based on power modes. I don't know how notifiers can give
>> this flexibility
>
> Well if you set the priority fields in the notifier blocks correctly
> then you can just return NOTIFY_STOP when you've saved/restored as much
> as you want. This assumes of course that you can identify which power
> mode you're entering/leaving and that each one is `deeper' than the previous.

I doubt its possible to create an order that will work for all
architectures, and returning NOTIFY_STOP would require the decision on
when to finish to be made by the notifier block instead of the
platform code.

Tegra has three possible idle modes:

1.  WFI - nothing reset
2.  CPU, TWD, L1, GIC lost, L2 needs to be disabled but not reset
3.  CPU, TWD, L1, GIC, and L2 lost

CPU and L1 are already handled by the platform-specific suspend code.
TWD is handled by the clockevents broadcast notifiers.  That leaves L2
and GIC.

The L2 can be idled in two ways: just disable it, but keep the
contents, which prevents having to refill the cache, or a full reset.
Disabling can already be handled by outer_cache_disable().

The GIC needs to be idled differently depending on which cpus are
idle.  On Tegra, if both cpus are idle, the secondary cpu needs to
disable its GIC and go to WFI, while the first cpu saves the GIC
distributor state and powers off both cpus.

While it seems possible to handle all of these cases with a notifier
chain, the amount of platform-specific knowledge and ordering seems
too much to make it worthwhile.

If the ordering requirements are close enough between platforms, or
non-existent (I don't think there are any requirements on Tegra), a
notifier chain with a mask of the hardware that is being reset could
work.  On Tegra, in case 2 above, CPU1 would call:

idle_notify(IDLE_ENTER | GIC_DISABLE)

and CPU0 would call:

idle_notify(IDLE_ENTER | GIC_DISABLE | GIC_SAVE | L2_DISABLE)
--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support for re-enabling l2x0
@ 2011-02-04 23:32               ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-02-04 23:32 UTC (permalink / raw)
  To: Will Deacon
  Cc: Santosh Shilimkar, Catalin Marinas, Russell King - ARM Linux,
	Linus Walleij, konkers, Tony Lindgren, linux-kernel, linux-tegra,
	olof, linux-arm-kernel

On Tue, Jan 25, 2011 at 12:39 PM, Will Deacon <will.deacon@arm.com> wrote:
> Santosh,
>
>> > > Maybe we need a notifier list which can be told when cpuidle
>> > events
>> > > happen, so that parts of the system such as VFP and L2 cache
>> > support
>> > > can do the right thing without having platforms add lots of stuff
>> > like
>> > >
>> > >         gic_secondary_init();
>> > >         gic_restore_interrupt_types();
>> > >         vfp_enable();
>> > >         l2x0_enable();
>> > >         twd_enable();
>> > >         ... etc ...
>> > >
>> > > in their SoC specific code.
>> >
>> > But do we need a strict order between such operations? The notifier
>> > call
>> > chain isn't too flexible.
>> >
>> I guess it does depends on how the archs have integrated a9. Example
>> on OMAP there are different power modes possible.
>>       1. CPU context ,TWD lost
>>       2. CPU context ,TWD + L1 is lost
>>       3. CPU context + L1 is lost + GIC lost
>>       4. CPU context + L1 is lost + GIC lost + L2 lost
>> So there is need to have flexibility of calling these function
>> based on power modes. I don't know how notifiers can give
>> this flexibility
>
> Well if you set the priority fields in the notifier blocks correctly
> then you can just return NOTIFY_STOP when you've saved/restored as much
> as you want. This assumes of course that you can identify which power
> mode you're entering/leaving and that each one is `deeper' than the previous.

I doubt its possible to create an order that will work for all
architectures, and returning NOTIFY_STOP would require the decision on
when to finish to be made by the notifier block instead of the
platform code.

Tegra has three possible idle modes:

1.  WFI - nothing reset
2.  CPU, TWD, L1, GIC lost, L2 needs to be disabled but not reset
3.  CPU, TWD, L1, GIC, and L2 lost

CPU and L1 are already handled by the platform-specific suspend code.
TWD is handled by the clockevents broadcast notifiers.  That leaves L2
and GIC.

The L2 can be idled in two ways: just disable it, but keep the
contents, which prevents having to refill the cache, or a full reset.
Disabling can already be handled by outer_cache_disable().

The GIC needs to be idled differently depending on which cpus are
idle.  On Tegra, if both cpus are idle, the secondary cpu needs to
disable its GIC and go to WFI, while the first cpu saves the GIC
distributor state and powers off both cpus.

While it seems possible to handle all of these cases with a notifier
chain, the amount of platform-specific knowledge and ordering seems
too much to make it worthwhile.

If the ordering requirements are close enough between platforms, or
non-existent (I don't think there are any requirements on Tegra), a
notifier chain with a mask of the hardware that is being reset could
work.  On Tegra, in case 2 above, CPU1 would call:

idle_notify(IDLE_ENTER | GIC_DISABLE)

and CPU0 would call:

idle_notify(IDLE_ENTER | GIC_DISABLE | GIC_SAVE | L2_DISABLE)

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support for re-enabling l2x0
@ 2011-02-04 23:32               ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-02-04 23:32 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jan 25, 2011 at 12:39 PM, Will Deacon <will.deacon@arm.com> wrote:
> Santosh,
>
>> > > Maybe we need a notifier list which can be told when cpuidle
>> > events
>> > > happen, so that parts of the system such as VFP and L2 cache
>> > support
>> > > can do the right thing without having platforms add lots of stuff
>> > like
>> > >
>> > > ? ? ? ? gic_secondary_init();
>> > > ? ? ? ? gic_restore_interrupt_types();
>> > > ? ? ? ? vfp_enable();
>> > > ? ? ? ? l2x0_enable();
>> > > ? ? ? ? twd_enable();
>> > > ? ? ? ? ... etc ...
>> > >
>> > > in their SoC specific code.
>> >
>> > But do we need a strict order between such operations? The notifier
>> > call
>> > chain isn't too flexible.
>> >
>> I guess it does depends on how the archs have integrated a9. Example
>> on OMAP there are different power modes possible.
>> ? ? ? 1. CPU context ,TWD lost
>> ? ? ? 2. CPU context ,TWD + L1 is lost
>> ? ? ? 3. CPU context + L1 is lost + GIC lost
>> ? ? ? 4. CPU context + L1 is lost + GIC lost + L2 lost
>> So there is need to have flexibility of calling these function
>> based on power modes. I don't know how notifiers can give
>> this flexibility
>
> Well if you set the priority fields in the notifier blocks correctly
> then you can just return NOTIFY_STOP when you've saved/restored as much
> as you want. This assumes of course that you can identify which power
> mode you're entering/leaving and that each one is `deeper' than the previous.

I doubt its possible to create an order that will work for all
architectures, and returning NOTIFY_STOP would require the decision on
when to finish to be made by the notifier block instead of the
platform code.

Tegra has three possible idle modes:

1.  WFI - nothing reset
2.  CPU, TWD, L1, GIC lost, L2 needs to be disabled but not reset
3.  CPU, TWD, L1, GIC, and L2 lost

CPU and L1 are already handled by the platform-specific suspend code.
TWD is handled by the clockevents broadcast notifiers.  That leaves L2
and GIC.

The L2 can be idled in two ways: just disable it, but keep the
contents, which prevents having to refill the cache, or a full reset.
Disabling can already be handled by outer_cache_disable().

The GIC needs to be idled differently depending on which cpus are
idle.  On Tegra, if both cpus are idle, the secondary cpu needs to
disable its GIC and go to WFI, while the first cpu saves the GIC
distributor state and powers off both cpus.

While it seems possible to handle all of these cases with a notifier
chain, the amount of platform-specific knowledge and ordering seems
too much to make it worthwhile.

If the ordering requirements are close enough between platforms, or
non-existent (I don't think there are any requirements on Tegra), a
notifier chain with a mask of the hardware that is being reset could
work.  On Tegra, in case 2 above, CPU1 would call:

idle_notify(IDLE_ENTER | GIC_DISABLE)

and CPU0 would call:

idle_notify(IDLE_ENTER | GIC_DISABLE | GIC_SAVE | L2_DISABLE)

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support for re-enabling l2x0
  2011-02-04 23:32               ` Colin Cross
@ 2011-02-04 23:43                 ` Russell King - ARM Linux
  -1 siblings, 0 replies; 137+ messages in thread
From: Russell King - ARM Linux @ 2011-02-04 23:43 UTC (permalink / raw)
  To: Colin Cross
  Cc: Will Deacon, Santosh Shilimkar, Catalin Marinas, Linus Walleij,
	konkers, Tony Lindgren, linux-kernel, linux-tegra, olof,
	linux-arm-kernel

On Fri, Feb 04, 2011 at 05:32:26PM -0600, Colin Cross wrote:
> On Tue, Jan 25, 2011 at 12:39 PM, Will Deacon <will.deacon@arm.com> wrote:
> > Well if you set the priority fields in the notifier blocks correctly
> > then you can just return NOTIFY_STOP when you've saved/restored as much
> > as you want. This assumes of course that you can identify which power
> > mode you're entering/leaving and that each one is `deeper' than the previous.
> 
> I doubt its possible to create an order that will work for all
> architectures, and returning NOTIFY_STOP would require the decision on
> when to finish to be made by the notifier block instead of the
> platform code.
> 
> Tegra has three possible idle modes:
> 
> 1.  WFI - nothing reset
> 2.  CPU, TWD, L1, GIC lost, L2 needs to be disabled but not reset
> 3.  CPU, TWD, L1, GIC, and L2 lost

(2) and (3) don't sound like per-cpu modes but system modes.  If you're
having to disable L2, then your other CPU can't be active.

> CPU and L1 are already handled by the platform-specific suspend code.
> TWD is handled by the clockevents broadcast notifiers.  That leaves L2
> and GIC.

GIC can be handled in just the same way - upon a CPU idling and it
being decided that the CPU should enter low power mode, the idle states
are entered which does what's required with TWD, L1, VFP, Neon, etc.
We just need the GIC CPU interface included in there.

When both CPUs are idled, then the L2 comes into play, and then modes
(2) and (3) become possible and this is where you start doing the extra
stuff.

Note that you have to do it that way anyway, because you can't save
the state of the other CPU's GIC without doing an IPI call, which
could kick it out of its idle mode.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support for re-enabling l2x0
@ 2011-02-04 23:43                 ` Russell King - ARM Linux
  0 siblings, 0 replies; 137+ messages in thread
From: Russell King - ARM Linux @ 2011-02-04 23:43 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Feb 04, 2011 at 05:32:26PM -0600, Colin Cross wrote:
> On Tue, Jan 25, 2011 at 12:39 PM, Will Deacon <will.deacon@arm.com> wrote:
> > Well if you set the priority fields in the notifier blocks correctly
> > then you can just return NOTIFY_STOP when you've saved/restored as much
> > as you want. This assumes of course that you can identify which power
> > mode you're entering/leaving and that each one is `deeper' than the previous.
> 
> I doubt its possible to create an order that will work for all
> architectures, and returning NOTIFY_STOP would require the decision on
> when to finish to be made by the notifier block instead of the
> platform code.
> 
> Tegra has three possible idle modes:
> 
> 1.  WFI - nothing reset
> 2.  CPU, TWD, L1, GIC lost, L2 needs to be disabled but not reset
> 3.  CPU, TWD, L1, GIC, and L2 lost

(2) and (3) don't sound like per-cpu modes but system modes.  If you're
having to disable L2, then your other CPU can't be active.

> CPU and L1 are already handled by the platform-specific suspend code.
> TWD is handled by the clockevents broadcast notifiers.  That leaves L2
> and GIC.

GIC can be handled in just the same way - upon a CPU idling and it
being decided that the CPU should enter low power mode, the idle states
are entered which does what's required with TWD, L1, VFP, Neon, etc.
We just need the GIC CPU interface included in there.

When both CPUs are idled, then the L2 comes into play, and then modes
(2) and (3) become possible and this is where you start doing the extra
stuff.

Note that you have to do it that way anyway, because you can't save
the state of the other CPU's GIC without doing an IPI call, which
could kick it out of its idle mode.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support for re-enabling l2x0
  2011-02-04 23:43                 ` Russell King - ARM Linux
  (?)
@ 2011-02-05  1:44                     ` Colin Cross
  -1 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-02-05  1:44 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Will Deacon, Santosh Shilimkar, Catalin Marinas, Linus Walleij,
	konkers-z5hGa2qSFaRBDgjK7y7TUQ, Tony Lindgren,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, olof-nZhT3qVonbNeoWH0uzbU5w,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On Fri, Feb 4, 2011 at 5:43 PM, Russell King - ARM Linux
<linux-lFZ/pmaqli7XmaaqVzeoHQ@public.gmane.org> wrote:
> On Fri, Feb 04, 2011 at 05:32:26PM -0600, Colin Cross wrote:
>> On Tue, Jan 25, 2011 at 12:39 PM, Will Deacon <will.deacon-5wv7dgnIgG8@public.gmane.org> wrote:
>> > Well if you set the priority fields in the notifier blocks correctly
>> > then you can just return NOTIFY_STOP when you've saved/restored as much
>> > as you want. This assumes of course that you can identify which power
>> > mode you're entering/leaving and that each one is `deeper' than the previous.
>>
>> I doubt its possible to create an order that will work for all
>> architectures, and returning NOTIFY_STOP would require the decision on
>> when to finish to be made by the notifier block instead of the
>> platform code.
>>
>> Tegra has three possible idle modes:
>>
>> 1.  WFI - nothing reset
>> 2.  CPU, TWD, L1, GIC lost, L2 needs to be disabled but not reset
>> 3.  CPU, TWD, L1, GIC, and L2 lost
>
> (2) and (3) don't sound like per-cpu modes but system modes.  If you're
> having to disable L2, then your other CPU can't be active.

Yes, 2 and 3 require both CPUs to be idle.  Unfortunately, on Tegra,
it is important to use at least 2 as much as possible, because the two
CPUs are not individually power gated.

>> CPU and L1 are already handled by the platform-specific suspend code.
>> TWD is handled by the clockevents broadcast notifiers.  That leaves L2
>> and GIC.
>
> GIC can be handled in just the same way - upon a CPU idling and it
> being decided that the CPU should enter low power mode, the idle states
> are entered which does what's required with TWD, L1, VFP, Neon, etc.
> We just need the GIC CPU interface included in there.
>
> When both CPUs are idled, then the L2 comes into play, and then modes
> (2) and (3) become possible and this is where you start doing the extra
> stuff.

Are you suggesting that the idle notifiers only handle TWD, L1, VFP,
Neon, and GIC?  That would simplify things, as there are probably no
ordering requirements, and they should be the same for any platform
that uses them.

> Note that you have to do it that way anyway, because you can't save
> the state of the other CPU's GIC without doing an IPI call, which
> could kick it out of its idle mode.

There is currently no state that needs to be saved in the GIC CPU
registers, they can all be reinitialized.
--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support for re-enabling l2x0
@ 2011-02-05  1:44                     ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-02-05  1:44 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Will Deacon, Santosh Shilimkar, Catalin Marinas, Linus Walleij,
	konkers, Tony Lindgren, linux-kernel, linux-tegra, olof,
	linux-arm-kernel

On Fri, Feb 4, 2011 at 5:43 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Fri, Feb 04, 2011 at 05:32:26PM -0600, Colin Cross wrote:
>> On Tue, Jan 25, 2011 at 12:39 PM, Will Deacon <will.deacon@arm.com> wrote:
>> > Well if you set the priority fields in the notifier blocks correctly
>> > then you can just return NOTIFY_STOP when you've saved/restored as much
>> > as you want. This assumes of course that you can identify which power
>> > mode you're entering/leaving and that each one is `deeper' than the previous.
>>
>> I doubt its possible to create an order that will work for all
>> architectures, and returning NOTIFY_STOP would require the decision on
>> when to finish to be made by the notifier block instead of the
>> platform code.
>>
>> Tegra has three possible idle modes:
>>
>> 1.  WFI - nothing reset
>> 2.  CPU, TWD, L1, GIC lost, L2 needs to be disabled but not reset
>> 3.  CPU, TWD, L1, GIC, and L2 lost
>
> (2) and (3) don't sound like per-cpu modes but system modes.  If you're
> having to disable L2, then your other CPU can't be active.

Yes, 2 and 3 require both CPUs to be idle.  Unfortunately, on Tegra,
it is important to use at least 2 as much as possible, because the two
CPUs are not individually power gated.

>> CPU and L1 are already handled by the platform-specific suspend code.
>> TWD is handled by the clockevents broadcast notifiers.  That leaves L2
>> and GIC.
>
> GIC can be handled in just the same way - upon a CPU idling and it
> being decided that the CPU should enter low power mode, the idle states
> are entered which does what's required with TWD, L1, VFP, Neon, etc.
> We just need the GIC CPU interface included in there.
>
> When both CPUs are idled, then the L2 comes into play, and then modes
> (2) and (3) become possible and this is where you start doing the extra
> stuff.

Are you suggesting that the idle notifiers only handle TWD, L1, VFP,
Neon, and GIC?  That would simplify things, as there are probably no
ordering requirements, and they should be the same for any platform
that uses them.

> Note that you have to do it that way anyway, because you can't save
> the state of the other CPU's GIC without doing an IPI call, which
> could kick it out of its idle mode.

There is currently no state that needs to be saved in the GIC CPU
registers, they can all be reinitialized.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support for re-enabling l2x0
@ 2011-02-05  1:44                     ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-02-05  1:44 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Feb 4, 2011 at 5:43 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Fri, Feb 04, 2011 at 05:32:26PM -0600, Colin Cross wrote:
>> On Tue, Jan 25, 2011 at 12:39 PM, Will Deacon <will.deacon@arm.com> wrote:
>> > Well if you set the priority fields in the notifier blocks correctly
>> > then you can just return NOTIFY_STOP when you've saved/restored as much
>> > as you want. This assumes of course that you can identify which power
>> > mode you're entering/leaving and that each one is `deeper' than the previous.
>>
>> I doubt its possible to create an order that will work for all
>> architectures, and returning NOTIFY_STOP would require the decision on
>> when to finish to be made by the notifier block instead of the
>> platform code.
>>
>> Tegra has three possible idle modes:
>>
>> 1. ?WFI - nothing reset
>> 2. ?CPU, TWD, L1, GIC lost, L2 needs to be disabled but not reset
>> 3. ?CPU, TWD, L1, GIC, and L2 lost
>
> (2) and (3) don't sound like per-cpu modes but system modes. ?If you're
> having to disable L2, then your other CPU can't be active.

Yes, 2 and 3 require both CPUs to be idle.  Unfortunately, on Tegra,
it is important to use at least 2 as much as possible, because the two
CPUs are not individually power gated.

>> CPU and L1 are already handled by the platform-specific suspend code.
>> TWD is handled by the clockevents broadcast notifiers. ?That leaves L2
>> and GIC.
>
> GIC can be handled in just the same way - upon a CPU idling and it
> being decided that the CPU should enter low power mode, the idle states
> are entered which does what's required with TWD, L1, VFP, Neon, etc.
> We just need the GIC CPU interface included in there.
>
> When both CPUs are idled, then the L2 comes into play, and then modes
> (2) and (3) become possible and this is where you start doing the extra
> stuff.

Are you suggesting that the idle notifiers only handle TWD, L1, VFP,
Neon, and GIC?  That would simplify things, as there are probably no
ordering requirements, and they should be the same for any platform
that uses them.

> Note that you have to do it that way anyway, because you can't save
> the state of the other CPU's GIC without doing an IPI call, which
> could kick it out of its idle mode.

There is currently no state that needs to be saved in the GIC CPU
registers, they can all be reinitialized.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* RE: [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support for re-enabling l2x0
  2011-02-05  1:44                     ` Colin Cross
  (?)
@ 2011-02-05  7:51                         ` Santosh Shilimkar
  -1 siblings, 0 replies; 137+ messages in thread
From: Santosh Shilimkar @ 2011-02-05  7:51 UTC (permalink / raw)
  To: Colin Cross, Russell King - ARM Linux
  Cc: Will Deacon, Catalin Marinas, Linus Walleij,
	konkers-z5hGa2qSFaRBDgjK7y7TUQ, Tony Lindgren,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, olof-nZhT3qVonbNeoWH0uzbU5w,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

> -----Original Message-----
> From: ccross-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org [mailto:ccross-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org] On Behalf Of
> Colin Cross
> Sent: Saturday, February 05, 2011 7:15 AM
> To: Russell King - ARM Linux
> Cc: Will Deacon; Santosh Shilimkar; Catalin Marinas; Linus Walleij;
> konkers-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org; Tony Lindgren; linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org;
> linux-tegra-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; olof-nZhT3qVonbNeoWH0uzbU5w@public.gmane.org; linux-arm-
> kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
> Subject: Re: [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support for
> re-enabling l2x0
>
> On Fri, Feb 4, 2011 at 5:43 PM, Russell King - ARM Linux
> <linux-lFZ/pmaqli7XmaaqVzeoHQ@public.gmane.org> wrote:
> > On Fri, Feb 04, 2011 at 05:32:26PM -0600, Colin Cross wrote:
> >> On Tue, Jan 25, 2011 at 12:39 PM, Will Deacon
> <will.deacon-5wv7dgnIgG8@public.gmane.org> wrote:
> >> > Well if you set the priority fields in the notifier blocks
> correctly
> >> > then you can just return NOTIFY_STOP when you've saved/restored
> as much
> >> > as you want. This assumes of course that you can identify which
> power
> >> > mode you're entering/leaving and that each one is `deeper' than
> the previous.
> >>
> >> I doubt its possible to create an order that will work for all
> >> architectures, and returning NOTIFY_STOP would require the
> decision on
> >> when to finish to be made by the notifier block instead of the
> >> platform code.
> >>
> >> Tegra has three possible idle modes:
> >>
> >> 1.  WFI - nothing reset
> >> 2.  CPU, TWD, L1, GIC lost, L2 needs to be disabled but not reset
> >> 3.  CPU, TWD, L1, GIC, and L2 lost
> >
> > (2) and (3) don't sound like per-cpu modes but system modes.  If
> you're
> > having to disable L2, then your other CPU can't be active.
>
> Yes, 2 and 3 require both CPUs to be idle.  Unfortunately, on Tegra,
> it is important to use at least 2 as much as possible, because the
> two
> CPUs are not individually power gated.
>
> >> CPU and L1 are already handled by the platform-specific suspend
> code.
> >> TWD is handled by the clockevents broadcast notifiers.  That
> leaves L2
> >> and GIC.
> >
> > GIC can be handled in just the same way - upon a CPU idling and it
> > being decided that the CPU should enter low power mode, the idle
> states
> > are entered which does what's required with TWD, L1, VFP, Neon,
> etc.
> > We just need the GIC CPU interface included in there.
> >
> > When both CPUs are idled, then the L2 comes into play, and then
> modes
> > (2) and (3) become possible and this is where you start doing the
> extra
> > stuff.
>
> Are you suggesting that the idle notifiers only handle TWD, L1, VFP,
> Neon, and GIC?  That would simplify things, as there are probably no
> ordering requirements, and they should be the same for any platform
> that uses them.
>
> > Note that you have to do it that way anyway, because you can't
> save
> > the state of the other CPU's GIC without doing an IPI call, which
> > could kick it out of its idle mode.
>
> There is currently no state that needs to be saved in the GIC CPU
> registers, they can all be reinitialized.
GIC save/restore on OMAP follows different strategy. There is a
Predefined layout to save content and restore is done atomically
by boot ROM code.
L2 cache also same case. Only AUXCTRL needs to be programmed on
wakeup from low power mode and that too with secure call. Rest
of the registers are managed by boot ROM code.

TWD is already managed through framework. Othe CPU low power
sequence is very small and OMAP has restrictions on the last
core to go down and first to wakeup.

So at least I don't see any use of common notifiers for GIC
and L2 will help OMAP lower power code.

Regards,
Santosh
--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 137+ messages in thread

* RE: [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support for re-enabling l2x0
@ 2011-02-05  7:51                         ` Santosh Shilimkar
  0 siblings, 0 replies; 137+ messages in thread
From: Santosh Shilimkar @ 2011-02-05  7:51 UTC (permalink / raw)
  To: Colin Cross, Russell King - ARM Linux
  Cc: Will Deacon, Catalin Marinas, Linus Walleij, konkers,
	Tony Lindgren, linux-kernel, linux-tegra, olof, linux-arm-kernel

> -----Original Message-----
> From: ccross@google.com [mailto:ccross@google.com] On Behalf Of
> Colin Cross
> Sent: Saturday, February 05, 2011 7:15 AM
> To: Russell King - ARM Linux
> Cc: Will Deacon; Santosh Shilimkar; Catalin Marinas; Linus Walleij;
> konkers@android.com; Tony Lindgren; linux-kernel@vger.kernel.org;
> linux-tegra@vger.kernel.org; olof@lixom.net; linux-arm-
> kernel@lists.infradead.org
> Subject: Re: [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support for
> re-enabling l2x0
>
> On Fri, Feb 4, 2011 at 5:43 PM, Russell King - ARM Linux
> <linux@arm.linux.org.uk> wrote:
> > On Fri, Feb 04, 2011 at 05:32:26PM -0600, Colin Cross wrote:
> >> On Tue, Jan 25, 2011 at 12:39 PM, Will Deacon
> <will.deacon@arm.com> wrote:
> >> > Well if you set the priority fields in the notifier blocks
> correctly
> >> > then you can just return NOTIFY_STOP when you've saved/restored
> as much
> >> > as you want. This assumes of course that you can identify which
> power
> >> > mode you're entering/leaving and that each one is `deeper' than
> the previous.
> >>
> >> I doubt its possible to create an order that will work for all
> >> architectures, and returning NOTIFY_STOP would require the
> decision on
> >> when to finish to be made by the notifier block instead of the
> >> platform code.
> >>
> >> Tegra has three possible idle modes:
> >>
> >> 1.  WFI - nothing reset
> >> 2.  CPU, TWD, L1, GIC lost, L2 needs to be disabled but not reset
> >> 3.  CPU, TWD, L1, GIC, and L2 lost
> >
> > (2) and (3) don't sound like per-cpu modes but system modes.  If
> you're
> > having to disable L2, then your other CPU can't be active.
>
> Yes, 2 and 3 require both CPUs to be idle.  Unfortunately, on Tegra,
> it is important to use at least 2 as much as possible, because the
> two
> CPUs are not individually power gated.
>
> >> CPU and L1 are already handled by the platform-specific suspend
> code.
> >> TWD is handled by the clockevents broadcast notifiers.  That
> leaves L2
> >> and GIC.
> >
> > GIC can be handled in just the same way - upon a CPU idling and it
> > being decided that the CPU should enter low power mode, the idle
> states
> > are entered which does what's required with TWD, L1, VFP, Neon,
> etc.
> > We just need the GIC CPU interface included in there.
> >
> > When both CPUs are idled, then the L2 comes into play, and then
> modes
> > (2) and (3) become possible and this is where you start doing the
> extra
> > stuff.
>
> Are you suggesting that the idle notifiers only handle TWD, L1, VFP,
> Neon, and GIC?  That would simplify things, as there are probably no
> ordering requirements, and they should be the same for any platform
> that uses them.
>
> > Note that you have to do it that way anyway, because you can't
> save
> > the state of the other CPU's GIC without doing an IPI call, which
> > could kick it out of its idle mode.
>
> There is currently no state that needs to be saved in the GIC CPU
> registers, they can all be reinitialized.
GIC save/restore on OMAP follows different strategy. There is a
Predefined layout to save content and restore is done atomically
by boot ROM code.
L2 cache also same case. Only AUXCTRL needs to be programmed on
wakeup from low power mode and that too with secure call. Rest
of the registers are managed by boot ROM code.

TWD is already managed through framework. Othe CPU low power
sequence is very small and OMAP has restrictions on the last
core to go down and first to wakeup.

So at least I don't see any use of common notifiers for GIC
and L2 will help OMAP lower power code.

Regards,
Santosh

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support for re-enabling l2x0
@ 2011-02-05  7:51                         ` Santosh Shilimkar
  0 siblings, 0 replies; 137+ messages in thread
From: Santosh Shilimkar @ 2011-02-05  7:51 UTC (permalink / raw)
  To: linux-arm-kernel

> -----Original Message-----
> From: ccross at google.com [mailto:ccross at google.com] On Behalf Of
> Colin Cross
> Sent: Saturday, February 05, 2011 7:15 AM
> To: Russell King - ARM Linux
> Cc: Will Deacon; Santosh Shilimkar; Catalin Marinas; Linus Walleij;
> konkers at android.com; Tony Lindgren; linux-kernel at vger.kernel.org;
> linux-tegra at vger.kernel.org; olof at lixom.net; linux-arm-
> kernel at lists.infradead.org
> Subject: Re: [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support for
> re-enabling l2x0
>
> On Fri, Feb 4, 2011 at 5:43 PM, Russell King - ARM Linux
> <linux@arm.linux.org.uk> wrote:
> > On Fri, Feb 04, 2011 at 05:32:26PM -0600, Colin Cross wrote:
> >> On Tue, Jan 25, 2011 at 12:39 PM, Will Deacon
> <will.deacon@arm.com> wrote:
> >> > Well if you set the priority fields in the notifier blocks
> correctly
> >> > then you can just return NOTIFY_STOP when you've saved/restored
> as much
> >> > as you want. This assumes of course that you can identify which
> power
> >> > mode you're entering/leaving and that each one is `deeper' than
> the previous.
> >>
> >> I doubt its possible to create an order that will work for all
> >> architectures, and returning NOTIFY_STOP would require the
> decision on
> >> when to finish to be made by the notifier block instead of the
> >> platform code.
> >>
> >> Tegra has three possible idle modes:
> >>
> >> 1. ?WFI - nothing reset
> >> 2. ?CPU, TWD, L1, GIC lost, L2 needs to be disabled but not reset
> >> 3. ?CPU, TWD, L1, GIC, and L2 lost
> >
> > (2) and (3) don't sound like per-cpu modes but system modes. ?If
> you're
> > having to disable L2, then your other CPU can't be active.
>
> Yes, 2 and 3 require both CPUs to be idle.  Unfortunately, on Tegra,
> it is important to use at least 2 as much as possible, because the
> two
> CPUs are not individually power gated.
>
> >> CPU and L1 are already handled by the platform-specific suspend
> code.
> >> TWD is handled by the clockevents broadcast notifiers. ?That
> leaves L2
> >> and GIC.
> >
> > GIC can be handled in just the same way - upon a CPU idling and it
> > being decided that the CPU should enter low power mode, the idle
> states
> > are entered which does what's required with TWD, L1, VFP, Neon,
> etc.
> > We just need the GIC CPU interface included in there.
> >
> > When both CPUs are idled, then the L2 comes into play, and then
> modes
> > (2) and (3) become possible and this is where you start doing the
> extra
> > stuff.
>
> Are you suggesting that the idle notifiers only handle TWD, L1, VFP,
> Neon, and GIC?  That would simplify things, as there are probably no
> ordering requirements, and they should be the same for any platform
> that uses them.
>
> > Note that you have to do it that way anyway, because you can't
> save
> > the state of the other CPU's GIC without doing an IPI call, which
> > could kick it out of its idle mode.
>
> There is currently no state that needs to be saved in the GIC CPU
> registers, they can all be reinitialized.
GIC save/restore on OMAP follows different strategy. There is a
Predefined layout to save content and restore is done atomically
by boot ROM code.
L2 cache also same case. Only AUXCTRL needs to be programmed on
wakeup from low power mode and that too with secure call. Rest
of the registers are managed by boot ROM code.

TWD is already managed through framework. Othe CPU low power
sequence is very small and OMAP has restrictions on the last
core to go down and first to wakeup.

So at least I don't see any use of common notifiers for GIC
and L2 will help OMAP lower power code.

Regards,
Santosh

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support for re-enabling l2x0
  2011-02-05  7:51                         ` Santosh Shilimkar
  (?)
@ 2011-02-05  9:47                             ` Russell King - ARM Linux
  -1 siblings, 0 replies; 137+ messages in thread
From: Russell King - ARM Linux @ 2011-02-05  9:47 UTC (permalink / raw)
  To: Santosh Shilimkar
  Cc: Colin Cross, Will Deacon, Catalin Marinas, Linus Walleij,
	konkers-z5hGa2qSFaRBDgjK7y7TUQ, Tony Lindgren,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, olof-nZhT3qVonbNeoWH0uzbU5w,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On Sat, Feb 05, 2011 at 01:21:24PM +0530, Santosh Shilimkar wrote:
> GIC save/restore on OMAP follows different strategy. There is a
> Predefined layout to save content and restore is done atomically
> by boot ROM code.
> L2 cache also same case. Only AUXCTRL needs to be programmed on
> wakeup from low power mode and that too with secure call. Rest
> of the registers are managed by boot ROM code.
> 
> TWD is already managed through framework. Othe CPU low power
> sequence is very small and OMAP has restrictions on the last
> core to go down and first to wakeup.
> 
> So at least I don't see any use of common notifiers for GIC
> and L2 will help OMAP lower power code.

What this means is that we're going to end up littering things like GIC
and other stuff with lots of individual SoC specific code to save state
into individual SoC specific structures.  This is not sane, and we're
not going to corrupt generic code with SoC specific code.
--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support for re-enabling l2x0
@ 2011-02-05  9:47                             ` Russell King - ARM Linux
  0 siblings, 0 replies; 137+ messages in thread
From: Russell King - ARM Linux @ 2011-02-05  9:47 UTC (permalink / raw)
  To: Santosh Shilimkar
  Cc: Colin Cross, Will Deacon, Catalin Marinas, Linus Walleij,
	konkers, Tony Lindgren, linux-kernel, linux-tegra, olof,
	linux-arm-kernel

On Sat, Feb 05, 2011 at 01:21:24PM +0530, Santosh Shilimkar wrote:
> GIC save/restore on OMAP follows different strategy. There is a
> Predefined layout to save content and restore is done atomically
> by boot ROM code.
> L2 cache also same case. Only AUXCTRL needs to be programmed on
> wakeup from low power mode and that too with secure call. Rest
> of the registers are managed by boot ROM code.
> 
> TWD is already managed through framework. Othe CPU low power
> sequence is very small and OMAP has restrictions on the last
> core to go down and first to wakeup.
> 
> So at least I don't see any use of common notifiers for GIC
> and L2 will help OMAP lower power code.

What this means is that we're going to end up littering things like GIC
and other stuff with lots of individual SoC specific code to save state
into individual SoC specific structures.  This is not sane, and we're
not going to corrupt generic code with SoC specific code.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support for re-enabling l2x0
@ 2011-02-05  9:47                             ` Russell King - ARM Linux
  0 siblings, 0 replies; 137+ messages in thread
From: Russell King - ARM Linux @ 2011-02-05  9:47 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, Feb 05, 2011 at 01:21:24PM +0530, Santosh Shilimkar wrote:
> GIC save/restore on OMAP follows different strategy. There is a
> Predefined layout to save content and restore is done atomically
> by boot ROM code.
> L2 cache also same case. Only AUXCTRL needs to be programmed on
> wakeup from low power mode and that too with secure call. Rest
> of the registers are managed by boot ROM code.
> 
> TWD is already managed through framework. Othe CPU low power
> sequence is very small and OMAP has restrictions on the last
> core to go down and first to wakeup.
> 
> So at least I don't see any use of common notifiers for GIC
> and L2 will help OMAP lower power code.

What this means is that we're going to end up littering things like GIC
and other stuff with lots of individual SoC specific code to save state
into individual SoC specific structures.  This is not sane, and we're
not going to corrupt generic code with SoC specific code.

^ permalink raw reply	[flat|nested] 137+ messages in thread

* RE: [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support forre-enabling l2x0
  2011-02-05  9:47                             ` Russell King - ARM Linux
@ 2011-02-05 10:41                               ` Santosh Shilimkar
  -1 siblings, 0 replies; 137+ messages in thread
From: Santosh Shilimkar @ 2011-02-05 10:41 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Colin Cross, Will Deacon, Catalin Marinas, Linus Walleij,
	konkers, Tony Lindgren, linux-kernel, linux-tegra, olof,
	linux-arm-kernel

> -----Original Message-----
> From: Russell King - ARM Linux [mailto:linux@arm.linux.org.uk]
> Sent: Saturday, February 05, 2011 3:18 PM
> To: Santosh Shilimkar
> Cc: Colin Cross; Will Deacon; Catalin Marinas; Linus Walleij;
> konkers@android.com; Tony Lindgren; linux-kernel@vger.kernel.org;
> linux-tegra@vger.kernel.org; olof@lixom.net; linux-arm-
> kernel@lists.infradead.org
> Subject: Re: [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support
> forre-enabling l2x0
>
> On Sat, Feb 05, 2011 at 01:21:24PM +0530, Santosh Shilimkar wrote:
> > GIC save/restore on OMAP follows different strategy. There is a
> > Predefined layout to save content and restore is done atomically
> > by boot ROM code.
> > L2 cache also same case. Only AUXCTRL needs to be programmed on
> > wakeup from low power mode and that too with secure call. Rest
> > of the registers are managed by boot ROM code.
> >
> > TWD is already managed through framework. Othe CPU low power
> > sequence is very small and OMAP has restrictions on the last
> > core to go down and first to wakeup.
> >
> > So at least I don't see any use of common notifiers for GIC
> > and L2 will help OMAP lower power code.
>
> What this means is that we're going to end up littering things like
> GIC
> and other stuff with lots of individual SoC specific code to save
> state
> into individual SoC specific structures.  This is not sane, and
> we're
> not going to corrupt generic code with SoC specific code.

Fully agree and hence flagged it early.

Regards,
Santosh

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support forre-enabling l2x0
@ 2011-02-05 10:41                               ` Santosh Shilimkar
  0 siblings, 0 replies; 137+ messages in thread
From: Santosh Shilimkar @ 2011-02-05 10:41 UTC (permalink / raw)
  To: linux-arm-kernel

> -----Original Message-----
> From: Russell King - ARM Linux [mailto:linux at arm.linux.org.uk]
> Sent: Saturday, February 05, 2011 3:18 PM
> To: Santosh Shilimkar
> Cc: Colin Cross; Will Deacon; Catalin Marinas; Linus Walleij;
> konkers at android.com; Tony Lindgren; linux-kernel at vger.kernel.org;
> linux-tegra at vger.kernel.org; olof at lixom.net; linux-arm-
> kernel at lists.infradead.org
> Subject: Re: [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support
> forre-enabling l2x0
>
> On Sat, Feb 05, 2011 at 01:21:24PM +0530, Santosh Shilimkar wrote:
> > GIC save/restore on OMAP follows different strategy. There is a
> > Predefined layout to save content and restore is done atomically
> > by boot ROM code.
> > L2 cache also same case. Only AUXCTRL needs to be programmed on
> > wakeup from low power mode and that too with secure call. Rest
> > of the registers are managed by boot ROM code.
> >
> > TWD is already managed through framework. Othe CPU low power
> > sequence is very small and OMAP has restrictions on the last
> > core to go down and first to wakeup.
> >
> > So at least I don't see any use of common notifiers for GIC
> > and L2 will help OMAP lower power code.
>
> What this means is that we're going to end up littering things like
> GIC
> and other stuff with lots of individual SoC specific code to save
> state
> into individual SoC specific structures.  This is not sane, and
> we're
> not going to corrupt generic code with SoC specific code.

Fully agree and hence flagged it early.

Regards,
Santosh

^ permalink raw reply	[flat|nested] 137+ messages in thread

* Re: [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support forre-enabling l2x0
  2011-02-05 10:41                               ` Santosh Shilimkar
@ 2011-02-05 16:36                                 ` Colin Cross
  -1 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-02-05 16:36 UTC (permalink / raw)
  To: Santosh Shilimkar
  Cc: Russell King - ARM Linux, Will Deacon, Catalin Marinas,
	Linus Walleij, konkers, Tony Lindgren, linux-kernel, linux-tegra,
	olof, linux-arm-kernel

On Sat, Feb 5, 2011 at 4:41 AM, Santosh Shilimkar
<santosh.shilimkar@ti.com> wrote:
>> -----Original Message-----
>> From: Russell King - ARM Linux [mailto:linux@arm.linux.org.uk]
>> Sent: Saturday, February 05, 2011 3:18 PM
>> To: Santosh Shilimkar
>> Cc: Colin Cross; Will Deacon; Catalin Marinas; Linus Walleij;
>> konkers@android.com; Tony Lindgren; linux-kernel@vger.kernel.org;
>> linux-tegra@vger.kernel.org; olof@lixom.net; linux-arm-
>> kernel@lists.infradead.org
>> Subject: Re: [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support
>> forre-enabling l2x0
>>
>> On Sat, Feb 05, 2011 at 01:21:24PM +0530, Santosh Shilimkar wrote:
>> > GIC save/restore on OMAP follows different strategy. There is a
>> > Predefined layout to save content and restore is done atomically
>> > by boot ROM code.
>> > L2 cache also same case. Only AUXCTRL needs to be programmed on
>> > wakeup from low power mode and that too with secure call. Rest
>> > of the registers are managed by boot ROM code.
>> >
>> > TWD is already managed through framework. Othe CPU low power
>> > sequence is very small and OMAP has restrictions on the last
>> > core to go down and first to wakeup.
>> >
>> > So at least I don't see any use of common notifiers for GIC
>> > and L2 will help OMAP lower power code.
>>
>> What this means is that we're going to end up littering things like
>> GIC
>> and other stuff with lots of individual SoC specific code to save
>> state
>> into individual SoC specific structures.  This is not sane, and
>> we're
>> not going to corrupt generic code with SoC specific code.
>
> Fully agree and hence flagged it early.
>
> Regards,
> Santosh
>

Would putting dummy values in the areas the boot ROM uses and then
letting the common GIC code restore over them cause any problems?

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support forre-enabling l2x0
@ 2011-02-05 16:36                                 ` Colin Cross
  0 siblings, 0 replies; 137+ messages in thread
From: Colin Cross @ 2011-02-05 16:36 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, Feb 5, 2011 at 4:41 AM, Santosh Shilimkar
<santosh.shilimkar@ti.com> wrote:
>> -----Original Message-----
>> From: Russell King - ARM Linux [mailto:linux at arm.linux.org.uk]
>> Sent: Saturday, February 05, 2011 3:18 PM
>> To: Santosh Shilimkar
>> Cc: Colin Cross; Will Deacon; Catalin Marinas; Linus Walleij;
>> konkers at android.com; Tony Lindgren; linux-kernel at vger.kernel.org;
>> linux-tegra at vger.kernel.org; olof at lixom.net; linux-arm-
>> kernel at lists.infradead.org
>> Subject: Re: [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support
>> forre-enabling l2x0
>>
>> On Sat, Feb 05, 2011 at 01:21:24PM +0530, Santosh Shilimkar wrote:
>> > GIC save/restore on OMAP follows different strategy. There is a
>> > Predefined layout to save content and restore is done atomically
>> > by boot ROM code.
>> > L2 cache also same case. Only AUXCTRL needs to be programmed on
>> > wakeup from low power mode and that too with secure call. Rest
>> > of the registers are managed by boot ROM code.
>> >
>> > TWD is already managed through framework. Othe CPU low power
>> > sequence is very small and OMAP has restrictions on the last
>> > core to go down and first to wakeup.
>> >
>> > So at least I don't see any use of common notifiers for GIC
>> > and L2 will help OMAP lower power code.
>>
>> What this means is that we're going to end up littering things like
>> GIC
>> and other stuff with lots of individual SoC specific code to save
>> state
>> into individual SoC specific structures. ?This is not sane, and
>> we're
>> not going to corrupt generic code with SoC specific code.
>
> Fully agree and hence flagged it early.
>
> Regards,
> Santosh
>

Would putting dummy values in the areas the boot ROM uses and then
letting the common GIC code restore over them cause any problems?

^ permalink raw reply	[flat|nested] 137+ messages in thread

* RE: [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support forre-enabling l2x0
  2011-02-05 16:36                                 ` Colin Cross
  (?)
@ 2011-02-07  6:13                                     ` Santosh Shilimkar
  -1 siblings, 0 replies; 137+ messages in thread
From: Santosh Shilimkar @ 2011-02-07  6:13 UTC (permalink / raw)
  To: Colin Cross
  Cc: Russell King - ARM Linux, Will Deacon, Catalin Marinas,
	Linus Walleij, konkers-z5hGa2qSFaRBDgjK7y7TUQ, Tony Lindgren,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, olof-nZhT3qVonbNeoWH0uzbU5w,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

> -----Original Message-----
> From: ccross-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org [mailto:ccross-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org] On Behalf Of
> Colin Cross
> Sent: Saturday, February 05, 2011 10:06 PM
> To: Santosh Shilimkar
> Cc: Russell King - ARM Linux; Will Deacon; Catalin Marinas; Linus
> Walleij; konkers-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org; Tony Lindgren; linux-
> kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; linux-tegra-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; olof-nZhT3qVonbNeoWH0uzbU5w@public.gmane.org;
> linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
> Subject: Re: [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support
> forre-enabling l2x0
>
> On Sat, Feb 5, 2011 at 4:41 AM, Santosh Shilimkar
> <santosh.shilimkar-l0cyMroinI0@public.gmane.org> wrote:
[....]

> >> On Sat, Feb 05, 2011 at 01:21:24PM +0530, Santosh Shilimkar
> wrote:
> >> > GIC save/restore on OMAP follows different strategy. There is a
> >> > Predefined layout to save content and restore is done
> atomically
> >> > by boot ROM code.
> >> > L2 cache also same case. Only AUXCTRL needs to be programmed on
> >> > wakeup from low power mode and that too with secure call. Rest
> >> > of the registers are managed by boot ROM code.
> >> >
> >> > TWD is already managed through framework. Othe CPU low power
> >> > sequence is very small and OMAP has restrictions on the last
> >> > core to go down and first to wakeup.
> >> >
> >> > So at least I don't see any use of common notifiers for GIC
> >> > and L2 will help OMAP lower power code.
> >>
> >> What this means is that we're going to end up littering things
> like
> >> GIC
> >> and other stuff with lots of individual SoC specific code to save
> >> state
> >> into individual SoC specific structures.  This is not sane, and
> >> we're
> >> not going to corrupt generic code with SoC specific code.
> >
> > Fully agree and hence flagged it early.
> >
[....]

>
> Would putting dummy values in the areas the boot ROM uses and then
> letting the common GIC code restore over them cause any problems?

Ya there are few issue. GIC and GIC OMAP extension are managed
together by BOOT ROM code. It's far optimal save and restore. Only
needed registers from OMAP point of view are saved/restored.
And for such reasons I would not like to use dummy stuff.

Regards
Santosh
--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 137+ messages in thread

* RE: [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support forre-enabling l2x0
@ 2011-02-07  6:13                                     ` Santosh Shilimkar
  0 siblings, 0 replies; 137+ messages in thread
From: Santosh Shilimkar @ 2011-02-07  6:13 UTC (permalink / raw)
  To: Colin Cross
  Cc: Russell King - ARM Linux, Will Deacon, Catalin Marinas,
	Linus Walleij, konkers, Tony Lindgren, linux-kernel, linux-tegra,
	olof, linux-arm-kernel

> -----Original Message-----
> From: ccross@google.com [mailto:ccross@google.com] On Behalf Of
> Colin Cross
> Sent: Saturday, February 05, 2011 10:06 PM
> To: Santosh Shilimkar
> Cc: Russell King - ARM Linux; Will Deacon; Catalin Marinas; Linus
> Walleij; konkers@android.com; Tony Lindgren; linux-
> kernel@vger.kernel.org; linux-tegra@vger.kernel.org; olof@lixom.net;
> linux-arm-kernel@lists.infradead.org
> Subject: Re: [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support
> forre-enabling l2x0
>
> On Sat, Feb 5, 2011 at 4:41 AM, Santosh Shilimkar
> <santosh.shilimkar@ti.com> wrote:
[....]

> >> On Sat, Feb 05, 2011 at 01:21:24PM +0530, Santosh Shilimkar
> wrote:
> >> > GIC save/restore on OMAP follows different strategy. There is a
> >> > Predefined layout to save content and restore is done
> atomically
> >> > by boot ROM code.
> >> > L2 cache also same case. Only AUXCTRL needs to be programmed on
> >> > wakeup from low power mode and that too with secure call. Rest
> >> > of the registers are managed by boot ROM code.
> >> >
> >> > TWD is already managed through framework. Othe CPU low power
> >> > sequence is very small and OMAP has restrictions on the last
> >> > core to go down and first to wakeup.
> >> >
> >> > So at least I don't see any use of common notifiers for GIC
> >> > and L2 will help OMAP lower power code.
> >>
> >> What this means is that we're going to end up littering things
> like
> >> GIC
> >> and other stuff with lots of individual SoC specific code to save
> >> state
> >> into individual SoC specific structures.  This is not sane, and
> >> we're
> >> not going to corrupt generic code with SoC specific code.
> >
> > Fully agree and hence flagged it early.
> >
[....]

>
> Would putting dummy values in the areas the boot ROM uses and then
> letting the common GIC code restore over them cause any problems?

Ya there are few issue. GIC and GIC OMAP extension are managed
together by BOOT ROM code. It's far optimal save and restore. Only
needed registers from OMAP point of view are saved/restored.
And for such reasons I would not like to use dummy stuff.

Regards
Santosh

^ permalink raw reply	[flat|nested] 137+ messages in thread

* [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support forre-enabling l2x0
@ 2011-02-07  6:13                                     ` Santosh Shilimkar
  0 siblings, 0 replies; 137+ messages in thread
From: Santosh Shilimkar @ 2011-02-07  6:13 UTC (permalink / raw)
  To: linux-arm-kernel

> -----Original Message-----
> From: ccross at google.com [mailto:ccross at google.com] On Behalf Of
> Colin Cross
> Sent: Saturday, February 05, 2011 10:06 PM
> To: Santosh Shilimkar
> Cc: Russell King - ARM Linux; Will Deacon; Catalin Marinas; Linus
> Walleij; konkers at android.com; Tony Lindgren; linux-
> kernel at vger.kernel.org; linux-tegra at vger.kernel.org; olof at lixom.net;
> linux-arm-kernel at lists.infradead.org
> Subject: Re: [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support
> forre-enabling l2x0
>
> On Sat, Feb 5, 2011 at 4:41 AM, Santosh Shilimkar
> <santosh.shilimkar@ti.com> wrote:
[....]

> >> On Sat, Feb 05, 2011 at 01:21:24PM +0530, Santosh Shilimkar
> wrote:
> >> > GIC save/restore on OMAP follows different strategy. There is a
> >> > Predefined layout to save content and restore is done
> atomically
> >> > by boot ROM code.
> >> > L2 cache also same case. Only AUXCTRL needs to be programmed on
> >> > wakeup from low power mode and that too with secure call. Rest
> >> > of the registers are managed by boot ROM code.
> >> >
> >> > TWD is already managed through framework. Othe CPU low power
> >> > sequence is very small and OMAP has restrictions on the last
> >> > core to go down and first to wakeup.
> >> >
> >> > So at least I don't see any use of common notifiers for GIC
> >> > and L2 will help OMAP lower power code.
> >>
> >> What this means is that we're going to end up littering things
> like
> >> GIC
> >> and other stuff with lots of individual SoC specific code to save
> >> state
> >> into individual SoC specific structures. ?This is not sane, and
> >> we're
> >> not going to corrupt generic code with SoC specific code.
> >
> > Fully agree and hence flagged it early.
> >
[....]

>
> Would putting dummy values in the areas the boot ROM uses and then
> letting the common GIC code restore over them cause any problems?

Ya there are few issue. GIC and GIC OMAP extension are managed
together by BOOT ROM code. It's far optimal save and restore. Only
needed registers from OMAP point of view are saved/restored.
And for such reasons I would not like to use dummy stuff.

Regards
Santosh

^ permalink raw reply	[flat|nested] 137+ messages in thread

end of thread, other threads:[~2011-02-07  6:13 UTC | newest]

Thread overview: 137+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-01-24  2:01 [PATCH v2 00/28] Updates for Tegra support in 2.6.39 Colin Cross
2011-01-24  2:01 ` [PATCH v2 01/28] ARM: tegra: irq: Rename gic pointers to avoid conflicts Colin Cross
2011-01-24  2:01   ` Colin Cross
2011-01-26  7:10   ` Colin Cross
2011-01-26  7:10     ` Colin Cross
2011-01-24  2:01 ` [PATCH v2 02/28] ARM: gic: Add functions to save and restore gic state Colin Cross
2011-01-24  2:01   ` Colin Cross
2011-02-01 13:03   ` Russell King - ARM Linux
2011-02-01 13:03     ` Russell King - ARM Linux
2011-01-24  2:01 ` [PATCH v2 03/28] ARM: gic: Export irq chip functions Colin Cross
2011-01-24  2:01   ` Colin Cross
2011-01-24  6:39   ` Santosh Shilimkar
2011-01-24  6:39     ` Santosh Shilimkar
2011-01-24  2:01 ` [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support for re-enabling l2x0 Colin Cross
2011-01-24  2:01   ` Colin Cross
2011-01-25 15:14   ` Catalin Marinas
2011-01-25 15:14     ` Catalin Marinas
2011-01-25 15:41     ` Russell King - ARM Linux
2011-01-25 15:41       ` Russell King - ARM Linux
2011-01-25 18:14       ` Catalin Marinas
2011-01-25 18:14         ` Catalin Marinas
2011-01-25 18:32         ` Santosh Shilimkar
2011-01-25 18:32           ` Santosh Shilimkar
2011-01-25 18:39           ` Will Deacon
     [not found]           ` <-8932138696981683633@unknownmsgid>
2011-02-04 23:32             ` Colin Cross
2011-02-04 23:32               ` Colin Cross
2011-02-04 23:32               ` Colin Cross
2011-02-04 23:43               ` Russell King - ARM Linux
2011-02-04 23:43                 ` Russell King - ARM Linux
     [not found]                 ` <20110204234331.GF8732-l+eeeJia6m9vn6HldHNs0ANdhmdF6hFW@public.gmane.org>
2011-02-05  1:44                   ` Colin Cross
2011-02-05  1:44                     ` Colin Cross
2011-02-05  1:44                     ` Colin Cross
     [not found]                     ` <AANLkTi=fHnivHXHnYrQvdP6JWbEA3t1X3DuBxj5gN3H0-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2011-02-05  7:51                       ` Santosh Shilimkar
2011-02-05  7:51                         ` Santosh Shilimkar
2011-02-05  7:51                         ` Santosh Shilimkar
     [not found]                         ` <1bebe4b5c8590059b70a146d5486fa6a-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2011-02-05  9:47                           ` Russell King - ARM Linux
2011-02-05  9:47                             ` Russell King - ARM Linux
2011-02-05  9:47                             ` Russell King - ARM Linux
2011-02-05 10:41                             ` [PATCH v2 04/28] ARM: mm: cache-l2x0: Add support forre-enabling l2x0 Santosh Shilimkar
2011-02-05 10:41                               ` Santosh Shilimkar
2011-02-05 16:36                               ` Colin Cross
2011-02-05 16:36                                 ` Colin Cross
     [not found]                                 ` <AANLkTik_r4k_5o+F47vRbGPcWLwfgHgWqhym49XfhBZ9-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2011-02-07  6:13                                   ` Santosh Shilimkar
2011-02-07  6:13                                     ` Santosh Shilimkar
2011-02-07  6:13                                     ` Santosh Shilimkar
2011-01-24  2:01 ` [PATCH v2 05/28] ARM: tegra: Centralize macros to define debug uart base Colin Cross
2011-01-24  2:01   ` Colin Cross
2011-01-24  2:01 ` [PATCH v2 06/28] ARM: tegra: Add api to control internal powergating Colin Cross
2011-01-24  2:01   ` Colin Cross
2011-01-24  2:01 ` [PATCH v2 07/28] ARM: tegra: irqs: Update irq list Colin Cross
2011-01-24  2:01   ` Colin Cross
2011-01-24  2:01 ` [PATCH v2 08/28] ARM: tegra: Add prototypes for subsystem suspend functions Colin Cross
2011-01-24  2:01   ` Colin Cross
2011-01-24  2:01 ` [PATCH v2 09/28] ARM: tegra: clock: Suspend fixes, and add new clocks Colin Cross
2011-01-24  2:01   ` Colin Cross
2011-01-24  2:01 ` [PATCH v2 10/28] ARM: tegra: pinmux: Add missing drive pingroups and fix suspend Colin Cross
2011-01-24  2:01   ` Colin Cross
2011-01-24  2:01 ` [PATCH v2 11/28] ARM: tegra: timer: Add idle and suspend support to timers Colin Cross
2011-01-24  2:01   ` Colin Cross
2011-01-24  2:01 ` [PATCH v2 12/28] ARM: tegra: irq: Add support for suspend wake sources Colin Cross
2011-01-24  2:01   ` Colin Cross
2011-01-24  2:01 ` [PATCH v2 13/28] ARM: tegra: Add suspend and hotplug support Colin Cross
2011-01-24  2:01   ` Colin Cross
2011-01-24  7:31   ` Colin Cross
2011-01-24  7:31     ` Colin Cross
2011-01-24  9:07   ` Russell King - ARM Linux
2011-01-24  9:07     ` Russell King - ARM Linux
2011-01-24  9:26     ` Colin Cross
2011-01-24  9:26       ` Colin Cross
2011-01-24 10:15       ` Russell King - ARM Linux
2011-01-24 10:15         ` Russell King - ARM Linux
2011-01-24  2:01 ` [PATCH v2 14/28] ARM: tegra: irq: Add set_wake and set_type support for suspend Colin Cross
2011-01-24  2:01   ` Colin Cross
2011-01-24  2:01 ` [PATCH v2 15/28] ARM: tegra: irq: Add debugfs file to show wake irqs Colin Cross
2011-01-24  2:01   ` Colin Cross
2011-01-27 19:45   ` Stephen Boyd
2011-01-27 19:45     ` Stephen Boyd
2011-01-24  2:01 ` [PATCH v2 16/28] ARM: tegra: irq: Implement retrigger Colin Cross
2011-01-24  2:01   ` Colin Cross
2011-01-24  2:01 ` [PATCH v2 17/28] ARM: tegra: gpio: Add support for waking from suspend Colin Cross
2011-01-24  2:01   ` Colin Cross
2011-01-24  2:01 ` [PATCH v2 18/28] ARM: tegra: add CPU_IDLE driver Colin Cross
2011-01-24  2:01   ` Colin Cross
2011-01-27 20:38   ` Stephen Boyd
2011-01-27 20:38     ` Stephen Boyd
2011-01-27 21:51     ` Colin Cross
2011-01-27 21:51       ` Colin Cross
2011-01-27 22:07       ` Colin Cross
2011-01-27 22:07         ` Colin Cross
2011-01-24  2:01 ` [PATCH v2 19/28] ARM: tegra: iomap: Add missing devices, fix use of SZ_8, SZ_64 Colin Cross
2011-01-24  2:01   ` Colin Cross
2011-01-24  2:01 ` [PATCH v2 20/28] ARM: tegra: cpufreq: Disable cpufreq during suspend Colin Cross
2011-01-24  2:01   ` Colin Cross
2011-01-24 14:41   ` Mark Brown
2011-01-24 14:41     ` Mark Brown
2011-01-24 18:50     ` Colin Cross
2011-01-24 18:50       ` Colin Cross
2011-01-24 19:35       ` Mark Brown
2011-01-24 19:35         ` Mark Brown
2011-01-24 19:52         ` Colin Cross
2011-01-24 19:52           ` Colin Cross
2011-01-24 20:26           ` Mark Brown
2011-01-24 20:26             ` Mark Brown
2011-01-24 20:52             ` Colin Cross
2011-01-24 20:52               ` Colin Cross
2011-01-24 21:08               ` Mark Brown
2011-01-24 21:08                 ` Mark Brown
2011-01-24 21:24                 ` Colin Cross
2011-01-24 21:24                   ` Colin Cross
2011-01-25  4:26         ` Kyungmin Park
2011-01-25  4:26           ` Kyungmin Park
2011-01-24  2:01 ` [PATCH v2 21/28] ARM: tegra: Allow overriding arch_reset Colin Cross
2011-01-24  2:01   ` Colin Cross
2011-01-24  2:01 ` [PATCH v2 22/28] ARM: tegra: dma: Fix critical data corruption bugs Colin Cross
2011-01-24  2:01   ` Colin Cross
2011-01-24  2:01 ` [PATCH v2 23/28] ARM: tegra: add tegra_defconfig Colin Cross
2011-01-24  2:01   ` Colin Cross
2011-01-24  2:01 ` [PATCH v2 24/28] ARM: tegra: Use writel_relaxed in tegra_init_cache Colin Cross
2011-01-24  2:01   ` Colin Cross
2011-01-24  2:01 ` [PATCH v2 25/28] ARM: tegra: suspend: Save protected aperture across LP0 Colin Cross
2011-01-24  2:01   ` Colin Cross
2011-01-24  2:01 ` [PATCH v2 26/28] ARM: tegra: suspend: protect suspend functions with CONFIG_PM Colin Cross
2011-01-24  2:01   ` Colin Cross
2011-01-24  2:01 ` [PATCH v2 27/28] ARM: tegra: enable emc clock updates after lp0 Colin Cross
2011-01-24  2:01   ` Colin Cross
2011-01-24  2:01 ` [PATCH v2 28/28] ARM: tegra: clock: Add forward reference to struct clk Colin Cross
2011-01-24  2:01   ` Colin Cross
2011-01-24 10:26   ` Russell King - ARM Linux
2011-01-24 10:26     ` Russell King - ARM Linux
2011-01-25  1:23     ` Colin Cross
2011-01-25  1:23       ` Colin Cross
2011-01-26  3:19   ` Grant Likely
2011-01-26  3:19     ` Grant Likely
2011-01-26  3:21     ` Colin Cross
2011-01-26  3:21       ` Colin Cross
2011-01-26  4:16       ` Grant Likely
2011-01-26  4:16         ` Grant Likely

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.