2 * QEMU 8253/8254 interval timer emulation
4 * Copyright (c) 2003-2004 Fabrice Bellard
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
41 #include <netinet/in.h>
46 #define RW_STATE_LSB 0
47 #define RW_STATE_MSB 1
48 #define RW_STATE_WORD0 2
49 #define RW_STATE_WORD1 3
50 #define RW_STATE_LATCHED_WORD0 4
51 #define RW_STATE_LATCHED_WORD1 5
53 PITChannelState pit_channels[3];
55 static int pit_get_count(PITChannelState *s)
60 d = muldiv64(cpu_get_ticks() - s->count_load_time, PIT_FREQ, ticks_per_sec);
66 counter = (s->count - d) & 0xffff;
69 /* XXX: may be incorrect for odd counts */
70 counter = s->count - ((2 * d) % s->count);
73 counter = s->count - (d % s->count);
79 /* get pit output bit */
80 int pit_get_out(PITChannelState *s)
85 d = muldiv64(cpu_get_ticks() - s->count_load_time, PIT_FREQ, ticks_per_sec);
89 out = (d >= s->count);
95 if ((d % s->count) == 0 && d != 0)
101 out = (d % s->count) < ((s->count + 1) >> 1);
105 out = (d == s->count);
111 /* get the number of 0 to 1 transitions we had since we call this
113 /* XXX: maybe better to use ticks precision to avoid getting edges
114 twice if checks are done at very small intervals */
115 int pit_get_out_edges(PITChannelState *s)
121 ticks = cpu_get_ticks();
122 d1 = muldiv64(s->count_last_edge_check_time - s->count_load_time,
123 PIT_FREQ, ticks_per_sec);
124 d2 = muldiv64(ticks - s->count_load_time,
125 PIT_FREQ, ticks_per_sec);
126 s->count_last_edge_check_time = ticks;
130 if (d1 < s->count && d2 >= s->count)
144 v = s->count - ((s->count + 1) >> 1);
145 d1 = (d1 + v) / s->count;
146 d2 = (d2 + v) / s->count;
151 if (d1 < s->count && d2 >= s->count)
160 /* val must be 0 or 1 */
161 void pit_set_gate(PITChannelState *s, int val)
167 /* XXX: just disable/enable counting */
172 /* restart counting on rising edge */
173 s->count_load_time = cpu_get_ticks();
174 s->count_last_edge_check_time = s->count_load_time;
180 /* restart counting on rising edge */
181 s->count_load_time = cpu_get_ticks();
182 s->count_last_edge_check_time = s->count_load_time;
184 /* XXX: disable/enable counting */
190 static inline void pit_load_count(PITChannelState *s, int val)
194 s->count_load_time = cpu_get_ticks();
195 s->count_last_edge_check_time = s->count_load_time;
197 if (s == &pit_channels[0] && val <= pit_min_timer_count) {
199 "\nWARNING: qemu: on your system, accurate timer emulation is impossible if its frequency is more than %d Hz. If using a 2.6 guest Linux kernel, you must patch asm/param.h to change HZ from 1000 to 100.\n\n",
200 PIT_FREQ / pit_min_timer_count);
204 static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val)
214 s = &pit_channels[channel];
215 access = (val >> 4) & 3;
218 s->latched_count = pit_get_count(s);
219 s->rw_state = RW_STATE_LATCHED_WORD0;
222 s->mode = (val >> 1) & 7;
224 s->rw_state = access - 1 + RW_STATE_LSB;
228 s = &pit_channels[addr];
229 switch(s->rw_state) {
231 pit_load_count(s, val);
234 pit_load_count(s, val << 8);
238 if (s->rw_state & 1) {
239 pit_load_count(s, (s->latched_count & 0xff) | (val << 8));
241 s->latched_count = val;
249 static uint32_t pit_ioport_read(void *opaque, uint32_t addr)
255 s = &pit_channels[addr];
256 switch(s->rw_state) {
261 count = pit_get_count(s);
263 ret = (count >> 8) & 0xff;
270 case RW_STATE_LATCHED_WORD0:
271 case RW_STATE_LATCHED_WORD1:
273 ret = s->latched_count >> 8;
275 ret = s->latched_count & 0xff;
282 void pit_init(int base)
287 for(i = 0;i < 3; i++) {
288 s = &pit_channels[i];
291 pit_load_count(s, 0);
294 register_ioport_write(base, 4, 1, pit_ioport_write, NULL);
295 register_ioport_read(base, 3, 1, pit_ioport_read, NULL);