X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/c5bbcaa4b7c0f8a322bebe9ec563560178a68b55..0efc9511aaa1bacb9dd9868a5f463180af5aa782:/hw/sd/sdhci.c diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index b064a087c9..1b828b104d 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -23,104 +23,205 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.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" #include "qemu/log.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/cutils.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 MASK_TRNMOD 0x0037 -#define MASKED_WRITE(reg, mask, val) (reg = (reg & (mask)) | (val)) +#define SDHC_CAPAB_REG_DEFAULT 0x057834b4 + +static inline unsigned int sdhci_get_fifolen(SDHCIState *s) +{ + return 1 << (9 + FIELD_EX32(s->capareg, SDHC_CAPAB, MAXBLOCKLENGTH)); +} + +/* return true on error */ +static bool sdhci_check_capab_freq_range(SDHCIState *s, const char *desc, + uint8_t freq, Error **errp) +{ + if (s->sd_spec_version >= 3) { + return false; + } + switch (freq) { + case 0: + case 10 ... 63: + break; + default: + error_setg(errp, "SD %s clock frequency can have value" + "in range 0-63 only", desc); + return true; + } + return false; +} + +static void sdhci_check_capareg(SDHCIState *s, Error **errp) +{ + uint64_t msk = s->capareg; + uint32_t val; + bool y; + + switch (s->sd_spec_version) { + case 4: + val = FIELD_EX64(s->capareg, SDHC_CAPAB, BUS64BIT_V4); + trace_sdhci_capareg("64-bit system bus (v4)", val); + msk = FIELD_DP64(msk, SDHC_CAPAB, BUS64BIT_V4, 0); + + val = FIELD_EX64(s->capareg, SDHC_CAPAB, UHS_II); + trace_sdhci_capareg("UHS-II", val); + msk = FIELD_DP64(msk, SDHC_CAPAB, UHS_II, 0); + + val = FIELD_EX64(s->capareg, SDHC_CAPAB, ADMA3); + trace_sdhci_capareg("ADMA3", val); + msk = FIELD_DP64(msk, SDHC_CAPAB, ADMA3, 0); + + /* fallthrough */ + case 3: + val = FIELD_EX64(s->capareg, SDHC_CAPAB, ASYNC_INT); + trace_sdhci_capareg("async interrupt", val); + msk = FIELD_DP64(msk, SDHC_CAPAB, ASYNC_INT, 0); + + val = FIELD_EX64(s->capareg, SDHC_CAPAB, SLOT_TYPE); + if (val) { + error_setg(errp, "slot-type not supported"); + return; + } + trace_sdhci_capareg("slot type", val); + msk = FIELD_DP64(msk, SDHC_CAPAB, SLOT_TYPE, 0); + + if (val != 2) { + val = FIELD_EX64(s->capareg, SDHC_CAPAB, EMBEDDED_8BIT); + trace_sdhci_capareg("8-bit bus", val); + } + msk = FIELD_DP64(msk, SDHC_CAPAB, EMBEDDED_8BIT, 0); + + val = FIELD_EX64(s->capareg, SDHC_CAPAB, BUS_SPEED); + trace_sdhci_capareg("bus speed mask", val); + msk = FIELD_DP64(msk, SDHC_CAPAB, BUS_SPEED, 0); + + val = FIELD_EX64(s->capareg, SDHC_CAPAB, DRIVER_STRENGTH); + trace_sdhci_capareg("driver strength mask", val); + msk = FIELD_DP64(msk, SDHC_CAPAB, DRIVER_STRENGTH, 0); + + val = FIELD_EX64(s->capareg, SDHC_CAPAB, TIMER_RETUNING); + trace_sdhci_capareg("timer re-tuning", val); + msk = FIELD_DP64(msk, SDHC_CAPAB, TIMER_RETUNING, 0); + + val = FIELD_EX64(s->capareg, SDHC_CAPAB, SDR50_TUNING); + trace_sdhci_capareg("use SDR50 tuning", val); + msk = FIELD_DP64(msk, SDHC_CAPAB, SDR50_TUNING, 0); + + val = FIELD_EX64(s->capareg, SDHC_CAPAB, RETUNING_MODE); + trace_sdhci_capareg("re-tuning mode", val); + msk = FIELD_DP64(msk, SDHC_CAPAB, RETUNING_MODE, 0); + + val = FIELD_EX64(s->capareg, SDHC_CAPAB, CLOCK_MULT); + trace_sdhci_capareg("clock multiplier", val); + msk = FIELD_DP64(msk, SDHC_CAPAB, CLOCK_MULT, 0); + + /* fallthrough */ + case 2: /* default version */ + val = FIELD_EX64(s->capareg, SDHC_CAPAB, ADMA2); + trace_sdhci_capareg("ADMA2", val); + msk = FIELD_DP64(msk, SDHC_CAPAB, ADMA2, 0); + + val = FIELD_EX64(s->capareg, SDHC_CAPAB, ADMA1); + trace_sdhci_capareg("ADMA1", val); + msk = FIELD_DP64(msk, SDHC_CAPAB, ADMA1, 0); + + val = FIELD_EX64(s->capareg, SDHC_CAPAB, BUS64BIT); + trace_sdhci_capareg("64-bit system bus (v3)", val); + msk = FIELD_DP64(msk, SDHC_CAPAB, BUS64BIT, 0); + + /* fallthrough */ + case 1: + y = FIELD_EX64(s->capareg, SDHC_CAPAB, TOUNIT); + msk = FIELD_DP64(msk, SDHC_CAPAB, TOUNIT, 0); + + val = FIELD_EX64(s->capareg, SDHC_CAPAB, TOCLKFREQ); + trace_sdhci_capareg(y ? "timeout (MHz)" : "Timeout (KHz)", val); + if (sdhci_check_capab_freq_range(s, "timeout", val, errp)) { + return; + } + msk = FIELD_DP64(msk, SDHC_CAPAB, TOCLKFREQ, 0); + + val = FIELD_EX64(s->capareg, SDHC_CAPAB, BASECLKFREQ); + trace_sdhci_capareg(y ? "base (MHz)" : "Base (KHz)", val); + if (sdhci_check_capab_freq_range(s, "base", val, errp)) { + return; + } + msk = FIELD_DP64(msk, SDHC_CAPAB, BASECLKFREQ, 0); + + val = FIELD_EX64(s->capareg, SDHC_CAPAB, MAXBLOCKLENGTH); + if (val >= 3) { + error_setg(errp, "block size can be 512, 1024 or 2048 only"); + return; + } + trace_sdhci_capareg("max block length", sdhci_get_fifolen(s)); + msk = FIELD_DP64(msk, SDHC_CAPAB, MAXBLOCKLENGTH, 0); + + val = FIELD_EX64(s->capareg, SDHC_CAPAB, HIGHSPEED); + trace_sdhci_capareg("high speed", val); + msk = FIELD_DP64(msk, SDHC_CAPAB, HIGHSPEED, 0); + + val = FIELD_EX64(s->capareg, SDHC_CAPAB, SDMA); + trace_sdhci_capareg("SDMA", val); + msk = FIELD_DP64(msk, SDHC_CAPAB, SDMA, 0); + + val = FIELD_EX64(s->capareg, SDHC_CAPAB, SUSPRESUME); + trace_sdhci_capareg("suspend/resume", val); + msk = FIELD_DP64(msk, SDHC_CAPAB, SUSPRESUME, 0); + + val = FIELD_EX64(s->capareg, SDHC_CAPAB, V33); + trace_sdhci_capareg("3.3v", val); + msk = FIELD_DP64(msk, SDHC_CAPAB, V33, 0); + + val = FIELD_EX64(s->capareg, SDHC_CAPAB, V30); + trace_sdhci_capareg("3.0v", val); + msk = FIELD_DP64(msk, SDHC_CAPAB, V30, 0); + + val = FIELD_EX64(s->capareg, SDHC_CAPAB, V18); + trace_sdhci_capareg("1.8v", val); + msk = FIELD_DP64(msk, SDHC_CAPAB, V18, 0); + break; + + default: + error_setg(errp, "Unsupported spec version: %u", s->sd_spec_version); + } + if (msk) { + qemu_log_mask(LOG_UNIMP, + "SDHCI: unknown CAPAB mask: 0x%016" PRIx64 "\n", msk); + } +} static uint8_t sdhci_slotint(SDHCIState *s) { @@ -153,8 +254,8 @@ static void sdhci_raise_insertion_irq(void *opaque) 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, @@ -195,7 +296,8 @@ static void sdhci_reset(SDHCIState *s) 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); @@ -236,7 +338,8 @@ static void sdhci_send_command(SDHCIState *s) 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) { @@ -244,7 +347,7 @@ static void sdhci_send_command(SDHCIState *s) 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]; @@ -254,18 +357,18 @@ static void sdhci_send_command(SDHCIState *s) (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; } @@ -292,7 +395,7 @@ static void sdhci_end_transfer(SDHCIState *s) 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) | @@ -313,19 +416,35 @@ static void sdhci_end_transfer(SDHCIState *s) /* * Programmed i/o data transfer */ +#define BLOCK_SIZE_MASK (4 * K_BYTE - 1) /* Fill host controller's read buffer with BLKSIZE bytes of data from card */ static void sdhci_read_block_from_card(SDHCIState *s) { int index = 0; + uint8_t data; + const uint16_t blk_size = s->blksize & BLOCK_SIZE_MASK; if ((s->trnmod & SDHC_TRNS_MULTI) && (s->trnmod & SDHC_TRNS_BLK_CNT_EN) && (s->blkcnt == 0)) { return; } - for (index = 0; index < (s->blksize & 0x0fff); index++) { - s->fifo_buffer[index] = sdbus_read_data(&s->sdbus); + for (index = 0; index < blk_size; index++) { + data = sdbus_read_data(&s->sdbus); + if (!FIELD_EX32(s->hostctl2, SDHC_HOSTCTL2, EXECUTE_TUNING)) { + /* Device is not in tuning */ + s->fifo_buffer[index] = data; + } + } + + if (FIELD_EX32(s->hostctl2, SDHC_HOSTCTL2, EXECUTE_TUNING)) { + /* Device is in tuning */ + s->hostctl2 &= ~R_SDHC_HOSTCTL2_EXECUTE_TUNING_MASK; + s->hostctl2 |= R_SDHC_HOSTCTL2_SAMPLING_CLKSEL_MASK; + s->prnsts &= ~(SDHC_DAT_LINE_ACTIVE | SDHC_DOING_READ | + SDHC_DATA_INHIBIT); + goto read_done; } /* New data now available for READ through Buffer Port Register */ @@ -350,6 +469,7 @@ static void sdhci_read_block_from_card(SDHCIState *s) } } +read_done: sdhci_update_irq(s); } @@ -361,7 +481,7 @@ static uint32_t sdhci_read_dataport(SDHCIState *s, unsigned size) /* 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; } @@ -369,9 +489,8 @@ static uint32_t sdhci_read_dataport(SDHCIState *s, unsigned size) value |= s->fifo_buffer[s->data_count] << i * 8; 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); + if ((s->data_count) >= (s->blksize & BLOCK_SIZE_MASK)) { + 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] */ @@ -417,7 +536,7 @@ static void sdhci_write_block_to_card(SDHCIState *s) } } - for (index = 0; index < (s->blksize & 0x0fff); index++) { + for (index = 0; index < (s->blksize & BLOCK_SIZE_MASK); index++) { sdbus_write_data(&s->sdbus, s->fifo_buffer[index]); } @@ -454,7 +573,7 @@ static void sdhci_write_dataport(SDHCIState *s, uint32_t value, unsigned size) /* 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; } @@ -462,9 +581,8 @@ static void sdhci_write_dataport(SDHCIState *s, uint32_t value, unsigned size) s->fifo_buffer[s->data_count] = value & 0xFF; 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); + if (s->data_count >= (s->blksize & BLOCK_SIZE_MASK)) { + trace_sdhci_write_dataport(s->data_count); s->data_count = 0; s->prnsts &= ~SDHC_SPACE_AVAILABLE; if (s->prnsts & SDHC_DOING_WRITE) { @@ -483,8 +601,8 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s) { bool page_aligned = false; unsigned int n, begin; - const uint16_t block_size = s->blksize & 0x0fff; - uint32_t boundary_chk = 1 << (((s->blksize & 0xf000) >> 12) + 12); + const uint16_t block_size = s->blksize & BLOCK_SIZE_MASK; + uint32_t boundary_chk = 1 << (((s->blksize & ~BLOCK_SIZE_MASK) >> 12) + 12); uint32_t boundary_count = boundary_chk - (s->sdmasysad % boundary_chk); if (!(s->trnmod & SDHC_TRNS_BLK_CNT_EN) || !s->blkcnt) { @@ -519,7 +637,7 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s) 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) { @@ -541,7 +659,7 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s) s->data_count = block_size; boundary_count -= block_size - begin; } - dma_memory_read(&address_space_memory, s->sdmasysad, + 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) { @@ -573,17 +691,15 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s) static void sdhci_sdma_transfer_single_block(SDHCIState *s) { int n; - uint32_t datacnt = s->blksize & 0x0fff; + uint32_t datacnt = s->blksize & BLOCK_SIZE_MASK; if (s->trnmod & SDHC_TRNS_READ) { 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]); } @@ -605,9 +721,9 @@ static void get_adma_description(SDHCIState *s, ADMADescr *dscr) uint32_t adma1 = 0; uint64_t adma2 = 0; hwaddr entry_addr = (hwaddr)s->admasysaddr; - switch (SDHC_DMA_TYPE(s->hostctl)) { + switch (SDHC_DMA_TYPE(s->hostctl1)) { 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. @@ -619,7 +735,7 @@ static void get_adma_description(SDHCIState *s, ADMADescr *dscr) 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); @@ -632,15 +748,15 @@ static void get_adma_description(SDHCIState *s, ADMADescr *dscr) } 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; + dscr->addr = le64_to_cpu(dscr->addr); + dscr->attr &= (uint8_t) ~0xC0; dscr->incr = 12; break; } @@ -651,16 +767,15 @@ static void get_adma_description(SDHCIState *s, ADMADescr *dscr) static void sdhci_do_adma(SDHCIState *s) { unsigned int n, begin, length; - const uint16_t block_size = s->blksize & 0x0fff; - ADMADescr dscr; + const uint16_t block_size = s->blksize & BLOCK_SIZE_MASK; + 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 */ @@ -697,7 +812,7 @@ static void sdhci_do_adma(SDHCIState *s) 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; @@ -721,7 +836,7 @@ static void sdhci_do_adma(SDHCIState *s) 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; @@ -743,8 +858,7 @@ static void sdhci_do_adma(SDHCIState *s) 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; @@ -752,8 +866,7 @@ static void sdhci_do_adma(SDHCIState *s) } 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; } @@ -764,15 +877,15 @@ static void sdhci_do_adma(SDHCIState *s) /* 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; } @@ -797,7 +910,7 @@ static void sdhci_data_transfer(void *opaque) SDHCIState *s = (SDHCIState *)opaque; if (s->trnmod & SDHC_TRNS_DMA) { - switch (SDHC_DMA_TYPE(s->hostctl)) { + 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); @@ -807,32 +920,32 @@ static void sdhci_data_transfer(void *opaque) break; case SDHC_CTRL_ADMA1_32: - if (!(s->capareg & SDHC_CAN_DO_ADMA1)) { - ERRPRINT("ADMA1 not supported\n"); + if (!(s->capareg & R_SDHC_CAPAB_ADMA1_MASK)) { + trace_sdhci_error("ADMA1 not supported"); break; } sdhci_do_adma(s); break; case SDHC_CTRL_ADMA2_32: - if (!(s->capareg & SDHC_CAN_DO_ADMA2)) { - ERRPRINT("ADMA2 not supported\n"); + if (!(s->capareg & R_SDHC_CAPAB_ADMA2_MASK)) { + trace_sdhci_error("ADMA2 not supported"); break; } sdhci_do_adma(s); 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"); + if (!(s->capareg & R_SDHC_CAPAB_ADMA2_MASK) || + !(s->capareg & R_SDHC_CAPAB_BUS64BIT_MASK)) { + 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 { @@ -867,8 +980,8 @@ static inline bool 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; @@ -898,16 +1011,19 @@ static uint64_t sdhci_read(void *opaque, hwaddr offset, unsigned size) 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_PRNSTS: ret = s->prnsts; + ret = FIELD_DP32(ret, SDHC_PRNSTS, DAT_LVL, + sdbus_get_dat_lines(&s->sdbus)); + ret = FIELD_DP32(ret, SDHC_PRNSTS, CMD_LVL, + sdbus_get_cmd_line(&s->sdbus)); break; case SDHC_HOSTCTL: - ret = s->hostctl | (s->pwrcon << 8) | (s->blkgap << 16) | + ret = s->hostctl1 | (s->pwrcon << 8) | (s->blkgap << 16) | (s->wakcon << 24); break; case SDHC_CLKCON: @@ -923,13 +1039,19 @@ static uint64_t sdhci_read(void *opaque, hwaddr offset, unsigned size) ret = s->norintsigen | (s->errintsigen << 16); break; case SDHC_ACMD12ERRSTS: - ret = s->acmd12errsts; + ret = s->acmd12errsts | (s->hostctl2 << 16); 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; @@ -941,16 +1063,17 @@ static uint64_t sdhci_read(void *opaque, hwaddr offset, unsigned size) 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; } @@ -1018,7 +1141,7 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) MASKED_WRITE(s->sdmasysad, mask, value); /* 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) { + s->blksize && SDHC_DMA_TYPE(s->hostctl1) == SDHC_CTRL_SDMA) { if (s->trnmod & SDHC_TRNS_MULTI) { sdhci_sdma_transfer_multi_blocks(s); } else { @@ -1048,10 +1171,10 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) case SDHC_TRNMOD: /* DMA can be enabled only if it is supported as indicated by * capabilities register */ - if (!(s->capareg & SDHC_CAN_DO_DMA)) { + if (!(s->capareg & R_SDHC_CAPAB_SDMA_MASK)) { value &= ~SDHC_TRNS_DMA; } - MASKED_WRITE(s->trnmod, mask, value & MASK_TRNMOD); + 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 */ @@ -1070,7 +1193,7 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) if (!(mask & 0xFF0000)) { sdhci_blkgap_write(s, value >> 16); } - MASKED_WRITE(s->hostctl, mask, value); + MASKED_WRITE(s->hostctl1, mask, value); MASKED_WRITE(s->pwrcon, mask >> 8, value >> 8); MASKED_WRITE(s->wakcon, mask >> 24, value >> 24); if (!(s->prnsts & SDHC_CARD_PRESENT) || ((s->pwrcon >> 1) & 0x7) < 5 || @@ -1149,13 +1272,34 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) } sdhci_update_irq(s); break; + case SDHC_ACMD12ERRSTS: + MASKED_WRITE(s->acmd12errsts, mask, value & UINT16_MAX); + if (s->uhs_mode >= UHS_I) { + MASKED_WRITE(s->hostctl2, mask >> 16, value >> 16); + + if (FIELD_EX32(s->hostctl2, SDHC_HOSTCTL2, V18_ENA)) { + sdbus_set_voltage(&s->sdbus, SD_VOLTAGE_1_8V); + } else { + sdbus_set_voltage(&s->sdbus, SD_VOLTAGE_3_3V); + } + } + 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 = { @@ -1169,21 +1313,37 @@ 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; + Error *local_err = NULL; + + switch (s->sd_spec_version) { + case 2 ... 3: + break; default: - hw_error("SDHC: unsupported value for maximum block size\n"); - return 0; + error_setg(errp, "Only Spec v2/v3 are supported"); + return; + } + s->version = (SDHC_HCVER_VENDOR << 8) | (s->sd_spec_version - 1); + + sdhci_check_capareg(s, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; } } +/* --- qdev common --- */ + +#define DEFINE_SDHCI_COMMON_PROPERTIES(_state) \ + DEFINE_PROP_UINT8("sd-spec-version", _state, sd_spec_version, 2), \ + DEFINE_PROP_UINT8("uhs", _state, uhs_mode, UHS_NOT_SUPPORTED), \ + \ + /* 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), @@ -1191,6 +1351,8 @@ static void sdhci_initfn(SDHCIState *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_ops; } static void sdhci_uninitfn(SDHCIState *s) @@ -1199,13 +1361,38 @@ 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; @@ -1237,7 +1424,7 @@ const VMStateDescription sdhci_vmstate = { VMSTATE_UINT16(cmdreg, SDHCIState), VMSTATE_UINT32_ARRAY(rspreg, SDHCIState, 4), VMSTATE_UINT32(prnsts, SDHCIState), - VMSTATE_UINT8(hostctl, SDHCIState), + VMSTATE_UINT8(hostctl1, SDHCIState), VMSTATE_UINT8(pwrcon, SDHCIState), VMSTATE_UINT8(blkgap, SDHCIState), VMSTATE_UINT8(wakcon, SDHCIState), @@ -1265,32 +1452,46 @@ const VMStateDescription sdhci_vmstate = { }, }; -/* 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); } @@ -1304,10 +1505,9 @@ static void sdhci_pci_class_init(ObjectClass *klass, void *data) 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; - dc->reset = sdhci_poweron_reset; + + sdhci_common_class_init(klass, data); } static const TypeInfo sdhci_pci_info = { @@ -1321,12 +1521,14 @@ static const TypeInfo sdhci_pci_info = { }, }; +/* --- 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_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(), }; @@ -1340,6 +1542,11 @@ static void sdhci_sysbus_init(Object *obj) 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); } @@ -1347,23 +1554,50 @@ static void sdhci_sysbus_realize(DeviceState *dev, Error ** errp) { SDHCIState *s = SYSBUS_SDHCI(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Error *local_err = NULL; + + sdhci_common_realize(s, errp); + if (local_err) { + error_propagate(errp, local_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; - dc->reset = sdhci_poweron_reset; + dc->unrealize = sdhci_sysbus_unrealize; + + sdhci_common_class_init(klass, data); } static const TypeInfo sdhci_sysbus_info = { @@ -1375,6 +1609,8 @@ 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); @@ -1390,11 +1626,232 @@ static const TypeInfo sdhci_bus_info = { .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 hostctl1; + + 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() + */ + hostctl1 = SDHC_DMA_TYPE(s->hostctl1) << (8 - 3); + + if (s->hostctl1 & SDHC_CTRL_8BITBUS) { + hostctl1 |= ESDHC_CTRL_8BITBUS; + } + + if (s->hostctl1 & SDHC_CTRL_4BITBUS) { + hostctl1 |= ESDHC_CTRL_4BITBUS; + } + + ret = hostctl1; + 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 hostctl1; + 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 + */ + hostctl1 = 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) { + hostctl1 |= SDHC_CTRL_8BITBUS; + } + + if (value & ESDHC_CTRL_4BITBUS) { + hostctl1 |= ESDHC_CTRL_4BITBUS; + } + + /* + * Third, move DMA select from bits 9 and 8 to bits 4 and 3 + */ + hostctl1 |= 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 |= hostctl1; + 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)