core.c 15.3 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"
Pierre Ossman's avatar
Pierre Ossman committed
30
31
32
33
#include "sysfs.h"

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

Pierre Ossman's avatar
Pierre Ossman committed
35
36
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
37
38

/**
39
40
41
 *	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
42
43
 *
 *	MMC drivers should call this function when they have completed
44
 *	their processing of a request.
Linus Torvalds's avatar
Linus Torvalds committed
45
46
47
48
 */
void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
{
	struct mmc_command *cmd = mrq->cmd;
49
50
51
52
53
54
55
	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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78

	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
79
80
81
82
#ifdef CONFIG_MMC_DEBUG
	unsigned int i, sz;
#endif

83
84
85
	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
86

Pierre Ossman's avatar
Pierre Ossman committed
87
	WARN_ON(!host->claimed);
Linus Torvalds's avatar
Linus Torvalds committed
88
89
90
91

	mrq->cmd->error = 0;
	mrq->cmd->mrq = mrq;
	if (mrq->data) {
92
		BUG_ON(mrq->data->blksz > host->max_blk_size);
93
94
95
		BUG_ON(mrq->data->blocks > host->max_blk_count);
		BUG_ON(mrq->data->blocks * mrq->data->blksz >
			host->max_req_size);
96

Pierre Ossman's avatar
Pierre Ossman committed
97
98
99
100
101
102
103
#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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
		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);
}

int mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
{
125
	DECLARE_COMPLETION_ONSTACK(complete);
Linus Torvalds's avatar
Linus Torvalds committed
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152

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

	mmc_start_request(host, mrq);

	wait_for_completion(&complete);

	return 0;
}

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
153
	BUG_ON(!host->claimed);
Linus Torvalds's avatar
Linus Torvalds committed
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169

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

170
171
172
173
174
175
176
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
202
203
204
205
206
207
208
209
210
/**
 *	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
 */
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;

211
212
213
214
		/*
		 * SDHC cards always use these fixed values.
		 */
		if (timeout_us > limit_us || mmc_card_blockaddr(card)) {
215
216
217
218
219
220
221
			data->timeout_ns = limit_us * 1000;
			data->timeout_clks = 0;
		}
	}
}
EXPORT_SYMBOL(mmc_set_data_timeout);

Linus Torvalds's avatar
Linus Torvalds committed
222
223
224
225
226
227
228
229
230
231
232
/**
 *	__mmc_claim_host - exclusively claim a host
 *	@host: mmc host to claim
 *	@card: mmc card to claim host for
 *
 *	Claim a host for a set of operations.  If a valid card
 *	is passed and this wasn't the last card selected, select
 *	the card before returning.
 *
 *	Note: you should use mmc_card_claim_host or mmc_claim_host.
 */
233
void mmc_claim_host(struct mmc_host *host)
Linus Torvalds's avatar
Linus Torvalds committed
234
235
236
237
238
239
240
241
{
	DECLARE_WAITQUEUE(wait, current);
	unsigned long flags;

	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
242
		if (!host->claimed)
Linus Torvalds's avatar
Linus Torvalds committed
243
244
245
246
247
248
			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
249
	host->claimed = 1;
Linus Torvalds's avatar
Linus Torvalds committed
250
251
252
253
	spin_unlock_irqrestore(&host->lock, flags);
	remove_wait_queue(&host->wq, &wait);
}

254
EXPORT_SYMBOL(mmc_claim_host);
Linus Torvalds's avatar
Linus Torvalds committed
255
256
257
258
259
260
261
262
263
264
265
266

