*/
#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/hw.h"
#include "sysemu/block-backend.h"
#include "sysemu/blockdev.h"
#include "sysemu/dma.h"
#include "qemu/timer.h"
#include "qemu/bitops.h"
+#include "hw/sd/sdhci.h"
#include "sdhci-internal.h"
-
-/* host controller debug messages */
-#ifndef SDHC_DEBUG
-#define SDHC_DEBUG 0
-#endif
-
-#define DPRINT_L1(fmt, args...) \
- do { \
- if (SDHC_DEBUG) { \
- fprintf(stderr, "QEMU SDHC: " fmt, ## args); \
- } \
- } while (0)
-#define DPRINT_L2(fmt, args...) \
- do { \
- if (SDHC_DEBUG > 1) { \
- fprintf(stderr, "QEMU SDHC: " fmt, ## args); \
- } \
- } while (0)
-#define ERRPRINT(fmt, args...) \
- do { \
- if (SDHC_DEBUG) { \
- fprintf(stderr, "QEMU SDHC ERROR: " fmt, ## args); \
- } \
- } while (0)
+#include "qemu/log.h"
+#include "trace.h"
#define TYPE_SDHCI_BUS "sdhci-bus"
#define SDHCI_BUS(obj) OBJECT_CHECK(SDBus, (obj), TYPE_SDHCI_BUS)
+#define MASKED_WRITE(reg, mask, val) (reg = (reg & (mask)) | (val))
+
/* Default SD/MMC host controller features information, which will be
* presented in CAPABILITIES register of generic SD host controller at reset.
- * If not stated otherwise:
- * 0 - not supported, 1 - supported, other - prohibited.
+ *
+ * support:
+ * - 3.3v and 1.8v voltages
+ * - SDMA/ADMA1/ADMA2
+ * - high-speed
+ * max host controller R/W buffers size: 512B
+ * max clock frequency for SDclock: 52 MHz
+ * timeout clock frequency: 52 MHz
+ *
+ * does not support:
+ * - 3.0v voltage
+ * - 64-bit system bus
+ * - suspend/resume
*/
-#define SDHC_CAPAB_64BITBUS 0ul /* 64-bit System Bus Support */
-#define SDHC_CAPAB_18V 1ul /* Voltage support 1.8v */
-#define SDHC_CAPAB_30V 0ul /* Voltage support 3.0v */
-#define SDHC_CAPAB_33V 1ul /* Voltage support 3.3v */
-#define SDHC_CAPAB_SUSPRESUME 0ul /* Suspend/resume support */
-#define SDHC_CAPAB_SDMA 1ul /* SDMA support */
-#define SDHC_CAPAB_HIGHSPEED 1ul /* High speed support */
-#define SDHC_CAPAB_ADMA1 1ul /* ADMA1 support */
-#define SDHC_CAPAB_ADMA2 1ul /* ADMA2 support */
-/* Maximum host controller R/W buffers size
- * Possible values: 512, 1024, 2048 bytes */
-#define SDHC_CAPAB_MAXBLOCKLENGTH 512ul
-/* Maximum clock frequency for SDclock in MHz
- * value in range 10-63 MHz, 0 - not defined */
-#define SDHC_CAPAB_BASECLKFREQ 52ul
-#define SDHC_CAPAB_TOUNIT 1ul /* Timeout clock unit 0 - kHz, 1 - MHz */
-/* Timeout clock frequency 1-63, 0 - not defined */
-#define SDHC_CAPAB_TOCLKFREQ 52ul
-
-/* Now check all parameters and calculate CAPABILITIES REGISTER value */
-#if SDHC_CAPAB_64BITBUS > 1 || SDHC_CAPAB_18V > 1 || SDHC_CAPAB_30V > 1 || \
- SDHC_CAPAB_33V > 1 || SDHC_CAPAB_SUSPRESUME > 1 || SDHC_CAPAB_SDMA > 1 || \
- SDHC_CAPAB_HIGHSPEED > 1 || SDHC_CAPAB_ADMA2 > 1 || SDHC_CAPAB_ADMA1 > 1 ||\
- SDHC_CAPAB_TOUNIT > 1
-#error Capabilities features can have value 0 or 1 only!
-#endif
-
-#if SDHC_CAPAB_MAXBLOCKLENGTH == 512
-#define MAX_BLOCK_LENGTH 0ul
-#elif SDHC_CAPAB_MAXBLOCKLENGTH == 1024
-#define MAX_BLOCK_LENGTH 1ul
-#elif SDHC_CAPAB_MAXBLOCKLENGTH == 2048
-#define MAX_BLOCK_LENGTH 2ul
-#else
-#error Max host controller block size can have value 512, 1024 or 2048 only!
-#endif
-
-#if (SDHC_CAPAB_BASECLKFREQ > 0 && SDHC_CAPAB_BASECLKFREQ < 10) || \
- SDHC_CAPAB_BASECLKFREQ > 63
-#error SDclock frequency can have value in range 0, 10-63 only!
-#endif
-
-#if SDHC_CAPAB_TOCLKFREQ > 63
-#error Timeout clock frequency can have value in range 0-63 only!
-#endif
-
-#define SDHC_CAPAB_REG_DEFAULT \
- ((SDHC_CAPAB_64BITBUS << 28) | (SDHC_CAPAB_18V << 26) | \
- (SDHC_CAPAB_30V << 25) | (SDHC_CAPAB_33V << 24) | \
- (SDHC_CAPAB_SUSPRESUME << 23) | (SDHC_CAPAB_SDMA << 22) | \
- (SDHC_CAPAB_HIGHSPEED << 21) | (SDHC_CAPAB_ADMA1 << 20) | \
- (SDHC_CAPAB_ADMA2 << 19) | (MAX_BLOCK_LENGTH << 16) | \
- (SDHC_CAPAB_BASECLKFREQ << 8) | (SDHC_CAPAB_TOUNIT << 7) | \
- (SDHC_CAPAB_TOCLKFREQ))
+#define SDHC_CAPAB_REG_DEFAULT 0x057834b4
-#define MASKED_WRITE(reg, mask, val) (reg = (reg & (mask)) | (val))
+static inline unsigned int sdhci_get_fifolen(SDHCIState *s)
+{
+ return 1 << (9 + FIELD_EX32(s->capareg, SDHC_CAPAB, MAXBLOCKLENGTH));
+}
static uint8_t sdhci_slotint(SDHCIState *s)
{
static void sdhci_set_inserted(DeviceState *dev, bool level)
{
SDHCIState *s = (SDHCIState *)dev;
- DPRINT_L1("Card state changed: %s!\n", level ? "insert" : "eject");
+ trace_sdhci_set_inserted(level ? "insert" : "eject");
if ((s->norintsts & SDHC_NIS_REMOVE) && level) {
/* Give target some time to notice card ejection */
timer_mod(s->insert_timer,
timer_del(s->insert_timer);
timer_del(s->transfer_timer);
- /* Set all registers to 0. Capabilities 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 */
memset(&s->sdmasysad, 0, (uintptr_t)&s->capareg - (uintptr_t)&s->sdmasysad);
- if (!s->noeject_quirk) {
- /* Reset other state based on current card insertion/readonly status */
- sdhci_set_inserted(dev, sdbus_get_inserted(&s->sdbus));
- sdhci_set_readonly(dev, sdbus_get_readonly(&s->sdbus));
- }
+ /* Reset other state based on current card insertion/readonly status */
+ sdhci_set_inserted(dev, sdbus_get_inserted(&s->sdbus));
+ sdhci_set_readonly(dev, sdbus_get_readonly(&s->sdbus));
s->data_count = 0;
s->stopped_state = sdhc_not_stopped;
+ s->pending_insert_state = false;
+}
+
+static void sdhci_poweron_reset(DeviceState *dev)
+{
+ /* QOM (ie power-on) reset. This is identical to reset
+ * commanded via device register apart from handling of the
+ * 'pending insert on powerup' quirk.
+ */
+ SDHCIState *s = (SDHCIState *)dev;
+
+ sdhci_reset(s);
+
+ if (s->pending_insert_quirk) {
+ s->pending_insert_state = true;
+ }
}
static void sdhci_data_transfer(void *opaque);
s->acmd12errsts = 0;
request.cmd = s->cmdreg >> 8;
request.arg = s->argument;
- DPRINT_L1("sending CMD%u ARG[0x%08x]\n", request.cmd, request.arg);
+
+ trace_sdhci_send_command(request.cmd, request.arg);
rlen = sdbus_do_command(&s->sdbus, &request, response);
if (s->cmdreg & SDHC_CMD_RESPONSE) {
s->rspreg[0] = (response[0] << 24) | (response[1] << 16) |
(response[2] << 8) | response[3];
s->rspreg[1] = s->rspreg[2] = s->rspreg[3] = 0;
- DPRINT_L1("Response: RSPREG[31..0]=0x%08x\n", s->rspreg[0]);
+ trace_sdhci_response4(s->rspreg[0]);
} else if (rlen == 16) {
s->rspreg[0] = (response[11] << 24) | (response[12] << 16) |
(response[13] << 8) | response[14];
(response[5] << 8) | response[6];
s->rspreg[3] = (response[0] << 16) | (response[1] << 8) |
response[2];
- DPRINT_L1("Response received:\n RSPREG[127..96]=0x%08x, RSPREG[95.."
- "64]=0x%08x,\n RSPREG[63..32]=0x%08x, RSPREG[31..0]=0x%08x\n",
- s->rspreg[3], s->rspreg[2], s->rspreg[1], s->rspreg[0]);
+ trace_sdhci_response16(s->rspreg[3], s->rspreg[2],
+ s->rspreg[1], s->rspreg[0]);
} else {
- ERRPRINT("Timeout waiting for command response\n");
+ trace_sdhci_error("timeout waiting for command response");
if (s->errintstsen & SDHC_EISEN_CMDTIMEOUT) {
s->errintsts |= SDHC_EIS_CMDTIMEOUT;
s->norintsts |= SDHC_NIS_ERR;
}
}
- if ((s->norintstsen & SDHC_NISEN_TRSCMP) &&
+ if (!(s->quirks & SDHCI_QUIRK_NO_BUSY_IRQ) &&
+ (s->norintstsen & SDHC_NISEN_TRSCMP) &&
(s->cmdreg & SDHC_CMD_RESPONSE) == SDHC_CMD_RSP_WITH_BUSY) {
s->norintsts |= SDHC_NIS_TRSCMP;
}
request.cmd = 0x0C;
request.arg = 0;
- DPRINT_L1("Automatically issue CMD%d %08x\n", request.cmd, request.arg);
+ trace_sdhci_end_transfer(request.cmd, request.arg);
sdbus_do_command(&s->sdbus, &request, response);
/* Auto CMD12 response goes to the upper Response register */
s->rspreg[3] = (response[0] << 24) | (response[1] << 16) |
/* first check that a valid data exists in host controller input buffer */
if ((s->prnsts & SDHC_DATA_AVAILABLE) == 0) {
- ERRPRINT("Trying to read from empty buffer\n");
+ trace_sdhci_error("read from empty buffer");
return 0;
}
s->data_count++;
/* check if we've read all valid data (blksize bytes) from buffer */
if ((s->data_count) >= (s->blksize & 0x0fff)) {
- DPRINT_L2("All %u bytes of data have been read from input buffer\n",
- s->data_count);
+ trace_sdhci_read_dataport(s->data_count);
s->prnsts &= ~SDHC_DATA_AVAILABLE; /* no more data in a buffer */
s->data_count = 0; /* next buff read must start at position [0] */
/* Check that there is free space left in a buffer */
if (!(s->prnsts & SDHC_SPACE_AVAILABLE)) {
- ERRPRINT("Can't write to data buffer: buffer full\n");
+ trace_sdhci_error("Can't write to data buffer: buffer full");
return;
}
s->data_count++;
value >>= 8;
if (s->data_count >= (s->blksize & 0x0fff)) {
- DPRINT_L2("write buffer filled with %u bytes of data\n",
- s->data_count);
+ trace_sdhci_write_dataport(s->data_count);
s->data_count = 0;
s->prnsts &= ~SDHC_SPACE_AVAILABLE;
if (s->prnsts & SDHC_DOING_WRITE) {
uint32_t boundary_chk = 1 << (((s->blksize & 0xf000) >> 12) + 12);
uint32_t boundary_count = boundary_chk - (s->sdmasysad % boundary_chk);
+ if (!(s->trnmod & SDHC_TRNS_BLK_CNT_EN) || !s->blkcnt) {
+ qemu_log_mask(LOG_UNIMP, "infinite transfer is not supported\n");
+ return;
+ }
+
/* 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 */
s->blkcnt--;
}
}
- dma_memory_write(&address_space_memory, s->sdmasysad,
+ dma_memory_write(s->dma_as, s->sdmasysad,
&s->fifo_buffer[begin], s->data_count - begin);
s->sdmasysad += s->data_count - begin;
if (s->data_count == block_size) {
s->data_count = block_size;
boundary_count -= block_size - begin;
}
- dma_memory_read(&address_space_memory, s->sdmasysad,
- &s->fifo_buffer[begin], s->data_count);
+ dma_memory_read(s->dma_as, s->sdmasysad,
+ &s->fifo_buffer[begin], s->data_count - begin);
s->sdmasysad += s->data_count - begin;
if (s->data_count == block_size) {
for (n = 0; n < block_size; n++) {
}
/* single block SDMA transfer */
-
static void sdhci_sdma_transfer_single_block(SDHCIState *s)
{
int n;
for (n = 0; n < datacnt; n++) {
s->fifo_buffer[n] = sdbus_read_data(&s->sdbus);
}
- dma_memory_write(&address_space_memory, s->sdmasysad, s->fifo_buffer,
- datacnt);
+ dma_memory_write(s->dma_as, s->sdmasysad, s->fifo_buffer, datacnt);
} else {
- dma_memory_read(&address_space_memory, s->sdmasysad, s->fifo_buffer,
- datacnt);
+ dma_memory_read(s->dma_as, s->sdmasysad, s->fifo_buffer, datacnt);
for (n = 0; n < datacnt; n++) {
sdbus_write_data(&s->sdbus, s->fifo_buffer[n]);
}
}
-
- if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) {
- s->blkcnt--;
- }
+ s->blkcnt--;
sdhci_end_transfer(s);
}
hwaddr entry_addr = (hwaddr)s->admasysaddr;
switch (SDHC_DMA_TYPE(s->hostctl)) {
case SDHC_CTRL_ADMA2_32:
- dma_memory_read(&address_space_memory, entry_addr, (uint8_t *)&adma2,
+ dma_memory_read(s->dma_as, entry_addr, (uint8_t *)&adma2,
sizeof(adma2));
adma2 = le64_to_cpu(adma2);
/* The spec does not specify endianness of descriptor table.
dscr->incr = 8;
break;
case SDHC_CTRL_ADMA1_32:
- dma_memory_read(&address_space_memory, entry_addr, (uint8_t *)&adma1,
+ dma_memory_read(s->dma_as, entry_addr, (uint8_t *)&adma1,
sizeof(adma1));
adma1 = le32_to_cpu(adma1);
dscr->addr = (hwaddr)(adma1 & 0xFFFFF000);
}
break;
case SDHC_CTRL_ADMA2_64:
- dma_memory_read(&address_space_memory, entry_addr,
+ dma_memory_read(s->dma_as, entry_addr,
(uint8_t *)(&dscr->attr), 1);
- dma_memory_read(&address_space_memory, entry_addr + 2,
+ dma_memory_read(s->dma_as, entry_addr + 2,
(uint8_t *)(&dscr->length), 2);
dscr->length = le16_to_cpu(dscr->length);
- dma_memory_read(&address_space_memory, entry_addr + 4,
+ dma_memory_read(s->dma_as, entry_addr + 4,
(uint8_t *)(&dscr->addr), 8);
dscr->attr = le64_to_cpu(dscr->attr);
dscr->attr &= 0xfffffff8;
{
unsigned int n, begin, length;
const uint16_t block_size = s->blksize & 0x0fff;
- ADMADescr dscr;
+ ADMADescr dscr = {};
int i;
for (i = 0; i < SDHC_ADMA_DESCS_PER_DELAY; ++i) {
s->admaerr &= ~SDHC_ADMAERR_LENGTH_MISMATCH;
get_adma_description(s, &dscr);
- DPRINT_L2("ADMA loop: addr=" TARGET_FMT_plx ", len=%d, attr=%x\n",
- dscr.addr, dscr.length, dscr.attr);
+ trace_sdhci_adma_loop(dscr.addr, dscr.length, dscr.attr);
if ((dscr.attr & SDHC_ADMA_ATTR_VALID) == 0) {
/* Indicate that error occurred in ST_FDS state */
s->data_count = block_size;
length -= block_size - begin;
}
- dma_memory_write(&address_space_memory, dscr.addr,
+ dma_memory_write(s->dma_as, dscr.addr,
&s->fifo_buffer[begin],
s->data_count - begin);
dscr.addr += s->data_count - begin;
s->data_count = block_size;
length -= block_size - begin;
}
- dma_memory_read(&address_space_memory, dscr.addr,
+ dma_memory_read(s->dma_as, dscr.addr,
&s->fifo_buffer[begin],
s->data_count - begin);
dscr.addr += s->data_count - begin;
break;
case SDHC_ADMA_ATTR_ACT_LINK: /* link to next descriptor table */
s->admasysaddr = dscr.addr;
- DPRINT_L1("ADMA link: admasysaddr=0x%" PRIx64 "\n",
- s->admasysaddr);
+ trace_sdhci_adma("link", s->admasysaddr);
break;
default:
s->admasysaddr += dscr.incr;
}
if (dscr.attr & SDHC_ADMA_ATTR_INT) {
- DPRINT_L1("ADMA interrupt: admasysaddr=0x%" PRIx64 "\n",
- s->admasysaddr);
+ trace_sdhci_adma("interrupt", s->admasysaddr);
if (s->norintstsen & SDHC_NISEN_DMA) {
s->norintsts |= SDHC_NIS_DMA;
}
/* ADMA transfer terminates if blkcnt == 0 or by END attribute */
if (((s->trnmod & SDHC_TRNS_BLK_CNT_EN) &&
(s->blkcnt == 0)) || (dscr.attr & SDHC_ADMA_ATTR_END)) {
- DPRINT_L2("ADMA transfer completed\n");
+ trace_sdhci_adma_transfer_completed();
if (length || ((dscr.attr & SDHC_ADMA_ATTR_END) &&
(s->trnmod & SDHC_TRNS_BLK_CNT_EN) &&
s->blkcnt != 0)) {
- ERRPRINT("SD/MMC host ADMA length mismatch\n");
+ trace_sdhci_error("SD/MMC host ADMA length mismatch");
s->admaerr |= SDHC_ADMAERR_LENGTH_MISMATCH |
SDHC_ADMAERR_STATE_ST_TFR;
if (s->errintstsen & SDHC_EISEN_ADMAERR) {
- ERRPRINT("Set ADMA error flag\n");
+ trace_sdhci_error("Set ADMA error flag");
s->errintsts |= SDHC_EIS_ADMAERR;
s->norintsts |= SDHC_NIS_ERR;
}
if (s->trnmod & SDHC_TRNS_DMA) {
switch (SDHC_DMA_TYPE(s->hostctl)) {
case SDHC_CTRL_SDMA:
- if ((s->trnmod & SDHC_TRNS_MULTI) &&
- (!(s->trnmod & SDHC_TRNS_BLK_CNT_EN) || s->blkcnt == 0)) {
- break;
- }
-
if ((s->blkcnt == 1) || !(s->trnmod & SDHC_TRNS_MULTI)) {
sdhci_sdma_transfer_single_block(s);
} else {
break;
case SDHC_CTRL_ADMA1_32:
if (!(s->capareg & SDHC_CAN_DO_ADMA1)) {
- ERRPRINT("ADMA1 not supported\n");
+ trace_sdhci_error("ADMA1 not supported");
break;
}
break;
case SDHC_CTRL_ADMA2_32:
if (!(s->capareg & SDHC_CAN_DO_ADMA2)) {
- ERRPRINT("ADMA2 not supported\n");
+ trace_sdhci_error("ADMA2 not supported");
break;
}
case SDHC_CTRL_ADMA2_64:
if (!(s->capareg & SDHC_CAN_DO_ADMA2) ||
!(s->capareg & SDHC_64_BIT_BUS_SUPPORT)) {
- ERRPRINT("64 bit ADMA not supported\n");
+ trace_sdhci_error("64 bit ADMA not supported");
break;
}
sdhci_do_adma(s);
break;
default:
- ERRPRINT("Unsupported DMA type\n");
+ trace_sdhci_error("Unsupported DMA type");
break;
}
} else {
sdhci_buff_access_is_sequential(SDHCIState *s, unsigned byte_num)
{
if ((s->data_count & 0x3) != byte_num) {
- ERRPRINT("Non-sequential access to Buffer Data Port register"
- "is prohibited\n");
+ trace_sdhci_error("Non-sequential access to Buffer Data Port register"
+ "is prohibited\n");
return false;
}
return true;
case SDHC_BDATA:
if (sdhci_buff_access_is_sequential(s, offset - SDHC_BDATA)) {
ret = sdhci_read_dataport(s, size);
- DPRINT_L2("read %ub: addr[0x%04x] -> %u(0x%x)\n", size, (int)offset,
- ret, ret);
+ trace_sdhci_access("rd", size << 3, offset, "->", ret, ret);
return ret;
}
break;
case SDHC_ACMD12ERRSTS:
ret = s->acmd12errsts;
break;
- case SDHC_CAPAREG:
- ret = s->capareg;
+ case SDHC_CAPAB:
+ ret = (uint32_t)s->capareg;
+ break;
+ case SDHC_CAPAB + 4:
+ ret = (uint32_t)(s->capareg >> 32);
break;
case SDHC_MAXCURR:
- ret = s->maxcurr;
+ ret = (uint32_t)s->maxcurr;
+ break;
+ case SDHC_MAXCURR + 4:
+ ret = (uint32_t)(s->maxcurr >> 32);
break;
case SDHC_ADMAERR:
ret = s->admaerr;
ret = (uint32_t)(s->admasysaddr >> 32);
break;
case SDHC_SLOT_INT_STATUS:
- ret = (SD_HOST_SPECv2_VERS << 16) | sdhci_slotint(s);
+ ret = (s->version << 16) | sdhci_slotint(s);
break;
default:
- ERRPRINT("bad %ub read: addr[0x%04x]\n", size, (int)offset);
+ qemu_log_mask(LOG_UNIMP, "SDHC rd_%ub @0x%02" HWADDR_PRIx " "
+ "not implemented\n", size, offset);
break;
}
ret >>= (offset & 0x3) * 8;
ret &= (1ULL << (size * 8)) - 1;
- DPRINT_L2("read %ub: addr[0x%04x] -> %u(0x%x)\n", size, (int)offset, ret, ret);
+ trace_sdhci_access("rd", size << 3, offset, "->", ret, ret);
return ret;
}
/* Writing to last byte of sdmasysad might trigger transfer */
if (!(mask & 0xFF000000) && TRANSFERRING_DATA(s->prnsts) && s->blkcnt &&
s->blksize && SDHC_DMA_TYPE(s->hostctl) == SDHC_CTRL_SDMA) {
- sdhci_sdma_transfer_multi_blocks(s);
+ if (s->trnmod & SDHC_TRNS_MULTI) {
+ sdhci_sdma_transfer_multi_blocks(s);
+ } else {
+ sdhci_sdma_transfer_single_block(s);
+ }
}
break;
case SDHC_BLKSIZE:
if (!(s->capareg & SDHC_CAN_DO_DMA)) {
value &= ~SDHC_TRNS_DMA;
}
- MASKED_WRITE(s->trnmod, mask, value);
+ MASKED_WRITE(s->trnmod, mask, value & SDHC_TRNMOD_MASK);
MASKED_WRITE(s->cmdreg, mask >> 16, value >> 16);
/* Writing to the upper byte of CMDREG triggers SD command generation */
} else {
s->norintsts &= ~SDHC_NIS_ERR;
}
+ /* 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;
+ s->pending_insert_state = false;
+ }
sdhci_update_irq(s);
break;
case SDHC_NORINTSIGEN:
}
sdhci_update_irq(s);
break;
+ case SDHC_ACMD12ERRSTS:
+ MASKED_WRITE(s->acmd12errsts, mask, value);
+ break;
+
+ case SDHC_CAPAB:
+ case SDHC_CAPAB + 4:
+ case SDHC_MAXCURR:
+ case SDHC_MAXCURR + 4:
+ qemu_log_mask(LOG_GUEST_ERROR, "SDHC wr_%ub @0x%02" HWADDR_PRIx
+ " <- 0x%08x read-only\n", size, offset, value >> shift);
+ break;
+
default:
- ERRPRINT("bad %ub write offset: addr[0x%04x] <- %u(0x%x)\n",
- size, (int)offset, value >> shift, value >> shift);
+ qemu_log_mask(LOG_UNIMP, "SDHC wr_%ub @0x%02" HWADDR_PRIx " <- 0x%08x "
+ "not implemented\n", size, offset, value >> shift);
break;
}
- DPRINT_L2("write %ub: addr[0x%04x] <- %u(0x%x)\n",
- size, (int)offset, value >> shift, value >> shift);
+ trace_sdhci_access("wr", size << 3, offset, "<-",
+ value >> shift, value >> shift);
}
static const MemoryRegionOps sdhci_mmio_ops = {
.endianness = DEVICE_LITTLE_ENDIAN,
};
-static inline unsigned int sdhci_get_fifolen(SDHCIState *s)
+static void sdhci_init_readonly_registers(SDHCIState *s, Error **errp)
{
- switch (SDHC_CAPAB_BLOCKSIZE(s->capareg)) {
- case 0:
- return 512;
- case 1:
- return 1024;
- case 2:
- return 2048;
- default:
- hw_error("SDHC: unsupported value for maximum block size\n");
- return 0;
+ if (s->sd_spec_version != 2) {
+ error_setg(errp, "Only Spec v2 is supported");
+ return;
}
+ s->version = (SDHC_HCVER_VENDOR << 8) | (s->sd_spec_version - 1);
}
+/* --- qdev common --- */
+
+#define DEFINE_SDHCI_COMMON_PROPERTIES(_state) \
+ DEFINE_PROP_UINT8("sd-spec-version", _state, sd_spec_version, 2), \
+ \
+ /* Capabilities registers provide information on supported
+ * features of this specific host controller implementation */ \
+ DEFINE_PROP_UINT64("capareg", _state, capareg, SDHC_CAPAB_REG_DEFAULT), \
+ DEFINE_PROP_UINT64("maxcurr", _state, maxcurr, 0)
+
static void sdhci_initfn(SDHCIState *s)
{
qbus_create_inplace(&s->sdbus, sizeof(s->sdbus),
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;
}
static void sdhci_uninitfn(SDHCIState *s)
timer_free(s->insert_timer);
timer_del(s->transfer_timer);
timer_free(s->transfer_timer);
- qemu_free_irq(s->eject_cb);
- qemu_free_irq(s->ro_cb);
g_free(s->fifo_buffer);
s->fifo_buffer = NULL;
}
+static void sdhci_common_realize(SDHCIState *s, Error **errp)
+{
+ Error *local_err = NULL;
+
+ sdhci_init_readonly_registers(s, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ s->buf_maxsz = sdhci_get_fifolen(s);
+ s->fifo_buffer = g_malloc0(s->buf_maxsz);
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &sdhci_mmio_ops, s, "sdhci",
+ SDHC_REGISTERS_MAP_SIZE);
+}
+
+static void sdhci_common_unrealize(SDHCIState *s, Error **errp)
+{
+ /* 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!). */
+ g_free(s->fifo_buffer);
+ s->fifo_buffer = NULL;
+}
+
+static bool sdhci_pending_insert_vmstate_needed(void *opaque)
+{
+ SDHCIState *s = opaque;
+
+ return s->pending_insert_state;
+}
+
+static const VMStateDescription sdhci_pending_insert_vmstate = {
+ .name = "sdhci/pending-insert",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = sdhci_pending_insert_vmstate_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_BOOL(pending_insert_state, SDHCIState),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
const VMStateDescription sdhci_vmstate = {
.name = "sdhci",
.version_id = 1,
VMSTATE_UINT16(data_count, SDHCIState),
VMSTATE_UINT64(admasysaddr, SDHCIState),
VMSTATE_UINT8(stopped_state, SDHCIState),
- VMSTATE_VBUFFER_UINT32(fifo_buffer, SDHCIState, 1, NULL, 0, buf_maxsz),
+ VMSTATE_VBUFFER_UINT32(fifo_buffer, SDHCIState, 1, NULL, buf_maxsz),
VMSTATE_TIMER_PTR(insert_timer, SDHCIState),
VMSTATE_TIMER_PTR(transfer_timer, SDHCIState),
VMSTATE_END_OF_LIST()
- }
+ },
+ .subsections = (const VMStateDescription*[]) {
+ &sdhci_pending_insert_vmstate,
+ NULL
+ },
};
-/* Capabilities registers provide information on supported features of this
- * specific host controller implementation */
+static void sdhci_common_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
+ dc->vmsd = &sdhci_vmstate;
+ dc->reset = sdhci_poweron_reset;
+}
+
+/* --- qdev PCI --- */
+
static Property sdhci_pci_properties[] = {
- DEFINE_PROP_UINT32("capareg", SDHCIState, capareg,
- SDHC_CAPAB_REG_DEFAULT),
- DEFINE_PROP_UINT32("maxcurr", SDHCIState, maxcurr, 0),
+ DEFINE_SDHCI_COMMON_PROPERTIES(SDHCIState),
DEFINE_PROP_END_OF_LIST(),
};
static void sdhci_pci_realize(PCIDevice *dev, Error **errp)
{
SDHCIState *s = PCI_SDHCI(dev);
+ Error *local_err = NULL;
+
+ sdhci_initfn(s);
+ sdhci_common_realize(s, errp);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
dev->config[PCI_CLASS_PROG] = 0x01; /* Standard Host supported DMA */
dev->config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin A */
- sdhci_initfn(s);
- s->buf_maxsz = sdhci_get_fifolen(s);
- s->fifo_buffer = g_malloc0(s->buf_maxsz);
s->irq = pci_allocate_irq(dev);
- memory_region_init_io(&s->iomem, OBJECT(s), &sdhci_mmio_ops, s, "sdhci",
- SDHC_REGISTERS_MAP_SIZE);
- pci_register_bar(dev, 0, 0, &s->iomem);
+ s->dma_as = pci_get_address_space(dev);
+ pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->iomem);
}
static void sdhci_pci_exit(PCIDevice *dev)
{
SDHCIState *s = PCI_SDHCI(dev);
+
+ sdhci_common_unrealize(s, &error_abort);
sdhci_uninitfn(s);
}
k->vendor_id = PCI_VENDOR_ID_REDHAT;
k->device_id = PCI_DEVICE_ID_REDHAT_SDHCI;
k->class_id = PCI_CLASS_SYSTEM_SDHCI;
- set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
- dc->vmsd = &sdhci_vmstate;
dc->props = sdhci_pci_properties;
+
+ sdhci_common_class_init(klass, data);
}
static const TypeInfo sdhci_pci_info = {
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(SDHCIState),
.class_init = sdhci_pci_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+ { },
+ },
};
+/* --- qdev SysBus --- */
+
static Property sdhci_sysbus_properties[] = {
- DEFINE_PROP_UINT32("capareg", SDHCIState, capareg,
- SDHC_CAPAB_REG_DEFAULT),
- DEFINE_PROP_UINT32("maxcurr", SDHCIState, maxcurr, 0),
- DEFINE_PROP_BOOL("noeject-quirk", SDHCIState, noeject_quirk, false),
+ 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(),
};
static void sdhci_sysbus_finalize(Object *obj)
{
SDHCIState *s = SYSBUS_SDHCI(obj);
+
+ if (s->dma_mr) {
+ object_unparent(OBJECT(s->dma_mr));
+ }
+
sdhci_uninitfn(s);
}
{
SDHCIState *s = SYSBUS_SDHCI(dev);
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
- DriveInfo *di;
- BlockBackend *blk;
- DeviceState *carddev;
- Error *err = NULL;
-
- /* Create and plug in the sd card.
- * FIXME: this should be done by the users of this device so we
- * do not use drive_get_next() here.
- */
- di = drive_get_next(IF_SD);
- blk = di ? blk_by_legacy_dinfo(di) : NULL;
+ Error *local_err = NULL;
- carddev = qdev_create(qdev_get_child_bus(dev, "sd-bus"), TYPE_SD_CARD);
- qdev_prop_set_drive(carddev, "drive", blk, &err);
- if (err) {
- error_propagate(errp, err);
+ sdhci_common_realize(s, errp);
+ if (local_err) {
+ error_propagate(errp, local_err);
return;
}
- object_property_set_bool(OBJECT(carddev), true, "realized", &err);
- if (err) {
- error_propagate(errp, err);
- return;
+
+ if (s->dma_mr) {
+ s->dma_as = &s->sysbus_dma_as;
+ address_space_init(s->dma_as, s->dma_mr, "sdhci-dma");
+ } else {
+ /* use system_memory() if property "dma" not set */
+ s->dma_as = &address_space_memory;
}
- s->buf_maxsz = sdhci_get_fifolen(s);
- s->fifo_buffer = g_malloc0(s->buf_maxsz);
sysbus_init_irq(sbd, &s->irq);
- memory_region_init_io(&s->iomem, OBJECT(s), &sdhci_mmio_ops, s, "sdhci",
+
+ memory_region_init_io(&s->iomem, OBJECT(s), s->io_ops, s, "sdhci",
SDHC_REGISTERS_MAP_SIZE);
+
sysbus_init_mmio(sbd, &s->iomem);
}
+static void sdhci_sysbus_unrealize(DeviceState *dev, Error **errp)
+{
+ SDHCIState *s = SYSBUS_SDHCI(dev);
+
+ sdhci_common_unrealize(s, &error_abort);
+
+ if (s->dma_mr) {
+ address_space_destroy(s->dma_as);
+ }
+}
+
static void sdhci_sysbus_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- dc->vmsd = &sdhci_vmstate;
dc->props = sdhci_sysbus_properties;
dc->realize = sdhci_sysbus_realize;
- /* Reason: instance_init() method uses drive_get_next() */
- dc->cannot_instantiate_with_device_add_yet = true;
+ dc->unrealize = sdhci_sysbus_unrealize;
+
+ sdhci_common_class_init(klass, data);
}
static const TypeInfo sdhci_sysbus_info = {
.class_init = sdhci_sysbus_class_init,
};
+/* --- qdev bus master --- */
+
static void sdhci_bus_class_init(ObjectClass *klass, void *data)
{
SDBusClass *sbc = SD_BUS_CLASS(klass);
.class_init = sdhci_bus_class_init,
};
+static uint64_t usdhc_read(void *opaque, hwaddr offset, unsigned size)
+{
+ SDHCIState *s = SYSBUS_SDHCI(opaque);
+ uint32_t ret;
+ uint16_t hostctl;
+
+ switch (offset) {
+ default:
+ return sdhci_read(opaque, offset, size);
+
+ case SDHC_HOSTCTL:
+ /*
+ * For a detailed explanation on the following bit
+ * manipulation code see comments in a similar part of
+ * usdhc_write()
+ */
+ hostctl = SDHC_DMA_TYPE(s->hostctl) << (8 - 3);
+
+ if (s->hostctl & SDHC_CTRL_8BITBUS) {
+ hostctl |= ESDHC_CTRL_8BITBUS;
+ }
+
+ if (s->hostctl & SDHC_CTRL_4BITBUS) {
+ hostctl |= ESDHC_CTRL_4BITBUS;
+ }
+
+ ret = hostctl;
+ ret |= (uint32_t)s->blkgap << 16;
+ ret |= (uint32_t)s->wakcon << 24;
+
+ break;
+
+ case ESDHC_DLL_CTRL:
+ case ESDHC_TUNE_CTRL_STATUS:
+ case ESDHC_UNDOCUMENTED_REG27:
+ case ESDHC_TUNING_CTRL:
+ case ESDHC_VENDOR_SPEC:
+ case ESDHC_MIX_CTRL:
+ case ESDHC_WTMK_LVL:
+ ret = 0;
+ break;
+ }
+
+ return ret;
+}
+
+static void
+usdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
+{
+ SDHCIState *s = SYSBUS_SDHCI(opaque);
+ uint8_t hostctl;
+ 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 ESDHC_VENDOR_SPEC:
+ break;
+
+ case SDHC_HOSTCTL:
+ /*
+ * Here's What ESDHCI has at offset 0x28 (SDHC_HOSTCTL)
+ *
+ * 7 6 5 4 3 2 1 0
+ * |-----------+--------+--------+-----------+----------+---------|
+ * | Card | Card | Endian | DATA3 | Data | Led |
+ * | Detect | Detect | Mode | as Card | Transfer | Control |
+ * | Signal | Test | | Detection | Width | |
+ * | Selection | Level | | Pin | | |
+ * |-----------+--------+--------+-----------+----------+---------|
+ *
+ * and 0x29
+ *
+ * 15 10 9 8
+ * |----------+------|
+ * | Reserved | DMA |
+ * | | Sel. |
+ * | | |
+ * |----------+------|
+ *
+ * and here's what SDCHI spec expects those offsets to be:
+ *
+ * 0x28 (Host Control Register)
+ *
+ * 7 6 5 4 3 2 1 0
+ * |--------+--------+----------+------+--------+----------+---------|
+ * | Card | Card | Extended | DMA | High | Data | LED |
+ * | Detect | Detect | Data | Sel. | Speed | Transfer | Control |
+ * | Signal | Test | Transfer | | Enable | Width | |
+ * | Sel. | Level | Width | | | | |
+ * |--------+--------+----------+------+--------+----------+---------|
+ *
+ * and 0x29 (Power Control Register)
+ *
+ * |----------------------------------|
+ * | Power Control Register |
+ * | |
+ * | Description omitted, |
+ * | since it has no analog in ESDHCI |
+ * | |
+ * |----------------------------------|
+ *
+ * Since offsets 0x2A and 0x2B should be compatible between
+ * both IP specs we only need to reconcile least 16-bit of the
+ * word we've been given.
+ */
+
+ /*
+ * First, save bits 7 6 and 0 since they are identical
+ */
+ hostctl = value & (SDHC_CTRL_LED |
+ SDHC_CTRL_CDTEST_INS |
+ SDHC_CTRL_CDTEST_EN);
+ /*
+ * Second, split "Data Transfer Width" from bits 2 and 1 in to
+ * bits 5 and 1
+ */
+ if (value & ESDHC_CTRL_8BITBUS) {
+ hostctl |= SDHC_CTRL_8BITBUS;
+ }
+
+ if (value & ESDHC_CTRL_4BITBUS) {
+ hostctl |= ESDHC_CTRL_4BITBUS;
+ }
+
+ /*
+ * Third, move DMA select from bits 9 and 8 to bits 4 and 3
+ */
+ hostctl |= SDHC_DMA_TYPE(value >> (8 - 3));
+
+ /*
+ * Now place the corrected value into low 16-bit of the value
+ * we are going to give standard SDHCI write function
+ *
+ * NOTE: This transformation should be the inverse of what can
+ * be found in drivers/mmc/host/sdhci-esdhc-imx.c in Linux
+ * kernel
+ */
+ value &= ~UINT16_MAX;
+ value |= hostctl;
+ value |= (uint16_t)s->pwrcon << 8;
+
+ sdhci_write(opaque, offset, value, size);
+ break;
+
+ case ESDHC_MIX_CTRL:
+ /*
+ * So, when SD/MMC stack in Linux tries to write to "Transfer
+ * Mode Register", ESDHC i.MX quirk code will translate it
+ * into a write to ESDHC_MIX_CTRL, so we do the opposite in
+ * order to get where we started
+ *
+ * Note that Auto CMD23 Enable bit is located in a wrong place
+ * 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
+ * sdhci_send_command(s) which we don't want.
+ *
+ */
+ s->trnmod = value & UINT16_MAX;
+ break;
+ case SDHC_TRNMOD:
+ /*
+ * Similar to above, but this time a write to "Command
+ * Register" will be translated into a 4-byte write to
+ * "Transfer Mode register" where lower 16-bit of value would
+ * be set to zero. So what we do is fill those bits with
+ * cached value from s->trnmod and let the SDHCI
+ * infrastructure handle the rest
+ */
+ sdhci_write(opaque, offset, val | s->trnmod, size);
+ break;
+ case SDHC_BLKSIZE:
+ /*
+ * ESDHCI does not implement "Host SDMA Buffer Boundary", and
+ * Linux driver will try to zero this field out which will
+ * break the rest of SDHCI emulation.
+ *
+ * Linux defaults to maximum possible setting (512K boundary)
+ * and it seems to be the only option that i.MX IP implements,
+ * so we artificially set it to that value.
+ */
+ val |= 0x7 << 12;
+ /* FALLTHROUGH */
+ default:
+ sdhci_write(opaque, offset, val, size);
+ break;
+ }
+}
+
+
+static const MemoryRegionOps usdhc_mmio_ops = {
+ .read = usdhc_read,
+ .write = usdhc_write,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ .unaligned = false
+ },
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void imx_usdhc_init(Object *obj)
+{
+ SDHCIState *s = SYSBUS_SDHCI(obj);
+
+ s->io_ops = &usdhc_mmio_ops;
+ 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,
+};
+
static void sdhci_register_types(void)
{
type_register_static(&sdhci_pci_info);
type_register_static(&sdhci_sysbus_info);
type_register_static(&sdhci_bus_info);
+ type_register_static(&imx_usdhc_info);
}
type_init(sdhci_register_types)