aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorColin Cross <ccross@android.com>2012-08-07 14:05:10 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2012-08-11 04:15:58 -0400
commit237ec6f2e51d2fc2ff37c7c5f1ccc9264d09c85b (patch)
tree17a21e6c00129a703d906166f0e640089e3af6b7 /arch
parentf1898f6be9a2e3690fb62d8307849b86a89cc36c (diff)
ARM: 7486/1: sched_clock: update epoch_cyc on resume
Many clocks that are used to provide sched_clock will reset during suspend. If read_sched_clock returns 0 after suspend, sched_clock will appear to jump forward. This patch resets cd.epoch_cyc to the current value of read_sched_clock during resume, which causes sched_clock() just after suspend to return the same value as sched_clock() just before suspend. In addition, during the window where epoch_ns has been updated before suspend, but epoch_cyc has not been updated after suspend, it is unknown whether the clock has reset or not, and sched_clock() could return a bogus value. Add a suspended flag, and return the pre-suspend epoch_ns value during this period. The new behavior is triggered by calling setup_sched_clock_needs_suspend instead of setup_sched_clock. Signed-off-by: Colin Cross <ccross@android.com> Reviewed-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/include/asm/sched_clock.h2
-rw-r--r--arch/arm/kernel/sched_clock.c24
2 files changed, 26 insertions, 0 deletions
diff --git a/arch/arm/include/asm/sched_clock.h b/arch/arm/include/asm/sched_clock.h
index e3f757263438..05b8e82ec9f5 100644
--- a/arch/arm/include/asm/sched_clock.h
+++ b/arch/arm/include/asm/sched_clock.h
@@ -10,5 +10,7 @@
10 10
11extern void sched_clock_postinit(void); 11extern void sched_clock_postinit(void);
12extern void setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate); 12extern void setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate);
13extern void setup_sched_clock_needs_suspend(u32 (*read)(void), int bits,
14 unsigned long rate);
13 15
14#endif 16#endif
diff --git a/arch/arm/kernel/sched_clock.c b/arch/arm/kernel/sched_clock.c
index 27d186abbc06..f4515393248d 100644
--- a/arch/arm/kernel/sched_clock.c
+++ b/arch/arm/kernel/sched_clock.c
@@ -21,6 +21,8 @@ struct clock_data {
21 u32 epoch_cyc_copy; 21 u32 epoch_cyc_copy;
22 u32 mult; 22 u32 mult;
23 u32 shift; 23 u32 shift;
24 bool suspended;
25 bool needs_suspend;
24}; 26};
25 27
26static void sched_clock_poll(unsigned long wrap_ticks); 28static void sched_clock_poll(unsigned long wrap_ticks);
@@ -49,6 +51,9 @@ static unsigned long long cyc_to_sched_clock(u32 cyc, u32 mask)
49 u64 epoch_ns; 51 u64 epoch_ns;
50 u32 epoch_cyc; 52 u32 epoch_cyc;
51 53
54 if (cd.suspended)
55 return cd.epoch_ns;
56
52 /* 57 /*
53 * Load the epoch_cyc and epoch_ns atomically. We do this by 58 * Load the epoch_cyc and epoch_ns atomically. We do this by
54 * ensuring that we always write epoch_cyc, epoch_ns and 59 * ensuring that we always write epoch_cyc, epoch_ns and
@@ -98,6 +103,13 @@ static void sched_clock_poll(unsigned long wrap_ticks)
98 update_sched_clock(); 103 update_sched_clock();
99} 104}
100 105
106void __init setup_sched_clock_needs_suspend(u32 (*read)(void), int bits,
107 unsigned long rate)
108{
109 setup_sched_clock(read, bits, rate);
110 cd.needs_suspend = true;
111}
112
101void __init setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate) 113void __init setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate)
102{ 114{
103 unsigned long r, w; 115 unsigned long r, w;
@@ -169,11 +181,23 @@ void __init sched_clock_postinit(void)
169static int sched_clock_suspend(void) 181static int sched_clock_suspend(void)
170{ 182{
171 sched_clock_poll(sched_clock_timer.data); 183 sched_clock_poll(sched_clock_timer.data);
184 if (cd.needs_suspend)
185 cd.suspended = true;
172 return 0; 186 return 0;
173} 187}
174 188
189static void sched_clock_resume(void)
190{
191 if (cd.needs_suspend) {
192 cd.epoch_cyc = read_sched_clock();
193 cd.epoch_cyc_copy = cd.epoch_cyc;
194 cd.suspended = false;
195 }
196}
197
175static struct syscore_ops sched_clock_ops = { 198static struct syscore_ops sched_clock_ops = {
176 .suspend = sched_clock_suspend, 199 .suspend = sched_clock_suspend,
200 .resume = sched_clock_resume,
177}; 201};
178 202
179static int __init sched_clock_syscore_init(void) 203static int __init sched_clock_syscore_init(void)