Skip to content

Commit

Permalink
Merge branch 'for_3_2/for-rmk/arm_cpu_pm' of git://gitorious.org/omap…
Browse files Browse the repository at this point in the history
…-sw-develoment/linux-omap-dev into devel-stable
  • Loading branch information
Russell King committed Sep 26, 2011
2 parents b0a37dc + 8fb5428 commit c825dda
Show file tree
Hide file tree
Showing 11 changed files with 578 additions and 9 deletions.
1 change: 1 addition & 0 deletions arch/arm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ config ARM
select HAVE_GENERIC_HARDIRQS
select HAVE_SPARSE_IRQ
select GENERIC_IRQ_SHOW
select CPU_PM if (SUSPEND || CPU_IDLE)
help
The ARM series is a line of low-power-consumption RISC chip designs
licensed by ARM Ltd and targeted at embedded applications and
Expand Down
188 changes: 188 additions & 0 deletions arch/arm/common/gic.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/smp.h>
#include <linux/cpu_pm.h>
#include <linux/cpumask.h>
#include <linux/io.h>

Expand Down Expand Up @@ -276,6 +277,8 @@ static void __init gic_dist_init(struct gic_chip_data *gic,
if (gic_irqs > 1020)
gic_irqs = 1020;

gic->gic_irqs = gic_irqs;

/*
* Set all global interrupts to be level triggered, active low.
*/
Expand Down Expand Up @@ -343,6 +346,189 @@ static void __cpuinit gic_cpu_init(struct gic_chip_data *gic)
writel_relaxed(1, base + GIC_CPU_CTRL);
}

#ifdef CONFIG_CPU_PM
/*
* Saves the GIC distributor registers during suspend or idle. Must be called
* with interrupts disabled but before powering down the GIC. After calling
* this function, no interrupts will be delivered by the GIC, and another
* platform-specific wakeup source must be enabled.
*/
static void gic_dist_save(unsigned int gic_nr)
{
unsigned int gic_irqs;
void __iomem *dist_base;
int i;

if (gic_nr >= MAX_GIC_NR)
BUG();

gic_irqs = gic_data[gic_nr].gic_irqs;
dist_base = gic_data[gic_nr].dist_base;

if (!dist_base)
return;

for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++)
gic_data[gic_nr].saved_spi_conf[i] =
readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4);

for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
gic_data[gic_nr].saved_spi_target[i] =
readl_relaxed(dist_base + GIC_DIST_TARGET + i * 4);

for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
gic_data[gic_nr].saved_spi_enable[i] =
readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4);
}

/*
* Restores the GIC distributor registers during resume or when coming out of
* idle. Must be called before enabling interrupts. If a level interrupt
* that occured while the GIC was suspended is still present, it will be
* handled normally, but any edge interrupts that occured will not be seen by
* the GIC and need to be handled by the platform-specific wakeup source.
*/
static void gic_dist_restore(unsigned int gic_nr)
{
unsigned int gic_irqs;
unsigned int i;
void __iomem *dist_base;

if (gic_nr >= MAX_GIC_NR)
BUG();

gic_irqs = gic_data[gic_nr].gic_irqs;
dist_base = gic_data[gic_nr].dist_base;

if (!dist_base)
return;

writel_relaxed(0, dist_base + GIC_DIST_CTRL);

for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++)
writel_relaxed(gic_data[gic_nr].saved_spi_conf[i],
dist_base + GIC_DIST_CONFIG + i * 4);

for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
writel_relaxed(0xa0a0a0a0,
dist_base + GIC_DIST_PRI + i * 4);

for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
writel_relaxed(gic_data[gic_nr].saved_spi_target[i],
dist_base + GIC_DIST_TARGET + i * 4);

for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
writel_relaxed(gic_data[gic_nr].saved_spi_enable[i],
dist_base + GIC_DIST_ENABLE_SET + i * 4);

writel_relaxed(1, dist_base + GIC_DIST_CTRL);
}

