diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-10-25 15:18:39 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-10-25 15:18:39 +0200 |
commit | 7e0bb71e75020348bee523720a0c2f04cc72f540 (patch) | |
tree | 1a22d65bbce34e8cc0f82c543c9486ffb58332f7 /arch | |
parent | b9e2780d576a010d4aba1e69f247170bf3718d6b (diff) | |
parent | 0ab1e79b825a5cd8aeb3b34d89c9a89dea900056 (diff) |
Merge branch 'pm-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
* 'pm-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (63 commits)
PM / Clocks: Remove redundant NULL checks before kfree()
PM / Documentation: Update docs about suspend and CPU hotplug
ACPI / PM: Add Sony VGN-FW21E to nonvs blacklist.
ARM: mach-shmobile: sh7372 A4R support (v4)
ARM: mach-shmobile: sh7372 A3SP support (v4)
PM / Sleep: Mark devices involved in wakeup signaling during suspend
PM / Hibernate: Improve performance of LZO/plain hibernation, checksum image
PM / Hibernate: Do not initialize static and extern variables to 0
PM / Freezer: Make fake_signal_wake_up() wake TASK_KILLABLE tasks too
PM / Hibernate: Add resumedelay kernel param in addition to resumewait
MAINTAINERS: Update linux-pm list address
PM / ACPI: Blacklist Vaio VGN-FW520F machine known to require acpi_sleep=nonvs
PM / ACPI: Blacklist Sony Vaio known to require acpi_sleep=nonvs
PM / Hibernate: Add resumewait param to support MMC-like devices as resume file
PM / Hibernate: Fix typo in a kerneldoc comment
PM / Hibernate: Freeze kernel threads after preallocating memory
PM: Update the policy on default wakeup settings
PM / VT: Cleanup #if defined uglyness and fix compile error
PM / Suspend: Off by one in pm_suspend()
PM / Hibernate: Include storage keys in hibernation image on s390
...
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-msm/clock.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-omap1/pm_bus.c | 1 | ||||
-rw-r--r-- | arch/arm/mach-shmobile/board-ap4evb.c | 6 | ||||
-rw-r--r-- | arch/arm/mach-shmobile/board-mackerel.c | 11 | ||||
-rw-r--r-- | arch/arm/mach-shmobile/include/mach/common.h | 4 | ||||
-rw-r--r-- | arch/arm/mach-shmobile/include/mach/sh7372.h | 13 | ||||
-rw-r--r-- | arch/arm/mach-shmobile/intc-sh7372.c | 52 | ||||
-rw-r--r-- | arch/arm/mach-shmobile/pm-sh7372.c | 342 | ||||
-rw-r--r-- | arch/arm/mach-shmobile/pm_runtime.c | 1 | ||||
-rw-r--r-- | arch/arm/mach-shmobile/setup-sh7372.c | 25 | ||||
-rw-r--r-- | arch/arm/mach-shmobile/sleep-sh7372.S | 221 | ||||
-rw-r--r-- | arch/s390/Kconfig | 1 | ||||
-rw-r--r-- | arch/s390/kernel/suspend.c | 118 | ||||
-rw-r--r-- | arch/s390/kernel/swsusp_asm64.S | 3 |
14 files changed, 534 insertions, 266 deletions
diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c index 22a53766962..d9145dfc2a3 100644 --- a/arch/arm/mach-msm/clock.c +++ b/arch/arm/mach-msm/clock.c @@ -18,7 +18,7 @@ #include <linux/list.h> #include <linux/err.h> #include <linux/spinlock.h> -#include <linux/pm_qos_params.h> +#include <linux/pm_qos.h> #include <linux/mutex.h> #include <linux/clk.h> #include <linux/string.h> diff --git a/arch/arm/mach-omap1/pm_bus.c b/arch/arm/mach-omap1/pm_bus.c index 943072d5a1d..7868e75ad07 100644 --- a/arch/arm/mach-omap1/pm_bus.c +++ b/arch/arm/mach-omap1/pm_bus.c @@ -13,6 +13,7 @@ #include <linux/kernel.h> #include <linux/io.h> #include <linux/pm_runtime.h> +#include <linux/pm_clock.h> #include <linux/platform_device.h> #include <linux/mutex.h> #include <linux/clk.h> diff --git a/arch/arm/mach-shmobile/board-ap4evb.c b/arch/arm/mach-shmobile/board-ap4evb.c index 523f608eb8c..7e90d064ebc 100644 --- a/arch/arm/mach-shmobile/board-ap4evb.c +++ b/arch/arm/mach-shmobile/board-ap4evb.c @@ -42,6 +42,7 @@ #include <linux/leds.h> #include <linux/input/sh_keysc.h> #include <linux/usb/r8a66597.h> +#include <linux/pm_clock.h> #include <media/sh_mobile_ceu.h> #include <media/sh_mobile_csi2.h> @@ -1408,6 +1409,11 @@ static void __init ap4evb_init(void) sh7372_add_device_to_domain(&sh7372_a4lc, &lcdc_device); sh7372_add_device_to_domain(&sh7372_a4mp, &fsi_device); + sh7372_add_device_to_domain(&sh7372_a3sp, &sh_mmcif_device); + sh7372_add_device_to_domain(&sh7372_a3sp, &sdhi0_device); + sh7372_add_device_to_domain(&sh7372_a3sp, &sdhi1_device); + sh7372_add_device_to_domain(&sh7372_a4r, &ceu_device); + hdmi_init_pm_clock(); fsi_init_pm_clock(); sh7372_pm_init(); diff --git a/arch/arm/mach-shmobile/board-mackerel.c b/arch/arm/mach-shmobile/board-mackerel.c index 802aa58ef9d..00273dad5bf 100644 --- a/arch/arm/mach-shmobile/board-mackerel.c +++ b/arch/arm/mach-shmobile/board-mackerel.c @@ -39,7 +39,7 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> #include <linux/mtd/physmap.h> -#include <linux/pm_runtime.h> +#include <linux/pm_clock.h> #include <linux/smsc911x.h> #include <linux/sh_intc.h> #include <linux/tca6416_keypad.h> @@ -1589,6 +1589,15 @@ static void __init mackerel_init(void) 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); + sh7372_add_device_to_domain(&sh7372_a3sp, &usbhs0_device); + sh7372_add_device_to_domain(&sh7372_a3sp, &usbhs1_device); + sh7372_add_device_to_domain(&sh7372_a3sp, &sh_mmcif_device); + sh7372_add_device_to_domain(&sh7372_a3sp, &sdhi0_device); +#if !defined(CONFIG_MMC_SH_MMCIF) && !defined(CONFIG_MMC_SH_MMCIF_MODULE) + sh7372_add_device_to_domain(&sh7372_a3sp, &sdhi1_device); +#endif + sh7372_add_device_to_domain(&sh7372_a3sp, &sdhi2_device); + sh7372_add_device_to_domain(&sh7372_a4r, &ceu_device); hdmi_init_pm_clock(); sh7372_pm_init(); diff --git a/arch/arm/mach-shmobile/include/mach/common.h b/arch/arm/mach-shmobile/include/mach/common.h index 06aecb31d9c..c0cdbf997c9 100644 --- a/arch/arm/mach-shmobile/include/mach/common.h +++ b/arch/arm/mach-shmobile/include/mach/common.h @@ -35,8 +35,8 @@ extern void sh7372_add_standard_devices(void); extern void sh7372_clock_init(void); extern void sh7372_pinmux_init(void); extern void sh7372_pm_init(void); -extern void sh7372_cpu_suspend(void); -extern void sh7372_cpu_resume(void); +extern void sh7372_resume_core_standby_a3sm(void); +extern int sh7372_do_idle_a3sm(unsigned long unused); extern struct clk sh7372_extal1_clk; extern struct clk sh7372_extal2_clk; diff --git a/arch/arm/mach-shmobile/include/mach/sh7372.h b/arch/arm/mach-shmobile/include/mach/sh7372.h index 24e63a85e66..84532f9629b 100644 --- a/arch/arm/mach-shmobile/include/mach/sh7372.h +++ b/arch/arm/mach-shmobile/include/mach/sh7372.h @@ -479,7 +479,12 @@ struct platform_device; struct sh7372_pm_domain { struct generic_pm_domain genpd; + struct dev_power_governor *gov; + void (*suspend)(void); + void (*resume)(void); unsigned int bit_shift; + bool no_debug; + bool stay_on; }; static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d) @@ -491,16 +496,24 @@ static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d) 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_a4r; extern struct sh7372_pm_domain sh7372_a3rv; extern struct sh7372_pm_domain sh7372_a3ri; +extern struct sh7372_pm_domain sh7372_a3sp; 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); +extern void sh7372_pm_add_subdomain(struct sh7372_pm_domain *sh7372_pd, + struct sh7372_pm_domain *sh7372_sd); #else #define sh7372_init_pm_domain(pd) do { } while(0) #define sh7372_add_device_to_domain(pd, pdev) do { } while(0) +#define sh7372_pm_add_subdomain(pd, sd) do { } while(0) #endif /* CONFIG_PM */ +extern void sh7372_intcs_suspend(void); +extern void sh7372_intcs_resume(void); + #endif /* __ASM_SH7372_H__ */ diff --git a/arch/arm/mach-shmobile/intc-sh7372.c b/arch/arm/mach-shmobile/intc-sh7372.c index 739315e30eb..29cdc0522d9 100644 --- a/arch/arm/mach-shmobile/intc-sh7372.c +++ b/arch/arm/mach-shmobile/intc-sh7372.c @@ -606,9 +606,16 @@ static void intcs_demux(unsigned int irq, struct irq_desc *desc) generic_handle_irq(intcs_evt2irq(evtcodeas)); } +static void __iomem *intcs_ffd2; +static void __iomem *intcs_ffd5; + void __init sh7372_init_irq(void) { - void __iomem *intevtsa = ioremap_nocache(0xffd20100, PAGE_SIZE); + void __iomem *intevtsa; + + intcs_ffd2 = ioremap_nocache(0xffd20000, PAGE_SIZE); + intevtsa = intcs_ffd2 + 0x100; + intcs_ffd5 = ioremap_nocache(0xffd50000, PAGE_SIZE); register_intc_controller(&intca_desc); register_intc_controller(&intcs_desc); @@ -617,3 +624,46 @@ void __init sh7372_init_irq(void) irq_set_handler_data(evt2irq(0xf80), (void *)intevtsa); irq_set_chained_handler(evt2irq(0xf80), intcs_demux); } + +static unsigned short ffd2[0x200]; +static unsigned short ffd5[0x100]; + +void sh7372_intcs_suspend(void) +{ + int k; + + for (k = 0x00; k <= 0x30; k += 4) + ffd2[k] = __raw_readw(intcs_ffd2 + k); + + for (k = 0x80; k <= 0xb0; k += 4) + ffd2[k] = __raw_readb(intcs_ffd2 + k); + + for (k = 0x180; k <= 0x188; k += 4) + ffd2[k] = __raw_readb(intcs_ffd2 + k); + + for (k = 0x00; k <= 0x3c; k += 4) + ffd5[k] = __raw_readw(intcs_ffd5 + k); + + for (k = 0x80; k <= 0x9c; k += 4) + ffd5[k] = __raw_readb(intcs_ffd5 + k); +} + +void sh7372_intcs_resume(void) +{ + int k; + + for (k = 0x00; k <= 0x30; k += 4) + __raw_writew(ffd2[k], intcs_ffd2 + k); + + for (k = 0x80; k <= 0xb0; k += 4) + __raw_writeb(ffd2[k], intcs_ffd2 + k); + + for (k = 0x180; k <= 0x188; k += 4) + __raw_writeb(ffd2[k], intcs_ffd2 + k); + + for (k = 0x00; k <= 0x3c; k += 4) + __raw_writew(ffd5[k], intcs_ffd5 + k); + + for (k = 0x80; k <= 0x9c; k += 4) + __raw_writeb(ffd5[k], intcs_ffd5 + k); +} diff --git a/arch/arm/mach-shmobile/pm-sh7372.c b/arch/arm/mach-shmobile/pm-sh7372.c index 933fb411be0..79612737c5b 100644 --- a/arch/arm/mach-shmobile/pm-sh7372.c +++ b/arch/arm/mach-shmobile/pm-sh7372.c @@ -15,23 +15,61 @@ #include <linux/list.h> #include <linux/err.h> #include <linux/slab.h> -#include <linux/pm_runtime.h> +#include <linux/pm_clock.h> #include <linux/platform_device.h> #include <linux/delay.h> +#include <linux/irq.h> +#include <linux/bitrev.h> #include <asm/system.h> #include <asm/io.h> #include <asm/tlbflush.h> +#include <asm/suspend.h> #include <mach/common.h> #include <mach/sh7372.h> -#define SMFRAM 0xe6a70000 -#define SYSTBCR 0xe6150024 -#define SBAR 0xe6180020 -#define APARMBAREA 0xe6f10020 +/* DBG */ +#define DBGREG1 0xe6100020 +#define DBGREG9 0xe6100040 +/* CPGA */ +#define SYSTBCR 0xe6150024 +#define MSTPSR0 0xe6150030 +#define MSTPSR1 0xe6150038 +#define MSTPSR2 0xe6150040 +#define MSTPSR3 0xe6150048 +#define MSTPSR4 0xe615004c +#define PLLC01STPCR 0xe61500c8 + +/* SYSC */ #define SPDCR 0xe6180008 #define SWUCR 0xe6180014 +#define SBAR 0xe6180020 +#define WUPRMSK 0xe6180028 +#define WUPSMSK 0xe618002c +#define WUPSMSK2 0xe6180048 #define PSTR 0xe6180080 +#define WUPSFAC 0xe6180098 +#define IRQCR 0xe618022c +#define IRQCR2 0xe6180238 +#define IRQCR3 0xe6180244 +#define IRQCR4 0xe6180248 +#define PDNSEL 0xe6180254 + +/* INTC */ +#define ICR1A 0xe6900000 +#define ICR2A 0xe6900004 +#define ICR3A 0xe6900008 +#define ICR4A 0xe690000c +#define INTMSK00A 0xe6900040 +#define INTMSK10A 0xe6900044 +#define INTMSK20A 0xe6900048 +#define INTMSK30A 0xe690004c + +/* MFIS */ +#define SMFRAM 0xe6a70000 + +/* AP-System Core */ +#define APARMBAREA 0xe6f10020 #define PSTR_RETRIES 100 #define PSTR_DELAY_US 10 @@ -43,6 +81,12 @@ 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 (sh7372_pd->suspend) + sh7372_pd->suspend(); + + if (sh7372_pd->stay_on) + return 0; + if (__raw_readl(PSTR) & mask) { unsigned int retry_count; @@ -55,8 +99,9 @@ static int pd_power_down(struct generic_pm_domain *genpd) } } - pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n", - mask, __raw_readl(PSTR)); + if (!sh7372_pd->no_debug) + pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n", + mask, __raw_readl(PSTR)); return 0; } @@ -68,6 +113,9 @@ static int pd_power_up(struct generic_pm_domain *genpd) unsigned int retry_count; int ret = 0; + if (sh7372_pd->stay_on) + goto out; + if (__raw_readl(PSTR) & mask) goto out; @@ -84,66 +132,48 @@ static int pd_power_up(struct generic_pm_domain *genpd) if (__raw_readl(SWUCR) & mask) ret = -EIO; + if (!sh7372_pd->no_debug) + pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n", + mask, __raw_readl(PSTR)); + out: - pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n", - mask, __raw_readl(PSTR)); + if (ret == 0 && sh7372_pd->resume) + sh7372_pd->resume(); return ret; } -static int pd_power_up_a3rv(struct generic_pm_domain *genpd) +static void sh7372_a4r_suspend(void) { - int ret = pd_power_up(genpd); - - /* force A4LC on after A3RV has been requested on */ - pm_genpd_poweron(&sh7372_a4lc.genpd); - - return ret; + sh7372_intcs_suspend(); + __raw_writel(0x300fffff, WUPRMSK); /* avoid wakeup */ } -static int pd_power_down_a3rv(struct generic_pm_domain *genpd) +static bool pd_active_wakeup(struct device *dev) { - 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; + return true; } -static int pd_power_down_a4lc(struct generic_pm_domain *genpd) +static bool sh7372_power_down_forbidden(struct dev_pm_domain *domain) { - /* only power down A4LC if A3RV is off */ - if (!(__raw_readl(PSTR) & (1 << sh7372_a3rv.bit_shift))) - return pd_power_down(genpd); - - return -EBUSY; + return false; } -static bool pd_active_wakeup(struct device *dev) -{ - return true; -} +struct dev_power_governor sh7372_always_on_gov = { + .power_down_ok = sh7372_power_down_forbidden, +}; 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); + pm_genpd_init(genpd, sh7372_pd->gov, false); genpd->stop_device = pm_clk_suspend; genpd->start_device = pm_clk_resume; + genpd->dev_irq_safe = true; 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_off = pd_power_down; + genpd->power_on = pd_power_up; genpd->power_on(&sh7372_pd->genpd); } @@ -152,11 +182,15 @@ void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd, { 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); + if (pm_clk_no_clocks(dev)) + pm_clk_add(dev, NULL); +} + +void sh7372_pm_add_subdomain(struct sh7372_pm_domain *sh7372_pd, + struct sh7372_pm_domain *sh7372_sd) +{ + pm_genpd_add_subdomain(&sh7372_pd->genpd, &sh7372_sd->genpd); } struct sh7372_pm_domain sh7372_a4lc = { @@ -171,6 +205,14 @@ struct sh7372_pm_domain sh7372_d4 = { .bit_shift = 3, }; +struct sh7372_pm_domain sh7372_a4r = { + .bit_shift = 5, + .gov = &sh7372_always_on_gov, + .suspend = sh7372_a4r_suspend, + .resume = sh7372_intcs_resume, + .stay_on = true, +}; + struct sh7372_pm_domain sh7372_a3rv = { .bit_shift = 6, }; @@ -179,39 +221,187 @@ struct sh7372_pm_domain sh7372_a3ri = { .bit_shift = 8, }; +struct sh7372_pm_domain sh7372_a3sp = { + .bit_shift = 11, + .gov = &sh7372_always_on_gov, + .no_debug = true, +}; + struct sh7372_pm_domain sh7372_a3sg = { .bit_shift = 13, }; #endif /* CONFIG_PM */ +#if defined(CONFIG_SUSPEND) || defined(CONFIG_CPU_IDLE) +static int sh7372_do_idle_core_standby(unsigned long unused) +{ + cpu_do_idle(); /* WFI when SYSTBCR == 0x10 -> Core Standby */ + return 0; +} + static void sh7372_enter_core_standby(void) { - void __iomem *smfram = (void __iomem *)SMFRAM; + /* set reset vector, translate 4k */ + __raw_writel(__pa(sh7372_resume_core_standby_a3sm), SBAR); + __raw_writel(0, APARMBAREA); - __raw_writel(0, APARMBAREA); /* translate 4k */ - __raw_writel(__pa(sh7372_cpu_resume), SBAR); /* set reset vector */ - __raw_writel(0x10, SYSTBCR); /* enable core standby */ + /* enter sleep mode with SYSTBCR to 0x10 */ + __raw_writel(0x10, SYSTBCR); + cpu_suspend(0, sh7372_do_idle_core_standby); + __raw_writel(0, SYSTBCR); - __raw_writel(0, smfram + 0x3c); /* clear page table address */ + /* disable reset vector translation */ + __raw_writel(0, SBAR); +} +#endif + +#ifdef CONFIG_SUSPEND +static void sh7372_enter_a3sm_common(int pllc0_on) +{ + /* set reset vector, translate 4k */ + __raw_writel(__pa(sh7372_resume_core_standby_a3sm), SBAR); + __raw_writel(0, APARMBAREA); + + if (pllc0_on) + __raw_writel(0, PLLC01STPCR); + else + __raw_writel(1 << 28, PLLC01STPCR); + + __raw_writel(0, PDNSEL); /* power-down A3SM only, not A4S */ + __raw_readl(WUPSFAC); /* read wakeup int. factor before sleep */ + cpu_suspend(0, sh7372_do_idle_a3sm); + __raw_readl(WUPSFAC); /* read wakeup int. factor after wakeup */ + + /* disable reset vector translation */ + __raw_writel(0, SBAR); +} + +static int sh7372_a3sm_valid(unsigned long *mskp, unsigned long *msk2p) +{ + unsigned long mstpsr0, mstpsr1, mstpsr2, mstpsr3, mstpsr4; + unsigned long msk, msk2; + + /* check active clocks to determine potential wakeup sources */ + + mstpsr0 = __raw_readl(MSTPSR0); + if ((mstpsr0 & 0x00000003) != 0x00000003) { + pr_debug("sh7372 mstpsr0 0x%08lx\n", mstpsr0); + return 0; + } + + mstpsr1 = __raw_readl(MSTPSR1); + if ((mstpsr1 & 0xff079b7f) != 0xff079b7f) { + pr_debug("sh7372 mstpsr1 0x%08lx\n", mstpsr1); + return 0; + } - sh7372_cpu_suspend(); - cpu_init(); + mstpsr2 = __raw_readl(MSTPSR2); + if ((mstpsr2 & 0x000741ff) != 0x000741ff) { + pr_debug("sh7372 mstpsr2 0x%08lx\n", mstpsr2); + return 0; + } - /* if page table address is non-NULL then we have been powered down */ - if (__raw_readl(smfram + 0x3c)) { - __raw_writel(__raw_readl(smfram + 0x40), - __va(__raw_readl(smfram + 0x3c))); + mstpsr3 = __raw_readl(MSTPSR3); + if ((mstpsr3 & 0x1a60f010) != 0x1a60f010) { + pr_debug("sh7372 mstpsr3 0x%08lx\n", mstpsr3); + return 0; + } - flush_tlb_all(); - set_cr(__raw_readl(smfram + 0x38)); + mstpsr4 = __raw_readl(MSTPSR4); + if ((mstpsr4 & 0x00008cf0) != 0x00008cf0) { + pr_debug("sh7372 mstpsr4 0x%08lx\n", mstpsr4); + return 0; } - __raw_writel(0, SYSTBCR); /* disable core standby */ - __raw_writel(0, SBAR); /* disable reset vector translation */ + msk = 0; + msk2 = 0; + + /* make bitmaps of limited number of wakeup sources */ + + if ((mstpsr2 & (1 << 23)) == 0) /* SPU2 */ + msk |= 1 << 31; + + if ((mstpsr2 & (1 << 12)) == 0) /* MFI_MFIM */ + msk |= 1 << 21; + + if ((mstpsr4 & (1 << 3)) == 0) /* KEYSC */ + msk |= 1 << 2; + + if ((mstpsr1 & (1 << 24)) == 0) /* CMT0 */ + msk |= 1 << 1; + + if ((mstpsr3 & (1 << 29)) == 0) /* CMT1 */ + msk |= 1 << 1; + + if ((mstpsr4 & (1 << 0)) == 0) /* CMT2 */ + msk |= 1 << 1; + + if ((mstpsr2 & (1 << 13)) == 0) /* MFI_MFIS */ + msk2 |= 1 << 17; + + *mskp = msk; + *msk2p = msk2; + + return 1; +} + +static void sh7372_icr_to_irqcr(unsigned long icr, u16 *irqcr1p, u16 *irqcr2p) +{ + u16 tmp, irqcr1, irqcr2; + int k; + + irqcr1 = 0; + irqcr2 = 0; + + /* convert INTCA ICR register layout to SYSC IRQCR+IRQCR2 */ + for (k = 0; k <= 7; k++) { + tmp = (icr >> ((7 - k) * 4)) & 0xf; + irqcr1 |= (tmp & 0x03) << (k * 2); + irqcr2 |= (tmp >> 2) << (k * 2); + } + + *irqcr1p = irqcr1; + *irqcr2p = irqcr2; +} + +static void sh7372_setup_a3sm(unsigned long msk, unsigned long msk2) +{ + u16 irqcrx_low, irqcrx_high, irqcry_low, irqcry_high; + unsigned long tmp; + + /* read IRQ0A -> IRQ15A mask */ + tmp = bitrev8(__raw_readb(INTMSK00A)); + tmp |= bitrev8(__raw_readb(INTMSK10A)) << 8; + + /* setup WUPSMSK from clocks and external IRQ mask */ + msk = (~msk & 0xc030000f) | (tmp << 4); + __raw_writel(msk, WUPSMSK); + + /* propage level/edge trigger for external IRQ 0->15 */ + sh7372_icr_to_irqcr(__raw_readl(ICR1A), &irqcrx_low, &irqcry_low); + sh7372_icr_to_irqcr(__raw_readl(ICR2A), &irqcrx_high, &irqcry_high); + __raw_writel((irqcrx_high << 16) | irqcrx_low, IRQCR); + __raw_writel((irqcry_high << 16) | irqcry_low, IRQCR2); + + /* read IRQ16A -> IRQ31A mask */ + tmp = bitrev8(__raw_readb(INTMSK20A)); + tmp |= bitrev8(__raw_readb(INTMSK30A)) << 8; + + /* setup WUPSMSK2 from clocks and external IRQ mask */ + msk2 = (~msk2 & 0x00030000) | tmp; + __raw_writel(msk2, WUPSMSK2); + + /* propage level/edge trigger for external IRQ 16->31 */ + sh7372_icr_to_irqcr(__raw_readl(ICR3A), &irqcrx_low, &irqcry_low); + sh7372_icr_to_irqcr(__raw_readl(ICR4A), &irqcrx_high, &irqcry_high); + __raw_writel((irqcrx_high << 16) | irqcrx_low, IRQCR3); + __raw_writel((irqcry_high << 16) | irqcry_low, IRQCR4); } +#endif #ifdef CONFIG_CPU_IDLE + static void sh7372_cpuidle_setup(struct cpuidle_device *dev) { struct cpuidle_state *state; @@ -239,9 +429,25 @@ static void sh7372_cpuidle_init(void) {} #endif #ifdef CONFIG_SUSPEND + static int sh7372_enter_suspend(suspend_state_t suspend_state) { - sh7372_enter_core_standby(); + unsigned long msk, msk2; + + /* check active clocks to determine potential wakeup sources */ + if (sh7372_a3sm_valid(&msk, &msk2)) { + + /* convert INTC mask and sense to SYSC mask and sense */ + sh7372_setup_a3sm(msk, msk2); + + /* enter A3SM sleep with PLLC0 off */ + pr_debug("entering A3SM\n"); + sh7372_enter_a3sm_common(0); + } else { + /* default to Core Standby that supports all wakeup sources */ + pr_debug("entering Core Standby\n"); + sh7372_enter_core_standby(); + } return 0; } @@ -253,9 +459,6 @@ static void sh7372_suspend_init(void) static void sh7372_suspend_init(void) {} #endif -#define DBGREG1 0xe6100020 -#define DBGREG9 0xe6100040 - void __init sh7372_pm_init(void) { /* enable DBG hardware block to kick SYSC */ @@ -263,6 +466,9 @@ void __init sh7372_pm_init(void) __raw_writel(0x0000a501, DBGREG9); __raw_writel(0x00000000, DBGREG1); + /* do not convert A3SM, A3SP, A3SG, A4R power down into A4S */ + __raw_writel(0, PDNSEL); + sh7372_suspend_init(); sh7372_cpuidle_init(); } diff --git a/arch/arm/mach-shmobile/pm_runtime.c b/arch/arm/mach-shmobile/pm_runtime.c index 6ec454e1e06..bd5c6a3b8c5 100644 --- a/arch/arm/mach-shmobile/pm_runtime.c +++ b/arch/arm/mach-shmobile/pm_runtime.c @@ -15,6 +15,7 @@ #include <linux/io.h> #include <linux/pm_runtime.h> #include <linux/pm_domain.h> +#include <linux/pm_clock.h> #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/sh_clk.h> diff --git a/arch/arm/mach-shmobile/setup-sh7372.c b/arch/arm/mach-shmobile/setup-sh7372.c index 2d9b1b1a253..2380389e6ac 100644 --- a/arch/arm/mach-shmobile/setup-sh7372.c +++ b/arch/arm/mach-shmobile/setup-sh7372.c @@ -30,6 +30,7 @@ #include <linux/sh_dma.h> #include <linux/sh_intc.h> #include <linux/sh_timer.h> +#include <linux/pm_domain.h> #include <mach/hardware.h> #include <mach/sh7372.h> #include <asm/mach-types.h> @@ -990,9 +991,14 @@ void __init sh7372_add_standard_devices(void) sh7372_init_pm_domain(&sh7372_a4lc); sh7372_init_pm_domain(&sh7372_a4mp); sh7372_init_pm_domain(&sh7372_d4); + sh7372_init_pm_domain(&sh7372_a4r); sh7372_init_pm_domain(&sh7372_a3rv); sh7372_init_pm_domain(&sh7372_a3ri); sh7372_init_pm_domain(&sh7372_a3sg); + sh7372_init_pm_domain(&sh7372_a3sp); + + sh7372_pm_add_subdomain(&sh7372_a4lc, &sh7372_a3rv); + sh7372_pm_add_subdomain(&sh7372_a4r, &sh7372_a4lc); platform_add_devices(sh7372_early_devices, ARRAY_SIZE(sh7372_early_devices)); @@ -1003,6 +1009,25 @@ void __init sh7372_add_standard_devices(void) sh7372_add_device_to_domain(&sh7372_a3rv, &vpu_device); sh7372_add_device_to_domain(&sh7372_a4mp, &spu0_device); sh7372_add_device_to_domain(&sh7372_a4mp, &spu1_device); + sh7372_add_device_to_domain(&sh7372_a3sp, &scif0_device); + sh7372_add_device_to_domain(&sh7372_a3sp, &scif1_device); + sh7372_add_device_to_domain(&sh7372_a3sp, &scif2_device); + sh7372_add_device_to_domain(&sh7372_a3sp, &scif3_device); + sh7372_add_device_to_domain(&sh7372_a3sp, &scif4_device); + sh7372_add_device_to_domain(&sh7372_a3sp, &scif5_device); + sh7372_add_device_to_domain(&sh7372_a3sp, &scif6_device); + sh7372_add_device_to_domain(&sh7372_a3sp, &iic1_device); + sh7372_add_device_to_domain(&sh7372_a3sp, &dma0_device); + sh7372_add_device_to_domain(&sh7372_a3sp, &dma1_device); + sh7372_add_device_to_domain(&sh7372_a3sp, &dma2_device); + sh7372_add_device_to_domain(&sh7372_a3sp, &usb_dma0_device); + sh7372_add_device_to_domain(&sh7372_a3sp, &usb_dma1_device); + sh7372_add_device_to_domain(&sh7372_a4r, &iic0_device); + sh7372_add_device_to_domain(&sh7372_a4r, &veu0_device); + sh7372_add_device_to_domain(&sh7372_a4r, &veu1_device); + sh7372_add_device_to_domain(&sh7372_a4r, &veu2_device); + sh7372_add_device_to_domain(&sh7372_a4r, &veu3_device); + sh7372_add_device_to_domain(&sh7372_a4r, &jpu_device); } void __init sh7372_add_early_devices(void) diff --git a/arch/arm/mach-shmobile/sleep-sh7372.S b/arch/arm/mach-shmobile/sleep-sh7372.S index d37d3ca4d18..f3ab3c5810e 100644 --- a/arch/arm/mach-shmobile/sleep-sh7372.S +++ b/arch/arm/mach-shmobile/sleep-sh7372.S @@ -30,58 +30,20 @@ */ #include <linux/linkage.h> +#include <linux/init.h> +#include <asm/memory.h> #include <asm/assembler.h> -#define SMFRAM 0xe6a70000 - - .align -kernel_flush: - .word v7_flush_dcache_all - - .align 3 -ENTRY(sh7372_cpu_suspend) - stmfd sp!, {r0-r12, lr} @ save registers on stack - - ldr r8, =SMFRAM - - mov r4, sp @ Store sp - mrs r5, spsr @ Store spsr - mov r6, lr @ Store lr - stmia r8!, {r4-r6} - - mrc p15, 0, r4, c1, c0, 2 @ Coprocessor access control register - mrc p15, 0, r5, c2, c0, 0 @ TTBR0 - mrc p15, 0, r6, c2, c0, 1 @ TTBR1 - mrc p15, 0, r7, c2, c0, 2 @ TTBCR - stmia r8!, {r4-r7} - - mrc p15, 0, r4, c3, c0, 0 @ Domain access Control Register - mrc p15, 0, r5, c10, c2, 0 @ PRRR - mrc p15, 0, r6, c10, c2, 1 @ NMRR - stmia r8!,{r4-r6} - - mrc p15, 0, r4, c13, c0, 1 @ Context ID - mrc p15, 0, r5, c13, c0, 2 @ User r/w thread and process ID - mrc p15, 0, r6, c12, c0, 0 @ Secure or NS vector base address - mrs r7, cpsr @ Store current cpsr - stmia r8!, {r4-r7} - - mrc p15, 0, r4, c1, c0, 0 @ save control register - stmia r8!, {r4} - - /* - * jump out to kernel flush routine - * - reuse that code is better - * - it executes in a cached space so is faster than refetch per-block - * - should be faster and will change with kernel - * - 'might' have to copy address, load and jump to it - * Flush all data from the L1 data cache before disabling - * SCTLR.C bit. - */ - ldr r1, kernel_flush - mov lr, pc - bx r1 +#if defined(CONFIG_SUSPEND) || defined(CONFIG_CPU_IDLE) + .align 12 + .text + .global sh7372_resume_core_standby_a3sm +sh7372_resume_core_standby_a3sm: + ldr pc, 1f +1: .long cpu_resume - PAGE_OFFSET + PLAT_PHYS_OFFSET + .global sh7372_do_idle_a3sm +sh7372_do_idle_a3sm: /* * Clear the SCTLR.C bit to prevent further data cache * allocation. Clearing SCTLR.C would make all the data accesses @@ -92,10 +54,13 @@ ENTRY(sh7372_cpu_suspend) mcr p15, 0, r0, c1, c0, 0 isb + /* disable L2 cache in the aux control register */ + mrc p15, 0, r10, c1, c0, 1 + bic r10, r10, #2 + mcr p15, 0, r10, c1, c0, 1 + /* - * Invalidate L1 data cache. Even though only invalidate is - * necessary exported flush API is used here. Doing clean - * on already clean cache would be almost NOP. + * Invalidate data cache again. */ ldr r1, kernel_flush blx r1 @@ -115,146 +80,16 @@ ENTRY(sh7372_cpu_suspend) dsb dmb -/* - * =================================== - * == WFI instruction => Enter idle == - * =================================== - */ - wfi @ wait for interrupt - -/* - * =================================== - * == Resume path for non-OFF modes == - * =================================== - */ - mrc p15, 0, r0, c1, c0, 0 - tst r0, #(1 << 2) @ Check C bit enabled? - orreq r0, r0, #(1 << 2) @ Enable the C bit if cleared - mcreq p15, 0, r0, c1, c0, 0 - isb - -/* - * =================================== - * == Exit point from non-OFF modes == - * =================================== - */ - ldmfd sp!, {r0-r12, pc} @ restore regs and return +#define SPDCR 0xe6180008 +#define A3SM (1 << 12) - .pool + /* A3SM power down */ + ldr r0, =SPDCR + ldr r1, =A3SM + str r1, [r0] +1: + b 1b - .align 12 - .text - .global sh7372_cpu_resume -sh7372_cpu_resume: - - mov r1, #0 - /* - * Invalidate all instruction caches to PoU - * and flush branch target cache - */ - mcr p15, 0, r1, c7, c5, 0 - - ldr r3, =SMFRAM - - ldmia r3!, {r4-r6} - mov sp, r4 @ Restore sp - msr spsr_cxsf, r5 @ Restore spsr - mov lr, r6 @ Restore lr - - ldmia r3!, {r4-r7} - mcr p15, 0, r4, c1, c0, 2 @ Coprocessor access Control Register - mcr p15, 0, r5, c2, c0, 0 @ TTBR0 - mcr p15, 0, r6, c2, c0, 1 @ TTBR1 - mcr p15, 0, r7, c2, c0, 2 @ TTBCR - - ldmia r3!,{r4-r6} - mcr p15, 0, r4, c3, c0, 0 @ Domain access Control Register - mcr p15, 0, r5, c10, c2, 0 @ PRRR - mcr p15, 0, r6, c10, c2, 1 @ NMRR - - ldmia r3!,{r4-r7} - mcr p15, 0, r4, c13, c0, 1 @ Context ID - mcr p15, 0, r5, c13, c0, 2 @ User r/w thread and process ID - mrc p15, 0, r6, c12, c0, 0 @ Secure or NS vector base address - msr cpsr, r7 @ store cpsr - - /* Starting to enable MMU here */ - mrc p15, 0, r7, c2, c0, 2 @ Read TTBRControl - /* Extract N (0:2) bits and decide whether to use TTBR0 or TTBR1 */ - and r7, #0x7 - cmp r7, #0x0 - beq usettbr0 -ttbr_error: - /* - * More work needs to be done to support N[0:2] value other than 0 - * So looping here so that the error can be detected - */ - b ttbr_error - - .align -cache_pred_disable_mask: - .word 0xFFFFE7FB -ttbrbit_mask: - .word 0xFFFFC000 -table_index_mask: - .word 0xFFF00000 -table_entry: - .word 0x00000C02 -usettbr0: - - mrc p15, 0, r2, c2, c0, 0 - ldr r5, ttbrbit_mask - and r2, r5 - mov r4, pc - ldr r5, table_index_mask - and r4, r5 @ r4 = 31 to 20 bits of pc - /* Extract the value to be written to table entry */ - ldr r6, table_entry - /* r6 has the value to be written to table entry */ - add r6, r6, r4 - /* Getting the address of table entry to modify */ - lsr r4, #18 - /* r2 has the location which needs to be modified */ - add r2, r4 - ldr r4, [r2] - str r6, [r2] /* modify the table entry */ - - mov r7, r6 - mov r5, r2 - mov r6, r4 - /* r5 = original page table address */ - /* r6 = original page table data */ - - mov r0, #0 - mcr p15, 0, r0, c7, c5, 4 @ Flush prefetch buffer - mcr p15, 0, r0, c7, c5, 6 @ Invalidate branch predictor array - mcr p15, 0, r0, c8, c5, 0 @ Invalidate instruction TLB - mcr p15, 0, r0, c8, c6, 0 @ Invalidate data TLB - - /* - * Restore control register. This enables the MMU. - * The caches and prediction are not enabled here, they - * will be enabled after restoring the MMU table entry. - */ - ldmia r3!, {r4} - stmia r3!, {r5} /* save original page table address */ - stmia r3!, {r6} /* save original page table data */ - stmia r3!, {r7} /* save modified page table data */ - - ldr r2, cache_pred_disable_mask - and r4, r2 - mcr p15, 0, r4, c1, c0, 0 - dsb - isb - - ldr r0, =restoremmu_on - bx r0 - -/* - * ============================== - * == Exit point from OFF mode == - * ============================== - */ -restoremmu_on: - - ldmfd sp!, {r0-r12, pc} @ restore regs and return +kernel_flush: + .word v7_flush_dcache_all +#endif diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index ed5cb5af528..6b99fc3f9b6 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -91,6 +91,7 @@ config S390 select HAVE_ARCH_MUTEX_CPU_RELAX select HAVE_ARCH_JUMP_LABEL if !MARCH_G5 select HAVE_RCU_TABLE_FREE if SMP + select ARCH_SAVE_PAGE_KEYS if HIBERNATION select ARCH_INLINE_SPIN_TRYLOCK select ARCH_INLINE_SPIN_TRYLOCK_BH select ARCH_INLINE_SPIN_LOCK diff --git a/arch/s390/kernel/suspend.c b/arch/s390/kernel/suspend.c index cf9e5c6d552..b6f9afed74e 100644 --- a/arch/s390/kernel/suspend.c +++ b/arch/s390/kernel/suspend.c @@ -7,6 +7,7 @@ */ #include <linux/pfn.h> +#include <linux/mm.h> #include <asm/system.h> /* @@ -14,6 +15,123 @@ */ extern const void __nosave_begin, __nosave_end; +/* + * The restore of the saved pages in an hibernation image will set + * the change and referenced bits in the storage key for each page. + * Overindication of the referenced bits after an hibernation cycle + * does not cause any harm but the overindication of the change bits + * would cause trouble. + * Use the ARCH_SAVE_PAGE_KEYS hooks to save the storage key of each + * page to the most significant byte of the associated page frame + * number in the hibernation image. + */ + +/* + * Key storage is allocated as a linked list of pages. + * The size of the keys array is (PAGE_SIZE - sizeof(long)) + */ +struct page_key_data { + struct page_key_data *next; + unsigned char data[]; +}; + +#define PAGE_KEY_DATA_SIZE (PAGE_SIZE - sizeof(struct page_key_data *)) + +static struct page_key_data *page_key_data; +static struct page_key_data *page_key_rp, *page_key_wp; +static unsigned long page_key_rx, page_key_wx; + +/* + * For each page in the hibernation image one additional byte is + * stored in the most significant byte of the page frame number. + * On suspend no additional memory is required but on resume the + * keys need to be memorized until the page data has been restored. + * Only then can the storage keys be set to their old state. + */ +unsigned long page_key_additional_pages(unsigned long pages) +{ + return DIV_ROUND_UP(pages, PAGE_KEY_DATA_SIZE); +} + +/* + * Free page_key_data list of arrays. + */ +void page_key_free(void) +{ + struct page_key_data *pkd; + + while (page_key_data) { + pkd = page_key_data; + page_key_data = pkd->next; + free_page((unsigned long) pkd); + } +} + +/* + * Allocate page_key_data list of arrays with enough room to store + * one byte for each page in the hibernation image. + */ +int page_key_alloc(unsigned long pages) +{ + struct page_key_data *pk; + unsigned long size; + + size = DIV_ROUND_UP(pages, PAGE_KEY_DATA_SIZE); + while (size--) { + pk = (struct page_key_data *) get_zeroed_page(GFP_KERNEL); + if (!pk) { + page_key_free(); + return -ENOMEM; + } + pk->next = page_key_data; + page_key_data = pk; + } + page_key_rp = page_key_wp = page_key_data; + page_key_rx = page_key_wx = 0; + return 0; +} + +/* + * Save the storage key into the upper 8 bits of the page frame number. + */ +void page_key_read(unsigned long *pfn) +{ + unsigned long addr; + + addr = (unsigned long) page_address(pfn_to_page(*pfn)); + *(unsigned char *) pfn = (unsigned char) page_get_storage_key(addr); +} + +/* + * Extract the storage key from the upper 8 bits of the page frame number + * and store it in the page_key_data list of arrays. + */ +void page_key_memorize(unsigned long *pfn) +{ + page_key_wp->data[page_key_wx] = *(unsigned char *) pfn; + *(unsigned char *) pfn = 0; + if (++page_key_wx < PAGE_KEY_DATA_SIZE) + return; + page_key_wp = page_key_wp->next; + page_key_wx = 0; +} + +/* + * Get the next key from the page_key_data list of arrays and set the + * storage key of the page referred by @address. If @address refers to + * a "safe" page the swsusp_arch_resume code will transfer the storage + * key from the buffer page to the original page. + */ +void page_key_write(void *address) +{ + page_set_storage_key((unsigned long) address, + page_key_rp->data[page_key_rx], 0); + if (++page_key_rx >= PAGE_KEY_DATA_SIZE) + return; + page_key_rp = page_key_rp->next; + page_key_rx = 0; +} + int pfn_is_nosave(unsigned long pfn) { unsigned long nosave_begin_pfn = PFN_DOWN(__pa(&__nosave_begin)); diff --git a/arch/s390/kernel/swsusp_asm64.S b/arch/s390/kernel/swsusp_asm64.S index 51bcdb50a23..acb78cdee89 100644 --- a/arch/s390/kernel/swsusp_asm64.S +++ b/arch/s390/kernel/swsusp_asm64.S @@ -136,11 +136,14 @@ ENTRY(swsusp_arch_resume) 0: lg %r2,8(%r1) lg %r4,0(%r1) + iske %r0,%r4 lghi %r3,PAGE_SIZE lghi %r5,PAGE_SIZE 1: mvcle %r2,%r4,0 jo 1b + lg %r2,8(%r1) + sske %r0,%r2 lg %r1,16(%r1) ltgr %r1,%r1 jnz 0b |