2019-06-04 16:11:39 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2012-05-07 11:24:01 +08:00
|
|
|
/*
|
|
|
|
* PXA910 Power Management Routines
|
|
|
|
*
|
|
|
|
* (C) Copyright 2009 Marvell International Ltd.
|
|
|
|
* All Rights Reserved
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/err.h>
|
|
|
|
#include <linux/time.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/suspend.h>
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/io.h>
|
|
|
|
#include <linux/irq.h>
|
|
|
|
#include <asm/mach-types.h>
|
ARM: move heavy barrier support out of line
The existing memory barrier macro causes a significant amount of code
to be inserted inline at every call site. For example, in
gpio_set_irq_type(), we have this for mb():
c0344c08: f57ff04e dsb st
c0344c0c: e59f8190 ldr r8, [pc, #400] ; c0344da4 <gpio_set_irq_type+0x230>
c0344c10: e3590004 cmp r9, #4
c0344c14: e5983014 ldr r3, [r8, #20]
c0344c18: 0a000054 beq c0344d70 <gpio_set_irq_type+0x1fc>
c0344c1c: e3530000 cmp r3, #0
c0344c20: 0a000004 beq c0344c38 <gpio_set_irq_type+0xc4>
c0344c24: e50b2030 str r2, [fp, #-48] ; 0xffffffd0
c0344c28: e50bc034 str ip, [fp, #-52] ; 0xffffffcc
c0344c2c: e12fff33 blx r3
c0344c30: e51bc034 ldr ip, [fp, #-52] ; 0xffffffcc
c0344c34: e51b2030 ldr r2, [fp, #-48] ; 0xffffffd0
c0344c38: e5963004 ldr r3, [r6, #4]
Moving the outer_cache_sync() call out of line reduces the impact of
the barrier:
c0344968: f57ff04e dsb st
c034496c: e35a0004 cmp sl, #4
c0344970: e50b2030 str r2, [fp, #-48] ; 0xffffffd0
c0344974: 0a000044 beq c0344a8c <gpio_set_irq_type+0x1b8>
c0344978: ebf363dd bl c001d8f4 <arm_heavy_mb>
c034497c: e5953004 ldr r3, [r5, #4]
This should reduce the cache footprint of this code. Overall, this
results in a reduction of around 20K in the kernel size:
text data bss dec hex filename
10773970 667392 10369656 21811018 14ccf4a ../build/imx6/vmlinux-old
10754219 667392 10369656 21791267 14c8223 ../build/imx6/vmlinux-new
Another advantage to this approach is that we can finally resolve the
issue of SoCs which have their own memory barrier requirements within
multiplatform kernels (such as OMAP.) Here, the bus interconnects
need additional handling to ensure that writes become visible in the
correct order (eg, between dma_map() operations, writes to DMA
coherent memory, and MMIO accesses.)
Acked-by: Tony Lindgren <tony@atomide.com>
Acked-by: Richard Woodruff <r-woodruff2@ti.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
2015-06-02 06:44:46 +08:00
|
|
|
#include <asm/outercache.h>
|
2014-04-16 02:38:32 +08:00
|
|
|
|
|
|
|
#include "cputype.h"
|
|
|
|
#include "addr-map.h"
|
|
|
|
#include "pm-pxa910.h"
|
|
|
|
#include "regs-icu.h"
|
|
|
|
#include "irqs.h"
|
2012-05-07 11:24:01 +08:00
|
|
|
|
|
|
|
int pxa910_set_wake(struct irq_data *data, unsigned int on)
|
|
|
|
{
|
|
|
|
uint32_t awucrm = 0, apcr = 0;
|
2014-02-24 05:40:13 +08:00
|
|
|
int irq = data->irq;
|
2012-05-07 11:24:01 +08:00
|
|
|
|
|
|
|
/* setting wakeup sources */
|
|
|
|
switch (irq) {
|
|
|
|
/* wakeup line 2 */
|
|
|
|
case IRQ_PXA910_AP_GPIO:
|
|
|
|
awucrm = MPMU_AWUCRM_WAKEUP(2);
|
|
|
|
apcr |= MPMU_APCR_SLPWP2;
|
|
|
|
break;
|
|
|
|
/* wakeup line 3 */
|
|
|
|
case IRQ_PXA910_KEYPAD:
|
|
|
|
awucrm = MPMU_AWUCRM_WAKEUP(3) | MPMU_AWUCRM_KEYPRESS;
|
|
|
|
apcr |= MPMU_APCR_SLPWP3;
|
|
|
|
break;
|
|
|
|
case IRQ_PXA910_ROTARY:
|
|
|
|
awucrm = MPMU_AWUCRM_WAKEUP(3) | MPMU_AWUCRM_NEWROTARY;
|
|
|
|
apcr |= MPMU_APCR_SLPWP3;
|
|
|
|
break;
|
|
|
|
case IRQ_PXA910_TRACKBALL:
|
|
|
|
awucrm = MPMU_AWUCRM_WAKEUP(3) | MPMU_AWUCRM_TRACKBALL;
|
|
|
|
apcr |= MPMU_APCR_SLPWP3;
|
|
|
|
break;
|
|
|
|
/* wakeup line 4 */
|
|
|
|
case IRQ_PXA910_AP1_TIMER1:
|
|
|
|
awucrm = MPMU_AWUCRM_WAKEUP(4) | MPMU_AWUCRM_AP1_TIMER_1;
|
|
|
|
apcr |= MPMU_APCR_SLPWP4;
|
|
|
|
break;
|
|
|
|
case IRQ_PXA910_AP1_TIMER2:
|
|
|
|
awucrm = MPMU_AWUCRM_WAKEUP(4) | MPMU_AWUCRM_AP1_TIMER_2;
|
|
|
|
apcr |= MPMU_APCR_SLPWP4;
|
|
|
|
break;
|
|
|
|
case IRQ_PXA910_AP1_TIMER3:
|
|
|
|
awucrm = MPMU_AWUCRM_WAKEUP(4) | MPMU_AWUCRM_AP1_TIMER_3;
|
|
|
|
apcr |= MPMU_APCR_SLPWP4;
|
|
|
|
break;
|
|
|
|
case IRQ_PXA910_AP2_TIMER1:
|
|
|
|
awucrm = MPMU_AWUCRM_WAKEUP(4) | MPMU_AWUCRM_AP2_TIMER_1;
|
|
|
|
apcr |= MPMU_APCR_SLPWP4;
|
|
|
|
break;
|
|
|
|
case IRQ_PXA910_AP2_TIMER2:
|
|
|
|
awucrm = MPMU_AWUCRM_WAKEUP(4) | MPMU_AWUCRM_AP2_TIMER_2;
|
|
|
|
apcr |= MPMU_APCR_SLPWP4;
|
|
|
|
break;
|
|
|
|
case IRQ_PXA910_AP2_TIMER3:
|
|
|
|
awucrm = MPMU_AWUCRM_WAKEUP(4) | MPMU_AWUCRM_AP2_TIMER_3;
|
|
|
|
apcr |= MPMU_APCR_SLPWP4;
|
|
|
|
break;
|
|
|
|
case IRQ_PXA910_RTC_ALARM:
|
|
|
|
awucrm = MPMU_AWUCRM_WAKEUP(4) | MPMU_AWUCRM_RTC_ALARM;
|
|
|
|
apcr |= MPMU_APCR_SLPWP4;
|
|
|
|
break;
|
|
|
|
/* wakeup line 5 */
|
|
|
|
case IRQ_PXA910_USB1:
|
|
|
|
case IRQ_PXA910_USB2:
|
|
|
|
awucrm = MPMU_AWUCRM_WAKEUP(5);
|
|
|
|
apcr |= MPMU_APCR_SLPWP5;
|
|
|
|
break;
|
|
|
|
/* wakeup line 6 */
|
|
|
|
case IRQ_PXA910_MMC:
|
|
|
|
awucrm = MPMU_AWUCRM_WAKEUP(6)
|
|
|
|
| MPMU_AWUCRM_SDH1
|
|
|
|
| MPMU_AWUCRM_SDH2;
|
|
|
|
apcr |= MPMU_APCR_SLPWP6;
|
|
|
|
break;
|
|
|
|
/* wakeup line 7 */
|
|
|
|
case IRQ_PXA910_PMIC_INT:
|
|
|
|
awucrm = MPMU_AWUCRM_WAKEUP(7);
|
|
|
|
apcr |= MPMU_APCR_SLPWP7;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (irq >= IRQ_GPIO_START && irq < IRQ_BOARD_START) {
|
|
|
|
awucrm = MPMU_AWUCRM_WAKEUP(2);
|
|
|
|
apcr |= MPMU_APCR_SLPWP2;
|
2014-02-24 05:40:13 +08:00
|
|
|
} else {
|
|
|
|
/* FIXME: This should return a proper error code ! */
|
2012-05-07 11:24:01 +08:00
|
|
|
printk(KERN_ERR "Error: no defined wake up source irq: %d\n",
|
|
|
|
irq);
|
2014-02-24 05:40:13 +08:00
|
|
|
}
|
2012-05-07 11:24:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (on) {
|
|
|
|
if (awucrm) {
|
|
|
|
awucrm |= __raw_readl(MPMU_AWUCRM);
|
|
|
|
__raw_writel(awucrm, MPMU_AWUCRM);
|
|
|
|
}
|
|
|
|
if (apcr) {
|
|
|
|
apcr = ~apcr & __raw_readl(MPMU_APCR);
|
|
|
|
__raw_writel(apcr, MPMU_APCR);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (awucrm) {
|
|
|
|
awucrm = ~awucrm & __raw_readl(MPMU_AWUCRM);
|
|
|
|
__raw_writel(awucrm, MPMU_AWUCRM);
|
|
|
|
}
|
|
|
|
if (apcr) {
|
|
|
|
apcr |= __raw_readl(MPMU_APCR);
|
|
|
|
__raw_writel(apcr, MPMU_APCR);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void pxa910_pm_enter_lowpower_mode(int state)
|
|
|
|
{
|
|
|
|
uint32_t idle_cfg, apcr;
|
|
|
|
|
|
|
|
idle_cfg = __raw_readl(APMU_MOH_IDLE_CFG);
|
|
|
|
apcr = __raw_readl(MPMU_APCR);
|
|
|
|
|
|
|
|
apcr &= ~(MPMU_APCR_DDRCORSD | MPMU_APCR_APBSD | MPMU_APCR_AXISD
|
|
|
|
| MPMU_APCR_VCTCXOSD | MPMU_APCR_STBYEN);
|
|
|
|
idle_cfg &= ~(APMU_MOH_IDLE_CFG_MOH_IDLE
|
|
|
|
| APMU_MOH_IDLE_CFG_MOH_PWRDWN);
|
|
|
|
|
|
|
|
switch (state) {
|
|
|
|
case POWER_MODE_UDR:
|
|
|
|
/* only shutdown APB in UDR */
|
|
|
|
apcr |= MPMU_APCR_STBYEN | MPMU_APCR_APBSD;
|
|
|
|
/* fall through */
|
|
|
|
case POWER_MODE_SYS_SLEEP:
|
|
|
|
apcr |= MPMU_APCR_SLPEN; /* set the SLPEN bit */
|
|
|
|
apcr |= MPMU_APCR_VCTCXOSD; /* set VCTCXOSD */
|
|
|
|
/* fall through */
|
|
|
|
case POWER_MODE_APPS_SLEEP:
|
|
|
|
apcr |= MPMU_APCR_DDRCORSD; /* set DDRCORSD */
|
|
|
|
/* fall through */
|
|
|
|
case POWER_MODE_APPS_IDLE:
|
|
|
|
apcr |= MPMU_APCR_AXISD; /* set AXISDD bit */
|
|
|
|
/* fall through */
|
|
|
|
case POWER_MODE_CORE_EXTIDLE:
|
|
|
|
idle_cfg |= APMU_MOH_IDLE_CFG_MOH_IDLE;
|
|
|
|
idle_cfg |= APMU_MOH_IDLE_CFG_MOH_PWRDWN;
|
|
|
|
idle_cfg |= APMU_MOH_IDLE_CFG_MOH_PWR_SW(3)
|
|
|
|
| APMU_MOH_IDLE_CFG_MOH_L2_PWR_SW(3);
|
|
|
|
/* fall through */
|
|
|
|
case POWER_MODE_CORE_INTIDLE:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* program the memory controller hardware sleep type and auto wakeup */
|
|
|
|
idle_cfg |= APMU_MOH_IDLE_CFG_MOH_DIS_MC_SW_REQ;
|
|
|
|
idle_cfg |= APMU_MOH_IDLE_CFG_MOH_MC_WAKE_EN;
|
|
|
|
__raw_writel(0x0, APMU_MC_HW_SLP_TYPE); /* auto refresh */
|
|
|
|
|
|
|
|
/* set DSPSD, DTCMSD, BBSD, MSASLPEN */
|
|
|
|
apcr |= MPMU_APCR_DSPSD | MPMU_APCR_DTCMSD | MPMU_APCR_BBSD
|
|
|
|
| MPMU_APCR_MSASLPEN;
|
|
|
|
|
|
|
|
/*always set SLEPEN bit mainly for MSA*/
|
|
|
|
apcr |= MPMU_APCR_SLPEN;
|
|
|
|
|
|
|
|
/* finally write the registers back */
|
|
|
|
__raw_writel(idle_cfg, APMU_MOH_IDLE_CFG);
|
|
|
|
__raw_writel(apcr, MPMU_APCR);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static int pxa910_pm_enter(suspend_state_t state)
|
|
|
|
{
|
|
|
|
unsigned int idle_cfg, reg = 0;
|
|
|
|
|
|
|
|
/*pmic thread not completed,exit;otherwise system can't be waked up*/
|
|
|
|
reg = __raw_readl(ICU_INT_CONF(IRQ_PXA910_PMIC_INT));
|
|
|
|
if ((reg & 0x3) == 0)
|
|
|
|
return -EAGAIN;
|
|
|
|
|
|
|
|
idle_cfg = __raw_readl(APMU_MOH_IDLE_CFG);
|
|
|
|
idle_cfg |= APMU_MOH_IDLE_CFG_MOH_PWRDWN
|
|
|
|
| APMU_MOH_IDLE_CFG_MOH_SRAM_PWRDWN;
|
|
|
|
__raw_writel(idle_cfg, APMU_MOH_IDLE_CFG);
|
|
|
|
|
|
|
|
/* disable L2 */
|
|
|
|
outer_disable();
|
|
|
|
/* wait for l2 idle */
|
|
|
|
while (!(readl(CIU_REG(0x8)) & (1 << 16)))
|
|
|
|
udelay(1);
|
|
|
|
|
|
|
|
cpu_do_idle();
|
|
|
|
|
|
|
|
/* enable L2 */
|
|
|
|
outer_resume();
|
|
|
|
/* wait for l2 idle */
|
|
|
|
while (!(readl(CIU_REG(0x8)) & (1 << 16)))
|
|
|
|
udelay(1);
|
|
|
|
|
|
|
|
idle_cfg = __raw_readl(APMU_MOH_IDLE_CFG);
|
|
|
|
idle_cfg &= ~(APMU_MOH_IDLE_CFG_MOH_PWRDWN
|
|
|
|
| APMU_MOH_IDLE_CFG_MOH_SRAM_PWRDWN);
|
|
|
|
__raw_writel(idle_cfg, APMU_MOH_IDLE_CFG);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Called after processes are frozen, but before we shut down devices.
|
|
|
|
*/
|
|
|
|
static int pxa910_pm_prepare(void)
|
|
|
|
{
|
|
|
|
pxa910_pm_enter_lowpower_mode(POWER_MODE_UDR);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Called after devices are re-setup, but before processes are thawed.
|
|
|
|
*/
|
|
|
|
static void pxa910_pm_finish(void)
|
|
|
|
{
|
|
|
|
pxa910_pm_enter_lowpower_mode(POWER_MODE_CORE_INTIDLE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int pxa910_pm_valid(suspend_state_t state)
|
|
|
|
{
|
|
|
|
return ((state == PM_SUSPEND_STANDBY) || (state == PM_SUSPEND_MEM));
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct platform_suspend_ops pxa910_pm_ops = {
|
|
|
|
.valid = pxa910_pm_valid,
|
|
|
|
.prepare = pxa910_pm_prepare,
|
|
|
|
.enter = pxa910_pm_enter,
|
|
|
|
.finish = pxa910_pm_finish,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __init pxa910_pm_init(void)
|
|
|
|
{
|
|
|
|
uint32_t awucrm = 0;
|
|
|
|
|
|
|
|
if (!cpu_is_pxa910())
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
suspend_set_ops(&pxa910_pm_ops);
|
|
|
|
|
|
|
|
/* Set the following bits for MMP3 playback with VCTXO on */
|
|
|
|
__raw_writel(__raw_readl(APMU_SQU_CLK_GATE_CTRL) | (1 << 30),
|
|
|
|
APMU_SQU_CLK_GATE_CTRL);
|
|
|
|
__raw_writel(__raw_readl(MPMU_FCCR) | (1 << 28), MPMU_FCCR);
|
|
|
|
|
|
|
|
awucrm |= MPMU_AWUCRM_AP_ASYNC_INT | MPMU_AWUCRM_AP_FULL_IDLE;
|
|
|
|
__raw_writel(awucrm, MPMU_AWUCRM);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
late_initcall(pxa910_pm_init);
|