acpi_hpet.c revision 169574
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 169574 2007-05-15 08:41:05Z takawata $");
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
67151912Sphkstruct timecounter hpet_timecounter = {
68151912Sphk	.tc_get_timecount =	hpet_get_timecount,
69151912Sphk	.tc_counter_mask =	~0u,
70151912Sphk	.tc_name =		"HPET",
71161211Sdes	.tc_quality =		2000,
72151912Sphk};
73151912Sphk
74159217Snjlstatic u_int
75151912Sphkhpet_get_timecount(struct timecounter *tc)
76151912Sphk{
77151912Sphk	struct acpi_hpet_softc *sc;
78151912Sphk
79151912Sphk	sc = tc->tc_priv;
80159217Snjl	return (bus_read_4(sc->mem_res, HPET_OFFSET_VALUE));
81151912Sphk}
82151912Sphk
83169574Stakawata#define DEV_HPET(x)		(acpi_get_magic(x) == (int)&acpi_hpet_devclass)
84169574Stakawata
85169574Stakawatavoid
86169574Stakawataacpi_hpet_table_probe(device_t parent)
87169574Stakawata{
88169574Stakawata	ACPI_TABLE_HPET *hpet;
89169574Stakawata	ACPI_TABLE_HEADER *hdr;
90169574Stakawata	ACPI_STATUS	status;
91169574Stakawata	device_t	child;
92169574Stakawata
93169574Stakawata	/*Currently, id and minimam clock tick info. is discarded.*/
94169574Stakawata
95169574Stakawata	status = AcpiGetTable(ACPI_SIG_HPET, 1, (ACPI_TABLE_HEADER **)&hdr);
96169574Stakawata	if (ACPI_FAILURE(status))
97169574Stakawata		return;
98169574Stakawata
99169574Stakawata	hpet = (ACPI_TABLE_HPET *) hdr;
100169574Stakawata
101169574Stakawata	/*unit No.hdr->Sequence*/
102169574Stakawata	if(hpet->Sequence)
103169574Stakawata		printf("HPET TABLE:Sequense is non zero %d\n", hpet->Sequence);
104169574Stakawata
105169574Stakawata	child = BUS_ADD_CHILD(parent, 0, "acpi_hpet", 0);
106169574Stakawata
107169574Stakawata	if (child == NULL) {
108169574Stakawata		printf("%s: can't add child\n", __func__);
109169574Stakawata		return;
110169574Stakawata	}
111169574Stakawata
112169574Stakawata	acpi_set_magic(child, (int)&acpi_hpet_devclass);
113169574Stakawata	bus_set_resource(child, SYS_RES_MEMORY, 0, hpet->Address.Address, HPET_MEM_WIDTH);
114169574Stakawata	if(device_probe_and_attach(child) != 0)
115169574Stakawata		device_delete_child(parent, child);
116169574Stakawata}
117169574Stakawata
118151912Sphkstatic int
119151912Sphkacpi_hpet_probe(device_t dev)
120151912Sphk{
121159217Snjl	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
122159217Snjl
123169574Stakawata	if (acpi_disabled("hpet")||
124169574Stakawata	    !DEV_HPET(dev) ||
125151912Sphk	    ACPI_ID_PROBE(device_get_parent(dev), dev, hpet_ids) == NULL ||
126151912Sphk	    device_get_unit(dev) != 0)
127151912Sphk		return (ENXIO);
128151912Sphk
129159217Snjl	device_set_desc(dev, "High Precision Event Timer");
130151912Sphk	return (0);
131151912Sphk}
132151912Sphk
133151912Sphkstatic int
134151912Sphkacpi_hpet_attach(device_t dev)
135151912Sphk{
136159217Snjl	struct acpi_hpet_softc *sc;
137159217Snjl	int rid;
138159217Snjl	uint32_t val;
139159217Snjl	uintmax_t freq;
140151912Sphk
141151912Sphk	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
142151912Sphk
143151912Sphk	sc = device_get_softc(dev);
144151912Sphk	sc->dev = dev;
145151912Sphk	sc->handle = acpi_get_handle(dev);
146151912Sphk
147159217Snjl	rid = 0;
148159217Snjl	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
149159217Snjl	    RF_ACTIVE);
150159217Snjl	if (sc->mem_res == NULL)
151159217Snjl		return (ENOMEM);
152151912Sphk
153159217Snjl	/* Validate that we can access the whole region. */
154159217Snjl	if (rman_get_size(sc->mem_res) < HPET_MEM_WIDTH) {
155159217Snjl		device_printf(dev, "memory region width %ld too small\n",
156159217Snjl		    rman_get_size(sc->mem_res));
157159217Snjl		bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
158159217Snjl		return (ENXIO);
159159217Snjl	}
160151912Sphk
161159217Snjl	/* Read basic statistics about the timer. */
162159217Snjl	val = bus_read_4(sc->mem_res, HPET_OFFSET_PERIOD);
163159217Snjl	freq = (1000000000000000LL + val / 2) / val;
164159217Snjl	if (bootverbose) {
165159217Snjl		val = bus_read_4(sc->mem_res, HPET_OFFSET_INFO);
166159217Snjl		device_printf(dev,
167159217Snjl		    "vend: 0x%x rev: 0x%x num: %d hz: %jd opts:%s%s\n",
168159217Snjl		    val >> 16, val & 0xff, (val >> 18) & 0xf, freq,
169159217Snjl		    ((val >> 15) & 1) ? " leg_route" : "",
170159217Snjl		    ((val >> 13) & 1) ? " count_size" : "");
171159217Snjl	}
172151912Sphk
173159217Snjl	/* Be sure it is enabled. */
174159217Snjl	bus_write_4(sc->mem_res, HPET_OFFSET_ENABLE, 1);
175151912Sphk
176159217Snjl	if (testenv("debug.acpi.hpet_test"))
177159217Snjl		acpi_hpet_test(sc);
178151912Sphk
179159217Snjl	hpet_timecounter.tc_frequency = freq;
180159217Snjl	hpet_timecounter.tc_priv = sc;
181159217Snjl	tc_init(&hpet_timecounter);
182159217Snjl
183159217Snjl	return (0);
184159217Snjl}
185159217Snjl
186159217Snjlstatic int
187159217Snjlacpi_hpet_detach(device_t dev)
188159217Snjl{
189159217Snjl	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
190159217Snjl
191159217Snjl	/* XXX Without a tc_remove() function, we can't detach. */
192159217Snjl	return (EBUSY);
193159217Snjl}
194159217Snjl
195168010Snjlstatic int
196168010Snjlacpi_hpet_resume(device_t dev)
197168010Snjl{
198168010Snjl	struct acpi_hpet_softc *sc;
199168010Snjl
200168010Snjl	/* Re-enable the timer after a resume to keep the clock advancing. */
201168010Snjl	sc = device_get_softc(dev);
202168010Snjl	bus_write_4(sc->mem_res, HPET_OFFSET_ENABLE, 1);
203168010Snjl
204168010Snjl	return (0);
205168010Snjl}
206168010Snjl
207159217Snjl/* Print some basic latency/rate information to assist in debugging. */
208159217Snjlstatic void
209159217Snjlacpi_hpet_test(struct acpi_hpet_softc *sc)
210159217Snjl{
211151912Sphk	int i;
212151912Sphk	uint32_t u1, u2;
213151912Sphk	struct bintime b0, b1, b2;
214151912Sphk	struct timespec ts;
215151912Sphk
216151912Sphk	binuptime(&b0);
217151912Sphk	binuptime(&b0);
218151912Sphk	binuptime(&b1);
219159217Snjl	u1 = bus_read_4(sc->mem_res, HPET_OFFSET_VALUE);
220151912Sphk	for (i = 1; i < 1000; i++)
221159217Snjl		u2 = bus_read_4(sc->mem_res, HPET_OFFSET_VALUE);
222151912Sphk	binuptime(&b2);
223159217Snjl	u2 = bus_read_4(sc->mem_res, HPET_OFFSET_VALUE);
224151912Sphk
225151912Sphk	bintime_sub(&b2, &b1);
226151912Sphk	bintime_sub(&b1, &b0);
227151912Sphk	bintime_sub(&b2, &b1);
228151912Sphk	bintime2timespec(&b2, &ts);
229151912Sphk
230159217Snjl	device_printf(sc->dev, "%ld.%09ld: %u ... %u = %u\n",
231151912Sphk	    (long)ts.tv_sec, ts.tv_nsec, u1, u2, u2 - u1);
232151912Sphk
233159217Snjl	device_printf(sc->dev, "time per call: %ld ns\n", ts.tv_nsec / 1000);
234151912Sphk}
235151912Sphk
236151912Sphkstatic device_method_t acpi_hpet_methods[] = {
237151912Sphk	/* Device interface */
238151912Sphk	DEVMETHOD(device_probe, acpi_hpet_probe),
239151912Sphk	DEVMETHOD(device_attach, acpi_hpet_attach),
240151912Sphk	DEVMETHOD(device_detach, acpi_hpet_detach),
241168010Snjl	DEVMETHOD(device_resume, acpi_hpet_resume),
242151912Sphk
243151912Sphk	{0, 0}
244151912Sphk};
245151912Sphk
246151912Sphkstatic driver_t	acpi_hpet_driver = {
247151912Sphk	"acpi_hpet",
248151912Sphk	acpi_hpet_methods,
249151912Sphk	sizeof(struct acpi_hpet_softc),
250151912Sphk};
251151912Sphk
252151912Sphk
253151912SphkDRIVER_MODULE(acpi_hpet, acpi, acpi_hpet_driver, acpi_hpet_devclass, 0, 0);
254151912SphkMODULE_DEPEND(acpi_hpet, acpi, 1, 1, 1);
255