aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFinn Thain <fthain@telegraphics.com.au>2018-11-30 19:53:10 -0500
committerGeert Uytterhoeven <geert@linux-m68k.org>2019-03-25 05:22:24 -0400
commit26ccd2d376d9b7de9a27b9a7ed71e16216101921 (patch)
tree514f9e44b8c2ef830c6c0e6a6bfb4d7df2a6b927
parent5afd3d06e5cb30a312aaa34348f9457b9fb38a52 (diff)
m68k: atari: Convert to clocksource API
Add a platform clocksource by adapting the existing arch_gettimeoffset implementation. Normally the MFP timer C interrupt flag would be used to check for timer counter wrap-around. Unfortunately, that flag gets cleared by the MFP itself (due to automatic End-of-Interrupt mode). This means that mfp_timer_c_handler() and atari_read_clk() must race when accounting for counter wrap-around. That problem is avoided by effectively stopping the clock when it might otherwise jump backwards (due to interrupt latency). Note that this may affect clock accuracy. After the timer interrupt is asserted, wait for the counter to be reloaded so that atari_read_clk() will not see the intermediate state as that would cause the clock to jump backwards. Signed-off-by: Finn Thain <fthain@telegraphics.com.au> Acked-by: Linus Walleij <linus.walleij@linaro.org> Tested-by: Michael Schmitz <schmitzmic@gmail.com> Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
-rw-r--r--arch/m68k/atari/time.c53
1 files changed, 36 insertions, 17 deletions
diff --git a/arch/m68k/atari/time.c b/arch/m68k/atari/time.c
index fafa20f75ab9..ce923a523695 100644
--- a/arch/m68k/atari/time.c
+++ b/arch/m68k/atari/time.c
@@ -16,6 +16,7 @@
16#include <linux/init.h> 16#include <linux/init.h>
17#include <linux/rtc.h> 17#include <linux/rtc.h>
18#include <linux/bcd.h> 18#include <linux/bcd.h>
19#include <linux/clocksource.h>
19#include <linux/delay.h> 20#include <linux/delay.h>
20#include <linux/export.h> 21#include <linux/export.h>
21 22
@@ -24,12 +25,29 @@
24DEFINE_SPINLOCK(rtc_lock); 25DEFINE_SPINLOCK(rtc_lock);
25EXPORT_SYMBOL_GPL(rtc_lock); 26EXPORT_SYMBOL_GPL(rtc_lock);
26 27
28static u64 atari_read_clk(struct clocksource *cs);
29
30static struct clocksource atari_clk = {
31 .name = "mfp",
32 .rating = 100,
33 .read = atari_read_clk,
34 .mask = CLOCKSOURCE_MASK(32),
35 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
36};
37
38static u32 clk_total;
39static u8 last_timer_count;
40
27static irqreturn_t mfp_timer_c_handler(int irq, void *dev_id) 41static irqreturn_t mfp_timer_c_handler(int irq, void *dev_id)
28{ 42{
29 irq_handler_t timer_routine = dev_id; 43 irq_handler_t timer_routine = dev_id;
30 unsigned long flags; 44 unsigned long flags;
31 45
32 local_irq_save(flags); 46 local_irq_save(flags);
47 do {
48 last_timer_count = st_mfp.tim_dt_c;
49 } while (last_timer_count == 1);
50 clk_total += INT_TICKS;
33 timer_routine(0, NULL); 51 timer_routine(0, NULL);
34 local_irq_restore(flags); 52 local_irq_restore(flags);
35 53
@@ -44,32 +62,33 @@ atari_sched_init(irq_handler_t timer_routine)
44 /* start timer C, div = 1:100 */ 62 /* start timer C, div = 1:100 */
45 st_mfp.tim_ct_cd = (st_mfp.tim_ct_cd & 15) | 0x60; 63 st_mfp.tim_ct_cd = (st_mfp.tim_ct_cd & 15) | 0x60;
46 /* install interrupt service routine for MFP Timer C */ 64 /* install interrupt service routine for MFP Timer C */
47 if (request_irq(IRQ_MFP_TIMC, mfp_timer_c_handler, 0, "timer", 65 if (request_irq(IRQ_MFP_TIMC, mfp_timer_c_handler, IRQF_TIMER, "timer",
48 timer_routine)) 66 timer_routine))
49 pr_err("Couldn't register timer interrupt\n"); 67 pr_err("Couldn't register timer interrupt\n");
68
69 clocksource_register_hz(&atari_clk, INT_CLK);
50} 70}
51 71
52/* ++andreas: gettimeoffset fixed to check for pending interrupt */ 72/* ++andreas: gettimeoffset fixed to check for pending interrupt */
53 73
54#define TICK_SIZE 10000 74static u64 atari_read_clk(struct clocksource *cs)
55
56/* This is always executed with interrupts disabled. */
57u32 atari_gettimeoffset(void)
58{ 75{
59 u32 ticks, offset = 0; 76 unsigned long flags;
60 77 u8 count;
61 /* read MFP timer C current value */ 78 u32 ticks;
62 ticks = st_mfp.tim_dt_c;
63 /* The probability of underflow is less than 2% */
64 if (ticks > INT_TICKS - INT_TICKS / 50)
65 /* Check for pending timer interrupt */
66 if (st_mfp.int_pn_b & (1 << 5))
67 offset = TICK_SIZE;
68 79
69 ticks = INT_TICKS - ticks; 80 local_irq_save(flags);
70 ticks = ticks * 10000L / INT_TICKS; 81 /* Ensure that the count is monotonically decreasing, even though
82 * the result may briefly stop changing after counter wrap-around.
83 */
84 count = min(st_mfp.tim_dt_c, last_timer_count);
85 last_timer_count = count;
86
87 ticks = INT_TICKS - count;
88 ticks += clk_total;
89 local_irq_restore(flags);
71 90
72 return (ticks + offset) * 1000; 91 return ticks;
73} 92}
74 93
75 94