aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host/sdhci-esdhc-imx.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/host/sdhci-esdhc-imx.c')
-rw-r--r--drivers/mmc/host/sdhci-esdhc-imx.c134
1 files changed, 122 insertions, 12 deletions
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index 9b82910b9dbb..3b5248567973 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -15,9 +15,11 @@
15#include <linux/delay.h> 15#include <linux/delay.h>
16#include <linux/err.h> 16#include <linux/err.h>
17#include <linux/clk.h> 17#include <linux/clk.h>
18#include <linux/gpio.h>
18#include <linux/mmc/host.h> 19#include <linux/mmc/host.h>
19#include <linux/mmc/sdhci-pltfm.h> 20#include <linux/mmc/sdhci-pltfm.h>
20#include <mach/hardware.h> 21#include <mach/hardware.h>
22#include <mach/esdhc.h>
21#include "sdhci.h" 23#include "sdhci.h"
22#include "sdhci-pltfm.h" 24#include "sdhci-pltfm.h"
23#include "sdhci-esdhc.h" 25#include "sdhci-esdhc.h"
@@ -30,6 +32,39 @@ static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, i
30 writel(((readl(base) & ~(mask << shift)) | (val << shift)), base); 32 writel(((readl(base) & ~(mask << shift)) | (val << shift)), base);
31} 33}
32 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
33static u16 esdhc_readw_le(struct sdhci_host *host, int reg) 68static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
34{ 69{
35 if (unlikely(reg == SDHCI_HOST_VERSION)) 70 if (unlikely(reg == SDHCI_HOST_VERSION))
@@ -100,10 +135,39 @@ static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host)
100 return clk_get_rate(pltfm_host->clk) / 256 / 16; 135 return clk_get_rate(pltfm_host->clk) / 256 / 16;
101} 136}
102 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
103static int esdhc_pltfm_init(struct sdhci_host *host, struct sdhci_pltfm_data *pdata) 165static int esdhc_pltfm_init(struct sdhci_host *host, struct sdhci_pltfm_data *pdata)
104{ 166{
105 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 167 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
168 struct esdhc_platform_data *boarddata = host->mmc->parent->platform_data;
106 struct clk *clk; 169 struct clk *clk;
170 int err;
107 171
108 clk = clk_get(mmc_dev(host->mmc), NULL); 172 clk = clk_get(mmc_dev(host->mmc), NULL);
109 if (IS_ERR(clk)) { 173 if (IS_ERR(clk)) {
@@ -116,32 +180,78 @@ static int esdhc_pltfm_init(struct sdhci_host *host, struct sdhci_pltfm_data *pd
116 if (cpu_is_mx35() || cpu_is_mx51()) 180 if (cpu_is_mx35() || cpu_is_mx51())
117 host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; 181 host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
118 182
119 /* Fix errata ENGcm07207 which is present on i.MX25 and i.MX35 */ 183 if (cpu_is_mx25() || cpu_is_mx35()) {
120 if (cpu_is_mx25() || cpu_is_mx35()) 184 /* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */
121 host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK; 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;
122 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:
123 return 0; 230 return 0;
124} 231}
125 232
126static void esdhc_pltfm_exit(struct sdhci_host *host) 233static void esdhc_pltfm_exit(struct sdhci_host *host)
127{ 234{
128 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 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 }
129 247
130 clk_disable(pltfm_host->clk); 248 clk_disable(pltfm_host->clk);
131 clk_put(pltfm_host->clk); 249 clk_put(pltfm_host->clk);
132} 250}
133 251
134static struct sdhci_ops sdhci_esdhc_ops = {
135 .read_w = esdhc_readw_le,
136 .write_w = esdhc_writew_le,
137 .write_b = esdhc_writeb_le,
138 .set_clock = esdhc_set_clock,
139 .get_max_clock = esdhc_pltfm_get_max_clock,
140 .get_min_clock = esdhc_pltfm_get_min_clock,
141};
142
143struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = { 252struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
144 .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_ADMA, 253 .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_ADMA
254 | SDHCI_QUIRK_BROKEN_CARD_DETECTION,
145 /* ADMA has issues. Might be fixable */ 255 /* ADMA has issues. Might be fixable */
146 .ops = &sdhci_esdhc_ops, 256 .ops = &sdhci_esdhc_ops,
147 .init = esdhc_pltfm_init, 257 .init = esdhc_pltfm_init,