core.c 15.8 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
/*
2
 *  linux/drivers/mmc/core/core.c
Linus Torvalds's avatar
Linus Torvalds committed
3
4
 *
 *  Copyright (C) 2003-2004 Russell King, All Rights Reserved.
5
 *  SD support Copyright (C) 2004 Ian Molton, All Rights Reserved.
6
 *  Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved.
7
 *  MMCv4 support Copyright (C) 2006 Philip Langdale, All Rights Reserved.
Linus Torvalds's avatar
Linus Torvalds committed
8
9
10
11
12
13
14
15
16
17
18
19
20
 *
 * 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.
 */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/completion.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/pagemap.h>
#include <linux/err.h>
Pierre Ossman's avatar
Pierre Ossman committed
21
22
#include <asm/scatterlist.h>
#include <linux/scatterlist.h>
Linus Torvalds's avatar
Linus Torvalds committed
23
24
25

#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
Pierre Ossman's avatar
Pierre Ossman committed
26
27
#include <linux/mmc/mmc.h>
#include <linux/mmc/sd.h>
Linus Torvalds's avatar
Linus Torvalds committed
28

29
#include "core.h"
30
31
#include "bus.h"
#include "host.h"
Pierre Ossman's avatar
Pierre Ossman committed
32
33
34

#include "mmc_ops.h"
#include "sd_ops.h"
Linus Torvalds's avatar
Linus Torvalds committed
35

Pierre Ossman's avatar
Pierre Ossman committed
36
37
extern int mmc_attach_mmc(struct mmc_host *host, u32 ocr);
extern int mmc_attach_sd(struct mmc_host *host, u32 ocr);
Linus Torvalds's avatar
Linus Torvalds committed
38

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
static struct workqueue_struct *workqueue;

/*
 * Internal function. Schedule delayed work in the MMC work queue.
 */
static int mmc_schedule_delayed_work(struct delayed_work *work,
				     unsigned long delay)
{
	return queue_delayed_work(workqueue, work, delay);
}

/*
 * Internal function. Flush all scheduled work from the MMC work queue.
 */
static void mmc_flush_scheduled_work(void)
{
	flush_workqueue(workqueue);
}

Linus Torvalds's avatar
Linus Torvalds committed
58
/**
59
60
61
 *	mmc_request_done - finish processing an MMC request
 *	@host: MMC host which completed request
 *	@mrq: MMC request which request
Linus Torvalds's avatar
Linus Torvalds committed
62
63
 *
 *	MMC drivers should call this function when they have completed
64
 *	their processing of a request.
Linus Torvalds's avatar
Linus Torvalds committed
65
66
67
68
 */
void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
{
	struct mmc_command *cmd = mrq->cmd;
69
70
	int err = cmd->error;

Linus Torvalds's avatar
Linus Torvalds committed
71
	if (err && cmd->retries) {
72
73
74
		pr_debug("%s: req failed (CMD%u): %d, retrying...\n",
			mmc_hostname(host), cmd->opcode, err);

Linus Torvalds's avatar
Linus Torvalds committed
75
76
77
		cmd->retries--;
		cmd->error = 0;
		host->ops->request(host, mrq);
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
	} else {
		pr_debug("%s: req done (CMD%u): %d: %08x %08x %08x %08x\n",
			mmc_hostname(host), cmd->opcode, err,
			cmd->resp[0], cmd->resp[1],
			cmd->resp[2], cmd->resp[3]);

		if (mrq->data) {
			pr_debug("%s:     %d bytes transferred: %d\n",
				mmc_hostname(host),
				mrq->data->bytes_xfered, mrq->data->error);
		}

		if (mrq->stop) {
			pr_debug("%s:     (CMD%u): %d: %08x %08x %08x %08x\n",
				mmc_hostname(host), mrq->stop->opcode,
				mrq->stop->error,
				mrq->stop->resp[0], mrq->stop->resp[1],
				mrq->stop->resp[2], mrq->stop->resp[3]);
		}

		if (mrq->done)
			mrq->done(mrq);
Linus Torvalds's avatar
Linus Torvalds committed
100
101
102
103
104
	}
}

