]>
Commit | Line | Data |
---|---|---|
5f97f7f9 | 1 | /* |
7760989e | 2 | * Copyright (C) 2004-2007 Atmel Corporation |
5f97f7f9 HS |
3 | * |
4 | * Based on MIPS implementation arch/mips/kernel/time.c | |
5 | * Copyright 2001 MontaVista Software Inc. | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License version 2 as | |
9 | * published by the Free Software Foundation. | |
10 | */ | |
11 | ||
12 | #include <linux/clk.h> | |
13 | #include <linux/clocksource.h> | |
14 | #include <linux/time.h> | |
15 | #include <linux/module.h> | |
16 | #include <linux/interrupt.h> | |
17 | #include <linux/irq.h> | |
18 | #include <linux/kernel_stat.h> | |
19 | #include <linux/errno.h> | |
20 | #include <linux/init.h> | |
21 | #include <linux/profile.h> | |
22 | #include <linux/sysdev.h> | |
7760989e | 23 | #include <linux/err.h> |
5f97f7f9 HS |
24 | |
25 | #include <asm/div64.h> | |
26 | #include <asm/sysreg.h> | |
27 | #include <asm/io.h> | |
28 | #include <asm/sections.h> | |
29 | ||
7760989e HCE |
30 | /* how many counter cycles in a jiffy? */ |
31 | static u32 cycles_per_jiffy; | |
32 | ||
33 | /* the count value for the next timer interrupt */ | |
34 | static u32 expirelo; | |
35 | ||
36 | cycle_t __weak read_cycle_count(void) | |
5f97f7f9 HS |
37 | { |
38 | return (cycle_t)sysreg_read(COUNT); | |
39 | } | |
40 | ||
7760989e | 41 | struct clocksource __weak clocksource_avr32 = { |
5f97f7f9 HS |
42 | .name = "avr32", |
43 | .rating = 350, | |
44 | .read = read_cycle_count, | |
45 | .mask = CLOCKSOURCE_MASK(32), | |
46 | .shift = 16, | |
2693506c | 47 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, |
5f97f7f9 HS |
48 | }; |
49 | ||
7760989e HCE |
50 | irqreturn_t __weak timer_interrupt(int irq, void *dev_id); |
51 | ||
52 | struct irqaction timer_irqaction = { | |
53 | .handler = timer_interrupt, | |
54 | .flags = IRQF_DISABLED, | |
55 | .name = "timer", | |
56 | }; | |
57 | ||
5f97f7f9 HS |
58 | /* |
59 | * By default we provide the null RTC ops | |
60 | */ | |
61 | static unsigned long null_rtc_get_time(void) | |
62 | { | |
7760989e | 63 | return mktime(2007, 1, 1, 0, 0, 0); |
5f97f7f9 HS |
64 | } |
65 | ||
66 | static int null_rtc_set_time(unsigned long sec) | |
67 | { | |
68 | return 0; | |
69 | } | |
70 | ||
71 | static unsigned long (*rtc_get_time)(void) = null_rtc_get_time; | |
72 | static int (*rtc_set_time)(unsigned long) = null_rtc_set_time; | |
73 | ||
5f97f7f9 HS |
74 | static void avr32_timer_ack(void) |
75 | { | |
7760989e | 76 | u32 count; |
5f97f7f9 HS |
77 | |
78 | /* Ack this timer interrupt and set the next one */ | |
79 | expirelo += cycles_per_jiffy; | |
7760989e | 80 | /* setting COMPARE to 0 stops the COUNT-COMPARE */ |
5f97f7f9 | 81 | if (expirelo == 0) { |
5f97f7f9 HS |
82 | sysreg_write(COMPARE, expirelo + 1); |
83 | } else { | |
84 | sysreg_write(COMPARE, expirelo); | |
85 | } | |
86 | ||
87 | /* Check to see if we have missed any timer interrupts */ | |
88 | count = sysreg_read(COUNT); | |
89 | if ((count - expirelo) < 0x7fffffff) { | |
90 | expirelo = count + cycles_per_jiffy; | |
91 | sysreg_write(COMPARE, expirelo); | |
92 | } | |
93 | } | |
94 | ||
7760989e | 95 | int __weak avr32_hpt_init(void) |
5f97f7f9 | 96 | { |
7760989e HCE |
97 | int ret; |
98 | unsigned long mult, shift, count_hz; | |
99 | ||
100 | count_hz = clk_get_rate(boot_cpu_data.clk); | |
101 | shift = clocksource_avr32.shift; | |
102 | mult = clocksource_hz2mult(count_hz, shift); | |
103 | clocksource_avr32.mult = mult; | |
104 | ||
105 | { | |
106 | u64 tmp; | |
107 | ||
108 | tmp = TICK_NSEC; | |
109 | tmp <<= shift; | |
110 | tmp += mult / 2; | |
111 | do_div(tmp, mult); | |
112 | ||
113 | cycles_per_jiffy = tmp; | |
114 | } | |
115 | ||
116 | ret = setup_irq(0, &timer_irqaction); | |
117 | if (ret) { | |
118 | pr_debug("timer: could not request IRQ 0: %d\n", ret); | |
119 | return -ENODEV; | |
120 | } | |
121 | ||
122 | printk(KERN_INFO "timer: AT32AP COUNT-COMPARE at irq 0, " | |
123 | "%lu.%03lu MHz\n", | |
124 | ((count_hz + 500) / 1000) / 1000, | |
125 | ((count_hz + 500) / 1000) % 1000); | |
126 | ||
127 | return 0; | |
5f97f7f9 HS |
128 | } |
129 | ||
130 | /* | |
131 | * Taken from MIPS c0_hpt_timer_init(). | |
132 | * | |
7760989e HCE |
133 | * The reason COUNT is written twice is probably to make sure we don't get any |
134 | * timer interrupts while we are messing with the counter. | |
5f97f7f9 | 135 | */ |
7760989e | 136 | int __weak avr32_hpt_start(void) |
5f97f7f9 | 137 | { |
7760989e | 138 | u32 count = sysreg_read(COUNT); |
5f97f7f9 HS |
139 | expirelo = (count / cycles_per_jiffy + 1) * cycles_per_jiffy; |
140 | sysreg_write(COUNT, expirelo - cycles_per_jiffy); | |
141 | sysreg_write(COMPARE, expirelo); | |
142 | sysreg_write(COUNT, count); | |
7760989e HCE |
143 | |
144 | return 0; | |
5f97f7f9 HS |
145 | } |
146 | ||
5f97f7f9 HS |
147 | /* |
148 | * local_timer_interrupt() does profiling and process accounting on a | |
149 | * per-CPU basis. | |
150 | * | |
151 | * In UP mode, it is invoked from the (global) timer_interrupt. | |
152 | */ | |
7760989e | 153 | void local_timer_interrupt(int irq, void *dev_id) |
5f97f7f9 HS |
154 | { |
155 | if (current->pid) | |
4e0fadfc HS |
156 | profile_tick(CPU_PROFILING); |
157 | update_process_times(user_mode(get_irq_regs())); | |
5f97f7f9 HS |
158 | } |
159 | ||
7760989e | 160 | irqreturn_t __weak timer_interrupt(int irq, void *dev_id) |
5f97f7f9 | 161 | { |
5f97f7f9 | 162 | /* ack timer interrupt and try to set next interrupt */ |
5f97f7f9 HS |
163 | avr32_timer_ack(); |
164 | ||
5f97f7f9 HS |
165 | /* |
166 | * Call the generic timer interrupt handler | |
167 | */ | |
168 | write_seqlock(&xtime_lock); | |
3171a030 | 169 | do_timer(1); |
5f97f7f9 HS |
170 | write_sequnlock(&xtime_lock); |
171 | ||
172 | /* | |
173 | * In UP mode, we call local_timer_interrupt() to do profiling | |
174 | * and process accounting. | |
175 | * | |
176 | * SMP is not supported yet. | |
177 | */ | |
4e0fadfc | 178 | local_timer_interrupt(irq, dev_id); |
5f97f7f9 HS |
179 | |
180 | return IRQ_HANDLED; | |
181 | } | |
182 | ||
5f97f7f9 HS |
183 | void __init time_init(void) |
184 | { | |
5f97f7f9 HS |
185 | int ret; |
186 | ||
7760989e HCE |
187 | /* |
188 | * Make sure we don't get any COMPARE interrupts before we can | |
189 | * handle them. | |
190 | */ | |
191 | sysreg_write(COMPARE, 0); | |
192 | ||
5f97f7f9 HS |
193 | xtime.tv_sec = rtc_get_time(); |
194 | xtime.tv_nsec = 0; | |
195 | ||
196 | set_normalized_timespec(&wall_to_monotonic, | |
197 | -xtime.tv_sec, -xtime.tv_nsec); | |
198 | ||
7760989e HCE |
199 | ret = avr32_hpt_init(); |
200 | if (ret) { | |
201 | pr_debug("timer: failed setup: %d\n", ret); | |
202 | return; | |
5f97f7f9 HS |
203 | } |
204 | ||
5f97f7f9 HS |
205 | ret = clocksource_register(&clocksource_avr32); |
206 | if (ret) | |
7760989e | 207 | pr_debug("timer: could not register clocksource: %d\n", ret); |
5f97f7f9 | 208 | |
7760989e HCE |
209 | ret = avr32_hpt_start(); |
210 | if (ret) { | |
211 | pr_debug("timer: failed starting: %d\n", ret); | |
212 | return; | |
213 | } | |
5f97f7f9 HS |
214 | } |
215 | ||
216 | static struct sysdev_class timer_class = { | |
af5ca3f4 | 217 | .name = "timer", |
5f97f7f9 HS |
218 | }; |
219 | ||
220 | static struct sys_device timer_device = { | |
221 | .id = 0, | |
222 | .cls = &timer_class, | |
223 | }; | |
224 | ||
225 | static int __init init_timer_sysfs(void) | |
226 | { | |
227 | int err = sysdev_class_register(&timer_class); | |
228 | if (!err) | |
229 | err = sysdev_register(&timer_device); | |
230 | return err; | |
231 | } | |
232 | ||
233 | device_initcall(init_timer_sysfs); |