summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHaibo Chen <haibo.chen@nxp.com>2017-09-01 17:05:26 +0800
committerLeonard Crestez <leonard.crestez@nxp.com>2018-08-24 12:41:33 +0300
commitebae84281a945bb16c4cf27fe067444c72ac2bfd (patch)
tree8c3ebfee46def644b0c58febf20ee6d47bc94dcc
parent5584b866bfe0cbf97a7cbdccead86971decee6d0 (diff)
MLK-16354-1 mmc: sdhci: add usdhc support for IOMMU
The default max segment size of IOMMU is 64KB, which exceed the ADMA limitation. ADMA only support max to 65535, 64KB - 1Byte. IOMMU will optimize the segments it received, merge the little segment into one big segment. If we use the default IOMMU config, then ADMA will get some segments which it's size is 64KB. Then ADMA error will shows up. Currently, when use standard tuning, driver default disable DMA. But on i.MX usdhc, this is not enough. Need also clear DMA_SEL. If not, once the DMA_SEL select AMDA2, even dma already disabled, when send tuning command, usdhc will still prefetch the ADMA script from wrong DMA address, then we will see IOMMU report some error which show lack of TLB mapping. This patch fix these two issue, make sure usdhc can work well by operate data through IOMMU. Signed-off-by: Haibo Chen <haibo.chen@nxp.com>
-rw-r--r--drivers/mmc/host/sdhci.c28
1 files changed, 26 insertions, 2 deletions
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index c498da5b46b2..35d493a0f477 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2053,6 +2053,7 @@ static void sdhci_send_tuning(struct sdhci_host *host, u32 opcode)
struct mmc_command cmd = {};
struct mmc_request mrq = {};
unsigned long flags;
+ u8 ctrl;
spin_lock_irqsave(&host->lock, flags);
@@ -2080,6 +2081,17 @@ static void sdhci_send_tuning(struct sdhci_host *host, u32 opcode)
*/
sdhci_writew(host, SDHCI_TRNS_READ, SDHCI_TRANSFER_MODE);
+ /*
+ * DMA already disabled, so clear the DMA Select here.
+ * Otherwise, if use ADMA2, even disable DMA, some
+ * controllers like i.MX usdhc will still prefetch the
+ * ADMA script when send tuning command, which will cause
+ * IOMMU report lack of TLB mapping error
+ */
+ ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
+ ctrl &= ~SDHCI_CTRL_DMA_MASK;
+ sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+
sdhci_send_command(host, &cmd);
host->cmd = NULL;
@@ -3235,6 +3247,7 @@ EXPORT_SYMBOL_GPL(__sdhci_read_caps);
int sdhci_setup_host(struct sdhci_host *host)
{
struct mmc_host *mmc;
+ struct device *dev;
u32 max_current_caps;
unsigned int ocr_avail;
unsigned int override_timeout_clk;
@@ -3246,6 +3259,7 @@ int sdhci_setup_host(struct sdhci_host *host)
return -EINVAL;
mmc = host->mmc;
+ dev = mmc_dev(mmc);
/*
* If there are external regulators, get them. Note this must be done
@@ -3681,10 +3695,20 @@ int sdhci_setup_host(struct sdhci_host *host)
* be larger than 64 KiB though.
*/
if (host->flags & SDHCI_USE_ADMA) {
- if (host->quirks & SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC)
+ if (host->quirks & SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC) {
mmc->max_seg_size = 65535;
- else
+
+ /*
+ * send the ADMA limitation to IOMMU. In default,
+ * the max segment size of IOMMU is 64KB, this exceed
+ * the ADMA max segment limitation, which is 65535.
+ */
+ dev->dma_parms = devm_kzalloc(dev,
+ sizeof(*dev->dma_parms), GFP_KERNEL);
+ dma_set_max_seg_size(dev, SZ_64K - 1);
+ } else {
mmc->max_seg_size = 65536;
+ }
} else {
mmc->max_seg_size = mmc->max_req_size;
}