Skip to content

Commit

Permalink
sdhci: Add some workarrounds for i.MX
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
c-mauderer committed Aug 16, 2021
1 parent f7eeccd commit 3b8fcca
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 0 deletions.
41 changes: 41 additions & 0 deletions freebsd/sys/dev/sdhci/fsl_sdhci.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
}

Expand All @@ -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);
}

Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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
Expand Down
40 changes: 40 additions & 0 deletions freebsd/sys/dev/sdhci/sdhci.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand Down Expand Up @@ -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);

/*
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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) {
Expand Down

0 comments on commit 3b8fcca

Please sign in to comment.