aboutsummaryrefslogtreecommitdiff
path: root/hw/sd/sdhci.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/sd/sdhci.c')
-rw-r--r--hw/sd/sdhci.c187
1 files changed, 135 insertions, 52 deletions
diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c
index 5b8678110b..c5e0bc018b 100644
--- a/hw/sd/sdhci.c
+++ b/hw/sd/sdhci.c
@@ -321,6 +321,8 @@ static void sdhci_poweron_reset(DeviceState *dev)
static void sdhci_data_transfer(void *opaque);
+#define BLOCK_SIZE_MASK (4 * KiB - 1)
+
static void sdhci_send_command(SDHCIState *s)
{
SDRequest request;
@@ -371,7 +373,8 @@ static void sdhci_send_command(SDHCIState *s)
sdhci_update_irq(s);
- if (!timeout && s->blksize && (s->cmdreg & SDHC_CMD_DATA_PRESENT)) {
+ if (!timeout && (s->blksize & BLOCK_SIZE_MASK) &&
+ (s->cmdreg & SDHC_CMD_DATA_PRESENT)) {
s->data_count = 0;
sdhci_data_transfer(s);
}
@@ -406,7 +409,6 @@ static void sdhci_end_transfer(SDHCIState *s)
/*
* Programmed i/o data transfer
*/
-#define BLOCK_SIZE_MASK (4 * KiB - 1)
/* Fill host controller's read buffer with BLKSIZE bytes of data from card */
static void sdhci_read_block_from_card(SDHCIState *s)
@@ -616,8 +618,8 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
s->blkcnt--;
}
}
- dma_memory_write(s->dma_as, s->sdmasysad,
- &s->fifo_buffer[begin], s->data_count - begin);
+ dma_memory_write(s->dma_as, s->sdmasysad, &s->fifo_buffer[begin],
+ s->data_count - begin, MEMTXATTRS_UNSPECIFIED);
s->sdmasysad += s->data_count - begin;
if (s->data_count == block_size) {
s->data_count = 0;
@@ -637,8 +639,8 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
s->data_count = block_size;
boundary_count -= block_size - begin;
}
- dma_memory_read(s->dma_as, s->sdmasysad,
- &s->fifo_buffer[begin], s->data_count - begin);
+ dma_memory_read(s->dma_as, s->sdmasysad, &s->fifo_buffer[begin],
+ s->data_count - begin, MEMTXATTRS_UNSPECIFIED);
s->sdmasysad += s->data_count - begin;
if (s->data_count == block_size) {
sdbus_write_data(&s->sdbus, s->fifo_buffer, block_size);
@@ -670,9 +672,11 @@ static void sdhci_sdma_transfer_single_block(SDHCIState *s)
if (s->trnmod & SDHC_TRNS_READ) {
sdbus_read_data(&s->sdbus, s->fifo_buffer, datacnt);
- dma_memory_write(s->dma_as, s->sdmasysad, s->fifo_buffer, datacnt);
+ dma_memory_write(s->dma_as, s->sdmasysad, s->fifo_buffer, datacnt,
+ MEMTXATTRS_UNSPECIFIED);
} else {
- dma_memory_read(s->dma_as, s->sdmasysad, s->fifo_buffer, datacnt);
+ dma_memory_read(s->dma_as, s->sdmasysad, s->fifo_buffer, datacnt,
+ MEMTXATTRS_UNSPECIFIED);
sdbus_write_data(&s->sdbus, s->fifo_buffer, datacnt);
}
s->blkcnt--;
@@ -694,7 +698,8 @@ static void get_adma_description(SDHCIState *s, ADMADescr *dscr)
hwaddr entry_addr = (hwaddr)s->admasysaddr;
switch (SDHC_DMA_TYPE(s->hostctl1)) {
case SDHC_CTRL_ADMA2_32:
- dma_memory_read(s->dma_as, entry_addr, &adma2, sizeof(adma2));
+ 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.
* We currently assume that it is LE.
@@ -705,7 +710,8 @@ static void get_adma_description(SDHCIState *s, ADMADescr *dscr)
dscr->incr = 8;
break;
case SDHC_CTRL_ADMA1_32:
- dma_memory_read(s->dma_as, entry_addr, &adma1, sizeof(adma1));
+ dma_memory_read(s->dma_as, entry_addr, &adma1, sizeof(adma1),
+ MEMTXATTRS_UNSPECIFIED);
adma1 = le32_to_cpu(adma1);
dscr->addr = (hwaddr)(adma1 & 0xFFFFF000);
dscr->attr = (uint8_t)extract32(adma1, 0, 7);
@@ -717,10 +723,13 @@ static void get_adma_description(SDHCIState *s, ADMADescr *dscr)
}
break;
case SDHC_CTRL_ADMA2_64:
- dma_memory_read(s->dma_as, entry_addr, &dscr->attr, 1);
- dma_memory_read(s->dma_as, entry_addr + 2, &dscr->length, 2);
+ dma_memory_read(s->dma_as, entry_addr, &dscr->attr, 1,
+ MEMTXATTRS_UNSPECIFIED);
+ dma_memory_read(s->dma_as, entry_addr + 2, &dscr->length, 2,
+ MEMTXATTRS_UNSPECIFIED);
dscr->length = le16_to_cpu(dscr->length);
- dma_memory_read(s->dma_as, entry_addr + 4, &dscr->addr, 8);
+ dma_memory_read(s->dma_as, entry_addr + 4, &dscr->addr, 8,
+ MEMTXATTRS_UNSPECIFIED);
dscr->addr = le64_to_cpu(dscr->addr);
dscr->attr &= (uint8_t) ~0xC0;
dscr->incr = 12;
@@ -734,7 +743,9 @@ static void sdhci_do_adma(SDHCIState *s)
{
unsigned int begin, length;
const uint16_t block_size = s->blksize & BLOCK_SIZE_MASK;
+ const MemTxAttrs attrs = { .memory = true };
ADMADescr dscr = {};
+ MemTxResult res;
int i;
if (s->trnmod & SDHC_TRNS_BLK_CNT_EN && !s->blkcnt) {
@@ -783,9 +794,13 @@ static void sdhci_do_adma(SDHCIState *s)
s->data_count = block_size;
length -= block_size - begin;
}
- dma_memory_write(s->dma_as, dscr.addr,
- &s->fifo_buffer[begin],
- s->data_count - begin);
+ res = dma_memory_write(s->dma_as, dscr.addr,
+ &s->fifo_buffer[begin],
+ s->data_count - begin,
+ attrs);
+ if (res != MEMTX_OK) {
+ break;
+ }
dscr.addr += s->data_count - begin;
if (s->data_count == block_size) {
s->data_count = 0;
@@ -808,9 +823,13 @@ static void sdhci_do_adma(SDHCIState *s)
s->data_count = block_size;
length -= block_size - begin;
}
- dma_memory_read(s->dma_as, dscr.addr,
- &s->fifo_buffer[begin],
- s->data_count - begin);
+ res = dma_memory_read(s->dma_as, dscr.addr,
+ &s->fifo_buffer[begin],
+ s->data_count - begin,
+ attrs);
+ if (res != MEMTX_OK) {
+ break;
+ }
dscr.addr += s->data_count - begin;
if (s->data_count == block_size) {
sdbus_write_data(&s->sdbus, s->fifo_buffer, block_size);
@@ -824,7 +843,16 @@ static void sdhci_do_adma(SDHCIState *s)
}
}
}
- s->admasysaddr += dscr.incr;
+ if (res != MEMTX_OK) {
+ if (s->errintstsen & SDHC_EISEN_ADMAERR) {
+ trace_sdhci_error("Set ADMA error flag");
+ s->errintsts |= SDHC_EIS_ADMAERR;
+ s->norintsts |= SDHC_NIS_ERR;
+ }
+ sdhci_update_irq(s);
+ } else {
+ s->admasysaddr += dscr.incr;
+ }
break;
case SDHC_ADMA_ATTR_ACT_LINK: /* link to next descriptor table */
s->admasysaddr = dscr.addr;
@@ -1128,7 +1156,8 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
s->sdmasysad = (s->sdmasysad & mask) | value;
MASKED_WRITE(s->sdmasysad, mask, value);
/* Writing to last byte of sdmasysad might trigger transfer */
- if (!(mask & 0xFF000000) && s->blkcnt && s->blksize &&
+ 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);
@@ -1142,7 +1171,11 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
if (!TRANSFERRING_DATA(s->prnsts)) {
uint16_t blksize = s->blksize;
- MASKED_WRITE(s->blksize, mask, extract32(value, 0, 12));
+ /*
+ * [14:12] SDMA Buffer Boundary
+ * [11:00] Transfer Block Size
+ */
+ MASKED_WRITE(s->blksize, mask, extract32(value, 0, 15));
MASKED_WRITE(s->blkcnt, mask >> 16, value >> 16);
/* Limit block size to the maximum buffer size */
@@ -1303,7 +1336,7 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
value >> shift, value >> shift);
}
-static const MemoryRegionOps sdhci_mmio_ops = {
+static const MemoryRegionOps sdhci_mmio_le_ops = {
.read = sdhci_read,
.write = sdhci_write,
.valid = {
@@ -1314,6 +1347,21 @@ static const MemoryRegionOps sdhci_mmio_ops = {
.endianness = DEVICE_LITTLE_ENDIAN,
};
+static const MemoryRegionOps sdhci_mmio_be_ops = {
+ .read = sdhci_read,
+ .write = sdhci_write,
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ .unaligned = false
+ },
+ .endianness = DEVICE_BIG_ENDIAN,
+};
+
static void sdhci_init_readonly_registers(SDHCIState *s, Error **errp)
{
ERRP_GUARD();
@@ -1337,13 +1385,12 @@ static void sdhci_init_readonly_registers(SDHCIState *s, Error **errp)
void sdhci_initfn(SDHCIState *s)
{
- qbus_create_inplace(&s->sdbus, sizeof(s->sdbus),
- TYPE_SDHCI_BUS, DEVICE(s), "sd-bus");
+ 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->io_ops = &sdhci_mmio_ops;
+ s->io_ops = &sdhci_mmio_le_ops;
}
void sdhci_uninitfn(SDHCIState *s)
@@ -1359,10 +1406,27 @@ void sdhci_common_realize(SDHCIState *s, Error **errp)
{
ERRP_GUARD();
+ switch (s->endianness) {
+ case DEVICE_LITTLE_ENDIAN:
+ /* s->io_ops is little endian by default */
+ break;
+ case DEVICE_BIG_ENDIAN:
+ if (s->io_ops != &sdhci_mmio_le_ops) {
+ error_setg(errp, "SD controller doesn't support big endianness");
+ return;
+ }
+ s->io_ops = &sdhci_mmio_be_ops;
+ break;
+ default:
+ error_setg(errp, "Incorrect endianness");
+ return;
+ }
+
sdhci_init_readonly_registers(s, errp);
if (*errp) {
return;
}
+
s->buf_maxsz = sdhci_get_fifolen(s);
s->fifo_buffer = g_malloc0(s->buf_maxsz);
@@ -1393,7 +1457,7 @@ static const VMStateDescription sdhci_pending_insert_vmstate = {
.version_id = 1,
.minimum_version_id = 1,
.needed = sdhci_pending_insert_vmstate_needed,
- .fields = (VMStateField[]) {
+ .fields = (const VMStateField[]) {
VMSTATE_BOOL(pending_insert_state, SDHCIState),
VMSTATE_END_OF_LIST()
},
@@ -1403,7 +1467,7 @@ const VMStateDescription sdhci_vmstate = {
.name = "sdhci",
.version_id = 1,
.minimum_version_id = 1,
- .fields = (VMStateField[]) {
+ .fields = (const VMStateField[]) {
VMSTATE_UINT32(sdmasysad, SDHCIState),
VMSTATE_UINT16(blksize, SDHCIState),
VMSTATE_UINT16(blkcnt, SDHCIState),
@@ -1434,7 +1498,7 @@ const VMStateDescription sdhci_vmstate = {
VMSTATE_TIMER_PTR(transfer_timer, SDHCIState),
VMSTATE_END_OF_LIST()
},
- .subsections = (const VMStateDescription*[]) {
+ .subsections = (const VMStateDescription * const []) {
&sdhci_pending_insert_vmstate,
NULL
},
@@ -1552,6 +1616,25 @@ static const TypeInfo sdhci_bus_info = {
/* --- qdev i.MX eSDHC --- */
+#define USDHC_MIX_CTRL 0x48
+
+#define USDHC_VENDOR_SPEC 0xc0
+#define USDHC_IMX_FRC_SDCLK_ON (1 << 8)
+
+#define USDHC_DLL_CTRL 0x60
+
+#define USDHC_TUNING_CTRL 0xcc
+#define USDHC_TUNE_CTRL_STATUS 0x68
+#define USDHC_WTMK_LVL 0x44
+
+/* Undocumented register used by guests working around erratum ERR004536 */
+#define USDHC_UNDOCUMENTED_REG27 0x6c
+
+#define USDHC_CTRL_4BITBUS (0x1 << 1)
+#define USDHC_CTRL_8BITBUS (0x2 << 1)
+
+#define USDHC_PRNSTS_SDSTB (1 << 3)
+
static uint64_t usdhc_read(void *opaque, hwaddr offset, unsigned size)
{
SDHCIState *s = SYSBUS_SDHCI(opaque);
@@ -1571,11 +1654,11 @@ static uint64_t usdhc_read(void *opaque, hwaddr offset, unsigned size)
hostctl1 = SDHC_DMA_TYPE(s->hostctl1) << (8 - 3);
if (s->hostctl1 & SDHC_CTRL_8BITBUS) {
- hostctl1 |= ESDHC_CTRL_8BITBUS;
+ hostctl1 |= USDHC_CTRL_8BITBUS;
}
if (s->hostctl1 & SDHC_CTRL_4BITBUS) {
- hostctl1 |= ESDHC_CTRL_4BITBUS;
+ hostctl1 |= USDHC_CTRL_4BITBUS;
}
ret = hostctl1;
@@ -1586,21 +1669,21 @@ static uint64_t usdhc_read(void *opaque, hwaddr offset, unsigned size)
case SDHC_PRNSTS:
/* Add SDSTB (SD Clock Stable) bit to PRNSTS */
- ret = sdhci_read(opaque, offset, size) & ~ESDHC_PRNSTS_SDSTB;
+ ret = sdhci_read(opaque, offset, size) & ~USDHC_PRNSTS_SDSTB;
if (s->clkcon & SDHC_CLOCK_INT_STABLE) {
- ret |= ESDHC_PRNSTS_SDSTB;
+ ret |= USDHC_PRNSTS_SDSTB;
}
break;
- case ESDHC_VENDOR_SPEC:
+ case USDHC_VENDOR_SPEC:
ret = s->vendor_spec;
break;
- case ESDHC_DLL_CTRL:
- case ESDHC_TUNE_CTRL_STATUS:
- case ESDHC_UNDOCUMENTED_REG27:
- case ESDHC_TUNING_CTRL:
- case ESDHC_MIX_CTRL:
- case ESDHC_WTMK_LVL:
+ case USDHC_DLL_CTRL:
+ case USDHC_TUNE_CTRL_STATUS:
+ case USDHC_UNDOCUMENTED_REG27:
+ case USDHC_TUNING_CTRL:
+ case USDHC_MIX_CTRL:
+ case USDHC_WTMK_LVL:
ret = 0;
break;
}
@@ -1616,18 +1699,18 @@ usdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
uint32_t value = (uint32_t)val;
switch (offset) {
- case ESDHC_DLL_CTRL:
- case ESDHC_TUNE_CTRL_STATUS:
- case ESDHC_UNDOCUMENTED_REG27:
- case ESDHC_TUNING_CTRL:
- case ESDHC_WTMK_LVL:
+ case USDHC_DLL_CTRL:
+ case USDHC_TUNE_CTRL_STATUS:
+ case USDHC_UNDOCUMENTED_REG27:
+ case USDHC_TUNING_CTRL:
+ case USDHC_WTMK_LVL:
break;
- case ESDHC_VENDOR_SPEC:
+ case USDHC_VENDOR_SPEC:
s->vendor_spec = value;
switch (s->vendor) {
case SDHCI_VENDOR_IMX:
- if (value & ESDHC_IMX_FRC_SDCLK_ON) {
+ if (value & USDHC_IMX_FRC_SDCLK_ON) {
s->prnsts &= ~SDHC_IMX_CLOCK_GATE_OFF;
} else {
s->prnsts |= SDHC_IMX_CLOCK_GATE_OFF;
@@ -1696,12 +1779,12 @@ usdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
* Second, split "Data Transfer Width" from bits 2 and 1 in to
* bits 5 and 1
*/
- if (value & ESDHC_CTRL_8BITBUS) {
+ if (value & USDHC_CTRL_8BITBUS) {
hostctl1 |= SDHC_CTRL_8BITBUS;
}
- if (value & ESDHC_CTRL_4BITBUS) {
- hostctl1 |= ESDHC_CTRL_4BITBUS;
+ if (value & USDHC_CTRL_4BITBUS) {
+ hostctl1 |= USDHC_CTRL_4BITBUS;
}
/*
@@ -1724,7 +1807,7 @@ usdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
sdhci_write(opaque, offset, value, size);
break;
- case ESDHC_MIX_CTRL:
+ case USDHC_MIX_CTRL:
/*
* So, when SD/MMC stack in Linux tries to write to "Transfer
* Mode Register", ESDHC i.MX quirk code will translate it
@@ -1735,7 +1818,7 @@ usdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
* on i.MX, but since it is not used by QEMU we do not care.
*
* We don't want to call sdhci_write(.., SDHC_TRNMOD, ...)
- * here becuase it will result in a call to
+ * here because it will result in a call to
* sdhci_send_command(s) which we don't want.
*
*/