EXPORT_SYMBOL(mmc_request_done);

105
static void
Linus Torvalds's avatar
Linus Torvalds committed
106
107
mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
{
Pierre Ossman's avatar
Pierre Ossman committed
108
109
110
111
#ifdef CONFIG_MMC_DEBUG
	unsigned int i, sz;
#endif

112
113
114
	pr_debug("%s: starting CMD%u arg %08x flags %08x\n",
		 mmc_hostname(host), mrq->cmd->opcode,
		 mrq->cmd->arg, mrq->cmd->flags);
Linus Torvalds's avatar
Linus Torvalds committed
115

116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
	if (mrq->data) {
		pr_debug("%s:     blksz %d blocks %d flags %08x "
			"tsac %d ms nsac %d\n",
			mmc_hostname(host), mrq->data->blksz,
			mrq->data->blocks, mrq->data->flags,
			mrq->data->timeout_ns / 10000000,
			mrq->data->timeout_clks);
	}

	if (mrq->stop) {
		pr_debug("%s:     CMD%u arg %08x flags %08x\n",
			 mmc_hostname(host), mrq->stop->opcode,
			 mrq->stop->arg, mrq->stop->flags);
	}

Pierre Ossman's avatar
Pierre Ossman committed
131
	WARN_ON(!host->claimed);
Linus Torvalds's avatar
Linus Torvalds committed
132
133
134
135

	mrq->cmd->error = 0;
	mrq->cmd->mrq = mrq;
	if (mrq->data) {
136
		BUG_ON(mrq->data->blksz > host->max_blk_size);
137
138
139
		BUG_ON(mrq->data->blocks > host->max_blk_count);
		BUG_ON(mrq->data->blocks * mrq->data->blksz >
			host->max_req_size);
140

Pierre Ossman's avatar
Pierre Ossman committed
141
142
143
144
145
146
147
#ifdef CONFIG_MMC_DEBUG
		sz = 0;
		for (i = 0;i < mrq->data->sg_len;i++)
			sz += mrq->data->sg[i].length;
		BUG_ON(sz != mrq->data->blocks * mrq->data->blksz);
#endif

Linus Torvalds's avatar
Linus Torvalds committed
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
		mrq->cmd->data = mrq->data;
		mrq->data->error = 0;
		mrq->data->mrq = mrq;
		if (mrq->stop) {
			mrq->data->stop = mrq->stop;
			mrq->stop->error = 0;
			mrq->stop->mrq = mrq;
		}
	}
	host->ops->request(host, mrq);
}

static void mmc_wait_done(struct mmc_request *mrq)
{
	complete(mrq->done_data);
}

Pierre Ossman's avatar
Pierre Ossman committed
165
166
167
168
169
170
171
172
173
174
/**
 *	mmc_wait_for_req - start a request and wait for completion
 *	@host: MMC host to start command
 *	@mrq: MMC request to start
 *
 *	Start a new MMC custom command request for a host, and wait
 *	for the command to complete. Does not attempt to parse the
 *	response.
 */
void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
Linus Torvalds's avatar
Linus Torvalds committed
175
{
176
	DECLARE_COMPLETION_ONSTACK(complete);
Linus Torvalds's avatar
Linus Torvalds committed
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201

	mrq->done_data = &complete;
	mrq->done = mmc_wait_done;

	mmc_start_request(host, mrq);

	wait_for_completion(&complete);
}

EXPORT_SYMBOL(mmc_wait_for_req);

/**
 *	mmc_wait_for_cmd - start a command and wait for completion
 *	@host: MMC host to start command
 *	@cmd: MMC command to start
 *	@retries: maximum number of retries
 *
 *	Start a new MMC command for a host, and wait for the command
 *	to complete.  Return any error that occurred while the command
 *	was executing.  Do not attempt to parse the response.
 */
