mmc: sdhci-esdhc: use writel/readl as general APIs
[linux-2.6-block.git] / drivers / mmc / host / sdhci-esdhc-imx.c
CommitLineData
95f25efe
WS
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>
0c6d49ce 18#include <linux/gpio.h>
e149860d 19#include <linux/slab.h>
95f25efe
WS
20#include <linux/mmc/host.h>
21#include <linux/mmc/sdhci-pltfm.h>
37865fe9 22#include <mach/hardware.h>
0c6d49ce 23#include <mach/esdhc.h>
95f25efe
WS
24#include "sdhci.h"
25#include "sdhci-pltfm.h"
26#include "sdhci-esdhc.h"
27
e149860d
RZ
28#define ESDHC_FLAG_GPIO_FOR_CD_WP (1 << 0)
29
30struct pltfm_imx_data {
31 int flags;
32 u32 scratchpad;
33};
34
95f25efe
WS
35static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg)
36{
37 void __iomem *base = host->ioaddr + (reg & ~0x3);
38 u32 shift = (reg & 0x3) * 8;
39
40 writel(((readl(base) & ~(mask << shift)) | (val << shift)), base);
41}
42
7e29c306
WS
43static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
44{
e149860d
RZ
45 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
46 struct pltfm_imx_data *imx_data = pltfm_host->priv;
47
7e29c306
WS
48 /* fake CARD_PRESENT flag on mx25/35 */
49 u32 val = readl(host->ioaddr + reg);
50
e149860d
RZ
51 if (unlikely((reg == SDHCI_PRESENT_STATE)
52 && (imx_data->flags & ESDHC_FLAG_GPIO_FOR_CD_WP))) {
7e29c306
WS
53 struct esdhc_platform_data *boarddata =
54 host->mmc->parent->platform_data;
55
56 if (boarddata && gpio_is_valid(boarddata->cd_gpio)
57 && gpio_get_value(boarddata->cd_gpio))
58 /* no card, if a valid gpio says so... */
59 val &= SDHCI_CARD_PRESENT;
60 else
61 /* ... in all other cases assume card is present */
62 val |= SDHCI_CARD_PRESENT;
63 }
64
65 return val;
66}
67
68static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
69{
e149860d
RZ
70 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
71 struct pltfm_imx_data *imx_data = pltfm_host->priv;
72
73 if (unlikely((reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE)
74 && (imx_data->flags & ESDHC_FLAG_GPIO_FOR_CD_WP)))
7e29c306
WS
75 /*
76 * these interrupts won't work with a custom card_detect gpio
77 * (only applied to mx25/35)
78 */
79 val &= ~(SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT);
80
81 writel(val, host->ioaddr + reg);
82}
83
95f25efe
WS
84static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
85{
86 if (unlikely(reg == SDHCI_HOST_VERSION))
87 reg ^= 2;
88
89 return readw(host->ioaddr + reg);
90}
91
92static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
93{
94 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
e149860d 95 struct pltfm_imx_data *imx_data = pltfm_host->priv;
95f25efe
WS
96
97 switch (reg) {
98 case SDHCI_TRANSFER_MODE:
99 /*
100 * Postpone this write, we must do it together with a
101 * command write that is down below.
102 */
e149860d 103 imx_data->scratchpad = val;
95f25efe
WS
104 return;
105 case SDHCI_COMMAND:
e149860d 106 writel(val << 16 | imx_data->scratchpad,
95f25efe
WS
107 host->ioaddr + SDHCI_TRANSFER_MODE);
108 return;
109 case SDHCI_BLOCK_SIZE:
110 val &= ~SDHCI_MAKE_BLKSZ(0x7, 0);
111 break;
112 }
113 esdhc_clrset_le(host, 0xffff, val, reg);
114}
115
116static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
117{
118 u32 new_val;
119
120 switch (reg) {
121 case SDHCI_POWER_CONTROL:
122 /*
123 * FSL put some DMA bits here
124 * If your board has a regulator, code should be here
125 */
126 return;
127 case SDHCI_HOST_CONTROL:
128 /* FSL messed up here, so we can just keep those two */
129 new_val = val & (SDHCI_CTRL_LED | SDHCI_CTRL_4BITBUS);
130 /* ensure the endianess */
131 new_val |= ESDHC_HOST_CONTROL_LE;
132 /* DMA mode bits are shifted */
133 new_val |= (val & SDHCI_CTRL_DMA_MASK) << 5;
134
135 esdhc_clrset_le(host, 0xffff, new_val, reg);
136 return;
137 }
138 esdhc_clrset_le(host, 0xff, val, reg);
139}
140
141static unsigned int esdhc_pltfm_get_max_clock(struct sdhci_host *host)
142{
143 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
144
145 return clk_get_rate(pltfm_host->clk);
146}
147
148static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host)
149{
150 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
151
152 return clk_get_rate(pltfm_host->clk) / 256 / 16;
153}
154
0c6d49ce
WS
155static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host)
156{
157 struct esdhc_platform_data *boarddata = host->mmc->parent->platform_data;
158
159 if (boarddata && gpio_is_valid(boarddata->wp_gpio))
160 return gpio_get_value(boarddata->wp_gpio);
161 else
162 return -ENOSYS;
163}
164
165static struct sdhci_ops sdhci_esdhc_ops = {
e149860d 166 .read_l = esdhc_readl_le,
0c6d49ce 167 .read_w = esdhc_readw_le,
e149860d 168 .write_l = esdhc_writel_le,
0c6d49ce
WS
169 .write_w = esdhc_writew_le,
170 .write_b = esdhc_writeb_le,
171 .set_clock = esdhc_set_clock,
172 .get_max_clock = esdhc_pltfm_get_max_clock,
173 .get_min_clock = esdhc_pltfm_get_min_clock,
174};
175
7e29c306
WS
176static irqreturn_t cd_irq(int irq, void *data)
177{
178 struct sdhci_host *sdhost = (struct sdhci_host *)data;
179
180 tasklet_schedule(&sdhost->card_tasklet);
181 return IRQ_HANDLED;
182};
183
95f25efe
WS
184static int esdhc_pltfm_init(struct sdhci_host *host, struct sdhci_pltfm_data *pdata)
185{
186 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
0c6d49ce 187 struct esdhc_platform_data *boarddata = host->mmc->parent->platform_data;
95f25efe 188 struct clk *clk;
0c6d49ce 189 int err;
e149860d 190 struct pltfm_imx_data *imx_data;
95f25efe
WS
191
192 clk = clk_get(mmc_dev(host->mmc), NULL);
193 if (IS_ERR(clk)) {
194 dev_err(mmc_dev(host->mmc), "clk err\n");
195 return PTR_ERR(clk);
196 }
197 clk_enable(clk);
198 pltfm_host->clk = clk;
199
e149860d
RZ
200 imx_data = kzalloc(sizeof(struct pltfm_imx_data), GFP_KERNEL);
201 if (!imx_data) {
202 clk_disable(pltfm_host->clk);
203 clk_put(pltfm_host->clk);
204 return -ENOMEM;
205 }
206 pltfm_host->priv = imx_data;
207
208 if (!cpu_is_mx25())
37865fe9
EB
209 host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
210
0c6d49ce
WS
211 if (cpu_is_mx25() || cpu_is_mx35()) {
212 /* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */
16a790bc 213 host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK;
0c6d49ce
WS
214 /* write_protect can't be routed to controller, use gpio */
215 sdhci_esdhc_ops.get_ro = esdhc_pltfm_get_ro;
216 }
217
218 if (boarddata) {
219 err = gpio_request_one(boarddata->wp_gpio, GPIOF_IN, "ESDHC_WP");
220 if (err) {
221 dev_warn(mmc_dev(host->mmc),
222 "no write-protect pin available!\n");
223 boarddata->wp_gpio = err;
224 }
7e29c306
WS
225
226 err = gpio_request_one(boarddata->cd_gpio, GPIOF_IN, "ESDHC_CD");
227 if (err) {
228 dev_warn(mmc_dev(host->mmc),
229 "no card-detect pin available!\n");
230 goto no_card_detect_pin;
231 }
232
233 /* i.MX5x has issues to be researched */
234 if (!cpu_is_mx25() && !cpu_is_mx35())
235 goto not_supported;
236
237 err = request_irq(gpio_to_irq(boarddata->cd_gpio), cd_irq,
238 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
239 mmc_hostname(host->mmc), host);
240 if (err) {
241 dev_warn(mmc_dev(host->mmc), "request irq error\n");
242 goto no_card_detect_irq;
243 }
244
e149860d 245 imx_data->flags |= ESDHC_FLAG_GPIO_FOR_CD_WP;
7e29c306
WS
246 /* Now we have a working card_detect again */
247 host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
0c6d49ce 248 }
16a790bc 249
95f25efe 250 return 0;
7e29c306
WS
251
252 no_card_detect_irq:
253 gpio_free(boarddata->cd_gpio);
254 no_card_detect_pin:
255 boarddata->cd_gpio = err;
256 not_supported:
e149860d 257 kfree(imx_data);
7e29c306 258 return 0;
95f25efe
WS
259}
260
261static void esdhc_pltfm_exit(struct sdhci_host *host)
262{
263 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
0c6d49ce 264 struct esdhc_platform_data *boarddata = host->mmc->parent->platform_data;
e149860d 265 struct pltfm_imx_data *imx_data = pltfm_host->priv;
0c6d49ce
WS
266
267 if (boarddata && gpio_is_valid(boarddata->wp_gpio))
268 gpio_free(boarddata->wp_gpio);
95f25efe 269
7e29c306
WS
270 if (boarddata && gpio_is_valid(boarddata->cd_gpio)) {
271 gpio_free(boarddata->cd_gpio);
272
273 if (!(host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION))
274 free_irq(gpio_to_irq(boarddata->cd_gpio), host);
275 }
276
95f25efe
WS
277 clk_disable(pltfm_host->clk);
278 clk_put(pltfm_host->clk);
e149860d 279 kfree(imx_data);
95f25efe
WS
280}
281
95f25efe 282struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
3bb2a9f6
WS
283 .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_ADMA
284 | SDHCI_QUIRK_BROKEN_CARD_DETECTION,
95f25efe 285 /* ADMA has issues. Might be fixable */
95f25efe
WS
286 .ops = &sdhci_esdhc_ops,
287 .init = esdhc_pltfm_init,
288 .exit = esdhc_pltfm_exit,
289};