aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/alchemy/common/time.c
diff options
context:
space:
mode:
authorManuel Lauss <mano@roarinelk.homelinux.net>2008-12-21 03:26:23 -0500
committerRalf Baechle <ralf@linux-mips.org>2009-01-11 04:57:27 -0500
commit0c694de12b54fa96b9555e07603f567906ce21c8 (patch)
treec7528273c1d86069cb6e83bd2b36706f663f1eb2 /arch/mips/alchemy/common/time.c
parent779e7d41ad004946603da139da99ba775f74cb1c (diff)
MIPS: Alchemy: RTC counter clocksource / clockevent support.
Add support for the 32 kHz counter1 (RTC) as clocksource / clockevent device. As a nice side effect, this also enables use of the 'wait' instruction for runtime idle power savings. If the counters aren't enabled/working properly, fall back on the cp0 counter clock code. Signed-off-by: Manuel Lauss <mano@roarinelk.homelinux.net> Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/alchemy/common/time.c')
-rw-r--r--arch/mips/alchemy/common/time.c292
1 files changed, 123 insertions, 169 deletions
diff --git a/arch/mips/alchemy/common/time.c b/arch/mips/alchemy/common/time.c
index 15185708ad8c..57f0aec590b8 100644
--- a/arch/mips/alchemy/common/time.c
+++ b/arch/mips/alchemy/common/time.c
@@ -1,5 +1,7 @@
1/* 1/*
2 * Copyright (C) 2008 Manuel Lauss <mano@roarinelk.homelinux.net>
2 * 3 *
4 * Previous incarnations were:
3 * Copyright (C) 2001, 2006, 2008 MontaVista Software, <source@mvista.com> 5 * Copyright (C) 2001, 2006, 2008 MontaVista Software, <source@mvista.com>
4 * Copied and modified Carsten Langgaard's time.c 6 * Copied and modified Carsten Langgaard's time.c
5 * 7 *
@@ -23,131 +25,27 @@
23 * 25 *
24 * ######################################################################## 26 * ########################################################################
25 * 27 *
26 * Setting up the clock on the MIPS boards. 28 * Clocksource/event using the 32.768kHz-clocked Counter1 ('RTC' in the
27 * 29 * databooks). Firmware/Board init code must enable the counters in the
28 * We provide the clock interrupt processing and the timer offset compute 30 * counter control register, otherwise the CP0 counter clocksource/event
29 * functions. If CONFIG_PM is selected, we also ensure the 32KHz timer is 31 * will be installed instead (and use of 'wait' instruction is prohibited).
30 * available. -- Dan
31 */ 32 */
32 33
33#include <linux/types.h> 34#include <linux/clockchips.h>
34#include <linux/init.h> 35#include <linux/clocksource.h>
36#include <linux/interrupt.h>
35#include <linux/spinlock.h> 37#include <linux/spinlock.h>
36 38
37#include <asm/mipsregs.h>
38#include <asm/time.h> 39#include <asm/time.h>
39#include <asm/mach-au1x00/au1000.h> 40#include <asm/mach-au1x00/au1000.h>
40 41
41static int no_au1xxx_32khz; 42/* 32kHz clock enabled and detected */
42extern int allow_au1k_wait; /* default off for CP0 Counter */ 43#define CNTR_OK (SYS_CNTRL_E0 | SYS_CNTRL_32S)
43 44
44#ifdef CONFIG_PM 45extern int allow_au1k_wait; /* default off for CP0 Counter */
45#if HZ < 100 || HZ > 1000
46#error "unsupported HZ value! Must be in [100,1000]"
47#endif
48#define MATCH20_INC (328 * 100 / HZ) /* magic number 328 is for HZ=100... */
49static unsigned long last_pc0, last_match20;
50#endif
51 46
52static DEFINE_SPINLOCK(time_lock); 47static DEFINE_SPINLOCK(time_lock);
53 48
54unsigned long wtimer;
55
56#ifdef CONFIG_PM
57static irqreturn_t counter0_irq(int irq, void *dev_id)
58{
59 unsigned long pc0;
60 int time_elapsed;
61 static int jiffie_drift;
62
63 if (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20) {
64 /* should never happen! */
65 printk(KERN_WARNING "counter 0 w status error\n");
66 return IRQ_NONE;
67 }
68
69 pc0 = au_readl(SYS_TOYREAD);
70 if (pc0 < last_match20)
71 /* counter overflowed */
72 time_elapsed = (0xffffffff - last_match20) + pc0;
73 else
74 time_elapsed = pc0 - last_match20;
75
76 while (time_elapsed > 0) {
77 do_timer(1);
78#ifndef CONFIG_SMP
79 update_process_times(user_mode(get_irq_regs()));
80#endif
81 time_elapsed -= MATCH20_INC;
82 last_match20 += MATCH20_INC;
83 jiffie_drift++;
84 }
85
86 last_pc0 = pc0;
87 au_writel(last_match20 + MATCH20_INC, SYS_TOYMATCH2);
88 au_sync();
89
90 /*
91 * Our counter ticks at 10.009765625 ms/tick, we we're running
92 * almost 10 uS too slow per tick.
93 */
94
95 if (jiffie_drift >= 999) {
96 jiffie_drift -= 999;
97 do_timer(1); /* increment jiffies by one */
98#ifndef CONFIG_SMP
99 update_process_times(user_mode(get_irq_regs()));
100#endif
101 }
102
103 return IRQ_HANDLED;
104}
105
106struct irqaction counter0_action = {
107 .handler = counter0_irq,
108 .flags = IRQF_DISABLED,
109 .name = "alchemy-toy",
110 .dev_id = NULL,
111};
112
113/* When we wakeup from sleep, we have to "catch up" on all of the
114 * timer ticks we have missed.
115 */
116void wakeup_counter0_adjust(void)
117{
118 unsigned long pc0;
119 int time_elapsed;
120
121 pc0 = au_readl(SYS_TOYREAD);
122 if (pc0 < last_match20)
123 /* counter overflowed */
124 time_elapsed = (0xffffffff - last_match20) + pc0;
125 else
126 time_elapsed = pc0 - last_match20;
127
128 while (time_elapsed > 0) {
129 time_elapsed -= MATCH20_INC;
130 last_match20 += MATCH20_INC;
131 }
132
133 last_pc0 = pc0;
134 au_writel(last_match20 + MATCH20_INC, SYS_TOYMATCH2);
135 au_sync();
136
137}
138
139/* This is just for debugging to set the timer for a sleep delay. */
140void wakeup_counter0_set(int ticks)
141{
142 unsigned long pc0;
143
144 pc0 = au_readl(SYS_TOYREAD);
145 last_pc0 = pc0;
146 au_writel(last_match20 + (MATCH20_INC * ticks), SYS_TOYMATCH2);
147 au_sync();
148}
149#endif
150
151/* 49/*
152 * I haven't found anyone that doesn't use a 12 MHz source clock, 50 * I haven't found anyone that doesn't use a 12 MHz source clock,
153 * but just in case..... 51 * but just in case.....
@@ -162,37 +60,15 @@ void wakeup_counter0_set(int ticks)
162 * this advertised speed will introduce error and sometimes not work 60 * this advertised speed will introduce error and sometimes not work
163 * properly. This function is futher convoluted to still allow configurations 61 * properly. This function is futher convoluted to still allow configurations
164 * to do that in case they have really, really old silicon with a 62 * to do that in case they have really, really old silicon with a
165 * write-only PLL register, that we need the 32 KHz when power management 63 * write-only PLL register. -- Dan
166 * "wait" is enabled, and we need to detect if the 32 KHz isn't present
167 * but requested......got it? :-) -- Dan
168 */ 64 */
169unsigned long calc_clock(void) 65unsigned long calc_clock(void)
170{ 66{
171 unsigned long cpu_speed; 67 unsigned long cpu_speed;
172 unsigned long flags; 68 unsigned long flags;
173 unsigned long counter;
174 69
175 spin_lock_irqsave(&time_lock, flags); 70 spin_lock_irqsave(&time_lock, flags);
176 71
177 /* Power management cares if we don't have a 32 KHz counter. */
178 no_au1xxx_32khz = 0;
179 counter = au_readl(SYS_COUNTER_CNTRL);
180 if (counter & SYS_CNTRL_E0) {
181 int trim_divide = 16;
182
183 au_writel(counter | SYS_CNTRL_EN1, SYS_COUNTER_CNTRL);
184
185 while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T1S);
186 /* RTC now ticks at 32.768/16 kHz */
187 au_writel(trim_divide - 1, SYS_RTCTRIM);
188 while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T1S);
189
190 while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S);
191 au_writel(0, SYS_TOYWRITE);
192 while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S);
193 } else
194 no_au1xxx_32khz = 1;
195
196 /* 72 /*
197 * On early Au1000, sys_cpupll was write-only. Since these 73 * On early Au1000, sys_cpupll was write-only. Since these
198 * silicon versions of Au1000 are not sold by AMD, we don't bend 74 * silicon versions of Au1000 are not sold by AMD, we don't bend
@@ -215,8 +91,65 @@ unsigned long calc_clock(void)
215 return cpu_speed; 91 return cpu_speed;
216} 92}
217 93
94static cycle_t au1x_counter1_read(void)
95{
96 return au_readl(SYS_RTCREAD);
97}
98
99static struct clocksource au1x_counter1_clocksource = {
100 .name = "alchemy-counter1",
101 .read = au1x_counter1_read,
102 .mask = CLOCKSOURCE_MASK(32),
103 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
104 .rating = 100,
105};
106
107static int au1x_rtcmatch2_set_next_event(unsigned long delta,
108 struct clock_event_device *cd)
109{
110 delta += au_readl(SYS_RTCREAD);
111 /* wait for register access */
112 while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M21)
113 ;
114 au_writel(delta, SYS_RTCMATCH2);
115 au_sync();
116
117 return 0;
118}
119
120static void au1x_rtcmatch2_set_mode(enum clock_event_mode mode,
121 struct clock_event_device *cd)
122{
123}
124
125static irqreturn_t au1x_rtcmatch2_irq(int irq, void *dev_id)
126{
127 struct clock_event_device *cd = dev_id;
128 cd->event_handler(cd);
129 return IRQ_HANDLED;
130}
131
132static struct clock_event_device au1x_rtcmatch2_clockdev = {
133 .name = "rtcmatch2",
134 .features = CLOCK_EVT_FEAT_ONESHOT,
135 .rating = 100,
136 .irq = AU1000_RTC_MATCH2_INT,
137 .set_next_event = au1x_rtcmatch2_set_next_event,
138 .set_mode = au1x_rtcmatch2_set_mode,
139 .cpumask = CPU_MASK_ALL,
140};
141
142static struct irqaction au1x_rtcmatch2_irqaction = {
143 .handler = au1x_rtcmatch2_irq,
144 .flags = IRQF_DISABLED | IRQF_TIMER,
145 .name = "timer",
146 .dev_id = &au1x_rtcmatch2_clockdev,
147};
148
218void __init plat_time_init(void) 149void __init plat_time_init(void)
219{ 150{
151 struct clock_event_device *cd = &au1x_rtcmatch2_clockdev;
152 unsigned long t;
220 unsigned int est_freq = calc_clock(); 153 unsigned int est_freq = calc_clock();
221 154
222 est_freq += 5000; /* round */ 155 est_freq += 5000; /* round */
@@ -225,41 +158,62 @@ void __init plat_time_init(void)
225 est_freq / 1000000, ((est_freq % 1000000) * 100) / 1000000); 158 est_freq / 1000000, ((est_freq % 1000000) * 100) / 1000000);
226 set_au1x00_speed(est_freq); 159 set_au1x00_speed(est_freq);
227 160
228#ifdef CONFIG_PM 161 /* Check if firmware (YAMON, ...) has enabled 32kHz and clock
229 /* 162 * has been detected. If so install the rtcmatch2 clocksource,
230 * setup counter 0, since it keeps ticking after a 163 * otherwise don't bother. Note that both bits being set is by
231 * 'wait' instruction has been executed. The CP0 timer and 164 * no means a definite guarantee that the counters actually work
232 * counter 1 do NOT continue running after 'wait' 165 * (the 32S bit seems to be stuck set to 1 once a single clock-
233 * 166 * edge is detected, hence the timeouts).
234 * It's too early to call request_irq() here, so we handle
235 * counter 0 interrupt as a special irq and it doesn't show
236 * up under /proc/interrupts.
237 *
238 * Check to ensure we really have a 32 KHz oscillator before
239 * we do this.
240 */ 167 */
241 if (no_au1xxx_32khz) 168 if (CNTR_OK != (au_readl(SYS_COUNTER_CNTRL) & CNTR_OK))
242 printk(KERN_WARNING "WARNING: no 32KHz clock found.\n"); 169 goto cntr_err;
243 else {
244 while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C0S);
245 au_writel(0, SYS_TOYWRITE);
246 while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C0S);
247 170
248 au_writel(au_readl(SYS_WAKEMSK) | (1 << 8), SYS_WAKEMSK); 171 /*
249 au_writel(~0, SYS_WAKESRC); 172 * setup counter 1 (RTC) to tick at full speed
250 au_sync(); 173 */
251 while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20); 174 t = 0xffffff;
175 while ((au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T1S) && t--)
176 asm volatile ("nop");
177 if (!t)
178 goto cntr_err;
252 179
253 /* Setup match20 to interrupt once every HZ */ 180 au_writel(0, SYS_RTCTRIM); /* 32.768 kHz */
254 last_pc0 = last_match20 = au_readl(SYS_TOYREAD); 181 au_sync();
255 au_writel(last_match20 + MATCH20_INC, SYS_TOYMATCH2);
256 au_sync();
257 while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20);
258 setup_irq(AU1000_TOY_MATCH2_INT, &counter0_action);
259 182
260 /* We can use the real 'wait' instruction. */ 183 t = 0xffffff;
261 allow_au1k_wait = 1; 184 while ((au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S) && t--)
262 } 185 asm volatile ("nop");
186 if (!t)
187 goto cntr_err;
188 au_writel(0, SYS_RTCWRITE);
189 au_sync();
263 190
264#endif 191 t = 0xffffff;
192 while ((au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S) && t--)
193 asm volatile ("nop");
194 if (!t)
195 goto cntr_err;
196
197 /* register counter1 clocksource and event device */
198 clocksource_set_clock(&au1x_counter1_clocksource, 32768);
199 clocksource_register(&au1x_counter1_clocksource);
200
201 cd->shift = 32;
202 cd->mult = div_sc(32768, NSEC_PER_SEC, cd->shift);
203 cd->max_delta_ns = clockevent_delta2ns(0xffffffff, cd);
204 cd->min_delta_ns = clockevent_delta2ns(8, cd); /* ~0.25ms */
205 clockevents_register_device(cd);
206 setup_irq(AU1000_RTC_MATCH2_INT, &au1x_rtcmatch2_irqaction);
207
208 printk(KERN_INFO "Alchemy clocksource installed\n");
209
210 /* can now use 'wait' */
211 allow_au1k_wait = 1;
212 return;
213
214cntr_err:
215 /* counters unusable, use C0 counter */
216 r4k_clockevent_init();
217 init_r4k_clocksource();
218 allow_au1k_wait = 0;
265} 219}