1/* $NetBSD: cpu.c,v 1.27 2009/03/18 10:22:21 cegger Exp $ */ 2 3/*- 4 * Copyright (c) 2000, 2001 Ben Harris 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29/* 30 * cpu.c - high-level CPU detection etc 31 */ 32 33#include <sys/param.h> 34 35__KERNEL_RCSID(0, "$NetBSD: cpu.c,v 1.27 2009/03/18 10:22:21 cegger Exp $"); 36 37#include <sys/device.h> 38#include <sys/proc.h> 39#include <sys/systm.h> 40#include <sys/time.h> 41#include <uvm/uvm_extern.h> 42#include <arm/armreg.h> 43#include <arm/cpuconf.h> 44#include <arm/undefined.h> 45#include <machine/machdep.h> 46#include <machine/pcb.h> 47 48#include <arch/acorn26/acorn26/cpuvar.h> 49 50static int cpu_match(device_t, cfdata_t, void *); 51static void cpu_attach(device_t, device_t, void *); 52static int cpu_search(device_t, cfdata_t, const int *, void *); 53static register_t cpu_identify(void); 54#ifdef CPU_ARM2 55static int arm2_undef_handler(u_int, u_int, struct trapframe *, int); 56static int swp_handler(u_int, u_int, struct trapframe *, int); 57#endif 58#ifdef CPU_ARM3 59static void cpu_arm3_setup(device_t, int); 60#endif 61static void cpu_delay_calibrate(device_t); 62 63CFATTACH_DECL_NEW(cpu_root, 0, cpu_match, cpu_attach, NULL, NULL); 64 65/* cf_flags bits */ 66#define CFF_NOCACHE 0x00000001 67 68static int 69cpu_match(device_t parent, cfdata_t cf, void *aux) 70{ 71 72 if (curcpu()->ci_dev == NULL) 73 return 1; 74 return 0; 75} 76 77static void 78cpu_attach(device_t parent, device_t self, void *aux) 79{ 80 int supported; 81 82 curcpu()->ci_dev = self; 83 aprint_normal(": "); 84 curcpu()->ci_arm_cpuid = cpu_identify(); 85 cputype = curcpu()->ci_arm_cputype = 86 curcpu()->ci_arm_cpuid & CPU_ID_CPU_MASK; 87 curcpu()->ci_arm_cpurev = 88 curcpu()->ci_arm_cpuid & CPU_ID_REVISION_MASK; 89 90 supported = 0; 91 switch (curcpu()->ci_arm_cputype) { 92 case CPU_ID_ARM2: 93 aprint_normal("ARM2"); 94#ifdef CPU_ARM2 95 supported = 1; 96 install_coproc_handler(CORE_UNKNOWN_HANDLER, 97 arm2_undef_handler); 98#endif 99 break; 100 case CPU_ID_ARM250: 101 aprint_normal("ARM250"); 102#ifdef CPU_ARM250 103 supported = 1; 104#endif 105 break; 106 case CPU_ID_ARM3: 107 aprint_normal("ARM3 (rev. %u)", curcpu()->ci_arm_cpurev); 108#ifdef CPU_ARM3 109 supported = 1; 110 cpu_arm3_setup(self, device_cfdata(self)->cf_flags); 111#endif 112 break; 113 default: 114 aprint_normal("Unknown type, ID=0x%08x", 115 curcpu()->ci_arm_cputype); 116 break; 117 } 118 aprint_normal("\n"); 119 set_cpufuncs(); 120 if (!supported) 121 aprint_error_dev(self, 122 "WARNING: CPU type not supported by kernel\n"); 123 config_interrupts(self, cpu_delay_calibrate); 124 config_search_ia(cpu_search, self, "cpu", NULL); 125} 126 127static int 128cpu_search(device_t parent, cfdata_t cf, const int *ldesc, void *aux) 129{ 130 131 if (config_match(parent, cf, NULL) > 0) 132 config_attach(parent, cf, NULL, NULL); 133 134 return 0; 135} 136 137static label_t undef_jmp; 138 139static int 140cpu_undef_handler(u_int addr, u_int insn, struct trapframe *tf, int fault_code) 141{ 142 143 longjmp(&undef_jmp); 144} 145 146static register_t 147cpu_identify(void) 148{ 149 register_t dummy; 150 volatile register_t id; 151 void *cp_core, *cp15; 152 153 cp_core = install_coproc_handler(CORE_UNKNOWN_HANDLER, 154 cpu_undef_handler); 155 cp15 = install_coproc_handler(SYSTEM_COPROC, cpu_undef_handler); 156 if (setjmp(&undef_jmp) == 0) { 157 id = CPU_ID_ARM2; 158 /* ARM250 and ARM3 support SWP. */ 159 __asm volatile ("swp r0, r0, [%0]" : : "r" (&dummy) : "r0"); 160 id = CPU_ID_ARM250; 161 /* ARM3 has an internal coprocessor 15 with an ID register. */ 162 __asm volatile ("mrc 15, 0, %0, cr0, cr0" : "=r" (id)); 163 } 164 remove_coproc_handler(cp_core); 165 remove_coproc_handler(cp15); 166 return id; 167} 168 169#ifdef CPU_ARM2 170static int 171arm2_undef_handler(u_int addr, u_int insn, struct trapframe *frame, 172 int fault_code) 173{ 174 175 if ((insn & 0x0fb00ff0) == 0x01000090) 176 /* It's a SWP */ 177 return swp_handler(addr, insn, frame, fault_code); 178 /* 179 * Check if the aborted instruction was a SWI (ARM2 bug -- 180 * ARM3 data sheet p87) and call SWI handler if so. 181 */ 182 if ((insn & 0x0f000000) == 0x0f000000) { 183 swi_handler(frame); 184 return 0; 185 } 186 return 1; 187} 188 189/* 190 * In order for the following macro to work, any function using it 191 * must ensure that tf->r15 is copied into getreg(15). This is safe 192 * with the current trapframe layout on acorn26, but be careful. 193 */ 194#define getreg(r) (((register_t *)&tf->tf_r0)[r]) 195 196static int 197swp_handler(u_int addr, u_int insn, struct trapframe *tf, int fault_code) 198{ 199 struct proc *p = curlwp->l_proc; 200 int rd, rm, rn, byte; 201 register_t temp; 202 void *uaddr; 203 int err; 204 205 KASSERT(fault_code & FAULT_USER); 206 rd = (insn & 0x0000f000) >> 12; 207 rm = (insn & 0x0000000f); 208 rn = (insn & 0x000f0000) >> 16; 209 byte = insn & 0x00400000; 210 211 if (rd == 15 || rm == 15 || rn == 15) 212 /* UNPREDICTABLE. Arbitrarily do nothing. */ 213 return 0; 214 uaddr = (void *)getreg(rn); 215 /* We want the page wired so we won't sleep */ 216 /* XXX only wire one byte due to weirdness with unaligned words */ 217 err = uvm_vslock(p->p_vmspace, uaddr, 1, VM_PROT_READ | VM_PROT_WRITE); 218 if (err != 0) { 219 ksiginfo_t ksi; 220 KSI_INIT_TRAP(&ksi); 221 ksi.ksi_signo = SIGSEGV; 222 ksi.ksi_addr = uaddr; 223 ksi.ksi_code = SEGV_MAPERR; 224 trapsignal(curlwp, &ksi); 225 return 0; 226 } 227 /* I believe the uvm_vslock() guarantees the fetch/store won't fail. */ 228 if (byte) { 229 temp = fubyte(uaddr); 230 subyte(uaddr, getreg(rm)); 231 getreg(rd) = temp; 232 } else { 233 /* 234 * XXX Unaligned addresses happen to be handled 235 * appropriately by [fs]uword at present. 236 */ 237 temp = fuword(uaddr); 238 suword(uaddr, getreg(rm)); 239 getreg(rd) = temp; 240 } 241 uvm_vsunlock(p->p_vmspace, uaddr, 1); 242 return 0; 243} 244#endif 245 246#ifdef CPU_ARM3 247 248#define ARM3_READ(reg, var) \ 249 __asm ("mrc 15, 0, %0, cr" __STRING(reg) ", cr0" : "=r" (var)) 250#define ARM3_WRITE(reg, val) \ 251 __asm ("mcr 15, 0, %0, cr" __STRING(reg) ", cr0" : : "r" (val)) 252 253static void 254cpu_arm3_setup(device_t self, int flags) 255{ 256 257 /* Disable the cache while we set things up. */ 258 ARM3_WRITE(ARM3_CP15_CONTROL, ARM3_CTL_SHARED); 259 if (flags & CFF_NOCACHE) { 260 aprint_normal(", cache disabled"); 261 return; 262 } 263 /* All RAM and ROM is cacheable. */ 264 ARM3_WRITE(ARM3_CP15_CACHEABLE, 0xfcffffff); 265 /* All RAM is updateable. */ 266 ARM3_WRITE(ARM3_CP15_UPDATEABLE, 0x00ffffff); 267 /* Nothing is disruptive. We'll do cache flushing manually. */ 268 ARM3_WRITE(ARM3_CP15_DISRUPTIVE, 0x00000000); 269 /* Flush the cache and turn it on. */ 270 ARM3_WRITE(ARM3_CP15_FLUSH, 0); 271 ARM3_WRITE(ARM3_CP15_CONTROL, ARM3_CTL_CACHE_ON | ARM3_CTL_SHARED); 272 aprint_normal(", cache enabled"); 273 cpu_delay_factor = 8; 274} 275#endif 276 277/* XXX This should be inlined. */ 278void 279cpu_cache_flush(void) 280{ 281 282#ifdef CPU_ARM3 283#if defined(CPU_ARM2) || defined(CPU_ARM250) 284 if ((cputype & CPU_ID_CPU_MASK) == CPU_ID_ARM3) 285#endif 286 ARM3_WRITE(ARM3_CP15_FLUSH, 0); 287#endif 288} 289 290int cpu_delay_factor = 1; 291 292static void 293cpu_delay_calibrate(device_t self) 294{ 295 struct timeval startt, end, diff; 296 297 microtime(&startt); 298 cpu_delayloop(10000); 299 microtime(&end); 300 timersub(&end, &startt, &diff); 301 cpu_delay_factor = 10000 / diff.tv_usec + 1; 302 aprint_normal_dev(self, "10000 loops in %d microseconds, " 303 "delay factor = %d\n", 304 diff.tv_usec, cpu_delay_factor); 305} 306