aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arc
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-01-25 13:50:43 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2014-01-25 13:50:43 -0500
commit7ae0e06b909e31bf9a396a2326ef09b768d9b4c4 (patch)
tree6dc477b7d657642d54aaacb214927318b7715833 /arch/arc
parent9b83d851a2bdd021e2135999e5bce3eb8fef94e6 (diff)
parente57d339a6264355df8c98948f05a46ff2bc5d504 (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/Kconfig11
-rw-r--r--arch/arc/include/asm/smp.h8
-rw-r--r--arch/arc/kernel/head.S26
-rw-r--r--arch/arc/kernel/setup.c48
-rw-r--r--arch/arc/kernel/smp.c124
-rw-r--r--arch/arc/plat-arcfpga/smp.c12
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
412config 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
423config ARC_BUILTIN_DTB_NAME 412config 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 */
52struct plat_smp_ops { 52struct 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
631:
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
30int running_on_hw = 1; /* vs. on ISS */ 30int running_on_hw = 1; /* vs. on ISS */
31 31
32char __initdata command_line[COMMAND_LINE_SIZE]; 32/* Part of U-boot ABI: see head.S */
33int __initdata uboot_tag;
34char __initdata *uboot_arg;
35
33const struct machine_desc *machine_desc; 36const struct machine_desc *machine_desc;
34 37
35struct task_struct *_current_task[NR_CPUS]; /* For stack switching */ 38struct 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
317static 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
314void __init setup_arch(char **cmdline_p) 324void __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 */
210enum ipi_msg_type { 200enum 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
217struct 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
221static DEFINE_PER_CPU(struct ipi_data, ipi_data); 213static DEFINE_PER_CPU(unsigned long, ipi_data);
222 214
223static void ipi_send_msg(const struct cpumask *callmap, enum ipi_msg_type msg) 215static 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
248static 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
242void smp_send_reschedule(int cpu) 256void 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
247void smp_send_stop(void) 261void smp_send_stop(void)
@@ -254,7 +268,7 @@ void smp_send_stop(void)
254 268
255void arch_send_call_function_single_ipi(int cpu) 269void 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
260void arch_send_call_function_ipi_mask(const struct cpumask *mask) 274void 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 */
268static void ipi_cpu_stop(unsigned int cpu) 282static void ipi_cpu_stop(void)
269{ 283{
270 machine_halt(); 284 machine_halt();
271} 285}
272 286
273static inline void __do_IPI(unsigned long *ops, struct ipi_data *ipi, int cpu) 287static 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 */
301irqreturn_t do_IPI(int irq, void *dev_id) 311irqreturn_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
91static void iss_model_ipi_send(void *arg) 91static 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
100static void iss_model_ipi_clear(int cpu, int irq) 96static 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
105void iss_model_init_early_smp(void) 101void iss_model_init_early_smp(void)