195f25efeSWolfram Sang /* 295f25efeSWolfram Sang * Freescale eSDHC i.MX controller driver for the platform bus. 395f25efeSWolfram Sang * 495f25efeSWolfram Sang * derived from the OF-version. 595f25efeSWolfram Sang * 695f25efeSWolfram Sang * Copyright (c) 2010 Pengutronix e.K. 795f25efeSWolfram Sang * Author: Wolfram Sang <w.sang@pengutronix.de> 895f25efeSWolfram Sang * 995f25efeSWolfram Sang * This program is free software; you can redistribute it and/or modify 1095f25efeSWolfram Sang * it under the terms of the GNU General Public License as published by 1195f25efeSWolfram Sang * the Free Software Foundation; either version 2 of the License. 1295f25efeSWolfram Sang */ 1395f25efeSWolfram Sang 1495f25efeSWolfram Sang #include <linux/io.h> 1595f25efeSWolfram Sang #include <linux/delay.h> 1695f25efeSWolfram Sang #include <linux/err.h> 1795f25efeSWolfram Sang #include <linux/clk.h> 180c6d49ceSWolfram Sang #include <linux/gpio.h> 1995f25efeSWolfram Sang #include <linux/mmc/host.h> 2095f25efeSWolfram Sang #include <linux/mmc/sdhci-pltfm.h> 2137865fe9SEric Bénard #include <mach/hardware.h> 220c6d49ceSWolfram Sang #include <mach/esdhc.h> 2395f25efeSWolfram Sang #include "sdhci.h" 2495f25efeSWolfram Sang #include "sdhci-pltfm.h" 2595f25efeSWolfram Sang #include "sdhci-esdhc.h" 2695f25efeSWolfram Sang 2795f25efeSWolfram Sang static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg) 2895f25efeSWolfram Sang { 2995f25efeSWolfram Sang void __iomem *base = host->ioaddr + (reg & ~0x3); 3095f25efeSWolfram Sang u32 shift = (reg & 0x3) * 8; 3195f25efeSWolfram Sang 3295f25efeSWolfram Sang writel(((readl(base) & ~(mask << shift)) | (val << shift)), base); 3395f25efeSWolfram Sang } 3495f25efeSWolfram Sang 3595f25efeSWolfram Sang static u16 esdhc_readw_le(struct sdhci_host *host, int reg) 3695f25efeSWolfram Sang { 3795f25efeSWolfram Sang if (unlikely(reg == SDHCI_HOST_VERSION)) 3895f25efeSWolfram Sang reg ^= 2; 3995f25efeSWolfram Sang 4095f25efeSWolfram Sang return readw(host->ioaddr + reg); 4195f25efeSWolfram Sang } 4295f25efeSWolfram Sang 4395f25efeSWolfram Sang static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) 4495f25efeSWolfram Sang { 4595f25efeSWolfram Sang struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 4695f25efeSWolfram Sang 4795f25efeSWolfram Sang switch (reg) { 4895f25efeSWolfram Sang case SDHCI_TRANSFER_MODE: 4995f25efeSWolfram Sang /* 5095f25efeSWolfram Sang * Postpone this write, we must do it together with a 5195f25efeSWolfram Sang * command write that is down below. 5295f25efeSWolfram Sang */ 5395f25efeSWolfram Sang pltfm_host->scratchpad = val; 5495f25efeSWolfram Sang return; 5595f25efeSWolfram Sang case SDHCI_COMMAND: 5695f25efeSWolfram Sang writel(val << 16 | pltfm_host->scratchpad, 5795f25efeSWolfram Sang host->ioaddr + SDHCI_TRANSFER_MODE); 5895f25efeSWolfram Sang return; 5995f25efeSWolfram Sang case SDHCI_BLOCK_SIZE: 6095f25efeSWolfram Sang val &= ~SDHCI_MAKE_BLKSZ(0x7, 0); 6195f25efeSWolfram Sang break; 6295f25efeSWolfram Sang } 6395f25efeSWolfram Sang esdhc_clrset_le(host, 0xffff, val, reg); 6495f25efeSWolfram Sang } 6595f25efeSWolfram Sang 6695f25efeSWolfram Sang static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg) 6795f25efeSWolfram Sang { 6895f25efeSWolfram Sang u32 new_val; 6995f25efeSWolfram Sang 7095f25efeSWolfram Sang switch (reg) { 7195f25efeSWolfram Sang case SDHCI_POWER_CONTROL: 7295f25efeSWolfram Sang /* 7395f25efeSWolfram Sang * FSL put some DMA bits here 7495f25efeSWolfram Sang * If your board has a regulator, code should be here 7595f25efeSWolfram Sang */ 7695f25efeSWolfram Sang return; 7795f25efeSWolfram Sang case SDHCI_HOST_CONTROL: 7895f25efeSWolfram Sang /* FSL messed up here, so we can just keep those two */ 7995f25efeSWolfram Sang new_val = val & (SDHCI_CTRL_LED | SDHCI_CTRL_4BITBUS); 8095f25efeSWolfram Sang /* ensure the endianess */ 8195f25efeSWolfram Sang new_val |= ESDHC_HOST_CONTROL_LE; 8295f25efeSWolfram Sang /* DMA mode bits are shifted */ 8395f25efeSWolfram Sang new_val |= (val & SDHCI_CTRL_DMA_MASK) << 5; 8495f25efeSWolfram Sang 8595f25efeSWolfram Sang esdhc_clrset_le(host, 0xffff, new_val, reg); 8695f25efeSWolfram Sang return; 8795f25efeSWolfram Sang } 8895f25efeSWolfram Sang esdhc_clrset_le(host, 0xff, val, reg); 8995f25efeSWolfram Sang } 9095f25efeSWolfram Sang 9195f25efeSWolfram Sang static unsigned int esdhc_pltfm_get_max_clock(struct sdhci_host *host) 9295f25efeSWolfram Sang { 9395f25efeSWolfram Sang struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 9495f25efeSWolfram Sang 9595f25efeSWolfram Sang return clk_get_rate(pltfm_host->clk); 9695f25efeSWolfram Sang } 9795f25efeSWolfram Sang 9895f25efeSWolfram Sang static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host) 9995f25efeSWolfram Sang { 10095f25efeSWolfram Sang struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 10195f25efeSWolfram Sang 10295f25efeSWolfram Sang return clk_get_rate(pltfm_host->clk) / 256 / 16; 10395f25efeSWolfram Sang } 10495f25efeSWolfram Sang 1050c6d49ceSWolfram Sang static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host) 1060c6d49ceSWolfram Sang { 1070c6d49ceSWolfram Sang struct esdhc_platform_data *boarddata = host->mmc->parent->platform_data; 1080c6d49ceSWolfram Sang 1090c6d49ceSWolfram Sang if (boarddata && gpio_is_valid(boarddata->wp_gpio)) 1100c6d49ceSWolfram Sang return gpio_get_value(boarddata->wp_gpio); 1110c6d49ceSWolfram Sang else 1120c6d49ceSWolfram Sang return -ENOSYS; 1130c6d49ceSWolfram Sang } 1140c6d49ceSWolfram Sang 1150c6d49ceSWolfram Sang static struct sdhci_ops sdhci_esdhc_ops = { 1160c6d49ceSWolfram Sang .read_w = esdhc_readw_le, 1170c6d49ceSWolfram Sang .write_w = esdhc_writew_le, 1180c6d49ceSWolfram Sang .write_b = esdhc_writeb_le, 1190c6d49ceSWolfram Sang .set_clock = esdhc_set_clock, 1200c6d49ceSWolfram Sang .get_max_clock = esdhc_pltfm_get_max_clock, 1210c6d49ceSWolfram Sang .get_min_clock = esdhc_pltfm_get_min_clock, 1220c6d49ceSWolfram Sang }; 1230c6d49ceSWolfram Sang 12495f25efeSWolfram Sang static int esdhc_pltfm_init(struct sdhci_host *host, struct sdhci_pltfm_data *pdata) 12595f25efeSWolfram Sang { 12695f25efeSWolfram Sang struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 1270c6d49ceSWolfram Sang struct esdhc_platform_data *boarddata = host->mmc->parent->platform_data; 12895f25efeSWolfram Sang struct clk *clk; 1290c6d49ceSWolfram Sang int err; 13095f25efeSWolfram Sang 13195f25efeSWolfram Sang clk = clk_get(mmc_dev(host->mmc), NULL); 13295f25efeSWolfram Sang if (IS_ERR(clk)) { 13395f25efeSWolfram Sang dev_err(mmc_dev(host->mmc), "clk err\n"); 13495f25efeSWolfram Sang return PTR_ERR(clk); 13595f25efeSWolfram Sang } 13695f25efeSWolfram Sang clk_enable(clk); 13795f25efeSWolfram Sang pltfm_host->clk = clk; 13895f25efeSWolfram Sang 13937865fe9SEric Bénard if (cpu_is_mx35() || cpu_is_mx51()) 14037865fe9SEric Bénard host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; 14137865fe9SEric Bénard 1420c6d49ceSWolfram Sang if (cpu_is_mx25() || cpu_is_mx35()) { 1430c6d49ceSWolfram Sang /* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */ 14416a790bcSEric Bénard host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK; 1450c6d49ceSWolfram Sang /* write_protect can't be routed to controller, use gpio */ 1460c6d49ceSWolfram Sang sdhci_esdhc_ops.get_ro = esdhc_pltfm_get_ro; 1470c6d49ceSWolfram Sang } 1480c6d49ceSWolfram Sang 1490c6d49ceSWolfram Sang if (boarddata) { 1500c6d49ceSWolfram Sang err = gpio_request_one(boarddata->wp_gpio, GPIOF_IN, "ESDHC_WP"); 1510c6d49ceSWolfram Sang if (err) { 1520c6d49ceSWolfram Sang dev_warn(mmc_dev(host->mmc), 1530c6d49ceSWolfram Sang "no write-protect pin available!\n"); 1540c6d49ceSWolfram Sang boarddata->wp_gpio = err; 1550c6d49ceSWolfram Sang } 1560c6d49ceSWolfram Sang } 15716a790bcSEric Bénard 15895f25efeSWolfram Sang return 0; 15995f25efeSWolfram Sang } 16095f25efeSWolfram Sang 16195f25efeSWolfram Sang static void esdhc_pltfm_exit(struct sdhci_host *host) 16295f25efeSWolfram Sang { 16395f25efeSWolfram Sang struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 1640c6d49ceSWolfram Sang struct esdhc_platform_data *boarddata = host->mmc->parent->platform_data; 1650c6d49ceSWolfram Sang 1660c6d49ceSWolfram Sang if (boarddata && gpio_is_valid(boarddata->wp_gpio)) 1670c6d49ceSWolfram Sang gpio_free(boarddata->wp_gpio); 16895f25efeSWolfram Sang 16995f25efeSWolfram Sang clk_disable(pltfm_host->clk); 17095f25efeSWolfram Sang clk_put(pltfm_host->clk); 17195f25efeSWolfram Sang } 17295f25efeSWolfram Sang 17395f25efeSWolfram Sang struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = { 17416a790bcSEric Bénard .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_ADMA, 17595f25efeSWolfram Sang /* ADMA has issues. Might be fixable */ 17695f25efeSWolfram Sang .ops = &sdhci_esdhc_ops, 17795f25efeSWolfram Sang .init = esdhc_pltfm_init, 17895f25efeSWolfram Sang .exit = esdhc_pltfm_exit, 17995f25efeSWolfram Sang }; 180