diff options
Diffstat (limited to 'drivers/mmc/core/sd_ops.c')
-rw-r--r-- | drivers/mmc/core/sd_ops.c | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c new file mode 100644 index 000000000000..9697ce581101 --- /dev/null +++ b/drivers/mmc/core/sd_ops.c | |||
@@ -0,0 +1,316 @@ | |||
1 | /* | ||
2 | * linux/drivers/mmc/sd_ops.h | ||
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> | ||
13 | #include <asm/scatterlist.h> | ||
14 | #include <linux/scatterlist.h> | ||
15 | |||
16 | #include <linux/mmc/host.h> | ||
17 | #include <linux/mmc/card.h> | ||
18 | #include <linux/mmc/mmc.h> | ||
19 | #include <linux/mmc/sd.h> | ||
20 | |||
21 | #include "core.h" | ||
22 | #include "sd_ops.h" | ||
23 | |||
24 | /** | ||
25 | * mmc_wait_for_app_cmd - start an application command and wait for | ||
26 | completion | ||
27 | * @host: MMC host to start command | ||
28 | * @rca: RCA to send MMC_APP_CMD to | ||
29 | * @cmd: MMC command to start | ||
30 | * @retries: maximum number of retries | ||
31 | * | ||
32 | * Sends a MMC_APP_CMD, checks the card response, sends the command | ||
33 | * in the parameter and waits for it to complete. Return any error | ||
34 | * that occurred while the command was executing. Do not attempt to | ||
35 | * parse the response. | ||
36 | */ | ||
37 | int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card, | ||
38 | struct mmc_command *cmd, int retries) | ||
39 | { | ||
40 | struct mmc_request mrq; | ||
41 | |||
42 | int i, err; | ||
43 | |||
44 | BUG_ON(!cmd); | ||
45 | BUG_ON(retries < 0); | ||
46 | |||
47 | err = MMC_ERR_INVALID; | ||
48 | |||
49 | /* | ||
50 | * We have to resend MMC_APP_CMD for each attempt so | ||
51 | * we cannot use the retries field in mmc_command. | ||
52 | */ | ||
53 | for (i = 0;i <= retries;i++) { | ||
54 | memset(&mrq, 0, sizeof(struct mmc_request)); | ||
55 | |||
56 | err = mmc_app_cmd(host, card); | ||
57 | if (err != MMC_ERR_NONE) | ||
58 | continue; | ||
59 | |||
60 | memset(&mrq, 0, sizeof(struct mmc_request)); | ||
61 | |||
62 | memset(cmd->resp, 0, sizeof(cmd->resp)); | ||
63 | cmd->retries = 0; | ||
64 | |||
65 | mrq.cmd = cmd; | ||
66 | cmd->data = NULL; | ||
67 | |||
68 | mmc_wait_for_req(host, &mrq); | ||
69 | |||
70 | err = cmd->error; | ||
71 | if (cmd->error == MMC_ERR_NONE) | ||
72 | break; | ||
73 | } | ||
74 | |||
75 | return err; | ||
76 | } | ||
77 | |||
78 | EXPORT_SYMBOL(mmc_wait_for_app_cmd); | ||
79 | |||
80 | int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card) | ||
81 | { | ||
82 | int err; | ||
83 | struct mmc_command cmd; | ||
84 | |||
85 | BUG_ON(!host); | ||
86 | BUG_ON(card && (card->host != host)); | ||
87 | |||
88 | cmd.opcode = MMC_APP_CMD; | ||
89 | |||
90 | if (card) { | ||
91 | cmd.arg = card->rca << 16; | ||
92 | cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; | ||
93 | } else { | ||
94 | cmd.arg = 0; | ||
95 | cmd.flags = MMC_RSP_R1 | MMC_CMD_BCR; | ||
96 | } | ||
97 | |||
98 | err = mmc_wait_for_cmd(host, &cmd, 0); | ||
99 | if (err != MMC_ERR_NONE) | ||
100 | return err; | ||
101 | |||
102 | /* Check that card supported application commands */ | ||
103 | if (!(cmd.resp[0] & R1_APP_CMD)) | ||
104 | return MMC_ERR_FAILED; | ||
105 | |||
106 | return MMC_ERR_NONE; | ||
107 | } | ||
108 | |||
109 | int mmc_app_set_bus_width(struct mmc_card *card, int width) | ||
110 | { | ||
111 | int err; | ||
112 | struct mmc_command cmd; | ||
113 | |||
114 | BUG_ON(!card); | ||
115 | BUG_ON(!card->host); | ||
116 | |||
117 | memset(&cmd, 0, sizeof(struct mmc_command)); | ||
118 | |||
119 | cmd.opcode = SD_APP_SET_BUS_WIDTH; | ||
120 | cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; | ||
121 | |||
122 | switch (width) { | ||
123 | case MMC_BUS_WIDTH_1: | ||
124 | cmd.arg = SD_BUS_WIDTH_1; | ||
125 | break; | ||
126 | case MMC_BUS_WIDTH_4: | ||
127 | cmd.arg = SD_BUS_WIDTH_4; | ||
128 | break; | ||
129 | default: | ||
130 | return MMC_ERR_INVALID; | ||
131 | } | ||
132 | |||
133 | err = mmc_wait_for_app_cmd(card->host, card, &cmd, MMC_CMD_RETRIES); | ||
134 | if (err != MMC_ERR_NONE) | ||
135 | return err; | ||
136 | |||
137 | return MMC_ERR_NONE; | ||
138 | } | ||
139 | |||
140 | int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) | ||
141 | { | ||
142 | struct mmc_command cmd; | ||
143 | int i, err = 0; | ||
144 | |||
145 | BUG_ON(!host); | ||
146 | |||
147 | memset(&cmd, 0, sizeof(struct mmc_command)); | ||
148 | |||
149 | cmd.opcode = SD_APP_OP_COND; | ||
150 | cmd.arg = ocr; | ||
151 | cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR; | ||
152 | |||
153 | for (i = 100; i; i--) { | ||
154 | err = mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES); | ||
155 | if (err != MMC_ERR_NONE) | ||
156 | break; | ||
157 | |||
158 | if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0) | ||
159 | break; | ||
160 | |||
161 | err = MMC_ERR_TIMEOUT; | ||
162 | |||
163 | mmc_delay(10); | ||
164 | } | ||
165 | |||
166 | if (rocr) | ||
167 | *rocr = cmd.resp[0]; | ||
168 | |||
169 | return err; | ||
170 | } | ||
171 | |||
172 | int mmc_send_if_cond(struct mmc_host *host, u32 ocr) | ||
173 | { | ||
174 | struct mmc_command cmd; | ||
175 | int err; | ||
176 | static const u8 test_pattern = 0xAA; | ||
177 | |||
178 | /* | ||
179 | * To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND | ||
180 | * before SD_APP_OP_COND. This command will harmlessly fail for | ||
181 | * SD 1.0 cards. | ||
182 | */ | ||
183 | cmd.opcode = SD_SEND_IF_COND; | ||
184 | cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern; | ||
185 | cmd.flags = MMC_RSP_R7 | MMC_CMD_BCR; | ||
186 | |||
187 | err = mmc_wait_for_cmd(host, &cmd, 0); | ||
188 | if (err != MMC_ERR_NONE) | ||
189 | return err; | ||
190 | |||
191 | if ((cmd.resp[0] & 0xFF) != test_pattern) | ||
192 | return MMC_ERR_FAILED; | ||
193 | |||
194 | return MMC_ERR_NONE; | ||
195 | } | ||
196 | |||
197 | int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca) | ||
198 | { | ||
199 | int err; | ||
200 | struct mmc_command cmd; | ||
201 | |||
202 | BUG_ON(!host); | ||
203 | BUG_ON(!rca); | ||
204 | |||
205 | memset(&cmd, 0, sizeof(struct mmc_command)); | ||
206 | |||
207 | cmd.opcode = SD_SEND_RELATIVE_ADDR; | ||
208 | cmd.arg = 0; | ||
209 | cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR; | ||
210 | |||
211 | err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); | ||
212 | if (err != MMC_ERR_NONE) | ||
213 | return err; | ||
214 | |||
215 | *rca = cmd.resp[0] >> 16; | ||
216 | |||
217 | return MMC_ERR_NONE; | ||
218 | } | ||
219 | |||
220 | int mmc_app_send_scr(struct mmc_card *card, u32 *scr) | ||
221 | { | ||
222 | int err; | ||
223 | struct mmc_request mrq; | ||
224 | struct mmc_command cmd; | ||
225 | struct mmc_data data; | ||
226 | struct scatterlist sg; | ||
227 | |||
228 | BUG_ON(!card); | ||
229 | BUG_ON(!card->host); | ||
230 | BUG_ON(!scr); | ||
231 | |||
232 | err = mmc_app_cmd(card->host, card); | ||
233 | if (err != MMC_ERR_NONE) | ||
234 | return err; | ||
235 | |||
236 | memset(&mrq, 0, sizeof(struct mmc_request)); | ||
237 | memset(&cmd, 0, sizeof(struct mmc_command)); | ||
238 | memset(&data, 0, sizeof(struct mmc_data)); | ||
239 | |||
240 | mrq.cmd = &cmd; | ||
241 | mrq.data = &data; | ||
242 | |||
243 | cmd.opcode = SD_APP_SEND_SCR; | ||
244 | cmd.arg = 0; | ||
245 | cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; | ||
246 | |||
247 | data.blksz = 8; | ||
248 | data.blocks = 1; | ||
249 | data.flags = MMC_DATA_READ; | ||
250 | data.sg = &sg; | ||
251 | data.sg_len = 1; | ||
252 | |||
253 | sg_init_one(&sg, scr, 8); | ||
254 | |||
255 | mmc_set_data_timeout(&data, card, 0); | ||
256 | |||
257 | mmc_wait_for_req(card->host, &mrq); | ||
258 | |||
259 | if (cmd.error != MMC_ERR_NONE) | ||
260 | return cmd.error; | ||
261 | if (data.error != MMC_ERR_NONE) | ||
262 | return data.error; | ||
263 | |||
264 | scr[0] = ntohl(scr[0]); | ||
265 | scr[1] = ntohl(scr[1]); | ||
266 | |||
267 | return MMC_ERR_NONE; | ||
268 | } | ||
269 | |||
270 | int mmc_sd_switch(struct mmc_card *card, int mode, int group, | ||
271 | u8 value, u8 *resp) | ||
272 | { | ||
273 | struct mmc_request mrq; | ||
274 | struct mmc_command cmd; | ||
275 | struct mmc_data data; | ||
276 | struct scatterlist sg; | ||
277 | |||
278 | BUG_ON(!card); | ||
279 | BUG_ON(!card->host); | ||
280 | |||
281 | mode = !!mode; | ||
282 | value &= 0xF; | ||
283 | |||
284 | memset(&mrq, 0, sizeof(struct mmc_request)); | ||
285 | memset(&cmd, 0, sizeof(struct mmc_command)); | ||
286 | memset(&data, 0, sizeof(struct mmc_data)); | ||
287 | |||
288 | mrq.cmd = &cmd; | ||
289 | mrq.data = &data; | ||
290 | |||
291 | cmd.opcode = SD_SWITCH; | ||
292 | cmd.arg = mode << 31 | 0x00FFFFFF; | ||
293 | cmd.arg &= ~(0xF << (group * 4)); | ||
294 | cmd.arg |= value << (group * 4); | ||
295 | cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; | ||
296 | |||
297 | data.blksz = 64; | ||
298 | data.blocks = 1; | ||
299 | data.flags = MMC_DATA_READ; | ||
300 | data.sg = &sg; | ||
301 | data.sg_len = 1; | ||
302 | |||
303 | sg_init_one(&sg, resp, 64); | ||
304 | |||
305 | mmc_set_data_timeout(&data, card, 0); | ||
306 | |||
307 | mmc_wait_for_req(card->host, &mrq); | ||
308 | |||
309 | if (cmd.error != MMC_ERR_NONE) | ||
310 | return cmd.error; | ||
311 | if (data.error != MMC_ERR_NONE) | ||
312 | return data.error; | ||
313 | |||
314 | return MMC_ERR_NONE; | ||
315 | } | ||
316 | |||