int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries)
{
	struct mmc_request mrq;

Pierre Ossman's avatar
Pierre Ossman committed
202
	BUG_ON(!host->claimed);
Linus Torvalds's avatar
Linus Torvalds committed
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218

	memset(&mrq, 0, sizeof(struct mmc_request));

	memset(cmd->resp, 0, sizeof(cmd->resp));
	cmd->retries = retries;

	mrq.cmd = cmd;
	cmd->data = NULL;

	mmc_wait_for_req(host, &mrq);

	return cmd->error;
}

EXPORT_SYMBOL(mmc_wait_for_cmd);

219
220
221
222
/**
 *	mmc_set_data_timeout - set the timeout for a data command
 *	@data: data phase for command
 *	@card: the MMC card associated with the data transfer
Pierre Ossman's avatar
Pierre Ossman committed
223
224
225
 *
 *	Computes the data timeout parameters according to the
 *	correct algorithm given the card type.
226
 */
227
void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card)
228
229
230
231
232
233
234
235
236
237
238
239
{
	unsigned int mult;

	/*
	 * SD cards use a 100 multiplier rather than 10
	 */
	mult = mmc_card_sd(card) ? 100 : 10;

	/*
	 * Scale up the multiplier (and therefore the timeout) by
	 * the r2w factor for writes.
	 */
240
	if (data->flags & MMC_DATA_WRITE)
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
		mult <<= card->csd.r2w_factor;

	data->timeout_ns = card->csd.tacc_ns * mult;
	data->timeout_clks = card->csd.tacc_clks * mult;

	/*
	 * SD cards also have an upper limit on the timeout.
	 */
	if (mmc_card_sd(card)) {
		unsigned int timeout_us, limit_us;

		timeout_us = data->timeout_ns / 1000;
		timeout_us += data->timeout_clks * 1000 /
			(card->host->ios.clock / 1000);

256
		if (data->flags & MMC_DATA_WRITE)
257
258
259
260
			limit_us = 250000;
		else
			limit_us = 100000;

261
262
263
264
		/*
		 * SDHC cards always use these fixed values.
		 */
		if (timeout_us > limit_us || mmc_card_blockaddr(card)) {
265
266
267
268
269
270
271
			data->timeout_ns = limit_us * 1000;
			data->timeout_clks = 0;
		}
	}
}
EXPORT_SYMBOL(mmc_set_data_timeout);

Linus Torvalds's avatar
Linus Torvalds committed
272
/**
Pierre Ossman's avatar
Pierre Ossman committed
273
 *	mmc_claim_host - exclusively claim a host
Linus Torvalds's avatar
Linus Torvalds committed
274
275
 *	@host: mmc host to claim
 *
Pierre Ossman's avatar
Pierre Ossman committed
276
 *	Claim a host for a set of operations.
Linus Torvalds's avatar
Linus Torvalds committed
277
 */
278
void mmc_claim_host(struct mmc_host *host)
Linus Torvalds's avatar
Linus Torvalds committed
279
280
281
282
{
	DECLARE_WAITQUEUE(wait, current);
	unsigned long flags;

283
284
	might_sleep();

Linus Torvalds's avatar
Linus Torvalds committed
285
286
287
288
	add_wait_queue(&host->wq, &wait);
	spin_lock_irqsave(&host->lock, flags);
	while (1) {
		set_current_state(TASK_UNINTERRUPTIBLE);
Pierre Ossman's avatar
Pierre Ossman committed
289
		if (!host->claimed)
Linus Torvalds's avatar
Linus Torvalds committed
290
291
292
293
294
295
			break;
		spin_unlock_irqrestore(&host->lock, flags);
		schedule();
		spin_lock_irqsave(&host->lock, flags);
	}
	set_current_state(TASK_RUNNING);
Pierre Ossman's avatar
Pierre Ossman committed
296
	host->claimed = 1;
Linus Torvalds's avatar
Linus Torvalds committed
297
298
299
300
	spin_unlock_irqrestore(&host->lock, flags);
	remove_wait_queue(&host->wq, &wait);
}

