acpi_hpet.c revision 208438
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 208438 2010-05-23 08:31:15Z mav $");
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
45203062Savg#define HPET_VENDID_AMD		0x4353
46203062Savg#define HPET_VENDID_INTEL	0x8086
47203062Savg
48151912SphkACPI_SERIAL_DECL(hpet, "ACPI HPET support");
49151912Sphk
50169574Stakawatastatic devclass_t acpi_hpet_devclass;
51169574Stakawata
52151931Sscottl/* ACPI CA debugging */
53151935Sscottl#define _COMPONENT	ACPI_TIMER
54151931SscottlACPI_MODULE_NAME("HPET")
55151931Sscottl
56151912Sphkstruct acpi_hpet_softc {
57151912Sphk	device_t		dev;
58159217Snjl	struct resource		*mem_res;
59151912Sphk	ACPI_HANDLE		handle;
60151912Sphk};
61151912Sphk
62159217Snjlstatic u_int hpet_get_timecount(struct timecounter *tc);
63159217Snjlstatic void acpi_hpet_test(struct acpi_hpet_softc *sc);
64151912Sphk
65159217Snjlstatic char *hpet_ids[] = { "PNP0103", NULL };
66159217Snjl
67151912Sphkstruct timecounter hpet_timecounter = {
68151912Sphk	.tc_get_timecount =	hpet_get_timecount,
69151912Sphk	.tc_counter_mask =	~0u,
70151912Sphk	.tc_name =		"HPET",
71171657Snjl	.tc_quality =		900,
72151912Sphk};
73151912Sphk
74159217Snjlstatic u_int
75151912Sphkhpet_get_timecount(struct timecounter *tc)
76151912Sphk{
77151912Sphk	struct acpi_hpet_softc *sc;
78151912Sphk
79151912Sphk	sc = tc->tc_priv;
80175385Sjhb	return (bus_read_4(sc->mem_res, HPET_MAIN_COUNTER));
81151912Sphk}
82151912Sphk
83175361Sjhbstatic void
84175361Sjhbhpet_enable(struct acpi_hpet_softc *sc)
85175361Sjhb{
86175361Sjhb	uint32_t val;
87175385Sjhb
88175385Sjhb	val = bus_read_4(sc->mem_res, HPET_CONFIG);
89185103Sjkim	val &= ~HPET_CNF_LEG_RT;
90185103Sjkim	val |= HPET_CNF_ENABLE;
91185103Sjkim	bus_write_4(sc->mem_res, HPET_CONFIG, val);
92175361Sjhb}
93175361Sjhb
94175361Sjhbstatic void
95175361Sjhbhpet_disable(struct acpi_hpet_softc *sc)
96175361Sjhb{
97175361Sjhb	uint32_t val;
98175385Sjhb
99175385Sjhb	val = bus_read_4(sc->mem_res, HPET_CONFIG);
100185103Sjkim	val &= ~HPET_CNF_ENABLE;
101185103Sjkim	bus_write_4(sc->mem_res, HPET_CONFIG, val);
102175361Sjhb}
103175361Sjhb
104208436Smavstatic ACPI_STATUS
105208436Smavacpi_hpet_find(ACPI_HANDLE handle, UINT32 level, void *context,
106208436Smav    void **status)
107208436Smav{
108208436Smav	char 		**ids;
109208436Smav	uint32_t	id = (uint32_t)(uintptr_t)context;
110208438Smav	uint32_t	uid = 0;
111208436Smav
112208436Smav	for (ids = hpet_ids; *ids != NULL; ids++) {
113208436Smav		if (acpi_MatchHid(handle, *ids))
114208436Smav		        break;
115208436Smav	}
116208436Smav	if (*ids == NULL)
117208436Smav		return (AE_OK);
118208438Smav	if (ACPI_FAILURE(acpi_GetInteger(handle, "_UID", &uid)))
119208438Smav		uid = 0;
120208438Smav	if (id == uid)
121208436Smav		*((int *)status) = 1;
122208436Smav	return (AE_OK);
123208436Smav}
124208436Smav
125169592Snjl/* Discover the HPET via the ACPI table of the same name. */
126172489Snjlstatic void
127172489Snjlacpi_hpet_identify(driver_t *driver, device_t parent)
128169574Stakawata{
129169574Stakawata	ACPI_TABLE_HPET *hpet;
130169574Stakawata	ACPI_STATUS	status;
131169574Stakawata	device_t	child;
132208436Smav	int 		i, found;
133169574Stakawata
134172489Snjl	/* Only one HPET device can be added. */
135172489Snjl	if (devclass_get_device(acpi_hpet_devclass, 0))
136172489Snjl		return;
137208436Smav	for (i = 1; ; i++) {
138208436Smav		/* Search for HPET table. */
139208436Smav		status = AcpiGetTable(ACPI_SIG_HPET, i, (ACPI_TABLE_HEADER **)&hpet);
140208436Smav		if (ACPI_FAILURE(status))
141208436Smav			return;
142208436Smav		/* Search for HPET device with same ID. */
143208436Smav		found = 0;
144208436Smav		AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
145208436Smav		    100, acpi_hpet_find, NULL, (void *)(uintptr_t)hpet->Sequence, (void *)&found);
146208436Smav		/* If found - let it be probed in normal way. */
147208436Smav		if (found)
148208436Smav			continue;
149208436Smav		/* If not - create it from table info. */
150208436Smav		child = BUS_ADD_CHILD(parent, ACPI_DEV_BASE_ORDER, "acpi_hpet", 0);
151208436Smav		if (child == NULL) {
152208436Smav			printf("%s: can't add child\n", __func__);
153208436Smav			continue;
154208436Smav		}
155208436Smav		bus_set_resource(child, SYS_RES_MEMORY, 0, hpet->Address.Address,
156208436Smav		    HPET_MEM_WIDTH);
157169574Stakawata	}
158169574Stakawata}
159169574Stakawata
160151912Sphkstatic int
161151912Sphkacpi_hpet_probe(device_t dev)
162151912Sphk{
163159217Snjl	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
164159217Snjl
165169592Snjl	if (acpi_disabled("hpet"))
166151912Sphk		return (ENXIO);
167199016Savg	if (acpi_get_handle(dev) != NULL &&
168208436Smav	    ACPI_ID_PROBE(device_get_parent(dev), dev, hpet_ids) == NULL)
169169592Snjl		return (ENXIO);
170151912Sphk
171159217Snjl	device_set_desc(dev, "High Precision Event Timer");
172151912Sphk	return (0);
173151912Sphk}
174151912Sphk
175151912Sphkstatic int
176151912Sphkacpi_hpet_attach(device_t dev)
177151912Sphk{
178159217Snjl	struct acpi_hpet_softc *sc;
179203062Savg	int rid, num_timers;
180171547Snjl	uint32_t val, val2;
181159217Snjl	uintmax_t freq;
182203062Savg	uint16_t vendor;
183151912Sphk
184151912Sphk	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
185151912Sphk
186151912Sphk	sc = device_get_softc(dev);
187151912Sphk	sc->dev = dev;
188151912Sphk	sc->handle = acpi_get_handle(dev);
189151912Sphk
190159217Snjl	rid = 0;
191159217Snjl	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
192159217Snjl	    RF_ACTIVE);
193159217Snjl	if (sc->mem_res == NULL)
194159217Snjl		return (ENOMEM);
195151912Sphk
196159217Snjl	/* Validate that we can access the whole region. */
197159217Snjl	if (rman_get_size(sc->mem_res) < HPET_MEM_WIDTH) {
198159217Snjl		device_printf(dev, "memory region width %ld too small\n",
199159217Snjl		    rman_get_size(sc->mem_res));
200159217Snjl		bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
201159217Snjl		return (ENXIO);
202159217Snjl	}
203151912Sphk
204171547Snjl	/* Be sure timer is enabled. */
205175361Sjhb	hpet_enable(sc);
206171547Snjl
207159217Snjl	/* Read basic statistics about the timer. */
208175385Sjhb	val = bus_read_4(sc->mem_res, HPET_PERIOD);
209175361Sjhb	if (val == 0) {
210175361Sjhb		device_printf(dev, "invalid period\n");
211175361Sjhb		hpet_disable(sc);
212175361Sjhb		bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
213175361Sjhb		return (ENXIO);
214175361Sjhb	}
215175361Sjhb
216159217Snjl	freq = (1000000000000000LL + val / 2) / val;
217159217Snjl	if (bootverbose) {
218175385Sjhb		val = bus_read_4(sc->mem_res, HPET_CAPABILITIES);
219203062Savg
220203062Savg		/*
221203062Savg		 * ATI/AMD violates IA-PC HPET (High Precision Event Timers)
222203062Savg		 * Specification and provides an off by one number
223203062Savg		 * of timers/comparators.
224203062Savg		 * Additionally, they use unregistered value in VENDOR_ID field.
225203062Savg		 */
226203062Savg		num_timers = 1 + ((val & HPET_CAP_NUM_TIM) >> 8);
227203062Savg		vendor = val >> 16;
228203062Savg		if (vendor == HPET_VENDID_AMD && num_timers > 0)
229203062Savg			num_timers--;
230159217Snjl		device_printf(dev,
231159217Snjl		    "vend: 0x%x rev: 0x%x num: %d hz: %jd opts:%s%s\n",
232203062Savg		    vendor, val & HPET_CAP_REV_ID,
233203062Savg		    num_timers, freq,
234175385Sjhb		    (val & HPET_CAP_LEG_RT) ? " legacy_route" : "",
235175385Sjhb		    (val & HPET_CAP_COUNT_SIZE) ? " 64-bit" : "");
236159217Snjl	}
237151912Sphk
238159217Snjl	if (testenv("debug.acpi.hpet_test"))
239159217Snjl		acpi_hpet_test(sc);
240151912Sphk
241171547Snjl	/*
242171547Snjl	 * Don't attach if the timer never increments.  Since the spec
243171547Snjl	 * requires it to be at least 10 MHz, it has to change in 1 us.
244171547Snjl	 */
245175385Sjhb	val = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
246171547Snjl	DELAY(1);
247175385Sjhb	val2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
248171547Snjl	if (val == val2) {
249171547Snjl		device_printf(dev, "HPET never increments, disabling\n");
250175361Sjhb		hpet_disable(sc);
251171547Snjl		bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
252171547Snjl		return (ENXIO);
253171547Snjl	}
254208436Smav	/* Announce first HPET as timecounter. */
255208436Smav	if (device_get_unit(dev) == 0) {
256208436Smav		hpet_timecounter.tc_frequency = freq;
257208436Smav		hpet_timecounter.tc_priv = sc;
258208436Smav		tc_init(&hpet_timecounter);
259208436Smav	}
260159217Snjl	return (0);
261159217Snjl}
262159217Snjl
263159217Snjlstatic int
264159217Snjlacpi_hpet_detach(device_t dev)
265159217Snjl{
266159217Snjl	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
267159217Snjl
268159217Snjl	/* XXX Without a tc_remove() function, we can't detach. */
269159217Snjl	return (EBUSY);
270159217Snjl}
271159217Snjl
272168010Snjlstatic int
273175361Sjhbacpi_hpet_suspend(device_t dev)
274175361Sjhb{
275175361Sjhb	struct acpi_hpet_softc *sc;
276175361Sjhb
277175361Sjhb	/*
278175361Sjhb	 * Disable the timer during suspend.  The timer will not lose
279175361Sjhb	 * its state in S1 or S2, but we are required to disable
280175361Sjhb	 * it.
281175361Sjhb	 */
282175361Sjhb	sc = device_get_softc(dev);
283175361Sjhb	hpet_disable(sc);
284175361Sjhb
285175361Sjhb	return (0);
286175361Sjhb}
287175361Sjhb
288175361Sjhbstatic int
289168010Snjlacpi_hpet_resume(device_t dev)
290168010Snjl{
291168010Snjl	struct acpi_hpet_softc *sc;
292168010Snjl
293168010Snjl	/* Re-enable the timer after a resume to keep the clock advancing. */
294168010Snjl	sc = device_get_softc(dev);
295175361Sjhb	hpet_enable(sc);
296168010Snjl
297168010Snjl	return (0);
298168010Snjl}
299168010Snjl
300159217Snjl/* Print some basic latency/rate information to assist in debugging. */
301159217Snjlstatic void
302159217Snjlacpi_hpet_test(struct acpi_hpet_softc *sc)
303159217Snjl{
304151912Sphk	int i;
305151912Sphk	uint32_t u1, u2;
306151912Sphk	struct bintime b0, b1, b2;
307151912Sphk	struct timespec ts;
308151912Sphk
309151912Sphk	binuptime(&b0);
310151912Sphk	binuptime(&b0);
311151912Sphk	binuptime(&b1);
312175385Sjhb	u1 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
313151912Sphk	for (i = 1; i < 1000; i++)
314175385Sjhb		u2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
315151912Sphk	binuptime(&b2);
316175385Sjhb	u2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
317151912Sphk
318151912Sphk	bintime_sub(&b2, &b1);
319151912Sphk	bintime_sub(&b1, &b0);
320151912Sphk	bintime_sub(&b2, &b1);
321151912Sphk	bintime2timespec(&b2, &ts);
322151912Sphk
323159217Snjl	device_printf(sc->dev, "%ld.%09ld: %u ... %u = %u\n",
324151912Sphk	    (long)ts.tv_sec, ts.tv_nsec, u1, u2, u2 - u1);
325151912Sphk
326159217Snjl	device_printf(sc->dev, "time per call: %ld ns\n", ts.tv_nsec / 1000);
327151912Sphk}
328151912Sphk
329151912Sphkstatic device_method_t acpi_hpet_methods[] = {
330151912Sphk	/* Device interface */
331172489Snjl	DEVMETHOD(device_identify, acpi_hpet_identify),
332151912Sphk	DEVMETHOD(device_probe, acpi_hpet_probe),
333151912Sphk	DEVMETHOD(device_attach, acpi_hpet_attach),
334151912Sphk	DEVMETHOD(device_detach, acpi_hpet_detach),
335175361Sjhb	DEVMETHOD(device_suspend, acpi_hpet_suspend),
336168010Snjl	DEVMETHOD(device_resume, acpi_hpet_resume),
337151912Sphk
338151912Sphk	{0, 0}
339151912Sphk};
340151912Sphk
341151912Sphkstatic driver_t	acpi_hpet_driver = {
342151912Sphk	"acpi_hpet",
343151912Sphk	acpi_hpet_methods,
344151912Sphk	sizeof(struct acpi_hpet_softc),
345151912Sphk};
346151912Sphk
347151912Sphk
348151912SphkDRIVER_MODULE(acpi_hpet, acpi, acpi_hpet_driver, acpi_hpet_devclass, 0, 0);
349151912SphkMODULE_DEPEND(acpi_hpet, acpi, 1, 1, 1);
350