diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-01-25 13:50:43 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-01-25 13:50:43 -0500 |
commit | 7ae0e06b909e31bf9a396a2326ef09b768d9b4c4 (patch) | |
tree | 6dc477b7d657642d54aaacb214927318b7715833 /arch/arc | |
parent | 9b83d851a2bdd021e2135999e5bce3eb8fef94e6 (diff) | |
parent | e57d339a6264355df8c98948f05a46ff2bc5d504 (diff) |
Merge tag 'arc-v3.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vgupta/arc
Pull ARC changes from Vineet Gupta:
- IPI optimization and cleanups
- Support for bootloader provided external Device Tree blobs
* tag 'arc-v3.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vgupta/arc:
ARC: [cmdline] support External Device Trees from u-boot
ARC: [cmdline] uboot cmdline handling rework
ARC: [SMP] optimize IPI send and receive
ARC: [SMP] simplify IPI code
ARC: [SMP] cpu halt interface doesn't need "self" cpu-id
ARC: [SMP] IPI ACK interface doesn't need "self" cpu-id
ARC: [SMP] cpumask not needed in IPI send path
Diffstat (limited to 'arch/arc')
-rw-r--r-- | arch/arc/Kconfig | 11 | ||||
-rw-r--r-- | arch/arc/include/asm/smp.h | 8 | ||||
-rw-r--r-- | arch/arc/kernel/head.S | 26 | ||||
-rw-r--r-- | arch/arc/kernel/setup.c | 48 | ||||
-rw-r--r-- | arch/arc/kernel/smp.c | 124 | ||||
-rw-r--r-- | arch/arc/plat-arcfpga/smp.c | 12 |
6 files changed, 121 insertions, 108 deletions
diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index 5438cabbc45d..9be30c8cb0c2 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig | |||
@@ -409,17 +409,6 @@ config ARC_DBG_TLB_MISS_COUNT | |||
409 | Counts number of I and D TLB Misses and exports them via Debugfs | 409 | Counts number of I and D TLB Misses and exports them via Debugfs |
410 | The counters can be cleared via Debugfs as well | 410 | The counters can be cleared via Debugfs as well |
411 | 411 | ||
412 | config CMDLINE_UBOOT | ||
413 | bool "Support U-boot kernel command line passing" | ||
414 | default n | ||
415 | help | ||
416 | If you are using U-boot (www.denx.de) and wish to pass the kernel | ||
417 | command line from the U-boot environment to the Linux kernel then | ||
418 | switch this option on. | ||
419 | ARC U-boot will setup the cmdline in RAM/flash and set r2 to point | ||
420 | to it. kernel startup code will append this to DeviceTree | ||
421 | /bootargs provided cmdline args. | ||
422 | |||
423 | config ARC_BUILTIN_DTB_NAME | 412 | config ARC_BUILTIN_DTB_NAME |
424 | string "Built in DTB" | 413 | string "Built in DTB" |
425 | help | 414 | help |
diff --git a/arch/arc/include/asm/smp.h b/arch/arc/include/asm/smp.h index eefc29f08cdb..5d06eee43ea9 100644 --- a/arch/arc/include/asm/smp.h +++ b/arch/arc/include/asm/smp.h | |||
@@ -46,14 +46,14 @@ extern int smp_ipi_irq_setup(int cpu, int irq); | |||
46 | * | 46 | * |
47 | * @info: SoC SMP specific info for /proc/cpuinfo etc | 47 | * @info: SoC SMP specific info for /proc/cpuinfo etc |
48 | * @cpu_kick: For Master to kickstart a cpu (optionally at a PC) | 48 | * @cpu_kick: For Master to kickstart a cpu (optionally at a PC) |
49 | * @ipi_send: To send IPI to a @cpumask | 49 | * @ipi_send: To send IPI to a @cpu |
50 | * @ips_clear: To clear IPI received by @cpu at @irq | 50 | * @ips_clear: To clear IPI received at @irq |
51 | */ | 51 | */ |
52 | struct plat_smp_ops { | 52 | struct plat_smp_ops { |
53 | const char *info; | 53 | const char *info; |
54 | void (*cpu_kick)(int cpu, unsigned long pc); | 54 | void (*cpu_kick)(int cpu, unsigned long pc); |
55 | void (*ipi_send)(void *callmap); | 55 | void (*ipi_send)(int cpu); |
56 | void (*ipi_clear)(int cpu, int irq); | 56 | void (*ipi_clear)(int irq); |
57 | }; | 57 | }; |
58 | 58 | ||
59 | /* TBD: stop exporting it for direct population by platform */ | 59 | /* TBD: stop exporting it for direct population by platform */ |
diff --git a/arch/arc/kernel/head.S b/arch/arc/kernel/head.S index 2c878e964a64..991997269d02 100644 --- a/arch/arc/kernel/head.S +++ b/arch/arc/kernel/head.S | |||
@@ -49,25 +49,13 @@ stext: | |||
49 | st.ab 0, [r5,4] | 49 | st.ab 0, [r5,4] |
50 | brlt r5, r6, 1b | 50 | brlt r5, r6, 1b |
51 | 51 | ||
52 | #ifdef CONFIG_CMDLINE_UBOOT | 52 | ; Uboot - kernel ABI |
53 | ; support for bootloader provided cmdline | 53 | ; r0 = [0] No uboot interaction, [1] cmdline in r2, [2] DTB in r2 |
54 | ; If cmdline passed by u-boot, then | 54 | ; r1 = magic number (board identity, unused as of now |
55 | ; r0 = 1 (because ATAGS parsing, now retired, used to use 0) | 55 | ; r2 = pointer to uboot provided cmdline or external DTB in mem |
56 | ; r1 = magic number (board identity) | 56 | ; These are handled later in setup_arch() |
57 | ; r2 = addr of cmdline string (somewhere in memory/flash) | 57 | st r0, [@uboot_tag] |
58 | 58 | st r2, [@uboot_arg] | |
59 | brne r0, 1, .Lother_bootup_chores ; u-boot didn't pass cmdline | ||
60 | breq r2, 0, .Lother_bootup_chores ; or cmdline is NULL | ||
61 | |||
62 | mov r5, @command_line | ||
63 | 1: | ||
64 | ldb.ab r6, [r2, 1] | ||
65 | breq r6, 0, .Lother_bootup_chores | ||
66 | b.d 1b | ||
67 | stb.ab r6, [r5, 1] | ||
68 | #endif | ||
69 | |||
70 | .Lother_bootup_chores: | ||
71 | 59 | ||
72 | ; Identify if running on ISS vs Silicon | 60 | ; Identify if running on ISS vs Silicon |
73 | ; IDENTITY Reg [ 3 2 1 0 ] | 61 | ; IDENTITY Reg [ 3 2 1 0 ] |
diff --git a/arch/arc/kernel/setup.c b/arch/arc/kernel/setup.c index 643eae4436e0..119dddb752b2 100644 --- a/arch/arc/kernel/setup.c +++ b/arch/arc/kernel/setup.c | |||
@@ -29,7 +29,10 @@ | |||
29 | 29 | ||
30 | int running_on_hw = 1; /* vs. on ISS */ | 30 | int running_on_hw = 1; /* vs. on ISS */ |
31 | 31 | ||
32 | char __initdata command_line[COMMAND_LINE_SIZE]; | 32 | /* Part of U-boot ABI: see head.S */ |
33 | int __initdata uboot_tag; | ||
34 | char __initdata *uboot_arg; | ||
35 | |||
33 | const struct machine_desc *machine_desc; | 36 | const struct machine_desc *machine_desc; |
34 | 37 | ||
35 | struct task_struct *_current_task[NR_CPUS]; /* For stack switching */ | 38 | struct task_struct *_current_task[NR_CPUS]; /* For stack switching */ |
@@ -311,19 +314,40 @@ void setup_processor(void) | |||
311 | arc_chk_fpu(); | 314 | arc_chk_fpu(); |
312 | } | 315 | } |
313 | 316 | ||
317 | static inline int is_kernel(unsigned long addr) | ||
318 | { | ||
319 | if (addr >= (unsigned long)_stext && addr <= (unsigned long)_end) | ||
320 | return 1; | ||
321 | return 0; | ||
322 | } | ||
323 | |||
314 | void __init setup_arch(char **cmdline_p) | 324 | void __init setup_arch(char **cmdline_p) |
315 | { | 325 | { |
316 | /* This also populates @boot_command_line from /bootargs */ | 326 | /* make sure that uboot passed pointer to cmdline/dtb is valid */ |
317 | machine_desc = setup_machine_fdt(__dtb_start); | 327 | if (uboot_tag && is_kernel((unsigned long)uboot_arg)) |
318 | if (!machine_desc) | 328 | panic("Invalid uboot arg\n"); |
319 | panic("Embedded DT invalid\n"); | 329 | |
320 | 330 | /* See if u-boot passed an external Device Tree blob */ | |
321 | /* Append any u-boot provided cmdline */ | 331 | machine_desc = setup_machine_fdt(uboot_arg); /* uboot_tag == 2 */ |
322 | #ifdef CONFIG_CMDLINE_UBOOT | 332 | if (!machine_desc) { |
323 | /* Add a whitespace seperator between the 2 cmdlines */ | 333 | /* No, so try the embedded one */ |
324 | strlcat(boot_command_line, " ", COMMAND_LINE_SIZE); | 334 | machine_desc = setup_machine_fdt(__dtb_start); |
325 | strlcat(boot_command_line, command_line, COMMAND_LINE_SIZE); | 335 | if (!machine_desc) |
326 | #endif | 336 | panic("Embedded DT invalid\n"); |
337 | |||
338 | /* | ||
339 | * If we are here, it is established that @uboot_arg didn't | ||
340 | * point to DT blob. Instead if u-boot says it is cmdline, | ||
341 | * Appent to embedded DT cmdline. | ||
342 | * setup_machine_fdt() would have populated @boot_command_line | ||
343 | */ | ||
344 | if (uboot_tag == 1) { | ||
345 | /* Ensure a whitespace between the 2 cmdlines */ | ||
346 | strlcat(boot_command_line, " ", COMMAND_LINE_SIZE); | ||
347 | strlcat(boot_command_line, uboot_arg, | ||
348 | COMMAND_LINE_SIZE); | ||
349 | } | ||
350 | } | ||
327 | 351 | ||
328 | /* Save unparsed command line copy for /proc/cmdline */ | 352 | /* Save unparsed command line copy for /proc/cmdline */ |
329 | *cmdline_p = boot_command_line; | 353 | *cmdline_p = boot_command_line; |
diff --git a/arch/arc/kernel/smp.c b/arch/arc/kernel/smp.c index c2f9ebbc38f6..40859e5619f9 100644 --- a/arch/arc/kernel/smp.c +++ b/arch/arc/kernel/smp.c | |||
@@ -197,51 +197,65 @@ int __init setup_profiling_timer(unsigned int multiplier) | |||
197 | /* Inter Processor Interrupt Handling */ | 197 | /* Inter Processor Interrupt Handling */ |
198 | /*****************************************************************************/ | 198 | /*****************************************************************************/ |
199 | 199 | ||
200 | /* | ||
201 | * structures for inter-processor calls | ||
202 | * A Collection of single bit ipi messages | ||
203 | * | ||
204 | */ | ||
205 | |||
206 | /* | ||
207 | * TODO_rajesh investigate tlb message types. | ||
208 | * IPI Timer not needed because each ARC has an individual Interrupting Timer | ||
209 | */ | ||
210 | enum ipi_msg_type { | 200 | enum ipi_msg_type { |
211 | IPI_NOP = 0, | 201 | IPI_EMPTY = 0, |
212 | IPI_RESCHEDULE = 1, | 202 | IPI_RESCHEDULE = 1, |
213 | IPI_CALL_FUNC, | 203 | IPI_CALL_FUNC, |
214 | IPI_CPU_STOP | 204 | IPI_CPU_STOP, |
215 | }; | 205 | }; |
216 | 206 | ||
217 | struct ipi_data { | 207 | /* |
218 | unsigned long bits; | 208 | * In arches with IRQ for each msg type (above), receiver can use IRQ-id to |
219 | }; | 209 | * figure out what msg was sent. For those which don't (ARC has dedicated IPI |
210 | * IRQ), the msg-type needs to be conveyed via per-cpu data | ||
211 | */ | ||
220 | 212 | ||
221 | static DEFINE_PER_CPU(struct ipi_data, ipi_data); | 213 | static DEFINE_PER_CPU(unsigned long, ipi_data); |
222 | 214 | ||
223 | static void ipi_send_msg(const struct cpumask *callmap, enum ipi_msg_type msg) | 215 | static void ipi_send_msg_one(int cpu, enum ipi_msg_type msg) |
224 | { | 216 | { |
217 | unsigned long __percpu *ipi_data_ptr = per_cpu_ptr(&ipi_data, cpu); | ||
218 | unsigned long old, new; | ||
225 | unsigned long flags; | 219 | unsigned long flags; |
226 | unsigned int cpu; | 220 | |
221 | pr_debug("%d Sending msg [%d] to %d\n", smp_processor_id(), msg, cpu); | ||
227 | 222 | ||
228 | local_irq_save(flags); | 223 | local_irq_save(flags); |
229 | 224 | ||
230 | for_each_cpu(cpu, callmap) { | 225 | /* |
231 | struct ipi_data *ipi = &per_cpu(ipi_data, cpu); | 226 | * Atomically write new msg bit (in case others are writing too), |
232 | set_bit(msg, &ipi->bits); | 227 | * and read back old value |
233 | } | 228 | */ |
229 | do { | ||
230 | new = old = *ipi_data_ptr; | ||
231 | new |= 1U << msg; | ||
232 | } while (cmpxchg(ipi_data_ptr, old, new) != old); | ||
234 | 233 | ||
235 | /* Call the platform specific cross-CPU call function */ | 234 | /* |
236 | if (plat_smp_ops.ipi_send) | 235 | * Call the platform specific IPI kick function, but avoid if possible: |
237 | plat_smp_ops.ipi_send((void *)callmap); | 236 | * Only do so if there's no pending msg from other concurrent sender(s). |
237 | * Otherwise, recevier will see this msg as well when it takes the | ||
238 | * IPI corresponding to that msg. This is true, even if it is already in | ||
239 | * IPI handler, because !@old means it has not yet dequeued the msg(s) | ||
240 | * so @new msg can be a free-loader | ||
241 | */ | ||
242 | if (plat_smp_ops.ipi_send && !old) | ||
243 | plat_smp_ops.ipi_send(cpu); | ||
238 | 244 | ||
239 | local_irq_restore(flags); | 245 | local_irq_restore(flags); |
240 | } | 246 | } |
241 | 247 | ||
248 | static void ipi_send_msg(const struct cpumask *callmap, enum ipi_msg_type msg) | ||
249 | { | ||
250 | unsigned int cpu; | ||
251 | |||
252 | for_each_cpu(cpu, callmap) | ||
253 | ipi_send_msg_one(cpu, msg); | ||
254 | } | ||
255 | |||
242 | void smp_send_reschedule(int cpu) | 256 | void smp_send_reschedule(int cpu) |
243 | { | 257 | { |
244 | ipi_send_msg(cpumask_of(cpu), IPI_RESCHEDULE); | 258 | ipi_send_msg_one(cpu, IPI_RESCHEDULE); |
245 | } | 259 | } |
246 | 260 | ||
247 | void smp_send_stop(void) | 261 | void smp_send_stop(void) |
@@ -254,7 +268,7 @@ void smp_send_stop(void) | |||
254 | 268 | ||
255 | void arch_send_call_function_single_ipi(int cpu) | 269 | void arch_send_call_function_single_ipi(int cpu) |
256 | { | 270 | { |
257 | ipi_send_msg(cpumask_of(cpu), IPI_CALL_FUNC); | 271 | ipi_send_msg_one(cpu, IPI_CALL_FUNC); |
258 | } | 272 | } |
259 | 273 | ||
260 | void arch_send_call_function_ipi_mask(const struct cpumask *mask) | 274 | void arch_send_call_function_ipi_mask(const struct cpumask *mask) |
@@ -265,33 +279,29 @@ void arch_send_call_function_ipi_mask(const struct cpumask *mask) | |||
265 | /* | 279 | /* |
266 | * ipi_cpu_stop - handle IPI from smp_send_stop() | 280 | * ipi_cpu_stop - handle IPI from smp_send_stop() |
267 | */ | 281 | */ |
268 | static void ipi_cpu_stop(unsigned int cpu) | 282 | static void ipi_cpu_stop(void) |
269 | { | 283 | { |
270 | machine_halt(); | 284 | machine_halt(); |
271 | } | 285 | } |
272 | 286 | ||
273 | static inline void __do_IPI(unsigned long *ops, struct ipi_data *ipi, int cpu) | 287 | static inline void __do_IPI(unsigned long msg) |
274 | { | 288 | { |
275 | unsigned long msg = 0; | 289 | switch (msg) { |
290 | case IPI_RESCHEDULE: | ||
291 | scheduler_ipi(); | ||
292 | break; | ||
276 | 293 | ||
277 | do { | 294 | case IPI_CALL_FUNC: |
278 | msg = find_next_bit(ops, BITS_PER_LONG, msg+1); | 295 | generic_smp_call_function_interrupt(); |
296 | break; | ||
279 | 297 | ||
280 | switch (msg) { | 298 | case IPI_CPU_STOP: |
281 | case IPI_RESCHEDULE: | 299 | ipi_cpu_stop(); |
282 | scheduler_ipi(); | 300 | break; |
283 | break; | ||
284 | |||
285 | case IPI_CALL_FUNC: | ||
286 | generic_smp_call_function_interrupt(); | ||
287 | break; | ||
288 | |||
289 | case IPI_CPU_STOP: | ||
290 | ipi_cpu_stop(cpu); | ||
291 | break; | ||
292 | } | ||
293 | } while (msg < BITS_PER_LONG); | ||
294 | 301 | ||
302 | default: | ||
303 | pr_warn("IPI with unexpected msg %ld\n", msg); | ||
304 | } | ||
295 | } | 305 | } |
296 | 306 | ||
297 | /* | 307 | /* |
@@ -300,19 +310,25 @@ static inline void __do_IPI(unsigned long *ops, struct ipi_data *ipi, int cpu) | |||
300 | */ | 310 | */ |
301 | irqreturn_t do_IPI(int irq, void *dev_id) | 311 | irqreturn_t do_IPI(int irq, void *dev_id) |
302 | { | 312 | { |
303 | int cpu = smp_processor_id(); | 313 | unsigned long pending; |
304 | struct ipi_data *ipi = &per_cpu(ipi_data, cpu); | 314 | |
305 | unsigned long ops; | 315 | pr_debug("IPI [%ld] received on cpu %d\n", |
316 | *this_cpu_ptr(&ipi_data), smp_processor_id()); | ||
306 | 317 | ||
307 | if (plat_smp_ops.ipi_clear) | 318 | if (plat_smp_ops.ipi_clear) |
308 | plat_smp_ops.ipi_clear(cpu, irq); | 319 | plat_smp_ops.ipi_clear(irq); |
309 | 320 | ||
310 | /* | 321 | /* |
311 | * XXX: is this loop really needed | 322 | * "dequeue" the msg corresponding to this IPI (and possibly other |
312 | * And do we need to move ipi_clean inside | 323 | * piggybacked msg from elided IPIs: see ipi_send_msg_one() above) |
313 | */ | 324 | */ |
314 | while ((ops = xchg(&ipi->bits, 0)) != 0) | 325 | pending = xchg(this_cpu_ptr(&ipi_data), 0); |
315 | __do_IPI(&ops, ipi, cpu); | 326 | |
327 | do { | ||
328 | unsigned long msg = __ffs(pending); | ||
329 | __do_IPI(msg); | ||
330 | pending &= ~(1U << msg); | ||
331 | } while (pending); | ||
316 | 332 | ||
317 | return IRQ_HANDLED; | 333 | return IRQ_HANDLED; |
318 | } | 334 | } |
diff --git a/arch/arc/plat-arcfpga/smp.c b/arch/arc/plat-arcfpga/smp.c index 91b55349a5f8..8a12741f5f7a 100644 --- a/arch/arc/plat-arcfpga/smp.c +++ b/arch/arc/plat-arcfpga/smp.c | |||
@@ -88,18 +88,14 @@ void iss_model_init_smp(unsigned int cpu) | |||
88 | smp_ipi_irq_setup(cpu, IDU_INTERRUPT_0 + cpu); | 88 | smp_ipi_irq_setup(cpu, IDU_INTERRUPT_0 + cpu); |
89 | } | 89 | } |
90 | 90 | ||
91 | static void iss_model_ipi_send(void *arg) | 91 | static void iss_model_ipi_send(int cpu) |
92 | { | 92 | { |
93 | struct cpumask *callmap = arg; | 93 | idu_irq_assert(cpu); |
94 | unsigned int cpu; | ||
95 | |||
96 | for_each_cpu(cpu, callmap) | ||
97 | idu_irq_assert(cpu); | ||
98 | } | 94 | } |
99 | 95 | ||
100 | static void iss_model_ipi_clear(int cpu, int irq) | 96 | static void iss_model_ipi_clear(int irq) |
101 | { | 97 | { |
102 | idu_irq_clear(IDU_INTERRUPT_0 + cpu); | 98 | idu_irq_clear(IDU_INTERRUPT_0 + smp_processor_id()); |
103 | } | 99 | } |
104 | 100 | ||
105 | void iss_model_init_early_smp(void) | 101 | void iss_model_init_early_smp(void) |