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/acpica/acpi_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 Poul-Henning Kamp
    3  * Copyright (c) 2010 Alexander Motin <mav@FreeBSD.org>
    4  * All rights reserved.
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice, this list of conditions and the following disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  *
   15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   25  * SUCH DAMAGE.
   26  */
   27 
   28 #include <sys/cdefs.h>
   29 __FBSDID("$FreeBSD$");
   30 
   31 #include "opt_acpi.h"
   32 #if defined(__amd64__) || defined(__ia64__)
   33 #define DEV_APIC
   34 #else
   35 #include "opt_apic.h"
   36 #endif
   37 #include <sys/param.h>
   38 #include <sys/conf.h>
   39 #include <sys/bus.h>
   40 #include <sys/kernel.h>
   41 #include <sys/module.h>
   42 #include <sys/proc.h>
   43 #include <sys/rman.h>
   44 #include <sys/mman.h>
   45 #include <sys/time.h>
   46 #include <sys/smp.h>
   47 #include <sys/sysctl.h>
   48 #include <sys/timeet.h>
   49 #include <sys/timetc.h>
   50 
   51 #include <contrib/dev/acpica/include/acpi.h>
   52 #include <contrib/dev/acpica/include/accommon.h>
   53 
   54 #include <dev/acpica/acpivar.h>
   55 #include <dev/acpica/acpi_hpet.h>
   56 
   57 #ifdef DEV_APIC
   58 #include "pcib_if.h"
   59 #endif
   60 
   61 #define HPET_VENDID_AMD         0x4353
   62 #define HPET_VENDID_AMD2        0x1022
   63 #define HPET_VENDID_INTEL       0x8086
   64 #define HPET_VENDID_NVIDIA      0x10de
   65 #define HPET_VENDID_SW          0x1166
   66 
   67 ACPI_SERIAL_DECL(hpet, "ACPI HPET support");
   68 
   69 static devclass_t hpet_devclass;
   70 
   71 /* ACPI CA debugging */
   72 #define _COMPONENT      ACPI_TIMER
   73 ACPI_MODULE_NAME("HPET")
   74 
   75 struct hpet_softc {
   76         device_t                dev;
   77         int                     mem_rid;
   78         int                     intr_rid;
   79         int                     irq;
   80         int                     useirq;
   81         int                     legacy_route;
   82         int                     per_cpu;
   83         uint32_t                allowed_irqs;
   84         struct resource         *mem_res;
   85         struct resource         *intr_res;
   86         void                    *intr_handle;
   87         ACPI_HANDLE             handle;
   88         uint64_t                freq;
   89         uint32_t                caps;
   90         struct timecounter      tc;
   91         struct hpet_timer {
   92                 struct eventtimer       et;
   93                 struct hpet_softc       *sc;
   94                 int                     num;
   95                 int                     mode;
   96 #define TIMER_STOPPED   0
   97 #define TIMER_PERIODIC  1
   98 #define TIMER_ONESHOT   2
   99                 int                     intr_rid;
  100                 int                     irq;
  101                 int                     pcpu_cpu;
  102                 int                     pcpu_misrouted;
  103                 int                     pcpu_master;
  104                 int                     pcpu_slaves[MAXCPU];
  105                 struct resource         *intr_res;
  106                 void                    *intr_handle;
  107                 uint32_t                caps;
  108                 uint32_t                vectors;
  109                 uint32_t                div;
  110                 uint32_t                next;
  111                 char                    name[8];
  112         }                       t[32];
  113         int                     num_timers;
  114         struct cdev             *pdev;
  115         int                     mmap_allow;
  116         int                     mmap_allow_write;
  117 };
  118 
  119 static d_open_t hpet_open;
  120 static d_mmap_t hpet_mmap;
  121 
  122 static struct cdevsw hpet_cdevsw = {
  123         .d_version =    D_VERSION,
  124         .d_name =       "hpet",
  125         .d_open =       hpet_open,
  126         .d_mmap =       hpet_mmap,
  127 };
  128 
  129 static u_int hpet_get_timecount(struct timecounter *tc);
  130 static void hpet_test(struct hpet_softc *sc);
  131 
  132 static char *hpet_ids[] = { "PNP0103", NULL };
  133 
  134 static u_int
  135 hpet_get_timecount(struct timecounter *tc)
  136 {
  137         struct hpet_softc *sc;
  138 
  139         sc = tc->tc_priv;
  140         return (bus_read_4(sc->mem_res, HPET_MAIN_COUNTER));
  141 }
  142 
  143 static void
  144 hpet_enable(struct hpet_softc *sc)
  145 {
  146         uint32_t val;
  147 
  148         val = bus_read_4(sc->mem_res, HPET_CONFIG);
  149         if (sc->legacy_route)
  150                 val |= HPET_CNF_LEG_RT;
  151         else
  152                 val &= ~HPET_CNF_LEG_RT;
  153         val |= HPET_CNF_ENABLE;
  154         bus_write_4(sc->mem_res, HPET_CONFIG, val);
  155 }
  156 
  157 static void
  158 hpet_disable(struct hpet_softc *sc)
  159 {
  160         uint32_t val;
  161 
  162         val = bus_read_4(sc->mem_res, HPET_CONFIG);
  163         val &= ~HPET_CNF_ENABLE;
  164         bus_write_4(sc->mem_res, HPET_CONFIG, val);
  165 }
  166 
  167 static int
  168 hpet_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
  169 {
  170         struct hpet_timer *mt = (struct hpet_timer *)et->et_priv;
  171         struct hpet_timer *t;
  172         struct hpet_softc *sc = mt->sc;
  173         uint32_t fdiv, now;
  174 
  175         t = (mt->pcpu_master < 0) ? mt : &sc->t[mt->pcpu_slaves[curcpu]];
  176         if (period != 0) {
  177                 t->mode = TIMER_PERIODIC;
  178                 t->div = (sc->freq * period) >> 32;
  179         } else {
  180                 t->mode = TIMER_ONESHOT;
  181                 t->div = 0;
  182         }
  183         if (first != 0)
  184                 fdiv = (sc->freq * first) >> 32;
  185         else
  186                 fdiv = t->div;
  187         if (t->irq < 0)
  188                 bus_write_4(sc->mem_res, HPET_ISR, 1 << t->num);
  189         t->caps |= HPET_TCNF_INT_ENB;
  190         now = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
  191 restart:
  192         t->next = now + fdiv;
  193         if (t->mode == TIMER_PERIODIC && (t->caps & HPET_TCAP_PER_INT)) {
  194                 t->caps |= HPET_TCNF_TYPE;
  195                 bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num),
  196                     t->caps | HPET_TCNF_VAL_SET);
  197                 bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
  198                     t->next);
  199                 bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
  200                     t->div);
  201         } else {
  202                 t->caps &= ~HPET_TCNF_TYPE;
  203                 bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num),
  204                     t->caps);
  205                 bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
  206                     t->next);
  207         }
  208         now = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
  209         if ((int32_t)(now - t->next + HPET_MIN_CYCLES) >= 0) {
  210                 fdiv *= 2;
  211                 goto restart;
  212         }
  213         return (0);
  214 }
  215 
  216 static int
  217 hpet_stop(struct eventtimer *et)
  218 {
  219         struct hpet_timer *mt = (struct hpet_timer *)et->et_priv;
  220         struct hpet_timer *t;
  221         struct hpet_softc *sc = mt->sc;
  222 
  223         t = (mt->pcpu_master < 0) ? mt : &sc->t[mt->pcpu_slaves[curcpu]];
  224         t->mode = TIMER_STOPPED;
  225         t->caps &= ~(HPET_TCNF_INT_ENB | HPET_TCNF_TYPE);
  226         bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num), t->caps);
  227         return (0);
  228 }
  229 
  230 static int
  231 hpet_intr_single(void *arg)
  232 {
  233         struct hpet_timer *t = (struct hpet_timer *)arg;
  234         struct hpet_timer *mt;
  235         struct hpet_softc *sc = t->sc;
  236         uint32_t now;
  237 
  238         if (t->mode == TIMER_STOPPED)
  239                 return (FILTER_STRAY);
  240         /* Check that per-CPU timer interrupt reached right CPU. */
  241         if (t->pcpu_cpu >= 0 && t->pcpu_cpu != curcpu) {
  242                 if ((++t->pcpu_misrouted) % 32 == 0) {
  243                         printf("HPET interrupt routed to the wrong CPU"
  244                             " (timer %d CPU %d -> %d)!\n",
  245                             t->num, t->pcpu_cpu, curcpu);
  246                 }
  247 
  248                 /*
  249                  * Reload timer, hoping that next time may be more lucky
  250                  * (system will manage proper interrupt binding).
  251                  */
  252                 if ((t->mode == TIMER_PERIODIC &&
  253                     (t->caps & HPET_TCAP_PER_INT) == 0) ||
  254                     t->mode == TIMER_ONESHOT) {
  255                         t->next = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER) +
  256                             sc->freq / 8;
  257                         bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
  258                             t->next);
  259                 }
  260                 return (FILTER_HANDLED);
  261         }
  262         if (t->mode == TIMER_PERIODIC &&
  263             (t->caps & HPET_TCAP_PER_INT) == 0) {
  264                 t->next += t->div;
  265                 now = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
  266                 if ((int32_t)((now + t->div / 2) - t->next) > 0)
  267                         t->next = now + t->div / 2;
  268                 bus_write_4(sc->mem_res,
  269                     HPET_TIMER_COMPARATOR(t->num), t->next);
  270         } else if (t->mode == TIMER_ONESHOT)
  271                 t->mode = TIMER_STOPPED;
  272         mt = (t->pcpu_master < 0) ? t : &sc->t[t->pcpu_master];
  273         if (mt->et.et_active)
  274                 mt->et.et_event_cb(&mt->et, mt->et.et_arg);
  275         return (FILTER_HANDLED);
  276 }
  277 
  278 static int
  279 hpet_intr(void *arg)
  280 {
  281         struct hpet_softc *sc = (struct hpet_softc *)arg;
  282         int i;
  283         uint32_t val;
  284 
  285         val = bus_read_4(sc->mem_res, HPET_ISR);
  286         if (val) {
  287                 bus_write_4(sc->mem_res, HPET_ISR, val);
  288                 val &= sc->useirq;
  289                 for (i = 0; i < sc->num_timers; i++) {
  290                         if ((val & (1 << i)) == 0)
  291                                 continue;
  292                         hpet_intr_single(&sc->t[i]);
  293                 }
  294                 return (FILTER_HANDLED);
  295         }
  296         return (FILTER_STRAY);
  297 }
  298 
  299 static ACPI_STATUS
  300 hpet_find(ACPI_HANDLE handle, UINT32 level, void *context,
  301     void **status)
  302 {
  303         char            **ids;
  304         uint32_t        id = (uint32_t)(uintptr_t)context;
  305         uint32_t        uid = 0;
  306 
  307         for (ids = hpet_ids; *ids != NULL; ids++) {
  308                 if (acpi_MatchHid(handle, *ids))
  309                         break;
  310         }
  311         if (*ids == NULL)
  312                 return (AE_OK);
  313         if (ACPI_FAILURE(acpi_GetInteger(handle, "_UID", &uid)) ||
  314             id == uid)
  315                 *status = acpi_get_device(handle);
  316         return (AE_OK);
  317 }
  318 
  319 /*
  320  * Find an existing IRQ resource that matches the requested IRQ range
  321  * and return its RID.  If one is not found, use a new RID.
  322  */
  323 static int
  324 hpet_find_irq_rid(device_t dev, u_long start, u_long end)
  325 {
  326         u_long irq;
  327         int error, rid;
  328 
  329         for (rid = 0;; rid++) {
  330                 error = bus_get_resource(dev, SYS_RES_IRQ, rid, &irq, NULL);
  331                 if (error != 0 || (start <= irq && irq <= end))
  332                         return (rid);
  333         }
  334 }
  335 
  336 static int
  337 hpet_open(struct cdev *cdev, int oflags, int devtype, struct thread *td)
  338 {
  339         struct hpet_softc *sc;
  340 
  341         sc = cdev->si_drv1;
  342         if (!sc->mmap_allow)
  343                 return (EPERM);
  344         else
  345                 return (0);
  346 }
  347 
  348 static int
  349 hpet_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr,
  350     int nprot, vm_memattr_t *memattr)
  351 {
  352         struct hpet_softc *sc;
  353 
  354         sc = cdev->si_drv1;
  355         if (offset > rman_get_size(sc->mem_res))
  356                 return (EINVAL);
  357         if (!sc->mmap_allow_write && (nprot & PROT_WRITE))
  358                 return (EPERM);
  359         *paddr = rman_get_start(sc->mem_res) + offset;
  360         *memattr = VM_MEMATTR_UNCACHEABLE;
  361 
  362         return (0);
  363 }
  364 
  365 /* Discover the HPET via the ACPI table of the same name. */
  366 static void
  367 hpet_identify(driver_t *driver, device_t parent)
  368 {
  369         ACPI_TABLE_HPET *hpet;
  370         ACPI_STATUS     status;
  371         device_t        child;
  372         int             i;
  373 
  374         /* Only one HPET device can be added. */
  375         if (devclass_get_device(hpet_devclass, 0))
  376                 return;
  377         for (i = 1; ; i++) {
  378                 /* Search for HPET table. */
  379                 status = AcpiGetTable(ACPI_SIG_HPET, i, (ACPI_TABLE_HEADER **)&hpet);
  380                 if (ACPI_FAILURE(status))
  381                         return;
  382                 /* Search for HPET device with same ID. */
  383                 child = NULL;
  384                 AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
  385                     100, hpet_find, NULL, (void *)(uintptr_t)hpet->Sequence,
  386                     (void *)&child);
  387                 /* If found - let it be probed in normal way. */
  388                 if (child) {
  389                         if (bus_get_resource(child, SYS_RES_MEMORY, 0,
  390                             NULL, NULL) != 0)
  391                                 bus_set_resource(child, SYS_RES_MEMORY, 0,
  392                                     hpet->Address.Address, HPET_MEM_WIDTH);
  393                         continue;
  394                 }
  395                 /* If not - create it from table info. */
  396                 child = BUS_ADD_CHILD(parent, 2, "hpet", 0);
  397                 if (child == NULL) {
  398                         printf("%s: can't add child\n", __func__);
  399                         continue;
  400                 }
  401                 bus_set_resource(child, SYS_RES_MEMORY, 0, hpet->Address.Address,
  402                     HPET_MEM_WIDTH);
  403         }
  404 }
  405 
  406 static int
  407 hpet_probe(device_t dev)
  408 {
  409         ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
  410 
  411         if (acpi_disabled("hpet"))
  412                 return (ENXIO);
  413         if (acpi_get_handle(dev) != NULL &&
  414             ACPI_ID_PROBE(device_get_parent(dev), dev, hpet_ids) == NULL)
  415                 return (ENXIO);
  416 
  417         device_set_desc(dev, "High Precision Event Timer");
  418         return (0);
  419 }
  420 
  421 static int
  422 hpet_attach(device_t dev)
  423 {
  424         struct hpet_softc *sc;
  425         struct hpet_timer *t;
  426         int i, j, num_msi, num_timers, num_percpu_et, num_percpu_t, cur_cpu;
  427         int pcpu_master;
  428         static int maxhpetet = 0;
  429         uint32_t val, val2, cvectors, dvectors;
  430         uint16_t vendor, rev;
  431 
  432         ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
  433 
  434         sc = device_get_softc(dev);
  435         sc->dev = dev;
  436         sc->handle = acpi_get_handle(dev);
  437 
  438         sc->mem_rid = 0;
  439         sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
  440             RF_ACTIVE);
  441         if (sc->mem_res == NULL)
  442                 return (ENOMEM);
  443 
  444         /* Validate that we can access the whole region. */
  445         if (rman_get_size(sc->mem_res) < HPET_MEM_WIDTH) {
  446                 device_printf(dev, "memory region width %ld too small\n",
  447                     rman_get_size(sc->mem_res));
  448                 bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
  449                 return (ENXIO);
  450         }
  451 
  452         /* Be sure timer is enabled. */
  453         hpet_enable(sc);
  454 
  455         /* Read basic statistics about the timer. */
  456         val = bus_read_4(sc->mem_res, HPET_PERIOD);
  457         if (val == 0) {
  458                 device_printf(dev, "invalid period\n");
  459                 hpet_disable(sc);
  460                 bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
  461                 return (ENXIO);
  462         }
  463 
  464         sc->freq = (1000000000000000LL + val / 2) / val;
  465         sc->caps = bus_read_4(sc->mem_res, HPET_CAPABILITIES);
  466         vendor = (sc->caps & HPET_CAP_VENDOR_ID) >> 16;
  467         rev = sc->caps & HPET_CAP_REV_ID;
  468         num_timers = 1 + ((sc->caps & HPET_CAP_NUM_TIM) >> 8);
  469         /*
  470          * ATI/AMD violates IA-PC HPET (High Precision Event Timers)
  471          * Specification and provides an off by one number
  472          * of timers/comparators.
  473          * Additionally, they use unregistered value in VENDOR_ID field.
  474          */
  475         if (vendor == HPET_VENDID_AMD && rev < 0x10 && num_timers > 0)
  476                 num_timers--;
  477         sc->num_timers = num_timers;
  478         if (bootverbose) {
  479                 device_printf(dev,
  480                     "vendor 0x%x, rev 0x%x, %jdHz%s, %d timers,%s\n",
  481                     vendor, rev, sc->freq,
  482                     (sc->caps & HPET_CAP_COUNT_SIZE) ? " 64bit" : "",
  483                     num_timers,
  484                     (sc->caps & HPET_CAP_LEG_RT) ? " legacy route" : "");
  485         }
  486         for (i = 0; i < num_timers; i++) {
  487                 t = &sc->t[i];
  488                 t->sc = sc;
  489                 t->num = i;
  490                 t->mode = TIMER_STOPPED;
  491                 t->intr_rid = -1;
  492                 t->irq = -1;
  493                 t->pcpu_cpu = -1;
  494                 t->pcpu_misrouted = 0;
  495                 t->pcpu_master = -1;
  496                 t->caps = bus_read_4(sc->mem_res, HPET_TIMER_CAP_CNF(i));
  497                 t->vectors = bus_read_4(sc->mem_res, HPET_TIMER_CAP_CNF(i) + 4);
  498                 if (bootverbose) {
  499                         device_printf(dev,
  500                             " t%d: irqs 0x%08x (%d)%s%s%s\n", i,
  501                             t->vectors, (t->caps & HPET_TCNF_INT_ROUTE) >> 9,
  502                             (t->caps & HPET_TCAP_FSB_INT_DEL) ? ", MSI" : "",
  503                             (t->caps & HPET_TCAP_SIZE) ? ", 64bit" : "",
  504                             (t->caps & HPET_TCAP_PER_INT) ? ", periodic" : "");
  505                 }
  506         }
  507         if (testenv("debug.acpi.hpet_test"))
  508                 hpet_test(sc);
  509         /*
  510          * Don't attach if the timer never increments.  Since the spec
  511          * requires it to be at least 10 MHz, it has to change in 1 us.
  512          */
  513         val = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
  514         DELAY(1);
  515         val2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
  516         if (val == val2) {
  517                 device_printf(dev, "HPET never increments, disabling\n");
  518                 hpet_disable(sc);
  519                 bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
  520                 return (ENXIO);
  521         }
  522         /* Announce first HPET as timecounter. */
  523         if (device_get_unit(dev) == 0) {
  524                 sc->tc.tc_get_timecount = hpet_get_timecount,
  525                 sc->tc.tc_counter_mask = ~0u,
  526                 sc->tc.tc_name = "HPET",
  527                 sc->tc.tc_quality = 950,
  528                 sc->tc.tc_frequency = sc->freq;
  529                 sc->tc.tc_priv = sc;
  530                 tc_init(&sc->tc);
  531         }
  532         /* If not disabled - setup and announce event timers. */
  533         if (resource_int_value(device_get_name(dev), device_get_unit(dev),
  534              "clock", &i) == 0 && i == 0)
  535                 return (0);
  536 
  537         /* Check whether we can and want legacy routing. */
  538         sc->legacy_route = 0;
  539         resource_int_value(device_get_name(dev), device_get_unit(dev),
  540              "legacy_route", &sc->legacy_route);
  541         if ((sc->caps & HPET_CAP_LEG_RT) == 0)
  542                 sc->legacy_route = 0;
  543         if (sc->legacy_route) {
  544                 sc->t[0].vectors = 0;
  545                 sc->t[1].vectors = 0;
  546         }
  547 
  548         /* Check what IRQs we want use. */
  549         /* By default allow any PCI IRQs. */
  550         sc->allowed_irqs = 0xffff0000;
  551         /*
  552          * HPETs in AMD chipsets before SB800 have problems with IRQs >= 16
  553          * Lower are also not always working for different reasons.
  554          * SB800 fixed it, but seems do not implements level triggering
  555          * properly, that makes it very unreliable - it freezes after any
  556          * interrupt loss. Avoid legacy IRQs for AMD.
  557          */
  558         if (vendor == HPET_VENDID_AMD || vendor == HPET_VENDID_AMD2)
  559                 sc->allowed_irqs = 0x00000000;
  560         /*
  561          * NVidia MCP5x chipsets have number of unexplained interrupt
  562          * problems. For some reason, using HPET interrupts breaks HDA sound.
  563          */
  564         if (vendor == HPET_VENDID_NVIDIA && rev <= 0x01)
  565                 sc->allowed_irqs = 0x00000000;
  566         /*
  567          * ServerWorks HT1000 reported to have problems with IRQs >= 16.
  568          * Lower IRQs are working, but allowed mask is not set correctly.
  569          * Legacy_route mode works fine.
  570          */
  571         if (vendor == HPET_VENDID_SW && rev <= 0x01)
  572                 sc->allowed_irqs = 0x00000000;
  573         /*
  574          * Neither QEMU nor VirtualBox report supported IRQs correctly.
  575          * The only way to use HPET there is to specify IRQs manually
  576          * and/or use legacy_route. Legacy_route mode works on both.
  577          */
  578         if (vm_guest)
  579                 sc->allowed_irqs = 0x00000000;
  580         /* Let user override. */
  581         resource_int_value(device_get_name(dev), device_get_unit(dev),
  582              "allowed_irqs", &sc->allowed_irqs);
  583 
  584         /* Get how much per-CPU timers we should try to provide. */
  585         sc->per_cpu = 1;
  586         resource_int_value(device_get_name(dev), device_get_unit(dev),
  587              "per_cpu", &sc->per_cpu);
  588 
  589         num_msi = 0;
  590         sc->useirq = 0;
  591         /* Find IRQ vectors for all timers. */
  592         cvectors = sc->allowed_irqs & 0xffff0000;
  593         dvectors = sc->allowed_irqs & 0x0000ffff;
  594         if (sc->legacy_route)
  595                 dvectors &= 0x0000fefe;
  596         for (i = 0; i < num_timers; i++) {
  597                 t = &sc->t[i];
  598                 if (sc->legacy_route && i < 2)
  599                         t->irq = (i == 0) ? 0 : 8;
  600 #ifdef DEV_APIC
  601                 else if (t->caps & HPET_TCAP_FSB_INT_DEL) {
  602                         if ((j = PCIB_ALLOC_MSIX(
  603                             device_get_parent(device_get_parent(dev)), dev,
  604                             &t->irq))) {
  605                                 device_printf(dev,
  606                                     "Can't allocate interrupt for t%d.\n", j);
  607                         }
  608                 }
  609 #endif
  610                 else if (dvectors & t->vectors) {
  611                         t->irq = ffs(dvectors & t->vectors) - 1;
  612                         dvectors &= ~(1 << t->irq);
  613                 }
  614                 if (t->irq >= 0) {
  615                         t->intr_rid = hpet_find_irq_rid(dev, t->irq, t->irq);
  616                         t->intr_res = bus_alloc_resource(dev, SYS_RES_IRQ,
  617                             &t->intr_rid, t->irq, t->irq, 1, RF_ACTIVE);
  618                         if (t->intr_res == NULL) {
  619                                 t->irq = -1;
  620                                 device_printf(dev,
  621                                     "Can't map interrupt for t%d.\n", i);
  622                         } else if (bus_setup_intr(dev, t->intr_res,
  623                             INTR_TYPE_CLK, hpet_intr_single, NULL, t,
  624                             &t->intr_handle) != 0) {
  625                                 t->irq = -1;
  626                                 device_printf(dev,
  627                                     "Can't setup interrupt for t%d.\n", i);
  628                         } else {
  629                                 bus_describe_intr(dev, t->intr_res,
  630                                     t->intr_handle, "t%d", i);
  631                                 num_msi++;
  632                         }
  633                 }
  634                 if (t->irq < 0 && (cvectors & t->vectors) != 0) {
  635                         cvectors &= t->vectors;
  636                         sc->useirq |= (1 << i);
  637                 }
  638         }
  639         if (sc->legacy_route && sc->t[0].irq < 0 && sc->t[1].irq < 0)
  640                 sc->legacy_route = 0;
  641         if (sc->legacy_route)
  642                 hpet_enable(sc);
  643         /* Group timers for per-CPU operation. */
  644         num_percpu_et = min(num_msi / mp_ncpus, sc->per_cpu);
  645         num_percpu_t = num_percpu_et * mp_ncpus;
  646         pcpu_master = 0;
  647         cur_cpu = CPU_FIRST();
  648         for (i = 0; i < num_timers; i++) {
  649                 t = &sc->t[i];
  650                 if (t->irq >= 0 && num_percpu_t > 0) {
  651                         if (cur_cpu == CPU_FIRST())
  652                                 pcpu_master = i;
  653                         t->pcpu_cpu = cur_cpu;
  654                         t->pcpu_master = pcpu_master;
  655                         sc->t[pcpu_master].
  656                             pcpu_slaves[cur_cpu] = i;
  657                         bus_bind_intr(dev, t->intr_res, cur_cpu);
  658                         cur_cpu = CPU_NEXT(cur_cpu);
  659                         num_percpu_t--;
  660                 } else if (t->irq >= 0)
  661                         bus_bind_intr(dev, t->intr_res, CPU_FIRST());
  662         }
  663         bus_write_4(sc->mem_res, HPET_ISR, 0xffffffff);
  664         sc->irq = -1;
  665         /* If at least one timer needs legacy IRQ - set it up. */
  666         if (sc->useirq) {
  667                 j = i = fls(cvectors) - 1;
  668                 while (j > 0 && (cvectors & (1 << (j - 1))) != 0)
  669                         j--;
  670                 sc->intr_rid = hpet_find_irq_rid(dev, j, i);
  671                 sc->intr_res = bus_alloc_resource(dev, SYS_RES_IRQ,
  672                     &sc->intr_rid, j, i, 1, RF_SHAREABLE | RF_ACTIVE);
  673                 if (sc->intr_res == NULL)
  674                         device_printf(dev, "Can't map interrupt.\n");
  675                 else if (bus_setup_intr(dev, sc->intr_res, INTR_TYPE_CLK,
  676                     hpet_intr, NULL, sc, &sc->intr_handle) != 0) {
  677                         device_printf(dev, "Can't setup interrupt.\n");
  678                 } else {
  679                         sc->irq = rman_get_start(sc->intr_res);
  680                         /* Bind IRQ to BSP to avoid live migration. */
  681                         bus_bind_intr(dev, sc->intr_res, CPU_FIRST());
  682                 }
  683         }
  684         /* Program and announce event timers. */
  685         for (i = 0; i < num_timers; i++) {
  686                 t = &sc->t[i];
  687                 t->caps &= ~(HPET_TCNF_FSB_EN | HPET_TCNF_INT_ROUTE);
  688                 t->caps &= ~(HPET_TCNF_VAL_SET | HPET_TCNF_INT_ENB);
  689                 t->caps &= ~(HPET_TCNF_INT_TYPE);
  690                 t->caps |= HPET_TCNF_32MODE;
  691                 if (t->irq >= 0 && sc->legacy_route && i < 2) {
  692                         /* Legacy route doesn't need more configuration. */
  693                 } else
  694 #ifdef DEV_APIC
  695                 if ((t->caps & HPET_TCAP_FSB_INT_DEL) && t->irq >= 0) {
  696                         uint64_t addr;
  697                         uint32_t data;
  698 
  699                         if (PCIB_MAP_MSI(
  700                             device_get_parent(device_get_parent(dev)), dev,
  701                             t->irq, &addr, &data) == 0) {
  702                                 bus_write_4(sc->mem_res,
  703                                     HPET_TIMER_FSB_ADDR(i), addr);
  704                                 bus_write_4(sc->mem_res,
  705                                     HPET_TIMER_FSB_VAL(i), data);
  706                                 t->caps |= HPET_TCNF_FSB_EN;
  707                         } else
  708                                 t->irq = -2;
  709                 } else
  710 #endif
  711                 if (t->irq >= 0)
  712                         t->caps |= (t->irq << 9);
  713                 else if (sc->irq >= 0 && (t->vectors & (1 << sc->irq)))
  714                         t->caps |= (sc->irq << 9) | HPET_TCNF_INT_TYPE;
  715                 bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(i), t->caps);
  716                 /* Skip event timers without set up IRQ. */
  717                 if (t->irq < 0 &&
  718                     (sc->irq < 0 || (t->vectors & (1 << sc->irq)) == 0))
  719                         continue;
  720                 /* Announce the reset. */
  721                 if (maxhpetet == 0)
  722                         t->et.et_name = "HPET";
  723                 else {
  724                         sprintf(t->name, "HPET%d", maxhpetet);
  725                         t->et.et_name = t->name;
  726                 }
  727                 t->et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT;
  728                 t->et.et_quality = 450;
  729                 if (t->pcpu_master >= 0) {
  730                         t->et.et_flags |= ET_FLAGS_PERCPU;
  731                         t->et.et_quality += 100;
  732                 } else if (mp_ncpus >= 8)
  733                         t->et.et_quality -= 100;
  734                 if ((t->caps & HPET_TCAP_PER_INT) == 0)
  735                         t->et.et_quality -= 10;
  736                 t->et.et_frequency = sc->freq;
  737                 t->et.et_min_period =
  738                     ((uint64_t)(HPET_MIN_CYCLES * 2) << 32) / sc->freq;
  739                 t->et.et_max_period = (0xfffffffeLLU << 32) / sc->freq;
  740                 t->et.et_start = hpet_start;
  741                 t->et.et_stop = hpet_stop;
  742                 t->et.et_priv = &sc->t[i];
  743                 if (t->pcpu_master < 0 || t->pcpu_master == i) {
  744                         et_register(&t->et);
  745                         maxhpetet++;
  746                 }
  747         }
  748 
  749         sc->pdev = make_dev(&hpet_cdevsw, 0, UID_ROOT, GID_WHEEL,
  750             0600, "hpet%d", device_get_unit(dev));
  751         if (sc->pdev) {
  752                 sc->pdev->si_drv1 = sc;
  753                 sc->mmap_allow = 1;
  754                 TUNABLE_INT_FETCH("hw.acpi.hpet.mmap_allow",
  755                     &sc->mmap_allow);
  756                 sc->mmap_allow_write = 1;
  757                 TUNABLE_INT_FETCH("hw.acpi.hpet.mmap_allow_write",
  758                     &sc->mmap_allow_write);
  759                 SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
  760                     SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
  761                     OID_AUTO, "mmap_allow",
  762                     CTLFLAG_RW, &sc->mmap_allow, 0,
  763                     "Allow userland to memory map HPET");
  764                 SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
  765                     SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
  766                     OID_AUTO, "mmap_allow_write",
  767                     CTLFLAG_RW, &sc->mmap_allow_write, 0,
  768                     "Allow userland write to the HPET register space");
  769         } else
  770                 device_printf(dev, "could not create /dev/hpet%d\n",
  771                     device_get_unit(dev));
  772 
  773         return (0);
  774 }
  775 
  776 static int
  777 hpet_detach(device_t dev)
  778 {
  779         ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
  780 
  781         /* XXX Without a tc_remove() function, we can't detach. */
  782         return (EBUSY);
  783 }
  784 
  785 static int
  786 hpet_suspend(device_t dev)
  787 {
  788 //      struct hpet_softc *sc;
  789 
  790         /*
  791          * Disable the timer during suspend.  The timer will not lose
  792          * its state in S1 or S2, but we are required to disable
  793          * it.
  794          */
  795 //      sc = device_get_softc(dev);
  796 //      hpet_disable(sc);
  797 
  798         return (0);
  799 }
  800 
  801 static int
  802 hpet_resume(device_t dev)
  803 {
  804         struct hpet_softc *sc;
  805         struct hpet_timer *t;
  806         int i;
  807 
  808         /* Re-enable the timer after a resume to keep the clock advancing. */
  809         sc = device_get_softc(dev);
  810         hpet_enable(sc);
  811         /* Restart event timers that were running on suspend. */
  812         for (i = 0; i < sc->num_timers; i++) {
  813                 t = &sc->t[i];
  814 #ifdef DEV_APIC
  815                 if (t->irq >= 0 && (sc->legacy_route == 0 || i >= 2)) {
  816                         uint64_t addr;
  817                         uint32_t data;
  818 
  819                         if (PCIB_MAP_MSI(
  820                             device_get_parent(device_get_parent(dev)), dev,
  821                             t->irq, &addr, &data) == 0) {
  822                                 bus_write_4(sc->mem_res,
  823                                     HPET_TIMER_FSB_ADDR(i), addr);
  824                                 bus_write_4(sc->mem_res,
  825                                     HPET_TIMER_FSB_VAL(i), data);
  826                         }
  827                 }
  828 #endif
  829                 if (t->mode == TIMER_STOPPED)
  830                         continue;
  831                 t->next = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
  832                 if (t->mode == TIMER_PERIODIC &&
  833                     (t->caps & HPET_TCAP_PER_INT) != 0) {
  834                         t->caps |= HPET_TCNF_TYPE;
  835                         t->next += t->div;
  836                         bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num),
  837                             t->caps | HPET_TCNF_VAL_SET);
  838                         bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
  839                             t->next);
  840                         bus_read_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num));
  841                         bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
  842                             t->div);
  843                 } else {
  844                         t->next += sc->freq / 1024;
  845                         bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
  846                             t->next);
  847                 }
  848                 bus_write_4(sc->mem_res, HPET_ISR, 1 << t->num);
  849                 bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num), t->caps);
  850         }
  851         return (0);
  852 }
  853 
  854 /* Print some basic latency/rate information to assist in debugging. */
  855 static void
  856 hpet_test(struct hpet_softc *sc)
  857 {
  858         int i;
  859         uint32_t u1, u2;
  860         struct bintime b0, b1, b2;
  861         struct timespec ts;
  862 
  863         binuptime(&b0);
  864         binuptime(&b0);
  865         binuptime(&b1);
  866         u1 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
  867         for (i = 1; i < 1000; i++)
  868                 u2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
  869         binuptime(&b2);
  870         u2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
  871 
  872         bintime_sub(&b2, &b1);
  873         bintime_sub(&b1, &b0);
  874         bintime_sub(&b2, &b1);
  875         bintime2timespec(&b2, &ts);
  876 
  877         device_printf(sc->dev, "%ld.%09ld: %u ... %u = %u\n",
  878             (long)ts.tv_sec, ts.tv_nsec, u1, u2, u2 - u1);
  879 
  880         device_printf(sc->dev, "time per call: %ld ns\n", ts.tv_nsec / 1000);
  881 }
  882 
  883 #ifdef DEV_APIC
  884 static int
  885 hpet_remap_intr(device_t dev, device_t child, u_int irq)
  886 {
  887         struct hpet_softc *sc = device_get_softc(dev);
  888         struct hpet_timer *t;
  889         uint64_t addr;
  890         uint32_t data;
  891         int error, i;
  892 
  893         for (i = 0; i < sc->num_timers; i++) {
  894                 t = &sc->t[i];
  895                 if (t->irq != irq)
  896                         continue;
  897                 error = PCIB_MAP_MSI(
  898                     device_get_parent(device_get_parent(dev)), dev,
  899                     irq, &addr, &data);
  900                 if (error)
  901                         return (error);
  902                 hpet_disable(sc); /* Stop timer to avoid interrupt loss. */
  903                 bus_write_4(sc->mem_res, HPET_TIMER_FSB_ADDR(i), addr);
  904                 bus_write_4(sc->mem_res, HPET_TIMER_FSB_VAL(i), data);
  905                 hpet_enable(sc);
  906                 return (0);
  907         }
  908         return (ENOENT);
  909 }
  910 #endif
  911 
  912 static device_method_t hpet_methods[] = {
  913         /* Device interface */
  914         DEVMETHOD(device_identify, hpet_identify),
  915         DEVMETHOD(device_probe, hpet_probe),
  916         DEVMETHOD(device_attach, hpet_attach),
  917         DEVMETHOD(device_detach, hpet_detach),
  918         DEVMETHOD(device_suspend, hpet_suspend),
  919         DEVMETHOD(device_resume, hpet_resume),
  920 
  921 #ifdef DEV_APIC
  922         DEVMETHOD(bus_remap_intr, hpet_remap_intr),
  923 #endif
  924 
  925         DEVMETHOD_END
  926 };
  927 
  928 static driver_t hpet_driver = {
  929         "hpet",
  930         hpet_methods,
  931         sizeof(struct hpet_softc),
  932 };
  933 
  934 DRIVER_MODULE(hpet, acpi, hpet_driver, hpet_devclass, 0, 0);
  935 MODULE_DEPEND(hpet, acpi, 1, 1, 1);

Cache object: ca6decde3ea511460aac083fe6bd6a49


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