diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/mmc/wbsd.c |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/mmc/wbsd.c')
-rw-r--r-- | drivers/mmc/wbsd.c | 1651 |
1 files changed, 1651 insertions, 0 deletions
diff --git a/drivers/mmc/wbsd.c b/drivers/mmc/wbsd.c new file mode 100644 index 00000000000..938bca0414e --- /dev/null +++ b/drivers/mmc/wbsd.c | |||
@@ -0,0 +1,1651 @@ | |||
1 | /* | ||
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 | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * | ||
11 | * Warning! | ||
12 | * | ||
13 | * Changes to the FIFO system should be done with extreme care since | ||
14 | * the hardware is full of bugs related to the FIFO. Known issues are: | ||
15 | * | ||
16 | * - FIFO size field in FSR is always zero. | ||
17 | * | ||
18 | * - FIFO interrupts tend not to work as they should. Interrupts are | ||
19 | * triggered only for full/empty events, not for threshold values. | ||
20 | * | ||
21 | * - On APIC systems the FIFO empty interrupt is sometimes lost. | ||
22 | */ | ||
23 | |||
24 | #include <linux/config.h> | ||
25 | #include <linux/module.h> | ||
26 | #include <linux/moduleparam.h> | ||
27 | #include <linux/init.h> | ||
28 | #include <linux/ioport.h> | ||
29 | #include <linux/device.h> | ||
30 | #include <linux/interrupt.h> | ||
31 | #include <linux/delay.h> | ||
32 | #include <linux/highmem.h> | ||
33 | #include <linux/mmc/host.h> | ||
34 | #include <linux/mmc/protocol.h> | ||
35 | |||
36 | #include <asm/io.h> | ||
37 | #include <asm/dma.h> | ||
38 | #include <asm/scatterlist.h> | ||
39 | |||
40 | #include "wbsd.h" | ||
41 | |||
42 | #define DRIVER_NAME "wbsd" | ||
43 | #define DRIVER_VERSION "1.1" | ||
44 | |||
45 | #ifdef CONFIG_MMC_DEBUG | ||
46 | #define DBG(x...) \ | ||
47 | printk(KERN_DEBUG DRIVER_NAME ": " x) | ||
48 | #define DBGF(f, x...) \ | ||
49 | printk(KERN_DEBUG DRIVER_NAME " [%s()]: " f, __func__ , ##x) | ||
50 | #else | ||
51 | #define DBG(x...) do { } while (0) | ||
52 | #define DBGF(x...) do { } while (0) | ||
53 | #endif | ||
54 | |||
55 | static unsigned int io = 0x248; | ||
56 | static unsigned int irq = 6; | ||
57 | static int dma = 2; | ||
58 | |||
59 | #ifdef CONFIG_MMC_DEBUG | ||
60 | void DBG_REG(int reg, u8 value) | ||
61 | { | ||
62 | int i; | ||
63 | |||
64 | printk(KERN_DEBUG "wbsd: Register %d: 0x%02X %3d '%c' ", | ||
65 | reg, (int)value, (int)value, (value < 0x20)?'.':value); | ||
66 | |||
67 | for (i = 7;i >= 0;i--) | ||
68 | { | ||
69 | if (value & (1 << i)) | ||
70 | printk("x"); | ||
71 | else | ||
72 | printk("."); | ||
73 | } | ||
74 | |||
75 | printk("\n"); | ||
76 | } | ||
77 | #else | ||
78 | #define DBG_REG(r, v) do {} while (0) | ||
79 | #endif | ||
80 | |||
81 | /* | ||
82 | * Basic functions | ||
83 | */ | ||
84 | |||
85 | static inline void wbsd_unlock_config(struct wbsd_host* host) | ||
86 | { | ||
87 | outb(host->unlock_code, host->config); | ||
88 | outb(host->unlock_code, host->config); | ||
89 | } | ||
90 | |||
91 | static inline void wbsd_lock_config(struct wbsd_host* host) | ||
92 | { | ||
93 | outb(LOCK_CODE, host->config); | ||
94 | } | ||
95 | |||
96 | static inline void wbsd_write_config(struct wbsd_host* host, u8 reg, u8 value) | ||
97 | { | ||
98 | outb(reg, host->config); | ||
99 | outb(value, host->config + 1); | ||
100 | } | ||
101 | |||
102 | static inline u8 wbsd_read_config(struct wbsd_host* host, u8 reg) | ||
103 | { | ||
104 | outb(reg, host->config); | ||
105 | return inb(host->config + 1); | ||
106 | } | ||
107 | |||
108 | static inline void wbsd_write_index(struct wbsd_host* host, u8 index, u8 value) | ||
109 | { | ||
110 | outb(index, host->base + WBSD_IDXR); | ||
111 | outb(value, host->base + WBSD_DATAR); | ||
112 | } | ||
113 | |||
114 | static inline u8 wbsd_read_index(struct wbsd_host* host, u8 index) | ||
115 | { | ||
116 | outb(index, host->base + WBSD_IDXR); | ||
117 | return inb(host->base + WBSD_DATAR); | ||
118 | } | ||
119 | |||
120 | /* | ||
121 | * Common routines | ||
122 | */ | ||
123 | |||
124 | static void wbsd_init_device(struct wbsd_host* host) | ||
125 | { | ||
126 | u8 setup, ier; | ||
127 | |||
128 | /* | ||
129 | * Reset chip (SD/MMC part) and fifo. | ||
130 | */ | ||
131 | setup = wbsd_read_index(host, WBSD_IDX_SETUP); | ||
132 | setup |= WBSD_FIFO_RESET | WBSD_SOFT_RESET; | ||
133 | wbsd_write_index(host, WBSD_IDX_SETUP, setup); | ||
134 | |||
135 | /* | ||
136 | * Read back default clock. | ||
137 | */ | ||
138 | host->clk = wbsd_read_index(host, WBSD_IDX_CLK); | ||
139 | |||
140 | /* | ||
141 | * Power down port. | ||
142 | */ | ||
143 | outb(WBSD_POWER_N, host->base + WBSD_CSR); | ||
144 | |||
145 | /* | ||
146 | * Set maximum timeout. | ||
147 | */ | ||
148 | wbsd_write_index(host, WBSD_IDX_TAAC, 0x7F); | ||
149 | |||
150 | /* | ||
151 | * Enable interesting interrupts. | ||
152 | */ | ||
153 | ier = 0; | ||
154 | ier |= WBSD_EINT_CARD; | ||
155 | ier |= WBSD_EINT_FIFO_THRE; | ||
156 | ier |= WBSD_EINT_CCRC; | ||
157 | ier |= WBSD_EINT_TIMEOUT; | ||
158 | ier |= WBSD_EINT_CRC; | ||
159 | ier |= WBSD_EINT_TC; | ||
160 | |||
161 | outb(ier, host->base + WBSD_EIR); | ||
162 | |||
163 | /* | ||
164 | * Clear interrupts. | ||
165 | */ | ||
166 | inb(host->base + WBSD_ISR); | ||
167 | } | ||
168 | |||
169 | static void wbsd_reset(struct wbsd_host* host) | ||
170 | { | ||
171 | u8 setup; | ||
172 | |||
173 | printk(KERN_ERR DRIVER_NAME ": Resetting chip\n"); | ||
174 | |||
175 | /* | ||
176 | * Soft reset of chip (SD/MMC part). | ||
177 | */ | ||
178 | setup = wbsd_read_index(host, WBSD_IDX_SETUP); | ||
179 | setup |= WBSD_SOFT_RESET; | ||
180 | wbsd_write_index(host, WBSD_IDX_SETUP, setup); | ||
181 | } | ||
182 | |||
183 | static void wbsd_request_end(struct wbsd_host* host, struct mmc_request* mrq) | ||
184 | { | ||
185 | unsigned long dmaflags; | ||
186 | |||
187 | DBGF("Ending request, cmd (%x)\n", mrq->cmd->opcode); | ||
188 | |||
189 | if (host->dma >= 0) | ||
190 | { | ||
191 | /* | ||
192 | * Release ISA DMA controller. | ||
193 | */ | ||
194 | dmaflags = claim_dma_lock(); | ||
195 | disable_dma(host->dma); | ||
196 | clear_dma_ff(host->dma); | ||
197 | release_dma_lock(dmaflags); | ||
198 | |||
199 | /* | ||
200 | * Disable DMA on host. | ||
201 | */ | ||
202 | wbsd_write_index(host, WBSD_IDX_DMA, 0); | ||
203 | } | ||
204 | |||
205 | host->mrq = NULL; | ||
206 | |||
207 | /* | ||
208 | * MMC layer might call back into the driver so first unlock. | ||
209 | */ | ||
210 | spin_unlock(&host->lock); | ||
211 | mmc_request_done(host->mmc, mrq); | ||
212 | spin_lock(&host->lock); | ||
213 | } | ||
214 | |||
215 | /* | ||
216 | * Scatter/gather functions | ||
217 | */ | ||
218 | |||
219 | static inline void wbsd_init_sg(struct wbsd_host* host, struct mmc_data* data) | ||
220 | { | ||
221 | /* | ||
222 | * Get info. about SG list from data structure. | ||
223 | */ | ||
224 | host->cur_sg = data->sg; | ||
225 | host->num_sg = data->sg_len; | ||
226 | |||
227 | host->offset = 0; | ||
228 | host->remain = host->cur_sg->length; | ||
229 | } | ||
230 | |||
231 | static inline int wbsd_next_sg(struct wbsd_host* host) | ||
232 | { | ||
233 | /* | ||
234 | * Skip to next SG entry. | ||
235 | */ | ||
236 | host->cur_sg++; | ||
237 | host->num_sg--; | ||
238 | |||
239 | /* | ||
240 | * Any entries left? | ||
241 | */ | ||
242 | if (host->num_sg > 0) | ||
243 | { | ||
244 | host->offset = 0; | ||
245 | host->remain = host->cur_sg->length; | ||
246 | } | ||
247 | |||
248 | return host->num_sg; | ||
249 | } | ||
250 | |||
251 | static inline char* wbsd_kmap_sg(struct wbsd_host* host) | ||
252 | { | ||
253 | host->mapped_sg = kmap_atomic(host->cur_sg->page, KM_BIO_SRC_IRQ) + | ||
254 | host->cur_sg->offset; | ||
255 | return host->mapped_sg; | ||
256 | } | ||
257 | |||
258 | static inline void wbsd_kunmap_sg(struct wbsd_host* host) | ||
259 | { | ||
260 | kunmap_atomic(host->mapped_sg, KM_BIO_SRC_IRQ); | ||
261 | } | ||
262 | |||
263 | static inline void wbsd_sg_to_dma(struct wbsd_host* host, struct mmc_data* data) | ||
264 | { | ||
265 | unsigned int len, i, size; | ||
266 | struct scatterlist* sg; | ||
267 | char* dmabuf = host->dma_buffer; | ||
268 | char* sgbuf; | ||
269 | |||
270 | size = host->size; | ||
271 | |||
272 | sg = data->sg; | ||
273 | len = data->sg_len; | ||
274 | |||
275 | /* | ||
276 | * Just loop through all entries. Size might not | ||
277 | * be the entire list though so make sure that | ||
278 | * we do not transfer too much. | ||
279 | */ | ||
280 | for (i = 0;i < len;i++) | ||
281 | { | ||
282 | sgbuf = kmap_atomic(sg[i].page, KM_BIO_SRC_IRQ) + sg[i].offset; | ||
283 | if (size < sg[i].length) | ||
284 | memcpy(dmabuf, sgbuf, size); | ||
285 | else | ||
286 | memcpy(dmabuf, sgbuf, sg[i].length); | ||
287 | kunmap_atomic(sgbuf, KM_BIO_SRC_IRQ); | ||
288 | dmabuf += sg[i].length; | ||
289 | |||
290 | if (size < sg[i].length) | ||
291 | size = 0; | ||
292 | else | ||
293 | size -= sg[i].length; | ||
294 | |||
295 | if (size == 0) | ||
296 | break; | ||
297 | } | ||
298 | |||
299 | /* | ||
300 | * Check that we didn't get a request to transfer | ||
301 | * more data than can fit into the SG list. | ||
302 | */ | ||
303 | |||
304 | BUG_ON(size != 0); | ||
305 | |||
306 | host->size -= size; | ||
307 | } | ||
308 | |||
309 | static inline void wbsd_dma_to_sg(struct wbsd_host* host, struct mmc_data* data) | ||
310 | { | ||
311 | unsigned int len, i, size; | ||
312 | struct scatterlist* sg; | ||
313 | char* dmabuf = host->dma_buffer; | ||
314 | char* sgbuf; | ||
315 | |||
316 | size = host->size; | ||
317 | |||
318 | sg = data->sg; | ||
319 | len = data->sg_len; | ||
320 | |||
321 | /* | ||
322 | * Just loop through all entries. Size might not | ||
323 | * be the entire list though so make sure that | ||
324 | * we do not transfer too much. | ||
325 | */ | ||
326 | for (i = 0;i < len;i++) | ||
327 | { | ||
328 | sgbuf = kmap_atomic(sg[i].page, KM_BIO_SRC_IRQ) + sg[i].offset; | ||
329 | if (size < sg[i].length) | ||
330 | memcpy(sgbuf, dmabuf, size); | ||
331 | else | ||
332 | memcpy(sgbuf, dmabuf, sg[i].length); | ||
333 | kunmap_atomic(sgbuf, KM_BIO_SRC_IRQ); | ||
334 | dmabuf += sg[i].length; | ||
335 | |||
336 | if (size < sg[i].length) | ||
337 | size = 0; | ||
338 | else | ||
339 | size -= sg[i].length; | ||
340 | |||
341 | if (size == 0) | ||
342 | break; | ||
343 | } | ||
344 | |||
345 | /* | ||
346 | * Check that we didn't get a request to transfer | ||
347 | * more data than can fit into the SG list. | ||
348 | */ | ||
349 | |||
350 | BUG_ON(size != 0); | ||
351 | |||
352 | host->size -= size; | ||
353 | } | ||
354 | |||
355 | /* | ||
356 | * Command handling | ||
357 | */ | ||
358 | |||
359 | static inline void wbsd_get_short_reply(struct wbsd_host* host, | ||
360 | struct mmc_command* cmd) | ||
361 | { | ||
362 | /* | ||
363 | * Correct response type? | ||
364 | */ | ||
365 | if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_SHORT) | ||
366 | { | ||
367 | cmd->error = MMC_ERR_INVALID; | ||
368 | return; | ||
369 | } | ||
370 | |||
371 | cmd->resp[0] = | ||
372 | wbsd_read_index(host, WBSD_IDX_RESP12) << 24; | ||
373 | cmd->resp[0] |= | ||
374 | wbsd_read_index(host, WBSD_IDX_RESP13) << 16; | ||
375 | cmd->resp[0] |= | ||
376 | wbsd_read_index(host, WBSD_IDX_RESP14) << 8; | ||
377 | cmd->resp[0] |= | ||
378 | wbsd_read_index(host, WBSD_IDX_RESP15) << 0; | ||
379 | cmd->resp[1] = | ||
380 | wbsd_read_index(host, WBSD_IDX_RESP16) << 24; | ||
381 | } | ||
382 | |||
383 | static inline void wbsd_get_long_reply(struct wbsd_host* host, | ||
384 | struct mmc_command* cmd) | ||
385 | { | ||
386 | int i; | ||
387 | |||
388 | /* | ||
389 | * Correct response type? | ||
390 | */ | ||
391 | if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_LONG) | ||
392 | { | ||
393 | cmd->error = MMC_ERR_INVALID; | ||
394 | return; | ||
395 | } | ||
396 | |||
397 | for (i = 0;i < 4;i++) | ||
398 | { | ||
399 | cmd->resp[i] = | ||
400 | wbsd_read_index(host, WBSD_IDX_RESP1 + i * 4) << 24; | ||
401 | cmd->resp[i] |= | ||
402 | wbsd_read_index(host, WBSD_IDX_RESP2 + i * 4) << 16; | ||
403 | cmd->resp[i] |= | ||
404 | wbsd_read_index(host, WBSD_IDX_RESP3 + i * 4) << 8; | ||
405 | cmd->resp[i] |= | ||
406 | wbsd_read_index(host, WBSD_IDX_RESP4 + i * 4) << 0; | ||
407 | } | ||
408 | } | ||
409 | |||
410 | static irqreturn_t wbsd_irq(int irq, void *dev_id, struct pt_regs *regs); | ||
411 | |||
412 | static void wbsd_send_command(struct wbsd_host* host, struct mmc_command* cmd) | ||
413 | { | ||
414 | int i; | ||
415 | u8 status, isr; | ||
416 | |||
417 | DBGF("Sending cmd (%x)\n", cmd->opcode); | ||
418 | |||
419 | /* | ||
420 | * Clear accumulated ISR. The interrupt routine | ||
421 | * will fill this one with events that occur during | ||
422 | * transfer. | ||
423 | */ | ||
424 | host->isr = 0; | ||
425 | |||
426 | /* | ||
427 | * Send the command (CRC calculated by host). | ||
428 | */ | ||
429 | outb(cmd->opcode, host->base + WBSD_CMDR); | ||
430 | for (i = 3;i >= 0;i--) | ||
431 | outb((cmd->arg >> (i * 8)) & 0xff, host->base + WBSD_CMDR); | ||
432 | |||
433 | cmd->error = MMC_ERR_NONE; | ||
434 | |||
435 | /* | ||
436 | * Wait for the request to complete. | ||
437 | */ | ||
438 | do { | ||
439 | status = wbsd_read_index(host, WBSD_IDX_STATUS); | ||
440 | } while (status & WBSD_CARDTRAFFIC); | ||
441 | |||
442 | /* | ||
443 | * Do we expect a reply? | ||
444 | */ | ||
445 | if ((cmd->flags & MMC_RSP_MASK) != MMC_RSP_NONE) | ||
446 | { | ||
447 | /* | ||
448 | * Read back status. | ||
449 | */ | ||
450 | isr = host->isr; | ||
451 | |||
452 | /* Card removed? */ | ||
453 | if (isr & WBSD_INT_CARD) | ||
454 | cmd->error = MMC_ERR_TIMEOUT; | ||
455 | /* Timeout? */ | ||
456 | else if (isr & WBSD_INT_TIMEOUT) | ||
457 | cmd->error = MMC_ERR_TIMEOUT; | ||
458 | /* CRC? */ | ||
459 | else if ((cmd->flags & MMC_RSP_CRC) && (isr & WBSD_INT_CRC)) | ||
460 | cmd->error = MMC_ERR_BADCRC; | ||
461 | /* All ok */ | ||
462 | else | ||
463 | { | ||
464 | if ((cmd->flags & MMC_RSP_MASK) == MMC_RSP_SHORT) | ||
465 | wbsd_get_short_reply(host, cmd); | ||
466 | else | ||
467 | wbsd_get_long_reply(host, cmd); | ||
468 | } | ||
469 | } | ||
470 | |||
471 | DBGF("Sent cmd (%x), res %d\n", cmd->opcode, cmd->error); | ||
472 | } | ||
473 | |||
474 | /* | ||
475 | * Data functions | ||
476 | */ | ||
477 | |||
478 | static void wbsd_empty_fifo(struct wbsd_host* host) | ||
479 | { | ||
480 | struct mmc_data* data = host->mrq->cmd->data; | ||
481 | char* buffer; | ||
482 | int i, fsr, fifo; | ||
483 | |||
484 | /* | ||
485 | * Handle excessive data. | ||
486 | */ | ||
487 | if (data->bytes_xfered == host->size) | ||
488 | return; | ||
489 | |||
490 | buffer = wbsd_kmap_sg(host) + host->offset; | ||
491 | |||
492 | /* | ||
493 | * Drain the fifo. This has a tendency to loop longer | ||
494 | * than the FIFO length (usually one block). | ||
495 | */ | ||
496 | while (!((fsr = inb(host->base + WBSD_FSR)) & WBSD_FIFO_EMPTY)) | ||
497 | { | ||
498 | /* | ||
499 | * The size field in the FSR is broken so we have to | ||
500 | * do some guessing. | ||
501 | */ | ||
502 | if (fsr & WBSD_FIFO_FULL) | ||
503 | fifo = 16; | ||
504 | else if (fsr & WBSD_FIFO_FUTHRE) | ||
505 | fifo = 8; | ||
506 | else | ||
507 | fifo = 1; | ||
508 | |||
509 | for (i = 0;i < fifo;i++) | ||
510 | { | ||
511 | *buffer = inb(host->base + WBSD_DFR); | ||
512 | buffer++; | ||
513 | host->offset++; | ||
514 | host->remain--; | ||
515 | |||
516 | data->bytes_xfered++; | ||
517 | |||
518 | /* | ||
519 | * Transfer done? | ||
520 | */ | ||
521 | if (data->bytes_xfered == host->size) | ||
522 | { | ||
523 | wbsd_kunmap_sg(host); | ||
524 | return; | ||
525 | } | ||
526 | |||
527 | /* | ||
528 | * End of scatter list entry? | ||
529 | */ | ||
530 | if (host->remain == 0) | ||
531 | { | ||
532 | wbsd_kunmap_sg(host); | ||
533 | |||
534 | /* | ||
535 | * Get next entry. Check if last. | ||
536 | */ | ||
537 | if (!wbsd_next_sg(host)) | ||
538 | { | ||
539 | /* | ||
540 | * We should never reach this point. | ||
541 | * It means that we're trying to | ||
542 | * transfer more blocks than can fit | ||
543 | * into the scatter list. | ||
544 | */ | ||
545 | BUG_ON(1); | ||
546 | |||
547 | host->size = data->bytes_xfered; | ||
548 | |||
549 | return; | ||
550 | } | ||
551 | |||
552 | buffer = wbsd_kmap_sg(host); | ||
553 | } | ||
554 | } | ||
555 | } | ||
556 | |||
557 | wbsd_kunmap_sg(host); | ||
558 | |||
559 | /* | ||
560 | * This is a very dirty hack to solve a | ||
561 | * hardware problem. The chip doesn't trigger | ||
562 | * FIFO threshold interrupts properly. | ||
563 | */ | ||
564 | if ((host->size - data->bytes_xfered) < 16) | ||
565 | tasklet_schedule(&host->fifo_tasklet); | ||
566 | } | ||
567 | |||
568 | static void wbsd_fill_fifo(struct wbsd_host* host) | ||
569 | { | ||
570 | struct mmc_data* data = host->mrq->cmd->data; | ||
571 | char* buffer; | ||
572 | int i, fsr, fifo; | ||
573 | |||
574 | /* | ||
575 | * Check that we aren't being called after the | ||
576 | * entire buffer has been transfered. | ||
577 | */ | ||
578 | if (data->bytes_xfered == host->size) | ||
579 | return; | ||
580 | |||
581 | buffer = wbsd_kmap_sg(host) + host->offset; | ||
582 | |||
583 | /* | ||
584 | * Fill the fifo. This has a tendency to loop longer | ||
585 | * than the FIFO length (usually one block). | ||
586 | */ | ||
587 | while (!((fsr = inb(host->base + WBSD_FSR)) & WBSD_FIFO_FULL)) | ||
588 | { | ||
589 | /* | ||
590 | * The size field in the FSR is broken so we have to | ||
591 | * do some guessing. | ||
592 | */ | ||
593 | if (fsr & WBSD_FIFO_EMPTY) | ||
594 | fifo = 0; | ||
595 | else if (fsr & WBSD_FIFO_EMTHRE) | ||
596 | fifo = 8; | ||
597 | else | ||
598 | fifo = 15; | ||
599 | |||
600 | for (i = 16;i > fifo;i--) | ||
601 | { | ||
602 | outb(*buffer, host->base + WBSD_DFR); | ||
603 | buffer++; | ||
604 | host->offset++; | ||
605 | host->remain--; | ||
606 | |||
607 | data->bytes_xfered++; | ||
608 | |||
609 | /* | ||
610 | * Transfer done? | ||
611 | */ | ||
612 | if (data->bytes_xfered == host->size) | ||
613 | { | ||
614 | wbsd_kunmap_sg(host); | ||
615 | return; | ||
616 | } | ||
617 | |||
618 | /* | ||
619 | * End of scatter list entry? | ||
620 | */ | ||
621 | if (host->remain == 0) | ||
622 | { | ||
623 | wbsd_kunmap_sg(host); | ||
624 | |||
625 | /* | ||
626 | * Get next entry. Check if last. | ||
627 | */ | ||
628 | if (!wbsd_next_sg(host)) | ||
629 | { | ||
630 | /* | ||
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); | ||
637 | |||
638 | host->size = data->bytes_xfered; | ||
639 | |||
640 | return; | ||
641 | } | ||
642 | |||
643 | buffer = wbsd_kmap_sg(host); | ||
644 | } | ||
645 | } | ||
646 | } | ||
647 | |||
648 | wbsd_kunmap_sg(host); | ||
649 | } | ||
650 | |||
651 | static void wbsd_prepare_data(struct wbsd_host* host, struct mmc_data* data) | ||
652 | { | ||
653 | u16 blksize; | ||
654 | u8 setup; | ||
655 | unsigned long dmaflags; | ||
656 | |||
657 | DBGF("blksz %04x blks %04x flags %08x\n", | ||
658 | 1 << data->blksz_bits, data->blocks, data->flags); | ||
659 | DBGF("tsac %d ms nsac %d clk\n", | ||
660 | data->timeout_ns / 1000000, data->timeout_clks); | ||
661 | |||
662 | /* | ||
663 | * Calculate size. | ||
664 | */ | ||
665 | host->size = data->blocks << data->blksz_bits; | ||
666 | |||
667 | /* | ||
668 | * Check timeout values for overflow. | ||
669 | * (Yes, some cards cause this value to overflow). | ||
670 | */ | ||
671 | if (data->timeout_ns > 127000000) | ||
672 | wbsd_write_index(host, WBSD_IDX_TAAC, 127); | ||
673 | else | ||
674 | wbsd_write_index(host, WBSD_IDX_TAAC, data->timeout_ns/1000000); | ||
675 | |||
676 | if (data->timeout_clks > 255) | ||
677 | wbsd_write_index(host, WBSD_IDX_NSAC, 255); | ||
678 | else | ||
679 | wbsd_write_index(host, WBSD_IDX_NSAC, data->timeout_clks); | ||
680 | |||
681 | /* | ||
682 | * Inform the chip of how large blocks will be | ||
683 | * sent. It needs this to determine when to | ||
684 | * calculate CRC. | ||
685 | * | ||
686 | * Space for CRC must be included in the size. | ||
687 | */ | ||
688 | blksize = (1 << data->blksz_bits) + 2; | ||
689 | |||
690 | wbsd_write_index(host, WBSD_IDX_PBSMSB, (blksize >> 4) & 0xF0); | ||
691 | wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF); | ||
692 | |||
693 | /* | ||
694 | * Clear the FIFO. This is needed even for DMA | ||
695 | * transfers since the chip still uses the FIFO | ||
696 | * internally. | ||
697 | */ | ||
698 | setup = wbsd_read_index(host, WBSD_IDX_SETUP); | ||
699 | setup |= WBSD_FIFO_RESET; | ||
700 | wbsd_write_index(host, WBSD_IDX_SETUP, setup); | ||
701 | |||
702 | /* | ||
703 | * DMA transfer? | ||
704 | */ | ||
705 | if (host->dma >= 0) | ||
706 | { | ||
707 | /* | ||
708 | * The buffer for DMA is only 64 kB. | ||
709 | */ | ||
710 | BUG_ON(host->size > 0x10000); | ||
711 | if (host->size > 0x10000) | ||
712 | { | ||
713 | data->error = MMC_ERR_INVALID; | ||
714 | return; | ||
715 | } | ||
716 | |||
717 | /* | ||
718 | * Transfer data from the SG list to | ||
719 | * the DMA buffer. | ||
720 | */ | ||
721 | if (data->flags & MMC_DATA_WRITE) | ||
722 | wbsd_sg_to_dma(host, data); | ||
723 | |||
724 | /* | ||
725 | * Initialise the ISA DMA controller. | ||
726 | */ | ||
727 | dmaflags = claim_dma_lock(); | ||
728 | disable_dma(host->dma); | ||
729 | clear_dma_ff(host->dma); | ||
730 | if (data->flags & MMC_DATA_READ) | ||
731 | set_dma_mode(host->dma, DMA_MODE_READ & ~0x40); | ||
732 | else | ||
733 | set_dma_mode(host->dma, DMA_MODE_WRITE & ~0x40); | ||
734 | set_dma_addr(host->dma, host->dma_addr); | ||
735 | set_dma_count(host->dma, host->size); | ||
736 | |||
737 | enable_dma(host->dma); | ||
738 | release_dma_lock(dmaflags); | ||
739 | |||
740 | /* | ||
741 | * Enable DMA on the host. | ||
742 | */ | ||
743 | wbsd_write_index(host, WBSD_IDX_DMA, WBSD_DMA_ENABLE); | ||
744 | } | ||
745 | else | ||
746 | { | ||
747 | /* | ||
748 | * This flag is used to keep printk | ||
749 | * output to a minimum. | ||
750 | */ | ||
751 | host->firsterr = 1; | ||
752 | |||
753 | /* | ||
754 | * Initialise the SG list. | ||
755 | */ | ||
756 | wbsd_init_sg(host, data); | ||
757 | |||
758 | /* | ||
759 | * Turn off DMA. | ||
760 | */ | ||
761 | wbsd_write_index(host, WBSD_IDX_DMA, 0); | ||
762 | |||
763 | /* | ||
764 | * Set up FIFO threshold levels (and fill | ||
765 | * buffer if doing a write). | ||
766 | */ | ||
767 | if (data->flags & MMC_DATA_READ) | ||
768 | { | ||
769 | wbsd_write_index(host, WBSD_IDX_FIFOEN, | ||
770 | WBSD_FIFOEN_FULL | 8); | ||
771 | } | ||
772 | else | ||
773 | { | ||
774 | wbsd_write_index(host, WBSD_IDX_FIFOEN, | ||
775 | WBSD_FIFOEN_EMPTY | 8); | ||
776 | wbsd_fill_fifo(host); | ||
777 | } | ||
778 | } | ||
779 | |||
780 | data->error = MMC_ERR_NONE; | ||
781 | } | ||
782 | |||
783 | static void wbsd_finish_data(struct wbsd_host* host, struct mmc_data* data) | ||
784 | { | ||
785 | unsigned long dmaflags; | ||
786 | int count; | ||
787 | u8 status; | ||
788 | |||
789 | WARN_ON(host->mrq == NULL); | ||
790 | |||
791 | /* | ||
792 | * Send a stop command if needed. | ||
793 | */ | ||
794 | if (data->stop) | ||
795 | wbsd_send_command(host, data->stop); | ||
796 | |||
797 | /* | ||
798 | * Wait for the controller to leave data | ||
799 | * transfer state. | ||
800 | */ | ||
801 | do | ||
802 | { | ||
803 | status = wbsd_read_index(host, WBSD_IDX_STATUS); | ||
804 | } while (status & (WBSD_BLOCK_READ | WBSD_BLOCK_WRITE)); | ||
805 | |||
806 | /* | ||
807 | * DMA transfer? | ||
808 | */ | ||
809 | if (host->dma >= 0) | ||
810 | { | ||
811 | /* | ||
812 | * Disable DMA on the host. | ||
813 | */ | ||
814 | wbsd_write_index(host, WBSD_IDX_DMA, 0); | ||
815 | |||
816 | /* | ||
817 | * Turn of ISA DMA controller. | ||
818 | */ | ||
819 | dmaflags = claim_dma_lock(); | ||
820 | disable_dma(host->dma); | ||
821 | clear_dma_ff(host->dma); | ||
822 | count = get_dma_residue(host->dma); | ||
823 | release_dma_lock(dmaflags); | ||
824 | |||
825 | /* | ||
826 | * Any leftover data? | ||
827 | */ | ||
828 | if (count) | ||
829 | { | ||
830 | printk(KERN_ERR DRIVER_NAME ": Incomplete DMA " | ||
831 | "transfer. %d bytes left.\n", count); | ||
832 | |||
833 | data->error = MMC_ERR_FAILED; | ||
834 | } | ||
835 | else | ||
836 | { | ||
837 | /* | ||
838 | * Transfer data from DMA buffer to | ||
839 | * SG list. | ||
840 | */ | ||
841 | if (data->flags & MMC_DATA_READ) | ||
842 | wbsd_dma_to_sg(host, data); | ||
843 | |||
844 | data->bytes_xfered = host->size; | ||
845 | } | ||
846 | } | ||
847 | |||
848 | DBGF("Ending data transfer (%d bytes)\n", data->bytes_xfered); | ||
849 | |||
850 | wbsd_request_end(host, host->mrq); | ||
851 | } | ||
852 | |||
853 | /* | ||
854 | * MMC Callbacks | ||
855 | */ | ||
856 | |||
857 | static void wbsd_request(struct mmc_host* mmc, struct mmc_request* mrq) | ||
858 | { | ||
859 | struct wbsd_host* host = mmc_priv(mmc); | ||
860 | struct mmc_command* cmd; | ||
861 | |||
862 | /* | ||
863 | * Disable tasklets to avoid a deadlock. | ||
864 | */ | ||
865 | spin_lock_bh(&host->lock); | ||
866 | |||
867 | BUG_ON(host->mrq != NULL); | ||
868 | |||
869 | cmd = mrq->cmd; | ||
870 | |||
871 | host->mrq = mrq; | ||
872 | |||
873 | /* | ||
874 | * If there is no card in the slot then | ||
875 | * timeout immediatly. | ||
876 | */ | ||
877 | if (!(inb(host->base + WBSD_CSR) & WBSD_CARDPRESENT)) | ||
878 | { | ||
879 | cmd->error = MMC_ERR_TIMEOUT; | ||
880 | goto done; | ||
881 | } | ||
882 | |||
883 | /* | ||
884 | * Does the request include data? | ||
885 | */ | ||
886 | if (cmd->data) | ||
887 | { | ||
888 | wbsd_prepare_data(host, cmd->data); | ||
889 | |||
890 | if (cmd->data->error != MMC_ERR_NONE) | ||
891 | goto done; | ||
892 | } | ||
893 | |||
894 | wbsd_send_command(host, cmd); | ||
895 | |||
896 | /* | ||
897 | * If this is a data transfer the request | ||
898 | * will be finished after the data has | ||
899 | * transfered. | ||
900 | */ | ||
901 | if (cmd->data && (cmd->error == MMC_ERR_NONE)) | ||
902 | { | ||
903 | /* | ||
904 | * Dirty fix for hardware bug. | ||
905 | */ | ||
906 | if (host->dma == -1) | ||
907 | tasklet_schedule(&host->fifo_tasklet); | ||
908 | |||
909 | spin_unlock_bh(&host->lock); | ||
910 | |||
911 | return; | ||
912 | } | ||
913 | |||
914 | done: | ||
915 | wbsd_request_end(host, mrq); | ||
916 | |||
917 | spin_unlock_bh(&host->lock); | ||
918 | } | ||
919 | |||
920 | static void wbsd_set_ios(struct mmc_host* mmc, struct mmc_ios* ios) | ||
921 | { | ||
922 | struct wbsd_host* host = mmc_priv(mmc); | ||
923 | u8 clk, setup, pwr; | ||
924 | |||
925 | DBGF("clock %uHz busmode %u powermode %u Vdd %u\n", | ||
926 | ios->clock, ios->bus_mode, ios->power_mode, ios->vdd); | ||
927 | |||
928 | spin_lock_bh(&host->lock); | ||
929 | |||
930 | /* | ||
931 | * Reset the chip on each power off. | ||
932 | * Should clear out any weird states. | ||
933 | */ | ||
934 | if (ios->power_mode == MMC_POWER_OFF) | ||
935 | wbsd_init_device(host); | ||
936 | |||
937 | if (ios->clock >= 24000000) | ||
938 | clk = WBSD_CLK_24M; | ||
939 | else if (ios->clock >= 16000000) | ||
940 | clk = WBSD_CLK_16M; | ||
941 | else if (ios->clock >= 12000000) | ||
942 | clk = WBSD_CLK_12M; | ||
943 | else | ||
944 | clk = WBSD_CLK_375K; | ||
945 | |||
946 | /* | ||
947 | * Only write to the clock register when | ||
948 | * there is an actual change. | ||
949 | */ | ||
950 | if (clk != host->clk) | ||
951 | { | ||
952 | wbsd_write_index(host, WBSD_IDX_CLK, clk); | ||
953 | host->clk = clk; | ||
954 | } | ||
955 | |||
956 | if (ios->power_mode != MMC_POWER_OFF) | ||
957 | { | ||
958 | /* | ||
959 | * Power up card. | ||
960 | */ | ||
961 | pwr = inb(host->base + WBSD_CSR); | ||
962 | pwr &= ~WBSD_POWER_N; | ||
963 | outb(pwr, host->base + WBSD_CSR); | ||
964 | |||
965 | /* | ||
966 | * This behaviour is stolen from the | ||
967 | * Windows driver. Don't know why, but | ||
968 | * it is needed. | ||
969 | */ | ||
970 | setup = wbsd_read_index(host, WBSD_IDX_SETUP); | ||
971 | if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) | ||
972 | setup |= WBSD_DAT3_H; | ||
973 | else | ||
974 | setup &= ~WBSD_DAT3_H; | ||
975 | wbsd_write_index(host, WBSD_IDX_SETUP, setup); | ||
976 | |||
977 | mdelay(1); | ||
978 | } | ||
979 | |||
980 | spin_unlock_bh(&host->lock); | ||
981 | } | ||
982 | |||
983 | /* | ||
984 | * Tasklets | ||
985 | */ | ||
986 | |||
987 | inline static struct mmc_data* wbsd_get_data(struct wbsd_host* host) | ||
988 | { | ||
989 | WARN_ON(!host->mrq); | ||
990 | if (!host->mrq) | ||
991 | return NULL; | ||
992 | |||
993 | WARN_ON(!host->mrq->cmd); | ||
994 | if (!host->mrq->cmd) | ||
995 | return NULL; | ||
996 | |||
997 | WARN_ON(!host->mrq->cmd->data); | ||
998 | if (!host->mrq->cmd->data) | ||
999 | return NULL; | ||
1000 | |||
1001 | return host->mrq->cmd->data; | ||
1002 | } | ||
1003 | |||
1004 | static void wbsd_tasklet_card(unsigned long param) | ||
1005 | { | ||
1006 | struct wbsd_host* host = (struct wbsd_host*)param; | ||
1007 | u8 csr; | ||
1008 | |||
1009 | spin_lock(&host->lock); | ||
1010 | |||
1011 | csr = inb(host->base + WBSD_CSR); | ||
1012 | WARN_ON(csr == 0xff); | ||
1013 | |||
1014 | if (csr & WBSD_CARDPRESENT) | ||
1015 | DBG("Card inserted\n"); | ||
1016 | else | ||
1017 | { | ||
1018 | DBG("Card removed\n"); | ||
1019 | |||
1020 | if (host->mrq) | ||
1021 | { | ||
1022 | printk(KERN_ERR DRIVER_NAME | ||
1023 | ": Card removed during transfer!\n"); | ||
1024 | wbsd_reset(host); | ||
1025 | |||
1026 | host->mrq->cmd->error = MMC_ERR_FAILED; | ||
1027 | tasklet_schedule(&host->finish_tasklet); | ||
1028 | } | ||
1029 | } | ||
1030 | |||
1031 | /* | ||
1032 | * Unlock first since we might get a call back. | ||
1033 | */ | ||
1034 | spin_unlock(&host->lock); | ||
1035 | |||
1036 | mmc_detect_change(host->mmc); | ||
1037 | } | ||
1038 | |||
1039 | static void wbsd_tasklet_fifo(unsigned long param) | ||
1040 | { | ||
1041 | struct wbsd_host* host = (struct wbsd_host*)param; | ||
1042 | struct mmc_data* data; | ||
1043 | |||
1044 | spin_lock(&host->lock); | ||
1045 | |||
1046 | if (!host->mrq) | ||
1047 | goto end; | ||
1048 | |||
1049 | data = wbsd_get_data(host); | ||
1050 | if (!data) | ||
1051 | goto end; | ||
1052 | |||
1053 | if (data->flags & MMC_DATA_WRITE) | ||
1054 | wbsd_fill_fifo(host); | ||
1055 | else | ||
1056 | wbsd_empty_fifo(host); | ||
1057 | |||
1058 | /* | ||
1059 | * Done? | ||
1060 | */ | ||
1061 | if (host->size == data->bytes_xfered) | ||
1062 | { | ||
1063 | wbsd_write_index(host, WBSD_IDX_FIFOEN, 0); | ||
1064 | tasklet_schedule(&host->finish_tasklet); | ||
1065 | } | ||
1066 | |||
1067 | end: | ||
1068 | spin_unlock(&host->lock); | ||
1069 | } | ||
1070 | |||
1071 | static void wbsd_tasklet_crc(unsigned long param) | ||
1072 | { | ||
1073 | struct wbsd_host* host = (struct wbsd_host*)param; | ||
1074 | struct mmc_data* data; | ||
1075 | |||
1076 | spin_lock(&host->lock); | ||
1077 | |||
1078 | if (!host->mrq) | ||
1079 | goto end; | ||
1080 | |||
1081 | data = wbsd_get_data(host); | ||
1082 | if (!data) | ||
1083 | goto end; | ||
1084 | |||
1085 | DBGF("CRC error\n"); | ||
1086 | |||
1087 | data->error = MMC_ERR_BADCRC; | ||
1088 | |||
1089 | tasklet_schedule(&host->finish_tasklet); | ||
1090 | |||
1091 | end: | ||
1092 | spin_unlock(&host->lock); | ||
1093 | } | ||
1094 | |||
1095 | static void wbsd_tasklet_timeout(unsigned long param) | ||
1096 | { | ||
1097 | struct wbsd_host* host = (struct wbsd_host*)param; | ||
1098 | struct mmc_data* data; | ||
1099 | |||
1100 | spin_lock(&host->lock); | ||
1101 | |||
1102 | if (!host->mrq) | ||
1103 | goto end; | ||
1104 | |||
1105 | data = wbsd_get_data(host); | ||
1106 | if (!data) | ||
1107 | goto end; | ||
1108 | |||
1109 | DBGF("Timeout\n"); | ||
1110 | |||
1111 | data->error = MMC_ERR_TIMEOUT; | ||
1112 | |||
1113 | tasklet_schedule(&host->finish_tasklet); | ||
1114 | |||
1115 | end: | ||
1116 | spin_unlock(&host->lock); | ||
1117 | } | ||
1118 | |||
1119 | static void wbsd_tasklet_finish(unsigned long param) | ||
1120 | { | ||
1121 | struct wbsd_host* host = (struct wbsd_host*)param; | ||
1122 | struct mmc_data* data; | ||
1123 | |||
1124 | spin_lock(&host->lock); | ||
1125 | |||
1126 | WARN_ON(!host->mrq); | ||
1127 | if (!host->mrq) | ||
1128 | goto end; | ||
1129 | |||
1130 | data = wbsd_get_data(host); | ||
1131 | if (!data) | ||
1132 | goto end; | ||
1133 | |||
1134 | wbsd_finish_data(host, data); | ||
1135 | |||
1136 | end: | ||
1137 | spin_unlock(&host->lock); | ||
1138 | } | ||
1139 | |||
1140 | static void wbsd_tasklet_block(unsigned long param) | ||
1141 | { | ||
1142 | struct wbsd_host* host = (struct wbsd_host*)param; | ||
1143 | struct mmc_data* data; | ||
1144 | |||
1145 | spin_lock(&host->lock); | ||
1146 | |||
1147 | if ((wbsd_read_index(host, WBSD_IDX_CRCSTATUS) & WBSD_CRC_MASK) != | ||
1148 | WBSD_CRC_OK) | ||
1149 | { | ||
1150 | data = wbsd_get_data(host); | ||
1151 | if (!data) | ||
1152 | goto end; | ||
1153 | |||
1154 | DBGF("CRC error\n"); | ||
1155 | |||
1156 | data->error = MMC_ERR_BADCRC; | ||
1157 | |||
1158 | tasklet_schedule(&host->finish_tasklet); | ||
1159 | } | ||
1160 | |||
1161 | end: | ||
1162 | spin_unlock(&host->lock); | ||
1163 | } | ||
1164 | |||
1165 | /* | ||
1166 | * Interrupt handling | ||
1167 | */ | ||
1168 | |||
1169 | static irqreturn_t wbsd_irq(int irq, void *dev_id, struct pt_regs *regs) | ||
1170 | { | ||
1171 | struct wbsd_host* host = dev_id; | ||
1172 | int isr; | ||
1173 | |||
1174 | isr = inb(host->base + WBSD_ISR); | ||
1175 | |||
1176 | /* | ||
1177 | * Was it actually our hardware that caused the interrupt? | ||
1178 | */ | ||
1179 | if (isr == 0xff || isr == 0x00) | ||
1180 | return IRQ_NONE; | ||
1181 | |||
1182 | host->isr |= isr; | ||
1183 | |||
1184 | /* | ||
1185 | * Schedule tasklets as needed. | ||
1186 | */ | ||
1187 | if (isr & WBSD_INT_CARD) | ||
1188 | tasklet_schedule(&host->card_tasklet); | ||
1189 | if (isr & WBSD_INT_FIFO_THRE) | ||
1190 | tasklet_schedule(&host->fifo_tasklet); | ||
1191 | if (isr & WBSD_INT_CRC) | ||
1192 | tasklet_hi_schedule(&host->crc_tasklet); | ||
1193 | if (isr & WBSD_INT_TIMEOUT) | ||
1194 | tasklet_hi_schedule(&host->timeout_tasklet); | ||
1195 | if (isr & WBSD_INT_BUSYEND) | ||
1196 | tasklet_hi_schedule(&host->block_tasklet); | ||
1197 | if (isr & WBSD_INT_TC) | ||
1198 | tasklet_schedule(&host->finish_tasklet); | ||
1199 | |||
1200 | return IRQ_HANDLED; | ||
1201 | } | ||
1202 | |||
1203 | /* | ||
1204 | * Support functions for probe | ||
1205 | */ | ||
1206 | |||
1207 | static int wbsd_scan(struct wbsd_host* host) | ||
1208 | { | ||
1209 | int i, j, k; | ||
1210 | int id; | ||
1211 | |||
1212 | /* | ||
1213 | * Iterate through all ports, all codes to | ||
1214 | * find hardware that is in our known list. | ||
1215 | */ | ||
1216 | for (i = 0;i < sizeof(config_ports)/sizeof(int);i++) | ||
1217 | { | ||
1218 | if (!request_region(config_ports[i], 2, DRIVER_NAME)) | ||
1219 | continue; | ||
1220 | |||
1221 | for (j = 0;j < sizeof(unlock_codes)/sizeof(int);j++) | ||
1222 | { | ||
1223 | id = 0xFFFF; | ||
1224 | |||
1225 | outb(unlock_codes[j], config_ports[i]); | ||
1226 | outb(unlock_codes[j], config_ports[i]); | ||
1227 | |||
1228 | outb(WBSD_CONF_ID_HI, config_ports[i]); | ||
1229 | id = inb(config_ports[i] + 1) << 8; | ||
1230 | |||
1231 | outb(WBSD_CONF_ID_LO, config_ports[i]); | ||
1232 | id |= inb(config_ports[i] + 1); | ||
1233 | |||
1234 | for (k = 0;k < sizeof(valid_ids)/sizeof(int);k++) | ||
1235 | { | ||
1236 | if (id == valid_ids[k]) | ||
1237 | { | ||
1238 | host->chip_id = id; | ||
1239 | host->config = config_ports[i]; | ||
1240 | host->unlock_code = unlock_codes[i]; | ||
1241 | |||
1242 | return 0; | ||
1243 | } | ||
1244 | } | ||
1245 | |||
1246 | if (id != 0xFFFF) | ||
1247 | { | ||
1248 | DBG("Unknown hardware (id %x) found at %x\n", | ||
1249 | id, config_ports[i]); | ||
1250 | } | ||
1251 | |||
1252 | outb(LOCK_CODE, config_ports[i]); | ||
1253 | } | ||
1254 | |||
1255 | release_region(config_ports[i], 2); | ||
1256 | } | ||
1257 | |||
1258 | return -ENODEV; | ||
1259 | } | ||
1260 | |||
1261 | static int wbsd_request_regions(struct wbsd_host* host) | ||
1262 | { | ||
1263 | if (io & 0x7) | ||
1264 | return -EINVAL; | ||
1265 | |||
1266 | if (!request_region(io, 8, DRIVER_NAME)) | ||
1267 | return -EIO; | ||
1268 | |||
1269 | host->base = io; | ||
1270 | |||
1271 | return 0; | ||
1272 | } | ||
1273 | |||
1274 | static void wbsd_release_regions(struct wbsd_host* host) | ||
1275 | { | ||
1276 | if (host->base) | ||
1277 | release_region(host->base, 8); | ||
1278 | |||
1279 | if (host->config) | ||
1280 | release_region(host->config, 2); | ||
1281 | } | ||
1282 | |||
1283 | static void wbsd_init_dma(struct wbsd_host* host) | ||
1284 | { | ||
1285 | host->dma = -1; | ||
1286 | |||
1287 | if (dma < 0) | ||
1288 | return; | ||
1289 | |||
1290 | if (request_dma(dma, DRIVER_NAME)) | ||
1291 | goto err; | ||
1292 | |||
1293 | /* | ||
1294 | * We need to allocate a special buffer in | ||
1295 | * order for ISA to be able to DMA to it. | ||
1296 | */ | ||
1297 | host->dma_buffer = kmalloc(65536, | ||
1298 | GFP_NOIO | GFP_DMA | __GFP_REPEAT | __GFP_NOWARN); | ||
1299 | if (!host->dma_buffer) | ||
1300 | goto free; | ||
1301 | |||
1302 | /* | ||
1303 | * Translate the address to a physical address. | ||
1304 | */ | ||
1305 | host->dma_addr = isa_virt_to_bus(host->dma_buffer); | ||
1306 | |||
1307 | /* | ||
1308 | * ISA DMA must be aligned on a 64k basis. | ||
1309 | */ | ||
1310 | if ((host->dma_addr & 0xffff) != 0) | ||
1311 | goto kfree; | ||
1312 | /* | ||
1313 | * ISA cannot access memory above 16 MB. | ||
1314 | */ | ||
1315 | else if (host->dma_addr >= 0x1000000) | ||
1316 | goto kfree; | ||
1317 | |||
1318 | host->dma = dma; | ||
1319 | |||
1320 | return; | ||
1321 | |||
1322 | kfree: | ||
1323 | /* | ||
1324 | * If we've gotten here then there is some kind of alignment bug | ||
1325 | */ | ||
1326 | BUG_ON(1); | ||
1327 | |||
1328 | kfree(host->dma_buffer); | ||
1329 | host->dma_buffer = NULL; | ||
1330 | |||
1331 | free: | ||
1332 | free_dma(dma); | ||
1333 | |||
1334 | err: | ||
1335 | printk(KERN_WARNING DRIVER_NAME ": Unable to allocate DMA %d. " | ||
1336 | "Falling back on FIFO.\n", dma); | ||
1337 | } | ||
1338 | |||
1339 | static struct mmc_host_ops wbsd_ops = { | ||
1340 | .request = wbsd_request, | ||
1341 | .set_ios = wbsd_set_ios, | ||
1342 | }; | ||
1343 | |||
1344 | /* | ||
1345 | * Device probe | ||
1346 | */ | ||
1347 | |||
1348 | static int wbsd_probe(struct device* dev) | ||
1349 | { | ||
1350 | struct wbsd_host* host = NULL; | ||
1351 | struct mmc_host* mmc = NULL; | ||
1352 | int ret; | ||
1353 | |||
1354 | /* | ||
1355 | * Allocate MMC structure. | ||
1356 | */ | ||
1357 | mmc = mmc_alloc_host(sizeof(struct wbsd_host), dev); | ||
1358 | if (!mmc) | ||
1359 | return -ENOMEM; | ||
1360 | |||
1361 | host = mmc_priv(mmc); | ||
1362 | host->mmc = mmc; | ||
1363 | |||
1364 | /* | ||
1365 | * Scan for hardware. | ||
1366 | */ | ||
1367 | ret = wbsd_scan(host); | ||
1368 | if (ret) | ||
1369 | goto freemmc; | ||
1370 | |||
1371 | /* | ||
1372 | * Reset the chip. | ||
1373 | */ | ||
1374 | wbsd_write_config(host, WBSD_CONF_SWRST, 1); | ||
1375 | wbsd_write_config(host, WBSD_CONF_SWRST, 0); | ||
1376 | |||
1377 | /* | ||
1378 | * Allocate I/O ports. | ||
1379 | */ | ||
1380 | ret = wbsd_request_regions(host); | ||
1381 | if (ret) | ||
1382 | goto release; | ||
1383 | |||
1384 | /* | ||
1385 | * Set host parameters. | ||
1386 | */ | ||
1387 | mmc->ops = &wbsd_ops; | ||
1388 | mmc->f_min = 375000; | ||
1389 | mmc->f_max = 24000000; | ||
1390 | mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34; | ||
1391 | |||
1392 | spin_lock_init(&host->lock); | ||
1393 | |||
1394 | /* | ||
1395 | * Select SD/MMC function. | ||
1396 | */ | ||
1397 | wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD); | ||
1398 | |||
1399 | /* | ||
1400 | * Set up card detection. | ||
1401 | */ | ||
1402 | wbsd_write_config(host, WBSD_CONF_PINS, 0x02); | ||
1403 | |||
1404 | /* | ||
1405 | * Configure I/O port. | ||
1406 | */ | ||
1407 | wbsd_write_config(host, WBSD_CONF_PORT_HI, host->base >> 8); | ||
1408 | wbsd_write_config(host, WBSD_CONF_PORT_LO, host->base & 0xff); | ||
1409 | |||
1410 | /* | ||
1411 | * Allocate interrupt. | ||
1412 | */ | ||
1413 | ret = request_irq(irq, wbsd_irq, SA_SHIRQ, DRIVER_NAME, host); | ||
1414 | if (ret) | ||
1415 | goto release; | ||
1416 | |||
1417 | host->irq = irq; | ||
1418 | |||
1419 | /* | ||
1420 | * Set up tasklets. | ||
1421 | */ | ||
1422 | tasklet_init(&host->card_tasklet, wbsd_tasklet_card, (unsigned long)host); | ||
1423 | tasklet_init(&host->fifo_tasklet, wbsd_tasklet_fifo, (unsigned long)host); | ||
1424 | tasklet_init(&host->crc_tasklet, wbsd_tasklet_crc, (unsigned long)host); | ||
1425 | tasklet_init(&host->timeout_tasklet, wbsd_tasklet_timeout, (unsigned long)host); | ||
1426 | tasklet_init(&host->finish_tasklet, wbsd_tasklet_finish, (unsigned long)host); | ||
1427 | tasklet_init(&host->block_tasklet, wbsd_tasklet_block, (unsigned long)host); | ||
1428 | |||
1429 | /* | ||
1430 | * Configure interrupt. | ||
1431 | */ | ||
1432 | wbsd_write_config(host, WBSD_CONF_IRQ, host->irq); | ||
1433 | |||
1434 | /* | ||
1435 | * Allocate DMA. | ||
1436 | */ | ||
1437 | wbsd_init_dma(host); | ||
1438 | |||
1439 | /* | ||
1440 | * If all went well, then configure DMA. | ||
1441 | */ | ||
1442 | if (host->dma >= 0) | ||
1443 | wbsd_write_config(host, WBSD_CONF_DRQ, host->dma); | ||
1444 | |||
1445 | /* | ||
1446 | * Maximum number of segments. Worst case is one sector per segment | ||
1447 | * so this will be 64kB/512. | ||
1448 | */ | ||
1449 | mmc->max_hw_segs = 128; | ||
1450 | mmc->max_phys_segs = 128; | ||
1451 | |||
1452 | /* | ||
1453 | * Maximum number of sectors in one transfer. Also limited by 64kB | ||
1454 | * buffer. | ||
1455 | */ | ||
1456 | mmc->max_sectors = 128; | ||
1457 | |||
1458 | /* | ||
1459 | * Maximum segment size. Could be one segment with the maximum number | ||
1460 | * of segments. | ||
1461 | */ | ||
1462 | mmc->max_seg_size = mmc->max_sectors * 512; | ||
1463 | |||
1464 | /* | ||
1465 | * Enable chip. | ||
1466 | */ | ||
1467 | wbsd_write_config(host, WBSD_CONF_ENABLE, 1); | ||
1468 | |||
1469 | /* | ||
1470 | * Power up chip. | ||
1471 | */ | ||
1472 | wbsd_write_config(host, WBSD_CONF_POWER, 0x20); | ||
1473 | |||
1474 | /* | ||
1475 | * Power Management stuff. No idea how this works. | ||
1476 | * Not tested. | ||
1477 | */ | ||
1478 | #ifdef CONFIG_PM | ||
1479 | wbsd_write_config(host, WBSD_CONF_PME, 0xA0); | ||
1480 | #endif | ||
1481 | |||
1482 | /* | ||
1483 | * Reset the chip into a known state. | ||
1484 | */ | ||
1485 | wbsd_init_device(host); | ||
1486 | |||
1487 | dev_set_drvdata(dev, mmc); | ||
1488 | |||
1489 | /* | ||
1490 | * Add host to MMC layer. | ||
1491 | */ | ||
1492 | mmc_add_host(mmc); | ||
1493 | |||
1494 | printk(KERN_INFO "%s: W83L51xD id %x at 0x%x irq %d dma %d\n", | ||
1495 | mmc->host_name, (int)host->chip_id, (int)host->base, | ||
1496 | (int)host->irq, (int)host->dma); | ||
1497 | |||
1498 | return 0; | ||
1499 | |||
1500 | release: | ||
1501 | wbsd_release_regions(host); | ||
1502 | |||
1503 | freemmc: | ||
1504 | mmc_free_host(mmc); | ||
1505 | |||
1506 | return ret; | ||
1507 | } | ||
1508 | |||
1509 | /* | ||
1510 | * Device remove | ||
1511 | */ | ||
1512 | |||
1513 | static int wbsd_remove(struct device* dev) | ||
1514 | { | ||
1515 | struct mmc_host* mmc = dev_get_drvdata(dev); | ||
1516 | struct wbsd_host* host; | ||
1517 | |||
1518 | if (!mmc) | ||
1519 | return 0; | ||
1520 | |||
1521 | host = mmc_priv(mmc); | ||
1522 | |||
1523 | /* | ||
1524 | * Unregister host with MMC layer. | ||
1525 | */ | ||
1526 | mmc_remove_host(mmc); | ||
1527 | |||
1528 | /* | ||
1529 | * Power down the SD/MMC function. | ||
1530 | */ | ||
1531 | wbsd_unlock_config(host); | ||
1532 | wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD); | ||
1533 | wbsd_write_config(host, WBSD_CONF_ENABLE, 0); | ||
1534 | wbsd_lock_config(host); | ||
1535 | |||
1536 | /* | ||
1537 | * Free resources. | ||
1538 | */ | ||
1539 | if (host->dma_buffer) | ||
1540 | kfree(host->dma_buffer); | ||
1541 | |||
1542 | if (host->dma >= 0) | ||
1543 | free_dma(host->dma); | ||
1544 | |||
1545 | free_irq(host->irq, host); | ||
1546 | |||
1547 | tasklet_kill(&host->card_tasklet); | ||
1548 | tasklet_kill(&host->fifo_tasklet); | ||
1549 | tasklet_kill(&host->crc_tasklet); | ||
1550 | tasklet_kill(&host->timeout_tasklet); | ||
1551 | tasklet_kill(&host->finish_tasklet); | ||
1552 | tasklet_kill(&host->block_tasklet); | ||
1553 | |||
1554 | wbsd_release_regions(host); | ||
1555 | |||
1556 | mmc_free_host(mmc); | ||
1557 | |||
1558 | return 0; | ||
1559 | } | ||
1560 | |||
1561 | /* | ||
1562 | * Power management | ||
1563 | */ | ||
1564 | |||
1565 | #ifdef CONFIG_PM | ||
1566 | static int wbsd_suspend(struct device *dev, u32 state, u32 level) | ||
1567 | { | ||
1568 | DBGF("Not yet supported\n"); | ||
1569 | |||
1570 | return 0; | ||
1571 | } | ||
1572 | |||
1573 | static int wbsd_resume(struct device *dev, u32 level) | ||
1574 | { | ||
1575 | DBGF("Not yet supported\n"); | ||
1576 | |||
1577 | return 0; | ||
1578 | } | ||
1579 | #else | ||
1580 | #define wbsd_suspend NULL | ||
1581 | #define wbsd_resume NULL | ||
1582 | #endif | ||
1583 | |||
1584 | static void wbsd_release(struct device *dev) | ||
1585 | { | ||
1586 | } | ||
1587 | |||
1588 | static struct platform_device wbsd_device = { | ||
1589 | .name = DRIVER_NAME, | ||
1590 | .id = -1, | ||
1591 | .dev = { | ||
1592 | .release = wbsd_release, | ||
1593 | }, | ||
1594 | }; | ||
1595 | |||
1596 | static struct device_driver wbsd_driver = { | ||
1597 | .name = DRIVER_NAME, | ||
1598 | .bus = &platform_bus_type, | ||
1599 | .probe = wbsd_probe, | ||
1600 | .remove = wbsd_remove, | ||
1601 | |||
1602 | .suspend = wbsd_suspend, | ||
1603 | .resume = wbsd_resume, | ||
1604 | }; | ||
1605 | |||
1606 | /* | ||
1607 | * Module loading/unloading | ||
1608 | */ | ||
1609 | |||
1610 | static int __init wbsd_drv_init(void) | ||
1611 | { | ||
1612 | int result; | ||
1613 | |||
1614 | printk(KERN_INFO DRIVER_NAME | ||
1615 | ": Winbond W83L51xD SD/MMC card interface driver, " | ||
1616 | DRIVER_VERSION "\n"); | ||
1617 | printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n"); | ||
1618 | |||
1619 | result = driver_register(&wbsd_driver); | ||
1620 | if (result < 0) | ||
1621 | return result; | ||
1622 | |||
1623 | result = platform_device_register(&wbsd_device); | ||
1624 | if (result < 0) | ||
1625 | return result; | ||
1626 | |||
1627 | return 0; | ||
1628 | } | ||
1629 | |||
1630 | static void __exit wbsd_drv_exit(void) | ||
1631 | { | ||
1632 | platform_device_unregister(&wbsd_device); | ||
1633 | |||
1634 | driver_unregister(&wbsd_driver); | ||
1635 | |||
1636 | DBG("unloaded\n"); | ||
1637 | } | ||
1638 | |||
1639 | module_init(wbsd_drv_init); | ||
1640 | module_exit(wbsd_drv_exit); | ||
1641 | module_param(io, uint, 0444); | ||
1642 | module_param(irq, uint, 0444); | ||
1643 | module_param(dma, int, 0444); | ||
1644 | |||
1645 | MODULE_LICENSE("GPL"); | ||
1646 | MODULE_DESCRIPTION("Winbond W83L51xD SD/MMC card interface driver"); | ||
1647 | MODULE_VERSION(DRIVER_VERSION); | ||
1648 | |||
1649 | MODULE_PARM_DESC(io, "I/O base to allocate. Must be 8 byte aligned. (default 0x248)"); | ||
1650 | MODULE_PARM_DESC(irq, "IRQ to allocate. (default 6)"); | ||
1651 | MODULE_PARM_DESC(dma, "DMA channel to allocate. -1 for no DMA. (default 2)"); | ||