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