acpi_hpet.c revision 151935
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 151935 2005-11-01 20:41:43Z scottl $");
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
44/* ACPI CA debugging */
45#define _COMPONENT	ACPI_TIMER
46ACPI_MODULE_NAME("HPET")
47
48struct acpi_hpet_softc {
49	device_t		dev;
50	struct resource		*res[1];
51	ACPI_HANDLE		handle;
52};
53
54static unsigned hpet_get_timecount(struct timecounter *tc);
55
56struct timecounter hpet_timecounter = {
57	.tc_get_timecount =	hpet_get_timecount,
58	.tc_counter_mask =	~0u,
59	.tc_name =		"HPET",
60	.tc_quality =		-200,
61};
62
63static char *hpet_ids[] = { "PNP0103", NULL };
64
65static unsigned
66hpet_get_timecount(struct timecounter *tc)
67{
68	struct acpi_hpet_softc *sc;
69
70	sc = tc->tc_priv;
71	return (bus_read_4(sc->res[0], 0xf0));
72}
73
74static int
75acpi_hpet_probe(device_t dev)
76{
77	if (acpi_disabled("hpet") ||
78	    ACPI_ID_PROBE(device_get_parent(dev), dev, hpet_ids) == NULL ||
79	    device_get_unit(dev) != 0)
80		return (ENXIO);
81
82	device_set_desc(dev, "HPET - High Precision Event Timers");
83	return (0);
84}
85
86static struct resource_spec hpet_res_spec[] = {
87	{ SYS_RES_MEMORY,	0,	RF_ACTIVE},
88	{ -1, 0, 0}
89};
90
91static int
92acpi_hpet_attach(device_t dev)
93{
94	struct acpi_hpet_softc	*sc;
95	int error;
96	uint32_t u;
97
98	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
99
100	sc = device_get_softc(dev);
101	sc->dev = dev;
102	sc->handle = acpi_get_handle(dev);
103
104	error = bus_alloc_resources(dev, hpet_res_spec, sc->res);
105	if (error)
106		return (error);
107
108	u = bus_read_4(sc->res[0], 0x0);
109	device_printf(dev, "Vendor: 0x%x\n", u >> 16);
110	device_printf(dev, "Leg_Route_Cap: %d\n", (u >> 15) & 1);
111	device_printf(dev, "Count_Size_Cap: %d\n", (u >> 13) & 1);
112	device_printf(dev, "Num_Tim_Cap: %d\n", (u >> 18) & 0xf);
113	device_printf(dev, "Rev_id: 0x%x\n", u & 0xff);
114
115	u = bus_read_4(sc->res[0], 0x4);
116	device_printf(dev, "Period: %d fs (%jd Hz)\n",
117	    u, (intmax_t)((1000000000000000LL + u / 2) / u));
118
119	hpet_timecounter.tc_frequency = (1000000000000000LL + u / 2) / u;
120
121	bus_write_4(sc->res[0], 0x10, 1);
122
123#if 0
124	{
125	int i;
126	uint32_t u1, u2;
127	struct bintime b0, b1, b2;
128	struct timespec ts;
129
130	binuptime(&b0);
131	binuptime(&b0);
132	binuptime(&b1);
133	u1 = bus_read_4(sc->res[0], 0xf0);
134	for (i = 1; i < 1000; i++)
135		u2 = bus_read_4(sc->res[0], 0xf0);
136	binuptime(&b2);
137	u2 = bus_read_4(sc->res[0], 0xf0);
138
139	bintime_sub(&b2, &b1);
140	bintime_sub(&b1, &b0);
141	bintime_sub(&b2, &b1);
142	bintime2timespec(&b2, &ts);
143
144	device_printf(dev, "%ld.%09ld: %u ... %u = %u\n",
145	    (long)ts.tv_sec, ts.tv_nsec, u1, u2, u2 - u1);
146
147	device_printf(dev, "time per call: %ld ns\n", ts.tv_nsec / 1000);
148	}
149#endif
150
151	device_printf(sc->dev, "HPET attach\n");
152
153	hpet_timecounter.tc_priv = sc;
154
155	tc_init(&hpet_timecounter);
156
157	return (0);
158}
159
160static int
161acpi_hpet_detach(device_t dev)
162{
163	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
164
165#if 1
166	return (EBUSY);
167#else
168	struct acpi_hpet_softc *sc = device_get_softc(dev);
169	bus_release_resources(dev, hpet_res_spec, sc->res);
170
171	device_printf(sc->dev, "HPET detach\n");
172	return (0);
173#endif
174}
175
176static device_method_t acpi_hpet_methods[] = {
177	/* Device interface */
178	DEVMETHOD(device_probe, acpi_hpet_probe),
179	DEVMETHOD(device_attach, acpi_hpet_attach),
180	DEVMETHOD(device_detach, acpi_hpet_detach),
181
182	{0, 0}
183};
184
185static driver_t	acpi_hpet_driver = {
186	"acpi_hpet",
187	acpi_hpet_methods,
188	sizeof(struct acpi_hpet_softc),
189};
190
191static devclass_t acpi_hpet_devclass;
192
193DRIVER_MODULE(acpi_hpet, acpi, acpi_hpet_driver, acpi_hpet_devclass, 0, 0);
194MODULE_DEPEND(acpi_hpet, acpi, 1, 1, 1);
195