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/dev/ic/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 /* $NetBSD: hpet.c,v 1.18 2022/08/20 06:47:28 mlelstv Exp $ */
    2 
    3 /*
    4  * Copyright (c) 2006 Nicolas Joly
    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 AND CONTRIBUTORS
   19  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   20  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   21  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
   22  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   28  * POSSIBILITY OF SUCH DAMAGE.
   29  */
   30 
   31 /*
   32  * High Precision Event Timer.
   33  */
   34 
   35 #include <sys/cdefs.h>
   36 __KERNEL_RCSID(0, "$NetBSD: hpet.c,v 1.18 2022/08/20 06:47:28 mlelstv Exp $");
   37 
   38 #include <sys/systm.h>
   39 #include <sys/device.h>
   40 #include <sys/module.h>
   41 
   42 #include <sys/time.h>
   43 #include <sys/timetc.h>
   44 
   45 #include <sys/bus.h>
   46 #include <sys/lock.h>
   47 
   48 #include <machine/cpu_counter.h>
   49 
   50 #include <dev/ic/hpetreg.h>
   51 #include <dev/ic/hpetvar.h>
   52 
   53 static u_int    hpet_get_timecount(struct timecounter *);
   54 static bool     hpet_resume(device_t, const pmf_qual_t *);
   55 
   56 static struct hpet_softc *hpet0 __read_mostly;
   57 
   58 int
   59 hpet_detach(device_t dv, int flags)
   60 {
   61 #if 0 /* XXX DELAY() is based off this, detaching is not a good idea. */
   62         struct hpet_softc *sc = device_private(dv);
   63         int rc;
   64 
   65         if ((rc = tc_detach(&sc->sc_tc)) != 0)
   66                 return rc;
   67 
   68         pmf_device_deregister(dv);
   69 
   70         bus_space_write_4(sc->sc_memt, sc->sc_memh, HPET_CONFIG, sc->sc_config);
   71 
   72         return 0;
   73 #else
   74         return EBUSY;
   75 #endif
   76 }
   77 
   78 void
   79 hpet_attach_subr(device_t dv)
   80 {
   81         struct hpet_softc *sc = device_private(dv);
   82         struct timecounter *tc;
   83         uint64_t tmp;
   84         uint32_t val, sval, eval;
   85         int i;
   86 
   87         tc = &sc->sc_tc;
   88 
   89         tc->tc_name = device_xname(dv);
   90         tc->tc_get_timecount = hpet_get_timecount;
   91         tc->tc_quality = 2000;
   92 
   93         tc->tc_counter_mask = 0xffffffff;
   94 
   95         /* Get frequency */
   96         sc->sc_period = bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_PERIOD);
   97         if (sc->sc_period == 0 || sc->sc_period > HPET_PERIOD_MAX) {
   98                 aprint_error_dev(dv, "invalid timer period\n");
   99                 return;
  100         }
  101 
  102         /*
  103          * The following loop is a workaround for AMD SB700 based systems.
  104          * http://kerneltrap.org/mailarchive/git-commits-head/2008/8/17/2964724
  105          * http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=a6825f1c1fa83b1e92b6715ee5771a4d6524d3b9
  106          */
  107         for (i = 0; bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_CONFIG)
  108             == 0xffffffff; i++) {
  109                 if (i >= 1000) {
  110                         aprint_error_dev(dv,
  111                             "HPET_CONFIG value = 0xffffffff\n");
  112                         return;
  113                 }
  114         }
  115 
  116         tmp = (1000000000000000ULL * 2) / sc->sc_period;
  117         tc->tc_frequency = (tmp / 2) + (tmp & 1);
  118 
  119         /* Enable timer */
  120         val = bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_CONFIG);
  121         sc->sc_config = val;
  122         if ((val & HPET_CONFIG_ENABLE) == 0) {
  123                 val |= HPET_CONFIG_ENABLE;
  124                 bus_space_write_4(sc->sc_memt, sc->sc_memh, HPET_CONFIG, val);
  125         }
  126 
  127         tc->tc_priv = sc;
  128         tc_init(tc);
  129 
  130         if (!pmf_device_register(dv, NULL, hpet_resume))
  131                 aprint_error_dev(dv, "couldn't establish power handler\n");
  132 
  133         if (device_unit(dv) == 0)
  134                 hpet0 = sc;
  135 
  136         /*
  137          * Determine approximately how long it takes to read the counter
  138          * register once, and compute an ajustment for hpet_delay() based on
  139          * that.
  140          */
  141         (void)bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_MCOUNT_LO);
  142         sval = bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_MCOUNT_LO);
  143         for (i = 0; i < 998; i++)
  144                 (void)bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_MCOUNT_LO);
  145         eval = bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_MCOUNT_LO);
  146         val = eval - sval;
  147         sc->sc_adj = (int64_t)val * sc->sc_period / 1000;
  148 }
  149 
  150 static u_int
  151 hpet_get_timecount(struct timecounter *tc)
  152 {
  153         struct hpet_softc *sc = tc->tc_priv;
  154 
  155         return bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_MCOUNT_LO);
  156 }
  157 
  158 static bool
  159 hpet_resume(device_t dv, const pmf_qual_t *qual)
  160 {
  161         struct hpet_softc *sc = device_private(dv);
  162         uint32_t val;
  163 
  164         val = bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_CONFIG);
  165         val |= HPET_CONFIG_ENABLE;
  166         bus_space_write_4(sc->sc_memt, sc->sc_memh, HPET_CONFIG, val);
  167 
  168         return true;
  169 }
  170 
  171 bool
  172 hpet_delay_p(void)
  173 {
  174 
  175         return hpet0 != NULL;
  176 }
  177 
  178 void
  179 hpet_delay(unsigned int us)
  180 {
  181         struct hpet_softc *sc;
  182         uint32_t ntick, otick;
  183         int64_t delta;
  184 
  185         /*
  186          * Read timer before slow division.  Convert microseconds to
  187          * femtoseconds, subtract the cost of 1 counter register access,
  188          * and convert to HPET units.
  189          */
  190         sc = hpet0;
  191         otick = bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_MCOUNT_LO);
  192         delta = (((int64_t)us * 1000000000) - sc->sc_adj) / sc->sc_period;
  193 
  194         while (delta > 0) {
  195                 SPINLOCK_BACKOFF_HOOK;
  196                 ntick = bus_space_read_4(sc->sc_memt, sc->sc_memh,
  197                     HPET_MCOUNT_LO);
  198                 delta -= (uint32_t)(ntick - otick);
  199                 otick = ntick;
  200         }
  201 }
  202 
  203 uint64_t
  204 hpet_tsc_freq(void)
  205 {
  206         struct hpet_softc *sc;
  207         uint64_t td0, td, val, freq;
  208         uint32_t hd0, hd;
  209         int s;
  210 
  211         if (hpet0 == NULL || !cpu_hascounter())
  212                 return 0;
  213 
  214         sc = hpet0;
  215 
  216         s = splhigh();
  217         (void)cpu_counter();
  218         (void)bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_MCOUNT_LO);
  219         hd0 = bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_MCOUNT_LO);
  220         td0 = cpu_counter();
  221         splx(s);
  222 
  223         /*
  224          * Wait 1000000 HPET ticks (typically 50..100ms).
  225          *
  226          * This interval can produce an error of 1ppm (a few kHz
  227          * in estimated TSC frequency), however the HPET timer is
  228          * allowed to drift +/- 500ppm in that interval.
  229          *
  230          */
  231         hpet_delay(sc->sc_period / 1000);
  232 
  233         /*
  234          * Determine TSC freq by comparing how far the TSC and HPET have
  235          * advanced and round result to the nearest 1000.
  236          */
  237         s = splhigh();
  238         (void)cpu_counter();
  239         (void)bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_MCOUNT_LO);
  240         hd = bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_MCOUNT_LO);
  241         td = cpu_counter();
  242         splx(s);
  243 
  244         val = (uint64_t)(hd - hd0) * sc->sc_period / 100000000;
  245         freq = (td - td0) * 10000000 / val;
  246         return rounddown(freq + 500, 1000);
  247 }
  248 
  249 MODULE(MODULE_CLASS_DRIVER, hpet, NULL);
  250 
  251 #ifdef _MODULE
  252 #include "ioconf.c"
  253 #endif
  254 
  255 static int
  256 hpet_modcmd(modcmd_t cmd, void *aux)
  257 {
  258         int rv = 0;
  259 
  260         switch (cmd) {
  261 
  262         case MODULE_CMD_INIT:
  263 
  264 #ifdef _MODULE
  265                 rv = config_init_component(cfdriver_ioconf_hpet,
  266                     cfattach_ioconf_hpet, cfdata_ioconf_hpet);
  267 #endif
  268                 break;
  269 
  270         case MODULE_CMD_FINI:
  271 
  272 #ifdef _MODULE
  273                 rv = config_fini_component(cfdriver_ioconf_hpet,
  274                     cfattach_ioconf_hpet, cfdata_ioconf_hpet);
  275 #endif
  276                 break;
  277 
  278         default:
  279                 rv = ENOTTY;
  280         }
  281 
  282         return rv;
  283 }

Cache object: 4988e8746e8961b9b7dbc3f2d55697b4


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