diff options
author | Paul Mundt <lethal@linux-sh.org> | 2007-09-21 05:32:32 -0400 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2007-09-21 05:32:32 -0400 |
commit | aba1030a7e529ec9fe47a8cfc06d12a39180fa71 (patch) | |
tree | a28de7f7b89610246fddf488f042b58c66101b95 /arch/sh/kernel | |
parent | f18d533e3cd476aedf41fe1e6e9dc3e0a2446bba (diff) |
sh: Bring SMP support back from the dead.
There was a very preliminary bunch of SMP code scattered around for the
SH7604 microcontrollers from way back when, and it has mostly suffered
bitrot since then. With the tree already having been slowly getting
prepped for SMP, this plugs in most of the remaining platform-independent
bits.
Signed-off-by: Magnus Damm <damm@igel.co.jp>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'arch/sh/kernel')
-rw-r--r-- | arch/sh/kernel/cpu/init.c | 16 | ||||
-rw-r--r-- | arch/sh/kernel/head.S | 18 | ||||
-rw-r--r-- | arch/sh/kernel/smp.c | 167 | ||||
-rw-r--r-- | arch/sh/kernel/traps.c | 5 |
4 files changed, 118 insertions, 88 deletions
diff --git a/arch/sh/kernel/cpu/init.c b/arch/sh/kernel/cpu/init.c index fdc245b7b043..c217c4bf0085 100644 --- a/arch/sh/kernel/cpu/init.c +++ b/arch/sh/kernel/cpu/init.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <asm/cache.h> | 22 | #include <asm/cache.h> |
23 | #include <asm/io.h> | 23 | #include <asm/io.h> |
24 | #include <asm/ubc.h> | 24 | #include <asm/ubc.h> |
25 | #include <asm/smp.h> | ||
25 | 26 | ||
26 | /* | 27 | /* |
27 | * Generic wrapper for command line arguments to disable on-chip | 28 | * Generic wrapper for command line arguments to disable on-chip |
@@ -216,8 +217,11 @@ static void __init dsp_init(void) | |||
216 | * Each processor family is still responsible for doing its own probing | 217 | * Each processor family is still responsible for doing its own probing |
217 | * and cache configuration in detect_cpu_and_cache_system(). | 218 | * and cache configuration in detect_cpu_and_cache_system(). |
218 | */ | 219 | */ |
219 | asmlinkage void __init sh_cpu_init(void) | 220 | |
221 | asmlinkage void __cpuinit sh_cpu_init(void) | ||
220 | { | 222 | { |
223 | current_thread_info()->cpu = hard_smp_processor_id(); | ||
224 | |||
221 | /* First, probe the CPU */ | 225 | /* First, probe the CPU */ |
222 | detect_cpu_and_cache_system(); | 226 | detect_cpu_and_cache_system(); |
223 | 227 | ||
@@ -227,9 +231,10 @@ asmlinkage void __init sh_cpu_init(void) | |||
227 | /* Init the cache */ | 231 | /* Init the cache */ |
228 | cache_init(); | 232 | cache_init(); |
229 | 233 | ||
230 | shm_align_mask = max_t(unsigned long, | 234 | if (raw_smp_processor_id() == 0) |
231 | current_cpu_data.dcache.way_size - 1, | 235 | shm_align_mask = max_t(unsigned long, |
232 | PAGE_SIZE - 1); | 236 | current_cpu_data.dcache.way_size - 1, |
237 | PAGE_SIZE - 1); | ||
233 | 238 | ||
234 | /* Disable the FPU */ | 239 | /* Disable the FPU */ |
235 | if (fpu_disabled) { | 240 | if (fpu_disabled) { |
@@ -268,6 +273,7 @@ asmlinkage void __init sh_cpu_init(void) | |||
268 | * like PTRACE_SINGLESTEP or doing hardware watchpoints in GDB. So .. | 273 | * like PTRACE_SINGLESTEP or doing hardware watchpoints in GDB. So .. |
269 | * we wake it up and hope that all is well. | 274 | * we wake it up and hope that all is well. |
270 | */ | 275 | */ |
271 | ubc_wakeup(); | 276 | if (raw_smp_processor_id() == 0) |
277 | ubc_wakeup(); | ||
272 | speculative_execution_init(); | 278 | speculative_execution_init(); |
273 | } | 279 | } |
diff --git a/arch/sh/kernel/head.S b/arch/sh/kernel/head.S index 0bccc0ca5a0f..3338239717f1 100644 --- a/arch/sh/kernel/head.S +++ b/arch/sh/kernel/head.S | |||
@@ -54,8 +54,8 @@ ENTRY(_stext) | |||
54 | mov.l 1f, r0 ! MD=1, RB=0, BL=0, IMASK=0xF | 54 | mov.l 1f, r0 ! MD=1, RB=0, BL=0, IMASK=0xF |
55 | ldc r0, sr | 55 | ldc r0, sr |
56 | ! Initialize global interrupt mask | 56 | ! Initialize global interrupt mask |
57 | mov #0, r0 | ||
58 | #ifdef CONFIG_CPU_HAS_SR_RB | 57 | #ifdef CONFIG_CPU_HAS_SR_RB |
58 | mov #0, r0 | ||
59 | ldc r0, r6_bank | 59 | ldc r0, r6_bank |
60 | #endif | 60 | #endif |
61 | 61 | ||
@@ -72,15 +72,18 @@ ENTRY(_stext) | |||
72 | ! | 72 | ! |
73 | mov.l 2f, r0 | 73 | mov.l 2f, r0 |
74 | mov r0, r15 ! Set initial r15 (stack pointer) | 74 | mov r0, r15 ! Set initial r15 (stack pointer) |
75 | mov #(THREAD_SIZE >> 10), r1 | ||
76 | shll8 r1 ! r1 = THREAD_SIZE | ||
77 | shll2 r1 | ||
78 | sub r1, r0 ! | ||
79 | #ifdef CONFIG_CPU_HAS_SR_RB | 75 | #ifdef CONFIG_CPU_HAS_SR_RB |
76 | mov.l 7f, r0 | ||
80 | ldc r0, r7_bank ! ... and initial thread_info | 77 | ldc r0, r7_bank ! ... and initial thread_info |
81 | #endif | 78 | #endif |
82 | 79 | ||
83 | ! Clear BSS area | 80 | ! Clear BSS area |
81 | #ifdef CONFIG_SMP | ||
82 | mov.l 3f, r0 | ||
83 | cmp/eq #0, r0 ! skip clear if set to zero | ||
84 | bt 10f | ||
85 | #endif | ||
86 | |||
84 | mov.l 3f, r1 | 87 | mov.l 3f, r1 |
85 | add #4, r1 | 88 | add #4, r1 |
86 | mov.l 4f, r2 | 89 | mov.l 4f, r2 |
@@ -89,13 +92,14 @@ ENTRY(_stext) | |||
89 | bf/s 9b ! while (r1 < r2) | 92 | bf/s 9b ! while (r1 < r2) |
90 | mov.l r0,@-r2 | 93 | mov.l r0,@-r2 |
91 | 94 | ||
95 | 10: | ||
92 | ! Additional CPU initialization | 96 | ! Additional CPU initialization |
93 | mov.l 6f, r0 | 97 | mov.l 6f, r0 |
94 | jsr @r0 | 98 | jsr @r0 |
95 | nop | 99 | nop |
96 | 100 | ||
97 | SYNCO() ! Wait for pending instructions.. | 101 | SYNCO() ! Wait for pending instructions.. |
98 | 102 | ||
99 | ! Start kernel | 103 | ! Start kernel |
100 | mov.l 5f, r0 | 104 | mov.l 5f, r0 |
101 | jmp @r0 | 105 | jmp @r0 |
@@ -107,8 +111,10 @@ ENTRY(_stext) | |||
107 | #else | 111 | #else |
108 | 1: .long 0x400080F0 ! MD=1, RB=0, BL=0, FD=1, IMASK=0xF | 112 | 1: .long 0x400080F0 ! MD=1, RB=0, BL=0, FD=1, IMASK=0xF |
109 | #endif | 113 | #endif |
114 | ENTRY(stack_start) | ||
110 | 2: .long init_thread_union+THREAD_SIZE | 115 | 2: .long init_thread_union+THREAD_SIZE |
111 | 3: .long __bss_start | 116 | 3: .long __bss_start |
112 | 4: .long _end | 117 | 4: .long _end |
113 | 5: .long start_kernel | 118 | 5: .long start_kernel |
114 | 6: .long sh_cpu_init | 119 | 6: .long sh_cpu_init |
120 | 7: .long init_thread_union | ||
diff --git a/arch/sh/kernel/smp.c b/arch/sh/kernel/smp.c index f93d5ffa9417..94075e1a1e61 100644 --- a/arch/sh/kernel/smp.c +++ b/arch/sh/kernel/smp.c | |||
@@ -3,68 +3,40 @@ | |||
3 | * | 3 | * |
4 | * SMP support for the SuperH processors. | 4 | * SMP support for the SuperH processors. |
5 | * | 5 | * |
6 | * Copyright (C) 2002, 2003 Paul Mundt | 6 | * Copyright (C) 2002 - 2007 Paul Mundt |
7 | * Copyright (C) 2006 - 2007 Akio Idehara | ||
7 | * | 8 | * |
8 | * This program is free software; you can redistribute it and/or modify it | 9 | * This file is subject to the terms and conditions of the GNU General Public |
9 | * under the terms of the GNU General Public License as published by the | 10 | * License. See the file "COPYING" in the main directory of this archive |
10 | * Free Software Foundation; either version 2 of the License, or (at your | 11 | * for more details. |
11 | * option) any later version. | ||
12 | */ | 12 | */ |
13 | |||
14 | #include <linux/err.h> | 13 | #include <linux/err.h> |
15 | #include <linux/cache.h> | 14 | #include <linux/cache.h> |
16 | #include <linux/cpumask.h> | 15 | #include <linux/cpumask.h> |
17 | #include <linux/delay.h> | 16 | #include <linux/delay.h> |
18 | #include <linux/init.h> | 17 | #include <linux/init.h> |
19 | #include <linux/interrupt.h> | ||
20 | #include <linux/spinlock.h> | 18 | #include <linux/spinlock.h> |
21 | #include <linux/threads.h> | 19 | #include <linux/mm.h> |
22 | #include <linux/module.h> | 20 | #include <linux/module.h> |
23 | #include <linux/time.h> | 21 | #include <linux/interrupt.h> |
24 | #include <linux/timex.h> | ||
25 | #include <linux/sched.h> | ||
26 | #include <linux/module.h> | ||
27 | |||
28 | #include <asm/atomic.h> | 22 | #include <asm/atomic.h> |
29 | #include <asm/processor.h> | 23 | #include <asm/processor.h> |
30 | #include <asm/system.h> | 24 | #include <asm/system.h> |
31 | #include <asm/mmu_context.h> | 25 | #include <asm/mmu_context.h> |
32 | #include <asm/smp.h> | 26 | #include <asm/smp.h> |
27 | #include <asm/cacheflush.h> | ||
28 | #include <asm/sections.h> | ||
33 | 29 | ||
34 | /* | 30 | int __cpu_number_map[NR_CPUS]; /* Map physical to logical */ |
35 | * This was written with the Sega Saturn (SMP SH-2 7604) in mind, | 31 | int __cpu_logical_map[NR_CPUS]; /* Map logical to physical */ |
36 | * but is designed to be usable regardless if there's an MMU | ||
37 | * present or not. | ||
38 | */ | ||
39 | struct sh_cpuinfo cpu_data[NR_CPUS]; | ||
40 | |||
41 | extern void per_cpu_trap_init(void); | ||
42 | 32 | ||
43 | cpumask_t cpu_possible_map; | 33 | cpumask_t cpu_possible_map; |
44 | EXPORT_SYMBOL(cpu_possible_map); | 34 | EXPORT_SYMBOL(cpu_possible_map); |
45 | 35 | ||
46 | cpumask_t cpu_online_map; | 36 | cpumask_t cpu_online_map; |
47 | EXPORT_SYMBOL(cpu_online_map); | 37 | EXPORT_SYMBOL(cpu_online_map); |
48 | static atomic_t cpus_booted = ATOMIC_INIT(0); | ||
49 | |||
50 | /* These are defined by the board-specific code. */ | ||
51 | 38 | ||
52 | /* | 39 | static atomic_t cpus_booted = ATOMIC_INIT(0); |
53 | * Cause the function described by call_data to be executed on the passed | ||
54 | * cpu. When the function has finished, increment the finished field of | ||
55 | * call_data. | ||
56 | */ | ||
57 | void __smp_send_ipi(unsigned int cpu, unsigned int action); | ||
58 | |||
59 | /* | ||
60 | * Find the number of available processors | ||
61 | */ | ||
62 | unsigned int __smp_probe_cpus(void); | ||
63 | |||
64 | /* | ||
65 | * Start a particular processor | ||
66 | */ | ||
67 | void __smp_slave_init(unsigned int cpu); | ||
68 | 40 | ||
69 | /* | 41 | /* |
70 | * Run specified function on a particular processor. | 42 | * Run specified function on a particular processor. |
@@ -73,74 +45,123 @@ void __smp_call_function(unsigned int cpu); | |||
73 | 45 | ||
74 | static inline void __init smp_store_cpu_info(unsigned int cpu) | 46 | static inline void __init smp_store_cpu_info(unsigned int cpu) |
75 | { | 47 | { |
76 | cpu_data[cpu].loops_per_jiffy = loops_per_jiffy; | 48 | struct sh_cpuinfo *c = cpu_data + cpu; |
49 | |||
50 | c->loops_per_jiffy = loops_per_jiffy; | ||
77 | } | 51 | } |
78 | 52 | ||
79 | void __init smp_prepare_cpus(unsigned int max_cpus) | 53 | void __init smp_prepare_cpus(unsigned int max_cpus) |
80 | { | 54 | { |
81 | unsigned int cpu = smp_processor_id(); | 55 | unsigned int cpu = smp_processor_id(); |
82 | int i; | ||
83 | 56 | ||
84 | atomic_set(&cpus_booted, 1); | 57 | init_new_context(current, &init_mm); |
85 | smp_store_cpu_info(cpu); | 58 | current_thread_info()->cpu = cpu; |
86 | 59 | plat_prepare_cpus(max_cpus); | |
87 | for (i = 0; i < __smp_probe_cpus(); i++) | 60 | |
88 | cpu_set(i, cpu_possible_map); | 61 | #ifndef CONFIG_HOTPLUG_CPU |
62 | cpu_present_map = cpu_possible_map; | ||
63 | #endif | ||
89 | } | 64 | } |
90 | 65 | ||
91 | void __devinit smp_prepare_boot_cpu(void) | 66 | void __devinit smp_prepare_boot_cpu(void) |
92 | { | 67 | { |
93 | unsigned int cpu = smp_processor_id(); | 68 | unsigned int cpu = smp_processor_id(); |
94 | 69 | ||
70 | __cpu_number_map[0] = cpu; | ||
71 | __cpu_logical_map[0] = cpu; | ||
72 | |||
95 | cpu_set(cpu, cpu_online_map); | 73 | cpu_set(cpu, cpu_online_map); |
96 | cpu_set(cpu, cpu_possible_map); | 74 | cpu_set(cpu, cpu_possible_map); |
97 | } | 75 | } |
98 | 76 | ||
99 | int __cpu_up(unsigned int cpu) | 77 | asmlinkage void __cpuinit start_secondary(void) |
100 | { | 78 | { |
101 | struct task_struct *tsk; | 79 | unsigned int cpu; |
80 | struct mm_struct *mm = &init_mm; | ||
102 | 81 | ||
103 | tsk = fork_idle(cpu); | 82 | atomic_inc(&mm->mm_count); |
83 | atomic_inc(&mm->mm_users); | ||
84 | current->active_mm = mm; | ||
85 | BUG_ON(current->mm); | ||
86 | enter_lazy_tlb(mm, current); | ||
87 | |||
88 | per_cpu_trap_init(); | ||
104 | 89 | ||
105 | if (IS_ERR(tsk)) | 90 | preempt_disable(); |
106 | panic("Failed forking idle task for cpu %d\n", cpu); | 91 | |
107 | 92 | local_irq_enable(); | |
108 | task_thread_info(tsk)->cpu = cpu; | 93 | |
94 | calibrate_delay(); | ||
95 | |||
96 | cpu = smp_processor_id(); | ||
97 | smp_store_cpu_info(cpu); | ||
109 | 98 | ||
110 | cpu_set(cpu, cpu_online_map); | 99 | cpu_set(cpu, cpu_online_map); |
111 | 100 | ||
112 | return 0; | 101 | cpu_idle(); |
113 | } | 102 | } |
114 | 103 | ||
115 | int start_secondary(void *unused) | 104 | extern struct { |
105 | unsigned long sp; | ||
106 | unsigned long bss_start; | ||
107 | unsigned long bss_end; | ||
108 | void *start_kernel_fn; | ||
109 | void *cpu_init_fn; | ||
110 | void *thread_info; | ||
111 | } stack_start; | ||
112 | |||
113 | int __cpuinit __cpu_up(unsigned int cpu) | ||
116 | { | 114 | { |
117 | unsigned int cpu; | 115 | struct task_struct *tsk; |
116 | unsigned long timeout; | ||
118 | 117 | ||
119 | cpu = smp_processor_id(); | 118 | tsk = fork_idle(cpu); |
119 | if (IS_ERR(tsk)) { | ||
120 | printk(KERN_ERR "Failed forking idle task for cpu %d\n", cpu); | ||
121 | return PTR_ERR(tsk); | ||
122 | } | ||
120 | 123 | ||
121 | atomic_inc(&init_mm.mm_count); | 124 | /* Fill in data in head.S for secondary cpus */ |
122 | current->active_mm = &init_mm; | 125 | stack_start.sp = tsk->thread.sp; |
126 | stack_start.thread_info = tsk->stack; | ||
127 | stack_start.bss_start = 0; /* don't clear bss for secondary cpus */ | ||
128 | stack_start.start_kernel_fn = start_secondary; | ||
123 | 129 | ||
124 | smp_store_cpu_info(cpu); | 130 | flush_cache_all(); |
125 | 131 | ||
126 | __smp_slave_init(cpu); | 132 | plat_start_cpu(cpu, (unsigned long)_stext); |
127 | preempt_disable(); | ||
128 | per_cpu_trap_init(); | ||
129 | |||
130 | atomic_inc(&cpus_booted); | ||
131 | 133 | ||
132 | cpu_idle(); | 134 | timeout = jiffies + HZ; |
133 | return 0; | 135 | while (time_before(jiffies, timeout)) { |
136 | if (cpu_online(cpu)) | ||
137 | break; | ||
138 | |||
139 | udelay(10); | ||
140 | } | ||
141 | |||
142 | if (cpu_online(cpu)) | ||
143 | return 0; | ||
144 | |||
145 | return -ENOENT; | ||
134 | } | 146 | } |
135 | 147 | ||
136 | void __init smp_cpus_done(unsigned int max_cpus) | 148 | void __init smp_cpus_done(unsigned int max_cpus) |
137 | { | 149 | { |
138 | smp_mb(); | 150 | unsigned long bogosum = 0; |
151 | int cpu; | ||
152 | |||
153 | for_each_online_cpu(cpu) | ||
154 | bogosum += cpu_data[cpu].loops_per_jiffy; | ||
155 | |||
156 | printk(KERN_INFO "SMP: Total of %d processors activated " | ||
157 | "(%lu.%02lu BogoMIPS).\n", num_online_cpus(), | ||
158 | bogosum / (500000/HZ), | ||
159 | (bogosum / (5000/HZ)) % 100); | ||
139 | } | 160 | } |
140 | 161 | ||
141 | void smp_send_reschedule(int cpu) | 162 | void smp_send_reschedule(int cpu) |
142 | { | 163 | { |
143 | __smp_send_ipi(cpu, SMP_MSG_RESCHEDULE); | 164 | plat_send_ipi(cpu, SMP_MSG_RESCHEDULE); |
144 | } | 165 | } |
145 | 166 | ||
146 | static void stop_this_cpu(void *unused) | 167 | static void stop_this_cpu(void *unused) |
@@ -157,7 +178,6 @@ void smp_send_stop(void) | |||
157 | smp_call_function(stop_this_cpu, 0, 1, 0); | 178 | smp_call_function(stop_this_cpu, 0, 1, 0); |
158 | } | 179 | } |
159 | 180 | ||
160 | |||
161 | struct smp_fn_call_struct smp_fn_call = { | 181 | struct smp_fn_call_struct smp_fn_call = { |
162 | .lock = SPIN_LOCK_UNLOCKED, | 182 | .lock = SPIN_LOCK_UNLOCKED, |
163 | .finished = ATOMIC_INIT(0), | 183 | .finished = ATOMIC_INIT(0), |
@@ -175,9 +195,6 @@ int smp_call_function(void (*func)(void *info), void *info, int retry, int wait) | |||
175 | unsigned int nr_cpus = atomic_read(&cpus_booted); | 195 | unsigned int nr_cpus = atomic_read(&cpus_booted); |
176 | int i; | 196 | int i; |
177 | 197 | ||
178 | if (nr_cpus < 2) | ||
179 | return 0; | ||
180 | |||
181 | /* Can deadlock when called with interrupts disabled */ | 198 | /* Can deadlock when called with interrupts disabled */ |
182 | WARN_ON(irqs_disabled()); | 199 | WARN_ON(irqs_disabled()); |
183 | 200 | ||
@@ -189,7 +206,7 @@ int smp_call_function(void (*func)(void *info), void *info, int retry, int wait) | |||
189 | 206 | ||
190 | for (i = 0; i < nr_cpus; i++) | 207 | for (i = 0; i < nr_cpus; i++) |
191 | if (i != smp_processor_id()) | 208 | if (i != smp_processor_id()) |
192 | __smp_call_function(i); | 209 | plat_send_ipi(i, SMP_MSG_FUNCTION); |
193 | 210 | ||
194 | if (wait) | 211 | if (wait) |
195 | while (atomic_read(&smp_fn_call.finished) != (nr_cpus - 1)); | 212 | while (atomic_read(&smp_fn_call.finished) != (nr_cpus - 1)); |
diff --git a/arch/sh/kernel/traps.c b/arch/sh/kernel/traps.c index 67015044d74a..dcb46e71da1c 100644 --- a/arch/sh/kernel/traps.c +++ b/arch/sh/kernel/traps.c | |||
@@ -807,12 +807,13 @@ static inline void __init gdb_vbr_init(void) | |||
807 | } | 807 | } |
808 | #endif | 808 | #endif |
809 | 809 | ||
810 | void __init per_cpu_trap_init(void) | 810 | void __cpuinit per_cpu_trap_init(void) |
811 | { | 811 | { |
812 | extern void *vbr_base; | 812 | extern void *vbr_base; |
813 | 813 | ||
814 | #ifdef CONFIG_SH_STANDARD_BIOS | 814 | #ifdef CONFIG_SH_STANDARD_BIOS |
815 | gdb_vbr_init(); | 815 | if (raw_smp_processor_id() == 0) |
816 | gdb_vbr_init(); | ||
816 | #endif | 817 | #endif |
817 | 818 | ||
818 | /* NOTE: The VBR value should be at P1 | 819 | /* NOTE: The VBR value should be at P1 |