static void gic_cpu_save(unsigned int gic_nr)
{
int i;
u32 *ptr;
void __iomem *dist_base;
void __iomem *cpu_base;

if (gic_nr >= MAX_GIC_NR)
BUG();

dist_base = gic_data[gic_nr].dist_base;
cpu_base = gic_data[gic_nr].cpu_base;

if (!dist_base || !cpu_base)
return;

ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_enable);
for (i = 0; i < DIV_ROUND_UP(32, 32); i++)
ptr[i] = readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4);

ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_conf);
for (i = 0; i < DIV_ROUND_UP(32, 16); i++)
ptr[i] = readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4);

}

static void gic_cpu_restore(unsigned int gic_nr)
{
int i;
u32 *ptr;
void __iomem *dist_base;
void __iomem *cpu_base;

if (gic_nr >= MAX_GIC_NR)
BUG();

dist_base = gic_data[gic_nr].dist_base;
cpu_base = gic_data[gic_nr].cpu_base;

if (!dist_base || !cpu_base)
return;

ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_enable);
for (i = 0; i < DIV_ROUND_UP(32, 32); i++)
writel_relaxed(ptr[i], dist_base + GIC_DIST_ENABLE_SET + i * 4);

ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_conf);
for (i = 0; i < DIV_ROUND_UP(32, 16); i++)
writel_relaxed(ptr[i], dist_base + GIC_DIST_CONFIG + i * 4);

for (i = 0; i < DIV_ROUND_UP(32, 4); i++)
writel_relaxed(0xa0a0a0a0, dist_base + GIC_DIST_PRI + i * 4);

writel_relaxed(0xf0, cpu_base + GIC_CPU_PRIMASK);
writel_relaxed(1, cpu_base + GIC_CPU_CTRL);
}

static int gic_notifier(struct notifier_block *self, unsigned long cmd, void *v)
{
int i;

for (i = 0; i < MAX_GIC_NR; i++) {
switch (cmd) {
case CPU_PM_ENTER:
gic_cpu_save(i);
break;
case CPU_PM_ENTER_FAILED:
case CPU_PM_EXIT:
gic_cpu_restore(i);
break;
case CPU_CLUSTER_PM_ENTER:
gic_dist_save(i);
break;
case CPU_CLUSTER_PM_ENTER_FAILED:
case CPU_CLUSTER_PM_EXIT:
gic_dist_restore(i);
break;
}
}

return NOTIFY_OK;
}

static struct notifier_block gic_notifier_block = {
.notifier_call = gic_notifier,
};

static void __init gic_pm_init(struct gic_chip_data *gic)
{
gic->saved_ppi_enable = __alloc_percpu(DIV_ROUND_UP(32, 32) * 4,
sizeof(u32));
BUG_ON(!gic->saved_ppi_enable);

gic->saved_ppi_conf = __alloc_percpu(DIV_ROUND_UP(32, 16) * 4,
sizeof(u32));
BUG_ON(!gic->saved_ppi_conf);

cpu_pm_register_notifier(&gic_notifier_block);
}
#else
static void __init gic_pm_init(struct gic_chip_data *gic)
{
}
#endif

void __init gic_init(unsigned int gic_nr, unsigned int irq_start,
void __iomem *dist_base, void __iomem *cpu_base)
{
Expand All @@ -358,8 +544,10 @@ void __init gic_init(unsigned int gic_nr, unsigned int irq_start,
if (gic_nr == 0)
gic_cpu_base_addr = cpu_base;

gic_chip.flags |= gic_arch_extn.flags;
gic_dist_init(gic, irq_start);
gic_cpu_init(gic);
gic_pm_init(gic);
}

void __cpuinit gic_secondary_init(unsigned int gic_nr)
Expand Down
8 changes: 8 additions & 0 deletions arch/arm/include/asm/hardware/gic.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ struct gic_chip_data {
unsigned int irq_offset;
void __iomem *dist_base;
void __iomem *cpu_base;
#ifdef CONFIG_CPU_PM
u32 saved_spi_enable[DIV_ROUND_UP(1020, 32)];
u32 saved_spi_conf[DIV_ROUND_UP(1020, 16)];
u32 saved_spi_target[DIV_ROUND_UP(1020, 4)];
u32 __percpu *saved_ppi_enable;
u32 __percpu *saved_ppi_conf;
#endif
unsigned int gic_irqs;
};
#endif

Expand Down
1 change: 1 addition & 0 deletions arch/arm/include/asm/mach/map.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ struct map_desc {
#define MT_MEMORY_NONCACHED 11
#define MT_MEMORY_DTCM 12
#define MT_MEMORY_ITCM 13
#define MT_MEMORY_SO 14

#ifdef CONFIG_MMU
extern void iotable_init(struct map_desc *, int);
Expand Down
3 changes: 3 additions & 0 deletions arch/arm/include/asm/pgtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,9 @@ extern pgprot_t pgprot_kernel;
#define pgprot_writecombine(prot) \
__pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_BUFFERABLE)

#define pgprot_stronglyordered(prot) \
__pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_UNCACHED)

