Skip to content

Commit

Permalink
Merge branch 'pm-domains' into for-linus
Browse files Browse the repository at this point in the history
* pm-domains: (33 commits)
  ARM / shmobile: Return -EBUSY from A4LC power off if A3RV is active
  PM / Domains: Take .power_off() error code into account
  ARM / shmobile: Use genpd_queue_power_off_work()
  ARM / shmobile: Use pm_genpd_poweroff_unused()
  PM / Domains: Introduce function to power off all unused PM domains
  PM / Domains: Queue up power off work only if it is not pending
  PM / Domains: Improve handling of wakeup devices during system suspend
  PM / Domains: Do not restore all devices on power off error
  PM / Domains: Allow callbacks to execute all runtime PM helpers
  PM / Domains: Do not execute device callbacks under locks
  PM / Domains: Make failing pm_genpd_prepare() clean up properly
  PM / Domains: Set device state to "active" during system resume
  ARM: mach-shmobile: sh7372 A3RV requires A4LC
  PM / Domains: Export pm_genpd_poweron() in header
  ARM: mach-shmobile: sh7372 late pm domain off
  ARM: mach-shmobile: Runtime PM late init callback
  ARM: mach-shmobile: sh7372 D4 support
  ARM: mach-shmobile: sh7372 A4MP support
  ARM: mach-shmobile: sh7372: make sure that fsi is peripheral of spu2
  ARM: mach-shmobile: sh7372 A3SG support
  ...
  • Loading branch information
rjwysocki committed Jul 15, 2011
2 parents f0c077a + 5ca8081 commit ba1389d
Show file tree
Hide file tree
Showing 24 changed files with 1,937 additions and 171 deletions.
8 changes: 4 additions & 4 deletions Documentation/power/devices.txt
Original file line number Diff line number Diff line change
Expand Up @@ -506,8 +506,8 @@ routines. Nevertheless, different callback pointers are used in case there is a
situation where it actually matters.


Device Power Domains
--------------------
Device Power Management Domains
-------------------------------
Sometimes devices share reference clocks or other power resources. In those
cases it generally is not possible to put devices into low-power states
individually. Instead, a set of devices sharing a power resource can be put
Expand All @@ -516,8 +516,8 @@ power resource. Of course, they also need to be put into the full-power state
together, by turning the shared power resource on. A set of devices with this
property is often referred to as a power domain.

Support for power domains is provided through the pwr_domain field of struct
device. This field is a pointer to an object of type struct dev_power_domain,
Support for power domains is provided through the pm_domain field of struct
device. This field is a pointer to an object of type struct dev_pm_domain,
defined in include/linux/pm.h, providing a set of power management callbacks
analogous to the subsystem-level and device driver callbacks that are executed
for the given device during all power transitions, instead of the respective
Expand Down
32 changes: 30 additions & 2 deletions Documentation/power/runtime_pm.txt
Original file line number Diff line number Diff line change
Expand Up @@ -606,32 +606,60 @@ driver/base/power/generic_ops.c:
callback provided by its driver and return its result, or return 0 if not
defined

int pm_generic_suspend_noirq(struct device *dev);
- if pm_runtime_suspended(dev) returns "false", invoke the ->suspend_noirq()
callback provided by the device's driver and return its result, or return
0 if not defined

int pm_generic_resume(struct device *dev);
- invoke the ->resume() callback provided by the driver of this device and,
if successful, change the device's runtime PM status to 'active'

int pm_generic_resume_noirq(struct device *dev);
- invoke the ->resume_noirq() callback provided by the driver of this device

int pm_generic_freeze(struct device *dev);
- if the device has not been suspended at run time, invoke the ->freeze()
callback provided by its driver and return its result, or return 0 if not
defined

int pm_generic_freeze_noirq(struct device *dev);
- if pm_runtime_suspended(dev) returns "false", invoke the ->freeze_noirq()
callback provided by the device's driver and return its result, or return
0 if not defined

int pm_generic_thaw(struct device *dev);
- if the device has not been suspended at run time, invoke the ->thaw()
callback provided by its driver and return its result, or return 0 if not
defined

