aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86
diff options
context:
space:
mode:
authorDimitri Sivanich <sivanich@sgi.com>2009-03-04 13:59:18 -0500
committerIngo Molnar <mingo@elte.hu>2009-03-04 14:25:38 -0500
commit5ab5ab34498f94d60884c4ccea890601e429042e (patch)
tree3ec74793d32450a2f77c8e3804d30256845651ad /arch/x86
parent8661984f628c6f7d9cbaac6697f26d6b0be3ad3b (diff)
x86: UV, SGI RTC: add UV RTC clocksource/clockevents
This patch provides a high resolution clock/timer source using the SGI UV system-wide synchronized RTC clock/timer hardware. Signed-off-by: Dimitri Sivanich <sivanich@sgi.com> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: john stultz <johnstul@us.ibm.com> LKML-Reference: <20090304185918.GC24419@sgi.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86')
-rw-r--r--arch/x86/kernel/Makefile2
-rw-r--r--arch/x86/kernel/uv_time.c391
2 files changed, 392 insertions, 1 deletions
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 95f216bbfaf1..339ce35648e6 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -111,7 +111,7 @@ obj-$(CONFIG_SWIOTLB) += pci-swiotlb_64.o # NB rename without _64
111### 111###
112# 64 bit specific files 112# 64 bit specific files
113ifeq ($(CONFIG_X86_64),y) 113ifeq ($(CONFIG_X86_64),y)
114 obj-$(CONFIG_X86_UV) += tlb_uv.o bios_uv.o uv_irq.o uv_sysfs.o 114 obj-$(CONFIG_X86_UV) += tlb_uv.o bios_uv.o uv_irq.o uv_sysfs.o uv_time.o
115 obj-$(CONFIG_X86_PM_TIMER) += pmtimer_64.o 115 obj-$(CONFIG_X86_PM_TIMER) += pmtimer_64.o
116 obj-$(CONFIG_AUDIT) += audit_64.o 116 obj-$(CONFIG_AUDIT) += audit_64.o
117 117
diff --git a/arch/x86/kernel/uv_time.c b/arch/x86/kernel/uv_time.c
new file mode 100644
index 000000000000..6f8e3256ab2a
--- /dev/null
+++ b/arch/x86/kernel/uv_time.c
@@ -0,0 +1,391 @@
1/*
2 * SGI RTC clock/timer routines.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 *
18 * Copyright (c) 2009 Silicon Graphics, Inc. All Rights Reserved.
19 * Copyright (c) Dimitri Sivanich
20 */
21#include <linux/clockchips.h>
22
23#include <asm/uv/uv_mmrs.h>
24#include <asm/uv/uv_hub.h>
25#include <asm/uv/bios.h>
26#include <asm/uv/uv.h>
27
28#define RTC_NAME "sgi_rtc"
29
30static cycle_t uv_read_rtc(void);
31static int uv_rtc_next_event(unsigned long, struct clock_event_device *);
32static void uv_rtc_timer_setup(enum clock_event_mode,
33 struct clock_event_device *);
34
35static struct clocksource clocksource_uv = {
36 .name = RTC_NAME,
37 .rating = 400,
38 .read = uv_read_rtc,
39 .mask = (cycle_t)UVH_RTC_REAL_TIME_CLOCK_MASK,
40 .shift = 10,
41 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
42};
43
44static struct clock_event_device clock_event_device_uv = {
45 .name = RTC_NAME,
46 .features = CLOCK_EVT_FEAT_ONESHOT,
47 .shift = 20,
48 .rating = 400,
49 .irq = -1,
50 .set_next_event = uv_rtc_next_event,
51 .set_mode = uv_rtc_timer_setup,
52 .event_handler = NULL,
53};
54
55static DEFINE_PER_CPU(struct clock_event_device, cpu_ced);
56
57/* There is one of these allocated per node */
58struct uv_rtc_timer_head {
59 spinlock_t lock;
60 /* next cpu waiting for timer, local node relative: */
61 int next_cpu;
62 /* number of cpus on this node: */
63 int ncpus;
64 struct {
65 int lcpu; /* systemwide logical cpu number */
66 u64 expires; /* next timer expiration for this cpu */
67 } cpu[1];
68};
69
70/*
71 * Access to uv_rtc_timer_head via blade id.
72 */
73static struct uv_rtc_timer_head **blade_info __read_mostly;
74
75static int uv_rtc_enable;
76
77/*
78 * Hardware interface routines
79 */
80
81/* Send IPIs to another node */
82static void uv_rtc_send_IPI(int cpu)
83{
84 unsigned long apicid, val;
85 int pnode;
86
87 apicid = per_cpu(x86_cpu_to_apicid, cpu);
88 pnode = uv_apicid_to_pnode(apicid);
89 val = (1UL << UVH_IPI_INT_SEND_SHFT) |
90 (apicid << UVH_IPI_INT_APIC_ID_SHFT) |
91 (GENERIC_INTERRUPT_VECTOR << UVH_IPI_INT_VECTOR_SHFT);
92
93 uv_write_global_mmr64(pnode, UVH_IPI_INT, val);
94}
95
96/* Check for an RTC interrupt pending */
97static int uv_intr_pending(int pnode)
98{
99 return uv_read_global_mmr64(pnode, UVH_EVENT_OCCURRED0) &
100 UVH_EVENT_OCCURRED0_RTC1_MASK;
101}
102
103/* Setup interrupt and return non-zero if early expiration occurred. */
104static int uv_setup_intr(int cpu, u64 expires)
105{
106 u64 val;
107 int pnode = uv_cpu_to_pnode(cpu);
108
109 uv_write_global_mmr64(pnode, UVH_RTC1_INT_CONFIG,
110 UVH_RTC1_INT_CONFIG_M_MASK);
111 uv_write_global_mmr64(pnode, UVH_INT_CMPB, -1L);
112
113 uv_write_global_mmr64(pnode, UVH_EVENT_OCCURRED0_ALIAS,
114 UVH_EVENT_OCCURRED0_RTC1_MASK);
115
116 val = (GENERIC_INTERRUPT_VECTOR << UVH_RTC1_INT_CONFIG_VECTOR_SHFT) |
117 ((u64)cpu_physical_id(cpu) << UVH_RTC1_INT_CONFIG_APIC_ID_SHFT);
118
119 /* Set configuration */
120 uv_write_global_mmr64(pnode, UVH_RTC1_INT_CONFIG, val);
121 /* Initialize comparator value */
122 uv_write_global_mmr64(pnode, UVH_INT_CMPB, expires);
123
124 return (expires < uv_read_rtc() && !uv_intr_pending(pnode));
125}
126
127/*
128 * Per-cpu timer tracking routines
129 */
130
131static __init void uv_rtc_deallocate_timers(void)
132{
133 int bid;
134
135 for_each_possible_blade(bid) {
136 kfree(blade_info[bid]);
137 }
138 kfree(blade_info);
139}
140
141/* Allocate per-node list of cpu timer expiration times. */
142static __init int uv_rtc_allocate_timers(void)
143{
144 int cpu;
145
146 blade_info = kmalloc(uv_possible_blades * sizeof(void *), GFP_KERNEL);
147 if (!blade_info)
148 return -ENOMEM;
149 memset(blade_info, 0, uv_possible_blades * sizeof(void *));
150
151 for_each_present_cpu(cpu) {
152 int nid = cpu_to_node(cpu);
153 int bid = uv_cpu_to_blade_id(cpu);
154 int bcpu = uv_cpu_hub_info(cpu)->blade_processor_id;
155 struct uv_rtc_timer_head *head = blade_info[bid];
156
157 if (!head) {
158 head = kmalloc_node(sizeof(struct uv_rtc_timer_head) +
159 (uv_blade_nr_possible_cpus(bid) *
160 2 * sizeof(u64)),
161 GFP_KERNEL, nid);
162 if (!head) {
163 uv_rtc_deallocate_timers();
164 return -ENOMEM;
165 }
166 spin_lock_init(&head->lock);
167 head->ncpus = uv_blade_nr_possible_cpus(bid);
168 head->next_cpu = -1;
169 blade_info[bid] = head;
170 }
171
172 head->cpu[bcpu].lcpu = cpu;
173 head->cpu[bcpu].expires = ULLONG_MAX;
174 }
175
176 return 0;
177}
178
179/* Find and set the next expiring timer. */
180static void uv_rtc_find_next_timer(struct uv_rtc_timer_head *head, int pnode)
181{
182 u64 lowest = ULLONG_MAX;
183 int c, bcpu = -1;
184
185 head->next_cpu = -1;
186 for (c = 0; c < head->ncpus; c++) {
187 u64 exp = head->cpu[c].expires;
188 if (exp < lowest) {
189 bcpu = c;
190 lowest = exp;
191 }
192 }
193 if (bcpu >= 0) {
194 head->next_cpu = bcpu;
195 c = head->cpu[bcpu].lcpu;
196 if (uv_setup_intr(c, lowest))
197 /* If we didn't set it up in time, trigger */
198 uv_rtc_send_IPI(c);
199 } else {
200 uv_write_global_mmr64(pnode, UVH_RTC1_INT_CONFIG,
201 UVH_RTC1_INT_CONFIG_M_MASK);
202 }
203}
204
205/*
206 * Set expiration time for current cpu.
207 *
208 * Returns 1 if we missed the expiration time.
209 */
210static int uv_rtc_set_timer(int cpu, u64 expires)
211{
212 int pnode = uv_cpu_to_pnode(cpu);
213 int bid = uv_cpu_to_blade_id(cpu);
214 struct uv_rtc_timer_head *head = blade_info[bid];
215 int bcpu = uv_cpu_hub_info(cpu)->blade_processor_id;
216 u64 *t = &head->cpu[bcpu].expires;
217 unsigned long flags;
218 int next_cpu;
219
220 spin_lock_irqsave(&head->lock, flags);
221
222 next_cpu = head->next_cpu;
223 *t = expires;
224 /* Will this one be next to go off? */
225 if (next_cpu < 0 || bcpu == next_cpu ||
226 expires < head->cpu[next_cpu].expires) {
227 head->next_cpu = bcpu;
228 if (uv_setup_intr(cpu, expires)) {
229 *t = ULLONG_MAX;
230 uv_rtc_find_next_timer(head, pnode);
231 spin_unlock_irqrestore(&head->lock, flags);
232 return 1;
233 }
234 }
235
236 spin_unlock_irqrestore(&head->lock, flags);
237 return 0;
238}
239
240/*
241 * Unset expiration time for current cpu.
242 *
243 * Returns 1 if this timer was pending.
244 */
245static int uv_rtc_unset_timer(int cpu)
246{
247 int pnode = uv_cpu_to_pnode(cpu);
248 int bid = uv_cpu_to_blade_id(cpu);
249 struct uv_rtc_timer_head *head = blade_info[bid];
250 int bcpu = uv_cpu_hub_info(cpu)->blade_processor_id;
251 u64 *t = &head->cpu[bcpu].expires;
252 unsigned long flags;
253 int rc = 0;
254
255 spin_lock_irqsave(&head->lock, flags);
256
257 if (head->next_cpu == bcpu && uv_read_rtc() >= *t)
258 rc = 1;
259
260 *t = ULLONG_MAX;
261
262 /* Was the hardware setup for this timer? */
263 if (head->next_cpu == bcpu)
264 uv_rtc_find_next_timer(head, pnode);
265
266 spin_unlock_irqrestore(&head->lock, flags);
267
268 return rc;
269}
270
271
272/*
273 * Kernel interface routines.
274 */
275
276/*
277 * Read the RTC.
278 */
279static cycle_t uv_read_rtc(void)
280{
281 return (cycle_t)uv_read_local_mmr(UVH_RTC);
282}
283
284/*
285 * Program the next event, relative to now
286 */
287static int uv_rtc_next_event(unsigned long delta,
288 struct clock_event_device *ced)
289{
290 int ced_cpu = cpumask_first(ced->cpumask);
291
292 return uv_rtc_set_timer(ced_cpu, delta + uv_read_rtc());
293}
294
295/*
296 * Setup the RTC timer in oneshot mode
297 */
298static void uv_rtc_timer_setup(enum clock_event_mode mode,
299 struct clock_event_device *evt)
300{
301 int ced_cpu = cpumask_first(evt->cpumask);
302
303 switch (mode) {
304 case CLOCK_EVT_MODE_PERIODIC:
305 case CLOCK_EVT_MODE_ONESHOT:
306 case CLOCK_EVT_MODE_RESUME:
307 /* Nothing to do here yet */
308 break;
309 case CLOCK_EVT_MODE_UNUSED:
310 case CLOCK_EVT_MODE_SHUTDOWN:
311 uv_rtc_unset_timer(ced_cpu);
312 break;
313 }
314}
315
316static void uv_rtc_interrupt(void)
317{
318 struct clock_event_device *ced = &__get_cpu_var(cpu_ced);
319 int cpu = smp_processor_id();
320
321 if (!ced || !ced->event_handler)
322 return;
323
324 if (uv_rtc_unset_timer(cpu) != 1)
325 return;
326
327 ced->event_handler(ced);
328}
329
330static int __init uv_enable_rtc(char *str)
331{
332 uv_rtc_enable = 1;
333
334 return 1;
335}
336__setup("uvrtc", uv_enable_rtc);
337
338static __init void uv_rtc_register_clockevents(struct work_struct *dummy)
339{
340 struct clock_event_device *ced = &__get_cpu_var(cpu_ced);
341
342 *ced = clock_event_device_uv;
343 ced->cpumask = cpumask_of(smp_processor_id());
344 clockevents_register_device(ced);
345}
346
347static __init int uv_rtc_setup_clock(void)
348{
349 int rc;
350
351 if (!uv_rtc_enable || !is_uv_system() || generic_interrupt_extension)
352 return -ENODEV;
353
354 generic_interrupt_extension = uv_rtc_interrupt;
355
356 clocksource_uv.mult = clocksource_hz2mult(sn_rtc_cycles_per_second,
357 clocksource_uv.shift);
358
359 rc = clocksource_register(&clocksource_uv);
360 if (rc) {
361 generic_interrupt_extension = NULL;
362 return rc;
363 }
364
365 /* Setup and register clockevents */
366 rc = uv_rtc_allocate_timers();
367 if (rc) {
368 clocksource_unregister(&clocksource_uv);
369 generic_interrupt_extension = NULL;
370 return rc;
371 }
372
373 clock_event_device_uv.mult = div_sc(sn_rtc_cycles_per_second,
374 NSEC_PER_SEC, clock_event_device_uv.shift);
375
376 clock_event_device_uv.min_delta_ns = NSEC_PER_SEC /
377 sn_rtc_cycles_per_second;
378
379 clock_event_device_uv.max_delta_ns = clocksource_uv.mask *
380 (NSEC_PER_SEC / sn_rtc_cycles_per_second);
381
382 rc = schedule_on_each_cpu(uv_rtc_register_clockevents);
383 if (rc) {
384 clocksource_unregister(&clocksource_uv);
385 generic_interrupt_extension = NULL;
386 uv_rtc_deallocate_timers();
387 }
388
389 return rc;
390}
391arch_initcall(uv_rtc_setup_clock);