1*24ee467dScheloha /* $OpenBSD: acpitimer.c,v 1.17 2023/02/04 19:19:37 cheloha Exp $ */
27934d707Stholo /*
37934d707Stholo * Copyright (c) 2005 Thorsten Lockert <tholo@sigmasoft.com>
47934d707Stholo *
57934d707Stholo * Permission to use, copy, modify, and distribute this software for any
67934d707Stholo * purpose with or without fee is hereby granted, provided that the above
77934d707Stholo * copyright notice and this permission notice appear in all copies.
87934d707Stholo *
97934d707Stholo * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
107934d707Stholo * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
117934d707Stholo * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
127934d707Stholo * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
137934d707Stholo * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
147934d707Stholo * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
157934d707Stholo * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
167934d707Stholo */
177934d707Stholo
187934d707Stholo #include <sys/param.h>
197934d707Stholo #include <sys/systm.h>
207934d707Stholo #include <sys/device.h>
21a00231d3Scheloha #include <sys/stdint.h>
227934d707Stholo #include <sys/timetc.h>
237934d707Stholo
247934d707Stholo #include <machine/bus.h>
25a00231d3Scheloha #include <machine/cpu.h>
267934d707Stholo
277934d707Stholo #include <dev/acpi/acpireg.h>
287934d707Stholo #include <dev/acpi/acpivar.h>
297934d707Stholo
30a00231d3Scheloha struct acpitimer_softc {
31a00231d3Scheloha struct device sc_dev;
32a00231d3Scheloha
33a00231d3Scheloha bus_space_tag_t sc_iot;
34a00231d3Scheloha bus_space_handle_t sc_ioh;
35a00231d3Scheloha };
36a00231d3Scheloha
377934d707Stholo int acpitimermatch(struct device *, void *, void *);
387934d707Stholo void acpitimerattach(struct device *, struct device *, void *);
39a00231d3Scheloha void acpitimer_delay(int);
407934d707Stholo u_int acpi_get_timecount(struct timecounter *tc);
41a00231d3Scheloha uint32_t acpitimer_read(struct acpitimer_softc *);
427934d707Stholo
437934d707Stholo static struct timecounter acpi_timecounter = {
448611d3cdScheloha .tc_get_timecount = acpi_get_timecount,
458611d3cdScheloha .tc_counter_mask = 0x00ffffff, /* 24 bits */
468611d3cdScheloha .tc_frequency = ACPI_FREQUENCY,
478611d3cdScheloha .tc_name = 0,
488611d3cdScheloha .tc_quality = 1000,
498611d3cdScheloha .tc_priv = NULL,
508611d3cdScheloha .tc_user = 0,
517934d707Stholo };
527934d707Stholo
53471aeecfSnaddy const struct cfattach acpitimer_ca = {
547934d707Stholo sizeof(struct acpitimer_softc), acpitimermatch, acpitimerattach
557934d707Stholo };
567934d707Stholo
577934d707Stholo struct cfdriver acpitimer_cd = {
587934d707Stholo NULL, "acpitimer", DV_DULL
597934d707Stholo };
607934d707Stholo
617934d707Stholo int
acpitimermatch(struct device * parent,void * match,void * aux)627934d707Stholo acpitimermatch(struct device *parent, void *match, void *aux)
637934d707Stholo {
647934d707Stholo struct acpi_attach_args *aa = aux;
657934d707Stholo struct cfdata *cf = match;
667934d707Stholo
677934d707Stholo /* sanity */
687934d707Stholo if (aa->aaa_name == NULL ||
697934d707Stholo strcmp(aa->aaa_name, cf->cf_driver->cd_name) != 0 ||
707934d707Stholo aa->aaa_table != NULL)
717934d707Stholo return (0);
727934d707Stholo
737934d707Stholo return (1);
747934d707Stholo }
757934d707Stholo
767934d707Stholo void
acpitimerattach(struct device * parent,struct device * self,void * aux)777934d707Stholo acpitimerattach(struct device *parent, struct device *self, void *aux)
787934d707Stholo {
797934d707Stholo struct acpitimer_softc *sc = (struct acpitimer_softc *) self;
807934d707Stholo struct acpi_softc *psc = (struct acpi_softc *) parent;
81b61ce84dSjordan int rc;
827934d707Stholo
83f2da8145Smikeb if (psc->sc_fadt->hdr_revision >= 3 &&
84f2da8145Smikeb psc->sc_fadt->x_pm_tmr_blk.address != 0)
85b61ce84dSjordan rc = acpi_map_address(psc, &psc->sc_fadt->x_pm_tmr_blk, 0,
86bde24260Smarco psc->sc_fadt->pm_tmr_len, &sc->sc_ioh, &sc->sc_iot);
87b61ce84dSjordan else
88b61ce84dSjordan rc = acpi_map_address(psc, NULL, psc->sc_fadt->pm_tmr_blk,
89bde24260Smarco psc->sc_fadt->pm_tmr_len, &sc->sc_ioh, &sc->sc_iot);
90b61ce84dSjordan if (rc) {
917934d707Stholo printf(": can't map i/o space\n");
927934d707Stholo return;
937934d707Stholo }
947934d707Stholo
9576390d40Sjordan printf(": %d Hz, %d bits\n", ACPI_FREQUENCY,
967934d707Stholo psc->sc_fadt->flags & FADT_TMR_VAL_EXT ? 32 : 24);
977934d707Stholo
987934d707Stholo if (psc->sc_fadt->flags & FADT_TMR_VAL_EXT)
997934d707Stholo acpi_timecounter.tc_counter_mask = 0xffffffffU;
1007934d707Stholo acpi_timecounter.tc_priv = sc;
1017934d707Stholo acpi_timecounter.tc_name = sc->sc_dev.dv_xname;
1027934d707Stholo tc_init(&acpi_timecounter);
103a00231d3Scheloha
104a00231d3Scheloha delay_init(acpitimer_delay, 1000);
105a00231d3Scheloha
106eb35b7b4Smikeb #if defined(__amd64__)
107eb35b7b4Smikeb extern void cpu_recalibrate_tsc(struct timecounter *);
108eb35b7b4Smikeb cpu_recalibrate_tsc(&acpi_timecounter);
109eb35b7b4Smikeb #endif
1107934d707Stholo }
1117934d707Stholo
112a00231d3Scheloha void
acpitimer_delay(int usecs)113a00231d3Scheloha acpitimer_delay(int usecs)
114a00231d3Scheloha {
115a00231d3Scheloha uint64_t count = 0, cycles;
116a00231d3Scheloha struct acpitimer_softc *sc = acpi_timecounter.tc_priv;
117a00231d3Scheloha uint32_t mask = acpi_timecounter.tc_counter_mask;
118a00231d3Scheloha uint32_t val1, val2;
119a00231d3Scheloha
120a00231d3Scheloha val2 = acpitimer_read(sc);
121a00231d3Scheloha cycles = usecs * acpi_timecounter.tc_frequency / 1000000;
122a00231d3Scheloha while (count < cycles) {
123a00231d3Scheloha CPU_BUSY_CYCLE();
124a00231d3Scheloha val1 = val2;
125a00231d3Scheloha val2 = acpitimer_read(sc);
126a00231d3Scheloha count += (val2 - val1) & mask;
127a00231d3Scheloha }
128a00231d3Scheloha }
1297934d707Stholo
1307934d707Stholo u_int
acpi_get_timecount(struct timecounter * tc)1317934d707Stholo acpi_get_timecount(struct timecounter *tc)
1327934d707Stholo {
133a00231d3Scheloha return acpitimer_read(tc->tc_priv);
134a00231d3Scheloha }
135a00231d3Scheloha
136a00231d3Scheloha uint32_t
acpitimer_read(struct acpitimer_softc * sc)137a00231d3Scheloha acpitimer_read(struct acpitimer_softc *sc)
138a00231d3Scheloha {
139a00231d3Scheloha uint32_t u1, u2, u3;
1407934d707Stholo
1417934d707Stholo u2 = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 0);
1427934d707Stholo u3 = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 0);
1437934d707Stholo do {
1447934d707Stholo u1 = u2;
1457934d707Stholo u2 = u3;
1467934d707Stholo u3 = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 0);
1477934d707Stholo } while (u1 > u2 || u2 > u3);
148bde24260Smarco
1497934d707Stholo return (u2);
1507934d707Stholo }
151