acpi_hpet.c revision 199016
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 199016 2009-11-07 11:46:38Z avg $");
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
39193530Sjkim#include <contrib/dev/acpica/include/acpi.h>
40193530Sjkim#include <contrib/dev/acpica/include/accommon.h>
41193530Sjkim
42151912Sphk#include <dev/acpica/acpivar.h>
43175385Sjhb#include <dev/acpica/acpi_hpet.h>
44151912Sphk
45151912SphkACPI_SERIAL_DECL(hpet, "ACPI HPET support");
46151912Sphk
47169574Stakawatastatic devclass_t acpi_hpet_devclass;
48169574Stakawata
49151931Sscottl/* ACPI CA debugging */
50151935Sscottl#define _COMPONENT	ACPI_TIMER
51151931SscottlACPI_MODULE_NAME("HPET")
52151931Sscottl
53151912Sphkstruct acpi_hpet_softc {
54151912Sphk	device_t		dev;
55159217Snjl	struct resource		*mem_res;
56151912Sphk	ACPI_HANDLE		handle;
57151912Sphk};
58151912Sphk
59159217Snjlstatic u_int hpet_get_timecount(struct timecounter *tc);
60159217Snjlstatic void acpi_hpet_test(struct acpi_hpet_softc *sc);
61151912Sphk
62159217Snjlstatic char *hpet_ids[] = { "PNP0103", NULL };
63159217Snjl
64151912Sphkstruct timecounter hpet_timecounter = {
65151912Sphk	.tc_get_timecount =	hpet_get_timecount,
66151912Sphk	.tc_counter_mask =	~0u,
67151912Sphk	.tc_name =		"HPET",
68171657Snjl	.tc_quality =		900,
69151912Sphk};
70151912Sphk
71159217Snjlstatic u_int
72151912Sphkhpet_get_timecount(struct timecounter *tc)
73151912Sphk{
74151912Sphk	struct acpi_hpet_softc *sc;
75151912Sphk
76151912Sphk	sc = tc->tc_priv;
77175385Sjhb	return (bus_read_4(sc->mem_res, HPET_MAIN_COUNTER));
78151912Sphk}
79151912Sphk
80175361Sjhbstatic void
81175361Sjhbhpet_enable(struct acpi_hpet_softc *sc)
82175361Sjhb{
83175361Sjhb	uint32_t val;
84175385Sjhb
85175385Sjhb	val = bus_read_4(sc->mem_res, HPET_CONFIG);
86185103Sjkim	val &= ~HPET_CNF_LEG_RT;
87185103Sjkim	val |= HPET_CNF_ENABLE;
88185103Sjkim	bus_write_4(sc->mem_res, HPET_CONFIG, val);
89175361Sjhb}
90175361Sjhb
91175361Sjhbstatic void
92175361Sjhbhpet_disable(struct acpi_hpet_softc *sc)
93175361Sjhb{
94175361Sjhb	uint32_t val;
95175385Sjhb
96175385Sjhb	val = bus_read_4(sc->mem_res, HPET_CONFIG);
97185103Sjkim	val &= ~HPET_CNF_ENABLE;
98185103Sjkim	bus_write_4(sc->mem_res, HPET_CONFIG, val);
99175361Sjhb}
100175361Sjhb
101169592Snjl/* Discover the HPET via the ACPI table of the same name. */
102172489Snjlstatic void
103172489Snjlacpi_hpet_identify(driver_t *driver, device_t parent)
104169574Stakawata{
105169574Stakawata	ACPI_TABLE_HPET *hpet;
106169574Stakawata	ACPI_TABLE_HEADER *hdr;
107169574Stakawata	ACPI_STATUS	status;
108169574Stakawata	device_t	child;
109169574Stakawata
110172489Snjl	/* Only one HPET device can be added. */
111172489Snjl	if (devclass_get_device(acpi_hpet_devclass, 0))
112172489Snjl		return;
113172489Snjl
114169592Snjl	/* Currently, ID and minimum clock tick info is unused. */
115169574Stakawata
116169574Stakawata	status = AcpiGetTable(ACPI_SIG_HPET, 1, (ACPI_TABLE_HEADER **)&hdr);
117169574Stakawata	if (ACPI_FAILURE(status))
118169574Stakawata		return;
119169574Stakawata
120169592Snjl	/*
121169592Snjl	 * The unit number could be derived from hdr->Sequence but we only
122169592Snjl	 * support one HPET device.
123169592Snjl	 */
124169592Snjl	hpet = (ACPI_TABLE_HPET *)hdr;
125169592Snjl	if (hpet->Sequence != 0)
126169592Snjl		printf("ACPI HPET table warning: Sequence is non-zero (%d)\n",
127169592Snjl		    hpet->Sequence);
128172489Snjl	child = BUS_ADD_CHILD(parent, ACPI_DEV_BASE_ORDER, "acpi_hpet", 0);
129169574Stakawata	if (child == NULL) {
130169574Stakawata		printf("%s: can't add child\n", __func__);
131169574Stakawata		return;
132169574Stakawata	}
133169592Snjl
134169592Snjl	bus_set_resource(child, SYS_RES_MEMORY, 0, hpet->Address.Address,
135169592Snjl	    HPET_MEM_WIDTH);
136169574Stakawata}
137169574Stakawata
138151912Sphkstatic int
139151912Sphkacpi_hpet_probe(device_t dev)
140151912Sphk{
141159217Snjl	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
142159217Snjl
143169592Snjl	if (acpi_disabled("hpet"))
144151912Sphk		return (ENXIO);
145199016Savg	if (acpi_get_handle(dev) != NULL &&
146169592Snjl	    (ACPI_ID_PROBE(device_get_parent(dev), dev, hpet_ids) == NULL ||
147169592Snjl	    device_get_unit(dev) != 0))
148169592Snjl		return (ENXIO);
149151912Sphk
150159217Snjl	device_set_desc(dev, "High Precision Event Timer");
151151912Sphk	return (0);
152151912Sphk}
153151912Sphk
154151912Sphkstatic int
155151912Sphkacpi_hpet_attach(device_t dev)
156151912Sphk{
157159217Snjl	struct acpi_hpet_softc *sc;
158159217Snjl	int rid;
159171547Snjl	uint32_t val, val2;
160159217Snjl	uintmax_t freq;
161151912Sphk
162151912Sphk	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
163151912Sphk
164151912Sphk	sc = device_get_softc(dev);
165151912Sphk	sc->dev = dev;
166151912Sphk	sc->handle = acpi_get_handle(dev);
167151912Sphk
168159217Snjl	rid = 0;
169159217Snjl	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
170159217Snjl	    RF_ACTIVE);
171159217Snjl	if (sc->mem_res == NULL)
172159217Snjl		return (ENOMEM);
173151912Sphk
174159217Snjl	/* Validate that we can access the whole region. */
175159217Snjl	if (rman_get_size(sc->mem_res) < HPET_MEM_WIDTH) {
176159217Snjl		device_printf(dev, "memory region width %ld too small\n",
177159217Snjl		    rman_get_size(sc->mem_res));
178159217Snjl		bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
179159217Snjl		return (ENXIO);
180159217Snjl	}
181151912Sphk
182171547Snjl	/* Be sure timer is enabled. */
183175361Sjhb	hpet_enable(sc);
184171547Snjl
185159217Snjl	/* Read basic statistics about the timer. */
186175385Sjhb	val = bus_read_4(sc->mem_res, HPET_PERIOD);
187175361Sjhb	if (val == 0) {
188175361Sjhb		device_printf(dev, "invalid period\n");
189175361Sjhb		hpet_disable(sc);
190175361Sjhb		bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
191175361Sjhb		return (ENXIO);
192175361Sjhb	}
193175361Sjhb
194159217Snjl	freq = (1000000000000000LL + val / 2) / val;
195159217Snjl	if (bootverbose) {
196175385Sjhb		val = bus_read_4(sc->mem_res, HPET_CAPABILITIES);
197159217Snjl		device_printf(dev,
198159217Snjl		    "vend: 0x%x rev: 0x%x num: %d hz: %jd opts:%s%s\n",
199175385Sjhb		    val >> 16, val & HPET_CAP_REV_ID,
200175385Sjhb		    (val & HPET_CAP_NUM_TIM) >> 8, freq,
201175385Sjhb		    (val & HPET_CAP_LEG_RT) ? " legacy_route" : "",
202175385Sjhb		    (val & HPET_CAP_COUNT_SIZE) ? " 64-bit" : "");
203159217Snjl	}
204151912Sphk
205159217Snjl	if (testenv("debug.acpi.hpet_test"))
206159217Snjl		acpi_hpet_test(sc);
207151912Sphk
208171547Snjl	/*
209171547Snjl	 * Don't attach if the timer never increments.  Since the spec
210171547Snjl	 * requires it to be at least 10 MHz, it has to change in 1 us.
211171547Snjl	 */
212175385Sjhb	val = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
213171547Snjl	DELAY(1);
214175385Sjhb	val2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
215171547Snjl	if (val == val2) {
216171547Snjl		device_printf(dev, "HPET never increments, disabling\n");
217175361Sjhb		hpet_disable(sc);
218171547Snjl		bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
219171547Snjl		return (ENXIO);
220171547Snjl	}
221171547Snjl
222159217Snjl	hpet_timecounter.tc_frequency = freq;
223159217Snjl	hpet_timecounter.tc_priv = sc;
224159217Snjl	tc_init(&hpet_timecounter);
225159217Snjl
226159217Snjl	return (0);
227159217Snjl}
228159217Snjl
229159217Snjlstatic int
230159217Snjlacpi_hpet_detach(device_t dev)
231159217Snjl{
232159217Snjl	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
233159217Snjl
234159217Snjl	/* XXX Without a tc_remove() function, we can't detach. */
235159217Snjl	return (EBUSY);
236159217Snjl}
237159217Snjl
238168010Snjlstatic int
239175361Sjhbacpi_hpet_suspend(device_t dev)
240175361Sjhb{
241175361Sjhb	struct acpi_hpet_softc *sc;
242175361Sjhb
243175361Sjhb	/*
244175361Sjhb	 * Disable the timer during suspend.  The timer will not lose
245175361Sjhb	 * its state in S1 or S2, but we are required to disable
246175361Sjhb	 * it.
247175361Sjhb	 */
248175361Sjhb	sc = device_get_softc(dev);
249175361Sjhb	hpet_disable(sc);
250175361Sjhb
251175361Sjhb	return (0);
252175361Sjhb}
253175361Sjhb
254175361Sjhbstatic int
255168010Snjlacpi_hpet_resume(device_t dev)
256168010Snjl{
257168010Snjl	struct acpi_hpet_softc *sc;
258168010Snjl
259168010Snjl	/* Re-enable the timer after a resume to keep the clock advancing. */
260168010Snjl	sc = device_get_softc(dev);
261175361Sjhb	hpet_enable(sc);
262168010Snjl
263168010Snjl	return (0);
264168010Snjl}
265168010Snjl
266159217Snjl/* Print some basic latency/rate information to assist in debugging. */
267159217Snjlstatic void
268159217Snjlacpi_hpet_test(struct acpi_hpet_softc *sc)
269159217Snjl{
270151912Sphk	int i;
271151912Sphk	uint32_t u1, u2;
272151912Sphk	struct bintime b0, b1, b2;
273151912Sphk	struct timespec ts;
274151912Sphk
275151912Sphk	binuptime(&b0);
276151912Sphk	binuptime(&b0);
277151912Sphk	binuptime(&b1);
278175385Sjhb	u1 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
279151912Sphk	for (i = 1; i < 1000; i++)
280175385Sjhb		u2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
281151912Sphk	binuptime(&b2);
282175385Sjhb	u2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
283151912Sphk
284151912Sphk	bintime_sub(&b2, &b1);
285151912Sphk	bintime_sub(&b1, &b0);
286151912Sphk	bintime_sub(&b2, &b1);
287151912Sphk	bintime2timespec(&b2, &ts);
288151912Sphk
289159217Snjl	device_printf(sc->dev, "%ld.%09ld: %u ... %u = %u\n",
290151912Sphk	    (long)ts.tv_sec, ts.tv_nsec, u1, u2, u2 - u1);
291151912Sphk
292159217Snjl	device_printf(sc->dev, "time per call: %ld ns\n", ts.tv_nsec / 1000);
293151912Sphk}
294151912Sphk
295151912Sphkstatic device_method_t acpi_hpet_methods[] = {
296151912Sphk	/* Device interface */
297172489Snjl	DEVMETHOD(device_identify, acpi_hpet_identify),
298151912Sphk	DEVMETHOD(device_probe, acpi_hpet_probe),
299151912Sphk	DEVMETHOD(device_attach, acpi_hpet_attach),
300151912Sphk	DEVMETHOD(device_detach, acpi_hpet_detach),
301175361Sjhb	DEVMETHOD(device_suspend, acpi_hpet_suspend),
302168010Snjl	DEVMETHOD(device_resume, acpi_hpet_resume),
303151912Sphk
304151912Sphk	{0, 0}
305151912Sphk};
306151912Sphk
307151912Sphkstatic driver_t	acpi_hpet_driver = {
308151912Sphk	"acpi_hpet",
309151912Sphk	acpi_hpet_methods,
310151912Sphk	sizeof(struct acpi_hpet_softc),
311151912Sphk};
312151912Sphk
313151912Sphk
314151912SphkDRIVER_MODULE(acpi_hpet, acpi, acpi_hpet_driver, acpi_hpet_devclass, 0, 0);
315151912SphkMODULE_DEPEND(acpi_hpet, acpi, 1, 1, 1);
316