diff options
author | Barry Song <Baohua.Song@csr.com> | 2012-12-20 06:37:32 -0500 |
---|---|---|
committer | Barry Song <Barry.Song@csr.com> | 2013-01-22 06:53:27 -0500 |
commit | 4898de3d15d8ba34aa7a1b0f753a476d52ebdf92 (patch) | |
tree | e2606fb82bab7bcbe0503404ecd67fd0d3d47427 /arch | |
parent | f2a94192d953990c5c928f52dd4122a67f93b980 (diff) |
ARM: PRIMA2: add new SiRFmarco SMP SoC infrastructures
this patch adds tick timer, smp entries and generic DT machine
for SiRFmarco dual-core SMP chips.
with the added marco, we change the defconfig, using the same
defconfig, we get a zImage which can work on both prima2 and
marco.
Signed-off-by: Barry Song <Baohua.Song@csr.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/boot/dts/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/configs/prima2_defconfig | 3 | ||||
-rw-r--r-- | arch/arm/mach-prima2/Kconfig | 10 | ||||
-rw-r--r-- | arch/arm/mach-prima2/Makefile | 3 | ||||
-rw-r--r-- | arch/arm/mach-prima2/common.c | 40 | ||||
-rw-r--r-- | arch/arm/mach-prima2/common.h | 11 | ||||
-rw-r--r-- | arch/arm/mach-prima2/headsmp.S | 79 | ||||
-rw-r--r-- | arch/arm/mach-prima2/hotplug.c | 41 | ||||
-rw-r--r-- | arch/arm/mach-prima2/platsmp.c | 163 | ||||
-rw-r--r-- | arch/arm/mach-prima2/timer-marco.c | 316 |
10 files changed, 666 insertions, 1 deletions
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index e44da40d984f..6af9901d3d2f 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile | |||
@@ -73,6 +73,7 @@ dtb-$(CONFIG_ARCH_KIRKWOOD) += kirkwood-dns320.dtb \ | |||
73 | kirkwood-ts219-6281.dtb \ | 73 | kirkwood-ts219-6281.dtb \ |
74 | kirkwood-ts219-6282.dtb \ | 74 | kirkwood-ts219-6282.dtb \ |
75 | kirkwood-openblocks_a6.dtb | 75 | kirkwood-openblocks_a6.dtb |
76 | dtb-$(CONFIG_ARCH_MARCO) += marco-evb.dtb | ||
76 | dtb-$(CONFIG_ARCH_MSM) += msm8660-surf.dtb \ | 77 | dtb-$(CONFIG_ARCH_MSM) += msm8660-surf.dtb \ |
77 | msm8960-cdp.dtb | 78 | msm8960-cdp.dtb |
78 | dtb-$(CONFIG_ARCH_MVEBU) += armada-370-db.dtb \ | 79 | dtb-$(CONFIG_ARCH_MVEBU) += armada-370-db.dtb \ |
diff --git a/arch/arm/configs/prima2_defconfig b/arch/arm/configs/prima2_defconfig index 6a936c7c078a..002a1ceadceb 100644 --- a/arch/arm/configs/prima2_defconfig +++ b/arch/arm/configs/prima2_defconfig | |||
@@ -11,6 +11,9 @@ CONFIG_PARTITION_ADVANCED=y | |||
11 | CONFIG_BSD_DISKLABEL=y | 11 | CONFIG_BSD_DISKLABEL=y |
12 | CONFIG_SOLARIS_X86_PARTITION=y | 12 | CONFIG_SOLARIS_X86_PARTITION=y |
13 | CONFIG_ARCH_SIRF=y | 13 | CONFIG_ARCH_SIRF=y |
14 | # CONFIG_SWP_EMULATE is not set | ||
15 | CONFIG_SMP=y | ||
16 | CONFIG_SCHED_MC=y | ||
14 | CONFIG_PREEMPT=y | 17 | CONFIG_PREEMPT=y |
15 | CONFIG_AEABI=y | 18 | CONFIG_AEABI=y |
16 | CONFIG_KEXEC=y | 19 | CONFIG_KEXEC=y |
diff --git a/arch/arm/mach-prima2/Kconfig b/arch/arm/mach-prima2/Kconfig index 558ccfb8d458..4f7379fe01e2 100644 --- a/arch/arm/mach-prima2/Kconfig +++ b/arch/arm/mach-prima2/Kconfig | |||
@@ -11,6 +11,16 @@ config ARCH_PRIMA2 | |||
11 | help | 11 | help |
12 | Support for CSR SiRFSoC ARM Cortex A9 Platform | 12 | Support for CSR SiRFSoC ARM Cortex A9 Platform |
13 | 13 | ||
14 | config ARCH_MARCO | ||
15 | bool "CSR SiRFSoC MARCO ARM Cortex A9 Platform" | ||
16 | default y | ||
17 | select ARM_GIC | ||
18 | select CPU_V7 | ||
19 | select HAVE_SMP | ||
20 | select SMP_ON_UP | ||
21 | help | ||
22 | Support for CSR SiRFSoC ARM Cortex A9 Platform | ||
23 | |||
14 | endmenu | 24 | endmenu |
15 | 25 | ||
16 | config SIRF_IRQ | 26 | config SIRF_IRQ |
diff --git a/arch/arm/mach-prima2/Makefile b/arch/arm/mach-prima2/Makefile index 0007a6ec78f2..bfe360cbd177 100644 --- a/arch/arm/mach-prima2/Makefile +++ b/arch/arm/mach-prima2/Makefile | |||
@@ -5,4 +5,7 @@ obj-$(CONFIG_DEBUG_LL) += lluart.o | |||
5 | obj-$(CONFIG_CACHE_L2X0) += l2x0.o | 5 | obj-$(CONFIG_CACHE_L2X0) += l2x0.o |
6 | obj-$(CONFIG_SUSPEND) += pm.o sleep.o | 6 | obj-$(CONFIG_SUSPEND) += pm.o sleep.o |
7 | obj-$(CONFIG_SIRF_IRQ) += irq.o | 7 | obj-$(CONFIG_SIRF_IRQ) += irq.o |
8 | obj-$(CONFIG_SMP) += platsmp.o headsmp.o | ||
9 | obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o | ||
8 | obj-$(CONFIG_ARCH_PRIMA2) += timer-prima2.o | 10 | obj-$(CONFIG_ARCH_PRIMA2) += timer-prima2.o |
11 | obj-$(CONFIG_ARCH_MARCO) += timer-marco.o | ||
diff --git a/arch/arm/mach-prima2/common.c b/arch/arm/mach-prima2/common.c index 99f9c7e391f5..00a65649a7e2 100644 --- a/arch/arm/mach-prima2/common.c +++ b/arch/arm/mach-prima2/common.c | |||
@@ -8,9 +8,11 @@ | |||
8 | 8 | ||
9 | #include <linux/init.h> | 9 | #include <linux/init.h> |
10 | #include <linux/kernel.h> | 10 | #include <linux/kernel.h> |
11 | #include <linux/of_irq.h> | ||
11 | #include <asm/sizes.h> | 12 | #include <asm/sizes.h> |
12 | #include <asm/mach-types.h> | 13 | #include <asm/mach-types.h> |
13 | #include <asm/mach/arch.h> | 14 | #include <asm/mach/arch.h> |
15 | #include <asm/hardware/gic.h> | ||
14 | #include <linux/of.h> | 16 | #include <linux/of.h> |
15 | #include <linux/of_platform.h> | 17 | #include <linux/of_platform.h> |
16 | #include "common.h" | 18 | #include "common.h" |
@@ -30,6 +32,12 @@ void __init sirfsoc_init_late(void) | |||
30 | sirfsoc_pm_init(); | 32 | sirfsoc_pm_init(); |
31 | } | 33 | } |
32 | 34 | ||
35 | static __init void sirfsoc_map_io(void) | ||
36 | { | ||
37 | sirfsoc_map_lluart(); | ||
38 | sirfsoc_map_scu(); | ||
39 | } | ||
40 | |||
33 | #ifdef CONFIG_ARCH_PRIMA2 | 41 | #ifdef CONFIG_ARCH_PRIMA2 |
34 | static const char *prima2_dt_match[] __initdata = { | 42 | static const char *prima2_dt_match[] __initdata = { |
35 | "sirf,prima2", | 43 | "sirf,prima2", |
@@ -38,7 +46,7 @@ static const char *prima2_dt_match[] __initdata = { | |||
38 | 46 | ||
39 | DT_MACHINE_START(PRIMA2_DT, "Generic PRIMA2 (Flattened Device Tree)") | 47 | DT_MACHINE_START(PRIMA2_DT, "Generic PRIMA2 (Flattened Device Tree)") |
40 | /* Maintainer: Barry Song <baohua.song@csr.com> */ | 48 | /* Maintainer: Barry Song <baohua.song@csr.com> */ |
41 | .map_io = sirfsoc_map_lluart, | 49 | .map_io = sirfsoc_map_io, |
42 | .init_irq = sirfsoc_of_irq_init, | 50 | .init_irq = sirfsoc_of_irq_init, |
43 | .init_time = sirfsoc_prima2_timer_init, | 51 | .init_time = sirfsoc_prima2_timer_init, |
44 | #ifdef CONFIG_MULTI_IRQ_HANDLER | 52 | #ifdef CONFIG_MULTI_IRQ_HANDLER |
@@ -51,3 +59,33 @@ DT_MACHINE_START(PRIMA2_DT, "Generic PRIMA2 (Flattened Device Tree)") | |||
51 | .restart = sirfsoc_restart, | 59 | .restart = sirfsoc_restart, |
52 | MACHINE_END | 60 | MACHINE_END |
53 | #endif | 61 | #endif |
62 | |||
63 | #ifdef CONFIG_ARCH_MARCO | ||
64 | static const struct of_device_id marco_irq_match[] __initconst = { | ||
65 | { .compatible = "arm,cortex-a9-gic", .data = gic_of_init, }, | ||
66 | { /* sentinel */ } | ||
67 | }; | ||
68 | |||
69 | static void __init marco_init_irq(void) | ||
70 | { | ||
71 | of_irq_init(marco_irq_match); | ||
72 | } | ||
73 | |||
74 | static const char *marco_dt_match[] __initdata = { | ||
75 | "sirf,marco", | ||
76 | NULL | ||
77 | }; | ||
78 | |||
79 | DT_MACHINE_START(MARCO_DT, "Generic MARCO (Flattened Device Tree)") | ||
80 | /* Maintainer: Barry Song <baohua.song@csr.com> */ | ||
81 | .smp = smp_ops(sirfsoc_smp_ops), | ||
82 | .map_io = sirfsoc_map_io, | ||
83 | .init_irq = marco_init_irq, | ||
84 | .init_time = sirfsoc_marco_timer_init, | ||
85 | .handle_irq = gic_handle_irq, | ||
86 | .init_machine = sirfsoc_mach_init, | ||
87 | .init_late = sirfsoc_init_late, | ||
88 | .dt_compat = marco_dt_match, | ||
89 | .restart = sirfsoc_restart, | ||
90 | MACHINE_END | ||
91 | #endif | ||
diff --git a/arch/arm/mach-prima2/common.h b/arch/arm/mach-prima2/common.h index a4f91a6de55e..b7c26b62e4a7 100644 --- a/arch/arm/mach-prima2/common.h +++ b/arch/arm/mach-prima2/common.h | |||
@@ -14,6 +14,11 @@ | |||
14 | #include <asm/exception.h> | 14 | #include <asm/exception.h> |
15 | 15 | ||
16 | extern void sirfsoc_prima2_timer_init(void); | 16 | extern void sirfsoc_prima2_timer_init(void); |
17 | extern void sirfsoc_marco_timer_init(void); | ||
18 | |||
19 | extern struct smp_operations sirfsoc_smp_ops; | ||
20 | extern void sirfsoc_secondary_startup(void); | ||
21 | extern void sirfsoc_cpu_die(unsigned int cpu); | ||
17 | 22 | ||
18 | extern void __init sirfsoc_of_irq_init(void); | 23 | extern void __init sirfsoc_of_irq_init(void); |
19 | extern void __init sirfsoc_of_clk_init(void); | 24 | extern void __init sirfsoc_of_clk_init(void); |
@@ -26,6 +31,12 @@ static inline void sirfsoc_map_lluart(void) {} | |||
26 | extern void __init sirfsoc_map_lluart(void); | 31 | extern void __init sirfsoc_map_lluart(void); |
27 | #endif | 32 | #endif |
28 | 33 | ||
34 | #ifndef CONFIG_SMP | ||
35 | static inline void sirfsoc_map_scu(void) {} | ||
36 | #else | ||
37 | extern void sirfsoc_map_scu(void); | ||
38 | #endif | ||
39 | |||
29 | #ifdef CONFIG_SUSPEND | 40 | #ifdef CONFIG_SUSPEND |
30 | extern int sirfsoc_pm_init(void); | 41 | extern int sirfsoc_pm_init(void); |
31 | #else | 42 | #else |
diff --git a/arch/arm/mach-prima2/headsmp.S b/arch/arm/mach-prima2/headsmp.S new file mode 100644 index 000000000000..6ec19d51a271 --- /dev/null +++ b/arch/arm/mach-prima2/headsmp.S | |||
@@ -0,0 +1,79 @@ | |||
1 | /* | ||
2 | * Entry of the second core for CSR Marco dual-core SMP SoCs | ||
3 | * | ||
4 | * Copyright (c) 2012 Cambridge Silicon Radio Limited, a CSR plc group company. | ||
5 | * | ||
6 | * Licensed under GPLv2 or later. | ||
7 | */ | ||
8 | |||
9 | #include <linux/linkage.h> | ||
10 | #include <linux/init.h> | ||
11 | |||
12 | __INIT | ||
13 | /* | ||
14 | * Cold boot and hardware reset show different behaviour, | ||
15 | * system will be always panic if we warm-reset the board | ||
16 | * Here we invalidate L1 of CPU1 to make sure there isn't | ||
17 | * uninitialized data written into memory later | ||
18 | */ | ||
19 | ENTRY(v7_invalidate_l1) | ||
20 | mov r0, #0 | ||
21 | mcr p15, 0, r0, c7, c5, 0 @ invalidate I cache | ||
22 | mcr p15, 2, r0, c0, c0, 0 | ||
23 | mrc p15, 1, r0, c0, c0, 0 | ||
24 | |||
25 | ldr r1, =0x7fff | ||
26 | and r2, r1, r0, lsr #13 | ||
27 | |||
28 | ldr r1, =0x3ff | ||
29 | |||
30 | and r3, r1, r0, lsr #3 @ NumWays - 1 | ||
31 | add r2, r2, #1 @ NumSets | ||
32 | |||
33 | and r0, r0, #0x7 | ||
34 | add r0, r0, #4 @ SetShift | ||
35 | |||
36 | clz r1, r3 @ WayShift | ||
37 | add r4, r3, #1 @ NumWays | ||
38 | 1: sub r2, r2, #1 @ NumSets-- | ||
39 | mov r3, r4 @ Temp = NumWays | ||
40 | 2: subs r3, r3, #1 @ Temp-- | ||
41 | mov r5, r3, lsl r1 | ||
42 | mov r6, r2, lsl r0 | ||
43 | orr r5, r5, r6 @ Reg = (Temp<<WayShift)|(NumSets<<SetShift) | ||
44 | mcr p15, 0, r5, c7, c6, 2 | ||
45 | bgt 2b | ||
46 | cmp r2, #0 | ||
47 | bgt 1b | ||
48 | dsb | ||
49 | isb | ||
50 | mov pc, lr | ||
51 | ENDPROC(v7_invalidate_l1) | ||
52 | |||
53 | /* | ||
54 | * SIRFSOC specific entry point for secondary CPUs. This provides | ||
55 | * a "holding pen" into which all secondary cores are held until we're | ||
56 | * ready for them to initialise. | ||
57 | */ | ||
58 | ENTRY(sirfsoc_secondary_startup) | ||
59 | bl v7_invalidate_l1 | ||
60 | mrc p15, 0, r0, c0, c0, 5 | ||
61 | and r0, r0, #15 | ||
62 | adr r4, 1f | ||
63 | ldmia r4, {r5, r6} | ||
64 | sub r4, r4, r5 | ||
65 | add r6, r6, r4 | ||
66 | pen: ldr r7, [r6] | ||
67 | cmp r7, r0 | ||
68 | bne pen | ||
69 | |||
70 | /* | ||
71 | * we've been released from the holding pen: secondary_stack | ||
72 | * should now contain the SVC stack for this core | ||
73 | */ | ||
74 | b secondary_startup | ||
75 | ENDPROC(sirfsoc_secondary_startup) | ||
76 | |||
77 | .align | ||
78 | 1: .long . | ||
79 | .long pen_release | ||
diff --git a/arch/arm/mach-prima2/hotplug.c b/arch/arm/mach-prima2/hotplug.c new file mode 100644 index 000000000000..97c1ee586442 --- /dev/null +++ b/arch/arm/mach-prima2/hotplug.c | |||
@@ -0,0 +1,41 @@ | |||
1 | /* | ||
2 | * CPU hotplug support for CSR Marco dual-core SMP SoCs | ||
3 | * | ||
4 | * Copyright (c) 2012 Cambridge Silicon Radio Limited, a CSR plc group company. | ||
5 | * | ||
6 | * Licensed under GPLv2 or later. | ||
7 | */ | ||
8 | |||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/errno.h> | ||
11 | #include <linux/smp.h> | ||
12 | |||
13 | #include <asm/cacheflush.h> | ||
14 | #include <asm/smp_plat.h> | ||
15 | |||
16 | static inline void platform_do_lowpower(unsigned int cpu) | ||
17 | { | ||
18 | flush_cache_all(); | ||
19 | |||
20 | /* we put the platform to just WFI */ | ||
21 | for (;;) { | ||
22 | __asm__ __volatile__("dsb\n\t" "wfi\n\t" | ||
23 | : : : "memory"); | ||
24 | if (pen_release == cpu_logical_map(cpu)) { | ||
25 | /* | ||
26 | * OK, proper wakeup, we're done | ||
27 | */ | ||
28 | break; | ||
29 | } | ||
30 | } | ||
31 | } | ||
32 | |||
33 | /* | ||
34 | * platform-specific code to shutdown a CPU | ||
35 | * | ||
36 | * Called with IRQs disabled | ||
37 | */ | ||
38 | void sirfsoc_cpu_die(unsigned int cpu) | ||
39 | { | ||
40 | platform_do_lowpower(cpu); | ||
41 | } | ||
diff --git a/arch/arm/mach-prima2/platsmp.c b/arch/arm/mach-prima2/platsmp.c new file mode 100644 index 000000000000..2395022bc733 --- /dev/null +++ b/arch/arm/mach-prima2/platsmp.c | |||
@@ -0,0 +1,163 @@ | |||
1 | /* | ||
2 | * plat smp support for CSR Marco dual-core SMP SoCs | ||
3 | * | ||
4 | * Copyright (c) 2012 Cambridge Silicon Radio Limited, a CSR plc group company. | ||
5 | * | ||
6 | * Licensed under GPLv2 or later. | ||
7 | */ | ||
8 | |||
9 | #include <linux/init.h> | ||
10 | #include <linux/smp.h> | ||
11 | #include <linux/delay.h> | ||
12 | #include <linux/of.h> | ||
13 | #include <linux/of_address.h> | ||
14 | #include <asm/page.h> | ||
15 | #include <asm/mach/map.h> | ||
16 | #include <asm/smp_plat.h> | ||
17 | #include <asm/smp_scu.h> | ||
18 | #include <asm/cacheflush.h> | ||
19 | #include <asm/cputype.h> | ||
20 | #include <asm/hardware/gic.h> | ||
21 | #include <mach/map.h> | ||
22 | |||
23 | #include "common.h" | ||
24 | |||
25 | static void __iomem *scu_base; | ||
26 | static void __iomem *rsc_base; | ||
27 | |||
28 | static DEFINE_SPINLOCK(boot_lock); | ||
29 | |||
30 | static struct map_desc scu_io_desc __initdata = { | ||
31 | .length = SZ_4K, | ||
32 | .type = MT_DEVICE, | ||
33 | }; | ||
34 | |||
35 | void __init sirfsoc_map_scu(void) | ||
36 | { | ||
37 | unsigned long base; | ||
38 | |||
39 | /* Get SCU base */ | ||
40 | asm("mrc p15, 4, %0, c15, c0, 0" : "=r" (base)); | ||
41 | |||
42 | scu_io_desc.virtual = SIRFSOC_VA(base); | ||
43 | scu_io_desc.pfn = __phys_to_pfn(base); | ||
44 | iotable_init(&scu_io_desc, 1); | ||
45 | |||
46 | scu_base = (void __iomem *)SIRFSOC_VA(base); | ||
47 | } | ||
48 | |||
49 | static void __cpuinit sirfsoc_secondary_init(unsigned int cpu) | ||
50 | { | ||
51 | /* | ||
52 | * if any interrupts are already enabled for the primary | ||
53 | * core (e.g. timer irq), then they will not have been enabled | ||
54 | * for us: do so | ||
55 | */ | ||
56 | gic_secondary_init(0); | ||
57 | |||
58 | /* | ||
59 | * let the primary processor know we're out of the | ||
60 | * pen, then head off into the C entry point | ||
61 | */ | ||
62 | pen_release = -1; | ||
63 | smp_wmb(); | ||
64 | |||
65 | /* | ||
66 | * Synchronise with the boot thread. | ||
67 | */ | ||
68 | spin_lock(&boot_lock); | ||
69 | spin_unlock(&boot_lock); | ||
70 | } | ||
71 | |||
72 | static struct of_device_id rsc_ids[] = { | ||
73 | { .compatible = "sirf,marco-rsc" }, | ||
74 | {}, | ||
75 | }; | ||
76 | |||
77 | static int __cpuinit sirfsoc_boot_secondary(unsigned int cpu, struct task_struct *idle) | ||
78 | { | ||
79 | unsigned long timeout; | ||
80 | struct device_node *np; | ||
81 | |||
82 | np = of_find_matching_node(NULL, rsc_ids); | ||
83 | if (!np) | ||
84 | return -ENODEV; | ||
85 | |||
86 | rsc_base = of_iomap(np, 0); | ||
87 | if (!rsc_base) | ||
88 | return -ENOMEM; | ||
89 | |||
90 | /* | ||
91 | * write the address of secondary startup into the sram register | ||
92 | * at offset 0x2C, then write the magic number 0x3CAF5D62 to the | ||
93 | * RSC register at offset 0x28, which is what boot rom code is | ||
94 | * waiting for. This would wake up the secondary core from WFE | ||
95 | */ | ||
96 | #define SIRFSOC_CPU1_JUMPADDR_OFFSET 0x2C | ||
97 | __raw_writel(virt_to_phys(sirfsoc_secondary_startup), | ||
98 | rsc_base + SIRFSOC_CPU1_JUMPADDR_OFFSET); | ||
99 | |||
100 | #define SIRFSOC_CPU1_WAKEMAGIC_OFFSET 0x28 | ||
101 | __raw_writel(0x3CAF5D62, | ||
102 | rsc_base + SIRFSOC_CPU1_WAKEMAGIC_OFFSET); | ||
103 | |||
104 | /* make sure write buffer is drained */ | ||
105 | mb(); | ||
106 | |||
107 | spin_lock(&boot_lock); | ||
108 | |||
109 | /* | ||
110 | * The secondary processor is waiting to be released from | ||
111 | * the holding pen - release it, then wait for it to flag | ||
112 | * that it has been released by resetting pen_release. | ||
113 | * | ||
114 | * Note that "pen_release" is the hardware CPU ID, whereas | ||
115 | * "cpu" is Linux's internal ID. | ||
116 | */ | ||
117 | pen_release = cpu_logical_map(cpu); | ||
118 | __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release)); | ||
119 | outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1)); | ||
120 | |||
121 | /* | ||
122 | * Send the secondary CPU SEV, thereby causing the boot monitor to read | ||
123 | * the JUMPADDR and WAKEMAGIC, and branch to the address found there. | ||
124 | */ | ||
125 | dsb_sev(); | ||
126 | |||
127 | timeout = jiffies + (1 * HZ); | ||
128 | while (time_before(jiffies, timeout)) { | ||
129 | smp_rmb(); | ||
130 | if (pen_release == -1) | ||
131 | break; | ||
132 | |||
133 | udelay(10); | ||
134 | } | ||
135 | |||
136 | /* | ||
137 | * now the secondary core is starting up let it run its | ||
138 | * calibrations, then wait for it to finish | ||
139 | */ | ||
140 | spin_unlock(&boot_lock); | ||
141 | |||
142 | return pen_release != -1 ? -ENOSYS : 0; | ||
143 | } | ||
144 | |||
145 | static void __init sirfsoc_smp_init_cpus(void) | ||
146 | { | ||
147 | set_smp_cross_call(gic_raise_softirq); | ||
148 | } | ||
149 | |||
150 | static void __init sirfsoc_smp_prepare_cpus(unsigned int max_cpus) | ||
151 | { | ||
152 | scu_enable(scu_base); | ||
153 | } | ||
154 | |||
155 | struct smp_operations sirfsoc_smp_ops __initdata = { | ||
156 | .smp_init_cpus = sirfsoc_smp_init_cpus, | ||
157 | .smp_prepare_cpus = sirfsoc_smp_prepare_cpus, | ||
158 | .smp_secondary_init = sirfsoc_secondary_init, | ||
159 | .smp_boot_secondary = sirfsoc_boot_secondary, | ||
160 | #ifdef CONFIG_HOTPLUG_CPU | ||
161 | .cpu_die = sirfsoc_cpu_die, | ||
162 | #endif | ||
163 | }; | ||
diff --git a/arch/arm/mach-prima2/timer-marco.c b/arch/arm/mach-prima2/timer-marco.c new file mode 100644 index 000000000000..f4eea2e97eb0 --- /dev/null +++ b/arch/arm/mach-prima2/timer-marco.c | |||
@@ -0,0 +1,316 @@ | |||
1 | /* | ||
2 | * System timer for CSR SiRFprimaII | ||
3 | * | ||
4 | * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. | ||
5 | * | ||
6 | * Licensed under GPLv2 or later. | ||
7 | */ | ||
8 | |||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/interrupt.h> | ||
11 | #include <linux/clockchips.h> | ||
12 | #include <linux/clocksource.h> | ||
13 | #include <linux/bitops.h> | ||
14 | #include <linux/irq.h> | ||
15 | #include <linux/clk.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/of.h> | ||
18 | #include <linux/of_irq.h> | ||
19 | #include <linux/of_address.h> | ||
20 | #include <asm/sched_clock.h> | ||
21 | #include <asm/localtimer.h> | ||
22 | #include <asm/mach/time.h> | ||
23 | |||
24 | #include "common.h" | ||
25 | |||
26 | #define SIRFSOC_TIMER_32COUNTER_0_CTRL 0x0000 | ||
27 | #define SIRFSOC_TIMER_32COUNTER_1_CTRL 0x0004 | ||
28 | #define SIRFSOC_TIMER_MATCH_0 0x0018 | ||
29 | #define SIRFSOC_TIMER_MATCH_1 0x001c | ||
30 | #define SIRFSOC_TIMER_COUNTER_0 0x0048 | ||
31 | #define SIRFSOC_TIMER_COUNTER_1 0x004c | ||
32 | #define SIRFSOC_TIMER_INTR_STATUS 0x0060 | ||
33 | #define SIRFSOC_TIMER_WATCHDOG_EN 0x0064 | ||
34 | #define SIRFSOC_TIMER_64COUNTER_CTRL 0x0068 | ||
35 | #define SIRFSOC_TIMER_64COUNTER_LO 0x006c | ||
36 | #define SIRFSOC_TIMER_64COUNTER_HI 0x0070 | ||
37 | #define SIRFSOC_TIMER_64COUNTER_LOAD_LO 0x0074 | ||
38 | #define SIRFSOC_TIMER_64COUNTER_LOAD_HI 0x0078 | ||
39 | #define SIRFSOC_TIMER_64COUNTER_RLATCHED_LO 0x007c | ||
40 | #define SIRFSOC_TIMER_64COUNTER_RLATCHED_HI 0x0080 | ||
41 | |||
42 | #define SIRFSOC_TIMER_REG_CNT 6 | ||
43 | |||
44 | static const u32 sirfsoc_timer_reg_list[SIRFSOC_TIMER_REG_CNT] = { | ||
45 | SIRFSOC_TIMER_WATCHDOG_EN, | ||
46 | SIRFSOC_TIMER_32COUNTER_0_CTRL, | ||
47 | SIRFSOC_TIMER_32COUNTER_1_CTRL, | ||
48 | SIRFSOC_TIMER_64COUNTER_CTRL, | ||
49 | SIRFSOC_TIMER_64COUNTER_RLATCHED_LO, | ||
50 | SIRFSOC_TIMER_64COUNTER_RLATCHED_HI, | ||
51 | }; | ||
52 | |||
53 | static u32 sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT]; | ||
54 | |||
55 | static void __iomem *sirfsoc_timer_base; | ||
56 | static void __init sirfsoc_of_timer_map(void); | ||
57 | |||
58 | /* disable count and interrupt */ | ||
59 | static inline void sirfsoc_timer_count_disable(int idx) | ||
60 | { | ||
61 | writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx) & ~0x7, | ||
62 | sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx); | ||
63 | } | ||
64 | |||
65 | /* enable count and interrupt */ | ||
66 | static inline void sirfsoc_timer_count_enable(int idx) | ||
67 | { | ||
68 | writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx) | 0x7, | ||
69 | sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx); | ||
70 | } | ||
71 | |||
72 | /* timer interrupt handler */ | ||
73 | static irqreturn_t sirfsoc_timer_interrupt(int irq, void *dev_id) | ||
74 | { | ||
75 | struct clock_event_device *ce = dev_id; | ||
76 | int cpu = smp_processor_id(); | ||
77 | |||
78 | /* clear timer interrupt */ | ||
79 | writel_relaxed(BIT(cpu), sirfsoc_timer_base + SIRFSOC_TIMER_INTR_STATUS); | ||
80 | |||
81 | if (ce->mode == CLOCK_EVT_MODE_ONESHOT) | ||
82 | sirfsoc_timer_count_disable(cpu); | ||
83 | |||
84 | ce->event_handler(ce); | ||
85 | |||
86 | return IRQ_HANDLED; | ||
87 | } | ||
88 | |||
89 | /* read 64-bit timer counter */ | ||
90 | static cycle_t sirfsoc_timer_read(struct clocksource *cs) | ||
91 | { | ||
92 | u64 cycles; | ||
93 | |||
94 | writel_relaxed((readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL) | | ||
95 | BIT(0)) & ~BIT(1), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL); | ||
96 | |||
97 | cycles = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_RLATCHED_HI); | ||
98 | cycles = (cycles << 32) | readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_RLATCHED_LO); | ||
99 | |||
100 | return cycles; | ||
101 | } | ||
102 | |||
103 | static int sirfsoc_timer_set_next_event(unsigned long delta, | ||
104 | struct clock_event_device *ce) | ||
105 | { | ||
106 | int cpu = smp_processor_id(); | ||
107 | |||
108 | writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_0 + | ||
109 | 4 * cpu); | ||
110 | writel_relaxed(delta, sirfsoc_timer_base + SIRFSOC_TIMER_MATCH_0 + | ||
111 | 4 * cpu); | ||
112 | |||
113 | /* enable the tick */ | ||
114 | sirfsoc_timer_count_enable(cpu); | ||
115 | |||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | static void sirfsoc_timer_set_mode(enum clock_event_mode mode, | ||
120 | struct clock_event_device *ce) | ||
121 | { | ||
122 | switch (mode) { | ||
123 | case CLOCK_EVT_MODE_ONESHOT: | ||
124 | /* enable in set_next_event */ | ||
125 | break; | ||
126 | default: | ||
127 | break; | ||
128 | } | ||
129 | |||
130 | sirfsoc_timer_count_disable(smp_processor_id()); | ||
131 | } | ||
132 | |||
133 | static void sirfsoc_clocksource_suspend(struct clocksource *cs) | ||
134 | { | ||
135 | int i; | ||
136 | |||
137 | for (i = 0; i < SIRFSOC_TIMER_REG_CNT; i++) | ||
138 | sirfsoc_timer_reg_val[i] = readl_relaxed(sirfsoc_timer_base + sirfsoc_timer_reg_list[i]); | ||
139 | } | ||
140 | |||
141 | static void sirfsoc_clocksource_resume(struct clocksource *cs) | ||
142 | { | ||
143 | int i; | ||
144 | |||
145 | for (i = 0; i < SIRFSOC_TIMER_REG_CNT - 2; i++) | ||
146 | writel_relaxed(sirfsoc_timer_reg_val[i], sirfsoc_timer_base + sirfsoc_timer_reg_list[i]); | ||
147 | |||
148 | writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 2], | ||
149 | sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_LO); | ||
150 | writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 1], | ||
151 | sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_HI); | ||
152 | |||
153 | writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL) | | ||
154 | BIT(1) | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL); | ||
155 | } | ||
156 | |||
157 | static struct clock_event_device sirfsoc_clockevent = { | ||
158 | .name = "sirfsoc_clockevent", | ||
159 | .rating = 200, | ||
160 | .features = CLOCK_EVT_FEAT_ONESHOT, | ||
161 | .set_mode = sirfsoc_timer_set_mode, | ||
162 | .set_next_event = sirfsoc_timer_set_next_event, | ||
163 | }; | ||
164 | |||
165 | static struct clocksource sirfsoc_clocksource = { | ||
166 | .name = "sirfsoc_clocksource", | ||
167 | .rating = 200, | ||
168 | .mask = CLOCKSOURCE_MASK(64), | ||
169 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
170 | .read = sirfsoc_timer_read, | ||
171 | .suspend = sirfsoc_clocksource_suspend, | ||
172 | .resume = sirfsoc_clocksource_resume, | ||
173 | }; | ||
174 | |||
175 | static struct irqaction sirfsoc_timer_irq = { | ||
176 | .name = "sirfsoc_timer0", | ||
177 | .flags = IRQF_TIMER | IRQF_NOBALANCING, | ||
178 | .handler = sirfsoc_timer_interrupt, | ||
179 | .dev_id = &sirfsoc_clockevent, | ||
180 | }; | ||
181 | |||
182 | #ifdef CONFIG_LOCAL_TIMERS | ||
183 | |||
184 | static struct irqaction sirfsoc_timer1_irq = { | ||
185 | .name = "sirfsoc_timer1", | ||
186 | .flags = IRQF_TIMER | IRQF_NOBALANCING, | ||
187 | .handler = sirfsoc_timer_interrupt, | ||
188 | }; | ||
189 | |||
190 | static int __cpuinit sirfsoc_local_timer_setup(struct clock_event_device *ce) | ||
191 | { | ||
192 | /* Use existing clock_event for cpu 0 */ | ||
193 | if (!smp_processor_id()) | ||
194 | return 0; | ||
195 | |||
196 | ce->irq = sirfsoc_timer1_irq.irq; | ||
197 | ce->name = "local_timer"; | ||
198 | ce->features = sirfsoc_clockevent.features; | ||
199 | ce->rating = sirfsoc_clockevent.rating; | ||
200 | ce->set_mode = sirfsoc_timer_set_mode; | ||
201 | ce->set_next_event = sirfsoc_timer_set_next_event; | ||
202 | ce->shift = sirfsoc_clockevent.shift; | ||
203 | ce->mult = sirfsoc_clockevent.mult; | ||
204 | ce->max_delta_ns = sirfsoc_clockevent.max_delta_ns; | ||
205 | ce->min_delta_ns = sirfsoc_clockevent.min_delta_ns; | ||
206 | |||
207 | sirfsoc_timer1_irq.dev_id = ce; | ||
208 | BUG_ON(setup_irq(ce->irq, &sirfsoc_timer1_irq)); | ||
209 | irq_set_affinity(sirfsoc_timer1_irq.irq, cpumask_of(1)); | ||
210 | |||
211 | clockevents_register_device(ce); | ||
212 | return 0; | ||
213 | } | ||
214 | |||
215 | static void sirfsoc_local_timer_stop(struct clock_event_device *ce) | ||
216 | { | ||
217 | sirfsoc_timer_count_disable(1); | ||
218 | |||
219 | remove_irq(sirfsoc_timer1_irq.irq, &sirfsoc_timer1_irq); | ||
220 | } | ||
221 | |||
222 | static struct local_timer_ops sirfsoc_local_timer_ops __cpuinitdata = { | ||
223 | .setup = sirfsoc_local_timer_setup, | ||
224 | .stop = sirfsoc_local_timer_stop, | ||
225 | }; | ||
226 | #endif /* CONFIG_LOCAL_TIMERS */ | ||
227 | |||
228 | static void __init sirfsoc_clockevent_init(void) | ||
229 | { | ||
230 | clockevents_calc_mult_shift(&sirfsoc_clockevent, CLOCK_TICK_RATE, 60); | ||
231 | |||
232 | sirfsoc_clockevent.max_delta_ns = | ||
233 | clockevent_delta2ns(-2, &sirfsoc_clockevent); | ||
234 | sirfsoc_clockevent.min_delta_ns = | ||
235 | clockevent_delta2ns(2, &sirfsoc_clockevent); | ||
236 | |||
237 | sirfsoc_clockevent.cpumask = cpumask_of(0); | ||
238 | clockevents_register_device(&sirfsoc_clockevent); | ||
239 | #ifdef CONFIG_LOCAL_TIMERS | ||
240 | local_timer_register(&sirfsoc_local_timer_ops); | ||
241 | #endif | ||
242 | } | ||
243 | |||
244 | /* initialize the kernel jiffy timer source */ | ||
245 | void __init sirfsoc_marco_timer_init(void) | ||
246 | { | ||
247 | unsigned long rate; | ||
248 | u32 timer_div; | ||
249 | struct clk *clk; | ||
250 | |||
251 | /* initialize clocking early, we want to set the OS timer */ | ||
252 | sirfsoc_of_clk_init(); | ||
253 | |||
254 | /* timer's input clock is io clock */ | ||
255 | clk = clk_get_sys("io", NULL); | ||
256 | |||
257 | BUG_ON(IS_ERR(clk)); | ||
258 | rate = clk_get_rate(clk); | ||
259 | |||
260 | BUG_ON(rate < CLOCK_TICK_RATE); | ||
261 | BUG_ON(rate % CLOCK_TICK_RATE); | ||
262 | |||
263 | sirfsoc_of_timer_map(); | ||
264 | |||
265 | /* Initialize the timer dividers */ | ||
266 | timer_div = rate / CLOCK_TICK_RATE - 1; | ||
267 | writel_relaxed(timer_div << 16, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL); | ||
268 | writel_relaxed(timer_div << 16, sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL); | ||
269 | writel_relaxed(timer_div << 16, sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_1_CTRL); | ||
270 | |||
271 | /* Initialize timer counters to 0 */ | ||
272 | writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_LO); | ||
273 | writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_HI); | ||
274 | writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL) | | ||
275 | BIT(1) | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL); | ||
276 | writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_0); | ||
277 | writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_1); | ||
278 | |||
279 | /* Clear all interrupts */ | ||
280 | writel_relaxed(0xFFFF, sirfsoc_timer_base + SIRFSOC_TIMER_INTR_STATUS); | ||
281 | |||
282 | BUG_ON(clocksource_register_hz(&sirfsoc_clocksource, CLOCK_TICK_RATE)); | ||
283 | |||
284 | BUG_ON(setup_irq(sirfsoc_timer_irq.irq, &sirfsoc_timer_irq)); | ||
285 | |||
286 | sirfsoc_clockevent_init(); | ||
287 | } | ||
288 | |||
289 | static struct of_device_id timer_ids[] = { | ||
290 | { .compatible = "sirf,marco-tick" }, | ||
291 | {}, | ||
292 | }; | ||
293 | |||
294 | static void __init sirfsoc_of_timer_map(void) | ||
295 | { | ||
296 | struct device_node *np; | ||
297 | |||
298 | np = of_find_matching_node(NULL, timer_ids); | ||
299 | if (!np) | ||
300 | return; | ||
301 | sirfsoc_timer_base = of_iomap(np, 0); | ||
302 | if (!sirfsoc_timer_base) | ||
303 | panic("unable to map timer cpu registers\n"); | ||
304 | |||
305 | sirfsoc_timer_irq.irq = irq_of_parse_and_map(np, 0); | ||
306 | if (!sirfsoc_timer_irq.irq) | ||
307 | panic("No irq passed for timer0 via DT\n"); | ||
308 | |||
309 | #ifdef CONFIG_LOCAL_TIMERS | ||
310 | sirfsoc_timer1_irq.irq = irq_of_parse_and_map(np, 1); | ||
311 | if (!sirfsoc_timer1_irq.irq) | ||
312 | panic("No irq passed for timer1 via DT\n"); | ||
313 | #endif | ||
314 | |||
315 | of_node_put(np); | ||
316 | } | ||