summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLudovic Desroches <ludovic.desroches@microchip.com>2017-07-26 16:02:46 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-08-11 08:49:28 -0700
commit347be00b56f72a5a6759930dc7269cb3b1fbbad6 (patch)
treeb8114599dba0b35817eb114655f20bc8105ffe48 /drivers
parentf7d3e54fb40cfc6f829b8f8ad3aab026714f2ffc (diff)
mmc: sdhci-of-at91: force card detect value for non removable devices
commit 7a1e3f143176e8ebdb2f5a9b3b47abc18b879d90 upstream. When the device is non removable, the card detect signal is often used for another purpose i.e. muxed to another SoC peripheral or used as a GPIO. It could lead to wrong behaviors depending the default value of this signal if not muxed to the SDHCI controller. Fixes: bb5f8ea4d514 ("mmc: sdhci-of-at91: introduce driver for the Atmel SDMMC") Signed-off-by: Ludovic Desroches <ludovic.desroches@microchip.com> Acked-by: Adrian Hunter <adrian.hunter@intel.com> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mmc/host/sdhci-of-at91.c35
1 files changed, 34 insertions, 1 deletions
diff --git a/drivers/mmc/host/sdhci-of-at91.c b/drivers/mmc/host/sdhci-of-at91.c
index a8b430ff117b..83b84ffec27d 100644
--- a/drivers/mmc/host/sdhci-of-at91.c
+++ b/drivers/mmc/host/sdhci-of-at91.c
@@ -31,6 +31,7 @@
#define SDMMC_MC1R 0x204
#define SDMMC_MC1R_DDR BIT(3)
+#define SDMMC_MC1R_FCD BIT(7)
#define SDMMC_CACR 0x230
#define SDMMC_CACR_CAPWREN BIT(0)
#define SDMMC_CACR_KEY (0x46 << 8)
@@ -43,6 +44,15 @@ struct sdhci_at91_priv {
struct clk *mainck;
};
+static void sdhci_at91_set_force_card_detect(struct sdhci_host *host)
+{
+ u8 mc1r;
+
+ mc1r = readb(host->ioaddr + SDMMC_MC1R);
+ mc1r |= SDMMC_MC1R_FCD;
+ writeb(mc1r, host->ioaddr + SDMMC_MC1R);
+}
+
static void sdhci_at91_set_clock(struct sdhci_host *host, unsigned int clock)
{
u16 clk;
@@ -112,10 +122,18 @@ void sdhci_at91_set_uhs_signaling(struct sdhci_host *host, unsigned int timing)
sdhci_set_uhs_signaling(host, timing);
}
+static void sdhci_at91_reset(struct sdhci_host *host, u8 mask)
+{
+ sdhci_reset(host, mask);
+
+ if (host->mmc->caps & MMC_CAP_NONREMOVABLE)
+ sdhci_at91_set_force_card_detect(host);
+}
+
static const struct sdhci_ops sdhci_at91_sama5d2_ops = {
.set_clock = sdhci_at91_set_clock,
.set_bus_width = sdhci_set_bus_width,
- .reset = sdhci_reset,
+ .reset = sdhci_at91_reset,
.set_uhs_signaling = sdhci_at91_set_uhs_signaling,
.set_power = sdhci_at91_set_power,
};
@@ -322,6 +340,21 @@ static int sdhci_at91_probe(struct platform_device *pdev)
host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
}
+ /*
+ * If the device attached to the MMC bus is not removable, it is safer
+ * to set the Force Card Detect bit. People often don't connect the
+ * card detect signal and use this pin for another purpose. If the card
+ * detect pin is not muxed to SDHCI controller, a default value is
+ * used. This value can be different from a SoC revision to another
+ * one. Problems come when this default value is not card present. To
+ * avoid this case, if the device is non removable then the card
+ * detection procedure using the SDMCC_CD signal is bypassed.
+ * This bit is reset when a software reset for all command is performed
+ * so we need to implement our own reset function to set back this bit.
+ */
+ if (host->mmc->caps & MMC_CAP_NONREMOVABLE)
+ sdhci_at91_set_force_card_detect(host);
+
pm_runtime_put_autosuspend(&pdev->dev);
return 0;