LCOV - code coverage report
Current view: top level - dev/acpi - acpihpet.c (source / functions) Hit Total Coverage
Test: 6.4 Lines: 0 118 0.0 %
Date: 2018-10-19 03:25:38 Functions: 0 6 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* $OpenBSD: acpihpet.c,v 1.23 2018/06/29 17:39:18 kettenis Exp $ */
       2             : /*
       3             :  * Copyright (c) 2005 Thorsten Lockert <tholo@sigmasoft.com>
       4             :  *
       5             :  * Permission to use, copy, modify, and distribute this software for any
       6             :  * purpose with or without fee is hereby granted, provided that the above
       7             :  * copyright notice and this permission notice appear in all copies.
       8             :  *
       9             :  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      10             :  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
      11             :  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
      12             :  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
      13             :  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
      14             :  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
      15             :  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
      16             :  */
      17             : 
      18             : #include <sys/param.h>
      19             : #include <sys/systm.h>
      20             : #include <sys/device.h>
      21             : #include <sys/timetc.h>
      22             : 
      23             : #include <machine/bus.h>
      24             : 
      25             : #include <dev/acpi/acpireg.h>
      26             : #include <dev/acpi/acpivar.h>
      27             : #include <dev/acpi/acpidev.h>
      28             : 
      29             : int acpihpet_attached;
      30             : 
      31             : int acpihpet_match(struct device *, void *, void *);
      32             : void acpihpet_attach(struct device *, struct device *, void *);
      33             : int acpihpet_activate(struct device *, int);
      34             : 
      35             : u_int acpihpet_gettime(struct timecounter *tc);
      36             : 
      37             : uint64_t        acpihpet_r(bus_space_tag_t _iot, bus_space_handle_t _ioh,
      38             :                     bus_size_t _ioa);
      39             : void            acpihpet_w(bus_space_tag_t _iot, bus_space_handle_t _ioh,
      40             :                     bus_size_t _ioa, uint64_t _val);
      41             : 
      42             : static struct timecounter hpet_timecounter = {
      43             :         acpihpet_gettime,       /* get_timecount */
      44             :         0,                      /* no poll_pps */
      45             :         0xffffffff,             /* counter_mask (32 bits) */
      46             :         0,                      /* frequency */
      47             :         0,                      /* name */
      48             :         1000                    /* quality */
      49             : };
      50             : 
      51             : #define HPET_TIMERS     3
      52             : struct hpet_regs {
      53             :         uint64_t        configuration;
      54             :         uint64_t        interrupt_status;
      55             :         uint64_t        main_counter;
      56             :         struct {        /* timers */
      57             :                 uint64_t config;
      58             :                 uint64_t compare;
      59             :                 uint64_t interrupt;
      60             :         } timers[HPET_TIMERS];
      61             : };
      62             : 
      63             : struct acpihpet_softc {
      64             :         struct device           sc_dev;
      65             : 
      66             :         bus_space_tag_t         sc_iot;
      67             :         bus_space_handle_t      sc_ioh;
      68             : 
      69             :         uint32_t                sc_conf;
      70             :         struct hpet_regs        sc_save;
      71             : };
      72             : 
      73             : struct cfattach acpihpet_ca = {
      74             :         sizeof(struct acpihpet_softc), acpihpet_match, acpihpet_attach,
      75             :         NULL, acpihpet_activate
      76             : };
      77             : 
      78             : struct cfdriver acpihpet_cd = {
      79             :         NULL, "acpihpet", DV_DULL
      80             : };
      81             : 
      82             : uint64_t
      83           0 : acpihpet_r(bus_space_tag_t iot, bus_space_handle_t ioh, bus_size_t ioa)
      84             : {
      85             :         uint64_t val;
      86             : 
      87           0 :         val = bus_space_read_4(iot, ioh, ioa + 4);
      88           0 :         val = val << 32;
      89           0 :         val |= bus_space_read_4(iot, ioh, ioa);
      90           0 :         return (val);
      91             : }
      92             : 
      93             : void
      94           0 : acpihpet_w(bus_space_tag_t iot, bus_space_handle_t ioh, bus_size_t ioa,
      95             :     uint64_t val)
      96             : {
      97           0 :         bus_space_write_4(iot, ioh, ioa + 4, val >> 32);
      98           0 :         bus_space_write_4(iot, ioh, ioa, val & 0xffffffff);
      99           0 : }
     100             : 
     101             : int
     102           0 : acpihpet_activate(struct device *self, int act)
     103             : {
     104           0 :         struct acpihpet_softc *sc = (struct acpihpet_softc *) self;
     105             : 
     106           0 :         switch (act) {
     107             :         case DVACT_SUSPEND:
     108             :                 /* stop, then save */
     109           0 :                 bus_space_write_4(sc->sc_iot, sc->sc_ioh,
     110             :                     HPET_CONFIGURATION, sc->sc_conf);
     111             : 
     112           0 :                 sc->sc_save.configuration = acpihpet_r(sc->sc_iot,
     113           0 :                     sc->sc_ioh, HPET_CONFIGURATION);
     114           0 :                 sc->sc_save.interrupt_status = acpihpet_r(sc->sc_iot,
     115           0 :                     sc->sc_ioh, HPET_INTERRUPT_STATUS);
     116           0 :                 sc->sc_save.main_counter = acpihpet_r(sc->sc_iot,
     117           0 :                     sc->sc_ioh, HPET_MAIN_COUNTER);
     118           0 :                 sc->sc_save.timers[0].config = acpihpet_r(sc->sc_iot,
     119           0 :                     sc->sc_ioh, HPET_TIMER0_CONFIG);
     120           0 :                 sc->sc_save.timers[0].interrupt = acpihpet_r(sc->sc_iot,
     121           0 :                     sc->sc_ioh, HPET_TIMER0_INTERRUPT);
     122           0 :                 sc->sc_save.timers[0].compare = acpihpet_r(sc->sc_iot,
     123           0 :                     sc->sc_ioh, HPET_TIMER0_COMPARE);
     124           0 :                 sc->sc_save.timers[1].config = acpihpet_r(sc->sc_iot,
     125           0 :                     sc->sc_ioh, HPET_TIMER1_CONFIG);
     126           0 :                 sc->sc_save.timers[1].interrupt = acpihpet_r(sc->sc_iot,
     127           0 :                     sc->sc_ioh, HPET_TIMER1_INTERRUPT);
     128           0 :                 sc->sc_save.timers[1].compare = acpihpet_r(sc->sc_iot,
     129           0 :                     sc->sc_ioh, HPET_TIMER1_COMPARE);
     130           0 :                 sc->sc_save.timers[2].config = acpihpet_r(sc->sc_iot,
     131           0 :                     sc->sc_ioh, HPET_TIMER2_CONFIG);
     132           0 :                 sc->sc_save.timers[2].interrupt = acpihpet_r(sc->sc_iot,
     133           0 :                     sc->sc_ioh, HPET_TIMER2_INTERRUPT);
     134           0 :                 sc->sc_save.timers[2].compare = acpihpet_r(sc->sc_iot,
     135           0 :                     sc->sc_ioh, HPET_TIMER2_COMPARE);
     136           0 :                 break;
     137             :         case DVACT_RESUME:
     138             :                 /* stop, restore, then restart */
     139           0 :                 bus_space_write_4(sc->sc_iot, sc->sc_ioh,
     140             :                     HPET_CONFIGURATION, sc->sc_conf);
     141             : 
     142           0 :                 acpihpet_w(sc->sc_iot, sc->sc_ioh,
     143           0 :                     HPET_CONFIGURATION, sc->sc_save.configuration);
     144           0 :                 acpihpet_w(sc->sc_iot, sc->sc_ioh,
     145           0 :                     HPET_INTERRUPT_STATUS, sc->sc_save.interrupt_status);
     146           0 :                 acpihpet_w(sc->sc_iot, sc->sc_ioh,
     147           0 :                     HPET_MAIN_COUNTER, sc->sc_save.main_counter);
     148           0 :                 acpihpet_w(sc->sc_iot, sc->sc_ioh,
     149           0 :                     HPET_TIMER0_CONFIG, sc->sc_save.timers[0].config);
     150           0 :                 acpihpet_w(sc->sc_iot, sc->sc_ioh,
     151           0 :                     HPET_TIMER0_INTERRUPT, sc->sc_save.timers[0].interrupt);
     152           0 :                 acpihpet_w(sc->sc_iot, sc->sc_ioh,
     153           0 :                     HPET_TIMER0_COMPARE, sc->sc_save.timers[0].compare);
     154           0 :                 acpihpet_w(sc->sc_iot, sc->sc_ioh,
     155           0 :                     HPET_TIMER1_CONFIG, sc->sc_save.timers[1].config);
     156           0 :                 acpihpet_w(sc->sc_iot, sc->sc_ioh,
     157           0 :                     HPET_TIMER1_INTERRUPT, sc->sc_save.timers[1].interrupt);
     158           0 :                 acpihpet_w(sc->sc_iot, sc->sc_ioh,
     159           0 :                     HPET_TIMER1_COMPARE, sc->sc_save.timers[1].compare);
     160           0 :                 acpihpet_w(sc->sc_iot, sc->sc_ioh,
     161           0 :                     HPET_TIMER2_CONFIG, sc->sc_save.timers[2].config);
     162           0 :                 acpihpet_w(sc->sc_iot, sc->sc_ioh,
     163           0 :                     HPET_TIMER2_INTERRUPT, sc->sc_save.timers[2].interrupt);
     164           0 :                 acpihpet_w(sc->sc_iot, sc->sc_ioh,
     165           0 :                     HPET_TIMER2_COMPARE, sc->sc_save.timers[2].compare);
     166           0 :                 bus_space_write_4(sc->sc_iot, sc->sc_ioh,
     167             :                     HPET_CONFIGURATION, sc->sc_conf | 1);
     168           0 :                 break;
     169             :         }
     170             : 
     171           0 :         return 0;
     172             : }
     173             : 
     174             : int
     175           0 : acpihpet_match(struct device *parent, void *match, void *aux)
     176             : {
     177           0 :         struct acpi_attach_args *aaa = aux;
     178             :         struct acpi_table_header *hdr;
     179             : 
     180             :         /*
     181             :          * If we do not have a table, it is not us; attach only once
     182             :          */
     183           0 :         if (acpihpet_attached || aaa->aaa_table == NULL)
     184           0 :                 return (0);
     185             : 
     186             :         /*
     187             :          * If it is an HPET table, we can attach
     188             :          */
     189           0 :         hdr = (struct acpi_table_header *)aaa->aaa_table;
     190           0 :         if (memcmp(hdr->signature, HPET_SIG, sizeof(HPET_SIG) - 1) != 0)
     191           0 :                 return (0);
     192             : 
     193           0 :         return (1);
     194           0 : }
     195             : 
     196             : void
     197           0 : acpihpet_attach(struct device *parent, struct device *self, void *aux)
     198             : {
     199           0 :         struct acpihpet_softc *sc = (struct acpihpet_softc *) self;
     200           0 :         struct acpi_softc *psc = (struct acpi_softc *)parent;
     201           0 :         struct acpi_attach_args *aaa = aux;
     202           0 :         struct acpi_hpet *hpet = (struct acpi_hpet *)aaa->aaa_table;
     203             :         uint64_t period, freq;  /* timer period in femtoseconds (10^-15) */
     204             :         uint32_t v1, v2;
     205             :         int timeout;
     206             : 
     207           0 :         if (acpi_map_address(psc, &hpet->base_address, 0, HPET_REG_SIZE,
     208           0 :             &sc->sc_ioh, &sc->sc_iot))    {
     209           0 :                 printf(": can't map i/o space\n");
     210           0 :                 return;
     211             :         }
     212             : 
     213             :         /*
     214             :          * Revisions 0x30 through 0x3a of the AMD SB700, with spread
     215             :          * spectrum enabled, have an SMM based HPET emulation that's
     216             :          * subtly broken.  The hardware is initialized upon first
     217             :          * access of the configuration register.  Initialization takes
     218             :          * some time during which the configuration register returns
     219             :          * 0xffffffff.
     220             :          */
     221             :         timeout = 1000;
     222           0 :         do {
     223           0 :                 if (bus_space_read_4(sc->sc_iot, sc->sc_ioh,
     224           0 :                     HPET_CONFIGURATION) != 0xffffffff)
     225             :                         break;
     226           0 :         } while(--timeout > 0);
     227             : 
     228           0 :         if (timeout == 0) {
     229           0 :                 printf(": disabled\n");
     230           0 :                 return;
     231             :         }
     232             : 
     233             :         /* enable hpet */
     234           0 :         sc->sc_conf = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
     235           0 :             HPET_CONFIGURATION) & ~1;
     236           0 :         bus_space_write_4(sc->sc_iot, sc->sc_ioh, HPET_CONFIGURATION,
     237             :             sc->sc_conf | 1);
     238             : 
     239             :         /* make sure hpet is working */
     240           0 :         v1 = bus_space_read_4(sc->sc_iot, sc->sc_ioh, HPET_MAIN_COUNTER);
     241           0 :         delay(1);
     242           0 :         v2 = bus_space_read_4(sc->sc_iot, sc->sc_ioh, HPET_MAIN_COUNTER);
     243           0 :         if (v1 == v2) {
     244           0 :                 printf(": counter not incrementing\n");
     245           0 :                 bus_space_write_4(sc->sc_iot, sc->sc_ioh,
     246             :                     HPET_CONFIGURATION, sc->sc_conf);
     247           0 :                 return;
     248             :         }
     249             : 
     250           0 :         period = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
     251             :             HPET_CAPABILITIES + sizeof(uint32_t));
     252             : 
     253             :         /* Period must be > 0 and less than 100ns (10^8 fs) */
     254           0 :         if (period == 0 || period > HPET_MAX_PERIOD) {
     255           0 :                 printf(": invalid period\n");
     256           0 :                 bus_space_write_4(sc->sc_iot, sc->sc_ioh,
     257             :                     HPET_CONFIGURATION, sc->sc_conf);
     258           0 :                 return;
     259             :         }
     260           0 :         freq = 1000000000000000ull / period;
     261           0 :         printf(": %lld Hz\n", freq);
     262             : 
     263           0 :         hpet_timecounter.tc_frequency = (uint32_t)freq;
     264           0 :         hpet_timecounter.tc_priv = sc;
     265           0 :         hpet_timecounter.tc_name = sc->sc_dev.dv_xname;
     266           0 :         tc_init(&hpet_timecounter);
     267             : #if defined(__amd64__)
     268             :         extern void cpu_recalibrate_tsc(struct timecounter *);
     269           0 :         cpu_recalibrate_tsc(&hpet_timecounter);
     270             : #endif
     271           0 :         acpihpet_attached++;
     272           0 : }
     273             : 
     274             : u_int
     275           0 : acpihpet_gettime(struct timecounter *tc)
     276             : {
     277           0 :         struct acpihpet_softc *sc = tc->tc_priv;
     278             : 
     279           0 :         return (bus_space_read_4(sc->sc_iot, sc->sc_ioh, HPET_MAIN_COUNTER));
     280             : }

Generated by: LCOV version 1.13