Merge trunk HEAD (46152)
[reactos.git] / drivers / bus / acpi / hardware / hwtimer.c
1
2 /******************************************************************************
3 *
4 * Name: hwtimer.c - ACPI Power Management Timer Interface
5 * $Revision: 1.1 $
6 *
7 *****************************************************************************/
8
9 /*
10 * Copyright (C) 2000, 2001 R. Byron Moore
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 */
26
27 #include <acpi.h>
28
29 #define _COMPONENT ACPI_HARDWARE
30 MODULE_NAME ("hwtimer")
31
32
33 /******************************************************************************
34 *
35 * FUNCTION: Acpi_get_timer_resolution
36 *
37 * PARAMETERS: none
38 *
39 * RETURN: Number of bits of resolution in the PM Timer (24 or 32).
40 *
41 * DESCRIPTION: Obtains resolution of the ACPI PM Timer.
42 *
43 ******************************************************************************/
44
45 ACPI_STATUS
46 acpi_get_timer_resolution (
47 u32 *resolution)
48 {
49 if (!resolution) {
50 return (AE_BAD_PARAMETER);
51 }
52
53 if (0 == acpi_gbl_FADT->tmr_val_ext) {
54 *resolution = 24;
55 }
56 else {
57 *resolution = 32;
58 }
59
60 return (AE_OK);
61 }
62
63
64 /******************************************************************************
65 *
66 * FUNCTION: Acpi_get_timer
67 *
68 * PARAMETERS: none
69 *
70 * RETURN: Current value of the ACPI PM Timer (in ticks).
71 *
72 * DESCRIPTION: Obtains current value of ACPI PM Timer.
73 *
74 ******************************************************************************/
75
76 ACPI_STATUS
77 acpi_get_timer (
78 u32 *ticks)
79 {
80 if (!ticks) {
81 return (AE_BAD_PARAMETER);
82 }
83
84 *ticks = acpi_os_in32 ((ACPI_IO_ADDRESS) ACPI_GET_ADDRESS (acpi_gbl_FADT->Xpm_tmr_blk.address));
85
86 return (AE_OK);
87 }
88
89
90 /******************************************************************************
91 *
92 * FUNCTION: Acpi_get_timer_duration
93 *
94 * PARAMETERS: Start_ticks
95 * End_ticks
96 * Time_elapsed
97 *
98 * RETURN: Time_elapsed
99 *
100 * DESCRIPTION: Computes the time elapsed (in microseconds) between two
101 * PM Timer time stamps, taking into account the possibility of
102 * rollovers, the timer resolution, and timer frequency.
103 *
104 * The PM Timer's clock ticks at roughly 3.6 times per
105 * _microsecond_, and its clock continues through Cx state
106 * transitions (unlike many CPU timestamp counters) -- making it
107 * a versatile and accurate timer.
108 *
109 * Note that this function accomodates only a single timer
110 * rollover. Thus for 24-bit timers, this function should only
111 * be used for calculating durations less than ~4.6 seconds
112 * (~20 hours for 32-bit timers).
113 *
114 ******************************************************************************/
115
116 ACPI_STATUS
117 acpi_get_timer_duration (
118 u32 start_ticks,
119 u32 end_ticks,
120 u32 *time_elapsed)
121 {
122 u32 delta_ticks = 0;
123 u32 seconds = 0;
124 u32 milliseconds = 0;
125 u32 microseconds = 0;
126 u32 remainder = 0;
127
128 if (!time_elapsed) {
129 return (AE_BAD_PARAMETER);
130 }
131
132 /*
133 * Compute Tick Delta:
134 * -------------------
135 * Handle (max one) timer rollovers on 24- versus 32-bit timers.
136 */
137 if (start_ticks < end_ticks) {
138 delta_ticks = end_ticks - start_ticks;
139 }
140 else if (start_ticks > end_ticks) {
141 /* 24-bit Timer */
142 if (0 == acpi_gbl_FADT->tmr_val_ext) {
143 delta_ticks = (((0x00FFFFFF - start_ticks) + end_ticks) & 0x00FFFFFF);
144 }
145 /* 32-bit Timer */
146 else {
147 delta_ticks = (0xFFFFFFFF - start_ticks) + end_ticks;
148 }
149 }
150 else {
151 *time_elapsed = 0;
152 return (AE_OK);
153 }
154
155 /*
156 * Compute Duration:
157 * -----------------
158 * Since certain compilers (gcc/Linux, argh!) don't support 64-bit
159 * divides in kernel-space we have to do some trickery to preserve
160 * accuracy while using 32-bit math.
161 *
162 * TODO: Change to use 64-bit math when supported.
163 *
164 * The process is as follows:
165 * 1. Compute the number of seconds by dividing Delta Ticks by
166 * the timer frequency.
167 * 2. Compute the number of milliseconds in the remainder from step #1
168 * by multiplying by 1000 and then dividing by the timer frequency.
169 * 3. Compute the number of microseconds in the remainder from step #2
170 * by multiplying by 1000 and then dividing by the timer frequency.
171 * 4. Add the results from steps 1, 2, and 3 to get the total duration.
172 *
173 * Example: The time elapsed for Delta_ticks = 0xFFFFFFFF should be
174 * 1199864031 microseconds. This is computed as follows:
175 * Step #1: Seconds = 1199; Remainder = 3092840
176 * Step #2: Milliseconds = 864; Remainder = 113120
177 * Step #3: Microseconds = 31; Remainder = <don't care!>
178 */
179
180 /* Step #1 */
181 seconds = delta_ticks / PM_TIMER_FREQUENCY;
182 remainder = delta_ticks % PM_TIMER_FREQUENCY;
183
184 /* Step #2 */
185 milliseconds = (remainder * 1000) / PM_TIMER_FREQUENCY;
186 remainder = (remainder * 1000) % PM_TIMER_FREQUENCY;
187
188 /* Step #3 */
189 microseconds = (remainder * 1000) / PM_TIMER_FREQUENCY;
190
191 /* Step #4 */
192 *time_elapsed = seconds * 1000000;
193 *time_elapsed += milliseconds * 1000;
194 *time_elapsed += microseconds;
195
196 return (AE_OK);
197 }
198
199