cuda.c 19 KB

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