301
EXPORT_SYMBOL(mmc_claim_host);
Linus Torvalds's avatar
Linus Torvalds committed
302
303
304
305
306
307
308
309
310
311
312
313

/**
 *	mmc_release_host - release a host
 *	@host: mmc host to release
 *
 *	Release a MMC host, allowing others to claim the host
 *	for their operations.
 */
void mmc_release_host(struct mmc_host *host)
{
	unsigned long flags;

Pierre Ossman's avatar
Pierre Ossman committed
314
	BUG_ON(!host->claimed);
Linus Torvalds's avatar
Linus Torvalds committed
315
316

	spin_lock_irqsave(&host->lock, flags);
Pierre Ossman's avatar
Pierre Ossman committed
317
	host->claimed = 0;
Linus Torvalds's avatar
Linus Torvalds committed
318
319
320
321
322
323
324
	spin_unlock_irqrestore(&host->lock, flags);

	wake_up(&host->wq);
}

EXPORT_SYMBOL(mmc_release_host);

Pierre Ossman's avatar
Pierre Ossman committed
325
326
327
328
/*
 * Internal function that does the actual ios call to the host driver,
 * optionally printing some debug output.
 */
329
330
331
332
static inline void mmc_set_ios(struct mmc_host *host)
{
	struct mmc_ios *ios = &host->ios;

333
334
	pr_debug("%s: clock %uHz busmode %u powermode %u cs %u Vdd %u "
		"width %u timing %u\n",
335
336
		 mmc_hostname(host), ios->clock, ios->bus_mode,
		 ios->power_mode, ios->chip_select, ios->vdd,
337
		 ios->bus_width, ios->timing);
338

339
340
341
	host->ops->set_ios(host, ios);
}

Pierre Ossman's avatar
Pierre Ossman committed
342
343
344
/*
 * Control chip select pin on a host.
 */
Pierre Ossman's avatar
Pierre Ossman committed
345
void mmc_set_chip_select(struct mmc_host *host, int mode)
Linus Torvalds's avatar
Linus Torvalds committed
346
{
Pierre Ossman's avatar
Pierre Ossman committed
347
348
	host->ios.chip_select = mode;
	mmc_set_ios(host);
Linus Torvalds's avatar
Linus Torvalds committed
349
350
}

Pierre Ossman's avatar
Pierre Ossman committed
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
/*
 * Sets the host clock to the highest possible frequency that
 * is below "hz".
 */
void mmc_set_clock(struct mmc_host *host, unsigned int hz)
{
	WARN_ON(hz < host->f_min);

	if (hz > host->f_max)
		hz = host->f_max;

	host->ios.clock = hz;
	mmc_set_ios(host);
}

/*
 * Change the bus mode (open drain/push-pull) of a host.
 */
void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode)
{
	host->ios.bus_mode = mode;
	mmc_set_ios(host);
}

/*
 * Change data bus width of a host.
 */
void mmc_set_bus_width(struct mmc_host *host, unsigned int width)
{
	host->ios.bus_width = width;
	mmc_set_ios(host);
}

Linus Torvalds's avatar
Linus Torvalds committed
384
385
386
387
/*
 * Mask off any voltages we don't support and select
 * the lowest voltage
 */
Pierre Ossman's avatar
Pierre Ossman committed
388
u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
Linus Torvalds's avatar
Linus Torvalds committed
389
390
391
392
393
394
395
396
397
{
	int bit;

	ocr &= host->ocr_avail;

	bit = ffs(ocr);
	if (bit) {
		bit -= 1;

398
		ocr &= 3 << bit;
Linus Torvalds's avatar
Linus Torvalds committed
399
400

		host->ios.vdd = bit;
401
		mmc_set_ios(host);
Linus Torvalds's avatar
Linus Torvalds committed
402
403
404
405
406
407
408
	} else {
		ocr = 0;
	}

	return ocr;
}

