[MIPS] Fixup migration to GENERIC_TIME
[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 #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 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         mips_hpt_init();
103
104         set_c0_status(ST0_CO | ST0_IE | ST0_IM);
105 }
106
107 void prom_smp_finish(void)
108 {
109 }
110
111 asmlinkage void titan_mailbox_irq(void)
112 {
113         int cpu = smp_processor_id();
114         unsigned long status;
115
116         if (cpu == 0) {
117                 status = OCD_READ(RM9000x2_OCD_INTP0STATUS3);
118                 OCD_WRITE(RM9000x2_OCD_INTP0CLEAR3, status);
119         }
120
121         if (cpu == 1) {
122                 status = OCD_READ(RM9000x2_OCD_INTP1STATUS3);
123                 OCD_WRITE(RM9000x2_OCD_INTP1CLEAR3, status);
124         }
125
126         if (status & 0x2)
127                 smp_call_function_interrupt();
128 }
129
130 /*
131  * Send inter-processor interrupt
132  */
133 void core_send_ipi(int cpu, unsigned int action)
134 {
135         /*
136          * Generate an INTMSG so that it can be sent over to the
137          * destination CPU. The INTMSG will put the STATUS bits
138          * based on the action desired. An alternative strategy
139          * is to write to the Interrupt Set register, read the
140          * Interrupt Status register and clear the Interrupt
141          * Clear register. The latter is preffered.
142          */
143         switch (action) {
144         case SMP_RESCHEDULE_YOURSELF:
145                 if (cpu == 1)
146                         OCD_WRITE(RM9000x2_OCD_INTP1SET3, 4);
147                 else
148                         OCD_WRITE(RM9000x2_OCD_INTP0SET3, 4);
149                 break;
150
151         case SMP_CALL_FUNCTION:
152                 if (cpu == 1)
153                         OCD_WRITE(RM9000x2_OCD_INTP1SET3, 2);
154                 else
155                         OCD_WRITE(RM9000x2_OCD_INTP0SET3, 2);
156                 break;
157         }
158 }