acpi_timer.c revision 128506
1/*-
2 * Copyright (c) 2000, 2001 Michael Smith
3 * Copyright (c) 2000 BSDi
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 *	$FreeBSD: head/sys/dev/acpica/acpi_timer.c 128506 2004-04-21 00:48:16Z njl $
28 */
29#include "opt_acpi.h"
30#include <sys/param.h>
31#include <sys/bus.h>
32#include <sys/kernel.h>
33#include <sys/sysctl.h>
34#include <sys/timetc.h>
35
36#include <machine/bus.h>
37#include <machine/resource.h>
38#include <sys/rman.h>
39
40#include "acpi.h"
41#include <dev/acpica/acpivar.h>
42#include <dev/pci/pcivar.h>
43
44/*
45 * A timecounter based on the free-running ACPI timer.
46 *
47 * Based on the i386-only mp_clock.c by <phk@FreeBSD.ORG>.
48 */
49
50/* Hooks for the ACPI CA debugging infrastructure */
51#define _COMPONENT	ACPI_TIMER
52ACPI_MODULE_NAME("TIMER")
53
54static device_t	acpi_timer_dev;
55struct resource	*acpi_timer_reg;
56
57static u_int	acpi_timer_frequency = 14318182 / 4;
58
59static void	acpi_timer_identify(driver_t *driver, device_t parent);
60static int	acpi_timer_probe(device_t dev);
61static int	acpi_timer_attach(device_t dev);
62static u_int	acpi_timer_get_timecount(struct timecounter *tc);
63static u_int	acpi_timer_get_timecount_safe(struct timecounter *tc);
64static int	acpi_timer_sysctl_freq(SYSCTL_HANDLER_ARGS);
65static void	acpi_timer_test(void);
66
67static u_int	read_counter(void);
68static int	test_counter(void);
69
70static device_method_t acpi_timer_methods[] = {
71    DEVMETHOD(device_identify,	acpi_timer_identify),
72    DEVMETHOD(device_probe,	acpi_timer_probe),
73    DEVMETHOD(device_attach,	acpi_timer_attach),
74
75    {0, 0}
76};
77
78static driver_t acpi_timer_driver = {
79    "acpi_timer",
80    acpi_timer_methods,
81    0,
82};
83
84static devclass_t acpi_timer_devclass;
85DRIVER_MODULE(acpi_timer, acpi, acpi_timer_driver, acpi_timer_devclass, 0, 0);
86MODULE_DEPEND(acpi_timer, acpi, 1, 1, 1);
87
88static struct timecounter acpi_timer_timecounter = {
89	.tc_get_timecount =	acpi_timer_get_timecount_safe,
90	.tc_poll_pps =		0,
91	.tc_counter_mask =	0,
92	.tc_frequency =		0,
93	.tc_name =		"ACPI",
94	.tc_quality =		1000
95};
96
97static u_int
98read_counter()
99{
100    bus_space_handle_t bsh;
101    bus_space_tag_t bst;
102    uint32_t tv;
103
104    bsh = rman_get_bushandle(acpi_timer_reg);
105    bst = rman_get_bustag(acpi_timer_reg);
106    tv = bus_space_read_4(bst, bsh, 0);
107    bus_space_barrier(bst, bsh, 0, 4, BUS_SPACE_BARRIER_READ);
108
109    return (tv);
110}
111
112#define N 2000
113static int
114test_counter()
115{
116    uint32_t	last, this;
117    int		min, max, n, delta;
118
119    min = 10000000;
120    max = 0;
121    last = read_counter();
122    for (n = 0; n < N; n++) {
123	this = read_counter();
124	delta = acpi_TimerDelta(this, last);
125	if (delta > max)
126	    max = delta;
127	else if (delta < min)
128	    min = delta;
129	last = this;
130    }
131    if (max - min > 2)
132	n = 0;
133    else if (min < 0 || max == 0)
134	n = 0;
135    else
136	n = 1;
137    if (bootverbose) {
138	printf("ACPI timer looks %s min = %d, max = %d, width = %d\n",
139		n ? "GOOD" : "BAD ",
140		min, max, max - min);
141    }
142
143    return (n);
144}
145#undef N
146
147/*
148 * Locate the ACPI timer using the FADT, set up and allocate the I/O resources
149 * we will be using.
150 */
151static void
152acpi_timer_identify(driver_t *driver, device_t parent)
153{
154    device_t	dev;
155    char	desc[40];
156    u_long	rlen, rstart;
157    int		i, j, rid, rtype;
158
159    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
160
161    if (acpi_disabled("timer") || AcpiGbl_FADT == NULL)
162	return_VOID;
163
164    if ((dev = BUS_ADD_CHILD(parent, 0, "acpi_timer", 0)) == NULL) {
165	device_printf(parent, "could not add acpi_timer0\n");
166	return_VOID;
167    }
168    acpi_timer_dev = dev;
169
170    rid = 0;
171    rlen = AcpiGbl_FADT->PmTmLen;
172    rtype = (AcpiGbl_FADT->XPmTmrBlk.AddressSpaceId)
173      ? SYS_RES_IOPORT : SYS_RES_MEMORY;
174    rstart = AcpiGbl_FADT->XPmTmrBlk.Address;
175    bus_set_resource(dev, rtype, rid, rstart, rlen);
176    acpi_timer_reg = bus_alloc_resource_any(dev, rtype, &rid, RF_ACTIVE);
177    if (acpi_timer_reg == NULL) {
178	device_printf(dev, "couldn't allocate I/O resource (%s 0x%lx)\n",
179		      rtype == SYS_RES_IOPORT ? "port" : "mem", rstart);
180	return_VOID;
181    }
182    if (AcpiGbl_FADT->TmrValExt != 0)
183	acpi_timer_timecounter.tc_counter_mask = 0xffffffff;
184    else
185	acpi_timer_timecounter.tc_counter_mask = 0x00ffffff;
186    acpi_timer_timecounter.tc_frequency = acpi_timer_frequency;
187    if (testenv("debug.acpi.timer_test"))
188	acpi_timer_test();
189
190    j = 0;
191    for (i = 0; i < 10; i++)
192	j += test_counter();
193    if (j == 10) {
194	acpi_timer_timecounter.tc_name = "ACPI-fast";
195	acpi_timer_timecounter.tc_get_timecount = acpi_timer_get_timecount;
196    } else {
197	acpi_timer_timecounter.tc_name = "ACPI-safe";
198	acpi_timer_timecounter.tc_get_timecount = acpi_timer_get_timecount_safe;
199    }
200    tc_init(&acpi_timer_timecounter);
201
202    sprintf(desc, "%d-bit timer at 3.579545MHz",
203	    AcpiGbl_FADT->TmrValExt ? 32 : 24);
204    device_set_desc_copy(dev, desc);
205
206    return_VOID;
207}
208
209static int
210acpi_timer_probe(device_t dev)
211{
212    if (dev == acpi_timer_dev)
213	return (0);
214
215    return (ENXIO);
216}
217
218static int
219acpi_timer_attach(device_t dev)
220{
221    return (0);
222}
223
224/*
225 * Fetch current time value from reliable hardware.
226 */
227static u_int
228acpi_timer_get_timecount(struct timecounter *tc)
229{
230    return (read_counter());
231}
232
233/*
234 * Fetch current time value from hardware that may not correctly
235 * latch the counter.
236 */
237static u_int
238acpi_timer_get_timecount_safe(struct timecounter *tc)
239{
240    u_int u1, u2, u3;
241
242    u2 = read_counter();
243    u3 = read_counter();
244    do {
245	u1 = u2;
246	u2 = u3;
247	u3 = read_counter();
248    } while (u1 > u2 || u2 > u3 || u3 - u1 > 15);
249
250    return (u2);
251}
252
253/*
254 * Timecounter freqency adjustment interface.
255 */
256static int
257acpi_timer_sysctl_freq(SYSCTL_HANDLER_ARGS)
258{
259    int error;
260    u_int freq;
261
262    if (acpi_timer_timecounter.tc_frequency == 0)
263	return (EOPNOTSUPP);
264    freq = acpi_timer_frequency;
265    error = sysctl_handle_int(oidp, &freq, sizeof(freq), req);
266    if (error == 0 && req->newptr != NULL) {
267	acpi_timer_frequency = freq;
268	acpi_timer_timecounter.tc_frequency = acpi_timer_frequency;
269    }
270
271    return (error);
272}
273
274SYSCTL_PROC(_machdep, OID_AUTO, acpi_timer_freq, CTLTYPE_INT | CTLFLAG_RW,
275	    0, sizeof(u_int), acpi_timer_sysctl_freq, "I", "");
276
277/*
278 * Test harness for verifying ACPI timer behaviour.
279 * Boot with debug.acpi.timer_test set to invoke this.
280 */
281static void
282acpi_timer_test(void)
283{
284    uint32_t u1, u2, u3;
285
286    u1 = read_counter();
287    u2 = read_counter();
288    u3 = read_counter();
289
290    device_printf(acpi_timer_dev, "timer test in progress, reboot to quit.\n");
291    for (;;) {
292	/*
293	 * The failure case is where u3 > u1, but u2 does not fall between
294	 * the two, ie. it contains garbage.
295	 */
296	if (u3 > u1) {
297	    if (u2 < u1 || u2 > u3)
298		device_printf(acpi_timer_dev,
299			      "timer is not monotonic: 0x%08x,0x%08x,0x%08x\n",
300			      u1, u2, u3);
301	}
302	u1 = u2;
303	u2 = u3;
304	u3 = read_counter();
305    }
306}
307