Pierre Ossman's avatar
Pierre Ossman committed
409
/*
Pierre Ossman's avatar
Pierre Ossman committed
410
 * Select timing parameters for host.
Pierre Ossman's avatar
Pierre Ossman committed
411
 */
Pierre Ossman's avatar
Pierre Ossman committed
412
void mmc_set_timing(struct mmc_host *host, unsigned int timing)
Pierre Ossman's avatar
Pierre Ossman committed
413
{
Pierre Ossman's avatar
Pierre Ossman committed
414
415
	host->ios.timing = timing;
	mmc_set_ios(host);
Pierre Ossman's avatar
Pierre Ossman committed
416
417
}

Linus Torvalds's avatar
Linus Torvalds committed
418
/*
419
420
421
422
423
424
425
426
427
 * Apply power to the MMC stack.  This is a two-stage process.
 * First, we enable power to the card without the clock running.
 * We then wait a bit for the power to stabilise.  Finally,
 * enable the bus drivers and clock to the card.
 *
 * We must _NOT_ enable the clock prior to power stablising.
 *
 * If a host does all the power sequencing itself, ignore the
 * initial MMC_POWER_UP stage.
Linus Torvalds's avatar
Linus Torvalds committed
428
429
430
431
432
433
434
 */
static void mmc_power_up(struct mmc_host *host)
{
	int bit = fls(host->ocr_avail) - 1;

	host->ios.vdd = bit;
	host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
Pierre Ossman's avatar
Pierre Ossman committed
435
	host->ios.chip_select = MMC_CS_DONTCARE;
Linus Torvalds's avatar
Linus Torvalds committed
436
	host->ios.power_mode = MMC_POWER_UP;
Pierre Ossman's avatar
Pierre Ossman committed
437
	host->ios.bus_width = MMC_BUS_WIDTH_1;
438
	host->ios.timing = MMC_TIMING_LEGACY;
439
	mmc_set_ios(host);
Linus Torvalds's avatar
Linus Torvalds committed
440
441
442
443
444

	mmc_delay(1);

	host->ios.clock = host->f_min;
	host->ios.power_mode = MMC_POWER_ON;
445
	mmc_set_ios(host);
Linus Torvalds's avatar
Linus Torvalds committed
446
447
448
449
450
451
452
453
454

	mmc_delay(2);
}

static void mmc_power_off(struct mmc_host *host)
{
	host->ios.clock = 0;
	host->ios.vdd = 0;
	host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
Pierre Ossman's avatar
Pierre Ossman committed
455
	host->ios.chip_select = MMC_CS_DONTCARE;
Linus Torvalds's avatar
Linus Torvalds committed
456
	host->ios.power_mode = MMC_POWER_OFF;
Pierre Ossman's avatar
Pierre Ossman committed
457
	host->ios.bus_width = MMC_BUS_WIDTH_1;
458
	host->ios.timing = MMC_TIMING_LEGACY;
459
	mmc_set_ios(host);
Linus Torvalds's avatar
Linus Torvalds committed
460
461
}

462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
/*
 * Cleanup when the last reference to the bus operator is dropped.
 */
void __mmc_release_bus(struct mmc_host *host)
{
	BUG_ON(!host);
	BUG_ON(host->bus_refs);
	BUG_ON(!host->bus_dead);

	host->bus_ops = NULL;
}

/*
 * Increase reference count of bus operator
 */
static inline void mmc_bus_get(struct mmc_host *host)
{
	unsigned long flags;

	spin_lock_irqsave(&host->lock, flags);
	host->bus_refs++;
	spin_unlock_irqrestore(&host->lock, flags);
}

/*
 * Decrease reference count of bus operator and free it if
 * it is the last reference.
 */
