aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Miao <eric.miao@linaro.org>2012-01-12 10:21:19 +0800
committerEric Miao <eric.miao@linaro.org>2012-01-12 10:21:19 +0800
commit9362902ae4bfa1ee0a67e715b506e19eb980f835 (patch)
treef7b28192c82ce1b7c29e644549a8a16ca6db13b9
parent1491c822ef056472dea6fa7a69f248738cc9ccd0 (diff)
parent2bb75d3fe1b99da89cb4d921841e2e99c7ad0055 (diff)
downloadlinux-linaro-9362902ae4bfa1ee0a67e715b506e19eb980f835.tar.gz
Merge branch 'topic/lt-3.2-imx6-sdhc' into lt-3.2-imx6
* topic/lt-3.2-imx6-sdhc: mmc: sdhci-esdhc-imx: support of 1.8V is board specific mmc: sdhci-esdhc-imx: support 8 bit MMC mmc: sdhci-esdhc-imx: sd dat1 glitch causes system panic mmc: sdhci-esdhc-imx: workaround for TC intr coming ealier than DMA intri
-rw-r--r--arch/arm/plat-mxc/include/mach/esdhc.h2
-rw-r--r--drivers/mmc/core/sd.c7
-rw-r--r--drivers/mmc/host/sdhci-esdhc-imx.c75
-rw-r--r--include/linux/mmc/host.h6
4 files changed, 85 insertions, 5 deletions
diff --git a/arch/arm/plat-mxc/include/mach/esdhc.h b/arch/arm/plat-mxc/include/mach/esdhc.h
index aaf97481f41..4ab603e25b8 100644
--- a/arch/arm/plat-mxc/include/mach/esdhc.h
+++ b/arch/arm/plat-mxc/include/mach/esdhc.h
@@ -32,6 +32,7 @@ enum cd_types {
* @cd_gpio: gpio for card_detect interrupt
* @wp_type: type of write_protect method (see wp_types enum above)
* @cd_type: type of card_detect method (see cd_types enum above)
+ * @vdd_180: 1.8V VDD supported
*/
struct esdhc_platform_data {
@@ -39,5 +40,6 @@ struct esdhc_platform_data {
unsigned int cd_gpio;
enum wp_types wp_type;
enum cd_types cd_type;
+ int vdd_180;
};
#endif /* __ASM_ARCH_IMX_ESDHC_H */
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index f2a05ea40f2..d426b0eb45f 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -740,11 +740,10 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr)
ocr |= SD_OCR_CCS;
/*
- * If the host supports one of UHS-I modes, request the card
- * to switch to 1.8V signaling level.
+ * If the host supports one of UHS-I modes, and support 1.8V VDD,
+ * request the card to switch to 1.8V signaling level.
*/
- if (host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
- MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50))
+ if ((host->caps & (MMC_CAP_UHS)) && (host->ocr_avail_sd & MMC_VDD_165_195))
ocr |= SD_OCR_S18R;
/* If the host can supply more than 150mA, XPC should be set to 1. */
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index 38ebc4ea259..4a2a9f674b4 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -1,4 +1,5 @@
/*
+#define MMC_CAP_UHS (
* Freescale eSDHC i.MX controller driver for the platform bus.
*
* derived from the OF-version.
@@ -29,11 +30,21 @@
#include "sdhci-esdhc.h"
#define SDHCI_CTRL_D3CD 0x08
+
+#define SDHCI_PROT_CTRL_DTW (3 << 1)
+#define SDHCI_PROT_CTRL_8BIT (2 << 1)
+#define SDHCI_PROT_CTRL_4BIT (1 << 1)
+#define SDHCI_PROT_CTRL_1BIT (0 << 1)
+
/* VENDOR SPEC register */
#define SDHCI_VENDOR_SPEC 0xC0
#define SDHCI_VENDOR_SPEC_SDIO_QUIRK 0x00000002
#define SDHCI_WTMK_LVL 0x44
#define SDHCI_MIX_CTRL 0x48
+#define SDHCI_MIX_CTRL_EXE_TUNE (1 << 22)
+#define SDHCI_MIX_CTRL_SMPCLK_SEL (1 << 23)
+#define SDHCI_MIX_CTRL_AUTO_TUNE (1 << 24)
+#define SDHCI_MIX_CTRL_FBCLK_SEL (1 << 25)
/*
* There is an INT DMA ERR mis-match between eSDHC and STD SDHC SPEC:
@@ -67,6 +78,7 @@ enum imx_esdhc_type {
struct pltfm_imx_data {
int flags;
u32 scratchpad;
+ int can_vdd_180; /* support 1.8V? */
enum imx_esdhc_type devtype;
struct esdhc_platform_data boarddata;
};
@@ -170,6 +182,19 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
}
if (unlikely(reg == SDHCI_INT_STATUS)) {
+ if (is_imx6q_usdhc(imx_data)) {
+ /*
+ * on mx6q, there is low possibility that
+ * DATA END interrupt comes ealier than DMA
+ * END interrupt which is conflict with standard
+ * host controller spec. In this case, read the
+ * status register again will workaround this issue.
+ */
+ if ((val & SDHCI_INT_DATA_END) && \
+ !(val & SDHCI_INT_DMA_END))
+ val = readl(host->ioaddr + reg);
+ }
+
if (val & SDHCI_INT_VENDOR_SPEC_DMA_ERR) {
val &= ~SDHCI_INT_VENDOR_SPEC_DMA_ERR;
val |= SDHCI_INT_ADMA_ERROR;
@@ -275,7 +300,11 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
if (is_imx6q_usdhc(imx_data)) {
u32 m = readl(host->ioaddr + SDHCI_MIX_CTRL);
- m = imx_data->scratchpad | (m & 0xffff0000);
+ m = imx_data->scratchpad | \
+ (m & (SDHCI_MIX_CTRL_EXE_TUNE | \
+ SDHCI_MIX_CTRL_SMPCLK_SEL | \
+ SDHCI_MIX_CTRL_AUTO_TUNE | \
+ SDHCI_MIX_CTRL_FBCLK_SEL));
writel(m, host->ioaddr + SDHCI_MIX_CTRL);
writel(val << 16,
host->ioaddr + SDHCI_TRANSFER_MODE);
@@ -363,6 +392,22 @@ static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host)
return -ENOSYS;
}
+static int plt_8bit_width(struct sdhci_host *host, int width)
+{
+ u32 reg = sdhci_readl(host, SDHCI_HOST_CONTROL);
+
+ reg &= ~SDHCI_PROT_CTRL_DTW;
+
+ if (width == MMC_BUS_WIDTH_8)
+ reg |= SDHCI_PROT_CTRL_8BIT;
+ else if (width == MMC_BUS_WIDTH_4)
+ reg |= SDHCI_PROT_CTRL_4BIT;
+
+ sdhci_writel(host, reg, SDHCI_HOST_CONTROL);
+
+ return 0;
+}
+
static struct sdhci_ops sdhci_esdhc_ops = {
.read_l = esdhc_readl_le,
.read_w = esdhc_readw_le,
@@ -373,6 +418,7 @@ static struct sdhci_ops sdhci_esdhc_ops = {
.get_max_clock = esdhc_pltfm_get_max_clock,
.get_min_clock = esdhc_pltfm_get_min_clock,
.get_ro = esdhc_pltfm_get_ro,
+ .platform_8bit_width = plt_8bit_width,
};
static struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
@@ -418,6 +464,8 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
if (gpio_is_valid(boarddata->wp_gpio))
boarddata->wp_type = ESDHC_WP_GPIO;
+ if (of_get_property(np, "support-vdd-180", NULL))
+ boarddata->vdd_180 = 1;
return 0;
}
#else
@@ -439,6 +487,7 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev)
struct clk *clk;
int err;
struct pltfm_imx_data *imx_data;
+ u32 reg;
host = sdhci_pltfm_init(pdev, &sdhci_esdhc_imx_pdata);
if (IS_ERR(host))
@@ -466,6 +515,20 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev)
clk_enable(clk);
pltfm_host->clk = clk;
+ /* disable card interrupt enable bit, and clear status bit
+ * the default value of this enable bit is 1, but it should
+ * be 0 regarding to standard host controller spec 2.1.3.
+ * if this bit is 1, it may cause some problems.
+ * there's dat1 glitch when some cards inserting into the slot,
+ * thus wrongly generate a card interrupt that will cause
+ * system panic because it lacks of sdio handler
+ * following code will solve the problem.
+ */
+ reg = sdhci_readl(host, SDHCI_INT_ENABLE);
+ reg &= ~SDHCI_INT_CARD_INT;
+ sdhci_writel(host, reg, SDHCI_INT_ENABLE);
+ sdhci_writel(host, SDHCI_INT_CARD_INT, SDHCI_INT_STATUS);
+
if (!is_imx25_esdhc(imx_data))
host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
@@ -507,6 +570,16 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev)
boarddata->wp_gpio = -EINVAL;
}
+ /* The imx6q uSDHC capabilities will always claim to support 1.8V
+ * while this is board specific, should be initialized properly
+ */
+ if (is_imx6q_usdhc(imx_data)) {
+ host->quirks |= SDHCI_QUIRK_MISSING_CAPS;
+ host->caps = readl(host->ioaddr + SDHCI_CAPABILITIES);
+ if (!boarddata->vdd_180)
+ host->caps &= ~SDHCI_CAN_VDD_180;
+ }
+
/* card_detect */
if (boarddata->cd_type != ESDHC_CD_GPIO)
boarddata->cd_gpio = -EINVAL;
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index a3ac9c48e5d..56b13b2dc81 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -236,6 +236,12 @@ struct mmc_host {
#define MMC_CAP_CMD23 (1 << 30) /* CMD23 supported. */
#define MMC_CAP_HW_RESET (1 << 31) /* Hardware reset */
+#define MMC_CAP_UHS (MMC_CAP_UHS_SDR12 |\
+ MMC_CAP_UHS_SDR25 |\
+ MMC_CAP_UHS_SDR50 |\
+ MMC_CAP_UHS_SDR104 |\
+ MMC_CAP_UHS_DDR50)
+
unsigned int caps2; /* More host capabilities */
#define MMC_CAP2_BOOTPART_NOACC (1 << 0) /* Boot partition no access */