Blame view

drivers/mmc/core/sd_ops.c 7.73 KB
da7fbe58d   Pierre Ossman   mmc: Separate out...
1
  /*
70f10482c   Pierre Ossman   mmc: update heade...
2
   *  linux/drivers/mmc/core/sd_ops.h
da7fbe58d   Pierre Ossman   mmc: Separate out...
3
4
5
6
7
8
9
10
   *
   *  Copyright 2006-2007 Pierre Ossman
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or (at
   * your option) any later version.
   */
4f665cb61   Yoshihiro Shimoda   mmc: fix mmc_app_...
11
  #include <linux/slab.h>
da7fbe58d   Pierre Ossman   mmc: Separate out...
12
  #include <linux/types.h>
3ef77af15   Paul Gortmaker   mmc: Add export.h...
13
  #include <linux/export.h>
da7fbe58d   Pierre Ossman   mmc: Separate out...
14
15
16
17
18
19
20
21
22
  #include <linux/scatterlist.h>
  
  #include <linux/mmc/host.h>
  #include <linux/mmc/card.h>
  #include <linux/mmc/mmc.h>
  #include <linux/mmc/sd.h>
  
  #include "core.h"
  #include "sd_ops.h"
cb87ea28e   John Calixto   mmc: core: Add mm...
23
  int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
393618510   Adrian Bunk   drivers/mmc/core/...
24
25
  {
  	int err;
1278dba16   Chris Ball   mmc: initialize s...
26
  	struct mmc_command cmd = {0};
393618510   Adrian Bunk   drivers/mmc/core/...
27
28
29
30
31
32
33
34
  
  	BUG_ON(!host);
  	BUG_ON(card && (card->host != host));
  
  	cmd.opcode = MMC_APP_CMD;
  
  	if (card) {
  		cmd.arg = card->rca << 16;
af5171507   David Brownell   MMC core learns a...
35
  		cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
393618510   Adrian Bunk   drivers/mmc/core/...
36
37
  	} else {
  		cmd.arg = 0;
af5171507   David Brownell   MMC core learns a...
38
  		cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_BCR;
393618510   Adrian Bunk   drivers/mmc/core/...
39
40
41
  	}
  
  	err = mmc_wait_for_cmd(host, &cmd, 0);
17b0429dd   Pierre Ossman   mmc: remove custo...
42
  	if (err)
393618510   Adrian Bunk   drivers/mmc/core/...
43
44
45
  		return err;
  
  	/* Check that card supported application commands */
af5171507   David Brownell   MMC core learns a...
46
  	if (!mmc_host_is_spi(host) && !(cmd.resp[0] & R1_APP_CMD))
17b0429dd   Pierre Ossman   mmc: remove custo...
47
  		return -EOPNOTSUPP;
393618510   Adrian Bunk   drivers/mmc/core/...
48

17b0429dd   Pierre Ossman   mmc: remove custo...
49
  	return 0;
393618510   Adrian Bunk   drivers/mmc/core/...
50
  }
cb87ea28e   John Calixto   mmc: core: Add mm...
51
  EXPORT_SYMBOL_GPL(mmc_app_cmd);
393618510   Adrian Bunk   drivers/mmc/core/...
52

