summaryrefslogtreecommitdiffstats
path: root/firmware/target/arm/imx233/sdmmc-imx233.c
diff options
context:
space:
mode:
authorAmaury Pouly <amaury.pouly@gmail.com>2012-08-23 15:06:45 +0200
committerAmaury Pouly <amaury.pouly@gmail.com>2012-08-23 15:06:45 +0200
commit4d8c5e59e7cb670ef473b92c9ffa7fbe43f6e87d (patch)
treed396ea5567e2b98a4b5935ec1978164ab7346df5 /firmware/target/arm/imx233/sdmmc-imx233.c
parent06aa7e83a92e2064a5c12a305313a84f6205b96c (diff)
downloadrockbox-4d8c5e59e7cb670ef473b92c9ffa7fbe43f6e87d.tar.gz
rockbox-4d8c5e59e7cb670ef473b92c9ffa7fbe43f6e87d.tar.bz2
rockbox-4d8c5e59e7cb670ef473b92c9ffa7fbe43f6e87d.zip
imx233: sdmmc driver enhancement
Implement cache aligned transfer of more than one sectors. The current code now transfers almost all data at once by moving it within the buffer to make it cache aligned. This greatly improves the performance of the transfers, especially in mass storage mode. Change-Id: Ic6e78773302f368426209f6fd6099089ea34cb16
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