acpi_hpet.c revision 171657
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 171657 2007-07-30 15:21:26Z 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
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
85169592Snjl/* Discover the HPET via the ACPI table of the same name. */
86169574Stakawatavoid
87169574Stakawataacpi_hpet_table_probe(device_t parent)
88169574Stakawata{
89169574Stakawata	ACPI_TABLE_HPET *hpet;
90169574Stakawata	ACPI_TABLE_HEADER *hdr;
91169574Stakawata	ACPI_STATUS	status;
92169574Stakawata	device_t	child;
93169574Stakawata
94169592Snjl	/* Currently, ID and minimum clock tick info is unused. */
95169574Stakawata
96169574Stakawata	status = AcpiGetTable(ACPI_SIG_HPET, 1, (ACPI_TABLE_HEADER **)&hdr);
97169574Stakawata	if (ACPI_FAILURE(status))
98169574Stakawata		return;
99169574Stakawata
100169592Snjl	/*
101169592Snjl	 * The unit number could be derived from hdr->Sequence but we only
102169592Snjl	 * support one HPET device.
103169592Snjl	 */
104169592Snjl	hpet = (ACPI_TABLE_HPET *)hdr;
105169592Snjl	if (hpet->Sequence != 0)
106169592Snjl		printf("ACPI HPET table warning: Sequence is non-zero (%d)\n",
107169592Snjl		    hpet->Sequence);
108169574Stakawata	child = BUS_ADD_CHILD(parent, 0, "acpi_hpet", 0);
109169574Stakawata	if (child == NULL) {
110169574Stakawata		printf("%s: can't add child\n", __func__);
111169574Stakawata		return;
112169574Stakawata	}
113169592Snjl
114169592Snjl	/* Record a magic value so we can detect this device later. */
115170783Snjl	acpi_set_magic(child, (uintptr_t)&acpi_hpet_devclass);
116169592Snjl	bus_set_resource(child, SYS_RES_MEMORY, 0, hpet->Address.Address,
117169592Snjl	    HPET_MEM_WIDTH);
118169592Snjl	if (device_probe_and_attach(child) != 0)
119169574Stakawata		device_delete_child(parent, child);
120169574Stakawata}
121169574Stakawata
122151912Sphkstatic int
123151912Sphkacpi_hpet_probe(device_t dev)
124151912Sphk{
125159217Snjl	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
126159217Snjl
127169592Snjl	if (acpi_disabled("hpet"))
128151912Sphk		return (ENXIO);
129169592Snjl	if (!DEV_HPET(dev) &&
130169592Snjl	    (ACPI_ID_PROBE(device_get_parent(dev), dev, hpet_ids) == NULL ||
131169592Snjl	    device_get_unit(dev) != 0))
132169592Snjl		return (ENXIO);
133151912Sphk
134159217Snjl	device_set_desc(dev, "High Precision Event Timer");
135151912Sphk	return (0);
136151912Sphk}
137151912Sphk
138151912Sphkstatic int
139151912Sphkacpi_hpet_attach(device_t dev)
140151912Sphk{
141159217Snjl	struct acpi_hpet_softc *sc;
142159217Snjl	int rid;
143171547Snjl	uint32_t val, val2;
144159217Snjl	uintmax_t freq;
145151912Sphk
146151912Sphk	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
147151912Sphk
148151912Sphk	sc = device_get_softc(dev);
149151912Sphk	sc->dev = dev;
150151912Sphk	sc->handle = acpi_get_handle(dev);
151151912Sphk
152159217Snjl	rid = 0;
153159217Snjl	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
154159217Snjl	    RF_ACTIVE);
155159217Snjl	if (sc->mem_res == NULL)
156159217Snjl		return (ENOMEM);
157151912Sphk
158159217Snjl	/* Validate that we can access the whole region. */
159159217Snjl	if (rman_get_size(sc->mem_res) < HPET_MEM_WIDTH) {
160159217Snjl		device_printf(dev, "memory region width %ld too small\n",
161159217Snjl		    rman_get_size(sc->mem_res));
162159217Snjl		bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
163159217Snjl		return (ENXIO);
164159217Snjl	}
165151912Sphk
166171547Snjl	/* Be sure timer is enabled. */
167171547Snjl	bus_write_4(sc->mem_res, HPET_OFFSET_ENABLE, 1);
168171547Snjl
169159217Snjl	/* Read basic statistics about the timer. */
170159217Snjl	val = bus_read_4(sc->mem_res, HPET_OFFSET_PERIOD);
171159217Snjl	freq = (1000000000000000LL + val / 2) / val;
172159217Snjl	if (bootverbose) {
173159217Snjl		val = bus_read_4(sc->mem_res, HPET_OFFSET_INFO);
174159217Snjl		device_printf(dev,
175159217Snjl		    "vend: 0x%x rev: 0x%x num: %d hz: %jd opts:%s%s\n",
176159217Snjl		    val >> 16, val & 0xff, (val >> 18) & 0xf, freq,
177159217Snjl		    ((val >> 15) & 1) ? " leg_route" : "",
178159217Snjl		    ((val >> 13) & 1) ? " count_size" : "");
179159217Snjl	}
180151912Sphk
181159217Snjl	if (testenv("debug.acpi.hpet_test"))
182159217Snjl		acpi_hpet_test(sc);
183151912Sphk
184171547Snjl	/*
185171547Snjl	 * Don't attach if the timer never increments.  Since the spec
186171547Snjl	 * requires it to be at least 10 MHz, it has to change in 1 us.
187171547Snjl	 */
188171547Snjl	val = bus_read_4(sc->mem_res, HPET_OFFSET_VALUE);
189171547Snjl	DELAY(1);
190171547Snjl	val2 = bus_read_4(sc->mem_res, HPET_OFFSET_VALUE);
191171547Snjl	if (val == val2) {
192171547Snjl		device_printf(dev, "HPET never increments, disabling\n");
193171547Snjl		bus_write_4(sc->mem_res, HPET_OFFSET_ENABLE, 0);
194171547Snjl		bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
195171547Snjl		return (ENXIO);
196171547Snjl	}
197171547Snjl
198159217Snjl	hpet_timecounter.tc_frequency = freq;
199159217Snjl	hpet_timecounter.tc_priv = sc;
200159217Snjl	tc_init(&hpet_timecounter);
201159217Snjl
202159217Snjl	return (0);
203159217Snjl}
204159217Snjl
205159217Snjlstatic int
206159217Snjlacpi_hpet_detach(device_t dev)
207159217Snjl{
208159217Snjl	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
209159217Snjl
210159217Snjl	/* XXX Without a tc_remove() function, we can't detach. */
211159217Snjl	return (EBUSY);
212159217Snjl}
213159217Snjl
214168010Snjlstatic int
215168010Snjlacpi_hpet_resume(device_t dev)
216168010Snjl{
217168010Snjl	struct acpi_hpet_softc *sc;
218168010Snjl
219168010Snjl	/* Re-enable the timer after a resume to keep the clock advancing. */
220168010Snjl	sc = device_get_softc(dev);
221168010Snjl	bus_write_4(sc->mem_res, HPET_OFFSET_ENABLE, 1);
222168010Snjl
223168010Snjl	return (0);
224168010Snjl}
225168010Snjl
226159217Snjl/* Print some basic latency/rate information to assist in debugging. */
227159217Snjlstatic void
228159217Snjlacpi_hpet_test(struct acpi_hpet_softc *sc)
229159217Snjl{
230151912Sphk	int i;
231151912Sphk	uint32_t u1, u2;
232151912Sphk	struct bintime b0, b1, b2;
233151912Sphk	struct timespec ts;
234151912Sphk
235151912Sphk	binuptime(&b0);
236151912Sphk	binuptime(&b0);
237151912Sphk	binuptime(&b1);
238159217Snjl	u1 = bus_read_4(sc->mem_res, HPET_OFFSET_VALUE);
239151912Sphk	for (i = 1; i < 1000; i++)
240159217Snjl		u2 = bus_read_4(sc->mem_res, HPET_OFFSET_VALUE);
241151912Sphk	binuptime(&b2);
242159217Snjl	u2 = bus_read_4(sc->mem_res, HPET_OFFSET_VALUE);
243151912Sphk
244151912Sphk	bintime_sub(&b2, &b1);
245151912Sphk	bintime_sub(&b1, &b0);
246151912Sphk	bintime_sub(&b2, &b1);
247151912Sphk	bintime2timespec(&b2, &ts);
248151912Sphk
249159217Snjl	device_printf(sc->dev, "%ld.%09ld: %u ... %u = %u\n",
250151912Sphk	    (long)ts.tv_sec, ts.tv_nsec, u1, u2, u2 - u1);
251151912Sphk
252159217Snjl	device_printf(sc->dev, "time per call: %ld ns\n", ts.tv_nsec / 1000);
253151912Sphk}
254151912Sphk
255151912Sphkstatic device_method_t acpi_hpet_methods[] = {
256151912Sphk	/* Device interface */
257151912Sphk	DEVMETHOD(device_probe, acpi_hpet_probe),
258151912Sphk	DEVMETHOD(device_attach, acpi_hpet_attach),
259151912Sphk	DEVMETHOD(device_detach, acpi_hpet_detach),
260168010Snjl	DEVMETHOD(device_resume, acpi_hpet_resume),
261151912Sphk
262151912Sphk	{0, 0}
263151912Sphk};
264151912Sphk
265151912Sphkstatic driver_t	acpi_hpet_driver = {
266151912Sphk	"acpi_hpet",
267151912Sphk	acpi_hpet_methods,
268151912Sphk	sizeof(struct acpi_hpet_softc),
269151912Sphk};
270151912Sphk
271151912Sphk
272151912SphkDRIVER_MODULE(acpi_hpet, acpi, acpi_hpet_driver, acpi_hpet_devclass, 0, 0);
273151912SphkMODULE_DEPEND(acpi_hpet, acpi, 1, 1, 1);
274