diff options
author | Wolfram Sang <w.sang@pengutronix.de> | 2011-02-26 08:44:41 -0500 |
---|---|---|
committer | Chris Ball <cjb@laptop.org> | 2011-03-17 15:35:04 -0400 |
commit | 7e29c3060369c2cd003b8069972b8535944310e4 (patch) | |
tree | 2f3bd10e1e6f1d9fdb413096ef47bbc3982c7dae /drivers/mmc/host/sdhci-esdhc-imx.c | |
parent | 3bb2a9f6a7c0887a7f79b59c5b9bff349a03247e (diff) |
mmc: sdhci-esdhc-imx: add card detect on custom GPIO for mx25/35
Signed-off-by: Wolfram Sang <w.sang@pengutronix.de>
Tested-by: Marc Reilly <marc@cpdesign.com.au>
Tested-by: Eric Benard <eric@eukrea.com>
Signed-off-by: Chris Ball <cjb@laptop.org>
Diffstat (limited to 'drivers/mmc/host/sdhci-esdhc-imx.c')
-rw-r--r-- | drivers/mmc/host/sdhci-esdhc-imx.c | 79 |
1 files changed, 79 insertions, 0 deletions
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 | |||
32 | writel(((readl(base) & ~(mask << shift)) | (val << shift)), base); | 32 | writel(((readl(base) & ~(mask << shift)) | (val << shift)), base); |
33 | } | 33 | } |
34 | 34 | ||
35 | static 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 | |||
56 | static 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 | |||
35 | static u16 esdhc_readw_le(struct sdhci_host *host, int reg) | 68 | static u16 esdhc_readw_le(struct sdhci_host *host, int reg) |
36 | { | 69 | { |
37 | if (unlikely(reg == SDHCI_HOST_VERSION)) | 70 | if (unlikely(reg == SDHCI_HOST_VERSION)) |
@@ -121,6 +154,14 @@ static struct sdhci_ops sdhci_esdhc_ops = { | |||
121 | .get_min_clock = esdhc_pltfm_get_min_clock, | 154 | .get_min_clock = esdhc_pltfm_get_min_clock, |
122 | }; | 155 | }; |
123 | 156 | ||
157 | static 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 | |||
124 | static int esdhc_pltfm_init(struct sdhci_host *host, struct sdhci_pltfm_data *pdata) | 165 | static int esdhc_pltfm_init(struct sdhci_host *host, struct sdhci_pltfm_data *pdata) |
125 | { | 166 | { |
126 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | 167 | 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 | |||
153 | "no write-protect pin available!\n"); | 194 | "no write-protect pin available!\n"); |
154 | boarddata->wp_gpio = err; | 195 | boarddata->wp_gpio = err; |
155 | } | 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; | ||
156 | } | 221 | } |
157 | 222 | ||
158 | return 0; | 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; | ||
159 | } | 231 | } |
160 | 232 | ||
161 | static void esdhc_pltfm_exit(struct sdhci_host *host) | 233 | static void esdhc_pltfm_exit(struct sdhci_host *host) |
@@ -166,6 +238,13 @@ static void esdhc_pltfm_exit(struct sdhci_host *host) | |||
166 | if (boarddata && gpio_is_valid(boarddata->wp_gpio)) | 238 | if (boarddata && gpio_is_valid(boarddata->wp_gpio)) |
167 | gpio_free(boarddata->wp_gpio); | 239 | gpio_free(boarddata->wp_gpio); |
168 | 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 | |||
169 | clk_disable(pltfm_host->clk); | 248 | clk_disable(pltfm_host->clk); |
170 | clk_put(pltfm_host->clk); | 249 | clk_put(pltfm_host->clk); |
171 | } | 250 | } |