Santosh Shilimkar <santosh.shilimkar@xxxxxx> writes: > 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 s/suppose/supposed/ > to be saved. Refer to ROM code specs for the GIC layout details. Does this doc have a name? I've never seen such a thing and would really like to. > Signed-off-by: Santosh Shilimkar <santosh.shilimkar@xxxxxx> > Reviewed-by: Kevin Hilman <khilman@xxxxxx> > --- > 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); > +} aha, this is what I was thinking of in the earlier SAR patch. Something like this should be part of the SAR code, not here. > +static inline u32 gic_readl(u32 offset, u8 idx) > +{ > + return __raw_readl(gic_dist_base_addr + offset + 4 * idx); > +} Similarily, it would be nice tos see this as part of GIC code so this code doesn't have to access a global base address pointer. > /* > * 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 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; > @@ -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; Based on the spec, this should also be max'd at 1020 so you never write the reserved values after 1020. Kevin > + /* > + * 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; -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html