da7fbe58d   Pierre Ossman   mmc: Separate out...
53
54
55
56
  /**
   *	mmc_wait_for_app_cmd - start an application command and wait for
   			       completion
   *	@host: MMC host to start command
67a61c484   Pierre Ossman   mmc: update kerne...
57
   *	@card: Card to send MMC_APP_CMD to
da7fbe58d   Pierre Ossman   mmc: Separate out...
58
59
60
61
62
63
64
65
66
67
68
   *	@cmd: MMC command to start
   *	@retries: maximum number of retries
   *
   *	Sends a MMC_APP_CMD, checks the card response, sends the command
   *	in the parameter and waits for it to complete. Return any error
   *	that occurred while the command was executing.  Do not attempt to
   *	parse the response.
   */
  int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card,
  	struct mmc_command *cmd, int retries)
  {
ad5fd9728   Venkatraman S   mmc: fix integer ...
69
  	struct mmc_request mrq = {NULL};
da7fbe58d   Pierre Ossman   mmc: Separate out...
70
71
72
73
74
  
  	int i, err;
  
  	BUG_ON(!cmd);
  	BUG_ON(retries < 0);
17b0429dd   Pierre Ossman   mmc: remove custo...
75
  	err = -EIO;
da7fbe58d   Pierre Ossman   mmc: Separate out...
76
77
78
79
80
81
  
  	/*
  	 * We have to resend MMC_APP_CMD for each attempt so
  	 * we cannot use the retries field in mmc_command.
  	 */
  	for (i = 0;i <= retries;i++) {
da7fbe58d   Pierre Ossman   mmc: Separate out...
82
  		err = mmc_app_cmd(host, card);
af5171507   David Brownell   MMC core learns a...
83
84
85
86
87
88
  		if (err) {
  			/* no point in retrying; no APP commands allowed */
  			if (mmc_host_is_spi(host)) {
  				if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
  					break;
  			}
da7fbe58d   Pierre Ossman   mmc: Separate out...
89
  			continue;
af5171507   David Brownell   MMC core learns a...
90
  		}
da7fbe58d   Pierre Ossman   mmc: Separate out...
91
92
93
94
95
96
97
98
99
100
101
102
  
  		memset(&mrq, 0, sizeof(struct mmc_request));
  
  		memset(cmd->resp, 0, sizeof(cmd->resp));
  		cmd->retries = 0;
  
  		mrq.cmd = cmd;
  		cmd->data = NULL;
  
  		mmc_wait_for_req(host, &mrq);
  
  		err = cmd->error;
17b0429dd   Pierre Ossman   mmc: remove custo...
103
  		if (!cmd->error)
da7fbe58d   Pierre Ossman   mmc: Separate out...
104
  			break;
af5171507   David Brownell   MMC core learns a...
105
106
107
108
109
110
  
  		/* no point in retrying illegal APP commands */
  		if (mmc_host_is_spi(host)) {
  			if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
  				break;
  		}
da7fbe58d   Pierre Ossman   mmc: Separate out...
111
112
113
114
115
116
  	}
  
  	return err;
  }
  
  EXPORT_SYMBOL(mmc_wait_for_app_cmd);
da7fbe58d   Pierre Ossman   mmc: Separate out...
117
118
  int mmc_app_set_bus_width(struct mmc_card *card, int width)
  {
1278dba16   Chris Ball   mmc: initialize s...
119
  	struct mmc_command cmd = {0};
da7fbe58d   Pierre Ossman   mmc: Separate out...
120
121
122
  
  	BUG_ON(!card);
  	BUG_ON(!card->host);
da7fbe58d   Pierre Ossman   mmc: Separate out...
123
124
125
126
127
128
129
130
131
132
133
  	cmd.opcode = SD_APP_SET_BUS_WIDTH;
  	cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
  
  	switch (width) {
  	case MMC_BUS_WIDTH_1:
  		cmd.arg = SD_BUS_WIDTH_1;
  		break;
  	case MMC_BUS_WIDTH_4:
  		cmd.arg = SD_BUS_WIDTH_4;
  		break;
  	default:
17b0429dd   Pierre Ossman   mmc: remove custo...
134
  		return -EINVAL;
da7fbe58d   Pierre Ossman   mmc: Separate out...
135
  	}
0899e7419   Masahiro Yamada   mmc: remove unnec...
136
  	return mmc_wait_for_app_cmd(card->host, card, &cmd, MMC_CMD_RETRIES);
da7fbe58d   Pierre Ossman   mmc: Separate out...
137
138
139
140
  }
  
  int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
  {
1278dba16   Chris Ball   mmc: initialize s...
141
  	struct mmc_command cmd = {0};
da7fbe58d   Pierre Ossman   mmc: Separate out...
142
143
144
  	int i, err = 0;
  
  	BUG_ON(!host);
da7fbe58d   Pierre Ossman   mmc: Separate out...
145
  	cmd.opcode = SD_APP_OP_COND;
af5171507   David Brownell   MMC core learns a...
146
147
148
149
150
  	if (mmc_host_is_spi(host))
  		cmd.arg = ocr & (1 << 30); /* SPI only defines one bit */
  	else
  		cmd.arg = ocr;
  	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;
da7fbe58d   Pierre Ossman   mmc: Separate out...
151
152
153
  
  	for (i = 100; i; i--) {
  		err = mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES);
17b0429dd   Pierre Ossman   mmc: remove custo...
154
  		if (err)
da7fbe58d   Pierre Ossman   mmc: Separate out...
155
  			break;
af5171507   David Brownell   MMC core learns a...
156
157
  		/* if we're just probing, do a single pass */
  		if (ocr == 0)
da7fbe58d   Pierre Ossman   mmc: Separate out...
158
  			break;
af5171507   David Brownell   MMC core learns a...
159
160
161
162
163
164
165
166
  		/* otherwise wait until reset completes */
  		if (mmc_host_is_spi(host)) {
  			if (!(cmd.resp[0] & R1_SPI_IDLE))
  				break;
  		} else {
  			if (cmd.resp[0] & MMC_CARD_BUSY)
  				break;
  		}
17b0429dd   Pierre Ossman   mmc: remove custo...
167
  		err = -ETIMEDOUT;
da7fbe58d   Pierre Ossman   mmc: Separate out...
168
169
170
  
  		mmc_delay(10);
  	}
