blob: 673c64661d36021fdbb1e23c75e23a5c00cdb536 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * linux/drivers/mmc/wbsd.c - Winbond W83L51xD SD/MMC driver
3 *
Alex Dubov14d836e2007-04-13 19:04:38 +02004 * Copyright (C) 2004-2007 Pierre Ossman, All Rights Reserved.
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 *
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"
Linus Torvalds1da177e2005-04-16 15:20:36 -070045
Linus Torvalds1da177e2005-04-16 15:20:36 -070046#define DBG(x...) \
Russell Kingc6563172006-03-29 09:30:20 +010047 pr_debug(DRIVER_NAME ": " x)
Linus Torvalds1da177e2005-04-16 15:20:36 -070048#define DBGF(f, x...) \
Russell Kingc6563172006-03-29 09:30:20 +010049 pr_debug(DRIVER_NAME " [%s()]: " f, __func__ , ##x)
Linus Torvalds1da177e2005-04-16 15:20:36 -070050
Linus Torvalds1da177e2005-04-16 15:20:36 -070051/*
Pierre Ossman85bcc132005-05-08 19:35:27 +010052 * Device resources
53 */
54
55#ifdef CONFIG_PNP
56
57static const struct pnp_device_id pnp_dev_table[] = {
58 { "WEC0517", 0 },
59 { "WEC0518", 0 },
60 { "", 0 },
61};
62
63MODULE_DEVICE_TABLE(pnp, pnp_dev_table);
64
65#endif /* CONFIG_PNP */
66
Adrian Bunk3eee0d02005-07-01 13:07:37 +010067static const int config_ports[] = { 0x2E, 0x4E };
68static const int unlock_codes[] = { 0x83, 0x87 };
69
70static const int valid_ids[] = {
71 0x7112,
72 };
73
Pierre Ossman85bcc132005-05-08 19:35:27 +010074#ifdef CONFIG_PNP
75static unsigned int nopnp = 0;
76#else
77static const unsigned int nopnp = 1;
78#endif
79static unsigned int io = 0x248;
80static unsigned int irq = 6;
81static int dma = 2;
82
83/*
Linus Torvalds1da177e2005-04-16 15:20:36 -070084 * Basic functions
85 */
86
Pierre Ossmancfa7f522006-01-08 18:17:55 +000087static inline void wbsd_unlock_config(struct wbsd_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -070088{
Pierre Ossman85bcc132005-05-08 19:35:27 +010089 BUG_ON(host->config == 0);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +010090
Linus Torvalds1da177e2005-04-16 15:20:36 -070091 outb(host->unlock_code, host->config);
92 outb(host->unlock_code, host->config);
93}
94
Pierre Ossmancfa7f522006-01-08 18:17:55 +000095static inline void wbsd_lock_config(struct wbsd_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -070096{
Pierre Ossman85bcc132005-05-08 19:35:27 +010097 BUG_ON(host->config == 0);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +010098
Linus Torvalds1da177e2005-04-16 15:20:36 -070099 outb(LOCK_CODE, host->config);
100}
101
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000102static inline void wbsd_write_config(struct wbsd_host *host, u8 reg, u8 value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103{
Pierre Ossman85bcc132005-05-08 19:35:27 +0100104 BUG_ON(host->config == 0);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100105
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106 outb(reg, host->config);
107 outb(value, host->config + 1);
108}
109
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000110static inline u8 wbsd_read_config(struct wbsd_host *host, u8 reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111{
Pierre Ossman85bcc132005-05-08 19:35:27 +0100112 BUG_ON(host->config == 0);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100113
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114 outb(reg, host->config);
115 return inb(host->config + 1);
116}
117
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000118static inline void wbsd_write_index(struct wbsd_host *host, u8 index, u8 value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119{
120 outb(index, host->base + WBSD_IDXR);
121 outb(value, host->base + WBSD_DATAR);
122}
123
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000124static inline u8 wbsd_read_index(struct wbsd_host *host, u8 index)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125{
126 outb(index, host->base + WBSD_IDXR);
127 return inb(host->base + WBSD_DATAR);
128}
129
130/*
131 * Common routines
132 */
133
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000134static void wbsd_init_device(struct wbsd_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135{
136 u8 setup, ier;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100137
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 /*
139 * Reset chip (SD/MMC part) and fifo.
140 */
141 setup = wbsd_read_index(host, WBSD_IDX_SETUP);
142 setup |= WBSD_FIFO_RESET | WBSD_SOFT_RESET;
143 wbsd_write_index(host, WBSD_IDX_SETUP, setup);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100144
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 /*
Pierre Ossman85bcc132005-05-08 19:35:27 +0100146 * Set DAT3 to input
147 */
148 setup &= ~WBSD_DAT3_H;
149 wbsd_write_index(host, WBSD_IDX_SETUP, setup);
150 host->flags &= ~WBSD_FIGNORE_DETECT;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100151
Pierre Ossman85bcc132005-05-08 19:35:27 +0100152 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153 * Read back default clock.
154 */
155 host->clk = wbsd_read_index(host, WBSD_IDX_CLK);
156
157 /*
158 * Power down port.
159 */
160 outb(WBSD_POWER_N, host->base + WBSD_CSR);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100161
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162 /*
163 * Set maximum timeout.
164 */
165 wbsd_write_index(host, WBSD_IDX_TAAC, 0x7F);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100166
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 /*
Pierre Ossman85bcc132005-05-08 19:35:27 +0100168 * Test for card presence
169 */
170 if (inb(host->base + WBSD_CSR) & WBSD_CARDPRESENT)
171 host->flags |= WBSD_FCARD_PRESENT;
172 else
173 host->flags &= ~WBSD_FCARD_PRESENT;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100174
Pierre Ossman85bcc132005-05-08 19:35:27 +0100175 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176 * Enable interesting interrupts.
177 */
178 ier = 0;
179 ier |= WBSD_EINT_CARD;
180 ier |= WBSD_EINT_FIFO_THRE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 ier |= WBSD_EINT_CRC;
Pierre Ossman5721dbf2007-04-13 23:25:59 +0200182 ier |= WBSD_EINT_TIMEOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 ier |= WBSD_EINT_TC;
184
185 outb(ier, host->base + WBSD_EIR);
186
187 /*
188 * Clear interrupts.
189 */
190 inb(host->base + WBSD_ISR);
191}
192
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000193static void wbsd_reset(struct wbsd_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194{
195 u8 setup;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100196
Pierre Ossmand1916342005-11-05 10:36:35 +0000197 printk(KERN_ERR "%s: Resetting chip\n", mmc_hostname(host->mmc));
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100198
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199 /*
200 * Soft reset of chip (SD/MMC part).
201 */
202 setup = wbsd_read_index(host, WBSD_IDX_SETUP);
203 setup |= WBSD_SOFT_RESET;
204 wbsd_write_index(host, WBSD_IDX_SETUP, setup);
205}
206
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000207static void wbsd_request_end(struct wbsd_host *host, struct mmc_request *mrq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208{
209 unsigned long dmaflags;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100210
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211 DBGF("Ending request, cmd (%x)\n", mrq->cmd->opcode);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100212
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000213 if (host->dma >= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 /*
215 * Release ISA DMA controller.
216 */
217 dmaflags = claim_dma_lock();
218 disable_dma(host->dma);
219 clear_dma_ff(host->dma);
220 release_dma_lock(dmaflags);
221
222 /*
223 * Disable DMA on host.
224 */
225 wbsd_write_index(host, WBSD_IDX_DMA, 0);
226 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100227
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228 host->mrq = NULL;
229
230 /*
231 * MMC layer might call back into the driver so first unlock.
232 */
233 spin_unlock(&host->lock);
234 mmc_request_done(host->mmc, mrq);
235 spin_lock(&host->lock);
236}
237
238/*
239 * Scatter/gather functions
240 */
241
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000242static inline void wbsd_init_sg(struct wbsd_host *host, struct mmc_data *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243{
244 /*
245 * Get info. about SG list from data structure.
246 */
247 host->cur_sg = data->sg;
248 host->num_sg = data->sg_len;
249
250 host->offset = 0;
251 host->remain = host->cur_sg->length;
252}
253
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000254static inline int wbsd_next_sg(struct wbsd_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255{
256 /*
257 * Skip to next SG entry.
258 */
259 host->cur_sg++;
260 host->num_sg--;
261
262 /*
263 * Any entries left?
264 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000265 if (host->num_sg > 0) {
266 host->offset = 0;
267 host->remain = host->cur_sg->length;
268 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100269
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270 return host->num_sg;
271}
272
Pierre Ossman4a0ddbd2007-01-31 18:20:48 +0100273static inline char *wbsd_sg_to_buffer(struct wbsd_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274{
Pierre Ossman4a0ddbd2007-01-31 18:20:48 +0100275 return page_address(host->cur_sg->page) + host->cur_sg->offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276}
277
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000278static inline void wbsd_sg_to_dma(struct wbsd_host *host, struct mmc_data *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279{
Alex Dubov14d836e2007-04-13 19:04:38 +0200280 unsigned int len, i;
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000281 struct scatterlist *sg;
282 char *dmabuf = host->dma_buffer;
283 char *sgbuf;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100284
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 sg = data->sg;
286 len = data->sg_len;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100287
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000288 for (i = 0; i < len; i++) {
Pierre Ossman4a0ddbd2007-01-31 18:20:48 +0100289 sgbuf = page_address(sg[i].page) + sg[i].offset;
Alex Dubov14d836e2007-04-13 19:04:38 +0200290 memcpy(dmabuf, sgbuf, sg[i].length);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 dmabuf += sg[i].length;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293}
294
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000295static inline void wbsd_dma_to_sg(struct wbsd_host *host, struct mmc_data *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296{
Alex Dubov14d836e2007-04-13 19:04:38 +0200297 unsigned int len, i;
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000298 struct scatterlist *sg;
299 char *dmabuf = host->dma_buffer;
300 char *sgbuf;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100301
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 sg = data->sg;
303 len = data->sg_len;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100304
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000305 for (i = 0; i < len; i++) {
Pierre Ossman4a0ddbd2007-01-31 18:20:48 +0100306 sgbuf = page_address(sg[i].page) + sg[i].offset;
Alex Dubov14d836e2007-04-13 19:04:38 +0200307 memcpy(sgbuf, dmabuf, sg[i].length);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308 dmabuf += sg[i].length;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310}
311
312/*
313 * Command handling
314 */
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100315
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000316static inline void wbsd_get_short_reply(struct wbsd_host *host,
317 struct mmc_command *cmd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318{
319 /*
320 * Correct response type?
321 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000322 if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_SHORT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 cmd->error = MMC_ERR_INVALID;
324 return;
325 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100326
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000327 cmd->resp[0] = wbsd_read_index(host, WBSD_IDX_RESP12) << 24;
328 cmd->resp[0] |= wbsd_read_index(host, WBSD_IDX_RESP13) << 16;
329 cmd->resp[0] |= wbsd_read_index(host, WBSD_IDX_RESP14) << 8;
330 cmd->resp[0] |= wbsd_read_index(host, WBSD_IDX_RESP15) << 0;
331 cmd->resp[1] = wbsd_read_index(host, WBSD_IDX_RESP16) << 24;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332}
333
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000334static inline void wbsd_get_long_reply(struct wbsd_host *host,
335 struct mmc_command *cmd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336{
337 int i;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100338
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 /*
340 * Correct response type?
341 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000342 if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_LONG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 cmd->error = MMC_ERR_INVALID;
344 return;
345 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100346
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000347 for (i = 0; i < 4; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348 cmd->resp[i] =
349 wbsd_read_index(host, WBSD_IDX_RESP1 + i * 4) << 24;
350 cmd->resp[i] |=
351 wbsd_read_index(host, WBSD_IDX_RESP2 + i * 4) << 16;
352 cmd->resp[i] |=
353 wbsd_read_index(host, WBSD_IDX_RESP3 + i * 4) << 8;
354 cmd->resp[i] |=
355 wbsd_read_index(host, WBSD_IDX_RESP4 + i * 4) << 0;
356 }
357}
358
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000359static void wbsd_send_command(struct wbsd_host *host, struct mmc_command *cmd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360{
361 int i;
362 u8 status, isr;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100363
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 DBGF("Sending cmd (%x)\n", cmd->opcode);
365
366 /*
367 * Clear accumulated ISR. The interrupt routine
368 * will fill this one with events that occur during
369 * transfer.
370 */
371 host->isr = 0;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100372
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373 /*
374 * Send the command (CRC calculated by host).
375 */
376 outb(cmd->opcode, host->base + WBSD_CMDR);
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000377 for (i = 3; i >= 0; i--)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 outb((cmd->arg >> (i * 8)) & 0xff, host->base + WBSD_CMDR);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100379
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380 cmd->error = MMC_ERR_NONE;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100381
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 /*
383 * Wait for the request to complete.
384 */
385 do {
386 status = wbsd_read_index(host, WBSD_IDX_STATUS);
387 } while (status & WBSD_CARDTRAFFIC);
388
389 /*
390 * Do we expect a reply?
391 */
Russell Kinge9225172006-02-02 12:23:12 +0000392 if (cmd->flags & MMC_RSP_PRESENT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393 /*
394 * Read back status.
395 */
396 isr = host->isr;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100397
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398 /* Card removed? */
399 if (isr & WBSD_INT_CARD)
400 cmd->error = MMC_ERR_TIMEOUT;
401 /* Timeout? */
402 else if (isr & WBSD_INT_TIMEOUT)
403 cmd->error = MMC_ERR_TIMEOUT;
404 /* CRC? */
405 else if ((cmd->flags & MMC_RSP_CRC) && (isr & WBSD_INT_CRC))
406 cmd->error = MMC_ERR_BADCRC;
407 /* All ok */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000408 else {
Russell Kinge9225172006-02-02 12:23:12 +0000409 if (cmd->flags & MMC_RSP_136)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 wbsd_get_long_reply(host, cmd);
Russell Kinge9225172006-02-02 12:23:12 +0000411 else
412 wbsd_get_short_reply(host, cmd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 }
414 }
415
416 DBGF("Sent cmd (%x), res %d\n", cmd->opcode, cmd->error);
417}
418
419/*
420 * Data functions
421 */
422
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000423static void wbsd_empty_fifo(struct wbsd_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424{
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000425 struct mmc_data *data = host->mrq->cmd->data;
426 char *buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427 int i, fsr, fifo;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100428
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 /*
430 * Handle excessive data.
431 */
Alex Dubov14d836e2007-04-13 19:04:38 +0200432 if (host->num_sg == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 return;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100434
Pierre Ossman4a0ddbd2007-01-31 18:20:48 +0100435 buffer = wbsd_sg_to_buffer(host) + host->offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436
437 /*
438 * Drain the fifo. This has a tendency to loop longer
439 * than the FIFO length (usually one block).
440 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000441 while (!((fsr = inb(host->base + WBSD_FSR)) & WBSD_FIFO_EMPTY)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442 /*
443 * The size field in the FSR is broken so we have to
444 * do some guessing.
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100445 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 if (fsr & WBSD_FIFO_FULL)
447 fifo = 16;
448 else if (fsr & WBSD_FIFO_FUTHRE)
449 fifo = 8;
450 else
451 fifo = 1;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100452
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000453 for (i = 0; i < fifo; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454 *buffer = inb(host->base + WBSD_DFR);
455 buffer++;
456 host->offset++;
457 host->remain--;
458
459 data->bytes_xfered++;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100460
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462 * End of scatter list entry?
463 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000464 if (host->remain == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465 /*
466 * Get next entry. Check if last.
467 */
Alex Dubov14d836e2007-04-13 19:04:38 +0200468 if (!wbsd_next_sg(host))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 return;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100470
Pierre Ossman4a0ddbd2007-01-31 18:20:48 +0100471 buffer = wbsd_sg_to_buffer(host);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 }
473 }
474 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100475
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476 /*
477 * This is a very dirty hack to solve a
478 * hardware problem. The chip doesn't trigger
479 * FIFO threshold interrupts properly.
480 */
Alex Dubov14d836e2007-04-13 19:04:38 +0200481 if ((data->blocks * data->blksz - data->bytes_xfered) < 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 tasklet_schedule(&host->fifo_tasklet);
483}
484
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000485static void wbsd_fill_fifo(struct wbsd_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486{
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000487 struct mmc_data *data = host->mrq->cmd->data;
488 char *buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489 int i, fsr, fifo;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100490
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 /*
492 * Check that we aren't being called after the
493 * entire buffer has been transfered.
494 */
Alex Dubov14d836e2007-04-13 19:04:38 +0200495 if (host->num_sg == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 return;
497
Pierre Ossman4a0ddbd2007-01-31 18:20:48 +0100498 buffer = wbsd_sg_to_buffer(host) + host->offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499
500 /*
501 * Fill the fifo. This has a tendency to loop longer
502 * than the FIFO length (usually one block).
503 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000504 while (!((fsr = inb(host->base + WBSD_FSR)) & WBSD_FIFO_FULL)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505 /*
506 * The size field in the FSR is broken so we have to
507 * do some guessing.
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100508 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 if (fsr & WBSD_FIFO_EMPTY)
510 fifo = 0;
511 else if (fsr & WBSD_FIFO_EMTHRE)
512 fifo = 8;
513 else
514 fifo = 15;
515
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000516 for (i = 16; i > fifo; i--) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 outb(*buffer, host->base + WBSD_DFR);
518 buffer++;
519 host->offset++;
520 host->remain--;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100521
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522 data->bytes_xfered++;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100523
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 * End of scatter list entry?
526 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000527 if (host->remain == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 /*
529 * Get next entry. Check if last.
530 */
Alex Dubov14d836e2007-04-13 19:04:38 +0200531 if (!wbsd_next_sg(host))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532 return;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100533
Pierre Ossman4a0ddbd2007-01-31 18:20:48 +0100534 buffer = wbsd_sg_to_buffer(host);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 }
536 }
537 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100538
Pierre Ossman85bcc132005-05-08 19:35:27 +0100539 /*
540 * The controller stops sending interrupts for
541 * 'FIFO empty' under certain conditions. So we
542 * need to be a bit more pro-active.
543 */
544 tasklet_schedule(&host->fifo_tasklet);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545}
546
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000547static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548{
549 u16 blksize;
550 u8 setup;
551 unsigned long dmaflags;
Alex Dubov14d836e2007-04-13 19:04:38 +0200552 unsigned int size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553
554 DBGF("blksz %04x blks %04x flags %08x\n",
Pavel Pisa2c171bf2006-05-19 21:48:03 +0100555 data->blksz, data->blocks, data->flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 DBGF("tsac %d ms nsac %d clk\n",
557 data->timeout_ns / 1000000, data->timeout_clks);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100558
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 /*
560 * Calculate size.
561 */
Alex Dubov14d836e2007-04-13 19:04:38 +0200562 size = data->blocks * data->blksz;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563
564 /*
565 * Check timeout values for overflow.
566 * (Yes, some cards cause this value to overflow).
567 */
568 if (data->timeout_ns > 127000000)
569 wbsd_write_index(host, WBSD_IDX_TAAC, 127);
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000570 else {
571 wbsd_write_index(host, WBSD_IDX_TAAC,
572 data->timeout_ns / 1000000);
573 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100574
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 if (data->timeout_clks > 255)
576 wbsd_write_index(host, WBSD_IDX_NSAC, 255);
577 else
578 wbsd_write_index(host, WBSD_IDX_NSAC, data->timeout_clks);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100579
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580 /*
581 * Inform the chip of how large blocks will be
582 * sent. It needs this to determine when to
583 * calculate CRC.
584 *
585 * Space for CRC must be included in the size.
Pierre Ossman65ae2112005-09-06 15:18:57 -0700586 * Two bytes are needed for each data line.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000588 if (host->bus_width == MMC_BUS_WIDTH_1) {
Pavel Pisa2c171bf2006-05-19 21:48:03 +0100589 blksize = data->blksz + 2;
Pierre Ossman65ae2112005-09-06 15:18:57 -0700590
591 wbsd_write_index(host, WBSD_IDX_PBSMSB, (blksize >> 4) & 0xF0);
592 wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF);
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000593 } else if (host->bus_width == MMC_BUS_WIDTH_4) {
Pavel Pisa2c171bf2006-05-19 21:48:03 +0100594 blksize = data->blksz + 2 * 4;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100595
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000596 wbsd_write_index(host, WBSD_IDX_PBSMSB,
597 ((blksize >> 4) & 0xF0) | WBSD_DATA_WIDTH);
Pierre Ossman65ae2112005-09-06 15:18:57 -0700598 wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF);
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000599 } else {
Pierre Ossman65ae2112005-09-06 15:18:57 -0700600 data->error = MMC_ERR_INVALID;
601 return;
602 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603
604 /*
605 * Clear the FIFO. This is needed even for DMA
606 * transfers since the chip still uses the FIFO
607 * internally.
608 */
609 setup = wbsd_read_index(host, WBSD_IDX_SETUP);
610 setup |= WBSD_FIFO_RESET;
611 wbsd_write_index(host, WBSD_IDX_SETUP, setup);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100612
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 /*
614 * DMA transfer?
615 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000616 if (host->dma >= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617 /*
618 * The buffer for DMA is only 64 kB.
619 */
Alex Dubov14d836e2007-04-13 19:04:38 +0200620 BUG_ON(size > 0x10000);
621 if (size > 0x10000) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 data->error = MMC_ERR_INVALID;
623 return;
624 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100625
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 /*
627 * Transfer data from the SG list to
628 * the DMA buffer.
629 */
630 if (data->flags & MMC_DATA_WRITE)
631 wbsd_sg_to_dma(host, data);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100632
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633 /*
634 * Initialise the ISA DMA controller.
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100635 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 dmaflags = claim_dma_lock();
637 disable_dma(host->dma);
638 clear_dma_ff(host->dma);
639 if (data->flags & MMC_DATA_READ)
640 set_dma_mode(host->dma, DMA_MODE_READ & ~0x40);
641 else
642 set_dma_mode(host->dma, DMA_MODE_WRITE & ~0x40);
643 set_dma_addr(host->dma, host->dma_addr);
Alex Dubov14d836e2007-04-13 19:04:38 +0200644 set_dma_count(host->dma, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645
646 enable_dma(host->dma);
647 release_dma_lock(dmaflags);
648
649 /*
650 * Enable DMA on the host.
651 */
652 wbsd_write_index(host, WBSD_IDX_DMA, WBSD_DMA_ENABLE);
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000653 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 /*
655 * This flag is used to keep printk
656 * output to a minimum.
657 */
658 host->firsterr = 1;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100659
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660 /*
661 * Initialise the SG list.
662 */
663 wbsd_init_sg(host, data);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100664
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 /*
666 * Turn off DMA.
667 */
668 wbsd_write_index(host, WBSD_IDX_DMA, 0);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100669
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 /*
671 * Set up FIFO threshold levels (and fill
672 * buffer if doing a write).
673 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000674 if (data->flags & MMC_DATA_READ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675 wbsd_write_index(host, WBSD_IDX_FIFOEN,
676 WBSD_FIFOEN_FULL | 8);
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000677 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 wbsd_write_index(host, WBSD_IDX_FIFOEN,
679 WBSD_FIFOEN_EMPTY | 8);
680 wbsd_fill_fifo(host);
681 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100682 }
683
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684 data->error = MMC_ERR_NONE;
685}
686
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000687static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688{
689 unsigned long dmaflags;
690 int count;
691 u8 status;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100692
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 WARN_ON(host->mrq == NULL);
694
695 /*
696 * Send a stop command if needed.
697 */
698 if (data->stop)
699 wbsd_send_command(host, data->stop);
700
701 /*
702 * Wait for the controller to leave data
703 * transfer state.
704 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000705 do {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 status = wbsd_read_index(host, WBSD_IDX_STATUS);
707 } while (status & (WBSD_BLOCK_READ | WBSD_BLOCK_WRITE));
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100708
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709 /*
710 * DMA transfer?
711 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000712 if (host->dma >= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 /*
714 * Disable DMA on the host.
715 */
716 wbsd_write_index(host, WBSD_IDX_DMA, 0);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100717
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718 /*
719 * Turn of ISA DMA controller.
720 */
721 dmaflags = claim_dma_lock();
722 disable_dma(host->dma);
723 clear_dma_ff(host->dma);
724 count = get_dma_residue(host->dma);
725 release_dma_lock(dmaflags);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100726
Alex Dubov14d836e2007-04-13 19:04:38 +0200727 data->bytes_xfered = host->mrq->data->blocks *
728 host->mrq->data->blksz - count;
729 data->bytes_xfered -= data->bytes_xfered % data->blksz;
730
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731 /*
732 * Any leftover data?
733 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000734 if (count) {
Pierre Ossmand1916342005-11-05 10:36:35 +0000735 printk(KERN_ERR "%s: Incomplete DMA transfer. "
736 "%d bytes left.\n",
737 mmc_hostname(host->mmc), count);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100738
Alex Dubov14d836e2007-04-13 19:04:38 +0200739 if (data->error == MMC_ERR_NONE)
740 data->error = MMC_ERR_FAILED;
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000741 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742 /*
743 * Transfer data from DMA buffer to
744 * SG list.
745 */
746 if (data->flags & MMC_DATA_READ)
747 wbsd_dma_to_sg(host, data);
Alex Dubov14d836e2007-04-13 19:04:38 +0200748 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100749
Alex Dubov14d836e2007-04-13 19:04:38 +0200750 if (data->error != MMC_ERR_NONE) {
751 if (data->bytes_xfered)
752 data->bytes_xfered -= data->blksz;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753 }
754 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100755
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756 DBGF("Ending data transfer (%d bytes)\n", data->bytes_xfered);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100757
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758 wbsd_request_end(host, host->mrq);
759}
760
Pierre Ossman85bcc132005-05-08 19:35:27 +0100761/*****************************************************************************\
762 * *
763 * MMC layer callbacks *
764 * *
765\*****************************************************************************/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000767static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768{
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000769 struct wbsd_host *host = mmc_priv(mmc);
770 struct mmc_command *cmd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771
772 /*
773 * Disable tasklets to avoid a deadlock.
774 */
775 spin_lock_bh(&host->lock);
776
777 BUG_ON(host->mrq != NULL);
778
779 cmd = mrq->cmd;
780
781 host->mrq = mrq;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100782
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 /*
784 * If there is no card in the slot then
785 * timeout immediatly.
786 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000787 if (!(host->flags & WBSD_FCARD_PRESENT)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 cmd->error = MMC_ERR_TIMEOUT;
789 goto done;
790 }
791
792 /*
793 * Does the request include data?
794 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000795 if (cmd->data) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 wbsd_prepare_data(host, cmd->data);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100797
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 if (cmd->data->error != MMC_ERR_NONE)
799 goto done;
800 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100801
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 wbsd_send_command(host, cmd);
803
804 /*
805 * If this is a data transfer the request
806 * will be finished after the data has
807 * transfered.
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100808 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000809 if (cmd->data && (cmd->error == MMC_ERR_NONE)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810 /*
Pierre Ossman5ba593a2006-11-21 17:45:37 +0100811 * The hardware is so delightfully stupid that it has a list
812 * of "data" commands. If a command isn't on this list, it'll
813 * just go back to the idle state and won't send any data
814 * interrupts.
815 */
816 switch (cmd->opcode) {
817 case 11:
818 case 17:
819 case 18:
820 case 20:
821 case 24:
822 case 25:
823 case 26:
824 case 27:
825 case 30:
826 case 42:
827 case 56:
828 break;
829
830 /* ACMDs. We don't keep track of state, so we just treat them
831 * like any other command. */
832 case 51:
833 break;
834
835 default:
836#ifdef CONFIG_MMC_DEBUG
837 printk(KERN_WARNING "%s: Data command %d is not "
838 "supported by this controller.\n",
839 mmc_hostname(host->mmc), cmd->opcode);
840#endif
841 cmd->data->error = MMC_ERR_INVALID;
842
843 if (cmd->data->stop)
844 wbsd_send_command(host, cmd->data->stop);
845
846 goto done;
847 };
848
849 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 * Dirty fix for hardware bug.
851 */
852 if (host->dma == -1)
853 tasklet_schedule(&host->fifo_tasklet);
854
855 spin_unlock_bh(&host->lock);
856
857 return;
858 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100859
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860done:
861 wbsd_request_end(host, mrq);
862
863 spin_unlock_bh(&host->lock);
864}
865
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000866static void wbsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867{
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000868 struct wbsd_host *host = mmc_priv(mmc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869 u8 clk, setup, pwr;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100870
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871 spin_lock_bh(&host->lock);
872
873 /*
874 * Reset the chip on each power off.
875 * Should clear out any weird states.
876 */
877 if (ios->power_mode == MMC_POWER_OFF)
878 wbsd_init_device(host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100879
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880 if (ios->clock >= 24000000)
881 clk = WBSD_CLK_24M;
882 else if (ios->clock >= 16000000)
883 clk = WBSD_CLK_16M;
884 else if (ios->clock >= 12000000)
885 clk = WBSD_CLK_12M;
886 else
887 clk = WBSD_CLK_375K;
888
889 /*
890 * Only write to the clock register when
891 * there is an actual change.
892 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000893 if (clk != host->clk) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894 wbsd_write_index(host, WBSD_IDX_CLK, clk);
895 host->clk = clk;
896 }
897
Pierre Ossman85bcc132005-05-08 19:35:27 +0100898 /*
899 * Power up card.
900 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000901 if (ios->power_mode != MMC_POWER_OFF) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902 pwr = inb(host->base + WBSD_CSR);
903 pwr &= ~WBSD_POWER_N;
904 outb(pwr, host->base + WBSD_CSR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905 }
906
Pierre Ossman85bcc132005-05-08 19:35:27 +0100907 /*
908 * MMC cards need to have pin 1 high during init.
Pierre Ossman85bcc132005-05-08 19:35:27 +0100909 * It wreaks havoc with the card detection though so
Pierre Ossman1656fa52005-09-03 16:45:49 +0100910 * that needs to be disabled.
Pierre Ossman85bcc132005-05-08 19:35:27 +0100911 */
912 setup = wbsd_read_index(host, WBSD_IDX_SETUP);
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000913 if (ios->chip_select == MMC_CS_HIGH) {
Pierre Ossman65ae2112005-09-06 15:18:57 -0700914 BUG_ON(ios->bus_width != MMC_BUS_WIDTH_1);
Pierre Ossman85bcc132005-05-08 19:35:27 +0100915 setup |= WBSD_DAT3_H;
916 host->flags |= WBSD_FIGNORE_DETECT;
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000917 } else {
918 if (setup & WBSD_DAT3_H) {
Pierre Ossman19c1f3c2005-10-28 21:37:29 +0100919 setup &= ~WBSD_DAT3_H;
Pierre Ossman1656fa52005-09-03 16:45:49 +0100920
Pierre Ossman19c1f3c2005-10-28 21:37:29 +0100921 /*
922 * We cannot resume card detection immediatly
923 * because of capacitance and delays in the chip.
924 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000925 mod_timer(&host->ignore_timer, jiffies + HZ / 100);
Pierre Ossman19c1f3c2005-10-28 21:37:29 +0100926 }
Pierre Ossman85bcc132005-05-08 19:35:27 +0100927 }
928 wbsd_write_index(host, WBSD_IDX_SETUP, setup);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +0100929
Pierre Ossman65ae2112005-09-06 15:18:57 -0700930 /*
931 * Store bus width for later. Will be used when
932 * setting up the data transfer.
933 */
934 host->bus_width = ios->bus_width;
935
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 spin_unlock_bh(&host->lock);
937}
938
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000939static int wbsd_get_ro(struct mmc_host *mmc)
Pierre Ossman65ae2112005-09-06 15:18:57 -0700940{
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000941 struct wbsd_host *host = mmc_priv(mmc);
Pierre Ossman65ae2112005-09-06 15:18:57 -0700942 u8 csr;
943
944 spin_lock_bh(&host->lock);
945
946 csr = inb(host->base + WBSD_CSR);
947 csr |= WBSD_MSLED;
948 outb(csr, host->base + WBSD_CSR);
949
950 mdelay(1);
951
952 csr = inb(host->base + WBSD_CSR);
953 csr &= ~WBSD_MSLED;
954 outb(csr, host->base + WBSD_CSR);
955
956 spin_unlock_bh(&host->lock);
957
958 return csr & WBSD_WRPT;
959}
960
David Brownellab7aefd2006-11-12 17:55:30 -0800961static const struct mmc_host_ops wbsd_ops = {
Pierre Ossman85bcc132005-05-08 19:35:27 +0100962 .request = wbsd_request,
963 .set_ios = wbsd_set_ios,
Pierre Ossman65ae2112005-09-06 15:18:57 -0700964 .get_ro = wbsd_get_ro,
Pierre Ossman85bcc132005-05-08 19:35:27 +0100965};
966
967/*****************************************************************************\
968 * *
969 * Interrupt handling *
970 * *
971\*****************************************************************************/
972
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973/*
Pierre Ossman1656fa52005-09-03 16:45:49 +0100974 * Helper function to reset detection ignore
975 */
976
977static void wbsd_reset_ignore(unsigned long data)
978{
Pierre Ossmancfa7f522006-01-08 18:17:55 +0000979 struct wbsd_host *host = (struct wbsd_host *)data;
Pierre Ossman1656fa52005-09-03 16:45:49 +0100980
981 BUG_ON(host == NULL);
982
983 DBG("Resetting card detection ignore\n");
984
985 spin_lock_bh(&host->lock);
986
987 host->flags &= ~WBSD_FIGNORE_DETECT;
988
989 /*
990 * Card status might have changed during the
991 * blackout.
992 */
993 tasklet_schedule(&host->card_tasklet);
994
995 spin_unlock_bh(&host->lock);
996}
997
998/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999 * Tasklets
1000 */
1001
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001002static inline struct mmc_data *wbsd_get_data(struct wbsd_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003{
1004 WARN_ON(!host->mrq);
1005 if (!host->mrq)
1006 return NULL;
1007
1008 WARN_ON(!host->mrq->cmd);
1009 if (!host->mrq->cmd)
1010 return NULL;
1011
1012 WARN_ON(!host->mrq->cmd->data);
1013 if (!host->mrq->cmd->data)
1014 return NULL;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001015
Linus Torvalds1da177e2005-04-16 15:20:36 -07001016 return host->mrq->cmd->data;
1017}
1018
1019static void wbsd_tasklet_card(unsigned long param)
1020{
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001021 struct wbsd_host *host = (struct wbsd_host *)param;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022 u8 csr;
Pierre Ossman210ce2a2005-09-12 20:36:19 +01001023 int delay = -1;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001024
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025 spin_lock(&host->lock);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001026
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001027 if (host->flags & WBSD_FIGNORE_DETECT) {
Pierre Ossman85bcc132005-05-08 19:35:27 +01001028 spin_unlock(&host->lock);
1029 return;
1030 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001031
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032 csr = inb(host->base + WBSD_CSR);
1033 WARN_ON(csr == 0xff);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001034
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001035 if (csr & WBSD_CARDPRESENT) {
1036 if (!(host->flags & WBSD_FCARD_PRESENT)) {
Pierre Ossman85bcc132005-05-08 19:35:27 +01001037 DBG("Card inserted\n");
1038 host->flags |= WBSD_FCARD_PRESENT;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001039
Pierre Ossman210ce2a2005-09-12 20:36:19 +01001040 delay = 500;
Pierre Ossman85bcc132005-05-08 19:35:27 +01001041 }
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001042 } else if (host->flags & WBSD_FCARD_PRESENT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043 DBG("Card removed\n");
Pierre Ossman85bcc132005-05-08 19:35:27 +01001044 host->flags &= ~WBSD_FCARD_PRESENT;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001045
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001046 if (host->mrq) {
Pierre Ossmand1916342005-11-05 10:36:35 +00001047 printk(KERN_ERR "%s: Card removed during transfer!\n",
1048 mmc_hostname(host->mmc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049 wbsd_reset(host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001050
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051 host->mrq->cmd->error = MMC_ERR_FAILED;
1052 tasklet_schedule(&host->finish_tasklet);
1053 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001054
Pierre Ossman210ce2a2005-09-12 20:36:19 +01001055 delay = 0;
Pierre Ossman6e6293d2005-07-01 12:13:55 +01001056 }
Pierre Ossman210ce2a2005-09-12 20:36:19 +01001057
1058 /*
1059 * Unlock first since we might get a call back.
1060 */
1061
1062 spin_unlock(&host->lock);
1063
1064 if (delay != -1)
1065 mmc_detect_change(host->mmc, msecs_to_jiffies(delay));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066}
1067
1068static void wbsd_tasklet_fifo(unsigned long param)
1069{
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001070 struct wbsd_host *host = (struct wbsd_host *)param;
1071 struct mmc_data *data;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001072
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 spin_lock(&host->lock);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001074
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075 if (!host->mrq)
1076 goto end;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001077
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078 data = wbsd_get_data(host);
1079 if (!data)
1080 goto end;
1081
1082 if (data->flags & MMC_DATA_WRITE)
1083 wbsd_fill_fifo(host);
1084 else
1085 wbsd_empty_fifo(host);
1086
1087 /*
1088 * Done?
1089 */
Alex Dubov14d836e2007-04-13 19:04:38 +02001090 if (host->num_sg == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091 wbsd_write_index(host, WBSD_IDX_FIFOEN, 0);
1092 tasklet_schedule(&host->finish_tasklet);
1093 }
1094
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001095end:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096 spin_unlock(&host->lock);
1097}
1098
1099static void wbsd_tasklet_crc(unsigned long param)
1100{
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001101 struct wbsd_host *host = (struct wbsd_host *)param;
1102 struct mmc_data *data;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001103
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104 spin_lock(&host->lock);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001105
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106 if (!host->mrq)
1107 goto end;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001108
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109 data = wbsd_get_data(host);
1110 if (!data)
1111 goto end;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001112
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113 DBGF("CRC error\n");
1114
1115 data->error = MMC_ERR_BADCRC;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001116
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117 tasklet_schedule(&host->finish_tasklet);
1118
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001119end:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120 spin_unlock(&host->lock);
1121}
1122
1123static void wbsd_tasklet_timeout(unsigned long param)
1124{
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001125 struct wbsd_host *host = (struct wbsd_host *)param;
1126 struct mmc_data *data;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001127
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128 spin_lock(&host->lock);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001129
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130 if (!host->mrq)
1131 goto end;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001132
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133 data = wbsd_get_data(host);
1134 if (!data)
1135 goto end;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001136
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137 DBGF("Timeout\n");
1138
1139 data->error = MMC_ERR_TIMEOUT;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001140
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141 tasklet_schedule(&host->finish_tasklet);
1142
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001143end:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 spin_unlock(&host->lock);
1145}
1146
1147static void wbsd_tasklet_finish(unsigned long param)
1148{
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001149 struct wbsd_host *host = (struct wbsd_host *)param;
1150 struct mmc_data *data;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001151
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 spin_lock(&host->lock);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001153
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 WARN_ON(!host->mrq);
1155 if (!host->mrq)
1156 goto end;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001157
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158 data = wbsd_get_data(host);
1159 if (!data)
1160 goto end;
1161
1162 wbsd_finish_data(host, data);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001163
1164end:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 spin_unlock(&host->lock);
1166}
1167
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168/*
1169 * Interrupt handling
1170 */
1171
David Howells7d12e782006-10-05 14:55:46 +01001172static irqreturn_t wbsd_irq(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173{
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001174 struct wbsd_host *host = dev_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175 int isr;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001176
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 isr = inb(host->base + WBSD_ISR);
1178
1179 /*
1180 * Was it actually our hardware that caused the interrupt?
1181 */
1182 if (isr == 0xff || isr == 0x00)
1183 return IRQ_NONE;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001184
Linus Torvalds1da177e2005-04-16 15:20:36 -07001185 host->isr |= isr;
1186
1187 /*
1188 * Schedule tasklets as needed.
1189 */
1190 if (isr & WBSD_INT_CARD)
1191 tasklet_schedule(&host->card_tasklet);
1192 if (isr & WBSD_INT_FIFO_THRE)
1193 tasklet_schedule(&host->fifo_tasklet);
1194 if (isr & WBSD_INT_CRC)
1195 tasklet_hi_schedule(&host->crc_tasklet);
1196 if (isr & WBSD_INT_TIMEOUT)
1197 tasklet_hi_schedule(&host->timeout_tasklet);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198 if (isr & WBSD_INT_TC)
1199 tasklet_schedule(&host->finish_tasklet);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001200
Linus Torvalds1da177e2005-04-16 15:20:36 -07001201 return IRQ_HANDLED;
1202}
1203
Pierre Ossman85bcc132005-05-08 19:35:27 +01001204/*****************************************************************************\
1205 * *
1206 * Device initialisation and shutdown *
1207 * *
1208\*****************************************************************************/
1209
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210/*
Pierre Ossman85bcc132005-05-08 19:35:27 +01001211 * Allocate/free MMC structure.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212 */
1213
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001214static int __devinit wbsd_alloc_mmc(struct device *dev)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001215{
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001216 struct mmc_host *mmc;
1217 struct wbsd_host *host;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001218
Pierre Ossman85bcc132005-05-08 19:35:27 +01001219 /*
1220 * Allocate MMC structure.
1221 */
1222 mmc = mmc_alloc_host(sizeof(struct wbsd_host), dev);
1223 if (!mmc)
1224 return -ENOMEM;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001225
Pierre Ossman85bcc132005-05-08 19:35:27 +01001226 host = mmc_priv(mmc);
1227 host->mmc = mmc;
1228
1229 host->dma = -1;
1230
1231 /*
1232 * Set host parameters.
1233 */
1234 mmc->ops = &wbsd_ops;
1235 mmc->f_min = 375000;
1236 mmc->f_max = 24000000;
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001237 mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
Russell King42431ac2006-09-24 10:44:09 +01001238 mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001239
Pierre Ossman85bcc132005-05-08 19:35:27 +01001240 spin_lock_init(&host->lock);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001241
Pierre Ossman85bcc132005-05-08 19:35:27 +01001242 /*
Pierre Ossman1656fa52005-09-03 16:45:49 +01001243 * Set up timers
Pierre Ossman6e6293d2005-07-01 12:13:55 +01001244 */
Pierre Ossman1656fa52005-09-03 16:45:49 +01001245 init_timer(&host->ignore_timer);
1246 host->ignore_timer.data = (unsigned long)host;
1247 host->ignore_timer.function = wbsd_reset_ignore;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001248
Pierre Ossman6e6293d2005-07-01 12:13:55 +01001249 /*
Pierre Ossman85bcc132005-05-08 19:35:27 +01001250 * Maximum number of segments. Worst case is one sector per segment
1251 * so this will be 64kB/512.
1252 */
1253 mmc->max_hw_segs = 128;
1254 mmc->max_phys_segs = 128;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001255
Pierre Ossman85bcc132005-05-08 19:35:27 +01001256 /*
Pierre Ossman55db8902006-11-21 17:55:45 +01001257 * Maximum request size. Also limited by 64KiB buffer.
Pierre Ossman85bcc132005-05-08 19:35:27 +01001258 */
Pierre Ossman55db8902006-11-21 17:55:45 +01001259 mmc->max_req_size = 65536;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001260
Pierre Ossman85bcc132005-05-08 19:35:27 +01001261 /*
1262 * Maximum segment size. Could be one segment with the maximum number
Pierre Ossman55db8902006-11-21 17:55:45 +01001263 * of bytes.
Pierre Ossman85bcc132005-05-08 19:35:27 +01001264 */
Pierre Ossman55db8902006-11-21 17:55:45 +01001265 mmc->max_seg_size = mmc->max_req_size;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001266
Pierre Ossmanfe4a3c72006-11-21 17:54:23 +01001267 /*
1268 * Maximum block size. We have 12 bits (= 4095) but have to subtract
1269 * space for CRC. So the maximum is 4095 - 4*2 = 4087.
1270 */
1271 mmc->max_blk_size = 4087;
1272
Pierre Ossman55db8902006-11-21 17:55:45 +01001273 /*
1274 * Maximum block count. There is no real limit so the maximum
1275 * request size will be the only restriction.
1276 */
1277 mmc->max_blk_count = mmc->max_req_size;
1278
Pierre Ossman85bcc132005-05-08 19:35:27 +01001279 dev_set_drvdata(dev, mmc);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001280
Pierre Ossman85bcc132005-05-08 19:35:27 +01001281 return 0;
1282}
1283
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001284static void __devexit wbsd_free_mmc(struct device *dev)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001285{
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001286 struct mmc_host *mmc;
1287 struct wbsd_host *host;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001288
Pierre Ossman85bcc132005-05-08 19:35:27 +01001289 mmc = dev_get_drvdata(dev);
1290 if (!mmc)
1291 return;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001292
Pierre Ossman6e6293d2005-07-01 12:13:55 +01001293 host = mmc_priv(mmc);
1294 BUG_ON(host == NULL);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001295
Pierre Ossman1656fa52005-09-03 16:45:49 +01001296 del_timer_sync(&host->ignore_timer);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001297
Pierre Ossman85bcc132005-05-08 19:35:27 +01001298 mmc_free_host(mmc);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001299
Pierre Ossman85bcc132005-05-08 19:35:27 +01001300 dev_set_drvdata(dev, NULL);
1301}
1302
1303/*
1304 * Scan for known chip id:s
1305 */
1306
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001307static int __devinit wbsd_scan(struct wbsd_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308{
1309 int i, j, k;
1310 int id;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001311
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312 /*
1313 * Iterate through all ports, all codes to
1314 * find hardware that is in our known list.
1315 */
Dmitry Torokhov63648fb2006-01-03 22:56:56 +00001316 for (i = 0; i < ARRAY_SIZE(config_ports); i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317 if (!request_region(config_ports[i], 2, DRIVER_NAME))
1318 continue;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001319
Dmitry Torokhov63648fb2006-01-03 22:56:56 +00001320 for (j = 0; j < ARRAY_SIZE(unlock_codes); j++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321 id = 0xFFFF;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001322
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001323 host->config = config_ports[i];
1324 host->unlock_code = unlock_codes[j];
1325
1326 wbsd_unlock_config(host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001327
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328 outb(WBSD_CONF_ID_HI, config_ports[i]);
1329 id = inb(config_ports[i] + 1) << 8;
1330
1331 outb(WBSD_CONF_ID_LO, config_ports[i]);
1332 id |= inb(config_ports[i] + 1);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001333
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001334 wbsd_lock_config(host);
1335
Dmitry Torokhov63648fb2006-01-03 22:56:56 +00001336 for (k = 0; k < ARRAY_SIZE(valid_ids); k++) {
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001337 if (id == valid_ids[k]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338 host->chip_id = id;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001339
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340 return 0;
1341 }
1342 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001343
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001344 if (id != 0xFFFF) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345 DBG("Unknown hardware (id %x) found at %x\n",
1346 id, config_ports[i]);
1347 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001349
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350 release_region(config_ports[i], 2);
1351 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001352
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001353 host->config = 0;
1354 host->unlock_code = 0;
1355
Linus Torvalds1da177e2005-04-16 15:20:36 -07001356 return -ENODEV;
1357}
1358
Pierre Ossman85bcc132005-05-08 19:35:27 +01001359/*
1360 * Allocate/free io port ranges
1361 */
1362
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001363static int __devinit wbsd_request_region(struct wbsd_host *host, int base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364{
Pierre Ossman916f3ac2006-08-06 22:22:23 +02001365 if (base & 0x7)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366 return -EINVAL;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001367
Pierre Ossman85bcc132005-05-08 19:35:27 +01001368 if (!request_region(base, 8, DRIVER_NAME))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369 return -EIO;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001370
Pierre Ossman916f3ac2006-08-06 22:22:23 +02001371 host->base = base;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001372
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373 return 0;
1374}
1375
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001376static void __devexit wbsd_release_regions(struct wbsd_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377{
1378 if (host->base)
1379 release_region(host->base, 8);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001380
Pierre Ossman85bcc132005-05-08 19:35:27 +01001381 host->base = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001382
1383 if (host->config)
1384 release_region(host->config, 2);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001385
Pierre Ossman85bcc132005-05-08 19:35:27 +01001386 host->config = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001387}
1388
Pierre Ossman85bcc132005-05-08 19:35:27 +01001389/*
1390 * Allocate/free DMA port and buffer
1391 */
1392
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001393static void __devinit wbsd_request_dma(struct wbsd_host *host, int dma)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395 if (dma < 0)
1396 return;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001397
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398 if (request_dma(dma, DRIVER_NAME))
1399 goto err;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001400
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401 /*
1402 * We need to allocate a special buffer in
1403 * order for ISA to be able to DMA to it.
1404 */
Pierre Ossman85bcc132005-05-08 19:35:27 +01001405 host->dma_buffer = kmalloc(WBSD_DMA_SIZE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001406 GFP_NOIO | GFP_DMA | __GFP_REPEAT | __GFP_NOWARN);
1407 if (!host->dma_buffer)
1408 goto free;
1409
1410 /*
1411 * Translate the address to a physical address.
1412 */
Greg Kroah-Hartmanfcaf71f2006-09-12 17:00:10 +02001413 host->dma_addr = dma_map_single(mmc_dev(host->mmc), host->dma_buffer,
Pierre Ossman85bcc132005-05-08 19:35:27 +01001414 WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001415
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416 /*
1417 * ISA DMA must be aligned on a 64k basis.
1418 */
1419 if ((host->dma_addr & 0xffff) != 0)
1420 goto kfree;
1421 /*
1422 * ISA cannot access memory above 16 MB.
1423 */
1424 else if (host->dma_addr >= 0x1000000)
1425 goto kfree;
1426
1427 host->dma = dma;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001428
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429 return;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001430
Linus Torvalds1da177e2005-04-16 15:20:36 -07001431kfree:
1432 /*
1433 * If we've gotten here then there is some kind of alignment bug
1434 */
1435 BUG_ON(1);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001436
Greg Kroah-Hartmanfcaf71f2006-09-12 17:00:10 +02001437 dma_unmap_single(mmc_dev(host->mmc), host->dma_addr,
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001438 WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
Pierre Ossman85bcc132005-05-08 19:35:27 +01001439 host->dma_addr = (dma_addr_t)NULL;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001440
Linus Torvalds1da177e2005-04-16 15:20:36 -07001441 kfree(host->dma_buffer);
1442 host->dma_buffer = NULL;
1443
1444free:
1445 free_dma(dma);
1446
1447err:
1448 printk(KERN_WARNING DRIVER_NAME ": Unable to allocate DMA %d. "
1449 "Falling back on FIFO.\n", dma);
1450}
1451
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001452static void __devexit wbsd_release_dma(struct wbsd_host *host)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001453{
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001454 if (host->dma_addr) {
Greg Kroah-Hartmanfcaf71f2006-09-12 17:00:10 +02001455 dma_unmap_single(mmc_dev(host->mmc), host->dma_addr,
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001456 WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
1457 }
Jesper Juhl6044ec82005-11-07 01:01:32 -08001458 kfree(host->dma_buffer);
Pierre Ossman85bcc132005-05-08 19:35:27 +01001459 if (host->dma >= 0)
1460 free_dma(host->dma);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001461
Pierre Ossman85bcc132005-05-08 19:35:27 +01001462 host->dma = -1;
1463 host->dma_buffer = NULL;
1464 host->dma_addr = (dma_addr_t)NULL;
1465}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001466
1467/*
Pierre Ossman85bcc132005-05-08 19:35:27 +01001468 * Allocate/free IRQ.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001469 */
1470
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001471static int __devinit wbsd_request_irq(struct wbsd_host *host, int irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001472{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001473 int ret;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001474
Linus Torvalds1da177e2005-04-16 15:20:36 -07001475 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001476 * Allocate interrupt.
1477 */
Pierre Ossman85bcc132005-05-08 19:35:27 +01001478
Thomas Gleixnerdace1452006-07-01 19:29:38 -07001479 ret = request_irq(irq, wbsd_irq, IRQF_SHARED, DRIVER_NAME, host);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001480 if (ret)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001481 return ret;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001482
Linus Torvalds1da177e2005-04-16 15:20:36 -07001483 host->irq = irq;
Pierre Ossman85bcc132005-05-08 19:35:27 +01001484
Linus Torvalds1da177e2005-04-16 15:20:36 -07001485 /*
1486 * Set up tasklets.
1487 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001488 tasklet_init(&host->card_tasklet, wbsd_tasklet_card,
1489 (unsigned long)host);
1490 tasklet_init(&host->fifo_tasklet, wbsd_tasklet_fifo,
1491 (unsigned long)host);
1492 tasklet_init(&host->crc_tasklet, wbsd_tasklet_crc,
1493 (unsigned long)host);
1494 tasklet_init(&host->timeout_tasklet, wbsd_tasklet_timeout,
1495 (unsigned long)host);
1496 tasklet_init(&host->finish_tasklet, wbsd_tasklet_finish,
1497 (unsigned long)host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001498
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500}
1501
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001502static void __devexit wbsd_release_irq(struct wbsd_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001503{
Pierre Ossman85bcc132005-05-08 19:35:27 +01001504 if (!host->irq)
1505 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001506
1507 free_irq(host->irq, host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001508
Pierre Ossman85bcc132005-05-08 19:35:27 +01001509 host->irq = 0;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001510
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511 tasklet_kill(&host->card_tasklet);
1512 tasklet_kill(&host->fifo_tasklet);
1513 tasklet_kill(&host->crc_tasklet);
1514 tasklet_kill(&host->timeout_tasklet);
1515 tasklet_kill(&host->finish_tasklet);
Pierre Ossman85bcc132005-05-08 19:35:27 +01001516}
1517
1518/*
1519 * Allocate all resources for the host.
1520 */
1521
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001522static int __devinit wbsd_request_resources(struct wbsd_host *host,
Pierre Ossman85bcc132005-05-08 19:35:27 +01001523 int base, int irq, int dma)
1524{
1525 int ret;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001526
Pierre Ossman85bcc132005-05-08 19:35:27 +01001527 /*
1528 * Allocate I/O ports.
1529 */
1530 ret = wbsd_request_region(host, base);
1531 if (ret)
1532 return ret;
1533
1534 /*
1535 * Allocate interrupt.
1536 */
1537 ret = wbsd_request_irq(host, irq);
1538 if (ret)
1539 return ret;
1540
1541 /*
1542 * Allocate DMA.
1543 */
1544 wbsd_request_dma(host, dma);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001545
Pierre Ossman85bcc132005-05-08 19:35:27 +01001546 return 0;
1547}
1548
1549/*
1550 * Release all resources for the host.
1551 */
1552
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001553static void __devexit wbsd_release_resources(struct wbsd_host *host)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001554{
1555 wbsd_release_dma(host);
1556 wbsd_release_irq(host);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557 wbsd_release_regions(host);
Pierre Ossman85bcc132005-05-08 19:35:27 +01001558}
1559
1560/*
1561 * Configure the resources the chip should use.
1562 */
1563
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001564static void wbsd_chip_config(struct wbsd_host *host)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001565{
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001566 wbsd_unlock_config(host);
1567
Pierre Ossman85bcc132005-05-08 19:35:27 +01001568 /*
1569 * Reset the chip.
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001570 */
Pierre Ossman85bcc132005-05-08 19:35:27 +01001571 wbsd_write_config(host, WBSD_CONF_SWRST, 1);
1572 wbsd_write_config(host, WBSD_CONF_SWRST, 0);
1573
1574 /*
1575 * Select SD/MMC function.
1576 */
1577 wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001578
Pierre Ossman85bcc132005-05-08 19:35:27 +01001579 /*
1580 * Set up card detection.
1581 */
1582 wbsd_write_config(host, WBSD_CONF_PINS, WBSD_PINS_DETECT_GP11);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001583
Pierre Ossman85bcc132005-05-08 19:35:27 +01001584 /*
1585 * Configure chip
1586 */
1587 wbsd_write_config(host, WBSD_CONF_PORT_HI, host->base >> 8);
1588 wbsd_write_config(host, WBSD_CONF_PORT_LO, host->base & 0xff);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001589
Pierre Ossman85bcc132005-05-08 19:35:27 +01001590 wbsd_write_config(host, WBSD_CONF_IRQ, host->irq);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001591
Pierre Ossman85bcc132005-05-08 19:35:27 +01001592 if (host->dma >= 0)
1593 wbsd_write_config(host, WBSD_CONF_DRQ, host->dma);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001594
Pierre Ossman85bcc132005-05-08 19:35:27 +01001595 /*
1596 * Enable and power up chip.
1597 */
1598 wbsd_write_config(host, WBSD_CONF_ENABLE, 1);
1599 wbsd_write_config(host, WBSD_CONF_POWER, 0x20);
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001600
1601 wbsd_lock_config(host);
Pierre Ossman85bcc132005-05-08 19:35:27 +01001602}
1603
1604/*
1605 * Check that configured resources are correct.
1606 */
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001607
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001608static int wbsd_chip_validate(struct wbsd_host *host)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001609{
1610 int base, irq, dma;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001611
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001612 wbsd_unlock_config(host);
1613
Pierre Ossman85bcc132005-05-08 19:35:27 +01001614 /*
1615 * Select SD/MMC function.
1616 */
1617 wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001618
Pierre Ossman85bcc132005-05-08 19:35:27 +01001619 /*
1620 * Read configuration.
1621 */
1622 base = wbsd_read_config(host, WBSD_CONF_PORT_HI) << 8;
1623 base |= wbsd_read_config(host, WBSD_CONF_PORT_LO);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001624
Pierre Ossman85bcc132005-05-08 19:35:27 +01001625 irq = wbsd_read_config(host, WBSD_CONF_IRQ);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001626
Pierre Ossman85bcc132005-05-08 19:35:27 +01001627 dma = wbsd_read_config(host, WBSD_CONF_DRQ);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001628
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001629 wbsd_lock_config(host);
1630
Pierre Ossman85bcc132005-05-08 19:35:27 +01001631 /*
1632 * Validate against given configuration.
1633 */
1634 if (base != host->base)
1635 return 0;
1636 if (irq != host->irq)
1637 return 0;
1638 if ((dma != host->dma) && (host->dma != -1))
1639 return 0;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001640
Pierre Ossman85bcc132005-05-08 19:35:27 +01001641 return 1;
1642}
1643
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001644/*
1645 * Powers down the SD function
1646 */
1647
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001648static void wbsd_chip_poweroff(struct wbsd_host *host)
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001649{
1650 wbsd_unlock_config(host);
1651
1652 wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
1653 wbsd_write_config(host, WBSD_CONF_ENABLE, 0);
1654
1655 wbsd_lock_config(host);
1656}
1657
Pierre Ossman85bcc132005-05-08 19:35:27 +01001658/*****************************************************************************\
1659 * *
1660 * Devices setup and shutdown *
1661 * *
1662\*****************************************************************************/
1663
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001664static int __devinit wbsd_init(struct device *dev, int base, int irq, int dma,
Pierre Ossman85bcc132005-05-08 19:35:27 +01001665 int pnp)
1666{
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001667 struct wbsd_host *host = NULL;
1668 struct mmc_host *mmc = NULL;
Pierre Ossman85bcc132005-05-08 19:35:27 +01001669 int ret;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001670
Pierre Ossman85bcc132005-05-08 19:35:27 +01001671 ret = wbsd_alloc_mmc(dev);
1672 if (ret)
1673 return ret;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001674
Pierre Ossman85bcc132005-05-08 19:35:27 +01001675 mmc = dev_get_drvdata(dev);
1676 host = mmc_priv(mmc);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001677
Pierre Ossman85bcc132005-05-08 19:35:27 +01001678 /*
1679 * Scan for hardware.
1680 */
1681 ret = wbsd_scan(host);
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001682 if (ret) {
1683 if (pnp && (ret == -ENODEV)) {
Pierre Ossman85bcc132005-05-08 19:35:27 +01001684 printk(KERN_WARNING DRIVER_NAME
1685 ": Unable to confirm device presence. You may "
1686 "experience lock-ups.\n");
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001687 } else {
Pierre Ossman85bcc132005-05-08 19:35:27 +01001688 wbsd_free_mmc(dev);
1689 return ret;
1690 }
1691 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001692
Pierre Ossman85bcc132005-05-08 19:35:27 +01001693 /*
1694 * Request resources.
1695 */
Pierre Ossmandd2c6092006-08-07 01:40:04 +02001696 ret = wbsd_request_resources(host, base, irq, dma);
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001697 if (ret) {
Pierre Ossman85bcc132005-05-08 19:35:27 +01001698 wbsd_release_resources(host);
1699 wbsd_free_mmc(dev);
1700 return ret;
1701 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001702
Pierre Ossman85bcc132005-05-08 19:35:27 +01001703 /*
1704 * See if chip needs to be configured.
1705 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001706 if (pnp) {
1707 if ((host->config != 0) && !wbsd_chip_validate(host)) {
Pierre Ossman85bcc132005-05-08 19:35:27 +01001708 printk(KERN_WARNING DRIVER_NAME
1709 ": PnP active but chip not configured! "
1710 "You probably have a buggy BIOS. "
1711 "Configuring chip manually.\n");
1712 wbsd_chip_config(host);
1713 }
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001714 } else
Pierre Ossman85bcc132005-05-08 19:35:27 +01001715 wbsd_chip_config(host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001716
Pierre Ossman85bcc132005-05-08 19:35:27 +01001717 /*
1718 * Power Management stuff. No idea how this works.
1719 * Not tested.
1720 */
1721#ifdef CONFIG_PM
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001722 if (host->config) {
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001723 wbsd_unlock_config(host);
Pierre Ossman85bcc132005-05-08 19:35:27 +01001724 wbsd_write_config(host, WBSD_CONF_PME, 0xA0);
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001725 wbsd_lock_config(host);
1726 }
Pierre Ossman85bcc132005-05-08 19:35:27 +01001727#endif
1728 /*
1729 * Allow device to initialise itself properly.
1730 */
1731 mdelay(5);
1732
1733 /*
1734 * Reset the chip into a known state.
1735 */
1736 wbsd_init_device(host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001737
Pierre Ossman85bcc132005-05-08 19:35:27 +01001738 mmc_add_host(mmc);
1739
Russell Kingd366b642005-08-19 09:40:08 +01001740 printk(KERN_INFO "%s: W83L51xD", mmc_hostname(mmc));
Pierre Ossman85bcc132005-05-08 19:35:27 +01001741 if (host->chip_id != 0)
1742 printk(" id %x", (int)host->chip_id);
1743 printk(" at 0x%x irq %d", (int)host->base, (int)host->irq);
1744 if (host->dma >= 0)
1745 printk(" dma %d", (int)host->dma);
1746 else
1747 printk(" FIFO");
1748 if (pnp)
1749 printk(" PnP");
1750 printk("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001751
1752 return 0;
1753}
1754
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001755static void __devexit wbsd_shutdown(struct device *dev, int pnp)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001756{
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001757 struct mmc_host *mmc = dev_get_drvdata(dev);
1758 struct wbsd_host *host;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001759
Pierre Ossman85bcc132005-05-08 19:35:27 +01001760 if (!mmc)
1761 return;
1762
1763 host = mmc_priv(mmc);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001764
Pierre Ossman85bcc132005-05-08 19:35:27 +01001765 mmc_remove_host(mmc);
1766
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001767 /*
1768 * Power down the SD/MMC function.
1769 */
Pierre Ossman85bcc132005-05-08 19:35:27 +01001770 if (!pnp)
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001771 wbsd_chip_poweroff(host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001772
Pierre Ossman85bcc132005-05-08 19:35:27 +01001773 wbsd_release_resources(host);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001774
Pierre Ossman85bcc132005-05-08 19:35:27 +01001775 wbsd_free_mmc(dev);
1776}
1777
1778/*
1779 * Non-PnP
1780 */
1781
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001782static int __devinit wbsd_probe(struct platform_device *dev)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001783{
Pierre Ossmandd2c6092006-08-07 01:40:04 +02001784 /* Use the module parameters for resources */
Russell King3ae5eae2005-11-09 22:32:44 +00001785 return wbsd_init(&dev->dev, io, irq, dma, 0);
Pierre Ossman85bcc132005-05-08 19:35:27 +01001786}
1787
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001788static int __devexit wbsd_remove(struct platform_device *dev)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001789{
Russell King3ae5eae2005-11-09 22:32:44 +00001790 wbsd_shutdown(&dev->dev, 0);
Pierre Ossman85bcc132005-05-08 19:35:27 +01001791
1792 return 0;
1793}
1794
1795/*
1796 * PnP
1797 */
1798
1799#ifdef CONFIG_PNP
1800
1801static int __devinit
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001802wbsd_pnp_probe(struct pnp_dev *pnpdev, const struct pnp_device_id *dev_id)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001803{
1804 int io, irq, dma;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001805
Pierre Ossman85bcc132005-05-08 19:35:27 +01001806 /*
1807 * Get resources from PnP layer.
1808 */
1809 io = pnp_port_start(pnpdev, 0);
1810 irq = pnp_irq(pnpdev, 0);
1811 if (pnp_dma_valid(pnpdev, 0))
1812 dma = pnp_dma(pnpdev, 0);
1813 else
1814 dma = -1;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001815
Pierre Ossman85bcc132005-05-08 19:35:27 +01001816 DBGF("PnP resources: port %3x irq %d dma %d\n", io, irq, dma);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001817
Pierre Ossman85bcc132005-05-08 19:35:27 +01001818 return wbsd_init(&pnpdev->dev, io, irq, dma, 1);
1819}
1820
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001821static void __devexit wbsd_pnp_remove(struct pnp_dev *dev)
Pierre Ossman85bcc132005-05-08 19:35:27 +01001822{
1823 wbsd_shutdown(&dev->dev, 1);
1824}
1825
1826#endif /* CONFIG_PNP */
1827
Linus Torvalds1da177e2005-04-16 15:20:36 -07001828/*
1829 * Power management
1830 */
1831
1832#ifdef CONFIG_PM
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001833
Pierre Ossman5e68d952006-01-08 14:21:52 +00001834static int wbsd_suspend(struct wbsd_host *host, pm_message_t state)
1835{
1836 BUG_ON(host == NULL);
1837
1838 return mmc_suspend_host(host->mmc, state);
1839}
1840
1841static int wbsd_resume(struct wbsd_host *host)
1842{
1843 BUG_ON(host == NULL);
1844
1845 wbsd_init_device(host);
1846
1847 return mmc_resume_host(host->mmc);
1848}
1849
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001850static int wbsd_platform_suspend(struct platform_device *dev,
1851 pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001852{
Russell King3ae5eae2005-11-09 22:32:44 +00001853 struct mmc_host *mmc = platform_get_drvdata(dev);
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001854 struct wbsd_host *host;
1855 int ret;
1856
Pierre Ossman5e68d952006-01-08 14:21:52 +00001857 if (mmc == NULL)
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001858 return 0;
1859
Pierre Ossman5e68d952006-01-08 14:21:52 +00001860 DBGF("Suspending...\n");
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001861
1862 host = mmc_priv(mmc);
1863
Pierre Ossman5e68d952006-01-08 14:21:52 +00001864 ret = wbsd_suspend(host, state);
1865 if (ret)
1866 return ret;
1867
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001868 wbsd_chip_poweroff(host);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001869
1870 return 0;
1871}
1872
Pierre Ossman5e68d952006-01-08 14:21:52 +00001873static int wbsd_platform_resume(struct platform_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001874{
Russell King3ae5eae2005-11-09 22:32:44 +00001875 struct mmc_host *mmc = platform_get_drvdata(dev);
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001876 struct wbsd_host *host;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001877
Pierre Ossman5e68d952006-01-08 14:21:52 +00001878 if (mmc == NULL)
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001879 return 0;
1880
Pierre Ossman5e68d952006-01-08 14:21:52 +00001881 DBGF("Resuming...\n");
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001882
1883 host = mmc_priv(mmc);
1884
1885 wbsd_chip_config(host);
1886
1887 /*
1888 * Allow device to initialise itself properly.
1889 */
1890 mdelay(5);
1891
Pierre Ossman5e68d952006-01-08 14:21:52 +00001892 return wbsd_resume(host);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001893}
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001894
Pierre Ossman5e68d952006-01-08 14:21:52 +00001895#ifdef CONFIG_PNP
1896
1897static int wbsd_pnp_suspend(struct pnp_dev *pnp_dev, pm_message_t state)
1898{
1899 struct mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev);
1900 struct wbsd_host *host;
1901
1902 if (mmc == NULL)
1903 return 0;
1904
1905 DBGF("Suspending...\n");
1906
1907 host = mmc_priv(mmc);
1908
1909 return wbsd_suspend(host, state);
1910}
1911
1912static int wbsd_pnp_resume(struct pnp_dev *pnp_dev)
1913{
1914 struct mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev);
1915 struct wbsd_host *host;
1916
1917 if (mmc == NULL)
1918 return 0;
1919
1920 DBGF("Resuming...\n");
1921
1922 host = mmc_priv(mmc);
1923
1924 /*
1925 * See if chip needs to be configured.
1926 */
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001927 if (host->config != 0) {
1928 if (!wbsd_chip_validate(host)) {
Pierre Ossman5e68d952006-01-08 14:21:52 +00001929 printk(KERN_WARNING DRIVER_NAME
1930 ": PnP active but chip not configured! "
1931 "You probably have a buggy BIOS. "
1932 "Configuring chip manually.\n");
1933 wbsd_chip_config(host);
1934 }
1935 }
1936
1937 /*
1938 * Allow device to initialise itself properly.
1939 */
1940 mdelay(5);
1941
1942 return wbsd_resume(host);
1943}
1944
1945#endif /* CONFIG_PNP */
1946
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001947#else /* CONFIG_PM */
1948
Pierre Ossman5e68d952006-01-08 14:21:52 +00001949#define wbsd_platform_suspend NULL
1950#define wbsd_platform_resume NULL
1951
1952#define wbsd_pnp_suspend NULL
1953#define wbsd_pnp_resume NULL
Pierre Ossman19c1f3c2005-10-28 21:37:29 +01001954
1955#endif /* CONFIG_PM */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001956
Pierre Ossman85bcc132005-05-08 19:35:27 +01001957static struct platform_device *wbsd_device;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001958
Russell King3ae5eae2005-11-09 22:32:44 +00001959static struct platform_driver wbsd_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001960 .probe = wbsd_probe,
Pierre Ossman93968d72005-11-09 23:21:06 +00001961 .remove = __devexit_p(wbsd_remove),
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001962
Pierre Ossman5e68d952006-01-08 14:21:52 +00001963 .suspend = wbsd_platform_suspend,
1964 .resume = wbsd_platform_resume,
Russell King3ae5eae2005-11-09 22:32:44 +00001965 .driver = {
1966 .name = DRIVER_NAME,
1967 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001968};
1969
Pierre Ossman85bcc132005-05-08 19:35:27 +01001970#ifdef CONFIG_PNP
1971
1972static struct pnp_driver wbsd_pnp_driver = {
1973 .name = DRIVER_NAME,
1974 .id_table = pnp_dev_table,
1975 .probe = wbsd_pnp_probe,
Pierre Ossman93968d72005-11-09 23:21:06 +00001976 .remove = __devexit_p(wbsd_pnp_remove),
Pierre Ossman5e68d952006-01-08 14:21:52 +00001977
1978 .suspend = wbsd_pnp_suspend,
1979 .resume = wbsd_pnp_resume,
Pierre Ossman85bcc132005-05-08 19:35:27 +01001980};
1981
1982#endif /* CONFIG_PNP */
1983
Linus Torvalds1da177e2005-04-16 15:20:36 -07001984/*
1985 * Module loading/unloading
1986 */
1987
1988static int __init wbsd_drv_init(void)
1989{
1990 int result;
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01001991
Linus Torvalds1da177e2005-04-16 15:20:36 -07001992 printk(KERN_INFO DRIVER_NAME
Pierre Ossman1615cc22007-02-09 08:19:07 +01001993 ": Winbond W83L51xD SD/MMC card interface driver\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001994 printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001995
Pierre Ossman85bcc132005-05-08 19:35:27 +01001996#ifdef CONFIG_PNP
1997
Pierre Ossmancfa7f522006-01-08 18:17:55 +00001998 if (!nopnp) {
Pierre Ossman85bcc132005-05-08 19:35:27 +01001999 result = pnp_register_driver(&wbsd_pnp_driver);
2000 if (result < 0)
2001 return result;
2002 }
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01002003#endif /* CONFIG_PNP */
2004
Pierre Ossmancfa7f522006-01-08 18:17:55 +00002005 if (nopnp) {
Russell King3ae5eae2005-11-09 22:32:44 +00002006 result = platform_driver_register(&wbsd_driver);
Pierre Ossman85bcc132005-05-08 19:35:27 +01002007 if (result < 0)
2008 return result;
2009
Dmitry Torokhov21500bb2006-01-03 22:57:35 +00002010 wbsd_device = platform_device_alloc(DRIVER_NAME, -1);
Pierre Ossmancfa7f522006-01-08 18:17:55 +00002011 if (!wbsd_device) {
Dmitry Torokhov21500bb2006-01-03 22:57:35 +00002012 platform_driver_unregister(&wbsd_driver);
2013 return -ENOMEM;
2014 }
2015
2016 result = platform_device_add(wbsd_device);
Pierre Ossmancfa7f522006-01-08 18:17:55 +00002017 if (result) {
Dmitry Torokhov21500bb2006-01-03 22:57:35 +00002018 platform_device_put(wbsd_device);
2019 platform_driver_unregister(&wbsd_driver);
2020 return result;
2021 }
Pierre Ossman85bcc132005-05-08 19:35:27 +01002022 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002023
2024 return 0;
2025}
2026
2027static void __exit wbsd_drv_exit(void)
2028{
Pierre Ossman85bcc132005-05-08 19:35:27 +01002029#ifdef CONFIG_PNP
2030
2031 if (!nopnp)
2032 pnp_unregister_driver(&wbsd_pnp_driver);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01002033
2034#endif /* CONFIG_PNP */
Pierre Ossman85bcc132005-05-08 19:35:27 +01002035
Pierre Ossmancfa7f522006-01-08 18:17:55 +00002036 if (nopnp) {
Pierre Ossman85bcc132005-05-08 19:35:27 +01002037 platform_device_unregister(wbsd_device);
Pierre Ossmanfecf92b2005-09-12 12:09:25 +01002038
Russell King3ae5eae2005-11-09 22:32:44 +00002039 platform_driver_unregister(&wbsd_driver);
Pierre Ossman85bcc132005-05-08 19:35:27 +01002040 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002041
2042 DBG("unloaded\n");
2043}
2044
2045module_init(wbsd_drv_init);
2046module_exit(wbsd_drv_exit);
Pierre Ossman85bcc132005-05-08 19:35:27 +01002047#ifdef CONFIG_PNP
2048module_param(nopnp, uint, 0444);
2049#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07002050module_param(io, uint, 0444);
2051module_param(irq, uint, 0444);
2052module_param(dma, int, 0444);
2053
2054MODULE_LICENSE("GPL");
Pierre Ossmande1d09e2005-11-09 23:21:49 +00002055MODULE_AUTHOR("Pierre Ossman <drzeus@drzeus.cx>");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002056MODULE_DESCRIPTION("Winbond W83L51xD SD/MMC card interface driver");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002057
Pierre Ossman85bcc132005-05-08 19:35:27 +01002058#ifdef CONFIG_PNP
2059MODULE_PARM_DESC(nopnp, "Scan for device instead of relying on PNP. (default 0)");
2060#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07002061MODULE_PARM_DESC(io, "I/O base to allocate. Must be 8 byte aligned. (default 0x248)");
2062MODULE_PARM_DESC(irq, "IRQ to allocate. (default 6)");
2063MODULE_PARM_DESC(dma, "DMA channel to allocate. -1 for no DMA. (default 2)");