Merge branch 'upstream'
[pandora-kernel.git] / arch / arm / mach-realview / localtimer.c
1 /*
2  *  linux/arch/arm/mach-realview/localtimer.c
3  *
4  *  Copyright (C) 2002 ARM Ltd.
5  *  All Rights Reserved
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 #include <linux/init.h>
12 #include <linux/kernel.h>
13 #include <linux/delay.h>
14 #include <linux/device.h>
15 #include <linux/smp.h>
16 #include <linux/jiffies.h>
17
18 #include <asm/mach/time.h>
19 #include <asm/hardware/arm_twd.h>
20 #include <asm/hardware/gic.h>
21 #include <asm/hardware.h>
22 #include <asm/io.h>
23 #include <asm/irq.h>
24
25 #define TWD_BASE(cpu)   (__io_address(REALVIEW_TWD_BASE) + \
26                          ((cpu) * REALVIEW_TWD_SIZE))
27
28 static unsigned long mpcore_timer_rate;
29
30 /*
31  * local_timer_ack: checks for a local timer interrupt.
32  *
33  * If a local timer interrupt has occured, acknowledge and return 1.
34  * Otherwise, return 0.
35  */
36 int local_timer_ack(void)
37 {
38         void __iomem *base = TWD_BASE(smp_processor_id());
39
40         if (__raw_readl(base + TWD_TIMER_INTSTAT)) {
41                 __raw_writel(1, base + TWD_TIMER_INTSTAT);
42                 return 1;
43         }
44
45         return 0;
46 }
47
48 void __cpuinit local_timer_setup(unsigned int cpu)
49 {
50         void __iomem *base = TWD_BASE(cpu);
51         unsigned int load, offset;
52         u64 waitjiffies;
53         unsigned int count;
54
55         /*
56          * If this is the first time round, we need to work out how fast
57          * the timer ticks
58          */
59         if (mpcore_timer_rate == 0) {
60                 printk("Calibrating local timer... ");
61
62                 /* Wait for a tick to start */
63                 waitjiffies = get_jiffies_64() + 1;
64
65                 while (get_jiffies_64() < waitjiffies)
66                         udelay(10);
67
68                 /* OK, now the tick has started, let's get the timer going */
69                 waitjiffies += 5;
70
71                                  /* enable, no interrupt or reload */
72                 __raw_writel(0x1, base + TWD_TIMER_CONTROL);
73
74                                  /* maximum value */
75                 __raw_writel(0xFFFFFFFFU, base + TWD_TIMER_COUNTER);
76
77                 while (get_jiffies_64() < waitjiffies)
78                         udelay(10);
79
80                 count = __raw_readl(base + TWD_TIMER_COUNTER);
81
82                 mpcore_timer_rate = (0xFFFFFFFFU - count) * (HZ / 5);
83
84                 printk("%lu.%02luMHz.\n", mpcore_timer_rate / 1000000,
85                         (mpcore_timer_rate / 100000) % 100);
86         }
87
88         load = mpcore_timer_rate / HZ;
89
90         __raw_writel(load, base + TWD_TIMER_LOAD);
91         __raw_writel(0x7,  base + TWD_TIMER_CONTROL);
92
93         /*
94          * Now maneuver our local tick into the right part of the jiffy.
95          * Start by working out where within the tick our local timer
96          * interrupt should go.
97          */
98         offset = ((mpcore_timer_rate / HZ) / (NR_CPUS + 1)) * (cpu + 1);
99
100         /*
101          * gettimeoffset() will return a number of us since the last tick.
102          * Convert this number of us to a local timer tick count.
103          * Be careful of integer overflow whilst keeping maximum precision.
104          *
105          * with HZ=100 and 1MHz (fpga) ~ 1GHz processor:
106          * load = 1 ~ 10,000
107          * mpcore_timer_rate/10000 = 100 ~ 100,000
108          *
109          * so the multiply value will be less than 10^9 always.
110          */
111         load = (system_timer->offset() * (mpcore_timer_rate / 10000)) / 100;
112
113         /* Add on our offset to get the load value */
114         load = (load + offset) % (mpcore_timer_rate / HZ);
115
116         __raw_writel(load, base + TWD_TIMER_COUNTER);
117
118         /* Make sure our local interrupt controller has this enabled */
119         __raw_writel(1 << IRQ_LOCALTIMER,
120                      __io_address(REALVIEW_GIC_DIST_BASE) + GIC_DIST_ENABLE_SET);
121 }
122
123 /*
124  * take a local timer down
125  */
126 void __cpuexit local_timer_stop(unsigned int cpu)
127 {
128         __raw_writel(0, TWD_BASE(cpu) + TWD_TIMER_CONTROL);
129 }