Hi Baolin, On 08/01/20 6:58 am, Baolin Wang wrote: > Hi Faiz, > > On Mon, Jan 6, 2020 at 7:01 PM Faiz Abbas <faiz_abbas@xxxxxx> wrote: >> >> From: Chunyan Zhang <zhang.chunyan@xxxxxxxxxx> >> >> Some standard SD host controllers can support both external dma >> controllers as well as ADMA/SDMA in which the SD host controller >> acts as DMA master. TI's omap controller is the case as an example. >> >> Currently the generic SDHCI code supports ADMA/SDMA integrated in >> the host controller but does not have any support for external DMA >> controllers implemented using dmaengine, meaning that custom code is >> needed for any systems that use an external DMA controller with SDHCI. >> >> Fixes by Faiz Abbas <faiz_abbas@xxxxxx>: >> 1. Map scatterlists before dmaengine_prep_slave_sg() >> 2. Use dma_async() functions inside of the send_command() path and call >> terminate_sync() in non-atomic context in case of an error. >> >> Signed-off-by: Chunyan Zhang <zhang.chunyan@xxxxxxxxxx> >> Signed-off-by: Faiz Abbas <faiz_abbas@xxxxxx> >> --- >> drivers/mmc/host/Kconfig | 3 + >> drivers/mmc/host/sdhci.c | 228 ++++++++++++++++++++++++++++++++++++++- >> drivers/mmc/host/sdhci.h | 8 ++ >> 3 files changed, 237 insertions(+), 2 deletions(-) >> >> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig >> index d06b2dfe3c95..adef971582a1 100644 >> --- a/drivers/mmc/host/Kconfig >> +++ b/drivers/mmc/host/Kconfig >> @@ -1040,3 +1040,6 @@ config MMC_OWL >> help >> This selects support for the SD/MMC Host Controller on >> Actions Semi Owl SoCs. >> + >> +config MMC_SDHCI_EXTERNAL_DMA >> + bool >> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c >> index f6999054abcf..8cc78c76bc3d 100644 >> --- a/drivers/mmc/host/sdhci.c >> +++ b/drivers/mmc/host/sdhci.c >> @@ -10,6 +10,7 @@ >> */ >> >> #include <linux/delay.h> >> +#include <linux/dmaengine.h> >> #include <linux/ktime.h> >> #include <linux/highmem.h> >> #include <linux/io.h> >> @@ -1157,6 +1158,188 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) >> sdhci_set_block_info(host, data); >> } >> >> +#if IS_ENABLED(CONFIG_MMC_SDHCI_EXTERNAL_DMA) >> + >> +static int sdhci_external_dma_init(struct sdhci_host *host) >> +{ >> + int ret = 0; >> + struct mmc_host *mmc = host->mmc; >> + >> + host->tx_chan = dma_request_chan(mmc->parent, "tx"); >> + if (IS_ERR(host->tx_chan)) { >> + ret = PTR_ERR(host->tx_chan); >> + if (ret != -EPROBE_DEFER) >> + pr_warn("Failed to request TX DMA channel.\n"); >> + host->tx_chan = NULL; >> + return ret; >> + } >> + >> + host->rx_chan = dma_request_chan(mmc->parent, "rx"); >> + if (IS_ERR(host->rx_chan)) { >> + if (host->tx_chan) { >> + dma_release_channel(host->tx_chan); >> + host->tx_chan = NULL; >> + } >> + >> + ret = PTR_ERR(host->rx_chan); >> + if (ret != -EPROBE_DEFER) >> + pr_warn("Failed to request RX DMA channel.\n"); >> + host->rx_chan = NULL; >> + } >> + >> + return ret; >> +} >> + >> +static struct dma_chan *sdhci_external_dma_channel(struct sdhci_host *host, >> + struct mmc_data *data) >> +{ >> + return data->flags & MMC_DATA_WRITE ? host->tx_chan : host->rx_chan; >> +} >> + >> +static int sdhci_external_dma_setup(struct sdhci_host *host, >> + struct mmc_command *cmd) >> +{ >> + int ret, i; >> + struct dma_async_tx_descriptor *desc; >> + struct mmc_data *data = cmd->data; >> + struct dma_chan *chan; >> + struct dma_slave_config cfg; >> + dma_cookie_t cookie; >> + int sg_cnt; >> + >> + if (!host->mapbase) >> + return -EINVAL; >> + >> + cfg.src_addr = host->mapbase + SDHCI_BUFFER; >> + cfg.dst_addr = host->mapbase + SDHCI_BUFFER; >> + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; >> + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; >> + cfg.src_maxburst = data->blksz / 4; >> + cfg.dst_maxburst = data->blksz / 4; >> + >> + /* Sanity check: all the SG entries must be aligned by block size. */ >> + for (i = 0; i < data->sg_len; i++) { >> + if ((data->sg + i)->length % data->blksz) >> + return -EINVAL; >> + } >> + >> + chan = sdhci_external_dma_channel(host, data); >> + >> + ret = dmaengine_slave_config(chan, &cfg); >> + if (ret) >> + return ret; >> + >> + sg_cnt = sdhci_pre_dma_transfer(host, data, COOKIE_MAPPED); >> + if (sg_cnt <= 0) >> + return -EINVAL; >> + >> + desc = dmaengine_prep_slave_sg(chan, data->sg, data->sg_len, >> + mmc_get_dma_dir(data), >> + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); >> + if (!desc) >> + return -EINVAL; >> + >> + desc->callback = NULL; >> + desc->callback_param = NULL; >> + >> + cookie = dmaengine_submit(desc); >> + if (cookie < 0) > > We usually use the DMA engine standard API: dma_submit_error() to > validate the cookie. > The if condition is doing the same thing as the API. Do we really require it? Thanks, Faiz