From: "ht.lin" <[email protected]> Implement DMA support in the MediaTek MMC driver to enhance data transfer speed.
- Define DMA control and configuration registers - Implement functions for starting, stopping, and completing DMA transfers - Modify data transfer logic to utilize DMA when enabled - Ensure proper cache management during DMA operations Signed-off-by: Wenbin Mei <[email protected]> Signed-off-by: ht.lin <[email protected]> Signed-off-by: Julien Masson <[email protected]> Signed-off-by: Macpaul Lin <[email protected]> Signed-off-by: David Lechner <[email protected]> --- Obviously, I didn't write this patch, but I did review and test it. On MT8365 EVK, reading a 32MiB partition from the eMMC increased from ~16MiB/s to ~46MiB/s. And I did a crc32 on the data read to ensure it was the same as before. --- drivers/mmc/mtk-sd.c | 164 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 151 insertions(+), 13 deletions(-) diff --git a/drivers/mmc/mtk-sd.c b/drivers/mmc/mtk-sd.c index d434363508c..4928a880038 100644 --- a/drivers/mmc/mtk-sd.c +++ b/drivers/mmc/mtk-sd.c @@ -7,6 +7,7 @@ */ #include <clk.h> +#include <cpu_func.h> #include <dm.h> #include <mmc.h> #include <errno.h> @@ -14,6 +15,7 @@ #include <mapmem.h> #include <stdbool.h> #include <asm/gpio.h> +#include <asm/types.h> #include <dm/device_compat.h> #include <dm/pinctrl.h> #include <linux/bitfield.h> @@ -56,6 +58,8 @@ #define MSDC_INT_XFER_COMPL BIT(12) #define MSDC_INT_DATTMO BIT(14) #define MSDC_INT_DATCRCERR BIT(15) +#define MSDC_INT_BDCSERR BIT(17) +#define MSDC_INT_GPDCSERR BIT(18) /* MSDC_FIFOCS */ #define MSDC_FIFOCS_CLR BIT(31) @@ -83,6 +87,16 @@ /* SDC_ADV_CFG0 */ #define SDC_RX_ENHANCE_EN BIT(20) +/* MSDC_DMA_CTRL */ +#define MSDC_DMA_CTRL_BURSTSZ GENMASK(14, 12) +#define MSDC_DMA_CTRL_LASTBUF BIT(10) +#define MSDC_DMA_CTRL_MODE BIT(8) +#define MSDC_DMA_CTRL_STOP BIT(1) +#define MSDC_DMA_CTRL_START BIT(0) + +/* DMA_CFG */ +#define MSDC_DMA_CFG_STS BIT(0) + /* PATCH_BIT0 */ #define MSDC_INT_DAT_LATCH_CK_SEL GENMASK(9, 7) @@ -195,7 +209,8 @@ (MSDC_INT_CMDRDY | MSDC_INT_RSPCRCERR | MSDC_INT_CMDTMO) #define DATA_INTS_MASK \ - (MSDC_INT_XFER_COMPL | MSDC_INT_DATTMO | MSDC_INT_DATCRCERR) + (MSDC_INT_XFER_COMPL | MSDC_INT_DATTMO | MSDC_INT_DATCRCERR | \ + MSDC_INT_BDCSERR | MSDC_INT_GPDCSERR) /* Register offset */ struct mtk_sd_regs { @@ -294,6 +309,7 @@ struct msdc_compatible { bool builtin_pad_ctrl; bool default_pad_dly; bool use_internal_cd; + bool use_dma_mode; }; struct msdc_delay_phase { @@ -502,6 +518,9 @@ static int msdc_cmd_done(struct msdc_host *host, int events, ret = -EIO; } + /* Clear CMD interrupt */ + writel(events & CMD_INTS_MASK, &host->base->msdc_int); + return ret; } @@ -549,10 +568,9 @@ static int msdc_start_command(struct msdc_host *host, struct mmc_cmd *cmd, FIELD_GET(MSDC_FIFOCS_RXCNT, readl(&host->base->msdc_fifocs))) { pr_err("TX/RX FIFO non-empty before start of IO. Reset\n"); msdc_reset_hw(host); + msdc_fifo_clr(host); } - msdc_fifo_clr(host); - host->last_resp_type = cmd->resp_type; host->last_data_write = 0; @@ -561,8 +579,6 @@ static int msdc_start_command(struct msdc_host *host, struct mmc_cmd *cmd, if (data) blocks = data->blocks; - writel(CMD_INTS_MASK, &host->base->msdc_int); - writel(DATA_INTS_MASK, &host->base->msdc_int); writel(blocks, &host->base->sdc_blk_num); writel(cmd->cmdarg, &host->base->sdc_arg); writel(rawcmd, &host->base->sdc_cmd); @@ -704,14 +720,11 @@ static int msdc_pio_write(struct msdc_host *host, const u8 *ptr, u32 size) return ret; } -static int msdc_start_data(struct msdc_host *host, struct mmc_data *data) +static int msdc_pio_transfer(struct msdc_host *host, struct mmc_data *data) { u32 size; int ret; - if (data->flags == MMC_DATA_WRITE) - host->last_data_write = 1; - size = data->blocks * data->blocksize; if (data->flags == MMC_DATA_WRITE) @@ -719,6 +732,124 @@ static int msdc_start_data(struct msdc_host *host, struct mmc_data *data) else ret = msdc_pio_read(host, (u8 *)data->dest, size); + return ret; +} + +static dma_addr_t msdc_flush_membuf(const void *ptr, size_t size, enum dma_data_direction dir) +{ + dma_addr_t addr = (dma_addr_t)ptr; + + if (dir == DMA_FROM_DEVICE) + invalidate_dcache_range(addr, addr + size); + else + flush_dcache_range(addr, addr + size); + + return addr; +} + +static void msdc_dma_start(struct msdc_host *host, dma_addr_t addr, u32 size) +{ + writel((u32)addr, &host->base->dma_sa); + clrsetbits_le32(&host->base->dma_ctrl, MSDC_DMA_CTRL_BURSTSZ, + FIELD_PREP(MSDC_DMA_CTRL_BURSTSZ, 6)); + + /* BASIC_DMA mode */ + clrbits_le32(&host->base->dma_ctrl, MSDC_DMA_CTRL_MODE); + + /* This is the last buffer */ + setbits_le32(&host->base->dma_ctrl, MSDC_DMA_CTRL_LASTBUF); + + /* Total transfer size */ + writel(size, &host->base->dma_length); + + /* Trigger DMA start */ + setbits_le32(&host->base->dma_ctrl, MSDC_DMA_CTRL_START); +} + +static void msdc_dma_stop(struct msdc_host *host) +{ + u32 reg; + + setbits_le32(&host->base->dma_ctrl, MSDC_DMA_CTRL_STOP); + readl_poll_timeout(&host->base->dma_cfg, reg, + !(reg & MSDC_DMA_CFG_STS), 1000000); +} + +static int msdc_dma_done(struct msdc_host *host, int events) +{ + int ret = 0; + u32 rawcmd, arg; + + if (!(events & MSDC_INT_XFER_COMPL)) { + rawcmd = readl(&host->base->sdc_cmd); + arg = readl(&host->base->sdc_arg); + + if (events & MSDC_INT_DATTMO) + ret = -ETIMEDOUT; + else if (events & (MSDC_INT_DATCRCERR | MSDC_INT_GPDCSERR | MSDC_INT_BDCSERR)) + ret = -EIO; + else + ret = -EBADRQC; + + pr_err("MSDC: start data failure with %d, INT(0x%x), rawcmd=0x%x, arg=0x%x\n", + ret, events, rawcmd, arg); + } + + /* Clear DAT interrupt */ + writel(events & DATA_INTS_MASK, &host->base->msdc_int); + + return ret; +} + +static int msdc_dma_transfer(struct msdc_host *host, struct mmc_data *data) +{ + u32 size, status; + int ret; + const void *buf; + enum dma_data_direction dir; + dma_addr_t dma_addr; + + size = data->blocks * data->blocksize; + if (data->flags == MMC_DATA_WRITE) { + buf = data->src; + dir = DMA_TO_DEVICE; + } else { + buf = data->dest; + dir = DMA_FROM_DEVICE; + } + + dma_addr = msdc_flush_membuf(buf, size, dir); + msdc_dma_start(host, dma_addr, size); + + ret = readl_poll_timeout(&host->base->msdc_int, status, + status & DATA_INTS_MASK, 5000000); + if (ret) + status = MSDC_INT_DATTMO; + + msdc_dma_stop(host); + + /* + * Need invalidate the dcache again to avoid any + * cache-refill during the DMA operations (pre-fetching) + */ + if (data->flags & MMC_DATA_READ) + invalidate_dcache_range(dma_addr, dma_addr + size); + + return msdc_dma_done(host, status); +} + +static int msdc_start_data(struct msdc_host *host, struct mmc_data *data) +{ + int ret; + + if (data->flags == MMC_DATA_WRITE) + host->last_data_write = 1; + + if (host->dev_comp->use_dma_mode) + ret = msdc_dma_transfer(host, data); + else + ret = msdc_pio_transfer(host, data); + if (ret) { msdc_reset_hw(host); msdc_fifo_clr(host); @@ -1174,12 +1305,14 @@ skip_fall: if (final_maxlen == final_rise_delay.maxlen) { clrbits_le32(&host->base->msdc_iocon, MSDC_IOCON_RSPL); clrsetbits_le32(tune_reg, MSDC_PAD_TUNE_CMDRDLY, - FIELD_PREP(MSDC_PAD_TUNE_CMDRDLY, final_rise_delay.final_phase)); + FIELD_PREP(MSDC_PAD_TUNE_CMDRDLY, + final_rise_delay.final_phase)); final_delay = final_rise_delay.final_phase; } else { setbits_le32(&host->base->msdc_iocon, MSDC_IOCON_RSPL); clrsetbits_le32(tune_reg, MSDC_PAD_TUNE_CMDRDLY, - FIELD_PREP(MSDC_PAD_TUNE_CMDRDLY, final_fall_delay.final_phase)); + FIELD_PREP(MSDC_PAD_TUNE_CMDRDLY, + final_fall_delay.final_phase)); final_delay = final_fall_delay.final_phase; } @@ -1425,8 +1558,11 @@ static void msdc_init_hw(struct msdc_host *host) /* Configure to MMC/SD mode, clock free running */ setbits_le32(&host->base->msdc_cfg, MSDC_CFG_MODE); - /* Use PIO mode */ - setbits_le32(&host->base->msdc_cfg, MSDC_CFG_PIO); + /* Data transfer mode */ + if (host->dev_comp->use_dma_mode) + clrbits_le32(&host->base->msdc_cfg, MSDC_CFG_PIO); + else + setbits_le32(&host->base->msdc_cfg, MSDC_CFG_PIO); /* Reset */ msdc_reset_hw(host); @@ -1839,6 +1975,8 @@ static const struct msdc_compatible mt8183_compat = { .data_tune = true, .busy_check = true, .stop_clk_fix = true, + .enhance_rx = true, + .use_dma_mode = true, }; static const struct udevice_id msdc_ids[] = { -- 2.43.0

