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 #if     MACH_KDB
   62 #include <machine/db_machdep.h>
   63 #include <ddb/db_aout.h>
   64 #include <ddb/db_access.h>
   65 #include <ddb/db_sym.h>
   66 #include <ddb/db_variables.h>
   67 #include <ddb/db_command.h>
   68 #include <ddb/db_output.h>
   69 #include <ddb/db_expr.h>
   70 #endif /* MACH_KDB */
   71 
   72 /* Decimal powers: */
   73 #define kilo (1000ULL)
   74 #define Mega (kilo * kilo)
   75 #define Giga (kilo * Mega)
   76 #define Tera (kilo * Giga)
   77 #define Peta (kilo * Tera)
   78 
   79 vm_offset_t hpetArea = 0;                       
   80 uint32_t hpetAreap = 0;                 
   81 uint64_t hpetFemto = 0;
   82 uint64_t hpetFreq = 0;
   83 uint64_t hpetCvt = 0;                   /* (TAKE OUT LATER)  */
   84 uint64_t hpetCvtt2n = 0;
   85 uint64_t hpetCvtn2t = 0;
   86 uint64_t tsc2hpet = 0;
   87 uint64_t hpet2tsc = 0;
   88 uint64_t bus2hpet = 0;
   89 uint64_t hpet2bus = 0;
   90 
   91 vm_offset_t rcbaArea = 0;                       
   92 uint32_t rcbaAreap = 0;                 
   93 
   94 static int (*hpet_req)(uint32_t apicid, void *arg, hpetRequest_t *hpet) = NULL;
   95 static void *hpet_arg = NULL;
   96 
   97 #if DEBUG
   98 #define DBG(x...)       kprintf("DBG: " x)
   99 #else
  100 #define DBG(x...)
  101 #endif
  102 
  103 int
  104 hpet_register_callback(int (*hpet_reqst)(uint32_t apicid,
  105                                          void *arg,
  106                                          hpetRequest_t *hpet),
  107                        void *arg)
  108 {
  109     hpet_req = hpet_reqst;
  110     hpet_arg = arg;
  111     return(0);
  112 }
  113 
  114 /*
  115  * This routine is called to obtain an HPET and have it assigned
  116  * to a CPU.  It returns 0 if successful and non-zero if one could
  117  * not be assigned.
  118  */
  119 int
  120 hpet_request(uint32_t cpu)
  121 {
  122     hpetRequest_t       hpetReq;
  123     int                 rc;
  124     x86_lcpu_t          *lcpu;
  125     x86_core_t          *core;
  126     x86_pkg_t           *pkg;
  127     boolean_t           enabled;
  128 
  129     if (hpet_req == NULL) {
  130         return(-1);
  131     }
  132 
  133     /*
  134      * Deal with the case where the CPU # passed in is past the
  135      * value specified in cpus=n in boot-args.
  136      */
  137     if (cpu >= real_ncpus) {
  138         enabled = ml_set_interrupts_enabled(FALSE);
  139         lcpu = cpu_to_lcpu(cpu);
  140         if (lcpu != NULL) {
  141             core = lcpu->core;
  142             pkg  = core->package;
  143 
  144             if (lcpu->primary) {
  145                 pkg->flags |= X86PKG_FL_HAS_HPET;
  146             }
  147         }
  148 
  149         ml_set_interrupts_enabled(enabled);
  150         return(0);
  151     }
  152 
  153     rc = (*hpet_req)(ml_get_apicid(cpu), hpet_arg, &hpetReq);
  154     if (rc != 0) {
  155         return(rc);
  156     }
  157 
  158     enabled = ml_set_interrupts_enabled(FALSE);
  159     lcpu = cpu_to_lcpu(cpu);
  160     core = lcpu->core;
  161     pkg  = core->package;
  162 
  163     /*
  164      * Compute the address of the HPET.
  165      */
  166     core->Hpet = (hpetTimer_t *)((uint8_t *)hpetArea + hpetReq.hpetOffset);
  167     core->HpetVec = hpetReq.hpetVector;
  168 
  169     /*
  170      * Enable interrupts
  171      */
  172     core->Hpet->Config |= Tn_INT_ENB_CNF;
  173 
  174     /*
  175      * Save the configuration
  176      */
  177     core->HpetCfg = core->Hpet->Config;
  178     core->HpetCmp = 0;
  179 
  180     /*
  181      * If the CPU is the "primary" for the package, then
  182      * add the HPET to the package too.
  183      */
  184     if (lcpu->primary) {
  185         pkg->Hpet = core->Hpet;
  186         pkg->HpetCfg = core->HpetCfg;
  187         pkg->HpetCmp = core->HpetCmp;
  188         pkg->flags |= X86PKG_FL_HAS_HPET;
  189     }
  190 
  191     ml_set_interrupts_enabled(enabled);
  192 
  193     return(0);
  194 }
  195 
  196 /*
  197  * Map the RCBA area.
  198  */
  199 static void
  200 map_rcbaArea(void)
  201 {
  202         /*
  203          * Get RCBA area physical address and map it
  204          */
  205         outl(cfgAdr, lpcCfg | (0xF0 & 0xFC));
  206         rcbaAreap = inl(cfgDat | (0xF0 & 0x03));
  207         rcbaArea = io_map_spec(rcbaAreap & -4096, PAGE_SIZE * 4, VM_WIMG_IO);
  208         kprintf("RCBA: vaddr = %lX, paddr = %08X\n", (unsigned long)rcbaArea, rcbaAreap);
  209 }
  210 
  211 /*
  212  * Initialize the HPET
  213  */
  214 void
  215 hpet_init(void)
  216 {
  217         unsigned int    *xmod;
  218 
  219         map_rcbaArea();
  220 
  221         /*
  222          * Is the HPET memory already enabled?
  223          * If not, set address and enable.
  224          */
  225         xmod = (uint32_t *)(rcbaArea + 0x3404); /* Point to the HPTC */
  226         uint32_t hptc = *xmod;                  /* Get HPET config */
  227         DBG("    current RCBA.HPTC:  %08X\n", *xmod);
  228         if(!(hptc & hptcAE)) {
  229                 DBG("HPET memory is not enabled, "
  230                     "enabling and assigning to 0xFED00000 (hope that's ok)\n");
  231                 *xmod = (hptc & ~3) | hptcAE;
  232         }
  233 
  234         /*
  235          * Get physical address of HPET and map it.
  236          */
  237         hpetAreap = hpetAddr | ((hptc & 3) << 12);
  238         hpetArea = io_map_spec(hpetAreap & -4096, PAGE_SIZE * 4, VM_WIMG_IO);
  239         kprintf("HPET: vaddr = %lX, paddr = %08X\n", (unsigned long)hpetArea, hpetAreap);
  240 
  241         /*
  242          * Extract the HPET tick rate.
  243          * The period of the HPET is reported in femtoseconds (10**-15s)
  244          * and convert to frequency in hertz.
  245          */
  246         hpetFemto = (uint32_t)(((hpetReg_t *)hpetArea)->GCAP_ID >> 32);
  247         hpetFreq = (1 * Peta) / hpetFemto;
  248 
  249         /*
  250          * The conversion factor is the number of nanoseconds per HPET tick
  251          * with about 32 bits of fraction.  The value is converted to a
  252          * base-2 fixed point number.  To convert from HPET to nanoseconds,
  253          * multiply the value by the conversion factor using 96-bit arithmetic,
  254          * then shift right 32 bits.  If the value is known to be small,
  255          * 64-bit arithmetic will work.
  256          */
  257 
  258         /*
  259          * Begin conversion of base 10 femtoseconds to base 2, calculate:
  260          *  - HPET ticks to nanoseconds conversion in base 2 fraction (* 2**32)
  261          *  - nanoseconds to HPET ticks conversion
  262          */
  263         hpetCvtt2n = (uint64_t)hpetFemto << 32;
  264         hpetCvtt2n = hpetCvtt2n / 1000000ULL;
  265         hpetCvtn2t = 0xFFFFFFFFFFFFFFFFULL / hpetCvtt2n;
  266         kprintf("HPET: Frequency = %6d.%04dMHz, "
  267                 "cvtt2n = %08X.%08X, cvtn2t = %08X.%08X\n",
  268                 (uint32_t)(hpetFreq / Mega), (uint32_t)(hpetFreq % Mega), 
  269                 (uint32_t)(hpetCvtt2n >> 32), (uint32_t)hpetCvtt2n,
  270                 (uint32_t)(hpetCvtn2t >> 32), (uint32_t)hpetCvtn2t);
  271 
  272 
  273         /* (TAKE OUT LATER)
  274          * Begin conversion of base 10 femtoseconds to base 2
  275          * HPET ticks to nanoseconds in base 2 fraction (times 1048576)
  276          */
  277         hpetCvt = (uint64_t)hpetFemto << 20;
  278         hpetCvt = hpetCvt / 1000000ULL;
  279 
  280         /* Calculate conversion from TSC to HPET */
  281         tsc2hpet = tmrCvt(tscFCvtt2n, hpetCvtn2t);
  282         DBG(" CVT: TSC to HPET = %08X.%08X\n",
  283             (uint32_t)(tsc2hpet >> 32), (uint32_t)tsc2hpet);
  284 
  285         /* Calculate conversion from HPET to TSC */
  286         hpet2tsc = tmrCvt(hpetCvtt2n, tscFCvtn2t);
  287         DBG(" CVT: HPET to TSC = %08X.%08X\n",
  288             (uint32_t)(hpet2tsc >> 32), (uint32_t)hpet2tsc);
  289 
  290         /* Calculate conversion from BUS to HPET */
  291         bus2hpet = tmrCvt(busFCvtt2n, hpetCvtn2t);
  292         DBG(" CVT: BUS to HPET = %08X.%08X\n",
  293             (uint32_t)(bus2hpet >> 32), (uint32_t)bus2hpet);
  294 
  295         /* Calculate conversion from HPET to BUS */
  296         hpet2bus = tmrCvt(hpetCvtt2n, busFCvtn2t);
  297         DBG(" CVT: HPET to BUS = %08X.%08X\n",
  298             (uint32_t)(hpet2bus >> 32), (uint32_t)hpet2bus);
  299 
  300 #if MACH_KDB
  301         db_display_hpet((hpetReg_t *)hpetArea); /* (BRINGUP) */
  302 #endif
  303 }
  304 
  305 /*
  306  * This routine is used to get various information about the HPET
  307  * without having to export gobs of globals.  It fills in a data
  308  * structure with the info.
  309  */
  310 void
  311 hpet_get_info(hpetInfo_t *info)
  312 {
  313     info->hpetCvtt2n = hpetCvtt2n;
  314     info->hpetCvtn2t = hpetCvtn2t;
  315     info->tsc2hpet   = tsc2hpet;
  316     info->hpet2tsc   = hpet2tsc;
  317     info->bus2hpet   = bus2hpet;
  318     info->hpet2bus   = hpet2bus;
  319     /*
  320      * XXX
  321      * We're repurposing the rcbaArea so we can use the HPET.
  322      * Eventually we'll rename this correctly.
  323      */
  324     info->rcbaArea   = hpetArea;
  325     info->rcbaAreap  = hpetAreap;
  326 }
  327 
  328 
  329 /*
  330  * This routine is called by the HPET driver
  331  * when it assigns an HPET timer to a processor.
  332  *
  333  * XXX with the new callback into the HPET driver,
  334  * this routine will be deprecated.
  335  */
  336 void
  337 ml_hpet_cfg(uint32_t cpu, uint32_t hpetVect)
  338 {
  339         uint64_t        *hpetVaddr;
  340         hpetTimer_t     *hpet;
  341         x86_lcpu_t      *lcpu;
  342         x86_core_t      *core;
  343         x86_pkg_t       *pkg;
  344         boolean_t       enabled;
  345         
  346         if(cpu > 1) {
  347                 panic("ml_hpet_cfg: invalid cpu = %d\n", cpu);
  348         }
  349 
  350         lcpu = cpu_to_lcpu(cpu);
  351         core = lcpu->core;
  352         pkg  = core->package;
  353 
  354         /*
  355          * Only deal with the primary CPU for the package.
  356          */
  357         if (!lcpu->primary)
  358             return;
  359 
  360         enabled = ml_set_interrupts_enabled(FALSE);
  361 
  362         /* Calculate address of the HPET for this processor */
  363         hpetVaddr = (uint64_t *)(((uintptr_t)&(((hpetReg_t *)hpetArea)->TIM1_CONF)) + (cpu << 5));
  364         hpet = (hpetTimer_t *)hpetVaddr;
  365 
  366         DBG("ml_hpet_cfg: HPET for cpu %d at %p, vector = %d\n",
  367              cpu, hpetVaddr, hpetVect);
  368 
  369         /* Save the address and vector of the HPET for this processor */
  370         core->Hpet = hpet;
  371         core->HpetVec = hpetVect;
  372 
  373         /*
  374          * Enable interrupts
  375          */
  376         core->Hpet->Config |= Tn_INT_ENB_CNF;
  377 
  378         /* Save the configuration */
  379         core->HpetCfg = core->Hpet->Config;
  380         core->HpetCmp = 0;
  381 
  382         /*
  383          * We're only doing this for the primary CPU, so go
  384          * ahead and add the HPET to the package too.
  385          */
  386         pkg->Hpet = core->Hpet;
  387         pkg->HpetVec = core->HpetVec;
  388         pkg->HpetCfg = core->HpetCfg;
  389         pkg->HpetCmp = core->HpetCmp;
  390         pkg->flags |= X86PKG_FL_HAS_HPET;
  391 
  392         ml_set_interrupts_enabled(enabled);
  393 }
  394 
  395 /*
  396  * This is the HPET interrupt handler.
  397  *
  398  * It just hands off to the power management code so that the
  399  * appropriate things get done there.
  400  */
  401 int
  402 HPETInterrupt(void)
  403 {
  404 
  405         /* All we do here is to bump the count */
  406         x86_package()->HpetInt++;
  407 
  408         /*
  409          * Let power management do it's thing.
  410          */
  411         pmHPETInterrupt();
  412 
  413         /* Return and show that the 'rupt has been handled... */
  414         return 1;
  415 }
  416 
  417 
  418 static hpetReg_t saved_hpet;
  419 
  420 void
  421 hpet_save(void)
  422 {
  423         hpetReg_t       *from = (hpetReg_t *) hpetArea;
  424         hpetReg_t       *to = &saved_hpet;
  425 
  426         to->GEN_CONF  = from->GEN_CONF;
  427         to->TIM0_CONF = from->TIM0_CONF;
  428         to->TIM0_COMP = from->TIM0_COMP;
  429         to->TIM1_CONF = from->TIM1_CONF;
  430         to->TIM1_COMP = from->TIM1_COMP;
  431         to->TIM2_CONF = from->TIM2_CONF;
  432         to->TIM2_COMP = from->TIM2_COMP;
  433         to->MAIN_CNT  = from->MAIN_CNT;
  434 }
  435 
  436 void
  437 hpet_restore(void)
  438 {
  439         hpetReg_t       *from = &saved_hpet;
  440         hpetReg_t       *to = (hpetReg_t *) hpetArea;
  441 
  442         /*
  443          * Is the HPET memory already enabled?
  444          * If not, set address and enable.
  445          */
  446         uint32_t *hptcp = (uint32_t *)(rcbaArea + 0x3404);
  447         uint32_t hptc = *hptcp;
  448         if(!(hptc & hptcAE)) {
  449                 DBG("HPET memory is not enabled, "
  450                     "enabling and assigning to 0xFED00000 (hope that's ok)\n");
  451                 *hptcp = (hptc & ~3) | hptcAE;
  452         }
  453 
  454         to->GEN_CONF  = from->GEN_CONF & ~1;
  455 
  456         to->TIM0_CONF = from->TIM0_CONF;
  457         to->TIM0_COMP = from->TIM0_COMP;
  458         to->TIM1_CONF = from->TIM1_CONF;
  459         to->TIM1_COMP = from->TIM1_COMP;
  460         to->TIM2_CONF = from->TIM2_CONF;
  461         to->TIM2_COMP = from->TIM2_COMP;
  462         to->GINTR_STA = -1ULL;
  463         to->MAIN_CNT  = from->MAIN_CNT;
  464 
  465         to->GEN_CONF = from->GEN_CONF;
  466 }
  467 
  468 /*
  469  *      Read the HPET timer
  470  *
  471  */
  472 uint64_t
  473 rdHPET(void)
  474 {
  475         hpetReg_t               *hpetp = (hpetReg_t *) hpetArea;
  476         volatile uint32_t       *regp = (uint32_t *) &hpetp->MAIN_CNT;
  477         uint32_t                high;
  478         uint32_t                low;
  479 
  480         do {
  481                 high = *(regp + 1);
  482                 low = *regp;
  483         } while (high != *(regp + 1));
  484 
  485         return (((uint64_t) high) << 32) | low;
  486 }
  487 
  488 #if MACH_KDB
  489 
  490 #define HI32(x) ((uint32_t)(((x) >> 32) & 0xFFFFFFFF))
  491 #define LO32(x) ((uint32_t)((x) & 0xFFFFFFFF))
  492 
  493 /*
  494  *      Displays HPET memory mapped area
  495  *      hp
  496  */
  497 void 
  498 db_hpet(__unused db_expr_t addr, __unused int have_addr, __unused db_expr_t count, __unused char *modif)
  499 {
  500 
  501         db_display_hpet((hpetReg_t *) hpetArea);        /* Dump out the HPET
  502                                                          * stuff */
  503         return;
  504 }
  505 
  506 void
  507 db_display_hpet(hpetReg_t *hpt)
  508 {
  509         uint64_t        cmain;
  510 
  511         cmain = hpt->MAIN_CNT;  /* Get the main timer */
  512 
  513         /* General capabilities */
  514         db_printf("  GCAP_ID = %08X.%08X\n",
  515                   HI32(hpt->GCAP_ID), LO32(hpt->GCAP_ID));
  516         /* General configuration */
  517         db_printf(" GEN_CONF = %08X.%08X\n",
  518                   HI32(hpt->GEN_CONF), LO32(hpt->GEN_CONF));
  519         /* General Interrupt status */
  520         db_printf("GINTR_STA = %08X.%08X\n",
  521                   HI32(hpt->GINTR_STA), LO32(hpt->GINTR_STA));
  522         /* Main counter */
  523         db_printf(" MAIN_CNT = %08X.%08X\n",
  524                   HI32(cmain), LO32(cmain));
  525         /* Timer 0 config and cap */
  526         db_printf("TIM0_CONF = %08X.%08X\n",
  527                   HI32(hpt->TIM0_CONF), LO32(hpt->TIM0_CONF));
  528         /* Timer 0 comparator */
  529         db_printf("TIM0_COMP = %08X.%08X\n",
  530                   HI32(hpt->TIM0_COMP), LO32(hpt->TIM0_COMP));
  531         /* Timer 1 config and cap */
  532         db_printf("TIM0_CONF = %08X.%08X\n",
  533                   HI32(hpt->TIM1_CONF), LO32(hpt->TIM1_CONF));
  534         /* Timer 1 comparator */
  535         db_printf("TIM1_COMP = %08X.%08X\n",
  536                   HI32(hpt->TIM1_COMP), LO32(hpt->TIM1_COMP));
  537         /* Timer 2 config and cap */
  538         db_printf("TIM2_CONF = %08X.%08X\n",
  539                   HI32(hpt->TIM2_CONF), LO32(hpt->TIM2_CONF));
  540         /* Timer 2 comparator */
  541         db_printf("TIM2_COMP = %08X.%08X\n",
  542                   HI32(hpt->TIM2_COMP), LO32(hpt->TIM2_COMP));
  543 
  544         db_printf("\nHPET Frequency = %d.%05dMHz\n",
  545           (uint32_t) (hpetFreq / 1000000), (uint32_t) (hpetFreq % 1000000));
  546 }
  547 #endif

Cache object: f668b1fd734fe0c4b8356b0493115191


[ 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.