xref: /freebsd/sys/dev/acpica/acpi_timer.c (revision 72e5754cfbd176d861c9f24890437a9ca70eb876)
115e32d5dSMike Smith /*-
2787fa5b8SMike Smith  * Copyright (c) 2000, 2001 Michael Smith
315e32d5dSMike Smith  * Copyright (c) 2000 BSDi
415e32d5dSMike Smith  * All rights reserved.
515e32d5dSMike Smith  *
615e32d5dSMike Smith  * Redistribution and use in source and binary forms, with or without
715e32d5dSMike Smith  * modification, are permitted provided that the following conditions
815e32d5dSMike Smith  * are met:
915e32d5dSMike Smith  * 1. Redistributions of source code must retain the above copyright
1015e32d5dSMike Smith  *    notice, this list of conditions and the following disclaimer.
1115e32d5dSMike Smith  * 2. Redistributions in binary form must reproduce the above copyright
1215e32d5dSMike Smith  *    notice, this list of conditions and the following disclaimer in the
1315e32d5dSMike Smith  *    documentation and/or other materials provided with the distribution.
1415e32d5dSMike Smith  *
1515e32d5dSMike Smith  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1615e32d5dSMike Smith  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1715e32d5dSMike Smith  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1815e32d5dSMike Smith  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1915e32d5dSMike Smith  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2015e32d5dSMike Smith  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2115e32d5dSMike Smith  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2215e32d5dSMike Smith  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2315e32d5dSMike Smith  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2415e32d5dSMike Smith  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2515e32d5dSMike Smith  * SUCH DAMAGE.
2615e32d5dSMike Smith  *
2715e32d5dSMike Smith  *	$FreeBSD$
2815e32d5dSMike Smith  */
2915e32d5dSMike Smith #include "opt_acpi.h"
3015e32d5dSMike Smith #include <sys/param.h>
3115e32d5dSMike Smith #include <sys/bus.h>
32787fa5b8SMike Smith #include <sys/kernel.h>
33787fa5b8SMike Smith #include <sys/sysctl.h>
34787fa5b8SMike Smith #include <sys/timetc.h>
35787fa5b8SMike Smith 
36787fa5b8SMike Smith #include <machine/bus_pio.h>
37787fa5b8SMike Smith #include <machine/bus.h>
38787fa5b8SMike Smith #include <machine/resource.h>
39787fa5b8SMike Smith #include <sys/rman.h>
4015e32d5dSMike Smith 
4115e32d5dSMike Smith #include "acpi.h"
4215e32d5dSMike Smith 
43787fa5b8SMike Smith #include <acpica/acpivar.h>
44787fa5b8SMike Smith #include <pci/pcivar.h>
45787fa5b8SMike Smith 
46787fa5b8SMike Smith /*
47787fa5b8SMike Smith  * A timecounter based on the free-running ACPI timer.
48787fa5b8SMike Smith  *
49787fa5b8SMike Smith  * Based on the i386-only mp_clock.c by <phk@FreeBSD.ORG>.
50787fa5b8SMike Smith  */
5115e32d5dSMike Smith 
520ae55423SMike Smith /*
530ae55423SMike Smith  * Hooks for the ACPI CA debugging infrastructure
540ae55423SMike Smith  */
552a4ac806SMike Smith #define _COMPONENT	ACPI_SYSTEM
5672e5754cSMike Smith ACPI_MODULE_NAME("TIMER")
570ae55423SMike Smith 
58787fa5b8SMike Smith static device_t	acpi_timer_dev;
59787fa5b8SMike Smith struct resource	*acpi_timer_reg;
60787fa5b8SMike Smith #define TIMER_READ	bus_space_read_4(rman_get_bustag(acpi_timer_reg),	\
61787fa5b8SMike Smith 					 rman_get_bushandle(acpi_timer_reg),	\
62787fa5b8SMike Smith 					 0)
6315e32d5dSMike Smith 
64787fa5b8SMike Smith static u_int	acpi_timer_frequency = 14318182/4;
6515e32d5dSMike Smith 
6615e32d5dSMike Smith static void	acpi_timer_identify(driver_t *driver, device_t parent);
6715e32d5dSMike Smith static int	acpi_timer_probe(device_t dev);
6815e32d5dSMike Smith static int	acpi_timer_attach(device_t dev);
69787fa5b8SMike Smith static unsigned	acpi_timer_get_timecount(struct timecounter *tc);
70feade919SMike Smith static unsigned	acpi_timer_get_timecount_safe(struct timecounter *tc);
71787fa5b8SMike Smith static int	acpi_timer_sysctl_freq(SYSCTL_HANDLER_ARGS);
72787fa5b8SMike Smith static void	acpi_timer_test(void);
7315e32d5dSMike Smith 
74787fa5b8SMike Smith /*
75787fa5b8SMike Smith  * Driver hung off ACPI.
76787fa5b8SMike Smith  */
7715e32d5dSMike Smith static device_method_t acpi_timer_methods[] = {
7815e32d5dSMike Smith     DEVMETHOD(device_identify,	acpi_timer_identify),
7915e32d5dSMike Smith     DEVMETHOD(device_probe,	acpi_timer_probe),
8015e32d5dSMike Smith     DEVMETHOD(device_attach,	acpi_timer_attach),
8115e32d5dSMike Smith 
8215e32d5dSMike Smith     {0, 0}
8315e32d5dSMike Smith };
8415e32d5dSMike Smith 
8515e32d5dSMike Smith static driver_t acpi_timer_driver = {
8615e32d5dSMike Smith     "acpi_timer",
8715e32d5dSMike Smith     acpi_timer_methods,
88787fa5b8SMike Smith     0,
8915e32d5dSMike Smith };
9015e32d5dSMike Smith 
913273b005SMike Smith static devclass_t acpi_timer_devclass;
9215e32d5dSMike Smith DRIVER_MODULE(acpi_timer, acpi, acpi_timer_driver, acpi_timer_devclass, 0, 0);
9315e32d5dSMike Smith 
94787fa5b8SMike Smith /*
95787fa5b8SMike Smith  * Timecounter.
96787fa5b8SMike Smith  */
97787fa5b8SMike Smith static struct timecounter acpi_timer_timecounter = {
98feade919SMike Smith     acpi_timer_get_timecount_safe,
99787fa5b8SMike Smith     0,
100787fa5b8SMike Smith     0xffffff,
101787fa5b8SMike Smith     0,
102787fa5b8SMike Smith     "ACPI"
103787fa5b8SMike Smith };
104787fa5b8SMike Smith 
105787fa5b8SMike Smith SYSCTL_OPAQUE(_debug, OID_AUTO, acpi_timecounter, CTLFLAG_RD,
106787fa5b8SMike Smith 	      &acpi_timer_timecounter, sizeof(acpi_timer_timecounter), "S,timecounter", "");
107787fa5b8SMike Smith 
108787fa5b8SMike Smith /*
109787fa5b8SMike Smith  * Locate the ACPI timer using the FADT, set up and allocate the I/O resources
110787fa5b8SMike Smith  * we will be using.
111787fa5b8SMike Smith  */
11215e32d5dSMike Smith static void
11315e32d5dSMike Smith acpi_timer_identify(driver_t *driver, device_t parent)
11415e32d5dSMike Smith {
11515e32d5dSMike Smith     device_t	dev;
11615e32d5dSMike Smith     char	desc[40];
117787fa5b8SMike Smith     int		rid;
11815e32d5dSMike Smith 
11972e5754cSMike Smith     ACPI_FUNCTION_TRACE(__func__);
1200ae55423SMike Smith 
1210ae55423SMike Smith     if (acpi_disabled("timer"))
1220ae55423SMike Smith 	return_VOID;
1230ae55423SMike Smith 
12491467fc6SMike Smith     if (AcpiGbl_FADT == NULL)
1250ae55423SMike Smith 	return_VOID;
12615e32d5dSMike Smith 
12715e32d5dSMike Smith     if ((dev = BUS_ADD_CHILD(parent, 0, "acpi_timer", 0)) == NULL) {
12815e32d5dSMike Smith 	device_printf(parent, "could not add acpi_timer0\n");
1290ae55423SMike Smith 	return_VOID;
13015e32d5dSMike Smith     }
131787fa5b8SMike Smith     acpi_timer_dev = dev;
132787fa5b8SMike Smith     rid = 0;
133787fa5b8SMike Smith     bus_set_resource(dev, SYS_RES_IOPORT, rid, AcpiGbl_FADT->V1_PmTmrBlk, sizeof(u_int32_t));
134787fa5b8SMike Smith     if ((acpi_timer_reg = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE)) == NULL) {
135787fa5b8SMike Smith 	device_printf(dev, "couldn't allocate I/O resource (port 0x%x)\n", AcpiGbl_FADT->V1_PmTmrBlk);
1360ae55423SMike Smith 	return_VOID;
13715e32d5dSMike Smith     }
138787fa5b8SMike Smith     if (getenv("debug.acpi.timer_test") != NULL)
139787fa5b8SMike Smith 	acpi_timer_test();
140787fa5b8SMike Smith 
141d8a9fe36SMike Smith     acpi_timer_timecounter.tc_get_timecount = acpi_timer_get_timecount;
142787fa5b8SMike Smith     acpi_timer_timecounter.tc_frequency = acpi_timer_frequency;
143787fa5b8SMike Smith     tc_init(&acpi_timer_timecounter);
14415e32d5dSMike Smith 
14591467fc6SMike Smith     sprintf(desc, "%d-bit timer at 3.579545MHz", AcpiGbl_FADT->TmrValExt ? 32 : 24);
14615e32d5dSMike Smith     device_set_desc_copy(dev, desc);
1470ae55423SMike Smith 
148feade919SMike Smith #if 0
149feade919SMike Smith     {
150feade919SMike Smith 	u_int64_t	first;
151feade919SMike Smith 
152feade919SMike Smith 	first = rdtsc();
153feade919SMike Smith 	acpi_timer_get_timecount(NULL);
154feade919SMike Smith 	printf("acpi_timer_get_timecount %lld cycles\n", rdtsc() - first);
155feade919SMike Smith 
156feade919SMike Smith 	first = rdtsc();
157feade919SMike Smith 	acpi_timer_get_timecount_safe(NULL);
158feade919SMike Smith 	printf("acpi_timer_get_timecount_safe %lld cycles\n", rdtsc() - first);
159feade919SMike Smith     }
160feade919SMike Smith #endif
161feade919SMike Smith 
1620ae55423SMike Smith     return_VOID;
16315e32d5dSMike Smith }
16415e32d5dSMike Smith 
16515e32d5dSMike Smith static int
16615e32d5dSMike Smith acpi_timer_probe(device_t dev)
16715e32d5dSMike Smith {
168787fa5b8SMike Smith     if (dev == acpi_timer_dev)
16915e32d5dSMike Smith 	return(0);
17015e32d5dSMike Smith     return(ENXIO);
17115e32d5dSMike Smith }
17215e32d5dSMike Smith 
17315e32d5dSMike Smith static int
17415e32d5dSMike Smith acpi_timer_attach(device_t dev)
17515e32d5dSMike Smith {
176787fa5b8SMike Smith     return(0);
17715e32d5dSMike Smith }
178787fa5b8SMike Smith 
179787fa5b8SMike Smith /*
180feade919SMike Smith  * Fetch current time value from reliable hardware.
181787fa5b8SMike Smith  */
182787fa5b8SMike Smith static unsigned
183787fa5b8SMike Smith acpi_timer_get_timecount(struct timecounter *tc)
184787fa5b8SMike Smith {
185787fa5b8SMike Smith     return(TIMER_READ);
186787fa5b8SMike Smith }
187787fa5b8SMike Smith 
188787fa5b8SMike Smith /*
189feade919SMike Smith  * Fetch current time value from hardware that may not correctly
190feade919SMike Smith  * latch the counter.
191feade919SMike Smith  */
192feade919SMike Smith static unsigned
193feade919SMike Smith acpi_timer_get_timecount_safe(struct timecounter *tc)
194feade919SMike Smith {
195feade919SMike Smith     unsigned u1, u2, u3;
196feade919SMike Smith 
197feade919SMike Smith     u2 = TIMER_READ;
198feade919SMike Smith     u3 = TIMER_READ;
199feade919SMike Smith     do {
200feade919SMike Smith 	u1 = u2;
201feade919SMike Smith 	u2 = u3;
202feade919SMike Smith 	u3 = TIMER_READ;
203feade919SMike Smith     } while (u1 > u2 || u2 > u3);
204feade919SMike Smith     return (u2);
205feade919SMike Smith }
206feade919SMike Smith 
207feade919SMike Smith /*
208787fa5b8SMike Smith  * Timecounter freqency adjustment interface.
209787fa5b8SMike Smith  */
210787fa5b8SMike Smith static int
211787fa5b8SMike Smith acpi_timer_sysctl_freq(SYSCTL_HANDLER_ARGS)
212787fa5b8SMike Smith {
213787fa5b8SMike Smith     int error;
214787fa5b8SMike Smith     u_int freq;
215787fa5b8SMike Smith 
216787fa5b8SMike Smith     if (acpi_timer_timecounter.tc_frequency == 0)
217787fa5b8SMike Smith 	return (EOPNOTSUPP);
218787fa5b8SMike Smith     freq = acpi_timer_frequency;
219787fa5b8SMike Smith     error = sysctl_handle_int(oidp, &freq, sizeof(freq), req);
220787fa5b8SMike Smith     if (error == 0 && req->newptr != NULL) {
221787fa5b8SMike Smith 	acpi_timer_frequency = freq;
222787fa5b8SMike Smith 	acpi_timer_timecounter.tc_frequency = acpi_timer_frequency;
223787fa5b8SMike Smith 	tc_update(&acpi_timer_timecounter);
224787fa5b8SMike Smith     }
225787fa5b8SMike Smith     return (error);
226787fa5b8SMike Smith }
227787fa5b8SMike Smith 
228787fa5b8SMike Smith SYSCTL_PROC(_machdep, OID_AUTO, acpi_timer_freq, CTLTYPE_INT | CTLFLAG_RW,
229787fa5b8SMike Smith 	    0, sizeof(u_int), acpi_timer_sysctl_freq, "I", "");
230787fa5b8SMike Smith 
231787fa5b8SMike Smith /*
232787fa5b8SMike Smith  * Test harness for verifying ACPI timer behaviour.
233787fa5b8SMike Smith  * Boot with debug.acpi.timer_test set to invoke this.
234787fa5b8SMike Smith  */
235787fa5b8SMike Smith static void
236787fa5b8SMike Smith acpi_timer_test(void)
237787fa5b8SMike Smith {
238787fa5b8SMike Smith     u_int32_t	u1, u2, u3;
239787fa5b8SMike Smith 
240787fa5b8SMike Smith     u1 = TIMER_READ;
241787fa5b8SMike Smith     u2 = TIMER_READ;
242787fa5b8SMike Smith     u3 = TIMER_READ;
243787fa5b8SMike Smith 
244787fa5b8SMike Smith     device_printf(acpi_timer_dev, "timer test in progress, reboot to quit.\n");
245787fa5b8SMike Smith     for (;;) {
246787fa5b8SMike Smith 	/*
247787fa5b8SMike Smith 	 * The failure case is where u3 > u1, but u2 does not fall between the two,
248787fa5b8SMike Smith 	 * ie. it contains garbage.
249787fa5b8SMike Smith 	 */
250787fa5b8SMike Smith 	if (u3 > u1) {
251787fa5b8SMike Smith 	    if ((u2 < u1) || (u2 > u3))
252787fa5b8SMike Smith 		device_printf(acpi_timer_dev, "timer is not monotonic: 0x%08x,0x%08x,0x%08x\n",
253787fa5b8SMike Smith 			      u1, u2, u3);
254787fa5b8SMike Smith 	}
255787fa5b8SMike Smith 	u1 = u2;
256787fa5b8SMike Smith 	u2 = u3;
257787fa5b8SMike Smith 	u3 = TIMER_READ;
258787fa5b8SMike Smith     }
259787fa5b8SMike Smith }
260787fa5b8SMike Smith 
261d8a9fe36SMike Smith /*
262d8a9fe36SMike Smith  * Chipset workaround driver hung off PCI.
263d8a9fe36SMike Smith  *
264feade919SMike Smith  * Some ACPI timers are known or believed to suffer from implementation
265feade919SMike Smith  * problems which can lead to erroneous values being read from the timer.
266feade919SMike Smith  *
267feade919SMike Smith  * Since we can't trust unknown chipsets, we default to a timer-read
268feade919SMike Smith  * routine which compensates for the most common problem (as detailed
269feade919SMike Smith  * in the excerpt from the Intel PIIX4 datasheet below).
270feade919SMike Smith  *
271feade919SMike Smith  * When we detect a known-functional chipset, we disable the workaround
272feade919SMike Smith  * to improve speed.
273feade919SMike Smith  *
274d8a9fe36SMike Smith  * ] 20. ACPI Timer Errata
275d8a9fe36SMike Smith  * ]
276d8a9fe36SMike Smith  * ]   Problem: The power management timer may return improper result when
277d8a9fe36SMike Smith  * ]   read. Although the timer value settles properly after incrementing,
278d8a9fe36SMike Smith  * ]   while incrementing there is a 3nS window every 69.8nS where the
279d8a9fe36SMike Smith  * ]   timer value is indeterminate (a 4.2% chance that the data will be
280d8a9fe36SMike Smith  * ]   incorrect when read). As a result, the ACPI free running count up
281d8a9fe36SMike Smith  * ]   timer specification is violated due to erroneous reads.  Implication:
282d8a9fe36SMike Smith  * ]   System hangs due to the "inaccuracy" of the timer when used by
283d8a9fe36SMike Smith  * ]   software for time critical events and delays.
284d8a9fe36SMike Smith  * ]
285d8a9fe36SMike Smith  * ] Workaround: Read the register twice and compare.
286d8a9fe36SMike Smith  * ] Status: This will not be fixed in the PIIX4 or PIIX4E, it is fixed
287d8a9fe36SMike Smith  * ] in the PIIX4M.
288d8a9fe36SMike Smith  *
289d8a9fe36SMike Smith  * The counter is in other words not latched to the PCI bus clock when
290d8a9fe36SMike Smith  * read.  Notice the workaround isn't:  We need to read until we have
291d8a9fe36SMike Smith  * three monotonic samples and then use the middle one, otherwise we are
292d8a9fe36SMike Smith  * not protected against the fact that the bits can be wrong in two
293d8a9fe36SMike Smith  * directions.  If we only cared about monosity two reads would be enough.
294d8a9fe36SMike Smith  */
295d8a9fe36SMike Smith 
296d8a9fe36SMike Smith static int	acpi_timer_pci_probe(device_t dev);
297d8a9fe36SMike Smith 
298d8a9fe36SMike Smith static device_method_t acpi_timer_pci_methods[] = {
299d8a9fe36SMike Smith     DEVMETHOD(device_probe,	acpi_timer_pci_probe),
300d8a9fe36SMike Smith     {0, 0}
301d8a9fe36SMike Smith };
302d8a9fe36SMike Smith 
303d8a9fe36SMike Smith static driver_t acpi_timer_pci_driver = {
304d8a9fe36SMike Smith     "acpi_timer_pci",
305d8a9fe36SMike Smith     acpi_timer_pci_methods,
306d8a9fe36SMike Smith     0,
307d8a9fe36SMike Smith };
308d8a9fe36SMike Smith 
309d8a9fe36SMike Smith devclass_t acpi_timer_pci_devclass;
310d8a9fe36SMike Smith DRIVER_MODULE(acpi_timer_pci, pci, acpi_timer_pci_driver, acpi_timer_pci_devclass, 0, 0);
311d8a9fe36SMike Smith 
312d8a9fe36SMike Smith /*
313feade919SMike Smith  * Look at PCI devices going past; if we detect one we know contains
314feade919SMike Smith  * a functional ACPI timer device, enable the faster timecounter read
315feade919SMike Smith  * routine.
316d8a9fe36SMike Smith  */
317d8a9fe36SMike Smith static int
318d8a9fe36SMike Smith acpi_timer_pci_probe(device_t dev)
319d8a9fe36SMike Smith {
320b2c98accSMike Smith     int vendor, device, revid;
321b2c98accSMike Smith 
322b2c98accSMike Smith     vendor = pci_get_vendor(dev);
323b2c98accSMike Smith     device = pci_get_device(dev);
324b2c98accSMike Smith     revid  = pci_get_revid(dev);
325b2c98accSMike Smith 
326b2c98accSMike Smith     if (((vendor == 0x8086) && (device == 0x7113) && (revid >= 0x03))	|| /* PIIX4M */
327b2c98accSMike Smith 	((vendor == 0x8086) && (device == 0x719b)) 			|| /* i440MX */
328b2c98accSMike Smith 	0) {
329b2c98accSMike Smith 
330feade919SMike Smith 	acpi_timer_timecounter.tc_get_timecount = acpi_timer_get_timecount;
331b2c98accSMike Smith 	acpi_timer_timecounter.tc_name = "ACPI-fast";
332feade919SMike Smith 	if (bootverbose)
333b2c98accSMike Smith 	    device_printf(acpi_timer_dev, "functional ACPI timer detected, enabling fast timecount interface\n");
334d8a9fe36SMike Smith     }
335d8a9fe36SMike Smith 
336d8a9fe36SMike Smith     return(ENXIO);		/* we never match anything */
337d8a9fe36SMike Smith }
338