On Sat, Sep 19, 2026 at 10:32 PM Bernhard Beschow <[email protected]> wrote:
>
>
>
> Am 4. September 2026 16:14:31 UTC schrieb Bin Meng 
> <[email protected]>:
> >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 <[email protected]>
> >---
> >
> >(no changes since v1)
> >
> > include/hw/sd/sdhci.h |  5 +++++
> > hw/sd/sdhci.c         | 37 ++++++++++++++++++++++++-------------
> > 2 files changed, 29 insertions(+), 13 deletions(-)
> >
> >diff --git a/include/hw/sd/sdhci.h b/include/hw/sd/sdhci.h
> >index 83d465774b..6664822398 100644
> >--- a/include/hw/sd/sdhci.h
> >+++ b/include/hw/sd/sdhci.h
> >@@ -118,6 +118,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,
> >diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c
> >index 4d7bbdbe7a..de0883497b 100644
> >--- a/hw/sd/sdhci.c
> >+++ b/hw/sd/sdhci.c
> >@@ -687,12 +687,14 @@ static void sdhci_advance_sdma_address(SDHCIState *s, 
> >uint32_t bytes)
> > /* 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_chk = 0;
> >     uint64_t sdma_address = sdhci_sdma_address(s);
> >-    uint32_t boundary_count = boundary_chk - (sdma_address % boundary_chk);
> >+    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");
> >@@ -704,8 +706,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 ((sdma_address % boundary_chk) == 0) {
> >-        page_aligned = true;
> >+    if (boundary_enabled) {
> >+        boundary_chk =
> >+            1 << (((s->blksize & ~BLOCK_SIZE_MASK) >> 12) + 12);
> >+        boundary_count = boundary_chk - (sdma_address % boundary_chk);
> >+        page_aligned = (sdma_address % boundary_chk) == 0;
> >     }
> >
> >     s->prnsts |= SDHC_DATA_INHIBIT | SDHC_DAT_LINE_ACTIVE;
> >@@ -721,7 +726,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--;
> >                 }
> >@@ -746,7 +753,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, sdhci_sdma_address(s),
> >                             &s->fifo_buffer[begin],
> >@@ -2015,13 +2024,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 */
> >@@ -2168,7 +2178,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;
>
> Given the above comments please also add the quirk to both ESDHC variants. 
> Thanks!

Will extend the quirk to eSDHC variants in v3.

>
> With the above fixed:
>
> Reviewed-by: Bernhard Beschow <[email protected]>
>

Regards,
Bin

Reply via email to