From: Kuo-Jung Su
The FTSDC010 functions as the master in an SD memory card interface.
It controls the communication between the AHB/APB bus and the SD card.
Its core supports the SD bus of the SD/SDIO operations and the MMC bus
of the MMC operation as well.
Signed-off-by: Kuo-Jung Su
---
hw/ftsdc010.c | 363 +
hw/ftsdc010.h | 88 ++
2 files changed, 451 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 000..36a6ed3
--- /dev/null
+++ b/hw/ftsdc010.c
@@ -0,0 +1,363 @@
+/*
+ * QEMU model of the FTSDC010 MMC/SD Host Controller
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su
+ *
+ * This file is licensed under GNU GPL v2.
+ */
+
+#include "sysbus.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/blockdev.h"
+#include "sd.h"
+
+#include "ftsdc010.h"
+
+#define TYPE_FTSDC010 "ftsdc010"
+
+typedef struct Ftsdc010State {
+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;
+
+#define FTSDC010(obj) \
+OBJECT_CHECK(ftsdc010_state, obj, TYPE_FTSDC010)
+
+static 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(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 size)
+{
+ftsdc010_state *s = FTSDC010(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:
+retu