summaryrefslogtreecommitdiff
path: root/arch/mips/cavium-octeon/smp.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/cavium-octeon/smp.c')
-rw-r--r--arch/mips/cavium-octeon/smp.c307
1 files changed, 204 insertions, 103 deletions
diff --git a/arch/mips/cavium-octeon/smp.c b/arch/mips/cavium-octeon/smp.c
index 295137dfdc37..054e331b3202 100644
--- a/arch/mips/cavium-octeon/smp.c
+++ b/arch/mips/cavium-octeon/smp.c
@@ -6,17 +6,21 @@
* Copyright (C) 2004-2008, 2009, 2010 Cavium Networks
*/
#include <linux/cpu.h>
-#include <linux/init.h>
#include <linux/delay.h>
#include <linux/smp.h>
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
#include <linux/sched.h>
-#include <linux/module.h>
+#include <linux/sched/hotplug.h>
+#include <linux/sched/task_stack.h>
+#include <linux/init.h>
+#include <linux/export.h>
+#include <linux/kexec.h>
#include <asm/mmu_context.h>
#include <asm/time.h>
#include <asm/setup.h>
+#include <asm/smp.h>
#include <asm/octeon/octeon.h>
@@ -25,35 +29,70 @@
volatile unsigned long octeon_processor_boot = 0xff;
volatile unsigned long octeon_processor_sp;
volatile unsigned long octeon_processor_gp;
+#ifdef CONFIG_RELOCATABLE
+volatile unsigned long octeon_processor_relocated_kernel_entry;
+#endif /* CONFIG_RELOCATABLE */
#ifdef CONFIG_HOTPLUG_CPU
uint64_t octeon_bootloader_entry_addr;
EXPORT_SYMBOL(octeon_bootloader_entry_addr);
#endif
+extern void kernel_entry(unsigned long arg1, ...);
+
+static void octeon_icache_flush(void)
+{
+ asm volatile ("synci 0($0)\n");
+}
+
+static void (*octeon_message_functions[8])(void) = {
+ scheduler_ipi,
+ generic_smp_call_function_interrupt,
+ octeon_icache_flush,
+};
+
static irqreturn_t mailbox_interrupt(int irq, void *dev_id)
{
- const int coreid = cvmx_get_core_num();
- uint64_t action;
+ u64 mbox_clrx = CVMX_CIU_MBOX_CLRX(cvmx_get_core_num());
+ u64 action;
+ int i;
+
+ /*
+ * Make sure the function array initialization remains
+ * correct.
+ */
+ BUILD_BUG_ON(SMP_RESCHEDULE_YOURSELF != (1 << 0));
+ BUILD_BUG_ON(SMP_CALL_FUNCTION != (1 << 1));
+ BUILD_BUG_ON(SMP_ICACHE_FLUSH != (1 << 2));
+
+ /*
+ * Load the mailbox register to figure out what we're supposed
+ * to do.
+ */
+ action = cvmx_read_csr(mbox_clrx);
- /* Load the mailbox register to figure out what we're supposed to do */
- action = cvmx_read_csr(CVMX_CIU_MBOX_CLRX(coreid)) & 0xffff;
+ if (OCTEON_IS_MODEL(OCTEON_CN68XX))
+ action &= 0xff;
+ else
+ action &= 0xffff;
/* Clear the mailbox to clear the interrupt */
- cvmx_write_csr(CVMX_CIU_MBOX_CLRX(coreid), action);
+ cvmx_write_csr(mbox_clrx, action);
- if (action & SMP_CALL_FUNCTION)
- smp_call_function_interrupt();
- if (action & SMP_RESCHEDULE_YOURSELF)
- scheduler_ipi();
+ for (i = 0; i < ARRAY_SIZE(octeon_message_functions) && action;) {
+ if (action & 1) {
+ void (*fn)(void) = octeon_message_functions[i];
- /* Check if we've been told to flush the icache */
- if (action & SMP_ICACHE_FLUSH)
- asm volatile ("synci 0($0)\n");
+ if (fn)
+ fn();
+ }
+ action >>= 1;
+ i++;
+ }
return IRQ_HANDLED;
}
-/**
+/*
* Cause the function described by call_data to be executed on the passed
* cpu. When the function has finished, increment the finished field of
* call_data.
@@ -73,11 +112,11 @@ static inline void octeon_send_ipi_mask(const struct cpumask *mask,
{
unsigned int i;
- for_each_cpu_mask(i, *mask)
+ for_each_cpu(i, mask)
octeon_send_ipi_single(i, action);
}
-/**
+/*
* Detect available CPUs, populate cpu_possible_mask
*/
static void octeon_smp_hotplug_setup(void)
@@ -85,21 +124,28 @@ static void octeon_smp_hotplug_setup(void)
#ifdef CONFIG_HOTPLUG_CPU
struct linux_app_boot_info *labi;
+ if (!setup_max_cpus)
+ return;
+
labi = (struct linux_app_boot_info *)PHYS_TO_XKSEG_CACHED(LABI_ADDR_IN_BOOTLOADER);
- if (labi->labi_signature != LABI_SIGNATURE)
- panic("The bootloader version on this board is incorrect.");
+ if (labi->labi_signature != LABI_SIGNATURE) {
+ pr_info("The bootloader on this board does not support HOTPLUG_CPU.");
+ return;
+ }
octeon_bootloader_entry_addr = labi->InitTLBStart_addr;
#endif
}
-static void octeon_smp_setup(void)
+static void __init octeon_smp_setup(void)
{
const int coreid = cvmx_get_core_num();
int cpus;
int id;
- int core_mask = octeon_get_boot_coremask();
+ struct cvmx_sysinfo *sysinfo = cvmx_sysinfo_get();
+
#ifdef CONFIG_HOTPLUG_CPU
+ int core_mask = octeon_get_boot_coremask();
unsigned int num_cores = cvmx_octeon_num_cores();
#endif
@@ -115,7 +161,7 @@ static void octeon_smp_setup(void)
/* The present CPUs get the lowest CPU numbers. */
cpus = 1;
for (id = 0; id < NR_CPUS; id++) {
- if ((id != coreid) && (core_mask & (1 << id))) {
+ if ((id != coreid) && cvmx_coremask_is_core_set(&sysinfo->core_mask, id)) {
set_cpu_possible(cpus, true);
set_cpu_present(cpus, true);
__cpu_number_map[id] = cpus;
@@ -130,7 +176,8 @@ static void octeon_smp_setup(void)
* will assign CPU numbers for possible cores as well. Cores
* are always consecutively numberd from 0.
*/
- for (id = 0; id < num_cores && id < NR_CPUS; id++) {
+ for (id = 0; setup_max_cpus && octeon_bootloader_entry_addr &&
+ id < num_cores && id < NR_CPUS; id++) {
if (!(core_mask & (1 << id))) {
set_cpu_possible(cpus, true);
__cpu_number_map[id] = cpus;
@@ -143,11 +190,23 @@ static void octeon_smp_setup(void)
octeon_smp_hotplug_setup();
}
-/**
+
+#ifdef CONFIG_RELOCATABLE
+int plat_post_relocation(long offset)
+{
+ unsigned long entry = (unsigned long)kernel_entry;
+
+ /* Send secondaries into relocated kernel */
+ octeon_processor_relocated_kernel_entry = entry + offset;
+
+ return 0;
+}
+#endif /* CONFIG_RELOCATABLE */
+
+/*
* Firmware CPU startup hook
- *
*/
-static void octeon_boot_secondary(int cpu, struct task_struct *idle)
+static int octeon_boot_secondary(int cpu, struct task_struct *idle)
{
int count;
@@ -165,15 +224,19 @@ static void octeon_boot_secondary(int cpu, struct task_struct *idle)
udelay(1);
count--;
}
- if (count == 0)
+ if (count == 0) {
pr_err("Secondary boot timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
}
-/**
+/*
* After we've done initial boot, this function is called to allow the
* board code to clean up state, if needed
*/
-static void __cpuinit octeon_init_secondary(void)
+static void octeon_init_secondary(void)
{
unsigned int sr;
@@ -187,20 +250,11 @@ static void __cpuinit octeon_init_secondary(void)
octeon_irq_setup_secondary();
}
-/**
+/*
* Callout to firmware before smp_init
- *
*/
-void octeon_prepare_cpus(unsigned int max_cpus)
+static void __init octeon_prepare_cpus(unsigned int max_cpus)
{
-#ifdef CONFIG_HOTPLUG_CPU
- struct linux_app_boot_info *labi;
-
- labi = (struct linux_app_boot_info *)PHYS_TO_XKSEG_CACHED(LABI_ADDR_IN_BOOTLOADER);
-
- if (labi->labi_signature != LABI_SIGNATURE)
- panic("The bootloader version on this board is incorrect.");
-#endif
/*
* Only the low order mailbox bits are used for IPIs, leave
* the other bits alone.
@@ -213,21 +267,12 @@ void octeon_prepare_cpus(unsigned int max_cpus)
}
}
-/**
+/*
* Last chance for the board code to finish SMP initialization before
* the CPU is "online".
*/
static void octeon_smp_finish(void)
{
-#ifdef CONFIG_CAVIUM_GDB
- unsigned long tmp;
- /* Pulse MCD0 signal on Ctrl-C to stop all the cores. Also set the MCD0
- to be not masked by this core so we know the signal is received by
- someone */
- asm volatile ("dmfc0 %0, $22\n"
- "ori %0, %0, 0x9100\n" "dmtc0 %0, $22\n" : "=r" (tmp));
-#endif
-
octeon_user_io_init();
/* to generate the first CPU timer interrupt */
@@ -235,42 +280,23 @@ static void octeon_smp_finish(void)
local_irq_enable();
}
-/**
- * Hook for after all CPUs are online
- */
-static void octeon_cpus_done(void)
-{
-#ifdef CONFIG_CAVIUM_GDB
- unsigned long tmp;
- /* Pulse MCD0 signal on Ctrl-C to stop all the cores. Also set the MCD0
- to be not masked by this core so we know the signal is received by
- someone */
- asm volatile ("dmfc0 %0, $22\n"
- "ori %0, %0, 0x9100\n" "dmtc0 %0, $22\n" : "=r" (tmp));
-#endif
-}
-
#ifdef CONFIG_HOTPLUG_CPU
/* State of each CPU. */
-DEFINE_PER_CPU(int, cpu_state);
-
-extern void fixup_irqs(void);
+static DEFINE_PER_CPU(int, cpu_state);
static int octeon_cpu_disable(void)
{
unsigned int cpu = smp_processor_id();
- if (cpu == 0)
- return -EBUSY;
+ if (!octeon_bootloader_entry_addr)
+ return -ENOTSUPP;
set_cpu_online(cpu, false);
- cpu_clear(cpu, cpu_callin_map);
- local_irq_disable();
- fixup_irqs();
- local_irq_enable();
+ calculate_cpu_foreign_map();
+ octeon_fixup_irqs();
- flush_cache_all();
+ __flush_cache_all();
local_flush_tlb_all();
return 0;
@@ -308,7 +334,7 @@ static void octeon_cpu_die(unsigned int cpu)
new_mask = *p;
}
- pr_info("Reset core %d. Available Coremask = 0x%x \n", coreid, new_mask);
+ pr_info("Reset core %d. Available Coremask = 0x%x\n", coreid, new_mask);
mb();
cvmx_write_csr(CVMX_CIU_PP_RST, 1 << coreid);
cvmx_write_csr(CVMX_CIU_PP_RST, 0);
@@ -319,6 +345,7 @@ void play_dead(void)
int cpu = cpu_number_map(cvmx_get_core_num());
idle_task_exit();
+ cpuhp_ap_report_dead();
octeon_processor_boot = 0xff;
per_cpu(cpu_state, cpu) = CPU_DEAD;
@@ -328,8 +355,6 @@ void play_dead(void)
;
}
-extern void kernel_entry(unsigned long arg1, ...);
-
static void start_after_reset(void)
{
kernel_entry(0, 0, 0); /* set a2 = 0 for secondary core */
@@ -359,7 +384,7 @@ static int octeon_update_boot_vector(unsigned int cpu)
}
if (!(avail_coremask & (1 << coreid))) {
- /* core not available, assume, that catched by simple-executive */
+ /* core not available, assume, that caught by simple-executive */
cvmx_write_csr(CVMX_CIU_PP_RST, 1 << coreid);
cvmx_write_csr(CVMX_CIU_PP_RST, 0);
}
@@ -375,40 +400,21 @@ static int octeon_update_boot_vector(unsigned int cpu)
return 0;
}
-static int __cpuinit octeon_cpu_callback(struct notifier_block *nfb,
- unsigned long action, void *hcpu)
-{
- unsigned int cpu = (unsigned long)hcpu;
-
- switch (action) {
- case CPU_UP_PREPARE:
- octeon_update_boot_vector(cpu);
- break;
- case CPU_ONLINE:
- pr_info("Cpu %d online\n", cpu);
- break;
- case CPU_DEAD:
- break;
- }
-
- return NOTIFY_OK;
-}
-
-static int __cpuinit register_cavium_notifier(void)
+static int register_cavium_notifier(void)
{
- hotcpu_notifier(octeon_cpu_callback, 0);
- return 0;
+ return cpuhp_setup_state_nocalls(CPUHP_MIPS_SOC_PREPARE,
+ "mips/cavium:prepare",
+ octeon_update_boot_vector, NULL);
}
late_initcall(register_cavium_notifier);
#endif /* CONFIG_HOTPLUG_CPU */
-struct plat_smp_ops octeon_smp_ops = {
+static const struct plat_smp_ops octeon_smp_ops = {
.send_ipi_single = octeon_send_ipi_single,
.send_ipi_mask = octeon_send_ipi_mask,
.init_secondary = octeon_init_secondary,
.smp_finish = octeon_smp_finish,
- .cpus_done = octeon_cpus_done,
.boot_secondary = octeon_boot_secondary,
.smp_setup = octeon_smp_setup,
.prepare_cpus = octeon_prepare_cpus,
@@ -416,4 +422,99 @@ struct plat_smp_ops octeon_smp_ops = {
.cpu_disable = octeon_cpu_disable,
.cpu_die = octeon_cpu_die,
#endif
+#ifdef CONFIG_KEXEC_CORE
+ .kexec_nonboot_cpu = kexec_nonboot_cpu_jump,
+#endif
};
+
+static irqreturn_t octeon_78xx_reched_interrupt(int irq, void *dev_id)
+{
+ scheduler_ipi();
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t octeon_78xx_call_function_interrupt(int irq, void *dev_id)
+{
+ generic_smp_call_function_interrupt();
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t octeon_78xx_icache_flush_interrupt(int irq, void *dev_id)
+{
+ octeon_icache_flush();
+ return IRQ_HANDLED;
+}
+
+/*
+ * Callout to firmware before smp_init
+ */
+static void octeon_78xx_prepare_cpus(unsigned int max_cpus)
+{
+ if (request_irq(OCTEON_IRQ_MBOX0 + 0,
+ octeon_78xx_reched_interrupt,
+ IRQF_PERCPU | IRQF_NO_THREAD, "Scheduler",
+ octeon_78xx_reched_interrupt)) {
+ panic("Cannot request_irq for SchedulerIPI");
+ }
+ if (request_irq(OCTEON_IRQ_MBOX0 + 1,
+ octeon_78xx_call_function_interrupt,
+ IRQF_PERCPU | IRQF_NO_THREAD, "SMP-Call",
+ octeon_78xx_call_function_interrupt)) {
+ panic("Cannot request_irq for SMP-Call");
+ }
+ if (request_irq(OCTEON_IRQ_MBOX0 + 2,
+ octeon_78xx_icache_flush_interrupt,
+ IRQF_PERCPU | IRQF_NO_THREAD, "ICache-Flush",
+ octeon_78xx_icache_flush_interrupt)) {
+ panic("Cannot request_irq for ICache-Flush");
+ }
+}
+
+static void octeon_78xx_send_ipi_single(int cpu, unsigned int action)
+{
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ if (action & 1)
+ octeon_ciu3_mbox_send(cpu, i);
+ action >>= 1;
+ }
+}
+
+static void octeon_78xx_send_ipi_mask(const struct cpumask *mask,
+ unsigned int action)
+{
+ unsigned int cpu;
+
+ for_each_cpu(cpu, mask)
+ octeon_78xx_send_ipi_single(cpu, action);
+}
+
+static const struct plat_smp_ops octeon_78xx_smp_ops = {
+ .send_ipi_single = octeon_78xx_send_ipi_single,
+ .send_ipi_mask = octeon_78xx_send_ipi_mask,
+ .init_secondary = octeon_init_secondary,
+ .smp_finish = octeon_smp_finish,
+ .boot_secondary = octeon_boot_secondary,
+ .smp_setup = octeon_smp_setup,
+ .prepare_cpus = octeon_78xx_prepare_cpus,
+#ifdef CONFIG_HOTPLUG_CPU
+ .cpu_disable = octeon_cpu_disable,
+ .cpu_die = octeon_cpu_die,
+#endif
+#ifdef CONFIG_KEXEC_CORE
+ .kexec_nonboot_cpu = kexec_nonboot_cpu_jump,
+#endif
+};
+
+void __init octeon_setup_smp(void)
+{
+ const struct plat_smp_ops *ops;
+
+ if (octeon_has_feature(OCTEON_FEATURE_CIU3))
+ ops = &octeon_78xx_smp_ops;
+ else
+ ops = &octeon_smp_ops;
+
+ register_smp_ops(ops);
+}