All of lore.kernel.org
 help / color / mirror / Atom feed
From: Bin Meng <bin.meng@processmission.com>
To: QEMU <qemu-devel@nongnu.org>
Cc: "Bin Meng" <bmeng.cn@gmail.com>,
	"Philippe Mathieu-Daudé" <philmd@mailo.com>,
	qemu-block@nongnu.org
Subject: [PATCH 08/10] hw/sd: sdhci: Skip SDMA boundary stops for i.MX uSDHC
Date: Wed, 12 Aug 2026 09:36:11 +0800	[thread overview]
Message-ID: <20260812013619.2134092-9-bin.meng@processmission.com> (raw)
In-Reply-To: <20260812013619.2134092-1-bin.meng@processmission.com>

The standard SDHCI Block Size register encodes an SDMA buffer boundary
in bits 14:12. The generic model stops at each selected boundary, sets
the DMA status, and waits for software to rewrite the SDMA system
address before continuing.

Freescale eSDHC and i.MX uSDHC instead expose BLKATTR without a
programmable standard boundary field. On both controller families,
bits 14:13 are reserved and bit 12 is the high bit of the 13-bit
block-size field. Linux masks the standard boundary encoding before
writing BLKATTR.

Despite its name, esdhc_write() is the shared register translation
helper. usdhc_write() delegates common register accesses to it. The
existing translation inserts 0x7 into the generic boundary field so
the common core sees a 512 KiB boundary.

For i.MX6UL uSDHC, BLK_ATT bits 15:13 are reserved and bit 12 is
part of its 13-bit block size. DINT reports successful completion of
the whole internal DMA transfer, not an intermediate boundary pause.

U-Boot programs one contiguous DMA address and a block count, then
waits for both DINT and transfer-complete. It never rewrites the address
at an intermediate boundary. Reads larger than 512 KiB therefore reach
the synthetic boundary and stall with blocks remaining.

The missing field alone does not establish whether every older eSDHC
has a fixed internal boundary handshake. Limit the new quirk to only
TYPE_IMX_USDHC, where the reference manual, U-Boot behavior, and the
observed failure agree. Existing FSL eSDHC and standard SDHCI behavior
remain unchanged.

Reference: IMX6ULRM (Rev. 2), section 56.8.2 and 56.8.13
https://www.nxp.com/webapp/Download?colCode=IMX6ULRM

Signed-off-by: Bin Meng <bin.meng@processmission.com>
---

 hw/sd/sdhci.c         | 37 ++++++++++++++++++++++++-------------
 include/hw/sd/sdhci.h |  5 +++++
 2 files changed, 29 insertions(+), 13 deletions(-)

diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c
index 2ee62847cb..7c014d0345 100644
--- a/hw/sd/sdhci.c
+++ b/hw/sd/sdhci.c
@@ -599,11 +599,13 @@ static void sdhci_write_dataport(SDHCIState *s, uint32_t value, unsigned size)
 /* Multi block SDMA transfer */
 static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
 {
+    bool boundary_enabled =
+        !(s->quirks & SDHCI_QUIRK_NO_SDMA_BOUNDARY);
     bool page_aligned = false;
     unsigned int begin;
     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);
+    uint32_t boundary_chk = 0;
+    uint32_t boundary_count = 0;
 
     if (!(s->trnmod & SDHC_TRNS_BLK_CNT_EN) || !s->blkcnt) {
         qemu_log_mask(LOG_UNIMP, "infinite transfer is not supported\n");
@@ -615,8 +617,11 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
      * possible stop at page boundary if initial address is not page aligned,
      * allow them to work properly
      */
-    if ((s->sdmasysad % boundary_chk) == 0) {
-        page_aligned = true;
+    if (boundary_enabled) {
+        boundary_chk =
+            1 << (((s->blksize & ~BLOCK_SIZE_MASK) >> 12) + 12);
+        boundary_count = boundary_chk - (s->sdmasysad % boundary_chk);
+        page_aligned = (s->sdmasysad % boundary_chk) == 0;
     }
 
     s->prnsts |= SDHC_DATA_INHIBIT | SDHC_DAT_LINE_ACTIVE;
@@ -632,7 +637,9 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
                 boundary_count = 0;
              } else {
                 s->data_count = block_size;
-                boundary_count -= block_size - begin;
+                if (boundary_enabled) {
+                    boundary_count -= block_size - begin;
+                }
                 if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) {
                     s->blkcnt--;
                 }
@@ -656,7 +663,9 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
                 boundary_count = 0;
              } else {
                 s->data_count = block_size;
-                boundary_count -= block_size - begin;
+                if (boundary_enabled) {
+                    boundary_count -= block_size - begin;
+                }
             }
             dma_memory_read(s->dma_as, s->sdmasysad, &s->fifo_buffer[begin],
                             s->data_count - begin, MEMTXATTRS_UNSPECIFIED);