static inline void mmc_bus_put(struct mmc_host *host)
{
	unsigned long flags;

	spin_lock_irqsave(&host->lock, flags);
	host->bus_refs--;
	if ((host->bus_refs == 0) && host->bus_ops)
		__mmc_release_bus(host);
	spin_unlock_irqrestore(&host->lock, flags);
}

Linus Torvalds's avatar
Linus Torvalds committed
501
/*
Pierre Ossman's avatar
Pierre Ossman committed
502
503
 * Assign a mmc bus handler to a host. Only one bus handler may control a
 * host at any given time.
Linus Torvalds's avatar
Linus Torvalds committed
504
 */
Pierre Ossman's avatar
Pierre Ossman committed
505
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops)
Linus Torvalds's avatar
Linus Torvalds committed
506
{
Pierre Ossman's avatar
Pierre Ossman committed
507
	unsigned long flags;
508

Pierre Ossman's avatar
Pierre Ossman committed
509
510
	BUG_ON(!host);
	BUG_ON(!ops);
511

Pierre Ossman's avatar
Pierre Ossman committed
512
	BUG_ON(!host->claimed);
513

Pierre Ossman's avatar
Pierre Ossman committed
514
	spin_lock_irqsave(&host->lock, flags);
515

Pierre Ossman's avatar
Pierre Ossman committed
516
517
	BUG_ON(host->bus_ops);
	BUG_ON(host->bus_refs);
Pierre Ossman's avatar
Pierre Ossman committed
518

Pierre Ossman's avatar
Pierre Ossman committed
519
520
521
	host->bus_ops = ops;
	host->bus_refs = 1;
	host->bus_dead = 0;
Pierre Ossman's avatar
Pierre Ossman committed
522

Pierre Ossman's avatar
Pierre Ossman committed
523
	spin_unlock_irqrestore(&host->lock, flags);
Pierre Ossman's avatar
Pierre Ossman committed
524
525
}

Pierre Ossman's avatar
Pierre Ossman committed
526
527
528
529
530
/*
 * Remove the current bus handler from a host. Assumes that there are
 * no interesting cards left, so the bus is powered down.
 */