5e863662a   Johan Rudholm   mmc: sd: warn if ...
171
172
173
  	if (!i)
  		pr_err("%s: card never left busy state
  ", mmc_hostname(host));
af5171507   David Brownell   MMC core learns a...
174
  	if (rocr && !mmc_host_is_spi(host))
da7fbe58d   Pierre Ossman   mmc: Separate out...
175
176
177
178
179
180
181
  		*rocr = cmd.resp[0];
  
  	return err;
  }
  
  int mmc_send_if_cond(struct mmc_host *host, u32 ocr)
  {
1278dba16   Chris Ball   mmc: initialize s...
182
  	struct mmc_command cmd = {0};
da7fbe58d   Pierre Ossman   mmc: Separate out...
183
184
  	int err;
  	static const u8 test_pattern = 0xAA;
af5171507   David Brownell   MMC core learns a...
185
  	u8 result_pattern;
da7fbe58d   Pierre Ossman   mmc: Separate out...
186
187
188
189
190
191
192
193
  
  	/*
  	 * To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND
  	 * before SD_APP_OP_COND. This command will harmlessly fail for
  	 * SD 1.0 cards.
  	 */
  	cmd.opcode = SD_SEND_IF_COND;
  	cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern;
af5171507   David Brownell   MMC core learns a...
194
  	cmd.flags = MMC_RSP_SPI_R7 | MMC_RSP_R7 | MMC_CMD_BCR;
da7fbe58d   Pierre Ossman   mmc: Separate out...
195
196
  
  	err = mmc_wait_for_cmd(host, &cmd, 0);
17b0429dd   Pierre Ossman   mmc: remove custo...
197
  	if (err)
da7fbe58d   Pierre Ossman   mmc: Separate out...
198
  		return err;
af5171507   David Brownell   MMC core learns a...
199
200
201
202
203
204
  	if (mmc_host_is_spi(host))
  		result_pattern = cmd.resp[1] & 0xFF;
  	else
  		result_pattern = cmd.resp[0] & 0xFF;
  
  	if (result_pattern != test_pattern)
17b0429dd   Pierre Ossman   mmc: remove custo...
205
  		return -EIO;
da7fbe58d   Pierre Ossman   mmc: Separate out...
206

17b0429dd   Pierre Ossman   mmc: remove custo...
207
  	return 0;
da7fbe58d   Pierre Ossman   mmc: Separate out...
208
209
210
211
212
  }
  
  int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca)
  {
  	int err;
1278dba16   Chris Ball   mmc: initialize s...
213
  	struct mmc_command cmd = {0};
da7fbe58d   Pierre Ossman   mmc: Separate out...
214
215
216
  
  	BUG_ON(!host);
  	BUG_ON(!rca);
da7fbe58d   Pierre Ossman   mmc: Separate out...
217
218
219
220
221
  	cmd.opcode = SD_SEND_RELATIVE_ADDR;
  	cmd.arg = 0;
  	cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR;
  
  	err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
17b0429dd   Pierre Ossman   mmc: remove custo...
222
  	if (err)
da7fbe58d   Pierre Ossman   mmc: Separate out...
223
224
225
  		return err;
  
  	*rca = cmd.resp[0] >> 16;
17b0429dd   Pierre Ossman   mmc: remove custo...
226
  	return 0;
da7fbe58d   Pierre Ossman   mmc: Separate out...
227
228
229
230
231
  }
  
  int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
  {
  	int err;
ad5fd9728   Venkatraman S   mmc: fix integer ...
232
  	struct mmc_request mrq = {NULL};
1278dba16   Chris Ball   mmc: initialize s...
233
  	struct mmc_command cmd = {0};
a61ad2b49   Chris Ball   mmc: initialize s...
234
  	struct mmc_data data = {0};
da7fbe58d   Pierre Ossman   mmc: Separate out...
235
  	struct scatterlist sg;
4f665cb61   Yoshihiro Shimoda   mmc: fix mmc_app_...
236
  	void *data_buf;
da7fbe58d   Pierre Ossman   mmc: Separate out...
237
238
239
240
  
  	BUG_ON(!card);
  	BUG_ON(!card->host);
  	BUG_ON(!scr);
af5171507   David Brownell   MMC core learns a...
241
  	/* NOTE: caller guarantees scr is heap-allocated */
da7fbe58d   Pierre Ossman   mmc: Separate out...
242
  	err = mmc_app_cmd(card->host, card);
17b0429dd   Pierre Ossman   mmc: remove custo...
243
  	if (err)
da7fbe58d   Pierre Ossman   mmc: Separate out...
244
  		return err;
4f665cb61   Yoshihiro Shimoda   mmc: fix mmc_app_...
245
246
247
248
249
250
  	/* dma onto stack is unsafe/nonportable, but callers to this
  	 * routine normally provide temporary on-stack buffers ...
  	 */
  	data_buf = kmalloc(sizeof(card->raw_scr), GFP_KERNEL);
  	if (data_buf == NULL)
  		return -ENOMEM;
da7fbe58d   Pierre Ossman   mmc: Separate out...
251
252
253
254
255
  	mrq.cmd = &cmd;
  	mrq.data = &data;
  
  	cmd.opcode = SD_APP_SEND_SCR;
  	cmd.arg = 0;
af5171507   David Brownell   MMC core learns a...
256
  	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
da7fbe58d   Pierre Ossman   mmc: Separate out...
257
258
259
260
261
262
  
  	data.blksz = 8;
  	data.blocks = 1;
  	data.flags = MMC_DATA_READ;
  	data.sg = &sg;
  	data.sg_len = 1;
4f665cb61   Yoshihiro Shimoda   mmc: fix mmc_app_...
263
  	sg_init_one(&sg, data_buf, 8);
da7fbe58d   Pierre Ossman   mmc: Separate out...
264

b146d26a6   Pierre Ossman   mmc: mmc_set_data...
265
  	mmc_set_data_timeout(&data, card);
da7fbe58d   Pierre Ossman   mmc: Separate out...
266
267
  
  	mmc_wait_for_req(card->host, &mrq);
4f665cb61   Yoshihiro Shimoda   mmc: fix mmc_app_...
268
269
  	memcpy(scr, data_buf, sizeof(card->raw_scr));
  	kfree(data_buf);
17b0429dd   Pierre Ossman   mmc: remove custo...
270
  	if (cmd.error)
da7fbe58d   Pierre Ossman   mmc: Separate out...
271
  		return cmd.error;
17b0429dd   Pierre Ossman   mmc: remove custo...
272
  	if (data.error)
da7fbe58d   Pierre Ossman   mmc: Separate out...
273
  		return data.error;
1fa8dd146   Pierre Ossman   mmc: use common b...
274
275
  	scr[0] = be32_to_cpu(scr[0]);
  	scr[1] = be32_to_cpu(scr[1]);
da7fbe58d   Pierre Ossman   mmc: Separate out...
276

17b0429dd   Pierre Ossman   mmc: remove custo...
277
  	return 0;
da7fbe58d   Pierre Ossman   mmc: Separate out...
278
279
280
281
282
  }
  
  int mmc_sd_switch(struct mmc_card *card, int mode, int group,
  	u8 value, u8 *resp)
  {
ad5fd9728   Venkatraman S   mmc: fix integer ...
283
  	struct mmc_request mrq = {NULL};
1278dba16   Chris Ball   mmc: initialize s...
284
  	struct mmc_command cmd = {0};
a61ad2b49   Chris Ball   mmc: initialize s...
285
  	struct mmc_data data = {0};
da7fbe58d   Pierre Ossman   mmc: Separate out...
286
287
288
289
  	struct scatterlist sg;
  
  	BUG_ON(!card);
  	BUG_ON(!card->host);
af5171507   David Brownell   MMC core learns a...
290
  	/* NOTE: caller guarantees resp is heap-allocated */
da7fbe58d   Pierre Ossman   mmc: Separate out...
291
292
  	mode = !!mode;
  	value &= 0xF;
da7fbe58d   Pierre Ossman   mmc: Separate out...
293
294
295
296
297
298
299
  	mrq.cmd = &cmd;
  	mrq.data = &data;
  
  	cmd.opcode = SD_SWITCH;
  	cmd.arg = mode << 31 | 0x00FFFFFF;
  	cmd.arg &= ~(0xF << (group * 4));
  	cmd.arg |= value << (group * 4);
af5171507   David Brownell   MMC core learns a...
300
  	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
da7fbe58d   Pierre Ossman   mmc: Separate out...
301
302
303
304
305
306
307
308
  
  	data.blksz = 64;
  	data.blocks = 1;
  	data.flags = MMC_DATA_READ;
  	data.sg = &sg;
  	data.sg_len = 1;
  
  	sg_init_one(&sg, resp, 64);
b146d26a6   Pierre Ossman   mmc: mmc_set_data...
309
  	mmc_set_data_timeout(&data, card);
da7fbe58d   Pierre Ossman   mmc: Separate out...
310
311
  
  	mmc_wait_for_req(card->host, &mrq);
17b0429dd   Pierre Ossman   mmc: remove custo...
312
  	if (cmd.error)
da7fbe58d   Pierre Ossman   mmc: Separate out...
313
  		return cmd.error;
17b0429dd   Pierre Ossman   mmc: remove custo...
314
  	if (data.error)
da7fbe58d   Pierre Ossman   mmc: Separate out...
315
  		return data.error;
17b0429dd   Pierre Ossman   mmc: remove custo...
316
  	return 0;
da7fbe58d   Pierre Ossman   mmc: Separate out...
317
  }