@@ -1818,13 +1827,14 @@ esdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
 
     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.
+         * Freescale eSDHC and i.MX uSDHC use BLKATTR without the standard
+         * Host SDMA Buffer Boundary field. uSDHC accesses reach this shared
+         * register translation through usdhc_write().
          *
-         * 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.
+         * Keep the largest boundary in the generic representation to
+         * preserve existing eSDHC behavior. TYPE_IMX_USDHC separately
+         * disables the associated stop semantics because its DINT reports
+         * completion of the entire transfer rather than a boundary event.
          */
         val |= 0x7 << 12;
         /* FALLTHROUGH */
@@ -1971,7 +1981,8 @@ static void imx_usdhc_init(Object *obj)
 
     s->io_ops = &usdhc_mmio_ops;
     s->quirks = SDHCI_QUIRK_NO_BUSY_IRQ |
-                SDHCI_QUIRK_CLOCKS_IN_VENDOR;
+                SDHCI_QUIRK_CLOCKS_IN_VENDOR |
+                SDHCI_QUIRK_NO_SDMA_BOUNDARY;
     qdev_prop_set_uint8(dev, "sd-spec-version", 3);
 }
 
diff --git a/include/hw/sd/sdhci.h b/include/hw/sd/sdhci.h
index c542d47f9b..387c2dc80f 100644
--- a/include/hw/sd/sdhci.h
+++ b/include/hw/sd/sdhci.h
@@ -116,6 +116,11 @@ typedef struct SDHCIState SDHCIState;
  * SDHCI clock-control fields
  */
 #define SDHCI_QUIRK_CLOCKS_IN_VENDOR     BIT(1)
+/*
+ * Controller completes contiguous SDMA transfers without the standard
+ * intermediate buffer-boundary stop and address-update handshake
+ */
+#define SDHCI_QUIRK_NO_SDMA_BOUNDARY     BIT(2)
 
 #define TYPE_PCI_SDHCI "sdhci-pci"
 DECLARE_INSTANCE_CHECKER(SDHCIState, PCI_SDHCI,
-- 
2.53.0



  parent reply	other threads:[~2026-08-12  1:38 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-12  1:36 [PATCH 00/10] hw/arm: Enable U-Boot boot on MCIMX6UL-EVK Bin Meng
2026-08-12  1:36 ` [PATCH 01/10] hw/arm: fsl-imx6ul: Add SCU compatibility window Bin Meng
2026-08-12  1:36 ` [PATCH 02/10] hw/misc: imx6ul_ccm: Update PMU_MISC0 reset value Bin Meng
2026-08-12  1:36 ` [PATCH 03/10] hw/arm: fsl-imx6ul: Map early firmware register placeholders Bin Meng
2026-08-12  1:36 ` [PATCH 04/10] hw/arm: fsl-imx6ul: Add a minimal MMDC geometry model Bin Meng
2026-08-12  1:36 ` [PATCH 05/10] hw/sd: sdhci: Use a QEMU-local no-busy IRQ quirk bit Bin Meng
2026-08-12  1:36 ` [PATCH 06/10] hw/sd: sdhci: Honor i.MX uSDHC vendor clock gates Bin Meng
2026-08-12  1:36 ` [PATCH 07/10] hw/sd: sdhci: Preserve uSDHC status enables for U-Boot Bin Meng
2026-08-12  1:36 ` Bin Meng [this message]
2026-08-12  1:36 ` [PATCH 09/10] docs/system/arm: Document MCIMX6UL-EVK Buildroot boot Bin Meng
2026-08-12  1:36 ` [PATCH 10/10] tests/functional/arm: Add MCIMX6UL-EVK boot tests Bin Meng

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=20260812013619.2134092-9-bin.meng@processmission.com \
    --to=bin.meng@processmission.com \
    --cc=bmeng.cn@gmail.com \
    --cc=philmd@mailo.com \
    --cc=qemu-block@nongnu.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.