xref: /freebsd/sys/dev/acpica/acpi_timer.c (revision 787fa5b8057b1db8d28bcd5c5bd064eb7e297585)
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  */
29787fa5b8SMike Smith /*
30787fa5b8SMike Smith  * ----------------------------------------------------------------------------
31787fa5b8SMike Smith  * "THE BEER-WARE LICENSE" (Revision 42):
32787fa5b8SMike Smith  * <phk@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
33787fa5b8SMike Smith  * can do whatever you want with this stuff. If we meet some day, and you think
34787fa5b8SMike Smith  * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
35787fa5b8SMike Smith  * ----------------------------------------------------------------------------
36787fa5b8SMike Smith  */
3715e32d5dSMike Smith #include "opt_acpi.h"
3815e32d5dSMike Smith #include <sys/param.h>
3915e32d5dSMike Smith #include <sys/bus.h>
40787fa5b8SMike Smith #include <sys/kernel.h>
41787fa5b8SMike Smith #include <sys/sysctl.h>
42787fa5b8SMike Smith #include <sys/timetc.h>
43787fa5b8SMike Smith 
44787fa5b8SMike Smith #include <machine/bus_pio.h>
45787fa5b8SMike Smith #include <machine/bus.h>
46787fa5b8SMike Smith #include <machine/resource.h>
47787fa5b8SMike Smith #include <sys/rman.h>
4815e32d5dSMike Smith 
4915e32d5dSMike Smith #include "acpi.h"
5015e32d5dSMike Smith 
51787fa5b8SMike Smith #include <acpica/acpivar.h>
52787fa5b8SMike Smith #include <pci/pcivar.h>
53787fa5b8SMike Smith 
54787fa5b8SMike Smith /*
55787fa5b8SMike Smith  * A timecounter based on the free-running ACPI timer.
56787fa5b8SMike Smith  *
57787fa5b8SMike Smith  * Based on the i386-only mp_clock.c by <phk@FreeBSD.ORG>.
58787fa5b8SMike Smith  */
5915e32d5dSMike Smith 
600ae55423SMike Smith /*
610ae55423SMike Smith  * Hooks for the ACPI CA debugging infrastructure
620ae55423SMike Smith  */
632a4ac806SMike Smith #define _COMPONENT	ACPI_SYSTEM
640ae55423SMike Smith MODULE_NAME("TIMER")
650ae55423SMike Smith 
66787fa5b8SMike Smith static device_t	acpi_timer_dev;
67787fa5b8SMike Smith struct resource	*acpi_timer_reg;
68787fa5b8SMike Smith #define TIMER_READ	bus_space_read_4(rman_get_bustag(acpi_timer_reg),	\
69787fa5b8SMike Smith 					 rman_get_bushandle(acpi_timer_reg),	\
70787fa5b8SMike Smith 					 0)
71787fa5b8SMike Smith static int	acpi_timer_flags;
72787fa5b8SMike Smith /*
73787fa5b8SMike Smith  * ] 20. ACPI Timer Errata
74787fa5b8SMike Smith  * ]
75787fa5b8SMike Smith  * ]   Problem: The power management timer may return improper result when
76787fa5b8SMike Smith  * ]   read. Although the timer value settles properly after incrementing,
77787fa5b8SMike Smith  * ]   while incrementing there is a 3nS window every 69.8nS where the
78787fa5b8SMike Smith  * ]   timer value is indeterminate (a 4.2% chance that the data will be
79787fa5b8SMike Smith  * ]   incorrect when read). As a result, the ACPI free running count up
80787fa5b8SMike Smith  * ]   timer specification is violated due to erroneous reads.  Implication:
81787fa5b8SMike Smith  * ]   System hangs due to the "inaccuracy" of the timer when used by
82787fa5b8SMike Smith  * ]   software for time critical events and delays.
83787fa5b8SMike Smith  * ]
84787fa5b8SMike Smith  * ] Workaround: Read the register twice and compare.
85787fa5b8SMike Smith  * ] Status: This will not be fixed in the PIIX4 or PIIX4E.
86787fa5b8SMike Smith  *
87787fa5b8SMike Smith  * The counter is in other words not latched to the PCI bus clock when
88787fa5b8SMike Smith  * read.  Notice the workaround isn't:  We need to read until we have
89787fa5b8SMike Smith  * three monotonic samples and then use the middle one, otherwise we are
90787fa5b8SMike Smith  * not protected against the fact that the bits can be wrong in two
91787fa5b8SMike Smith  * directions.  If we only cared about monosity two reads would be enough.
92787fa5b8SMike Smith  */
93787fa5b8SMike Smith #define TFLAG_NEED_PIIX_WAR	(1 << 0)
9415e32d5dSMike Smith 
95787fa5b8SMike Smith static u_int	acpi_timer_frequency = 14318182/4;
9615e32d5dSMike Smith 
9715e32d5dSMike Smith static void	acpi_timer_identify(driver_t *driver, device_t parent);
9815e32d5dSMike Smith static int	acpi_timer_probe(device_t dev);
9915e32d5dSMike Smith static int	acpi_timer_attach(device_t dev);
100787fa5b8SMike Smith static int	acpi_timer_pci_probe(device_t dev);
101787fa5b8SMike Smith static unsigned	acpi_timer_get_timecount(struct timecounter *tc);
102787fa5b8SMike Smith static int	acpi_timer_sysctl_freq(SYSCTL_HANDLER_ARGS);
103787fa5b8SMike Smith static void	acpi_timer_test(void);
10415e32d5dSMike Smith 
105787fa5b8SMike Smith /*
106787fa5b8SMike Smith  * Driver hung off ACPI.
107787fa5b8SMike Smith  */
10815e32d5dSMike Smith static device_method_t acpi_timer_methods[] = {
10915e32d5dSMike Smith     DEVMETHOD(device_identify,	acpi_timer_identify),
11015e32d5dSMike Smith     DEVMETHOD(device_probe,	acpi_timer_probe),
11115e32d5dSMike Smith     DEVMETHOD(device_attach,	acpi_timer_attach),
11215e32d5dSMike Smith 
11315e32d5dSMike Smith     {0, 0}
11415e32d5dSMike Smith };
11515e32d5dSMike Smith 
11615e32d5dSMike Smith static driver_t acpi_timer_driver = {
11715e32d5dSMike Smith     "acpi_timer",
11815e32d5dSMike Smith     acpi_timer_methods,
119787fa5b8SMike Smith     0,
12015e32d5dSMike Smith };
12115e32d5dSMike Smith 
12215e32d5dSMike Smith devclass_t acpi_timer_devclass;
12315e32d5dSMike Smith DRIVER_MODULE(acpi_timer, acpi, acpi_timer_driver, acpi_timer_devclass, 0, 0);
12415e32d5dSMike Smith 
125787fa5b8SMike Smith /*
126787fa5b8SMike Smith  * Chipset workaround driver hung off PCI.
127787fa5b8SMike Smith  */
128787fa5b8SMike Smith static device_method_t acpi_timer_pci_methods[] = {
129787fa5b8SMike Smith     DEVMETHOD(device_probe,	acpi_timer_pci_probe),
130787fa5b8SMike Smith     {0, 0}
131787fa5b8SMike Smith };
132787fa5b8SMike Smith 
133787fa5b8SMike Smith static driver_t acpi_timer_pci_driver = {
134787fa5b8SMike Smith     "acpi_timer_pci",
135787fa5b8SMike Smith     acpi_timer_pci_methods,
136787fa5b8SMike Smith     0,
137787fa5b8SMike Smith };
138787fa5b8SMike Smith 
139787fa5b8SMike Smith devclass_t acpi_timer_pci_devclass;
140787fa5b8SMike Smith DRIVER_MODULE(acpi_timer_pci, pci, acpi_timer_pci_driver, acpi_timer_pci_devclass, 0, 0);
141787fa5b8SMike Smith 
142787fa5b8SMike Smith /*
143787fa5b8SMike Smith  * Timecounter.
144787fa5b8SMike Smith  */
145787fa5b8SMike Smith static struct timecounter acpi_timer_timecounter = {
146787fa5b8SMike Smith     acpi_timer_get_timecount,
147787fa5b8SMike Smith     0,
148787fa5b8SMike Smith     0xffffff,
149787fa5b8SMike Smith     0,
150787fa5b8SMike Smith     "ACPI"
151787fa5b8SMike Smith };
152787fa5b8SMike Smith 
153787fa5b8SMike Smith SYSCTL_OPAQUE(_debug, OID_AUTO, acpi_timecounter, CTLFLAG_RD,
154787fa5b8SMike Smith 	      &acpi_timer_timecounter, sizeof(acpi_timer_timecounter), "S,timecounter", "");
155787fa5b8SMike Smith 
156787fa5b8SMike Smith /*
157787fa5b8SMike Smith  * Locate the ACPI timer using the FADT, set up and allocate the I/O resources
158787fa5b8SMike Smith  * we will be using.
159787fa5b8SMike Smith  */
16015e32d5dSMike Smith static void
16115e32d5dSMike Smith acpi_timer_identify(driver_t *driver, device_t parent)
16215e32d5dSMike Smith {
16315e32d5dSMike Smith     device_t	dev;
16415e32d5dSMike Smith     char	desc[40];
165787fa5b8SMike Smith     int		rid;
16615e32d5dSMike Smith 
1672a4ac806SMike Smith     FUNCTION_TRACE(__func__);
1680ae55423SMike Smith 
1690ae55423SMike Smith     if (acpi_disabled("timer"))
1700ae55423SMike Smith 	return_VOID;
1710ae55423SMike Smith 
17291467fc6SMike Smith     if (AcpiGbl_FADT == NULL)
1730ae55423SMike Smith 	return_VOID;
17415e32d5dSMike Smith 
17515e32d5dSMike Smith     if ((dev = BUS_ADD_CHILD(parent, 0, "acpi_timer", 0)) == NULL) {
17615e32d5dSMike Smith 	device_printf(parent, "could not add acpi_timer0\n");
1770ae55423SMike Smith 	return_VOID;
17815e32d5dSMike Smith     }
179787fa5b8SMike Smith     acpi_timer_dev = dev;
180787fa5b8SMike Smith     rid = 0;
181787fa5b8SMike Smith     bus_set_resource(dev, SYS_RES_IOPORT, rid, AcpiGbl_FADT->V1_PmTmrBlk, sizeof(u_int32_t));
182787fa5b8SMike Smith     if ((acpi_timer_reg = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE)) == NULL) {
183787fa5b8SMike Smith 	device_printf(dev, "couldn't allocate I/O resource (port 0x%x)\n", AcpiGbl_FADT->V1_PmTmrBlk);
1840ae55423SMike Smith 	return_VOID;
18515e32d5dSMike Smith     }
186787fa5b8SMike Smith     if (getenv("debug.acpi.timer_test") != NULL)
187787fa5b8SMike Smith 	acpi_timer_test();
188787fa5b8SMike Smith 
189787fa5b8SMike Smith     acpi_timer_timecounter.tc_frequency = acpi_timer_frequency;
190787fa5b8SMike Smith     tc_init(&acpi_timer_timecounter);
19115e32d5dSMike Smith 
19291467fc6SMike Smith     sprintf(desc, "%d-bit timer at 3.579545MHz", AcpiGbl_FADT->TmrValExt ? 32 : 24);
19315e32d5dSMike Smith     device_set_desc_copy(dev, desc);
1940ae55423SMike Smith 
1950ae55423SMike Smith     return_VOID;
19615e32d5dSMike Smith }
19715e32d5dSMike Smith 
19815e32d5dSMike Smith static int
19915e32d5dSMike Smith acpi_timer_probe(device_t dev)
20015e32d5dSMike Smith {
201787fa5b8SMike Smith     if (dev == acpi_timer_dev)
20215e32d5dSMike Smith 	return(0);
20315e32d5dSMike Smith     return(ENXIO);
20415e32d5dSMike Smith }
20515e32d5dSMike Smith 
20615e32d5dSMike Smith static int
20715e32d5dSMike Smith acpi_timer_attach(device_t dev)
20815e32d5dSMike Smith {
209787fa5b8SMike Smith     return(0);
21015e32d5dSMike Smith }
211787fa5b8SMike Smith 
212787fa5b8SMike Smith /*
213787fa5b8SMike Smith  * Look at PCI devices as they go past, and if we detect a PIIX4, set
214787fa5b8SMike Smith  * the PIIX_WAR flag.
215787fa5b8SMike Smith  *
216787fa5b8SMike Smith  * XXX do we know that other timecounters work?  Interesting question.
217787fa5b8SMike Smith  */
218787fa5b8SMike Smith static int
219787fa5b8SMike Smith acpi_timer_pci_probe(device_t dev)
220787fa5b8SMike Smith {
221787fa5b8SMike Smith     if ((pci_get_vendor(dev) == 0x8086) &&
222787fa5b8SMike Smith 	(pci_get_device(dev) == 0x7113)) {
223787fa5b8SMike Smith 	acpi_timer_flags |= TFLAG_NEED_PIIX_WAR;
224787fa5b8SMike Smith 	device_printf(acpi_timer_dev, "enabling PIIX4 timer workaround\n");
225787fa5b8SMike Smith     }
226787fa5b8SMike Smith 
227787fa5b8SMike Smith     return(ENXIO);		/* we never match anything */
228787fa5b8SMike Smith }
229787fa5b8SMike Smith 
230787fa5b8SMike Smith /*
231787fa5b8SMike Smith  * Fetch current time value from hardware.
232787fa5b8SMike Smith  *
233787fa5b8SMike Smith  * XXX This is currently written to be "correct", not
234787fa5b8SMike Smith  *     "fast".  Optimisation is strongly indicated.
235787fa5b8SMike Smith  */
236787fa5b8SMike Smith static unsigned
237787fa5b8SMike Smith acpi_timer_get_timecount(struct timecounter *tc)
238787fa5b8SMike Smith {
239787fa5b8SMike Smith     unsigned u1, u2, u3;
240787fa5b8SMike Smith 
241787fa5b8SMike Smith     if (acpi_timer_flags & TFLAG_NEED_PIIX_WAR) {
242787fa5b8SMike Smith 	u2 = TIMER_READ;
243787fa5b8SMike Smith 	u3 = TIMER_READ;
244787fa5b8SMike Smith 	do {
245787fa5b8SMike Smith 	    u1 = u2;
246787fa5b8SMike Smith 	    u2 = u3;
247787fa5b8SMike Smith 	    u3 = TIMER_READ;
248787fa5b8SMike Smith 	} while (u1 > u2 || u2 > u3);
249787fa5b8SMike Smith 	return (u2);
250787fa5b8SMike Smith     } else {
251787fa5b8SMike Smith 	return(TIMER_READ);
252787fa5b8SMike Smith     }
253787fa5b8SMike Smith }
254787fa5b8SMike Smith 
255787fa5b8SMike Smith /*
256787fa5b8SMike Smith  * Timecounter freqency adjustment interface.
257787fa5b8SMike Smith  */
258787fa5b8SMike Smith static int
259787fa5b8SMike Smith acpi_timer_sysctl_freq(SYSCTL_HANDLER_ARGS)
260787fa5b8SMike Smith {
261787fa5b8SMike Smith     int error;
262787fa5b8SMike Smith     u_int freq;
263787fa5b8SMike Smith 
264787fa5b8SMike Smith     if (acpi_timer_timecounter.tc_frequency == 0)
265787fa5b8SMike Smith 	return (EOPNOTSUPP);
266787fa5b8SMike Smith     freq = acpi_timer_frequency;
267787fa5b8SMike Smith     error = sysctl_handle_int(oidp, &freq, sizeof(freq), req);
268787fa5b8SMike Smith     if (error == 0 && req->newptr != NULL) {
269787fa5b8SMike Smith 	acpi_timer_frequency = freq;
270787fa5b8SMike Smith 	acpi_timer_timecounter.tc_frequency = acpi_timer_frequency;
271787fa5b8SMike Smith 	tc_update(&acpi_timer_timecounter);
272787fa5b8SMike Smith     }
273787fa5b8SMike Smith     return (error);
274787fa5b8SMike Smith }
275787fa5b8SMike Smith 
276787fa5b8SMike Smith SYSCTL_PROC(_machdep, OID_AUTO, acpi_timer_freq, CTLTYPE_INT | CTLFLAG_RW,
277787fa5b8SMike Smith 	    0, sizeof(u_int), acpi_timer_sysctl_freq, "I", "");
278787fa5b8SMike Smith 
279787fa5b8SMike Smith /*
280787fa5b8SMike Smith  * Test harness for verifying ACPI timer behaviour.
281787fa5b8SMike Smith  * Boot with debug.acpi.timer_test set to invoke this.
282787fa5b8SMike Smith  */
283787fa5b8SMike Smith static void
284787fa5b8SMike Smith acpi_timer_test(void)
285787fa5b8SMike Smith {
286787fa5b8SMike Smith     u_int32_t	u1, u2, u3;
287787fa5b8SMike Smith 
288787fa5b8SMike Smith     u1 = TIMER_READ;
289787fa5b8SMike Smith     u2 = TIMER_READ;
290787fa5b8SMike Smith     u3 = TIMER_READ;
291787fa5b8SMike Smith 
292787fa5b8SMike Smith     device_printf(acpi_timer_dev, "timer test in progress, reboot to quit.\n");
293787fa5b8SMike Smith     for (;;) {
294787fa5b8SMike Smith 	/*
295787fa5b8SMike Smith 	 * The failure case is where u3 > u1, but u2 does not fall between the two,
296787fa5b8SMike Smith 	 * ie. it contains garbage.
297787fa5b8SMike Smith 	 */
298787fa5b8SMike Smith 	if (u3 > u1) {
299787fa5b8SMike Smith 	    if ((u2 < u1) || (u2 > u3))
300787fa5b8SMike Smith 		device_printf(acpi_timer_dev, "timer is not monotonic: 0x%08x,0x%08x,0x%08x\n",
301787fa5b8SMike Smith 			      u1, u2, u3);
302787fa5b8SMike Smith 	}
303787fa5b8SMike Smith 	u1 = u2;
304787fa5b8SMike Smith 	u2 = u3;
305787fa5b8SMike Smith 	u3 = TIMER_READ;
306787fa5b8SMike Smith     }
307787fa5b8SMike Smith }
308787fa5b8SMike Smith 
309