Skip to content

Commit

Permalink
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel…
Browse files Browse the repository at this point in the history
…/git/rafael/suspend-2.6

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/suspend-2.6: (51 commits)
  PM: Improve error code of pm_notifier_call_chain()
  PM: Add "RTC" to PM trace time stamps to avoid confusion
  PM / Suspend: Export suspend_set_ops, suspend_valid_only_mem
  PM / Suspend: Add .suspend_again() callback to suspend_ops
  PM / OPP: Introduce function to free cpufreq table
  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
  OMAP: PM: disable idle on suspend for GPIO and UART
  OMAP: PM: omap_device: add API to disable idle on suspend
  OMAP: PM: omap_device: add system PM methods for PM domain handling
  OMAP: PM: omap_device: conditionally use PM domain runtime helpers
  PM / Runtime: Add new helper function: pm_runtime_status_suspended()
  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
  ...
  • Loading branch information
Linus Torvalds committed Jul 22, 2011
2 parents 72f96e0 + 7ae033c commit 431bf99
Show file tree
Hide file tree
Showing 40 changed files with 2,274 additions and 324 deletions.
14 changes: 7 additions & 7 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 Expand Up @@ -604,7 +604,7 @@ state temporarily, for example so that its system wakeup capability can be
disabled. This all depends on the hardware and the design of the subsystem and
device driver in question.

During system-wide resume from a sleep state it's best to put devices into the
full-power state, as explained in Documentation/power/runtime_pm.txt. Refer to
that document for more information regarding this particular issue as well as
During system-wide resume from a sleep state it's easiest to put devices into
the full-power state, as explained in Documentation/power/runtime_pm.txt. Refer
to that document for more information regarding this particular issue as well as
for information on the device runtime power management framework in general.
2 changes: 2 additions & 0 deletions Documentation/power/opp.txt
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,8 @@ opp_init_cpufreq_table - cpufreq framework typically is initialized with
addition to CONFIG_PM as power management feature is required to
dynamically scale voltage and frequency in a system.

opp_free_cpufreq_table - Free up the table allocated by opp_init_cpufreq_table

7. Data Structures
==================
Typically an SoC contains multiple voltage domains which are variable. Each
Expand Down
229 changes: 152 additions & 77 deletions Documentation/power/runtime_pm.txt

Large diffs are not rendered by default.

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
2 changes: 2 additions & 0 deletions arch/arm/mach-omap2/gpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ static int omap2_gpio_dev_init(struct omap_hwmod *oh, void *unused)
return PTR_ERR(od);
}

omap_device_disable_idle_on_suspend(od);

gpio_bank_count++;
return 0;
}
Expand Down
1 change: 1 addition & 0 deletions arch/arm/mach-omap2/serial.c
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,7 @@ void __init omap_serial_init_port(struct omap_board_data *bdata)
WARN(IS_ERR(od), "Could not build omap_device for %s: %s.\n",
name, oh->name);

omap_device_disable_idle_on_suspend(od);
oh->mux = omap_hwmod_mux_init(bdata->pads, bdata->pads_cnt);

uart->irq = oh->mpu_irqs[0].irq;
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 431bf99

Please sign in to comment.