From: Dongsheng Wang <dongsheng.wang@freescale.com>
To: <scottwood@freescale.com>
Cc: linuxppc-dev@lists.ozlabs.org, chenhui.zhao@freescale.com,
jason.jin@freescale.com,
Wang Dongsheng <dongsheng.wang@freescale.com>
Subject: [PATCH 2/2] fsl/mpic_timer: make mpic_timer to support deep sleep feature
Date: Mon, 14 Apr 2014 10:24:10 +0800 [thread overview]
Message-ID: <1397442250-14886-2-git-send-email-dongsheng.wang@freescale.com> (raw)
In-Reply-To: <1397442250-14886-1-git-send-email-dongsheng.wang@freescale.com>
From: Wang Dongsheng <dongsheng.wang@freescale.com>
At T104x platfrom the timer clock will be changed when system going to
deep sleep. Add suspend function to switch timer time before system
going to deep sleep, and recovery the time after resume from deep sleep.
Signed-off-by: Wang Dongsheng <dongsheng.wang@freescale.com>
diff --git a/arch/powerpc/sysdev/mpic_timer.c b/arch/powerpc/sysdev/mpic_timer.c
index 9d9b062..737a53d 100644
--- a/arch/powerpc/sysdev/mpic_timer.c
+++ b/arch/powerpc/sysdev/mpic_timer.c
@@ -11,6 +11,7 @@
* option) any later version.
*/
+#include <linux/fsl/fsl_pm.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
@@ -18,6 +19,7 @@
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
+#include <linux/suspend.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
@@ -26,6 +28,7 @@
#include <sysdev/fsl_soc.h>
#include <asm/io.h>
+#include <asm/mpc85xx.h>
#include <asm/mpic_timer.h>
#define FSL_GLOBAL_TIMER 0x1
@@ -71,8 +74,10 @@ struct timer_group_priv {
struct timer_regs __iomem *regs;
struct mpic_timer timer[TIMERS_PER_GROUP];
struct list_head node;
+ unsigned long idle;
unsigned int timerfreq;
- unsigned int idle;
+ unsigned int suspended_timerfreq;
+ unsigned int resume_timerfreq;
unsigned int flags;
spinlock_t lock;
void __iomem *group_tcr;
@@ -88,6 +93,7 @@ static struct cascade_priv cascade_timer[] = {
};
static LIST_HEAD(timer_group_list);
+static int switch_freq_flag;
static void convert_ticks_to_time(struct timer_group_priv *priv,
const u64 ticks, struct timeval *time)
@@ -423,6 +429,33 @@ struct mpic_timer *mpic_request_timer(irq_handler_t fn, void *dev,
}
EXPORT_SYMBOL(mpic_request_timer);
+static void timer_group_get_suspended_freq(struct timer_group_priv *priv)
+{
+ struct device_node *np;
+
+ np = of_find_compatible_node(NULL, NULL, "fsl,qoriq-clockgen-2.0");
+ if (!np) {
+ pr_err("mpic timer: Missing clockgen device node.\n");
+
+ return;
+ }
+
+ of_property_read_u32(np, "clock-frequency", &priv->suspended_timerfreq);
+ of_node_put(np);
+}
+
+static int need_to_switch_freq(void)
+{
+ u32 svr;
+
+ svr = mfspr(SPRN_SVR);
+ if (SVR_SOC_VER(svr) == SVR_T1040 ||
+ SVR_SOC_VER(svr) == SVR_T1042)
+ return 1;
+
+ return 0;
+}
+
static int timer_group_get_freq(struct device_node *np,
struct timer_group_priv *priv)
{
@@ -437,6 +470,15 @@ static int timer_group_get_freq(struct device_node *np,
&priv->timerfreq);
of_node_put(dn);
}
+
+ /*
+ * For deep sleep, if system going to deep sleep,
+ * timer freq will be changed.
+ */
+ if (need_to_switch_freq()) {
+ timer_group_get_suspended_freq(priv);
+ switch_freq_flag = 1;
+ }
}
if (priv->timerfreq <= 0)
@@ -445,6 +487,7 @@ static int timer_group_get_freq(struct device_node *np,
if (priv->flags & FSL_GLOBAL_TIMER) {
div = (1 << (MPIC_TIMER_TCR_CLKDIV >> 8)) * 8;
priv->timerfreq /= div;
+ priv->suspended_timerfreq /= div;
}
return 0;
@@ -564,14 +607,190 @@ out:
kfree(priv);
}
+static void mpic_reset_time(struct mpic_timer *handle, struct timeval *bcr_time,
+ struct timeval *ccr_time)
+{
+ struct timer_group_priv *priv = container_of(handle,
+ struct timer_group_priv, timer[handle->num]);
+
+ u64 ccr_ticks = 0;
+ u64 bcr_ticks = 0;
+
+ /* switch bcr time */
+ convert_time_to_ticks(priv, bcr_time, &bcr_ticks);
+
+ /* switch ccr time */
+ convert_time_to_ticks(priv, ccr_time, &ccr_ticks);
+
+ if (handle->cascade_handle) {
+ u32 tmp_ticks;
+ u32 rem_ticks;
+
+ /* reset ccr ticks to bcr */
+ tmp_ticks = div_u64_rem(ccr_ticks, MAX_TICKS_CASCADE,
+ &rem_ticks);
+ out_be32(&priv->regs[handle->num].gtbcr,
+ tmp_ticks | TIMER_STOP);
+ out_be32(&priv->regs[handle->num - 1].gtbcr, rem_ticks);
+
+ /* start timer */
+ clrbits32(&priv->regs[handle->num].gtbcr, TIMER_STOP);
+
+ /* reset bcr */
+ tmp_ticks = div_u64_rem(bcr_ticks, MAX_TICKS_CASCADE,
+ &rem_ticks);
+ out_be32(&priv->regs[handle->num].gtbcr,
+ tmp_ticks & ~TIMER_STOP);
+ out_be32(&priv->regs[handle->num - 1].gtbcr, rem_ticks);
+ } else {
+ /* reset ccr ticks to bcr */
+ out_be32(&priv->regs[handle->num].gtbcr,
+ ccr_ticks | TIMER_STOP);
+ /* start timer */
+ clrbits32(&priv->regs[handle->num].gtbcr, TIMER_STOP);
+ /* reset bcr */
+ out_be32(&priv->regs[handle->num].gtbcr,
+ bcr_ticks & ~TIMER_STOP);
+ }
+}
+
+static void do_switch_time(struct mpic_timer *handle, unsigned int new_freq)
+{
+ struct timer_group_priv *priv = container_of(handle,
+ struct timer_group_priv, timer[handle->num]);
+ struct timeval ccr_time;
+ struct timeval bcr_time;
+ unsigned int timerfreq;
+ u32 test_stop;
+ u64 ticks;
+
+ test_stop = in_be32(&priv->regs[handle->num].gtbcr);
+ test_stop &= TIMER_STOP;
+ if (test_stop)
+ return;
+
+ /* stop timer, prepare reset time */
+ setbits32(&priv->regs[handle->num].gtbcr, TIMER_STOP);
+
+ /* get bcr time */
+ if (handle->cascade_handle) {
+ u32 tmp_ticks;
+
+ tmp_ticks = in_be32(&priv->regs[handle->num].gtbcr);
+ tmp_ticks &= ~TIMER_STOP;
+ ticks = ((u64)tmp_ticks & UINT_MAX) * (u64)MAX_TICKS_CASCADE;
+ tmp_ticks = in_be32(&priv->regs[handle->num - 1].gtbcr);
+ ticks += tmp_ticks;
+ } else {
+ ticks = in_be32(&priv->regs[handle->num].gtbcr);
+ ticks &= ~TIMER_STOP;
+ }
+ convert_ticks_to_time(priv, ticks, &bcr_time);
+
+ /* get ccr time */
+ mpic_get_remain_time(handle, &ccr_time);
+
+ /* recalculate timer time */
+ timerfreq = priv->timerfreq;
+ priv->timerfreq = new_freq;
+ mpic_reset_time(handle, &bcr_time, &ccr_time);
+ priv->timerfreq = timerfreq;
+}
+
+static void switch_group_timer(struct timer_group_priv *priv,
+ unsigned int new_freq)
+{
+ int i, num;
+
+ for (i = 0; i < TIMERS_PER_GROUP; i++) {
+ num = TIMERS_PER_GROUP - 1 - i;
+ /* cascade */
+ if ((i + 1) < TIMERS_PER_GROUP &&
+ priv->timer[num].cascade_handle) {
+ do_switch_time(&priv->timer[num], new_freq);
+ i++;
+ continue;
+ }
+
+ if (!test_bit(i, &priv->idle))
+ do_switch_time(&priv->timer[num], new_freq);
+ }
+}
+
+static int mpic_timer_suspend(void)
+{
+ struct timer_group_priv *priv;
+ suspend_state_t pm_state;
+
+ pm_state = pm_suspend_state();
+
+ list_for_each_entry(priv, &timer_group_list, node) {
+ /* timer not be used */
+ if (priv->idle == 0xf)
+ continue;
+
+ switch (pm_state) {
+ case PM_SUSPEND_STANDBY:
+ break;
+ case PM_SUSPEND_MEM:
+ if (!switch_freq_flag)
+ continue;
+
+ if (!priv->suspended_timerfreq) {
+ pr_warn("Mpic timer will not be accurate.\n");
+ continue;
+ }
+
+ /* will switch timers, a set of timer */
+ switch_group_timer(priv, priv->suspended_timerfreq);
+
+ /* Software: switch timerfreq to suspended freq */
+ priv->resume_timerfreq = priv->timerfreq;
+ priv->timerfreq = priv->suspended_timerfreq;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
static void mpic_timer_resume(void)
{
struct timer_group_priv *priv;
+ suspend_state_t pm_state;
+
+ pm_state = pm_suspend_state();
list_for_each_entry(priv, &timer_group_list, node) {
/* Init FSL timer hardware */
if (priv->flags & FSL_GLOBAL_TIMER)
setbits32(priv->group_tcr, MPIC_TIMER_TCR_CLKDIV);
+
+ /* timer not be used */
+ if (priv->idle == 0xf)
+ continue;
+
+ switch (pm_state) {
+ case PM_SUSPEND_STANDBY:
+ break;
+ case PM_SUSPEND_MEM:
+ if (!switch_freq_flag)
+ continue;
+
+ if (!priv->suspended_timerfreq)
+ continue;
+
+ /* will switch timers, a set of timer */
+ switch_group_timer(priv, priv->resume_timerfreq);
+
+ /* restore timerfreq */
+ priv->timerfreq = priv->resume_timerfreq;
+ break;
+ default:
+ break;
+ }
}
}
@@ -581,6 +800,7 @@ static const struct of_device_id mpic_timer_ids[] = {
};
static struct syscore_ops mpic_timer_syscore_ops = {
+ .suspend = mpic_timer_suspend,
.resume = mpic_timer_resume,
};
--
1.8.5
next prev parent reply other threads:[~2014-04-14 2:26 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-04-14 2:24 [PATCH 1/2] powerpc/mpc85xx: add two functions to get suspend state which is standby or mem Dongsheng Wang
2014-04-14 2:24 ` Dongsheng Wang [this message]
2014-04-14 23:35 ` [PATCH 2/2] fsl/mpic_timer: make mpic_timer to support deep sleep feature Scott Wood
2014-04-15 3:23 ` Dongsheng.Wang
2014-04-15 21:06 ` Scott Wood
2014-04-16 6:06 ` Dongsheng.Wang
2014-04-14 23:27 ` [PATCH 1/2] powerpc/mpc85xx: add two functions to get suspend state which is standby or mem Scott Wood
2014-04-15 2:19 ` Dongsheng.Wang
2014-04-15 19:35 ` Scott Wood
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1397442250-14886-2-git-send-email-dongsheng.wang@freescale.com \
--to=dongsheng.wang@freescale.com \
--cc=chenhui.zhao@freescale.com \
--cc=jason.jin@freescale.com \
--cc=linuxppc-dev@lists.ozlabs.org \
--cc=scottwood@freescale.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).