Merge tag 'sdmmc-20240702' of https://github.com/philmd/qemu into staging
SD/MMC patches queue
- Deprecate SD spec v1.10
- Improve tracing
- Fix endianness on DAT lines
- Introduce helpers for commands transmitting data on the DAT lines
- Convert most commands to new helpers
- Register various optional commands
- Add command {name, type, class} fields to SDProto structure
# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEE+qvnXhKRciHc/Wuy4+MsLN6twN4FAmaDtrEACgkQ4+MsLN6t
# wN7wABAAjjo+8ZYTxyh+P5oPvv4ec0SLFyTzOyDlzl/8rif9l54UP1kvBGtDYBaF
# hdHM/BguTQVdomObCrjmcXLmb/+ZnQ23x8Bn8rm1mqjXmV8UvuZ9KReAhQeEgBNN
# rtWClQqgtuBAbhh2uh0zNmwPhzCavSostd5nQtJXvUfCZUIyTmUBqUNFCZc+RkGy
# q6kUDzlgrH7uLzVMU5M6QvmOT1LbmWGQAdV+zWXBKXEBgysxzmm6bC7wRCoN+wPQ
# 9OtdHEZsxEDaIJFK7YcDu0lCOsGFzu62d8N7TA/v4nIWaxrppm4FK7kxgmsO15uY
# jEq3p2PbDj6UVYrqGpuw4qMcJ0Cs6dkGEfCw9eqwYyhAPd/Y+s/NBAgc6BieZWj6
# D32goEWykLorxu9sPGgR9qNWo7gbHanMo4ps3v4++D8Qtb3CpANXDIC5H1WR9azH
# tdiepDx0nW9bSo/hE32bL70TA8noqUCsrr1yIN7eydpJ2ZTa2ywMfIifLnowoOD2
# mPQ/QvUsAwJdjGXsz21loQdycOsuBmqZ4bKU45ez3bxKdq1LFC4q6xsVPeS0UVFy
# 6dSj2Bi44Y+Ub/UoNij2/6WzuIfNtSKXrs47et2v2SFjuPJt0g34cv3mLCz6CQQZ
# 2WFk42vyGSZ9gmWmt+G64nksQlifVWnGQP6jub9ThQ5rxqzFTU0=
# =vqY+
# -----END PGP SIGNATURE-----
# gpg: Signature made Tue 02 Jul 2024 01:13:37 AM PDT
# gpg: using RSA key FAABE75E12917221DCFD6BB2E3E32C2CDEADC0DE
# gpg: Good signature from "Philippe Mathieu-Daudé (F4BUG) <f4bug@amsat.org>" [full]
* tag 'sdmmc-20240702' of https://github.com/philmd/qemu: (67 commits)
hw/sd/sdcard: Add sd_acmd_SEND_SCR handler (ACMD51)
hw/sd/sdcard: Add sd_acmd_SET_CLR_CARD_DETECT handler (ACMD42)
hw/sd/sdcard: Add sd_acmd_SD_APP_OP_COND handler (ACMD41)
hw/sd/sdcard: Add sd_acmd_SET_WR_BLK_ERASE_COUNT handler (ACMD23)
hw/sd/sdcard: Add sd_acmd_SEND_NUM_WR_BLOCKS handler (ACMD22)
hw/sd/sdcard: Add sd_acmd_SD_STATUS handler (ACMD13)
hw/sd/sdcard: Add sd_acmd_SET_BUS_WIDTH handler (ACMD6)
hw/sd/sdcard: Add spi_cmd_CRC_ON_OFF handler (CMD59)
hw/sd/sdcard: Add spi_cmd_READ_OCR handler (CMD58)
hw/sd/sdcard: Add sd_cmd_APP_CMD handler (CMD55)
hw/sd/sdcard: Add sd_cmd_LOCK_UNLOCK handler (CMD42)
hw/sd/sdcard: Add sd_cmd_ERASE handler (CMD38)
hw/sd/sdcard: Add sd_cmd_ERASE_WR_BLK_START/END handlers (CMD32 & CMD33)
hw/sd/sdcard: Add sd_cmd_SEND_WRITE_PROT handler (CMD30)
hw/sd/sdcard: Add sd_cmd_SET/CLR_WRITE_PROT handler (CMD28 & CMD29)
hw/sd/sdcard: Add sd_cmd_PROGRAM_CSD handler (CMD27)
hw/sd/sdcard: Add sd_cmd_WRITE_SINGLE_BLOCK handler (CMD24)
hw/sd/sdcard: Add sd_cmd_READ_SINGLE_BLOCK handler (CMD17)
hw/sd/sdcard: Add sd_cmd_SET_BLOCKLEN handler (CMD16)
hw/sd/sdcard: Add sd_cmd_GO_INACTIVE_STATE handler (CMD15)
...
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst
index 5ab4028..20b7a17 100644
--- a/docs/about/deprecated.rst
+++ b/docs/about/deprecated.rst
@@ -383,6 +383,12 @@
- "Zve64f" should be replaced with "zve64f"
- "Zve64d" should be replaced with "zve64d"
+``-device sd-card,spec_version=1`` (since 9.1)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+SD physical layer specification v2.00 supersedes the v1.10 one.
+v2.00 is the default since QEMU 3.0.0.
+
Block device options
''''''''''''''''''''
diff --git a/hw/sd/meson.build b/hw/sd/meson.build
index abfac9e..bbb75af 100644
--- a/hw/sd/meson.build
+++ b/hw/sd/meson.build
@@ -1,5 +1,5 @@
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'))
diff --git a/hw/sd/sd.c b/hw/sd/sd.c
index a48010c..552957b 100644
--- a/hw/sd/sd.c
+++ b/hw/sd/sd.c
@@ -75,24 +75,30 @@
};
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_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,
};
+#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 {
@@ -116,6 +122,8 @@
uint8_t spec_version;
BlockBackend *blk;
+ const SDProto *proto;
+
/* Runtime changeables */
uint32_t mode; /* current card mode, one of SDCardModes */
@@ -133,13 +141,16 @@
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;
@@ -151,18 +162,11 @@
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 bool sd_is_spi(SDState *sd)
{
- return sd_proto(sd) == &sd_proto_spi;
+ return sd->proto == &sd_proto_spi;
}
static const char *sd_version_str(enum SDPhySpecificationVersion version)
@@ -230,6 +234,48 @@
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",
+ [21] = "DPS_spec",
+ [25] = "WRITE_MULTIPLE_BLOCK",
+ [26] = "MANUF_RSVD",
+ [40] = "DPS_spec",
+ [56] = "GEN_CMD",
+ [60] = "MANUF_RSVD", [61] = "MANUF_RSVD",
+ [62] = "MANUF_RSVD", [63] = "MANUF_RSVD",
+ };
+ 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)
+{
+ static const char *acmd_abbrev[SDMMC_CMD_MAX] = {
+ [14] = "DPS_spec", [15] = "DPS_spec",
+ [16] = "DPS_spec",
+ [18] = "SECU_spec",
+ [52] = "SECU_spec", [53] = "SECU_spec",
+ [54] = "SECU_spec",
+ [56] = "SECU_spec", [57] = "SECU_spec",
+ [58] = "SECU_spec", [59] = "SECU_spec",
+ };
+ const SDProto *sdp = sd->proto;
+
+ if (sdp->acmd[cmd].handler) {
+ assert(!acmd_abbrev[cmd]);
+ return sdp->acmd[cmd].name;
+ }
+
+ return acmd_abbrev[cmd] ? acmd_abbrev[cmd] : "UNKNOWN_ACMD";
+}
+
static uint8_t sd_get_dat_lines(SDState *sd)
{
return sd->enable ? sd->dat_lines : 0;
@@ -278,27 +324,6 @@
}
}
-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;
@@ -497,10 +522,24 @@
static uint16_t sd_req_get_rca(SDState *s, SDRequest req)
{
- if (sd_cmd_type[req.cmd] == sd_ac || sd_cmd_type[req.cmd] == sd_adtc) {
+ switch (s->proto->cmd[req.cmd].type) {
+ case sd_none:
+ /* Called from legacy code not ported to SDProto array */
+ assert(!s->proto->cmd[req.cmd].handler);
+ /* fall-through */
+ case sd_ac:
+ case sd_adtc:
return req.arg >> 16;
+ case sd_spi:
+ g_assert_not_reached();
+ default:
+ return 0;
}
- return 0;
+}
+
+static bool sd_req_rca_same(SDState *s, SDRequest req)
+{
+ return sd_req_get_rca(s, req) == s->rca;
}
/* Card Status register */
@@ -558,7 +597,7 @@
static void sd_set_cardstatus(SDState *sd)
{
- sd->card_status = 0x00000100;
+ sd->card_status = READY_FOR_DATA;
}
static void sd_set_sdstatus(SDState *sd)
@@ -566,6 +605,21 @@
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];
@@ -605,12 +659,25 @@
stl_be_p(response, sd->vhs);
}
-static uint64_t sd_req_get_address(SDState *sd, SDRequest req)
+static uint32_t sd_blk_len(SDState *sd)
{
if (FIELD_EX32(sd->ocr, OCR, CARD_CAPACITY)) {
- return (uint64_t) req.arg << HWBLOCK_SHIFT;
+ return 1 << HWBLOCK_SHIFT;
}
- return req.arg;
+ return sd->blk_len;
+}
+
+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)
@@ -1035,7 +1102,7 @@
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;
@@ -1044,7 +1111,7 @@
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(sd)->name, req.cmd, sd_mode_name(sd->mode),
+ sd->proto->name, req.cmd, sd_mode_name(sd->mode),
sd_version_str(sd->spec_version));
return sd_illegal;
@@ -1053,29 +1120,71 @@
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;
}
/* Commands that are recognised but not yet implemented. */
-__attribute__((unused))
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_inactive_state) {
- sd->state = sd_idle_state;
- sd_reset(DEVICE(sd));
- }
+ sd->state = sd_idle_state;
+ sd_reset(DEVICE(sd));
return sd_is_spi(sd) ? sd_r1 : sd_r0;
}
@@ -1115,6 +1224,221 @@
}
}
+/* 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);
+}
+
+/* CMD7 */
+static sd_rsp_type_t sd_cmd_DE_SELECT_CARD(SDState *sd, SDRequest 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;
+
+ case sd_transfer_state:
+ case sd_sendingdata_state:
+ if (same_rca) {
+ break;
+ }
+ sd->state = sd_standby_state;
+ return sd_r1b;
+
+ case sd_disconnect_state:
+ if (!same_rca) {
+ return sd_r0;
+ }
+ sd->state = sd_programming_state;
+ return sd_r1b;
+
+ 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);
+}
+
+/* CMD8 */
+static sd_rsp_type_t sd_cmd_SEND_IF_COND(SDState *sd, SDRequest req)
+{
+ 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;
+
+ /* 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;
+ }
+
+ /* Accept. */
+ sd->vhs = req.arg;
+ return sd_r7;
+}
+
+/* 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);
+}
+
+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);
+ }
+
+ 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);
+}
+
+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);
+ }
+
+ 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);
+ }
+
+ if (sd_is_spi(sd)) {
+ return sd_r2_s;
+ }
+
+ 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;
+ }
+
+ return sd_r0;
+}
+
+/* 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;
+ }
+
+ return sd_r1;
+}
+
+/* CMD17 */
+static sd_rsp_type_t sd_cmd_READ_SINGLE_BLOCK(SDState *sd, SDRequest req)
+{
+ uint64_t addr;
+
+ if (sd->state != sd_transfer_state) {
+ return sd_invalid_state_for_cmd(sd, req);
+ }
+
+ addr = sd_req_get_address(sd, req);
+ if (!address_in_range(sd, "READ_SINGLE_BLOCK", addr, sd->blk_len)) {
+ return sd_r1;
+ }
+
+ sd_blk_read(sd, addr, sd->blk_len);
+ return sd_cmd_to_sendingdata(sd, req, addr, NULL, sd->blk_len);
+}
+
/* CMD19 */
static sd_rsp_type_t sd_cmd_SEND_TUNING_BLOCK(SDState *sd, SDRequest req)
{
@@ -1122,14 +1446,9 @@
return sd_cmd_illegal(sd, req);
}
- if (sd->state != sd_transfer_state) {
- return sd_invalid_state_for_cmd(sd, req);
- }
-
- sd->state = sd_sendingdata_state;
- sd->data_offset = 0;
-
- return sd_r1;
+ return sd_cmd_to_sendingdata(sd, req, 0,
+ sd_tuning_block_pattern4,
+ sizeof(sd_tuning_block_pattern4));
}
/* CMD23 */
@@ -1149,17 +1468,293 @@
return sd_r1;
}
-static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
+/* CMD24 */
+static sd_rsp_type_t sd_cmd_WRITE_SINGLE_BLOCK(SDState *sd, SDRequest req)
{
- uint16_t rca;
uint64_t addr;
+ if (sd->state != sd_transfer_state) {
+ return sd_invalid_state_for_cmd(sd, req);
+ }
+
+ 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;
+ }
+ }
+ if (sd->csd[14] & 0x30) {
+ sd->card_status |= WP_VIOLATION;
+ }
+
+ sd->blk_written = 0;
+ return sd_cmd_to_receivingdata(sd, req, addr, sd->blk_len);
+}
+
+/* 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));
+}
+
+static sd_rsp_type_t sd_cmd_SET_CLR_WRITE_PROT(SDState *sd, SDRequest req,
+ bool is_write)
+{
+ uint64_t addr;
+
+ if (sd->size > SDSC_MAX_CAPACITY) {
+ return sd_illegal;
+ }
+
+ if (sd->state != sd_transfer_state) {
+ return sd_invalid_state_for_cmd(sd, req);
+ }
+
+ 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;
+ }
+
+ 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;
+}
+
+/* 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);
+}
+
+/* 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);
+}
+
+/* CMD30 */
+static sd_rsp_type_t sd_cmd_SEND_WRITE_PROT(SDState *sd, SDRequest req)
+{
+ uint64_t addr;
+ uint32_t data;
+
+ if (sd->size > SDSC_MAX_CAPACITY) {
+ return sd_illegal;
+ }
+
+ if (sd->state != sd_transfer_state) {
+ return sd_invalid_state_for_cmd(sd, req);
+ }
+
+ addr = sd_req_get_address(sd, req);
+ if (!address_in_range(sd, "SEND_WRITE_PROT", addr, sd->blk_len)) {
+ return sd_r1;
+ }
+
+ data = sd_wpbits(sd, req.arg);
+ return sd_cmd_to_sendingdata(sd, req, addr, &data, sizeof(data));
+}
+
+/* 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;
+}
+
+/* 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;
+}
+
+/* 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_programming_state;
+ sd_erase(sd);
+ /* Bzzzzzzztt .... Operation complete. */
+ sd->state = sd_transfer_state;
+ return sd_r1b;
+}
+
+/* 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:
+ 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;
+
+ return sd_r1;
+}
+
+/* CMD58 */
+static sd_rsp_type_t spi_cmd_READ_OCR(SDState *sd, SDRequest req)
+{
+ return sd_r3;
+}
+
+/* CMD59 */
+static sd_rsp_type_t spi_cmd_CRC_ON_OFF(SDState *sd, SDRequest req)
+{
+ 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);
+ }
+
+ sd->sd_status[0] &= 0x3f;
+ sd->sd_status[0] |= (req.arg & 0x03) << 6;
+ 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));
+}
+
+/* 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));
+}
+
+/* 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;
+}
+
+/* ACMD41 */
+static sd_rsp_type_t sd_acmd_SD_APP_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));
+ }
+ }
+ }
+
+ 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;
+}
+
+/* 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);
+ }
+
+ /* Bringing in the 50KOhm pull-up resistor... Done. */
+ return sd_r1;
+}
+
+/* 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));
+}
+
+static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
+{
+ uint64_t addr;
+
+ 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(sd)->name,
- sd_cmd_name(req.cmd), req.cmd,
+ trace_sdcard_normal_command(sd->proto->name,
+ sd->last_cmd_name, req.cmd,
req.arg, sd_state_name(sd->state));
}
@@ -1172,197 +1767,18 @@
sd->multi_blk_cnt = 0;
}
- if (sd_cmd_class[req.cmd] == 6 && FIELD_EX32(sd->ocr, OCR, CARD_CAPACITY)) {
+ 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(sd)->cmd[req.cmd]) {
- return sd_proto(sd)->cmd[req.cmd](sd, req);
+ if (sd->proto->cmd[req.cmd].handler) {
+ return sd->proto->cmd[req.cmd].handler(sd, req);
}
switch (req.cmd) {
- /* Basic commands (Class 0 and Class 1) */
- case 4: /* CMD4: SEND_DSR */
- switch (sd->state) {
- case sd_standby_state:
- break;
-
- default:
- break;
- }
- break;
-
- case 6: /* CMD6: SWITCH_FUNCTION */
- if (sd->mode != sd_data_transfer_mode) {
- return sd_invalid_mode_for_cmd(sd, req);
- }
- sd_function_switch(sd, req.arg);
- sd->state = sd_sendingdata_state;
- sd->data_start = 0;
- sd->data_offset = 0;
- return sd_r1;
-
- case 7: /* CMD7: SELECT/DESELECT_CARD */
- rca = sd_req_get_rca(sd, req);
- switch (sd->state) {
- case sd_standby_state:
- if (sd->rca != rca)
- return sd_r0;
-
- sd->state = sd_transfer_state;
- return sd_r1b;
-
- case sd_transfer_state:
- case sd_sendingdata_state:
- if (sd->rca == rca)
- break;
-
- sd->state = sd_standby_state;
- return sd_r1b;
-
- case sd_disconnect_state:
- if (sd->rca != rca)
- return sd_r0;
-
- sd->state = sd_programming_state;
- return sd_r1b;
-
- case sd_programming_state:
- if (sd->rca == rca)
- break;
-
- sd->state = sd_disconnect_state;
- return sd_r1b;
-
- default:
- break;
- }
- break;
-
- 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;
-
- /* 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;
- }
-
- /* Accept. */
- sd->vhs = req.arg;
- return sd_r7;
-
- case 9: /* CMD9: SEND_CSD */
- rca = sd_req_get_rca(sd, req);
- switch (sd->state) {
- case sd_standby_state:
- if (sd->rca != rca)
- return sd_r0;
-
- return sd_r2_s;
-
- case sd_transfer_state:
- if (!sd_is_spi(sd)) {
- break;
- }
- sd->state = sd_sendingdata_state;
- memcpy(sd->data, sd->csd, 16);
- sd->data_start = sd_req_get_address(sd, req);
- sd->data_offset = 0;
- return sd_r1;
-
- default:
- break;
- }
- break;
-
- case 10: /* CMD10: SEND_CID */
- rca = sd_req_get_rca(sd, req);
- switch (sd->state) {
- case sd_standby_state:
- if (sd->rca != rca)
- return sd_r0;
-
- return sd_r2_i;
-
- case sd_transfer_state:
- if (!sd_is_spi(sd)) {
- break;
- }
- sd->state = sd_sendingdata_state;
- memcpy(sd->data, sd->cid, 16);
- sd->data_start = sd_req_get_address(sd, req);
- sd->data_offset = 0;
- return sd_r1;
-
- default:
- break;
- }
- break;
-
- case 12: /* CMD12: STOP_TRANSMISSION */
- 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_r1b;
-
- default:
- break;
- }
- break;
-
- case 13: /* CMD13: SEND_STATUS */
- rca = sd_req_get_rca(sd, req);
- if (sd->mode != sd_data_transfer_mode) {
- return sd_invalid_mode_for_cmd(sd, req);
- }
- if (!sd_is_spi(sd) && sd->rca != rca) {
- return sd_r0;
- }
-
- return sd_r1;
-
- case 15: /* CMD15: GO_INACTIVE_STATE */
- if (sd->mode != sd_data_transfer_mode) {
- return sd_invalid_mode_for_cmd(sd, req);
- }
- rca = sd_req_get_rca(sd, req);
- if (sd->rca == rca) {
- sd->state = sd_inactive_state;
- }
- return sd_r0;
-
/* 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;
- }
-
- return sd_r1;
-
- default:
- break;
- }
- break;
-
- case 17: /* CMD17: READ_SINGLE_BLOCK */
case 18: /* CMD18: READ_MULTIPLE_BLOCK */
addr = sd_req_get_address(sd, req);
switch (sd->state) {
@@ -1383,7 +1799,6 @@
break;
/* Block write commands (Class 4) */
- case 24: /* CMD24: WRITE_SINGLE_BLOCK */
case 25: /* CMD25: WRITE_MULTIPLE_BLOCK */
addr = sd_req_get_address(sd, req);
switch (sd->state) {
@@ -1414,180 +1829,9 @@
break;
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;
-
- default:
- break;
- }
- break;
-
- 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;
-
- default:
- break;
- }
- break;
-
- /* Write protection (Class 6) */
- case 28: /* CMD28: SET_WRITE_PROT */
- if (sd->size > SDSC_MAX_CAPACITY) {
- return sd_illegal;
- }
- addr = sd_req_get_address(sd, req);
- switch (sd->state) {
- case sd_transfer_state:
- if (!address_in_range(sd, "SET_WRITE_PROT", addr, 1)) {
- return sd_r1b;
- }
-
- 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;
-
- default:
- break;
- }
- break;
-
- case 29: /* CMD29: CLR_WRITE_PROT */
- if (sd->size > SDSC_MAX_CAPACITY) {
- return sd_illegal;
- }
- addr = sd_req_get_address(sd, req);
- switch (sd->state) {
- case sd_transfer_state:
- if (!address_in_range(sd, "CLR_WRITE_PROT", addr, 1)) {
- return sd_r1b;
- }
-
- 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;
-
- default:
- break;
- }
- break;
-
- case 30: /* CMD30: SEND_WRITE_PROT */
- if (sd->size > SDSC_MAX_CAPACITY) {
- return sd_illegal;
- }
- 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)) {
- 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;
-
- default:
- break;
- }
- 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 */
- 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;
- }
-
- 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_offset = 0;
- return sd_r1;
-
- default:
- break;
- }
- break;
+ return sd_cmd_to_receivingdata(sd, req, 0, sizeof(sd->cid));
/* Application specific commands (Class 8) */
- case 55: /* CMD55: APP_CMD */
- rca = sd_req_get_rca(sd, req);
- 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);
- }
- default:
- break;
- }
- if (!sd_is_spi(sd)) {
- if (sd->rca != rca) {
- return sd_r0;
- }
- }
- sd->expecting_acmd = true;
- sd->card_status |= APP_CMD;
- return sd_r1;
-
case 56: /* CMD56: GEN_CMD */
switch (sd->state) {
case sd_transfer_state:
@@ -1603,12 +1847,6 @@
}
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;
@@ -1620,126 +1858,16 @@
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:
@@ -1763,6 +1891,8 @@
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)
@@ -1777,7 +1907,12 @@
if (cmd == 16 || cmd == 55) {
return true;
}
- return sd_cmd_class[cmd] == 0 || sd_cmd_class[cmd] == 7;
+ if (!sd->proto->cmd[cmd].handler) {
+ return false;
+ }
+ cmd_class = sd->proto->cmd[cmd].class;
+
+ return cmd_class == 0 || cmd_class == 7;
}
int sd_do_command(SDState *sd, SDRequest *req,
@@ -1790,6 +1925,11 @@
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;
@@ -1896,6 +2036,31 @@
return rsplen;
}
+/* 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;
+}
+
void sd_write_byte(SDState *sd, uint8_t value)
{
int i;
@@ -1912,13 +2077,12 @@
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;
sd_blk_write(sd, sd->data_start, sd->data_offset);
@@ -1967,8 +2131,7 @@
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 ++)
@@ -1986,8 +2149,7 @@
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 ++)
@@ -2010,8 +2172,7 @@
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);
@@ -2034,20 +2195,6 @@
}
}
-#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)
{
/* TODO: Append CRCs */
@@ -2066,42 +2213,22 @@
if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION))
return 0x00;
- 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, 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 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) {
- sd_blk_read(sd, 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 */
+ sd_generic_read_byte(sd, &ret);
break;
case 18: /* CMD18: READ_MULTIPLE_BLOCK */
@@ -2128,34 +2255,6 @@
}
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);
@@ -2191,29 +2290,116 @@
static const SDProto sd_proto_spi = {
.name = "SPI",
.cmd = {
- [0] = sd_cmd_GO_IDLE_STATE,
- [1] = spi_cmd_SEND_OP_COND,
+ [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},
+ [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 = {
- [41] = spi_cmd_SEND_OP_COND,
+ [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,
- [2] = sd_cmd_ALL_SEND_CID,
- [3] = sd_cmd_SEND_RELATIVE_ADDR,
- [19] = sd_cmd_SEND_TUNING_BLOCK,
- [23] = sd_cmd_SET_BLOCK_COUNT,
+ [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},
+ [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, "SD_APP_OP_COND", sd_acmd_SD_APP_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 void sd_instance_init(Object *obj)
{
SDState *sd = SD_CARD(obj);
+ SDCardClass *sc = SD_CARD_GET_CLASS(sd);
+ sd->proto = sc->proto;
+ sd->last_cmd_name = "UNSET";
sd->enable = true;
sd->ocr_power_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sd_ocr_powerup, sd);
}
diff --git a/hw/sd/sdmmc-internal.c b/hw/sd/sdmmc-internal.c
deleted file mode 100644
index c1d5508..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_COND",
- [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..cc0b69e 100644
--- a/hw/sd/sdmmc-internal.h
+++ b/hw/sd/sdmmc-internal.h
@@ -11,30 +11,112 @@
#ifndef SDMMC_INTERNAL_H
#define SDMMC_INTERNAL_H
-#define SDMMC_CMD_MAX 64
-
-/**
- * sd_cmd_name:
- * @cmd: A SD "normal" command, up to SDMMC_CMD_MAX.
+/*
+ * EXT_CSD Modes segment
*
- * 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.
- *
- * 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.
+#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 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.
- *
- * 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/trace-events b/hw/sd/trace-events
index 724365e..43eaeba 100644
--- a/hw/sd/trace-events
+++ b/hw/sd/trace-events
@@ -50,10 +50,11 @@
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, uint32_t length) "%s %20s/ CMD%02d ofs %"PRIu32" len %" PRIu32
sdcard_set_voltage(uint16_t millivolts) "%u mV"
# pxa2xx_mmci.c
diff --git a/include/hw/sd/sd.h b/include/hw/sd/sd.h
index 2c8748f..29c7693 100644
--- a/include/hw/sd/sd.h
+++ b/include/hw/sd/sd.h
@@ -76,8 +76,9 @@
} sd_uhs_mode_t;
typedef enum {
- sd_none = -1,
- sd_bc = 0, /* broadcast -- no response */
+ sd_none = 0,
+ sd_spi,
+ sd_bc, /* broadcast -- no response */
sd_bcr, /* broadcast with response */
sd_ac, /* addressed -- no data transfer */
sd_adtc, /* addressed with data transfer */