Blame view

drivers/mmc/host/pxamci.c 20.7 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /*
70f10482c   Pierre Ossman   mmc: update heade...
2
   *  linux/drivers/mmc/host/pxa.c - PXA MMCI driver
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
   *
   *  Copyright (C) 2003 Russell King, All Rights Reserved.
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   *
   *  This hardware is really sick:
   *   - No way to clear interrupts.
   *   - Have to turn off the clock whenever we touch the device.
   *   - Doesn't tell you how many data blocks were transferred.
   *  Yuck!
   *
   *	1 and 3 byte data transfers not supported
   *	max block length up to 1023
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
20
21
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/ioport.h>
d052d1bef   Russell King   Create platform_d...
22
  #include <linux/platform_device.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
24
  #include <linux/delay.h>
  #include <linux/interrupt.h>
6464b7140   Daniel Mack   mmc: pxamci: swit...
25
  #include <linux/dmaengine.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
26
  #include <linux/dma-mapping.h>
6464b7140   Daniel Mack   mmc: pxamci: swit...
27
  #include <linux/dma/pxa-dma.h>
ebebd9b0a   Russell King   [ARM] pxa: update...
28
29
  #include <linux/clk.h>
  #include <linux/err.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
  #include <linux/mmc/host.h>
fd546ee6a   Robert Jarzmik   mmc: pxamci: fix ...
31
  #include <linux/mmc/slot-gpio.h>
05678a96d   Russell King   [ARM] pxa: avoid ...
32
  #include <linux/io.h>
8385f9cb7   Daniel Ribeiro   pxamci: add regul...
33
  #include <linux/regulator/consumer.h>
b405db6c0   Robert Jarzmik   [ARM] pxamci: add...
34
  #include <linux/gpio.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
35
  #include <linux/gfp.h>
e6027b467   Daniel Mack   mmc: pxa-mci: add...
36
37
38
  #include <linux/of.h>
  #include <linux/of_gpio.h>
  #include <linux/of_device.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
40
  #include <asm/sizes.h>
05678a96d   Russell King   [ARM] pxa: avoid ...
41
  #include <mach/hardware.h>
293b2da1b   Arnd Bergmann   ARM: pxa: move pl...
42
  #include <linux/platform_data/mmc-pxamci.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43
44
  
  #include "pxamci.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
45
46
47
  #define DRIVER_NAME	"pxa2xx-mci"
  
  #define NR_SG	1
d8cb70d10   Russell King   [ARM] Fix pxamci ...
48
  #define CLKRT_OFF	(~0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
49

fa3f99384   Haojian Zhuang   pxamci: introduce...
50
51
  #define mmc_has_26MHz()		(cpu_is_pxa300() || cpu_is_pxa310() \
  				|| cpu_is_pxa935())
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
52
53
54
55
56
  struct pxamci_host {
  	struct mmc_host		*mmc;
  	spinlock_t		lock;
  	struct resource		*res;
  	void __iomem		*base;
ebebd9b0a   Russell King   [ARM] pxa: update...
57
58
  	struct clk		*clk;
  	unsigned long		clkrate;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
59
  	int			irq;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60
61
62
63
64
65
66
67
68
  	unsigned int		clkrt;
  	unsigned int		cmdat;
  	unsigned int		imask;
  	unsigned int		power_mode;
  	struct pxamci_platform_data *pdata;
  
  	struct mmc_request	*mrq;
  	struct mmc_command	*cmd;
  	struct mmc_data		*data;
6464b7140   Daniel Mack   mmc: pxamci: swit...
69
70
71
  	struct dma_chan		*dma_chan_rx;
  	struct dma_chan		*dma_chan_tx;
  	dma_cookie_t		dma_cookie;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72
  	dma_addr_t		sg_dma;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
74
75
  	unsigned int		dma_len;
  
  	unsigned int		dma_dir;
9a788c6b7   Bridge Wu   [ARM] 4711/1: pxa...
76
77
  	unsigned int		dma_drcmrrx;
  	unsigned int		dma_drcmrtx;
8385f9cb7   Daniel Ribeiro   pxamci: add regul...
78
79
  
  	struct regulator	*vcc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
  };
8385f9cb7   Daniel Ribeiro   pxamci: add regul...
81
82
83
  static inline void pxamci_init_ocr(struct pxamci_host *host)
  {
  #ifdef CONFIG_REGULATOR
07e7716c7   Robert Jarzmik   mmc: pxamci: fix ...
84
  	host->vcc = devm_regulator_get_optional(mmc_dev(host->mmc), "vmmc");
8385f9cb7   Daniel Ribeiro   pxamci: add regul...
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
  
  	if (IS_ERR(host->vcc))
  		host->vcc = NULL;
  	else {
  		host->mmc->ocr_avail = mmc_regulator_get_ocrmask(host->vcc);
  		if (host->pdata && host->pdata->ocr_mask)
  			dev_warn(mmc_dev(host->mmc),
  				"ocr_mask/setpower will not be used
  ");
  	}
  #endif
  	if (host->vcc == NULL) {
  		/* fall-back to platform data */
  		host->mmc->ocr_avail = host->pdata ?
  			host->pdata->ocr_mask :
  			MMC_VDD_32_33 | MMC_VDD_33_34;
  	}
  }
99fc51310   Linus Walleij   mmc: Move regulat...
103
104
105
  static inline int pxamci_set_power(struct pxamci_host *host,
  				    unsigned char power_mode,
  				    unsigned int vdd)
