aboutsummaryrefslogtreecommitdiffstats
path: root/arch/tile/kernel/time.c
diff options
context:
space:
mode:
authorChris Metcalf <cmetcalf@tilera.com>2010-05-28 23:09:12 -0400
committerChris Metcalf <cmetcalf@tilera.com>2010-06-04 17:11:18 -0400
commit867e359b97c970a60626d5d76bbe2a8fadbf38fb (patch)
treec5ccbb7f5172e8555977119608ecb1eee3cc37e3 /arch/tile/kernel/time.c
parent5360bd776f73d0a7da571d72a09a03f237e99900 (diff)
arch/tile: core support for Tilera 32-bit chips.
This change is the core kernel support for TILEPro and TILE64 chips. No driver support (except the console driver) is included yet. This includes the relevant Linux headers in asm/; the low-level low-level "Tile architecture" headers in arch/, which are shared with the hypervisor, etc., and are build-system agnostic; and the relevant hypervisor headers in hv/. Signed-off-by: Chris Metcalf <cmetcalf@tilera.com> Acked-by: Arnd Bergmann <arnd@arndb.de> Acked-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> Reviewed-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'arch/tile/kernel/time.c')
-rw-r--r--arch/tile/kernel/time.c220
1 files changed, 220 insertions, 0 deletions
diff --git a/arch/tile/kernel/time.c b/arch/tile/kernel/time.c
new file mode 100644
index 000000000000..47500a324e32
--- /dev/null
+++ b/arch/tile/kernel/time.c
@@ -0,0 +1,220 @@
1/*
2 * Copyright 2010 Tilera Corporation. All Rights Reserved.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation, version 2.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
11 * NON INFRINGEMENT. See the GNU General Public License for
12 * more details.
13 *
14 * Support the cycle counter clocksource and tile timer clock event device.
15 */
16
17#include <linux/time.h>
18#include <linux/timex.h>
19#include <linux/clocksource.h>
20#include <linux/clockchips.h>
21#include <linux/hardirq.h>
22#include <linux/sched.h>
23#include <linux/smp.h>
24#include <linux/delay.h>
25#include <asm/irq_regs.h>
26#include <hv/hypervisor.h>
27#include <arch/interrupts.h>
28#include <arch/spr_def.h>
29
30
31/*
32 * Define the cycle counter clock source.
33 */
34
35/* How many cycles per second we are running at. */
36static cycles_t cycles_per_sec __write_once;
37
38/*
39 * We set up shift and multiply values with a minsec of five seconds,
40 * since our timer counter counts down 31 bits at a frequency of
41 * no less than 500 MHz. See @minsec for clocks_calc_mult_shift().
42 * We could use a different value for the 64-bit free-running
43 * cycle counter, but we use the same one for consistency, and since
44 * we will be reasonably precise with this value anyway.
45 */
46#define TILE_MINSEC 5
47
48cycles_t get_clock_rate()
49{
50 return cycles_per_sec;
51}
52
53#if CHIP_HAS_SPLIT_CYCLE()
54cycles_t get_cycles()
55{
56 unsigned int high = __insn_mfspr(SPR_CYCLE_HIGH);
57 unsigned int low = __insn_mfspr(SPR_CYCLE_LOW);
58 unsigned int high2 = __insn_mfspr(SPR_CYCLE_HIGH);
59
60 while (unlikely(high != high2)) {
61 low = __insn_mfspr(SPR_CYCLE_LOW);
62 high = high2;
63 high2 = __insn_mfspr(SPR_CYCLE_HIGH);
64 }
65
66 return (((cycles_t)high) << 32) | low;
67}
68#endif
69
70cycles_t clocksource_get_cycles(struct clocksource *cs)
71{
72 return get_cycles();
73}
74
75static struct clocksource cycle_counter_cs = {
76 .name = "cycle counter",
77 .rating = 300,
78 .read = clocksource_get_cycles,
79 .mask = CLOCKSOURCE_MASK(64),
80 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
81};
82
83/*
84 * Called very early from setup_arch() to set cycles_per_sec.
85 * We initialize it early so we can use it to set up loops_per_jiffy.
86 */
87void __init setup_clock(void)
88{
89 cycles_per_sec = hv_sysconf(HV_SYSCONF_CPU_SPEED);
90 clocksource_calc_mult_shift(&cycle_counter_cs, cycles_per_sec,
91 TILE_MINSEC);
92}
93
94void __init calibrate_delay(void)
95{
96 loops_per_jiffy = get_clock_rate() / HZ;
97 pr_info("Clock rate yields %lu.%02lu BogoMIPS (lpj=%lu)\n",
98 loops_per_jiffy/(500000/HZ),
99 (loops_per_jiffy/(5000/HZ)) % 100, loops_per_jiffy);
100}
101
102/* Called fairly late in init/main.c, but before we go smp. */
103void __init time_init(void)
104{
105 /* Initialize and register the clock source. */
106 clocksource_register(&cycle_counter_cs);
107
108 /* Start up the tile-timer interrupt source on the boot cpu. */
109 setup_tile_timer();
110}
111
112
113/*
114 * Define the tile timer clock event device. The timer is driven by
115 * the TILE_TIMER_CONTROL register, which consists of a 31-bit down
116 * counter, plus bit 31, which signifies that the counter has wrapped
117 * from zero to (2**31) - 1. The INT_TILE_TIMER interrupt will be
118 * raised as long as bit 31 is set.
119 */
120
121#define MAX_TICK 0x7fffffff /* we have 31 bits of countdown timer */
122
123static int tile_timer_set_next_event(unsigned long ticks,
124 struct clock_event_device *evt)
125{
126 BUG_ON(ticks > MAX_TICK);
127 __insn_mtspr(SPR_TILE_TIMER_CONTROL, ticks);
128 raw_local_irq_unmask_now(INT_TILE_TIMER);
129 return 0;
130}
131
132/*
133 * Whenever anyone tries to change modes, we just mask interrupts
134 * and wait for the next event to get set.
135 */
136static void tile_timer_set_mode(enum clock_event_mode mode,
137 struct clock_event_device *evt)
138{
139 raw_local_irq_mask_now(INT_TILE_TIMER);
140}
141
142/*
143 * Set min_delta_ns to 1 microsecond, since it takes about
144 * that long to fire the interrupt.
145 */
146static DEFINE_PER_CPU(struct clock_event_device, tile_timer) = {
147 .name = "tile timer",
148 .features = CLOCK_EVT_FEAT_ONESHOT,
149 .min_delta_ns = 1000,
150 .rating = 100,
151 .irq = -1,
152 .set_next_event = tile_timer_set_next_event,
153 .set_mode = tile_timer_set_mode,
154};
155
156void __cpuinit setup_tile_timer(void)
157{
158 struct clock_event_device *evt = &__get_cpu_var(tile_timer);
159
160 /* Fill in fields that are speed-specific. */
161 clockevents_calc_mult_shift(evt, cycles_per_sec, TILE_MINSEC);
162 evt->max_delta_ns = clockevent_delta2ns(MAX_TICK, evt);
163
164 /* Mark as being for this cpu only. */
165 evt->cpumask = cpumask_of(smp_processor_id());
166
167 /* Start out with timer not firing. */
168 raw_local_irq_mask_now(INT_TILE_TIMER);
169
170 /* Register tile timer. */
171 clockevents_register_device(evt);
172}
173
174/* Called from the interrupt vector. */
175void do_timer_interrupt(struct pt_regs *regs, int fault_num)
176{
177 struct pt_regs *old_regs = set_irq_regs(regs);
178 struct clock_event_device *evt = &__get_cpu_var(tile_timer);
179
180 /*
181 * Mask the timer interrupt here, since we are a oneshot timer
182 * and there are now by definition no events pending.
183 */
184 raw_local_irq_mask(INT_TILE_TIMER);
185
186 /* Track time spent here in an interrupt context */
187 irq_enter();
188
189 /* Track interrupt count. */
190 __get_cpu_var(irq_stat).irq_timer_count++;
191
192 /* Call the generic timer handler */
193 evt->event_handler(evt);
194
195 /*
196 * Track time spent against the current process again and
197 * process any softirqs if they are waiting.
198 */
199 irq_exit();
200
201 set_irq_regs(old_regs);
202}
203
204/*
205 * Scheduler clock - returns current time in nanosec units.
206 * Note that with LOCKDEP, this is called during lockdep_init(), and
207 * we will claim that sched_clock() is zero for a little while, until
208 * we run setup_clock(), above.
209 */
210unsigned long long sched_clock(void)
211{
212 return clocksource_cyc2ns(get_cycles(),
213 cycle_counter_cs.mult,
214 cycle_counter_cs.shift);
215}
216
217int setup_profiling_timer(unsigned int multiplier)
218{
219 return -EINVAL;
220}