1267002cdSbellard /* 23cbee15bSj_mayer * QEMU PowerMac CUDA device support 3267002cdSbellard * 43cbee15bSj_mayer * Copyright (c) 2004-2007 Fabrice Bellard 53cbee15bSj_mayer * Copyright (c) 2007 Jocelyn Mayer 6267002cdSbellard * 7267002cdSbellard * Permission is hereby granted, free of charge, to any person obtaining a copy 8267002cdSbellard * of this software and associated documentation files (the "Software"), to deal 9267002cdSbellard * in the Software without restriction, including without limitation the rights 10267002cdSbellard * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11267002cdSbellard * copies of the Software, and to permit persons to whom the Software is 12267002cdSbellard * furnished to do so, subject to the following conditions: 13267002cdSbellard * 14267002cdSbellard * The above copyright notice and this permission notice shall be included in 15267002cdSbellard * all copies or substantial portions of the Software. 16267002cdSbellard * 17267002cdSbellard * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18267002cdSbellard * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19267002cdSbellard * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20267002cdSbellard * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21267002cdSbellard * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22267002cdSbellard * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23267002cdSbellard * THE SOFTWARE. 24267002cdSbellard */ 2583c9f4caSPaolo Bonzini #include "hw/hw.h" 2683c9f4caSPaolo Bonzini #include "hw/ppc/mac.h" 270d09e41aSPaolo Bonzini #include "hw/input/adb.h" 281de7afc9SPaolo Bonzini #include "qemu/timer.h" 299c17d615SPaolo Bonzini #include "sysemu/sysemu.h" 30267002cdSbellard 3161271e5cSbellard /* XXX: implement all timer modes */ 3261271e5cSbellard 33ea026b2fSblueswir1 /* debug CUDA */ 34819e712bSbellard //#define DEBUG_CUDA 35ea026b2fSblueswir1 36ea026b2fSblueswir1 /* debug CUDA packets */ 37819e712bSbellard //#define DEBUG_CUDA_PACKET 38819e712bSbellard 39ea026b2fSblueswir1 #ifdef DEBUG_CUDA 40001faf32SBlue Swirl #define CUDA_DPRINTF(fmt, ...) \ 41001faf32SBlue Swirl do { printf("CUDA: " fmt , ## __VA_ARGS__); } while (0) 42ea026b2fSblueswir1 #else 43001faf32SBlue Swirl #define CUDA_DPRINTF(fmt, ...) 44ea026b2fSblueswir1 #endif 45ea026b2fSblueswir1 46267002cdSbellard /* Bits in B data register: all active low */ 47267002cdSbellard #define TREQ 0x08 /* Transfer request (input) */ 48267002cdSbellard #define TACK 0x10 /* Transfer acknowledge (output) */ 49267002cdSbellard #define TIP 0x20 /* Transfer in progress (output) */ 50267002cdSbellard 51267002cdSbellard /* Bits in ACR */ 52267002cdSbellard #define SR_CTRL 0x1c /* Shift register control bits */ 53267002cdSbellard #define SR_EXT 0x0c /* Shift on external clock */ 54267002cdSbellard #define SR_OUT 0x10 /* Shift out if 1 */ 55267002cdSbellard 56267002cdSbellard /* Bits in IFR and IER */ 57267002cdSbellard #define IER_SET 0x80 /* set bits in IER */ 58267002cdSbellard #define IER_CLR 0 /* clear bits in IER */ 59267002cdSbellard #define SR_INT 0x04 /* Shift register full/empty */ 60267002cdSbellard #define T1_INT 0x40 /* Timer 1 interrupt */ 6161271e5cSbellard #define T2_INT 0x20 /* Timer 2 interrupt */ 62267002cdSbellard 63267002cdSbellard /* Bits in ACR */ 64267002cdSbellard #define T1MODE 0xc0 /* Timer 1 mode */ 65267002cdSbellard #define T1MODE_CONT 0x40 /* continuous interrupts */ 66267002cdSbellard 67267002cdSbellard /* commands (1st byte) */ 68267002cdSbellard #define ADB_PACKET 0 69267002cdSbellard #define CUDA_PACKET 1 70267002cdSbellard #define ERROR_PACKET 2 71267002cdSbellard #define TIMER_PACKET 3 72267002cdSbellard #define POWER_PACKET 4 73267002cdSbellard #define MACIIC_PACKET 5 74267002cdSbellard #define PMU_PACKET 6 75267002cdSbellard 76267002cdSbellard 77267002cdSbellard /* CUDA commands (2nd byte) */ 78267002cdSbellard #define CUDA_WARM_START 0x0 79267002cdSbellard #define CUDA_AUTOPOLL 0x1 80267002cdSbellard #define CUDA_GET_6805_ADDR 0x2 81267002cdSbellard #define CUDA_GET_TIME 0x3 82267002cdSbellard #define CUDA_GET_PRAM 0x7 83267002cdSbellard #define CUDA_SET_6805_ADDR 0x8 84267002cdSbellard #define CUDA_SET_TIME 0x9 85267002cdSbellard #define CUDA_POWERDOWN 0xa 86267002cdSbellard #define CUDA_POWERUP_TIME 0xb 87267002cdSbellard #define CUDA_SET_PRAM 0xc 88267002cdSbellard #define CUDA_MS_RESET 0xd 89267002cdSbellard #define CUDA_SEND_DFAC 0xe 90267002cdSbellard #define CUDA_BATTERY_SWAP_SENSE 0x10 91267002cdSbellard #define CUDA_RESET_SYSTEM 0x11 92267002cdSbellard #define CUDA_SET_IPL 0x12 93267002cdSbellard #define CUDA_FILE_SERVER_FLAG 0x13 94267002cdSbellard #define CUDA_SET_AUTO_RATE 0x14 95267002cdSbellard #define CUDA_GET_AUTO_RATE 0x16 96267002cdSbellard #define CUDA_SET_DEVICE_LIST 0x19 97267002cdSbellard #define CUDA_GET_DEVICE_LIST 0x1a 98267002cdSbellard #define CUDA_SET_ONE_SECOND_MODE 0x1b 99267002cdSbellard #define CUDA_SET_POWER_MESSAGES 0x21 100267002cdSbellard #define CUDA_GET_SET_IIC 0x22 101267002cdSbellard #define CUDA_WAKEUP 0x23 102267002cdSbellard #define CUDA_TIMER_TICKLE 0x24 103267002cdSbellard #define CUDA_COMBINED_FORMAT_IIC 0x25 104267002cdSbellard 105267002cdSbellard #define CUDA_TIMER_FREQ (4700000 / 6) 106e2733d20Sbellard #define CUDA_ADB_POLL_FREQ 50 107267002cdSbellard 108d7ce296fSbellard /* CUDA returns time_t's offset from Jan 1, 1904, not 1970 */ 109d7ce296fSbellard #define RTC_OFFSET 2082844800 110d7ce296fSbellard 111267002cdSbellard static void cuda_update(CUDAState *s); 112267002cdSbellard static void cuda_receive_packet_from_host(CUDAState *s, 113267002cdSbellard const uint8_t *data, int len); 114819e712bSbellard static void cuda_timer_update(CUDAState *s, CUDATimer *ti, 115819e712bSbellard int64_t current_time); 116267002cdSbellard 117267002cdSbellard static void cuda_update_irq(CUDAState *s) 118267002cdSbellard { 119819e712bSbellard if (s->ifr & s->ier & (SR_INT | T1_INT)) { 120d537cf6cSpbrook qemu_irq_raise(s->irq); 121267002cdSbellard } else { 122d537cf6cSpbrook qemu_irq_lower(s->irq); 123267002cdSbellard } 124267002cdSbellard } 125267002cdSbellard 126b981289cSAlexander Graf static uint64_t get_tb(uint64_t freq) 127b981289cSAlexander Graf { 128b981289cSAlexander Graf return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 129b981289cSAlexander Graf freq, get_ticks_per_sec()); 130b981289cSAlexander Graf } 131b981289cSAlexander Graf 132267002cdSbellard static unsigned int get_counter(CUDATimer *s) 133267002cdSbellard { 134267002cdSbellard int64_t d; 135267002cdSbellard unsigned int counter; 136b981289cSAlexander Graf uint64_t tb_diff; 137267002cdSbellard 138b981289cSAlexander Graf /* Reverse of the tb calculation algorithm that Mac OS X uses on bootup. */ 139b981289cSAlexander Graf tb_diff = get_tb(s->frequency) - s->load_time; 140b981289cSAlexander Graf d = (tb_diff * 0xBF401675E5DULL) / (s->frequency << 24); 141b981289cSAlexander Graf 14261271e5cSbellard if (s->index == 0) { 14361271e5cSbellard /* the timer goes down from latch to -1 (period of latch + 2) */ 14461271e5cSbellard if (d <= (s->counter_value + 1)) { 14561271e5cSbellard counter = (s->counter_value - d) & 0xffff; 146267002cdSbellard } else { 14761271e5cSbellard counter = (d - (s->counter_value + 1)) % (s->latch + 2); 14861271e5cSbellard counter = (s->latch - counter) & 0xffff; 14961271e5cSbellard } 15061271e5cSbellard } else { 15161271e5cSbellard counter = (s->counter_value - d) & 0xffff; 152267002cdSbellard } 153267002cdSbellard return counter; 154267002cdSbellard } 155267002cdSbellard 156819e712bSbellard static void set_counter(CUDAState *s, CUDATimer *ti, unsigned int val) 157267002cdSbellard { 158ea026b2fSblueswir1 CUDA_DPRINTF("T%d.counter=%d\n", 1 + (ti->timer == NULL), val); 159b981289cSAlexander Graf ti->load_time = get_tb(s->frequency); 160819e712bSbellard ti->counter_value = val; 161819e712bSbellard cuda_timer_update(s, ti, ti->load_time); 162267002cdSbellard } 163267002cdSbellard 164267002cdSbellard static int64_t get_next_irq_time(CUDATimer *s, int64_t current_time) 165267002cdSbellard { 16661271e5cSbellard int64_t d, next_time; 16761271e5cSbellard unsigned int counter; 16861271e5cSbellard 169267002cdSbellard /* current counter value */ 170267002cdSbellard d = muldiv64(current_time - s->load_time, 1716ee093c9SJuan Quintela CUDA_TIMER_FREQ, get_ticks_per_sec()); 17261271e5cSbellard /* the timer goes down from latch to -1 (period of latch + 2) */ 17361271e5cSbellard if (d <= (s->counter_value + 1)) { 17461271e5cSbellard counter = (s->counter_value - d) & 0xffff; 17561271e5cSbellard } else { 17661271e5cSbellard counter = (d - (s->counter_value + 1)) % (s->latch + 2); 17761271e5cSbellard counter = (s->latch - counter) & 0xffff; 17861271e5cSbellard } 17961271e5cSbellard 18061271e5cSbellard /* Note: we consider the irq is raised on 0 */ 18161271e5cSbellard if (counter == 0xffff) { 18261271e5cSbellard next_time = d + s->latch + 1; 18361271e5cSbellard } else if (counter == 0) { 18461271e5cSbellard next_time = d + s->latch + 2; 18561271e5cSbellard } else { 18661271e5cSbellard next_time = d + counter; 187267002cdSbellard } 188ea026b2fSblueswir1 CUDA_DPRINTF("latch=%d counter=%" PRId64 " delta_next=%" PRId64 "\n", 189819e712bSbellard s->latch, d, next_time - d); 1906ee093c9SJuan Quintela next_time = muldiv64(next_time, get_ticks_per_sec(), CUDA_TIMER_FREQ) + 191267002cdSbellard s->load_time; 192267002cdSbellard if (next_time <= current_time) 193267002cdSbellard next_time = current_time + 1; 194267002cdSbellard return next_time; 195267002cdSbellard } 196267002cdSbellard 197819e712bSbellard static void cuda_timer_update(CUDAState *s, CUDATimer *ti, 198819e712bSbellard int64_t current_time) 199819e712bSbellard { 200819e712bSbellard if (!ti->timer) 201819e712bSbellard return; 202819e712bSbellard if ((s->acr & T1MODE) != T1MODE_CONT) { 203bc72ad67SAlex Bligh timer_del(ti->timer); 204819e712bSbellard } else { 205819e712bSbellard ti->next_irq_time = get_next_irq_time(ti, current_time); 206bc72ad67SAlex Bligh timer_mod(ti->timer, ti->next_irq_time); 207819e712bSbellard } 208819e712bSbellard } 209819e712bSbellard 210267002cdSbellard static void cuda_timer1(void *opaque) 211267002cdSbellard { 212267002cdSbellard CUDAState *s = opaque; 213267002cdSbellard CUDATimer *ti = &s->timers[0]; 214267002cdSbellard 215819e712bSbellard cuda_timer_update(s, ti, ti->next_irq_time); 216267002cdSbellard s->ifr |= T1_INT; 217267002cdSbellard cuda_update_irq(s); 218267002cdSbellard } 219267002cdSbellard 220a8170e5eSAvi Kivity static uint32_t cuda_readb(void *opaque, hwaddr addr) 221267002cdSbellard { 222267002cdSbellard CUDAState *s = opaque; 223267002cdSbellard uint32_t val; 224267002cdSbellard 225267002cdSbellard addr = (addr >> 9) & 0xf; 226267002cdSbellard switch(addr) { 227267002cdSbellard case 0: 228267002cdSbellard val = s->b; 229267002cdSbellard break; 230267002cdSbellard case 1: 231267002cdSbellard val = s->a; 232267002cdSbellard break; 233267002cdSbellard case 2: 234267002cdSbellard val = s->dirb; 235267002cdSbellard break; 236267002cdSbellard case 3: 237267002cdSbellard val = s->dira; 238267002cdSbellard break; 239267002cdSbellard case 4: 240267002cdSbellard val = get_counter(&s->timers[0]) & 0xff; 241267002cdSbellard s->ifr &= ~T1_INT; 242267002cdSbellard cuda_update_irq(s); 243267002cdSbellard break; 244267002cdSbellard case 5: 245267002cdSbellard val = get_counter(&s->timers[0]) >> 8; 246267002cdSbellard cuda_update_irq(s); 247267002cdSbellard break; 248267002cdSbellard case 6: 249267002cdSbellard val = s->timers[0].latch & 0xff; 250267002cdSbellard break; 251267002cdSbellard case 7: 25261271e5cSbellard /* XXX: check this */ 253267002cdSbellard val = (s->timers[0].latch >> 8) & 0xff; 254267002cdSbellard break; 255267002cdSbellard case 8: 256267002cdSbellard val = get_counter(&s->timers[1]) & 0xff; 25761271e5cSbellard s->ifr &= ~T2_INT; 258267002cdSbellard break; 259267002cdSbellard case 9: 260267002cdSbellard val = get_counter(&s->timers[1]) >> 8; 261267002cdSbellard break; 262267002cdSbellard case 10: 263819e712bSbellard val = s->sr; 264819e712bSbellard s->ifr &= ~SR_INT; 265819e712bSbellard cuda_update_irq(s); 266267002cdSbellard break; 267267002cdSbellard case 11: 268267002cdSbellard val = s->acr; 269267002cdSbellard break; 270267002cdSbellard case 12: 271267002cdSbellard val = s->pcr; 272267002cdSbellard break; 273267002cdSbellard case 13: 274267002cdSbellard val = s->ifr; 275b7c7b181Sbellard if (s->ifr & s->ier) 276b7c7b181Sbellard val |= 0x80; 277267002cdSbellard break; 278267002cdSbellard case 14: 279b7c7b181Sbellard val = s->ier | 0x80; 280267002cdSbellard break; 281267002cdSbellard default: 282267002cdSbellard case 15: 283267002cdSbellard val = s->anh; 284267002cdSbellard break; 285267002cdSbellard } 2863c83eb4fSBlue Swirl if (addr != 13 || val != 0) { 287ea026b2fSblueswir1 CUDA_DPRINTF("read: reg=0x%x val=%02x\n", (int)addr, val); 2883c83eb4fSBlue Swirl } 2893c83eb4fSBlue Swirl 290267002cdSbellard return val; 291267002cdSbellard } 292267002cdSbellard 293a8170e5eSAvi Kivity static void cuda_writeb(void *opaque, hwaddr addr, uint32_t val) 294267002cdSbellard { 295267002cdSbellard CUDAState *s = opaque; 296267002cdSbellard 297267002cdSbellard addr = (addr >> 9) & 0xf; 298ea026b2fSblueswir1 CUDA_DPRINTF("write: reg=0x%x val=%02x\n", (int)addr, val); 299267002cdSbellard 300267002cdSbellard switch(addr) { 301267002cdSbellard case 0: 302267002cdSbellard s->b = val; 303267002cdSbellard cuda_update(s); 304267002cdSbellard break; 305267002cdSbellard case 1: 306267002cdSbellard s->a = val; 307267002cdSbellard break; 308267002cdSbellard case 2: 309267002cdSbellard s->dirb = val; 310267002cdSbellard break; 311267002cdSbellard case 3: 312267002cdSbellard s->dira = val; 313267002cdSbellard break; 314267002cdSbellard case 4: 31561271e5cSbellard s->timers[0].latch = (s->timers[0].latch & 0xff00) | val; 316bc72ad67SAlex Bligh cuda_timer_update(s, &s->timers[0], qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); 317267002cdSbellard break; 318267002cdSbellard case 5: 31961271e5cSbellard s->timers[0].latch = (s->timers[0].latch & 0xff) | (val << 8); 32061271e5cSbellard s->ifr &= ~T1_INT; 32161271e5cSbellard set_counter(s, &s->timers[0], s->timers[0].latch); 322267002cdSbellard break; 323267002cdSbellard case 6: 324267002cdSbellard s->timers[0].latch = (s->timers[0].latch & 0xff00) | val; 325bc72ad67SAlex Bligh cuda_timer_update(s, &s->timers[0], qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); 326267002cdSbellard break; 327267002cdSbellard case 7: 328267002cdSbellard s->timers[0].latch = (s->timers[0].latch & 0xff) | (val << 8); 32961271e5cSbellard s->ifr &= ~T1_INT; 330bc72ad67SAlex Bligh cuda_timer_update(s, &s->timers[0], qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); 331267002cdSbellard break; 332267002cdSbellard case 8: 33361271e5cSbellard s->timers[1].latch = val; 334819e712bSbellard set_counter(s, &s->timers[1], val); 335267002cdSbellard break; 336267002cdSbellard case 9: 33761271e5cSbellard set_counter(s, &s->timers[1], (val << 8) | s->timers[1].latch); 338267002cdSbellard break; 339267002cdSbellard case 10: 340267002cdSbellard s->sr = val; 341267002cdSbellard break; 342267002cdSbellard case 11: 343267002cdSbellard s->acr = val; 344bc72ad67SAlex Bligh cuda_timer_update(s, &s->timers[0], qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); 345267002cdSbellard cuda_update(s); 346267002cdSbellard break; 347267002cdSbellard case 12: 348267002cdSbellard s->pcr = val; 349267002cdSbellard break; 350267002cdSbellard case 13: 351267002cdSbellard /* reset bits */ 352267002cdSbellard s->ifr &= ~val; 353267002cdSbellard cuda_update_irq(s); 354267002cdSbellard break; 355267002cdSbellard case 14: 356267002cdSbellard if (val & IER_SET) { 357267002cdSbellard /* set bits */ 358267002cdSbellard s->ier |= val & 0x7f; 359267002cdSbellard } else { 360267002cdSbellard /* reset bits */ 361267002cdSbellard s->ier &= ~val; 362267002cdSbellard } 363267002cdSbellard cuda_update_irq(s); 364267002cdSbellard break; 365267002cdSbellard default: 366267002cdSbellard case 15: 367267002cdSbellard s->anh = val; 368267002cdSbellard break; 369267002cdSbellard } 370267002cdSbellard } 371267002cdSbellard 372267002cdSbellard /* NOTE: TIP and TREQ are negated */ 373267002cdSbellard static void cuda_update(CUDAState *s) 374267002cdSbellard { 375819e712bSbellard int packet_received, len; 376819e712bSbellard 377819e712bSbellard packet_received = 0; 378819e712bSbellard if (!(s->b & TIP)) { 379819e712bSbellard /* transfer requested from host */ 380267002cdSbellard 381267002cdSbellard if (s->acr & SR_OUT) { 382267002cdSbellard /* data output */ 383819e712bSbellard if ((s->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) { 384267002cdSbellard if (s->data_out_index < sizeof(s->data_out)) { 385ea026b2fSblueswir1 CUDA_DPRINTF("send: %02x\n", s->sr); 386267002cdSbellard s->data_out[s->data_out_index++] = s->sr; 387819e712bSbellard s->ifr |= SR_INT; 388819e712bSbellard cuda_update_irq(s); 389819e712bSbellard } 390819e712bSbellard } 391819e712bSbellard } else { 392819e712bSbellard if (s->data_in_index < s->data_in_size) { 393819e712bSbellard /* data input */ 394819e712bSbellard if ((s->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) { 395819e712bSbellard s->sr = s->data_in[s->data_in_index++]; 396ea026b2fSblueswir1 CUDA_DPRINTF("recv: %02x\n", s->sr); 397819e712bSbellard /* indicate end of transfer */ 398819e712bSbellard if (s->data_in_index >= s->data_in_size) { 399819e712bSbellard s->b = (s->b | TREQ); 400267002cdSbellard } 401267002cdSbellard s->ifr |= SR_INT; 402267002cdSbellard cuda_update_irq(s); 403267002cdSbellard } 404267002cdSbellard } 405267002cdSbellard } 406819e712bSbellard } else { 407819e712bSbellard /* no transfer requested: handle sync case */ 408819e712bSbellard if ((s->last_b & TIP) && (s->b & TACK) != (s->last_b & TACK)) { 409819e712bSbellard /* update TREQ state each time TACK change state */ 410819e712bSbellard if (s->b & TACK) 411819e712bSbellard s->b = (s->b | TREQ); 412819e712bSbellard else 413819e712bSbellard s->b = (s->b & ~TREQ); 414819e712bSbellard s->ifr |= SR_INT; 415819e712bSbellard cuda_update_irq(s); 416819e712bSbellard } else { 417819e712bSbellard if (!(s->last_b & TIP)) { 418e91c8a77Sths /* handle end of host to cuda transfer */ 419819e712bSbellard packet_received = (s->data_out_index > 0); 420e91c8a77Sths /* always an IRQ at the end of transfer */ 421819e712bSbellard s->ifr |= SR_INT; 422819e712bSbellard cuda_update_irq(s); 423819e712bSbellard } 424819e712bSbellard /* signal if there is data to read */ 425819e712bSbellard if (s->data_in_index < s->data_in_size) { 426819e712bSbellard s->b = (s->b & ~TREQ); 427819e712bSbellard } 428819e712bSbellard } 429819e712bSbellard } 430819e712bSbellard 431267002cdSbellard s->last_acr = s->acr; 432267002cdSbellard s->last_b = s->b; 433819e712bSbellard 434819e712bSbellard /* NOTE: cuda_receive_packet_from_host() can call cuda_update() 435819e712bSbellard recursively */ 436819e712bSbellard if (packet_received) { 437819e712bSbellard len = s->data_out_index; 438819e712bSbellard s->data_out_index = 0; 439819e712bSbellard cuda_receive_packet_from_host(s, s->data_out, len); 440819e712bSbellard } 441267002cdSbellard } 442267002cdSbellard 443267002cdSbellard static void cuda_send_packet_to_host(CUDAState *s, 444267002cdSbellard const uint8_t *data, int len) 445267002cdSbellard { 446819e712bSbellard #ifdef DEBUG_CUDA_PACKET 447819e712bSbellard { 448819e712bSbellard int i; 449819e712bSbellard printf("cuda_send_packet_to_host:\n"); 450819e712bSbellard for(i = 0; i < len; i++) 451819e712bSbellard printf(" %02x", data[i]); 452819e712bSbellard printf("\n"); 453819e712bSbellard } 454819e712bSbellard #endif 455267002cdSbellard memcpy(s->data_in, data, len); 456267002cdSbellard s->data_in_size = len; 457267002cdSbellard s->data_in_index = 0; 458267002cdSbellard cuda_update(s); 459267002cdSbellard s->ifr |= SR_INT; 460267002cdSbellard cuda_update_irq(s); 461267002cdSbellard } 462267002cdSbellard 4637db4eea6Sbellard static void cuda_adb_poll(void *opaque) 464e2733d20Sbellard { 465e2733d20Sbellard CUDAState *s = opaque; 466e2733d20Sbellard uint8_t obuf[ADB_MAX_OUT_LEN + 2]; 467e2733d20Sbellard int olen; 468e2733d20Sbellard 469293c867dSAndreas Färber olen = adb_poll(&s->adb_bus, obuf + 2); 470e2733d20Sbellard if (olen > 0) { 471e2733d20Sbellard obuf[0] = ADB_PACKET; 472e2733d20Sbellard obuf[1] = 0x40; /* polled data */ 473e2733d20Sbellard cuda_send_packet_to_host(s, obuf, olen + 2); 474e2733d20Sbellard } 475bc72ad67SAlex Bligh timer_mod(s->adb_poll_timer, 476bc72ad67SAlex Bligh qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 4776ee093c9SJuan Quintela (get_ticks_per_sec() / CUDA_ADB_POLL_FREQ)); 478e2733d20Sbellard } 479e2733d20Sbellard 480267002cdSbellard static void cuda_receive_packet(CUDAState *s, 481267002cdSbellard const uint8_t *data, int len) 482267002cdSbellard { 483267002cdSbellard uint8_t obuf[16]; 4845703c174Saurel32 int autopoll; 4855703c174Saurel32 uint32_t ti; 486267002cdSbellard 487267002cdSbellard switch(data[0]) { 488267002cdSbellard case CUDA_AUTOPOLL: 489e2733d20Sbellard autopoll = (data[1] != 0); 490e2733d20Sbellard if (autopoll != s->autopoll) { 491e2733d20Sbellard s->autopoll = autopoll; 492e2733d20Sbellard if (autopoll) { 493bc72ad67SAlex Bligh timer_mod(s->adb_poll_timer, 494bc72ad67SAlex Bligh qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 4956ee093c9SJuan Quintela (get_ticks_per_sec() / CUDA_ADB_POLL_FREQ)); 496e2733d20Sbellard } else { 497bc72ad67SAlex Bligh timer_del(s->adb_poll_timer); 498e2733d20Sbellard } 499e2733d20Sbellard } 500267002cdSbellard obuf[0] = CUDA_PACKET; 501267002cdSbellard obuf[1] = data[1]; 502267002cdSbellard cuda_send_packet_to_host(s, obuf, 2); 503267002cdSbellard break; 504dccfafc4Sbellard case CUDA_SET_TIME: 5055703c174Saurel32 ti = (((uint32_t)data[1]) << 24) + (((uint32_t)data[2]) << 16) + (((uint32_t)data[3]) << 8) + data[4]; 506bc72ad67SAlex Bligh s->tick_offset = ti - (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / get_ticks_per_sec()); 5075703c174Saurel32 obuf[0] = CUDA_PACKET; 5085703c174Saurel32 obuf[1] = 0; 5095703c174Saurel32 obuf[2] = 0; 5105703c174Saurel32 cuda_send_packet_to_host(s, obuf, 3); 5115703c174Saurel32 break; 5125703c174Saurel32 case CUDA_GET_TIME: 513bc72ad67SAlex Bligh ti = s->tick_offset + (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / get_ticks_per_sec()); 514267002cdSbellard obuf[0] = CUDA_PACKET; 515267002cdSbellard obuf[1] = 0; 516267002cdSbellard obuf[2] = 0; 517267002cdSbellard obuf[3] = ti >> 24; 518267002cdSbellard obuf[4] = ti >> 16; 519267002cdSbellard obuf[5] = ti >> 8; 520267002cdSbellard obuf[6] = ti; 521267002cdSbellard cuda_send_packet_to_host(s, obuf, 7); 522267002cdSbellard break; 523267002cdSbellard case CUDA_FILE_SERVER_FLAG: 524267002cdSbellard case CUDA_SET_DEVICE_LIST: 525267002cdSbellard case CUDA_SET_AUTO_RATE: 526267002cdSbellard case CUDA_SET_POWER_MESSAGES: 527267002cdSbellard obuf[0] = CUDA_PACKET; 528267002cdSbellard obuf[1] = 0; 529267002cdSbellard cuda_send_packet_to_host(s, obuf, 2); 530267002cdSbellard break; 531d7ce296fSbellard case CUDA_POWERDOWN: 532d7ce296fSbellard obuf[0] = CUDA_PACKET; 533d7ce296fSbellard obuf[1] = 0; 534d7ce296fSbellard cuda_send_packet_to_host(s, obuf, 2); 535d7ce296fSbellard qemu_system_shutdown_request(); 536d7ce296fSbellard break; 5370686970fSj_mayer case CUDA_RESET_SYSTEM: 5380686970fSj_mayer obuf[0] = CUDA_PACKET; 5390686970fSj_mayer obuf[1] = 0; 5400686970fSj_mayer cuda_send_packet_to_host(s, obuf, 2); 5410686970fSj_mayer qemu_system_reset_request(); 5420686970fSj_mayer break; 543267002cdSbellard default: 544267002cdSbellard break; 545267002cdSbellard } 546267002cdSbellard } 547267002cdSbellard 548267002cdSbellard static void cuda_receive_packet_from_host(CUDAState *s, 549267002cdSbellard const uint8_t *data, int len) 550267002cdSbellard { 551819e712bSbellard #ifdef DEBUG_CUDA_PACKET 552819e712bSbellard { 553819e712bSbellard int i; 554cadae95fSbellard printf("cuda_receive_packet_from_host:\n"); 555819e712bSbellard for(i = 0; i < len; i++) 556819e712bSbellard printf(" %02x", data[i]); 557819e712bSbellard printf("\n"); 558819e712bSbellard } 559819e712bSbellard #endif 560267002cdSbellard switch(data[0]) { 561267002cdSbellard case ADB_PACKET: 562e2733d20Sbellard { 563*6729aa40SMark Cave-Ayland uint8_t obuf[ADB_MAX_OUT_LEN + 3]; 564e2733d20Sbellard int olen; 565293c867dSAndreas Färber olen = adb_request(&s->adb_bus, obuf + 2, data + 1, len - 1); 56638f0b147Sbellard if (olen > 0) { 567e2733d20Sbellard obuf[0] = ADB_PACKET; 568e2733d20Sbellard obuf[1] = 0x00; 569*6729aa40SMark Cave-Ayland cuda_send_packet_to_host(s, obuf, olen + 2); 570e2733d20Sbellard } else { 57138f0b147Sbellard /* error */ 572e2733d20Sbellard obuf[0] = ADB_PACKET; 57338f0b147Sbellard obuf[1] = -olen; 574*6729aa40SMark Cave-Ayland obuf[2] = data[1]; 57538f0b147Sbellard olen = 0; 576*6729aa40SMark Cave-Ayland cuda_send_packet_to_host(s, obuf, olen + 3); 577e2733d20Sbellard } 578e2733d20Sbellard } 579267002cdSbellard break; 580267002cdSbellard case CUDA_PACKET: 581267002cdSbellard cuda_receive_packet(s, data + 1, len - 1); 582267002cdSbellard break; 583267002cdSbellard } 584267002cdSbellard } 585267002cdSbellard 586a8170e5eSAvi Kivity static void cuda_writew (void *opaque, hwaddr addr, uint32_t value) 587267002cdSbellard { 588267002cdSbellard } 589267002cdSbellard 590a8170e5eSAvi Kivity static void cuda_writel (void *opaque, hwaddr addr, uint32_t value) 591267002cdSbellard { 592267002cdSbellard } 593267002cdSbellard 594a8170e5eSAvi Kivity static uint32_t cuda_readw (void *opaque, hwaddr addr) 595267002cdSbellard { 596267002cdSbellard return 0; 597267002cdSbellard } 598267002cdSbellard 599a8170e5eSAvi Kivity static uint32_t cuda_readl (void *opaque, hwaddr addr) 600267002cdSbellard { 601267002cdSbellard return 0; 602267002cdSbellard } 603267002cdSbellard 604a348f108SStefan Weil static const MemoryRegionOps cuda_ops = { 605ea0a7eb4SAlexander Graf .old_mmio = { 606ea0a7eb4SAlexander Graf .write = { 607ea0a7eb4SAlexander Graf cuda_writeb, 608ea0a7eb4SAlexander Graf cuda_writew, 609ea0a7eb4SAlexander Graf cuda_writel, 610ea0a7eb4SAlexander Graf }, 611ea0a7eb4SAlexander Graf .read = { 612ea0a7eb4SAlexander Graf cuda_readb, 613ea0a7eb4SAlexander Graf cuda_readw, 614ea0a7eb4SAlexander Graf cuda_readl, 615ea0a7eb4SAlexander Graf }, 616ea0a7eb4SAlexander Graf }, 617ea0a7eb4SAlexander Graf .endianness = DEVICE_NATIVE_ENDIAN, 618267002cdSbellard }; 619267002cdSbellard 620c0a93a9eSJuan Quintela static bool cuda_timer_exist(void *opaque, int version_id) 6219b64997fSblueswir1 { 622c0a93a9eSJuan Quintela CUDATimer *s = opaque; 623c0a93a9eSJuan Quintela 624c0a93a9eSJuan Quintela return s->timer != NULL; 6259b64997fSblueswir1 } 6269b64997fSblueswir1 627c0a93a9eSJuan Quintela static const VMStateDescription vmstate_cuda_timer = { 628c0a93a9eSJuan Quintela .name = "cuda_timer", 629c0a93a9eSJuan Quintela .version_id = 0, 630c0a93a9eSJuan Quintela .minimum_version_id = 0, 631c0a93a9eSJuan Quintela .fields = (VMStateField[]) { 632c0a93a9eSJuan Quintela VMSTATE_UINT16(latch, CUDATimer), 633c0a93a9eSJuan Quintela VMSTATE_UINT16(counter_value, CUDATimer), 634c0a93a9eSJuan Quintela VMSTATE_INT64(load_time, CUDATimer), 635c0a93a9eSJuan Quintela VMSTATE_INT64(next_irq_time, CUDATimer), 636e720677eSPaolo Bonzini VMSTATE_TIMER_PTR_TEST(timer, CUDATimer, cuda_timer_exist), 637c0a93a9eSJuan Quintela VMSTATE_END_OF_LIST() 6389b64997fSblueswir1 } 639c0a93a9eSJuan Quintela }; 6409b64997fSblueswir1 641c0a93a9eSJuan Quintela static const VMStateDescription vmstate_cuda = { 642c0a93a9eSJuan Quintela .name = "cuda", 6436cb577ddSMark Cave-Ayland .version_id = 2, 6446cb577ddSMark Cave-Ayland .minimum_version_id = 2, 645c0a93a9eSJuan Quintela .fields = (VMStateField[]) { 646c0a93a9eSJuan Quintela VMSTATE_UINT8(a, CUDAState), 647c0a93a9eSJuan Quintela VMSTATE_UINT8(b, CUDAState), 648c0a93a9eSJuan Quintela VMSTATE_UINT8(dira, CUDAState), 649c0a93a9eSJuan Quintela VMSTATE_UINT8(dirb, CUDAState), 650c0a93a9eSJuan Quintela VMSTATE_UINT8(sr, CUDAState), 651c0a93a9eSJuan Quintela VMSTATE_UINT8(acr, CUDAState), 652c0a93a9eSJuan Quintela VMSTATE_UINT8(pcr, CUDAState), 653c0a93a9eSJuan Quintela VMSTATE_UINT8(ifr, CUDAState), 654c0a93a9eSJuan Quintela VMSTATE_UINT8(ier, CUDAState), 655c0a93a9eSJuan Quintela VMSTATE_UINT8(anh, CUDAState), 656c0a93a9eSJuan Quintela VMSTATE_INT32(data_in_size, CUDAState), 657c0a93a9eSJuan Quintela VMSTATE_INT32(data_in_index, CUDAState), 658c0a93a9eSJuan Quintela VMSTATE_INT32(data_out_index, CUDAState), 659c0a93a9eSJuan Quintela VMSTATE_UINT8(autopoll, CUDAState), 660c0a93a9eSJuan Quintela VMSTATE_BUFFER(data_in, CUDAState), 661c0a93a9eSJuan Quintela VMSTATE_BUFFER(data_out, CUDAState), 662c0a93a9eSJuan Quintela VMSTATE_UINT32(tick_offset, CUDAState), 663c0a93a9eSJuan Quintela VMSTATE_STRUCT_ARRAY(timers, CUDAState, 2, 1, 664c0a93a9eSJuan Quintela vmstate_cuda_timer, CUDATimer), 6656cb577ddSMark Cave-Ayland VMSTATE_TIMER_PTR(adb_poll_timer, CUDAState), 666c0a93a9eSJuan Quintela VMSTATE_END_OF_LIST() 6679b64997fSblueswir1 } 668c0a93a9eSJuan Quintela }; 6699b64997fSblueswir1 67045fa67fbSAndreas Färber static void cuda_reset(DeviceState *dev) 6716e6b7363Sblueswir1 { 67245fa67fbSAndreas Färber CUDAState *s = CUDA(dev); 6736e6b7363Sblueswir1 6746e6b7363Sblueswir1 s->b = 0; 6756e6b7363Sblueswir1 s->a = 0; 6766e6b7363Sblueswir1 s->dirb = 0; 6776e6b7363Sblueswir1 s->dira = 0; 6786e6b7363Sblueswir1 s->sr = 0; 6796e6b7363Sblueswir1 s->acr = 0; 6806e6b7363Sblueswir1 s->pcr = 0; 6816e6b7363Sblueswir1 s->ifr = 0; 6826e6b7363Sblueswir1 s->ier = 0; 6836e6b7363Sblueswir1 // s->ier = T1_INT | SR_INT; 6846e6b7363Sblueswir1 s->anh = 0; 6856e6b7363Sblueswir1 s->data_in_size = 0; 6866e6b7363Sblueswir1 s->data_in_index = 0; 6876e6b7363Sblueswir1 s->data_out_index = 0; 6886e6b7363Sblueswir1 s->autopoll = 0; 6896e6b7363Sblueswir1 6906e6b7363Sblueswir1 s->timers[0].latch = 0xffff; 6916e6b7363Sblueswir1 set_counter(s, &s->timers[0], 0xffff); 6926e6b7363Sblueswir1 6936e6b7363Sblueswir1 s->timers[1].latch = 0; 6946e6b7363Sblueswir1 set_counter(s, &s->timers[1], 0xffff); 6956e6b7363Sblueswir1 } 6966e6b7363Sblueswir1 69745fa67fbSAndreas Färber static void cuda_realizefn(DeviceState *dev, Error **errp) 698267002cdSbellard { 69945fa67fbSAndreas Färber CUDAState *s = CUDA(dev); 7005703c174Saurel32 struct tm tm; 701267002cdSbellard 702bc72ad67SAlex Bligh s->timers[0].timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_timer1, s); 703b981289cSAlexander Graf s->timers[0].frequency = s->frequency; 704b981289cSAlexander Graf s->timers[1].frequency = s->frequency; 70561271e5cSbellard 7069c554c1cSaurel32 qemu_get_timedate(&tm, 0); 7079c554c1cSaurel32 s->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET; 7085703c174Saurel32 709bc72ad67SAlex Bligh s->adb_poll_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_adb_poll, s); 710267002cdSbellard } 71145fa67fbSAndreas Färber 71245fa67fbSAndreas Färber static void cuda_initfn(Object *obj) 71345fa67fbSAndreas Färber { 71445fa67fbSAndreas Färber SysBusDevice *d = SYS_BUS_DEVICE(obj); 71545fa67fbSAndreas Färber CUDAState *s = CUDA(obj); 71645fa67fbSAndreas Färber int i; 71745fa67fbSAndreas Färber 71881e0ab48SPaolo Bonzini memory_region_init_io(&s->mem, obj, &cuda_ops, s, "cuda", 0x2000); 71945fa67fbSAndreas Färber sysbus_init_mmio(d, &s->mem); 72045fa67fbSAndreas Färber sysbus_init_irq(d, &s->irq); 72145fa67fbSAndreas Färber 72245fa67fbSAndreas Färber for (i = 0; i < ARRAY_SIZE(s->timers); i++) { 72345fa67fbSAndreas Färber s->timers[i].index = i; 72445fa67fbSAndreas Färber } 72584ede329SAndreas Färber 726fb17dfe0SAndreas Färber qbus_create_inplace(&s->adb_bus, sizeof(s->adb_bus), TYPE_ADB_BUS, 727fb17dfe0SAndreas Färber DEVICE(obj), "adb.0"); 72845fa67fbSAndreas Färber } 72945fa67fbSAndreas Färber 730b981289cSAlexander Graf static Property cuda_properties[] = { 731b981289cSAlexander Graf DEFINE_PROP_UINT64("frequency", CUDAState, frequency, 0), 732b981289cSAlexander Graf DEFINE_PROP_END_OF_LIST() 733b981289cSAlexander Graf }; 734b981289cSAlexander Graf 73545fa67fbSAndreas Färber static void cuda_class_init(ObjectClass *oc, void *data) 73645fa67fbSAndreas Färber { 73745fa67fbSAndreas Färber DeviceClass *dc = DEVICE_CLASS(oc); 73845fa67fbSAndreas Färber 73945fa67fbSAndreas Färber dc->realize = cuda_realizefn; 74045fa67fbSAndreas Färber dc->reset = cuda_reset; 74145fa67fbSAndreas Färber dc->vmsd = &vmstate_cuda; 742b981289cSAlexander Graf dc->props = cuda_properties; 743599d7326SLaurent Vivier set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); 74445fa67fbSAndreas Färber } 74545fa67fbSAndreas Färber 74645fa67fbSAndreas Färber static const TypeInfo cuda_type_info = { 74745fa67fbSAndreas Färber .name = TYPE_CUDA, 74845fa67fbSAndreas Färber .parent = TYPE_SYS_BUS_DEVICE, 74945fa67fbSAndreas Färber .instance_size = sizeof(CUDAState), 75045fa67fbSAndreas Färber .instance_init = cuda_initfn, 75145fa67fbSAndreas Färber .class_init = cuda_class_init, 75245fa67fbSAndreas Färber }; 75345fa67fbSAndreas Färber 75445fa67fbSAndreas Färber static void cuda_register_types(void) 75545fa67fbSAndreas Färber { 75645fa67fbSAndreas Färber type_register_static(&cuda_type_info); 75745fa67fbSAndreas Färber } 75845fa67fbSAndreas Färber 75945fa67fbSAndreas Färber type_init(cuda_register_types) 760