Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 09/17] omap4: pm: Add WakeupGen save/restore support
From: Santosh Shilimkar @ 2011-02-19 10:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1298112158-28469-1-git-send-email-santosh.shilimkar@ti.com>

WakeupGen is lost only when device hits off-mode. Though the register
context is retained in MPUSS OFF/OSWR state, hardware recommondation is
to save/restore WakeupGen along with GIC to have consistent interrupt
state at both the blocks. The ROM code restore mechinism also does
restore of wakeupgen on mpu OFF/OSWR

Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
---
 arch/arm/mach-omap2/include/mach/omap-wakeupgen.h |    1 +
 arch/arm/mach-omap2/omap-wakeupgen.c              |   74 +++++++++++++++++++++
 arch/arm/mach-omap2/omap4-mpuss-lowpower.c        |    2 +
 arch/arm/mach-omap2/omap4-sar-layout.h            |   11 +++
 4 files changed, 88 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-omap2/include/mach/omap-wakeupgen.h b/arch/arm/mach-omap2/include/mach/omap-wakeupgen.h
index f10d106..66f31c3 100644
--- a/arch/arm/mach-omap2/include/mach/omap-wakeupgen.h
+++ b/arch/arm/mach-omap2/include/mach/omap-wakeupgen.h
@@ -37,4 +37,5 @@
 
 extern int __init omap_wakeupgen_init(void);
 extern void omap_wakeupgen_irqmask_all(unsigned int cpu, unsigned int set);
+extern void omap_wakeupgen_save(void);
 #endif
diff --git a/arch/arm/mach-omap2/omap-wakeupgen.c b/arch/arm/mach-omap2/omap-wakeupgen.c
index e26a0ed..0f0a5ed 100644
--- a/arch/arm/mach-omap2/omap-wakeupgen.c
+++ b/arch/arm/mach-omap2/omap-wakeupgen.c
@@ -24,6 +24,9 @@
 #include <asm/hardware/gic.h>
 
 #include <mach/omap-wakeupgen.h>
+#include <mach/omap4-common.h>
+
+#include "omap4-sar-layout.h"
 
 #define NR_BANKS		4
 #define MAX_IRQS		128
@@ -54,6 +57,11 @@ static inline void cpu_writel(u32 val, u8 idx, u32 cpu)
 				(cpu * CPU_ENA_OFFSET) + (idx * 4));
 }
 
+static inline void sar_writel(u32 val, u32 offset, u8 idx)
+{
+	__raw_writel(val, sar_ram_base + offset + (idx * 4));
+}
+
 static void _wakeupgen_set_all(unsigned int cpu, unsigned int reg)
 {
 	u8 i;
@@ -211,3 +219,69 @@ int __init omap_wakeupgen_init(void)
 
 	return 0;
 }
+
+/**
+ * omap_wakeupgen_save() - WakeupGen context save function
+ *
+ * Save WakewupGen context in SAR BANK3. Restore is done by ROM code.
+ * WakeupGen is lost only when DEVICE hits OFF. Though the register
+ * context is retained in MPU OFF/OSWR state, hw recommondation is to
+ * save/restore WakeupGen along with GIC to have consistent interrupt
+ * state at both the blocks.
+ *
+ * During normal operation, WakeupGen delivers external interrupts
+ * directly to the GIC. When the CPU asserts StandbyWFI, indicating
+ * it wants to enter lowpower state, the Standby Controller checks
+ * with the WakeupGen unit using the idlereq/idleack handshake to make
+ * sure there is no incoming interrupts.
+ */
+
+void omap_wakeupgen_save(void)
+{
+	u8 i;
+	u32 val;
+
+	if (omap_rev() == OMAP4430_REV_ES1_0)
+		return;
+
+	for (i = 0; i < NR_BANKS; i++) {
+		/* Save the CPUx interrupt mask for IRQ 0 to 127 */
+		val = cpu_readl(i, 0);
+		sar_writel(val, WAKEUPGENENB_OFFSET_CPU0, i);
+		val = cpu_readl(i, 1);
+		sar_writel(val, WAKEUPGENENB_OFFSET_CPU1, i);
+
+		/*
+		 * Disable the secure interrupts for CPUx. The restore
+		 * code blindly restores secure and non-secure interrupt
+		 * masks from SAR RAM. Secure interrupts are not suppose
+		 * to be enabled from HLOS. So overwrite the SAR location
+		 * so that the secure interrupt remains disabled.
+		 */
+		sar_writel(0x0, WAKEUPGENENB_SECURE_OFFSET_CPU0, i);
+		sar_writel(0x0, WAKEUPGENENB_SECURE_OFFSET_CPU1, i);
+	}
+
+	/* Save AuxBoot* registers */
+	val = __raw_readl(wakeupgen_base + OMAP_AUX_CORE_BOOT_0);
+	__raw_writel(val, sar_ram_base + AUXCOREBOOT0_OFFSET);
+	val = __raw_readl(wakeupgen_base + OMAP_AUX_CORE_BOOT_0);
+	__raw_writel(val, sar_ram_base + AUXCOREBOOT1_OFFSET);
+
+	/* Save SyncReq generation logic */
+	val = __raw_readl(wakeupgen_base + OMAP_AUX_CORE_BOOT_0);
+	__raw_writel(val, sar_ram_base + AUXCOREBOOT0_OFFSET);
+	val = __raw_readl(wakeupgen_base + OMAP_AUX_CORE_BOOT_0);
+	__raw_writel(val, sar_ram_base + AUXCOREBOOT1_OFFSET);
+
+	/* Save SyncReq generation logic */
+	val = __raw_readl(wakeupgen_base + OMAP_PTMSYNCREQ_MASK);
+	__raw_writel(val, sar_ram_base + PTMSYNCREQ_MASK_OFFSET);
+	val = __raw_readl(wakeupgen_base + OMAP_PTMSYNCREQ_EN);
+	__raw_writel(val, sar_ram_base + PTMSYNCREQ_EN_OFFSET);
+
+	/* Set the Backup Bit Mask status */
+	val = __raw_readl(sar_ram_base + SAR_BACKUP_STATUS_OFFSET);
+	val |= SAR_BACKUP_STATUS_WAKEUPGEN;
+	__raw_writel(val, sar_ram_base + SAR_BACKUP_STATUS_OFFSET);
+}
diff --git a/arch/arm/mach-omap2/omap4-mpuss-lowpower.c b/arch/arm/mach-omap2/omap4-mpuss-lowpower.c
index 4140251..a30f19b 100644
--- a/arch/arm/mach-omap2/omap4-mpuss-lowpower.c
+++ b/arch/arm/mach-omap2/omap4-mpuss-lowpower.c
@@ -52,6 +52,7 @@
 
 #include <plat/omap44xx.h>
 #include <mach/omap4-common.h>
+#include <mach/omap-wakeupgen.h>
 
 #include "omap4-sar-layout.h"
 #include "pm.h"
@@ -290,6 +291,7 @@ int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state)
 	 * GIC lost during MPU OFF and OSWR
 	 */
 	if (pwrdm_read_next_pwrst(mpuss_pd) == PWRDM_POWER_OFF) {
+		omap_wakeupgen_save();
 		gic_save_context();
 		save_state = 3;
 	}
diff --git a/arch/arm/mach-omap2/omap4-sar-layout.h b/arch/arm/mach-omap2/omap4-sar-layout.h
index 0a19f49..5a815c4 100644
--- a/arch/arm/mach-omap2/omap4-sar-layout.h
+++ b/arch/arm/mach-omap2/omap4-sar-layout.h
@@ -52,6 +52,17 @@
 
 #ifndef __ASSEMBLER__
 
+/* WakeUpGen save restore offset from OMAP44XX_SAR_RAM_BASE */
+#define WAKEUPGENENB_OFFSET_CPU0		(SAR_BANK3_OFFSET + 0x684)
+#define WAKEUPGENENB_SECURE_OFFSET_CPU0		(SAR_BANK3_OFFSET + 0x694)
+#define WAKEUPGENENB_OFFSET_CPU1		(SAR_BANK3_OFFSET + 0x6a4)
+#define WAKEUPGENENB_SECURE_OFFSET_CPU1		(SAR_BANK3_OFFSET + 0x6b4)
+#define AUXCOREBOOT0_OFFSET			(SAR_BANK3_OFFSET + 0x6c4)
+#define AUXCOREBOOT1_OFFSET			(SAR_BANK3_OFFSET + 0x6c8)
+#define PTMSYNCREQ_MASK_OFFSET			(SAR_BANK3_OFFSET + 0x6cc)
+#define PTMSYNCREQ_EN_OFFSET			(SAR_BANK3_OFFSET + 0x6d0)
+#define SAR_BACKUP_STATUS_WAKEUPGEN		0x10
+
 extern void __iomem *sar_ram_base;
 
 #endif
-- 
1.6.0.4

^ permalink raw reply related

* [PATCH 08/17] omap4: pm: Add GIC save/restore support
From: Santosh Shilimkar @ 2011-02-19 10:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1298112158-28469-1-git-send-email-santosh.shilimkar@ti.com>

On OMAP4 when attempting MPU off-mode or OSWR, the GIC context is
lost. This patch adds GIC context save and restore support.

The context save is done by software and restore is done by
ROM code from predefined SAR locations where the context suppose
to be saved. Refer to ROM code specs for the GIC layout details.

Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
---
 arch/arm/mach-omap2/omap-hotplug.c         |    4 +
 arch/arm/mach-omap2/omap4-mpuss-lowpower.c |  176 +++++++++++++++++++++++++++-
 arch/arm/mach-omap2/omap4-sar-layout.h     |   20 +++
 3 files changed, 199 insertions(+), 1 deletions(-)

diff --git a/arch/arm/mach-omap2/omap-hotplug.c b/arch/arm/mach-omap2/omap-hotplug.c
index cf4ab15..deab389 100644
--- a/arch/arm/mach-omap2/omap-hotplug.c
+++ b/arch/arm/mach-omap2/omap-hotplug.c
@@ -19,6 +19,8 @@
 #include <linux/smp.h>
 
 #include <asm/cacheflush.h>
+#include <asm/hardware/gic.h>
+
 #include <mach/omap4-common.h>
 #include <mach/omap-wakeupgen.h>
 
@@ -58,6 +60,7 @@ void platform_cpu_die(unsigned int cpu)
 		 * clear all interrupt wakeup sources
 		 */
 		omap_wakeupgen_irqmask_all(cpu, 1);