void mmc_detach_bus(struct mmc_host *host)
531
{
Pierre Ossman's avatar
Pierre Ossman committed
532
	unsigned long flags;
533

Pierre Ossman's avatar
Pierre Ossman committed
534
	BUG_ON(!host);
535

Pierre Ossman's avatar
Pierre Ossman committed
536
537
	BUG_ON(!host->claimed);
	BUG_ON(!host->bus_ops);
538

Pierre Ossman's avatar
Pierre Ossman committed
539
	spin_lock_irqsave(&host->lock, flags);
540

Pierre Ossman's avatar
Pierre Ossman committed
541
	host->bus_dead = 1;
542

Pierre Ossman's avatar
Pierre Ossman committed
543
	spin_unlock_irqrestore(&host->lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
544

Pierre Ossman's avatar
Pierre Ossman committed
545
	mmc_power_off(host);
Linus Torvalds's avatar
Linus Torvalds committed
546

Pierre Ossman's avatar
Pierre Ossman committed
547
	mmc_bus_put(host);
Linus Torvalds's avatar
Linus Torvalds committed
548
549
550
551
552
}

/**
 *	mmc_detect_change - process change of state on a MMC socket
 *	@host: host which changed state.
553
 *	@delay: optional delay to wait before detection (jiffies)
Linus Torvalds's avatar
Linus Torvalds committed
554
 *
Pierre Ossman's avatar
Pierre Ossman committed
555
556
557
558
 *	MMC drivers should call this when they detect a card has been
 *	inserted or removed. The MMC layer will confirm that any
 *	present card is still functional, and initialize any newly
 *	inserted.
Linus Torvalds's avatar
Linus Torvalds committed
559
 */
560
void mmc_detect_change(struct mmc_host *host, unsigned long delay)
Linus Torvalds's avatar
Linus Torvalds committed
561
{
562
#ifdef CONFIG_MMC_DEBUG
563
	unsigned long flags;
Andrew Morton's avatar
Andrew Morton committed
564
	spin_lock_irqsave(&host->lock, flags);
565
	BUG_ON(host->removed);
Andrew Morton's avatar
Andrew Morton committed
566
	spin_unlock_irqrestore(&host->lock, flags);
567
568
#endif

David Howells's avatar
David Howells committed
569
	mmc_schedule_delayed_work(&host->detect, delay);
Linus Torvalds's avatar
Linus Torvalds committed
570
571
572
573
574
}

EXPORT_SYMBOL(mmc_detect_change);


575
void mmc_rescan(struct work_struct *work)
Linus Torvalds's avatar
Linus Torvalds committed
576
{
David Howells's avatar
David Howells committed
577
578
	struct mmc_host *host =
		container_of(work, struct mmc_host, detect.work);
Pierre Ossman's avatar
Pierre Ossman committed
579
580
	u32 ocr;
	int err;
Linus Torvalds's avatar
Linus Torvalds committed
581

Pierre Ossman's avatar
Pierre Ossman committed
582
	mmc_bus_get(host);
583

Pierre Ossman's avatar
Pierre Ossman committed
584
	if (host->bus_ops == NULL) {
Linus Torvalds's avatar
Linus Torvalds committed
585
		/*
Pierre Ossman's avatar
Pierre Ossman committed
586
587
		 * Only we can add a new handler, so it's safe to
		 * release the lock here.
Linus Torvalds's avatar
Linus Torvalds committed
588
		 */
Pierre Ossman's avatar
Pierre Ossman committed
589
		mmc_bus_put(host);
Linus Torvalds's avatar
Linus Torvalds committed
590

Pierre Ossman's avatar
Pierre Ossman committed
591
		mmc_claim_host(host);
Linus Torvalds's avatar
Linus Torvalds committed
592

Pierre Ossman's avatar
Pierre Ossman committed
593
594
		mmc_power_up(host);
		mmc_go_idle(host);
Linus Torvalds's avatar
Linus Torvalds committed
595

Pierre Ossman's avatar
Pierre Ossman committed
596
		mmc_send_if_cond(host, host->ocr_avail);
Linus Torvalds's avatar
Linus Torvalds committed
597

Pierre Ossman's avatar
Pierre Ossman committed
598
		err = mmc_send_app_op_cond(host, 0, &ocr);
Pierre Ossman's avatar
Pierre Ossman committed
599
		if (!err) {
Pierre Ossman's avatar
Pierre Ossman committed
600
601
602
603
604
605
606
607
			if (mmc_attach_sd(host, ocr))
				mmc_power_off(host);
		} else {
			/*
			 * If we fail to detect any SD cards then try
			 * searching for MMC cards.
			 */
			err = mmc_send_op_cond(host, 0, &ocr);
Pierre Ossman's avatar
Pierre Ossman committed
608
			if (!err) {
Pierre Ossman's avatar
Pierre Ossman committed
609
610
611
612
613
614
615
616
617
618
619
620
621
				if (mmc_attach_mmc(host, ocr))
					mmc_power_off(host);
			} else {
				mmc_power_off(host);
				mmc_release_host(host);
			}
		}
	} else {
		if (host->bus_ops->detect && !host->bus_dead)
			host->bus_ops->detect(host);

		mmc_bus_put(host);
	}
Linus Torvalds's avatar
Linus Torvalds committed
622
623
}

624
void mmc_start_host(struct mmc_host *host)
Linus Torvalds's avatar
Linus Torvalds committed
625
{
626
627
	mmc_power_off(host);
	mmc_detect_change(host, 0);
Linus Torvalds's avatar
Linus Torvalds committed
628
629
}

630
void mmc_stop_host(struct mmc_host *host)
Linus Torvalds's avatar
Linus Torvalds committed
631
{
632
#ifdef CONFIG_MMC_DEBUG
633
634
	unsigned long flags;
	spin_lock_irqsave(&host->lock, flags);
635
	host->removed = 1;
636
	spin_unlock_irqrestore(&host->lock, flags);
637
638
639
640
#endif

	mmc_flush_scheduled_work();

Pierre Ossman's avatar
Pierre Ossman committed
641
642
643
644
645
646
647
648
	mmc_bus_get(host);
	if (host->bus_ops && !host->bus_dead) {
		if (host->bus_ops->remove)
			host->bus_ops->remove(host);

		mmc_claim_host(host);
		mmc_detach_bus(host);
		mmc_release_host(host);
Linus Torvalds's avatar
Linus Torvalds committed
649
	}
Pierre Ossman's avatar
Pierre Ossman committed
650
651
652
	mmc_bus_put(host);

	BUG_ON(host->card);
Linus Torvalds's avatar
Linus Torvalds committed
653
654
655
656
657
658
659
660
661
662
663

	mmc_power_off(host);
}

#ifdef CONFIG_PM

/**
 *	mmc_suspend_host - suspend a host
 *	@host: mmc host
 *	@state: suspend mode (PM_SUSPEND_xxx)
 */
664
int mmc_suspend_host(struct mmc_host *host, pm_message_t state)
Linus Torvalds's avatar
Linus Torvalds committed
665
{
Pierre Ossman's avatar
Pierre Ossman committed
666
667
	mmc_flush_scheduled_work();

Pierre Ossman's avatar
Pierre Ossman committed
668
669
	mmc_bus_get(host);
	if (host->bus_ops && !host->bus_dead) {
670
671
672
673
674
675
676
677
678
679
		if (host->bus_ops->suspend)
			host->bus_ops->suspend(host);
		if (!host->bus_ops->resume) {
			if (host->bus_ops->remove)
				host->bus_ops->remove(host);

			mmc_claim_host(host);
			mmc_detach_bus(host);
			mmc_release_host(host);
		}
Pierre Ossman's avatar
Pierre Ossman committed
680
	}
Pierre Ossman's avatar
Pierre Ossman committed
681
682
	mmc_bus_put(host);

Linus Torvalds's avatar
Linus Torvalds committed
683
684
685
686
687
688
689
690
691
692
693
694
695
	mmc_power_off(host);

	return 0;
}

EXPORT_SYMBOL(mmc_suspend_host);

/**
 *	mmc_resume_host - resume a previously suspended host
 *	@host: mmc host
 */
int mmc_resume_host(struct mmc_host *host)
{
696
697
698
699
700
701
702
703
704
705
706
707
708
	mmc_bus_get(host);
	if (host->bus_ops && !host->bus_dead) {
		mmc_power_up(host);
		BUG_ON(!host->bus_ops->resume);
		host->bus_ops->resume(host);
	}
	mmc_bus_put(host);

	/*
	 * We add a slight delay here so that resume can progress
	 * in parallel.
	 */
	mmc_detect_change(host, 1);
Linus Torvalds's avatar
Linus Torvalds committed
709
710
711
712
713
714
715
716

	return 0;
}

EXPORT_SYMBOL(mmc_resume_host);

#endif

717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
static int __init mmc_init(void)
{
	int ret;

	workqueue = create_singlethread_workqueue("kmmcd");
	if (!workqueue)
		return -ENOMEM;

	ret = mmc_register_bus();
	if (ret == 0) {
		ret = mmc_register_host_class();
		if (ret)
			mmc_unregister_bus();
	}
	return ret;
}

static void __exit mmc_exit(void)
{
	mmc_unregister_host_class();
	mmc_unregister_bus();
	destroy_workqueue(workqueue);
}

module_init(mmc_init);
module_exit(mmc_exit);

Linus Torvalds's avatar
Linus Torvalds committed
744
MODULE_LICENSE("GPL");