diff options
author | Venkat Gopalakrishnan <venkatg@codeaurora.org> | 2012-09-17 16:00:15 -0700 |
---|---|---|
committer | Subhash Jadavani <subhashj@codeaurora.org> | 2016-05-27 10:28:24 -0700 |
commit | 226107c254a8ddd29097a78d99c4ab8432a5f40f (patch) | |
tree | 39fc4b219cf90c80f9ac23bc63d35b68e0c27c36 | |
parent | 1370edc0b20767c949ca77433f21414d0c35b29a (diff) |
mmc: sdhci: Add SW workarounds for HW bugs
Initial version of Qualcomm SDHC has the following two h/w
issues. This patch adds s/w workarounds for the same.
H/W issue: Read Transfer Active/ Write Transfer Active may be not
de-asserted after end of transaction.
S/W workaround: Set Software Reset for DAT line in Software Reset
Register (Bit 2).
Added a quirk SDHCI_QUIRK2_RDWR_TX_ACTIVE_EOT to enable this workaround.
H/W issue: Slow interrupt clearance at 400KHz may cause host controller
driver interrupt handler to be called twice.
S/W Workaround: Add 40us delay in interrupt handler when operating at
initialization frequency(400KHz).
Added a quirk SDHCI_QUIRK2_SLOW_INT_CLR to enable this workaround.
Change-Id: I8b4062f101085adadd66560f77b98b04d75cb836
Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org>
Signed-off-by: Asutosh Das <asutoshd@codeaurora.org>
Signed-off-by: Sahitya Tummala <stummala@codeaurora.org>
[subhashj@codeaurora.org: fix trivial merge conflict]
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
-rw-r--r-- | drivers/mmc/host/sdhci-msm.c | 21 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci.c | 15 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci.h | 12 |
3 files changed, 46 insertions, 2 deletions
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 6b45dd682760..e9d811043f0b 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -30,6 +30,7 @@ #include "sdhci-pltfm.h" +#define SDHCI_VER_100 0x2B #define CORE_HC_MODE 0x78 #define HC_MODE_EN 0x1 @@ -1047,6 +1048,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) struct sdhci_msm_host *msm_host; struct resource *core_memres = NULL; int ret = 0, pwr_irq = 0, dead = 0; + u32 host_version; pr_debug("%s: Enter %s\n", dev_name(&pdev->dev), __func__); msm_host = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_msm_host), @@ -1145,6 +1147,25 @@ static int sdhci_msm_probe(struct platform_device *pdev) host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; host->quirks |= SDHCI_QUIRK_SINGLE_POWER_WRITE; + host_version = readl_relaxed((host->ioaddr + SDHCI_HOST_VERSION)); + dev_dbg(&pdev->dev, "Host Version: 0x%x Vendor Version 0x%x\n", + host_version, ((host_version & SDHCI_VENDOR_VER_MASK) >> + SDHCI_VENDOR_VER_SHIFT)); + if (((host_version & SDHCI_VENDOR_VER_MASK) >> + SDHCI_VENDOR_VER_SHIFT) == SDHCI_VER_100) { + /* + * Add 40us delay in interrupt handler when + * operating at initialization frequency(400KHz). + */ + host->quirks2 |= SDHCI_QUIRK2_SLOW_INT_CLR; + /* + * Set Software Reset for DAT line in Software + * Reset Register (Bit 2). + */ + host->quirks2 |= SDHCI_QUIRK2_RDWR_TX_ACTIVE_EOT; + } + + /* Setup PWRCTL irq */ pwr_irq = platform_get_irq_byname(pdev, "pwr_irq"); if (pwr_irq < 0) { dev_err(&pdev->dev, "Failed to get pwr_irq by name (%d)\n", diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 9323d7fbef1d..9a0a9069a15b 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2272,6 +2272,9 @@ static void sdhci_tasklet_finish(unsigned long param) controllers do not like that. */ sdhci_do_reset(host, SDHCI_RESET_CMD); sdhci_do_reset(host, SDHCI_RESET_DATA); + } else { + if (host->quirks2 & SDHCI_QUIRK2_RDWR_TX_ACTIVE_EOT) + sdhci_reset(host, SDHCI_RESET_DATA); } host->mrq = NULL; @@ -2588,12 +2591,20 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) result = IRQ_WAKE_THREAD; } - if (intmask & SDHCI_INT_CMD_MASK) + if (intmask & SDHCI_INT_CMD_MASK) { + if ((host->quirks2 & SDHCI_QUIRK2_SLOW_INT_CLR) && + (host->clock <= 400000)) + udelay(40); sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK, &intmask); + } - if (intmask & SDHCI_INT_DATA_MASK) + if (intmask & SDHCI_INT_DATA_MASK) { + if ((host->quirks2 & SDHCI_QUIRK2_SLOW_INT_CLR) && + (host->clock <= 400000)) + udelay(40); sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK); + } if (intmask & SDHCI_INT_BUS_POWER) pr_err("%s: Card is consuming too much power!\n", diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 9d4aa31b683a..d348e1d917cb 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -418,6 +418,18 @@ struct sdhci_host { */ #define SDHCI_QUIRK2_NEED_DELAY_AFTER_INT_CLK_RST (1<<16) +/* + * Read Transfer Active/ Write Transfer Active may be not + * de-asserted after end of transaction. Issue reset for DAT line. + */ +#define SDHCI_QUIRK2_RDWR_TX_ACTIVE_EOT (1<<17) +/* + * Slow interrupt clearance at 400KHz may cause + * host controller driver interrupt handler to + * be called twice. + */ +#define SDHCI_QUIRK2_SLOW_INT_CLR (1<<18) + int irq; /* Device IRQ */ void __iomem *ioaddr; /* Mapped address */ |