core.c 14.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
71
72
73
74
75
	int err = cmd->error;

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

	if (err && cmd->retries) {
		cmd->retries--;
		cmd->error = 0;
		host->ops->request(host, mrq);
	} else if (mrq->done) {
		mrq->done(mrq);
	}
}

EXPORT_SYMBOL(mmc_request_done);

/**
 *	mmc_start_request - start a command on a host
 *	@host: MMC host to start command on
 *	@mrq: MMC request to start
 *
 *	Queue a command on the specified host.  We expect the
 *	caller to be holding the host lock with interrupts disabled.
 */
void
mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
{
Pierre Ossman's avatar
Pierre Ossman committed
99
100
101
102
#ifdef CONFIG_MMC_DEBUG
	unsigned int i, sz;
#endif

103
104
105
	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
106

Pierre Ossman's avatar
Pierre Ossman committed
107
	WARN_ON(!host->claimed);
Linus Torvalds's avatar
Linus Torvalds committed
108
109
110
111

	mrq->cmd->error = 0;
	mrq->cmd->mrq = mrq;
	if (mrq->data) {
112
		BUG_ON(mrq->data->blksz > host->max_blk_size);
113
114
115
		BUG_ON(mrq->data->blocks > host->max_blk_count);
		BUG_ON(mrq->data->blocks * mrq->data->blksz >
			host->max_req_size);
116

Pierre Ossman's avatar
Pierre Ossman committed
117
118
119
120
121
122
123
#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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
		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);
}

EXPORT_SYMBOL(mmc_start_request);

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

Pierre Ossman's avatar
Pierre Ossman committed
143
144
145
146
147
148
149
150
151
152
/**
 *	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
153
{
154
	DECLARE_COMPLETION_ONSTACK(complete);
Linus Torvalds's avatar
Linus Torvalds committed
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179

	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
180
	BUG_ON(!host->claimed);
Linus Torvalds's avatar
Linus Torvalds committed
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196

	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);

197
198
199
200
201
/**
 *	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
 *	@write: flag to differentiate reads from writes
Pierre Ossman's avatar
Pierre Ossman committed
202
203
204
 *
 *	Computes the data timeout parameters according to the
 *	correct algorithm given the card type.
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
 */
void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card,
			  int write)
{
	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.
	 */
	if (write)
		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);

		if (write)
			limit_us = 250000;
		else
			limit_us = 100000;

241
242
243
244
		/*
		 * SDHC cards always use these fixed values.
		 */
		if (timeout_us > limit_us || mmc_card_blockaddr(card)) {
245
246
247
248
249
250
251
			data->timeout_ns = limit_us * 1000;
			data->timeout_clks = 0;
		}
	}
}
EXPORT_SYMBOL(mmc_set_data_timeout);

Linus Torvalds's avatar
Linus Torvalds committed
252
/**
Pierre Ossman's avatar
Pierre Ossman committed
253
 *	mmc_claim_host - exclusively claim a host
Linus Torvalds's avatar
Linus Torvalds committed
254
255
 *	@host: mmc host to claim
 *
Pierre Ossman's avatar
Pierre Ossman committed
256
 *	Claim a host for a set of operations.
Linus Torvalds's avatar
Linus Torvalds committed
257
 */
258
void mmc_claim_host(struct mmc_host *host)
Linus Torvalds's avatar
Linus Torvalds committed
259
260
261
262
{
	DECLARE_WAITQUEUE(wait, current);
	unsigned long flags;

263
264
	might_sleep();

Linus Torvalds's avatar
Linus Torvalds committed
265
266
267
268
	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
269
		if (!host->claimed)
Linus Torvalds's avatar
Linus Torvalds committed
270
271
272
273
274
275
			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
276
	host->claimed = 1;
Linus Torvalds's avatar
Linus Torvalds committed
277
278
279
280
	spin_unlock_irqrestore(&host->lock, flags);
	remove_wait_queue(&host->wq, &wait);
}

281
EXPORT_SYMBOL(mmc_claim_host);
Linus Torvalds's avatar
Linus Torvalds committed
282
283
284
285
286
287
288
289
290
291
292
293

