*/
#include "vl.h"
+/* XXX: implement all timer modes */
+
+//#define DEBUG_CUDA
+//#define DEBUG_CUDA_PACKET
+
/* Bits in B data register: all active low */
#define TREQ 0x08 /* Transfer request (input) */
#define TACK 0x10 /* Transfer acknowledge (output) */
#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 CUDA_COMBINED_FORMAT_IIC 0x25
#define CUDA_TIMER_FREQ (4700000 / 6)
+#define CUDA_ADB_POLL_FREQ 50
+
+/* CUDA returns time_t's offset from Jan 1, 1904, not 1970 */
+#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;
int data_in_index;
int data_out_index;
+ SetIRQFunc *set_irq;
int irq;
+ void *irq_opaque;
uint8_t autopoll;
uint8_t data_in[128];
uint8_t data_out[16];
+ QEMUTimer *adb_poll_timer;
} CUDAState;
static CUDAState cuda_state;
static void cuda_update(CUDAState *s);
static void cuda_receive_packet_from_host(CUDAState *s,
const uint8_t *data, int len);
+static void cuda_timer_update(CUDAState *s, CUDATimer *ti,
+ int64_t current_time);
static void cuda_update_irq(CUDAState *s)
{
- if (s->ifr & s->ier & SR_INT) {
- pic_set_irq(s->irq, 1);
+ if (s->ifr & s->ier & (SR_INT | T1_INT)) {
+ s->set_irq(s->irq_opaque, s->irq, 1);
} else {
- pic_set_irq(s->irq, 0);
+ s->set_irq(s->irq_opaque, s->irq, 0);
}
}
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 void set_counter(CUDATimer *s, unsigned int val)
+static void set_counter(CUDAState *s, CUDATimer *ti, unsigned int val)
{
- s->load_time = qemu_get_clock(vm_clock);
- s->counter_value = val;
+#ifdef DEBUG_CUDA
+ printf("cuda: T%d.counter=%d\n",
+ 1 + (ti->timer == NULL), val);
+#endif
+ ti->load_time = qemu_get_clock(vm_clock);
+ ti->counter_value = val;
+ cuda_timer_update(s, ti, ti->load_time);
}
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;
+ /* 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 {
- base = ((d - s->counter_value) % s->latch);
- base = (base * s->latch) + s->counter_value;
- next_time = base + s->latch;
+ 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
+ printf("latch=%d counter=%lld delta_next=%lld\n",
+ s->latch, d, next_time - d);
+#endif
+#endif
next_time = muldiv64(next_time, ticks_per_sec, CUDA_TIMER_FREQ) +
s->load_time;
if (next_time <= current_time)
return next_time;
}
+static void cuda_timer_update(CUDAState *s, CUDATimer *ti,
+ int64_t current_time)
+{
+ if (!ti->timer)
+ return;
+ if ((s->acr & T1MODE) != T1MODE_CONT) {
+ qemu_del_timer(ti->timer);
+ } else {
+ ti->next_irq_time = get_next_irq_time(ti, current_time);
+ qemu_mod_timer(ti->timer, ti->next_irq_time);
+ }
+}
+
static void cuda_timer1(void *opaque)
{
CUDAState *s = opaque;
CUDATimer *ti = &s->timers[0];
- ti->next_irq_time = get_next_irq_time(ti, ti->next_irq_time);
- qemu_mod_timer(ti->timer, ti->next_irq_time);
+ cuda_timer_update(s, ti, ti->next_irq_time);
s->ifr |= T1_INT;
cuda_update_irq(s);
}
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 10:
- if (s->data_in_index < s->data_in_size) {
- val = s->data_in[s->data_in_index];
- } else {
- val = 0;
- }
+ val = s->sr;
+ s->ifr &= ~SR_INT;
+ cuda_update_irq(s);
break;
case 11:
val = s->acr;
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:
break;
}
#ifdef DEBUG_CUDA
- printf("cuda: read: reg=0x%x val=%02x\n", addr, val);
+ if (addr != 13 || val != 0)
+ printf("cuda: read: reg=0x%x val=%02x\n", addr, val);
#endif
return val;
}
s->dira = val;
break;
case 4:
- val = val | (get_counter(&s->timers[0]) & 0xff00);
- set_counter(&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->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;
+ cuda_timer_update(s, &s->timers[0], qemu_get_clock(vm_clock));
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);
- set_counter(&s->timers[1], val);
+ 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->timers[1], val);
+ set_counter(s, &s->timers[1], (val << 8) | s->timers[1].latch);
break;
case 10:
s->sr = val;
break;
case 11:
s->acr = val;
- if ((s->acr & T1MODE) == T1MODE_CONT) {
- if ((s->last_acr & T1MODE) != T1MODE_CONT) {
- CUDATimer *ti = &s->timers[0];
- /* activate timer interrupt */
- ti->next_irq_time = get_next_irq_time(ti, qemu_get_clock(vm_clock));
- qemu_mod_timer(ti->timer, ti->next_irq_time);
- }
- } else {
- if ((s->last_acr & T1MODE) == T1MODE_CONT) {
- CUDATimer *ti = &s->timers[0];
- qemu_del_timer(ti->timer);
- }
- }
+ cuda_timer_update(s, &s->timers[0], qemu_get_clock(vm_clock));
cuda_update(s);
break;
case 12:
/* NOTE: TIP and TREQ are negated */
static void cuda_update(CUDAState *s)
{
- if (s->data_in_index < s->data_in_size) {
- /* data input */
- if (!(s->b & TIP) &&
- (s->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) {
- s->sr = s->data_in[s->data_in_index++];
- s->ifr |= SR_INT;
- cuda_update_irq(s);
- }
- }
- if (s->data_in_index < s->data_in_size) {
- /* there is some data to read */
- s->b = (s->b & ~TREQ);
- } else {
- s->b = (s->b | TREQ);
- }
+ int packet_received, len;
- if (s->acr & SR_OUT) {
- /* data output */
- if (!(s->b & TIP) &&
- (s->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) {
- if (s->data_out_index < sizeof(s->data_out)) {
- s->data_out[s->data_out_index++] = s->sr;
+ packet_received = 0;
+ if (!(s->b & TIP)) {
+ /* transfer requested from host */
+
+ if (s->acr & SR_OUT) {
+ /* data output */
+ if ((s->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) {
+ if (s->data_out_index < sizeof(s->data_out)) {
+#ifdef DEBUG_CUDA
+ printf("cuda: send: %02x\n", s->sr);
+#endif
+ s->data_out[s->data_out_index++] = s->sr;
+ s->ifr |= SR_INT;
+ cuda_update_irq(s);
+ }
+ }
+ } else {
+ if (s->data_in_index < s->data_in_size) {
+ /* data input */
+ if ((s->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) {
+ s->sr = s->data_in[s->data_in_index++];
+#ifdef DEBUG_CUDA
+ printf("cuda: recv: %02x\n", s->sr);
+#endif
+ /* indicate end of transfer */
+ if (s->data_in_index >= s->data_in_size) {
+ s->b = (s->b | TREQ);
+ }
+ s->ifr |= SR_INT;
+ cuda_update_irq(s);
+ }
}
+ }
+ } else {
+ /* no transfer requested: handle sync case */
+ if ((s->last_b & TIP) && (s->b & TACK) != (s->last_b & TACK)) {
+ /* update TREQ state each time TACK change state */
+ if (s->b & TACK)
+ s->b = (s->b | TREQ);
+ else
+ s->b = (s->b & ~TREQ);
s->ifr |= SR_INT;
cuda_update_irq(s);
+ } else {
+ if (!(s->last_b & TIP)) {
+ /* handle end of host to cuda transfert */
+ packet_received = (s->data_out_index > 0);
+ /* always an IRQ at the end of transfert */
+ s->ifr |= SR_INT;
+ cuda_update_irq(s);
+ }
+ /* signal if there is data to read */
+ if (s->data_in_index < s->data_in_size) {
+ s->b = (s->b & ~TREQ);
+ }
}
}
- /* check end of data output */
- if (!(s->acr & SR_OUT) && (s->last_acr & SR_OUT)) {
- if (s->data_out_index > 0)
- cuda_receive_packet_from_host(s, s->data_out, s->data_out_index);
- s->data_out_index = 0;
- }
s->last_acr = s->acr;
s->last_b = s->b;
+
+ /* NOTE: cuda_receive_packet_from_host() can call cuda_update()
+ recursively */
+ if (packet_received) {
+ len = s->data_out_index;
+ s->data_out_index = 0;
+ cuda_receive_packet_from_host(s, s->data_out, len);
+ }
}
static void cuda_send_packet_to_host(CUDAState *s,
const uint8_t *data, int len)
{
+#ifdef DEBUG_CUDA_PACKET
+ {
+ int i;
+ printf("cuda_send_packet_to_host:\n");
+ for(i = 0; i < len; i++)
+ printf(" %02x", data[i]);
+ printf("\n");
+ }
+#endif
memcpy(s->data_in, data, len);
s->data_in_size = len;
s->data_in_index = 0;
cuda_update_irq(s);
}
-void adb_send_packet(ADBBusState *bus, const uint8_t *buf, int len)
+static void cuda_adb_poll(void *opaque)
{
- CUDAState *s = &cuda_state;
- uint8_t data[16];
-
- memcpy(data + 1, buf, len);
- data[0] = ADB_PACKET;
- cuda_send_packet_to_host(s, data, len + 1);
+ CUDAState *s = opaque;
+ uint8_t obuf[ADB_MAX_OUT_LEN + 2];
+ int olen;
+
+ olen = adb_poll(&adb_bus, obuf + 2);
+ if (olen > 0) {
+ obuf[0] = ADB_PACKET;
+ obuf[1] = 0x40; /* polled data */
+ cuda_send_packet_to_host(s, obuf, olen + 2);
+ }
+ qemu_mod_timer(s->adb_poll_timer,
+ qemu_get_clock(vm_clock) +
+ (ticks_per_sec / CUDA_ADB_POLL_FREQ));
}
static void cuda_receive_packet(CUDAState *s,
const uint8_t *data, int len)
{
uint8_t obuf[16];
- int ti;
+ int ti, autopoll;
switch(data[0]) {
case CUDA_AUTOPOLL:
- s->autopoll = data[1];
+ autopoll = (data[1] != 0);
+ if (autopoll != s->autopoll) {
+ s->autopoll = autopoll;
+ if (autopoll) {
+ qemu_mod_timer(s->adb_poll_timer,
+ qemu_get_clock(vm_clock) +
+ (ticks_per_sec / CUDA_ADB_POLL_FREQ));
+ } else {
+ qemu_del_timer(s->adb_poll_timer);
+ }
+ }
obuf[0] = CUDA_PACKET;
obuf[1] = data[1];
cuda_send_packet_to_host(s, obuf, 2);
break;
case CUDA_GET_TIME:
+ case CUDA_SET_TIME:
/* XXX: add time support ? */
- ti = 0;
+ ti = time(NULL) + RTC_OFFSET;
obuf[0] = CUDA_PACKET;
obuf[1] = 0;
obuf[2] = 0;
obuf[6] = ti;
cuda_send_packet_to_host(s, obuf, 7);
break;
- case CUDA_SET_TIME:
case CUDA_FILE_SERVER_FLAG:
case CUDA_SET_DEVICE_LIST:
case CUDA_SET_AUTO_RATE:
obuf[1] = 0;
cuda_send_packet_to_host(s, obuf, 2);
break;
+ case CUDA_POWERDOWN:
+ obuf[0] = CUDA_PACKET;
+ obuf[1] = 0;
+ cuda_send_packet_to_host(s, obuf, 2);
+ qemu_system_shutdown_request();
+ break;
default:
break;
}
static void cuda_receive_packet_from_host(CUDAState *s,
const uint8_t *data, int len)
{
+#ifdef DEBUG_CUDA_PACKET
+ {
+ int i;
+ printf("cuda_receive_packet_from_host:\n");
+ for(i = 0; i < len; i++)
+ printf(" %02x", data[i]);
+ printf("\n");
+ }
+#endif
switch(data[0]) {
case ADB_PACKET:
- adb_receive_packet(&adb_bus, data + 1, len - 1);
+ {
+ uint8_t obuf[ADB_MAX_OUT_LEN + 2];
+ int olen;
+ olen = adb_request(&adb_bus, obuf + 2, data + 1, len - 1);
+ if (olen > 0) {
+ obuf[0] = ADB_PACKET;
+ obuf[1] = 0x00;
+ } else {
+ /* error */
+ obuf[0] = ADB_PACKET;
+ obuf[1] = -olen;
+ olen = 0;
+ }
+ cuda_send_packet_to_host(s, obuf, olen + 2);
+ }
break;
case CUDA_PACKET:
cuda_receive_packet(s, data + 1, len - 1);
&cuda_readl,
};
-int cuda_init(void)
+int cuda_init(SetIRQFunc *set_irq, void *irq_opaque, int irq)
{
CUDAState *s = &cuda_state;
int cuda_mem_index;
- s->timers[0].latch = 0x10000;
- set_counter(&s->timers[0], 0xffff);
+ s->set_irq = set_irq;
+ 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[1].latch = 0x10000;
- set_counter(&s->timers[1], 0xffff);
+ s->timers[0].latch = 0xffff;
+ set_counter(s, &s->timers[0], 0xffff);
+
+ 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);
+
+ s->adb_poll_timer = qemu_new_timer(vm_clock, cuda_adb_poll, s);
cuda_mem_index = cpu_register_io_memory(0, cuda_read, cuda_write, s);
return cuda_mem_index;
}