diff options
author | Eric Miao <eric.miao@linaro.org> | 2012-01-12 10:21:19 +0800 |
---|---|---|
committer | Eric Miao <eric.miao@linaro.org> | 2012-01-12 10:21:19 +0800 |
commit | 9362902ae4bfa1ee0a67e715b506e19eb980f835 (patch) | |
tree | f7b28192c82ce1b7c29e644549a8a16ca6db13b9 | |
parent | 1491c822ef056472dea6fa7a69f248738cc9ccd0 (diff) | |
parent | 2bb75d3fe1b99da89cb4d921841e2e99c7ad0055 (diff) |
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.h | 2 | ||||
-rw-r--r-- | drivers/mmc/core/sd.c | 7 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-esdhc-imx.c | 75 | ||||
-rw-r--r-- | include/linux/mmc/host.h | 6 |
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 */ |