From e4243f13d10e5fbe2b84e211dcac3bc6e0792167 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 21 Feb 2011 18:35:28 +0800 Subject: mmc: mxs-mmc: add mmc host driver for i.MX23/28 This adds the mmc host driver for Freescale MXS-based SoC i.MX23/28. The driver calls into mxs-dma via generic dmaengine api for both pio and data transfer. Thanks Chris Ball for the indentation patch. Signed-off-by: Shawn Guo Reviewed-by: Arnd Bergmann Tested-by: Wolfram Sang Signed-off-by: Chris Ball --- arch/arm/mach-mxs/include/mach/mmc.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 arch/arm/mach-mxs/include/mach/mmc.h (limited to 'arch') diff --git a/arch/arm/mach-mxs/include/mach/mmc.h b/arch/arm/mach-mxs/include/mach/mmc.h new file mode 100644 index 000000000000..211547a05564 --- /dev/null +++ b/arch/arm/mach-mxs/include/mach/mmc.h @@ -0,0 +1,18 @@ +/* + * Copyright 2011 Freescale Semiconductor, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __MACH_MXS_MMC_H__ +#define __MACH_MXS_MMC_H__ + +struct mxs_mmc_platform_data { + int wp_gpio; /* write protect pin */ + unsigned int flags; +#define SLOTF_4_BIT_CAPABLE (1 << 0) +#define SLOTF_8_BIT_CAPABLE (1 << 1) +}; +#endif /* __MACH_MXS_MMC_H__ */ -- cgit v1.2.3 From 0c6d49ce53bc18c0ac637fb137f1e01ee9082201 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sat, 26 Feb 2011 14:44:39 +0100 Subject: mmc: sdhci-esdhc-imx: add write protect on custom GPIO on mx25/35 Signed-off-by: Wolfram Sang Tested-by: Marc Reilly Tested-by: Eric Benard Signed-off-by: Chris Ball --- arch/arm/plat-mxc/include/mach/esdhc.h | 10 ++++++- drivers/mmc/host/sdhci-esdhc-imx.c | 52 +++++++++++++++++++++++++++------- 2 files changed, 50 insertions(+), 12 deletions(-) (limited to 'arch') diff --git a/arch/arm/plat-mxc/include/mach/esdhc.h b/arch/arm/plat-mxc/include/mach/esdhc.h index a48a9aaa56b1..47da109ce09b 100644 --- a/arch/arm/plat-mxc/include/mach/esdhc.h +++ b/arch/arm/plat-mxc/include/mach/esdhc.h @@ -10,7 +10,15 @@ #ifndef __ASM_ARCH_IMX_ESDHC_H #define __ASM_ARCH_IMX_ESDHC_H +/** + * struct esdhc_platform_data - optional platform data for esdhc on i.MX + * + * strongly recommended for i.MX25/35, not needed for other variants + * + * @wp_gpio: gpio for write_protect (-EINVAL if unused) + */ + struct esdhc_platform_data { - unsigned int wp_gpio; /* write protect pin */ + unsigned int wp_gpio; }; #endif /* __ASM_ARCH_IMX_ESDHC_H */ diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 9b82910b9dbb..65df00bb00dd 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -15,9 +15,11 @@ #include #include #include +#include #include #include #include +#include #include "sdhci.h" #include "sdhci-pltfm.h" #include "sdhci-esdhc.h" @@ -100,10 +102,31 @@ static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host) return clk_get_rate(pltfm_host->clk) / 256 / 16; } +static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host) +{ + struct esdhc_platform_data *boarddata = host->mmc->parent->platform_data; + + if (boarddata && gpio_is_valid(boarddata->wp_gpio)) + return gpio_get_value(boarddata->wp_gpio); + else + return -ENOSYS; +} + +static struct sdhci_ops sdhci_esdhc_ops = { + .read_w = esdhc_readw_le, + .write_w = esdhc_writew_le, + .write_b = esdhc_writeb_le, + .set_clock = esdhc_set_clock, + .get_max_clock = esdhc_pltfm_get_max_clock, + .get_min_clock = esdhc_pltfm_get_min_clock, +}; + static int esdhc_pltfm_init(struct sdhci_host *host, struct sdhci_pltfm_data *pdata) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct esdhc_platform_data *boarddata = host->mmc->parent->platform_data; struct clk *clk; + int err; clk = clk_get(mmc_dev(host->mmc), NULL); if (IS_ERR(clk)) { @@ -116,9 +139,21 @@ static int esdhc_pltfm_init(struct sdhci_host *host, struct sdhci_pltfm_data *pd if (cpu_is_mx35() || cpu_is_mx51()) host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; - /* Fix errata ENGcm07207 which is present on i.MX25 and i.MX35 */ - if (cpu_is_mx25() || cpu_is_mx35()) + if (cpu_is_mx25() || cpu_is_mx35()) { + /* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */ host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK; + /* write_protect can't be routed to controller, use gpio */ + sdhci_esdhc_ops.get_ro = esdhc_pltfm_get_ro; + } + + if (boarddata) { + err = gpio_request_one(boarddata->wp_gpio, GPIOF_IN, "ESDHC_WP"); + if (err) { + dev_warn(mmc_dev(host->mmc), + "no write-protect pin available!\n"); + boarddata->wp_gpio = err; + } + } return 0; } @@ -126,20 +161,15 @@ static int esdhc_pltfm_init(struct sdhci_host *host, struct sdhci_pltfm_data *pd static void esdhc_pltfm_exit(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct esdhc_platform_data *boarddata = host->mmc->parent->platform_data; + + if (boarddata && gpio_is_valid(boarddata->wp_gpio)) + gpio_free(boarddata->wp_gpio); clk_disable(pltfm_host->clk); clk_put(pltfm_host->clk); } -static struct sdhci_ops sdhci_esdhc_ops = { - .read_w = esdhc_readw_le, - .write_w = esdhc_writew_le, - .write_b = esdhc_writeb_le, - .set_clock = esdhc_set_clock, - .get_max_clock = esdhc_pltfm_get_max_clock, - .get_min_clock = esdhc_pltfm_get_min_clock, -}; - struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = { .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_ADMA, /* ADMA has issues. Might be fixable */ -- cgit v1.2.3 From 7e29c3060369c2cd003b8069972b8535944310e4 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sat, 26 Feb 2011 14:44:41 +0100 Subject: mmc: sdhci-esdhc-imx: add card detect on custom GPIO for mx25/35 Signed-off-by: Wolfram Sang Tested-by: Marc Reilly Tested-by: Eric Benard Signed-off-by: Chris Ball --- arch/arm/plat-mxc/include/mach/esdhc.h | 2 + drivers/mmc/host/sdhci-esdhc-imx.c | 79 ++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) (limited to 'arch') diff --git a/arch/arm/plat-mxc/include/mach/esdhc.h b/arch/arm/plat-mxc/include/mach/esdhc.h index 47da109ce09b..86003f411755 100644 --- a/arch/arm/plat-mxc/include/mach/esdhc.h +++ b/arch/arm/plat-mxc/include/mach/esdhc.h @@ -16,9 +16,11 @@ * strongly recommended for i.MX25/35, not needed for other variants * * @wp_gpio: gpio for write_protect (-EINVAL if unused) + * @cd_gpio: gpio for card_detect interrupt (-EINVAL if unused) */ struct esdhc_platform_data { unsigned int wp_gpio; + unsigned int cd_gpio; }; #endif /* __ASM_ARCH_IMX_ESDHC_H */ diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 49c9801ef122..3b5248567973 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -32,6 +32,39 @@ static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, i writel(((readl(base) & ~(mask << shift)) | (val << shift)), base); } +static u32 esdhc_readl_le(struct sdhci_host *host, int reg) +{ + /* fake CARD_PRESENT flag on mx25/35 */ + u32 val = readl(host->ioaddr + reg); + + if (unlikely(reg == SDHCI_PRESENT_STATE)) { + struct esdhc_platform_data *boarddata = + host->mmc->parent->platform_data; + + if (boarddata && gpio_is_valid(boarddata->cd_gpio) + && gpio_get_value(boarddata->cd_gpio)) + /* no card, if a valid gpio says so... */ + val &= SDHCI_CARD_PRESENT; + else + /* ... in all other cases assume card is present */ + val |= SDHCI_CARD_PRESENT; + } + + return val; +} + +static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg) +{ + if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE)) + /* + * these interrupts won't work with a custom card_detect gpio + * (only applied to mx25/35) + */ + val &= ~(SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT); + + writel(val, host->ioaddr + reg); +} + static u16 esdhc_readw_le(struct sdhci_host *host, int reg) { if (unlikely(reg == SDHCI_HOST_VERSION)) @@ -121,6 +154,14 @@ static struct sdhci_ops sdhci_esdhc_ops = { .get_min_clock = esdhc_pltfm_get_min_clock, }; +static irqreturn_t cd_irq(int irq, void *data) +{ + struct sdhci_host *sdhost = (struct sdhci_host *)data; + + tasklet_schedule(&sdhost->card_tasklet); + return IRQ_HANDLED; +}; + static int esdhc_pltfm_init(struct sdhci_host *host, struct sdhci_pltfm_data *pdata) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -153,9 +194,40 @@ static int esdhc_pltfm_init(struct sdhci_host *host, struct sdhci_pltfm_data *pd "no write-protect pin available!\n"); boarddata->wp_gpio = err; } + + err = gpio_request_one(boarddata->cd_gpio, GPIOF_IN, "ESDHC_CD"); + if (err) { + dev_warn(mmc_dev(host->mmc), + "no card-detect pin available!\n"); + goto no_card_detect_pin; + } + + /* i.MX5x has issues to be researched */ + if (!cpu_is_mx25() && !cpu_is_mx35()) + goto not_supported; + + err = request_irq(gpio_to_irq(boarddata->cd_gpio), cd_irq, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + mmc_hostname(host->mmc), host); + if (err) { + dev_warn(mmc_dev(host->mmc), "request irq error\n"); + goto no_card_detect_irq; + } + + sdhci_esdhc_ops.write_l = esdhc_writel_le; + sdhci_esdhc_ops.read_l = esdhc_readl_le; + /* Now we have a working card_detect again */ + host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; } return 0; + + no_card_detect_irq: + gpio_free(boarddata->cd_gpio); + no_card_detect_pin: + boarddata->cd_gpio = err; + not_supported: + return 0; } static void esdhc_pltfm_exit(struct sdhci_host *host) @@ -166,6 +238,13 @@ static void esdhc_pltfm_exit(struct sdhci_host *host) if (boarddata && gpio_is_valid(boarddata->wp_gpio)) gpio_free(boarddata->wp_gpio); + if (boarddata && gpio_is_valid(boarddata->cd_gpio)) { + gpio_free(boarddata->cd_gpio); + + if (!(host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)) + free_irq(gpio_to_irq(boarddata->cd_gpio), host); + } + clk_disable(pltfm_host->clk); clk_put(pltfm_host->clk); } -- cgit v1.2.3