blob: 3b5248567973dc794d2cf1c8232317d118f1727a [file] [log] [blame]
Wolfram Sang95f25ef2010-10-15 12:21:04 +02001/*
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>
Wolfram Sang0c6d49c2011-02-26 14:44:39 +010018#include <linux/gpio.h>
Wolfram Sang95f25ef2010-10-15 12:21:04 +020019#include <linux/mmc/host.h>
20#include <linux/mmc/sdhci-pltfm.h>
Eric Bénard37865fe2010-10-23 01:57:21 +020021#include <mach/hardware.h>
Wolfram Sang0c6d49c2011-02-26 14:44:39 +010022#include <mach/esdhc.h>
Wolfram Sang95f25ef2010-10-15 12:21:04 +020023#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
Wolfram Sang7e29c302011-02-26 14:44:41 +010035static 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
Wolfram Sang95f25ef2010-10-15 12:21:04 +020068static 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
Wolfram Sang0c6d49c2011-02-26 14:44:39 +0100138static 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
Wolfram Sang7e29c302011-02-26 14:44:41 +0100157static 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
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200165static int esdhc_pltfm_init(struct sdhci_host *host, struct sdhci_pltfm_data *pdata)
166{
167 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
Wolfram Sang0c6d49c2011-02-26 14:44:39 +0100168 struct esdhc_platform_data *boarddata = host->mmc->parent->platform_data;
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200169 struct clk *clk;
Wolfram Sang0c6d49c2011-02-26 14:44:39 +0100170 int err;
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200171
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
Eric Bénard37865fe2010-10-23 01:57:21 +0200180 if (cpu_is_mx35() || cpu_is_mx51())
181 host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
182
Wolfram Sang0c6d49c2011-02-26 14:44:39 +0100183 if (cpu_is_mx25() || cpu_is_mx35()) {
184 /* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */
Eric Bénard16a790b2010-10-23 01:57:22 +0200185 host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK;
Wolfram Sang0c6d49c2011-02-26 14:44:39 +0100186 /* 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 }
Wolfram Sang7e29c302011-02-26 14:44:41 +0100197
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;
Wolfram Sang0c6d49c2011-02-26 14:44:39 +0100221 }
Eric Bénard16a790b2010-10-23 01:57:22 +0200222
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200223 return 0;
Wolfram Sang7e29c302011-02-26 14:44:41 +0100224
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;
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200231}
232
233static void esdhc_pltfm_exit(struct sdhci_host *host)
234{
235 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
Wolfram Sang0c6d49c2011-02-26 14:44:39 +0100236 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);
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200240
Wolfram Sang7e29c302011-02-26 14:44:41 +0100241 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
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200248 clk_disable(pltfm_host->clk);
249 clk_put(pltfm_host->clk);
250}
251
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200252struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
Wolfram Sang3bb2a9f2011-02-26 14:44:40 +0100253 .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_ADMA
254 | SDHCI_QUIRK_BROKEN_CARD_DETECTION,
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200255 /* ADMA has issues. Might be fixable */
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200256 .ops = &sdhci_esdhc_ops,
257 .init = esdhc_pltfm_init,
258 .exit = esdhc_pltfm_exit,
259};