summaryrefslogtreecommitdiff
path: root/firmware/target/arm/imx233/sdmmc-imx233.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/imx233/sdmmc-imx233.c')
-rw-r--r--firmware/target/arm/imx233/sdmmc-imx233.c120
1 files changed, 87 insertions, 33 deletions
diff --git a/firmware/target/arm/imx233/sdmmc-imx233.c b/firmware/target/arm/imx233/sdmmc-imx233.c
index 72295bd313..e250662e2c 100644
--- a/firmware/target/arm/imx233/sdmmc-imx233.c
+++ b/firmware/target/arm/imx233/sdmmc-imx233.c
@@ -460,10 +460,43 @@ static int init_mmc_drive(int drive)
}
#endif
+// low-level function, don't call directly!
+static int __xfer_sectors(int drive, unsigned long start, int count, void *buf, bool read)
+{
+ uint32_t resp;
+ int ret = 0;
+ while(count != 0)
+ {
+ int this_count = MIN(count, IMX233_MAX_SINGLE_DMA_XFER_SIZE / 512);
+ /* Set bank_start to the correct unit (blocks or bytes).
+ * MMC drives use block addressing, SD cards bytes or blocks */
+ int bank_start = start;
+ if(SDMMC_MODE(drive) == SD_MODE && !(SDMMC_INFO(drive).ocr & (1<<30))) /* not SDHC */
+ bank_start *= SD_BLOCK_SIZE;
+ /* issue read/write
+ * NOTE: rely on SD_{READ,WRITE}_MULTIPLE_BLOCK=MMC_{READ,WRITE}_MULTIPLE_BLOCK */
+ ret = imx233_ssp_sd_mmc_transfer(SDMMC_SSP(drive),
+ read ? SD_READ_MULTIPLE_BLOCK : SD_WRITE_MULTIPLE_BLOCK,
+ bank_start, SSP_SHORT_RESP, buf, this_count, false, read, &resp);
+ if(ret != SSP_SUCCESS)
+ break;
+ /* stop transmission
+ * NOTE: rely on SD_STOP_TRANSMISSION=MMC_STOP_TRANSMISSION */
+ if(!send_cmd(drive, SD_STOP_TRANSMISSION, 0, MCI_RESP|MCI_BUSY, &resp))
+ {
+ ret = -15;
+ break;
+ }
+ count -= this_count;
+ start += this_count;
+ buf += this_count * 512;
+ }
+ return ret;
+}
+
static int transfer_sectors(int drive, unsigned long start, int count, void *buf, bool read)
{
int ret = 0;
- uint32_t resp;
/* update disk activity */
disk_last_activity[drive] = current_tick;
@@ -488,7 +521,6 @@ static int transfer_sectors(int drive, unsigned long start, int count, void *buf
ret = -201;
goto Lend;
}
-
/* select card.
* NOTE: rely on SD_SELECT_CARD=MMC_SELECT_CARD */
if(!send_cmd(drive, SD_SELECT_CARD, SDMMC_RCA(drive), MCI_NO_RESP, NULL))
@@ -501,41 +533,63 @@ static int transfer_sectors(int drive, unsigned long start, int count, void *buf
ret = wait_for_state(drive, SD_TRAN);
if(ret < 0)
goto Ldeselect;
- while(count != 0)
+
+ /**
+ * NOTE: we need to make sure dma transfers are aligned. This handled
+ * differently for read and write transfers. We do not repeat it each
+ * time but it should be noted that all transfers are limited by
+ * IMX233_MAX_SINGLE_DMA_XFER_SIZE and thus need to be split if needed.
+ *
+ * Read transfers:
+ * If the buffer is already aligned, transfer everything at once.
+ * Otherwise, transfer all sectors but one to the sub-buffer starting
+ * on the next cache ligned and then move the data. Then transfer the
+ * last sector to the aligned_buffer and then copy to the buffer.
+ *
+ * Write transfers:
+ * If the buffer is already aligned, transfer everything at once.
+ * Otherwise, copy the first sector to the aligned_buffer and transfer.
+ * Then move all other sectors within the buffer to make it cache
+ * aligned and transfer it.
+ */
+ if(read)
{
- /* FIXME implement this_count > 1 by using a sub-buffer of [sub] that is
- * cache-aligned and then moving the data when possible. This way we could
- * transfer much greater amount of data at once */
- int this_count = 1;
- /* Set bank_start to the correct unit (blocks or bytes).
- * MMC drives use block addressing, SD cards bytes or blocks */
- int bank_start = start;
- if(SDMMC_MODE(drive) == SD_MODE && !(SDMMC_INFO(drive).ocr & (1<<30))) /* not SDHC */
- bank_start *= SD_BLOCK_SIZE;
- /* on write transfers, copy data to the aligned buffer */
- if(!read)
- memcpy(aligned_buffer[drive], buf, 512);
- /* issue read/write
- * NOTE: rely on SD_{READ,WRITE}_MULTIPLE_BLOCK=MMC_{READ,WRITE}_MULTIPLE_BLOCK */
- ret = imx233_ssp_sd_mmc_transfer(SDMMC_SSP(drive),
- read ? SD_READ_MULTIPLE_BLOCK : SD_WRITE_MULTIPLE_BLOCK,
- bank_start, SSP_SHORT_RESP, aligned_buffer[drive], this_count, false, read, &resp);
- if(ret != SSP_SUCCESS)
- break;
- /* stop transmission
- * NOTE: rely on SD_STOP_TRANSMISSION=MMC_STOP_TRANSMISSION */
- if(!send_cmd(drive, SD_STOP_TRANSMISSION, 0, MCI_RESP|MCI_BUSY, &resp))
+ void *ptr = CACHEALIGN_UP(buf);
+ if(buf != ptr)
{
- ret = -15;
- break;
+ // copy count-1 sector and then move within the buffer
+ ret = __xfer_sectors(drive, start, count - 1, ptr, read);
+ memmove(buf, ptr, 512 * (count - 1));
+ if(ret >= 0)
+ {
+ // transfer the last sector the aligned_buffer and copy
+ ret = __xfer_sectors(drive, start + count - 1, 1,
+ aligned_buffer[drive], read);
+ memcpy(buf + 512 * (count - 1), aligned_buffer[drive], 512);
+ }
}
- /* on read transfers, copy the data back to the user buffer */
- if(read)
- memcpy(buf, aligned_buffer[drive], 512);
- count -= this_count;
- start += this_count;
- buf += this_count * 512;
+ else
+ ret = __xfer_sectors(drive, start, count, buf, read);
+ }
+ else
+ {
+ void *ptr = CACHEALIGN_UP(buf);
+ if(buf != ptr)
+ {
+ // transfer the first sector to aligned_buffer and copy
+ memcpy(aligned_buffer[drive], buf, 512);
+ ret = __xfer_sectors(drive, start, 1, aligned_buffer[drive], read);
+ if(ret >= 0)
+ {
+ // move within the buffer and transfer
+ memmove(ptr, buf + 512, 512 * (count - 1));
+ ret = __xfer_sectors(drive, start + 1, count - 1, ptr, read);
+ }
+ }
+ else
+ ret = __xfer_sectors(drive, start, count, buf, read);
}
+
/* deselect card */
Ldeselect:
/* CMD7 w/rca =0 : deselects card & puts it in STBY state