acpi_hpet.c revision 168010
1151912Sphk/*-
2151912Sphk * Copyright (c) 2005 Poul-Henning Kamp
3151912Sphk * All rights reserved.
4151912Sphk *
5151912Sphk * Redistribution and use in source and binary forms, with or without
6151912Sphk * modification, are permitted provided that the following conditions
7151912Sphk * are met:
8151912Sphk * 1. Redistributions of source code must retain the above copyright
9151912Sphk *    notice, this list of conditions and the following disclaimer.
10151912Sphk * 2. Redistributions in binary form must reproduce the above copyright
11151912Sphk *    notice, this list of conditions and the following disclaimer in the
12151912Sphk *    documentation and/or other materials provided with the distribution.
13151912Sphk *
14151912Sphk * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15151912Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16151912Sphk * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17151912Sphk * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18151912Sphk * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19151912Sphk * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20151912Sphk * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21151912Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22151912Sphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23151912Sphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24151912Sphk * SUCH DAMAGE.
25151912Sphk */
26151912Sphk
27151912Sphk#include <sys/cdefs.h>
28151912Sphk__FBSDID("$FreeBSD: head/sys/dev/acpica/acpi_hpet.c 168010 2007-03-28 22:28:48Z njl $");
29151912Sphk
30151912Sphk#include "opt_acpi.h"
31151912Sphk#include <sys/param.h>
32159217Snjl#include <sys/bus.h>
33151912Sphk#include <sys/kernel.h>
34151912Sphk#include <sys/module.h>
35151912Sphk#include <sys/rman.h>
36151912Sphk#include <sys/time.h>
37151912Sphk#include <sys/timetc.h>
38159217Snjl
39151912Sphk#include <contrib/dev/acpica/acpi.h>
40151912Sphk#include <dev/acpica/acpivar.h>
41151912Sphk
42151912SphkACPI_SERIAL_DECL(hpet, "ACPI HPET support");
43151912Sphk
44151931Sscottl/* ACPI CA debugging */
45151935Sscottl#define _COMPONENT	ACPI_TIMER
46151931SscottlACPI_MODULE_NAME("HPET")
47151931Sscottl
48151912Sphkstruct acpi_hpet_softc {
49151912Sphk	device_t		dev;
50159217Snjl	struct resource		*mem_res;
51151912Sphk	ACPI_HANDLE		handle;
52151912Sphk};
53151912Sphk
54159217Snjlstatic u_int hpet_get_timecount(struct timecounter *tc);
55159217Snjlstatic void acpi_hpet_test(struct acpi_hpet_softc *sc);
56151912Sphk
57159217Snjlstatic char *hpet_ids[] = { "PNP0103", NULL };
58159217Snjl
59159217Snjl#define HPET_MEM_WIDTH		0x400	/* Expected memory region size */
60159217Snjl#define HPET_OFFSET_INFO	0	/* Location of info in region */
61159217Snjl#define HPET_OFFSET_PERIOD	4	/* Location of period (1/hz) */
62159217Snjl#define HPET_OFFSET_ENABLE	0x10	/* Location of enable word */
63159217Snjl#define HPET_OFFSET_VALUE	0xf0	/* Location of actual timer value */
64159217Snjl
65151912Sphkstruct timecounter hpet_timecounter = {
66151912Sphk	.tc_get_timecount =	hpet_get_timecount,
67151912Sphk	.tc_counter_mask =	~0u,
68151912Sphk	.tc_name =		"HPET",
69161211Sdes	.tc_quality =		2000,
70151912Sphk};
71151912Sphk
72159217Snjlstatic u_int
73151912Sphkhpet_get_timecount(struct timecounter *tc)
74151912Sphk{
75151912Sphk	struct acpi_hpet_softc *sc;
76151912Sphk
77151912Sphk	sc = tc->tc_priv;
78159217Snjl	return (bus_read_4(sc->mem_res, HPET_OFFSET_VALUE));
79151912Sphk}
80151912Sphk
81151912Sphkstatic int
82151912Sphkacpi_hpet_probe(device_t dev)
83151912Sphk{
84159217Snjl	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
85159217Snjl
86151912Sphk	if (acpi_disabled("hpet") ||
87151912Sphk	    ACPI_ID_PROBE(device_get_parent(dev), dev, hpet_ids) == NULL ||
88151912Sphk	    device_get_unit(dev) != 0)
89151912Sphk		return (ENXIO);
90151912Sphk
91159217Snjl	device_set_desc(dev, "High Precision Event Timer");
92151912Sphk	return (0);
93151912Sphk}
94151912Sphk
95151912Sphkstatic int
96151912Sphkacpi_hpet_attach(device_t dev)
97151912Sphk{
98159217Snjl	struct acpi_hpet_softc *sc;
99159217Snjl	int rid;
100159217Snjl	uint32_t val;
101159217Snjl	uintmax_t freq;
102151912Sphk
103151912Sphk	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
104151912Sphk
105151912Sphk	sc = device_get_softc(dev);
106151912Sphk	sc->dev = dev;
107151912Sphk	sc->handle = acpi_get_handle(dev);
108151912Sphk
109159217Snjl	rid = 0;
110159217Snjl	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
111159217Snjl	    RF_ACTIVE);
112159217Snjl	if (sc->mem_res == NULL)
113159217Snjl		return (ENOMEM);
114151912Sphk
115159217Snjl	/* Validate that we can access the whole region. */
116159217Snjl	if (rman_get_size(sc->mem_res) < HPET_MEM_WIDTH) {
117159217Snjl		device_printf(dev, "memory region width %ld too small\n",
118159217Snjl		    rman_get_size(sc->mem_res));
119159217Snjl		bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
120159217Snjl		return (ENXIO);
121159217Snjl	}
122151912Sphk
123159217Snjl	/* Read basic statistics about the timer. */
124159217Snjl	val = bus_read_4(sc->mem_res, HPET_OFFSET_PERIOD);
125159217Snjl	freq = (1000000000000000LL + val / 2) / val;
126159217Snjl	if (bootverbose) {
127159217Snjl		val = bus_read_4(sc->mem_res, HPET_OFFSET_INFO);
128159217Snjl		device_printf(dev,
129159217Snjl		    "vend: 0x%x rev: 0x%x num: %d hz: %jd opts:%s%s\n",
130159217Snjl		    val >> 16, val & 0xff, (val >> 18) & 0xf, freq,
131159217Snjl		    ((val >> 15) & 1) ? " leg_route" : "",
132159217Snjl		    ((val >> 13) & 1) ? " count_size" : "");
133159217Snjl	}
134151912Sphk
135159217Snjl	/* Be sure it is enabled. */
136159217Snjl	bus_write_4(sc->mem_res, HPET_OFFSET_ENABLE, 1);
137151912Sphk
138159217Snjl	if (testenv("debug.acpi.hpet_test"))
139159217Snjl		acpi_hpet_test(sc);
140151912Sphk
141159217Snjl	hpet_timecounter.tc_frequency = freq;
142159217Snjl	hpet_timecounter.tc_priv = sc;
143159217Snjl	tc_init(&hpet_timecounter);
144159217Snjl
145159217Snjl	return (0);
146159217Snjl}
147159217Snjl
148159217Snjlstatic int
149159217Snjlacpi_hpet_detach(device_t dev)
150159217Snjl{
151159217Snjl	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
152159217Snjl
153159217Snjl	/* XXX Without a tc_remove() function, we can't detach. */
154159217Snjl	return (EBUSY);
155159217Snjl}
156159217Snjl
157168010Snjlstatic int
158168010Snjlacpi_hpet_resume(device_t dev)
159168010Snjl{
160168010Snjl	struct acpi_hpet_softc *sc;
161168010Snjl
162168010Snjl	/* Re-enable the timer after a resume to keep the clock advancing. */
163168010Snjl	sc = device_get_softc(dev);
164168010Snjl	bus_write_4(sc->mem_res, HPET_OFFSET_ENABLE, 1);
165168010Snjl
166168010Snjl	return (0);
167168010Snjl}
168168010Snjl
169159217Snjl/* Print some basic latency/rate information to assist in debugging. */
170159217Snjlstatic void
171159217Snjlacpi_hpet_test(struct acpi_hpet_softc *sc)
172159217Snjl{
173151912Sphk	int i;
174151912Sphk	uint32_t u1, u2;
175151912Sphk	struct bintime b0, b1, b2;
176151912Sphk	struct timespec ts;
177151912Sphk
178151912Sphk	binuptime(&b0);
179151912Sphk	binuptime(&b0);
180151912Sphk	binuptime(&b1);
181159217Snjl	u1 = bus_read_4(sc->mem_res, HPET_OFFSET_VALUE);
182151912Sphk	for (i = 1; i < 1000; i++)
183159217Snjl		u2 = bus_read_4(sc->mem_res, HPET_OFFSET_VALUE);
184151912Sphk	binuptime(&b2);
185159217Snjl	u2 = bus_read_4(sc->mem_res, HPET_OFFSET_VALUE);
186151912Sphk
187151912Sphk	bintime_sub(&b2, &b1);
188151912Sphk	bintime_sub(&b1, &b0);
189151912Sphk	bintime_sub(&b2, &b1);
190151912Sphk	bintime2timespec(&b2, &ts);
191151912Sphk
192159217Snjl	device_printf(sc->dev, "%ld.%09ld: %u ... %u = %u\n",
193151912Sphk	    (long)ts.tv_sec, ts.tv_nsec, u1, u2, u2 - u1);
194151912Sphk
195159217Snjl	device_printf(sc->dev, "time per call: %ld ns\n", ts.tv_nsec / 1000);
196151912Sphk}
197151912Sphk
198151912Sphkstatic device_method_t acpi_hpet_methods[] = {
199151912Sphk	/* Device interface */
200151912Sphk	DEVMETHOD(device_probe, acpi_hpet_probe),
201151912Sphk	DEVMETHOD(device_attach, acpi_hpet_attach),
202151912Sphk	DEVMETHOD(device_detach, acpi_hpet_detach),
203168010Snjl	DEVMETHOD(device_resume, acpi_hpet_resume),
204151912Sphk
205151912Sphk	{0, 0}
206151912Sphk};
207151912Sphk
208151912Sphkstatic driver_t	acpi_hpet_driver = {
209151912Sphk	"acpi_hpet",
210151912Sphk	acpi_hpet_methods,
211151912Sphk	sizeof(struct acpi_hpet_softc),
212151912Sphk};
213151912Sphk
214151912Sphkstatic devclass_t acpi_hpet_devclass;
215151912Sphk
216151912SphkDRIVER_MODULE(acpi_hpet, acpi, acpi_hpet_driver, acpi_hpet_devclass, 0, 0);
217151912SphkMODULE_DEPEND(acpi_hpet, acpi, 1, 1, 1);
218