summaryrefslogtreecommitdiff
path: root/arch/arc
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arc')
-rw-r--r--arch/arc/Kconfig15
-rw-r--r--arch/arc/include/asm/Kbuild1
-rw-r--r--arch/arc/include/asm/linkage.h2
-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
8 files changed, 126 insertions, 110 deletions
diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig
index 9063ae6553cc..9be30c8cb0c2 100644
--- a/arch/arc/Kconfig
+++ b/arch/arc/Kconfig
@@ -128,8 +128,8 @@ config SMP
default n
help
This enables support for systems with more than one CPU. If you have
- a system with only one CPU, like most personal computers, say N. If
- you have a system with more than one CPU, say Y.
+ a system with only one CPU, say N. If you have a system with more
+ than one CPU, say Y.
if SMP
@@ -409,17 +409,6 @@ config ARC_DBG_TLB_MISS_COUNT
Counts number of I and D TLB Misses and exports them via Debugfs
The counters can be cleared via Debugfs as well
-config CMDLINE_UBOOT
- bool "Support U-boot kernel command line passing"
- default n
- help
- If you are using U-boot (www.denx.de) and wish to pass the kernel
- command line from the U-boot environment to the Linux kernel then
- switch this option on.
- ARC U-boot will setup the cmdline in RAM/flash and set r2 to point
- to it. kernel startup code will append this to DeviceTree
- /bootargs provided cmdline args.
-
config ARC_BUILTIN_DTB_NAME
string "Built in DTB"
help
diff --git a/arch/arc/include/asm/Kbuild b/arch/arc/include/asm/Kbuild
index 9ae21c198007..0d3362991c31 100644
--- a/arch/arc/include/asm/Kbuild
+++ b/arch/arc/include/asm/Kbuild
@@ -12,6 +12,7 @@ generic-y += fcntl.h
generic-y += fb.h
generic-y += ftrace.h
generic-y += hardirq.h
+generic-y += hash.h
generic-y += hw_irq.h
generic-y += ioctl.h
generic-y += ioctls.h
diff --git a/arch/arc/include/asm/linkage.h b/arch/arc/include/asm/linkage.h
index 0283e9e44e0d..66ee5527aefc 100644
--- a/arch/arc/include/asm/linkage.h
+++ b/arch/arc/include/asm/linkage.h
@@ -11,6 +11,8 @@
#ifdef __ASSEMBLY__
+#define ASM_NL ` /* use '`' to mark new line in macro */
+
/* Can't use the ENTRY macro in linux/linkage.h
* gas considers ';' as comment vs. newline
*/
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);
*
* @info: SoC SMP specific info for /proc/cpuinfo etc
* @cpu_kick: For Master to kickstart a cpu (optionally at a PC)
- * @ipi_send: To send IPI to a @cpumask
- * @ips_clear: To clear IPI received by @cpu at @irq
+ * @ipi_send: To send IPI to a @cpu
+ * @ips_clear: To clear IPI received at @irq
*/
struct plat_smp_ops {
const char *info;
void (*cpu_kick)(int cpu, unsigned long pc);
- void (*ipi_send)(void *callmap);
- void (*ipi_clear)(int cpu, int irq);
+ void (*ipi_send)(int cpu);
+ void (*ipi_clear)(int irq);
};
/* 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:
st.ab 0, [r5,4]
brlt r5, r6, 1b
-#ifdef CONFIG_CMDLINE_UBOOT
- ; support for bootloader provided cmdline
- ; If cmdline passed by u-boot, then
- ; r0 = 1 (because ATAGS parsing, now retired, used to use 0)
- ; r1 = magic number (board identity)
- ; r2 = addr of cmdline string (somewhere in memory/flash)
-
- brne r0, 1, .Lother_bootup_chores ; u-boot didn't pass cmdline
- breq r2, 0, .Lother_bootup_chores ; or cmdline is NULL
-
- mov r5, @command_line
-1:
- ldb.ab r6, [r2, 1]
- breq r6, 0, .Lother_bootup_chores
- b.d 1b
- stb.ab r6, [r5, 1]
-#endif
-
-.Lother_bootup_chores:
+ ; Uboot - kernel ABI
+ ; r0 = [0] No uboot interaction, [1] cmdline in r2, [2] DTB in r2
+ ; r1 = magic number (board identity, unused as of now
+ ; r2 = pointer to uboot provided cmdline or external DTB in mem
+ ; These are handled later in setup_arch()
+ st r0, [@uboot_tag]
+ st r2, [@uboot_arg]
; Identify if running on ISS vs Silicon
; 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 @@
int running_on_hw = 1; /* vs. on ISS */
-char __initdata command_line[COMMAND_LINE_SIZE];
+/* Part of U-boot ABI: see head.S */
+int __initdata uboot_tag;
+char __initdata *uboot_arg;
+
const struct machine_desc *machine_desc;
struct task_struct *_current_task[NR_CPUS]; /* For stack switching */
@@ -311,19 +314,40 @@ void setup_processor(void)
arc_chk_fpu();
}
+static inline int is_kernel(unsigned long addr)
+{
+ if (addr >= (unsigned long)_stext && addr <= (unsigned long)_end)
+ return 1;
+ return 0;
+}
+
void __init setup_arch(char **cmdline_p)
{
- /* This also populates @boot_command_line from /bootargs */
- machine_desc = setup_machine_fdt(__dtb_start);
- if (!machine_desc)
- panic("Embedded DT invalid\n");
-
- /* Append any u-boot provided cmdline */
-#ifdef CONFIG_CMDLINE_UBOOT
- /* Add a whitespace seperator between the 2 cmdlines */
- strlcat(boot_command_line, " ", COMMAND_LINE_SIZE);
- strlcat(boot_command_line, command_line, COMMAND_LINE_SIZE);
-#endif
+ /* make sure that uboot passed pointer to cmdline/dtb is valid */
+ if (uboot_tag && is_kernel((unsigned long)uboot_arg))
+ panic("Invalid uboot arg\n");
+
+ /* See if u-boot passed an external Device Tree blob */
+ machine_desc = setup_machine_fdt(uboot_arg); /* uboot_tag == 2 */
+ if (!machine_desc) {
+ /* No, so try the embedded one */
+ machine_desc = setup_machine_fdt(__dtb_start);
+ if (!machine_desc)
+ panic("Embedded DT invalid\n");
+
+ /*
+ * If we are here, it is established that @uboot_arg didn't
+ * point to DT blob. Instead if u-boot says it is cmdline,
+ * Appent to embedded DT cmdline.
+ * setup_machine_fdt() would have populated @boot_command_line
+ */
+ if (uboot_tag == 1) {
+ /* Ensure a whitespace between the 2 cmdlines */
+ strlcat(boot_command_line, " ", COMMAND_LINE_SIZE);
+ strlcat(boot_command_line, uboot_arg,
+ COMMAND_LINE_SIZE);
+ }
+ }
/* Save unparsed command line copy for /proc/cmdline */
*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)
/* Inter Processor Interrupt Handling */
/*****************************************************************************/
-/*
- * structures for inter-processor calls
- * A Collection of single bit ipi messages
- *
- */
-
-/*
- * TODO_rajesh investigate tlb message types.
- * IPI Timer not needed because each ARC has an individual Interrupting Timer
- */
enum ipi_msg_type {
- IPI_NOP = 0,
+ IPI_EMPTY = 0,
IPI_RESCHEDULE = 1,
IPI_CALL_FUNC,
- IPI_CPU_STOP
+ IPI_CPU_STOP,
};
-struct ipi_data {
- unsigned long bits;
-};
+/*
+ * In arches with IRQ for each msg type (above), receiver can use IRQ-id to
+ * figure out what msg was sent. For those which don't (ARC has dedicated IPI
+ * IRQ), the msg-type needs to be conveyed via per-cpu data
+ */
-static DEFINE_PER_CPU(struct ipi_data, ipi_data);
+static DEFINE_PER_CPU(unsigned long, ipi_data);
-static void ipi_send_msg(const struct cpumask *callmap, enum ipi_msg_type msg)
+static void ipi_send_msg_one(int cpu, enum ipi_msg_type msg)
{
+ unsigned long __percpu *ipi_data_ptr = per_cpu_ptr(&ipi_data, cpu);
+ unsigned long old, new;
unsigned long flags;
- unsigned int cpu;
+
+ pr_debug("%d Sending msg [%d] to %d\n", smp_processor_id(), msg, cpu);
local_irq_save(flags);
- for_each_cpu(cpu, callmap) {
- struct ipi_data *ipi = &per_cpu(ipi_data, cpu);
- set_bit(msg, &ipi->bits);
- }
+ /*
+ * Atomically write new msg bit (in case others are writing too),
+ * and read back old value
+ */
+ do {
+ new = old = *ipi_data_ptr;
+ new |= 1U << msg;
+ } while (cmpxchg(ipi_data_ptr, old, new) != old);
- /* Call the platform specific cross-CPU call function */
- if (plat_smp_ops.ipi_send)
- plat_smp_ops.ipi_send((void *)callmap);
+ /*
+ * Call the platform specific IPI kick function, but avoid if possible:
+ * Only do so if there's no pending msg from other concurrent sender(s).
+ * Otherwise, recevier will see this msg as well when it takes the
+ * IPI corresponding to that msg. This is true, even if it is already in
+ * IPI handler, because !@old means it has not yet dequeued the msg(s)
+ * so @new msg can be a free-loader
+ */
+ if (plat_smp_ops.ipi_send && !old)
+ plat_smp_ops.ipi_send(cpu);
local_irq_restore(flags);
}
+static void ipi_send_msg(const struct cpumask *callmap, enum ipi_msg_type msg)
+{
+ unsigned int cpu;
+
+ for_each_cpu(cpu, callmap)
+ ipi_send_msg_one(cpu, msg);
+}
+
void smp_send_reschedule(int cpu)
{
- ipi_send_msg(cpumask_of(cpu), IPI_RESCHEDULE);
+ ipi_send_msg_one(cpu, IPI_RESCHEDULE);
}
void smp_send_stop(void)
@@ -254,7 +268,7 @@ void smp_send_stop(void)
void arch_send_call_function_single_ipi(int cpu)
{
- ipi_send_msg(cpumask_of(cpu), IPI_CALL_FUNC);
+ ipi_send_msg_one(cpu, IPI_CALL_FUNC);
}
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)
/*
* ipi_cpu_stop - handle IPI from smp_send_stop()
*/
-static void ipi_cpu_stop(unsigned int cpu)
+static void ipi_cpu_stop(void)
{
machine_halt();
}
-static inline void __do_IPI(unsigned long *ops, struct ipi_data *ipi, int cpu)
+static inline void __do_IPI(unsigned long msg)
{
- unsigned long msg = 0;
+ switch (msg) {
+ case IPI_RESCHEDULE:
+ scheduler_ipi();
+ break;
- do {
- msg = find_next_bit(ops, BITS_PER_LONG, msg+1);
+ case IPI_CALL_FUNC:
+ generic_smp_call_function_interrupt();
+ break;
- switch (msg) {
- case IPI_RESCHEDULE:
- scheduler_ipi();
- break;
-
- case IPI_CALL_FUNC:
- generic_smp_call_function_interrupt();
- break;
-
- case IPI_CPU_STOP:
- ipi_cpu_stop(cpu);
- break;
- }
- } while (msg < BITS_PER_LONG);
+ case IPI_CPU_STOP:
+ ipi_cpu_stop();
+ break;
+ default:
+ pr_warn("IPI with unexpected msg %ld\n", msg);
+ }
}
/*
@@ -300,19 +310,25 @@ static inline void __do_IPI(unsigned long *ops, struct ipi_data *ipi, int cpu)
*/
irqreturn_t do_IPI(int irq, void *dev_id)
{
- int cpu = smp_processor_id();
- struct ipi_data *ipi = &per_cpu(ipi_data, cpu);
- unsigned long ops;
+ unsigned long pending;
+
+ pr_debug("IPI [%ld] received on cpu %d\n",
+ *this_cpu_ptr(&ipi_data), smp_processor_id());
if (plat_smp_ops.ipi_clear)
- plat_smp_ops.ipi_clear(cpu, irq);
+ plat_smp_ops.ipi_clear(irq);
/*
- * XXX: is this loop really needed
- * And do we need to move ipi_clean inside
+ * "dequeue" the msg corresponding to this IPI (and possibly other
+ * piggybacked msg from elided IPIs: see ipi_send_msg_one() above)
*/
- while ((ops = xchg(&ipi->bits, 0)) != 0)
- __do_IPI(&ops, ipi, cpu);
+ pending = xchg(this_cpu_ptr(&ipi_data), 0);
+
+ do {
+ unsigned long msg = __ffs(pending);
+ __do_IPI(msg);
+ pending &= ~(1U << msg);
+ } while (pending);
return IRQ_HANDLED;
}
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)
smp_ipi_irq_setup(cpu, IDU_INTERRUPT_0 + cpu);
}
-static void iss_model_ipi_send(void *arg)
+static void iss_model_ipi_send(int cpu)
{
- struct cpumask *callmap = arg;
- unsigned int cpu;
-
- for_each_cpu(cpu, callmap)
- idu_irq_assert(cpu);
+ idu_irq_assert(cpu);
}
-static void iss_model_ipi_clear(int cpu, int irq)
+static void iss_model_ipi_clear(int irq)
{
- idu_irq_clear(IDU_INTERRUPT_0 + cpu);
+ idu_irq_clear(IDU_INTERRUPT_0 + smp_processor_id());
}
void iss_model_init_early_smp(void)