xref: /openbmc/qemu/hw/misc/macio/mac_dbdma.c (revision 49ab747f668f421138d5b40d83fa279c4c5e278d)
1*49ab747fSPaolo Bonzini /*
2*49ab747fSPaolo Bonzini  * PowerMac descriptor-based DMA emulation
3*49ab747fSPaolo Bonzini  *
4*49ab747fSPaolo Bonzini  * Copyright (c) 2005-2007 Fabrice Bellard
5*49ab747fSPaolo Bonzini  * Copyright (c) 2007 Jocelyn Mayer
6*49ab747fSPaolo Bonzini  * Copyright (c) 2009 Laurent Vivier
7*49ab747fSPaolo Bonzini  *
8*49ab747fSPaolo Bonzini  * some parts from linux-2.6.28, arch/powerpc/include/asm/dbdma.h
9*49ab747fSPaolo Bonzini  *
10*49ab747fSPaolo Bonzini  *   Definitions for using the Apple Descriptor-Based DMA controller
11*49ab747fSPaolo Bonzini  *   in Power Macintosh computers.
12*49ab747fSPaolo Bonzini  *
13*49ab747fSPaolo Bonzini  *   Copyright (C) 1996 Paul Mackerras.
14*49ab747fSPaolo Bonzini  *
15*49ab747fSPaolo Bonzini  * some parts from mol 0.9.71
16*49ab747fSPaolo Bonzini  *
17*49ab747fSPaolo Bonzini  *   Descriptor based DMA emulation
18*49ab747fSPaolo Bonzini  *
19*49ab747fSPaolo Bonzini  *   Copyright (C) 1998-2004 Samuel Rydh (samuel@ibrium.se)
20*49ab747fSPaolo Bonzini  *
21*49ab747fSPaolo Bonzini  * Permission is hereby granted, free of charge, to any person obtaining a copy
22*49ab747fSPaolo Bonzini  * of this software and associated documentation files (the "Software"), to deal
23*49ab747fSPaolo Bonzini  * in the Software without restriction, including without limitation the rights
24*49ab747fSPaolo Bonzini  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
25*49ab747fSPaolo Bonzini  * copies of the Software, and to permit persons to whom the Software is
26*49ab747fSPaolo Bonzini  * furnished to do so, subject to the following conditions:
27*49ab747fSPaolo Bonzini  *
28*49ab747fSPaolo Bonzini  * The above copyright notice and this permission notice shall be included in
29*49ab747fSPaolo Bonzini  * all copies or substantial portions of the Software.
30*49ab747fSPaolo Bonzini  *
31*49ab747fSPaolo Bonzini  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
32*49ab747fSPaolo Bonzini  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33*49ab747fSPaolo Bonzini  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
34*49ab747fSPaolo Bonzini  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
35*49ab747fSPaolo Bonzini  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
36*49ab747fSPaolo Bonzini  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
37*49ab747fSPaolo Bonzini  * THE SOFTWARE.
38*49ab747fSPaolo Bonzini  */
39*49ab747fSPaolo Bonzini #include "hw/hw.h"
40*49ab747fSPaolo Bonzini #include "hw/isa/isa.h"
41*49ab747fSPaolo Bonzini #include "hw/ppc/mac_dbdma.h"
42*49ab747fSPaolo Bonzini #include "qemu/main-loop.h"
43*49ab747fSPaolo Bonzini 
44*49ab747fSPaolo Bonzini /* debug DBDMA */
45*49ab747fSPaolo Bonzini //#define DEBUG_DBDMA
46*49ab747fSPaolo Bonzini 
47*49ab747fSPaolo Bonzini #ifdef DEBUG_DBDMA
48*49ab747fSPaolo Bonzini #define DBDMA_DPRINTF(fmt, ...)                                 \
49*49ab747fSPaolo Bonzini     do { printf("DBDMA: " fmt , ## __VA_ARGS__); } while (0)
50*49ab747fSPaolo Bonzini #else
51*49ab747fSPaolo Bonzini #define DBDMA_DPRINTF(fmt, ...)
52*49ab747fSPaolo Bonzini #endif
53*49ab747fSPaolo Bonzini 
54*49ab747fSPaolo Bonzini /*
55*49ab747fSPaolo Bonzini  */
56*49ab747fSPaolo Bonzini 
57*49ab747fSPaolo Bonzini /*
58*49ab747fSPaolo Bonzini  * DBDMA control/status registers.  All little-endian.
59*49ab747fSPaolo Bonzini  */
60*49ab747fSPaolo Bonzini 
61*49ab747fSPaolo Bonzini #define DBDMA_CONTROL         0x00
62*49ab747fSPaolo Bonzini #define DBDMA_STATUS          0x01
63*49ab747fSPaolo Bonzini #define DBDMA_CMDPTR_HI       0x02
64*49ab747fSPaolo Bonzini #define DBDMA_CMDPTR_LO       0x03
65*49ab747fSPaolo Bonzini #define DBDMA_INTR_SEL        0x04
66*49ab747fSPaolo Bonzini #define DBDMA_BRANCH_SEL      0x05
67*49ab747fSPaolo Bonzini #define DBDMA_WAIT_SEL        0x06
68*49ab747fSPaolo Bonzini #define DBDMA_XFER_MODE       0x07
69*49ab747fSPaolo Bonzini #define DBDMA_DATA2PTR_HI     0x08
70*49ab747fSPaolo Bonzini #define DBDMA_DATA2PTR_LO     0x09
71*49ab747fSPaolo Bonzini #define DBDMA_RES1            0x0A
72*49ab747fSPaolo Bonzini #define DBDMA_ADDRESS_HI      0x0B
73*49ab747fSPaolo Bonzini #define DBDMA_BRANCH_ADDR_HI  0x0C
74*49ab747fSPaolo Bonzini #define DBDMA_RES2            0x0D
75*49ab747fSPaolo Bonzini #define DBDMA_RES3            0x0E
76*49ab747fSPaolo Bonzini #define DBDMA_RES4            0x0F
77*49ab747fSPaolo Bonzini 
78*49ab747fSPaolo Bonzini #define DBDMA_REGS            16
79*49ab747fSPaolo Bonzini #define DBDMA_SIZE            (DBDMA_REGS * sizeof(uint32_t))
80*49ab747fSPaolo Bonzini 
81*49ab747fSPaolo Bonzini #define DBDMA_CHANNEL_SHIFT   7
82*49ab747fSPaolo Bonzini #define DBDMA_CHANNEL_SIZE    (1 << DBDMA_CHANNEL_SHIFT)
83*49ab747fSPaolo Bonzini 
84*49ab747fSPaolo Bonzini #define DBDMA_CHANNELS        (0x1000 >> DBDMA_CHANNEL_SHIFT)
85*49ab747fSPaolo Bonzini 
86*49ab747fSPaolo Bonzini /* Bits in control and status registers */
87*49ab747fSPaolo Bonzini 
88*49ab747fSPaolo Bonzini #define RUN	0x8000
89*49ab747fSPaolo Bonzini #define PAUSE	0x4000
90*49ab747fSPaolo Bonzini #define FLUSH	0x2000
91*49ab747fSPaolo Bonzini #define WAKE	0x1000
92*49ab747fSPaolo Bonzini #define DEAD	0x0800
93*49ab747fSPaolo Bonzini #define ACTIVE	0x0400
94*49ab747fSPaolo Bonzini #define BT	0x0100
95*49ab747fSPaolo Bonzini #define DEVSTAT	0x00ff
96*49ab747fSPaolo Bonzini 
97*49ab747fSPaolo Bonzini /*
98*49ab747fSPaolo Bonzini  * DBDMA command structure.  These fields are all little-endian!
99*49ab747fSPaolo Bonzini  */
100*49ab747fSPaolo Bonzini 
101*49ab747fSPaolo Bonzini typedef struct dbdma_cmd {
102*49ab747fSPaolo Bonzini     uint16_t req_count;	  /* requested byte transfer count */
103*49ab747fSPaolo Bonzini     uint16_t command;	  /* command word (has bit-fields) */
104*49ab747fSPaolo Bonzini     uint32_t phy_addr;	  /* physical data address */
105*49ab747fSPaolo Bonzini     uint32_t cmd_dep;	  /* command-dependent field */
106*49ab747fSPaolo Bonzini     uint16_t res_count;	  /* residual count after completion */
107*49ab747fSPaolo Bonzini     uint16_t xfer_status; /* transfer status */
108*49ab747fSPaolo Bonzini } dbdma_cmd;
109*49ab747fSPaolo Bonzini 
110*49ab747fSPaolo Bonzini /* DBDMA command values in command field */
111*49ab747fSPaolo Bonzini 
112*49ab747fSPaolo Bonzini #define COMMAND_MASK    0xf000
113*49ab747fSPaolo Bonzini #define OUTPUT_MORE	0x0000	/* transfer memory data to stream */
114*49ab747fSPaolo Bonzini #define OUTPUT_LAST	0x1000	/* ditto followed by end marker */
115*49ab747fSPaolo Bonzini #define INPUT_MORE	0x2000	/* transfer stream data to memory */
116*49ab747fSPaolo Bonzini #define INPUT_LAST	0x3000	/* ditto, expect end marker */
117*49ab747fSPaolo Bonzini #define STORE_WORD	0x4000	/* write word (4 bytes) to device reg */
118*49ab747fSPaolo Bonzini #define LOAD_WORD	0x5000	/* read word (4 bytes) from device reg */
119*49ab747fSPaolo Bonzini #define DBDMA_NOP	0x6000	/* do nothing */
120*49ab747fSPaolo Bonzini #define DBDMA_STOP	0x7000	/* suspend processing */
121*49ab747fSPaolo Bonzini 
122*49ab747fSPaolo Bonzini /* Key values in command field */
123*49ab747fSPaolo Bonzini 
124*49ab747fSPaolo Bonzini #define KEY_MASK        0x0700
125*49ab747fSPaolo Bonzini #define KEY_STREAM0	0x0000	/* usual data stream */
126*49ab747fSPaolo Bonzini #define KEY_STREAM1	0x0100	/* control/status stream */
127*49ab747fSPaolo Bonzini #define KEY_STREAM2	0x0200	/* device-dependent stream */
128*49ab747fSPaolo Bonzini #define KEY_STREAM3	0x0300	/* device-dependent stream */
129*49ab747fSPaolo Bonzini #define KEY_STREAM4	0x0400	/* reserved */
130*49ab747fSPaolo Bonzini #define KEY_REGS	0x0500	/* device register space */
131*49ab747fSPaolo Bonzini #define KEY_SYSTEM	0x0600	/* system memory-mapped space */
132*49ab747fSPaolo Bonzini #define KEY_DEVICE	0x0700	/* device memory-mapped space */
133*49ab747fSPaolo Bonzini 
134*49ab747fSPaolo Bonzini /* Interrupt control values in command field */
135*49ab747fSPaolo Bonzini 
136*49ab747fSPaolo Bonzini #define INTR_MASK       0x0030
137*49ab747fSPaolo Bonzini #define INTR_NEVER	0x0000	/* don't interrupt */
138*49ab747fSPaolo Bonzini #define INTR_IFSET	0x0010	/* intr if condition bit is 1 */
139*49ab747fSPaolo Bonzini #define INTR_IFCLR	0x0020	/* intr if condition bit is 0 */
140*49ab747fSPaolo Bonzini #define INTR_ALWAYS	0x0030	/* always interrupt */
141*49ab747fSPaolo Bonzini 
142*49ab747fSPaolo Bonzini /* Branch control values in command field */
143*49ab747fSPaolo Bonzini 
144*49ab747fSPaolo Bonzini #define BR_MASK         0x000c
145*49ab747fSPaolo Bonzini #define BR_NEVER	0x0000	/* don't branch */
146*49ab747fSPaolo Bonzini #define BR_IFSET	0x0004	/* branch if condition bit is 1 */
147*49ab747fSPaolo Bonzini #define BR_IFCLR	0x0008	/* branch if condition bit is 0 */
148*49ab747fSPaolo Bonzini #define BR_ALWAYS	0x000c	/* always branch */
149*49ab747fSPaolo Bonzini 
150*49ab747fSPaolo Bonzini /* Wait control values in command field */
151*49ab747fSPaolo Bonzini 
152*49ab747fSPaolo Bonzini #define WAIT_MASK       0x0003
153*49ab747fSPaolo Bonzini #define WAIT_NEVER	0x0000	/* don't wait */
154*49ab747fSPaolo Bonzini #define WAIT_IFSET	0x0001	/* wait if condition bit is 1 */
155*49ab747fSPaolo Bonzini #define WAIT_IFCLR	0x0002	/* wait if condition bit is 0 */
156*49ab747fSPaolo Bonzini #define WAIT_ALWAYS	0x0003	/* always wait */
157*49ab747fSPaolo Bonzini 
158*49ab747fSPaolo Bonzini typedef struct DBDMA_channel {
159*49ab747fSPaolo Bonzini     int channel;
160*49ab747fSPaolo Bonzini     uint32_t regs[DBDMA_REGS];
161*49ab747fSPaolo Bonzini     qemu_irq irq;
162*49ab747fSPaolo Bonzini     DBDMA_io io;
163*49ab747fSPaolo Bonzini     DBDMA_rw rw;
164*49ab747fSPaolo Bonzini     DBDMA_flush flush;
165*49ab747fSPaolo Bonzini     dbdma_cmd current;
166*49ab747fSPaolo Bonzini     int processing;
167*49ab747fSPaolo Bonzini } DBDMA_channel;
168*49ab747fSPaolo Bonzini 
169*49ab747fSPaolo Bonzini typedef struct {
170*49ab747fSPaolo Bonzini     MemoryRegion mem;
171*49ab747fSPaolo Bonzini     DBDMA_channel channels[DBDMA_CHANNELS];
172*49ab747fSPaolo Bonzini } DBDMAState;
173*49ab747fSPaolo Bonzini 
174*49ab747fSPaolo Bonzini #ifdef DEBUG_DBDMA
175*49ab747fSPaolo Bonzini static void dump_dbdma_cmd(dbdma_cmd *cmd)
176*49ab747fSPaolo Bonzini {
177*49ab747fSPaolo Bonzini     printf("dbdma_cmd %p\n", cmd);
178*49ab747fSPaolo Bonzini     printf("    req_count 0x%04x\n", le16_to_cpu(cmd->req_count));
179*49ab747fSPaolo Bonzini     printf("    command 0x%04x\n", le16_to_cpu(cmd->command));
180*49ab747fSPaolo Bonzini     printf("    phy_addr 0x%08x\n", le32_to_cpu(cmd->phy_addr));
181*49ab747fSPaolo Bonzini     printf("    cmd_dep 0x%08x\n", le32_to_cpu(cmd->cmd_dep));
182*49ab747fSPaolo Bonzini     printf("    res_count 0x%04x\n", le16_to_cpu(cmd->res_count));
183*49ab747fSPaolo Bonzini     printf("    xfer_status 0x%04x\n", le16_to_cpu(cmd->xfer_status));
184*49ab747fSPaolo Bonzini }
185*49ab747fSPaolo Bonzini #else
186*49ab747fSPaolo Bonzini static void dump_dbdma_cmd(dbdma_cmd *cmd)
187*49ab747fSPaolo Bonzini {
188*49ab747fSPaolo Bonzini }
189*49ab747fSPaolo Bonzini #endif
190*49ab747fSPaolo Bonzini static void dbdma_cmdptr_load(DBDMA_channel *ch)
191*49ab747fSPaolo Bonzini {
192*49ab747fSPaolo Bonzini     DBDMA_DPRINTF("dbdma_cmdptr_load 0x%08x\n",
193*49ab747fSPaolo Bonzini                   ch->regs[DBDMA_CMDPTR_LO]);
194*49ab747fSPaolo Bonzini     cpu_physical_memory_read(ch->regs[DBDMA_CMDPTR_LO],
195*49ab747fSPaolo Bonzini                              (uint8_t*)&ch->current, sizeof(dbdma_cmd));
196*49ab747fSPaolo Bonzini }
197*49ab747fSPaolo Bonzini 
198*49ab747fSPaolo Bonzini static void dbdma_cmdptr_save(DBDMA_channel *ch)
199*49ab747fSPaolo Bonzini {
200*49ab747fSPaolo Bonzini     DBDMA_DPRINTF("dbdma_cmdptr_save 0x%08x\n",
201*49ab747fSPaolo Bonzini                   ch->regs[DBDMA_CMDPTR_LO]);
202*49ab747fSPaolo Bonzini     DBDMA_DPRINTF("xfer_status 0x%08x res_count 0x%04x\n",
203*49ab747fSPaolo Bonzini                   le16_to_cpu(ch->current.xfer_status),
204*49ab747fSPaolo Bonzini                   le16_to_cpu(ch->current.res_count));
205*49ab747fSPaolo Bonzini     cpu_physical_memory_write(ch->regs[DBDMA_CMDPTR_LO],
206*49ab747fSPaolo Bonzini                               (uint8_t*)&ch->current, sizeof(dbdma_cmd));
207*49ab747fSPaolo Bonzini }
208*49ab747fSPaolo Bonzini 
209*49ab747fSPaolo Bonzini static void kill_channel(DBDMA_channel *ch)
210*49ab747fSPaolo Bonzini {
211*49ab747fSPaolo Bonzini     DBDMA_DPRINTF("kill_channel\n");
212*49ab747fSPaolo Bonzini 
213*49ab747fSPaolo Bonzini     ch->regs[DBDMA_STATUS] |= DEAD;
214*49ab747fSPaolo Bonzini     ch->regs[DBDMA_STATUS] &= ~ACTIVE;
215*49ab747fSPaolo Bonzini 
216*49ab747fSPaolo Bonzini     qemu_irq_raise(ch->irq);
217*49ab747fSPaolo Bonzini }
218*49ab747fSPaolo Bonzini 
219*49ab747fSPaolo Bonzini static void conditional_interrupt(DBDMA_channel *ch)
220*49ab747fSPaolo Bonzini {
221*49ab747fSPaolo Bonzini     dbdma_cmd *current = &ch->current;
222*49ab747fSPaolo Bonzini     uint16_t intr;
223*49ab747fSPaolo Bonzini     uint16_t sel_mask, sel_value;
224*49ab747fSPaolo Bonzini     uint32_t status;
225*49ab747fSPaolo Bonzini     int cond;
226*49ab747fSPaolo Bonzini 
227*49ab747fSPaolo Bonzini     DBDMA_DPRINTF("conditional_interrupt\n");
228*49ab747fSPaolo Bonzini 
229*49ab747fSPaolo Bonzini     intr = le16_to_cpu(current->command) & INTR_MASK;
230*49ab747fSPaolo Bonzini 
231*49ab747fSPaolo Bonzini     switch(intr) {
232*49ab747fSPaolo Bonzini     case INTR_NEVER:  /* don't interrupt */
233*49ab747fSPaolo Bonzini         return;
234*49ab747fSPaolo Bonzini     case INTR_ALWAYS: /* always interrupt */
235*49ab747fSPaolo Bonzini         qemu_irq_raise(ch->irq);
236*49ab747fSPaolo Bonzini         return;
237*49ab747fSPaolo Bonzini     }
238*49ab747fSPaolo Bonzini 
239*49ab747fSPaolo Bonzini     status = ch->regs[DBDMA_STATUS] & DEVSTAT;
240*49ab747fSPaolo Bonzini 
241*49ab747fSPaolo Bonzini     sel_mask = (ch->regs[DBDMA_INTR_SEL] >> 16) & 0x0f;
242*49ab747fSPaolo Bonzini     sel_value = ch->regs[DBDMA_INTR_SEL] & 0x0f;
243*49ab747fSPaolo Bonzini 
244*49ab747fSPaolo Bonzini     cond = (status & sel_mask) == (sel_value & sel_mask);
245*49ab747fSPaolo Bonzini 
246*49ab747fSPaolo Bonzini     switch(intr) {
247*49ab747fSPaolo Bonzini     case INTR_IFSET:  /* intr if condition bit is 1 */
248*49ab747fSPaolo Bonzini         if (cond)
249*49ab747fSPaolo Bonzini             qemu_irq_raise(ch->irq);
250*49ab747fSPaolo Bonzini         return;
251*49ab747fSPaolo Bonzini     case INTR_IFCLR:  /* intr if condition bit is 0 */
252*49ab747fSPaolo Bonzini         if (!cond)
253*49ab747fSPaolo Bonzini             qemu_irq_raise(ch->irq);
254*49ab747fSPaolo Bonzini         return;
255*49ab747fSPaolo Bonzini     }
256*49ab747fSPaolo Bonzini }
257*49ab747fSPaolo Bonzini 
258*49ab747fSPaolo Bonzini static int conditional_wait(DBDMA_channel *ch)
259*49ab747fSPaolo Bonzini {
260*49ab747fSPaolo Bonzini     dbdma_cmd *current = &ch->current;
261*49ab747fSPaolo Bonzini     uint16_t wait;
262*49ab747fSPaolo Bonzini     uint16_t sel_mask, sel_value;
263*49ab747fSPaolo Bonzini     uint32_t status;
264*49ab747fSPaolo Bonzini     int cond;
265*49ab747fSPaolo Bonzini 
266*49ab747fSPaolo Bonzini     DBDMA_DPRINTF("conditional_wait\n");
267*49ab747fSPaolo Bonzini 
268*49ab747fSPaolo Bonzini     wait = le16_to_cpu(current->command) & WAIT_MASK;
269*49ab747fSPaolo Bonzini 
270*49ab747fSPaolo Bonzini     switch(wait) {
271*49ab747fSPaolo Bonzini     case WAIT_NEVER:  /* don't wait */
272*49ab747fSPaolo Bonzini         return 0;
273*49ab747fSPaolo Bonzini     case WAIT_ALWAYS: /* always wait */
274*49ab747fSPaolo Bonzini         return 1;
275*49ab747fSPaolo Bonzini     }
276*49ab747fSPaolo Bonzini 
277*49ab747fSPaolo Bonzini     status = ch->regs[DBDMA_STATUS] & DEVSTAT;
278*49ab747fSPaolo Bonzini 
279*49ab747fSPaolo Bonzini     sel_mask = (ch->regs[DBDMA_WAIT_SEL] >> 16) & 0x0f;
280*49ab747fSPaolo Bonzini     sel_value = ch->regs[DBDMA_WAIT_SEL] & 0x0f;
281*49ab747fSPaolo Bonzini 
282*49ab747fSPaolo Bonzini     cond = (status & sel_mask) == (sel_value & sel_mask);
283*49ab747fSPaolo Bonzini 
284*49ab747fSPaolo Bonzini     switch(wait) {
285*49ab747fSPaolo Bonzini     case WAIT_IFSET:  /* wait if condition bit is 1 */
286*49ab747fSPaolo Bonzini         if (cond)
287*49ab747fSPaolo Bonzini             return 1;
288*49ab747fSPaolo Bonzini         return 0;
289*49ab747fSPaolo Bonzini     case WAIT_IFCLR:  /* wait if condition bit is 0 */
290*49ab747fSPaolo Bonzini         if (!cond)
291*49ab747fSPaolo Bonzini             return 1;
292*49ab747fSPaolo Bonzini         return 0;
293*49ab747fSPaolo Bonzini     }
294*49ab747fSPaolo Bonzini     return 0;
295*49ab747fSPaolo Bonzini }
296*49ab747fSPaolo Bonzini 
297*49ab747fSPaolo Bonzini static void next(DBDMA_channel *ch)
298*49ab747fSPaolo Bonzini {
299*49ab747fSPaolo Bonzini     uint32_t cp;
300*49ab747fSPaolo Bonzini 
301*49ab747fSPaolo Bonzini     ch->regs[DBDMA_STATUS] &= ~BT;
302*49ab747fSPaolo Bonzini 
303*49ab747fSPaolo Bonzini     cp = ch->regs[DBDMA_CMDPTR_LO];
304*49ab747fSPaolo Bonzini     ch->regs[DBDMA_CMDPTR_LO] = cp + sizeof(dbdma_cmd);
305*49ab747fSPaolo Bonzini     dbdma_cmdptr_load(ch);
306*49ab747fSPaolo Bonzini }
307*49ab747fSPaolo Bonzini 
308*49ab747fSPaolo Bonzini static void branch(DBDMA_channel *ch)
309*49ab747fSPaolo Bonzini {
310*49ab747fSPaolo Bonzini     dbdma_cmd *current = &ch->current;
311*49ab747fSPaolo Bonzini 
312*49ab747fSPaolo Bonzini     ch->regs[DBDMA_CMDPTR_LO] = current->cmd_dep;
313*49ab747fSPaolo Bonzini     ch->regs[DBDMA_STATUS] |= BT;
314*49ab747fSPaolo Bonzini     dbdma_cmdptr_load(ch);
315*49ab747fSPaolo Bonzini }
316*49ab747fSPaolo Bonzini 
317*49ab747fSPaolo Bonzini static void conditional_branch(DBDMA_channel *ch)
318*49ab747fSPaolo Bonzini {
319*49ab747fSPaolo Bonzini     dbdma_cmd *current = &ch->current;
320*49ab747fSPaolo Bonzini     uint16_t br;
321*49ab747fSPaolo Bonzini     uint16_t sel_mask, sel_value;
322*49ab747fSPaolo Bonzini     uint32_t status;
323*49ab747fSPaolo Bonzini     int cond;
324*49ab747fSPaolo Bonzini 
325*49ab747fSPaolo Bonzini     DBDMA_DPRINTF("conditional_branch\n");
326*49ab747fSPaolo Bonzini 
327*49ab747fSPaolo Bonzini     /* check if we must branch */
328*49ab747fSPaolo Bonzini 
329*49ab747fSPaolo Bonzini     br = le16_to_cpu(current->command) & BR_MASK;
330*49ab747fSPaolo Bonzini 
331*49ab747fSPaolo Bonzini     switch(br) {
332*49ab747fSPaolo Bonzini     case BR_NEVER:  /* don't branch */
333*49ab747fSPaolo Bonzini         next(ch);
334*49ab747fSPaolo Bonzini         return;
335*49ab747fSPaolo Bonzini     case BR_ALWAYS: /* always branch */
336*49ab747fSPaolo Bonzini         branch(ch);
337*49ab747fSPaolo Bonzini         return;
338*49ab747fSPaolo Bonzini     }
339*49ab747fSPaolo Bonzini 
340*49ab747fSPaolo Bonzini     status = ch->regs[DBDMA_STATUS] & DEVSTAT;
341*49ab747fSPaolo Bonzini 
342*49ab747fSPaolo Bonzini     sel_mask = (ch->regs[DBDMA_BRANCH_SEL] >> 16) & 0x0f;
343*49ab747fSPaolo Bonzini     sel_value = ch->regs[DBDMA_BRANCH_SEL] & 0x0f;
344*49ab747fSPaolo Bonzini 
345*49ab747fSPaolo Bonzini     cond = (status & sel_mask) == (sel_value & sel_mask);
346*49ab747fSPaolo Bonzini 
347*49ab747fSPaolo Bonzini     switch(br) {
348*49ab747fSPaolo Bonzini     case BR_IFSET:  /* branch if condition bit is 1 */
349*49ab747fSPaolo Bonzini         if (cond)
350*49ab747fSPaolo Bonzini             branch(ch);
351*49ab747fSPaolo Bonzini         else
352*49ab747fSPaolo Bonzini             next(ch);
353*49ab747fSPaolo Bonzini         return;
354*49ab747fSPaolo Bonzini     case BR_IFCLR:  /* branch if condition bit is 0 */
355*49ab747fSPaolo Bonzini         if (!cond)
356*49ab747fSPaolo Bonzini             branch(ch);
357*49ab747fSPaolo Bonzini         else
358*49ab747fSPaolo Bonzini             next(ch);
359*49ab747fSPaolo Bonzini         return;
360*49ab747fSPaolo Bonzini     }
361*49ab747fSPaolo Bonzini }
362*49ab747fSPaolo Bonzini 
363*49ab747fSPaolo Bonzini static QEMUBH *dbdma_bh;
364*49ab747fSPaolo Bonzini static void channel_run(DBDMA_channel *ch);
365*49ab747fSPaolo Bonzini 
366*49ab747fSPaolo Bonzini static void dbdma_end(DBDMA_io *io)
367*49ab747fSPaolo Bonzini {
368*49ab747fSPaolo Bonzini     DBDMA_channel *ch = io->channel;
369*49ab747fSPaolo Bonzini     dbdma_cmd *current = &ch->current;
370*49ab747fSPaolo Bonzini 
371*49ab747fSPaolo Bonzini     if (conditional_wait(ch))
372*49ab747fSPaolo Bonzini         goto wait;
373*49ab747fSPaolo Bonzini 
374*49ab747fSPaolo Bonzini     current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]);
375*49ab747fSPaolo Bonzini     current->res_count = cpu_to_le16(io->len);
376*49ab747fSPaolo Bonzini     dbdma_cmdptr_save(ch);
377*49ab747fSPaolo Bonzini     if (io->is_last)
378*49ab747fSPaolo Bonzini         ch->regs[DBDMA_STATUS] &= ~FLUSH;
379*49ab747fSPaolo Bonzini 
380*49ab747fSPaolo Bonzini     conditional_interrupt(ch);
381*49ab747fSPaolo Bonzini     conditional_branch(ch);
382*49ab747fSPaolo Bonzini 
383*49ab747fSPaolo Bonzini wait:
384*49ab747fSPaolo Bonzini     ch->processing = 0;
385*49ab747fSPaolo Bonzini     if ((ch->regs[DBDMA_STATUS] & RUN) &&
386*49ab747fSPaolo Bonzini         (ch->regs[DBDMA_STATUS] & ACTIVE))
387*49ab747fSPaolo Bonzini         channel_run(ch);
388*49ab747fSPaolo Bonzini }
389*49ab747fSPaolo Bonzini 
390*49ab747fSPaolo Bonzini static void start_output(DBDMA_channel *ch, int key, uint32_t addr,
391*49ab747fSPaolo Bonzini                         uint16_t req_count, int is_last)
392*49ab747fSPaolo Bonzini {
393*49ab747fSPaolo Bonzini     DBDMA_DPRINTF("start_output\n");
394*49ab747fSPaolo Bonzini 
395*49ab747fSPaolo Bonzini     /* KEY_REGS, KEY_DEVICE and KEY_STREAM
396*49ab747fSPaolo Bonzini      * are not implemented in the mac-io chip
397*49ab747fSPaolo Bonzini      */
398*49ab747fSPaolo Bonzini 
399*49ab747fSPaolo Bonzini     DBDMA_DPRINTF("addr 0x%x key 0x%x\n", addr, key);
400*49ab747fSPaolo Bonzini     if (!addr || key > KEY_STREAM3) {
401*49ab747fSPaolo Bonzini         kill_channel(ch);
402*49ab747fSPaolo Bonzini         return;
403*49ab747fSPaolo Bonzini     }
404*49ab747fSPaolo Bonzini 
405*49ab747fSPaolo Bonzini     ch->io.addr = addr;
406*49ab747fSPaolo Bonzini     ch->io.len = req_count;
407*49ab747fSPaolo Bonzini     ch->io.is_last = is_last;
408*49ab747fSPaolo Bonzini     ch->io.dma_end = dbdma_end;
409*49ab747fSPaolo Bonzini     ch->io.is_dma_out = 1;
410*49ab747fSPaolo Bonzini     ch->processing = 1;
411*49ab747fSPaolo Bonzini     if (ch->rw) {
412*49ab747fSPaolo Bonzini         ch->rw(&ch->io);
413*49ab747fSPaolo Bonzini     }
414*49ab747fSPaolo Bonzini }
415*49ab747fSPaolo Bonzini 
416*49ab747fSPaolo Bonzini static void start_input(DBDMA_channel *ch, int key, uint32_t addr,
417*49ab747fSPaolo Bonzini                        uint16_t req_count, int is_last)
418*49ab747fSPaolo Bonzini {
419*49ab747fSPaolo Bonzini     DBDMA_DPRINTF("start_input\n");
420*49ab747fSPaolo Bonzini 
421*49ab747fSPaolo Bonzini     /* KEY_REGS, KEY_DEVICE and KEY_STREAM
422*49ab747fSPaolo Bonzini      * are not implemented in the mac-io chip
423*49ab747fSPaolo Bonzini      */
424*49ab747fSPaolo Bonzini 
425*49ab747fSPaolo Bonzini     if (!addr || key > KEY_STREAM3) {
426*49ab747fSPaolo Bonzini         kill_channel(ch);
427*49ab747fSPaolo Bonzini         return;
428*49ab747fSPaolo Bonzini     }
429*49ab747fSPaolo Bonzini 
430*49ab747fSPaolo Bonzini     ch->io.addr = addr;
431*49ab747fSPaolo Bonzini     ch->io.len = req_count;
432*49ab747fSPaolo Bonzini     ch->io.is_last = is_last;
433*49ab747fSPaolo Bonzini     ch->io.dma_end = dbdma_end;
434*49ab747fSPaolo Bonzini     ch->io.is_dma_out = 0;
435*49ab747fSPaolo Bonzini     ch->processing = 1;
436*49ab747fSPaolo Bonzini     if (ch->rw) {
437*49ab747fSPaolo Bonzini         ch->rw(&ch->io);
438*49ab747fSPaolo Bonzini     }
439*49ab747fSPaolo Bonzini }
440*49ab747fSPaolo Bonzini 
441*49ab747fSPaolo Bonzini static void load_word(DBDMA_channel *ch, int key, uint32_t addr,
442*49ab747fSPaolo Bonzini                      uint16_t len)
443*49ab747fSPaolo Bonzini {
444*49ab747fSPaolo Bonzini     dbdma_cmd *current = &ch->current;
445*49ab747fSPaolo Bonzini     uint32_t val;
446*49ab747fSPaolo Bonzini 
447*49ab747fSPaolo Bonzini     DBDMA_DPRINTF("load_word\n");
448*49ab747fSPaolo Bonzini 
449*49ab747fSPaolo Bonzini     /* only implements KEY_SYSTEM */
450*49ab747fSPaolo Bonzini 
451*49ab747fSPaolo Bonzini     if (key != KEY_SYSTEM) {
452*49ab747fSPaolo Bonzini         printf("DBDMA: LOAD_WORD, unimplemented key %x\n", key);
453*49ab747fSPaolo Bonzini         kill_channel(ch);
454*49ab747fSPaolo Bonzini         return;
455*49ab747fSPaolo Bonzini     }
456*49ab747fSPaolo Bonzini 
457*49ab747fSPaolo Bonzini     cpu_physical_memory_read(addr, (uint8_t*)&val, len);
458*49ab747fSPaolo Bonzini 
459*49ab747fSPaolo Bonzini     if (len == 2)
460*49ab747fSPaolo Bonzini         val = (val << 16) | (current->cmd_dep & 0x0000ffff);
461*49ab747fSPaolo Bonzini     else if (len == 1)
462*49ab747fSPaolo Bonzini         val = (val << 24) | (current->cmd_dep & 0x00ffffff);
463*49ab747fSPaolo Bonzini 
464*49ab747fSPaolo Bonzini     current->cmd_dep = val;
465*49ab747fSPaolo Bonzini 
466*49ab747fSPaolo Bonzini     if (conditional_wait(ch))
467*49ab747fSPaolo Bonzini         goto wait;
468*49ab747fSPaolo Bonzini 
469*49ab747fSPaolo Bonzini     current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]);
470*49ab747fSPaolo Bonzini     dbdma_cmdptr_save(ch);
471*49ab747fSPaolo Bonzini     ch->regs[DBDMA_STATUS] &= ~FLUSH;
472*49ab747fSPaolo Bonzini 
473*49ab747fSPaolo Bonzini     conditional_interrupt(ch);
474*49ab747fSPaolo Bonzini     next(ch);
475*49ab747fSPaolo Bonzini 
476*49ab747fSPaolo Bonzini wait:
477*49ab747fSPaolo Bonzini     qemu_bh_schedule(dbdma_bh);
478*49ab747fSPaolo Bonzini }
479*49ab747fSPaolo Bonzini 
480*49ab747fSPaolo Bonzini static void store_word(DBDMA_channel *ch, int key, uint32_t addr,
481*49ab747fSPaolo Bonzini                       uint16_t len)
482*49ab747fSPaolo Bonzini {
483*49ab747fSPaolo Bonzini     dbdma_cmd *current = &ch->current;
484*49ab747fSPaolo Bonzini     uint32_t val;
485*49ab747fSPaolo Bonzini 
486*49ab747fSPaolo Bonzini     DBDMA_DPRINTF("store_word\n");
487*49ab747fSPaolo Bonzini 
488*49ab747fSPaolo Bonzini     /* only implements KEY_SYSTEM */
489*49ab747fSPaolo Bonzini 
490*49ab747fSPaolo Bonzini     if (key != KEY_SYSTEM) {
491*49ab747fSPaolo Bonzini         printf("DBDMA: STORE_WORD, unimplemented key %x\n", key);
492*49ab747fSPaolo Bonzini         kill_channel(ch);
493*49ab747fSPaolo Bonzini         return;
494*49ab747fSPaolo Bonzini     }
495*49ab747fSPaolo Bonzini 
496*49ab747fSPaolo Bonzini     val = current->cmd_dep;
497*49ab747fSPaolo Bonzini     if (len == 2)
498*49ab747fSPaolo Bonzini         val >>= 16;
499*49ab747fSPaolo Bonzini     else if (len == 1)
500*49ab747fSPaolo Bonzini         val >>= 24;
501*49ab747fSPaolo Bonzini 
502*49ab747fSPaolo Bonzini     cpu_physical_memory_write(addr, (uint8_t*)&val, len);
503*49ab747fSPaolo Bonzini 
504*49ab747fSPaolo Bonzini     if (conditional_wait(ch))
505*49ab747fSPaolo Bonzini         goto wait;
506*49ab747fSPaolo Bonzini 
507*49ab747fSPaolo Bonzini     current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]);
508*49ab747fSPaolo Bonzini     dbdma_cmdptr_save(ch);
509*49ab747fSPaolo Bonzini     ch->regs[DBDMA_STATUS] &= ~FLUSH;
510*49ab747fSPaolo Bonzini 
511*49ab747fSPaolo Bonzini     conditional_interrupt(ch);
512*49ab747fSPaolo Bonzini     next(ch);
513*49ab747fSPaolo Bonzini 
514*49ab747fSPaolo Bonzini wait:
515*49ab747fSPaolo Bonzini     qemu_bh_schedule(dbdma_bh);
516*49ab747fSPaolo Bonzini }
517*49ab747fSPaolo Bonzini 
518*49ab747fSPaolo Bonzini static void nop(DBDMA_channel *ch)
519*49ab747fSPaolo Bonzini {
520*49ab747fSPaolo Bonzini     dbdma_cmd *current = &ch->current;
521*49ab747fSPaolo Bonzini 
522*49ab747fSPaolo Bonzini     if (conditional_wait(ch))
523*49ab747fSPaolo Bonzini         goto wait;
524*49ab747fSPaolo Bonzini 
525*49ab747fSPaolo Bonzini     current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]);
526*49ab747fSPaolo Bonzini     dbdma_cmdptr_save(ch);
527*49ab747fSPaolo Bonzini 
528*49ab747fSPaolo Bonzini     conditional_interrupt(ch);
529*49ab747fSPaolo Bonzini     conditional_branch(ch);
530*49ab747fSPaolo Bonzini 
531*49ab747fSPaolo Bonzini wait:
532*49ab747fSPaolo Bonzini     qemu_bh_schedule(dbdma_bh);
533*49ab747fSPaolo Bonzini }
534*49ab747fSPaolo Bonzini 
535*49ab747fSPaolo Bonzini static void stop(DBDMA_channel *ch)
536*49ab747fSPaolo Bonzini {
537*49ab747fSPaolo Bonzini     ch->regs[DBDMA_STATUS] &= ~(ACTIVE|DEAD|FLUSH);
538*49ab747fSPaolo Bonzini 
539*49ab747fSPaolo Bonzini     /* the stop command does not increment command pointer */
540*49ab747fSPaolo Bonzini }
541*49ab747fSPaolo Bonzini 
542*49ab747fSPaolo Bonzini static void channel_run(DBDMA_channel *ch)
543*49ab747fSPaolo Bonzini {
544*49ab747fSPaolo Bonzini     dbdma_cmd *current = &ch->current;
545*49ab747fSPaolo Bonzini     uint16_t cmd, key;
546*49ab747fSPaolo Bonzini     uint16_t req_count;
547*49ab747fSPaolo Bonzini     uint32_t phy_addr;
548*49ab747fSPaolo Bonzini 
549*49ab747fSPaolo Bonzini     DBDMA_DPRINTF("channel_run\n");
550*49ab747fSPaolo Bonzini     dump_dbdma_cmd(current);
551*49ab747fSPaolo Bonzini 
552*49ab747fSPaolo Bonzini     /* clear WAKE flag at command fetch */
553*49ab747fSPaolo Bonzini 
554*49ab747fSPaolo Bonzini     ch->regs[DBDMA_STATUS] &= ~WAKE;
555*49ab747fSPaolo Bonzini 
556*49ab747fSPaolo Bonzini     cmd = le16_to_cpu(current->command) & COMMAND_MASK;
557*49ab747fSPaolo Bonzini 
558*49ab747fSPaolo Bonzini     switch (cmd) {
559*49ab747fSPaolo Bonzini     case DBDMA_NOP:
560*49ab747fSPaolo Bonzini         nop(ch);
561*49ab747fSPaolo Bonzini 	return;
562*49ab747fSPaolo Bonzini 
563*49ab747fSPaolo Bonzini     case DBDMA_STOP:
564*49ab747fSPaolo Bonzini         stop(ch);
565*49ab747fSPaolo Bonzini 	return;
566*49ab747fSPaolo Bonzini     }
567*49ab747fSPaolo Bonzini 
568*49ab747fSPaolo Bonzini     key = le16_to_cpu(current->command) & 0x0700;
569*49ab747fSPaolo Bonzini     req_count = le16_to_cpu(current->req_count);
570*49ab747fSPaolo Bonzini     phy_addr = le32_to_cpu(current->phy_addr);
571*49ab747fSPaolo Bonzini 
572*49ab747fSPaolo Bonzini     if (key == KEY_STREAM4) {
573*49ab747fSPaolo Bonzini         printf("command %x, invalid key 4\n", cmd);
574*49ab747fSPaolo Bonzini         kill_channel(ch);
575*49ab747fSPaolo Bonzini         return;
576*49ab747fSPaolo Bonzini     }
577*49ab747fSPaolo Bonzini 
578*49ab747fSPaolo Bonzini     switch (cmd) {
579*49ab747fSPaolo Bonzini     case OUTPUT_MORE:
580*49ab747fSPaolo Bonzini         start_output(ch, key, phy_addr, req_count, 0);
581*49ab747fSPaolo Bonzini 	return;
582*49ab747fSPaolo Bonzini 
583*49ab747fSPaolo Bonzini     case OUTPUT_LAST:
584*49ab747fSPaolo Bonzini         start_output(ch, key, phy_addr, req_count, 1);
585*49ab747fSPaolo Bonzini 	return;
586*49ab747fSPaolo Bonzini 
587*49ab747fSPaolo Bonzini     case INPUT_MORE:
588*49ab747fSPaolo Bonzini         start_input(ch, key, phy_addr, req_count, 0);
589*49ab747fSPaolo Bonzini 	return;
590*49ab747fSPaolo Bonzini 
591*49ab747fSPaolo Bonzini     case INPUT_LAST:
592*49ab747fSPaolo Bonzini         start_input(ch, key, phy_addr, req_count, 1);
593*49ab747fSPaolo Bonzini 	return;
594*49ab747fSPaolo Bonzini     }
595*49ab747fSPaolo Bonzini 
596*49ab747fSPaolo Bonzini     if (key < KEY_REGS) {
597*49ab747fSPaolo Bonzini         printf("command %x, invalid key %x\n", cmd, key);
598*49ab747fSPaolo Bonzini         key = KEY_SYSTEM;
599*49ab747fSPaolo Bonzini     }
600*49ab747fSPaolo Bonzini 
601*49ab747fSPaolo Bonzini     /* for LOAD_WORD and STORE_WORD, req_count is on 3 bits
602*49ab747fSPaolo Bonzini      * and BRANCH is invalid
603*49ab747fSPaolo Bonzini      */
604*49ab747fSPaolo Bonzini 
605*49ab747fSPaolo Bonzini     req_count = req_count & 0x0007;
606*49ab747fSPaolo Bonzini     if (req_count & 0x4) {
607*49ab747fSPaolo Bonzini         req_count = 4;
608*49ab747fSPaolo Bonzini         phy_addr &= ~3;
609*49ab747fSPaolo Bonzini     } else if (req_count & 0x2) {
610*49ab747fSPaolo Bonzini         req_count = 2;
611*49ab747fSPaolo Bonzini         phy_addr &= ~1;
612*49ab747fSPaolo Bonzini     } else
613*49ab747fSPaolo Bonzini         req_count = 1;
614*49ab747fSPaolo Bonzini 
615*49ab747fSPaolo Bonzini     switch (cmd) {
616*49ab747fSPaolo Bonzini     case LOAD_WORD:
617*49ab747fSPaolo Bonzini         load_word(ch, key, phy_addr, req_count);
618*49ab747fSPaolo Bonzini 	return;
619*49ab747fSPaolo Bonzini 
620*49ab747fSPaolo Bonzini     case STORE_WORD:
621*49ab747fSPaolo Bonzini         store_word(ch, key, phy_addr, req_count);
622*49ab747fSPaolo Bonzini 	return;
623*49ab747fSPaolo Bonzini     }
624*49ab747fSPaolo Bonzini }
625*49ab747fSPaolo Bonzini 
626*49ab747fSPaolo Bonzini static void DBDMA_run(DBDMAState *s)
627*49ab747fSPaolo Bonzini {
628*49ab747fSPaolo Bonzini     int channel;
629*49ab747fSPaolo Bonzini 
630*49ab747fSPaolo Bonzini     for (channel = 0; channel < DBDMA_CHANNELS; channel++) {
631*49ab747fSPaolo Bonzini         DBDMA_channel *ch = &s->channels[channel];
632*49ab747fSPaolo Bonzini         uint32_t status = ch->regs[DBDMA_STATUS];
633*49ab747fSPaolo Bonzini         if (!ch->processing && (status & RUN) && (status & ACTIVE)) {
634*49ab747fSPaolo Bonzini             channel_run(ch);
635*49ab747fSPaolo Bonzini         }
636*49ab747fSPaolo Bonzini     }
637*49ab747fSPaolo Bonzini }
638*49ab747fSPaolo Bonzini 
639*49ab747fSPaolo Bonzini static void DBDMA_run_bh(void *opaque)
640*49ab747fSPaolo Bonzini {
641*49ab747fSPaolo Bonzini     DBDMAState *s = opaque;
642*49ab747fSPaolo Bonzini 
643*49ab747fSPaolo Bonzini     DBDMA_DPRINTF("DBDMA_run_bh\n");
644*49ab747fSPaolo Bonzini 
645*49ab747fSPaolo Bonzini     DBDMA_run(s);
646*49ab747fSPaolo Bonzini }
647*49ab747fSPaolo Bonzini 
648*49ab747fSPaolo Bonzini void DBDMA_register_channel(void *dbdma, int nchan, qemu_irq irq,
649*49ab747fSPaolo Bonzini                             DBDMA_rw rw, DBDMA_flush flush,
650*49ab747fSPaolo Bonzini                             void *opaque)
651*49ab747fSPaolo Bonzini {
652*49ab747fSPaolo Bonzini     DBDMAState *s = dbdma;
653*49ab747fSPaolo Bonzini     DBDMA_channel *ch = &s->channels[nchan];
654*49ab747fSPaolo Bonzini 
655*49ab747fSPaolo Bonzini     DBDMA_DPRINTF("DBDMA_register_channel 0x%x\n", nchan);
656*49ab747fSPaolo Bonzini 
657*49ab747fSPaolo Bonzini     ch->irq = irq;
658*49ab747fSPaolo Bonzini     ch->channel = nchan;
659*49ab747fSPaolo Bonzini     ch->rw = rw;
660*49ab747fSPaolo Bonzini     ch->flush = flush;
661*49ab747fSPaolo Bonzini     ch->io.opaque = opaque;
662*49ab747fSPaolo Bonzini     ch->io.channel = ch;
663*49ab747fSPaolo Bonzini }
664*49ab747fSPaolo Bonzini 
665*49ab747fSPaolo Bonzini static void
666*49ab747fSPaolo Bonzini dbdma_control_write(DBDMA_channel *ch)
667*49ab747fSPaolo Bonzini {
668*49ab747fSPaolo Bonzini     uint16_t mask, value;
669*49ab747fSPaolo Bonzini     uint32_t status;
670*49ab747fSPaolo Bonzini 
671*49ab747fSPaolo Bonzini     mask = (ch->regs[DBDMA_CONTROL] >> 16) & 0xffff;
672*49ab747fSPaolo Bonzini     value = ch->regs[DBDMA_CONTROL] & 0xffff;
673*49ab747fSPaolo Bonzini 
674*49ab747fSPaolo Bonzini     value &= (RUN | PAUSE | FLUSH | WAKE | DEVSTAT);
675*49ab747fSPaolo Bonzini 
676*49ab747fSPaolo Bonzini     status = ch->regs[DBDMA_STATUS];
677*49ab747fSPaolo Bonzini 
678*49ab747fSPaolo Bonzini     status = (value & mask) | (status & ~mask);
679*49ab747fSPaolo Bonzini 
680*49ab747fSPaolo Bonzini     if (status & WAKE)
681*49ab747fSPaolo Bonzini         status |= ACTIVE;
682*49ab747fSPaolo Bonzini     if (status & RUN) {
683*49ab747fSPaolo Bonzini         status |= ACTIVE;
684*49ab747fSPaolo Bonzini         status &= ~DEAD;
685*49ab747fSPaolo Bonzini     }
686*49ab747fSPaolo Bonzini     if (status & PAUSE)
687*49ab747fSPaolo Bonzini         status &= ~ACTIVE;
688*49ab747fSPaolo Bonzini     if ((ch->regs[DBDMA_STATUS] & RUN) && !(status & RUN)) {
689*49ab747fSPaolo Bonzini         /* RUN is cleared */
690*49ab747fSPaolo Bonzini         status &= ~(ACTIVE|DEAD);
691*49ab747fSPaolo Bonzini         if ((status & FLUSH) && ch->flush) {
692*49ab747fSPaolo Bonzini             ch->flush(&ch->io);
693*49ab747fSPaolo Bonzini             status &= ~FLUSH;
694*49ab747fSPaolo Bonzini         }
695*49ab747fSPaolo Bonzini     }
696*49ab747fSPaolo Bonzini 
697*49ab747fSPaolo Bonzini     DBDMA_DPRINTF("    status 0x%08x\n", status);
698*49ab747fSPaolo Bonzini 
699*49ab747fSPaolo Bonzini     ch->regs[DBDMA_STATUS] = status;
700*49ab747fSPaolo Bonzini 
701*49ab747fSPaolo Bonzini     if (status & ACTIVE)
702*49ab747fSPaolo Bonzini         qemu_bh_schedule(dbdma_bh);
703*49ab747fSPaolo Bonzini     if ((status & FLUSH) && ch->flush)
704*49ab747fSPaolo Bonzini         ch->flush(&ch->io);
705*49ab747fSPaolo Bonzini }
706*49ab747fSPaolo Bonzini 
707*49ab747fSPaolo Bonzini static void dbdma_write(void *opaque, hwaddr addr,
708*49ab747fSPaolo Bonzini                         uint64_t value, unsigned size)
709*49ab747fSPaolo Bonzini {
710*49ab747fSPaolo Bonzini     int channel = addr >> DBDMA_CHANNEL_SHIFT;
711*49ab747fSPaolo Bonzini     DBDMAState *s = opaque;
712*49ab747fSPaolo Bonzini     DBDMA_channel *ch = &s->channels[channel];
713*49ab747fSPaolo Bonzini     int reg = (addr - (channel << DBDMA_CHANNEL_SHIFT)) >> 2;
714*49ab747fSPaolo Bonzini 
715*49ab747fSPaolo Bonzini     DBDMA_DPRINTF("writel 0x" TARGET_FMT_plx " <= 0x%08x\n", addr, value);
716*49ab747fSPaolo Bonzini     DBDMA_DPRINTF("channel 0x%x reg 0x%x\n",
717*49ab747fSPaolo Bonzini                   (uint32_t)addr >> DBDMA_CHANNEL_SHIFT, reg);
718*49ab747fSPaolo Bonzini 
719*49ab747fSPaolo Bonzini     /* cmdptr cannot be modified if channel is RUN or ACTIVE */
720*49ab747fSPaolo Bonzini 
721*49ab747fSPaolo Bonzini     if (reg == DBDMA_CMDPTR_LO &&
722*49ab747fSPaolo Bonzini         (ch->regs[DBDMA_STATUS] & (RUN | ACTIVE)))
723*49ab747fSPaolo Bonzini 	return;
724*49ab747fSPaolo Bonzini 
725*49ab747fSPaolo Bonzini     ch->regs[reg] = value;
726*49ab747fSPaolo Bonzini 
727*49ab747fSPaolo Bonzini     switch(reg) {
728*49ab747fSPaolo Bonzini     case DBDMA_CONTROL:
729*49ab747fSPaolo Bonzini         dbdma_control_write(ch);
730*49ab747fSPaolo Bonzini         break;
731*49ab747fSPaolo Bonzini     case DBDMA_CMDPTR_LO:
732*49ab747fSPaolo Bonzini         /* 16-byte aligned */
733*49ab747fSPaolo Bonzini         ch->regs[DBDMA_CMDPTR_LO] &= ~0xf;
734*49ab747fSPaolo Bonzini         dbdma_cmdptr_load(ch);
735*49ab747fSPaolo Bonzini         break;
736*49ab747fSPaolo Bonzini     case DBDMA_STATUS:
737*49ab747fSPaolo Bonzini     case DBDMA_INTR_SEL:
738*49ab747fSPaolo Bonzini     case DBDMA_BRANCH_SEL:
739*49ab747fSPaolo Bonzini     case DBDMA_WAIT_SEL:
740*49ab747fSPaolo Bonzini         /* nothing to do */
741*49ab747fSPaolo Bonzini         break;
742*49ab747fSPaolo Bonzini     case DBDMA_XFER_MODE:
743*49ab747fSPaolo Bonzini     case DBDMA_CMDPTR_HI:
744*49ab747fSPaolo Bonzini     case DBDMA_DATA2PTR_HI:
745*49ab747fSPaolo Bonzini     case DBDMA_DATA2PTR_LO:
746*49ab747fSPaolo Bonzini     case DBDMA_ADDRESS_HI:
747*49ab747fSPaolo Bonzini     case DBDMA_BRANCH_ADDR_HI:
748*49ab747fSPaolo Bonzini     case DBDMA_RES1:
749*49ab747fSPaolo Bonzini     case DBDMA_RES2:
750*49ab747fSPaolo Bonzini     case DBDMA_RES3:
751*49ab747fSPaolo Bonzini     case DBDMA_RES4:
752*49ab747fSPaolo Bonzini         /* unused */
753*49ab747fSPaolo Bonzini         break;
754*49ab747fSPaolo Bonzini     }
755*49ab747fSPaolo Bonzini }
756*49ab747fSPaolo Bonzini 
757*49ab747fSPaolo Bonzini static uint64_t dbdma_read(void *opaque, hwaddr addr,
758*49ab747fSPaolo Bonzini                            unsigned size)
759*49ab747fSPaolo Bonzini {
760*49ab747fSPaolo Bonzini     uint32_t value;
761*49ab747fSPaolo Bonzini     int channel = addr >> DBDMA_CHANNEL_SHIFT;
762*49ab747fSPaolo Bonzini     DBDMAState *s = opaque;
763*49ab747fSPaolo Bonzini     DBDMA_channel *ch = &s->channels[channel];
764*49ab747fSPaolo Bonzini     int reg = (addr - (channel << DBDMA_CHANNEL_SHIFT)) >> 2;
765*49ab747fSPaolo Bonzini 
766*49ab747fSPaolo Bonzini     value = ch->regs[reg];
767*49ab747fSPaolo Bonzini 
768*49ab747fSPaolo Bonzini     DBDMA_DPRINTF("readl 0x" TARGET_FMT_plx " => 0x%08x\n", addr, value);
769*49ab747fSPaolo Bonzini     DBDMA_DPRINTF("channel 0x%x reg 0x%x\n",
770*49ab747fSPaolo Bonzini                   (uint32_t)addr >> DBDMA_CHANNEL_SHIFT, reg);
771*49ab747fSPaolo Bonzini 
772*49ab747fSPaolo Bonzini     switch(reg) {
773*49ab747fSPaolo Bonzini     case DBDMA_CONTROL:
774*49ab747fSPaolo Bonzini         value = 0;
775*49ab747fSPaolo Bonzini         break;
776*49ab747fSPaolo Bonzini     case DBDMA_STATUS:
777*49ab747fSPaolo Bonzini     case DBDMA_CMDPTR_LO:
778*49ab747fSPaolo Bonzini     case DBDMA_INTR_SEL:
779*49ab747fSPaolo Bonzini     case DBDMA_BRANCH_SEL:
780*49ab747fSPaolo Bonzini     case DBDMA_WAIT_SEL:
781*49ab747fSPaolo Bonzini         /* nothing to do */
782*49ab747fSPaolo Bonzini         break;
783*49ab747fSPaolo Bonzini     case DBDMA_XFER_MODE:
784*49ab747fSPaolo Bonzini     case DBDMA_CMDPTR_HI:
785*49ab747fSPaolo Bonzini     case DBDMA_DATA2PTR_HI:
786*49ab747fSPaolo Bonzini     case DBDMA_DATA2PTR_LO:
787*49ab747fSPaolo Bonzini     case DBDMA_ADDRESS_HI:
788*49ab747fSPaolo Bonzini     case DBDMA_BRANCH_ADDR_HI:
789*49ab747fSPaolo Bonzini         /* unused */
790*49ab747fSPaolo Bonzini         value = 0;
791*49ab747fSPaolo Bonzini         break;
792*49ab747fSPaolo Bonzini     case DBDMA_RES1:
793*49ab747fSPaolo Bonzini     case DBDMA_RES2:
794*49ab747fSPaolo Bonzini     case DBDMA_RES3:
795*49ab747fSPaolo Bonzini     case DBDMA_RES4:
796*49ab747fSPaolo Bonzini         /* reserved */
797*49ab747fSPaolo Bonzini         break;
798*49ab747fSPaolo Bonzini     }
799*49ab747fSPaolo Bonzini 
800*49ab747fSPaolo Bonzini     return value;
801*49ab747fSPaolo Bonzini }
802*49ab747fSPaolo Bonzini 
803*49ab747fSPaolo Bonzini static const MemoryRegionOps dbdma_ops = {
804*49ab747fSPaolo Bonzini     .read = dbdma_read,
805*49ab747fSPaolo Bonzini     .write = dbdma_write,
806*49ab747fSPaolo Bonzini     .endianness = DEVICE_LITTLE_ENDIAN,
807*49ab747fSPaolo Bonzini     .valid = {
808*49ab747fSPaolo Bonzini         .min_access_size = 4,
809*49ab747fSPaolo Bonzini         .max_access_size = 4,
810*49ab747fSPaolo Bonzini     },
811*49ab747fSPaolo Bonzini };
812*49ab747fSPaolo Bonzini 
813*49ab747fSPaolo Bonzini static const VMStateDescription vmstate_dbdma_channel = {
814*49ab747fSPaolo Bonzini     .name = "dbdma_channel",
815*49ab747fSPaolo Bonzini     .version_id = 0,
816*49ab747fSPaolo Bonzini     .minimum_version_id = 0,
817*49ab747fSPaolo Bonzini     .minimum_version_id_old = 0,
818*49ab747fSPaolo Bonzini     .fields      = (VMStateField[]) {
819*49ab747fSPaolo Bonzini         VMSTATE_UINT32_ARRAY(regs, struct DBDMA_channel, DBDMA_REGS),
820*49ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
821*49ab747fSPaolo Bonzini     }
822*49ab747fSPaolo Bonzini };
823*49ab747fSPaolo Bonzini 
824*49ab747fSPaolo Bonzini static const VMStateDescription vmstate_dbdma = {
825*49ab747fSPaolo Bonzini     .name = "dbdma",
826*49ab747fSPaolo Bonzini     .version_id = 2,
827*49ab747fSPaolo Bonzini     .minimum_version_id = 2,
828*49ab747fSPaolo Bonzini     .minimum_version_id_old = 2,
829*49ab747fSPaolo Bonzini     .fields      = (VMStateField[]) {
830*49ab747fSPaolo Bonzini         VMSTATE_STRUCT_ARRAY(channels, DBDMAState, DBDMA_CHANNELS, 1,
831*49ab747fSPaolo Bonzini                              vmstate_dbdma_channel, DBDMA_channel),
832*49ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
833*49ab747fSPaolo Bonzini     }
834*49ab747fSPaolo Bonzini };
835*49ab747fSPaolo Bonzini 
836*49ab747fSPaolo Bonzini static void dbdma_reset(void *opaque)
837*49ab747fSPaolo Bonzini {
838*49ab747fSPaolo Bonzini     DBDMAState *s = opaque;
839*49ab747fSPaolo Bonzini     int i;
840*49ab747fSPaolo Bonzini 
841*49ab747fSPaolo Bonzini     for (i = 0; i < DBDMA_CHANNELS; i++)
842*49ab747fSPaolo Bonzini         memset(s->channels[i].regs, 0, DBDMA_SIZE);
843*49ab747fSPaolo Bonzini }
844*49ab747fSPaolo Bonzini 
845*49ab747fSPaolo Bonzini void* DBDMA_init (MemoryRegion **dbdma_mem)
846*49ab747fSPaolo Bonzini {
847*49ab747fSPaolo Bonzini     DBDMAState *s;
848*49ab747fSPaolo Bonzini 
849*49ab747fSPaolo Bonzini     s = g_malloc0(sizeof(DBDMAState));
850*49ab747fSPaolo Bonzini 
851*49ab747fSPaolo Bonzini     memory_region_init_io(&s->mem, &dbdma_ops, s, "dbdma", 0x1000);
852*49ab747fSPaolo Bonzini     *dbdma_mem = &s->mem;
853*49ab747fSPaolo Bonzini     vmstate_register(NULL, -1, &vmstate_dbdma, s);
854*49ab747fSPaolo Bonzini     qemu_register_reset(dbdma_reset, s);
855*49ab747fSPaolo Bonzini 
856*49ab747fSPaolo Bonzini     dbdma_bh = qemu_bh_new(DBDMA_run_bh, s);
857*49ab747fSPaolo Bonzini 
858*49ab747fSPaolo Bonzini     return s;
859*49ab747fSPaolo Bonzini }
860