diff options
Diffstat (limited to 'drivers/mmc/renesas-sdhi.c')
-rw-r--r-- | drivers/mmc/renesas-sdhi.c | 103 |
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); |