blob: cf16e44c030198c05b1d4f0e9766d1b99ac1b1bf [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * linux/drivers/mmc/wbsd.c - Winbond W83L51xD SD/MMC driver
3 *
4 * Copyright (C) 2004-2005 Pierre Ossman, All Rights Reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify
Pierre Ossman643f7202006-09-30 23:27:52 -07007 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or (at
9 * your option) any later version.
Linus Torvalds1da177e2005-04-16 15:20:36 -070010 *
11 *
12 * Warning!
13 *
14 * Changes to the FIFO system should be done with extreme care since
15 * the hardware is full of bugs related to the FIFO. Known issues are:
16 *
17 * - FIFO size field in FSR is always zero.
18 *
19 * - FIFO interrupts tend not to work as they should. Interrupts are
20 * triggered only for full/empty events, not for threshold values.
21 *
22 * - On APIC systems the FIFO empty interrupt is sometimes lost.
23 */
24
Linus Torvalds1da177e2005-04-16 15:20:36 -070025#include <linux/module.h>
26#include <linux/moduleparam.h>
27#include <linux/init.h>
28#include <linux/ioport.h>
Russell Kingd052d1b2005-10-29 19:07:23 +010029#include <linux/platform_device.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <linux/interrupt.h>
Pierre Ossman85bcc132005-05-08 19:35:27 +010031#include <linux/dma-mapping.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070032#include <linux/delay.h>
Pierre Ossman85bcc132005-05-08 19:35:27 +010033#include <linux/pnp.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070034#include <linux/highmem.h>
35#include <linux/mmc/host.h>
36#include <linux/mmc/protocol.h>
37
38#include <asm/io.h>
39#include <asm/dma.h>
40#include <asm/scatterlist.h>
41
42#include "wbsd.h"
43
44#define DRIVER_NAME "wbsd"
Pierre Ossman916f3ac2006-08-06 22:22:23 +020045#define DRIVER_VERSION "1.6"
Linus Torvalds1da177e2005-04-16 15:20:36 -070046
Linus Torvalds1da177e2005-04-16 15:20:36 -070047#define DBG(x...) \
Russell Kingc6563172006-03-29 09:30:20 +010048 pr_debug(DRIVER_NAME ": " x)
Linus Torvalds1da177e2005-04-16 15:20:36 -070049#define DBGF(f, x...) \
Russell Kingc6563172006-03-29 09:30:20 +010050 pr_debug(DRIVER_NAME " [%s()]: " f, __func__ , ##x)
Linus Torvalds1da177e2005-04-16 15:20:36 -070051
Linus Torvalds1da177e2005-04-16 15:20:36 -070052/*
Pierre Ossman85bcc132005-05-08 19:35:27 +010053 * Device resources
54 */
55
56#ifdef CONFIG_PNP
57
58static const struct pnp_device_id pnp_dev_table[] = {
59 { "WEC0517", 0 },
60 { "WEC0518", 0 },
61 { "", 0 },
62};
63
64MODULE_DEVICE_TABLE(pnp, pnp_dev_table);
65
66#endif /* CONFIG_PNP */
67
Adrian Bunk3eee0d02005-07-01 13:07:37 +010068static const int config_ports[] = { 0x2E, 0x4E };
69static const int unlock_codes[] = { 0x83, 0x87 };
70
71static const int valid_ids[] = {
72 0x7112,
73 };
74
Pierre Ossman85bcc132005-05-08 19:35:27 +010075#ifdef CONFIG_PNP
76static unsigned int nopnp = 0;
77#else
78static const unsigned int nopnp = 1;
79#endif
80static unsigned int io = 0x248;
81static unsigned int irq = 6;
82static int dma = 2;
83
84/*
Linus Torvalds1da177e2005-04-16 15:20:36 -070085 * Basic functions
86 */
87
Pierre Ossmancfa7f522006-01-08 18:17:55 +000088static inline void wbsd_unlock_config(struct wbsd_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -070089{
Pierre Ossman85bcc132005-05-08 19:35:27 +010090 BUG_ON(host->config == 0);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +010091
Linus Torvalds1da177e2005-04-16 15:20:36 -070092 outb(host->unlock_code, host->config);
93 outb(host->unlock_code, host->config);
94}
95
Pierre Ossmancfa7f522006-01-08 18:17:55 +000096static inline void wbsd_lock_config(struct wbsd_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -070097{
Pierre Ossman85bcc132005-05-08 19:35:27 +010098 BUG_ON(host->config == 0);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +010099
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100 outb(LOCK_CODE, host->config);
101}
102
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000103static inline void wbsd_write_config(struct wbsd_host *host, u8 reg, u8 value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104{
Pierre Ossman85bcc132005-05-08 19:35:27 +0100105 BUG_ON(host->config == 0);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100106
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107 outb(reg, host->config);
108 outb(value, host->config + 1);
109}
110
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000111static inline u8 wbsd_read_config(struct wbsd_host *host, u8 reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112{
Pierre Ossman85bcc132005-05-08 19:35:27 +0100113 BUG_ON(host->config == 0);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100114
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115 outb(reg, host->config);
116 return inb(host->config + 1);
117}
118
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000119static inline void wbsd_write_index(struct wbsd_host *host, u8 index, u8 value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120{
121 outb(index, host->base + WBSD_IDXR);
122 outb(value, host->base + WBSD_DATAR);
123}
124
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000125static inline u8 wbsd_read_index(struct wbsd_host *host, u8 index)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126{
127 outb(index, host->base + WBSD_IDXR);
128 return inb(host->base + WBSD_DATAR);
129}
130
131/*
132 * Common routines
133 */
134
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000135static void wbsd_init_device(struct wbsd_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136{
137 u8 setup, ier;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100138
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 /*
140 * Reset chip (SD/MMC part) and fifo.
141 */
142 setup = wbsd_read_index(host, WBSD_IDX_SETUP);
143 setup |= WBSD_FIFO_RESET | WBSD_SOFT_RESET;
144 wbsd_write_index(host, WBSD_IDX_SETUP, setup);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100145
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 /*
Pierre Ossman85bcc132005-05-08 19:35:27 +0100147 * Set DAT3 to input
148 */
149 setup &= ~WBSD_DAT3_H;
150 wbsd_write_index(host, WBSD_IDX_SETUP, setup);
151 host->flags &= ~WBSD_FIGNORE_DETECT;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100152
Pierre Ossman85bcc132005-05-08 19:35:27 +0100153 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 * Read back default clock.
155 */
156 host->clk = wbsd_read_index(host, WBSD_IDX_CLK);
157
158 /*
159 * Power down port.
160 */
161 outb(WBSD_POWER_N, host->base + WBSD_CSR);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100162
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163 /*
164 * Set maximum timeout.
165 */
166 wbsd_write_index(host, WBSD_IDX_TAAC, 0x7F);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100167
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 /*
Pierre Ossman85bcc132005-05-08 19:35:27 +0100169 * Test for card presence
170 */
171 if (inb(host->base + WBSD_CSR) & WBSD_CARDPRESENT)
172 host->flags |= WBSD_FCARD_PRESENT;
173 else
174 host->flags &= ~WBSD_FCARD_PRESENT;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100175
Pierre Ossman85bcc132005-05-08 19:35:27 +0100176 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 * Enable interesting interrupts.
178 */
179 ier = 0;
180 ier |= WBSD_EINT_CARD;
181 ier |= WBSD_EINT_FIFO_THRE;
182 ier |= WBSD_EINT_CCRC;
183 ier |= WBSD_EINT_TIMEOUT;
184 ier |= WBSD_EINT_CRC;
185 ier |= WBSD_EINT_TC;
186
187 outb(ier, host->base + WBSD_EIR);
188
189 /*
190 * Clear interrupts.
191 */
192 inb(host->base + WBSD_ISR);
193}
194
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000195static void wbsd_reset(struct wbsd_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196{
197 u8 setup;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100198
Pierre Ossmand1916342005-11-05 10:36:35 +0000199 printk(KERN_ERR "%s: Resetting chip\n", mmc_hostname(host->mmc));
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100200
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 /*
202 * Soft reset of chip (SD/MMC part).
203 */
204 setup = wbsd_read_index(host, WBSD_IDX_SETUP);
205 setup |= WBSD_SOFT_RESET;
206 wbsd_write_index(host, WBSD_IDX_SETUP, setup);
207}
208
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000209static void wbsd_request_end(struct wbsd_host *host, struct mmc_request *mrq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210{
211 unsigned long dmaflags;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100212
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 DBGF("Ending request, cmd (%x)\n", mrq->cmd->opcode);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100214
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000215 if (host->dma >= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216 /*
217 * Release ISA DMA controller.
218 */
219 dmaflags = claim_dma_lock();
220 disable_dma(host->dma);
221 clear_dma_ff(host->dma);
222 release_dma_lock(dmaflags);
223
224 /*
225 * Disable DMA on host.
226 */
227 wbsd_write_index(host, WBSD_IDX_DMA, 0);
228 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100229
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 host->mrq = NULL;
231
232 /*
233 * MMC layer might call back into the driver so first unlock.
234 */
235 spin_unlock(&host->lock);
236 mmc_request_done(host->mmc, mrq);
237 spin_lock(&host->lock);
238}
239
240/*
241 * Scatter/gather functions
242 */
243
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000244static inline void wbsd_init_sg(struct wbsd_host *host, struct mmc_data *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245{
246 /*
247 * Get info. about SG list from data structure.
248 */
249 host->cur_sg = data->sg;
250 host->num_sg = data->sg_len;
251
252 host->offset = 0;
253 host->remain = host->cur_sg->length;
254}
255
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000256static inline int wbsd_next_sg(struct wbsd_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257{
258 /*
259 * Skip to next SG entry.
260 */
261 host->cur_sg++;
262 host->num_sg--;
263
264 /*
265 * Any entries left?
266 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000267 if (host->num_sg > 0) {
268 host->offset = 0;
269 host->remain = host->cur_sg->length;
270 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100271
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 return host->num_sg;
273}
274
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000275static inline char *wbsd_kmap_sg(struct wbsd_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276{
277 host->mapped_sg = kmap_atomic(host->cur_sg->page, KM_BIO_SRC_IRQ) +
278 host->cur_sg->offset;
279 return host->mapped_sg;
280}
281
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000282static inline void wbsd_kunmap_sg(struct wbsd_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283{
284 kunmap_atomic(host->mapped_sg, KM_BIO_SRC_IRQ);
285}
286
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000287static inline void wbsd_sg_to_dma(struct wbsd_host *host, struct mmc_data *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288{
289 unsigned int len, i, size;
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000290 struct scatterlist *sg;
291 char *dmabuf = host->dma_buffer;
292 char *sgbuf;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100293
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294 size = host->size;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100295
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 sg = data->sg;
297 len = data->sg_len;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100298
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299 /*
300 * Just loop through all entries. Size might not
301 * be the entire list though so make sure that
302 * we do not transfer too much.
303 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000304 for (i = 0; i < len; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305 sgbuf = kmap_atomic(sg[i].page, KM_BIO_SRC_IRQ) + sg[i].offset;
306 if (size < sg[i].length)
307 memcpy(dmabuf, sgbuf, size);
308 else
309 memcpy(dmabuf, sgbuf, sg[i].length);
310 kunmap_atomic(sgbuf, KM_BIO_SRC_IRQ);
311 dmabuf += sg[i].length;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100312
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 if (size < sg[i].length)
314 size = 0;
315 else
316 size -= sg[i].length;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100317
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 if (size == 0)
319 break;
320 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100321
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 /*
323 * Check that we didn't get a request to transfer
324 * more data than can fit into the SG list.
325 */
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100326
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327 BUG_ON(size != 0);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100328
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 host->size -= size;
330}
331
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000332static inline void wbsd_dma_to_sg(struct wbsd_host *host, struct mmc_data *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333{
334 unsigned int len, i, size;
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000335 struct scatterlist *sg;
336 char *dmabuf = host->dma_buffer;
337 char *sgbuf;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100338
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 size = host->size;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100340
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 sg = data->sg;
342 len = data->sg_len;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100343
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 /*
345 * Just loop through all entries. Size might not
346 * be the entire list though so make sure that
347 * we do not transfer too much.
348 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000349 for (i = 0; i < len; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 sgbuf = kmap_atomic(sg[i].page, KM_BIO_SRC_IRQ) + sg[i].offset;
351 if (size < sg[i].length)
352 memcpy(sgbuf, dmabuf, size);
353 else
354 memcpy(sgbuf, dmabuf, sg[i].length);
355 kunmap_atomic(sgbuf, KM_BIO_SRC_IRQ);
356 dmabuf += sg[i].length;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100357
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 if (size < sg[i].length)
359 size = 0;
360 else
361 size -= sg[i].length;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100362
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363 if (size == 0)
364 break;
365 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100366
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367 /*
368 * Check that we didn't get a request to transfer
369 * more data than can fit into the SG list.
370 */
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100371
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 BUG_ON(size != 0);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100373
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 host->size -= size;
375}
376
377/*
378 * Command handling
379 */
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100380
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000381static inline void wbsd_get_short_reply(struct wbsd_host *host,
382 struct mmc_command *cmd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383{
384 /*
385 * Correct response type?
386 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000387 if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_SHORT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 cmd->error = MMC_ERR_INVALID;
389 return;
390 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100391
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000392 cmd->resp[0] = wbsd_read_index(host, WBSD_IDX_RESP12) << 24;
393 cmd->resp[0] |= wbsd_read_index(host, WBSD_IDX_RESP13) << 16;
394 cmd->resp[0] |= wbsd_read_index(host, WBSD_IDX_RESP14) << 8;
395 cmd->resp[0] |= wbsd_read_index(host, WBSD_IDX_RESP15) << 0;
396 cmd->resp[1] = wbsd_read_index(host, WBSD_IDX_RESP16) << 24;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397}
398
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000399static inline void wbsd_get_long_reply(struct wbsd_host *host,
400 struct mmc_command *cmd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401{
402 int i;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100403
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 /*
405 * Correct response type?
406 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000407 if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_LONG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 cmd->error = MMC_ERR_INVALID;
409 return;
410 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100411
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000412 for (i = 0; i < 4; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 cmd->resp[i] =
414 wbsd_read_index(host, WBSD_IDX_RESP1 + i * 4) << 24;
415 cmd->resp[i] |=
416 wbsd_read_index(host, WBSD_IDX_RESP2 + i * 4) << 16;
417 cmd->resp[i] |=
418 wbsd_read_index(host, WBSD_IDX_RESP3 + i * 4) << 8;
419 cmd->resp[i] |=
420 wbsd_read_index(host, WBSD_IDX_RESP4 + i * 4) << 0;
421 }
422}
423
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000424static void wbsd_send_command(struct wbsd_host *host, struct mmc_command *cmd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425{
426 int i;
427 u8 status, isr;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100428
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 DBGF("Sending cmd (%x)\n", cmd->opcode);
430
431 /*
432 * Clear accumulated ISR. The interrupt routine
433 * will fill this one with events that occur during
434 * transfer.
435 */
436 host->isr = 0;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100437
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438 /*
439 * Send the command (CRC calculated by host).
440 */
441 outb(cmd->opcode, host->base + WBSD_CMDR);
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000442 for (i = 3; i >= 0; i--)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 outb((cmd->arg >> (i * 8)) & 0xff, host->base + WBSD_CMDR);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100444
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445 cmd->error = MMC_ERR_NONE;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100446
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447 /*
448 * Wait for the request to complete.
449 */
450 do {
451 status = wbsd_read_index(host, WBSD_IDX_STATUS);
452 } while (status & WBSD_CARDTRAFFIC);
453
454 /*
455 * Do we expect a reply?
456 */
Russell Kinge9225172006-02-02 12:23:12 +0000457 if (cmd->flags & MMC_RSP_PRESENT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458 /*
459 * Read back status.
460 */
461 isr = host->isr;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100462
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463 /* Card removed? */
464 if (isr & WBSD_INT_CARD)
465 cmd->error = MMC_ERR_TIMEOUT;
466 /* Timeout? */
467 else if (isr & WBSD_INT_TIMEOUT)
468 cmd->error = MMC_ERR_TIMEOUT;
469 /* CRC? */
470 else if ((cmd->flags & MMC_RSP_CRC) && (isr & WBSD_INT_CRC))
471 cmd->error = MMC_ERR_BADCRC;
472 /* All ok */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000473 else {
Russell Kinge9225172006-02-02 12:23:12 +0000474 if (cmd->flags & MMC_RSP_136)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475 wbsd_get_long_reply(host, cmd);
Russell Kinge9225172006-02-02 12:23:12 +0000476 else
477 wbsd_get_short_reply(host, cmd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478 }
479 }
480
481 DBGF("Sent cmd (%x), res %d\n", cmd->opcode, cmd->error);
482}
483
484/*
485 * Data functions
486 */
487
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000488static void wbsd_empty_fifo(struct wbsd_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489{
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000490 struct mmc_data *data = host->mrq->cmd->data;
491 char *buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 int i, fsr, fifo;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100493
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 /*
495 * Handle excessive data.
496 */
497 if (data->bytes_xfered == host->size)
498 return;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100499
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 buffer = wbsd_kmap_sg(host) + host->offset;
501
502 /*
503 * Drain the fifo. This has a tendency to loop longer
504 * than the FIFO length (usually one block).
505 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000506 while (!((fsr = inb(host->base + WBSD_FSR)) & WBSD_FIFO_EMPTY)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 /*
508 * The size field in the FSR is broken so we have to
509 * do some guessing.
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100510 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 if (fsr & WBSD_FIFO_FULL)
512 fifo = 16;
513 else if (fsr & WBSD_FIFO_FUTHRE)
514 fifo = 8;
515 else
516 fifo = 1;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100517
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000518 for (i = 0; i < fifo; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 *buffer = inb(host->base + WBSD_DFR);
520 buffer++;
521 host->offset++;
522 host->remain--;
523
524 data->bytes_xfered++;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100525
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 /*
527 * Transfer done?
528 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000529 if (data->bytes_xfered == host->size) {
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100530 wbsd_kunmap_sg(host);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 return;
532 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100533
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534 /*
535 * End of scatter list entry?
536 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000537 if (host->remain == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 wbsd_kunmap_sg(host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100539
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 /*
541 * Get next entry. Check if last.
542 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000543 if (!wbsd_next_sg(host)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 /*
545 * We should never reach this point.
546 * It means that we're trying to
547 * transfer more blocks than can fit
548 * into the scatter list.
549 */
550 BUG_ON(1);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100551
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552 host->size = data->bytes_xfered;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100553
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554 return;
555 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100556
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 buffer = wbsd_kmap_sg(host);
558 }
559 }
560 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100561
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562 wbsd_kunmap_sg(host);
563
564 /*
565 * This is a very dirty hack to solve a
566 * hardware problem. The chip doesn't trigger
567 * FIFO threshold interrupts properly.
568 */
569 if ((host->size - data->bytes_xfered) < 16)
570 tasklet_schedule(&host->fifo_tasklet);
571}
572
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000573static void wbsd_fill_fifo(struct wbsd_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574{
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000575 struct mmc_data *data = host->mrq->cmd->data;
576 char *buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577 int i, fsr, fifo;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100578
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 /*
580 * Check that we aren't being called after the
581 * entire buffer has been transfered.
582 */
583 if (data->bytes_xfered == host->size)
584 return;
585
586 buffer = wbsd_kmap_sg(host) + host->offset;
587
588 /*
589 * Fill the fifo. This has a tendency to loop longer
590 * than the FIFO length (usually one block).
591 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000592 while (!((fsr = inb(host->base + WBSD_FSR)) & WBSD_FIFO_FULL)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 /*
594 * The size field in the FSR is broken so we have to
595 * do some guessing.
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100596 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 if (fsr & WBSD_FIFO_EMPTY)
598 fifo = 0;
599 else if (fsr & WBSD_FIFO_EMTHRE)
600 fifo = 8;
601 else
602 fifo = 15;
603
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000604 for (i = 16; i > fifo; i--) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605 outb(*buffer, host->base + WBSD_DFR);
606 buffer++;
607 host->offset++;
608 host->remain--;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100609
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610 data->bytes_xfered++;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100611
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612 /*
613 * Transfer done?
614 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000615 if (data->bytes_xfered == host->size) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616 wbsd_kunmap_sg(host);
617 return;
618 }
619
620 /*
621 * End of scatter list entry?
622 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000623 if (host->remain == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624 wbsd_kunmap_sg(host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100625
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 /*
627 * Get next entry. Check if last.
628 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000629 if (!wbsd_next_sg(host)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630 /*
631 * We should never reach this point.
632 * It means that we're trying to
633 * transfer more blocks than can fit
634 * into the scatter list.
635 */
636 BUG_ON(1);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100637
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 host->size = data->bytes_xfered;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100639
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640 return;
641 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100642
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 buffer = wbsd_kmap_sg(host);
644 }
645 }
646 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100647
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648 wbsd_kunmap_sg(host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100649
Pierre Ossman85bcc132005-05-08 19:35:27 +0100650 /*
651 * The controller stops sending interrupts for
652 * 'FIFO empty' under certain conditions. So we
653 * need to be a bit more pro-active.
654 */
655 tasklet_schedule(&host->fifo_tasklet);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656}
657
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000658static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659{
660 u16 blksize;
661 u8 setup;
662 unsigned long dmaflags;
663
664 DBGF("blksz %04x blks %04x flags %08x\n",
Pavel Pisa2c171bf2006-05-19 21:48:03 +0100665 data->blksz, data->blocks, data->flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666 DBGF("tsac %d ms nsac %d clk\n",
667 data->timeout_ns / 1000000, data->timeout_clks);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100668
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 /*
670 * Calculate size.
671 */
Pavel Pisa2c171bf2006-05-19 21:48:03 +0100672 host->size = data->blocks * data->blksz;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673
674 /*
675 * Check timeout values for overflow.
676 * (Yes, some cards cause this value to overflow).
677 */
678 if (data->timeout_ns > 127000000)
679 wbsd_write_index(host, WBSD_IDX_TAAC, 127);
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000680 else {
681 wbsd_write_index(host, WBSD_IDX_TAAC,
682 data->timeout_ns / 1000000);
683 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100684
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685 if (data->timeout_clks > 255)
686 wbsd_write_index(host, WBSD_IDX_NSAC, 255);
687 else
688 wbsd_write_index(host, WBSD_IDX_NSAC, data->timeout_clks);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100689
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 /*
691 * Inform the chip of how large blocks will be
692 * sent. It needs this to determine when to
693 * calculate CRC.
694 *
695 * Space for CRC must be included in the size.
Pierre Ossman65ae2112005-09-06 15:18:57 -0700696 * Two bytes are needed for each data line.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000698 if (host->bus_width == MMC_BUS_WIDTH_1) {
Pavel Pisa2c171bf2006-05-19 21:48:03 +0100699 blksize = data->blksz + 2;
Pierre Ossman65ae2112005-09-06 15:18:57 -0700700
701 wbsd_write_index(host, WBSD_IDX_PBSMSB, (blksize >> 4) & 0xF0);
702 wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF);
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000703 } else if (host->bus_width == MMC_BUS_WIDTH_4) {
Pavel Pisa2c171bf2006-05-19 21:48:03 +0100704 blksize = data->blksz + 2 * 4;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100705
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000706 wbsd_write_index(host, WBSD_IDX_PBSMSB,
707 ((blksize >> 4) & 0xF0) | WBSD_DATA_WIDTH);
Pierre Ossman65ae2112005-09-06 15:18:57 -0700708 wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF);
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000709 } else {
Pierre Ossman65ae2112005-09-06 15:18:57 -0700710 data->error = MMC_ERR_INVALID;
711 return;
712 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713
714 /*
715 * Clear the FIFO. This is needed even for DMA
716 * transfers since the chip still uses the FIFO
717 * internally.
718 */
719 setup = wbsd_read_index(host, WBSD_IDX_SETUP);
720 setup |= WBSD_FIFO_RESET;
721 wbsd_write_index(host, WBSD_IDX_SETUP, setup);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100722
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 /*
724 * DMA transfer?
725 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000726 if (host->dma >= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727 /*
728 * The buffer for DMA is only 64 kB.
729 */
730 BUG_ON(host->size > 0x10000);
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000731 if (host->size > 0x10000) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 data->error = MMC_ERR_INVALID;
733 return;
734 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100735
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736 /*
737 * Transfer data from the SG list to
738 * the DMA buffer.
739 */
740 if (data->flags & MMC_DATA_WRITE)
741 wbsd_sg_to_dma(host, data);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100742
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 /*
744 * Initialise the ISA DMA controller.
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100745 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746 dmaflags = claim_dma_lock();
747 disable_dma(host->dma);
748 clear_dma_ff(host->dma);
749 if (data->flags & MMC_DATA_READ)
750 set_dma_mode(host->dma, DMA_MODE_READ & ~0x40);
751 else
752 set_dma_mode(host->dma, DMA_MODE_WRITE & ~0x40);
753 set_dma_addr(host->dma, host->dma_addr);
754 set_dma_count(host->dma, host->size);
755
756 enable_dma(host->dma);
757 release_dma_lock(dmaflags);
758
759 /*
760 * Enable DMA on the host.
761 */
762 wbsd_write_index(host, WBSD_IDX_DMA, WBSD_DMA_ENABLE);
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000763 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764 /*
765 * This flag is used to keep printk
766 * output to a minimum.
767 */
768 host->firsterr = 1;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100769
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770 /*
771 * Initialise the SG list.
772 */
773 wbsd_init_sg(host, data);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100774
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 /*
776 * Turn off DMA.
777 */
778 wbsd_write_index(host, WBSD_IDX_DMA, 0);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100779
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 /*
781 * Set up FIFO threshold levels (and fill
782 * buffer if doing a write).
783 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000784 if (data->flags & MMC_DATA_READ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 wbsd_write_index(host, WBSD_IDX_FIFOEN,
786 WBSD_FIFOEN_FULL | 8);
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000787 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 wbsd_write_index(host, WBSD_IDX_FIFOEN,
789 WBSD_FIFOEN_EMPTY | 8);
790 wbsd_fill_fifo(host);
791 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100792 }
793
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 data->error = MMC_ERR_NONE;
795}
796
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000797static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798{
799 unsigned long dmaflags;
800 int count;
801 u8 status;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100802
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 WARN_ON(host->mrq == NULL);
804
805 /*
806 * Send a stop command if needed.
807 */
808 if (data->stop)
809 wbsd_send_command(host, data->stop);
810
811 /*
812 * Wait for the controller to leave data
813 * transfer state.
814 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000815 do {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 status = wbsd_read_index(host, WBSD_IDX_STATUS);
817 } while (status & (WBSD_BLOCK_READ | WBSD_BLOCK_WRITE));
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100818
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819 /*
820 * DMA transfer?
821 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000822 if (host->dma >= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823 /*
824 * Disable DMA on the host.
825 */
826 wbsd_write_index(host, WBSD_IDX_DMA, 0);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100827
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 /*
829 * Turn of ISA DMA controller.
830 */
831 dmaflags = claim_dma_lock();
832 disable_dma(host->dma);
833 clear_dma_ff(host->dma);
834 count = get_dma_residue(host->dma);
835 release_dma_lock(dmaflags);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100836
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837 /*
838 * Any leftover data?
839 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000840 if (count) {
Pierre Ossmand1916342005-11-05 10:36:35 +0000841 printk(KERN_ERR "%s: Incomplete DMA transfer. "
842 "%d bytes left.\n",
843 mmc_hostname(host->mmc), count);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100844
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 data->error = MMC_ERR_FAILED;
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000846 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847 /*
848 * Transfer data from DMA buffer to
849 * SG list.
850 */
851 if (data->flags & MMC_DATA_READ)
852 wbsd_dma_to_sg(host, data);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100853
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854 data->bytes_xfered = host->size;
855 }
856 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100857
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 DBGF("Ending data transfer (%d bytes)\n", data->bytes_xfered);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100859
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860 wbsd_request_end(host, host->mrq);
861}
862
Pierre Ossman85bcc132005-05-08 19:35:27 +0100863/*****************************************************************************\
864 * *
865 * MMC layer callbacks *
866 * *
867\*****************************************************************************/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000869static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870{
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000871 struct wbsd_host *host = mmc_priv(mmc);
872 struct mmc_command *cmd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873
874 /*
875 * Disable tasklets to avoid a deadlock.
876 */
877 spin_lock_bh(&host->lock);
878
879 BUG_ON(host->mrq != NULL);
880
881 cmd = mrq->cmd;
882
883 host->mrq = mrq;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100884
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885 /*
886 * If there is no card in the slot then
887 * timeout immediatly.
888 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000889 if (!(host->flags & WBSD_FCARD_PRESENT)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890 cmd->error = MMC_ERR_TIMEOUT;
891 goto done;
892 }
893
894 /*
895 * Does the request include data?
896 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000897 if (cmd->data) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898 wbsd_prepare_data(host, cmd->data);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100899
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900 if (cmd->data->error != MMC_ERR_NONE)
901 goto done;
902 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100903
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904 wbsd_send_command(host, cmd);
905
906 /*
907 * If this is a data transfer the request
908 * will be finished after the data has
909 * transfered.
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100910 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000911 if (cmd->data && (cmd->error == MMC_ERR_NONE)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912 /*
913 * Dirty fix for hardware bug.
914 */
915 if (host->dma == -1)
916 tasklet_schedule(&host->fifo_tasklet);
917
918 spin_unlock_bh(&host->lock);
919
920 return;
921 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100922
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923done:
924 wbsd_request_end(host, mrq);
925
926 spin_unlock_bh(&host->lock);
927}
928
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000929static void wbsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930{
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000931 struct wbsd_host *host = mmc_priv(mmc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932 u8 clk, setup, pwr;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100933
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934 spin_lock_bh(&host->lock);
935
936 /*
937 * Reset the chip on each power off.
938 * Should clear out any weird states.
939 */
940 if (ios->power_mode == MMC_POWER_OFF)
941 wbsd_init_device(host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100942
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943 if (ios->clock >= 24000000)
944 clk = WBSD_CLK_24M;
945 else if (ios->clock >= 16000000)
946 clk = WBSD_CLK_16M;
947 else if (ios->clock >= 12000000)
948 clk = WBSD_CLK_12M;
949 else
950 clk = WBSD_CLK_375K;
951
952 /*
953 * Only write to the clock register when
954 * there is an actual change.
955 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000956 if (clk != host->clk) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957 wbsd_write_index(host, WBSD_IDX_CLK, clk);
958 host->clk = clk;
959 }
960
Pierre Ossman85bcc132005-05-08 19:35:27 +0100961 /*
962 * Power up card.
963 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000964 if (ios->power_mode != MMC_POWER_OFF) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965 pwr = inb(host->base + WBSD_CSR);
966 pwr &= ~WBSD_POWER_N;
967 outb(pwr, host->base + WBSD_CSR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968 }
969
Pierre Ossman85bcc132005-05-08 19:35:27 +0100970 /*
971 * MMC cards need to have pin 1 high during init.
Pierre Ossman85bcc132005-05-08 19:35:27 +0100972 * It wreaks havoc with the card detection though so
Pierre Ossman1656fa52005-09-03 16:45:49 +0100973 * that needs to be disabled.
Pierre Ossman85bcc132005-05-08 19:35:27 +0100974 */
975 setup = wbsd_read_index(host, WBSD_IDX_SETUP);
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000976 if (ios->chip_select == MMC_CS_HIGH) {
Pierre Ossman65ae2112005-09-06 15:18:57 -0700977 BUG_ON(ios->bus_width != MMC_BUS_WIDTH_1);
Pierre Ossman85bcc132005-05-08 19:35:27 +0100978 setup |= WBSD_DAT3_H;
979 host->flags |= WBSD_FIGNORE_DETECT;
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000980 } else {
981 if (setup & WBSD_DAT3_H) {
Pierre Ossman19c1f3c2005-10-28 21:37:29 +0100982 setup &= ~WBSD_DAT3_H;
Pierre Ossman1656fa52005-09-03 16:45:49 +0100983
Pierre Ossman19c1f3c2005-10-28 21:37:29 +0100984 /*
985 * We cannot resume card detection immediatly
986 * because of capacitance and delays in the chip.
987 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000988 mod_timer(&host->ignore_timer, jiffies + HZ / 100);
Pierre Ossman19c1f3c2005-10-28 21:37:29 +0100989 }
Pierre Ossman85bcc132005-05-08 19:35:27 +0100990 }
991 wbsd_write_index(host, WBSD_IDX_SETUP, setup);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100992
Pierre Ossman65ae2112005-09-06 15:18:57 -0700993 /*
994 * Store bus width for later. Will be used when
995 * setting up the data transfer.
996 */
997 host->bus_width = ios->bus_width;
998
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999 spin_unlock_bh(&host->lock);
1000}
1001
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001002static int wbsd_get_ro(struct mmc_host *mmc)
Pierre Ossman65ae2112005-09-06 15:18:57 -07001003{
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001004 struct wbsd_host *host = mmc_priv(mmc);
Pierre Ossman65ae2112005-09-06 15:18:57 -07001005 u8 csr;
1006
1007 spin_lock_bh(&host->lock);
1008
1009 csr = inb(host->base + WBSD_CSR);
1010 csr |= WBSD_MSLED;
1011 outb(csr, host->base + WBSD_CSR);
1012
1013 mdelay(1);
1014
1015 csr = inb(host->base + WBSD_CSR);
1016 csr &= ~WBSD_MSLED;
1017 outb(csr, host->base + WBSD_CSR);
1018
1019 spin_unlock_bh(&host->lock);
1020
1021 return csr & WBSD_WRPT;
1022}
1023
David Brownellab7aefd2006-11-12 17:55:30 -08001024static const struct mmc_host_ops wbsd_ops = {
Pierre Ossman85bcc132005-05-08 19:35:27 +01001025 .request = wbsd_request,
1026 .set_ios = wbsd_set_ios,
Pierre Ossman65ae2112005-09-06 15:18:57 -07001027 .get_ro = wbsd_get_ro,
Pierre Ossman85bcc132005-05-08 19:35:27 +01001028};
1029
1030/*****************************************************************************\
1031 * *
1032 * Interrupt handling *
1033 * *
1034\*****************************************************************************/
1035
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036/*
Pierre Ossman1656fa52005-09-03 16:45:49 +01001037 * Helper function to reset detection ignore
1038 */
1039
1040static void wbsd_reset_ignore(unsigned long data)
1041{
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001042 struct wbsd_host *host = (struct wbsd_host *)data;
Pierre Ossman1656fa52005-09-03 16:45:49 +01001043
1044 BUG_ON(host == NULL);
1045
1046 DBG("Resetting card detection ignore\n");
1047
1048 spin_lock_bh(&host->lock);
1049
1050 host->flags &= ~WBSD_FIGNORE_DETECT;
1051
1052 /*
1053 * Card status might have changed during the
1054 * blackout.
1055 */
1056 tasklet_schedule(&host->card_tasklet);
1057
1058 spin_unlock_bh(&host->lock);
1059}
1060
1061/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 * Tasklets
1063 */
1064
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001065static inline struct mmc_data *wbsd_get_data(struct wbsd_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066{
1067 WARN_ON(!host->mrq);
1068 if (!host->mrq)
1069 return NULL;
1070
1071 WARN_ON(!host->mrq->cmd);
1072 if (!host->mrq->cmd)
1073 return NULL;
1074
1075 WARN_ON(!host->mrq->cmd->data);
1076 if (!host->mrq->cmd->data)
1077 return NULL;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001078
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 return host->mrq->cmd->data;
1080}
1081
1082static void wbsd_tasklet_card(unsigned long param)
1083{
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001084 struct wbsd_host *host = (struct wbsd_host *)param;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 u8 csr;
Pierre Ossman210ce2a2005-09-12 20:36:19 +01001086 int delay = -1;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001087
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 spin_lock(&host->lock);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001089
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001090 if (host->flags & WBSD_FIGNORE_DETECT) {
Pierre Ossman85bcc132005-05-08 19:35:27 +01001091 spin_unlock(&host->lock);
1092 return;
1093 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001094
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095 csr = inb(host->base + WBSD_CSR);
1096 WARN_ON(csr == 0xff);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001097
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001098 if (csr & WBSD_CARDPRESENT) {
1099 if (!(host->flags & WBSD_FCARD_PRESENT)) {
Pierre Ossman85bcc132005-05-08 19:35:27 +01001100 DBG("Card inserted\n");
1101 host->flags |= WBSD_FCARD_PRESENT;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001102
Pierre Ossman210ce2a2005-09-12 20:36:19 +01001103 delay = 500;
Pierre Ossman85bcc132005-05-08 19:35:27 +01001104 }
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001105 } else if (host->flags & WBSD_FCARD_PRESENT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106 DBG("Card removed\n");
Pierre Ossman85bcc132005-05-08 19:35:27 +01001107 host->flags &= ~WBSD_FCARD_PRESENT;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001108
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001109 if (host->mrq) {
Pierre Ossmand1916342005-11-05 10:36:35 +00001110 printk(KERN_ERR "%s: Card removed during transfer!\n",
1111 mmc_hostname(host->mmc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112 wbsd_reset(host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001113
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114 host->mrq->cmd->error = MMC_ERR_FAILED;
1115 tasklet_schedule(&host->finish_tasklet);
1116 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001117
Pierre Ossman210ce2a2005-09-12 20:36:19 +01001118 delay = 0;
Pierre Ossman6e6293d2005-07-01 12:13:55 +01001119 }
Pierre Ossman210ce2a2005-09-12 20:36:19 +01001120
1121 /*
1122 * Unlock first since we might get a call back.
1123 */
1124
1125 spin_unlock(&host->lock);
1126
1127 if (delay != -1)
1128 mmc_detect_change(host->mmc, msecs_to_jiffies(delay));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129}
1130
1131static void wbsd_tasklet_fifo(unsigned long param)
1132{
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001133 struct wbsd_host *host = (struct wbsd_host *)param;
1134 struct mmc_data *data;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001135
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136 spin_lock(&host->lock);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001137
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138 if (!host->mrq)
1139 goto end;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001140
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141 data = wbsd_get_data(host);
1142 if (!data)
1143 goto end;
1144
1145 if (data->flags & MMC_DATA_WRITE)
1146 wbsd_fill_fifo(host);
1147 else
1148 wbsd_empty_fifo(host);
1149
1150 /*
1151 * Done?
1152 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001153 if (host->size == data->bytes_xfered) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 wbsd_write_index(host, WBSD_IDX_FIFOEN, 0);
1155 tasklet_schedule(&host->finish_tasklet);
1156 }
1157
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001158end:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159 spin_unlock(&host->lock);
1160}
1161
1162static void wbsd_tasklet_crc(unsigned long param)
1163{
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001164 struct wbsd_host *host = (struct wbsd_host *)param;
1165 struct mmc_data *data;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001166
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167 spin_lock(&host->lock);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001168
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 if (!host->mrq)
1170 goto end;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001171
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172 data = wbsd_get_data(host);
1173 if (!data)
1174 goto end;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001175
Linus Torvalds1da177e2005-04-16 15:20:36 -07001176 DBGF("CRC error\n");
1177
1178 data->error = MMC_ERR_BADCRC;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001179
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180 tasklet_schedule(&host->finish_tasklet);
1181
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001182end:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001183 spin_unlock(&host->lock);
1184}
1185
1186static void wbsd_tasklet_timeout(unsigned long param)
1187{
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001188 struct wbsd_host *host = (struct wbsd_host *)param;
1189 struct mmc_data *data;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001190
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191 spin_lock(&host->lock);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001192
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193 if (!host->mrq)
1194 goto end;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001195
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196 data = wbsd_get_data(host);
1197 if (!data)
1198 goto end;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001199
Linus Torvalds1da177e2005-04-16 15:20:36 -07001200 DBGF("Timeout\n");
1201
1202 data->error = MMC_ERR_TIMEOUT;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001203
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 tasklet_schedule(&host->finish_tasklet);
1205
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001206end:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207 spin_unlock(&host->lock);
1208}
1209
1210static void wbsd_tasklet_finish(unsigned long param)
1211{
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001212 struct wbsd_host *host = (struct wbsd_host *)param;
1213 struct mmc_data *data;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001214
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215 spin_lock(&host->lock);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001216
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217 WARN_ON(!host->mrq);
1218 if (!host->mrq)
1219 goto end;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001220
Linus Torvalds1da177e2005-04-16 15:20:36 -07001221 data = wbsd_get_data(host);
1222 if (!data)
1223 goto end;
1224
1225 wbsd_finish_data(host, data);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001226
1227end:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001228 spin_unlock(&host->lock);
1229}
1230
1231static void wbsd_tasklet_block(unsigned long param)
1232{
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001233 struct wbsd_host *host = (struct wbsd_host *)param;
1234 struct mmc_data *data;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001235
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236 spin_lock(&host->lock);
1237
1238 if ((wbsd_read_index(host, WBSD_IDX_CRCSTATUS) & WBSD_CRC_MASK) !=
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001239 WBSD_CRC_OK) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001240 data = wbsd_get_data(host);
1241 if (!data)
1242 goto end;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001243
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244 DBGF("CRC error\n");
1245
1246 data->error = MMC_ERR_BADCRC;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001247
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248 tasklet_schedule(&host->finish_tasklet);
1249 }
1250
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001251end:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252 spin_unlock(&host->lock);
1253}
1254
1255/*
1256 * Interrupt handling
1257 */
1258
David Howells7d12e782006-10-05 14:55:46 +01001259static irqreturn_t wbsd_irq(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260{
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001261 struct wbsd_host *host = dev_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262 int isr;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001263
Linus Torvalds1da177e2005-04-16 15:20:36 -07001264 isr = inb(host->base + WBSD_ISR);
1265
1266 /*
1267 * Was it actually our hardware that caused the interrupt?
1268 */
1269 if (isr == 0xff || isr == 0x00)
1270 return IRQ_NONE;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001271
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272 host->isr |= isr;
1273
1274 /*
1275 * Schedule tasklets as needed.
1276 */
1277 if (isr & WBSD_INT_CARD)
1278 tasklet_schedule(&host->card_tasklet);
1279 if (isr & WBSD_INT_FIFO_THRE)
1280 tasklet_schedule(&host->fifo_tasklet);
1281 if (isr & WBSD_INT_CRC)
1282 tasklet_hi_schedule(&host->crc_tasklet);
1283 if (isr & WBSD_INT_TIMEOUT)
1284 tasklet_hi_schedule(&host->timeout_tasklet);
1285 if (isr & WBSD_INT_BUSYEND)
1286 tasklet_hi_schedule(&host->block_tasklet);
1287 if (isr & WBSD_INT_TC)
1288 tasklet_schedule(&host->finish_tasklet);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001289
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290 return IRQ_HANDLED;
1291}
1292
Pierre Ossman85bcc132005-05-08 19:35:27 +01001293/*****************************************************************************\
1294 * *
1295 * Device initialisation and shutdown *
1296 * *
1297\*****************************************************************************/
1298
Linus Torvalds1da177e2005-04-16 15:20:36 -07001299/*
Pierre Ossman85bcc132005-05-08 19:35:27 +01001300 * Allocate/free MMC structure.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001301 */
1302
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001303static int __devinit wbsd_alloc_mmc(struct device *dev)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001304{
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001305 struct mmc_host *mmc;
1306 struct wbsd_host *host;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001307
Pierre Ossman85bcc132005-05-08 19:35:27 +01001308 /*
1309 * Allocate MMC structure.
1310 */
1311 mmc = mmc_alloc_host(sizeof(struct wbsd_host), dev);
1312 if (!mmc)
1313 return -ENOMEM;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001314
Pierre Ossman85bcc132005-05-08 19:35:27 +01001315 host = mmc_priv(mmc);
1316 host->mmc = mmc;
1317
1318 host->dma = -1;
1319
1320 /*
1321 * Set host parameters.
1322 */
1323 mmc->ops = &wbsd_ops;
1324 mmc->f_min = 375000;
1325 mmc->f_max = 24000000;
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001326 mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
Russell King42431ac2006-09-24 10:44:09 +01001327 mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001328
Pierre Ossman85bcc132005-05-08 19:35:27 +01001329 spin_lock_init(&host->lock);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001330
Pierre Ossman85bcc132005-05-08 19:35:27 +01001331 /*
Pierre Ossman1656fa52005-09-03 16:45:49 +01001332 * Set up timers
Pierre Ossman6e6293d2005-07-01 12:13:55 +01001333 */
Pierre Ossman1656fa52005-09-03 16:45:49 +01001334 init_timer(&host->ignore_timer);
1335 host->ignore_timer.data = (unsigned long)host;
1336 host->ignore_timer.function = wbsd_reset_ignore;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001337
Pierre Ossman6e6293d2005-07-01 12:13:55 +01001338 /*
Pierre Ossman85bcc132005-05-08 19:35:27 +01001339 * Maximum number of segments. Worst case is one sector per segment
1340 * so this will be 64kB/512.
1341 */
1342 mmc->max_hw_segs = 128;
1343 mmc->max_phys_segs = 128;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001344
Pierre Ossman85bcc132005-05-08 19:35:27 +01001345 /*
Pierre Ossman55db8902006-11-21 17:55:45 +01001346 * Maximum request size. Also limited by 64KiB buffer.
Pierre Ossman85bcc132005-05-08 19:35:27 +01001347 */
Pierre Ossman55db8902006-11-21 17:55:45 +01001348 mmc->max_req_size = 65536;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001349
Pierre Ossman85bcc132005-05-08 19:35:27 +01001350 /*
1351 * Maximum segment size. Could be one segment with the maximum number
Pierre Ossman55db8902006-11-21 17:55:45 +01001352 * of bytes.
Pierre Ossman85bcc132005-05-08 19:35:27 +01001353 */
Pierre Ossman55db8902006-11-21 17:55:45 +01001354 mmc->max_seg_size = mmc->max_req_size;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001355
Pierre Ossmanfe4a3c72006-11-21 17:54:23 +01001356 /*
1357 * Maximum block size. We have 12 bits (= 4095) but have to subtract
1358 * space for CRC. So the maximum is 4095 - 4*2 = 4087.
1359 */
1360 mmc->max_blk_size = 4087;
1361
Pierre Ossman55db8902006-11-21 17:55:45 +01001362 /*
1363 * Maximum block count. There is no real limit so the maximum
1364 * request size will be the only restriction.
1365 */
1366 mmc->max_blk_count = mmc->max_req_size;
1367
Pierre Ossman85bcc132005-05-08 19:35:27 +01001368 dev_set_drvdata(dev, mmc);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001369
Pierre Ossman85bcc132005-05-08 19:35:27 +01001370 return 0;
1371}
1372
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001373static void __devexit wbsd_free_mmc(struct device *dev)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001374{
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001375 struct mmc_host *mmc;
1376 struct wbsd_host *host;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001377
Pierre Ossman85bcc132005-05-08 19:35:27 +01001378 mmc = dev_get_drvdata(dev);
1379 if (!mmc)
1380 return;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001381
Pierre Ossman6e6293d2005-07-01 12:13:55 +01001382 host = mmc_priv(mmc);
1383 BUG_ON(host == NULL);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001384
Pierre Ossman1656fa52005-09-03 16:45:49 +01001385 del_timer_sync(&host->ignore_timer);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001386
Pierre Ossman85bcc132005-05-08 19:35:27 +01001387 mmc_free_host(mmc);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001388
Pierre Ossman85bcc132005-05-08 19:35:27 +01001389 dev_set_drvdata(dev, NULL);
1390}
1391
1392/*
1393 * Scan for known chip id:s
1394 */
1395
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001396static int __devinit wbsd_scan(struct wbsd_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001397{
1398 int i, j, k;
1399 int id;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001400
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401 /*
1402 * Iterate through all ports, all codes to
1403 * find hardware that is in our known list.
1404 */
Dmitry Torokhov63648fb2006-01-03 22:56:56 +00001405 for (i = 0; i < ARRAY_SIZE(config_ports); i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001406 if (!request_region(config_ports[i], 2, DRIVER_NAME))
1407 continue;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001408
Dmitry Torokhov63648fb2006-01-03 22:56:56 +00001409 for (j = 0; j < ARRAY_SIZE(unlock_codes); j++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410 id = 0xFFFF;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001411
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001412 host->config = config_ports[i];
1413 host->unlock_code = unlock_codes[j];
1414
1415 wbsd_unlock_config(host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001416
Linus Torvalds1da177e2005-04-16 15:20:36 -07001417 outb(WBSD_CONF_ID_HI, config_ports[i]);
1418 id = inb(config_ports[i] + 1) << 8;
1419
1420 outb(WBSD_CONF_ID_LO, config_ports[i]);
1421 id |= inb(config_ports[i] + 1);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001422
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001423 wbsd_lock_config(host);
1424
Dmitry Torokhov63648fb2006-01-03 22:56:56 +00001425 for (k = 0; k < ARRAY_SIZE(valid_ids); k++) {
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001426 if (id == valid_ids[k]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427 host->chip_id = id;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001428
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429 return 0;
1430 }
1431 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001432
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001433 if (id != 0xFFFF) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001434 DBG("Unknown hardware (id %x) found at %x\n",
1435 id, config_ports[i]);
1436 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001437 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001438
Linus Torvalds1da177e2005-04-16 15:20:36 -07001439 release_region(config_ports[i], 2);
1440 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001441
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001442 host->config = 0;
1443 host->unlock_code = 0;
1444
Linus Torvalds1da177e2005-04-16 15:20:36 -07001445 return -ENODEV;
1446}
1447
Pierre Ossman85bcc132005-05-08 19:35:27 +01001448/*
1449 * Allocate/free io port ranges
1450 */
1451
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001452static int __devinit wbsd_request_region(struct wbsd_host *host, int base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001453{
Pierre Ossman916f3ac2006-08-06 22:22:23 +02001454 if (base & 0x7)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455 return -EINVAL;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001456
Pierre Ossman85bcc132005-05-08 19:35:27 +01001457 if (!request_region(base, 8, DRIVER_NAME))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001458 return -EIO;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001459
Pierre Ossman916f3ac2006-08-06 22:22:23 +02001460 host->base = base;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001461
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462 return 0;
1463}
1464
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001465static void __devexit wbsd_release_regions(struct wbsd_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001466{
1467 if (host->base)
1468 release_region(host->base, 8);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001469
Pierre Ossman85bcc132005-05-08 19:35:27 +01001470 host->base = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471
1472 if (host->config)
1473 release_region(host->config, 2);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001474
Pierre Ossman85bcc132005-05-08 19:35:27 +01001475 host->config = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001476}
1477
Pierre Ossman85bcc132005-05-08 19:35:27 +01001478/*
1479 * Allocate/free DMA port and buffer
1480 */
1481
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001482static void __devinit wbsd_request_dma(struct wbsd_host *host, int dma)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001483{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001484 if (dma < 0)
1485 return;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001486
Linus Torvalds1da177e2005-04-16 15:20:36 -07001487 if (request_dma(dma, DRIVER_NAME))
1488 goto err;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001489
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490 /*
1491 * We need to allocate a special buffer in
1492 * order for ISA to be able to DMA to it.
1493 */
Pierre Ossman85bcc132005-05-08 19:35:27 +01001494 host->dma_buffer = kmalloc(WBSD_DMA_SIZE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001495 GFP_NOIO | GFP_DMA | __GFP_REPEAT | __GFP_NOWARN);
1496 if (!host->dma_buffer)
1497 goto free;
1498
1499 /*
1500 * Translate the address to a physical address.
1501 */
Greg Kroah-Hartmanfcaf71f2006-09-12 17:00:10 +02001502 host->dma_addr = dma_map_single(mmc_dev(host->mmc), host->dma_buffer,
Pierre Ossman85bcc132005-05-08 19:35:27 +01001503 WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001504
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505 /*
1506 * ISA DMA must be aligned on a 64k basis.
1507 */
1508 if ((host->dma_addr & 0xffff) != 0)
1509 goto kfree;
1510 /*
1511 * ISA cannot access memory above 16 MB.
1512 */
1513 else if (host->dma_addr >= 0x1000000)
1514 goto kfree;
1515
1516 host->dma = dma;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001517
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518 return;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001519
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520kfree:
1521 /*
1522 * If we've gotten here then there is some kind of alignment bug
1523 */
1524 BUG_ON(1);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001525
Greg Kroah-Hartmanfcaf71f2006-09-12 17:00:10 +02001526 dma_unmap_single(mmc_dev(host->mmc), host->dma_addr,
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001527 WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
Pierre Ossman85bcc132005-05-08 19:35:27 +01001528 host->dma_addr = (dma_addr_t)NULL;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001529
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530 kfree(host->dma_buffer);
1531 host->dma_buffer = NULL;
1532
1533free:
1534 free_dma(dma);
1535
1536err:
1537 printk(KERN_WARNING DRIVER_NAME ": Unable to allocate DMA %d. "
1538 "Falling back on FIFO.\n", dma);
1539}
1540
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001541static void __devexit wbsd_release_dma(struct wbsd_host *host)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001542{
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001543 if (host->dma_addr) {
Greg Kroah-Hartmanfcaf71f2006-09-12 17:00:10 +02001544 dma_unmap_single(mmc_dev(host->mmc), host->dma_addr,
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001545 WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
1546 }
Jesper Juhl6044ec82005-11-07 01:01:32 -08001547 kfree(host->dma_buffer);
Pierre Ossman85bcc132005-05-08 19:35:27 +01001548 if (host->dma >= 0)
1549 free_dma(host->dma);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001550
Pierre Ossman85bcc132005-05-08 19:35:27 +01001551 host->dma = -1;
1552 host->dma_buffer = NULL;
1553 host->dma_addr = (dma_addr_t)NULL;
1554}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001555
1556/*
Pierre Ossman85bcc132005-05-08 19:35:27 +01001557 * Allocate/free IRQ.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001558 */
1559
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001560static int __devinit wbsd_request_irq(struct wbsd_host *host, int irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001561{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001562 int ret;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001563
Linus Torvalds1da177e2005-04-16 15:20:36 -07001564 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001565 * Allocate interrupt.
1566 */
Pierre Ossman85bcc132005-05-08 19:35:27 +01001567
Thomas Gleixnerdace1452006-07-01 19:29:38 -07001568 ret = request_irq(irq, wbsd_irq, IRQF_SHARED, DRIVER_NAME, host);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001569 if (ret)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001570 return ret;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001571
Linus Torvalds1da177e2005-04-16 15:20:36 -07001572 host->irq = irq;
Pierre Ossman85bcc132005-05-08 19:35:27 +01001573
Linus Torvalds1da177e2005-04-16 15:20:36 -07001574 /*
1575 * Set up tasklets.
1576 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001577 tasklet_init(&host->card_tasklet, wbsd_tasklet_card,
1578 (unsigned long)host);
1579 tasklet_init(&host->fifo_tasklet, wbsd_tasklet_fifo,
1580 (unsigned long)host);
1581 tasklet_init(&host->crc_tasklet, wbsd_tasklet_crc,
1582 (unsigned long)host);
1583 tasklet_init(&host->timeout_tasklet, wbsd_tasklet_timeout,
1584 (unsigned long)host);
1585 tasklet_init(&host->finish_tasklet, wbsd_tasklet_finish,
1586 (unsigned long)host);
1587 tasklet_init(&host->block_tasklet, wbsd_tasklet_block,
1588 (unsigned long)host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001589
Linus Torvalds1da177e2005-04-16 15:20:36 -07001590 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591}
1592
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001593static void __devexit wbsd_release_irq(struct wbsd_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001594{
Pierre Ossman85bcc132005-05-08 19:35:27 +01001595 if (!host->irq)
1596 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001597
1598 free_irq(host->irq, host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001599
Pierre Ossman85bcc132005-05-08 19:35:27 +01001600 host->irq = 0;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001601
Linus Torvalds1da177e2005-04-16 15:20:36 -07001602 tasklet_kill(&host->card_tasklet);
1603 tasklet_kill(&host->fifo_tasklet);
1604 tasklet_kill(&host->crc_tasklet);
1605 tasklet_kill(&host->timeout_tasklet);
1606 tasklet_kill(&host->finish_tasklet);
1607 tasklet_kill(&host->block_tasklet);
Pierre Ossman85bcc132005-05-08 19:35:27 +01001608}
1609
1610/*
1611 * Allocate all resources for the host.
1612 */
1613
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001614static int __devinit wbsd_request_resources(struct wbsd_host *host,
Pierre Ossman85bcc132005-05-08 19:35:27 +01001615 int base, int irq, int dma)
1616{
1617 int ret;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001618
Pierre Ossman85bcc132005-05-08 19:35:27 +01001619 /*
1620 * Allocate I/O ports.
1621 */
1622 ret = wbsd_request_region(host, base);
1623 if (ret)
1624 return ret;
1625
1626 /*
1627 * Allocate interrupt.
1628 */
1629 ret = wbsd_request_irq(host, irq);
1630 if (ret)
1631 return ret;
1632
1633 /*
1634 * Allocate DMA.
1635 */
1636 wbsd_request_dma(host, dma);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001637
Pierre Ossman85bcc132005-05-08 19:35:27 +01001638 return 0;
1639}
1640
1641/*
1642 * Release all resources for the host.
1643 */
1644
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001645static void __devexit wbsd_release_resources(struct wbsd_host *host)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001646{
1647 wbsd_release_dma(host);
1648 wbsd_release_irq(host);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001649 wbsd_release_regions(host);
Pierre Ossman85bcc132005-05-08 19:35:27 +01001650}
1651
1652/*
1653 * Configure the resources the chip should use.
1654 */
1655
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001656static void wbsd_chip_config(struct wbsd_host *host)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001657{
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001658 wbsd_unlock_config(host);
1659
Pierre Ossman85bcc132005-05-08 19:35:27 +01001660 /*
1661 * Reset the chip.
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001662 */
Pierre Ossman85bcc132005-05-08 19:35:27 +01001663 wbsd_write_config(host, WBSD_CONF_SWRST, 1);
1664 wbsd_write_config(host, WBSD_CONF_SWRST, 0);
1665
1666 /*
1667 * Select SD/MMC function.
1668 */
1669 wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001670
Pierre Ossman85bcc132005-05-08 19:35:27 +01001671 /*
1672 * Set up card detection.
1673 */
1674 wbsd_write_config(host, WBSD_CONF_PINS, WBSD_PINS_DETECT_GP11);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001675
Pierre Ossman85bcc132005-05-08 19:35:27 +01001676 /*
1677 * Configure chip
1678 */
1679 wbsd_write_config(host, WBSD_CONF_PORT_HI, host->base >> 8);
1680 wbsd_write_config(host, WBSD_CONF_PORT_LO, host->base & 0xff);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001681
Pierre Ossman85bcc132005-05-08 19:35:27 +01001682 wbsd_write_config(host, WBSD_CONF_IRQ, host->irq);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001683
Pierre Ossman85bcc132005-05-08 19:35:27 +01001684 if (host->dma >= 0)
1685 wbsd_write_config(host, WBSD_CONF_DRQ, host->dma);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001686
Pierre Ossman85bcc132005-05-08 19:35:27 +01001687 /*
1688 * Enable and power up chip.
1689 */
1690 wbsd_write_config(host, WBSD_CONF_ENABLE, 1);
1691 wbsd_write_config(host, WBSD_CONF_POWER, 0x20);
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001692
1693 wbsd_lock_config(host);
Pierre Ossman85bcc132005-05-08 19:35:27 +01001694}
1695
1696/*
1697 * Check that configured resources are correct.
1698 */
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001699
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001700static int wbsd_chip_validate(struct wbsd_host *host)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001701{
1702 int base, irq, dma;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001703
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001704 wbsd_unlock_config(host);
1705
Pierre Ossman85bcc132005-05-08 19:35:27 +01001706 /*
1707 * Select SD/MMC function.
1708 */
1709 wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001710
Pierre Ossman85bcc132005-05-08 19:35:27 +01001711 /*
1712 * Read configuration.
1713 */
1714 base = wbsd_read_config(host, WBSD_CONF_PORT_HI) << 8;
1715 base |= wbsd_read_config(host, WBSD_CONF_PORT_LO);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001716
Pierre Ossman85bcc132005-05-08 19:35:27 +01001717 irq = wbsd_read_config(host, WBSD_CONF_IRQ);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001718
Pierre Ossman85bcc132005-05-08 19:35:27 +01001719 dma = wbsd_read_config(host, WBSD_CONF_DRQ);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001720
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001721 wbsd_lock_config(host);
1722
Pierre Ossman85bcc132005-05-08 19:35:27 +01001723 /*
1724 * Validate against given configuration.
1725 */
1726 if (base != host->base)
1727 return 0;
1728 if (irq != host->irq)
1729 return 0;
1730 if ((dma != host->dma) && (host->dma != -1))
1731 return 0;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001732
Pierre Ossman85bcc132005-05-08 19:35:27 +01001733 return 1;
1734}
1735
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001736/*
1737 * Powers down the SD function
1738 */
1739
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001740static void wbsd_chip_poweroff(struct wbsd_host *host)
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001741{
1742 wbsd_unlock_config(host);
1743
1744 wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
1745 wbsd_write_config(host, WBSD_CONF_ENABLE, 0);
1746
1747 wbsd_lock_config(host);
1748}
1749
Pierre Ossman85bcc132005-05-08 19:35:27 +01001750/*****************************************************************************\
1751 * *
1752 * Devices setup and shutdown *
1753 * *
1754\*****************************************************************************/
1755
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001756static int __devinit wbsd_init(struct device *dev, int base, int irq, int dma,
Pierre Ossman85bcc132005-05-08 19:35:27 +01001757 int pnp)
1758{
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001759 struct wbsd_host *host = NULL;
1760 struct mmc_host *mmc = NULL;
Pierre Ossman85bcc132005-05-08 19:35:27 +01001761 int ret;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001762
Pierre Ossman85bcc132005-05-08 19:35:27 +01001763 ret = wbsd_alloc_mmc(dev);
1764 if (ret)
1765 return ret;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001766
Pierre Ossman85bcc132005-05-08 19:35:27 +01001767 mmc = dev_get_drvdata(dev);
1768 host = mmc_priv(mmc);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001769
Pierre Ossman85bcc132005-05-08 19:35:27 +01001770 /*
1771 * Scan for hardware.
1772 */
1773 ret = wbsd_scan(host);
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001774 if (ret) {
1775 if (pnp && (ret == -ENODEV)) {
Pierre Ossman85bcc132005-05-08 19:35:27 +01001776 printk(KERN_WARNING DRIVER_NAME
1777 ": Unable to confirm device presence. You may "
1778 "experience lock-ups.\n");
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001779 } else {
Pierre Ossman85bcc132005-05-08 19:35:27 +01001780 wbsd_free_mmc(dev);
1781 return ret;
1782 }
1783 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001784
Pierre Ossman85bcc132005-05-08 19:35:27 +01001785 /*
1786 * Request resources.
1787 */
Pierre Ossmandd2c6092006-08-07 01:40:04 +02001788 ret = wbsd_request_resources(host, base, irq, dma);
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001789 if (ret) {
Pierre Ossman85bcc132005-05-08 19:35:27 +01001790 wbsd_release_resources(host);
1791 wbsd_free_mmc(dev);
1792 return ret;
1793 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001794
Pierre Ossman85bcc132005-05-08 19:35:27 +01001795 /*
1796 * See if chip needs to be configured.
1797 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001798 if (pnp) {
1799 if ((host->config != 0) && !wbsd_chip_validate(host)) {
Pierre Ossman85bcc132005-05-08 19:35:27 +01001800 printk(KERN_WARNING DRIVER_NAME
1801 ": PnP active but chip not configured! "
1802 "You probably have a buggy BIOS. "
1803 "Configuring chip manually.\n");
1804 wbsd_chip_config(host);
1805 }
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001806 } else
Pierre Ossman85bcc132005-05-08 19:35:27 +01001807 wbsd_chip_config(host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001808
Pierre Ossman85bcc132005-05-08 19:35:27 +01001809 /*
1810 * Power Management stuff. No idea how this works.
1811 * Not tested.
1812 */
1813#ifdef CONFIG_PM
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001814 if (host->config) {
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001815 wbsd_unlock_config(host);
Pierre Ossman85bcc132005-05-08 19:35:27 +01001816 wbsd_write_config(host, WBSD_CONF_PME, 0xA0);
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001817 wbsd_lock_config(host);
1818 }
Pierre Ossman85bcc132005-05-08 19:35:27 +01001819#endif
1820 /*
1821 * Allow device to initialise itself properly.
1822 */
1823 mdelay(5);
1824
1825 /*
1826 * Reset the chip into a known state.
1827 */
1828 wbsd_init_device(host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001829
Pierre Ossman85bcc132005-05-08 19:35:27 +01001830 mmc_add_host(mmc);
1831
Russell Kingd366b642005-08-19 09:40:08 +01001832 printk(KERN_INFO "%s: W83L51xD", mmc_hostname(mmc));
Pierre Ossman85bcc132005-05-08 19:35:27 +01001833 if (host->chip_id != 0)
1834 printk(" id %x", (int)host->chip_id);
1835 printk(" at 0x%x irq %d", (int)host->base, (int)host->irq);
1836 if (host->dma >= 0)
1837 printk(" dma %d", (int)host->dma);
1838 else
1839 printk(" FIFO");
1840 if (pnp)
1841 printk(" PnP");
1842 printk("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001843
1844 return 0;
1845}
1846
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001847static void __devexit wbsd_shutdown(struct device *dev, int pnp)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001848{
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001849 struct mmc_host *mmc = dev_get_drvdata(dev);
1850 struct wbsd_host *host;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001851
Pierre Ossman85bcc132005-05-08 19:35:27 +01001852 if (!mmc)
1853 return;
1854
1855 host = mmc_priv(mmc);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001856
Pierre Ossman85bcc132005-05-08 19:35:27 +01001857 mmc_remove_host(mmc);
1858
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001859 /*
1860 * Power down the SD/MMC function.
1861 */
Pierre Ossman85bcc132005-05-08 19:35:27 +01001862 if (!pnp)
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001863 wbsd_chip_poweroff(host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001864
Pierre Ossman85bcc132005-05-08 19:35:27 +01001865 wbsd_release_resources(host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001866
Pierre Ossman85bcc132005-05-08 19:35:27 +01001867 wbsd_free_mmc(dev);
1868}
1869
1870/*
1871 * Non-PnP
1872 */
1873
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001874static int __devinit wbsd_probe(struct platform_device *dev)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001875{
Pierre Ossmandd2c6092006-08-07 01:40:04 +02001876 /* Use the module parameters for resources */
Russell King3ae5eae2005-11-09 22:32:44 +00001877 return wbsd_init(&dev->dev, io, irq, dma, 0);
Pierre Ossman85bcc132005-05-08 19:35:27 +01001878}
1879
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001880static int __devexit wbsd_remove(struct platform_device *dev)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001881{
Russell King3ae5eae2005-11-09 22:32:44 +00001882 wbsd_shutdown(&dev->dev, 0);
Pierre Ossman85bcc132005-05-08 19:35:27 +01001883
1884 return 0;
1885}
1886
1887/*
1888 * PnP
1889 */
1890
1891#ifdef CONFIG_PNP
1892
1893static int __devinit
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001894wbsd_pnp_probe(struct pnp_dev *pnpdev, const struct pnp_device_id *dev_id)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001895{
1896 int io, irq, dma;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001897
Pierre Ossman85bcc132005-05-08 19:35:27 +01001898 /*
1899 * Get resources from PnP layer.
1900 */
1901 io = pnp_port_start(pnpdev, 0);
1902 irq = pnp_irq(pnpdev, 0);
1903 if (pnp_dma_valid(pnpdev, 0))
1904 dma = pnp_dma(pnpdev, 0);
1905 else
1906 dma = -1;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001907
Pierre Ossman85bcc132005-05-08 19:35:27 +01001908 DBGF("PnP resources: port %3x irq %d dma %d\n", io, irq, dma);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001909
Pierre Ossman85bcc132005-05-08 19:35:27 +01001910 return wbsd_init(&pnpdev->dev, io, irq, dma, 1);
1911}
1912
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001913static void __devexit wbsd_pnp_remove(struct pnp_dev *dev)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001914{
1915 wbsd_shutdown(&dev->dev, 1);
1916}
1917
1918#endif /* CONFIG_PNP */
1919
Linus Torvalds1da177e2005-04-16 15:20:36 -07001920/*
1921 * Power management
1922 */
1923
1924#ifdef CONFIG_PM
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001925
Pierre Ossman5e68d952006-01-08 14:21:52 +00001926static int wbsd_suspend(struct wbsd_host *host, pm_message_t state)
1927{
1928 BUG_ON(host == NULL);
1929
1930 return mmc_suspend_host(host->mmc, state);
1931}
1932
1933static int wbsd_resume(struct wbsd_host *host)
1934{
1935 BUG_ON(host == NULL);
1936
1937 wbsd_init_device(host);
1938
1939 return mmc_resume_host(host->mmc);
1940}
1941
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001942static int wbsd_platform_suspend(struct platform_device *dev,
1943 pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001944{
Russell King3ae5eae2005-11-09 22:32:44 +00001945 struct mmc_host *mmc = platform_get_drvdata(dev);
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001946 struct wbsd_host *host;
1947 int ret;
1948
Pierre Ossman5e68d952006-01-08 14:21:52 +00001949 if (mmc == NULL)
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001950 return 0;
1951
Pierre Ossman5e68d952006-01-08 14:21:52 +00001952 DBGF("Suspending...\n");
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001953
1954 host = mmc_priv(mmc);
1955
Pierre Ossman5e68d952006-01-08 14:21:52 +00001956 ret = wbsd_suspend(host, state);
1957 if (ret)
1958 return ret;
1959
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001960 wbsd_chip_poweroff(host);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001961
1962 return 0;
1963}
1964
Pierre Ossman5e68d952006-01-08 14:21:52 +00001965static int wbsd_platform_resume(struct platform_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001966{
Russell King3ae5eae2005-11-09 22:32:44 +00001967 struct mmc_host *mmc = platform_get_drvdata(dev);
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001968 struct wbsd_host *host;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001969
Pierre Ossman5e68d952006-01-08 14:21:52 +00001970 if (mmc == NULL)
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001971 return 0;
1972
Pierre Ossman5e68d952006-01-08 14:21:52 +00001973 DBGF("Resuming...\n");
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001974
1975 host = mmc_priv(mmc);
1976
1977 wbsd_chip_config(host);
1978
1979 /*
1980 * Allow device to initialise itself properly.
1981 */
1982 mdelay(5);
1983
Pierre Ossman5e68d952006-01-08 14:21:52 +00001984 return wbsd_resume(host);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001985}
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001986
Pierre Ossman5e68d952006-01-08 14:21:52 +00001987#ifdef CONFIG_PNP
1988
1989static int wbsd_pnp_suspend(struct pnp_dev *pnp_dev, pm_message_t state)
1990{
1991 struct mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev);
1992 struct wbsd_host *host;
1993
1994 if (mmc == NULL)
1995 return 0;
1996
1997 DBGF("Suspending...\n");
1998
1999 host = mmc_priv(mmc);
2000
2001 return wbsd_suspend(host, state);
2002}
2003
2004static int wbsd_pnp_resume(struct pnp_dev *pnp_dev)
2005{
2006 struct mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev);
2007 struct wbsd_host *host;
2008
2009 if (mmc == NULL)
2010 return 0;
2011
2012 DBGF("Resuming...\n");
2013
2014 host = mmc_priv(mmc);
2015
2016 /*
2017 * See if chip needs to be configured.
2018 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +00002019 if (host->config != 0) {
2020 if (!wbsd_chip_validate(host)) {
Pierre Ossman5e68d952006-01-08 14:21:52 +00002021 printk(KERN_WARNING DRIVER_NAME
2022 ": PnP active but chip not configured! "
2023 "You probably have a buggy BIOS. "
2024 "Configuring chip manually.\n");
2025 wbsd_chip_config(host);
2026 }
2027 }
2028
2029 /*
2030 * Allow device to initialise itself properly.
2031 */
2032 mdelay(5);
2033
2034 return wbsd_resume(host);
2035}
2036
2037#endif /* CONFIG_PNP */
2038
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01002039#else /* CONFIG_PM */
2040
Pierre Ossman5e68d952006-01-08 14:21:52 +00002041#define wbsd_platform_suspend NULL
2042#define wbsd_platform_resume NULL
2043
2044#define wbsd_pnp_suspend NULL
2045#define wbsd_pnp_resume NULL
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01002046
2047#endif /* CONFIG_PM */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002048
Pierre Ossman85bcc132005-05-08 19:35:27 +01002049static struct platform_device *wbsd_device;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002050
Russell King3ae5eae2005-11-09 22:32:44 +00002051static struct platform_driver wbsd_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002052 .probe = wbsd_probe,
Pierre Ossman93968d72005-11-09 23:21:06 +00002053 .remove = __devexit_p(wbsd_remove),
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01002054
Pierre Ossman5e68d952006-01-08 14:21:52 +00002055 .suspend = wbsd_platform_suspend,
2056 .resume = wbsd_platform_resume,
Russell King3ae5eae2005-11-09 22:32:44 +00002057 .driver = {
2058 .name = DRIVER_NAME,
2059 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07002060};
2061
Pierre Ossman85bcc132005-05-08 19:35:27 +01002062#ifdef CONFIG_PNP
2063
2064static struct pnp_driver wbsd_pnp_driver = {
2065 .name = DRIVER_NAME,
2066 .id_table = pnp_dev_table,
2067 .probe = wbsd_pnp_probe,
Pierre Ossman93968d72005-11-09 23:21:06 +00002068 .remove = __devexit_p(wbsd_pnp_remove),
Pierre Ossman5e68d952006-01-08 14:21:52 +00002069
2070 .suspend = wbsd_pnp_suspend,
2071 .resume = wbsd_pnp_resume,
Pierre Ossman85bcc132005-05-08 19:35:27 +01002072};
2073
2074#endif /* CONFIG_PNP */
2075
Linus Torvalds1da177e2005-04-16 15:20:36 -07002076/*
2077 * Module loading/unloading
2078 */
2079
2080static int __init wbsd_drv_init(void)
2081{
2082 int result;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01002083
Linus Torvalds1da177e2005-04-16 15:20:36 -07002084 printk(KERN_INFO DRIVER_NAME
2085 ": Winbond W83L51xD SD/MMC card interface driver, "
2086 DRIVER_VERSION "\n");
2087 printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002088
Pierre Ossman85bcc132005-05-08 19:35:27 +01002089#ifdef CONFIG_PNP
2090
Pierre Ossmancfa7f522006-01-08 18:17:55 +00002091 if (!nopnp) {
Pierre Ossman85bcc132005-05-08 19:35:27 +01002092 result = pnp_register_driver(&wbsd_pnp_driver);
2093 if (result < 0)
2094 return result;
2095 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01002096#endif /* CONFIG_PNP */
2097
Pierre Ossmancfa7f522006-01-08 18:17:55 +00002098 if (nopnp) {
Russell King3ae5eae2005-11-09 22:32:44 +00002099 result = platform_driver_register(&wbsd_driver);
Pierre Ossman85bcc132005-05-08 19:35:27 +01002100 if (result < 0)
2101 return result;
2102
Dmitry Torokhov21500bb2006-01-03 22:57:35 +00002103 wbsd_device = platform_device_alloc(DRIVER_NAME, -1);
Pierre Ossmancfa7f522006-01-08 18:17:55 +00002104 if (!wbsd_device) {
Dmitry Torokhov21500bb2006-01-03 22:57:35 +00002105 platform_driver_unregister(&wbsd_driver);
2106 return -ENOMEM;
2107 }
2108
2109 result = platform_device_add(wbsd_device);
Pierre Ossmancfa7f522006-01-08 18:17:55 +00002110 if (result) {
Dmitry Torokhov21500bb2006-01-03 22:57:35 +00002111 platform_device_put(wbsd_device);
2112 platform_driver_unregister(&wbsd_driver);
2113 return result;
2114 }
Pierre Ossman85bcc132005-05-08 19:35:27 +01002115 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002116
2117 return 0;
2118}
2119
2120static void __exit wbsd_drv_exit(void)
2121{
Pierre Ossman85bcc132005-05-08 19:35:27 +01002122#ifdef CONFIG_PNP
2123
2124 if (!nopnp)
2125 pnp_unregister_driver(&wbsd_pnp_driver);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01002126
2127#endif /* CONFIG_PNP */
Pierre Ossman85bcc132005-05-08 19:35:27 +01002128
Pierre Ossmancfa7f522006-01-08 18:17:55 +00002129 if (nopnp) {
Pierre Ossman85bcc132005-05-08 19:35:27 +01002130 platform_device_unregister(wbsd_device);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01002131
Russell King3ae5eae2005-11-09 22:32:44 +00002132 platform_driver_unregister(&wbsd_driver);
Pierre Ossman85bcc132005-05-08 19:35:27 +01002133 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002134
2135 DBG("unloaded\n");
2136}
2137
2138module_init(wbsd_drv_init);
2139module_exit(wbsd_drv_exit);
Pierre Ossman85bcc132005-05-08 19:35:27 +01002140#ifdef CONFIG_PNP
2141module_param(nopnp, uint, 0444);
2142#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07002143module_param(io, uint, 0444);
2144module_param(irq, uint, 0444);
2145module_param(dma, int, 0444);
2146
2147MODULE_LICENSE("GPL");
Pierre Ossmande1d09e2005-11-09 23:21:49 +00002148MODULE_AUTHOR("Pierre Ossman <drzeus@drzeus.cx>");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002149MODULE_DESCRIPTION("Winbond W83L51xD SD/MMC card interface driver");
2150MODULE_VERSION(DRIVER_VERSION);
2151
Pierre Ossman85bcc132005-05-08 19:35:27 +01002152#ifdef CONFIG_PNP
2153MODULE_PARM_DESC(nopnp, "Scan for device instead of relying on PNP. (default 0)");
2154#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07002155MODULE_PARM_DESC(io, "I/O base to allocate. Must be 8 byte aligned. (default 0x248)");
2156MODULE_PARM_DESC(irq, "IRQ to allocate. (default 6)");
2157MODULE_PARM_DESC(dma, "DMA channel to allocate. -1 for no DMA. (default 2)");