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> 1895f25efeSWolfram Sang #include <linux/mmc/host.h> 1995f25efeSWolfram Sang #include <linux/mmc/sdhci-pltfm.h> 2037865fe9SEric Bénard #include <mach/hardware.h> 2195f25efeSWolfram Sang #include "sdhci.h" 2295f25efeSWolfram Sang #include "sdhci-pltfm.h" 2395f25efeSWolfram Sang #include "sdhci-esdhc.h" 2495f25efeSWolfram Sang 2595f25efeSWolfram Sang static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg) 2695f25efeSWolfram Sang { 2795f25efeSWolfram Sang void __iomem *base = host->ioaddr + (reg & ~0x3); 2895f25efeSWolfram Sang u32 shift = (reg & 0x3) * 8; 2995f25efeSWolfram Sang 3095f25efeSWolfram Sang writel(((readl(base) & ~(mask << shift)) | (val << shift)), base); 3195f25efeSWolfram Sang } 3295f25efeSWolfram Sang 3395f25efeSWolfram Sang static u16 esdhc_readw_le(struct sdhci_host *host, int reg) 3495f25efeSWolfram Sang { 3595f25efeSWolfram Sang if (unlikely(reg == SDHCI_HOST_VERSION)) 3695f25efeSWolfram Sang reg ^= 2; 3795f25efeSWolfram Sang 3895f25efeSWolfram Sang return readw(host->ioaddr + reg); 3995f25efeSWolfram Sang } 4095f25efeSWolfram Sang 4195f25efeSWolfram Sang static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) 4295f25efeSWolfram Sang { 4395f25efeSWolfram Sang struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 4495f25efeSWolfram Sang 4595f25efeSWolfram Sang switch (reg) { 4695f25efeSWolfram Sang case SDHCI_TRANSFER_MODE: 4795f25efeSWolfram Sang /* 4895f25efeSWolfram Sang * Postpone this write, we must do it together with a 4995f25efeSWolfram Sang * command write that is down below. 5095f25efeSWolfram Sang */ 5195f25efeSWolfram Sang pltfm_host->scratchpad = val; 5295f25efeSWolfram Sang return; 5395f25efeSWolfram Sang case SDHCI_COMMAND: 5495f25efeSWolfram Sang writel(val << 16 | pltfm_host->scratchpad, 5595f25efeSWolfram Sang host->ioaddr + SDHCI_TRANSFER_MODE); 5695f25efeSWolfram Sang return; 5795f25efeSWolfram Sang case SDHCI_BLOCK_SIZE: 5895f25efeSWolfram Sang val &= ~SDHCI_MAKE_BLKSZ(0x7, 0); 5995f25efeSWolfram Sang break; 6095f25efeSWolfram Sang } 6195f25efeSWolfram Sang esdhc_clrset_le(host, 0xffff, val, reg); 6295f25efeSWolfram Sang } 6395f25efeSWolfram Sang 6495f25efeSWolfram Sang static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg) 6595f25efeSWolfram Sang { 6695f25efeSWolfram Sang u32 new_val; 6795f25efeSWolfram Sang 6895f25efeSWolfram Sang switch (reg) { 6995f25efeSWolfram Sang case SDHCI_POWER_CONTROL: 7095f25efeSWolfram Sang /* 7195f25efeSWolfram Sang * FSL put some DMA bits here 7295f25efeSWolfram Sang * If your board has a regulator, code should be here 7395f25efeSWolfram Sang */ 7495f25efeSWolfram Sang return; 7595f25efeSWolfram Sang case SDHCI_HOST_CONTROL: 7695f25efeSWolfram Sang /* FSL messed up here, so we can just keep those two */ 7795f25efeSWolfram Sang new_val = val & (SDHCI_CTRL_LED | SDHCI_CTRL_4BITBUS); 7895f25efeSWolfram Sang /* ensure the endianess */ 7995f25efeSWolfram Sang new_val |= ESDHC_HOST_CONTROL_LE; 8095f25efeSWolfram Sang /* DMA mode bits are shifted */ 8195f25efeSWolfram Sang new_val |= (val & SDHCI_CTRL_DMA_MASK) << 5; 8295f25efeSWolfram Sang 8395f25efeSWolfram Sang esdhc_clrset_le(host, 0xffff, new_val, reg); 8495f25efeSWolfram Sang return; 8595f25efeSWolfram Sang } 8695f25efeSWolfram Sang esdhc_clrset_le(host, 0xff, val, reg); 8795f25efeSWolfram Sang } 8895f25efeSWolfram Sang 8995f25efeSWolfram Sang static unsigned int esdhc_pltfm_get_max_clock(struct sdhci_host *host) 9095f25efeSWolfram Sang { 9195f25efeSWolfram Sang struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 9295f25efeSWolfram Sang 9395f25efeSWolfram Sang return clk_get_rate(pltfm_host->clk); 9495f25efeSWolfram Sang } 9595f25efeSWolfram Sang 9695f25efeSWolfram Sang static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host) 9795f25efeSWolfram Sang { 9895f25efeSWolfram Sang struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 9995f25efeSWolfram Sang 10095f25efeSWolfram Sang return clk_get_rate(pltfm_host->clk) / 256 / 16; 10195f25efeSWolfram Sang } 10295f25efeSWolfram Sang 10395f25efeSWolfram Sang static int esdhc_pltfm_init(struct sdhci_host *host, struct sdhci_pltfm_data *pdata) 10495f25efeSWolfram Sang { 10595f25efeSWolfram Sang struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 10695f25efeSWolfram Sang struct clk *clk; 10795f25efeSWolfram Sang 10895f25efeSWolfram Sang clk = clk_get(mmc_dev(host->mmc), NULL); 10995f25efeSWolfram Sang if (IS_ERR(clk)) { 11095f25efeSWolfram Sang dev_err(mmc_dev(host->mmc), "clk err\n"); 11195f25efeSWolfram Sang return PTR_ERR(clk); 11295f25efeSWolfram Sang } 11395f25efeSWolfram Sang clk_enable(clk); 11495f25efeSWolfram Sang pltfm_host->clk = clk; 11595f25efeSWolfram Sang 11637865fe9SEric Bénard if (cpu_is_mx35() || cpu_is_mx51()) 11737865fe9SEric Bénard host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; 11837865fe9SEric Bénard 119*16a790bcSEric Bénard /* Fix errata ENGcm07207 which is present on i.MX25 and i.MX35 */ 120*16a790bcSEric Bénard if (cpu_is_mx25() || cpu_is_mx35()) 121*16a790bcSEric Bénard host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK; 122*16a790bcSEric Bénard 12395f25efeSWolfram Sang return 0; 12495f25efeSWolfram Sang } 12595f25efeSWolfram Sang 12695f25efeSWolfram Sang static void esdhc_pltfm_exit(struct sdhci_host *host) 12795f25efeSWolfram Sang { 12895f25efeSWolfram Sang struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 12995f25efeSWolfram Sang 13095f25efeSWolfram Sang clk_disable(pltfm_host->clk); 13195f25efeSWolfram Sang clk_put(pltfm_host->clk); 13295f25efeSWolfram Sang } 13395f25efeSWolfram Sang 13495f25efeSWolfram Sang static struct sdhci_ops sdhci_esdhc_ops = { 13595f25efeSWolfram Sang .read_w = esdhc_readw_le, 13695f25efeSWolfram Sang .write_w = esdhc_writew_le, 13795f25efeSWolfram Sang .write_b = esdhc_writeb_le, 13895f25efeSWolfram Sang .set_clock = esdhc_set_clock, 13995f25efeSWolfram Sang .get_max_clock = esdhc_pltfm_get_max_clock, 14095f25efeSWolfram Sang .get_min_clock = esdhc_pltfm_get_min_clock, 14195f25efeSWolfram Sang }; 14295f25efeSWolfram Sang 14395f25efeSWolfram Sang struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = { 144*16a790bcSEric Bénard .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_ADMA, 14595f25efeSWolfram Sang /* ADMA has issues. Might be fixable */ 14695f25efeSWolfram Sang .ops = &sdhci_esdhc_ops, 14795f25efeSWolfram Sang .init = esdhc_pltfm_init, 14895f25efeSWolfram Sang .exit = esdhc_pltfm_exit, 14995f25efeSWolfram Sang }; 150