xref: /qemu/hw/misc/macio/cuda.c (revision 6729aa40135bc96d69b5bf5e65a7d463ef7793e7)
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