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 void pit_ioport_write(CPUState
*env
, 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 uint32_t pit_ioport_read(CPUState
*env
, 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;
287 for(i
= 0;i
< 3; i
++) {
288 s
= &pit_channels
[i
];
291 pit_load_count(s
, 0);
294 register_ioport_write(0x40, 4, pit_ioport_write
, 1);
295 register_ioport_read(0x40, 3, pit_ioport_read
, 1);