X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/5d5c5d0d5b79ade9a973d55186ffda2638ba2b6e..f427ee49d309d8fc33ebf3042c3a775f2f530ded:/osfmk/i386/hpet.c diff --git a/osfmk/i386/hpet.c b/osfmk/i386/hpet.c index bef43d064..8b9f03b2e 100644 --- a/osfmk/i386/hpet.c +++ b/osfmk/i386/hpet.c @@ -1,31 +1,29 @@ /* * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_OSREFERENCE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. The rights granted to you under the - * License may not be used to create, or enable the creation or - * redistribution of, unlawful or unlicensed copies of an Apple operating - * system, or to circumvent, violate, or enable the circumvention or - * violation of, any terms of an Apple operating system software license - * agreement. + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * - * Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and * limitations under the License. * - * @APPLE_LICENSE_OSREFERENCE_HEADER_END@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ #include @@ -44,12 +42,12 @@ #include #include #include -#include -#include #include -#include #include +#include #include +#include +#include #include #include #include @@ -57,17 +55,9 @@ #include #include #include +#include +#include #include -#if MACH_KDB -#include -#include -#include -#include -#include -#include -#include -#endif /* MACH_KDB */ -#include /* Decimal powers: */ #define kilo (1000ULL) @@ -76,11 +66,11 @@ #define Tera (kilo * Giga) #define Peta (kilo * Tera) -uint32_t hpetArea = 0; -uint32_t hpetAreap = 0; +vm_offset_t hpetArea = 0; +uint32_t hpetAreap = 0; uint64_t hpetFemto = 0; uint64_t hpetFreq = 0; -uint64_t hpetCvt = 0; /* (TAKE OUT LATER) */ +uint64_t hpetCvt = 0; /* (TAKE OUT LATER) */ uint64_t hpetCvtt2n = 0; uint64_t hpetCvtn2t = 0; uint64_t tsc2hpet = 0; @@ -88,15 +78,111 @@ uint64_t hpet2tsc = 0; uint64_t bus2hpet = 0; uint64_t hpet2bus = 0; -uint32_t rcbaArea = 0; -uint32_t rcbaAreap = 0; +vm_offset_t rcbaArea = 0; +uint32_t rcbaAreap = 0; + +static int (*hpet_req)(uint32_t apicid, void *arg, hpetRequest_t *hpet) = NULL; +static void *hpet_arg = NULL; #if DEBUG -#define DBG(x...) kprintf("DBG: " x) +#define DBG(x...) kprintf("DBG: " x) #else #define DBG(x...) #endif +int +hpet_register_callback(int (*hpet_reqst)(uint32_t apicid, + void *arg, + hpetRequest_t *hpet), + void *arg) +{ + hpet_req = hpet_reqst; + hpet_arg = arg; + return 0; +} + +/* + * This routine is called to obtain an HPET and have it assigned + * to a CPU. It returns 0 if successful and non-zero if one could + * not be assigned. + */ +int +hpet_request(uint32_t cpu) +{ + hpetRequest_t hpetReq; + int rc; + x86_lcpu_t *lcpu; + x86_core_t *core; + x86_pkg_t *pkg; + boolean_t enabled; + + if (hpet_req == NULL) { + return -1; + } + + /* + * Deal with the case where the CPU # passed in is past the + * value specified in cpus=n in boot-args. + */ + if (cpu >= real_ncpus) { + enabled = ml_set_interrupts_enabled(FALSE); + lcpu = cpu_to_lcpu(cpu); + if (lcpu != NULL) { + core = lcpu->core; + pkg = core->package; + + if (lcpu->primary) { + pkg->flags |= X86PKG_FL_HAS_HPET; + } + } + + ml_set_interrupts_enabled(enabled); + return 0; + } + + rc = (*hpet_req)(ml_get_apicid(cpu), hpet_arg, &hpetReq); + if (rc != 0) { + return rc; + } + + enabled = ml_set_interrupts_enabled(FALSE); + lcpu = cpu_to_lcpu(cpu); + core = lcpu->core; + pkg = core->package; + + /* + * Compute the address of the HPET. + */ + core->Hpet = (hpetTimer_t *)((uint8_t *)hpetArea + hpetReq.hpetOffset); + core->HpetVec = hpetReq.hpetVector; + + /* + * Enable interrupts + */ + core->Hpet->Config |= Tn_INT_ENB_CNF; + + /* + * Save the configuration + */ + core->HpetCfg = core->Hpet->Config; + core->HpetCmp = 0; + + /* + * If the CPU is the "primary" for the package, then + * add the HPET to the package too. + */ + if (lcpu->primary) { + pkg->Hpet = core->Hpet; + pkg->HpetCfg = core->HpetCfg; + pkg->HpetCmp = core->HpetCmp; + pkg->flags |= X86PKG_FL_HAS_HPET; + } + + ml_set_interrupts_enabled(enabled); + + return 0; +} + /* * Map the RCBA area. */ @@ -109,7 +195,7 @@ map_rcbaArea(void) outl(cfgAdr, lpcCfg | (0xF0 & 0xFC)); rcbaAreap = inl(cfgDat | (0xF0 & 0x03)); rcbaArea = io_map_spec(rcbaAreap & -4096, PAGE_SIZE * 4, VM_WIMG_IO); - kprintf("RCBA: vaddr = %08X, paddr = %08X\n", rcbaArea, rcbaAreap); + kprintf("RCBA: vaddr = %lX, paddr = %08X\n", (unsigned long)rcbaArea, rcbaAreap); } /* @@ -118,7 +204,7 @@ map_rcbaArea(void) void hpet_init(void) { - unsigned int *xmod; + unsigned int *xmod; map_rcbaArea(); @@ -126,10 +212,10 @@ hpet_init(void) * Is the HPET memory already enabled? * If not, set address and enable. */ - xmod = (uint32_t *)(rcbaArea + 0x3404); /* Point to the HPTC */ - uint32_t hptc = *xmod; /* Get HPET config */ + xmod = (uint32_t *)(rcbaArea + 0x3404); /* Point to the HPTC */ + uint32_t hptc = *xmod; /* Get HPET config */ DBG(" current RCBA.HPTC: %08X\n", *xmod); - if(!(hptc & hptcAE)) { + if (!(hptc & hptcAE)) { DBG("HPET memory is not enabled, " "enabling and assigning to 0xFED00000 (hope that's ok)\n"); *xmod = (hptc & ~3) | hptcAE; @@ -140,7 +226,7 @@ hpet_init(void) */ hpetAreap = hpetAddr | ((hptc & 3) << 12); hpetArea = io_map_spec(hpetAreap & -4096, PAGE_SIZE * 4, VM_WIMG_IO); - kprintf("HPET: vaddr = %08X, paddr = %08X\n", hpetArea, hpetAreap); + kprintf("HPET: vaddr = %lX, paddr = %08X\n", (unsigned long)hpetArea, hpetAreap); /* * Extract the HPET tick rate. @@ -168,10 +254,10 @@ hpet_init(void) hpetCvtt2n = hpetCvtt2n / 1000000ULL; hpetCvtn2t = 0xFFFFFFFFFFFFFFFFULL / hpetCvtt2n; kprintf("HPET: Frequency = %6d.%04dMHz, " - "cvtt2n = %08X.%08X, cvtn2t = %08X.%08X\n", - (uint32_t)(hpetFreq / Mega), (uint32_t)(hpetFreq % Mega), - (uint32_t)(hpetCvtt2n >> 32), (uint32_t)hpetCvtt2n, - (uint32_t)(hpetCvtn2t >> 32), (uint32_t)hpetCvtn2t); + "cvtt2n = %08X.%08X, cvtn2t = %08X.%08X\n", + (uint32_t)(hpetFreq / Mega), (uint32_t)(hpetFreq % Mega), + (uint32_t)(hpetCvtt2n >> 32), (uint32_t)hpetCvtt2n, + (uint32_t)(hpetCvtn2t >> 32), (uint32_t)hpetCvtn2t); /* (TAKE OUT LATER) @@ -200,28 +286,6 @@ hpet_init(void) hpet2bus = tmrCvt(hpetCvtt2n, busFCvtn2t); DBG(" CVT: HPET to BUS = %08X.%08X\n", (uint32_t)(hpet2bus >> 32), (uint32_t)hpet2bus); - - /* Make sure the counter is off in the HPET configuration flags */ - uint64_t hpetcon = ((hpetReg_t *)hpetArea)->GEN_CONF; - hpetcon = hpetcon & ~1; - ((hpetReg_t *)hpetArea)->GEN_CONF = hpetcon; - - /* - * Convert current TSC to HPET value, - * set it, and start it ticking. - */ - uint64_t currtsc = rdtsc64(); - uint64_t tscInHPET = tmrCvt(currtsc, tsc2hpet); - ((hpetReg_t *)hpetArea)->MAIN_CNT = tscInHPET; - hpetcon = hpetcon | 1; - ((hpetReg_t *)hpetArea)->GEN_CONF = hpetcon; - kprintf("HPET started: TSC = %08X.%08X, HPET = %08X.%08X\n", - (uint32_t)(currtsc >> 32), (uint32_t)currtsc, - (uint32_t)(tscInHPET >> 32), (uint32_t)tscInHPET); - -#if MACH_KDB - db_display_hpet((hpetReg_t *)hpetArea); /* (BRINGUP) */ -#endif } /* @@ -232,69 +296,105 @@ hpet_init(void) void hpet_get_info(hpetInfo_t *info) { - info->hpetCvtt2n = hpetCvtt2n; - info->hpetCvtn2t = hpetCvtn2t; - info->tsc2hpet = tsc2hpet; - info->hpet2tsc = hpet2tsc; - info->bus2hpet = bus2hpet; - info->hpet2bus = hpet2bus; - info->rcbaArea = rcbaArea; - info->rcbaAreap = rcbaAreap; + info->hpetCvtt2n = hpetCvtt2n; + info->hpetCvtn2t = hpetCvtn2t; + info->tsc2hpet = tsc2hpet; + info->hpet2tsc = hpet2tsc; + info->bus2hpet = bus2hpet; + info->hpet2bus = hpet2bus; + /* + * XXX + * We're repurposing the rcbaArea so we can use the HPET. + * Eventually we'll rename this correctly. + */ + info->rcbaArea = hpetArea; + info->rcbaAreap = hpetAreap; } /* * This routine is called by the HPET driver - * when it assigns an HPET timer to a processor + * when it assigns an HPET timer to a processor. + * + * XXX with the new callback into the HPET driver, + * this routine will be deprecated. */ - void ml_hpet_cfg(uint32_t cpu, uint32_t hpetVect) { - uint64_t *hpetVaddr; - uint64_t hpetcnf; - - if(cpu > 1) { + uint64_t *hpetVaddr; + hpetTimer_t *hpet; + x86_lcpu_t *lcpu; + x86_core_t *core; + x86_pkg_t *pkg; + boolean_t enabled; + + if (cpu > 1) { panic("ml_hpet_cfg: invalid cpu = %d\n", cpu); } + lcpu = cpu_to_lcpu(cpu); + core = lcpu->core; + pkg = core->package; + + /* + * Only deal with the primary CPU for the package. + */ + if (!lcpu->primary) { + return; + } + + enabled = ml_set_interrupts_enabled(FALSE); + /* Calculate address of the HPET for this processor */ - hpetVaddr = (uint64_t *)(((uint32_t)&(((hpetReg_t *)hpetArea)->TIM1_CONF)) + (cpu << 5)); + hpetVaddr = (uint64_t *)(((uintptr_t)&(((hpetReg_t *)hpetArea)->TIM1_CONF)) + (cpu << 5)); + hpet = (hpetTimer_t *)hpetVaddr; + + DBG("ml_hpet_cfg: HPET for cpu %d at %p, vector = %d\n", + cpu, hpetVaddr, hpetVect); - DBG("ml_hpet_cfg: HPET for cpu %d at %08X, vector = %d\n", - cpu, hpetVaddr, hpetVect); - /* Save the address and vector of the HPET for this processor */ - cpu_data_ptr[cpu]->cpu_pmHpet = (uint64_t *)hpetVaddr; - cpu_data_ptr[cpu]->cpu_pmHpetVec = hpetVect; + core->Hpet = hpet; + core->HpetVec = hpetVect; - /* Enable the interruptions now that we have a vector */ - hpetcnf = *hpetVaddr; - hpetcnf = hpetcnf | Tn_INT_ENB_CNF; - *hpetVaddr = hpetcnf; + /* + * Enable interrupts + */ + core->Hpet->Config |= Tn_INT_ENB_CNF; /* Save the configuration */ - cpu_data_ptr[cpu]->cpu_pmStats.pmHpetCfg = hpetcnf; - cpu_data_ptr[cpu]->cpu_pmStats.pmHpetCmp = 0; + core->HpetCfg = core->Hpet->Config; + core->HpetCmp = 0; - /* See if nap policy has changed now */ - machine_nap_policy(); + /* + * We're only doing this for the primary CPU, so go + * ahead and add the HPET to the package too. + */ + pkg->Hpet = core->Hpet; + pkg->HpetVec = core->HpetVec; + pkg->HpetCfg = core->HpetCfg; + pkg->HpetCmp = core->HpetCmp; + pkg->flags |= X86PKG_FL_HAS_HPET; + ml_set_interrupts_enabled(enabled); } /* * This is the HPET interrupt handler. * - * We really don't want to be here, but so far, I haven't figured out - * a way to cancel the interrupt. Hopefully, some day we will figure out - * how to do that or switch all timers to the HPET. + * It just hands off to the power management code so that the + * appropriate things get done there. */ int HPETInterrupt(void) { - /* All we do here is to bump the count */ - current_cpu_datap()->cpu_pmStats.pmHPETRupt++; + x86_package()->HpetInt++; + + /* + * Let power management do it's thing. + */ + pmHPETInterrupt(); /* Return and show that the 'rupt has been handled... */ return 1; @@ -303,10 +403,11 @@ HPETInterrupt(void) static hpetReg_t saved_hpet; -void hpet_save( void ) +void +hpet_save(void) { - hpetReg_t *from = (hpetReg_t *) hpetArea; - hpetReg_t *to = &saved_hpet; + hpetReg_t *from = (hpetReg_t *) hpetArea; + hpetReg_t *to = &saved_hpet; to->GEN_CONF = from->GEN_CONF; to->TIM0_CONF = from->TIM0_CONF; @@ -318,10 +419,11 @@ void hpet_save( void ) to->MAIN_CNT = from->MAIN_CNT; } -void hpet_restore( void ) +void +hpet_restore(void) { - hpetReg_t *from = &saved_hpet; - hpetReg_t *to = (hpetReg_t *) hpetArea; + hpetReg_t *from = &saved_hpet; + hpetReg_t *to = (hpetReg_t *) hpetArea; /* * Is the HPET memory already enabled? @@ -329,7 +431,7 @@ void hpet_restore( void ) */ uint32_t *hptcp = (uint32_t *)(rcbaArea + 0x3404); uint32_t hptc = *hptcp; - if(!(hptc & hptcAE)) { + if (!(hptc & hptcAE)) { DBG("HPET memory is not enabled, " "enabling and assigning to 0xFED00000 (hope that's ok)\n"); *hptcp = (hptc & ~3) | hptcAE; @@ -356,10 +458,10 @@ void hpet_restore( void ) uint64_t rdHPET(void) { - hpetReg_t *hpetp = (hpetReg_t *) hpetArea; - volatile uint32_t *regp = (uint32_t *) &hpetp->MAIN_CNT; - uint32_t high; - uint32_t low; + hpetReg_t *hpetp = (hpetReg_t *) hpetArea; + volatile uint32_t *regp = (uint32_t *) &hpetp->MAIN_CNT; + uint32_t high; + uint32_t low; do { high = *(regp + 1); @@ -368,69 +470,3 @@ rdHPET(void) return (((uint64_t) high) << 32) | low; } - -#if MACH_KDB - -#define HI32(x) ((uint32_t)(((x) >> 32) & 0xFFFFFFFF)) -#define LO32(x) ((uint32_t)((x) & 0xFFFFFFFF)) - -/* - * Displays HPET memory mapped area - * hp - */ -void -db_hpet(__unused db_expr_t addr, __unused int have_addr, __unused db_expr_t count, __unused char *modif) -{ - - db_display_hpet((hpetReg_t *) hpetArea); /* Dump out the HPET - * stuff */ - return; -} - -void -db_display_hpet(hpetReg_t * hpt) -{ - - uint64_t cmain; - - cmain = hpt->MAIN_CNT; /* Get the main timer */ - - /* General capabilities */ - db_printf(" GCAP_ID = %08X.%08X\n", - HI32(hpt->GCAP_ID), LO32(hpt->GCAP_ID)); - /* General configuration */ - db_printf(" GEN_CONF = %08X.%08X\n", - HI32(hpt->GEN_CONF), LO32(hpt->GEN_CONF)); - /* General Interrupt status */ - db_printf("GINTR_STA = %08X.%08X\n", - HI32(hpt->GINTR_STA), LO32(hpt->GINTR_STA)); - /* Main counter */ - db_printf(" MAIN_CNT = %08X.%08X\n", - HI32(cmain), LO32(cmain)); - /* Timer 0 config and cap */ - db_printf("TIM0_CONF = %08X.%08X\n", - HI32(hpt->TIM0_CONF), LO32(hpt->TIM0_CONF)); - /* Timer 0 comparator */ - db_printf("TIM0_COMP = %08X.%08X\n", - HI32(hpt->TIM0_COMP), LO32(hpt->TIM0_COMP)); - /* Timer 1 config and cap */ - db_printf("TIM0_CONF = %08X.%08X\n", - HI32(hpt->TIM1_CONF), LO32(hpt->TIM1_CONF)); - /* Timer 1 comparator */ - db_printf("TIM1_COMP = %08X.%08X\n", - HI32(hpt->TIM1_COMP), LO32(hpt->TIM1_COMP)); - /* Timer 2 config and cap */ - db_printf("TIM2_CONF = %08X.%08X\n", - HI32(hpt->TIM2_CONF), LO32(hpt->TIM2_CONF)); - /* Timer 2 comparator */ - db_printf("TIM2_COMP = %08X.%08X\n", - HI32(hpt->TIM2_COMP), LO32(hpt->TIM2_COMP)); - - db_printf("\nHPET Frequency = %d.%05dMHz\n", - (uint32_t) (hpetFreq / 1000000), (uint32_t) (hpetFreq % 1000000)); - - return; - -} - -#endif