| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
| Pierre Ossman | 70f1048 | 2007-07-11 20:04:50 +0200 | [diff] [blame] | 2 | *  linux/drivers/mmc/host/pxa.c - PXA MMCI driver | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3 | * | 
|  | 4 | *  Copyright (C) 2003 Russell King, All Rights Reserved. | 
|  | 5 | * | 
|  | 6 | * This program is free software; you can redistribute it and/or modify | 
|  | 7 | * it under the terms of the GNU General Public License version 2 as | 
|  | 8 | * published by the Free Software Foundation. | 
|  | 9 | * | 
|  | 10 | *  This hardware is really sick: | 
|  | 11 | *   - No way to clear interrupts. | 
|  | 12 | *   - Have to turn off the clock whenever we touch the device. | 
|  | 13 | *   - Doesn't tell you how many data blocks were transferred. | 
|  | 14 | *  Yuck! | 
|  | 15 | * | 
|  | 16 | *	1 and 3 byte data transfers not supported | 
|  | 17 | *	max block length up to 1023 | 
|  | 18 | */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 19 | #include <linux/module.h> | 
|  | 20 | #include <linux/init.h> | 
|  | 21 | #include <linux/ioport.h> | 
| Russell King | d052d1b | 2005-10-29 19:07:23 +0100 | [diff] [blame] | 22 | #include <linux/platform_device.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 23 | #include <linux/delay.h> | 
|  | 24 | #include <linux/interrupt.h> | 
|  | 25 | #include <linux/dma-mapping.h> | 
| Russell King | ebebd9b | 2007-08-20 10:20:03 +0100 | [diff] [blame] | 26 | #include <linux/clk.h> | 
|  | 27 | #include <linux/err.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 28 | #include <linux/mmc/host.h> | 
| Russell King | 05678a9 | 2008-11-28 16:04:54 +0000 | [diff] [blame] | 29 | #include <linux/io.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 30 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 31 | #include <asm/sizes.h> | 
|  | 32 |  | 
| Russell King | dcea83a | 2008-11-29 11:40:28 +0000 | [diff] [blame] | 33 | #include <mach/dma.h> | 
| Russell King | 05678a9 | 2008-11-28 16:04:54 +0000 | [diff] [blame] | 34 | #include <mach/hardware.h> | 
| Russell King | a09e64f | 2008-08-05 16:14:15 +0100 | [diff] [blame] | 35 | #include <mach/pxa-regs.h> | 
|  | 36 | #include <mach/mmc.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 37 |  | 
|  | 38 | #include "pxamci.h" | 
|  | 39 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 40 | #define DRIVER_NAME	"pxa2xx-mci" | 
|  | 41 |  | 
|  | 42 | #define NR_SG	1 | 
| Russell King | d8cb70d | 2007-10-26 17:56:40 +0100 | [diff] [blame] | 43 | #define CLKRT_OFF	(~0) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 44 |  | 
|  | 45 | struct pxamci_host { | 
|  | 46 | struct mmc_host		*mmc; | 
|  | 47 | spinlock_t		lock; | 
|  | 48 | struct resource		*res; | 
|  | 49 | void __iomem		*base; | 
| Russell King | ebebd9b | 2007-08-20 10:20:03 +0100 | [diff] [blame] | 50 | struct clk		*clk; | 
|  | 51 | unsigned long		clkrate; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 52 | int			irq; | 
|  | 53 | int			dma; | 
|  | 54 | unsigned int		clkrt; | 
|  | 55 | unsigned int		cmdat; | 
|  | 56 | unsigned int		imask; | 
|  | 57 | unsigned int		power_mode; | 
|  | 58 | struct pxamci_platform_data *pdata; | 
|  | 59 |  | 
|  | 60 | struct mmc_request	*mrq; | 
|  | 61 | struct mmc_command	*cmd; | 
|  | 62 | struct mmc_data		*data; | 
|  | 63 |  | 
|  | 64 | dma_addr_t		sg_dma; | 
|  | 65 | struct pxa_dma_desc	*sg_cpu; | 
|  | 66 | unsigned int		dma_len; | 
|  | 67 |  | 
|  | 68 | unsigned int		dma_dir; | 
| Bridge Wu | 9a788c6 | 2007-12-14 10:40:25 +0100 | [diff] [blame] | 69 | unsigned int		dma_drcmrrx; | 
|  | 70 | unsigned int		dma_drcmrtx; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 71 | }; | 
|  | 72 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 73 | static void pxamci_stop_clock(struct pxamci_host *host) | 
|  | 74 | { | 
|  | 75 | if (readl(host->base + MMC_STAT) & STAT_CLK_EN) { | 
|  | 76 | unsigned long timeout = 10000; | 
|  | 77 | unsigned int v; | 
|  | 78 |  | 
|  | 79 | writel(STOP_CLOCK, host->base + MMC_STRPCL); | 
|  | 80 |  | 
|  | 81 | do { | 
|  | 82 | v = readl(host->base + MMC_STAT); | 
|  | 83 | if (!(v & STAT_CLK_EN)) | 
|  | 84 | break; | 
|  | 85 | udelay(1); | 
|  | 86 | } while (timeout--); | 
|  | 87 |  | 
|  | 88 | if (v & STAT_CLK_EN) | 
|  | 89 | dev_err(mmc_dev(host->mmc), "unable to stop clock\n"); | 
|  | 90 | } | 
|  | 91 | } | 
|  | 92 |  | 
|  | 93 | static void pxamci_enable_irq(struct pxamci_host *host, unsigned int mask) | 
|  | 94 | { | 
|  | 95 | unsigned long flags; | 
|  | 96 |  | 
|  | 97 | spin_lock_irqsave(&host->lock, flags); | 
|  | 98 | host->imask &= ~mask; | 
|  | 99 | writel(host->imask, host->base + MMC_I_MASK); | 
|  | 100 | spin_unlock_irqrestore(&host->lock, flags); | 
|  | 101 | } | 
|  | 102 |  | 
|  | 103 | static void pxamci_disable_irq(struct pxamci_host *host, unsigned int mask) | 
|  | 104 | { | 
|  | 105 | unsigned long flags; | 
|  | 106 |  | 
|  | 107 | spin_lock_irqsave(&host->lock, flags); | 
|  | 108 | host->imask |= mask; | 
|  | 109 | writel(host->imask, host->base + MMC_I_MASK); | 
|  | 110 | spin_unlock_irqrestore(&host->lock, flags); | 
|  | 111 | } | 
|  | 112 |  | 
|  | 113 | static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) | 
|  | 114 | { | 
|  | 115 | unsigned int nob = data->blocks; | 
| Russell King | 3d63abe | 2006-04-24 11:27:02 +0100 | [diff] [blame] | 116 | unsigned long long clks; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 117 | unsigned int timeout; | 
| Philipp Zabel | 97f8571 | 2008-07-06 01:15:34 +0200 | [diff] [blame] | 118 | bool dalgn = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 119 | u32 dcmd; | 
|  | 120 | int i; | 
|  | 121 |  | 
|  | 122 | host->data = data; | 
|  | 123 |  | 
|  | 124 | if (data->flags & MMC_DATA_STREAM) | 
|  | 125 | nob = 0xffff; | 
|  | 126 |  | 
|  | 127 | writel(nob, host->base + MMC_NOB); | 
| Pavel Pisa | 2c171bf | 2006-05-19 21:48:03 +0100 | [diff] [blame] | 128 | writel(data->blksz, host->base + MMC_BLKLEN); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 129 |  | 
| Russell King | ebebd9b | 2007-08-20 10:20:03 +0100 | [diff] [blame] | 130 | clks = (unsigned long long)data->timeout_ns * host->clkrate; | 
| Russell King | 3d63abe | 2006-04-24 11:27:02 +0100 | [diff] [blame] | 131 | do_div(clks, 1000000000UL); | 
|  | 132 | timeout = (unsigned int)clks + (data->timeout_clks << host->clkrt); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 133 | writel((timeout + 255) / 256, host->base + MMC_RDTO); | 
|  | 134 |  | 
|  | 135 | if (data->flags & MMC_DATA_READ) { | 
|  | 136 | host->dma_dir = DMA_FROM_DEVICE; | 
|  | 137 | dcmd = DCMD_INCTRGADDR | DCMD_FLOWTRG; | 
| Bridge Wu | 9a788c6 | 2007-12-14 10:40:25 +0100 | [diff] [blame] | 138 | DRCMR(host->dma_drcmrtx) = 0; | 
|  | 139 | DRCMR(host->dma_drcmrrx) = host->dma | DRCMR_MAPVLD; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 140 | } else { | 
|  | 141 | host->dma_dir = DMA_TO_DEVICE; | 
|  | 142 | dcmd = DCMD_INCSRCADDR | DCMD_FLOWSRC; | 
| Bridge Wu | 9a788c6 | 2007-12-14 10:40:25 +0100 | [diff] [blame] | 143 | DRCMR(host->dma_drcmrrx) = 0; | 
|  | 144 | DRCMR(host->dma_drcmrtx) = host->dma | DRCMR_MAPVLD; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 145 | } | 
|  | 146 |  | 
|  | 147 | dcmd |= DCMD_BURST32 | DCMD_WIDTH1; | 
|  | 148 |  | 
|  | 149 | host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, | 
|  | 150 | host->dma_dir); | 
|  | 151 |  | 
|  | 152 | for (i = 0; i < host->dma_len; i++) { | 
| Nicolas Pitre | c783837 | 2007-10-09 17:07:58 -0400 | [diff] [blame] | 153 | unsigned int length = sg_dma_len(&data->sg[i]); | 
|  | 154 | host->sg_cpu[i].dcmd = dcmd | length; | 
|  | 155 | if (length & 31 && !(data->flags & MMC_DATA_READ)) | 
|  | 156 | host->sg_cpu[i].dcmd |= DCMD_ENDIRQEN; | 
| Philipp Zabel | 97f8571 | 2008-07-06 01:15:34 +0200 | [diff] [blame] | 157 | /* Not aligned to 8-byte boundary? */ | 
|  | 158 | if (sg_dma_address(&data->sg[i]) & 0x7) | 
|  | 159 | dalgn = 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 160 | if (data->flags & MMC_DATA_READ) { | 
|  | 161 | host->sg_cpu[i].dsadr = host->res->start + MMC_RXFIFO; | 
|  | 162 | host->sg_cpu[i].dtadr = sg_dma_address(&data->sg[i]); | 
|  | 163 | } else { | 
|  | 164 | host->sg_cpu[i].dsadr = sg_dma_address(&data->sg[i]); | 
|  | 165 | host->sg_cpu[i].dtadr = host->res->start + MMC_TXFIFO; | 
|  | 166 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 167 | host->sg_cpu[i].ddadr = host->sg_dma + (i + 1) * | 
|  | 168 | sizeof(struct pxa_dma_desc); | 
|  | 169 | } | 
|  | 170 | host->sg_cpu[host->dma_len - 1].ddadr = DDADR_STOP; | 
|  | 171 | wmb(); | 
|  | 172 |  | 
| Philipp Zabel | 97f8571 | 2008-07-06 01:15:34 +0200 | [diff] [blame] | 173 | /* | 
|  | 174 | * The PXA27x DMA controller encounters overhead when working with | 
|  | 175 | * unaligned (to 8-byte boundaries) data, so switch on byte alignment | 
|  | 176 | * mode only if we have unaligned data. | 
|  | 177 | */ | 
|  | 178 | if (dalgn) | 
|  | 179 | DALGN |= (1 << host->dma); | 
|  | 180 | else | 
| Karl Beldan | 4fe1689 | 2008-07-16 18:29:11 +0200 | [diff] [blame] | 181 | DALGN &= ~(1 << host->dma); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 182 | DDADR(host->dma) = host->sg_dma; | 
| Cliff Brake | b601895 | 2009-01-22 17:07:03 -0500 | [diff] [blame] | 183 |  | 
|  | 184 | /* | 
|  | 185 | * workaround for erratum #91: | 
|  | 186 | * only start DMA now if we are doing a read, | 
|  | 187 | * otherwise we wait until CMD/RESP has finished | 
|  | 188 | * before starting DMA. | 
|  | 189 | */ | 
|  | 190 | if (!cpu_is_pxa27x() || data->flags & MMC_DATA_READ) | 
|  | 191 | DCSR(host->dma) = DCSR_RUN; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 192 | } | 
|  | 193 |  | 
|  | 194 | static void pxamci_start_cmd(struct pxamci_host *host, struct mmc_command *cmd, unsigned int cmdat) | 
|  | 195 | { | 
|  | 196 | WARN_ON(host->cmd != NULL); | 
|  | 197 | host->cmd = cmd; | 
|  | 198 |  | 
|  | 199 | if (cmd->flags & MMC_RSP_BUSY) | 
|  | 200 | cmdat |= CMDAT_BUSY; | 
|  | 201 |  | 
| Russell King | e922517 | 2006-02-02 12:23:12 +0000 | [diff] [blame] | 202 | #define RSP_TYPE(x)	((x) & ~(MMC_RSP_BUSY|MMC_RSP_OPCODE)) | 
|  | 203 | switch (RSP_TYPE(mmc_resp_type(cmd))) { | 
| Philip Langdale | 6f94990 | 2007-01-04 07:04:47 -0800 | [diff] [blame] | 204 | case RSP_TYPE(MMC_RSP_R1): /* r1, r1b, r6, r7 */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 205 | cmdat |= CMDAT_RESP_SHORT; | 
|  | 206 | break; | 
| Russell King | e922517 | 2006-02-02 12:23:12 +0000 | [diff] [blame] | 207 | case RSP_TYPE(MMC_RSP_R3): | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 208 | cmdat |= CMDAT_RESP_R3; | 
|  | 209 | break; | 
| Russell King | e922517 | 2006-02-02 12:23:12 +0000 | [diff] [blame] | 210 | case RSP_TYPE(MMC_RSP_R2): | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 211 | cmdat |= CMDAT_RESP_R2; | 
|  | 212 | break; | 
|  | 213 | default: | 
|  | 214 | break; | 
|  | 215 | } | 
|  | 216 |  | 
|  | 217 | writel(cmd->opcode, host->base + MMC_CMD); | 
|  | 218 | writel(cmd->arg >> 16, host->base + MMC_ARGH); | 
|  | 219 | writel(cmd->arg & 0xffff, host->base + MMC_ARGL); | 
|  | 220 | writel(cmdat, host->base + MMC_CMDAT); | 
|  | 221 | writel(host->clkrt, host->base + MMC_CLKRT); | 
|  | 222 |  | 
|  | 223 | writel(START_CLOCK, host->base + MMC_STRPCL); | 
|  | 224 |  | 
|  | 225 | pxamci_enable_irq(host, END_CMD_RES); | 
|  | 226 | } | 
|  | 227 |  | 
|  | 228 | static void pxamci_finish_request(struct pxamci_host *host, struct mmc_request *mrq) | 
|  | 229 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 230 | host->mrq = NULL; | 
|  | 231 | host->cmd = NULL; | 
|  | 232 | host->data = NULL; | 
|  | 233 | mmc_request_done(host->mmc, mrq); | 
|  | 234 | } | 
|  | 235 |  | 
|  | 236 | static int pxamci_cmd_done(struct pxamci_host *host, unsigned int stat) | 
|  | 237 | { | 
|  | 238 | struct mmc_command *cmd = host->cmd; | 
|  | 239 | int i; | 
|  | 240 | u32 v; | 
|  | 241 |  | 
|  | 242 | if (!cmd) | 
|  | 243 | return 0; | 
|  | 244 |  | 
|  | 245 | host->cmd = NULL; | 
|  | 246 |  | 
|  | 247 | /* | 
|  | 248 | * Did I mention this is Sick.  We always need to | 
|  | 249 | * discard the upper 8 bits of the first 16-bit word. | 
|  | 250 | */ | 
|  | 251 | v = readl(host->base + MMC_RES) & 0xffff; | 
|  | 252 | for (i = 0; i < 4; i++) { | 
|  | 253 | u32 w1 = readl(host->base + MMC_RES) & 0xffff; | 
|  | 254 | u32 w2 = readl(host->base + MMC_RES) & 0xffff; | 
|  | 255 | cmd->resp[i] = v << 24 | w1 << 8 | w2 >> 8; | 
|  | 256 | v = w2; | 
|  | 257 | } | 
|  | 258 |  | 
|  | 259 | if (stat & STAT_TIME_OUT_RESPONSE) { | 
| Pierre Ossman | 17b0429 | 2007-07-22 22:18:46 +0200 | [diff] [blame] | 260 | cmd->error = -ETIMEDOUT; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 261 | } else if (stat & STAT_RES_CRC_ERR && cmd->flags & MMC_RSP_CRC) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 262 | /* | 
|  | 263 | * workaround for erratum #42: | 
|  | 264 | * Intel PXA27x Family Processor Specification Update Rev 001 | 
| Nicolas Pitre | 90e07d9 | 2007-05-13 18:03:08 +0200 | [diff] [blame] | 265 | * A bogus CRC error can appear if the msb of a 136 bit | 
|  | 266 | * response is a one. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 267 | */ | 
| Cliff Brake | e10a854 | 2009-01-22 16:58:58 -0500 | [diff] [blame] | 268 | if (cpu_is_pxa27x() && | 
|  | 269 | (cmd->flags & MMC_RSP_136 && cmd->resp[0] & 0x80000000)) | 
| Nicolas Pitre | 90e07d9 | 2007-05-13 18:03:08 +0200 | [diff] [blame] | 270 | pr_debug("ignoring CRC from command %d - *risky*\n", cmd->opcode); | 
| Cliff Brake | e10a854 | 2009-01-22 16:58:58 -0500 | [diff] [blame] | 271 | else | 
|  | 272 | cmd->error = -EILSEQ; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 273 | } | 
|  | 274 |  | 
|  | 275 | pxamci_disable_irq(host, END_CMD_RES); | 
| Pierre Ossman | 17b0429 | 2007-07-22 22:18:46 +0200 | [diff] [blame] | 276 | if (host->data && !cmd->error) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 277 | pxamci_enable_irq(host, DATA_TRAN_DONE); | 
| Cliff Brake | b601895 | 2009-01-22 17:07:03 -0500 | [diff] [blame] | 278 | /* | 
|  | 279 | * workaround for erratum #91, if doing write | 
|  | 280 | * enable DMA late | 
|  | 281 | */ | 
|  | 282 | if (cpu_is_pxa27x() && host->data->flags & MMC_DATA_WRITE) | 
|  | 283 | DCSR(host->dma) = DCSR_RUN; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 284 | } else { | 
|  | 285 | pxamci_finish_request(host, host->mrq); | 
|  | 286 | } | 
|  | 287 |  | 
|  | 288 | return 1; | 
|  | 289 | } | 
|  | 290 |  | 
|  | 291 | static int pxamci_data_done(struct pxamci_host *host, unsigned int stat) | 
|  | 292 | { | 
|  | 293 | struct mmc_data *data = host->data; | 
|  | 294 |  | 
|  | 295 | if (!data) | 
|  | 296 | return 0; | 
|  | 297 |  | 
|  | 298 | DCSR(host->dma) = 0; | 
| Vernon Sauder | c00a46a | 2008-12-29 19:21:28 -0500 | [diff] [blame] | 299 | dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 300 | host->dma_dir); | 
|  | 301 |  | 
|  | 302 | if (stat & STAT_READ_TIME_OUT) | 
| Pierre Ossman | 17b0429 | 2007-07-22 22:18:46 +0200 | [diff] [blame] | 303 | data->error = -ETIMEDOUT; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 304 | else if (stat & (STAT_CRC_READ_ERROR|STAT_CRC_WRITE_ERROR)) | 
| Pierre Ossman | 17b0429 | 2007-07-22 22:18:46 +0200 | [diff] [blame] | 305 | data->error = -EILSEQ; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 306 |  | 
|  | 307 | /* | 
|  | 308 | * There appears to be a hardware design bug here.  There seems to | 
|  | 309 | * be no way to find out how much data was transferred to the card. | 
|  | 310 | * This means that if there was an error on any block, we mark all | 
|  | 311 | * data blocks as being in error. | 
|  | 312 | */ | 
| Pierre Ossman | 17b0429 | 2007-07-22 22:18:46 +0200 | [diff] [blame] | 313 | if (!data->error) | 
| Pavel Pisa | 2c171bf | 2006-05-19 21:48:03 +0100 | [diff] [blame] | 314 | data->bytes_xfered = data->blocks * data->blksz; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 315 | else | 
|  | 316 | data->bytes_xfered = 0; | 
|  | 317 |  | 
|  | 318 | pxamci_disable_irq(host, DATA_TRAN_DONE); | 
|  | 319 |  | 
|  | 320 | host->data = NULL; | 
| Russell King | 58741e8 | 2006-05-02 20:02:39 +0100 | [diff] [blame] | 321 | if (host->mrq->stop) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 322 | pxamci_stop_clock(host); | 
| Bridge Wu | df456f4 | 2007-09-25 19:09:19 +0200 | [diff] [blame] | 323 | pxamci_start_cmd(host, host->mrq->stop, host->cmdat); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 324 | } else { | 
|  | 325 | pxamci_finish_request(host, host->mrq); | 
|  | 326 | } | 
|  | 327 |  | 
|  | 328 | return 1; | 
|  | 329 | } | 
|  | 330 |  | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 331 | static irqreturn_t pxamci_irq(int irq, void *devid) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 332 | { | 
|  | 333 | struct pxamci_host *host = devid; | 
|  | 334 | unsigned int ireg; | 
|  | 335 | int handled = 0; | 
|  | 336 |  | 
| Bridge Wu | 81ab570f | 2007-09-25 18:59:07 +0200 | [diff] [blame] | 337 | ireg = readl(host->base + MMC_I_REG) & ~readl(host->base + MMC_I_MASK); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 338 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 339 | if (ireg) { | 
|  | 340 | unsigned stat = readl(host->base + MMC_STAT); | 
|  | 341 |  | 
| Russell King | d78e907 | 2006-05-02 20:18:53 +0100 | [diff] [blame] | 342 | pr_debug("PXAMCI: irq %08x stat %08x\n", ireg, stat); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 343 |  | 
|  | 344 | if (ireg & END_CMD_RES) | 
|  | 345 | handled |= pxamci_cmd_done(host, stat); | 
|  | 346 | if (ireg & DATA_TRAN_DONE) | 
|  | 347 | handled |= pxamci_data_done(host, stat); | 
| Bridge Wu | 5d3ad4e | 2007-09-25 19:11:00 +0200 | [diff] [blame] | 348 | if (ireg & SDIO_INT) { | 
|  | 349 | mmc_signal_sdio_irq(host->mmc); | 
|  | 350 | handled = 1; | 
|  | 351 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 352 | } | 
|  | 353 |  | 
|  | 354 | return IRQ_RETVAL(handled); | 
|  | 355 | } | 
|  | 356 |  | 
|  | 357 | static void pxamci_request(struct mmc_host *mmc, struct mmc_request *mrq) | 
|  | 358 | { | 
|  | 359 | struct pxamci_host *host = mmc_priv(mmc); | 
|  | 360 | unsigned int cmdat; | 
|  | 361 |  | 
|  | 362 | WARN_ON(host->mrq != NULL); | 
|  | 363 |  | 
|  | 364 | host->mrq = mrq; | 
|  | 365 |  | 
|  | 366 | pxamci_stop_clock(host); | 
|  | 367 |  | 
|  | 368 | cmdat = host->cmdat; | 
|  | 369 | host->cmdat &= ~CMDAT_INIT; | 
|  | 370 |  | 
|  | 371 | if (mrq->data) { | 
|  | 372 | pxamci_setup_data(host, mrq->data); | 
|  | 373 |  | 
|  | 374 | cmdat &= ~CMDAT_BUSY; | 
|  | 375 | cmdat |= CMDAT_DATAEN | CMDAT_DMAEN; | 
|  | 376 | if (mrq->data->flags & MMC_DATA_WRITE) | 
|  | 377 | cmdat |= CMDAT_WRITE; | 
|  | 378 |  | 
|  | 379 | if (mrq->data->flags & MMC_DATA_STREAM) | 
|  | 380 | cmdat |= CMDAT_STREAM; | 
|  | 381 | } | 
|  | 382 |  | 
|  | 383 | pxamci_start_cmd(host, mrq->cmd, cmdat); | 
|  | 384 | } | 
|  | 385 |  | 
| Richard Purdie | e619524 | 2005-09-06 15:18:56 -0700 | [diff] [blame] | 386 | static int pxamci_get_ro(struct mmc_host *mmc) | 
|  | 387 | { | 
|  | 388 | struct pxamci_host *host = mmc_priv(mmc); | 
|  | 389 |  | 
|  | 390 | if (host->pdata && host->pdata->get_ro) | 
| Anton Vorontsov | 08f80bb | 2008-06-17 18:17:39 +0400 | [diff] [blame] | 391 | return !!host->pdata->get_ro(mmc_dev(mmc)); | 
|  | 392 | /* | 
|  | 393 | * Board doesn't support read only detection; let the mmc core | 
|  | 394 | * decide what to do. | 
|  | 395 | */ | 
|  | 396 | return -ENOSYS; | 
| Richard Purdie | e619524 | 2005-09-06 15:18:56 -0700 | [diff] [blame] | 397 | } | 
|  | 398 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 399 | static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | 
|  | 400 | { | 
|  | 401 | struct pxamci_host *host = mmc_priv(mmc); | 
|  | 402 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 403 | if (ios->clock) { | 
| Russell King | ebebd9b | 2007-08-20 10:20:03 +0100 | [diff] [blame] | 404 | unsigned long rate = host->clkrate; | 
|  | 405 | unsigned int clk = rate / ios->clock; | 
|  | 406 |  | 
| Russell King | d8cb70d | 2007-10-26 17:56:40 +0100 | [diff] [blame] | 407 | if (host->clkrt == CLKRT_OFF) | 
|  | 408 | clk_enable(host->clk); | 
|  | 409 |  | 
| Bridge Wu | 64eb036 | 2007-12-13 07:24:30 +0100 | [diff] [blame] | 410 | if (ios->clock == 26000000) { | 
|  | 411 | /* to support 26MHz on pxa300/pxa310 */ | 
|  | 412 | host->clkrt = 7; | 
|  | 413 | } else { | 
|  | 414 | /* to handle (19.5MHz, 26MHz) */ | 
|  | 415 | if (!clk) | 
|  | 416 | clk = 1; | 
|  | 417 |  | 
|  | 418 | /* | 
|  | 419 | * clk might result in a lower divisor than we | 
|  | 420 | * desire.  check for that condition and adjust | 
|  | 421 | * as appropriate. | 
|  | 422 | */ | 
|  | 423 | if (rate / clk > ios->clock) | 
|  | 424 | clk <<= 1; | 
|  | 425 | host->clkrt = fls(clk) - 1; | 
|  | 426 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 427 |  | 
|  | 428 | /* | 
|  | 429 | * we write clkrt on the next command | 
|  | 430 | */ | 
|  | 431 | } else { | 
|  | 432 | pxamci_stop_clock(host); | 
| Russell King | d8cb70d | 2007-10-26 17:56:40 +0100 | [diff] [blame] | 433 | if (host->clkrt != CLKRT_OFF) { | 
|  | 434 | host->clkrt = CLKRT_OFF; | 
|  | 435 | clk_disable(host->clk); | 
|  | 436 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 437 | } | 
|  | 438 |  | 
|  | 439 | if (host->power_mode != ios->power_mode) { | 
|  | 440 | host->power_mode = ios->power_mode; | 
|  | 441 |  | 
|  | 442 | if (host->pdata && host->pdata->setpower) | 
| Sascha Hauer | 9e86619 | 2006-12-05 07:41:09 +0100 | [diff] [blame] | 443 | host->pdata->setpower(mmc_dev(mmc), ios->vdd); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 444 |  | 
|  | 445 | if (ios->power_mode == MMC_POWER_ON) | 
|  | 446 | host->cmdat |= CMDAT_INIT; | 
|  | 447 | } | 
|  | 448 |  | 
| Bridge Wu | df456f4 | 2007-09-25 19:09:19 +0200 | [diff] [blame] | 449 | if (ios->bus_width == MMC_BUS_WIDTH_4) | 
|  | 450 | host->cmdat |= CMDAT_SD_4DAT; | 
|  | 451 | else | 
|  | 452 | host->cmdat &= ~CMDAT_SD_4DAT; | 
|  | 453 |  | 
| Russell King | d78e907 | 2006-05-02 20:18:53 +0100 | [diff] [blame] | 454 | pr_debug("PXAMCI: clkrt = %x cmdat = %x\n", | 
| Russell King | c656317 | 2006-03-29 09:30:20 +0100 | [diff] [blame] | 455 | host->clkrt, host->cmdat); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 456 | } | 
|  | 457 |  | 
| Bridge Wu | 5d3ad4e | 2007-09-25 19:11:00 +0200 | [diff] [blame] | 458 | static void pxamci_enable_sdio_irq(struct mmc_host *host, int enable) | 
|  | 459 | { | 
|  | 460 | struct pxamci_host *pxa_host = mmc_priv(host); | 
|  | 461 |  | 
|  | 462 | if (enable) | 
|  | 463 | pxamci_enable_irq(pxa_host, SDIO_INT); | 
|  | 464 | else | 
|  | 465 | pxamci_disable_irq(pxa_host, SDIO_INT); | 
|  | 466 | } | 
|  | 467 |  | 
| David Brownell | ab7aefd | 2006-11-12 17:55:30 -0800 | [diff] [blame] | 468 | static const struct mmc_host_ops pxamci_ops = { | 
| Bridge Wu | 5d3ad4e | 2007-09-25 19:11:00 +0200 | [diff] [blame] | 469 | .request		= pxamci_request, | 
|  | 470 | .get_ro			= pxamci_get_ro, | 
|  | 471 | .set_ios		= pxamci_set_ios, | 
|  | 472 | .enable_sdio_irq	= pxamci_enable_sdio_irq, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 473 | }; | 
|  | 474 |  | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 475 | static void pxamci_dma_irq(int dma, void *devid) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 476 | { | 
| Nicolas Pitre | c783837 | 2007-10-09 17:07:58 -0400 | [diff] [blame] | 477 | struct pxamci_host *host = devid; | 
|  | 478 | int dcsr = DCSR(dma); | 
|  | 479 | DCSR(dma) = dcsr & ~DCSR_STOPIRQEN; | 
|  | 480 |  | 
|  | 481 | if (dcsr & DCSR_ENDINTR) { | 
|  | 482 | writel(BUF_PART_FULL, host->base + MMC_PRTBUF); | 
|  | 483 | } else { | 
|  | 484 | printk(KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n", | 
|  | 485 | mmc_hostname(host->mmc), dma, dcsr); | 
|  | 486 | host->data->error = -EIO; | 
|  | 487 | pxamci_data_done(host, 0); | 
|  | 488 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 489 | } | 
|  | 490 |  | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 491 | static irqreturn_t pxamci_detect_irq(int irq, void *devid) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 492 | { | 
| Richard Purdie | c26971c | 2005-09-08 22:48:16 +0100 | [diff] [blame] | 493 | struct pxamci_host *host = mmc_priv(devid); | 
|  | 494 |  | 
|  | 495 | mmc_detect_change(devid, host->pdata->detect_delay); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 496 | return IRQ_HANDLED; | 
|  | 497 | } | 
|  | 498 |  | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 499 | static int pxamci_probe(struct platform_device *pdev) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 500 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 501 | struct mmc_host *mmc; | 
|  | 502 | struct pxamci_host *host = NULL; | 
| Bridge Wu | 9a788c6 | 2007-12-14 10:40:25 +0100 | [diff] [blame] | 503 | struct resource *r, *dmarx, *dmatx; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 504 | int ret, irq; | 
|  | 505 |  | 
|  | 506 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 
|  | 507 | irq = platform_get_irq(pdev, 0); | 
| David Vrabel | 4894473 | 2006-01-19 17:56:29 +0000 | [diff] [blame] | 508 | if (!r || irq < 0) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 509 | return -ENXIO; | 
|  | 510 |  | 
|  | 511 | r = request_mem_region(r->start, SZ_4K, DRIVER_NAME); | 
|  | 512 | if (!r) | 
|  | 513 | return -EBUSY; | 
|  | 514 |  | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 515 | mmc = mmc_alloc_host(sizeof(struct pxamci_host), &pdev->dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 516 | if (!mmc) { | 
|  | 517 | ret = -ENOMEM; | 
|  | 518 | goto out; | 
|  | 519 | } | 
|  | 520 |  | 
|  | 521 | mmc->ops = &pxamci_ops; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 522 |  | 
|  | 523 | /* | 
|  | 524 | * We can do SG-DMA, but we don't because we never know how much | 
|  | 525 | * data we successfully wrote to the card. | 
|  | 526 | */ | 
|  | 527 | mmc->max_phys_segs = NR_SG; | 
|  | 528 |  | 
|  | 529 | /* | 
|  | 530 | * Our hardware DMA can handle a maximum of one page per SG entry. | 
|  | 531 | */ | 
|  | 532 | mmc->max_seg_size = PAGE_SIZE; | 
|  | 533 |  | 
| Pierre Ossman | fe4a3c7 | 2006-11-21 17:54:23 +0100 | [diff] [blame] | 534 | /* | 
| Nicolas Pitre | fe2dc44 | 2007-09-24 15:47:18 -0400 | [diff] [blame] | 535 | * Block length register is only 10 bits before PXA27x. | 
| Pierre Ossman | fe4a3c7 | 2006-11-21 17:54:23 +0100 | [diff] [blame] | 536 | */ | 
| Eric Miao | 0ffcbfd | 2008-09-11 10:27:30 +0800 | [diff] [blame] | 537 | mmc->max_blk_size = cpu_is_pxa25x() ? 1023 : 2048; | 
| Pierre Ossman | fe4a3c7 | 2006-11-21 17:54:23 +0100 | [diff] [blame] | 538 |  | 
| Pierre Ossman | 55db890 | 2006-11-21 17:55:45 +0100 | [diff] [blame] | 539 | /* | 
|  | 540 | * Block count register is 16 bits. | 
|  | 541 | */ | 
|  | 542 | mmc->max_blk_count = 65535; | 
|  | 543 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 544 | host = mmc_priv(mmc); | 
|  | 545 | host->mmc = mmc; | 
|  | 546 | host->dma = -1; | 
|  | 547 | host->pdata = pdev->dev.platform_data; | 
| Russell King | d8cb70d | 2007-10-26 17:56:40 +0100 | [diff] [blame] | 548 | host->clkrt = CLKRT_OFF; | 
| Russell King | ebebd9b | 2007-08-20 10:20:03 +0100 | [diff] [blame] | 549 |  | 
| Russell King | e0d8b13 | 2008-11-11 17:52:32 +0000 | [diff] [blame] | 550 | host->clk = clk_get(&pdev->dev, NULL); | 
| Russell King | ebebd9b | 2007-08-20 10:20:03 +0100 | [diff] [blame] | 551 | if (IS_ERR(host->clk)) { | 
|  | 552 | ret = PTR_ERR(host->clk); | 
|  | 553 | host->clk = NULL; | 
|  | 554 | goto out; | 
|  | 555 | } | 
|  | 556 |  | 
|  | 557 | host->clkrate = clk_get_rate(host->clk); | 
|  | 558 |  | 
|  | 559 | /* | 
|  | 560 | * Calculate minimum clock rate, rounding up. | 
|  | 561 | */ | 
|  | 562 | mmc->f_min = (host->clkrate + 63) / 64; | 
| Bridge Wu | 64eb036 | 2007-12-13 07:24:30 +0100 | [diff] [blame] | 563 | mmc->f_max = (cpu_is_pxa300() || cpu_is_pxa310()) ? 26000000 | 
|  | 564 | : host->clkrate; | 
| Russell King | ebebd9b | 2007-08-20 10:20:03 +0100 | [diff] [blame] | 565 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 566 | mmc->ocr_avail = host->pdata ? | 
|  | 567 | host->pdata->ocr_mask : | 
|  | 568 | MMC_VDD_32_33|MMC_VDD_33_34; | 
| Bridge Wu | df456f4 | 2007-09-25 19:09:19 +0200 | [diff] [blame] | 569 | mmc->caps = 0; | 
| Bridge Wu | 5d3ad4e | 2007-09-25 19:11:00 +0200 | [diff] [blame] | 570 | host->cmdat = 0; | 
| Eric Miao | 0ffcbfd | 2008-09-11 10:27:30 +0800 | [diff] [blame] | 571 | if (!cpu_is_pxa25x()) { | 
| Bridge Wu | 5d3ad4e | 2007-09-25 19:11:00 +0200 | [diff] [blame] | 572 | mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ; | 
|  | 573 | host->cmdat |= CMDAT_SDIO_INT_EN; | 
| Bridge Wu | 64eb036 | 2007-12-13 07:24:30 +0100 | [diff] [blame] | 574 | if (cpu_is_pxa300() || cpu_is_pxa310()) | 
|  | 575 | mmc->caps |= MMC_CAP_MMC_HIGHSPEED | | 
|  | 576 | MMC_CAP_SD_HIGHSPEED; | 
| Bridge Wu | 5d3ad4e | 2007-09-25 19:11:00 +0200 | [diff] [blame] | 577 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 578 |  | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 579 | host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 580 | if (!host->sg_cpu) { | 
|  | 581 | ret = -ENOMEM; | 
|  | 582 | goto out; | 
|  | 583 | } | 
|  | 584 |  | 
|  | 585 | spin_lock_init(&host->lock); | 
|  | 586 | host->res = r; | 
|  | 587 | host->irq = irq; | 
|  | 588 | host->imask = MMC_I_MASK_ALL; | 
|  | 589 |  | 
|  | 590 | host->base = ioremap(r->start, SZ_4K); | 
|  | 591 | if (!host->base) { | 
|  | 592 | ret = -ENOMEM; | 
|  | 593 | goto out; | 
|  | 594 | } | 
|  | 595 |  | 
|  | 596 | /* | 
|  | 597 | * Ensure that the host controller is shut down, and setup | 
|  | 598 | * with our defaults. | 
|  | 599 | */ | 
|  | 600 | pxamci_stop_clock(host); | 
|  | 601 | writel(0, host->base + MMC_SPI); | 
|  | 602 | writel(64, host->base + MMC_RESTO); | 
|  | 603 | writel(host->imask, host->base + MMC_I_MASK); | 
|  | 604 |  | 
|  | 605 | host->dma = pxa_request_dma(DRIVER_NAME, DMA_PRIO_LOW, | 
|  | 606 | pxamci_dma_irq, host); | 
|  | 607 | if (host->dma < 0) { | 
|  | 608 | ret = -EBUSY; | 
|  | 609 | goto out; | 
|  | 610 | } | 
|  | 611 |  | 
|  | 612 | ret = request_irq(host->irq, pxamci_irq, 0, DRIVER_NAME, host); | 
|  | 613 | if (ret) | 
|  | 614 | goto out; | 
|  | 615 |  | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 616 | platform_set_drvdata(pdev, mmc); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 617 |  | 
| Bridge Wu | 9a788c6 | 2007-12-14 10:40:25 +0100 | [diff] [blame] | 618 | dmarx = platform_get_resource(pdev, IORESOURCE_DMA, 0); | 
|  | 619 | if (!dmarx) { | 
|  | 620 | ret = -ENXIO; | 
|  | 621 | goto out; | 
|  | 622 | } | 
|  | 623 | host->dma_drcmrrx = dmarx->start; | 
|  | 624 |  | 
|  | 625 | dmatx = platform_get_resource(pdev, IORESOURCE_DMA, 1); | 
|  | 626 | if (!dmatx) { | 
|  | 627 | ret = -ENXIO; | 
|  | 628 | goto out; | 
|  | 629 | } | 
|  | 630 | host->dma_drcmrtx = dmatx->start; | 
|  | 631 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 632 | if (host->pdata && host->pdata->init) | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 633 | host->pdata->init(&pdev->dev, pxamci_detect_irq, mmc); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 634 |  | 
|  | 635 | mmc_add_host(mmc); | 
|  | 636 |  | 
|  | 637 | return 0; | 
|  | 638 |  | 
|  | 639 | out: | 
|  | 640 | if (host) { | 
|  | 641 | if (host->dma >= 0) | 
|  | 642 | pxa_free_dma(host->dma); | 
|  | 643 | if (host->base) | 
|  | 644 | iounmap(host->base); | 
|  | 645 | if (host->sg_cpu) | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 646 | dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); | 
| Russell King | ebebd9b | 2007-08-20 10:20:03 +0100 | [diff] [blame] | 647 | if (host->clk) | 
|  | 648 | clk_put(host->clk); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 649 | } | 
|  | 650 | if (mmc) | 
|  | 651 | mmc_free_host(mmc); | 
|  | 652 | release_resource(r); | 
|  | 653 | return ret; | 
|  | 654 | } | 
|  | 655 |  | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 656 | static int pxamci_remove(struct platform_device *pdev) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 657 | { | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 658 | struct mmc_host *mmc = platform_get_drvdata(pdev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 659 |  | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 660 | platform_set_drvdata(pdev, NULL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 661 |  | 
|  | 662 | if (mmc) { | 
|  | 663 | struct pxamci_host *host = mmc_priv(mmc); | 
|  | 664 |  | 
|  | 665 | if (host->pdata && host->pdata->exit) | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 666 | host->pdata->exit(&pdev->dev, mmc); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 667 |  | 
|  | 668 | mmc_remove_host(mmc); | 
|  | 669 |  | 
|  | 670 | pxamci_stop_clock(host); | 
|  | 671 | writel(TXFIFO_WR_REQ|RXFIFO_RD_REQ|CLK_IS_OFF|STOP_CMD| | 
|  | 672 | END_CMD_RES|PRG_DONE|DATA_TRAN_DONE, | 
|  | 673 | host->base + MMC_I_MASK); | 
|  | 674 |  | 
| Bridge Wu | 9a788c6 | 2007-12-14 10:40:25 +0100 | [diff] [blame] | 675 | DRCMR(host->dma_drcmrrx) = 0; | 
|  | 676 | DRCMR(host->dma_drcmrtx) = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 677 |  | 
|  | 678 | free_irq(host->irq, host); | 
|  | 679 | pxa_free_dma(host->dma); | 
|  | 680 | iounmap(host->base); | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 681 | dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 682 |  | 
| Russell King | ebebd9b | 2007-08-20 10:20:03 +0100 | [diff] [blame] | 683 | clk_put(host->clk); | 
|  | 684 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 685 | release_resource(host->res); | 
|  | 686 |  | 
|  | 687 | mmc_free_host(mmc); | 
|  | 688 | } | 
|  | 689 | return 0; | 
|  | 690 | } | 
|  | 691 |  | 
|  | 692 | #ifdef CONFIG_PM | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 693 | static int pxamci_suspend(struct platform_device *dev, pm_message_t state) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 694 | { | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 695 | struct mmc_host *mmc = platform_get_drvdata(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 696 | int ret = 0; | 
|  | 697 |  | 
| Russell King | 9480e30 | 2005-10-28 09:52:56 -0700 | [diff] [blame] | 698 | if (mmc) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 699 | ret = mmc_suspend_host(mmc, state); | 
|  | 700 |  | 
|  | 701 | return ret; | 
|  | 702 | } | 
|  | 703 |  | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 704 | static int pxamci_resume(struct platform_device *dev) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 705 | { | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 706 | struct mmc_host *mmc = platform_get_drvdata(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 707 | int ret = 0; | 
|  | 708 |  | 
| Russell King | 9480e30 | 2005-10-28 09:52:56 -0700 | [diff] [blame] | 709 | if (mmc) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 710 | ret = mmc_resume_host(mmc); | 
|  | 711 |  | 
|  | 712 | return ret; | 
|  | 713 | } | 
|  | 714 | #else | 
|  | 715 | #define pxamci_suspend	NULL | 
|  | 716 | #define pxamci_resume	NULL | 
|  | 717 | #endif | 
|  | 718 |  | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 719 | static struct platform_driver pxamci_driver = { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 720 | .probe		= pxamci_probe, | 
|  | 721 | .remove		= pxamci_remove, | 
|  | 722 | .suspend	= pxamci_suspend, | 
|  | 723 | .resume		= pxamci_resume, | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 724 | .driver		= { | 
|  | 725 | .name	= DRIVER_NAME, | 
| Kay Sievers | bc65c72 | 2008-04-15 14:34:28 -0700 | [diff] [blame] | 726 | .owner	= THIS_MODULE, | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 727 | }, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 728 | }; | 
|  | 729 |  | 
|  | 730 | static int __init pxamci_init(void) | 
|  | 731 | { | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 732 | return platform_driver_register(&pxamci_driver); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 733 | } | 
|  | 734 |  | 
|  | 735 | static void __exit pxamci_exit(void) | 
|  | 736 | { | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 737 | platform_driver_unregister(&pxamci_driver); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 738 | } | 
|  | 739 |  | 
|  | 740 | module_init(pxamci_init); | 
|  | 741 | module_exit(pxamci_exit); | 
|  | 742 |  | 
|  | 743 | MODULE_DESCRIPTION("PXA Multimedia Card Interface Driver"); | 
|  | 744 | MODULE_LICENSE("GPL"); | 
| Kay Sievers | bc65c72 | 2008-04-15 14:34:28 -0700 | [diff] [blame] | 745 | MODULE_ALIAS("platform:pxa2xx-mci"); |