+		gic_secondary_set(0, true);
 		omap4_enter_lowpower(cpu, PWRDM_POWER_OFF);
 		this_cpu = hard_smp_processor_id();
 		if (omap_read_auxcoreboot0() == this_cpu) {
@@ -65,6 +68,7 @@ void platform_cpu_die(unsigned int cpu)
 			 * OK, proper wakeup, we're done
 			 */
 			omap_wakeupgen_irqmask_all(this_cpu, 0);
+			gic_secondary_set(0, false);
 
 			/* Restore clockdomain to hardware supervised */
 			clkdm_allow_idle(cpu1_clkdm);
diff --git a/arch/arm/mach-omap2/omap4-mpuss-lowpower.c b/arch/arm/mach-omap2/omap4-mpuss-lowpower.c
index c0f358d..4140251 100644
--- a/arch/arm/mach-omap2/omap4-mpuss-lowpower.c
+++ b/arch/arm/mach-omap2/omap4-mpuss-lowpower.c
@@ -47,6 +47,8 @@
 #include <asm/tlbflush.h>
 #include <asm/smp_scu.h>
 #include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/hardware/gic.h>
 
 #include <plat/omap44xx.h>
 #include <mach/omap4-common.h>
@@ -59,6 +61,19 @@
 
 #define CPU0_ID				0x0
 #define CPU1_ID				0x1
+#define GIC_MASK_ALL			0x0
+#define GIC_ISR_NON_SECURE		0xffffffff
+#define SPI_ENABLE_SET_OFFSET		0x04
+#define PPI_PRI_OFFSET			0x1c
+#define SPI_PRI_OFFSET			0x20
+#define SPI_TARGET_OFFSET		0x20
+#define SPI_CONFIG_OFFSET		0x20
+
+/* GIC save SAR bank base */
+static struct powerdomain *mpuss_pd;
+
+/* Variables to store maximum spi(Shared Peripheral Interrupts) registers. */
+static u32 max_spi_irq, max_spi_reg;
 
 struct omap4_cpu_pm_info {
 	struct powerdomain *pwrdm;
@@ -67,6 +82,17 @@ struct omap4_cpu_pm_info {
 
 static DEFINE_PER_CPU(struct omap4_cpu_pm_info, omap4_pm_info);
 
+/* Helper functions */
+static inline void sar_writel(u32 val, u32 offset, u8 idx)
+{
+	__raw_writel(val, sar_ram_base + offset + 4 * idx);
+}
+
+static inline u32 gic_readl(u32 offset, u8 idx)
+{
+	return __raw_readl(gic_dist_base_addr + offset + 4 * idx);
+}
+
 /*
  * Set the CPUx powerdomain's previous power state
  */
@@ -124,6 +150,85 @@ static void scu_pwrst_prepare(unsigned int cpu_id, unsigned int cpu_state)
 }
 
 /*
+ * Save GIC context in SAR RAM. Restore is done by ROM code
+ * GIC is lost only when MPU hits OSWR or OFF. It consists
+ * of a distributor and a per-CPU interface module. The GIC
+ * save restore is optimised to save only necessary registers.
+ */
+static void gic_save_context(void)
+{
+	u8 i;
+	u32 val;
+
+	/*
+	 * Interrupt Clear Enable registers are inverse of set enable
+	 * and hence not needed to be saved. ROM code programs it
+	 * based on Set Enable register values.
+	 */
+
+	/* Save CPU 0 Interrupt Set Enable register */
+	val = gic_readl(GIC_DIST_ENABLE_SET, 0);
+	sar_writel(val, ICDISER_CPU0_OFFSET, 0);
+
+	/* Disable interrupts on CPU1 */
+	sar_writel(GIC_MASK_ALL, ICDISER_CPU1_OFFSET, 0);
+
+	/* Save all SPI Set Enable register */
+	for (i = 0; i < max_spi_reg; i++) {
+		val = gic_readl(GIC_DIST_ENABLE_SET + SPI_ENABLE_SET_OFFSET, i);
+		sar_writel(val, ICDISER_SPI_OFFSET, i);
+	}
+
+	/*
+	 * Interrupt Priority Registers
+	 * Secure sw accesses, last 5 bits of the 8 bits (bit[7:3] are used)
+	 * Non-Secure sw accesses, last 4 bits (i.e. bits[7:4] are used)
+	 * But the Secure Bits[7:3] are shifted by 1 in Non-Secure access.
+	 * Secure (bits[7:3] << 1)== Non Secure bits[7:4]
+	 * Hence right shift the value by 1 while saving the priority
+	 */
+
+	/* Save SGI priority registers (Software Generated Interrupt) */
+	for (i = 0; i < 4; i++) {
+		val = gic_readl(GIC_DIST_PRI, i);
+
+		/* Save the priority bits of the Interrupts */
+		sar_writel(val >> 0x1, ICDIPR_SFI_CPU0_OFFSET, i);
+
+		/* Disable the interrupts on CPU1 */
+		sar_writel(GIC_MASK_ALL, ICDIPR_SFI_CPU1_OFFSET, i);
+	}
+
+	/* Save PPI priority registers (Private Peripheral Intterupts) */
+	val = gic_readl(GIC_DIST_PRI + PPI_PRI_OFFSET, 0);
+	sar_writel(val >> 0x1, ICDIPR_PPI_CPU0_OFFSET, 0);
+	sar_writel(GIC_MASK_ALL, ICDIPR_PPI_CPU1_OFFSET, 0);
+
+	/* SPI priority registers - 4 interrupts/register */
+	for (i = 0; i < (max_spi_irq / 4); i++) {
+		val = gic_readl((GIC_DIST_PRI + SPI_PRI_OFFSET), i);
+		sar_writel(val >> 0x1, ICDIPR_SPI_OFFSET, i);
+	}
+
+	/* SPI Interrupt Target registers - 4 interrupts/register */
+	for (i = 0; i < (max_spi_irq / 4); i++) {
+		val = gic_readl((GIC_DIST_TARGET + SPI_TARGET_OFFSET), i);
+		sar_writel(val, ICDIPTR_SPI_OFFSET, i);
+	}
+
+	/* SPI Interrupt Congigeration eegisters- 16 interrupts/register */
+	for (i = 0; i < (max_spi_irq / 16); i++) {
+		val = gic_readl((GIC_DIST_CONFIG + SPI_CONFIG_OFFSET), i);
+		sar_writel(val, ICDICFR_OFFSET, i);
+	}
+
+	/* Set the Backup Bit Mask status for GIC */
+	val = __raw_readl(sar_ram_base + SAR_BACKUP_STATUS_OFFSET);
+	val |= (SAR_BACKUP_STATUS_GIC_CPU0 | SAR_BACKUP_STATUS_GIC_CPU1);
+	__raw_writel(val, sar_ram_base + SAR_BACKUP_STATUS_OFFSET);
+}
+
+/*
  * OMAP4 MPUSS Low Power Entry Function
  *
  * The purpose of this function is to manage low power programming
@@ -131,11 +236,25 @@ static void scu_pwrst_prepare(unsigned int cpu_id, unsigned int cpu_state)
  * Paramenters:
  *	cpu : CPU ID
  *	power_state: Targetted Low power state.
+ *
+ * MPUSS Low power states
+ * The basic rule is that the MPUSS power domain must be@the higher or
+ * equal power state (state that consume more power) than the higher of the
+ * two CPUs. For example, it is illegal for system power to be OFF, while
+ * the power of one or both of the CPU is DORMANT. When an illegal state is
+ * entered, then the hardware behavior is unpredictable.
+ *
+ * MPUSS state for the context save
+ * save_state =
+ *	0 - Nothing lost and no need to save: MPUSS INACTIVE
+ *	1 - CPUx L1 and logic lost: MPUSS CSWR
+ *	2 - CPUx L1 and logic lost + GIC lost: MPUSS OSWR
+ *	3 - CPUx L1 and logic lost + GIC + L2 lost: MPUSS OFF
  */
 int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state)
 {
 	unsigned int save_state = 0;
-	unsigned int wakeup_cpu = hard_smp_processor_id();
+	unsigned int wakeup_cpu;
 
 	if ((cpu > NR_CPUS) || (omap_rev() == OMAP4430_REV_ES1_0))
 		goto ret;
@@ -159,6 +278,23 @@ int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state)
 		goto ret;
 	}
 
+	/*
+	 * MPUSS book keeping should be executed by master
+	 * CPU only which is also the last CPU to go down.
+	 */
+	if (cpu)
+		goto cpu_prepare;
+
+	/*
+	 * Check MPUSS next state and save GIC if needed
+	 * GIC lost during MPU OFF and OSWR
+	 */
+	if (pwrdm_read_next_pwrst(mpuss_pd) == PWRDM_POWER_OFF) {
+		gic_save_context();
+		save_state = 3;
+	}
+
+cpu_prepare:
 	clear_cpu_prev_pwrst(cpu);
 	set_cpu_next_pwrst(cpu, power_state);
 	scu_pwrst_prepare(cpu, power_state);
@@ -179,6 +315,19 @@ int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state)
 	wakeup_cpu = hard_smp_processor_id();
 	set_cpu_next_pwrst(wakeup_cpu, PWRDM_POWER_ON);
 
+	/* If !master cpu return to hotplug-path */
+	if (wakeup_cpu)
+		goto ret;
+
+	/* Check MPUSS previous power state and enable GIC if needed */
+	if (pwrdm_read_prev_pwrst(mpuss_pd) == PWRDM_POWER_OFF) {
+		/* Clear SAR BACKUP status */
+		__raw_writel(0x0, sar_ram_base + SAR_BACKUP_STATUS_OFFSET);
+		/* Enable GIC distributor and inteface on CPU0*/
+		gic_secondary_set(CPU0_ID, 1);
+		gic_dist_set(CPU0_ID, 1);
+	}
+
 ret:
 	return 0;
 }
