blob: e2676c4d8f07293f627ec3e6ed012b565fb49c2a [file] [log] [blame]
/*
* (C) Copyright 2012
* Marvell Semiconductor <www.marvell.com>
* Written-by: Jane Li <jiel@marvell.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <command.h>
#include <asm/io.h>
#include <asm/arch/cpu.h>
#include <asm/arch/features.h>
#include <asm/processor.h>
#include <power/pxa_common.h>
DECLARE_GLOBAL_DATA_PTR;
#define AXI_PHYS_BASE 0xd4200000
#define APB_PHYS_BASE 0xd4000000
#define APBC_PHYS_BASE (APB_PHYS_BASE + 0x015000)
#define APBC_REG(x) (APBC_PHYS_BASE + (x))
#define APMU_PHYS_BASE (AXI_PHYS_BASE + 0x82800)
#define APMU_REG(x) (APMU_PHYS_BASE + (x))
#define APMU_MC_HW_SLP_TYPE APMU_REG(0x00b0)
#define PMU_CORE_STATUS APMU_REG(0x0090)
#define PMU_CC2_AP APMU_REG(0x0100)
#define PMU_CORE0_IDLE_CFG APMU_REG(0x0124)
#define PMU_CORE1_IDLE_CFG APMU_REG(0x0128)
#define PMU_CORE2_IDLE_CFG APMU_REG(0x0160)
#define PMU_CORE3_IDLE_CFG APMU_REG(0x0164)
#define PMU_CP_IDLE_CFG APMU_REG(0x014)
#define PMU_SP_IDLE_CFG APMU_REG(0x0f8)
#define PMU_MP_IDLE_CFG0 APMU_REG(0x0120)
#define PMU_MP_IDLE_CFG1 APMU_REG(0x00e4)
#define PMU_MP_IDLE_CFG2 APMU_REG(0x0150)
#define PMU_MP_IDLE_CFG3 APMU_REG(0x0154)
#define PMU_DEBUG_REG APMU_REG(0x0088)
#define ICU_PHYS_BASE (AXI_PHYS_BASE + 0x82000)
#define ICU_REG(x) (ICU_PHYS_BASE + (x))
#define PXA1088_ICU_APC0_GBL_INT_MSK ICU_REG(0x228)
#define PXA1088_ICU_APC1_GBL_INT_MSK ICU_REG(0x238)
#define PXA1088_ICU_APC2_GBL_INT_MSK ICU_REG(0x248)
#define PXA1088_ICU_APC3_GBL_INT_MSK ICU_REG(0x258)
#define PXA988_ICU_A9C0_GBL_INT_MSK ICU_REG(0x114)
#define PXA988_ICU_A9C1_GBL_INT_MSK ICU_REG(0x144)
#define CIU_PHYS_BASE (AXI_PHYS_BASE + 0x82c00)
#define CIU_REG(x) (CIU_PHYS_BASE + (x))
#define CIU_CA9_WARM_RESET_VECTOR CIU_REG(0x00d8)
#define CIU_PJ4MP1_PDWN_CFG_CTL CIU_REG(0x007c)
#define MPMU_PHYS_BASE (APB_PHYS_BASE + 0x50000)
#define MPMU_REG(off) (MPMU_PHYS_BASE + (off))
#define MPMU_AWUCRM MPMU_REG(0x104c)
#define MPMU_APCR MPMU_REG(0x1000)
#define MPMU_CPCR MPMU_REG(0x0000)
#define MPMU_SCCR MPMU_REG(0x0038)
#define APBC_TIMERS0 APBC_REG(0x034)
#define APBC_TIMERS1 APBC_REG(0x044)
#define APBC_TIMERS2 APBC_REG(0x068)
#define TIMERS_PHYS_BASE_0 (APB_PHYS_BASE + 0x14000)
#define TIMERS_PHYS_BASE_1 (APB_PHYS_BASE + 0x16000)
#define TIMERS_PHYS_BASE_2 (APB_PHYS_BASE + 0x1F000)
#define TMR_CCR (0x0000)
#define TMR_TN_MM(n, m) (0x0004 + ((n) << 3) + (((n) + (m)) << 2))
#define TMR_CR(n) (0x0028 + ((n) << 2))
#define TMR_SR(n) (0x0034 + ((n) << 2))
#define TMR_IER(n) (0x0040 + ((n) << 2))
#define TMR_PLVR(n) (0x004c + ((n) << 2))
#define TMR_PLCR(n) (0x0058 + ((n) << 2))
#define TMR_WMER (0x0064)
#define TMR_WMR (0x0068)
#define TMR_WVR (0x006c)
#define TMR_WSR (0x0070)
#define TMR_ICR(n) (0x0074 + ((n) << 2))
#define TMR_WICR (0x0080)
#define TMR_CER (0x0084)
#define TMR_CMR (0x0088)
#define TMR_ILR(n) (0x008c + ((n) << 2))
#define TMR_WCR (0x0098)
#define TMR_WFAR (0x009c)
#define TMR_WSAR (0x00A0)
#define TMR_CVWR(n) (0x00A4 + ((n) << 2))
#define TMR_CCR_CS_0(x) (((x) & 0x3) << 0)
#define TMR_CCR_CS_1(x) (((x) & 0x3) << 2)
#define TMR_CCR_CS_2(x) (((x) & 0x3) << 5)
#ifdef CONFIG_PXA1088
#define PGU_PHYS_BASE 0xd1df8000
#endif
#define GIC_DIST_PHYS_BASE (PGU_PHYS_BASE + 0x1000)
#define GIC_CPU_PHYS_BASE (PGU_PHYS_BASE + 0x0100)
#define GIC_CPU_CTRL 0x00
#define GIC_CPU_PRIMASK 0x04
#define GIC_CPU_BINPOINT 0x08
#define GIC_CPU_INTACK 0x0c
#define GIC_CPU_EOI 0x10
#define GIC_CPU_RUNNINGPRI 0x14
#define GIC_CPU_HIGHPRI 0x18
#define GIC_DIST_CTRL 0x000
#define GIC_DIST_CTR 0x004
#define GIC_DIST_ENABLE_SET 0x100
#define GIC_DIST_ENABLE_CLEAR 0x180
#define GIC_DIST_PENDING_SET 0x200
#define GIC_DIST_PENDING_CLEAR 0x280
#define GIC_DIST_ACTIVE_BIT 0x300
#define GIC_DIST_PRI 0x400
#define GIC_DIST_TARGET 0x800
#define GIC_DIST_CONFIG 0xc00
#define GIC_DIST_SOFTINT 0xf00
#define PMUA_CORE_IDLE (1 << 0)
#define PMUA_CORE_POWER_DOWN (1 << 1)
#define PMUA_CORE_L1_SRAM_POWER_DOWN (1 << 2)
#define PMUA_GIC_IRQ_GLOBAL_MASK (1 << 3)
#define PMUA_GIC_FIQ_GLOBAL_MASK (1 << 4)
#define PMUA_MP_IDLE (1 << 0)
#define PMUA_MP_POWER_DOWN (1 << 1)
#define PMUA_MP_L2_SRAM_POWER_DOWN (1 << 2)
#define PMUA_MP_SCU_SRAM_POWER_DOWN (1 << 3)
#define ICU_MASK_FIQ (1 << 0)
#define ICU_MASK_IRQ (1 << 1)
#define PMUM_AXISD (1 << 31)
#define PMUM_DSPSD (1 << 30)
#define PMUM_SLPEN (1 << 29)
#define PMUM_DTCMSD (1 << 28)
#define PMUM_DDRCORSD (1 << 27)
#define PMUM_APBSD (1 << 26)
#define PMUM_BBSD (1 << 25)
#define PMUM_INTCLR (1 << 24)
#define PMUM_SLPWP0 (1 << 23)
#define PMUM_SLPWP1 (1 << 22)
#define PMUM_SLPWP2 (1 << 21)
#define PMUM_SLPWP3 (1 << 20)
#define PMUM_VCTCXOSD (1 << 19)
#define PMUM_SLPWP4 (1 << 18)
#define PMUM_SLPWP5 (1 << 17)
#define PMUM_SLPWP6 (1 << 16)
#define PMUM_SLPWP7 (1 << 15)
#define PMUM_MSASLPEN (1 << 14)
#define PMUM_STBYEN (1 << 13)
#define PMUM_SPDTCMSD (1 << 12)
#define PMUM_LDMA_MASK (1 << 3)
#define PMUM_GSM_WAKEUPWMX (1 << 29)
#define PMUM_WCDMA_WAKEUPX (1 << 28)
#define PMUM_GSM_WAKEUPWM (1 << 27)
#define PMUM_WCDMA_WAKEUPWM (1 << 26)
#define PMUM_AP_ASYNC_INT (1 << 25)
#define PMUM_AP_FULL_IDLE (1 << 24)
#define PMUM_SQU (1 << 23)
#define PMUM_SDH (1 << 22)
#define PMUM_KEYPRESS (1 << 21)
#define PMUM_TRACKBALL (1 << 20)
#define PMUM_NEWROTARY (1 << 19)
#define PMUM_WDT (1 << 18)
#define PMUM_RTC_ALARM (1 << 17)
#define PMUM_CP_TIMER_3 (1 << 16)
#define PMUM_CP_TIMER_2 (1 << 15)
#define PMUM_CP_TIMER_1 (1 << 14)
/*
* For Helan2 and HelanLTE, AP_TIMER0 and
* AP_TIMER2 shares the same wakeup bits
* in AWUCRM.
*/
#define PMUM_AP1_TIMER_3 (1 << 13)
#define PMUM_AP1_TIMER_2 (1 << 12)
#define PMUM_AP1_TIMER_1 (1 << 11)
#define PMUM_AP0_2_TIMER_3 (1 << 10)
#define PMUM_AP0_2_TIMER_2 (1 << 9)
#define PMUM_AP0_2_TIMER_1 (1 << 8)
#define PMUM_WAKEUP7 (1 << 7)
#define PMUM_WAKEUP6 (1 << 6)
#define PMUM_WAKEUP5 (1 << 5)
#define PMUM_WAKEUP4 (1 << 4)
#define PMUM_WAKEUP3 (1 << 3)
#define PMUM_WAKEUP2 (1 << 2)
#define PMUM_WAKEUP1 (1 << 1)
#define PMUM_WAKEUP0 (1 << 0)
#define CPU_MASK(n) (1 << n)
#define CORE0_1_WAKEUP (PMUM_AP1_TIMER_1 | PMUM_AP1_TIMER_2 | \
PMUM_AP1_TIMER_3)
#define CORE2_3_WAKEUP (PMUM_AP0_2_TIMER_1 | PMUM_AP0_2_TIMER_2 | \
PMUM_AP0_2_TIMER_3)
#ifdef CONFIG_PXA1088
#define LPM_WAKEUP (CORE0_1_WAKEUP | CORE2_3_WAKEUP | PMUM_WAKEUP4)
#endif
/*Core Active means: Core C2/WFI Flag Set*/
#ifdef CONFIG_PXA1088
#define C2_BIT(n) (7 + 3 * n)
#define C1_BIT(n) (6 + 3 * n)
#define WFI_BIT(n) (5 + 3 * n)
#define DBG_BIT(n) (18 + 3 * n)
#define SW_BIT(n) (17 + 3 * n)
#define POR_BIT(n) (16 + 3 * n)
#define CORE_N_DBG(n) (1 << (DBG_BIT(n)))
#define CORE_N_SW(n) (1 << (SW_BIT(n)))
#define CORE_N_POR(n) (1 << (POR_BIT(n)))
#define CORE_N_RESET(n) ((CORE_N_POR(n)) | (CORE_N_SW(n)) | \
(CORE_N_DBG(n)))
#endif
#define CORE_N_C2(n) (1 << (C2_BIT(n)))
#define CORE_N_C1(n) (1 << (C1_BIT(n)))
#define CORE_N_WFI(n) (1 << (WFI_BIT(n)))
#define CORE_N_SLEEP(n) ((CORE_N_C2(n)) | (CORE_N_C1(n)) | \
(CORE_N_WFI(n)))
/*This is for pxa988 Zx only*/
#define IDLE_BIT(n) (3 << (2 + 2 * n))
enum pxa988_lowpower_state {
POWER_MODE_ACTIVE = 0, /* not used */
/* core level power modes */
POWER_MODE_CORE_INTIDLE, /* C12 */
POWER_MODE_CORE_EXTIDLE, /* C13 */
POWER_MODE_CORE_PWRDOWN_L1_ON, /* C22P, only for PXA988*/
POWER_MODE_CORE_PWRDOWN, /* C22 */
/* MP level power modes */
POWER_MODE_MP_IDLE_CORE_EXTIDLE, /* M11_C13 */
POWER_MODE_MP_IDLE_CORE_PWRDOWN_L1_ON, /* M11_C22P, only for PXA988 */
POWER_MODE_MP_IDLE_CORE_PWRDOWN, /* M11_C22 */
POWER_MODE_MP_PWRDOWN_SCU_ON, /* M21P, only for PXA988 */
POWER_MODE_MP_PWRDOWN, /* M21 */
POWER_MODE_MP_PWRDOWN_L2_OFF, /* M22 */
/* AP subsystem level power modes */
POWER_MODE_APPS_IDLE, /* D1P */
POWER_MODE_APPS_IDLE_DDR, /* D1PDDR */
POWER_MODE_APPS_SLEEP, /* D1PP */
POWER_MODE_APPS_SLEEP_UDR, /* D1PPSTDBY */
/* chip level power modes*/
POWER_MODE_SYS_SLEEP, /* D1 */
POWER_MODE_UDR_VCTCXO_ON_L2_ON, /* D2 */
POWER_MODE_UDR_VCTCXO_OFF_L2_ON, /* D2P */
POWER_MODE_UDR_VCTCXO_OFF_L2_OFF, /* D2PP */
};
static inline void core_exit_coherency(void)
{
unsigned int v;
asm volatile(
" mrc p15, 0, %0, c1, c0, 1\n"
" bic %0, %0, #(1 << 6)\n"
" mcr p15, 0, %0, c1, c0, 1\n"
" .word 0xf57ff06f\n"/* isb */
: "=&r" (v) : : "cc");
}
static inline void disable_l1_cache(void)
{
unsigned int v;
asm volatile(
" mrc p15, 0, %0, c1, c0, 0\n"
" bic %0, %0, %1\n"
" mcr p15, 0, %0, c1, c0, 0\n"
" .word 0xf57ff06f\n"/* isb */
: "=&r" (v) : "Ir" (CR_C) : "cc");
}
static inline void core_enter_coherency(void)
{
unsigned int v;
asm volatile(
" mrc p15, 0, %0, c1, c0, 1\n"
" orr %0, %0, #(1 << 6)\n"
" mcr p15, 0, %0, c1, c0, 1\n"
" .word 0xf57ff06f\n"/* isb */
: "=&r" (v) : : "cc");
}
static inline void enable_l1_cache(void)
{
unsigned int v;
asm volatile(
" mrc p15, 0, %0, c1, c0, 0\n"
" orr %0, %0, %1\n"
" mcr p15, 0, %0, c1, c0, 0\n"
" .word 0xf57ff06f\n"/* isb */
: "=&r" (v) : "Ir" (CR_C) : "cc");
}
#define hard_smp_processor_id() \
({ \
unsigned int cpunum; \
__asm__("mrc p15, 0, %0, c0, c0, 5" \
: "=r" (cpunum)); \
cpunum &= 0x0F; \
})
static inline void uartsw_to_active_core(void)
{
#ifdef CONFIG_PXA_AMP_SUPPORT
int cpu, cur_cpu = hard_smp_processor_id();
char cmd[20];
unsigned int core_active_status, core_active_tmp_status;
unsigned int core_reset_status, core_reset_tmp_status;
core_active_status = __raw_readl(PMU_CORE_STATUS);
core_reset_status = __raw_readl(PMU_CC2_AP);
for (cpu = 0; cpu < CONFIG_NR_CPUS; cpu++) {
if (cur_cpu == cpu)
continue;
if (*(u32 *)CONFIG_CORE_BUSY_ADDR & (1 << cpu))
continue;
if (has_feat_legacy_apmu_core_status())
core_active_tmp_status =
core_active_status & IDLE_BIT(cpu);
else
core_active_tmp_status =
core_active_status & CORE_N_SLEEP(cpu);
core_reset_tmp_status = core_reset_status & CORE_N_RESET(cpu);
if (!core_active_tmp_status && !core_reset_tmp_status) {
printf("console switch from: %d to %d\n", cur_cpu, cpu);
sprintf(cmd, "uartsw %d", cpu);
run_command(cmd, 0);
break;
}
}
printf("UART NOT Switch, the CPU%d is the last man\n", cur_cpu);
#endif
}
static inline void core_wfi(void)
{
uartsw_to_active_core();
/* wfi */
__asm__ __volatile__ (".word 0xe320f003");
}
static inline void core_pwr_down_fallback(void)
{
core_enter_coherency();
enable_l1_cache();
}
static inline void core_pwr_down(void)
{
if (cpu_is_pxa988() || cpu_is_pxa986()) {
flush_dcache_all();
/* dsb */
__asm__ __volatile__ (".word 0xf57ff04f");
invalidate_icache_all();
} else {
disable_l1_cache();
flush_dcache_all();
/* clrex */
__asm__ __volatile__ (".word 0xf57ff01f");
core_exit_coherency();
/* dsb */
__asm__ __volatile__ (".word 0xf57ff04f");
}
core_wfi();
if ((cpu_is_pxa1088() || cpu_is_pxa1L88() || cpu_is_pxa1U88())) {
/* The fall back shouldn't be run in normal cases */
printf("C2 Core Power Down fall back!");
core_pwr_down_fallback();
}
}
static void __attribute__ ((unused)) gic_dist_init(int base)
{
unsigned int max_irq, i;
/* Hard coding the cpumask as CPU0 */
int cpumask = 1;
cpumask |= cpumask << 8;
cpumask |= cpumask << 16;
writel(0, base + GIC_DIST_CTRL);
/*
* Find out how many interrupts are supported.
*/
max_irq = readl(base + GIC_DIST_CTR) & 0x1f;
max_irq = (max_irq + 1) * 32;
/*
* Set all global interrupts to be level triggered, active low.
*/
for (i = 32; i < max_irq; i += 16)
writel(0, base + GIC_DIST_CONFIG + i * 4 / 16);
/*
* Set all global interrupts to this CPU only.
*/
for (i = 32; i < max_irq; i += 4)
writel(cpumask, base + GIC_DIST_TARGET + i * 4 / 4);
/* irq 13 route to cpu0, irq 14 route to cpu1 */
writel((0x1 << 8) | (0x2 << 16),
(base + GIC_DIST_TARGET + (32 + 12) * 4 / 4));
/*
* Set priority on all interrupts.
*/
for (i = 0; i < max_irq; i += 4)
writel(0xa0a0a0a0, base + GIC_DIST_PRI + i * 4 / 4);
/*
* Disable all interrupts.
*/
for (i = 0; i < max_irq; i += 32)
writel(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i * 4 / 32);
writel(1, base + GIC_DIST_CTRL);
}
static void __attribute__ ((unused)) gic_cpu_init(int base)
{
writel(0xf0, base + GIC_CPU_PRIMASK);
writel(1, base + GIC_CPU_CTRL);
}
static void __attribute__ ((unused)) gic_init(void)
{
int irq;
gic_dist_init(GIC_DIST_PHYS_BASE);
gic_cpu_init(GIC_CPU_PHYS_BASE);
/* enable AP timer0 and timer1 interrupt */
irq = 32 + 13;
writel((1 << (irq % 32)), GIC_DIST_PHYS_BASE + GIC_DIST_ENABLE_SET +
(irq / 32) * 4);
irq = 32 + 14;
writel((1 << (irq % 32)), GIC_DIST_PHYS_BASE + GIC_DIST_ENABLE_SET +
(irq / 32) * 4);
}
static void icu_enable_timer0_1(void)
{
/* Timer 1_0 route to CPU 0 */
__raw_writel(0x1 | (0x1 << 6) | (0x1 << 4), ICU_REG(29 << 2));
/* Timer 1_1 route to CPU 1 */
__raw_writel(0x1 | (0x1 << 7) | (0x1 << 4), ICU_REG(30 << 2));
/*
* TIMER0 is security accessed only in PXA1088 and PXA1L88, but in
* PXA1U88 it changes to TIMER2.
*/
if (cpu_is_pxa1088() || cpu_is_pxa1L88()) {
/* Timer 1_0 route to CPU 2 */
__raw_writel(0x1 | (0x1 << 8) | (0x1 << 4), ICU_REG(64 << 2));
/* Timer 1_1 route to CPU 3 */
__raw_writel(0x1 | (0x1 << 9) | (0x1 << 4), ICU_REG(65 << 2));
} else if (cpu_is_pxa1U88()) {
/* Timer 0_0 route to CPU 2 */
__raw_writel(0x1 | (0x1 << 8) | (0x1 << 4), ICU_REG(13 << 2));
/* Timer 0_1 route to CPU 3 */
__raw_writel(0x1 | (0x1 << 9) | (0x1 << 4), ICU_REG(14 << 2));
}
}
static void timer_config(int cpu)
{
unsigned long timer_phy_base = (cpu < 2) ? TIMERS_PHYS_BASE_1 :
cpu_is_pxa1U88() ? TIMERS_PHYS_BASE_0 : TIMERS_PHYS_BASE_2;
unsigned long timer_clk_base = (cpu < 2) ? APBC_TIMERS1 :
cpu_is_pxa1U88() ? APBC_TIMERS0 : APBC_TIMERS2;
/* Helan2 adds extal bit 7 to enable timer0 wakeup */
unsigned long timer_clk_val = (cpu >= 2 && cpu_is_pxa1U88()) ?
(0x1 << 7) | APBC_APBCLK | APBC_FNCLK | APBC_FNCLKSEL(3) :
APBC_APBCLK | APBC_FNCLK | APBC_FNCLKSEL(3);
unsigned long ccr = __raw_readl(timer_phy_base + TMR_CCR);
unsigned long cer = __raw_readl(timer_phy_base + TMR_CER);
unsigned long cmr = __raw_readl(timer_phy_base + TMR_CMR);
unsigned long cur_clk_val = __raw_readl(timer_clk_base);
if (cur_clk_val != timer_clk_val) {
__raw_writel(APBC_APBCLK | APBC_RST, timer_clk_base);
__raw_writel(timer_clk_val, timer_clk_base);
}
cpu = cpu % 2;
/* disable Timer */
cer &= ~CPU_MASK(cpu);
__raw_writel(cer, timer_phy_base + TMR_CER);
/* clock frequency from clock/reset control register for Timer 0 */
if (!cpu) {
ccr &= ~TMR_CCR_CS_0(3);
ccr |= TMR_CCR_CS_0(1); /* Timer 0 -- 32KHz */
} else {
ccr &= ~TMR_CCR_CS_1(3);
ccr |= TMR_CCR_CS_1(1); /* Timer 1 -- 32KHz */
}
__raw_writel(ccr, timer_phy_base + TMR_CCR);
/* periodic mode */
__raw_writel(cmr & ~CPU_MASK(cpu), timer_phy_base + TMR_CMR);
/* All this is for Comparator 0 */
/* clear status */
__raw_writel(0x01, timer_phy_base + TMR_ICR(cpu));
/* disable int */
__raw_writel(0x00, timer_phy_base + TMR_IER(cpu));
/* enable Timer */
cer |= CPU_MASK(cpu);
__raw_writel(cer, timer_phy_base + TMR_CER);
}
static void timer_set_match(int cpu, int seconds)
{
unsigned long cer;
unsigned long timer_phy_base = (cpu < 2) ? TIMERS_PHYS_BASE_1 :
cpu_is_pxa1U88() ? TIMERS_PHYS_BASE_0 : TIMERS_PHYS_BASE_2;
unsigned long ccr = __raw_readl(timer_phy_base + TMR_CCR);
unsigned long ccr_saved = ccr;
/* save the info that timer has been set to wakeup cpu */
*(u32 *)(CONFIG_LPM_TIMER_WAKEUP) |= 1 << cpu;
cpu = cpu % 2;
/* disable APx_Timer */
cer = __raw_readl(timer_phy_base + TMR_CER);
cer &= ~CPU_MASK(cpu);
__raw_writel(cer, timer_phy_base + TMR_CER);
if (!cpu)
ccr &= ~TMR_CCR_CS_0(3);
else
ccr &= ~TMR_CCR_CS_1(3);
__raw_writel(ccr, timer_phy_base + TMR_CCR);
/* clear pending interrupt status and enable */
/* All this is for Comparator 0 */
__raw_writel(0x01, timer_phy_base + TMR_ICR(cpu));
__raw_writel(0x01, timer_phy_base + TMR_IER(cpu));
/* We use 32k Timer here */
__raw_writel(32768 * seconds, timer_phy_base + TMR_TN_MM(cpu, 0));
__raw_writel(ccr_saved, timer_phy_base + TMR_CCR);
/* enable APx_Timer */
cer |= CPU_MASK(cpu);
__raw_writel(cer, timer_phy_base + TMR_CER);
}
static void timer_clear_irq(int cpu)
{
unsigned long timer_phy_base = (cpu < 2) ? TIMERS_PHYS_BASE_1 :
cpu_is_pxa1U88() ? TIMERS_PHYS_BASE_0 : TIMERS_PHYS_BASE_2;
/* clr the info that timer has been set to wakeup cpu */
*(u32 *)(CONFIG_LPM_TIMER_WAKEUP) &= ~(1 << cpu);
cpu = cpu % 2;
/* disable and clear pending interrupt status */
__raw_writel(0x0, timer_phy_base + TMR_IER(cpu));
__raw_writel(0x1, timer_phy_base + TMR_ICR(cpu));
}
static const u32 APMU_CORE_IDLE_CFG[] = {
(u32)PMU_CORE0_IDLE_CFG, (u32)PMU_CORE1_IDLE_CFG,
#ifdef CONFIG_PXA1088
(u32)PMU_CORE2_IDLE_CFG, (u32)PMU_CORE3_IDLE_CFG,
#endif
};
static const u32 APMU_MP_IDLE_CFG[] = {
(u32)PMU_MP_IDLE_CFG0, (u32)PMU_MP_IDLE_CFG1,
#ifdef CONFIG_PXA1088
(u32)PMU_MP_IDLE_CFG2, (u32)PMU_MP_IDLE_CFG3,
#endif
};
static const u32 ICU_GBL_INT_MSK[] = {
#ifdef CONFIG_PXA1088
(u32)PXA1088_ICU_APC0_GBL_INT_MSK, (u32)PXA1088_ICU_APC1_GBL_INT_MSK,
(u32)PXA1088_ICU_APC2_GBL_INT_MSK, (u32)PXA1088_ICU_APC3_GBL_INT_MSK,
#endif
};
static void pxa988_lowpower_config(u32 cpu,
u32 power_state, u32 lowpower_enable)
{
u32 core_idle_cfg, mp_idle_cfg, apcr, mc_slp_type = 0, debug = 0;
core_idle_cfg = __raw_readl(APMU_CORE_IDLE_CFG[cpu]);
mp_idle_cfg = __raw_readl(APMU_MP_IDLE_CFG[cpu]);
apcr = __raw_readl(MPMU_APCR);
if (has_feat_debugreg_in_d1p() && !has_feat_d1p_hipwr()) {
mc_slp_type = __raw_readl(APMU_MC_HW_SLP_TYPE);
debug = __raw_readl(PMU_DEBUG_REG);
}
if (lowpower_enable) {
switch (power_state) {
case POWER_MODE_UDR_VCTCXO_OFF_L2_OFF:
mp_idle_cfg |= PMUA_MP_L2_SRAM_POWER_DOWN;
/* fall through */
case POWER_MODE_UDR_VCTCXO_OFF_L2_ON:
apcr |= PMUM_VCTCXOSD;
/* fall through */
case POWER_MODE_UDR_VCTCXO_ON_L2_ON:
apcr |= PMUM_STBYEN;
/* fall through */
case POWER_MODE_SYS_SLEEP:
apcr |= PMUM_APBSD;
/*
* AP subsystem should shutdown before chip level
* power modes
*/
apcr |= PMUM_SLPEN | PMUM_DDRCORSD | PMUM_AXISD;
if (cpu_is_pxa988() || cpu_is_pxa986()) {
core_idle_cfg |= PMUA_CORE_L1_SRAM_POWER_DOWN;
mp_idle_cfg |= PMUA_MP_SCU_SRAM_POWER_DOWN;
}
mp_idle_cfg |= PMUA_MP_POWER_DOWN | PMUA_MP_IDLE;
core_idle_cfg |= PMUA_CORE_POWER_DOWN | PMUA_CORE_IDLE;
break;
/* AP SUB SYSTEM Power modes */
case POWER_MODE_APPS_SLEEP_UDR:
apcr |= PMUM_STBYEN;
/* fall through */
case POWER_MODE_APPS_SLEEP:
apcr |= PMUM_SLPEN;
/* fall through */
case POWER_MODE_APPS_IDLE_DDR:
if (!has_feat_debugreg_in_d1p())
apcr |= PMUM_DDRCORSD;
/* fall through */
case POWER_MODE_APPS_IDLE:
apcr |= PMUM_AXISD;
/* MP should shutdown before AP subsystem power modes */
/*
* FIXME: This is for PXA988 Z0, for A0 here we only
* need to vote PMUM_AXISD.
* Note that on Z0 we have to modify APMU_MC_HW_SLP_TYPE
* to change ddr sleep type from self-refresh to active
* power down. This makes ddr accessable in AP_IDLE.
* This is supposed to be fixed on A0.
*/
if (has_feat_debugreg_in_d1p() && !has_feat_d1p_hipwr())
apcr |= PMUM_DDRCORSD;
if (cpu_is_pxa988() || cpu_is_pxa986()) {
core_idle_cfg |= PMUA_CORE_L1_SRAM_POWER_DOWN;
mp_idle_cfg |= PMUA_MP_SCU_SRAM_POWER_DOWN;
}
mp_idle_cfg |= PMUA_MP_POWER_DOWN | PMUA_MP_IDLE;
core_idle_cfg |= PMUA_CORE_POWER_DOWN | PMUA_CORE_IDLE;
break;
/* MP level Power modes */
case POWER_MODE_MP_PWRDOWN_L2_OFF:
mp_idle_cfg |= PMUA_MP_L2_SRAM_POWER_DOWN;
/* fall through */
case POWER_MODE_MP_PWRDOWN:
if (cpu_is_pxa988() || cpu_is_pxa986())
mp_idle_cfg |= PMUA_MP_SCU_SRAM_POWER_DOWN;
/* fall through */
case POWER_MODE_MP_PWRDOWN_SCU_ON:
mp_idle_cfg |= PMUA_MP_POWER_DOWN;
case POWER_MODE_MP_IDLE_CORE_PWRDOWN:
if (cpu_is_pxa988() || cpu_is_pxa986())
core_idle_cfg |= PMUA_CORE_L1_SRAM_POWER_DOWN;
/* fall through */
case POWER_MODE_MP_IDLE_CORE_PWRDOWN_L1_ON:
core_idle_cfg |= PMUA_CORE_POWER_DOWN;
/* fall through */
case POWER_MODE_MP_IDLE_CORE_EXTIDLE:
mp_idle_cfg |= PMUA_MP_IDLE;
/* Core should be idle before MP level power modes */
core_idle_cfg |= PMUA_CORE_IDLE;
break;
/* Core level Power Modes */
case POWER_MODE_CORE_PWRDOWN:
if (cpu_is_pxa988() || cpu_is_pxa986())
core_idle_cfg |= PMUA_CORE_L1_SRAM_POWER_DOWN;
/* fall through */
case POWER_MODE_CORE_PWRDOWN_L1_ON:
core_idle_cfg |= PMUA_CORE_POWER_DOWN;
/* fall through */
case POWER_MODE_CORE_EXTIDLE:
core_idle_cfg |= PMUA_CORE_IDLE;
/* fall through */
case POWER_MODE_CORE_INTIDLE:
break;
default:
printf("Invalid power state!\n");
}
/* FIXME: need to set APMU_DEBUG_REGISTER for DDR access */
if (has_feat_debugreg_in_d1p() && !has_feat_d1p_hipwr() &&
(power_state == POWER_MODE_APPS_IDLE)) {
mc_slp_type &= ~0x7;
mc_slp_type |= 0x4;
debug |= (1 << 14) | (1 << 23);
}
} else {
core_idle_cfg &= ~(PMUA_CORE_IDLE | PMUA_CORE_POWER_DOWN);
mp_idle_cfg &= ~(PMUA_MP_IDLE | PMUA_MP_POWER_DOWN |
PMUA_MP_L2_SRAM_POWER_DOWN);
if (cpu_is_pxa988() || cpu_is_pxa986()) {
core_idle_cfg &= PMUA_CORE_L1_SRAM_POWER_DOWN;
mp_idle_cfg &= PMUA_MP_SCU_SRAM_POWER_DOWN;
}
apcr &= ~(PMUM_DDRCORSD | PMUM_APBSD | PMUM_AXISD |
PMUM_VCTCXOSD | PMUM_STBYEN | PMUM_SLPEN);
if (has_feat_debugreg_in_d1p() && !has_feat_d1p_hipwr()) {
mc_slp_type &= ~0x7;
debug &= ~((1 << 14) | (1 << 23));
}
}
/* set DSPSD, DTCMSD, BBSD, MSASLPEN */
apcr |= PMUM_DTCMSD | PMUM_BBSD | PMUM_MSASLPEN;
if (cpu_is_pxa988() || cpu_is_pxa986())
apcr |= PMUM_DSPSD;
if (cpu_is_pxa1U88())
apcr |= PMUM_SPDTCMSD | PMUM_LDMA_MASK;
/*
* FIXME: PXA920 was always setting SLEPEN bit but it seems no need
* to do that according to the power measurement.
*/
/* apcr |= PMUM_SLPEN; */
__raw_writel(core_idle_cfg, APMU_CORE_IDLE_CFG[cpu]);
__raw_writel(mp_idle_cfg, APMU_MP_IDLE_CFG[cpu]);
__raw_writel(apcr, MPMU_APCR);
if (has_feat_debugreg_in_d1p() && !has_feat_d1p_hipwr()) {
__raw_writel(mc_slp_type, APMU_MC_HW_SLP_TYPE);
__raw_writel(debug, PMU_DEBUG_REG);
}
}
static void pxa988_gic_global_mask(u32 cpu, u32 mask)
{
u32 core_idle_cfg;
core_idle_cfg = __raw_readl(APMU_CORE_IDLE_CFG[cpu]);
if (mask) {
core_idle_cfg |= PMUA_GIC_IRQ_GLOBAL_MASK;
core_idle_cfg |= PMUA_GIC_FIQ_GLOBAL_MASK;
} else {
core_idle_cfg &= ~(PMUA_GIC_IRQ_GLOBAL_MASK |
PMUA_GIC_FIQ_GLOBAL_MASK);
}
__raw_writel(core_idle_cfg, APMU_CORE_IDLE_CFG[cpu]);
}
static void pxa988_icu_global_mask(u32 cpu, u32 mask)
{
u32 icu_msk;
icu_msk = __raw_readl(ICU_GBL_INT_MSK[cpu]);
if (mask) {
icu_msk |= ICU_MASK_FIQ;
icu_msk |= ICU_MASK_IRQ;
} else {
icu_msk &= ~(ICU_MASK_FIQ | ICU_MASK_IRQ);
}
__raw_writel(icu_msk, ICU_GBL_INT_MSK[cpu]);
}
#define SCU_PM_NORMAL 0
#define SCU_PM_DORMANT 2
#define SCU_PM_POWEROFF 3
#define SCU_CPU_STATUS 0x08
#define SL2C_PHYS_BASE 0xd1dfb000
#define L2X0_POWER_CTRL 0xF80
#define SCU_CTRL 0x00
int scu_power_mode(void *scu_base, int mode)
{
unsigned int val;
int cpu = hard_smp_processor_id();
val = __raw_readb(scu_base + SCU_CPU_STATUS + cpu) & ~0x03;
val |= mode;
__raw_writeb(val, scu_base + SCU_CPU_STATUS + cpu);
return 0;
}
static void pxa988_pre_enter_lpm(u32 cpu, u32 power_mode)
{
pxa988_lowpower_config(cpu, power_mode, 1);
/* Mask GIC global interrupt */
pxa988_gic_global_mask(cpu, 1);
/* Mask ICU global interrupt */
pxa988_icu_global_mask(cpu, 1);
if (cpu_is_pxa988() || cpu_is_pxa986()) {
/* Seems not useful to enter M1/M2? */
scu_power_mode((void *)PGU_PHYS_BASE, SCU_PM_POWEROFF);
}
}
static void pxa988_post_enter_lpm(u32 cpu, u32 power_mode)
{
/* Unmask GIC interrtup */
pxa988_gic_global_mask(cpu, 0);
/*
* FIXME: Do we need to mask ICU before cpu_cluster_pm_exit
* to avoid GIC ID31 interrupt?
*/
/* Mask ICU global interrupt */
pxa988_icu_global_mask(cpu, 1);
pxa988_lowpower_config(cpu, power_mode, 0);
}
int do_lpm_debug(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
{
int i;
printf("cpu_id\t\t");
for (i = 0; i < CONFIG_NR_CPUS; i++)
printf("cpu%d\t\t", i);
printf("\n");
printf("CORE_IDLE_CFG\t");
for (i = 0; i < CONFIG_NR_CPUS; i++)
printf("0x%08x\t", __raw_readl(APMU_CORE_IDLE_CFG[i]));
printf("\n");
printf("MP_IDLE_CFG\t");
for (i = 0; i < CONFIG_NR_CPUS; i++)
printf("0x%08x\t", __raw_readl(APMU_MP_IDLE_CFG[i]));
printf("\n");
printf("CP_IDLE_CFG\t");
printf("0x%08x\n", __raw_readl(PMU_CP_IDLE_CFG));
if (cpu_is_pxa1U88()) {
printf("SP_IDLE_CFG\t");
printf("0x%08x\n", __raw_readl(PMU_SP_IDLE_CFG));
}
printf("APCR\t");
printf("0x%08x\n", __raw_readl(MPMU_APCR));
printf("CPCR\t");
printf("0x%08x\n", __raw_readl(MPMU_CPCR));
printf("WARM_RESET_VECTOR\t");
printf("0x%08x\n", __raw_readl(CIU_CA9_WARM_RESET_VECTOR));
return 0;
}
U_BOOT_CMD(
lpm_debug, 1, 1, do_lpm_debug,
"lpm debug",
""
);
static void do_c12(void)
{
int cpu = hard_smp_processor_id();
/* Mask ICU global interrupt, ICU could wakeup WFI */
pxa988_icu_global_mask(cpu, 0);
pxa988_lowpower_config(cpu, POWER_MODE_CORE_INTIDLE, 1);
core_wfi();
pxa988_lowpower_config(cpu, POWER_MODE_CORE_INTIDLE, 0);
}
static void do_c13(void)
{
int cpu = hard_smp_processor_id();
pxa988_lowpower_config(cpu, POWER_MODE_CORE_EXTIDLE, 1);
core_wfi();
pxa988_lowpower_config(cpu, POWER_MODE_CORE_EXTIDLE, 0);
}
static void do_c22p(void)
{
int cpu = hard_smp_processor_id();
pxa988_pre_enter_lpm(cpu, POWER_MODE_CORE_PWRDOWN_L1_ON);
core_pwr_down();
pxa988_post_enter_lpm(cpu, POWER_MODE_CORE_PWRDOWN_L1_ON);
}
static void do_c22(void)
{
int cpu = hard_smp_processor_id();
pxa988_pre_enter_lpm(cpu, POWER_MODE_CORE_PWRDOWN);
core_pwr_down();
pxa988_post_enter_lpm(cpu, POWER_MODE_CORE_PWRDOWN);
}
void do_m11_c13(void)
{
int cpu = hard_smp_processor_id();
pxa988_lowpower_config(cpu, POWER_MODE_MP_IDLE_CORE_EXTIDLE, 1);
core_wfi();
pxa988_lowpower_config(cpu, POWER_MODE_MP_IDLE_CORE_EXTIDLE, 0);
}
void do_m11_c22p(void)
{
int cpu = hard_smp_processor_id();
pxa988_pre_enter_lpm(cpu, POWER_MODE_MP_IDLE_CORE_PWRDOWN_L1_ON);
core_pwr_down();
pxa988_post_enter_lpm(cpu, POWER_MODE_MP_IDLE_CORE_PWRDOWN_L1_ON);
}
void do_m11_c22(void)
{
int cpu = hard_smp_processor_id();
pxa988_pre_enter_lpm(cpu, POWER_MODE_MP_IDLE_CORE_PWRDOWN);
core_pwr_down();
pxa988_post_enter_lpm(cpu, POWER_MODE_MP_IDLE_CORE_PWRDOWN);
}
void do_m21p(void)
{
int cpu = hard_smp_processor_id();
pxa988_pre_enter_lpm(cpu, POWER_MODE_MP_PWRDOWN_SCU_ON);
core_pwr_down();
pxa988_post_enter_lpm(cpu, POWER_MODE_MP_PWRDOWN_SCU_ON);
}
void do_m21(void)
{
int cpu = hard_smp_processor_id();
pxa988_pre_enter_lpm(cpu, POWER_MODE_MP_PWRDOWN);
core_pwr_down();
pxa988_post_enter_lpm(cpu, POWER_MODE_MP_PWRDOWN);
}
void do_m22(void)
{
int cpu = hard_smp_processor_id();
pxa988_pre_enter_lpm(cpu, POWER_MODE_MP_PWRDOWN_L2_OFF);
core_pwr_down();
pxa988_post_enter_lpm(cpu, POWER_MODE_MP_PWRDOWN_L2_OFF);
}
static void do_d1p(void)
{
int cpu = hard_smp_processor_id();
pxa988_pre_enter_lpm(cpu, POWER_MODE_APPS_IDLE);
core_pwr_down();
/* FIXME: need cpu_suspend/resume and CP voting support */
pxa988_post_enter_lpm(cpu, POWER_MODE_APPS_IDLE);
}
static void do_d1pddr(void)
{
int cpu = hard_smp_processor_id();
pxa988_pre_enter_lpm(cpu, POWER_MODE_APPS_IDLE_DDR);
core_pwr_down();
/* FIXME: need cpu_suspend/resume and CP voting support */
pxa988_post_enter_lpm(cpu, POWER_MODE_APPS_IDLE_DDR);
}
static void do_d1pp(void)
{
int cpu = hard_smp_processor_id();
pxa988_pre_enter_lpm(cpu, POWER_MODE_APPS_SLEEP);
/* Need ASYNC enable here */
__raw_writel(PMUM_AP_ASYNC_INT, MPMU_AWUCRM);
core_pwr_down();
/* FIXME: need cpu_suspend/resume and CP voting support */
pxa988_post_enter_lpm(cpu, POWER_MODE_APPS_SLEEP);
}
static void do_d1ppstdby(void)
{
int cpu = hard_smp_processor_id();
pxa988_pre_enter_lpm(cpu, POWER_MODE_APPS_SLEEP_UDR);
/* Need ASYNC enable here */
__raw_writel(PMUM_AP_ASYNC_INT, MPMU_AWUCRM);
core_pwr_down();
/* FIXME: need cpu_suspend/resume and CP voting support */
pxa988_post_enter_lpm(cpu, POWER_MODE_APPS_SLEEP_UDR);
}
static void do_d1(void)
{
int cpu = hard_smp_processor_id();
pxa988_pre_enter_lpm(cpu, POWER_MODE_SYS_SLEEP);
/* Need to set wake up source here */
__raw_writel(LPM_WAKEUP, MPMU_AWUCRM);
__raw_writel(__raw_readl(MPMU_APCR) & 0xff087fff, MPMU_APCR);
core_pwr_down();
/* FIXME: need cpu_suspend/resume and CP voting support */
pxa988_post_enter_lpm(cpu, POWER_MODE_SYS_SLEEP);
}
static void do_d2(void)
{
int cpu = hard_smp_processor_id();
pxa988_pre_enter_lpm(cpu, POWER_MODE_UDR_VCTCXO_ON_L2_ON);
/* Need to set wake up source here */
__raw_writel(LPM_WAKEUP, MPMU_AWUCRM);
__raw_writel(__raw_readl(MPMU_APCR) & 0xff087fff, MPMU_APCR);
core_pwr_down();
/* FIXME: need cpu_suspend/resume and CP voting support */
pxa988_post_enter_lpm(cpu, POWER_MODE_UDR_VCTCXO_ON_L2_ON);
}
static void do_d2p(void)
{
int cpu = hard_smp_processor_id();
pxa988_pre_enter_lpm(cpu, POWER_MODE_UDR_VCTCXO_OFF_L2_ON);
/* FIXME: Need to set wake up source here */
__raw_writel(LPM_WAKEUP, MPMU_AWUCRM);
__raw_writel(__raw_readl(MPMU_APCR) & 0xff087fff, MPMU_APCR);
core_pwr_down();
/* FIXME: need cpu_suspend/resume and CP voting support */
pxa988_post_enter_lpm(cpu, POWER_MODE_UDR_VCTCXO_OFF_L2_ON);
}
static void do_d2pp(void)
{
int cpu = hard_smp_processor_id();
pxa988_pre_enter_lpm(cpu, POWER_MODE_UDR_VCTCXO_OFF_L2_OFF);
/* FIXME: Need to set wake up source here */
__raw_writel(LPM_WAKEUP, MPMU_AWUCRM);
__raw_writel(__raw_readl(MPMU_APCR) & 0xff087fff, MPMU_APCR);
core_pwr_down();
/* FIXME: need cpu_suspend/resume and CP voting support */
pxa988_post_enter_lpm(cpu, POWER_MODE_UDR_VCTCXO_OFF_L2_OFF);
}
u32 get_idle_count(u32 cpu)
{
unsigned int *addr = (unsigned int *)IDLE_CNT_ADDR(cpu);
return *addr;
}
void set_idle_count(u32 cpu, u32 val)
{
unsigned int *addr = (unsigned int *)IDLE_CNT_ADDR(cpu);
*addr = val;
}
int do_lpm_test(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
{
int i = 0;
int seconds;
int cpu = hard_smp_processor_id();
int v;
int idle_counts = get_idle_count(cpu);
set_idle_count(cpu, ++idle_counts);
/* Disable dbgnopwd */
int coresight_addr = 0xd4100000;
printf("coresight: 0x%08x, 0x%08x\n",
__raw_readl(coresight_addr+0x10310),
__raw_readl(coresight_addr+0x12310));
/* Disable dbgnopowerdown */
/* step 1 to enable the SW access first */
int read_data;
read_data = __raw_readl(0xd4282cd0);
read_data = read_data | 0x100000;
__raw_writel(read_data, 0xd4282cd0);
read_data = __raw_readl(0xd4282ce0);
read_data = read_data | 0x100000;
__raw_writel(read_data, 0xd4282ce0);
/* step 2 enable the write access to Core0 and Core1 */
__raw_writel(0xC5ACCE55, coresight_addr + 0x10000 + 0xFB0);
__raw_writel(0xC5ACCE55, coresight_addr + 0x12000 + 0xFB0);
/* step3 enable the core0 */
read_data = __raw_readl(coresight_addr+0x10310);
read_data = read_data & 0xFFFFFFFE;
__raw_writel(read_data, coresight_addr+0x10310);
/* step3 enable the core1 */
read_data = __raw_readl(coresight_addr+0x12310);
read_data = read_data & 0xFFFFFFFE;
__raw_writel(read_data, coresight_addr+0x12310);
printf("coresight: 0x%08x, 0x%08x\n",
__raw_readl(coresight_addr+0x10310),
__raw_readl(coresight_addr+0x12310));
pxa988_lowpower_config(cpu, POWER_MODE_UDR_VCTCXO_OFF_L2_OFF, 0);
if (argc < 2)
return 0;
if (idle_counts == 1) {
/* gic_init(); */
/* Disable GIC in cpu interface */
v = __raw_readl(GIC_CPU_PHYS_BASE);
if (v & 0x1)
__raw_writel(v & (~0x1), GIC_CPU_PHYS_BASE);
/*
* We have to use ICU to wake up since in M1/M2
* GIC is clock gated
*/
icu_enable_timer0_1();
timer_config(cpu);
/*
* need this udelay otherwise the first time
* reading timer0 not correct
* Avoid to use udelay to corrupt timer.
*/
/* udelay(10); */
while (i < 3000000)
i++;
if (cpu_is_pxa988() || cpu_is_pxa986()) {
/* According to Emei Dragon Application Guide */
__raw_writel(0x3, SL2C_PHYS_BASE + L2X0_POWER_CTRL);
/* Seems not useful to enter M2? */
__raw_writel((__raw_readl(PGU_PHYS_BASE + SCU_CTRL) |
(1 << 5)),
PGU_PHYS_BASE + SCU_CTRL);
}
}
printf("set timer\n");
if (argc == 2)
seconds = 10;
else
seconds = simple_strtoul(argv[2], NULL, 10);
printf("timer interrupt will come after %d seconds\n", seconds);
timer_set_match(cpu, seconds);
printf("%u: core %u enter %s\n",
idle_counts, hard_smp_processor_id(), argv[1]);
if (strcmp("c12", argv[1]) == 0 ||
strcmp("C12", argv[1]) == 0) {
do_c12();
} else if (strcmp("c13", argv[1]) == 0 ||
strcmp("C13", argv[1]) == 0) {
do_c13();
} else if (strcmp("c22p", argv[1]) == 0 ||
strcmp("C22P", argv[1]) == 0) {
if (cpu_is_pxa988() || cpu_is_pxa986())
do_c22p();
else
printf("No %s state!\n", argv[1]);
} else if (strcmp("c22", argv[1]) == 0 ||
strcmp("C22", argv[1]) == 0) {
do_c22();
} else if (strcmp("m11_c13", argv[1]) == 0 ||
strcmp("M11_C13", argv[1]) == 0) {
do_m11_c13();
} else if (strcmp("m11_c22p", argv[1]) == 0 ||
strcmp("M11_C22P", argv[1]) == 0) {
if (cpu_is_pxa988() || cpu_is_pxa986())
do_m11_c22p();
else
printf("No %s state!\n", argv[1]);
} else if (strcmp("m11_c22", argv[1]) == 0 ||
strcmp("M11_C22", argv[1]) == 0) {
do_m11_c22();
} else if (strcmp("m21p", argv[1]) == 0 ||
strcmp("M21P", argv[1]) == 0) {
if (cpu_is_pxa988() || cpu_is_pxa986())
do_m21p();
else
printf("No %s state!\n", argv[1]);
} else if (strcmp("m21", argv[1]) == 0 ||
strcmp("M21", argv[1]) == 0) {
do_m21();
} else if (strcmp("m22", argv[1]) == 0 ||
strcmp("M22", argv[1]) == 0) {
do_m22();
} else if (strcmp("d1p", argv[1]) == 0 ||
strcmp("D1P", argv[1]) == 0) {
do_d1p();
} else if (strcmp("d1pddr", argv[1]) == 0 ||
strcmp("D1PDDR", argv[1]) == 0) {
do_d1pddr();
} else if (strcmp("d1pp", argv[1]) == 0 ||
strcmp("D1PP", argv[1]) == 0) {
do_d1pp();
} else if (strcmp("d1ppstdby", argv[1]) == 0 ||
strcmp("D1PPstdby", argv[1]) == 0) {
do_d1ppstdby();
} else if (strcmp("d1", argv[1]) == 0 ||
strcmp("D1", argv[1]) == 0) {
do_d1();
} else if (strcmp("d2", argv[1]) == 0 ||
strcmp("D2", argv[1]) == 0) {
do_d2();
} else if (strcmp("d2p", argv[1]) == 0 ||
strcmp("D2P", argv[1]) == 0) {
do_d2p();
} else if (strcmp("d2pp", argv[1]) == 0 ||
strcmp("D2PP", argv[1]) == 0) {
do_d2pp();
} else {
printf("Unsupported power state!\n");
}
printf("%u: exit %s\n ", idle_counts, argv[1]);
timer_clear_irq(cpu);
return 0;
}
U_BOOT_CMD(
lpm, 3, 1, do_lpm_test,
"lpm test",
"USAGE: lpm state timeout\n"
"1. timeout is measured with secs\n"
"2. Current support states are:\n"
" c12/C12: WFI\n"
" c13/C13: core external idle\n"
" c22p/C22P[PXA988 ONLY]: core power down with L1 on\n"
" c22/c22: core power down\n"
" m11_c13/M11_C13: all cores in C13 and MP internal idle\n"
" m11_c22p/M11_C22P[PXA988 ONLY]: all cores in C22P and MP internal idle\n"
" m11_c22/M11_C22: all cores in C22 and MP internl idle\n"
" m21p/M21P[PXA988 ONLY]: all cores in C22 and MP power down with SCU on\n"
" m21/M21: MP in m21p and SCU off\n"
" m22/M22: MP in M21 and L2 off\n"
" d1p/D1P: axi vote\n"
" d1pddr/D1PDDR: axi and ddr vote\n"
" d1pp/D1PP: axi, ddr, ap sleep vote\n"
" d1ppstdby: axi, ddr, ap sleep, ap standby vote\n"
" d1/D1: axi, ddr, ap sleep and apb vote\n"
" d2/D2: axi, ddr, ap sleep, apb and ap standby vote\n"
" d2p/D2P: axi, ddr, ap sleep, apb, ap standby and vctcxo vote\n"
" d2pp/D2PP: axi, ddr, ap sleep, apb, ap standby and vctcxo vote,L2 off\n"
);
int cp_load(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
{
__raw_writel(0x06000000, 0xd4282c24);
__raw_writel(__raw_readl(0xd4051020) & 0xfffffffe, 0xd4051020);
return 0;
}
U_BOOT_CMD(
cpload, 1, 1, cp_load,
"Load CP and MSA images",
""
);
int cp_vote(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
{
pxa988_lowpower_config(0, POWER_MODE_UDR_VCTCXO_OFF_L2_OFF, 1);
return 0;
}
U_BOOT_CMD(
cpvote, 1, 1, cp_vote,
"Vote CP LPM bits",
""
);
enum deadidlelvl {
DEADIDLE_VPU = (1 << 0),
DEADIDLE_GPU = (1 << 1),
DEADIDLE_ISP = (1 << 2),
DEADIDLE_OTHERPERI = (1 << 3),
DEADIDLE_MCKFAB = (1 << 4),
DEADIDLE_GNSS = (1 << 5),
DEADIDLE_ALL = 0xffffffff,
};
static void pxa1x88_deadidle(unsigned int cond)
{
u32 rstbit, enbit;
if (cond & DEADIDLE_MCKFAB) {
/* Fabric0, Fabric1 dynamic clk gate enable */
*(unsigned int *)(APMU_REG(0x02c)) = 0x0;
*(unsigned int *)(APMU_REG(0x030)) = 0x0;
/* other Fabric enalbe */
/*
* Set 19bit & 3bit to 0x0, will enalbe all SOC and MCK dyn
* clk gate.
*/
*(unsigned int *)(CIU_REG(0x040)) &= ~(0x1<<19);
*(unsigned int *)(CIU_REG(0x040)) |= (0xff<<8);
*(unsigned int *)(CIU_REG(0x040)) |= (0x7<<16);
*(unsigned int *)(CIU_REG(0x040)) |= (0x1 << 21);
*(unsigned int *)(CIU_REG(0x040)) |= (0x3<<26);
if (cpu_is_pxa1088() || cpu_is_pxa1L88()) {
*(unsigned int *)(CIU_REG(0x040)) |= (0x1 << 30);
*(unsigned int *)(CIU_REG(0x040)) |= (0x1 << 29);
} else if (cpu_is_pxa988() || cpu_is_pxa986()) {
*(unsigned int *)(CIU_REG(0x040)) |= (0x1 << 20);
*(unsigned int *)(CIU_REG(0x040)) &= ~(0x1<<3);
} else if (cpu_is_pxa1U88()) {
*(unsigned int *)(CIU_REG(0x040)) |=
((0x3 << 29) | (1 << 20));
}
}
/* on1, on2, off time */
*(unsigned int *)(APMU_REG(0x0dc)) = 0x20001fff;
/* VPU off */
if (cond & DEADIDLE_VPU) {
printf("VPU Off\n");
/* 1. Enable VPU Isolation */
*(unsigned int *)(APMU_REG(0x0A4)) &= ~(0x1<<16);
/* 2. Assert bus reset, func and AHB reset */
*(unsigned int *)(APMU_REG(0x0A4)) &= ~(0x7<<0);
/* 3. Reset bus clock enable and func and AHB clock enable */
*(unsigned int *)(APMU_REG(0x0A4)) &= ~(0x7<<3);
/* 4. Set Dx8 in HW mod */
*(unsigned int *)(APMU_REG(0x0A4)) |= (0x1<<19);
/* 5. ref Clock Reset */
*(unsigned int *)(APMU_REG(0x0A4)) &= ~(0x1<<24);
/* 6. ref Clock Disable */
*(unsigned int *)(APMU_REG(0x0A4)) &= ~(0x1<<25);
/* 7. power off */
*(unsigned int *)(APMU_REG(0x0d8)) &= ~(0x1<<2);
}
/* GC off */
if (cond & DEADIDLE_GPU) {
printf("GC_3D Off\n");
/* 1. Enable GC3D Isolation */
*(unsigned int *)(APMU_REG(0x0CC)) &= ~(0x1<<8);
if (has_feat_gcshader()) {
/* 2. Assert shader clock reset */
*(unsigned int *)(APMU_REG(0x0CC)) &= ~(0x1<<24);
/* 3. Disable shader clock */
*(unsigned int *)(APMU_REG(0x0CC)) &= ~(0x1<<25);
}
/* 4. Assert bus, func and HCLK reset */
*(unsigned int *)(APMU_REG(0x0CC)) &= ~(0x7<<0);
/* 5. Reset bus, func and HCLK clock enable */
*(unsigned int *)(APMU_REG(0x0CC)) &= ~(0x7<<3);
/* 6. Set GC in HW mod */
*(unsigned int *)(APMU_REG(0x0CC)) |= (0x1<<11);
if (has_feat_gc2d()) {
printf("GC_2D Off\n");
/* 1. Enable GC2D Isolation */
*(unsigned int *)(APMU_REG(0x0F4)) &= ~(0x1<<8);
/* 2. Assert bus, func and HCLK reset */
*(unsigned int *)(APMU_REG(0x0F4)) &= ~(0x7<<0);
/* 3. Reset bus, func and HCLK clock enable */
*(unsigned int *)(APMU_REG(0x0F4)) &= ~(0x7<<3);
/* 4. Set GC2D in HW mod */
*(unsigned int *)(APMU_REG(0x0F4)) |= (0x1<<11);
/* 5. GC 2D power off */
if (has_feat_gc2d_separatepwr())
*(unsigned int *)(APMU_REG(0x0d8)) &= ~(0x1<<6);
}
/* 7. GC 3D(on pxa1L88) / All of GC(on pxa1088) power off */
*(unsigned int *)(APMU_REG(0x0d8)) &= ~(0x1<<0);
}
/* ISP off */
if (cond & DEADIDLE_ISP) {
printf("ISP Off\n");
if (cpu_is_pxa1U88()) {
rstbit = (1 << 0) | (1 << 8) | (1 << 16) | (1 << 27);
enbit = (1 << 1) | (1 << 17) | (1 << 28);
} else {
rstbit = (1 << 0) | (1 << 8) | (1 << 10);
enbit = (1 << 1) | (1 << 9) | (1 << 11);
}
/* 1. Enable ISP Isolation */
*(unsigned int *)(APMU_REG(0x038)) &= ~(1<<12);
/* 2. Assert func, AXI and AHB reset */
*(unsigned int *)(APMU_REG(0x038)) &= ~rstbit;
/* 3. Reset func, AXI and AHB clock enable */
*(unsigned int *)(APMU_REG(0x038)) &= ~enbit;
/* 4. Set ISP in HW mod */
*(unsigned int *)(APMU_REG(0x038)) |= (0x1<<15);
/* 5. power off */
*(unsigned int *)(APMU_REG(0x0d8)) &= ~(0x1<<4);
}
if (cond & DEADIDLE_GNSS) {
if (has_feat_gnss()) {
printf("GNSS Off\n");
/* 1. Enable Isolation */
*(unsigned int *)(APMU_REG(0x0FC)) &= ~(1 << 2);
/* 2. Assert reset */
*(unsigned int *)(APMU_REG(0x0FC)) &= ~(1 << 1);
/* 3. Set in HW mod */
*(unsigned int *)(APMU_REG(0x0FC)) |= (1 << 0);
/* 4. power off */
*(unsigned int *)(APMU_REG(0x0d8)) &= ~(1 << 8);
}
}
/* other devs */
if (cond & DEADIDLE_OTHERPERI) {
printf("Other devices reset\n");
/* USB */
printf("USB reset\n");
*(unsigned int *)(APMU_REG(0x034)) = 0x0;
*(unsigned int *)(APMU_REG(0x05C)) = 0x0;
/* HSI/LTE_DMA */
printf("HSI/LTE_DMA reset\n");
*(unsigned int *)(APMU_REG(0x048)) = 0x0;
/* SRAM power down, need to check */
printf("SRAM Power Down\n");
/* Let L1 and L2 Power down to APMU Core power down sequence */
/* Config L2 SRAM to HW control */
*(unsigned int *)(APMU_REG(0x08C)) &= ~(0x3<<18);
*(unsigned int *)(APMU_REG(0x08C)) &= ~(0x3<<20);
/* Config L1 SRAM to HW control */
*(unsigned int *)(APMU_REG(0x08C)) &= ~(0x3<<8);
*(unsigned int *)(APMU_REG(0x08C)) &= ~(0x3<<0);
/* Power down it SQU SRAM */
*(unsigned int *)(APMU_REG(0x08C)) |= (0x3<<16);
/* LCD */
printf("LCD clk gate reset\n");
/* clk reset */
*(unsigned int *)(APMU_REG(0x04C)) &= ~(0x1<<16);
*(unsigned int *)(APMU_REG(0x04C)) &= ~(0x7<<0);
/* clk enable */
if (cpu_is_pxa1U88()) {
*(unsigned int *)(APMU_REG(0x04C)) &= ~(0x3f<<3);
} else {
*(unsigned int *)(APMU_REG(0x04C)) &= ~(0x7<<3);
*(unsigned int *)(APMU_REG(0x04C)) &= ~(0x3<<8);
}
udelay(100);
/* DSI PHY ESC reset and disable */
*(unsigned int *)(APMU_REG(0x044)) &= ~(0x3<<3);
*(unsigned int *)(APMU_REG(0x044)) &= ~(0x1<<2);
*(unsigned int *)(APMU_REG(0x044)) &= ~(0x1<<5);
udelay(100);
/* ISCCR0/1 */
printf("I2S clk gate reset\n");
/* sysclk, baseclk disable */
*(unsigned int *)(MPMU_REG(0x040)) &= ~(0x1<<31);
*(unsigned int *)(MPMU_REG(0x040)) &= ~(0x1<<29);
/* sysclk, baseclk disable */
*(unsigned int *)(MPMU_REG(0x044)) &= ~(0x1<<31);
*(unsigned int *)(MPMU_REG(0x044)) &= ~(0x1<<29);
/* CCIC */
/* CCIC all dynamic change enalbe */
*(unsigned int *)(APMU_REG(0x028)) = 0x0;
if (cpu_is_pxa1U88()) {
rstbit = (1 << 1) | (1 << 2) | (1 << 22);
enbit = (1 << 4) | (1 << 5) | (1 << 21);
*(unsigned int *)(APMU_REG(0x050)) &= ~rstbit;
*(unsigned int *)(APMU_REG(0x050)) &= ~enbit;
} else {
/* Disable PHY */
*(unsigned int *)(APMU_REG(0x050)) &=
~((0x1<<5) | (0x1<<9));
*(unsigned int *)(APMU_REG(0x088)) &= ~(0x3<<25);
/* Disable func clock */
*(unsigned int *)(APMU_REG(0x050)) &= ~(0x1<<4);
/* Disable AXI clock */
*(unsigned int *)(APMU_REG(0x050)) &= ~(0x1<<3);
}
/* For CCIC2 */
if (has_feat_ccic2()) {
if (cpu_is_pxa1U88()) {
rstbit = (1 << 1) | (1 << 2);
enbit = (1 << 4) | (1 << 5) | (1 << 26);
*(unsigned int *)(APMU_REG(0x024)) &= ~rstbit;
*(unsigned int *)(APMU_REG(0x024)) &= ~enbit;
} else {
/* Disable PHY for CCIC2*/
*(unsigned int *)(APMU_REG(0x024)) &=
~((0x1<<5) | (0x1<<9));
/* Disable func clock for CCIC2 */
*(unsigned int *)(APMU_REG(0x024)) &= ~(0x1<<4);
/* Disable AXI clock for CCIC2*/
*(unsigned int *)(APMU_REG(0x024)) &= ~(0x1<<3);
}
}
/* SDH0/1/2 */
printf("SDH clk gate reset\n");
/* reset and disable */
/* SDH0 Reset and Disable */
*(unsigned int *)(APMU_REG(0x054)) &= ~(0x1<<1);
*(unsigned int *)(APMU_REG(0x054)) &= ~(0x1<<4);
/* SDH1 Reset and Disable */
*(unsigned int *)(APMU_REG(0x058)) &= ~(0x1<<1);
*(unsigned int *)(APMU_REG(0x058)) &= ~(0x1<<4);
/* SDH2 Reset and Disable */
*(unsigned int *)(APMU_REG(0x0e0)) &= ~(0x1<<1);
*(unsigned int *)(APMU_REG(0x0e0)) &= ~(0x1<<4);
/* ALL SDH AXI Reset and Disable */
*(unsigned int *)(APMU_REG(0x054)) &= ~(0x1<<0);
*(unsigned int *)(APMU_REG(0x054)) &= ~(0x1<<3);
/* NF */
printf("NF clk gate reset\n");
*(unsigned int *)(APMU_REG(0x060)) = 0x0;
/* AES */
printf("AES clk gate reset\n");
/*
* In PXA1U88, reset AES clk will not decrease but increase
* VCC_MAIN current. Thus we only shutdown its clk.
*/
if (cpu_is_pxa1U88())
*(unsigned int *)(APMU_REG(0x068)) = 0x50;
else
*(unsigned int *)(APMU_REG(0x068)) = 0x0;
/* DTC */
printf("DTC clk gate reset\n");
*(unsigned int *)(APMU_REG(0x0aC)) = 0x0;
/* SMC */
printf("SMC clk gate reset\n");
*(unsigned int *)(APMU_REG(0x0d4)) &= ~(0x3<<0);
*(unsigned int *)(APMU_REG(0x0d4)) &= ~(0x3<<3);
/* MCK4 AHB HCLK */
printf("MCK4 AHB HCLK clk gate reset\n");
*(unsigned int *)(APMU_REG(0x0e8)) = 0x0;
/* trace reset */
printf("Trace clk gate power off\n");
*(unsigned int *)(APMU_REG(0x108)) &= ~(0x1<<16);
*(unsigned int *)(APMU_REG(0x108)) &= ~(0x3<<3);
/* ACLK gate enable */
printf("ACLK gate enable\n");
printf("ciu=%d\n", *(unsigned int *)(CIU_REG(0x05c)));
/* Gated or not no influnce */
/* *(unsigned int *)(CIU_REG(0x05c)) |= (0x1<<9); */
/* DCLK gate enable */
printf("DCLK gate enable\n");
/* Gated or not no influnce */
/* *(unsigned int *)(CIU_REG(0x05c)) &= ~(0x1<10); */
/* Global clk gat baypas and debug gate disenable */
if (cpu_is_pxa988()) {
printf("Debug enable\n");
*(unsigned int *)(CIU_REG(0x068)) &= ~(0x1<<8);
*(unsigned int *)(CIU_REG(0x068)) &= ~(0x1<<9);
}
/* SQU dynamic clk gate enable */
printf("SQU gate enable\n");
*(unsigned int *)(APMU_REG(0x01c)) &= ~(0x3fffffff<<0);
/* CCGR & ACGR */
/* printf("CCGR & ACGR\n"); */
/* Please use this setting when Getting Deadidle power
* and OP(CORE/DDR/AXI) has been set to: 312/104/104
*/
/* *(unsigned int *)(PMUM_ADDR+0x0024) = 0xa010; */
/* *(unsigned int *)(PMUM_ADDR+0x1024) = 0x2812; */
/* APB */
printf("APB\n");
printf("APB UART1\n");
*(unsigned int *)(APBC_REG(0x0004)) &= ~(0x3<<0);
*(unsigned int *)(APBC_REG(0x0004)) |= (0x1<<2);
printf("APB GPIO\n");
*(unsigned int *)(APBC_REG(0x0008)) &= ~(0x3<<0);
/* Do not set, will chang pmic, then fix voltage to 1.2V */
/* *(unsigned int *)(APBC_REG(0x0008)) |= (0x1<<2); */
printf("APB PWM0\n");
*(unsigned int *)(APBC_REG(0x000c)) |= (0x1<<2);
*(unsigned int *)(APBC_REG(0x000c)) &= ~(0x3<<0);
printf("APB PWM1\n");
*(unsigned int *)(APBC_REG(0x0010)) |= (0x1<<2);
*(unsigned int *)(APBC_REG(0x0010)) &= ~(0x2<<0);
printf("APB PWM2\n");
*(unsigned int *)(APBC_REG(0x0014)) &= ~(0x3<<0);
*(unsigned int *)(APBC_REG(0x0014)) |= (0x1<<2);
printf("APB PWM3\n");
*(unsigned int *)(APBC_REG(0x0018)) &= ~(0x2<<0);
*(unsigned int *)(APBC_REG(0x0018)) |= (0x1<<2);
printf("APB SSP0\n");
*(unsigned int *)(APBC_REG(0x001c)) &= ~(0x3<<0);
*(unsigned int *)(APBC_REG(0x001c)) |= (0x1<<2);
printf("APB SSP1\n");
*(unsigned int *)(APBC_REG(0x0020)) &= ~(0x3<<0);
*(unsigned int *)(APBC_REG(0x0020)) |= (0x1<<2);
printf("APB IPC\n");
*(unsigned int *)(APBC_REG(0x0024)) &= ~(0x3<<0);
*(unsigned int *)(APBC_REG(0x0024)) |= (0x1<<2);
printf("APB RTC\n");
*(unsigned int *)(APBC_REG(0x0028)) &= ~(0x3<<0);
*(unsigned int *)(APBC_REG(0x0028)) |= (0x1<<2);
*(unsigned int *)(APBC_REG(0x0028)) &= ~(0x1<<7);
printf("APB TWSI\n");
*(unsigned int *)(APBC_REG(0x002c)) &= ~(0x3<<0);
*(unsigned int *)(APBC_REG(0x002c)) |= (0x1<<2);
printf("APB KPC\n");
*(unsigned int *)(APBC_REG(0x0030)) &= ~(0x3<<0);
*(unsigned int *)(APBC_REG(0x0030)) |= (0x1<<2);
printf("APB TIMERS\n");
*(unsigned int *)(APBC_REG(0x0034)) &= ~(0x3<<0);
*(unsigned int *)(APBC_REG(0x0034)) |= (0x1<<2);
printf("APB TB_ROTARY\n");
*(unsigned int *)(APBC_REG(0x0038)) &= ~(0x3<<0);
*(unsigned int *)(APBC_REG(0x0038)) |= (0x1<<2);
printf("APB ATB\n");
*(unsigned int *)(APBC_REG(0x003c)) &= ~(0x3<<0);
*(unsigned int *)(APBC_REG(0x003c)) |= (0x1<<2);
printf("APB SW_JTAG\n");
*(unsigned int *)(APBC_REG(0x0040)) &= ~(0x3<<0);
*(unsigned int *)(APBC_REG(0x0040)) |= (0x1<<2);
/*
* Do not shutdown timer1 clk if it has already been
* set to wakeup cpu0/1
*/
if ((*(u32 *)CONFIG_LPM_TIMER_WAKEUP & (1 << 0 | 1 << 1))
== 0) {
printf("APB TIMERS1\n");
*(unsigned int *)(APBC_REG(0x0044)) &= ~(0x3<<0);
*(unsigned int *)(APBC_REG(0x0044)) |= (0x1<<2);
}
printf("APB ONEWIRE\n");
*(unsigned int *)(APBC_REG(0x0048)) &= ~(0x3<<0);
*(unsigned int *)(APBC_REG(0x0048)) |= (0x1<<2);
printf("APB SSP2\n");
*(unsigned int *)(APBC_REG(0x004c)) &= ~(0x3<<0);
*(unsigned int *)(APBC_REG(0x004c)) |= (0x1<<2);
printf("APB DRO_TS\n");
*(unsigned int *)(APBC_REG(0x0058)) &= ~(0x3<<0);
*(unsigned int *)(APBC_REG(0x0058)) |= (0x1<<2);
printf("APB I2C1\n");
*(unsigned int *)(APBC_REG(0x0060)) &= ~(0x3<<0);
*(unsigned int *)(APBC_REG(0x0060)) |= (0x1<<2);
if (has_feat_timer2()) {
/*
* Do not shutdown timer2 clk if it has already been
* set to wakeup cpu2/3.
*/
if ((*(u32 *)CONFIG_LPM_TIMER_WAKEUP &
(1 << 2 | 1 << 3)) == 0) {
printf("APB TIMERS2\n");
*(unsigned int *)(APBC_REG(0x0068)) &=
~(0x3<<0);
*(unsigned int *)(APBC_REG(0x0068)) |=
(0x1<<2);
}
}
/* hardware dynamic clk gate */
printf("Hardware dynamic clk gate reset\n");
*(unsigned int *)(APMU_REG(0x040)) = 0x0;
printf("APB UART0\n");
/* *(unsigned int *)(APBC_REG(0x0000)) &= ~(0x3<<0); */
/* *(unsigned int *)(APBC_REG(0x0000)) |= (0x1<<2); */
}
}
int do_deadidle(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
printf("deadidle\n");
u32 condition = simple_strtoul(argv[1], NULL, 10);
if (!condition)
condition = DEADIDLE_ALL;
pxa1x88_deadidle(condition);
printf("done\n");
printf("0xd42828f0=0x%010x\n", *(unsigned int *)(0xd42828f0));
return 0;
}
U_BOOT_CMD(
deadidle, 2, 1, do_deadidle,
"ready to do deadidle",
"[ op number ]"
);
int do_core_busy(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
int cur_cpu = hard_smp_processor_id();
*(u32 *)(CONFIG_CORE_BUSY_ADDR) |= 1 << cur_cpu;
uartsw_to_active_core();
while (1)
cpu_relax();
}
U_BOOT_CMD(
core_busy, 2, 1, do_core_busy,
"let current cpu do while(1)",
"[ core_busy]"
);
uint32_t seagull_wfi_op = 0xee074f90;
uint32_t seagull_loop_ops[] = {0xe1a00000, 0xeafffffd};
uint32_t msa_d2_ops[] = {
0x9ffce109, 0xd1e0e149, 0x327e3271, 0x0000e19c,
0x0000e19d, 0x0000e19e, 0x0000e19f, 0x0000e10a,
0xd407e14a, 0x60029111, 0x03d8e102, 0x3221504a,
0x00000064, 0x00000000, 0x00000000, 0x00000000,
0x00002000, 0x0035e800, 0x0004e100, 0x34094281,
0xffe0e151, 0xba709d08, 0xe141b870, 0x3401d101,
0xe10a9f00, 0xe14a1004, 0x9110ffe0, 0xb941bb40,
0x0008e100, 0xe1404280, 0x3208d101, 0x60089309,
0x00249f08, 0x00249310, 0x0000e100, 0xe1404280,
0x3200d101, 0x93016001, 0x91002001, 0xb920bb20,
0x1c020e18, 0x91002063, 0xb910bb10, 0x4a104f80,
0x1300e112, 0xffe0e152, 0x91009f10, 0xb900bb00,
0x4f804a50, 0x9f104a10, 0xba909100, 0x4a58b890,
0x4a104f80, 0x91009f10, 0xb880ba80, 0x4a584a50,
0x4a104f80, 0x91009f10, 0xb8d0bad0, 0x4a004f80,
0x9f104a10, 0xbac09100, 0x4a50b8c0, 0x4a004f80,
0x9f104a10, 0xbab09100, 0x4a58b8b0, 0x4a004f80,
0x9f104a10, 0xbaa09100, 0x4a50b8a0, 0x4f804a58,
0x4a104a00, 0x91009f10, 0xb932bb30, 0xe1804f82,
0x56824005, 0x91029f12, 0xb8e2bae2, 0x4f824a52,
0x9f125682, 0xbaf29102, 0x4a5ab8f2, 0x56824f82,
0x91029f12, 0xb9b2bbb2, 0x4a5a4a52, 0x56024f82,
0x91009f10, 0x93006408, 0x93012f99, 0x91002001,
0xb9e0bbe0, 0x1c020e18, 0x91002067, 0xb9d0bbd0,
0x4a104f80, 0x0300e112, 0xffe0e152, 0x91009f10,
0xb9c0bbc0, 0x4f804a50, 0x9f104a10, 0xbb609100,
0x4a58b960, 0x4a104f80, 0x91009f10, 0xb950bb50,
0x4a584a50, 0x4a104f80, 0x91009f10, 0xb990bb90,
0x4a004f80, 0x9f104a10, 0xbb809100, 0x4a50b980,
0x4a004f80, 0x9f104a10, 0xbb709100, 0x4a58b970,
0x4a004f80, 0x9f104a10, 0xbba09100, 0x4a50b9a0,
0x4f804a58, 0x4a104a00, 0x91009f10, 0xb812ba10,
0xe1804f82, 0x56824005, 0x91029f12, 0xffdae63a,
0xffdae43a, 0x4f824a52, 0x9f125682, 0xba629102,
0x4a5ab862, 0x56824f82, 0x91029f12, 0xffd8e63a,
0xffd8e43a, 0x4a5a4a52, 0x56024f82, 0x91009f10,
0x93006408, 0x93012f95, 0x91002001, 0xffd7e638,
0xffd7e438, 0x1c020e38, 0x9100201d, 0xffd6e638,
0xffd6e438, 0x4a004f68, 0x1300e112, 0xffe0e152,
0x91009f10, 0xffcfe638, 0xffcfe438, 0x4a004f68,
0x0300e112, 0xffe0e152, 0x91009f10, 0x93006408,
0x93012fdd, 0x91002001, 0xffcee638, 0xffcee438,
0x1c020e18, 0x9100208b, 0xffd3e638, 0xffd3e438,
0x4a104f80, 0x1300e112, 0xffe0e152, 0x91009f10,
0xffd2e638, 0xffd2e438, 0x4f804a50, 0x9f104a10,
0xe6389100, 0xe438ffd1, 0x4a58ffd1, 0x4a104f80,
0x91009f10, 0xffd0e638, 0xffd0e438, 0x4a584a50,
0x4a104f80, 0x91009f10, 0xffd9e638, 0xffd9e438,
0x9f104f80, 0xe6389100, 0xe438ffd4, 0x4a50ffd4,
0x9f104f80, 0xe6389100, 0xe438ffd5, 0x4a58ffd5,
0x9f104f80, 0xba209100, 0x4a50b820, 0x4f804a58,
0x91009f10, 0xb850ba50, 0x4a104f80, 0x0300e112,
0xffe0e152, 0x91009f10, 0xb840ba40, 0x4f804a50,
0x9f104a10, 0xba309100, 0x4a58b830, 0x4a104f80,
0x91009f10, 0xffdce638, 0xffdce438, 0x4a584a50,
0x4a104f80, 0x91009f10, 0xffdbe638, 0xffdbe438,
0x9f104f80, 0xba009100, 0x4a50b800, 0x9f104f80,
0xe6389100, 0xe438ffdf, 0x4a58ffdf, 0x9f104f80,
0xe6389100, 0xe438ffde, 0x4a50ffde, 0x4f804a58,
0x91009f10, 0x93006408, 0x9d002f6f, 0xffdde638,
0xffdde438, 0x00249f08, 0xbbf09108, 0x9310b9f0,
0xe8010024, 0x00100000, 0xe80005f5, 0xe10d0004,
0xe14d200c, 0xe100ffa8, 0x42860000, 0xe147303e,
0x932fd408, 0xe1000024, 0x42800014, 0xf024e140,
0xe1003200, 0xe14046e0, 0x9300c001, 0xe7fce108,
0xd401e148, 0x0049e180, 0xe3ff9300, 0xe108fe15,
0xe1480100, 0x9100f00e, 0xb9f1bbf0, 0xffffe100,
0xe1404280, 0x5401fe3f, 0x93004a00, 0xe1080024,
0xe1485050, 0x6008ffd0, 0x00249300, 0x1008e146,
0x0024932e, 0x2000e108, 0xffa8e148, 0x5008e100,
0xfe08e140, 0x00249300, 0x00240020, 0x00000000,
0x00000000, 0x00000000, 0x0024932f
};
uint32_t msa_loop_ops[] = {0x64086000, 0x00002fff};
void cp_msa_sleep(void)
{
if (__raw_readl(0xd4051004) & (1 << 31)) {
printf("C2 and MSA are already in D2!\n");
return;
}
if (cpu_is_pxa1U88()) {
/*
* Helan2 adds 2 extra bits: bit3 and bit12. They have
* to be set to ensure system can enter idle states.
*/
*(uint32_t *)(0xd4051000) = 0x12005008;
/*
* CPCR: must disable wakeup port0(GSM) and port1(TD) since
* there will be a constant GSM/TD wakeup event after board
* boots up.
* Pls note for Helan2, port5 must be enabled since it's used
* for GPS wakeup.
*/
*(uint32_t *)(0xD4050000) = 0xBEC87008;
} else {
*(uint32_t *)(0xd4051000) = 0x52004000;
*(uint32_t *)(0xD4050000) = 0xFECA6000;
}
/* POCR */
*(uint32_t *)(0xD405000c) = 0x001F0500;
/* SCCR */
*(uint32_t *)(0xD4050038) = 0x00000005;
/*
* PMUA_CP_IDLE_CFG, set 0x803FC720 (both retain),
* set 0x803f0760 for both power down, [15:14]-L2,
* [6]-L1
*/
*(uint32_t *)(0xd4282814) = 0x803fc720;
/* ICU_CP_GBL_INT_MSK */
*(uint32_t *)(0xd4282110) |= 0x3;
printf("put Seagull into D2 state now...\r\n");
*(uint32_t *)(0xd4282c24) = (uint32_t)&seagull_wfi_op;
/* release CP and Vote MSA SS available */
*(uint32_t *)(0xd4051020) &= (~0x5);
if (cpu_is_pxa988() || cpu_is_pxa1088() || cpu_is_pxa1L88()) {
*(uint32_t *)(0xd4200200) =
(((uint32_t)(&msa_d2_ops[0])) & 0xFF000000) >> 24;
*(uint32_t *)(0xd4070000) =
0xd0000000 + (((uint32_t)(&msa_d2_ops[0])) & 0xFFFFFF);
} else if (cpu_is_pxa1U88()) {
*(uint32_t *)(0xd4282d24) =
(((uint32_t)(&msa_d2_ops[0])) & 0xFC000000) >> 26;
*(uint32_t *)(0xd4070000) =
0xd8000000 + (((uint32_t)(&msa_d2_ops[0])) & 0x3FFFFFF);
}
printf("put MSA into D2 state now...\r\n");
*(uint32_t *)(0xd405003c) &= 0xfffffffc;
*(uint32_t *)(0xd4050020) |= 0x2C;
/* 500us */
udelay(512);
*(uint32_t *)(0xd4050020) &= ~(1 << 3);
*(uint32_t *)(0xd4050020) &= ~(1 << 2);
/* 20us */
udelay(32);
*(uint32_t *)(0xd4050020) &= ~(1 << 5);
udelay(1000);
/* until D2 is really entered */
while (!(__raw_readl(0xd4051004) & (1 << 31)))
cpu_relax();
printf("C2 and MSA in D2!\n");
}
int comm_d2(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
{
cp_msa_sleep();
return 0;
}
U_BOOT_CMD(
cpmsa, 1, 1, comm_d2,
"CP & MSA enter low power mode",
""
);