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