Skip to content

Commit

Permalink
PM / shmobile: Add support for the sh7372 A4S power domain / sleep mode
Browse files Browse the repository at this point in the history
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: Magnus Damm <damm@opensource.se>
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
  • Loading branch information
Magnus Damm authored and Rafael J. Wysocki committed Dec 25, 2011
1 parent c656c30 commit f7dadb3
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 31 deletions.
4 changes: 2 additions & 2 deletions arch/arm/mach-shmobile/include/mach/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ extern void sh7372_add_standard_devices(void);
extern void sh7372_clock_init(void);
extern void sh7372_pinmux_init(void);
extern void sh7372_pm_init(void);
extern void sh7372_resume_core_standby_a3sm(void);
extern int sh7372_do_idle_a3sm(unsigned long unused);
extern void sh7372_resume_core_standby_sysc(void);
extern int sh7372_do_idle_sysc(unsigned long sleep_mode);
extern struct clk sh7372_extal1_clk;
extern struct clk sh7372_extal2_clk;

Expand Down
3 changes: 3 additions & 0 deletions arch/arm/mach-shmobile/include/mach/sh7372.h
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,7 @@ extern struct sh7372_pm_domain sh7372_d4;
extern struct sh7372_pm_domain sh7372_a4r;
extern struct sh7372_pm_domain sh7372_a3rv;
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_a3sg;

Expand All @@ -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_resume(void);
extern void sh7372_intca_suspend(void);
extern void sh7372_intca_resume(void);

#endif /* __ASM_SH7372_H__ */
49 changes: 49 additions & 0 deletions arch/arm/mach-shmobile/intc-sh7372.c
Original file line number Diff line number Diff line change
Expand Up @@ -611,3 +611,52 @@ void sh7372_intcs_resume(void)
for (k = 0x80; k <= 0x9c; k += 4)
__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);
}
66 changes: 48 additions & 18 deletions arch/arm/mach-shmobile/pm-sh7372.c
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,14 @@ struct sh7372_pm_domain sh7372_a3ri = {
.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 = {
.genpd.name = "A3SP",
.bit_shift = 11,
Expand Down Expand Up @@ -289,11 +297,16 @@ static int sh7372_do_idle_core_standby(unsigned long unused)
return 0;
}

static void sh7372_enter_core_standby(void)
static void sh7372_set_reset_vector(unsigned long address)
{
/* set reset vector, translate 4k */
__raw_writel(__pa(sh7372_resume_core_standby_a3sm), SBAR);
__raw_writel(address, SBAR);
__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 */
__raw_writel(0x10, SYSTBCR);
Expand All @@ -306,27 +319,22 @@ static void sh7372_enter_core_standby(void)
#endif

#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)
__raw_writel(0, PLLC01STPCR);
else
__raw_writel(1 << 28, PLLC01STPCR);

__raw_writel(0, PDNSEL); /* power-down A3SM only, not A4S */
__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 */

/* disable reset vector translation */
__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 msk, msk2;
Expand Down Expand Up @@ -414,7 +422,7 @@ static void sh7372_icr_to_irqcr(unsigned long icr, u16 *irqcr1p, u16 *irqcr2p)
*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;
unsigned long tmp;
Expand Down Expand Up @@ -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((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

#ifdef CONFIG_CPU_IDLE
Expand Down Expand Up @@ -480,14 +504,20 @@ static int sh7372_enter_suspend(suspend_state_t suspend_state)
unsigned long msk, msk2;

/* 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 */
sh7372_setup_a3sm(msk, msk2);

/* enter A3SM sleep with PLLC0 off */
pr_debug("entering A3SM\n");
sh7372_enter_a3sm_common(0);
sh7372_setup_sysc(msk, msk2);

if (!sh7372_a3sp.stay_on &&
sh7372_a4s.genpd.status == GPD_STATE_POWER_OFF) {
/* 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 {
/* default to Core Standby that supports all wakeup sources */
pr_debug("entering Core Standby\n");
Expand Down
6 changes: 5 additions & 1 deletion arch/arm/mach-shmobile/setup-sh7372.c
Original file line number Diff line number Diff line change
Expand Up @@ -994,12 +994,16 @@ void __init sh7372_add_standard_devices(void)
sh7372_init_pm_domain(&sh7372_a4r);
sh7372_init_pm_domain(&sh7372_a3rv);
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_a3sg);

sh7372_pm_add_subdomain(&sh7372_a4lc, &sh7372_a3rv);
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,
ARRAY_SIZE(sh7372_early_devices));

Expand Down
21 changes: 11 additions & 10 deletions arch/arm/mach-shmobile/sleep-sh7372.S
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,18 @@
#if defined(CONFIG_SUSPEND) || defined(CONFIG_CPU_IDLE)
.align 12
.text
.global sh7372_resume_core_standby_a3sm
sh7372_resume_core_standby_a3sm:
.global sh7372_resume_core_standby_sysc
sh7372_resume_core_standby_sysc:
ldr pc, 1f
1: .long cpu_resume - PAGE_OFFSET + PLAT_PHYS_OFFSET

.global sh7372_do_idle_a3sm
sh7372_do_idle_a3sm:
#define SPDCR 0xe6180008

/* 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
* allocation. Clearing SCTLR.C would make all the data accesses
Expand Down Expand Up @@ -80,13 +85,9 @@ sh7372_do_idle_a3sm:
dsb
dmb

#define SPDCR 0xe6180008
#define A3SM (1 << 12)

/* A3SM power down */
/* SYSC power down */
ldr r0, =SPDCR
ldr r1, =A3SM
str r1, [r0]
str r8, [r0]
1:
b 1b

Expand Down

0 comments on commit f7dadb3

Please sign in to comment.