|
@@ -412,3 +412,128 @@ static struct cpuidle_driver sh7372_cpuidle_driver = {
|
|
|
.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_set_driver(&sh7372_cpuidle_driver);
|
|
|
+}
|
|
|
+#else
|
|
|
+static void sh7372_cpuidle_init(void) {}
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifdef CONFIG_SUSPEND
|
|
|
+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) && 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 */
|
|
|
+ pr_debug("entering A3SM\n");
|
|
|
+ sh7372_enter_a3sm_common(0);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * sh7372_pm_notifier_fn - SH7372 PM notifier routine.
|
|
|
+ * @notifier: Unused.
|
|
|
+ * @pm_event: Event being handled.
|
|
|
+ * @unused: Unused.
|
|
|
+ */
|
|
|
+static int sh7372_pm_notifier_fn(struct notifier_block *notifier,
|
|
|
+ unsigned long pm_event, void *unused)
|
|
|
+{
|
|
|
+ switch (pm_event) {
|
|
|
+ case PM_SUSPEND_PREPARE:
|
|
|
+ /*
|
|
|
+ * This is necessary, because the A4R domain has to be "on"
|
|
|
+ * when suspend_device_irqs() and resume_device_irqs() are
|
|
|
+ * executed during system suspend and resume, respectively, so
|
|
|
+ * that those functions don't crash while accessing the INTCS.
|
|
|
+ */
|
|
|
+ pm_genpd_name_poweron("A4R");
|
|
|
+ break;
|
|
|
+ case PM_POST_SUSPEND:
|
|
|
+ pm_genpd_poweroff_unused();
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return NOTIFY_DONE;
|
|
|
+}
|
|
|
+
|
|
|
+static void sh7372_suspend_init(void)
|
|
|
+{
|
|
|
+ shmobile_suspend_ops.enter = sh7372_enter_suspend;
|
|
|
+ pm_notifier(sh7372_pm_notifier_fn, 0);
|
|
|
+}
|
|
|
+#else
|
|
|
+static void sh7372_suspend_init(void) {}
|
|
|
+#endif
|
|
|
+
|
|
|
+void __init sh7372_pm_init(void)
|
|
|
+{
|
|
|
+ /* enable DBG hardware block to kick SYSC */
|
|
|
+ __raw_writel(0x0000a500, DBGREG9);
|
|
|
+ __raw_writel(0x0000a501, DBGREG9);
|
|
|
+ __raw_writel(0x00000000, DBGREG1);
|
|
|
+
|
|
|
+ /* 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);
|
|
|
+}
|