1/* 2 * This program is free software; you can distribute it and/or modify it 3 * under the terms of the GNU General Public License (Version 2) as 4 * published by the Free Software Foundation. 5 * 6 * This program is distributed in the hope it will be useful, but WITHOUT 7 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 8 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 9 * for more details. 10 * 11 * You should have received a copy of the GNU General Public License along 12 * with this program; if not, write to the Free Software Foundation, Inc., 13 * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. 14 * 15 * Copyright (C) 2004, 05, 06 MIPS Technologies, Inc. 16 * Elizabeth Clarke (beth@mips.com) 17 * Ralf Baechle (ralf@linux-mips.org) 18 * Copyright (C) 2006 Ralf Baechle (ralf@linux-mips.org) 19 */ 20#include <linux/kernel.h> 21#include <linux/sched.h> 22#include <linux/cpumask.h> 23#include <linux/interrupt.h> 24#include <linux/compiler.h> 25#include <linux/smp.h> 26 27#include <asm/atomic.h> 28#include <asm/cacheflush.h> 29#include <asm/cpu.h> 30#include <asm/processor.h> 31#include <asm/system.h> 32#include <asm/hardirq.h> 33#include <asm/mmu_context.h> 34#include <asm/time.h> 35#include <asm/mipsregs.h> 36#include <asm/mipsmtregs.h> 37#include <asm/mips_mt.h> 38#include <asm/gic.h> 39 40extern int gic_present; 41static void __init smvp_copy_vpe_config(void) 42{ 43 write_vpe_c0_status( 44 (read_c0_status() & ~(ST0_IM | ST0_IE | ST0_KSU)) | ST0_CU0); 45 46 /* set config to be the same as vpe0, particularly kseg0 coherency alg */ 47 write_vpe_c0_config( read_c0_config()); 48 49 /* make sure there are no software interrupts pending */ 50 write_vpe_c0_cause(0); 51 52 /* Propagate Config7 */ 53 write_vpe_c0_config7(read_c0_config7()); 54 55 write_vpe_c0_count(read_c0_count()); 56} 57 58static unsigned int __init smvp_vpe_init(unsigned int tc, unsigned int mvpconf0, 59 unsigned int ncpu) 60{ 61 if (tc > ((mvpconf0 & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT)) 62 return ncpu; 63 64 /* Deactivate all but VPE 0 */ 65 if (tc != 0) { 66 unsigned long tmp = read_vpe_c0_vpeconf0(); 67 68 tmp &= ~VPECONF0_VPA; 69 70 /* master VPE */ 71 tmp |= VPECONF0_MVP; 72 write_vpe_c0_vpeconf0(tmp); 73 74 /* Record this as available CPU */ 75 set_cpu_possible(tc, true); 76 __cpu_number_map[tc] = ++ncpu; 77 __cpu_logical_map[ncpu] = tc; 78 } 79 80 /* Disable multi-threading with TC's */ 81 write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() & ~VPECONTROL_TE); 82 83 if (tc != 0) 84 smvp_copy_vpe_config(); 85 86 return ncpu; 87} 88 89static void __init smvp_tc_init(unsigned int tc, unsigned int mvpconf0) 90{ 91 unsigned long tmp; 92 93 if (!tc) 94 return; 95 96 /* bind a TC to each VPE, May as well put all excess TC's 97 on the last VPE */ 98 if (tc >= (((mvpconf0 & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT)+1)) 99 write_tc_c0_tcbind(read_tc_c0_tcbind() | ((mvpconf0 & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT)); 100 else { 101 write_tc_c0_tcbind(read_tc_c0_tcbind() | tc); 102 103 /* and set XTC */ 104 write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | (tc << VPECONF0_XTC_SHIFT)); 105 } 106 107 tmp = read_tc_c0_tcstatus(); 108 109 /* mark not allocated and not dynamically allocatable */ 110 tmp &= ~(TCSTATUS_A | TCSTATUS_DA); 111 tmp |= TCSTATUS_IXMT; /* interrupt exempt */ 112 write_tc_c0_tcstatus(tmp); 113 114 write_tc_c0_tchalt(TCHALT_H); 115} 116 117static void mp_send_ipi_single(int cpu, unsigned int action) 118{ 119 unsigned long flags; 120 121 local_irq_save(flags); 122 123 switch (action) { 124 case SMP_CALL_FUNCTION: 125 gic_send_ipi(plat_ipi_call_int_xlate(cpu)); 126 break; 127 128 case SMP_RESCHEDULE_YOURSELF: 129 gic_send_ipi(plat_ipi_resched_int_xlate(cpu)); 130 break; 131 } 132 133 local_irq_restore(flags); 134} 135 136static void vsmp_send_ipi_single(int cpu, unsigned int action) 137{ 138 int i; 139 unsigned long flags; 140 int vpflags; 141 142 if (gic_present) { 143 mp_send_ipi_single(cpu, action); 144 return; 145 } 146 local_irq_save(flags); 147 148 vpflags = dvpe(); /* cant access the other CPU's registers whilst MVPE enabled */ 149 150 switch (action) { 151 case SMP_CALL_FUNCTION: 152 i = C_SW1; 153 break; 154 155 case SMP_RESCHEDULE_YOURSELF: 156 default: 157 i = C_SW0; 158 break; 159 } 160 161 /* 1:1 mapping of vpe and tc... */ 162 settc(cpu); 163 write_vpe_c0_cause(read_vpe_c0_cause() | i); 164 evpe(vpflags); 165 166 local_irq_restore(flags); 167} 168 169static void vsmp_send_ipi_mask(const struct cpumask *mask, unsigned int action) 170{ 171 unsigned int i; 172 173 for_each_cpu(i, mask) 174 vsmp_send_ipi_single(i, action); 175} 176 177static void __cpuinit vsmp_init_secondary(void) 178{ 179 pr_debug("SMPMT: CPU%d: vsmp_init_secondary\n", smp_processor_id()); 180 /* This is Malta specific: IPI,performance and timer inetrrupts */ 181 if (gic_present) 182 change_c0_status(ST0_IM, STATUSF_IP3 | STATUSF_IP4 | 183 STATUSF_IP6 | STATUSF_IP7); 184 else 185 change_c0_status(ST0_IM, STATUSF_IP0 | STATUSF_IP1 | 186 STATUSF_IP6 | STATUSF_IP7); 187} 188 189static void __cpuinit vsmp_smp_finish(void) 190{ 191 pr_debug("SMPMT: CPU%d: vsmp_smp_finish\n", smp_processor_id()); 192 193 /* CDFIXME: remove this? */ 194 write_c0_compare(read_c0_count() + (8* mips_hpt_frequency/HZ)); 195 196#ifdef CONFIG_MIPS_MT_FPAFF 197 /* If we have an FPU, enroll ourselves in the FPU-full mask */ 198 if (cpu_has_fpu) 199 cpu_set(smp_processor_id(), mt_fpu_cpumask); 200#endif /* CONFIG_MIPS_MT_FPAFF */ 201 202 local_irq_enable(); 203} 204 205static void vsmp_cpus_done(void) 206{ 207 pr_debug("SMPMT: CPU%d: vsmp_cpus_done\n", smp_processor_id()); 208} 209 210/* 211 * Setup the PC, SP, and GP of a secondary processor and start it 212 * running! 213 * smp_bootstrap is the place to resume from 214 * __KSTK_TOS(idle) is apparently the stack pointer 215 * (unsigned long)idle->thread_info the gp 216 * assumes a 1:1 mapping of TC => VPE 217 */ 218static void __cpuinit vsmp_boot_secondary(int cpu, struct task_struct *idle) 219{ 220 struct thread_info *gp = task_thread_info(idle); 221 pr_debug("SMPMT: CPU%d: vsmp_boot_secondary cpu %d\n", 222 smp_processor_id(), cpu); 223 dvpe(); 224 set_c0_mvpcontrol(MVPCONTROL_VPC); 225 226 settc(cpu); 227 228 /* restart */ 229 write_tc_c0_tcrestart((unsigned long)&smp_bootstrap); 230 231 /* enable the tc this vpe/cpu will be running */ 232 write_tc_c0_tcstatus((read_tc_c0_tcstatus() & ~TCSTATUS_IXMT) | TCSTATUS_A); 233 234 write_tc_c0_tchalt(0); 235 236 /* enable the VPE */ 237 write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_VPA); 238 239 /* stack pointer */ 240 write_tc_gpr_sp( __KSTK_TOS(idle)); 241 242 /* global pointer */ 243 write_tc_gpr_gp((unsigned long)gp); 244 245 flush_icache_range((unsigned long)gp, 246 (unsigned long)(gp + sizeof(struct thread_info))); 247 248 /* finally out of configuration and into chaos */ 249 clear_c0_mvpcontrol(MVPCONTROL_VPC); 250 251 evpe(EVPE_ENABLE); 252} 253 254/* 255 * Common setup before any secondaries are started 256 * Make sure all CPU's are in a sensible state before we boot any of the 257 * secondaries 258 */ 259static void __init vsmp_smp_setup(void) 260{ 261 unsigned int mvpconf0, ntc, tc, ncpu = 0; 262 unsigned int nvpe; 263 264 pr_debug("SMPMT: CPU%d: vsmp_smp_setup\n", smp_processor_id()); 265#ifdef CONFIG_MIPS_MT_FPAFF 266 /* If we have an FPU, enroll ourselves in the FPU-full mask */ 267 if (cpu_has_fpu) 268 cpu_set(0, mt_fpu_cpumask); 269#endif /* CONFIG_MIPS_MT_FPAFF */ 270 if (!cpu_has_mipsmt) 271 return; 272 273 /* disable MT so we can configure */ 274 dvpe(); 275 dmt(); 276 277 /* Put MVPE's into 'configuration state' */ 278 set_c0_mvpcontrol(MVPCONTROL_VPC); 279 280 mvpconf0 = read_c0_mvpconf0(); 281 ntc = (mvpconf0 & MVPCONF0_PTC) >> MVPCONF0_PTC_SHIFT; 282 283 nvpe = ((mvpconf0 & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1; 284 smp_num_siblings = nvpe; 285 286 /* we'll always have more TC's than VPE's, so loop setting everything 287 to a sensible state */ 288 for (tc = 0; tc <= ntc; tc++) { 289 settc(tc); 290 291 smvp_tc_init(tc, mvpconf0); 292 ncpu = smvp_vpe_init(tc, mvpconf0, ncpu); 293 } 294 295 /* Release config state */ 296 clear_c0_mvpcontrol(MVPCONTROL_VPC); 297 298 /* We'll wait until starting the secondaries before starting MVPE */ 299 300 printk(KERN_INFO "Detected %i available secondary CPU(s)\n", ncpu); 301} 302 303static void __init vsmp_prepare_cpus(unsigned int max_cpus) 304{ 305 pr_debug("SMPMT: CPU%d: vsmp_prepare_cpus %d\n", 306 smp_processor_id(), max_cpus); 307 mips_mt_set_cpuoptions(); 308} 309 310struct plat_smp_ops vsmp_smp_ops = { 311 .send_ipi_single = vsmp_send_ipi_single, 312 .send_ipi_mask = vsmp_send_ipi_mask, 313 .init_secondary = vsmp_init_secondary, 314 .smp_finish = vsmp_smp_finish, 315 .cpus_done = vsmp_cpus_done, 316 .boot_secondary = vsmp_boot_secondary, 317 .smp_setup = vsmp_smp_setup, 318 .prepare_cpus = vsmp_prepare_cpus, 319}; 320