int pm_generic_thaw_noirq(struct device *dev);
- if pm_runtime_suspended(dev) returns "false", invoke the ->thaw_noirq()
callback provided by the device's driver and return its result, or return
0 if not defined

int pm_generic_poweroff(struct device *dev);
- if the device has not been suspended at run time, invoke the ->poweroff()
callback provided by its driver and return its result, or return 0 if not
defined

int pm_generic_poweroff_noirq(struct device *dev);
- if pm_runtime_suspended(dev) returns "false", run the ->poweroff_noirq()
callback provided by the device's driver and return its result, or return
0 if not defined

int pm_generic_restore(struct device *dev);
- invoke the ->restore() callback provided by the driver of this device and,
if successful, change the device's runtime PM status to 'active'

int pm_generic_restore_noirq(struct device *dev);
- invoke the ->restore_noirq() callback provided by the device's driver

These functions can be assigned to the ->runtime_idle(), ->runtime_suspend(),
->runtime_resume(), ->suspend(), ->resume(), ->freeze(), ->thaw(), ->poweroff(),
or ->restore() callback pointers in the subsystem-level dev_pm_ops structures.
->runtime_resume(), ->suspend(), ->suspend_noirq(), ->resume(),
->resume_noirq(), ->freeze(), ->freeze_noirq(), ->thaw(), ->thaw_noirq(),
->poweroff(), ->poweroff_noirq(), ->restore(), ->restore_noirq() callback
pointers in the subsystem-level dev_pm_ops structures.

If a subsystem wishes to use all of them at the same time, it can simply assign
the GENERIC_SUBSYS_PM_OPS macro, defined in include/linux/pm.h, to its
Expand Down
1 change: 1 addition & 0 deletions arch/arm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,7 @@ config ARCH_SHMOBILE
select NO_IOPORT
select SPARSE_IRQ
select MULTI_IRQ_HANDLER
select PM_GENERIC_DOMAINS if PM
help
Support for Renesas's SH-Mobile and R-Mobile ARM platforms.

Expand Down
14 changes: 7 additions & 7 deletions arch/arm/mach-omap1/pm_bus.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ static int omap1_pm_runtime_suspend(struct device *dev)
if (ret)
return ret;

ret = pm_runtime_clk_suspend(dev);
ret = pm_clk_suspend(dev);
if (ret) {
pm_generic_runtime_resume(dev);
return ret;
Expand All @@ -45,24 +45,24 @@ static int omap1_pm_runtime_resume(struct device *dev)
{
dev_dbg(dev, "%s\n", __func__);

pm_runtime_clk_resume(dev);
pm_clk_resume(dev);
return pm_generic_runtime_resume(dev);
}

static struct dev_power_domain default_power_domain = {
static struct dev_pm_domain default_pm_domain = {
.ops = {
.runtime_suspend = omap1_pm_runtime_suspend,
.runtime_resume = omap1_pm_runtime_resume,
USE_PLATFORM_PM_SLEEP_OPS
},
};
#define OMAP1_PWR_DOMAIN (&default_power_domain)
#define OMAP1_PM_DOMAIN (&default_pm_domain)
#else
#define OMAP1_PWR_DOMAIN NULL
#define OMAP1_PM_DOMAIN NULL
#endif /* CONFIG_PM_RUNTIME */

static struct pm_clk_notifier_block platform_bus_notifier = {
.pwr_domain = OMAP1_PWR_DOMAIN,
.pm_domain = OMAP1_PM_DOMAIN,
.con_ids = { "ick", "fck", NULL, },
};

Expand All @@ -71,7 +71,7 @@ static int __init omap1_pm_runtime_init(void)
if (!cpu_class_is_omap1())
return -ENODEV;

pm_runtime_clk_add_notifier(&platform_bus_type, &platform_bus_notifier);
pm_clk_add_notifier(&platform_bus_type, &platform_bus_notifier);

return 0;
}
Expand Down
5 changes: 5 additions & 0 deletions arch/arm/mach-shmobile/board-ap4evb.c
Original file line number Diff line number Diff line change
Expand Up @@ -1408,9 +1408,14 @@ static void __init ap4evb_init(void)

platform_add_devices(ap4evb_devices, ARRAY_SIZE(ap4evb_devices));

sh7372_add_device_to_domain(&sh7372_a4lc, &lcdc1_device);
sh7372_add_device_to_domain(&sh7372_a4lc, &lcdc_device);
sh7372_add_device_to_domain(&sh7372_a4mp, &fsi_device);

hdmi_init_pm_clock();
fsi_init_pm_clock();
sh7372_pm_init();
pm_clk_add(&fsi_device.dev, "spu2");
}

