All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH 0/2] mac-io IDE DB-DMA support
@ 2009-01-29 20:25 Laurent Vivier
  2009-01-29 21:33 ` [Qemu-devel] [PATCH 1/2] mac-io " Laurent Vivier
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: Laurent Vivier @ 2009-01-29 20:25 UTC (permalink / raw)
  To: qemu-devel; +Cc: laurent

This series of patch allows to use powermac mac-io IDE interface by  
implementing
DB-DMA. Linux 2.6.18 seems to not be able to use correctly mac-io IDE  
interface
without DMA.

[PATCH 1/2] mac-io DB-DMA support
[PATCH 2/2] IDE DB-DMA support

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [Qemu-devel] [PATCH 1/2] mac-io DB-DMA support
  2009-01-29 20:25 [Qemu-devel] [PATCH 0/2] mac-io IDE DB-DMA support Laurent Vivier
@ 2009-01-29 21:33 ` Laurent Vivier
  2009-01-29 21:33 ` [Qemu-devel] [PATCH 2/2] IDE " Laurent Vivier
  2009-01-30 20:39 ` [Qemu-devel] [PATCH 0/2] mac-io " Aurelien Jarno
  2 siblings, 0 replies; 5+ messages in thread
From: Laurent Vivier @ 2009-01-29 21:33 UTC (permalink / raw)
  To: qemu-devel

