]> git.proxmox.com Git - mirror_qemu.git/commitdiff
hw: move SD/MMC devices to hw/sd/, configure with default-configs/
authorPaolo Bonzini <pbonzini@redhat.com>
Tue, 5 Feb 2013 13:54:35 +0000 (14:54 +0100)
committerPaolo Bonzini <pbonzini@redhat.com>
Mon, 8 Apr 2013 16:13:14 +0000 (18:13 +0200)
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
hw/arm/Makefile.objs
hw/lm32/Makefile.objs
hw/milkymist-memcard.c [deleted file]
hw/omap_mmc.c [deleted file]
hw/pxa2xx_mmci.c [deleted file]
hw/sd/Makefile.objs
hw/sd/milkymist-memcard.c [new file with mode: 0644]
hw/sd/omap_mmc.c [new file with mode: 0644]
hw/sd/pxa2xx_mmci.c [new file with mode: 0644]

index 76915402976c3045b61b6b77085f02cdfe11cff6..8e8e79907600d5cea2547b9469d1353a4b083d97 100644 (file)
@@ -7,9 +7,9 @@ obj-y += exynos4210_pmu.o
 obj-y += a15mpcore.o
 obj-y += armv7m_nvic.o
 obj-y += pxa2xx_dma.o
-obj-y += pxa2xx_mmci.o pxa2xx_pcmcia.o
+obj-y += pxa2xx_pcmcia.o
 obj-y += zaurus.o
-obj-y += omap_dma.o omap_clk.o omap_mmc.o \
+obj-y += omap_dma.o omap_clk.o \
                 omap_gpio.o omap_intc.o
 obj-y += soc_dma.o \
                 omap_gpmc.o omap_sdrc.o omap_tap.o omap_l4.o
index f911ac61d1ef40bdaf89d3ed02c54214cccc0e3e..bf8d152831af42071957730b0dc7c4ef6a8da352 100644 (file)
@@ -2,7 +2,6 @@
 obj-y += lm32_pic.o
 obj-y += lm32_sys.o
 obj-y += milkymist-hpdmc.o
-obj-y += milkymist-memcard.o
 obj-y += milkymist-pfpu.o
 
 obj-y := $(addprefix ../,$(obj-y))
