aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-ks8695
diff options
context:
space:
mode:
authorLinus Walleij <linus.walleij@linaro.org>2012-08-29 14:27:22 -0400
committerLinus Walleij <linus.walleij@linaro.org>2012-09-05 02:49:22 -0400
commitc7e783d6adc7798307e7063e11f4127117446d5a (patch)
treeec58ba9e50f3d849527d0ebc914b0c0fe2a903f3 /arch/arm/mach-ks8695
parentd7dda9875b84eb6c2828592b17aa173ac17bf75d (diff)
ARM: ks8695: convert to generic time and clocksource
Old platforms using ancient gettimeoffset() and other arcane APIs are standing in the way of cleaning up the ARM kernel. The gettimeoffset() was also broken: it would try to read out the timer counter value, while this would not work (the counter statically returns the initially programmed value) so the implementation would anyway fall back to a homebrew version of jiffie calculation. This is an attempt at blind-coding a generic time and clocksource driver for the platform by way of a datasheet and looking at the old code. Tested-by: Greg Ungerer <gerg@snapgear.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'arch/arm/mach-ks8695')
-rw-r--r--arch/arm/mach-ks8695/time.c105
1 files changed, 67 insertions, 38 deletions
diff --git a/arch/arm/mach-ks8695/time.c b/arch/arm/mach-ks8695/time.c
index 6974c2355601..46c84bc7792c 100644
--- a/arch/arm/mach-ks8695/time.c
+++ b/arch/arm/mach-ks8695/time.c
@@ -25,6 +25,7 @@
25#include <linux/kernel.h> 25#include <linux/kernel.h>
26#include <linux/sched.h> 26#include <linux/sched.h>
27#include <linux/io.h> 27#include <linux/io.h>
28#include <linux/clockchips.h>
28 29
29#include <asm/mach/time.h> 30#include <asm/mach/time.h>
30#include <asm/system_misc.h> 31#include <asm/system_misc.h>
@@ -53,44 +54,69 @@
53/* Timer0 Timeout Counter Register */ 54/* Timer0 Timeout Counter Register */
54#define T0TC_WATCHDOG (0xff) /* Enable watchdog mode */ 55#define T0TC_WATCHDOG (0xff) /* Enable watchdog mode */
55 56
56/* 57static void ks8695_set_mode(enum clock_event_mode mode,
57 * Returns number of ms since last clock interrupt. Note that interrupts 58 struct clock_event_device *evt)
58 * will have been disabled by do_gettimeoffset()
59 */
60static unsigned long ks8695_gettimeoffset (void)
61{ 59{
62 unsigned long elapsed, tick2, intpending; 60 u32 tmcon;
63 61
64 /* 62 if (mode == CLOCK_EVT_FEAT_PERIODIC) {
65 * Get the current number of ticks. Note that there is a race 63 u32 rate = DIV_ROUND_CLOSEST(KS8695_CLOCK_RATE, HZ);
66 * condition between us reading the timer and checking for an 64 u32 half = DIV_ROUND_CLOSEST(rate, 2);
67 * interrupt. We solve this by ensuring that the counter has not 65
68 * reloaded between our two reads. 66 /* Disable timer 1 */
69 */ 67 tmcon = readl_relaxed(KS8695_TMR_VA + KS8695_TMCON);
70 elapsed = readl_relaxed(KS8695_TMR_VA + KS8695_T1TC) + readl_relaxed(KS8695_TMR_VA + KS8695_T1PD); 68 tmcon &= ~TMCON_T1EN;
71 do { 69 writel_relaxed(tmcon, KS8695_TMR_VA + KS8695_TMCON);
72 tick2 = elapsed; 70
73 intpending = readl_relaxed(KS8695_IRQ_VA + KS8695_INTST) & (1 << KS8695_IRQ_TIMER1); 71 /* Both registers need to count down */
74 elapsed = readl_relaxed(KS8695_TMR_VA + KS8695_T1TC) + readl_relaxed(KS8695_TMR_VA + KS8695_T1PD); 72 writel_relaxed(half, KS8695_TMR_VA + KS8695_T1TC);
75 } while (elapsed > tick2); 73 writel_relaxed(half, KS8695_TMR_VA + KS8695_T1PD);
76 74
77 /* Convert to number of ticks expired (not remaining) */ 75 /* Re-enable timer1 */
78 elapsed = (CLOCK_TICK_RATE / HZ) - elapsed; 76 tmcon |= TMCON_T1EN;
79 77 writel_relaxed(tmcon, KS8695_TMR_VA + KS8695_TMCON);
80 /* Is interrupt pending? If so, then timer has been reloaded already. */ 78 }
81 if (intpending) 79}
82 elapsed += (CLOCK_TICK_RATE / HZ); 80
83 81static int ks8695_set_next_event(unsigned long cycles,
84 /* Convert ticks to usecs */ 82 struct clock_event_device *evt)
85 return (unsigned long)(elapsed * (tick_nsec / 1000)) / LATCH; 83
84{
85 u32 half = DIV_ROUND_CLOSEST(cycles, 2);
86 u32 tmcon;
87
88 /* Disable timer 1 */
89 tmcon = readl_relaxed(KS8695_TMR_VA + KS8695_TMCON);
90 tmcon &= ~TMCON_T1EN;
91 writel_relaxed(tmcon, KS8695_TMR_VA + KS8695_TMCON);
92
93 /* Both registers need to count down */
94 writel_relaxed(half, KS8695_TMR_VA + KS8695_T1TC);
95 writel_relaxed(half, KS8695_TMR_VA + KS8695_T1PD);
96
97 /* Re-enable timer1 */
98 tmcon |= TMCON_T1EN;
99 writel_relaxed(tmcon, KS8695_TMR_VA + KS8695_TMCON);
100
101 return 0;
86} 102}
87 103
104static struct clock_event_device clockevent_ks8695 = {
105 .name = "ks8695_t1tc",
106 .rating = 300, /* Reasonably fast and accurate clock event */
107 .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC,
108 .set_next_event = ks8695_set_next_event,
109 .set_mode = ks8695_set_mode,
110};
111
88/* 112/*
89 * IRQ handler for the timer. 113 * IRQ handler for the timer.
90 */ 114 */
91static irqreturn_t ks8695_timer_interrupt(int irq, void *dev_id) 115static irqreturn_t ks8695_timer_interrupt(int irq, void *dev_id)
92{ 116{
93 timer_tick(); 117 struct clock_event_device *evt = &clockevent_ks8695;
118
119 evt->event_handler(evt);
94 return IRQ_HANDLED; 120 return IRQ_HANDLED;
95} 121}
96 122
@@ -102,18 +128,22 @@ static struct irqaction ks8695_timer_irq = {
102 128
103static void ks8695_timer_setup(void) 129static void ks8695_timer_setup(void)
104{ 130{
105 unsigned long tmout = CLOCK_TICK_RATE / HZ;
106 unsigned long tmcon; 131 unsigned long tmcon;
107 132
108 /* disable timer1 */ 133 /* Disable timer 0 and 1 */
109 tmcon = readl_relaxed(KS8695_TMR_VA + KS8695_TMCON); 134 tmcon = readl_relaxed(KS8695_TMR_VA + KS8695_TMCON);
110 writel_relaxed(tmcon & ~TMCON_T1EN, KS8695_TMR_VA + KS8695_TMCON); 135 tmcon &= ~TMCON_T0EN;
111 136 tmcon &= ~TMCON_T1EN;
112 writel_relaxed(tmout / 2, KS8695_TMR_VA + KS8695_T1TC); 137 writel_relaxed(tmcon, KS8695_TMR_VA + KS8695_TMCON);
113 writel_relaxed(tmout / 2, KS8695_TMR_VA + KS8695_T1PD);
114 138
115 /* re-enable timer1 */ 139 /*
116 writel_relaxed(tmcon | TMCON_T1EN, KS8695_TMR_VA + KS8695_TMCON); 140 * Use timer 1 to fire IRQs on the timeline, minimum 2 cycles
141 * (one on each counter) maximum 2*2^32, but the API will only
142 * accept up to a 32bit full word (0xFFFFFFFFU).
143 */
144 clockevents_config_and_register(&clockevent_ks8695,
145 KS8695_CLOCK_RATE, 2,
146 0xFFFFFFFFU);
117} 147}
118 148
119static void __init ks8695_timer_init (void) 149static void __init ks8695_timer_init (void)
@@ -126,7 +156,6 @@ static void __init ks8695_timer_init (void)
126 156
127struct sys_timer ks8695_timer = { 157struct sys_timer ks8695_timer = {
128 .init = ks8695_timer_init, 158 .init = ks8695_timer_init,
129 .offset = ks8695_gettimeoffset,
130}; 159};
131 160
132void ks8695_restart(char mode, const char *cmd) 161void ks8695_restart(char mode, const char *cmd)