diff options
author | David Daney <david.daney@cavium.com> | 2016-02-09 22:00:12 +0300 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2016-05-13 15:01:41 +0300 |
commit | c6d2b22eb5211e96e932428bc1183c6ad57d5e54 (patch) | |
tree | 9f0fba6e78f38b3a4bcc02be2690818374b301b0 /arch/mips/cavium-octeon/smp.c | |
parent | ce210d35bb93c2c5c8857878123275ecdcfd7018 (diff) | |
download | linux-c6d2b22eb5211e96e932428bc1183c6ad57d5e54.tar.xz |
MIPS: OCTEON: Add SMP support for OCTEON cn78xx et al.
OCTEON chips with the CIU3 interrupt controller use a different IPI
mechanism that previous models.
Add plat_smp_ops for the cn78xx and probing code to choose between the
two types of ops.
Signed-off-by: David Daney <david.daney@cavium.com>
Cc: linux-mips@linux-mips.org
Cc: linux-kernel@vger.kernel.org
Patchwork: https://patchwork.linux-mips.org/patch/12499/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/cavium-octeon/smp.c')
-rw-r--r-- | arch/mips/cavium-octeon/smp.c | 145 |
1 files changed, 132 insertions, 13 deletions
diff --git a/arch/mips/cavium-octeon/smp.c b/arch/mips/cavium-octeon/smp.c index 42395c561aa5..32c0cce73ed7 100644 --- a/arch/mips/cavium-octeon/smp.c +++ b/arch/mips/cavium-octeon/smp.c @@ -30,25 +30,55 @@ uint64_t octeon_bootloader_entry_addr; EXPORT_SYMBOL(octeon_bootloader_entry_addr); #endif +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) - generic_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; } @@ -102,10 +132,10 @@ static void 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 @@ -390,3 +420,92 @@ struct plat_smp_ops octeon_smp_ops = { .cpu_die = octeon_cpu_die, #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 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 +}; + +void __init octeon_setup_smp(void) +{ + 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); +} |