#ifdef CONFIG_ARM_DMA_MEM_BUFFERABLE
#define pgprot_dmacoherent(prot) \
__pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_BUFFERABLE | L_PTE_XN)
Expand Down
8 changes: 8 additions & 0 deletions arch/arm/mm/mmu.c
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,14 @@ static struct mem_type mem_types[] = {
.prot_l1 = PMD_TYPE_TABLE,
.domain = DOMAIN_KERNEL,
},
[MT_MEMORY_SO] = {
.prot_pte = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |
L_PTE_MT_UNCACHED,
.prot_l1 = PMD_TYPE_TABLE,
.prot_sect = PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_S |
PMD_SECT_UNCACHED | PMD_SECT_XN,
.domain = DOMAIN_KERNEL,
},
};

const struct mem_type *get_mem_type(unsigned int type)
Expand Down
31 changes: 22 additions & 9 deletions arch/arm/vfp/vfpmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <linux/module.h>
#include <linux/types.h>
#include <linux/cpu.h>
#include <linux/cpu_pm.h>
#include <linux/kernel.h>
#include <linux/notifier.h>
#include <linux/signal.h>
Expand Down Expand Up @@ -68,7 +69,7 @@ static bool vfp_state_in_hw(unsigned int cpu, struct thread_info *thread)
/*
* Force a reload of the VFP context from the thread structure. We do
* this by ensuring that access to the VFP hardware is disabled, and
* clear last_VFP_context. Must be called from non-preemptible context.
* clear vfp_current_hw_state. Must be called from non-preemptible context.
*/
static void vfp_force_reload(unsigned int cpu, struct thread_info *thread)
{
Expand Down Expand Up @@ -436,9 +437,7 @@ static void vfp_enable(void *unused)
set_copro_access(access | CPACC_FULL(10) | CPACC_FULL(11));
}

#ifdef CONFIG_PM
#include <linux/syscore_ops.h>

#ifdef CONFIG_CPU_PM
static int vfp_pm_suspend(void)
{
struct thread_info *ti = current_thread_info();
Expand Down Expand Up @@ -468,19 +467,33 @@ static void vfp_pm_resume(void)
fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN);
}

static struct syscore_ops vfp_pm_syscore_ops = {
.suspend = vfp_pm_suspend,
.resume = vfp_pm_resume,
static int vfp_cpu_pm_notifier(struct notifier_block *self, unsigned long cmd,
void *v)
{
switch (cmd) {
case CPU_PM_ENTER:
vfp_pm_suspend();
break;
case CPU_PM_ENTER_FAILED:
case CPU_PM_EXIT:
vfp_pm_resume();
break;
}
return NOTIFY_OK;
}

static struct notifier_block vfp_cpu_pm_notifier_block = {
.notifier_call = vfp_cpu_pm_notifier,
};

static void vfp_pm_init(void)
{
register_syscore_ops(&vfp_pm_syscore_ops);
cpu_pm_register_notifier(&vfp_cpu_pm_notifier_block);
}

#else
static inline void vfp_pm_init(void) { }
#endif /* CONFIG_PM */
#endif /* CONFIG_CPU_PM */

/*
* Ensure that the VFP state stored in 'thread->vfpstate' is up to date
Expand Down
Loading

0 comments on commit c825dda

Please sign in to comment.