/**
 *	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
267
	BUG_ON(!host->claimed);
Linus Torvalds's avatar
Linus Torvalds committed
268
269

	spin_lock_irqsave(&host->lock, flags);
Pierre Ossman's avatar
Pierre Ossman committed
270
	host->claimed = 0;
Linus Torvalds's avatar
Linus Torvalds committed
271
272
273
274
275
276
277
	spin_unlock_irqrestore(&host->lock, flags);

	wake_up(&host->wq);
}

EXPORT_SYMBOL(mmc_release_host);

Pierre Ossman's avatar
Pierre Ossman committed
278
279
280
281
/*
 * Internal function that does the actual ios call to the host driver,
 * optionally printing some debug output.
 */
282
283
284
285
static inline void mmc_set_ios(struct mmc_host *host)
{
	struct mmc_ios *ios = &host->ios;

286
287
	pr_debug("%s: clock %uHz busmode %u powermode %u cs %u Vdd %u "
		"width %u timing %u\n",
288
289
		 mmc_hostname(host), ios->clock, ios->bus_mode,
		 ios->power_mode, ios->chip_select, ios->vdd,
290
		 ios->bus_width, ios->timing);
291

292
293
294
	host->ops->set_ios(host, ios);
}

Pierre Ossman's avatar
Pierre Ossman committed
295
296
297
/*
 * Control chip select pin on a host.
 */
Pierre Ossman's avatar
Pierre Ossman committed
298
void mmc_set_chip_select(struct mmc_host *host, int mode)
Linus Torvalds's avatar
Linus Torvalds committed
299
{
Pierre Ossman's avatar
Pierre Ossman committed
300
301
	host->ios.chip_select = mode;
	mmc_set_ios(host);
Linus Torvalds's avatar
Linus Torvalds committed
302
303
}

Pierre Ossman's avatar
Pierre Ossman committed
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
/*
 * 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
337
338
339
340
/*
 * Mask off any voltages we don't support and select
 * the lowest voltage
 */
Pierre Ossman's avatar
Pierre Ossman committed
341
u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
Linus Torvalds's avatar
Linus Torvalds committed
342
343
344
345
346
347
348
349
350
{
	int bit;

	ocr &= host->ocr_avail;

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

351
		ocr &= 3 << bit;
Linus Torvalds's avatar
Linus Torvalds committed
352
353

		host->ios.vdd = bit;
354
		mmc_set_ios(host);
Linus Torvalds's avatar
Linus Torvalds committed
355
356
357
358
359
360
361
	} else {
		ocr = 0;
	}

	return ocr;
}

Pierre Ossman's avatar
Pierre Ossman committed
362
/*
Pierre Ossman's avatar
Pierre Ossman committed
363
 * Select timing parameters for host.
Pierre Ossman's avatar
Pierre Ossman committed
364
 */
Pierre Ossman's avatar
Pierre Ossman committed
365
void mmc_set_timing(struct mmc_host *host, unsigned int timing)
Pierre Ossman's avatar
Pierre Ossman committed
366
{
Pierre Ossman's avatar
Pierre Ossman committed
367
368
	host->ios.timing = timing;
	mmc_set_ios(host);
Pierre Ossman's avatar
Pierre Ossman committed
369
370
}

Linus Torvalds's avatar
Linus Torvalds committed
371
/*
372
 * Allocate a new MMC card
Linus Torvalds's avatar
Linus Torvalds committed
373
 */
Pierre Ossman's avatar
Pierre Ossman committed
374
struct mmc_card *mmc_alloc_card(struct mmc_host *host)
Linus Torvalds's avatar
Linus Torvalds committed
375
{
376
	struct mmc_card *card;
Linus Torvalds's avatar
Linus Torvalds committed
377
378
379
380
381
382
383
384
385
386
387

	card = kmalloc(sizeof(struct mmc_card), GFP_KERNEL);
	if (!card)
		return ERR_PTR(-ENOMEM);

	mmc_init_card(card, host);

	return card;
}

/*
388
389
390
391
392
393
394
395
396
 * 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
397
398
399
400
401
402
403
 */
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
404
	host->ios.chip_select = MMC_CS_DONTCARE;
Linus Torvalds's avatar
Linus Torvalds committed
405
	host->ios.power_mode = MMC_POWER_UP;