/**
 *	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
294
	BUG_ON(!host->claimed);
Linus Torvalds's avatar
Linus Torvalds committed
295
296

	spin_lock_irqsave(&host->lock, flags);
Pierre Ossman's avatar
Pierre Ossman committed
297
	host->claimed = 0;
Linus Torvalds's avatar
Linus Torvalds committed
298
299
300
301
302
303
304
	spin_unlock_irqrestore(&host->lock, flags);

	wake_up(&host->wq);
}

EXPORT_SYMBOL(mmc_release_host);

Pierre Ossman's avatar
Pierre Ossman committed
305
306
307
308
/*
 * Internal function that does the actual ios call to the host driver,
 * optionally printing some debug output.
 */
309
310
311
312
static inline void mmc_set_ios(struct mmc_host *host)
{
	struct mmc_ios *ios = &host->ios;

313
314
	pr_debug("%s: clock %uHz busmode %u powermode %u cs %u Vdd %u "
		"width %u timing %u\n",
315
316
		 mmc_hostname(host), ios->clock, ios->bus_mode,
		 ios->power_mode, ios->chip_select, ios->vdd,
317
		 ios->bus_width, ios->timing);
318

319
320
321
	host->ops->set_ios(host, ios);
}

Pierre Ossman's avatar
Pierre Ossman committed
322
323
324
/*
 * Control chip select pin on a host.
 */
Pierre Ossman's avatar
Pierre Ossman committed
325
void mmc_set_chip_select(struct mmc_host *host, int mode)
Linus Torvalds's avatar
Linus Torvalds committed
326
{
Pierre Ossman's avatar
Pierre Ossman committed
327
328
	host->ios.chip_select = mode;
	mmc_set_ios(host);
Linus Torvalds's avatar
Linus Torvalds committed
329
330
}

Pierre Ossman's avatar
Pierre Ossman committed
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
/*
 * 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
364
365
366
367
/*
 * Mask off any voltages we don't support and select
 * the lowest voltage
 */
Pierre Ossman's avatar
Pierre Ossman committed
368
u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
Linus Torvalds's avatar
Linus Torvalds committed
369
370
371
372
373
374
375
376
377
{
	int bit;

	ocr &= host->ocr_avail;

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

378
		ocr &= 3 << bit;
Linus Torvalds's avatar
Linus Torvalds committed
379
380

		host->ios.vdd = bit;
381
		mmc_set_ios(host);
Linus Torvalds's avatar
Linus Torvalds committed
382
383
384
385
386
387
388
	} else {
		ocr = 0;
	}

	return ocr;
}

Pierre Ossman's avatar
Pierre Ossman committed
389
/*
Pierre Ossman's avatar
Pierre Ossman committed
390
 * Select timing parameters for host.
Pierre Ossman's avatar
Pierre Ossman committed
391
 */
Pierre Ossman's avatar
Pierre Ossman committed
392
void mmc_set_timing(struct mmc_host *host, unsigned int timing)
Pierre Ossman's avatar
Pierre Ossman committed
393
{
Pierre Ossman's avatar
Pierre Ossman committed
394
395
	host->ios.timing = timing;
	mmc_set_ios(host);
Pierre Ossman's avatar
Pierre Ossman committed
396
397
}

Linus Torvalds's avatar
Linus Torvalds committed
398
/*
399
400
401
402
403
404
405
406
407
 * 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
408
409
410
411
412
413
414
 */
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
415
	host->ios.chip_select = MMC_CS_DONTCARE;
Linus Torvalds's avatar
Linus Torvalds committed
416
	host->ios.power_mode = MMC_POWER_UP;
Pierre Ossman's avatar
Pierre Ossman committed
417
	host->ios.bus_width = MMC_BUS_WIDTH_1;
418
	host->ios.timing = MMC_TIMING_LEGACY;
419
	mmc_set_ios(host);
Linus Torvalds's avatar
Linus Torvalds committed
420
421
422
423
424

	mmc_delay(1);

	host->ios.clock = host->f_min;
	host->ios.power_mode = MMC_POWER_ON;
425
	mmc_set_ios(host);
Linus Torvalds's avatar
Linus Torvalds committed
426
427
428
429
430
431
432
433
434

	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
