2 * QEMU PowerMac CUDA device support
4 * Copyright (c) 2004-2007 Fabrice Bellard
5 * Copyright (c) 2007 Jocelyn Mayer
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:
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
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
25 #include "qemu/osdep.h"
27 #include "hw/ppc/mac.h"
28 #include "hw/input/adb.h"
29 #include "hw/misc/mos6522.h"
30 #include "hw/misc/macio/cuda.h"
31 #include "qemu/timer.h"
32 #include "sysemu/sysemu.h"
33 #include "qemu/cutils.h"
37 /* Bits in B data register: all active low */
38 #define TREQ 0x08 /* Transfer request (input) */
39 #define TACK 0x10 /* Transfer acknowledge (output) */
40 #define TIP 0x20 /* Transfer in progress (output) */
42 /* commands (1st byte) */
45 #define ERROR_PACKET 2
46 #define TIMER_PACKET 3
47 #define POWER_PACKET 4
48 #define MACIIC_PACKET 5
51 #define CUDA_TIMER_FREQ (4700000 / 6)
53 /* CUDA returns time_t's offset from Jan 1, 1904, not 1970 */
54 #define RTC_OFFSET 2082844800
56 static void cuda_receive_packet_from_host(CUDAState
*s
,
57 const uint8_t *data
, int len
);
59 /* MacOS uses timer 1 for calibration on startup, so we use
60 * the timebase frequency and cuda_get_counter_value() with
61 * cuda_get_load_time() to steer MacOS to calculate calibrate its timers
62 * correctly for both TCG and KVM (see commit b981289c49 "PPC: Cuda: Use cuda
63 * timer to expose tbfreq to guest" for more information) */
65 static uint64_t cuda_get_counter_value(MOS6522State
*s
, MOS6522Timer
*ti
)
67 MOS6522CUDAState
*mcs
= container_of(s
, MOS6522CUDAState
, parent_obj
);
68 CUDAState
*cs
= container_of(mcs
, CUDAState
, mos6522_cuda
);
70 /* Reverse of the tb calculation algorithm that Mac OS X uses on bootup */
71 uint64_t tb_diff
= muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
),
72 cs
->tb_frequency
, NANOSECONDS_PER_SECOND
) -
75 return (tb_diff
* 0xBF401675E5DULL
) / (cs
->tb_frequency
<< 24);
78 static uint64_t cuda_get_load_time(MOS6522State
*s
, MOS6522Timer
*ti
)
80 MOS6522CUDAState
*mcs
= container_of(s
, MOS6522CUDAState
, parent_obj
);
81 CUDAState
*cs
= container_of(mcs
, CUDAState
, mos6522_cuda
);
83 uint64_t load_time
= muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
),
84 cs
->tb_frequency
, NANOSECONDS_PER_SECOND
);
88 static void cuda_set_sr_int(void *opaque
)
90 CUDAState
*s
= opaque
;
91 MOS6522CUDAState
*mcs
= &s
->mos6522_cuda
;
92 MOS6522State
*ms
= MOS6522(mcs
);
93 MOS6522DeviceClass
*mdc
= MOS6522_DEVICE_GET_CLASS(ms
);
98 static void cuda_delay_set_sr_int(CUDAState
*s
)
100 MOS6522CUDAState
*mcs
= &s
->mos6522_cuda
;
101 MOS6522State
*ms
= MOS6522(mcs
);
102 MOS6522DeviceClass
*mdc
= MOS6522_DEVICE_GET_CLASS(ms
);
105 if (ms
->dirb
== 0xff || s
->sr_delay_ns
== 0) {
106 /* Disabled or not in Mac OS, fire the IRQ directly */
111 trace_cuda_delay_set_sr_int();
113 expire
= qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
) + s
->sr_delay_ns
;
114 timer_mod(s
->sr_delay_timer
, expire
);
117 /* NOTE: TIP and TREQ are negated */
118 static void cuda_update(CUDAState
*s
)
120 MOS6522CUDAState
*mcs
= &s
->mos6522_cuda
;
121 MOS6522State
*ms
= MOS6522(mcs
);
122 int packet_received
, len
;
125 if (!(ms
->b
& TIP
)) {
126 /* transfer requested from host */
128 if (ms
->acr
& SR_OUT
) {
130 if ((ms
->b
& (TACK
| TIP
)) != (s
->last_b
& (TACK
| TIP
))) {
131 if (s
->data_out_index
< sizeof(s
->data_out
)) {
132 trace_cuda_data_send(ms
->sr
);
133 s
->data_out
[s
->data_out_index
++] = ms
->sr
;
134 cuda_delay_set_sr_int(s
);
138 if (s
->data_in_index
< s
->data_in_size
) {
140 if ((ms
->b
& (TACK
| TIP
)) != (s
->last_b
& (TACK
| TIP
))) {
141 ms
->sr
= s
->data_in
[s
->data_in_index
++];
142 trace_cuda_data_recv(ms
->sr
);
143 /* indicate end of transfer */
144 if (s
->data_in_index
>= s
->data_in_size
) {
145 ms
->b
= (ms
->b
| TREQ
);
147 cuda_delay_set_sr_int(s
);
152 /* no transfer requested: handle sync case */
153 if ((s
->last_b
& TIP
) && (ms
->b
& TACK
) != (s
->last_b
& TACK
)) {
154 /* update TREQ state each time TACK change state */
156 ms
->b
= (ms
->b
| TREQ
);
158 ms
->b
= (ms
->b
& ~TREQ
);
160 cuda_delay_set_sr_int(s
);
162 if (!(s
->last_b
& TIP
)) {
163 /* handle end of host to cuda transfer */
164 packet_received
= (s
->data_out_index
> 0);
165 /* always an IRQ at the end of transfer */
166 cuda_delay_set_sr_int(s
);
168 /* signal if there is data to read */
169 if (s
->data_in_index
< s
->data_in_size
) {
170 ms
->b
= (ms
->b
& ~TREQ
);
175 s
->last_acr
= ms
->acr
;
178 /* NOTE: cuda_receive_packet_from_host() can call cuda_update()
180 if (packet_received
) {
181 len
= s
->data_out_index
;
182 s
->data_out_index
= 0;
183 cuda_receive_packet_from_host(s
, s
->data_out
, len
);
187 static void cuda_send_packet_to_host(CUDAState
*s
,
188 const uint8_t *data
, int len
)
192 trace_cuda_packet_send(len
);
193 for (i
= 0; i
< len
; i
++) {
194 trace_cuda_packet_send_data(i
, data
[i
]);
197 memcpy(s
->data_in
, data
, len
);
198 s
->data_in_size
= len
;
199 s
->data_in_index
= 0;
201 cuda_delay_set_sr_int(s
);
204 static void cuda_adb_poll(void *opaque
)
206 CUDAState
*s
= opaque
;
207 uint8_t obuf
[ADB_MAX_OUT_LEN
+ 2];
210 olen
= adb_poll(&s
->adb_bus
, obuf
+ 2, s
->adb_poll_mask
);
212 obuf
[0] = ADB_PACKET
;
213 obuf
[1] = 0x40; /* polled data */
214 cuda_send_packet_to_host(s
, obuf
, olen
+ 2);
216 timer_mod(s
->adb_poll_timer
, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
) +
217 (NANOSECONDS_PER_SECOND
/ (1000 / s
->autopoll_rate_ms
)));
220 /* description of commands */
221 typedef struct CudaCommand
{
224 bool (*handler
)(CUDAState
*s
,
225 const uint8_t *in_args
, int in_len
,
226 uint8_t *out_args
, int *out_len
);
229 static bool cuda_cmd_autopoll(CUDAState
*s
,
230 const uint8_t *in_data
, int in_len
,
231 uint8_t *out_data
, int *out_len
)
239 autopoll
= (in_data
[0] != 0);
240 if (autopoll
!= s
->autopoll
) {
241 s
->autopoll
= autopoll
;
243 timer_mod(s
->adb_poll_timer
,
244 qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
) +
245 (NANOSECONDS_PER_SECOND
/ (1000 / s
->autopoll_rate_ms
)));
247 timer_del(s
->adb_poll_timer
);
253 static bool cuda_cmd_set_autorate(CUDAState
*s
,
254 const uint8_t *in_data
, int in_len
,
255 uint8_t *out_data
, int *out_len
)
261 /* we don't want a period of 0 ms */
262 /* FIXME: check what real hardware does */
263 if (in_data
[0] == 0) {
267 s
->autopoll_rate_ms
= in_data
[0];
269 timer_mod(s
->adb_poll_timer
,
270 qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
) +
271 (NANOSECONDS_PER_SECOND
/ (1000 / s
->autopoll_rate_ms
)));
276 static bool cuda_cmd_set_device_list(CUDAState
*s
,
277 const uint8_t *in_data
, int in_len
,
278 uint8_t *out_data
, int *out_len
)
284 s
->adb_poll_mask
= (((uint16_t)in_data
[0]) << 8) | in_data
[1];
288 static bool cuda_cmd_powerdown(CUDAState
*s
,
289 const uint8_t *in_data
, int in_len
,
290 uint8_t *out_data
, int *out_len
)
296 qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN
);
300 static bool cuda_cmd_reset_system(CUDAState
*s
,
301 const uint8_t *in_data
, int in_len
,
302 uint8_t *out_data
, int *out_len
)
308 qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET
);
312 static bool cuda_cmd_set_file_server_flag(CUDAState
*s
,
313 const uint8_t *in_data
, int in_len
,
314 uint8_t *out_data
, int *out_len
)
320 qemu_log_mask(LOG_UNIMP
,
321 "CUDA: unimplemented command FILE_SERVER_FLAG %d\n",
326 static bool cuda_cmd_set_power_message(CUDAState
*s
,
327 const uint8_t *in_data
, int in_len
,
328 uint8_t *out_data
, int *out_len
)
334 qemu_log_mask(LOG_UNIMP
,
335 "CUDA: unimplemented command SET_POWER_MESSAGE %d\n",
340 static bool cuda_cmd_get_time(CUDAState
*s
,
341 const uint8_t *in_data
, int in_len
,
342 uint8_t *out_data
, int *out_len
)
350 ti
= s
->tick_offset
+ (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
)
351 / NANOSECONDS_PER_SECOND
);
352 out_data
[0] = ti
>> 24;
353 out_data
[1] = ti
>> 16;
354 out_data
[2] = ti
>> 8;
360 static bool cuda_cmd_set_time(CUDAState
*s
,
361 const uint8_t *in_data
, int in_len
,
362 uint8_t *out_data
, int *out_len
)
370 ti
= (((uint32_t)in_data
[0]) << 24) + (((uint32_t)in_data
[1]) << 16)
371 + (((uint32_t)in_data
[2]) << 8) + in_data
[3];
372 s
->tick_offset
= ti
- (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
)
373 / NANOSECONDS_PER_SECOND
);
377 static const CudaCommand handlers
[] = {
378 { CUDA_AUTOPOLL
, "AUTOPOLL", cuda_cmd_autopoll
},
379 { CUDA_SET_AUTO_RATE
, "SET_AUTO_RATE", cuda_cmd_set_autorate
},
380 { CUDA_SET_DEVICE_LIST
, "SET_DEVICE_LIST", cuda_cmd_set_device_list
},
381 { CUDA_POWERDOWN
, "POWERDOWN", cuda_cmd_powerdown
},
382 { CUDA_RESET_SYSTEM
, "RESET_SYSTEM", cuda_cmd_reset_system
},
383 { CUDA_FILE_SERVER_FLAG
, "FILE_SERVER_FLAG",
384 cuda_cmd_set_file_server_flag
},
385 { CUDA_SET_POWER_MESSAGES
, "SET_POWER_MESSAGES",
386 cuda_cmd_set_power_message
},
387 { CUDA_GET_TIME
, "GET_TIME", cuda_cmd_get_time
},
388 { CUDA_SET_TIME
, "SET_TIME", cuda_cmd_set_time
},
391 static void cuda_receive_packet(CUDAState
*s
,
392 const uint8_t *data
, int len
)
394 uint8_t obuf
[16] = { CUDA_PACKET
, 0, data
[0] };
397 for (i
= 0; i
< ARRAY_SIZE(handlers
); i
++) {
398 const CudaCommand
*desc
= &handlers
[i
];
399 if (desc
->command
== data
[0]) {
400 trace_cuda_receive_packet_cmd(desc
->name
);
402 if (desc
->handler(s
, data
+ 1, len
- 1, obuf
+ 3, &out_len
)) {
403 cuda_send_packet_to_host(s
, obuf
, 3 + out_len
);
405 qemu_log_mask(LOG_GUEST_ERROR
,
406 "CUDA: %s: wrong parameters %d\n",
408 obuf
[0] = ERROR_PACKET
;
409 obuf
[1] = 0x5; /* bad parameters */
410 obuf
[2] = CUDA_PACKET
;
412 cuda_send_packet_to_host(s
, obuf
, 4);
418 qemu_log_mask(LOG_GUEST_ERROR
, "CUDA: unknown command 0x%02x\n", data
[0]);
419 obuf
[0] = ERROR_PACKET
;
420 obuf
[1] = 0x2; /* unknown command */
421 obuf
[2] = CUDA_PACKET
;
423 cuda_send_packet_to_host(s
, obuf
, 4);
426 static void cuda_receive_packet_from_host(CUDAState
*s
,
427 const uint8_t *data
, int len
)
431 trace_cuda_packet_receive(len
);
432 for (i
= 0; i
< len
; i
++) {
433 trace_cuda_packet_receive_data(i
, data
[i
]);
439 uint8_t obuf
[ADB_MAX_OUT_LEN
+ 3];
441 olen
= adb_request(&s
->adb_bus
, obuf
+ 2, data
+ 1, len
- 1);
443 obuf
[0] = ADB_PACKET
;
445 cuda_send_packet_to_host(s
, obuf
, olen
+ 2);
448 obuf
[0] = ADB_PACKET
;
452 cuda_send_packet_to_host(s
, obuf
, olen
+ 3);
457 cuda_receive_packet(s
, data
+ 1, len
- 1);
462 static uint64_t mos6522_cuda_read(void *opaque
, hwaddr addr
, unsigned size
)
464 CUDAState
*s
= opaque
;
465 MOS6522CUDAState
*mcs
= &s
->mos6522_cuda
;
466 MOS6522State
*ms
= MOS6522(mcs
);
468 addr
= (addr
>> 9) & 0xf;
469 return mos6522_read(ms
, addr
, size
);
472 static void mos6522_cuda_write(void *opaque
, hwaddr addr
, uint64_t val
,
475 CUDAState
*s
= opaque
;
476 MOS6522CUDAState
*mcs
= &s
->mos6522_cuda
;
477 MOS6522State
*ms
= MOS6522(mcs
);
479 addr
= (addr
>> 9) & 0xf;
480 mos6522_write(ms
, addr
, val
, size
);
483 static const MemoryRegionOps mos6522_cuda_ops
= {
484 .read
= mos6522_cuda_read
,
485 .write
= mos6522_cuda_write
,
486 .endianness
= DEVICE_BIG_ENDIAN
,
488 .min_access_size
= 1,
489 .max_access_size
= 1,
493 static const VMStateDescription vmstate_cuda
= {
496 .minimum_version_id
= 5,
497 .fields
= (VMStateField
[]) {
498 VMSTATE_STRUCT(mos6522_cuda
.parent_obj
, CUDAState
, 0, vmstate_mos6522
,
500 VMSTATE_UINT8(last_b
, CUDAState
),
501 VMSTATE_UINT8(last_acr
, CUDAState
),
502 VMSTATE_INT32(data_in_size
, CUDAState
),
503 VMSTATE_INT32(data_in_index
, CUDAState
),
504 VMSTATE_INT32(data_out_index
, CUDAState
),
505 VMSTATE_UINT8(autopoll
, CUDAState
),
506 VMSTATE_UINT8(autopoll_rate_ms
, CUDAState
),
507 VMSTATE_UINT16(adb_poll_mask
, CUDAState
),
508 VMSTATE_BUFFER(data_in
, CUDAState
),
509 VMSTATE_BUFFER(data_out
, CUDAState
),
510 VMSTATE_UINT32(tick_offset
, CUDAState
),
511 VMSTATE_TIMER_PTR(adb_poll_timer
, CUDAState
),
512 VMSTATE_TIMER_PTR(sr_delay_timer
, CUDAState
),
513 VMSTATE_END_OF_LIST()
517 static void cuda_reset(DeviceState
*dev
)
519 CUDAState
*s
= CUDA(dev
);
522 s
->data_in_index
= 0;
523 s
->data_out_index
= 0;
527 static void cuda_realize(DeviceState
*dev
, Error
**errp
)
529 CUDAState
*s
= CUDA(dev
);
535 /* Pass IRQ from 6522 */
536 d
= DEVICE(&s
->mos6522_cuda
);
538 sbd
= SYS_BUS_DEVICE(s
);
539 sysbus_pass_irq(sbd
, SYS_BUS_DEVICE(ms
));
541 qemu_get_timedate(&tm
, 0);
542 s
->tick_offset
= (uint32_t)mktimegm(&tm
) + RTC_OFFSET
;
544 s
->sr_delay_timer
= timer_new_ns(QEMU_CLOCK_VIRTUAL
, cuda_set_sr_int
, s
);
545 s
->sr_delay_ns
= 300 * SCALE_US
;
547 s
->adb_poll_timer
= timer_new_ns(QEMU_CLOCK_VIRTUAL
, cuda_adb_poll
, s
);
548 s
->adb_poll_mask
= 0xffff;
549 s
->autopoll_rate_ms
= 20;
552 static void cuda_init(Object
*obj
)
554 CUDAState
*s
= CUDA(obj
);
555 SysBusDevice
*sbd
= SYS_BUS_DEVICE(obj
);
557 sysbus_init_child_obj(obj
, "mos6522-cuda", &s
->mos6522_cuda
,
558 sizeof(s
->mos6522_cuda
), TYPE_MOS6522_CUDA
);
560 memory_region_init_io(&s
->mem
, obj
, &mos6522_cuda_ops
, s
, "cuda", 0x2000);
561 sysbus_init_mmio(sbd
, &s
->mem
);
563 qbus_create_inplace(&s
->adb_bus
, sizeof(s
->adb_bus
), TYPE_ADB_BUS
,
564 DEVICE(obj
), "adb.0");
567 static Property cuda_properties
[] = {
568 DEFINE_PROP_UINT64("timebase-frequency", CUDAState
, tb_frequency
, 0),
569 DEFINE_PROP_END_OF_LIST()
572 static void cuda_class_init(ObjectClass
*oc
, void *data
)
574 DeviceClass
*dc
= DEVICE_CLASS(oc
);
576 dc
->realize
= cuda_realize
;
577 dc
->reset
= cuda_reset
;
578 dc
->vmsd
= &vmstate_cuda
;
579 dc
->props
= cuda_properties
;
580 set_bit(DEVICE_CATEGORY_BRIDGE
, dc
->categories
);
583 static const TypeInfo cuda_type_info
= {
585 .parent
= TYPE_SYS_BUS_DEVICE
,
586 .instance_size
= sizeof(CUDAState
),
587 .instance_init
= cuda_init
,
588 .class_init
= cuda_class_init
,
591 static void mos6522_cuda_portB_write(MOS6522State
*s
)
593 MOS6522CUDAState
*mcs
= container_of(s
, MOS6522CUDAState
, parent_obj
);
594 CUDAState
*cs
= container_of(mcs
, CUDAState
, mos6522_cuda
);
599 static void mos6522_cuda_reset(DeviceState
*dev
)
601 MOS6522State
*ms
= MOS6522(dev
);
602 MOS6522DeviceClass
*mdc
= MOS6522_DEVICE_GET_CLASS(ms
);
604 mdc
->parent_reset(dev
);
606 ms
->timers
[0].frequency
= CUDA_TIMER_FREQ
;
607 ms
->timers
[1].frequency
= (SCALE_US
* 6000) / 4700;
610 static void mos6522_cuda_class_init(ObjectClass
*oc
, void *data
)
612 DeviceClass
*dc
= DEVICE_CLASS(oc
);
613 MOS6522DeviceClass
*mdc
= MOS6522_DEVICE_CLASS(oc
);
615 dc
->reset
= mos6522_cuda_reset
;
616 mdc
->portB_write
= mos6522_cuda_portB_write
;
617 mdc
->get_timer1_counter_value
= cuda_get_counter_value
;
618 mdc
->get_timer2_counter_value
= cuda_get_counter_value
;
619 mdc
->get_timer1_load_time
= cuda_get_load_time
;
620 mdc
->get_timer2_load_time
= cuda_get_load_time
;
623 static const TypeInfo mos6522_cuda_type_info
= {
624 .name
= TYPE_MOS6522_CUDA
,
625 .parent
= TYPE_MOS6522
,
626 .instance_size
= sizeof(MOS6522CUDAState
),
627 .class_init
= mos6522_cuda_class_init
,
630 static void cuda_register_types(void)
632 type_register_static(&mos6522_cuda_type_info
);
633 type_register_static(&cuda_type_info
);
636 type_init(cuda_register_types
)