diff --git a/hw/milkymist-memcard.c b/hw/milkymist-memcard.c
deleted file mode 100644 (file)
index d5944bc..0000000
+++ /dev/null
@@ -1,303 +0,0 @@
-/*
- *  QEMU model of the Milkymist SD Card Controller.
- *
- *  Copyright (c) 2010 Michael Walle <michael@walle.cc>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- *
- *
- * Specification available at:
- *   http://www.milkymist.org/socdoc/memcard.pdf
- */
-
-#include "hw/hw.h"
-#include "hw/sysbus.h"
-#include "sysemu/sysemu.h"
-#include "trace.h"
-#include "qemu/error-report.h"
-#include "sysemu/blockdev.h"
-#include "hw/sd.h"
-
-enum {
-    ENABLE_CMD_TX   = (1<<0),
-    ENABLE_CMD_RX   = (1<<1),
-    ENABLE_DAT_TX   = (1<<2),
-    ENABLE_DAT_RX   = (1<<3),
-};
-
-enum {
-    PENDING_CMD_TX   = (1<<0),
-    PENDING_CMD_RX   = (1<<1),
-    PENDING_DAT_TX   = (1<<2),
-    PENDING_DAT_RX   = (1<<3),
-};
-
-enum {
-    START_CMD_TX    = (1<<0),
-    START_DAT_RX    = (1<<1),
-};
-
-enum {
-    R_CLK2XDIV = 0,
-    R_ENABLE,
-    R_PENDING,
-    R_START,
-    R_CMD,
-    R_DAT,
-    R_MAX
-};
-
-struct MilkymistMemcardState {
-    SysBusDevice busdev;
-    MemoryRegion regs_region;
-    SDState *card;
-
-    int command_write_ptr;
-    int response_read_ptr;
-    int response_len;
-    int ignore_next_cmd;
-    int enabled;
-    uint8_t command[6];
-    uint8_t response[17];
-    uint32_t regs[R_MAX];
-};
-typedef struct MilkymistMemcardState MilkymistMemcardState;
-
-static void update_pending_bits(MilkymistMemcardState *s)
-{
-    /* transmits are instantaneous, thus tx pending bits are never set */
-    s->regs[R_PENDING] = 0;
-    /* if rx is enabled the corresponding pending bits are always set */
-    if (s->regs[R_ENABLE] & ENABLE_CMD_RX) {
-        s->regs[R_PENDING] |= PENDING_CMD_RX;
-    }
-    if (s->regs[R_ENABLE] & ENABLE_DAT_RX) {
-        s->regs[R_PENDING] |= PENDING_DAT_RX;
-    }
-}
-
-static void memcard_sd_command(MilkymistMemcardState *s)
-{
-    SDRequest req;
-
-    req.cmd = s->command[0] & 0x3f;
-    req.arg = (s->command[1] << 24) | (s->command[2] << 16)
-              | (s->command[3] << 8) | s->command[4];
-    req.crc = s->command[5];
-
-    s->response[0] = req.cmd;
-    s->response_len = sd_do_command(s->card, &req, s->response+1);
-    s->response_read_ptr = 0;
-
-    if (s->response_len == 16) {
-        /* R2 response */
-        s->response[0] = 0x3f;
-        s->response_len += 1;
-    } else if (s->response_len == 4) {
-        /* no crc calculation, insert dummy byte */
-        s->response[5] = 0;
-        s->response_len += 2;
-    }
-
-    if (req.cmd == 0) {
-        /* next write is a dummy byte to clock the initialization of the sd
-         * card */
-        s->ignore_next_cmd = 1;
-    }
-}
-
-static uint64_t memcard_read(void *opaque, hwaddr addr,
-                             unsigned size)
-{
-    MilkymistMemcardState *s = opaque;
-    uint32_t r = 0;
-
-    addr >>= 2;
-    switch (addr) {
-    case R_CMD:
-        if (!s->enabled) {
-            r = 0xff;
-        } else {
-            r = s->response[s->response_read_ptr++];
-            if (s->response_read_ptr > s->response_len) {
-                error_report("milkymist_memcard: "
-                        "read more cmd bytes than available. Clipping.");
-                s->response_read_ptr = 0;
-            }
-        }
-        break;
-    case R_DAT:
-        if (!s->enabled) {
-            r = 0xffffffff;
-        } else {
-            r = 0;
-            r |= sd_read_data(s->card) << 24;
-            r |= sd_read_data(s->card) << 16;
-            r |= sd_read_data(s->card) << 8;
-            r |= sd_read_data(s->card);
-        }
-        break;
-    case R_CLK2XDIV:
-    case R_ENABLE:
-    case R_PENDING:
-    case R_START:
-        r = s->regs[addr];
-        break;
-
-    default:
-        error_report("milkymist_memcard: read access to unknown register 0x"
-                TARGET_FMT_plx, addr << 2);
-        break;
-    }
-
-    trace_milkymist_memcard_memory_read(addr << 2, r);
-
-    return r;
-}
-
-static void memcard_write(void *opaque, hwaddr addr, uint64_t value,
-                          unsigned size)
-{
-    MilkymistMemcardState *s = opaque;
-
-    trace_milkymist_memcard_memory_write(addr, value);
-
-    addr >>= 2;
-    switch (addr) {
-    case R_PENDING:
-        /* clear rx pending bits */
-        s->regs[R_PENDING] &= ~(value & (PENDING_CMD_RX | PENDING_DAT_RX));
-        update_pending_bits(s);
-        break;
-    case R_CMD:
-        if (!s->enabled) {
-            break;
-        }
-        if (s->ignore_next_cmd) {
-            s->ignore_next_cmd = 0;
-            break;
-        }
-        s->command[s->command_write_ptr] = value & 0xff;
-        s->command_write_ptr = (s->command_write_ptr + 1) % 6;
-        if (s->command_write_ptr == 0) {
-            memcard_sd_command(s);
-        }
-        break;
-    case R_DAT:
-        if (!s->enabled) {
-            break;
-        }
-        sd_write_data(s->card, (value >> 24) & 0xff);
-        sd_write_data(s->card, (value >> 16) & 0xff);
-        sd_write_data(s->card, (value >> 8) & 0xff);
-        sd_write_data(s->card, value & 0xff);
-        break;
-    case R_ENABLE:
-        s->regs[addr] = value;
-        update_pending_bits(s);
-        break;
-    case R_CLK2XDIV:
-    case R_START:
-        s->regs[addr] = value;
-        break;
-
-    default:
-        error_report("milkymist_memcard: write access to unknown register 0x"
-                TARGET_FMT_plx, addr << 2);
-        break;
-    }
-}
-
-static const MemoryRegionOps memcard_mmio_ops = {
-    .read = memcard_read,
-    .write = memcard_write,
-    .valid = {
-        .min_access_size = 4,
-        .max_access_size = 4,
-    },
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static void milkymist_memcard_reset(DeviceState *d)
-{
-    MilkymistMemcardState *s =
-            container_of(d, MilkymistMemcardState, busdev.qdev);
-    int i;
-
-    s->command_write_ptr = 0;
-    s->response_read_ptr = 0;
-    s->response_len = 0;
-
-    for (i = 0; i < R_MAX; i++) {
-        s->regs[i] = 0;
-    }
-}
-
-static int milkymist_memcard_init(SysBusDevice *dev)
-{
-    MilkymistMemcardState *s = FROM_SYSBUS(typeof(*s), dev);
-    DriveInfo *dinfo;
-
-    dinfo = drive_get_next(IF_SD);
-    s->card = sd_init(dinfo ? dinfo->bdrv : NULL, 0);
-    s->enabled = dinfo ? bdrv_is_inserted(dinfo->bdrv) : 0;
-
-    memory_region_init_io(&s->regs_region, &memcard_mmio_ops, s,
-            "milkymist-memcard", R_MAX * 4);
-    sysbus_init_mmio(dev, &s->regs_region);
-
-    return 0;
-}
-
-static const VMStateDescription vmstate_milkymist_memcard = {
-    .name = "milkymist-memcard",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_INT32(command_write_ptr, MilkymistMemcardState),
-        VMSTATE_INT32(response_read_ptr, MilkymistMemcardState),
-        VMSTATE_INT32(response_len, MilkymistMemcardState),
-        VMSTATE_INT32(ignore_next_cmd, MilkymistMemcardState),
-        VMSTATE_INT32(enabled, MilkymistMemcardState),
-        VMSTATE_UINT8_ARRAY(command, MilkymistMemcardState, 6),
-        VMSTATE_UINT8_ARRAY(response, MilkymistMemcardState, 17),
-        VMSTATE_UINT32_ARRAY(regs, MilkymistMemcardState, R_MAX),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void milkymist_memcard_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = milkymist_memcard_init;
-    dc->reset = milkymist_memcard_reset;
-    dc->vmsd = &vmstate_milkymist_memcard;
-}
-
-static const TypeInfo milkymist_memcard_info = {
-    .name          = "milkymist-memcard",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(MilkymistMemcardState),
-    .class_init    = milkymist_memcard_class_init,
-};
-
-static void milkymist_memcard_register_types(void)
-{
-    type_register_static(&milkymist_memcard_info);
-}
-
-type_init(milkymist_memcard_register_types)
diff --git a/hw/omap_mmc.c b/hw/omap_mmc.c
deleted file mode 100644 (file)
index d4079cd..0000000
+++ /dev/null
@@ -1,641 +0,0 @@
-/*
- * OMAP on-chip MMC/SD host emulation.
- *
- * Copyright (C) 2006-2007 Andrzej Zaborowski  <balrog@zabor.org>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 or
- * (at your option) version 3 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-#include "hw/hw.h"
-#include "hw/arm/omap.h"
-#include "hw/sd.h"
-
-struct omap_mmc_s {
-    qemu_irq irq;
-    qemu_irq *dma;
-    qemu_irq coverswitch;
-    MemoryRegion iomem;
-    omap_clk clk;
-    SDState *card;
-    uint16_t last_cmd;
-    uint16_t sdio;
-    uint16_t rsp[8];
-    uint32_t arg;
-    int lines;
-    int dw;
-    int mode;
-    int enable;
-    int be;
-    int rev;
-    uint16_t status;
-    uint16_t mask;
-    uint8_t cto;
-    uint16_t dto;
-    int clkdiv;
-    uint16_t fifo[32];
-    int fifo_start;
-    int fifo_len;
-    uint16_t blen;
-    uint16_t blen_counter;
-    uint16_t nblk;
-    uint16_t nblk_counter;
-    int tx_dma;
-    int rx_dma;
-    int af_level;
-    int ae_level;
-
-    int ddir;
-    int transfer;
-
-    int cdet_wakeup;
-    int cdet_enable;
-    int cdet_state;
-    qemu_irq cdet;
-};
-
-static void omap_mmc_interrupts_update(struct omap_mmc_s *s)
-{
-    qemu_set_irq(s->irq, !!(s->status & s->mask));
-}
-
-static void omap_mmc_fifolevel_update(struct omap_mmc_s *host)
-{
-    if (!host->transfer && !host->fifo_len) {
-        host->status &= 0xf3ff;
-        return;
-    }
-
-    if (host->fifo_len > host->af_level && host->ddir) {
-        if (host->rx_dma) {
-            host->status &= 0xfbff;
-            qemu_irq_raise(host->dma[1]);
-        } else
-            host->status |= 0x0400;
-    } else {
-        host->status &= 0xfbff;
-        qemu_irq_lower(host->dma[1]);
-    }
-
-    if (host->fifo_len < host->ae_level && !host->ddir) {
-        if (host->tx_dma) {
-            host->status &= 0xf7ff;
-            qemu_irq_raise(host->dma[0]);
-        } else
-            host->status |= 0x0800;
-    } else {
-        qemu_irq_lower(host->dma[0]);
-        host->status &= 0xf7ff;
-    }
-}
-
-typedef enum {
-    sd_nore = 0,       /* no response */
-    sd_r1,             /* normal response command */
-    sd_r2,             /* CID, CSD registers */
-    sd_r3,             /* OCR register */
-    sd_r6 = 6,         /* Published RCA response */
-    sd_r1b = -1,
-} sd_rsp_type_t;
-
-static void omap_mmc_command(struct omap_mmc_s *host, int cmd, int dir,
-                sd_cmd_type_t type, int busy, sd_rsp_type_t resptype, int init)
-{
-    uint32_t rspstatus, mask;
-    int rsplen, timeout;
-    SDRequest request;
-    uint8_t response[16];
-
-    if (init && cmd == 0) {
-        host->status |= 0x0001;
-        return;
-    }
-
-    if (resptype == sd_r1 && busy)
-        resptype = sd_r1b;
-
-    if (type == sd_adtc) {
-        host->fifo_start = 0;
-        host->fifo_len = 0;
-        host->transfer = 1;
-        host->ddir = dir;
-    } else
-        host->transfer = 0;
-    timeout = 0;
-    mask = 0;
-    rspstatus = 0;
-
-    request.cmd = cmd;
-    request.arg = host->arg;
-    request.crc = 0; /* FIXME */
-
-    rsplen = sd_do_command(host->card, &request, response);
-
-    /* TODO: validate CRCs */
-    switch (resptype) {
-    case sd_nore:
-        rsplen = 0;
-        break;
-
-    case sd_r1:
-    case sd_r1b:
-        if (rsplen < 4) {
-            timeout = 1;
-            break;
-        }
-        rsplen = 4;
-
-        mask = OUT_OF_RANGE | ADDRESS_ERROR | BLOCK_LEN_ERROR |
-                ERASE_SEQ_ERROR | ERASE_PARAM | WP_VIOLATION |
-                LOCK_UNLOCK_FAILED | COM_CRC_ERROR | ILLEGAL_COMMAND |
-                CARD_ECC_FAILED | CC_ERROR | SD_ERROR |
-                CID_CSD_OVERWRITE;
-        if (host->sdio & (1 << 13))
-            mask |= AKE_SEQ_ERROR;
-        rspstatus = (response[0] << 24) | (response[1] << 16) |
-                (response[2] << 8) | (response[3] << 0);
-        break;
-
-    case sd_r2:
-        if (rsplen < 16) {
-            timeout = 1;
-            break;
-        }
-        rsplen = 16;
-        break;
-
-    case sd_r3:
-        if (rsplen < 4) {
-            timeout = 1;
-            break;
-        }
-        rsplen = 4;
-
-        rspstatus = (response[0] << 24) | (response[1] << 16) |
-                (response[2] << 8) | (response[3] << 0);
-        if (rspstatus & 0x80000000)
-            host->status &= 0xe000;
-        else
-            host->status |= 0x1000;
-        break;
-
-    case sd_r6:
-        if (rsplen < 4) {
-            timeout = 1;
-            break;
-        }
-        rsplen = 4;
-
-        mask = 0xe000 | AKE_SEQ_ERROR;
-        rspstatus = (response[2] << 8) | (response[3] << 0);
-    }
-
-    if (rspstatus & mask)
-        host->status |= 0x4000;
-    else
-        host->status &= 0xb000;
-
-    if (rsplen)
-        for (rsplen = 0; rsplen < 8; rsplen ++)
-            host->rsp[~rsplen & 7] = response[(rsplen << 1) | 1] |
-                    (response[(rsplen << 1) | 0] << 8);
-
-    if (timeout)
-        host->status |= 0x0080;
-    else if (cmd == 12)
-        host->status |= 0x0005;        /* Makes it more real */
-    else
-        host->status |= 0x0001;
-}
-
-static void omap_mmc_transfer(struct omap_mmc_s *host)
-{
-    uint8_t value;
-
-    if (!host->transfer)
-        return;
-
-    while (1) {
-        if (host->ddir) {
-            if (host->fifo_len > host->af_level)
-                break;
-
-            value = sd_read_data(host->card);
-            host->fifo[(host->fifo_start + host->fifo_len) & 31] = value;
-            if (-- host->blen_counter) {
-                value = sd_read_data(host->card);
-                host->fifo[(host->fifo_start + host->fifo_len) & 31] |=
-                        value << 8;
-                host->blen_counter --;
-            }
-
-            host->fifo_len ++;
-        } else {
-            if (!host->fifo_len)
-                break;
-
-            value = host->fifo[host->fifo_start] & 0xff;
-            sd_write_data(host->card, value);
-            if (-- host->blen_counter) {
-                value = host->fifo[host->fifo_start] >> 8;
-                sd_write_data(host->card, value);
-                host->blen_counter --;
-            }
-
-            host->fifo_start ++;
-            host->fifo_len --;
-            host->fifo_start &= 31;
-        }
-
-        if (host->blen_counter == 0) {
-            host->nblk_counter --;
-            host->blen_counter = host->blen;
-
-            if (host->nblk_counter == 0) {
-                host->nblk_counter = host->nblk;
-                host->transfer = 0;
-                host->status |= 0x0008;
-                break;
-            }
-        }
-    }
-}
-
-static void omap_mmc_update(void *opaque)
-{
-    struct omap_mmc_s *s = opaque;
-    omap_mmc_transfer(s);
-    omap_mmc_fifolevel_update(s);
-    omap_mmc_interrupts_update(s);
-}
-
-void omap_mmc_reset(struct omap_mmc_s *host)
-{
-    host->last_cmd = 0;
-    memset(host->rsp, 0, sizeof(host->rsp));
-    host->arg = 0;
-    host->dw = 0;
-    host->mode = 0;
-    host->enable = 0;
-    host->status = 0;
-    host->mask = 0;
-    host->cto = 0;
-    host->dto = 0;
-    host->fifo_len = 0;
-    host->blen = 0;
-    host->blen_counter = 0;
-    host->nblk = 0;
-    host->nblk_counter = 0;
-    host->tx_dma = 0;
-    host->rx_dma = 0;
-    host->ae_level = 0x00;
-    host->af_level = 0x1f;
-    host->transfer = 0;
-    host->cdet_wakeup = 0;
-    host->cdet_enable = 0;
-    qemu_set_irq(host->coverswitch, host->cdet_state);
-    host->clkdiv = 0;
-}
-
-static uint64_t omap_mmc_read(void *opaque, hwaddr offset,
-                              unsigned size)
-{
-    uint16_t i;
-    struct omap_mmc_s *s = (struct omap_mmc_s *) opaque;
-
-    if (size != 2) {
-        return omap_badwidth_read16(opaque, offset);
-    }
-
-    switch (offset) {
-    case 0x00: /* MMC_CMD */
-        return s->last_cmd;
-
-    case 0x04: /* MMC_ARGL */
-        return s->arg & 0x0000ffff;
-
-    case 0x08: /* MMC_ARGH */
-        return s->arg >> 16;
-
-    case 0x0c: /* MMC_CON */
-        return (s->dw << 15) | (s->mode << 12) | (s->enable << 11) | 
-                (s->be << 10) | s->clkdiv;
-
-    case 0x10: /* MMC_STAT */
-        return s->status;
-
-    case 0x14: /* MMC_IE */
-        return s->mask;
-
-    case 0x18: /* MMC_CTO */
-        return s->cto;
-
-    case 0x1c: /* MMC_DTO */
-        return s->dto;
-
-    case 0x20: /* MMC_DATA */
-        /* TODO: support 8-bit access */
-        i = s->fifo[s->fifo_start];
-        if (s->fifo_len == 0) {
-            printf("MMC: FIFO underrun\n");
-            return i;
-        }
-        s->fifo_start ++;
-        s->fifo_len --;
-        s->fifo_start &= 31;
-        omap_mmc_transfer(s);
-        omap_mmc_fifolevel_update(s);
-        omap_mmc_interrupts_update(s);
-        return i;
-
-    case 0x24: /* MMC_BLEN */
-        return s->blen_counter;
-
-    case 0x28: /* MMC_NBLK */
-        return s->nblk_counter;
-
-    case 0x2c: /* MMC_BUF */
-        return (s->rx_dma << 15) | (s->af_level << 8) |
-            (s->tx_dma << 7) | s->ae_level;
-
-    case 0x30: /* MMC_SPI */
-        return 0x0000;
-    case 0x34: /* MMC_SDIO */
-        return (s->cdet_wakeup << 2) | (s->cdet_enable) | s->sdio;
-    case 0x38: /* MMC_SYST */
-        return 0x0000;
-
-    case 0x3c: /* MMC_REV */
-        return s->rev;
-
-    case 0x40: /* MMC_RSP0 */
-    case 0x44: /* MMC_RSP1 */
-    case 0x48: /* MMC_RSP2 */
-    case 0x4c: /* MMC_RSP3 */
-    case 0x50: /* MMC_RSP4 */
-    case 0x54: /* MMC_RSP5 */
-    case 0x58: /* MMC_RSP6 */
-    case 0x5c: /* MMC_RSP7 */
-        return s->rsp[(offset - 0x40) >> 2];
-
-    /* OMAP2-specific */
-    case 0x60: /* MMC_IOSR */
-    case 0x64: /* MMC_SYSC */
-        return 0;
-    case 0x68: /* MMC_SYSS */
-        return 1;                                              /* RSTD */
-    }
-
-    OMAP_BAD_REG(offset);
-    return 0;
-}
-
-static void omap_mmc_write(void *opaque, hwaddr offset,
-                           uint64_t value, unsigned size)
-{
-    int i;
-    struct omap_mmc_s *s = (struct omap_mmc_s *) opaque;
-
-    if (size != 2) {
-        return omap_badwidth_write16(opaque, offset, value);
-    }
-
-    switch (offset) {
-    case 0x00: /* MMC_CMD */
-        if (!s->enable)
-            break;
-
-        s->last_cmd = value;
-        for (i = 0; i < 8; i ++)
-            s->rsp[i] = 0x0000;
-        omap_mmc_command(s, value & 63, (value >> 15) & 1,
-                (sd_cmd_type_t) ((value >> 12) & 3),
-                (value >> 11) & 1,
-                (sd_rsp_type_t) ((value >> 8) & 7),
-                (value >> 7) & 1);
-        omap_mmc_update(s);
-        break;
-
-    case 0x04: /* MMC_ARGL */
-        s->arg &= 0xffff0000;
-        s->arg |= 0x0000ffff & value;
-        break;
-
-    case 0x08: /* MMC_ARGH */
-        s->arg &= 0x0000ffff;
-        s->arg |= value << 16;
-        break;
-
-    case 0x0c: /* MMC_CON */
-        s->dw = (value >> 15) & 1;
-        s->mode = (value >> 12) & 3;
-        s->enable = (value >> 11) & 1;
-        s->be = (value >> 10) & 1;
-        s->clkdiv = (value >> 0) & (s->rev >= 2 ? 0x3ff : 0xff);
-        if (s->mode != 0)
-            printf("SD mode %i unimplemented!\n", s->mode);
-        if (s->be != 0)
-            printf("SD FIFO byte sex unimplemented!\n");
-        if (s->dw != 0 && s->lines < 4)
-            printf("4-bit SD bus enabled\n");
-        if (!s->enable)
-            omap_mmc_reset(s);
-        break;
-
-    case 0x10: /* MMC_STAT */
-        s->status &= ~value;
-        omap_mmc_interrupts_update(s);
-        break;
-
-    case 0x14: /* MMC_IE */
-        s->mask = value & 0x7fff;
-        omap_mmc_interrupts_update(s);
-        break;
-
-    case 0x18: /* MMC_CTO */
-        s->cto = value & 0xff;
-        if (s->cto > 0xfd && s->rev <= 1)
-            printf("MMC: CTO of 0xff and 0xfe cannot be used!\n");
-        break;
-
-    case 0x1c: /* MMC_DTO */
-        s->dto = value & 0xffff;
-        break;
-
-    case 0x20: /* MMC_DATA */
-        /* TODO: support 8-bit access */
-        if (s->fifo_len == 32)
-            break;
-        s->fifo[(s->fifo_start + s->fifo_len) & 31] = value;
-        s->fifo_len ++;
-        omap_mmc_transfer(s);
-        omap_mmc_fifolevel_update(s);
-        omap_mmc_interrupts_update(s);
-        break;
-
-    case 0x24: /* MMC_BLEN */
-        s->blen = (value & 0x07ff) + 1;
-        s->blen_counter = s->blen;
-        break;
-
-    case 0x28: /* MMC_NBLK */
-        s->nblk = (value & 0x07ff) + 1;
-        s->nblk_counter = s->nblk;
-        s->blen_counter = s->blen;
-        break;
-
-    case 0x2c: /* MMC_BUF */
-        s->rx_dma = (value >> 15) & 1;
-        s->af_level = (value >> 8) & 0x1f;
-        s->tx_dma = (value >> 7) & 1;
-        s->ae_level = value & 0x1f;
-
-        if (s->rx_dma)
-            s->status &= 0xfbff;
-        if (s->tx_dma)
-            s->status &= 0xf7ff;
-        omap_mmc_fifolevel_update(s);
-        omap_mmc_interrupts_update(s);
-        break;
-
-    /* SPI, SDIO and TEST modes unimplemented */
-    case 0x30: /* MMC_SPI (OMAP1 only) */
-        break;
-    case 0x34: /* MMC_SDIO */
-        s->sdio = value & (s->rev >= 2 ? 0xfbf3 : 0x2020);
-        s->cdet_wakeup = (value >> 9) & 1;
-        s->cdet_enable = (value >> 2) & 1;
-        break;
-    case 0x38: /* MMC_SYST */
-        break;
-
-    case 0x3c: /* MMC_REV */
-    case 0x40: /* MMC_RSP0 */
-    case 0x44: /* MMC_RSP1 */
-    case 0x48: /* MMC_RSP2 */
-    case 0x4c: /* MMC_RSP3 */
-    case 0x50: /* MMC_RSP4 */
-    case 0x54: /* MMC_RSP5 */
-    case 0x58: /* MMC_RSP6 */
-    case 0x5c: /* MMC_RSP7 */
-        OMAP_RO_REG(offset);
-        break;
-
-    /* OMAP2-specific */
-    case 0x60: /* MMC_IOSR */
-        if (value & 0xf)
-            printf("MMC: SDIO bits used!\n");
-        break;
-    case 0x64: /* MMC_SYSC */
-        if (value & (1 << 2))                                  /* SRTS */
-            omap_mmc_reset(s);
-        break;
-    case 0x68: /* MMC_SYSS */
-        OMAP_RO_REG(offset);
-        break;
-
-    default:
-        OMAP_BAD_REG(offset);
-    }
-}
-
-static const MemoryRegionOps omap_mmc_ops = {
-    .read = omap_mmc_read,
-    .write = omap_mmc_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static void omap_mmc_cover_cb(void *opaque, int line, int level)
-{
-    struct omap_mmc_s *host = (struct omap_mmc_s *) opaque;
-
-    if (!host->cdet_state && level) {
-        host->status |= 0x0002;
-        omap_mmc_interrupts_update(host);
-        if (host->cdet_wakeup) {
-            /* TODO: Assert wake-up */
-        }
-    }
-
-    if (host->cdet_state != level) {
-        qemu_set_irq(host->coverswitch, level);
-        host->cdet_state = level;
-    }
-}
-
-struct omap_mmc_s *omap_mmc_init(hwaddr base,
-                MemoryRegion *sysmem,
-                BlockDriverState *bd,
-                qemu_irq irq, qemu_irq dma[], omap_clk clk)
-{
-    struct omap_mmc_s *s = (struct omap_mmc_s *)
-            g_malloc0(sizeof(struct omap_mmc_s));
-
-    s->irq = irq;
-    s->dma = dma;
-    s->clk = clk;
-    s->lines = 1;      /* TODO: needs to be settable per-board */
-    s->rev = 1;
-
-    omap_mmc_reset(s);
-
-    memory_region_init_io(&s->iomem, &omap_mmc_ops, s, "omap.mmc", 0x800);
-    memory_region_add_subregion(sysmem, base, &s->iomem);
-
-    /* Instantiate the storage */
-    s->card = sd_init(bd, 0);
-
-    return s;
-}
-
-struct omap_mmc_s *omap2_mmc_init(struct omap_target_agent_s *ta,
-                BlockDriverState *bd, qemu_irq irq, qemu_irq dma[],
-                omap_clk fclk, omap_clk iclk)
-{
-    struct omap_mmc_s *s = (struct omap_mmc_s *)
-            g_malloc0(sizeof(struct omap_mmc_s));
-
-    s->irq = irq;
-    s->dma = dma;
-    s->clk = fclk;
-    s->lines = 4;
-    s->rev = 2;
-
-    omap_mmc_reset(s);
-
-    memory_region_init_io(&s->iomem, &omap_mmc_ops, s, "omap.mmc",
-                          omap_l4_region_size(ta, 0));
-    omap_l4_attach(ta, 0, &s->iomem);
-
-    /* Instantiate the storage */
-    s->card = sd_init(bd, 0);
-
-    s->cdet = qemu_allocate_irqs(omap_mmc_cover_cb, s, 1)[0];
-    sd_set_cb(s->card, NULL, s->cdet);
-
-    return s;
-}
-
-void omap_mmc_handlers(struct omap_mmc_s *s, qemu_irq ro, qemu_irq cover)
-{
-    if (s->cdet) {
-        sd_set_cb(s->card, ro, s->cdet);
-        s->coverswitch = cover;
-        qemu_set_irq(cover, s->cdet_state);
-    } else
-        sd_set_cb(s->card, ro, cover);
-}
-
-void omap_mmc_enable(struct omap_mmc_s *s, int enable)
-{
-    sd_enable(s->card, enable);
-}
diff --git a/hw/pxa2xx_mmci.c b/hw/pxa2xx_mmci.c
deleted file mode 100644 (file)
index 2db1cab..0000000
+++ /dev/null
@@ -1,553 +0,0 @@
-/*
- * Intel XScale PXA255/270 MultiMediaCard/SD/SDIO Controller emulation.
- *
- * Copyright (c) 2006 Openedhand Ltd.
- * Written by Andrzej Zaborowski <balrog@zabor.org>
- *
- * This code is licensed under the GPLv2.
- *
- * Contributions after 2012-01-13 are licensed under the terms of the
- * GNU GPL, version 2 or (at your option) any later version.
- */
-
-#include "hw/hw.h"
-#include "hw/arm/pxa.h"
-#include "hw/sd.h"
-#include "hw/qdev.h"
-
-struct PXA2xxMMCIState {
-    MemoryRegion iomem;
-    qemu_irq irq;
-    qemu_irq rx_dma;
-    qemu_irq tx_dma;
-
-    SDState *card;
-
-    uint32_t status;
-    uint32_t clkrt;
-    uint32_t spi;
-    uint32_t cmdat;
-    uint32_t resp_tout;
-    uint32_t read_tout;
-    int blklen;
-    int numblk;
-    uint32_t intmask;
-    uint32_t intreq;
-    int cmd;
-    uint32_t arg;
-
-    int active;
-    int bytesleft;
-    uint8_t tx_fifo[64];
-    int tx_start;
-    int tx_len;
-    uint8_t rx_fifo[32];
-    int rx_start;
-    int rx_len;
-    uint16_t resp_fifo[9];
-    int resp_len;
-
-    int cmdreq;
-    int ac_width;
-};
-
-#define MMC_STRPCL     0x00    /* MMC Clock Start/Stop register */
-#define MMC_STAT       0x04    /* MMC Status register */
-#define MMC_CLKRT      0x08    /* MMC Clock Rate register */
-#define MMC_SPI                0x0c    /* MMC SPI Mode register */
-#define MMC_CMDAT      0x10    /* MMC Command/Data register */
-#define MMC_RESTO      0x14    /* MMC Response Time-Out register */
-#define MMC_RDTO       0x18    /* MMC Read Time-Out register */
-#define MMC_BLKLEN     0x1c    /* MMC Block Length register */
-#define MMC_NUMBLK     0x20    /* MMC Number of Blocks register */
-#define MMC_PRTBUF     0x24    /* MMC Buffer Partly Full register */
-#define MMC_I_MASK     0x28    /* MMC Interrupt Mask register */
-#define MMC_I_REG      0x2c    /* MMC Interrupt Request register */
-#define MMC_CMD                0x30    /* MMC Command register */
-#define MMC_ARGH       0x34    /* MMC Argument High register */
-#define MMC_ARGL       0x38    /* MMC Argument Low register */
-#define MMC_RES                0x3c    /* MMC Response FIFO */
-#define MMC_RXFIFO     0x40    /* MMC Receive FIFO */
-#define MMC_TXFIFO     0x44    /* MMC Transmit FIFO */
-#define MMC_RDWAIT     0x48    /* MMC RD_WAIT register */
-#define MMC_BLKS_REM   0x4c    /* MMC Blocks Remaining register */
-
-/* Bitfield masks */
-#define STRPCL_STOP_CLK        (1 << 0)
-#define STRPCL_STRT_CLK        (1 << 1)
-#define STAT_TOUT_RES  (1 << 1)
-#define STAT_CLK_EN    (1 << 8)
-#define STAT_DATA_DONE (1 << 11)
-#define STAT_PRG_DONE  (1 << 12)
-#define STAT_END_CMDRES        (1 << 13)
-#define SPI_SPI_MODE   (1 << 0)
-#define CMDAT_RES_TYPE (3 << 0)
-#define CMDAT_DATA_EN  (1 << 2)
-#define CMDAT_WR_RD    (1 << 3)
-#define CMDAT_DMA_EN   (1 << 7)
-#define CMDAT_STOP_TRAN        (1 << 10)
-#define INT_DATA_DONE  (1 << 0)
-#define INT_PRG_DONE   (1 << 1)
-#define INT_END_CMD    (1 << 2)
-#define INT_STOP_CMD   (1 << 3)
-#define INT_CLK_OFF    (1 << 4)
-#define INT_RXFIFO_REQ (1 << 5)
-#define INT_TXFIFO_REQ (1 << 6)
-#define INT_TINT       (1 << 7)
-#define INT_DAT_ERR    (1 << 8)
-#define INT_RES_ERR    (1 << 9)
-#define INT_RD_STALLED (1 << 10)
-#define INT_SDIO_INT   (1 << 11)
-#define INT_SDIO_SACK  (1 << 12)
-#define PRTBUF_PRT_BUF (1 << 0)
-
-/* Route internal interrupt lines to the global IC and DMA */
-static void pxa2xx_mmci_int_update(PXA2xxMMCIState *s)
-{
-    uint32_t mask = s->intmask;
-    if (s->cmdat & CMDAT_DMA_EN) {
-        mask |= INT_RXFIFO_REQ | INT_TXFIFO_REQ;
-
-        qemu_set_irq(s->rx_dma, !!(s->intreq & INT_RXFIFO_REQ));
-        qemu_set_irq(s->tx_dma, !!(s->intreq & INT_TXFIFO_REQ));
-    }
-
-    qemu_set_irq(s->irq, !!(s->intreq & ~mask));
-}
-
-static void pxa2xx_mmci_fifo_update(PXA2xxMMCIState *s)
-{
-    if (!s->active)
-        return;
-
-    if (s->cmdat & CMDAT_WR_RD) {
-        while (s->bytesleft && s->tx_len) {
-            sd_write_data(s->card, s->tx_fifo[s->tx_start ++]);
-            s->tx_start &= 0x1f;
-            s->tx_len --;
-            s->bytesleft --;
-        }
-        if (s->bytesleft)
-            s->intreq |= INT_TXFIFO_REQ;
-    } else
-        while (s->bytesleft && s->rx_len < 32) {
-            s->rx_fifo[(s->rx_start + (s->rx_len ++)) & 0x1f] =
-                sd_read_data(s->card);
-            s->bytesleft --;
-            s->intreq |= INT_RXFIFO_REQ;
-        }
-
-    if (!s->bytesleft) {
-        s->active = 0;
-        s->intreq |= INT_DATA_DONE;
-        s->status |= STAT_DATA_DONE;
-
-        if (s->cmdat & CMDAT_WR_RD) {
-            s->intreq |= INT_PRG_DONE;
-            s->status |= STAT_PRG_DONE;
-        }
-    }
-
-    pxa2xx_mmci_int_update(s);
-}
-
-static void pxa2xx_mmci_wakequeues(PXA2xxMMCIState *s)
-{
-    int rsplen, i;
-    SDRequest request;
-    uint8_t response[16];
-
-    s->active = 1;
-    s->rx_len = 0;
-    s->tx_len = 0;
-    s->cmdreq = 0;
-
-    request.cmd = s->cmd;
-    request.arg = s->arg;
-    request.crc = 0;   /* FIXME */
-
-    rsplen = sd_do_command(s->card, &request, response);
-    s->intreq |= INT_END_CMD;
-
-    memset(s->resp_fifo, 0, sizeof(s->resp_fifo));
-    switch (s->cmdat & CMDAT_RES_TYPE) {
-#define PXAMMCI_RESP(wd, value0, value1)       \
-        s->resp_fifo[(wd) + 0] |= (value0);    \
-        s->resp_fifo[(wd) + 1] |= (value1) << 8;
-    case 0:    /* No response */
-        goto complete;
-
-    case 1:    /* R1, R4, R5 or R6 */
-        if (rsplen < 4)
-            goto timeout;
-        goto complete;
-
-    case 2:    /* R2 */
-        if (rsplen < 16)
-            goto timeout;
-        goto complete;
-
-    case 3:    /* R3 */
-        if (rsplen < 4)
-            goto timeout;
-        goto complete;
-
-    complete:
-        for (i = 0; rsplen > 0; i ++, rsplen -= 2) {
-            PXAMMCI_RESP(i, response[i * 2], response[i * 2 + 1]);
-        }
-        s->status |= STAT_END_CMDRES;
-
-        if (!(s->cmdat & CMDAT_DATA_EN))
-            s->active = 0;
-        else
-            s->bytesleft = s->numblk * s->blklen;
-
-        s->resp_len = 0;
-        break;
-
-    timeout:
-        s->active = 0;
-        s->status |= STAT_TOUT_RES;
-        break;
-    }
-
-    pxa2xx_mmci_fifo_update(s);
-}
-
-static uint32_t pxa2xx_mmci_read(void *opaque, hwaddr offset)
-{
-    PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
-    uint32_t ret;
-
-    switch (offset) {
-    case MMC_STRPCL:
-        return 0;
-    case MMC_STAT:
-        return s->status;
-    case MMC_CLKRT:
-        return s->clkrt;
-    case MMC_SPI:
-        return s->spi;
-    case MMC_CMDAT:
-        return s->cmdat;
-    case MMC_RESTO:
-        return s->resp_tout;
-    case MMC_RDTO:
-        return s->read_tout;
-    case MMC_BLKLEN:
-        return s->blklen;
-    case MMC_NUMBLK:
-        return s->numblk;
-    case MMC_PRTBUF:
-        return 0;
-    case MMC_I_MASK:
-        return s->intmask;
-    case MMC_I_REG:
-        return s->intreq;
-    case MMC_CMD:
-        return s->cmd | 0x40;
-    case MMC_ARGH:
-        return s->arg >> 16;
-    case MMC_ARGL:
-        return s->arg & 0xffff;
-    case MMC_RES:
-        if (s->resp_len < 9)
-            return s->resp_fifo[s->resp_len ++];
-        return 0;
-    case MMC_RXFIFO:
-        ret = 0;
-        while (s->ac_width -- && s->rx_len) {
-            ret |= s->rx_fifo[s->rx_start ++] << (s->ac_width << 3);
-            s->rx_start &= 0x1f;
-            s->rx_len --;
-        }
-        s->intreq &= ~INT_RXFIFO_REQ;
-        pxa2xx_mmci_fifo_update(s);
-        return ret;
-    case MMC_RDWAIT:
-        return 0;
-    case MMC_BLKS_REM:
-        return s->numblk;
-    default:
-        hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset);
-    }
-
-    return 0;
-}
-
-static void pxa2xx_mmci_write(void *opaque,
-                hwaddr offset, uint32_t value)
-{
-    PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
-
-    switch (offset) {
-    case MMC_STRPCL:
-        if (value & STRPCL_STRT_CLK) {
-            s->status |= STAT_CLK_EN;
-            s->intreq &= ~INT_CLK_OFF;
-
-            if (s->cmdreq && !(s->cmdat & CMDAT_STOP_TRAN)) {
-                s->status &= STAT_CLK_EN;
-                pxa2xx_mmci_wakequeues(s);
-            }
-        }
-
-        if (value & STRPCL_STOP_CLK) {
-            s->status &= ~STAT_CLK_EN;
-            s->intreq |= INT_CLK_OFF;
-            s->active = 0;
-        }
-
-        pxa2xx_mmci_int_update(s);
-        break;
-
-    case MMC_CLKRT:
-        s->clkrt = value & 7;
-        break;
-
-    case MMC_SPI:
-        s->spi = value & 0xf;
-        if (value & SPI_SPI_MODE)
-            printf("%s: attempted to use card in SPI mode\n", __FUNCTION__);
-        break;
-
-    case MMC_CMDAT:
-        s->cmdat = value & 0x3dff;
-        s->active = 0;
-        s->cmdreq = 1;
-        if (!(value & CMDAT_STOP_TRAN)) {
-            s->status &= STAT_CLK_EN;
-
-            if (s->status & STAT_CLK_EN)
-                pxa2xx_mmci_wakequeues(s);
-        }
-
-        pxa2xx_mmci_int_update(s);
-        break;
-
-    case MMC_RESTO:
-        s->resp_tout = value & 0x7f;
-        break;
-
-    case MMC_RDTO:
-        s->read_tout = value & 0xffff;
-        break;
-
-    case MMC_BLKLEN:
-        s->blklen = value & 0xfff;
-        break;
-
-    case MMC_NUMBLK:
-        s->numblk = value & 0xffff;
-        break;
-
-    case MMC_PRTBUF:
-        if (value & PRTBUF_PRT_BUF) {
-            s->tx_start ^= 32;
-            s->tx_len = 0;
-        }
-        pxa2xx_mmci_fifo_update(s);
-        break;
-
-    case MMC_I_MASK:
-        s->intmask = value & 0x1fff;
-        pxa2xx_mmci_int_update(s);
-        break;
-
-    case MMC_CMD:
-        s->cmd = value & 0x3f;
-        break;
-
-    case MMC_ARGH:
-        s->arg &= 0x0000ffff;
-        s->arg |= value << 16;
-        break;
-
-    case MMC_ARGL:
-        s->arg &= 0xffff0000;
-        s->arg |= value & 0x0000ffff;
-        break;
-
-    case MMC_TXFIFO:
-        while (s->ac_width -- && s->tx_len < 0x20)
-            s->tx_fifo[(s->tx_start + (s->tx_len ++)) & 0x1f] =
-                    (value >> (s->ac_width << 3)) & 0xff;
-        s->intreq &= ~INT_TXFIFO_REQ;
-        pxa2xx_mmci_fifo_update(s);
-        break;
-
-    case MMC_RDWAIT:
-    case MMC_BLKS_REM:
-        break;
-
-    default:
-        hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset);
-    }
-}
-
-static uint32_t pxa2xx_mmci_readb(void *opaque, hwaddr offset)
-{
-    PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
-    s->ac_width = 1;
-    return pxa2xx_mmci_read(opaque, offset);
-}
-
-static uint32_t pxa2xx_mmci_readh(void *opaque, hwaddr offset)
-{
-    PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
-    s->ac_width = 2;
-    return pxa2xx_mmci_read(opaque, offset);
-}
-
-static uint32_t pxa2xx_mmci_readw(void *opaque, hwaddr offset)
-{
-    PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
-    s->ac_width = 4;
-    return pxa2xx_mmci_read(opaque, offset);
-}
-
-static void pxa2xx_mmci_writeb(void *opaque,
-                hwaddr offset, uint32_t value)
-{
-    PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
-    s->ac_width = 1;
-    pxa2xx_mmci_write(opaque, offset, value);
-}
-
-static void pxa2xx_mmci_writeh(void *opaque,
-                hwaddr offset, uint32_t value)
-{
-    PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
-    s->ac_width = 2;
-    pxa2xx_mmci_write(opaque, offset, value);
-}
-
-static void pxa2xx_mmci_writew(void *opaque,
-                hwaddr offset, uint32_t value)
-{
-    PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
-    s->ac_width = 4;
-    pxa2xx_mmci_write(opaque, offset, value);
-}
-
-static const MemoryRegionOps pxa2xx_mmci_ops = {
-    .old_mmio = {
-        .read = { pxa2xx_mmci_readb,
-                  pxa2xx_mmci_readh,
-                  pxa2xx_mmci_readw, },
-        .write = { pxa2xx_mmci_writeb,
-                   pxa2xx_mmci_writeh,
-                   pxa2xx_mmci_writew, },
-    },
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static void pxa2xx_mmci_save(QEMUFile *f, void *opaque)
-{
-    PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
-    int i;
-
-    qemu_put_be32s(f, &s->status);
-    qemu_put_be32s(f, &s->clkrt);
-    qemu_put_be32s(f, &s->spi);
-    qemu_put_be32s(f, &s->cmdat);
-    qemu_put_be32s(f, &s->resp_tout);
-    qemu_put_be32s(f, &s->read_tout);
-    qemu_put_be32(f, s->blklen);
-    qemu_put_be32(f, s->numblk);
-    qemu_put_be32s(f, &s->intmask);
-    qemu_put_be32s(f, &s->intreq);
-    qemu_put_be32(f, s->cmd);
-    qemu_put_be32s(f, &s->arg);
-    qemu_put_be32(f, s->cmdreq);
-    qemu_put_be32(f, s->active);
-    qemu_put_be32(f, s->bytesleft);
-
-    qemu_put_byte(f, s->tx_len);
-    for (i = 0; i < s->tx_len; i ++)
-        qemu_put_byte(f, s->tx_fifo[(s->tx_start + i) & 63]);
-
-    qemu_put_byte(f, s->rx_len);
-    for (i = 0; i < s->rx_len; i ++)
-        qemu_put_byte(f, s->rx_fifo[(s->rx_start + i) & 31]);
-
-    qemu_put_byte(f, s->resp_len);
-    for (i = s->resp_len; i < 9; i ++)
-        qemu_put_be16s(f, &s->resp_fifo[i]);
-}
-
-static int pxa2xx_mmci_load(QEMUFile *f, void *opaque, int version_id)
-{
-    PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
-    int i;
-
-    qemu_get_be32s(f, &s->status);
-    qemu_get_be32s(f, &s->clkrt);
-    qemu_get_be32s(f, &s->spi);
-    qemu_get_be32s(f, &s->cmdat);
-    qemu_get_be32s(f, &s->resp_tout);
-    qemu_get_be32s(f, &s->read_tout);
-    s->blklen = qemu_get_be32(f);
-    s->numblk = qemu_get_be32(f);
-    qemu_get_be32s(f, &s->intmask);
-    qemu_get_be32s(f, &s->intreq);
-    s->cmd = qemu_get_be32(f);
-    qemu_get_be32s(f, &s->arg);
-    s->cmdreq = qemu_get_be32(f);
-    s->active = qemu_get_be32(f);
-    s->bytesleft = qemu_get_be32(f);
-
-    s->tx_len = qemu_get_byte(f);
-    s->tx_start = 0;
-    if (s->tx_len >= sizeof(s->tx_fifo) || s->tx_len < 0)
-        return -EINVAL;
-    for (i = 0; i < s->tx_len; i ++)
-        s->tx_fifo[i] = qemu_get_byte(f);
-
-    s->rx_len = qemu_get_byte(f);
-    s->rx_start = 0;
-    if (s->rx_len >= sizeof(s->rx_fifo) || s->rx_len < 0)
-        return -EINVAL;
-    for (i = 0; i < s->rx_len; i ++)
-        s->rx_fifo[i] = qemu_get_byte(f);
-
-    s->resp_len = qemu_get_byte(f);
-    if (s->resp_len > 9 || s->resp_len < 0)
-        return -EINVAL;
-    for (i = s->resp_len; i < 9; i ++)
-         qemu_get_be16s(f, &s->resp_fifo[i]);
-
-    return 0;
-}
-
-PXA2xxMMCIState *pxa2xx_mmci_init(MemoryRegion *sysmem,
-                hwaddr base,
-                BlockDriverState *bd, qemu_irq irq,
-                qemu_irq rx_dma, qemu_irq tx_dma)
-{
-    PXA2xxMMCIState *s;
-
-    s = (PXA2xxMMCIState *) g_malloc0(sizeof(PXA2xxMMCIState));
-    s->irq = irq;
-    s->rx_dma = rx_dma;
-    s->tx_dma = tx_dma;
-
-    memory_region_init_io(&s->iomem, &pxa2xx_mmci_ops, s,
-                          "pxa2xx-mmci", 0x00100000);
-    memory_region_add_subregion(sysmem, base, &s->iomem);
-
-    /* Instantiate the actual storage */
-    s->card = sd_init(bd, 0);
-
-    register_savevm(NULL, "pxa2xx_mmci", 0, 0,
-                    pxa2xx_mmci_save, pxa2xx_mmci_load, s);
-
-    return s;
-}
-
-void pxa2xx_mmci_handlers(PXA2xxMMCIState *s, qemu_irq readonly,
-                qemu_irq coverswitch)
-{
-    sd_set_cb(s->card, readonly, coverswitch);
-}
index 8acce025180c75696ce870bfc0c06364dd3192d9..f1aed83d9d05652afa76f624b1fbca4be3dd16b7 100644 (file)
@@ -2,3 +2,7 @@ common-obj-$(CONFIG_PL181) += pl181.o
 common-obj-$(CONFIG_SSI_SD) += ssi-sd.o
 common-obj-$(CONFIG_SD) += sd.o
 common-obj-$(CONFIG_SDHCI) += sdhci.o
+
+obj-$(CONFIG_MILKYMIST) += milkymist-memcard.o
+obj-$(CONFIG_OMAP) += omap_mmc.o
+obj-$(CONFIG_PXA2XX) += pxa2xx_mmci.o
diff --git a/hw/sd/milkymist-memcard.c b/hw/sd/milkymist-memcard.c
new file mode 100644 (file)
index 0000000..d5944bc
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+ *  QEMU model of the Milkymist SD Card Controller.
+ *
+ *  Copyright (c) 2010 Michael Walle <michael@walle.cc>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Specification available at:
+ *   http://www.milkymist.org/socdoc/memcard.pdf
+ */
+
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "sysemu/sysemu.h"
+#include "trace.h"
+#include "qemu/error-report.h"
+#include "sysemu/blockdev.h"
+#include "hw/sd.h"
+
+enum {
+    ENABLE_CMD_TX   = (1<<0),
+    ENABLE_CMD_RX   = (1<<1),
+    ENABLE_DAT_TX   = (1<<2),
+    ENABLE_DAT_RX   = (1<<3),
+};
+
+enum {
+    PENDING_CMD_TX   = (1<<0),
+    PENDING_CMD_RX   = (1<<1),
+    PENDING_DAT_TX   = (1<<2),
+    PENDING_DAT_RX   = (1<<3),
+};
+
+enum {
+    START_CMD_TX    = (1<<0),
+    START_DAT_RX    = (1<<1),
+};
+
+enum {
+    R_CLK2XDIV = 0,
+    R_ENABLE,
+    R_PENDING,
+    R_START,
+    R_CMD,
+    R_DAT,
+    R_MAX
+};
+
+struct MilkymistMemcardState {
+    SysBusDevice busdev;
+    MemoryRegion regs_region;
+    SDState *card;
+
+    int command_write_ptr;
+    int response_read_ptr;
+    int response_len;
+    int ignore_next_cmd;
+    int enabled;
+    uint8_t command[6];
+    uint8_t response[17];
+    uint32_t regs[R_MAX];
+};
+typedef struct MilkymistMemcardState MilkymistMemcardState;
+
+static void update_pending_bits(MilkymistMemcardState *s)
+{
+    /* transmits are instantaneous, thus tx pending bits are never set */
+    s->regs[R_PENDING] = 0;
+    /* if rx is enabled the corresponding pending bits are always set */
+    if (s->regs[R_ENABLE] & ENABLE_CMD_RX) {
+        s->regs[R_PENDING] |= PENDING_CMD_RX;
+    }
+    if (s->regs[R_ENABLE] & ENABLE_DAT_RX) {
+        s->regs[R_PENDING] |= PENDING_DAT_RX;
+    }
+}
+
+static void memcard_sd_command(MilkymistMemcardState *s)
+{
+    SDRequest req;
+
+    req.cmd = s->command[0] & 0x3f;
+    req.arg = (s->command[1] << 24) | (s->command[2] << 16)
+              | (s->command[3] << 8) | s->command[4];
+    req.crc = s->command[5];
+
+    s->response[0] = req.cmd;
+    s->response_len = sd_do_command(s->card, &req, s->response+1);
+    s->response_read_ptr = 0;
+
+    if (s->response_len == 16) {
+        /* R2 response */
+        s->response[0] = 0x3f;
+        s->response_len += 1;
+    } else if (s->response_len == 4) {
+        /* no crc calculation, insert dummy byte */
+        s->response[5] = 0;
+        s->response_len += 2;
+    }
+
+    if (req.cmd == 0) {
+        /* next write is a dummy byte to clock the initialization of the sd
+         * card */
+        s->ignore_next_cmd = 1;
+    }
+}
+
+static uint64_t memcard_read(void *opaque, hwaddr addr,
+                             unsigned size)
+{
+    MilkymistMemcardState *s = opaque;
+    uint32_t r = 0;
+
+    addr >>= 2;
+    switch (addr) {
+    case R_CMD:
+        if (!s->enabled) {
+            r = 0xff;
+        } else {
+            r = s->response[s->response_read_ptr++];
+            if (s->response_read_ptr > s->response_len) {
+                error_report("milkymist_memcard: "
+                        "read more cmd bytes than available. Clipping.");
+                s->response_read_ptr = 0;
+            }
+        }
+        break;
+    case R_DAT:
+        if (!s->enabled) {
+            r = 0xffffffff;
+        } else {
+            r = 0;
+            r |= sd_read_data(s->card) << 24;
+            r |= sd_read_data(s->card) << 16;
+            r |= sd_read_data(s->card) << 8;
+            r |= sd_read_data(s->card);
+        }
+        break;
+    case R_CLK2XDIV:
+    case R_ENABLE:
+    case R_PENDING:
+    case R_START:
+        r = s->regs[addr];
+        break;
+
+    default:
+        error_report("milkymist_memcard: read access to unknown register 0x"
+                TARGET_FMT_plx, addr << 2);
+        break;
+    }
+
+    trace_milkymist_memcard_memory_read(addr << 2, r);
+
+    return r;
+}
+
+static void memcard_write(void *opaque, hwaddr addr, uint64_t value,
+                          unsigned size)
+{
+    MilkymistMemcardState *s = opaque;
+
+    trace_milkymist_memcard_memory_write(addr, value);
+
+    addr >>= 2;
+    switch (addr) {
+    case R_PENDING:
+        /* clear rx pending bits */
+        s->regs[R_PENDING] &= ~(value & (PENDING_CMD_RX | PENDING_DAT_RX));
+        update_pending_bits(s);
+        break;
+    case R_CMD:
+        if (!s->enabled) {
+            break;
+        }
+        if (s->ignore_next_cmd) {
+            s->ignore_next_cmd = 0;
+            break;
+        }
+        s->command[s->command_write_ptr] = value & 0xff;
+        s->command_write_ptr = (s->command_write_ptr + 1) % 6;
+        if (s->command_write_ptr == 0) {
+            memcard_sd_command(s);
+        }
+        break;
+    case R_DAT:
+        if (!s->enabled) {
+            break;
+        }
+        sd_write_data(s->card, (value >> 24) & 0xff);
+        sd_write_data(s->card, (value >> 16) & 0xff);
+        sd_write_data(s->card, (value >> 8) & 0xff);
+        sd_write_data(s->card, value & 0xff);
+        break;
+    case R_ENABLE:
+        s->regs[addr] = value;
+        update_pending_bits(s);
+        break;
+    case R_CLK2XDIV:
+    case R_START:
+        s->regs[addr] = value;
+        break;
+
+    default:
+        error_report("milkymist_memcard: write access to unknown register 0x"
+                TARGET_FMT_plx, addr << 2);
+        break;
+    }
+}
+
+static const MemoryRegionOps memcard_mmio_ops = {
+    .read = memcard_read,
+    .write = memcard_write,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void milkymist_memcard_reset(DeviceState *d)
+{
+    MilkymistMemcardState *s =
+            container_of(d, MilkymistMemcardState, busdev.qdev);
+    int i;
+
+    s->command_write_ptr = 0;
+    s->response_read_ptr = 0;
+    s->response_len = 0;
+
+    for (i = 0; i < R_MAX; i++) {
+        s->regs[i] = 0;
+    }
+}
+
+static int milkymist_memcard_init(SysBusDevice *dev)
+{
+    MilkymistMemcardState *s = FROM_SYSBUS(typeof(*s), dev);
+    DriveInfo *dinfo;
+
+    dinfo = drive_get_next(IF_SD);
+    s->card = sd_init(dinfo ? dinfo->bdrv : NULL, 0);
+    s->enabled = dinfo ? bdrv_is_inserted(dinfo->bdrv) : 0;
+
+    memory_region_init_io(&s->regs_region, &memcard_mmio_ops, s,
+            "milkymist-memcard", R_MAX * 4);
+    sysbus_init_mmio(dev, &s->regs_region);
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_milkymist_memcard = {
+    .name = "milkymist-memcard",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_INT32(command_write_ptr, MilkymistMemcardState),
+        VMSTATE_INT32(response_read_ptr, MilkymistMemcardState),
+        VMSTATE_INT32(response_len, MilkymistMemcardState),
+        VMSTATE_INT32(ignore_next_cmd, MilkymistMemcardState),
+        VMSTATE_INT32(enabled, MilkymistMemcardState),
+        VMSTATE_UINT8_ARRAY(command, MilkymistMemcardState, 6),
+        VMSTATE_UINT8_ARRAY(response, MilkymistMemcardState, 17),
+        VMSTATE_UINT32_ARRAY(regs, MilkymistMemcardState, R_MAX),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void milkymist_memcard_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = milkymist_memcard_init;
+    dc->reset = milkymist_memcard_reset;
+    dc->vmsd = &vmstate_milkymist_memcard;
+}
+
+static const TypeInfo milkymist_memcard_info = {
+    .name          = "milkymist-memcard",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(MilkymistMemcardState),
+    .class_init    = milkymist_memcard_class_init,
+};
+
+static void milkymist_memcard_register_types(void)
+{
+    type_register_static(&milkymist_memcard_info);
+}
+
+type_init(milkymist_memcard_register_types)
diff --git a/hw/sd/omap_mmc.c b/hw/sd/omap_mmc.c
new file mode 100644 (file)
index 0000000..d4079cd
--- /dev/null
@@ -0,0 +1,641 @@
+/*
+ * OMAP on-chip MMC/SD host emulation.
+ *
+ * Copyright (C) 2006-2007 Andrzej Zaborowski  <balrog@zabor.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include "hw/hw.h"
+#include "hw/arm/omap.h"
+#include "hw/sd.h"
+
+struct omap_mmc_s {
+    qemu_irq irq;
+    qemu_irq *dma;
+    qemu_irq coverswitch;
+    MemoryRegion iomem;
+    omap_clk clk;
+    SDState *card;
+    uint16_t last_cmd;
+    uint16_t sdio;
+    uint16_t rsp[8];
+    uint32_t arg;
+    int lines;
+    int dw;
+    int mode;
+    int enable;
+    int be;
+    int rev;
+    uint16_t status;
+    uint16_t mask;
+    uint8_t cto;
+    uint16_t dto;
+    int clkdiv;
+    uint16_t fifo[32];
+    int fifo_start;
+    int fifo_len;
+    uint16_t blen;
+    uint16_t blen_counter;
+    uint16_t nblk;
+    uint16_t nblk_counter;
+    int tx_dma;
+    int rx_dma;
+    int af_level;
+    int ae_level;
+
+    int ddir;
+    int transfer;
+
+    int cdet_wakeup;
+    int cdet_enable;
+    int cdet_state;
+    qemu_irq cdet;
+};
+
+static void omap_mmc_interrupts_update(struct omap_mmc_s *s)
+{
+    qemu_set_irq(s->irq, !!(s->status & s->mask));
+}
+
+static void omap_mmc_fifolevel_update(struct omap_mmc_s *host)
+{
+    if (!host->transfer && !host->fifo_len) {
+        host->status &= 0xf3ff;
+        return;
+    }
+
+    if (host->fifo_len > host->af_level && host->ddir) {
+        if (host->rx_dma) {
+            host->status &= 0xfbff;
+            qemu_irq_raise(host->dma[1]);
+        } else
+            host->status |= 0x0400;
+    } else {
+        host->status &= 0xfbff;
+        qemu_irq_lower(host->dma[1]);
+    }
+
+    if (host->fifo_len < host->ae_level && !host->ddir) {
+        if (host->tx_dma) {
+            host->status &= 0xf7ff;
+            qemu_irq_raise(host->dma[0]);
+        } else
+            host->status |= 0x0800;
+    } else {
+        qemu_irq_lower(host->dma[0]);
+        host->status &= 0xf7ff;
+    }
+}
+
+typedef enum {
+    sd_nore = 0,       /* no response */
+    sd_r1,             /* normal response command */
+    sd_r2,             /* CID, CSD registers */
+    sd_r3,             /* OCR register */
+    sd_r6 = 6,         /* Published RCA response */
+    sd_r1b = -1,
+} sd_rsp_type_t;
+
+static void omap_mmc_command(struct omap_mmc_s *host, int cmd, int dir,
+                sd_cmd_type_t type, int busy, sd_rsp_type_t resptype, int init)
+{
+    uint32_t rspstatus, mask;
+    int rsplen, timeout;
+    SDRequest request;
+    uint8_t response[16];
+
+    if (init && cmd == 0) {
+        host->status |= 0x0001;
+        return;
+    }
+
+    if (resptype == sd_r1 && busy)
+        resptype = sd_r1b;
+
+    if (type == sd_adtc) {
+        host->fifo_start = 0;
+        host->fifo_len = 0;
+        host->transfer = 1;
+        host->ddir = dir;
+    } else
+        host->transfer = 0;
+    timeout = 0;
+    mask = 0;
+    rspstatus = 0;
+
+    request.cmd = cmd;
+    request.arg = host->arg;
+    request.crc = 0; /* FIXME */
+
+    rsplen = sd_do_command(host->card, &request, response);
+
+    /* TODO: validate CRCs */
+    switch (resptype) {
+    case sd_nore:
+        rsplen = 0;
+        break;
+
+    case sd_r1:
+    case sd_r1b:
+        if (rsplen < 4) {
+            timeout = 1;
+            break;
+        }
+        rsplen = 4;
+
+        mask = OUT_OF_RANGE | ADDRESS_ERROR | BLOCK_LEN_ERROR |
+                ERASE_SEQ_ERROR | ERASE_PARAM | WP_VIOLATION |
+                LOCK_UNLOCK_FAILED | COM_CRC_ERROR | ILLEGAL_COMMAND |
+                CARD_ECC_FAILED | CC_ERROR | SD_ERROR |
+                CID_CSD_OVERWRITE;
+        if (host->sdio & (1 << 13))
+            mask |= AKE_SEQ_ERROR;
+        rspstatus = (response[0] << 24) | (response[1] << 16) |
+                (response[2] << 8) | (response[3] << 0);
+        break;
+
+    case sd_r2:
+        if (rsplen < 16) {
+            timeout = 1;
+            break;
+        }
+        rsplen = 16;
+        break;
+
+    case sd_r3:
+        if (rsplen < 4) {
+            timeout = 1;
+            break;
+        }
+        rsplen = 4;
+
+        rspstatus = (response[0] << 24) | (response[1] << 16) |
+                (response[2] << 8) | (response[3] << 0);
+        if (rspstatus & 0x80000000)
+            host->status &= 0xe000;
+        else
+            host->status |= 0x1000;
+        break;
+
+    case sd_r6:
+        if (rsplen < 4) {
+            timeout = 1;
+            break;
+        }
+        rsplen = 4;
+
+        mask = 0xe000 | AKE_SEQ_ERROR;
+        rspstatus = (response[2] << 8) | (response[3] << 0);
+    }
+
+    if (rspstatus & mask)
+        host->status |= 0x4000;
+    else
+        host->status &= 0xb000;
+
+    if (rsplen)
+        for (rsplen = 0; rsplen < 8; rsplen ++)
+            host->rsp[~rsplen & 7] = response[(rsplen << 1) | 1] |
+                    (response[(rsplen << 1) | 0] << 8);
+
+    if (timeout)
+        host->status |= 0x0080;
+    else if (cmd == 12)
+        host->status |= 0x0005;        /* Makes it more real */
+    else
+        host->status |= 0x0001;
+}
+
+static void omap_mmc_transfer(struct omap_mmc_s *host)
+{
+    uint8_t value;
+
+    if (!host->transfer)
+        return;
+
+    while (1) {
+        if (host->ddir) {
+            if (host->fifo_len > host->af_level)
+                break;
+
+            value = sd_read_data(host->card);
+            host->fifo[(host->fifo_start + host->fifo_len) & 31] = value;
+            if (-- host->blen_counter) {
+                value = sd_read_data(host->card);
+                host->fifo[(host->fifo_start + host->fifo_len) & 31] |=
+                        value << 8;
+                host->blen_counter --;
+            }
+
+            host->fifo_len ++;
+        } else {
+            if (!host->fifo_len)
+                break;
+
+            value = host->fifo[host->fifo_start] & 0xff;
+            sd_write_data(host->card, value);
+            if (-- host->blen_counter) {
+                value = host->fifo[host->fifo_start] >> 8;
+                sd_write_data(host->card, value);
+                host->blen_counter --;
+            }
+
+            host->fifo_start ++;
+            host->fifo_len --;
+            host->fifo_start &= 31;
+        }
+
+        if (host->blen_counter == 0) {
+            host->nblk_counter --;
+            host->blen_counter = host->blen;
+
+            if (host->nblk_counter == 0) {
+                host->nblk_counter = host->nblk;
+                host->transfer = 0;
+                host->status |= 0x0008;
+                break;
+            }
+        }
+    }
+}
+
+static void omap_mmc_update(void *opaque)
+{
+    struct omap_mmc_s *s = opaque;
+    omap_mmc_transfer(s);
+    omap_mmc_fifolevel_update(s);
+    omap_mmc_interrupts_update(s);
+}
+
+void omap_mmc_reset(struct omap_mmc_s *host)
+{
+    host->last_cmd = 0;
+    memset(host->rsp, 0, sizeof(host->rsp));
+    host->arg = 0;
+    host->dw = 0;
+    host->mode = 0;
+    host->enable = 0;
+    host->status = 0;
+    host->mask = 0;
+    host->cto = 0;
+    host->dto = 0;
+    host->fifo_len = 0;
+    host->blen = 0;
+    host->blen_counter = 0;
+    host->nblk = 0;
+    host->nblk_counter = 0;
+    host->tx_dma = 0;
+    host->rx_dma = 0;
+    host->ae_level = 0x00;
+    host->af_level = 0x1f;
+    host->transfer = 0;
+    host->cdet_wakeup = 0;
+    host->cdet_enable = 0;
+    qemu_set_irq(host->coverswitch, host->cdet_state);
+    host->clkdiv = 0;
+}
+
+static uint64_t omap_mmc_read(void *opaque, hwaddr offset,
+                              unsigned size)
+{
+    uint16_t i;
+    struct omap_mmc_s *s = (struct omap_mmc_s *) opaque;
+
+    if (size != 2) {
+        return omap_badwidth_read16(opaque, offset);
+    }
+
+    switch (offset) {
+    case 0x00: /* MMC_CMD */
+        return s->last_cmd;
+
+    case 0x04: /* MMC_ARGL */
+        return s->arg & 0x0000ffff;
+
+    case 0x08: /* MMC_ARGH */
+        return s->arg >> 16;
+
+    case 0x0c: /* MMC_CON */
+        return (s->dw << 15) | (s->mode << 12) | (s->enable << 11) | 
+                (s->be << 10) | s->clkdiv;
+
+    case 0x10: /* MMC_STAT */
+        return s->status;
+
+    case 0x14: /* MMC_IE */
+        return s->mask;
+
+    case 0x18: /* MMC_CTO */
+        return s->cto;
+
+    case 0x1c: /* MMC_DTO */
+        return s->dto;
+
+    case 0x20: /* MMC_DATA */
+        /* TODO: support 8-bit access */
+        i = s->fifo[s->fifo_start];
+        if (s->fifo_len == 0) {
+            printf("MMC: FIFO underrun\n");
+            return i;
+        }
+        s->fifo_start ++;
+        s->fifo_len --;
+        s->fifo_start &= 31;
+        omap_mmc_transfer(s);
+        omap_mmc_fifolevel_update(s);
+        omap_mmc_interrupts_update(s);
+        return i;
+
+    case 0x24: /* MMC_BLEN */
+        return s->blen_counter;
+
+    case 0x28: /* MMC_NBLK */
+        return s->nblk_counter;
+
+    case 0x2c: /* MMC_BUF */
+        return (s->rx_dma << 15) | (s->af_level << 8) |
+            (s->tx_dma << 7) | s->ae_level;
+
+    case 0x30: /* MMC_SPI */
+        return 0x0000;
+    case 0x34: /* MMC_SDIO */
+        return (s->cdet_wakeup << 2) | (s->cdet_enable) | s->sdio;
+    case 0x38: /* MMC_SYST */
+        return 0x0000;
+
+    case 0x3c: /* MMC_REV */
+        return s->rev;
+
+    case 0x40: /* MMC_RSP0 */
+    case 0x44: /* MMC_RSP1 */
+    case 0x48: /* MMC_RSP2 */
+    case 0x4c: /* MMC_RSP3 */
+    case 0x50: /* MMC_RSP4 */
+    case 0x54: /* MMC_RSP5 */
+    case 0x58: /* MMC_RSP6 */
+    case 0x5c: /* MMC_RSP7 */
+        return s->rsp[(offset - 0x40) >> 2];
+
+    /* OMAP2-specific */
+    case 0x60: /* MMC_IOSR */
+    case 0x64: /* MMC_SYSC */
+        return 0;
+    case 0x68: /* MMC_SYSS */
+        return 1;                                              /* RSTD */
+    }
+
+    OMAP_BAD_REG(offset);
+    return 0;
+}
+
+static void omap_mmc_write(void *opaque, hwaddr offset,
+                           uint64_t value, unsigned size)
+{
+    int i;
+    struct omap_mmc_s *s = (struct omap_mmc_s *) opaque;
+
+    if (size != 2) {
+        return omap_badwidth_write16(opaque, offset, value);
+    }
+
+    switch (offset) {
+    case 0x00: /* MMC_CMD */
+        if (!s->enable)
+            break;
+
+        s->last_cmd = value;
+        for (i = 0; i < 8; i ++)
+            s->rsp[i] = 0x0000;
+        omap_mmc_command(s, value & 63, (value >> 15) & 1,
+                (sd_cmd_type_t) ((value >> 12) & 3),
+                (value >> 11) & 1,
+                (sd_rsp_type_t) ((value >> 8) & 7),
+                (value >> 7) & 1);
+        omap_mmc_update(s);
+        break;
+
+    case 0x04: /* MMC_ARGL */
+        s->arg &= 0xffff0000;
+        s->arg |= 0x0000ffff & value;
+        break;
+
+    case 0x08: /* MMC_ARGH */
+        s->arg &= 0x0000ffff;
+        s->arg |= value << 16;
+        break;
+
+    case 0x0c: /* MMC_CON */
+        s->dw = (value >> 15) & 1;
+        s->mode = (value >> 12) & 3;
+        s->enable = (value >> 11) & 1;
+        s->be = (value >> 10) & 1;
+        s->clkdiv = (value >> 0) & (s->rev >= 2 ? 0x3ff : 0xff);
+        if (s->mode != 0)
+            printf("SD mode %i unimplemented!\n", s->mode);
+        if (s->be != 0)
+            printf("SD FIFO byte sex unimplemented!\n");
+        if (s->dw != 0 && s->lines < 4)
+            printf("4-bit SD bus enabled\n");
+        if (!s->enable)
+            omap_mmc_reset(s);
+        break;
+
+    case 0x10: /* MMC_STAT */
+        s->status &= ~value;
+        omap_mmc_interrupts_update(s);
+        break;
+
+    case 0x14: /* MMC_IE */
+        s->mask = value & 0x7fff;
+        omap_mmc_interrupts_update(s);
+        break;
+
+    case 0x18: /* MMC_CTO */
+        s->cto = value & 0xff;
+        if (s->cto > 0xfd && s->rev <= 1)
+            printf("MMC: CTO of 0xff and 0xfe cannot be used!\n");
+        break;
+
+    case 0x1c: /* MMC_DTO */
+        s->dto = value & 0xffff;
+        break;
+
+    case 0x20: /* MMC_DATA */
+        /* TODO: support 8-bit access */
+        if (s->fifo_len == 32)
+            break;
+        s->fifo[(s->fifo_start + s->fifo_len) & 31] = value;
+        s->fifo_len ++;
+        omap_mmc_transfer(s);
+        omap_mmc_fifolevel_update(s);
+        omap_mmc_interrupts_update(s);
+        break;
+
+    case 0x24: /* MMC_BLEN */
+        s->blen = (value & 0x07ff) + 1;
+        s->blen_counter = s->blen;
+        break;
+
+    case 0x28: /* MMC_NBLK */
+        s->nblk = (value & 0x07ff) + 1;
+        s->nblk_counter = s->nblk;
+        s->blen_counter = s->blen;
+        break;
+
+    case 0x2c: /* MMC_BUF */
+        s->rx_dma = (value >> 15) & 1;
+        s->af_level = (value >> 8) & 0x1f;
+        s->tx_dma = (value >> 7) & 1;
+        s->ae_level = value & 0x1f;
+
+        if (s->rx_dma)
+            s->status &= 0xfbff;
+        if (s->tx_dma)
+            s->status &= 0xf7ff;
+        omap_mmc_fifolevel_update(s);
+        omap_mmc_interrupts_update(s);
+        break;
+
+    /* SPI, SDIO and TEST modes unimplemented */
+    case 0x30: /* MMC_SPI (OMAP1 only) */
+        break;
+    case 0x34: /* MMC_SDIO */
+        s->sdio = value & (s->rev >= 2 ? 0xfbf3 : 0x2020);
+        s->cdet_wakeup = (value >> 9) & 1;
+        s->cdet_enable = (value >> 2) & 1;
+        break;
+    case 0x38: /* MMC_SYST */
+        break;
+
+    case 0x3c: /* MMC_REV */
+    case 0x40: /* MMC_RSP0 */
+    case 0x44: /* MMC_RSP1 */
+    case 0x48: /* MMC_RSP2 */
+    case 0x4c: /* MMC_RSP3 */
+    case 0x50: /* MMC_RSP4 */
+    case 0x54: /* MMC_RSP5 */
+    case 0x58: /* MMC_RSP6 */
+    case 0x5c: /* MMC_RSP7 */
+        OMAP_RO_REG(offset);
+        break;
+
+    /* OMAP2-specific */
+    case 0x60: /* MMC_IOSR */
+        if (value & 0xf)
+            printf("MMC: SDIO bits used!\n");
+        break;
+    case 0x64: /* MMC_SYSC */
+        if (value & (1 << 2))                                  /* SRTS */
+            omap_mmc_reset(s);
+        break;
+    case 0x68: /* MMC_SYSS */
+        OMAP_RO_REG(offset);
+        break;
+
+    default:
+        OMAP_BAD_REG(offset);
+    }
+}
+
+static const MemoryRegionOps omap_mmc_ops = {
+    .read = omap_mmc_read,
+    .write = omap_mmc_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void omap_mmc_cover_cb(void *opaque, int line, int level)
+{
+    struct omap_mmc_s *host = (struct omap_mmc_s *) opaque;
+
+    if (!host->cdet_state && level) {
+        host->status |= 0x0002;
+        omap_mmc_interrupts_update(host);
+        if (host->cdet_wakeup) {
+            /* TODO: Assert wake-up */
+        }
+    }
+
+    if (host->cdet_state != level) {
+        qemu_set_irq(host->coverswitch, level);
+        host->cdet_state = level;
+    }
+}
+
+struct omap_mmc_s *omap_mmc_init(hwaddr base,
+                MemoryRegion *sysmem,
+                BlockDriverState *bd,
+                qemu_irq irq, qemu_irq dma[], omap_clk clk)
+{
+    struct omap_mmc_s *s = (struct omap_mmc_s *)
+            g_malloc0(sizeof(struct omap_mmc_s));
+
+    s->irq = irq;
+    s->dma = dma;
+    s->clk = clk;
+    s->lines = 1;      /* TODO: needs to be settable per-board */
+    s->rev = 1;
+
+    omap_mmc_reset(s);
+
+    memory_region_init_io(&s->iomem, &omap_mmc_ops, s, "omap.mmc", 0x800);
+    memory_region_add_subregion(sysmem, base, &s->iomem);
+
+    /* Instantiate the storage */
+    s->card = sd_init(bd, 0);
+
+    return s;
+}
+
+struct omap_mmc_s *omap2_mmc_init(struct omap_target_agent_s *ta,
+                BlockDriverState *bd, qemu_irq irq, qemu_irq dma[],
+                omap_clk fclk, omap_clk iclk)
+{
+    struct omap_mmc_s *s = (struct omap_mmc_s *)
+            g_malloc0(sizeof(struct omap_mmc_s));
+
+    s->irq = irq;
+    s->dma = dma;
+    s->clk = fclk;
+    s->lines = 4;
+    s->rev = 2;
+
+    omap_mmc_reset(s);
+
+    memory_region_init_io(&s->iomem, &omap_mmc_ops, s, "omap.mmc",
+                          omap_l4_region_size(ta, 0));
+    omap_l4_attach(ta, 0, &s->iomem);
+
+    /* Instantiate the storage */
+    s->card = sd_init(bd, 0);
+
+    s->cdet = qemu_allocate_irqs(omap_mmc_cover_cb, s, 1)[0];
+    sd_set_cb(s->card, NULL, s->cdet);
+
+    return s;
+}
+
+void omap_mmc_handlers(struct omap_mmc_s *s, qemu_irq ro, qemu_irq cover)
+{
+    if (s->cdet) {
+        sd_set_cb(s->card, ro, s->cdet);
+        s->coverswitch = cover;
+        qemu_set_irq(cover, s->cdet_state);
+    } else
+        sd_set_cb(s->card, ro, cover);
+}
+
+void omap_mmc_enable(struct omap_mmc_s *s, int enable)
+{
+    sd_enable(s->card, enable);
+}
diff --git a/hw/sd/pxa2xx_mmci.c b/hw/sd/pxa2xx_mmci.c
new file mode 100644 (file)
index 0000000..2db1cab
--- /dev/null
@@ -0,0 +1,553 @@
+/*
+ * Intel XScale PXA255/270 MultiMediaCard/SD/SDIO Controller emulation.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licensed under the GPLv2.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include "hw/hw.h"
+#include "hw/arm/pxa.h"
+#include "hw/sd.h"
+#include "hw/qdev.h"
+
+struct PXA2xxMMCIState {
+    MemoryRegion iomem;
+    qemu_irq irq;
+    qemu_irq rx_dma;
+    qemu_irq tx_dma;
+
+    SDState *card;
+
+    uint32_t status;
+    uint32_t clkrt;
+    uint32_t spi;
+    uint32_t cmdat;
+    uint32_t resp_tout;
+    uint32_t read_tout;
+    int blklen;
+    int numblk;
+    uint32_t intmask;
+    uint32_t intreq;
+    int cmd;
+    uint32_t arg;
+
+    int active;
+    int bytesleft;
+    uint8_t tx_fifo[64];
+    int tx_start;
+    int tx_len;
+    uint8_t rx_fifo[32];
+    int rx_start;
+    int rx_len;
+    uint16_t resp_fifo[9];
+    int resp_len;
+
+    int cmdreq;
+    int ac_width;
+};
+
+#define MMC_STRPCL     0x00    /* MMC Clock Start/Stop register */
+#define MMC_STAT       0x04    /* MMC Status register */
+#define MMC_CLKRT      0x08    /* MMC Clock Rate register */
+#define MMC_SPI                0x0c    /* MMC SPI Mode register */
+#define MMC_CMDAT      0x10    /* MMC Command/Data register */
+#define MMC_RESTO      0x14    /* MMC Response Time-Out register */
+#define MMC_RDTO       0x18    /* MMC Read Time-Out register */
+#define MMC_BLKLEN     0x1c    /* MMC Block Length register */
+#define MMC_NUMBLK     0x20    /* MMC Number of Blocks register */
+#define MMC_PRTBUF     0x24    /* MMC Buffer Partly Full register */
+#define MMC_I_MASK     0x28    /* MMC Interrupt Mask register */
+#define MMC_I_REG      0x2c    /* MMC Interrupt Request register */
+#define MMC_CMD                0x30    /* MMC Command register */
+#define MMC_ARGH       0x34    /* MMC Argument High register */
+#define MMC_ARGL       0x38    /* MMC Argument Low register */
+#define MMC_RES                0x3c    /* MMC Response FIFO */
+#define MMC_RXFIFO     0x40    /* MMC Receive FIFO */
+#define MMC_TXFIFO     0x44    /* MMC Transmit FIFO */
+#define MMC_RDWAIT     0x48    /* MMC RD_WAIT register */
+#define MMC_BLKS_REM   0x4c    /* MMC Blocks Remaining register */
+
+/* Bitfield masks */
+#define STRPCL_STOP_CLK        (1 << 0)
+#define STRPCL_STRT_CLK        (1 << 1)
+#define STAT_TOUT_RES  (1 << 1)
+#define STAT_CLK_EN    (1 << 8)
+#define STAT_DATA_DONE (1 << 11)
+#define STAT_PRG_DONE  (1 << 12)
+#define STAT_END_CMDRES        (1 << 13)
+#define SPI_SPI_MODE   (1 << 0)
+#define CMDAT_RES_TYPE (3 << 0)
+#define CMDAT_DATA_EN  (1 << 2)
+#define CMDAT_WR_RD    (1 << 3)
+#define CMDAT_DMA_EN   (1 << 7)
+#define CMDAT_STOP_TRAN        (1 << 10)
+#define INT_DATA_DONE  (1 << 0)
+#define INT_PRG_DONE   (1 << 1)
+#define INT_END_CMD    (1 << 2)
+#define INT_STOP_CMD   (1 << 3)
+#define INT_CLK_OFF    (1 << 4)
+#define INT_RXFIFO_REQ (1 << 5)
+#define INT_TXFIFO_REQ (1 << 6)
+#define INT_TINT       (1 << 7)
+#define INT_DAT_ERR    (1 << 8)
+#define INT_RES_ERR    (1 << 9)
+#define INT_RD_STALLED (1 << 10)
+#define INT_SDIO_INT   (1 << 11)
+#define INT_SDIO_SACK  (1 << 12)
+#define PRTBUF_PRT_BUF (1 << 0)
+
+/* Route internal interrupt lines to the global IC and DMA */
+static void pxa2xx_mmci_int_update(PXA2xxMMCIState *s)
+{
+    uint32_t mask = s->intmask;
+    if (s->cmdat & CMDAT_DMA_EN) {
+        mask |= INT_RXFIFO_REQ | INT_TXFIFO_REQ;
+
+        qemu_set_irq(s->rx_dma, !!(s->intreq & INT_RXFIFO_REQ));
+        qemu_set_irq(s->tx_dma, !!(s->intreq & INT_TXFIFO_REQ));
+    }
+
+    qemu_set_irq(s->irq, !!(s->intreq & ~mask));
+}
+
+static void pxa2xx_mmci_fifo_update(PXA2xxMMCIState *s)
+{
+    if (!s->active)
+        return;
+
+    if (s->cmdat & CMDAT_WR_RD) {
+        while (s->bytesleft && s->tx_len) {
+            sd_write_data(s->card, s->tx_fifo[s->tx_start ++]);
+            s->tx_start &= 0x1f;
+            s->tx_len --;
+            s->bytesleft --;
+        }
+        if (s->bytesleft)
+            s->intreq |= INT_TXFIFO_REQ;
+    } else
+        while (s->bytesleft && s->rx_len < 32) {
+            s->rx_fifo[(s->rx_start + (s->rx_len ++)) & 0x1f] =
+                sd_read_data(s->card);
+            s->bytesleft --;
+            s->intreq |= INT_RXFIFO_REQ;
+        }
+
+    if (!s->bytesleft) {
+        s->active = 0;
+        s->intreq |= INT_DATA_DONE;
+        s->status |= STAT_DATA_DONE;
+
+        if (s->cmdat & CMDAT_WR_RD) {
+            s->intreq |= INT_PRG_DONE;
+            s->status |= STAT_PRG_DONE;
+        }
+    }
+
+    pxa2xx_mmci_int_update(s);
+}
+
+static void pxa2xx_mmci_wakequeues(PXA2xxMMCIState *s)
+{
+    int rsplen, i;
+    SDRequest request;
+    uint8_t response[16];
+
+    s->active = 1;
+    s->rx_len = 0;
+    s->tx_len = 0;
+    s->cmdreq = 0;
+
+    request.cmd = s->cmd;
+    request.arg = s->arg;
+    request.crc = 0;   /* FIXME */
+
+    rsplen = sd_do_command(s->card, &request, response);
+    s->intreq |= INT_END_CMD;
+
+    memset(s->resp_fifo, 0, sizeof(s->resp_fifo));
+    switch (s->cmdat & CMDAT_RES_TYPE) {
+#define PXAMMCI_RESP(wd, value0, value1)       \
+        s->resp_fifo[(wd) + 0] |= (value0);    \
+        s->resp_fifo[(wd) + 1] |= (value1) << 8;
+    case 0:    /* No response */
+        goto complete;
+
+    case 1:    /* R1, R4, R5 or R6 */
+        if (rsplen < 4)
+            goto timeout;
+        goto complete;
+
+    case 2:    /* R2 */
+        if (rsplen < 16)
+            goto timeout;
+        goto complete;
+
+    case 3:    /* R3 */
+        if (rsplen < 4)
+            goto timeout;
+        goto complete;
+
+    complete:
+        for (i = 0; rsplen > 0; i ++, rsplen -= 2) {
+            PXAMMCI_RESP(i, response[i * 2], response[i * 2 + 1]);
+        }
+        s->status |= STAT_END_CMDRES;
+
+        if (!(s->cmdat & CMDAT_DATA_EN))
+            s->active = 0;
+        else
+            s->bytesleft = s->numblk * s->blklen;
+
+        s->resp_len = 0;
+        break;
+
+    timeout:
+        s->active = 0;
+        s->status |= STAT_TOUT_RES;
+        break;
+    }
+
+    pxa2xx_mmci_fifo_update(s);
+}
+
+static uint32_t pxa2xx_mmci_read(void *opaque, hwaddr offset)
+{
+    PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
+    uint32_t ret;
+
+    switch (offset) {
+    case MMC_STRPCL:
+        return 0;
+    case MMC_STAT:
+        return s->status;
+    case MMC_CLKRT:
+        return s->clkrt;
+    case MMC_SPI:
+        return s->spi;
+    case MMC_CMDAT:
+        return s->cmdat;
+    case MMC_RESTO:
+        return s->resp_tout;
+    case MMC_RDTO:
+        return s->read_tout;
+    case MMC_BLKLEN:
+        return s->blklen;
+    case MMC_NUMBLK:
+        return s->numblk;
+    case MMC_PRTBUF:
+        return 0;
+    case MMC_I_MASK:
+        return s->intmask;
+    case MMC_I_REG:
+        return s->intreq;
+    case MMC_CMD:
+        return s->cmd | 0x40;
+    case MMC_ARGH:
+        return s->arg >> 16;
+    case MMC_ARGL:
+        return s->arg & 0xffff;
+    case MMC_RES:
+        if (s->resp_len < 9)
+            return s->resp_fifo[s->resp_len ++];
+        return 0;
+    case MMC_RXFIFO:
+        ret = 0;
+        while (s->ac_width -- && s->rx_len) {
+            ret |= s->rx_fifo[s->rx_start ++] << (s->ac_width << 3);
+            s->rx_start &= 0x1f;
+            s->rx_len --;
+        }
+        s->intreq &= ~INT_RXFIFO_REQ;
+        pxa2xx_mmci_fifo_update(s);
+        return ret;
+    case MMC_RDWAIT:
+        return 0;
+    case MMC_BLKS_REM:
+        return s->numblk;
+    default:
+        hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset);
+    }
+
+    return 0;
+}
+
+static void pxa2xx_mmci_write(void *opaque,
+                hwaddr offset, uint32_t value)
+{
+    PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
+
+    switch (offset) {
+    case MMC_STRPCL:
+        if (value & STRPCL_STRT_CLK) {
+            s->status |= STAT_CLK_EN;
+            s->intreq &= ~INT_CLK_OFF;
+
+            if (s->cmdreq && !(s->cmdat & CMDAT_STOP_TRAN)) {
+                s->status &= STAT_CLK_EN;
+                pxa2xx_mmci_wakequeues(s);
+            }
+        }
+
+        if (value & STRPCL_STOP_CLK) {
+            s->status &= ~STAT_CLK_EN;
+            s->intreq |= INT_CLK_OFF;
+            s->active = 0;
+        }
+
+        pxa2xx_mmci_int_update(s);
+        break;
+
+    case MMC_CLKRT:
+        s->clkrt = value & 7;
+        break;
+
+    case MMC_SPI:
+        s->spi = value & 0xf;
+        if (value & SPI_SPI_MODE)
+            printf("%s: attempted to use card in SPI mode\n", __FUNCTION__);
+        break;
+
+    case MMC_CMDAT:
+        s->cmdat = value & 0x3dff;
+        s->active = 0;
+        s->cmdreq = 1;
+        if (!(value & CMDAT_STOP_TRAN)) {
+            s->status &= STAT_CLK_EN;
+
+            if (s->status & STAT_CLK_EN)
+                pxa2xx_mmci_wakequeues(s);
+        }
+
+        pxa2xx_mmci_int_update(s);
+        break;
+
+    case MMC_RESTO:
+        s->resp_tout = value & 0x7f;
+        break;
+
+    case MMC_RDTO:
+        s->read_tout = value & 0xffff;
+        break;
+
+    case MMC_BLKLEN:
+        s->blklen = value & 0xfff;
+        break;
+
+    case MMC_NUMBLK:
+        s->numblk = value & 0xffff;
+        break;
+
+    case MMC_PRTBUF:
+        if (value & PRTBUF_PRT_BUF) {
+            s->tx_start ^= 32;
+            s->tx_len = 0;
+        }
+        pxa2xx_mmci_fifo_update(s);
+        break;
+
+    case MMC_I_MASK:
+        s->intmask = value & 0x1fff;
+        pxa2xx_mmci_int_update(s);
+        break;
+
+    case MMC_CMD:
+        s->cmd = value & 0x3f;
+        break;
+
+    case MMC_ARGH:
+        s->arg &= 0x0000ffff;
+        s->arg |= value << 16;
+        break;
+
+    case MMC_ARGL:
+        s->arg &= 0xffff0000;
+        s->arg |= value & 0x0000ffff;
+        break;
+
+    case MMC_TXFIFO:
+        while (s->ac_width -- && s->tx_len < 0x20)
+            s->tx_fifo[(s->tx_start + (s->tx_len ++)) & 0x1f] =
+                    (value >> (s->ac_width << 3)) & 0xff;
+        s->intreq &= ~INT_TXFIFO_REQ;
+        pxa2xx_mmci_fifo_update(s);
+        break;
+
+    case MMC_RDWAIT:
+    case MMC_BLKS_REM:
+        break;
+
+    default:
+        hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset);
+    }
+}
+
+static uint32_t pxa2xx_mmci_readb(void *opaque, hwaddr offset)
+{
+    PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
+    s->ac_width = 1;
+    return pxa2xx_mmci_read(opaque, offset);
+}
+
+static uint32_t pxa2xx_mmci_readh(void *opaque, hwaddr offset)
+{
+    PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
+    s->ac_width = 2;
+    return pxa2xx_mmci_read(opaque, offset);
+}
+
+static uint32_t pxa2xx_mmci_readw(void *opaque, hwaddr offset)
+{
+    PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
+    s->ac_width = 4;
+    return pxa2xx_mmci_read(opaque, offset);
+}
+
+static void pxa2xx_mmci_writeb(void *opaque,
+                hwaddr offset, uint32_t value)
+{
+    PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
+    s->ac_width = 1;
+    pxa2xx_mmci_write(opaque, offset, value);
+}
+
+static void pxa2xx_mmci_writeh(void *opaque,
+                hwaddr offset, uint32_t value)
+{
+    PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
+    s->ac_width = 2;
+    pxa2xx_mmci_write(opaque, offset, value);
+}
+
+static void pxa2xx_mmci_writew(void *opaque,
+                hwaddr offset, uint32_t value)
+{
+    PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
+    s->ac_width = 4;
+    pxa2xx_mmci_write(opaque, offset, value);
+}
+
+static const MemoryRegionOps pxa2xx_mmci_ops = {
+    .old_mmio = {
+        .read = { pxa2xx_mmci_readb,
+                  pxa2xx_mmci_readh,
+                  pxa2xx_mmci_readw, },
+        .write = { pxa2xx_mmci_writeb,
+                   pxa2xx_mmci_writeh,
+                   pxa2xx_mmci_writew, },
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void pxa2xx_mmci_save(QEMUFile *f, void *opaque)
+{
+    PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
+    int i;
+
+    qemu_put_be32s(f, &s->status);
+    qemu_put_be32s(f, &s->clkrt);
+    qemu_put_be32s(f, &s->spi);
+    qemu_put_be32s(f, &s->cmdat);
+    qemu_put_be32s(f, &s->resp_tout);
+    qemu_put_be32s(f, &s->read_tout);
+    qemu_put_be32(f, s->blklen);
+    qemu_put_be32(f, s->numblk);
+    qemu_put_be32s(f, &s->intmask);
+    qemu_put_be32s(f, &s->intreq);
+    qemu_put_be32(f, s->cmd);
+    qemu_put_be32s(f, &s->arg);
+    qemu_put_be32(f, s->cmdreq);
+    qemu_put_be32(f, s->active);
+    qemu_put_be32(f, s->bytesleft);
+
+    qemu_put_byte(f, s->tx_len);
+    for (i = 0; i < s->tx_len; i ++)
+        qemu_put_byte(f, s->tx_fifo[(s->tx_start + i) & 63]);
+
+    qemu_put_byte(f, s->rx_len);
+    for (i = 0; i < s->rx_len; i ++)
+        qemu_put_byte(f, s->rx_fifo[(s->rx_start + i) & 31]);
+
+    qemu_put_byte(f, s->resp_len);
+    for (i = s->resp_len; i < 9; i ++)
+        qemu_put_be16s(f, &s->resp_fifo[i]);
+}
+
+static int pxa2xx_mmci_load(QEMUFile *f, void *opaque, int version_id)
+{
+    PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
+    int i;
+
+    qemu_get_be32s(f, &s->status);
+    qemu_get_be32s(f, &s->clkrt);
+    qemu_get_be32s(f, &s->spi);
+    qemu_get_be32s(f, &s->cmdat);
+    qemu_get_be32s(f, &s->resp_tout);
+    qemu_get_be32s(f, &s->read_tout);
+    s->blklen = qemu_get_be32(f);
+    s->numblk = qemu_get_be32(f);
+    qemu_get_be32s(f, &s->intmask);
+    qemu_get_be32s(f, &s->intreq);
+    s->cmd = qemu_get_be32(f);
+    qemu_get_be32s(f, &s->arg);
+    s->cmdreq = qemu_get_be32(f);
+    s->active = qemu_get_be32(f);
+    s->bytesleft = qemu_get_be32(f);
+
+    s->tx_len = qemu_get_byte(f);
+    s->tx_start = 0;
+    if (s->tx_len >= sizeof(s->tx_fifo) || s->tx_len < 0)
+        return -EINVAL;
+    for (i = 0; i < s->tx_len; i ++)
+        s->tx_fifo[i] = qemu_get_byte(f);
+
+    s->rx_len = qemu_get_byte(f);
+    s->rx_start = 0;
+    if (s->rx_len >= sizeof(s->rx_fifo) || s->rx_len < 0)
+        return -EINVAL;
+    for (i = 0; i < s->rx_len; i ++)
+        s->rx_fifo[i] = qemu_get_byte(f);
+
+    s->resp_len = qemu_get_byte(f);
+    if (s->resp_len > 9 || s->resp_len < 0)
+        return -EINVAL;
+    for (i = s->resp_len; i < 9; i ++)
+         qemu_get_be16s(f, &s->resp_fifo[i]);
+
+    return 0;
+}
+
+PXA2xxMMCIState *pxa2xx_mmci_init(MemoryRegion *sysmem,
+                hwaddr base,
+                BlockDriverState *bd, qemu_irq irq,
+                qemu_irq rx_dma, qemu_irq tx_dma)
+{
+    PXA2xxMMCIState *s;
+
+    s = (PXA2xxMMCIState *) g_malloc0(sizeof(PXA2xxMMCIState));
+    s->irq = irq;
+    s->rx_dma = rx_dma;
+    s->tx_dma = tx_dma;
+
+    memory_region_init_io(&s->iomem, &pxa2xx_mmci_ops, s,
+                          "pxa2xx-mmci", 0x00100000);
+    memory_region_add_subregion(sysmem, base, &s->iomem);
+
+    /* Instantiate the actual storage */
+    s->card = sd_init(bd, 0);
+
+    register_savevm(NULL, "pxa2xx_mmci", 0, 0,
+                    pxa2xx_mmci_save, pxa2xx_mmci_load, s);
+
+    return s;
+}
+
+void pxa2xx_mmci_handlers(PXA2xxMMCIState *s, qemu_irq readonly,
+                qemu_irq coverswitch)
+{
+    sd_set_cb(s->card, readonly, coverswitch);
+}