c1b659c55da5553dada9e91d3121e24f397fbeba
[linux-2.6.git] / arch / mips / pmc-sierra / yosemite / smp.c
1 #include <linux/linkage.h>
2 #include <linux/sched.h>
3
4 #include <asm/pmon.h>
5 #include <asm/titan_dep.h>
6
7 #define LAUNCHSTACK_SIZE 256
8
9 static spinlock_t launch_lock __initdata;
10
11 static unsigned long secondary_sp __initdata;
12 static unsigned long secondary_gp __initdata;
13
14 static unsigned char launchstack[LAUNCHSTACK_SIZE] __initdata
15         __attribute__((aligned(2 * sizeof(long))));
16
17 static void __init prom_smp_bootstrap(void)
18 {
19         local_irq_disable();
20
21         while (spin_is_locked(&launch_lock));
22
23         __asm__ __volatile__(
24         "       move    $sp, %0         \n"
25         "       move    $gp, %1         \n"
26         "       j       smp_bootstrap   \n"
27         :
28         : "r" (secondary_sp), "r" (secondary_gp));
29 }
30
31 /*
32  * PMON is a fragile beast.  It'll blow up once the mappings it's littering
33  * right into the middle of KSEG3 are blown away so we have to grab the slave
34  * core early and keep it in a waiting loop.
35  */
36 void __init prom_grab_secondary(void)
37 {
38         spin_lock(&launch_lock);
39
40         debug_vectors->cpustart(1, &prom_smp_bootstrap,
41                                 launchstack + LAUNCHSTACK_SIZE, 0);
42 }
43
44 /*
45  * Detect available CPUs, populate phys_cpu_present_map before smp_init
46  *
47  * We don't want to start the secondary CPU yet nor do we have a nice probing
48  * feature in PMON so we just assume presence of the secondary core.
49  */
50 void prom_prepare_cpus(unsigned int max_cpus)
51 {
52         cpus_clear(phys_cpu_present_map);
53
54         /*
55          * The boot CPU
56          */
57         cpu_set(0, phys_cpu_present_map);
58         __cpu_number_map[0]     = 0;
59         __cpu_logical_map[0]    = 0;
60
61         /*
62          * The secondary core
63          */
64         cpu_set(1, phys_cpu_present_map);
65         __cpu_number_map[1]     = 1;
66         __cpu_logical_map[1]    = 1;
67 }
68
69 /*
70  * Firmware CPU startup hook
71  * Complicated by PMON's weird interface which tries to minimic the UNIX fork.
72  * It launches the next * available CPU and copies some information on the
73  * stack so the first thing we do is throw away that stuff and load useful
74  * values into the registers ...
75  */
76 void prom_boot_secondary(int cpu, struct task_struct *idle)
77 {
78         unsigned long gp = (unsigned long) idle->thread_info;
79         unsigned long sp = gp + THREAD_SIZE - 32;
80
81         secondary_sp = sp;
82         secondary_gp = gp;
83
84         spin_unlock(&launch_lock);
85 }
86
87 /* Hook for after all CPUs are online */
88 void prom_cpus_done(void)
89 {
90 }
91
92 /*
93  *  After we've done initial boot, this function is called to allow the
94  *  board code to clean up state, if needed
95  */
96 void prom_init_secondary(void)
97 {
98         set_c0_status(ST0_CO | ST0_IE | ST0_IM);
99 }
100
101 void prom_smp_finish(void)
102 {
103 }
104
105 asmlinkage void titan_mailbox_irq(struct pt_regs *regs)
106 {
107         int cpu = smp_processor_id();
108         unsigned long status;
109
110         if (cpu == 0) {
111                 status = OCD_READ(RM9000x2_OCD_INTP0STATUS3);
112                 OCD_WRITE(RM9000x2_OCD_INTP0CLEAR3, status);
113         }
114
115         if (cpu == 1) {
116                 status = OCD_READ(RM9000x2_OCD_INTP1STATUS3);
117                 OCD_WRITE(RM9000x2_OCD_INTP1CLEAR3, status);
118         }
119
120         if (status & 0x2)
121                 smp_call_function_interrupt();
122 }
123
124 /*
125  * Send inter-processor interrupt
126  */
127 void core_send_ipi(int cpu, unsigned int action)
128 {
129         /*
130          * Generate an INTMSG so that it can be sent over to the
131          * destination CPU. The INTMSG will put the STATUS bits
132          * based on the action desired. An alternative strategy
133          * is to write to the Interrupt Set register, read the
134          * Interrupt Status register and clear the Interrupt
135          * Clear register. The latter is preffered.
136          */
137         switch (action) {
138         case SMP_RESCHEDULE_YOURSELF:
139                 if (cpu == 1)
140                         OCD_WRITE(RM9000x2_OCD_INTP1SET3, 4);
141                 else
142                         OCD_WRITE(RM9000x2_OCD_INTP0SET3, 4);
143                 break;
144
145         case SMP_CALL_FUNCTION:
146                 if (cpu == 1)
147                         OCD_WRITE(RM9000x2_OCD_INTP1SET3, 2);
148                 else
149                         OCD_WRITE(RM9000x2_OCD_INTP0SET3, 2);
150                 break;
151         }
152 }