Pierre Ossman's avatar
Pierre Ossman committed
406
	host->ios.bus_width = MMC_BUS_WIDTH_1;
407
	host->ios.timing = MMC_TIMING_LEGACY;
408
	mmc_set_ios(host);
Linus Torvalds's avatar
Linus Torvalds committed
409
410
411
412
413

	mmc_delay(1);

	host->ios.clock = host->f_min;
	host->ios.power_mode = MMC_POWER_ON;
414
	mmc_set_ios(host);
Linus Torvalds's avatar
Linus Torvalds committed
415
416
417
418
419
420
421
422
423

	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
424
	host->ios.chip_select = MMC_CS_DONTCARE;
Linus Torvalds's avatar
Linus Torvalds committed
425
	host->ios.power_mode = MMC_POWER_OFF;
Pierre Ossman's avatar
Pierre Ossman committed
426
	host->ios.bus_width = MMC_BUS_WIDTH_1;
427
	host->ios.timing = MMC_TIMING_LEGACY;
428
	mmc_set_ios(host);
Linus Torvalds's avatar
Linus Torvalds committed
429
430
431
}

/*
Pierre Ossman's avatar
Pierre Ossman committed
432
433
 * 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
434
 */
Pierre Ossman's avatar
Pierre Ossman committed
435
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops)
Linus Torvalds's avatar
Linus Torvalds committed
436
{
Pierre Ossman's avatar
Pierre Ossman committed
437
	unsigned long flags;
438

Pierre Ossman's avatar
Pierre Ossman committed
439
440
	BUG_ON(!host);
	BUG_ON(!ops);
441

Pierre Ossman's avatar
Pierre Ossman committed
442
	BUG_ON(!host->claimed);
443

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

Pierre Ossman's avatar
Pierre Ossman committed
446
447
	BUG_ON(host->bus_ops);
	BUG_ON(host->bus_refs);
Pierre Ossman's avatar
Pierre Ossman committed
448

Pierre Ossman's avatar
Pierre Ossman committed
449
450
451
	host->bus_ops = ops;
	host->bus_refs = 1;
	host->bus_dead = 0;
Pierre Ossman's avatar
Pierre Ossman committed
452

Pierre Ossman's avatar
Pierre Ossman committed
453
	spin_unlock_irqrestore(&host->lock, flags);
Pierre Ossman's avatar
Pierre Ossman committed
454
455
}

Pierre Ossman's avatar
Pierre Ossman committed
456
457
458
459
460
/*
 * 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)
461
{
Pierre Ossman's avatar
Pierre Ossman committed
462
	unsigned long flags;
463

Pierre Ossman's avatar
Pierre Ossman committed
464
	BUG_ON(!host);
465

Pierre Ossman's avatar
Pierre Ossman committed
466
467
	BUG_ON(!host->claimed);
	BUG_ON(!host->bus_ops);
468

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

Pierre Ossman's avatar
Pierre Ossman committed
471
	host->bus_dead = 1;
472

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

Pierre Ossman's avatar
Pierre Ossman committed
475
	mmc_power_off(host);
Linus Torvalds's avatar
Linus Torvalds committed
476

Pierre Ossman's avatar
Pierre Ossman committed
477
	mmc_bus_put(host);
Linus Torvalds's avatar
Linus Torvalds committed
478
479
480
}

/*
Pierre Ossman's avatar
Pierre Ossman committed
481
 * Cleanup when the last reference to the bus operator is dropped.
Linus Torvalds's avatar
Linus Torvalds committed
482
 */
Pierre Ossman's avatar
Pierre Ossman committed
483
void __mmc_release_bus(struct mmc_host *host)
Linus Torvalds's avatar
Linus Torvalds committed
484
{
Pierre Ossman's avatar
Pierre Ossman committed
485
486
487
	BUG_ON(!host);
	BUG_ON(host->bus_refs);
	BUG_ON(!host->bus_dead);
Linus Torvalds's avatar
Linus Torvalds committed
488

Pierre Ossman's avatar
Pierre Ossman committed
489
	host->bus_ops = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
490
491
492
493
494
}