[-- Attachment #1: Type: text/plain, Size: 29405 bytes --]


This patch adds powermac Descriptor-Based DMA.
It is used by mac-io based IDE, ethernet, sounds and serial devices.

Signed-off-by: Laurent Vivier <Laurent@lvivier.info>
---
  hw/mac_dbdma.c    |  824 ++++++++++++++++++++++++++++++++++++++++++++ 
+++++++--
  hw/mac_dbdma.h    |   41 +++
  hw/ppc_chrp.c     |    4 +-
  hw/ppc_mac.h      |    3 -
  hw/ppc_oldworld.c |    7 +-
  5 files changed, 844 insertions(+), 35 deletions(-)
  create mode 100644 hw/mac_dbdma.h

diff --git a/hw/mac_dbdma.c b/hw/mac_dbdma.c
index 13fbd7b..d01633b 100644
--- a/hw/mac_dbdma.c
+++ b/hw/mac_dbdma.c
@@ -3,6 +3,20 @@
   *
   * Copyright (c) 2005-2007 Fabrice Bellard
   * Copyright (c) 2007 Jocelyn Mayer
+ * Copyright (c) 2009 Laurent Vivier
+ *
+ * some parts from linux-2.6.28, arch/powerpc/include/asm/dbdma.h
+ *
+ *   Definitions for using the Apple Descriptor-Based DMA controller
+ *   in Power Macintosh computers.
+ *
+ *   Copyright (C) 1996 Paul Mackerras.
+ *
+ * some parts from mol 0.9.71
+ *
+ *   Descriptor based DMA emulation
+ *
+ *   Copyright (C) 1998-2004 Samuel Rydh (samuel@ibrium.se)
   *
   * Permission is hereby granted, free of charge, to any person  
obtaining a copy
   * of this software and associated documentation files (the  
"Software"), to deal
@@ -23,7 +37,8 @@
   * THE SOFTWARE.
   */
  #include "hw.h"
-#include "ppc_mac.h"
+#include "isa.h"
+#include "mac_dbdma.h"

  /* debug DBDMA */
  //#define DEBUG_DBDMA
@@ -35,79 +50,830 @@ do { printf("DBDMA: " fmt , ##args); } while (0)
  #define DBDMA_DPRINTF(fmt, args...)
  #endif

-/* DBDMA: currently no op - should suffice right now */
+/*
+ */
+
+/*
+ * DBDMA control/status registers.  All little-endian.
+ */

-static void dbdma_writeb (void *opaque,
-                          target_phys_addr_t addr, uint32_t value)
+#define DBDMA_CONTROL         0x00
+#define DBDMA_STATUS          0x01
+#define DBDMA_CMDPTR_HI       0x02
+#define DBDMA_CMDPTR_LO       0x03
+#define DBDMA_INTR_SEL        0x04
+#define DBDMA_BRANCH_SEL      0x05
+#define DBDMA_WAIT_SEL        0x06
+#define DBDMA_XFER_MODE       0x07
+#define DBDMA_DATA2PTR_HI     0x08
+#define DBDMA_DATA2PTR_LO     0x09
+#define DBDMA_RES1            0x0A
+#define DBDMA_ADDRESS_HI      0x0B
+#define DBDMA_BRANCH_ADDR_HI  0x0C
+#define DBDMA_RES2            0x0D
+#define DBDMA_RES3            0x0E
+#define DBDMA_RES4            0x0F
+
+#define DBDMA_REGS            16
+#define DBDMA_SIZE            (DBDMA_REGS * sizeof(uint32_t))
+
+#define DBDMA_CHANNEL_SHIFT   7
+#define DBDMA_CHANNEL_SIZE    (1 << DBDMA_CHANNEL_SHIFT)
+
+#define DBDMA_CHANNELS        (0x1000 >> DBDMA_CHANNEL_SHIFT)
+
+/* Bits in control and status registers */
+
+#define RUN	0x8000
+#define PAUSE	0x4000
+#define FLUSH	0x2000
+#define WAKE	0x1000
+#define DEAD	0x0800
+#define ACTIVE	0x0400
+#define BT	0x0100
+#define DEVSTAT	0x00ff
+
+/*
+ * DBDMA command structure.  These fields are all little-endian!
+ */
+
+typedef struct dbdma_cmd {
+    uint16_t req_count;	  /* requested byte transfer count */
+    uint16_t command;	  /* command word (has bit-fields) */
+    uint32_t phy_addr;	  /* physical data address */
+    uint32_t cmd_dep;	  /* command-dependent field */
+    uint16_t res_count;	  /* residual count after completion */
+    uint16_t xfer_status; /* transfer status */
+} dbdma_cmd;
+
+/* DBDMA command values in command field */
+
+#define COMMAND_MASK    0xf000
+#define OUTPUT_MORE	0x0000	/* transfer memory data to stream */
+#define OUTPUT_LAST	0x1000	/* ditto followed by end marker */
+#define INPUT_MORE	0x2000	/* transfer stream data to memory */
+#define INPUT_LAST	0x3000	/* ditto, expect end marker */
+#define STORE_WORD	0x4000	/* write word (4 bytes) to device reg */
+#define LOAD_WORD	0x5000	/* read word (4 bytes) from device reg */
+#define DBDMA_NOP	0x6000	/* do nothing */
+#define DBDMA_STOP	0x7000	/* suspend processing */
+
+/* Key values in command field */
+
+#define KEY_MASK        0x0700
+#define KEY_STREAM0	0x0000	/* usual data stream */
+#define KEY_STREAM1	0x0100	/* control/status stream */
+#define KEY_STREAM2	0x0200	/* device-dependent stream */
+#define KEY_STREAM3	0x0300	/* device-dependent stream */
+#define KEY_STREAM4	0x0400	/* reserved */
+#define KEY_REGS	0x0500	/* device register space */
+#define KEY_SYSTEM	0x0600	/* system memory-mapped space */
+#define KEY_DEVICE	0x0700	/* device memory-mapped space */
+
+/* Interrupt control values in command field */
+
+#define INTR_MASK       0x0030
+#define INTR_NEVER	0x0000	/* don't interrupt */
+#define INTR_IFSET	0x0010	/* intr if condition bit is 1 */
+#define INTR_IFCLR	0x0020	/* intr if condition bit is 0 */
+#define INTR_ALWAYS	0x0030	/* always interrupt */
+
+/* Branch control values in command field */
+
+#define BR_MASK         0x000c
+#define BR_NEVER	0x0000	/* don't branch */
+#define BR_IFSET	0x0004	/* branch if condition bit is 1 */
+#define BR_IFCLR	0x0008	/* branch if condition bit is 0 */
+#define BR_ALWAYS	0x000c	/* always branch */
+
+/* Wait control values in command field */
+
+#define WAIT_MASK       0x0003
+#define WAIT_NEVER	0x0000	/* don't wait */
+#define WAIT_IFSET	0x0001	/* wait if condition bit is 1 */
+#define WAIT_IFCLR	0x0002	/* wait if condition bit is 0 */
+#define WAIT_ALWAYS	0x0003	/* always wait */
+
+typedef struct DBDMA_channel {
+    int channel;
+    uint32_t regs[DBDMA_REGS];
+    qemu_irq irq;
+    DBDMA_transfer io;
+    DBDMA_transfer_handler transfer_handler;
+    dbdma_cmd current;
+} DBDMA_channel;
+
+#ifdef DEBUG_DBDMA
+static void dump_dbdma_cmd(dbdma_cmd *cmd)
+{
+    printf("dbdma_cmd %p\n", cmd);
+    printf("    req_count 0x%04x\n", le16_to_cpu(cmd->req_count));
+    printf("    command 0x%04x\n", le16_to_cpu(cmd->command));
+    printf("    phy_addr 0x%08x\n", le32_to_cpu(cmd->phy_addr));
+    printf("    cmd_dep 0x%08x\n", le32_to_cpu(cmd->cmd_dep));
+    printf("    res_count 0x%04x\n", le16_to_cpu(cmd->res_count));
+    printf("    xfer_status 0x%04x\n", le16_to_cpu(cmd->xfer_status));
+}
+#else
+static void dump_dbdma_cmd(dbdma_cmd *cmd)
  {
-    DBDMA_DPRINTF("writeb 0x" TARGET_FMT_plx " <= 0x%08x\n", addr,  
value);
+}
+#endif
+static void dbdma_cmdptr_load(DBDMA_channel *ch)
+{
+    DBDMA_DPRINTF("dbdma_cmdptr_load 0x%08x\n",
+                  be32_to_cpu(ch->regs[DBDMA_CMDPTR_LO]));
+    cpu_physical_memory_read(be32_to_cpu(ch->regs[DBDMA_CMDPTR_LO]),
+                             (uint8_t*)&ch->current,  
sizeof(dbdma_cmd));
  }

-static void dbdma_writew (void *opaque,
-                          target_phys_addr_t addr, uint32_t value)
+static void dbdma_cmdptr_save(DBDMA_channel *ch)
  {
-    DBDMA_DPRINTF("writew 0x" TARGET_FMT_plx " <= 0x%08x\n", addr,  
value);
+    DBDMA_DPRINTF("dbdma_cmdptr_save 0x%08x\n",
+                  be32_to_cpu(ch->regs[DBDMA_CMDPTR_LO]));
+    DBDMA_DPRINTF("xfer_status 0x%08x res_count 0x%04x\n",
+                  le16_to_cpu(ch->current.xfer_status),
+                  le16_to_cpu(ch->current.res_count));
+    cpu_physical_memory_write(be32_to_cpu(ch->regs[DBDMA_CMDPTR_LO]),
+                              (uint8_t*)&ch->current,  
sizeof(dbdma_cmd));
  }

-static void dbdma_writel (void *opaque,
-                          target_phys_addr_t addr, uint32_t value)
+static void kill_channel(DBDMA_channel *ch)
  {
-    DBDMA_DPRINTF("writel 0x" TARGET_FMT_plx " <= 0x%08x\n", addr,  
value);
+    DBDMA_DPRINTF("kill_channel\n");
+
+    ch->regs[DBDMA_STATUS] |= cpu_to_be32(DEAD);
+    ch->regs[DBDMA_STATUS] &= cpu_to_be32(~ACTIVE);
+
+    qemu_irq_raise(ch->irq);
+}
+
+static void conditionnal_interrupt(DBDMA_channel *ch)
+{
+    dbdma_cmd *current = &ch->current;
+    uint16_t intr;
+    uint16_t sel_mask, sel_value;
+    uint32_t status;
+    int cond;
+
+    DBDMA_DPRINTF("conditionnal_interrupt\n");
+
+    intr = be16_to_cpu(current->command) & INTR_MASK;
+
+    switch(intr) {
+    case INTR_NEVER:  /* don't interrupt */
+        return;
+    case INTR_ALWAYS: /* always interrupt */
+        qemu_irq_raise(ch->irq);
+        return;
+    }
+
+    status = be32_to_cpu(ch->regs[DBDMA_STATUS]) & DEVSTAT;
+
+    sel_mask = (be32_to_cpu(ch->regs[DBDMA_INTR_SEL]) >> 16) & 0x0f;
+    sel_value = be32_to_cpu(ch->regs[DBDMA_INTR_SEL]) & 0x0f;
+
+    cond = (status & sel_mask) == (sel_value & sel_mask);
+
+    switch(intr) {
+    case INTR_IFSET:  /* intr if condition bit is 1 */
+        if (cond)
+            qemu_irq_raise(ch->irq);
+        return;
+    case INTR_IFCLR:  /* intr if condition bit is 0 */
+        if (!cond)
+            qemu_irq_raise(ch->irq);
+        return;
+    }
  }

-static uint32_t dbdma_readb (void *opaque, target_phys_addr_t addr)
+static int conditionnal_wait(DBDMA_channel *ch)
  {
-    DBDMA_DPRINTF("readb 0x" TARGET_FMT_plx " => 0\n", addr);
+    dbdma_cmd *current = &ch->current;
+    uint16_t wait;
+    uint16_t sel_mask, sel_value;
+    uint32_t status;
+    int cond;

+    DBDMA_DPRINTF("conditionnal_wait\n");
+
+    wait = be16_to_cpu(current->command) & WAIT_MASK;
+
+    switch(wait) {
+    case WAIT_NEVER:  /* don't wait */
+        return 0;
+    case WAIT_ALWAYS: /* always wait */
+        return 1;
+    }
+
+    status = be32_to_cpu(ch->regs[DBDMA_STATUS]) & DEVSTAT;
+
+    sel_mask = (be32_to_cpu(ch->regs[DBDMA_WAIT_SEL]) >> 16) & 0x0f;
+    sel_value = be32_to_cpu(ch->regs[DBDMA_WAIT_SEL]) & 0x0f;
+
+    cond = (status & sel_mask) == (sel_value & sel_mask);
+
+    switch(wait) {
+    case WAIT_IFSET:  /* wait if condition bit is 1 */
+        if (cond)
+            return 1;
+        return 0;
+    case WAIT_IFCLR:  /* wait if condition bit is 0 */
+        if (!cond)
+            return 1;
+        return 0;
+    }
      return 0;
  }

-static uint32_t dbdma_readw (void *opaque, target_phys_addr_t addr)
+static void next(DBDMA_channel *ch)
+{
+    uint32_t cp;
+
+    ch->regs[DBDMA_STATUS] &= cpu_to_be32(~BT);
+
+    cp = be32_to_cpu(ch->regs[DBDMA_CMDPTR_LO]);
+    ch->regs[DBDMA_CMDPTR_LO] = cpu_to_be32(cp + sizeof(dbdma_cmd));
+    dbdma_cmdptr_load(ch);
+}
+
+static void branch(DBDMA_channel *ch)
+{
+    dbdma_cmd *current = &ch->current;
+
+    ch->regs[DBDMA_CMDPTR_LO] = current->cmd_dep;
+    ch->regs[DBDMA_STATUS] |= cpu_to_be32(BT);
+    dbdma_cmdptr_load(ch);
+}
+
+static void conditionnal_branch(DBDMA_channel *ch)
+{
+    dbdma_cmd *current = &ch->current;
+    uint16_t br;
+    uint16_t sel_mask, sel_value;
+    uint32_t status;
+    int cond;
+
+    DBDMA_DPRINTF("conditionnal_branch\n");
+
+    /* check if we must branch */
+
+    br = be16_to_cpu(current->command) & BR_MASK;
+
+    switch(br) {
+    case BR_NEVER:  /* don't branch */
+        next(ch);
+        return;
+    case BR_ALWAYS: /* always branch */
+        branch(ch);
+        return;
+    }
+
+    status = be32_to_cpu(ch->regs[DBDMA_STATUS]) & DEVSTAT;
+
+    sel_mask = (be32_to_cpu(ch->regs[DBDMA_BRANCH_SEL]) >> 16) & 0x0f;
+    sel_value = be32_to_cpu(ch->regs[DBDMA_BRANCH_SEL]) & 0x0f;
+
+    cond = (status & sel_mask) == (sel_value & sel_mask);
+
+    switch(br) {
+    case BR_IFSET:  /* branch if condition bit is 1 */
+        if (cond)
+            branch(ch);
+        else
+            next(ch);
+        return;
+    case BR_IFCLR:  /* branch if condition bit is 0 */
+        if (!cond)
+            branch(ch);
+        else
+            next(ch);
+        return;
+    }
+}
+
+static int dbdma_read_memory(DBDMA_transfer *io)
+{
+    DBDMA_channel *ch = io->channel;
+    dbdma_cmd *current = &ch->current;
+
+    DBDMA_DPRINTF("DBDMA_read_memory\n");
+
+    cpu_physical_memory_read(le32_to_cpu(current->phy_addr) + io- 
 >buf_pos,
+                             io->buf, io->buf_len);
+
+    return io->buf_len;
+}
+
+static int dbdma_write_memory(DBDMA_transfer *io)
+{
+    DBDMA_channel *ch = io->channel;
+    dbdma_cmd *current = &ch->current;
+
+    DBDMA_DPRINTF("DBDMA_write_memory\n");
+
+    cpu_physical_memory_write(le32_to_cpu(current->phy_addr) + io- 
 >buf_pos,
+                              io->buf, io->buf_len);
+
+    return io->buf_len;
+}
+
+static int start_output(DBDMA_channel *ch, int key, uint32_t addr,
+                        uint16_t req_count, int is_last)
+{
+    dbdma_cmd *current = &ch->current;
+    uint32_t n;
+
+    DBDMA_DPRINTF("start_output\n");
+
+    /* KEY_REGS, KEY_DEVICE and KEY_STREAM
+     * are not implemented in the mac-io chip
+     */
+
+    DBDMA_DPRINTF("addr 0x%x key 0x%x\n", addr, key);
+    if (!addr || key > KEY_STREAM3) {
+        kill_channel(ch);
+        return 0;
+    }
+
+    ch->io.buf = NULL;
+    ch->io.buf_pos = 0;
+    ch->io.buf_len = 0;
+    ch->io.len = req_count;
+    ch->io.is_last = is_last;
+    n = ch->transfer_handler(&ch->io, dbdma_read_memory);
+
+    if (conditionnal_wait(ch))
+        return 1;
+
+    current->xfer_status = cpu_to_le16(be32_to_cpu(ch- 
 >regs[DBDMA_STATUS]));
+    current->res_count = cpu_to_le16(0);
+    dbdma_cmdptr_save(ch);
+
+    conditionnal_interrupt(ch);
+    conditionnal_branch(ch);
+
+    return 1;
+}
+
+static int start_input(DBDMA_channel *ch, int key, uint32_t addr,
+                       uint16_t req_count, int is_last)
  {
-    DBDMA_DPRINTF("readw 0x" TARGET_FMT_plx " => 0\n", addr);
+    dbdma_cmd *current = &ch->current;
+    uint32_t n;
+
+    DBDMA_DPRINTF("start_input\n");
+
+    /* KEY_REGS, KEY_DEVICE and KEY_STREAM
+     * are not implemented in the mac-io chip
+     */
+
+    if (!addr || key > KEY_STREAM3) {
+        kill_channel(ch);
+        return 0;
+    }
+
+    ch->io.buf = NULL;
+    ch->io.buf_pos = 0;
+    ch->io.buf_len = 0;
+    ch->io.len = req_count;
+    ch->io.is_last = is_last;
+    n = ch->transfer_handler(&ch->io, dbdma_write_memory);
+
+    if (conditionnal_wait(ch))
+        return 1;
+
+    current->xfer_status = cpu_to_le16(be32_to_cpu(ch- 
 >regs[DBDMA_STATUS]));
+    current->res_count = cpu_to_le16(0);
+    dbdma_cmdptr_save(ch);
+
+    conditionnal_interrupt(ch);
+    conditionnal_branch(ch);
+
+    return 1;
+}
+
+static int load_word(DBDMA_channel *ch, int key, uint32_t addr,
+                     uint16_t len)
+{
+    dbdma_cmd *current = &ch->current;
+    uint32_t val;
+
+    DBDMA_DPRINTF("load_word\n");
+
+    /* only implements KEY_SYSTEM */
+
+    if (key != KEY_SYSTEM) {
+        printf("DBDMA: LOAD_WORD, unimplemented key %x\n", key);
+        kill_channel(ch);
+        return 0;
+    }
+
+    cpu_physical_memory_read(addr, (uint8_t*)&val, len);
+
+    if (len == 2)
+        val = (val << 16) | (current->cmd_dep & 0x0000ffff);
+    else if (len == 1)
+        val = (val << 24) | (current->cmd_dep & 0x00ffffff);
+
+    current->cmd_dep = val;
+
+    if (conditionnal_wait(ch))
+        return 1;
+
+    current->xfer_status = cpu_to_le16(be32_to_cpu(ch- 
 >regs[DBDMA_STATUS]));
+    dbdma_cmdptr_save(ch);
+
+    conditionnal_interrupt(ch);
+    next(ch);
+
+    return 1;
+}
+
+static int store_word(DBDMA_channel *ch, int key, uint32_t addr,
+                      uint16_t len)
+{
+    dbdma_cmd *current = &ch->current;
+    uint32_t val;
+
+    DBDMA_DPRINTF("store_word\n");
+
+    /* only implements KEY_SYSTEM */
+
+    if (key != KEY_SYSTEM) {
+        printf("DBDMA: STORE_WORD, unimplemented key %x\n", key);
+        kill_channel(ch);
+        return 0;
+    }
+
+    val = current->cmd_dep;
+    if (len == 2)
+        val >>= 16;
+    else if (len == 1)
+        val >>= 24;
+
+    cpu_physical_memory_write(addr, (uint8_t*)&val, len);
+
+    if (conditionnal_wait(ch))
+        return 1;
+
+    current->xfer_status = cpu_to_le16(be32_to_cpu(ch- 
 >regs[DBDMA_STATUS]));
+    dbdma_cmdptr_save(ch);
+
+    conditionnal_interrupt(ch);
+    next(ch);
+
+    return 1;
+}
+
+static int nop(DBDMA_channel *ch)
+{
+    dbdma_cmd *current = &ch->current;
+
+    if (conditionnal_wait(ch))
+        return 1;
+
+    current->xfer_status = cpu_to_le16(be32_to_cpu(ch- 
 >regs[DBDMA_STATUS]));
+    dbdma_cmdptr_save(ch);
+
+    conditionnal_interrupt(ch);
+    conditionnal_branch(ch);
+
+    return 1;
+}
+
+static int stop(DBDMA_channel *ch)
+{
+    ch->regs[DBDMA_STATUS] &= cpu_to_be32(~(ACTIVE|DEAD));
+
+    /* the stop command does not increment command pointer */

      return 0;
  }

-static uint32_t dbdma_readl (void *opaque, target_phys_addr_t addr)
+static int channel_run(DBDMA_channel *ch)
  {
-    DBDMA_DPRINTF("readl 0x" TARGET_FMT_plx " => 0\n", addr);
+    dbdma_cmd *current = &ch->current;
+    uint16_t cmd, key;
+    uint16_t req_count;
+    uint32_t phy_addr;
+
+    DBDMA_DPRINTF("channel_run\n");
+    dump_dbdma_cmd(current);
+
+    /* clear WAKE flag at command fetch */
+
+    ch->regs[DBDMA_STATUS] &= cpu_to_be32(~WAKE);
+
+    cmd = le16_to_cpu(current->command) & COMMAND_MASK;
+
+    switch (cmd) {
+    case DBDMA_NOP:
+        return nop(ch);
+
+    case DBDMA_STOP:
+        return stop(ch);
+    }
+
+    key = le16_to_cpu(current->command) & 0x0700;
+    req_count = le16_to_cpu(current->req_count);
+    phy_addr = le32_to_cpu(current->phy_addr);
+
+    if (key == KEY_STREAM4) {
+        printf("command %x, invalid key 4\n", cmd);
+        kill_channel(ch);
+        return 0;
+    }
+
+    switch (cmd) {
+    case OUTPUT_MORE:
+        return start_output(ch, key, phy_addr, req_count, 0);
+
+    case OUTPUT_LAST:
+        return start_output(ch, key, phy_addr, req_count, 1);
+
+    case INPUT_MORE:
+        return start_input(ch, key, phy_addr, req_count, 0);
+
+    case INPUT_LAST:
+        return start_input(ch, key, phy_addr, req_count, 1);
+    }
+
+    if (key < KEY_REGS) {
+        printf("command %x, invalid key %x\n", cmd, key);
+        key = KEY_SYSTEM;
+    }
+
+    /* for LOAD_WORD and STORE_WORD, req_count is on 3 bits
+     * and BRANCH is invalid
+     */
+
+    req_count = req_count & 0x0007;
+    if (req_count & 0x4) {
+        req_count = 4;
+        phy_addr &= ~3;
+    } else if (req_count & 0x2) {
+        req_count = 2;
+        phy_addr &= ~1;
+    } else
+        req_count = 1;
+
+    switch (cmd) {
+    case LOAD_WORD:
+        return load_word(ch, key, phy_addr, req_count);
+
+    case STORE_WORD:
+        return store_word(ch, key, phy_addr, req_count);
+    }

      return 0;
  }

+static QEMUBH *dbdma_bh;
+
+static void DBDMA_run (DBDMA_channel *ch)
+{
+    int channel;
+    int rearm = 0;
+
+    for (channel = 0; channel < DBDMA_CHANNELS; channel++, ch++) {
+            uint32_t status = be32_to_cpu(ch->regs[DBDMA_STATUS]);
+            if ((status & RUN) && (status & ACTIVE)) {
+		if (status & FLUSH)
+                    while (channel_run(ch));
+                else if (channel_run(ch))
+                    rearm = 1;
+            }
+            ch->regs[DBDMA_STATUS] &= cpu_to_be32(~FLUSH);
+    }
+
+    if (rearm)
+        qemu_bh_schedule_idle(dbdma_bh);
+}
+
+static void DBDMA_run_bh(void *opaque)
+{
+    DBDMA_channel *ch = opaque;
+
+    DBDMA_DPRINTF("DBDMA_run_bh\n");
+
+    DBDMA_run(ch);
+}
+
+void DBDMA_register_channel(void *dbdma, int nchan, qemu_irq irq,
+                            DBDMA_transfer_handler transfer_handler,
+                            void *opaque)
+{
+    DBDMA_channel *ch = ( DBDMA_channel *)dbdma + nchan;
+
+    DBDMA_DPRINTF("DBDMA_register_channel 0x%x\n", nchan);
+
+    ch->irq = irq;
+    ch->channel = nchan;
+    ch->transfer_handler = transfer_handler;
+    ch->io.opaque = opaque;
+    ch->io.channel = ch;
+}
+
+void DBDMA_schedule(void)
+{
+    CPUState *env = cpu_single_env;
+    if (env)
+        cpu_interrupt(env, CPU_INTERRUPT_EXIT);
+}
+
+static void
+dbdma_control_write(DBDMA_channel *ch)
+{
+    uint16_t mask, value;
+    uint32_t status;
+
+    mask = (be32_to_cpu(ch->regs[DBDMA_CONTROL]) >> 16) & 0xffff;
+    value = be32_to_cpu(ch->regs[DBDMA_CONTROL]) & 0xffff;
+
+    value &= (RUN | PAUSE | FLUSH | WAKE | DEVSTAT);
+
+    status = be32_to_cpu(ch->regs[DBDMA_STATUS]);
+
+    status = (value & mask) | (status & ~mask);
+
+    if (status & WAKE)
+        status |= ACTIVE;
+    if (status & RUN) {
+        status |= ACTIVE;
+        status &= ~DEAD;
+    }
+    if (status & PAUSE)
+        status &= ~ACTIVE;
+    if ((be32_to_cpu(ch->regs[DBDMA_STATUS]) & RUN) && !(status &  
RUN)) {
+        /* RUN is cleared */
+        status &= ~(ACTIVE|DEAD);
+    }
+
+    DBDMA_DPRINTF("    status 0x%08x\n", status);
+
+    ch->regs[DBDMA_STATUS] = cpu_to_be32(status);
+
+    if (status & ACTIVE) {
+        qemu_bh_schedule_idle(dbdma_bh);
+        if (status & FLUSH)
+            DBDMA_schedule();
+    }
+}
+
+static void dbdma_writel (void *opaque,
+                          target_phys_addr_t addr, uint32_t value)
+{
+    int channel = addr >> DBDMA_CHANNEL_SHIFT;
+    DBDMA_channel *ch = (DBDMA_channel *)opaque + channel;
+    int reg = (addr - (channel << DBDMA_CHANNEL_SHIFT)) >> 2;
+
+    DBDMA_DPRINTF("writel 0x" TARGET_FMT_plx " <= 0x%08x\n", addr,  
value);
+    DBDMA_DPRINTF("channel 0x%x reg 0x%x\n",
+                  (uint32_t)addr >> DBDMA_CHANNEL_SHIFT, reg);
+
+    /* cmdptr cannot be modified if channel is RUN or ACTIVE */
+
+    if (reg == DBDMA_CMDPTR_LO &&
+        (ch->regs[DBDMA_STATUS] & cpu_to_be32(RUN | ACTIVE)))
+	return;
+
+    ch->regs[reg] = value;
+
+    switch(reg) {
+    case DBDMA_CONTROL:
+        dbdma_control_write(ch);
+        break;
+    case DBDMA_CMDPTR_LO:
+        /* 16-byte aligned */
+        ch->regs[DBDMA_CMDPTR_LO] &= cpu_to_be32(~0xf);
+        dbdma_cmdptr_load(ch);
+        break;
+    case DBDMA_STATUS:
+    case DBDMA_INTR_SEL:
+    case DBDMA_BRANCH_SEL:
+    case DBDMA_WAIT_SEL:
+        /* nothing to do */
+        break;
+    case DBDMA_XFER_MODE:
+    case DBDMA_CMDPTR_HI:
+    case DBDMA_DATA2PTR_HI:
+    case DBDMA_DATA2PTR_LO:
+    case DBDMA_ADDRESS_HI:
+    case DBDMA_BRANCH_ADDR_HI:
+    case DBDMA_RES1:
+    case DBDMA_RES2:
+    case DBDMA_RES3:
+    case DBDMA_RES4:
+        /* unused */
+        break;
+    }
+}
+
+static uint32_t dbdma_readl (void *opaque, target_phys_addr_t addr)
+{
+    uint32_t value;
+    int channel = addr >> DBDMA_CHANNEL_SHIFT;
+    DBDMA_channel *ch = (DBDMA_channel *)opaque + channel;
+    int reg = (addr - (channel << DBDMA_CHANNEL_SHIFT)) >> 2;
+
+    value = ch->regs[reg];
+
+    DBDMA_DPRINTF("readl 0x" TARGET_FMT_plx " => 0x%08x\n", addr,  
value);
+    DBDMA_DPRINTF("channel 0x%x reg 0x%x\n",
+                  (uint32_t)addr >> DBDMA_CHANNEL_SHIFT, reg);
+
+    switch(reg) {
+    case DBDMA_CONTROL:
+        value = 0;
+        break;
+    case DBDMA_STATUS:
+    case DBDMA_CMDPTR_LO:
+    case DBDMA_INTR_SEL:
+    case DBDMA_BRANCH_SEL:
+    case DBDMA_WAIT_SEL:
+        /* nothing to do */
+        break;
+    case DBDMA_XFER_MODE:
+    case DBDMA_CMDPTR_HI:
+    case DBDMA_DATA2PTR_HI:
+    case DBDMA_DATA2PTR_LO:
+    case DBDMA_ADDRESS_HI:
+    case DBDMA_BRANCH_ADDR_HI:
+        /* unused */
+        value = 0;
+        break;
+    case DBDMA_RES1:
+    case DBDMA_RES2:
+    case DBDMA_RES3:
+    case DBDMA_RES4:
+        /* reserved */
+        break;
+    }
+
+    return value;
+}
+
  static CPUWriteMemoryFunc *dbdma_write[] = {
-    &dbdma_writeb,
-    &dbdma_writew,
-    &dbdma_writel,
+    NULL,
+    NULL,
+    dbdma_writel,
  };

  static CPUReadMemoryFunc *dbdma_read[] = {
-    &dbdma_readb,
-    &dbdma_readw,
-    &dbdma_readl,
+    NULL,
+    NULL,
+    dbdma_readl,
  };

  static void dbdma_save(QEMUFile *f, void *opaque)
  {
+    DBDMA_channel *s = opaque;
+    unsigned int i, j;
+
+    for (i = 0; i < DBDMA_CHANNELS; i++)
+        for (j = 0; j < DBDMA_REGS; j++)
+            qemu_put_be32s(f, &s[i].regs[j]);
  }

  static int dbdma_load(QEMUFile *f, void *opaque, int version_id)
  {
-    if (version_id != 1)
+    DBDMA_channel *s = opaque;
+    unsigned int i, j;
+
+    if (version_id != 2)
          return -EINVAL;

+    for (i = 0; i < DBDMA_CHANNELS; i++)
+        for (j = 0; j < DBDMA_REGS; j++)
+            qemu_get_be32s(f, &s[i].regs[j]);
+
      return 0;
  }

  static void dbdma_reset(void *opaque)
  {
+    DBDMA_channel *s = opaque;
+    int i;
+
+    for (i = 0; i < DBDMA_CHANNELS; i++)
+        memset(s[i].regs, 0, DBDMA_SIZE);
  }

-void dbdma_init (int *dbdma_mem_index)
+void* DBDMA_init (int *dbdma_mem_index)
  {
-    *dbdma_mem_index = cpu_register_io_memory(0, dbdma_read,  
dbdma_write, NULL);
-    register_savevm("dbdma", -1, 1, dbdma_save, dbdma_load, NULL);
-    qemu_register_reset(dbdma_reset, NULL);
-    dbdma_reset(NULL);
+    DBDMA_channel *s;
+
+    s = qemu_mallocz(sizeof(DBDMA_channel) * DBDMA_CHANNELS);
+    if (!s)
+        return NULL;
+
+    *dbdma_mem_index = cpu_register_io_memory(0, dbdma_read,  
dbdma_write, s);
+    register_savevm("dbdma", -1, 1, dbdma_save, dbdma_load, s);
+    qemu_register_reset(dbdma_reset, s);
+    dbdma_reset(s);
+
+    dbdma_bh = qemu_bh_new(DBDMA_run_bh, s);
+
+    return s;
  }
diff --git a/hw/mac_dbdma.h b/hw/mac_dbdma.h
new file mode 100644
index 0000000..d1a02ed
--- /dev/null
+++ b/hw/mac_dbdma.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2009 Laurent Vivier
+ *
+ * Permission is hereby granted, free of charge, to any person  
obtaining a copy
+ * of this software and associated documentation files (the  
"Software"), to deal
+ * in the Software without restriction, including without limitation  
the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/ 
or sell
+ * copies of the Software, and to permit persons to whom the Software  
is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be  
included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,  
EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF  
MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT  
SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES  
OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,  
ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER  
DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+typedef struct {
+    void *opaque;
+    void *channel;
+    int len;
+    int is_last;
+    void *buf;
+    int buf_pos;
+    int buf_len;
+} DBDMA_transfer;
+
+typedef int (*DBDMA_transfer_cb)(DBDMA_transfer *info);
+typedef int (*DBDMA_transfer_handler)(DBDMA_transfer *info,
+                                      DBDMA_transfer_cb cb);
+
+void DBDMA_register_channel(void *dbdma, int nchan, qemu_irq irq,
+                            DBDMA_transfer_handler transfer_handler,
+                            void *opaque);
+void DBDMA_schedule(void);
+void* DBDMA_init (int *dbdma_mem_index);
diff --git a/hw/ppc_chrp.c b/hw/ppc_chrp.c
index 64a613c..e28819d 100644
--- a/hw/ppc_chrp.c
+++ b/hw/ppc_chrp.c
@@ -25,6 +25,7 @@
  #include "hw.h"
  #include "ppc.h"
  #include "ppc_mac.h"
+#include "mac_dbdma.h"
  #include "nvram.h"
  #include "pc.h"
  #include "pci.h"
@@ -86,6 +87,7 @@ static void ppc_core99_init (ram_addr_t ram_size,  
int vga_ram_size,
      int ppc_boot_device;
      int index;
      BlockDriverState *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
+    void *dbdma;

      linux_boot = (kernel_filename != NULL);

@@ -280,6 +282,7 @@ static void ppc_core99_init (ram_addr_t ram_size,  
int vga_ram_size,
          else
              hd[i] = NULL;
      }
+    dbdma = DBDMA_init(&dbdma_mem_index);
  #if 1
      ide_mem_index[0] = pmac_ide_init(&hd[0], pic[0x13]);
      ide_mem_index[1] = pmac_ide_init(&hd[2], pic[0x14]);
@@ -292,7 +295,6 @@ static void ppc_core99_init (ram_addr_t ram_size,  
int vga_ram_size,
      adb_kbd_init(&adb_bus);
      adb_mouse_init(&adb_bus);

-    dbdma_init(&dbdma_mem_index);

      macio_init(pci_bus, 0x0022, 0, pic_mem_index, dbdma_mem_index,
                 cuda_mem_index, NULL, 2, ide_mem_index,  
escc_mem_index);
diff --git a/hw/ppc_mac.h b/hw/ppc_mac.h
index cc70fb7..9ebb6c1 100644
--- a/hw/ppc_mac.h
+++ b/hw/ppc_mac.h
@@ -39,9 +39,6 @@

  #define ESCC_CLOCK 3686400

-/* DBDMA */
-void dbdma_init (int *dbdma_mem_index);
-
  /* Cuda */
  void cuda_init (int *cuda_mem_index, qemu_irq irq);

diff --git a/hw/ppc_oldworld.c b/hw/ppc_oldworld.c
index fa8d106..6771567 100644
--- a/hw/ppc_oldworld.c
+++ b/hw/ppc_oldworld.c
@@ -25,6 +25,7 @@
  #include "hw.h"
  #include "ppc.h"
  #include "ppc_mac.h"
+#include "mac_dbdma.h"
  #include "nvram.h"
  #include "pc.h"
  #include "sysemu.h"
@@ -132,6 +133,7 @@ static void ppc_heathrow_init (ram_addr_t  
ram_size, int vga_ram_size,
      BlockDriverState *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
      int index;
      void *fw_cfg;
+    void *dbdma;

      linux_boot = (kernel_filename != NULL);

@@ -343,6 +345,9 @@ static void ppc_heathrow_init (ram_addr_t  
ram_size, int vga_ram_size,
          hd[1] = NULL;
      else
          hd[1] =  drives_table[index].bdrv;
+
+    dbdma = DBDMA_init(&dbdma_mem_index);
+
      ide_mem_index[0] = -1;
      ide_mem_index[1] = pmac_ide_init(hd, pic[0x0D]);

@@ -355,8 +360,6 @@ static void ppc_heathrow_init (ram_addr_t  
ram_size, int vga_ram_size,
      nvr = macio_nvram_init(&nvram_mem_index, 0x2000);
      pmac_format_nvram_partition(nvr, 0x2000);

-    dbdma_init(&dbdma_mem_index);
-
      macio_init(pci_bus, 0x0010, 1, pic_mem_index, dbdma_mem_index,
                 cuda_mem_index, nvr, 2, ide_mem_index, escc_mem_index);


[-- Attachment #2: 0001-mac-io-DB-DMA-support.patch --]
[-- Type: application/octet-stream, Size: 30565 bytes --]

From 9668e1eb9e892f87a3a3e4c7fba5e9fd20052afd Mon Sep 17 00:00:00 2001
From: Laurent Vivier <Laurent@lvivier.info>
Date: Tue, 27 Jan 2009 22:49:00 +0100
Subject: [PATCH 1/2] mac-io DB-DMA support

This patch adds powermac Descriptor-Based DMA.
It is used by mac-io based IDE, ethernet, sounds and serial devices.

This implementation uses only synchrone I/O.

Signed-off-by: Laurent Vivier <Laurent@lvivier.info>
---
 hw/mac_dbdma.c    |  824 +++++++++++++++++++++++++++++++++++++++++++++++++++--
 hw/mac_dbdma.h    |   41 +++
 hw/ppc_chrp.c     |    4 +-
 hw/ppc_mac.h      |    3 -
 hw/ppc_oldworld.c |    7 +-
 5 files changed, 844 insertions(+), 35 deletions(-)
 create mode 100644 hw/mac_dbdma.h

diff --git a/hw/mac_dbdma.c b/hw/mac_dbdma.c
index 13fbd7b..d01633b 100644
--- a/hw/mac_dbdma.c
+++ b/hw/mac_dbdma.c
@@ -3,6 +3,20 @@
  *
  * Copyright (c) 2005-2007 Fabrice Bellard
  * Copyright (c) 2007 Jocelyn Mayer
+ * Copyright (c) 2009 Laurent Vivier
+ *
+ * some parts from linux-2.6.28, arch/powerpc/include/asm/dbdma.h
+ *
+ *   Definitions for using the Apple Descriptor-Based DMA controller
+ *   in Power Macintosh computers.
+ *
+ *   Copyright (C) 1996 Paul Mackerras.
+ *
+ * some parts from mol 0.9.71
+ *
+ *   Descriptor based DMA emulation
+ *
+ *   Copyright (C) 1998-2004 Samuel Rydh (samuel@ibrium.se)
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -23,7 +37,8 @@
  * THE SOFTWARE.
  */
 #include "hw.h"
-#include "ppc_mac.h"
+#include "isa.h"
+#include "mac_dbdma.h"
 
 /* debug DBDMA */
 //#define DEBUG_DBDMA
@@ -35,79 +50,830 @@ do { printf("DBDMA: " fmt , ##args); } while (0)
 #define DBDMA_DPRINTF(fmt, args...)
 #endif
 
-/* DBDMA: currently no op - should suffice right now */
+/*
+ */
+
+/*
+ * DBDMA control/status registers.  All little-endian.
+ */
 
-static void dbdma_writeb (void *opaque,
-                          target_phys_addr_t addr, uint32_t value)
+#define DBDMA_CONTROL         0x00
+#define DBDMA_STATUS          0x01
+#define DBDMA_CMDPTR_HI       0x02
+#define DBDMA_CMDPTR_LO       0x03
+#define DBDMA_INTR_SEL        0x04
+#define DBDMA_BRANCH_SEL      0x05
+#define DBDMA_WAIT_SEL        0x06
+#define DBDMA_XFER_MODE       0x07
+#define DBDMA_DATA2PTR_HI     0x08
+#define DBDMA_DATA2PTR_LO     0x09
+#define DBDMA_RES1            0x0A
+#define DBDMA_ADDRESS_HI      0x0B
+#define DBDMA_BRANCH_ADDR_HI  0x0C
+#define DBDMA_RES2            0x0D
+#define DBDMA_RES3            0x0E
+#define DBDMA_RES4            0x0F
+
+#define DBDMA_REGS            16
+#define DBDMA_SIZE            (DBDMA_REGS * sizeof(uint32_t))
+
+#define DBDMA_CHANNEL_SHIFT   7
+#define DBDMA_CHANNEL_SIZE    (1 << DBDMA_CHANNEL_SHIFT)
+
+#define DBDMA_CHANNELS        (0x1000 >> DBDMA_CHANNEL_SHIFT)
+
+/* Bits in control and status registers */
+
+#define RUN	0x8000
+#define PAUSE	0x4000
+#define FLUSH	0x2000
+#define WAKE	0x1000
+#define DEAD	0x0800
+#define ACTIVE	0x0400
+#define BT	0x0100
+#define DEVSTAT	0x00ff
+
+/*
+ * DBDMA command structure.  These fields are all little-endian!
+ */
+
+typedef struct dbdma_cmd {
+    uint16_t req_count;	  /* requested byte transfer count */
+    uint16_t command;	  /* command word (has bit-fields) */
+    uint32_t phy_addr;	  /* physical data address */
+    uint32_t cmd_dep;	  /* command-dependent field */
+    uint16_t res_count;	  /* residual count after completion */
+    uint16_t xfer_status; /* transfer status */
+} dbdma_cmd;
+
+/* DBDMA command values in command field */
+
+#define COMMAND_MASK    0xf000
+#define OUTPUT_MORE	0x0000	/* transfer memory data to stream */
+#define OUTPUT_LAST	0x1000	/* ditto followed by end marker */
+#define INPUT_MORE	0x2000	/* transfer stream data to memory */
+#define INPUT_LAST	0x3000	/* ditto, expect end marker */
+#define STORE_WORD	0x4000	/* write word (4 bytes) to device reg */
+#define LOAD_WORD	0x5000	/* read word (4 bytes) from device reg */
+#define DBDMA_NOP	0x6000	/* do nothing */
+#define DBDMA_STOP	0x7000	/* suspend processing */
+
+/* Key values in command field */
+
+#define KEY_MASK        0x0700
+#define KEY_STREAM0	0x0000	/* usual data stream */
+#define KEY_STREAM1	0x0100	/* control/status stream */
+#define KEY_STREAM2	0x0200	/* device-dependent stream */
+#define KEY_STREAM3	0x0300	/* device-dependent stream */
+#define KEY_STREAM4	0x0400	/* reserved */
+#define KEY_REGS	0x0500	/* device register space */
+#define KEY_SYSTEM	0x0600	/* system memory-mapped space */
+#define KEY_DEVICE	0x0700	/* device memory-mapped space */
+
+/* Interrupt control values in command field */
+
+#define INTR_MASK       0x0030
+#define INTR_NEVER	0x0000	/* don't interrupt */
+#define INTR_IFSET	0x0010	/* intr if condition bit is 1 */
+#define INTR_IFCLR	0x0020	/* intr if condition bit is 0 */
+#define INTR_ALWAYS	0x0030	/* always interrupt */
+
+/* Branch control values in command field */
+
+#define BR_MASK         0x000c
+#define BR_NEVER	0x0000	/* don't branch */
+#define BR_IFSET	0x0004	/* branch if condition bit is 1 */
+#define BR_IFCLR	0x0008	/* branch if condition bit is 0 */
+#define BR_ALWAYS	0x000c	/* always branch */
+
+/* Wait control values in command field */
+
+#define WAIT_MASK       0x0003
+#define WAIT_NEVER	0x0000	/* don't wait */
+#define WAIT_IFSET	0x0001	/* wait if condition bit is 1 */
+#define WAIT_IFCLR	0x0002	/* wait if condition bit is 0 */
+#define WAIT_ALWAYS	0x0003	/* always wait */
+
+typedef struct DBDMA_channel {
+    int channel;
+    uint32_t regs[DBDMA_REGS];
+    qemu_irq irq;
+    DBDMA_transfer io;
+    DBDMA_transfer_handler transfer_handler;
+    dbdma_cmd current;
+} DBDMA_channel;
+
+#ifdef DEBUG_DBDMA
+static void dump_dbdma_cmd(dbdma_cmd *cmd)
+{
+    printf("dbdma_cmd %p\n", cmd);
+    printf("    req_count 0x%04x\n", le16_to_cpu(cmd->req_count));
+    printf("    command 0x%04x\n", le16_to_cpu(cmd->command));
+    printf("    phy_addr 0x%08x\n", le32_to_cpu(cmd->phy_addr));
+    printf("    cmd_dep 0x%08x\n", le32_to_cpu(cmd->cmd_dep));
+    printf("    res_count 0x%04x\n", le16_to_cpu(cmd->res_count));
+    printf("    xfer_status 0x%04x\n", le16_to_cpu(cmd->xfer_status));
+}
+#else
+static void dump_dbdma_cmd(dbdma_cmd *cmd)
 {
-    DBDMA_DPRINTF("writeb 0x" TARGET_FMT_plx " <= 0x%08x\n", addr, value);
+}
+#endif
+static void dbdma_cmdptr_load(DBDMA_channel *ch)
+{
+    DBDMA_DPRINTF("dbdma_cmdptr_load 0x%08x\n",
+                  be32_to_cpu(ch->regs[DBDMA_CMDPTR_LO]));
+    cpu_physical_memory_read(be32_to_cpu(ch->regs[DBDMA_CMDPTR_LO]),
+                             (uint8_t*)&ch->current, sizeof(dbdma_cmd));
 }
 
-static void dbdma_writew (void *opaque,
-                          target_phys_addr_t addr, uint32_t value)
+static void dbdma_cmdptr_save(DBDMA_channel *ch)
 {
-    DBDMA_DPRINTF("writew 0x" TARGET_FMT_plx " <= 0x%08x\n", addr, value);
+    DBDMA_DPRINTF("dbdma_cmdptr_save 0x%08x\n",
+                  be32_to_cpu(ch->regs[DBDMA_CMDPTR_LO]));
+    DBDMA_DPRINTF("xfer_status 0x%08x res_count 0x%04x\n",
+                  le16_to_cpu(ch->current.xfer_status),
+                  le16_to_cpu(ch->current.res_count));
+    cpu_physical_memory_write(be32_to_cpu(ch->regs[DBDMA_CMDPTR_LO]),
+                              (uint8_t*)&ch->current, sizeof(dbdma_cmd));
 }
 
-static void dbdma_writel (void *opaque,
-                          target_phys_addr_t addr, uint32_t value)
+static void kill_channel(DBDMA_channel *ch)
 {
-    DBDMA_DPRINTF("writel 0x" TARGET_FMT_plx " <= 0x%08x\n", addr, value);
+    DBDMA_DPRINTF("kill_channel\n");
+
+    ch->regs[DBDMA_STATUS] |= cpu_to_be32(DEAD);
+    ch->regs[DBDMA_STATUS] &= cpu_to_be32(~ACTIVE);
+
+    qemu_irq_raise(ch->irq);
+}
+
+static void conditionnal_interrupt(DBDMA_channel *ch)
+{
+    dbdma_cmd *current = &ch->current;
+    uint16_t intr;
+    uint16_t sel_mask, sel_value;
+    uint32_t status;
+    int cond;
+
+    DBDMA_DPRINTF("conditionnal_interrupt\n");
+
+    intr = be16_to_cpu(current->command) & INTR_MASK;
+
+    switch(intr) {
+    case INTR_NEVER:  /* don't interrupt */
+        return;
+    case INTR_ALWAYS: /* always interrupt */
+        qemu_irq_raise(ch->irq);
+        return;
+    }
+
+    status = be32_to_cpu(ch->regs[DBDMA_STATUS]) & DEVSTAT;
+
+    sel_mask = (be32_to_cpu(ch->regs[DBDMA_INTR_SEL]) >> 16) & 0x0f;
+    sel_value = be32_to_cpu(ch->regs[DBDMA_INTR_SEL]) & 0x0f;
+
+    cond = (status & sel_mask) == (sel_value & sel_mask);
+
+    switch(intr) {
+    case INTR_IFSET:  /* intr if condition bit is 1 */
+        if (cond)
+            qemu_irq_raise(ch->irq);
+        return;
+    case INTR_IFCLR:  /* intr if condition bit is 0 */
+        if (!cond)
+            qemu_irq_raise(ch->irq);
+        return;
+    }
 }
 
-static uint32_t dbdma_readb (void *opaque, target_phys_addr_t addr)
+static int conditionnal_wait(DBDMA_channel *ch)
 {
-    DBDMA_DPRINTF("readb 0x" TARGET_FMT_plx " => 0\n", addr);
+    dbdma_cmd *current = &ch->current;
+    uint16_t wait;
+    uint16_t sel_mask, sel_value;
+    uint32_t status;
+    int cond;
 
+    DBDMA_DPRINTF("conditionnal_wait\n");
+
+    wait = be16_to_cpu(current->command) & WAIT_MASK;
+
+    switch(wait) {
+    case WAIT_NEVER:  /* don't wait */
+        return 0;
+    case WAIT_ALWAYS: /* always wait */
+        return 1;
+    }
+
+    status = be32_to_cpu(ch->regs[DBDMA_STATUS]) & DEVSTAT;
+
+    sel_mask = (be32_to_cpu(ch->regs[DBDMA_WAIT_SEL]) >> 16) & 0x0f;
+    sel_value = be32_to_cpu(ch->regs[DBDMA_WAIT_SEL]) & 0x0f;
+
+    cond = (status & sel_mask) == (sel_value & sel_mask);
+
+    switch(wait) {
+    case WAIT_IFSET:  /* wait if condition bit is 1 */
+        if (cond)
+            return 1;
+        return 0;
+    case WAIT_IFCLR:  /* wait if condition bit is 0 */
+        if (!cond)
+            return 1;
+        return 0;
+    }
     return 0;
 }
 
-static uint32_t dbdma_readw (void *opaque, target_phys_addr_t addr)
+static void next(DBDMA_channel *ch)
+{
+    uint32_t cp;
+
+    ch->regs[DBDMA_STATUS] &= cpu_to_be32(~BT);
+
+    cp = be32_to_cpu(ch->regs[DBDMA_CMDPTR_LO]);
+    ch->regs[DBDMA_CMDPTR_LO] = cpu_to_be32(cp + sizeof(dbdma_cmd));
+    dbdma_cmdptr_load(ch);
+}
+
+static void branch(DBDMA_channel *ch)
+{
+    dbdma_cmd *current = &ch->current;
+
+    ch->regs[DBDMA_CMDPTR_LO] = current->cmd_dep;
+    ch->regs[DBDMA_STATUS] |= cpu_to_be32(BT);
+    dbdma_cmdptr_load(ch);
+}
+
+static void conditionnal_branch(DBDMA_channel *ch)
+{
+    dbdma_cmd *current = &ch->current;
+    uint16_t br;
+    uint16_t sel_mask, sel_value;
+    uint32_t status;
+    int cond;
+
+    DBDMA_DPRINTF("conditionnal_branch\n");
+
+    /* check if we must branch */
+
+    br = be16_to_cpu(current->command) & BR_MASK;
+
+    switch(br) {
+    case BR_NEVER:  /* don't branch */
+        next(ch);
+        return;
+    case BR_ALWAYS: /* always branch */
+        branch(ch);
+        return;
+    }
+
+    status = be32_to_cpu(ch->regs[DBDMA_STATUS]) & DEVSTAT;
+
+    sel_mask = (be32_to_cpu(ch->regs[DBDMA_BRANCH_SEL]) >> 16) & 0x0f;
+    sel_value = be32_to_cpu(ch->regs[DBDMA_BRANCH_SEL]) & 0x0f;
+
+    cond = (status & sel_mask) == (sel_value & sel_mask);
+
+    switch(br) {
+    case BR_IFSET:  /* branch if condition bit is 1 */
+        if (cond)
+            branch(ch);
+        else
+            next(ch);
+        return;
+    case BR_IFCLR:  /* branch if condition bit is 0 */
+        if (!cond)
+            branch(ch);
+        else
+            next(ch);
+        return;
+    }
+}
+
+static int dbdma_read_memory(DBDMA_transfer *io)
+{
+    DBDMA_channel *ch = io->channel;
+    dbdma_cmd *current = &ch->current;
+
+    DBDMA_DPRINTF("DBDMA_read_memory\n");
+
+    cpu_physical_memory_read(le32_to_cpu(current->phy_addr) + io->buf_pos,
+                             io->buf, io->buf_len);
+
+    return io->buf_len;
+}
+
+static int dbdma_write_memory(DBDMA_transfer *io)
+{
+    DBDMA_channel *ch = io->channel;
+    dbdma_cmd *current = &ch->current;
+
+    DBDMA_DPRINTF("DBDMA_write_memory\n");
+
+    cpu_physical_memory_write(le32_to_cpu(current->phy_addr) + io->buf_pos,
+                              io->buf, io->buf_len);
+
+    return io->buf_len;
+}
+
+static int start_output(DBDMA_channel *ch, int key, uint32_t addr,
+                        uint16_t req_count, int is_last)
+{
+    dbdma_cmd *current = &ch->current;
+    uint32_t n;
+
+    DBDMA_DPRINTF("start_output\n");
+
+    /* KEY_REGS, KEY_DEVICE and KEY_STREAM
+     * are not implemented in the mac-io chip
+     */
+
+    DBDMA_DPRINTF("addr 0x%x key 0x%x\n", addr, key);
+    if (!addr || key > KEY_STREAM3) {
+        kill_channel(ch);
+        return 0;
+    }
+
+    ch->io.buf = NULL;
+    ch->io.buf_pos = 0;
+    ch->io.buf_len = 0;
+    ch->io.len = req_count;
+    ch->io.is_last = is_last;
+    n = ch->transfer_handler(&ch->io, dbdma_read_memory);
+
+    if (conditionnal_wait(ch))
+        return 1;
+
+    current->xfer_status = cpu_to_le16(be32_to_cpu(ch->regs[DBDMA_STATUS]));
+    current->res_count = cpu_to_le16(0);
+    dbdma_cmdptr_save(ch);
+
+    conditionnal_interrupt(ch);
+    conditionnal_branch(ch);
+
+    return 1;
+}
+
+static int start_input(DBDMA_channel *ch, int key, uint32_t addr,
+                       uint16_t req_count, int is_last)
 {
-    DBDMA_DPRINTF("readw 0x" TARGET_FMT_plx " => 0\n", addr);
+    dbdma_cmd *current = &ch->current;
+    uint32_t n;
+
+    DBDMA_DPRINTF("start_input\n");
+
+    /* KEY_REGS, KEY_DEVICE and KEY_STREAM
+     * are not implemented in the mac-io chip
+     */
+
+    if (!addr || key > KEY_STREAM3) {
+        kill_channel(ch);
+        return 0;
+    }
+
+    ch->io.buf = NULL;
+    ch->io.buf_pos = 0;
+    ch->io.buf_len = 0;
+    ch->io.len = req_count;
+    ch->io.is_last = is_last;
+    n = ch->transfer_handler(&ch->io, dbdma_write_memory);
+
+    if (conditionnal_wait(ch))
+        return 1;
+
+    current->xfer_status = cpu_to_le16(be32_to_cpu(ch->regs[DBDMA_STATUS]));
+    current->res_count = cpu_to_le16(0);
+    dbdma_cmdptr_save(ch);
+
+    conditionnal_interrupt(ch);
+    conditionnal_branch(ch);
+
+    return 1;
+}
+
+static int load_word(DBDMA_channel *ch, int key, uint32_t addr,
+                     uint16_t len)
+{
+    dbdma_cmd *current = &ch->current;
+    uint32_t val;
+
+    DBDMA_DPRINTF("load_word\n");
+
+    /* only implements KEY_SYSTEM */
+
+    if (key != KEY_SYSTEM) {
+        printf("DBDMA: LOAD_WORD, unimplemented key %x\n", key);
+        kill_channel(ch);
+        return 0;
+    }
+
+    cpu_physical_memory_read(addr, (uint8_t*)&val, len);
+
+    if (len == 2)
+        val = (val << 16) | (current->cmd_dep & 0x0000ffff);
+    else if (len == 1)
+        val = (val << 24) | (current->cmd_dep & 0x00ffffff);
+
+    current->cmd_dep = val;
+
+    if (conditionnal_wait(ch))
+        return 1;
+
+    current->xfer_status = cpu_to_le16(be32_to_cpu(ch->regs[DBDMA_STATUS]));
+    dbdma_cmdptr_save(ch);
+
+    conditionnal_interrupt(ch);
+    next(ch);
+
+    return 1;
+}
+
+static int store_word(DBDMA_channel *ch, int key, uint32_t addr,
+                      uint16_t len)
+{
+    dbdma_cmd *current = &ch->current;
+    uint32_t val;
+
+    DBDMA_DPRINTF("store_word\n");
+
+    /* only implements KEY_SYSTEM */
+
+    if (key != KEY_SYSTEM) {
+        printf("DBDMA: STORE_WORD, unimplemented key %x\n", key);
+        kill_channel(ch);
+        return 0;
+    }
+
+    val = current->cmd_dep;
+    if (len == 2)
+        val >>= 16;
+    else if (len == 1)
+        val >>= 24;
+
+    cpu_physical_memory_write(addr, (uint8_t*)&val, len);
+
+    if (conditionnal_wait(ch))
+        return 1;
+
+    current->xfer_status = cpu_to_le16(be32_to_cpu(ch->regs[DBDMA_STATUS]));
+    dbdma_cmdptr_save(ch);
+
+    conditionnal_interrupt(ch);
+    next(ch);
+
+    return 1;
+}
+
+static int nop(DBDMA_channel *ch)
+{
+    dbdma_cmd *current = &ch->current;
+
+    if (conditionnal_wait(ch))
+        return 1;
+
+    current->xfer_status = cpu_to_le16(be32_to_cpu(ch->regs[DBDMA_STATUS]));
+    dbdma_cmdptr_save(ch);
+
+    conditionnal_interrupt(ch);
+    conditionnal_branch(ch);
+
+    return 1;
+}
+
+static int stop(DBDMA_channel *ch)
+{
+    ch->regs[DBDMA_STATUS] &= cpu_to_be32(~(ACTIVE|DEAD));
+
+    /* the stop command does not increment command pointer */
 
     return 0;
 }
 
-static uint32_t dbdma_readl (void *opaque, target_phys_addr_t addr)
+static int channel_run(DBDMA_channel *ch)
 {
-    DBDMA_DPRINTF("readl 0x" TARGET_FMT_plx " => 0\n", addr);
+    dbdma_cmd *current = &ch->current;
+    uint16_t cmd, key;
+    uint16_t req_count;
+    uint32_t phy_addr;
+
+    DBDMA_DPRINTF("channel_run\n");
+    dump_dbdma_cmd(current);
+
+    /* clear WAKE flag at command fetch */
+
+    ch->regs[DBDMA_STATUS] &= cpu_to_be32(~WAKE);
+
+    cmd = le16_to_cpu(current->command) & COMMAND_MASK;
+
+    switch (cmd) {
+    case DBDMA_NOP:
+        return nop(ch);
+
+    case DBDMA_STOP:
+        return stop(ch);
+    }
+
+    key = le16_to_cpu(current->command) & 0x0700;
+    req_count = le16_to_cpu(current->req_count);
+    phy_addr = le32_to_cpu(current->phy_addr);
+
+    if (key == KEY_STREAM4) {
+        printf("command %x, invalid key 4\n", cmd);
+        kill_channel(ch);
+        return 0;
+    }
+
+    switch (cmd) {
+    case OUTPUT_MORE:
+        return start_output(ch, key, phy_addr, req_count, 0);
+
+    case OUTPUT_LAST:
+        return start_output(ch, key, phy_addr, req_count, 1);
+
+    case INPUT_MORE:
+        return start_input(ch, key, phy_addr, req_count, 0);
+
+    case INPUT_LAST:
+        return start_input(ch, key, phy_addr, req_count, 1);
+    }
+
+    if (key < KEY_REGS) {
+        printf("command %x, invalid key %x\n", cmd, key);
+        key = KEY_SYSTEM;
+    }
+
+    /* for LOAD_WORD and STORE_WORD, req_count is on 3 bits
+     * and BRANCH is invalid
+     */
+
+    req_count = req_count & 0x0007;
+    if (req_count & 0x4) {
+        req_count = 4;
+        phy_addr &= ~3;
+    } else if (req_count & 0x2) {
+        req_count = 2;
+        phy_addr &= ~1;
+    } else
+        req_count = 1;
+
+    switch (cmd) {
+    case LOAD_WORD:
+        return load_word(ch, key, phy_addr, req_count);
+
+    case STORE_WORD:
+        return store_word(ch, key, phy_addr, req_count);
+    }
 
     return 0;
 }
 
+static QEMUBH *dbdma_bh;
+
+static void DBDMA_run (DBDMA_channel *ch)
+{
+    int channel;
+    int rearm = 0;
+
+    for (channel = 0; channel < DBDMA_CHANNELS; channel++, ch++) {
+            uint32_t status = be32_to_cpu(ch->regs[DBDMA_STATUS]);
+            if ((status & RUN) && (status & ACTIVE)) {
+		if (status & FLUSH)
+                    while (channel_run(ch));
+                else if (channel_run(ch))
+                    rearm = 1;
+            }
+            ch->regs[DBDMA_STATUS] &= cpu_to_be32(~FLUSH);
+    }
+
+    if (rearm)
+        qemu_bh_schedule_idle(dbdma_bh);
+}
+
+static void DBDMA_run_bh(void *opaque)
+{
+    DBDMA_channel *ch = opaque;
+
+    DBDMA_DPRINTF("DBDMA_run_bh\n");
+
+    DBDMA_run(ch);
+}
+
+void DBDMA_register_channel(void *dbdma, int nchan, qemu_irq irq,
+                            DBDMA_transfer_handler transfer_handler,
+                            void *opaque)
+{
+    DBDMA_channel *ch = ( DBDMA_channel *)dbdma + nchan;
+
+    DBDMA_DPRINTF("DBDMA_register_channel 0x%x\n", nchan);
+
+    ch->irq = irq;
+    ch->channel = nchan;
+    ch->transfer_handler = transfer_handler;
+    ch->io.opaque = opaque;
+    ch->io.channel = ch;
+}
+
+void DBDMA_schedule(void)
+{
+    CPUState *env = cpu_single_env;
+    if (env)
+        cpu_interrupt(env, CPU_INTERRUPT_EXIT);
+}
+
+static void
+dbdma_control_write(DBDMA_channel *ch)
+{
+    uint16_t mask, value;
+    uint32_t status;
+
+    mask = (be32_to_cpu(ch->regs[DBDMA_CONTROL]) >> 16) & 0xffff;
+    value = be32_to_cpu(ch->regs[DBDMA_CONTROL]) & 0xffff;
+
+    value &= (RUN | PAUSE | FLUSH | WAKE | DEVSTAT);
+ 
+    status = be32_to_cpu(ch->regs[DBDMA_STATUS]);
+
+    status = (value & mask) | (status & ~mask);
+
+    if (status & WAKE)
+        status |= ACTIVE;
+    if (status & RUN) {
+        status |= ACTIVE;
+        status &= ~DEAD;
+    }
+    if (status & PAUSE)
+        status &= ~ACTIVE;
+    if ((be32_to_cpu(ch->regs[DBDMA_STATUS]) & RUN) && !(status & RUN)) {
+        /* RUN is cleared */
+        status &= ~(ACTIVE|DEAD);
+    }
+
+    DBDMA_DPRINTF("    status 0x%08x\n", status);
+
+    ch->regs[DBDMA_STATUS] = cpu_to_be32(status);
+
+    if (status & ACTIVE) {
+        qemu_bh_schedule_idle(dbdma_bh);
+        if (status & FLUSH)
+            DBDMA_schedule();
+    }
+}
+
+static void dbdma_writel (void *opaque,
+                          target_phys_addr_t addr, uint32_t value)
+{
+    int channel = addr >> DBDMA_CHANNEL_SHIFT;
+    DBDMA_channel *ch = (DBDMA_channel *)opaque + channel;
+    int reg = (addr - (channel << DBDMA_CHANNEL_SHIFT)) >> 2;
+
+    DBDMA_DPRINTF("writel 0x" TARGET_FMT_plx " <= 0x%08x\n", addr, value);
+    DBDMA_DPRINTF("channel 0x%x reg 0x%x\n",
+                  (uint32_t)addr >> DBDMA_CHANNEL_SHIFT, reg);
+
+    /* cmdptr cannot be modified if channel is RUN or ACTIVE */
+
+    if (reg == DBDMA_CMDPTR_LO &&
+        (ch->regs[DBDMA_STATUS] & cpu_to_be32(RUN | ACTIVE)))
+	return;
+
+    ch->regs[reg] = value;
+
+    switch(reg) {
+    case DBDMA_CONTROL:
+        dbdma_control_write(ch);
+        break;
+    case DBDMA_CMDPTR_LO:
+        /* 16-byte aligned */
+        ch->regs[DBDMA_CMDPTR_LO] &= cpu_to_be32(~0xf);
+        dbdma_cmdptr_load(ch);
+        break;
+    case DBDMA_STATUS:
+    case DBDMA_INTR_SEL:
+    case DBDMA_BRANCH_SEL:
+    case DBDMA_WAIT_SEL:
+        /* nothing to do */
+        break;
+    case DBDMA_XFER_MODE:
+    case DBDMA_CMDPTR_HI:
+    case DBDMA_DATA2PTR_HI:
+    case DBDMA_DATA2PTR_LO:
+    case DBDMA_ADDRESS_HI:
+    case DBDMA_BRANCH_ADDR_HI:
+    case DBDMA_RES1:
+    case DBDMA_RES2:
+    case DBDMA_RES3:
+    case DBDMA_RES4:
+        /* unused */
+        break;
+    }
+}
+
+static uint32_t dbdma_readl (void *opaque, target_phys_addr_t addr)
+{
+    uint32_t value;
+    int channel = addr >> DBDMA_CHANNEL_SHIFT;
+    DBDMA_channel *ch = (DBDMA_channel *)opaque + channel;
+    int reg = (addr - (channel << DBDMA_CHANNEL_SHIFT)) >> 2;
+
+    value = ch->regs[reg];
+
+    DBDMA_DPRINTF("readl 0x" TARGET_FMT_plx " => 0x%08x\n", addr, value);
+    DBDMA_DPRINTF("channel 0x%x reg 0x%x\n",
+                  (uint32_t)addr >> DBDMA_CHANNEL_SHIFT, reg);
+
+    switch(reg) {
+    case DBDMA_CONTROL:
+        value = 0;
+        break;
+    case DBDMA_STATUS:
+    case DBDMA_CMDPTR_LO:
+    case DBDMA_INTR_SEL:
+    case DBDMA_BRANCH_SEL:
+    case DBDMA_WAIT_SEL:
+        /* nothing to do */
+        break;
+    case DBDMA_XFER_MODE:
+    case DBDMA_CMDPTR_HI:
+    case DBDMA_DATA2PTR_HI:
+    case DBDMA_DATA2PTR_LO:
+    case DBDMA_ADDRESS_HI:
+    case DBDMA_BRANCH_ADDR_HI:
+        /* unused */
+        value = 0;
+        break;
+    case DBDMA_RES1:
+    case DBDMA_RES2:
+    case DBDMA_RES3:
+    case DBDMA_RES4:
+        /* reserved */
+        break;
+    }
+
+    return value;
+}
+
 static CPUWriteMemoryFunc *dbdma_write[] = {
-    &dbdma_writeb,
-    &dbdma_writew,
-    &dbdma_writel,
+    NULL,
+    NULL,
+    dbdma_writel,
 };
 
 static CPUReadMemoryFunc *dbdma_read[] = {
-    &dbdma_readb,
-    &dbdma_readw,
-    &dbdma_readl,
+    NULL,
+    NULL,
+    dbdma_readl,
 };
 
 static void dbdma_save(QEMUFile *f, void *opaque)
 {
+    DBDMA_channel *s = opaque;
+    unsigned int i, j;
+
+    for (i = 0; i < DBDMA_CHANNELS; i++)
+        for (j = 0; j < DBDMA_REGS; j++)
+            qemu_put_be32s(f, &s[i].regs[j]);
 }
 
 static int dbdma_load(QEMUFile *f, void *opaque, int version_id)
 {
-    if (version_id != 1)
+    DBDMA_channel *s = opaque;
+    unsigned int i, j;
+
+    if (version_id != 2)
         return -EINVAL;
 
+    for (i = 0; i < DBDMA_CHANNELS; i++)
+        for (j = 0; j < DBDMA_REGS; j++)
+            qemu_get_be32s(f, &s[i].regs[j]);
+
     return 0;
 }
 
 static void dbdma_reset(void *opaque)
 {
+    DBDMA_channel *s = opaque;
+    int i;
+
+    for (i = 0; i < DBDMA_CHANNELS; i++)
+        memset(s[i].regs, 0, DBDMA_SIZE);
 }
 
-void dbdma_init (int *dbdma_mem_index)
+void* DBDMA_init (int *dbdma_mem_index)
 {
-    *dbdma_mem_index = cpu_register_io_memory(0, dbdma_read, dbdma_write, NULL);
-    register_savevm("dbdma", -1, 1, dbdma_save, dbdma_load, NULL);
-    qemu_register_reset(dbdma_reset, NULL);
-    dbdma_reset(NULL);
+    DBDMA_channel *s;
+
+    s = qemu_mallocz(sizeof(DBDMA_channel) * DBDMA_CHANNELS);
+    if (!s)
+        return NULL;
+
+    *dbdma_mem_index = cpu_register_io_memory(0, dbdma_read, dbdma_write, s);
+    register_savevm("dbdma", -1, 1, dbdma_save, dbdma_load, s);
+    qemu_register_reset(dbdma_reset, s);
+    dbdma_reset(s);
+
+    dbdma_bh = qemu_bh_new(DBDMA_run_bh, s);
+
+    return s;
 }
diff --git a/hw/mac_dbdma.h b/hw/mac_dbdma.h
new file mode 100644
index 0000000..d1a02ed
--- /dev/null
+++ b/hw/mac_dbdma.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2009 Laurent Vivier
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+typedef struct {
+    void *opaque;
+    void *channel;
+    int len;
+    int is_last;
+    void *buf;
+    int buf_pos;
+    int buf_len;
+} DBDMA_transfer;
+
+typedef int (*DBDMA_transfer_cb)(DBDMA_transfer *info);
+typedef int (*DBDMA_transfer_handler)(DBDMA_transfer *info,
+                                      DBDMA_transfer_cb cb);
+
+void DBDMA_register_channel(void *dbdma, int nchan, qemu_irq irq,
+                            DBDMA_transfer_handler transfer_handler,
+                            void *opaque);
+void DBDMA_schedule(void);
+void* DBDMA_init (int *dbdma_mem_index);
diff --git a/hw/ppc_chrp.c b/hw/ppc_chrp.c
index 64a613c..e28819d 100644
--- a/hw/ppc_chrp.c
+++ b/hw/ppc_chrp.c
@@ -25,6 +25,7 @@
 #include "hw.h"
 #include "ppc.h"
 #include "ppc_mac.h"
+#include "mac_dbdma.h"
 #include "nvram.h"
 #include "pc.h"
 #include "pci.h"
@@ -86,6 +87,7 @@ static void ppc_core99_init (ram_addr_t ram_size, int vga_ram_size,
     int ppc_boot_device;
     int index;
     BlockDriverState *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
+    void *dbdma;
 
     linux_boot = (kernel_filename != NULL);
 
@@ -280,6 +282,7 @@ static void ppc_core99_init (ram_addr_t ram_size, int vga_ram_size,
         else
             hd[i] = NULL;
     }
+    dbdma = DBDMA_init(&dbdma_mem_index);
 #if 1
     ide_mem_index[0] = pmac_ide_init(&hd[0], pic[0x13]);
     ide_mem_index[1] = pmac_ide_init(&hd[2], pic[0x14]);
@@ -292,7 +295,6 @@ static void ppc_core99_init (ram_addr_t ram_size, int vga_ram_size,
     adb_kbd_init(&adb_bus);
     adb_mouse_init(&adb_bus);
 
-    dbdma_init(&dbdma_mem_index);
 
     macio_init(pci_bus, 0x0022, 0, pic_mem_index, dbdma_mem_index,
                cuda_mem_index, NULL, 2, ide_mem_index, escc_mem_index);
diff --git a/hw/ppc_mac.h b/hw/ppc_mac.h
index cc70fb7..9ebb6c1 100644
--- a/hw/ppc_mac.h
+++ b/hw/ppc_mac.h
@@ -39,9 +39,6 @@
 
 #define ESCC_CLOCK 3686400
 
-/* DBDMA */
-void dbdma_init (int *dbdma_mem_index);
-
 /* Cuda */
 void cuda_init (int *cuda_mem_index, qemu_irq irq);
 
diff --git a/hw/ppc_oldworld.c b/hw/ppc_oldworld.c
index fa8d106..6771567 100644
--- a/hw/ppc_oldworld.c
+++ b/hw/ppc_oldworld.c
@@ -25,6 +25,7 @@
 #include "hw.h"
 #include "ppc.h"
 #include "ppc_mac.h"
+#include "mac_dbdma.h"
 #include "nvram.h"
 #include "pc.h"
 #include "sysemu.h"
@@ -132,6 +133,7 @@ static void ppc_heathrow_init (ram_addr_t ram_size, int vga_ram_size,
     BlockDriverState *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
     int index;
     void *fw_cfg;
+    void *dbdma;
 
     linux_boot = (kernel_filename != NULL);
 
@@ -343,6 +345,9 @@ static void ppc_heathrow_init (ram_addr_t ram_size, int vga_ram_size,
         hd[1] = NULL;
     else
         hd[1] =  drives_table[index].bdrv;
+
+    dbdma = DBDMA_init(&dbdma_mem_index);
+
     ide_mem_index[0] = -1;
     ide_mem_index[1] = pmac_ide_init(hd, pic[0x0D]);
 
@@ -355,8 +360,6 @@ static void ppc_heathrow_init (ram_addr_t ram_size, int vga_ram_size,
     nvr = macio_nvram_init(&nvram_mem_index, 0x2000);
     pmac_format_nvram_partition(nvr, 0x2000);
 
-    dbdma_init(&dbdma_mem_index);
-
     macio_init(pci_bus, 0x0010, 1, pic_mem_index, dbdma_mem_index,
                cuda_mem_index, nvr, 2, ide_mem_index, escc_mem_index);
 
-- 
1.5.6.5


[-- Attachment #3: Type: text/plain, Size: 1 bytes --]



^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [Qemu-devel] [PATCH 2/2] IDE DB-DMA support
  2009-01-29 20:25 [Qemu-devel] [PATCH 0/2] mac-io IDE DB-DMA support Laurent Vivier
  2009-01-29 21:33 ` [Qemu-devel] [PATCH 1/2] mac-io " Laurent Vivier
@ 2009-01-29 21:33 ` Laurent Vivier
  2009-01-30 20:39 ` [Qemu-devel] [PATCH 0/2] mac-io " Aurelien Jarno
  2 siblings, 0 replies; 5+ messages in thread
From: Laurent Vivier @ 2009-01-29 21:33 UTC (permalink / raw)
  To: qemu-devel

[-- Attachment #1: Type: text/plain, Size: 11339 bytes --]


This patches allows powermac IDE interface to use DB-DMA.

This implementation uses only synchrone I/O.

Signed-off-by: Laurent Vivier <Laurent@lvivier.info>
---
  hw/ide.c          |  170 ++++++++++++++++++++++++++++++++++++++++++++ 
+++------
  hw/ppc_chrp.c     |    4 +-
  hw/ppc_mac.h      |    3 +-
  hw/ppc_oldworld.c |    2 +-
  4 files changed, 156 insertions(+), 23 deletions(-)

diff --git a/hw/ide.c b/hw/ide.c
index c0e357b..7daff5f 100644
--- a/hw/ide.c
+++ b/hw/ide.c
@@ -31,6 +31,7 @@
  #include "qemu-timer.h"
  #include "sysemu.h"
  #include "ppc_mac.h"
+#include "mac_dbdma.h"
  #include "sh.h"

  /* debug IDE devices */
@@ -437,6 +438,8 @@ typedef struct IDEState {
      uint32_t mdata_size;
      uint8_t *mdata_storage;
      int media_changed;
+    /* for pmac */
+    int is_read;
  } IDEState;

  /* XXX: DVDs that could fit on a CD will be reported as a CD */
@@ -1094,6 +1097,7 @@ static void ide_sector_read_dma(IDEState *s)
      s->status = READY_STAT | SEEK_STAT | DRQ_STAT | BUSY_STAT;
      s->io_buffer_index = 0;
      s->io_buffer_size = 0;
+    s->is_read = 1;
      ide_dma_start(s, ide_read_dma_cb);
  }

@@ -1222,6 +1226,7 @@ static void ide_sector_write_dma(IDEState *s)
      s->status = READY_STAT | SEEK_STAT | DRQ_STAT | BUSY_STAT;
      s->io_buffer_index = 0;
      s->io_buffer_size = 0;
+    s->is_read = 0;
      ide_dma_start(s, ide_write_dma_cb);
  }

@@ -3473,21 +3478,130 @@ void pci_piix4_ide_init(PCIBus *bus,  
BlockDriverState **hd_table, int devfn,
      register_savevm("ide", 0, 2, pci_ide_save, pci_ide_load, d);
  }

+#if defined(TARGET_PPC)
  /***********************************************************/
  /* MacIO based PowerPC IDE */

+typedef struct MACIOIDEState {
+    IDEState ide_if[2];
+    void *dbdma;
+    int stream_index;
+} MACIOIDEState;
+
+static int pmac_atapi_read(DBDMA_transfer *info, DBDMA_transfer_cb cb)
+{
+    MACIOIDEState *m = info->opaque;
+    IDEState *s = m->ide_if->cur_drive;
+    int ret;
+
+    if (s->lba == -1)
+        return 0;
+
+    info->buf_pos = 0;
+
+    while (info->buf_pos < info->len && s->packet_transfer_size > 0) {
+
+        ret = cd_read_sector(s->bs, s->lba, s->io_buffer, s- 
 >cd_sector_size);
+        if (ret < 0) {
+            ide_transfer_stop(s);
+            ide_atapi_io_error(s, ret);
+            return info->buf_pos;
+        }
+
+        info->buf = s->io_buffer + m->stream_index;
+
+        info->buf_len = s->cd_sector_size;
+        if (info->buf_pos + info->buf_len > info->len)
+            info->buf_len = info->len - info->buf_pos;
+
+        cb(info);
+
+	/* db-dma can ask for 512 bytes whereas block size is 2048... */
+
+        m->stream_index += info->buf_len;
+        s->lba += m->stream_index / s->cd_sector_size;
+        m->stream_index %= s->cd_sector_size;
+
+        info->buf_pos += info->buf_len;
+        s->packet_transfer_size -= info->buf_len;
+    }
+    if (s->packet_transfer_size <= 0) {
+        s->status = READY_STAT | SEEK_STAT;
+        s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO
+                                       | ATAPI_INT_REASON_CD;
+        ide_set_irq(s);
+    }
+
+    return info->buf_pos;
+}
+
+static int pmac_ide_transfer(DBDMA_transfer *info,
+                             DBDMA_transfer_cb cb)
+{
+    MACIOIDEState *m = info->opaque;
+    IDEState *s = m->ide_if->cur_drive;
+    int64_t sector_num;
+    int ret, n;
+
+    if (s->is_cdrom)
+        return pmac_atapi_read(info, cb);
+
+    info->buf = s->io_buffer;
+    info->buf_pos = 0;
+    while (info->buf_pos < info->len && s->nsector > 0) {
+
+        sector_num = ide_get_sector(s);
+
+        n = s->nsector;
+        if (n > IDE_DMA_BUF_SECTORS)
+            n = IDE_DMA_BUF_SECTORS;
+
+        info->buf_len = n << 9;
+        if (info->buf_pos + info->buf_len > info->len)
+            info->buf_len = info->len - info->buf_pos;
+        n = info->buf_len >> 9;
+
+        if (s->is_read) {
+            ret = bdrv_read(s->bs, sector_num, s->io_buffer, n);
+            if (ret == 0)
+                cb(info);
+        } else {
+            cb(info);
+            ret = bdrv_write(s->bs, sector_num, s->io_buffer, n);
+        }
+
+        if (ret != 0) {
+            ide_rw_error(s);
+            return info->buf_pos;
+        }
+
+        info->buf_pos += n << 9;
+        ide_set_sector(s, sector_num + n);
+        s->nsector -= n;
+    }
+
+    if (s->nsector <= 0) {
+        s->status = READY_STAT | SEEK_STAT;
+        ide_set_irq(s);
+    }
+
+    return info->buf_pos;
+}
+
  /* PowerMac IDE memory IO */
  static void pmac_ide_writeb (void *opaque,
                               target_phys_addr_t addr, uint32_t val)
  {
+    MACIOIDEState *d = opaque;
+
      addr = (addr & 0xFFF) >> 4;
      switch (addr) {
      case 1 ... 7:
-        ide_ioport_write(opaque, addr, val);
+        ide_ioport_write(d->ide_if, addr, val);
          break;
      case 8:
      case 22:
-        ide_cmd_write(opaque, 0, val);
+        ide_cmd_write(d->ide_if, 0, val);
          break;
      default:
          break;
@@ -3497,15 +3611,16 @@ static void pmac_ide_writeb (void *opaque,
  static uint32_t pmac_ide_readb (void *opaque,target_phys_addr_t addr)
  {
      uint8_t retval;
+    MACIOIDEState *d = opaque;

      addr = (addr & 0xFFF) >> 4;
      switch (addr) {
      case 1 ... 7:
-        retval = ide_ioport_read(opaque, addr);
+        retval = ide_ioport_read(d->ide_if, addr);
          break;
      case 8:
      case 22:
-        retval = ide_status_read(opaque, 0);
+        retval = ide_status_read(d->ide_if, 0);
          break;
      default:
          retval = 0xFF;
@@ -3517,22 +3632,25 @@ static uint32_t pmac_ide_readb (void  
*opaque,target_phys_addr_t addr)
  static void pmac_ide_writew (void *opaque,
                               target_phys_addr_t addr, uint32_t val)
  {
+    MACIOIDEState *d = opaque;
+
      addr = (addr & 0xFFF) >> 4;
  #ifdef TARGET_WORDS_BIGENDIAN
      val = bswap16(val);
  #endif
      if (addr == 0) {
-        ide_data_writew(opaque, 0, val);
+        ide_data_writew(d->ide_if, 0, val);
      }
  }

  static uint32_t pmac_ide_readw (void *opaque,target_phys_addr_t addr)
  {
      uint16_t retval;
+    MACIOIDEState *d = opaque;

      addr = (addr & 0xFFF) >> 4;
      if (addr == 0) {
-        retval = ide_data_readw(opaque, 0);
+        retval = ide_data_readw(d->ide_if, 0);
      } else {
          retval = 0xFFFF;
      }
@@ -3545,22 +3663,25 @@ static uint32_t pmac_ide_readw (void  
*opaque,target_phys_addr_t addr)
  static void pmac_ide_writel (void *opaque,
                               target_phys_addr_t addr, uint32_t val)
  {
+    MACIOIDEState *d = opaque;
+
      addr = (addr & 0xFFF) >> 4;
  #ifdef TARGET_WORDS_BIGENDIAN
      val = bswap32(val);
  #endif
      if (addr == 0) {
-        ide_data_writel(opaque, 0, val);
+        ide_data_writel(d->ide_if, 0, val);
      }
  }

  static uint32_t pmac_ide_readl (void *opaque,target_phys_addr_t addr)
  {
      uint32_t retval;
+    MACIOIDEState *d = opaque;

      addr = (addr & 0xFFF) >> 4;
      if (addr == 0) {
-        retval = ide_data_readl(opaque, 0);
+        retval = ide_data_readl(d->ide_if, 0);
      } else {
          retval = 0xFFFFFFFF;
      }
@@ -3584,7 +3705,8 @@ static CPUReadMemoryFunc *pmac_ide_read[] = {

  static void pmac_ide_save(QEMUFile *f, void *opaque)
  {
-    IDEState *s = (IDEState *)opaque;
+    MACIOIDEState *d = opaque;
+    IDEState *s = d->ide_if;
      uint8_t drive1_selected;
      unsigned int i;

@@ -3601,7 +3723,8 @@ static void pmac_ide_save(QEMUFile *f, void  
*opaque)

  static int pmac_ide_load(QEMUFile *f, void *opaque, int version_id)
  {
-    IDEState *s = (IDEState *)opaque;
+    MACIOIDEState *d = opaque;
+    IDEState *s = d->ide_if;
      uint8_t drive1_selected;
      unsigned int i;

@@ -3622,7 +3745,8 @@ static int pmac_ide_load(QEMUFile *f, void  
*opaque, int version_id)

  static void pmac_ide_reset(void *opaque)
  {
-    IDEState *s = (IDEState *)opaque;
+    MACIOIDEState *d = opaque;
+    IDEState *s = d->ide_if;

      ide_reset(&s[0]);
      ide_reset(&s[1]);
@@ -3631,21 +3755,29 @@ static void pmac_ide_reset(void *opaque)
  /* hd_table must contain 4 block drivers */
  /* PowerMac uses memory mapped registers, not I/O. Return the memory
     I/O index to access the ide. */
-int pmac_ide_init (BlockDriverState **hd_table, qemu_irq irq)
+int pmac_ide_init (BlockDriverState **hd_table, qemu_irq irq,
+		   void *dbdma, int channel, qemu_irq dma_irq)
  {
-    IDEState *ide_if;
+    MACIOIDEState *d;
      int pmac_ide_memory;

-    ide_if = qemu_mallocz(sizeof(IDEState) * 2);
-    ide_init2(&ide_if[0], hd_table[0], hd_table[1], irq);
+    d = qemu_mallocz(sizeof(MACIOIDEState));
+    ide_init2(d->ide_if, hd_table[0], hd_table[1], irq);
+
+    if (dbdma) {
+        d->dbdma = dbdma;
+        DBDMA_register_channel(dbdma, channel, dma_irq,  
pmac_ide_transfer, d);
+    }

      pmac_ide_memory = cpu_register_io_memory(0, pmac_ide_read,
-                                             pmac_ide_write,  
&ide_if[0]);
-    register_savevm("ide", 0, 1, pmac_ide_save, pmac_ide_load,  
&ide_if[0]);
-    qemu_register_reset(pmac_ide_reset, &ide_if[0]);
-    pmac_ide_reset(&ide_if[0]);
+                                             pmac_ide_write, d);
+    register_savevm("ide", 0, 1, pmac_ide_save, pmac_ide_load, d);
+    qemu_register_reset(pmac_ide_reset, d);
+    pmac_ide_reset(d);
+
      return pmac_ide_memory;
  }
+#endif /* TARGET_PPC */

  /***********************************************************/
  /* MMIO based ide port
diff --git a/hw/ppc_chrp.c b/hw/ppc_chrp.c
index e28819d..a4da33e 100644
--- a/hw/ppc_chrp.c
+++ b/hw/ppc_chrp.c
@@ -284,8 +284,8 @@ static void ppc_core99_init (ram_addr_t ram_size,  
int vga_ram_size,
      }
      dbdma = DBDMA_init(&dbdma_mem_index);
  #if 1
-    ide_mem_index[0] = pmac_ide_init(&hd[0], pic[0x13]);
-    ide_mem_index[1] = pmac_ide_init(&hd[2], pic[0x14]);
+    ide_mem_index[0] = pmac_ide_init(&hd[0], pic[0x13], dbdma, 0x14,  
pic[0x01]);
+    ide_mem_index[1] = pmac_ide_init(&hd[2], pic[0x14], dbdma, 0x16,  
pic[0x02]);
  #else
      pci_cmd646_ide_init(pci_bus, &hd[0], 0);
  #endif
diff --git a/hw/ppc_mac.h b/hw/ppc_mac.h
index 9ebb6c1..1fe5c57 100644
--- a/hw/ppc_mac.h
+++ b/hw/ppc_mac.h
@@ -48,7 +48,8 @@ void macio_init (PCIBus *bus, int device_id, int  
is_oldworld, int pic_mem_index,
                   int nb_ide, int *ide_mem_index, int escc_mem_index);

  /* NewWorld PowerMac IDE */
-int pmac_ide_init (BlockDriverState **hd_table, qemu_irq irq);
+int pmac_ide_init (BlockDriverState **hd_table, qemu_irq irq,
+                   void *dbdma, int channel, qemu_irq dma_irq);

  /* Heathrow PIC */
  qemu_irq *heathrow_pic_init(int *pmem_index,
diff --git a/hw/ppc_oldworld.c b/hw/ppc_oldworld.c
index 6771567..260e15d 100644
--- a/hw/ppc_oldworld.c
+++ b/hw/ppc_oldworld.c
@@ -349,7 +349,7 @@ static void ppc_heathrow_init (ram_addr_t  
ram_size, int vga_ram_size,
      dbdma = DBDMA_init(&dbdma_mem_index);

      ide_mem_index[0] = -1;
-    ide_mem_index[1] = pmac_ide_init(hd, pic[0x0D]);
+    ide_mem_index[1] = pmac_ide_init(hd, pic[0x0D], dbdma, 0x16,  
pic[0x02]);

      /* cuda also initialize ADB */
      cuda_init(&cuda_mem_index, pic[0x12]);


[-- Attachment #2: 0002-IDE-DB-DMA-support.patch --]
[-- Type: application/octet-stream, Size: 11736 bytes --]

From 4bbf1739e0161ba217a23ac901873fa26e664a69 Mon Sep 17 00:00:00 2001
From: Laurent Vivier <Laurent@lvivier.info>
Date: Thu, 29 Jan 2009 21:11:24 +0100
Subject: [PATCH 2/2] IDE DB-DMA support

This patches allows powermac IDE interface to use DB-DMA.

Signed-off-by: Laurent Vivier <Laurent@lvivier.info>
---
 hw/ide.c          |  170 +++++++++++++++++++++++++++++++++++++++++++++++------
 hw/ppc_chrp.c     |    4 +-
 hw/ppc_mac.h      |    3 +-
 hw/ppc_oldworld.c |    2 +-
 4 files changed, 156 insertions(+), 23 deletions(-)

diff --git a/hw/ide.c b/hw/ide.c
index c0e357b..7daff5f 100644
--- a/hw/ide.c
+++ b/hw/ide.c
@@ -31,6 +31,7 @@
 #include "qemu-timer.h"
 #include "sysemu.h"
 #include "ppc_mac.h"
+#include "mac_dbdma.h"
 #include "sh.h"
 
 /* debug IDE devices */
@@ -437,6 +438,8 @@ typedef struct IDEState {
     uint32_t mdata_size;
     uint8_t *mdata_storage;
     int media_changed;
+    /* for pmac */
+    int is_read;
 } IDEState;
 
 /* XXX: DVDs that could fit on a CD will be reported as a CD */
@@ -1094,6 +1097,7 @@ static void ide_sector_read_dma(IDEState *s)
     s->status = READY_STAT | SEEK_STAT | DRQ_STAT | BUSY_STAT;
     s->io_buffer_index = 0;
     s->io_buffer_size = 0;
+    s->is_read = 1;
     ide_dma_start(s, ide_read_dma_cb);
 }
 
@@ -1222,6 +1226,7 @@ static void ide_sector_write_dma(IDEState *s)
     s->status = READY_STAT | SEEK_STAT | DRQ_STAT | BUSY_STAT;
     s->io_buffer_index = 0;
     s->io_buffer_size = 0;
+    s->is_read = 0;
     ide_dma_start(s, ide_write_dma_cb);
 }
 
@@ -3473,21 +3478,130 @@ void pci_piix4_ide_init(PCIBus *bus, BlockDriverState **hd_table, int devfn,
     register_savevm("ide", 0, 2, pci_ide_save, pci_ide_load, d);
 }
 
+#if defined(TARGET_PPC)
 /***********************************************************/
 /* MacIO based PowerPC IDE */
 
+typedef struct MACIOIDEState {
+    IDEState ide_if[2];
+    void *dbdma;
+    int stream_index;
+} MACIOIDEState;
+
+static int pmac_atapi_read(DBDMA_transfer *info, DBDMA_transfer_cb cb)
+{
+    MACIOIDEState *m = info->opaque;
+    IDEState *s = m->ide_if->cur_drive;
+    int ret;
+
+    if (s->lba == -1)
+        return 0;
+
+    info->buf_pos = 0;
+
+    while (info->buf_pos < info->len && s->packet_transfer_size > 0) {
+
+        ret = cd_read_sector(s->bs, s->lba, s->io_buffer, s->cd_sector_size);
+        if (ret < 0) {
+            ide_transfer_stop(s);
+            ide_atapi_io_error(s, ret);
+            return info->buf_pos;
+        }
+
+        info->buf = s->io_buffer + m->stream_index;
+
+        info->buf_len = s->cd_sector_size;
+        if (info->buf_pos + info->buf_len > info->len)
+            info->buf_len = info->len - info->buf_pos;
+
+        cb(info);
+
+	/* db-dma can ask for 512 bytes whereas block size is 2048... */
+
+        m->stream_index += info->buf_len;
+        s->lba += m->stream_index / s->cd_sector_size;
+        m->stream_index %= s->cd_sector_size;
+
+        info->buf_pos += info->buf_len;
+        s->packet_transfer_size -= info->buf_len;
+    }
+    if (s->packet_transfer_size <= 0) {
+        s->status = READY_STAT | SEEK_STAT;
+        s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO
+                                       | ATAPI_INT_REASON_CD;
+        ide_set_irq(s);
+    }
+
+    return info->buf_pos;
+}
+
+static int pmac_ide_transfer(DBDMA_transfer *info,
+                             DBDMA_transfer_cb cb)
+{
+    MACIOIDEState *m = info->opaque;
+    IDEState *s = m->ide_if->cur_drive;
+    int64_t sector_num;
+    int ret, n;
+
+    if (s->is_cdrom)
+        return pmac_atapi_read(info, cb);
+
+    info->buf = s->io_buffer;
+    info->buf_pos = 0;
+    while (info->buf_pos < info->len && s->nsector > 0) {
+
+        sector_num = ide_get_sector(s);
+
+        n = s->nsector;
+        if (n > IDE_DMA_BUF_SECTORS)
+            n = IDE_DMA_BUF_SECTORS;
+
+        info->buf_len = n << 9;
+        if (info->buf_pos + info->buf_len > info->len)
+            info->buf_len = info->len - info->buf_pos;
+        n = info->buf_len >> 9;
+
+        if (s->is_read) {
+            ret = bdrv_read(s->bs, sector_num, s->io_buffer, n);
+            if (ret == 0)
+                cb(info);
+        } else {
+            cb(info);
+            ret = bdrv_write(s->bs, sector_num, s->io_buffer, n);
+        }
+
+        if (ret != 0) {
+            ide_rw_error(s);
+            return info->buf_pos;
+        }
+
+        info->buf_pos += n << 9;
+        ide_set_sector(s, sector_num + n);
+        s->nsector -= n;
+    }
+
+    if (s->nsector <= 0) {
+        s->status = READY_STAT | SEEK_STAT;
+        ide_set_irq(s);
+    }
+
+    return info->buf_pos;
+}
+
 /* PowerMac IDE memory IO */
 static void pmac_ide_writeb (void *opaque,
                              target_phys_addr_t addr, uint32_t val)
 {
+    MACIOIDEState *d = opaque;
+
     addr = (addr & 0xFFF) >> 4;
     switch (addr) {
     case 1 ... 7:
-        ide_ioport_write(opaque, addr, val);
+        ide_ioport_write(d->ide_if, addr, val);
         break;
     case 8:
     case 22:
-        ide_cmd_write(opaque, 0, val);
+        ide_cmd_write(d->ide_if, 0, val);
         break;
     default:
         break;
@@ -3497,15 +3611,16 @@ static void pmac_ide_writeb (void *opaque,
 static uint32_t pmac_ide_readb (void *opaque,target_phys_addr_t addr)
 {
     uint8_t retval;
+    MACIOIDEState *d = opaque;
 
     addr = (addr & 0xFFF) >> 4;
     switch (addr) {
     case 1 ... 7:
-        retval = ide_ioport_read(opaque, addr);
+        retval = ide_ioport_read(d->ide_if, addr);
         break;
     case 8:
     case 22:
-        retval = ide_status_read(opaque, 0);
+        retval = ide_status_read(d->ide_if, 0);
         break;
     default:
         retval = 0xFF;
@@ -3517,22 +3632,25 @@ static uint32_t pmac_ide_readb (void *opaque,target_phys_addr_t addr)
 static void pmac_ide_writew (void *opaque,
                              target_phys_addr_t addr, uint32_t val)
 {
+    MACIOIDEState *d = opaque;
+
     addr = (addr & 0xFFF) >> 4;
 #ifdef TARGET_WORDS_BIGENDIAN
     val = bswap16(val);
 #endif
     if (addr == 0) {
-        ide_data_writew(opaque, 0, val);
+        ide_data_writew(d->ide_if, 0, val);
     }
 }
 
 static uint32_t pmac_ide_readw (void *opaque,target_phys_addr_t addr)
 {
     uint16_t retval;
+    MACIOIDEState *d = opaque;
 
     addr = (addr & 0xFFF) >> 4;
     if (addr == 0) {
-        retval = ide_data_readw(opaque, 0);
+        retval = ide_data_readw(d->ide_if, 0);
     } else {
         retval = 0xFFFF;
     }
@@ -3545,22 +3663,25 @@ static uint32_t pmac_ide_readw (void *opaque,target_phys_addr_t addr)
 static void pmac_ide_writel (void *opaque,
                              target_phys_addr_t addr, uint32_t val)
 {
+    MACIOIDEState *d = opaque;
+
     addr = (addr & 0xFFF) >> 4;
 #ifdef TARGET_WORDS_BIGENDIAN
     val = bswap32(val);
 #endif
     if (addr == 0) {
-        ide_data_writel(opaque, 0, val);
+        ide_data_writel(d->ide_if, 0, val);
     }
 }
 
 static uint32_t pmac_ide_readl (void *opaque,target_phys_addr_t addr)
 {
     uint32_t retval;
+    MACIOIDEState *d = opaque;
 
     addr = (addr & 0xFFF) >> 4;
     if (addr == 0) {
-        retval = ide_data_readl(opaque, 0);
+        retval = ide_data_readl(d->ide_if, 0);
     } else {
         retval = 0xFFFFFFFF;
     }
@@ -3584,7 +3705,8 @@ static CPUReadMemoryFunc *pmac_ide_read[] = {
 
 static void pmac_ide_save(QEMUFile *f, void *opaque)
 {
-    IDEState *s = (IDEState *)opaque;
+    MACIOIDEState *d = opaque;
+    IDEState *s = d->ide_if;
     uint8_t drive1_selected;
     unsigned int i;
 
@@ -3601,7 +3723,8 @@ static void pmac_ide_save(QEMUFile *f, void *opaque)
 
 static int pmac_ide_load(QEMUFile *f, void *opaque, int version_id)
 {
-    IDEState *s = (IDEState *)opaque;
+    MACIOIDEState *d = opaque;
+    IDEState *s = d->ide_if;
     uint8_t drive1_selected;
     unsigned int i;
 
@@ -3622,7 +3745,8 @@ static int pmac_ide_load(QEMUFile *f, void *opaque, int version_id)
 
 static void pmac_ide_reset(void *opaque)
 {
-    IDEState *s = (IDEState *)opaque;
+    MACIOIDEState *d = opaque;
+    IDEState *s = d->ide_if;
 
     ide_reset(&s[0]);
     ide_reset(&s[1]);
@@ -3631,21 +3755,29 @@ static void pmac_ide_reset(void *opaque)
 /* hd_table must contain 4 block drivers */
 /* PowerMac uses memory mapped registers, not I/O. Return the memory
    I/O index to access the ide. */
-int pmac_ide_init (BlockDriverState **hd_table, qemu_irq irq)
+int pmac_ide_init (BlockDriverState **hd_table, qemu_irq irq,
+		   void *dbdma, int channel, qemu_irq dma_irq)
 {
-    IDEState *ide_if;
+    MACIOIDEState *d;
     int pmac_ide_memory;
 
-    ide_if = qemu_mallocz(sizeof(IDEState) * 2);
-    ide_init2(&ide_if[0], hd_table[0], hd_table[1], irq);
+    d = qemu_mallocz(sizeof(MACIOIDEState));
+    ide_init2(d->ide_if, hd_table[0], hd_table[1], irq);
+
+    if (dbdma) {
+        d->dbdma = dbdma;
+        DBDMA_register_channel(dbdma, channel, dma_irq, pmac_ide_transfer, d);
+    }
 
     pmac_ide_memory = cpu_register_io_memory(0, pmac_ide_read,
-                                             pmac_ide_write, &ide_if[0]);
-    register_savevm("ide", 0, 1, pmac_ide_save, pmac_ide_load, &ide_if[0]);
-    qemu_register_reset(pmac_ide_reset, &ide_if[0]);
-    pmac_ide_reset(&ide_if[0]);
+                                             pmac_ide_write, d);
+    register_savevm("ide", 0, 1, pmac_ide_save, pmac_ide_load, d);
+    qemu_register_reset(pmac_ide_reset, d);
+    pmac_ide_reset(d);
+
     return pmac_ide_memory;
 }
+#endif /* TARGET_PPC */
 
 /***********************************************************/
 /* MMIO based ide port
diff --git a/hw/ppc_chrp.c b/hw/ppc_chrp.c
index e28819d..a4da33e 100644
--- a/hw/ppc_chrp.c
+++ b/hw/ppc_chrp.c
@@ -284,8 +284,8 @@ static void ppc_core99_init (ram_addr_t ram_size, int vga_ram_size,
     }
     dbdma = DBDMA_init(&dbdma_mem_index);
 #if 1
-    ide_mem_index[0] = pmac_ide_init(&hd[0], pic[0x13]);
-    ide_mem_index[1] = pmac_ide_init(&hd[2], pic[0x14]);
+    ide_mem_index[0] = pmac_ide_init(&hd[0], pic[0x13], dbdma, 0x14, pic[0x01]);
+    ide_mem_index[1] = pmac_ide_init(&hd[2], pic[0x14], dbdma, 0x16, pic[0x02]);
 #else
     pci_cmd646_ide_init(pci_bus, &hd[0], 0);
 #endif
diff --git a/hw/ppc_mac.h b/hw/ppc_mac.h
index 9ebb6c1..1fe5c57 100644
--- a/hw/ppc_mac.h
+++ b/hw/ppc_mac.h
@@ -48,7 +48,8 @@ void macio_init (PCIBus *bus, int device_id, int is_oldworld, int pic_mem_index,
                  int nb_ide, int *ide_mem_index, int escc_mem_index);
 
 /* NewWorld PowerMac IDE */
-int pmac_ide_init (BlockDriverState **hd_table, qemu_irq irq);
+int pmac_ide_init (BlockDriverState **hd_table, qemu_irq irq,
+                   void *dbdma, int channel, qemu_irq dma_irq);
 
 /* Heathrow PIC */
 qemu_irq *heathrow_pic_init(int *pmem_index,
diff --git a/hw/ppc_oldworld.c b/hw/ppc_oldworld.c
index 6771567..260e15d 100644
--- a/hw/ppc_oldworld.c
+++ b/hw/ppc_oldworld.c
@@ -349,7 +349,7 @@ static void ppc_heathrow_init (ram_addr_t ram_size, int vga_ram_size,
     dbdma = DBDMA_init(&dbdma_mem_index);
 
     ide_mem_index[0] = -1;
-    ide_mem_index[1] = pmac_ide_init(hd, pic[0x0D]);
+    ide_mem_index[1] = pmac_ide_init(hd, pic[0x0D], dbdma, 0x16, pic[0x02]);
 
     /* cuda also initialize ADB */
     cuda_init(&cuda_mem_index, pic[0x12]);
-- 
1.5.6.5


[-- Attachment #3: Type: text/plain, Size: 1 bytes --]



^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [Qemu-devel] [PATCH 0/2] mac-io IDE DB-DMA support
  2009-01-29 20:25 [Qemu-devel] [PATCH 0/2] mac-io IDE DB-DMA support Laurent Vivier
  2009-01-29 21:33 ` [Qemu-devel] [PATCH 1/2] mac-io " Laurent Vivier
  2009-01-29 21:33 ` [Qemu-devel] [PATCH 2/2] IDE " Laurent Vivier
@ 2009-01-30 20:39 ` Aurelien Jarno
  2009-01-30 20:46   ` Laurent Vivier
  2 siblings, 1 reply; 5+ messages in thread
From: Aurelien Jarno @ 2009-01-30 20:39 UTC (permalink / raw)
  To: qemu-devel; +Cc: laurent

On Thu, Jan 29, 2009 at 09:25:18PM +0100, Laurent Vivier wrote:
> This series of patch allows to use powermac mac-io IDE interface by  
> implementing
> DB-DMA. Linux 2.6.18 seems to not be able to use correctly mac-io IDE  
> interface
> without DMA.
>
> [PATCH 1/2] mac-io DB-DMA support
> [PATCH 2/2] IDE DB-DMA support

Thanks a lot for your work. I have tried it, but I have a few problems
with it. I tried with OpenBIOS revision 431.

With a 2.6.18 kernel (Debian Etch), it works without problem, I get a 
about 13MB/s, compared to 1.5MB/s without DMA and about 21MB/s with the
CMD646.

With the 2.6.26 kernel (Debian Lenny), I get 1.2MB/S only. Moreover,
trying an installation with a CD-ROM, the VM freeze while loading udeb
packages. Note that the DMA is correctly detected and actived.

Given that those patches really improve the support for the mac-io IDE,
I have merged them with one minor change to the first one
(s/conditionnal/conditional/g).

-- 
Aurelien Jarno	                        GPG: 1024D/F1BCDB73
aurelien@aurel32.net                 http://www.aurel32.net

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [Qemu-devel] [PATCH 0/2] mac-io IDE DB-DMA support
  2009-01-30 20:39 ` [Qemu-devel] [PATCH 0/2] mac-io " Aurelien Jarno
@ 2009-01-30 20:46   ` Laurent Vivier
  0 siblings, 0 replies; 5+ messages in thread
From: Laurent Vivier @ 2009-01-30 20:46 UTC (permalink / raw)
  To: Aurelien Jarno; +Cc: qemu-devel


Le 30 janv. 09 à 21:39, Aurelien Jarno a écrit :

> On Thu, Jan 29, 2009 at 09:25:18PM +0100, Laurent Vivier wrote:
>> This series of patch allows to use powermac mac-io IDE interface by
>> implementing
>> DB-DMA. Linux 2.6.18 seems to not be able to use correctly mac-io IDE
>> interface
>> without DMA.
>>
>> [PATCH 1/2] mac-io DB-DMA support
>> [PATCH 2/2] IDE DB-DMA support
>
> Thanks a lot for your work. I have tried it, but I have a few problems
> with it. I tried with OpenBIOS revision 431.
>
> With a 2.6.18 kernel (Debian Etch), it works without problem, I get a
> about 13MB/s, compared to 1.5MB/s without DMA and about 21MB/s with  
> the
> CMD646.
>
> With the 2.6.26 kernel (Debian Lenny), I get 1.2MB/S only. Moreover,
> trying an installation with a CD-ROM, the VM freeze while loading udeb
> packages. Note that the DMA is correctly detected and actived.
>
> Given that those patches really improve the support for the mac-io  
> IDE,
> I have merged them with one minor change to the first one
> (s/conditionnal/conditional/g).

Thank you,

I'm working on new version using asynchronous I/O and  
cpu_physical_memory_map(). It should improve performance.

Regards,
Laurent

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2009-01-30 20:46 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-01-29 20:25 [Qemu-devel] [PATCH 0/2] mac-io IDE DB-DMA support Laurent Vivier
2009-01-29 21:33 ` [Qemu-devel] [PATCH 1/2] mac-io " Laurent Vivier
2009-01-29 21:33 ` [Qemu-devel] [PATCH 2/2] IDE " Laurent Vivier
2009-01-30 20:39 ` [Qemu-devel] [PATCH 0/2] mac-io " Aurelien Jarno
2009-01-30 20:46   ` Laurent Vivier

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.