aboutsummaryrefslogtreecommitdiff
path: root/drivers/mmc
diff options
context:
space:
mode:
authorMarek Vasut <marek.vasut@gmail.com>2020-04-04 12:45:04 +0200
committerPeng Fan <peng.fan@nxp.com>2020-04-22 20:41:56 +0800
commitd2661d8e9f9e83cb15ab981cb42e84c51bd85c2d (patch)
treeae9d5ab9133f92cee21f71fd0f1b2be59c801c0a /drivers/mmc
parent80a624f2f06b2b314795d9191bb7e29cd84e7f13 (diff)
downloadu-boot-d2661d8e9f9e83cb15ab981cb42e84c51bd85c2d.zip
u-boot-d2661d8e9f9e83cb15ab981cb42e84c51bd85c2d.tar.gz
u-boot-d2661d8e9f9e83cb15ab981cb42e84c51bd85c2d.tar.bz2
mmc: tmio: sdhi: Use bounce buffer to avoid DMA limitations
The R-Car SDHI DMA controller has various restrictions. To work around those restrictions without falling back to PIO, implement bounce buffer with custom alignment check function which tests for those limitations. Signed-off-by: Marek Vasut <marek.vasut+renesas@gmail.com> Cc: Daniel Schwierzeck <daniel.schwierzeck@gmail.com> Cc: Masahiro Yamada <yamada.masahiro@socionext.com> Cc: Peng Fan <peng.fan@nxp.com> Cc: Simon Glass <sjg@chromium.org> Cc: Tom Rini <trini@konsulko.com>
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/Kconfig1
-rw-r--r--drivers/mmc/renesas-sdhi.c77
2 files changed, 78 insertions, 0 deletions
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index bb38787..8f0df56 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -358,6 +358,7 @@ config RENESAS_SDHI
depends on ARCH_RMOBILE
depends on BLK && DM_MMC
depends on OF_CONTROL
+ select BOUNCE_BUFFER
help
This selects support for the Matsushita SD/MMC Host Controller on
Renesas R-Car SoCs.
diff --git a/drivers/mmc/renesas-sdhi.c b/drivers/mmc/renesas-sdhi.c
index c3b1313..231a781 100644
--- a/drivers/mmc/renesas-sdhi.c
+++ b/drivers/mmc/renesas-sdhi.c
@@ -4,6 +4,7 @@
*/
#include <common.h>
+#include <bouncebuf.h>
#include <clk.h>
#include <fdtdec.h>
#include <malloc.h>
@@ -689,12 +690,88 @@ static int renesas_sdhi_wait_dat0(struct udevice *dev, int state,
}
#endif
+#define RENESAS_SDHI_DMA_ALIGNMENT 128
+
+static int renesas_sdhi_addr_aligned(struct bounce_buffer *state)
+{
+ uintptr_t ubuf = (uintptr_t)state->user_buffer;
+
+ /* Check if start is aligned */
+ if (!IS_ALIGNED(ubuf, RENESAS_SDHI_DMA_ALIGNMENT)) {
+ debug("Unaligned buffer address %p\n", state->user_buffer);
+ return 0;
+ }
+
+ /* Check if length is aligned */
+ if (state->len != state->len_aligned) {
+ debug("Unaligned buffer length %zu\n", state->len);
+ return 0;
+ }
+
+#ifdef CONFIG_PHYS_64BIT
+ /* Check if below 32bit boundary */
+ if ((ubuf >> 32) || (ubuf + state->len_aligned) >> 32) {
+ debug("Buffer above 32bit boundary %p-%p\n",
+ state->user_buffer,
+ state->user_buffer + state->len_aligned);
+ return 0;
+ }
+#endif
+
+ /* Aligned */
+ return 1;
+}
+
static int renesas_sdhi_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
struct mmc_data *data)
{
+ struct bounce_buffer bbstate;
+ unsigned int bbflags;
+ bool bbok = false;
+ size_t len;
+ void *buf;
int ret;
+ if (data) {
+ if (data->flags & MMC_DATA_READ) {
+ buf = data->dest;
+ bbflags = GEN_BB_WRITE;
+ } else {
+ buf = (void *)data->src;
+ bbflags = GEN_BB_READ;
+ }
+ len = data->blocks * data->blocksize;
+
+ ret = bounce_buffer_start_extalign(&bbstate, buf, len, bbflags,
+ RENESAS_SDHI_DMA_ALIGNMENT,
+ renesas_sdhi_addr_aligned);
+ /*
+ * If the amount of data to transfer is too large, we can get
+ * -ENOMEM when starting the bounce buffer. If that happens,
+ * fall back to PIO as it was before, otherwise use the BB.
+ */
+ if (!ret) {
+ bbok = true;
+ if (data->flags & MMC_DATA_READ)
+ data->dest = bbstate.bounce_buffer;
+ else
+ data->src = bbstate.bounce_buffer;
+ }
+ }
+
ret = tmio_sd_send_cmd(dev, cmd, data);
+
+ if (data && bbok) {
+ buf = bbstate.user_buffer;
+
+ bounce_buffer_stop(&bbstate);
+
+ if (data->flags & MMC_DATA_READ)
+ data->dest = buf;
+ else
+ data->src = buf;
+ }
+
if (ret)
return ret;