| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2007-07-11 20:04:50 +02:00
										 |  |  |  *  linux/drivers/mmc/host/wbsd.c - Winbond W83L51xD SD/MMC driver | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2007-04-13 19:04:38 +02:00
										 |  |  |  *  Copyright (C) 2004-2007 Pierre Ossman, All Rights Reserved. | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or modify | 
					
						
							| 
									
										
										
										
											2006-09-30 23:27:52 -07:00
										 |  |  |  * it under the terms of the GNU General Public License as published by | 
					
						
							|  |  |  |  * the Free Software Foundation; either version 2 of the License, or (at | 
					
						
							|  |  |  |  * your option) any later version. | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  |  * | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Warning! | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Changes to the FIFO system should be done with extreme care since | 
					
						
							|  |  |  |  * the hardware is full of bugs related to the FIFO. Known issues are: | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * - FIFO size field in FSR is always zero. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * - FIFO interrupts tend not to work as they should. Interrupts are | 
					
						
							|  |  |  |  *   triggered only for full/empty events, not for threshold values. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * - On APIC systems the FIFO empty interrupt is sometimes lost. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/module.h>
 | 
					
						
							|  |  |  | #include <linux/moduleparam.h>
 | 
					
						
							|  |  |  | #include <linux/init.h>
 | 
					
						
							|  |  |  | #include <linux/ioport.h>
 | 
					
						
							| 
									
										
										
										
											2005-10-29 19:07:23 +01:00
										 |  |  | #include <linux/platform_device.h>
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #include <linux/interrupt.h>
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | #include <linux/dma-mapping.h>
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #include <linux/delay.h>
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | #include <linux/pnp.h>
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #include <linux/highmem.h>
 | 
					
						
							|  |  |  | #include <linux/mmc/host.h>
 | 
					
						
							| 
									
										
										
										
											2007-10-24 09:01:09 +02:00
										 |  |  | #include <linux/scatterlist.h>
 | 
					
						
							| 
									
										
											  
											
												include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files.  percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed.  Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability.  As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
  http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
  only the necessary includes are there.  ie. if only gfp is used,
  gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
  blocks and try to put the new include such that its order conforms
  to its surrounding.  It's put in the include block which contains
  core kernel includes, in the same order that the rest are ordered -
  alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
  doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
  because the file doesn't have fitting include block), it prints out
  an error message indicating which .h file needs to be added to the
  file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
   over 4000 files, deleting around 700 includes and adding ~480 gfp.h
   and ~3000 slab.h inclusions.  The script emitted errors for ~400
   files.
