Commit | Line | Data |
---|---|---|
da7fbe58 | 1 | /* |
70f10482 | 2 | * linux/drivers/mmc/core/sd_ops.h |
da7fbe58 PO |
3 | * |
4 | * Copyright 2006-2007 Pierre Ossman | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or (at | |
9 | * your option) any later version. | |
10 | */ | |
11 | ||
12 | #include <linux/types.h> | |
da7fbe58 PO |
13 | #include <linux/scatterlist.h> |
14 | ||
15 | #include <linux/mmc/host.h> | |
16 | #include <linux/mmc/card.h> | |
17 | #include <linux/mmc/mmc.h> | |
18 | #include <linux/mmc/sd.h> | |
19 | ||
20 | #include "core.h" | |
21 | #include "sd_ops.h" | |
22 | ||
39361851 AB |
23 | static int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card) |
24 | { | |
25 | int err; | |
26 | struct mmc_command cmd; | |
27 | ||
28 | BUG_ON(!host); | |
29 | BUG_ON(card && (card->host != host)); | |
30 | ||
31 | cmd.opcode = MMC_APP_CMD; | |
32 | ||
33 | if (card) { | |
34 | cmd.arg = card->rca << 16; | |
af517150 | 35 | cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC; |
39361851 AB |
36 | } else { |
37 | cmd.arg = 0; | |
af517150 | 38 | cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_BCR; |
39361851 AB |
39 | } |
40 | ||
41 | err = mmc_wait_for_cmd(host, &cmd, 0); | |
17b0429d | 42 | if (err) |
39361851 AB |
43 | return err; |
44 | ||
45 | /* Check that card supported application commands */ | |
af517150 | 46 | if (!mmc_host_is_spi(host) && !(cmd.resp[0] & R1_APP_CMD)) |
17b0429d | 47 | return -EOPNOTSUPP; |
39361851 | 48 | |
17b0429d | 49 | return 0; |
39361851 AB |
50 | } |
51 | ||
da7fbe58 PO |
52 | /** |
53 | * mmc_wait_for_app_cmd - start an application command and wait for | |
54 | completion | |
55 | * @host: MMC host to start command | |
67a61c48 | 56 | * @card: Card to send MMC_APP_CMD to |
da7fbe58 PO |
57 | * @cmd: MMC command to start |
58 | * @retries: maximum number of retries | |
59 | * | |
60 | * Sends a MMC_APP_CMD, checks the card response, sends the command | |
61 | * in the parameter and waits for it to complete. Return any error | |
62 | * that occurred while the command was executing. Do not attempt to | |
63 | * parse the response. | |
64 | */ | |
65 | int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card, | |
66 | struct mmc_command *cmd, int retries) | |
67 | { | |
68 | struct mmc_request mrq; | |
69 | ||
70 | int i, err; | |
71 | ||
72 | BUG_ON(!cmd); | |
73 | BUG_ON(retries < 0); | |
74 | ||
17b0429d | 75 | err = -EIO; |
da7fbe58 PO |
76 | |
77 | /* | |
78 | * We have to resend MMC_APP_CMD for each attempt so | |
79 | * we cannot use the retries field in mmc_command. | |
80 | */ | |
81 | for (i = 0;i <= retries;i++) { | |
da7fbe58 | 82 | err = mmc_app_cmd(host, card); |
af517150 DB |
83 | if (err) { |
84 | /* no point in retrying; no APP commands allowed */ | |
85 | if (mmc_host_is_spi(host)) { | |
86 | if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND) | |
87 | break; | |
88 | } | |
da7fbe58 | 89 | continue; |
af517150 | 90 | } |
da7fbe58 PO |
91 | |
92 | memset(&mrq, 0, sizeof(struct mmc_request)); | |
93 | ||
94 | memset(cmd->resp, 0, sizeof(cmd->resp)); | |
95 | cmd->retries = 0; | |
96 | ||
97 | mrq.cmd = cmd; | |
98 | cmd->data = NULL; | |
99 | ||
100 | mmc_wait_for_req(host, &mrq); | |
101 | ||
102 | err = cmd->error; | |
17b0429d | 103 | if (!cmd->error) |
da7fbe58 | 104 | break; |
af517150 DB |
105 | |
106 | /* no point in retrying illegal APP commands */ | |
107 | if (mmc_host_is_spi(host)) { | |
108 | if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND) | |
109 | break; | |
110 | } | |
da7fbe58 PO |
111 | } |
112 | ||
113 | return err; | |
114 | } | |
115 | ||
116 | EXPORT_SYMBOL(mmc_wait_for_app_cmd); | |
117 | ||
da7fbe58 PO |
118 | int mmc_app_set_bus_width(struct mmc_card *card, int width) |
119 | { | |
120 | int err; | |
121 | struct mmc_command cmd; | |
122 | ||
123 | BUG_ON(!card); | |
124 | BUG_ON(!card->host); | |
125 | ||
126 | memset(&cmd, 0, sizeof(struct mmc_command)); | |
127 | ||
128 | cmd.opcode = SD_APP_SET_BUS_WIDTH; | |
129 | cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; | |
130 | ||
131 | switch (width) { | |
132 | case MMC_BUS_WIDTH_1: | |
133 | cmd.arg = SD_BUS_WIDTH_1; | |
134 | break; | |
135 | case MMC_BUS_WIDTH_4: | |
136 | cmd.arg = SD_BUS_WIDTH_4; | |
137 | break; | |
138 | default: | |
17b0429d | 139 | return -EINVAL; |
da7fbe58 PO |
140 | } |
141 | ||
142 | err = mmc_wait_for_app_cmd(card->host, card, &cmd, MMC_CMD_RETRIES); | |
17b0429d | 143 | if (err) |
da7fbe58 PO |
144 | return err; |
145 | ||
17b0429d | 146 | return 0; |
da7fbe58 PO |
147 | } |
148 | ||
149 | int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) | |
150 | { | |
151 | struct mmc_command cmd; | |
152 | int i, err = 0; | |
153 | ||
154 | BUG_ON(!host); | |
155 | ||
156 | memset(&cmd, 0, sizeof(struct mmc_command)); | |
157 | ||
158 | cmd.opcode = SD_APP_OP_COND; | |
af517150 DB |
159 | if (mmc_host_is_spi(host)) |
160 | cmd.arg = ocr & (1 << 30); /* SPI only defines one bit */ | |
161 | else | |
162 | cmd.arg = ocr; | |
163 | cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR; | |
da7fbe58 PO |
164 | |
165 | for (i = 100; i; i--) { | |
166 | err = mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES); | |
17b0429d | 167 | if (err) |
da7fbe58 PO |
168 | break; |
169 | ||
af517150 DB |
170 | /* if we're just probing, do a single pass */ |
171 | if (ocr == 0) | |
da7fbe58 PO |
172 | break; |
173 | ||
af517150 DB |
174 | /* otherwise wait until reset completes */ |
175 | if (mmc_host_is_spi(host)) { | |
176 | if (!(cmd.resp[0] & R1_SPI_IDLE)) | |
177 | break; | |
178 | } else { | |
179 | if (cmd.resp[0] & MMC_CARD_BUSY) | |
180 | break; | |
181 | } | |
182 | ||
17b0429d | 183 | err = -ETIMEDOUT; |
da7fbe58 PO |
184 | |
185 | mmc_delay(10); | |
186 | } | |
187 | ||
af517150 | 188 | if (rocr && !mmc_host_is_spi(host)) |
da7fbe58 PO |
189 | *rocr = cmd.resp[0]; |
190 | ||
191 | return err; | |
192 | } | |
193 | ||
194 | int mmc_send_if_cond(struct mmc_host *host, u32 ocr) | |
195 | { | |
196 | struct mmc_command cmd; | |
197 | int err; | |
198 | static const u8 test_pattern = 0xAA; | |
af517150 | 199 | u8 result_pattern; |
da7fbe58 PO |
200 | |
201 | /* | |
202 | * To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND | |
203 | * before SD_APP_OP_COND. This command will harmlessly fail for | |
204 | * SD 1.0 cards. | |
205 | */ | |
206 | cmd.opcode = SD_SEND_IF_COND; | |
207 | cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern; | |
af517150 | 208 | cmd.flags = MMC_RSP_SPI_R7 | MMC_RSP_R7 | MMC_CMD_BCR; |
da7fbe58 PO |
209 | |
210 | err = mmc_wait_for_cmd(host, &cmd, 0); | |
17b0429d | 211 | if (err) |
da7fbe58 PO |
212 | return err; |
213 | ||
af517150 DB |
214 | if (mmc_host_is_spi(host)) |
215 | result_pattern = cmd.resp[1] & 0xFF; | |
216 | else | |
217 | result_pattern = cmd.resp[0] & 0xFF; | |
218 | ||
219 | if (result_pattern != test_pattern) | |
17b0429d | 220 | return -EIO; |
da7fbe58 | 221 | |
17b0429d | 222 | return 0; |
da7fbe58 PO |
223 | } |
224 | ||
225 | int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca) | |
226 | { | |
227 | int err; | |
228 | struct mmc_command cmd; | |
229 | ||
230 | BUG_ON(!host); | |
231 | BUG_ON(!rca); | |
232 | ||
233 | memset(&cmd, 0, sizeof(struct mmc_command)); | |
234 | ||
235 | cmd.opcode = SD_SEND_RELATIVE_ADDR; | |
236 | cmd.arg = 0; | |
237 | cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR; | |
238 | ||
239 | err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); | |
17b0429d | 240 | if (err) |
da7fbe58 PO |
241 | return err; |
242 | ||
243 | *rca = cmd.resp[0] >> 16; | |
244 | ||
17b0429d | 245 | return 0; |
da7fbe58 PO |
246 | } |
247 | ||
248 | int mmc_app_send_scr(struct mmc_card *card, u32 *scr) | |
249 | { | |
250 | int err; | |
251 | struct mmc_request mrq; | |
252 | struct mmc_command cmd; | |
253 | struct mmc_data data; | |
254 | struct scatterlist sg; | |
255 | ||
256 | BUG_ON(!card); | |
257 | BUG_ON(!card->host); | |
258 | BUG_ON(!scr); | |
259 | ||
af517150 DB |
260 | /* NOTE: caller guarantees scr is heap-allocated */ |
261 | ||
da7fbe58 | 262 | err = mmc_app_cmd(card->host, card); |
17b0429d | 263 | if (err) |
da7fbe58 PO |
264 | return err; |
265 | ||
266 | memset(&mrq, 0, sizeof(struct mmc_request)); | |
267 | memset(&cmd, 0, sizeof(struct mmc_command)); | |
268 | memset(&data, 0, sizeof(struct mmc_data)); | |
269 | ||
270 | mrq.cmd = &cmd; | |
271 | mrq.data = &data; | |
272 | ||
273 | cmd.opcode = SD_APP_SEND_SCR; | |
274 | cmd.arg = 0; | |
af517150 | 275 | cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; |
da7fbe58 PO |
276 | |
277 | data.blksz = 8; | |
278 | data.blocks = 1; | |
279 | data.flags = MMC_DATA_READ; | |
280 | data.sg = &sg; | |
281 | data.sg_len = 1; | |
282 | ||
283 | sg_init_one(&sg, scr, 8); | |
284 | ||
b146d26a | 285 | mmc_set_data_timeout(&data, card); |
da7fbe58 PO |
286 | |
287 | mmc_wait_for_req(card->host, &mrq); | |
288 | ||
17b0429d | 289 | if (cmd.error) |
da7fbe58 | 290 | return cmd.error; |
17b0429d | 291 | if (data.error) |
da7fbe58 PO |
292 | return data.error; |
293 | ||
1fa8dd14 PO |
294 | scr[0] = be32_to_cpu(scr[0]); |
295 | scr[1] = be32_to_cpu(scr[1]); | |
da7fbe58 | 296 | |
17b0429d | 297 | return 0; |
da7fbe58 PO |
298 | } |
299 | ||
300 | int mmc_sd_switch(struct mmc_card *card, int mode, int group, | |
301 | u8 value, u8 *resp) | |
302 | { | |
303 | struct mmc_request mrq; | |
304 | struct mmc_command cmd; | |
305 | struct mmc_data data; | |
306 | struct scatterlist sg; | |
307 | ||
308 | BUG_ON(!card); | |
309 | BUG_ON(!card->host); | |
310 | ||
af517150 DB |
311 | /* NOTE: caller guarantees resp is heap-allocated */ |
312 | ||
da7fbe58 PO |
313 | mode = !!mode; |
314 | value &= 0xF; | |
315 | ||
316 | memset(&mrq, 0, sizeof(struct mmc_request)); | |
317 | memset(&cmd, 0, sizeof(struct mmc_command)); | |
318 | memset(&data, 0, sizeof(struct mmc_data)); | |
319 | ||
320 | mrq.cmd = &cmd; | |
321 | mrq.data = &data; | |
322 | ||
323 | cmd.opcode = SD_SWITCH; | |
324 | cmd.arg = mode << 31 | 0x00FFFFFF; | |
325 | cmd.arg &= ~(0xF << (group * 4)); | |
326 | cmd.arg |= value << (group * 4); | |
af517150 | 327 | cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; |
da7fbe58 PO |
328 | |
329 | data.blksz = 64; | |
330 | data.blocks = 1; | |
331 | data.flags = MMC_DATA_READ; | |
332 | data.sg = &sg; | |
333 | data.sg_len = 1; | |
334 | ||
335 | sg_init_one(&sg, resp, 64); | |
336 | ||
b146d26a | 337 | mmc_set_data_timeout(&data, card); |
da7fbe58 PO |
338 | |
339 | mmc_wait_for_req(card->host, &mrq); | |
340 | ||
17b0429d | 341 | if (cmd.error) |
da7fbe58 | 342 | return cmd.error; |
17b0429d | 343 | if (data.error) |
da7fbe58 PO |
344 | return data.error; |
345 | ||
17b0429d | 346 | return 0; |
da7fbe58 PO |
347 | } |
348 | ||
dfe86cba AH |
349 | int mmc_app_sd_status(struct mmc_card *card, void *ssr) |
350 | { | |
351 | int err; | |
352 | struct mmc_request mrq; | |
353 | struct mmc_command cmd; | |
354 | struct mmc_data data; | |
355 | struct scatterlist sg; | |
356 | ||
357 | BUG_ON(!card); | |
358 | BUG_ON(!card->host); | |
359 | BUG_ON(!ssr); | |
360 | ||
361 | /* NOTE: caller guarantees ssr is heap-allocated */ | |
362 | ||
363 | err = mmc_app_cmd(card->host, card); | |
364 | if (err) | |
365 | return err; | |
366 | ||
367 | memset(&mrq, 0, sizeof(struct mmc_request)); | |
368 | memset(&cmd, 0, sizeof(struct mmc_command)); | |
369 | memset(&data, 0, sizeof(struct mmc_data)); | |
370 | ||
371 | mrq.cmd = &cmd; | |
372 | mrq.data = &data; | |
373 | ||
374 | cmd.opcode = SD_APP_SD_STATUS; | |
375 | cmd.arg = 0; | |
376 | cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_ADTC; | |
377 | ||
378 | data.blksz = 64; | |
379 | data.blocks = 1; | |
380 | data.flags = MMC_DATA_READ; | |
381 | data.sg = &sg; | |
382 | data.sg_len = 1; | |
383 | ||
384 | sg_init_one(&sg, ssr, 64); | |
385 | ||
386 | mmc_set_data_timeout(&data, card); | |
387 | ||
388 | mmc_wait_for_req(card->host, &mrq); | |
389 | ||
390 | if (cmd.error) | |
391 | return cmd.error; | |
392 | if (data.error) | |
393 | return data.error; | |
394 | ||
395 | return 0; | |
396 | } |