diff options
Diffstat (limited to 'arch/arm/mach-shmobile/pm-sh7372.c')
-rw-r--r-- | arch/arm/mach-shmobile/pm-sh7372.c | 283 |
1 files changed, 181 insertions, 102 deletions
diff --git a/arch/arm/mach-shmobile/pm-sh7372.c b/arch/arm/mach-shmobile/pm-sh7372.c index 162121842a2b..a0826a48dd08 100644 --- a/arch/arm/mach-shmobile/pm-sh7372.c +++ b/arch/arm/mach-shmobile/pm-sh7372.c @@ -21,6 +21,7 @@ #include <linux/irq.h> #include <linux/bitrev.h> #include <linux/console.h> +#include <asm/cpuidle.h> #include <asm/io.h> #include <asm/tlbflush.h> #include <asm/suspend.h> @@ -72,20 +73,7 @@ #ifdef CONFIG_PM -struct rmobile_pm_domain sh7372_pd_a4lc = { - .genpd.name = "A4LC", - .bit_shift = 1, -}; - -struct rmobile_pm_domain sh7372_pd_a4mp = { - .genpd.name = "A4MP", - .bit_shift = 2, -}; - -struct rmobile_pm_domain sh7372_pd_d4 = { - .genpd.name = "D4", - .bit_shift = 3, -}; +#define PM_DOMAIN_ON_OFF_LATENCY_NS 250000 static int sh7372_a4r_pd_suspend(void) { @@ -94,39 +82,25 @@ static int sh7372_a4r_pd_suspend(void) return 0; } -struct rmobile_pm_domain sh7372_pd_a4r = { - .genpd.name = "A4R", - .bit_shift = 5, - .suspend = sh7372_a4r_pd_suspend, - .resume = sh7372_intcs_resume, -}; +static bool a4s_suspend_ready; -struct rmobile_pm_domain sh7372_pd_a3rv = { - .genpd.name = "A3RV", - .bit_shift = 6, -}; - -struct rmobile_pm_domain sh7372_pd_a3ri = { - .genpd.name = "A3RI", - .bit_shift = 8, -}; - -static int sh7372_pd_a4s_suspend(void) +static int sh7372_a4s_pd_suspend(void) { /* * The A4S domain contains the CPU core and therefore it should - * only be turned off if the CPU is in use. + * only be turned off if the CPU is not in use. This may happen + * during system suspend, when SYSC is going to be used for generating + * resume signals and a4s_suspend_ready is set to let + * sh7372_enter_suspend() know that it can turn A4S off. */ + a4s_suspend_ready = true; return -EBUSY; } -struct rmobile_pm_domain sh7372_pd_a4s = { - .genpd.name = "A4S", - .bit_shift = 10, - .gov = &pm_domain_always_on_gov, - .no_debug = true, - .suspend = sh7372_pd_a4s_suspend, -}; +static void sh7372_a4s_pd_resume(void) +{ + a4s_suspend_ready = false; +} static int sh7372_a3sp_pd_suspend(void) { @@ -137,18 +111,80 @@ static int sh7372_a3sp_pd_suspend(void) return console_suspend_enabled ? 0 : -EBUSY; } -struct rmobile_pm_domain sh7372_pd_a3sp = { - .genpd.name = "A3SP", - .bit_shift = 11, - .gov = &pm_domain_always_on_gov, - .no_debug = true, - .suspend = sh7372_a3sp_pd_suspend, +static struct rmobile_pm_domain sh7372_pm_domains[] = { + { + .genpd.name = "A4LC", + .genpd.power_on_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, + .genpd.power_off_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, + .bit_shift = 1, + }, + { + .genpd.name = "A4MP", + .genpd.power_on_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, + .genpd.power_off_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, + .bit_shift = 2, + }, + { + .genpd.name = "D4", + .genpd.power_on_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, + .genpd.power_off_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, + .bit_shift = 3, + }, + { + .genpd.name = "A4R", + .genpd.power_on_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, + .genpd.power_off_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, + .bit_shift = 5, + .suspend = sh7372_a4r_pd_suspend, + .resume = sh7372_intcs_resume, + }, + { + .genpd.name = "A3RV", + .genpd.power_on_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, + .genpd.power_off_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, + .bit_shift = 6, + }, + { + .genpd.name = "A3RI", + .genpd.power_on_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, + .genpd.power_off_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, + .bit_shift = 8, + }, + { + .genpd.name = "A4S", + .genpd.power_on_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, + .genpd.power_off_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, + .bit_shift = 10, + .gov = &pm_domain_always_on_gov, + .no_debug = true, + .suspend = sh7372_a4s_pd_suspend, + .resume = sh7372_a4s_pd_resume, + }, + { + .genpd.name = "A3SP", + .genpd.power_on_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, + .genpd.power_off_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, + .bit_shift = 11, + .gov = &pm_domain_always_on_gov, + .no_debug = true, + .suspend = sh7372_a3sp_pd_suspend, + }, + { + .genpd.name = "A3SG", + .genpd.power_on_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, + .genpd.power_off_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, + .bit_shift = 13, + }, }; -struct rmobile_pm_domain sh7372_pd_a3sg = { - .genpd.name = "A3SG", - .bit_shift = 13, -}; +void __init sh7372_init_pm_domains(void) +{ + rmobile_init_domains(sh7372_pm_domains, ARRAY_SIZE(sh7372_pm_domains)); + pm_genpd_add_subdomain_names("A4LC", "A3RV"); + pm_genpd_add_subdomain_names("A4R", "A4LC"); + pm_genpd_add_subdomain_names("A4S", "A3SG"); + pm_genpd_add_subdomain_names("A4S", "A3SP"); +} #endif /* CONFIG_PM */ @@ -304,6 +340,21 @@ 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(); + sh7372_set_reset_vector(SMFRAM); + sh7372_enter_sysc(pllc0_on, 1 << 10); + sh7372_intca_resume(); +} + +static void sh7372_pm_setup_smfram(void) +{ + memcpy((void *)SMFRAM, sh7372_resume_core_standby_sysc, 0x100); +} +#else +static inline void sh7372_pm_setup_smfram(void) {} #endif /* CONFIG_SUSPEND || CONFIG_CPU_IDLE */ #ifdef CONFIG_CPU_IDLE @@ -313,7 +364,8 @@ static int sh7372_do_idle_core_standby(unsigned long unused) return 0; } -static void sh7372_enter_core_standby(void) +static int sh7372_enter_core_standby(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) { sh7372_set_reset_vector(__pa(sh7372_resume_core_standby_sysc)); @@ -324,83 +376,102 @@ static void sh7372_enter_core_standby(void) /* disable reset vector translation */ __raw_writel(0, SBAR); + + return 1; } -static void sh7372_enter_a3sm_pll_on(void) +static int sh7372_enter_a3sm_pll_on(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) { sh7372_enter_a3sm_common(1); + return 2; } -static void sh7372_enter_a3sm_pll_off(void) +static int sh7372_enter_a3sm_pll_off(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) { sh7372_enter_a3sm_common(0); + return 3; } -static void sh7372_cpuidle_setup(struct cpuidle_driver *drv) +static int sh7372_enter_a4s(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) { - struct cpuidle_state *state = &drv->states[drv->state_count]; - - snprintf(state->name, CPUIDLE_NAME_LEN, "C2"); - strncpy(state->desc, "Core Standby Mode", CPUIDLE_DESC_LEN); - state->exit_latency = 10; - state->target_residency = 20 + 10; - state->flags = CPUIDLE_FLAG_TIME_VALID; - shmobile_cpuidle_modes[drv->state_count] = sh7372_enter_core_standby; - drv->state_count++; - - state = &drv->states[drv->state_count]; - snprintf(state->name, CPUIDLE_NAME_LEN, "C3"); - strncpy(state->desc, "A3SM PLL ON", CPUIDLE_DESC_LEN); - state->exit_latency = 20; - state->target_residency = 30 + 20; - state->flags = CPUIDLE_FLAG_TIME_VALID; - shmobile_cpuidle_modes[drv->state_count] = sh7372_enter_a3sm_pll_on; - drv->state_count++; - - state = &drv->states[drv->state_count]; - snprintf(state->name, CPUIDLE_NAME_LEN, "C4"); - strncpy(state->desc, "A3SM PLL OFF", CPUIDLE_DESC_LEN); - state->exit_latency = 120; - state->target_residency = 30 + 120; - state->flags = CPUIDLE_FLAG_TIME_VALID; - shmobile_cpuidle_modes[drv->state_count] = sh7372_enter_a3sm_pll_off; - drv->state_count++; + unsigned long msk, msk2; + + if (!sh7372_sysc_valid(&msk, &msk2)) + return sh7372_enter_a3sm_pll_off(dev, drv, index); + + sh7372_setup_sysc(msk, msk2); + sh7372_enter_a4s_common(0); + return 4; } +static struct cpuidle_driver sh7372_cpuidle_driver = { + .name = "sh7372_cpuidle", + .owner = THIS_MODULE, + .en_core_tk_irqen = 1, + .state_count = 5, + .safe_state_index = 0, /* C1 */ + .states[0] = ARM_CPUIDLE_WFI_STATE, + .states[0].enter = shmobile_enter_wfi, + .states[1] = { + .name = "C2", + .desc = "Core Standby Mode", + .exit_latency = 10, + .target_residency = 20 + 10, + .flags = CPUIDLE_FLAG_TIME_VALID, + .enter = sh7372_enter_core_standby, + }, + .states[2] = { + .name = "C3", + .desc = "A3SM PLL ON", + .exit_latency = 20, + .target_residency = 30 + 20, + .flags = CPUIDLE_FLAG_TIME_VALID, + .enter = sh7372_enter_a3sm_pll_on, + }, + .states[3] = { + .name = "C4", + .desc = "A3SM PLL OFF", + .exit_latency = 120, + .target_residency = 30 + 120, + .flags = CPUIDLE_FLAG_TIME_VALID, + .enter = sh7372_enter_a3sm_pll_off, + }, + .states[4] = { + .name = "C5", + .desc = "A4S PLL OFF", + .exit_latency = 240, + .target_residency = 30 + 240, + .flags = CPUIDLE_FLAG_TIME_VALID, + .enter = sh7372_enter_a4s, + .disabled = true, + }, +}; + static void sh7372_cpuidle_init(void) { - shmobile_cpuidle_setup = sh7372_cpuidle_setup; + shmobile_cpuidle_set_driver(&sh7372_cpuidle_driver); } #else static void sh7372_cpuidle_init(void) {} #endif #ifdef CONFIG_SUSPEND -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(); -} - static int sh7372_enter_suspend(suspend_state_t suspend_state) { unsigned long msk, msk2; /* check active clocks to determine potential wakeup sources */ - if (sh7372_sysc_valid(&msk, &msk2)) { - if (!console_suspend_enabled && - sh7372_pd_a4s.genpd.status == GPD_STATE_POWER_OFF) { - /* convert INTC mask/sense to SYSC mask/sense */ - sh7372_setup_sysc(msk, msk2); - - /* enter A4S sleep with PLLC0 off */ - pr_debug("entering A4S\n"); - sh7372_enter_a4s_common(0); - return 0; - } + if (sh7372_sysc_valid(&msk, &msk2) && a4s_suspend_ready) { + /* convert INTC mask/sense to SYSC mask/sense */ + sh7372_setup_sysc(msk, msk2); + + /* enter A4S sleep with PLLC0 off */ + pr_debug("entering A4S\n"); + sh7372_enter_a4s_common(0); + return 0; } /* default to enter A3SM sleep with PLLC0 off */ @@ -426,7 +497,7 @@ static int sh7372_pm_notifier_fn(struct notifier_block *notifier, * executed during system suspend and resume, respectively, so * that those functions don't crash while accessing the INTCS. */ - pm_genpd_poweron(&sh7372_pd_a4r.genpd); + pm_genpd_name_poweron("A4R"); break; case PM_POST_SUSPEND: pm_genpd_poweroff_unused(); @@ -455,6 +526,14 @@ void __init sh7372_pm_init(void) /* do not convert A3SM, A3SP, A3SG, A4R power down into A4S */ __raw_writel(0, PDNSEL); + sh7372_pm_setup_smfram(); + sh7372_suspend_init(); sh7372_cpuidle_init(); } + +void __init sh7372_pm_init_late(void) +{ + shmobile_init_late(); + pm_genpd_name_attach_cpuidle("A4S", 4); +} |