Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at 55fa6091d83160ca772fc37cebae45d42695a708 259 lines 6.9 kB view raw
1/* 2 * Freescale eSDHC i.MX controller driver for the platform bus. 3 * 4 * derived from the OF-version. 5 * 6 * Copyright (c) 2010 Pengutronix e.K. 7 * Author: Wolfram Sang <w.sang@pengutronix.de> 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License. 12 */ 13 14#include <linux/io.h> 15#include <linux/delay.h> 16#include <linux/err.h> 17#include <linux/clk.h> 18#include <linux/gpio.h> 19#include <linux/mmc/host.h> 20#include <linux/mmc/sdhci-pltfm.h> 21#include <mach/hardware.h> 22#include <mach/esdhc.h> 23#include "sdhci.h" 24#include "sdhci-pltfm.h" 25#include "sdhci-esdhc.h" 26 27static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg) 28{ 29 void __iomem *base = host->ioaddr + (reg & ~0x3); 30 u32 shift = (reg & 0x3) * 8; 31 32 writel(((readl(base) & ~(mask << shift)) | (val << shift)), base); 33} 34 35static u32 esdhc_readl_le(struct sdhci_host *host, int reg) 36{ 37 /* fake CARD_PRESENT flag on mx25/35 */ 38 u32 val = readl(host->ioaddr + reg); 39 40 if (unlikely(reg == SDHCI_PRESENT_STATE)) { 41 struct esdhc_platform_data *boarddata = 42 host->mmc->parent->platform_data; 43 44 if (boarddata && gpio_is_valid(boarddata->cd_gpio) 45 && gpio_get_value(boarddata->cd_gpio)) 46 /* no card, if a valid gpio says so... */ 47 val &= SDHCI_CARD_PRESENT; 48 else 49 /* ... in all other cases assume card is present */ 50 val |= SDHCI_CARD_PRESENT; 51 } 52 53 return val; 54} 55 56static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg) 57{ 58 if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE)) 59 /* 60 * these interrupts won't work with a custom card_detect gpio 61 * (only applied to mx25/35) 62 */ 63 val &= ~(SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT); 64 65 writel(val, host->ioaddr + reg); 66} 67 68static u16 esdhc_readw_le(struct sdhci_host *host, int reg) 69{ 70 if (unlikely(reg == SDHCI_HOST_VERSION)) 71 reg ^= 2; 72 73 return readw(host->ioaddr + reg); 74} 75 76static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) 77{ 78 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 79 80 switch (reg) { 81 case SDHCI_TRANSFER_MODE: 82 /* 83 * Postpone this write, we must do it together with a 84 * command write that is down below. 85 */ 86 pltfm_host->scratchpad = val; 87 return; 88 case SDHCI_COMMAND: 89 writel(val << 16 | pltfm_host->scratchpad, 90 host->ioaddr + SDHCI_TRANSFER_MODE); 91 return; 92 case SDHCI_BLOCK_SIZE: 93 val &= ~SDHCI_MAKE_BLKSZ(0x7, 0); 94 break; 95 } 96 esdhc_clrset_le(host, 0xffff, val, reg); 97} 98 99static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg) 100{ 101 u32 new_val; 102 103 switch (reg) { 104 case SDHCI_POWER_CONTROL: 105 /* 106 * FSL put some DMA bits here 107 * If your board has a regulator, code should be here 108 */ 109 return; 110 case SDHCI_HOST_CONTROL: 111 /* FSL messed up here, so we can just keep those two */ 112 new_val = val & (SDHCI_CTRL_LED | SDHCI_CTRL_4BITBUS); 113 /* ensure the endianess */ 114 new_val |= ESDHC_HOST_CONTROL_LE; 115 /* DMA mode bits are shifted */ 116 new_val |= (val & SDHCI_CTRL_DMA_MASK) << 5; 117 118 esdhc_clrset_le(host, 0xffff, new_val, reg); 119 return; 120 } 121 esdhc_clrset_le(host, 0xff, val, reg); 122} 123 124static unsigned int esdhc_pltfm_get_max_clock(struct sdhci_host *host) 125{ 126 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 127 128 return clk_get_rate(pltfm_host->clk); 129} 130 131static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host) 132{ 133 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 134 135 return clk_get_rate(pltfm_host->clk) / 256 / 16; 136} 137 138static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host) 139{ 140 struct esdhc_platform_data *boarddata = host->mmc->parent->platform_data; 141 142 if (boarddata && gpio_is_valid(boarddata->wp_gpio)) 143 return gpio_get_value(boarddata->wp_gpio); 144 else 145 return -ENOSYS; 146} 147 148static struct sdhci_ops sdhci_esdhc_ops = { 149 .read_w = esdhc_readw_le, 150 .write_w = esdhc_writew_le, 151 .write_b = esdhc_writeb_le, 152 .set_clock = esdhc_set_clock, 153 .get_max_clock = esdhc_pltfm_get_max_clock, 154 .get_min_clock = esdhc_pltfm_get_min_clock, 155}; 156 157static irqreturn_t cd_irq(int irq, void *data) 158{ 159 struct sdhci_host *sdhost = (struct sdhci_host *)data; 160 161 tasklet_schedule(&sdhost->card_tasklet); 162 return IRQ_HANDLED; 163}; 164 165static int esdhc_pltfm_init(struct sdhci_host *host, struct sdhci_pltfm_data *pdata) 166{ 167 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 168 struct esdhc_platform_data *boarddata = host->mmc->parent->platform_data; 169 struct clk *clk; 170 int err; 171 172 clk = clk_get(mmc_dev(host->mmc), NULL); 173 if (IS_ERR(clk)) { 174 dev_err(mmc_dev(host->mmc), "clk err\n"); 175 return PTR_ERR(clk); 176 } 177 clk_enable(clk); 178 pltfm_host->clk = clk; 179 180 if (cpu_is_mx35() || cpu_is_mx51()) 181 host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; 182 183 if (cpu_is_mx25() || cpu_is_mx35()) { 184 /* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */ 185 host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK; 186 /* write_protect can't be routed to controller, use gpio */ 187 sdhci_esdhc_ops.get_ro = esdhc_pltfm_get_ro; 188 } 189 190 if (boarddata) { 191 err = gpio_request_one(boarddata->wp_gpio, GPIOF_IN, "ESDHC_WP"); 192 if (err) { 193 dev_warn(mmc_dev(host->mmc), 194 "no write-protect pin available!\n"); 195 boarddata->wp_gpio = err; 196 } 197 198 err = gpio_request_one(boarddata->cd_gpio, GPIOF_IN, "ESDHC_CD"); 199 if (err) { 200 dev_warn(mmc_dev(host->mmc), 201 "no card-detect pin available!\n"); 202 goto no_card_detect_pin; 203 } 204 205 /* i.MX5x has issues to be researched */ 206 if (!cpu_is_mx25() && !cpu_is_mx35()) 207 goto not_supported; 208 209 err = request_irq(gpio_to_irq(boarddata->cd_gpio), cd_irq, 210 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, 211 mmc_hostname(host->mmc), host); 212 if (err) { 213 dev_warn(mmc_dev(host->mmc), "request irq error\n"); 214 goto no_card_detect_irq; 215 } 216 217 sdhci_esdhc_ops.write_l = esdhc_writel_le; 218 sdhci_esdhc_ops.read_l = esdhc_readl_le; 219 /* Now we have a working card_detect again */ 220 host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; 221 } 222 223 return 0; 224 225 no_card_detect_irq: 226 gpio_free(boarddata->cd_gpio); 227 no_card_detect_pin: 228 boarddata->cd_gpio = err; 229 not_supported: 230 return 0; 231} 232 233static void esdhc_pltfm_exit(struct sdhci_host *host) 234{ 235 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 236 struct esdhc_platform_data *boarddata = host->mmc->parent->platform_data; 237 238 if (boarddata && gpio_is_valid(boarddata->wp_gpio)) 239 gpio_free(boarddata->wp_gpio); 240 241 if (boarddata && gpio_is_valid(boarddata->cd_gpio)) { 242 gpio_free(boarddata->cd_gpio); 243 244 if (!(host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)) 245 free_irq(gpio_to_irq(boarddata->cd_gpio), host); 246 } 247 248 clk_disable(pltfm_host->clk); 249 clk_put(pltfm_host->clk); 250} 251 252struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = { 253 .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_ADMA 254 | SDHCI_QUIRK_BROKEN_CARD_DETECTION, 255 /* ADMA has issues. Might be fixable */ 256 .ops = &sdhci_esdhc_ops, 257 .init = esdhc_pltfm_init, 258 .exit = esdhc_pltfm_exit, 259};