blob: 9e6e1e3d3b8afa1d54dabdcbe7ebbd640a090f25 [file] [log] [blame]
wdenk71f95112003-06-15 22:40:42 +00001/*
2 * (C) Copyright 2003
3 * Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net
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>
29
30#ifdef CONFIG_MMC
31
32extern int
33fat_register_read(int(*block_read)(int device, ulong blknr, ulong blkcnt, uchar *buffer));
34
wdenk8bde7f72003-06-27 21:31:46 +000035/*
wdenk71f95112003-06-15 22:40:42 +000036 * FIXME needs to read cid and csd info to determine block size
37 * and other parameters
38 */
39static uchar mmc_buf[MMC_BLOCK_SIZE];
40static mmc_csd_t mmc_csd;
41static int mmc_ready = 0;
42
43
wdenk8bde7f72003-06-27 21:31:46 +000044static uchar *
wdenk71f95112003-06-15 22:40:42 +000045/****************************************************/
46mmc_cmd(ushort cmd, ushort argh, ushort argl, ushort cmdat)
47/****************************************************/
48{
49 static uchar resp[20];
50 ulong status;
51 int words, i;
52
53 debug("mmc_cmd %x %x %x %x\n", cmd, argh, argl, cmdat);
54 MMC_STRPCL = MMC_STRPCL_STOP_CLK;
55 MMC_I_MASK = ~MMC_I_MASK_CLK_IS_OFF;
56 while (!(MMC_I_REG & MMC_I_REG_CLK_IS_OFF));
wdenk8bde7f72003-06-27 21:31:46 +000057 MMC_CMD = cmd;
wdenk71f95112003-06-15 22:40:42 +000058 MMC_ARGH = argh;
59 MMC_ARGL = argl;
60 MMC_CMDAT = cmdat;
61 MMC_I_MASK = ~MMC_I_MASK_END_CMD_RES;
62 MMC_STRPCL = MMC_STRPCL_START_CLK;
63 while (!(MMC_I_REG & MMC_I_REG_END_CMD_RES));
64
65 status = MMC_STAT;
66 debug("MMC status %x\n", status);
67 if (status & MMC_STAT_TIME_OUT_RESPONSE)
68 {
69 return 0;
70 }
71
72 switch (cmdat & 0x3)
73 {
74 case MMC_CMDAT_R1:
75 case MMC_CMDAT_R3:
wdenk8bde7f72003-06-27 21:31:46 +000076 words = 3;
wdenk71f95112003-06-15 22:40:42 +000077 break;
78
79 case MMC_CMDAT_R2:
wdenk8bde7f72003-06-27 21:31:46 +000080 words = 8;
wdenk71f95112003-06-15 22:40:42 +000081 break;
82
83 default:
84 return 0;
85 }
86 for (i = words-1; i >= 0; i--)
87 {
88 ulong res_fifo = MMC_RES;
89 int offset = i << 1;
90
91 resp[offset] = ((uchar *)&res_fifo)[0];
92 resp[offset+1] = ((uchar *)&res_fifo)[1];
93 }
94#ifdef MMC_DEBUG
95 for (i=0; i<words*2; i += 2)
96 {
97 printf("MMC resp[%d] = %02x\n", i, resp[i]);
98 printf("MMC resp[%d] = %02x\n", i+1, resp[i+1]);
99 }
100#endif
101 return resp;
102}
103
104int
105/****************************************************/
106mmc_block_read(uchar *dst, ulong src, ulong len)
107/****************************************************/
108{
109 uchar *resp;
110 ushort argh, argl;
111 ulong status;
112
113 if (len == 0)
114 {
115 return 0;
116 }
117
118 debug("mmc_block_rd dst %lx src %lx len %d\n", (ulong)dst, src, len);
119
120 argh = len >> 16;
121 argl = len & 0xffff;
122
123 /* set block len */
124 resp = mmc_cmd(MMC_CMD_SET_BLOCKLEN, argh, argl, MMC_CMDAT_R1);
125
126 /* send read command */
127 argh = src >> 16;
128 argl = src & 0xffff;
129 MMC_STRPCL = MMC_STRPCL_STOP_CLK;
130 MMC_RDTO = 0xffff;
131 MMC_NOB = 1;
132 MMC_BLKLEN = len;
wdenk8bde7f72003-06-27 21:31:46 +0000133 resp = mmc_cmd(MMC_CMD_READ_BLOCK, argh, argl,
wdenk71f95112003-06-15 22:40:42 +0000134 MMC_CMDAT_R1|MMC_CMDAT_READ|MMC_CMDAT_BLOCK|MMC_CMDAT_DATA_EN);
135
wdenk8bde7f72003-06-27 21:31:46 +0000136
wdenk71f95112003-06-15 22:40:42 +0000137 MMC_I_MASK = ~MMC_I_MASK_RXFIFO_RD_REQ;
138 while (len)
139 {
140 if (MMC_I_REG & MMC_I_REG_RXFIFO_RD_REQ)
141 {
142 *dst++ = MMC_RXFIFO;
143 len--;
144 }
145 status = MMC_STAT;
146 if (status & MMC_STAT_ERRORS)
147 {
148 printf("MMC_STAT error %lx\n", status);
149 return -1;
150 }
151 }
152 MMC_I_MASK = ~MMC_I_MASK_DATA_TRAN_DONE;
153 while (!(MMC_I_REG & MMC_I_REG_DATA_TRAN_DONE));
154 status = MMC_STAT;
155 if (status & MMC_STAT_ERRORS)
156 {
157 printf("MMC_STAT error %lx\n", status);
158 return -1;
159 }
160 return 0;
161}
162
163int
164/****************************************************/
165mmc_block_write(ulong dst, uchar *src, int len)
166/****************************************************/
167{
168 uchar *resp;
169 ushort argh, argl;
170 ulong status;
171
172 if (len == 0)
173 {
174 return 0;
175 }
176
177 debug("mmc_block_wr dst %lx src %lx len %d\n", dst, (ulong)src, len);
178
179 argh = len >> 16;
180 argl = len & 0xffff;
181
182 /* set block len */
183 resp = mmc_cmd(MMC_CMD_SET_BLOCKLEN, argh, argl, MMC_CMDAT_R1);
184
185 /* send write command */
186 argh = dst >> 16;
187 argl = dst & 0xffff;
188 MMC_STRPCL = MMC_STRPCL_STOP_CLK;
189 MMC_NOB = 1;
190 MMC_BLKLEN = len;
wdenk8bde7f72003-06-27 21:31:46 +0000191 resp = mmc_cmd(MMC_CMD_WRITE_BLOCK, argh, argl,
wdenk71f95112003-06-15 22:40:42 +0000192 MMC_CMDAT_R1|MMC_CMDAT_WRITE|MMC_CMDAT_BLOCK|MMC_CMDAT_DATA_EN);
wdenk8bde7f72003-06-27 21:31:46 +0000193
wdenk71f95112003-06-15 22:40:42 +0000194 MMC_I_MASK = ~MMC_I_MASK_TXFIFO_WR_REQ;
195 while (len)
196 {
197 if (MMC_I_REG & MMC_I_REG_TXFIFO_WR_REQ)
198 {
199 int i, bytes = min(32,len);
200
201 for (i=0; i<bytes; i++)
202 {
203 MMC_TXFIFO = *src++;
204 }
205 if (bytes < 32)
206 {
207 MMC_PRTBUF = MMC_PRTBUF_BUF_PART_FULL;
208 }
209 len -= bytes;
210 }
211 status = MMC_STAT;
212 if (status & MMC_STAT_ERRORS)
213 {
214 printf("MMC_STAT error %lx\n", status);
215 return -1;
216 }
217 }
218 MMC_I_MASK = ~MMC_I_MASK_DATA_TRAN_DONE;
219 while (!(MMC_I_REG & MMC_I_REG_DATA_TRAN_DONE));
220 MMC_I_MASK = ~MMC_I_MASK_PRG_DONE;
221 while (!(MMC_I_REG & MMC_I_REG_PRG_DONE));
222 status = MMC_STAT;
223 if (status & MMC_STAT_ERRORS)
224 {
225 printf("MMC_STAT error %lx\n", status);
226 return -1;
227 }
228 return 0;
229}
230
231
232int
233/****************************************************/
234mmc_read(ulong src, uchar *dst, int size)
235/****************************************************/
236{
237 ulong end, part_start, part_end, part_len, aligned_start, aligned_end;
238 ulong mmc_block_size, mmc_block_address;
239
240 if (size == 0)
241 {
242 return 0;
243 }
244
245 if (!mmc_ready)
246 {
247 printf("Please initial the MMC first\n");
248 return -1;
249 }
250
251 mmc_block_size = MMC_BLOCK_SIZE;
252 mmc_block_address = ~(mmc_block_size - 1);
253
254 src -= CFG_MMC_BASE;
255 end = src + size;
256 part_start = ~mmc_block_address & src;
257 part_end = ~mmc_block_address & end;
258 aligned_start = mmc_block_address & src;
259 aligned_end = mmc_block_address & end;
260
261 /* all block aligned accesses */
262 debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
263 src, (ulong)dst, end, part_start, part_end, aligned_start, aligned_end);
264 if (part_start)
265 {
266 part_len = mmc_block_size - part_start;
267 debug("ps src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
268 src, (ulong)dst, end, part_start, part_end, aligned_start, aligned_end);
269 if ((mmc_block_read(mmc_buf, aligned_start, mmc_block_size)) < 0)
270 {
271 return -1;
272 }
273 memcpy(dst, mmc_buf+part_start, part_len);
274 dst += part_len;
275 src += part_len;
276 }
277 debug("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, aligned_end);
279 for (; src < aligned_end; src += mmc_block_size, dst += mmc_block_size)
280 {
281 debug("al src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
282 src, (ulong)dst, end, part_start, part_end, aligned_start, aligned_end);
283 if ((mmc_block_read((uchar *)(dst), src, mmc_block_size)) < 0)
284 {
285 return -1;
286 }
287 }
288 debug("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, aligned_end);
290 if (part_end && src < end)
291 {
292 debug("pe src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
293 src, (ulong)dst, end, part_start, part_end, aligned_start, aligned_end);
294 if ((mmc_block_read(mmc_buf, aligned_end, mmc_block_size)) < 0)
295 {
296 return -1;
297 }
298 memcpy(dst, mmc_buf, part_end);
299 }
300 return 0;
301}
302
303int
304/****************************************************/
305mmc_write(uchar *src, ulong dst, int size)
306/****************************************************/
307{
308 ulong end, part_start, part_end, part_len, aligned_start, aligned_end;
309 ulong mmc_block_size, mmc_block_address;
310
311 if (size == 0)
312 {
313 return 0;
314 }
315
316 if (!mmc_ready)
317 {
318 printf("Please initial the MMC first\n");
319 return -1;
320 }
321
322 mmc_block_size = MMC_BLOCK_SIZE;
323 mmc_block_address = ~(mmc_block_size - 1);
324
325 dst -= CFG_MMC_BASE;
326 end = dst + size;
327 part_start = ~mmc_block_address & dst;
328 part_end = ~mmc_block_address & end;
329 aligned_start = mmc_block_address & dst;
330 aligned_end = mmc_block_address & end;
331
332 /* all block aligned accesses */
333 debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
334 src, (ulong)dst, end, part_start, part_end, aligned_start, aligned_end);
335 if (part_start)
336 {
337 part_len = mmc_block_size - part_start;
338 debug("ps src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
339 (ulong)src, dst, end, part_start, part_end, aligned_start, aligned_end);
340 if ((mmc_block_read(mmc_buf, aligned_start, mmc_block_size)) < 0)
341 {
342 return -1;
343 }
344 memcpy(mmc_buf+part_start, src, part_len);
345 if ((mmc_block_write(aligned_start, mmc_buf, mmc_block_size)) < 0)
346 {
347 return -1;
348 }
349 dst += part_len;
350 src += part_len;
351 }
352 debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
353 src, (ulong)dst, end, part_start, part_end, aligned_start, aligned_end);
354 for (; dst < aligned_end; src += mmc_block_size, dst += mmc_block_size)
355 {
356 debug("al src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
357 src, (ulong)dst, end, part_start, part_end, aligned_start, aligned_end);
358 if ((mmc_block_write(dst, (uchar *)src, mmc_block_size)) < 0)
359 {
360 return -1;
361 }
362 }
363 debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
364 src, (ulong)dst, end, part_start, part_end, aligned_start, aligned_end);
365 if (part_end && dst < end)
366 {
367 debug("pe src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
368 src, (ulong)dst, end, part_start, part_end, aligned_start, aligned_end);
369 if ((mmc_block_read(mmc_buf, aligned_end, mmc_block_size)) < 0)
370 {
371 return -1;
372 }
373 memcpy(mmc_buf, src, part_end);
374 if ((mmc_block_write(aligned_end, mmc_buf, mmc_block_size)) < 0)
375 {
376 return -1;
377 }
378 }
379 return 0;
380}
381
wdenk8bde7f72003-06-27 21:31:46 +0000382int
wdenk71f95112003-06-15 22:40:42 +0000383/****************************************************/
384mmc_bread(int dev_num, ulong blknr, ulong blkcnt, uchar *dst)
385/****************************************************/
386{
387 int mmc_block_size = MMC_BLOCK_SIZE;
388 ulong src = blknr * mmc_block_size + CFG_MMC_BASE;
389
390 mmc_read(src, (uchar *)dst, blkcnt*mmc_block_size);
391 return blkcnt;
392}
393
394int
395/****************************************************/
396mmc_init(int verbose)
397/****************************************************/
398{
399 int retries, rc = -ENODEV;
400 uchar *resp;
401
402#ifdef CONFIG_LUBBOCK
403 set_GPIO_mode( GPIO6_MMCCLK_MD );
404 set_GPIO_mode( GPIO8_MMCCS0_MD );
405#endif
406 CKEN |= CKEN12_MMC; /* enable MMC unit clock */
407
408 mmc_csd.c_size = 0;
409
410 MMC_CLKRT = MMC_CLKRT_0_3125MHZ;
wdenk8bde7f72003-06-27 21:31:46 +0000411 MMC_RESTO = MMC_RES_TO_MAX;
wdenk71f95112003-06-15 22:40:42 +0000412 MMC_SPI = MMC_SPI_DISABLE;
413
414 /* reset */
415 retries = 10;
416 resp = mmc_cmd(0, 0, 0, 0);
417 resp = mmc_cmd(1, 0x00ff, 0xc000, MMC_CMDAT_INIT|MMC_CMDAT_BUSY|MMC_CMDAT_R3);
418 while (retries-- && resp && !(resp[4] & 0x80))
419 {
420 debug("resp %x %x\n", resp[0], resp[1]);
421 udelay(50);
422 resp = mmc_cmd(1, 0x00ff, 0xff00, MMC_CMDAT_BUSY|MMC_CMDAT_R3);
423 }
424
425 /* try to get card id */
426 resp = mmc_cmd(2, 0, 0, MMC_CMDAT_R2);
427 if (resp)
428 {
429 /* TODO configure mmc driver depending on card attributes */
430 mmc_cid_t *cid = (mmc_cid_t *)resp;
431 if (verbose)
432 {
433 printf("MMC found. Card desciption is:\n");
wdenk8bde7f72003-06-27 21:31:46 +0000434 printf("Manufacturer ID = %02x%02x%02x\n",
wdenk71f95112003-06-15 22:40:42 +0000435 cid->id[0], cid->id[1], cid->id[2]);
436 printf("HW/FW Revision = %x %x\n",cid->hwrev, cid->fwrev);
437 cid->hwrev = cid->fwrev = 0; /* null terminate string */
438 printf("Product Name = %s\n",cid->name);
wdenk8bde7f72003-06-27 21:31:46 +0000439 printf("Serial Number = %02x%02x%02x\n",
wdenk71f95112003-06-15 22:40:42 +0000440 cid->sn[0], cid->sn[1], cid->sn[2]);
441 printf("Month = %d\n",cid->month);
442 printf("Year = %d\n",1997 + cid->year);
443 }
444
445 /* MMC exists, get CSD too */
446 resp = mmc_cmd(MMC_CMD_SET_RCA, MMC_DEFAULT_RCA, 0, MMC_CMDAT_R1);
447 resp = mmc_cmd(MMC_CMD_SEND_CSD, MMC_DEFAULT_RCA, 0, MMC_CMDAT_R2);
448 if (resp)
449 {
450 mmc_csd_t *csd = (mmc_csd_t *)resp;
451 memcpy(&mmc_csd, csd, sizeof(csd));
452 rc = 0;
453 mmc_ready = 1;
454 /* FIXME add verbose printout for csd */
455 }
456 }
457
458 MMC_CLKRT = 0; /* 20 MHz */
459 resp = mmc_cmd(7, MMC_DEFAULT_RCA, 0, MMC_CMDAT_R1);
460
461 fat_register_read(mmc_bread);
462
463 return rc;
464}
465
466int
467mmc_ident(block_dev_desc_t *dev)
468{
469 return 0;
470}
471
472int
473mmc2info(ulong addr)
474{
475 /* FIXME hard codes to 32 MB device */
476 if (addr >= CFG_MMC_BASE && addr < CFG_MMC_BASE + 0x02000000)
477 {
478 return 1;
479 }
480 return 0;
481}
482
483#endif