acpi_timer.c revision 128506
167761Smsmith/*-
280070Smsmith * Copyright (c) 2000, 2001 Michael Smith
367761Smsmith * Copyright (c) 2000 BSDi
467761Smsmith * All rights reserved.
567761Smsmith *
667761Smsmith * Redistribution and use in source and binary forms, with or without
767761Smsmith * modification, are permitted provided that the following conditions
867761Smsmith * are met:
967761Smsmith * 1. Redistributions of source code must retain the above copyright
1067761Smsmith *    notice, this list of conditions and the following disclaimer.
1167761Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1267761Smsmith *    notice, this list of conditions and the following disclaimer in the
1367761Smsmith *    documentation and/or other materials provided with the distribution.
1467761Smsmith *
1567761Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1667761Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1767761Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1867761Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1967761Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2067761Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2167761Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2267761Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2367761Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2467761Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2567761Smsmith * SUCH DAMAGE.
2667761Smsmith *
2767761Smsmith *	$FreeBSD: head/sys/dev/acpica/acpi_timer.c 128506 2004-04-21 00:48:16Z njl $
2867761Smsmith */
2967761Smsmith#include "opt_acpi.h"
3067761Smsmith#include <sys/param.h>
3180070Smsmith#include <sys/bus.h>
3267761Smsmith#include <sys/kernel.h>
3380070Smsmith#include <sys/sysctl.h>
3480070Smsmith#include <sys/timetc.h>
3567761Smsmith
3680070Smsmith#include <machine/bus.h>
3780070Smsmith#include <machine/resource.h>
3880070Smsmith#include <sys/rman.h>
3980070Smsmith
4067761Smsmith#include "acpi.h"
41104726Sjhb#include <dev/acpica/acpivar.h>
42119281Simp#include <dev/pci/pcivar.h>
4367761Smsmith
4469744Smsmith/*
4580070Smsmith * A timecounter based on the free-running ACPI timer.
4680070Smsmith *
4780070Smsmith * Based on the i386-only mp_clock.c by <phk@FreeBSD.ORG>.
4880070Smsmith */
4980070Smsmith
50119529Snjl/* Hooks for the ACPI CA debugging infrastructure */
51126517Snjl#define _COMPONENT	ACPI_TIMER
5291128SmsmithACPI_MODULE_NAME("TIMER")
5369744Smsmith
5480070Smsmithstatic device_t	acpi_timer_dev;
5580070Smsmithstruct resource	*acpi_timer_reg;
5667761Smsmith
57119529Snjlstatic u_int	acpi_timer_frequency = 14318182 / 4;
5867761Smsmith
5967761Smsmithstatic void	acpi_timer_identify(driver_t *driver, device_t parent);
6067761Smsmithstatic int	acpi_timer_probe(device_t dev);
6167761Smsmithstatic int	acpi_timer_attach(device_t dev);
62128506Snjlstatic u_int	acpi_timer_get_timecount(struct timecounter *tc);
63128506Snjlstatic u_int	acpi_timer_get_timecount_safe(struct timecounter *tc);
6480070Smsmithstatic int	acpi_timer_sysctl_freq(SYSCTL_HANDLER_ARGS);
6580070Smsmithstatic void	acpi_timer_test(void);
6667761Smsmith
67128506Snjlstatic u_int	read_counter(void);
68119529Snjlstatic int	test_counter(void);
69114277Smarcel
7067761Smsmithstatic device_method_t acpi_timer_methods[] = {
7167761Smsmith    DEVMETHOD(device_identify,	acpi_timer_identify),
7267761Smsmith    DEVMETHOD(device_probe,	acpi_timer_probe),
7367761Smsmith    DEVMETHOD(device_attach,	acpi_timer_attach),
7467761Smsmith
7567761Smsmith    {0, 0}
7667761Smsmith};
7767761Smsmith
7867761Smsmithstatic driver_t acpi_timer_driver = {
7967761Smsmith    "acpi_timer",
8067761Smsmith    acpi_timer_methods,
8180070Smsmith    0,
8267761Smsmith};
8367761Smsmith
8489054Smsmithstatic devclass_t acpi_timer_devclass;
8567761SmsmithDRIVER_MODULE(acpi_timer, acpi, acpi_timer_driver, acpi_timer_devclass, 0, 0);
86128071SnjlMODULE_DEPEND(acpi_timer, acpi, 1, 1, 1);
8767761Smsmith
8880070Smsmithstatic struct timecounter acpi_timer_timecounter = {
89128506Snjl	.tc_get_timecount =	acpi_timer_get_timecount_safe,
90128506Snjl	.tc_poll_pps =		0,
91128506Snjl	.tc_counter_mask =	0,
92128506Snjl	.tc_frequency =		0,
93128506Snjl	.tc_name =		"ACPI",
94128506Snjl	.tc_quality =		1000
9580070Smsmith};
9680070Smsmith
97128506Snjlstatic u_int
98114277Smarcelread_counter()
99114277Smarcel{
100119529Snjl    bus_space_handle_t bsh;
101119529Snjl    bus_space_tag_t bst;
102128506Snjl    uint32_t tv;
103114277Smarcel
104119529Snjl    bsh = rman_get_bushandle(acpi_timer_reg);
105119529Snjl    bst = rman_get_bustag(acpi_timer_reg);
106119529Snjl    tv = bus_space_read_4(bst, bsh, 0);
107119529Snjl    bus_space_barrier(bst, bsh, 0, 4, BUS_SPACE_BARRIER_READ);
108119529Snjl
109119529Snjl    return (tv);
110114277Smarcel}
111114277Smarcel
11291237Sphk#define N 2000
11391237Sphkstatic int
11491237Sphktest_counter()
11591237Sphk{
116128506Snjl    uint32_t	last, this;
117119529Snjl    int		min, max, n, delta;
11891237Sphk
119119529Snjl    min = 10000000;
120119529Snjl    max = 0;
121119529Snjl    last = read_counter();
122119529Snjl    for (n = 0; n < N; n++) {
123119529Snjl	this = read_counter();
124128506Snjl	delta = acpi_TimerDelta(this, last);
125119529Snjl	if (delta > max)
126119529Snjl	    max = delta;
127119529Snjl	else if (delta < min)
128119529Snjl	    min = delta;
129119529Snjl	last = this;
130119529Snjl    }
131119529Snjl    if (max - min > 2)
132119529Snjl	n = 0;
133119529Snjl    else if (min < 0 || max == 0)
134119529Snjl	n = 0;
135119529Snjl    else
136119529Snjl	n = 1;
137119529Snjl    if (bootverbose) {
138119529Snjl	printf("ACPI timer looks %s min = %d, max = %d, width = %d\n",
139119529Snjl		n ? "GOOD" : "BAD ",
140119529Snjl		min, max, max - min);
141119529Snjl    }
142119529Snjl
143119529Snjl    return (n);
14491237Sphk}
145119529Snjl#undef N
14691237Sphk
14780070Smsmith/*
14880070Smsmith * Locate the ACPI timer using the FADT, set up and allocate the I/O resources
14980070Smsmith * we will be using.
15080070Smsmith */
15167761Smsmithstatic void
15267761Smsmithacpi_timer_identify(driver_t *driver, device_t parent)
15367761Smsmith{
15480070Smsmith    device_t	dev;
15580070Smsmith    char	desc[40];
156114277Smarcel    u_long	rlen, rstart;
157114277Smarcel    int		i, j, rid, rtype;
15867761Smsmith
15996926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
16069744Smsmith
161119529Snjl    if (acpi_disabled("timer") || AcpiGbl_FADT == NULL)
16269744Smsmith	return_VOID;
163128506Snjl
16467761Smsmith    if ((dev = BUS_ADD_CHILD(parent, 0, "acpi_timer", 0)) == NULL) {
16567761Smsmith	device_printf(parent, "could not add acpi_timer0\n");
16669744Smsmith	return_VOID;
16767761Smsmith    }
16880070Smsmith    acpi_timer_dev = dev;
169114277Smarcel
17080070Smsmith    rid = 0;
171114277Smarcel    rlen = AcpiGbl_FADT->PmTmLen;
172114277Smarcel    rtype = (AcpiGbl_FADT->XPmTmrBlk.AddressSpaceId)
173114277Smarcel      ? SYS_RES_IOPORT : SYS_RES_MEMORY;
174114277Smarcel    rstart = AcpiGbl_FADT->XPmTmrBlk.Address;
175114277Smarcel    bus_set_resource(dev, rtype, rid, rstart, rlen);
176127135Snjl    acpi_timer_reg = bus_alloc_resource_any(dev, rtype, &rid, RF_ACTIVE);
177114277Smarcel    if (acpi_timer_reg == NULL) {
178114277Smarcel	device_printf(dev, "couldn't allocate I/O resource (%s 0x%lx)\n",
179119529Snjl		      rtype == SYS_RES_IOPORT ? "port" : "mem", rstart);
18069744Smsmith	return_VOID;
18167761Smsmith    }
182128506Snjl    if (AcpiGbl_FADT->TmrValExt != 0)
183128506Snjl	acpi_timer_timecounter.tc_counter_mask = 0xffffffff;
184128506Snjl    else
185128506Snjl	acpi_timer_timecounter.tc_counter_mask = 0x00ffffff;
186128506Snjl    acpi_timer_timecounter.tc_frequency = acpi_timer_frequency;
18794936Smux    if (testenv("debug.acpi.timer_test"))
18880070Smsmith	acpi_timer_test();
18967761Smsmith
19091237Sphk    j = 0;
191128506Snjl    for (i = 0; i < 10; i++)
19291237Sphk	j += test_counter();
19391237Sphk    if (j == 10) {
19491237Sphk	acpi_timer_timecounter.tc_name = "ACPI-fast";
19591237Sphk	acpi_timer_timecounter.tc_get_timecount = acpi_timer_get_timecount;
19691237Sphk    } else {
19791237Sphk	acpi_timer_timecounter.tc_name = "ACPI-safe";
19891237Sphk	acpi_timer_timecounter.tc_get_timecount = acpi_timer_get_timecount_safe;
19991237Sphk    }
20080070Smsmith    tc_init(&acpi_timer_timecounter);
20180070Smsmith
202119529Snjl    sprintf(desc, "%d-bit timer at 3.579545MHz",
203119529Snjl	    AcpiGbl_FADT->TmrValExt ? 32 : 24);
20467761Smsmith    device_set_desc_copy(dev, desc);
20569744Smsmith
20669744Smsmith    return_VOID;
20767761Smsmith}
20867761Smsmith
20967761Smsmithstatic int
21067761Smsmithacpi_timer_probe(device_t dev)
21167761Smsmith{
21280070Smsmith    if (dev == acpi_timer_dev)
213119529Snjl	return (0);
214119529Snjl
215119529Snjl    return (ENXIO);
21667761Smsmith}
21767761Smsmith
21867761Smsmithstatic int
21967761Smsmithacpi_timer_attach(device_t dev)
22067761Smsmith{
221119529Snjl    return (0);
22280070Smsmith}
22367761Smsmith
22480070Smsmith/*
22581096Smsmith * Fetch current time value from reliable hardware.
22680070Smsmith */
227128506Snjlstatic u_int
22880070Smsmithacpi_timer_get_timecount(struct timecounter *tc)
22980070Smsmith{
230114277Smarcel    return (read_counter());
23167761Smsmith}
23280070Smsmith
23380070Smsmith/*
23481096Smsmith * Fetch current time value from hardware that may not correctly
23581096Smsmith * latch the counter.
23681096Smsmith */
237128506Snjlstatic u_int
23881096Smsmithacpi_timer_get_timecount_safe(struct timecounter *tc)
23981096Smsmith{
240128506Snjl    u_int u1, u2, u3;
24181096Smsmith
242114277Smarcel    u2 = read_counter();
243114277Smarcel    u3 = read_counter();
24481096Smsmith    do {
24581096Smsmith	u1 = u2;
24681096Smsmith	u2 = u3;
247114277Smarcel	u3 = read_counter();
248119529Snjl    } while (u1 > u2 || u2 > u3 || u3 - u1 > 15);
249119529Snjl
25081096Smsmith    return (u2);
25181096Smsmith}
25281096Smsmith
25381096Smsmith/*
25480070Smsmith * Timecounter freqency adjustment interface.
25580070Smsmith */
25680070Smsmithstatic int
25780070Smsmithacpi_timer_sysctl_freq(SYSCTL_HANDLER_ARGS)
25880070Smsmith{
25980070Smsmith    int error;
26080070Smsmith    u_int freq;
26180070Smsmith
26280070Smsmith    if (acpi_timer_timecounter.tc_frequency == 0)
26380070Smsmith	return (EOPNOTSUPP);
26480070Smsmith    freq = acpi_timer_frequency;
26580070Smsmith    error = sysctl_handle_int(oidp, &freq, sizeof(freq), req);
26680070Smsmith    if (error == 0 && req->newptr != NULL) {
26780070Smsmith	acpi_timer_frequency = freq;
26880070Smsmith	acpi_timer_timecounter.tc_frequency = acpi_timer_frequency;
26980070Smsmith    }
270119529Snjl
27180070Smsmith    return (error);
27280070Smsmith}
27380070Smsmith
27480070SmsmithSYSCTL_PROC(_machdep, OID_AUTO, acpi_timer_freq, CTLTYPE_INT | CTLFLAG_RW,
27580070Smsmith	    0, sizeof(u_int), acpi_timer_sysctl_freq, "I", "");
27680070Smsmith
27780070Smsmith/*
27880070Smsmith * Test harness for verifying ACPI timer behaviour.
27980070Smsmith * Boot with debug.acpi.timer_test set to invoke this.
28080070Smsmith */
28180070Smsmithstatic void
28280070Smsmithacpi_timer_test(void)
28380070Smsmith{
284128506Snjl    uint32_t u1, u2, u3;
285128506Snjl
286114277Smarcel    u1 = read_counter();
287114277Smarcel    u2 = read_counter();
288114277Smarcel    u3 = read_counter();
289128506Snjl
29080070Smsmith    device_printf(acpi_timer_dev, "timer test in progress, reboot to quit.\n");
29180070Smsmith    for (;;) {
29280070Smsmith	/*
293119529Snjl	 * The failure case is where u3 > u1, but u2 does not fall between
294119529Snjl	 * the two, ie. it contains garbage.
29580070Smsmith	 */
29680070Smsmith	if (u3 > u1) {
297119529Snjl	    if (u2 < u1 || u2 > u3)
298119529Snjl		device_printf(acpi_timer_dev,
299119529Snjl			      "timer is not monotonic: 0x%08x,0x%08x,0x%08x\n",
30080070Smsmith			      u1, u2, u3);
30180070Smsmith	}
30280070Smsmith	u1 = u2;
30380070Smsmith	u2 = u3;
304114277Smarcel	u3 = read_counter();
30580070Smsmith    }
30680070Smsmith}
307