acpi_timer.c revision 1.23 1 /* $NetBSD: acpi_timer.c,v 1.23 2018/10/15 11:33:09 jmcneill Exp $ */
2
3 /*-
4 * Copyright (c) 2006 Matthias Drochner <drochner (at) NetBSD.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: acpi_timer.c,v 1.23 2018/10/15 11:33:09 jmcneill Exp $");
31
32 #include <sys/types.h>
33 #include <sys/systm.h>
34 #include <sys/time.h>
35 #include <sys/timetc.h>
36
37 #include <dev/acpi/acpivar.h>
38 #include <dev/acpi/acpi_timer.h>
39
40 #include <machine/acpi_machdep.h>
41
42 static int acpitimer_test(void);
43
44 static struct timecounter acpi_timecounter = {
45 acpitimer_read_safe,
46 0,
47 0x00ffffff,
48 ACPI_PM_TIMER_FREQUENCY,
49 "ACPI-Safe",
50 900,
51 NULL,
52 NULL,
53 };
54
55 static bool
56 acpitimer_supported(void)
57 {
58 return AcpiGbl_FADT.PmTimerLength != 0;
59 }
60
61 int
62 acpitimer_init(struct acpi_softc *sc)
63 {
64 ACPI_STATUS rv;
65 uint32_t bits;
66 int i, j;
67
68 if (!acpitimer_supported())
69 return -1;
70
71 rv = AcpiGetTimerResolution(&bits);
72
73 if (ACPI_FAILURE(rv))
74 return -1;
75
76 if (bits == 32)
77 acpi_timecounter.tc_counter_mask = 0xffffffff;
78
79 for (i = j = 0; i < 10; i++)
80 j += acpitimer_test();
81
82 if (j >= 10) {
83 acpi_timecounter.tc_name = "ACPI-Fast";
84 acpi_timecounter.tc_get_timecount = acpitimer_read_fast;
85 acpi_timecounter.tc_quality = 1000;
86 }
87
88 tc_init(&acpi_timecounter);
89
90 aprint_debug_dev(sc->sc_dev, "%s %d-bit timer\n",
91 acpi_timecounter.tc_name, bits);
92
93 return 0;
94 }
95
96 int
97 acpitimer_detach(void)
98 {
99
100 if (!acpitimer_supported())
101 return -1;
102
103 return tc_detach(&acpi_timecounter);
104 }
105
106 u_int
107 acpitimer_read_fast(struct timecounter *tc)
108 {
109 uint32_t t;
110
111 (void)AcpiGetTimer(&t);
112
113 return t;
114 }
115
116 /*
117 * Some chipsets (PIIX4 variants) do not latch correctly;
118 * there is a chance that a transition is hit.
119 */
120 u_int
121 acpitimer_read_safe(struct timecounter *tc)
122 {
123 uint32_t t1, t2, t3;
124
125 (void)AcpiGetTimer(&t2);
126 (void)AcpiGetTimer(&t3);
127
128 do {
129 t1 = t2;
130 t2 = t3;
131
132 (void)AcpiGetTimer(&t3);
133
134 } while ((t1 > t2) || (t2 > t3));
135
136 return t2;
137 }
138
139 uint32_t
140 acpitimer_delta(uint32_t end, uint32_t start)
141 {
142 const u_int mask = acpi_timecounter.tc_counter_mask;
143 uint32_t delta;
144
145 if (end >= start)
146 delta = end - start;
147 else
148 delta = ((mask - start) + end + 1) & mask;
149
150 return delta;
151 }
152
153 #define N 2000
154
155 static int
156 acpitimer_test(void)
157 {
158 uint32_t last, this, delta;
159 int minl, maxl, n;
160
161 minl = 10000000;
162 maxl = 0;
163
164 acpi_md_OsDisableInterrupt();
165
166 (void)AcpiGetTimer(&last);
167
168 for (n = 0; n < N; n++) {
169
170 (void)AcpiGetTimer(&this);
171
172 delta = acpitimer_delta(this, last);
173
174 if (delta > maxl)
175 maxl = delta;
176 else if (delta < minl)
177 minl = delta;
178
179 last = this;
180 }
181
182 acpi_md_OsEnableInterrupt();
183
184 if (maxl - minl > 2 )
185 n = 0;
186 else if (minl < 0 || maxl == 0)
187 n = 0;
188 else
189 n = 1;
190
191 return n;
192 }
193