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