]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - arch/arm/mach-shmobile/pm-sh7372.c
Merge tag 'pm-for-3.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael...
[karo-tx-linux.git] / arch / arm / mach-shmobile / pm-sh7372.c
index 162121842a2b0e97cb2d032ce8d0ba6c0958041a..a0826a48dd0885b921d5cc95d524848683353790 100644 (file)
@@ -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>
 
 #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);
+}