Commit f7dadb37 authored by Magnus Damm's avatar Magnus Damm Committed by Rafael J. Wysocki

PM / shmobile: Add support for the sh7372 A4S power domain / sleep mode

The sh7372 contains a power domain named A4S which in turn
contains power domains for both I/O Devices and CPU cores.

At this point only System wide Suspend-to-RAM is supported,
but the the hardware can also support CPUIdle. With more
efforts in the future CPUIdle can work with bot A4S and A3SM.

Tested on the sh7372 Mackerel board.

[rjw: Rebased on top of the current linux-pm tree.]
Signed-off-by: default avatarMagnus Damm <damm@opensource.se>
Signed-off-by: default avatarRafael J. Wysocki <rjw@sisk.pl>
parent c656c306
...@@ -35,8 +35,8 @@ extern void sh7372_add_standard_devices(void); ...@@ -35,8 +35,8 @@ extern void sh7372_add_standard_devices(void);
extern void sh7372_clock_init(void); extern void sh7372_clock_init(void);
extern void sh7372_pinmux_init(void); extern void sh7372_pinmux_init(void);
extern void sh7372_pm_init(void); extern void sh7372_pm_init(void);
extern void sh7372_resume_core_standby_a3sm(void); extern void sh7372_resume_core_standby_sysc(void);
extern int sh7372_do_idle_a3sm(unsigned long unused); extern int sh7372_do_idle_sysc(unsigned long sleep_mode);
extern struct clk sh7372_extal1_clk; extern struct clk sh7372_extal1_clk;
extern struct clk sh7372_extal2_clk; extern struct clk sh7372_extal2_clk;
......
...@@ -499,6 +499,7 @@ extern struct sh7372_pm_domain sh7372_d4; ...@@ -499,6 +499,7 @@ extern struct sh7372_pm_domain sh7372_d4;
extern struct sh7372_pm_domain sh7372_a4r; extern struct sh7372_pm_domain sh7372_a4r;
extern struct sh7372_pm_domain sh7372_a3rv; extern struct sh7372_pm_domain sh7372_a3rv;
extern struct sh7372_pm_domain sh7372_a3ri; extern struct sh7372_pm_domain sh7372_a3ri;
extern struct sh7372_pm_domain sh7372_a4s;
extern struct sh7372_pm_domain sh7372_a3sp; extern struct sh7372_pm_domain sh7372_a3sp;
extern struct sh7372_pm_domain sh7372_a3sg; extern struct sh7372_pm_domain sh7372_a3sg;
...@@ -515,5 +516,7 @@ extern void sh7372_pm_add_subdomain(struct sh7372_pm_domain *sh7372_pd, ...@@ -515,5 +516,7 @@ extern void sh7372_pm_add_subdomain(struct sh7372_pm_domain *sh7372_pd,
extern void sh7372_intcs_suspend(void); extern void sh7372_intcs_suspend(void);
extern void sh7372_intcs_resume(void); extern void sh7372_intcs_resume(void);
extern void sh7372_intca_suspend(void);
extern void sh7372_intca_resume(void);
#endif /* __ASM_SH7372_H__ */ #endif /* __ASM_SH7372_H__ */
...@@ -611,3 +611,52 @@ void sh7372_intcs_resume(void) ...@@ -611,3 +611,52 @@ void sh7372_intcs_resume(void)
for (k = 0x80; k <= 0x9c; k += 4) for (k = 0x80; k <= 0x9c; k += 4)
__raw_writeb(ffd5[k], intcs_ffd5 + k); __raw_writeb(ffd5[k], intcs_ffd5 + k);
} }
static unsigned short e694[0x200];
static unsigned short e695[0x200];
void sh7372_intca_suspend(void)
{
int k;
for (k = 0x00; k <= 0x38; k += 4)
e694[k] = __raw_readw(0xe6940000 + k);
for (k = 0x80; k <= 0xb4; k += 4)
e694[k] = __raw_readb(0xe6940000 + k);
for (k = 0x180; k <= 0x1b4; k += 4)
e694[k] = __raw_readb(0xe6940000 + k);
for (k = 0x00; k <= 0x50; k += 4)
e695[k] = __raw_readw(0xe6950000 + k);
for (k = 0x80; k <= 0xa8; k += 4)
e695[k] = __raw_readb(0xe6950000 + k);
for (k = 0x180; k <= 0x1a8; k += 4)
e695[k] = __raw_readb(0xe6950000 + k);
}
void sh7372_intca_resume(void)
{
int k;
for (k = 0x00; k <= 0x38; k += 4)
__raw_writew(e694[k], 0xe6940000 + k);
for (k = 0x80; k <= 0xb4; k += 4)
__raw_writeb(e694[k], 0xe6940000 + k);
for (k = 0x180; k <= 0x1b4; k += 4)
__raw_writeb(e694[k], 0xe6940000 + k);
for (k = 0x00; k <= 0x50; k += 4)
__raw_writew(e695[k], 0xe6950000 + k);
for (k = 0x80; k <= 0xa8; k += 4)
__raw_writeb(e695[k], 0xe6950000 + k);
for (k = 0x180; k <= 0x1a8; k += 4)
__raw_writeb(e695[k], 0xe6950000 + k);
}
...@@ -256,6 +256,14 @@ struct sh7372_pm_domain sh7372_a3ri = { ...@@ -256,6 +256,14 @@ struct sh7372_pm_domain sh7372_a3ri = {
.bit_shift = 8, .bit_shift = 8,
}; };
struct sh7372_pm_domain sh7372_a4s = {
.genpd.name = "A4S",
.bit_shift = 10,
.gov = &pm_domain_always_on_gov,
.no_debug = true,
.stay_on = true,
};
struct sh7372_pm_domain sh7372_a3sp = { struct sh7372_pm_domain sh7372_a3sp = {
.genpd.name = "A3SP", .genpd.name = "A3SP",
.bit_shift = 11, .bit_shift = 11,
...@@ -289,11 +297,16 @@ static int sh7372_do_idle_core_standby(unsigned long unused) ...@@ -289,11 +297,16 @@ static int sh7372_do_idle_core_standby(unsigned long unused)
return 0; return 0;
} }
static void sh7372_enter_core_standby(void) static void sh7372_set_reset_vector(unsigned long address)
{ {
/* set reset vector, translate 4k */ /* set reset vector, translate 4k */
__raw_writel(__pa(sh7372_resume_core_standby_a3sm), SBAR); __raw_writel(address, SBAR);
__raw_writel(0, APARMBAREA); __raw_writel(0, APARMBAREA);
}
static void sh7372_enter_core_standby(void)
{
sh7372_set_reset_vector(__pa(sh7372_resume_core_standby_sysc));
/* enter sleep mode with SYSTBCR to 0x10 */ /* enter sleep mode with SYSTBCR to 0x10 */
__raw_writel(0x10, SYSTBCR); __raw_writel(0x10, SYSTBCR);
...@@ -306,27 +319,22 @@ static void sh7372_enter_core_standby(void) ...@@ -306,27 +319,22 @@ static void sh7372_enter_core_standby(void)
#endif #endif
#ifdef CONFIG_SUSPEND #ifdef CONFIG_SUSPEND
static void sh7372_enter_a3sm_common(int pllc0_on) static void sh7372_enter_sysc(int pllc0_on, unsigned long sleep_mode)
{ {
/* set reset vector, translate 4k */
__raw_writel(__pa(sh7372_resume_core_standby_a3sm), SBAR);
__raw_writel(0, APARMBAREA);
if (pllc0_on) if (pllc0_on)
__raw_writel(0, PLLC01STPCR); __raw_writel(0, PLLC01STPCR);
else else
__raw_writel(1 << 28, PLLC01STPCR); __raw_writel(1 << 28, PLLC01STPCR);
__raw_writel(0, PDNSEL); /* power-down A3SM only, not A4S */
__raw_readl(WUPSFAC); /* read wakeup int. factor before sleep */ __raw_readl(WUPSFAC); /* read wakeup int. factor before sleep */
cpu_suspend(0, sh7372_do_idle_a3sm); cpu_suspend(sleep_mode, sh7372_do_idle_sysc);
__raw_readl(WUPSFAC); /* read wakeup int. factor after wakeup */ __raw_readl(WUPSFAC); /* read wakeup int. factor after wakeup */
/* disable reset vector translation */ /* disable reset vector translation */
__raw_writel(0, SBAR); __raw_writel(0, SBAR);
} }
static int sh7372_a3sm_valid(unsigned long *mskp, unsigned long *msk2p) static int sh7372_sysc_valid(unsigned long *mskp, unsigned long *msk2p)
{ {
unsigned long mstpsr0, mstpsr1, mstpsr2, mstpsr3, mstpsr4; unsigned long mstpsr0, mstpsr1, mstpsr2, mstpsr3, mstpsr4;
unsigned long msk, msk2; unsigned long msk, msk2;
...@@ -414,7 +422,7 @@ static void sh7372_icr_to_irqcr(unsigned long icr, u16 *irqcr1p, u16 *irqcr2p) ...@@ -414,7 +422,7 @@ static void sh7372_icr_to_irqcr(unsigned long icr, u16 *irqcr1p, u16 *irqcr2p)
*irqcr2p = irqcr2; *irqcr2p = irqcr2;
} }
static void sh7372_setup_a3sm(unsigned long msk, unsigned long msk2) static void sh7372_setup_sysc(unsigned long msk, unsigned long msk2)
{ {
u16 irqcrx_low, irqcrx_high, irqcry_low, irqcry_high; u16 irqcrx_low, irqcrx_high, irqcry_low, irqcry_high;
unsigned long tmp; unsigned long tmp;
...@@ -447,6 +455,22 @@ static void sh7372_setup_a3sm(unsigned long msk, unsigned long msk2) ...@@ -447,6 +455,22 @@ static void sh7372_setup_a3sm(unsigned long msk, unsigned long msk2)
__raw_writel((irqcrx_high << 16) | irqcrx_low, IRQCR3); __raw_writel((irqcrx_high << 16) | irqcrx_low, IRQCR3);
__raw_writel((irqcry_high << 16) | irqcry_low, IRQCR4); __raw_writel((irqcry_high << 16) | irqcry_low, IRQCR4);
} }
static void sh7372_enter_a3sm_common(int pllc0_on)
{
sh7372_set_reset_vector(__pa(sh7372_resume_core_standby_sysc));
sh7372_enter_sysc(pllc0_on, 1 << 12);
}
static void sh7372_enter_a4s_common(int pllc0_on)
{
sh7372_intca_suspend();
memcpy((void *)SMFRAM, sh7372_resume_core_standby_sysc, 0x100);
sh7372_set_reset_vector(SMFRAM);
sh7372_enter_sysc(pllc0_on, 1 << 10);
sh7372_intca_resume();
}
#endif #endif
#ifdef CONFIG_CPU_IDLE #ifdef CONFIG_CPU_IDLE
...@@ -480,14 +504,20 @@ static int sh7372_enter_suspend(suspend_state_t suspend_state) ...@@ -480,14 +504,20 @@ static int sh7372_enter_suspend(suspend_state_t suspend_state)
unsigned long msk, msk2; unsigned long msk, msk2;
/* check active clocks to determine potential wakeup sources */ /* check active clocks to determine potential wakeup sources */
if (sh7372_a3sm_valid(&msk, &msk2)) { if (sh7372_sysc_valid(&msk, &msk2)) {
/* convert INTC mask and sense to SYSC mask and sense */ /* convert INTC mask and sense to SYSC mask and sense */
sh7372_setup_a3sm(msk, msk2); sh7372_setup_sysc(msk, msk2);
/* enter A3SM sleep with PLLC0 off */ if (!sh7372_a3sp.stay_on &&
pr_debug("entering A3SM\n"); sh7372_a4s.genpd.status == GPD_STATE_POWER_OFF) {
sh7372_enter_a3sm_common(0); /* enter A4S sleep with PLLC0 off */
pr_debug("entering A4S\n");
sh7372_enter_a4s_common(0);
} else {
/* enter A3SM sleep with PLLC0 off */
pr_debug("entering A3SM\n");
sh7372_enter_a3sm_common(0);
}
} else { } else {
/* default to Core Standby that supports all wakeup sources */ /* default to Core Standby that supports all wakeup sources */
pr_debug("entering Core Standby\n"); pr_debug("entering Core Standby\n");
......
...@@ -994,12 +994,16 @@ void __init sh7372_add_standard_devices(void) ...@@ -994,12 +994,16 @@ void __init sh7372_add_standard_devices(void)
sh7372_init_pm_domain(&sh7372_a4r); sh7372_init_pm_domain(&sh7372_a4r);
sh7372_init_pm_domain(&sh7372_a3rv); sh7372_init_pm_domain(&sh7372_a3rv);
sh7372_init_pm_domain(&sh7372_a3ri); sh7372_init_pm_domain(&sh7372_a3ri);
sh7372_init_pm_domain(&sh7372_a3sg); sh7372_init_pm_domain(&sh7372_a4s);
sh7372_init_pm_domain(&sh7372_a3sp); sh7372_init_pm_domain(&sh7372_a3sp);
sh7372_init_pm_domain(&sh7372_a3sg);
sh7372_pm_add_subdomain(&sh7372_a4lc, &sh7372_a3rv); sh7372_pm_add_subdomain(&sh7372_a4lc, &sh7372_a3rv);
sh7372_pm_add_subdomain(&sh7372_a4r, &sh7372_a4lc); sh7372_pm_add_subdomain(&sh7372_a4r, &sh7372_a4lc);
sh7372_pm_add_subdomain(&sh7372_a4s, &sh7372_a3sg);
sh7372_pm_add_subdomain(&sh7372_a4s, &sh7372_a3sp);
platform_add_devices(sh7372_early_devices, platform_add_devices(sh7372_early_devices,
ARRAY_SIZE(sh7372_early_devices)); ARRAY_SIZE(sh7372_early_devices));
......
...@@ -37,13 +37,18 @@ ...@@ -37,13 +37,18 @@
#if defined(CONFIG_SUSPEND) || defined(CONFIG_CPU_IDLE) #if defined(CONFIG_SUSPEND) || defined(CONFIG_CPU_IDLE)
.align 12 .align 12
.text .text
.global sh7372_resume_core_standby_a3sm .global sh7372_resume_core_standby_sysc
sh7372_resume_core_standby_a3sm: sh7372_resume_core_standby_sysc:
ldr pc, 1f ldr pc, 1f
1: .long cpu_resume - PAGE_OFFSET + PLAT_PHYS_OFFSET 1: .long cpu_resume - PAGE_OFFSET + PLAT_PHYS_OFFSET
.global sh7372_do_idle_a3sm #define SPDCR 0xe6180008
sh7372_do_idle_a3sm:
/* A3SM & A4S power down */
.global sh7372_do_idle_sysc
sh7372_do_idle_sysc:
mov r8, r0 /* sleep mode passed in r0 */
/* /*
* Clear the SCTLR.C bit to prevent further data cache * Clear the SCTLR.C bit to prevent further data cache
* allocation. Clearing SCTLR.C would make all the data accesses * allocation. Clearing SCTLR.C would make all the data accesses
...@@ -80,13 +85,9 @@ sh7372_do_idle_a3sm: ...@@ -80,13 +85,9 @@ sh7372_do_idle_a3sm:
dsb dsb
dmb dmb
#define SPDCR 0xe6180008 /* SYSC power down */
#define A3SM (1 << 12)
/* A3SM power down */
ldr r0, =SPDCR ldr r0, =SPDCR
ldr r1, =A3SM str r8, [r0]
str r1, [r0]
1: 1:
b 1b b 1b
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment