blob: bf2f762e6a476e71c3f3c17824972e6171f8f933 [file] [log] [blame]
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -07001/*
Hans-Christian Egtvedt77609892007-03-12 18:15:16 +01002 * Copyright (C) 2004-2007 Atmel Corporation
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -07003 *
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>
Hans-Christian Egtvedt77609892007-03-12 18:15:16 +010023#include <linux/err.h>
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070024
25#include <asm/div64.h>
26#include <asm/sysreg.h>
27#include <asm/io.h>
28#include <asm/sections.h>
29
Hans-Christian Egtvedt77609892007-03-12 18:15:16 +010030/* how many counter cycles in a jiffy? */
31static u32 cycles_per_jiffy;
32
33/* the count value for the next timer interrupt */
34static u32 expirelo;
35
36cycle_t __weak read_cycle_count(void)
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070037{
38 return (cycle_t)sysreg_read(COUNT);
39}
40
David Brownell62c6df62008-02-12 14:45:49 -080041/*
42 * The architectural cycle count registers are a fine clocksource unless
43 * the system idle loop use sleep states like "idle": the CPU cycles
44 * measured by COUNT (and COMPARE) don't happen during sleep states.
45 * So we rate the clocksource using COUNT as very low quality.
46 */
Hans-Christian Egtvedt77609892007-03-12 18:15:16 +010047struct clocksource __weak clocksource_avr32 = {
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070048 .name = "avr32",
David Brownell62c6df62008-02-12 14:45:49 -080049 .rating = 50,
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070050 .read = read_cycle_count,
51 .mask = CLOCKSOURCE_MASK(32),
52 .shift = 16,
Thomas Gleixner26935062007-02-16 01:27:38 -080053 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070054};
55
Hans-Christian Egtvedt77609892007-03-12 18:15:16 +010056irqreturn_t __weak timer_interrupt(int irq, void *dev_id);
57
58struct irqaction timer_irqaction = {
59 .handler = timer_interrupt,
60 .flags = IRQF_DISABLED,
61 .name = "timer",
62};
63
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070064static void avr32_timer_ack(void)
65{
Hans-Christian Egtvedt77609892007-03-12 18:15:16 +010066 u32 count;
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070067
68 /* Ack this timer interrupt and set the next one */
69 expirelo += cycles_per_jiffy;
Hans-Christian Egtvedt77609892007-03-12 18:15:16 +010070 /* setting COMPARE to 0 stops the COUNT-COMPARE */
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070071 if (expirelo == 0) {
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070072 sysreg_write(COMPARE, expirelo + 1);
73 } else {
74 sysreg_write(COMPARE, expirelo);
75 }
76
77 /* Check to see if we have missed any timer interrupts */
78 count = sysreg_read(COUNT);
79 if ((count - expirelo) < 0x7fffffff) {
80 expirelo = count + cycles_per_jiffy;
81 sysreg_write(COMPARE, expirelo);
82 }
83}
84
Hans-Christian Egtvedt77609892007-03-12 18:15:16 +010085int __weak avr32_hpt_init(void)
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -070086{
Hans-Christian Egtvedt77609892007-03-12 18:15:16 +010087 int ret;
88 unsigned long mult, shift, count_hz;
89
90 count_hz = clk_get_rate(boot_cpu_data.clk);
91 shift = clocksource_avr32.shift;
92 mult = clocksource_hz2mult(count_hz, shift);
93 clocksource_avr32.mult = mult;
94
95 {
96 u64 tmp;
97
98 tmp = TICK_NSEC;
99 tmp <<= shift;
100 tmp += mult / 2;
101 do_div(tmp, mult);
102
103 cycles_per_jiffy = tmp;
104 }
105
106 ret = setup_irq(0, &timer_irqaction);
107 if (ret) {
108 pr_debug("timer: could not request IRQ 0: %d\n", ret);
109 return -ENODEV;
110 }
111
112 printk(KERN_INFO "timer: AT32AP COUNT-COMPARE at irq 0, "
113 "%lu.%03lu MHz\n",
114 ((count_hz + 500) / 1000) / 1000,
115 ((count_hz + 500) / 1000) % 1000);
116
117 return 0;
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700118}
119
120/*
121 * Taken from MIPS c0_hpt_timer_init().
122 *
Hans-Christian Egtvedt77609892007-03-12 18:15:16 +0100123 * The reason COUNT is written twice is probably to make sure we don't get any
124 * timer interrupts while we are messing with the counter.
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700125 */
Hans-Christian Egtvedt77609892007-03-12 18:15:16 +0100126int __weak avr32_hpt_start(void)
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700127{
Hans-Christian Egtvedt77609892007-03-12 18:15:16 +0100128 u32 count = sysreg_read(COUNT);
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700129 expirelo = (count / cycles_per_jiffy + 1) * cycles_per_jiffy;
130 sysreg_write(COUNT, expirelo - cycles_per_jiffy);
131 sysreg_write(COMPARE, expirelo);
132 sysreg_write(COUNT, count);
Hans-Christian Egtvedt77609892007-03-12 18:15:16 +0100133
134 return 0;
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700135}
136
137/*
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700138 * local_timer_interrupt() does profiling and process accounting on a
139 * per-CPU basis.
140 *
141 * In UP mode, it is invoked from the (global) timer_interrupt.
142 */
Hans-Christian Egtvedt77609892007-03-12 18:15:16 +0100143void local_timer_interrupt(int irq, void *dev_id)
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700144{
145 if (current->pid)
Haavard Skinnemoen4e0fadf2006-10-11 01:20:37 -0700146 profile_tick(CPU_PROFILING);
147 update_process_times(user_mode(get_irq_regs()));
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700148}
149
Hans-Christian Egtvedt77609892007-03-12 18:15:16 +0100150irqreturn_t __weak timer_interrupt(int irq, void *dev_id)
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700151{
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700152 /* ack timer interrupt and try to set next interrupt */
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700153 avr32_timer_ack();
154
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700155 /*
156 * Call the generic timer interrupt handler
157 */
158 write_seqlock(&xtime_lock);
Atsushi Nemoto3171a032006-09-29 02:00:32 -0700159 do_timer(1);
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700160 write_sequnlock(&xtime_lock);
161
162 /*
163 * In UP mode, we call local_timer_interrupt() to do profiling
164 * and process accounting.
165 *
166 * SMP is not supported yet.
167 */
Haavard Skinnemoen4e0fadf2006-10-11 01:20:37 -0700168 local_timer_interrupt(irq, dev_id);
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700169
170 return IRQ_HANDLED;
171}
172
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700173void __init time_init(void)
174{
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700175 int ret;
176
Hans-Christian Egtvedt77609892007-03-12 18:15:16 +0100177 /*
178 * Make sure we don't get any COMPARE interrupts before we can
179 * handle them.
180 */
181 sysreg_write(COMPARE, 0);
182
David Brownell62c6df62008-02-12 14:45:49 -0800183 xtime.tv_sec = mktime(2007, 1, 1, 0, 0, 0);
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700184 xtime.tv_nsec = 0;
185
186 set_normalized_timespec(&wall_to_monotonic,
187 -xtime.tv_sec, -xtime.tv_nsec);
188
Hans-Christian Egtvedt77609892007-03-12 18:15:16 +0100189 ret = avr32_hpt_init();
190 if (ret) {
191 pr_debug("timer: failed setup: %d\n", ret);
192 return;
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700193 }
194
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700195 ret = clocksource_register(&clocksource_avr32);
196 if (ret)
Hans-Christian Egtvedt77609892007-03-12 18:15:16 +0100197 pr_debug("timer: could not register clocksource: %d\n", ret);
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700198
Hans-Christian Egtvedt77609892007-03-12 18:15:16 +0100199 ret = avr32_hpt_start();
200 if (ret) {
201 pr_debug("timer: failed starting: %d\n", ret);
202 return;
203 }
Haavard Skinnemoen5f97f7f2006-09-25 23:32:13 -0700204}