From: "Philippe Mathieu-Daudé" <philmd@linaro.org>
To: qemu-devel@nongnu.org
Subject: [PULL v2 06/41] hw/sd/sd: Allow multi-byte read/write for generic paths
Date: Tue, 12 May 2026 22:38:42 +0200 [thread overview]
Message-ID: <20260512203842.4863-2-philmd@linaro.org> (raw)
In-Reply-To: <20260512203842.4863-1-philmd@linaro.org>
From: Christian Speich <c.speich@avm.de>
Paths that use sd_generic_write/read_data can now write/read multiple
bytes with one call.
Signed-off-by: Christian Speich <c.speich@avm.de>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Message-ID: <20260417-sdcard-performance-b4-v4-2-119e66be10c2@avm.de>
[PMD: Access &sd->data[sd->data_offset] in sd_generic_read/write_data]
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
---
hw/sd/sd.c | 62 ++++++++++++++++++++++++++++++------------------------
1 file changed, 35 insertions(+), 27 deletions(-)
diff --git a/hw/sd/sd.c b/hw/sd/sd.c
index c0b89735f69..11f6b91576a 100644
--- a/hw/sd/sd.c
+++ b/hw/sd/sd.c
@@ -1610,7 +1610,7 @@ static sd_rsp_type_t sd_cmd_optional(SDState *sd, SDRequest req)
return sd_illegal;
}
-/* Configure fields for following sd_generic_write_byte() calls */
+/* Configure fields for following sd_generic_write_data() calls */
static sd_rsp_type_t sd_cmd_to_receivingdata(SDState *sd, SDRequest req,
uint64_t start, size_t size)
{
@@ -1625,7 +1625,7 @@ static sd_rsp_type_t sd_cmd_to_receivingdata(SDState *sd, SDRequest req,
return sd_r1;
}
-/* Configure fields for following sd_generic_read_byte() calls */
+/* Configure fields for following sd_generic_read_data() calls */
static sd_rsp_type_t sd_cmd_to_sendingdata(SDState *sd, SDRequest req,
uint64_t start,
const void *data, size_t size)
@@ -2615,11 +2615,15 @@ send_response:
}
/* Return true if buffer is consumed. Configured by sd_cmd_to_receivingdata() */
-static bool sd_generic_write_byte(SDState *sd, uint8_t value)
+static bool sd_generic_write_data(SDState *sd, const void *buf, size_t *len)
{
- sd->data[sd->data_offset] = value;
+ size_t to_write = MIN(sd->data_size - sd->data_offset, *len);
- if (++sd->data_offset >= sd->data_size) {
+ memcpy(&sd->data[sd->data_offset], buf, to_write);
+ sd->data_offset += to_write;
+ *len = to_write;
+
+ if (sd->data_offset >= sd->data_size) {
sd->state = sd_transfer_state;
return true;
}
@@ -2627,11 +2631,15 @@ static bool sd_generic_write_byte(SDState *sd, uint8_t value)
}
/* Return true when buffer is consumed. Configured by sd_cmd_to_sendingdata() */
-static bool sd_generic_read_byte(SDState *sd, uint8_t *value)
+static bool sd_generic_read_data(SDState *sd, void *buf, size_t *len)
{
- *value = sd->data[sd->data_offset];
+ size_t to_read = MIN(sd->data_size - sd->data_offset, *len);
- if (++sd->data_offset >= sd->data_size) {
+ memcpy(buf, &sd->data[sd->data_offset], to_read);
+ sd->data_offset += to_read;
+ *len = to_read;
+
+ if (sd->data_offset >= sd->data_size) {
sd->state = sd_transfer_state;
return true;
}
@@ -2658,18 +2666,12 @@ static size_t sd_write_data(SDState *sd, const void *buf, size_t length)
if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION))
return length;
- /*
- * Only read one byte at a time. We will be called again with the
- * remaining.
- */
- length = 1;
-
trace_sdcard_write_data(sd->proto->name,
sd->last_cmd_name,
sd->current_cmd, sd->data_offset, value[0]);
switch (sd->current_cmd) {
case 24: /* CMD24: WRITE_SINGLE_BLOCK */
- if (sd_generic_write_byte(sd, value[0])) {
+ if (sd_generic_write_data(sd, buf, &length)) {
/* TODO: Check CRC before committing */
sd->state = sd_programming_state;
sd_blk_write(sd, sd->data_start, sd->data_offset);
@@ -2681,6 +2683,12 @@ static size_t sd_write_data(SDState *sd, const void *buf, size_t length)
break;
case 25: /* CMD25: WRITE_MULTIPLE_BLOCK */
+ /*
+ * Only read one byte at a time. We will be called again with the
+ * remaining.
+ */
+ length = 1;
+
if (sd->data_offset == 0) {
/* Start of the block - let's check the address is valid */
if (!address_in_range(sd, "WRITE_MULTIPLE_BLOCK",
@@ -2724,7 +2732,7 @@ static size_t sd_write_data(SDState *sd, const void *buf, size_t length)
break;
case 26: /* CMD26: PROGRAM_CID */
- if (sd_generic_write_byte(sd, value[0])) {
+ if (sd_generic_write_data(sd, buf, &length)) {
/* TODO: Check CRC before committing */
sd->state = sd_programming_state;
for (i = 0; i < sizeof(sd->cid); i ++)
@@ -2742,7 +2750,7 @@ static size_t sd_write_data(SDState *sd, const void *buf, size_t length)
break;
case 27: /* CMD27: PROGRAM_CSD */
- if (sd_generic_write_byte(sd, value[0])) {
+ if (sd_generic_write_data(sd, buf, &length)) {
/* TODO: Check CRC before committing */
sd->state = sd_programming_state;
for (i = 0; i < sizeof(sd->csd); i ++)
@@ -2765,7 +2773,7 @@ static size_t sd_write_data(SDState *sd, const void *buf, size_t length)
break;
case 42: /* CMD42: LOCK_UNLOCK */
- if (sd_generic_write_byte(sd, value[0])) {
+ if (sd_generic_write_data(sd, buf, &length)) {
/* TODO: Check CRC before committing */
sd->state = sd_programming_state;
sd_lock_command(sd);
@@ -2775,7 +2783,7 @@ static size_t sd_write_data(SDState *sd, const void *buf, size_t length)
break;
case 56: /* CMD56: GEN_CMD */
- sd_generic_write_byte(sd, value[0]);
+ sd_generic_write_data(sd, buf, &length);
break;
default:
@@ -2810,12 +2818,6 @@ static size_t sd_read_data(SDState *sd, void *buf, size_t length)
return length;
}
- /*
- * We will only read one byte at a time. We will be called again with the
- * remaining buffer.
- */
- length = 1;
-
io_len = sd_blk_len(sd);
trace_sdcard_read_data(sd->proto->name,
@@ -2833,10 +2835,16 @@ static size_t sd_read_data(SDState *sd, void *buf, size_t length)
case 30: /* CMD30: SEND_WRITE_PROT */
case 51: /* ACMD51: SEND_SCR */
case 56: /* CMD56: GEN_CMD */
- sd_generic_read_byte(sd, value);
+ sd_generic_read_data(sd, buf, &length);
break;
case 18: /* CMD18: READ_MULTIPLE_BLOCK */
+ /*
+ * We will only read one byte at a time. We will be called again with
+ * the remaining buffer.
+ */
+ length = 1;
+
if (sd->data_offset == 0) {
if (!address_in_range(sd, "READ_MULTIPLE_BLOCK",
sd->data_start, io_len)) {
@@ -2869,7 +2877,7 @@ static size_t sd_read_data(SDState *sd, void *buf, size_t length)
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: DAT read illegal for command %s\n",
__func__, sd->last_cmd_name);
- *value = dummy_byte;
+ memset(buf, dummy_byte, length);
}
return length;
--
2.53.0
next prev parent reply other threads:[~2026-05-12 20:39 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-12 20:38 [PULL v2 00/41] Misc HW patches for 2026-05-12 Philippe Mathieu-Daudé
2026-05-12 20:38 ` Philippe Mathieu-Daudé [this message]
2026-05-14 16:27 ` Stefan Hajnoczi
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260512203842.4863-2-philmd@linaro.org \
--to=philmd@linaro.org \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.