* [PATCH 00/21] Updates for Tegra support in 2.6.38
@ 2010-12-05 23:08 Colin Cross
2010-12-05 23:08 ` [PATCH 01/21] ARM: tegra: irq: Rename gic pointers to avoid conflicts Colin Cross
` (20 more replies)
0 siblings, 21 replies; 27+ messages in thread
From: Colin Cross @ 2010-12-05 23:08 UTC (permalink / raw)
To: linux-arm-kernel
This patch set adds support for suspend, idle, and cpufreq
for the Tegra subsystem.
Patches 2 and 3 make changes to the GIC driver that will
likely conflict with Russell's recent changes to GIC
initialization. Patch 2 adds functions to save and
restore the GIC state during suspend and resume, 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.
arch/arm/common/gic.c | 146 ++++-
arch/arm/include/asm/hardware/gic.h | 12 +
arch/arm/mach-tegra/Makefile | 12 +-
arch/arm/mach-tegra/common.c | 6 +-
arch/arm/mach-tegra/cortex-a9.S | 710 +++++++++++++++++++
arch/arm/mach-tegra/cpu-tegra.c | 74 ++-
arch/arm/mach-tegra/cpuidle.c | 711 +++++++++++++++++++
arch/arm/mach-tegra/dma.c | 198 +++---
arch/arm/mach-tegra/gpio.c | 15 +-
arch/arm/mach-tegra/headsmp-t2.S | 218 ++++++
arch/arm/mach-tegra/headsmp.S | 61 --
arch/arm/mach-tegra/hotplug.c | 140 ----
arch/arm/mach-tegra/include/mach/debug-macro.S | 31 +-
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 | 18 +-
arch/arm/mach-tegra/include/mach/uncompress.h | 18 +-
arch/arm/mach-tegra/irq.c | 326 +++++++---
arch/arm/mach-tegra/legacy_irq.c | 109 +++-
arch/arm/mach-tegra/pinmux-t2-tables.c | 26 +-
arch/arm/mach-tegra/platsmp.c | 130 +++-
arch/arm/mach-tegra/power-macros.S | 57 ++
arch/arm/mach-tegra/power.h | 64 ++
arch/arm/mach-tegra/powergate.c | 211 ++++++
arch/arm/mach-tegra/suspend-t2.c | 413 +++++++++++
arch/arm/mach-tegra/suspend.c | 870 ++++++++++++++++++++++++
arch/arm/mach-tegra/tegra2_clocks.c | 264 +++++++-
arch/arm/mach-tegra/tegra2_save.S | 413 +++++++++++
arch/arm/mach-tegra/timer.c | 114 +++-
33 files changed, 5075 insertions(+), 512 deletions(-)
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 01/21] ARM: tegra: irq: Rename gic pointers to avoid conflicts
2010-12-05 23:08 [PATCH 00/21] Updates for Tegra support in 2.6.38 Colin Cross
@ 2010-12-05 23:08 ` Colin Cross
2010-12-05 23:08 ` [PATCH 02/21] ARM: gic: Add functions to save and restore gic state Colin Cross
` (19 subsequent siblings)
20 siblings, 0 replies; 27+ messages in thread
From: Colin Cross @ 2010-12-05 23:08 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 | 12 ++++++------
1 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/arch/arm/mach-tegra/irq.c b/arch/arm/mach-tegra/irq.c
index 50a8dfb..37b4f97 100644
--- a/arch/arm/mach-tegra/irq.c
+++ b/arch/arm/mach-tegra/irq.c
@@ -46,8 +46,8 @@
#define ICTLR_COP_IER_CLR 0x38
#define ICTLR_COP_IEP_CLASS 0x3c
-static void (*gic_mask_irq)(unsigned int irq);
-static void (*gic_unmask_irq)(unsigned int irq);
+static void (*tegra_gic_mask_irq)(unsigned int irq);
+static void (*tegra_gic_unmask_irq)(unsigned int irq);
#define irq_to_ictlr(irq) (((irq)-32) >> 5)
static void __iomem *tegra_ictlr_base = IO_ADDRESS(TEGRA_PRIMARY_ICTLR_BASE);
@@ -56,14 +56,14 @@ static void __iomem *tegra_ictlr_base = IO_ADDRESS(TEGRA_PRIMARY_ICTLR_BASE);
static void tegra_mask(unsigned int irq)
{
void __iomem *addr = ictlr_to_virt(irq_to_ictlr(irq));
- gic_mask_irq(irq);
+ tegra_gic_mask_irq(irq);
writel(1<<(irq&31), addr+ICTLR_CPU_IER_CLR);
}
static void tegra_unmask(unsigned int irq)
{
void __iomem *addr = ictlr_to_virt(irq_to_ictlr(irq));
- gic_unmask_irq(irq);
+ tegra_gic_unmask_irq(irq);
writel(1<<(irq&31), addr+ICTLR_CPU_IER_SET);
}
@@ -98,8 +98,8 @@ void __init tegra_init_irq(void)
gic_cpu_init(0, IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100));
gic = get_irq_chip(29);
- gic_unmask_irq = gic->unmask;
- gic_mask_irq = gic->mask;
+ tegra_gic_unmask_irq = gic->unmask;
+ tegra_gic_mask_irq = gic->mask;
tegra_irq.ack = gic->ack;
#ifdef CONFIG_SMP
tegra_irq.set_affinity = gic->set_affinity;
--
1.7.3.1
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 02/21] ARM: gic: Add functions to save and restore gic state
2010-12-05 23:08 [PATCH 00/21] Updates for Tegra support in 2.6.38 Colin Cross
2010-12-05 23:08 ` [PATCH 01/21] ARM: tegra: irq: Rename gic pointers to avoid conflicts Colin Cross
@ 2010-12-05 23:08 ` Colin Cross
2010-12-05 23:30 ` Russell King - ARM Linux
2010-12-05 23:08 ` [PATCH 03/21] ARM: gic: Export irq chip functions Colin Cross
` (18 subsequent siblings)
20 siblings, 1 reply; 27+ messages in thread
From: Colin Cross @ 2010-12-05 23:08 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>
---
arch/arm/common/gic.c | 136 ++++++++++++++++++++++++++++++++--
arch/arm/include/asm/hardware/gic.h | 4 +
2 files changed, 132 insertions(+), 8 deletions(-)
diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c
index 772f95f..9deb34f 100644
--- a/arch/arm/common/gic.c
+++ b/arch/arm/common/gic.c
@@ -39,6 +39,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 max_irq;
};
#ifndef MAX_GIC_NR
@@ -207,21 +214,15 @@ void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq)
set_irq_chained_handler(irq, gic_handle_cascade_irq);
}
-void __init gic_dist_init(unsigned int gic_nr, void __iomem *base,
- unsigned int irq_start)
+static unsigned int _gic_dist_init(unsigned int gic_nr)
{
unsigned int max_irq, i;
+ void __iomem *base = gic_data[gic_nr].dist_base;
u32 cpumask = 1 << smp_processor_id();
- if (gic_nr >= MAX_GIC_NR)
- BUG();
-
cpumask |= cpumask << 8;
cpumask |= cpumask << 16;
- gic_data[gic_nr].dist_base = base;
- gic_data[gic_nr].irq_offset = (irq_start - 1) & ~31;
-
writel(0, base + GIC_DIST_CTRL);
/*
@@ -263,6 +264,109 @@ void __init gic_dist_init(unsigned int gic_nr, void __iomem *base,
for (i = 32; i < max_irq; i += 32)
writel(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i * 4 / 32);
+ return max_irq;
+}
+
+static void _gic_dist_exit(unsigned int gic_nr)
+{
+ writel(0, gic_data[gic_nr].dist_base + GIC_DIST_CTRL);
+}
+
+#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 max_irq = gic_data[gic_nr].max_irq;
+ 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(max_irq, 16); i++)
+ gic_data[gic_nr].saved_conf[i] =
+ readl(dist_base + GIC_DIST_CONFIG + i * 4);
+
+ for (i = 0; i < DIV_ROUND_UP(max_irq, 4); i++)
+ gic_data[gic_nr].saved_pri[i] =
+ readl(dist_base + GIC_DIST_PRI + i * 4);
+
+ for (i = 0; i < DIV_ROUND_UP(max_irq, 4); i++)
+ gic_data[gic_nr].saved_target[i] =
+ readl(dist_base + GIC_DIST_TARGET + i * 4);
+
+ for (i = 0; i < DIV_ROUND_UP(max_irq, 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 max_irq;
+ unsigned int i;
+ void __iomem *dist_base;
+ void __iomem *cpu_base;
+
+ if (gic_nr >= MAX_GIC_NR)
+ BUG();
+
+ _gic_dist_init(gic_nr);
+
+ max_irq = gic_data[gic_nr].max_irq;
+ dist_base = gic_data[gic_nr].dist_base;
+ cpu_base = gic_data[gic_nr].cpu_base;
+
+ for (i = 0; i < DIV_ROUND_UP(max_irq, 16); i++)
+ writel(gic_data[gic_nr].saved_conf[i],
+ dist_base + GIC_DIST_CONFIG + i * 4);
+
+ for (i = 0; i < DIV_ROUND_UP(max_irq, 4); i++)
+ writel(gic_data[gic_nr].saved_pri[i],
+ dist_base + GIC_DIST_PRI + i * 4);
+
+ for (i = 0; i < DIV_ROUND_UP(max_irq, 4); i++)
+ writel(gic_data[gic_nr].saved_target[i],
+ dist_base + GIC_DIST_TARGET + i * 4);
+
+ for (i = 0; i < DIV_ROUND_UP(max_irq, 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
+
+void __init gic_dist_init(unsigned int gic_nr, void __iomem *base,
+ unsigned int irq_start)
+{
+ unsigned int max_irq;
+ unsigned int i;
+
+ if (gic_nr >= MAX_GIC_NR)
+ BUG();
+
+ gic_data[gic_nr].dist_base = base;
+ gic_data[gic_nr].irq_offset = (irq_start - 1) & ~31;
+
+ max_irq = _gic_dist_init(gic_nr);
+ gic_data[gic_nr].max_irq = max_irq;
+
/*
* Setup the Linux IRQ subsystem.
*/
@@ -276,6 +380,14 @@ void __init gic_dist_init(unsigned int gic_nr, void __iomem *base,
writel(1, base + GIC_DIST_CTRL);
}
+void gic_dist_exit(unsigned int gic_nr)
+{
+ if (gic_nr >= MAX_GIC_NR)
+ BUG();
+
+ _gic_dist_exit(gic_nr);
+}
+
void __cpuinit gic_cpu_init(unsigned int gic_nr, void __iomem *base)
{
void __iomem *dist_base;
@@ -306,6 +418,14 @@ void __cpuinit gic_cpu_init(unsigned int gic_nr, void __iomem *base)
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);
+}
+
#ifdef CONFIG_SMP
void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
{
diff --git a/arch/arm/include/asm/hardware/gic.h b/arch/arm/include/asm/hardware/gic.h
index 7f34333..0a198b0 100644
--- a/arch/arm/include/asm/hardware/gic.h
+++ b/arch/arm/include/asm/hardware/gic.h
@@ -34,7 +34,11 @@
#ifndef __ASSEMBLY__
void gic_dist_init(unsigned int gic_nr, void __iomem *base, unsigned int irq_start);
+void gic_dist_save(unsigned int gic_nr);
+void gic_dist_restore(unsigned int gic_nr);
+void gic_dist_exit(unsigned int gic_nr);
void gic_cpu_init(unsigned int gic_nr, void __iomem *base);
+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);
#endif
--
1.7.3.1
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 03/21] ARM: gic: Export irq chip functions
2010-12-05 23:08 [PATCH 00/21] Updates for Tegra support in 2.6.38 Colin Cross
2010-12-05 23:08 ` [PATCH 01/21] ARM: tegra: irq: Rename gic pointers to avoid conflicts Colin Cross
2010-12-05 23:08 ` [PATCH 02/21] ARM: gic: Add functions to save and restore gic state Colin Cross
@ 2010-12-05 23:08 ` Colin Cross
2010-12-05 23:08 ` [PATCH 04/21] ARM: tegra: Centralize macros to define debug uart base Colin Cross
` (17 subsequent siblings)
20 siblings, 0 replies; 27+ messages in thread
From: Colin Cross @ 2010-12-05 23:08 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>
---
arch/arm/common/gic.c | 10 +++++-----
arch/arm/include/asm/hardware/gic.h | 8 ++++++++
2 files changed, 13 insertions(+), 5 deletions(-)
diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c
index 9deb34f..5c11dab 100644
--- a/arch/arm/common/gic.c
+++ b/arch/arm/common/gic.c
@@ -75,7 +75,7 @@ static inline unsigned int gic_irq(unsigned int irq)
/*
* Routines to acknowledge, disable and enable interrupts
*/
-static void gic_ack_irq(unsigned int irq)
+void gic_ack_irq(unsigned int irq)
{
spin_lock(&irq_controller_lock);
@@ -83,7 +83,7 @@ static void gic_ack_irq(unsigned int irq)
spin_unlock(&irq_controller_lock);
}
-static void gic_mask_irq(unsigned int irq)
+void gic_mask_irq(unsigned int irq)
{
u32 mask = 1 << (irq % 32);
@@ -92,7 +92,7 @@ static void gic_mask_irq(unsigned int irq)
spin_unlock(&irq_controller_lock);
}
-static void gic_unmask_irq(unsigned int irq)
+void gic_unmask_irq(unsigned int irq)
{
u32 mask = 1 << (irq % 32);
@@ -101,7 +101,7 @@ static void gic_unmask_irq(unsigned int irq)
spin_unlock(&irq_controller_lock);
}
-static int gic_set_type(unsigned int irq, unsigned int type)
+int gic_set_type(unsigned int irq, unsigned int type)
{
void __iomem *base = gic_dist_base(irq);
unsigned int gicirq = gic_irq(irq);
@@ -147,7 +147,7 @@ static int gic_set_type(unsigned int irq, unsigned int type)
}
#ifdef CONFIG_SMP
-static int gic_set_cpu(unsigned int irq, const struct cpumask *mask_val)
+int gic_set_cpu(unsigned int irq, const struct cpumask *mask_val)
{
void __iomem *reg = gic_dist_base(irq) + GIC_DIST_TARGET + (gic_irq(irq) & ~3);
unsigned int shift = (irq % 4) * 8;
diff --git a/arch/arm/include/asm/hardware/gic.h b/arch/arm/include/asm/hardware/gic.h
index 0a198b0..5fd0ff2 100644
--- a/arch/arm/include/asm/hardware/gic.h
+++ b/arch/arm/include/asm/hardware/gic.h
@@ -41,6 +41,14 @@ void gic_cpu_init(unsigned int gic_nr, void __iomem *base);
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_ack_irq(unsigned int irq);
+void gic_mask_irq(unsigned int irq);
+void gic_unmask_irq(unsigned int irq);
+int gic_set_type(unsigned int irq, unsigned int type);
+#ifdef CONFIG_SMP
+int gic_set_cpu(unsigned int irq, const struct cpumask *mask_val);
+#endif
#endif
#endif
--
1.7.3.1
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 04/21] ARM: tegra: Centralize macros to define debug uart base
2010-12-05 23:08 [PATCH 00/21] Updates for Tegra support in 2.6.38 Colin Cross
` (2 preceding siblings ...)
2010-12-05 23:08 ` [PATCH 03/21] ARM: gic: Export irq chip functions Colin Cross
@ 2010-12-05 23:08 ` Colin Cross
2010-12-05 23:08 ` [PATCH 05/21] ARM: tegra: Add api to control internal powergating Colin Cross
` (16 subsequent siblings)
20 siblings, 0 replies; 27+ messages in thread
From: Colin Cross @ 2010-12-05 23:08 UTC (permalink / raw)
To: linux-arm-kernel
From: Colin Cross <ccross@google.com>
Signed-off-by: Colin Cross <ccross@google.com>
---
arch/arm/mach-tegra/include/mach/debug-macro.S | 31 ++++++-----------------
arch/arm/mach-tegra/include/mach/iomap.h | 14 ++++++++++
arch/arm/mach-tegra/include/mach/uncompress.h | 18 +------------
3 files changed, 24 insertions(+), 39 deletions(-)
diff --git a/arch/arm/mach-tegra/include/mach/debug-macro.S b/arch/arm/mach-tegra/include/mach/debug-macro.S
index 8ea3bff..2e3b8e3 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
- ldreq \rp, =IO_APB_PHYS @ physical
- ldrne \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
+ .macro addruart, rp, rv
+ ldr \rp, =IO_APB_PHYS @ physical
+ ldr \rv, =IO_APB_VIRT @ virtual
+ orr \rp, \rp, #(TEGRA_DEBUG_UART_BASE & 0xFF00)
+ orr \rp, \rp, #(TEGRA_DEBUG_UART_BASE & 0x00FF)
+ orr \rv, \rv, #(TEGRA_DEBUG_UART_BASE & 0xFF00)
+ orr \rv, \rv, #(TEGRA_DEBUG_UART_BASE & 0x00FF)
.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] 27+ messages in thread
* [PATCH 05/21] ARM: tegra: Add api to control internal powergating
2010-12-05 23:08 [PATCH 00/21] Updates for Tegra support in 2.6.38 Colin Cross
` (3 preceding siblings ...)
2010-12-05 23:08 ` [PATCH 04/21] ARM: tegra: Centralize macros to define debug uart base Colin Cross
@ 2010-12-05 23:08 ` Colin Cross
2010-12-05 23:08 ` [PATCH 06/21] ARM: tegra: irqs: Update irq list Colin Cross
` (15 subsequent siblings)
20 siblings, 0 replies; 27+ messages in thread
From: Colin Cross @ 2010-12-05 23:08 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 | 211 ++++++++++++++++++++++++++
3 files changed, 252 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..9fbe727
--- /dev/null
+++ b/arch/arm/mach-tegra/powergate.c
@@ -0,0 +1,211 @@
+/*
+ * 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;
+
+ 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 *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] 27+ messages in thread
* [PATCH 06/21] ARM: tegra: irqs: Update irq list
2010-12-05 23:08 [PATCH 00/21] Updates for Tegra support in 2.6.38 Colin Cross
` (4 preceding siblings ...)
2010-12-05 23:08 ` [PATCH 05/21] ARM: tegra: Add api to control internal powergating Colin Cross
@ 2010-12-05 23:08 ` Colin Cross
2010-12-05 23:08 ` [PATCH 07/21] ARM: tegra: Add prototypes for subsystem suspend functions Colin Cross
` (14 subsequent siblings)
20 siblings, 0 replies; 27+ messages in thread
From: Colin Cross @ 2010-12-05 23:08 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] 27+ messages in thread
* [PATCH 07/21] ARM: tegra: Add prototypes for subsystem suspend functions
2010-12-05 23:08 [PATCH 00/21] Updates for Tegra support in 2.6.38 Colin Cross
` (5 preceding siblings ...)
2010-12-05 23:08 ` [PATCH 06/21] ARM: tegra: irqs: Update irq list Colin Cross
@ 2010-12-05 23:08 ` Colin Cross
2010-12-05 23:08 ` [PATCH 08/21] ARM: tegra: clock: Suspend fixes, and add new clocks Colin Cross
` (13 subsequent siblings)
20 siblings, 0 replies; 27+ messages in thread
From: Colin Cross @ 2010-12-05 23:08 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 0775265..1bf92e5 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 37b4f97..2de626f 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 ae3b308..c6fa5e7 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -28,6 +28,7 @@
#include <asm/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 b49f2f5..ef2fc29 100644
--- a/arch/arm/mach-tegra/timer.c
+++ b/arch/arm/mach-tegra/timer.c
@@ -32,6 +32,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] 27+ messages in thread
* [PATCH 08/21] ARM: tegra: clock: Suspend fixes, and add new clocks
2010-12-05 23:08 [PATCH 00/21] Updates for Tegra support in 2.6.38 Colin Cross
` (6 preceding siblings ...)
2010-12-05 23:08 ` [PATCH 07/21] ARM: tegra: Add prototypes for subsystem suspend functions Colin Cross
@ 2010-12-05 23:08 ` Colin Cross
2010-12-05 23:08 ` [PATCH 09/21] ARM: tegra: pinmux: Add missing drive pingroups and fix suspend Colin Cross
` (12 subsequent siblings)
20 siblings, 0 replies; 27+ messages in thread
From: Colin Cross @ 2010-12-05 23:08 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 c6fa5e7..1cc8c58d 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -53,7 +53,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)
@@ -136,12 +136,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)
{
@@ -359,6 +376,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);
@@ -371,6 +391,7 @@ static int tegra2_cpu_clk_set_rate(struct clk *c, unsigned long rate)
return ret;
}
+out:
return 0;
}
@@ -430,6 +451,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)
{
@@ -930,6 +1032,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;
@@ -1008,6 +1111,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",
@@ -1228,10 +1362,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 },
};
@@ -1373,6 +1518,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 {
@@ -1487,7 +1650,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,
@@ -1513,6 +1676,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},
@@ -1627,7 +1798,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? */
@@ -1643,34 +1814,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),
@@ -1695,9 +1866,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) \
@@ -1733,7 +1910,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)
@@ -1776,14 +1956,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) {
@@ -1802,6 +2002,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)
@@ -1814,6 +2018,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] 27+ messages in thread
* [PATCH 09/21] ARM: tegra: pinmux: Add missing drive pingroups and fix suspend
2010-12-05 23:08 [PATCH 00/21] Updates for Tegra support in 2.6.38 Colin Cross
` (7 preceding siblings ...)
2010-12-05 23:08 ` [PATCH 08/21] ARM: tegra: clock: Suspend fixes, and add new clocks Colin Cross
@ 2010-12-05 23:08 ` Colin Cross
2010-12-05 23:08 ` [PATCH 10/21] ARM: tegra: timer: Add idle and suspend support to timers Colin Cross
` (11 subsequent siblings)
20 siblings, 0 replies; 27+ messages in thread
From: Colin Cross @ 2010-12-05 23:08 UTC (permalink / raw)
To: linux-arm-kernel
From: Colin Cross <ccross@google.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] 27+ messages in thread
* [PATCH 10/21] ARM: tegra: timer: Add idle and suspend support to timers
2010-12-05 23:08 [PATCH 00/21] Updates for Tegra support in 2.6.38 Colin Cross
` (8 preceding siblings ...)
2010-12-05 23:08 ` [PATCH 09/21] ARM: tegra: pinmux: Add missing drive pingroups and fix suspend Colin Cross
@ 2010-12-05 23:08 ` Colin Cross
2010-12-05 23:08 ` [PATCH 11/21] ARM: tegra: irq: Add support for suspend wake sources Colin Cross
` (10 subsequent siblings)
20 siblings, 0 replies; 27+ messages in thread
From: Colin Cross @ 2010-12-05 23:08 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 | 113 +++++++++++++++++++++++++++++++++++++++++-
1 files changed, 110 insertions(+), 3 deletions(-)
diff --git a/arch/arm/mach-tegra/timer.c b/arch/arm/mach-tegra/timer.c
index ef2fc29..b46650b 100644
--- a/arch/arm/mach-tegra/timer.c
+++ b/arch/arm/mach-tegra/timer.c
@@ -28,6 +28,7 @@
#include <linux/cnt32_to_63.h>
#include <asm/mach/time.h>
+#include <asm/mach/time.h>
#include <asm/localtimer.h>
#include <mach/iomap.h>
@@ -37,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
@@ -49,15 +54,17 @@
#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);
#define timer_writel(value, reg) \
__raw_writel(value, (u32)timer_reg_base + (reg))
#define timer_readl(reg) \
__raw_readl((u32)timer_reg_base + (reg))
+static u64 tegra_sched_clock_offset;
+static u64 tegra_sched_clock_resume_offset;
+
static int tegra_timer_set_next_event(unsigned long cycles,
struct clock_event_device *evt)
{
@@ -111,9 +118,61 @@ static struct clocksource tegra_clocksource = {
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
+/*
+ * 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 * 1000 + 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
+ */
+static struct timespec persistent_ts;
+static u64 persistent_ms, last_persistent_ms;
+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 * 1000000);
+ *ts = *tsp;
+}
+
unsigned long long sched_clock(void)
{
- return cnt32_to_63(timer_readl(TIMERUS_CNTR_1US)) * 1000;
+ return tegra_sched_clock_offset +
+ cnt32_to_63(timer_readl(TIMERUS_CNTR_1US)) * 1000;
+}
+
+static void tegra_sched_clock_suspend(void)
+{
+ tegra_sched_clock_resume_offset = sched_clock() -
+ tegra_rtc_read_ms() * 1000000;
+}
+
+static void tegra_sched_clock_resume(void)
+{
+ tegra_sched_clock_offset += tegra_sched_clock_resume_offset +
+ tegra_rtc_read_ms() * 1000000 -
+ sched_clock();
}
static irqreturn_t tegra_timer_interrupt(int irq, void *dev_id)
@@ -132,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();
@@ -169,6 +242,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);
@@ -184,3 +263,31 @@ 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;
+}
+
+static u32 usec_config;
+void tegra_timer_suspend(void)
+{
+ tegra_sched_clock_suspend();
+ usec_config = timer_readl(TIMERUS_USEC_CFG);
+}
+
+void tegra_timer_resume(void)
+{
+ timer_writel(usec_config, TIMERUS_USEC_CFG);
+ tegra_sched_clock_resume();
+}
--
1.7.3.1
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 11/21] ARM: tegra: irq: Add support for suspend wake sources
2010-12-05 23:08 [PATCH 00/21] Updates for Tegra support in 2.6.38 Colin Cross
` (9 preceding siblings ...)
2010-12-05 23:08 ` [PATCH 10/21] ARM: tegra: timer: Add idle and suspend support to timers Colin Cross
@ 2010-12-05 23:08 ` Colin Cross
2010-12-05 23:08 ` [PATCH 12/21] ARM: tegra: Add suspend and hotplug support Colin Cross
` (9 subsequent siblings)
20 siblings, 0 replies; 27+ messages in thread
From: Colin Cross @ 2010-12-05 23:08 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 | 184 ++++++++++---------------
arch/arm/mach-tegra/legacy_irq.c | 109 ++++++++++++++-
3 files changed, 184 insertions(+), 113 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 2de626f..8f47a9c 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)(unsigned int irq);
-static void (*tegra_gic_unmask_irq)(unsigned int irq);
-
-#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(unsigned int irq)
+int tegra_set_lp1_wake(int irq, int enable)
{
- void __iomem *addr = ictlr_to_virt(irq_to_ictlr(irq));
- tegra_gic_mask_irq(irq);
- writel(1<<(irq&31), addr+ICTLR_CPU_IER_CLR);
+ return tegra_legacy_irq_set_wake(irq, enable);
}
-static void tegra_unmask(unsigned int irq)
+void tegra_set_lp0_wake_pads(u32 wake_enb, u32 wake_level, u32 wake_any)
{
- void __iomem *addr = ictlr_to_virt(irq_to_ictlr(irq));
- tegra_gic_unmask_irq(irq);
- writel(1<<(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(unsigned int irq)
+{
+ gic_mask_irq(irq);
+ tegra_legacy_mask_irq(irq);
+}
-static int tegra_set_wake(unsigned int irq, unsigned int on)
+static void tegra_unmask(unsigned int irq)
{
- return 0;
+ gic_unmask_irq(irq);
+ tegra_legacy_unmask_irq(irq);
}
-#endif
static struct irq_chip tegra_irq = {
.name = "PPI",
+ .ack = gic_ack_irq,
.mask = tegra_mask,
.unmask = tegra_unmask,
-#ifdef CONFIG_PM
- .set_wake = tegra_set_wake,
+#ifdef CONFIG_SMP
+ .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_dist_init(0, IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE), 29);
gic_cpu_init(0, IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100));
- gic = get_irq_chip(29);
- tegra_gic_unmask_irq = gic->unmask;
- tegra_gic_mask_irq = gic->mask;
- tegra_irq.ack = gic->ack;
-#ifdef CONFIG_SMP
- tegra_irq.set_affinity = gic->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] 27+ messages in thread
* [PATCH 12/21] ARM: tegra: Add suspend and hotplug support
2010-12-05 23:08 [PATCH 00/21] Updates for Tegra support in 2.6.38 Colin Cross
` (10 preceding siblings ...)
2010-12-05 23:08 ` [PATCH 11/21] ARM: tegra: irq: Add support for suspend wake sources Colin Cross
@ 2010-12-05 23:08 ` Colin Cross
2010-12-06 0:01 ` Russell King - ARM Linux
2010-12-05 23:09 ` [PATCH 13/21] ARM: tegra: irq: Add set_wake and set_type support for suspend Colin Cross
` (8 subsequent siblings)
20 siblings, 1 reply; 27+ messages in thread
From: Colin Cross @ 2010-12-05 23:08 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>
---
arch/arm/mach-tegra/Makefile | 10 +-
arch/arm/mach-tegra/common.c | 3 +-
arch/arm/mach-tegra/cortex-a9.S | 710 +++++++++++++++++++++++
arch/arm/mach-tegra/headsmp-t2.S | 218 +++++++
arch/arm/mach-tegra/headsmp.S | 61 --
arch/arm/mach-tegra/hotplug.c | 140 -----
arch/arm/mach-tegra/include/mach/suspend.h | 42 ++
arch/arm/mach-tegra/platsmp.c | 130 ++++-
arch/arm/mach-tegra/power-macros.S | 57 ++
arch/arm/mach-tegra/power.h | 64 ++
arch/arm/mach-tegra/suspend-t2.c | 413 +++++++++++++
arch/arm/mach-tegra/suspend.c | 870 ++++++++++++++++++++++++++++
arch/arm/mach-tegra/tegra2_save.S | 413 +++++++++++++
13 files changed, 2910 insertions(+), 221 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..4b36385 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_SMP) += 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/common.c b/arch/arm/mach-tegra/common.c
index 7c91e2b..64ea415 100644
--- a/arch/arm/mach-tegra/common.c
+++ b/arch/arm/mach-tegra/common.c
@@ -30,6 +30,7 @@
#include "board.h"
#include "clock.h"
#include "fuse.h"
+#include "power.h"
static __initdata struct tegra_clk_init_table common_clk_init_table[] = {
/* name parent rate enabled */
@@ -45,7 +46,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;
diff --git a/arch/arm/mach-tegra/cortex-a9.S b/arch/arm/mach-tegra/cortex-a9.S
new file mode 100644
index 0000000..869ad89
--- /dev/null
+++ b/arch/arm/mach-tegra/cortex-a9.S
@@ -0,0 +1,710 @@
+/*
+ * 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}
+
+#ifdef CONFIG_VFPv3
+ orr r2, r3, #0xF00000
+ mcr p15, 0, r2, c1, c0, 2 @ enable access to FPU
+ VFPFMRX r2, FPEXC
+ str r2, [r8, #CTX_FPEXC]
+ mov r1, #0x40000000 @ enable access to FPU
+ VFPFMXR FPEXC, r1
+ VFPFMRX r1, FPSCR
+ str r1, [r8, #CTX_FPSCR]
+ isb
+ add r9, r8, #CTX_VFP_REGS
+
+ VFPFSTMIA r9, r12 @ save out (16 or 32)*8B of FPU registers
+ VFPFMXR FPEXC, r2
+ mrc p15, 0, r3, c1, c0, 2 @ restore original FPEXC/CPACR
+#endif
+ 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}
+
+ cps 0x1f @ SYS mode
+ add r9, r8, #CTX_SYS_SP
+ stmia r9, {sp,lr}
+
+ cps 0x17 @ Abort mode
+ mrs r12, spsr
+ add r9, r8, #CTX_ABT_SPSR
+ stmia r9, {r12,sp,lr}
+
+ cps 0x12 @ IRQ mode
+ mrs r12, spsr
+ add r9, r8, #CTX_IRQ_SPSR
+ stmia r9, {r12,sp,lr}
+
+ cps 0x1b @ Undefined mode
+ mrs r12, spsr
+ add r9, r8, #CTX_UND_SPSR
+ stmia r9, {r12,sp,lr}
+
+ 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
+
+ /* Save CP14 debug controller context */
+ add r9, r8, #CTX_CP14_REGS
+ mrc p14, 0, r0, c0, c1, 0 @ DSCR
+ mrc p14, 0, r1, c0, c6, 0 @ WFAR
+ mrc p14, 0, r2, c0, c7, 0 @ VCR
+ mrc p14, 0, r3, c7, c9, 6 @ CLAIM
+ stmia r9, {r0-r3}
+
+ add r9, r8, #CTS_CP14_BKPT_0
+ mrc p14, 0, r2, c0, c0, 4
+ mrc p14, 0, r3, c0, c0, 5
+ stmia r9!, {r2-r3} @ BRKPT_0
+ mrc p14, 0, r2, c0, c1, 4
+ mrc p14, 0, r3, c0, c1, 5
+ stmia r9!, {r2-r3} @ BRKPT_0
+ mrc p14, 0, r2, c0, c2, 4
+ mrc p14, 0, r3, c0, c2, 5
+ stmia r9!, {r2-r3} @ BRKPT_0
+ mrc p14, 0, r2, c0, c3, 4
+ mrc p14, 0, r3, c0, c3, 5
+ stmia r9!, {r2-r3} @ BRKPT_0
+ mrc p14, 0, r2, c0, c4, 4
+ mrc p14, 0, r3, c0, c4, 5
+ stmia r9!, {r2-r3} @ BRKPT_0
+ mrc p14, 0, r2, c0, c5, 4
+ mrc p14, 0, r3, c0, c5, 5
+ stmia r9!, {r2-r3} @ BRKPT_0
+
+ add r9, r8, #CTS_CP14_WPT_0
+ mrc p14, 0, r2, c0, c0, 6
+ mrc p14, 0, r3, c0, c0, 7
+ stmia r9!, {r2-r3} @ WPT_0
+ mrc p14, 0, r2, c0, c1, 6
+ mrc p14, 0, r3, c0, c1, 7
+ stmia r9!, {r2-r3} @ WPT_0
+ mrc p14, 0, r2, c0, c2, 6
+ mrc p14, 0, r3, c0, c2, 7
+ stmia r9!, {r2-r3} @ WPT_0
+ mrc p14, 0, r2, c0, c3, 6
+ mrc p14, 0, r3, c0, c3, 7
+ stmia r9!, {r2-r3} @ WPT_0
+
+#ifdef CONFIG_CACHE_L2X0
+ cpu_id r4
+ cmp r4, #0
+ bne __cortex_a9_save_clean_cache
+ mov32 r4, (TEGRA_ARM_PL310_BASE-IO_CPU_PHYS+IO_CPU_VIRT)
+ add r9, r8, #CTX_L2_CTRL
+ ldr r0, [r4, #L2X0_CTRL]
+ ldr r1, [r4, #L2X0_AUX_CTRL]
+ ldr r2, [r4, #L2X0_TAG_LATENCY_CTRL]
+ ldr r3, [r4, #L2X0_DATA_LATENCY_CTRL]
+ ldr r4, [r4, #L2X0_PREFETCH_CTRL]
+ stmia r9, {r0-r4}
+#endif
+
+
+__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 0x12 @ IRQ mode
+ add r1, r0, #CTX_IRQ_SPSR
+ ldmia r1, {r12, sp, lr}
+ msr spsr_fsxc, r12
+
+ cps 0x17 @ abort mode
+ add r1, r0, #CTX_ABT_SPSR
+ ldmia r1, {r12, sp, lr}
+ msr spsr_fsxc, r12
+
+ cps 0x1f @ SYS mode
+ add r1, r0, #CTX_SYS_SP
+ ldmia r1, {sp, lr}
+
+ cps 0x1b @ Undefined mode
+ add r1, r0, #CTX_UND_SPSR
+ ldmia r1, {r12, sp, lr}
+ msr spsr_fsxc, r12
+
+ 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
+
+ /* Restore CP14 debug controller context */
+ add r9, r8, #CTX_CP14_REGS
+ ldmia r9, {r0-r3}
+ mcr p14, 0, r1, c0, c6, 0 @ WFAR
+ mcr p14, 0, r2, c0, c7, 0 @ VCR
+ mcr p14, 0, r3, c7, c8, 6 @ CLAIM
+
+ add r9, r8, #CTS_CP14_BKPT_0
+ ldmia r9!, {r2-r3} @ BRKPT_0
+ mcr p14, 0, r2, c0, c0, 4
+ mcr p14, 0, r3, c0, c0, 5
+ ldmia r9!, {r2-r3} @ BRKPT_0
+ mcr p14, 0, r2, c0, c1, 4
+ mcr p14, 0, r3, c0, c1, 5
+ ldmia r9!, {r2-r3} @ BRKPT_0
+ mcr p14, 0, r2, c0, c2, 4
+ mcr p14, 0, r3, c0, c2, 5
+ ldmia r9!, {r2-r3} @ BRKPT_0
+ mcr p14, 0, r2, c0, c3, 4
+ mcr p14, 0, r3, c0, c3, 5
+ ldmia r9!, {r2-r3} @ BRKPT_0
+ mcr p14, 0, r2, c0, c4, 4
+ mcr p14, 0, r3, c0, c4, 5
+ ldmia r9!, {r2-r3} @ BRKPT_0
+ mcr p14, 0, r2, c0, c5, 4
+ mcr p14, 0, r3, c0, c5, 5
+
+ add r9, r8, #CTS_CP14_WPT_0
+ ldmia r9!, {r2-r3} @ WPT_0
+ mcr p14, 0, r2, c0, c0, 6
+ mcr p14, 0, r3, c0, c0, 7
+ ldmia r9!, {r2-r3} @ WPT_0
+ mcr p14, 0, r2, c0, c1, 6
+ mcr p14, 0, r3, c0, c1, 7
+ ldmia r9!, {r2-r3} @ WPT_0
+ mcr p14, 0, r2, c0, c2, 6
+ mcr p14, 0, r3, c0, c2, 7
+ ldmia r9!, {r2-r3} @ WPT_0
+ mcr p14, 0, r2, c0, c3, 6
+ mcr p14, 0, r3, c0, c3, 7
+ isb
+ mcr p14, 0, r0, c0, c2, 2 @ DSCR
+ isb
+
+#ifdef CONFIG_VFPv3
+ orr r4, lr, #0xF00000
+ mcr p15, 0, r4, c1, c0, 2 @ enable coproc access
+ mov r5, #0x40000000
+ VFPFMXR FPEXC, r5 @ enable FPU access
+ add r9, r8, #CTX_VFP_REGS
+ add r7, r8, #CTX_FPEXC
+ VFPFLDMIA r9, r10
+ ldmia r7, {r0, r4}
+ VFPFMXR FPSCR, r4
+ VFPFMXR FPEXC, r0
+#endif
+ 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)
+
+/*
+ * __cortex_a9_l2x0_restart(bool invalidate)
+ *
+ * Reconfigures the L2 cache following a power event.
+ */
+ .align L1_CACHE_SHIFT
+ENTRY(__cortex_a9_l2x0_restart)
+#ifdef CONFIG_CACHE_L2X0
+ ctx_ptr r8, r9
+ mov32 r9, (TEGRA_ARM_PL310_BASE-IO_CPU_PHYS+IO_CPU_VIRT)
+ add r10, r8, #CTX_L2_CTRL
+ ldmia r10, {r3-r7}
+ str r5, [r9, #L2X0_TAG_LATENCY_CTRL]
+ str r6, [r9, #L2X0_DATA_LATENCY_CTRL]
+ str r7, [r9, #L2X0_PREFETCH_CTRL]
+ str r4, [r9, #L2X0_AUX_CTRL]
+ cmp r0, #0
+
+ beq __reenable_l2x0
+
+ mov r0, #0xff
+ str r0, [r9, #L2X0_INV_WAY]
+1: ldr r1, [r9, #L2X0_INV_WAY]
+ tst r1, r0
+ bne 1b
+ mov r0, #0
+ str r0, [r9, #L2X0_CACHE_SYNC]
+__reenable_l2x0:
+ mov r5, #0
+ mcr p15, 0, r5, c8, c3, 0 @ invalidate TLB
+ mcr p15, 0, r5, c7, c5, 6 @ flush BTAC
+ mcr p15, 0, r5, c7, c5, 0 @ flush instruction cache
+ dsb
+ isb
+ str r3, [r9, #L2X0_CTRL]
+#endif
+ b __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..9da0ed6
--- /dev/null
+++ b/arch/arm/mach-tegra/headsmp-t2.S
@@ -0,0 +1,218 @@
+/*
+ * 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
+ /* FIXME: need to record actual power transition here */
+ mov r0, #0
+ b __cortex_a9_l2x0_restart
+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 8e7f115..0000000
--- a/arch/arm/mach-tegra/hotplug.c
+++ /dev/null
@@ -1,140 +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 <linux/completion.h>
-
-#include <asm/cacheflush.h>
-
-static DECLARE_COMPLETION(cpu_killed);
-
-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, #0x04\n"
- " mcr p15, 0, %0, c1, c0, 0\n"
- : "=&r" (v)
- : "r" (0)
- : "cc");
-}
-
-static inline void cpu_leave_lowpower(void)
-{
- unsigned int v;
-
- asm volatile(
- "mrc p15, 0, %0, c1, c0, 0\n"
- " orr %0, %0, #0x04\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)
- :
- : "cc");
-}
-
-static inline void platform_do_lowpower(unsigned int cpu)
-{
- /*
- * 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
- *
- * The trouble is, letting people know about this is not really
- * possible, since we are currently running incoherently, and
- * therefore cannot safely call printk() or anything else
- */
-#ifdef DEBUG
- printk(KERN_WARN "CPU%u: spurious wakeup call\n", cpu);
-#endif
- }
-}
-
-int platform_cpu_kill(unsigned int cpu)
-{
- return wait_for_completion_timeout(&cpu_killed, 5000);
-}
-
-/*
- * platform-specific code to shutdown a CPU
- *
- * Called with IRQs disabled
- */
-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
-
- printk(KERN_NOTICE "CPU%u: shutdown\n", cpu);
- complete(&cpu_killed);
-
- /*
- * we're ready for shutdown now, so do it
- */
- cpu_enter_lowpower();
- platform_do_lowpower(cpu);
-
- /*
- * bring this CPU back into the world of cache
- * coherency, and then restore interrupts
- */
- cpu_leave_lowpower();
-}
-
-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 1c0fd92..659c669 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,42 +20,59 @@
#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/localtimer.h>
+#include <asm/tlbflush.h>
#include <asm/smp_scu.h>
+#include <asm/cpu.h>
+#include <asm/mmu_context.h>
#include <mach/iomap.h>
+#include "power.h"
+
extern void tegra_secondary_startup(void);
static DEFINE_SPINLOCK(boot_lock);
static void __iomem *scu_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE);
+#ifdef CONFIG_HOTPLUG_CPU
+static DEFINE_PER_CPU(struct completion, cpu_killed);
+extern void tegra_hotplug_startup(void);
+#endif
+
+static DECLARE_BITMAP(cpu_init_bits, CONFIG_NR_CPUS) __read_mostly;
+const struct cpumask *const cpu_init_mask = to_cpumask(cpu_init_bits);
+#define cpu_init_map (*(cpumask_t *)cpu_init_mask)
+
#define EVP_CPU_RESET_VECTOR \
(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)
void __cpuinit platform_secondary_init(unsigned int cpu)
{
trace_hardirqs_off();
-
- /*
- * if any interrupts are already enabled for the primary
- * core (e.g. timer irq), then they will not have been enabled
- * for us: do so
- */
gic_cpu_init(0, IO_ADDRESS(TEGRA_ARM_PERIF_BASE) + 0x100);
-
/*
* Synchronise with the boot thread.
*/
spin_lock(&boot_lock);
+#ifdef CONFIG_HOTPLUG_CPU
+ cpu_set(cpu, cpu_init_map);
+ INIT_COMPLETION(per_cpu(cpu_killed, cpu));
+#endif
spin_unlock(&boot_lock);
}
@@ -70,27 +89,30 @@ 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 */
+#ifdef CONFIG_HOTPLUG_CPU
+ if (cpumask_test_cpu(cpu, cpu_init_mask))
+ boot_vector = virt_to_phys(tegra_hotplug_startup);
+ else
+#endif
+ boot_vector = virt_to_phys(tegra_secondary_startup);
+
+ smp_wmb();
- boot_vector = virt_to_phys(tegra_secondary_startup);
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;
@@ -142,6 +164,12 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
for (i = 0; i < max_cpus; i++)
set_cpu_present(i, true);
+#ifdef CONFIG_HOTPLUG_CPU
+ for_each_present_cpu(i) {
+ init_completion(&per_cpu(cpu_killed, i));
+ }
+#endif
+
/*
* Initialise the SCU if there are more than one CPU and let
* them know where to start. Note that, on modern versions of
@@ -154,3 +182,71 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
scu_enable(scu_base);
}
}
+
+#ifdef CONFIG_HOTPLUG_CPU
+
+extern void vfp_sync_state(struct thread_info *thread);
+
+void __cpuinit secondary_start_kernel(void);
+
+int platform_cpu_kill(unsigned int cpu)
+{
+ unsigned int reg;
+ int e;
+
+ e = wait_for_completion_timeout(&per_cpu(cpu_killed, cpu), 100);
+ printk(KERN_NOTICE "CPU%u: %s shutdown\n", cpu, (e) ? "clean":"forced");
+
+ if (e) {
+ do {
+ reg = readl(CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET);
+ cpu_relax();
+ } while (!(reg & (1<<cpu)));
+ } else {
+ writel(0x1111<<cpu, CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET);
+ /* put flow controller in WAIT_EVENT mode */
+ writel(2<<29, IO_ADDRESS(TEGRA_FLOW_CTRL_BASE)+0x14 + 0x8*(cpu-1));
+ }
+ 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 e;
+}
+
+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();
+ complete(&per_cpu(cpu_killed, cpu));
+ flush_cache_all();
+ barrier();
+ __cortex_a9_save(0);
+
+ /* return happens from __cortex_a9_restore */
+ barrier();
+ writel(smp_processor_id(), EVP_CPU_RESET_VECTOR);
+}
+
+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..3f67423
--- /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..a6f7ae6
--- /dev/null
+++ b/arch/arm/mach-tegra/suspend-t2.c
@@ -0,0 +1,413 @@
+/*
+ * 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/init.h>
+
+#include <asm/io.h>
+
+#include <mach/gpio.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 {
+ void __iomem *addr;
+ unsigned int mask;
+ int shift_src;
+ int shift_dst;
+};
+
+#define field(module, offs, field, dst) \
+ { \
+ .addr = IO_ADDRESS(TEGRA_##module##_BASE) + offs, \
+ .mask = 0xfffffffful >> (31 - ((1?field) - (0?field))), \
+ .shift_src = 0?field, \
+ .shift_dst = 0?dst, \
+ }
+
+static const struct pmc_scratch_field pllx[] __initdata = {
+ field(CLK_RESET, 0xe0, 22:20, 17:15), /* PLLX_DIVP */
+ field(CLK_RESET, 0xe0, 17:8, 14:5), /* PLLX_DIVN */
+ field(CLK_RESET, 0xe0, 4:0, 4:0), /* PLLX_DIVM */
+ field(CLK_RESET, 0xe4, 11:8, 25:22), /* PLLX_CPCON */
+ field(CLK_RESET, 0xe4, 7:4, 21:18), /* PLLX_LFCON */
+ field(APB_MISC, 0x8e4, 27:24, 30:27), /* XM2CFGC_VREF_DQ */
+ field(APB_MISC, 0x8c8, 3:3, 26:26), /* XM2CFGC_SCHMT_EN */
+ field(APB_MISC, 0x8d0, 2:2, 31:31), /* XM2CLKCFG_PREEMP_EN */
+};
+
+static const struct pmc_scratch_field emc_0[] __initdata = {
+ field(EMC, 0x3c, 4:0, 31:27), /* R2W */
+ field(EMC, 0x34, 5:0, 20:15), /* RAS */
+ field(EMC, 0x2c, 5:0, 5:0), /* RC */
+ field(EMC, 0x30, 8:0, 14:6), /* RFC */
+ field(EMC, 0x38, 5:0, 26:21), /* RP */
+};
+
+static const struct pmc_scratch_field emc_1[] __initdata = {
+ field(EMC, 0x44, 4:0, 9:5), /* R2P */
+ field(EMC, 0x4c, 5:0, 20:15), /* RD_RCD */
+ field(EMC, 0x54, 3:0, 30:27), /* RRD */
+ field(EMC, 0x48, 4:0, 14:10), /* W2P */
+ field(EMC, 0x40, 4:0, 4:0), /* W2R */
+ field(EMC, 0x50, 5:0, 26:21), /* WR_RCD */
+};
+
+static const struct pmc_scratch_field emc_2[] __initdata = {
+ field(EMC, 0x2b8, 2:2, 31:31), /* CLKCHANGE_SR_ENABLE */
+ field(EMC, 0x2b8, 10:10, 30:30), /* USE_ADDR_CLK */
+ field(EMC, 0x80, 4:0, 29:25), /* PCHG2PDEN */
+ field(EMC, 0x64, 3:0, 15:12), /* QRST */
+ field(EMC, 0x68, 3:0, 19:16), /* QSAFE */
+ field(EMC, 0x60, 3:0, 11:8), /* QUSE */
+ field(EMC, 0x6c, 4:0, 24:20), /* RDV */
+ field(EMC, 0x58, 3:0, 3:0), /* REXT */
+ field(EMC, 0x5c, 3:0, 7:4), /* WDV */
+};
+
+static const struct pmc_scratch_field emc_3[] __initdata = {
+ field(EMC, 0x74, 3:0, 19:16), /* BURST_REFRESH_NUM */
+ field(EMC, 0x7c, 3:0, 27:24), /* PDEX2RD */
+ field(EMC, 0x78, 3:0, 23:20), /* PDEX2WR */
+ field(EMC, 0x70, 4:0, 4:0), /* REFRESH_LO */
+ field(EMC, 0x70, 15:5, 15:5), /* REFRESH */
+ field(EMC, 0xa0, 3:0, 31:28), /* TCLKSTABLE */
+};
+
+static const struct pmc_scratch_field emc_4[] __initdata = {
+ field(EMC, 0x84, 4:0, 4:0), /* ACT2PDEN */
+ field(EMC, 0x88, 4:0, 9:5), /* AR2PDEN */
+ field(EMC, 0x8c, 5:0, 15:10), /* RW2PDEN */
+ field(EMC, 0x94, 3:0, 31:28), /* TCKE */
+ field(EMC, 0x90, 11:0, 27:16), /* TXSR */
+};
+
+static const struct pmc_scratch_field emc_5[] __initdata = {
+ field(EMC, 0x8, 10:10, 30:30), /* AP_REQ_BUSY_CTRL */
+ field(EMC, 0x8, 24:24, 31:31), /* CFG_PRIORITY */
+ field(EMC, 0x8, 2:2, 26:26), /* FORCE_UPDATE */
+ field(EMC, 0x8, 4:4, 27:27), /* MRS_WAIT */
+ field(EMC, 0x8, 5:5, 28:28), /* PERIODIC_QRST */
+ field(EMC, 0x8, 9:9, 29:29), /* READ_DQM_CTRL */
+ field(EMC, 0x8, 0:0, 24:24), /* READ_MUX */
+ field(EMC, 0x8, 1:1, 25:25), /* WRITE_MUX */
+ field(EMC, 0xa4, 3:0, 9:6), /* TCLKSTOP */
+ field(EMC, 0xa8, 13:0, 23:10), /* TREFBW */
+ field(EMC, 0x9c, 5:0, 5:0), /* TRPAB */
+};
+
+static const struct pmc_scratch_field emc_6[] __initdata = {
+ field(EMC, 0xfc, 1:0, 1:0), /* DQSIB_DLY_MSB_BYTE_0 */
+ field(EMC, 0xfc, 9:8, 3:2), /* DQSIB_DLY_MSB_BYTE_1 */
+ field(EMC, 0xfc, 17:16, 5:4), /* DQSIB_DLY_MSB_BYTE_2 */
+ field(EMC, 0xfc, 25:24, 7:6), /* DQSIB_DLY_MSB_BYTE_3 */
+ field(EMC, 0x110, 1:0, 9:8), /* QUSE_DLY_MSB_BYTE_0 */
+ field(EMC, 0x110, 9:8, 11:10), /* QUSE_DLY_MSB_BYTE_1 */
+ field(EMC, 0x110, 17:16, 13:12), /* QUSE_DLY_MSB_BYTE_2 */
+ field(EMC, 0x110, 25:24, 15:14), /* QUSE_DLY_MSB_BYTE_3 */
+ field(EMC, 0xac, 3:0, 25:22), /* QUSE_EXTRA */
+ field(EMC, 0x98, 5:0, 21:16), /* TFAW */
+ field(APB_MISC, 0x8e4, 5:5, 30:30), /* XM2CFGC_VREF_DQ_EN */
+ field(APB_MISC, 0x8e4, 19:16, 29:26), /* XM2CFGC_VREF_DQS */
+};
+
+static const struct pmc_scratch_field emc_dqsib_dly[] __initdata = {
+ field(EMC, 0xf8, 31:0, 31:0), /* DQSIB_DLY_BYTE_0 - DQSIB_DLY_BYTE_3*/
+};
+
+static const struct pmc_scratch_field emc_quse_dly[] __initdata = {
+ field(EMC, 0x10c, 31:0, 31:0), /* QUSE_DLY_BYTE_0 - QUSE_DLY_BYTE_3*/
+};
+
+static const struct pmc_scratch_field emc_clktrim[] __initdata = {
+ field(EMC, 0x2d0, 29:0, 29:0), /* DATA0_CLKTRIM - DATA3_CLKTRIM +
+ * MCLK_ADDR_CLKTRIM */
+};
+
+static const struct pmc_scratch_field emc_autocal_fbio[] __initdata = {
+ field(EMC, 0x2a4, 29:29, 29:29), /* AUTO_CAL_ENABLE */
+ field(EMC, 0x2a4, 30:30, 30:30), /* AUTO_CAL_OVERRIDE */
+ field(EMC, 0x2a4, 12:8, 18:14), /* AUTO_CAL_PD_OFFSET */
+ field(EMC, 0x2a4, 4:0, 13:9), /* AUTO_CAL_PU_OFFSET */
+ field(EMC, 0x2a4, 25:16, 28:19), /* AUTO_CAL_STEP */
+ field(EMC, 0xf4, 16:16, 0:0), /* CFG_DEN_EARLY */
+ field(EMC, 0x104, 8:8, 8:8), /* CTT_TERMINATION */
+ field(EMC, 0x104, 7:7, 7:7), /* DIFFERENTIAL_DQS */
+ field(EMC, 0x104, 9:9, 31:31), /* DQS_PULLD */
+ field(EMC, 0x104, 1:0, 5:4), /* DRAM_TYPE */
+ field(EMC, 0x104, 4:4, 6:6), /* DRAM_WIDTH */
+ field(EMC, 0x114, 2:0, 3:1), /* CFG_QUSE_LATE */
+};
+
+static const struct pmc_scratch_field emc_autocal_interval[] __initdata = {
+ field(EMC, 0x2a8, 27:0, 27:0), /* AUTOCAL_INTERVAL */
+ field(EMC, 0x2b8, 1:1, 29:29), /* CLKCHANGE_PD_ENABLE */
+ field(EMC, 0x2b8, 0:0, 28:28), /* CLKCHANGE_REQ_ENABLE */
+ field(EMC, 0x2b8, 9:8, 31:30), /* PIN_CONFIG */
+};
+
+static const struct pmc_scratch_field emc_cfgs[] __initdata = {
+ field(EMC, 0x10, 9:8, 4:3), /* EMEM_BANKWIDTH */
+ field(EMC, 0x10, 2:0, 2:0), /* EMEM_COLWIDTH */
+ field(EMC, 0x10, 19:16, 8:5), /* EMEM_DEVSIZE */
+ field(EMC, 0x10, 25:24, 10:9), /* EMEM_NUMDEV */
+ field(EMC, 0xc, 24:24, 21:21), /* AUTO_PRE_RD */
+ field(EMC, 0xc, 25:25, 22:22), /* AUTO_PRE_WR */
+ field(EMC, 0xc, 16:16, 20:20), /* CLEAR_AP_PREV_SPREQ */
+ field(EMC, 0xc, 29:29, 23:23), /* DRAM_ACPD */
+ field(EMC, 0xc, 30:30, 24:24), /* DRAM_CLKSTOP_PDSR_ONLY */
+ field(EMC, 0xc, 31:31, 25:25), /* DRAM_CLKSTOP */
+ field(EMC, 0xc, 15:8, 19:12), /* PRE_IDLE_CYCLES */
+ field(EMC, 0xc, 0:0, 11:11), /* PRE_IDLE_EN */
+ field(EMC, 0x2bc, 29:28, 29:28), /* CFG_DLL_LOCK_LIMIT */
+ field(EMC, 0x2bc, 7:6, 31:30), /* CFG_DLL_MODE */
+ field(MC, 0x10c, 0:0, 26:26), /* LL_CTRL */
+ field(MC, 0x10c, 1:1, 27:27), /* LL_SEND_BOTH */
+};
+
+static const struct pmc_scratch_field emc_adr_cfg1[] __initdata = {
+ field(EMC, 0x14, 9:8, 9:8), /* EMEM1_BANKWIDTH */
+ field(EMC, 0x14, 2:0, 7:5), /* EMEM1_COLWIDTH */
+ field(EMC, 0x14, 19:16, 13:10), /* EMEM1_DEVSIZE */
+ field(EMC, 0x2dc, 28:24, 4:0), /* TERM_DRVUP */
+ field(APB_MISC, 0x8d4, 3:0, 17:14), /* XM2COMP_VREF_SEL */
+ field(APB_MISC, 0x8d8, 18:16, 23:21), /* XM2VTTGEN_CAL_DRVDN */
+ field(APB_MISC, 0x8d8, 26:24, 20:18), /* XM2VTTGEN_CAL_DRVUP */
+ field(APB_MISC, 0x8d8, 1:1, 30:30), /* XM2VTTGEN_SHORT_PWRGND */
+ field(APB_MISC, 0x8d8, 0:0, 31:31), /* XM2VTTGEN_SHORT */
+ field(APB_MISC, 0x8d8, 14:12, 26:24), /* XM2VTTGEN_VAUXP_LEVEL */
+ field(APB_MISC, 0x8d8, 10:8, 29:27), /* XM2VTTGEN_VCLAMP_LEVEL */
+};
+
+static const struct pmc_scratch_field emc_digital_dll[] __initdata = {
+ field(EMC, 0x2bc, 1:1, 23:23), /* DLI_TRIMMER_EN */
+ field(EMC, 0x2bc, 0:0, 22:22), /* DLL_EN */
+ field(EMC, 0x2bc, 5:5, 27:27), /* DLL_LOWSPEED */
+ field(EMC, 0x2bc, 2:2, 24:24), /* DLL_OVERRIDE_EN */
+ field(EMC, 0x2bc, 11:8, 31:28), /* DLL_UDSET */
+ field(EMC, 0x2bc, 4:4, 26:26), /* PERBYTE_TRIMMER_OVERRIDE */
+ field(EMC, 0x2bc, 3:3, 25:25), /* USE_SINGLE_DLL */
+ field(MC, 0xc, 21:0, 21:0), /* EMEM_SIZE_KB */
+};
+
+static const struct pmc_scratch_field emc_dqs_clktrim[] __initdata = {
+ field(EMC, 0x2d4, 29:0, 29:0), /* DQS0_CLKTRIM - DQS3 + MCLK*/
+ field(APB_MISC, 0x8e4, 3:3, 31:31), /* XM2CFGC_CTT_HIZ_EN */
+ field(APB_MISC, 0x8e4, 4:4, 30:30), /* XM2CFGC_VREF_DQS_EN */
+};
+
+static const struct pmc_scratch_field emc_dq_clktrim[] __initdata = {
+ field(EMC, 0x2d8, 29:0, 29:0),
+ field(APB_MISC, 0x8e4, 2:2, 30:30), /* XM2CFGC_PREEMP_EN */
+ field(APB_MISC, 0x8e4, 0:0, 31:31), /* XM2CFGC_RX_FT_REC_EN */
+};
+
+static const struct pmc_scratch_field emc_dll_xform_dqs[] __initdata = {
+ field(EMC, 0x2bc, 25:16, 29:20), /* CFG_DLL_OVERRIDE_VAL */
+ field(EMC, 0x2c0, 4:0, 4:0), /* DQS_MULT */
+ field(EMC, 0x2c0, 22:8, 19:5), /* DQS_OFFS */
+ field(MC, 0x10c, 31:31, 30:30), /* LL_DRAM_INTERLEAVE */
+};
+
+static const struct pmc_scratch_field emc_odt_rw[] __initdata = {
+ field(EMC, 0x2c4, 4:0, 4:0), /* QUSE_MULT */
+ field(EMC, 0x2c4, 22:8, 19:5), /* QUSE_OFF */
+ field(EMC, 0xb4, 31:31, 29:29), /* DISABLE_ODT_DURING_READ */
+ field(EMC, 0xb4, 30:30, 28:28), /* B4_READ */
+ field(EMC, 0xb4, 2:0, 27:25), /* RD_DELAY */
+ field(EMC, 0xb0, 31:31, 24:24), /* ENABLE_ODT_DURING_WRITE */
+ field(EMC, 0xb0, 30:30, 23:23), /* B4_WRITE */
+ field(EMC, 0xb0, 2:0, 22:20), /* WR_DELAY */
+};
+
+static const struct pmc_scratch_field arbitration_xbar[] __initdata = {
+ field(AHB_GIZMO, 0xdc, 31:0, 31:0),
+};
+
+static const struct pmc_scratch_field emc_zcal[] __initdata = {
+ field(EMC, 0x2e0, 23:0, 23:0), /* ZCAL_REF_INTERVAL */
+ field(EMC, 0x2e4, 7:0, 31:24), /* ZCAL_WAIT_CNT */
+};
+
+static const struct pmc_scratch_field emc_ctt_term[] __initdata = {
+ field(EMC, 0x2dc, 19:15, 30:26), /* TERM_DRVDN */
+ field(EMC, 0x2dc, 12:8, 25:21), /* TERM_OFFSET */
+ field(EMC, 0x2dc, 31:31, 31:31), /* TERM_OVERRIDE */
+ field(EMC, 0x2dc, 2:0, 20:18), /* TERM_SLOPE */
+ field(EMC, 0x2e8, 23:16, 15:8), /* ZQ_MRW_MA */
+ field(EMC, 0x2e8, 7:0, 7:0), /* ZQ_MRW_OP */
+};
+
+static const struct pmc_scratch_field xm2_cfgd[] __initdata = {
+ field(APB_MISC, 0x8e8, 18:16, 11:9), /* CFGD0_DLYIN_TRM */
+ field(APB_MISC, 0x8e8, 22:20, 8:6), /* CFGD1_DLYIN_TRM */
+ field(APB_MISC, 0x8e8, 26:24, 5:3), /* CFGD2_DLYIN_TRM */
+ field(APB_MISC, 0x8e8, 30:28, 2:0), /* CFGD3_DLYIN_TRM */
+ field(APB_MISC, 0x8e8, 3:3, 12:12), /* XM2CFGD_CTT_HIZ_EN */
+ field(APB_MISC, 0x8e8, 2:2, 13:13), /* XM2CFGD_PREEMP_EN */
+ field(APB_MISC, 0x8e8, 0:0, 14: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;
+
+ for (i=0; i<ARRAY_SIZE(scratch); i++) {
+ unsigned int r = 0;
+ int j;
+
+ for (j=0; j<scratch[i].num_fields; j++) {
+ unsigned int v = readl(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..1246afe
--- /dev/null
+++ b/arch/arm/mach-tegra/suspend.c
@@ -0,0 +1,870 @@
+/*
+ * 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 "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;
+};
+
+volatile 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 = NULL;
+
+static struct clk *tegra_pclk = NULL;
+static const struct tegra_suspend_platform_data *pdata = NULL;
+static unsigned long wb0_restore = 0;
+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)
+{
+ static int last_pclk = 0;
+ unsigned long long ticks;
+ unsigned long long pclk;
+
+ if (WARN_ON_ONCE(rate <= 0))
+ pclk = 100000000;
+ else
+ pclk = rate;
+
+ if (rate != 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();
+ }
+ last_pclk = pclk;
+}
+
+static int create_suspend_pgtable(void)
+{
+ int i;
+ pmd_t *pmd;
+ /* arrays of virtual-to-physical mappings which must be
+ * present to safely boot hotplugged / LP2-idled CPUs.
+ * tegra_hotplug_startup (hotplug reset vector) is mapped
+ * VA=PA so that the translation post-MMU is the same as
+ * pre-MMU, IRAM is mapped VA=PA so that SDRAM self-refresh
+ * can safely disable the MMU */
+ unsigned long addr_v[] = {
+ PHYS_OFFSET,
+ IO_IRAM_PHYS,
+ (unsigned long)tegra_context_area,
+#ifdef CONFIG_HOTPLUG_CPU
+ (unsigned long)virt_to_phys(tegra_hotplug_startup),
+#endif
+ (unsigned long)__cortex_a9_restore,
+ (unsigned long)virt_to_phys(__shut_off_mmu),
+ };
+ unsigned long addr_p[] = {
+ PHYS_OFFSET,
+ IO_IRAM_PHYS,
+ (unsigned long)virt_to_phys(tegra_context_area),
+#ifdef CONFIG_HOTPLUG_CPU
+ (unsigned long)virt_to_phys(tegra_hotplug_startup),
+#endif
+ (unsigned long)virt_to_phys(__cortex_a9_restore),
+ (unsigned long)virt_to_phys(__shut_off_mmu),
+ };
+ unsigned int flags = PMD_TYPE_SECT | PMD_SECT_AP_WRITE |
+ PMD_SECT_WBWA | PMD_SECT_S;
+
+ tegra_pgd = pgd_alloc(&init_mm);
+ if (!tegra_pgd)
+ return -ENOMEM;
+
+ for (i=0; i<ARRAY_SIZE(addr_p); i++) {
+ unsigned long v = addr_v[i];
+ pmd = pmd_offset(tegra_pgd + pgd_index(v), v);
+ *pmd = __pmd((addr_p[i] & PGDIR_MASK) | flags);
+ flush_pmd_entry(pmd);
+ outer_clean_range(__pa(pmd), __pa(pmd + 1));
+ }
+
+ tegra_pgd_phys = virt_to_phys(tegra_pgd);
+ __cpuc_flush_dcache_area(&tegra_pgd_phys,
+ sizeof(tegra_pgd_phys));
+ outer_clean_range(__pa(&tegra_pgd_phys),
+ __pa(&tegra_pgd_phys+1));
+
+ __cpuc_flush_dcache_area(&tegra_context_area,
+ sizeof(tegra_context_area));
+ outer_clean_range(__pa(&tegra_context_area),
+ __pa(&tegra_context_area+1));
+
+ return 0;
+}
+
+
+
+/*
+ * suspend_cpu_complex
+ *
+ * disable periodic IRQs used for DVFS to prevent suspend wakeups
+ * disable coresight debug interface
+ *
+ *
+ */
+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);
+ get_irq_chip(IRQ_LOCALTIMER)->unmask(IRQ_LOCALTIMER);
+
+ enable_irq(INT_SYS_STATS_MON);
+}
+
+static noinline void suspend_cpu_complex(void)
+{
+ unsigned int reg;
+ int i;
+
+ disable_irq(INT_SYS_STATS_MON);
+
+ /* 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);
+ local_timer_stop();
+#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();
+ /* structure is written by reset code, so the L2 lines
+ * must be invalidated */
+ outer_flush_range(__pa(&tegra_sctx),__pa(&tegra_sctx+1));
+ barrier();
+
+ __cortex_a9_save(mode);
+ /* return from __cortex_a9_restore */
+ barrier();
+ restore_cpu_complex();
+
+ 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 = NULL;
+static unsigned int iram_save_size = 0;
+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);
+ 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)
+{
+ struct irq_desc *desc;
+ void __iomem *mc = IO_ADDRESS(TEGRA_MC_BASE);
+ unsigned long flags;
+ u32 mc_data[2];
+ int irq;
+ 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);
+ }
+
+ for_each_irq_desc(irq, desc) {
+ if ((desc->status & IRQ_WAKEUP) &&
+ (desc->status & IRQ_SUSPENDED)) {
+ get_irq_chip(irq)->unmask(irq);
+ }
+ }
+
+ 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();
+
+ for_each_irq_desc(irq, desc) {
+ if ((desc->status & IRQ_WAKEUP) &&
+ (desc->status & IRQ_SUSPENDED)) {
+ get_irq_chip(irq)->mask(irq);
+ }
+ }
+
+ /* 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 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
+
+static unsigned long lp0_vec_orig_start = 0;
+static unsigned long lp0_vec_orig_size = 0;
+
+static int __init tegra_lp0_vec_arg(char *options)
+{
+ char *p = options;
+
+ lp0_vec_orig_size = memparse(p, &p);
+ if (*p == '@')
+ lp0_vec_orig_start = memparse(p+1, &p);
+
+ return 0;
+}
+__setup("lp0_vec=", tegra_lp0_vec_arg);
+
+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 &&
+ lp0_vec_orig_size && lp0_vec_orig_start) {
+ unsigned char *reloc_lp0;
+ unsigned long tmp;
+ void __iomem *orig;
+ reloc_lp0 = kmalloc(lp0_vec_orig_size+L1_CACHE_BYTES-1,
+ GFP_KERNEL);
+ WARN_ON(!reloc_lp0);
+ if (!reloc_lp0)
+ goto out;
+
+ orig = ioremap(lp0_vec_orig_start, lp0_vec_orig_size);
+ WARN_ON(!orig);
+ if (!orig) {
+ kfree(reloc_lp0);
+ goto out;
+ }
+ tmp = (unsigned long) reloc_lp0;
+ tmp = (tmp + L1_CACHE_BYTES - 1) & ~(L1_CACHE_BYTES-1);
+ reloc_lp0 = (unsigned char *)tmp;
+ memcpy(reloc_lp0, orig, lp0_vec_orig_size);
+ iounmap(orig);
+ wb0_restore = virt_to_phys(reloc_lp0);
+ }
+out:
+ if (plat->suspend_mode == TEGRA_SUSPEND_LP0 && !wb0_restore) {
+ 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 *)¤t_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] 27+ messages in thread
* [PATCH 13/21] ARM: tegra: irq: Add set_wake and set_type support for suspend
2010-12-05 23:08 [PATCH 00/21] Updates for Tegra support in 2.6.38 Colin Cross
` (11 preceding siblings ...)
2010-12-05 23:08 ` [PATCH 12/21] ARM: tegra: Add suspend and hotplug support Colin Cross
@ 2010-12-05 23:09 ` Colin Cross
2010-12-05 23:09 ` [PATCH 14/21] ARM: tegra: irq: Add debugfs file to show wake irqs Colin Cross
` (7 subsequent siblings)
20 siblings, 0 replies; 27+ messages in thread
From: Colin Cross @ 2010-12-05 23:09 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 8f47a9c..ced7007 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(unsigned int irq)
{
gic_mask_irq(irq);
@@ -109,11 +181,34 @@ static void tegra_unmask(unsigned int irq)
tegra_legacy_unmask_irq(irq);
}
+static int tegra_set_wake(unsigned int irq, unsigned int enable)
+{
+ int ret;
+ ret = tegra_set_lp1_wake(irq, enable);
+ if (ret)
+ return ret;
+
+ if (tegra_get_suspend_mode() == TEGRA_SUSPEND_LP0)
+ return tegra_set_lp0_wake(irq, enable);
+
+ return 0;
+}
+
+static int tegra_set_type(unsigned int irq, unsigned int flow_type)
+{
+ if (tegra_get_suspend_mode() == TEGRA_SUSPEND_LP0)
+ return tegra_set_lp0_wake_type(irq, flow_type);
+
+ return 0;
+}
+
static struct irq_chip tegra_irq = {
.name = "PPI",
.ack = gic_ack_irq,
.mask = tegra_mask,
.unmask = tegra_unmask,
+ .set_wake = tegra_set_wake,
+ .set_type = tegra_set_type,
#ifdef CONFIG_SMP
.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] 27+ messages in thread
* [PATCH 14/21] ARM: tegra: irq: Add debugfs file to show wake irqs
2010-12-05 23:08 [PATCH 00/21] Updates for Tegra support in 2.6.38 Colin Cross
` (12 preceding siblings ...)
2010-12-05 23:09 ` [PATCH 13/21] ARM: tegra: irq: Add set_wake and set_type support for suspend Colin Cross
@ 2010-12-05 23:09 ` Colin Cross
2010-12-06 21:07 ` Stephen Boyd
2010-12-05 23:09 ` [PATCH 15/21] ARM: tegra: irq: Implement retrigger Colin Cross
` (6 subsequent siblings)
20 siblings, 1 reply; 27+ messages in thread
From: Colin Cross @ 2010-12-05 23:09 UTC (permalink / raw)
To: linux-arm-kernel
Signed-off-by: Colin Cross <ccross@android.com>
---
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 ced7007..8f0ddc6 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", 0755, 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] 27+ messages in thread
* [PATCH 15/21] ARM: tegra: irq: Implement retrigger
2010-12-05 23:08 [PATCH 00/21] Updates for Tegra support in 2.6.38 Colin Cross
` (13 preceding siblings ...)
2010-12-05 23:09 ` [PATCH 14/21] ARM: tegra: irq: Add debugfs file to show wake irqs Colin Cross
@ 2010-12-05 23:09 ` Colin Cross
2010-12-05 23:09 ` [PATCH 16/21] ARM: tegra: gpio: Add support for waking from suspend Colin Cross
` (5 subsequent siblings)
20 siblings, 0 replies; 27+ messages in thread
From: Colin Cross @ 2010-12-05 23:09 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 8f0ddc6..458f9c8 100644
--- a/arch/arm/mach-tegra/irq.c
+++ b/arch/arm/mach-tegra/irq.c
@@ -208,9 +208,21 @@ static int tegra_set_type(unsigned int irq, unsigned int flow_type)
return 0;
}
+static void tegra_ack(unsigned int irq)
+{
+ tegra_legacy_force_irq_clr(irq);
+ gic_ack_irq(irq);
+}
+
+static int tegra_retrigger(unsigned int irq)
+{
+ tegra_legacy_force_irq_set(irq);
+ return 1;
+}
+
static struct irq_chip tegra_irq = {
.name = "PPI",
- .ack = gic_ack_irq,
+ .ack = tegra_ack,
.mask = tegra_mask,
.unmask = tegra_unmask,
.set_wake = tegra_set_wake,
@@ -218,6 +230,7 @@ static struct irq_chip tegra_irq = {
#ifdef CONFIG_SMP
.set_affinity = gic_set_cpu,
#endif
+ .retrigger = tegra_retrigger,
};
void __init tegra_init_irq(void)
--
1.7.3.1
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 16/21] ARM: tegra: gpio: Add support for waking from suspend
2010-12-05 23:08 [PATCH 00/21] Updates for Tegra support in 2.6.38 Colin Cross
` (14 preceding siblings ...)
2010-12-05 23:09 ` [PATCH 15/21] ARM: tegra: irq: Implement retrigger Colin Cross
@ 2010-12-05 23:09 ` Colin Cross
2010-12-05 23:09 ` [PATCH 17/21] ARM: tegra: add CPU_IDLE driver Colin Cross
` (4 subsequent siblings)
20 siblings, 0 replies; 27+ messages in thread
From: Colin Cross @ 2010-12-05 23:09 UTC (permalink / raw)
To: linux-arm-kernel
From: Colin Cross <ccross@google.com>
Signed-off-by: Colin Cross <ccross@google.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 1bf92e5..0187aaa 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(unsigned int irq, unsigned int type)
else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
__set_irq_handler_unlocked(irq, handle_edge_irq);
+ if (tegra_get_suspend_mode() == TEGRA_SUSPEND_LP0)
+ tegra_set_lp0_wake_type(irq, type);
+
return 0;
}
@@ -319,8 +322,17 @@ void tegra_gpio_suspend(void)
static int tegra_gpio_wake_enable(unsigned int irq, unsigned int enable)
{
+ int ret;
struct tegra_gpio_bank *bank = get_irq_chip_data(irq);
- 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(irq, enable);
+
+ return 0;
}
#endif
--
1.7.3.1
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 17/21] ARM: tegra: add CPU_IDLE driver
2010-12-05 23:08 [PATCH 00/21] Updates for Tegra support in 2.6.38 Colin Cross
` (15 preceding siblings ...)
2010-12-05 23:09 ` [PATCH 16/21] ARM: tegra: gpio: Add support for waking from suspend Colin Cross
@ 2010-12-05 23:09 ` Colin Cross
2010-12-05 23:09 ` [PATCH 18/21] ARM: tegra: iomap: Add missing devices, fix use of SZ_8, SZ_64 Colin Cross
` (3 subsequent siblings)
20 siblings, 0 replies; 27+ messages in thread
From: Colin Cross @ 2010-12-05 23:09 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 | 711 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 712 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 4b36385..ffef418 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..765e368
--- /dev/null
+++ b/arch/arm/mach-tegra/cpuidle.c
@@ -0,0 +1,711 @@
+/*
+ * 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);
+ chip->unmask(irq);
+}
+
+static inline void tegra_mask_irq(int irq)
+{
+ struct irq_chip *chip = get_irq_chip(irq);
+ chip->mask(irq);
+}
+
+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();
+ writel(twd_ctrl, twd_base + 0x8);
+ writel(twd_load, twd_base + 0);
+ gic_cpu_init(0, IO_ADDRESS(TEGRA_ARM_PERIF_BASE) + 0x100);
+ 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_SHALLOW | 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_BALANCED | 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] 27+ messages in thread
* [PATCH 18/21] ARM: tegra: iomap: Add missing devices, fix use of SZ_8, SZ_64
2010-12-05 23:08 [PATCH 00/21] Updates for Tegra support in 2.6.38 Colin Cross
` (16 preceding siblings ...)
2010-12-05 23:09 ` [PATCH 17/21] ARM: tegra: add CPU_IDLE driver Colin Cross
@ 2010-12-05 23:09 ` Colin Cross
2010-12-05 23:09 ` [PATCH 19/21] ARM: tegra: cpufreq: Disable cpufreq during suspend Colin Cross
` (2 subsequent siblings)
20 siblings, 0 replies; 27+ messages in thread
From: Colin Cross @ 2010-12-05 23:09 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] 27+ messages in thread
* [PATCH 19/21] ARM: tegra: cpufreq: Disable cpufreq during suspend
2010-12-05 23:08 [PATCH 00/21] Updates for Tegra support in 2.6.38 Colin Cross
` (17 preceding siblings ...)
2010-12-05 23:09 ` [PATCH 18/21] ARM: tegra: iomap: Add missing devices, fix use of SZ_8, SZ_64 Colin Cross
@ 2010-12-05 23:09 ` Colin Cross
2010-12-05 23:09 ` [PATCH 20/21] ARM: tegra: Allow overriding arch_reset Colin Cross
2010-12-05 23:09 ` [PATCH 21/21] ARM: tegra: dma: Fix critical data corruption bugs Colin Cross
20 siblings, 0 replies; 27+ messages in thread
From: Colin Cross @ 2010-12-05 23:09 UTC (permalink / raw)
To: linux-arm-kernel
From: Colin Cross <ccross@google.com>
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@google.com>
---
arch/arm/mach-tegra/cpu-tegra.c | 74 +++++++++++++++++++++++++++++++--------
1 files changed, 59 insertions(+), 15 deletions(-)
diff --git a/arch/arm/mach-tegra/cpu-tegra.c b/arch/arm/mach-tegra/cpu-tegra.c
index fea5719..8363a21 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,29 @@ 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 +135,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 +183,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] 27+ messages in thread
* [PATCH 20/21] ARM: tegra: Allow overriding arch_reset
2010-12-05 23:08 [PATCH 00/21] Updates for Tegra support in 2.6.38 Colin Cross
` (18 preceding siblings ...)
2010-12-05 23:09 ` [PATCH 19/21] ARM: tegra: cpufreq: Disable cpufreq during suspend Colin Cross
@ 2010-12-05 23:09 ` Colin Cross
2010-12-05 23:39 ` Russell King - ARM Linux
2010-12-05 23:09 ` [PATCH 21/21] ARM: tegra: dma: Fix critical data corruption bugs Colin Cross
20 siblings, 1 reply; 27+ messages in thread
From: Colin Cross @ 2010-12-05 23:09 UTC (permalink / raw)
To: linux-arm-kernel
From: Colin Cross <ccross@google.com>
Signed-off-by: Colin Cross <ccross@google.com>
---
arch/arm/mach-tegra/common.c | 3 +++
arch/arm/mach-tegra/include/mach/system.h | 18 ++++++++++++++++--
2 files changed, 19 insertions(+), 2 deletions(-)
diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c
index 64ea415..c8e9bdb 100644
--- a/arch/arm/mach-tegra/common.c
+++ b/arch/arm/mach-tegra/common.c
@@ -26,12 +26,15 @@
#include <mach/iomap.h>
#include <mach/dma.h>
+#include <mach/system.h>
#include "board.h"
#include "clock.h"
#include "fuse.h"
#include "power.h"
+void (*tegra_reset)(char mode, const char *cmd);
+
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..70c95ac 100644
--- a/arch/arm/mach-tegra/include/mach/system.h
+++ b/arch/arm/mach-tegra/include/mach/system.h
@@ -24,16 +24,30 @@
#include <mach/hardware.h>
#include <mach/iomap.h>
+extern void (*tegra_reset)(char mode, const char *cmd);
+
static inline void arch_idle(void)
{
}
-static inline void arch_reset(char mode, const char *cmd)
+static inline void tegra_assert_system_reset(void)
{
void __iomem *reset = IO_ADDRESS(TEGRA_CLK_RESET_BASE + 0x04);
- u32 reg = readl(reset);
+ u32 reg;
+
+ reg = readl(reset);
reg |= 0x04;
writel(reg, reset);
}
+static inline void arch_reset(char mode, const char *cmd)
+{
+ if (tegra_reset)
+ tegra_reset(mode, cmd);
+ else
+ tegra_assert_system_reset();
+
+ do { } while (1);
+}
+
#endif
--
1.7.3.1
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 21/21] ARM: tegra: dma: Fix critical data corruption bugs
2010-12-05 23:08 [PATCH 00/21] Updates for Tegra support in 2.6.38 Colin Cross
` (19 preceding siblings ...)
2010-12-05 23:09 ` [PATCH 20/21] ARM: tegra: Allow overriding arch_reset Colin Cross
@ 2010-12-05 23:09 ` Colin Cross
20 siblings, 0 replies; 27+ messages in thread
From: Colin Cross @ 2010-12-05 23:09 UTC (permalink / raw)
To: linux-arm-kernel
From: Colin Cross <ccross@google.com>
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..c2bb582 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] 27+ messages in thread
* [PATCH 02/21] ARM: gic: Add functions to save and restore gic state
2010-12-05 23:08 ` [PATCH 02/21] ARM: gic: Add functions to save and restore gic state Colin Cross
@ 2010-12-05 23:30 ` Russell King - ARM Linux
2010-12-05 23:52 ` Colin Cross
0 siblings, 1 reply; 27+ messages in thread
From: Russell King - ARM Linux @ 2010-12-05 23:30 UTC (permalink / raw)
To: linux-arm-kernel
On Sun, Dec 05, 2010 at 03:08:49PM -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
This clashes with the GIC cleanups I've previously posted.
> 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>
> ---
> arch/arm/common/gic.c | 136 ++++++++++++++++++++++++++++++++--
> arch/arm/include/asm/hardware/gic.h | 4 +
> 2 files changed, 132 insertions(+), 8 deletions(-)
>
> diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c
> index 772f95f..9deb34f 100644
> --- a/arch/arm/common/gic.c
> +++ b/arch/arm/common/gic.c
> @@ -39,6 +39,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 max_irq;
> };
>
> #ifndef MAX_GIC_NR
> @@ -207,21 +214,15 @@ void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq)
> set_irq_chained_handler(irq, gic_handle_cascade_irq);
> }
>
> -void __init gic_dist_init(unsigned int gic_nr, void __iomem *base,
> - unsigned int irq_start)
> +static unsigned int _gic_dist_init(unsigned int gic_nr)
> {
> unsigned int max_irq, i;
> + void __iomem *base = gic_data[gic_nr].dist_base;
> u32 cpumask = 1 << smp_processor_id();
>
> - if (gic_nr >= MAX_GIC_NR)
> - BUG();
> -
> cpumask |= cpumask << 8;
> cpumask |= cpumask << 16;
>
> - gic_data[gic_nr].dist_base = base;
> - gic_data[gic_nr].irq_offset = (irq_start - 1) & ~31;
> -
> writel(0, base + GIC_DIST_CTRL);
>
> /*
> @@ -263,6 +264,109 @@ void __init gic_dist_init(unsigned int gic_nr, void __iomem *base,
> for (i = 32; i < max_irq; i += 32)
> writel(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i * 4 / 32);
>
> + return max_irq;
> +}
> +
> +static void _gic_dist_exit(unsigned int gic_nr)
> +{
> + writel(0, gic_data[gic_nr].dist_base + GIC_DIST_CTRL);
> +}
> +
> +#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 max_irq = gic_data[gic_nr].max_irq;
> + 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(max_irq, 16); i++)
> + gic_data[gic_nr].saved_conf[i] =
> + readl(dist_base + GIC_DIST_CONFIG + i * 4);
> +
> + for (i = 0; i < DIV_ROUND_UP(max_irq, 4); i++)
> + gic_data[gic_nr].saved_pri[i] =
> + readl(dist_base + GIC_DIST_PRI + i * 4);
> +
> + for (i = 0; i < DIV_ROUND_UP(max_irq, 4); i++)
> + gic_data[gic_nr].saved_target[i] =
> + readl(dist_base + GIC_DIST_TARGET + i * 4);
> +
> + for (i = 0; i < DIV_ROUND_UP(max_irq, 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 max_irq;
> + unsigned int i;
> + void __iomem *dist_base;
> + void __iomem *cpu_base;
> +
> + if (gic_nr >= MAX_GIC_NR)
> + BUG();
> +
> + _gic_dist_init(gic_nr);
> +
> + max_irq = gic_data[gic_nr].max_irq;
> + dist_base = gic_data[gic_nr].dist_base;
> + cpu_base = gic_data[gic_nr].cpu_base;
> +
> + for (i = 0; i < DIV_ROUND_UP(max_irq, 16); i++)
> + writel(gic_data[gic_nr].saved_conf[i],
> + dist_base + GIC_DIST_CONFIG + i * 4);
> +
> + for (i = 0; i < DIV_ROUND_UP(max_irq, 4); i++)
> + writel(gic_data[gic_nr].saved_pri[i],
> + dist_base + GIC_DIST_PRI + i * 4);
> +
> + for (i = 0; i < DIV_ROUND_UP(max_irq, 4); i++)
> + writel(gic_data[gic_nr].saved_target[i],
> + dist_base + GIC_DIST_TARGET + i * 4);
> +
> + for (i = 0; i < DIV_ROUND_UP(max_irq, 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
> +
> +void __init gic_dist_init(unsigned int gic_nr, void __iomem *base,
> + unsigned int irq_start)
> +{
> + unsigned int max_irq;
> + unsigned int i;
> +
> + if (gic_nr >= MAX_GIC_NR)
> + BUG();
> +
> + gic_data[gic_nr].dist_base = base;
> + gic_data[gic_nr].irq_offset = (irq_start - 1) & ~31;
> +
> + max_irq = _gic_dist_init(gic_nr);
> + gic_data[gic_nr].max_irq = max_irq;
> +
> /*
> * Setup the Linux IRQ subsystem.
> */
> @@ -276,6 +380,14 @@ void __init gic_dist_init(unsigned int gic_nr, void __iomem *base,
> writel(1, base + GIC_DIST_CTRL);
> }
>
> +void gic_dist_exit(unsigned int gic_nr)
> +{
> + if (gic_nr >= MAX_GIC_NR)
> + BUG();
> +
> + _gic_dist_exit(gic_nr);
> +}
> +
> void __cpuinit gic_cpu_init(unsigned int gic_nr, void __iomem *base)
> {
> void __iomem *dist_base;
> @@ -306,6 +418,14 @@ void __cpuinit gic_cpu_init(unsigned int gic_nr, void __iomem *base)
> 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);
> +}
> +
> #ifdef CONFIG_SMP
> void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
> {
> diff --git a/arch/arm/include/asm/hardware/gic.h b/arch/arm/include/asm/hardware/gic.h
> index 7f34333..0a198b0 100644
> --- a/arch/arm/include/asm/hardware/gic.h
> +++ b/arch/arm/include/asm/hardware/gic.h
> @@ -34,7 +34,11 @@
>
> #ifndef __ASSEMBLY__
> void gic_dist_init(unsigned int gic_nr, void __iomem *base, unsigned int irq_start);
> +void gic_dist_save(unsigned int gic_nr);
> +void gic_dist_restore(unsigned int gic_nr);
> +void gic_dist_exit(unsigned int gic_nr);
> void gic_cpu_init(unsigned int gic_nr, void __iomem *base);
> +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);
> #endif
> --
> 1.7.3.1
>
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 20/21] ARM: tegra: Allow overriding arch_reset
2010-12-05 23:09 ` [PATCH 20/21] ARM: tegra: Allow overriding arch_reset Colin Cross
@ 2010-12-05 23:39 ` Russell King - ARM Linux
0 siblings, 0 replies; 27+ messages in thread
From: Russell King - ARM Linux @ 2010-12-05 23:39 UTC (permalink / raw)
To: linux-arm-kernel
On Sun, Dec 05, 2010 at 03:09:07PM -0800, Colin Cross wrote:
> diff --git a/arch/arm/mach-tegra/include/mach/system.h b/arch/arm/mach-tegra/include/mach/system.h
> index 84d5d46..70c95ac 100644
> --- a/arch/arm/mach-tegra/include/mach/system.h
> +++ b/arch/arm/mach-tegra/include/mach/system.h
> @@ -24,16 +24,30 @@
> #include <mach/hardware.h>
> #include <mach/iomap.h>
>
> +extern void (*tegra_reset)(char mode, const char *cmd);
> +
> static inline void arch_idle(void)
> {
> }
>
> -static inline void arch_reset(char mode, const char *cmd)
> +static inline void tegra_assert_system_reset(void)
> {
> void __iomem *reset = IO_ADDRESS(TEGRA_CLK_RESET_BASE + 0x04);
> - u32 reg = readl(reset);
> + u32 reg;
> +
> + reg = readl(reset);
> reg |= 0x04;
> writel(reg, reset);
> }
>
> +static inline void arch_reset(char mode, const char *cmd)
> +{
> + if (tegra_reset)
> + tegra_reset(mode, cmd);
> + else
> + tegra_assert_system_reset();
> +
> + do { } while (1);
> +}
> +
How about:
extern void (*arch_reset)(char mode, const char *cmd);
and then in a tegra private .c file:
static void tegra_assert_system_reset(char mode, const char *cmd)
{
...
}
void (*arch_reset)(char mode, const char *cmd) = tegra_assert_system_reset;
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 02/21] ARM: gic: Add functions to save and restore gic state
2010-12-05 23:30 ` Russell King - ARM Linux
@ 2010-12-05 23:52 ` Colin Cross
0 siblings, 0 replies; 27+ messages in thread
From: Colin Cross @ 2010-12-05 23:52 UTC (permalink / raw)
To: linux-arm-kernel
On Sun, Dec 5, 2010 at 3:30 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Sun, Dec 05, 2010 at 03:08:49PM -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
>
> This clashes with the GIC cleanups I've previously posted.
Yes, I mentioned that in the patch set overview. I wanted to get the
concept out there for review, I'll fix it up and repost it once your
patches are final before submitting anything to the patch system.
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 12/21] ARM: tegra: Add suspend and hotplug support
2010-12-05 23:08 ` [PATCH 12/21] ARM: tegra: Add suspend and hotplug support Colin Cross
@ 2010-12-06 0:01 ` Russell King - ARM Linux
0 siblings, 0 replies; 27+ messages in thread
From: Russell King - ARM Linux @ 2010-12-06 0:01 UTC (permalink / raw)
To: linux-arm-kernel
On Sun, Dec 05, 2010 at 03:08:59PM -0800, Colin Cross wrote:
> 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_SMP) += localtimer.o
This wants to be CONFIG_LOCAL_TIMERS, not CONFIG_SMP. Local timer support
is dependent on CONFIG_LOCAL_TIMERS being set.
> +#ifdef CONFIG_VFPv3
> + orr r2, r3, #0xF00000
> + mcr p15, 0, r2, c1, c0, 2 @ enable access to FPU
> + VFPFMRX r2, FPEXC
> + str r2, [r8, #CTX_FPEXC]
> + mov r1, #0x40000000 @ enable access to FPU
> + VFPFMXR FPEXC, r1
> + VFPFMRX r1, FPSCR
> + str r1, [r8, #CTX_FPSCR]
> + isb
> + add r9, r8, #CTX_VFP_REGS
> +
> + VFPFSTMIA r9, r12 @ save out (16 or 32)*8B of FPU registers
> + VFPFMXR FPEXC, r2
> + mrc p15, 0, r3, c1, c0, 2 @ restore original FPEXC/CPACR
> +#endif
There's already functions provided for saving/restoring VFP state. Please
use them rather than inventing a different method.
> + cps 0x1f @ SYS mode
> + add r9, r8, #CTX_SYS_SP
> + stmia r9, {sp,lr}
> +
> + cps 0x17 @ Abort mode
> + mrs r12, spsr
> + add r9, r8, #CTX_ABT_SPSR
> + stmia r9, {r12,sp,lr}
> +
> + cps 0x12 @ IRQ mode
> + mrs r12, spsr
> + add r9, r8, #CTX_IRQ_SPSR
> + stmia r9, {r12,sp,lr}
> +
> + cps 0x1b @ Undefined mode
> + mrs r12, spsr
> + add r9, r8, #CTX_UND_SPSR
> + stmia r9, {r12,sp,lr}
> +
> + mov r0, r8
> + add r1, r8, #CTX_FIQ_SPSR
> + cps 0x11 @ FIQ mode
> + mrs r7, spsr
> + stmia r1, {r7-r12,sp,lr}
Same old mistakes... Take a look at what we put in these registers,
and then go and look at cpu_init(), and ask whether you really need
to save all this.
> + add r9, r8, #CTS_CP14_BKPT_0
> + mrc p14, 0, r2, c0, c0, 4
> + mrc p14, 0, r3, c0, c0, 5
> + stmia r9!, {r2-r3} @ BRKPT_0
> + mrc p14, 0, r2, c0, c1, 4
> + mrc p14, 0, r3, c0, c1, 5
> + stmia r9!, {r2-r3} @ BRKPT_0
> + mrc p14, 0, r2, c0, c2, 4
> + mrc p14, 0, r3, c0, c2, 5
> + stmia r9!, {r2-r3} @ BRKPT_0
> + mrc p14, 0, r2, c0, c3, 4
> + mrc p14, 0, r3, c0, c3, 5
> + stmia r9!, {r2-r3} @ BRKPT_0
> + mrc p14, 0, r2, c0, c4, 4
> + mrc p14, 0, r3, c0, c4, 5
> + stmia r9!, {r2-r3} @ BRKPT_0
> + mrc p14, 0, r2, c0, c5, 4
> + mrc p14, 0, r3, c0, c5, 5
> + stmia r9!, {r2-r3} @ BRKPT_0
> +
> + add r9, r8, #CTS_CP14_WPT_0
> + mrc p14, 0, r2, c0, c0, 6
> + mrc p14, 0, r3, c0, c0, 7
> + stmia r9!, {r2-r3} @ WPT_0
> + mrc p14, 0, r2, c0, c1, 6
> + mrc p14, 0, r3, c0, c1, 7
> + stmia r9!, {r2-r3} @ WPT_0
> + mrc p14, 0, r2, c0, c2, 6
> + mrc p14, 0, r3, c0, c2, 7
> + stmia r9!, {r2-r3} @ WPT_0
> + mrc p14, 0, r2, c0, c3, 6
> + mrc p14, 0, r3, c0, c3, 7
> + stmia r9!, {r2-r3} @ WPT_0
Breakpoint and watchdog registers should be handled by the perf code.
If not, it needs to be added there, rather than inventing your own here.
> +#ifdef CONFIG_CACHE_L2X0
> + cpu_id r4
> + cmp r4, #0
> + bne __cortex_a9_save_clean_cache
> + mov32 r4, (TEGRA_ARM_PL310_BASE-IO_CPU_PHYS+IO_CPU_VIRT)
> + add r9, r8, #CTX_L2_CTRL
> + ldr r0, [r4, #L2X0_CTRL]
> + ldr r1, [r4, #L2X0_AUX_CTRL]
> + ldr r2, [r4, #L2X0_TAG_LATENCY_CTRL]
> + ldr r3, [r4, #L2X0_DATA_LATENCY_CTRL]
> + ldr r4, [r4, #L2X0_PREFETCH_CTRL]
> + stmia r9, {r0-r4}
> +#endif
PM support needs to be added to the L2x0 support code.
> +#ifdef CONFIG_HOTPLUG_CPU
> +static DEFINE_PER_CPU(struct completion, cpu_killed);
> +extern void tegra_hotplug_startup(void);
> +#endif
You don't need a per-CPU cpu_killed completion. Only one CPU can be
taken offline at a time - it's serialized by the cpu_add_remove_lock
mutex.
In any case, as a result of my cleanups, this is now in core code.
> +
> +static DECLARE_BITMAP(cpu_init_bits, CONFIG_NR_CPUS) __read_mostly;
> +const struct cpumask *const cpu_init_mask = to_cpumask(cpu_init_bits);
> +#define cpu_init_map (*(cpumask_t *)cpu_init_mask)
> +
> #define EVP_CPU_RESET_VECTOR \
> (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)
>
> void __cpuinit platform_secondary_init(unsigned int cpu)
> {
> trace_hardirqs_off();
This has also been moved to core code.
> -
> - /*
> - * if any interrupts are already enabled for the primary
> - * core (e.g. timer irq), then they will not have been enabled
> - * for us: do so
> - */
> gic_cpu_init(0, IO_ADDRESS(TEGRA_ARM_PERIF_BASE) + 0x100);
> -
> /*
> * Synchronise with the boot thread.
> */
> spin_lock(&boot_lock);
> +#ifdef CONFIG_HOTPLUG_CPU
> + cpu_set(cpu, cpu_init_map);
> + INIT_COMPLETION(per_cpu(cpu_killed, cpu));
> +#endif
> spin_unlock(&boot_lock);
> }
>
> @@ -70,27 +89,30 @@ 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 */
> +#ifdef CONFIG_HOTPLUG_CPU
> + if (cpumask_test_cpu(cpu, cpu_init_mask))
> + boot_vector = virt_to_phys(tegra_hotplug_startup);
> + else
> +#endif
> + boot_vector = virt_to_phys(tegra_secondary_startup);
> +
> + smp_wmb();
>
> - boot_vector = virt_to_phys(tegra_secondary_startup);
> 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;
> @@ -142,6 +164,12 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
> for (i = 0; i < max_cpus; i++)
> set_cpu_present(i, true);
>
> +#ifdef CONFIG_HOTPLUG_CPU
> + for_each_present_cpu(i) {
> + init_completion(&per_cpu(cpu_killed, i));
> + }
> +#endif
> +
> /*
> * Initialise the SCU if there are more than one CPU and let
> * them know where to start. Note that, on modern versions of
> @@ -154,3 +182,71 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
> scu_enable(scu_base);
> }
> }
> +
> +#ifdef CONFIG_HOTPLUG_CPU
> +
> +extern void vfp_sync_state(struct thread_info *thread);
Looks unused, and in any case is not necessary. With SMP, we always
save the VFP state when switching away from a task, and that includes
when we switch to the idle task. We can only go offline from the idle
task, so we've already saved the VFP state - specific to the last running
thread which used VFP - safely away.
> +
> +void __cpuinit secondary_start_kernel(void);
> +
> +int platform_cpu_kill(unsigned int cpu)
> +{
> + unsigned int reg;
> + int e;
> +
> + e = wait_for_completion_timeout(&per_cpu(cpu_killed, cpu), 100);
> + printk(KERN_NOTICE "CPU%u: %s shutdown\n", cpu, (e) ? "clean":"forced");
> +
> + if (e) {
> + do {
> + reg = readl(CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET);
> + cpu_relax();
> + } while (!(reg & (1<<cpu)));
> + } else {
> + writel(0x1111<<cpu, CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET);
> + /* put flow controller in WAIT_EVENT mode */
> + writel(2<<29, IO_ADDRESS(TEGRA_FLOW_CTRL_BASE)+0x14 + 0x8*(cpu-1));
> + }
> + 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 e;
> +}
> +
> +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();
> + complete(&per_cpu(cpu_killed, cpu));
> + flush_cache_all();
> + barrier();
> + __cortex_a9_save(0);
> +
> + /* return happens from __cortex_a9_restore */
> + barrier();
> + writel(smp_processor_id(), EVP_CPU_RESET_VECTOR);
Hopefully it doesn't return here - if you can take the core offline
properly, you should restore it by effectively rebooting it.
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +
> +#include <asm/io.h>
linux/io.h (and any other places in this patch set which also have asm/io.h
includes.)
> +
> +#include <mach/gpio.h>
linux/gpio.h (and any other places which have mach/gpio.h)
> + unsigned int flags = PMD_TYPE_SECT | PMD_SECT_AP_WRITE |
> + PMD_SECT_WBWA | PMD_SECT_S;
> +
> + tegra_pgd = pgd_alloc(&init_mm);
> + if (!tegra_pgd)
> + return -ENOMEM;
> +
> + for (i=0; i<ARRAY_SIZE(addr_p); i++) {
> + unsigned long v = addr_v[i];
> + pmd = pmd_offset(tegra_pgd + pgd_index(v), v);
> + *pmd = __pmd((addr_p[i] & PGDIR_MASK) | flags);
> + flush_pmd_entry(pmd);
> + outer_clean_range(__pa(pmd), __pa(pmd + 1));
> + }
> +
> + tegra_pgd_phys = virt_to_phys(tegra_pgd);
> + __cpuc_flush_dcache_area(&tegra_pgd_phys,
> + sizeof(tegra_pgd_phys));
> + outer_clean_range(__pa(&tegra_pgd_phys),
> + __pa(&tegra_pgd_phys+1));
> +
> + __cpuc_flush_dcache_area(&tegra_context_area,
> + sizeof(tegra_context_area));
> + outer_clean_range(__pa(&tegra_context_area),
> + __pa(&tegra_context_area+1));
Eww, no. We should have this as a separate API. It's also not
checkpatch-clean.
> +
> + return 0;
> +}
> +
> +
> +
> +/*
> + * suspend_cpu_complex
> + *
> + * disable periodic IRQs used for DVFS to prevent suspend wakeups
> + * disable coresight debug interface
> + *
> + *
> + */
> +static noinline void restore_cpu_complex(void)
Comment doesn't match function.
> +{
> + 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);
> + get_irq_chip(IRQ_LOCALTIMER)->unmask(IRQ_LOCALTIMER);
> +
> + enable_irq(INT_SYS_STATS_MON);
> +}
> +
> +static noinline void suspend_cpu_complex(void)
> +{
> + unsigned int reg;
> + int i;
> +
> + disable_irq(INT_SYS_STATS_MON);
> +
> + /* 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);
> + local_timer_stop();
> +#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();
> + /* structure is written by reset code, so the L2 lines
> + * must be invalidated */
> + outer_flush_range(__pa(&tegra_sctx),__pa(&tegra_sctx+1));
> + barrier();
> +
> + __cortex_a9_save(mode);
> + /* return from __cortex_a9_restore */
> + barrier();
> + restore_cpu_complex();
> +
> + 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 = NULL;
> +static unsigned int iram_save_size = 0;
> +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);
> + 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)
> +{
> + struct irq_desc *desc;
> + void __iomem *mc = IO_ADDRESS(TEGRA_MC_BASE);
> + unsigned long flags;
> + u32 mc_data[2];
> + int irq;
> + 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);
> + }
> +
> + for_each_irq_desc(irq, desc) {
> + if ((desc->status & IRQ_WAKEUP) &&
> + (desc->status & IRQ_SUSPENDED)) {
> + get_irq_chip(irq)->unmask(irq);
> + }
> + }
> +
> + 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();
> +
> + for_each_irq_desc(irq, desc) {
> + if ((desc->status & IRQ_WAKEUP) &&
> + (desc->status & IRQ_SUSPENDED)) {
> + get_irq_chip(irq)->mask(irq);
> + }
> + }
> +
> + /* 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 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
> +
> +static unsigned long lp0_vec_orig_start = 0;
> +static unsigned long lp0_vec_orig_size = 0;
> +
> +static int __init tegra_lp0_vec_arg(char *options)
> +{
> + char *p = options;
> +
> + lp0_vec_orig_size = memparse(p, &p);
> + if (*p == '@')
> + lp0_vec_orig_start = memparse(p+1, &p);
> +
> + return 0;
> +}
> +__setup("lp0_vec=", tegra_lp0_vec_arg);
> +
> +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 &&
> + lp0_vec_orig_size && lp0_vec_orig_start) {
> + unsigned char *reloc_lp0;
> + unsigned long tmp;
> + void __iomem *orig;
> + reloc_lp0 = kmalloc(lp0_vec_orig_size+L1_CACHE_BYTES-1,
> + GFP_KERNEL);
> + WARN_ON(!reloc_lp0);
> + if (!reloc_lp0)
> + goto out;
> +
> + orig = ioremap(lp0_vec_orig_start, lp0_vec_orig_size);
> + WARN_ON(!orig);
> + if (!orig) {
> + kfree(reloc_lp0);
> + goto out;
> + }
> + tmp = (unsigned long) reloc_lp0;
> + tmp = (tmp + L1_CACHE_BYTES - 1) & ~(L1_CACHE_BYTES-1);
> + reloc_lp0 = (unsigned char *)tmp;
> + memcpy(reloc_lp0, orig, lp0_vec_orig_size);
> + iounmap(orig);
> + wb0_restore = virt_to_phys(reloc_lp0);
> + }
> +out:
> + if (plat->suspend_mode == TEGRA_SUSPEND_LP0 && !wb0_restore) {
> + 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 *)¤t_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
>
>
> _______________________________________________
> 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] 27+ messages in thread
* [PATCH 14/21] ARM: tegra: irq: Add debugfs file to show wake irqs
2010-12-05 23:09 ` [PATCH 14/21] ARM: tegra: irq: Add debugfs file to show wake irqs Colin Cross
@ 2010-12-06 21:07 ` Stephen Boyd
0 siblings, 0 replies; 27+ messages in thread
From: Stephen Boyd @ 2010-12-06 21:07 UTC (permalink / raw)
To: linux-arm-kernel
On 12/05/2010 03:09 PM, Colin Cross wrote:
> +static int __init tegra_irq_debug_init(void)
> +{
> + struct dentry *d;
> +
> + d = debugfs_create_file("wake_irq", 0755, NULL, NULL,
> + &tegra_wake_irq_debug_fops);
Why not use S_IRUGO? And why is this file executable?
--
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] 27+ messages in thread
end of thread, other threads:[~2010-12-06 21:07 UTC | newest]
Thread overview: 27+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-12-05 23:08 [PATCH 00/21] Updates for Tegra support in 2.6.38 Colin Cross
2010-12-05 23:08 ` [PATCH 01/21] ARM: tegra: irq: Rename gic pointers to avoid conflicts Colin Cross
2010-12-05 23:08 ` [PATCH 02/21] ARM: gic: Add functions to save and restore gic state Colin Cross
2010-12-05 23:30 ` Russell King - ARM Linux
2010-12-05 23:52 ` Colin Cross
2010-12-05 23:08 ` [PATCH 03/21] ARM: gic: Export irq chip functions Colin Cross
2010-12-05 23:08 ` [PATCH 04/21] ARM: tegra: Centralize macros to define debug uart base Colin Cross
2010-12-05 23:08 ` [PATCH 05/21] ARM: tegra: Add api to control internal powergating Colin Cross
2010-12-05 23:08 ` [PATCH 06/21] ARM: tegra: irqs: Update irq list Colin Cross
2010-12-05 23:08 ` [PATCH 07/21] ARM: tegra: Add prototypes for subsystem suspend functions Colin Cross
2010-12-05 23:08 ` [PATCH 08/21] ARM: tegra: clock: Suspend fixes, and add new clocks Colin Cross
2010-12-05 23:08 ` [PATCH 09/21] ARM: tegra: pinmux: Add missing drive pingroups and fix suspend Colin Cross
2010-12-05 23:08 ` [PATCH 10/21] ARM: tegra: timer: Add idle and suspend support to timers Colin Cross
2010-12-05 23:08 ` [PATCH 11/21] ARM: tegra: irq: Add support for suspend wake sources Colin Cross
2010-12-05 23:08 ` [PATCH 12/21] ARM: tegra: Add suspend and hotplug support Colin Cross
2010-12-06 0:01 ` Russell King - ARM Linux
2010-12-05 23:09 ` [PATCH 13/21] ARM: tegra: irq: Add set_wake and set_type support for suspend Colin Cross
2010-12-05 23:09 ` [PATCH 14/21] ARM: tegra: irq: Add debugfs file to show wake irqs Colin Cross
2010-12-06 21:07 ` Stephen Boyd
2010-12-05 23:09 ` [PATCH 15/21] ARM: tegra: irq: Implement retrigger Colin Cross
2010-12-05 23:09 ` [PATCH 16/21] ARM: tegra: gpio: Add support for waking from suspend Colin Cross
2010-12-05 23:09 ` [PATCH 17/21] ARM: tegra: add CPU_IDLE driver Colin Cross
2010-12-05 23:09 ` [PATCH 18/21] ARM: tegra: iomap: Add missing devices, fix use of SZ_8, SZ_64 Colin Cross
2010-12-05 23:09 ` [PATCH 19/21] ARM: tegra: cpufreq: Disable cpufreq during suspend Colin Cross
2010-12-05 23:09 ` [PATCH 20/21] ARM: tegra: Allow overriding arch_reset Colin Cross
2010-12-05 23:39 ` Russell King - ARM Linux
2010-12-05 23:09 ` [PATCH 21/21] ARM: tegra: dma: Fix critical data corruption bugs Colin Cross
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).