/**
 *	mmc_detect_change - process change of state on a MMC socket
 *	@host: host which changed state.
495
 *	@delay: optional delay to wait before detection (jiffies)
Linus Torvalds's avatar
Linus Torvalds committed
496
497
498
499
 *
 *	All we know is that card(s) have been inserted or removed
 *	from the socket(s).  We don't know which socket or cards.
 */
500
void mmc_detect_change(struct mmc_host *host, unsigned long delay)
Linus Torvalds's avatar
Linus Torvalds committed
501
{
502
503
504
505
506
507
#ifdef CONFIG_MMC_DEBUG
	mmc_claim_host(host);
	BUG_ON(host->removed);
	mmc_release_host(host);
#endif

David Howells's avatar
David Howells committed
508
	mmc_schedule_delayed_work(&host->detect, delay);
Linus Torvalds's avatar
Linus Torvalds committed
509
510
511
512
513
}

EXPORT_SYMBOL(mmc_detect_change);


David Howells's avatar
David Howells committed
514
static void mmc_rescan(struct work_struct *work)
Linus Torvalds's avatar
Linus Torvalds committed
515
{
David Howells's avatar
David Howells committed
516
517
	struct mmc_host *host =
		container_of(work, struct mmc_host, detect.work);
Pierre Ossman's avatar
Pierre Ossman committed
518
519
	u32 ocr;
	int err;
Linus Torvalds's avatar
Linus Torvalds committed
520

Pierre Ossman's avatar
Pierre Ossman committed
521
	mmc_bus_get(host);
522

Pierre Ossman's avatar
Pierre Ossman committed
523
	if (host->bus_ops == NULL) {
Linus Torvalds's avatar
Linus Torvalds committed
524
		/*
Pierre Ossman's avatar
Pierre Ossman committed
525
526
		 * Only we can add a new handler, so it's safe to
		 * release the lock here.
Linus Torvalds's avatar
Linus Torvalds committed
527
		 */
Pierre Ossman's avatar
Pierre Ossman committed
528
		mmc_bus_put(host);
Linus Torvalds's avatar
Linus Torvalds committed
529

Pierre Ossman's avatar
Pierre Ossman committed
530
		mmc_claim_host(host);
Linus Torvalds's avatar
Linus Torvalds committed
531

Pierre Ossman's avatar
Pierre Ossman committed
532
533
		mmc_power_up(host);
		mmc_go_idle(host);
Linus Torvalds's avatar
Linus Torvalds committed
534

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

Pierre Ossman's avatar
Pierre Ossman committed
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
		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
561
562
563
564
565
566
567
568
569
570
571
572
573
574
}


/**
 *	mmc_alloc_host - initialise the per-host structure.
 *	@extra: sizeof private data structure
 *	@dev: pointer to host device model structure
 *
 *	Initialise the per-host structure.
 */
struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
{
	struct mmc_host *host;

Russell King's avatar
Russell King committed
575
	host = mmc_alloc_host_sysfs(extra, dev);
Linus Torvalds's avatar
Linus Torvalds committed
576
577
578
	if (host) {
		spin_lock_init(&host->lock);
		init_waitqueue_head(&host->wq);
David Howells's avatar
David Howells committed
579
		INIT_DELAYED_WORK(&host->detect, mmc_rescan);
Linus Torvalds's avatar
Linus Torvalds committed
580
581
582
583
584
585
586
587

		/*
		 * By default, hosts do not support SGIO or large requests.
		 * They have to set these according to their abilities.
		 */
		host->max_hw_segs = 1;
		host->max_phys_segs = 1;
		host->max_seg_size = PAGE_CACHE_SIZE;
588

589
		host->max_req_size = PAGE_CACHE_SIZE;
590
		host->max_blk_size = 512;
591
		host->max_blk_count = PAGE_CACHE_SIZE / 512;
Linus Torvalds's avatar
Linus Torvalds committed
592
593
594
595
596
597
598
599
600
601
602
603
604
	}

	return host;
}

EXPORT_SYMBOL(mmc_alloc_host);

/**
 *	mmc_add_host - initialise host hardware
 *	@host: mmc host
 */
int mmc_add_host(struct mmc_host *host)
{
Russell King's avatar
Russell King committed
605
	int ret;
Linus Torvalds's avatar
Linus Torvalds committed
606

Russell King's avatar
Russell King committed
607
608
609
	ret = mmc_add_host_sysfs(host);
	if (ret == 0) {
		mmc_power_off(host);
610
		mmc_detect_change(host, 0);
Russell King's avatar
Russell King committed
611
	}
Linus Torvalds's avatar
Linus Torvalds committed
612

Russell King's avatar
Russell King committed
613
	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
614
615
616
617
618
619
620
621
622
623
624
625
626
}

EXPORT_SYMBOL(mmc_add_host);

/**
 *	mmc_remove_host - remove host hardware
 *	@host: mmc host
 *
 *	Unregister and remove all cards associated with this host,
 *	and power down the MMC bus.
 */
void mmc_remove_host(struct mmc_host *host)
{
627
628
629
630
631
632
633
634
#ifdef CONFIG_MMC_DEBUG
	mmc_claim_host(host);
	host->removed = 1;
	mmc_release_host(host);
#endif

	mmc_flush_scheduled_work();

Pierre Ossman's avatar
Pierre Ossman committed
635
636
637
638
639
640
641
642
	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
643
	}
Pierre Ossman's avatar
Pierre Ossman committed
644
645
646
	mmc_bus_put(host);

	BUG_ON(host->card);
Linus Torvalds's avatar
Linus Torvalds committed
647
648

	mmc_power_off(host);
Russell King's avatar
Russell King committed
649
	mmc_remove_host_sysfs(host);
Linus Torvalds's avatar
Linus Torvalds committed
650
651
652
653
654
655
656
657
658
659
660
661
}

