#include #include #include #include #include #include #define LAUNCHSTACK_SIZE 256 static __cpuinitdata arch_spinlock_t launch_lock = __ARCH_SPIN_LOCK_UNLOCKED; static unsigned long secondary_sp __cpuinitdata; static unsigned long secondary_gp __cpuinitdata; static unsigned char launchstack[LAUNCHSTACK_SIZE] __initdata __attribute__((aligned(2 * sizeof(long)))); static void __init prom_smp_bootstrap(void) { local_irq_disable(); while (arch_spin_is_locked(&launch_lock)); __asm__ __volatile__( " move $sp, %0 \n" " move $gp, %1 \n" " j smp_bootstrap \n" : : "r" (secondary_sp), "r" (secondary_gp)); } /* * PMON is a fragile beast. It'll blow up once the mappings it's littering * right into the middle of KSEG3 are blown away so we have to grab the slave * core early and keep it in a waiting loop. */ void __init prom_grab_secondary(void) { arch_spin_lock(&launch_lock); pmon_cpustart(1, &prom_smp_bootstrap, launchstack + LAUNCHSTACK_SIZE, 0); } void titan_mailbox_irq(void) { int cpu = smp_processor_id(); unsigned long status; switch (cpu) { case 0: status = OCD_READ(RM9000x2_OCD_INTP0STATUS3); OCD_WRITE(RM9000x2_OCD_INTP0CLEAR3, status); if (status & 0x2) smp_call_function_interrupt(); if (status & 0x4) scheduler_ipi(); break; case 1: status = OCD_READ(RM9000x2_OCD_INTP1STATUS3); OCD_WRITE(RM9000x2_OCD_INTP1CLEAR3, status); if (status & 0x2) smp_call_function_interrupt(); if (status & 0x4) scheduler_ipi(); break; } } /* * Send inter-processor interrupt */ static void yos_send_ipi_single(int cpu, unsigned int action) { /* * Generate an INTMSG so that it can be sent over to the * destination CPU. The INTMSG will put the STATUS bits * based on the action desired. An alternative strategy * is to write to the Interrupt Set register, read the * Interrupt Status register and clear the Interrupt * Clear register. The latter is preffered. */ switch (action) { case SMP_RESCHEDULE_YOURSELF: if (cpu == 1) OCD_WRITE(RM9000x2_OCD_INTP1SET3, 4); else OCD_WRITE(RM9000x2_OCD_INTP0SET3, 4); break; case SMP_CALL_FUNCTION: if (cpu == 1) OCD_WRITE(RM9000x2_OCD_INTP1SET3, 2); else OCD_WRITE(RM9000x2_OCD_INTP0SET3, 2); break; } } static void yos_send_ipi_mask(const struct cpumask *mask, unsigned int action) { unsigned int i; for_each_cpu(i, mask) yos_send_ipi_single(i, action); } /* * After we've done initial boot, this function is called to allow the * board code to clean up state, if needed */ static void __cpuinit yos_init_secondary(void) { set_c0_status(ST0_CO | ST0_IE | ST0_IM); } static void __cpuinit yos_smp_finish(void) { } /* Hook for after all CPUs are online */ static void yos_cpus_done(void) { } /* * Firmware CPU startup hook * Complicated by PMON's weird interface which tries to minimic the UNIX fork. * It launches the next * available CPU and copies some information on the * stack so the first thing we do is throw away that stuff and load useful * values into the registers ... */ static void __cpuinit yos_boot_secondary(int cpu, struct task_struct *idle) { unsigned long gp = (unsigned long) task_thread_info(idle); unsigned long sp = __KSTK_TOS(idle); secondary_sp = sp; secondary_gp = gp; arch_spin_unlock(&launch_lock); } /* * Detect available CPUs, populate cpu_possible_map before smp_init * * We don't want to start the secondary CPU yet nor do we have a nice probing * feature in PMON so we just assume presence of the secondary core. */ static void __init yos_smp_setup(void) { int i; init_cpu_possible(cpu_none_mask); for (i = 0; i < 2; i++) { set_cpu_possible(i, true); __cpu_number_map[i] = i; __cpu_logical_map[i] = i; } } static void __init yos_prepare_cpus(unsigned int max_cpus) { /* * Be paranoid. Enable the IPI only if we're really about to go SMP. */ if (num_possible_cpus()) set_c0_status(STATUSF_IP5); } struct plat_smp_ops yos_smp_ops = { .send_ipi_single = yos_send_ipi_single, .send_ipi_mask = yos_send_ipi_mask, .init_secondary = yos_init_secondary, .smp_finish = yos_smp_finish, .cpus_done = yos_cpus_done, .boot_secondary = yos_boot_secondary, .smp_setup = yos_smp_setup, .prepare_cpus = yos_prepare_cpus, };