]>
Commit | Line | Data |
---|---|---|
3cbee15b JM |
1 | /* |
2 | * PowerMac descriptor-based DMA emulation | |
3 | * | |
4 | * Copyright (c) 2005-2007 Fabrice Bellard | |
5 | * Copyright (c) 2007 Jocelyn Mayer | |
28ce5ce6 AJ |
6 | * Copyright (c) 2009 Laurent Vivier |
7 | * | |
8 | * some parts from linux-2.6.28, arch/powerpc/include/asm/dbdma.h | |
9 | * | |
10 | * Definitions for using the Apple Descriptor-Based DMA controller | |
11 | * in Power Macintosh computers. | |
12 | * | |
13 | * Copyright (C) 1996 Paul Mackerras. | |
14 | * | |
15 | * some parts from mol 0.9.71 | |
16 | * | |
17 | * Descriptor based DMA emulation | |
18 | * | |
19 | * Copyright (C) 1998-2004 Samuel Rydh (samuel@ibrium.se) | |
3cbee15b JM |
20 | * |
21 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
22 | * of this software and associated documentation files (the "Software"), to deal | |
23 | * in the Software without restriction, including without limitation the rights | |
24 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
25 | * copies of the Software, and to permit persons to whom the Software is | |
26 | * furnished to do so, subject to the following conditions: | |
27 | * | |
28 | * The above copyright notice and this permission notice shall be included in | |
29 | * all copies or substantial portions of the Software. | |
30 | * | |
31 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
32 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
33 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
34 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
35 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
36 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
37 | * THE SOFTWARE. | |
38 | */ | |
0b8fa32f | 39 | |
0d75590d | 40 | #include "qemu/osdep.h" |
83c9f4ca | 41 | #include "hw/hw.h" |
64552b6b | 42 | #include "hw/irq.h" |
0d09e41a | 43 | #include "hw/ppc/mac_dbdma.h" |
d6454270 | 44 | #include "migration/vmstate.h" |
1de7afc9 | 45 | #include "qemu/main-loop.h" |
0b8fa32f | 46 | #include "qemu/module.h" |
03dd024f | 47 | #include "qemu/log.h" |
88655881 | 48 | #include "sysemu/dma.h" |
3cbee15b | 49 | |
ea026b2f | 50 | /* debug DBDMA */ |
ba0b17dd | 51 | #define DEBUG_DBDMA 0 |
3e49c439 | 52 | #define DEBUG_DBDMA_CHANMASK ((1ull << DBDMA_CHANNELS) - 1) |
ea026b2f | 53 | |
ba0b17dd MCA |
54 | #define DBDMA_DPRINTF(fmt, ...) do { \ |
55 | if (DEBUG_DBDMA) { \ | |
56 | printf("DBDMA: " fmt , ## __VA_ARGS__); \ | |
57 | } \ | |
2562755e | 58 | } while (0) |
ea026b2f | 59 | |
3e49c439 MCA |
60 | #define DBDMA_DPRINTFCH(ch, fmt, ...) do { \ |
61 | if (DEBUG_DBDMA) { \ | |
62 | if ((1ul << (ch)->channel) & DEBUG_DBDMA_CHANMASK) { \ | |
63 | printf("DBDMA[%02x]: " fmt , (ch)->channel, ## __VA_ARGS__); \ | |
64 | } \ | |
65 | } \ | |
2562755e | 66 | } while (0) |
3e49c439 | 67 | |
28ce5ce6 AJ |
68 | /* |
69 | */ | |
70 | ||
d2f0ce21 AG |
71 | static DBDMAState *dbdma_from_ch(DBDMA_channel *ch) |
72 | { | |
73 | return container_of(ch, DBDMAState, channels[ch->channel]); | |
74 | } | |
75 | ||
ba0b17dd | 76 | #if DEBUG_DBDMA |
b7d67813 | 77 | static void dump_dbdma_cmd(DBDMA_channel *ch, dbdma_cmd *cmd) |
28ce5ce6 | 78 | { |
b7d67813 MCA |
79 | DBDMA_DPRINTFCH(ch, "dbdma_cmd %p\n", cmd); |
80 | DBDMA_DPRINTFCH(ch, " req_count 0x%04x\n", le16_to_cpu(cmd->req_count)); | |
81 | DBDMA_DPRINTFCH(ch, " command 0x%04x\n", le16_to_cpu(cmd->command)); | |
82 | DBDMA_DPRINTFCH(ch, " phy_addr 0x%08x\n", le32_to_cpu(cmd->phy_addr)); | |
83 | DBDMA_DPRINTFCH(ch, " cmd_dep 0x%08x\n", le32_to_cpu(cmd->cmd_dep)); | |
84 | DBDMA_DPRINTFCH(ch, " res_count 0x%04x\n", le16_to_cpu(cmd->res_count)); | |
85 | DBDMA_DPRINTFCH(ch, " xfer_status 0x%04x\n", | |
86 | le16_to_cpu(cmd->xfer_status)); | |
28ce5ce6 AJ |
87 | } |
88 | #else | |
b7d67813 | 89 | static void dump_dbdma_cmd(DBDMA_channel *ch, dbdma_cmd *cmd) |
3cbee15b | 90 | { |
28ce5ce6 AJ |
91 | } |
92 | #endif | |
93 | static void dbdma_cmdptr_load(DBDMA_channel *ch) | |
94 | { | |
3e49c439 MCA |
95 | DBDMA_DPRINTFCH(ch, "dbdma_cmdptr_load 0x%08x\n", |
96 | ch->regs[DBDMA_CMDPTR_LO]); | |
88655881 MCA |
97 | dma_memory_read(&address_space_memory, ch->regs[DBDMA_CMDPTR_LO], |
98 | &ch->current, sizeof(dbdma_cmd)); | |
3cbee15b JM |
99 | } |
100 | ||
28ce5ce6 | 101 | static void dbdma_cmdptr_save(DBDMA_channel *ch) |
3cbee15b | 102 | { |
77453882 BH |
103 | DBDMA_DPRINTFCH(ch, "-> update 0x%08x stat=0x%08x, res=0x%04x\n", |
104 | ch->regs[DBDMA_CMDPTR_LO], | |
3e49c439 MCA |
105 | le16_to_cpu(ch->current.xfer_status), |
106 | le16_to_cpu(ch->current.res_count)); | |
88655881 MCA |
107 | dma_memory_write(&address_space_memory, ch->regs[DBDMA_CMDPTR_LO], |
108 | &ch->current, sizeof(dbdma_cmd)); | |
3cbee15b JM |
109 | } |
110 | ||
28ce5ce6 | 111 | static void kill_channel(DBDMA_channel *ch) |
3cbee15b | 112 | { |
3e49c439 | 113 | DBDMA_DPRINTFCH(ch, "kill_channel\n"); |
28ce5ce6 | 114 | |
ad674e53 AJ |
115 | ch->regs[DBDMA_STATUS] |= DEAD; |
116 | ch->regs[DBDMA_STATUS] &= ~ACTIVE; | |
28ce5ce6 AJ |
117 | |
118 | qemu_irq_raise(ch->irq); | |
119 | } | |
120 | ||
121 | static void conditional_interrupt(DBDMA_channel *ch) | |
122 | { | |
123 | dbdma_cmd *current = &ch->current; | |
124 | uint16_t intr; | |
125 | uint16_t sel_mask, sel_value; | |
126 | uint32_t status; | |
127 | int cond; | |
128 | ||
3e49c439 | 129 | DBDMA_DPRINTFCH(ch, "%s\n", __func__); |
28ce5ce6 | 130 | |
b42ec42d | 131 | intr = le16_to_cpu(current->command) & INTR_MASK; |
28ce5ce6 AJ |
132 | |
133 | switch(intr) { | |
134 | case INTR_NEVER: /* don't interrupt */ | |
135 | return; | |
136 | case INTR_ALWAYS: /* always interrupt */ | |
137 | qemu_irq_raise(ch->irq); | |
3e49c439 | 138 | DBDMA_DPRINTFCH(ch, "%s: raise\n", __func__); |
28ce5ce6 AJ |
139 | return; |
140 | } | |
141 | ||
ad674e53 | 142 | status = ch->regs[DBDMA_STATUS] & DEVSTAT; |
28ce5ce6 | 143 | |
ad674e53 AJ |
144 | sel_mask = (ch->regs[DBDMA_INTR_SEL] >> 16) & 0x0f; |
145 | sel_value = ch->regs[DBDMA_INTR_SEL] & 0x0f; | |
28ce5ce6 AJ |
146 | |
147 | cond = (status & sel_mask) == (sel_value & sel_mask); | |
148 | ||
149 | switch(intr) { | |
150 | case INTR_IFSET: /* intr if condition bit is 1 */ | |
33ce36bb | 151 | if (cond) { |
28ce5ce6 | 152 | qemu_irq_raise(ch->irq); |
3e49c439 | 153 | DBDMA_DPRINTFCH(ch, "%s: raise\n", __func__); |
33ce36bb | 154 | } |
28ce5ce6 AJ |
155 | return; |
156 | case INTR_IFCLR: /* intr if condition bit is 0 */ | |
33ce36bb | 157 | if (!cond) { |
28ce5ce6 | 158 | qemu_irq_raise(ch->irq); |
3e49c439 | 159 | DBDMA_DPRINTFCH(ch, "%s: raise\n", __func__); |
33ce36bb | 160 | } |
28ce5ce6 AJ |
161 | return; |
162 | } | |
163 | } | |
164 | ||
165 | static int conditional_wait(DBDMA_channel *ch) | |
166 | { | |
167 | dbdma_cmd *current = &ch->current; | |
168 | uint16_t wait; | |
169 | uint16_t sel_mask, sel_value; | |
170 | uint32_t status; | |
171 | int cond; | |
77453882 | 172 | int res = 0; |
28ce5ce6 | 173 | |
b42ec42d | 174 | wait = le16_to_cpu(current->command) & WAIT_MASK; |
28ce5ce6 AJ |
175 | switch(wait) { |
176 | case WAIT_NEVER: /* don't wait */ | |
177 | return 0; | |
178 | case WAIT_ALWAYS: /* always wait */ | |
77453882 | 179 | DBDMA_DPRINTFCH(ch, " [WAIT_ALWAYS]\n"); |
28ce5ce6 AJ |
180 | return 1; |
181 | } | |
182 | ||
ad674e53 | 183 | status = ch->regs[DBDMA_STATUS] & DEVSTAT; |
28ce5ce6 | 184 | |
ad674e53 AJ |
185 | sel_mask = (ch->regs[DBDMA_WAIT_SEL] >> 16) & 0x0f; |
186 | sel_value = ch->regs[DBDMA_WAIT_SEL] & 0x0f; | |
28ce5ce6 AJ |
187 | |
188 | cond = (status & sel_mask) == (sel_value & sel_mask); | |
189 | ||
190 | switch(wait) { | |
191 | case WAIT_IFSET: /* wait if condition bit is 1 */ | |
77453882 BH |
192 | if (cond) { |
193 | res = 1; | |
194 | } | |
195 | DBDMA_DPRINTFCH(ch, " [WAIT_IFSET=%d]\n", res); | |
196 | break; | |
28ce5ce6 | 197 | case WAIT_IFCLR: /* wait if condition bit is 0 */ |
77453882 BH |
198 | if (!cond) { |
199 | res = 1; | |
200 | } | |
201 | DBDMA_DPRINTFCH(ch, " [WAIT_IFCLR=%d]\n", res); | |
202 | break; | |
28ce5ce6 | 203 | } |
77453882 | 204 | return res; |
28ce5ce6 AJ |
205 | } |
206 | ||
207 | static void next(DBDMA_channel *ch) | |
208 | { | |
209 | uint32_t cp; | |
210 | ||
ad674e53 | 211 | ch->regs[DBDMA_STATUS] &= ~BT; |
28ce5ce6 | 212 | |
ad674e53 AJ |
213 | cp = ch->regs[DBDMA_CMDPTR_LO]; |
214 | ch->regs[DBDMA_CMDPTR_LO] = cp + sizeof(dbdma_cmd); | |
28ce5ce6 AJ |
215 | dbdma_cmdptr_load(ch); |
216 | } | |
217 | ||
218 | static void branch(DBDMA_channel *ch) | |
219 | { | |
220 | dbdma_cmd *current = &ch->current; | |
221 | ||
3f0d4128 | 222 | ch->regs[DBDMA_CMDPTR_LO] = le32_to_cpu(current->cmd_dep); |
ad674e53 | 223 | ch->regs[DBDMA_STATUS] |= BT; |
28ce5ce6 AJ |
224 | dbdma_cmdptr_load(ch); |
225 | } | |
226 | ||
227 | static void conditional_branch(DBDMA_channel *ch) | |
228 | { | |
229 | dbdma_cmd *current = &ch->current; | |
230 | uint16_t br; | |
231 | uint16_t sel_mask, sel_value; | |
232 | uint32_t status; | |
233 | int cond; | |
234 | ||
28ce5ce6 AJ |
235 | /* check if we must branch */ |
236 | ||
b42ec42d | 237 | br = le16_to_cpu(current->command) & BR_MASK; |
28ce5ce6 AJ |
238 | |
239 | switch(br) { | |
240 | case BR_NEVER: /* don't branch */ | |
241 | next(ch); | |
242 | return; | |
243 | case BR_ALWAYS: /* always branch */ | |
77453882 | 244 | DBDMA_DPRINTFCH(ch, " [BR_ALWAYS]\n"); |
28ce5ce6 AJ |
245 | branch(ch); |
246 | return; | |
247 | } | |
248 | ||
ad674e53 | 249 | status = ch->regs[DBDMA_STATUS] & DEVSTAT; |
28ce5ce6 | 250 | |
ad674e53 AJ |
251 | sel_mask = (ch->regs[DBDMA_BRANCH_SEL] >> 16) & 0x0f; |
252 | sel_value = ch->regs[DBDMA_BRANCH_SEL] & 0x0f; | |
28ce5ce6 AJ |
253 | |
254 | cond = (status & sel_mask) == (sel_value & sel_mask); | |
255 | ||
256 | switch(br) { | |
257 | case BR_IFSET: /* branch if condition bit is 1 */ | |
77453882 BH |
258 | if (cond) { |
259 | DBDMA_DPRINTFCH(ch, " [BR_IFSET = 1]\n"); | |
28ce5ce6 | 260 | branch(ch); |
77453882 BH |
261 | } else { |
262 | DBDMA_DPRINTFCH(ch, " [BR_IFSET = 0]\n"); | |
28ce5ce6 | 263 | next(ch); |
77453882 | 264 | } |
28ce5ce6 AJ |
265 | return; |
266 | case BR_IFCLR: /* branch if condition bit is 0 */ | |
77453882 BH |
267 | if (!cond) { |
268 | DBDMA_DPRINTFCH(ch, " [BR_IFCLR = 1]\n"); | |
28ce5ce6 | 269 | branch(ch); |
77453882 BH |
270 | } else { |
271 | DBDMA_DPRINTFCH(ch, " [BR_IFCLR = 0]\n"); | |
28ce5ce6 | 272 | next(ch); |
77453882 | 273 | } |
28ce5ce6 AJ |
274 | return; |
275 | } | |
276 | } | |
277 | ||
b42ec42d | 278 | static void channel_run(DBDMA_channel *ch); |
28ce5ce6 | 279 | |
b42ec42d | 280 | static void dbdma_end(DBDMA_io *io) |
28ce5ce6 AJ |
281 | { |
282 | DBDMA_channel *ch = io->channel; | |
283 | dbdma_cmd *current = &ch->current; | |
284 | ||
3e49c439 | 285 | DBDMA_DPRINTFCH(ch, "%s\n", __func__); |
33ce36bb | 286 | |
b42ec42d AJ |
287 | if (conditional_wait(ch)) |
288 | goto wait; | |
28ce5ce6 | 289 | |
ad674e53 AJ |
290 | current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]); |
291 | current->res_count = cpu_to_le16(io->len); | |
b42ec42d | 292 | dbdma_cmdptr_save(ch); |
862c9280 | 293 | if (io->is_last) |
ad674e53 | 294 | ch->regs[DBDMA_STATUS] &= ~FLUSH; |
b42ec42d AJ |
295 | |
296 | conditional_interrupt(ch); | |
297 | conditional_branch(ch); | |
28ce5ce6 | 298 | |
b42ec42d | 299 | wait: |
03ee3b1e AG |
300 | /* Indicate that we're ready for a new DMA round */ |
301 | ch->io.processing = false; | |
302 | ||
ad674e53 AJ |
303 | if ((ch->regs[DBDMA_STATUS] & RUN) && |
304 | (ch->regs[DBDMA_STATUS] & ACTIVE)) | |
b42ec42d | 305 | channel_run(ch); |
28ce5ce6 AJ |
306 | } |
307 | ||
b42ec42d | 308 | static void start_output(DBDMA_channel *ch, int key, uint32_t addr, |
28ce5ce6 AJ |
309 | uint16_t req_count, int is_last) |
310 | { | |
3e49c439 | 311 | DBDMA_DPRINTFCH(ch, "start_output\n"); |
28ce5ce6 AJ |
312 | |
313 | /* KEY_REGS, KEY_DEVICE and KEY_STREAM | |
314 | * are not implemented in the mac-io chip | |
315 | */ | |
316 | ||
3e49c439 | 317 | DBDMA_DPRINTFCH(ch, "addr 0x%x key 0x%x\n", addr, key); |
28ce5ce6 AJ |
318 | if (!addr || key > KEY_STREAM3) { |
319 | kill_channel(ch); | |
b42ec42d | 320 | return; |
28ce5ce6 AJ |
321 | } |
322 | ||
b42ec42d | 323 | ch->io.addr = addr; |
28ce5ce6 AJ |
324 | ch->io.len = req_count; |
325 | ch->io.is_last = is_last; | |
b42ec42d AJ |
326 | ch->io.dma_end = dbdma_end; |
327 | ch->io.is_dma_out = 1; | |
03ee3b1e | 328 | ch->io.processing = true; |
a9ceb76d AG |
329 | if (ch->rw) { |
330 | ch->rw(&ch->io); | |
331 | } | |
28ce5ce6 AJ |
332 | } |
333 | ||
b42ec42d | 334 | static void start_input(DBDMA_channel *ch, int key, uint32_t addr, |
28ce5ce6 AJ |
335 | uint16_t req_count, int is_last) |
336 | { | |
3e49c439 | 337 | DBDMA_DPRINTFCH(ch, "start_input\n"); |
28ce5ce6 AJ |
338 | |
339 | /* KEY_REGS, KEY_DEVICE and KEY_STREAM | |
340 | * are not implemented in the mac-io chip | |
341 | */ | |
342 | ||
3e49c439 | 343 | DBDMA_DPRINTFCH(ch, "addr 0x%x key 0x%x\n", addr, key); |
28ce5ce6 AJ |
344 | if (!addr || key > KEY_STREAM3) { |
345 | kill_channel(ch); | |
b42ec42d | 346 | return; |
28ce5ce6 AJ |
347 | } |
348 | ||
b42ec42d | 349 | ch->io.addr = addr; |
28ce5ce6 AJ |
350 | ch->io.len = req_count; |
351 | ch->io.is_last = is_last; | |
b42ec42d AJ |
352 | ch->io.dma_end = dbdma_end; |
353 | ch->io.is_dma_out = 0; | |
03ee3b1e | 354 | ch->io.processing = true; |
a9ceb76d AG |
355 | if (ch->rw) { |
356 | ch->rw(&ch->io); | |
357 | } | |
28ce5ce6 AJ |
358 | } |
359 | ||
b42ec42d | 360 | static void load_word(DBDMA_channel *ch, int key, uint32_t addr, |
28ce5ce6 AJ |
361 | uint16_t len) |
362 | { | |
363 | dbdma_cmd *current = &ch->current; | |
28ce5ce6 | 364 | |
e12f50b9 | 365 | DBDMA_DPRINTFCH(ch, "load_word %d bytes, addr=%08x\n", len, addr); |
28ce5ce6 AJ |
366 | |
367 | /* only implements KEY_SYSTEM */ | |
368 | ||
369 | if (key != KEY_SYSTEM) { | |
370 | printf("DBDMA: LOAD_WORD, unimplemented key %x\n", key); | |
371 | kill_channel(ch); | |
b42ec42d | 372 | return; |
28ce5ce6 AJ |
373 | } |
374 | ||
e12f50b9 | 375 | dma_memory_read(&address_space_memory, addr, ¤t->cmd_dep, len); |
28ce5ce6 AJ |
376 | |
377 | if (conditional_wait(ch)) | |
b42ec42d | 378 | goto wait; |
28ce5ce6 | 379 | |
ad674e53 | 380 | current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]); |
28ce5ce6 | 381 | dbdma_cmdptr_save(ch); |
ad674e53 | 382 | ch->regs[DBDMA_STATUS] &= ~FLUSH; |
28ce5ce6 AJ |
383 | |
384 | conditional_interrupt(ch); | |
385 | next(ch); | |
386 | ||
b42ec42d | 387 | wait: |
d2f0ce21 | 388 | DBDMA_kick(dbdma_from_ch(ch)); |
28ce5ce6 AJ |
389 | } |
390 | ||
b42ec42d | 391 | static void store_word(DBDMA_channel *ch, int key, uint32_t addr, |
28ce5ce6 AJ |
392 | uint16_t len) |
393 | { | |
394 | dbdma_cmd *current = &ch->current; | |
28ce5ce6 | 395 | |
e12f50b9 MCA |
396 | DBDMA_DPRINTFCH(ch, "store_word %d bytes, addr=%08x pa=%x\n", |
397 | len, addr, le32_to_cpu(current->cmd_dep)); | |
28ce5ce6 AJ |
398 | |
399 | /* only implements KEY_SYSTEM */ | |
400 | ||
401 | if (key != KEY_SYSTEM) { | |
402 | printf("DBDMA: STORE_WORD, unimplemented key %x\n", key); | |
403 | kill_channel(ch); | |
b42ec42d | 404 | return; |
28ce5ce6 AJ |
405 | } |
406 | ||
e12f50b9 | 407 | dma_memory_write(&address_space_memory, addr, ¤t->cmd_dep, len); |
28ce5ce6 AJ |
408 | |
409 | if (conditional_wait(ch)) | |
b42ec42d | 410 | goto wait; |
28ce5ce6 | 411 | |
ad674e53 | 412 | current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]); |
28ce5ce6 | 413 | dbdma_cmdptr_save(ch); |
ad674e53 | 414 | ch->regs[DBDMA_STATUS] &= ~FLUSH; |
28ce5ce6 AJ |
415 | |
416 | conditional_interrupt(ch); | |
417 | next(ch); | |
418 | ||
b42ec42d | 419 | wait: |
d2f0ce21 | 420 | DBDMA_kick(dbdma_from_ch(ch)); |
28ce5ce6 AJ |
421 | } |
422 | ||
b42ec42d | 423 | static void nop(DBDMA_channel *ch) |
28ce5ce6 AJ |
424 | { |
425 | dbdma_cmd *current = &ch->current; | |
426 | ||
427 | if (conditional_wait(ch)) | |
b42ec42d | 428 | goto wait; |
28ce5ce6 | 429 | |
ad674e53 | 430 | current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]); |
28ce5ce6 AJ |
431 | dbdma_cmdptr_save(ch); |
432 | ||
433 | conditional_interrupt(ch); | |
434 | conditional_branch(ch); | |
435 | ||
b42ec42d | 436 | wait: |
d2f0ce21 | 437 | DBDMA_kick(dbdma_from_ch(ch)); |
3cbee15b JM |
438 | } |
439 | ||
b42ec42d | 440 | static void stop(DBDMA_channel *ch) |
3cbee15b | 441 | { |
77453882 | 442 | ch->regs[DBDMA_STATUS] &= ~(ACTIVE); |
28ce5ce6 AJ |
443 | |
444 | /* the stop command does not increment command pointer */ | |
3cbee15b JM |
445 | } |
446 | ||
b42ec42d | 447 | static void channel_run(DBDMA_channel *ch) |
3cbee15b | 448 | { |
28ce5ce6 AJ |
449 | dbdma_cmd *current = &ch->current; |
450 | uint16_t cmd, key; | |
451 | uint16_t req_count; | |
452 | uint32_t phy_addr; | |
453 | ||
3e49c439 | 454 | DBDMA_DPRINTFCH(ch, "channel_run\n"); |
b7d67813 | 455 | dump_dbdma_cmd(ch, current); |
28ce5ce6 AJ |
456 | |
457 | /* clear WAKE flag at command fetch */ | |
458 | ||
ad674e53 | 459 | ch->regs[DBDMA_STATUS] &= ~WAKE; |
28ce5ce6 AJ |
460 | |
461 | cmd = le16_to_cpu(current->command) & COMMAND_MASK; | |
462 | ||
463 | switch (cmd) { | |
464 | case DBDMA_NOP: | |
b42ec42d | 465 | nop(ch); |
9e232428 | 466 | return; |
28ce5ce6 AJ |
467 | |
468 | case DBDMA_STOP: | |
b42ec42d | 469 | stop(ch); |
9e232428 | 470 | return; |
28ce5ce6 AJ |
471 | } |
472 | ||
473 | key = le16_to_cpu(current->command) & 0x0700; | |
474 | req_count = le16_to_cpu(current->req_count); | |
475 | phy_addr = le32_to_cpu(current->phy_addr); | |
476 | ||
477 | if (key == KEY_STREAM4) { | |
478 | printf("command %x, invalid key 4\n", cmd); | |
479 | kill_channel(ch); | |
b42ec42d | 480 | return; |
28ce5ce6 AJ |
481 | } |
482 | ||
483 | switch (cmd) { | |
484 | case OUTPUT_MORE: | |
77453882 | 485 | DBDMA_DPRINTFCH(ch, "* OUTPUT_MORE *\n"); |
b42ec42d | 486 | start_output(ch, key, phy_addr, req_count, 0); |
9e232428 | 487 | return; |
28ce5ce6 AJ |
488 | |
489 | case OUTPUT_LAST: | |
77453882 | 490 | DBDMA_DPRINTFCH(ch, "* OUTPUT_LAST *\n"); |
b42ec42d | 491 | start_output(ch, key, phy_addr, req_count, 1); |
9e232428 | 492 | return; |
28ce5ce6 AJ |
493 | |
494 | case INPUT_MORE: | |
77453882 | 495 | DBDMA_DPRINTFCH(ch, "* INPUT_MORE *\n"); |
b42ec42d | 496 | start_input(ch, key, phy_addr, req_count, 0); |
9e232428 | 497 | return; |
28ce5ce6 AJ |
498 | |
499 | case INPUT_LAST: | |
77453882 | 500 | DBDMA_DPRINTFCH(ch, "* INPUT_LAST *\n"); |
b42ec42d | 501 | start_input(ch, key, phy_addr, req_count, 1); |
9e232428 | 502 | return; |
28ce5ce6 AJ |
503 | } |
504 | ||
505 | if (key < KEY_REGS) { | |
506 | printf("command %x, invalid key %x\n", cmd, key); | |
507 | key = KEY_SYSTEM; | |
508 | } | |
509 | ||
510 | /* for LOAD_WORD and STORE_WORD, req_count is on 3 bits | |
511 | * and BRANCH is invalid | |
512 | */ | |
513 | ||
514 | req_count = req_count & 0x0007; | |
515 | if (req_count & 0x4) { | |
516 | req_count = 4; | |
517 | phy_addr &= ~3; | |
518 | } else if (req_count & 0x2) { | |
519 | req_count = 2; | |
520 | phy_addr &= ~1; | |
521 | } else | |
522 | req_count = 1; | |
523 | ||
524 | switch (cmd) { | |
525 | case LOAD_WORD: | |
77453882 | 526 | DBDMA_DPRINTFCH(ch, "* LOAD_WORD *\n"); |
b42ec42d | 527 | load_word(ch, key, phy_addr, req_count); |
9e232428 | 528 | return; |
28ce5ce6 AJ |
529 | |
530 | case STORE_WORD: | |
77453882 | 531 | DBDMA_DPRINTFCH(ch, "* STORE_WORD *\n"); |
b42ec42d | 532 | store_word(ch, key, phy_addr, req_count); |
9e232428 | 533 | return; |
28ce5ce6 | 534 | } |
3cbee15b JM |
535 | } |
536 | ||
c20df14b | 537 | static void DBDMA_run(DBDMAState *s) |
28ce5ce6 AJ |
538 | { |
539 | int channel; | |
28ce5ce6 | 540 | |
c20df14b JQ |
541 | for (channel = 0; channel < DBDMA_CHANNELS; channel++) { |
542 | DBDMA_channel *ch = &s->channels[channel]; | |
543 | uint32_t status = ch->regs[DBDMA_STATUS]; | |
03ee3b1e | 544 | if (!ch->io.processing && (status & RUN) && (status & ACTIVE)) { |
c20df14b JQ |
545 | channel_run(ch); |
546 | } | |
28ce5ce6 | 547 | } |
28ce5ce6 AJ |
548 | } |
549 | ||
550 | static void DBDMA_run_bh(void *opaque) | |
551 | { | |
c20df14b | 552 | DBDMAState *s = opaque; |
28ce5ce6 | 553 | |
3e49c439 | 554 | DBDMA_DPRINTF("-> DBDMA_run_bh\n"); |
c20df14b | 555 | DBDMA_run(s); |
3e49c439 | 556 | DBDMA_DPRINTF("<- DBDMA_run_bh\n"); |
28ce5ce6 AJ |
557 | } |
558 | ||
d1e562de AG |
559 | void DBDMA_kick(DBDMAState *dbdma) |
560 | { | |
d2f0ce21 | 561 | qemu_bh_schedule(dbdma->bh); |
d1e562de AG |
562 | } |
563 | ||
28ce5ce6 | 564 | void DBDMA_register_channel(void *dbdma, int nchan, qemu_irq irq, |
862c9280 | 565 | DBDMA_rw rw, DBDMA_flush flush, |
28ce5ce6 AJ |
566 | void *opaque) |
567 | { | |
c20df14b JQ |
568 | DBDMAState *s = dbdma; |
569 | DBDMA_channel *ch = &s->channels[nchan]; | |
28ce5ce6 | 570 | |
3e49c439 | 571 | DBDMA_DPRINTFCH(ch, "DBDMA_register_channel 0x%x\n", nchan); |
28ce5ce6 | 572 | |
2d7d06d8 HP |
573 | assert(rw); |
574 | assert(flush); | |
575 | ||
28ce5ce6 | 576 | ch->irq = irq; |
b42ec42d | 577 | ch->rw = rw; |
862c9280 | 578 | ch->flush = flush; |
28ce5ce6 | 579 | ch->io.opaque = opaque; |
28ce5ce6 AJ |
580 | } |
581 | ||
77453882 | 582 | static void dbdma_control_write(DBDMA_channel *ch) |
28ce5ce6 AJ |
583 | { |
584 | uint16_t mask, value; | |
585 | uint32_t status; | |
77453882 | 586 | bool do_flush = false; |
28ce5ce6 | 587 | |
ad674e53 AJ |
588 | mask = (ch->regs[DBDMA_CONTROL] >> 16) & 0xffff; |
589 | value = ch->regs[DBDMA_CONTROL] & 0xffff; | |
28ce5ce6 | 590 | |
77453882 BH |
591 | /* This is the status register which we'll update |
592 | * appropriately and store back | |
593 | */ | |
ad674e53 | 594 | status = ch->regs[DBDMA_STATUS]; |
28ce5ce6 | 595 | |
77453882 BH |
596 | /* RUN and PAUSE are bits under SW control only |
597 | * FLUSH and WAKE are set by SW and cleared by HW | |
598 | * DEAD, ACTIVE and BT are only under HW control | |
599 | * | |
600 | * We handle ACTIVE separately at the end of the | |
601 | * logic to ensure all cases are covered. | |
602 | */ | |
28ce5ce6 | 603 | |
77453882 BH |
604 | /* Setting RUN will tentatively activate the channel |
605 | */ | |
606 | if ((mask & RUN) && (value & RUN)) { | |
607 | status |= RUN; | |
608 | DBDMA_DPRINTFCH(ch, " Setting RUN !\n"); | |
609 | } | |
610 | ||
611 | /* Clearing RUN 1->0 will stop the channel */ | |
612 | if ((mask & RUN) && !(value & RUN)) { | |
613 | /* This has the side effect of clearing the DEAD bit */ | |
614 | status &= ~(DEAD | RUN); | |
615 | DBDMA_DPRINTFCH(ch, " Clearing RUN !\n"); | |
616 | } | |
617 | ||
618 | /* Setting WAKE wakes up an idle channel if it's running | |
619 | * | |
620 | * Note: The doc doesn't say so but assume that only works | |
621 | * on a channel whose RUN bit is set. | |
622 | * | |
623 | * We set WAKE in status, it's not terribly useful as it will | |
624 | * be cleared on the next command fetch but it seems to mimmic | |
625 | * the HW behaviour and is useful for the way we handle | |
626 | * ACTIVE further down. | |
627 | */ | |
628 | if ((mask & WAKE) && (value & WAKE) && (status & RUN)) { | |
629 | status |= WAKE; | |
630 | DBDMA_DPRINTFCH(ch, " Setting WAKE !\n"); | |
631 | } | |
632 | ||
633 | /* PAUSE being set will deactivate (or prevent activation) | |
634 | * of the channel. We just copy it over for now, ACTIVE will | |
635 | * be re-evaluated later. | |
636 | */ | |
637 | if (mask & PAUSE) { | |
638 | status = (status & ~PAUSE) | (value & PAUSE); | |
639 | DBDMA_DPRINTFCH(ch, " %sing PAUSE !\n", | |
640 | (value & PAUSE) ? "sett" : "clear"); | |
641 | } | |
642 | ||
643 | /* FLUSH is its own thing */ | |
644 | if ((mask & FLUSH) && (value & FLUSH)) { | |
645 | DBDMA_DPRINTFCH(ch, " Setting FLUSH !\n"); | |
646 | /* We set flush directly in the status register, we do *NOT* | |
647 | * set it in "status" so that it gets naturally cleared when | |
648 | * we update the status register further down. That way it | |
649 | * will be set only during the HW flush operation so it is | |
650 | * visible to any completions happening during that time. | |
651 | */ | |
652 | ch->regs[DBDMA_STATUS] |= FLUSH; | |
653 | do_flush = true; | |
28ce5ce6 | 654 | } |
77453882 BH |
655 | |
656 | /* If either RUN or PAUSE is clear, so should ACTIVE be, | |
657 | * otherwise, ACTIVE will be set if we modified RUN, PAUSE or | |
658 | * set WAKE. That means that PAUSE was just cleared, RUN was | |
659 | * just set or WAKE was just set. | |
660 | */ | |
661 | if ((status & PAUSE) || !(status & RUN)) { | |
28ce5ce6 | 662 | status &= ~ACTIVE; |
77453882 BH |
663 | DBDMA_DPRINTFCH(ch, " -> ACTIVE down !\n"); |
664 | ||
665 | /* We stopped processing, we want the underlying HW command | |
666 | * to complete *before* we clear the ACTIVE bit. Otherwise | |
667 | * we can get into a situation where the command status will | |
668 | * have RUN or ACTIVE not set which is going to confuse the | |
669 | * MacOS driver. | |
670 | */ | |
671 | do_flush = true; | |
672 | } else if (mask & (RUN | PAUSE)) { | |
673 | status |= ACTIVE; | |
674 | DBDMA_DPRINTFCH(ch, " -> ACTIVE up !\n"); | |
675 | } else if ((mask & WAKE) && (value & WAKE)) { | |
676 | status |= ACTIVE; | |
677 | DBDMA_DPRINTFCH(ch, " -> ACTIVE up !\n"); | |
1cde732d MCA |
678 | } |
679 | ||
77453882 BH |
680 | DBDMA_DPRINTFCH(ch, " new status=0x%08x\n", status); |
681 | ||
682 | /* If we need to flush the underlying HW, do it now, this happens | |
683 | * both on FLUSH commands and when stopping the channel for safety. | |
684 | */ | |
685 | if (do_flush && ch->flush) { | |
1cde732d | 686 | ch->flush(&ch->io); |
28ce5ce6 AJ |
687 | } |
688 | ||
77453882 | 689 | /* Finally update the status register image */ |
ad674e53 | 690 | ch->regs[DBDMA_STATUS] = status; |
28ce5ce6 | 691 | |
77453882 | 692 | /* If active, make sure the BH gets to run */ |
d2f0ce21 AG |
693 | if (status & ACTIVE) { |
694 | DBDMA_kick(dbdma_from_ch(ch)); | |
695 | } | |
28ce5ce6 AJ |
696 | } |
697 | ||
a8170e5e | 698 | static void dbdma_write(void *opaque, hwaddr addr, |
23c5e4ca | 699 | uint64_t value, unsigned size) |
28ce5ce6 AJ |
700 | { |
701 | int channel = addr >> DBDMA_CHANNEL_SHIFT; | |
c20df14b JQ |
702 | DBDMAState *s = opaque; |
703 | DBDMA_channel *ch = &s->channels[channel]; | |
28ce5ce6 AJ |
704 | int reg = (addr - (channel << DBDMA_CHANNEL_SHIFT)) >> 2; |
705 | ||
3e49c439 MCA |
706 | DBDMA_DPRINTFCH(ch, "writel 0x" TARGET_FMT_plx " <= 0x%08"PRIx64"\n", |
707 | addr, value); | |
708 | DBDMA_DPRINTFCH(ch, "channel 0x%x reg 0x%x\n", | |
709 | (uint32_t)addr >> DBDMA_CHANNEL_SHIFT, reg); | |
28ce5ce6 | 710 | |
7eaba824 | 711 | /* cmdptr cannot be modified if channel is ACTIVE */ |
28ce5ce6 | 712 | |
7eaba824 | 713 | if (reg == DBDMA_CMDPTR_LO && (ch->regs[DBDMA_STATUS] & ACTIVE)) { |
9e232428 | 714 | return; |
7eaba824 | 715 | } |
28ce5ce6 AJ |
716 | |
717 | ch->regs[reg] = value; | |
718 | ||
719 | switch(reg) { | |
720 | case DBDMA_CONTROL: | |
721 | dbdma_control_write(ch); | |
722 | break; | |
723 | case DBDMA_CMDPTR_LO: | |
724 | /* 16-byte aligned */ | |
ad674e53 | 725 | ch->regs[DBDMA_CMDPTR_LO] &= ~0xf; |
28ce5ce6 AJ |
726 | dbdma_cmdptr_load(ch); |
727 | break; | |
728 | case DBDMA_STATUS: | |
729 | case DBDMA_INTR_SEL: | |
730 | case DBDMA_BRANCH_SEL: | |
731 | case DBDMA_WAIT_SEL: | |
732 | /* nothing to do */ | |
733 | break; | |
734 | case DBDMA_XFER_MODE: | |
735 | case DBDMA_CMDPTR_HI: | |
736 | case DBDMA_DATA2PTR_HI: | |
737 | case DBDMA_DATA2PTR_LO: | |
738 | case DBDMA_ADDRESS_HI: | |
739 | case DBDMA_BRANCH_ADDR_HI: | |
740 | case DBDMA_RES1: | |
741 | case DBDMA_RES2: | |
742 | case DBDMA_RES3: | |
743 | case DBDMA_RES4: | |
744 | /* unused */ | |
745 | break; | |
746 | } | |
747 | } | |
748 | ||
a8170e5e | 749 | static uint64_t dbdma_read(void *opaque, hwaddr addr, |
23c5e4ca | 750 | unsigned size) |
3cbee15b | 751 | { |
28ce5ce6 AJ |
752 | uint32_t value; |
753 | int channel = addr >> DBDMA_CHANNEL_SHIFT; | |
c20df14b JQ |
754 | DBDMAState *s = opaque; |
755 | DBDMA_channel *ch = &s->channels[channel]; | |
28ce5ce6 | 756 | int reg = (addr - (channel << DBDMA_CHANNEL_SHIFT)) >> 2; |
ea026b2f | 757 | |
28ce5ce6 AJ |
758 | value = ch->regs[reg]; |
759 | ||
28ce5ce6 AJ |
760 | switch(reg) { |
761 | case DBDMA_CONTROL: | |
77453882 | 762 | value = ch->regs[DBDMA_STATUS]; |
28ce5ce6 AJ |
763 | break; |
764 | case DBDMA_STATUS: | |
765 | case DBDMA_CMDPTR_LO: | |
766 | case DBDMA_INTR_SEL: | |
767 | case DBDMA_BRANCH_SEL: | |
768 | case DBDMA_WAIT_SEL: | |
769 | /* nothing to do */ | |
770 | break; | |
771 | case DBDMA_XFER_MODE: | |
772 | case DBDMA_CMDPTR_HI: | |
773 | case DBDMA_DATA2PTR_HI: | |
774 | case DBDMA_DATA2PTR_LO: | |
775 | case DBDMA_ADDRESS_HI: | |
776 | case DBDMA_BRANCH_ADDR_HI: | |
777 | /* unused */ | |
778 | value = 0; | |
779 | break; | |
780 | case DBDMA_RES1: | |
781 | case DBDMA_RES2: | |
782 | case DBDMA_RES3: | |
783 | case DBDMA_RES4: | |
784 | /* reserved */ | |
785 | break; | |
786 | } | |
787 | ||
77453882 BH |
788 | DBDMA_DPRINTFCH(ch, "readl 0x" TARGET_FMT_plx " => 0x%08x\n", addr, value); |
789 | DBDMA_DPRINTFCH(ch, "channel 0x%x reg 0x%x\n", | |
790 | (uint32_t)addr >> DBDMA_CHANNEL_SHIFT, reg); | |
791 | ||
28ce5ce6 | 792 | return value; |
3cbee15b JM |
793 | } |
794 | ||
23c5e4ca AK |
795 | static const MemoryRegionOps dbdma_ops = { |
796 | .read = dbdma_read, | |
797 | .write = dbdma_write, | |
798 | .endianness = DEVICE_LITTLE_ENDIAN, | |
799 | .valid = { | |
800 | .min_access_size = 4, | |
801 | .max_access_size = 4, | |
802 | }, | |
3cbee15b JM |
803 | }; |
804 | ||
627be2f2 MCA |
805 | static const VMStateDescription vmstate_dbdma_io = { |
806 | .name = "dbdma_io", | |
807 | .version_id = 0, | |
808 | .minimum_version_id = 0, | |
809 | .fields = (VMStateField[]) { | |
810 | VMSTATE_UINT64(addr, struct DBDMA_io), | |
811 | VMSTATE_INT32(len, struct DBDMA_io), | |
812 | VMSTATE_INT32(is_last, struct DBDMA_io), | |
813 | VMSTATE_INT32(is_dma_out, struct DBDMA_io), | |
814 | VMSTATE_BOOL(processing, struct DBDMA_io), | |
815 | VMSTATE_END_OF_LIST() | |
816 | } | |
817 | }; | |
818 | ||
819 | static const VMStateDescription vmstate_dbdma_cmd = { | |
820 | .name = "dbdma_cmd", | |
da26fdc3 JQ |
821 | .version_id = 0, |
822 | .minimum_version_id = 0, | |
627be2f2 MCA |
823 | .fields = (VMStateField[]) { |
824 | VMSTATE_UINT16(req_count, dbdma_cmd), | |
825 | VMSTATE_UINT16(command, dbdma_cmd), | |
826 | VMSTATE_UINT32(phy_addr, dbdma_cmd), | |
827 | VMSTATE_UINT32(cmd_dep, dbdma_cmd), | |
828 | VMSTATE_UINT16(res_count, dbdma_cmd), | |
829 | VMSTATE_UINT16(xfer_status, dbdma_cmd), | |
830 | VMSTATE_END_OF_LIST() | |
831 | } | |
832 | }; | |
833 | ||
834 | static const VMStateDescription vmstate_dbdma_channel = { | |
835 | .name = "dbdma_channel", | |
836 | .version_id = 1, | |
837 | .minimum_version_id = 1, | |
35d08458 | 838 | .fields = (VMStateField[]) { |
da26fdc3 | 839 | VMSTATE_UINT32_ARRAY(regs, struct DBDMA_channel, DBDMA_REGS), |
627be2f2 MCA |
840 | VMSTATE_STRUCT(io, struct DBDMA_channel, 0, vmstate_dbdma_io, DBDMA_io), |
841 | VMSTATE_STRUCT(current, struct DBDMA_channel, 0, vmstate_dbdma_cmd, | |
842 | dbdma_cmd), | |
da26fdc3 JQ |
843 | VMSTATE_END_OF_LIST() |
844 | } | |
845 | }; | |
28ce5ce6 | 846 | |
da26fdc3 JQ |
847 | static const VMStateDescription vmstate_dbdma = { |
848 | .name = "dbdma", | |
627be2f2 MCA |
849 | .version_id = 3, |
850 | .minimum_version_id = 3, | |
35d08458 | 851 | .fields = (VMStateField[]) { |
da26fdc3 JQ |
852 | VMSTATE_STRUCT_ARRAY(channels, DBDMAState, DBDMA_CHANNELS, 1, |
853 | vmstate_dbdma_channel, DBDMA_channel), | |
854 | VMSTATE_END_OF_LIST() | |
855 | } | |
856 | }; | |
9b64997f | 857 | |
1d27f351 | 858 | static void mac_dbdma_reset(DeviceState *d) |
6e6b7363 | 859 | { |
1d27f351 | 860 | DBDMAState *s = MAC_DBDMA(d); |
28ce5ce6 AJ |
861 | int i; |
862 | ||
1d27f351 | 863 | for (i = 0; i < DBDMA_CHANNELS; i++) { |
c20df14b | 864 | memset(s->channels[i].regs, 0, DBDMA_SIZE); |
1d27f351 | 865 | } |
6e6b7363 BS |
866 | } |
867 | ||
2d7d06d8 | 868 | static void dbdma_unassigned_rw(DBDMA_io *io) |
2d7d06d8 HP |
869 | { |
870 | DBDMA_channel *ch = io->channel; | |
89499390 MCA |
871 | dbdma_cmd *current = &ch->current; |
872 | uint16_t cmd; | |
2d7d06d8 HP |
873 | qemu_log_mask(LOG_GUEST_ERROR, "%s: use of unassigned channel %d\n", |
874 | __func__, ch->channel); | |
77453882 | 875 | ch->io.processing = false; |
89499390 MCA |
876 | |
877 | cmd = le16_to_cpu(current->command) & COMMAND_MASK; | |
878 | if (cmd == OUTPUT_MORE || cmd == OUTPUT_LAST || | |
879 | cmd == INPUT_MORE || cmd == INPUT_LAST) { | |
77453882 | 880 | current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]); |
89499390 MCA |
881 | current->res_count = cpu_to_le16(io->len); |
882 | dbdma_cmdptr_save(ch); | |
883 | } | |
2d7d06d8 HP |
884 | } |
885 | ||
77453882 BH |
886 | static void dbdma_unassigned_flush(DBDMA_io *io) |
887 | { | |
888 | DBDMA_channel *ch = io->channel; | |
889 | qemu_log_mask(LOG_GUEST_ERROR, "%s: use of unassigned channel %d\n", | |
890 | __func__, ch->channel); | |
891 | } | |
892 | ||
1d27f351 MCA |
893 | static void mac_dbdma_init(Object *obj) |
894 | { | |
895 | SysBusDevice *sbd = SYS_BUS_DEVICE(obj); | |
896 | DBDMAState *s = MAC_DBDMA(obj); | |
897 | int i; | |
28ce5ce6 | 898 | |
3e300fa6 | 899 | for (i = 0; i < DBDMA_CHANNELS; i++) { |
2d7d06d8 | 900 | DBDMA_channel *ch = &s->channels[i]; |
2d7d06d8 HP |
901 | |
902 | ch->rw = dbdma_unassigned_rw; | |
903 | ch->flush = dbdma_unassigned_flush; | |
904 | ch->channel = i; | |
905 | ch->io.channel = ch; | |
3e300fa6 AG |
906 | } |
907 | ||
1d27f351 MCA |
908 | memory_region_init_io(&s->mem, obj, &dbdma_ops, s, "dbdma", 0x1000); |
909 | sysbus_init_mmio(sbd, &s->mem); | |
910 | } | |
911 | ||
912 | static void mac_dbdma_realize(DeviceState *dev, Error **errp) | |
913 | { | |
914 | DBDMAState *s = MAC_DBDMA(dev); | |
28ce5ce6 | 915 | |
d2f0ce21 | 916 | s->bh = qemu_bh_new(DBDMA_run_bh, s); |
1d27f351 | 917 | } |
28ce5ce6 | 918 | |
1d27f351 MCA |
919 | static void mac_dbdma_class_init(ObjectClass *oc, void *data) |
920 | { | |
921 | DeviceClass *dc = DEVICE_CLASS(oc); | |
922 | ||
923 | dc->realize = mac_dbdma_realize; | |
924 | dc->reset = mac_dbdma_reset; | |
925 | dc->vmsd = &vmstate_dbdma; | |
3cbee15b | 926 | } |
1d27f351 MCA |
927 | |
928 | static const TypeInfo mac_dbdma_type_info = { | |
929 | .name = TYPE_MAC_DBDMA, | |
930 | .parent = TYPE_SYS_BUS_DEVICE, | |
931 | .instance_size = sizeof(DBDMAState), | |
932 | .instance_init = mac_dbdma_init, | |
933 | .class_init = mac_dbdma_class_init | |
934 | }; | |
935 | ||
936 | static void mac_dbdma_register_types(void) | |
937 | { | |
938 | type_register_static(&mac_dbdma_type_info); | |
939 | } | |
940 | ||
941 | type_init(mac_dbdma_register_types) |