qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 10/18] hw: add QEMU model for Faraday MMC/SD host co


From: Dante
Subject: [Qemu-devel] [PATCH 10/18] hw: add QEMU model for Faraday MMC/SD host controller
Date: Fri, 18 Jan 2013 14:31:23 +0800

Signed-off-by: Kuo-Jung Su <address@hidden>
---
 hw/ftsdc010.c |  360 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/ftsdc010.h |  103 +++++++++++++++++
 2 files changed, 463 insertions(+)
 create mode 100644 hw/ftsdc010.c
 create mode 100644 hw/ftsdc010.h

diff --git a/hw/ftsdc010.c b/hw/ftsdc010.c
new file mode 100644
index 0000000..006fcae
--- /dev/null
+++ b/hw/ftsdc010.c
@@ -0,0 +1,360 @@
+/*
+ * QEMU model of the FTSDC010 MMC/SD Host Controller
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Copyright (C) 2012 Dante Su <address@hidden>
+ *
+ * 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.
+ */
+
+#include "sysbus.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/blockdev.h"
+#include "sd.h"
+
+#include "ftsdc010.h"
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    SDState *card;
+    qemu_irq irq;
+    
+    /* DMA hardware handshake */
+    qemu_irq req;
+    
+    uint32_t datacnt;
+    
+    /* HW register cache */
+    uint32_t cmd;
+    uint32_t arg;
+    uint32_t rsp[4];
+    uint32_t rspcmd;
+    uint32_t dcr;
+    uint32_t dtr;
+    uint32_t dlr;
+    uint32_t status;
+    uint32_t ier;
+    uint32_t pwr;
+    uint32_t clk;
+} ftsdc010_state;
+
+static inline void ftsdc010_update_irq(ftsdc010_state *s)
+{
+    if (s->ier & s->status)
+        qemu_set_irq(s->irq, 1);
+    else
+        qemu_set_irq(s->irq, 0);
+}
+
+static void ftsdc010_handle_ack(void *opaque, int line, int level)
+{
+    ftsdc010_state *s = (ftsdc010_state *)opaque;
+    
+    if (!(s->dcr & DCR_DMA))
+        return;
+    
+    if (level) {
+        qemu_set_irq(s->req, 0);
+    } else if (s->datacnt > 0) {
+        qemu_set_irq(s->req, 1);
+    }
+}
+
+static void ftsdc010_send_command(ftsdc010_state *s)
+{
+    SDRequest request;
+    uint8_t response[16];
+    int rlen;
+
+    request.cmd = s->cmd & CMD_IDX;
+    request.arg = s->arg;
+
+    rlen = sd_do_command(s->card, &request, response);
+    if (rlen < 0)
+        goto error;
+    if (s->cmd & CMD_WAIT_RSP) {
+#define RWORD(n) ((response[n] << 24) | (response[n + 1] << 16) \
+                  | (response[n + 2] << 8) | response[n + 3])
+        if (rlen == 0 || (rlen == 4 && (s->cmd & CMD_LONG_RSP)))
+            goto error;
+        if (rlen != 4 && rlen != 16)
+            goto error;
+        if (rlen == 4) {
+            s->rsp[0] = RWORD(0);
+            s->rsp[1] = s->rsp[2] = s->rsp[3] = 0;
+        } else {
+            s->rsp[3] = RWORD(0);
+            s->rsp[2] = RWORD(4);
+            s->rsp[1] = RWORD(8);
+            s->rsp[0] = RWORD(12) & ~1;
+        }
+        s->rspcmd  = (s->cmd & CMD_IDX);
+        s->rspcmd |= (s->cmd & 0x100) ? (1 << 6) : 0;
+        s->status |= STR_RSP;
+#undef RWORD
+    } else {
+        s->status |= STR_CMD;
+    }
+
+    if ((s->dcr & DCR_DMA) && (s->datacnt > 0))
+        qemu_set_irq(s->req, 1);
+
+    return;
+
+error:
+    s->status |= STR_RSP_TIMEOUT;
+}
+
+static void ftsdc010_chip_reset(ftsdc010_state *s)
+{
+    s->cmd = 0;
+    s->arg = 0;
+    s->rsp[0] = 0;
+    s->rsp[1] = 0;
+    s->rsp[2] = 0;
+    s->rsp[3] = 0;
+    s->rspcmd = 0;
+    s->dcr = 0;
+    s->dtr = 0;
+    s->dlr = 0;
+    s->datacnt = 0;
+    s->status &= ~(STR_CARD_REMOVED | STR_WPROT);
+    s->status |= STR_TXRDY | STR_RXRDY;
+    s->ier = 0;
+    s->pwr = 0;
+    s->clk = 0;    
+}
+
+static uint64_t ftsdc010_mem_read(void *opaque, hwaddr addr, unsigned int size)
+{
+    ftsdc010_state *s = opaque;
+    uint32_t ret = 0;
+
+    switch(addr) {
+    case REG_STR:
+        return s->status;
+    case REG_DWR:
+        if (!(s->dcr & DCR_WR) && (s->datacnt > 0)) {
+            ret = sd_read_data(s->card)
+                | sd_read_data(s->card) << 8
+                | sd_read_data(s->card) << 16
+                | sd_read_data(s->card) << 24;
+            s->datacnt -= 4;
+            if (s->datacnt <= 0)
+                s->status |= STR_DAT_END;
+            s->status |= STR_DAT;
+        }
+        break;
+    case REG_DCR:
+        return s->dcr;
+    case REG_DTR:
+        return s->dtr;
+    case REG_DLR:
+        return s->dlr;
+    case REG_CMD:
+        return s->cmd;
+    case REG_ARG:
+        return s->arg;
+    case REG_RSP0:
+        return s->rsp[0];
+    case REG_RSP1:
+        return s->rsp[1];
+    case REG_RSP2:
+        return s->rsp[2];
+    case REG_RSP3:
+        return s->rsp[3];
+    case REG_RSPCMD:
+        return s->rspcmd;
+    case REG_IER:
+        return s->ier;
+    case REG_PWR:
+        return s->pwr;
+    case REG_CLK:
+        return s->clk;
+    case REG_BUS:
+        return 0x00000009;
+    case REG_FEA:
+        return 0x00000010;
+    case REG_REV:
+        return 0x00030300;
+    default:
+        break;
+    }
+
+    return ret;
+}
+
+static void ftsdc010_mem_write(void *opaque, hwaddr addr, uint64_t val, 
unsigned int size)
+{
+    ftsdc010_state *s = opaque;
+    
+    switch(addr) {
+    case REG_DWR:
+        if ((s->dcr & DCR_WR) && (s->datacnt > 0)) {
+            sd_write_data(s->card, ((uint32_t)val >>  0) & 0xff);
+            sd_write_data(s->card, ((uint32_t)val >>  8) & 0xff);
+            sd_write_data(s->card, ((uint32_t)val >> 16) & 0xff);
+            sd_write_data(s->card, ((uint32_t)val >> 24) & 0xff);
+            s->datacnt -= 4;
+            if (s->datacnt <= 0)
+                s->status |= STR_DAT_END;
+            s->status |= STR_DAT;
+        }
+        break;
+    case REG_CMD:
+        s->cmd = (uint32_t)val;
+        if (s->cmd & (1 << 10)) {
+            ftsdc010_chip_reset(s);
+        } else if (s->cmd & (1 << 9)) {
+            s->cmd &= ~(1 << 9);
+            s->status &= ~(STR_CMD | STR_RSP | STR_RSP_ERR);
+            ftsdc010_send_command(s);
+        }
+        break;
+    case REG_ARG:
+        s->arg = (uint32_t)val;
+        break;
+    case REG_DCR:
+        s->dcr = (uint32_t)val;
+        if (s->dcr & DCR_EN) {
+            s->dcr &= ~(DCR_EN);
+            s->status &= ~(STR_DAT | STR_DAT_END | STR_DAT_ERR);
+            s->datacnt = s->dlr;
+        }
+        break;
+    case REG_DTR:
+        s->dtr = (uint32_t)val;
+        break;
+    case REG_DLR:
+        s->dlr = (uint32_t)val;
+        break;
+    case REG_SCR:
+        s->status &= ~((uint32_t)val & 0x14ff);
+        ftsdc010_update_irq(s);
+        break;
+    case REG_IER:
+        s->ier = (uint32_t)val;
+        ftsdc010_update_irq(s);
+        break;
+    case REG_PWR:
+        s->pwr = (uint32_t)val;
+        break;
+    case REG_CLK:
+        s->clk = (uint32_t)val;
+        break;
+    default:
+        break;
+    }
+}
+
+static const MemoryRegionOps ftsdc010_mem_ops = {
+    .read  = ftsdc010_mem_read,
+    .write = ftsdc010_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static void ftsdc010_reset(DeviceState *d)
+{
+    ftsdc010_state *s = DO_UPCAST(ftsdc010_state, busdev.qdev, d);
+    
+    ftsdc010_chip_reset(s);
+
+    sd_set_cb(s->card, NULL, NULL);
+    
+    /* We can assume our GPIO outputs have been wired up now */
+    qemu_set_irq(s->req, 0);
+}
+
+static int ftsdc010_init(SysBusDevice *dev)
+{
+    ftsdc010_state *s = FROM_SYSBUS(typeof(*s), dev);
+    DriveInfo *dinfo;
+
+    memory_region_init_io(&s->iomem, &ftsdc010_mem_ops, s, "ftsdc010", 0x1000);
+    sysbus_init_mmio(dev, &s->iomem);
+    sysbus_init_irq(dev, &s->irq);
+    
+    qdev_init_gpio_in (&s->busdev.qdev, ftsdc010_handle_ack, 1);
+    qdev_init_gpio_out(&s->busdev.qdev, &s->req, 1);
+    
+    dinfo = drive_get_next(IF_SD);
+    s->card = sd_init(dinfo ? dinfo->bdrv : NULL, 0);
+
+    s->status = STR_CARD_REMOVED;
+    if (dinfo) {
+        if (bdrv_is_read_only(dinfo->bdrv))
+            s->status |= STR_WPROT;
+        if (bdrv_is_inserted(dinfo->bdrv))
+            s->status &= ~STR_CARD_REMOVED;
+    }
+    
+    return 0;
+}
+
+static const VMStateDescription vmstate_ftsdc010 = {
+    .name = "ftsdc010",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(cmd, ftsdc010_state),
+        VMSTATE_UINT32(arg, ftsdc010_state),
+        VMSTATE_UINT32_ARRAY(rsp, ftsdc010_state, 4),
+        VMSTATE_UINT32(rspcmd, ftsdc010_state),
+        VMSTATE_UINT32(dcr, ftsdc010_state),
+        VMSTATE_UINT32(dtr, ftsdc010_state),
+        VMSTATE_UINT32(dlr, ftsdc010_state),
+        VMSTATE_UINT32(datacnt, ftsdc010_state),
+        VMSTATE_UINT32(status, ftsdc010_state),
+        VMSTATE_UINT32(ier, ftsdc010_state),
+        VMSTATE_UINT32(pwr, ftsdc010_state),
+        VMSTATE_UINT32(clk, ftsdc010_state),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void ftsdc010_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+    DeviceClass *k = DEVICE_CLASS(klass);
+
+    sdc->init  = ftsdc010_init;
+    k->vmsd    = &vmstate_ftsdc010;
+    k->reset   = ftsdc010_reset;
+    k->no_user = 1;
+}
+
+static TypeInfo ftsdc010_info = {
+    .name           = "ftsdc010",
+    .parent         = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(ftsdc010_state),
+    .class_init     = ftsdc010_class_init,
+};
+
+static void ftsdc010_register_types(void)
+{
+    type_register_static(&ftsdc010_info);
+}
+
+type_init(ftsdc010_register_types)
diff --git a/hw/ftsdc010.h b/hw/ftsdc010.h
new file mode 100644
index 0000000..482e6e2
--- /dev/null
+++ b/hw/ftsdc010.h
@@ -0,0 +1,103 @@
+/*
+ * (C) Copyright 2010
+ * Faraday Technology Inc. <www.faraday-tech.com>
+ * Dante Su <address@hidden>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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 of
+ * the License, or (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef _FTSDC010_H
+#define _FTSDC010_H
+
+/* sd controller register */
+#define REG_CMD                0x0000
+#define REG_ARG                0x0004
+#define REG_RSP0               0x0008    /* response */
+#define REG_RSP1               0x000C
+#define REG_RSP2               0x0010
+#define REG_RSP3               0x0014
+#define REG_RSPCMD             0x0018    /* responsed command */
+#define REG_DCR                0x001C    /* data control */
+#define REG_DTR                0x0020    /* data timeout */
+#define REG_DLR                0x0024    /* data length */
+#define REG_STR                0x0028    /* status register */
+#define REG_SCR                0x002C    /* status clear register */
+#define REG_IER                0x0030    /* interrupt mask/enable register */
+#define REG_PWR                0x0034    /* power control */
+#define REG_CLK                0x0038    /* clock control */
+#define REG_BUS                0x003C    /* bus width */
+#define REG_DWR                0x0040    /* data window */
+#define REG_GPOR               0x0048
+#define REG_FEA                0x009C
+#define REG_REV                0x00A0
+
+/* bit mapping of command register */
+#define CMD_IDX                0x0000003F
+#define CMD_WAIT_RSP           0x00000040
+#define CMD_LONG_RSP           0x00000080
+#define CMD_APP                0x00000100
+#define CMD_EN                 0x00000200
+#define CMD_RST                0x00000400
+
+/* bit mapping of response command register */
+#define RSP_CMDIDX             0x0000003F
+#define RSP_CMDAPP             0x00000040
+
+/* bit mapping of data control register */
+#define DCR_BKSZ               0x0000000F
+#define DCR_WR                 0x00000010
+#define DCR_RD                 0x00000000
+#define DCR_DMA                0x00000020
+#define DCR_EN                 0x00000040
+#define DCR_THRES              0x00000080
+#define DCR_BURST1             0x00000000
+#define DCR_BURST4             0x00000100
+#define DCR_BURST8             0x00000200
+#define DCR_FIFO_RESET         0x00000400
+
+/* bit mapping of status register */
+#define STR_RSP_CRC            0x00000001
+#define STR_DAT_CRC            0x00000002
+#define STR_RSP_TIMEOUT        0x00000004
+#define STR_DAT_TIMEOUT        0x00000008
+#define STR_RSP_ERR            (STR_RSP_CRC | STR_RSP_TIMEOUT)
+#define STR_DAT_ERR            (STR_DAT_CRC | STR_DAT_TIMEOUT)
+#define STR_RSP                0x00000010
+#define STR_DAT                0x00000020
+#define STR_CMD                0x00000040
+#define STR_DAT_END            0x00000080
+#define STR_TXRDY              0x00000100
+#define STR_RXRDY              0x00000200
+#define STR_CARD_CHANGE        0x00000400
+#define STR_CARD_REMOVED       0x00000800
+#define STR_WPROT              0x00001000
+#define STR_SDIO               0x00010000
+#define STR_DAT0               0x00020000
+
+/* bit mapping of clock register */
+#define CLK_HISPD              0x00000200
+#define CLK_OFF                0x00000100
+#define CLK_SD                 0x00000080
+
+/* bit mapping of bus register */
+#define BUS_CARD_DATA3         0x00000020
+#define BUS_4BITS_SUPP         0x00000008
+#define BUS_8BITS_SUPP         0x00000010
+
+#endif
-- 
1.7.9.5


********************* Confidentiality Notice ************************
This electronic message and any attachments may contain
confidential and legally privileged information or
information which is otherwise protected from disclosure.
If you are not the intended recipient,please do not disclose
the contents, either in whole or in part, to anyone,and
immediately delete the message and any attachments from
your computer system and destroy all hard copies.
Thank you for your cooperation.
***********************************************************************




reply via email to

[Prev in Thread] Current Thread [Next in Thread]