[PATCH rtems-libbsd 1/2] sdhci: Add some workarrounds for i.MX

Christian Mauderer christian.mauderer at embedded-brains.de
Wed Sep 9 11:16:21 UTC 2020


Some bits are in the wrong order. Beneath that, the interrupts can occur
in an unexpected order. The DATA_AVAIL interrupt can occur at the same
time as the DMA interrupt (or slightly before it). With that, the DMA
and PIO interrupt handling doesn't work well together. Beneath that the
DMA interrupt isn't only executed at block ends but also if all data is
transfered. That can lead to problems with the DATA_END interrupt.
Therefore check whether there is really data left to unload.

Update #3869
---
 freebsd/sys/dev/sdhci/fsl_sdhci.c | 41 +++++++++++++++++++++++++++++++
 freebsd/sys/dev/sdhci/sdhci.c     | 40 ++++++++++++++++++++++++++++++
 2 files changed, 81 insertions(+)

diff --git a/freebsd/sys/dev/sdhci/fsl_sdhci.c b/freebsd/sys/dev/sdhci/fsl_sdhci.c
index be3d1de3..acef1c4a 100644
--- a/freebsd/sys/dev/sdhci/fsl_sdhci.c
+++ b/freebsd/sys/dev/sdhci/fsl_sdhci.c
@@ -74,6 +74,9 @@ uint32_t mpc85xx_get_system_clock(void);
 #endif /* __rtems__ */
 #endif
 
+#ifndef __rtems__
+#include <bsp.h>
+#endif /* __rtems__ */
 #include <dev/gpio/gpiobusvar.h>
 
 #include <dev/ofw/ofw_bus.h>
@@ -168,6 +171,16 @@ struct fsl_sdhci_softc {
 #define	 SDHC_PROT_CDSS		(1 << 7)
 
 #define	SDHC_SYS_CTRL		0x2c
+#ifdef __rtems__
+
+/*
+ * Freescale messed up the INT DMA ERR bit and placed it at bit 28 instead of
+ * bit 25 which would be standard.
+ */
+#define SDHC_INT_DMAES		(1 << 28)
+
+#define	 SDHC_CAN_DO_ADMA2	0x00100000
+#endif /* __rtems__ */
 
 /*
  * The clock enable bits exist in different registers for ESDHC vs USDHC, but
@@ -349,6 +362,16 @@ fsl_sdhci_read_4(device_t dev, struct sdhci_slot *slot, bus_size_t off)
 		val32 &= ~SDHCI_CAN_VDD_180;
 		val32 &= ~SDHCI_CAN_DO_SUSPEND;
 		val32 |= SDHCI_CAN_DO_8BITBUS;
+#ifdef __rtems__
+		/*
+		 * Freescale signals ADMA2 capability via bit 20 (which would be
+		 * ADMA1) instead of 19.
+		 */
+		if (val32 & SDHC_CAN_DO_ADMA2) {
+			val32 &= ~SDHC_CAN_DO_ADMA2;
+			val32 |= SDHCI_CAN_DO_ADMA2;
+		}
+#endif /* __rtems__ */
 		return (val32);
 	}
 	
@@ -373,6 +396,13 @@ fsl_sdhci_read_4(device_t dev, struct sdhci_slot *slot, bus_size_t off)
 	 * command with an R1B response, mix it into the hardware status.
 	 */
 	if (off == SDHCI_INT_STATUS) {
+#ifdef __rtems__
+		/* Fix messed up DMA error. */
+		if (val32 & SDHC_INT_DMAES) {
+			val32 &= ~SDHC_INT_DMAES;
+			val32 |= SDHCI_INT_ADMAERR;
+		}
+#endif /* __rtems__ */
 		return (val32 | sc->r1bfix_intmask);
 	}
 
@@ -519,6 +549,15 @@ fsl_sdhci_write_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint32_
 	if (off == SDHCI_INT_STATUS) {
 		sc->r1bfix_intmask &= ~val;
 	}
+#ifdef __rtems__
+	/* Fix messed up DMA error. */
+	if (off == SDHCI_INT_STATUS || off == SDHCI_INT_ENABLE || off == SDHCI_SIGNAL_ENABLE) {
+		if (val & SDHCI_INT_ADMAERR) {
+			val &= ~SDHCI_INT_ADMAERR;
+			val |= SDHC_INT_DMAES;
+		}
+	}
+#endif /* __rtems__ */
 
 	WR4(sc, off, val);
 }
@@ -884,10 +923,12 @@ fsl_sdhci_attach(device_t dev)
 
 	sc->slot.quirks |= SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK;
 
+#if !defined(__rtems__) || !defined(LIBBSP_ARM_IMX_BSP_H)
 	/*
 	 * DMA is not really broken, I just haven't implemented it yet.
 	 */
 	sc->slot.quirks |= SDHCI_QUIRK_BROKEN_DMA;