435
	host->ios.chip_select = MMC_CS_DONTCARE;
Linus Torvalds's avatar
Linus Torvalds committed
436
	host->ios.power_mode = MMC_POWER_OFF;
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
}

/*
Pierre Ossman's avatar
Pierre Ossman committed
443
444
 * 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
445
 */
Pierre Ossman's avatar
Pierre Ossman committed
446
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops)
Linus Torvalds's avatar
Linus Torvalds committed
447
{
Pierre Ossman's avatar
Pierre Ossman committed
448
	unsigned long flags;
449

Pierre Ossman's avatar
Pierre Ossman committed
450
451
	BUG_ON(!host);
	BUG_ON(!ops);
452

Pierre Ossman's avatar
Pierre Ossman committed
453
	BUG_ON(!host->claimed);
454

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

Pierre Ossman's avatar
Pierre Ossman committed
457
458
	BUG_ON(host->bus_ops);
	BUG_ON(host->bus_refs);
Pierre Ossman's avatar
Pierre Ossman committed
459

Pierre Ossman's avatar
Pierre Ossman committed
460
461
462
	host->bus_ops = ops;
	host->bus_refs = 1;
	host->bus_dead = 0;
Pierre Ossman's avatar
Pierre Ossman committed
463

Pierre Ossman's avatar
Pierre Ossman committed
464
	spin_unlock_irqrestore(&host->lock, flags);
Pierre Ossman's avatar
Pierre Ossman committed
465
466
}

Pierre Ossman's avatar
Pierre Ossman committed
467
468
469
470
471
/*
 * 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)
472
{
Pierre Ossman's avatar
Pierre Ossman committed
473
	unsigned long flags;
474

Pierre Ossman's avatar
Pierre Ossman committed
475
	BUG_ON(!host);
476

Pierre Ossman's avatar
Pierre Ossman committed
477
478
	BUG_ON(!host->claimed);
	BUG_ON(!host->bus_ops);
479

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

Pierre Ossman's avatar
Pierre Ossman committed
482
	host->bus_dead = 1;
483

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

Pierre Ossman's avatar
Pierre Ossman committed
486
	mmc_power_off(host);
Linus Torvalds's avatar
Linus Torvalds committed
487

Pierre Ossman's avatar
Pierre Ossman committed
488
	mmc_bus_put(host);
Linus Torvalds's avatar
Linus Torvalds committed
489
490
491
}

/*
Pierre Ossman's avatar
Pierre Ossman committed
492
 * Cleanup when the last reference to the bus operator is dropped.
Linus Torvalds's avatar
Linus Torvalds committed
493
 */
Pierre Ossman's avatar
Pierre Ossman committed
494
void __mmc_release_bus(struct mmc_host *host)
Linus Torvalds's avatar
Linus Torvalds committed
495
{
Pierre Ossman's avatar
Pierre Ossman committed
496
497
498
	BUG_ON(!host);
	BUG_ON(host->bus_refs);
	BUG_ON(!host->bus_dead);
Linus Torvalds's avatar
Linus Torvalds committed
499

Pierre Ossman's avatar
Pierre Ossman committed
500
	host->bus_ops = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
501
502
503
504
505
}

/**
 *	mmc_detect_change - process change of state on a MMC socket
 *	@host: host which changed state.
506
 *	@delay: optional delay to wait before detection (jiffies)
Linus Torvalds's avatar
Linus Torvalds committed
507
 *
Pierre Ossman's avatar
Pierre Ossman committed
508
509
510
511
 *	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
512
 */
513
void mmc_detect_change(struct mmc_host *host, unsigned long delay)
Linus Torvalds's avatar
Linus Torvalds committed
514
{
515
#ifdef CONFIG_MMC_DEBUG
516
	unsigned long flags;
Andrew Morton's avatar
Andrew Morton committed
517
	spin_lock_irqsave(&host->lock, flags);
518
	BUG_ON(host->removed);
Andrew Morton's avatar
Andrew Morton committed
519
	spin_unlock_irqrestore(&host->lock, flags);
520
521
#endif

David Howells's avatar
David Howells committed
522
	mmc_schedule_delayed_work(&host->detect, delay);
Linus Torvalds's avatar
Linus Torvalds committed
523
524
525
526
527
}

