acpi_hpet.c revision 175361
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 175361 2008-01-15 18:50:47Z jhb $");
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
44169574Stakawatastatic devclass_t acpi_hpet_devclass;
45169574Stakawata
46151931Sscottl/* ACPI CA debugging */
47151935Sscottl#define _COMPONENT	ACPI_TIMER
48151931SscottlACPI_MODULE_NAME("HPET")
49151931Sscottl
50151912Sphkstruct acpi_hpet_softc {
51151912Sphk	device_t		dev;
52159217Snjl	struct resource		*mem_res;
53151912Sphk	ACPI_HANDLE		handle;
54151912Sphk};
55151912Sphk
56159217Snjlstatic u_int hpet_get_timecount(struct timecounter *tc);
57159217Snjlstatic void acpi_hpet_test(struct acpi_hpet_softc *sc);
58151912Sphk
59159217Snjlstatic char *hpet_ids[] = { "PNP0103", NULL };
60159217Snjl
61159217Snjl#define HPET_MEM_WIDTH		0x400	/* Expected memory region size */
62159217Snjl#define HPET_OFFSET_INFO	0	/* Location of info in region */
63159217Snjl#define HPET_OFFSET_PERIOD	4	/* Location of period (1/hz) */
64159217Snjl#define HPET_OFFSET_ENABLE	0x10	/* Location of enable word */
65159217Snjl#define HPET_OFFSET_VALUE	0xf0	/* Location of actual timer value */
66159217Snjl
67170783Snjl#define DEV_HPET(x)	(acpi_get_magic(x) == (uintptr_t)&acpi_hpet_devclass)
68169592Snjl
69151912Sphkstruct timecounter hpet_timecounter = {
70151912Sphk	.tc_get_timecount =	hpet_get_timecount,
71151912Sphk	.tc_counter_mask =	~0u,
72151912Sphk	.tc_name =		"HPET",
73171657Snjl	.tc_quality =		900,
74151912Sphk};
75151912Sphk
76159217Snjlstatic u_int
77151912Sphkhpet_get_timecount(struct timecounter *tc)
78151912Sphk{
79151912Sphk	struct acpi_hpet_softc *sc;
80151912Sphk
81151912Sphk	sc = tc->tc_priv;
82159217Snjl	return (bus_read_4(sc->mem_res, HPET_OFFSET_VALUE));
83151912Sphk}
84151912Sphk
85175361Sjhbstatic void
86175361Sjhbhpet_enable(struct acpi_hpet_softc *sc)
87175361Sjhb{
88175361Sjhb	uint32_t val;
89175361Sjhb
90175361Sjhb	val = bus_read_4(sc->mem_res, HPET_OFFSET_ENABLE);
91175361Sjhb	bus_write_4(sc->mem_res, HPET_OFFSET_ENABLE, val | 1);
92175361Sjhb}
93175361Sjhb
94175361Sjhbstatic void
95175361Sjhbhpet_disable(struct acpi_hpet_softc *sc)
96175361Sjhb{
97175361Sjhb	uint32_t val;
98175361Sjhb
99175361Sjhb	val = bus_read_4(sc->mem_res, HPET_OFFSET_ENABLE);
100175361Sjhb	bus_write_4(sc->mem_res, HPET_OFFSET_ENABLE, val & ~1);
101175361Sjhb}
102175361Sjhb
103169592Snjl/* Discover the HPET via the ACPI table of the same name. */
104172489Snjlstatic void
105172489Snjlacpi_hpet_identify(driver_t *driver, device_t parent)
106169574Stakawata{
107169574Stakawata	ACPI_TABLE_HPET *hpet;
108169574Stakawata	ACPI_TABLE_HEADER *hdr;
109169574Stakawata	ACPI_STATUS	status;
110169574Stakawata	device_t	child;
111169574Stakawata
112172489Snjl	/* Only one HPET device can be added. */
113172489Snjl	if (devclass_get_device(acpi_hpet_devclass, 0))
114172489Snjl		return;
115172489Snjl
116169592Snjl	/* Currently, ID and minimum clock tick info is unused. */
117169574Stakawata
118169574Stakawata	status = AcpiGetTable(ACPI_SIG_HPET, 1, (ACPI_TABLE_HEADER **)&hdr);
119169574Stakawata	if (ACPI_FAILURE(status))
120169574Stakawata		return;
121169574Stakawata
122169592Snjl	/*
123169592Snjl	 * The unit number could be derived from hdr->Sequence but we only
124169592Snjl	 * support one HPET device.
125169592Snjl	 */
126169592Snjl	hpet = (ACPI_TABLE_HPET *)hdr;
127169592Snjl	if (hpet->Sequence != 0)
128169592Snjl		printf("ACPI HPET table warning: Sequence is non-zero (%d)\n",
129169592Snjl		    hpet->Sequence);
130172489Snjl	child = BUS_ADD_CHILD(parent, ACPI_DEV_BASE_ORDER, "acpi_hpet", 0);
131169574Stakawata	if (child == NULL) {
132169574Stakawata		printf("%s: can't add child\n", __func__);
133169574Stakawata		return;
134169574Stakawata	}
135169592Snjl
136169592Snjl	/* Record a magic value so we can detect this device later. */
137170783Snjl	acpi_set_magic(child, (uintptr_t)&acpi_hpet_devclass);
138169592Snjl	bus_set_resource(child, SYS_RES_MEMORY, 0, hpet->Address.Address,
139169592Snjl	    HPET_MEM_WIDTH);
140169574Stakawata}
141169574Stakawata
142151912Sphkstatic int
143151912Sphkacpi_hpet_probe(device_t dev)
144151912Sphk{
145159217Snjl	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
146159217Snjl
147169592Snjl	if (acpi_disabled("hpet"))
148151912Sphk		return (ENXIO);
149169592Snjl	if (!DEV_HPET(dev) &&
150169592Snjl	    (ACPI_ID_PROBE(device_get_parent(dev), dev, hpet_ids) == NULL ||
151169592Snjl	    device_get_unit(dev) != 0))
152169592Snjl		return (ENXIO);
153151912Sphk
154159217Snjl	device_set_desc(dev, "High Precision Event Timer");
155151912Sphk	return (0);
156151912Sphk}
157151912Sphk
158151912Sphkstatic int
159151912Sphkacpi_hpet_attach(device_t dev)
160151912Sphk{
161159217Snjl	struct acpi_hpet_softc *sc;
162159217Snjl	int rid;
163171547Snjl	uint32_t val, val2;
164159217Snjl	uintmax_t freq;
165151912Sphk
166151912Sphk	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
167151912Sphk
168151912Sphk	sc = device_get_softc(dev);
169151912Sphk	sc->dev = dev;
170151912Sphk	sc->handle = acpi_get_handle(dev);
171151912Sphk
172159217Snjl	rid = 0;
173159217Snjl	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
174159217Snjl	    RF_ACTIVE);
175159217Snjl	if (sc->mem_res == NULL)
176159217Snjl		return (ENOMEM);
177151912Sphk
178159217Snjl	/* Validate that we can access the whole region. */
179159217Snjl	if (rman_get_size(sc->mem_res) < HPET_MEM_WIDTH) {
180159217Snjl		device_printf(dev, "memory region width %ld too small\n",
181159217Snjl		    rman_get_size(sc->mem_res));
182159217Snjl		bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
183159217Snjl		return (ENXIO);
184159217Snjl	}
185151912Sphk
186171547Snjl	/* Be sure timer is enabled. */
187175361Sjhb	hpet_enable(sc);
188171547Snjl
189159217Snjl	/* Read basic statistics about the timer. */
190159217Snjl	val = bus_read_4(sc->mem_res, HPET_OFFSET_PERIOD);
191175361Sjhb	if (val == 0) {
192175361Sjhb		device_printf(dev, "invalid period\n");
193175361Sjhb		hpet_disable(sc);
194175361Sjhb		bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
195175361Sjhb		return (ENXIO);
196175361Sjhb	}
197175361Sjhb
198159217Snjl	freq = (1000000000000000LL + val / 2) / val;
199159217Snjl	if (bootverbose) {
200159217Snjl		val = bus_read_4(sc->mem_res, HPET_OFFSET_INFO);
201159217Snjl		device_printf(dev,
202159217Snjl		    "vend: 0x%x rev: 0x%x num: %d hz: %jd opts:%s%s\n",
203159217Snjl		    val >> 16, val & 0xff, (val >> 18) & 0xf, freq,
204159217Snjl		    ((val >> 15) & 1) ? " leg_route" : "",
205159217Snjl		    ((val >> 13) & 1) ? " count_size" : "");
206159217Snjl	}
207151912Sphk
208159217Snjl	if (testenv("debug.acpi.hpet_test"))
209159217Snjl		acpi_hpet_test(sc);
210151912Sphk
211171547Snjl	/*
212171547Snjl	 * Don't attach if the timer never increments.  Since the spec
213171547Snjl	 * requires it to be at least 10 MHz, it has to change in 1 us.
214171547Snjl	 */
215171547Snjl	val = bus_read_4(sc->mem_res, HPET_OFFSET_VALUE);
216171547Snjl	DELAY(1);
217171547Snjl	val2 = bus_read_4(sc->mem_res, HPET_OFFSET_VALUE);
218171547Snjl	if (val == val2) {
219171547Snjl		device_printf(dev, "HPET never increments, disabling\n");
220175361Sjhb		hpet_disable(sc);
221171547Snjl		bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
222171547Snjl		return (ENXIO);
223171547Snjl	}
224171547Snjl
225159217Snjl	hpet_timecounter.tc_frequency = freq;
226159217Snjl	hpet_timecounter.tc_priv = sc;
227159217Snjl	tc_init(&hpet_timecounter);
228159217Snjl
229159217Snjl	return (0);
230159217Snjl}
231159217Snjl
232159217Snjlstatic int
233159217Snjlacpi_hpet_detach(device_t dev)
234159217Snjl{
235159217Snjl	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
236159217Snjl
237159217Snjl	/* XXX Without a tc_remove() function, we can't detach. */
238159217Snjl	return (EBUSY);
239159217Snjl}
240159217Snjl
241168010Snjlstatic int
242175361Sjhbacpi_hpet_suspend(device_t dev)
243175361Sjhb{
244175361Sjhb	struct acpi_hpet_softc *sc;
245175361Sjhb
246175361Sjhb	/*
247175361Sjhb	 * Disable the timer during suspend.  The timer will not lose
248175361Sjhb	 * its state in S1 or S2, but we are required to disable
249175361Sjhb	 * it.
250175361Sjhb	 */
251175361Sjhb	sc = device_get_softc(dev);
252175361Sjhb	hpet_disable(sc);
253175361Sjhb
254175361Sjhb	return (0);
255175361Sjhb}
256175361Sjhb
257175361Sjhbstatic int
258168010Snjlacpi_hpet_resume(device_t dev)
259168010Snjl{
260168010Snjl	struct acpi_hpet_softc *sc;
261168010Snjl
262168010Snjl	/* Re-enable the timer after a resume to keep the clock advancing. */
263168010Snjl	sc = device_get_softc(dev);
264175361Sjhb	hpet_enable(sc);
265168010Snjl
266168010Snjl	return (0);
267168010Snjl}
268168010Snjl
269159217Snjl/* Print some basic latency/rate information to assist in debugging. */
270159217Snjlstatic void
271159217Snjlacpi_hpet_test(struct acpi_hpet_softc *sc)
272159217Snjl{
273151912Sphk	int i;
274151912Sphk	uint32_t u1, u2;
275151912Sphk	struct bintime b0, b1, b2;
276151912Sphk	struct timespec ts;
277151912Sphk
278151912Sphk	binuptime(&b0);
279151912Sphk	binuptime(&b0);
280151912Sphk	binuptime(&b1);
281159217Snjl	u1 = bus_read_4(sc->mem_res, HPET_OFFSET_VALUE);
282151912Sphk	for (i = 1; i < 1000; i++)
283159217Snjl		u2 = bus_read_4(sc->mem_res, HPET_OFFSET_VALUE);
284151912Sphk	binuptime(&b2);
285159217Snjl	u2 = bus_read_4(sc->mem_res, HPET_OFFSET_VALUE);
286151912Sphk
287151912Sphk	bintime_sub(&b2, &b1);
288151912Sphk	bintime_sub(&b1, &b0);
289151912Sphk	bintime_sub(&b2, &b1);
290151912Sphk	bintime2timespec(&b2, &ts);
291151912Sphk
292159217Snjl	device_printf(sc->dev, "%ld.%09ld: %u ... %u = %u\n",
293151912Sphk	    (long)ts.tv_sec, ts.tv_nsec, u1, u2, u2 - u1);
294151912Sphk
295159217Snjl	device_printf(sc->dev, "time per call: %ld ns\n", ts.tv_nsec / 1000);
296151912Sphk}
297151912Sphk
298151912Sphkstatic device_method_t acpi_hpet_methods[] = {
299151912Sphk	/* Device interface */
300172489Snjl	DEVMETHOD(device_identify, acpi_hpet_identify),
301151912Sphk	DEVMETHOD(device_probe, acpi_hpet_probe),
302151912Sphk	DEVMETHOD(device_attach, acpi_hpet_attach),
303151912Sphk	DEVMETHOD(device_detach, acpi_hpet_detach),
304175361Sjhb	DEVMETHOD(device_suspend, acpi_hpet_suspend),
305168010Snjl	DEVMETHOD(device_resume, acpi_hpet_resume),
306151912Sphk
307151912Sphk	{0, 0}
308151912Sphk};
309151912Sphk
310151912Sphkstatic driver_t	acpi_hpet_driver = {
311151912Sphk	"acpi_hpet",
312151912Sphk	acpi_hpet_methods,
313151912Sphk	sizeof(struct acpi_hpet_softc),
314151912Sphk};
315151912Sphk
316151912Sphk
317151912SphkDRIVER_MODULE(acpi_hpet, acpi, acpi_hpet_driver, acpi_hpet_devclass, 0, 0);
318151912SphkMODULE_DEPEND(acpi_hpet, acpi, 1, 1, 1);
319