blob: 5711beecb4e8cdb193a68b2fdfc1247c5c86c09d [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 /*
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 Ossmanfe4a3c72006-11-21 17:54:23 +01001357 /*
1358 * Maximum block size. We have 12 bits (= 4095) but have to subtract
1359 * space for CRC. So the maximum is 4095 - 4*2 = 4087.
1360 */
1361 mmc->max_blk_size = 4087;
1362
Pierre Ossman85bcc132005-05-08 19:35:27 +01001363 dev_set_drvdata(dev, mmc);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001364
Pierre Ossman85bcc132005-05-08 19:35:27 +01001365 return 0;
1366}
1367
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001368static void __devexit wbsd_free_mmc(struct device *dev)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001369{
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001370 struct mmc_host *mmc;
1371 struct wbsd_host *host;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001372
Pierre Ossman85bcc132005-05-08 19:35:27 +01001373 mmc = dev_get_drvdata(dev);
1374 if (!mmc)
1375 return;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001376
Pierre Ossman6e6293d2005-07-01 12:13:55 +01001377 host = mmc_priv(mmc);
1378 BUG_ON(host == NULL);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001379
Pierre Ossman1656fa52005-09-03 16:45:49 +01001380 del_timer_sync(&host->ignore_timer);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001381
Pierre Ossman85bcc132005-05-08 19:35:27 +01001382 mmc_free_host(mmc);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001383
Pierre Ossman85bcc132005-05-08 19:35:27 +01001384 dev_set_drvdata(dev, NULL);
1385}
1386
1387/*
1388 * Scan for known chip id:s
1389 */
1390
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001391static int __devinit wbsd_scan(struct wbsd_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392{
1393 int i, j, k;
1394 int id;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001395
Linus Torvalds1da177e2005-04-16 15:20:36 -07001396 /*
1397 * Iterate through all ports, all codes to
1398 * find hardware that is in our known list.
1399 */
Dmitry Torokhov63648fb2006-01-03 22:56:56 +00001400 for (i = 0; i < ARRAY_SIZE(config_ports); i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401 if (!request_region(config_ports[i], 2, DRIVER_NAME))
1402 continue;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001403
Dmitry Torokhov63648fb2006-01-03 22:56:56 +00001404 for (j = 0; j < ARRAY_SIZE(unlock_codes); j++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001405 id = 0xFFFF;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001406
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001407 host->config = config_ports[i];
1408 host->unlock_code = unlock_codes[j];
1409
1410 wbsd_unlock_config(host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001411
Linus Torvalds1da177e2005-04-16 15:20:36 -07001412 outb(WBSD_CONF_ID_HI, config_ports[i]);
1413 id = inb(config_ports[i] + 1) << 8;
1414
1415 outb(WBSD_CONF_ID_LO, config_ports[i]);
1416 id |= inb(config_ports[i] + 1);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001417
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001418 wbsd_lock_config(host);
1419
Dmitry Torokhov63648fb2006-01-03 22:56:56 +00001420 for (k = 0; k < ARRAY_SIZE(valid_ids); k++) {
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001421 if (id == valid_ids[k]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001422 host->chip_id = id;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001423
Linus Torvalds1da177e2005-04-16 15:20:36 -07001424 return 0;
1425 }
1426 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001427
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001428 if (id != 0xFFFF) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429 DBG("Unknown hardware (id %x) found at %x\n",
1430 id, config_ports[i]);
1431 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001432 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001433
Linus Torvalds1da177e2005-04-16 15:20:36 -07001434 release_region(config_ports[i], 2);
1435 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001436
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001437 host->config = 0;
1438 host->unlock_code = 0;
1439
Linus Torvalds1da177e2005-04-16 15:20:36 -07001440 return -ENODEV;
1441}
1442
Pierre Ossman85bcc132005-05-08 19:35:27 +01001443/*
1444 * Allocate/free io port ranges
1445 */
1446
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001447static int __devinit wbsd_request_region(struct wbsd_host *host, int base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001448{
Pierre Ossman916f3ac2006-08-06 22:22:23 +02001449 if (base & 0x7)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001450 return -EINVAL;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001451
Pierre Ossman85bcc132005-05-08 19:35:27 +01001452 if (!request_region(base, 8, DRIVER_NAME))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001453 return -EIO;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001454
Pierre Ossman916f3ac2006-08-06 22:22:23 +02001455 host->base = base;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001456
Linus Torvalds1da177e2005-04-16 15:20:36 -07001457 return 0;
1458}
1459
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001460static void __devexit wbsd_release_regions(struct wbsd_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001461{
1462 if (host->base)
1463 release_region(host->base, 8);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001464
Pierre Ossman85bcc132005-05-08 19:35:27 +01001465 host->base = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001466
1467 if (host->config)
1468 release_region(host->config, 2);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001469
Pierre Ossman85bcc132005-05-08 19:35:27 +01001470 host->config = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471}
1472
Pierre Ossman85bcc132005-05-08 19:35:27 +01001473/*
1474 * Allocate/free DMA port and buffer
1475 */
1476
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001477static void __devinit wbsd_request_dma(struct wbsd_host *host, int dma)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001478{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001479 if (dma < 0)
1480 return;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001481
Linus Torvalds1da177e2005-04-16 15:20:36 -07001482 if (request_dma(dma, DRIVER_NAME))
1483 goto err;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001484
Linus Torvalds1da177e2005-04-16 15:20:36 -07001485 /*
1486 * We need to allocate a special buffer in
1487 * order for ISA to be able to DMA to it.
1488 */
Pierre Ossman85bcc132005-05-08 19:35:27 +01001489 host->dma_buffer = kmalloc(WBSD_DMA_SIZE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490 GFP_NOIO | GFP_DMA | __GFP_REPEAT | __GFP_NOWARN);
1491 if (!host->dma_buffer)
1492 goto free;
1493
1494 /*
1495 * Translate the address to a physical address.
1496 */
Greg Kroah-Hartmanfcaf71f2006-09-12 17:00:10 +02001497 host->dma_addr = dma_map_single(mmc_dev(host->mmc), host->dma_buffer,
Pierre Ossman85bcc132005-05-08 19:35:27 +01001498 WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001499
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500 /*
1501 * ISA DMA must be aligned on a 64k basis.
1502 */
1503 if ((host->dma_addr & 0xffff) != 0)
1504 goto kfree;
1505 /*
1506 * ISA cannot access memory above 16 MB.
1507 */
1508 else if (host->dma_addr >= 0x1000000)
1509 goto kfree;
1510
1511 host->dma = dma;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001512
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513 return;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001514
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515kfree:
1516 /*
1517 * If we've gotten here then there is some kind of alignment bug
1518 */
1519 BUG_ON(1);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001520
Greg Kroah-Hartmanfcaf71f2006-09-12 17:00:10 +02001521 dma_unmap_single(mmc_dev(host->mmc), host->dma_addr,
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001522 WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
Pierre Ossman85bcc132005-05-08 19:35:27 +01001523 host->dma_addr = (dma_addr_t)NULL;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001524
Linus Torvalds1da177e2005-04-16 15:20:36 -07001525 kfree(host->dma_buffer);
1526 host->dma_buffer = NULL;
1527
1528free:
1529 free_dma(dma);
1530
1531err:
1532 printk(KERN_WARNING DRIVER_NAME ": Unable to allocate DMA %d. "
1533 "Falling back on FIFO.\n", dma);
1534}
1535
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001536static void __devexit wbsd_release_dma(struct wbsd_host *host)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001537{
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001538 if (host->dma_addr) {
Greg Kroah-Hartmanfcaf71f2006-09-12 17:00:10 +02001539 dma_unmap_single(mmc_dev(host->mmc), host->dma_addr,
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001540 WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
1541 }
Jesper Juhl6044ec82005-11-07 01:01:32 -08001542 kfree(host->dma_buffer);
Pierre Ossman85bcc132005-05-08 19:35:27 +01001543 if (host->dma >= 0)
1544 free_dma(host->dma);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001545
Pierre Ossman85bcc132005-05-08 19:35:27 +01001546 host->dma = -1;
1547 host->dma_buffer = NULL;
1548 host->dma_addr = (dma_addr_t)NULL;
1549}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001550
1551/*
Pierre Ossman85bcc132005-05-08 19:35:27 +01001552 * Allocate/free IRQ.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001553 */
1554
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001555static int __devinit wbsd_request_irq(struct wbsd_host *host, int irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557 int ret;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001558
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001560 * Allocate interrupt.
1561 */
Pierre Ossman85bcc132005-05-08 19:35:27 +01001562
Thomas Gleixnerdace1452006-07-01 19:29:38 -07001563 ret = request_irq(irq, wbsd_irq, IRQF_SHARED, DRIVER_NAME, host);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001564 if (ret)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001565 return ret;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001566
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567 host->irq = irq;
Pierre Ossman85bcc132005-05-08 19:35:27 +01001568
Linus Torvalds1da177e2005-04-16 15:20:36 -07001569 /*
1570 * Set up tasklets.
1571 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001572 tasklet_init(&host->card_tasklet, wbsd_tasklet_card,
1573 (unsigned long)host);
1574 tasklet_init(&host->fifo_tasklet, wbsd_tasklet_fifo,
1575 (unsigned long)host);
1576 tasklet_init(&host->crc_tasklet, wbsd_tasklet_crc,
1577 (unsigned long)host);
1578 tasklet_init(&host->timeout_tasklet, wbsd_tasklet_timeout,
1579 (unsigned long)host);
1580 tasklet_init(&host->finish_tasklet, wbsd_tasklet_finish,
1581 (unsigned long)host);
1582 tasklet_init(&host->block_tasklet, wbsd_tasklet_block,
1583 (unsigned long)host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001584
Linus Torvalds1da177e2005-04-16 15:20:36 -07001585 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001586}
1587
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001588static void __devexit wbsd_release_irq(struct wbsd_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589{
Pierre Ossman85bcc132005-05-08 19:35:27 +01001590 if (!host->irq)
1591 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001592
1593 free_irq(host->irq, host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001594
Pierre Ossman85bcc132005-05-08 19:35:27 +01001595 host->irq = 0;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001596
Linus Torvalds1da177e2005-04-16 15:20:36 -07001597 tasklet_kill(&host->card_tasklet);
1598 tasklet_kill(&host->fifo_tasklet);
1599 tasklet_kill(&host->crc_tasklet);
1600 tasklet_kill(&host->timeout_tasklet);
1601 tasklet_kill(&host->finish_tasklet);
1602 tasklet_kill(&host->block_tasklet);
Pierre Ossman85bcc132005-05-08 19:35:27 +01001603}
1604
1605/*
1606 * Allocate all resources for the host.
1607 */
1608
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001609static int __devinit wbsd_request_resources(struct wbsd_host *host,
Pierre Ossman85bcc132005-05-08 19:35:27 +01001610 int base, int irq, int dma)
1611{
1612 int ret;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001613
Pierre Ossman85bcc132005-05-08 19:35:27 +01001614 /*
1615 * Allocate I/O ports.
1616 */
1617 ret = wbsd_request_region(host, base);
1618 if (ret)
1619 return ret;
1620
1621 /*
1622 * Allocate interrupt.
1623 */
1624 ret = wbsd_request_irq(host, irq);
1625 if (ret)
1626 return ret;
1627
1628 /*
1629 * Allocate DMA.
1630 */
1631 wbsd_request_dma(host, dma);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001632
Pierre Ossman85bcc132005-05-08 19:35:27 +01001633 return 0;
1634}
1635
1636/*
1637 * Release all resources for the host.
1638 */
1639
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001640static void __devexit wbsd_release_resources(struct wbsd_host *host)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001641{
1642 wbsd_release_dma(host);
1643 wbsd_release_irq(host);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001644 wbsd_release_regions(host);
Pierre Ossman85bcc132005-05-08 19:35:27 +01001645}
1646
1647/*
1648 * Configure the resources the chip should use.
1649 */
1650
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001651static void wbsd_chip_config(struct wbsd_host *host)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001652{
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001653 wbsd_unlock_config(host);
1654
Pierre Ossman85bcc132005-05-08 19:35:27 +01001655 /*
1656 * Reset the chip.
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001657 */
Pierre Ossman85bcc132005-05-08 19:35:27 +01001658 wbsd_write_config(host, WBSD_CONF_SWRST, 1);
1659 wbsd_write_config(host, WBSD_CONF_SWRST, 0);
1660
1661 /*
1662 * Select SD/MMC function.
1663 */
1664 wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001665
Pierre Ossman85bcc132005-05-08 19:35:27 +01001666 /*
1667 * Set up card detection.
1668 */
1669 wbsd_write_config(host, WBSD_CONF_PINS, WBSD_PINS_DETECT_GP11);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001670
Pierre Ossman85bcc132005-05-08 19:35:27 +01001671 /*
1672 * Configure chip
1673 */
1674 wbsd_write_config(host, WBSD_CONF_PORT_HI, host->base >> 8);
1675 wbsd_write_config(host, WBSD_CONF_PORT_LO, host->base & 0xff);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001676
Pierre Ossman85bcc132005-05-08 19:35:27 +01001677 wbsd_write_config(host, WBSD_CONF_IRQ, host->irq);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001678
Pierre Ossman85bcc132005-05-08 19:35:27 +01001679 if (host->dma >= 0)
1680 wbsd_write_config(host, WBSD_CONF_DRQ, host->dma);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001681
Pierre Ossman85bcc132005-05-08 19:35:27 +01001682 /*
1683 * Enable and power up chip.
1684 */
1685 wbsd_write_config(host, WBSD_CONF_ENABLE, 1);
1686 wbsd_write_config(host, WBSD_CONF_POWER, 0x20);
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001687
1688 wbsd_lock_config(host);
Pierre Ossman85bcc132005-05-08 19:35:27 +01001689}
1690
1691/*
1692 * Check that configured resources are correct.
1693 */
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001694
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001695static int wbsd_chip_validate(struct wbsd_host *host)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001696{
1697 int base, irq, dma;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001698
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001699 wbsd_unlock_config(host);
1700
Pierre Ossman85bcc132005-05-08 19:35:27 +01001701 /*
1702 * Select SD/MMC function.
1703 */
1704 wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001705
Pierre Ossman85bcc132005-05-08 19:35:27 +01001706 /*
1707 * Read configuration.
1708 */
1709 base = wbsd_read_config(host, WBSD_CONF_PORT_HI) << 8;
1710 base |= wbsd_read_config(host, WBSD_CONF_PORT_LO);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001711
Pierre Ossman85bcc132005-05-08 19:35:27 +01001712 irq = wbsd_read_config(host, WBSD_CONF_IRQ);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001713
Pierre Ossman85bcc132005-05-08 19:35:27 +01001714 dma = wbsd_read_config(host, WBSD_CONF_DRQ);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001715
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001716 wbsd_lock_config(host);
1717
Pierre Ossman85bcc132005-05-08 19:35:27 +01001718 /*
1719 * Validate against given configuration.
1720 */
1721 if (base != host->base)
1722 return 0;
1723 if (irq != host->irq)
1724 return 0;
1725 if ((dma != host->dma) && (host->dma != -1))
1726 return 0;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001727
Pierre Ossman85bcc132005-05-08 19:35:27 +01001728 return 1;
1729}
1730
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001731/*
1732 * Powers down the SD function
1733 */
1734
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001735static void wbsd_chip_poweroff(struct wbsd_host *host)
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001736{
1737 wbsd_unlock_config(host);
1738
1739 wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
1740 wbsd_write_config(host, WBSD_CONF_ENABLE, 0);
1741
1742 wbsd_lock_config(host);
1743}
1744
Pierre Ossman85bcc132005-05-08 19:35:27 +01001745/*****************************************************************************\
1746 * *
1747 * Devices setup and shutdown *
1748 * *
1749\*****************************************************************************/
1750
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001751static int __devinit wbsd_init(struct device *dev, int base, int irq, int dma,
Pierre Ossman85bcc132005-05-08 19:35:27 +01001752 int pnp)
1753{
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001754 struct wbsd_host *host = NULL;
1755 struct mmc_host *mmc = NULL;
Pierre Ossman85bcc132005-05-08 19:35:27 +01001756 int ret;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001757
Pierre Ossman85bcc132005-05-08 19:35:27 +01001758 ret = wbsd_alloc_mmc(dev);
1759 if (ret)
1760 return ret;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001761
Pierre Ossman85bcc132005-05-08 19:35:27 +01001762 mmc = dev_get_drvdata(dev);
1763 host = mmc_priv(mmc);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001764
Pierre Ossman85bcc132005-05-08 19:35:27 +01001765 /*
1766 * Scan for hardware.
1767 */
1768 ret = wbsd_scan(host);
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001769 if (ret) {
1770 if (pnp && (ret == -ENODEV)) {
Pierre Ossman85bcc132005-05-08 19:35:27 +01001771 printk(KERN_WARNING DRIVER_NAME
1772 ": Unable to confirm device presence. You may "
1773 "experience lock-ups.\n");
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001774 } else {
Pierre Ossman85bcc132005-05-08 19:35:27 +01001775 wbsd_free_mmc(dev);
1776 return ret;
1777 }
1778 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001779
Pierre Ossman85bcc132005-05-08 19:35:27 +01001780 /*
1781 * Request resources.
1782 */
Pierre Ossmandd2c6092006-08-07 01:40:04 +02001783 ret = wbsd_request_resources(host, base, irq, dma);
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001784 if (ret) {
Pierre Ossman85bcc132005-05-08 19:35:27 +01001785 wbsd_release_resources(host);
1786 wbsd_free_mmc(dev);
1787 return ret;
1788 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001789
Pierre Ossman85bcc132005-05-08 19:35:27 +01001790 /*
1791 * See if chip needs to be configured.
1792 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001793 if (pnp) {
1794 if ((host->config != 0) && !wbsd_chip_validate(host)) {
Pierre Ossman85bcc132005-05-08 19:35:27 +01001795 printk(KERN_WARNING DRIVER_NAME
1796 ": PnP active but chip not configured! "
1797 "You probably have a buggy BIOS. "
1798 "Configuring chip manually.\n");
1799 wbsd_chip_config(host);
1800 }
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001801 } else
Pierre Ossman85bcc132005-05-08 19:35:27 +01001802 wbsd_chip_config(host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001803
Pierre Ossman85bcc132005-05-08 19:35:27 +01001804 /*
1805 * Power Management stuff. No idea how this works.
1806 * Not tested.
1807 */
1808#ifdef CONFIG_PM
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001809 if (host->config) {
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001810 wbsd_unlock_config(host);
Pierre Ossman85bcc132005-05-08 19:35:27 +01001811 wbsd_write_config(host, WBSD_CONF_PME, 0xA0);
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001812 wbsd_lock_config(host);
1813 }
Pierre Ossman85bcc132005-05-08 19:35:27 +01001814#endif
1815 /*
1816 * Allow device to initialise itself properly.
1817 */
1818 mdelay(5);
1819
1820 /*
1821 * Reset the chip into a known state.
1822 */
1823 wbsd_init_device(host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001824
Pierre Ossman85bcc132005-05-08 19:35:27 +01001825 mmc_add_host(mmc);
1826
Russell Kingd366b642005-08-19 09:40:08 +01001827 printk(KERN_INFO "%s: W83L51xD", mmc_hostname(mmc));
Pierre Ossman85bcc132005-05-08 19:35:27 +01001828 if (host->chip_id != 0)
1829 printk(" id %x", (int)host->chip_id);
1830 printk(" at 0x%x irq %d", (int)host->base, (int)host->irq);
1831 if (host->dma >= 0)
1832 printk(" dma %d", (int)host->dma);
1833 else
1834 printk(" FIFO");
1835 if (pnp)
1836 printk(" PnP");
1837 printk("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001838
1839 return 0;
1840}
1841
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001842static void __devexit wbsd_shutdown(struct device *dev, int pnp)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001843{
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001844 struct mmc_host *mmc = dev_get_drvdata(dev);
1845 struct wbsd_host *host;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001846
Pierre Ossman85bcc132005-05-08 19:35:27 +01001847 if (!mmc)
1848 return;
1849
1850 host = mmc_priv(mmc);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001851
Pierre Ossman85bcc132005-05-08 19:35:27 +01001852 mmc_remove_host(mmc);
1853
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001854 /*
1855 * Power down the SD/MMC function.
1856 */
Pierre Ossman85bcc132005-05-08 19:35:27 +01001857 if (!pnp)
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001858 wbsd_chip_poweroff(host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001859
Pierre Ossman85bcc132005-05-08 19:35:27 +01001860 wbsd_release_resources(host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001861
Pierre Ossman85bcc132005-05-08 19:35:27 +01001862 wbsd_free_mmc(dev);
1863}
1864
1865/*
1866 * Non-PnP
1867 */
1868
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001869static int __devinit wbsd_probe(struct platform_device *dev)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001870{
Pierre Ossmandd2c6092006-08-07 01:40:04 +02001871 /* Use the module parameters for resources */
Russell King3ae5eae2005-11-09 22:32:44 +00001872 return wbsd_init(&dev->dev, io, irq, dma, 0);
Pierre Ossman85bcc132005-05-08 19:35:27 +01001873}
1874
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001875static int __devexit wbsd_remove(struct platform_device *dev)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001876{
Russell King3ae5eae2005-11-09 22:32:44 +00001877 wbsd_shutdown(&dev->dev, 0);
Pierre Ossman85bcc132005-05-08 19:35:27 +01001878
1879 return 0;
1880}
1881
1882/*
1883 * PnP
1884 */
1885
1886#ifdef CONFIG_PNP
1887
1888static int __devinit
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001889wbsd_pnp_probe(struct pnp_dev *pnpdev, const struct pnp_device_id *dev_id)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001890{
1891 int io, irq, dma;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001892
Pierre Ossman85bcc132005-05-08 19:35:27 +01001893 /*
1894 * Get resources from PnP layer.
1895 */
1896 io = pnp_port_start(pnpdev, 0);
1897 irq = pnp_irq(pnpdev, 0);
1898 if (pnp_dma_valid(pnpdev, 0))
1899 dma = pnp_dma(pnpdev, 0);
1900 else
1901 dma = -1;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001902
Pierre Ossman85bcc132005-05-08 19:35:27 +01001903 DBGF("PnP resources: port %3x irq %d dma %d\n", io, irq, dma);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001904
Pierre Ossman85bcc132005-05-08 19:35:27 +01001905 return wbsd_init(&pnpdev->dev, io, irq, dma, 1);
1906}
1907
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001908static void __devexit wbsd_pnp_remove(struct pnp_dev *dev)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001909{
1910 wbsd_shutdown(&dev->dev, 1);
1911}
1912
1913#endif /* CONFIG_PNP */
1914
Linus Torvalds1da177e2005-04-16 15:20:36 -07001915/*
1916 * Power management
1917 */
1918
1919#ifdef CONFIG_PM
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001920
Pierre Ossman5e68d952006-01-08 14:21:52 +00001921static int wbsd_suspend(struct wbsd_host *host, pm_message_t state)
1922{
1923 BUG_ON(host == NULL);
1924
1925 return mmc_suspend_host(host->mmc, state);
1926}
1927
1928static int wbsd_resume(struct wbsd_host *host)
1929{
1930 BUG_ON(host == NULL);
1931
1932 wbsd_init_device(host);
1933
1934 return mmc_resume_host(host->mmc);
1935}
1936
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001937static int wbsd_platform_suspend(struct platform_device *dev,
1938 pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939{
Russell King3ae5eae2005-11-09 22:32:44 +00001940 struct mmc_host *mmc = platform_get_drvdata(dev);
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001941 struct wbsd_host *host;
1942 int ret;
1943
Pierre Ossman5e68d952006-01-08 14:21:52 +00001944 if (mmc == NULL)
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001945 return 0;
1946
Pierre Ossman5e68d952006-01-08 14:21:52 +00001947 DBGF("Suspending...\n");
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001948
1949 host = mmc_priv(mmc);
1950
Pierre Ossman5e68d952006-01-08 14:21:52 +00001951 ret = wbsd_suspend(host, state);
1952 if (ret)
1953 return ret;
1954
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001955 wbsd_chip_poweroff(host);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001956
1957 return 0;
1958}
1959
Pierre Ossman5e68d952006-01-08 14:21:52 +00001960static int wbsd_platform_resume(struct platform_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001961{
Russell King3ae5eae2005-11-09 22:32:44 +00001962 struct mmc_host *mmc = platform_get_drvdata(dev);
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001963 struct wbsd_host *host;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001964
Pierre Ossman5e68d952006-01-08 14:21:52 +00001965 if (mmc == NULL)
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001966 return 0;
1967
Pierre Ossman5e68d952006-01-08 14:21:52 +00001968 DBGF("Resuming...\n");
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001969
1970 host = mmc_priv(mmc);
1971
1972 wbsd_chip_config(host);
1973
1974 /*
1975 * Allow device to initialise itself properly.
1976 */
1977 mdelay(5);
1978
Pierre Ossman5e68d952006-01-08 14:21:52 +00001979 return wbsd_resume(host);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001980}
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001981
Pierre Ossman5e68d952006-01-08 14:21:52 +00001982#ifdef CONFIG_PNP
1983
1984static int wbsd_pnp_suspend(struct pnp_dev *pnp_dev, pm_message_t state)
1985{
1986 struct mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev);
1987 struct wbsd_host *host;
1988
1989 if (mmc == NULL)
1990 return 0;
1991
1992 DBGF("Suspending...\n");
1993
1994 host = mmc_priv(mmc);
1995
1996 return wbsd_suspend(host, state);
1997}
1998
1999static int wbsd_pnp_resume(struct pnp_dev *pnp_dev)
2000{
2001 struct mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev);
2002 struct wbsd_host *host;
2003
2004 if (mmc == NULL)
2005 return 0;
2006
2007 DBGF("Resuming...\n");
2008
2009 host = mmc_priv(mmc);
2010
2011 /*
2012 * See if chip needs to be configured.
2013 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +00002014 if (host->config != 0) {
2015 if (!wbsd_chip_validate(host)) {
Pierre Ossman5e68d952006-01-08 14:21:52 +00002016 printk(KERN_WARNING DRIVER_NAME
2017 ": PnP active but chip not configured! "
2018 "You probably have a buggy BIOS. "
2019 "Configuring chip manually.\n");
2020 wbsd_chip_config(host);
2021 }
2022 }
2023
2024 /*
2025 * Allow device to initialise itself properly.
2026 */
2027 mdelay(5);
2028
2029 return wbsd_resume(host);
2030}
2031
2032#endif /* CONFIG_PNP */
2033
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01002034#else /* CONFIG_PM */
2035
Pierre Ossman5e68d952006-01-08 14:21:52 +00002036#define wbsd_platform_suspend NULL
2037#define wbsd_platform_resume NULL
2038
2039#define wbsd_pnp_suspend NULL
2040#define wbsd_pnp_resume NULL
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01002041
2042#endif /* CONFIG_PM */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002043
Pierre Ossman85bcc132005-05-08 19:35:27 +01002044static struct platform_device *wbsd_device;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002045
Russell King3ae5eae2005-11-09 22:32:44 +00002046static struct platform_driver wbsd_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002047 .probe = wbsd_probe,
Pierre Ossman93968d72005-11-09 23:21:06 +00002048 .remove = __devexit_p(wbsd_remove),
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01002049
Pierre Ossman5e68d952006-01-08 14:21:52 +00002050 .suspend = wbsd_platform_suspend,
2051 .resume = wbsd_platform_resume,
Russell King3ae5eae2005-11-09 22:32:44 +00002052 .driver = {
2053 .name = DRIVER_NAME,
2054 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07002055};
2056
Pierre Ossman85bcc132005-05-08 19:35:27 +01002057#ifdef CONFIG_PNP
2058
2059static struct pnp_driver wbsd_pnp_driver = {
2060 .name = DRIVER_NAME,
2061 .id_table = pnp_dev_table,
2062 .probe = wbsd_pnp_probe,
Pierre Ossman93968d72005-11-09 23:21:06 +00002063 .remove = __devexit_p(wbsd_pnp_remove),
Pierre Ossman5e68d952006-01-08 14:21:52 +00002064
2065 .suspend = wbsd_pnp_suspend,
2066 .resume = wbsd_pnp_resume,
Pierre Ossman85bcc132005-05-08 19:35:27 +01002067};
2068
2069#endif /* CONFIG_PNP */
2070
Linus Torvalds1da177e2005-04-16 15:20:36 -07002071/*
2072 * Module loading/unloading
2073 */
2074
2075static int __init wbsd_drv_init(void)
2076{
2077 int result;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01002078
Linus Torvalds1da177e2005-04-16 15:20:36 -07002079 printk(KERN_INFO DRIVER_NAME
2080 ": Winbond W83L51xD SD/MMC card interface driver, "
2081 DRIVER_VERSION "\n");
2082 printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002083
Pierre Ossman85bcc132005-05-08 19:35:27 +01002084#ifdef CONFIG_PNP
2085
Pierre Ossmancfa7f522006-01-08 18:17:55 +00002086 if (!nopnp) {
Pierre Ossman85bcc132005-05-08 19:35:27 +01002087 result = pnp_register_driver(&wbsd_pnp_driver);
2088 if (result < 0)
2089 return result;
2090 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01002091#endif /* CONFIG_PNP */
2092
Pierre Ossmancfa7f522006-01-08 18:17:55 +00002093 if (nopnp) {
Russell King3ae5eae2005-11-09 22:32:44 +00002094 result = platform_driver_register(&wbsd_driver);
Pierre Ossman85bcc132005-05-08 19:35:27 +01002095 if (result < 0)
2096 return result;
2097
Dmitry Torokhov21500bb2006-01-03 22:57:35 +00002098 wbsd_device = platform_device_alloc(DRIVER_NAME, -1);
Pierre Ossmancfa7f522006-01-08 18:17:55 +00002099 if (!wbsd_device) {
Dmitry Torokhov21500bb2006-01-03 22:57:35 +00002100 platform_driver_unregister(&wbsd_driver);
2101 return -ENOMEM;
2102 }
2103
2104 result = platform_device_add(wbsd_device);
Pierre Ossmancfa7f522006-01-08 18:17:55 +00002105 if (result) {
Dmitry Torokhov21500bb2006-01-03 22:57:35 +00002106 platform_device_put(wbsd_device);
2107 platform_driver_unregister(&wbsd_driver);
2108 return result;
2109 }
Pierre Ossman85bcc132005-05-08 19:35:27 +01002110 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002111
2112 return 0;
2113}
2114
2115static void __exit wbsd_drv_exit(void)
2116{
Pierre Ossman85bcc132005-05-08 19:35:27 +01002117#ifdef CONFIG_PNP
2118
2119 if (!nopnp)
2120 pnp_unregister_driver(&wbsd_pnp_driver);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01002121
2122#endif /* CONFIG_PNP */
Pierre Ossman85bcc132005-05-08 19:35:27 +01002123
Pierre Ossmancfa7f522006-01-08 18:17:55 +00002124 if (nopnp) {
Pierre Ossman85bcc132005-05-08 19:35:27 +01002125 platform_device_unregister(wbsd_device);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01002126
Russell King3ae5eae2005-11-09 22:32:44 +00002127 platform_driver_unregister(&wbsd_driver);
Pierre Ossman85bcc132005-05-08 19:35:27 +01002128 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002129
2130 DBG("unloaded\n");
2131}
2132
2133module_init(wbsd_drv_init);
2134module_exit(wbsd_drv_exit);
Pierre Ossman85bcc132005-05-08 19:35:27 +01002135#ifdef CONFIG_PNP
2136module_param(nopnp, uint, 0444);
2137#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07002138module_param(io, uint, 0444);
2139module_param(irq, uint, 0444);
2140module_param(dma, int, 0444);
2141
2142MODULE_LICENSE("GPL");
Pierre Ossmande1d09e2005-11-09 23:21:49 +00002143MODULE_AUTHOR("Pierre Ossman <drzeus@drzeus.cx>");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002144MODULE_DESCRIPTION("Winbond W83L51xD SD/MMC card interface driver");
2145MODULE_VERSION(DRIVER_VERSION);
2146
Pierre Ossman85bcc132005-05-08 19:35:27 +01002147#ifdef CONFIG_PNP
2148MODULE_PARM_DESC(nopnp, "Scan for device instead of relying on PNP. (default 0)");
2149#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07002150MODULE_PARM_DESC(io, "I/O base to allocate. Must be 8 byte aligned. (default 0x248)");
2151MODULE_PARM_DESC(irq, "IRQ to allocate. (default 6)");
2152MODULE_PARM_DESC(dma, "DMA channel to allocate. -1 for no DMA. (default 2)");