dfe86cba7   Adrian Hunter   mmc: add erase, s...
318
319
320
  int mmc_app_sd_status(struct mmc_card *card, void *ssr)
  {
  	int err;
ad5fd9728   Venkatraman S   mmc: fix integer ...
321
  	struct mmc_request mrq = {NULL};
1278dba16   Chris Ball   mmc: initialize s...
322
  	struct mmc_command cmd = {0};
a61ad2b49   Chris Ball   mmc: initialize s...
323
  	struct mmc_data data = {0};
dfe86cba7   Adrian Hunter   mmc: add erase, s...
324
325
326
327
328
329
330
331
332
333
334
  	struct scatterlist sg;
  
  	BUG_ON(!card);
  	BUG_ON(!card->host);
  	BUG_ON(!ssr);
  
  	/* NOTE: caller guarantees ssr is heap-allocated */
  
  	err = mmc_app_cmd(card->host, card);
  	if (err)
  		return err;
dfe86cba7   Adrian Hunter   mmc: add erase, s...
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
  	mrq.cmd = &cmd;
  	mrq.data = &data;
  
  	cmd.opcode = SD_APP_SD_STATUS;
  	cmd.arg = 0;
  	cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_ADTC;
  
  	data.blksz = 64;
  	data.blocks = 1;
  	data.flags = MMC_DATA_READ;
  	data.sg = &sg;
  	data.sg_len = 1;
  
  	sg_init_one(&sg, ssr, 64);
  
  	mmc_set_data_timeout(&data, card);
  
  	mmc_wait_for_req(card->host, &mrq);
  
  	if (cmd.error)
  		return cmd.error;
  	if (data.error)
  		return data.error;
  
  	return 0;
  }