linux/arch/mips/pmc-sierra/yosemite/smp.c
<<
>>
Prefs
   1#include <linux/linkage.h>
   2#include <linux/sched.h>
   3
   4#include <asm/pmon.h>
   5#include <asm/titan_dep.h>
   6#include <asm/time.h>
   7
   8#define LAUNCHSTACK_SIZE 256
   9
  10static __cpuinitdata DEFINE_SPINLOCK(launch_lock);
  11
  12static unsigned long secondary_sp __cpuinitdata;
  13static unsigned long secondary_gp __cpuinitdata;
  14
  15static unsigned char launchstack[LAUNCHSTACK_SIZE] __initdata
  16        __attribute__((aligned(2 * sizeof(long))));
  17
  18static void __init prom_smp_bootstrap(void)
  19{
  20        local_irq_disable();
  21
  22        while (spin_is_locked(&launch_lock));
  23
  24        __asm__ __volatile__(
  25        "       move    $sp, %0         \n"
  26        "       move    $gp, %1         \n"
  27        "       j       smp_bootstrap   \n"
  28        :
  29        : "r" (secondary_sp), "r" (secondary_gp));
  30}
  31
  32/*
  33 * PMON is a fragile beast.  It'll blow up once the mappings it's littering
  34 * right into the middle of KSEG3 are blown away so we have to grab the slave
  35 * core early and keep it in a waiting loop.
  36 */
  37void __init prom_grab_secondary(void)
  38{
  39        spin_lock(&launch_lock);
  40
  41        pmon_cpustart(1, &prom_smp_bootstrap,
  42                      launchstack + LAUNCHSTACK_SIZE, 0);
  43}
  44
  45void titan_mailbox_irq(void)
  46{
  47        int cpu = smp_processor_id();
  48        unsigned long status;
  49
  50        switch (cpu) {
  51        case 0:
  52                status = OCD_READ(RM9000x2_OCD_INTP0STATUS3);
  53                OCD_WRITE(RM9000x2_OCD_INTP0CLEAR3, status);
  54
  55                if (status & 0x2)
  56                        smp_call_function_interrupt();
  57                break;
  58
  59        case 1:
  60                status = OCD_READ(RM9000x2_OCD_INTP1STATUS3);
  61                OCD_WRITE(RM9000x2_OCD_INTP1CLEAR3, status);
  62
  63                if (status & 0x2)
  64                        smp_call_function_interrupt();
  65                break;
  66        }
  67}
  68
  69/*
  70 * Send inter-processor interrupt
  71 */
  72static void yos_send_ipi_single(int cpu, unsigned int action)
  73{
  74        /*
  75         * Generate an INTMSG so that it can be sent over to the
  76         * destination CPU. The INTMSG will put the STATUS bits
  77         * based on the action desired. An alternative strategy
  78         * is to write to the Interrupt Set register, read the
  79         * Interrupt Status register and clear the Interrupt
  80         * Clear register. The latter is preffered.
  81         */
  82        switch (action) {
  83        case SMP_RESCHEDULE_YOURSELF:
  84                if (cpu == 1)
  85                        OCD_WRITE(RM9000x2_OCD_INTP1SET3, 4);
  86                else
  87                        OCD_WRITE(RM9000x2_OCD_INTP0SET3, 4);
  88                break;
  89
  90        case SMP_CALL_FUNCTION:
  91                if (cpu == 1)
  92                        OCD_WRITE(RM9000x2_OCD_INTP1SET3, 2);
  93                else
  94                        OCD_WRITE(RM9000x2_OCD_INTP0SET3, 2);
  95                break;
  96        }
  97}
  98
  99static void yos_send_ipi_mask(cpumask_t mask, unsigned int action)
 100{
 101        unsigned int i;
 102
 103        for_each_cpu_mask(i, mask)
 104                yos_send_ipi_single(i, action);
 105}
 106
 107/*
 108 *  After we've done initial boot, this function is called to allow the
 109 *  board code to clean up state, if needed
 110 */
 111static void __cpuinit yos_init_secondary(void)
 112{
 113        set_c0_status(ST0_CO | ST0_IE | ST0_IM);
 114}
 115
 116static void __cpuinit yos_smp_finish(void)
 117{
 118}
 119
 120/* Hook for after all CPUs are online */
 121static void yos_cpus_done(void)
 122{
 123}
 124
 125/*
 126 * Firmware CPU startup hook
 127 * Complicated by PMON's weird interface which tries to minimic the UNIX fork.
 128 * It launches the next * available CPU and copies some information on the
 129 * stack so the first thing we do is throw away that stuff and load useful
 130 * values into the registers ...
 131 */
 132static void __cpuinit yos_boot_secondary(int cpu, struct task_struct *idle)
 133{
 134        unsigned long gp = (unsigned long) task_thread_info(idle);
 135        unsigned long sp = __KSTK_TOS(idle);
 136
 137        secondary_sp = sp;
 138        secondary_gp = gp;
 139
 140        spin_unlock(&launch_lock);
 141}
 142
 143/*
 144 * Detect available CPUs, populate phys_cpu_present_map before smp_init
 145 *
 146 * We don't want to start the secondary CPU yet nor do we have a nice probing
 147 * feature in PMON so we just assume presence of the secondary core.
 148 */
 149static void __init yos_smp_setup(void)
 150{
 151        int i;
 152
 153        cpus_clear(phys_cpu_present_map);
 154
 155        for (i = 0; i < 2; i++) {
 156                cpu_set(i, phys_cpu_present_map);
 157                __cpu_number_map[i]     = i;
 158                __cpu_logical_map[i]    = i;
 159        }
 160}
 161
 162static void __init yos_prepare_cpus(unsigned int max_cpus)
 163{
 164        /*
 165         * Be paranoid.  Enable the IPI only if we're really about to go SMP.
 166         */
 167        if (cpus_weight(cpu_possible_map))
 168                set_c0_status(STATUSF_IP5);
 169}
 170
 171struct plat_smp_ops yos_smp_ops = {
 172        .send_ipi_single        = yos_send_ipi_single,
 173        .send_ipi_mask          = yos_send_ipi_mask,
 174        .init_secondary         = yos_init_secondary,
 175        .smp_finish             = yos_smp_finish,
 176        .cpus_done              = yos_cpus_done,
 177        .boot_secondary         = yos_boot_secondary,
 178        .smp_setup              = yos_smp_setup,
 179        .prepare_cpus           = yos_prepare_cpus,
 180};
 181
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.