2. Each error was manually checked.  Some didn't need the inclusion,
   some needed manual addition while adding it to implementation .h or
   embedding .c file was more appropriate for others.  This step added
   inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
   from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
   e.g. lib/decompress_*.c used malloc/free() wrappers around slab
   APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
   editing them as sprinkling gfp.h and slab.h inclusions around .h
   files could easily lead to inclusion dependency hell.  Most gfp.h
   inclusion directives were ignored as stuff from gfp.h was usually
   wildly available and often used in preprocessor macros.  Each
   slab.h inclusion directive was examined and added manually as
   necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
   were fixed.  CONFIG_GCOV_KERNEL was turned off for all tests (as my
   distributed build env didn't work with gcov compiles) and a few
   more options had to be turned off depending on archs to make things
   build (like ipr on powerpc/64 which failed due to missing writeq).
   * x86 and x86_64 UP and SMP allmodconfig and a custom test config.
   * powerpc and powerpc64 SMP allmodconfig
   * sparc and sparc64 SMP allmodconfig
   * ia64 SMP allmodconfig
   * s390 SMP allmodconfig
   * alpha SMP allmodconfig
   * um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
   a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
											
										 
											2010-03-24 17:04:11 +09:00
										 |  |  | #include <linux/slab.h>
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include <asm/io.h>
 | 
					
						
							|  |  |  | #include <asm/dma.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "wbsd.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define DRIVER_NAME "wbsd"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define DBG(x...) \
 | 
					
						
							| 
									
										
										
										
											2006-03-29 09:30:20 +01:00
										 |  |  | 	pr_debug(DRIVER_NAME ": " x) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #define DBGF(f, x...) \
 | 
					
						
							| 
									
										
										
										
											2006-03-29 09:30:20 +01:00
										 |  |  | 	pr_debug(DRIVER_NAME " [%s()]: " f, __func__ , ##x) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Device resources | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_PNP
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const struct pnp_device_id pnp_dev_table[] = { | 
					
						
							|  |  |  | 	{ "WEC0517", 0 }, | 
					
						
							|  |  |  | 	{ "WEC0518", 0 }, | 
					
						
							|  |  |  | 	{ "", 0 }, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | MODULE_DEVICE_TABLE(pnp, pnp_dev_table); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #endif /* CONFIG_PNP */
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-07-01 13:07:37 +01:00
										 |  |  | static const int config_ports[] = { 0x2E, 0x4E }; | 
					
						
							|  |  |  | static const int unlock_codes[] = { 0x83, 0x87 }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const int valid_ids[] = { | 
					
						
							|  |  |  | 	0x7112, | 
					
						
							| 
									
										
										
										
											2008-06-30 10:50:23 +03:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2005-07-01 13:07:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | #ifdef CONFIG_PNP
 | 
					
						
							| 
									
										
										
										
											2008-06-30 10:50:23 +03:00
										 |  |  | static unsigned int param_nopnp = 0; | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | #else
 | 
					
						
							| 
									
										
										
										
											2008-06-30 10:50:23 +03:00
										 |  |  | static const unsigned int param_nopnp = 1; | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2008-06-30 10:50:23 +03:00
										 |  |  | static unsigned int param_io = 0x248; | 
					
						
							|  |  |  | static unsigned int param_irq = 6; | 
					
						
							|  |  |  | static int param_dma = 2; | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Basic functions | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static inline void wbsd_unlock_config(struct wbsd_host *host) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	BUG_ON(host->config == 0); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	outb(host->unlock_code, host->config); | 
					
						
							|  |  |  | 	outb(host->unlock_code, host->config); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static inline void wbsd_lock_config(struct wbsd_host *host) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	BUG_ON(host->config == 0); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	outb(LOCK_CODE, host->config); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static inline void wbsd_write_config(struct wbsd_host *host, u8 reg, u8 value) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	BUG_ON(host->config == 0); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	outb(reg, host->config); | 
					
						
							|  |  |  | 	outb(value, host->config + 1); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static inline u8 wbsd_read_config(struct wbsd_host *host, u8 reg) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	BUG_ON(host->config == 0); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	outb(reg, host->config); | 
					
						
							|  |  |  | 	return inb(host->config + 1); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static inline void wbsd_write_index(struct wbsd_host *host, u8 index, u8 value) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	outb(index, host->base + WBSD_IDXR); | 
					
						
							|  |  |  | 	outb(value, host->base + WBSD_DATAR); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static inline u8 wbsd_read_index(struct wbsd_host *host, u8 index) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	outb(index, host->base + WBSD_IDXR); | 
					
						
							|  |  |  | 	return inb(host->base + WBSD_DATAR); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Common routines | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static void wbsd_init_device(struct wbsd_host *host) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	u8 setup, ier; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Reset chip (SD/MMC part) and fifo. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	setup = wbsd_read_index(host, WBSD_IDX_SETUP); | 
					
						
							|  |  |  | 	setup |= WBSD_FIFO_RESET | WBSD_SOFT_RESET; | 
					
						
							|  |  |  | 	wbsd_write_index(host, WBSD_IDX_SETUP, setup); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Set DAT3 to input | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	setup &= ~WBSD_DAT3_H; | 
					
						
							|  |  |  | 	wbsd_write_index(host, WBSD_IDX_SETUP, setup); | 
					
						
							|  |  |  | 	host->flags &= ~WBSD_FIGNORE_DETECT; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Read back default clock. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	host->clk = wbsd_read_index(host, WBSD_IDX_CLK); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Power down port. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	outb(WBSD_POWER_N, host->base + WBSD_CSR); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Set maximum timeout. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	wbsd_write_index(host, WBSD_IDX_TAAC, 0x7F); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Test for card presence | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (inb(host->base + WBSD_CSR) & WBSD_CARDPRESENT) | 
					
						
							|  |  |  | 		host->flags |= WBSD_FCARD_PRESENT; | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		host->flags &= ~WBSD_FCARD_PRESENT; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Enable interesting interrupts. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	ier = 0; | 
					
						
							|  |  |  | 	ier |= WBSD_EINT_CARD; | 
					
						
							|  |  |  | 	ier |= WBSD_EINT_FIFO_THRE; | 
					
						
							|  |  |  | 	ier |= WBSD_EINT_CRC; | 
					
						
							| 
									
										
										
										
											2007-04-13 23:25:59 +02:00
										 |  |  | 	ier |= WBSD_EINT_TIMEOUT; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	ier |= WBSD_EINT_TC; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	outb(ier, host->base + WBSD_EIR); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Clear interrupts. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	inb(host->base + WBSD_ISR); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static void wbsd_reset(struct wbsd_host *host) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	u8 setup; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-11-05 10:36:35 +00:00
										 |  |  | 	printk(KERN_ERR "%s: Resetting chip\n", mmc_hostname(host->mmc)); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Soft reset of chip (SD/MMC part). | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	setup = wbsd_read_index(host, WBSD_IDX_SETUP); | 
					
						
							|  |  |  | 	setup |= WBSD_SOFT_RESET; | 
					
						
							|  |  |  | 	wbsd_write_index(host, WBSD_IDX_SETUP, setup); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static void wbsd_request_end(struct wbsd_host *host, struct mmc_request *mrq) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	unsigned long dmaflags; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	if (host->dma >= 0) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Release ISA DMA controller. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		dmaflags = claim_dma_lock(); | 
					
						
							|  |  |  | 		disable_dma(host->dma); | 
					
						
							|  |  |  | 		clear_dma_ff(host->dma); | 
					
						
							|  |  |  | 		release_dma_lock(dmaflags); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Disable DMA on host. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		wbsd_write_index(host, WBSD_IDX_DMA, 0); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	host->mrq = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * MMC layer might call back into the driver so first unlock. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	spin_unlock(&host->lock); | 
					
						
							|  |  |  | 	mmc_request_done(host->mmc, mrq); | 
					
						
							|  |  |  | 	spin_lock(&host->lock); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Scatter/gather functions | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static inline void wbsd_init_sg(struct wbsd_host *host, struct mmc_data *data) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Get info. about SG list from data structure. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	host->cur_sg = data->sg; | 
					
						
							|  |  |  | 	host->num_sg = data->sg_len; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	host->offset = 0; | 
					
						
							|  |  |  | 	host->remain = host->cur_sg->length; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static inline int wbsd_next_sg(struct wbsd_host *host) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Skip to next SG entry. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	host->cur_sg++; | 
					
						
							|  |  |  | 	host->num_sg--; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Any entries left? | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	if (host->num_sg > 0) { | 
					
						
							|  |  |  | 		host->offset = 0; | 
					
						
							|  |  |  | 		host->remain = host->cur_sg->length; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	return host->num_sg; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-01-31 18:20:48 +01:00
										 |  |  | static inline char *wbsd_sg_to_buffer(struct wbsd_host *host) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2007-10-22 21:19:53 +02:00
										 |  |  | 	return sg_virt(host->cur_sg); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static inline void wbsd_sg_to_dma(struct wbsd_host *host, struct mmc_data *data) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2007-04-13 19:04:38 +02:00
										 |  |  | 	unsigned int len, i; | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	struct scatterlist *sg; | 
					
						
							|  |  |  | 	char *dmabuf = host->dma_buffer; | 
					
						
							|  |  |  | 	char *sgbuf; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	sg = data->sg; | 
					
						
							|  |  |  | 	len = data->sg_len; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	for (i = 0; i < len; i++) { | 
					
						
							| 
									
										
										
										
											2007-10-22 21:19:53 +02:00
										 |  |  | 		sgbuf = sg_virt(&sg[i]); | 
					
						
							| 
									
										
										
										
											2007-04-13 19:04:38 +02:00
										 |  |  | 		memcpy(dmabuf, sgbuf, sg[i].length); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		dmabuf += sg[i].length; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static inline void wbsd_dma_to_sg(struct wbsd_host *host, struct mmc_data *data) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2007-04-13 19:04:38 +02:00
										 |  |  | 	unsigned int len, i; | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	struct scatterlist *sg; | 
					
						
							|  |  |  | 	char *dmabuf = host->dma_buffer; | 
					
						
							|  |  |  | 	char *sgbuf; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	sg = data->sg; | 
					
						
							|  |  |  | 	len = data->sg_len; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	for (i = 0; i < len; i++) { | 
					
						
							| 
									
										
										
										
											2007-10-22 21:19:53 +02:00
										 |  |  | 		sgbuf = sg_virt(&sg[i]); | 
					
						
							| 
									
										
										
										
											2007-04-13 19:04:38 +02:00
										 |  |  | 		memcpy(sgbuf, dmabuf, sg[i].length); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		dmabuf += sg[i].length; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Command handling | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static inline void wbsd_get_short_reply(struct wbsd_host *host, | 
					
						
							|  |  |  | 					struct mmc_command *cmd) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Correct response type? | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_SHORT) { | 
					
						
							| 
									
										
										
										
											2007-07-22 22:18:46 +02:00
										 |  |  | 		cmd->error = -EILSEQ; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	cmd->resp[0]  = wbsd_read_index(host, WBSD_IDX_RESP12) << 24; | 
					
						
							|  |  |  | 	cmd->resp[0] |= wbsd_read_index(host, WBSD_IDX_RESP13) << 16; | 
					
						
							|  |  |  | 	cmd->resp[0] |= wbsd_read_index(host, WBSD_IDX_RESP14) << 8; | 
					
						
							|  |  |  | 	cmd->resp[0] |= wbsd_read_index(host, WBSD_IDX_RESP15) << 0; | 
					
						
							|  |  |  | 	cmd->resp[1]  = wbsd_read_index(host, WBSD_IDX_RESP16) << 24; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static inline void wbsd_get_long_reply(struct wbsd_host *host, | 
					
						
							|  |  |  | 	struct mmc_command *cmd) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Correct response type? | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_LONG) { | 
					
						
							| 
									
										
										
										
											2007-07-22 22:18:46 +02:00
										 |  |  | 		cmd->error = -EILSEQ; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	for (i = 0; i < 4; i++) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		cmd->resp[i] = | 
					
						
							|  |  |  | 			wbsd_read_index(host, WBSD_IDX_RESP1 + i * 4) << 24; | 
					
						
							|  |  |  | 		cmd->resp[i] |= | 
					
						
							|  |  |  | 			wbsd_read_index(host, WBSD_IDX_RESP2 + i * 4) << 16; | 
					
						
							|  |  |  | 		cmd->resp[i] |= | 
					
						
							|  |  |  | 			wbsd_read_index(host, WBSD_IDX_RESP3 + i * 4) << 8; | 
					
						
							|  |  |  | 		cmd->resp[i] |= | 
					
						
							|  |  |  | 			wbsd_read_index(host, WBSD_IDX_RESP4 + i * 4) << 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static void wbsd_send_command(struct wbsd_host *host, struct mmc_command *cmd) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 	u8 status, isr; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Clear accumulated ISR. The interrupt routine | 
					
						
							|  |  |  | 	 * will fill this one with events that occur during | 
					
						
							|  |  |  | 	 * transfer. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	host->isr = 0; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Send the command (CRC calculated by host). | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	outb(cmd->opcode, host->base + WBSD_CMDR); | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	for (i = 3; i >= 0; i--) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		outb((cmd->arg >> (i * 8)) & 0xff, host->base + WBSD_CMDR); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-22 22:18:46 +02:00
										 |  |  | 	cmd->error = 0; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Wait for the request to complete. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	do { | 
					
						
							|  |  |  | 		status = wbsd_read_index(host, WBSD_IDX_STATUS); | 
					
						
							|  |  |  | 	} while (status & WBSD_CARDTRAFFIC); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Do we expect a reply? | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2006-02-02 12:23:12 +00:00
										 |  |  | 	if (cmd->flags & MMC_RSP_PRESENT) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Read back status. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		isr = host->isr; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		/* Card removed? */ | 
					
						
							|  |  |  | 		if (isr & WBSD_INT_CARD) | 
					
						
							| 
									
										
										
										
											2007-07-22 22:18:46 +02:00
										 |  |  | 			cmd->error = -ENOMEDIUM; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		/* Timeout? */ | 
					
						
							|  |  |  | 		else if (isr & WBSD_INT_TIMEOUT) | 
					
						
							| 
									
										
										
										
											2007-07-22 22:18:46 +02:00
										 |  |  | 			cmd->error = -ETIMEDOUT; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		/* CRC? */ | 
					
						
							|  |  |  | 		else if ((cmd->flags & MMC_RSP_CRC) && (isr & WBSD_INT_CRC)) | 
					
						
							| 
									
										
										
										
											2007-07-22 22:18:46 +02:00
										 |  |  | 			cmd->error = -EILSEQ; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		/* All ok */ | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 		else { | 
					
						
							| 
									
										
										
										
											2006-02-02 12:23:12 +00:00
										 |  |  | 			if (cmd->flags & MMC_RSP_136) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 				wbsd_get_long_reply(host, cmd); | 
					
						
							| 
									
										
										
										
											2006-02-02 12:23:12 +00:00
										 |  |  | 			else | 
					
						
							|  |  |  | 				wbsd_get_short_reply(host, cmd); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Data functions | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static void wbsd_empty_fifo(struct wbsd_host *host) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	struct mmc_data *data = host->mrq->cmd->data; | 
					
						
							|  |  |  | 	char *buffer; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	int i, fsr, fifo; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Handle excessive data. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2007-04-13 19:04:38 +02:00
										 |  |  | 	if (host->num_sg == 0) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		return; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-01-31 18:20:48 +01:00
										 |  |  | 	buffer = wbsd_sg_to_buffer(host) + host->offset; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Drain the fifo. This has a tendency to loop longer | 
					
						
							|  |  |  | 	 * than the FIFO length (usually one block). | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	while (!((fsr = inb(host->base + WBSD_FSR)) & WBSD_FIFO_EMPTY)) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * The size field in the FSR is broken so we have to | 
					
						
							|  |  |  | 		 * do some guessing. | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		if (fsr & WBSD_FIFO_FULL) | 
					
						
							|  |  |  | 			fifo = 16; | 
					
						
							|  |  |  | 		else if (fsr & WBSD_FIFO_FUTHRE) | 
					
						
							|  |  |  | 			fifo = 8; | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			fifo = 1; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 		for (i = 0; i < fifo; i++) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			*buffer = inb(host->base + WBSD_DFR); | 
					
						
							|  |  |  | 			buffer++; | 
					
						
							|  |  |  | 			host->offset++; | 
					
						
							|  |  |  | 			host->remain--; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			data->bytes_xfered++; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			/*
 | 
					
						
							|  |  |  | 			 * End of scatter list entry? | 
					
						
							|  |  |  | 			 */ | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 			if (host->remain == 0) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 				/*
 | 
					
						
							|  |  |  | 				 * Get next entry. Check if last. | 
					
						
							|  |  |  | 				 */ | 
					
						
							| 
									
										
										
										
											2007-04-13 19:04:38 +02:00
										 |  |  | 				if (!wbsd_next_sg(host)) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 					return; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-01-31 18:20:48 +01:00
										 |  |  | 				buffer = wbsd_sg_to_buffer(host); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * This is a very dirty hack to solve a | 
					
						
							|  |  |  | 	 * hardware problem. The chip doesn't trigger | 
					
						
							|  |  |  | 	 * FIFO threshold interrupts properly. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2007-04-13 19:04:38 +02:00
										 |  |  | 	if ((data->blocks * data->blksz - data->bytes_xfered) < 16) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		tasklet_schedule(&host->fifo_tasklet); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static void wbsd_fill_fifo(struct wbsd_host *host) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	struct mmc_data *data = host->mrq->cmd->data; | 
					
						
							|  |  |  | 	char *buffer; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	int i, fsr, fifo; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Check that we aren't being called after the | 
					
						
							|  |  |  | 	 * entire buffer has been transfered. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2007-04-13 19:04:38 +02:00
										 |  |  | 	if (host->num_sg == 0) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-01-31 18:20:48 +01:00
										 |  |  | 	buffer = wbsd_sg_to_buffer(host) + host->offset; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Fill the fifo. This has a tendency to loop longer | 
					
						
							|  |  |  | 	 * than the FIFO length (usually one block). | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	while (!((fsr = inb(host->base + WBSD_FSR)) & WBSD_FIFO_FULL)) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * The size field in the FSR is broken so we have to | 
					
						
							|  |  |  | 		 * do some guessing. | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		if (fsr & WBSD_FIFO_EMPTY) | 
					
						
							|  |  |  | 			fifo = 0; | 
					
						
							|  |  |  | 		else if (fsr & WBSD_FIFO_EMTHRE) | 
					
						
							|  |  |  | 			fifo = 8; | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			fifo = 15; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 		for (i = 16; i > fifo; i--) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			outb(*buffer, host->base + WBSD_DFR); | 
					
						
							|  |  |  | 			buffer++; | 
					
						
							|  |  |  | 			host->offset++; | 
					
						
							|  |  |  | 			host->remain--; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			data->bytes_xfered++; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			/*
 | 
					
						
							|  |  |  | 			 * End of scatter list entry? | 
					
						
							|  |  |  | 			 */ | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 			if (host->remain == 0) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 				/*
 | 
					
						
							|  |  |  | 				 * Get next entry. Check if last. | 
					
						
							|  |  |  | 				 */ | 
					
						
							| 
									
										
										
										
											2007-04-13 19:04:38 +02:00
										 |  |  | 				if (!wbsd_next_sg(host)) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 					return; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-01-31 18:20:48 +01:00
										 |  |  | 				buffer = wbsd_sg_to_buffer(host); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * The controller stops sending interrupts for | 
					
						
							|  |  |  | 	 * 'FIFO empty' under certain conditions. So we | 
					
						
							|  |  |  | 	 * need to be a bit more pro-active. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	tasklet_schedule(&host->fifo_tasklet); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	u16 blksize; | 
					
						
							|  |  |  | 	u8 setup; | 
					
						
							|  |  |  | 	unsigned long dmaflags; | 
					
						
							| 
									
										
										
										
											2007-04-13 19:04:38 +02:00
										 |  |  | 	unsigned int size; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Calculate size. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2007-04-13 19:04:38 +02:00
										 |  |  | 	size = data->blocks * data->blksz; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Check timeout values for overflow. | 
					
						
							|  |  |  | 	 * (Yes, some cards cause this value to overflow). | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (data->timeout_ns > 127000000) | 
					
						
							|  |  |  | 		wbsd_write_index(host, WBSD_IDX_TAAC, 127); | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	else { | 
					
						
							|  |  |  | 		wbsd_write_index(host, WBSD_IDX_TAAC, | 
					
						
							|  |  |  | 			data->timeout_ns / 1000000); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	if (data->timeout_clks > 255) | 
					
						
							|  |  |  | 		wbsd_write_index(host, WBSD_IDX_NSAC, 255); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		wbsd_write_index(host, WBSD_IDX_NSAC, data->timeout_clks); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Inform the chip of how large blocks will be | 
					
						
							|  |  |  | 	 * sent. It needs this to determine when to | 
					
						
							|  |  |  | 	 * calculate CRC. | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * Space for CRC must be included in the size. | 
					
						
							| 
									
										
										
										
											2005-09-06 15:18:57 -07:00
										 |  |  | 	 * Two bytes are needed for each data line. | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	if (host->bus_width == MMC_BUS_WIDTH_1) { | 
					
						
							| 
									
										
										
										
											2006-05-19 21:48:03 +01:00
										 |  |  | 		blksize = data->blksz + 2; | 
					
						
							| 
									
										
										
										
											2005-09-06 15:18:57 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		wbsd_write_index(host, WBSD_IDX_PBSMSB, (blksize >> 4) & 0xF0); | 
					
						
							|  |  |  | 		wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF); | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	} else if (host->bus_width == MMC_BUS_WIDTH_4) { | 
					
						
							| 
									
										
										
										
											2006-05-19 21:48:03 +01:00
										 |  |  | 		blksize = data->blksz + 2 * 4; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 		wbsd_write_index(host, WBSD_IDX_PBSMSB, | 
					
						
							|  |  |  | 			((blksize >> 4) & 0xF0) | WBSD_DATA_WIDTH); | 
					
						
							| 
									
										
										
										
											2005-09-06 15:18:57 -07:00
										 |  |  | 		wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF); | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2007-07-22 22:18:46 +02:00
										 |  |  | 		data->error = -EINVAL; | 
					
						
							| 
									
										
										
										
											2005-09-06 15:18:57 -07:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Clear the FIFO. This is needed even for DMA | 
					
						
							|  |  |  | 	 * transfers since the chip still uses the FIFO | 
					
						
							|  |  |  | 	 * internally. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	setup = wbsd_read_index(host, WBSD_IDX_SETUP); | 
					
						
							|  |  |  | 	setup |= WBSD_FIFO_RESET; | 
					
						
							|  |  |  | 	wbsd_write_index(host, WBSD_IDX_SETUP, setup); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * DMA transfer? | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	if (host->dma >= 0) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * The buffer for DMA is only 64 kB. | 
					
						
							|  |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2007-04-13 19:04:38 +02:00
										 |  |  | 		BUG_ON(size > 0x10000); | 
					
						
							|  |  |  | 		if (size > 0x10000) { | 
					
						
							| 
									
										
										
										
											2007-07-22 22:18:46 +02:00
										 |  |  | 			data->error = -EINVAL; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			return; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Transfer data from the SG list to | 
					
						
							|  |  |  | 		 * the DMA buffer. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		if (data->flags & MMC_DATA_WRITE) | 
					
						
							|  |  |  | 			wbsd_sg_to_dma(host, data); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Initialise the ISA DMA controller. | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		dmaflags = claim_dma_lock(); | 
					
						
							|  |  |  | 		disable_dma(host->dma); | 
					
						
							|  |  |  | 		clear_dma_ff(host->dma); | 
					
						
							|  |  |  | 		if (data->flags & MMC_DATA_READ) | 
					
						
							|  |  |  | 			set_dma_mode(host->dma, DMA_MODE_READ & ~0x40); | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			set_dma_mode(host->dma, DMA_MODE_WRITE & ~0x40); | 
					
						
							|  |  |  | 		set_dma_addr(host->dma, host->dma_addr); | 
					
						
							| 
									
										
										
										
											2007-04-13 19:04:38 +02:00
										 |  |  | 		set_dma_count(host->dma, size); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		enable_dma(host->dma); | 
					
						
							|  |  |  | 		release_dma_lock(dmaflags); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Enable DMA on the host. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		wbsd_write_index(host, WBSD_IDX_DMA, WBSD_DMA_ENABLE); | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * This flag is used to keep printk | 
					
						
							|  |  |  | 		 * output to a minimum. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		host->firsterr = 1; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Initialise the SG list. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		wbsd_init_sg(host, data); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Turn off DMA. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		wbsd_write_index(host, WBSD_IDX_DMA, 0); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Set up FIFO threshold levels (and fill | 
					
						
							|  |  |  | 		 * buffer if doing a write). | 
					
						
							|  |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 		if (data->flags & MMC_DATA_READ) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			wbsd_write_index(host, WBSD_IDX_FIFOEN, | 
					
						
							|  |  |  | 				WBSD_FIFOEN_FULL | 8); | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			wbsd_write_index(host, WBSD_IDX_FIFOEN, | 
					
						
							|  |  |  | 				WBSD_FIFOEN_EMPTY | 8); | 
					
						
							|  |  |  | 			wbsd_fill_fifo(host); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-22 22:18:46 +02:00
										 |  |  | 	data->error = 0; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	unsigned long dmaflags; | 
					
						
							|  |  |  | 	int count; | 
					
						
							|  |  |  | 	u8 status; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	WARN_ON(host->mrq == NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Send a stop command if needed. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (data->stop) | 
					
						
							|  |  |  | 		wbsd_send_command(host, data->stop); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Wait for the controller to leave data | 
					
						
							|  |  |  | 	 * transfer state. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	do { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		status = wbsd_read_index(host, WBSD_IDX_STATUS); | 
					
						
							|  |  |  | 	} while (status & (WBSD_BLOCK_READ | WBSD_BLOCK_WRITE)); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * DMA transfer? | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	if (host->dma >= 0) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Disable DMA on the host. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		wbsd_write_index(host, WBSD_IDX_DMA, 0); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Turn of ISA DMA controller. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		dmaflags = claim_dma_lock(); | 
					
						
							|  |  |  | 		disable_dma(host->dma); | 
					
						
							|  |  |  | 		clear_dma_ff(host->dma); | 
					
						
							|  |  |  | 		count = get_dma_residue(host->dma); | 
					
						
							|  |  |  | 		release_dma_lock(dmaflags); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-04-13 19:04:38 +02:00
										 |  |  | 		data->bytes_xfered = host->mrq->data->blocks * | 
					
						
							|  |  |  | 			host->mrq->data->blksz - count; | 
					
						
							|  |  |  | 		data->bytes_xfered -= data->bytes_xfered % data->blksz; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Any leftover data? | 
					
						
							|  |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 		if (count) { | 
					
						
							| 
									
										
										
										
											2005-11-05 10:36:35 +00:00
										 |  |  | 			printk(KERN_ERR "%s: Incomplete DMA transfer. " | 
					
						
							|  |  |  | 				"%d bytes left.\n", | 
					
						
							|  |  |  | 				mmc_hostname(host->mmc), count); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-22 22:18:46 +02:00
										 |  |  | 			if (!data->error) | 
					
						
							|  |  |  | 				data->error = -EIO; | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			/*
 | 
					
						
							|  |  |  | 			 * Transfer data from DMA buffer to | 
					
						
							|  |  |  | 			 * SG list. | 
					
						
							|  |  |  | 			 */ | 
					
						
							|  |  |  | 			if (data->flags & MMC_DATA_READ) | 
					
						
							|  |  |  | 				wbsd_dma_to_sg(host, data); | 
					
						
							| 
									
										
										
										
											2007-04-13 19:04:38 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-22 22:18:46 +02:00
										 |  |  | 		if (data->error) { | 
					
						
							| 
									
										
										
										
											2007-04-13 19:04:38 +02:00
										 |  |  | 			if (data->bytes_xfered) | 
					
						
							|  |  |  | 				data->bytes_xfered -= data->blksz; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	wbsd_request_end(host, host->mrq); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | /*****************************************************************************\
 | 
					
						
							|  |  |  |  *                                                                           * | 
					
						
							|  |  |  |  * MMC layer callbacks                                                       * | 
					
						
							|  |  |  |  *                                                                           * | 
					
						
							|  |  |  | \*****************************************************************************/ | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	struct wbsd_host *host = mmc_priv(mmc); | 
					
						
							|  |  |  | 	struct mmc_command *cmd; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Disable tasklets to avoid a deadlock. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	spin_lock_bh(&host->lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BUG_ON(host->mrq != NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cmd = mrq->cmd; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	host->mrq = mrq; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/*
 | 
					
						
							| 
									
										
										
										
											2007-07-22 22:18:46 +02:00
										 |  |  | 	 * Check that there is actually a card in the slot. | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	if (!(host->flags & WBSD_FCARD_PRESENT)) { | 
					
						
							| 
									
										
										
										
											2007-07-22 22:18:46 +02:00
										 |  |  | 		cmd->error = -ENOMEDIUM; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		goto done; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	if (cmd->data) { | 
					
						
							| 
									
										
										
										
											2006-11-21 17:45:37 +01:00
										 |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * The hardware is so delightfully stupid that it has a list | 
					
						
							|  |  |  | 		 * of "data" commands. If a command isn't on this list, it'll | 
					
						
							|  |  |  | 		 * just go back to the idle state and won't send any data | 
					
						
							|  |  |  | 		 * interrupts. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		switch (cmd->opcode) { | 
					
						
							|  |  |  | 		case 11: | 
					
						
							|  |  |  | 		case 17: | 
					
						
							|  |  |  | 		case 18: | 
					
						
							|  |  |  | 		case 20: | 
					
						
							|  |  |  | 		case 24: | 
					
						
							|  |  |  | 		case 25: | 
					
						
							|  |  |  | 		case 26: | 
					
						
							|  |  |  | 		case 27: | 
					
						
							|  |  |  | 		case 30: | 
					
						
							|  |  |  | 		case 42: | 
					
						
							|  |  |  | 		case 56: | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* ACMDs. We don't keep track of state, so we just treat them
 | 
					
						
							|  |  |  | 		 * like any other command. */ | 
					
						
							|  |  |  | 		case 51: | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | #ifdef CONFIG_MMC_DEBUG
 | 
					
						
							|  |  |  | 			printk(KERN_WARNING "%s: Data command %d is not " | 
					
						
							|  |  |  | 				"supported by this controller.\n", | 
					
						
							|  |  |  | 				mmc_hostname(host->mmc), cmd->opcode); | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2007-07-22 22:18:46 +02:00
										 |  |  | 			cmd->error = -EINVAL; | 
					
						
							| 
									
										
										
										
											2006-11-21 17:45:37 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			goto done; | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2007-05-01 13:35:19 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2006-11-21 17:45:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-05-01 13:35:19 +02:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Does the request include data? | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (cmd->data) { | 
					
						
							|  |  |  | 		wbsd_prepare_data(host, cmd->data); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-22 22:18:46 +02:00
										 |  |  | 		if (cmd->data->error) | 
					
						
							| 
									
										
										
										
											2007-05-01 13:35:19 +02:00
										 |  |  | 			goto done; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	wbsd_send_command(host, cmd); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * If this is a data transfer the request | 
					
						
							|  |  |  | 	 * will be finished after the data has | 
					
						
							|  |  |  | 	 * transfered. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2007-07-22 22:18:46 +02:00
										 |  |  | 	if (cmd->data && !cmd->error) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Dirty fix for hardware bug. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		if (host->dma == -1) | 
					
						
							|  |  |  | 			tasklet_schedule(&host->fifo_tasklet); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		spin_unlock_bh(&host->lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | done: | 
					
						
							|  |  |  | 	wbsd_request_end(host, mrq); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_unlock_bh(&host->lock); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static void wbsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	struct wbsd_host *host = mmc_priv(mmc); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	u8 clk, setup, pwr; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	spin_lock_bh(&host->lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Reset the chip on each power off. | 
					
						
							|  |  |  | 	 * Should clear out any weird states. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (ios->power_mode == MMC_POWER_OFF) | 
					
						
							|  |  |  | 		wbsd_init_device(host); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	if (ios->clock >= 24000000) | 
					
						
							|  |  |  | 		clk = WBSD_CLK_24M; | 
					
						
							|  |  |  | 	else if (ios->clock >= 16000000) | 
					
						
							|  |  |  | 		clk = WBSD_CLK_16M; | 
					
						
							|  |  |  | 	else if (ios->clock >= 12000000) | 
					
						
							|  |  |  | 		clk = WBSD_CLK_12M; | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		clk = WBSD_CLK_375K; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Only write to the clock register when | 
					
						
							|  |  |  | 	 * there is an actual change. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	if (clk != host->clk) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		wbsd_write_index(host, WBSD_IDX_CLK, clk); | 
					
						
							|  |  |  | 		host->clk = clk; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Power up card. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	if (ios->power_mode != MMC_POWER_OFF) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		pwr = inb(host->base + WBSD_CSR); | 
					
						
							|  |  |  | 		pwr &= ~WBSD_POWER_N; | 
					
						
							|  |  |  | 		outb(pwr, host->base + WBSD_CSR); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * MMC cards need to have pin 1 high during init. | 
					
						
							|  |  |  | 	 * It wreaks havoc with the card detection though so | 
					
						
							| 
									
										
										
										
											2005-09-03 16:45:49 +01:00
										 |  |  | 	 * that needs to be disabled. | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	 */ | 
					
						
							|  |  |  | 	setup = wbsd_read_index(host, WBSD_IDX_SETUP); | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	if (ios->chip_select == MMC_CS_HIGH) { | 
					
						
							| 
									
										
										
										
											2005-09-06 15:18:57 -07:00
										 |  |  | 		BUG_ON(ios->bus_width != MMC_BUS_WIDTH_1); | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 		setup |= WBSD_DAT3_H; | 
					
						
							|  |  |  | 		host->flags |= WBSD_FIGNORE_DETECT; | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	} else { | 
					
						
							|  |  |  | 		if (setup & WBSD_DAT3_H) { | 
					
						
							| 
									
										
										
										
											2005-10-28 21:37:29 +01:00
										 |  |  | 			setup &= ~WBSD_DAT3_H; | 
					
						
							| 
									
										
										
										
											2005-09-03 16:45:49 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-10-28 21:37:29 +01:00
										 |  |  | 			/*
 | 
					
						
							|  |  |  | 			 * We cannot resume card detection immediatly | 
					
						
							|  |  |  | 			 * because of capacitance and delays in the chip. | 
					
						
							|  |  |  | 			 */ | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 			mod_timer(&host->ignore_timer, jiffies + HZ / 100); | 
					
						
							| 
									
										
										
										
											2005-10-28 21:37:29 +01:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	wbsd_write_index(host, WBSD_IDX_SETUP, setup); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-09-06 15:18:57 -07:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Store bus width for later. Will be used when | 
					
						
							|  |  |  | 	 * setting up the data transfer. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	host->bus_width = ios->bus_width; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	spin_unlock_bh(&host->lock); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static int wbsd_get_ro(struct mmc_host *mmc) | 
					
						
							| 
									
										
										
										
											2005-09-06 15:18:57 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	struct wbsd_host *host = mmc_priv(mmc); | 
					
						
							| 
									
										
										
										
											2005-09-06 15:18:57 -07:00
										 |  |  | 	u8 csr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock_bh(&host->lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	csr = inb(host->base + WBSD_CSR); | 
					
						
							|  |  |  | 	csr |= WBSD_MSLED; | 
					
						
							|  |  |  | 	outb(csr, host->base + WBSD_CSR); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mdelay(1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	csr = inb(host->base + WBSD_CSR); | 
					
						
							|  |  |  | 	csr &= ~WBSD_MSLED; | 
					
						
							|  |  |  | 	outb(csr, host->base + WBSD_CSR); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_unlock_bh(&host->lock); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-17 18:17:39 +04:00
										 |  |  | 	return !!(csr & WBSD_WRPT); | 
					
						
							| 
									
										
										
										
											2005-09-06 15:18:57 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-11-12 17:55:30 -08:00
										 |  |  | static const struct mmc_host_ops wbsd_ops = { | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	.request	= wbsd_request, | 
					
						
							|  |  |  | 	.set_ios	= wbsd_set_ios, | 
					
						
							| 
									
										
										
										
											2005-09-06 15:18:57 -07:00
										 |  |  | 	.get_ro		= wbsd_get_ro, | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*****************************************************************************\
 | 
					
						
							|  |  |  |  *                                                                           * | 
					
						
							|  |  |  |  * Interrupt handling                                                        * | 
					
						
							|  |  |  |  *                                                                           * | 
					
						
							|  |  |  | \*****************************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-09-03 16:45:49 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Helper function to reset detection ignore | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void wbsd_reset_ignore(unsigned long data) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	struct wbsd_host *host = (struct wbsd_host *)data; | 
					
						
							| 
									
										
										
										
											2005-09-03 16:45:49 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	BUG_ON(host == NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	DBG("Resetting card detection ignore\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock_bh(&host->lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	host->flags &= ~WBSD_FIGNORE_DETECT; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Card status might have changed during the | 
					
						
							|  |  |  | 	 * blackout. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	tasklet_schedule(&host->card_tasklet); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_unlock_bh(&host->lock); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Tasklets | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static inline struct mmc_data *wbsd_get_data(struct wbsd_host *host) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	WARN_ON(!host->mrq); | 
					
						
							|  |  |  | 	if (!host->mrq) | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	WARN_ON(!host->mrq->cmd); | 
					
						
							|  |  |  | 	if (!host->mrq->cmd) | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	WARN_ON(!host->mrq->cmd->data); | 
					
						
							|  |  |  | 	if (!host->mrq->cmd->data) | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	return host->mrq->cmd->data; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void wbsd_tasklet_card(unsigned long param) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	struct wbsd_host *host = (struct wbsd_host *)param; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	u8 csr; | 
					
						
							| 
									
										
										
										
											2005-09-12 20:36:19 +01:00
										 |  |  | 	int delay = -1; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	spin_lock(&host->lock); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	if (host->flags & WBSD_FIGNORE_DETECT) { | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 		spin_unlock(&host->lock); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	csr = inb(host->base + WBSD_CSR); | 
					
						
							|  |  |  | 	WARN_ON(csr == 0xff); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	if (csr & WBSD_CARDPRESENT) { | 
					
						
							|  |  |  | 		if (!(host->flags & WBSD_FCARD_PRESENT)) { | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 			DBG("Card inserted\n"); | 
					
						
							|  |  |  | 			host->flags |= WBSD_FCARD_PRESENT; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-09-12 20:36:19 +01:00
										 |  |  | 			delay = 500; | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	} else if (host->flags & WBSD_FCARD_PRESENT) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		DBG("Card removed\n"); | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 		host->flags &= ~WBSD_FCARD_PRESENT; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 		if (host->mrq) { | 
					
						
							| 
									
										
										
										
											2005-11-05 10:36:35 +00:00
										 |  |  | 			printk(KERN_ERR "%s: Card removed during transfer!\n", | 
					
						
							|  |  |  | 				mmc_hostname(host->mmc)); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			wbsd_reset(host); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-22 22:18:46 +02:00
										 |  |  | 			host->mrq->cmd->error = -ENOMEDIUM; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			tasklet_schedule(&host->finish_tasklet); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-09-12 20:36:19 +01:00
										 |  |  | 		delay = 0; | 
					
						
							| 
									
										
										
										
											2005-07-01 12:13:55 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-09-12 20:36:19 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Unlock first since we might get a call back. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_unlock(&host->lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (delay != -1) | 
					
						
							|  |  |  | 		mmc_detect_change(host->mmc, msecs_to_jiffies(delay)); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void wbsd_tasklet_fifo(unsigned long param) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	struct wbsd_host *host = (struct wbsd_host *)param; | 
					
						
							|  |  |  | 	struct mmc_data *data; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	spin_lock(&host->lock); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	if (!host->mrq) | 
					
						
							|  |  |  | 		goto end; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	data = wbsd_get_data(host); | 
					
						
							|  |  |  | 	if (!data) | 
					
						
							|  |  |  | 		goto end; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (data->flags & MMC_DATA_WRITE) | 
					
						
							|  |  |  | 		wbsd_fill_fifo(host); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		wbsd_empty_fifo(host); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Done? | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2007-04-13 19:04:38 +02:00
										 |  |  | 	if (host->num_sg == 0) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		wbsd_write_index(host, WBSD_IDX_FIFOEN, 0); | 
					
						
							|  |  |  | 		tasklet_schedule(&host->finish_tasklet); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | end: | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	spin_unlock(&host->lock); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void wbsd_tasklet_crc(unsigned long param) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	struct wbsd_host *host = (struct wbsd_host *)param; | 
					
						
							|  |  |  | 	struct mmc_data *data; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	spin_lock(&host->lock); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	if (!host->mrq) | 
					
						
							|  |  |  | 		goto end; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	data = wbsd_get_data(host); | 
					
						
							|  |  |  | 	if (!data) | 
					
						
							|  |  |  | 		goto end; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	DBGF("CRC error\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-22 22:18:46 +02:00
										 |  |  | 	data->error = -EILSEQ; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	tasklet_schedule(&host->finish_tasklet); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | end: | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	spin_unlock(&host->lock); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void wbsd_tasklet_timeout(unsigned long param) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	struct wbsd_host *host = (struct wbsd_host *)param; | 
					
						
							|  |  |  | 	struct mmc_data *data; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	spin_lock(&host->lock); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	if (!host->mrq) | 
					
						
							|  |  |  | 		goto end; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	data = wbsd_get_data(host); | 
					
						
							|  |  |  | 	if (!data) | 
					
						
							|  |  |  | 		goto end; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	DBGF("Timeout\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-22 22:18:46 +02:00
										 |  |  | 	data->error = -ETIMEDOUT; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	tasklet_schedule(&host->finish_tasklet); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | end: | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	spin_unlock(&host->lock); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void wbsd_tasklet_finish(unsigned long param) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	struct wbsd_host *host = (struct wbsd_host *)param; | 
					
						
							|  |  |  | 	struct mmc_data *data; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	spin_lock(&host->lock); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	WARN_ON(!host->mrq); | 
					
						
							|  |  |  | 	if (!host->mrq) | 
					
						
							|  |  |  | 		goto end; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	data = wbsd_get_data(host); | 
					
						
							|  |  |  | 	if (!data) | 
					
						
							|  |  |  | 		goto end; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	wbsd_finish_data(host, data); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | end: | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	spin_unlock(&host->lock); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Interrupt handling | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around.  On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable.  On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions.  Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller.  A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386.  I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs.  Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
	struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
	set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
	-	update_process_times(user_mode(regs));
	-	profile_tick(CPU_PROFILING, regs);
	+	update_process_times(user_mode(get_irq_regs()));
	+	profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
 (*) input_dev() is now gone entirely.  The regs pointer is no longer stored in
     the input_dev struct.
 (*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking.  It does
     something different depending on whether it's been supplied with a regs
     pointer or not.
 (*) Various IRQ handler function pointers have been moved to type
     irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
											
										 
											2006-10-05 14:55:46 +01:00
										 |  |  | static irqreturn_t wbsd_irq(int irq, void *dev_id) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	struct wbsd_host *host = dev_id; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	int isr; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	isr = inb(host->base + WBSD_ISR); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Was it actually our hardware that caused the interrupt? | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (isr == 0xff || isr == 0x00) | 
					
						
							|  |  |  | 		return IRQ_NONE; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	host->isr |= isr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Schedule tasklets as needed. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (isr & WBSD_INT_CARD) | 
					
						
							|  |  |  | 		tasklet_schedule(&host->card_tasklet); | 
					
						
							|  |  |  | 	if (isr & WBSD_INT_FIFO_THRE) | 
					
						
							|  |  |  | 		tasklet_schedule(&host->fifo_tasklet); | 
					
						
							|  |  |  | 	if (isr & WBSD_INT_CRC) | 
					
						
							|  |  |  | 		tasklet_hi_schedule(&host->crc_tasklet); | 
					
						
							|  |  |  | 	if (isr & WBSD_INT_TIMEOUT) | 
					
						
							|  |  |  | 		tasklet_hi_schedule(&host->timeout_tasklet); | 
					
						
							|  |  |  | 	if (isr & WBSD_INT_TC) | 
					
						
							|  |  |  | 		tasklet_schedule(&host->finish_tasklet); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	return IRQ_HANDLED; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | /*****************************************************************************\
 | 
					
						
							|  |  |  |  *                                                                           * | 
					
						
							|  |  |  |  * Device initialisation and shutdown                                        * | 
					
						
							|  |  |  |  *                                                                           * | 
					
						
							|  |  |  | \*****************************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  |  * Allocate/free MMC structure. | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static int __devinit wbsd_alloc_mmc(struct device *dev) | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	struct mmc_host *mmc; | 
					
						
							|  |  |  | 	struct wbsd_host *host; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Allocate MMC structure. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	mmc = mmc_alloc_host(sizeof(struct wbsd_host), dev); | 
					
						
							|  |  |  | 	if (!mmc) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	host = mmc_priv(mmc); | 
					
						
							|  |  |  | 	host->mmc = mmc; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	host->dma = -1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Set host parameters. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	mmc->ops = &wbsd_ops; | 
					
						
							|  |  |  | 	mmc->f_min = 375000; | 
					
						
							|  |  |  | 	mmc->f_max = 24000000; | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; | 
					
						
							| 
									
										
										
										
											2008-07-06 01:10:27 +02:00
										 |  |  | 	mmc->caps = MMC_CAP_4_BIT_DATA; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	spin_lock_init(&host->lock); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-07-01 12:13:55 +01:00
										 |  |  | 	/*
 | 
					
						
							| 
									
										
										
										
											2005-09-03 16:45:49 +01:00
										 |  |  | 	 * Set up timers | 
					
						
							| 
									
										
										
										
											2005-07-01 12:13:55 +01:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2005-09-03 16:45:49 +01:00
										 |  |  | 	init_timer(&host->ignore_timer); | 
					
						
							|  |  |  | 	host->ignore_timer.data = (unsigned long)host; | 
					
						
							|  |  |  | 	host->ignore_timer.function = wbsd_reset_ignore; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Maximum number of segments. Worst case is one sector per segment | 
					
						
							|  |  |  | 	 * so this will be 64kB/512. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	mmc->max_hw_segs = 128; | 
					
						
							|  |  |  | 	mmc->max_phys_segs = 128; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	/*
 | 
					
						
							| 
									
										
										
										
											2006-11-21 17:55:45 +01:00
										 |  |  | 	 * Maximum request size. Also limited by 64KiB buffer. | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2006-11-21 17:55:45 +01:00
										 |  |  | 	mmc->max_req_size = 65536; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Maximum segment size. Could be one segment with the maximum number | 
					
						
							| 
									
										
										
										
											2006-11-21 17:55:45 +01:00
										 |  |  | 	 * of bytes. | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2006-11-21 17:55:45 +01:00
										 |  |  | 	mmc->max_seg_size = mmc->max_req_size; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-11-21 17:54:23 +01:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Maximum block size. We have 12 bits (= 4095) but have to subtract | 
					
						
							|  |  |  | 	 * space for CRC. So the maximum is 4095 - 4*2 = 4087. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	mmc->max_blk_size = 4087; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-11-21 17:55:45 +01:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Maximum block count. There is no real limit so the maximum | 
					
						
							|  |  |  | 	 * request size will be the only restriction. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	mmc->max_blk_count = mmc->max_req_size; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	dev_set_drvdata(dev, mmc); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-02 20:20:44 +02:00
										 |  |  | static void wbsd_free_mmc(struct device *dev) | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	struct mmc_host *mmc; | 
					
						
							|  |  |  | 	struct wbsd_host *host; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	mmc = dev_get_drvdata(dev); | 
					
						
							|  |  |  | 	if (!mmc) | 
					
						
							|  |  |  | 		return; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-07-01 12:13:55 +01:00
										 |  |  | 	host = mmc_priv(mmc); | 
					
						
							|  |  |  | 	BUG_ON(host == NULL); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-09-03 16:45:49 +01:00
										 |  |  | 	del_timer_sync(&host->ignore_timer); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	mmc_free_host(mmc); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	dev_set_drvdata(dev, NULL); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Scan for known chip id:s | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static int __devinit wbsd_scan(struct wbsd_host *host) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	int i, j, k; | 
					
						
							|  |  |  | 	int id; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Iterate through all ports, all codes to | 
					
						
							|  |  |  | 	 * find hardware that is in our known list. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2006-01-03 22:56:56 +00:00
										 |  |  | 	for (i = 0; i < ARRAY_SIZE(config_ports); i++) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		if (!request_region(config_ports[i], 2, DRIVER_NAME)) | 
					
						
							|  |  |  | 			continue; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-03 22:56:56 +00:00
										 |  |  | 		for (j = 0; j < ARRAY_SIZE(unlock_codes); j++) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			id = 0xFFFF; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-10-28 21:37:29 +01:00
										 |  |  | 			host->config = config_ports[i]; | 
					
						
							|  |  |  | 			host->unlock_code = unlock_codes[j]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			wbsd_unlock_config(host); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			outb(WBSD_CONF_ID_HI, config_ports[i]); | 
					
						
							|  |  |  | 			id = inb(config_ports[i] + 1) << 8; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			outb(WBSD_CONF_ID_LO, config_ports[i]); | 
					
						
							|  |  |  | 			id |= inb(config_ports[i] + 1); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-10-28 21:37:29 +01:00
										 |  |  | 			wbsd_lock_config(host); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-03 22:56:56 +00:00
										 |  |  | 			for (k = 0; k < ARRAY_SIZE(valid_ids); k++) { | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 				if (id == valid_ids[k]) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 					host->chip_id = id; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 					return 0; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 			if (id != 0xFFFF) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 				DBG("Unknown hardware (id %x) found at %x\n", | 
					
						
							|  |  |  | 					id, config_ports[i]); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		release_region(config_ports[i], 2); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-10-28 21:37:29 +01:00
										 |  |  | 	host->config = 0; | 
					
						
							|  |  |  | 	host->unlock_code = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	return -ENODEV; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Allocate/free io port ranges | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static int __devinit wbsd_request_region(struct wbsd_host *host, int base) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2006-08-06 22:22:23 +02:00
										 |  |  | 	if (base & 0x7) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		return -EINVAL; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	if (!request_region(base, 8, DRIVER_NAME)) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		return -EIO; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-08-06 22:22:23 +02:00
										 |  |  | 	host->base = base; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-02 20:20:44 +02:00
										 |  |  | static void wbsd_release_regions(struct wbsd_host *host) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	if (host->base) | 
					
						
							|  |  |  | 		release_region(host->base, 8); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	host->base = 0; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (host->config) | 
					
						
							|  |  |  | 		release_region(host->config, 2); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	host->config = 0; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Allocate/free DMA port and buffer | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static void __devinit wbsd_request_dma(struct wbsd_host *host, int dma) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	if (dma < 0) | 
					
						
							|  |  |  | 		return; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	if (request_dma(dma, DRIVER_NAME)) | 
					
						
							|  |  |  | 		goto err; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * We need to allocate a special buffer in | 
					
						
							|  |  |  | 	 * order for ISA to be able to DMA to it. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	host->dma_buffer = kmalloc(WBSD_DMA_SIZE, | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		GFP_NOIO | GFP_DMA | __GFP_REPEAT | __GFP_NOWARN); | 
					
						
							|  |  |  | 	if (!host->dma_buffer) | 
					
						
							|  |  |  | 		goto free; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Translate the address to a physical address. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2006-09-12 17:00:10 +02:00
										 |  |  | 	host->dma_addr = dma_map_single(mmc_dev(host->mmc), host->dma_buffer, | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 		WBSD_DMA_SIZE, DMA_BIDIRECTIONAL); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * ISA DMA must be aligned on a 64k basis. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if ((host->dma_addr & 0xffff) != 0) | 
					
						
							|  |  |  | 		goto kfree; | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * ISA cannot access memory above 16 MB. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	else if (host->dma_addr >= 0x1000000) | 
					
						
							|  |  |  | 		goto kfree; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	host->dma = dma; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	return; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | kfree: | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * If we've gotten here then there is some kind of alignment bug | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	BUG_ON(1); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-09-12 17:00:10 +02:00
										 |  |  | 	dma_unmap_single(mmc_dev(host->mmc), host->dma_addr, | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 		WBSD_DMA_SIZE, DMA_BIDIRECTIONAL); | 
					
						
							| 
									
										
										
										
											2008-07-06 00:51:07 +02:00
										 |  |  | 	host->dma_addr = 0; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	kfree(host->dma_buffer); | 
					
						
							|  |  |  | 	host->dma_buffer = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | free: | 
					
						
							|  |  |  | 	free_dma(dma); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | err: | 
					
						
							|  |  |  | 	printk(KERN_WARNING DRIVER_NAME ": Unable to allocate DMA %d. " | 
					
						
							|  |  |  | 		"Falling back on FIFO.\n", dma); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-02 20:20:44 +02:00
										 |  |  | static void wbsd_release_dma(struct wbsd_host *host) | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	if (host->dma_addr) { | 
					
						
							| 
									
										
										
										
											2006-09-12 17:00:10 +02:00
										 |  |  | 		dma_unmap_single(mmc_dev(host->mmc), host->dma_addr, | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 			WBSD_DMA_SIZE, DMA_BIDIRECTIONAL); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-11-07 01:01:32 -08:00
										 |  |  | 	kfree(host->dma_buffer); | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	if (host->dma >= 0) | 
					
						
							|  |  |  | 		free_dma(host->dma); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	host->dma = -1; | 
					
						
							|  |  |  | 	host->dma_buffer = NULL; | 
					
						
							| 
									
										
										
										
											2008-07-06 00:51:07 +02:00
										 |  |  | 	host->dma_addr = 0; | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  |  * Allocate/free IRQ. | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static int __devinit wbsd_request_irq(struct wbsd_host *host, int irq) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	int ret; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/*
 | 
					
						
							| 
									
										
										
										
											2008-06-12 15:21:42 -07:00
										 |  |  | 	 * Set up tasklets. Must be done before requesting interrupt. | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	tasklet_init(&host->card_tasklet, wbsd_tasklet_card, | 
					
						
							|  |  |  | 			(unsigned long)host); | 
					
						
							|  |  |  | 	tasklet_init(&host->fifo_tasklet, wbsd_tasklet_fifo, | 
					
						
							|  |  |  | 			(unsigned long)host); | 
					
						
							|  |  |  | 	tasklet_init(&host->crc_tasklet, wbsd_tasklet_crc, | 
					
						
							|  |  |  | 			(unsigned long)host); | 
					
						
							|  |  |  | 	tasklet_init(&host->timeout_tasklet, wbsd_tasklet_timeout, | 
					
						
							|  |  |  | 			(unsigned long)host); | 
					
						
							|  |  |  | 	tasklet_init(&host->finish_tasklet, wbsd_tasklet_finish, | 
					
						
							|  |  |  | 			(unsigned long)host); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-12 15:21:42 -07:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Allocate interrupt. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	ret = request_irq(irq, wbsd_irq, IRQF_SHARED, DRIVER_NAME, host); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	host->irq = irq; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-02 20:20:44 +02:00
										 |  |  | static void  wbsd_release_irq(struct wbsd_host *host) | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | { | 
					
						
							|  |  |  | 	if (!host->irq) | 
					
						
							|  |  |  | 		return; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	free_irq(host->irq, host); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	host->irq = 0; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	tasklet_kill(&host->card_tasklet); | 
					
						
							|  |  |  | 	tasklet_kill(&host->fifo_tasklet); | 
					
						
							|  |  |  | 	tasklet_kill(&host->crc_tasklet); | 
					
						
							|  |  |  | 	tasklet_kill(&host->timeout_tasklet); | 
					
						
							|  |  |  | 	tasklet_kill(&host->finish_tasklet); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Allocate all resources for the host. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static int __devinit wbsd_request_resources(struct wbsd_host *host, | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	int base, int irq, int dma) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int ret; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Allocate I/O ports. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	ret = wbsd_request_region(host, base); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	if (ret) | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 		return ret; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	 * Allocate interrupt. | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	ret = wbsd_request_irq(host, irq); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Allocate DMA. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	wbsd_request_dma(host, dma); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Release all resources for the host. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-02 20:20:44 +02:00
										 |  |  | static void wbsd_release_resources(struct wbsd_host *host) | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | { | 
					
						
							|  |  |  | 	wbsd_release_dma(host); | 
					
						
							|  |  |  | 	wbsd_release_irq(host); | 
					
						
							|  |  |  | 	wbsd_release_regions(host); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Configure the resources the chip should use. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static void wbsd_chip_config(struct wbsd_host *host) | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2005-10-28 21:37:29 +01:00
										 |  |  | 	wbsd_unlock_config(host); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Reset the chip. | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	wbsd_write_config(host, WBSD_CONF_SWRST, 1); | 
					
						
							|  |  |  | 	wbsd_write_config(host, WBSD_CONF_SWRST, 0); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Select SD/MMC function. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Set up card detection. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	wbsd_write_config(host, WBSD_CONF_PINS, WBSD_PINS_DETECT_GP11); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/*
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	 * Configure chip | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	 */ | 
					
						
							|  |  |  | 	wbsd_write_config(host, WBSD_CONF_PORT_HI, host->base >> 8); | 
					
						
							|  |  |  | 	wbsd_write_config(host, WBSD_CONF_PORT_LO, host->base & 0xff); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	wbsd_write_config(host, WBSD_CONF_IRQ, host->irq); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	if (host->dma >= 0) | 
					
						
							|  |  |  | 		wbsd_write_config(host, WBSD_CONF_DRQ, host->dma); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/*
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	 * Enable and power up chip. | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	wbsd_write_config(host, WBSD_CONF_ENABLE, 1); | 
					
						
							|  |  |  | 	wbsd_write_config(host, WBSD_CONF_POWER, 0x20); | 
					
						
							| 
									
										
										
										
											2005-10-28 21:37:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	wbsd_lock_config(host); | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Check that configured resources are correct. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static int wbsd_chip_validate(struct wbsd_host *host) | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | { | 
					
						
							|  |  |  | 	int base, irq, dma; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-10-28 21:37:29 +01:00
										 |  |  | 	wbsd_unlock_config(host); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/*
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	 * Select SD/MMC function. | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/*
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	 * Read configuration. | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	base = wbsd_read_config(host, WBSD_CONF_PORT_HI) << 8; | 
					
						
							|  |  |  | 	base |= wbsd_read_config(host, WBSD_CONF_PORT_LO); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	irq = wbsd_read_config(host, WBSD_CONF_IRQ); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	dma = wbsd_read_config(host, WBSD_CONF_DRQ); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-10-28 21:37:29 +01:00
										 |  |  | 	wbsd_lock_config(host); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/*
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	 * Validate against given configuration. | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	if (base != host->base) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	if (irq != host->irq) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	if ((dma != host->dma) && (host->dma != -1)) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	return 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-10-28 21:37:29 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Powers down the SD function | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static void wbsd_chip_poweroff(struct wbsd_host *host) | 
					
						
							| 
									
										
										
										
											2005-10-28 21:37:29 +01:00
										 |  |  | { | 
					
						
							|  |  |  | 	wbsd_unlock_config(host); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD); | 
					
						
							|  |  |  | 	wbsd_write_config(host, WBSD_CONF_ENABLE, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	wbsd_lock_config(host); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | /*****************************************************************************\
 | 
					
						
							|  |  |  |  *                                                                           * | 
					
						
							|  |  |  |  * Devices setup and shutdown                                                * | 
					
						
							|  |  |  |  *                                                                           * | 
					
						
							|  |  |  | \*****************************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static int __devinit wbsd_init(struct device *dev, int base, int irq, int dma, | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	int pnp) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	struct wbsd_host *host = NULL; | 
					
						
							|  |  |  | 	struct mmc_host *mmc = NULL; | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	int ret; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	ret = wbsd_alloc_mmc(dev); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	mmc = dev_get_drvdata(dev); | 
					
						
							|  |  |  | 	host = mmc_priv(mmc); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/*
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	 * Scan for hardware. | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	ret = wbsd_scan(host); | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	if (ret) { | 
					
						
							|  |  |  | 		if (pnp && (ret == -ENODEV)) { | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 			printk(KERN_WARNING DRIVER_NAME | 
					
						
							|  |  |  | 				": Unable to confirm device presence. You may " | 
					
						
							|  |  |  | 				"experience lock-ups.\n"); | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 			wbsd_free_mmc(dev); | 
					
						
							|  |  |  | 			return ret; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/*
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	 * Request resources. | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2006-08-07 01:40:04 +02:00
										 |  |  | 	ret = wbsd_request_resources(host, base, irq, dma); | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	if (ret) { | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 		wbsd_release_resources(host); | 
					
						
							|  |  |  | 		wbsd_free_mmc(dev); | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/*
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	 * See if chip needs to be configured. | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	if (pnp) { | 
					
						
							|  |  |  | 		if ((host->config != 0) && !wbsd_chip_validate(host)) { | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 			printk(KERN_WARNING DRIVER_NAME | 
					
						
							|  |  |  | 				": PnP active but chip not configured! " | 
					
						
							|  |  |  | 				"You probably have a buggy BIOS. " | 
					
						
							|  |  |  | 				"Configuring chip manually.\n"); | 
					
						
							|  |  |  | 			wbsd_chip_config(host); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	} else | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 		wbsd_chip_config(host); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Power Management stuff. No idea how this works. | 
					
						
							|  |  |  | 	 * Not tested. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | #ifdef CONFIG_PM
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	if (host->config) { | 
					
						
							| 
									
										
										
										
											2005-10-28 21:37:29 +01:00
										 |  |  | 		wbsd_unlock_config(host); | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 		wbsd_write_config(host, WBSD_CONF_PME, 0xA0); | 
					
						
							| 
									
										
										
										
											2005-10-28 21:37:29 +01:00
										 |  |  | 		wbsd_lock_config(host); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Allow device to initialise itself properly. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	mdelay(5); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Reset the chip into a known state. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	wbsd_init_device(host); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	mmc_add_host(mmc); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-08-19 09:40:08 +01:00
										 |  |  | 	printk(KERN_INFO "%s: W83L51xD", mmc_hostname(mmc)); | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	if (host->chip_id != 0) | 
					
						
							|  |  |  | 		printk(" id %x", (int)host->chip_id); | 
					
						
							|  |  |  | 	printk(" at 0x%x irq %d", (int)host->base, (int)host->irq); | 
					
						
							|  |  |  | 	if (host->dma >= 0) | 
					
						
							|  |  |  | 		printk(" dma %d", (int)host->dma); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		printk(" FIFO"); | 
					
						
							|  |  |  | 	if (pnp) | 
					
						
							|  |  |  | 		printk(" PnP"); | 
					
						
							|  |  |  | 	printk("\n"); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static void __devexit wbsd_shutdown(struct device *dev, int pnp) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	struct mmc_host *mmc = dev_get_drvdata(dev); | 
					
						
							|  |  |  | 	struct wbsd_host *host; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	if (!mmc) | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 		return; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	host = mmc_priv(mmc); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	mmc_remove_host(mmc); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-10-28 21:37:29 +01:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Power down the SD/MMC function. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	if (!pnp) | 
					
						
							| 
									
										
										
										
											2005-10-28 21:37:29 +01:00
										 |  |  | 		wbsd_chip_poweroff(host); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	wbsd_release_resources(host); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	wbsd_free_mmc(dev); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Non-PnP | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static int __devinit wbsd_probe(struct platform_device *dev) | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2006-08-07 01:40:04 +02:00
										 |  |  | 	/* Use the module parameters for resources */ | 
					
						
							| 
									
										
										
										
											2008-06-30 10:50:23 +03:00
										 |  |  | 	return wbsd_init(&dev->dev, param_io, param_irq, param_dma, 0); | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static int __devexit wbsd_remove(struct platform_device *dev) | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2005-11-09 22:32:44 +00:00
										 |  |  | 	wbsd_shutdown(&dev->dev, 0); | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * PnP | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_PNP
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __devinit | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | wbsd_pnp_probe(struct pnp_dev *pnpdev, const struct pnp_device_id *dev_id) | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | { | 
					
						
							|  |  |  | 	int io, irq, dma; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Get resources from PnP layer. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	io = pnp_port_start(pnpdev, 0); | 
					
						
							|  |  |  | 	irq = pnp_irq(pnpdev, 0); | 
					
						
							|  |  |  | 	if (pnp_dma_valid(pnpdev, 0)) | 
					
						
							|  |  |  | 		dma = pnp_dma(pnpdev, 0); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		dma = -1; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	DBGF("PnP resources: port %3x irq %d dma %d\n", io, irq, dma); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	return wbsd_init(&pnpdev->dev, io, irq, dma, 1); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static void __devexit wbsd_pnp_remove(struct pnp_dev *dev) | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | { | 
					
						
							|  |  |  | 	wbsd_shutdown(&dev->dev, 1); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | #endif /* CONFIG_PNP */
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Power management | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_PM
 | 
					
						
							| 
									
										
										
										
											2005-10-28 21:37:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 14:21:52 +00:00
										 |  |  | static int wbsd_suspend(struct wbsd_host *host, pm_message_t state) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	BUG_ON(host == NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-05-26 14:42:08 -07:00
										 |  |  | 	return mmc_suspend_host(host->mmc); | 
					
						
							| 
									
										
										
										
											2006-01-08 14:21:52 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int wbsd_resume(struct wbsd_host *host) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	BUG_ON(host == NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	wbsd_init_device(host); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return mmc_resume_host(host->mmc); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | static int wbsd_platform_suspend(struct platform_device *dev, | 
					
						
							|  |  |  | 				 pm_message_t state) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2005-11-09 22:32:44 +00:00
										 |  |  | 	struct mmc_host *mmc = platform_get_drvdata(dev); | 
					
						
							| 
									
										
										
										
											2005-10-28 21:37:29 +01:00
										 |  |  | 	struct wbsd_host *host; | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 14:21:52 +00:00
										 |  |  | 	if (mmc == NULL) | 
					
						
							| 
									
										
										
										
											2005-10-28 21:37:29 +01:00
										 |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 14:21:52 +00:00
										 |  |  | 	DBGF("Suspending...\n"); | 
					
						
							| 
									
										
										
										
											2005-10-28 21:37:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	host = mmc_priv(mmc); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 14:21:52 +00:00
										 |  |  | 	ret = wbsd_suspend(host, state); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-10-28 21:37:29 +01:00
										 |  |  | 	wbsd_chip_poweroff(host); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 14:21:52 +00:00
										 |  |  | static int wbsd_platform_resume(struct platform_device *dev) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2005-11-09 22:32:44 +00:00
										 |  |  | 	struct mmc_host *mmc = platform_get_drvdata(dev); | 
					
						
							| 
									
										
										
										
											2005-10-28 21:37:29 +01:00
										 |  |  | 	struct wbsd_host *host; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 14:21:52 +00:00
										 |  |  | 	if (mmc == NULL) | 
					
						
							| 
									
										
										
										
											2005-10-28 21:37:29 +01:00
										 |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 14:21:52 +00:00
										 |  |  | 	DBGF("Resuming...\n"); | 
					
						
							| 
									
										
										
										
											2005-10-28 21:37:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	host = mmc_priv(mmc); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	wbsd_chip_config(host); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Allow device to initialise itself properly. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	mdelay(5); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 14:21:52 +00:00
										 |  |  | 	return wbsd_resume(host); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_PNP
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int wbsd_pnp_suspend(struct pnp_dev *pnp_dev, pm_message_t state) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev); | 
					
						
							|  |  |  | 	struct wbsd_host *host; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (mmc == NULL) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							| 
									
										
										
										
											2005-10-28 21:37:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 14:21:52 +00:00
										 |  |  | 	DBGF("Suspending...\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	host = mmc_priv(mmc); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return wbsd_suspend(host, state); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2005-10-28 21:37:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 14:21:52 +00:00
										 |  |  | static int wbsd_pnp_resume(struct pnp_dev *pnp_dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev); | 
					
						
							|  |  |  | 	struct wbsd_host *host; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (mmc == NULL) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	DBGF("Resuming...\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	host = mmc_priv(mmc); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * See if chip needs to be configured. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 	if (host->config != 0) { | 
					
						
							|  |  |  | 		if (!wbsd_chip_validate(host)) { | 
					
						
							| 
									
										
										
										
											2006-01-08 14:21:52 +00:00
										 |  |  | 			printk(KERN_WARNING DRIVER_NAME | 
					
						
							|  |  |  | 				": PnP active but chip not configured! " | 
					
						
							|  |  |  | 				"You probably have a buggy BIOS. " | 
					
						
							|  |  |  | 				"Configuring chip manually.\n"); | 
					
						
							|  |  |  | 			wbsd_chip_config(host); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Allow device to initialise itself properly. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	mdelay(5); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return wbsd_resume(host); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #endif /* CONFIG_PNP */
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-10-28 21:37:29 +01:00
										 |  |  | #else /* CONFIG_PM */
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 14:21:52 +00:00
										 |  |  | #define wbsd_platform_suspend NULL
 | 
					
						
							|  |  |  | #define wbsd_platform_resume NULL
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define wbsd_pnp_suspend NULL
 | 
					
						
							|  |  |  | #define wbsd_pnp_resume NULL
 | 
					
						
							| 
									
										
										
										
											2005-10-28 21:37:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | #endif /* CONFIG_PM */
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | static struct platform_device *wbsd_device; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-11-09 22:32:44 +00:00
										 |  |  | static struct platform_driver wbsd_driver = { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	.probe		= wbsd_probe, | 
					
						
							| 
									
										
										
										
											2005-11-09 23:21:06 +00:00
										 |  |  | 	.remove		= __devexit_p(wbsd_remove), | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-08 14:21:52 +00:00
										 |  |  | 	.suspend	= wbsd_platform_suspend, | 
					
						
							|  |  |  | 	.resume		= wbsd_platform_resume, | 
					
						
							| 
									
										
										
										
											2005-11-09 22:32:44 +00:00
										 |  |  | 	.driver		= { | 
					
						
							|  |  |  | 		.name	= DRIVER_NAME, | 
					
						
							| 
									
										
										
										
											2008-04-15 14:34:28 -07:00
										 |  |  | 		.owner	= THIS_MODULE, | 
					
						
							| 
									
										
										
										
											2005-11-09 22:32:44 +00:00
										 |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | #ifdef CONFIG_PNP
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct pnp_driver wbsd_pnp_driver = { | 
					
						
							|  |  |  | 	.name		= DRIVER_NAME, | 
					
						
							|  |  |  | 	.id_table	= pnp_dev_table, | 
					
						
							|  |  |  | 	.probe		= wbsd_pnp_probe, | 
					
						
							| 
									
										
										
										
											2005-11-09 23:21:06 +00:00
										 |  |  | 	.remove		= __devexit_p(wbsd_pnp_remove), | 
					
						
							| 
									
										
										
										
											2006-01-08 14:21:52 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	.suspend	= wbsd_pnp_suspend, | 
					
						
							|  |  |  | 	.resume		= wbsd_pnp_resume, | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #endif /* CONFIG_PNP */
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Module loading/unloading | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __init wbsd_drv_init(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int result; | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	printk(KERN_INFO DRIVER_NAME | 
					
						
							| 
									
										
										
										
											2007-02-09 08:19:07 +01:00
										 |  |  | 		": Winbond W83L51xD SD/MMC card interface driver\n"); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | #ifdef CONFIG_PNP
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-30 10:50:23 +03:00
										 |  |  | 	if (!param_nopnp) { | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 		result = pnp_register_driver(&wbsd_pnp_driver); | 
					
						
							|  |  |  | 		if (result < 0) | 
					
						
							|  |  |  | 			return result; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | #endif /* CONFIG_PNP */
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-30 10:50:23 +03:00
										 |  |  | 	if (param_nopnp) { | 
					
						
							| 
									
										
										
										
											2005-11-09 22:32:44 +00:00
										 |  |  | 		result = platform_driver_register(&wbsd_driver); | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 		if (result < 0) | 
					
						
							|  |  |  | 			return result; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-03 22:57:35 +00:00
										 |  |  | 		wbsd_device = platform_device_alloc(DRIVER_NAME, -1); | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 		if (!wbsd_device) { | 
					
						
							| 
									
										
										
										
											2006-01-03 22:57:35 +00:00
										 |  |  | 			platform_driver_unregister(&wbsd_driver); | 
					
						
							|  |  |  | 			return -ENOMEM; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		result = platform_device_add(wbsd_device); | 
					
						
							| 
									
										
										
										
											2006-01-08 18:17:55 +00:00
										 |  |  | 		if (result) { | 
					
						
							| 
									
										
										
										
											2006-01-03 22:57:35 +00:00
										 |  |  | 			platform_device_put(wbsd_device); | 
					
						
							|  |  |  | 			platform_driver_unregister(&wbsd_driver); | 
					
						
							|  |  |  | 			return result; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void __exit wbsd_drv_exit(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | #ifdef CONFIG_PNP
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-30 10:50:23 +03:00
										 |  |  | 	if (!param_nopnp) | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 		pnp_unregister_driver(&wbsd_pnp_driver); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | #endif /* CONFIG_PNP */
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-30 10:50:23 +03:00
										 |  |  | 	if (param_nopnp) { | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 		platform_device_unregister(wbsd_device); | 
					
						
							| 
									
										
										
										
											2005-09-12 12:09:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-11-09 22:32:44 +00:00
										 |  |  | 		platform_driver_unregister(&wbsd_driver); | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	DBG("unloaded\n"); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module_init(wbsd_drv_init); | 
					
						
							|  |  |  | module_exit(wbsd_drv_exit); | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | #ifdef CONFIG_PNP
 | 
					
						
							| 
									
										
										
										
											2008-06-30 10:50:23 +03:00
										 |  |  | module_param_named(nopnp, param_nopnp, uint, 0444); | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2008-06-30 10:50:23 +03:00
										 |  |  | module_param_named(io, param_io, uint, 0444); | 
					
						
							|  |  |  | module_param_named(irq, param_irq, uint, 0444); | 
					
						
							|  |  |  | module_param_named(dma, param_dma, int, 0444); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | MODULE_LICENSE("GPL"); | 
					
						
							| 
									
										
										
										
											2009-04-08 20:14:54 +02:00
										 |  |  | MODULE_AUTHOR("Pierre Ossman <pierre@ossman.eu>"); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | MODULE_DESCRIPTION("Winbond W83L51xD SD/MMC card interface driver"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-08 19:35:27 +01:00
										 |  |  | #ifdef CONFIG_PNP
 | 
					
						
							|  |  |  | MODULE_PARM_DESC(nopnp, "Scan for device instead of relying on PNP. (default 0)"); | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | MODULE_PARM_DESC(io, "I/O base to allocate. Must be 8 byte aligned. (default 0x248)"); | 
					
						
							|  |  |  | MODULE_PARM_DESC(irq, "IRQ to allocate. (default 6)"); | 
					
						
							|  |  |  | MODULE_PARM_DESC(dma, "DMA channel to allocate. -1 for no DMA. (default 2)"); |