From patchwork Mon Mar 28 09:22:26 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Santosh Shilimkar X-Patchwork-Id: 667751 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id p2S9RAFD004341 for ; Mon, 28 Mar 2011 09:27:12 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752112Ab1C1JXC (ORCPT ); Mon, 28 Mar 2011 05:23:02 -0400 Received: from comal.ext.ti.com ([198.47.26.152]:34459 "EHLO comal.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751931Ab1C1JW7 (ORCPT ); Mon, 28 Mar 2011 05:22:59 -0400 Received: from dbdp31.itg.ti.com ([172.24.170.98]) by comal.ext.ti.com (8.13.7/8.13.7) with ESMTP id p2S9MfTr025955 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Mon, 28 Mar 2011 04:22:44 -0500 Received: from linfarm476.india.ti.com (localhost [127.0.0.1]) by dbdp31.itg.ti.com (8.13.8/8.13.8) with ESMTP id p2S9Mebr002315; Mon, 28 Mar 2011 14:52:40 +0530 (IST) Received: from linfarm476.india.ti.com (localhost [127.0.0.1]) by linfarm476.india.ti.com (8.12.11/8.12.11) with ESMTP id p2S9MeGJ002805; Mon, 28 Mar 2011 14:52:40 +0530 Received: (from a0393909@localhost) by linfarm476.india.ti.com (8.12.11/8.12.11/Submit) id p2S9Mejw002803; Mon, 28 Mar 2011 14:52:40 +0530 From: Santosh Shilimkar To: linux-omap@vger.kernel.org Cc: khilman@ti.com, rnayak@ti.com, linux-arm-kernel@lists.infradead.org, Santosh Shilimkar Subject: [pm-core][PATCH v3 10/21] OMAP4: PM: Add GIC save/restore support Date: Mon, 28 Mar 2011 14:52:26 +0530 Message-Id: <1301304157-2466-11-git-send-email-santosh.shilimkar@ti.com> X-Mailer: git-send-email 1.5.6.6 In-Reply-To: <1301304157-2466-1-git-send-email-santosh.shilimkar@ti.com> References: <1301304157-2466-1-git-send-email-santosh.shilimkar@ti.com> Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter1.kernel.org [140.211.167.41]); Mon, 28 Mar 2011 09:27:12 +0000 (UTC) diff --git a/arch/arm/mach-omap2/omap-hotplug.c b/arch/arm/mach-omap2/omap-hotplug.c index fe2dae1..f69cd5c 100644 --- a/arch/arm/mach-omap2/omap-hotplug.c +++ b/arch/arm/mach-omap2/omap-hotplug.c @@ -19,6 +19,8 @@ #include #include +#include + #include #include @@ -57,6 +59,7 @@ void platform_cpu_die(unsigned int cpu) * clear all interrupt wakeup sources */ omap_wakeupgen_irqmask_all(cpu, 1); + gic_cpu_disable(); omap4_enter_lowpower(cpu, PWRDM_POWER_OFF); this_cpu = hard_smp_processor_id(); if (omap_read_auxcoreboot0() == this_cpu) { @@ -64,6 +67,7 @@ void platform_cpu_die(unsigned int cpu) * OK, proper wakeup, we're done */ omap_wakeupgen_irqmask_all(this_cpu, 0); + gic_cpu_enable(); /* 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 21c5ad3..446b956 100644 --- a/arch/arm/mach-omap2/omap4-mpuss-lowpower.c +++ b/arch/arm/mach-omap2/omap4-mpuss-lowpower.c @@ -47,6 +47,8 @@ #include #include #include +#include +#include #include #include @@ -57,13 +59,41 @@ #ifdef CONFIG_SMP +#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; void __iomem *scu_sar_addr; }; +static void __iomem *gic_dist_base; +static void __iomem *sar_base; + 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_base + offset + 4 * idx); +} + +static inline u32 gic_readl(u32 offset, u8 idx) +{ + return __raw_readl(gic_dist_base + offset + 4 * idx); +} + /* * Set the CPUx powerdomain's previous power state */ @@ -121,6 +151,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_base + SAR_BACKUP_STATUS_OFFSET); + val |= (SAR_BACKUP_STATUS_GIC_CPU0 | SAR_BACKUP_STATUS_GIC_CPU1); + __raw_writel(val, sar_base + SAR_BACKUP_STATUS_OFFSET); +} + +/* * OMAP4 MPUSS Low Power Entry Function * * The purpose of this function is to manage low power programming @@ -128,11 +237,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 at 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; @@ -157,6 +280,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); @@ -177,6 +317,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_base + SAR_BACKUP_STATUS_OFFSET); + /* Enable GIC distributor and inteface on CPU0*/ + gic_cpu_enable(); + gic_dist_enable(); + } + ret: return 0; } @@ -187,7 +340,11 @@ ret: int __init omap4_mpuss_init(void) { struct omap4_cpu_pm_info *pm_info; - void __iomem *sar_ram_base = omap4_get_sar_ram_base(); + u8 i; + + /* Get GIC and SAR RAM base addresses */ + sar_base = omap4_get_sar_ram_base(); + gic_dist_base = omap4_get_gic_dist_base(); if (omap_rev() == OMAP4430_REV_ES1_0) { WARN(1, "Power Management not supported on OMAP4430 ES1.0\n"); @@ -196,7 +353,7 @@ int __init omap4_mpuss_init(void) /* Initilaise per CPU PM information */ pm_info = &per_cpu(omap4_pm_info, 0x0); - pm_info->scu_sar_addr = sar_ram_base + SCU_OFFSET0; + pm_info->scu_sar_addr = sar_base + SCU_OFFSET0; pm_info->pwrdm = pwrdm_lookup("cpu0_pwrdm"); if (!pm_info->pwrdm) { pr_err("Lookup failed for CPU0 pwrdm\n"); @@ -210,7 +367,7 @@ int __init omap4_mpuss_init(void) pwrdm_set_next_pwrst(pm_info->pwrdm, PWRDM_POWER_ON); pm_info = &per_cpu(omap4_pm_info, 0x1); - pm_info->scu_sar_addr = sar_ram_base + SCU_OFFSET1; + pm_info->scu_sar_addr = sar_base + SCU_OFFSET1; pm_info->pwrdm = pwrdm_lookup("cpu1_pwrdm"); if (!pm_info->pwrdm) { pr_err("Lookup failed for CPU1 pwrdm\n"); @@ -229,9 +386,35 @@ int __init omap4_mpuss_init(void) * is fixed so programit in init itself. */ __raw_writel(virt_to_phys(omap4_cpu_resume), - sar_ram_base + CPU1_WAKEUP_NS_PA_ADDR_OFFSET); + sar_base + CPU1_WAKEUP_NS_PA_ADDR_OFFSET); __raw_writel(virt_to_phys(omap4_cpu_resume), - sar_ram_base + CPU0_WAKEUP_NS_PA_ADDR_OFFSET); + sar_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. On OMAP4, maximum SPIs are + * fused in DIST_CTR bit-fields as 128. Hence the code is safe + * from reserved register writes since its well within 1020. + */ + max_spi_reg = __raw_readl(gic_dist_base + 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 8b71b1e..baac4c0 100644 --- a/arch/arm/mach-omap2/omap4-sar-layout.h +++ b/arch/arm/mach-omap2/omap4-sar-layout.h @@ -30,4 +30,24 @@ #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 + #endif