1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * 4 * Copyright (C) 2004, 05, 06 MIPS Technologies, Inc. 5 * Elizabeth Clarke (beth@mips.com) 6 * Ralf Baechle (ralf@linux-mips.org) 7 * Copyright (C) 2006 Ralf Baechle (ralf@linux-mips.org) 8 */ 9 #include <linux/kernel.h> 10 #include <linux/sched.h> 11 #include <linux/cpumask.h> 12 #include <linux/interrupt.h> 13 #include <linux/compiler.h> 14 #include <linux/sched/task_stack.h> 15 #include <linux/smp.h> 16 17 #include <linux/atomic.h> 18 #include <asm/cacheflush.h> 19 #include <asm/cpu.h> 20 #include <asm/processor.h> 21 #include <asm/hardirq.h> 22 #include <asm/mmu_context.h> 23 #include <asm/time.h> 24 #include <asm/mipsregs.h> 25 #include <asm/mipsmtregs.h> 26 #include <asm/mips_mt.h> 27 #include <asm/mips-cps.h> 28 29 static void __init smvp_copy_vpe_config(void) 30 { 31 write_vpe_c0_status( 32 (read_c0_status() & ~(ST0_IM | ST0_IE | ST0_KSU)) | ST0_CU0); 33 34 /* set config to be the same as vpe0, particularly kseg0 coherency alg */ 35 write_vpe_c0_config( read_c0_config()); 36 37 /* make sure there are no software interrupts pending */ 38 write_vpe_c0_cause(0); 39 40 /* Propagate Config7 */ 41 write_vpe_c0_config7(read_c0_config7()); 42 43 write_vpe_c0_count(read_c0_count()); 44 } 45 46 static unsigned int __init smvp_vpe_init(unsigned int tc, unsigned int mvpconf0, 47 unsigned int ncpu) 48 { 49 if (tc >= smp_max_threads || 50 (tc > ((mvpconf0 & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT))) 51 return ncpu; 52 53 /* Deactivate all but VPE 0 */ 54 if (tc != 0) { 55 unsigned long tmp = read_vpe_c0_vpeconf0(); 56 57 tmp &= ~VPECONF0_VPA; 58 59 /* master VPE */ 60 tmp |= VPECONF0_MVP; 61 write_vpe_c0_vpeconf0(tmp); 62 63 /* Record this as available CPU */ 64 set_cpu_possible(tc, true); 65 set_cpu_present(tc, true); 66 __cpu_number_map[tc] = ++ncpu; 67 __cpu_logical_map[ncpu] = tc; 68 } 69 70 /* Disable multi-threading with TC's */ 71 write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() & ~VPECONTROL_TE); 72 73 if (tc != 0) 74 smvp_copy_vpe_config(); 75 76 cpu_set_vpe_id(&cpu_data[ncpu], tc); 77 78 return ncpu; 79 } 80 81 static void __init smvp_tc_init(unsigned int tc, unsigned int mvpconf0) 82 { 83 unsigned long tmp; 84 85 if (!tc) 86 return; 87 88 /* bind a TC to each VPE, May as well put all excess TC's 89 on the last VPE */ 90 if (tc >= (((mvpconf0 & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT)+1)) 91 write_tc_c0_tcbind(read_tc_c0_tcbind() | ((mvpconf0 & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT)); 92 else { 93 write_tc_c0_tcbind(read_tc_c0_tcbind() | tc); 94 95 /* and set XTC */ 96 write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | (tc << VPECONF0_XTC_SHIFT)); 97 } 98 99 tmp = read_tc_c0_tcstatus(); 100 101 /* mark not allocated and not dynamically allocatable */ 102 tmp &= ~(TCSTATUS_A | TCSTATUS_DA); 103 tmp |= TCSTATUS_IXMT; /* interrupt exempt */ 104 write_tc_c0_tcstatus(tmp); 105 106 write_tc_c0_tchalt(TCHALT_H); 107 } 108 109 static void vsmp_init_secondary(void) 110 { 111 /* This is Malta specific: IPI,performance and timer interrupts */ 112 if (mips_gic_present()) 113 change_c0_status(ST0_IM, STATUSF_IP2 | STATUSF_IP3 | 114 STATUSF_IP4 | STATUSF_IP5 | 115 STATUSF_IP6 | STATUSF_IP7); 116 else 117 change_c0_status(ST0_IM, STATUSF_IP0 | STATUSF_IP1 | 118 STATUSF_IP6 | STATUSF_IP7); 119 } 120 121 static void vsmp_smp_finish(void) 122 { 123 /* CDFIXME: remove this? */ 124 write_c0_compare(read_c0_count() + (8* mips_hpt_frequency/HZ)); 125 126 #ifdef CONFIG_MIPS_MT_FPAFF 127 /* If we have an FPU, enroll ourselves in the FPU-full mask */ 128 if (cpu_has_fpu) 129 cpumask_set_cpu(smp_processor_id(), &mt_fpu_cpumask); 130 #endif /* CONFIG_MIPS_MT_FPAFF */ 131 132 local_irq_enable(); 133 } 134 135 /* 136 * Setup the PC, SP, and GP of a secondary processor and start it 137 * running! 138 * smp_bootstrap is the place to resume from 139 * __KSTK_TOS(idle) is apparently the stack pointer 140 * (unsigned long)idle->thread_info the gp 141 * assumes a 1:1 mapping of TC => VPE 142 */ 143 static int vsmp_boot_secondary(int cpu, struct task_struct *idle) 144 { 145 struct thread_info *gp = task_thread_info(idle); 146 dvpe(); 147 set_c0_mvpcontrol(MVPCONTROL_VPC); 148 149 settc(cpu); 150 151 /* restart */ 152 write_tc_c0_tcrestart((unsigned long)&smp_bootstrap); 153 154 /* enable the tc this vpe/cpu will be running */ 155 write_tc_c0_tcstatus((read_tc_c0_tcstatus() & ~TCSTATUS_IXMT) | TCSTATUS_A); 156 157 write_tc_c0_tchalt(0); 158 159 /* enable the VPE */ 160 write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_VPA); 161 162 /* stack pointer */ 163 write_tc_gpr_sp( __KSTK_TOS(idle)); 164 165 /* global pointer */ 166 write_tc_gpr_gp((unsigned long)gp); 167 168 flush_icache_range((unsigned long)gp, 169 (unsigned long)(gp + sizeof(struct thread_info))); 170 171 /* finally out of configuration and into chaos */ 172 clear_c0_mvpcontrol(MVPCONTROL_VPC); 173 174 evpe(EVPE_ENABLE); 175 176 return 0; 177 } 178 179 /* 180 * Common setup before any secondaries are started 181 * Make sure all CPU's are in a sensible state before we boot any of the 182 * secondaries 183 */ 184 static void __init vsmp_smp_setup(void) 185 { 186 unsigned int mvpconf0, ntc, tc, ncpu = 0; 187 unsigned int nvpe; 188 189 #ifdef CONFIG_MIPS_MT_FPAFF 190 /* If we have an FPU, enroll ourselves in the FPU-full mask */ 191 if (cpu_has_fpu) 192 cpumask_set_cpu(0, &mt_fpu_cpumask); 193 #endif /* CONFIG_MIPS_MT_FPAFF */ 194 if (!cpu_has_mipsmt) 195 return; 196 197 /* disable MT so we can configure */ 198 dvpe(); 199 dmt(); 200 201 /* Put MVPE's into 'configuration state' */ 202 set_c0_mvpcontrol(MVPCONTROL_VPC); 203 204 mvpconf0 = read_c0_mvpconf0(); 205 ntc = (mvpconf0 & MVPCONF0_PTC) >> MVPCONF0_PTC_SHIFT; 206 207 nvpe = ((mvpconf0 & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1; 208 smp_num_siblings = nvpe; 209 210 /* we'll always have more TC's than VPE's, so loop setting everything 211 to a sensible state */ 212 for (tc = 0; tc <= ntc; tc++) { 213 settc(tc); 214 215 smvp_tc_init(tc, mvpconf0); 216 ncpu = smvp_vpe_init(tc, mvpconf0, ncpu); 217 } 218 219 /* Release config state */ 220 clear_c0_mvpcontrol(MVPCONTROL_VPC); 221 222 /* We'll wait until starting the secondaries before starting MVPE */ 223 224 printk(KERN_INFO "Detected %i available secondary CPU(s)\n", ncpu); 225 } 226 227 static void __init vsmp_prepare_cpus(unsigned int max_cpus) 228 { 229 mips_mt_set_cpuoptions(); 230 } 231 232 const struct plat_smp_ops vsmp_smp_ops = { 233 .send_ipi_single = mips_smp_send_ipi_single, 234 .send_ipi_mask = mips_smp_send_ipi_mask, 235 .init_secondary = vsmp_init_secondary, 236 .smp_finish = vsmp_smp_finish, 237 .boot_secondary = vsmp_boot_secondary, 238 .smp_setup = vsmp_smp_setup, 239 .prepare_cpus = vsmp_prepare_cpus, 240 }; 241 242
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.