blob: 67a2da961bb57f6e751b98e9598b487500d258a1 [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>
Richard Zhue1498602011-03-25 09:18:27 -040019#include <linux/slab.h>
Wolfram Sang95f25ef2010-10-15 12:21:04 +020020#include <linux/mmc/host.h>
21#include <linux/mmc/sdhci-pltfm.h>
Eric Bénard37865fe2010-10-23 01:57:21 +020022#include <mach/hardware.h>
Wolfram Sang0c6d49c2011-02-26 14:44:39 +010023#include <mach/esdhc.h>
Wolfram Sang95f25ef2010-10-15 12:21:04 +020024#include "sdhci.h"
25#include "sdhci-pltfm.h"
26#include "sdhci-esdhc.h"
27
Richard Zhue1498602011-03-25 09:18:27 -040028#define ESDHC_FLAG_GPIO_FOR_CD_WP (1 << 0)
29
30struct pltfm_imx_data {
31 int flags;
32 u32 scratchpad;
33};
34
Wolfram Sang95f25ef2010-10-15 12:21:04 +020035static 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
Wolfram Sang7e29c302011-02-26 14:44:41 +010043static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
44{
Richard Zhue1498602011-03-25 09:18:27 -040045 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
46 struct pltfm_imx_data *imx_data = pltfm_host->priv;
47
Wolfram Sang7e29c302011-02-26 14:44:41 +010048 /* fake CARD_PRESENT flag on mx25/35 */
49 u32 val = readl(host->ioaddr + reg);
50
Richard Zhue1498602011-03-25 09:18:27 -040051 if (unlikely((reg == SDHCI_PRESENT_STATE)
52 && (imx_data->flags & ESDHC_FLAG_GPIO_FOR_CD_WP))) {
Wolfram Sang7e29c302011-02-26 14:44:41 +010053 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{
Richard Zhue1498602011-03-25 09:18:27 -040070 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)))
Wolfram Sang7e29c302011-02-26 14:44:41 +010075 /*
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
Wolfram Sang95f25ef2010-10-15 12:21:04 +020084static 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);
Richard Zhue1498602011-03-25 09:18:27 -040095 struct pltfm_imx_data *imx_data = pltfm_host->priv;
Wolfram Sang95f25ef2010-10-15 12:21:04 +020096
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 */
Richard Zhue1498602011-03-25 09:18:27 -0400103 imx_data->scratchpad = val;
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200104 return;
105 case SDHCI_COMMAND:
Richard Zhue1498602011-03-25 09:18:27 -0400106 writel(val << 16 | imx_data->scratchpad,
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200107 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
Wolfram Sang0c6d49c2011-02-26 14:44:39 +0100155static 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 = {
Richard Zhue1498602011-03-25 09:18:27 -0400166 .read_l = esdhc_readl_le,
Wolfram Sang0c6d49c2011-02-26 14:44:39 +0100167 .read_w = esdhc_readw_le,
Richard Zhue1498602011-03-25 09:18:27 -0400168 .write_l = esdhc_writel_le,
Wolfram Sang0c6d49c2011-02-26 14:44:39 +0100169 .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
Wolfram Sang7e29c302011-02-26 14:44:41 +0100176static 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
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200184static int esdhc_pltfm_init(struct sdhci_host *host, struct sdhci_pltfm_data *pdata)
185{
186 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
Wolfram Sang0c6d49c2011-02-26 14:44:39 +0100187 struct esdhc_platform_data *boarddata = host->mmc->parent->platform_data;
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200188 struct clk *clk;
Wolfram Sang0c6d49c2011-02-26 14:44:39 +0100189 int err;
Richard Zhue1498602011-03-25 09:18:27 -0400190 struct pltfm_imx_data *imx_data;
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200191
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
Richard Zhue1498602011-03-25 09:18:27 -0400200 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())
Eric Bénard37865fe2010-10-23 01:57:21 +0200209 host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
210
Wolfram Sang0c6d49c2011-02-26 14:44:39 +0100211 if (cpu_is_mx25() || cpu_is_mx35()) {
212 /* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */
Eric Bénard16a790b2010-10-23 01:57:22 +0200213 host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK;
Wolfram Sang0c6d49c2011-02-26 14:44:39 +0100214 /* 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 }
Wolfram Sang7e29c302011-02-26 14:44:41 +0100225
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
Richard Zhue1498602011-03-25 09:18:27 -0400245 imx_data->flags |= ESDHC_FLAG_GPIO_FOR_CD_WP;
Wolfram Sang7e29c302011-02-26 14:44:41 +0100246 /* Now we have a working card_detect again */
247 host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
Wolfram Sang0c6d49c2011-02-26 14:44:39 +0100248 }
Eric Bénard16a790b2010-10-23 01:57:22 +0200249
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200250 return 0;
Wolfram Sang7e29c302011-02-26 14:44:41 +0100251
252 no_card_detect_irq:
253 gpio_free(boarddata->cd_gpio);
254 no_card_detect_pin:
255 boarddata->cd_gpio = err;
256 not_supported:
Richard Zhue1498602011-03-25 09:18:27 -0400257 kfree(imx_data);
Wolfram Sang7e29c302011-02-26 14:44:41 +0100258 return 0;
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200259}
260
261static void esdhc_pltfm_exit(struct sdhci_host *host)
262{
263 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
Wolfram Sang0c6d49c2011-02-26 14:44:39 +0100264 struct esdhc_platform_data *boarddata = host->mmc->parent->platform_data;
Richard Zhue1498602011-03-25 09:18:27 -0400265 struct pltfm_imx_data *imx_data = pltfm_host->priv;
Wolfram Sang0c6d49c2011-02-26 14:44:39 +0100266
267 if (boarddata && gpio_is_valid(boarddata->wp_gpio))
268 gpio_free(boarddata->wp_gpio);
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200269
Wolfram Sang7e29c302011-02-26 14:44:41 +0100270 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
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200277 clk_disable(pltfm_host->clk);
278 clk_put(pltfm_host->clk);
Richard Zhue1498602011-03-25 09:18:27 -0400279 kfree(imx_data);
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200280}
281
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200282struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
Wolfram Sang3bb2a9f2011-02-26 14:44:40 +0100283 .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_ADMA
284 | SDHCI_QUIRK_BROKEN_CARD_DETECTION,
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200285 /* ADMA has issues. Might be fixable */
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200286 .ops = &sdhci_esdhc_ops,
287 .init = esdhc_pltfm_init,
288 .exit = esdhc_pltfm_exit,
289};