qemu/hw/misc/macio/cuda.c
<<
>>
Prefs
   1/*
   2 * QEMU PowerMac CUDA device support
   3 *
   4 * Copyright (c) 2004-2007 Fabrice Bellard
   5 * Copyright (c) 2007 Jocelyn Mayer
   6 *
   7 * Permission is hereby granted, free of charge, to any person obtaining a copy
   8 * of this software and associated documentation files (the "Software"), to deal
   9 * in the Software without restriction, including without limitation the rights
  10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11 * copies of the Software, and to permit persons to whom the Software is
  12 * furnished to do so, subject to the following conditions:
  13 *
  14 * The above copyright notice and this permission notice shall be included in
  15 * all copies or substantial portions of the Software.
  16 *
  17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23 * THE SOFTWARE.
  24 */
  25
  26#include "qemu/osdep.h"
  27#include "hw/irq.h"
  28#include "hw/qdev-properties.h"
  29#include "migration/vmstate.h"
  30#include "hw/misc/macio/cuda.h"
  31#include "qemu/timer.h"
  32#include "sysemu/runstate.h"
  33#include "sysemu/rtc.h"
  34#include "qapi/error.h"
  35#include "qemu/cutils.h"
  36#include "qemu/log.h"
  37#include "qemu/module.h"
  38#include "trace.h"
  39
  40/* Bits in B data register: all active low */
  41#define TREQ            0x08    /* Transfer request (input) */
  42#define TACK            0x10    /* Transfer acknowledge (output) */
  43#define TIP             0x20    /* Transfer in progress (output) */
  44
  45/* commands (1st byte) */
  46#define ADB_PACKET      0
  47#define CUDA_PACKET     1
  48#define ERROR_PACKET    2
  49#define TIMER_PACKET    3
  50#define POWER_PACKET    4
  51#define MACIIC_PACKET   5
  52#define PMU_PACKET      6
  53
  54#define CUDA_TIMER_FREQ (4700000 / 6)
  55
  56/* CUDA returns time_t's offset from Jan 1, 1904, not 1970 */
  57#define RTC_OFFSET                      2082844800
  58
  59static void cuda_receive_packet_from_host(CUDAState *s,
  60                                          const uint8_t *data, int len);
  61
  62/* MacOS uses timer 1 for calibration on startup, so we use
  63 * the timebase frequency and cuda_get_counter_value() with
  64 * cuda_get_load_time() to steer MacOS to calculate calibrate its timers
  65 * correctly for both TCG and KVM (see commit b981289c49 "PPC: Cuda: Use cuda
  66 * timer to expose tbfreq to guest" for more information) */
  67
  68static uint64_t cuda_get_counter_value(MOS6522State *s, MOS6522Timer *ti)
  69{
  70    MOS6522CUDAState *mcs = container_of(s, MOS6522CUDAState, parent_obj);
  71    CUDAState *cs = container_of(mcs, CUDAState, mos6522_cuda);
  72
  73    /* Reverse of the tb calculation algorithm that Mac OS X uses on bootup */
  74    uint64_t tb_diff = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
  75                                cs->tb_frequency, NANOSECONDS_PER_SECOND) -
  76                           ti->load_time;
  77
  78    return (tb_diff * 0xBF401675E5DULL) / (cs->tb_frequency << 24);
  79}
  80
  81static uint64_t cuda_get_load_time(MOS6522State *s, MOS6522Timer *ti)
  82{
  83    MOS6522CUDAState *mcs = container_of(s, MOS6522CUDAState, parent_obj);
  84    CUDAState *cs = container_of(mcs, CUDAState, mos6522_cuda);
  85
  86    uint64_t load_time = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
  87                                  cs->tb_frequency, NANOSECONDS_PER_SECOND);
  88    return load_time;
  89}
  90
  91static void cuda_set_sr_int(void *opaque)
  92{
  93    CUDAState *s = opaque;
  94    MOS6522CUDAState *mcs = &s->mos6522_cuda;
  95    MOS6522State *ms = MOS6522(mcs);
  96    qemu_irq irq = qdev_get_gpio_in(DEVICE(ms), SR_INT_BIT);
  97
  98    qemu_set_irq(irq, 1);
  99}
 100
 101static void cuda_delay_set_sr_int(CUDAState *s)
 102{
 103    int64_t expire;
 104
 105    trace_cuda_delay_set_sr_int();
 106
 107    expire = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->sr_delay_ns;
 108    timer_mod(s->sr_delay_timer, expire);
 109}
 110
 111/* NOTE: TIP and TREQ are negated */
 112static void cuda_update(CUDAState *s)
 113{
 114    MOS6522CUDAState *mcs = &s->mos6522_cuda;
 115    MOS6522State *ms = MOS6522(mcs);
 116    ADBBusState *adb_bus = &s->adb_bus;
 117    int packet_received, len;
 118
 119    packet_received = 0;
 120    if (!(ms->b & TIP)) {
 121        /* transfer requested from host */
 122
 123        if (ms->acr & SR_OUT) {
 124            /* data output */
 125            if ((ms->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) {
 126                if (s->data_out_index < sizeof(s->data_out)) {
 127                    if (s->data_out_index == 0) {
 128                        adb_autopoll_block(adb_bus);
 129                    }
 130                    trace_cuda_data_send(ms->sr);
 131                    s->data_out[s->data_out_index++] = ms->sr;
 132                    cuda_delay_set_sr_int(s);
 133                }
 134            }
 135        } else {
 136            if (s->data_in_index < s->data_in_size) {
 137                /* data input */
 138                if ((ms->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) {
 139                    ms->sr = s->data_in[s->data_in_index++];
 140                    trace_cuda_data_recv(ms->sr);
 141                    /* indicate end of transfer */
 142                    if (s->data_in_index >= s->data_in_size) {
 143                        ms->b = (ms->b | TREQ);
 144                        adb_autopoll_unblock(adb_bus);
 145                    }
 146                    cuda_delay_set_sr_int(s);
 147                }
 148            }
 149        }
 150    } else {
 151        /* no transfer requested: handle sync case */
 152        if ((s->last_b & TIP) && (ms->b & TACK) != (s->last_b & TACK)) {
 153            /* update TREQ state each time TACK change state */
 154            if (ms->b & TACK) {
 155                ms->b = (ms->b | TREQ);
 156            } else {
 157                ms->b = (ms->b & ~TREQ);
 158            }
 159            cuda_delay_set_sr_int(s);
 160        } else {
 161            if (!(s->last_b & TIP)) {
 162                /* handle end of host to cuda transfer */
 163                packet_received = (s->data_out_index > 0);
 164                /* always an IRQ at the end of transfer */
 165                cuda_delay_set_sr_int(s);
 166            }
 167            /* signal if there is data to read */
 168            if (s->data_in_index < s->data_in_size) {
 169                ms->b = (ms->b & ~TREQ);
 170            }
 171        }
 172    }
 173
 174    s->last_acr = ms->acr;
 175    s->last_b = ms->b;
 176
 177    /* NOTE: cuda_receive_packet_from_host() can call cuda_update()
 178       recursively */
 179    if (packet_received) {
 180        len = s->data_out_index;
 181        s->data_out_index = 0;
 182        cuda_receive_packet_from_host(s, s->data_out, len);
 183    }
 184}
 185
 186static void cuda_send_packet_to_host(CUDAState *s,
 187                                     const uint8_t *data, int len)
 188{
 189    int i;
 190
 191    trace_cuda_packet_send(len);
 192    for (i = 0; i < len; i++) {
 193        trace_cuda_packet_send_data(i, data[i]);
 194    }
 195
 196    memcpy(s->data_in, data, len);
 197    s->data_in_size = len;
 198    s->data_in_index = 0;
 199    cuda_update(s);
 200    cuda_delay_set_sr_int(s);
 201}
 202
 203static void cuda_adb_poll(void *opaque)
 204{
 205    CUDAState *s = opaque;
 206    ADBBusState *adb_bus = &s->adb_bus;
 207    uint8_t obuf[ADB_MAX_OUT_LEN + 2];
 208    int olen;
 209
 210    olen = adb_poll(adb_bus, obuf + 2, adb_bus->autopoll_mask);
 211    if (olen > 0) {
 212        obuf[0] = ADB_PACKET;
 213        obuf[1] = 0x40; /* polled data */
 214        cuda_send_packet_to_host(s, obuf, olen + 2);
 215    }
 216}
 217
 218/* description of commands */
 219typedef struct CudaCommand {
 220    uint8_t command;
 221    const char *name;
 222    bool (*handler)(CUDAState *s,
 223                    const uint8_t *in_args, int in_len,
 224                    uint8_t *out_args, int *out_len);
 225} CudaCommand;
 226
 227static bool cuda_cmd_autopoll(CUDAState *s,
 228                              const uint8_t *in_data, int in_len,
 229                              uint8_t *out_data, int *out_len)
 230{
 231    ADBBusState *adb_bus = &s->adb_bus;
 232    bool autopoll;
 233
 234    if (in_len != 1) {
 235        return false;
 236    }
 237
 238    autopoll = (in_data[0] != 0) ? true : false;
 239
 240    adb_set_autopoll_enabled(adb_bus, autopoll);
 241    return true;
 242}
 243
 244static bool cuda_cmd_set_autorate(CUDAState *s,
 245                                  const uint8_t *in_data, int in_len,
 246                                  uint8_t *out_data, int *out_len)
 247{
 248    ADBBusState *adb_bus = &s->adb_bus;
 249
 250    if (in_len != 1) {
 251        return false;
 252    }
 253
 254    /* we don't want a period of 0 ms */
 255    /* FIXME: check what real hardware does */
 256    if (in_data[0] == 0) {
 257        return false;
 258    }
 259
 260    adb_set_autopoll_rate_ms(adb_bus, in_data[0]);
 261    return true;
 262}
 263
 264static bool cuda_cmd_set_device_list(CUDAState *s,
 265                                     const uint8_t *in_data, int in_len,
 266                                     uint8_t *out_data, int *out_len)
 267{
 268    ADBBusState *adb_bus = &s->adb_bus;
 269    uint16_t mask;
 270
 271    if (in_len != 2) {
 272        return false;
 273    }
 274
 275    mask = (((uint16_t)in_data[0]) << 8) | in_data[1];
 276
 277    adb_set_autopoll_mask(adb_bus, mask);
 278    return true;
 279}
 280
 281static bool cuda_cmd_powerdown(CUDAState *s,
 282                               const uint8_t *in_data, int in_len,
 283                               uint8_t *out_data, int *out_len)
 284{
 285    if (in_len != 0) {
 286        return false;
 287    }
 288
 289    qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
 290    return true;
 291}
 292
 293static bool cuda_cmd_reset_system(CUDAState *s,
 294                                  const uint8_t *in_data, int in_len,
 295                                  uint8_t *out_data, int *out_len)
 296{
 297    if (in_len != 0) {
 298        return false;
 299    }
 300
 301    qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
 302    return true;
 303}
 304
 305static bool cuda_cmd_set_file_server_flag(CUDAState *s,
 306                                          const uint8_t *in_data, int in_len,
 307                                          uint8_t *out_data, int *out_len)
 308{
 309    if (in_len != 1) {
 310        return false;
 311    }
 312
 313    qemu_log_mask(LOG_UNIMP,
 314                  "CUDA: unimplemented command FILE_SERVER_FLAG %d\n",
 315                  in_data[0]);
 316    return true;
 317}
 318
 319static bool cuda_cmd_set_power_message(CUDAState *s,
 320                                       const uint8_t *in_data, int in_len,
 321                                       uint8_t *out_data, int *out_len)
 322{
 323    if (in_len != 1) {
 324        return false;
 325    }
 326
 327    qemu_log_mask(LOG_UNIMP,
 328                  "CUDA: unimplemented command SET_POWER_MESSAGE %d\n",
 329                  in_data[0]);
 330    return true;
 331}
 332
 333static bool cuda_cmd_get_time(CUDAState *s,
 334                              const uint8_t *in_data, int in_len,
 335                              uint8_t *out_data, int *out_len)
 336{
 337    uint32_t ti;
 338
 339    if (in_len != 0) {
 340        return false;
 341    }
 342
 343    ti = s->tick_offset + (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)
 344                           / NANOSECONDS_PER_SECOND);
 345    out_data[0] = ti >> 24;
 346    out_data[1] = ti >> 16;
 347    out_data[2] = ti >> 8;
 348    out_data[3] = ti;
 349    *out_len = 4;
 350    return true;
 351}
 352
 353static bool cuda_cmd_set_time(CUDAState *s,
 354                              const uint8_t *in_data, int in_len,
 355                              uint8_t *out_data, int *out_len)
 356{
 357    uint32_t ti;
 358
 359    if (in_len != 4) {
 360        return false;
 361    }
 362
 363    ti = (((uint32_t)in_data[0]) << 24) + (((uint32_t)in_data[1]) << 16)
 364         + (((uint32_t)in_data[2]) << 8) + in_data[3];
 365    s->tick_offset = ti - (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)
 366                           / NANOSECONDS_PER_SECOND);
 367    return true;
 368}
 369
 370static const CudaCommand handlers[] = {
 371    { CUDA_AUTOPOLL, "AUTOPOLL", cuda_cmd_autopoll },
 372    { CUDA_SET_AUTO_RATE, "SET_AUTO_RATE",  cuda_cmd_set_autorate },
 373    { CUDA_SET_DEVICE_LIST, "SET_DEVICE_LIST", cuda_cmd_set_device_list },
 374    { CUDA_POWERDOWN, "POWERDOWN", cuda_cmd_powerdown },
 375    { CUDA_RESET_SYSTEM, "RESET_SYSTEM", cuda_cmd_reset_system },
 376    { CUDA_FILE_SERVER_FLAG, "FILE_SERVER_FLAG",
 377      cuda_cmd_set_file_server_flag },
 378    { CUDA_SET_POWER_MESSAGES, "SET_POWER_MESSAGES",
 379      cuda_cmd_set_power_message },
 380    { CUDA_GET_TIME, "GET_TIME", cuda_cmd_get_time },
 381    { CUDA_SET_TIME, "SET_TIME", cuda_cmd_set_time },
 382};
 383
 384static void cuda_receive_packet(CUDAState *s,
 385                                const uint8_t *data, int len)
 386{
 387    uint8_t obuf[16] = { CUDA_PACKET, 0, data[0] };
 388    int i, out_len = 0;
 389
 390    for (i = 0; i < ARRAY_SIZE(handlers); i++) {
 391        const CudaCommand *desc = &handlers[i];
 392        if (desc->command == data[0]) {
 393            trace_cuda_receive_packet_cmd(desc->name);
 394            out_len = 0;
 395            if (desc->handler(s, data + 1, len - 1, obuf + 3, &out_len)) {
 396                cuda_send_packet_to_host(s, obuf, 3 + out_len);
 397            } else {
 398                qemu_log_mask(LOG_GUEST_ERROR,
 399                              "CUDA: %s: wrong parameters %d\n",
 400                              desc->name, len);
 401                obuf[0] = ERROR_PACKET;
 402                obuf[1] = 0x5; /* bad parameters */
 403                obuf[2] = CUDA_PACKET;
 404                obuf[3] = data[0];
 405                cuda_send_packet_to_host(s, obuf, 4);
 406            }
 407            return;
 408        }
 409    }
 410
 411    qemu_log_mask(LOG_GUEST_ERROR, "CUDA: unknown command 0x%02x\n", data[0]);
 412    obuf[0] = ERROR_PACKET;
 413    obuf[1] = 0x2; /* unknown command */
 414    obuf[2] = CUDA_PACKET;
 415    obuf[3] = data[0];
 416    cuda_send_packet_to_host(s, obuf, 4);
 417}
 418
 419static void cuda_receive_packet_from_host(CUDAState *s,
 420                                          const uint8_t *data, int len)
 421{
 422    int i;
 423
 424    trace_cuda_packet_receive(len);
 425    for (i = 0; i < len; i++) {
 426        trace_cuda_packet_receive_data(i, data[i]);
 427    }
 428
 429    switch(data[0]) {
 430    case ADB_PACKET:
 431        {
 432            uint8_t obuf[ADB_MAX_OUT_LEN + 3];
 433            int olen;
 434            olen = adb_request(&s->adb_bus, obuf + 2, data + 1, len - 1);
 435            if (olen > 0) {
 436                obuf[0] = ADB_PACKET;
 437                obuf[1] = 0x00;
 438                cuda_send_packet_to_host(s, obuf, olen + 2);
 439            } else {
 440                /* error */
 441                obuf[0] = ADB_PACKET;
 442                obuf[1] = -olen;
 443                obuf[2] = data[1];
 444                olen = 0;
 445                cuda_send_packet_to_host(s, obuf, olen + 3);
 446            }
 447        }
 448        break;
 449    case CUDA_PACKET:
 450        cuda_receive_packet(s, data + 1, len - 1);
 451        break;
 452    }
 453}
 454
 455static uint64_t mos6522_cuda_read(void *opaque, hwaddr addr, unsigned size)
 456{
 457    CUDAState *s = opaque;
 458    MOS6522CUDAState *mcs = &s->mos6522_cuda;
 459    MOS6522State *ms = MOS6522(mcs);
 460
 461    addr = (addr >> 9) & 0xf;
 462    return mos6522_read(ms, addr, size);
 463}
 464
 465static void mos6522_cuda_write(void *opaque, hwaddr addr, uint64_t val,
 466                               unsigned size)
 467{
 468    CUDAState *s = opaque;
 469    MOS6522CUDAState *mcs = &s->mos6522_cuda;
 470    MOS6522State *ms = MOS6522(mcs);
 471
 472    addr = (addr >> 9) & 0xf;
 473    mos6522_write(ms, addr, val, size);
 474}
 475
 476static const MemoryRegionOps mos6522_cuda_ops = {
 477    .read = mos6522_cuda_read,
 478    .write = mos6522_cuda_write,
 479    .endianness = DEVICE_BIG_ENDIAN,
 480    .valid = {
 481        .min_access_size = 1,
 482        .max_access_size = 1,
 483    },
 484};
 485
 486static const VMStateDescription vmstate_cuda = {
 487    .name = "cuda",
 488    .version_id = 6,
 489    .minimum_version_id = 6,
 490    .fields = (VMStateField[]) {
 491        VMSTATE_STRUCT(mos6522_cuda.parent_obj, CUDAState, 0, vmstate_mos6522,
 492                       MOS6522State),
 493        VMSTATE_UINT8(last_b, CUDAState),
 494        VMSTATE_UINT8(last_acr, CUDAState),
 495        VMSTATE_INT32(data_in_size, CUDAState),
 496        VMSTATE_INT32(data_in_index, CUDAState),
 497        VMSTATE_INT32(data_out_index, CUDAState),
 498        VMSTATE_BUFFER(data_in, CUDAState),
 499        VMSTATE_BUFFER(data_out, CUDAState),
 500        VMSTATE_UINT32(tick_offset, CUDAState),
 501        VMSTATE_TIMER_PTR(sr_delay_timer, CUDAState),
 502        VMSTATE_END_OF_LIST()
 503    }
 504};
 505
 506static void cuda_reset(DeviceState *dev)
 507{
 508    CUDAState *s = CUDA(dev);
 509    ADBBusState *adb_bus = &s->adb_bus;
 510
 511    s->data_in_size = 0;
 512    s->data_in_index = 0;
 513    s->data_out_index = 0;
 514
 515    adb_set_autopoll_enabled(adb_bus, false);
 516}
 517
 518static void cuda_realize(DeviceState *dev, Error **errp)
 519{
 520    CUDAState *s = CUDA(dev);
 521    SysBusDevice *sbd;
 522    ADBBusState *adb_bus = &s->adb_bus;
 523    struct tm tm;
 524
 525    if (!sysbus_realize(SYS_BUS_DEVICE(&s->mos6522_cuda), errp)) {
 526        return;
 527    }
 528
 529    /* Pass IRQ from 6522 */
 530    sbd = SYS_BUS_DEVICE(s);
 531    sysbus_pass_irq(sbd, SYS_BUS_DEVICE(&s->mos6522_cuda));
 532
 533    qemu_get_timedate(&tm, 0);
 534    s->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET;
 535
 536    s->sr_delay_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_set_sr_int, s);
 537    s->sr_delay_ns = 20 * SCALE_US;
 538
 539    adb_register_autopoll_callback(adb_bus, cuda_adb_poll, s);
 540}
 541
 542static void cuda_init(Object *obj)
 543{
 544    CUDAState *s = CUDA(obj);
 545    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 546
 547    object_initialize_child(obj, "mos6522-cuda", &s->mos6522_cuda,
 548                            TYPE_MOS6522_CUDA);
 549
 550    memory_region_init_io(&s->mem, obj, &mos6522_cuda_ops, s, "cuda", 0x2000);
 551    sysbus_init_mmio(sbd, &s->mem);
 552
 553    qbus_init(&s->adb_bus, sizeof(s->adb_bus), TYPE_ADB_BUS,
 554              DEVICE(obj), "adb.0");
 555}
 556
 557static Property cuda_properties[] = {
 558    DEFINE_PROP_UINT64("timebase-frequency", CUDAState, tb_frequency, 0),
 559    DEFINE_PROP_END_OF_LIST()
 560};
 561
 562static void cuda_class_init(ObjectClass *oc, void *data)
 563{
 564    DeviceClass *dc = DEVICE_CLASS(oc);
 565
 566    dc->realize = cuda_realize;
 567    dc->reset = cuda_reset;
 568    dc->vmsd = &vmstate_cuda;
 569    device_class_set_props(dc, cuda_properties);
 570    set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
 571}
 572
 573static const TypeInfo cuda_type_info = {
 574    .name = TYPE_CUDA,
 575    .parent = TYPE_SYS_BUS_DEVICE,
 576    .instance_size = sizeof(CUDAState),
 577    .instance_init = cuda_init,
 578    .class_init = cuda_class_init,
 579};
 580
 581static void mos6522_cuda_portB_write(MOS6522State *s)
 582{
 583    MOS6522CUDAState *mcs = container_of(s, MOS6522CUDAState, parent_obj);
 584    CUDAState *cs = container_of(mcs, CUDAState, mos6522_cuda);
 585
 586    cuda_update(cs);
 587}
 588
 589static void mos6522_cuda_reset_hold(Object *obj)
 590{
 591    MOS6522State *ms = MOS6522(obj);
 592    MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(ms);
 593
 594    if (mdc->parent_phases.hold) {
 595        mdc->parent_phases.hold(obj);
 596    }
 597
 598    ms->timers[0].frequency = CUDA_TIMER_FREQ;
 599    ms->timers[1].frequency = (SCALE_US * 6000) / 4700;
 600}
 601
 602static void mos6522_cuda_class_init(ObjectClass *oc, void *data)
 603{
 604    ResettableClass *rc = RESETTABLE_CLASS(oc);
 605    MOS6522DeviceClass *mdc = MOS6522_CLASS(oc);
 606
 607    resettable_class_set_parent_phases(rc, NULL, mos6522_cuda_reset_hold,
 608                                       NULL, &mdc->parent_phases);
 609    mdc->portB_write = mos6522_cuda_portB_write;
 610    mdc->get_timer1_counter_value = cuda_get_counter_value;
 611    mdc->get_timer2_counter_value = cuda_get_counter_value;
 612    mdc->get_timer1_load_time = cuda_get_load_time;
 613    mdc->get_timer2_load_time = cuda_get_load_time;
 614}
 615
 616static const TypeInfo mos6522_cuda_type_info = {
 617    .name = TYPE_MOS6522_CUDA,
 618    .parent = TYPE_MOS6522,
 619    .instance_size = sizeof(MOS6522CUDAState),
 620    .class_init = mos6522_cuda_class_init,
 621};
 622
 623static void cuda_register_types(void)
 624{
 625    type_register_static(&mos6522_cuda_type_info);
 626    type_register_static(&cuda_type_info);
 627}
 628
 629type_init(cuda_register_types)
 630