Merge davem@master.kernel.org:/pub/scm/linux/kernel/git/vxy/lksctp-dev
[linux-2.6] / 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 #include <asm/time.h>
7
8 #define LAUNCHSTACK_SIZE 256
9
10 static __initdata DEFINE_SPINLOCK(launch_lock);
11
12 static unsigned long secondary_sp __initdata;
13 static unsigned long secondary_gp __initdata;
14
15 static unsigned char launchstack[LAUNCHSTACK_SIZE] __initdata
16         __attribute__((aligned(2 * sizeof(long))));
17
18 static 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  */
37 void __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
45 /*
46  * Detect available CPUs, populate phys_cpu_present_map before smp_init
47  *
48  * We don't want to start the secondary CPU yet nor do we have a nice probing
49  * feature in PMON so we just assume presence of the secondary core.
50  */
51 void __init plat_smp_setup(void)
52 {
53         int i;
54
55         cpus_clear(phys_cpu_present_map);
56
57         for (i = 0; i < 2; i++) {
58                 cpu_set(i, phys_cpu_present_map);
59                 __cpu_number_map[i]     = i;
60                 __cpu_logical_map[i]    = i;
61         }
62 }
63
64 void __init plat_prepare_cpus(unsigned int max_cpus)
65 {
66         /*
67          * Be paranoid.  Enable the IPI only if we're really about to go SMP.
68          */
69         if (cpus_weight(cpu_possible_map))
70                 set_c0_status(STATUSF_IP5);
71 }
72
73 /*
74  * Firmware CPU startup hook
75  * Complicated by PMON's weird interface which tries to minimic the UNIX fork.
76  * It launches the next * available CPU and copies some information on the
77  * stack so the first thing we do is throw away that stuff and load useful
78  * values into the registers ...
79  */
80 void __init prom_boot_secondary(int cpu, struct task_struct *idle)
81 {
82         unsigned long gp = (unsigned long) task_thread_info(idle);
83         unsigned long sp = __KSTK_TOS(idle);
84
85         secondary_sp = sp;
86         secondary_gp = gp;
87
88         spin_unlock(&launch_lock);
89 }
90
91 /* Hook for after all CPUs are online */
92 void prom_cpus_done(void)
93 {
94 }
95
96 /*
97  *  After we've done initial boot, this function is called to allow the
98  *  board code to clean up state, if needed
99  */
100 void prom_init_secondary(void)
101 {
102         set_c0_status(ST0_CO | ST0_IE | ST0_IM);
103 }
104
105 void prom_smp_finish(void)
106 {
107 }
108
109 void titan_mailbox_irq(void)
110 {
111         int cpu = smp_processor_id();
112         unsigned long status;
113
114         switch (cpu) {
115         case 0:
116                 status = OCD_READ(RM9000x2_OCD_INTP0STATUS3);
117                 OCD_WRITE(RM9000x2_OCD_INTP0CLEAR3, status);
118
119                 if (status & 0x2)
120                         smp_call_function_interrupt();
121                 break;
122
123         case 1:
124                 status = OCD_READ(RM9000x2_OCD_INTP1STATUS3);
125                 OCD_WRITE(RM9000x2_OCD_INTP1CLEAR3, status);
126
127                 if (status & 0x2)
128                         smp_call_function_interrupt();
129                 break;
130         }
131 }
132
133 /*
134  * Send inter-processor interrupt
135  */
136 void core_send_ipi(int cpu, unsigned int action)
137 {
138         /*
139          * Generate an INTMSG so that it can be sent over to the
140          * destination CPU. The INTMSG will put the STATUS bits
141          * based on the action desired. An alternative strategy
142          * is to write to the Interrupt Set register, read the
143          * Interrupt Status register and clear the Interrupt
144          * Clear register. The latter is preffered.
145          */
146         switch (action) {
147         case SMP_RESCHEDULE_YOURSELF:
148                 if (cpu == 1)
149                         OCD_WRITE(RM9000x2_OCD_INTP1SET3, 4);
150                 else
151                         OCD_WRITE(RM9000x2_OCD_INTP0SET3, 4);
152                 break;
153
154         case SMP_CALL_FUNCTION:
155                 if (cpu == 1)
156                         OCD_WRITE(RM9000x2_OCD_INTP1SET3, 2);
157                 else
158                         OCD_WRITE(RM9000x2_OCD_INTP0SET3, 2);
159                 break;
160         }
161 }