ia64/xen-unstable

view tools/ioemu/hw/i8254.c @ 6422:e24fd7012ffb

merge?
author cl349@firebug.cl.cam.ac.uk
date Thu Aug 25 10:09:39 2005 +0000 (2005-08-25)
parents 2f20c2fce2c5 6e899a3840b2
children 4abd299ef2f6
line source
1 /*
2 * QEMU 8253/8254 interval timer emulation
3 *
4 * Copyright (c) 2003-2004 Fabrice Bellard
5 *
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:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
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
22 * THE SOFTWARE.
23 */
24 #include "vl.h"
25 #include "xenctrl.h"
26 #include <io/ioreq.h>
28 //#define DEBUG_PIT
30 #define RW_STATE_LSB 1
31 #define RW_STATE_MSB 2
32 #define RW_STATE_WORD0 3
33 #define RW_STATE_WORD1 4
35 typedef struct PITChannelState {
36 int count; /* can be 65536 */
37 uint16_t latched_count;
38 uint8_t count_latched;
39 uint8_t status_latched;
40 uint8_t status;
41 uint8_t read_state;
42 uint8_t write_state;
43 uint8_t write_latch;
44 uint8_t rw_mode;
45 uint8_t mode;
46 uint8_t bcd; /* not supported */
47 uint8_t gate; /* timer start */
48 int64_t count_load_time;
49 /* irq handling */
50 int64_t next_transition_time;
51 QEMUTimer *irq_timer;
52 int irq;
53 int vmx_channel; /* Is this accelerated by VMX ? */
54 } PITChannelState;
56 struct PITState {
57 PITChannelState channels[3];
58 };
60 static PITState pit_state;
62 static void pit_irq_timer_update(PITChannelState *s, int64_t current_time);
64 /* currently operate which channel for vmx use */
65 int vmx_channel = -1;
66 extern FILE *logfile;
67 static int pit_get_count(PITChannelState *s)
68 {
69 uint64_t d;
70 int counter;
72 d = muldiv64(qemu_get_clock(vm_clock) - s->count_load_time, PIT_FREQ, ticks_per_sec);
73 switch(s->mode) {
74 case 0:
75 case 1:
76 case 4:
77 case 5:
78 counter = (s->count - d) & 0xffff;
79 break;
80 case 3:
81 /* XXX: may be incorrect for odd counts */
82 counter = s->count - ((2 * d) % s->count);
83 break;
84 default:
85 counter = s->count - (d % s->count);
86 break;
87 }
88 return counter;
89 }
91 /* get pit output bit */
92 static int pit_get_out1(PITChannelState *s, int64_t current_time)
93 {
94 uint64_t d;
95 int out;
97 d = muldiv64(current_time - s->count_load_time, PIT_FREQ, ticks_per_sec);
98 switch(s->mode) {
99 default:
100 case 0:
101 out = (d >= s->count);
102 break;
103 case 1:
104 out = (d < s->count);
105 break;
106 case 2:
107 if ((d % s->count) == 0 && d != 0)
108 out = 1;
109 else
110 out = 0;
111 break;
112 case 3:
113 out = (d % s->count) < ((s->count + 1) >> 1);
114 break;
115 case 4:
116 case 5:
117 out = (d == s->count);
118 break;
119 }
120 return out;
121 }
123 int pit_get_out(PITState *pit, int channel, int64_t current_time)
124 {
125 PITChannelState *s = &pit->channels[channel];
126 return pit_get_out1(s, current_time);
127 }
129 /* return -1 if no transition will occur. */
130 static int64_t pit_get_next_transition_time(PITChannelState *s,
131 int64_t current_time)
132 {
133 uint64_t d, next_time, base;
134 int period2;
136 d = muldiv64(current_time - s->count_load_time, PIT_FREQ, ticks_per_sec);
137 switch(s->mode) {
138 default:
139 case 0:
140 case 1:
141 if (d < s->count)
142 next_time = s->count;
143 else
144 return -1;
145 break;
146 case 2:
147 base = (d / s->count) * s->count;
148 if ((d - base) == 0 && d != 0)
149 next_time = base + s->count;
150 else
151 next_time = base + s->count + 1;
152 break;
153 case 3:
154 base = (d / s->count) * s->count;
155 period2 = ((s->count + 1) >> 1);
156 if ((d - base) < period2)
157 next_time = base + period2;
158 else
159 next_time = base + s->count;
160 break;
161 case 4:
162 case 5:
163 if (d < s->count)
164 next_time = s->count;
165 else if (d == s->count)
166 next_time = s->count + 1;
167 else
168 return -1;
169 break;
170 }
171 /* convert to timer units */
172 next_time = s->count_load_time + muldiv64(next_time, ticks_per_sec, PIT_FREQ);
173 /* fix potential rounding problems */
174 /* XXX: better solution: use a clock at PIT_FREQ Hz */
175 if (next_time <= current_time)
176 next_time = current_time + 1;
177 return next_time;
178 }
180 /* val must be 0 or 1 */
181 void pit_set_gate(PITState *pit, int channel, int val)
182 {
183 PITChannelState *s = &pit->channels[channel];
185 switch(s->mode) {
186 default:
187 case 0:
188 case 4:
189 /* XXX: just disable/enable counting */
190 break;
191 case 1:
192 case 5:
193 if (s->gate < val) {
194 /* restart counting on rising edge */
195 s->count_load_time = qemu_get_clock(vm_clock);
196 pit_irq_timer_update(s, s->count_load_time);
197 }
198 break;
199 case 2:
200 case 3:
201 if (s->gate < val) {
202 /* restart counting on rising edge */
203 s->count_load_time = qemu_get_clock(vm_clock);
204 pit_irq_timer_update(s, s->count_load_time);
205 }
206 /* XXX: disable/enable counting */
207 break;
208 }
209 s->gate = val;
210 }
212 int pit_get_gate(PITState *pit, int channel)
213 {
214 PITChannelState *s = &pit->channels[channel];
215 return s->gate;
216 }
218 void pit_reset_vmx_vectors()
219 {
220 extern shared_iopage_t *shared_page;
221 ioreq_t *req;
222 int irq, i;
223 PITChannelState *s;
225 /* Assumes PIT is wired to IRQ0 and -1 is uninitialized irq base */
226 if ((irq = pic_irq2vec(0)) == -1)
227 return;
229 for(i = 0; i < 3; i++) {
230 if (pit_state.channels[i].vmx_channel)
231 break;
232 }
234 if (i == 3)
235 return;
237 /* Assumes just one VMX accelerated channel */
238 vmx_channel = i;
239 s = &pit_state.channels[vmx_channel];
240 fprintf(logfile,
241 "VMX_PIT:guest init pit channel %d!\n", vmx_channel);
242 req = &shared_page->vcpu_iodata[0].vp_ioreq;
244 req->state = STATE_IORESP_HOOK;
245 /*
246 * info passed to HV as following
247 * -- init count:16 bit, timer vec:8 bit,
248 * PIT channel(0~2):2 bit, rw mode:2 bit
249 */
250 req->u.data = s->count;
251 req->u.data |= (irq << 16);
252 req->u.data |= (vmx_channel << 24);
253 req->u.data |= ((s->rw_mode) << 26);
254 fprintf(logfile, "VMX_PIT:pass info 0x%llx to HV!\n", req->u.data);
255 }
257 static inline void pit_load_count(PITChannelState *s, int val)
258 {
259 if (val == 0)
260 val = 0x10000;
261 s->count_load_time = qemu_get_clock(vm_clock);
262 s->count = val;
264 /* guest init this pit channel for periodic mode. we do not update related
265 * timer so the channel never send intr from device model*/
266 if (vmx_channel != -1 && s->mode == 2) {
267 pit_reset_vmx_vectors();
268 vmx_channel = -1;
269 }
271 /* pit_irq_timer_update(s, s->count_load_time);*/
272 }
274 /* if already latched, do not latch again */
275 static void pit_latch_count(PITChannelState *s)
276 {
277 if (!s->count_latched) {
278 s->latched_count = pit_get_count(s);
279 s->count_latched = s->rw_mode;
280 }
281 }
283 static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val)
284 {
285 PITState *pit = opaque;
286 int channel, access;
287 PITChannelState *s;
289 addr &= 3;
290 if (addr == 3) {
291 channel = val >> 6;
292 if (channel == 3) {
293 /* read back command */
294 for(channel = 0; channel < 3; channel++) {
295 s = &pit->channels[channel];
296 if (val & (2 << channel)) {
297 if (!(val & 0x20)) {
298 pit_latch_count(s);
299 }
300 if (!(val & 0x10) && !s->status_latched) {
301 /* status latch */
302 /* XXX: add BCD and null count */
303 s->status = (pit_get_out1(s, qemu_get_clock(vm_clock)) << 7) |
304 (s->rw_mode << 4) |
305 (s->mode << 1) |
306 s->bcd;
307 s->status_latched = 1;
308 }
309 }
310 }
311 } else {
312 s = &pit->channels[channel];
313 access = (val >> 4) & 3;
314 if (access == 0) {
315 pit_latch_count(s);
316 } else {
317 s->rw_mode = access;
318 s->read_state = access;
319 s->write_state = access;
321 s->mode = (val >> 1) & 7;
322 s->bcd = val & 1;
323 /* XXX: update irq timer ? */
324 }
325 }
326 } else {
327 s = &pit->channels[addr];
328 s->vmx_channel = 1;
329 vmx_channel = addr;
330 switch(s->write_state) {
331 default:
332 case RW_STATE_LSB:
333 pit_load_count(s, val);
334 break;
335 case RW_STATE_MSB:
336 pit_load_count(s, val << 8);
337 break;
338 case RW_STATE_WORD0:
339 s->write_latch = val;
340 s->write_state = RW_STATE_WORD1;
341 break;
342 case RW_STATE_WORD1:
343 pit_load_count(s, s->write_latch | (val << 8));
344 s->write_state = RW_STATE_WORD0;
345 break;
346 }
347 }
348 }
350 static uint32_t pit_ioport_read(void *opaque, uint32_t addr)
351 {
352 PITState *pit = opaque;
353 int ret, count;
354 PITChannelState *s;
356 addr &= 3;
357 s = &pit->channels[addr];
358 if (s->status_latched) {
359 s->status_latched = 0;
360 ret = s->status;
361 } else if (s->count_latched) {
362 switch(s->count_latched) {
363 default:
364 case RW_STATE_LSB:
365 ret = s->latched_count & 0xff;
366 s->count_latched = 0;
367 break;
368 case RW_STATE_MSB:
369 ret = s->latched_count >> 8;
370 s->count_latched = 0;
371 break;
372 case RW_STATE_WORD0:
373 ret = s->latched_count & 0xff;
374 s->count_latched = RW_STATE_MSB;
375 break;
376 }
377 } else {
378 switch(s->read_state) {
379 default:
380 case RW_STATE_LSB:
381 count = pit_get_count(s);
382 ret = count & 0xff;
383 break;
384 case RW_STATE_MSB:
385 count = pit_get_count(s);
386 ret = (count >> 8) & 0xff;
387 break;
388 case RW_STATE_WORD0:
389 count = pit_get_count(s);
390 ret = count & 0xff;
391 s->read_state = RW_STATE_WORD1;
392 break;
393 case RW_STATE_WORD1:
394 count = pit_get_count(s);
395 ret = (count >> 8) & 0xff;
396 s->read_state = RW_STATE_WORD0;
397 break;
398 }
399 }
400 return ret;
401 }
403 static void pit_irq_timer_update(PITChannelState *s, int64_t current_time)
404 {
405 int64_t expire_time;
406 int irq_level;
408 if (!s->irq_timer)
409 return;
410 expire_time = pit_get_next_transition_time(s, current_time);
411 irq_level = pit_get_out1(s, current_time);
412 pic_set_irq(s->irq, irq_level);
413 #ifdef DEBUG_PIT
414 printf("irq_level=%d next_delay=%f\n",
415 irq_level,
416 (double)(expire_time - current_time) / ticks_per_sec);
417 #endif
418 s->next_transition_time = expire_time;
419 if (expire_time != -1)
420 qemu_mod_timer(s->irq_timer, expire_time);
421 else
422 qemu_del_timer(s->irq_timer);
423 }
425 static void pit_irq_timer(void *opaque)
426 {
427 PITChannelState *s = opaque;
429 pit_irq_timer_update(s, s->next_transition_time);
430 }
432 static void pit_save(QEMUFile *f, void *opaque)
433 {
434 PITState *pit = opaque;
435 PITChannelState *s;
436 int i;
438 for(i = 0; i < 3; i++) {
439 s = &pit->channels[i];
440 qemu_put_be32s(f, &s->count);
441 qemu_put_be16s(f, &s->latched_count);
442 qemu_put_8s(f, &s->count_latched);
443 qemu_put_8s(f, &s->status_latched);
444 qemu_put_8s(f, &s->status);
445 qemu_put_8s(f, &s->read_state);
446 qemu_put_8s(f, &s->write_state);
447 qemu_put_8s(f, &s->write_latch);
448 qemu_put_8s(f, &s->rw_mode);
449 qemu_put_8s(f, &s->mode);
450 qemu_put_8s(f, &s->bcd);
451 qemu_put_8s(f, &s->gate);
452 qemu_put_be64s(f, &s->count_load_time);
453 if (s->irq_timer) {
454 qemu_put_be64s(f, &s->next_transition_time);
455 qemu_put_timer(f, s->irq_timer);
456 }
457 }
458 }
460 static int pit_load(QEMUFile *f, void *opaque, int version_id)
461 {
462 PITState *pit = opaque;
463 PITChannelState *s;
464 int i;
466 if (version_id != 1)
467 return -EINVAL;
469 for(i = 0; i < 3; i++) {
470 s = &pit->channels[i];
471 qemu_get_be32s(f, &s->count);
472 qemu_get_be16s(f, &s->latched_count);
473 qemu_get_8s(f, &s->count_latched);
474 qemu_get_8s(f, &s->status_latched);
475 qemu_get_8s(f, &s->status);
476 qemu_get_8s(f, &s->read_state);
477 qemu_get_8s(f, &s->write_state);
478 qemu_get_8s(f, &s->write_latch);
479 qemu_get_8s(f, &s->rw_mode);
480 qemu_get_8s(f, &s->mode);
481 qemu_get_8s(f, &s->bcd);
482 qemu_get_8s(f, &s->gate);
483 qemu_get_be64s(f, &s->count_load_time);
484 if (s->irq_timer) {
485 qemu_get_be64s(f, &s->next_transition_time);
486 qemu_get_timer(f, s->irq_timer);
487 }
488 }
489 return 0;
490 }
492 static void pit_reset(void *opaque)
493 {
494 PITState *pit = opaque;
495 PITChannelState *s;
496 int i;
498 for(i = 0;i < 3; i++) {
499 s = &pit->channels[i];
500 s->mode = 3;
501 s->gate = (i != 2);
502 pit_load_count(s, 0);
503 }
504 }
506 PITState *pit_init(int base, int irq)
507 {
508 PITState *pit = &pit_state;
509 PITChannelState *s;
511 s = &pit->channels[0];
512 /* the timer 0 is connected to an IRQ */
513 s->irq_timer = qemu_new_timer(vm_clock, pit_irq_timer, s);
514 s->irq = irq;
516 register_savevm("i8254", base, 1, pit_save, pit_load, pit);
518 qemu_register_reset(pit_reset, pit);
519 register_ioport_write(base, 4, 1, pit_ioport_write, pit);
520 register_ioport_read(base, 3, 1, pit_ioport_read, pit);
522 pit_reset(pit);
524 return pit;
525 }