* [PATCH V2 01/12] mfd: db8500: Add IO force function
2013-03-28 16:11 [PATCH V2 00/12] ux500 suspend-resume Rickard Andersson
@ 2013-03-28 16:11 ` Rickard Andersson
2013-03-28 16:11 ` [PATCH V2 02/12] ARM: ux500: Add platform suspend ops Rickard Andersson
` (10 subsequent siblings)
11 siblings, 0 replies; 17+ messages in thread
From: Rickard Andersson @ 2013-03-28 16:11 UTC (permalink / raw)
To: rjw, linux-pm, linux-arm-kernel
Cc: hongbo.zhang, ulf.hansson, khilman, linus.walleij, daniel.lezcano,
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] 17+ messages in thread* [PATCH V2 02/12] ARM: ux500: Add platform suspend ops
2013-03-28 16:11 [PATCH V2 00/12] ux500 suspend-resume Rickard Andersson
2013-03-28 16:11 ` [PATCH V2 01/12] mfd: db8500: Add IO force function Rickard Andersson
@ 2013-03-28 16:11 ` Rickard Andersson
2013-03-28 16:11 ` [PATCH V2 03/12] PM / Domains: Add on-off notifiers Rickard Andersson
` (9 subsequent siblings)
11 siblings, 0 replies; 17+ messages in thread
From: Rickard Andersson @ 2013-03-28 16:11 UTC (permalink / raw)
To: rjw, linux-pm, linux-arm-kernel
Cc: hongbo.zhang, ulf.hansson, khilman, linus.walleij, daniel.lezcano,
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] 17+ messages in thread* [PATCH V2 03/12] PM / Domains: Add on-off notifiers
2013-03-28 16:11 [PATCH V2 00/12] ux500 suspend-resume Rickard Andersson
2013-03-28 16:11 ` [PATCH V2 01/12] mfd: db8500: Add IO force function Rickard Andersson
2013-03-28 16:11 ` [PATCH V2 02/12] ARM: ux500: Add platform suspend ops Rickard Andersson
@ 2013-03-28 16:11 ` Rickard Andersson
2013-03-30 23:17 ` Rafael J. Wysocki
2013-03-28 16:11 ` [PATCH V2 04/12] PM / Domains: Lookup domain by name Rickard Andersson
` (8 subsequent siblings)
11 siblings, 1 reply; 17+ messages in thread
From: Rickard Andersson @ 2013-03-28 16:11 UTC (permalink / raw)
To: rjw, linux-pm, linux-arm-kernel
Cc: hongbo.zhang, ulf.hansson, khilman, linus.walleij, daniel.lezcano,
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>
---
I believe notifiers are the best solution to the problem of
restoring context early when waking up after sleep. It is
similar to what is done in /kernel/cpu_pm.c but but for a
generic power domain instead.
An alternative solution could be to try to call the drivers
runtime_resume/runtime_suspend callbacks instead via genpd
from cpuidle or suspend/resume. I do not recommend that
solution for the following reasons:
* Altered use of runtime PM. As it is today the driver itself
controls when to runtime-suspend/runtime-resume.
* Runtime PM is disabled during late stages of suspend.
* Complicates runtime PM for a few special cases.
---
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] 17+ messages in thread* Re: [PATCH V2 03/12] PM / Domains: Add on-off notifiers
2013-03-28 16:11 ` [PATCH V2 03/12] PM / Domains: Add on-off notifiers Rickard Andersson
@ 2013-03-30 23:17 ` Rafael J. Wysocki
2013-04-23 12:26 ` Rickard Andersson
0 siblings, 1 reply; 17+ messages in thread
From: Rafael J. Wysocki @ 2013-03-30 23:17 UTC (permalink / raw)
To: Rickard Andersson
Cc: linux-pm, linux-arm-kernel, daniel.lezcano, hongbo.zhang, khilman,
ulf.hansson, linus.walleij
On Thursday, March 28, 2013 05:11:29 PM Rickard Andersson wrote:
> 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>
> ---
> I believe notifiers are the best solution to the problem of
> restoring context early when waking up after sleep.
I don't.
> It is similar to what is done in /kernel/cpu_pm.c
Which is causing tons of problems to happen.
> but but for a generic power domain instead.
Which has a potential of being even worse.
> An alternative solution could be to try to call the drivers
> runtime_resume/runtime_suspend callbacks instead via genpd
> from cpuidle or suspend/resume. I do not recommend that
> solution for the following reasons:
>
> * Altered use of runtime PM. As it is today the driver itself
> controls when to runtime-suspend/runtime-resume.
That's not correct.
> * Runtime PM is disabled during late stages of suspend.
> * Complicates runtime PM for a few special cases.
Well, quite frankly, it is hard to say what problem this is supposed to
address from your description. Care to give some more details?
Rafael
> ---
> 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)
> {
>
--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH V2 03/12] PM / Domains: Add on-off notifiers
2013-03-30 23:17 ` Rafael J. Wysocki
@ 2013-04-23 12:26 ` Rickard Andersson
2013-04-29 12:14 ` Linus Walleij
0 siblings, 1 reply; 17+ messages in thread
From: Rickard Andersson @ 2013-04-23 12:26 UTC (permalink / raw)
To: Rafael J. Wysocki
Cc: linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
daniel.lezcano@linaro.org, hongbo.zhang@linaro.org,
khilman@linaro.org, ulf.hansson@linaro.org, Linus WALLEIJ
On 03/31/2013 12:17 AM, Rafael J. Wysocki wrote:
> On Thursday, March 28, 2013 05:11:29 PM Rickard Andersson wrote:
>> 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>
>> ---
>> I believe notifiers are the best solution to the problem of
>> restoring context early when waking up after sleep.
> I don't.
Some motivation would be good.
>> It is similar to what is done in /kernel/cpu_pm.c
> Which is causing tons of problems to happen.
What type of problems have you seen with the notifiers in
"/kernel/cpu_pm.c"?
>> but but for a generic power domain instead.
> Which has a potential of being even worse.
>
>> An alternative solution could be to try to call the drivers
>> runtime_resume/runtime_suspend callbacks instead via genpd
>> from cpuidle or suspend/resume. I do not recommend that
>> solution for the following reasons:
>>
>> * Altered use of runtime PM. As it is today the driver itself
>> controls when to runtime-suspend/runtime-resume.
> That's not correct.
>
>> * Runtime PM is disabled during late stages of suspend.
>> * Complicates runtime PM for a few special cases.
> Well, quite frankly, it is hard to say what problem this is supposed to
> address from your description. Care to give some more details?
>
> Rafael
The problem is that some hardware blocks needs to be restored as early
as possible after leaving the U8500 CPU coupled sleep state ApSleep (APE
power domain being off). This is comparable to CPU needing early restore
using "/kernel/cpu_pm.c" but for the APE power domain instead.
One could argue that one should save/restore the context directly in the
cpuidle driver instead but that means duplicated code since hardware
block registers will be modified both in the block driver and in the
cpuidle driver. With the notifiers solution the cpuidle driver does not
need to be aware of which hardware blocks that is being included in the
APE power domain (needing early context restore), it is the same for
system suspend/resume. Cpuidle and system suspend/resume should use the
function pm_genpd_notify_power_on_off(..) to tell the system that it
will change/has changed the state of a certain power domain.
Note that only a few of the hardware blocks in the APE power domain
needs to use these notifiers, most drivers will do fine using pm_runtime.
If you for example look at patch "[PATCH V2 06/12] clk: ux500: Add PRCC
power management" you can see that all PRCC clock registers are handled
in the clock driver. There is no need to put duplicated code handling
clocks context in "/arch/arm/mach-ux500/" for suspend/resume and
cpuidle. Clock settings should be set directly as soon as possible after
leaving the sleep state ApSleep, that is why the notifiers are being used.
If you do not think this mechanism is a way forward then please advice
how you think is should be solved.
Thanks
Rickard
>
>> ---
>> 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)
>> {
>>
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH V2 03/12] PM / Domains: Add on-off notifiers
2013-04-23 12:26 ` Rickard Andersson
@ 2013-04-29 12:14 ` Linus Walleij
2013-04-30 12:55 ` Rickard Andersson
0 siblings, 1 reply; 17+ messages in thread
From: Linus Walleij @ 2013-04-29 12:14 UTC (permalink / raw)
To: Rickard Andersson
Cc: Rafael J. Wysocki, linux-pm@vger.kernel.org,
linux-arm-kernel@lists.infradead.org, daniel.lezcano@linaro.org,
hongbo.zhang@linaro.org, khilman@linaro.org,
ulf.hansson@linaro.org, Linus WALLEIJ
On Tue, Apr 23, 2013 at 2:26 PM, Rickard Andersson
<rickard.andersson@stericsson.com> wrote:
> On 03/31/2013 12:17 AM, Rafael J. Wysocki wrote:
>> On Thursday, March 28, 2013 05:11:29 PM Rickard Andersson wrote:
>>> I believe notifiers are the best solution to the problem of
>>> restoring context early when waking up after sleep.
>>
>> I don't.
>
> Some motivation would be good.
(...)
>> Well, quite frankly, it is hard to say what problem this is supposed to
>> address from your description. Care to give some more details?
>
> The problem is that some hardware blocks needs to be restored as early as
> possible after leaving the U8500 CPU coupled sleep state ApSleep (APE power
> domain being off).
(APE = Application Processor Environment, on embedded)
I just have this vague feeling that this is one of those embedded SoC
vs desktop things where worlds collide.
Isn't it so that on common x86 (desktop, server, laptop) the context of
the system is saved by some BIOS/ACPI call, and then the ROM
takes you back up with all devices put back into context?
On embedded SoC's we don't have this, all states in the hardware
has to be saved and restored with kernel code.
Then there is a question about whether this shall be done centrally
(there is one piece of code that knows all volatile registers of all
hardware blocks in the system) which is close to what the desktop
BIOS may do, or whether you want to do this in a distributed manner,
so that each driver saves and restores the context of it's particular
hardware.
The suggested mechanism is useful for the latter design pattern,
where drivers know for themselves how to save and restore their
contexts.
Yours,
Linus Walleij
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH V2 03/12] PM / Domains: Add on-off notifiers
2013-04-29 12:14 ` Linus Walleij
@ 2013-04-30 12:55 ` Rickard Andersson
0 siblings, 0 replies; 17+ messages in thread
From: Rickard Andersson @ 2013-04-30 12:55 UTC (permalink / raw)
To: Linus Walleij, Rafael J. Wysocki
Cc: linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
daniel.lezcano@linaro.org, hongbo.zhang@linaro.org,
khilman@linaro.org, ulf.hansson@linaro.org, Linus WALLEIJ
On 04/29/2013 02:14 PM, Linus Walleij wrote:
> On Tue, Apr 23, 2013 at 2:26 PM, Rickard Andersson
> <rickard.andersson@stericsson.com> wrote:
>> On 03/31/2013 12:17 AM, Rafael J. Wysocki wrote:
>>> On Thursday, March 28, 2013 05:11:29 PM Rickard Andersson wrote:
>>>> I believe notifiers are the best solution to the problem of
>>>> restoring context early when waking up after sleep.
>>> I don't.
>> Some motivation would be good.
> (...)
>>> Well, quite frankly, it is hard to say what problem this is supposed to
>>> address from your description. Care to give some more details?
>> The problem is that some hardware blocks needs to be restored as early as
>> possible after leaving the U8500 CPU coupled sleep state ApSleep (APE power
>> domain being off).
> (APE = Application Processor Environment, on embedded)
>
> I just have this vague feeling that this is one of those embedded SoC
> vs desktop things where worlds collide.
>
> Isn't it so that on common x86 (desktop, server, laptop) the context of
> the system is saved by some BIOS/ACPI call, and then the ROM
> takes you back up with all devices put back into context?
>
> On embedded SoC's we don't have this, all states in the hardware
> has to be saved and restored with kernel code.
>
> Then there is a question about whether this shall be done centrally
> (there is one piece of code that knows all volatile registers of all
> hardware blocks in the system) which is close to what the desktop
> BIOS may do, or whether you want to do this in a distributed manner,
> so that each driver saves and restores the context of it's particular
> hardware.
>
The advantage with using the distributed approach is that all knowledge
of a certain hardware block can be handled in one place, the driver.
Having all knowledge in one place is a standard software design pattern.
Most drivers in the APE domain will be fine using runtime pm, but a few
needs their context (i.e. registers) to be restored directly when
leaving a sleep state where the APE power domain has been off. For
example the following hardware blocks: APE clocks, ICN (main
interconnect bus) and TPIU (Trace Port Interface Unit).
Using the TPIU block as an example: tracing with debugger (using TPIU
block) needs to be active (context restored) directly after sleep in
order to be able to record the execution also after waking up from
sleep. If the TPIU context is not restored after waking up from sleep
then the debuggers recording of the execution will also stop after
waking up from sleep which is not a good thing when looking for
difficult errors using a debugger.
Rafael: Can you please share your view?
Thanks in advance!
/Rickard
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH V2 04/12] PM / Domains: Lookup domain by name
2013-03-28 16:11 [PATCH V2 00/12] ux500 suspend-resume Rickard Andersson
` (2 preceding siblings ...)
2013-03-28 16:11 ` [PATCH V2 03/12] PM / Domains: Add on-off notifiers Rickard Andersson
@ 2013-03-28 16:11 ` Rickard Andersson
2013-03-28 16:11 ` [PATCH V2 05/12] ARM: ux500: Create APE generic power domain Rickard Andersson
` (7 subsequent siblings)
11 siblings, 0 replies; 17+ messages in thread
From: Rickard Andersson @ 2013-03-28 16:11 UTC (permalink / raw)
To: rjw, linux-pm, linux-arm-kernel
Cc: hongbo.zhang, ulf.hansson, khilman, linus.walleij, daniel.lezcano,
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] 17+ messages in thread* [PATCH V2 05/12] ARM: ux500: Create APE generic power domain
2013-03-28 16:11 [PATCH V2 00/12] ux500 suspend-resume Rickard Andersson
` (3 preceding siblings ...)
2013-03-28 16:11 ` [PATCH V2 04/12] PM / Domains: Lookup domain by name Rickard Andersson
@ 2013-03-28 16:11 ` Rickard Andersson
2013-03-28 16:11 ` [PATCH V2 06/12] clk: ux500: Add PRCC power management Rickard Andersson
` (6 subsequent siblings)
11 siblings, 0 replies; 17+ messages in thread
From: Rickard Andersson @ 2013-03-28 16:11 UTC (permalink / raw)
To: rjw, linux-pm, linux-arm-kernel
Cc: hongbo.zhang, ulf.hansson, khilman, linus.walleij, daniel.lezcano,
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] 17+ messages in thread* [PATCH V2 06/12] clk: ux500: Add PRCC power management
2013-03-28 16:11 [PATCH V2 00/12] ux500 suspend-resume Rickard Andersson
` (4 preceding siblings ...)
2013-03-28 16:11 ` [PATCH V2 05/12] ARM: ux500: Create APE generic power domain Rickard Andersson
@ 2013-03-28 16:11 ` Rickard Andersson
2013-03-28 16:11 ` [PATCH V2 07/12] ARM: ux500: Create u8500-clk device Rickard Andersson
` (5 subsequent siblings)
11 siblings, 0 replies; 17+ messages in thread
From: Rickard Andersson @ 2013-03-28 16:11 UTC (permalink / raw)
To: rjw, linux-pm, linux-arm-kernel
Cc: hongbo.zhang, ulf.hansson, khilman, linus.walleij, daniel.lezcano,
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 | 71 ++++++++++++++++++++++++++++++++++++-
drivers/clk/ux500/clk.h | 5 +++
drivers/clk/ux500/u8500_clk.c | 82 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 157 insertions(+), 1 deletion(-)
diff --git a/drivers/clk/ux500/clk-prcc.c b/drivers/clk/ux500/clk-prcc.c
index 7eee7f7..01739f7 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
*/
@@ -13,10 +14,13 @@
#include <linux/io.h>
#include <linux/err.h>
#include <linux/types.h>
+#include <linux/platform_device.h>
#include <mach/hardware.h>
#include "clk.h"
+#define UX500_NR_PRCC_BANKS 5
+
#define PRCC_PCKEN 0x000
#define PRCC_PCKDIS 0x004
#define PRCC_KCKEN 0x008
@@ -33,6 +37,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 +172,62 @@ 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(struct platform_device *pdev)
+{
+ int i;
+ struct resource *res;
+ int ret = 0;
+
+ for (i = 0; i < UX500_NR_PRCC_BANKS; i++) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+ if (!res) {
+ ret = -EINVAL;
+ goto err_iounmap;
+ }
+
+ context_prcc[i].base = ioremap(res->start, resource_size(res));
+ if (!context_prcc[i].base) {
+ ret = -ENOMEM;
+ goto err_iounmap;
+ }
+ }
+
+ return ret;
+
+ err_iounmap:
+ for (i = 0; i < UX500_NR_PRCC_BANKS; i++) {
+ if (context_prcc[i].base)
+ iounmap(context_prcc[i].base);
+ }
+ return ret;
+}
diff --git a/drivers/clk/ux500/clk.h b/drivers/clk/ux500/clk.h
index c3e4491..3394fb8 100644
--- a/drivers/clk/ux500/clk.h
+++ b/drivers/clk/ux500/clk.h
@@ -11,6 +11,7 @@
#define __UX500_CLK_H
#include <linux/clk.h>
+#include <linux/platform_device.h>
struct clk *clk_reg_prcc_pclk(const char *name,
const char *parent_name,
@@ -57,4 +58,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(struct platform_device *pdev);
+
#endif /* __UX500_CLK_H */
diff --git a/drivers/clk/ux500/u8500_clk.c b/drivers/clk/ux500/u8500_clk.c
index 6b889a0..63ed580 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(pdev);
+ 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] 17+ messages in thread* [PATCH V2 07/12] ARM: ux500: Create u8500-clk device
2013-03-28 16:11 [PATCH V2 00/12] ux500 suspend-resume Rickard Andersson
` (5 preceding siblings ...)
2013-03-28 16:11 ` [PATCH V2 06/12] clk: ux500: Add PRCC power management Rickard Andersson
@ 2013-03-28 16:11 ` Rickard Andersson
2013-03-28 16:11 ` [PATCH V2 08/12] ARM: ux500: Add ApSleep state to suspend Rickard Andersson
` (4 subsequent siblings)
11 siblings, 0 replies; 17+ messages in thread
From: Rickard Andersson @ 2013-03-28 16:11 UTC (permalink / raw)
To: rjw, linux-pm, linux-arm-kernel
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/boot/dts/dbx5x0.dtsi | 9 ++++++++
arch/arm/mach-ux500/board-mop500.c | 8 +++++++
arch/arm/mach-ux500/cpu-db8500.c | 1 +
arch/arm/mach-ux500/devices-db8500.c | 41 ++++++++++++++++++++++++++++++++++++
arch/arm/mach-ux500/devices-db8500.h | 1 +
drivers/clk/ux500/u8500_clk.c | 6 ++++++
6 files changed, 66 insertions(+)
diff --git a/arch/arm/boot/dts/dbx5x0.dtsi b/arch/arm/boot/dts/dbx5x0.dtsi
index 63f2fbc..b1438d2 100644
--- a/arch/arm/boot/dts/dbx5x0.dtsi
+++ b/arch/arm/boot/dts/dbx5x0.dtsi
@@ -657,6 +657,15 @@
status = "disabled";
};
+ clk {
+ compatible = "stericsson,u8500-clk";
+ reg = <0x8012f000 0x1000>,
+ <0x8011f000 0x1000>,
+ <0x8000f000 0x1000>,
+ <0xa03ff000 0x1000>,
+ <0xa03cf000 0x1000>;
+ };
+
external-bus@50000000 {
compatible = "simple-bus";
reg = <0x50000000 0x4000000>;
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..ad6036b 100644
--- a/arch/arm/mach-ux500/devices-db8500.c
+++ b/arch/arm/mach-ux500/devices-db8500.c
@@ -194,3 +194,44 @@ struct platform_device u8500_ske_keypad_device = {
.num_resources = ARRAY_SIZE(keypad_resources),
.resource = keypad_resources,
};
+
+static struct resource clk_resources[] = {
+ [0] = {
+ .start = U8500_CLKRST1_BASE,
+ .end = U8500_CLKRST1_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ .name = "clkrst1_base",
+ },
+ [1] = {
+ .start = U8500_CLKRST2_BASE,
+ .end = U8500_CLKRST2_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ .name = "clkrst2_base",
+ },
+ [2] = {
+ .start = U8500_CLKRST3_BASE,
+ .end = U8500_CLKRST3_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ .name = "clkrst3_base",
+ },
+ [3] = {
+ .start = U8500_CLKRST5_BASE,
+ .end = U8500_CLKRST5_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ .name = "clkrst5_base",
+ },
+ [4] = {
+ .start = U8500_CLKRST6_BASE,
+ .end = U8500_CLKRST6_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ .name = "clkrst6_base",
+ },
+};
+
+struct platform_device db8500_clk_device = {
+ .name = "u8500-clk",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(clk_resources),
+ .resource = clk_resources
+
+};
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,
diff --git a/drivers/clk/ux500/u8500_clk.c b/drivers/clk/ux500/u8500_clk.c
index 63ed580..8a3ac27 100644
--- a/drivers/clk/ux500/u8500_clk.c
+++ b/drivers/clk/ux500/u8500_clk.c
@@ -566,9 +566,15 @@ static struct notifier_block prcc_context_notifier = {
.notifier_call = prcc_context_call,
};
+static const struct of_device_id u8500_clk_match[] = {
+ { .compatible = "stericsson,u8500-clk", },
+ {},
+};
+
static struct platform_driver u8500_clk_plat_driver = {
.driver = {
.name = "u8500-clk",
+ .of_match_table = u8500_clk_match,
},
};
--
1.8.2
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH V2 08/12] ARM: ux500: Add ApSleep state to suspend
2013-03-28 16:11 [PATCH V2 00/12] ux500 suspend-resume Rickard Andersson
` (6 preceding siblings ...)
2013-03-28 16:11 ` [PATCH V2 07/12] ARM: ux500: Create u8500-clk device Rickard Andersson
@ 2013-03-28 16:11 ` Rickard Andersson
2013-03-28 16:11 ` [PATCH V2 09/12] drivers: bus: ux500: Add ICN driver Rickard Andersson
` (3 subsequent siblings)
11 siblings, 0 replies; 17+ messages in thread
From: Rickard Andersson @ 2013-03-28 16:11 UTC (permalink / raw)
To: rjw, linux-pm, linux-arm-kernel
Cc: hongbo.zhang, ulf.hansson, khilman, linus.walleij, daniel.lezcano,
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] 17+ messages in thread* [PATCH V2 09/12] drivers: bus: ux500: Add ICN driver
2013-03-28 16:11 [PATCH V2 00/12] ux500 suspend-resume Rickard Andersson
` (7 preceding siblings ...)
2013-03-28 16:11 ` [PATCH V2 08/12] ARM: ux500: Add ApSleep state to suspend Rickard Andersson
@ 2013-03-28 16:11 ` Rickard Andersson
2013-03-28 16:11 ` [PATCH V2 10/12] ARM: ux500: Create ICN device Rickard Andersson
` (2 subsequent siblings)
11 siblings, 0 replies; 17+ messages in thread
From: Rickard Andersson @ 2013-03-28 16:11 UTC (permalink / raw)
To: rjw, linux-pm, linux-arm-kernel
Cc: hongbo.zhang, ulf.hansson, khilman, linus.walleij, daniel.lezcano,
rickard.andersson
Add driver for the U8500 main interconnect bus. The
settings of these hardware registers are lost when
the APE power domain is powered off.
The ICN default settings are done during early boot.
Signed-off-by: Rickard Andersson <rickard.andersson@stericsson.com>
---
drivers/bus/dbx500-icn.c | 522 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 522 insertions(+)
create mode 100644 drivers/bus/dbx500-icn.c
diff --git a/drivers/bus/dbx500-icn.c b/drivers/bus/dbx500-icn.c
new file mode 100644
index 0000000..5173650
--- /dev/null
+++ b/drivers/bus/dbx500-icn.c
@@ -0,0 +1,522 @@
+/*
+ * U8500 interconnect driver
+ *
+ * Copyright (C) STMicroelectronics 2009
+ * Copyright (C) ST-Ericsson SA 2010-2013
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Sundar Iyer for ST-Ericsson
+ * Rickard Andersson <rickard.andersson@stericsson.com>
+ *
+ */
+
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <mach/hardware.h>
+
+/* Priority, BW limiter register offsets */
+#define NODE_HIBW1_ESRAM_IN_0_PRIORITY 0x00
+#define NODE_HIBW1_ESRAM_IN_1_PRIORITY 0x04
+#define NODE_HIBW1_ESRAM_IN_2_PRIORITY 0x08
+#define NODE_HIBW1_ESRAM_IN_0_ARB_1_LIMIT 0x24
+#define NODE_HIBW1_ESRAM_IN_0_ARB_2_LIMIT 0x28
+#define NODE_HIBW1_ESRAM_IN_0_ARB_3_LIMIT 0x2C
+#define NODE_HIBW1_ESRAM_IN_1_ARB_1_LIMIT 0x30
+#define NODE_HIBW1_ESRAM_IN_1_ARB_2_LIMIT 0x34
+#define NODE_HIBW1_ESRAM_IN_1_ARB_3_LIMIT 0x38
+#define NODE_HIBW1_ESRAM_IN_2_ARB_1_LIMIT 0x3C
+#define NODE_HIBW1_ESRAM_IN_2_ARB_2_LIMIT 0x40
+#define NODE_HIBW1_ESRAM_IN_2_ARB_3_LIMIT 0x44
+#define NODE_HIBW1_DDR_IN_0_PRIORITY 0x400
+#define NODE_HIBW1_DDR_IN_1_PRIORITY 0x404
+#define NODE_HIBW1_DDR_IN_2_PRIORITY 0x408
+#define NODE_HIBW1_DDR_IN_0_LIMIT 0x424
+#define NODE_HIBW1_DDR_IN_1_LIMIT 0x428
+#define NODE_HIBW1_DDR_IN_2_LIMIT 0x42C
+#define NODE_HIBW1_DDR_OUT_0_PRIORITY 0x430
+#define NODE_HIBW2_ESRAM_IN_0_PRIORITY 0x800
+#define NODE_HIBW2_ESRAM_IN_1_PRIORITY 0x804
+#define NODE_HIBW2_ESRAM_IN_0_ARB_1_LIMIT 0x818
+#define NODE_HIBW2_ESRAM_IN_0_ARB_2_LIMIT 0x81C
+#define NODE_HIBW2_ESRAM_IN_0_ARB_3_LIMIT 0x820
+#define NODE_HIBW2_ESRAM_IN_1_ARB_1_LIMIT 0x824
+#define NODE_HIBW2_ESRAM_IN_1_ARB_2_LIMIT 0x828
+#define NODE_HIBW2_ESRAM_IN_1_ARB_3_LIMIT 0x82C
+#define NODE_HIBW2_DDR_IN_0_PRIORITY 0xC00
+#define NODE_HIBW2_DDR_IN_1_PRIORITY 0xC04
+#define NODE_HIBW2_DDR_IN_2_PRIORITY 0xC08
+
+#define NODE_HIBW2_DDR_IN_0_LIMIT 0xC24
+#define NODE_HIBW2_DDR_IN_1_LIMIT 0xC28
+#define NODE_HIBW2_DDR_IN_2_LIMIT 0xC2C
+#define NODE_HIBW2_DDR_OUT_0_PRIORITY 0xC30
+
+/*
+ * Note the following addresses are presented in
+ * db8500 design spec v3.1 and v3.3, table 10.
+ * But their addresses are not the same as in the
+ * description. The addresses in the description
+ * of each registers are correct.
+ * NODE_HIBW2_DDR_IN_3_LIMIT is only present in v1.
+ *
+ * Faulty registers addresses in table 10:
+ * NODE_HIBW2_DDR_IN_2_LIMIT 0xC38
+ * NODE_HIBW2_DDR_IN_3_LIMIT 0xC3C
+ * NODE_HIBW2_DDR_OUT_0_PRIORITY 0xC40
+ */
+
+#define NODE_ESRAM0_IN_0_PRIORITY 0x1000
+#define NODE_ESRAM0_IN_1_PRIORITY 0x1004
+#define NODE_ESRAM0_IN_2_PRIORITY 0x1008
+#define NODE_ESRAM0_IN_3_PRIORITY 0x100C
+#define NODE_ESRAM0_IN_0_LIMIT 0x1030
+#define NODE_ESRAM0_IN_1_LIMIT 0x1034
+#define NODE_ESRAM0_IN_2_LIMIT 0x1038
+#define NODE_ESRAM0_IN_3_LIMIT 0x103C
+/* Common */
+#define NODE_ESRAM1_2_IN_0_PRIORITY 0x1400
+#define NODE_ESRAM1_2_IN_1_PRIORITY 0x1404
+#define NODE_ESRAM1_2_IN_2_PRIORITY 0x1408
+#define NODE_ESRAM1_2_IN_3_PRIORITY 0x140C
+#define NODE_ESRAM1_2_IN_0_ARB_1_LIMIT 0x1430
+#define NODE_ESRAM1_2_IN_0_ARB_2_LIMIT 0x1434
+#define NODE_ESRAM1_2_IN_1_ARB_1_LIMIT 0x1438
+#define NODE_ESRAM1_2_IN_1_ARB_2_LIMIT 0x143C
+#define NODE_ESRAM1_2_IN_2_ARB_1_LIMIT 0x1440
+#define NODE_ESRAM1_2_IN_2_ARB_2_LIMIT 0x1444
+#define NODE_ESRAM1_2_IN_3_ARB_1_LIMIT 0x1448
+#define NODE_ESRAM1_2_IN_3_ARB_2_LIMIT 0x144C
+
+#define NODE_ESRAM3_4_IN_0_PRIORITY 0x1800
+#define NODE_ESRAM3_4_IN_1_PRIORITY 0x1804
+#define NODE_ESRAM3_4_IN_2_PRIORITY 0x1808
+#define NODE_ESRAM3_4_IN_3_PRIORITY 0x180C
+#define NODE_ESRAM3_4_IN_0_ARB_1_LIMIT 0x1830
+#define NODE_ESRAM3_4_IN_0_ARB_2_LIMIT 0x1834
+#define NODE_ESRAM3_4_IN_1_ARB_1_LIMIT 0x1838
+#define NODE_ESRAM3_4_IN_1_ARB_2_LIMIT 0x183C
+#define NODE_ESRAM3_4_IN_2_ARB_1_LIMIT 0x1840
+#define NODE_ESRAM3_4_IN_2_ARB_2_LIMIT 0x1844
+#define NODE_ESRAM3_4_IN_3_ARB_1_LIMIT 0x1848
+#define NODE_ESRAM3_4_IN_3_ARB_2_LIMIT 0x184C
+
+static struct {
+ void __iomem *base;
+ u32 hibw1_esram_in_pri[3];
+ u32 hibw1_esram_in0_arb[3];
+ u32 hibw1_esram_in1_arb[3];
+ u32 hibw1_esram_in2_arb[3];
+ u32 hibw1_ddr_in_prio[3];
+ u32 hibw1_ddr_in_limit[3];
+ u32 hibw1_ddr_out_prio;
+
+ /* HiBw2 node registers */
+ u32 hibw2_esram_in_pri[2];
+ u32 hibw2_esram_in0_arblimit[3];
+ u32 hibw2_esram_in1_arblimit[3];
+ u32 hibw2_ddr_in_prio[4];
+ u32 hibw2_ddr_in_limit[4];
+ u32 hibw2_ddr_out_prio;
+
+ /* ESRAM node registers */
+ u32 esram_in_prio[4];
+ u32 esram_in_lim[4];
+ u32 esram0_in_prio[4];
+ u32 esram0_in_lim[4];
+ u32 esram12_in_prio[4];
+ u32 esram12_in_arb_lim[8];
+ u32 esram34_in_prio[4];
+ u32 esram34_in_arb_lim[8];
+} context_icn;
+
+/**
+ * icn_save_context() - Save interconnect context
+ */
+static void icn_save_context(void)
+{
+ void __iomem *b = context_icn.base;
+
+ context_icn.hibw1_esram_in_pri[0] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_0_PRIORITY);
+ context_icn.hibw1_esram_in_pri[1] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_1_PRIORITY);
+ context_icn.hibw1_esram_in_pri[2] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_2_PRIORITY);
+
+ context_icn.hibw1_esram_in0_arb[0] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_0_ARB_1_LIMIT);
+ context_icn.hibw1_esram_in0_arb[1] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_0_ARB_2_LIMIT);
+ context_icn.hibw1_esram_in0_arb[2] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_0_ARB_3_LIMIT);
+
+ context_icn.hibw1_esram_in1_arb[0] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_1_ARB_1_LIMIT);
+ context_icn.hibw1_esram_in1_arb[1] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_1_ARB_2_LIMIT);
+ context_icn.hibw1_esram_in1_arb[2] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_1_ARB_3_LIMIT);
+
+ context_icn.hibw1_esram_in2_arb[0] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_2_ARB_1_LIMIT);
+ context_icn.hibw1_esram_in2_arb[1] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_2_ARB_2_LIMIT);
+ context_icn.hibw1_esram_in2_arb[2] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_2_ARB_3_LIMIT);
+
+ context_icn.hibw1_ddr_in_prio[0] =
+ readl_relaxed(b + NODE_HIBW1_DDR_IN_0_PRIORITY);
+ context_icn.hibw1_ddr_in_prio[1] =
+ readl_relaxed(b + NODE_HIBW1_DDR_IN_1_PRIORITY);
+ context_icn.hibw1_ddr_in_prio[2] =
+ readl_relaxed(b + NODE_HIBW1_DDR_IN_2_PRIORITY);
+
+ context_icn.hibw1_ddr_in_limit[0] =
+ readl_relaxed(b + NODE_HIBW1_DDR_IN_0_LIMIT);
+ context_icn.hibw1_ddr_in_limit[1] =
+ readl_relaxed(b + NODE_HIBW1_DDR_IN_1_LIMIT);
+ context_icn.hibw1_ddr_in_limit[2] =
+ readl_relaxed(b + NODE_HIBW1_DDR_IN_2_LIMIT);
+
+ context_icn.hibw1_ddr_out_prio =
+ readl_relaxed(b + NODE_HIBW1_DDR_OUT_0_PRIORITY);
+
+ context_icn.hibw2_esram_in_pri[0] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_0_PRIORITY);
+ context_icn.hibw2_esram_in_pri[1] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_1_PRIORITY);
+
+ context_icn.hibw2_esram_in0_arblimit[0] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_0_ARB_1_LIMIT);
+ context_icn.hibw2_esram_in0_arblimit[1] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_0_ARB_2_LIMIT);
+ context_icn.hibw2_esram_in0_arblimit[2] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_0_ARB_3_LIMIT);
+
+ context_icn.hibw2_esram_in1_arblimit[0] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_1_ARB_1_LIMIT);
+ context_icn.hibw2_esram_in1_arblimit[1] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_1_ARB_2_LIMIT);
+ context_icn.hibw2_esram_in1_arblimit[2] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_1_ARB_3_LIMIT);
+
+ context_icn.hibw2_ddr_in_prio[0] =
+ readl_relaxed(b + NODE_HIBW2_DDR_IN_0_PRIORITY);
+ context_icn.hibw2_ddr_in_prio[1] =
+ readl_relaxed(b + NODE_HIBW2_DDR_IN_1_PRIORITY);
+ context_icn.hibw2_ddr_in_prio[2] =
+ readl_relaxed(b + NODE_HIBW2_DDR_IN_2_PRIORITY);
+
+ context_icn.hibw2_ddr_in_limit[0] =
+ readl_relaxed(b + NODE_HIBW2_DDR_IN_0_LIMIT);
+ context_icn.hibw2_ddr_in_limit[1] =
+ readl_relaxed(b + NODE_HIBW2_DDR_IN_1_LIMIT);
+
+ context_icn.hibw2_ddr_in_limit[2] =
+ readl_relaxed(b + NODE_HIBW2_DDR_IN_2_LIMIT);
+
+ context_icn.hibw2_ddr_out_prio =
+ readl_relaxed(b + NODE_HIBW2_DDR_OUT_0_PRIORITY);
+
+ context_icn.esram0_in_prio[0] =
+ readl_relaxed(b + NODE_ESRAM0_IN_0_PRIORITY);
+ context_icn.esram0_in_prio[1] =
+ readl_relaxed(b + NODE_ESRAM0_IN_1_PRIORITY);
+ context_icn.esram0_in_prio[2] =
+ readl_relaxed(b + NODE_ESRAM0_IN_2_PRIORITY);
+ context_icn.esram0_in_prio[3] =
+ readl_relaxed(b + NODE_ESRAM0_IN_3_PRIORITY);
+
+ context_icn.esram0_in_lim[0] =
+ readl_relaxed(b + NODE_ESRAM0_IN_0_LIMIT);
+ context_icn.esram0_in_lim[1] =
+ readl_relaxed(b + NODE_ESRAM0_IN_1_LIMIT);
+ context_icn.esram0_in_lim[2] =
+ readl_relaxed(b + NODE_ESRAM0_IN_2_LIMIT);
+ context_icn.esram0_in_lim[3] =
+ readl_relaxed(b + NODE_ESRAM0_IN_3_LIMIT);
+
+ context_icn.esram12_in_prio[0] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_0_PRIORITY);
+ context_icn.esram12_in_prio[1] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_1_PRIORITY);
+ context_icn.esram12_in_prio[2] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_2_PRIORITY);
+ context_icn.esram12_in_prio[3] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_3_PRIORITY);
+
+ context_icn.esram12_in_arb_lim[0] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_0_ARB_1_LIMIT);
+ context_icn.esram12_in_arb_lim[1] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_0_ARB_2_LIMIT);
+ context_icn.esram12_in_arb_lim[2] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_1_ARB_1_LIMIT);
+ context_icn.esram12_in_arb_lim[3] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_1_ARB_2_LIMIT);
+ context_icn.esram12_in_arb_lim[4] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_2_ARB_1_LIMIT);
+ context_icn.esram12_in_arb_lim[5] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_2_ARB_2_LIMIT);
+ context_icn.esram12_in_arb_lim[6] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_3_ARB_1_LIMIT);
+ context_icn.esram12_in_arb_lim[7] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_3_ARB_2_LIMIT);
+
+ context_icn.esram34_in_prio[0] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_0_PRIORITY);
+ context_icn.esram34_in_prio[1] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_1_PRIORITY);
+ context_icn.esram34_in_prio[2] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_2_PRIORITY);
+ context_icn.esram34_in_prio[3] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_3_PRIORITY);
+
+ context_icn.esram34_in_arb_lim[0] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_0_ARB_1_LIMIT);
+ context_icn.esram34_in_arb_lim[1] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_0_ARB_2_LIMIT);
+ context_icn.esram34_in_arb_lim[2] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_1_ARB_1_LIMIT);
+ context_icn.esram34_in_arb_lim[3] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_1_ARB_2_LIMIT);
+ context_icn.esram34_in_arb_lim[4] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_2_ARB_1_LIMIT);
+ context_icn.esram34_in_arb_lim[5] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_2_ARB_2_LIMIT);
+ context_icn.esram34_in_arb_lim[6] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_3_ARB_1_LIMIT);
+ context_icn.esram34_in_arb_lim[7] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_3_ARB_2_LIMIT);
+}
+
+/**
+ * icn_restore_context() - Restore interconnect context
+ */
+static void icn_restore_context(void)
+{
+ void __iomem *b = context_icn.base;
+
+ writel_relaxed(context_icn.hibw1_esram_in_pri[0],
+ b + NODE_HIBW1_ESRAM_IN_0_PRIORITY);
+ writel_relaxed(context_icn.hibw1_esram_in_pri[1],
+ b + NODE_HIBW1_ESRAM_IN_1_PRIORITY);
+ writel_relaxed(context_icn.hibw1_esram_in_pri[2],
+ b + NODE_HIBW1_ESRAM_IN_2_PRIORITY);
+
+ writel_relaxed(context_icn.hibw1_esram_in0_arb[0],
+ b + NODE_HIBW1_ESRAM_IN_0_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in0_arb[1],
+ b + NODE_HIBW1_ESRAM_IN_0_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in0_arb[2],
+ b + NODE_HIBW1_ESRAM_IN_0_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw1_esram_in1_arb[0],
+ b + NODE_HIBW1_ESRAM_IN_1_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in1_arb[1],
+ b + NODE_HIBW1_ESRAM_IN_1_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in1_arb[2],
+ b + NODE_HIBW1_ESRAM_IN_1_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw1_esram_in2_arb[0],
+ b + NODE_HIBW1_ESRAM_IN_2_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in2_arb[1],
+ b + NODE_HIBW1_ESRAM_IN_2_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in2_arb[2],
+ b + NODE_HIBW1_ESRAM_IN_2_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw1_ddr_in_prio[0],
+ b + NODE_HIBW1_DDR_IN_0_PRIORITY);
+ writel_relaxed(context_icn.hibw1_ddr_in_prio[1],
+ b + NODE_HIBW1_DDR_IN_1_PRIORITY);
+ writel_relaxed(context_icn.hibw1_ddr_in_prio[2],
+ b + NODE_HIBW1_DDR_IN_2_PRIORITY);
+
+ writel_relaxed(context_icn.hibw1_ddr_in_limit[0],
+ b + NODE_HIBW1_DDR_IN_0_LIMIT);
+ writel_relaxed(context_icn.hibw1_ddr_in_limit[1],
+ b + NODE_HIBW1_DDR_IN_1_LIMIT);
+ writel_relaxed(context_icn.hibw1_ddr_in_limit[2],
+ b + NODE_HIBW1_DDR_IN_2_LIMIT);
+
+ writel_relaxed(context_icn.hibw1_ddr_out_prio,
+ b + NODE_HIBW1_DDR_OUT_0_PRIORITY);
+
+ writel_relaxed(context_icn.hibw2_esram_in_pri[0],
+ b + NODE_HIBW2_ESRAM_IN_0_PRIORITY);
+ writel_relaxed(context_icn.hibw2_esram_in_pri[1],
+ b + NODE_HIBW2_ESRAM_IN_1_PRIORITY);
+
+ writel_relaxed(context_icn.hibw2_esram_in0_arblimit[0],
+ b + NODE_HIBW2_ESRAM_IN_0_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw2_esram_in0_arblimit[1],
+ b + NODE_HIBW2_ESRAM_IN_0_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw2_esram_in0_arblimit[2],
+ b + NODE_HIBW2_ESRAM_IN_0_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw2_esram_in1_arblimit[0],
+ b + NODE_HIBW2_ESRAM_IN_1_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw2_esram_in1_arblimit[1],
+ b + NODE_HIBW2_ESRAM_IN_1_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw2_esram_in1_arblimit[2],
+ b + NODE_HIBW2_ESRAM_IN_1_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw2_ddr_in_prio[0],
+ b + NODE_HIBW2_DDR_IN_0_PRIORITY);
+ writel_relaxed(context_icn.hibw2_ddr_in_prio[1],
+ b + NODE_HIBW2_DDR_IN_1_PRIORITY);
+ writel_relaxed(context_icn.hibw2_ddr_in_prio[2],
+ b + NODE_HIBW2_DDR_IN_2_PRIORITY);
+ writel_relaxed(context_icn.hibw2_ddr_in_limit[0],
+ b + NODE_HIBW2_DDR_IN_0_LIMIT);
+ writel_relaxed(context_icn.hibw2_ddr_in_limit[1],
+ b + NODE_HIBW2_DDR_IN_1_LIMIT);
+ writel_relaxed(context_icn.hibw2_ddr_in_limit[2],
+ b + NODE_HIBW2_DDR_IN_2_LIMIT);
+ writel_relaxed(context_icn.hibw2_ddr_out_prio,
+ b + NODE_HIBW2_DDR_OUT_0_PRIORITY);
+
+ writel_relaxed(context_icn.esram0_in_prio[0],
+ b + NODE_ESRAM0_IN_0_PRIORITY);
+ writel_relaxed(context_icn.esram0_in_prio[1],
+ b + NODE_ESRAM0_IN_1_PRIORITY);
+ writel_relaxed(context_icn.esram0_in_prio[2],
+ b + NODE_ESRAM0_IN_2_PRIORITY);
+ writel_relaxed(context_icn.esram0_in_prio[3],
+ b + NODE_ESRAM0_IN_3_PRIORITY);
+
+ writel_relaxed(context_icn.esram0_in_lim[0],
+ b + NODE_ESRAM0_IN_0_LIMIT);
+ writel_relaxed(context_icn.esram0_in_lim[1],
+ b + NODE_ESRAM0_IN_1_LIMIT);
+ writel_relaxed(context_icn.esram0_in_lim[2],
+ b + NODE_ESRAM0_IN_2_LIMIT);
+ writel_relaxed(context_icn.esram0_in_lim[3],
+ b + NODE_ESRAM0_IN_3_LIMIT);
+
+ writel_relaxed(context_icn.esram12_in_prio[0],
+ b + NODE_ESRAM1_2_IN_0_PRIORITY);
+ writel_relaxed(context_icn.esram12_in_prio[1],
+ b + NODE_ESRAM1_2_IN_1_PRIORITY);
+ writel_relaxed(context_icn.esram12_in_prio[2],
+ b + NODE_ESRAM1_2_IN_2_PRIORITY);
+ writel_relaxed(context_icn.esram12_in_prio[3],
+ b + NODE_ESRAM1_2_IN_3_PRIORITY);
+
+ writel_relaxed(context_icn.esram12_in_arb_lim[0],
+ b + NODE_ESRAM1_2_IN_0_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[1],
+ b + NODE_ESRAM1_2_IN_0_ARB_2_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[2],
+ b + NODE_ESRAM1_2_IN_1_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[3],
+ b + NODE_ESRAM1_2_IN_1_ARB_2_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[4],
+ b + NODE_ESRAM1_2_IN_2_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[5],
+ b + NODE_ESRAM1_2_IN_2_ARB_2_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[6],
+ b + NODE_ESRAM1_2_IN_3_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[7],
+ b + NODE_ESRAM1_2_IN_3_ARB_2_LIMIT);
+
+ writel_relaxed(context_icn.esram34_in_prio[0],
+ b + NODE_ESRAM3_4_IN_0_PRIORITY);
+ writel_relaxed(context_icn.esram34_in_prio[1],
+ b + NODE_ESRAM3_4_IN_1_PRIORITY);
+ writel_relaxed(context_icn.esram34_in_prio[2],
+ b + NODE_ESRAM3_4_IN_2_PRIORITY);
+ writel_relaxed(context_icn.esram34_in_prio[3],
+ b + NODE_ESRAM3_4_IN_3_PRIORITY);
+
+ writel_relaxed(context_icn.esram34_in_arb_lim[0],
+ b + NODE_ESRAM3_4_IN_0_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[1],
+ b + NODE_ESRAM3_4_IN_0_ARB_2_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[2],
+ b + NODE_ESRAM3_4_IN_1_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[3],
+ b + NODE_ESRAM3_4_IN_1_ARB_2_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[4],
+ b + NODE_ESRAM3_4_IN_2_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[5],
+ b + NODE_ESRAM3_4_IN_2_ARB_2_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[6],
+ b + NODE_ESRAM3_4_IN_3_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[7],
+ b + NODE_ESRAM3_4_IN_3_ARB_2_LIMIT);
+}
+
+static int icn_context_call(struct notifier_block *this,
+ unsigned long event, void *data)
+{
+ bool power_on = (bool)event;
+
+ if (power_on)
+ icn_restore_context();
+ else
+ icn_save_context();
+
+ return 0;
+}
+
+static struct notifier_block icn_context_notifier = {
+ .notifier_call = icn_context_call,
+};
+
+static const struct of_device_id dbx500_icn_match[] = {
+ { .compatible = "stericsson,db8500-icn", },
+ {},
+};
+
+static struct platform_driver dbx500_icn_plat_driver = {
+ .driver = {
+ .name = "dbx500-icn",
+ .of_match_table = dbx500_icn_match,
+ },
+};
+
+static int __init dbx500_icn_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "missing platform resources\n");
+ return -EINVAL;
+ }
+
+ if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
+ dev_err(&pdev->dev, "failed to request I/O memory\n");
+ return -EBUSY;
+ }
+
+ context_icn.base = ioremap(res->start, resource_size(res));
+ if (!context_icn.base) {
+ ret = -ENOMEM;
+ goto err_free_mem_region;
+ }
+
+ ret = pm_genpd_register_on_off_notifier(&pdev->dev,
+ &icn_context_notifier);
+ if (ret)
+ goto err_iounmap;
+
+ return ret;
+
+ err_iounmap:
+ iounmap(context_icn.base);
+ err_free_mem_region:
+ release_mem_region(res->start, resource_size(res));
+ return ret;
+
+}
+
+static int __init dbx500_icn_init(void)
+{
+ return platform_driver_probe(&dbx500_icn_plat_driver,
+ dbx500_icn_probe);
+}
+
+arch_initcall(dbx500_icn_init);
--
1.8.2
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH V2 10/12] ARM: ux500: Create ICN device
2013-03-28 16:11 [PATCH V2 00/12] ux500 suspend-resume Rickard Andersson
` (8 preceding siblings ...)
2013-03-28 16:11 ` [PATCH V2 09/12] drivers: bus: ux500: Add ICN driver Rickard Andersson
@ 2013-03-28 16:11 ` Rickard Andersson
2013-03-28 16:11 ` [PATCH V2 11/12] misc: ux500: Add TPIU driver Rickard Andersson
2013-03-28 16:11 ` [PATCH V2 12/12] ARM: ux500: Create TPIU device Rickard Andersson
11 siblings, 0 replies; 17+ messages in thread
From: Rickard Andersson @ 2013-03-28 16:11 UTC (permalink / raw)
To: rjw, linux-pm, linux-arm-kernel
Cc: hongbo.zhang, ulf.hansson, khilman, linus.walleij, daniel.lezcano,
rickard.andersson
Add platform device for dbx500-icn. Also add
the device to the generic power domain APE.
Signed-off-by: Rickard Andersson <rickard.andersson@stericsson.com>
---
arch/arm/boot/dts/dbx5x0.dtsi | 5 +++++
arch/arm/mach-ux500/board-mop500.c | 1 +
arch/arm/mach-ux500/cpu-db8500.c | 1 +
arch/arm/mach-ux500/devices-common.c | 16 ++++++++++++++++
arch/arm/mach-ux500/devices-common.h | 1 +
drivers/bus/Makefile | 1 +
6 files changed, 25 insertions(+)
diff --git a/arch/arm/boot/dts/dbx5x0.dtsi b/arch/arm/boot/dts/dbx5x0.dtsi
index b1438d2..ef98422 100644
--- a/arch/arm/boot/dts/dbx5x0.dtsi
+++ b/arch/arm/boot/dts/dbx5x0.dtsi
@@ -666,6 +666,11 @@
<0xa03cf000 0x1000>;
};
+ icn {
+ compatible = "stericsson,db8500-icn";
+ reg = <0x81000000 0x2000>;
+ };
+
external-bus@50000000 {
compatible = "simple-bus";
reg = <0x50000000 0x4000000>;
diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c
index b351f25..76bace3 100644
--- a/arch/arm/mach-ux500/board-mop500.c
+++ b/arch/arm/mach-ux500/board-mop500.c
@@ -660,6 +660,7 @@ static void __init init_pm_domains(void)
pm_genpd_init(&ape_domain, NULL, false);
add_platform_device_to_genpd(&db8500_clk_device, &ape_domain);
+ add_platform_device_to_genpd(&dbx500_icn_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 6ead606..d849986 100644
--- a/arch/arm/mach-ux500/cpu-db8500.c
+++ b/arch/arm/mach-ux500/cpu-db8500.c
@@ -143,6 +143,7 @@ static struct platform_device db8500_prcmu_device = {
static struct platform_device *platform_devs[] __initdata = {
&db8500_clk_device,
+ &dbx500_icn_device,
&u8500_dma40_device,
&db8500_pmu_device,
&db8500_prcmu_device,
diff --git a/arch/arm/mach-ux500/devices-common.c b/arch/arm/mach-ux500/devices-common.c
index 16b5f71..74c7a76 100644
--- a/arch/arm/mach-ux500/devices-common.c
+++ b/arch/arm/mach-ux500/devices-common.c
@@ -18,6 +18,22 @@
#include "devices-common.h"
+static struct resource icn_resources[] = {
+ [0] = {
+ .start = U8500_ICN_BASE,
+ .end = U8500_ICN_BASE + SZ_8K - 1,
+ .flags = IORESOURCE_MEM,
+ .name = "base",
+ },
+};
+
+struct platform_device dbx500_icn_device = {
+ .name = "dbx500-icn",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(icn_resources),
+ .resource = icn_resources
+};
+
static struct platform_device *
dbx500_add_gpio(struct device *parent, int id, resource_size_t addr, int irq,
struct nmk_gpio_platform_data *pdata)
diff --git a/arch/arm/mach-ux500/devices-common.h b/arch/arm/mach-ux500/devices-common.h
index 96fa4ac..adbf524 100644
--- a/arch/arm/mach-ux500/devices-common.h
+++ b/arch/arm/mach-ux500/devices-common.h
@@ -16,6 +16,7 @@
#include <linux/platform_data/crypto-ux500.h>
struct spi_master_cntlr;
+extern struct platform_device dbx500_icn_device;
static inline struct amba_device *
dbx500_add_msp_spi(struct device *parent, const char *name,
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
index 45d997c..78851c0 100644
--- a/drivers/bus/Makefile
+++ b/drivers/bus/Makefile
@@ -2,6 +2,7 @@
# Makefile for the bus drivers.
#
+obj-$(CONFIG_UX500_SOC_DB8500) += dbx500-icn.o
obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o
# Interconnect bus driver for OMAP SoCs.
--
1.8.2
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH V2 11/12] misc: ux500: Add TPIU driver
2013-03-28 16:11 [PATCH V2 00/12] ux500 suspend-resume Rickard Andersson
` (9 preceding siblings ...)
2013-03-28 16:11 ` [PATCH V2 10/12] ARM: ux500: Create ICN device Rickard Andersson
@ 2013-03-28 16:11 ` Rickard Andersson
2013-03-28 16:11 ` [PATCH V2 12/12] ARM: ux500: Create TPIU device Rickard Andersson
11 siblings, 0 replies; 17+ messages in thread
From: Rickard Andersson @ 2013-03-28 16:11 UTC (permalink / raw)
To: rjw, linux-pm, linux-arm-kernel
Cc: hongbo.zhang, ulf.hansson, khilman, linus.walleij, daniel.lezcano,
rickard.andersson
TPIU is Trace Port Interface Unit. Ux500 needs a TPIU
driver because in Ux500 the TPIU hardware block loses
its settings when the APE power domain is turned off.
Settings needs to be saved before the sleep state ApSleep
or ApDeepSleep is reached and the block needs to be
unlocked and restored when leaving those sleep states.
If this is not done PTM tracing with debugger stops
working after the first sleep where the power domain is
off.
Signed-off-by: Rickard Andersson <rickard.andersson@stericsson.com>
---
drivers/misc/Makefile | 1 +
drivers/misc/dbx500-tpiu.c | 182 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 183 insertions(+)
create mode 100644 drivers/misc/dbx500-tpiu.c
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 2129377..9a774ab 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -49,3 +49,4 @@ obj-y += carma/
obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/
obj-$(CONFIG_INTEL_MEI) += mei/
+obj-$(CONFIG_UX500_SOC_DB8500) += dbx500-tpiu.o
\ No newline at end of file
diff --git a/drivers/misc/dbx500-tpiu.c b/drivers/misc/dbx500-tpiu.c
new file mode 100644
index 0000000..fd07c6f
--- /dev/null
+++ b/drivers/misc/dbx500-tpiu.c
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010-2013
+ * Author: Rickard Andersson <rickard.andersson@stericsson.com>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <mach/hardware.h>
+
+#define TPIU_PORT_SIZE 0x4
+#define TPIU_TRIGGER_COUNTER 0x104
+#define TPIU_TRIGGER_MULTIPLIER 0x108
+#define TPIU_CURRENT_TEST_PATTERN 0x204
+#define TPIU_TEST_PATTERN_REPEAT 0x208
+#define TPIU_FORMATTER 0x304
+#define TPIU_FORMATTER_SYNC 0x308
+#define TPIU_LOCK_ACCESS_REGISTER 0xFB0
+
+#define TPIU_UNLOCK_CODE 0xc5acce55
+
+/* The context of the Trace Port Interface Unit (TPIU) */
+static struct {
+ void __iomem *base;
+ u32 port_size;
+ u32 trigger_counter;
+ u32 trigger_multiplier;
+ u32 current_test_pattern;
+ u32 test_pattern_repeat;
+ u32 formatter;
+ u32 formatter_sync;
+} context_tpiu;
+
+/*
+ * Save the context of the DB8500 Trace Port Interface Unit (TPIU).
+ * Saving/restoring is needed for the PTM tracing to work together
+ * with sleep states where the APE power domain is turned off.
+ */
+static void tpiu_save_context(void)
+{
+ context_tpiu.port_size = readl(context_tpiu.base +
+ TPIU_PORT_SIZE);
+ context_tpiu.trigger_counter = readl(context_tpiu.base +
+ TPIU_TRIGGER_COUNTER);
+ context_tpiu.trigger_multiplier = readl(context_tpiu.base +
+ TPIU_TRIGGER_MULTIPLIER);
+ context_tpiu.current_test_pattern = readl(context_tpiu.base +
+ TPIU_CURRENT_TEST_PATTERN);
+ context_tpiu.test_pattern_repeat = readl(context_tpiu.base +
+ TPIU_TEST_PATTERN_REPEAT);
+ context_tpiu.formatter = readl(context_tpiu.base +
+ TPIU_FORMATTER);
+ context_tpiu.formatter_sync = readl(context_tpiu.base +
+ TPIU_FORMATTER_SYNC);
+}
+
+/*
+ * Restore the context of the DB8500 Trace Port Interface Unit (TPIU).
+ * Saving/restoring is needed for the PTM tracing to work together
+ * with the sleep states where the APE power domain is turned off.
+ */
+static void tpiu_restore_context(void)
+{
+ writel(TPIU_UNLOCK_CODE,
+ context_tpiu.base + TPIU_LOCK_ACCESS_REGISTER);
+
+ writel(context_tpiu.port_size,
+ context_tpiu.base + TPIU_PORT_SIZE);
+ writel(context_tpiu.trigger_counter,
+ context_tpiu.base + TPIU_TRIGGER_COUNTER);
+ writel(context_tpiu.trigger_multiplier,
+ context_tpiu.base + TPIU_TRIGGER_MULTIPLIER);
+ writel(context_tpiu.current_test_pattern,
+ context_tpiu.base + TPIU_CURRENT_TEST_PATTERN);
+ writel(context_tpiu.test_pattern_repeat,
+ context_tpiu.base + TPIU_TEST_PATTERN_REPEAT);
+ writel(context_tpiu.formatter,
+ context_tpiu.base + TPIU_FORMATTER);
+ writel(context_tpiu.formatter_sync,
+ context_tpiu.base + TPIU_FORMATTER_SYNC);
+}
+
+static int tpiu_context_call(struct notifier_block *this,
+ unsigned long event, void *data)
+{
+ bool power_on = (bool)event;
+
+ if (power_on)
+ tpiu_restore_context();
+ else
+ tpiu_save_context();
+
+ return 0;
+}
+
+static struct notifier_block tpiu_context_notifier = {
+ .notifier_call = tpiu_context_call,
+};
+
+static const struct of_device_id dbx500_tpiu_match[] = {
+ { .compatible = "stericsson,dbx500-tpiu", },
+ {},
+};
+
+static struct platform_driver dbx500_tpiu_plat_driver = {
+ .driver = {
+ .name = "dbx500-tpiu",
+ .of_match_table = dbx500_tpiu_match,
+ },
+ .remove = __exit_p(dbx500_tpiu_remove),
+};
+
+static int __init dbx500_tpiu_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "missing platform resources\n");
+ return -EINVAL;
+ }
+
+ if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
+ dev_err(&pdev->dev, "failed to request I/O memory\n");
+ return -EBUSY;
+ }
+
+ context_tpiu.base = ioremap(res->start, resource_size(res));
+ if (!context_tpiu.base) {
+ ret = -ENOMEM;
+ goto err_free_mem_region;
+ }
+
+ ret = pm_genpd_register_on_off_notifier(&pdev->dev,
+ &tpiu_context_notifier);
+ if (ret)
+ goto err_iounmap;
+
+ return ret;
+
+ err_iounmap:
+ iounmap(context_tpiu.base);
+ err_free_mem_region:
+ release_mem_region(res->start, resource_size(res));
+ return ret;
+}
+
+static int __exit dbx500_tpiu_remove(struct platform_device *pdev)
+{
+ int ret;
+
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ ret = pm_genpd_unregister_on_off_notifier(&pdev->dev,
+ &tpiu_context_notifier);
+ iounmap(context_tpiu.base);
+ release_mem_region(res->start, resource_size(res));
+
+ return ret;
+}
+
+static int __init dbx500_tpiu_init(void)
+{
+ return platform_driver_probe(&dbx500_tpiu_plat_driver,
+ dbx500_tpiu_probe);
+}
+
+static void __exit dbx500_tpiu_exit(void)
+{
+ return platform_driver_unregister(&dbx500_tpiu_plat_driver);
+}
+
+arch_initcall(dbx500_tpiu_init);
+module_exit(dbx500_tpiu_exit);
+
+MODULE_DESCRIPTION("TPIU driver for dbx500");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Rickard Andersson <rickard.andersson@stericsson.com>");
--
1.8.2
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH V2 12/12] ARM: ux500: Create TPIU device
2013-03-28 16:11 [PATCH V2 00/12] ux500 suspend-resume Rickard Andersson
` (10 preceding siblings ...)
2013-03-28 16:11 ` [PATCH V2 11/12] misc: ux500: Add TPIU driver Rickard Andersson
@ 2013-03-28 16:11 ` Rickard Andersson
11 siblings, 0 replies; 17+ messages in thread
From: Rickard Andersson @ 2013-03-28 16:11 UTC (permalink / raw)
To: rjw, linux-pm, linux-arm-kernel
Cc: hongbo.zhang, ulf.hansson, khilman, linus.walleij, daniel.lezcano,
rickard.andersson
Add platform device for the ux500 TPIU driver. TPIU
is Trace Port Interface Unit. Also add the device
to the generic power domain APE.
Signed-off-by: Rickard Andersson <rickard.andersson@stericsson.com>
---
arch/arm/boot/dts/dbx5x0.dtsi | 5 +++++
arch/arm/mach-ux500/board-mop500.c | 1 +
arch/arm/mach-ux500/cpu-db8500.c | 1 +
arch/arm/mach-ux500/devices-common.c | 16 ++++++++++++++++
arch/arm/mach-ux500/devices-common.h | 1 +
5 files changed, 24 insertions(+)
diff --git a/arch/arm/boot/dts/dbx5x0.dtsi b/arch/arm/boot/dts/dbx5x0.dtsi
index ef98422..9e5fce5 100644
--- a/arch/arm/boot/dts/dbx5x0.dtsi
+++ b/arch/arm/boot/dts/dbx5x0.dtsi
@@ -671,6 +671,11 @@
reg = <0x81000000 0x2000>;
};
+ tpiu {
+ compatible = "stericsson,dbx500-tpiu";
+ reg = <0x80190000 0x1000>;
+ };
+
external-bus@50000000 {
compatible = "simple-bus";
reg = <0x50000000 0x4000000>;
diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c
index 76bace3..f1c08a9 100644
--- a/arch/arm/mach-ux500/board-mop500.c
+++ b/arch/arm/mach-ux500/board-mop500.c
@@ -661,6 +661,7 @@ static void __init init_pm_domains(void)
add_platform_device_to_genpd(&db8500_clk_device, &ape_domain);
add_platform_device_to_genpd(&dbx500_icn_device, &ape_domain);
+ add_platform_device_to_genpd(&dbx500_tpiu_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 d849986..7096462 100644
--- a/arch/arm/mach-ux500/cpu-db8500.c
+++ b/arch/arm/mach-ux500/cpu-db8500.c
@@ -144,6 +144,7 @@ static struct platform_device db8500_prcmu_device = {
static struct platform_device *platform_devs[] __initdata = {
&db8500_clk_device,
&dbx500_icn_device,
+ &dbx500_tpiu_device,
&u8500_dma40_device,
&db8500_pmu_device,
&db8500_prcmu_device,
diff --git a/arch/arm/mach-ux500/devices-common.c b/arch/arm/mach-ux500/devices-common.c
index 74c7a76..a0f87ae 100644
--- a/arch/arm/mach-ux500/devices-common.c
+++ b/arch/arm/mach-ux500/devices-common.c
@@ -34,6 +34,22 @@ struct platform_device dbx500_icn_device = {
.resource = icn_resources
};
+static struct resource tpiu_resources[] = {
+ [0] = {
+ .start = U8500_TPIU_BASE,
+ .end = U8500_TPIU_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ .name = "base",
+ },
+};
+
+struct platform_device dbx500_tpiu_device = {
+ .name = "dbx500-tpiu",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(tpiu_resources),
+ .resource = tpiu_resources
+};
+
static struct platform_device *
dbx500_add_gpio(struct device *parent, int id, resource_size_t addr, int irq,
struct nmk_gpio_platform_data *pdata)
diff --git a/arch/arm/mach-ux500/devices-common.h b/arch/arm/mach-ux500/devices-common.h
index adbf524..3106b13 100644
--- a/arch/arm/mach-ux500/devices-common.h
+++ b/arch/arm/mach-ux500/devices-common.h
@@ -17,6 +17,7 @@
struct spi_master_cntlr;
extern struct platform_device dbx500_icn_device;
+extern struct platform_device dbx500_tpiu_device;
static inline struct amba_device *
dbx500_add_msp_spi(struct device *parent, const char *name,
--
1.8.2
^ permalink raw reply related [flat|nested] 17+ messages in thread