8385f9cb7   Daniel Ribeiro   pxamci: add regul...
106
  {
b405db6c0   Robert Jarzmik   [ARM] pxamci: add...
107
  	int on;
99fc51310   Linus Walleij   mmc: Move regulat...
108
109
110
111
112
113
114
115
116
117
118
119
120
  	if (host->vcc) {
  		int ret;
  
  		if (power_mode == MMC_POWER_UP) {
  			ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd);
  			if (ret)
  				return ret;
  		} else if (power_mode == MMC_POWER_OFF) {
  			ret = mmc_regulator_set_ocr(host->mmc, host->vcc, 0);
  			if (ret)
  				return ret;
  		}
  	}
b405db6c0   Robert Jarzmik   [ARM] pxamci: add...
121
122
123
124
125
126
  	if (!host->vcc && host->pdata &&
  	    gpio_is_valid(host->pdata->gpio_power)) {
  		on = ((1 << vdd) & host->pdata->ocr_mask);
  		gpio_set_value(host->pdata->gpio_power,
  			       !!on ^ host->pdata->gpio_power_invert);
  	}
8385f9cb7   Daniel Ribeiro   pxamci: add regul...
127
  	if (!host->vcc && host->pdata && host->pdata->setpower)
a829abf8d   Arnd Bergmann   ARM: pxa: propaga...
128
  		return host->pdata->setpower(mmc_dev(host->mmc), vdd);
99fc51310   Linus Walleij   mmc: Move regulat...
129
130
  
  	return 0;