@@ -189,6 +338,7 @@ ret:
 int __init omap4_mpuss_init(void)
 {
 	struct omap4_cpu_pm_info *pm_info;
+	u8 i;
 
 	if (omap_rev() == OMAP4430_REV_ES1_0) {
 		WARN(1, "Power Management not supported on OMAP4430 ES1.0\n");
@@ -234,6 +384,30 @@ int __init omap4_mpuss_init(void)
 	__raw_writel(virt_to_phys(omap4_cpu_resume),
 			sar_ram_base + CPU0_WAKEUP_NS_PA_ADDR_OFFSET);
 
+	mpuss_pd = pwrdm_lookup("mpu_pwrdm");
+	if (!mpuss_pd) {
+		pr_err("Failed to get lookup for MPUSS pwrdm\n");
+		return -ENODEV;
+	}
+
+	/*
+	 * Find out how many interrupts are supported.
+	 * OMAP4 supports max of 128 SPIs where as GIC can support
+	 * up to 1020 interrupt sources.
+	 */
+	max_spi_reg = __raw_readl(gic_dist_base_addr + GIC_DIST_CTR) & 0x1f;
+	max_spi_irq = max_spi_reg * 32;
+
+	/*
+	 * Mark the PPI and SPI interrupts as non-secure.
+	 * program the SAR locations for interrupt security registers to
+	 * reflect the same.
+	 */
+	sar_writel(GIC_ISR_NON_SECURE, ICDISR_CPU0_OFFSET, 0);
+	sar_writel(GIC_ISR_NON_SECURE, ICDISR_CPU1_OFFSET, 0);
+	for (i = 0; i < max_spi_reg; i++)
+		sar_writel(GIC_ISR_NON_SECURE, ICDISR_SPI_OFFSET, i);
+
 	return 0;
 }
 
diff --git a/arch/arm/mach-omap2/omap4-sar-layout.h b/arch/arm/mach-omap2/omap4-sar-layout.h
index c4251db..0a19f49 100644
--- a/arch/arm/mach-omap2/omap4-sar-layout.h
+++ b/arch/arm/mach-omap2/omap4-sar-layout.h
@@ -30,6 +30,26 @@
 #define CPU0_WAKEUP_NS_PA_ADDR_OFFSET		0xa04
 #define CPU1_WAKEUP_NS_PA_ADDR_OFFSET		0xa08
 
+ /* GIC save restore offset from SAR_BANK3 */
+#define SAR_BACKUP_STATUS_OFFSET		(SAR_BANK3_OFFSET + 0x500)
+#define SAR_SECURE_RAM_SIZE_OFFSET		(SAR_BANK3_OFFSET + 0x504)
+#define SAR_SECRAM_SAVED_AT_OFFSET		(SAR_BANK3_OFFSET + 0x508)
+#define ICDISR_CPU0_OFFSET			(SAR_BANK3_OFFSET + 0x50c)
+#define ICDISR_CPU1_OFFSET			(SAR_BANK3_OFFSET + 0x510)
+#define ICDISR_SPI_OFFSET			(SAR_BANK3_OFFSET + 0x514)
+#define ICDISER_CPU0_OFFSET			(SAR_BANK3_OFFSET + 0x524)
+#define ICDISER_CPU1_OFFSET			(SAR_BANK3_OFFSET + 0x528)
+#define ICDISER_SPI_OFFSET			(SAR_BANK3_OFFSET + 0x52c)
+#define ICDIPR_SFI_CPU0_OFFSET			(SAR_BANK3_OFFSET + 0x53c)
+#define ICDIPR_PPI_CPU0_OFFSET			(SAR_BANK3_OFFSET + 0x54c)
+#define ICDIPR_SFI_CPU1_OFFSET			(SAR_BANK3_OFFSET + 0x550)
+#define ICDIPR_PPI_CPU1_OFFSET			(SAR_BANK3_OFFSET + 0x560)
+#define ICDIPR_SPI_OFFSET			(SAR_BANK3_OFFSET + 0x564)
+#define ICDIPTR_SPI_OFFSET			(SAR_BANK3_OFFSET + 0x5e4)
+#define ICDICFR_OFFSET				(SAR_BANK3_OFFSET + 0x664)
+#define SAR_BACKUP_STATUS_GIC_CPU0		0x1
+#define SAR_BACKUP_STATUS_GIC_CPU1		0x2
+
 #ifndef __ASSEMBLER__
 
 extern void __iomem *sar_ram_base;
-- 
1.6.0.4

^ permalink raw reply related

* [PATCH 07/17] omap4: pm: CPU1 wakeup workaround form Low power modes
From: Santosh Shilimkar @ 2011-02-19 10:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1298112158-28469-1-git-send-email-santosh.shilimkar@ti.com>

The SGI(Software Generated Interrupts) are not wakeup capable from
low power states. This is known limitation on OMAP4 and needs to be
worked around by using software forced clockdomain wake-up. CPU0 forces
the CPU1 clockdomain to software force wakeup. After the wakeup, CPU1
restores its clockdomain hardware supervised mode.

More details can be found in OMAP4430 TRM - Version J
Section :
	4.3.4.2 Power States of CPU0 and CPU1

Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
---
 arch/arm/mach-omap2/omap-hotplug.c |    9 +++++++++
 arch/arm/mach-omap2/omap-smp.c     |   27 ++++++++++++++++++++++++++-
 2 files changed, 35 insertions(+), 1 deletions(-)

diff --git a/arch/arm/mach-omap2/omap-hotplug.c b/arch/arm/mach-omap2/omap-hotplug.c
index 9f8f097..cf4ab15 100644
--- a/arch/arm/mach-omap2/omap-hotplug.c
+++ b/arch/arm/mach-omap2/omap-hotplug.c
@@ -23,6 +23,7 @@
 #include <mach/omap-wakeupgen.h>
 
 #include "powerdomain.h"
+#include "clockdomain.h"
 
 int platform_cpu_kill(unsigned int cpu)
 {
@@ -36,6 +37,11 @@ int platform_cpu_kill(unsigned int cpu)
 void platform_cpu_die(unsigned int cpu)
 {
 	unsigned int this_cpu;
+	static struct clockdomain *cpu1_clkdm;
+
+	/* To avoid cpu1 clockdomain lookup every time */
+	if (!cpu1_clkdm)
+		cpu1_clkdm = clkdm_lookup("mpu1_clkdm");
 
 	flush_cache_all();
 	dsb();
@@ -59,6 +65,9 @@ void platform_cpu_die(unsigned int cpu)
 			 * OK, proper wakeup, we're done
 			 */
 			omap_wakeupgen_irqmask_all(this_cpu, 0);
+
+			/* Restore clockdomain to hardware supervised */
+			clkdm_allow_idle(cpu1_clkdm);
 			break;
 		}
 		pr_debug("CPU%u: spurious wakeup call\n", cpu);
diff --git a/arch/arm/mach-omap2/omap-smp.c b/arch/arm/mach-omap2/omap-smp.c
index b105a29..31c9b79 100644
--- a/arch/arm/mach-omap2/omap-smp.c
+++ b/arch/arm/mach-omap2/omap-smp.c
@@ -25,6 +25,8 @@
 #include <mach/hardware.h>
 #include <mach/omap4-common.h>
 
+#include "clockdomain.h"
+
 /* SCU base address */
 void __iomem *scu_base;
 
@@ -48,6 +50,8 @@ void __cpuinit platform_secondary_init(unsigned int cpu)
 
 int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
 {
+	static struct clockdomain *cpu1_clkdm;
+	static bool booted;
 	/*
 	 * Set synchronisation state between this boot processor
 	 * and the secondary one
@@ -63,7 +67,28 @@ int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
 	omap_modify_auxcoreboot0(0x200, 0xfffffdff);
 	flush_cache_all();
 	smp_wmb();
-	smp_cross_call(cpumask_of(cpu), 1);
+
+	/* To avoid cpu1 clockdomain lookup every time */
+	if (!cpu1_clkdm)
+		cpu1_clkdm = clkdm_lookup("mpu1_clkdm");
+
+	/*
+	 * The SGI(Software Generated Interrupts) are not wakeup capable
+	 * from low power states. This is known limitation on OMAP4 and
+	 * needs to be worked around by using software forced clockdomain
+	 * wake-up. To wakeup CPU1, CPU0 forces the CPU1 clockdomain to
+	 * software force wakeup. After the wakeup, CPU1 restores its
+	 * clockdomain hardware supervised mode.
+	 * More details can be found in OMAP4430 TRM - Version J
+	 * Section :
+	 *	4.3.4.2 Power States of CPU0 and CPU1
+	 */
+	if (booted) {
+		clkdm_wakeup(cpu1_clkdm);
+	} else {
+		dsb_sev();
+		booted = true;
+	}
 
 	/*
 	 * Now the secondary core is starting up let it run its
-- 
1.6.0.4

^ permalink raw reply related

* [PATCH 06/17] omap4: pm: Program CPU1 to hit OFF when off-lined
From: Santosh Shilimkar @ 2011-02-19 10:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1298112158-28469-1-git-send-email-santosh.shilimkar@ti.com>

Program non-boot CPUs to hit lowest supported power state
when it is off-lined using cpu hotplug framework.

Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
---
 arch/arm/mach-omap2/omap-hotplug.c |   18 +++++++++++++-----
 1 files changed, 13 insertions(+), 5 deletions(-)

diff --git a/arch/arm/mach-omap2/omap-hotplug.c b/arch/arm/mach-omap2/omap-hotplug.c
index 4976b93..9f8f097 100644
--- a/arch/arm/mach-omap2/omap-hotplug.c
+++ b/arch/arm/mach-omap2/omap-hotplug.c
@@ -20,6 +20,9 @@
 
 #include <asm/cacheflush.h>
 #include <mach/omap4-common.h>
+#include <mach/omap-wakeupgen.h>
+
+#include "powerdomain.h"
 
 int platform_cpu_kill(unsigned int cpu)
 {
@@ -32,6 +35,8 @@ int platform_cpu_kill(unsigned int cpu)
  */
 void platform_cpu_die(unsigned int cpu)
 {
+	unsigned int this_cpu;
+
 	flush_cache_all();
 	dsb();
 
@@ -39,18 +44,21 @@ void platform_cpu_die(unsigned int cpu)
 	 * we're ready for shutdown now, so do it
 	 */
 	if (omap_modify_auxcoreboot0(0x0, 0x200) != 0x0)
-		printk(KERN_CRIT "Secure clear status failed\n");
+		pr_err("Secure clear status failed\n");
 
 	for (;;) {
 		/*
-		 * Execute WFI
+		 * Enter into low power state
+		 * clear all interrupt wakeup sources
 		 */
-		do_wfi();
-
-		if (omap_read_auxcoreboot0() == cpu) {
+		omap_wakeupgen_irqmask_all(cpu, 1);
+		omap4_enter_lowpower(cpu, PWRDM_POWER_OFF);
+		this_cpu = hard_smp_processor_id();
+		if (omap_read_auxcoreboot0() == this_cpu) {
 			/*
 			 * OK, proper wakeup, we're done
 			 */
+			omap_wakeupgen_irqmask_all(this_cpu, 0);
 			break;
 		}
 		pr_debug("CPU%u: spurious wakeup call\n", cpu);
-- 
1.6.0.4

^ permalink raw reply related

* [PATCH 05/17] omap4: pm: Initialise all the clockdomains to supported states
From: Santosh Shilimkar @ 2011-02-19 10:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1298112158-28469-1-git-send-email-santosh.shilimkar@ti.com>

Initialise hardware supervised mode for all clockdomains if it's
supported. Initiate sleep transition for other clockdomains,
if they are not being used.

Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Signed-off-by: Rajendra Nayak <rnayak@ti.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
---
 arch/arm/mach-omap2/pm44xx.c |   22 +++++++++++++++++++++-
 1 files changed, 21 insertions(+), 1 deletions(-)

diff --git a/arch/arm/mach-omap2/pm44xx.c b/arch/arm/mach-omap2/pm44xx.c
index b142673..9a9dc41 100644
--- a/arch/arm/mach-omap2/pm44xx.c
+++ b/arch/arm/mach-omap2/pm44xx.c
@@ -17,9 +17,11 @@
 #include <linux/slab.h>
 #include <plat/clock.h>
 
-#include "powerdomain.h"
 #include <mach/omap4-common.h>
 
+#include "powerdomain.h"
+#include "clockdomain.h"
+
 struct power_state {
 	struct powerdomain *pwrdm;
 	u32 next_state;
@@ -74,6 +76,22 @@ static const struct platform_suspend_ops omap_pm_ops = {
 };
 #endif /* CONFIG_SUSPEND */
 
+/*
+ * Enable hardwarew supervised mode for all clockdomains if it's
+ * supported. Initiate sleep transition for other clockdomains, if
+ * they are not used
+ */
+static int __init clkdms_setup(struct clockdomain *clkdm, void *unused)
+{
+	if (clkdm->flags & CLKDM_CAN_ENABLE_AUTO)
+		clkdm_allow_idle(clkdm);
+	else if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP &&
+			atomic_read(&clkdm->usecount) == 0)
+		clkdm_sleep(clkdm);
+	return 0;
+}
+
+
 static int __init pwrdms_setup(struct powerdomain *pwrdm, void *unused)
 {
 	struct power_state *pwrst;
@@ -116,6 +134,8 @@ static int __init omap4_pm_init(void)
 	/* Enable autoidle for all clks which support it*/
 	omap_clk_enable_autoidle();
 
+	(void) clkdm_for_each(clkdms_setup, NULL);
+
 	ret = omap4_mpuss_init();
 	if (ret) {
 		pr_err("Failed to initialise OMAP4 MPUSS\n");
-- 
1.6.0.4

^ permalink raw reply related

* [PATCH 04/17] omap4: pm: Add CPUx OFF mode support
From: Santosh Shilimkar @ 2011-02-19 10:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1298112158-28469-1-git-send-email-santosh.shilimkar@ti.com>

This patch adds the CPU0 and CPU1 off mode support. CPUX close switch
retention (CSWR) is not supported by hardware design.

The CPUx OFF mode isn't supported on OMAP4430 ES1.0

CPUx sleep code is common for hotplug, suspend and cpuilde.

Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
---
 arch/arm/mach-omap2/Makefile                    |    4 +-
 arch/arm/mach-omap2/include/mach/omap4-common.h |   46 +++
 arch/arm/mach-omap2/omap4-mpuss-lowpower.c      |  241 ++++++++++++++++
 arch/arm/mach-omap2/omap4-sar-layout.h          |   14 +
 arch/arm/mach-omap2/pm44xx.c                    |    6 +
 arch/arm/mach-omap2/sleep44xx.S                 |  334 +++++++++++++++++++++++
 6 files changed, 644 insertions(+), 1 deletions(-)
 create mode 100644 arch/arm/mach-omap2/omap4-mpuss-lowpower.c
 create mode 100644 arch/arm/mach-omap2/sleep44xx.S

diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index 54ff219..5d94f7e 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -63,13 +63,15 @@ obj-$(CONFIG_ARCH_OMAP2)		+= pm24xx.o
 obj-$(CONFIG_ARCH_OMAP2)		+= sleep24xx.o pm_bus.o voltage.o
 obj-$(CONFIG_ARCH_OMAP3)		+= pm34xx.o sleep34xx.o voltage.o \
 					   cpuidle34xx.o pm_bus.o
-obj-$(CONFIG_ARCH_OMAP4)		+= pm44xx.o voltage.o pm_bus.o
+obj-$(CONFIG_ARCH_OMAP4)		+= pm44xx.o voltage.o pm_bus.o \
+					   omap4-mpuss-lowpower.o sleep44xx.o
 obj-$(CONFIG_PM_DEBUG)			+= pm-debug.o
 obj-$(CONFIG_OMAP_SMARTREFLEX)          += sr_device.o smartreflex.o
 obj-$(CONFIG_OMAP_SMARTREFLEX_CLASS3)	+= smartreflex-class3.o
 
 AFLAGS_sleep24xx.o			:=-Wa,-march=armv6
 AFLAGS_sleep34xx.o			:=-Wa,-march=armv7-a
+AFLAGS_sleep44xx.o			:=-Wa,-march=armv7-a
 
 ifeq ($(CONFIG_PM_VERBOSE),y)
 CFLAGS_pm_bus.o				+= -DDEBUG
diff --git a/arch/arm/mach-omap2/include/mach/omap4-common.h b/arch/arm/mach-omap2/include/mach/omap4-common.h
index 0e5edd8..74c9aa7 100644
--- a/arch/arm/mach-omap2/include/mach/omap4-common.h
+++ b/arch/arm/mach-omap2/include/mach/omap4-common.h
@@ -13,6 +13,9 @@
 #ifndef OMAP_ARCH_OMAP4_COMMON_H
 #define OMAP_ARCH_OMAP4_COMMON_H
 
+#include <asm/proc-fns.h>
+
+#ifndef __ASSEMBLER__
 /*
  * wfi used in low power code. Directly opcode is used instead
  * of instruction to avoid mulit-omap build break
@@ -33,4 +36,47 @@ extern void __iomem *scu_base;
 extern void __init gic_init_irq(void);
 extern void omap_smc1(u32 fn, u32 arg);
 
+/*
+ * Read MPIDR: Multiprocessor affinity register
+ */
+static inline unsigned int hard_smp_processor_id(void)
+{
+	unsigned int cpunum;
+
+	asm volatile (
+	"mrc	 p15, 0, %0, c0, c0, 5\n"
+		: "=r" (cpunum));
+	return cpunum &= 0x0F;
+}
+
+#if defined(CONFIG_SMP)	&& defined(CONFIG_PM)
+extern int omap4_mpuss_init(void);
+extern int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state);
+extern void omap4_cpu_suspend(unsigned int cpu, unsigned int save_state);
+extern void omap4_cpu_resume(void);
+
+#else
+
+static inline int omap4_enter_lowpower(unsigned int cpu,
+					unsigned int power_state)
+{
+	cpu_do_idle();
+	return 0;
+}
+
+static inline int omap4_mpuss_init(void)
+{
+	return 0;
+}
+
+static inline void omap4_cpu_suspend(unsigned int cpu, unsigned int save_state)
+{
+}
+
+static inline void omap4_cpu_resume(void)
+{
+}
+
 #endif
+#endif /* __ASSEMBLER__ */
+#endif /* OMAP_ARCH_OMAP4_COMMON_H */
diff --git a/arch/arm/mach-omap2/omap4-mpuss-lowpower.c b/arch/arm/mach-omap2/omap4-mpuss-lowpower.c
new file mode 100644
index 0000000..c0f358d
--- /dev/null
+++ b/arch/arm/mach-omap2/omap4-mpuss-lowpower.c
@@ -0,0 +1,241 @@
+/*
+ * OMAP4 MPUSS low power code
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Written by Santosh Shilimkar <santosh.shilimkar@ti.com>
+ *
+ * OMAP4430 MPUSS mainly consists of dual Cortex-A9 with per-CPU
+ * Local timer and Watchdog, GIC, SCU, PL310 L2 cache controller,
+ * CPU0 and CPU1 LPRM modules.
+ * CPU0, CPU1 and MPUSS each have there own power domain and
+ * hence multiple low power combinations of MPUSS are possible.
+ *
+ * The CPU0 and CPU1 can't support Closed switch Retention (CSWR)
+ * because the mode is not supported by hw constraints of dormant
+ * mode. While waking up from the dormant mode, a reset  signal
+ * to the Cortex-A9 processor must be asserted by the external
+ * power controller.
+ *
+ * With architectural inputs and hardware recommendations, only
+ * below modes are supported from power gain vs latency point of view.
+ *
+ *	CPU0		CPU1		MPUSS
+ *	----------------------------------------------
+ *	ON		ON		ON
+ *	ON(Inactive)	OFF		ON(Inactive)
+ *	OFF		OFF		CSWR
+ *	OFF		OFF		OSWR (*TBD)
+ *	OFF		OFF		OFF* (*TBD)
+ *	----------------------------------------------
+ *
+ * Note: CPU0 is the master core and it is the last CPU to go down
+ * and first to wake-up when MPUSS low power states are excercised
+ *
+ *
+ * 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/io.h>
+#include <linux/errno.h>
+#include <linux/linkage.h>
+#include <linux/smp.h>
+
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
+#include <asm/smp_scu.h>
+#include <asm/system.h>
+
+#include <plat/omap44xx.h>
+#include <mach/omap4-common.h>
+
+#include "omap4-sar-layout.h"
+#include "pm.h"
+#include "powerdomain.h"
+
+#ifdef CONFIG_SMP
+
+#define CPU0_ID				0x0
+#define CPU1_ID				0x1
+
+struct omap4_cpu_pm_info {
+	struct powerdomain *pwrdm;
+	void __iomem *scu_sar_addr;
+};
+
+static DEFINE_PER_CPU(struct omap4_cpu_pm_info, omap4_pm_info);
+
+/*
+ * Set the CPUx powerdomain's previous power state
+ */
+static inline void set_cpu_next_pwrst(unsigned int cpu_id,
+				unsigned int power_state)
+{
+	struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id);
+
+	pwrdm_set_next_pwrst(pm_info->pwrdm, power_state);
+}
+
+/*
+ * Read CPU's previous power state
+ */
+static inline unsigned int read_cpu_prev_pwrst(unsigned int cpu_id)
+{
+	struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id);
+
+	return pwrdm_read_prev_pwrst(pm_info->pwrdm);
+}
+
+/*
+ * Clear the CPUx powerdomain's previous power state
+ */
+static inline void clear_cpu_prev_pwrst(unsigned int cpu_id)
+{
+	struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id);
+
+	pwrdm_clear_all_prev_pwrst(pm_info->pwrdm);
+}
+
+/*
+ * Store the SCU power status value to scratchpad memory
+ */
+static void scu_pwrst_prepare(unsigned int cpu_id, unsigned int cpu_state)
+{
+	struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id);
+	u32 scu_pwr_st;
+
+	switch (cpu_state) {
+	case PWRDM_POWER_RET:
+		scu_pwr_st = SCU_PM_DORMANT;
+		break;
+	case PWRDM_POWER_OFF:
+		scu_pwr_st = SCU_PM_POWEROFF;
+		break;
+	case PWRDM_POWER_ON:
+	case PWRDM_POWER_INACTIVE:
+	default:
+		scu_pwr_st = SCU_PM_NORMAL;
+		break;
+	}
+
+	__raw_writel(scu_pwr_st, pm_info->scu_sar_addr);
+}
+
+/*
+ * OMAP4 MPUSS Low Power Entry Function
+ *
+ * The purpose of this function is to manage low power programming
+ * of OMAP4 MPUSS subsystem
+ * Paramenters:
+ *	cpu : CPU ID
+ *	power_state: Targetted Low power state.
+ */
+int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state)
+{
+	unsigned int save_state = 0;
+	unsigned int wakeup_cpu = hard_smp_processor_id();
+
+	if ((cpu > NR_CPUS) || (omap_rev() == OMAP4430_REV_ES1_0))
+		goto ret;
+
+	switch (power_state) {
+	case PWRDM_POWER_ON:
+	case PWRDM_POWER_INACTIVE:
+		save_state = 0;
+		break;
+	case PWRDM_POWER_OFF:
+		save_state = 1;
+		break;
+	case PWRDM_POWER_RET:
+	default:
+		/*
+		 * CPUx CSWR is invalid hardware state. Also CPUx OSWR
+		 * doesn't make much scense, since logic is lost and $L1
+		 * needs to be cleaned because of coherency. This makes
+		 * CPUx OSWR equivalent to CPUX OFF and hence not supported
+		 */
+		goto ret;
+	}
+
+	clear_cpu_prev_pwrst(cpu);
+	set_cpu_next_pwrst(cpu, power_state);
+	scu_pwrst_prepare(cpu, power_state);
+
+	/*
+	 * Call low level function  with targeted CPU id
+	 * and its low power state.
+	 */
+	omap4_cpu_suspend(cpu, save_state);
+
+	/*
+	 * Restore the CPUx power state to ON otherwise CPUx
+	 * power domain can transitions to programmed low power
+	 * state while doing WFI outside the low powe code. On
+	 * secure devices, CPUx does WFI which can result in
+	 * domain transition
+	 */
+	wakeup_cpu = hard_smp_processor_id();
+	set_cpu_next_pwrst(wakeup_cpu, PWRDM_POWER_ON);
+
+ret:
+	return 0;
+}
+
+/*
+ * Initialise OMAP4 MPUSS
+ */
+int __init omap4_mpuss_init(void)
+{
+	struct omap4_cpu_pm_info *pm_info;
+
+	if (omap_rev() == OMAP4430_REV_ES1_0) {
+		WARN(1, "Power Management not supported on OMAP4430 ES1.0\n");
+		return -EPERM;
+	}
+
+	/* Initilaise per CPU PM information */
+	pm_info = &per_cpu(omap4_pm_info, CPU0_ID);
+	pm_info->scu_sar_addr = sar_ram_base + SCU_OFFSET0;
+	pm_info->pwrdm = pwrdm_lookup("cpu0_pwrdm");
+	if (!pm_info->pwrdm) {
+		pr_err("Lookup failed for CPU0 pwrdm\n");
+		return -ENODEV;
+	}
+
+	/* Clear CPU previous power domain state */
+	pwrdm_clear_all_prev_pwrst(pm_info->pwrdm);
+
+	/* Initialise CPU0 power domain state to ON */
+	pwrdm_set_next_pwrst(pm_info->pwrdm, PWRDM_POWER_ON);
+
+	pm_info = &per_cpu(omap4_pm_info, CPU1_ID);
+	pm_info->scu_sar_addr = sar_ram_base + SCU_OFFSET1;
+	pm_info->pwrdm = pwrdm_lookup("cpu1_pwrdm");
+	if (!pm_info->pwrdm) {
+		pr_err("Lookup failed for CPU1 pwrdm\n");
+		return -ENODEV;
+	}
+
+	/* Clear CPU previous power domain state */
+	pwrdm_clear_all_prev_pwrst(pm_info->pwrdm);
+
+	/* Initialise CPU1 power domain state to ON */
+	pwrdm_set_next_pwrst(pm_info->pwrdm, PWRDM_POWER_ON);
+
+	/*
+	 * Program the wakeup routine address for the CPU0 and CPU1
+	 * used for OFF or DORMANT wakeup. Wakeup routine address
+	 * is fixed so programit in init itself.
+	 */
+	__raw_writel(virt_to_phys(omap4_cpu_resume),
+			sar_ram_base + CPU1_WAKEUP_NS_PA_ADDR_OFFSET);
+	__raw_writel(virt_to_phys(omap4_cpu_resume),
+			sar_ram_base + CPU0_WAKEUP_NS_PA_ADDR_OFFSET);
+
+	return 0;
+}
+
+#endif
+
diff --git a/arch/arm/mach-omap2/omap4-sar-layout.h b/arch/arm/mach-omap2/omap4-sar-layout.h
index bb66816..c4251db 100644
--- a/arch/arm/mach-omap2/omap4-sar-layout.h
+++ b/arch/arm/mach-omap2/omap4-sar-layout.h
@@ -19,6 +19,20 @@
 #define SAR_BANK3_OFFSET		0x2000
 #define SAR_BANK4_OFFSET		0x3000
 
+/* Scratch pad memory offsets from SAR_BANK1 */
+#define CPU0_SAVE_OFFSET			0xb00
+#define CPU1_SAVE_OFFSET			0xc00
+#define MMU_OFFSET				0xd00
+#define SCU_OFFSET0				0xd20
+#define SCU_OFFSET1				0xd24
+
+/* CPUx Wakeup Non-Secure Physical Address offsets in SAR_BANK3 */
+#define CPU0_WAKEUP_NS_PA_ADDR_OFFSET		0xa04
+#define CPU1_WAKEUP_NS_PA_ADDR_OFFSET		0xa08
+
+#ifndef __ASSEMBLER__
+
 extern void __iomem *sar_ram_base;
 
 #endif
+#endif
diff --git a/arch/arm/mach-omap2/pm44xx.c b/arch/arm/mach-omap2/pm44xx.c
index 8431d41..b142673 100644
--- a/arch/arm/mach-omap2/pm44xx.c
+++ b/arch/arm/mach-omap2/pm44xx.c
@@ -115,6 +115,12 @@ static int __init omap4_pm_init(void)
 
 	/* Enable autoidle for all clks which support it*/
 	omap_clk_enable_autoidle();
+
+	ret = omap4_mpuss_init();
+	if (ret) {
+		pr_err("Failed to initialise OMAP4 MPUSS\n");
+		goto err2;
+	}
 #endif
 
 #ifdef CONFIG_SUSPEND
diff --git a/arch/arm/mach-omap2/sleep44xx.S b/arch/arm/mach-omap2/sleep44xx.S
new file mode 100644
index 0000000..bb42a7a
--- /dev/null
+++ b/arch/arm/mach-omap2/sleep44xx.S
@@ -0,0 +1,334 @@
+/*
+ * OMAP44xx CPU low power powerdown and powerup code.
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Written by Santosh Shilimkar <santosh.shilimkar@ti.com>
+ *
+ * This program is free software,you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/linkage.h>
+#include <asm/system.h>
+#include <asm/smp_scu.h>
+#include <asm/memory.h>
+
+#include <plat/omap44xx.h>
+#include <mach/omap4-common.h>
+#include <asm/hardware/cache-l2x0.h>
+
+#include "omap4-sar-layout.h"
+
+#ifdef CONFIG_SMP
+
+/* Masks used for MMU manipulation */
+#define TTRBIT_MASK				0xffffc000
+#define TABLE_INDEX_MASK			0xfff00000
+#define TABLE_ENTRY				0x00000c02
+#define CACHE_DISABLE_MASK			0xffffe7fb
+
+/*
+ * =============================
+ * == CPU suspend entry point ==
+ * =============================
+ *
+ * void omap4_cpu_suspend(unsigned int cpu, unsigned int save_state)
+ *
+ * This function code saves the CPU context and performs the CPU
+ * power down sequence. Calling WFI effectively changes the CPU
+ * power domains states to the desired target power state.
+ *
+ * @cpu : contains cpu id (r0)
+ * @save_state : contains context save state (r1)
+ *	0 - No context lost
+ * 	1 - CPUx L1 and logic lost: MPUSS CSWR
+ * 	2 - CPUx L1 and logic lost + GIC lost: MPUSS OSWR
+ *	3 - CPUx L1 and logic lost + GIC + L2 lost: MPUSS OFF
+ * @return: This function never returns for CPU OFF and DORMANT power states.
+ * It retunrs to the caller for CPU INACTIVE and ON power states or in case
+ * CPU failed to transition to targeted OFF/DORMANT state.
+ */
+
+ENTRY(omap4_cpu_suspend)
+	stmfd	sp!, {r0-r12, lr}		@ Save registers on stack
+	cmp	r1, #0x0
+	beq	do_WFI				@ Nothing to save, jump to WFI
+	ldr	r8, =sar_ram_base
+	ldr	r8, [r8]
+	ands	r0, r0, #0x0f
+	orreq	r8, r8, #CPU0_SAVE_OFFSET
+	orrne	r8, r8, #CPU1_SAVE_OFFSET
+
+	/*
+	 * Save only needed CPU CP15 registers. VFP, breakpoint,
+	 * performance monitor registers are not saved. Generic
+	 * code suppose to take care of those.
+	 */
+	mov	r4, sp				@ Store sp
+	mrs	r5, spsr			@ Store spsr
+	mov	r6, lr				@ Store lr
+	stmia	r8!, {r4-r6}
+
+	/* c1 and c2 registers */
+	mrc	p15, 0, r4, c1, c0, 2		@ CPACR
+	mrc	p15, 0, r5, c2, c0, 0		@ TTBR0
+	mrc	p15, 0, r6, c2, c0, 1		@ TTBR1
+	mrc	p15, 0, r7, c2, c0, 2		@ TTBCR
+	stmia	r8!, {r4-r7}
+
+	/* c3 and c10 registers */
+	mrc	p15, 0, r4, c3, c0, 0		@ DACR
+	mrc	p15, 0, r5, c10, c2, 0		@ PRRR
+	mrc	p15, 0, r6, c10, c2, 1		@ NMRR
+	stmia	r8!,{r4-r6}
+
+	/* c13 registers */
+	mrc	p15, 0, r4, c13, c0, 1		@ Context ID
+	mrc	p15, 0, r5, c13, c0, 2		@ User r/w thread ID
+	mrc	p15, 0, r6, c13, c0, 3		@ User ro thread ID
+	mrc	p15, 0, r7, c13, c0, 4		@ Privilege only thread ID
+	stmia	r8!, {r4-r7}
+
+	/* c12 and CPSR registers */
+	mrc	p15, 0, r4, c12, c0, 0		@ Secure or NS VBAR
+	mrs	r5, cpsr			@ Store CPSR
+	stmia	r8!, {r4,r5}
+
+	/* c1 control register */
+	mrc	p15, 0, r4, c1, c0, 0		@ Save control register
+	stmia	r8!, {r4}
+
+	/* Clear the SCTLR C bit to prevent further data cache allocation */
+	mrc	p15, 0, r0, c1, c0, 0
+	bic	r0, r0, #(1 << 2)		@ Disable the C bit
+	mcr	p15, 0, r0, c1, c0, 0
+	isb
+
+	/*
+	 * Clean and invalidate all data from the L1 data cache.
+	 * The L2 duplicate snoop tag RAM for this CPU is now
+	 * empty. This prevents any new data cache snoops or data
+	 * cache maintenance operations from other CPUs in the cluster
+	 * being issued to this CPU.
+	 */
+	bl	v7_flush_dcache_all
+
+	/*
+	 * Switch the CPU from Symmetric Multiprocessing (SMP) mode
+	 * to AsymmetricMultiprocessing (AMP) mode by programming
+	 * the SCU power status to DORMANT or OFF mode.
+	 * This enables the CPU to be taken out of coherency by
+	 * preventing the CPU from receiving cache, TLB, or BTB
+	 * maintenance operations broadcast by other CPUs in the cluster.
+	 */
+	ldr	r8, =sar_ram_base
+	ldr	r8, [r8]
+	mrc	p15, 0, r0, c0, c0, 5		@ Read MPIDR
+	ands	r0, r0, #0x0f
+	ldreq	r1, [r8, #SCU_OFFSET0]
+	ldrne	r1, [r8, #SCU_OFFSET1]
+	ldr	r0, =scu_base
+	ldr	r0, [r0]
+	bl     scu_power_mode
+
+do_WFI:
+	/*
+	 * Execute an ISB instruction to ensure that all of the
+	 * CP15 register changes have been committed.
+	 */
+	isb
+
+	/*
+	 * Execute a barrier instruction to ensure that all cache,
+	 * TLB and branch predictor maintenance operations issued
+	 * by any CPU in the cluster have completed.
+	 */
+	dsb
+	dmb
+
+	/*
+	 * Execute a WFI instruction and wait until the
+	 * STANDBYWFI output is asserted to indicate that the
+	 * CPU is in idle and low power state.
+	 */
+	wfi					@ Wait For Interrupt
+
+	/*
+	 * CPU is here when it failed to enter OFF/DORMANT or
+	 * no low power state was attempted.
+	 */
+	mrc	p15, 0, r0, c1, c0, 0
+	tst	r0, #(1 << 2)			@ Check C bit enabled?
+	orreq	r0, r0, #(1 << 2)		@ Enable the C bit
+	mcreq	p15, 0, r0, c1, c0, 0
+	isb
+
+	/*
+	 * Ensure the CPU power state is set to NORMAL in
+	 * SCU power state so that CPU is back in coherency.
+	 * In non-coherent mode CPU can lock-up and lead to
+	 * system deadlock.
+	 */
+	ldr	r0, =scu_base
+	ldr	r0, [r0]
+	mov	r1, #SCU_PM_NORMAL
+	bl     scu_power_mode
+	isb
+	dsb
+
+	ldmfd	sp!, {r0-r12, pc}		@ Restore regs and return
+ENDPROC(omap4_cpu_suspend)
+
+/*
+ * ============================
+ * == CPU resume entry point ==
+ * ============================
+ *
+ * void omap4_cpu_resume(void)
+ *
+ * ROM code jumps to this function while waking up from CPU
+ * OFF or DORMANT state. Physical address of the function is
+ * stored in the SAR RAM while entering to OFF or DORMANT mode.
+ */
+
+ENTRY(omap4_cpu_resume)
+	/*
+	 * Check the wakeup cpuid and use appropriate
+	 * SAR BANK location for context restore.
+	 */
+	ldr	r3, =OMAP44XX_SAR_RAM_BASE
+	mov	r1, #0
+	mcr	p15, 0, r1, c7, c5, 0		@ Invalidate L1 I
+	mrc	p15, 0, r0, c0, c0, 5		@ MPIDR
+	ands	r0, r0, #0x0f
+	orreq	r3, r3, #CPU0_SAVE_OFFSET
+	orrne	r3, r3, #CPU1_SAVE_OFFSET
+
+	/* Restore cp15 registers */
+	ldmia	r3!, {r4-r6}
+	mov	sp, r4				@ Restore sp
+	msr	spsr_cxsf, r5			@ Restore spsr
+	mov	lr, r6				@ Restore lr
+
+	/* c1 and c2 registers */
+	ldmia	r3!, {r4-r7}
+	mcr	p15, 0, r4, c1, c0, 2		@ CPACR
+	mcr	p15, 0, r5, c2, c0, 0		@ TTBR0
+	mcr	p15, 0, r6, c2, c0, 1		@ TTBR1
+	mcr	p15, 0, r7, c2, c0, 2		@ TTBCR
+
+	/* c3 and c10 registers */
+	ldmia	r3!,{r4-r6}
+	mcr	p15, 0, r4, c3, c0, 0		@ DACR
+	mcr	p15, 0, r5, c10, c2, 0		@ PRRR
+	mcr	p15, 0, r6, c10, c2, 1		@ NMRR
+
+	/* c13 registers */
+	ldmia	r3!,{r4-r7}
+	mcr	p15, 0, r4, c13, c0, 1		@ Context ID
+	mcr	p15, 0, r5, c13, c0, 2		@ User r/w thread ID
+	mcr	p15, 0, r6, c13, c0, 3		@ User ro thread ID
+	mcr	p15, 0, r7, c13, c0, 4		@ Privilege only thread ID
+
+	/* c12 and CPSR registers */
+	ldmia	r3!,{r4,r5}
+	mrc	p15, 0, r4, c12, c0, 0		@ Secure or NS VBAR
+	msr	cpsr, r5			@ store cpsr
+
+	/*
+	 * Enabling MMU here. Page entry needs to be altered
+	 * to create temporary 1:1 map and then resore the entry
+	 * ones MMU is enabled
+	 */
+	mrc	p15, 0, r7, c2, c0, 2		@ Read TTBRControl
+	and	r7, #0x7			@ Extract N (0:2) to decide
+	cmp	r7, #0x0			@ TTBR0/TTBR1
+	beq	use_ttbr0
+ttbr_error:
+	b	ttbr_error			@ Only N = 0 supported
+use_ttbr0:
+	mrc	p15, 0, r2, c2, c0, 0		@ Read TTBR0
+	ldr	r5, =TTRBIT_MASK
+	and	r2, r5
+	mov	r4, pc
+	ldr	r5, =TABLE_INDEX_MASK
+	and	r4, r5				@ r4 = 31 to 20 bits of pc
+	ldr	r1, =TABLE_ENTRY
+	add	r1, r1, r4			@ r1 has value of table entry
+	lsr	r4, #18				@ Address of table entry
+	add	r2, r4				@ r2 - location to be modified
+
+	/* Ensure the modified entry makes it to main memory */
+#ifdef CONFIG_CACHE_L2X0
+	ldr	r5, =OMAP44XX_L2CACHE_BASE
+	str	r2, [r5, #L2X0_CLEAN_INV_LINE_PA]
+wait_l2:
+	ldr	r0, [r5, #L2X0_CLEAN_INV_LINE_PA]
+	ands	r0, #1
+	bne	wait_l2
+#endif
+
+	/* Storing previous entry of location being modified */
+	ldr     r5, =OMAP44XX_SAR_RAM_BASE
+	ldr	r4, [r2]
+	str	r4, [r5, #MMU_OFFSET]		@ Modify the table entry
+	str	r1, [r2]
+
+	/*
+	 * Storing address of entry being modified
+	 * It will be restored after enabling MMU
+	 */
+	ldr     r5, =OMAP44XX_SAR_RAM_BASE
+	orr	r5, r5, #MMU_OFFSET
+	str	r2, [r5, #0x04]
+	mov	r0, #0
+	mcr	p15, 0, r0, c7, c5, 4		@ Flush prefetch buffer
+	mcr	p15, 0, r0, c7, c5, 6		@ Invalidate BTB
+	mcr	p15, 0, r0, c8, c5, 0		@ Invalidate ITLB
+	mcr	p15, 0, r0, c8, c6, 0		@ Invalidate DTLB
+
+	/*
+	 * Restore control register  but don't enable Data caches here.
+	 * Caches will be enabled after restoring MMU table entry.
+	 */
+	ldmia	r3!, {r4}
+	str	r4, [r5, #0x08]			@ Store previous value of CR
+	ldr	r2, =CACHE_DISABLE_MASK
+	and	r4, r2
+	mcr	p15, 0, r4, c1, c0, 0
+	isb
+	dsb
+	ldr	r0, =mmu_on_label
+	bx	r0
+mmu_on_label:
+	/* Set up the per-CPU stacks */
+	bl	cpu_init
+
+	/*
+	 * Restore the MMU table entry that was modified for
+	 * enabling MMU.
+	 */
+	ldr	r8, =sar_ram_base
+	ldr	r8, [r8]
+	orr	r8, r8, #MMU_OFFSET		@ Get address of entry that..
+	ldr	r2, [r8, #0x04]			@ was modified
+	ldr	r3, =local_va2pa_offet
+	add	r2, r2, r3
+	ldr	r0, [r8]			@ Get the previous value..
+	str	r0, [r2]			@ which needs to be restored
+	mov	r0, #0
+	mcr	p15, 0, r0, c7, c1, 6		@ flush TLB and issue barriers
+	dsb
+	isb
+	ldr	r0, [r8, #0x08]			@ Restore the Control register
+	mcr     p15, 0, r0, c1, c0, 0		@ with caches enabled.
+	isb
+
+	ldmfd	sp!, {r0-r12, pc}		@ restore regs and return
+
+	.equ	local_va2pa_offet, (PHYS_OFFSET + PAGE_OFFSET)
+
+ENDPROC(omap4_cpu_resume)
+
+#endif
-- 
1.6.0.4

^ permalink raw reply related

* [PATCH 03/17] omap4: Export scu base address
From: Santosh Shilimkar @ 2011-02-19 10:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1298112158-28469-1-git-send-email-santosh.shilimkar@ti.com>

The scu base address needs to be accessed in cpu hotplug
for power management. Hence export the same

Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
---
 arch/arm/mach-omap2/include/mach/omap4-common.h |    4 ++++
 arch/arm/mach-omap2/omap-smp.c                  |    2 +-
 2 files changed, 5 insertions(+), 1 deletions(-)

diff --git a/arch/arm/mach-omap2/include/mach/omap4-common.h b/arch/arm/mach-omap2/include/mach/omap4-common.h
index 5b0270b..0e5edd8 100644
--- a/arch/arm/mach-omap2/include/mach/omap4-common.h
+++ b/arch/arm/mach-omap2/include/mach/omap4-common.h
@@ -26,6 +26,10 @@ extern void __iomem *l2cache_base;
 
 extern void __iomem *gic_dist_base_addr;
 
+#ifdef CONFIG_SMP
+extern void __iomem *scu_base;
+#endif
+
 extern void __init gic_init_irq(void);
 extern void omap_smc1(u32 fn, u32 arg);
 
diff --git a/arch/arm/mach-omap2/omap-smp.c b/arch/arm/mach-omap2/omap-smp.c
index b66cfe8..b105a29 100644
--- a/arch/arm/mach-omap2/omap-smp.c
+++ b/arch/arm/mach-omap2/omap-smp.c
@@ -26,7 +26,7 @@
 #include <mach/omap4-common.h>
 
 /* SCU base address */
-static void __iomem *scu_base;
+void __iomem *scu_base;
 
 static DEFINE_SPINLOCK(boot_lock);
 
-- 
1.6.0.4

^ permalink raw reply related

* [PATCH 02/17] omap4: pm: Add SAR RAM support
From: Santosh Shilimkar @ 2011-02-19 10:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1298112158-28469-1-git-send-email-santosh.shilimkar@ti.com>

This patch adds SAR RAM support on OMAP4430. SAR RAM used to save
and restore the HW context in low power modes.

Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
---
 arch/arm/mach-omap2/omap4-common.c         |   25 ++++++++++++++++++++++++-
 arch/arm/mach-omap2/omap4-sar-layout.h     |   24 ++++++++++++++++++++++++
 arch/arm/plat-omap/include/plat/omap44xx.h |    1 +
 3 files changed, 49 insertions(+), 1 deletions(-)
 create mode 100644 arch/arm/mach-omap2/omap4-sar-layout.h

diff --git a/arch/arm/mach-omap2/omap4-common.c b/arch/arm/mach-omap2/omap4-common.c
index 559d227..48484a5 100644
--- a/arch/arm/mach-omap2/omap4-common.c
+++ b/arch/arm/mach-omap2/omap4-common.c
@@ -23,12 +23,14 @@
 #include <mach/omap4-common.h>
 #include <mach/omap-wakeupgen.h>
 
+#include "omap4-sar-layout.h"
+
 #ifdef CONFIG_CACHE_L2X0
 void __iomem *l2cache_base;
 #endif
 
 void __iomem *gic_dist_base_addr;
-
+void __iomem *sar_ram_base;
 
 void __init gic_init_irq(void)
 {
@@ -107,3 +109,24 @@ static int __init omap_l2_cache_init(void)
 }
 early_initcall(omap_l2_cache_init);
 #endif
+
+/*
+ * SAR RAM used to save and restore the HW
+ * context in low power modes
+ */
+static int __init omap4_sar_ram_init(void)
+{
+	/*
+	 * To avoid code running on other OMAPs in
+	 * multi-omap builds
+	 */
+	if (!cpu_is_omap44xx())
+		return -ENODEV;
+
+	/* Static mapping, never released */
+	sar_ram_base = ioremap(OMAP44XX_SAR_RAM_BASE, SZ_8K);
+	BUG_ON(!sar_ram_base);
+
+	return 0;
+}
+early_initcall(omap4_sar_ram_init);
diff --git a/arch/arm/mach-omap2/omap4-sar-layout.h b/arch/arm/mach-omap2/omap4-sar-layout.h
new file mode 100644
index 0000000..bb66816
--- /dev/null
+++ b/arch/arm/mach-omap2/omap4-sar-layout.h
@@ -0,0 +1,24 @@
+/*
+ * omap4-sar-layout.h: OMAP4 SAR RAM layout header file
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Santosh Shilimkar <santosh.shilimkar@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef OMAP_ARCH_OMAP4_SAR_LAYOUT_H
+#define OMAP_ARCH_OMAP4_SAR_LAYOUT_H
+
+/*
+ * SAR BANK offsets from base address OMAP44XX_SAR_RAM_BASE
+ */
+#define SAR_BANK1_OFFSET		0x0000
+#define SAR_BANK2_OFFSET		0x1000
+#define SAR_BANK3_OFFSET		0x2000
+#define SAR_BANK4_OFFSET		0x3000
+
+extern void __iomem *sar_ram_base;
+
+#endif
diff --git a/arch/arm/plat-omap/include/plat/omap44xx.h b/arch/arm/plat-omap/include/plat/omap44xx.h
index ea2b8a6..c0d478e 100644
--- a/arch/arm/plat-omap/include/plat/omap44xx.h
+++ b/arch/arm/plat-omap/include/plat/omap44xx.h
@@ -45,6 +45,7 @@
 #define OMAP44XX_WKUPGEN_BASE		0x48281000
 #define OMAP44XX_MCPDM_BASE		0x40132000
 #define OMAP44XX_MCPDM_L3_BASE		0x49032000
+#define OMAP44XX_SAR_RAM_BASE		0x4a326000
 
 #define OMAP44XX_MAILBOX_BASE		(L4_44XX_BASE + 0xF4000)
 #define OMAP44XX_HSUSB_OTG_BASE		(L4_44XX_BASE + 0xAB000)
-- 
1.6.0.4

^ permalink raw reply related

* [PATCH 01/17] omap4: pm: Add omap WakeupGen module support
From: Santosh Shilimkar @ 2011-02-19 10:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1298112158-28469-1-git-send-email-santosh.shilimkar@ti.com>

This patch adds OMAP WakeupGen support. The WakeupGen unit is responsible
for generating wakeup event from the incoming interrupts and enable bits.
The WakeupGen is implemented in MPU Always-On power domain. During normal
operation, WakeupGen delivers external interrupts directly to the GIC.
When the CPUx asserts StandbyWFI, indicating it wants to enter lowpower
state, the Standby Controller checks with the WakeupGen unit using the
idlereq/idleack handshake to make sure there is no incoming interrupts.
The GIC and WakeupGen needs to be kept in synchronisation for proper
interrupt functioning.

Hence this patch hooks up the omap WakeupGen mask/unmask along with GIC using
architecture specific hooks.

Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
---
 arch/arm/mach-omap2/Makefile                      |    3 +-
 arch/arm/mach-omap2/include/mach/omap-wakeupgen.h |   40 ++++
 arch/arm/mach-omap2/omap-wakeupgen.c              |  213 +++++++++++++++++++++
 arch/arm/mach-omap2/omap4-common.c                |    3 +
 4 files changed, 258 insertions(+), 1 deletions(-)
 create mode 100644 arch/arm/mach-omap2/include/mach/omap-wakeupgen.h
 create mode 100644 arch/arm/mach-omap2/omap-wakeupgen.c

diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index 72f2891..54ff219 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -24,7 +24,8 @@ obj-$(CONFIG_TWL4030_CORE) += omap_twl.o
 obj-$(CONFIG_SMP)			+= omap-smp.o omap-headsmp.o
 obj-$(CONFIG_LOCAL_TIMERS)		+= timer-mpu.o
 obj-$(CONFIG_HOTPLUG_CPU)		+= omap-hotplug.o
-obj-$(CONFIG_ARCH_OMAP4)		+= omap44xx-smc.o omap4-common.o
+obj-$(CONFIG_ARCH_OMAP4)		+= omap44xx-smc.o omap4-common.o \
+					   omap-wakeupgen.o
 
 plus_sec := $(call as-instr,.arch_extension sec,+sec)
 AFLAGS_omap-headsmp.o			:=-Wa,-march=armv7-a$(plus_sec)
diff --git a/arch/arm/mach-omap2/include/mach/omap-wakeupgen.h b/arch/arm/mach-omap2/include/mach/omap-wakeupgen.h
new file mode 100644
index 0000000..f10d106
--- /dev/null
+++ b/arch/arm/mach-omap2/include/mach/omap-wakeupgen.h
@@ -0,0 +1,40 @@
+/*
+ * OMAP WakeupGen header file
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Written by Santosh Shilimkar <santosh.shilimkar@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef OMAP_ARCH_WAKEUPGEN_H
+#define OMAP_ARCH_WAKEUPGEN_H
+
+#define OMAP_WKG_CONTROL_0			0x00
+#define OMAP_WKG_ENB_A_0			0x10
+#define OMAP_WKG_ENB_B_0			0x14
+#define OMAP_WKG_ENB_C_0			0x18
+#define OMAP_WKG_ENB_D_0			0x1c
+#define OMAP_WKG_ENB_SECURE_A_0			0x20
+#define OMAP_WKG_ENB_SECURE_B_0			0x24
+#define OMAP_WKG_ENB_SECURE_C_0			0x28
+#define OMAP_WKG_ENB_SECURE_D_0			0x2c
+#define OMAP_WKG_ENB_A_1			0x410
+#define OMAP_WKG_ENB_B_1			0x414
+#define OMAP_WKG_ENB_C_1			0x418
+#define OMAP_WKG_ENB_D_1			0x41c
+#define OMAP_WKG_ENB_SECURE_A_1			0x420
+#define OMAP_WKG_ENB_SECURE_B_1			0x424
+#define OMAP_WKG_ENB_SECURE_C_1			0x428
+#define OMAP_WKG_ENB_SECURE_D_1			0x42c
+#define OMAP_AUX_CORE_BOOT_0			0x800
+#define OMAP_AUX_CORE_BOOT_1			0x804
+#define OMAP_PTMSYNCREQ_MASK			0xc00
+#define OMAP_PTMSYNCREQ_EN			0xc04
+#define OMAP_TIMESTAMPCYCLELO			0xc08
+#define OMAP_TIMESTAMPCYCLEHI			0xc0c
+
+extern int __init omap_wakeupgen_init(void);
+extern void omap_wakeupgen_irqmask_all(unsigned int cpu, unsigned int set);
+#endif
diff --git a/arch/arm/mach-omap2/omap-wakeupgen.c b/arch/arm/mach-omap2/omap-wakeupgen.c
new file mode 100644
index 0000000..e26a0ed
--- /dev/null
+++ b/arch/arm/mach-omap2/omap-wakeupgen.c
@@ -0,0 +1,213 @@
+/*
+ * OMAP WakeupGen Source file
+ *
+ * The WakeupGen unit is responsible for generating wakeup event from the
+ * incoming interrupts and enable bits. The WakeupGen is implemented in MPU
+ * always-On power domain. The WakeupGen consists of two sub-units, one for
+ * each CPU and manages only SPI interrupts. Hardware requirements is that
+ * the GIC and WakeupGen should be kept in sync for proper operation.
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Written by Santosh Shilimkar <santosh.shilimkar@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+
+#include <asm/hardware/gic.h>
+
+#include <mach/omap-wakeupgen.h>
+
+#define NR_BANKS		4
+#define MAX_IRQS		128
+#define WKG_MASK_ALL		0x00000000
+#define WKG_UNMASK_ALL		0xffffffff
+#define CPU_ENA_OFFSET		0x400
+#define CPU0_ID			0x0
+#define CPU1_ID			0x1
+
+/* WakeupGen Base addres */
+static void __iomem *wakeupgen_base;
+static DEFINE_PER_CPU(u32 [NR_BANKS], irqmasks);
+static DEFINE_SPINLOCK(wakeupgen_lock);
+
+/*
+ * Static helper functions
+ */
+
+static inline u32 cpu_readl(u8 idx, u32 cpu)
+{
+	return __raw_readl(wakeupgen_base + OMAP_WKG_ENB_A_0 +
+				(cpu * CPU_ENA_OFFSET) + (idx * 4));
+}
+
+static inline void cpu_writel(u32 val, u8 idx, u32 cpu)
+{
+	__raw_writel(val, wakeupgen_base + OMAP_WKG_ENB_A_0 +
+				(cpu * CPU_ENA_OFFSET) + (idx * 4));
+}
+
+static void _wakeupgen_set_all(unsigned int cpu, unsigned int reg)
+{
+	u8 i;
+
+	for (i = 0; i < NR_BANKS; i++)
+		cpu_writel(reg, i, cpu);
+}
+
+static void _wakeupgen_set(unsigned int irq, unsigned int set)
+{
+	unsigned int val, spi_irq;
+	unsigned int cpu = smp_processor_id();
+	u8 i;
+
+	/*
+	 * PPIs and SGIs are not supported
+	 */
+	if (irq < OMAP44XX_IRQ_GIC_START)
+		return;
+
+	/*
+	 * Subtract the GIC offset
+	 */
+	spi_irq = irq - OMAP44XX_IRQ_GIC_START;
+	if (spi_irq > MAX_IRQS) {
+		pr_err("omap wakeupGen: Invalid IRQ%d\n", irq);
+		return ;
+	}
+
+	/*
+	 * Each wakeup gen register controls 32
+	 * interrupts. i.e 1 bit per SPI IRQ
+	 */
+	i = spi_irq >> 5;
+	spi_irq %= 32;
+
+	val = cpu_readl(i, cpu);
+	if (set)
+		val |= BIT(spi_irq);
+	else
+		val &= ~BIT(spi_irq);
+	cpu_writel(val, i, cpu);
+}
+
+static void _wakeupgen_save_masks(unsigned int cpu)
+{
+	u8 i;
+
+	for (i = 0; i < NR_BANKS; i++)
+		per_cpu(irqmasks, cpu)[i] = cpu_readl(i, cpu);
+}
+
+static void _wakeupgen_restore_masks(unsigned int cpu)
+{
+	u8 i;
+
+	for (i = 0; i < NR_BANKS; i++)
+		cpu_writel(per_cpu(irqmasks, cpu)[i], i, cpu);
+}
+
+/*
+ * Architecture specific Mask extensiom
+ */
+static void wakeupgen_mask(struct irq_data *d)
+{
+	spin_lock(&wakeupgen_lock);
+	_wakeupgen_set(d->irq, 0);
+	spin_unlock(&wakeupgen_lock);
+}
+
+/*
+ * Architecture specific Unmask extensiom
+ */
+static void wakeupgen_unmask(struct irq_data *d)
+{
+
+	spin_lock(&wakeupgen_lock);
+	_wakeupgen_set(d->irq, 1);
+	spin_unlock(&wakeupgen_lock);
+}
+
+#ifdef CONFIG_PM
+/*
+ * Architecture specific set_wake extension
+ */
+static int wakeupgen_set_wake(struct irq_data *d, unsigned int on)
+{
+	spin_lock(&wakeupgen_lock);
+	_wakeupgen_set(d->irq, on);
+	spin_unlock(&wakeupgen_lock);
+
+	return 0;
+}
+
+#else
+#define wakeupgen_set_wake	NULL
+#endif
+
+/**
+ * omap_wakeupgen_irqmask_all() -  Mask or unmask interrupts
+ * @cpu - CPU ID
+ * @set - The IRQ register mask.
+ *	0 = Mask all interrupts on the 'cpu'
+ *	1 = Unmask all interrupts on the 'cpu'
+ *
+ * Ensure that the initial mask is maintained. This is faster than
+ * iterating through GIC rgeisters to arrive at the correct masks
+ */
+void omap_wakeupgen_irqmask_all(unsigned int cpu, unsigned int set)
+{
+	if (omap_rev() == OMAP4430_REV_ES1_0)
+		return;
+
+	spin_lock(&wakeupgen_lock);
+	if (set) {
+		_wakeupgen_save_masks(cpu);
+		_wakeupgen_set_all(cpu, WKG_MASK_ALL);
+	} else {
+		_wakeupgen_set_all(cpu, WKG_UNMASK_ALL);
+		_wakeupgen_restore_masks(cpu);
+	}
+	spin_unlock(&wakeupgen_lock);
+}
+
+/*
+ * Initialse the wakeupgen module
+ */
+int __init omap_wakeupgen_init(void)
+{
+	u8 i;
+
+	/* Not supported on on OMAP4 ES1.0 silicon */
+	if (omap_rev() == OMAP4430_REV_ES1_0) {
+		WARN(1, "WakeupGen: Not supported on OMAP4430 ES1.0\n");
+		return -EPERM;
+	}
+
+	/* Static mapping, never released */
+	wakeupgen_base = ioremap(OMAP44XX_WKUPGEN_BASE, SZ_4K);
+	BUG_ON(!wakeupgen_base);
+
+	/* Clear all IRQ bitmasks at wakeupGen level */
+	for (i = 0; i < NR_BANKS; i++) {
+		cpu_writel(0, i, CPU0_ID);
+		cpu_writel(0, i, CPU1_ID);
+	}
+
+	/*
+	 * Override gic architecture specific fucntioms to add
+	 * OMAP WakeupGen interrupt controller along with GIC
+	 */
+	gic_arch_extn.irq_mask = wakeupgen_mask;
+	gic_arch_extn.irq_unmask = wakeupgen_unmask;
+	gic_arch_extn.irq_set_wake = wakeupgen_set_wake;
+
+	return 0;
+}
diff --git a/arch/arm/mach-omap2/omap4-common.c b/arch/arm/mach-omap2/omap4-common.c
index 1926864..559d227 100644
--- a/arch/arm/mach-omap2/omap4-common.c
+++ b/arch/arm/mach-omap2/omap4-common.c
@@ -21,6 +21,7 @@
 
 #include <mach/hardware.h>
 #include <mach/omap4-common.h>
+#include <mach/omap-wakeupgen.h>
 
 #ifdef CONFIG_CACHE_L2X0
 void __iomem *l2cache_base;
@@ -41,6 +42,8 @@ void __init gic_init_irq(void)
 	gic_cpu_base = ioremap(OMAP44XX_GIC_CPU_BASE, SZ_512);
 	BUG_ON(!gic_cpu_base);
 
+	omap_wakeupgen_init();
+
 	gic_init(0, 29, gic_dist_base_addr, gic_cpu_base);
 }
 
-- 
1.6.0.4

^ permalink raw reply related

* [PATCH 00/17] omap4: pm: suspend, hotplug and cpuilde support
From: Santosh Shilimkar @ 2011-02-19 10:42 UTC (permalink / raw)
  To: linux-arm-kernel

This series adds OMAP4 suspend and cpuidle support till MPU subsystem
(MPUSS) off-mode. The suspend on SMP machines uses cpu-hotplug 
infrastructure to take down the non-boot CPUs. We put secondary
CPU(CPU1 in OMAP4) to OFF state via cpu-hotplug.
In cpuidle too, low power states are attempted only when the
CPU1 is put to OFF state via cpu-hotplug because of hardware
constraints.

Timer wakeup from suspend, debug pm counters and enable_off_mode
provisions are supported as well.

Special thanks to Kevin Hilman <khilman@ti.com> for doing detail
off-the list reviews.

The patches are generated against mainline 2.6.38-rc5 and tested with
OMAP4430 SDP and OMAP4 PANDA board. Any OMAP4 board with ES2.X silicon,
below features should work with this series. On ES1.0, these PM 
features are not supported.
	1. CPU hotplug (CPU is put into off-mode)
	2. Suspend (Both CPUs put to off-mode and MPUSS to OFF/RET)
	3. CPUILDE with below C-states.
		C1 - CPU0 ON + CPU1 ON/OFF + MPU ON + CORE ON
		C2 - CPU0 ON + CPU1 OFF + MPU ON + CORE ON
		C3 - CPU0 OFF + CPU1 OFF + MPU CSWR + CORE ON
		C4 - CPU0 OFF + CPU1 OFF + MPU OFF + CORE ON

In OMAP4 mpuss consist of dual Cortex-A9 with per-cpu local timers
GIC(Generic Interrupt Controller), SCU(Snoop Control Unit) and PL310
L2 cache controller and  CPU0/CPU1 LPRM modules.
CPU0, CPU1 and MPUSS have there own power domain and hence multiple
low power state combinations are possible. The CPU10 and CPU1
Close switch Retention(CSWR) isn't supported by hardware.
Based on various studies, measurements, hardware constraints
and recommendations from hardware team, only below low power
modes are supported on OMAP4.
----------------------------------------
CPU0		CPU1		MPUSS
----------------------------------------
ON		ON		ON
OFF		OFF		CSWR
OFF		OFF		OSWR 
OFF		OFF		OFF
-----------------------------------------
Note: CPU0 is the master core and it is the last CPU to go down
and first to wake-up when MPUSS low power states are attempted

OSWR(Open Switch Retention) is not added as part of this series
because it needs some power domain level support which isn't ready
yet.
	http://www.mail-archive.com/linux-omap at vger.kernel.org/msg38667.html

Powerdomain INACTIVE support is also dropped because of its 
inconsistency between OMAP4 and OMAP3.
More information  on this thread -
	http://www.spinics.net/lists/linux-omap/msg45370.html

This series has a dependency on few patches from below series which
are posted earlier.
	- Clock domain split series [1] (Already in Paul's queue)  
	- Few power domain patches from [2] (Already in Paul's queue)
	- GIC and SCU patches from [3]
	- Local Timer patches from [4]

The cpu-hotplug and suspend works with omap2plus_defconfig. Not to damage
your file system with current omap2plus_defconfig, disable ARCH_OMAP2 so
that V6 and V7 support is not built together with SMP.
To tryout cpuidle, CONFIG_CPU_IDLE needs to be enabled in the build.

CPU-HOTPLUG commands :
	offline : $echo 0 > /sys/devices/system/cpu/cpu1/online
	online : $echo 1 > /sys/devices/system/cpu/cpu1/online

Suspend :$echo mem > /sys/power/state

cpuilde : To trigger cpuidle deeper C-states on OMAP4, CPU1 needs
	to be offlied
	$echo 0 > /sys/devices/system/cpu/cpu1/online

To see PM debug counters,
	$mount -t debugfs debugfs /proc/sys/debug/
	$cat /proc/sys/debug/pm_debug/count
off-mode debugfs control:
	enable: $echo 1 > /proc/sys/debug/pm_debug/enable_off_mode
	disable: $echo 0 > /proc/sys/debug/pm_debug/enable_off_mode

For testing, I have put together all these patches and dependencies
on below git branch. 

git://dev.omapzoom.org/pub/scm/santosh/kernel-omap4-base.git omap4_pm_fornext

Rajendra Nayak (1):
  omap4: cpuidle: Basic CPUidle support

Santosh Shilimkar (16):
  omap4: pm: Add omap WakeupGen module support
  omap4: pm: Add SAR RAM support
  omap4: Export scu base address
  omap4: pm: Add CPUx OFF mode support
  omap4: pm: Initialise all the clockdomains to supported states
  omap4: pm: Program CPU1 to hit OFF when off-lined
  omap4: pm: CPU1 wakeup workaround form Low power modes
  omap4: pm: Add GIC save/restore support
  omap4: pm: Add WakeupGen save/restore support
  omap4: pm: Add L2 cache lowpower support
  omap4: suspend: Add MPUSS RET and OFF support
  omap4: pm-debug: Add wakeup timer and debug counters
  omap4: cpuidle: Add MPUSS RET OFF states
  omap4: cpuidle: Switch to gptimer from twd in deeper C-states.
  omap4: cpuidle: Allow debugfs control through enable_off_mode
  omap4: Remove un-used do_wfi() macro.

 arch/arm/mach-omap2/Makefile                      |    8 +-
 arch/arm/mach-omap2/cpuidle44xx.c                 |  365 +++++++++++++++++
 arch/arm/mach-omap2/include/mach/omap-wakeupgen.h |   41 ++
 arch/arm/mach-omap2/include/mach/omap4-common.h   |   56 +++-
 arch/arm/mach-omap2/omap-hotplug.c                |   31 ++-
 arch/arm/mach-omap2/omap-smp.c                    |   29 ++-
 arch/arm/mach-omap2/omap-wakeupgen.c              |  287 +++++++++++++
 arch/arm/mach-omap2/omap4-common.c                |   28 ++-
 arch/arm/mach-omap2/omap4-mpuss-lowpower.c        |  442 +++++++++++++++++++++
 arch/arm/mach-omap2/omap4-sar-layout.h            |   71 ++++
 arch/arm/mach-omap2/pm-debug.c                    |    8 +-
 arch/arm/mach-omap2/pm.h                          |    3 +
 arch/arm/mach-omap2/pm44xx.c                      |  125 ++++++-
 arch/arm/mach-omap2/sleep44xx.S                   |  398 +++++++++++++++++++
 arch/arm/plat-omap/include/plat/omap44xx.h        |    1 +
 15 files changed, 1870 insertions(+), 23 deletions(-)
 create mode 100644 arch/arm/mach-omap2/cpuidle44xx.c
 create mode 100644 arch/arm/mach-omap2/include/mach/omap-wakeupgen.h
 create mode 100644 arch/arm/mach-omap2/omap-wakeupgen.c
 create mode 100644 arch/arm/mach-omap2/omap4-mpuss-lowpower.c
 create mode 100644 arch/arm/mach-omap2/omap4-sar-layout.h
 create mode 100644 arch/arm/mach-omap2/sleep44xx.S


Regrads,
Santosh
[1] http://www.spinics.net/lists/linux-omap/msg44153.html
[2] http://www.mail-archive.com/linux-omap at vger.kernel.org/msg43257.html
[3] http://www.mail-archive.com/linux-omap at vger.kernel.org/msg42939.html
[4] http://www.spinics.net/lists/linux-omap/msg45710.html

^ permalink raw reply

* [PATCH 1/2] PRUSS UIO driver support
From: TK, Pratheesh Gangadhar @ 2011-02-19 10:19 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <201102181644.17634.arnd@arndb.de>

> -----Original Message-----
> From: Arnd Bergmann [mailto:arnd at arndb.de]
> Sent: Friday, February 18, 2011 9:14 PM
> To: linux-arm-kernel at lists.infradead.org
> Cc: TK, Pratheesh Gangadhar; davinci-linux-open-
> source at linux.davincidsp.com; gregkh at suse.de; Chatterjee, Amit;
> hjk at linutronix.de; linux-kernel at vger.kernel.org
> Subject: Re: [PATCH 1/2] PRUSS UIO driver support
> 
> On Friday 18 February 2011, Pratheesh Gangadhar wrote:
> > Signed-off-by: Pratheesh Gangadhar <pratheesh@ti.com>
> >
> > This patch implements PRUSS (Programmable Real-time Unit Sub System)
> > UIO driver which exports SOC resources associated with PRUSS like
> > I/O, memories and IRQs to user space. PRUSS is dual 32-bit RISC
> > processors which is efficient in performing embedded tasks that
> > require manipulation of packed memory mapped data structures and
> > efficient in handling system events that have tight real time
> > constraints. This driver is currently supported on Texas Instruments
> > DA850, AM18xx and OMAPL1-38 devices.
> > For example, PRUSS runs firmware for real-time critical industrial
> > communication data link layer and communicates with application stack
> > running in user space via shared memory and IRQs.
> 
> Looks basically ok, but there are two limitations that I see that you
> might consider fixing.
> 
> Oh, and you should put the Signed-off-by statement below the changelog,
> not above it, but that has nothing to do with the code.
> 
> > +/*
> > + * Host event IRQ numbers from PRUSS
> > + * 3 PRU_EVTOUT0 PRUSS Interrupt
> > + * 4 PRU_EVTOUT1 PRUSS Interrupt
> > + * 5 PRU_EVTOUT2 PRUSS Interrupt
> > + * 6 PRU_EVTOUT3 PRUSS Interrupt
> > + * 7 PRU_EVTOUT4 PRUSS Interrupt
> > + * 8 PRU_EVTOUT5 PRUSS Interrupt
> > + * 9 PRU_EVTOUT6 PRUSS Interrupt
> > + * 10 PRU_EVTOUT7 PRUSS Interrupt
> > +*/
> > +
> > +#define MAX_PRUSS_EVTOUT_INSTANCE	(8)
> > +
> > +static struct clk *pruss_clk;
> > +static struct uio_info *info[MAX_PRUSS_EVTOUT_INSTANCE];
> > +static void *ddr_virt_addr;
> > +static dma_addr_t ddr_phy_addr;
> 
> By making all of these static variables, you limit youself to
> a single PRUSS instance in the system. It's generally better
> to write device drivers in a way that makes it possible to
> have multiple instances, e.g. by moving these four variables
> into the 'priv' part of struct uio_info.

Ok, I agree with making them non-static. Making them part of uio_info might
not be desired. PRUSS_EVOUT_INSTANCE is not same as PRUSS instances. Each
PRUSS can have up to 8 event out interrupt lines to host ARM.
> 
> > +static irqreturn_t pruss_handler(int irq, struct uio_info *dev_info)
> > +{
> > +	return IRQ_HANDLED;
> > +}
> 
> An empty interrupt handler is rather pointless. I guess you really
> notify user space when the interrupt handler gets called, as this
> is the main point of a UIO driver as far as I understand it.
> 
As discussed in the later E-mails, UIO core takes care of this.
Will cover this in following responses.

Thanks,
Pratheesh

^ permalink raw reply

* platform/i2c busses: pm runtime and system sleep
From: Linus Walleij @ 2011-02-19 10:16 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20110219100017.GA29493@n2100.arm.linux.org.uk>

2011/2/19 Russell King - ARM Linux <linux@arm.linux.org.uk>:
> [Me]
>> Both of these problems are solved by elegance if we use runtime
>> PM, since it will provide a hysteresis timeout that can be triggered
>> from interrupt context and call the idling hooks in process context.
>
> So what's the interdependence with the platform bus that was being talked
> about earlier in this thread?

That's about consistency of runtime PM semantics across
different buses as I understand it.

We have both platform bus and AMBA bus devices in the system,
so it is desireable if the semantics of their runtime PM are identical.

If I understand it, the difference is that the platform bus will call
runtime_suspend() on the device even if it was already in suspended
state, so the question is about whether the AMBA runtime PM
should do this too since it is similar to the platform bus, or if it should
go for the more intutive approach of not suspending suspended
hardware.

I think the current patch from Rabin as it stands does the latter, and
is good as it stands. It's the other buses and their drivers that
need patching.

Yours,
Linus Walleij

^ permalink raw reply

* platform/i2c busses: pm runtime and system sleep
From: Russell King - ARM Linux @ 2011-02-19 10:00 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <AANLkTim2TfG-18XN3r9m3Rq68ZD6QHUY7X82E4-XWH83@mail.gmail.com>

On Sat, Feb 19, 2011 at 10:54:57AM +0100, Linus Walleij wrote:
> 2011/2/18 Russell King - ARM Linux <linux@arm.linux.org.uk>:
> 
> > Do we have any pressing need to convert AMBA stuff? ?I haven't heard any
> > reason yet to convert them to runtime PM - they don't even make any
> > runtime PM calls.
> >
> > Maybe Linus can comment on the PM stuff as he has SoCs with these in.
> > As my boards don't have any sensible PM support, I don't have any
> > visibility of what PM facilities would be required.
> 
> Sure, basically I ACK Rabins patch and his reasoning for it. (BTW
> Rabin spends most of his days working on the Ux500 SoCs too.)
> 
> The runtime PM we need for Ux500 is to switch off silicon core
> voltage first and foremost. The call I've added to switch of a core
> voltage regulator will need to be called when the silicon is idle.
> 
> In spi/amba-pl022.c I take the most brutal approach with a recent
> patch: hammer off this core switch (and clock) whenever the hardware
> is not used. This is simple in this driver since it has no state to preserve
> across transfers, it is written such that the core is loaded with the
> appropriate state for each message.
> 
> Continuing this approach we run into two problems with this
> and other drivers:
> 
> -  Hammering off/on the clock+voltage is causing delays in HW
>    so what you want is some hysteresis (usually, wait a few us/ms
>    then switch off) - sort of a takeoff/landing effect.
> 
> -  Modelling voltage domains as regulators is nice, but require
>    us to switch on/off from process context, so we cannot do this
>    from interrupt handlers.
> 
> Both of these problems are solved by elegance if we use runtime
> PM, since it will provide a hysteresis timeout that can be triggered
> from interrupt context and call the idling hooks in process context.

So what's the interdependence with the platform bus that was being talked
about earlier in this thread?

^ permalink raw reply

* platform/i2c busses: pm runtime and system sleep
From: Linus Walleij @ 2011-02-19  9:54 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20110218202744.GA19427@n2100.arm.linux.org.uk>

2011/2/18 Russell King - ARM Linux <linux@arm.linux.org.uk>:

> Do we have any pressing need to convert AMBA stuff? ?I haven't heard any
> reason yet to convert them to runtime PM - they don't even make any
> runtime PM calls.
>
> Maybe Linus can comment on the PM stuff as he has SoCs with these in.
> As my boards don't have any sensible PM support, I don't have any
> visibility of what PM facilities would be required.

Sure, basically I ACK Rabins patch and his reasoning for it. (BTW
Rabin spends most of his days working on the Ux500 SoCs too.)

The runtime PM we need for Ux500 is to switch off silicon core
voltage first and foremost. The call I've added to switch of a core
voltage regulator will need to be called when the silicon is idle.

In spi/amba-pl022.c I take the most brutal approach with a recent
patch: hammer off this core switch (and clock) whenever the hardware
is not used. This is simple in this driver since it has no state to preserve
across transfers, it is written such that the core is loaded with the
appropriate state for each message.

Continuing this approach we run into two problems with this
and other drivers:

-  Hammering off/on the clock+voltage is causing delays in HW
   so what you want is some hysteresis (usually, wait a few us/ms
   then switch off) - sort of a takeoff/landing effect.

-  Modelling voltage domains as regulators is nice, but require
   us to switch on/off from process context, so we cannot do this
   from interrupt handlers.

Both of these problems are solved by elegance if we use runtime
PM, since it will provide a hysteresis timeout that can be triggered
from interrupt context and call the idling hooks in process context.

Yours,
Linus Walleij

^ permalink raw reply

* MMC quirks relating to performance/lifetime.
From: Arnd Bergmann @ 2011-02-19  9:54 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <AANLkTi=rg2Du2RbakqpRNZZ7HHkhrsobz713YL29Wqoi@mail.gmail.com>

On Friday 18 February 2011 23:40:16 Andrei Warkentin wrote:
> On Fri, Feb 18, 2011 at 1:47 PM, Andrei Warkentin <andreiw@motorola.com> wrote:
>
> Flashbench timings for both Sandisk and Toshiba cards. Attaching due to size.

Very nice, thanks for the measurement!

I don't think having the results inline in the mail is a problem,
it would even make it easier to quote.
 
> Some interesting things that I don't understand. For the align test, I
> extended it to do a write align test (-A). I tried two partitions that
> I could write over, and both read and writes behaved differently for
> the two partitions on same device. Odd. They are both 4MB aligned.

I never did a write align test because the results will be highly
unreliable as soon as you get into thrashing. Your results seem
to be meaningful still, so maybe we should have it after all, but
I'll put a big warning on it.

> On the sandisk it was the write align that made the page size stand
> out.  The read align had pretty constant results.

I've noticed on other Sandisk media that the read align test is
sometimes useless. It may help to do a full erase of the partition,
or to fill it with data before running the test.

> On the toshiba the results varied wildly for the two partitions. For
> partition 6, there was a clear pattern in the diff values for read
> align. For 9, it was all over the place. For 9 with the write align,
> 8K and 16K the crossing writes took ~115ms!! Look in attached files
> for all the data.

Partition 6 is a lot smaller, so you have the accesses less than a
segment apart, so it shows other effects.

> The AU tests were interesting too, especially how with several open
> AUs the throughput is higher for certain smaller sizes on sandisk, but
> if I interpret it correctly both cards have at least 4 AUs, as I
> didn't see yet a significant drop for small sizes. The larger ones I
> am running now on mmcblk0p9 which is sufficiently larger for these
> tests... (mmcblk0p6 is only 40mb, p9 is 314 mb)

Right, you should try larger values for --open-au-nr here. It's at
least a good sign that the drive can do random access inside a segment
and that it can have at least 4 segments open. This is much better
than I expected from your descriptions at first.

However, the drop from 32 KB to 16 KB in performance is horrifying
for the Toshiba drive, it's clear that this one does not like
to be accessed smaller than 32 KB at a time, an obvious optimization
for FAT32 with 32 KB clusters. How does this change with your
kernel patches?

For the sandisk drive, it's funny how it is consistently faster
doing random access than linear access. I don't think I've seem that
before. It does seem to have some cache for linear access using
smaller than 16 KB, and can probably combine them when it's only
writing to a single segment.

	Arnd

^ permalink raw reply

* platform/i2c busses: pm runtime and system sleep
From: Rabin Vincent @ 2011-02-19  7:24 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20110218202744.GA19427@n2100.arm.linux.org.uk>

On Sat, Feb 19, 2011 at 01:57, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> Do we have any pressing need to convert AMBA stuff? ?I haven't heard any
> reason yet to convert them to runtime PM - they don't even make any
> runtime PM calls.
>
> Maybe Linus can comment on the PM stuff as he has SoCs with these in.
> As my boards don't have any sensible PM support, I don't have any
> visibility of what PM facilities would be required.

The rationale for runtime power control is the same as that for
65500fa94aaeb3 "ARM: 6467/1: amba: optional PrimeCell core voltage
switch".

As compared to the regulator API which that patch is using, the runtime
pm usage is more flexible (for example allowing certain power control
APIs to be called from atomic context), provides callbacks for
asynchronous turnoff with callbacks back to the driver to save/restore
state (runtime_suspend()/runtime_resume()), and provides core support
for things like "autosuspend" which allows delaying suspend until some
time after last inactivity.  Using runtime PM also allows use of the new
device-level power domain support ("PM: Add support for device power
domains", in -next) to easily implement SoC-specific handling.

We need to first add bus support for this to allow drivers to use this
API.  It is possible to make the AMBA patch smaller and touch only the
AMBA bus code by implementing support for the legacy bus-specific
suspend/resume calls, and drivers can be later converted to pm-ops as
needed.

^ permalink raw reply

* [PATCH 16/17] mc13xxx: mfd_cell is now implicitly available to drivers
From: Fabio Estevam @ 2011-02-19  3:31 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20110217131025.GO22310@pengutronix.de>

Hi Uwe,

2011/2/17 Uwe Kleine-K?nig <u.kleine-koenig@pengutronix.de>:
...
>> I built it as module and the build went fine.
> And you did a runtime test, too?
>
> If you did and that went smooth, I'll take a look again, because I
> expected it to barf.

I haven?t performed a runtime test yet. I am travelling today and will
be one week out of the office, but I can test it when I return.

Regards,

Fabio Estevam

^ permalink raw reply

* [PATCH] msm: headsmp.S: Fix section mismatch
From: Stephen Boyd @ 2011-02-19  3:14 UTC (permalink / raw)
  To: linux-arm-kernel

WARNING: vmlinux.o(.cpuinit.text+0xc80): Section mismatch in
reference from the function boot_secondary() to the variable
.init.text:msm_secondary_startup
The function __cpuinit boot_secondary() references a variable
__init msm_secondary_startup.  If msm_secondary_startup is only
used by boot_secondary then annotate msm_secondary_startup with
a matching annotation.

Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
 arch/arm/mach-msm/headsmp.S |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/arch/arm/mach-msm/headsmp.S b/arch/arm/mach-msm/headsmp.S
index d0c2143..0c631a9 100644
--- a/arch/arm/mach-msm/headsmp.S
+++ b/arch/arm/mach-msm/headsmp.S
@@ -11,7 +11,7 @@
 #include <linux/linkage.h>
 #include <linux/init.h>
 
-	__INIT
+	__CPUINIT
 
 /*
  * MSM specific entry point for secondary CPUs.  This provides
-- 
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 related

* [PATCH 3/7] Add i.MX5 framebuffer driver
From: Jason Chen @ 2011-02-19  2:22 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20110218095221.GK24426@pengutronix.de>

hi, Sascha,

2011/2/18 Sascha Hauer <s.hauer@pengutronix.de>:
> Hi Jason,
>
> On Fri, Feb 18, 2011 at 05:22:09PM +0800, Jason Chen wrote:
>> hi, Sasha,
>>
>> > + ? ? ? if (var->vmode & FB_VMODE_ODD_FLD_FIRST) /* PAL */
>> > + ? ? ? ? ? ? ? sig_cfg.odd_field_first = 1;
>> > + ? ? ? if (var->sync & FB_SYNC_EXT)
>> > + ? ? ? ? ? ? ? sig_cfg.ext_clk = 1;
>> > + ? ? ? if (var->sync & FB_SYNC_HOR_HIGH_ACT)
>> > + ? ? ? ? ? ? ? sig_cfg.Hsync_pol = 1;
>> Please remove FB_SYNC_EXT support.
>
> No problem, can do. But why? Is it nonfunctional in the hardware
> or is it that the current code just misses more pieces to support
> this?
>
This flag represents external sync, some application will not check
this flag when they reset fb var, for example, X server fb driver will
ignore it, if so, the ipu external clock can not be set correctly.
I think ipu should decide external clock usage by other method. The
latest method in freesacle mainline is decide it automatically -- if
internal clock can not fit target clock rate well, switch it to
external.
>>
>> +static void imx_ipu_fb_disable_overlay(struct fb_info *ovl)
>> +{
>> + ? ? ? struct imx_ipu_fb_info *mxc_ovl = ovl->par;
>> +
>> + ? ? ? if (!mxc_ovl->enabled)
>> + ? ? ? ? ? ? ? return;
>> +
>> + ? ? ? ipu_dp_disable_fg(mxc_ovl->dp);
>> + ? ? ? ipu_wait_for_interrupt(451, 100);
>> + ? ? ? ipu_idmac_disable_channel(mxc_ovl->ipu_ch);
>> + ? ? ? ipu_dmfc_disable_channel(mxc_ovl->dmfc);
>> + ? ? ? mxc_ovl->enabled = 0;
>> +}
>> Had better has a definition of ipu irq 451.
>
> Ok.
>
> Sascha
>
> --
> Pengutronix e.K. ? ? ? ? ? ? ? ? ? ? ? ? ? | ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
> Industrial Linux Solutions ? ? ? ? ? ? ? ? | http://www.pengutronix.de/ ?|
> Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 ? ?|
> Amtsgericht Hildesheim, HRA 2686 ? ? ? ? ? | Fax: ? +49-5121-206917-5555 |
>

^ permalink raw reply

* [PATCH 1/3] ARM: Tegra: Add pll_d table entries for 252MHz
From: Stephen Warren @ 2011-02-18 23:48 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <AANLkTimp==gtU2jvCYExqoV6tv-_dgvsF0yAf_k-F7CX@mail.gmail.com>

Erik Gilling wrote at Friday, February 18, 2011 4:46 PM:
>
> These look good.  Have you run these through a CTS tester at 720x480
> and 1280x720?  We pass those now and I want to make sure there's not
> regressions.  We're supposed to be getting one in a week or two.  If
> you haven't run it by then, I'll run a pass with these then merge.

Unfortunately, I don't have one available, so no.

(although I don't believe I made any changes to clocking, CTS, or N
for those two modes, just 640x480).

Thanks for the review and offer to test!

> 
> Cheers,
>    Erik
> 
> On Fri, Feb 18, 2011 at 3:18 PM, Stephen Warren <swarren@nvidia.com>
> wrote:
> > This rate is required for correct generation of 640x480 HDMI video;
> > see subsequent patches.
> >
> > Note: I tested the entry with 12MHz input. The other entries are untested,
> > but appear logically correct. However, I'm not familiar enough with PLLs
> > to know if they perhaps exceed the PLL VCO frequency, nor whether the
> > cpcon values are strictly correct.
> >
> > Signed-off-by: Stephen Warren <swarren@nvidia.com>
> > ---
> > ?arch/arm/mach-tegra/tegra2_clocks.c | ? ?5 +++++
> > ?1 files changed, 5 insertions(+), 0 deletions(-)

-- 
Nvpublic

^ permalink raw reply

* [PATCH 1/3] ARM: Tegra: Add pll_d table entries for 252MHz
From: Erik Gilling @ 2011-02-18 23:45 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1298071093-23865-2-git-send-email-swarren@nvidia.com>

These look good.  Have you run these through a CTS tester at 720x480
and 1280x720?  We pass those now and I want to make sure there's not
regressions.  We're supposed to be getting one in a week or two.  If
you haven't run it by then, I'll run a pass with these then merge.

Cheers,
   Erik

On Fri, Feb 18, 2011 at 3:18 PM, Stephen Warren <swarren@nvidia.com> wrote:
> This rate is required for correct generation of 640x480 HDMI video;
> see subsequent patches.
>
> Note: I tested the entry with 12MHz input. The other entries are untested,
> but appear logically correct. However, I'm not familiar enough with PLLs
> to know if they perhaps exceed the PLL VCO frequency, nor whether the
> cpcon values are strictly correct.
>
> Signed-off-by: Stephen Warren <swarren@nvidia.com>
> ---
> ?arch/arm/mach-tegra/tegra2_clocks.c | ? ?5 +++++
> ?1 files changed, 5 insertions(+), 0 deletions(-)
>
> diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
> index 41f5953..f3ead19 100644
> --- a/arch/arm/mach-tegra/tegra2_clocks.c
> +++ b/arch/arm/mach-tegra/tegra2_clocks.c
> @@ -1531,6 +1531,11 @@ static struct clk_pll_freq_table tegra_pll_d_freq_table[] = {
> ? ? ? ?{ 19200000, 216000000, 135, 12, 1, 3},
> ? ? ? ?{ 26000000, 216000000, 216, 26, 1, 4},
>
> + ? ? ? { 12000000, 252000000, 252, 12, 1, 4},
> + ? ? ? { 13000000, 252000000, 252, 13, 1, 4},
> + ? ? ? { 19200000, 252000000, 210, 16, 1, 3},
> + ? ? ? { 26000000, 252000000, 252, 26, 1, 4},
> +
> ? ? ? ?{ 12000000, 594000000, 594, 12, 1, 8},
> ? ? ? ?{ 13000000, 594000000, 594, 13, 1, 8},
> ? ? ? ?{ 19200000, 594000000, 495, 16, 1, 8},
> --
> 1.7.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

* [PATCH 3/3] video: tegra: Run pll_d at 252mhz for HDMI 640x480
From: Stephen Warren @ 2011-02-18 23:18 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1298071093-23865-1-git-send-email-swarren@nvidia.com>

Using a pll_d clock rate of 216MHz yields a 24MHz pixel clock instead of
25.2MHz. This is just under a 5% error. Instead, use a 252MHz pll_d rate,
which generates 25.2MHz exactly.

This makes it more likely that monitors will sync to the emitted signal.

This also fixes HDMI audio issues, since the CTS/N tables are now based
on the actual pixel clock rate, not merely the intended pixel clock rate.

Tested with a 12MHz pll_m rate on Seaboard, all of 32/44.1/48KHz audio,
all video modes supported by the Tegra dc HDMI driver, and a Dell U2410
dislay.

Signed-off-by: Stephen Warren <swarren@nvidia.com>
---
 drivers/video/tegra/dc/dc.c |    4 +++-
 1 files changed, 3 insertions(+), 1 deletions(-)

diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c
index 16789c2..c1eea05 100644
--- a/drivers/video/tegra/dc/dc.c
+++ b/drivers/video/tegra/dc/dc.c
@@ -699,8 +699,10 @@ void tegra_dc_setup_clk(struct tegra_dc *dc, struct clk *clk)
 
 		if (dc->mode.pclk > 70000000)
 			rate = 594000000;
-		else
+		else if (dc->mode.pclk >= 27000000)
 			rate = 216000000;
+		else
+			rate = 252000000;
 
 		if (rate != clk_get_rate(pll_d_clk))
 			clk_set_rate(pll_d_clk, rate);
-- 
1.7.1

^ permalink raw reply related

* [PATCH 2/3] video: tegra: Fix CTS/N table for 25.2MHz pclk, 44.1Hz audio
From: Stephen Warren @ 2011-02-18 23:18 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1298071093-23865-1-git-send-email-swarren@nvidia.com>

The existing values are for a 25.25MHz clock, not a 25.20MHz clock:

25250000 * 14112 / (128 * 63125) == 44100.000
25200000 * 5656  / (128 * 25250) == 44100.000

Signed-off-by: Stephen Warren <swarren@nvidia.com>
---
 drivers/video/tegra/dc/hdmi.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/video/tegra/dc/hdmi.c b/drivers/video/tegra/dc/hdmi.c
index 878cca5..9210737 100644
--- a/drivers/video/tegra/dc/hdmi.c
+++ b/drivers/video/tegra/dc/hdmi.c
@@ -152,7 +152,7 @@ const struct tegra_hdmi_audio_config tegra_hdmi_audio_32k[] = {
 };
 
 const struct tegra_hdmi_audio_config tegra_hdmi_audio_44_1k[] = {
-	{25200000,	14112,	63125},
+	{25200000,	5656,	25250},
 	{27000000,	6272,	30000},
 	{54000000,	6272,	60000},
 	{74250000,	6272,	82500},
-- 
1.7.1

^ permalink raw reply related

* [PATCH 1/3] ARM: Tegra: Add pll_d table entries for 252MHz
From: Stephen Warren @ 2011-02-18 23:18 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1298071093-23865-1-git-send-email-swarren@nvidia.com>

This rate is required for correct generation of 640x480 HDMI video;
see subsequent patches.

Note: I tested the entry with 12MHz input. The other entries are untested,
but appear logically correct. However, I'm not familiar enough with PLLs
to know if they perhaps exceed the PLL VCO frequency, nor whether the
cpcon values are strictly correct.

Signed-off-by: Stephen Warren <swarren@nvidia.com>
---
 arch/arm/mach-tegra/tegra2_clocks.c |    5 +++++
 1 files changed, 5 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index 41f5953..f3ead19 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -1531,6 +1531,11 @@ static struct clk_pll_freq_table tegra_pll_d_freq_table[] = {
 	{ 19200000, 216000000, 135, 12, 1, 3},
 	{ 26000000, 216000000, 216, 26, 1, 4},
 
+	{ 12000000, 252000000, 252, 12, 1, 4},
+	{ 13000000, 252000000, 252, 13, 1, 4},
+	{ 19200000, 252000000, 210, 16, 1, 3},
+	{ 26000000, 252000000, 252, 26, 1, 4},
+
 	{ 12000000, 594000000, 594, 12, 1, 8},
 	{ 13000000, 594000000, 594, 13, 1, 8},
 	{ 19200000, 594000000, 495, 16, 1, 8},
-- 
1.7.1

^ permalink raw reply related

* [PATCH 0/3] Tegra 640x480 HDMI audio patches
From: Stephen Warren @ 2011-02-18 23:18 UTC (permalink / raw)
  To: linux-arm-kernel

This patch set fixes 640x480 HDMI video clocking, audio over HDMI at 640x80,
and especially 44.1KHz audio at that resolution.

It was generated against linux-tegra-2.6.37, but probably is worth applying
to, or merging into, all of:

for-next
android-tegra-2.6.37
linux-tegra-2.6.37
android-tegra-2.6.36
linux-tegra-2.6.36

Stephen Warren (3):
  ARM: Tegra: Add pll_d table entries for 252MHz
  video: tegra: Fix CTS/N table for 25.2MHz pclk, 44.1Hz audio
  video: tegra: Run pll_d at 252mhz for HDMI 640x480

 arch/arm/mach-tegra/tegra2_clocks.c |    5 +++++
 drivers/video/tegra/dc/dc.c         |    4 +++-
 drivers/video/tegra/dc/hdmi.c       |    2 +-
 3 files changed, 9 insertions(+), 2 deletions(-)

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox