]>
Commit | Line | Data |
---|---|---|
71f95118 WD |
1 | /* |
2 | * (C) Copyright 2003 | |
3 | * Kyle Harris, Nexus Technologies, Inc. [email protected] | |
4 | * | |
5 | * See file CREDITS for list of people who contributed to this | |
6 | * project. | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU General Public License as | |
10 | * published by the Free Software Foundation; either version 2 of | |
11 | * the License, or (at your option) any later version. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program; if not, write to the Free Software | |
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
21 | * MA 02111-1307 USA | |
22 | */ | |
23 | ||
24 | #include <config.h> | |
25 | #include <common.h> | |
26 | #include <mmc.h> | |
27 | #include <asm/errno.h> | |
28 | #include <asm/arch/hardware.h> | |
7205e407 | 29 | #include <part.h> |
71f95118 WD |
30 | |
31 | #ifdef CONFIG_MMC | |
32 | ||
c95219fa | 33 | extern int fat_register_device(block_dev_desc_t * dev_desc, int part_no); |
7205e407 WD |
34 | |
35 | static block_dev_desc_t mmc_dev; | |
36 | ||
c95219fa | 37 | block_dev_desc_t *mmc_get_dev(int dev) |
7205e407 | 38 | { |
c95219fa | 39 | return ((block_dev_desc_t *) & mmc_dev); |
7205e407 | 40 | } |
71f95118 | 41 | |
8bde7f77 | 42 | /* |
71f95118 WD |
43 | * FIXME needs to read cid and csd info to determine block size |
44 | * and other parameters | |
45 | */ | |
46 | static uchar mmc_buf[MMC_BLOCK_SIZE]; | |
c95219fa | 47 | static uchar spec_ver; |
71f95118 | 48 | static int mmc_ready = 0; |
c95219fa | 49 | static int wide = 0; |
71f95118 | 50 | |
c95219fa | 51 | static uint32_t * |
71f95118 WD |
52 | /****************************************************/ |
53 | mmc_cmd(ushort cmd, ushort argh, ushort argl, ushort cmdat) | |
54 | /****************************************************/ | |
55 | { | |
c95219fa | 56 | static uint32_t resp[4], a, b, c; |
71f95118 | 57 | ulong status; |
c95219fa | 58 | int i; |
71f95118 | 59 | |
c95219fa SB |
60 | debug("mmc_cmd %u 0x%04x 0x%04x 0x%04x\n", cmd, argh, argl, |
61 | cmdat | wide); | |
71f95118 WD |
62 | MMC_STRPCL = MMC_STRPCL_STOP_CLK; |
63 | MMC_I_MASK = ~MMC_I_MASK_CLK_IS_OFF; | |
c95219fa SB |
64 | while (!(MMC_I_REG & MMC_I_REG_CLK_IS_OFF)) ; |
65 | MMC_CMD = cmd; | |
66 | MMC_ARGH = argh; | |
67 | MMC_ARGL = argl; | |
68 | MMC_CMDAT = cmdat | wide; | |
71f95118 WD |
69 | MMC_I_MASK = ~MMC_I_MASK_END_CMD_RES; |
70 | MMC_STRPCL = MMC_STRPCL_START_CLK; | |
c95219fa | 71 | while (!(MMC_I_REG & MMC_I_REG_END_CMD_RES)) ; |
71f95118 WD |
72 | |
73 | status = MMC_STAT; | |
c95219fa | 74 | debug("MMC status 0x%08x\n", status); |
20787e23 | 75 | if (status & MMC_STAT_TIME_OUT_RESPONSE) { |
71f95118 WD |
76 | return 0; |
77 | } | |
78 | ||
c95219fa SB |
79 | /* Linux says: |
80 | * Did I mention this is Sick. We always need to | |
81 | * discard the upper 8 bits of the first 16-bit word. | |
82 | */ | |
83 | a = (MMC_RES & 0xffff); | |
84 | for (i = 0; i < 4; i++) { | |
85 | b = (MMC_RES & 0xffff); | |
86 | c = (MMC_RES & 0xffff); | |
87 | resp[i] = (a << 24) | (b << 8) | (c >> 8); | |
88 | a = c; | |
89 | debug("MMC resp[%d] = %#08x\n", i, resp[i]); | |
71f95118 | 90 | } |
71f95118 | 91 | |
71f95118 WD |
92 | return resp; |
93 | } | |
94 | ||
95 | int | |
96 | /****************************************************/ | |
c95219fa | 97 | mmc_block_read(uchar * dst, ulong src, ulong len) |
71f95118 WD |
98 | /****************************************************/ |
99 | { | |
71f95118 WD |
100 | ushort argh, argl; |
101 | ulong status; | |
102 | ||
20787e23 | 103 | if (len == 0) { |
71f95118 WD |
104 | return 0; |
105 | } | |
106 | ||
c95219fa | 107 | debug("mmc_block_rd dst %lx src %lx len %d\n", (ulong) dst, src, len); |
71f95118 WD |
108 | |
109 | argh = len >> 16; | |
110 | argl = len & 0xffff; | |
111 | ||
112 | /* set block len */ | |
c95219fa | 113 | mmc_cmd(MMC_CMD_SET_BLOCKLEN, argh, argl, MMC_CMDAT_R1); |
71f95118 WD |
114 | |
115 | /* send read command */ | |
116 | argh = src >> 16; | |
117 | argl = src & 0xffff; | |
118 | MMC_STRPCL = MMC_STRPCL_STOP_CLK; | |
119 | MMC_RDTO = 0xffff; | |
120 | MMC_NOB = 1; | |
121 | MMC_BLKLEN = len; | |
341188b9 | 122 | mmc_cmd(MMC_CMD_READ_SINGLE_BLOCK, argh, argl, |
c95219fa SB |
123 | MMC_CMDAT_R1 | MMC_CMDAT_READ | MMC_CMDAT_BLOCK | |
124 | MMC_CMDAT_DATA_EN); | |
8bde7f77 | 125 | |
71f95118 | 126 | MMC_I_MASK = ~MMC_I_MASK_RXFIFO_RD_REQ; |
20787e23 WD |
127 | while (len) { |
128 | if (MMC_I_REG & MMC_I_REG_RXFIFO_RD_REQ) { | |
129 | #ifdef CONFIG_PXA27X | |
130 | int i; | |
c95219fa SB |
131 | for (i = min(len, 32); i; i--) { |
132 | *dst++ = *((volatile uchar *)&MMC_RXFIFO); | |
20787e23 WD |
133 | len--; |
134 | } | |
135 | #else | |
71f95118 WD |
136 | *dst++ = MMC_RXFIFO; |
137 | len--; | |
20787e23 | 138 | #endif |
71f95118 WD |
139 | } |
140 | status = MMC_STAT; | |
20787e23 | 141 | if (status & MMC_STAT_ERRORS) { |
71f95118 WD |
142 | printf("MMC_STAT error %lx\n", status); |
143 | return -1; | |
144 | } | |
145 | } | |
146 | MMC_I_MASK = ~MMC_I_MASK_DATA_TRAN_DONE; | |
c95219fa | 147 | while (!(MMC_I_REG & MMC_I_REG_DATA_TRAN_DONE)) ; |
71f95118 | 148 | status = MMC_STAT; |
20787e23 | 149 | if (status & MMC_STAT_ERRORS) { |
71f95118 WD |
150 | printf("MMC_STAT error %lx\n", status); |
151 | return -1; | |
152 | } | |
153 | return 0; | |
154 | } | |
155 | ||
156 | int | |
157 | /****************************************************/ | |
c95219fa | 158 | mmc_block_write(ulong dst, uchar * src, int len) |
71f95118 WD |
159 | /****************************************************/ |
160 | { | |
71f95118 WD |
161 | ushort argh, argl; |
162 | ulong status; | |
163 | ||
20787e23 | 164 | if (len == 0) { |
71f95118 WD |
165 | return 0; |
166 | } | |
167 | ||
c95219fa | 168 | debug("mmc_block_wr dst %lx src %lx len %d\n", dst, (ulong) src, len); |
71f95118 WD |
169 | |
170 | argh = len >> 16; | |
171 | argl = len & 0xffff; | |
172 | ||
173 | /* set block len */ | |
c95219fa | 174 | mmc_cmd(MMC_CMD_SET_BLOCKLEN, argh, argl, MMC_CMDAT_R1); |
71f95118 WD |
175 | |
176 | /* send write command */ | |
177 | argh = dst >> 16; | |
178 | argl = dst & 0xffff; | |
179 | MMC_STRPCL = MMC_STRPCL_STOP_CLK; | |
180 | MMC_NOB = 1; | |
181 | MMC_BLKLEN = len; | |
c95219fa SB |
182 | mmc_cmd(MMC_CMD_WRITE_BLOCK, argh, argl, |
183 | MMC_CMDAT_R1 | MMC_CMDAT_WRITE | MMC_CMDAT_BLOCK | | |
184 | MMC_CMDAT_DATA_EN); | |
8bde7f77 | 185 | |
71f95118 | 186 | MMC_I_MASK = ~MMC_I_MASK_TXFIFO_WR_REQ; |
20787e23 WD |
187 | while (len) { |
188 | if (MMC_I_REG & MMC_I_REG_TXFIFO_WR_REQ) { | |
c95219fa | 189 | int i, bytes = min(32, len); |
71f95118 | 190 | |
c95219fa | 191 | for (i = 0; i < bytes; i++) { |
71f95118 WD |
192 | MMC_TXFIFO = *src++; |
193 | } | |
20787e23 | 194 | if (bytes < 32) { |
71f95118 WD |
195 | MMC_PRTBUF = MMC_PRTBUF_BUF_PART_FULL; |
196 | } | |
197 | len -= bytes; | |
198 | } | |
199 | status = MMC_STAT; | |
20787e23 | 200 | if (status & MMC_STAT_ERRORS) { |
71f95118 WD |
201 | printf("MMC_STAT error %lx\n", status); |
202 | return -1; | |
203 | } | |
204 | } | |
205 | MMC_I_MASK = ~MMC_I_MASK_DATA_TRAN_DONE; | |
c95219fa | 206 | while (!(MMC_I_REG & MMC_I_REG_DATA_TRAN_DONE)) ; |
71f95118 | 207 | MMC_I_MASK = ~MMC_I_MASK_PRG_DONE; |
c95219fa | 208 | while (!(MMC_I_REG & MMC_I_REG_PRG_DONE)) ; |
71f95118 | 209 | status = MMC_STAT; |
20787e23 | 210 | if (status & MMC_STAT_ERRORS) { |
71f95118 WD |
211 | printf("MMC_STAT error %lx\n", status); |
212 | return -1; | |
213 | } | |
214 | return 0; | |
215 | } | |
216 | ||
71f95118 WD |
217 | int |
218 | /****************************************************/ | |
c95219fa | 219 | mmc_read(ulong src, uchar * dst, int size) |
71f95118 WD |
220 | /****************************************************/ |
221 | { | |
222 | ulong end, part_start, part_end, part_len, aligned_start, aligned_end; | |
223 | ulong mmc_block_size, mmc_block_address; | |
224 | ||
20787e23 | 225 | if (size == 0) { |
71f95118 WD |
226 | return 0; |
227 | } | |
228 | ||
20787e23 | 229 | if (!mmc_ready) { |
71f95118 WD |
230 | printf("Please initial the MMC first\n"); |
231 | return -1; | |
232 | } | |
233 | ||
234 | mmc_block_size = MMC_BLOCK_SIZE; | |
235 | mmc_block_address = ~(mmc_block_size - 1); | |
236 | ||
6d0f6bcf | 237 | src -= CONFIG_SYS_MMC_BASE; |
71f95118 WD |
238 | end = src + size; |
239 | part_start = ~mmc_block_address & src; | |
240 | part_end = ~mmc_block_address & end; | |
241 | aligned_start = mmc_block_address & src; | |
242 | aligned_end = mmc_block_address & end; | |
243 | ||
244 | /* all block aligned accesses */ | |
c95219fa SB |
245 | debug |
246 | ("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n", | |
247 | src, (ulong) dst, end, part_start, part_end, aligned_start, | |
248 | aligned_end); | |
20787e23 | 249 | if (part_start) { |
71f95118 | 250 | part_len = mmc_block_size - part_start; |
c95219fa SB |
251 | debug |
252 | ("ps src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n", | |
253 | src, (ulong) dst, end, part_start, part_end, aligned_start, | |
254 | aligned_end); | |
255 | if ((mmc_block_read(mmc_buf, aligned_start, mmc_block_size)) < | |
256 | 0) { | |
71f95118 WD |
257 | return -1; |
258 | } | |
c95219fa | 259 | memcpy(dst, mmc_buf + part_start, part_len); |
71f95118 WD |
260 | dst += part_len; |
261 | src += part_len; | |
262 | } | |
c95219fa SB |
263 | debug |
264 | ("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n", | |
265 | src, (ulong) dst, end, part_start, part_end, aligned_start, | |
266 | aligned_end); | |
20787e23 | 267 | for (; src < aligned_end; src += mmc_block_size, dst += mmc_block_size) { |
c95219fa SB |
268 | debug |
269 | ("al src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n", | |
270 | src, (ulong) dst, end, part_start, part_end, aligned_start, | |
271 | aligned_end); | |
272 | if ((mmc_block_read((uchar *) (dst), src, mmc_block_size)) < 0) { | |
71f95118 WD |
273 | return -1; |
274 | } | |
275 | } | |
c95219fa SB |
276 | debug |
277 | ("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n", | |
278 | src, (ulong) dst, end, part_start, part_end, aligned_start, | |
279 | aligned_end); | |
20787e23 | 280 | if (part_end && src < end) { |
c95219fa SB |
281 | debug |
282 | ("pe src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n", | |
283 | src, (ulong) dst, end, part_start, part_end, aligned_start, | |
284 | aligned_end); | |
20787e23 | 285 | if ((mmc_block_read(mmc_buf, aligned_end, mmc_block_size)) < 0) { |
71f95118 WD |
286 | return -1; |
287 | } | |
288 | memcpy(dst, mmc_buf, part_end); | |
289 | } | |
290 | return 0; | |
291 | } | |
292 | ||
293 | int | |
294 | /****************************************************/ | |
c95219fa | 295 | mmc_write(uchar * src, ulong dst, int size) |
71f95118 WD |
296 | /****************************************************/ |
297 | { | |
298 | ulong end, part_start, part_end, part_len, aligned_start, aligned_end; | |
299 | ulong mmc_block_size, mmc_block_address; | |
300 | ||
20787e23 | 301 | if (size == 0) { |
71f95118 WD |
302 | return 0; |
303 | } | |
304 | ||
20787e23 | 305 | if (!mmc_ready) { |
71f95118 WD |
306 | printf("Please initial the MMC first\n"); |
307 | return -1; | |
308 | } | |
309 | ||
310 | mmc_block_size = MMC_BLOCK_SIZE; | |
311 | mmc_block_address = ~(mmc_block_size - 1); | |
312 | ||
6d0f6bcf | 313 | dst -= CONFIG_SYS_MMC_BASE; |
71f95118 WD |
314 | end = dst + size; |
315 | part_start = ~mmc_block_address & dst; | |
316 | part_end = ~mmc_block_address & end; | |
317 | aligned_start = mmc_block_address & dst; | |
318 | aligned_end = mmc_block_address & end; | |
319 | ||
320 | /* all block aligned accesses */ | |
c95219fa SB |
321 | debug |
322 | ("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n", | |
323 | src, (ulong) dst, end, part_start, part_end, aligned_start, | |
324 | aligned_end); | |
20787e23 | 325 | if (part_start) { |
71f95118 | 326 | part_len = mmc_block_size - part_start; |
c95219fa SB |
327 | debug |
328 | ("ps src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n", | |
329 | (ulong) src, dst, end, part_start, part_end, aligned_start, | |
330 | aligned_end); | |
331 | if ((mmc_block_read(mmc_buf, aligned_start, mmc_block_size)) < | |
332 | 0) { | |
71f95118 WD |
333 | return -1; |
334 | } | |
c95219fa SB |
335 | memcpy(mmc_buf + part_start, src, part_len); |
336 | if ((mmc_block_write(aligned_start, mmc_buf, mmc_block_size)) < | |
337 | 0) { | |
71f95118 WD |
338 | return -1; |
339 | } | |
340 | dst += part_len; | |
341 | src += part_len; | |
342 | } | |
c95219fa SB |
343 | debug |
344 | ("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n", | |
345 | src, (ulong) dst, end, part_start, part_end, aligned_start, | |
346 | aligned_end); | |
20787e23 | 347 | for (; dst < aligned_end; src += mmc_block_size, dst += mmc_block_size) { |
c95219fa SB |
348 | debug |
349 | ("al src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n", | |
350 | src, (ulong) dst, end, part_start, part_end, aligned_start, | |
351 | aligned_end); | |
352 | if ((mmc_block_write(dst, (uchar *) src, mmc_block_size)) < 0) { | |
71f95118 WD |
353 | return -1; |
354 | } | |
355 | } | |
c95219fa SB |
356 | debug |
357 | ("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n", | |
358 | src, (ulong) dst, end, part_start, part_end, aligned_start, | |
359 | aligned_end); | |
20787e23 | 360 | if (part_end && dst < end) { |
c95219fa SB |
361 | debug |
362 | ("pe src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n", | |
363 | src, (ulong) dst, end, part_start, part_end, aligned_start, | |
364 | aligned_end); | |
20787e23 | 365 | if ((mmc_block_read(mmc_buf, aligned_end, mmc_block_size)) < 0) { |
71f95118 WD |
366 | return -1; |
367 | } | |
368 | memcpy(mmc_buf, src, part_end); | |
20787e23 | 369 | if ((mmc_block_write(aligned_end, mmc_buf, mmc_block_size)) < 0) { |
71f95118 WD |
370 | return -1; |
371 | } | |
372 | } | |
373 | return 0; | |
374 | } | |
375 | ||
7205e407 | 376 | ulong |
71f95118 | 377 | /****************************************************/ |
0937b8d8 | 378 | mmc_bread(int dev_num, ulong blknr, lbaint_t blkcnt, void *dst) |
71f95118 WD |
379 | /****************************************************/ |
380 | { | |
381 | int mmc_block_size = MMC_BLOCK_SIZE; | |
6d0f6bcf | 382 | ulong src = blknr * mmc_block_size + CONFIG_SYS_MMC_BASE; |
71f95118 | 383 | |
c95219fa | 384 | mmc_read(src, (uchar *) dst, blkcnt * mmc_block_size); |
71f95118 WD |
385 | return blkcnt; |
386 | } | |
387 | ||
c95219fa SB |
388 | #ifdef __GNUC__ |
389 | #define likely(x) __builtin_expect(!!(x), 1) | |
390 | #define unlikely(x) __builtin_expect(!!(x), 0) | |
391 | #else | |
392 | #define likely(x) (x) | |
393 | #define unlikely(x) (x) | |
394 | #endif | |
395 | ||
396 | #define UNSTUFF_BITS(resp,start,size) \ | |
397 | ({ \ | |
398 | const int __size = size; \ | |
399 | const uint32_t __mask = (__size < 32 ? 1 << __size : 0) - 1; \ | |
400 | const int32_t __off = 3 - ((start) / 32); \ | |
401 | const int32_t __shft = (start) & 31; \ | |
402 | uint32_t __res; \ | |
403 | \ | |
404 | __res = resp[__off] >> __shft; \ | |
405 | if (__size + __shft > 32) \ | |
406 | __res |= resp[__off-1] << ((32 - __shft) % 32); \ | |
407 | __res & __mask; \ | |
408 | }) | |
409 | ||
410 | /* | |
411 | * Given the decoded CSD structure, decode the raw CID to our CID structure. | |
412 | */ | |
413 | static void mmc_decode_cid(uint32_t * resp) | |
414 | { | |
415 | if (IF_TYPE_SD == mmc_dev.if_type) { | |
416 | /* | |
417 | * SD doesn't currently have a version field so we will | |
418 | * have to assume we can parse this. | |
419 | */ | |
420 | sprintf((char *)mmc_dev.vendor, | |
421 | "Man %02x OEM %c%c \"%c%c%c%c%c\" Date %02u/%04u", | |
422 | UNSTUFF_BITS(resp, 120, 8), UNSTUFF_BITS(resp, 112, 8), | |
423 | UNSTUFF_BITS(resp, 104, 8), UNSTUFF_BITS(resp, 96, 8), | |
424 | UNSTUFF_BITS(resp, 88, 8), UNSTUFF_BITS(resp, 80, 8), | |
425 | UNSTUFF_BITS(resp, 72, 8), UNSTUFF_BITS(resp, 64, 8), | |
426 | UNSTUFF_BITS(resp, 8, 4), UNSTUFF_BITS(resp, 12, | |
427 | 8) + 2000); | |
428 | sprintf((char *)mmc_dev.revision, "%d.%d", | |
429 | UNSTUFF_BITS(resp, 60, 4), UNSTUFF_BITS(resp, 56, 4)); | |
430 | sprintf((char *)mmc_dev.product, "%u", | |
431 | UNSTUFF_BITS(resp, 24, 32)); | |
432 | } else { | |
433 | /* | |
434 | * The selection of the format here is based upon published | |
435 | * specs from sandisk and from what people have reported. | |
436 | */ | |
437 | switch (spec_ver) { | |
438 | case 0: /* MMC v1.0 - v1.2 */ | |
439 | case 1: /* MMC v1.4 */ | |
440 | sprintf((char *)mmc_dev.vendor, | |
441 | "Man %02x%02x%02x \"%c%c%c%c%c%c%c\" Date %02u/%04u", | |
442 | UNSTUFF_BITS(resp, 120, 8), UNSTUFF_BITS(resp, | |
443 | 112, | |
444 | 8), | |
445 | UNSTUFF_BITS(resp, 104, 8), UNSTUFF_BITS(resp, | |
446 | 96, 8), | |
447 | UNSTUFF_BITS(resp, 88, 8), UNSTUFF_BITS(resp, | |
448 | 80, 8), | |
449 | UNSTUFF_BITS(resp, 72, 8), UNSTUFF_BITS(resp, | |
450 | 64, 8), | |
451 | UNSTUFF_BITS(resp, 56, 8), UNSTUFF_BITS(resp, | |
452 | 48, 8), | |
453 | UNSTUFF_BITS(resp, 12, 4), UNSTUFF_BITS(resp, 8, | |
454 | 4) + | |
455 | 1997); | |
456 | sprintf((char *)mmc_dev.revision, "%d.%d", | |
457 | UNSTUFF_BITS(resp, 44, 4), UNSTUFF_BITS(resp, | |
458 | 40, 4)); | |
459 | sprintf((char *)mmc_dev.product, "%u", | |
460 | UNSTUFF_BITS(resp, 16, 24)); | |
461 | break; | |
462 | ||
463 | case 2: /* MMC v2.0 - v2.2 */ | |
464 | case 3: /* MMC v3.1 - v3.3 */ | |
465 | case 4: /* MMC v4 */ | |
466 | sprintf((char *)mmc_dev.vendor, | |
467 | "Man %02x OEM %04x \"%c%c%c%c%c%c\" Date %02u/%04u", | |
468 | UNSTUFF_BITS(resp, 120, 8), UNSTUFF_BITS(resp, | |
469 | 104, | |
470 | 16), | |
471 | UNSTUFF_BITS(resp, 96, 8), UNSTUFF_BITS(resp, | |
472 | 88, 8), | |
473 | UNSTUFF_BITS(resp, 80, 8), UNSTUFF_BITS(resp, | |
474 | 72, 8), | |
475 | UNSTUFF_BITS(resp, 64, 8), UNSTUFF_BITS(resp, | |
476 | 56, 8), | |
477 | UNSTUFF_BITS(resp, 12, 4), UNSTUFF_BITS(resp, 8, | |
478 | 4) + | |
479 | 1997); | |
480 | sprintf((char *)mmc_dev.product, "%u", | |
481 | UNSTUFF_BITS(resp, 16, 32)); | |
482 | sprintf((char *)mmc_dev.revision, "N/A"); | |
483 | break; | |
484 | ||
485 | default: | |
486 | printf("MMC card has unknown MMCA version %d\n", | |
487 | spec_ver); | |
488 | break; | |
489 | } | |
490 | } | |
491 | printf("%s card.\nVendor: %s\nProduct: %s\nRevision: %s\n", | |
492 | (IF_TYPE_SD == mmc_dev.if_type) ? "SD" : "MMC", mmc_dev.vendor, | |
493 | mmc_dev.product, mmc_dev.revision); | |
494 | } | |
495 | ||
496 | /* | |
497 | * Given a 128-bit response, decode to our card CSD structure. | |
498 | */ | |
499 | static void mmc_decode_csd(uint32_t * resp) | |
500 | { | |
501 | unsigned int mult, csd_struct; | |
502 | ||
503 | if (IF_TYPE_SD == mmc_dev.if_type) { | |
504 | csd_struct = UNSTUFF_BITS(resp, 126, 2); | |
505 | if (csd_struct != 0) { | |
506 | printf("SD: unrecognised CSD structure version %d\n", | |
507 | csd_struct); | |
508 | return; | |
509 | } | |
510 | } else { | |
511 | /* | |
512 | * We only understand CSD structure v1.1 and v1.2. | |
513 | * v1.2 has extra information in bits 15, 11 and 10. | |
514 | */ | |
515 | csd_struct = UNSTUFF_BITS(resp, 126, 2); | |
516 | if (csd_struct != 1 && csd_struct != 2) { | |
517 | printf("MMC: unrecognised CSD structure version %d\n", | |
518 | csd_struct); | |
519 | return; | |
520 | } | |
521 | ||
522 | spec_ver = UNSTUFF_BITS(resp, 122, 4); | |
523 | mmc_dev.if_type = IF_TYPE_MMC; | |
524 | } | |
525 | ||
526 | mult = 1 << (UNSTUFF_BITS(resp, 47, 3) + 2); | |
527 | mmc_dev.lba = (1 + UNSTUFF_BITS(resp, 62, 12)) * mult; | |
528 | mmc_dev.blksz = 1 << UNSTUFF_BITS(resp, 80, 4); | |
529 | ||
530 | /* FIXME: The following just makes assumes that's the partition type -- should really read it */ | |
531 | mmc_dev.part_type = PART_TYPE_DOS; | |
532 | mmc_dev.dev = 0; | |
533 | mmc_dev.lun = 0; | |
534 | mmc_dev.type = DEV_TYPE_HARDDISK; | |
535 | mmc_dev.removable = 0; | |
536 | mmc_dev.block_read = mmc_bread; | |
537 | ||
0a5676be JCPV |
538 | printf("Detected: %lu blocks of %lu bytes (%luMB) ", |
539 | mmc_dev.lba, | |
540 | mmc_dev.blksz, | |
541 | mmc_dev.lba * mmc_dev.blksz / (1024 * 1024)); | |
c95219fa SB |
542 | } |
543 | ||
71f95118 WD |
544 | int |
545 | /****************************************************/ | |
546 | mmc_init(int verbose) | |
547 | /****************************************************/ | |
548 | { | |
c95219fa SB |
549 | int retries, rc = -ENODEV; |
550 | uint32_t cid_resp[4]; | |
551 | uint32_t *resp; | |
552 | uint16_t rca = 0; | |
553 | ||
554 | /* Reset device interface type */ | |
555 | mmc_dev.if_type = IF_TYPE_UNKNOWN; | |
71f95118 | 556 | |
c95219fa SB |
557 | #if defined (CONFIG_LUBBOCK) || (defined (CONFIG_GUMSTIX) && !defined(CONFIG_PXA27X)) |
558 | set_GPIO_mode(GPIO6_MMCCLK_MD); | |
559 | set_GPIO_mode(GPIO8_MMCCS0_MD); | |
71f95118 | 560 | #endif |
c95219fa | 561 | CKEN |= CKEN12_MMC; /* enable MMC unit clock */ |
71f95118 | 562 | |
c95219fa SB |
563 | MMC_CLKRT = MMC_CLKRT_0_3125MHZ; |
564 | MMC_RESTO = MMC_RES_TO_MAX; | |
565 | MMC_SPI = MMC_SPI_DISABLE; | |
71f95118 WD |
566 | |
567 | /* reset */ | |
341188b9 | 568 | mmc_cmd(MMC_CMD_GO_IDLE_STATE, 0, 0, MMC_CMDAT_INIT | MMC_CMDAT_R0); |
c95219fa SB |
569 | udelay(200000); |
570 | retries = 3; | |
571 | while (retries--) { | |
572 | resp = mmc_cmd(MMC_CMD_APP_CMD, 0, 0, MMC_CMDAT_R1); | |
573 | if (!(resp[0] & 0x00000020)) { /* Card does not support APP_CMD */ | |
574 | debug("Card does not support APP_CMD\n"); | |
575 | break; | |
576 | } | |
577 | ||
c455d073 AF |
578 | /* Select 3.2-3.3V and 3.3-3.4V */ |
579 | resp = mmc_cmd(SD_CMD_APP_SEND_OP_COND, 0x0030, 0x0000, | |
341188b9 HS |
580 | MMC_CMDAT_R3 | (retries < 2 ? 0 |
581 | : MMC_CMDAT_INIT)); | |
c95219fa SB |
582 | if (resp[0] & 0x80000000) { |
583 | mmc_dev.if_type = IF_TYPE_SD; | |
584 | debug("Detected SD card\n"); | |
585 | break; | |
586 | } | |
20787e23 WD |
587 | #ifdef CONFIG_PXA27X |
588 | udelay(10000); | |
589 | #else | |
c95219fa | 590 | udelay(200000); |
20787e23 | 591 | #endif |
c95219fa SB |
592 | } |
593 | ||
594 | if (retries <= 0 || !(IF_TYPE_SD == mmc_dev.if_type)) { | |
595 | debug("Failed to detect SD Card, trying MMC\n"); | |
596 | resp = | |
597 | mmc_cmd(MMC_CMD_SEND_OP_COND, 0x00ff, 0x8000, MMC_CMDAT_R3); | |
598 | ||
599 | retries = 10; | |
600 | while (retries-- && resp && !(resp[0] & 0x80000000)) { | |
601 | #ifdef CONFIG_PXA27X | |
602 | udelay(10000); | |
603 | #else | |
604 | udelay(200000); | |
605 | #endif | |
606 | resp = | |
607 | mmc_cmd(MMC_CMD_SEND_OP_COND, 0x00ff, 0x8000, | |
608 | MMC_CMDAT_R3); | |
609 | } | |
71f95118 WD |
610 | } |
611 | ||
612 | /* try to get card id */ | |
c95219fa SB |
613 | resp = |
614 | mmc_cmd(MMC_CMD_ALL_SEND_CID, 0, 0, MMC_CMDAT_R2 | MMC_CMDAT_BUSY); | |
20787e23 | 615 | if (resp) { |
c95219fa | 616 | memcpy(cid_resp, resp, sizeof(cid_resp)); |
71f95118 WD |
617 | |
618 | /* MMC exists, get CSD too */ | |
341188b9 | 619 | resp = mmc_cmd(MMC_CMD_SET_RELATIVE_ADDR, 0, 0, MMC_CMDAT_R1); |
c95219fa SB |
620 | if (IF_TYPE_SD == mmc_dev.if_type) |
621 | rca = ((resp[0] & 0xffff0000) >> 16); | |
622 | resp = mmc_cmd(MMC_CMD_SEND_CSD, rca, 0, MMC_CMDAT_R2); | |
20787e23 | 623 | if (resp) { |
c95219fa | 624 | mmc_decode_csd(resp); |
71f95118 WD |
625 | rc = 0; |
626 | mmc_ready = 1; | |
71f95118 | 627 | } |
c95219fa SB |
628 | |
629 | mmc_decode_cid(cid_resp); | |
71f95118 WD |
630 | } |
631 | ||
c95219fa SB |
632 | MMC_CLKRT = 0; /* 20 MHz */ |
633 | resp = mmc_cmd(MMC_CMD_SELECT_CARD, rca, 0, MMC_CMDAT_R1); | |
634 | ||
20787e23 | 635 | #ifdef CONFIG_PXA27X |
c95219fa SB |
636 | if (IF_TYPE_SD == mmc_dev.if_type) { |
637 | resp = mmc_cmd(MMC_CMD_APP_CMD, rca, 0, MMC_CMDAT_R1); | |
638 | resp = mmc_cmd(SD_CMD_APP_SET_BUS_WIDTH, 0, 2, MMC_CMDAT_R1); | |
639 | wide = MMC_CMDAT_SD_4DAT; | |
640 | } | |
20787e23 | 641 | #endif |
71f95118 | 642 | |
c95219fa | 643 | fat_register_device(&mmc_dev, 1); /* partitions start counting with 1 */ |
71f95118 WD |
644 | |
645 | return rc; | |
646 | } | |
647 | ||
c95219fa | 648 | int mmc_ident(block_dev_desc_t * dev) |
71f95118 WD |
649 | { |
650 | return 0; | |
651 | } | |
652 | ||
c95219fa | 653 | int mmc2info(ulong addr) |
71f95118 | 654 | { |
6d0f6bcf JCPV |
655 | if (addr >= CONFIG_SYS_MMC_BASE |
656 | && addr < CONFIG_SYS_MMC_BASE + (mmc_dev.lba * mmc_dev.blksz)) { | |
71f95118 WD |
657 | return 1; |
658 | } | |
659 | return 0; | |
660 | } | |
661 | ||
c95219fa | 662 | #endif /* CONFIG_MMC */ |