acpi_hpet.c revision 151912
1/*-
2 * Copyright (c) 2005 Poul-Henning Kamp
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/dev/acpica/acpi_hpet.c 151912 2005-10-31 21:39:50Z phk $");
29
30#include "opt_acpi.h"
31#include <sys/param.h>
32#include <sys/kernel.h>
33#include <sys/module.h>
34#include <sys/rman.h>
35#include <sys/time.h>
36#include <sys/timetc.h>
37#include <sys/bus.h>
38#include <contrib/dev/acpica/acpi.h>
39#include "acpi_if.h"
40#include <dev/acpica/acpivar.h>
41
42ACPI_SERIAL_DECL(hpet, "ACPI HPET support");
43
44struct acpi_hpet_softc {
45	device_t		dev;
46	struct resource		*res[1];
47	ACPI_HANDLE		handle;
48};
49
50static unsigned hpet_get_timecount(struct timecounter *tc);
51
52struct timecounter hpet_timecounter = {
53	.tc_get_timecount =	hpet_get_timecount,
54	.tc_counter_mask =	~0u,
55	.tc_name =		"HPET",
56	.tc_quality =		-200,
57};
58
59static char *hpet_ids[] = { "PNP0103", NULL };
60
61static unsigned
62hpet_get_timecount(struct timecounter *tc)
63{
64	struct acpi_hpet_softc *sc;
65
66	sc = tc->tc_priv;
67	return (bus_read_4(sc->res[0], 0xf0));
68}
69
70static int
71acpi_hpet_probe(device_t dev)
72{
73	if (acpi_disabled("hpet") ||
74	    ACPI_ID_PROBE(device_get_parent(dev), dev, hpet_ids) == NULL ||
75	    device_get_unit(dev) != 0)
76		return (ENXIO);
77
78	device_set_desc(dev, "HPET - High Precision Event Timers");
79	return (0);
80}
81
82static struct resource_spec hpet_res_spec[] = {
83	{ SYS_RES_MEMORY,	0,	RF_ACTIVE},
84	{ -1, 0, 0}
85};
86
87static int
88acpi_hpet_attach(device_t dev)
89{
90	struct acpi_hpet_softc	*sc;
91	int error;
92	uint32_t u;
93
94	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
95
96	sc = device_get_softc(dev);
97	sc->dev = dev;
98	sc->handle = acpi_get_handle(dev);
99
100	error = bus_alloc_resources(dev, hpet_res_spec, sc->res);
101	if (error)
102		return (error);
103
104	u = bus_read_4(sc->res[0], 0x0);
105	device_printf(dev, "Vendor: 0x%x\n", u >> 16);
106	device_printf(dev, "Leg_Route_Cap: %d\n", (u >> 15) & 1);
107	device_printf(dev, "Count_Size_Cap: %d\n", (u >> 13) & 1);
108	device_printf(dev, "Num_Tim_Cap: %d\n", (u >> 18) & 0xf);
109	device_printf(dev, "Rev_id: 0x%x\n", u & 0xff);
110
111	u = bus_read_4(sc->res[0], 0x4);
112	device_printf(dev, "Period: %d fs (%jd Hz)\n",
113	    u, (intmax_t)((1000000000000000LL + u / 2) / u));
114
115	hpet_timecounter.tc_frequency = (1000000000000000LL + u / 2) / u;
116
117	bus_write_4(sc->res[0], 0x10, 1);
118
119#if 0
120	{
121	int i;
122	uint32_t u1, u2;
123	struct bintime b0, b1, b2;
124	struct timespec ts;
125
126	binuptime(&b0);
127	binuptime(&b0);
128	binuptime(&b1);
129	u1 = bus_read_4(sc->res[0], 0xf0);
130	for (i = 1; i < 1000; i++)
131		u2 = bus_read_4(sc->res[0], 0xf0);
132	binuptime(&b2);
133	u2 = bus_read_4(sc->res[0], 0xf0);
134
135	bintime_sub(&b2, &b1);
136	bintime_sub(&b1, &b0);
137	bintime_sub(&b2, &b1);
138	bintime2timespec(&b2, &ts);
139
140	device_printf(dev, "%ld.%09ld: %u ... %u = %u\n",
141	    (long)ts.tv_sec, ts.tv_nsec, u1, u2, u2 - u1);
142
143	device_printf(dev, "time per call: %ld ns\n", ts.tv_nsec / 1000);
144	}
145#endif
146
147	device_printf(sc->dev, "HPET attach\n");
148
149	hpet_timecounter.tc_priv = sc;
150
151	tc_init(&hpet_timecounter);
152
153	return (0);
154}
155
156static int
157acpi_hpet_detach(device_t dev)
158{
159	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
160
161#if 1
162	return (EBUSY);
163#else
164	struct acpi_hpet_softc *sc = device_get_softc(dev);
165	bus_release_resources(dev, hpet_res_spec, sc->res);
166
167	device_printf(sc->dev, "HPET detach\n");
168	return (0);
169#endif
170}
171
172static device_method_t acpi_hpet_methods[] = {
173	/* Device interface */
174	DEVMETHOD(device_probe, acpi_hpet_probe),
175	DEVMETHOD(device_attach, acpi_hpet_attach),
176	DEVMETHOD(device_detach, acpi_hpet_detach),
177
178	{0, 0}
179};
180
181static driver_t	acpi_hpet_driver = {
182	"acpi_hpet",
183	acpi_hpet_methods,
184	sizeof(struct acpi_hpet_softc),
185};
186
187static devclass_t acpi_hpet_devclass;
188
189DRIVER_MODULE(acpi_hpet, acpi, acpi_hpet_driver, acpi_hpet_devclass, 0, 0);
190MODULE_DEPEND(acpi_hpet, acpi, 1, 1, 1);
191