The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/osfmk/i386/hpet.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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 
   69 vm_offset_t hpetArea = 0;                       
   70 uint32_t hpetAreap = 0;                 
   71 uint64_t hpetFemto = 0;
   72 uint64_t hpetFreq = 0;
   73 uint64_t hpetCvt = 0;                   /* (TAKE OUT LATER)  */
   74 uint64_t hpetCvtt2n = 0;
   75 uint64_t hpetCvtn2t = 0;
   76 uint64_t tsc2hpet = 0;
   77 uint64_t hpet2tsc = 0;
   78 uint64_t bus2hpet = 0;
   79 uint64_t hpet2bus = 0;
   80 
   81 vm_offset_t rcbaArea = 0;                       
   82 uint32_t rcbaAreap = 0;                 
   83 
   84 static int (*hpet_req)(uint32_t apicid, void *arg, hpetRequest_t *hpet) = NULL;
   85 static void *hpet_arg = NULL;
   86 
   87 #if DEBUG
   88 #define DBG(x...)       kprintf("DBG: " x)
   89 #else
   90 #define DBG(x...)
   91 #endif
   92 
   93 int
   94 hpet_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  */
  109 int
  110 hpet_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  */
  189 static void
  190 map_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  */
  204 void
  205 hpet_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  */
  296 void
  297 hpet_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  */
  322 void
  323 ml_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  */
  387 int
  388 HPETInterrupt(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 
  404 static hpetReg_t saved_hpet;
  405 
  406 void
  407 hpet_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 
  422 void
  423 hpet_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  */
  458 uint64_t
  459 rdHPET(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 }

Cache object: f4a9030eaa0453ea6033a8ee9a230aef


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.