diff options
Diffstat (limited to 'arch/sparc/kernel')
-rw-r--r-- | arch/sparc/kernel/irq.h | 19 | ||||
-rw-r--r-- | arch/sparc/kernel/kernel.h | 2 | ||||
-rw-r--r-- | arch/sparc/kernel/leon_kernel.c | 49 | ||||
-rw-r--r-- | arch/sparc/kernel/leon_smp.c | 34 | ||||
-rw-r--r-- | arch/sparc/kernel/pcic.c | 48 | ||||
-rw-r--r-- | arch/sparc/kernel/smp_32.c | 21 | ||||
-rw-r--r-- | arch/sparc/kernel/sun4c_irq.c | 11 | ||||
-rw-r--r-- | arch/sparc/kernel/sun4d_irq.c | 18 | ||||
-rw-r--r-- | arch/sparc/kernel/sun4d_smp.c | 34 | ||||
-rw-r--r-- | arch/sparc/kernel/sun4m_irq.c | 22 | ||||
-rw-r--r-- | arch/sparc/kernel/sun4m_smp.c | 38 | ||||
-rw-r--r-- | arch/sparc/kernel/time_32.c | 215 |
12 files changed, 331 insertions, 180 deletions
diff --git a/arch/sparc/kernel/irq.h b/arch/sparc/kernel/irq.h index 3f6ca4d55dc4..8b946b1bc3b9 100644 --- a/arch/sparc/kernel/irq.h +++ b/arch/sparc/kernel/irq.h | |||
@@ -41,15 +41,32 @@ struct sun4m_irq_global { | |||
41 | extern struct sun4m_irq_percpu __iomem *sun4m_irq_percpu[SUN4M_NCPUS]; | 41 | extern struct sun4m_irq_percpu __iomem *sun4m_irq_percpu[SUN4M_NCPUS]; |
42 | extern struct sun4m_irq_global __iomem *sun4m_irq_global; | 42 | extern struct sun4m_irq_global __iomem *sun4m_irq_global; |
43 | 43 | ||
44 | /* The following definitions describe the individual platform features: */ | ||
45 | #define FEAT_L10_CLOCKSOURCE (1 << 0) /* L10 timer is used as a clocksource */ | ||
46 | #define FEAT_L10_CLOCKEVENT (1 << 1) /* L10 timer is used as a clockevent */ | ||
47 | #define FEAT_L14_ONESHOT (1 << 2) /* L14 timer clockevent can oneshot */ | ||
48 | |||
44 | /* | 49 | /* |
45 | * Platform specific configuration | 50 | * Platform specific configuration |
46 | * The individual platforms assign their platform | 51 | * The individual platforms assign their platform |
47 | * specifics in their init functions. | 52 | * specifics in their init functions. |
48 | */ | 53 | */ |
49 | struct sparc_config { | 54 | struct sparc_config { |
50 | void (*init_timers)(irq_handler_t); | 55 | void (*init_timers)(void); |
51 | unsigned int (*build_device_irq)(struct platform_device *op, | 56 | unsigned int (*build_device_irq)(struct platform_device *op, |
52 | unsigned int real_irq); | 57 | unsigned int real_irq); |
58 | |||
59 | /* generic clockevent features - see FEAT_* above */ | ||
60 | int features; | ||
61 | |||
62 | /* clock rate used for clock event timer */ | ||
63 | int clock_rate; | ||
64 | |||
65 | /* one period for clock source timer */ | ||
66 | unsigned int cs_period; | ||
67 | |||
68 | /* function to obtain offsett for cs period */ | ||
69 | unsigned int (*get_cycles_offset)(void); | ||
53 | }; | 70 | }; |
54 | extern struct sparc_config sparc_config; | 71 | extern struct sparc_config sparc_config; |
55 | 72 | ||
diff --git a/arch/sparc/kernel/kernel.h b/arch/sparc/kernel/kernel.h index fd6c36b1df74..8abbad38e34e 100644 --- a/arch/sparc/kernel/kernel.h +++ b/arch/sparc/kernel/kernel.h | |||
@@ -47,8 +47,6 @@ extern void init_IRQ(void); | |||
47 | extern void sun4c_init_IRQ(void); | 47 | extern void sun4c_init_IRQ(void); |
48 | 48 | ||
49 | /* sun4m_irq.c */ | 49 | /* sun4m_irq.c */ |
50 | extern unsigned int lvl14_resolution; | ||
51 | |||
52 | extern void sun4m_init_IRQ(void); | 50 | extern void sun4m_init_IRQ(void); |
53 | extern void sun4m_unmask_profile_irq(void); | 51 | extern void sun4m_unmask_profile_irq(void); |
54 | extern void sun4m_clear_profile_irq(int cpu); | 52 | extern void sun4m_clear_profile_irq(int cpu); |
diff --git a/arch/sparc/kernel/leon_kernel.c b/arch/sparc/kernel/leon_kernel.c index a94122bc0c7b..722650ab83da 100644 --- a/arch/sparc/kernel/leon_kernel.c +++ b/arch/sparc/kernel/leon_kernel.c | |||
@@ -10,6 +10,8 @@ | |||
10 | #include <linux/of_platform.h> | 10 | #include <linux/of_platform.h> |
11 | #include <linux/interrupt.h> | 11 | #include <linux/interrupt.h> |
12 | #include <linux/of_device.h> | 12 | #include <linux/of_device.h> |
13 | #include <linux/clocksource.h> | ||
14 | #include <linux/clockchips.h> | ||
13 | 15 | ||
14 | #include <asm/oplib.h> | 16 | #include <asm/oplib.h> |
15 | #include <asm/timer.h> | 17 | #include <asm/timer.h> |
@@ -250,7 +252,38 @@ void leon_update_virq_handling(unsigned int virq, | |||
250 | irq_set_chip_data(virq, (void *)mask); | 252 | irq_set_chip_data(virq, (void *)mask); |
251 | } | 253 | } |
252 | 254 | ||
253 | void __init leon_init_timers(irq_handler_t counter_fn) | 255 | static u32 leon_cycles_offset(void) |
256 | { | ||
257 | u32 rld, val, off; | ||
258 | rld = LEON3_BYPASS_LOAD_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].rld); | ||
259 | val = LEON3_BYPASS_LOAD_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].val); | ||
260 | off = rld - val; | ||
261 | return rld - val; | ||
262 | } | ||
263 | |||
264 | #ifdef CONFIG_SMP | ||
265 | |||
266 | /* smp clockevent irq */ | ||
267 | irqreturn_t leon_percpu_timer_ce_interrupt(int irq, void *unused) | ||
268 | { | ||
269 | struct clock_event_device *ce; | ||
270 | int cpu = smp_processor_id(); | ||
271 | |||
272 | leon_clear_profile_irq(cpu); | ||
273 | |||
274 | ce = &per_cpu(sparc32_clockevent, cpu); | ||
275 | |||
276 | irq_enter(); | ||
277 | if (ce->event_handler) | ||
278 | ce->event_handler(ce); | ||
279 | irq_exit(); | ||
280 | |||
281 | return IRQ_HANDLED; | ||
282 | } | ||
283 | |||
284 | #endif /* CONFIG_SMP */ | ||
285 | |||
286 | void __init leon_init_timers(void) | ||
254 | { | 287 | { |
255 | int irq, eirq; | 288 | int irq, eirq; |
256 | struct device_node *rootnp, *np, *nnp; | 289 | struct device_node *rootnp, *np, *nnp; |
@@ -260,6 +293,14 @@ void __init leon_init_timers(irq_handler_t counter_fn) | |||
260 | int ampopts; | 293 | int ampopts; |
261 | int err; | 294 | int err; |
262 | 295 | ||
296 | sparc_config.get_cycles_offset = leon_cycles_offset; | ||
297 | sparc_config.cs_period = 1000000 / HZ; | ||
298 | sparc_config.features |= FEAT_L10_CLOCKSOURCE; | ||
299 | |||
300 | #ifndef CONFIG_SMP | ||
301 | sparc_config.features |= FEAT_L10_CLOCKEVENT; | ||
302 | #endif | ||
303 | |||
263 | leondebug_irq_disable = 0; | 304 | leondebug_irq_disable = 0; |
264 | leon_debug_irqout = 0; | 305 | leon_debug_irqout = 0; |
265 | master_l10_counter = (unsigned int *)&dummy_master_l10_counter; | 306 | master_l10_counter = (unsigned int *)&dummy_master_l10_counter; |
@@ -369,7 +410,7 @@ void __init leon_init_timers(irq_handler_t counter_fn) | |||
369 | leon_eirq_setup(eirq); | 410 | leon_eirq_setup(eirq); |
370 | 411 | ||
371 | irq = _leon_build_device_irq(NULL, leon3_gptimer_irq+leon3_gptimer_idx); | 412 | irq = _leon_build_device_irq(NULL, leon3_gptimer_irq+leon3_gptimer_idx); |
372 | err = request_irq(irq, counter_fn, IRQF_TIMER, "timer", NULL); | 413 | err = request_irq(irq, timer_interrupt, IRQF_TIMER, "timer", NULL); |
373 | if (err) { | 414 | if (err) { |
374 | printk(KERN_ERR "unable to attach timer IRQ%d\n", irq); | 415 | printk(KERN_ERR "unable to attach timer IRQ%d\n", irq); |
375 | prom_halt(); | 416 | prom_halt(); |
@@ -401,7 +442,7 @@ void __init leon_init_timers(irq_handler_t counter_fn) | |||
401 | /* Install per-cpu IRQ handler for broadcasted ticker */ | 442 | /* Install per-cpu IRQ handler for broadcasted ticker */ |
402 | irq = leon_build_device_irq(leon3_ticker_irq, handle_percpu_irq, | 443 | irq = leon_build_device_irq(leon3_ticker_irq, handle_percpu_irq, |
403 | "per-cpu", 0); | 444 | "per-cpu", 0); |
404 | err = request_irq(irq, leon_percpu_timer_interrupt, | 445 | err = request_irq(irq, leon_percpu_timer_ce_interrupt, |
405 | IRQF_PERCPU | IRQF_TIMER, "ticker", | 446 | IRQF_PERCPU | IRQF_TIMER, "ticker", |
406 | NULL); | 447 | NULL); |
407 | if (err) { | 448 | if (err) { |
@@ -428,7 +469,6 @@ void leon_clear_clock_irq(void) | |||
428 | 469 | ||
429 | void leon_load_profile_irq(int cpu, unsigned int limit) | 470 | void leon_load_profile_irq(int cpu, unsigned int limit) |
430 | { | 471 | { |
431 | BUG(); | ||
432 | } | 472 | } |
433 | 473 | ||
434 | void __init leon_trans_init(struct device_node *dp) | 474 | void __init leon_trans_init(struct device_node *dp) |
@@ -496,6 +536,7 @@ void __init leon_init_IRQ(void) | |||
496 | { | 536 | { |
497 | sparc_config.init_timers = leon_init_timers; | 537 | sparc_config.init_timers = leon_init_timers; |
498 | sparc_config.build_device_irq = _leon_build_device_irq; | 538 | sparc_config.build_device_irq = _leon_build_device_irq; |
539 | sparc_config.clock_rate = 1000000; | ||
499 | 540 | ||
500 | BTFIXUPSET_CALL(clear_clock_irq, leon_clear_clock_irq, | 541 | BTFIXUPSET_CALL(clear_clock_irq, leon_clear_clock_irq, |
501 | BTFIXUPCALL_NORM); | 542 | BTFIXUPCALL_NORM); |
diff --git a/arch/sparc/kernel/leon_smp.c b/arch/sparc/kernel/leon_smp.c index 1210fde18740..6173f4d82ded 100644 --- a/arch/sparc/kernel/leon_smp.c +++ b/arch/sparc/kernel/leon_smp.c | |||
@@ -23,6 +23,8 @@ | |||
23 | #include <linux/pm.h> | 23 | #include <linux/pm.h> |
24 | #include <linux/delay.h> | 24 | #include <linux/delay.h> |
25 | #include <linux/gfp.h> | 25 | #include <linux/gfp.h> |
26 | #include <linux/cpu.h> | ||
27 | #include <linux/clockchips.h> | ||
26 | 28 | ||
27 | #include <asm/cacheflush.h> | 29 | #include <asm/cacheflush.h> |
28 | #include <asm/tlbflush.h> | 30 | #include <asm/tlbflush.h> |
@@ -42,6 +44,7 @@ | |||
42 | #include <asm/asi.h> | 44 | #include <asm/asi.h> |
43 | #include <asm/leon.h> | 45 | #include <asm/leon.h> |
44 | #include <asm/leon_amba.h> | 46 | #include <asm/leon_amba.h> |
47 | #include <asm/timer.h> | ||
45 | 48 | ||
46 | #include "kernel.h" | 49 | #include "kernel.h" |
47 | 50 | ||
@@ -68,8 +71,6 @@ static inline unsigned long do_swap(volatile unsigned long *ptr, | |||
68 | return val; | 71 | return val; |
69 | } | 72 | } |
70 | 73 | ||
71 | static void smp_setup_percpu_timer(void); | ||
72 | |||
73 | void __cpuinit leon_callin(void) | 74 | void __cpuinit leon_callin(void) |
74 | { | 75 | { |
75 | int cpuid = hard_smpleon_processor_id(); | 76 | int cpuid = hard_smpleon_processor_id(); |
@@ -79,7 +80,7 @@ void __cpuinit leon_callin(void) | |||
79 | leon_configure_cache_smp(); | 80 | leon_configure_cache_smp(); |
80 | 81 | ||
81 | /* Get our local ticker going. */ | 82 | /* Get our local ticker going. */ |
82 | smp_setup_percpu_timer(); | 83 | register_percpu_ce(cpuid); |
83 | 84 | ||
84 | calibrate_delay(); | 85 | calibrate_delay(); |
85 | smp_store_cpu_info(cpuid); | 86 | smp_store_cpu_info(cpuid); |
@@ -196,7 +197,6 @@ void __init leon_boot_cpus(void) | |||
196 | leon_smp_setbroadcast(1 << LEON3_IRQ_TICKER); | 197 | leon_smp_setbroadcast(1 << LEON3_IRQ_TICKER); |
197 | 198 | ||
198 | leon_configure_cache_smp(); | 199 | leon_configure_cache_smp(); |
199 | smp_setup_percpu_timer(); | ||
200 | local_flush_cache_all(); | 200 | local_flush_cache_all(); |
201 | 201 | ||
202 | } | 202 | } |
@@ -489,32 +489,6 @@ void leon_cross_call_irq(void) | |||
489 | ccall_info.processors_out[i] = 1; | 489 | ccall_info.processors_out[i] = 1; |
490 | } | 490 | } |
491 | 491 | ||
492 | irqreturn_t leon_percpu_timer_interrupt(int irq, void *unused) | ||
493 | { | ||
494 | int cpu = smp_processor_id(); | ||
495 | |||
496 | leon_clear_profile_irq(cpu); | ||
497 | |||
498 | profile_tick(CPU_PROFILING); | ||
499 | |||
500 | if (!--prof_counter(cpu)) { | ||
501 | int user = user_mode(get_irq_regs()); | ||
502 | |||
503 | update_process_times(user); | ||
504 | |||
505 | prof_counter(cpu) = prof_multiplier(cpu); | ||
506 | } | ||
507 | |||
508 | return IRQ_HANDLED; | ||
509 | } | ||
510 | |||
511 | static void __init smp_setup_percpu_timer(void) | ||
512 | { | ||
513 | int cpu = smp_processor_id(); | ||
514 | |||
515 | prof_counter(cpu) = prof_multiplier(cpu) = 1; | ||
516 | } | ||
517 | |||
518 | void __init leon_blackbox_id(unsigned *addr) | 492 | void __init leon_blackbox_id(unsigned *addr) |
519 | { | 493 | { |
520 | int rd = *addr & 0x3e000000; | 494 | int rd = *addr & 0x3e000000; |
diff --git a/arch/sparc/kernel/pcic.c b/arch/sparc/kernel/pcic.c index 6edec801e46a..118a3f5806a8 100644 --- a/arch/sparc/kernel/pcic.c +++ b/arch/sparc/kernel/pcic.c | |||
@@ -703,31 +703,28 @@ static void pcic_clear_clock_irq(void) | |||
703 | pcic_timer_dummy = readl(pcic0.pcic_regs+PCI_SYS_LIMIT); | 703 | pcic_timer_dummy = readl(pcic0.pcic_regs+PCI_SYS_LIMIT); |
704 | } | 704 | } |
705 | 705 | ||
706 | static irqreturn_t pcic_timer_handler (int irq, void *h) | 706 | /* CPU frequency is 100 MHz, timer increments every 4 CPU clocks */ |
707 | #define USECS_PER_JIFFY (1000000 / HZ) | ||
708 | #define TICK_TIMER_LIMIT ((100 * 1000000 / 4) / HZ) | ||
709 | |||
710 | static unsigned int pcic_cycles_offset(void) | ||
707 | { | 711 | { |
708 | pcic_clear_clock_irq(); | 712 | u32 value, count; |
709 | xtime_update(1); | ||
710 | #ifndef CONFIG_SMP | ||
711 | update_process_times(user_mode(get_irq_regs())); | ||
712 | #endif | ||
713 | return IRQ_HANDLED; | ||
714 | } | ||
715 | 713 | ||
716 | #define USECS_PER_JIFFY 10000 /* We have 100HZ "standard" timer for sparc */ | 714 | value = readl(pcic0.pcic_regs + PCI_SYS_COUNTER); |
717 | #define TICK_TIMER_LIMIT ((100*1000000/4)/100) | 715 | count = value & ~PCI_SYS_COUNTER_OVERFLOW; |
718 | 716 | ||
719 | u32 pci_gettimeoffset(void) | 717 | if (value & PCI_SYS_COUNTER_OVERFLOW) |
720 | { | 718 | count += TICK_TIMER_LIMIT; |
721 | /* | 719 | /* |
722 | * We divide all by 100 | 720 | * We divide all by HZ |
723 | * to have microsecond resolution and to avoid overflow | 721 | * to have microsecond resolution and to avoid overflow |
724 | */ | 722 | */ |
725 | unsigned long count = | 723 | count = ((count / HZ) * USECS_PER_JIFFY) / (TICK_TIMER_LIMIT / HZ); |
726 | readl(pcic0.pcic_regs+PCI_SYS_COUNTER) & ~PCI_SYS_COUNTER_OVERFLOW; | ||
727 | count = ((count/100)*USECS_PER_JIFFY) / (TICK_TIMER_LIMIT/100); | ||
728 | return count * 1000; | ||
729 | } | ||
730 | 724 | ||
725 | /* Coordinate with the fact that timer_cs rate is 2MHz */ | ||
726 | return count * 2; | ||
727 | } | ||
731 | 728 | ||
732 | void __init pci_time_init(void) | 729 | void __init pci_time_init(void) |
733 | { | 730 | { |
@@ -736,9 +733,16 @@ void __init pci_time_init(void) | |||
736 | int timer_irq, irq; | 733 | int timer_irq, irq; |
737 | int err; | 734 | int err; |
738 | 735 | ||
739 | do_arch_gettimeoffset = pci_gettimeoffset; | 736 | #ifndef CONFIG_SMP |
740 | 737 | /* | |
741 | btfixup(); | 738 | * It's in SBUS dimension, because timer_cs is in this dimension. |
739 | * We take into account this in pcic_cycles_offset() | ||
740 | */ | ||
741 | timer_cs_period = SBUS_CLOCK_RATE / HZ; | ||
742 | sparc_config.features |= FEAT_L10_CLOCKEVENT; | ||
743 | #endif | ||
744 | sparc_config.features |= FEAT_L10_CLOCKSOURCE; | ||
745 | sparc_config.get_cycles_offset = pcic_cycles_offset; | ||
742 | 746 | ||
743 | writel (TICK_TIMER_LIMIT, pcic->pcic_regs+PCI_SYS_LIMIT); | 747 | writel (TICK_TIMER_LIMIT, pcic->pcic_regs+PCI_SYS_LIMIT); |
744 | /* PROM should set appropriate irq */ | 748 | /* PROM should set appropriate irq */ |
@@ -747,7 +751,7 @@ void __init pci_time_init(void) | |||
747 | writel (PCI_COUNTER_IRQ_SET(timer_irq, 0), | 751 | writel (PCI_COUNTER_IRQ_SET(timer_irq, 0), |
748 | pcic->pcic_regs+PCI_COUNTER_IRQ); | 752 | pcic->pcic_regs+PCI_COUNTER_IRQ); |
749 | irq = pcic_build_device_irq(NULL, timer_irq); | 753 | irq = pcic_build_device_irq(NULL, timer_irq); |
750 | err = request_irq(irq, pcic_timer_handler, | 754 | err = request_irq(irq, timer_interrupt, |
751 | IRQF_TIMER, "timer", NULL); | 755 | IRQF_TIMER, "timer", NULL); |
752 | if (err) { | 756 | if (err) { |
753 | prom_printf("time_init: unable to attach IRQ%d\n", timer_irq); | 757 | prom_printf("time_init: unable to attach IRQ%d\n", timer_irq); |
diff --git a/arch/sparc/kernel/smp_32.c b/arch/sparc/kernel/smp_32.c index f671e7fd6ddc..569a8a9d24a2 100644 --- a/arch/sparc/kernel/smp_32.c +++ b/arch/sparc/kernel/smp_32.c | |||
@@ -301,28 +301,9 @@ void smp_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr) | |||
301 | local_flush_sig_insns(mm, insn_addr); | 301 | local_flush_sig_insns(mm, insn_addr); |
302 | } | 302 | } |
303 | 303 | ||
304 | extern unsigned int lvl14_resolution; | ||
305 | |||
306 | /* /proc/profile writes can call this, don't __init it please. */ | ||
307 | static DEFINE_SPINLOCK(prof_setup_lock); | ||
308 | |||
309 | int setup_profiling_timer(unsigned int multiplier) | 304 | int setup_profiling_timer(unsigned int multiplier) |
310 | { | 305 | { |
311 | int i; | 306 | return -EINVAL; |
312 | unsigned long flags; | ||
313 | |||
314 | /* Prevent level14 ticker IRQ flooding. */ | ||
315 | if((!multiplier) || (lvl14_resolution / multiplier) < 500) | ||
316 | return -EINVAL; | ||
317 | |||
318 | spin_lock_irqsave(&prof_setup_lock, flags); | ||
319 | for_each_possible_cpu(i) { | ||
320 | load_profile_irq(i, lvl14_resolution / multiplier); | ||
321 | prof_multiplier(i) = multiplier; | ||
322 | } | ||
323 | spin_unlock_irqrestore(&prof_setup_lock, flags); | ||
324 | |||
325 | return 0; | ||
326 | } | 307 | } |
327 | 308 | ||
328 | void __init smp_prepare_cpus(unsigned int max_cpus) | 309 | void __init smp_prepare_cpus(unsigned int max_cpus) |
diff --git a/arch/sparc/kernel/sun4c_irq.c b/arch/sparc/kernel/sun4c_irq.c index d4e3c832c341..39c64211b1b6 100644 --- a/arch/sparc/kernel/sun4c_irq.c +++ b/arch/sparc/kernel/sun4c_irq.c | |||
@@ -174,7 +174,7 @@ static void sun4c_load_profile_irq(int cpu, unsigned int limit) | |||
174 | /* Errm.. not sure how to do this.. */ | 174 | /* Errm.. not sure how to do this.. */ |
175 | } | 175 | } |
176 | 176 | ||
177 | static void __init sun4c_init_timers(irq_handler_t counter_fn) | 177 | static void __init sun4c_init_timers(void) |
178 | { | 178 | { |
179 | const struct linux_prom_irqs *prom_irqs; | 179 | const struct linux_prom_irqs *prom_irqs; |
180 | struct device_node *dp; | 180 | struct device_node *dp; |
@@ -207,12 +207,16 @@ static void __init sun4c_init_timers(irq_handler_t counter_fn) | |||
207 | * level 14 timer limit since we are letting the prom handle | 207 | * level 14 timer limit since we are letting the prom handle |
208 | * them until we have a real console driver so L1-A works. | 208 | * them until we have a real console driver so L1-A works. |
209 | */ | 209 | */ |
210 | sbus_writel((((1000000/HZ) + 1) << 10), &sun4c_timers->l10_limit); | 210 | sparc_config.cs_period = SBUS_CLOCK_RATE / HZ; |
211 | sparc_config.features |= | ||
212 | FEAT_L10_CLOCKSOURCE | FEAT_L10_CLOCKEVENT; | ||
213 | sbus_writel(timer_value(sparc_config.cs_period), | ||
214 | &sun4c_timers->l10_limit); | ||
211 | 215 | ||
212 | master_l10_counter = &sun4c_timers->l10_count; | 216 | master_l10_counter = &sun4c_timers->l10_count; |
213 | 217 | ||
214 | irq = sun4c_build_device_irq(NULL, prom_irqs[0].pri); | 218 | irq = sun4c_build_device_irq(NULL, prom_irqs[0].pri); |
215 | err = request_irq(irq, counter_fn, IRQF_TIMER, "timer", NULL); | 219 | err = request_irq(irq, timer_interrupt, IRQF_TIMER, "timer", NULL); |
216 | if (err) { | 220 | if (err) { |
217 | prom_printf("sun4c_init_timers: request_irq() fails with %d\n", err); | 221 | prom_printf("sun4c_init_timers: request_irq() fails with %d\n", err); |
218 | prom_halt(); | 222 | prom_halt(); |
@@ -253,6 +257,7 @@ void __init sun4c_init_IRQ(void) | |||
253 | 257 | ||
254 | sparc_config.init_timers = sun4c_init_timers; | 258 | sparc_config.init_timers = sun4c_init_timers; |
255 | sparc_config.build_device_irq = sun4c_build_device_irq; | 259 | sparc_config.build_device_irq = sun4c_build_device_irq; |
260 | sparc_config.clock_rate = SBUS_CLOCK_RATE; | ||
256 | 261 | ||
257 | #ifdef CONFIG_SMP | 262 | #ifdef CONFIG_SMP |
258 | BTFIXUPSET_CALL(set_cpu_int, sun4c_nop, BTFIXUPCALL_NOP); | 263 | BTFIXUPSET_CALL(set_cpu_int, sun4c_nop, BTFIXUPCALL_NOP); |
diff --git a/arch/sparc/kernel/sun4d_irq.c b/arch/sparc/kernel/sun4d_irq.c index 30119f662eff..abf52654a8bc 100644 --- a/arch/sparc/kernel/sun4d_irq.c +++ b/arch/sparc/kernel/sun4d_irq.c | |||
@@ -282,7 +282,8 @@ static void sun4d_clear_clock_irq(void) | |||
282 | 282 | ||
283 | static void sun4d_load_profile_irq(int cpu, unsigned int limit) | 283 | static void sun4d_load_profile_irq(int cpu, unsigned int limit) |
284 | { | 284 | { |
285 | bw_set_prof_limit(cpu, limit); | 285 | unsigned int value = limit ? timer_value(limit) : 0; |
286 | bw_set_prof_limit(cpu, value); | ||
286 | } | 287 | } |
287 | 288 | ||
288 | static void __init sun4d_load_profile_irqs(void) | 289 | static void __init sun4d_load_profile_irqs(void) |
@@ -423,7 +424,7 @@ static void __init sun4d_fixup_trap_table(void) | |||
423 | #endif | 424 | #endif |
424 | } | 425 | } |
425 | 426 | ||
426 | static void __init sun4d_init_timers(irq_handler_t counter_fn) | 427 | static void __init sun4d_init_timers(void) |
427 | { | 428 | { |
428 | struct device_node *dp; | 429 | struct device_node *dp; |
429 | struct resource res; | 430 | struct resource res; |
@@ -466,12 +467,20 @@ static void __init sun4d_init_timers(irq_handler_t counter_fn) | |||
466 | prom_halt(); | 467 | prom_halt(); |
467 | } | 468 | } |
468 | 469 | ||
469 | sbus_writel((((1000000/HZ) + 1) << 10), &sun4d_timers->l10_timer_limit); | 470 | #ifdef CONFIG_SMP |
471 | sparc_config.cs_period = SBUS_CLOCK_RATE * 2; /* 2 seconds */ | ||
472 | #else | ||
473 | sparc_config.cs_period = SBUS_CLOCK_RATE / HZ; /* 1/HZ sec */ | ||
474 | sparc_config.features |= FEAT_L10_CLOCKEVENT; | ||
475 | #endif | ||
476 | sparc_config.features |= FEAT_L10_CLOCKSOURCE; | ||
477 | sbus_writel(timer_value(sparc_config.cs_period), | ||
478 | &sun4d_timers->l10_timer_limit); | ||
470 | 479 | ||
471 | master_l10_counter = &sun4d_timers->l10_cur_count; | 480 | master_l10_counter = &sun4d_timers->l10_cur_count; |
472 | 481 | ||
473 | irq = sun4d_build_timer_irq(board, SUN4D_TIMER_IRQ); | 482 | irq = sun4d_build_timer_irq(board, SUN4D_TIMER_IRQ); |
474 | err = request_irq(irq, counter_fn, IRQF_TIMER, "timer", NULL); | 483 | err = request_irq(irq, timer_interrupt, IRQF_TIMER, "timer", NULL); |
475 | if (err) { | 484 | if (err) { |
476 | prom_printf("sun4d_init_timers: request_irq() failed with %d\n", | 485 | prom_printf("sun4d_init_timers: request_irq() failed with %d\n", |
477 | err); | 486 | err); |
@@ -514,6 +523,7 @@ void __init sun4d_init_IRQ(void) | |||
514 | 523 | ||
515 | sparc_config.init_timers = sun4d_init_timers; | 524 | sparc_config.init_timers = sun4d_init_timers; |
516 | sparc_config.build_device_irq = sun4d_build_device_irq; | 525 | sparc_config.build_device_irq = sun4d_build_device_irq; |
526 | sparc_config.clock_rate = SBUS_CLOCK_RATE; | ||
517 | 527 | ||
518 | #ifdef CONFIG_SMP | 528 | #ifdef CONFIG_SMP |
519 | BTFIXUPSET_CALL(set_cpu_int, sun4d_set_cpu_int, BTFIXUPCALL_NORM); | 529 | BTFIXUPSET_CALL(set_cpu_int, sun4d_set_cpu_int, BTFIXUPCALL_NORM); |
diff --git a/arch/sparc/kernel/sun4d_smp.c b/arch/sparc/kernel/sun4d_smp.c index 540b2fec09f0..576fe74d226b 100644 --- a/arch/sparc/kernel/sun4d_smp.c +++ b/arch/sparc/kernel/sun4d_smp.c | |||
@@ -6,16 +6,18 @@ | |||
6 | * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) | 6 | * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include <linux/clockchips.h> | ||
9 | #include <linux/interrupt.h> | 10 | #include <linux/interrupt.h> |
10 | #include <linux/profile.h> | 11 | #include <linux/profile.h> |
11 | #include <linux/delay.h> | 12 | #include <linux/delay.h> |
12 | #include <linux/cpu.h> | 13 | #include <linux/cpu.h> |
13 | 14 | ||
15 | #include <asm/cacheflush.h> | ||
16 | #include <asm/switch_to.h> | ||
17 | #include <asm/tlbflush.h> | ||
18 | #include <asm/timer.h> | ||
14 | #include <asm/sbi.h> | 19 | #include <asm/sbi.h> |
15 | #include <asm/mmu.h> | 20 | #include <asm/mmu.h> |
16 | #include <asm/tlbflush.h> | ||
17 | #include <asm/switch_to.h> | ||
18 | #include <asm/cacheflush.h> | ||
19 | 21 | ||
20 | #include "kernel.h" | 22 | #include "kernel.h" |
21 | #include "irq.h" | 23 | #include "irq.h" |
@@ -34,7 +36,6 @@ static inline unsigned long sun4d_swap(volatile unsigned long *ptr, unsigned lon | |||
34 | } | 36 | } |
35 | 37 | ||
36 | static void smp4d_ipi_init(void); | 38 | static void smp4d_ipi_init(void); |
37 | static void smp_setup_percpu_timer(void); | ||
38 | 39 | ||
39 | static unsigned char cpu_leds[32]; | 40 | static unsigned char cpu_leds[32]; |
40 | 41 | ||
@@ -70,7 +71,7 @@ void __cpuinit smp4d_callin(void) | |||
70 | * to call the scheduler code. | 71 | * to call the scheduler code. |
71 | */ | 72 | */ |
72 | /* Get our local ticker going. */ | 73 | /* Get our local ticker going. */ |
73 | smp_setup_percpu_timer(); | 74 | register_percpu_ce(cpuid); |
74 | 75 | ||
75 | calibrate_delay(); | 76 | calibrate_delay(); |
76 | smp_store_cpu_info(cpuid); | 77 | smp_store_cpu_info(cpuid); |
@@ -123,7 +124,6 @@ void __init smp4d_boot_cpus(void) | |||
123 | smp4d_ipi_init(); | 124 | smp4d_ipi_init(); |
124 | if (boot_cpu_id) | 125 | if (boot_cpu_id) |
125 | current_set[0] = NULL; | 126 | current_set[0] = NULL; |
126 | smp_setup_percpu_timer(); | ||
127 | local_flush_cache_all(); | 127 | local_flush_cache_all(); |
128 | } | 128 | } |
129 | 129 | ||
@@ -364,6 +364,7 @@ void smp4d_percpu_timer_interrupt(struct pt_regs *regs) | |||
364 | { | 364 | { |
365 | struct pt_regs *old_regs; | 365 | struct pt_regs *old_regs; |
366 | int cpu = hard_smp4d_processor_id(); | 366 | int cpu = hard_smp4d_processor_id(); |
367 | struct clock_event_device *ce; | ||
367 | static int cpu_tick[NR_CPUS]; | 368 | static int cpu_tick[NR_CPUS]; |
368 | static char led_mask[] = { 0xe, 0xd, 0xb, 0x7, 0xb, 0xd }; | 369 | static char led_mask[] = { 0xe, 0xd, 0xb, 0x7, 0xb, 0xd }; |
369 | 370 | ||
@@ -379,28 +380,15 @@ void smp4d_percpu_timer_interrupt(struct pt_regs *regs) | |||
379 | show_leds(cpu); | 380 | show_leds(cpu); |
380 | } | 381 | } |
381 | 382 | ||
382 | profile_tick(CPU_PROFILING); | 383 | ce = &per_cpu(sparc32_clockevent, cpu); |
383 | |||
384 | if (!--prof_counter(cpu)) { | ||
385 | int user = user_mode(regs); | ||
386 | 384 | ||
387 | irq_enter(); | 385 | irq_enter(); |
388 | update_process_times(user); | 386 | ce->event_handler(ce); |
389 | irq_exit(); | 387 | irq_exit(); |
390 | 388 | ||
391 | prof_counter(cpu) = prof_multiplier(cpu); | ||
392 | } | ||
393 | set_irq_regs(old_regs); | 389 | set_irq_regs(old_regs); |
394 | } | 390 | } |
395 | 391 | ||
396 | static void __cpuinit smp_setup_percpu_timer(void) | ||
397 | { | ||
398 | int cpu = hard_smp4d_processor_id(); | ||
399 | |||
400 | prof_counter(cpu) = prof_multiplier(cpu) = 1; | ||
401 | load_profile_irq(cpu, lvl14_resolution); | ||
402 | } | ||
403 | |||
404 | void __init smp4d_blackbox_id(unsigned *addr) | 392 | void __init smp4d_blackbox_id(unsigned *addr) |
405 | { | 393 | { |
406 | int rd = *addr & 0x3e000000; | 394 | int rd = *addr & 0x3e000000; |
diff --git a/arch/sparc/kernel/sun4m_irq.c b/arch/sparc/kernel/sun4m_irq.c index 0d3a2d8cb266..87908a5b1223 100644 --- a/arch/sparc/kernel/sun4m_irq.c +++ b/arch/sparc/kernel/sun4m_irq.c | |||
@@ -318,9 +318,6 @@ struct sun4m_timer_global { | |||
318 | 318 | ||
319 | static struct sun4m_timer_global __iomem *timers_global; | 319 | static struct sun4m_timer_global __iomem *timers_global; |
320 | 320 | ||
321 | |||
322 | unsigned int lvl14_resolution = (((1000000/HZ) + 1) << 10); | ||
323 | |||
324 | static void sun4m_clear_clock_irq(void) | 321 | static void sun4m_clear_clock_irq(void) |
325 | { | 322 | { |
326 | sbus_readl(&timers_global->l10_limit); | 323 | sbus_readl(&timers_global->l10_limit); |
@@ -369,10 +366,11 @@ void sun4m_clear_profile_irq(int cpu) | |||
369 | 366 | ||
370 | static void sun4m_load_profile_irq(int cpu, unsigned int limit) | 367 | static void sun4m_load_profile_irq(int cpu, unsigned int limit) |
371 | { | 368 | { |
372 | sbus_writel(limit, &timers_percpu[cpu]->l14_limit); | 369 | unsigned int value = limit ? timer_value(limit) : 0; |
370 | sbus_writel(value, &timers_percpu[cpu]->l14_limit); | ||
373 | } | 371 | } |
374 | 372 | ||
375 | static void __init sun4m_init_timers(irq_handler_t counter_fn) | 373 | static void __init sun4m_init_timers(void) |
376 | { | 374 | { |
377 | struct device_node *dp = of_find_node_by_name(NULL, "counter"); | 375 | struct device_node *dp = of_find_node_by_name(NULL, "counter"); |
378 | int i, err, len, num_cpu_timers; | 376 | int i, err, len, num_cpu_timers; |
@@ -402,13 +400,22 @@ static void __init sun4m_init_timers(irq_handler_t counter_fn) | |||
402 | /* Every per-cpu timer works in timer mode */ | 400 | /* Every per-cpu timer works in timer mode */ |
403 | sbus_writel(0x00000000, &timers_global->timer_config); | 401 | sbus_writel(0x00000000, &timers_global->timer_config); |
404 | 402 | ||
405 | sbus_writel((((1000000/HZ) + 1) << 10), &timers_global->l10_limit); | 403 | #ifdef CONFIG_SMP |
404 | sparc_config.cs_period = SBUS_CLOCK_RATE * 2; /* 2 seconds */ | ||
405 | sparc_config.features |= FEAT_L14_ONESHOT; | ||
406 | #else | ||
407 | sparc_config.cs_period = SBUS_CLOCK_RATE / HZ; /* 1/HZ sec */ | ||
408 | sparc_config.features |= FEAT_L10_CLOCKEVENT; | ||
409 | #endif | ||
410 | sparc_config.features |= FEAT_L10_CLOCKSOURCE; | ||
411 | sbus_writel(timer_value(sparc_config.cs_period), | ||
412 | &timers_global->l10_limit); | ||
406 | 413 | ||
407 | master_l10_counter = &timers_global->l10_count; | 414 | master_l10_counter = &timers_global->l10_count; |
408 | 415 | ||
409 | irq = sun4m_build_device_irq(NULL, SUN4M_TIMER_IRQ); | 416 | irq = sun4m_build_device_irq(NULL, SUN4M_TIMER_IRQ); |
410 | 417 | ||
411 | err = request_irq(irq, counter_fn, IRQF_TIMER, "timer", NULL); | 418 | err = request_irq(irq, timer_interrupt, IRQF_TIMER, "timer", NULL); |
412 | if (err) { | 419 | if (err) { |
413 | printk(KERN_ERR "sun4m_init_timers: Register IRQ error %d.\n", | 420 | printk(KERN_ERR "sun4m_init_timers: Register IRQ error %d.\n", |
414 | err); | 421 | err); |
@@ -480,6 +487,7 @@ void __init sun4m_init_IRQ(void) | |||
480 | 487 | ||
481 | sparc_config.init_timers = sun4m_init_timers; | 488 | sparc_config.init_timers = sun4m_init_timers; |
482 | sparc_config.build_device_irq = sun4m_build_device_irq; | 489 | sparc_config.build_device_irq = sun4m_build_device_irq; |
490 | sparc_config.clock_rate = SBUS_CLOCK_RATE; | ||
483 | 491 | ||
484 | #ifdef CONFIG_SMP | 492 | #ifdef CONFIG_SMP |
485 | BTFIXUPSET_CALL(set_cpu_int, sun4m_send_ipi, BTFIXUPCALL_NORM); | 493 | BTFIXUPSET_CALL(set_cpu_int, sun4m_send_ipi, BTFIXUPCALL_NORM); |
diff --git a/arch/sparc/kernel/sun4m_smp.c b/arch/sparc/kernel/sun4m_smp.c index 02db9a0412ce..29f8ace10b59 100644 --- a/arch/sparc/kernel/sun4m_smp.c +++ b/arch/sparc/kernel/sun4m_smp.c | |||
@@ -4,6 +4,7 @@ | |||
4 | * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) | 4 | * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) |
5 | */ | 5 | */ |
6 | 6 | ||
7 | #include <linux/clockchips.h> | ||
7 | #include <linux/interrupt.h> | 8 | #include <linux/interrupt.h> |
8 | #include <linux/profile.h> | 9 | #include <linux/profile.h> |
9 | #include <linux/delay.h> | 10 | #include <linux/delay.h> |
@@ -12,6 +13,7 @@ | |||
12 | #include <asm/cacheflush.h> | 13 | #include <asm/cacheflush.h> |
13 | #include <asm/switch_to.h> | 14 | #include <asm/switch_to.h> |
14 | #include <asm/tlbflush.h> | 15 | #include <asm/tlbflush.h> |
16 | #include <asm/timer.h> | ||
15 | 17 | ||
16 | #include "irq.h" | 18 | #include "irq.h" |
17 | #include "kernel.h" | 19 | #include "kernel.h" |
@@ -31,7 +33,6 @@ swap_ulong(volatile unsigned long *ptr, unsigned long val) | |||
31 | } | 33 | } |
32 | 34 | ||
33 | static void smp4m_ipi_init(void); | 35 | static void smp4m_ipi_init(void); |
34 | static void smp_setup_percpu_timer(void); | ||
35 | 36 | ||
36 | void __cpuinit smp4m_callin(void) | 37 | void __cpuinit smp4m_callin(void) |
37 | { | 38 | { |
@@ -42,8 +43,7 @@ void __cpuinit smp4m_callin(void) | |||
42 | 43 | ||
43 | notify_cpu_starting(cpuid); | 44 | notify_cpu_starting(cpuid); |
44 | 45 | ||
45 | /* Get our local ticker going. */ | 46 | register_percpu_ce(cpuid); |
46 | smp_setup_percpu_timer(); | ||
47 | 47 | ||
48 | calibrate_delay(); | 48 | calibrate_delay(); |
49 | smp_store_cpu_info(cpuid); | 49 | smp_store_cpu_info(cpuid); |
@@ -87,7 +87,7 @@ void __cpuinit smp4m_callin(void) | |||
87 | void __init smp4m_boot_cpus(void) | 87 | void __init smp4m_boot_cpus(void) |
88 | { | 88 | { |
89 | smp4m_ipi_init(); | 89 | smp4m_ipi_init(); |
90 | smp_setup_percpu_timer(); | 90 | sun4m_unmask_profile_irq(); |
91 | local_flush_cache_all(); | 91 | local_flush_cache_all(); |
92 | } | 92 | } |
93 | 93 | ||
@@ -260,37 +260,25 @@ void smp4m_cross_call_irq(void) | |||
260 | void smp4m_percpu_timer_interrupt(struct pt_regs *regs) | 260 | void smp4m_percpu_timer_interrupt(struct pt_regs *regs) |
261 | { | 261 | { |
262 | struct pt_regs *old_regs; | 262 | struct pt_regs *old_regs; |
263 | struct clock_event_device *ce; | ||
263 | int cpu = smp_processor_id(); | 264 | int cpu = smp_processor_id(); |
264 | 265 | ||
265 | old_regs = set_irq_regs(regs); | 266 | old_regs = set_irq_regs(regs); |
266 | 267 | ||
267 | sun4m_clear_profile_irq(cpu); | 268 | ce = &per_cpu(sparc32_clockevent, cpu); |
268 | 269 | ||
269 | profile_tick(CPU_PROFILING); | 270 | if (ce->mode & CLOCK_EVT_MODE_PERIODIC) |
271 | sun4m_clear_profile_irq(cpu); | ||
272 | else | ||
273 | load_profile_irq(cpu, 0); /* Is this needless? */ | ||
270 | 274 | ||
271 | if (!--prof_counter(cpu)) { | 275 | irq_enter(); |
272 | int user = user_mode(regs); | 276 | ce->event_handler(ce); |
277 | irq_exit(); | ||
273 | 278 | ||
274 | irq_enter(); | ||
275 | update_process_times(user); | ||
276 | irq_exit(); | ||
277 | |||
278 | prof_counter(cpu) = prof_multiplier(cpu); | ||
279 | } | ||
280 | set_irq_regs(old_regs); | 279 | set_irq_regs(old_regs); |
281 | } | 280 | } |
282 | 281 | ||
283 | static void __cpuinit smp_setup_percpu_timer(void) | ||
284 | { | ||
285 | int cpu = smp_processor_id(); | ||
286 | |||
287 | prof_counter(cpu) = prof_multiplier(cpu) = 1; | ||
288 | load_profile_irq(cpu, lvl14_resolution); | ||
289 | |||
290 | if (cpu == boot_cpu_id) | ||
291 | sun4m_unmask_profile_irq(); | ||
292 | } | ||
293 | |||
294 | static void __init smp4m_blackbox_id(unsigned *addr) | 282 | static void __init smp4m_blackbox_id(unsigned *addr) |
295 | { | 283 | { |
296 | int rd = *addr & 0x3e000000; | 284 | int rd = *addr & 0x3e000000; |
diff --git a/arch/sparc/kernel/time_32.c b/arch/sparc/kernel/time_32.c index 68e0284bf3f3..89e890bc0941 100644 --- a/arch/sparc/kernel/time_32.c +++ b/arch/sparc/kernel/time_32.c | |||
@@ -26,6 +26,8 @@ | |||
26 | #include <linux/rtc.h> | 26 | #include <linux/rtc.h> |
27 | #include <linux/rtc/m48t59.h> | 27 | #include <linux/rtc/m48t59.h> |
28 | #include <linux/timex.h> | 28 | #include <linux/timex.h> |
29 | #include <linux/clocksource.h> | ||
30 | #include <linux/clockchips.h> | ||
29 | #include <linux/init.h> | 31 | #include <linux/init.h> |
30 | #include <linux/pci.h> | 32 | #include <linux/pci.h> |
31 | #include <linux/ioport.h> | 33 | #include <linux/ioport.h> |
@@ -44,9 +46,21 @@ | |||
44 | #include <asm/page.h> | 46 | #include <asm/page.h> |
45 | #include <asm/pcic.h> | 47 | #include <asm/pcic.h> |
46 | #include <asm/irq_regs.h> | 48 | #include <asm/irq_regs.h> |
49 | #include <asm/setup.h> | ||
47 | 50 | ||
48 | #include "irq.h" | 51 | #include "irq.h" |
49 | 52 | ||
53 | static __cacheline_aligned_in_smp DEFINE_SEQLOCK(timer_cs_lock); | ||
54 | static __volatile__ u64 timer_cs_internal_counter = 0; | ||
55 | static char timer_cs_enabled = 0; | ||
56 | |||
57 | static struct clock_event_device timer_ce; | ||
58 | static char timer_ce_enabled = 0; | ||
59 | |||
60 | #ifdef CONFIG_SMP | ||
61 | DEFINE_PER_CPU(struct clock_event_device, sparc32_clockevent); | ||
62 | #endif | ||
63 | |||
50 | DEFINE_SPINLOCK(rtc_lock); | 64 | DEFINE_SPINLOCK(rtc_lock); |
51 | EXPORT_SYMBOL(rtc_lock); | 65 | EXPORT_SYMBOL(rtc_lock); |
52 | 66 | ||
@@ -75,36 +89,167 @@ EXPORT_SYMBOL(profile_pc); | |||
75 | 89 | ||
76 | __volatile__ unsigned int *master_l10_counter; | 90 | __volatile__ unsigned int *master_l10_counter; |
77 | 91 | ||
78 | u32 (*do_arch_gettimeoffset)(void); | ||
79 | |||
80 | int update_persistent_clock(struct timespec now) | 92 | int update_persistent_clock(struct timespec now) |
81 | { | 93 | { |
82 | return set_rtc_mmss(now.tv_sec); | 94 | return set_rtc_mmss(now.tv_sec); |
83 | } | 95 | } |
84 | 96 | ||
85 | /* | 97 | irqreturn_t notrace timer_interrupt(int dummy, void *dev_id) |
86 | * timer_interrupt() needs to keep up the real-time clock, | 98 | { |
87 | * as well as call the "xtime_update()" routine every clocktick | 99 | if (timer_cs_enabled) { |
88 | */ | 100 | write_seqlock(&timer_cs_lock); |
101 | timer_cs_internal_counter++; | ||
102 | clear_clock_irq(); | ||
103 | write_sequnlock(&timer_cs_lock); | ||
104 | } else { | ||
105 | clear_clock_irq(); | ||
106 | } | ||
89 | 107 | ||
90 | #define TICK_SIZE (tick_nsec / 1000) | 108 | if (timer_ce_enabled) |
109 | timer_ce.event_handler(&timer_ce); | ||
91 | 110 | ||
92 | static irqreturn_t timer_interrupt(int dummy, void *dev_id) | 111 | return IRQ_HANDLED; |
112 | } | ||
113 | |||
114 | static void timer_ce_set_mode(enum clock_event_mode mode, | ||
115 | struct clock_event_device *evt) | ||
93 | { | 116 | { |
94 | #ifndef CONFIG_SMP | 117 | switch (mode) { |
95 | profile_tick(CPU_PROFILING); | 118 | case CLOCK_EVT_MODE_PERIODIC: |
96 | #endif | 119 | case CLOCK_EVT_MODE_RESUME: |
120 | timer_ce_enabled = 1; | ||
121 | break; | ||
122 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
123 | timer_ce_enabled = 0; | ||
124 | break; | ||
125 | default: | ||
126 | break; | ||
127 | } | ||
128 | smp_mb(); | ||
129 | } | ||
130 | |||
131 | static __init void setup_timer_ce(void) | ||
132 | { | ||
133 | struct clock_event_device *ce = &timer_ce; | ||
134 | |||
135 | BUG_ON(smp_processor_id() != boot_cpu_id); | ||
136 | |||
137 | ce->name = "timer_ce"; | ||
138 | ce->rating = 100; | ||
139 | ce->features = CLOCK_EVT_FEAT_PERIODIC; | ||
140 | ce->set_mode = timer_ce_set_mode; | ||
141 | ce->cpumask = cpu_possible_mask; | ||
142 | ce->shift = 32; | ||
143 | ce->mult = div_sc(sparc_config.clock_rate, NSEC_PER_SEC, | ||
144 | ce->shift); | ||
145 | clockevents_register_device(ce); | ||
146 | } | ||
97 | 147 | ||
98 | clear_clock_irq(); | 148 | static unsigned int sbus_cycles_offset(void) |
149 | { | ||
150 | unsigned int val, offset; | ||
99 | 151 | ||
100 | xtime_update(1); | 152 | val = *master_l10_counter; |
153 | offset = (val >> TIMER_VALUE_SHIFT) & TIMER_VALUE_MASK; | ||
101 | 154 | ||
102 | #ifndef CONFIG_SMP | 155 | /* Limit hit? */ |
103 | update_process_times(user_mode(get_irq_regs())); | 156 | if (val & TIMER_LIMIT_BIT) |
104 | #endif | 157 | offset += sparc_config.cs_period; |
105 | return IRQ_HANDLED; | 158 | |
159 | return offset; | ||
106 | } | 160 | } |
107 | 161 | ||
162 | static cycle_t timer_cs_read(struct clocksource *cs) | ||
163 | { | ||
164 | unsigned int seq, offset; | ||
165 | u64 cycles; | ||
166 | |||
167 | do { | ||
168 | seq = read_seqbegin(&timer_cs_lock); | ||
169 | |||
170 | cycles = timer_cs_internal_counter; | ||
171 | offset = sparc_config.get_cycles_offset(); | ||
172 | } while (read_seqretry(&timer_cs_lock, seq)); | ||
173 | |||
174 | /* Count absolute cycles */ | ||
175 | cycles *= sparc_config.cs_period; | ||
176 | cycles += offset; | ||
177 | |||
178 | return cycles; | ||
179 | } | ||
180 | |||
181 | static struct clocksource timer_cs = { | ||
182 | .name = "timer_cs", | ||
183 | .rating = 100, | ||
184 | .read = timer_cs_read, | ||
185 | .mask = CLOCKSOURCE_MASK(64), | ||
186 | .shift = 2, | ||
187 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
188 | }; | ||
189 | |||
190 | static __init int setup_timer_cs(void) | ||
191 | { | ||
192 | timer_cs_enabled = 1; | ||
193 | timer_cs.mult = clocksource_hz2mult(sparc_config.clock_rate, | ||
194 | timer_cs.shift); | ||
195 | |||
196 | return clocksource_register(&timer_cs); | ||
197 | } | ||
198 | |||
199 | #ifdef CONFIG_SMP | ||
200 | static void percpu_ce_setup(enum clock_event_mode mode, | ||
201 | struct clock_event_device *evt) | ||
202 | { | ||
203 | int cpu = __first_cpu(evt->cpumask); | ||
204 | |||
205 | switch (mode) { | ||
206 | case CLOCK_EVT_MODE_PERIODIC: | ||
207 | load_profile_irq(cpu, SBUS_CLOCK_RATE / HZ); | ||
208 | break; | ||
209 | case CLOCK_EVT_MODE_ONESHOT: | ||
210 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
211 | case CLOCK_EVT_MODE_UNUSED: | ||
212 | load_profile_irq(cpu, 0); | ||
213 | break; | ||
214 | default: | ||
215 | break; | ||
216 | } | ||
217 | } | ||
218 | |||
219 | static int percpu_ce_set_next_event(unsigned long delta, | ||
220 | struct clock_event_device *evt) | ||
221 | { | ||
222 | int cpu = __first_cpu(evt->cpumask); | ||
223 | unsigned int next = (unsigned int)delta; | ||
224 | |||
225 | load_profile_irq(cpu, next); | ||
226 | return 0; | ||
227 | } | ||
228 | |||
229 | void register_percpu_ce(int cpu) | ||
230 | { | ||
231 | struct clock_event_device *ce = &per_cpu(sparc32_clockevent, cpu); | ||
232 | unsigned int features = CLOCK_EVT_FEAT_PERIODIC; | ||
233 | |||
234 | if (sparc_config.features & FEAT_L14_ONESHOT) | ||
235 | features |= CLOCK_EVT_FEAT_ONESHOT; | ||
236 | |||
237 | ce->name = "percpu_ce"; | ||
238 | ce->rating = 200; | ||
239 | ce->features = features; | ||
240 | ce->set_mode = percpu_ce_setup; | ||
241 | ce->set_next_event = percpu_ce_set_next_event; | ||
242 | ce->cpumask = cpumask_of(cpu); | ||
243 | ce->shift = 32; | ||
244 | ce->mult = div_sc(sparc_config.clock_rate, NSEC_PER_SEC, | ||
245 | ce->shift); | ||
246 | ce->max_delta_ns = clockevent_delta2ns(sparc_config.clock_rate, ce); | ||
247 | ce->min_delta_ns = clockevent_delta2ns(100, ce); | ||
248 | |||
249 | clockevents_register_device(ce); | ||
250 | } | ||
251 | #endif | ||
252 | |||
108 | static unsigned char mostek_read_byte(struct device *dev, u32 ofs) | 253 | static unsigned char mostek_read_byte(struct device *dev, u32 ofs) |
109 | { | 254 | { |
110 | struct platform_device *pdev = to_platform_device(dev); | 255 | struct platform_device *pdev = to_platform_device(dev); |
@@ -195,38 +340,30 @@ static int __init clock_init(void) | |||
195 | */ | 340 | */ |
196 | fs_initcall(clock_init); | 341 | fs_initcall(clock_init); |
197 | 342 | ||
198 | 343 | static void __init sparc32_late_time_init(void) | |
199 | u32 sbus_do_gettimeoffset(void) | ||
200 | { | 344 | { |
201 | unsigned long val = *master_l10_counter; | 345 | if (sparc_config.features & FEAT_L10_CLOCKEVENT) |
202 | unsigned long usec = (val >> 10) & 0x1fffff; | 346 | setup_timer_ce(); |
203 | 347 | if (sparc_config.features & FEAT_L10_CLOCKSOURCE) | |
204 | /* Limit hit? */ | 348 | setup_timer_cs(); |
205 | if (val & 0x80000000) | 349 | #ifdef CONFIG_SMP |
206 | usec += 1000000 / HZ; | 350 | register_percpu_ce(smp_processor_id()); |
207 | 351 | #endif | |
208 | return usec * 1000; | ||
209 | } | 352 | } |
210 | 353 | ||
211 | 354 | static void __init sbus_time_init(void) | |
212 | u32 arch_gettimeoffset(void) | ||
213 | { | 355 | { |
214 | if (unlikely(!do_arch_gettimeoffset)) | 356 | sparc_config.get_cycles_offset = sbus_cycles_offset; |
215 | return 0; | 357 | sparc_config.init_timers(); |
216 | return do_arch_gettimeoffset(); | ||
217 | } | 358 | } |
218 | 359 | ||
219 | static void __init sbus_time_init(void) | 360 | void __init time_init(void) |
220 | { | 361 | { |
221 | do_arch_gettimeoffset = sbus_do_gettimeoffset; | ||
222 | |||
223 | btfixup(); | 362 | btfixup(); |
224 | 363 | ||
225 | sparc_config.init_timers(timer_interrupt); | 364 | sparc_config.features = 0; |
226 | } | 365 | late_time_init = sparc32_late_time_init; |
227 | 366 | ||
228 | void __init time_init(void) | ||
229 | { | ||
230 | if (pcic_present()) | 367 | if (pcic_present()) |
231 | pci_time_init(); | 368 | pci_time_init(); |
232 | else | 369 | else |