EXPORT_SYMBOL(mmc_detect_change);


528
void mmc_rescan(struct work_struct *work)
Linus Torvalds's avatar
Linus Torvalds committed
529
{
David Howells's avatar
David Howells committed
530
531
	struct mmc_host *host =
		container_of(work, struct mmc_host, detect.work);
Pierre Ossman's avatar
Pierre Ossman committed
532
533
	u32 ocr;
	int err;
Linus Torvalds's avatar
Linus Torvalds committed
534

Pierre Ossman's avatar
Pierre Ossman committed
535
	mmc_bus_get(host);
536

Pierre Ossman's avatar
Pierre Ossman committed
537
	if (host->bus_ops == NULL) {
Linus Torvalds's avatar
Linus Torvalds committed
538
		/*
Pierre Ossman's avatar
Pierre Ossman committed
539
540
		 * Only we can add a new handler, so it's safe to
		 * release the lock here.
Linus Torvalds's avatar
Linus Torvalds committed
541
		 */
Pierre Ossman's avatar
Pierre Ossman committed
542
		mmc_bus_put(host);
Linus Torvalds's avatar
Linus Torvalds committed
543

Pierre Ossman's avatar
Pierre Ossman committed
544
		mmc_claim_host(host);
Linus Torvalds's avatar
Linus Torvalds committed
545

Pierre Ossman's avatar
Pierre Ossman committed
546
547
		mmc_power_up(host);
		mmc_go_idle(host);
Linus Torvalds's avatar
Linus Torvalds committed
548

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

Pierre Ossman's avatar
Pierre Ossman committed
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
		err = mmc_send_app_op_cond(host, 0, &ocr);
		if (err == MMC_ERR_NONE) {
			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);
			if (err == MMC_ERR_NONE) {
				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
575
576
}

577
void mmc_start_host(struct mmc_host *host)
Linus Torvalds's avatar
Linus Torvalds committed
578
{
579
580
	mmc_power_off(host);
	mmc_detect_change(host, 0);
Linus Torvalds's avatar
Linus Torvalds committed
581
582
}

583
void mmc_stop_host(struct mmc_host *host)
Linus Torvalds's avatar
Linus Torvalds committed
584
{
585
#ifdef CONFIG_MMC_DEBUG
586
587
	unsigned long flags;
	spin_lock_irqsave(&host->lock, flags);
588
	host->removed = 1;
589
	spin_unlock_irqrestore(&host->lock, flags);
590
591
592
593
#endif

	mmc_flush_scheduled_work();

Pierre Ossman's avatar
Pierre Ossman committed
594
595
596
597
598
599
600
601
	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
602
	}
Pierre Ossman's avatar
Pierre Ossman committed
603
604
605
	mmc_bus_put(host);

	BUG_ON(host->card);
Linus Torvalds's avatar
Linus Torvalds committed
606
607
608
609
610
611
612
613
614
615
616

	mmc_power_off(host);
}

#ifdef CONFIG_PM

/**
 *	mmc_suspend_host - suspend a host
 *	@host: mmc host
 *	@state: suspend mode (PM_SUSPEND_xxx)
 */
617
int mmc_suspend_host(struct mmc_host *host, pm_message_t state)
Linus Torvalds's avatar
Linus Torvalds committed
618
{
Pierre Ossman's avatar
Pierre Ossman committed
619
620
	mmc_flush_scheduled_work();

Pierre Ossman's avatar
Pierre Ossman committed
621
622
	mmc_bus_get(host);
	if (host->bus_ops && !host->bus_dead) {
623
624
625
626
627
628
629
630
631
632
		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
633
	}
Pierre Ossman's avatar
Pierre Ossman committed
634
635
	mmc_bus_put(host);

Linus Torvalds's avatar
Linus Torvalds committed
636
637
638
639
640
641
642
643
644
645
646
647
648
	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)
{
649
650
651
652
653
654
655
656
657
658
659
660
661
	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
662
663
664
665
666
667
668
669

	return 0;
}

EXPORT_SYMBOL(mmc_resume_host);

#endif

670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
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
697
MODULE_LICENSE("GPL");