summaryrefslogtreecommitdiff
path: root/drivers/mmc/renesas-sdhi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/renesas-sdhi.c')
-rw-r--r--drivers/mmc/renesas-sdhi.c103
1 files changed, 103 insertions, 0 deletions
diff --git a/drivers/mmc/renesas-sdhi.c b/drivers/mmc/renesas-sdhi.c
index c3b13136f8..88a7160b0a 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,94 @@ static int renesas_sdhi_wait_dat0(struct udevice *dev, int state,
}
#endif
+#define RENESAS_SDHI_DMA_ALIGNMENT 128
+
+static int renesas_sdhi_addr_aligned_gen(uintptr_t ubuf,
+ size_t len, size_t len_aligned)
+{
+ /* Check if start is aligned */
+ if (!IS_ALIGNED(ubuf, RENESAS_SDHI_DMA_ALIGNMENT)) {
+ debug("Unaligned buffer address %lx\n", ubuf);
+ return 0;
+ }
+
+ /* Check if length is aligned */
+ if (len != len_aligned) {
+ debug("Unaligned buffer length %zu\n", len);
+ return 0;
+ }
+
+#ifdef CONFIG_PHYS_64BIT
+ /* Check if below 32bit boundary */
+ if ((ubuf >> 32) || (ubuf + len_aligned) >> 32) {
+ debug("Buffer above 32bit boundary %lx-%lx\n",
+ ubuf, ubuf + len_aligned);
+ return 0;
+ }
+#endif
+
+ /* Aligned */
+ return 1;
+}
+
+static int renesas_sdhi_addr_aligned(struct bounce_buffer *state)
+{
+ uintptr_t ubuf = (uintptr_t)state->user_buffer;
+
+ return renesas_sdhi_addr_aligned_gen(ubuf, state->len,
+ state->len_aligned);
+}
+
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;
@@ -712,6 +795,24 @@ static int renesas_sdhi_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
return 0;
}
+int renesas_sdhi_get_b_max(struct udevice *dev, void *dst, lbaint_t blkcnt)
+{
+ struct tmio_sd_priv *priv = dev_get_priv(dev);
+ struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
+ struct mmc *mmc = upriv->mmc;
+ size_t len = blkcnt * mmc->read_bl_len;
+ size_t len_align = roundup(len, RENESAS_SDHI_DMA_ALIGNMENT);
+
+ if (renesas_sdhi_addr_aligned_gen((uintptr_t)dst, len, len_align)) {
+ if (priv->quirks & TMIO_SD_CAP_16BIT)
+ return U16_MAX;
+ else
+ return U32_MAX;
+ } else {
+ return (CONFIG_SYS_MALLOC_LEN / 4) / mmc->read_bl_len;
+ }
+}
+
static const struct dm_mmc_ops renesas_sdhi_ops = {
.send_cmd = renesas_sdhi_send_cmd,
.set_ios = renesas_sdhi_set_ios,
@@ -724,6 +825,7 @@ static const struct dm_mmc_ops renesas_sdhi_ops = {
#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT)
.wait_dat0 = renesas_sdhi_wait_dat0,
#endif
+ .get_b_max = renesas_sdhi_get_b_max,
};
#define RENESAS_GEN2_QUIRKS TMIO_SD_CAP_RCAR_GEN2
@@ -889,6 +991,7 @@ static int renesas_sdhi_probe(struct udevice *dev)
return ret;
}
+ priv->quirks = quirks;
ret = tmio_sd_probe(dev, quirks);
renesas_sdhi_filter_caps(dev);