acpi_hpet.c revision 203062
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 203062 2010-01-27 10:17:28Z 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
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
104169592Snjl/* Discover the HPET via the ACPI table of the same name. */
105172489Snjlstatic void
106172489Snjlacpi_hpet_identify(driver_t *driver, device_t parent)
107169574Stakawata{
108169574Stakawata	ACPI_TABLE_HPET *hpet;
109169574Stakawata	ACPI_TABLE_HEADER *hdr;
110169574Stakawata	ACPI_STATUS	status;
111169574Stakawata	device_t	child;
112169574Stakawata
113172489Snjl	/* Only one HPET device can be added. */
114172489Snjl	if (devclass_get_device(acpi_hpet_devclass, 0))
115172489Snjl		return;
116172489Snjl
117169592Snjl	/* Currently, ID and minimum clock tick info is unused. */
118169574Stakawata
119169574Stakawata	status = AcpiGetTable(ACPI_SIG_HPET, 1, (ACPI_TABLE_HEADER **)&hdr);
120169574Stakawata	if (ACPI_FAILURE(status))
121169574Stakawata		return;
122169574Stakawata
123169592Snjl	/*
124169592Snjl	 * The unit number could be derived from hdr->Sequence but we only
125169592Snjl	 * support one HPET device.
126169592Snjl	 */
127169592Snjl	hpet = (ACPI_TABLE_HPET *)hdr;
128169592Snjl	if (hpet->Sequence != 0)
129169592Snjl		printf("ACPI HPET table warning: Sequence is non-zero (%d)\n",
130169592Snjl		    hpet->Sequence);
131172489Snjl	child = BUS_ADD_CHILD(parent, ACPI_DEV_BASE_ORDER, "acpi_hpet", 0);
132169574Stakawata	if (child == NULL) {
133169574Stakawata		printf("%s: can't add child\n", __func__);
134169574Stakawata		return;
135169574Stakawata	}
136169592Snjl
137169592Snjl	bus_set_resource(child, SYS_RES_MEMORY, 0, hpet->Address.Address,
138169592Snjl	    HPET_MEM_WIDTH);
139169574Stakawata}
140169574Stakawata
141151912Sphkstatic int
142151912Sphkacpi_hpet_probe(device_t dev)
143151912Sphk{
144159217Snjl	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
145159217Snjl
146169592Snjl	if (acpi_disabled("hpet"))
147151912Sphk		return (ENXIO);
148199016Savg	if (acpi_get_handle(dev) != NULL &&
149169592Snjl	    (ACPI_ID_PROBE(device_get_parent(dev), dev, hpet_ids) == NULL ||
150169592Snjl	    device_get_unit(dev) != 0))
151169592Snjl		return (ENXIO);
152151912Sphk
153159217Snjl	device_set_desc(dev, "High Precision Event Timer");
154151912Sphk	return (0);
155151912Sphk}
156151912Sphk
157151912Sphkstatic int
158151912Sphkacpi_hpet_attach(device_t dev)
159151912Sphk{
160159217Snjl	struct acpi_hpet_softc *sc;
161203062Savg	int rid, num_timers;
162171547Snjl	uint32_t val, val2;
163159217Snjl	uintmax_t freq;
164203062Savg	uint16_t vendor;
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. */
190175385Sjhb	val = bus_read_4(sc->mem_res, HPET_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) {
200175385Sjhb		val = bus_read_4(sc->mem_res, HPET_CAPABILITIES);
201203062Savg
202203062Savg		/*
203203062Savg		 * ATI/AMD violates IA-PC HPET (High Precision Event Timers)
204203062Savg		 * Specification and provides an off by one number
205203062Savg		 * of timers/comparators.
206203062Savg		 * Additionally, they use unregistered value in VENDOR_ID field.
207203062Savg		 */
208203062Savg		num_timers = 1 + ((val & HPET_CAP_NUM_TIM) >> 8);
209203062Savg		vendor = val >> 16;
210203062Savg		if (vendor == HPET_VENDID_AMD && num_timers > 0)
211203062Savg			num_timers--;
212159217Snjl		device_printf(dev,
213159217Snjl		    "vend: 0x%x rev: 0x%x num: %d hz: %jd opts:%s%s\n",
214203062Savg		    vendor, val & HPET_CAP_REV_ID,
215203062Savg		    num_timers, freq,
216175385Sjhb		    (val & HPET_CAP_LEG_RT) ? " legacy_route" : "",
217175385Sjhb		    (val & HPET_CAP_COUNT_SIZE) ? " 64-bit" : "");
218159217Snjl	}
219151912Sphk
220159217Snjl	if (testenv("debug.acpi.hpet_test"))
221159217Snjl		acpi_hpet_test(sc);
222151912Sphk
223171547Snjl	/*
224171547Snjl	 * Don't attach if the timer never increments.  Since the spec
225171547Snjl	 * requires it to be at least 10 MHz, it has to change in 1 us.
226171547Snjl	 */
227175385Sjhb	val = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
228171547Snjl	DELAY(1);
229175385Sjhb	val2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
230171547Snjl	if (val == val2) {
231171547Snjl		device_printf(dev, "HPET never increments, disabling\n");
232175361Sjhb		hpet_disable(sc);
233171547Snjl		bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
234171547Snjl		return (ENXIO);
235171547Snjl	}
236171547Snjl
237159217Snjl	hpet_timecounter.tc_frequency = freq;
238159217Snjl	hpet_timecounter.tc_priv = sc;
239159217Snjl	tc_init(&hpet_timecounter);
240159217Snjl
241159217Snjl	return (0);
242159217Snjl}
243159217Snjl
244159217Snjlstatic int
245159217Snjlacpi_hpet_detach(device_t dev)
246159217Snjl{
247159217Snjl	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
248159217Snjl
249159217Snjl	/* XXX Without a tc_remove() function, we can't detach. */
250159217Snjl	return (EBUSY);
251159217Snjl}
252159217Snjl
253168010Snjlstatic int
254175361Sjhbacpi_hpet_suspend(device_t dev)
255175361Sjhb{
256175361Sjhb	struct acpi_hpet_softc *sc;
257175361Sjhb
258175361Sjhb	/*
259175361Sjhb	 * Disable the timer during suspend.  The timer will not lose
260175361Sjhb	 * its state in S1 or S2, but we are required to disable
261175361Sjhb	 * it.
262175361Sjhb	 */
263175361Sjhb	sc = device_get_softc(dev);
264175361Sjhb	hpet_disable(sc);
265175361Sjhb
266175361Sjhb	return (0);
267175361Sjhb}
268175361Sjhb
269175361Sjhbstatic int
270168010Snjlacpi_hpet_resume(device_t dev)
271168010Snjl{
272168010Snjl	struct acpi_hpet_softc *sc;
273168010Snjl
274168010Snjl	/* Re-enable the timer after a resume to keep the clock advancing. */
275168010Snjl	sc = device_get_softc(dev);
276175361Sjhb	hpet_enable(sc);
277168010Snjl
278168010Snjl	return (0);
279168010Snjl}
280168010Snjl
281159217Snjl/* Print some basic latency/rate information to assist in debugging. */
282159217Snjlstatic void
283159217Snjlacpi_hpet_test(struct acpi_hpet_softc *sc)
284159217Snjl{
285151912Sphk	int i;
286151912Sphk	uint32_t u1, u2;
287151912Sphk	struct bintime b0, b1, b2;
288151912Sphk	struct timespec ts;
289151912Sphk
290151912Sphk	binuptime(&b0);
291151912Sphk	binuptime(&b0);
292151912Sphk	binuptime(&b1);
293175385Sjhb	u1 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
294151912Sphk	for (i = 1; i < 1000; i++)
295175385Sjhb		u2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
296151912Sphk	binuptime(&b2);
297175385Sjhb	u2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
298151912Sphk
299151912Sphk	bintime_sub(&b2, &b1);
300151912Sphk	bintime_sub(&b1, &b0);
301151912Sphk	bintime_sub(&b2, &b1);
302151912Sphk	bintime2timespec(&b2, &ts);
303151912Sphk
304159217Snjl	device_printf(sc->dev, "%ld.%09ld: %u ... %u = %u\n",
305151912Sphk	    (long)ts.tv_sec, ts.tv_nsec, u1, u2, u2 - u1);
306151912Sphk
307159217Snjl	device_printf(sc->dev, "time per call: %ld ns\n", ts.tv_nsec / 1000);
308151912Sphk}
309151912Sphk
310151912Sphkstatic device_method_t acpi_hpet_methods[] = {
311151912Sphk	/* Device interface */
312172489Snjl	DEVMETHOD(device_identify, acpi_hpet_identify),
313151912Sphk	DEVMETHOD(device_probe, acpi_hpet_probe),
314151912Sphk	DEVMETHOD(device_attach, acpi_hpet_attach),
315151912Sphk	DEVMETHOD(device_detach, acpi_hpet_detach),
316175361Sjhb	DEVMETHOD(device_suspend, acpi_hpet_suspend),
317168010Snjl	DEVMETHOD(device_resume, acpi_hpet_resume),
318151912Sphk
319151912Sphk	{0, 0}
320151912Sphk};
321151912Sphk
322151912Sphkstatic driver_t	acpi_hpet_driver = {
323151912Sphk	"acpi_hpet",
324151912Sphk	acpi_hpet_methods,
325151912Sphk	sizeof(struct acpi_hpet_softc),
326151912Sphk};
327151912Sphk
328151912Sphk
329151912SphkDRIVER_MODULE(acpi_hpet, acpi, acpi_hpet_driver, acpi_hpet_devclass, 0, 0);
330151912SphkMODULE_DEPEND(acpi_hpet, acpi, 1, 1, 1);
331