diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-01-26 18:12:47 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-01-26 18:12:47 -0500 |
commit | 5376071069ec8a7e6a8112beab16fc24f5139475 (patch) | |
tree | 81f90d6839b606d907a449d8d83b839ef829f7b0 /drivers/mmc/host/omap_hsmmc.c | |
parent | cfb901bf84fe22eb86525e9fb20675b53fb0462a (diff) | |
parent | 24f11ec001920f1cfaeeed8e8b55725d900bbb56 (diff) |
Merge master.kernel.org:/home/rmk/linux-2.6-arm
* master.kernel.org:/home/rmk/linux-2.6-arm: (22 commits)
[ARM] fix section-based ioremap
[NET] am79c961a: fix spin_lock usage
[ARM] omap: usb: thou shalt not provide empty release functions
[ARM] omap: watchdog: allow OMAP watchdog driver on OMAP34xx platforms
[ARM] 5369/1: omap mmc: Add new omap hsmmc controller for 2430 and 34xx, v3
[ARM] clkdev: fix clock matching
[ARM] 5370/1: at91: fix rm9200 watchdog
[ARM] 5368/1: arch/arm/mach-davinci/usb.c buildfix
[ARM] 5365/1: s3cmci: Use new include path of dma.h
[ARM] fix StrongARM-11x0 page copy implementation
[ARM] omap: ensure OMAP drivers pass a struct device to clk_get()
ARM: OMAP: Fix compile for h3 MMC
ARM: OMAP: Remove unused platform devices, v3
ARM: OMAP: Fix ASoC by enabling writes to XCCR and RCCR McBSP registers, v3
ARM: OMAP: Fix OSK ASoC by registering I2C board info for tlvaic23
ARM: OMAP: remove duplicated #include's
ARM: OMAP: Fix DMA CCR programming for request line > 63, v3
ARM: OMAP: Fix gpio.c compile on 15xx with CONFIG_DEBUGFS
ARM: OMAP: Fix compile for beagle
ARM: OMAP: Fix gpio by switching to generic gpio calls, v2
...
Diffstat (limited to 'drivers/mmc/host/omap_hsmmc.c')
-rw-r--r-- | drivers/mmc/host/omap_hsmmc.c | 1242 |
1 files changed, 1242 insertions, 0 deletions
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c new file mode 100644 index 000000000000..db37490f67ec --- /dev/null +++ b/drivers/mmc/host/omap_hsmmc.c | |||
@@ -0,0 +1,1242 @@ | |||
1 | /* | ||
2 | * drivers/mmc/host/omap_hsmmc.c | ||
3 | * | ||
4 | * Driver for OMAP2430/3430 MMC controller. | ||
5 | * | ||
6 | * Copyright (C) 2007 Texas Instruments. | ||
7 | * | ||
8 | * Authors: | ||
9 | * Syed Mohammed Khasim <x0khasim@ti.com> | ||
10 | * Madhusudhan <madhu.cr@ti.com> | ||
11 | * Mohit Jalori <mjalori@ti.com> | ||
12 | * | ||
13 | * This file is licensed under the terms of the GNU General Public License | ||
14 | * version 2. This program is licensed "as is" without any warranty of any | ||
15 | * kind, whether express or implied. | ||
16 | */ | ||
17 | |||
18 | #include <linux/module.h> | ||
19 | #include <linux/init.h> | ||
20 | #include <linux/interrupt.h> | ||
21 | #include <linux/delay.h> | ||
22 | #include <linux/dma-mapping.h> | ||
23 | #include <linux/platform_device.h> | ||
24 | #include <linux/workqueue.h> | ||
25 | #include <linux/timer.h> | ||
26 | #include <linux/clk.h> | ||
27 | #include <linux/mmc/host.h> | ||
28 | #include <linux/io.h> | ||
29 | #include <linux/semaphore.h> | ||
30 | #include <mach/dma.h> | ||
31 | #include <mach/hardware.h> | ||
32 | #include <mach/board.h> | ||
33 | #include <mach/mmc.h> | ||
34 | #include <mach/cpu.h> | ||
35 | |||
36 | /* OMAP HSMMC Host Controller Registers */ | ||
37 | #define OMAP_HSMMC_SYSCONFIG 0x0010 | ||
38 | #define OMAP_HSMMC_CON 0x002C | ||
39 | #define OMAP_HSMMC_BLK 0x0104 | ||
40 | #define OMAP_HSMMC_ARG 0x0108 | ||
41 | #define OMAP_HSMMC_CMD 0x010C | ||
42 | #define OMAP_HSMMC_RSP10 0x0110 | ||
43 | #define OMAP_HSMMC_RSP32 0x0114 | ||
44 | #define OMAP_HSMMC_RSP54 0x0118 | ||
45 | #define OMAP_HSMMC_RSP76 0x011C | ||
46 | #define OMAP_HSMMC_DATA 0x0120 | ||
47 | #define OMAP_HSMMC_HCTL 0x0128 | ||
48 | #define OMAP_HSMMC_SYSCTL 0x012C | ||
49 | #define OMAP_HSMMC_STAT 0x0130 | ||
50 | #define OMAP_HSMMC_IE 0x0134 | ||
51 | #define OMAP_HSMMC_ISE 0x0138 | ||
52 | #define OMAP_HSMMC_CAPA 0x0140 | ||
53 | |||
54 | #define VS18 (1 << 26) | ||
55 | #define VS30 (1 << 25) | ||
56 | #define SDVS18 (0x5 << 9) | ||
57 | #define SDVS30 (0x6 << 9) | ||
58 | #define SDVSCLR 0xFFFFF1FF | ||
59 | #define SDVSDET 0x00000400 | ||
60 | #define AUTOIDLE 0x1 | ||
61 | #define SDBP (1 << 8) | ||
62 | #define DTO 0xe | ||
63 | #define ICE 0x1 | ||
64 | #define ICS 0x2 | ||
65 | #define CEN (1 << 2) | ||
66 | #define CLKD_MASK 0x0000FFC0 | ||
67 | #define CLKD_SHIFT 6 | ||
68 | #define DTO_MASK 0x000F0000 | ||
69 | #define DTO_SHIFT 16 | ||
70 | #define INT_EN_MASK 0x307F0033 | ||
71 | #define INIT_STREAM (1 << 1) | ||
72 | #define DP_SELECT (1 << 21) | ||
73 | #define DDIR (1 << 4) | ||
74 | #define DMA_EN 0x1 | ||
75 | #define MSBS (1 << 5) | ||
76 | #define BCE (1 << 1) | ||
77 | #define FOUR_BIT (1 << 1) | ||
78 | #define CC 0x1 | ||
79 | #define TC 0x02 | ||
80 | #define OD 0x1 | ||
81 | #define ERR (1 << 15) | ||
82 | #define CMD_TIMEOUT (1 << 16) | ||
83 | #define DATA_TIMEOUT (1 << 20) | ||
84 | #define CMD_CRC (1 << 17) | ||
85 | #define DATA_CRC (1 << 21) | ||
86 | #define CARD_ERR (1 << 28) | ||
87 | #define STAT_CLEAR 0xFFFFFFFF | ||
88 | #define INIT_STREAM_CMD 0x00000000 | ||
89 | #define DUAL_VOLT_OCR_BIT 7 | ||
90 | #define SRC (1 << 25) | ||
91 | #define SRD (1 << 26) | ||
92 | |||
93 | /* | ||
94 | * FIXME: Most likely all the data using these _DEVID defines should come | ||
95 | * from the platform_data, or implemented in controller and slot specific | ||
96 | * functions. | ||
97 | */ | ||
98 | #define OMAP_MMC1_DEVID 0 | ||
99 | #define OMAP_MMC2_DEVID 1 | ||
100 | |||
101 | #define OMAP_MMC_DATADIR_NONE 0 | ||
102 | #define OMAP_MMC_DATADIR_READ 1 | ||
103 | #define OMAP_MMC_DATADIR_WRITE 2 | ||
104 | #define MMC_TIMEOUT_MS 20 | ||
105 | #define OMAP_MMC_MASTER_CLOCK 96000000 | ||
106 | #define DRIVER_NAME "mmci-omap-hs" | ||
107 | |||
108 | /* | ||
109 | * One controller can have multiple slots, like on some omap boards using | ||
110 | * omap.c controller driver. Luckily this is not currently done on any known | ||
111 | * omap_hsmmc.c device. | ||
112 | */ | ||
113 | #define mmc_slot(host) (host->pdata->slots[host->slot_id]) | ||
114 | |||
115 | /* | ||
116 | * MMC Host controller read/write API's | ||
117 | */ | ||
118 | #define OMAP_HSMMC_READ(base, reg) \ | ||
119 | __raw_readl((base) + OMAP_HSMMC_##reg) | ||
120 | |||
121 | #define OMAP_HSMMC_WRITE(base, reg, val) \ | ||
122 | __raw_writel((val), (base) + OMAP_HSMMC_##reg) | ||
123 | |||
124 | struct mmc_omap_host { | ||
125 | struct device *dev; | ||
126 | struct mmc_host *mmc; | ||
127 | struct mmc_request *mrq; | ||
128 | struct mmc_command *cmd; | ||
129 | struct mmc_data *data; | ||
130 | struct clk *fclk; | ||
131 | struct clk *iclk; | ||
132 | struct clk *dbclk; | ||
133 | struct semaphore sem; | ||
134 | struct work_struct mmc_carddetect_work; | ||
135 | void __iomem *base; | ||
136 | resource_size_t mapbase; | ||
137 | unsigned int id; | ||
138 | unsigned int dma_len; | ||
139 | unsigned int dma_dir; | ||
140 | unsigned char bus_mode; | ||
141 | unsigned char datadir; | ||
142 | u32 *buffer; | ||
143 | u32 bytesleft; | ||
144 | int suspended; | ||
145 | int irq; | ||
146 | int carddetect; | ||
147 | int use_dma, dma_ch; | ||
148 | int initstr; | ||
149 | int slot_id; | ||
150 | int dbclk_enabled; | ||
151 | struct omap_mmc_platform_data *pdata; | ||
152 | }; | ||
153 | |||
154 | /* | ||
155 | * Stop clock to the card | ||
156 | */ | ||
157 | static void omap_mmc_stop_clock(struct mmc_omap_host *host) | ||
158 | { | ||
159 | OMAP_HSMMC_WRITE(host->base, SYSCTL, | ||
160 | OMAP_HSMMC_READ(host->base, SYSCTL) & ~CEN); | ||
161 | if ((OMAP_HSMMC_READ(host->base, SYSCTL) & CEN) != 0x0) | ||
162 | dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stoped\n"); | ||
163 | } | ||
164 | |||
165 | /* | ||
166 | * Send init stream sequence to card | ||
167 | * before sending IDLE command | ||
168 | */ | ||
169 | static void send_init_stream(struct mmc_omap_host *host) | ||
170 | { | ||
171 | int reg = 0; | ||
172 | unsigned long timeout; | ||
173 | |||
174 | disable_irq(host->irq); | ||
175 | OMAP_HSMMC_WRITE(host->base, CON, | ||
176 | OMAP_HSMMC_READ(host->base, CON) | INIT_STREAM); | ||
177 | OMAP_HSMMC_WRITE(host->base, CMD, INIT_STREAM_CMD); | ||
178 | |||
179 | timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS); | ||
180 | while ((reg != CC) && time_before(jiffies, timeout)) | ||
181 | reg = OMAP_HSMMC_READ(host->base, STAT) & CC; | ||
182 | |||
183 | OMAP_HSMMC_WRITE(host->base, CON, | ||
184 | OMAP_HSMMC_READ(host->base, CON) & ~INIT_STREAM); | ||
185 | enable_irq(host->irq); | ||
186 | } | ||
187 | |||
188 | static inline | ||
189 | int mmc_omap_cover_is_closed(struct mmc_omap_host *host) | ||
190 | { | ||
191 | int r = 1; | ||
192 | |||
193 | if (host->pdata->slots[host->slot_id].get_cover_state) | ||
194 | r = host->pdata->slots[host->slot_id].get_cover_state(host->dev, | ||
195 | host->slot_id); | ||
196 | return r; | ||
197 | } | ||
198 | |||
199 | static ssize_t | ||
200 | mmc_omap_show_cover_switch(struct device *dev, struct device_attribute *attr, | ||
201 | char *buf) | ||
202 | { | ||
203 | struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev); | ||
204 | struct mmc_omap_host *host = mmc_priv(mmc); | ||
205 | |||
206 | return sprintf(buf, "%s\n", mmc_omap_cover_is_closed(host) ? "closed" : | ||
207 | "open"); | ||
208 | } | ||
209 | |||
210 | static DEVICE_ATTR(cover_switch, S_IRUGO, mmc_omap_show_cover_switch, NULL); | ||
211 | |||
212 | static ssize_t | ||
213 | mmc_omap_show_slot_name(struct device *dev, struct device_attribute *attr, | ||
214 | char *buf) | ||
215 | { | ||
216 | struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev); | ||
217 | struct mmc_omap_host *host = mmc_priv(mmc); | ||
218 | struct omap_mmc_slot_data slot = host->pdata->slots[host->slot_id]; | ||
219 | |||
220 | return sprintf(buf, "slot:%s\n", slot.name); | ||
221 | } | ||
222 | |||
223 | static DEVICE_ATTR(slot_name, S_IRUGO, mmc_omap_show_slot_name, NULL); | ||
224 | |||
225 | /* | ||
226 | * Configure the response type and send the cmd. | ||
227 | */ | ||
228 | static void | ||
229 | mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd, | ||
230 | struct mmc_data *data) | ||
231 | { | ||
232 | int cmdreg = 0, resptype = 0, cmdtype = 0; | ||
233 | |||
234 | dev_dbg(mmc_dev(host->mmc), "%s: CMD%d, argument 0x%08x\n", | ||
235 | mmc_hostname(host->mmc), cmd->opcode, cmd->arg); | ||
236 | host->cmd = cmd; | ||
237 | |||
238 | /* | ||
239 | * Clear status bits and enable interrupts | ||
240 | */ | ||
241 | OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); | ||
242 | OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK); | ||
243 | OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK); | ||
244 | |||
245 | if (cmd->flags & MMC_RSP_PRESENT) { | ||
246 | if (cmd->flags & MMC_RSP_136) | ||
247 | resptype = 1; | ||
248 | else | ||
249 | resptype = 2; | ||
250 | } | ||
251 | |||
252 | /* | ||
253 | * Unlike OMAP1 controller, the cmdtype does not seem to be based on | ||
254 | * ac, bc, adtc, bcr. Only commands ending an open ended transfer need | ||
255 | * a val of 0x3, rest 0x0. | ||
256 | */ | ||
257 | if (cmd == host->mrq->stop) | ||
258 | cmdtype = 0x3; | ||
259 | |||
260 | cmdreg = (cmd->opcode << 24) | (resptype << 16) | (cmdtype << 22); | ||
261 | |||
262 | if (data) { | ||
263 | cmdreg |= DP_SELECT | MSBS | BCE; | ||
264 | if (data->flags & MMC_DATA_READ) | ||
265 | cmdreg |= DDIR; | ||
266 | else | ||
267 | cmdreg &= ~(DDIR); | ||
268 | } | ||
269 | |||
270 | if (host->use_dma) | ||
271 | cmdreg |= DMA_EN; | ||
272 | |||
273 | OMAP_HSMMC_WRITE(host->base, ARG, cmd->arg); | ||
274 | OMAP_HSMMC_WRITE(host->base, CMD, cmdreg); | ||
275 | } | ||
276 | |||
277 | /* | ||
278 | * Notify the transfer complete to MMC core | ||
279 | */ | ||
280 | static void | ||
281 | mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data) | ||
282 | { | ||
283 | host->data = NULL; | ||
284 | |||
285 | if (host->use_dma && host->dma_ch != -1) | ||
286 | dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len, | ||
287 | host->dma_dir); | ||
288 | |||
289 | host->datadir = OMAP_MMC_DATADIR_NONE; | ||
290 | |||
291 | if (!data->error) | ||
292 | data->bytes_xfered += data->blocks * (data->blksz); | ||
293 | else | ||
294 | data->bytes_xfered = 0; | ||
295 | |||
296 | if (!data->stop) { | ||
297 | host->mrq = NULL; | ||
298 | mmc_request_done(host->mmc, data->mrq); | ||
299 | return; | ||
300 | } | ||
301 | mmc_omap_start_command(host, data->stop, NULL); | ||
302 | } | ||
303 | |||
304 | /* | ||
305 | * Notify the core about command completion | ||
306 | */ | ||
307 | static void | ||
308 | mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd) | ||
309 | { | ||
310 | host->cmd = NULL; | ||
311 | |||
312 | if (cmd->flags & MMC_RSP_PRESENT) { | ||
313 | if (cmd->flags & MMC_RSP_136) { | ||
314 | /* response type 2 */ | ||
315 | cmd->resp[3] = OMAP_HSMMC_READ(host->base, RSP10); | ||
316 | cmd->resp[2] = OMAP_HSMMC_READ(host->base, RSP32); | ||
317 | cmd->resp[1] = OMAP_HSMMC_READ(host->base, RSP54); | ||
318 | cmd->resp[0] = OMAP_HSMMC_READ(host->base, RSP76); | ||
319 | } else { | ||
320 | /* response types 1, 1b, 3, 4, 5, 6 */ | ||
321 | cmd->resp[0] = OMAP_HSMMC_READ(host->base, RSP10); | ||
322 | } | ||
323 | } | ||
324 | if (host->data == NULL || cmd->error) { | ||
325 | host->mrq = NULL; | ||
326 | mmc_request_done(host->mmc, cmd->mrq); | ||
327 | } | ||
328 | } | ||
329 | |||
330 | /* | ||
331 | * DMA clean up for command errors | ||
332 | */ | ||
333 | static void mmc_dma_cleanup(struct mmc_omap_host *host) | ||
334 | { | ||
335 | host->data->error = -ETIMEDOUT; | ||
336 | |||
337 | if (host->use_dma && host->dma_ch != -1) { | ||
338 | dma_unmap_sg(mmc_dev(host->mmc), host->data->sg, host->dma_len, | ||
339 | host->dma_dir); | ||
340 | omap_free_dma(host->dma_ch); | ||
341 | host->dma_ch = -1; | ||
342 | up(&host->sem); | ||
343 | } | ||
344 | host->data = NULL; | ||
345 | host->datadir = OMAP_MMC_DATADIR_NONE; | ||
346 | } | ||
347 | |||
348 | /* | ||
349 | * Readable error output | ||
350 | */ | ||
351 | #ifdef CONFIG_MMC_DEBUG | ||
352 | static void mmc_omap_report_irq(struct mmc_omap_host *host, u32 status) | ||
353 | { | ||
354 | /* --- means reserved bit without definition at documentation */ | ||
355 | static const char *mmc_omap_status_bits[] = { | ||
356 | "CC", "TC", "BGE", "---", "BWR", "BRR", "---", "---", "CIRQ", | ||
357 | "OBI", "---", "---", "---", "---", "---", "ERRI", "CTO", "CCRC", | ||
358 | "CEB", "CIE", "DTO", "DCRC", "DEB", "---", "ACE", "---", | ||
359 | "---", "---", "---", "CERR", "CERR", "BADA", "---", "---", "---" | ||
360 | }; | ||
361 | char res[256]; | ||
362 | char *buf = res; | ||
363 | int len, i; | ||
364 | |||
365 | len = sprintf(buf, "MMC IRQ 0x%x :", status); | ||
366 | buf += len; | ||
367 | |||
368 | for (i = 0; i < ARRAY_SIZE(mmc_omap_status_bits); i++) | ||
369 | if (status & (1 << i)) { | ||
370 | len = sprintf(buf, " %s", mmc_omap_status_bits[i]); | ||
371 | buf += len; | ||
372 | } | ||
373 | |||
374 | dev_dbg(mmc_dev(host->mmc), "%s\n", res); | ||
375 | } | ||
376 | #endif /* CONFIG_MMC_DEBUG */ | ||
377 | |||
378 | |||
379 | /* | ||
380 | * MMC controller IRQ handler | ||
381 | */ | ||
382 | static irqreturn_t mmc_omap_irq(int irq, void *dev_id) | ||
383 | { | ||
384 | struct mmc_omap_host *host = dev_id; | ||
385 | struct mmc_data *data; | ||
386 | int end_cmd = 0, end_trans = 0, status; | ||
387 | |||
388 | if (host->cmd == NULL && host->data == NULL) { | ||
389 | OMAP_HSMMC_WRITE(host->base, STAT, | ||
390 | OMAP_HSMMC_READ(host->base, STAT)); | ||
391 | return IRQ_HANDLED; | ||
392 | } | ||
393 | |||
394 | data = host->data; | ||
395 | status = OMAP_HSMMC_READ(host->base, STAT); | ||
396 | dev_dbg(mmc_dev(host->mmc), "IRQ Status is %x\n", status); | ||
397 | |||
398 | if (status & ERR) { | ||
399 | #ifdef CONFIG_MMC_DEBUG | ||
400 | mmc_omap_report_irq(host, status); | ||
401 | #endif | ||
402 | if ((status & CMD_TIMEOUT) || | ||
403 | (status & CMD_CRC)) { | ||
404 | if (host->cmd) { | ||
405 | if (status & CMD_TIMEOUT) { | ||
406 | OMAP_HSMMC_WRITE(host->base, SYSCTL, | ||
407 | OMAP_HSMMC_READ(host->base, | ||
408 | SYSCTL) | SRC); | ||
409 | while (OMAP_HSMMC_READ(host->base, | ||
410 | SYSCTL) & SRC) | ||
411 | ; | ||
412 | |||
413 | host->cmd->error = -ETIMEDOUT; | ||
414 | } else { | ||
415 | host->cmd->error = -EILSEQ; | ||
416 | } | ||
417 | end_cmd = 1; | ||
418 | } | ||
419 | if (host->data) | ||
420 | mmc_dma_cleanup(host); | ||
421 | } | ||
422 | if ((status & DATA_TIMEOUT) || | ||
423 | (status & DATA_CRC)) { | ||
424 | if (host->data) { | ||
425 | if (status & DATA_TIMEOUT) | ||
426 | mmc_dma_cleanup(host); | ||
427 | else | ||
428 | host->data->error = -EILSEQ; | ||
429 | OMAP_HSMMC_WRITE(host->base, SYSCTL, | ||
430 | OMAP_HSMMC_READ(host->base, | ||
431 | SYSCTL) | SRD); | ||
432 | while (OMAP_HSMMC_READ(host->base, | ||
433 | SYSCTL) & SRD) | ||
434 | ; | ||
435 | end_trans = 1; | ||
436 | } | ||
437 | } | ||
438 | if (status & CARD_ERR) { | ||
439 | dev_dbg(mmc_dev(host->mmc), | ||
440 | "Ignoring card err CMD%d\n", host->cmd->opcode); | ||
441 | if (host->cmd) | ||
442 | end_cmd = 1; | ||
443 | if (host->data) | ||
444 | end_trans = 1; | ||
445 | } | ||
446 | } | ||
447 | |||
448 | OMAP_HSMMC_WRITE(host->base, STAT, status); | ||
449 | |||
450 | if (end_cmd || (status & CC)) | ||
451 | mmc_omap_cmd_done(host, host->cmd); | ||
452 | if (end_trans || (status & TC)) | ||
453 | mmc_omap_xfer_done(host, data); | ||
454 | |||
455 | return IRQ_HANDLED; | ||
456 | } | ||
457 | |||
458 | /* | ||
459 | * Switch MMC operating voltage | ||
460 | */ | ||
461 | static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd) | ||
462 | { | ||
463 | u32 reg_val = 0; | ||
464 | int ret; | ||
465 | |||
466 | /* Disable the clocks */ | ||
467 | clk_disable(host->fclk); | ||
468 | clk_disable(host->iclk); | ||
469 | clk_disable(host->dbclk); | ||
470 | |||
471 | /* Turn the power off */ | ||
472 | ret = mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0); | ||
473 | if (ret != 0) | ||
474 | goto err; | ||
475 | |||
476 | /* Turn the power ON with given VDD 1.8 or 3.0v */ | ||
477 | ret = mmc_slot(host).set_power(host->dev, host->slot_id, 1, vdd); | ||
478 | if (ret != 0) | ||
479 | goto err; | ||
480 | |||
481 | clk_enable(host->fclk); | ||
482 | clk_enable(host->iclk); | ||
483 | clk_enable(host->dbclk); | ||
484 | |||
485 | OMAP_HSMMC_WRITE(host->base, HCTL, | ||
486 | OMAP_HSMMC_READ(host->base, HCTL) & SDVSCLR); | ||
487 | reg_val = OMAP_HSMMC_READ(host->base, HCTL); | ||
488 | /* | ||
489 | * If a MMC dual voltage card is detected, the set_ios fn calls | ||
490 | * this fn with VDD bit set for 1.8V. Upon card removal from the | ||
491 | * slot, omap_mmc_set_ios sets the VDD back to 3V on MMC_POWER_OFF. | ||
492 | * | ||
493 | * Only MMC1 supports 3.0V. MMC2 will not function if SDVS30 is | ||
494 | * set in HCTL. | ||
495 | */ | ||
496 | if (host->id == OMAP_MMC1_DEVID && (((1 << vdd) == MMC_VDD_32_33) || | ||
497 | ((1 << vdd) == MMC_VDD_33_34))) | ||
498 | reg_val |= SDVS30; | ||
499 | if ((1 << vdd) == MMC_VDD_165_195) | ||
500 | reg_val |= SDVS18; | ||
501 | |||
502 | OMAP_HSMMC_WRITE(host->base, HCTL, reg_val); | ||
503 | |||
504 | OMAP_HSMMC_WRITE(host->base, HCTL, | ||
505 | OMAP_HSMMC_READ(host->base, HCTL) | SDBP); | ||
506 | |||
507 | return 0; | ||
508 | err: | ||
509 | dev_dbg(mmc_dev(host->mmc), "Unable to switch operating voltage\n"); | ||
510 | return ret; | ||
511 | } | ||
512 | |||
513 | /* | ||
514 | * Work Item to notify the core about card insertion/removal | ||
515 | */ | ||
516 | static void mmc_omap_detect(struct work_struct *work) | ||
517 | { | ||
518 | struct mmc_omap_host *host = container_of(work, struct mmc_omap_host, | ||
519 | mmc_carddetect_work); | ||
520 | |||
521 | sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch"); | ||
522 | if (host->carddetect) { | ||
523 | mmc_detect_change(host->mmc, (HZ * 200) / 1000); | ||
524 | } else { | ||
525 | OMAP_HSMMC_WRITE(host->base, SYSCTL, | ||
526 | OMAP_HSMMC_READ(host->base, SYSCTL) | SRD); | ||
527 | while (OMAP_HSMMC_READ(host->base, SYSCTL) & SRD) | ||
528 | ; | ||
529 | |||
530 | mmc_detect_change(host->mmc, (HZ * 50) / 1000); | ||
531 | } | ||
532 | } | ||
533 | |||
534 | /* | ||
535 | * ISR for handling card insertion and removal | ||
536 | */ | ||
537 | static irqreturn_t omap_mmc_cd_handler(int irq, void *dev_id) | ||
538 | { | ||
539 | struct mmc_omap_host *host = (struct mmc_omap_host *)dev_id; | ||
540 | |||
541 | host->carddetect = mmc_slot(host).card_detect(irq); | ||
542 | schedule_work(&host->mmc_carddetect_work); | ||
543 | |||
544 | return IRQ_HANDLED; | ||
545 | } | ||
546 | |||
547 | /* | ||
548 | * DMA call back function | ||
549 | */ | ||
550 | static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data) | ||
551 | { | ||
552 | struct mmc_omap_host *host = data; | ||
553 | |||
554 | if (ch_status & OMAP2_DMA_MISALIGNED_ERR_IRQ) | ||
555 | dev_dbg(mmc_dev(host->mmc), "MISALIGNED_ADRS_ERR\n"); | ||
556 | |||
557 | if (host->dma_ch < 0) | ||
558 | return; | ||
559 | |||
560 | omap_free_dma(host->dma_ch); | ||
561 | host->dma_ch = -1; | ||
562 | /* | ||
563 | * DMA Callback: run in interrupt context. | ||
564 | * mutex_unlock will through a kernel warning if used. | ||
565 | */ | ||
566 | up(&host->sem); | ||
567 | } | ||
568 | |||
569 | /* | ||
570 | * Configure dma src and destination parameters | ||
571 | */ | ||
572 | static int mmc_omap_config_dma_param(int sync_dir, struct mmc_omap_host *host, | ||
573 | struct mmc_data *data) | ||
574 | { | ||
575 | if (sync_dir == 0) { | ||
576 | omap_set_dma_dest_params(host->dma_ch, 0, | ||
577 | OMAP_DMA_AMODE_CONSTANT, | ||
578 | (host->mapbase + OMAP_HSMMC_DATA), 0, 0); | ||
579 | omap_set_dma_src_params(host->dma_ch, 0, | ||
580 | OMAP_DMA_AMODE_POST_INC, | ||
581 | sg_dma_address(&data->sg[0]), 0, 0); | ||
582 | } else { | ||
583 | omap_set_dma_src_params(host->dma_ch, 0, | ||
584 | OMAP_DMA_AMODE_CONSTANT, | ||
585 | (host->mapbase + OMAP_HSMMC_DATA), 0, 0); | ||
586 | omap_set_dma_dest_params(host->dma_ch, 0, | ||
587 | OMAP_DMA_AMODE_POST_INC, | ||
588 | sg_dma_address(&data->sg[0]), 0, 0); | ||
589 | } | ||
590 | return 0; | ||
591 | } | ||
592 | /* | ||
593 | * Routine to configure and start DMA for the MMC card | ||
594 | */ | ||
595 | static int | ||
596 | mmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req) | ||
597 | { | ||
598 | int sync_dev, sync_dir = 0; | ||
599 | int dma_ch = 0, ret = 0, err = 1; | ||
600 | struct mmc_data *data = req->data; | ||
601 | |||
602 | /* | ||
603 | * If for some reason the DMA transfer is still active, | ||
604 | * we wait for timeout period and free the dma | ||
605 | */ | ||
606 | if (host->dma_ch != -1) { | ||
607 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
608 | schedule_timeout(100); | ||
609 | if (down_trylock(&host->sem)) { | ||
610 | omap_free_dma(host->dma_ch); | ||
611 | host->dma_ch = -1; | ||
612 | up(&host->sem); | ||
613 | return err; | ||
614 | } | ||
615 | } else { | ||
616 | if (down_trylock(&host->sem)) | ||
617 | return err; | ||
618 | } | ||
619 | |||
620 | if (!(data->flags & MMC_DATA_WRITE)) { | ||
621 | host->dma_dir = DMA_FROM_DEVICE; | ||
622 | if (host->id == OMAP_MMC1_DEVID) | ||
623 | sync_dev = OMAP24XX_DMA_MMC1_RX; | ||
624 | else | ||
625 | sync_dev = OMAP24XX_DMA_MMC2_RX; | ||
626 | } else { | ||
627 | host->dma_dir = DMA_TO_DEVICE; | ||
628 | if (host->id == OMAP_MMC1_DEVID) | ||
629 | sync_dev = OMAP24XX_DMA_MMC1_TX; | ||
630 | else | ||
631 | sync_dev = OMAP24XX_DMA_MMC2_TX; | ||
632 | } | ||
633 | |||
634 | ret = omap_request_dma(sync_dev, "MMC/SD", mmc_omap_dma_cb, | ||
635 | host, &dma_ch); | ||
636 | if (ret != 0) { | ||
637 | dev_dbg(mmc_dev(host->mmc), | ||
638 | "%s: omap_request_dma() failed with %d\n", | ||
639 | mmc_hostname(host->mmc), ret); | ||
640 | return ret; | ||
641 | } | ||
642 | |||
643 | host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, | ||
644 | data->sg_len, host->dma_dir); | ||
645 | host->dma_ch = dma_ch; | ||
646 | |||
647 | if (!(data->flags & MMC_DATA_WRITE)) | ||
648 | mmc_omap_config_dma_param(1, host, data); | ||
649 | else | ||
650 | mmc_omap_config_dma_param(0, host, data); | ||
651 | |||
652 | if ((data->blksz % 4) == 0) | ||
653 | omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32, | ||
654 | (data->blksz / 4), data->blocks, OMAP_DMA_SYNC_FRAME, | ||
655 | sync_dev, sync_dir); | ||
656 | else | ||
657 | /* REVISIT: The MMC buffer increments only when MSB is written. | ||
658 | * Return error for blksz which is non multiple of four. | ||
659 | */ | ||
660 | return -EINVAL; | ||
661 | |||
662 | omap_start_dma(dma_ch); | ||
663 | return 0; | ||
664 | } | ||
665 | |||
666 | static void set_data_timeout(struct mmc_omap_host *host, | ||
667 | struct mmc_request *req) | ||
668 | { | ||
669 | unsigned int timeout, cycle_ns; | ||
670 | uint32_t reg, clkd, dto = 0; | ||
671 | |||
672 | reg = OMAP_HSMMC_READ(host->base, SYSCTL); | ||
673 | clkd = (reg & CLKD_MASK) >> CLKD_SHIFT; | ||
674 | if (clkd == 0) | ||
675 | clkd = 1; | ||
676 | |||
677 | cycle_ns = 1000000000 / (clk_get_rate(host->fclk) / clkd); | ||
678 | timeout = req->data->timeout_ns / cycle_ns; | ||
679 | timeout += req->data->timeout_clks; | ||
680 | if (timeout) { | ||
681 | while ((timeout & 0x80000000) == 0) { | ||
682 | dto += 1; | ||
683 | timeout <<= 1; | ||
684 | } | ||
685 | dto = 31 - dto; | ||
686 | timeout <<= 1; | ||
687 | if (timeout && dto) | ||
688 | dto += 1; | ||
689 | if (dto >= 13) | ||
690 | dto -= 13; | ||
691 | else | ||
692 | dto = 0; | ||
693 | if (dto > 14) | ||
694 | dto = 14; | ||
695 | } | ||
696 | |||
697 | reg &= ~DTO_MASK; | ||
698 | reg |= dto << DTO_SHIFT; | ||
699 | OMAP_HSMMC_WRITE(host->base, SYSCTL, reg); | ||
700 | } | ||
701 | |||
702 | /* | ||
703 | * Configure block length for MMC/SD cards and initiate the transfer. | ||
704 | */ | ||
705 | static int | ||
706 | mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req) | ||
707 | { | ||
708 | int ret; | ||
709 | host->data = req->data; | ||
710 | |||
711 | if (req->data == NULL) { | ||
712 | host->datadir = OMAP_MMC_DATADIR_NONE; | ||
713 | OMAP_HSMMC_WRITE(host->base, BLK, 0); | ||
714 | return 0; | ||
715 | } | ||
716 | |||
717 | OMAP_HSMMC_WRITE(host->base, BLK, (req->data->blksz) | ||
718 | | (req->data->blocks << 16)); | ||
719 | set_data_timeout(host, req); | ||
720 | |||
721 | host->datadir = (req->data->flags & MMC_DATA_WRITE) ? | ||
722 | OMAP_MMC_DATADIR_WRITE : OMAP_MMC_DATADIR_READ; | ||
723 | |||
724 | if (host->use_dma) { | ||
725 | ret = mmc_omap_start_dma_transfer(host, req); | ||
726 | if (ret != 0) { | ||
727 | dev_dbg(mmc_dev(host->mmc), "MMC start dma failure\n"); | ||
728 | return ret; | ||
729 | } | ||
730 | } | ||
731 | return 0; | ||
732 | } | ||
733 | |||
734 | /* | ||
735 | * Request function. for read/write operation | ||
736 | */ | ||
737 | static void omap_mmc_request(struct mmc_host *mmc, struct mmc_request *req) | ||
738 | { | ||
739 | struct mmc_omap_host *host = mmc_priv(mmc); | ||
740 | |||
741 | WARN_ON(host->mrq != NULL); | ||
742 | host->mrq = req; | ||
743 | mmc_omap_prepare_data(host, req); | ||
744 | mmc_omap_start_command(host, req->cmd, req->data); | ||
745 | } | ||
746 | |||
747 | |||
748 | /* Routine to configure clock values. Exposed API to core */ | ||
749 | static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | ||
750 | { | ||
751 | struct mmc_omap_host *host = mmc_priv(mmc); | ||
752 | u16 dsor = 0; | ||
753 | unsigned long regval; | ||
754 | unsigned long timeout; | ||
755 | |||
756 | switch (ios->power_mode) { | ||
757 | case MMC_POWER_OFF: | ||
758 | mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0); | ||
759 | /* | ||
760 | * Reset bus voltage to 3V if it got set to 1.8V earlier. | ||
761 | * REVISIT: If we are able to detect cards after unplugging | ||
762 | * a 1.8V card, this code should not be needed. | ||
763 | */ | ||
764 | if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) { | ||
765 | int vdd = fls(host->mmc->ocr_avail) - 1; | ||
766 | if (omap_mmc_switch_opcond(host, vdd) != 0) | ||
767 | host->mmc->ios.vdd = vdd; | ||
768 | } | ||
769 | break; | ||
770 | case MMC_POWER_UP: | ||
771 | mmc_slot(host).set_power(host->dev, host->slot_id, 1, ios->vdd); | ||
772 | break; | ||
773 | } | ||
774 | |||
775 | switch (mmc->ios.bus_width) { | ||
776 | case MMC_BUS_WIDTH_4: | ||
777 | OMAP_HSMMC_WRITE(host->base, HCTL, | ||
778 | OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT); | ||
779 | break; | ||
780 | case MMC_BUS_WIDTH_1: | ||
781 | OMAP_HSMMC_WRITE(host->base, HCTL, | ||
782 | OMAP_HSMMC_READ(host->base, HCTL) & ~FOUR_BIT); | ||
783 | break; | ||
784 | } | ||
785 | |||
786 | if (host->id == OMAP_MMC1_DEVID) { | ||
787 | /* Only MMC1 can operate at 3V/1.8V */ | ||
788 | if ((OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET) && | ||
789 | (ios->vdd == DUAL_VOLT_OCR_BIT)) { | ||
790 | /* | ||
791 | * The mmc_select_voltage fn of the core does | ||
792 | * not seem to set the power_mode to | ||
793 | * MMC_POWER_UP upon recalculating the voltage. | ||
794 | * vdd 1.8v. | ||
795 | */ | ||
796 | if (omap_mmc_switch_opcond(host, ios->vdd) != 0) | ||
797 | dev_dbg(mmc_dev(host->mmc), | ||
798 | "Switch operation failed\n"); | ||
799 | } | ||
800 | } | ||
801 | |||
802 | if (ios->clock) { | ||
803 | dsor = OMAP_MMC_MASTER_CLOCK / ios->clock; | ||
804 | if (dsor < 1) | ||
805 | dsor = 1; | ||
806 | |||
807 | if (OMAP_MMC_MASTER_CLOCK / dsor > ios->clock) | ||
808 | dsor++; | ||
809 | |||
810 | if (dsor > 250) | ||
811 | dsor = 250; | ||
812 | } | ||
813 | omap_mmc_stop_clock(host); | ||
814 | regval = OMAP_HSMMC_READ(host->base, SYSCTL); | ||
815 | regval = regval & ~(CLKD_MASK); | ||
816 | regval = regval | (dsor << 6) | (DTO << 16); | ||
817 | OMAP_HSMMC_WRITE(host->base, SYSCTL, regval); | ||
818 | OMAP_HSMMC_WRITE(host->base, SYSCTL, | ||
819 | OMAP_HSMMC_READ(host->base, SYSCTL) | ICE); | ||
820 | |||
821 | /* Wait till the ICS bit is set */ | ||
822 | timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS); | ||
823 | while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != 0x2 | ||
824 | && time_before(jiffies, timeout)) | ||
825 | msleep(1); | ||
826 | |||
827 | OMAP_HSMMC_WRITE(host->base, SYSCTL, | ||
828 | OMAP_HSMMC_READ(host->base, SYSCTL) | CEN); | ||
829 | |||
830 | if (ios->power_mode == MMC_POWER_ON) | ||
831 | send_init_stream(host); | ||
832 | |||
833 | if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) | ||
834 | OMAP_HSMMC_WRITE(host->base, CON, | ||
835 | OMAP_HSMMC_READ(host->base, CON) | OD); | ||
836 | } | ||
837 | |||
838 | static int omap_hsmmc_get_cd(struct mmc_host *mmc) | ||
839 | { | ||
840 | struct mmc_omap_host *host = mmc_priv(mmc); | ||
841 | struct omap_mmc_platform_data *pdata = host->pdata; | ||
842 | |||
843 | if (!pdata->slots[0].card_detect) | ||
844 | return -ENOSYS; | ||
845 | return pdata->slots[0].card_detect(pdata->slots[0].card_detect_irq); | ||
846 | } | ||
847 | |||
848 | static int omap_hsmmc_get_ro(struct mmc_host *mmc) | ||
849 | { | ||
850 | struct mmc_omap_host *host = mmc_priv(mmc); | ||
851 | struct omap_mmc_platform_data *pdata = host->pdata; | ||
852 | |||
853 | if (!pdata->slots[0].get_ro) | ||
854 | return -ENOSYS; | ||
855 | return pdata->slots[0].get_ro(host->dev, 0); | ||
856 | } | ||
857 | |||
858 | static struct mmc_host_ops mmc_omap_ops = { | ||
859 | .request = omap_mmc_request, | ||
860 | .set_ios = omap_mmc_set_ios, | ||
861 | .get_cd = omap_hsmmc_get_cd, | ||
862 | .get_ro = omap_hsmmc_get_ro, | ||
863 | /* NYET -- enable_sdio_irq */ | ||
864 | }; | ||
865 | |||
866 | static int __init omap_mmc_probe(struct platform_device *pdev) | ||
867 | { | ||
868 | struct omap_mmc_platform_data *pdata = pdev->dev.platform_data; | ||
869 | struct mmc_host *mmc; | ||
870 | struct mmc_omap_host *host = NULL; | ||
871 | struct resource *res; | ||
872 | int ret = 0, irq; | ||
873 | u32 hctl, capa; | ||
874 | |||
875 | if (pdata == NULL) { | ||
876 | dev_err(&pdev->dev, "Platform Data is missing\n"); | ||
877 | return -ENXIO; | ||
878 | } | ||
879 | |||
880 | if (pdata->nr_slots == 0) { | ||
881 | dev_err(&pdev->dev, "No Slots\n"); | ||
882 | return -ENXIO; | ||
883 | } | ||
884 | |||
885 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
886 | irq = platform_get_irq(pdev, 0); | ||
887 | if (res == NULL || irq < 0) | ||
888 | return -ENXIO; | ||
889 | |||
890 | res = request_mem_region(res->start, res->end - res->start + 1, | ||
891 | pdev->name); | ||
892 | if (res == NULL) | ||
893 | return -EBUSY; | ||
894 | |||
895 | mmc = mmc_alloc_host(sizeof(struct mmc_omap_host), &pdev->dev); | ||
896 | if (!mmc) { | ||
897 | ret = -ENOMEM; | ||
898 | goto err; | ||
899 | } | ||
900 | |||
901 | host = mmc_priv(mmc); | ||
902 | host->mmc = mmc; | ||
903 | host->pdata = pdata; | ||
904 | host->dev = &pdev->dev; | ||
905 | host->use_dma = 1; | ||
906 | host->dev->dma_mask = &pdata->dma_mask; | ||
907 | host->dma_ch = -1; | ||
908 | host->irq = irq; | ||
909 | host->id = pdev->id; | ||
910 | host->slot_id = 0; | ||
911 | host->mapbase = res->start; | ||
912 | host->base = ioremap(host->mapbase, SZ_4K); | ||
913 | |||
914 | platform_set_drvdata(pdev, host); | ||
915 | INIT_WORK(&host->mmc_carddetect_work, mmc_omap_detect); | ||
916 | |||
917 | mmc->ops = &mmc_omap_ops; | ||
918 | mmc->f_min = 400000; | ||
919 | mmc->f_max = 52000000; | ||
920 | |||
921 | sema_init(&host->sem, 1); | ||
922 | |||
923 | host->iclk = clk_get(&pdev->dev, "mmchs_ick"); | ||
924 | if (IS_ERR(host->iclk)) { | ||
925 | ret = PTR_ERR(host->iclk); | ||
926 | host->iclk = NULL; | ||
927 | goto err1; | ||
928 | } | ||
929 | host->fclk = clk_get(&pdev->dev, "mmchs_fck"); | ||
930 | if (IS_ERR(host->fclk)) { | ||
931 | ret = PTR_ERR(host->fclk); | ||
932 | host->fclk = NULL; | ||
933 | clk_put(host->iclk); | ||
934 | goto err1; | ||
935 | } | ||
936 | |||
937 | if (clk_enable(host->fclk) != 0) { | ||
938 | clk_put(host->iclk); | ||
939 | clk_put(host->fclk); | ||
940 | goto err1; | ||
941 | } | ||
942 | |||
943 | if (clk_enable(host->iclk) != 0) { | ||
944 | clk_disable(host->fclk); | ||
945 | clk_put(host->iclk); | ||
946 | clk_put(host->fclk); | ||
947 | goto err1; | ||
948 | } | ||
949 | |||
950 | host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck"); | ||
951 | /* | ||
952 | * MMC can still work without debounce clock. | ||
953 | */ | ||
954 | if (IS_ERR(host->dbclk)) | ||
955 | dev_warn(mmc_dev(host->mmc), "Failed to get debounce clock\n"); | ||
956 | else | ||
957 | if (clk_enable(host->dbclk) != 0) | ||
958 | dev_dbg(mmc_dev(host->mmc), "Enabling debounce" | ||
959 | " clk failed\n"); | ||
960 | else | ||
961 | host->dbclk_enabled = 1; | ||
962 | |||
963 | #ifdef CONFIG_MMC_BLOCK_BOUNCE | ||
964 | mmc->max_phys_segs = 1; | ||
965 | mmc->max_hw_segs = 1; | ||
966 | #endif | ||
967 | mmc->max_blk_size = 512; /* Block Length at max can be 1024 */ | ||
968 | mmc->max_blk_count = 0xFFFF; /* No. of Blocks is 16 bits */ | ||
969 | mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; | ||
970 | mmc->max_seg_size = mmc->max_req_size; | ||
971 | |||
972 | mmc->ocr_avail = mmc_slot(host).ocr_mask; | ||
973 | mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED; | ||
974 | |||
975 | if (pdata->slots[host->slot_id].wires >= 4) | ||
976 | mmc->caps |= MMC_CAP_4_BIT_DATA; | ||
977 | |||
978 | /* Only MMC1 supports 3.0V */ | ||
979 | if (host->id == OMAP_MMC1_DEVID) { | ||
980 | hctl = SDVS30; | ||
981 | capa = VS30 | VS18; | ||
982 | } else { | ||
983 | hctl = SDVS18; | ||
984 | capa = VS18; | ||
985 | } | ||
986 | |||
987 | OMAP_HSMMC_WRITE(host->base, HCTL, | ||
988 | OMAP_HSMMC_READ(host->base, HCTL) | hctl); | ||
989 | |||
990 | OMAP_HSMMC_WRITE(host->base, CAPA, | ||
991 | OMAP_HSMMC_READ(host->base, CAPA) | capa); | ||
992 | |||
993 | /* Set the controller to AUTO IDLE mode */ | ||
994 | OMAP_HSMMC_WRITE(host->base, SYSCONFIG, | ||
995 | OMAP_HSMMC_READ(host->base, SYSCONFIG) | AUTOIDLE); | ||
996 | |||
997 | /* Set SD bus power bit */ | ||
998 | OMAP_HSMMC_WRITE(host->base, HCTL, | ||
999 | OMAP_HSMMC_READ(host->base, HCTL) | SDBP); | ||
1000 | |||
1001 | /* Request IRQ for MMC operations */ | ||
1002 | ret = request_irq(host->irq, mmc_omap_irq, IRQF_DISABLED, | ||
1003 | mmc_hostname(mmc), host); | ||
1004 | if (ret) { | ||
1005 | dev_dbg(mmc_dev(host->mmc), "Unable to grab HSMMC IRQ\n"); | ||
1006 | goto err_irq; | ||
1007 | } | ||
1008 | |||
1009 | if (pdata->init != NULL) { | ||
1010 | if (pdata->init(&pdev->dev) != 0) { | ||
1011 | dev_dbg(mmc_dev(host->mmc), | ||
1012 | "Unable to configure MMC IRQs\n"); | ||
1013 | goto err_irq_cd_init; | ||
1014 | } | ||
1015 | } | ||
1016 | |||
1017 | /* Request IRQ for card detect */ | ||
1018 | if ((mmc_slot(host).card_detect_irq) && (mmc_slot(host).card_detect)) { | ||
1019 | ret = request_irq(mmc_slot(host).card_detect_irq, | ||
1020 | omap_mmc_cd_handler, | ||
1021 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | ||
1022 | | IRQF_DISABLED, | ||
1023 | mmc_hostname(mmc), host); | ||
1024 | if (ret) { | ||
1025 | dev_dbg(mmc_dev(host->mmc), | ||
1026 | "Unable to grab MMC CD IRQ\n"); | ||
1027 | goto err_irq_cd; | ||
1028 | } | ||
1029 | } | ||
1030 | |||
1031 | OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK); | ||
1032 | OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK); | ||
1033 | |||
1034 | mmc_add_host(mmc); | ||
1035 | |||
1036 | if (host->pdata->slots[host->slot_id].name != NULL) { | ||
1037 | ret = device_create_file(&mmc->class_dev, &dev_attr_slot_name); | ||
1038 | if (ret < 0) | ||
1039 | goto err_slot_name; | ||
1040 | } | ||
1041 | if (mmc_slot(host).card_detect_irq && mmc_slot(host).card_detect && | ||
1042 | host->pdata->slots[host->slot_id].get_cover_state) { | ||
1043 | ret = device_create_file(&mmc->class_dev, | ||
1044 | &dev_attr_cover_switch); | ||
1045 | if (ret < 0) | ||
1046 | goto err_cover_switch; | ||
1047 | } | ||
1048 | |||
1049 | return 0; | ||
1050 | |||
1051 | err_cover_switch: | ||
1052 | device_remove_file(&mmc->class_dev, &dev_attr_cover_switch); | ||
1053 | err_slot_name: | ||
1054 | mmc_remove_host(mmc); | ||
1055 | err_irq_cd: | ||
1056 | free_irq(mmc_slot(host).card_detect_irq, host); | ||
1057 | err_irq_cd_init: | ||
1058 | free_irq(host->irq, host); | ||
1059 | err_irq: | ||
1060 | clk_disable(host->fclk); | ||
1061 | clk_disable(host->iclk); | ||
1062 | clk_put(host->fclk); | ||
1063 | clk_put(host->iclk); | ||
1064 | if (host->dbclk_enabled) { | ||
1065 | clk_disable(host->dbclk); | ||
1066 | clk_put(host->dbclk); | ||
1067 | } | ||
1068 | |||
1069 | err1: | ||
1070 | iounmap(host->base); | ||
1071 | err: | ||
1072 | dev_dbg(mmc_dev(host->mmc), "Probe Failed\n"); | ||
1073 | release_mem_region(res->start, res->end - res->start + 1); | ||
1074 | if (host) | ||
1075 | mmc_free_host(mmc); | ||
1076 | return ret; | ||
1077 | } | ||
1078 | |||
1079 | static int omap_mmc_remove(struct platform_device *pdev) | ||
1080 | { | ||
1081 | struct mmc_omap_host *host = platform_get_drvdata(pdev); | ||
1082 | struct resource *res; | ||
1083 | |||
1084 | if (host) { | ||
1085 | mmc_remove_host(host->mmc); | ||
1086 | if (host->pdata->cleanup) | ||
1087 | host->pdata->cleanup(&pdev->dev); | ||
1088 | free_irq(host->irq, host); | ||
1089 | if (mmc_slot(host).card_detect_irq) | ||
1090 | free_irq(mmc_slot(host).card_detect_irq, host); | ||
1091 | flush_scheduled_work(); | ||
1092 | |||
1093 | clk_disable(host->fclk); | ||
1094 | clk_disable(host->iclk); | ||
1095 | clk_put(host->fclk); | ||
1096 | clk_put(host->iclk); | ||
1097 | if (host->dbclk_enabled) { | ||
1098 | clk_disable(host->dbclk); | ||
1099 | clk_put(host->dbclk); | ||
1100 | } | ||
1101 | |||
1102 | mmc_free_host(host->mmc); | ||
1103 | iounmap(host->base); | ||
1104 | } | ||
1105 | |||
1106 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
1107 | if (res) | ||
1108 | release_mem_region(res->start, res->end - res->start + 1); | ||
1109 | platform_set_drvdata(pdev, NULL); | ||
1110 | |||
1111 | return 0; | ||
1112 | } | ||
1113 | |||
1114 | #ifdef CONFIG_PM | ||
1115 | static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state) | ||
1116 | { | ||
1117 | int ret = 0; | ||
1118 | struct mmc_omap_host *host = platform_get_drvdata(pdev); | ||
1119 | |||
1120 | if (host && host->suspended) | ||
1121 | return 0; | ||
1122 | |||
1123 | if (host) { | ||
1124 | ret = mmc_suspend_host(host->mmc, state); | ||
1125 | if (ret == 0) { | ||
1126 | host->suspended = 1; | ||
1127 | |||
1128 | OMAP_HSMMC_WRITE(host->base, ISE, 0); | ||
1129 | OMAP_HSMMC_WRITE(host->base, IE, 0); | ||
1130 | |||
1131 | if (host->pdata->suspend) { | ||
1132 | ret = host->pdata->suspend(&pdev->dev, | ||
1133 | host->slot_id); | ||
1134 | if (ret) | ||
1135 | dev_dbg(mmc_dev(host->mmc), | ||
1136 | "Unable to handle MMC board" | ||
1137 | " level suspend\n"); | ||
1138 | } | ||
1139 | |||
1140 | if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) { | ||
1141 | OMAP_HSMMC_WRITE(host->base, HCTL, | ||
1142 | OMAP_HSMMC_READ(host->base, HCTL) | ||
1143 | & SDVSCLR); | ||
1144 | OMAP_HSMMC_WRITE(host->base, HCTL, | ||
1145 | OMAP_HSMMC_READ(host->base, HCTL) | ||
1146 | | SDVS30); | ||
1147 | OMAP_HSMMC_WRITE(host->base, HCTL, | ||
1148 | OMAP_HSMMC_READ(host->base, HCTL) | ||
1149 | | SDBP); | ||
1150 | } | ||
1151 | |||
1152 | clk_disable(host->fclk); | ||
1153 | clk_disable(host->iclk); | ||
1154 | clk_disable(host->dbclk); | ||
1155 | } | ||
1156 | |||
1157 | } | ||
1158 | return ret; | ||
1159 | } | ||
1160 | |||
1161 | /* Routine to resume the MMC device */ | ||
1162 | static int omap_mmc_resume(struct platform_device *pdev) | ||
1163 | { | ||
1164 | int ret = 0; | ||
1165 | struct mmc_omap_host *host = platform_get_drvdata(pdev); | ||
1166 | |||
1167 | if (host && !host->suspended) | ||
1168 | return 0; | ||
1169 | |||
1170 | if (host) { | ||
1171 | |||
1172 | ret = clk_enable(host->fclk); | ||
1173 | if (ret) | ||
1174 | goto clk_en_err; | ||
1175 | |||
1176 | ret = clk_enable(host->iclk); | ||
1177 | if (ret) { | ||
1178 | clk_disable(host->fclk); | ||
1179 | clk_put(host->fclk); | ||
1180 | goto clk_en_err; | ||
1181 | } | ||
1182 | |||
1183 | if (clk_enable(host->dbclk) != 0) | ||
1184 | dev_dbg(mmc_dev(host->mmc), | ||
1185 | "Enabling debounce clk failed\n"); | ||
1186 | |||
1187 | if (host->pdata->resume) { | ||
1188 | ret = host->pdata->resume(&pdev->dev, host->slot_id); | ||
1189 | if (ret) | ||
1190 | dev_dbg(mmc_dev(host->mmc), | ||
1191 | "Unmask interrupt failed\n"); | ||
1192 | } | ||
1193 | |||
1194 | /* Notify the core to resume the host */ | ||
1195 | ret = mmc_resume_host(host->mmc); | ||
1196 | if (ret == 0) | ||
1197 | host->suspended = 0; | ||
1198 | } | ||
1199 | |||
1200 | return ret; | ||
1201 | |||
1202 | clk_en_err: | ||
1203 | dev_dbg(mmc_dev(host->mmc), | ||
1204 | "Failed to enable MMC clocks during resume\n"); | ||
1205 | return ret; | ||
1206 | } | ||
1207 | |||
1208 | #else | ||
1209 | #define omap_mmc_suspend NULL | ||
1210 | #define omap_mmc_resume NULL | ||
1211 | #endif | ||
1212 | |||
1213 | static struct platform_driver omap_mmc_driver = { | ||
1214 | .probe = omap_mmc_probe, | ||
1215 | .remove = omap_mmc_remove, | ||
1216 | .suspend = omap_mmc_suspend, | ||
1217 | .resume = omap_mmc_resume, | ||
1218 | .driver = { | ||
1219 | .name = DRIVER_NAME, | ||
1220 | .owner = THIS_MODULE, | ||
1221 | }, | ||
1222 | }; | ||
1223 | |||
1224 | static int __init omap_mmc_init(void) | ||
1225 | { | ||
1226 | /* Register the MMC driver */ | ||
1227 | return platform_driver_register(&omap_mmc_driver); | ||
1228 | } | ||
1229 | |||
1230 | static void __exit omap_mmc_cleanup(void) | ||
1231 | { | ||
1232 | /* Unregister MMC driver */ | ||
1233 | platform_driver_unregister(&omap_mmc_driver); | ||
1234 | } | ||
1235 | |||
1236 | module_init(omap_mmc_init); | ||
1237 | module_exit(omap_mmc_cleanup); | ||
1238 | |||
1239 | MODULE_DESCRIPTION("OMAP High Speed Multimedia Card driver"); | ||
1240 | MODULE_LICENSE("GPL"); | ||
1241 | MODULE_ALIAS("platform:" DRIVER_NAME); | ||
1242 | MODULE_AUTHOR("Texas Instruments Inc"); | ||