xref: /openbsd-src/sys/dev/acpi/acpitimer.c (revision 24ee467d1e5e812429867003c29e5339cd18bad7)
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