0.7.0-alt1
[qemu] / qemu / hw / i8254.c
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
26 //#define DEBUG_PIT
27
28 #define RW_STATE_LSB 1
29 #define RW_STATE_MSB 2
30 #define RW_STATE_WORD0 3
31 #define RW_STATE_WORD1 4
32
33 typedef struct PITChannelState {
34     int count; /* can be 65536 */
35     uint16_t latched_count;
36     uint8_t count_latched;
37     uint8_t status_latched;
38     uint8_t status;
39     uint8_t read_state;
40     uint8_t write_state;
41     uint8_t write_latch;
42     uint8_t rw_mode;
43     uint8_t mode;
44     uint8_t bcd; /* not supported */
45     uint8_t gate; /* timer start */
46     int64_t count_load_time;
47     /* irq handling */
48     int64_t next_transition_time;
49     QEMUTimer *irq_timer;
50     int irq;
51 } PITChannelState;
52
53 struct PITState {
54     PITChannelState channels[3];
55 };
56
57 static PITState pit_state;
58
59 static void pit_irq_timer_update(PITChannelState *s, int64_t current_time);
60
61 static int pit_get_count(PITChannelState *s)
62 {
63     uint64_t d;
64     int counter;
65
66     d = muldiv64(qemu_get_clock(vm_clock) - s->count_load_time, PIT_FREQ, ticks_per_sec);
67     switch(s->mode) {
68     case 0:
69     case 1:
70     case 4:
71     case 5:
72         counter = (s->count - d) & 0xffff;
73         break;
74     case 3:
75         /* XXX: may be incorrect for odd counts */
76         counter = s->count - ((2 * d) % s->count);
77         break;
78     default:
79         counter = s->count - (d % s->count);
80         break;
81     }
82     return counter;
83 }
84
85 /* get pit output bit */
86 static int pit_get_out1(PITChannelState *s, int64_t current_time)
87 {
88     uint64_t d;
89     int out;
90
91     d = muldiv64(current_time - s->count_load_time, PIT_FREQ, ticks_per_sec);
92     switch(s->mode) {
93     default:
94     case 0:
95         out = (d >= s->count);
96         break;
97     case 1:
98         out = (d < s->count);
99         break;
100     case 2:
101         if ((d % s->count) == 0 && d != 0)
102             out = 1;
103         else
104             out = 0;
105         break;
106     case 3:
107         out = (d % s->count) < ((s->count + 1) >> 1);
108         break;
109     case 4:
110     case 5:
111         out = (d == s->count);
112         break;
113     }
114     return out;
115 }
116
117 int pit_get_out(PITState *pit, int channel, int64_t current_time)
118 {
119     PITChannelState *s = &pit->channels[channel];
120     return pit_get_out1(s, current_time);
121 }
122
123 /* return -1 if no transition will occur.  */
124 static int64_t pit_get_next_transition_time(PITChannelState *s, 
125                                             int64_t current_time)
126 {
127     uint64_t d, next_time, base;
128     int period2;
129
130     d = muldiv64(current_time - s->count_load_time, PIT_FREQ, ticks_per_sec);
131     switch(s->mode) {
132     default:
133     case 0:
134     case 1:
135         if (d < s->count)
136             next_time = s->count;
137         else
138             return -1;
139         break;
140     case 2:
141         base = (d / s->count) * s->count;
142         if ((d - base) == 0 && d != 0)
143             next_time = base + s->count;
144         else
145             next_time = base + s->count + 1;
146         break;
147     case 3:
148         base = (d / s->count) * s->count;
149         period2 = ((s->count + 1) >> 1);
150         if ((d - base) < period2) 
151             next_time = base + period2;
152         else
153             next_time = base + s->count;
154         break;
155     case 4:
156     case 5:
157         if (d < s->count)
158             next_time = s->count;
159         else if (d == s->count)
160             next_time = s->count + 1;
161         else
162             return -1;
163         break;
164     }
165     /* convert to timer units */
166     next_time = s->count_load_time + muldiv64(next_time, ticks_per_sec, PIT_FREQ);
167     /* fix potential rounding problems */
168     /* XXX: better solution: use a clock at PIT_FREQ Hz */
169     if (next_time <= current_time)
170         next_time = current_time + 1;
171     return next_time;
172 }
173
174 /* val must be 0 or 1 */
175 void pit_set_gate(PITState *pit, int channel, int val)
176 {
177     PITChannelState *s = &pit->channels[channel];
178
179     switch(s->mode) {
180     default:
181     case 0:
182     case 4:
183         /* XXX: just disable/enable counting */
184         break;
185     case 1:
186     case 5:
187         if (s->gate < val) {
188             /* restart counting on rising edge */
189             s->count_load_time = qemu_get_clock(vm_clock);
190             pit_irq_timer_update(s, s->count_load_time);
191         }
192         break;
193     case 2:
194     case 3:
195         if (s->gate < val) {
196             /* restart counting on rising edge */
197             s->count_load_time = qemu_get_clock(vm_clock);
198             pit_irq_timer_update(s, s->count_load_time);
199         }
200         /* XXX: disable/enable counting */
201         break;
202     }
203     s->gate = val;
204 }
205
206 int pit_get_gate(PITState *pit, int channel)
207 {
208     PITChannelState *s = &pit->channels[channel];
209     return s->gate;
210 }
211
212 static inline void pit_load_count(PITChannelState *s, int val)
213 {
214     if (val == 0)
215         val = 0x10000;
216     s->count_load_time = qemu_get_clock(vm_clock);
217     s->count = val;
218     pit_irq_timer_update(s, s->count_load_time);
219 }
220
221 /* if already latched, do not latch again */
222 static void pit_latch_count(PITChannelState *s)
223 {
224     if (!s->count_latched) {
225         s->latched_count = pit_get_count(s);
226         s->count_latched = s->rw_mode;
227     }
228 }
229
230 static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val)
231 {
232     PITState *pit = opaque;
233     int channel, access;
234     PITChannelState *s;
235
236     addr &= 3;
237     if (addr == 3) {
238         channel = val >> 6;
239         if (channel == 3) {
240             /* read back command */
241             for(channel = 0; channel < 3; channel++) {
242                 s = &pit->channels[channel];
243                 if (val & (2 << channel)) {
244                     if (!(val & 0x20)) {
245                         pit_latch_count(s);
246                     }
247                     if (!(val & 0x10) && !s->status_latched) {
248                         /* status latch */
249                         /* XXX: add BCD and null count */
250                         s->status =  (pit_get_out1(s, qemu_get_clock(vm_clock)) << 7) |
251                             (s->rw_mode << 4) |
252                             (s->mode << 1) |
253                             s->bcd;
254                         s->status_latched = 1;
255                     }
256                 }
257             }
258         } else {
259             s = &pit->channels[channel];
260             access = (val >> 4) & 3;
261             if (access == 0) {
262                 pit_latch_count(s);
263             } else {
264                 s->rw_mode = access;
265                 s->read_state = access;
266                 s->write_state = access;
267
268                 s->mode = (val >> 1) & 7;
269                 s->bcd = val & 1;
270                 /* XXX: update irq timer ? */
271             }
272         }
273     } else {
274         s = &pit->channels[addr];
275         switch(s->write_state) {
276         default:
277         case RW_STATE_LSB:
278             pit_load_count(s, val);
279             break;
280         case RW_STATE_MSB:
281             pit_load_count(s, val << 8);
282             break;
283         case RW_STATE_WORD0:
284             s->write_latch = val;
285             s->write_state = RW_STATE_WORD1;
286             break;
287         case RW_STATE_WORD1:
288             pit_load_count(s, s->write_latch | (val << 8));
289             s->write_state = RW_STATE_WORD0;
290             break;
291         }
292     }
293 }
294
295 static uint32_t pit_ioport_read(void *opaque, uint32_t addr)
296 {
297     PITState *pit = opaque;
298     int ret, count;
299     PITChannelState *s;
300     
301     addr &= 3;
302     s = &pit->channels[addr];
303     if (s->status_latched) {
304         s->status_latched = 0;
305         ret = s->status;
306     } else if (s->count_latched) {
307         switch(s->count_latched) {
308         default:
309         case RW_STATE_LSB:
310             ret = s->latched_count & 0xff;
311             s->count_latched = 0;
312             break;
313         case RW_STATE_MSB:
314             ret = s->latched_count >> 8;
315             s->count_latched = 0;
316             break;
317         case RW_STATE_WORD0:
318             ret = s->latched_count & 0xff;
319             s->count_latched = RW_STATE_MSB;
320             break;
321         }
322     } else {
323         switch(s->read_state) {
324         default:
325         case RW_STATE_LSB:
326             count = pit_get_count(s);
327             ret = count & 0xff;
328             break;
329         case RW_STATE_MSB:
330             count = pit_get_count(s);
331             ret = (count >> 8) & 0xff;
332             break;
333         case RW_STATE_WORD0:
334             count = pit_get_count(s);
335             ret = count & 0xff;
336             s->read_state = RW_STATE_WORD1;
337             break;
338         case RW_STATE_WORD1:
339             count = pit_get_count(s);
340             ret = (count >> 8) & 0xff;
341             s->read_state = RW_STATE_WORD0;
342             break;
343         }
344     }
345     return ret;
346 }
347
348 static void pit_irq_timer_update(PITChannelState *s, int64_t current_time)
349 {
350     int64_t expire_time;
351     int irq_level;
352
353     if (!s->irq_timer)
354         return;
355     expire_time = pit_get_next_transition_time(s, current_time);
356     irq_level = pit_get_out1(s, current_time);
357     pic_set_irq(s->irq, irq_level);
358 #ifdef DEBUG_PIT
359     printf("irq_level=%d next_delay=%f\n",
360            irq_level, 
361            (double)(expire_time - current_time) / ticks_per_sec);
362 #endif
363     s->next_transition_time = expire_time;
364     if (expire_time != -1)
365         qemu_mod_timer(s->irq_timer, expire_time);
366     else
367         qemu_del_timer(s->irq_timer);
368 }
369
370 static void pit_irq_timer(void *opaque)
371 {
372     PITChannelState *s = opaque;
373
374     pit_irq_timer_update(s, s->next_transition_time);
375 }
376
377 static void pit_save(QEMUFile *f, void *opaque)
378 {
379     PITState *pit = opaque;
380     PITChannelState *s;
381     int i;
382     
383     for(i = 0; i < 3; i++) {
384         s = &pit->channels[i];
385         qemu_put_be32s(f, &s->count);
386         qemu_put_be16s(f, &s->latched_count);
387         qemu_put_8s(f, &s->count_latched);
388         qemu_put_8s(f, &s->status_latched);
389         qemu_put_8s(f, &s->status);
390         qemu_put_8s(f, &s->read_state);
391         qemu_put_8s(f, &s->write_state);
392         qemu_put_8s(f, &s->write_latch);
393         qemu_put_8s(f, &s->rw_mode);
394         qemu_put_8s(f, &s->mode);
395         qemu_put_8s(f, &s->bcd);
396         qemu_put_8s(f, &s->gate);
397         qemu_put_be64s(f, &s->count_load_time);
398         if (s->irq_timer) {
399             qemu_put_be64s(f, &s->next_transition_time);
400             qemu_put_timer(f, s->irq_timer);
401         }
402     }
403 }
404
405 static int pit_load(QEMUFile *f, void *opaque, int version_id)
406 {
407     PITState *pit = opaque;
408     PITChannelState *s;
409     int i;
410     
411     if (version_id != 1)
412         return -EINVAL;
413
414     for(i = 0; i < 3; i++) {
415         s = &pit->channels[i];
416         qemu_get_be32s(f, &s->count);
417         qemu_get_be16s(f, &s->latched_count);
418         qemu_get_8s(f, &s->count_latched);
419         qemu_get_8s(f, &s->status_latched);
420         qemu_get_8s(f, &s->status);
421         qemu_get_8s(f, &s->read_state);
422         qemu_get_8s(f, &s->write_state);
423         qemu_get_8s(f, &s->write_latch);
424         qemu_get_8s(f, &s->rw_mode);
425         qemu_get_8s(f, &s->mode);
426         qemu_get_8s(f, &s->bcd);
427         qemu_get_8s(f, &s->gate);
428         qemu_get_be64s(f, &s->count_load_time);
429         if (s->irq_timer) {
430             qemu_get_be64s(f, &s->next_transition_time);
431             qemu_get_timer(f, s->irq_timer);
432         }
433     }
434     return 0;
435 }
436
437 static void pit_reset(void *opaque)
438 {
439     PITState *pit = opaque;
440     PITChannelState *s;
441     int i;
442
443     for(i = 0;i < 3; i++) {
444         s = &pit->channels[i];
445         s->mode = 3;
446         s->gate = (i != 2);
447         pit_load_count(s, 0);
448     }
449 }
450
451 PITState *pit_init(int base, int irq)
452 {
453     PITState *pit = &pit_state;
454     PITChannelState *s;
455
456     s = &pit->channels[0];
457     /* the timer 0 is connected to an IRQ */
458     s->irq_timer = qemu_new_timer(vm_clock, pit_irq_timer, s);
459     s->irq = irq;
460
461     register_savevm("i8254", base, 1, pit_save, pit_load, pit);
462
463     qemu_register_reset(pit_reset, pit);
464     register_ioport_write(base, 4, 1, pit_ioport_write, pit);
465     register_ioport_read(base, 3, 1, pit_ioport_read, pit);
466
467     pit_reset(pit);
468
469     return pit;
470 }