8385f9cb7   Daniel Ribeiro   pxamci: add regul...
131
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
  static void pxamci_stop_clock(struct pxamci_host *host)
  {
  	if (readl(host->base + MMC_STAT) & STAT_CLK_EN) {
  		unsigned long timeout = 10000;
  		unsigned int v;
  
  		writel(STOP_CLOCK, host->base + MMC_STRPCL);
  
  		do {
  			v = readl(host->base + MMC_STAT);
  			if (!(v & STAT_CLK_EN))
  				break;
  			udelay(1);
  		} while (timeout--);
  
  		if (v & STAT_CLK_EN)
  			dev_err(mmc_dev(host->mmc), "unable to stop clock
  ");
  	}
  }
  
  static void pxamci_enable_irq(struct pxamci_host *host, unsigned int mask)
  {
  	unsigned long flags;
  
  	spin_lock_irqsave(&host->lock, flags);
  	host->imask &= ~mask;
  	writel(host->imask, host->base + MMC_I_MASK);
  	spin_unlock_irqrestore(&host->lock, flags);
  }
  
  static void pxamci_disable_irq(struct pxamci_host *host, unsigned int mask)
  {
  	unsigned long flags;
  
  	spin_lock_irqsave(&host->lock, flags);
  	host->imask |= mask;
  	writel(host->imask, host->base + MMC_I_MASK);
  	spin_unlock_irqrestore(&host->lock, flags);
  }
6464b7140   Daniel Mack   mmc: pxamci: swit...
172
  static void pxamci_dma_irq(void *param);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
173
174
  static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
  {
6464b7140   Daniel Mack   mmc: pxamci: swit...
175
176
177
178
  	struct dma_async_tx_descriptor *tx;
  	enum dma_data_direction direction;
  	struct dma_slave_config	config;
  	struct dma_chan *chan;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
179
  	unsigned int nob = data->blocks;
3d63abe56   Russell King   [MMC] pxamci: fix...
180
  	unsigned long long clks;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181
  	unsigned int timeout;
6464b7140   Daniel Mack   mmc: pxamci: swit...
182
  	int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183
184
  
  	host->data = data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
  	writel(nob, host->base + MMC_NOB);
2c171bf13   Pavel Pisa   [ARM] 3531/1: i.M...
186
  	writel(data->blksz, host->base + MMC_BLKLEN);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
187

ebebd9b0a   Russell King   [ARM] pxa: update...
188
  	clks = (unsigned long long)data->timeout_ns * host->clkrate;
3d63abe56   Russell King   [MMC] pxamci: fix...
189
190
  	do_div(clks, 1000000000UL);
  	timeout = (unsigned int)clks + (data->timeout_clks << host->clkrt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191
  	writel((timeout + 255) / 256, host->base + MMC_RDTO);
6464b7140   Daniel Mack   mmc: pxamci: swit...
192
193
194
195
196
197
198
  	memset(&config, 0, sizeof(config));
  	config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
  	config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
  	config.src_addr = host->res->start + MMC_RXFIFO;
  	config.dst_addr = host->res->start + MMC_TXFIFO;
  	config.src_maxburst = 32;
  	config.dst_maxburst = 32;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
199
200
  	if (data->flags & MMC_DATA_READ) {
  		host->dma_dir = DMA_FROM_DEVICE;
6464b7140   Daniel Mack   mmc: pxamci: swit...
201
202
  		direction = DMA_DEV_TO_MEM;
  		chan = host->dma_chan_rx;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203
204
  	} else {
  		host->dma_dir = DMA_TO_DEVICE;
6464b7140   Daniel Mack   mmc: pxamci: swit...
205
206
  		direction = DMA_MEM_TO_DEV;
  		chan = host->dma_chan_tx;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
207
  	}
6464b7140   Daniel Mack   mmc: pxamci: swit...
208
209
210
211
212
213
214
215
  	config.direction = direction;
  
  	ret = dmaengine_slave_config(chan, &config);
  	if (ret < 0) {
  		dev_err(mmc_dev(host->mmc), "dma slave config failed
  ");
  		return;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
216

6464b7140   Daniel Mack   mmc: pxamci: swit...
217
  	host->dma_len = dma_map_sg(chan->device->dev, data->sg, data->sg_len,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
218
  				   host->dma_dir);
6464b7140   Daniel Mack   mmc: pxamci: swit...
219
220
221
222
223
224
  	tx = dmaengine_prep_slave_sg(chan, data->sg, host->dma_len, direction,
  				     DMA_PREP_INTERRUPT);
  	if (!tx) {
  		dev_err(mmc_dev(host->mmc), "prep_slave_sg() failed
  ");
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
225
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
226

6464b7140   Daniel Mack   mmc: pxamci: swit...
227
228
229
230
231
232
  	if (!(data->flags & MMC_DATA_READ)) {
  		tx->callback = pxamci_dma_irq;
  		tx->callback_param = host;
  	}
  
  	host->dma_cookie = dmaengine_submit(tx);
b6018958a   Cliff Brake   pxamci: enable DM...
233
234
235
236
237
238
239
240
  
  	/*
  	 * workaround for erratum #91:
  	 * only start DMA now if we are doing a read,
  	 * otherwise we wait until CMD/RESP has finished
  	 * before starting DMA.
  	 */
  	if (!cpu_is_pxa27x() || data->flags & MMC_DATA_READ)
6464b7140   Daniel Mack   mmc: pxamci: swit...
241
  		dma_async_issue_pending(chan);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
242
243
244
245
246
247
248
249
250
  }
  
  static void pxamci_start_cmd(struct pxamci_host *host, struct mmc_command *cmd, unsigned int cmdat)
  {
  	WARN_ON(host->cmd != NULL);
  	host->cmd = cmd;
  
  	if (cmd->flags & MMC_RSP_BUSY)
  		cmdat |= CMDAT_BUSY;
e92251762   Russell King   [MMC] Add MMC com...
251
252
  #define RSP_TYPE(x)	((x) & ~(MMC_RSP_BUSY|MMC_RSP_OPCODE))
  	switch (RSP_TYPE(mmc_resp_type(cmd))) {
6f949909e   Philip Langdale   mmc: Correct defi...
253
  	case RSP_TYPE(MMC_RSP_R1): /* r1, r1b, r6, r7 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
254
255
  		cmdat |= CMDAT_RESP_SHORT;
  		break;
e92251762   Russell King   [MMC] Add MMC com...
256
  	case RSP_TYPE(MMC_RSP_R3):
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
257
258
  		cmdat |= CMDAT_RESP_R3;
  		break;
e92251762   Russell King   [MMC] Add MMC com...
259
  	case RSP_TYPE(MMC_RSP_R2):
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
  		cmdat |= CMDAT_RESP_R2;
  		break;
  	default:
  		break;
  	}
  
  	writel(cmd->opcode, host->base + MMC_CMD);
  	writel(cmd->arg >> 16, host->base + MMC_ARGH);
  	writel(cmd->arg & 0xffff, host->base + MMC_ARGL);
  	writel(cmdat, host->base + MMC_CMDAT);
  	writel(host->clkrt, host->base + MMC_CLKRT);
  
  	writel(START_CLOCK, host->base + MMC_STRPCL);
  
  	pxamci_enable_irq(host, END_CMD_RES);
  }
  
  static void pxamci_finish_request(struct pxamci_host *host, struct mmc_request *mrq)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
  	host->mrq = NULL;
  	host->cmd = NULL;
  	host->data = NULL;
  	mmc_request_done(host->mmc, mrq);
  }
  
  static int pxamci_cmd_done(struct pxamci_host *host, unsigned int stat)
  {
  	struct mmc_command *cmd = host->cmd;
  	int i;
  	u32 v;
  
  	if (!cmd)
  		return 0;
  
  	host->cmd = NULL;
  
  	/*
  	 * Did I mention this is Sick.  We always need to
  	 * discard the upper 8 bits of the first 16-bit word.
  	 */
  	v = readl(host->base + MMC_RES) & 0xffff;
  	for (i = 0; i < 4; i++) {
  		u32 w1 = readl(host->base + MMC_RES) & 0xffff;
  		u32 w2 = readl(host->base + MMC_RES) & 0xffff;
  		cmd->resp[i] = v << 24 | w1 << 8 | w2 >> 8;
  		v = w2;
  	}
  
  	if (stat & STAT_TIME_OUT_RESPONSE) {
17b0429dd   Pierre Ossman   mmc: remove custo...
309
  		cmd->error = -ETIMEDOUT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
310
  	} else if (stat & STAT_RES_CRC_ERR && cmd->flags & MMC_RSP_CRC) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
311
312
313
  		/*
  		 * workaround for erratum #42:
  		 * Intel PXA27x Family Processor Specification Update Rev 001
90e07d9f5   Nicolas Pitre   pxamci: fix PXA27...
314
315
  		 * A bogus CRC error can appear if the msb of a 136 bit
  		 * response is a one.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
316
  		 */
e10a854c4   Cliff Brake   pxamci: replace #...
317
318
  		if (cpu_is_pxa27x() &&
  		    (cmd->flags & MMC_RSP_136 && cmd->resp[0] & 0x80000000))
90e07d9f5   Nicolas Pitre   pxamci: fix PXA27...
319
320
  			pr_debug("ignoring CRC from command %d - *risky*
  ", cmd->opcode);
e10a854c4   Cliff Brake   pxamci: replace #...
321
322
  		else
  			cmd->error = -EILSEQ;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
323
324
325
  	}
  
  	pxamci_disable_irq(host, END_CMD_RES);
17b0429dd   Pierre Ossman   mmc: remove custo...
326
  	if (host->data && !cmd->error) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
327
  		pxamci_enable_irq(host, DATA_TRAN_DONE);
b6018958a   Cliff Brake   pxamci: enable DM...
328
329
330
331
332
  		/*
  		 * workaround for erratum #91, if doing write
  		 * enable DMA late
  		 */
  		if (cpu_is_pxa27x() && host->data->flags & MMC_DATA_WRITE)
6464b7140   Daniel Mack   mmc: pxamci: swit...
333
  			dma_async_issue_pending(host->dma_chan_tx);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
334
335
336
337
338
339
340
341
342
343
  	} else {
  		pxamci_finish_request(host, host->mrq);
  	}
  
  	return 1;
  }
  
  static int pxamci_data_done(struct pxamci_host *host, unsigned int stat)
  {
  	struct mmc_data *data = host->data;
6464b7140   Daniel Mack   mmc: pxamci: swit...
344
  	struct dma_chan *chan;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
345
346
347
  
  	if (!data)
  		return 0;
6464b7140   Daniel Mack   mmc: pxamci: swit...
348
349
350
351
352
353
  	if (data->flags & MMC_DATA_READ)
  		chan = host->dma_chan_rx;
  	else
  		chan = host->dma_chan_tx;
  	dma_unmap_sg(chan->device->dev,
  		     data->sg, data->sg_len, host->dma_dir);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
354
355
  
  	if (stat & STAT_READ_TIME_OUT)
17b0429dd   Pierre Ossman   mmc: remove custo...
356
  		data->error = -ETIMEDOUT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
357
  	else if (stat & (STAT_CRC_READ_ERROR|STAT_CRC_WRITE_ERROR))
17b0429dd   Pierre Ossman   mmc: remove custo...
358
  		data->error = -EILSEQ;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
359
360
361
362
363
364
365
  
  	/*
  	 * There appears to be a hardware design bug here.  There seems to
  	 * be no way to find out how much data was transferred to the card.
  	 * This means that if there was an error on any block, we mark all
  	 * data blocks as being in error.
  	 */
17b0429dd   Pierre Ossman   mmc: remove custo...
366
  	if (!data->error)
2c171bf13   Pavel Pisa   [ARM] 3531/1: i.M...
367
  		data->bytes_xfered = data->blocks * data->blksz;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
368
369
370
371
372
373
  	else
  		data->bytes_xfered = 0;
  
  	pxamci_disable_irq(host, DATA_TRAN_DONE);
  
  	host->data = NULL;
58741e8b3   Russell King   [MMC] PXA and i.M...
374
  	if (host->mrq->stop) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
375
  		pxamci_stop_clock(host);
df456f479   Bridge Wu   mmc: pxamci: set ...
376
  		pxamci_start_cmd(host, host->mrq->stop, host->cmdat);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
377
378
379
380
381
382
  	} else {
  		pxamci_finish_request(host, host->mrq);
  	}
  
  	return 1;
  }
7d12e780e   David Howells   IRQ: Maintain reg...
383
  static irqreturn_t pxamci_irq(int irq, void *devid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
384
385
386
387
  {
  	struct pxamci_host *host = devid;
  	unsigned int ireg;
  	int handled = 0;
81ab570f6   Bridge Wu   mmc: pxamci: bett...
388
  	ireg = readl(host->base + MMC_I_REG) & ~readl(host->base + MMC_I_MASK);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
389

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
390
391
  	if (ireg) {
  		unsigned stat = readl(host->base + MMC_STAT);
d78e9079a   Russell King   [MMC] PXA: reduce...
392
393
  		pr_debug("PXAMCI: irq %08x stat %08x
  ", ireg, stat);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
394
395
396
397
398
  
  		if (ireg & END_CMD_RES)
  			handled |= pxamci_cmd_done(host, stat);
  		if (ireg & DATA_TRAN_DONE)
  			handled |= pxamci_data_done(host, stat);
5d3ad4e8a   Bridge Wu   mmc: pxamci: add ...
399
400
401
402
  		if (ireg & SDIO_INT) {
  			mmc_signal_sdio_irq(host->mmc);
  			handled = 1;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
  	}
  
  	return IRQ_RETVAL(handled);
  }
  
  static void pxamci_request(struct mmc_host *mmc, struct mmc_request *mrq)
  {
  	struct pxamci_host *host = mmc_priv(mmc);
  	unsigned int cmdat;
  
  	WARN_ON(host->mrq != NULL);
  
  	host->mrq = mrq;
  
  	pxamci_stop_clock(host);
  
  	cmdat = host->cmdat;
  	host->cmdat &= ~CMDAT_INIT;
  
  	if (mrq->data) {
  		pxamci_setup_data(host, mrq->data);
  
  		cmdat &= ~CMDAT_BUSY;
  		cmdat |= CMDAT_DATAEN | CMDAT_DMAEN;
  		if (mrq->data->flags & MMC_DATA_WRITE)
  			cmdat |= CMDAT_WRITE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
429
430
431
432
  	}
  
  	pxamci_start_cmd(host, mrq->cmd, cmdat);
  }
e619524fe   Richard Purdie   [PATCH] Add write...
433
434
435
  static int pxamci_get_ro(struct mmc_host *mmc)
  {
  	struct pxamci_host *host = mmc_priv(mmc);
fd546ee6a   Robert Jarzmik   mmc: pxamci: fix ...
436
437
  	if (host->pdata && gpio_is_valid(host->pdata->gpio_card_ro))
  		return mmc_gpio_get_ro(mmc);
e619524fe   Richard Purdie   [PATCH] Add write...
438
  	if (host->pdata && host->pdata->get_ro)
08f80bb51   Anton Vorontsov   mmc: change .get_...
439
440
441
442
443
444
  		return !!host->pdata->get_ro(mmc_dev(mmc));
  	/*
  	 * Board doesn't support read only detection; let the mmc core
  	 * decide what to do.
  	 */
  	return -ENOSYS;
e619524fe   Richard Purdie   [PATCH] Add write...
445
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
446
447
448
  static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
  {
  	struct pxamci_host *host = mmc_priv(mmc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
449
  	if (ios->clock) {
ebebd9b0a   Russell King   [ARM] pxa: update...
450
451
  		unsigned long rate = host->clkrate;
  		unsigned int clk = rate / ios->clock;
d8cb70d10   Russell King   [ARM] Fix pxamci ...
452
  		if (host->clkrt == CLKRT_OFF)
e73708190   Robert Jarzmik   mmc: pxamci: prep...
453
  			clk_prepare_enable(host->clk);
d8cb70d10   Russell King   [ARM] Fix pxamci ...
454

64eb036af   Bridge Wu   [ARM] 4709/1: pxa...
455
  		if (ios->clock == 26000000) {
fa3f99384   Haojian Zhuang   pxamci: introduce...
456
  			/* to support 26MHz */
64eb036af   Bridge Wu   [ARM] 4709/1: pxa...
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
  			host->clkrt = 7;
  		} else {
  			/* to handle (19.5MHz, 26MHz) */
  			if (!clk)
  				clk = 1;
  
  			/*
  			 * clk might result in a lower divisor than we
  			 * desire.  check for that condition and adjust
  			 * as appropriate.
  			 */
  			if (rate / clk > ios->clock)
  				clk <<= 1;
  			host->clkrt = fls(clk) - 1;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
472
473
474
475
476
477
  
  		/*
  		 * we write clkrt on the next command
  		 */
  	} else {
  		pxamci_stop_clock(host);
d8cb70d10   Russell King   [ARM] Fix pxamci ...
478
479
  		if (host->clkrt != CLKRT_OFF) {
  			host->clkrt = CLKRT_OFF;
e73708190   Robert Jarzmik   mmc: pxamci: prep...
480
  			clk_disable_unprepare(host->clk);
d8cb70d10   Russell King   [ARM] Fix pxamci ...
481
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
482
483
484
  	}
  
  	if (host->power_mode != ios->power_mode) {
99fc51310   Linus Walleij   mmc: Move regulat...
485
  		int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
486
  		host->power_mode = ios->power_mode;
99fc51310   Linus Walleij   mmc: Move regulat...
487
488
489
490
491
492
493
494
495
496
497
498
  		ret = pxamci_set_power(host, ios->power_mode, ios->vdd);
  		if (ret) {
  			dev_err(mmc_dev(mmc), "unable to set power
  ");
  			/*
  			 * The .set_ios() function in the mmc_host_ops
  			 * struct return void, and failing to set the
  			 * power should be rare so we print an error and
  			 * return here.
  			 */
  			return;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
499
500
501
502
  
  		if (ios->power_mode == MMC_POWER_ON)
  			host->cmdat |= CMDAT_INIT;
  	}
df456f479   Bridge Wu   mmc: pxamci: set ...
503
504
505
506
  	if (ios->bus_width == MMC_BUS_WIDTH_4)
  		host->cmdat |= CMDAT_SD_4DAT;
  	else
  		host->cmdat &= ~CMDAT_SD_4DAT;
99fc51310   Linus Walleij   mmc: Move regulat...
507
508
509
  	dev_dbg(mmc_dev(mmc), "PXAMCI: clkrt = %x cmdat = %x
  ",
  		host->clkrt, host->cmdat);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
510
  }
5d3ad4e8a   Bridge Wu   mmc: pxamci: add ...
511
512
513
514
515
516
517
518
519
  static void pxamci_enable_sdio_irq(struct mmc_host *host, int enable)
  {
  	struct pxamci_host *pxa_host = mmc_priv(host);
  
  	if (enable)
  		pxamci_enable_irq(pxa_host, SDIO_INT);
  	else
  		pxamci_disable_irq(pxa_host, SDIO_INT);
  }
ab7aefd0b   David Brownell   mmc: constify mmc...
520
  static const struct mmc_host_ops pxamci_ops = {
5d3ad4e8a   Bridge Wu   mmc: pxamci: add ...
521
  	.request		= pxamci_request,
fd546ee6a   Robert Jarzmik   mmc: pxamci: fix ...
522
  	.get_cd			= mmc_gpio_get_cd,
5d3ad4e8a   Bridge Wu   mmc: pxamci: add ...
523
524
525
  	.get_ro			= pxamci_get_ro,
  	.set_ios		= pxamci_set_ios,
  	.enable_sdio_irq	= pxamci_enable_sdio_irq,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
526
  };
6464b7140   Daniel Mack   mmc: pxamci: swit...
527
  static void pxamci_dma_irq(void *param)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
528
  {
6464b7140   Daniel Mack   mmc: pxamci: swit...
529
530
531
532
533
534
535
536
537
538
  	struct pxamci_host *host = param;
  	struct dma_tx_state state;
  	enum dma_status status;
  	struct dma_chan *chan;
  	unsigned long flags;
  
  	spin_lock_irqsave(&host->lock, flags);
  
  	if (!host->data)
  		goto out_unlock;
c783837bc   Nicolas Pitre   pxamci: support a...
539

6464b7140   Daniel Mack   mmc: pxamci: swit...
540
541
542
543
544
545
546
547
  	if (host->data->flags & MMC_DATA_READ)
  		chan = host->dma_chan_rx;
  	else
  		chan = host->dma_chan_tx;
  
  	status = dmaengine_tx_status(chan, host->dma_cookie, &state);
  
  	if (likely(status == DMA_COMPLETE)) {
c783837bc   Nicolas Pitre   pxamci: support a...
548
549
  		writel(BUF_PART_FULL, host->base + MMC_PRTBUF);
  	} else {
6464b7140   Daniel Mack   mmc: pxamci: swit...
550
551
552
  		pr_err("%s: DMA error on %s channel
  ", mmc_hostname(host->mmc),
  			host->data->flags & MMC_DATA_READ ? "rx" : "tx");
c783837bc   Nicolas Pitre   pxamci: support a...
553
554
555
  		host->data->error = -EIO;
  		pxamci_data_done(host, 0);
  	}
6464b7140   Daniel Mack   mmc: pxamci: swit...
556
557
558
  
  out_unlock:
  	spin_unlock_irqrestore(&host->lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
559
  }
7d12e780e   David Howells   IRQ: Maintain reg...
560
  static irqreturn_t pxamci_detect_irq(int irq, void *devid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
561
  {
c26971cbb   Richard Purdie   [MMC] Add mmc_det...
562
  	struct pxamci_host *host = mmc_priv(devid);
f97cab28b   Eric Miao   [ARM] pxa: make i...
563
  	mmc_detect_change(devid, msecs_to_jiffies(host->pdata->detect_delay_ms));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
564
565
  	return IRQ_HANDLED;
  }
e6027b467   Daniel Mack   mmc: pxa-mci: add...
566
567
568
569
570
571
572
  #ifdef CONFIG_OF
  static const struct of_device_id pxa_mmc_dt_ids[] = {
          { .compatible = "marvell,pxa-mmc" },
          { }
  };
  
  MODULE_DEVICE_TABLE(of, pxa_mmc_dt_ids);
c3be1efd4   Bill Pemberton   mmc: remove use o...
573
  static int pxamci_of_init(struct platform_device *pdev)
e6027b467   Daniel Mack   mmc: pxa-mci: add...
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
  {
          struct device_node *np = pdev->dev.of_node;
          struct pxamci_platform_data *pdata;
          u32 tmp;
  
          if (!np)
                  return 0;
  
          pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
          if (!pdata)
                  return -ENOMEM;
  
  	pdata->gpio_card_detect =
  		of_get_named_gpio(np, "cd-gpios", 0);
  	pdata->gpio_card_ro =
  		of_get_named_gpio(np, "wp-gpios", 0);
  
  	/* pxa-mmc specific */
  	pdata->gpio_power =
  		of_get_named_gpio(np, "pxa-mmc,gpio-power", 0);
  
  	if (of_property_read_u32(np, "pxa-mmc,detect-delay-ms", &tmp) == 0)
  		pdata->detect_delay_ms = tmp;
  
          pdev->dev.platform_data = pdata;
  
          return 0;
  }
  #else
c3be1efd4   Bill Pemberton   mmc: remove use o...
603
  static int pxamci_of_init(struct platform_device *pdev)
e6027b467   Daniel Mack   mmc: pxa-mci: add...
604
605
606
607
  {
          return 0;
  }
  #endif
3ae5eaec1   Russell King   [DRIVER MODEL] Co...
608
  static int pxamci_probe(struct platform_device *pdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
609
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
610
611
  	struct mmc_host *mmc;
  	struct pxamci_host *host = NULL;
9a788c6b7   Bridge Wu   [ARM] 4711/1: pxa...
612
  	struct resource *r, *dmarx, *dmatx;
6464b7140   Daniel Mack   mmc: pxamci: swit...
613
  	struct pxad_param param_rx, param_tx;
b405db6c0   Robert Jarzmik   [ARM] pxamci: add...
614
  	int ret, irq, gpio_cd = -1, gpio_ro = -1, gpio_power = -1;
6464b7140   Daniel Mack   mmc: pxamci: swit...
615
  	dma_cap_mask_t mask;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
616

e6027b467   Daniel Mack   mmc: pxa-mci: add...
617
618
619
  	ret = pxamci_of_init(pdev);
  	if (ret)
  		return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
620
621
  	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  	irq = platform_get_irq(pdev, 0);
07e7716c7   Robert Jarzmik   mmc: pxamci: fix ...
622
623
  	if (irq < 0)
  		return irq;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
624

3ae5eaec1   Russell King   [DRIVER MODEL] Co...
625
  	mmc = mmc_alloc_host(sizeof(struct pxamci_host), &pdev->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
626
627
628
629
630
631
  	if (!mmc) {
  		ret = -ENOMEM;
  		goto out;
  	}
  
  	mmc->ops = &pxamci_ops;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
632
633
634
635
636
  
  	/*
  	 * We can do SG-DMA, but we don't because we never know how much
  	 * data we successfully wrote to the card.
  	 */
a36274e01   Martin K. Petersen   mmc: Remove disti...
637
  	mmc->max_segs = NR_SG;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
638
639
640
641
642
  
  	/*
  	 * Our hardware DMA can handle a maximum of one page per SG entry.
  	 */
  	mmc->max_seg_size = PAGE_SIZE;
fe4a3c7a2   Pierre Ossman   mmc: Allow host d...
643
  	/*
fe2dc44ea   Nicolas Pitre   mmc: pxamci: set ...
644
  	 * Block length register is only 10 bits before PXA27x.
fe4a3c7a2   Pierre Ossman   mmc: Allow host d...
645
  	 */
0ffcbfd54   Eric Miao   [ARM] pxa: make c...
646
  	mmc->max_blk_size = cpu_is_pxa25x() ? 1023 : 2048;
fe4a3c7a2   Pierre Ossman   mmc: Allow host d...
647

55db890a8   Pierre Ossman   mmc: Allow host d...
648
649
650
651
  	/*
  	 * Block count register is 16 bits.
  	 */
  	mmc->max_blk_count = 65535;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
652
653
  	host = mmc_priv(mmc);
  	host->mmc = mmc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
654
  	host->pdata = pdev->dev.platform_data;
d8cb70d10   Russell King   [ARM] Fix pxamci ...
655
  	host->clkrt = CLKRT_OFF;
ebebd9b0a   Russell King   [ARM] pxa: update...
656

07e7716c7   Robert Jarzmik   mmc: pxamci: fix ...
657
  	host->clk = devm_clk_get(&pdev->dev, NULL);
ebebd9b0a   Russell King   [ARM] pxa: update...
658
659
660
661
662
663
664
665
666
667
668
669
  	if (IS_ERR(host->clk)) {
  		ret = PTR_ERR(host->clk);
  		host->clk = NULL;
  		goto out;
  	}
  
  	host->clkrate = clk_get_rate(host->clk);
  
  	/*
  	 * Calculate minimum clock rate, rounding up.
  	 */
  	mmc->f_min = (host->clkrate + 63) / 64;
fa3f99384   Haojian Zhuang   pxamci: introduce...
670
  	mmc->f_max = (mmc_has_26MHz()) ? 26000000 : host->clkrate;
ebebd9b0a   Russell King   [ARM] pxa: update...
671

8385f9cb7   Daniel Ribeiro   pxamci: add regul...
672
  	pxamci_init_ocr(host);
df456f479   Bridge Wu   mmc: pxamci: set ...
673
  	mmc->caps = 0;
5d3ad4e8a   Bridge Wu   mmc: pxamci: add ...
674
  	host->cmdat = 0;
0ffcbfd54   Eric Miao   [ARM] pxa: make c...
675
  	if (!cpu_is_pxa25x()) {
5d3ad4e8a   Bridge Wu   mmc: pxamci: add ...
676
677
  		mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
  		host->cmdat |= CMDAT_SDIO_INT_EN;
fa3f99384   Haojian Zhuang   pxamci: introduce...
678
  		if (mmc_has_26MHz())
64eb036af   Bridge Wu   [ARM] 4709/1: pxa...
679
680
  			mmc->caps |= MMC_CAP_MMC_HIGHSPEED |
  				     MMC_CAP_SD_HIGHSPEED;
5d3ad4e8a   Bridge Wu   mmc: pxamci: add ...
681
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
682

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
683
684
685
686
  	spin_lock_init(&host->lock);
  	host->res = r;
  	host->irq = irq;
  	host->imask = MMC_I_MASK_ALL;
07e7716c7   Robert Jarzmik   mmc: pxamci: fix ...
687
688
689
  	host->base = devm_ioremap_resource(&pdev->dev, r);
  	if (IS_ERR(host->base)) {
  		ret = PTR_ERR(host->base);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
690
691
692
693
694
695
696
697
698
699
700
  		goto out;
  	}
  
  	/*
  	 * Ensure that the host controller is shut down, and setup
  	 * with our defaults.
  	 */
  	pxamci_stop_clock(host);
  	writel(0, host->base + MMC_SPI);
  	writel(64, host->base + MMC_RESTO);
  	writel(host->imask, host->base + MMC_I_MASK);
07e7716c7   Robert Jarzmik   mmc: pxamci: fix ...
701
702
  	ret = devm_request_irq(&pdev->dev, host->irq, pxamci_irq, 0,
  			       DRIVER_NAME, host);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
703
704
  	if (ret)
  		goto out;
3ae5eaec1   Russell King   [DRIVER MODEL] Co...
705
  	platform_set_drvdata(pdev, mmc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
706

6464b7140   Daniel Mack   mmc: pxamci: swit...
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
  	if (!pdev->dev.of_node) {
  		dmarx = platform_get_resource(pdev, IORESOURCE_DMA, 0);
  		dmatx = platform_get_resource(pdev, IORESOURCE_DMA, 1);
  		if (!dmarx || !dmatx) {
  			ret = -ENXIO;
  			goto out;
  		}
  		param_rx.prio = PXAD_PRIO_LOWEST;
  		param_rx.drcmr = dmarx->start;
  		param_tx.prio = PXAD_PRIO_LOWEST;
  		param_tx.drcmr = dmatx->start;
  	}
  
  	dma_cap_zero(mask);
  	dma_cap_set(DMA_SLAVE, mask);
  
  	host->dma_chan_rx =
  		dma_request_slave_channel_compat(mask, pxad_filter_fn,
  						 &param_rx, &pdev->dev, "rx");
  	if (host->dma_chan_rx == NULL) {
  		dev_err(&pdev->dev, "unable to request rx dma channel
  ");
  		ret = -ENODEV;
9a788c6b7   Bridge Wu   [ARM] 4711/1: pxa...
730
731
  		goto out;
  	}
9a788c6b7   Bridge Wu   [ARM] 4711/1: pxa...
732

6464b7140   Daniel Mack   mmc: pxamci: swit...
733
734
735
736
737
738
739
  	host->dma_chan_tx =
  		dma_request_slave_channel_compat(mask, pxad_filter_fn,
  						 &param_tx,  &pdev->dev, "tx");
  	if (host->dma_chan_tx == NULL) {
  		dev_err(&pdev->dev, "unable to request tx dma channel
  ");
  		ret = -ENODEV;
9a788c6b7   Bridge Wu   [ARM] 4711/1: pxa...
740
741
  		goto out;
  	}
9a788c6b7   Bridge Wu   [ARM] 4711/1: pxa...
742

b405db6c0   Robert Jarzmik   [ARM] pxamci: add...
743
744
745
746
747
748
  	if (host->pdata) {
  		gpio_cd = host->pdata->gpio_card_detect;
  		gpio_ro = host->pdata->gpio_card_ro;
  		gpio_power = host->pdata->gpio_power;
  	}
  	if (gpio_is_valid(gpio_power)) {
fd546ee6a   Robert Jarzmik   mmc: pxamci: fix ...
749
750
  		ret = devm_gpio_request(&pdev->dev, gpio_power,
  					"mmc card power");
b405db6c0   Robert Jarzmik   [ARM] pxamci: add...
751
  		if (ret) {
fd546ee6a   Robert Jarzmik   mmc: pxamci: fix ...
752
753
754
  			dev_err(&pdev->dev, "Failed requesting gpio_power %d
  ",
  				gpio_power);
b405db6c0   Robert Jarzmik   [ARM] pxamci: add...
755
756
757
758
759
  			goto out;
  		}
  		gpio_direction_output(gpio_power,
  				      host->pdata->gpio_power_invert);
  	}
b3802db5e   Robert Jarzmik   mmc: pxamci: fix ...
760
  	if (gpio_is_valid(gpio_ro)) {
fd546ee6a   Robert Jarzmik   mmc: pxamci: fix ...
761
  		ret = mmc_gpio_request_ro(mmc, gpio_ro);
b3802db5e   Robert Jarzmik   mmc: pxamci: fix ...
762
763
764
765
766
767
768
769
770
  		if (ret) {
  			dev_err(&pdev->dev, "Failed requesting gpio_ro %d
  ",
  				gpio_ro);
  			goto out;
  		} else {
  			mmc->caps2 |= host->pdata->gpio_card_ro_invert ?
  				0 : MMC_CAP2_RO_ACTIVE_HIGH;
  		}
b405db6c0   Robert Jarzmik   [ARM] pxamci: add...
771
  	}
b405db6c0   Robert Jarzmik   [ARM] pxamci: add...
772

fd546ee6a   Robert Jarzmik   mmc: pxamci: fix ...
773
774
775
776
777
778
  	if (gpio_is_valid(gpio_cd))
  		ret = mmc_gpio_request_cd(mmc, gpio_cd, 0);
  	if (ret) {
  		dev_err(&pdev->dev, "Failed requesting gpio_cd %d
  ", gpio_cd);
  		goto out;
b405db6c0   Robert Jarzmik   [ARM] pxamci: add...
779
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
780
  	if (host->pdata && host->pdata->init)
3ae5eaec1   Russell King   [DRIVER MODEL] Co...
781
  		host->pdata->init(&pdev->dev, pxamci_detect_irq, mmc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
782

b405db6c0   Robert Jarzmik   [ARM] pxamci: add...
783
784
785
786
787
788
  	if (gpio_is_valid(gpio_power) && host->pdata->setpower)
  		dev_warn(&pdev->dev, "gpio_power and setpower() both defined
  ");
  	if (gpio_is_valid(gpio_ro) && host->pdata->get_ro)
  		dev_warn(&pdev->dev, "gpio_ro and get_ro() both defined
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
789
790
791
  	mmc_add_host(mmc);
  
  	return 0;
fd546ee6a   Robert Jarzmik   mmc: pxamci: fix ...
792
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
793
  	if (host) {
6464b7140   Daniel Mack   mmc: pxamci: swit...
794
795
796
797
  		if (host->dma_chan_rx)
  			dma_release_channel(host->dma_chan_rx);
  		if (host->dma_chan_tx)
  			dma_release_channel(host->dma_chan_tx);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
798
799
800
  	}
  	if (mmc)
  		mmc_free_host(mmc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
801
802
  	return ret;
  }
3ae5eaec1   Russell King   [DRIVER MODEL] Co...
803
  static int pxamci_remove(struct platform_device *pdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
804
  {
3ae5eaec1   Russell King   [DRIVER MODEL] Co...
805
  	struct mmc_host *mmc = platform_get_drvdata(pdev);
b405db6c0   Robert Jarzmik   [ARM] pxamci: add...
806
  	int gpio_cd = -1, gpio_ro = -1, gpio_power = -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
807

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
808
809
  	if (mmc) {
  		struct pxamci_host *host = mmc_priv(mmc);
5d6b1edf8   Daniel Mack   [ARM] pxamci: cal...
810
  		mmc_remove_host(mmc);
b405db6c0   Robert Jarzmik   [ARM] pxamci: add...
811
812
813
814
815
  		if (host->pdata) {
  			gpio_cd = host->pdata->gpio_card_detect;
  			gpio_ro = host->pdata->gpio_card_ro;
  			gpio_power = host->pdata->gpio_power;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
816
  		if (host->pdata && host->pdata->exit)
3ae5eaec1   Russell King   [DRIVER MODEL] Co...
817
  			host->pdata->exit(&pdev->dev, mmc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
818

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
819
820
821
822
  		pxamci_stop_clock(host);
  		writel(TXFIFO_WR_REQ|RXFIFO_RD_REQ|CLK_IS_OFF|STOP_CMD|
  		       END_CMD_RES|PRG_DONE|DATA_TRAN_DONE,
  		       host->base + MMC_I_MASK);
6464b7140   Daniel Mack   mmc: pxamci: swit...
823
824
825
826
  		dmaengine_terminate_all(host->dma_chan_rx);
  		dmaengine_terminate_all(host->dma_chan_tx);
  		dma_release_channel(host->dma_chan_rx);
  		dma_release_channel(host->dma_chan_tx);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
827
828
829
830
831
  
  		mmc_free_host(mmc);
  	}
  	return 0;
  }
3ae5eaec1   Russell King   [DRIVER MODEL] Co...
832
  static struct platform_driver pxamci_driver = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
833
834
  	.probe		= pxamci_probe,
  	.remove		= pxamci_remove,
3ae5eaec1   Russell King   [DRIVER MODEL] Co...
835
836
  	.driver		= {
  		.name	= DRIVER_NAME,
e6027b467   Daniel Mack   mmc: pxa-mci: add...
837
  		.of_match_table = of_match_ptr(pxa_mmc_dt_ids),
3ae5eaec1   Russell King   [DRIVER MODEL] Co...
838
  	},
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
839
  };
d1f81a64a   Axel Lin   mmc: convert driv...
840
  module_platform_driver(pxamci_driver);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
841
842
843
  
  MODULE_DESCRIPTION("PXA Multimedia Card Interface Driver");
  MODULE_LICENSE("GPL");
bc65c724d   Kay Sievers   mmc: fix platform...
844
  MODULE_ALIAS("platform:pxa2xx-mci");