EXPORT_SYMBOL(mmc_remove_host);

/**
 *	mmc_free_host - free the host structure
 *	@host: mmc host
 *
 *	Free the host once all references to it have been dropped.
 */
void mmc_free_host(struct mmc_host *host)
{
Russell King's avatar
Russell King committed
662
	mmc_free_host_sysfs(host);
Linus Torvalds's avatar
Linus Torvalds committed
663
664
665
666
667
668
669
670
671
672
673
}

EXPORT_SYMBOL(mmc_free_host);

#ifdef CONFIG_PM

/**
 *	mmc_suspend_host - suspend a host
 *	@host: mmc host
 *	@state: suspend mode (PM_SUSPEND_xxx)
 */
674
int mmc_suspend_host(struct mmc_host *host, pm_message_t state)
Linus Torvalds's avatar
Linus Torvalds committed
675
{
Pierre Ossman's avatar
Pierre Ossman committed
676
677
	mmc_flush_scheduled_work();

Pierre Ossman's avatar
Pierre Ossman committed
678
679
	mmc_bus_get(host);
	if (host->bus_ops && !host->bus_dead) {
680
681
682
683
684
685
686
687
688
689
		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
690
	}
Pierre Ossman's avatar
Pierre Ossman committed
691
692
	mmc_bus_put(host);

Linus Torvalds's avatar
Linus Torvalds committed
693
694
695
696
697
698
699
700
701
702
703
704
705
	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)
{
706
707
708
709
710
711
712
713
714
715
716
717
718
	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
719
720
721
722
723
724
725
726
727

	return 0;
}

EXPORT_SYMBOL(mmc_resume_host);

#endif

MODULE_LICENSE("GPL");