Merge tag 'boards' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
[linux-2.6.git] / arch / arm / mach-s3c64xx / pm.c
index 0868d13..055dac9 100644 (file)
 #include <linux/serial_core.h>
 #include <linux/io.h>
 #include <linux/gpio.h>
+#include <linux/pm_domain.h>
 
 #include <mach/map.h>
 #include <mach/irqs.h>
 
+#include <plat/devs.h>
 #include <plat/pm.h>
 #include <plat/wakeup-mask.h>
 
 #include <mach/regs-gpio-memport.h>
 #include <mach/regs-modem.h>
 
+struct s3c64xx_pm_domain {
+       char *const name;
+       u32 ena;
+       u32 pwr_stat;
+       struct generic_pm_domain pd;
+};
+
+static int s3c64xx_pd_off(struct generic_pm_domain *domain)
+{
+       struct s3c64xx_pm_domain *pd;
+       u32 val;
+
+       pd = container_of(domain, struct s3c64xx_pm_domain, pd);
+
+       val = __raw_readl(S3C64XX_NORMAL_CFG);
+       val &= ~(pd->ena);
+       __raw_writel(val, S3C64XX_NORMAL_CFG);
+
+       return 0;
+}
+
+static int s3c64xx_pd_on(struct generic_pm_domain *domain)
+{
+       struct s3c64xx_pm_domain *pd;
+       u32 val;
+       long retry = 1000000L;
+
+       pd = container_of(domain, struct s3c64xx_pm_domain, pd);
+
+       val = __raw_readl(S3C64XX_NORMAL_CFG);
+       val |= pd->ena;
+       __raw_writel(val, S3C64XX_NORMAL_CFG);
+
+       /* Not all domains provide power status readback */
+       if (pd->pwr_stat) {
+               do {
+                       cpu_relax();
+                       if (__raw_readl(S3C64XX_BLK_PWR_STAT) & pd->pwr_stat)
+                               break;
+               } while (retry--);
+
+               if (!retry) {
+                       pr_err("Failed to start domain %s\n", pd->name);
+                       return -EBUSY;
+               }
+       }
+
+       return 0;
+}
+
+static struct s3c64xx_pm_domain s3c64xx_pm_irom = {
+       .name = "IROM",
+       .ena = S3C64XX_NORMALCFG_IROM_ON,
+       .pd = {
+               .power_off = s3c64xx_pd_off,
+               .power_on = s3c64xx_pd_on,
+       },
+};
+
+static struct s3c64xx_pm_domain s3c64xx_pm_etm = {
+       .name = "ETM",
+       .ena = S3C64XX_NORMALCFG_DOMAIN_ETM_ON,
+       .pwr_stat = S3C64XX_BLKPWRSTAT_ETM,
+       .pd = {
+               .power_off = s3c64xx_pd_off,
+               .power_on = s3c64xx_pd_on,
+       },
+};
+
+static struct s3c64xx_pm_domain s3c64xx_pm_s = {
+       .name = "S",
+       .ena = S3C64XX_NORMALCFG_DOMAIN_S_ON,
+       .pwr_stat = S3C64XX_BLKPWRSTAT_S,
+       .pd = {
+               .power_off = s3c64xx_pd_off,
+               .power_on = s3c64xx_pd_on,
+       },
+};
+
+static struct s3c64xx_pm_domain s3c64xx_pm_f = {
+       .name = "F",
+       .ena = S3C64XX_NORMALCFG_DOMAIN_F_ON,
+       .pwr_stat = S3C64XX_BLKPWRSTAT_F,
+       .pd = {
+               .power_off = s3c64xx_pd_off,
+               .power_on = s3c64xx_pd_on,
+       },
+};
+
+static struct s3c64xx_pm_domain s3c64xx_pm_p = {
+       .name = "P",
+       .ena = S3C64XX_NORMALCFG_DOMAIN_P_ON,
+       .pwr_stat = S3C64XX_BLKPWRSTAT_P,
+       .pd = {
+               .power_off = s3c64xx_pd_off,
+               .power_on = s3c64xx_pd_on,
+       },
+};
+
+static struct s3c64xx_pm_domain s3c64xx_pm_i = {
+       .name = "I",
+       .ena = S3C64XX_NORMALCFG_DOMAIN_I_ON,
+       .pwr_stat = S3C64XX_BLKPWRSTAT_I,
+       .pd = {
+               .power_off = s3c64xx_pd_off,
+               .power_on = s3c64xx_pd_on,
+       },
+};
+
+static struct s3c64xx_pm_domain s3c64xx_pm_g = {
+       .name = "G",
+       .ena = S3C64XX_NORMALCFG_DOMAIN_G_ON,
+       .pd = {
+               .power_off = s3c64xx_pd_off,
+               .power_on = s3c64xx_pd_on,
+       },
+};
+
+static struct s3c64xx_pm_domain s3c64xx_pm_v = {
+       .name = "V",
+       .ena = S3C64XX_NORMALCFG_DOMAIN_V_ON,
+       .pwr_stat = S3C64XX_BLKPWRSTAT_V,
+       .pd = {
+               .power_off = s3c64xx_pd_off,
+               .power_on = s3c64xx_pd_on,
+       },
+};
+
+static struct s3c64xx_pm_domain *s3c64xx_always_on_pm_domains[] = {
+       &s3c64xx_pm_irom,
+};
+
+static struct s3c64xx_pm_domain *s3c64xx_pm_domains[] = {
+       &s3c64xx_pm_etm,
+       &s3c64xx_pm_g,
+       &s3c64xx_pm_v,
+       &s3c64xx_pm_i,
+       &s3c64xx_pm_p,
+       &s3c64xx_pm_s,
+       &s3c64xx_pm_f,
+};
+
 #ifdef CONFIG_S3C_PM_DEBUG_LED_SMDK
 void s3c_pm_debug_smdkled(u32 set, u32 clear)
 {
@@ -181,7 +325,26 @@ static void s3c64xx_pm_prepare(void)
        __raw_writel(__raw_readl(S3C64XX_WAKEUP_STAT), S3C64XX_WAKEUP_STAT);
 }
 
-static int s3c64xx_pm_init(void)
+int __init s3c64xx_pm_init(void)
+{
+       int i;
+
+       s3c_pm_init();
+
+       for (i = 0; i < ARRAY_SIZE(s3c64xx_always_on_pm_domains); i++)
+               pm_genpd_init(&s3c64xx_always_on_pm_domains[i]->pd,
+                             &pm_domain_always_on_gov, false);
+
+       for (i = 0; i < ARRAY_SIZE(s3c64xx_pm_domains); i++)
+               pm_genpd_init(&s3c64xx_pm_domains[i]->pd, NULL, false);
+
+       if (dev_get_platdata(&s3c_device_fb.dev))
+               pm_genpd_add_device(&s3c64xx_pm_f.pd, &s3c_device_fb.dev);
+
+       return 0;
+}
+
+static __init int s3c64xx_pm_initcall(void)
 {
        u32 val;
 
@@ -213,5 +376,12 @@ static int s3c64xx_pm_init(void)
 
        return 0;
 }
+arch_initcall(s3c64xx_pm_initcall);
 
-arch_initcall(s3c64xx_pm_init);
+static __init int s3c64xx_pm_late_initcall(void)
+{
+       pm_genpd_poweroff_unused();
+
+       return 0;
+}
+late_initcall(s3c64xx_pm_late_initcall);