[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH v3 19/20] arm: add Faraday FTSDC010 MMC/SD controlle
From: |
Kuo-Jung Su |
Subject: |
[Qemu-devel] [PATCH v3 19/20] arm: add Faraday FTSDC010 MMC/SD controller support |
Date: |
Wed, 6 Feb 2013 17:45:23 +0800 |
From: Kuo-Jung Su <address@hidden>
The FTSDC010 is a clone of Arm PrimeCell PL181.
While they share lots of design concept, the implementation
differ slightly.
Signed-off-by: Kuo-Jung Su <address@hidden>
---
hw/arm/Makefile.objs | 1 +
hw/arm/faraday_a360.c | 7 +
hw/arm/faraday_a369.c | 7 +
hw/arm/ftsdc010.c | 385 +++++++++++++++++++++++++++++++++++++++++++++++++
hw/arm/ftsdc010.h | 88 +++++++++++
5 files changed, 488 insertions(+)
create mode 100644 hw/arm/ftsdc010.c
create mode 100644 hw/arm/ftsdc010.h
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 854c30a..508335a 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -51,3 +51,4 @@ obj-y += ftmac110.o
obj-y += ftgmac100.o
obj-y += ftlcdc200.o
obj-y += fttsc010.o
+obj-y += ftsdc010.o
diff --git a/hw/arm/faraday_a360.c b/hw/arm/faraday_a360.c
index d3df30e..9717f84 100644
--- a/hw/arm/faraday_a360.c
+++ b/hw/arm/faraday_a360.c
@@ -135,6 +135,13 @@ a360_device_init(A360State *s)
/* ftlcdc200 */
sysbus_create_simple("ftlcdc200", 0x90600000, pic[27]);
+
+ /* ftsdc010 */
+ ds = sysbus_create_simple("ftsdc010", 0x90700000, pic[5]);
+ ack = qdev_get_gpio_in(ds, 0);
+ req = qdev_get_gpio_in(s->hdma[0], 0);
+ qdev_connect_gpio_out(s->hdma[0], 0, ack);
+ qdev_connect_gpio_out(ds, 0, req);
}
static void
diff --git a/hw/arm/faraday_a369.c b/hw/arm/faraday_a369.c
index fd81e2b..4ad48f5 100644
--- a/hw/arm/faraday_a369.c
+++ b/hw/arm/faraday_a369.c
@@ -196,6 +196,13 @@ a369_device_init(A369State *s)
/* fttsc010 */
sysbus_create_simple("fttsc010", 0x92400000, pic[19]);
+
+ /* ftsdc010 */
+ ds = sysbus_create_simple("ftsdc010", 0x90600000, pic[39]);
+ ack = qdev_get_gpio_in(ds, 0);
+ req = qdev_get_gpio_in(s->hdma[0], 13);
+ qdev_connect_gpio_out(s->hdma[0], 13, ack);
+ qdev_connect_gpio_out(ds, 0, req);
}
static void
diff --git a/hw/arm/ftsdc010.c b/hw/arm/ftsdc010.c
new file mode 100644
index 0000000..361fe56
--- /dev/null
+++ b/hw/arm/ftsdc010.c
@@ -0,0 +1,385 @@
+/*
+ * QEMU model of the FTSDC010 MMC/SD Host Controller
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <address@hidden>
+ *
+ * This file is licensed under GNU GPL v2+.
+ */
+
+#include <hw/sysbus.h>
+#include <hw/sd.h>
+#include <sysemu/sysemu.h>
+#include <sysemu/blockdev.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;
+} Ftsdc010State;
+
+#define FTSDC010(obj) \
+ OBJECT_CHECK(Ftsdc010State, obj, TYPE_FTSDC010)
+
+static void ftsdc010_update_irq(Ftsdc010State *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)
+{
+ Ftsdc010State *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(Ftsdc010State *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(Ftsdc010State *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)
+{
+ Ftsdc010State *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) && sd_data_ready(s->card)) {
+ if (s->datacnt > 0) {
+ ret |= sd_read_data(s->card);
+ --s->datacnt;
+ }
+ if (s->datacnt > 0) {
+ ret |= sd_read_data(s->card) << 8;
+ --s->datacnt;
+ }
+ if (s->datacnt > 0) {
+ ret |= sd_read_data(s->card) << 16;
+ --s->datacnt;
+ }
+ if (s->datacnt > 0) {
+ ret |= sd_read_data(s->card) << 24;
+ --s->datacnt;
+ }
+ 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 size)
+{
+ Ftsdc010State *s = FTSDC010(opaque);
+
+ switch (addr) {
+ case REG_DWR:
+ if ((s->dcr & DCR_WR) && (s->datacnt > 0) && sd_data_ready(s->card)) {
+ if (s->datacnt > 0) {
+ sd_write_data(s->card, ((uint32_t)val >> 0) & 0xff);
+ --s->datacnt;
+ }
+ if (s->datacnt > 0) {
+ sd_write_data(s->card, ((uint32_t)val >> 8) & 0xff);
+ --s->datacnt;
+ }
+ if (s->datacnt > 0) {
+ sd_write_data(s->card, ((uint32_t)val >> 16) & 0xff);
+ --s->datacnt;
+ }
+ if (s->datacnt > 0) {
+ sd_write_data(s->card, ((uint32_t)val >> 24) & 0xff);
+ --s->datacnt;
+ }
+ 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_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 *ds)
+{
+ SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
+ Ftsdc010State *s = FTSDC010(FROM_SYSBUS(Ftsdc010State, busdev));
+
+ 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)
+{
+ Ftsdc010State *s = FTSDC010(FROM_SYSBUS(Ftsdc010State, dev));
+ DriveInfo *dinfo;
+
+ memory_region_init_io(&s->iomem, &ftsdc010_ops, s, TYPE_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 = TYPE_FTSDC010,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(cmd, Ftsdc010State),
+ VMSTATE_UINT32(arg, Ftsdc010State),
+ VMSTATE_UINT32_ARRAY(rsp, Ftsdc010State, 4),
+ VMSTATE_UINT32(rspcmd, Ftsdc010State),
+ VMSTATE_UINT32(dcr, Ftsdc010State),
+ VMSTATE_UINT32(dtr, Ftsdc010State),
+ VMSTATE_UINT32(dlr, Ftsdc010State),
+ VMSTATE_UINT32(datacnt, Ftsdc010State),
+ VMSTATE_UINT32(status, Ftsdc010State),
+ VMSTATE_UINT32(ier, Ftsdc010State),
+ VMSTATE_UINT32(pwr, Ftsdc010State),
+ VMSTATE_UINT32(clk, Ftsdc010State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void ftsdc010_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ k->init = ftsdc010_init;
+ dc->vmsd = &vmstate_ftsdc010;
+ dc->reset = ftsdc010_reset;
+ dc->no_user = 1;
+}
+
+static const TypeInfo ftsdc010_info = {
+ .name = TYPE_FTSDC010,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Ftsdc010State),
+ .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/arm/ftsdc010.h b/hw/arm/ftsdc010.h
new file mode 100644
index 0000000..1def683
--- /dev/null
+++ b/hw/arm/ftsdc010.h
@@ -0,0 +1,88 @@
+/*
+ * QEMU model of the FTSDC010 MMC/SD Host Controller
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <address@hidden>
+ *
+ * This file is licensed under GNU GPL v2+.
+ */
+
+#ifndef HW_ARM_FTSDC010_H
+#define HW_ARM_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
- Re: [Qemu-devel] [PATCH v3 04/20] arm: add Faraday FTDDRII030 support, (continued)
[Qemu-devel] [PATCH v3 11/20] arm: add Faraday FTAPBBRG020 APB DMA support, Kuo-Jung Su, 2013/02/06
[Qemu-devel] [PATCH v3 07/20] arm: add Faraday FTTMR010 timer support, Kuo-Jung Su, 2013/02/06
[Qemu-devel] [PATCH v3 08/20] arm: add Faraday FTPWMTMR010 timer support, Kuo-Jung Su, 2013/02/06
[Qemu-devel] [PATCH v3 15/20] arm: add Faraday FTMAC110 10/100Mbps ethernet support, Kuo-Jung Su, 2013/02/06
[Qemu-devel] [PATCH v3 10/20] arm: add Faraday FTDMAC020 AHB DMA support, Kuo-Jung Su, 2013/02/06
[Qemu-devel] [PATCH v3 19/20] arm: add Faraday FTSDC010 MMC/SD controller support,
Kuo-Jung Su <=
[Qemu-devel] [PATCH v3 20/20] arm: add Faraday FTSPI020 SPI flash controller support, Kuo-Jung Su, 2013/02/06
- Re: [Qemu-devel] [PATCH v3 20/20] arm: add Faraday FTSPI020 SPI flash controller support, Peter Crosthwaite, 2013/02/06
- Re: [Qemu-devel] [PATCH v3 20/20] arm: add Faraday FTSPI020 SPI flash controller support, Andreas Färber, 2013/02/07
- Re: [Qemu-devel] [PATCH v3 20/20] arm: add Faraday FTSPI020 SPI flash controller support, Peter Maydell, 2013/02/07
- Re: [Qemu-devel] [PATCH v3 20/20] arm: add Faraday FTSPI020 SPI flash controller support, Andreas Färber, 2013/02/07
- Re: [Qemu-devel] [PATCH v3 20/20] arm: add Faraday FTSPI020 SPI flash controller support, Paolo Bonzini, 2013/02/07
- Re: [Qemu-devel] [PATCH v3 20/20] arm: add Faraday FTSPI020 SPI flash controller support, Peter Maydell, 2013/02/07