static void __init ap4evb_timer_init(void)
Expand Down
5 changes: 5 additions & 0 deletions arch/arm/mach-shmobile/board-mackerel.c
Original file line number Diff line number Diff line change
Expand Up @@ -1582,8 +1582,13 @@ static void __init mackerel_init(void)

platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));

sh7372_add_device_to_domain(&sh7372_a4lc, &lcdc_device);
sh7372_add_device_to_domain(&sh7372_a4lc, &hdmi_lcdc_device);
sh7372_add_device_to_domain(&sh7372_a4mp, &fsi_device);

hdmi_init_pm_clock();
sh7372_pm_init();
pm_clk_add(&fsi_device.dev, "spu2");
}

static void __init mackerel_timer_init(void)
Expand Down
1 change: 1 addition & 0 deletions arch/arm/mach-shmobile/clock-sh7372.c
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,7 @@ static struct clk_lookup lookups[] = {
CLKDEV_ICK_ID("ick", "sh-mobile-hdmi", &div6_reparent_clks[DIV6_HDMI]),
CLKDEV_ICK_ID("icka", "sh_fsi2", &div6_reparent_clks[DIV6_FSIA]),
CLKDEV_ICK_ID("ickb", "sh_fsi2", &div6_reparent_clks[DIV6_FSIB]),
CLKDEV_ICK_ID("spu2", "sh_fsi2", &mstp_clks[MSTP223]),
};

void __init sh7372_clock_init(void)
Expand Down
29 changes: 29 additions & 0 deletions arch/arm/mach-shmobile/include/mach/sh7372.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#define __ASM_SH7372_H__

#include <linux/sh_clk.h>
#include <linux/pm_domain.h>

/*
* Pin Function Controller:
Expand Down Expand Up @@ -470,4 +471,32 @@ extern struct clk sh7372_fsibck_clk;
extern struct clk sh7372_fsidiva_clk;
extern struct clk sh7372_fsidivb_clk;

struct platform_device;

struct sh7372_pm_domain {
struct generic_pm_domain genpd;
unsigned int bit_shift;
};

static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d)
{
return container_of(d, struct sh7372_pm_domain, genpd);
}

#ifdef CONFIG_PM
extern struct sh7372_pm_domain sh7372_a4lc;
extern struct sh7372_pm_domain sh7372_a4mp;
extern struct sh7372_pm_domain sh7372_d4;
extern struct sh7372_pm_domain sh7372_a3rv;
extern struct sh7372_pm_domain sh7372_a3ri;
extern struct sh7372_pm_domain sh7372_a3sg;

extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
struct platform_device *pdev);
#else
#define sh7372_init_pm_domain(pd) do { } while(0)
#define sh7372_add_device_to_domain(pd, pdev) do { } while(0)
#endif /* CONFIG_PM */

#endif /* __ASM_SH7372_H__ */
160 changes: 160 additions & 0 deletions arch/arm/mach-shmobile/pm-sh7372.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,176 @@
#include <linux/list.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/tlbflush.h>
#include <mach/common.h>
#include <mach/sh7372.h>

#define SMFRAM 0xe6a70000
#define SYSTBCR 0xe6150024
#define SBAR 0xe6180020
#define APARMBAREA 0xe6f10020

#define SPDCR 0xe6180008
#define SWUCR 0xe6180014
#define PSTR 0xe6180080

