1/* 2 * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29#include <string.h> 30#include <mach/vm_param.h> 31#include <mach/vm_prot.h> 32#include <mach/machine.h> 33#include <mach/time_value.h> 34#include <kern/spl.h> 35#include <kern/assert.h> 36#include <kern/debug.h> 37#include <kern/misc_protos.h> 38#include <kern/startup.h> 39#include <kern/clock.h> 40#include <kern/cpu_data.h> 41#include <kern/processor.h> 42#include <vm/vm_page.h> 43#include <vm/pmap.h> 44#include <vm/vm_kern.h> 45#include <i386/cpuid.h> 46#include <i386/machine_cpu.h> 47#include <i386/mp.h> 48#include <i386/machine_routines.h> 49#include <i386/pmap.h> 50#include <i386/misc_protos.h> 51#include <i386/io_map_entries.h> 52#include <architecture/i386/pio.h> 53#include <i386/cpuid.h> 54#include <i386/apic.h> 55#include <i386/tsc.h> 56#include <i386/hpet.h> 57#include <i386/pmCPU.h> 58#include <i386/cpu_topology.h> 59#include <i386/cpu_threads.h> 60#include <pexpert/device_tree.h> 61 62/* Decimal powers: */ 63#define kilo (1000ULL) 64#define Mega (kilo * kilo) 65#define Giga (kilo * Mega) 66#define Tera (kilo * Giga) 67#define Peta (kilo * Tera) 68 69vm_offset_t hpetArea = 0; 70uint32_t hpetAreap = 0; 71uint64_t hpetFemto = 0; 72uint64_t hpetFreq = 0; 73uint64_t hpetCvt = 0; /* (TAKE OUT LATER) */ 74uint64_t hpetCvtt2n = 0; 75uint64_t hpetCvtn2t = 0; 76uint64_t tsc2hpet = 0; 77uint64_t hpet2tsc = 0; 78uint64_t bus2hpet = 0; 79uint64_t hpet2bus = 0; 80 81vm_offset_t rcbaArea = 0; 82uint32_t rcbaAreap = 0; 83 84static int (*hpet_req)(uint32_t apicid, void *arg, hpetRequest_t *hpet) = NULL; 85static void *hpet_arg = NULL; 86 87#if DEBUG 88#define DBG(x...) kprintf("DBG: " x) 89#else 90#define DBG(x...) 91#endif 92 93int 94hpet_register_callback(int (*hpet_reqst)(uint32_t apicid, 95 void *arg, 96 hpetRequest_t *hpet), 97 void *arg) 98{ 99 hpet_req = hpet_reqst; 100 hpet_arg = arg; 101 return(0); 102} 103 104/* 105 * This routine is called to obtain an HPET and have it assigned 106 * to a CPU. It returns 0 if successful and non-zero if one could 107 * not be assigned. 108 */ 109int 110hpet_request(uint32_t cpu) 111{ 112 hpetRequest_t hpetReq; 113 int rc; 114 x86_lcpu_t *lcpu; 115 x86_core_t *core; 116 x86_pkg_t *pkg; 117 boolean_t enabled; 118 119 if (hpet_req == NULL) { 120 return(-1); 121 } 122 123 /* 124 * Deal with the case where the CPU # passed in is past the 125 * value specified in cpus=n in boot-args. 126 */ 127 if (cpu >= real_ncpus) { 128 enabled = ml_set_interrupts_enabled(FALSE); 129 lcpu = cpu_to_lcpu(cpu); 130 if (lcpu != NULL) { 131 core = lcpu->core; 132 pkg = core->package; 133 134 if (lcpu->primary) { 135 pkg->flags |= X86PKG_FL_HAS_HPET; 136 } 137 } 138 139 ml_set_interrupts_enabled(enabled); 140 return(0); 141 } 142 143 rc = (*hpet_req)(ml_get_apicid(cpu), hpet_arg, &hpetReq); 144 if (rc != 0) { 145 return(rc); 146 } 147 148 enabled = ml_set_interrupts_enabled(FALSE); 149 lcpu = cpu_to_lcpu(cpu); 150 core = lcpu->core; 151 pkg = core->package; 152 153 /* 154 * Compute the address of the HPET. 155 */ 156 core->Hpet = (hpetTimer_t *)((uint8_t *)hpetArea + hpetReq.hpetOffset); 157 core->HpetVec = hpetReq.hpetVector; 158 159 /* 160 * Enable interrupts 161 */ 162 core->Hpet->Config |= Tn_INT_ENB_CNF; 163 164 /* 165 * Save the configuration 166 */ 167 core->HpetCfg = core->Hpet->Config; 168 core->HpetCmp = 0; 169 170 /* 171 * If the CPU is the "primary" for the package, then 172 * add the HPET to the package too. 173 */ 174 if (lcpu->primary) { 175 pkg->Hpet = core->Hpet; 176 pkg->HpetCfg = core->HpetCfg; 177 pkg->HpetCmp = core->HpetCmp; 178 pkg->flags |= X86PKG_FL_HAS_HPET; 179 } 180 181 ml_set_interrupts_enabled(enabled); 182 183 return(0); 184} 185 186/* 187 * Map the RCBA area. 188 */ 189static void 190map_rcbaArea(void) 191{ 192 /* 193 * Get RCBA area physical address and map it 194 */ 195 outl(cfgAdr, lpcCfg | (0xF0 & 0xFC)); 196 rcbaAreap = inl(cfgDat | (0xF0 & 0x03)); 197 rcbaArea = io_map_spec(rcbaAreap & -4096, PAGE_SIZE * 4, VM_WIMG_IO); 198 kprintf("RCBA: vaddr = %lX, paddr = %08X\n", (unsigned long)rcbaArea, rcbaAreap); 199} 200 201/* 202 * Initialize the HPET 203 */ 204void 205hpet_init(void) 206{ 207 unsigned int *xmod; 208 209 map_rcbaArea(); 210 211 /* 212 * Is the HPET memory already enabled? 213 * If not, set address and enable. 214 */ 215 xmod = (uint32_t *)(rcbaArea + 0x3404); /* Point to the HPTC */ 216 uint32_t hptc = *xmod; /* Get HPET config */ 217 DBG(" current RCBA.HPTC: %08X\n", *xmod); 218 if(!(hptc & hptcAE)) { 219 DBG("HPET memory is not enabled, " 220 "enabling and assigning to 0xFED00000 (hope that's ok)\n"); 221 *xmod = (hptc & ~3) | hptcAE; 222 } 223 224 /* 225 * Get physical address of HPET and map it. 226 */ 227 hpetAreap = hpetAddr | ((hptc & 3) << 12); 228 hpetArea = io_map_spec(hpetAreap & -4096, PAGE_SIZE * 4, VM_WIMG_IO); 229 kprintf("HPET: vaddr = %lX, paddr = %08X\n", (unsigned long)hpetArea, hpetAreap); 230 231 /* 232 * Extract the HPET tick rate. 233 * The period of the HPET is reported in femtoseconds (10**-15s) 234 * and convert to frequency in hertz. 235 */ 236 hpetFemto = (uint32_t)(((hpetReg_t *)hpetArea)->GCAP_ID >> 32); 237 hpetFreq = (1 * Peta) / hpetFemto; 238 239 /* 240 * The conversion factor is the number of nanoseconds per HPET tick 241 * with about 32 bits of fraction. The value is converted to a 242 * base-2 fixed point number. To convert from HPET to nanoseconds, 243 * multiply the value by the conversion factor using 96-bit arithmetic, 244 * then shift right 32 bits. If the value is known to be small, 245 * 64-bit arithmetic will work. 246 */ 247 248 /* 249 * Begin conversion of base 10 femtoseconds to base 2, calculate: 250 * - HPET ticks to nanoseconds conversion in base 2 fraction (* 2**32) 251 * - nanoseconds to HPET ticks conversion 252 */ 253 hpetCvtt2n = (uint64_t)hpetFemto << 32; 254 hpetCvtt2n = hpetCvtt2n / 1000000ULL; 255 hpetCvtn2t = 0xFFFFFFFFFFFFFFFFULL / hpetCvtt2n; 256 kprintf("HPET: Frequency = %6d.%04dMHz, " 257 "cvtt2n = %08X.%08X, cvtn2t = %08X.%08X\n", 258 (uint32_t)(hpetFreq / Mega), (uint32_t)(hpetFreq % Mega), 259 (uint32_t)(hpetCvtt2n >> 32), (uint32_t)hpetCvtt2n, 260 (uint32_t)(hpetCvtn2t >> 32), (uint32_t)hpetCvtn2t); 261 262 263 /* (TAKE OUT LATER) 264 * Begin conversion of base 10 femtoseconds to base 2 265 * HPET ticks to nanoseconds in base 2 fraction (times 1048576) 266 */ 267 hpetCvt = (uint64_t)hpetFemto << 20; 268 hpetCvt = hpetCvt / 1000000ULL; 269 270 /* Calculate conversion from TSC to HPET */ 271 tsc2hpet = tmrCvt(tscFCvtt2n, hpetCvtn2t); 272 DBG(" CVT: TSC to HPET = %08X.%08X\n", 273 (uint32_t)(tsc2hpet >> 32), (uint32_t)tsc2hpet); 274 275 /* Calculate conversion from HPET to TSC */ 276 hpet2tsc = tmrCvt(hpetCvtt2n, tscFCvtn2t); 277 DBG(" CVT: HPET to TSC = %08X.%08X\n", 278 (uint32_t)(hpet2tsc >> 32), (uint32_t)hpet2tsc); 279 280 /* Calculate conversion from BUS to HPET */ 281 bus2hpet = tmrCvt(busFCvtt2n, hpetCvtn2t); 282 DBG(" CVT: BUS to HPET = %08X.%08X\n", 283 (uint32_t)(bus2hpet >> 32), (uint32_t)bus2hpet); 284 285 /* Calculate conversion from HPET to BUS */ 286 hpet2bus = tmrCvt(hpetCvtt2n, busFCvtn2t); 287 DBG(" CVT: HPET to BUS = %08X.%08X\n", 288 (uint32_t)(hpet2bus >> 32), (uint32_t)hpet2bus); 289} 290 291/* 292 * This routine is used to get various information about the HPET 293 * without having to export gobs of globals. It fills in a data 294 * structure with the info. 295 */ 296void 297hpet_get_info(hpetInfo_t *info) 298{ 299 info->hpetCvtt2n = hpetCvtt2n; 300 info->hpetCvtn2t = hpetCvtn2t; 301 info->tsc2hpet = tsc2hpet; 302 info->hpet2tsc = hpet2tsc; 303 info->bus2hpet = bus2hpet; 304 info->hpet2bus = hpet2bus; 305 /* 306 * XXX 307 * We're repurposing the rcbaArea so we can use the HPET. 308 * Eventually we'll rename this correctly. 309 */ 310 info->rcbaArea = hpetArea; 311 info->rcbaAreap = hpetAreap; 312} 313 314 315/* 316 * This routine is called by the HPET driver 317 * when it assigns an HPET timer to a processor. 318 * 319 * XXX with the new callback into the HPET driver, 320 * this routine will be deprecated. 321 */ 322void 323ml_hpet_cfg(uint32_t cpu, uint32_t hpetVect) 324{ 325 uint64_t *hpetVaddr; 326 hpetTimer_t *hpet; 327 x86_lcpu_t *lcpu; 328 x86_core_t *core; 329 x86_pkg_t *pkg; 330 boolean_t enabled; 331 332 if(cpu > 1) { 333 panic("ml_hpet_cfg: invalid cpu = %d\n", cpu); 334 } 335 336 lcpu = cpu_to_lcpu(cpu); 337 core = lcpu->core; 338 pkg = core->package; 339 340 /* 341 * Only deal with the primary CPU for the package. 342 */ 343 if (!lcpu->primary) 344 return; 345 346 enabled = ml_set_interrupts_enabled(FALSE); 347 348 /* Calculate address of the HPET for this processor */ 349 hpetVaddr = (uint64_t *)(((uintptr_t)&(((hpetReg_t *)hpetArea)->TIM1_CONF)) + (cpu << 5)); 350 hpet = (hpetTimer_t *)hpetVaddr; 351 352 DBG("ml_hpet_cfg: HPET for cpu %d at %p, vector = %d\n", 353 cpu, hpetVaddr, hpetVect); 354 355 /* Save the address and vector of the HPET for this processor */ 356 core->Hpet = hpet; 357 core->HpetVec = hpetVect; 358 359 /* 360 * Enable interrupts 361 */ 362 core->Hpet->Config |= Tn_INT_ENB_CNF; 363 364 /* Save the configuration */ 365 core->HpetCfg = core->Hpet->Config; 366 core->HpetCmp = 0; 367 368 /* 369 * We're only doing this for the primary CPU, so go 370 * ahead and add the HPET to the package too. 371 */ 372 pkg->Hpet = core->Hpet; 373 pkg->HpetVec = core->HpetVec; 374 pkg->HpetCfg = core->HpetCfg; 375 pkg->HpetCmp = core->HpetCmp; 376 pkg->flags |= X86PKG_FL_HAS_HPET; 377 378 ml_set_interrupts_enabled(enabled); 379} 380 381/* 382 * This is the HPET interrupt handler. 383 * 384 * It just hands off to the power management code so that the 385 * appropriate things get done there. 386 */ 387int 388HPETInterrupt(void) 389{ 390 391 /* All we do here is to bump the count */ 392 x86_package()->HpetInt++; 393 394 /* 395 * Let power management do it's thing. 396 */ 397 pmHPETInterrupt(); 398 399 /* Return and show that the 'rupt has been handled... */ 400 return 1; 401} 402 403 404static hpetReg_t saved_hpet; 405 406void 407hpet_save(void) 408{ 409 hpetReg_t *from = (hpetReg_t *) hpetArea; 410 hpetReg_t *to = &saved_hpet; 411 412 to->GEN_CONF = from->GEN_CONF; 413 to->TIM0_CONF = from->TIM0_CONF; 414 to->TIM0_COMP = from->TIM0_COMP; 415 to->TIM1_CONF = from->TIM1_CONF; 416 to->TIM1_COMP = from->TIM1_COMP; 417 to->TIM2_CONF = from->TIM2_CONF; 418 to->TIM2_COMP = from->TIM2_COMP; 419 to->MAIN_CNT = from->MAIN_CNT; 420} 421 422void 423hpet_restore(void) 424{ 425 hpetReg_t *from = &saved_hpet; 426 hpetReg_t *to = (hpetReg_t *) hpetArea; 427 428 /* 429 * Is the HPET memory already enabled? 430 * If not, set address and enable. 431 */ 432 uint32_t *hptcp = (uint32_t *)(rcbaArea + 0x3404); 433 uint32_t hptc = *hptcp; 434 if(!(hptc & hptcAE)) { 435 DBG("HPET memory is not enabled, " 436 "enabling and assigning to 0xFED00000 (hope that's ok)\n"); 437 *hptcp = (hptc & ~3) | hptcAE; 438 } 439 440 to->GEN_CONF = from->GEN_CONF & ~1; 441 442 to->TIM0_CONF = from->TIM0_CONF; 443 to->TIM0_COMP = from->TIM0_COMP; 444 to->TIM1_CONF = from->TIM1_CONF; 445 to->TIM1_COMP = from->TIM1_COMP; 446 to->TIM2_CONF = from->TIM2_CONF; 447 to->TIM2_COMP = from->TIM2_COMP; 448 to->GINTR_STA = -1ULL; 449 to->MAIN_CNT = from->MAIN_CNT; 450 451 to->GEN_CONF = from->GEN_CONF; 452} 453 454/* 455 * Read the HPET timer 456 * 457 */ 458uint64_t 459rdHPET(void) 460{ 461 hpetReg_t *hpetp = (hpetReg_t *) hpetArea; 462 volatile uint32_t *regp = (uint32_t *) &hpetp->MAIN_CNT; 463 uint32_t high; 464 uint32_t low; 465 466 do { 467 high = *(regp + 1); 468 low = *regp; 469 } while (high != *(regp + 1)); 470 471 return (((uint64_t) high) << 32) | low; 472} 473