Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -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;

+3 −0
Original line number Diff line number Diff line
@@ -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;

@@ -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 −0
Original line number Diff line number Diff line
@@ -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);
}
+48 −18
Original line number Diff line number Diff line
@@ -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,
@@ -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);
@@ -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;
@@ -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;
@@ -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
@@ -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);
		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");
+5 −1
Original line number Diff line number Diff line
@@ -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));

Loading