diff options
Diffstat (limited to 'arch/arm/kernel')
-rw-r--r-- | arch/arm/kernel/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/kernel/entry-armv.S | 3 | ||||
-rw-r--r-- | arch/arm/kernel/entry-common.S | 3 | ||||
-rw-r--r-- | arch/arm/kernel/process.c | 2 | ||||
-rw-r--r-- | arch/arm/kernel/signal.c | 8 | ||||
-rw-r--r-- | arch/arm/kernel/smp.c | 131 | ||||
-rw-r--r-- | arch/arm/kernel/smp_scu.c | 48 | ||||
-rw-r--r-- | arch/arm/kernel/smp_twd.c | 175 | ||||
-rw-r--r-- | arch/arm/kernel/vmlinux.lds.S | 2 |
9 files changed, 334 insertions, 40 deletions
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 11a5197a221f..ff89d0b3abc5 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile | |||
@@ -22,6 +22,8 @@ obj-$(CONFIG_ARTHUR) += arthur.o | |||
22 | obj-$(CONFIG_ISA_DMA) += dma-isa.o | 22 | obj-$(CONFIG_ISA_DMA) += dma-isa.o |
23 | obj-$(CONFIG_PCI) += bios32.o isa.o | 23 | obj-$(CONFIG_PCI) += bios32.o isa.o |
24 | obj-$(CONFIG_SMP) += smp.o | 24 | obj-$(CONFIG_SMP) += smp.o |
25 | obj-$(CONFIG_HAVE_ARM_SCU) += smp_scu.o | ||
26 | obj-$(CONFIG_HAVE_ARM_TWD) += smp_twd.o | ||
25 | obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o | 27 | obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o |
26 | obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o | 28 | obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o |
27 | obj-$(CONFIG_KPROBES) += kprobes.o kprobes-decode.o | 29 | obj-$(CONFIG_KPROBES) += kprobes.o kprobes-decode.o |
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S index 83b1da6b7baa..fc8af43c5000 100644 --- a/arch/arm/kernel/entry-armv.S +++ b/arch/arm/kernel/entry-armv.S | |||
@@ -482,6 +482,9 @@ __und_usr: | |||
482 | subeq r4, r2, #4 @ ARM instr at LR - 4 | 482 | subeq r4, r2, #4 @ ARM instr at LR - 4 |
483 | subne r4, r2, #2 @ Thumb instr at LR - 2 | 483 | subne r4, r2, #2 @ Thumb instr at LR - 2 |
484 | 1: ldreqt r0, [r4] | 484 | 1: ldreqt r0, [r4] |
485 | #ifdef CONFIG_CPU_ENDIAN_BE8 | ||
486 | reveq r0, r0 @ little endian instruction | ||
487 | #endif | ||
485 | beq call_fpe | 488 | beq call_fpe |
486 | @ Thumb instruction | 489 | @ Thumb instruction |
487 | #if __LINUX_ARM_ARCH__ >= 7 | 490 | #if __LINUX_ARM_ARCH__ >= 7 |
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S index b55cb0331809..366e5097a41a 100644 --- a/arch/arm/kernel/entry-common.S +++ b/arch/arm/kernel/entry-common.S | |||
@@ -210,6 +210,9 @@ ENTRY(vector_swi) | |||
210 | A710( teq ip, #0x0f000000 ) | 210 | A710( teq ip, #0x0f000000 ) |
211 | A710( bne .Larm710bug ) | 211 | A710( bne .Larm710bug ) |
212 | #endif | 212 | #endif |
213 | #ifdef CONFIG_CPU_ENDIAN_BE8 | ||
214 | rev r10, r10 @ little endian instruction | ||
215 | #endif | ||
213 | 216 | ||
214 | #elif defined(CONFIG_AEABI) | 217 | #elif defined(CONFIG_AEABI) |
215 | 218 | ||
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index c3265a2e7cd4..1585423699ee 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c | |||
@@ -365,7 +365,7 @@ pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) | |||
365 | regs.ARM_r2 = (unsigned long)fn; | 365 | regs.ARM_r2 = (unsigned long)fn; |
366 | regs.ARM_r3 = (unsigned long)do_exit; | 366 | regs.ARM_r3 = (unsigned long)do_exit; |
367 | regs.ARM_pc = (unsigned long)kernel_thread_helper; | 367 | regs.ARM_pc = (unsigned long)kernel_thread_helper; |
368 | regs.ARM_cpsr = SVC_MODE; | 368 | regs.ARM_cpsr = SVC_MODE | PSR_ENDSTATE; |
369 | 369 | ||
370 | return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); | 370 | return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); |
371 | } | 371 | } |
diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 80b8b5c7e07a..442b87476f97 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c | |||
@@ -426,9 +426,13 @@ setup_return(struct pt_regs *regs, struct k_sigaction *ka, | |||
426 | */ | 426 | */ |
427 | thumb = handler & 1; | 427 | thumb = handler & 1; |
428 | 428 | ||
429 | if (thumb) | 429 | if (thumb) { |
430 | cpsr |= PSR_T_BIT; | 430 | cpsr |= PSR_T_BIT; |
431 | else | 431 | #if __LINUX_ARM_ARCH__ >= 7 |
432 | /* clear the If-Then Thumb-2 execution state */ | ||
433 | cpsr &= ~PSR_IT_MASK; | ||
434 | #endif | ||
435 | } else | ||
432 | cpsr &= ~PSR_T_BIT; | 436 | cpsr &= ~PSR_T_BIT; |
433 | } | 437 | } |
434 | #endif | 438 | #endif |
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 6014dfd22af4..de885fd256c5 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c | |||
@@ -22,16 +22,20 @@ | |||
22 | #include <linux/smp.h> | 22 | #include <linux/smp.h> |
23 | #include <linux/seq_file.h> | 23 | #include <linux/seq_file.h> |
24 | #include <linux/irq.h> | 24 | #include <linux/irq.h> |
25 | #include <linux/percpu.h> | ||
26 | #include <linux/clockchips.h> | ||
25 | 27 | ||
26 | #include <asm/atomic.h> | 28 | #include <asm/atomic.h> |
27 | #include <asm/cacheflush.h> | 29 | #include <asm/cacheflush.h> |
28 | #include <asm/cpu.h> | 30 | #include <asm/cpu.h> |
31 | #include <asm/cputype.h> | ||
29 | #include <asm/mmu_context.h> | 32 | #include <asm/mmu_context.h> |
30 | #include <asm/pgtable.h> | 33 | #include <asm/pgtable.h> |
31 | #include <asm/pgalloc.h> | 34 | #include <asm/pgalloc.h> |
32 | #include <asm/processor.h> | 35 | #include <asm/processor.h> |
33 | #include <asm/tlbflush.h> | 36 | #include <asm/tlbflush.h> |
34 | #include <asm/ptrace.h> | 37 | #include <asm/ptrace.h> |
38 | #include <asm/localtimer.h> | ||
35 | 39 | ||
36 | /* | 40 | /* |
37 | * as from 2.5, kernels no longer have an init_tasks structure | 41 | * as from 2.5, kernels no longer have an init_tasks structure |
@@ -163,7 +167,7 @@ int __cpuexit __cpu_disable(void) | |||
163 | * Take this CPU offline. Once we clear this, we can't return, | 167 | * Take this CPU offline. Once we clear this, we can't return, |
164 | * and we must not schedule until we're ready to give up the cpu. | 168 | * and we must not schedule until we're ready to give up the cpu. |
165 | */ | 169 | */ |
166 | cpu_clear(cpu, cpu_online_map); | 170 | set_cpu_online(cpu, false); |
167 | 171 | ||
168 | /* | 172 | /* |
169 | * OK - migrate IRQs away from this CPU | 173 | * OK - migrate IRQs away from this CPU |
@@ -274,9 +278,9 @@ asmlinkage void __cpuinit secondary_start_kernel(void) | |||
274 | local_fiq_enable(); | 278 | local_fiq_enable(); |
275 | 279 | ||
276 | /* | 280 | /* |
277 | * Setup local timer for this CPU. | 281 | * Setup the percpu timer for this CPU. |
278 | */ | 282 | */ |
279 | local_timer_setup(); | 283 | percpu_timer_setup(); |
280 | 284 | ||
281 | calibrate_delay(); | 285 | calibrate_delay(); |
282 | 286 | ||
@@ -285,7 +289,7 @@ asmlinkage void __cpuinit secondary_start_kernel(void) | |||
285 | /* | 289 | /* |
286 | * OK, now it's safe to let the boot CPU continue | 290 | * OK, now it's safe to let the boot CPU continue |
287 | */ | 291 | */ |
288 | cpu_set(cpu, cpu_online_map); | 292 | set_cpu_online(cpu, true); |
289 | 293 | ||
290 | /* | 294 | /* |
291 | * OK, it's off to the idle thread for us | 295 | * OK, it's off to the idle thread for us |
@@ -383,10 +387,16 @@ void show_local_irqs(struct seq_file *p) | |||
383 | seq_putc(p, '\n'); | 387 | seq_putc(p, '\n'); |
384 | } | 388 | } |
385 | 389 | ||
390 | /* | ||
391 | * Timer (local or broadcast) support | ||
392 | */ | ||
393 | static DEFINE_PER_CPU(struct clock_event_device, percpu_clockevent); | ||
394 | |||
386 | static void ipi_timer(void) | 395 | static void ipi_timer(void) |
387 | { | 396 | { |
397 | struct clock_event_device *evt = &__get_cpu_var(percpu_clockevent); | ||
388 | irq_enter(); | 398 | irq_enter(); |
389 | local_timer_interrupt(); | 399 | evt->event_handler(evt); |
390 | irq_exit(); | 400 | irq_exit(); |
391 | } | 401 | } |
392 | 402 | ||
@@ -405,6 +415,42 @@ asmlinkage void __exception do_local_timer(struct pt_regs *regs) | |||
405 | } | 415 | } |
406 | #endif | 416 | #endif |
407 | 417 | ||
418 | #ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST | ||
419 | static void smp_timer_broadcast(const struct cpumask *mask) | ||
420 | { | ||
421 | send_ipi_message(mask, IPI_TIMER); | ||
422 | } | ||
423 | |||
424 | static void broadcast_timer_set_mode(enum clock_event_mode mode, | ||
425 | struct clock_event_device *evt) | ||
426 | { | ||
427 | } | ||
428 | |||
429 | static void local_timer_setup(struct clock_event_device *evt) | ||
430 | { | ||
431 | evt->name = "dummy_timer"; | ||
432 | evt->features = CLOCK_EVT_FEAT_ONESHOT | | ||
433 | CLOCK_EVT_FEAT_PERIODIC | | ||
434 | CLOCK_EVT_FEAT_DUMMY; | ||
435 | evt->rating = 400; | ||
436 | evt->mult = 1; | ||
437 | evt->set_mode = broadcast_timer_set_mode; | ||
438 | evt->broadcast = smp_timer_broadcast; | ||
439 | |||
440 | clockevents_register_device(evt); | ||
441 | } | ||
442 | #endif | ||
443 | |||
444 | void __cpuinit percpu_timer_setup(void) | ||
445 | { | ||
446 | unsigned int cpu = smp_processor_id(); | ||
447 | struct clock_event_device *evt = &per_cpu(percpu_clockevent, cpu); | ||
448 | |||
449 | evt->cpumask = cpumask_of(cpu); | ||
450 | |||
451 | local_timer_setup(evt); | ||
452 | } | ||
453 | |||
408 | static DEFINE_SPINLOCK(stop_lock); | 454 | static DEFINE_SPINLOCK(stop_lock); |
409 | 455 | ||
410 | /* | 456 | /* |
@@ -417,7 +463,7 @@ static void ipi_cpu_stop(unsigned int cpu) | |||
417 | dump_stack(); | 463 | dump_stack(); |
418 | spin_unlock(&stop_lock); | 464 | spin_unlock(&stop_lock); |
419 | 465 | ||
420 | cpu_clear(cpu, cpu_online_map); | 466 | set_cpu_online(cpu, false); |
421 | 467 | ||
422 | local_fiq_disable(); | 468 | local_fiq_disable(); |
423 | local_irq_disable(); | 469 | local_irq_disable(); |
@@ -501,11 +547,6 @@ void smp_send_reschedule(int cpu) | |||
501 | send_ipi_message(cpumask_of(cpu), IPI_RESCHEDULE); | 547 | send_ipi_message(cpumask_of(cpu), IPI_RESCHEDULE); |
502 | } | 548 | } |
503 | 549 | ||
504 | void smp_timer_broadcast(const struct cpumask *mask) | ||
505 | { | ||
506 | send_ipi_message(mask, IPI_TIMER); | ||
507 | } | ||
508 | |||
509 | void smp_send_stop(void) | 550 | void smp_send_stop(void) |
510 | { | 551 | { |
511 | cpumask_t mask = cpu_online_map; | 552 | cpumask_t mask = cpu_online_map; |
@@ -545,6 +586,12 @@ struct tlb_args { | |||
545 | unsigned long ta_end; | 586 | unsigned long ta_end; |
546 | }; | 587 | }; |
547 | 588 | ||
589 | /* all SMP configurations have the extended CPUID registers */ | ||
590 | static inline int tlb_ops_need_broadcast(void) | ||
591 | { | ||
592 | return ((read_cpuid_ext(CPUID_EXT_MMFR3) >> 12) & 0xf) < 2; | ||
593 | } | ||
594 | |||
548 | static inline void ipi_flush_tlb_all(void *ignored) | 595 | static inline void ipi_flush_tlb_all(void *ignored) |
549 | { | 596 | { |
550 | local_flush_tlb_all(); | 597 | local_flush_tlb_all(); |
@@ -587,51 +634,61 @@ static inline void ipi_flush_tlb_kernel_range(void *arg) | |||
587 | 634 | ||
588 | void flush_tlb_all(void) | 635 | void flush_tlb_all(void) |
589 | { | 636 | { |
590 | on_each_cpu(ipi_flush_tlb_all, NULL, 1); | 637 | if (tlb_ops_need_broadcast()) |
638 | on_each_cpu(ipi_flush_tlb_all, NULL, 1); | ||
639 | else | ||
640 | local_flush_tlb_all(); | ||
591 | } | 641 | } |
592 | 642 | ||
593 | void flush_tlb_mm(struct mm_struct *mm) | 643 | void flush_tlb_mm(struct mm_struct *mm) |
594 | { | 644 | { |
595 | on_each_cpu_mask(ipi_flush_tlb_mm, mm, 1, &mm->cpu_vm_mask); | 645 | if (tlb_ops_need_broadcast()) |
646 | on_each_cpu_mask(ipi_flush_tlb_mm, mm, 1, &mm->cpu_vm_mask); | ||
647 | else | ||
648 | local_flush_tlb_mm(mm); | ||
596 | } | 649 | } |
597 | 650 | ||
598 | void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr) | 651 | void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr) |
599 | { | 652 | { |
600 | struct tlb_args ta; | 653 | if (tlb_ops_need_broadcast()) { |
601 | 654 | struct tlb_args ta; | |
602 | ta.ta_vma = vma; | 655 | ta.ta_vma = vma; |
603 | ta.ta_start = uaddr; | 656 | ta.ta_start = uaddr; |
604 | 657 | on_each_cpu_mask(ipi_flush_tlb_page, &ta, 1, &vma->vm_mm->cpu_vm_mask); | |
605 | on_each_cpu_mask(ipi_flush_tlb_page, &ta, 1, &vma->vm_mm->cpu_vm_mask); | 658 | } else |
659 | local_flush_tlb_page(vma, uaddr); | ||
606 | } | 660 | } |
607 | 661 | ||
608 | void flush_tlb_kernel_page(unsigned long kaddr) | 662 | void flush_tlb_kernel_page(unsigned long kaddr) |
609 | { | 663 | { |
610 | struct tlb_args ta; | 664 | if (tlb_ops_need_broadcast()) { |
611 | 665 | struct tlb_args ta; | |
612 | ta.ta_start = kaddr; | 666 | ta.ta_start = kaddr; |
613 | 667 | on_each_cpu(ipi_flush_tlb_kernel_page, &ta, 1); | |
614 | on_each_cpu(ipi_flush_tlb_kernel_page, &ta, 1); | 668 | } else |
669 | local_flush_tlb_kernel_page(kaddr); | ||
615 | } | 670 | } |
616 | 671 | ||
617 | void flush_tlb_range(struct vm_area_struct *vma, | 672 | void flush_tlb_range(struct vm_area_struct *vma, |
618 | unsigned long start, unsigned long end) | 673 | unsigned long start, unsigned long end) |
619 | { | 674 | { |
620 | struct tlb_args ta; | 675 | if (tlb_ops_need_broadcast()) { |
621 | 676 | struct tlb_args ta; | |
622 | ta.ta_vma = vma; | 677 | ta.ta_vma = vma; |
623 | ta.ta_start = start; | 678 | ta.ta_start = start; |
624 | ta.ta_end = end; | 679 | ta.ta_end = end; |
625 | 680 | on_each_cpu_mask(ipi_flush_tlb_range, &ta, 1, &vma->vm_mm->cpu_vm_mask); | |
626 | on_each_cpu_mask(ipi_flush_tlb_range, &ta, 1, &vma->vm_mm->cpu_vm_mask); | 681 | } else |
682 | local_flush_tlb_range(vma, start, end); | ||
627 | } | 683 | } |
628 | 684 | ||
629 | void flush_tlb_kernel_range(unsigned long start, unsigned long end) | 685 | void flush_tlb_kernel_range(unsigned long start, unsigned long end) |
630 | { | 686 | { |
631 | struct tlb_args ta; | 687 | if (tlb_ops_need_broadcast()) { |
632 | 688 | struct tlb_args ta; | |
633 | ta.ta_start = start; | 689 | ta.ta_start = start; |
634 | ta.ta_end = end; | 690 | ta.ta_end = end; |
635 | 691 | on_each_cpu(ipi_flush_tlb_kernel_range, &ta, 1); | |
636 | on_each_cpu(ipi_flush_tlb_kernel_range, &ta, 1); | 692 | } else |
693 | local_flush_tlb_kernel_range(start, end); | ||
637 | } | 694 | } |
diff --git a/arch/arm/kernel/smp_scu.c b/arch/arm/kernel/smp_scu.c new file mode 100644 index 000000000000..d3831f616ee9 --- /dev/null +++ b/arch/arm/kernel/smp_scu.c | |||
@@ -0,0 +1,48 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/kernel/smp_scu.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/io.h> | ||
13 | |||
14 | #include <asm/smp_scu.h> | ||
15 | #include <asm/cacheflush.h> | ||
16 | |||
17 | #define SCU_CTRL 0x00 | ||
18 | #define SCU_CONFIG 0x04 | ||
19 | #define SCU_CPU_STATUS 0x08 | ||
20 | #define SCU_INVALIDATE 0x0c | ||
21 | #define SCU_FPGA_REVISION 0x10 | ||
22 | |||
23 | /* | ||
24 | * Get the number of CPU cores from the SCU configuration | ||
25 | */ | ||
26 | unsigned int __init scu_get_core_count(void __iomem *scu_base) | ||
27 | { | ||
28 | unsigned int ncores = __raw_readl(scu_base + SCU_CONFIG); | ||
29 | return (ncores & 0x03) + 1; | ||
30 | } | ||
31 | |||
32 | /* | ||
33 | * Enable the SCU | ||
34 | */ | ||
35 | void __init scu_enable(void __iomem *scu_base) | ||
36 | { | ||
37 | u32 scu_ctrl; | ||
38 | |||
39 | scu_ctrl = __raw_readl(scu_base + SCU_CTRL); | ||
40 | scu_ctrl |= 1; | ||
41 | __raw_writel(scu_ctrl, scu_base + SCU_CTRL); | ||
42 | |||
43 | /* | ||
44 | * Ensure that the data accessed by CPU0 before the SCU was | ||
45 | * initialised is visible to the other CPUs. | ||
46 | */ | ||
47 | flush_cache_all(); | ||
48 | } | ||
diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c new file mode 100644 index 000000000000..d8c88c633c6f --- /dev/null +++ b/arch/arm/kernel/smp_twd.c | |||
@@ -0,0 +1,175 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/kernel/smp_twd.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 | #include <linux/clockchips.h> | ||
18 | #include <linux/irq.h> | ||
19 | #include <linux/io.h> | ||
20 | |||
21 | #include <asm/smp_twd.h> | ||
22 | #include <asm/hardware/gic.h> | ||
23 | |||
24 | #define TWD_TIMER_LOAD 0x00 | ||
25 | #define TWD_TIMER_COUNTER 0x04 | ||
26 | #define TWD_TIMER_CONTROL 0x08 | ||
27 | #define TWD_TIMER_INTSTAT 0x0C | ||
28 | |||
29 | #define TWD_WDOG_LOAD 0x20 | ||
30 | #define TWD_WDOG_COUNTER 0x24 | ||
31 | #define TWD_WDOG_CONTROL 0x28 | ||
32 | #define TWD_WDOG_INTSTAT 0x2C | ||
33 | #define TWD_WDOG_RESETSTAT 0x30 | ||
34 | #define TWD_WDOG_DISABLE 0x34 | ||
35 | |||
36 | #define TWD_TIMER_CONTROL_ENABLE (1 << 0) | ||
37 | #define TWD_TIMER_CONTROL_ONESHOT (0 << 1) | ||
38 | #define TWD_TIMER_CONTROL_PERIODIC (1 << 1) | ||
39 | #define TWD_TIMER_CONTROL_IT_ENABLE (1 << 2) | ||
40 | |||
41 | /* set up by the platform code */ | ||
42 | void __iomem *twd_base; | ||
43 | |||
44 | static unsigned long twd_timer_rate; | ||
45 | |||
46 | static void twd_set_mode(enum clock_event_mode mode, | ||
47 | struct clock_event_device *clk) | ||
48 | { | ||
49 | unsigned long ctrl; | ||
50 | |||
51 | switch (mode) { | ||
52 | case CLOCK_EVT_MODE_PERIODIC: | ||
53 | /* timer load already set up */ | ||
54 | ctrl = TWD_TIMER_CONTROL_ENABLE | TWD_TIMER_CONTROL_IT_ENABLE | ||
55 | | TWD_TIMER_CONTROL_PERIODIC; | ||
56 | break; | ||
57 | case CLOCK_EVT_MODE_ONESHOT: | ||
58 | /* period set, and timer enabled in 'next_event' hook */ | ||
59 | ctrl = TWD_TIMER_CONTROL_IT_ENABLE | TWD_TIMER_CONTROL_ONESHOT; | ||
60 | break; | ||
61 | case CLOCK_EVT_MODE_UNUSED: | ||
62 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
63 | default: | ||
64 | ctrl = 0; | ||
65 | } | ||
66 | |||
67 | __raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL); | ||
68 | } | ||
69 | |||
70 | static int twd_set_next_event(unsigned long evt, | ||
71 | struct clock_event_device *unused) | ||
72 | { | ||
73 | unsigned long ctrl = __raw_readl(twd_base + TWD_TIMER_CONTROL); | ||
74 | |||
75 | ctrl |= TWD_TIMER_CONTROL_ENABLE; | ||
76 | |||
77 | __raw_writel(evt, twd_base + TWD_TIMER_COUNTER); | ||
78 | __raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL); | ||
79 | |||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | /* | ||
84 | * local_timer_ack: checks for a local timer interrupt. | ||
85 | * | ||
86 | * If a local timer interrupt has occurred, acknowledge and return 1. | ||
87 | * Otherwise, return 0. | ||
88 | */ | ||
89 | int twd_timer_ack(void) | ||
90 | { | ||
91 | if (__raw_readl(twd_base + TWD_TIMER_INTSTAT)) { | ||
92 | __raw_writel(1, twd_base + TWD_TIMER_INTSTAT); | ||
93 | return 1; | ||
94 | } | ||
95 | |||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | static void __cpuinit twd_calibrate_rate(void) | ||
100 | { | ||
101 | unsigned long load, count; | ||
102 | u64 waitjiffies; | ||
103 | |||
104 | /* | ||
105 | * If this is the first time round, we need to work out how fast | ||
106 | * the timer ticks | ||
107 | */ | ||
108 | if (twd_timer_rate == 0) { | ||
109 | printk(KERN_INFO "Calibrating local timer... "); | ||
110 | |||
111 | /* Wait for a tick to start */ | ||
112 | waitjiffies = get_jiffies_64() + 1; | ||
113 | |||
114 | while (get_jiffies_64() < waitjiffies) | ||
115 | udelay(10); | ||
116 | |||
117 | /* OK, now the tick has started, let's get the timer going */ | ||
118 | waitjiffies += 5; | ||
119 | |||
120 | /* enable, no interrupt or reload */ | ||
121 | __raw_writel(0x1, twd_base + TWD_TIMER_CONTROL); | ||
122 | |||
123 | /* maximum value */ | ||
124 | __raw_writel(0xFFFFFFFFU, twd_base + TWD_TIMER_COUNTER); | ||
125 | |||
126 | while (get_jiffies_64() < waitjiffies) | ||
127 | udelay(10); | ||
128 | |||
129 | count = __raw_readl(twd_base + TWD_TIMER_COUNTER); | ||
130 | |||
131 | twd_timer_rate = (0xFFFFFFFFU - count) * (HZ / 5); | ||
132 | |||
133 | printk("%lu.%02luMHz.\n", twd_timer_rate / 1000000, | ||
134 | (twd_timer_rate / 100000) % 100); | ||
135 | } | ||
136 | |||
137 | load = twd_timer_rate / HZ; | ||
138 | |||
139 | __raw_writel(load, twd_base + TWD_TIMER_LOAD); | ||
140 | } | ||
141 | |||
142 | /* | ||
143 | * Setup the local clock events for a CPU. | ||
144 | */ | ||
145 | void __cpuinit twd_timer_setup(struct clock_event_device *clk) | ||
146 | { | ||
147 | unsigned long flags; | ||
148 | |||
149 | twd_calibrate_rate(); | ||
150 | |||
151 | clk->name = "local_timer"; | ||
152 | clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; | ||
153 | clk->rating = 350; | ||
154 | clk->set_mode = twd_set_mode; | ||
155 | clk->set_next_event = twd_set_next_event; | ||
156 | clk->shift = 20; | ||
157 | clk->mult = div_sc(twd_timer_rate, NSEC_PER_SEC, clk->shift); | ||
158 | clk->max_delta_ns = clockevent_delta2ns(0xffffffff, clk); | ||
159 | clk->min_delta_ns = clockevent_delta2ns(0xf, clk); | ||
160 | |||
161 | /* Make sure our local interrupt controller has this enabled */ | ||
162 | local_irq_save(flags); | ||
163 | get_irq_chip(clk->irq)->unmask(clk->irq); | ||
164 | local_irq_restore(flags); | ||
165 | |||
166 | clockevents_register_device(clk); | ||
167 | } | ||
168 | |||
169 | /* | ||
170 | * take a local timer down | ||
171 | */ | ||
172 | void __cpuexit twd_timer_stop(void) | ||
173 | { | ||
174 | __raw_writel(0, twd_base + TWD_TIMER_CONTROL); | ||
175 | } | ||
diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S index c90f27250ead..6c0779792546 100644 --- a/arch/arm/kernel/vmlinux.lds.S +++ b/arch/arm/kernel/vmlinux.lds.S | |||
@@ -141,6 +141,7 @@ SECTIONS | |||
141 | 141 | ||
142 | .data : AT(__data_loc) { | 142 | .data : AT(__data_loc) { |
143 | _data = .; /* address in memory */ | 143 | _data = .; /* address in memory */ |
144 | _sdata = .; | ||
144 | 145 | ||
145 | /* | 146 | /* |
146 | * first, the init task union, aligned | 147 | * first, the init task union, aligned |
@@ -192,6 +193,7 @@ SECTIONS | |||
192 | __bss_start = .; /* BSS */ | 193 | __bss_start = .; /* BSS */ |
193 | *(.bss) | 194 | *(.bss) |
194 | *(COMMON) | 195 | *(COMMON) |
196 | __bss_stop = .; | ||
195 | _end = .; | 197 | _end = .; |
196 | } | 198 | } |
197 | /* Stabs debugging sections. */ | 199 | /* Stabs debugging sections. */ |