Skip to content

Commit

Permalink
ARM: S3C64XX: Implement basic power domain support
Browse files Browse the repository at this point in the history
The S3C64xx SoCs contain a set of gateable power domains which can be
enabled and disabled at runtime in order to save power.  Use the generic
power domain code to implement support for these in software, enabling
runtime control of most domains:

 - ETM (not supported in mainline).
 - Domain G: 3D acceleration (no mainline support).
 - Domain V: MFC (no mainline support).
 - Domain I: JPEG and camera interface (no mainline support).
 - Domain P: 2D acceleration, TV encoder and scaler (no mainline support)
 - Domain S: Security (no mainline support).
 - Domain F: LCD (driver already uses runtime PM), post processing and
   rotation (no mainline support).

The IROM domain is marked as always enabled as we should arrange for it
to be enabled when we suspend which will need a bit more work.

Due to all the conditional device registration that the platform does
wrap s3c_pm_init() with s3c64xx_pm_init() which actually puts the device
into the power domain after the machines have registered, looking for
platform data to tell if the device was registered. Since currently only
Cragganmore actually sets up PM that is the only machine updated.

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Acked-by: Kukjin Kim <kgene.kim@samsung.com>
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
  • Loading branch information
Mark Brown authored and Rafael J. Wysocki committed Dec 9, 2011
1 parent a87dc8f commit c656c30
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 3 deletions.
1 change: 1 addition & 0 deletions arch/arm/mach-s3c64xx/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ config PLAT_S3C64XX
bool
depends on ARCH_S3C64XX
select SAMSUNG_WAKEMASK
select PM_GENERIC_DOMAINS
default y
help
Base platform code for any Samsung S3C64XX device
Expand Down
2 changes: 1 addition & 1 deletion arch/arm/mach-s3c64xx/mach-crag6410.c
Original file line number Diff line number Diff line change
Expand Up @@ -704,7 +704,7 @@ static void __init crag6410_machine_init(void)

regulator_has_full_constraints();

s3c_pm_init();
s3c64xx_pm_init();
}

MACHINE_START(WLF_CRAGG_6410, "Wolfson Cragganmore 6410")
Expand Down
176 changes: 174 additions & 2 deletions arch/arm/mach-s3c64xx/pm.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
#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>

Expand All @@ -31,6 +33,148 @@
#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)
{
Expand Down Expand Up @@ -89,6 +233,8 @@ static struct sleep_save misc_save[] = {

SAVE_ITEM(S3C64XX_SDMA_SEL),
SAVE_ITEM(S3C64XX_MODEM_MIFPCON),

SAVE_ITEM(S3C64XX_NORMAL_CFG),
};

void s3c_pm_configure_extint(void)
Expand Down Expand Up @@ -179,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)
{
pm_cpu_prep = s3c64xx_pm_prepare;
pm_cpu_sleep = s3c64xx_cpu_suspend;
Expand All @@ -198,5 +363,12 @@ static int s3c64xx_pm_init(void)

return 0;
}
arch_initcall(s3c64xx_pm_initcall);

static __init int s3c64xx_pm_late_initcall(void)
{
pm_genpd_poweroff_unused();

arch_initcall(s3c64xx_pm_init);
return 0;
}
late_initcall(s3c64xx_pm_late_initcall);
6 changes: 6 additions & 0 deletions arch/arm/plat-samsung/include/plat/pm.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,19 @@ struct sys_device;
#ifdef CONFIG_PM

extern __init int s3c_pm_init(void);
extern __init int s3c64xx_pm_init(void);

#else

static inline int s3c_pm_init(void)
{
return 0;
}

static inline int s3c64xx_pm_init(void)
{
return 0;
}
#endif

/* configuration for the IRQ mask over sleep */
Expand Down

0 comments on commit c656c30

Please sign in to comment.