xref: /openbmc/qemu/hw/misc/macio/cuda.c (revision 09a573474ba0bb83df8159d0f24c0f209734da33)
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 #include "qemu/osdep.h"
26 #include "hw/hw.h"
27 #include "hw/ppc/mac.h"
28 #include "hw/input/adb.h"
29 #include "hw/misc/mos6522.h"
30 #include "qemu/timer.h"
31 #include "sysemu/sysemu.h"
32 #include "qemu/cutils.h"
33 #include "qemu/log.h"
34 
35 /* debug CUDA packets */
36 //#define DEBUG_CUDA_PACKET
37 
38 /* debug CUDA packets */
39 //#define DEBUG_CUDA_PACKET
40 
41 #ifdef DEBUG_CUDA
42 #define CUDA_DPRINTF(fmt, ...)                                  \
43     do { printf("CUDA: " fmt , ## __VA_ARGS__); } while (0)
44 #else
45 #define CUDA_DPRINTF(fmt, ...)
46 #endif
47 
48 /* Bits in B data register: all active low */
49 #define TREQ            0x08    /* Transfer request (input) */
50 #define TACK            0x10    /* Transfer acknowledge (output) */
51 #define TIP             0x20    /* Transfer in progress (output) */
52 
53 /* commands (1st byte) */
54 #define ADB_PACKET      0
55 #define CUDA_PACKET     1
56 #define ERROR_PACKET    2
57 #define TIMER_PACKET    3
58 #define POWER_PACKET    4
59 #define MACIIC_PACKET   5
60 #define PMU_PACKET      6
61 
62 #define CUDA_TIMER_FREQ (4700000 / 6)
63 
64 /* CUDA returns time_t's offset from Jan 1, 1904, not 1970 */
65 #define RTC_OFFSET                      2082844800
66 
67 static void cuda_receive_packet_from_host(CUDAState *s,
68                                           const uint8_t *data, int len);
69 
70 /* MacOS uses timer 1 for calibration on startup, so we use
71  * the timebase frequency and cuda_get_counter_value() with
72  * cuda_get_load_time() to steer MacOS to calculate calibrate its timers
73  * correctly for both TCG and KVM (see commit b981289c49 "PPC: Cuda: Use cuda
74  * timer to expose tbfreq to guest" for more information) */
75 
76 static uint64_t cuda_get_counter_value(MOS6522State *s, MOS6522Timer *ti)
77 {
78     MOS6522CUDAState *mcs = container_of(s, MOS6522CUDAState, parent_obj);
79     CUDAState *cs = mcs->cuda;
80 
81     /* Reverse of the tb calculation algorithm that Mac OS X uses on bootup */
82     uint64_t tb_diff = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
83                                 cs->tb_frequency, NANOSECONDS_PER_SECOND) -
84                            ti->load_time;
85 
86     return (tb_diff * 0xBF401675E5DULL) / (cs->tb_frequency << 24);
87 }
88 
89 static uint64_t cuda_get_load_time(MOS6522State *s, MOS6522Timer *ti)
90 {
91     MOS6522CUDAState *mcs = container_of(s, MOS6522CUDAState, parent_obj);
92     CUDAState *cs = mcs->cuda;
93 
94     uint64_t load_time = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
95                                   cs->tb_frequency, NANOSECONDS_PER_SECOND);
96     return load_time;
97 }
98 
99 static void cuda_set_sr_int(void *opaque)
100 {
101     CUDAState *s = opaque;
102     MOS6522CUDAState *mcs = s->mos6522_cuda;
103     MOS6522State *ms = MOS6522(mcs);
104     MOS6522DeviceClass *mdc = MOS6522_DEVICE_GET_CLASS(ms);
105 
106     mdc->set_sr_int(ms);
107 }
108 
109 static void cuda_delay_set_sr_int(CUDAState *s)
110 {
111     MOS6522CUDAState *mcs = s->mos6522_cuda;
112     MOS6522State *ms = MOS6522(mcs);
113     MOS6522DeviceClass *mdc = MOS6522_DEVICE_GET_CLASS(ms);
114     int64_t expire;
115 
116     if (ms->dirb == 0xff || s->sr_delay_ns == 0) {
117         /* Disabled or not in Mac OS, fire the IRQ directly */
118         mdc->set_sr_int(ms);
119         return;
120     }
121 
122     CUDA_DPRINTF("CUDA: %s:%d\n", __func__, __LINE__);
123 
124     expire = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->sr_delay_ns;
125     timer_mod(s->sr_delay_timer, expire);
126 }
127 
128 /* NOTE: TIP and TREQ are negated */
129 static void cuda_update(CUDAState *s)
130 {
131     MOS6522CUDAState *mcs = s->mos6522_cuda;
132     MOS6522State *ms = MOS6522(mcs);
133     int packet_received, len;
134 
135     packet_received = 0;
136     if (!(ms->b & TIP)) {
137         /* transfer requested from host */
138 
139         if (ms->acr & SR_OUT) {
140             /* data output */
141             if ((ms->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) {
142                 if (s->data_out_index < sizeof(s->data_out)) {
143                     CUDA_DPRINTF("send: %02x\n", ms->sr);
144                     s->data_out[s->data_out_index++] = ms->sr;
145                     cuda_delay_set_sr_int(s);
146                 }
147             }
148         } else {
149             if (s->data_in_index < s->data_in_size) {
150                 /* data input */
151                 if ((ms->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) {
152                     ms->sr = s->data_in[s->data_in_index++];
153                     CUDA_DPRINTF("recv: %02x\n", ms->sr);
154                     /* indicate end of transfer */
155                     if (s->data_in_index >= s->data_in_size) {
156                         ms->b = (ms->b | TREQ);
157                     }
158                     cuda_delay_set_sr_int(s);
159                 }
160             }
161         }
162     } else {
163         /* no transfer requested: handle sync case */
164         if ((s->last_b & TIP) && (ms->b & TACK) != (s->last_b & TACK)) {
165             /* update TREQ state each time TACK change state */
166             if (ms->b & TACK) {
167                 ms->b = (ms->b | TREQ);
168             } else {
169                 ms->b = (ms->b & ~TREQ);
170             }
171             cuda_delay_set_sr_int(s);
172         } else {
173             if (!(s->last_b & TIP)) {
174                 /* handle end of host to cuda transfer */
175                 packet_received = (s->data_out_index > 0);
176                 /* always an IRQ at the end of transfer */
177                 cuda_delay_set_sr_int(s);
178             }
179             /* signal if there is data to read */
180             if (s->data_in_index < s->data_in_size) {
181                 ms->b = (ms->b & ~TREQ);
182             }
183         }
184     }
185 
186     s->last_acr = ms->acr;
187     s->last_b = ms->b;
188 
189     /* NOTE: cuda_receive_packet_from_host() can call cuda_update()
190        recursively */
191     if (packet_received) {
192         len = s->data_out_index;
193         s->data_out_index = 0;
194         cuda_receive_packet_from_host(s, s->data_out, len);
195     }
196 }
197 
198 static void cuda_send_packet_to_host(CUDAState *s,
199                                      const uint8_t *data, int len)
200 {
201 #ifdef DEBUG_CUDA_PACKET
202     {
203         int i;
204         printf("cuda_send_packet_to_host:\n");
205         for(i = 0; i < len; i++)
206             printf(" %02x", data[i]);
207         printf("\n");
208     }
209 #endif
210     memcpy(s->data_in, data, len);
211     s->data_in_size = len;
212     s->data_in_index = 0;
213     cuda_update(s);
214     cuda_delay_set_sr_int(s);
215 }
216 
217 static void cuda_adb_poll(void *opaque)
218 {
219     CUDAState *s = opaque;
220     uint8_t obuf[ADB_MAX_OUT_LEN + 2];
221     int olen;
222 
223     olen = adb_poll(&s->adb_bus, obuf + 2, s->adb_poll_mask);
224     if (olen > 0) {
225         obuf[0] = ADB_PACKET;
226         obuf[1] = 0x40; /* polled data */
227         cuda_send_packet_to_host(s, obuf, olen + 2);
228     }
229     timer_mod(s->adb_poll_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
230               (NANOSECONDS_PER_SECOND / (1000 / s->autopoll_rate_ms)));
231 }
232 
233 /* description of commands */
234 typedef struct CudaCommand {
235     uint8_t command;
236     const char *name;
237     bool (*handler)(CUDAState *s,
238                     const uint8_t *in_args, int in_len,
239                     uint8_t *out_args, int *out_len);
240 } CudaCommand;
241 
242 static bool cuda_cmd_autopoll(CUDAState *s,
243                               const uint8_t *in_data, int in_len,
244                               uint8_t *out_data, int *out_len)
245 {
246     int autopoll;
247 
248     if (in_len != 1) {
249         return false;
250     }
251 
252     autopoll = (in_data[0] != 0);
253     if (autopoll != s->autopoll) {
254         s->autopoll = autopoll;
255         if (autopoll) {
256             timer_mod(s->adb_poll_timer,
257                       qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
258                       (NANOSECONDS_PER_SECOND / (1000 / s->autopoll_rate_ms)));
259         } else {
260             timer_del(s->adb_poll_timer);
261         }
262     }
263     return true;
264 }
265 
266 static bool cuda_cmd_set_autorate(CUDAState *s,
267                                   const uint8_t *in_data, int in_len,
268                                   uint8_t *out_data, int *out_len)
269 {
270     if (in_len != 1) {
271         return false;
272     }
273 
274     /* we don't want a period of 0 ms */
275     /* FIXME: check what real hardware does */
276     if (in_data[0] == 0) {
277         return false;
278     }
279 
280     s->autopoll_rate_ms = in_data[0];
281     if (s->autopoll) {
282         timer_mod(s->adb_poll_timer,
283                   qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
284                   (NANOSECONDS_PER_SECOND / (1000 / s->autopoll_rate_ms)));
285     }
286     return true;
287 }
288 
289 static bool cuda_cmd_set_device_list(CUDAState *s,
290                                      const uint8_t *in_data, int in_len,
291                                      uint8_t *out_data, int *out_len)
292 {
293     if (in_len != 2) {
294         return false;
295     }
296 
297     s->adb_poll_mask = (((uint16_t)in_data[0]) << 8) | in_data[1];
298     return true;
299 }
300 
301 static bool cuda_cmd_powerdown(CUDAState *s,
302                                const uint8_t *in_data, int in_len,
303                                uint8_t *out_data, int *out_len)
304 {
305     if (in_len != 0) {
306         return false;
307     }
308 
309     qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
310     return true;
311 }
312 
313 static bool cuda_cmd_reset_system(CUDAState *s,
314                                   const uint8_t *in_data, int in_len,
315                                   uint8_t *out_data, int *out_len)
316 {
317     if (in_len != 0) {
318         return false;
319     }
320 
321     qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
322     return true;
323 }
324 
325 static bool cuda_cmd_set_file_server_flag(CUDAState *s,
326                                           const uint8_t *in_data, int in_len,
327                                           uint8_t *out_data, int *out_len)
328 {
329     if (in_len != 1) {
330         return false;
331     }
332 
333     qemu_log_mask(LOG_UNIMP,
334                   "CUDA: unimplemented command FILE_SERVER_FLAG %d\n",
335                   in_data[0]);
336     return true;
337 }
338 
339 static bool cuda_cmd_set_power_message(CUDAState *s,
340                                        const uint8_t *in_data, int in_len,
341                                        uint8_t *out_data, int *out_len)
342 {
343     if (in_len != 1) {
344         return false;
345     }
346 
347     qemu_log_mask(LOG_UNIMP,
348                   "CUDA: unimplemented command SET_POWER_MESSAGE %d\n",
349                   in_data[0]);
350     return true;
351 }
352 
353 static bool cuda_cmd_get_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 != 0) {
360         return false;
361     }
362 
363     ti = s->tick_offset + (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)
364                            / NANOSECONDS_PER_SECOND);
365     out_data[0] = ti >> 24;
366     out_data[1] = ti >> 16;
367     out_data[2] = ti >> 8;
368     out_data[3] = ti;
369     *out_len = 4;
370     return true;
371 }
372 
373 static bool cuda_cmd_set_time(CUDAState *s,
374                               const uint8_t *in_data, int in_len,
375                               uint8_t *out_data, int *out_len)
376 {
377     uint32_t ti;
378 
379     if (in_len != 4) {
380         return false;
381     }
382 
383     ti = (((uint32_t)in_data[0]) << 24) + (((uint32_t)in_data[1]) << 16)
384          + (((uint32_t)in_data[2]) << 8) + in_data[3];
385     s->tick_offset = ti - (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)
386                            / NANOSECONDS_PER_SECOND);
387     return true;
388 }
389 
390 static const CudaCommand handlers[] = {
391     { CUDA_AUTOPOLL, "AUTOPOLL", cuda_cmd_autopoll },
392     { CUDA_SET_AUTO_RATE, "SET_AUTO_RATE",  cuda_cmd_set_autorate },
393     { CUDA_SET_DEVICE_LIST, "SET_DEVICE_LIST", cuda_cmd_set_device_list },
394     { CUDA_POWERDOWN, "POWERDOWN", cuda_cmd_powerdown },
395     { CUDA_RESET_SYSTEM, "RESET_SYSTEM", cuda_cmd_reset_system },
396     { CUDA_FILE_SERVER_FLAG, "FILE_SERVER_FLAG",
397       cuda_cmd_set_file_server_flag },
398     { CUDA_SET_POWER_MESSAGES, "SET_POWER_MESSAGES",
399       cuda_cmd_set_power_message },
400     { CUDA_GET_TIME, "GET_TIME", cuda_cmd_get_time },
401     { CUDA_SET_TIME, "SET_TIME", cuda_cmd_set_time },
402 };
403 
404 static void cuda_receive_packet(CUDAState *s,
405                                 const uint8_t *data, int len)
406 {
407     uint8_t obuf[16] = { CUDA_PACKET, 0, data[0] };
408     int i, out_len = 0;
409 
410     for (i = 0; i < ARRAY_SIZE(handlers); i++) {
411         const CudaCommand *desc = &handlers[i];
412         if (desc->command == data[0]) {
413             CUDA_DPRINTF("handling command %s\n", desc->name);
414             out_len = 0;
415             if (desc->handler(s, data + 1, len - 1, obuf + 3, &out_len)) {
416                 cuda_send_packet_to_host(s, obuf, 3 + out_len);
417             } else {
418                 qemu_log_mask(LOG_GUEST_ERROR,
419                               "CUDA: %s: wrong parameters %d\n",
420                               desc->name, len);
421                 obuf[0] = ERROR_PACKET;
422                 obuf[1] = 0x5; /* bad parameters */
423                 obuf[2] = CUDA_PACKET;
424                 obuf[3] = data[0];
425                 cuda_send_packet_to_host(s, obuf, 4);
426             }
427             return;
428         }
429     }
430 
431     qemu_log_mask(LOG_GUEST_ERROR, "CUDA: unknown command 0x%02x\n", data[0]);
432     obuf[0] = ERROR_PACKET;
433     obuf[1] = 0x2; /* unknown command */
434     obuf[2] = CUDA_PACKET;
435     obuf[3] = data[0];
436     cuda_send_packet_to_host(s, obuf, 4);
437 }
438 
439 static void cuda_receive_packet_from_host(CUDAState *s,
440                                           const uint8_t *data, int len)
441 {
442 #ifdef DEBUG_CUDA_PACKET
443     {
444         int i;
445         printf("cuda_receive_packet_from_host:\n");
446         for(i = 0; i < len; i++)
447             printf(" %02x", data[i]);
448         printf("\n");
449     }
450 #endif
451     switch(data[0]) {
452     case ADB_PACKET:
453         {
454             uint8_t obuf[ADB_MAX_OUT_LEN + 3];
455             int olen;
456             olen = adb_request(&s->adb_bus, obuf + 2, data + 1, len - 1);
457             if (olen > 0) {
458                 obuf[0] = ADB_PACKET;
459                 obuf[1] = 0x00;
460                 cuda_send_packet_to_host(s, obuf, olen + 2);
461             } else {
462                 /* error */
463                 obuf[0] = ADB_PACKET;
464                 obuf[1] = -olen;
465                 obuf[2] = data[1];
466                 olen = 0;
467                 cuda_send_packet_to_host(s, obuf, olen + 3);
468             }
469         }
470         break;
471     case CUDA_PACKET:
472         cuda_receive_packet(s, data + 1, len - 1);
473         break;
474     }
475 }
476 
477 static uint64_t mos6522_cuda_read(void *opaque, hwaddr addr, unsigned size)
478 {
479     CUDAState *s = opaque;
480     MOS6522CUDAState *mcs = s->mos6522_cuda;
481     MOS6522State *ms = MOS6522(mcs);
482 
483     addr = (addr >> 9) & 0xf;
484     return mos6522_read(ms, addr, size);
485 }
486 
487 static void mos6522_cuda_write(void *opaque, hwaddr addr, uint64_t val,
488                                unsigned size)
489 {
490     CUDAState *s = opaque;
491     MOS6522CUDAState *mcs = s->mos6522_cuda;
492     MOS6522State *ms = MOS6522(mcs);
493 
494     addr = (addr >> 9) & 0xf;
495     mos6522_write(ms, addr, val, size);
496 }
497 
498 static const MemoryRegionOps mos6522_cuda_ops = {
499     .read = mos6522_cuda_read,
500     .write = mos6522_cuda_write,
501     .endianness = DEVICE_BIG_ENDIAN,
502     .valid = {
503         .min_access_size = 1,
504         .max_access_size = 1,
505     },
506 };
507 
508 static const VMStateDescription vmstate_cuda = {
509     .name = "cuda",
510     .version_id = 4,
511     .minimum_version_id = 4,
512     .fields = (VMStateField[]) {
513         VMSTATE_UINT8(last_b, CUDAState),
514         VMSTATE_UINT8(last_acr, CUDAState),
515         VMSTATE_INT32(data_in_size, CUDAState),
516         VMSTATE_INT32(data_in_index, CUDAState),
517         VMSTATE_INT32(data_out_index, CUDAState),
518         VMSTATE_UINT8(autopoll, CUDAState),
519         VMSTATE_UINT8(autopoll_rate_ms, CUDAState),
520         VMSTATE_UINT16(adb_poll_mask, CUDAState),
521         VMSTATE_BUFFER(data_in, CUDAState),
522         VMSTATE_BUFFER(data_out, CUDAState),
523         VMSTATE_UINT32(tick_offset, CUDAState),
524         VMSTATE_TIMER_PTR(adb_poll_timer, CUDAState),
525         VMSTATE_TIMER_PTR(sr_delay_timer, CUDAState),
526         VMSTATE_END_OF_LIST()
527     }
528 };
529 
530 static void cuda_reset(DeviceState *dev)
531 {
532     CUDAState *s = CUDA(dev);
533 
534     s->data_in_size = 0;
535     s->data_in_index = 0;
536     s->data_out_index = 0;
537     s->autopoll = 0;
538 }
539 
540 static void cuda_realize(DeviceState *dev, Error **errp)
541 {
542     CUDAState *s = CUDA(dev);
543     SysBusDevice *sbd;
544     MOS6522State *ms;
545     DeviceState *d;
546     struct tm tm;
547 
548     d = qdev_create(NULL, TYPE_MOS6522_CUDA);
549     object_property_set_link(OBJECT(d), OBJECT(s), "cuda", errp);
550     qdev_init_nofail(d);
551     s->mos6522_cuda = MOS6522_CUDA(d);
552 
553     /* Pass IRQ from 6522 */
554     ms = MOS6522(d);
555     sbd = SYS_BUS_DEVICE(s);
556     sysbus_pass_irq(sbd, SYS_BUS_DEVICE(ms));
557 
558     qemu_get_timedate(&tm, 0);
559     s->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET;
560 
561     s->sr_delay_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_set_sr_int, s);
562     s->sr_delay_ns = 300 * SCALE_US;
563 
564     s->adb_poll_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_adb_poll, s);
565     s->adb_poll_mask = 0xffff;
566     s->autopoll_rate_ms = 20;
567 }
568 
569 static void cuda_init(Object *obj)
570 {
571     CUDAState *s = CUDA(obj);
572     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
573 
574     memory_region_init_io(&s->mem, obj, &mos6522_cuda_ops, s, "cuda", 0x2000);
575     sysbus_init_mmio(sbd, &s->mem);
576 
577     qbus_create_inplace(&s->adb_bus, sizeof(s->adb_bus), TYPE_ADB_BUS,
578                         DEVICE(obj), "adb.0");
579 }
580 
581 static Property cuda_properties[] = {
582     DEFINE_PROP_UINT64("timebase-frequency", CUDAState, tb_frequency, 0),
583     DEFINE_PROP_END_OF_LIST()
584 };
585 
586 static void cuda_class_init(ObjectClass *oc, void *data)
587 {
588     DeviceClass *dc = DEVICE_CLASS(oc);
589 
590     dc->realize = cuda_realize;
591     dc->reset = cuda_reset;
592     dc->vmsd = &vmstate_cuda;
593     dc->props = cuda_properties;
594     set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
595 }
596 
597 static const TypeInfo cuda_type_info = {
598     .name = TYPE_CUDA,
599     .parent = TYPE_SYS_BUS_DEVICE,
600     .instance_size = sizeof(CUDAState),
601     .instance_init = cuda_init,
602     .class_init = cuda_class_init,
603 };
604 
605 static void mos6522_cuda_portB_write(MOS6522State *s)
606 {
607     MOS6522CUDAState *mcs = container_of(s, MOS6522CUDAState, parent_obj);
608 
609     cuda_update(mcs->cuda);
610 }
611 
612 static void mos6522_cuda_realize(DeviceState *dev, Error **errp)
613 {
614     MOS6522State *ms = MOS6522(dev);
615     MOS6522DeviceClass *mdc = MOS6522_DEVICE_GET_CLASS(ms);
616 
617     mdc->parent_realize(dev, errp);
618 
619     ms->timers[0].frequency = CUDA_TIMER_FREQ;
620     ms->timers[1].frequency = (SCALE_US * 6000) / 4700;
621 }
622 
623 static void mos6522_cuda_init(Object *obj)
624 {
625     MOS6522CUDAState *s = MOS6522_CUDA(obj);
626 
627     object_property_add_link(obj, "cuda", TYPE_CUDA,
628                              (Object **) &s->cuda,
629                              qdev_prop_allow_set_link_before_realize,
630                              0, NULL);
631 }
632 
633 static void mos6522_cuda_class_init(ObjectClass *oc, void *data)
634 {
635     DeviceClass *dc = DEVICE_CLASS(oc);
636     MOS6522DeviceClass *mdc = MOS6522_DEVICE_CLASS(oc);
637 
638     dc->realize = mos6522_cuda_realize;
639     mdc->portB_write = mos6522_cuda_portB_write;
640     mdc->get_timer1_counter_value = cuda_get_counter_value;
641     mdc->get_timer2_counter_value = cuda_get_counter_value;
642     mdc->get_timer1_load_time = cuda_get_load_time;
643     mdc->get_timer2_load_time = cuda_get_load_time;
644 }
645 
646 static const TypeInfo mos6522_cuda_type_info = {
647     .name = TYPE_MOS6522_CUDA,
648     .parent = TYPE_MOS6522,
649     .instance_size = sizeof(MOS6522CUDAState),
650     .instance_init = mos6522_cuda_init,
651     .class_init = mos6522_cuda_class_init,
652 };
653 
654 static void cuda_register_types(void)
655 {
656     type_register_static(&mos6522_cuda_type_info);
657     type_register_static(&cuda_type_info);
658 }
659 
660 type_init(cuda_register_types)
661