#define PSTR_RETRIES 100
#define PSTR_DELAY_US 10

#ifdef CONFIG_PM

static int pd_power_down(struct generic_pm_domain *genpd)
{
struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
unsigned int mask = 1 << sh7372_pd->bit_shift;

if (__raw_readl(PSTR) & mask) {
unsigned int retry_count;

__raw_writel(mask, SPDCR);

for (retry_count = PSTR_RETRIES; retry_count; retry_count--) {
if (!(__raw_readl(SPDCR) & mask))
break;
cpu_relax();
}
}

pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
mask, __raw_readl(PSTR));

return 0;
}

static int pd_power_up(struct generic_pm_domain *genpd)
{
struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
unsigned int mask = 1 << sh7372_pd->bit_shift;
unsigned int retry_count;
int ret = 0;

if (__raw_readl(PSTR) & mask)
goto out;

__raw_writel(mask, SWUCR);

for (retry_count = 2 * PSTR_RETRIES; retry_count; retry_count--) {
if (!(__raw_readl(SWUCR) & mask))
goto out;
if (retry_count > PSTR_RETRIES)
udelay(PSTR_DELAY_US);
else
cpu_relax();
}
if (__raw_readl(SWUCR) & mask)
ret = -EIO;

out:
pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n",
mask, __raw_readl(PSTR));

return ret;
}

static int pd_power_up_a3rv(struct generic_pm_domain *genpd)
{
int ret = pd_power_up(genpd);

/* force A4LC on after A3RV has been requested on */
pm_genpd_poweron(&sh7372_a4lc.genpd);

return ret;
}

static int pd_power_down_a3rv(struct generic_pm_domain *genpd)
{
int ret = pd_power_down(genpd);

/* try to power down A4LC after A3RV is requested off */
genpd_queue_power_off_work(&sh7372_a4lc.genpd);

return ret;
}

static int pd_power_down_a4lc(struct generic_pm_domain *genpd)
{
/* only power down A4LC if A3RV is off */
if (!(__raw_readl(PSTR) & (1 << sh7372_a3rv.bit_shift)))
return pd_power_down(genpd);

return -EBUSY;
}

static bool pd_active_wakeup(struct device *dev)
{
return true;
}

void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd)
{
struct generic_pm_domain *genpd = &sh7372_pd->genpd;

pm_genpd_init(genpd, NULL, false);
genpd->stop_device = pm_clk_suspend;
genpd->start_device = pm_clk_resume;
genpd->active_wakeup = pd_active_wakeup;

if (sh7372_pd == &sh7372_a4lc) {
genpd->power_off = pd_power_down_a4lc;
genpd->power_on = pd_power_up;
} else if (sh7372_pd == &sh7372_a3rv) {
genpd->power_off = pd_power_down_a3rv;
genpd->power_on = pd_power_up_a3rv;
} else {
genpd->power_off = pd_power_down;
genpd->power_on = pd_power_up;
}
genpd->power_on(&sh7372_pd->genpd);
}

void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
struct platform_device *pdev)
{
struct device *dev = &pdev->dev;

if (!dev->power.subsys_data) {
pm_clk_init(dev);
pm_clk_add(dev, NULL);
}
pm_genpd_add_device(&sh7372_pd->genpd, dev);
}

struct sh7372_pm_domain sh7372_a4lc = {
.bit_shift = 1,
};

struct sh7372_pm_domain sh7372_a4mp = {
.bit_shift = 2,
};

struct sh7372_pm_domain sh7372_d4 = {
.bit_shift = 3,
};

struct sh7372_pm_domain sh7372_a3rv = {
.bit_shift = 6,
};

struct sh7372_pm_domain sh7372_a3ri = {
.bit_shift = 8,
};

struct sh7372_pm_domain sh7372_a3sg = {
.bit_shift = 13,
};

#endif /* CONFIG_PM */

static void sh7372_enter_core_standby(void)
{
void __iomem *smfram = (void __iomem *)SMFRAM;
Expand Down
Loading

0 comments on commit ba1389d

Please sign in to comment.