summaryrefslogtreecommitdiffstats
path: root/bsps/powerpc/qoriq/start/bspsmp.c
diff options
context:
space:
mode:
Diffstat (limited to 'bsps/powerpc/qoriq/start/bspsmp.c')
-rw-r--r--bsps/powerpc/qoriq/start/bspsmp.c252
1 files changed, 252 insertions, 0 deletions
diff --git a/bsps/powerpc/qoriq/start/bspsmp.c b/bsps/powerpc/qoriq/start/bspsmp.c
new file mode 100644
index 0000000000..a2d9fbede5
--- /dev/null
+++ b/bsps/powerpc/qoriq/start/bspsmp.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2013, 2017 embedded brains GmbH. All rights reserved.
+ *
+ * embedded brains GmbH
+ * Dornierstr. 4
+ * 82178 Puchheim
+ * Germany
+ * <rtems@embedded-brains.de>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#include <rtems/score/smpimpl.h>
+
+#include <libfdt.h>
+
+#include <libcpu/powerpc-utility.h>
+
+#include <bsp.h>
+#include <bsp/fdt.h>
+#include <bsp/mmu.h>
+#include <bsp/fatal.h>
+#include <bsp/qoriq.h>
+#include <bsp/vectors.h>
+#include <bsp/bootcard.h>
+#include <bsp/irq-generic.h>
+#include <bsp/linker-symbols.h>
+
+LINKER_SYMBOL(bsp_exc_vector_base);
+
+#if QORIQ_THREAD_COUNT > 1
+void _start_thread(void);
+#endif
+
+void _start_secondary_processor(void);
+
+#define IPI_INDEX 0
+
+#if QORIQ_THREAD_COUNT > 1
+static bool is_started_by_u_boot(uint32_t cpu_index)
+{
+ return cpu_index % QORIQ_THREAD_COUNT == 0;
+}
+
+void qoriq_start_thread(void)
+{
+ const Per_CPU_Control *cpu_self = _Per_CPU_Get();
+
+ ppc_exc_initialize_interrupt_stack(
+ (uintptr_t) cpu_self->interrupt_stack_low,
+ rtems_configuration_get_interrupt_stack_size()
+ );
+
+ bsp_interrupt_facility_initialize();
+
+ _SMP_Start_multitasking_on_secondary_processor();
+}
+#endif
+
+static void start_thread_if_necessary(uint32_t cpu_index_self)
+{
+#if QORIQ_THREAD_COUNT > 1
+ uint32_t i;
+
+ for (i = 1; i < QORIQ_THREAD_COUNT; ++i) {
+ uint32_t cpu_index_next = cpu_index_self + i;
+
+ if (
+ is_started_by_u_boot(cpu_index_self)
+ && cpu_index_next < rtems_configuration_get_maximum_processors()
+ && _SMP_Should_start_processor(cpu_index_next)
+ ) {
+ /* Thread Initial Next Instruction Address (INIA) */
+ PPC_SET_THREAD_MGMT_REGISTER(321, (uintptr_t) _start_thread);
+
+ /* Thread Initial Machine State (IMSR) */
+ PPC_SET_THREAD_MGMT_REGISTER(289, QORIQ_INITIAL_MSR);
+
+ /* Thread Enable Set (TENS) */
+ PPC_SET_SPECIAL_PURPOSE_REGISTER(FSL_EIS_TENS, 1U << i);
+ }
+ }
+#endif
+}
+
+void bsp_start_on_secondary_processor(void)
+{
+ uint32_t cpu_index_self = _SMP_Get_current_processor();
+ const Per_CPU_Control *cpu_self = _Per_CPU_Get_by_index(cpu_index_self);
+
+ qoriq_initialize_exceptions(cpu_self->interrupt_stack_low);
+ bsp_interrupt_facility_initialize();
+
+ start_thread_if_necessary(cpu_index_self);
+
+ _SMP_Start_multitasking_on_secondary_processor();
+}
+
+#ifndef QORIQ_IS_HYPERVISOR_GUEST
+static void bsp_inter_processor_interrupt(void *arg)
+{
+ _SMP_Inter_processor_interrupt_handler();
+}
+#endif
+
+static void setup_boot_page(void)
+{
+#ifdef QORIQ_IS_HYPERVISOR_GUEST
+ qoriq_mmu_context mmu_context;
+
+ qoriq_mmu_context_init(&mmu_context);
+ qoriq_mmu_add(
+ &mmu_context,
+ 0xfffff000,
+ 0xffffffff,
+ 0,
+ 0,
+ FSL_EIS_MAS3_SR | FSL_EIS_MAS3_SW,
+ 0
+ );
+ qoriq_mmu_partition(&mmu_context, 1);
+ qoriq_mmu_write_to_tlb1(&mmu_context, QORIQ_TLB1_ENTRY_COUNT - 1);
+#endif
+}
+
+static uint32_t discover_processors(void)
+{
+ const void *fdt = bsp_fdt_get();
+ int cpus = fdt_path_offset(fdt, "/cpus");
+ int node = fdt_first_subnode(fdt, cpus);
+ uint32_t cpu = 0;
+
+ while (node >= 0 && cpu < RTEMS_ARRAY_SIZE(qoriq_start_spin_table_addr)) {
+ int len;
+ fdt64_t *addr_fdt = (fdt64_t *)
+ fdt_getprop(fdt, node, "cpu-release-addr", &len);
+
+ if (addr_fdt != NULL) {
+ uintptr_t addr = (uintptr_t) fdt64_to_cpu(*addr_fdt);
+
+ qoriq_start_spin_table_addr[cpu] = (qoriq_start_spin_table *) addr;
+ }
+
+ ++cpu;
+ node = fdt_next_subnode(fdt, node);
+ }
+
+ return cpu * QORIQ_THREAD_COUNT;
+}
+
+uint32_t _CPU_SMP_Initialize(void)
+{
+ uint32_t cpu_count = 1;
+
+ if (rtems_configuration_get_maximum_processors() > 0) {
+ setup_boot_page();
+ cpu_count = discover_processors();
+ }
+
+ start_thread_if_necessary(0);
+
+ return cpu_count;
+}
+
+static bool release_processor(
+ qoriq_start_spin_table *spin_table,
+ uint32_t cpu_index
+)
+{
+ bool spin_table_present = (spin_table != NULL);
+
+ if (spin_table_present) {
+ const Per_CPU_Control *cpu = _Per_CPU_Get_by_index(cpu_index);
+
+ spin_table->pir = cpu_index;
+ spin_table->r3 = (uintptr_t) cpu->interrupt_stack_high;
+ rtems_cache_flush_multiple_data_lines(spin_table, sizeof(*spin_table));
+ ppc_synchronize_data();
+ spin_table->addr = (uintptr_t) _start_secondary_processor;
+ rtems_cache_flush_multiple_data_lines(spin_table, sizeof(*spin_table));
+ }
+
+ return spin_table_present;
+}
+
+static qoriq_start_spin_table *get_spin_table(uint32_t cpu_index)
+{
+ qoriq_start_spin_table *spin_table;
+
+ spin_table = qoriq_start_spin_table_addr[cpu_index / QORIQ_THREAD_COUNT];
+
+ return spin_table;
+}
+
+bool _CPU_SMP_Start_processor(uint32_t cpu_index)
+{
+#if QORIQ_THREAD_COUNT > 1
+ if (is_started_by_u_boot(cpu_index)) {
+ qoriq_start_spin_table *spin_table = get_spin_table(cpu_index);
+
+ return release_processor(spin_table, cpu_index);
+ } else {
+ return _SMP_Should_start_processor(cpu_index - 1);
+ }
+#else
+ qoriq_start_spin_table *spin_table = get_spin_table(cpu_index);
+
+ return release_processor(spin_table, cpu_index);
+#endif
+}
+
+void _CPU_SMP_Finalize_initialization(uint32_t cpu_count)
+{
+#ifdef QORIQ_IS_HYPERVISOR_GUEST
+ (void) cpu_count;
+#else
+ if (cpu_count > 1) {
+ rtems_status_code sc;
+
+ sc = rtems_interrupt_handler_install(
+ QORIQ_IRQ_IPI_0 + IPI_INDEX,
+ "IPI",
+ RTEMS_INTERRUPT_UNIQUE,
+ bsp_inter_processor_interrupt,
+ NULL
+ );
+ if (sc != RTEMS_SUCCESSFUL) {
+ bsp_fatal(QORIQ_FATAL_SMP_IPI_HANDLER_INSTALL);
+ }
+ }
+#endif
+}
+
+void _CPU_SMP_Prepare_start_multitasking(void)
+{
+ /* Do nothing */
+}
+
+void _CPU_SMP_Send_interrupt(uint32_t target_processor_index)
+{
+#ifdef QORIQ_IS_HYPERVISOR_GUEST
+ uint32_t msg;
+
+ /* DBELL message type */
+ msg = (0U << (63 - 36)) | target_processor_index;
+ __asm__ volatile ("msgsnd %0" : : "r" (msg));
+#else
+ qoriq.pic.ipidr [IPI_INDEX].reg = 1U << target_processor_index;
+#endif
+}