+#endif /* __rtems__ */
 
 	/*
 	 * Set the buffer watermark level to 128 words (512 bytes) for both read
diff --git a/freebsd/sys/dev/sdhci/sdhci.c b/freebsd/sys/dev/sdhci/sdhci.c
index ed6010e8..53d86fe8 100644
--- a/freebsd/sys/dev/sdhci/sdhci.c
+++ b/freebsd/sys/dev/sdhci/sdhci.c
@@ -68,6 +68,9 @@ __FBSDID("$FreeBSD$");
 #include <rtems/bsd/local/sdhci_if.h>
 
 #include <rtems/bsd/local/opt_mmccam.h>
+#ifdef __rtems__
+#include <bsp.h>
+#endif /* __rtems__ */
 
 SYSCTL_NODE(_hw, OID_AUTO, sdhci, CTLFLAG_RD, 0, "sdhci driver");
 
@@ -766,6 +769,17 @@ sdhci_dma_alloc(struct sdhci_slot *slot)
 		else
 			slot->sdma_boundary = SDHCI_BLKSZ_SDMA_BNDRY_512K;
 	}
+#ifdef __rtems__
+#if defined(LIBBSP_ARM_IMX_BSP_H)
+	/*
+	 * i.MX6ULL doesn't have the SDMA Buffer Boundary bits. Instead the
+	 * BLKSIZE is one bit larger and would overlap the Buffer Boundary.
+	 * Setting the Buffer Boundary to 4K makes sure that the highest BLKSIZE
+	 * bit is always 0.
+	 */
+	slot->sdma_boundary = SDHCI_BLKSZ_SDMA_BNDRY_4K;
+#endif
+#endif /* __rtems__ */
 	slot->sdma_bbufsz = SDHCI_SDMA_BNDRY_TO_BBUFSZ(slot->sdma_boundary);
 
 	/*
@@ -1896,6 +1910,10 @@ sdhci_start_data(struct sdhci_slot *slot, const struct mmc_data *data)
 			    BUS_DMASYNC_PREWRITE);
 		}
 		WR4(slot, SDHCI_DMA_ADDRESS, slot->paddr);
+#ifdef __rtems__
+		/* Avoid PIO interrupt if we use DMA */
+		slot->intmask &= ~(SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL);
+#endif /* __rtems__ */
 		/*
 		 * Interrupt aggregation: Mask border interrupt for the last
 		 * bounce buffer and unmask otherwise.
@@ -1932,15 +1950,27 @@ sdhci_finish_data(struct sdhci_slot *slot)
 		WR4(slot, SDHCI_SIGNAL_ENABLE,
 		    slot->intmask |= SDHCI_INT_RESPONSE);
 	}
+#ifdef __rtems__
+	/* Restore PIO interrupts in case they are necessary elsewhere */
+	if (slot->flags & SDHCI_USE_DMA) {
+		slot->intmask |= SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL;
+	}
+#endif /* __rtems__ */
 	/* Unload rest of data from DMA buffer. */
 	if (!slot->data_done && (slot->flags & SDHCI_USE_DMA) &&
 	    slot->curcmd->data != NULL) {
 		if (data->flags & MMC_DATA_READ) {
 			left = data->len - slot->offset;
+#ifdef __rtems__
+		    if (left > 0) {
+#endif /* __rtems__ */
 			bus_dmamap_sync(slot->dmatag, slot->dmamap,
 			    BUS_DMASYNC_POSTREAD);
 			memcpy((u_char*)data->data + slot->offset, slot->dmamem,
 			    ulmin(left, slot->sdma_bbufsz));
+#ifdef __rtems__
+		    }
+#endif /* __rtems__ */
 		} else
 			bus_dmamap_sync(slot->dmatag, slot->dmamap,
 			    BUS_DMASYNC_POSTWRITE);
@@ -2201,8 +2231,15 @@ sdhci_data_irq(struct sdhci_slot *slot, uint32_t intmask)
 			    BUS_DMASYNC_POSTWRITE);
 		}
 		/* ... and reload it again. */
+#ifdef __rtems__
+		slot->offset += ulmin(left, sdma_bbufsz);
+#else /* __rtems__ */
 		slot->offset += sdma_bbufsz;
+#endif /* __rtems__ */
 		left = data->len - slot->offset;
+#ifdef __rtems__
+	    if (left > 0) {
+#endif /* __rtems__ */
 		if (data->flags & MMC_DATA_READ) {
 			bus_dmamap_sync(slot->dmatag, slot->dmamap,
 			    BUS_DMASYNC_PREREAD);
@@ -2222,6 +2259,9 @@ sdhci_data_irq(struct sdhci_slot *slot, uint32_t intmask)
 		}
 		/* Restart DMA. */
 		WR4(slot, SDHCI_DMA_ADDRESS, slot->paddr);
+#ifdef __rtems__
+	    }
+#endif /* __rtems__ */
 	}
 	/* We have got all data. */
 	if (intmask & SDHCI_INT_DATA_END) {
-- 
2.26.2



More information about the devel mailing list