acpi_timer.c revision 80602
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 80602 2001-07-30 08:57:55Z msmith $
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_pio.h>
37#include <machine/bus.h>
38#include <machine/resource.h>
39#include <sys/rman.h>
40
41#include "acpi.h"
42
43#include <acpica/acpivar.h>
44#include <pci/pcivar.h>
45
46/*
47 * A timecounter based on the free-running ACPI timer.
48 *
49 * Based on the i386-only mp_clock.c by <phk@FreeBSD.ORG>.
50 */
51
52/*
53 * Hooks for the ACPI CA debugging infrastructure
54 */
55#define _COMPONENT	ACPI_SYSTEM
56MODULE_NAME("TIMER")
57
58static device_t	acpi_timer_dev;
59struct resource	*acpi_timer_reg;
60#define TIMER_READ	bus_space_read_4(rman_get_bustag(acpi_timer_reg),	\
61					 rman_get_bushandle(acpi_timer_reg),	\
62					 0)
63
64static u_int	acpi_timer_frequency = 14318182/4;
65
66static void	acpi_timer_identify(driver_t *driver, device_t parent);
67static int	acpi_timer_probe(device_t dev);
68static int	acpi_timer_attach(device_t dev);
69static unsigned	acpi_timer_get_timecount(struct timecounter *tc);
70static int	acpi_timer_sysctl_freq(SYSCTL_HANDLER_ARGS);
71static void	acpi_timer_test(void);
72
73/*
74 * Driver hung off ACPI.
75 */
76static device_method_t acpi_timer_methods[] = {
77    DEVMETHOD(device_identify,	acpi_timer_identify),
78    DEVMETHOD(device_probe,	acpi_timer_probe),
79    DEVMETHOD(device_attach,	acpi_timer_attach),
80
81    {0, 0}
82};
83
84static driver_t acpi_timer_driver = {
85    "acpi_timer",
86    acpi_timer_methods,
87    0,
88};
89
90devclass_t acpi_timer_devclass;
91DRIVER_MODULE(acpi_timer, acpi, acpi_timer_driver, acpi_timer_devclass, 0, 0);
92
93/*
94 * Timecounter.
95 */
96static struct timecounter acpi_timer_timecounter = {
97    acpi_timer_get_timecount,
98    0,
99    0xffffff,
100    0,
101    "ACPI"
102};
103
104SYSCTL_OPAQUE(_debug, OID_AUTO, acpi_timecounter, CTLFLAG_RD,
105	      &acpi_timer_timecounter, sizeof(acpi_timer_timecounter), "S,timecounter", "");
106
107/*
108 * Locate the ACPI timer using the FADT, set up and allocate the I/O resources
109 * we will be using.
110 */
111static void
112acpi_timer_identify(driver_t *driver, device_t parent)
113{
114    device_t	dev;
115    char	desc[40];
116    int		rid;
117
118    FUNCTION_TRACE(__func__);
119
120    if (acpi_disabled("timer"))
121	return_VOID;
122
123    if (AcpiGbl_FADT == NULL)
124	return_VOID;
125
126    if ((dev = BUS_ADD_CHILD(parent, 0, "acpi_timer", 0)) == NULL) {
127	device_printf(parent, "could not add acpi_timer0\n");
128	return_VOID;
129    }
130    acpi_timer_dev = dev;
131    rid = 0;
132    bus_set_resource(dev, SYS_RES_IOPORT, rid, AcpiGbl_FADT->V1_PmTmrBlk, sizeof(u_int32_t));
133    if ((acpi_timer_reg = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE)) == NULL) {
134	device_printf(dev, "couldn't allocate I/O resource (port 0x%x)\n", AcpiGbl_FADT->V1_PmTmrBlk);
135	return_VOID;
136    }
137    if (getenv("debug.acpi.timer_test") != NULL)
138	acpi_timer_test();
139
140    acpi_timer_timecounter.tc_get_timecount = acpi_timer_get_timecount;
141    acpi_timer_timecounter.tc_frequency = acpi_timer_frequency;
142    tc_init(&acpi_timer_timecounter);
143
144    sprintf(desc, "%d-bit timer at 3.579545MHz", AcpiGbl_FADT->TmrValExt ? 32 : 24);
145    device_set_desc_copy(dev, desc);
146
147    return_VOID;
148}
149
150static int
151acpi_timer_probe(device_t dev)
152{
153    if (dev == acpi_timer_dev)
154	return(0);
155    return(ENXIO);
156}
157
158static int
159acpi_timer_attach(device_t dev)
160{
161    return(0);
162}
163
164/*
165 * Fetch current time value from hardware.
166 */
167static unsigned
168acpi_timer_get_timecount(struct timecounter *tc)
169{
170    return(TIMER_READ);
171}
172
173/*
174 * Timecounter freqency adjustment interface.
175 */
176static int
177acpi_timer_sysctl_freq(SYSCTL_HANDLER_ARGS)
178{
179    int error;
180    u_int freq;
181
182    if (acpi_timer_timecounter.tc_frequency == 0)
183	return (EOPNOTSUPP);
184    freq = acpi_timer_frequency;
185    error = sysctl_handle_int(oidp, &freq, sizeof(freq), req);
186    if (error == 0 && req->newptr != NULL) {
187	acpi_timer_frequency = freq;
188	acpi_timer_timecounter.tc_frequency = acpi_timer_frequency;
189	tc_update(&acpi_timer_timecounter);
190    }
191    return (error);
192}
193
194SYSCTL_PROC(_machdep, OID_AUTO, acpi_timer_freq, CTLTYPE_INT | CTLFLAG_RW,
195	    0, sizeof(u_int), acpi_timer_sysctl_freq, "I", "");
196
197/*
198 * Test harness for verifying ACPI timer behaviour.
199 * Boot with debug.acpi.timer_test set to invoke this.
200 */
201static void
202acpi_timer_test(void)
203{
204    u_int32_t	u1, u2, u3;
205
206    u1 = TIMER_READ;
207    u2 = TIMER_READ;
208    u3 = TIMER_READ;
209
210    device_printf(acpi_timer_dev, "timer test in progress, reboot to quit.\n");
211    for (;;) {
212	/*
213	 * The failure case is where u3 > u1, but u2 does not fall between the two,
214	 * ie. it contains garbage.
215	 */
216	if (u3 > u1) {
217	    if ((u2 < u1) || (u2 > u3))
218		device_printf(acpi_timer_dev, "timer is not monotonic: 0x%08x,0x%08x,0x%08x\n",
219			      u1, u2, u3);
220	}
221	u1 = u2;
222	u2 = u3;
223	u3 = TIMER_READ;
224    }
225}
226
227/*
228 * Chipset workaround driver hung off PCI.
229 *
230 * ] 20. ACPI Timer Errata
231 * ]
232 * ]   Problem: The power management timer may return improper result when
233 * ]   read. Although the timer value settles properly after incrementing,
234 * ]   while incrementing there is a 3nS window every 69.8nS where the
235 * ]   timer value is indeterminate (a 4.2% chance that the data will be
236 * ]   incorrect when read). As a result, the ACPI free running count up
237 * ]   timer specification is violated due to erroneous reads.  Implication:
238 * ]   System hangs due to the "inaccuracy" of the timer when used by
239 * ]   software for time critical events and delays.
240 * ]
241 * ] Workaround: Read the register twice and compare.
242 * ] Status: This will not be fixed in the PIIX4 or PIIX4E, it is fixed
243 * ] in the PIIX4M.
244 *
245 * The counter is in other words not latched to the PCI bus clock when
246 * read.  Notice the workaround isn't:  We need to read until we have
247 * three monotonic samples and then use the middle one, otherwise we are
248 * not protected against the fact that the bits can be wrong in two
249 * directions.  If we only cared about monosity two reads would be enough.
250 */
251
252static int	acpi_timer_pci_probe(device_t dev);
253static unsigned	acpi_timer_get_timecount_piix(struct timecounter *tc);
254
255static device_method_t acpi_timer_pci_methods[] = {
256    DEVMETHOD(device_probe,	acpi_timer_pci_probe),
257    {0, 0}
258};
259
260static driver_t acpi_timer_pci_driver = {
261    "acpi_timer_pci",
262    acpi_timer_pci_methods,
263    0,
264};
265
266devclass_t acpi_timer_pci_devclass;
267DRIVER_MODULE(acpi_timer_pci, pci, acpi_timer_pci_driver, acpi_timer_pci_devclass, 0, 0);
268
269/*
270 * Look at PCI devices as they go past, and if we detect a PIIX4 older than
271 * the PIIX4M, use an alternate get_timecount routine.
272 *
273 * XXX do we know that other timecounters work?  Perhaps we should test them?
274 */
275static int
276acpi_timer_pci_probe(device_t dev)
277{
278    if ((pci_get_vendor(dev) == 0x8086) &&
279	(pci_get_device(dev) == 0x7113) &&
280	(pci_get_revid(dev) < 0x03)) {
281	acpi_timer_timecounter.tc_get_timecount = acpi_timer_get_timecount_piix;
282	acpi_timer_timecounter.tc_name = "ACPI-PIIX";
283	device_printf(acpi_timer_dev, "enabling PIIX4 timer workaround\n");
284    }
285
286    return(ENXIO);		/* we never match anything */
287}
288
289/*
290 * Read the buggy PIIX4 ACPI timer and compensate for its behaviour.
291 */
292static unsigned
293acpi_timer_get_timecount_piix(struct timecounter *tc)
294{
295    unsigned u1, u2, u3;
296
297    u2 = TIMER_READ;
298    u3 = TIMER_READ;
299    do {
300	u1 = u2;
301	u2 = u3;
302	u3 = TIMER_READ;
303    } while (u1 > u2 || u2 > u3);
304    return (u2);
305}
306
307