Message ID | 20220328145114.334577-1-yann.gautier@foss.st.com |
---|---|
State | New |
Headers | show |
Series | [v2] mmc: mmci: stm32: use a buffer for unaligned DMA requests | expand |
On Mon, 28 Mar 2022 at 16:51, Yann Gautier <yann.gautier@foss.st.com> wrote: > > In SDIO mode, the sg list for requests can be unaligned with what the > STM32 SDMMC internal DMA can support. In that case, instead of failing, > use a temporary bounce buffer to copy from/to the sg list. > This buffer is limited to 1MB. But for that we need to also limit > max_req_size to 1MB. It has not shown any throughput penalties for > SD-cards or eMMC. > > Signed-off-by: Yann Gautier <yann.gautier@foss.st.com> Queued up for v5.19 on the devel branch, thanks! Kind regards Uffe > --- > Changes since v1: > - allocate bounce buffer in sdmmc_idma_validate_data() > - realign on top of mmc/devel branch > (25e14a52d35928a1831ca98889a8a25ac3017990) > > drivers/mmc/host/mmci_stm32_sdmmc.c | 88 +++++++++++++++++++++++------ > 1 file changed, 71 insertions(+), 17 deletions(-) > > diff --git a/drivers/mmc/host/mmci_stm32_sdmmc.c b/drivers/mmc/host/mmci_stm32_sdmmc.c > index 4566d7fc9055..60bca78a72b1 100644 > --- a/drivers/mmc/host/mmci_stm32_sdmmc.c > +++ b/drivers/mmc/host/mmci_stm32_sdmmc.c > @@ -43,6 +43,9 @@ struct sdmmc_lli_desc { > struct sdmmc_idma { > dma_addr_t sg_dma; > void *sg_cpu; > + dma_addr_t bounce_dma_addr; > + void *bounce_buf; > + bool use_bounce_buffer; > }; > > struct sdmmc_dlyb { > @@ -54,6 +57,8 @@ struct sdmmc_dlyb { > static int sdmmc_idma_validate_data(struct mmci_host *host, > struct mmc_data *data) > { > + struct sdmmc_idma *idma = host->dma_priv; > + struct device *dev = mmc_dev(host->mmc); > struct scatterlist *sg; > int i; > > @@ -61,41 +66,69 @@ static int sdmmc_idma_validate_data(struct mmci_host *host, > * idma has constraints on idmabase & idmasize for each element > * excepted the last element which has no constraint on idmasize > */ > + idma->use_bounce_buffer = false; > for_each_sg(data->sg, sg, data->sg_len - 1, i) { > if (!IS_ALIGNED(sg->offset, sizeof(u32)) || > !IS_ALIGNED(sg->length, SDMMC_IDMA_BURST)) { > - dev_err(mmc_dev(host->mmc), > + dev_dbg(mmc_dev(host->mmc), > "unaligned scatterlist: ofst:%x length:%d\n", > data->sg->offset, data->sg->length); > - return -EINVAL; > + goto use_bounce_buffer; > } > } > > if (!IS_ALIGNED(sg->offset, sizeof(u32))) { > - dev_err(mmc_dev(host->mmc), > + dev_dbg(mmc_dev(host->mmc), > "unaligned last scatterlist: ofst:%x length:%d\n", > data->sg->offset, data->sg->length); > - return -EINVAL; > + goto use_bounce_buffer; > } > > + return 0; > + > +use_bounce_buffer: > + if (!idma->bounce_buf) { > + idma->bounce_buf = dmam_alloc_coherent(dev, > + host->mmc->max_req_size, > + &idma->bounce_dma_addr, > + GFP_KERNEL); > + if (!idma->bounce_buf) { > + dev_err(dev, "Unable to map allocate DMA bounce buffer.\n"); > + return -ENOMEM; > + } > + } > + > + idma->use_bounce_buffer = true; > + > return 0; > } > > static int _sdmmc_idma_prep_data(struct mmci_host *host, > struct mmc_data *data) > { > - int n_elem; > + struct sdmmc_idma *idma = host->dma_priv; > > - n_elem = dma_map_sg(mmc_dev(host->mmc), > - data->sg, > - data->sg_len, > - mmc_get_dma_dir(data)); > + if (idma->use_bounce_buffer) { > + if (data->flags & MMC_DATA_WRITE) { > + unsigned int xfer_bytes = data->blksz * data->blocks; > > - if (!n_elem) { > - dev_err(mmc_dev(host->mmc), "dma_map_sg failed\n"); > - return -EINVAL; > - } > + sg_copy_to_buffer(data->sg, data->sg_len, > + idma->bounce_buf, xfer_bytes); > + dma_wmb(); > + } > + } else { > + int n_elem; > + > + n_elem = dma_map_sg(mmc_dev(host->mmc), > + data->sg, > + data->sg_len, > + mmc_get_dma_dir(data)); > > + if (!n_elem) { > + dev_err(mmc_dev(host->mmc), "dma_map_sg failed\n"); > + return -EINVAL; > + } > + } > return 0; > } > > @@ -112,8 +145,19 @@ static int sdmmc_idma_prep_data(struct mmci_host *host, > static void sdmmc_idma_unprep_data(struct mmci_host *host, > struct mmc_data *data, int err) > { > - dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, > - mmc_get_dma_dir(data)); > + struct sdmmc_idma *idma = host->dma_priv; > + > + if (idma->use_bounce_buffer) { > + if (data->flags & MMC_DATA_READ) { > + unsigned int xfer_bytes = data->blksz * data->blocks; > + > + sg_copy_from_buffer(data->sg, data->sg_len, > + idma->bounce_buf, xfer_bytes); > + } > + } else { > + dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, > + mmc_get_dma_dir(data)); > + } > } > > static int sdmmc_idma_setup(struct mmci_host *host) > @@ -137,6 +181,8 @@ static int sdmmc_idma_setup(struct mmci_host *host) > host->mmc->max_segs = SDMMC_LLI_BUF_LEN / > sizeof(struct sdmmc_lli_desc); > host->mmc->max_seg_size = host->variant->stm32_idmabsize_mask; > + > + host->mmc->max_req_size = SZ_1M; > } else { > host->mmc->max_segs = 1; > host->mmc->max_seg_size = host->mmc->max_req_size; > @@ -154,8 +200,16 @@ static int sdmmc_idma_start(struct mmci_host *host, unsigned int *datactrl) > struct scatterlist *sg; > int i; > > - if (!host->variant->dma_lli || data->sg_len == 1) { > - writel_relaxed(sg_dma_address(data->sg), > + if (!host->variant->dma_lli || data->sg_len == 1 || > + idma->use_bounce_buffer) { > + u32 dma_addr; > + > + if (idma->use_bounce_buffer) > + dma_addr = idma->bounce_dma_addr; > + else > + dma_addr = sg_dma_address(data->sg); > + > + writel_relaxed(dma_addr, > host->base + MMCI_STM32_IDMABASE0R); > writel_relaxed(MMCI_STM32_IDMAEN, > host->base + MMCI_STM32_IDMACTRLR); > -- > 2.25.1 >
diff --git a/drivers/mmc/host/mmci_stm32_sdmmc.c b/drivers/mmc/host/mmci_stm32_sdmmc.c index 4566d7fc9055..60bca78a72b1 100644 --- a/drivers/mmc/host/mmci_stm32_sdmmc.c +++ b/drivers/mmc/host/mmci_stm32_sdmmc.c @@ -43,6 +43,9 @@ struct sdmmc_lli_desc { struct sdmmc_idma { dma_addr_t sg_dma; void *sg_cpu; + dma_addr_t bounce_dma_addr; + void *bounce_buf; + bool use_bounce_buffer; }; struct sdmmc_dlyb { @@ -54,6 +57,8 @@ struct sdmmc_dlyb { static int sdmmc_idma_validate_data(struct mmci_host *host, struct mmc_data *data) { + struct sdmmc_idma *idma = host->dma_priv; + struct device *dev = mmc_dev(host->mmc); struct scatterlist *sg; int i; @@ -61,41 +66,69 @@ static int sdmmc_idma_validate_data(struct mmci_host *host, * idma has constraints on idmabase & idmasize for each element * excepted the last element which has no constraint on idmasize */ + idma->use_bounce_buffer = false; for_each_sg(data->sg, sg, data->sg_len - 1, i) { if (!IS_ALIGNED(sg->offset, sizeof(u32)) || !IS_ALIGNED(sg->length, SDMMC_IDMA_BURST)) { - dev_err(mmc_dev(host->mmc), + dev_dbg(mmc_dev(host->mmc), "unaligned scatterlist: ofst:%x length:%d\n", data->sg->offset, data->sg->length); - return -EINVAL; + goto use_bounce_buffer; } } if (!IS_ALIGNED(sg->offset, sizeof(u32))) { - dev_err(mmc_dev(host->mmc), + dev_dbg(mmc_dev(host->mmc), "unaligned last scatterlist: ofst:%x length:%d\n", data->sg->offset, data->sg->length); - return -EINVAL; + goto use_bounce_buffer; } + return 0; + +use_bounce_buffer: + if (!idma->bounce_buf) { + idma->bounce_buf = dmam_alloc_coherent(dev, + host->mmc->max_req_size, + &idma->bounce_dma_addr, + GFP_KERNEL); + if (!idma->bounce_buf) { + dev_err(dev, "Unable to map allocate DMA bounce buffer.\n"); + return -ENOMEM; + } + } + + idma->use_bounce_buffer = true; + return 0; } static int _sdmmc_idma_prep_data(struct mmci_host *host, struct mmc_data *data) { - int n_elem; + struct sdmmc_idma *idma = host->dma_priv; - n_elem = dma_map_sg(mmc_dev(host->mmc), - data->sg, - data->sg_len, - mmc_get_dma_dir(data)); + if (idma->use_bounce_buffer) { + if (data->flags & MMC_DATA_WRITE) { + unsigned int xfer_bytes = data->blksz * data->blocks; - if (!n_elem) { - dev_err(mmc_dev(host->mmc), "dma_map_sg failed\n"); - return -EINVAL; - } + sg_copy_to_buffer(data->sg, data->sg_len, + idma->bounce_buf, xfer_bytes); + dma_wmb(); + } + } else { + int n_elem; + + n_elem = dma_map_sg(mmc_dev(host->mmc), + data->sg, + data->sg_len, + mmc_get_dma_dir(data)); + if (!n_elem) { + dev_err(mmc_dev(host->mmc), "dma_map_sg failed\n"); + return -EINVAL; + } + } return 0; } @@ -112,8 +145,19 @@ static int sdmmc_idma_prep_data(struct mmci_host *host, static void sdmmc_idma_unprep_data(struct mmci_host *host, struct mmc_data *data, int err) { - dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, - mmc_get_dma_dir(data)); + struct sdmmc_idma *idma = host->dma_priv; + + if (idma->use_bounce_buffer) { + if (data->flags & MMC_DATA_READ) { + unsigned int xfer_bytes = data->blksz * data->blocks; + + sg_copy_from_buffer(data->sg, data->sg_len, + idma->bounce_buf, xfer_bytes); + } + } else { + dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, + mmc_get_dma_dir(data)); + } } static int sdmmc_idma_setup(struct mmci_host *host) @@ -137,6 +181,8 @@ static int sdmmc_idma_setup(struct mmci_host *host) host->mmc->max_segs = SDMMC_LLI_BUF_LEN / sizeof(struct sdmmc_lli_desc); host->mmc->max_seg_size = host->variant->stm32_idmabsize_mask; + + host->mmc->max_req_size = SZ_1M; } else { host->mmc->max_segs = 1; host->mmc->max_seg_size = host->mmc->max_req_size; @@ -154,8 +200,16 @@ static int sdmmc_idma_start(struct mmci_host *host, unsigned int *datactrl) struct scatterlist *sg; int i; - if (!host->variant->dma_lli || data->sg_len == 1) { - writel_relaxed(sg_dma_address(data->sg), + if (!host->variant->dma_lli || data->sg_len == 1 || + idma->use_bounce_buffer) { + u32 dma_addr; + + if (idma->use_bounce_buffer) + dma_addr = idma->bounce_dma_addr; + else + dma_addr = sg_dma_address(data->sg); + + writel_relaxed(dma_addr, host->base + MMCI_STM32_IDMABASE0R); writel_relaxed(MMCI_STM32_IDMAEN, host->base + MMCI_STM32_IDMACTRLR);
In SDIO mode, the sg list for requests can be unaligned with what the STM32 SDMMC internal DMA can support. In that case, instead of failing, use a temporary bounce buffer to copy from/to the sg list. This buffer is limited to 1MB. But for that we need to also limit max_req_size to 1MB. It has not shown any throughput penalties for SD-cards or eMMC. Signed-off-by: Yann Gautier <yann.gautier@foss.st.com> --- Changes since v1: - allocate bounce buffer in sdmmc_idma_validate_data() - realign on top of mmc/devel branch (25e14a52d35928a1831ca98889a8a25ac3017990) drivers/mmc/host/mmci_stm32_sdmmc.c | 88 +++++++++++++++++++++++------ 1 file changed, 71 insertions(+), 17 deletions(-)