blob: 682e62b0b09db65d0539742f3f7c804dc0aaba82 [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
Pierre Ossman85bcc132005-05-08 19:35:27 +01001024static struct mmc_host_ops wbsd_ops = {
1025 .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 /*
1346 * Maximum number of sectors in one transfer. Also limited by 64kB
1347 * buffer.
1348 */
1349 mmc->max_sectors = 128;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001350
Pierre Ossman85bcc132005-05-08 19:35:27 +01001351 /*
1352 * Maximum segment size. Could be one segment with the maximum number
1353 * of segments.
1354 */
1355 mmc->max_seg_size = mmc->max_sectors * 512;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001356
Pierre Ossman85bcc132005-05-08 19:35:27 +01001357 dev_set_drvdata(dev, mmc);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001358
Pierre Ossman85bcc132005-05-08 19:35:27 +01001359 return 0;
1360}
1361
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001362static void __devexit wbsd_free_mmc(struct device *dev)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001363{
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001364 struct mmc_host *mmc;
1365 struct wbsd_host *host;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001366
Pierre Ossman85bcc132005-05-08 19:35:27 +01001367 mmc = dev_get_drvdata(dev);
1368 if (!mmc)
1369 return;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001370
Pierre Ossman6e6293d2005-07-01 12:13:55 +01001371 host = mmc_priv(mmc);
1372 BUG_ON(host == NULL);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001373
Pierre Ossman1656fa52005-09-03 16:45:49 +01001374 del_timer_sync(&host->ignore_timer);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001375
Pierre Ossman85bcc132005-05-08 19:35:27 +01001376 mmc_free_host(mmc);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001377
Pierre Ossman85bcc132005-05-08 19:35:27 +01001378 dev_set_drvdata(dev, NULL);
1379}
1380
1381/*
1382 * Scan for known chip id:s
1383 */
1384
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001385static int __devinit wbsd_scan(struct wbsd_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001386{
1387 int i, j, k;
1388 int id;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001389
Linus Torvalds1da177e2005-04-16 15:20:36 -07001390 /*
1391 * Iterate through all ports, all codes to
1392 * find hardware that is in our known list.
1393 */
Dmitry Torokhov63648fb2006-01-03 22:56:56 +00001394 for (i = 0; i < ARRAY_SIZE(config_ports); i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395 if (!request_region(config_ports[i], 2, DRIVER_NAME))
1396 continue;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001397
Dmitry Torokhov63648fb2006-01-03 22:56:56 +00001398 for (j = 0; j < ARRAY_SIZE(unlock_codes); j++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001399 id = 0xFFFF;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001400
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001401 host->config = config_ports[i];
1402 host->unlock_code = unlock_codes[j];
1403
1404 wbsd_unlock_config(host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001405
Linus Torvalds1da177e2005-04-16 15:20:36 -07001406 outb(WBSD_CONF_ID_HI, config_ports[i]);
1407 id = inb(config_ports[i] + 1) << 8;
1408
1409 outb(WBSD_CONF_ID_LO, config_ports[i]);
1410 id |= inb(config_ports[i] + 1);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001411
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001412 wbsd_lock_config(host);
1413
Dmitry Torokhov63648fb2006-01-03 22:56:56 +00001414 for (k = 0; k < ARRAY_SIZE(valid_ids); k++) {
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001415 if (id == valid_ids[k]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416 host->chip_id = id;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001417
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418 return 0;
1419 }
1420 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001421
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001422 if (id != 0xFFFF) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423 DBG("Unknown hardware (id %x) found at %x\n",
1424 id, config_ports[i]);
1425 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001426 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001427
Linus Torvalds1da177e2005-04-16 15:20:36 -07001428 release_region(config_ports[i], 2);
1429 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001430
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001431 host->config = 0;
1432 host->unlock_code = 0;
1433
Linus Torvalds1da177e2005-04-16 15:20:36 -07001434 return -ENODEV;
1435}
1436
Pierre Ossman85bcc132005-05-08 19:35:27 +01001437/*
1438 * Allocate/free io port ranges
1439 */
1440
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001441static int __devinit wbsd_request_region(struct wbsd_host *host, int base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442{
Pierre Ossman916f3ac2006-08-06 22:22:23 +02001443 if (base & 0x7)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444 return -EINVAL;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001445
Pierre Ossman85bcc132005-05-08 19:35:27 +01001446 if (!request_region(base, 8, DRIVER_NAME))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447 return -EIO;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001448
Pierre Ossman916f3ac2006-08-06 22:22:23 +02001449 host->base = base;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001450
Linus Torvalds1da177e2005-04-16 15:20:36 -07001451 return 0;
1452}
1453
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001454static void __devexit wbsd_release_regions(struct wbsd_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455{
1456 if (host->base)
1457 release_region(host->base, 8);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001458
Pierre Ossman85bcc132005-05-08 19:35:27 +01001459 host->base = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001460
1461 if (host->config)
1462 release_region(host->config, 2);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001463
Pierre Ossman85bcc132005-05-08 19:35:27 +01001464 host->config = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001465}
1466
Pierre Ossman85bcc132005-05-08 19:35:27 +01001467/*
1468 * Allocate/free DMA port and buffer
1469 */
1470
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001471static void __devinit wbsd_request_dma(struct wbsd_host *host, int dma)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001472{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001473 if (dma < 0)
1474 return;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001475
Linus Torvalds1da177e2005-04-16 15:20:36 -07001476 if (request_dma(dma, DRIVER_NAME))
1477 goto err;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001478
Linus Torvalds1da177e2005-04-16 15:20:36 -07001479 /*
1480 * We need to allocate a special buffer in
1481 * order for ISA to be able to DMA to it.
1482 */
Pierre Ossman85bcc132005-05-08 19:35:27 +01001483 host->dma_buffer = kmalloc(WBSD_DMA_SIZE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001484 GFP_NOIO | GFP_DMA | __GFP_REPEAT | __GFP_NOWARN);
1485 if (!host->dma_buffer)
1486 goto free;
1487
1488 /*
1489 * Translate the address to a physical address.
1490 */
Greg Kroah-Hartmanfcaf71f2006-09-12 17:00:10 +02001491 host->dma_addr = dma_map_single(mmc_dev(host->mmc), host->dma_buffer,
Pierre Ossman85bcc132005-05-08 19:35:27 +01001492 WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001493
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494 /*
1495 * ISA DMA must be aligned on a 64k basis.
1496 */
1497 if ((host->dma_addr & 0xffff) != 0)
1498 goto kfree;
1499 /*
1500 * ISA cannot access memory above 16 MB.
1501 */
1502 else if (host->dma_addr >= 0x1000000)
1503 goto kfree;
1504
1505 host->dma = dma;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001506
Linus Torvalds1da177e2005-04-16 15:20:36 -07001507 return;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001508
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509kfree:
1510 /*
1511 * If we've gotten here then there is some kind of alignment bug
1512 */
1513 BUG_ON(1);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001514
Greg Kroah-Hartmanfcaf71f2006-09-12 17:00:10 +02001515 dma_unmap_single(mmc_dev(host->mmc), host->dma_addr,
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001516 WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
Pierre Ossman85bcc132005-05-08 19:35:27 +01001517 host->dma_addr = (dma_addr_t)NULL;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001518
Linus Torvalds1da177e2005-04-16 15:20:36 -07001519 kfree(host->dma_buffer);
1520 host->dma_buffer = NULL;
1521
1522free:
1523 free_dma(dma);
1524
1525err:
1526 printk(KERN_WARNING DRIVER_NAME ": Unable to allocate DMA %d. "
1527 "Falling back on FIFO.\n", dma);
1528}
1529
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001530static void __devexit wbsd_release_dma(struct wbsd_host *host)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001531{
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001532 if (host->dma_addr) {
Greg Kroah-Hartmanfcaf71f2006-09-12 17:00:10 +02001533 dma_unmap_single(mmc_dev(host->mmc), host->dma_addr,
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001534 WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
1535 }
Jesper Juhl6044ec82005-11-07 01:01:32 -08001536 kfree(host->dma_buffer);
Pierre Ossman85bcc132005-05-08 19:35:27 +01001537 if (host->dma >= 0)
1538 free_dma(host->dma);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001539
Pierre Ossman85bcc132005-05-08 19:35:27 +01001540 host->dma = -1;
1541 host->dma_buffer = NULL;
1542 host->dma_addr = (dma_addr_t)NULL;
1543}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544
1545/*
Pierre Ossman85bcc132005-05-08 19:35:27 +01001546 * Allocate/free IRQ.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547 */
1548
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001549static int __devinit wbsd_request_irq(struct wbsd_host *host, int irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001550{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551 int ret;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001552
Linus Torvalds1da177e2005-04-16 15:20:36 -07001553 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554 * Allocate interrupt.
1555 */
Pierre Ossman85bcc132005-05-08 19:35:27 +01001556
Thomas Gleixnerdace1452006-07-01 19:29:38 -07001557 ret = request_irq(irq, wbsd_irq, IRQF_SHARED, DRIVER_NAME, host);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001558 if (ret)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001559 return ret;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001560
Linus Torvalds1da177e2005-04-16 15:20:36 -07001561 host->irq = irq;
Pierre Ossman85bcc132005-05-08 19:35:27 +01001562
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563 /*
1564 * Set up tasklets.
1565 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001566 tasklet_init(&host->card_tasklet, wbsd_tasklet_card,
1567 (unsigned long)host);
1568 tasklet_init(&host->fifo_tasklet, wbsd_tasklet_fifo,
1569 (unsigned long)host);
1570 tasklet_init(&host->crc_tasklet, wbsd_tasklet_crc,
1571 (unsigned long)host);
1572 tasklet_init(&host->timeout_tasklet, wbsd_tasklet_timeout,
1573 (unsigned long)host);
1574 tasklet_init(&host->finish_tasklet, wbsd_tasklet_finish,
1575 (unsigned long)host);
1576 tasklet_init(&host->block_tasklet, wbsd_tasklet_block,
1577 (unsigned long)host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001578
Linus Torvalds1da177e2005-04-16 15:20:36 -07001579 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001580}
1581
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001582static void __devexit wbsd_release_irq(struct wbsd_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583{
Pierre Ossman85bcc132005-05-08 19:35:27 +01001584 if (!host->irq)
1585 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001586
1587 free_irq(host->irq, host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001588
Pierre Ossman85bcc132005-05-08 19:35:27 +01001589 host->irq = 0;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001590
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591 tasklet_kill(&host->card_tasklet);
1592 tasklet_kill(&host->fifo_tasklet);
1593 tasklet_kill(&host->crc_tasklet);
1594 tasklet_kill(&host->timeout_tasklet);
1595 tasklet_kill(&host->finish_tasklet);
1596 tasklet_kill(&host->block_tasklet);
Pierre Ossman85bcc132005-05-08 19:35:27 +01001597}
1598
1599/*
1600 * Allocate all resources for the host.
1601 */
1602
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001603static int __devinit wbsd_request_resources(struct wbsd_host *host,
Pierre Ossman85bcc132005-05-08 19:35:27 +01001604 int base, int irq, int dma)
1605{
1606 int ret;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001607
Pierre Ossman85bcc132005-05-08 19:35:27 +01001608 /*
1609 * Allocate I/O ports.
1610 */
1611 ret = wbsd_request_region(host, base);
1612 if (ret)
1613 return ret;
1614
1615 /*
1616 * Allocate interrupt.
1617 */
1618 ret = wbsd_request_irq(host, irq);
1619 if (ret)
1620 return ret;
1621
1622 /*
1623 * Allocate DMA.
1624 */
1625 wbsd_request_dma(host, dma);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001626
Pierre Ossman85bcc132005-05-08 19:35:27 +01001627 return 0;
1628}
1629
1630/*
1631 * Release all resources for the host.
1632 */
1633
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001634static void __devexit wbsd_release_resources(struct wbsd_host *host)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001635{
1636 wbsd_release_dma(host);
1637 wbsd_release_irq(host);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001638 wbsd_release_regions(host);
Pierre Ossman85bcc132005-05-08 19:35:27 +01001639}
1640
1641/*
1642 * Configure the resources the chip should use.
1643 */
1644
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001645static void wbsd_chip_config(struct wbsd_host *host)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001646{
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001647 wbsd_unlock_config(host);
1648
Pierre Ossman85bcc132005-05-08 19:35:27 +01001649 /*
1650 * Reset the chip.
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001651 */
Pierre Ossman85bcc132005-05-08 19:35:27 +01001652 wbsd_write_config(host, WBSD_CONF_SWRST, 1);
1653 wbsd_write_config(host, WBSD_CONF_SWRST, 0);
1654
1655 /*
1656 * Select SD/MMC function.
1657 */
1658 wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001659
Pierre Ossman85bcc132005-05-08 19:35:27 +01001660 /*
1661 * Set up card detection.
1662 */
1663 wbsd_write_config(host, WBSD_CONF_PINS, WBSD_PINS_DETECT_GP11);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001664
Pierre Ossman85bcc132005-05-08 19:35:27 +01001665 /*
1666 * Configure chip
1667 */
1668 wbsd_write_config(host, WBSD_CONF_PORT_HI, host->base >> 8);
1669 wbsd_write_config(host, WBSD_CONF_PORT_LO, host->base & 0xff);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001670
Pierre Ossman85bcc132005-05-08 19:35:27 +01001671 wbsd_write_config(host, WBSD_CONF_IRQ, host->irq);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001672
Pierre Ossman85bcc132005-05-08 19:35:27 +01001673 if (host->dma >= 0)
1674 wbsd_write_config(host, WBSD_CONF_DRQ, host->dma);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001675
Pierre Ossman85bcc132005-05-08 19:35:27 +01001676 /*
1677 * Enable and power up chip.
1678 */
1679 wbsd_write_config(host, WBSD_CONF_ENABLE, 1);
1680 wbsd_write_config(host, WBSD_CONF_POWER, 0x20);
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001681
1682 wbsd_lock_config(host);
Pierre Ossman85bcc132005-05-08 19:35:27 +01001683}
1684
1685/*
1686 * Check that configured resources are correct.
1687 */
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001688
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001689static int wbsd_chip_validate(struct wbsd_host *host)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001690{
1691 int base, irq, dma;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001692
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001693 wbsd_unlock_config(host);
1694
Pierre Ossman85bcc132005-05-08 19:35:27 +01001695 /*
1696 * Select SD/MMC function.
1697 */
1698 wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001699
Pierre Ossman85bcc132005-05-08 19:35:27 +01001700 /*
1701 * Read configuration.
1702 */
1703 base = wbsd_read_config(host, WBSD_CONF_PORT_HI) << 8;
1704 base |= wbsd_read_config(host, WBSD_CONF_PORT_LO);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001705
Pierre Ossman85bcc132005-05-08 19:35:27 +01001706 irq = wbsd_read_config(host, WBSD_CONF_IRQ);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001707
Pierre Ossman85bcc132005-05-08 19:35:27 +01001708 dma = wbsd_read_config(host, WBSD_CONF_DRQ);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001709
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001710 wbsd_lock_config(host);
1711
Pierre Ossman85bcc132005-05-08 19:35:27 +01001712 /*
1713 * Validate against given configuration.
1714 */
1715 if (base != host->base)
1716 return 0;
1717 if (irq != host->irq)
1718 return 0;
1719 if ((dma != host->dma) && (host->dma != -1))
1720 return 0;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001721
Pierre Ossman85bcc132005-05-08 19:35:27 +01001722 return 1;
1723}
1724
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001725/*
1726 * Powers down the SD function
1727 */
1728
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001729static void wbsd_chip_poweroff(struct wbsd_host *host)
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001730{
1731 wbsd_unlock_config(host);
1732
1733 wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
1734 wbsd_write_config(host, WBSD_CONF_ENABLE, 0);
1735
1736 wbsd_lock_config(host);
1737}
1738
Pierre Ossman85bcc132005-05-08 19:35:27 +01001739/*****************************************************************************\
1740 * *
1741 * Devices setup and shutdown *
1742 * *
1743\*****************************************************************************/
1744
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001745static int __devinit wbsd_init(struct device *dev, int base, int irq, int dma,
Pierre Ossman85bcc132005-05-08 19:35:27 +01001746 int pnp)
1747{
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001748 struct wbsd_host *host = NULL;
1749 struct mmc_host *mmc = NULL;
Pierre Ossman85bcc132005-05-08 19:35:27 +01001750 int ret;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001751
Pierre Ossman85bcc132005-05-08 19:35:27 +01001752 ret = wbsd_alloc_mmc(dev);
1753 if (ret)
1754 return ret;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001755
Pierre Ossman85bcc132005-05-08 19:35:27 +01001756 mmc = dev_get_drvdata(dev);
1757 host = mmc_priv(mmc);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001758
Pierre Ossman85bcc132005-05-08 19:35:27 +01001759 /*
1760 * Scan for hardware.
1761 */
1762 ret = wbsd_scan(host);
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001763 if (ret) {
1764 if (pnp && (ret == -ENODEV)) {
Pierre Ossman85bcc132005-05-08 19:35:27 +01001765 printk(KERN_WARNING DRIVER_NAME
1766 ": Unable to confirm device presence. You may "
1767 "experience lock-ups.\n");
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001768 } else {
Pierre Ossman85bcc132005-05-08 19:35:27 +01001769 wbsd_free_mmc(dev);
1770 return ret;
1771 }
1772 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001773
Pierre Ossman85bcc132005-05-08 19:35:27 +01001774 /*
1775 * Request resources.
1776 */
Pierre Ossmandd2c6092006-08-07 01:40:04 +02001777 ret = wbsd_request_resources(host, base, irq, dma);
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001778 if (ret) {
Pierre Ossman85bcc132005-05-08 19:35:27 +01001779 wbsd_release_resources(host);
1780 wbsd_free_mmc(dev);
1781 return ret;
1782 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001783
Pierre Ossman85bcc132005-05-08 19:35:27 +01001784 /*
1785 * See if chip needs to be configured.
1786 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001787 if (pnp) {
1788 if ((host->config != 0) && !wbsd_chip_validate(host)) {
Pierre Ossman85bcc132005-05-08 19:35:27 +01001789 printk(KERN_WARNING DRIVER_NAME
1790 ": PnP active but chip not configured! "
1791 "You probably have a buggy BIOS. "
1792 "Configuring chip manually.\n");
1793 wbsd_chip_config(host);
1794 }
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001795 } else
Pierre Ossman85bcc132005-05-08 19:35:27 +01001796 wbsd_chip_config(host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001797
Pierre Ossman85bcc132005-05-08 19:35:27 +01001798 /*
1799 * Power Management stuff. No idea how this works.
1800 * Not tested.
1801 */
1802#ifdef CONFIG_PM
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001803 if (host->config) {
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001804 wbsd_unlock_config(host);
Pierre Ossman85bcc132005-05-08 19:35:27 +01001805 wbsd_write_config(host, WBSD_CONF_PME, 0xA0);
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001806 wbsd_lock_config(host);
1807 }
Pierre Ossman85bcc132005-05-08 19:35:27 +01001808#endif
1809 /*
1810 * Allow device to initialise itself properly.
1811 */
1812 mdelay(5);
1813
1814 /*
1815 * Reset the chip into a known state.
1816 */
1817 wbsd_init_device(host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001818
Pierre Ossman85bcc132005-05-08 19:35:27 +01001819 mmc_add_host(mmc);
1820
Russell Kingd366b642005-08-19 09:40:08 +01001821 printk(KERN_INFO "%s: W83L51xD", mmc_hostname(mmc));
Pierre Ossman85bcc132005-05-08 19:35:27 +01001822 if (host->chip_id != 0)
1823 printk(" id %x", (int)host->chip_id);
1824 printk(" at 0x%x irq %d", (int)host->base, (int)host->irq);
1825 if (host->dma >= 0)
1826 printk(" dma %d", (int)host->dma);
1827 else
1828 printk(" FIFO");
1829 if (pnp)
1830 printk(" PnP");
1831 printk("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001832
1833 return 0;
1834}
1835
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001836static void __devexit wbsd_shutdown(struct device *dev, int pnp)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001837{
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001838 struct mmc_host *mmc = dev_get_drvdata(dev);
1839 struct wbsd_host *host;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001840
Pierre Ossman85bcc132005-05-08 19:35:27 +01001841 if (!mmc)
1842 return;
1843
1844 host = mmc_priv(mmc);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001845
Pierre Ossman85bcc132005-05-08 19:35:27 +01001846 mmc_remove_host(mmc);
1847
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001848 /*
1849 * Power down the SD/MMC function.
1850 */
Pierre Ossman85bcc132005-05-08 19:35:27 +01001851 if (!pnp)
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001852 wbsd_chip_poweroff(host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001853
Pierre Ossman85bcc132005-05-08 19:35:27 +01001854 wbsd_release_resources(host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001855
Pierre Ossman85bcc132005-05-08 19:35:27 +01001856 wbsd_free_mmc(dev);
1857}
1858
1859/*
1860 * Non-PnP
1861 */
1862
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001863static int __devinit wbsd_probe(struct platform_device *dev)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001864{
Pierre Ossmandd2c6092006-08-07 01:40:04 +02001865 /* Use the module parameters for resources */
Russell King3ae5eae2005-11-09 22:32:44 +00001866 return wbsd_init(&dev->dev, io, irq, dma, 0);
Pierre Ossman85bcc132005-05-08 19:35:27 +01001867}
1868
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001869static int __devexit wbsd_remove(struct platform_device *dev)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001870{
Russell King3ae5eae2005-11-09 22:32:44 +00001871 wbsd_shutdown(&dev->dev, 0);
Pierre Ossman85bcc132005-05-08 19:35:27 +01001872
1873 return 0;
1874}
1875
1876/*
1877 * PnP
1878 */
1879
1880#ifdef CONFIG_PNP
1881
1882static int __devinit
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001883wbsd_pnp_probe(struct pnp_dev *pnpdev, const struct pnp_device_id *dev_id)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001884{
1885 int io, irq, dma;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001886
Pierre Ossman85bcc132005-05-08 19:35:27 +01001887 /*
1888 * Get resources from PnP layer.
1889 */
1890 io = pnp_port_start(pnpdev, 0);
1891 irq = pnp_irq(pnpdev, 0);
1892 if (pnp_dma_valid(pnpdev, 0))
1893 dma = pnp_dma(pnpdev, 0);
1894 else
1895 dma = -1;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001896
Pierre Ossman85bcc132005-05-08 19:35:27 +01001897 DBGF("PnP resources: port %3x irq %d dma %d\n", io, irq, dma);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001898
Pierre Ossman85bcc132005-05-08 19:35:27 +01001899 return wbsd_init(&pnpdev->dev, io, irq, dma, 1);
1900}
1901
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001902static void __devexit wbsd_pnp_remove(struct pnp_dev *dev)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001903{
1904 wbsd_shutdown(&dev->dev, 1);
1905}
1906
1907#endif /* CONFIG_PNP */
1908
Linus Torvalds1da177e2005-04-16 15:20:36 -07001909/*
1910 * Power management
1911 */
1912
1913#ifdef CONFIG_PM
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001914
Pierre Ossman5e68d952006-01-08 14:21:52 +00001915static int wbsd_suspend(struct wbsd_host *host, pm_message_t state)
1916{
1917 BUG_ON(host == NULL);
1918
1919 return mmc_suspend_host(host->mmc, state);
1920}
1921
1922static int wbsd_resume(struct wbsd_host *host)
1923{
1924 BUG_ON(host == NULL);
1925
1926 wbsd_init_device(host);
1927
1928 return mmc_resume_host(host->mmc);
1929}
1930
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001931static int wbsd_platform_suspend(struct platform_device *dev,
1932 pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933{
Russell King3ae5eae2005-11-09 22:32:44 +00001934 struct mmc_host *mmc = platform_get_drvdata(dev);
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001935 struct wbsd_host *host;
1936 int ret;
1937
Pierre Ossman5e68d952006-01-08 14:21:52 +00001938 if (mmc == NULL)
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001939 return 0;
1940
Pierre Ossman5e68d952006-01-08 14:21:52 +00001941 DBGF("Suspending...\n");
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001942
1943 host = mmc_priv(mmc);
1944
Pierre Ossman5e68d952006-01-08 14:21:52 +00001945 ret = wbsd_suspend(host, state);
1946 if (ret)
1947 return ret;
1948
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001949 wbsd_chip_poweroff(host);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001950
1951 return 0;
1952}
1953
Pierre Ossman5e68d952006-01-08 14:21:52 +00001954static int wbsd_platform_resume(struct platform_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001955{
Russell King3ae5eae2005-11-09 22:32:44 +00001956 struct mmc_host *mmc = platform_get_drvdata(dev);
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001957 struct wbsd_host *host;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001958
Pierre Ossman5e68d952006-01-08 14:21:52 +00001959 if (mmc == NULL)
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001960 return 0;
1961
Pierre Ossman5e68d952006-01-08 14:21:52 +00001962 DBGF("Resuming...\n");
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001963
1964 host = mmc_priv(mmc);
1965
1966 wbsd_chip_config(host);
1967
1968 /*
1969 * Allow device to initialise itself properly.
1970 */
1971 mdelay(5);
1972
Pierre Ossman5e68d952006-01-08 14:21:52 +00001973 return wbsd_resume(host);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001974}
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001975
Pierre Ossman5e68d952006-01-08 14:21:52 +00001976#ifdef CONFIG_PNP
1977
1978static int wbsd_pnp_suspend(struct pnp_dev *pnp_dev, pm_message_t state)
1979{
1980 struct mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev);
1981 struct wbsd_host *host;
1982
1983 if (mmc == NULL)
1984 return 0;
1985
1986 DBGF("Suspending...\n");
1987
1988 host = mmc_priv(mmc);
1989
1990 return wbsd_suspend(host, state);
1991}
1992
1993static int wbsd_pnp_resume(struct pnp_dev *pnp_dev)
1994{
1995 struct mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev);
1996 struct wbsd_host *host;
1997
1998 if (mmc == NULL)
1999 return 0;
2000
2001 DBGF("Resuming...\n");
2002
2003 host = mmc_priv(mmc);
2004
2005 /*
2006 * See if chip needs to be configured.
2007 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +00002008 if (host->config != 0) {
2009 if (!wbsd_chip_validate(host)) {
Pierre Ossman5e68d952006-01-08 14:21:52 +00002010 printk(KERN_WARNING DRIVER_NAME
2011 ": PnP active but chip not configured! "
2012 "You probably have a buggy BIOS. "
2013 "Configuring chip manually.\n");
2014 wbsd_chip_config(host);
2015 }
2016 }
2017
2018 /*
2019 * Allow device to initialise itself properly.
2020 */
2021 mdelay(5);
2022
2023 return wbsd_resume(host);
2024}
2025
2026#endif /* CONFIG_PNP */
2027
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01002028#else /* CONFIG_PM */
2029
Pierre Ossman5e68d952006-01-08 14:21:52 +00002030#define wbsd_platform_suspend NULL
2031#define wbsd_platform_resume NULL
2032
2033#define wbsd_pnp_suspend NULL
2034#define wbsd_pnp_resume NULL
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01002035
2036#endif /* CONFIG_PM */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002037
Pierre Ossman85bcc132005-05-08 19:35:27 +01002038static struct platform_device *wbsd_device;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002039
Russell King3ae5eae2005-11-09 22:32:44 +00002040static struct platform_driver wbsd_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002041 .probe = wbsd_probe,
Pierre Ossman93968d72005-11-09 23:21:06 +00002042 .remove = __devexit_p(wbsd_remove),
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01002043
Pierre Ossman5e68d952006-01-08 14:21:52 +00002044 .suspend = wbsd_platform_suspend,
2045 .resume = wbsd_platform_resume,
Russell King3ae5eae2005-11-09 22:32:44 +00002046 .driver = {
2047 .name = DRIVER_NAME,
2048 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07002049};
2050
Pierre Ossman85bcc132005-05-08 19:35:27 +01002051#ifdef CONFIG_PNP
2052
2053static struct pnp_driver wbsd_pnp_driver = {
2054 .name = DRIVER_NAME,
2055 .id_table = pnp_dev_table,
2056 .probe = wbsd_pnp_probe,
Pierre Ossman93968d72005-11-09 23:21:06 +00002057 .remove = __devexit_p(wbsd_pnp_remove),
Pierre Ossman5e68d952006-01-08 14:21:52 +00002058
2059 .suspend = wbsd_pnp_suspend,
2060 .resume = wbsd_pnp_resume,
Pierre Ossman85bcc132005-05-08 19:35:27 +01002061};
2062
2063#endif /* CONFIG_PNP */
2064
Linus Torvalds1da177e2005-04-16 15:20:36 -07002065/*
2066 * Module loading/unloading
2067 */
2068
2069static int __init wbsd_drv_init(void)
2070{
2071 int result;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01002072
Linus Torvalds1da177e2005-04-16 15:20:36 -07002073 printk(KERN_INFO DRIVER_NAME
2074 ": Winbond W83L51xD SD/MMC card interface driver, "
2075 DRIVER_VERSION "\n");
2076 printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002077
Pierre Ossman85bcc132005-05-08 19:35:27 +01002078#ifdef CONFIG_PNP
2079
Pierre Ossmancfa7f522006-01-08 18:17:55 +00002080 if (!nopnp) {
Pierre Ossman85bcc132005-05-08 19:35:27 +01002081 result = pnp_register_driver(&wbsd_pnp_driver);
2082 if (result < 0)
2083 return result;
2084 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01002085#endif /* CONFIG_PNP */
2086
Pierre Ossmancfa7f522006-01-08 18:17:55 +00002087 if (nopnp) {
Russell King3ae5eae2005-11-09 22:32:44 +00002088 result = platform_driver_register(&wbsd_driver);
Pierre Ossman85bcc132005-05-08 19:35:27 +01002089 if (result < 0)
2090 return result;
2091
Dmitry Torokhov21500bb2006-01-03 22:57:35 +00002092 wbsd_device = platform_device_alloc(DRIVER_NAME, -1);
Pierre Ossmancfa7f522006-01-08 18:17:55 +00002093 if (!wbsd_device) {
Dmitry Torokhov21500bb2006-01-03 22:57:35 +00002094 platform_driver_unregister(&wbsd_driver);
2095 return -ENOMEM;
2096 }
2097
2098 result = platform_device_add(wbsd_device);
Pierre Ossmancfa7f522006-01-08 18:17:55 +00002099 if (result) {
Dmitry Torokhov21500bb2006-01-03 22:57:35 +00002100 platform_device_put(wbsd_device);
2101 platform_driver_unregister(&wbsd_driver);
2102 return result;
2103 }
Pierre Ossman85bcc132005-05-08 19:35:27 +01002104 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002105
2106 return 0;
2107}
2108
2109static void __exit wbsd_drv_exit(void)
2110{
Pierre Ossman85bcc132005-05-08 19:35:27 +01002111#ifdef CONFIG_PNP
2112
2113 if (!nopnp)
2114 pnp_unregister_driver(&wbsd_pnp_driver);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01002115
2116#endif /* CONFIG_PNP */
Pierre Ossman85bcc132005-05-08 19:35:27 +01002117
Pierre Ossmancfa7f522006-01-08 18:17:55 +00002118 if (nopnp) {
Pierre Ossman85bcc132005-05-08 19:35:27 +01002119 platform_device_unregister(wbsd_device);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01002120
Russell King3ae5eae2005-11-09 22:32:44 +00002121 platform_driver_unregister(&wbsd_driver);
Pierre Ossman85bcc132005-05-08 19:35:27 +01002122 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002123
2124 DBG("unloaded\n");
2125}
2126
2127module_init(wbsd_drv_init);
2128module_exit(wbsd_drv_exit);
Pierre Ossman85bcc132005-05-08 19:35:27 +01002129#ifdef CONFIG_PNP
2130module_param(nopnp, uint, 0444);
2131#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07002132module_param(io, uint, 0444);
2133module_param(irq, uint, 0444);
2134module_param(dma, int, 0444);
2135
2136MODULE_LICENSE("GPL");
Pierre Ossmande1d09e2005-11-09 23:21:49 +00002137MODULE_AUTHOR("Pierre Ossman <drzeus@drzeus.cx>");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002138MODULE_DESCRIPTION("Winbond W83L51xD SD/MMC card interface driver");
2139MODULE_VERSION(DRIVER_VERSION);
2140
Pierre Ossman85bcc132005-05-08 19:35:27 +01002141#ifdef CONFIG_PNP
2142MODULE_PARM_DESC(nopnp, "Scan for device instead of relying on PNP. (default 0)");
2143#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07002144MODULE_PARM_DESC(io, "I/O base to allocate. Must be 8 byte aligned. (default 0x248)");
2145MODULE_PARM_DESC(irq, "IRQ to allocate. (default 6)");
2146MODULE_PARM_DESC(dma, "DMA channel to allocate. -1 for no DMA. (default 2)");