aboutsummaryrefslogtreecommitdiff
path: root/hw/sd
diff options
context:
space:
mode:
Diffstat (limited to 'hw/sd')
-rw-r--r--hw/sd/allwinner-sdhost.c26
-rw-r--r--hw/sd/aspeed_sdhci.c110
-rw-r--r--hw/sd/bcm2835_sdhost.c6
-rw-r--r--hw/sd/cadence_sdhci.c4
-rw-r--r--hw/sd/core.c29
-rw-r--r--hw/sd/meson.build3
-rw-r--r--hw/sd/npcm7xx_sdhci.c7
-rw-r--r--hw/sd/omap_mmc.c309
-rw-r--r--hw/sd/pl181.c8
-rw-r--r--hw/sd/pxa2xx_mmci.c594
-rw-r--r--hw/sd/sd.c2135
-rw-r--r--hw/sd/sdhci-internal.h2
-rw-r--r--hw/sd/sdhci-pci.c9
-rw-r--r--hw/sd/sdhci.c204
-rw-r--r--hw/sd/sdmmc-internal.c72
-rw-r--r--hw/sd/sdmmc-internal.h125
-rw-r--r--hw/sd/ssi-sd.c6
-rw-r--r--hw/sd/trace-events15
18 files changed, 1847 insertions, 1817 deletions
diff --git a/hw/sd/allwinner-sdhost.c b/hw/sd/allwinner-sdhost.c
index a1b7230..b31da5c 100644
--- a/hw/sd/allwinner-sdhost.c
+++ b/hw/sd/allwinner-sdhost.c
@@ -22,8 +22,8 @@
#include "qemu/module.h"
#include "qemu/units.h"
#include "qapi/error.h"
-#include "sysemu/blockdev.h"
-#include "sysemu/dma.h"
+#include "system/blockdev.h"
+#include "system/dma.h"
#include "hw/qdev-properties.h"
#include "hw/irq.h"
#include "hw/sd/allwinner-sdhost.h"
@@ -761,7 +761,7 @@ static void allwinner_sdhost_write(void *opaque, hwaddr offset,
static const MemoryRegionOps allwinner_sdhost_ops = {
.read = allwinner_sdhost_read,
.write = allwinner_sdhost_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
+ .endianness = DEVICE_LITTLE_ENDIAN,
.valid = {
.min_access_size = 4,
.max_access_size = 4,
@@ -808,10 +808,9 @@ static const VMStateDescription vmstate_allwinner_sdhost = {
}
};
-static Property allwinner_sdhost_properties[] = {
+static const Property allwinner_sdhost_properties[] = {
DEFINE_PROP_LINK("dma-memory", AwSdHostState, dma_mr,
TYPE_MEMORY_REGION, MemoryRegion *),
- DEFINE_PROP_END_OF_LIST(),
};
static void allwinner_sdhost_init(Object *obj)
@@ -889,24 +888,26 @@ static void allwinner_sdhost_reset(DeviceState *dev)
}
}
-static void allwinner_sdhost_bus_class_init(ObjectClass *klass, void *data)
+static void allwinner_sdhost_bus_class_init(ObjectClass *klass,
+ const void *data)
{
SDBusClass *sbc = SD_BUS_CLASS(klass);
sbc->set_inserted = allwinner_sdhost_set_inserted;
}
-static void allwinner_sdhost_class_init(ObjectClass *klass, void *data)
+static void allwinner_sdhost_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- dc->reset = allwinner_sdhost_reset;
+ device_class_set_legacy_reset(dc, allwinner_sdhost_reset);
dc->vmsd = &vmstate_allwinner_sdhost;
dc->realize = allwinner_sdhost_realize;
device_class_set_props(dc, allwinner_sdhost_properties);
}
-static void allwinner_sdhost_sun4i_class_init(ObjectClass *klass, void *data)
+static void allwinner_sdhost_sun4i_class_init(ObjectClass *klass,
+ const void *data)
{
AwSdHostClass *sc = AW_SDHOST_CLASS(klass);
sc->max_desc_size = 8 * KiB;
@@ -914,7 +915,8 @@ static void allwinner_sdhost_sun4i_class_init(ObjectClass *klass, void *data)
sc->can_calibrate = false;
}
-static void allwinner_sdhost_sun5i_class_init(ObjectClass *klass, void *data)
+static void allwinner_sdhost_sun5i_class_init(ObjectClass *klass,
+ const void *data)
{
AwSdHostClass *sc = AW_SDHOST_CLASS(klass);
sc->max_desc_size = 64 * KiB;
@@ -923,7 +925,7 @@ static void allwinner_sdhost_sun5i_class_init(ObjectClass *klass, void *data)
}
static void allwinner_sdhost_sun50i_a64_class_init(ObjectClass *klass,
- void *data)
+ const void *data)
{
AwSdHostClass *sc = AW_SDHOST_CLASS(klass);
sc->max_desc_size = 64 * KiB;
@@ -932,7 +934,7 @@ static void allwinner_sdhost_sun50i_a64_class_init(ObjectClass *klass,
}
static void allwinner_sdhost_sun50i_a64_emmc_class_init(ObjectClass *klass,
- void *data)
+ const void *data)
{
AwSdHostClass *sc = AW_SDHOST_CLASS(klass);
sc->max_desc_size = 8 * KiB;
diff --git a/hw/sd/aspeed_sdhci.c b/hw/sd/aspeed_sdhci.c
index 3b63926..fc38ad3 100644
--- a/hw/sd/aspeed_sdhci.c
+++ b/hw/sd/aspeed_sdhci.c
@@ -24,8 +24,10 @@
#define ASPEED_SDHCI_DEBOUNCE_RESET 0x00000005
#define ASPEED_SDHCI_BUS 0x08
#define ASPEED_SDHCI_SDIO_140 0x10
+#define ASPEED_SDHCI_SDIO_144 0x14
#define ASPEED_SDHCI_SDIO_148 0x18
#define ASPEED_SDHCI_SDIO_240 0x20
+#define ASPEED_SDHCI_SDIO_244 0x24
#define ASPEED_SDHCI_SDIO_248 0x28
#define ASPEED_SDHCI_WP_POL 0xec
#define ASPEED_SDHCI_CARD_DET 0xf0
@@ -35,21 +37,27 @@
static uint64_t aspeed_sdhci_read(void *opaque, hwaddr addr, unsigned int size)
{
- uint32_t val = 0;
+ uint64_t val = 0;
AspeedSDHCIState *sdhci = opaque;
switch (addr) {
case ASPEED_SDHCI_SDIO_140:
- val = (uint32_t)sdhci->slots[0].capareg;
+ val = extract64(sdhci->slots[0].capareg, 0, 32);
+ break;
+ case ASPEED_SDHCI_SDIO_144:
+ val = extract64(sdhci->slots[0].capareg, 32, 32);
break;
case ASPEED_SDHCI_SDIO_148:
- val = (uint32_t)sdhci->slots[0].maxcurr;
+ val = extract64(sdhci->slots[0].maxcurr, 0, 32);
break;
case ASPEED_SDHCI_SDIO_240:
- val = (uint32_t)sdhci->slots[1].capareg;
+ val = extract64(sdhci->slots[1].capareg, 0, 32);
+ break;
+ case ASPEED_SDHCI_SDIO_244:
+ val = extract64(sdhci->slots[1].capareg, 32, 32);
break;
case ASPEED_SDHCI_SDIO_248:
- val = (uint32_t)sdhci->slots[1].maxcurr;
+ val = extract64(sdhci->slots[1].maxcurr, 0, 32);
break;
default:
if (addr < ASPEED_SDHCI_REG_SIZE) {
@@ -61,9 +69,9 @@ static uint64_t aspeed_sdhci_read(void *opaque, hwaddr addr, unsigned int size)
}
}
- trace_aspeed_sdhci_read(addr, size, (uint64_t) val);
+ trace_aspeed_sdhci_read(addr, size, val);
- return (uint64_t)val;
+ return val;
}
static void aspeed_sdhci_write(void *opaque, hwaddr addr, uint64_t val,
@@ -79,16 +87,28 @@ static void aspeed_sdhci_write(void *opaque, hwaddr addr, uint64_t val,
sdhci->regs[TO_REG(addr)] = (uint32_t)val & ~ASPEED_SDHCI_INFO_RESET;
break;
case ASPEED_SDHCI_SDIO_140:
- sdhci->slots[0].capareg = (uint64_t)(uint32_t)val;
+ sdhci->slots[0].capareg = deposit64(sdhci->slots[0].capareg,
+ 0, 32, val);
+ break;
+ case ASPEED_SDHCI_SDIO_144:
+ sdhci->slots[0].capareg = deposit64(sdhci->slots[0].capareg,
+ 32, 32, val);
break;
case ASPEED_SDHCI_SDIO_148:
- sdhci->slots[0].maxcurr = (uint64_t)(uint32_t)val;
+ sdhci->slots[0].maxcurr = deposit64(sdhci->slots[0].maxcurr,
+ 0, 32, val);
break;
case ASPEED_SDHCI_SDIO_240:
- sdhci->slots[1].capareg = (uint64_t)(uint32_t)val;
+ sdhci->slots[1].capareg = deposit64(sdhci->slots[1].capareg,
+ 0, 32, val);
+ break;
+ case ASPEED_SDHCI_SDIO_244:
+ sdhci->slots[1].capareg = deposit64(sdhci->slots[1].capareg,
+ 32, 32, val);
break;
case ASPEED_SDHCI_SDIO_248:
- sdhci->slots[1].maxcurr = (uint64_t)(uint32_t)val;
+ sdhci->slots[1].maxcurr = deposit64(sdhci->slots[0].maxcurr,
+ 0, 32, val);
break;
default:
if (addr < ASPEED_SDHCI_REG_SIZE) {
@@ -128,6 +148,7 @@ static void aspeed_sdhci_realize(DeviceState *dev, Error **errp)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
AspeedSDHCIState *sdhci = ASPEED_SDHCI(dev);
+ AspeedSDHCIClass *asc = ASPEED_SDHCI_GET_CLASS(sdhci);
/* Create input irqs for the slots */
qdev_init_gpio_in_named_with_opaque(DEVICE(sbd), aspeed_sdhci_set_irq,
@@ -147,7 +168,7 @@ static void aspeed_sdhci_realize(DeviceState *dev, Error **errp)
}
if (!object_property_set_uint(sdhci_slot, "capareg",
- ASPEED_SDHCI_CAPABILITIES, errp)) {
+ asc->capareg, errp)) {
return;
}
@@ -183,27 +204,84 @@ static const VMStateDescription vmstate_aspeed_sdhci = {
},
};
-static Property aspeed_sdhci_properties[] = {
+static const Property aspeed_sdhci_properties[] = {
DEFINE_PROP_UINT8("num-slots", AspeedSDHCIState, num_slots, 0),
- DEFINE_PROP_END_OF_LIST(),
};
-static void aspeed_sdhci_class_init(ObjectClass *classp, void *data)
+static void aspeed_sdhci_class_init(ObjectClass *classp, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(classp);
dc->realize = aspeed_sdhci_realize;
- dc->reset = aspeed_sdhci_reset;
+ device_class_set_legacy_reset(dc, aspeed_sdhci_reset);
dc->vmsd = &vmstate_aspeed_sdhci;
device_class_set_props(dc, aspeed_sdhci_properties);
}
+static void aspeed_2400_sdhci_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ AspeedSDHCIClass *asc = ASPEED_SDHCI_CLASS(klass);
+
+ dc->desc = "ASPEED 2400 SDHCI Controller";
+ asc->capareg = 0x0000000001e80080;
+}
+
+static void aspeed_2500_sdhci_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ AspeedSDHCIClass *asc = ASPEED_SDHCI_CLASS(klass);
+
+ dc->desc = "ASPEED 2500 SDHCI Controller";
+ asc->capareg = 0x0000000001e80080;
+}
+
+static void aspeed_2600_sdhci_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ AspeedSDHCIClass *asc = ASPEED_SDHCI_CLASS(klass);
+
+ dc->desc = "ASPEED 2600 SDHCI Controller";
+ asc->capareg = 0x0000000701f80080;
+}
+
+static void aspeed_2700_sdhci_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ AspeedSDHCIClass *asc = ASPEED_SDHCI_CLASS(klass);
+
+ dc->desc = "ASPEED 2700 SDHCI Controller";
+ asc->capareg = 0x0000000719f80080;
+}
+
static const TypeInfo aspeed_sdhci_types[] = {
{
.name = TYPE_ASPEED_SDHCI,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(AspeedSDHCIState),
.class_init = aspeed_sdhci_class_init,
+ .class_size = sizeof(AspeedSDHCIClass),
+ .abstract = true,
+ },
+ {
+ .name = TYPE_ASPEED_2400_SDHCI,
+ .parent = TYPE_ASPEED_SDHCI,
+ .class_init = aspeed_2400_sdhci_class_init,
+ },
+ {
+ .name = TYPE_ASPEED_2500_SDHCI,
+ .parent = TYPE_ASPEED_SDHCI,
+ .class_init = aspeed_2500_sdhci_class_init,
+ },
+ {
+ .name = TYPE_ASPEED_2600_SDHCI,
+ .parent = TYPE_ASPEED_SDHCI,
+ .class_init = aspeed_2600_sdhci_class_init,
+ },
+ {
+ .name = TYPE_ASPEED_2700_SDHCI,
+ .parent = TYPE_ASPEED_SDHCI,
+ .class_init = aspeed_2700_sdhci_class_init,
},
};
diff --git a/hw/sd/bcm2835_sdhost.c b/hw/sd/bcm2835_sdhost.c
index 11c54dd..29debdf 100644
--- a/hw/sd/bcm2835_sdhost.c
+++ b/hw/sd/bcm2835_sdhost.c
@@ -14,7 +14,7 @@
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "qemu/module.h"
-#include "sysemu/blockdev.h"
+#include "system/blockdev.h"
#include "hw/irq.h"
#include "hw/sd/bcm2835_sdhost.h"
#include "migration/vmstate.h"
@@ -428,11 +428,11 @@ static void bcm2835_sdhost_reset(DeviceState *dev)
s->fifo_len = 0;
}
-static void bcm2835_sdhost_class_init(ObjectClass *klass, void *data)
+static void bcm2835_sdhost_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- dc->reset = bcm2835_sdhost_reset;
+ device_class_set_legacy_reset(dc, bcm2835_sdhost_reset);
dc->vmsd = &vmstate_bcm2835_sdhost;
}
diff --git a/hw/sd/cadence_sdhci.c b/hw/sd/cadence_sdhci.c
index 7c8bc54..d576855 100644
--- a/hw/sd/cadence_sdhci.c
+++ b/hw/sd/cadence_sdhci.c
@@ -165,13 +165,13 @@ static const VMStateDescription vmstate_cadence_sdhci = {
},
};
-static void cadence_sdhci_class_init(ObjectClass *classp, void *data)
+static void cadence_sdhci_class_init(ObjectClass *classp, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(classp);
dc->desc = "Cadence SD/SDIO/eMMC Host Controller (SD4HC)";
dc->realize = cadence_sdhci_realize;
- dc->reset = cadence_sdhci_reset;
+ device_class_set_legacy_reset(dc, cadence_sdhci_reset);
dc->vmsd = &vmstate_cadence_sdhci;
}
diff --git a/hw/sd/core.c b/hw/sd/core.c
index 52d5d90..4b30218 100644
--- a/hw/sd/core.c
+++ b/hw/sd/core.c
@@ -24,6 +24,7 @@
#include "hw/sd/sd.h"
#include "qemu/module.h"
#include "qapi/error.h"
+#include "sdmmc-internal.h"
#include "trace.h"
static inline const char *sdbus_name(SDBus *sdbus)
@@ -39,7 +40,7 @@ static SDState *get_card(SDBus *sdbus)
if (!kid) {
return NULL;
}
- return SD_CARD(kid->child);
+ return SDMMC_COMMON(kid->child);
}
uint8_t sdbus_get_dat_lines(SDBus *sdbus)
@@ -48,7 +49,7 @@ uint8_t sdbus_get_dat_lines(SDBus *sdbus)
uint8_t dat_lines = 0b1111; /* 4 bit bus width */
if (slave) {
- SDCardClass *sc = SD_CARD_GET_CLASS(slave);
+ SDCardClass *sc = SDMMC_COMMON_GET_CLASS(slave);
if (sc->get_dat_lines) {
dat_lines = sc->get_dat_lines(slave);
@@ -65,7 +66,7 @@ bool sdbus_get_cmd_line(SDBus *sdbus)
bool cmd_line = true;
if (slave) {
- SDCardClass *sc = SD_CARD_GET_CLASS(slave);
+ SDCardClass *sc = SDMMC_COMMON_GET_CLASS(slave);
if (sc->get_cmd_line) {
cmd_line = sc->get_cmd_line(slave);
@@ -82,7 +83,7 @@ void sdbus_set_voltage(SDBus *sdbus, uint16_t millivolts)
trace_sdbus_set_voltage(sdbus_name(sdbus), millivolts);
if (card) {
- SDCardClass *sc = SD_CARD_GET_CLASS(card);
+ SDCardClass *sc = SDMMC_COMMON_GET_CLASS(card);
assert(sc->set_voltage);
sc->set_voltage(card, millivolts);
@@ -95,7 +96,7 @@ int sdbus_do_command(SDBus *sdbus, SDRequest *req, uint8_t *response)
trace_sdbus_command(sdbus_name(sdbus), req->cmd, req->arg);
if (card) {
- SDCardClass *sc = SD_CARD_GET_CLASS(card);
+ SDCardClass *sc = SDMMC_COMMON_GET_CLASS(card);
return sc->do_command(card, req, response);
}
@@ -109,7 +110,7 @@ void sdbus_write_byte(SDBus *sdbus, uint8_t value)
trace_sdbus_write(sdbus_name(sdbus), value);
if (card) {
- SDCardClass *sc = SD_CARD_GET_CLASS(card);
+ SDCardClass *sc = SDMMC_COMMON_GET_CLASS(card);
sc->write_byte(card, value);
}
@@ -121,7 +122,7 @@ void sdbus_write_data(SDBus *sdbus, const void *buf, size_t length)
const uint8_t *data = buf;
if (card) {
- SDCardClass *sc = SD_CARD_GET_CLASS(card);
+ SDCardClass *sc = SDMMC_COMMON_GET_CLASS(card);
for (size_t i = 0; i < length; i++) {
trace_sdbus_write(sdbus_name(sdbus), data[i]);
@@ -136,7 +137,7 @@ uint8_t sdbus_read_byte(SDBus *sdbus)
uint8_t value = 0;
if (card) {
- SDCardClass *sc = SD_CARD_GET_CLASS(card);
+ SDCardClass *sc = SDMMC_COMMON_GET_CLASS(card);
value = sc->read_byte(card);
}
@@ -151,7 +152,7 @@ void sdbus_read_data(SDBus *sdbus, void *buf, size_t length)
uint8_t *data = buf;
if (card) {
- SDCardClass *sc = SD_CARD_GET_CLASS(card);
+ SDCardClass *sc = SDMMC_COMMON_GET_CLASS(card);
for (size_t i = 0; i < length; i++) {
data[i] = sc->read_byte(card);
@@ -165,7 +166,7 @@ bool sdbus_receive_ready(SDBus *sdbus)
SDState *card = get_card(sdbus);
if (card) {
- SDCardClass *sc = SD_CARD_GET_CLASS(card);
+ SDCardClass *sc = SDMMC_COMMON_GET_CLASS(card);
return sc->receive_ready(card);
}
@@ -178,7 +179,7 @@ bool sdbus_data_ready(SDBus *sdbus)
SDState *card = get_card(sdbus);
if (card) {
- SDCardClass *sc = SD_CARD_GET_CLASS(card);
+ SDCardClass *sc = SDMMC_COMMON_GET_CLASS(card);
return sc->data_ready(card);
}
@@ -191,7 +192,7 @@ bool sdbus_get_inserted(SDBus *sdbus)
SDState *card = get_card(sdbus);
if (card) {
- SDCardClass *sc = SD_CARD_GET_CLASS(card);
+ SDCardClass *sc = SDMMC_COMMON_GET_CLASS(card);
return sc->get_inserted(card);
}
@@ -204,7 +205,7 @@ bool sdbus_get_readonly(SDBus *sdbus)
SDState *card = get_card(sdbus);
if (card) {
- SDCardClass *sc = SD_CARD_GET_CLASS(card);
+ SDCardClass *sc = SDMMC_COMMON_GET_CLASS(card);
return sc->get_readonly(card);
}
@@ -250,7 +251,7 @@ void sdbus_reparent_card(SDBus *from, SDBus *to)
return;
}
- sc = SD_CARD_GET_CLASS(card);
+ sc = SDMMC_COMMON_GET_CLASS(card);
readonly = sc->get_readonly(card);
sdbus_set_inserted(from, false);
diff --git a/hw/sd/meson.build b/hw/sd/meson.build
index abfac9e..b43d45b 100644
--- a/hw/sd/meson.build
+++ b/hw/sd/meson.build
@@ -1,11 +1,10 @@
system_ss.add(when: 'CONFIG_PL181', if_true: files('pl181.c'))
-system_ss.add(when: 'CONFIG_SD', if_true: files('sd.c', 'core.c', 'sdmmc-internal.c'))
+system_ss.add(when: 'CONFIG_SD', if_true: files('sd.c', 'core.c'))
system_ss.add(when: 'CONFIG_SDHCI', if_true: files('sdhci.c'))
system_ss.add(when: 'CONFIG_SDHCI_PCI', if_true: files('sdhci-pci.c'))
system_ss.add(when: 'CONFIG_SSI_SD', if_true: files('ssi-sd.c'))
system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_mmc.c'))
-system_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_mmci.c'))
system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_sdhost.c'))
system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_sdhci.c'))
system_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-sdhost.c'))
diff --git a/hw/sd/npcm7xx_sdhci.c b/hw/sd/npcm7xx_sdhci.c
index e93dab8..0233d7b 100644
--- a/hw/sd/npcm7xx_sdhci.c
+++ b/hw/sd/npcm7xx_sdhci.c
@@ -16,6 +16,7 @@
#include "qemu/osdep.h"
+#include "hw/sd/sdhci.h"
#include "hw/sd/npcm7xx_sdhci.h"
#include "migration/vmstate.h"
#include "sdhci-internal.h"
@@ -148,13 +149,13 @@ static const VMStateDescription vmstate_npcm7xx_sdhci = {
},
};
-static void npcm7xx_sdhci_class_init(ObjectClass *classp, void *data)
+static void npcm7xx_sdhci_class_init(ObjectClass *classp, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(classp);
dc->desc = "NPCM7xx SD/eMMC Host Controller";
dc->realize = npcm7xx_sdhci_realize;
- dc->reset = npcm7xx_sdhci_reset;
+ device_class_set_legacy_reset(dc, npcm7xx_sdhci_reset);
dc->vmsd = &vmstate_npcm7xx_sdhci;
}
@@ -162,7 +163,7 @@ static void npcm7xx_sdhci_instance_init(Object *obj)
{
NPCM7xxSDHCIState *s = NPCM7XX_SDHCI(obj);
- object_initialize_child(OBJECT(s), "generic-sdhci", &s->sdhci,
+ object_initialize_child(OBJECT(s), TYPE_SYSBUS_SDHCI, &s->sdhci,
TYPE_SYSBUS_SDHCI);
}
diff --git a/hw/sd/omap_mmc.c b/hw/sd/omap_mmc.c
index edd3cf2..b7648d4 100644
--- a/hw/sd/omap_mmc.c
+++ b/hw/sd/omap_mmc.c
@@ -21,17 +21,22 @@
#include "qemu/osdep.h"
#include "qemu/log.h"
+#include "qapi/error.h"
#include "hw/irq.h"
+#include "hw/sysbus.h"
#include "hw/arm/omap.h"
-#include "hw/sd/sdcard_legacy.h"
+#include "hw/sd/sd.h"
+
+typedef struct OMAPMMCState {
+ SysBusDevice parent_obj;
+
+ SDBus sdbus;
-struct omap_mmc_s {
qemu_irq irq;
- qemu_irq *dma;
- qemu_irq coverswitch;
+ qemu_irq dma_tx_gpio;
+ qemu_irq dma_rx_gpio;
MemoryRegion iomem;
omap_clk clk;
- SDState *card;
uint16_t last_cmd;
uint16_t sdio;
uint16_t rsp[8];
@@ -64,16 +69,15 @@ struct omap_mmc_s {
int cdet_wakeup;
int cdet_enable;
- int cdet_state;
qemu_irq cdet;
-};
+} OMAPMMCState;
-static void omap_mmc_interrupts_update(struct omap_mmc_s *s)
+static void omap_mmc_interrupts_update(OMAPMMCState *s)
{
qemu_set_irq(s->irq, !!(s->status & s->mask));
}
-static void omap_mmc_fifolevel_update(struct omap_mmc_s *host)
+static void omap_mmc_fifolevel_update(OMAPMMCState *host)
{
if (!host->transfer && !host->fifo_len) {
host->status &= 0xf3ff;
@@ -83,37 +87,47 @@ static void omap_mmc_fifolevel_update(struct omap_mmc_s *host)
if (host->fifo_len > host->af_level && host->ddir) {
if (host->rx_dma) {
host->status &= 0xfbff;
- qemu_irq_raise(host->dma[1]);
+ qemu_irq_raise(host->dma_rx_gpio);
} else
host->status |= 0x0400;
} else {
host->status &= 0xfbff;
- qemu_irq_lower(host->dma[1]);
+ qemu_irq_lower(host->dma_rx_gpio);
}
if (host->fifo_len < host->ae_level && !host->ddir) {
if (host->tx_dma) {
host->status &= 0xf7ff;
- qemu_irq_raise(host->dma[0]);
+ qemu_irq_raise(host->dma_tx_gpio);
} else
host->status |= 0x0800;
} else {
- qemu_irq_lower(host->dma[0]);
+ qemu_irq_lower(host->dma_tx_gpio);
host->status &= 0xf7ff;
}
}
+/* These must match the encoding of the MMC_CMD Response field */
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_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)
+/* These must match the encoding of the MMC_CMD Type field */
+typedef enum {
+ SD_TYPE_BC = 0, /* broadcast -- no response */
+ SD_TYPE_BCR = 1, /* broadcast with response */
+ SD_TYPE_AC = 2, /* addressed -- no data transfer */
+ SD_TYPE_ADTC = 3, /* addressed with data transfer */
+} MMCCmdType;
+
+static void omap_mmc_command(OMAPMMCState *host, int cmd, int dir,
+ MMCCmdType type, int busy,
+ sd_rsp_type_t resptype, int init)
{
uint32_t rspstatus, mask;
int rsplen, timeout;
@@ -128,7 +142,7 @@ static void omap_mmc_command(struct omap_mmc_s *host, int cmd, int dir,
if (resptype == sd_r1 && busy)
resptype = sd_r1b;
- if (type == sd_adtc) {
+ if (type == SD_TYPE_ADTC) {
host->fifo_start = 0;
host->fifo_len = 0;
host->transfer = 1;
@@ -143,7 +157,7 @@ static void omap_mmc_command(struct omap_mmc_s *host, int cmd, int dir,
request.arg = host->arg;
request.crc = 0; /* FIXME */
- rsplen = sd_do_command(host->card, &request, response);
+ rsplen = sdbus_do_command(&host->sdbus, &request, response);
/* TODO: validate CRCs */
switch (resptype) {
@@ -215,12 +229,12 @@ static void omap_mmc_command(struct omap_mmc_s *host, int cmd, int dir,
if (timeout)
host->status |= 0x0080;
else if (cmd == 12)
- host->status |= 0x0005; /* Makes it more real */
+ host->status |= 0x0005; /* Makes it more real */
else
host->status |= 0x0001;
}
-static void omap_mmc_transfer(struct omap_mmc_s *host)
+static void omap_mmc_transfer(OMAPMMCState *host)
{
uint8_t value;
@@ -232,10 +246,10 @@ static void omap_mmc_transfer(struct omap_mmc_s *host)
if (host->fifo_len > host->af_level)
break;
- value = sd_read_byte(host->card);
+ value = sdbus_read_byte(&host->sdbus);
host->fifo[(host->fifo_start + host->fifo_len) & 31] = value;
if (-- host->blen_counter) {
- value = sd_read_byte(host->card);
+ value = sdbus_read_byte(&host->sdbus);
host->fifo[(host->fifo_start + host->fifo_len) & 31] |=
value << 8;
host->blen_counter --;
@@ -247,10 +261,10 @@ static void omap_mmc_transfer(struct omap_mmc_s *host)
break;
value = host->fifo[host->fifo_start] & 0xff;
- sd_write_byte(host->card, value);
+ sdbus_write_byte(&host->sdbus, value);
if (-- host->blen_counter) {
value = host->fifo[host->fifo_start] >> 8;
- sd_write_byte(host->card, value);
+ sdbus_write_byte(&host->sdbus, value);
host->blen_counter --;
}
@@ -275,19 +289,19 @@ static void omap_mmc_transfer(struct omap_mmc_s *host)
static void omap_mmc_update(void *opaque)
{
- struct omap_mmc_s *s = opaque;
+ OMAPMMCState *s = opaque;
omap_mmc_transfer(s);
omap_mmc_fifolevel_update(s);
omap_mmc_interrupts_update(s);
}
-static void omap_mmc_pseudo_reset(struct omap_mmc_s *host)
+static void omap_mmc_pseudo_reset(OMAPMMCState *host)
{
host->status = 0;
host->fifo_len = 0;
}
-void omap_mmc_reset(struct omap_mmc_s *host)
+static void omap_mmc_reset(OMAPMMCState *host)
{
host->last_cmd = 0;
memset(host->rsp, 0, sizeof(host->rsp));
@@ -309,54 +323,47 @@ void omap_mmc_reset(struct omap_mmc_s *host)
host->transfer = 0;
host->cdet_wakeup = 0;
host->cdet_enable = 0;
- qemu_set_irq(host->coverswitch, host->cdet_state);
host->clkdiv = 0;
omap_mmc_pseudo_reset(host);
-
- /* Since we're still using the legacy SD API the card is not plugged
- * into any bus, and we must reset it manually. When omap_mmc is
- * QOMified this must move into the QOM reset function.
- */
- device_cold_reset(DEVICE(host->card));
}
static uint64_t omap_mmc_read(void *opaque, hwaddr offset, unsigned size)
{
uint16_t i;
- struct omap_mmc_s *s = opaque;
+ OMAPMMCState *s = opaque;
if (size != 2) {
return omap_badwidth_read16(opaque, offset);
}
switch (offset) {
- case 0x00: /* MMC_CMD */
+ case 0x00: /* MMC_CMD */
return s->last_cmd;
- case 0x04: /* MMC_ARGL */
+ case 0x04: /* MMC_ARGL */
return s->arg & 0x0000ffff;
- case 0x08: /* MMC_ARGH */
+ case 0x08: /* MMC_ARGH */
return s->arg >> 16;
- case 0x0c: /* MMC_CON */
+ case 0x0c: /* MMC_CON */
return (s->dw << 15) | (s->mode << 12) | (s->enable << 11) |
(s->be << 10) | s->clkdiv;
- case 0x10: /* MMC_STAT */
+ case 0x10: /* MMC_STAT */
return s->status;
- case 0x14: /* MMC_IE */
+ case 0x14: /* MMC_IE */
return s->mask;
- case 0x18: /* MMC_CTO */
+ case 0x18: /* MMC_CTO */
return s->cto;
- case 0x1c: /* MMC_DTO */
+ case 0x1c: /* MMC_DTO */
return s->dto;
- case 0x20: /* MMC_DATA */
+ case 0x20: /* MMC_DATA */
/* TODO: support 8-bit access */
i = s->fifo[s->fifo_start];
if (s->fifo_len == 0) {
@@ -371,42 +378,42 @@ static uint64_t omap_mmc_read(void *opaque, hwaddr offset, unsigned size)
omap_mmc_interrupts_update(s);
return i;
- case 0x24: /* MMC_BLEN */
+ case 0x24: /* MMC_BLEN */
return s->blen_counter;
- case 0x28: /* MMC_NBLK */
+ case 0x28: /* MMC_NBLK */
return s->nblk_counter;
- case 0x2c: /* MMC_BUF */
+ case 0x2c: /* MMC_BUF */
return (s->rx_dma << 15) | (s->af_level << 8) |
(s->tx_dma << 7) | s->ae_level;
- case 0x30: /* MMC_SPI */
+ case 0x30: /* MMC_SPI */
return 0x0000;
- case 0x34: /* MMC_SDIO */
+ case 0x34: /* MMC_SDIO */
return (s->cdet_wakeup << 2) | (s->cdet_enable) | s->sdio;
- case 0x38: /* MMC_SYST */
+ case 0x38: /* MMC_SYST */
return 0x0000;
- case 0x3c: /* MMC_REV */
+ 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 */
+ 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 */
+ case 0x60: /* MMC_IOSR */
+ case 0x64: /* MMC_SYSC */
return 0;
- case 0x68: /* MMC_SYSS */
- return 1; /* RSTD */
+ case 0x68: /* MMC_SYSS */
+ return 1; /* RSTD */
}
OMAP_BAD_REG(offset);
@@ -417,7 +424,7 @@ static void omap_mmc_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size)
{
int i;
- struct omap_mmc_s *s = opaque;
+ OMAPMMCState *s = opaque;
if (size != 2) {
omap_badwidth_write16(opaque, offset, value);
@@ -425,7 +432,7 @@ static void omap_mmc_write(void *opaque, hwaddr offset,
}
switch (offset) {
- case 0x00: /* MMC_CMD */
+ case 0x00: /* MMC_CMD */
if (!s->enable)
break;
@@ -433,24 +440,24 @@ static void omap_mmc_write(void *opaque, hwaddr offset,
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);
+ (MMCCmdType)((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 */
+ case 0x04: /* MMC_ARGL */
s->arg &= 0xffff0000;
s->arg |= 0x0000ffff & value;
break;
- case 0x08: /* MMC_ARGH */
+ case 0x08: /* MMC_ARGH */
s->arg &= 0x0000ffff;
s->arg |= value << 16;
break;
- case 0x0c: /* MMC_CON */
+ case 0x0c: /* MMC_CON */
s->dw = (value >> 15) & 1;
s->mode = (value >> 12) & 3;
s->enable = (value >> 11) & 1;
@@ -470,27 +477,27 @@ static void omap_mmc_write(void *opaque, hwaddr offset,
omap_mmc_pseudo_reset(s);
break;
- case 0x10: /* MMC_STAT */
+ case 0x10: /* MMC_STAT */
s->status &= ~value;
omap_mmc_interrupts_update(s);
break;
- case 0x14: /* MMC_IE */
+ case 0x14: /* MMC_IE */
s->mask = value & 0x7fff;
omap_mmc_interrupts_update(s);
break;
- case 0x18: /* MMC_CTO */
+ 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 */
+ case 0x1c: /* MMC_DTO */
s->dto = value & 0xffff;
break;
- case 0x20: /* MMC_DATA */
+ case 0x20: /* MMC_DATA */
/* TODO: support 8-bit access */
if (s->fifo_len == 32)
break;
@@ -501,18 +508,18 @@ static void omap_mmc_write(void *opaque, hwaddr offset,
omap_mmc_interrupts_update(s);
break;
- case 0x24: /* MMC_BLEN */
+ case 0x24: /* MMC_BLEN */
s->blen = (value & 0x07ff) + 1;
s->blen_counter = s->blen;
break;
- case 0x28: /* MMC_NBLK */
+ case 0x28: /* MMC_NBLK */
s->nblk = (value & 0x07ff) + 1;
s->nblk_counter = s->nblk;
s->blen_counter = s->blen;
break;
- case 0x2c: /* MMC_BUF */
+ case 0x2c: /* MMC_BUF */
s->rx_dma = (value >> 15) & 1;
s->af_level = (value >> 8) & 0x1f;
s->tx_dma = (value >> 7) & 1;
@@ -527,38 +534,38 @@ static void omap_mmc_write(void *opaque, hwaddr offset,
break;
/* SPI, SDIO and TEST modes unimplemented */
- case 0x30: /* MMC_SPI (OMAP1 only) */
+ case 0x30: /* MMC_SPI (OMAP1 only) */
break;
- case 0x34: /* MMC_SDIO */
+ 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 */
+ 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 */
+ 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 */
+ case 0x60: /* MMC_IOSR */
if (value & 0xf)
printf("MMC: SDIO bits used!\n");
break;
- case 0x64: /* MMC_SYSC */
- if (value & (1 << 2)) /* SRTS */
+ case 0x64: /* MMC_SYSC */
+ if (value & (1 << 2)) /* SRTS */
omap_mmc_reset(s);
break;
- case 0x68: /* MMC_SYSS */
+ case 0x68: /* MMC_SYSS */
OMAP_RO_REG(offset);
break;
@@ -573,92 +580,56 @@ static const MemoryRegionOps omap_mmc_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
-static void omap_mmc_cover_cb(void *opaque, int line, int level)
+void omap_mmc_set_clk(DeviceState *dev, omap_clk clk)
{
- struct omap_mmc_s *host = opaque;
+ OMAPMMCState *s = OMAP_MMC(dev);
- 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;
- }
+ s->clk = clk;
}
-struct omap_mmc_s *omap_mmc_init(hwaddr base,
- MemoryRegion *sysmem,
- BlockBackend *blk,
- qemu_irq irq, qemu_irq dma[], omap_clk clk)
+static void omap_mmc_reset_hold(Object *obj, ResetType type)
{
- struct omap_mmc_s *s = g_new0(struct omap_mmc_s, 1);
-
- s->irq = irq;
- s->dma = dma;
- s->clk = clk;
- s->lines = 1; /* TODO: needs to be settable per-board */
- s->rev = 1;
-
- memory_region_init_io(&s->iomem, NULL, &omap_mmc_ops, s, "omap.mmc", 0x800);
- memory_region_add_subregion(sysmem, base, &s->iomem);
-
- /* Instantiate the storage */
- s->card = sd_init(blk, false);
- if (s->card == NULL) {
- exit(1);
- }
+ OMAPMMCState *s = OMAP_MMC(obj);
omap_mmc_reset(s);
-
- return s;
}
-struct omap_mmc_s *omap2_mmc_init(struct omap_target_agent_s *ta,
- BlockBackend *blk, qemu_irq irq, qemu_irq dma[],
- omap_clk fclk, omap_clk iclk)
+static void omap_mmc_initfn(Object *obj)
{
- struct omap_mmc_s *s = g_new0(struct omap_mmc_s, 1);
-
- s->irq = irq;
- s->dma = dma;
- s->clk = fclk;
- s->lines = 4;
- s->rev = 2;
-
- memory_region_init_io(&s->iomem, NULL, &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(blk, false);
- if (s->card == NULL) {
- exit(1);
- }
+ OMAPMMCState *s = OMAP_MMC(obj);
- s->cdet = qemu_allocate_irq(omap_mmc_cover_cb, s, 0);
- sd_set_cb(s->card, NULL, s->cdet);
+ /* In theory these could be settable per-board */
+ s->lines = 1;
+ s->rev = 1;
- omap_mmc_reset(s);
+ memory_region_init_io(&s->iomem, obj, &omap_mmc_ops, s, "omap.mmc", 0x800);
+ sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
- return s;
+ sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
+ qdev_init_gpio_out_named(DEVICE(obj), &s->dma_tx_gpio, "dma-tx", 1);
+ qdev_init_gpio_out_named(DEVICE(obj), &s->dma_rx_gpio, "dma-rx", 1);
+
+ qbus_init(&s->sdbus, sizeof(s->sdbus), TYPE_SD_BUS, DEVICE(obj), "sd-bus");
}
-void omap_mmc_handlers(struct omap_mmc_s *s, qemu_irq ro, qemu_irq cover)
+static void omap_mmc_class_init(ObjectClass *oc, const void *data)
{
- 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);
+ ResettableClass *rc = RESETTABLE_CLASS(oc);
+
+ rc->phases.hold = omap_mmc_reset_hold;
}
-void omap_mmc_enable(struct omap_mmc_s *s, int enable)
+static const TypeInfo omap_mmc_info = {
+ .name = TYPE_OMAP_MMC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(OMAPMMCState),
+ .instance_init = omap_mmc_initfn,
+ .class_init = omap_mmc_class_init,
+};
+
+static void omap_mmc_register_types(void)
{
- sd_enable(s->card, enable);
+ type_register_static(&omap_mmc_info);
}
+
+type_init(omap_mmc_register_types)
diff --git a/hw/sd/pl181.c b/hw/sd/pl181.c
index e3633c2..b8fc9f8 100644
--- a/hw/sd/pl181.c
+++ b/hw/sd/pl181.c
@@ -8,7 +8,7 @@
*/
#include "qemu/osdep.h"
-#include "sysemu/blockdev.h"
+#include "system/blockdev.h"
#include "hw/sysbus.h"
#include "migration/vmstate.h"
#include "hw/irq.h"
@@ -509,17 +509,17 @@ static void pl181_init(Object *obj)
qbus_init(&s->sdbus, sizeof(s->sdbus), TYPE_PL181_BUS, dev, "sd-bus");
}
-static void pl181_class_init(ObjectClass *klass, void *data)
+static void pl181_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *k = DEVICE_CLASS(klass);
k->vmsd = &vmstate_pl181;
- k->reset = pl181_reset;
+ device_class_set_legacy_reset(k, pl181_reset);
/* Reason: output IRQs should be wired up */
k->user_creatable = false;
}
-static void pl181_bus_class_init(ObjectClass *klass, void *data)
+static void pl181_bus_class_init(ObjectClass *klass, const void *data)
{
SDBusClass *sbc = SD_BUS_CLASS(klass);
diff --git a/hw/sd/pxa2xx_mmci.c b/hw/sd/pxa2xx_mmci.c
deleted file mode 100644
index 8252970..0000000
--- a/hw/sd/pxa2xx_mmci.c
+++ /dev/null
@@ -1,594 +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 "qemu/osdep.h"
-#include "qapi/error.h"
-#include "hw/irq.h"
-#include "hw/sysbus.h"
-#include "migration/vmstate.h"
-#include "hw/arm/pxa.h"
-#include "hw/sd/sd.h"
-#include "hw/qdev-properties.h"
-#include "qemu/log.h"
-#include "qemu/module.h"
-#include "trace.h"
-#include "qom/object.h"
-
-#define TYPE_PXA2XX_MMCI_BUS "pxa2xx-mmci-bus"
-/* This is reusing the SDBus typedef from SD_BUS */
-DECLARE_INSTANCE_CHECKER(SDBus, PXA2XX_MMCI_BUS,
- TYPE_PXA2XX_MMCI_BUS)
-
-struct PXA2xxMMCIState {
- SysBusDevice parent_obj;
-
- MemoryRegion iomem;
- qemu_irq irq;
- qemu_irq rx_dma;
- qemu_irq tx_dma;
- qemu_irq inserted;
- qemu_irq readonly;
-
- BlockBackend *blk;
- SDBus sdbus;
-
- uint32_t status;
- uint32_t clkrt;
- uint32_t spi;
- uint32_t cmdat;
- uint32_t resp_tout;
- uint32_t read_tout;
- int32_t blklen;
- int32_t numblk;
- uint32_t intmask;
- uint32_t intreq;
- int32_t cmd;
- uint32_t arg;
-
- int32_t active;
- int32_t bytesleft;
- uint8_t tx_fifo[64];
- uint32_t tx_start;
- uint32_t tx_len;
- uint8_t rx_fifo[32];
- uint32_t rx_start;
- uint32_t rx_len;
- uint16_t resp_fifo[9];
- uint32_t resp_len;
-
- int32_t cmdreq;
-};
-
-static bool pxa2xx_mmci_vmstate_validate(void *opaque, int version_id)
-{
- PXA2xxMMCIState *s = opaque;
-
- return s->tx_start < ARRAY_SIZE(s->tx_fifo)
- && s->rx_start < ARRAY_SIZE(s->rx_fifo)
- && s->tx_len <= ARRAY_SIZE(s->tx_fifo)
- && s->rx_len <= ARRAY_SIZE(s->rx_fifo)
- && s->resp_len <= ARRAY_SIZE(s->resp_fifo);
-}
-
-
-static const VMStateDescription vmstate_pxa2xx_mmci = {
- .name = "pxa2xx-mmci",
- .version_id = 2,
- .minimum_version_id = 2,
- .fields = (const VMStateField[]) {
- VMSTATE_UINT32(status, PXA2xxMMCIState),
- VMSTATE_UINT32(clkrt, PXA2xxMMCIState),
- VMSTATE_UINT32(spi, PXA2xxMMCIState),
- VMSTATE_UINT32(cmdat, PXA2xxMMCIState),
- VMSTATE_UINT32(resp_tout, PXA2xxMMCIState),
- VMSTATE_UINT32(read_tout, PXA2xxMMCIState),
- VMSTATE_INT32(blklen, PXA2xxMMCIState),
- VMSTATE_INT32(numblk, PXA2xxMMCIState),
- VMSTATE_UINT32(intmask, PXA2xxMMCIState),
- VMSTATE_UINT32(intreq, PXA2xxMMCIState),
- VMSTATE_INT32(cmd, PXA2xxMMCIState),
- VMSTATE_UINT32(arg, PXA2xxMMCIState),
- VMSTATE_INT32(cmdreq, PXA2xxMMCIState),
- VMSTATE_INT32(active, PXA2xxMMCIState),
- VMSTATE_INT32(bytesleft, PXA2xxMMCIState),
- VMSTATE_UINT32(tx_start, PXA2xxMMCIState),
- VMSTATE_UINT32(tx_len, PXA2xxMMCIState),
- VMSTATE_UINT32(rx_start, PXA2xxMMCIState),
- VMSTATE_UINT32(rx_len, PXA2xxMMCIState),
- VMSTATE_UINT32(resp_len, PXA2xxMMCIState),
- VMSTATE_VALIDATE("fifo size incorrect", pxa2xx_mmci_vmstate_validate),
- VMSTATE_UINT8_ARRAY(tx_fifo, PXA2xxMMCIState, 64),
- VMSTATE_UINT8_ARRAY(rx_fifo, PXA2xxMMCIState, 32),
- VMSTATE_UINT16_ARRAY(resp_fifo, PXA2xxMMCIState, 9),
- VMSTATE_END_OF_LIST()
- }
-};
-
-#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) {
- sdbus_write_byte(&s->sdbus, 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] =
- sdbus_read_byte(&s->sdbus);
- 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 = sdbus_do_command(&s->sdbus, &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 uint64_t pxa2xx_mmci_read(void *opaque, hwaddr offset, unsigned size)
-{
- PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
- uint32_t ret = 0;
-
- switch (offset) {
- case MMC_STRPCL:
- break;
- case MMC_STAT:
- ret = s->status;
- break;
- case MMC_CLKRT:
- ret = s->clkrt;
- break;
- case MMC_SPI:
- ret = s->spi;
- break;
- case MMC_CMDAT:
- ret = s->cmdat;
- break;
- case MMC_RESTO:
- ret = s->resp_tout;
- break;
- case MMC_RDTO:
- ret = s->read_tout;
- break;
- case MMC_BLKLEN:
- ret = s->blklen;
- break;
- case MMC_NUMBLK:
- ret = s->numblk;
- break;
- case MMC_PRTBUF:
- break;
- case MMC_I_MASK:
- ret = s->intmask;
- break;
- case MMC_I_REG:
- ret = s->intreq;
- break;
- case MMC_CMD:
- ret = s->cmd | 0x40;
- break;
- case MMC_ARGH:
- ret = s->arg >> 16;
- break;
- case MMC_ARGL:
- ret = s->arg & 0xffff;
- break;
- case MMC_RES:
- ret = (s->resp_len < 9) ? s->resp_fifo[s->resp_len++] : 0;
- break;
- case MMC_RXFIFO:
- while (size-- && s->rx_len) {
- ret |= s->rx_fifo[s->rx_start++] << (size << 3);
- s->rx_start &= 0x1f;
- s->rx_len --;
- }
- s->intreq &= ~INT_RXFIFO_REQ;
- pxa2xx_mmci_fifo_update(s);
- break;
- case MMC_RDWAIT:
- break;
- case MMC_BLKS_REM:
- ret = s->numblk;
- break;
- default:
- qemu_log_mask(LOG_GUEST_ERROR,
- "%s: incorrect register 0x%02" HWADDR_PRIx "\n",
- __func__, offset);
- }
- trace_pxa2xx_mmci_read(size, offset, ret);
-
- return ret;
-}
-
-static void pxa2xx_mmci_write(void *opaque,
- hwaddr offset, uint64_t value, unsigned size)
-{
- PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
-
- trace_pxa2xx_mmci_write(size, offset, value);
- 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) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "%s: attempted to use card in SPI mode\n", __func__);
- }
- 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 (size-- && s->tx_len < 0x20)
- s->tx_fifo[(s->tx_start + (s->tx_len ++)) & 0x1f] =
- (value >> (size << 3)) & 0xff;
- s->intreq &= ~INT_TXFIFO_REQ;
- pxa2xx_mmci_fifo_update(s);
- break;
-
- case MMC_RDWAIT:
- case MMC_BLKS_REM:
- break;
-
- default:
- qemu_log_mask(LOG_GUEST_ERROR,
- "%s: incorrect reg 0x%02" HWADDR_PRIx " "
- "(value 0x%08" PRIx64 ")\n", __func__, offset, value);
- }
-}
-
-static const MemoryRegionOps pxa2xx_mmci_ops = {
- .read = pxa2xx_mmci_read,
- .write = pxa2xx_mmci_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-PXA2xxMMCIState *pxa2xx_mmci_init(MemoryRegion *sysmem,
- hwaddr base,
- qemu_irq irq, qemu_irq rx_dma, qemu_irq tx_dma)
-{
- DeviceState *dev;
-
- dev = sysbus_create_simple(TYPE_PXA2XX_MMCI, base, irq);
- qdev_connect_gpio_out_named(dev, "rx-dma", 0, rx_dma);
- qdev_connect_gpio_out_named(dev, "tx-dma", 0, tx_dma);
-
- return PXA2XX_MMCI(dev);
-}
-
-static void pxa2xx_mmci_set_inserted(DeviceState *dev, bool inserted)
-{
- PXA2xxMMCIState *s = PXA2XX_MMCI(dev);
-
- qemu_set_irq(s->inserted, inserted);
-}
-
-static void pxa2xx_mmci_set_readonly(DeviceState *dev, bool readonly)
-{
- PXA2xxMMCIState *s = PXA2XX_MMCI(dev);
-
- qemu_set_irq(s->readonly, readonly);
-}
-
-void pxa2xx_mmci_handlers(PXA2xxMMCIState *s, qemu_irq readonly,
- qemu_irq coverswitch)
-{
- DeviceState *dev = DEVICE(s);
-
- s->readonly = readonly;
- s->inserted = coverswitch;
-
- pxa2xx_mmci_set_inserted(dev, sdbus_get_inserted(&s->sdbus));
- pxa2xx_mmci_set_readonly(dev, sdbus_get_readonly(&s->sdbus));
-}
-
-static void pxa2xx_mmci_reset(DeviceState *d)
-{
- PXA2xxMMCIState *s = PXA2XX_MMCI(d);
-
- s->status = 0;
- s->clkrt = 0;
- s->spi = 0;
- s->cmdat = 0;
- s->resp_tout = 0;
- s->read_tout = 0;
- s->blklen = 0;
- s->numblk = 0;
- s->intmask = 0;
- s->intreq = 0;
- s->cmd = 0;
- s->arg = 0;
- s->active = 0;
- s->bytesleft = 0;
- s->tx_start = 0;
- s->tx_len = 0;
- s->rx_start = 0;
- s->rx_len = 0;
- s->resp_len = 0;
- s->cmdreq = 0;
- memset(s->tx_fifo, 0, sizeof(s->tx_fifo));
- memset(s->rx_fifo, 0, sizeof(s->rx_fifo));
- memset(s->resp_fifo, 0, sizeof(s->resp_fifo));
-}
-
-static void pxa2xx_mmci_instance_init(Object *obj)
-{
- PXA2xxMMCIState *s = PXA2XX_MMCI(obj);
- SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
- DeviceState *dev = DEVICE(obj);
-
- memory_region_init_io(&s->iomem, obj, &pxa2xx_mmci_ops, s,
- "pxa2xx-mmci", 0x00100000);
- sysbus_init_mmio(sbd, &s->iomem);
- sysbus_init_irq(sbd, &s->irq);
- qdev_init_gpio_out_named(dev, &s->rx_dma, "rx-dma", 1);
- qdev_init_gpio_out_named(dev, &s->tx_dma, "tx-dma", 1);
-
- qbus_init(&s->sdbus, sizeof(s->sdbus),
- TYPE_PXA2XX_MMCI_BUS, DEVICE(obj), "sd-bus");
-}
-
-static void pxa2xx_mmci_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
-
- dc->vmsd = &vmstate_pxa2xx_mmci;
- dc->reset = pxa2xx_mmci_reset;
-}
-
-static void pxa2xx_mmci_bus_class_init(ObjectClass *klass, void *data)
-{
- SDBusClass *sbc = SD_BUS_CLASS(klass);
-
- sbc->set_inserted = pxa2xx_mmci_set_inserted;
- sbc->set_readonly = pxa2xx_mmci_set_readonly;
-}
-
-static const TypeInfo pxa2xx_mmci_types[] = {
- {
- .name = TYPE_PXA2XX_MMCI,
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(PXA2xxMMCIState),
- .instance_init = pxa2xx_mmci_instance_init,
- .class_init = pxa2xx_mmci_class_init,
- },
- {
- .name = TYPE_PXA2XX_MMCI_BUS,
- .parent = TYPE_SD_BUS,
- .instance_size = sizeof(SDBus),
- .class_init = pxa2xx_mmci_bus_class_init,
- },
-};
-
-DEFINE_TYPES(pxa2xx_mmci_types)
diff --git a/hw/sd/sd.c b/hw/sd/sd.c
index 807b5d3..c275fdd 100644
--- a/hw/sd/sd.c
+++ b/hw/sd/sd.c
@@ -2,6 +2,8 @@
* SD Memory Card emulation as defined in the "SD Memory Card Physical
* layer specification, Version 2.00."
*
+ * eMMC emulation defined in "JEDEC Standard No. 84-A43"
+ *
* Copyright (c) 2006 Andrzej Zaborowski <balrog@zabor.org>
* Copyright (c) 2007 CodeSourcery
* Copyright (c) 2018 Philippe Mathieu-Daudé <f4bug@amsat.org>
@@ -35,9 +37,8 @@
#include "qemu/cutils.h"
#include "hw/irq.h"
#include "hw/registerfields.h"
-#include "sysemu/block-backend.h"
+#include "system/block-backend.h"
#include "hw/sd/sd.h"
-#include "hw/sd/sdcard_legacy.h"
#include "migration/vmstate.h"
#include "qapi/error.h"
#include "qemu/bitmap.h"
@@ -46,6 +47,7 @@
#include "qemu/error-report.h"
#include "qemu/timer.h"
#include "qemu/log.h"
+#include "qemu/guest-random.h"
#include "qemu/module.h"
#include "sdmmc-internal.h"
#include "trace.h"
@@ -68,6 +70,14 @@ typedef enum {
sd_illegal = -2,
} sd_rsp_type_t;
+typedef enum {
+ sd_spi,
+ sd_bc, /* broadcast -- no response */
+ sd_bcr, /* broadcast with response */
+ sd_ac, /* addressed -- no data transfer */
+ sd_adtc, /* addressed with data transfer */
+} sd_cmd_type_t;
+
enum SDCardModes {
sd_inactive,
sd_card_identification_mode,
@@ -75,33 +85,40 @@ enum SDCardModes {
};
enum SDCardStates {
- sd_inactive_state = -1,
- sd_idle_state = 0,
- sd_ready_state,
- sd_identification_state,
- sd_standby_state,
- sd_transfer_state,
- sd_sendingdata_state,
- sd_receivingdata_state,
- sd_programming_state,
- sd_disconnect_state,
+ sd_waitirq_state = -2, /* emmc */
+ sd_inactive_state = -1,
+
+ sd_idle_state = 0,
+ sd_ready_state = 1,
+ sd_identification_state = 2,
+ sd_standby_state = 3,
+ sd_transfer_state = 4,
+ sd_sendingdata_state = 5,
+ sd_receivingdata_state = 6,
+ sd_programming_state = 7,
+ sd_disconnect_state = 8,
+ sd_bus_test_state = 9, /* emmc */
+ sd_sleep_state = 10, /* emmc */
+ sd_io_state = 15 /* sd */
};
+#define SDMMC_CMD_MAX 64
+
typedef sd_rsp_type_t (*sd_cmd_handler)(SDState *sd, SDRequest req);
typedef struct SDProto {
const char *name;
- sd_cmd_handler cmd[SDMMC_CMD_MAX];
- sd_cmd_handler acmd[SDMMC_CMD_MAX];
+ struct {
+ const unsigned class;
+ const sd_cmd_type_t type;
+ const char *name;
+ sd_cmd_handler handler;
+ } cmd[SDMMC_CMD_MAX], acmd[SDMMC_CMD_MAX];
} SDProto;
struct SDState {
DeviceState parent_obj;
- /* If true, created by sd_init() for a non-qdevified caller */
- /* TODO purge them with fire */
- bool me_no_qdev_me_kill_mammoth_with_rocks;
-
/* SD Memory Card Registers */
uint32_t ocr;
uint8_t scr[8];
@@ -110,11 +127,22 @@ struct SDState {
uint16_t rca;
uint32_t card_status;
uint8_t sd_status[64];
+ union {
+ uint8_t ext_csd[512];
+ struct {
+ uint8_t ext_csd_rw[192]; /* Modes segment */
+ uint8_t ext_csd_ro[320]; /* Properties segment */
+ };
+ };
/* Static properties */
uint8_t spec_version;
+ uint64_t boot_part_size;
BlockBackend *blk;
+ uint8_t boot_config;
+
+ const SDProto *proto;
/* Runtime changeables */
@@ -133,36 +161,35 @@ struct SDState {
uint32_t pwd_len;
uint8_t function_group[6];
uint8_t current_cmd;
+ const char *last_cmd_name;
/* True if we will handle the next command as an ACMD. Note that this does
* *not* track the APP_CMD status bit!
*/
bool expecting_acmd;
uint32_t blk_written;
+
uint64_t data_start;
uint32_t data_offset;
+ size_t data_size;
uint8_t data[512];
- qemu_irq readonly_cb;
- qemu_irq inserted_cb;
QEMUTimer *ocr_power_timer;
- bool enable;
uint8_t dat_lines;
bool cmd_line;
};
static void sd_realize(DeviceState *dev, Error **errp);
-static const struct SDProto *sd_proto(SDState *sd)
-{
- SDCardClass *sc = SD_CARD_GET_CLASS(sd);
-
- return sc->proto;
-}
-
static const SDProto sd_proto_spi;
+static const SDProto sd_proto_emmc;
static bool sd_is_spi(SDState *sd)
{
- return sd_proto(sd) == &sd_proto_spi;
+ return sd->proto == &sd_proto_spi;
+}
+
+static bool sd_is_emmc(SDState *sd)
+{
+ return sd->proto == &sd_proto_emmc;
}
static const char *sd_version_str(enum SDPhySpecificationVersion version)
@@ -178,6 +205,17 @@ static const char *sd_version_str(enum SDPhySpecificationVersion version)
return sdphy_version[version];
}
+static const char *sd_mode_name(enum SDCardModes mode)
+{
+ static const char *mode_name[] = {
+ [sd_inactive] = "inactive",
+ [sd_card_identification_mode] = "identification",
+ [sd_data_transfer_mode] = "transfer",
+ };
+ assert(mode < ARRAY_SIZE(mode_name));
+ return mode_name[mode];
+}
+
static const char *sd_state_name(enum SDCardStates state)
{
static const char *state_name[] = {
@@ -187,13 +225,19 @@ static const char *sd_state_name(enum SDCardStates state)
[sd_standby_state] = "standby",
[sd_transfer_state] = "transfer",
[sd_sendingdata_state] = "sendingdata",
+ [sd_bus_test_state] = "bus-test",
[sd_receivingdata_state] = "receivingdata",
[sd_programming_state] = "programming",
[sd_disconnect_state] = "disconnect",
+ [sd_sleep_state] = "sleep",
+ [sd_io_state] = "i/o"
};
if (state == sd_inactive_state) {
return "inactive";
}
+ if (state == sd_waitirq_state) {
+ return "wait-irq";
+ }
assert(state < ARRAY_SIZE(state_name));
return state_name[state];
}
@@ -219,14 +263,40 @@ static const char *sd_response_name(sd_rsp_type_t rsp)
return response_name[rsp];
}
+static const char *sd_cmd_name(SDState *sd, uint8_t cmd)
+{
+ static const char *cmd_abbrev[SDMMC_CMD_MAX] = {
+ [18] = "READ_MULTIPLE_BLOCK",
+ [25] = "WRITE_MULTIPLE_BLOCK",
+ };
+ const SDProto *sdp = sd->proto;
+
+ if (sdp->cmd[cmd].handler) {
+ assert(!cmd_abbrev[cmd]);
+ return sdp->cmd[cmd].name;
+ }
+ return cmd_abbrev[cmd] ? cmd_abbrev[cmd] : "UNKNOWN_CMD";
+}
+
+static const char *sd_acmd_name(SDState *sd, uint8_t cmd)
+{
+ const SDProto *sdp = sd->proto;
+
+ if (sdp->acmd[cmd].handler) {
+ return sdp->acmd[cmd].name;
+ }
+
+ return "UNKNOWN_ACMD";
+}
+
static uint8_t sd_get_dat_lines(SDState *sd)
{
- return sd->enable ? sd->dat_lines : 0;
+ return sd->dat_lines;
}
static bool sd_get_cmd_line(SDState *sd)
{
- return sd->enable ? sd->cmd_line : false;
+ return sd->cmd_line;
}
static void sd_set_voltage(SDState *sd, uint16_t millivolts)
@@ -267,27 +337,6 @@ static void sd_set_mode(SDState *sd)
}
}
-static const sd_cmd_type_t sd_cmd_type[SDMMC_CMD_MAX] = {
- sd_bc, sd_none, sd_bcr, sd_bcr, sd_none, sd_none, sd_none, sd_ac,
- sd_bcr, sd_ac, sd_ac, sd_adtc, sd_ac, sd_ac, sd_none, sd_ac,
- /* 16 */
- sd_ac, sd_adtc, sd_adtc, sd_none, sd_none, sd_none, sd_none, sd_none,
- sd_adtc, sd_adtc, sd_adtc, sd_adtc, sd_ac, sd_ac, sd_adtc, sd_none,
- /* 32 */
- sd_ac, sd_ac, sd_none, sd_none, sd_none, sd_none, sd_ac, sd_none,
- sd_none, sd_none, sd_bc, sd_none, sd_none, sd_none, sd_none, sd_none,
- /* 48 */
- sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_ac,
- sd_adtc, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none,
-};
-
-static const int sd_cmd_class[SDMMC_CMD_MAX] = {
- 0, 0, 0, 0, 0, 9, 10, 0, 0, 0, 0, 1, 0, 0, 0, 0,
- 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 6, 6, 6, 6,
- 5, 5, 10, 10, 10, 10, 5, 9, 9, 9, 7, 7, 7, 7, 7, 7,
- 7, 7, 10, 7, 9, 9, 9, 8, 8, 10, 8, 8, 8, 8, 8, 8,
-};
-
static uint8_t sd_crc7(const void *message, size_t width)
{
int i, bit;
@@ -304,6 +353,8 @@ static uint8_t sd_crc7(const void *message, size_t width)
return shift_reg;
}
+/* Operation Conditions register */
+
#define OCR_POWER_DELAY_NS 500000 /* 0.5ms */
FIELD(OCR, VDD_VOLTAGE_WINDOW, 0, 24)
@@ -353,6 +404,8 @@ static void sd_set_ocr(SDState *sd)
}
}
+/* SD Configuration register */
+
static void sd_set_scr(SDState *sd)
{
sd->scr[0] = 0 << 4; /* SCR structure version 1.0 */
@@ -375,6 +428,8 @@ static void sd_set_scr(SDState *sd)
sd->scr[7] = 0x00;
}
+/* Card IDentification register */
+
#define MID 0xaa
#define OID "XY"
#define PNM "QEMU!"
@@ -393,16 +448,32 @@ static void sd_set_cid(SDState *sd)
sd->cid[6] = PNM[3];
sd->cid[7] = PNM[4];
sd->cid[8] = PRV; /* Fake product revision (PRV) */
- sd->cid[9] = 0xde; /* Fake serial number (PSN) */
- sd->cid[10] = 0xad;
- sd->cid[11] = 0xbe;
- sd->cid[12] = 0xef;
+ stl_be_p(&sd->cid[9], 0xdeadbeef); /* Fake serial number (PSN) */
sd->cid[13] = 0x00 | /* Manufacture date (MDT) */
((MDT_YR - 2000) / 10);
sd->cid[14] = ((MDT_YR % 10) << 4) | MDT_MON;
sd->cid[15] = (sd_crc7(sd->cid, 15) << 1) | 1;
}
+static void emmc_set_cid(SDState *sd)
+{
+ sd->cid[0] = MID; /* Fake card manufacturer ID (MID) */
+ sd->cid[1] = 0b01; /* CBX: soldered BGA */
+ sd->cid[2] = OID[0]; /* OEM/Application ID (OID) */
+ sd->cid[3] = PNM[0]; /* Fake product name (PNM) */
+ sd->cid[4] = PNM[1];
+ sd->cid[5] = PNM[2];
+ sd->cid[6] = PNM[3];
+ sd->cid[7] = PNM[4];
+ sd->cid[8] = PNM[4];
+ sd->cid[9] = PRV; /* Fake product revision (PRV) */
+ stl_be_p(&sd->cid[10], 0xdeadbeef); /* Fake serial number (PSN) */
+ sd->cid[14] = (MDT_MON << 4) | (MDT_YR - 1997); /* Manufacture date (MDT) */
+ sd->cid[15] = (sd_crc7(sd->cid, 15) << 1) | 1;
+}
+
+/* Card-Specific Data register */
+
#define HWBLOCK_SHIFT 9 /* 512 bytes */
#define SECTOR_SHIFT 5 /* 16 kilobytes */
#define WPGROUP_SHIFT 7 /* 2 megs */
@@ -414,6 +485,79 @@ static const uint8_t sd_csd_rw_mask[16] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfe,
};
+static void emmc_set_ext_csd(SDState *sd, uint64_t size)
+{
+ uint32_t sectcount = size >> HWBLOCK_SHIFT;
+
+ memset(sd->ext_csd, 0, sizeof(sd->ext_csd)); /* FIXME only RW at reset */
+
+ /* Properties segment (RO) */
+ sd->ext_csd[EXT_CSD_S_CMD_SET] = 0b1; /* supported command sets */
+ sd->ext_csd[EXT_CSD_BOOT_INFO] = 0x0; /* Boot information */
+ /* Boot partition size. 128KB unit */
+ sd->ext_csd[EXT_CSD_BOOT_MULT] = sd->boot_part_size / (128 * KiB);
+ sd->ext_csd[EXT_CSD_ACC_SIZE] = 0x1; /* Access size */
+ sd->ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] = 0x01; /* HC Erase unit size */
+ sd->ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT] = 0x01; /* HC erase timeout */
+ sd->ext_csd[EXT_CSD_REL_WR_SEC_C] = 0x1; /* Reliable write sector count */
+ sd->ext_csd[EXT_CSD_HC_WP_GRP_SIZE] = 0x01; /* HC write protect group size */
+ sd->ext_csd[EXT_CSD_S_C_VCC] = 0x01; /* Sleep current VCC */
+ sd->ext_csd[EXT_CSD_S_C_VCCQ] = 0x01; /* Sleep current VCCQ */
+ sd->ext_csd[EXT_CSD_S_A_TIMEOUT] = 0x01; /* Sleep/Awake timeout */
+ stl_le_p(&sd->ext_csd[EXT_CSD_SEC_CNT], sectcount); /* Sector count */
+ sd->ext_csd[210] = 0x46; /* Min write perf for 8bit@52Mhz */
+ sd->ext_csd[209] = 0x46; /* Min read perf for 8bit@52Mhz */
+ sd->ext_csd[208] = 0x46; /* Min write perf for 4bit@52Mhz */
+ sd->ext_csd[207] = 0x46; /* Min read perf for 4bit@52Mhz */
+ sd->ext_csd[206] = 0x46; /* Min write perf for 4bit@26Mhz */
+ sd->ext_csd[205] = 0x46; /* Min read perf for 4bit@26Mhz */
+ sd->ext_csd[EXT_CSD_CARD_TYPE] = 0b11;
+ sd->ext_csd[EXT_CSD_STRUCTURE] = 2;
+ sd->ext_csd[EXT_CSD_REV] = 3;
+
+ /* Mode segment (RW) */
+ sd->ext_csd[EXT_CSD_PART_CONFIG] = sd->boot_config;
+}
+
+static void emmc_set_csd(SDState *sd, uint64_t size)
+{
+ int hwblock_shift = HWBLOCK_SHIFT;
+ uint32_t sectsize = (1 << (SECTOR_SHIFT + 1)) - 1;
+ uint32_t wpsize = (1 << (WPGROUP_SHIFT + 1)) - 1;
+
+ sd->csd[0] = (3 << 6) | (4 << 2); /* Spec v4.3 with EXT_CSD */
+ sd->csd[1] = (1 << 3) | 6; /* Asynchronous data access time: 1ms */
+ sd->csd[2] = 0x00;
+ sd->csd[3] = (1 << 3) | 3;; /* Maximum bus clock frequency: 100MHz */
+ sd->csd[4] = 0x0f;
+ if (size <= 2 * GiB) {
+ /* use 1k blocks */
+ uint32_t csize1k = (size >> (CMULT_SHIFT + 10)) - 1;
+ sd->csd[5] = 0x5a;
+ sd->csd[6] = 0x80 | ((csize1k >> 10) & 0xf);
+ sd->csd[7] = (csize1k >> 2) & 0xff;
+ } else { /* >= 2GB : size stored in ext CSD, block addressing */
+ sd->csd[5] = 0x59;
+ sd->csd[6] = 0x8f;
+ sd->csd[7] = 0xff;
+ sd->ocr = FIELD_DP32(sd->ocr, OCR, CARD_CAPACITY, 1);
+ }
+ sd->csd[8] = 0xff;
+ sd->csd[9] = 0xfc | /* Max. write current */
+ ((CMULT_SHIFT - 2) >> 1);
+ sd->csd[10] = 0x40 | /* Erase sector size */
+ (((CMULT_SHIFT - 2) << 7) & 0x80) | (sectsize >> 1);
+ sd->csd[11] = 0x00 | /* Write protect group size */
+ ((sectsize << 7) & 0x80) | wpsize;
+ sd->csd[12] = 0x90 | /* Write speed factor */
+ (hwblock_shift >> 2);
+ sd->csd[13] = 0x20 | /* Max. write data block length */
+ ((hwblock_shift << 6) & 0xc0);
+ sd->csd[14] = 0x00;
+ sd->csd[15] = (sd_crc7(sd->csd, 15) << 1) | 1;
+ emmc_set_ext_csd(sd, size);
+}
+
static void sd_set_csd(SDState *sd, uint64_t size)
{
int hwblock_shift = HWBLOCK_SHIFT;
@@ -462,9 +606,7 @@ static void sd_set_csd(SDState *sd, uint64_t size)
sd->csd[4] = 0x5b;
sd->csd[5] = 0x59;
sd->csd[6] = 0x00;
- sd->csd[7] = (size >> 16) & 0xff;
- sd->csd[8] = (size >> 8) & 0xff;
- sd->csd[9] = (size & 0xff);
+ st24_be_p(&sd->csd[7], size);
sd->csd[10] = 0x7f;
sd->csd[11] = 0x80;
sd->csd[12] = 0x0a;
@@ -474,14 +616,37 @@ static void sd_set_csd(SDState *sd, uint64_t size)
sd->csd[15] = (sd_crc7(sd->csd, 15) << 1) | 1;
}
-static void sd_set_rca(SDState *sd)
+/* Relative Card Address register */
+
+static void sd_set_rca(SDState *sd, uint16_t value)
{
- sd->rca += 0x4567;
+ trace_sdcard_set_rca(value);
+ sd->rca = value;
}
+static uint16_t sd_req_get_rca(SDState *s, SDRequest req)
+{
+ switch (s->proto->cmd[req.cmd].type) {
+ case sd_ac:
+ case sd_adtc:
+ return req.arg >> 16;
+ case sd_spi:
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static bool sd_req_rca_same(SDState *s, SDRequest req)
+{
+ return sd_req_get_rca(s, req) == s->rca;
+}
+
+/* Card Status register */
+
FIELD(CSR, AKE_SEQ_ERROR, 3, 1)
FIELD(CSR, APP_CMD, 5, 1)
FIELD(CSR, FX_EVENT, 6, 1)
+FIELD(CSR, SWITCH_ERROR, 7, 1)
FIELD(CSR, READY_FOR_DATA, 8, 1)
FIELD(CSR, CURRENT_STATE, 9, 4)
FIELD(CSR, ERASE_RESET, 13, 1)
@@ -532,7 +697,7 @@ FIELD(CSR, OUT_OF_RANGE, 31, 1)
static void sd_set_cardstatus(SDState *sd)
{
- sd->card_status = 0x00000100;
+ sd->card_status = READY_FOR_DATA;
}
static void sd_set_sdstatus(SDState *sd)
@@ -540,6 +705,21 @@ static void sd_set_sdstatus(SDState *sd)
memset(sd->sd_status, 0, 64);
}
+static const uint8_t sd_tuning_block_pattern4[64] = {
+ /*
+ * See: Physical Layer Simplified Specification Version 3.01,
+ * Table 4-2.
+ */
+ 0xff, 0x0f, 0xff, 0x00, 0x0f, 0xfc, 0xc3, 0xcc,
+ 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef,
+ 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb,
+ 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef,
+ 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c,
+ 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee,
+ 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff,
+ 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde
+};
+
static int sd_req_crc_validate(SDRequest *req)
{
uint8_t buffer[5];
@@ -579,6 +759,54 @@ static void sd_response_r7_make(SDState *sd, uint8_t *response)
stl_be_p(response, sd->vhs);
}
+static uint32_t sd_blk_len(SDState *sd)
+{
+ if (FIELD_EX32(sd->ocr, OCR, CARD_CAPACITY)) {
+ return 1 << HWBLOCK_SHIFT;
+ }
+ return sd->blk_len;
+}
+
+/*
+ * This requires a disk image that has two boot partitions inserted at the
+ * beginning of it. The size of the boot partitions is the "boot-size"
+ * property.
+ */
+static uint32_t sd_bootpart_offset(SDState *sd)
+{
+ unsigned partition_access;
+
+ if (!sd->boot_part_size || !sd_is_emmc(sd)) {
+ return 0;
+ }
+
+ partition_access = sd->ext_csd[EXT_CSD_PART_CONFIG]
+ & EXT_CSD_PART_CONFIG_ACC_MASK;
+ switch (partition_access) {
+ case EXT_CSD_PART_CONFIG_ACC_DEFAULT:
+ return sd->boot_part_size * 2;
+ case EXT_CSD_PART_CONFIG_ACC_BOOT0:
+ return 0;
+ case EXT_CSD_PART_CONFIG_ACC_BOOT0 + 1:
+ return sd->boot_part_size * 1;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static uint64_t sd_req_get_address(SDState *sd, SDRequest req)
+{
+ uint64_t addr;
+
+ if (FIELD_EX32(sd->ocr, OCR, CARD_CAPACITY)) {
+ addr = (uint64_t) req.arg << HWBLOCK_SHIFT;
+ } else {
+ addr = req.arg;
+ }
+ trace_sdcard_req_addr(req.arg, addr);
+ return addr;
+}
+
static inline uint64_t sd_addr_to_wpnum(uint64_t addr)
{
return addr >> (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT);
@@ -586,7 +814,8 @@ static inline uint64_t sd_addr_to_wpnum(uint64_t addr)
static void sd_reset(DeviceState *dev)
{
- SDState *sd = SD_CARD(dev);
+ SDState *sd = SDMMC_COMMON(dev);
+ SDCardClass *sc = SDMMC_COMMON_GET_CLASS(sd);
uint64_t size;
uint64_t sect;
@@ -596,17 +825,22 @@ static void sd_reset(DeviceState *dev)
} else {
sect = 0;
}
- size = sect << 9;
+ size = sect << HWBLOCK_SHIFT;
+ if (sd_is_emmc(sd)) {
+ size -= sd->boot_part_size * 2;
+ }
sect = sd_addr_to_wpnum(size) + 1;
sd->state = sd_idle_state;
- sd->rca = 0x0000;
+
+ /* card registers */
+ sd->rca = sd_is_emmc(sd) ? 0x0001 : 0x0000;
sd->size = size;
sd_set_ocr(sd);
sd_set_scr(sd);
- sd_set_cid(sd);
- sd_set_csd(sd, size);
+ sc->set_cid(sd);
+ sc->set_csd(sd, size);
sd_set_cardstatus(sd);
sd_set_sdstatus(sd);
@@ -650,17 +884,10 @@ static void sd_cardchange(void *opaque, bool load, Error **errp)
trace_sdcard_ejected();
}
- if (sd->me_no_qdev_me_kill_mammoth_with_rocks) {
- qemu_set_irq(sd->inserted_cb, inserted);
- if (inserted) {
- qemu_set_irq(sd->readonly_cb, readonly);
- }
- } else {
- sdbus = SD_BUS(qdev_get_parent_bus(dev));
- sdbus_set_inserted(sdbus, inserted);
- if (inserted) {
- sdbus_set_readonly(sdbus, readonly);
- }
+ sdbus = SD_BUS(qdev_get_parent_bus(dev));
+ sdbus_set_inserted(sdbus, inserted);
+ if (inserted) {
+ sdbus_set_readonly(sdbus, readonly);
}
}
@@ -688,6 +915,24 @@ static const VMStateDescription sd_ocr_vmstate = {
},
};
+static bool vmstate_needed_for_emmc(void *opaque)
+{
+ SDState *sd = opaque;
+
+ return sd_is_emmc(sd);
+}
+
+static const VMStateDescription emmc_extcsd_vmstate = {
+ .name = "sd-card/ext_csd_modes-state",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = vmstate_needed_for_emmc,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT8_ARRAY(ext_csd_rw, SDState, 192),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
static int sd_vmstate_pre_load(void *opaque)
{
SDState *sd = opaque;
@@ -730,60 +975,20 @@ static const VMStateDescription sd_vmstate = {
VMSTATE_UINT32(data_offset, SDState),
VMSTATE_UINT8_ARRAY(data, SDState, 512),
VMSTATE_UNUSED_V(1, 512),
- VMSTATE_BOOL(enable, SDState),
+ VMSTATE_UNUSED(1),
VMSTATE_END_OF_LIST()
},
.subsections = (const VMStateDescription * const []) {
&sd_ocr_vmstate,
+ &emmc_extcsd_vmstate,
NULL
},
};
-/* Legacy initialization function for use by non-qdevified callers */
-SDState *sd_init(BlockBackend *blk, bool is_spi)
-{
- Object *obj;
- DeviceState *dev;
- SDState *sd;
- Error *err = NULL;
-
- obj = object_new(is_spi ? TYPE_SD_CARD_SPI : TYPE_SD_CARD);
- dev = DEVICE(obj);
- if (!qdev_prop_set_drive_err(dev, "drive", blk, &err)) {
- error_reportf_err(err, "sd_init failed: ");
- return NULL;
- }
-
- /*
- * Realizing the device properly would put it into the QOM
- * composition tree even though it is not plugged into an
- * appropriate bus. That's a no-no. Hide the device from
- * QOM/qdev, and call its qdev realize callback directly.
- */
- object_ref(obj);
- object_unparent(obj);
- sd_realize(dev, &err);
- if (err) {
- error_reportf_err(err, "sd_init failed: ");
- return NULL;
- }
-
- sd = SD_CARD(dev);
- sd->me_no_qdev_me_kill_mammoth_with_rocks = true;
- return sd;
-}
-
-void sd_set_cb(SDState *sd, qemu_irq readonly, qemu_irq insert)
-{
- sd->readonly_cb = readonly;
- sd->inserted_cb = insert;
- qemu_set_irq(readonly, sd->blk ? !blk_is_writable(sd->blk) : 0);
- qemu_set_irq(insert, sd->blk ? blk_is_inserted(sd->blk) : 0);
-}
-
static void sd_blk_read(SDState *sd, uint64_t addr, uint32_t len)
{
trace_sdcard_read_block(addr, len);
+ addr += sd_bootpart_offset(sd);
if (!sd->blk || blk_pread(sd->blk, addr, len, sd->data, 0) < 0) {
fprintf(stderr, "sd_blk_read: read error on host side\n");
}
@@ -792,16 +997,12 @@ static void sd_blk_read(SDState *sd, uint64_t addr, uint32_t len)
static void sd_blk_write(SDState *sd, uint64_t addr, uint32_t len)
{
trace_sdcard_write_block(addr, len);
+ addr += sd_bootpart_offset(sd);
if (!sd->blk || blk_pwrite(sd->blk, addr, len, sd->data, 0) < 0) {
fprintf(stderr, "sd_blk_write: write error on host side\n");
}
}
-#define BLK_READ_BLOCK(a, len) sd_blk_read(sd, a, len)
-#define BLK_WRITE_BLOCK(a, len) sd_blk_write(sd, a, len)
-#define APP_READ_BLOCK(a, len) memset(sd->data, 0xec, len)
-#define APP_WRITE_BLOCK(a, len)
-
static void sd_erase(SDState *sd)
{
uint64_t erase_start = sd->erase_start;
@@ -822,8 +1023,8 @@ static void sd_erase(SDState *sd)
if (FIELD_EX32(sd->ocr, OCR, CARD_CAPACITY)) {
/* High capacity memory card: erase units are 512 byte blocks */
- erase_start *= 512;
- erase_end *= 512;
+ erase_start <<= HWBLOCK_SHIFT;
+ erase_end <<= HWBLOCK_SHIFT;
sdsc = false;
}
@@ -850,7 +1051,7 @@ static void sd_erase(SDState *sd)
continue;
}
}
- BLK_WRITE_BLOCK(erase_addr, erase_len);
+ sd_blk_write(sd, erase_addr, erase_len);
}
}
@@ -878,6 +1079,47 @@ static uint32_t sd_wpbits(SDState *sd, uint64_t addr)
return ret;
}
+enum ExtCsdAccessMode {
+ EXT_CSD_ACCESS_MODE_COMMAND_SET = 0,
+ EXT_CSD_ACCESS_MODE_SET_BITS = 1,
+ EXT_CSD_ACCESS_MODE_CLEAR_BITS = 2,
+ EXT_CSD_ACCESS_MODE_WRITE_BYTE = 3
+};
+
+static void emmc_function_switch(SDState *sd, uint32_t arg)
+{
+ uint8_t access = extract32(arg, 24, 2);
+ uint8_t index = extract32(arg, 16, 8);
+ uint8_t value = extract32(arg, 8, 8);
+ uint8_t b = sd->ext_csd[index];
+
+ trace_sdcard_switch(access, index, value, extract32(arg, 0, 2));
+
+ if (index >= 192) {
+ qemu_log_mask(LOG_GUEST_ERROR, "MMC switching illegal offset\n");
+ sd->card_status |= R_CSR_SWITCH_ERROR_MASK;
+ return;
+ }
+
+ switch (access) {
+ case EXT_CSD_ACCESS_MODE_COMMAND_SET:
+ qemu_log_mask(LOG_UNIMP, "MMC Command set switching not supported\n");
+ return;
+ case EXT_CSD_ACCESS_MODE_SET_BITS:
+ b |= value;
+ break;
+ case EXT_CSD_ACCESS_MODE_CLEAR_BITS:
+ b &= ~value;
+ break;
+ case EXT_CSD_ACCESS_MODE_WRITE_BYTE:
+ b = value;
+ break;
+ }
+
+ trace_sdcard_ext_csd_update(index, sd->ext_csd[index], b);
+ sd->ext_csd[index] = b;
+}
+
static void sd_function_switch(SDState *sd, uint32_t arg)
{
int i, mode, new_func;
@@ -1001,7 +1243,16 @@ static bool address_in_range(SDState *sd, const char *desc,
static sd_rsp_type_t sd_invalid_state_for_cmd(SDState *sd, SDRequest req)
{
qemu_log_mask(LOG_GUEST_ERROR, "%s: CMD%i in a wrong state: %s (spec %s)\n",
- sd_proto(sd)->name, req.cmd, sd_state_name(sd->state),
+ sd->proto->name, req.cmd, sd_state_name(sd->state),
+ sd_version_str(sd->spec_version));
+
+ return sd_illegal;
+}
+
+static sd_rsp_type_t sd_invalid_mode_for_cmd(SDState *sd, SDRequest req)
+{
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: CMD%i in a wrong mode: %s (spec %s)\n",
+ sd->proto->name, req.cmd, sd_mode_name(sd->mode),
sd_version_str(sd->spec_version));
return sd_illegal;
@@ -1010,7 +1261,7 @@ static sd_rsp_type_t sd_invalid_state_for_cmd(SDState *sd, SDRequest req)
static sd_rsp_type_t sd_cmd_illegal(SDState *sd, SDRequest req)
{
qemu_log_mask(LOG_GUEST_ERROR, "%s: Unknown CMD%i for spec %s\n",
- sd_proto(sd)->name, req.cmd,
+ sd->proto->name, req.cmd,
sd_version_str(sd->spec_version));
return sd_illegal;
@@ -1020,13 +1271,68 @@ static sd_rsp_type_t sd_cmd_illegal(SDState *sd, SDRequest req)
static sd_rsp_type_t sd_cmd_unimplemented(SDState *sd, SDRequest req)
{
qemu_log_mask(LOG_UNIMP, "%s: CMD%i not implemented\n",
- sd_proto(sd)->name, req.cmd);
+ sd->proto->name, req.cmd);
return sd_illegal;
}
+static sd_rsp_type_t sd_cmd_optional(SDState *sd, SDRequest req)
+{
+ qemu_log_mask(LOG_UNIMP, "%s: Optional CMD%i not implemented\n",
+ sd->proto->name, req.cmd);
+
+ return sd_illegal;
+}
+
+/* Configure fields for following sd_generic_write_byte() calls */
+static sd_rsp_type_t sd_cmd_to_receivingdata(SDState *sd, SDRequest req,
+ uint64_t start, size_t size)
+{
+ if (sd->state != sd_transfer_state) {
+ return sd_invalid_state_for_cmd(sd, req);
+ }
+ sd->state = sd_receivingdata_state;
+ sd->data_start = start;
+ sd->data_offset = 0;
+ /* sd->data[] used as receive buffer */
+ sd->data_size = size ?: sizeof(sd->data);
+ return sd_r1;
+}
+
+/* Configure fields for following sd_generic_read_byte() calls */
+static sd_rsp_type_t sd_cmd_to_sendingdata(SDState *sd, SDRequest req,
+ uint64_t start,
+ const void *data, size_t size)
+{
+ if (sd->state != sd_transfer_state) {
+ sd_invalid_state_for_cmd(sd, req);
+ }
+
+ sd->state = sd_sendingdata_state;
+ sd->data_start = start;
+ sd->data_offset = 0;
+ if (data) {
+ assert(size > 0 && size <= sizeof(sd->data));
+ memcpy(sd->data, data, size);
+ }
+ if (size) {
+ sd->data_size = size;
+ }
+ return sd_r1;
+}
+
+/* CMD0 */
static sd_rsp_type_t sd_cmd_GO_IDLE_STATE(SDState *sd, SDRequest req)
{
+ if (sd->state == sd_sleep_state) {
+ switch (req.arg) {
+ case 0x00000000:
+ case 0xf0f0f0f0:
+ break;
+ default:
+ return sd_r0;
+ }
+ }
if (sd->state != sd_inactive_state) {
sd->state = sd_idle_state;
sd_reset(DEVICE(sd));
@@ -1035,31 +1341,37 @@ static sd_rsp_type_t sd_cmd_GO_IDLE_STATE(SDState *sd, SDRequest req)
return sd_is_spi(sd) ? sd_r1 : sd_r0;
}
-static sd_rsp_type_t sd_cmd_SEND_OP_CMD(SDState *sd, SDRequest req)
+/* CMD1 */
+static sd_rsp_type_t spi_cmd_SEND_OP_COND(SDState *sd, SDRequest req)
{
sd->state = sd_transfer_state;
return sd_r1;
}
+/* CMD2 */
static sd_rsp_type_t sd_cmd_ALL_SEND_CID(SDState *sd, SDRequest req)
{
- if (sd->state != sd_ready_state) {
+ switch (sd->state) {
+ case sd_ready_state:
+ sd->state = sd_identification_state;
+ return sd_r2_i;
+ default:
return sd_invalid_state_for_cmd(sd, req);
}
-
- sd->state = sd_identification_state;
-
- return sd_r2_i;
}
+/* CMD3 */
static sd_rsp_type_t sd_cmd_SEND_RELATIVE_ADDR(SDState *sd, SDRequest req)
{
+ uint16_t random_rca;
+
switch (sd->state) {
case sd_identification_state:
case sd_standby_state:
sd->state = sd_standby_state;
- sd_set_rca(sd);
+ qemu_guest_getrandom_nofail(&random_rca, sizeof(random_rca));
+ sd_set_rca(sd, random_rca);
return sd_r6;
default:
@@ -1067,397 +1379,659 @@ static sd_rsp_type_t sd_cmd_SEND_RELATIVE_ADDR(SDState *sd, SDRequest req)
}
}
-static sd_rsp_type_t sd_cmd_SEND_TUNING_BLOCK(SDState *sd, SDRequest req)
+static sd_rsp_type_t emmc_cmd_SET_RELATIVE_ADDR(SDState *sd, SDRequest req)
+{
+ switch (sd->state) {
+ case sd_identification_state:
+ case sd_standby_state:
+ sd->state = sd_standby_state;
+ sd_set_rca(sd, req.arg >> 16);
+ return sd_r1;
+
+ default:
+ return sd_invalid_state_for_cmd(sd, req);
+ }
+}
+
+/* CMD5 */
+static sd_rsp_type_t emmc_cmd_sleep_awake(SDState *sd, SDRequest req)
{
- if (sd->spec_version < SD_PHY_SPECv3_01_VERS) {
- return sd_cmd_illegal(sd, req);
+ bool do_sleep = extract32(req.arg, 15, 1);
+
+ switch (sd->state) {
+ case sd_sleep_state:
+ if (!do_sleep) {
+ /* Awake */
+ sd->state = sd_standby_state;
}
+ return sd_r1b;
- if (sd->state != sd_transfer_state) {
- return sd_invalid_state_for_cmd(sd, req);
+ case sd_standby_state:
+ if (do_sleep) {
+ sd->state = sd_sleep_state;
}
+ return sd_r1b;
- sd->state = sd_sendingdata_state;
- sd->data_offset = 0;
+ default:
+ return sd_invalid_state_for_cmd(sd, req);
+ }
+}
- return sd_r1;
+/* CMD6 */
+static sd_rsp_type_t sd_cmd_SWITCH_FUNCTION(SDState *sd, SDRequest req)
+{
+ if (sd->mode != sd_data_transfer_mode) {
+ return sd_invalid_mode_for_cmd(sd, req);
+ }
+ if (sd->state != sd_transfer_state) {
+ return sd_invalid_state_for_cmd(sd, req);
+ }
+
+ sd_function_switch(sd, req.arg);
+ return sd_cmd_to_sendingdata(sd, req, 0, NULL, 64);
}
-static sd_rsp_type_t sd_cmd_SET_BLOCK_COUNT(SDState *sd, SDRequest req)
+static sd_rsp_type_t emmc_cmd_SWITCH(SDState *sd, SDRequest req)
+{
+ switch (sd->state) {
+ case sd_transfer_state:
+ sd->state = sd_programming_state;
+ emmc_function_switch(sd, req.arg);
+ sd->state = sd_transfer_state;
+ return sd_r1b;
+ default:
+ return sd_invalid_state_for_cmd(sd, req);
+ }
+}
+
+/* CMD7 */
+static sd_rsp_type_t sd_cmd_DE_SELECT_CARD(SDState *sd, SDRequest req)
{
- if (sd->spec_version < SD_PHY_SPECv3_01_VERS) {
- return sd_cmd_illegal(sd, req);
+ bool same_rca = sd_req_rca_same(sd, req);
+
+ switch (sd->state) {
+ case sd_standby_state:
+ if (!same_rca) {
+ return sd_r0;
}
+ sd->state = sd_transfer_state;
+ return sd_r1b;
- if (sd->state != sd_transfer_state) {
- return sd_invalid_state_for_cmd(sd, req);
+ case sd_transfer_state:
+ case sd_sendingdata_state:
+ if (same_rca) {
+ break;
}
+ sd->state = sd_standby_state;
+ return sd_r1b;
- sd->multi_blk_cnt = req.arg;
+ case sd_disconnect_state:
+ if (!same_rca) {
+ return sd_r0;
+ }
+ sd->state = sd_programming_state;
+ return sd_r1b;
- return sd_r1;
+ case sd_programming_state:
+ if (same_rca) {
+ break;
+ }
+ sd->state = sd_disconnect_state;
+ return sd_r1b;
+
+ default:
+ break;
+ }
+ return sd_invalid_state_for_cmd(sd, req);
}
-static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
+/* CMD8 */
+static sd_rsp_type_t sd_cmd_SEND_IF_COND(SDState *sd, SDRequest req)
{
- uint32_t rca = 0x0000;
- uint64_t addr = (sd->ocr & (1 << 30)) ? (uint64_t) req.arg << 9 : req.arg;
+ if (sd->spec_version < SD_PHY_SPECv2_00_VERS) {
+ return sd_cmd_illegal(sd, req);
+ }
+ if (sd->state != sd_idle_state) {
+ return sd_invalid_state_for_cmd(sd, req);
+ }
+ sd->vhs = 0;
- /* CMD55 precedes an ACMD, so we are not interested in tracing it.
- * However there is no ACMD55, so we want to trace this particular case.
- */
- if (req.cmd != 55 || sd->expecting_acmd) {
- trace_sdcard_normal_command(sd_proto(sd)->name,
- sd_cmd_name(req.cmd), req.cmd,
- req.arg, sd_state_name(sd->state));
+ /* No response if not exactly one VHS bit is set. */
+ if (!(req.arg >> 8) || (req.arg >> (ctz32(req.arg & ~0xff) + 1))) {
+ return sd_is_spi(sd) ? sd_r7 : sd_r0;
}
- /* Not interpreting this as an app command */
- sd->card_status &= ~APP_CMD;
+ /* Accept. */
+ sd->vhs = req.arg;
+ return sd_r7;
+}
- if (sd_cmd_type[req.cmd] == sd_ac
- || sd_cmd_type[req.cmd] == sd_adtc) {
- rca = req.arg >> 16;
+/* CMD8 */
+static sd_rsp_type_t emmc_cmd_SEND_EXT_CSD(SDState *sd, SDRequest req)
+{
+ if (sd->state != sd_transfer_state) {
+ return sd_invalid_state_for_cmd(sd, req);
}
- /* CMD23 (set block count) must be immediately followed by CMD18 or CMD25
- * if not, its effects are cancelled */
- if (sd->multi_blk_cnt != 0 && !(req.cmd == 18 || req.cmd == 25)) {
- sd->multi_blk_cnt = 0;
+ return sd_cmd_to_sendingdata(sd, req, sd_req_get_address(sd, req),
+ sd->ext_csd, sizeof(sd->ext_csd));
+}
+
+/* CMD9 */
+static sd_rsp_type_t spi_cmd_SEND_CSD(SDState *sd, SDRequest req)
+{
+ if (sd->state != sd_standby_state) {
+ return sd_invalid_state_for_cmd(sd, req);
}
+ return sd_cmd_to_sendingdata(sd, req, sd_req_get_address(sd, req),
+ sd->csd, 16);
+}
- if (sd_cmd_class[req.cmd] == 6 && FIELD_EX32(sd->ocr, OCR, CARD_CAPACITY)) {
- /* Only Standard Capacity cards support class 6 commands */
- return sd_illegal;
+static sd_rsp_type_t sd_cmd_SEND_CSD(SDState *sd, SDRequest req)
+{
+ if (sd->state != sd_standby_state) {
+ return sd_invalid_state_for_cmd(sd, req);
}
- if (sd_proto(sd)->cmd[req.cmd]) {
- return sd_proto(sd)->cmd[req.cmd](sd, req);
+ return sd_req_rca_same(sd, req) ? sd_r2_s : sd_r0;
+}
+
+/* CMD10 */
+static sd_rsp_type_t spi_cmd_SEND_CID(SDState *sd, SDRequest req)
+{
+ if (sd->state != sd_standby_state) {
+ return sd_invalid_state_for_cmd(sd, req);
}
+ return sd_cmd_to_sendingdata(sd, req, sd_req_get_address(sd, req),
+ sd->cid, 16);
+}
- switch (req.cmd) {
- /* Basic commands (Class 0 and Class 1) */
- case 4: /* CMD4: SEND_DSR */
- switch (sd->state) {
- case sd_standby_state:
- break;
+static sd_rsp_type_t sd_cmd_SEND_CID(SDState *sd, SDRequest req)
+{
+ if (sd->state != sd_standby_state) {
+ return sd_invalid_state_for_cmd(sd, req);
+ }
- default:
- break;
- }
+ return sd_req_rca_same(sd, req) ? sd_r2_i : sd_r0;
+}
+
+/* CMD12 */
+static sd_rsp_type_t sd_cmd_STOP_TRANSMISSION(SDState *sd, SDRequest req)
+{
+ switch (sd->state) {
+ case sd_sendingdata_state:
+ sd->state = sd_transfer_state;
+ return sd_r1b;
+ case sd_receivingdata_state:
+ sd->state = sd_programming_state;
+ /* Bzzzzzzztt .... Operation complete. */
+ sd->state = sd_transfer_state;
+ return sd_r1;
+ default:
+ return sd_invalid_state_for_cmd(sd, req);
+ }
+}
+
+/* CMD13 */
+static sd_rsp_type_t sd_cmd_SEND_STATUS(SDState *sd, SDRequest req)
+{
+ if (sd->mode != sd_data_transfer_mode) {
+ return sd_invalid_mode_for_cmd(sd, req);
+ }
+
+ switch (sd->state) {
+ case sd_standby_state:
+ case sd_transfer_state:
+ case sd_sendingdata_state:
+ case sd_receivingdata_state:
+ case sd_programming_state:
+ case sd_disconnect_state:
break;
+ default:
+ return sd_invalid_state_for_cmd(sd, req);
+ }
- case 6: /* CMD6: SWITCH_FUNCTION */
- switch (sd->mode) {
- case sd_data_transfer_mode:
- sd_function_switch(sd, req.arg);
- sd->state = sd_sendingdata_state;
- sd->data_start = 0;
- sd->data_offset = 0;
- return sd_r1;
+ if (sd_is_spi(sd)) {
+ return sd_r2_s;
+ }
- default:
- break;
- }
+ return sd_req_rca_same(sd, req) ? sd_r1 : sd_r0;
+}
+
+/* CMD15 */
+static sd_rsp_type_t sd_cmd_GO_INACTIVE_STATE(SDState *sd, SDRequest req)
+{
+ if (sd->mode != sd_data_transfer_mode) {
+ return sd_invalid_mode_for_cmd(sd, req);
+ }
+ switch (sd->state) {
+ case sd_standby_state:
+ case sd_transfer_state:
+ case sd_sendingdata_state:
+ case sd_receivingdata_state:
+ case sd_programming_state:
+ case sd_disconnect_state:
break;
+ default:
+ return sd_invalid_state_for_cmd(sd, req);
+ }
+ if (sd_req_rca_same(sd, req)) {
+ sd->state = sd_inactive_state;
+ }
- case 7: /* CMD7: SELECT/DESELECT_CARD */
- switch (sd->state) {
- case sd_standby_state:
- if (sd->rca != rca)
- return sd_r0;
+ return sd_r0;
+}
- sd->state = sd_transfer_state;
- return sd_r1b;
+/* CMD16 */
+static sd_rsp_type_t sd_cmd_SET_BLOCKLEN(SDState *sd, SDRequest req)
+{
+ if (sd->state != sd_transfer_state) {
+ return sd_invalid_state_for_cmd(sd, req);
+ }
+ if (req.arg > (1 << HWBLOCK_SHIFT)) {
+ sd->card_status |= BLOCK_LEN_ERROR;
+ } else {
+ trace_sdcard_set_blocklen(req.arg);
+ sd->blk_len = req.arg;
+ }
- case sd_transfer_state:
- case sd_sendingdata_state:
- if (sd->rca == rca)
- break;
+ return sd_r1;
+}
- sd->state = sd_standby_state;
- return sd_r1b;
+/* CMD17 */
+static sd_rsp_type_t sd_cmd_READ_SINGLE_BLOCK(SDState *sd, SDRequest req)
+{
+ uint64_t addr;
- case sd_disconnect_state:
- if (sd->rca != rca)
- return sd_r0;
+ if (sd->state != sd_transfer_state) {
+ return sd_invalid_state_for_cmd(sd, req);
+ }
- sd->state = sd_programming_state;
- return sd_r1b;
+ addr = sd_req_get_address(sd, req);
+ if (!address_in_range(sd, "READ_SINGLE_BLOCK", addr, sd->blk_len)) {
+ return sd_r1;
+ }
- case sd_programming_state:
- if (sd->rca == rca)
- break;
+ sd_blk_read(sd, addr, sd->blk_len);
+ return sd_cmd_to_sendingdata(sd, req, addr, NULL, sd->blk_len);
+}
- sd->state = sd_disconnect_state;
- return sd_r1b;
+/* CMD19 */
+static sd_rsp_type_t sd_cmd_SEND_TUNING_BLOCK(SDState *sd, SDRequest req)
+{
+ if (sd->spec_version < SD_PHY_SPECv3_01_VERS) {
+ return sd_cmd_illegal(sd, req);
+ }
- default:
- break;
- }
- break;
+ return sd_cmd_to_sendingdata(sd, req, 0,
+ sd_tuning_block_pattern4,
+ sizeof(sd_tuning_block_pattern4));
+}
- case 8: /* CMD8: SEND_IF_COND */
- if (sd->spec_version < SD_PHY_SPECv2_00_VERS) {
- break;
- }
- if (sd->state != sd_idle_state) {
- break;
- }
- sd->vhs = 0;
+/* CMD23 */
+static sd_rsp_type_t sd_cmd_SET_BLOCK_COUNT(SDState *sd, SDRequest req)
+{
+ if (sd->spec_version < SD_PHY_SPECv3_01_VERS) {
+ return sd_cmd_illegal(sd, req);
+ }
- /* No response if not exactly one VHS bit is set. */
- if (!(req.arg >> 8) || (req.arg >> (ctz32(req.arg & ~0xff) + 1))) {
- return sd_is_spi(sd) ? sd_r7 : sd_r0;
- }
+ if (sd->state != sd_transfer_state) {
+ return sd_invalid_state_for_cmd(sd, req);
+ }
- /* Accept. */
- sd->vhs = req.arg;
- return sd_r7;
+ sd->multi_blk_cnt = req.arg;
+ if (sd_is_emmc(sd)) {
+ sd->multi_blk_cnt &= 0xffff;
+ }
+ trace_sdcard_set_block_count(sd->multi_blk_cnt);
- case 9: /* CMD9: SEND_CSD */
- switch (sd->state) {
- case sd_standby_state:
- if (sd->rca != rca)
- return sd_r0;
+ return sd_r1;
+}
- return sd_r2_s;
+/* CMD24 */
+static sd_rsp_type_t sd_cmd_WRITE_SINGLE_BLOCK(SDState *sd, SDRequest req)
+{
+ uint64_t addr;
- case sd_transfer_state:
- if (!sd_is_spi(sd)) {
- break;
- }
- sd->state = sd_sendingdata_state;
- memcpy(sd->data, sd->csd, 16);
- sd->data_start = addr;
- sd->data_offset = 0;
- return sd_r1;
+ if (sd->state != sd_transfer_state) {
+ return sd_invalid_state_for_cmd(sd, req);
+ }
- default:
- break;
+ addr = sd_req_get_address(sd, req);
+ if (!address_in_range(sd, "WRITE_SINGLE_BLOCK", addr, sd->blk_len)) {
+ return sd_r1;
+ }
+
+ if (sd->size <= SDSC_MAX_CAPACITY) {
+ if (sd_wp_addr(sd, addr)) {
+ sd->card_status |= WP_VIOLATION;
}
- break;
+ }
+ if (sd->csd[14] & 0x30) {
+ sd->card_status |= WP_VIOLATION;
+ }
- case 10: /* CMD10: SEND_CID */
- switch (sd->state) {
- case sd_standby_state:
- if (sd->rca != rca)
- return sd_r0;
+ sd->blk_written = 0;
+ return sd_cmd_to_receivingdata(sd, req, addr, sd->blk_len);
+}
- return sd_r2_i;
+/* CMD26 */
+static sd_rsp_type_t emmc_cmd_PROGRAM_CID(SDState *sd, SDRequest req)
+{
+ return sd_cmd_to_receivingdata(sd, req, 0, sizeof(sd->cid));
+}
- case sd_transfer_state:
- if (!sd_is_spi(sd)) {
- break;
- }
- sd->state = sd_sendingdata_state;
- memcpy(sd->data, sd->cid, 16);
- sd->data_start = addr;
- sd->data_offset = 0;
- return sd_r1;
+/* CMD27 */
+static sd_rsp_type_t sd_cmd_PROGRAM_CSD(SDState *sd, SDRequest req)
+{
+ return sd_cmd_to_receivingdata(sd, req, 0, sizeof(sd->csd));
+}
- default:
- break;
- }
- break;
+static sd_rsp_type_t sd_cmd_SET_CLR_WRITE_PROT(SDState *sd, SDRequest req,
+ bool is_write)
+{
+ uint64_t addr;
- case 12: /* CMD12: STOP_TRANSMISSION */
- switch (sd->state) {
- case sd_sendingdata_state:
- sd->state = sd_transfer_state;
- return sd_r1b;
+ if (sd->size > SDSC_MAX_CAPACITY) {
+ return sd_illegal;
+ }
- case sd_receivingdata_state:
- sd->state = sd_programming_state;
- /* Bzzzzzzztt .... Operation complete. */
- sd->state = sd_transfer_state;
- return sd_r1b;
+ if (sd->state != sd_transfer_state) {
+ return sd_invalid_state_for_cmd(sd, req);
+ }
- default:
- break;
- }
- break;
+ addr = sd_req_get_address(sd, req);
+ if (!address_in_range(sd, is_write ? "SET_WRITE_PROT" : "CLR_WRITE_PROT",
+ addr, 1)) {
+ return sd_r1b;
+ }
- case 13: /* CMD13: SEND_STATUS */
- switch (sd->mode) {
- case sd_data_transfer_mode:
- if (!sd_is_spi(sd) && sd->rca != rca) {
- return sd_r0;
- }
+ sd->state = sd_programming_state;
+ if (is_write) {
+ set_bit(sd_addr_to_wpnum(addr), sd->wp_group_bmap);
+ } else {
+ clear_bit(sd_addr_to_wpnum(addr), sd->wp_group_bmap);
+ }
+ /* Bzzzzzzztt .... Operation complete. */
+ sd->state = sd_transfer_state;
+ return sd_r1;
+}
- return sd_r1;
+/* CMD28 */
+static sd_rsp_type_t sd_cmd_SET_WRITE_PROT(SDState *sd, SDRequest req)
+{
+ return sd_cmd_SET_CLR_WRITE_PROT(sd, req, true);
+}
- default:
- break;
- }
- break;
+/* CMD29 */
+static sd_rsp_type_t sd_cmd_CLR_WRITE_PROT(SDState *sd, SDRequest req)
+{
+ return sd_cmd_SET_CLR_WRITE_PROT(sd, req, false);
+}
- case 15: /* CMD15: GO_INACTIVE_STATE */
- switch (sd->mode) {
- case sd_data_transfer_mode:
- if (sd->rca != rca)
- return sd_r0;
+/* CMD30 */
+static sd_rsp_type_t sd_cmd_SEND_WRITE_PROT(SDState *sd, SDRequest req)
+{
+ uint64_t addr;
+ uint32_t data;
- sd->state = sd_inactive_state;
- return sd_r0;
+ if (sd->size > SDSC_MAX_CAPACITY) {
+ return sd_illegal;
+ }
- default:
- break;
- }
- break;
+ if (sd->state != sd_transfer_state) {
+ return sd_invalid_state_for_cmd(sd, req);
+ }
- /* Block read commands (Class 2) */
- case 16: /* CMD16: SET_BLOCKLEN */
- switch (sd->state) {
- case sd_transfer_state:
- if (req.arg > (1 << HWBLOCK_SHIFT)) {
- sd->card_status |= BLOCK_LEN_ERROR;
- } else {
- trace_sdcard_set_blocklen(req.arg);
- sd->blk_len = req.arg;
- }
+ addr = sd_req_get_address(sd, req);
+ if (!address_in_range(sd, "SEND_WRITE_PROT", addr, sd->blk_len)) {
+ return sd_r1;
+ }
- return sd_r1;
+ data = sd_wpbits(sd, req.arg);
+ return sd_cmd_to_sendingdata(sd, req, addr, &data, sizeof(data));
+}
- default:
- break;
- }
- break;
+/* CMD32 */
+static sd_rsp_type_t sd_cmd_ERASE_WR_BLK_START(SDState *sd, SDRequest req)
+{
+ if (sd->state != sd_transfer_state) {
+ return sd_invalid_state_for_cmd(sd, req);
+ }
+ sd->erase_start = req.arg;
+ return sd_r1;
+}
- case 17: /* CMD17: READ_SINGLE_BLOCK */
- case 18: /* CMD18: READ_MULTIPLE_BLOCK */
- switch (sd->state) {
- case sd_transfer_state:
+/* CMD33 */
+static sd_rsp_type_t sd_cmd_ERASE_WR_BLK_END(SDState *sd, SDRequest req)
+{
+ if (sd->state != sd_transfer_state) {
+ return sd_invalid_state_for_cmd(sd, req);
+ }
+ sd->erase_end = req.arg;
+ return sd_r1;
+}
- if (!address_in_range(sd, "READ_BLOCK", addr, sd->blk_len)) {
- return sd_r1;
- }
+/* CMD38 */
+static sd_rsp_type_t sd_cmd_ERASE(SDState *sd, SDRequest req)
+{
+ if (sd->state != sd_transfer_state) {
+ return sd_invalid_state_for_cmd(sd, req);
+ }
+ if (sd->csd[14] & 0x30) {
+ sd->card_status |= WP_VIOLATION;
+ return sd_r1b;
+ }
- sd->state = sd_sendingdata_state;
- sd->data_start = addr;
- sd->data_offset = 0;
- return sd_r1;
+ sd->state = sd_programming_state;
+ sd_erase(sd);
+ /* Bzzzzzzztt .... Operation complete. */
+ sd->state = sd_transfer_state;
+ return sd_r1b;
+}
- default:
- break;
+/* CMD42 */
+static sd_rsp_type_t sd_cmd_LOCK_UNLOCK(SDState *sd, SDRequest req)
+{
+ return sd_cmd_to_receivingdata(sd, req, 0, 0);
+}
+
+/* CMD55 */
+static sd_rsp_type_t sd_cmd_APP_CMD(SDState *sd, SDRequest req)
+{
+ switch (sd->state) {
+ case sd_ready_state:
+ case sd_identification_state:
+ case sd_inactive_state:
+ case sd_sleep_state:
+ return sd_invalid_state_for_cmd(sd, req);
+ case sd_idle_state:
+ if (!sd_is_spi(sd) && sd_req_get_rca(sd, req) != 0x0000) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "SD: illegal RCA 0x%04x for APP_CMD\n", req.cmd);
}
+ /* fall-through */
+ default:
break;
+ }
+ if (!sd_is_spi(sd) && !sd_req_rca_same(sd, req)) {
+ return sd_r0;
+ }
+ sd->expecting_acmd = true;
+ sd->card_status |= APP_CMD;
- /* Block write commands (Class 4) */
- case 24: /* CMD24: WRITE_SINGLE_BLOCK */
- case 25: /* CMD25: WRITE_MULTIPLE_BLOCK */
- switch (sd->state) {
- case sd_transfer_state:
+ return sd_r1;
+}
- if (!address_in_range(sd, "WRITE_BLOCK", addr, sd->blk_len)) {
- return sd_r1;
- }
+/* CMD56 */
+static sd_rsp_type_t sd_cmd_GEN_CMD(SDState *sd, SDRequest req)
+{
+ if (sd->state != sd_transfer_state) {
+ return sd_invalid_state_for_cmd(sd, req);
+ }
- sd->state = sd_receivingdata_state;
- sd->data_start = addr;
- sd->data_offset = 0;
- sd->blk_written = 0;
+ /* Vendor specific command: our model is RAZ/WI */
+ if (req.arg & 1) {
+ memset(sd->data, 0, sizeof(sd->data));
+ return sd_cmd_to_sendingdata(sd, req, 0, NULL, 0);
+ } else {
+ return sd_cmd_to_receivingdata(sd, req, 0, 0);
+ }
+}
- if (sd->size <= SDSC_MAX_CAPACITY) {
- if (sd_wp_addr(sd, sd->data_start)) {
- sd->card_status |= WP_VIOLATION;
- }
- }
- if (sd->csd[14] & 0x30) {
- sd->card_status |= WP_VIOLATION;
- }
- return sd_r1;
+/* CMD58 */
+static sd_rsp_type_t spi_cmd_READ_OCR(SDState *sd, SDRequest req)
+{
+ return sd_r3;
+}
- default:
- break;
- }
- break;
+/* CMD59 */
+static sd_rsp_type_t spi_cmd_CRC_ON_OFF(SDState *sd, SDRequest req)
+{
+ return sd_r1;
+}
- case 26: /* CMD26: PROGRAM_CID */
- switch (sd->state) {
- case sd_transfer_state:
- sd->state = sd_receivingdata_state;
- sd->data_start = 0;
- sd->data_offset = 0;
- return sd_r1;
+/* ACMD6 */
+static sd_rsp_type_t sd_acmd_SET_BUS_WIDTH(SDState *sd, SDRequest req)
+{
+ if (sd->state != sd_transfer_state) {
+ return sd_invalid_state_for_cmd(sd, req);
+ }
- default:
- break;
- }
- break;
+ sd->sd_status[0] &= 0x3f;
+ sd->sd_status[0] |= (req.arg & 0x03) << 6;
+ return sd_r1;
+}
- case 27: /* CMD27: PROGRAM_CSD */
- switch (sd->state) {
- case sd_transfer_state:
- sd->state = sd_receivingdata_state;
- sd->data_start = 0;
- sd->data_offset = 0;
- return sd_r1;
+/* ACMD13 */
+static sd_rsp_type_t sd_acmd_SD_STATUS(SDState *sd, SDRequest req)
+{
+ return sd_cmd_to_sendingdata(sd, req, 0,
+ sd->sd_status, sizeof(sd->sd_status));
+}
- default:
- break;
- }
- break;
+/* ACMD22 */
+static sd_rsp_type_t sd_acmd_SEND_NUM_WR_BLOCKS(SDState *sd, SDRequest req)
+{
+ return sd_cmd_to_sendingdata(sd, req, 0,
+ &sd->blk_written, sizeof(sd->blk_written));
+}
- /* Write protection (Class 6) */
- case 28: /* CMD28: SET_WRITE_PROT */
- if (sd->size > SDSC_MAX_CAPACITY) {
- return sd_illegal;
- }
+/* ACMD23 */
+static sd_rsp_type_t sd_acmd_SET_WR_BLK_ERASE_COUNT(SDState *sd, SDRequest req)
+{
+ if (sd->state != sd_transfer_state) {
+ return sd_invalid_state_for_cmd(sd, req);
+ }
+ return sd_r1;
+}
- switch (sd->state) {
- case sd_transfer_state:
- if (!address_in_range(sd, "SET_WRITE_PROT", addr, 1)) {
- return sd_r1b;
+/* ACMD41 */
+static sd_rsp_type_t sd_cmd_SEND_OP_COND(SDState *sd, SDRequest req)
+{
+ if (sd->state != sd_idle_state) {
+ return sd_invalid_state_for_cmd(sd, req);
+ }
+
+ /*
+ * If it's the first ACMD41 since reset, we need to decide
+ * whether to power up. If this is not an enquiry ACMD41,
+ * we immediately report power on and proceed below to the
+ * ready state, but if it is, we set a timer to model a
+ * delay for power up. This works around a bug in EDK2
+ * UEFI, which sends an initial enquiry ACMD41, but
+ * assumes that the card is in ready state as soon as it
+ * sees the power up bit set.
+ */
+ if (!FIELD_EX32(sd->ocr, OCR, CARD_POWER_UP)) {
+ if ((req.arg & ACMD41_ENQUIRY_MASK) != 0) {
+ timer_del(sd->ocr_power_timer);
+ sd_ocr_powerup(sd);
+ } else {
+ trace_sdcard_inquiry_cmd41();
+ if (!timer_pending(sd->ocr_power_timer)) {
+ timer_mod_ns(sd->ocr_power_timer,
+ (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)
+ + OCR_POWER_DELAY_NS));
}
+ }
+ }
- sd->state = sd_programming_state;
- set_bit(sd_addr_to_wpnum(addr), sd->wp_group_bmap);
- /* Bzzzzzzztt .... Operation complete. */
- sd->state = sd_transfer_state;
- return sd_r1b;
+ if (FIELD_EX32(sd->ocr & req.arg, OCR, VDD_VOLTAGE_WINDOW)) {
+ /*
+ * We accept any voltage. 10000 V is nothing.
+ *
+ * Once we're powered up, we advance straight to ready state
+ * unless it's an enquiry ACMD41 (bits 23:0 == 0).
+ */
+ sd->state = sd_ready_state;
+ }
- default:
- break;
- }
- break;
+ return sd_r3;
+}
- case 29: /* CMD29: CLR_WRITE_PROT */
- if (sd->size > SDSC_MAX_CAPACITY) {
- return sd_illegal;
- }
+/* ACMD42 */
+static sd_rsp_type_t sd_acmd_SET_CLR_CARD_DETECT(SDState *sd, SDRequest req)
+{
+ if (sd->state != sd_transfer_state) {
+ return sd_invalid_state_for_cmd(sd, req);
+ }
- switch (sd->state) {
- case sd_transfer_state:
- if (!address_in_range(sd, "CLR_WRITE_PROT", addr, 1)) {
- return sd_r1b;
- }
+ /* Bringing in the 50KOhm pull-up resistor... Done. */
+ return sd_r1;
+}
- sd->state = sd_programming_state;
- clear_bit(sd_addr_to_wpnum(addr), sd->wp_group_bmap);
- /* Bzzzzzzztt .... Operation complete. */
- sd->state = sd_transfer_state;
- return sd_r1b;
+/* ACMD51 */
+static sd_rsp_type_t sd_acmd_SEND_SCR(SDState *sd, SDRequest req)
+{
+ return sd_cmd_to_sendingdata(sd, req, 0, sd->scr, sizeof(sd->scr));
+}
- default:
- break;
- }
- break;
+static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
+{
+ uint64_t addr;
- case 30: /* CMD30: SEND_WRITE_PROT */
- if (sd->size > SDSC_MAX_CAPACITY) {
- return sd_illegal;
- }
+ sd->last_cmd_name = sd_cmd_name(sd, req.cmd);
+ /* CMD55 precedes an ACMD, so we are not interested in tracing it.
+ * However there is no ACMD55, so we want to trace this particular case.
+ */
+ if (req.cmd != 55 || sd->expecting_acmd) {
+ trace_sdcard_normal_command(sd->proto->name,
+ sd->last_cmd_name, req.cmd,
+ req.arg, sd_state_name(sd->state));
+ }
+ /* Not interpreting this as an app command */
+ sd->card_status &= ~APP_CMD;
+
+ /* CMD23 (set block count) must be immediately followed by CMD18 or CMD25
+ * if not, its effects are cancelled */
+ if (sd->multi_blk_cnt != 0 && !(req.cmd == 18 || req.cmd == 25)) {
+ sd->multi_blk_cnt = 0;
+ }
+
+ if (sd->proto->cmd[req.cmd].class == 6 && FIELD_EX32(sd->ocr, OCR,
+ CARD_CAPACITY)) {
+ /* Only Standard Capacity cards support class 6 commands */
+ return sd_illegal;
+ }
+
+ if (sd->proto->cmd[req.cmd].handler) {
+ return sd->proto->cmd[req.cmd].handler(sd, req);
+ }
+
+ switch (req.cmd) {
+ /* Block read commands (Class 2) */
+ case 18: /* CMD18: READ_MULTIPLE_BLOCK */
+ addr = sd_req_get_address(sd, req);
switch (sd->state) {
case sd_transfer_state:
- if (!address_in_range(sd, "SEND_WRITE_PROT",
- req.arg, sd->blk_len)) {
+
+ if (!address_in_range(sd, "READ_BLOCK", addr, sd->blk_len)) {
return sd_r1;
}
sd->state = sd_sendingdata_state;
- *(uint32_t *) sd->data = sd_wpbits(sd, req.arg);
sd->data_start = addr;
sd->data_offset = 0;
return sd_r1;
@@ -1467,94 +2041,29 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
}
break;
- /* Erase commands (Class 5) */
- case 32: /* CMD32: ERASE_WR_BLK_START */
- switch (sd->state) {
- case sd_transfer_state:
- sd->erase_start = req.arg;
- return sd_r1;
-
- default:
- break;
- }
- break;
-
- case 33: /* CMD33: ERASE_WR_BLK_END */
+ /* Block write commands (Class 4) */
+ case 25: /* CMD25: WRITE_MULTIPLE_BLOCK */
+ addr = sd_req_get_address(sd, req);
switch (sd->state) {
case sd_transfer_state:
- sd->erase_end = req.arg;
- return sd_r1;
- default:
- break;
- }
- break;
-
- case 38: /* CMD38: ERASE */
- switch (sd->state) {
- case sd_transfer_state:
- if (sd->csd[14] & 0x30) {
- sd->card_status |= WP_VIOLATION;
- return sd_r1b;
+ if (!address_in_range(sd, "WRITE_BLOCK", addr, sd->blk_len)) {
+ return sd_r1;
}
- sd->state = sd_programming_state;
- sd_erase(sd);
- /* Bzzzzzzztt .... Operation complete. */
- sd->state = sd_transfer_state;
- return sd_r1b;
-
- default:
- break;
- }
- break;
-
- /* Lock card commands (Class 7) */
- case 42: /* CMD42: LOCK_UNLOCK */
- switch (sd->state) {
- case sd_transfer_state:
sd->state = sd_receivingdata_state;
- sd->data_start = 0;
+ sd->data_start = addr;
sd->data_offset = 0;
- return sd_r1;
-
- default:
- break;
- }
- break;
+ sd->blk_written = 0;
- /* Application specific commands (Class 8) */
- case 55: /* CMD55: APP_CMD */
- switch (sd->state) {
- case sd_ready_state:
- case sd_identification_state:
- case sd_inactive_state:
- return sd_illegal;
- case sd_idle_state:
- if (rca) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "SD: illegal RCA 0x%04x for APP_CMD\n", req.cmd);
+ if (sd->size <= SDSC_MAX_CAPACITY) {
+ if (sd_wp_addr(sd, sd->data_start)) {
+ sd->card_status |= WP_VIOLATION;
+ }
}
- default:
- break;
- }
- if (!sd_is_spi(sd)) {
- if (sd->rca != rca) {
- return sd_r0;
+ if (sd->csd[14] & 0x30) {
+ sd->card_status |= WP_VIOLATION;
}
- }
- sd->expecting_acmd = true;
- sd->card_status |= APP_CMD;
- return sd_r1;
-
- case 56: /* CMD56: GEN_CMD */
- switch (sd->state) {
- case sd_transfer_state:
- sd->data_offset = 0;
- if (req.arg & 1)
- sd->state = sd_sendingdata_state;
- else
- sd->state = sd_receivingdata_state;
return sd_r1;
default:
@@ -1562,12 +2071,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
}
break;
- case 58: /* CMD58: READ_OCR (SPI) */
- return sd_r3;
-
- case 59: /* CMD59: CRC_ON_OFF (SPI) */
- return sd_r1;
-
default:
qemu_log_mask(LOG_GUEST_ERROR, "SD: Unknown CMD%i\n", req.cmd);
return sd_illegal;
@@ -1579,126 +2082,16 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
static sd_rsp_type_t sd_app_command(SDState *sd,
SDRequest req)
{
- trace_sdcard_app_command(sd_proto(sd)->name, sd_acmd_name(req.cmd),
+ sd->last_cmd_name = sd_acmd_name(sd, req.cmd);
+ trace_sdcard_app_command(sd->proto->name, sd->last_cmd_name,
req.cmd, req.arg, sd_state_name(sd->state));
sd->card_status |= APP_CMD;
- if (sd_proto(sd)->acmd[req.cmd]) {
- return sd_proto(sd)->acmd[req.cmd](sd, req);
+ if (sd->proto->acmd[req.cmd].handler) {
+ return sd->proto->acmd[req.cmd].handler(sd, req);
}
switch (req.cmd) {
- case 6: /* ACMD6: SET_BUS_WIDTH */
- switch (sd->state) {
- case sd_transfer_state:
- sd->sd_status[0] &= 0x3f;
- sd->sd_status[0] |= (req.arg & 0x03) << 6;
- return sd_r1;
-
- default:
- break;
- }
- break;
-
- case 13: /* ACMD13: SD_STATUS */
- switch (sd->state) {
- case sd_transfer_state:
- sd->state = sd_sendingdata_state;
- sd->data_start = 0;
- sd->data_offset = 0;
- return sd_r1;
-
- default:
- break;
- }
- break;
-
- case 22: /* ACMD22: SEND_NUM_WR_BLOCKS */
- switch (sd->state) {
- case sd_transfer_state:
- *(uint32_t *) sd->data = sd->blk_written;
-
- sd->state = sd_sendingdata_state;
- sd->data_start = 0;
- sd->data_offset = 0;
- return sd_r1;
-
- default:
- break;
- }
- break;
-
- case 23: /* ACMD23: SET_WR_BLK_ERASE_COUNT */
- switch (sd->state) {
- case sd_transfer_state:
- return sd_r1;
-
- default:
- break;
- }
- break;
-
- case 41: /* ACMD41: SD_APP_OP_COND */
- if (sd->state != sd_idle_state) {
- break;
- }
- /* If it's the first ACMD41 since reset, we need to decide
- * whether to power up. If this is not an enquiry ACMD41,
- * we immediately report power on and proceed below to the
- * ready state, but if it is, we set a timer to model a
- * delay for power up. This works around a bug in EDK2
- * UEFI, which sends an initial enquiry ACMD41, but
- * assumes that the card is in ready state as soon as it
- * sees the power up bit set. */
- if (!FIELD_EX32(sd->ocr, OCR, CARD_POWER_UP)) {
- if ((req.arg & ACMD41_ENQUIRY_MASK) != 0) {
- timer_del(sd->ocr_power_timer);
- sd_ocr_powerup(sd);
- } else {
- trace_sdcard_inquiry_cmd41();
- if (!timer_pending(sd->ocr_power_timer)) {
- timer_mod_ns(sd->ocr_power_timer,
- (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)
- + OCR_POWER_DELAY_NS));
- }
- }
- }
-
- if (FIELD_EX32(sd->ocr & req.arg, OCR, VDD_VOLTAGE_WINDOW)) {
- /* We accept any voltage. 10000 V is nothing.
- *
- * Once we're powered up, we advance straight to ready state
- * unless it's an enquiry ACMD41 (bits 23:0 == 0).
- */
- sd->state = sd_ready_state;
- }
-
- return sd_r3;
-
- case 42: /* ACMD42: SET_CLR_CARD_DETECT */
- switch (sd->state) {
- case sd_transfer_state:
- /* Bringing in the 50KOhm pull-up resistor... Done. */
- return sd_r1;
-
- default:
- break;
- }
- break;
-
- case 51: /* ACMD51: SEND_SCR */
- switch (sd->state) {
- case sd_transfer_state:
- sd->state = sd_sendingdata_state;
- sd->data_start = 0;
- sd->data_offset = 0;
- return sd_r1;
-
- default:
- break;
- }
- break;
-
case 18: /* Reserved for SD security applications */
case 25:
case 26:
@@ -1720,8 +2113,10 @@ static sd_rsp_type_t sd_app_command(SDState *sd,
return sd_illegal;
}
-static int cmd_valid_while_locked(SDState *sd, const uint8_t cmd)
+static bool cmd_valid_while_locked(SDState *sd, unsigned cmd)
{
+ unsigned cmd_class;
+
/* Valid commands in locked state:
* basic class (0)
* lock card class (7)
@@ -1734,21 +2129,31 @@ static int cmd_valid_while_locked(SDState *sd, const uint8_t cmd)
return cmd == 41 || cmd == 42;
}
if (cmd == 16 || cmd == 55) {
- return 1;
+ return true;
+ }
+ if (!sd->proto->cmd[cmd].handler) {
+ return false;
}
- return sd_cmd_class[cmd] == 0 || sd_cmd_class[cmd] == 7;
+ cmd_class = sd->proto->cmd[cmd].class;
+
+ return cmd_class == 0 || cmd_class == 7;
}
-int sd_do_command(SDState *sd, SDRequest *req,
- uint8_t *response) {
+static int sd_do_command(SDState *sd, SDRequest *req,
+ uint8_t *response) {
int last_state;
sd_rsp_type_t rtype;
int rsplen;
- if (!sd->blk || !blk_is_inserted(sd->blk) || !sd->enable) {
+ if (!sd->blk || !blk_is_inserted(sd->blk)) {
return 0;
}
+ if (sd->state == sd_inactive_state) {
+ rtype = sd_illegal;
+ goto send_response;
+ }
+
if (sd_req_crc_validate(req)) {
sd->card_status |= COM_CRC_ERROR;
rtype = sd_illegal;
@@ -1761,6 +2166,12 @@ int sd_do_command(SDState *sd, SDRequest *req,
req->cmd &= 0x3f;
}
+ if (sd->state == sd_sleep_state && req->cmd) {
+ qemu_log_mask(LOG_GUEST_ERROR, "SD: Card is sleeping\n");
+ rtype = sd_r0;
+ goto send_response;
+ }
+
if (sd->card_status & CARD_IS_LOCKED) {
if (!cmd_valid_while_locked(sd, req->cmd)) {
sd->card_status |= ILLEGAL_COMMAND;
@@ -1787,9 +2198,8 @@ int sd_do_command(SDState *sd, SDRequest *req,
/* Valid command, we can update the 'state before command' bits.
* (Do this now so they appear in r1 responses.)
*/
- sd->current_cmd = req->cmd;
- sd->card_status &= ~CURRENT_STATE;
- sd->card_status |= (last_state << 9);
+ sd->card_status = FIELD_DP32(sd->card_status, CSR,
+ CURRENT_STATE, last_state);
}
send_response:
@@ -1826,6 +2236,13 @@ send_response:
break;
case sd_r0:
+ /*
+ * Invalid state transition, reset implementation
+ * fields to avoid OOB abuse.
+ */
+ sd->data_start = 0;
+ sd->data_offset = 0;
+ /* fall-through */
case sd_illegal:
rsplen = 0;
break;
@@ -1845,15 +2262,43 @@ send_response:
qemu_hexdump(stderr, "Response", response, rsplen);
#endif
+ sd->current_cmd = rtype == sd_illegal ? 0 : req->cmd;
+
return rsplen;
}
-void sd_write_byte(SDState *sd, uint8_t value)
+/* Return true if buffer is consumed. Configured by sd_cmd_to_receivingdata() */
+static bool sd_generic_write_byte(SDState *sd, uint8_t value)
+{
+ sd->data[sd->data_offset] = value;
+
+ if (++sd->data_offset >= sd->data_size) {
+ sd->state = sd_transfer_state;
+ return true;
+ }
+ return false;
+}
+
+/* Return true when buffer is consumed. Configured by sd_cmd_to_sendingdata() */
+static bool sd_generic_read_byte(SDState *sd, uint8_t *value)
+{
+ *value = sd->data[sd->data_offset];
+
+ if (++sd->data_offset >= sd->data_size) {
+ sd->state = sd_transfer_state;
+ return true;
+ }
+
+ return false;
+}
+
+static void sd_write_byte(SDState *sd, uint8_t value)
{
int i;
- if (!sd->blk || !blk_is_inserted(sd->blk) || !sd->enable)
+ if (!sd->blk || !blk_is_inserted(sd->blk)) {
return;
+ }
if (sd->state != sd_receivingdata_state) {
qemu_log_mask(LOG_GUEST_ERROR,
@@ -1864,16 +2309,15 @@ void sd_write_byte(SDState *sd, uint8_t value)
if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION))
return;
- trace_sdcard_write_data(sd_proto(sd)->name,
- sd_acmd_name(sd->current_cmd),
- sd->current_cmd, value);
+ trace_sdcard_write_data(sd->proto->name,
+ sd->last_cmd_name,
+ sd->current_cmd, sd->data_offset, value);
switch (sd->current_cmd) {
case 24: /* CMD24: WRITE_SINGLE_BLOCK */
- sd->data[sd->data_offset ++] = value;
- if (sd->data_offset >= sd->blk_len) {
+ if (sd_generic_write_byte(sd, value)) {
/* TODO: Check CRC before committing */
sd->state = sd_programming_state;
- BLK_WRITE_BLOCK(sd->data_start, sd->data_offset);
+ sd_blk_write(sd, sd->data_start, sd->data_offset);
sd->blk_written ++;
sd->csd[14] |= 0x40;
/* Bzzzzzzztt .... Operation complete. */
@@ -1899,7 +2343,7 @@ void sd_write_byte(SDState *sd, uint8_t value)
if (sd->data_offset >= sd->blk_len) {
/* TODO: Check CRC before committing */
sd->state = sd_programming_state;
- BLK_WRITE_BLOCK(sd->data_start, sd->data_offset);
+ sd_blk_write(sd, sd->data_start, sd->data_offset);
sd->blk_written++;
sd->data_start += sd->blk_len;
sd->data_offset = 0;
@@ -1919,8 +2363,7 @@ void sd_write_byte(SDState *sd, uint8_t value)
break;
case 26: /* CMD26: PROGRAM_CID */
- sd->data[sd->data_offset ++] = value;
- if (sd->data_offset >= sizeof(sd->cid)) {
+ if (sd_generic_write_byte(sd, value)) {
/* TODO: Check CRC before committing */
sd->state = sd_programming_state;
for (i = 0; i < sizeof(sd->cid); i ++)
@@ -1938,8 +2381,7 @@ void sd_write_byte(SDState *sd, uint8_t value)
break;
case 27: /* CMD27: PROGRAM_CSD */
- sd->data[sd->data_offset ++] = value;
- if (sd->data_offset >= sizeof(sd->csd)) {
+ if (sd_generic_write_byte(sd, value)) {
/* TODO: Check CRC before committing */
sd->state = sd_programming_state;
for (i = 0; i < sizeof(sd->csd); i ++)
@@ -1962,8 +2404,7 @@ void sd_write_byte(SDState *sd, uint8_t value)
break;
case 42: /* CMD42: LOCK_UNLOCK */
- sd->data[sd->data_offset ++] = value;
- if (sd->data_offset >= sd->blk_len) {
+ if (sd_generic_write_byte(sd, value)) {
/* TODO: Check CRC before committing */
sd->state = sd_programming_state;
sd_lock_command(sd);
@@ -1973,95 +2414,62 @@ void sd_write_byte(SDState *sd, uint8_t value)
break;
case 56: /* CMD56: GEN_CMD */
- sd->data[sd->data_offset ++] = value;
- if (sd->data_offset >= sd->blk_len) {
- APP_WRITE_BLOCK(sd->data_start, sd->data_offset);
- sd->state = sd_transfer_state;
- }
+ sd_generic_write_byte(sd, value);
break;
default:
- qemu_log_mask(LOG_GUEST_ERROR, "%s: unknown command\n", __func__);
- break;
+ g_assert_not_reached();
}
}
-#define SD_TUNING_BLOCK_SIZE 64
-
-static const uint8_t sd_tuning_block_pattern[SD_TUNING_BLOCK_SIZE] = {
- /* See: Physical Layer Simplified Specification Version 3.01, Table 4-2 */
- 0xff, 0x0f, 0xff, 0x00, 0x0f, 0xfc, 0xc3, 0xcc,
- 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef,
- 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb,
- 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef,
- 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c,
- 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee,
- 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff,
- 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde,
-};
-
-uint8_t sd_read_byte(SDState *sd)
+static uint8_t sd_read_byte(SDState *sd)
{
/* TODO: Append CRCs */
+ const uint8_t dummy_byte = 0x00;
uint8_t ret;
uint32_t io_len;
- if (!sd->blk || !blk_is_inserted(sd->blk) || !sd->enable)
- return 0x00;
+ if (!sd->blk || !blk_is_inserted(sd->blk)) {
+ return dummy_byte;
+ }
if (sd->state != sd_sendingdata_state) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: not in Sending-Data state\n", __func__);
- return 0x00;
+ return dummy_byte;
}
- if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION))
- return 0x00;
+ if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION)) {
+ return dummy_byte;
+ }
- io_len = (sd->ocr & (1 << 30)) ? 512 : sd->blk_len;
+ io_len = sd_blk_len(sd);
- trace_sdcard_read_data(sd_proto(sd)->name,
- sd_acmd_name(sd->current_cmd),
- sd->current_cmd, io_len);
+ trace_sdcard_read_data(sd->proto->name,
+ sd->last_cmd_name, sd->current_cmd,
+ sd->data_offset, sd->data_size, io_len);
switch (sd->current_cmd) {
case 6: /* CMD6: SWITCH_FUNCTION */
- ret = sd->data[sd->data_offset ++];
-
- if (sd->data_offset >= 64)
- sd->state = sd_transfer_state;
- break;
-
+ case 8: /* CMD8: SEND_EXT_CSD */
case 9: /* CMD9: SEND_CSD */
- case 10: /* CMD10: SEND_CID */
- ret = sd->data[sd->data_offset ++];
-
- if (sd->data_offset >= 16)
- sd->state = sd_transfer_state;
- break;
-
- case 13: /* ACMD13: SD_STATUS */
- ret = sd->sd_status[sd->data_offset ++];
-
- if (sd->data_offset >= sizeof(sd->sd_status))
- sd->state = sd_transfer_state;
- break;
-
- case 17: /* CMD17: READ_SINGLE_BLOCK */
- if (sd->data_offset == 0)
- BLK_READ_BLOCK(sd->data_start, io_len);
- ret = sd->data[sd->data_offset ++];
-
- if (sd->data_offset >= io_len)
- sd->state = sd_transfer_state;
+ case 10: /* CMD10: SEND_CID */
+ case 13: /* ACMD13: SD_STATUS */
+ case 17: /* CMD17: READ_SINGLE_BLOCK */
+ case 19: /* CMD19: SEND_TUNING_BLOCK (SD) */
+ case 22: /* ACMD22: SEND_NUM_WR_BLOCKS */
+ case 30: /* CMD30: SEND_WRITE_PROT */
+ case 51: /* ACMD51: SEND_SCR */
+ case 56: /* CMD56: GEN_CMD */
+ sd_generic_read_byte(sd, &ret);
break;
case 18: /* CMD18: READ_MULTIPLE_BLOCK */
if (sd->data_offset == 0) {
if (!address_in_range(sd, "READ_MULTIPLE_BLOCK",
sd->data_start, io_len)) {
- return 0x00;
+ return dummy_byte;
}
- BLK_READ_BLOCK(sd->data_start, io_len);
+ sd_blk_read(sd, sd->data_start, io_len);
}
ret = sd->data[sd->data_offset ++];
@@ -2079,46 +2487,10 @@ uint8_t sd_read_byte(SDState *sd)
}
break;
- case 19: /* CMD19: SEND_TUNING_BLOCK (SD) */
- if (sd->data_offset >= SD_TUNING_BLOCK_SIZE - 1) {
- sd->state = sd_transfer_state;
- }
- ret = sd_tuning_block_pattern[sd->data_offset++];
- break;
-
- case 22: /* ACMD22: SEND_NUM_WR_BLOCKS */
- ret = sd->data[sd->data_offset ++];
-
- if (sd->data_offset >= 4)
- sd->state = sd_transfer_state;
- break;
-
- case 30: /* CMD30: SEND_WRITE_PROT */
- ret = sd->data[sd->data_offset ++];
-
- if (sd->data_offset >= 4)
- sd->state = sd_transfer_state;
- break;
-
- case 51: /* ACMD51: SEND_SCR */
- ret = sd->scr[sd->data_offset ++];
-
- if (sd->data_offset >= sizeof(sd->scr))
- sd->state = sd_transfer_state;
- break;
-
- case 56: /* CMD56: GEN_CMD */
- if (sd->data_offset == 0)
- APP_READ_BLOCK(sd->data_start, sd->blk_len);
- ret = sd->data[sd->data_offset ++];
-
- if (sd->data_offset >= sd->blk_len)
- sd->state = sd_transfer_state;
- break;
-
default:
- qemu_log_mask(LOG_GUEST_ERROR, "%s: unknown command\n", __func__);
- return 0x00;
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: DAT read illegal for command %s\n",
+ __func__, sd->last_cmd_name);
+ return dummy_byte;
}
return ret;
@@ -2134,63 +2506,178 @@ static bool sd_data_ready(SDState *sd)
return sd->state == sd_sendingdata_state;
}
-void sd_enable(SDState *sd, bool enable)
-{
- sd->enable = enable;
-}
-
static const SDProto sd_proto_spi = {
.name = "SPI",
.cmd = {
- [0] = sd_cmd_GO_IDLE_STATE,
- [1] = sd_cmd_SEND_OP_CMD,
- [2 ... 4] = sd_cmd_illegal,
- [5] = sd_cmd_illegal,
- [7] = sd_cmd_illegal,
- [15] = sd_cmd_illegal,
- [26] = sd_cmd_illegal,
- [52 ... 54] = sd_cmd_illegal,
+ [0] = {0, sd_spi, "GO_IDLE_STATE", sd_cmd_GO_IDLE_STATE},
+ [1] = {0, sd_spi, "SEND_OP_COND", spi_cmd_SEND_OP_COND},
+ [5] = {9, sd_spi, "IO_SEND_OP_COND", sd_cmd_optional},
+ [6] = {10, sd_spi, "SWITCH_FUNCTION", sd_cmd_SWITCH_FUNCTION},
+ [8] = {0, sd_spi, "SEND_IF_COND", sd_cmd_SEND_IF_COND},
+ [9] = {0, sd_spi, "SEND_CSD", spi_cmd_SEND_CSD},
+ [10] = {0, sd_spi, "SEND_CID", spi_cmd_SEND_CID},
+ [12] = {0, sd_spi, "STOP_TRANSMISSION", sd_cmd_STOP_TRANSMISSION},
+ [13] = {0, sd_spi, "SEND_STATUS", sd_cmd_SEND_STATUS},
+ [16] = {2, sd_spi, "SET_BLOCKLEN", sd_cmd_SET_BLOCKLEN},
+ [17] = {2, sd_spi, "READ_SINGLE_BLOCK", sd_cmd_READ_SINGLE_BLOCK},
+ [24] = {4, sd_spi, "WRITE_SINGLE_BLOCK", sd_cmd_WRITE_SINGLE_BLOCK},
+ [27] = {4, sd_spi, "PROGRAM_CSD", sd_cmd_PROGRAM_CSD},
+ [28] = {6, sd_spi, "SET_WRITE_PROT", sd_cmd_SET_WRITE_PROT},
+ [29] = {6, sd_spi, "CLR_WRITE_PROT", sd_cmd_CLR_WRITE_PROT},
+ [30] = {6, sd_spi, "SEND_WRITE_PROT", sd_cmd_SEND_WRITE_PROT},
+ [32] = {5, sd_spi, "ERASE_WR_BLK_START", sd_cmd_ERASE_WR_BLK_START},
+ [33] = {5, sd_spi, "ERASE_WR_BLK_END", sd_cmd_ERASE_WR_BLK_END},
+ [34] = {10, sd_spi, "READ_SEC_CMD", sd_cmd_optional},
+ [35] = {10, sd_spi, "WRITE_SEC_CMD", sd_cmd_optional},
+ [36] = {10, sd_spi, "SEND_PSI", sd_cmd_optional},
+ [37] = {10, sd_spi, "CONTROL_ASSD_SYSTEM", sd_cmd_optional},
+ [38] = {5, sd_spi, "ERASE", sd_cmd_ERASE},
+ [42] = {7, sd_spi, "LOCK_UNLOCK", sd_cmd_LOCK_UNLOCK},
+ [50] = {10, sd_spi, "DIRECT_SECURE_READ", sd_cmd_optional},
+ [52] = {9, sd_spi, "IO_RW_DIRECT", sd_cmd_optional},
+ [53] = {9, sd_spi, "IO_RW_EXTENDED", sd_cmd_optional},
+ [55] = {8, sd_spi, "APP_CMD", sd_cmd_APP_CMD},
+ [56] = {8, sd_spi, "GEN_CMD", sd_cmd_GEN_CMD},
+ [57] = {10, sd_spi, "DIRECT_SECURE_WRITE", sd_cmd_optional},
+ [58] = {0, sd_spi, "READ_OCR", spi_cmd_READ_OCR},
+ [59] = {0, sd_spi, "CRC_ON_OFF", spi_cmd_CRC_ON_OFF},
},
.acmd = {
- [6] = sd_cmd_unimplemented,
- [41] = sd_cmd_SEND_OP_CMD,
+ [13] = {8, sd_spi, "SD_STATUS", sd_acmd_SD_STATUS},
+ [22] = {8, sd_spi, "SEND_NUM_WR_BLOCKS", sd_acmd_SEND_NUM_WR_BLOCKS},
+ [23] = {8, sd_spi, "SET_WR_BLK_ERASE_COUNT", sd_acmd_SET_WR_BLK_ERASE_COUNT},
+ [41] = {8, sd_spi, "SEND_OP_COND", spi_cmd_SEND_OP_COND},
+ [42] = {8, sd_spi, "SET_CLR_CARD_DETECT", sd_acmd_SET_CLR_CARD_DETECT},
+ [51] = {8, sd_spi, "SEND_SCR", sd_acmd_SEND_SCR},
},
};
static const SDProto sd_proto_sd = {
.name = "SD",
.cmd = {
- [0] = sd_cmd_GO_IDLE_STATE,
- [1] = sd_cmd_illegal,
- [2] = sd_cmd_ALL_SEND_CID,
- [3] = sd_cmd_SEND_RELATIVE_ADDR,
- [5] = sd_cmd_illegal,
- [19] = sd_cmd_SEND_TUNING_BLOCK,
- [23] = sd_cmd_SET_BLOCK_COUNT,
- [52 ... 54] = sd_cmd_illegal,
- [58] = sd_cmd_illegal,
- [59] = sd_cmd_illegal,
+ [0] = {0, sd_bc, "GO_IDLE_STATE", sd_cmd_GO_IDLE_STATE},
+ [2] = {0, sd_bcr, "ALL_SEND_CID", sd_cmd_ALL_SEND_CID},
+ [3] = {0, sd_bcr, "SEND_RELATIVE_ADDR", sd_cmd_SEND_RELATIVE_ADDR},
+ [4] = {0, sd_bc, "SEND_DSR", sd_cmd_unimplemented},
+ [5] = {9, sd_bc, "IO_SEND_OP_COND", sd_cmd_optional},
+ [6] = {10, sd_adtc, "SWITCH_FUNCTION", sd_cmd_SWITCH_FUNCTION},
+ [7] = {0, sd_ac, "(DE)SELECT_CARD", sd_cmd_DE_SELECT_CARD},
+ [8] = {0, sd_bcr, "SEND_IF_COND", sd_cmd_SEND_IF_COND},
+ [9] = {0, sd_ac, "SEND_CSD", sd_cmd_SEND_CSD},
+ [10] = {0, sd_ac, "SEND_CID", sd_cmd_SEND_CID},
+ [11] = {0, sd_ac, "VOLTAGE_SWITCH", sd_cmd_optional},
+ [12] = {0, sd_ac, "STOP_TRANSMISSION", sd_cmd_STOP_TRANSMISSION},
+ [13] = {0, sd_ac, "SEND_STATUS", sd_cmd_SEND_STATUS},
+ [15] = {0, sd_ac, "GO_INACTIVE_STATE", sd_cmd_GO_INACTIVE_STATE},
+ [16] = {2, sd_ac, "SET_BLOCKLEN", sd_cmd_SET_BLOCKLEN},
+ [17] = {2, sd_adtc, "READ_SINGLE_BLOCK", sd_cmd_READ_SINGLE_BLOCK},
+ [19] = {2, sd_adtc, "SEND_TUNING_BLOCK", sd_cmd_SEND_TUNING_BLOCK},
+ [20] = {2, sd_ac, "SPEED_CLASS_CONTROL", sd_cmd_optional},
+ [23] = {2, sd_ac, "SET_BLOCK_COUNT", sd_cmd_SET_BLOCK_COUNT},
+ [24] = {4, sd_adtc, "WRITE_SINGLE_BLOCK", sd_cmd_WRITE_SINGLE_BLOCK},
+ [27] = {4, sd_adtc, "PROGRAM_CSD", sd_cmd_PROGRAM_CSD},
+ [28] = {6, sd_ac, "SET_WRITE_PROT", sd_cmd_SET_WRITE_PROT},
+ [29] = {6, sd_ac, "CLR_WRITE_PROT", sd_cmd_CLR_WRITE_PROT},
+ [30] = {6, sd_adtc, "SEND_WRITE_PROT", sd_cmd_SEND_WRITE_PROT},
+ [32] = {5, sd_ac, "ERASE_WR_BLK_START", sd_cmd_ERASE_WR_BLK_START},
+ [33] = {5, sd_ac, "ERASE_WR_BLK_END", sd_cmd_ERASE_WR_BLK_END},
+ [34] = {10, sd_adtc, "READ_SEC_CMD", sd_cmd_optional},
+ [35] = {10, sd_adtc, "WRITE_SEC_CMD", sd_cmd_optional},
+ [36] = {10, sd_adtc, "SEND_PSI", sd_cmd_optional},
+ [37] = {10, sd_ac, "CONTROL_ASSD_SYSTEM", sd_cmd_optional},
+ [38] = {5, sd_ac, "ERASE", sd_cmd_ERASE},
+ [42] = {7, sd_adtc, "LOCK_UNLOCK", sd_cmd_LOCK_UNLOCK},
+ [43] = {1, sd_ac, "Q_MANAGEMENT", sd_cmd_optional},
+ [44] = {1, sd_ac, "Q_TASK_INFO_A", sd_cmd_optional},
+ [45] = {1, sd_ac, "Q_TASK_INFO_B", sd_cmd_optional},
+ [46] = {1, sd_adtc, "Q_RD_TASK", sd_cmd_optional},
+ [47] = {1, sd_adtc, "Q_WR_TASK", sd_cmd_optional},
+ [48] = {1, sd_adtc, "READ_EXTR_SINGLE", sd_cmd_optional},
+ [49] = {1, sd_adtc, "WRITE_EXTR_SINGLE", sd_cmd_optional},
+ [50] = {10, sd_adtc, "DIRECT_SECURE_READ", sd_cmd_optional},
+ [52] = {9, sd_bc, "IO_RW_DIRECT", sd_cmd_optional},
+ [53] = {9, sd_bc, "IO_RW_EXTENDED", sd_cmd_optional},
+ [55] = {8, sd_ac, "APP_CMD", sd_cmd_APP_CMD},
+ [56] = {8, sd_adtc, "GEN_CMD", sd_cmd_GEN_CMD},
+ [57] = {10, sd_adtc, "DIRECT_SECURE_WRITE", sd_cmd_optional},
+ [58] = {11, sd_adtc, "READ_EXTR_MULTI", sd_cmd_optional},
+ [59] = {11, sd_adtc, "WRITE_EXTR_MULTI", sd_cmd_optional},
+ },
+ .acmd = {
+ [6] = {8, sd_ac, "SET_BUS_WIDTH", sd_acmd_SET_BUS_WIDTH},
+ [13] = {8, sd_adtc, "SD_STATUS", sd_acmd_SD_STATUS},
+ [22] = {8, sd_adtc, "SEND_NUM_WR_BLOCKS", sd_acmd_SEND_NUM_WR_BLOCKS},
+ [23] = {8, sd_ac, "SET_WR_BLK_ERASE_COUNT", sd_acmd_SET_WR_BLK_ERASE_COUNT},
+ [41] = {8, sd_bcr, "SEND_OP_COND", sd_cmd_SEND_OP_COND},
+ [42] = {8, sd_ac, "SET_CLR_CARD_DETECT", sd_acmd_SET_CLR_CARD_DETECT},
+ [51] = {8, sd_adtc, "SEND_SCR", sd_acmd_SEND_SCR},
+ },
+};
+
+static const SDProto sd_proto_emmc = {
+ /* Only v4.3 is supported */
+ .name = "eMMC",
+ .cmd = {
+ [0] = {0, sd_bc, "GO_IDLE_STATE", sd_cmd_GO_IDLE_STATE},
+ [1] = {0, sd_bcr, "SEND_OP_COND", sd_cmd_SEND_OP_COND},
+ [2] = {0, sd_bcr, "ALL_SEND_CID", sd_cmd_ALL_SEND_CID},
+ [3] = {0, sd_ac, "SET_RELATIVE_ADDR", emmc_cmd_SET_RELATIVE_ADDR},
+ [4] = {0, sd_bc, "SEND_DSR", sd_cmd_unimplemented},
+ [5] = {0, sd_ac, "SLEEP/AWAKE", emmc_cmd_sleep_awake},
+ [6] = {10, sd_adtc, "SWITCH", emmc_cmd_SWITCH},
+ [7] = {0, sd_ac, "(DE)SELECT_CARD", sd_cmd_DE_SELECT_CARD},
+ [8] = {0, sd_adtc, "SEND_EXT_CSD", emmc_cmd_SEND_EXT_CSD},
+ [9] = {0, sd_ac, "SEND_CSD", sd_cmd_SEND_CSD},
+ [10] = {0, sd_ac, "SEND_CID", sd_cmd_SEND_CID},
+ [11] = {1, sd_adtc, "READ_DAT_UNTIL_STOP", sd_cmd_unimplemented},
+ [12] = {0, sd_ac, "STOP_TRANSMISSION", sd_cmd_STOP_TRANSMISSION},
+ [13] = {0, sd_ac, "SEND_STATUS", sd_cmd_SEND_STATUS},
+ [14] = {0, sd_adtc, "BUSTEST_R", sd_cmd_unimplemented},
+ [15] = {0, sd_ac, "GO_INACTIVE_STATE", sd_cmd_GO_INACTIVE_STATE},
+ [16] = {2, sd_ac, "SET_BLOCKLEN", sd_cmd_SET_BLOCKLEN},
+ [17] = {2, sd_adtc, "READ_SINGLE_BLOCK", sd_cmd_READ_SINGLE_BLOCK},
+ [19] = {0, sd_adtc, "BUSTEST_W", sd_cmd_unimplemented},
+ [20] = {3, sd_adtc, "WRITE_DAT_UNTIL_STOP", sd_cmd_unimplemented},
+ [23] = {2, sd_ac, "SET_BLOCK_COUNT", sd_cmd_SET_BLOCK_COUNT},
+ [24] = {4, sd_adtc, "WRITE_SINGLE_BLOCK", sd_cmd_WRITE_SINGLE_BLOCK},
+ [26] = {4, sd_adtc, "PROGRAM_CID", emmc_cmd_PROGRAM_CID},
+ [27] = {4, sd_adtc, "PROGRAM_CSD", sd_cmd_PROGRAM_CSD},
+ [28] = {6, sd_ac, "SET_WRITE_PROT", sd_cmd_SET_WRITE_PROT},
+ [29] = {6, sd_ac, "CLR_WRITE_PROT", sd_cmd_CLR_WRITE_PROT},
+ [30] = {6, sd_adtc, "SEND_WRITE_PROT", sd_cmd_SEND_WRITE_PROT},
+ [31] = {6, sd_adtc, "SEND_WRITE_PROT_TYPE", sd_cmd_unimplemented},
+ [35] = {5, sd_ac, "ERASE_WR_BLK_START", sd_cmd_ERASE_WR_BLK_START},
+ [36] = {5, sd_ac, "ERASE_WR_BLK_END", sd_cmd_ERASE_WR_BLK_END},
+ [38] = {5, sd_ac, "ERASE", sd_cmd_ERASE},
+ [39] = {9, sd_ac, "FAST_IO", sd_cmd_unimplemented},
+ [40] = {9, sd_bcr, "GO_IRQ_STATE", sd_cmd_unimplemented},
+ [42] = {7, sd_adtc, "LOCK_UNLOCK", sd_cmd_LOCK_UNLOCK},
+ [49] = {0, sd_adtc, "SET_TIME", sd_cmd_unimplemented},
+ [55] = {8, sd_ac, "APP_CMD", sd_cmd_APP_CMD},
+ [56] = {8, sd_adtc, "GEN_CMD", sd_cmd_GEN_CMD},
},
};
static void sd_instance_init(Object *obj)
{
- SDState *sd = SD_CARD(obj);
+ SDState *sd = SDMMC_COMMON(obj);
+ SDCardClass *sc = SDMMC_COMMON_GET_CLASS(sd);
- sd->enable = true;
+ sd->proto = sc->proto;
+ sd->last_cmd_name = "UNSET";
sd->ocr_power_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sd_ocr_powerup, sd);
}
static void sd_instance_finalize(Object *obj)
{
- SDState *sd = SD_CARD(obj);
+ SDState *sd = SDMMC_COMMON(obj);
timer_free(sd->ocr_power_timer);
}
static void sd_realize(DeviceState *dev, Error **errp)
{
- SDState *sd = SD_CARD(dev);
+ SDState *sd = SDMMC_COMMON(dev);
int ret;
switch (sd->spec_version) {
@@ -2241,26 +2728,37 @@ static void sd_realize(DeviceState *dev, Error **errp)
}
}
-static Property sd_properties[] = {
- DEFINE_PROP_UINT8("spec_version", SDState,
- spec_version, SD_PHY_SPECv2_00_VERS),
+static void emmc_realize(DeviceState *dev, Error **errp)
+{
+ SDState *sd = SDMMC_COMMON(dev);
+
+ sd->spec_version = SD_PHY_SPECv3_01_VERS; /* Actually v4.3 */
+
+ sd_realize(dev, errp);
+}
+
+static const Property sdmmc_common_properties[] = {
DEFINE_PROP_DRIVE("drive", SDState, blk),
- /* We do not model the chip select pin, so allow the board to select
- * whether card should be in SSI or MMC/SD mode. It is also up to the
- * board to ensure that ssi transfers only occur when the chip select
- * is asserted. */
- DEFINE_PROP_END_OF_LIST()
};
-static void sd_class_init(ObjectClass *klass, void *data)
+static const Property sd_properties[] = {
+ DEFINE_PROP_UINT8("spec_version", SDState,
+ spec_version, SD_PHY_SPECv3_01_VERS),
+};
+
+static const Property emmc_properties[] = {
+ DEFINE_PROP_UINT64("boot-partition-size", SDState, boot_part_size, 0),
+ DEFINE_PROP_UINT8("boot-config", SDState, boot_config, 0x0),
+};
+
+static void sdmmc_common_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SDCardClass *sc = SD_CARD_CLASS(klass);
+ SDCardClass *sc = SDMMC_COMMON_CLASS(klass);
- dc->realize = sd_realize;
- device_class_set_props(dc, sd_properties);
+ device_class_set_props(dc, sdmmc_common_properties);
dc->vmsd = &sd_vmstate;
- dc->reset = sd_reset;
+ device_class_set_legacy_reset(dc, sd_reset);
dc->bus_type = TYPE_SD_BUS;
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
@@ -2272,9 +2770,20 @@ static void sd_class_init(ObjectClass *klass, void *data)
sc->read_byte = sd_read_byte;
sc->receive_ready = sd_receive_ready;
sc->data_ready = sd_data_ready;
- sc->enable = sd_enable;
sc->get_inserted = sd_get_inserted;
sc->get_readonly = sd_get_readonly;
+}
+
+static void sd_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SDCardClass *sc = SDMMC_COMMON_CLASS(klass);
+
+ dc->realize = sd_realize;
+ device_class_set_props(dc, sd_properties);
+
+ sc->set_cid = sd_set_cid;
+ sc->set_csd = sd_set_csd;
sc->proto = &sd_proto_sd;
}
@@ -2284,30 +2793,58 @@ static void sd_class_init(ObjectClass *klass, void *data)
* board to ensure that ssi transfers only occur when the chip select
* is asserted.
*/
-static void sd_spi_class_init(ObjectClass *klass, void *data)
+static void sd_spi_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SDCardClass *sc = SD_CARD_CLASS(klass);
+ SDCardClass *sc = SDMMC_COMMON_CLASS(klass);
dc->desc = "SD SPI";
sc->proto = &sd_proto_spi;
}
+static void emmc_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SDCardClass *sc = SDMMC_COMMON_CLASS(klass);
+
+ dc->desc = "eMMC";
+ dc->realize = emmc_realize;
+ device_class_set_props(dc, emmc_properties);
+ /* Reason: Soldered on board */
+ dc->user_creatable = false;
+
+ sc->proto = &sd_proto_emmc;
+
+ sc->set_cid = emmc_set_cid;
+ sc->set_csd = emmc_set_csd;
+}
+
static const TypeInfo sd_types[] = {
{
- .name = TYPE_SD_CARD,
+ .name = TYPE_SDMMC_COMMON,
.parent = TYPE_DEVICE,
+ .abstract = true,
.instance_size = sizeof(SDState),
.class_size = sizeof(SDCardClass),
- .class_init = sd_class_init,
+ .class_init = sdmmc_common_class_init,
.instance_init = sd_instance_init,
.instance_finalize = sd_instance_finalize,
},
{
+ .name = TYPE_SD_CARD,
+ .parent = TYPE_SDMMC_COMMON,
+ .class_init = sd_class_init,
+ },
+ {
.name = TYPE_SD_CARD_SPI,
.parent = TYPE_SD_CARD,
.class_init = sd_spi_class_init,
},
+ {
+ .name = TYPE_EMMC,
+ .parent = TYPE_SDMMC_COMMON,
+ .class_init = emmc_class_init,
+ },
};
DEFINE_TYPES(sd_types)
diff --git a/hw/sd/sdhci-internal.h b/hw/sd/sdhci-internal.h
index 5f3765f..9f768c4 100644
--- a/hw/sd/sdhci-internal.h
+++ b/hw/sd/sdhci-internal.h
@@ -322,6 +322,6 @@ void sdhci_initfn(SDHCIState *s);
void sdhci_uninitfn(SDHCIState *s);
void sdhci_common_realize(SDHCIState *s, Error **errp);
void sdhci_common_unrealize(SDHCIState *s);
-void sdhci_common_class_init(ObjectClass *klass, void *data);
+void sdhci_common_class_init(ObjectClass *klass, const void *data);
#endif
diff --git a/hw/sd/sdhci-pci.c b/hw/sd/sdhci-pci.c
index 9b7bee8..c18b91f 100644
--- a/hw/sd/sdhci-pci.c
+++ b/hw/sd/sdhci-pci.c
@@ -18,13 +18,13 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qemu/module.h"
+#include "hw/irq.h"
#include "hw/qdev-properties.h"
#include "hw/sd/sdhci.h"
#include "sdhci-internal.h"
-static Property sdhci_pci_properties[] = {
+static const Property sdhci_pci_properties[] = {
DEFINE_SDHCI_COMMON_PROPERTIES(SDHCIState),
- DEFINE_PROP_END_OF_LIST(),
};
static void sdhci_pci_realize(PCIDevice *dev, Error **errp)
@@ -49,11 +49,12 @@ static void sdhci_pci_exit(PCIDevice *dev)
{
SDHCIState *s = PCI_SDHCI(dev);
+ qemu_free_irq(s->irq);
sdhci_common_unrealize(s);
sdhci_uninitfn(s);
}
-static void sdhci_pci_class_init(ObjectClass *klass, void *data)
+static void sdhci_pci_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
@@ -74,7 +75,7 @@ static const TypeInfo sdhci_pci_types[] = {
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(SDHCIState),
.class_init = sdhci_pci_class_init,
- .interfaces = (InterfaceInfo[]) {
+ .interfaces = (const InterfaceInfo[]) {
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
{ },
},
diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c
index 27673e1..226ff13 100644
--- a/hw/sd/sdhci.c
+++ b/hw/sd/sdhci.c
@@ -30,14 +30,13 @@
#include "qapi/error.h"
#include "hw/irq.h"
#include "hw/qdev-properties.h"
-#include "sysemu/dma.h"
+#include "system/dma.h"
#include "qemu/timer.h"
#include "qemu/bitops.h"
#include "hw/sd/sdhci.h"
#include "migration/vmstate.h"
#include "sdhci-internal.h"
#include "qemu/log.h"
-#include "qemu/module.h"
#include "trace.h"
#include "qom/object.h"
@@ -234,7 +233,7 @@ static void sdhci_raise_insertion_irq(void *opaque)
if (s->norintsts & SDHC_NIS_REMOVE) {
timer_mod(s->insert_timer,
- qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + SDHC_INSERTION_DELAY);
+ qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + SDHC_INSERTION_DELAY);
} else {
s->prnsts = 0x1ff0000;
if (s->norintstsen & SDHC_NISEN_INSERT) {
@@ -252,7 +251,7 @@ static void sdhci_set_inserted(DeviceState *dev, bool level)
if ((s->norintsts & SDHC_NIS_REMOVE) && level) {
/* Give target some time to notice card ejection */
timer_mod(s->insert_timer,
- qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + SDHC_INSERTION_DELAY);
+ qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + SDHC_INSERTION_DELAY);
} else {
if (level) {
s->prnsts = 0x1ff0000;
@@ -275,6 +274,10 @@ static void sdhci_set_readonly(DeviceState *dev, bool level)
{
SDHCIState *s = (SDHCIState *)dev;
+ if (s->wp_inverted) {
+ level = !level;
+ }
+
if (level) {
s->prnsts &= ~SDHC_WRITE_PROTECT;
} else {
@@ -290,9 +293,11 @@ static void sdhci_reset(SDHCIState *s)
timer_del(s->insert_timer);
timer_del(s->transfer_timer);
- /* Set all registers to 0. Capabilities/Version registers are not cleared
+ /*
+ * Set all registers to 0. Capabilities/Version registers are not cleared
* and assumed to always preserve their value, given to them during
- * initialization */
+ * initialization
+ */
memset(&s->sdmasysad, 0, (uintptr_t)&s->capareg - (uintptr_t)&s->sdmasysad);
/* Reset other state based on current card insertion/readonly status */
@@ -302,11 +307,16 @@ static void sdhci_reset(SDHCIState *s)
s->data_count = 0;
s->stopped_state = sdhc_not_stopped;
s->pending_insert_state = false;
+ if (s->vendor == SDHCI_VENDOR_FSL) {
+ s->norintstsen = 0x013f;
+ s->errintstsen = 0x117f;
+ }
}
static void sdhci_poweron_reset(DeviceState *dev)
{
- /* QOM (ie power-on) reset. This is identical to reset
+ /*
+ * QOM (ie power-on) reset. This is identical to reset
* commanded via device register apart from handling of the
* 'pending insert on powerup' quirk.
*/
@@ -446,8 +456,10 @@ static void sdhci_read_block_from_card(SDHCIState *s)
s->prnsts &= ~SDHC_DAT_LINE_ACTIVE;
}
- /* If stop at block gap request was set and it's not the last block of
- * data - generate Block Event interrupt */
+ /*
+ * If stop at block gap request was set and it's not the last block of
+ * data - generate Block Event interrupt
+ */
if (s->stopped_state == sdhc_gap_read && (s->trnmod & SDHC_TRNS_MULTI) &&
s->blkcnt != 1) {
s->prnsts &= ~SDHC_DAT_LINE_ACTIVE;
@@ -549,8 +561,10 @@ static void sdhci_write_block_to_card(SDHCIState *s)
sdhci_update_irq(s);
}
-/* Write @size bytes of @value data to host controller @s Buffer Data Port
- * register */
+/*
+ * Write @size bytes of @value data to host controller @s Buffer Data Port
+ * register
+ */
static void sdhci_write_dataport(SDHCIState *s, uint32_t value, unsigned size)
{
unsigned i;
@@ -595,9 +609,11 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
return;
}
- /* XXX: Some sd/mmc drivers (for example, u-boot-slp) do not account for
+ /*
+ * XXX: Some sd/mmc drivers (for example, u-boot-slp) do not account for
* possible stop at page boundary if initial address is not page aligned,
- * allow them to work properly */
+ * allow them to work properly
+ */
if ((s->sdmasysad % boundary_chk) == 0) {
page_aligned = true;
}
@@ -657,12 +673,13 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
}
}
+ if (s->norintstsen & SDHC_NISEN_DMA) {
+ s->norintsts |= SDHC_NIS_DMA;
+ }
+
if (s->blkcnt == 0) {
sdhci_end_transfer(s);
} else {
- if (s->norintstsen & SDHC_NISEN_DMA) {
- s->norintsts |= SDHC_NIS_DMA;
- }
sdhci_update_irq(s);
}
}
@@ -683,9 +700,22 @@ static void sdhci_sdma_transfer_single_block(SDHCIState *s)
}
s->blkcnt--;
+ if (s->norintstsen & SDHC_NISEN_DMA) {
+ s->norintsts |= SDHC_NIS_DMA;
+ }
+
sdhci_end_transfer(s);
}
+static void sdhci_sdma_transfer(SDHCIState *s)
+{
+ if ((s->blkcnt == 1) || !(s->trnmod & SDHC_TRNS_MULTI)) {
+ sdhci_sdma_transfer_single_block(s);
+ } else {
+ sdhci_sdma_transfer_multi_blocks(s);
+ }
+}
+
typedef struct ADMADescr {
hwaddr addr;
uint16_t length;
@@ -703,7 +733,8 @@ static void get_adma_description(SDHCIState *s, ADMADescr *dscr)
dma_memory_read(s->dma_as, entry_addr, &adma2, sizeof(adma2),
MEMTXATTRS_UNSPECIFIED);
adma2 = le64_to_cpu(adma2);
- /* The spec does not specify endianness of descriptor table.
+ /*
+ * The spec does not specify endianness of descriptor table.
* We currently assume that it is LE.
*/
dscr->addr = (hwaddr)extract64(adma2, 32, 32) & ~0x3ull;
@@ -747,7 +778,7 @@ static void sdhci_do_adma(SDHCIState *s)
const uint16_t block_size = s->blksize & BLOCK_SIZE_MASK;
const MemTxAttrs attrs = { .memory = true };
ADMADescr dscr = {};
- MemTxResult res;
+ MemTxResult res = MEMTX_ERROR;
int i;
if (s->trnmod & SDHC_TRNS_BLK_CNT_EN && !s->blkcnt) {
@@ -846,6 +877,7 @@ static void sdhci_do_adma(SDHCIState *s)
}
}
if (res != MEMTX_OK) {
+ s->data_count = 0;
if (s->errintstsen & SDHC_EISEN_ADMAERR) {
trace_sdhci_error("Set ADMA error flag");
s->errintsts |= SDHC_EIS_ADMAERR;
@@ -915,12 +947,7 @@ static void sdhci_data_transfer(void *opaque)
if (s->trnmod & SDHC_TRNS_DMA) {
switch (SDHC_DMA_TYPE(s->hostctl1)) {
case SDHC_CTRL_SDMA:
- if ((s->blkcnt == 1) || !(s->trnmod & SDHC_TRNS_MULTI)) {
- sdhci_sdma_transfer_single_block(s);
- } else {
- sdhci_sdma_transfer_multi_blocks(s);
- }
-
+ sdhci_sdma_transfer(s);
break;
case SDHC_CTRL_ADMA1_32:
if (!(s->capareg & R_SDHC_CAPAB_ADMA1_MASK)) {
@@ -977,14 +1004,17 @@ static bool sdhci_can_issue_command(SDHCIState *s)
return true;
}
-/* The Buffer Data Port register must be accessed in sequential and
- * continuous manner */
+/*
+ * The Buffer Data Port register must be accessed in sequential and
+ * continuous manner
+ */
static inline bool
sdhci_buff_access_is_sequential(SDHCIState *s, unsigned byte_num)
{
if ((s->data_count & 0x3) != byte_num) {
- trace_sdhci_error("Non-sequential access to Buffer Data Port register"
- "is prohibited\n");
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "SDHCI: Non-sequential access to Buffer Data Port"
+ " register is prohibited\n");
return false;
}
return true;
@@ -1161,11 +1191,7 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
if (!(mask & 0xFF000000) && s->blkcnt &&
(s->blksize & BLOCK_SIZE_MASK) &&
SDHC_DMA_TYPE(s->hostctl1) == SDHC_CTRL_SDMA) {
- if (s->trnmod & SDHC_TRNS_MULTI) {
- sdhci_sdma_transfer_multi_blocks(s);
- } else {
- sdhci_sdma_transfer_single_block(s);
- }
+ sdhci_sdma_transfer(s);
}
}
break;
@@ -1205,8 +1231,10 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
MASKED_WRITE(s->argument, mask, value);
break;
case SDHC_TRNMOD:
- /* DMA can be enabled only if it is supported as indicated by
- * capabilities register */
+ /*
+ * DMA can be enabled only if it is supported as indicated by
+ * capabilities register
+ */
if (!(s->capareg & R_SDHC_CAPAB_SDMA_MASK)) {
value &= ~SDHC_TRNS_DMA;
}
@@ -1278,8 +1306,10 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
} else {
s->norintsts &= ~SDHC_NIS_ERR;
}
- /* Quirk for Raspberry Pi: pending card insert interrupt
- * appears when first enabled after power on */
+ /*
+ * Quirk for Raspberry Pi: pending card insert interrupt
+ * appears when first enabled after power on
+ */
if ((s->norintstsen & SDHC_NISEN_INSERT) && s->pending_insert_state) {
assert(s->pending_insert_quirk);
s->norintsts |= SDHC_NIS_INSERT;
@@ -1395,8 +1425,10 @@ void sdhci_initfn(SDHCIState *s)
{
qbus_init(&s->sdbus, sizeof(s->sdbus), TYPE_SDHCI_BUS, DEVICE(s), "sd-bus");
- s->insert_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sdhci_raise_insertion_irq, s);
- s->transfer_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sdhci_data_transfer, s);
+ s->insert_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+ sdhci_raise_insertion_irq, s);
+ s->transfer_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+ sdhci_data_transfer, s);
s->io_ops = &sdhci_mmio_le_ops;
}
@@ -1444,11 +1476,13 @@ void sdhci_common_realize(SDHCIState *s, Error **errp)
void sdhci_common_unrealize(SDHCIState *s)
{
- /* This function is expected to be called only once for each class:
+ /*
+ * This function is expected to be called only once for each class:
* - SysBus: via DeviceClass->unrealize(),
* - PCI: via PCIDeviceClass->exit().
* However to avoid double-free and/or use-after-free we still nullify
- * this variable (better safe than sorry!). */
+ * this variable (better safe than sorry!).
+ */
g_free(s->fifo_buffer);
s->fifo_buffer = NULL;
}
@@ -1512,24 +1546,25 @@ const VMStateDescription sdhci_vmstate = {
},
};
-void sdhci_common_class_init(ObjectClass *klass, void *data)
+void sdhci_common_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
dc->vmsd = &sdhci_vmstate;
- dc->reset = sdhci_poweron_reset;
+ device_class_set_legacy_reset(dc, sdhci_poweron_reset);
}
/* --- qdev SysBus --- */
-static Property sdhci_sysbus_properties[] = {
+static const Property sdhci_sysbus_properties[] = {
DEFINE_SDHCI_COMMON_PROPERTIES(SDHCIState),
DEFINE_PROP_BOOL("pending-insert-quirk", SDHCIState, pending_insert_quirk,
false),
DEFINE_PROP_LINK("dma", SDHCIState,
dma_mr, TYPE_MEMORY_REGION, MemoryRegion *),
- DEFINE_PROP_END_OF_LIST(),
+ DEFINE_PROP_BOOL("wp-inverted", SDHCIState,
+ wp_inverted, false),
};
static void sdhci_sysbus_init(Object *obj)
@@ -1585,7 +1620,7 @@ static void sdhci_sysbus_unrealize(DeviceState *dev)
}
}
-static void sdhci_sysbus_class_init(ObjectClass *klass, void *data)
+static void sdhci_sysbus_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -1596,18 +1631,9 @@ static void sdhci_sysbus_class_init(ObjectClass *klass, void *data)
sdhci_common_class_init(klass, data);
}
-static const TypeInfo sdhci_sysbus_info = {
- .name = TYPE_SYSBUS_SDHCI,
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(SDHCIState),
- .instance_init = sdhci_sysbus_init,
- .instance_finalize = sdhci_sysbus_finalize,
- .class_init = sdhci_sysbus_class_init,
-};
-
/* --- qdev bus master --- */
-static void sdhci_bus_class_init(ObjectClass *klass, void *data)
+static void sdhci_bus_class_init(ObjectClass *klass, const void *data)
{
SDBusClass *sbc = SD_BUS_CLASS(klass);
@@ -1615,13 +1641,6 @@ static void sdhci_bus_class_init(ObjectClass *klass, void *data)
sbc->set_readonly = sdhci_set_readonly;
}
-static const TypeInfo sdhci_bus_info = {
- .name = TYPE_SDHCI_BUS,
- .parent = TYPE_SD_BUS,
- .instance_size = sizeof(SDBus),
- .class_init = sdhci_bus_class_init,
-};
-
/* --- qdev i.MX eSDHC --- */
#define USDHC_MIX_CTRL 0x48
@@ -1716,16 +1735,10 @@ usdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
case USDHC_VENDOR_SPEC:
s->vendor_spec = value;
- switch (s->vendor) {
- case SDHCI_VENDOR_IMX:
- if (value & USDHC_IMX_FRC_SDCLK_ON) {
- s->prnsts &= ~SDHC_IMX_CLOCK_GATE_OFF;
- } else {
- s->prnsts |= SDHC_IMX_CLOCK_GATE_OFF;
- }
- break;
- default:
- break;
+ if (value & USDHC_IMX_FRC_SDCLK_ON) {
+ s->prnsts &= ~SDHC_IMX_CLOCK_GATE_OFF;
+ } else {
+ s->prnsts |= SDHC_IMX_CLOCK_GATE_OFF;
}
break;
@@ -1880,12 +1893,6 @@ static void imx_usdhc_init(Object *obj)
s->quirks = SDHCI_QUIRK_NO_BUSY_IRQ;
}
-static const TypeInfo imx_usdhc_info = {
- .name = TYPE_IMX_USDHC,
- .parent = TYPE_SYSBUS_SDHCI,
- .instance_init = imx_usdhc_init,
-};
-
/* --- qdev Samsung s3c --- */
#define S3C_SDHCI_CONTROL2 0x80
@@ -1944,18 +1951,31 @@ static void sdhci_s3c_init(Object *obj)
s->io_ops = &sdhci_s3c_mmio_ops;
}
-static const TypeInfo sdhci_s3c_info = {
- .name = TYPE_S3C_SDHCI ,
- .parent = TYPE_SYSBUS_SDHCI,
- .instance_init = sdhci_s3c_init,
+static const TypeInfo sdhci_types[] = {
+ {
+ .name = TYPE_SDHCI_BUS,
+ .parent = TYPE_SD_BUS,
+ .instance_size = sizeof(SDBus),
+ .class_init = sdhci_bus_class_init,
+ },
+ {
+ .name = TYPE_SYSBUS_SDHCI,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SDHCIState),
+ .instance_init = sdhci_sysbus_init,
+ .instance_finalize = sdhci_sysbus_finalize,
+ .class_init = sdhci_sysbus_class_init,
+ },
+ {
+ .name = TYPE_IMX_USDHC,
+ .parent = TYPE_SYSBUS_SDHCI,
+ .instance_init = imx_usdhc_init,
+ },
+ {
+ .name = TYPE_S3C_SDHCI,
+ .parent = TYPE_SYSBUS_SDHCI,
+ .instance_init = sdhci_s3c_init,
+ },
};
-static void sdhci_register_types(void)
-{
- type_register_static(&sdhci_sysbus_info);
- type_register_static(&sdhci_bus_info);
- type_register_static(&imx_usdhc_info);
- type_register_static(&sdhci_s3c_info);
-}
-
-type_init(sdhci_register_types)
+DEFINE_TYPES(sdhci_types)
diff --git a/hw/sd/sdmmc-internal.c b/hw/sd/sdmmc-internal.c
deleted file mode 100644
index 8648a78..0000000
--- a/hw/sd/sdmmc-internal.c
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * SD/MMC cards common helpers
- *
- * Copyright (c) 2018 Philippe Mathieu-Daudé <f4bug@amsat.org>
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- * SPDX-License-Identifier: GPL-2.0-or-later
- */
-
-#include "qemu/osdep.h"
-#include "sdmmc-internal.h"
-
-const char *sd_cmd_name(uint8_t cmd)
-{
- static const char *cmd_abbrev[SDMMC_CMD_MAX] = {
- [0] = "GO_IDLE_STATE", [1] = "SEND_OP_CMD",
- [2] = "ALL_SEND_CID", [3] = "SEND_RELATIVE_ADDR",
- [4] = "SET_DSR", [5] = "IO_SEND_OP_COND",
- [6] = "SWITCH_FUNC", [7] = "SELECT/DESELECT_CARD",
- [8] = "SEND_IF_COND", [9] = "SEND_CSD",
- [10] = "SEND_CID", [11] = "VOLTAGE_SWITCH",
- [12] = "STOP_TRANSMISSION", [13] = "SEND_STATUS",
- [15] = "GO_INACTIVE_STATE",
- [16] = "SET_BLOCKLEN", [17] = "READ_SINGLE_BLOCK",
- [18] = "READ_MULTIPLE_BLOCK", [19] = "SEND_TUNING_BLOCK",
- [20] = "SPEED_CLASS_CONTROL", [21] = "DPS_spec",
- [23] = "SET_BLOCK_COUNT",
- [24] = "WRITE_BLOCK", [25] = "WRITE_MULTIPLE_BLOCK",
- [26] = "MANUF_RSVD", [27] = "PROGRAM_CSD",
- [28] = "SET_WRITE_PROT", [29] = "CLR_WRITE_PROT",
- [30] = "SEND_WRITE_PROT",
- [32] = "ERASE_WR_BLK_START", [33] = "ERASE_WR_BLK_END",
- [34] = "SW_FUNC_RSVD", [35] = "SW_FUNC_RSVD",
- [36] = "SW_FUNC_RSVD", [37] = "SW_FUNC_RSVD",
- [38] = "ERASE",
- [40] = "DPS_spec",
- [42] = "LOCK_UNLOCK", [43] = "Q_MANAGEMENT",
- [44] = "Q_TASK_INFO_A", [45] = "Q_TASK_INFO_B",
- [46] = "Q_RD_TASK", [47] = "Q_WR_TASK",
- [48] = "READ_EXTR_SINGLE", [49] = "WRITE_EXTR_SINGLE",
- [50] = "SW_FUNC_RSVD",
- [52] = "IO_RW_DIRECT", [53] = "IO_RW_EXTENDED",
- [54] = "SDIO_RSVD", [55] = "APP_CMD",
- [56] = "GEN_CMD", [57] = "SW_FUNC_RSVD",
- [58] = "READ_EXTR_MULTI", [59] = "WRITE_EXTR_MULTI",
- [60] = "MANUF_RSVD", [61] = "MANUF_RSVD",
- [62] = "MANUF_RSVD", [63] = "MANUF_RSVD",
- };
- return cmd_abbrev[cmd] ? cmd_abbrev[cmd] : "UNKNOWN_CMD";
-}
-
-const char *sd_acmd_name(uint8_t cmd)
-{
- static const char *acmd_abbrev[SDMMC_CMD_MAX] = {
- [6] = "SET_BUS_WIDTH",
- [13] = "SD_STATUS",
- [14] = "DPS_spec", [15] = "DPS_spec",
- [16] = "DPS_spec",
- [18] = "SECU_spec",
- [22] = "SEND_NUM_WR_BLOCKS", [23] = "SET_WR_BLK_ERASE_COUNT",
- [41] = "SD_SEND_OP_COND",
- [42] = "SET_CLR_CARD_DETECT",
- [51] = "SEND_SCR",
- [52] = "SECU_spec", [53] = "SECU_spec",
- [54] = "SECU_spec",
- [56] = "SECU_spec", [57] = "SECU_spec",
- [58] = "SECU_spec", [59] = "SECU_spec",
- };
-
- return acmd_abbrev[cmd] ? acmd_abbrev[cmd] : "UNKNOWN_ACMD";
-}
diff --git a/hw/sd/sdmmc-internal.h b/hw/sd/sdmmc-internal.h
index d8bf17d..91eb5b6 100644
--- a/hw/sd/sdmmc-internal.h
+++ b/hw/sd/sdmmc-internal.h
@@ -11,30 +11,115 @@
#ifndef SDMMC_INTERNAL_H
#define SDMMC_INTERNAL_H
-#define SDMMC_CMD_MAX 64
+#define TYPE_SDMMC_COMMON "sdmmc-common"
+DECLARE_OBJ_CHECKERS(SDState, SDCardClass, SDMMC_COMMON, TYPE_SDMMC_COMMON)
-/**
- * sd_cmd_name:
- * @cmd: A SD "normal" command, up to SDMMC_CMD_MAX.
- *
- * Returns a human-readable name describing the command.
- * The return value is always a static string which does not need
- * to be freed after use.
+/*
+ * EXT_CSD Modes segment
*
- * Returns: The command name of @cmd or "UNKNOWN_CMD".
+ * Define the configuration the Device is working in.
+ * These modes can be changed by the host by means of the SWITCH command.
*/
-const char *sd_cmd_name(uint8_t cmd);
-
-/**
- * sd_acmd_name:
- * @cmd: A SD "Application-Specific" command, up to SDMMC_CMD_MAX.
- *
- * Returns a human-readable name describing the application command.
- * The return value is always a static string which does not need
- * to be freed after use.
+#define EXT_CSD_CMDQ_MODE_EN 15 /* R/W */
+#define EXT_CSD_FLUSH_CACHE 32 /* W */
+#define EXT_CSD_CACHE_CTRL 33 /* R/W */
+#define EXT_CSD_POWER_OFF_NOTIFICATION 34 /* R/W */
+#define EXT_CSD_PACKED_FAILURE_INDEX 35 /* RO */
+#define EXT_CSD_PACKED_CMD_STATUS 36 /* RO */
+#define EXT_CSD_EXP_EVENTS_STATUS 54 /* RO, 2 bytes */
+#define EXT_CSD_EXP_EVENTS_CTRL 56 /* R/W, 2 bytes */
+#define EXT_CSD_CLASS_6_CTRL 59
+#define EXT_CSD_INI_TIMEOUT_EMU 60
+#define EXT_CSD_DATA_SECTOR_SIZE 61 /* R */
+#define EXT_CSD_USE_NATIVE_SECTOR 62
+#define EXT_CSD_NATIVE_SECTOR_SIZE 63
+#define EXT_CSD_VENDOR_SPECIFIC_FIELD 64 /* 64 bytes */
+#define EXT_CSD_PROGRAM_CID_CSD_DDR_SUPPORT 130
+#define EXT_CSD_PERIODIC_WAKEUP 131
+#define EXT_CSD_TCASE_SUPPORT 132
+#define EXT_CSD_SEC_BAD_BLK_MGMNT 134
+#define EXT_CSD_GP_SIZE_MULT 143 /* R/W */
+#define EXT_CSD_PARTITION_SETTING_COMPLETED 155 /* R/W */
+#define EXT_CSD_PARTITION_ATTRIBUTE 156 /* R/W */
+#define EXT_CSD_MAX_ENH_SIZE_MULT 157 /* RO, 3 bytes */
+#define EXT_CSD_PARTITION_SUPPORT 160 /* RO */
+#define EXT_CSD_HPI_MGMT 161 /* R/W */
+#define EXT_CSD_RST_N_FUNCTION 162 /* R/W */
+#define EXT_CSD_BKOPS_EN 163 /* R/W */
+#define EXT_CSD_BKOPS_START 164 /* W */
+#define EXT_CSD_SANITIZE_START 165 /* W */
+#define EXT_CSD_WR_REL_PARAM 166 /* RO */
+#define EXT_CSD_WR_REL_SET 167
+#define EXT_CSD_RPMB_MULT 168 /* RO */
+#define EXT_CSD_FW_CONFIG 169 /* R/W */
+#define EXT_CSD_USER_WP 171
+#define EXT_CSD_BOOT_WP 173 /* R/W */
+#define EXT_CSD_BOOT_WP_STATUS 174
+#define EXT_CSD_ERASE_GROUP_DEF 175 /* R/W */
+#define EXT_CSD_BOOT_BUS_CONDITIONS 177
+#define EXT_CSD_BOOT_CONFIG_PROT 178
+#define EXT_CSD_PART_CONFIG 179 /* R/W */
+#define EXT_CSD_ERASED_MEM_CONT 181 /* RO */
+#define EXT_CSD_BUS_WIDTH 183 /* R/W */
+#define EXT_CSD_STROBE_SUPPORT 184 /* RO */
+#define EXT_CSD_HS_TIMING 185 /* R/W */
+#define EXT_CSD_POWER_CLASS 187 /* R/W */
+#define EXT_CSD_CMD_SET_REV 189
+#define EXT_CSD_CMD_SET 191
+/*
+ * EXT_CSD Properties segment
*
- * Returns: The application command name of @cmd or "UNKNOWN_ACMD".
+ * Define the Device capabilities, cannot be modified by the host.
*/
-const char *sd_acmd_name(uint8_t cmd);
+#define EXT_CSD_REV 192
+#define EXT_CSD_STRUCTURE 194
+#define EXT_CSD_CARD_TYPE 196
+#define EXT_CSD_DRIVER_STRENGTH 197
+#define EXT_CSD_OUT_OF_INTERRUPT_TIME 198
+#define EXT_CSD_PART_SWITCH_TIME 199
+#define EXT_CSD_PWR_CL_52_195 200
+#define EXT_CSD_PWR_CL_26_195 201
+#define EXT_CSD_PWR_CL_52_360 202
+#define EXT_CSD_PWR_CL_26_360 203
+#define EXT_CSD_SEC_CNT 212 /* 4 bytes */
+#define EXT_CSD_S_A_TIMEOUT 217
+#define EXT_CSD_S_C_VCCQ 219
+#define EXT_CSD_S_C_VCC 220
+#define EXT_CSD_REL_WR_SEC_C 222
+#define EXT_CSD_HC_WP_GRP_SIZE 221
+#define EXT_CSD_ERASE_TIMEOUT_MULT 223
+#define EXT_CSD_HC_ERASE_GRP_SIZE 224
+#define EXT_CSD_ACC_SIZE 225
+#define EXT_CSD_BOOT_MULT 226
+#define EXT_CSD_BOOT_INFO 228
+#define EXT_CSD_SEC_FEATURE_SUPPORT 231
+#define EXT_CSD_TRIM_MULT 232
+#define EXT_CSD_INI_TIMEOUT_PA 241
+#define EXT_CSD_BKOPS_STATUS 246
+#define EXT_CSD_POWER_OFF_LONG_TIME 247
+#define EXT_CSD_GENERIC_CMD6_TIME 248
+#define EXT_CSD_CACHE_SIZE 249 /* 4 bytes */
+#define EXT_CSD_EXT_SUPPORT 494
+#define EXT_CSD_LARGE_UNIT_SIZE_M1 495
+#define EXT_CSD_CONTEXT_CAPABILITIES 496
+#define EXT_CSD_TAG_RES_SIZE 497
+#define EXT_CSD_TAG_UNIT_SIZE 498
+#define EXT_CSD_DATA_TAG_SUPPORT 499
+#define EXT_CSD_MAX_PACKED_WRITES 500
+#define EXT_CSD_MAX_PACKED_READS 501
+#define EXT_CSD_BKOPS_SUPPORT 502
+#define EXT_CSD_HPI_FEATURES 503
+#define EXT_CSD_S_CMD_SET 504
+
+#define EXT_CSD_WR_REL_PARAM_EN (1 << 2)
+#define EXT_CSD_WR_REL_PARAM_EN_RPMB_REL_WR (1 << 4)
+
+#define EXT_CSD_PART_CONFIG_ACC_MASK (0x7)
+#define EXT_CSD_PART_CONFIG_ACC_DEFAULT (0x0)
+#define EXT_CSD_PART_CONFIG_ACC_BOOT0 (0x1)
+
+#define EXT_CSD_PART_CONFIG_EN_MASK (0x7 << 3)
+#define EXT_CSD_PART_CONFIG_EN_BOOT0 (0x1 << 3)
+#define EXT_CSD_PART_CONFIG_EN_USER (0x7 << 3)
#endif
diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c
index 2dd070f..6c90a86 100644
--- a/hw/sd/ssi-sd.c
+++ b/hw/sd/ssi-sd.c
@@ -16,7 +16,7 @@
*/
#include "qemu/osdep.h"
-#include "sysemu/blockdev.h"
+#include "system/blockdev.h"
#include "hw/ssi/ssi.h"
#include "migration/vmstate.h"
#include "hw/qdev-properties.h"
@@ -389,7 +389,7 @@ static void ssi_sd_reset(DeviceState *dev)
s->stopping = 0;
}
-static void ssi_sd_class_init(ObjectClass *klass, void *data)
+static void ssi_sd_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass);
@@ -398,7 +398,7 @@ static void ssi_sd_class_init(ObjectClass *klass, void *data)
k->transfer = ssi_sd_transfer;
k->cs_polarity = SSI_CS_LOW;
dc->vmsd = &vmstate_ssi_sd;
- dc->reset = ssi_sd_reset;
+ device_class_set_legacy_reset(dc, ssi_sd_reset);
/* Reason: GPIO chip-select line should be wired up */
dc->user_creatable = false;
}
diff --git a/hw/sd/trace-events b/hw/sd/trace-events
index 94a0055..db06442 100644
--- a/hw/sd/trace-events
+++ b/hw/sd/trace-events
@@ -43,21 +43,22 @@ sdcard_response(const char *rspdesc, int rsplen) "%s (sz:%d)"
sdcard_powerup(void) ""
sdcard_inquiry_cmd41(void) ""
sdcard_reset(void) ""
-sdcard_set_blocklen(uint16_t length) "0x%03x"
+sdcard_set_rca(uint16_t value) "new RCA: 0x%04x"
+sdcard_set_blocklen(uint16_t length) "block len 0x%03x"
+sdcard_set_block_count(uint32_t cnt) "block cnt 0x%"PRIx32
sdcard_inserted(bool readonly) "read_only: %u"
sdcard_ejected(void) ""
sdcard_erase(uint32_t first, uint32_t last) "addr first 0x%" PRIx32" last 0x%" PRIx32
sdcard_lock(void) ""
sdcard_unlock(void) ""
+sdcard_req_addr(uint32_t req_arg, uint64_t addr) "req 0x%" PRIx32 " addr 0x%" PRIx64
sdcard_read_block(uint64_t addr, uint32_t len) "addr 0x%" PRIx64 " size 0x%x"
sdcard_write_block(uint64_t addr, uint32_t len) "addr 0x%" PRIx64 " size 0x%x"
-sdcard_write_data(const char *proto, const char *cmd_desc, uint8_t cmd, uint8_t value) "%s %20s/ CMD%02d value 0x%02x"
-sdcard_read_data(const char *proto, const char *cmd_desc, uint8_t cmd, uint32_t length) "%s %20s/ CMD%02d len %" PRIu32
+sdcard_write_data(const char *proto, const char *cmd_desc, uint8_t cmd, uint32_t offset, uint8_t value) "%s %20s/ CMD%02d ofs %"PRIu32" value 0x%02x"
+sdcard_read_data(const char *proto, const char *cmd_desc, uint8_t cmd, uint32_t offset, uint64_t size, uint32_t blklen) "%s %20s/ CMD%02d ofs %"PRIu32" size %"PRIu64" blklen %" PRIu32
sdcard_set_voltage(uint16_t millivolts) "%u mV"
-
-# pxa2xx_mmci.c
-pxa2xx_mmci_read(uint8_t size, uint32_t addr, uint32_t value) "size %d addr 0x%02x value 0x%08x"
-pxa2xx_mmci_write(uint8_t size, uint32_t addr, uint32_t value) "size %d addr 0x%02x value 0x%08x"
+sdcard_ext_csd_update(unsigned index, uint8_t oval, uint8_t nval) "index %u: 0x%02x -> 0x%02x"
+sdcard_switch(unsigned access, unsigned index, unsigned value, unsigned set) "SWITCH acc:%u idx:%u val:%u set:%u"
# pl181.c
pl181_command_send(uint8_t cmd, uint32_t arg) "sending CMD%02d arg 0x%08" PRIx32