* [RFC PATCH 1/8] mfd: db8500: Add IO force function
2013-03-22 16:20 [RFC PATCH 0/8] ux500 suspend-resume Rickard Andersson
@ 2013-03-22 16:20 ` Rickard Andersson
2013-03-22 16:20 ` [RFC PATCH 2/8] ARM: ux500: Add platform suspend ops Rickard Andersson
` (6 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Rickard Andersson @ 2013-03-22 16:20 UTC (permalink / raw)
To: rjw, linux-pm
Cc: daniel.lezcano, hongbo.zhang, khilman, ulf.hansson, linus.walleij,
rickard.andersson
The IO force signal needs to be controlled when
the system enters/exits the sleep state ApSleep.
This hardware signal tells the system that some
pins could be driven by GPIO settings instead of
directly from the hardware block.
Signed-off-by: Rickard Andersson <rickard.andersson@stericsson.com>
---
drivers/mfd/db8500-prcmu.c | 9 +++++++++
include/linux/mfd/db8500-prcmu.h | 1 +
include/linux/mfd/dbx500-prcmu.h | 5 +++++
3 files changed, 15 insertions(+)
diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c
index 268f45d..7f32575 100644
--- a/drivers/mfd/db8500-prcmu.c
+++ b/drivers/mfd/db8500-prcmu.c
@@ -879,6 +879,15 @@ bool db8500_prcmu_pending_irq(void)
return false;
}
+void db8500_prcmu_set_ioforce(bool enable)
+{
+ u32 val = readl(PRCM_IOCR) & ~PRCM_IOCR_IOFORCE;
+
+ if (enable)
+ val |= PRCM_IOCR_IOFORCE;
+ writel(val, PRCM_IOCR);
+}
+
/*
* This function checks if the specified cpu is in in WFI. It's usage
* makes sense only if the gic is decoupled with the db8500_prcmu_gic_decouple
diff --git a/include/linux/mfd/db8500-prcmu.h b/include/linux/mfd/db8500-prcmu.h
index 6ee4247..21660f6 100644
--- a/include/linux/mfd/db8500-prcmu.h
+++ b/include/linux/mfd/db8500-prcmu.h
@@ -547,6 +547,7 @@ int db8500_prcmu_gic_recouple(void);
int db8500_prcmu_copy_gic_settings(void);
bool db8500_prcmu_gic_pending_irq(void);
bool db8500_prcmu_pending_irq(void);
+void db8500_prcmu_set_ioforce(bool enable);
bool db8500_prcmu_is_cpu_in_wfi(int cpu);
void db8500_prcmu_enable_wakeups(u32 wakeups);
int db8500_prcmu_set_epod(u16 epod_id, u8 epod_state);
diff --git a/include/linux/mfd/dbx500-prcmu.h b/include/linux/mfd/dbx500-prcmu.h
index c202d6c..2909546 100644
--- a/include/linux/mfd/dbx500-prcmu.h
+++ b/include/linux/mfd/dbx500-prcmu.h
@@ -267,6 +267,11 @@ static inline bool prcmu_pending_irq(void)
return db8500_prcmu_pending_irq();
}
+static inline void prcmu_set_ioforce(bool force)
+{
+ return db8500_prcmu_set_ioforce(force);
+}
+
static inline int prcmu_set_epod(u16 epod_id, u8 epod_state)
{
return db8500_prcmu_set_epod(epod_id, epod_state);
--
1.8.2
^ permalink raw reply related [flat|nested] 9+ messages in thread* [RFC PATCH 2/8] ARM: ux500: Add platform suspend ops
2013-03-22 16:20 [RFC PATCH 0/8] ux500 suspend-resume Rickard Andersson
2013-03-22 16:20 ` [RFC PATCH 1/8] mfd: db8500: Add IO force function Rickard Andersson
@ 2013-03-22 16:20 ` Rickard Andersson
2013-03-22 16:20 ` [RFC PATCH 3/8] PM / Domains: Add on-off notifiers Rickard Andersson
` (5 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Rickard Andersson @ 2013-03-22 16:20 UTC (permalink / raw)
To: rjw, linux-pm
Cc: daniel.lezcano, hongbo.zhang, khilman, ulf.hansson, linus.walleij,
rickard.andersson
Support for platform suspend operation
WFI (Wait For Interrupt) is added.
Signed-off-by: Rickard Andersson <rickard.andersson@stericsson.com>
---
arch/arm/mach-ux500/Makefile | 1 +
arch/arm/mach-ux500/suspend.c | 35 +++++++++++++++++++++++++++++++++++
2 files changed, 36 insertions(+)
create mode 100644 arch/arm/mach-ux500/suspend.c
diff --git a/arch/arm/mach-ux500/Makefile b/arch/arm/mach-ux500/Makefile
index f24710d..f182cc21 100644
--- a/arch/arm/mach-ux500/Makefile
+++ b/arch/arm/mach-ux500/Makefile
@@ -5,6 +5,7 @@
obj-y := cpu.o devices.o devices-common.o \
id.o usb.o timer.o
obj-$(CONFIG_CPU_IDLE) += cpuidle.o
+obj-$(CONFIG_SUSPEND) += suspend.o
obj-$(CONFIG_CACHE_L2X0) += cache-l2x0.o
obj-$(CONFIG_UX500_SOC_DB8500) += cpu-db8500.o devices-db8500.o
obj-$(CONFIG_MACH_MOP500) += board-mop500.o board-mop500-sdi.o \
diff --git a/arch/arm/mach-ux500/suspend.c b/arch/arm/mach-ux500/suspend.c
new file mode 100644
index 0000000..d1ac203
--- /dev/null
+++ b/arch/arm/mach-ux500/suspend.c
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) STMicroelectronics 2009
+ * Copyright (C) ST-Ericsson SA 2010-2013
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Authors: Rickard Andersson <rickard.andersson@stericsson.com>,
+ * Jonas Aaberg <jonas.aberg@stericsson.com>,
+ * Sundar Iyer for ST-Ericsson.
+ */
+
+#include <linux/suspend.h>
+
+static int ux500_suspend_enter(suspend_state_t state)
+{
+ cpu_do_idle();
+ return 0;
+}
+
+static int ux500_suspend_valid(suspend_state_t state)
+{
+ return state == PM_SUSPEND_MEM || state == PM_SUSPEND_STANDBY;
+}
+
+static const struct platform_suspend_ops ux500_suspend_ops = {
+ .enter = ux500_suspend_enter,
+ .valid = ux500_suspend_valid,
+};
+
+static __init int ux500_suspend_init(void)
+{
+ suspend_set_ops(&ux500_suspend_ops);
+ return 0;
+}
+device_initcall(ux500_suspend_init);
--
1.8.2
^ permalink raw reply related [flat|nested] 9+ messages in thread* [RFC PATCH 3/8] PM / Domains: Add on-off notifiers
2013-03-22 16:20 [RFC PATCH 0/8] ux500 suspend-resume Rickard Andersson
2013-03-22 16:20 ` [RFC PATCH 1/8] mfd: db8500: Add IO force function Rickard Andersson
2013-03-22 16:20 ` [RFC PATCH 2/8] ARM: ux500: Add platform suspend ops Rickard Andersson
@ 2013-03-22 16:20 ` Rickard Andersson
2013-03-22 16:20 ` [RFC PATCH 4/8] PM / Domains: Lookup domain by name Rickard Andersson
` (4 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Rickard Andersson @ 2013-03-22 16:20 UTC (permalink / raw)
To: rjw, linux-pm
Cc: daniel.lezcano, hongbo.zhang, khilman, ulf.hansson, linus.walleij,
rickard.andersson
Some drivers needs to restore their context directly
when a power domain is activated. For example a driver
handling system bus settings must be able to restore
context before the bus is being used for the first time.
Other examples could be clock settings hardware blocks and
special debugging hardware blocks which should be restored
as early as possible in order to be able to debug direcly
from waking up. Typically these notifers are needed in
systems with CPU coupled power domains. The notifiers
are intended to be trigged by cpuidle driver or the
suspend ops hooks.
The drivers that needs to use these notifiers are
some special cases. Most drivers should not use this
method and instead control their context via the
pm_runtime interface.
Signed-off-by: Rickard Andersson <rickard.andersson@stericsson.com>
---
drivers/base/power/domain.c | 63 +++++++++++++++++++++++++++++++++++++++++++++
include/linux/pm_domain.h | 22 ++++++++++++++++
2 files changed, 85 insertions(+)
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index acc3a8d..91e3b7d 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -1956,6 +1956,68 @@ int pm_genpd_name_detach_cpuidle(const char *name)
return pm_genpd_detach_cpuidle(pm_genpd_lookup_name(name));
}
+/**
+ * pm_genpd_register_on_off_notifier - Register to early power on /
+ * late power off notifications.
+ * Only use this function if absolutly needed. It is only intended to be
+ * used for power domains that are coupled with the CPU, that is, power
+ * domains being controlled from cpuidle and the platform suspend ops hooks.
+ * Also only devices that needs their context to be restored directly when
+ * leaving a sleep state should use this method. Most devices should be
+ * fine handling their context and power domains via pm_runtime.
+ * @dev: Device to register.
+ * @nb: Notifier block to be registered.
+ */
+int pm_genpd_register_on_off_notifier(struct device *dev,
+ struct notifier_block *nb)
+{
+ struct generic_pm_domain *genpd;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ genpd = dev_to_genpd(dev);
+
+ return atomic_notifier_chain_register(&genpd->on_off_notifier, nb);
+}
+
+/**
+ * pm_genpd_unregister_on_off_notifier - Unregister to early power on /
+ * late power off notification.
+ * @dev: Device to unregister.
+ * @nb: Notifier block to be unregistered.
+ */
+int pm_genpd_unregister_on_off_notifier(struct device *dev,
+ struct notifier_block *nb)
+{
+ struct generic_pm_domain *genpd;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ genpd = dev_to_genpd(dev);
+
+ return atomic_notifier_chain_unregister(&genpd->on_off_notifier, nb);
+}
+
+/**
+ * pm_genpd_notify_power_on_off - Notify that the CPU coupled power
+ * domain is going to be powered off or has been powered on.5D
+ * Intended users of this function are cpuidle drivers and the platform
+ * suspend operations implementation.
+ * @genpd: pm domain that will change state.
+ * @power_on_off: true = has been powered on, false = will power off.
+ */
+int pm_genpd_notify_power_on_off(struct generic_pm_domain *genpd,
+ bool power_on_off)
+{
+ if (IS_ERR_OR_NULL(genpd))
+ return -EINVAL;
+
+ atomic_notifier_call_chain((&genpd->on_off_notifier),
+ power_on_off, NULL);
+
+ return 0;
+}
+
/* Default device callbacks for generic PM domains. */
/**
@@ -2137,6 +2199,7 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
atomic_set(&genpd->sd_count, 0);
genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE;
init_waitqueue_head(&genpd->status_wait_queue);
+ ATOMIC_INIT_NOTIFIER_HEAD(&genpd->on_off_notifier);
genpd->poweroff_task = NULL;
genpd->resume_count = 0;
genpd->device_count = 0;
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index 7c1d252..bf5e7d514 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -82,6 +82,7 @@ struct generic_pm_domain {
bool cached_power_down_ok;
struct device_node *of_node; /* Node in device tree */
struct gpd_cpu_data *cpu_data;
+ struct atomic_notifier_head on_off_notifier;
};
static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd)
@@ -159,6 +160,12 @@ extern int pm_genpd_attach_cpuidle(struct generic_pm_domain *genpd, int state);
extern int pm_genpd_name_attach_cpuidle(const char *name, int state);
extern int pm_genpd_detach_cpuidle(struct generic_pm_domain *genpd);
extern int pm_genpd_name_detach_cpuidle(const char *name);
+extern int pm_genpd_register_on_off_notifier(struct device *dev,
+ struct notifier_block *nb);
+extern int pm_genpd_unregister_on_off_notifier(struct device *dev,
+ struct notifier_block *nb);
+extern int pm_genpd_notify_power_on_off(struct generic_pm_domain *genpd,
+ bool power_on_off);
extern void pm_genpd_init(struct generic_pm_domain *genpd,
struct dev_power_governor *gov, bool is_off);
@@ -243,6 +250,21 @@ static inline int pm_genpd_name_detach_cpuidle(const char *name)
{
return -ENOSYS;
}
+static inline int pm_genpd_register_on_off_notifier(struct device *dev,
+ struct notifier_block *nb)
+{
+ return -ENOSYS;
+}
+static inline int pm_genpd_unregister_on_off_notifier(struct device *dev,
+ struct notifier_block *nb)
+{
+ return -ENOSYS;
+}
+static inline int pm_genpd_notify_power_on_off(struct generic_pm_domain *genpd,
+ bool power_on_off)
+{
+ return -ENOSYS;
+}
static inline void pm_genpd_init(struct generic_pm_domain *genpd,
struct dev_power_governor *gov, bool is_off)
{
--
1.8.2
^ permalink raw reply related [flat|nested] 9+ messages in thread* [RFC PATCH 4/8] PM / Domains: Lookup domain by name
2013-03-22 16:20 [RFC PATCH 0/8] ux500 suspend-resume Rickard Andersson
` (2 preceding siblings ...)
2013-03-22 16:20 ` [RFC PATCH 3/8] PM / Domains: Add on-off notifiers Rickard Andersson
@ 2013-03-22 16:20 ` Rickard Andersson
2013-03-22 16:20 ` [RFC PATCH 5/8] ARM: ux500: Create APE generic power domain Rickard Andersson
` (3 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Rickard Andersson @ 2013-03-22 16:20 UTC (permalink / raw)
To: rjw, linux-pm
Cc: daniel.lezcano, hongbo.zhang, khilman, ulf.hansson, linus.walleij,
rickard.andersson
Make the function pm_genpd_lookup_name(..) non
static. This is done to limit the need for "name
functions" as e.g. pm_genpd_name_attach_cpuidle.
Also the name lookup takes a mutex and should not
be called when IRQ's are off. So by exporting this
function the name lookup can be done before IRQ's
are off.
Signed-off-by: Rickard Andersson <rickard.andersson@stericsson.com>
---
drivers/base/power/domain.c | 2 +-
include/linux/pm_domain.h | 6 +++++-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 91e3b7d..a8755cb 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -53,7 +53,7 @@
static LIST_HEAD(gpd_list);
static DEFINE_MUTEX(gpd_list_lock);
-static struct generic_pm_domain *pm_genpd_lookup_name(const char *domain_name)
+struct generic_pm_domain *pm_genpd_lookup_name(const char *domain_name)
{
struct generic_pm_domain *genpd = NULL, *gpd;
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index bf5e7d514..0fa2d5e 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -129,7 +129,7 @@ static inline struct generic_pm_domain_data *dev_gpd_data(struct device *dev)
}
extern struct dev_power_governor simple_qos_governor;
-
+extern struct generic_pm_domain *pm_genpd_lookup_name(const char *domain_name);
extern struct generic_pm_domain *dev_to_genpd(struct device *dev);
extern int __pm_genpd_add_device(struct generic_pm_domain *genpd,
struct device *dev,
@@ -181,6 +181,10 @@ static inline struct generic_pm_domain_data *dev_gpd_data(struct device *dev)
{
return ERR_PTR(-ENOSYS);
}
+static struct generic_pm_domain *pm_genpd_lookup_name(const char *domain_name)
+{
+ return ERR_PTR(-ENOSYS);
+}
static inline struct generic_pm_domain *dev_to_genpd(struct device *dev)
{
return ERR_PTR(-ENOSYS);
--
1.8.2
^ permalink raw reply related [flat|nested] 9+ messages in thread* [RFC PATCH 5/8] ARM: ux500: Create APE generic power domain
2013-03-22 16:20 [RFC PATCH 0/8] ux500 suspend-resume Rickard Andersson
` (3 preceding siblings ...)
2013-03-22 16:20 ` [RFC PATCH 4/8] PM / Domains: Lookup domain by name Rickard Andersson
@ 2013-03-22 16:20 ` Rickard Andersson
2013-03-22 16:20 ` [RFC PATCH 6/8] clk: ux500: Add PRCC power management Rickard Andersson
` (2 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Rickard Andersson @ 2013-03-22 16:20 UTC (permalink / raw)
To: rjw, linux-pm
Cc: daniel.lezcano, hongbo.zhang, khilman, ulf.hansson, linus.walleij,
rickard.andersson
The APE generic power domain is created and
initiated. This power domain is turned off in
the sleep states ApSleep and ApDeepSleep
Signed-off-by: Rickard Andersson <rickard.andersson@stericsson.com>
---
arch/arm/mach-ux500/Kconfig | 1 +
arch/arm/mach-ux500/board-mop500.c | 17 ++++++++++++++++-
2 files changed, 17 insertions(+), 1 deletion(-)
diff --git a/arch/arm/mach-ux500/Kconfig b/arch/arm/mach-ux500/Kconfig
index 5dea906..b2e5c8b 100644
--- a/arch/arm/mach-ux500/Kconfig
+++ b/arch/arm/mach-ux500/Kconfig
@@ -12,6 +12,7 @@ config UX500_SOC_COMMON
select PINCTRL
select PINCTRL_NOMADIK
select PL310_ERRATA_753970 if CACHE_PL310
+ select PM_GENERIC_DOMAINS if PM
config UX500_SOC_DB8500
bool
diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c
index d453522..77d3d1a 100644
--- a/arch/arm/mach-ux500/board-mop500.c
+++ b/arch/arm/mach-ux500/board-mop500.c
@@ -37,7 +37,7 @@
#include <linux/pinctrl/consumer.h>
#include <linux/platform_data/pinctrl-nomadik.h>
#include <linux/platform_data/dma-ste-dma40.h>
-
+#include <linux/pm_domain.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/hardware/gic.h>
@@ -645,6 +645,15 @@ static struct platform_device *snowball_platform_devs[] __initdata = {
&u8500_cpufreq_cooling_device,
};
+struct generic_pm_domain ape_domain = {
+ .name = "APE",
+};
+
+static void __init init_pm_domains(void)
+{
+ pm_genpd_init(&ape_domain, NULL, false);
+}
+
static void __init mop500_init_machine(void)
{
struct device *parent = NULL;
@@ -668,6 +677,8 @@ static void __init mop500_init_machine(void)
mop500_audio_init(parent);
mop500_uart_init(parent);
+ init_pm_domains();
+
u8500_cryp1_hash1_init(parent);
i2c0_devs = ARRAY_SIZE(mop500_i2c0_devices);
@@ -700,6 +711,8 @@ static void __init snowball_init_machine(void)
mop500_audio_init(parent);
mop500_uart_init(parent);
+ init_pm_domains();
+
/* This board has full regulator constraints */
regulator_has_full_constraints();
}
@@ -732,6 +745,8 @@ static void __init hrefv60_init_machine(void)
mop500_audio_init(parent);
mop500_uart_init(parent);
+ init_pm_domains();
+
i2c0_devs = ARRAY_SIZE(mop500_i2c0_devices);
i2c0_devs -= NUM_PRE_V60_I2C0_DEVICES;
--
1.8.2
^ permalink raw reply related [flat|nested] 9+ messages in thread* [RFC PATCH 6/8] clk: ux500: Add PRCC power management
2013-03-22 16:20 [RFC PATCH 0/8] ux500 suspend-resume Rickard Andersson
` (4 preceding siblings ...)
2013-03-22 16:20 ` [RFC PATCH 5/8] ARM: ux500: Create APE generic power domain Rickard Andersson
@ 2013-03-22 16:20 ` Rickard Andersson
2013-03-22 16:20 ` [RFC PATCH 7/8] ARM: ux500: Create u8500-clk device Rickard Andersson
2013-03-22 16:20 ` [RFC PATCH 8/8] ARM: ux500: Add ApSleep state to suspend Rickard Andersson
7 siblings, 0 replies; 9+ messages in thread
From: Rickard Andersson @ 2013-03-22 16:20 UTC (permalink / raw)
To: rjw, linux-pm
Cc: daniel.lezcano, hongbo.zhang, khilman, ulf.hansson, linus.walleij,
rickard.andersson
When reaching a sleep state where the APE power domain
is being turned off the context of the PRCC block needs
to saved. When the power domain is turned on again the
context is restored in order for the PRCC clocks to
function correctly.
Signed-off-by: Rickard Andersson <rickard.andersson@stericsson.com>
---
drivers/clk/ux500/clk-prcc.c | 59 ++++++++++++++++++++++++++++++-
drivers/clk/ux500/clk.h | 4 +++
drivers/clk/ux500/u8500_clk.c | 82 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 144 insertions(+), 1 deletion(-)
diff --git a/drivers/clk/ux500/clk-prcc.c b/drivers/clk/ux500/clk-prcc.c
index 7eee7f7..116da35 100644
--- a/drivers/clk/ux500/clk-prcc.c
+++ b/drivers/clk/ux500/clk-prcc.c
@@ -3,7 +3,8 @@
*
* Copyright (C) 2012 ST-Ericsson SA
* Author: Ulf Hansson <ulf.hansson@linaro.org>
- *
+ * Rickard Andersson <rickard.andersson@stericsson.com>
+ * Jonas Aaberg <jonas.aberg@stericsson.com>
* License terms: GNU General Public License (GPL) version 2
*/
@@ -17,6 +18,8 @@
#include "clk.h"
+#define UX500_NR_PRCC_BANKS 5
+
#define PRCC_PCKEN 0x000
#define PRCC_PCKDIS 0x004
#define PRCC_KCKEN 0x008
@@ -33,6 +36,12 @@ struct clk_prcc {
int is_enabled;
};
+static struct {
+ void __iomem *base;
+ u32 bus_clk;
+ u32 kern_clk;
+} context_prcc[UX500_NR_PRCC_BANKS];
+
/* PRCC clock operations. */
static int clk_prcc_pclk_enable(struct clk_hw *hw)
@@ -162,3 +171,51 @@ struct clk *clk_reg_prcc_kclk(const char *name,
return clk_reg_prcc(name, parent_name, phy_base, cg_sel, flags,
&clk_prcc_kclk_ops);
}
+
+void clk_prcc_save_context(void)
+{
+ int i;
+
+ for (i = 0; i < UX500_NR_PRCC_BANKS; i++) {
+ context_prcc[i].bus_clk =
+ readl(context_prcc[i].base + PRCC_PCKSR);
+ context_prcc[i].kern_clk =
+ readl(context_prcc[i].base + PRCC_KCKSR);
+ }
+}
+
+void clk_prcc_restore_context(void)
+{
+ int i;
+
+ for (i = 0; i < UX500_NR_PRCC_BANKS; i++) {
+ writel(~context_prcc[i].bus_clk,
+ context_prcc[i].base + PRCC_PCKDIS);
+ writel(~context_prcc[i].kern_clk,
+ context_prcc[i].base + PRCC_KCKDIS);
+
+ writel(context_prcc[i].bus_clk,
+ context_prcc[i].base + PRCC_PCKEN);
+ writel(context_prcc[i].kern_clk,
+ context_prcc[i].base + PRCC_KCKEN);
+ }
+}
+
+int __init clk_prcc_init(void)
+{
+ /* PERIPH4 is always on, so no need saving prcc */
+ context_prcc[0].base = ioremap(U8500_CLKRST1_BASE, SZ_4K);
+ context_prcc[1].base = ioremap(U8500_CLKRST2_BASE, SZ_4K);
+ context_prcc[2].base = ioremap(U8500_CLKRST3_BASE, SZ_4K);
+ context_prcc[3].base = ioremap(U8500_CLKRST5_BASE, SZ_4K);
+ context_prcc[4].base = ioremap(U8500_CLKRST6_BASE, SZ_4K);
+
+ if (!context_prcc[0].base
+ || !context_prcc[1].base
+ || !context_prcc[2].base
+ || !context_prcc[3].base
+ || !context_prcc[4].base)
+ return -ENOMEM;
+
+ return 0;
+}
diff --git a/drivers/clk/ux500/clk.h b/drivers/clk/ux500/clk.h
index c3e4491..7936b67 100644
--- a/drivers/clk/ux500/clk.h
+++ b/drivers/clk/ux500/clk.h
@@ -57,4 +57,8 @@ struct clk *clk_reg_prcmu_opp_volt_scalable(const char *name,
unsigned long rate,
unsigned long flags);
+void clk_prcc_save_context(void);
+void clk_prcc_restore_context(void);
+int __init clk_prcc_init(void);
+
#endif /* __UX500_CLK_H */
diff --git a/drivers/clk/ux500/u8500_clk.c b/drivers/clk/ux500/u8500_clk.c
index 6b889a0..0c4319d 100644
--- a/drivers/clk/ux500/u8500_clk.c
+++ b/drivers/clk/ux500/u8500_clk.c
@@ -12,9 +12,19 @@
#include <linux/clk-provider.h>
#include <linux/mfd/dbx500-prcmu.h>
#include <linux/platform_data/clk-ux500.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
#include <mach/db8500-regs.h>
#include "clk.h"
+/*
+ * Number of PRCC parent clocks that needs to be
+ * enabled when saving/restoring PRCC context.
+ */
+#define NR_PRCC_PARENT_CLOCKS 5
+
+struct clk *prcc_context_parent[NR_PRCC_PARENT_CLOCKS];
+
void u8500_clk_init(void)
{
struct prcmu_fw_version *fw_version;
@@ -522,3 +532,75 @@ void u8500_clk_init(void)
U8500_CLKRST6_BASE, BIT(0), CLK_SET_RATE_GATE);
clk_register_clkdev(clk, NULL, "rng");
}
+
+static void prcc_parents_enable(bool enable)
+{
+ int i;
+
+ for (i = 0; i < NR_PRCC_PARENT_CLOCKS; i++) {
+ if (enable)
+ clk_enable(prcc_context_parent[i]);
+ else
+ clk_disable(prcc_context_parent[i]);
+ }
+}
+
+static int prcc_context_call(struct notifier_block *this,
+ unsigned long event, void *data)
+{
+ bool power_on = (bool)event;
+
+ prcc_parents_enable(true);
+
+ if (power_on)
+ clk_prcc_restore_context();
+ else
+ clk_prcc_save_context();
+
+ prcc_parents_enable(false);
+
+ return 0;
+}
+
+static struct notifier_block prcc_context_notifier = {
+ .notifier_call = prcc_context_call,
+};
+
+static struct platform_driver u8500_clk_plat_driver = {
+ .driver = {
+ .name = "u8500-clk",
+ },
+};
+
+static int __init u8500_clk_probe(struct platform_device *pdev)
+{
+ int ret;
+ int i;
+
+ ret = clk_prcc_init();
+ if (ret)
+ return ret;
+
+ for (i = 0; i < NR_PRCC_PARENT_CLOCKS; i++) {
+ const int clusters[] = {1, 2, 3, 5, 6};
+ char clkname[10];
+
+ snprintf(clkname, sizeof(clkname), "PERIPH%d", clusters[i]);
+
+ prcc_context_parent[i] = clk_get_sys(clkname, NULL);
+ BUG_ON(IS_ERR(prcc_context_parent[i]));
+ clk_prepare(prcc_context_parent[i]);
+ }
+
+ ret = pm_genpd_register_on_off_notifier(&pdev->dev,
+ &prcc_context_notifier);
+ return ret;
+}
+
+static int __init u8500_clk_arch_init(void)
+{
+ return platform_driver_probe(&u8500_clk_plat_driver,
+ u8500_clk_probe);
+}
+
+arch_initcall(u8500_clk_arch_init);
--
1.8.2
^ permalink raw reply related [flat|nested] 9+ messages in thread* [RFC PATCH 7/8] ARM: ux500: Create u8500-clk device
2013-03-22 16:20 [RFC PATCH 0/8] ux500 suspend-resume Rickard Andersson
` (5 preceding siblings ...)
2013-03-22 16:20 ` [RFC PATCH 6/8] clk: ux500: Add PRCC power management Rickard Andersson
@ 2013-03-22 16:20 ` Rickard Andersson
2013-03-22 16:20 ` [RFC PATCH 8/8] ARM: ux500: Add ApSleep state to suspend Rickard Andersson
7 siblings, 0 replies; 9+ messages in thread
From: Rickard Andersson @ 2013-03-22 16:20 UTC (permalink / raw)
To: rjw, linux-pm
Cc: daniel.lezcano, hongbo.zhang, khilman, ulf.hansson, linus.walleij,
rickard.andersson
Add platform device for u8500-clk. Also add
the device to the generic power domain APE.
Signed-off-by: Rickard Andersson <rickard.andersson@stericsson.com>
---
arch/arm/mach-ux500/board-mop500.c | 8 ++++++++
arch/arm/mach-ux500/cpu-db8500.c | 1 +
arch/arm/mach-ux500/devices-db8500.c | 5 +++++
arch/arm/mach-ux500/devices-db8500.h | 1 +
4 files changed, 15 insertions(+)
diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c
index 77d3d1a..b351f25 100644
--- a/arch/arm/mach-ux500/board-mop500.c
+++ b/arch/arm/mach-ux500/board-mop500.c
@@ -649,9 +649,17 @@ struct generic_pm_domain ape_domain = {
.name = "APE",
};
+static __init void add_platform_device_to_genpd(struct platform_device *pdev,
+ struct generic_pm_domain *genpd)
+{
+ pm_genpd_add_device(genpd, &pdev->dev);
+}
+
static void __init init_pm_domains(void)
{
pm_genpd_init(&ape_domain, NULL, false);
+
+ add_platform_device_to_genpd(&db8500_clk_device, &ape_domain);
}
static void __init mop500_init_machine(void)
diff --git a/arch/arm/mach-ux500/cpu-db8500.c b/arch/arm/mach-ux500/cpu-db8500.c
index 5b286e0..6ead606 100644
--- a/arch/arm/mach-ux500/cpu-db8500.c
+++ b/arch/arm/mach-ux500/cpu-db8500.c
@@ -142,6 +142,7 @@ static struct platform_device db8500_prcmu_device = {
};
static struct platform_device *platform_devs[] __initdata = {
+ &db8500_clk_device,
&u8500_dma40_device,
&db8500_pmu_device,
&db8500_prcmu_device,
diff --git a/arch/arm/mach-ux500/devices-db8500.c b/arch/arm/mach-ux500/devices-db8500.c
index 318d490..f57c694 100644
--- a/arch/arm/mach-ux500/devices-db8500.c
+++ b/arch/arm/mach-ux500/devices-db8500.c
@@ -194,3 +194,8 @@ struct platform_device u8500_ske_keypad_device = {
.num_resources = ARRAY_SIZE(keypad_resources),
.resource = keypad_resources,
};
+
+struct platform_device db8500_clk_device = {
+ .name = "u8500-clk",
+ .id = -1,
+};
diff --git a/arch/arm/mach-ux500/devices-db8500.h b/arch/arm/mach-ux500/devices-db8500.h
index a5e05f6..0094f3a 100644
--- a/arch/arm/mach-ux500/devices-db8500.h
+++ b/arch/arm/mach-ux500/devices-db8500.h
@@ -14,6 +14,7 @@
struct ske_keypad_platform_data;
struct pl022_ssp_controller;
+extern struct platform_device db8500_clk_device;
static inline struct platform_device *
db8500_add_ske_keypad(struct device *parent,
--
1.8.2
^ permalink raw reply related [flat|nested] 9+ messages in thread* [RFC PATCH 8/8] ARM: ux500: Add ApSleep state to suspend
2013-03-22 16:20 [RFC PATCH 0/8] ux500 suspend-resume Rickard Andersson
` (6 preceding siblings ...)
2013-03-22 16:20 ` [RFC PATCH 7/8] ARM: ux500: Create u8500-clk device Rickard Andersson
@ 2013-03-22 16:20 ` Rickard Andersson
7 siblings, 0 replies; 9+ messages in thread
From: Rickard Andersson @ 2013-03-22 16:20 UTC (permalink / raw)
To: rjw, linux-pm
Cc: daniel.lezcano, hongbo.zhang, khilman, ulf.hansson, linus.walleij,
rickard.andersson
Support for power save state ApSleep is
added to the platform suspend operation
hook.
Signed-off-by: Rickard Andersson <rickard.andersson@stericsson.com>
---
arch/arm/mach-ux500/suspend.c | 57 ++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 56 insertions(+), 1 deletion(-)
diff --git a/arch/arm/mach-ux500/suspend.c b/arch/arm/mach-ux500/suspend.c
index d1ac203..875a92d 100644
--- a/arch/arm/mach-ux500/suspend.c
+++ b/arch/arm/mach-ux500/suspend.c
@@ -10,11 +10,60 @@
*/
#include <linux/suspend.h>
+#include <linux/pm_domain.h>
+#include <linux/mfd/dbx500-prcmu.h>
+
+static u32 suspend_wakeups;
+static u32 running_wakeups;
+static struct generic_pm_domain *ape_domain;
static int ux500_suspend_enter(suspend_state_t state)
{
+ int ret = 0;
+
+ /* Configure the PRCMU for a sleep wakeup */
+ prcmu_enable_wakeups(suspend_wakeups);
+
+ pm_genpd_notify_power_on_off(ape_domain, false);
+
+ prcmu_gic_decouple();
+
+ /* Copy GIC interrupt settings to PRCMU interrupt settings */
+ prcmu_copy_gic_settings();
+
+ if (prcmu_gic_pending_irq()) {
+ pr_info("suspend/resume: pending interrupt\n");
+
+ /* Recouple GIC with the interrupt bus */
+ prcmu_gic_recouple();
+ ret = -EBUSY;
+ goto exit;
+ }
+
+ if (prcmu_pending_irq()) {
+ pr_info("suspend/resume: pending interrupt prcmu\n");
+
+ /* Recouple GIC with the interrupt bus */
+ prcmu_gic_recouple();
+ ret = -EBUSY;
+ goto exit;
+ }
+
+ prcmu_set_ioforce(true);
+
+ prcmu_set_power_state(PRCMU_AP_SLEEP, false, false);
+
cpu_do_idle();
- return 0;
+
+ pm_genpd_notify_power_on_off(ape_domain, true);
+
+ prcmu_set_ioforce(false);
+
+ exit:
+ /* Configure the prcmu with the wake-ups that cpuidle needs */
+ prcmu_enable_wakeups(running_wakeups);
+
+ return ret;
}
static int ux500_suspend_valid(suspend_state_t state)
@@ -29,6 +78,12 @@ static const struct platform_suspend_ops ux500_suspend_ops = {
static __init int ux500_suspend_init(void)
{
+ suspend_wakeups = PRCMU_WAKEUP(ABB) | PRCMU_WAKEUP(RTC);
+ running_wakeups = (PRCMU_WAKEUP(ARM) | PRCMU_WAKEUP(RTC) |
+ PRCMU_WAKEUP(ABB));
+
+ ape_domain = pm_genpd_lookup_name("APE");
+
suspend_set_ops(&ux500_suspend_ops);
return 0;
}
--
1.8.2
^ permalink raw reply related [flat|nested] 9+ messages in thread