*/
#include "vl.h"
+/* XXX: implement all timer modes */
+
//#define DEBUG_CUDA
//#define DEBUG_CUDA_PACKET
#define IER_CLR 0 /* clear bits in IER */
#define SR_INT 0x04 /* Shift register full/empty */
#define T1_INT 0x40 /* Timer 1 interrupt */
+#define T2_INT 0x20 /* Timer 2 interrupt */
/* Bits in ACR */
#define T1MODE 0xc0 /* Timer 1 mode */
#define RTC_OFFSET 2082844800
typedef struct CUDATimer {
- unsigned int latch;
+ int index;
+ uint16_t latch;
uint16_t counter_value; /* counter value at load time */
int64_t load_time;
int64_t next_irq_time;
d = muldiv64(qemu_get_clock(vm_clock) - s->load_time,
CUDA_TIMER_FREQ, ticks_per_sec);
- if (d <= s->counter_value) {
- counter = d;
+ if (s->index == 0) {
+ /* the timer goes down from latch to -1 (period of latch + 2) */
+ if (d <= (s->counter_value + 1)) {
+ counter = (s->counter_value - d) & 0xffff;
+ } else {
+ counter = (d - (s->counter_value + 1)) % (s->latch + 2);
+ counter = (s->latch - counter) & 0xffff;
+ }
} else {
- counter = s->latch - 1 - ((d - s->counter_value) % s->latch);
+ counter = (s->counter_value - d) & 0xffff;
}
return counter;
}
static int64_t get_next_irq_time(CUDATimer *s, int64_t current_time)
{
- int64_t d, next_time, base;
+ int64_t d, next_time;
+ unsigned int counter;
+
/* current counter value */
d = muldiv64(current_time - s->load_time,
CUDA_TIMER_FREQ, ticks_per_sec);
- if (d < s->counter_value) {
- next_time = s->counter_value + 1;
- } else
- {
- base = ((d - s->counter_value + 1) / s->latch);
- base = (base * s->latch) + s->counter_value;
- next_time = base + s->latch;
+ /* the timer goes down from latch to -1 (period of latch + 2) */
+ if (d <= (s->counter_value + 1)) {
+ counter = (s->counter_value - d) & 0xffff;
+ } else {
+ counter = (d - (s->counter_value + 1)) % (s->latch + 2);
+ counter = (s->latch - counter) & 0xffff;
+ }
+
+ /* Note: we consider the irq is raised on 0 */
+ if (counter == 0xffff) {
+ next_time = d + s->latch + 1;
+ } else if (counter == 0) {
+ next_time = d + s->latch + 2;
+ } else {
+ next_time = d + counter;
}
#if 0
#ifdef DEBUG_CUDA
break;
case 5:
val = get_counter(&s->timers[0]) >> 8;
- s->ifr &= ~T1_INT;
cuda_update_irq(s);
break;
case 6:
val = s->timers[0].latch & 0xff;
break;
case 7:
+ /* XXX: check this */
val = (s->timers[0].latch >> 8) & 0xff;
break;
case 8:
val = get_counter(&s->timers[1]) & 0xff;
+ s->ifr &= ~T2_INT;
break;
case 9:
val = get_counter(&s->timers[1]) >> 8;
break;
case 13:
val = s->ifr;
+ if (s->ifr & s->ier)
+ val |= 0x80;
break;
case 14:
- val = s->ier;
+ val = s->ier | 0x80;
break;
default:
case 15:
s->dira = val;
break;
case 4:
- val = val | (get_counter(&s->timers[0]) & 0xff00);
- set_counter(s, &s->timers[0], val);
+ s->timers[0].latch = (s->timers[0].latch & 0xff00) | val;
+ cuda_timer_update(s, &s->timers[0], qemu_get_clock(vm_clock));
break;
case 5:
- val = (val << 8) | (get_counter(&s->timers[0]) & 0xff);
- set_counter(s, &s->timers[0], val);
+ s->timers[0].latch = (s->timers[0].latch & 0xff) | (val << 8);
+ s->ifr &= ~T1_INT;
+ set_counter(s, &s->timers[0], s->timers[0].latch);
break;
case 6:
s->timers[0].latch = (s->timers[0].latch & 0xff00) | val;
break;
case 7:
s->timers[0].latch = (s->timers[0].latch & 0xff) | (val << 8);
+ s->ifr &= ~T1_INT;
cuda_timer_update(s, &s->timers[0], qemu_get_clock(vm_clock));
break;
case 8:
- val = val | (get_counter(&s->timers[1]) & 0xff00);
+ s->timers[1].latch = val;
set_counter(s, &s->timers[1], val);
break;
case 9:
- val = (val << 8) | (get_counter(&s->timers[1]) & 0xff);
- set_counter(s, &s->timers[1], val);
+ set_counter(s, &s->timers[1], (val << 8) | s->timers[1].latch);
break;
case 10:
s->sr = val;
cuda_update_irq(s);
break;
case 14:
-#if 0
if (val & IER_SET) {
/* set bits */
s->ier |= val & 0x7f;
/* reset bits */
s->ier &= ~val;
}
-#else
- /* XXX: please explain me why the SPEC is not correct ! */
- s->ier = val;
-#endif
cuda_update_irq(s);
break;
default:
s->irq_opaque = irq_opaque;
s->irq = irq;
+ s->timers[0].index = 0;
s->timers[0].timer = qemu_new_timer(vm_clock, cuda_timer1, s);
- s->timers[0].latch = 0x10000;
+ s->timers[0].latch = 0xffff;
set_counter(s, &s->timers[0], 0xffff);
- s->timers[1].latch = 0x10000;
+
+ s->timers[1].index = 1;
+ s->timers[1].latch = 0;
// s->ier = T1_INT | SR_INT;
s->ier = 0;
set_counter(s, &s->timers[1], 0xffff);