xref: /freebsd/sys/dev/acpica/acpi_hpet.c (revision aa64588d28258aef88cc33b8043112e8856948d0)
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$");
29 
30 #include "opt_acpi.h"
31 #include <sys/param.h>
32 #include <sys/bus.h>
33 #include <sys/kernel.h>
34 #include <sys/module.h>
35 #include <sys/rman.h>
36 #include <sys/time.h>
37 #include <sys/timetc.h>
38 
39 #include <contrib/dev/acpica/include/acpi.h>
40 #include <contrib/dev/acpica/include/accommon.h>
41 
42 #include <dev/acpica/acpivar.h>
43 #include <dev/acpica/acpi_hpet.h>
44 
45 #define HPET_VENDID_AMD		0x4353
46 #define HPET_VENDID_INTEL	0x8086
47 
48 ACPI_SERIAL_DECL(hpet, "ACPI HPET support");
49 
50 static devclass_t acpi_hpet_devclass;
51 
52 /* ACPI CA debugging */
53 #define _COMPONENT	ACPI_TIMER
54 ACPI_MODULE_NAME("HPET")
55 
56 struct acpi_hpet_softc {
57 	device_t		dev;
58 	struct resource		*mem_res;
59 	ACPI_HANDLE		handle;
60 };
61 
62 static u_int hpet_get_timecount(struct timecounter *tc);
63 static void acpi_hpet_test(struct acpi_hpet_softc *sc);
64 
65 static char *hpet_ids[] = { "PNP0103", NULL };
66 
67 struct timecounter hpet_timecounter = {
68 	.tc_get_timecount =	hpet_get_timecount,
69 	.tc_counter_mask =	~0u,
70 	.tc_name =		"HPET",
71 	.tc_quality =		900,
72 };
73 
74 static u_int
75 hpet_get_timecount(struct timecounter *tc)
76 {
77 	struct acpi_hpet_softc *sc;
78 
79 	sc = tc->tc_priv;
80 	return (bus_read_4(sc->mem_res, HPET_MAIN_COUNTER));
81 }
82 
83 static void
84 hpet_enable(struct acpi_hpet_softc *sc)
85 {
86 	uint32_t val;
87 
88 	val = bus_read_4(sc->mem_res, HPET_CONFIG);
89 	val &= ~HPET_CNF_LEG_RT;
90 	val |= HPET_CNF_ENABLE;
91 	bus_write_4(sc->mem_res, HPET_CONFIG, val);
92 }
93 
94 static void
95 hpet_disable(struct acpi_hpet_softc *sc)
96 {
97 	uint32_t val;
98 
99 	val = bus_read_4(sc->mem_res, HPET_CONFIG);
100 	val &= ~HPET_CNF_ENABLE;
101 	bus_write_4(sc->mem_res, HPET_CONFIG, val);
102 }
103 
104 static ACPI_STATUS
105 acpi_hpet_find(ACPI_HANDLE handle, UINT32 level, void *context,
106     void **status)
107 {
108 	char 		**ids;
109 	uint32_t	id = (uint32_t)(uintptr_t)context;
110 	uint32_t	uid = 0;
111 
112 	for (ids = hpet_ids; *ids != NULL; ids++) {
113 		if (acpi_MatchHid(handle, *ids))
114 		        break;
115 	}
116 	if (*ids == NULL)
117 		return (AE_OK);
118 	if (ACPI_FAILURE(acpi_GetInteger(handle, "_UID", &uid)))
119 		uid = 0;
120 	if (id == uid)
121 		*((int *)status) = 1;
122 	return (AE_OK);
123 }
124 
125 /* Discover the HPET via the ACPI table of the same name. */
126 static void
127 acpi_hpet_identify(driver_t *driver, device_t parent)
128 {
129 	ACPI_TABLE_HPET *hpet;
130 	ACPI_STATUS	status;
131 	device_t	child;
132 	int 		i, found;
133 
134 	/* Only one HPET device can be added. */
135 	if (devclass_get_device(acpi_hpet_devclass, 0))
136 		return;
137 	for (i = 1; ; i++) {
138 		/* Search for HPET table. */
139 		status = AcpiGetTable(ACPI_SIG_HPET, i, (ACPI_TABLE_HEADER **)&hpet);
140 		if (ACPI_FAILURE(status))
141 			return;
142 		/* Search for HPET device with same ID. */
143 		found = 0;
144 		AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
145 		    100, acpi_hpet_find, NULL, (void *)(uintptr_t)hpet->Sequence, (void *)&found);
146 		/* If found - let it be probed in normal way. */
147 		if (found)
148 			continue;
149 		/* If not - create it from table info. */
150 		child = BUS_ADD_CHILD(parent, ACPI_DEV_BASE_ORDER, "acpi_hpet", 0);
151 		if (child == NULL) {
152 			printf("%s: can't add child\n", __func__);
153 			continue;
154 		}
155 		bus_set_resource(child, SYS_RES_MEMORY, 0, hpet->Address.Address,
156 		    HPET_MEM_WIDTH);
157 	}
158 }
159 
160 static int
161 acpi_hpet_probe(device_t dev)
162 {
163 	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
164 
165 	if (acpi_disabled("hpet"))
166 		return (ENXIO);
167 	if (acpi_get_handle(dev) != NULL &&
168 	    ACPI_ID_PROBE(device_get_parent(dev), dev, hpet_ids) == NULL)
169 		return (ENXIO);
170 
171 	device_set_desc(dev, "High Precision Event Timer");
172 	return (0);
173 }
174 
175 static int
176 acpi_hpet_attach(device_t dev)
177 {
178 	struct acpi_hpet_softc *sc;
179 	int rid, num_timers;
180 	uint32_t val, val2;
181 	uintmax_t freq;
182 	uint16_t vendor;
183 
184 	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
185 
186 	sc = device_get_softc(dev);
187 	sc->dev = dev;
188 	sc->handle = acpi_get_handle(dev);
189 
190 	rid = 0;
191 	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
192 	    RF_ACTIVE);
193 	if (sc->mem_res == NULL)
194 		return (ENOMEM);
195 
196 	/* Validate that we can access the whole region. */
197 	if (rman_get_size(sc->mem_res) < HPET_MEM_WIDTH) {
198 		device_printf(dev, "memory region width %ld too small\n",
199 		    rman_get_size(sc->mem_res));
200 		bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
201 		return (ENXIO);
202 	}
203 
204 	/* Be sure timer is enabled. */
205 	hpet_enable(sc);
206 
207 	/* Read basic statistics about the timer. */
208 	val = bus_read_4(sc->mem_res, HPET_PERIOD);
209 	if (val == 0) {
210 		device_printf(dev, "invalid period\n");
211 		hpet_disable(sc);
212 		bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
213 		return (ENXIO);
214 	}
215 
216 	freq = (1000000000000000LL + val / 2) / val;
217 	if (bootverbose) {
218 		val = bus_read_4(sc->mem_res, HPET_CAPABILITIES);
219 
220 		/*
221 		 * ATI/AMD violates IA-PC HPET (High Precision Event Timers)
222 		 * Specification and provides an off by one number
223 		 * of timers/comparators.
224 		 * Additionally, they use unregistered value in VENDOR_ID field.
225 		 */
226 		num_timers = 1 + ((val & HPET_CAP_NUM_TIM) >> 8);
227 		vendor = val >> 16;
228 		if (vendor == HPET_VENDID_AMD && num_timers > 0)
229 			num_timers--;
230 		device_printf(dev,
231 		    "vend: 0x%x rev: 0x%x num: %d hz: %jd opts:%s%s\n",
232 		    vendor, val & HPET_CAP_REV_ID,
233 		    num_timers, freq,
234 		    (val & HPET_CAP_LEG_RT) ? " legacy_route" : "",
235 		    (val & HPET_CAP_COUNT_SIZE) ? " 64-bit" : "");
236 	}
237 
238 	if (testenv("debug.acpi.hpet_test"))
239 		acpi_hpet_test(sc);
240 
241 	/*
242 	 * Don't attach if the timer never increments.  Since the spec
243 	 * requires it to be at least 10 MHz, it has to change in 1 us.
244 	 */
245 	val = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
246 	DELAY(1);
247 	val2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
248 	if (val == val2) {
249 		device_printf(dev, "HPET never increments, disabling\n");
250 		hpet_disable(sc);
251 		bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
252 		return (ENXIO);
253 	}
254 	/* Announce first HPET as timecounter. */
255 	if (device_get_unit(dev) == 0) {
256 		hpet_timecounter.tc_frequency = freq;
257 		hpet_timecounter.tc_priv = sc;
258 		tc_init(&hpet_timecounter);
259 	}
260 	return (0);
261 }
262 
263 static int
264 acpi_hpet_detach(device_t dev)
265 {
266 	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
267 
268 	/* XXX Without a tc_remove() function, we can't detach. */
269 	return (EBUSY);
270 }
271 
272 static int
273 acpi_hpet_suspend(device_t dev)
274 {
275 	struct acpi_hpet_softc *sc;
276 
277 	/*
278 	 * Disable the timer during suspend.  The timer will not lose
279 	 * its state in S1 or S2, but we are required to disable
280 	 * it.
281 	 */
282 	sc = device_get_softc(dev);
283 	hpet_disable(sc);
284 
285 	return (0);
286 }
287 
288 static int
289 acpi_hpet_resume(device_t dev)
290 {
291 	struct acpi_hpet_softc *sc;
292 
293 	/* Re-enable the timer after a resume to keep the clock advancing. */
294 	sc = device_get_softc(dev);
295 	hpet_enable(sc);
296 
297 	return (0);
298 }
299 
300 /* Print some basic latency/rate information to assist in debugging. */
301 static void
302 acpi_hpet_test(struct acpi_hpet_softc *sc)
303 {
304 	int i;
305 	uint32_t u1, u2;
306 	struct bintime b0, b1, b2;
307 	struct timespec ts;
308 
309 	binuptime(&b0);
310 	binuptime(&b0);
311 	binuptime(&b1);
312 	u1 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
313 	for (i = 1; i < 1000; i++)
314 		u2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
315 	binuptime(&b2);
316 	u2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
317 
318 	bintime_sub(&b2, &b1);
319 	bintime_sub(&b1, &b0);
320 	bintime_sub(&b2, &b1);
321 	bintime2timespec(&b2, &ts);
322 
323 	device_printf(sc->dev, "%ld.%09ld: %u ... %u = %u\n",
324 	    (long)ts.tv_sec, ts.tv_nsec, u1, u2, u2 - u1);
325 
326 	device_printf(sc->dev, "time per call: %ld ns\n", ts.tv_nsec / 1000);
327 }
328 
329 static device_method_t acpi_hpet_methods[] = {
330 	/* Device interface */
331 	DEVMETHOD(device_identify, acpi_hpet_identify),
332 	DEVMETHOD(device_probe, acpi_hpet_probe),
333 	DEVMETHOD(device_attach, acpi_hpet_attach),
334 	DEVMETHOD(device_detach, acpi_hpet_detach),
335 	DEVMETHOD(device_suspend, acpi_hpet_suspend),
336 	DEVMETHOD(device_resume, acpi_hpet_resume),
337 
338 	{0, 0}
339 };
340 
341 static driver_t	acpi_hpet_driver = {
342 	"acpi_hpet",
343 	acpi_hpet_methods,
344 	sizeof(struct acpi_hpet_softc),
345 };
346 
347 
348 DRIVER_MODULE(acpi_hpet, acpi, acpi_hpet_driver, acpi_hpet_devclass, 0, 0);
349 MODULE_DEPEND(acpi_hpet, acpi, 1, 1, 1);
350