2 * u-boot S3C2410 MMC/SD card driver
3 * (C) Copyright 2006 by OpenMoko, Inc.
4 * Author: Harald Welte <laforge@openmoko.org>
6 * based on u-boot pxa MMC driver and linux/drivers/mmc/s3c2410mci.c
7 * (C) 2005-2005 Thomas Kleffel
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of
12 * the License, or (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
28 #include <asm/errno.h>
34 #if defined(CONFIG_MMC) && defined(CONFIG_MMC_S3C)
36 #define CONFIG_MMC_WIDE
38 static S3C2410_SDI
*sdi
;
40 static block_dev_desc_t mmc_dev
;
42 block_dev_desc_t
* mmc_get_dev(int dev
)
44 return ((block_dev_desc_t
*)&mmc_dev
);
48 * FIXME needs to read cid and csd info to determine block size
49 * and other parameters
51 static uchar mmc_buf
[MMC_BLOCK_SIZE
];
52 static mmc_csd_t mmc_csd
;
53 static int mmc_ready
= 0;
57 #define CMD_F_RESP 0x01
58 #define CMD_F_RESP_LONG 0x02
60 static u_int32_t
*mmc_cmd(ushort cmd
, ulong arg
, ushort flags
)
62 static u_int32_t resp
[5];
65 u_int32_t csta_rdy_bit
= S3C2410_SDICMDSTAT_CMDSENT
;
67 memset(resp
, 0, sizeof(resp
));
69 debug("mmc_cmd CMD%d arg=0x%08x flags=%x\n", cmd
, arg
, flags
);
71 sdi
->SDICSTA
= 0xffffffff;
72 sdi
->SDIDSTA
= 0xffffffff;
73 sdi
->SDIFSTA
= 0xffffffff;
77 ccon
= cmd
& S3C2410_SDICMDCON_INDEX
;
78 ccon
|= S3C2410_SDICMDCON_SENDERHOST
|S3C2410_SDICMDCON_CMDSTART
;
80 if (flags
& CMD_F_RESP
) {
81 ccon
|= S3C2410_SDICMDCON_WAITRSP
;
82 csta_rdy_bit
= S3C2410_SDICMDSTAT_RSPFIN
; /* 1 << 9 */
85 if (flags
& CMD_F_RESP_LONG
)
86 ccon
|= S3C2410_SDICMDCON_LONGRSP
;
92 if (csta
& csta_rdy_bit
)
94 if (csta
& S3C2410_SDICMDSTAT_CMDTIMEOUT
) {
95 printf("===============> MMC CMD Timeout\n");
96 sdi
->SDICSTA
|= S3C2410_SDICMDSTAT_CMDTIMEOUT
;
101 debug("final MMC CMD status 0x%x\n", csta
);
103 sdi
->SDICSTA
|= csta_rdy_bit
;
105 if (flags
& CMD_F_RESP
) {
106 resp
[0] = sdi
->SDIRSP0
;
107 resp
[1] = sdi
->SDIRSP1
;
108 resp
[2] = sdi
->SDIRSP2
;
109 resp
[3] = sdi
->SDIRSP3
;
115 #define FIFO_FILL(host) ((host->SDIFSTA & S3C2410_SDIFSTA_COUNTMASK) >> 2)
117 static int mmc_block_read(uchar
*dst
, ulong src
, ulong len
)
119 u_int32_t dcon
, fifo
;
120 u_int32_t
*dst_u32
= (u_int32_t
*)dst
;
126 debug("mmc_block_rd dst %lx src %lx len %d\n", (ulong
)dst
, src
, len
);
129 resp
= mmc_cmd(MMC_CMD_SET_BLOCKLEN
, len
, CMD_F_RESP
);
132 //sdi->SDIPRE = 0xff;
135 dcon
= (len
>> 9) & S3C2410_SDIDCON_BLKNUM
;
136 dcon
|= S3C2410_SDIDCON_BLOCKMODE
;
137 dcon
|= S3C2410_SDIDCON_RXAFTERCMD
|S3C2410_SDIDCON_XFER_RXSTART
;
139 dcon
|= S3C2410_SDIDCON_WIDEBUS
;
140 #if defined(CONFIG_S3C2440) || defined(CONFIG_S3C2442)
141 dcon
|= S3C2440_SDIDCON_DS_WORD
| S3C2440_SDIDCON_DATSTART
;
145 /* send read command */
146 resp
= mmc_cmd(MMC_CMD_READ_BLOCK
, src
, CMD_F_RESP
);
149 u_int32_t sdidsta
= sdi
->SDIDSTA
;
150 fifo
= FIFO_FILL(sdi
);
151 if (sdidsta
& (S3C2410_SDIDSTA_FIFOFAIL
|
152 S3C2410_SDIDSTA_CRCFAIL
|
153 S3C2410_SDIDSTA_RXCRCFAIL
|
154 S3C2410_SDIDSTA_DATATIMEOUT
)) {
155 printf("mmc_block_read: err SDIDSTA=0x%08x\n", sdidsta
);
160 //debug("dst_u32 = 0x%08x\n", dst_u32);
161 *(dst_u32
++) = sdi
->SDIDAT
;
171 debug("waiting for SDIDSTA (currently 0x%08x\n", sdi
->SDIDSTA
);
172 while (!(sdi
->SDIDSTA
& (1 << 4))) {}
173 debug("done waiting for SDIDSTA (currently 0x%08x\n", sdi
->SDIDSTA
);
177 if (!(sdi
->SDIDSTA
& S3C2410_SDIDSTA_XFERFINISH
))
178 debug("mmc_block_read; transfer not finished!\n");
183 static int mmc_block_write(ulong dst
, uchar
*src
, int len
)
185 printf("MMC block write not yet supported on S3C2410!\n");
190 int mmc_read(ulong src
, uchar
*dst
, int size
)
192 ulong end
, part_start
, part_end
, part_len
, aligned_start
, aligned_end
;
193 ulong mmc_block_size
, mmc_block_address
;
199 printf("Please initialize the MMC first\n");
203 mmc_block_size
= MMC_BLOCK_SIZE
;
204 mmc_block_address
= ~(mmc_block_size
- 1);
208 part_start
= ~mmc_block_address
& src
;
209 part_end
= ~mmc_block_address
& end
;
210 aligned_start
= mmc_block_address
& src
;
211 aligned_end
= mmc_block_address
& end
;
213 /* all block aligned accesses */
214 debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
215 src
, (ulong
)dst
, end
, part_start
, part_end
, aligned_start
, aligned_end
);
217 part_len
= mmc_block_size
- part_start
;
218 debug("ps src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
219 src
, (ulong
)dst
, end
, part_start
, part_end
, aligned_start
, aligned_end
);
220 if ((mmc_block_read(mmc_buf
, aligned_start
, mmc_block_size
)) < 0)
223 memcpy(dst
, mmc_buf
+part_start
, part_len
);
227 debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
228 src
, (ulong
)dst
, end
, part_start
, part_end
, aligned_start
, aligned_end
);
229 for (; src
< aligned_end
; src
+= mmc_block_size
, dst
+= mmc_block_size
) {
230 debug("al src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
231 src
, (ulong
)dst
, end
, part_start
, part_end
, aligned_start
, aligned_end
);
232 if ((mmc_block_read((uchar
*)(dst
), src
, mmc_block_size
)) < 0)
235 debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
236 src
, (ulong
)dst
, end
, part_start
, part_end
, aligned_start
, aligned_end
);
237 if (part_end
&& src
< end
) {
238 debug("pe src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
239 src
, (ulong
)dst
, end
, part_start
, part_end
, aligned_start
, aligned_end
);
240 if ((mmc_block_read(mmc_buf
, aligned_end
, mmc_block_size
)) < 0)
243 memcpy(dst
, mmc_buf
, part_end
);
248 int mmc_write(uchar
*src
, ulong dst
, int size
)
250 ulong end
, part_start
, part_end
, part_len
, aligned_start
, aligned_end
;
251 ulong mmc_block_size
, mmc_block_address
;
257 printf("Please initialize the MMC first\n");
261 mmc_block_size
= MMC_BLOCK_SIZE
;
262 mmc_block_address
= ~(mmc_block_size
- 1);
266 part_start
= ~mmc_block_address
& dst
;
267 part_end
= ~mmc_block_address
& end
;
268 aligned_start
= mmc_block_address
& dst
;
269 aligned_end
= mmc_block_address
& end
;
271 /* all block aligned accesses */
272 debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
273 src
, (ulong
)dst
, end
, part_start
, part_end
, aligned_start
, aligned_end
);
275 part_len
= mmc_block_size
- part_start
;
276 debug("ps src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
277 (ulong
)src
, dst
, end
, part_start
, part_end
, aligned_start
, aligned_end
);
278 if ((mmc_block_read(mmc_buf
, aligned_start
, mmc_block_size
)) < 0)
281 memcpy(mmc_buf
+part_start
, src
, part_len
);
282 if ((mmc_block_write(aligned_start
, mmc_buf
, mmc_block_size
)) < 0)
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 for (; dst
< aligned_end
; src
+= mmc_block_size
, dst
+= mmc_block_size
) {
291 debug("al src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
292 src
, (ulong
)dst
, end
, part_start
, part_end
, aligned_start
, aligned_end
);
293 if ((mmc_block_write(dst
, (uchar
*)src
, mmc_block_size
)) < 0)
297 debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
298 src
, (ulong
)dst
, end
, part_start
, part_end
, aligned_start
, aligned_end
);
299 if (part_end
&& dst
< end
) {
300 debug("pe src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
301 src
, (ulong
)dst
, end
, part_start
, part_end
, aligned_start
, aligned_end
);
302 if ((mmc_block_read(mmc_buf
, aligned_end
, mmc_block_size
)) < 0)
305 memcpy(mmc_buf
, src
, part_end
);
306 if ((mmc_block_write(aligned_end
, mmc_buf
, mmc_block_size
)) < 0)
313 ulong
mmc_bread(int dev_num
, ulong blknr
, ulong blkcnt
, void *dst
)
315 int mmc_block_size
= MMC_BLOCK_SIZE
;
316 ulong src
= blknr
* mmc_block_size
+ CFG_MMC_BASE
;
318 mmc_read(src
, dst
, blkcnt
*mmc_block_size
);
322 /* MMC_DEFAULT_RCA should probably be just 1, but this may break other code
323 that expects it to be shifted. */
324 static u_int16_t rca
= MMC_DEFAULT_RCA
>> 16;
326 static u_int32_t
mmc_size(const struct mmc_csd
*csd
)
328 u_int32_t block_len
, mult
, blocknr
;
330 block_len
= csd
->read_bl_len
<< 12;
331 mult
= csd
->c_size_mult1
<< 8;
332 blocknr
= (csd
->c_size
+1) * mult
;
334 return blocknr
* block_len
;
338 char pnm_0
; /* product name */
339 char oid_1
; /* OEM/application ID */
341 uint8_t mid
; /* manufacturer ID */
346 uint8_t psn_2
; /* product serial number */
348 uint8_t psn_0
; /* MSB */
349 uint8_t prv
; /* product revision */
350 uint8_t crc
; /* CRC7 checksum, b0 is unused and set to 1 */
351 uint8_t mdt_1
; /* manufacturing date, LSB, RRRRyyyy yyyymmmm */
352 uint8_t mdt_0
; /* MSB */
353 uint8_t psn_3
; /* LSB */
356 static void print_mmc_cid(mmc_cid_t
*cid
)
358 printf("MMC found. Card desciption is:\n");
359 printf("Manufacturer ID = %02x%02x%02x\n",
360 cid
->id
[0], cid
->id
[1], cid
->id
[2]);
361 printf("HW/FW Revision = %x %x\n",cid
->hwrev
, cid
->fwrev
);
362 cid
->hwrev
= cid
->fwrev
= 0; /* null terminate string */
363 printf("Product Name = %s\n",cid
->name
);
364 printf("Serial Number = %02x%02x%02x\n",
365 cid
->sn
[0], cid
->sn
[1], cid
->sn
[2]);
366 printf("Month = %d\n",cid
->month
);
367 printf("Year = %d\n",1997 + cid
->year
);
370 static void print_sd_cid(const struct sd_cid
*cid
)
372 printf("Manufacturer: 0x%02x, OEM \"%c%c\"\n",
373 cid
->mid
, cid
->oid_0
, cid
->oid_1
);
374 printf("Product name: \"%c%c%c%c%c\", revision %d.%d\n",
375 cid
->pnm_0
, cid
->pnm_1
, cid
->pnm_2
, cid
->pnm_3
, cid
->pnm_4
,
376 cid
->prv
>> 4, cid
->prv
& 15);
377 printf("Serial number: %u\n",
378 cid
->psn_0
<< 24 | cid
->psn_1
<< 16 | cid
->psn_2
<< 8 |
380 printf("Manufacturing date: %d/%d\n",
382 2000+((cid
->mdt_0
& 15) << 4)+((cid
->mdt_1
& 0xf0) >> 4));
383 printf("CRC: 0x%02x, b0 = %d\n",
384 cid
->crc
>> 1, cid
->crc
& 1);
387 int mmc_init(int verbose
)
389 int retries
, rc
= -ENODEV
;
392 S3C24X0_CLOCK_POWER
* const clk_power
= S3C24X0_GetBase_CLOCK_POWER();
394 sdi
= S3C2410_GetBase_SDI();
396 debug("mmc_init(PCLK=%u)\n", get_PCLK());
398 clk_power
->CLKCON
|= (1 << 9);
401 #if defined(CONFIG_S3C2410)
402 /* S3C2410 has some bug that prevents reliable operation at higher speed */
403 //sdi->SDIPRE = 0x3e; /* SDCLK = PCLK/2 / (SDIPRE+1) = 396kHz */
404 sdi
->SDIPRE
= 0x02; /* 2410: SDCLK = PCLK/2 / (SDIPRE+1) = 11MHz */
405 sdi
->SDIDTIMER
= 0xffff;
406 #elif defined(CONFIG_S3C2440) || defined(CONFIG_S3C2442)
407 sdi
->SDIPRE
= 0x05; /* 2410: SDCLK = PCLK / (SDIPRE+1) = 11MHz */
408 sdi
->SDIDTIMER
= 0x7fffff;
411 sdi
->SDICON
= S3C2410_SDICON_FIFORESET
|S3C2410_SDICON_CLOCKTYPE
;
412 udelay(125000); /* FIXME: 74 SDCLK cycles */
418 resp
= mmc_cmd(MMC_CMD_RESET
, 0, 0);
420 printf("trying to detect SD Card...\n");
423 resp
= mmc_cmd(55, 0x00000000, CMD_F_RESP
);
424 resp
= mmc_cmd(41, 0x00300000, CMD_F_RESP
);
426 if (resp
[0] & (1 << 31)) {
432 if (retries
== 0 && !is_sd
) {
434 printf("failed to detect SD Card, trying MMC\n");
435 resp
= mmc_cmd(MMC_CMD_SEND_OP_COND
, 0x00ffc000, CMD_F_RESP
);
436 while (retries
-- && resp
&& !(resp
[4] & 0x80)) {
437 debug("resp %x %x\n", resp
[0], resp
[1]);
439 resp
= mmc_cmd(1, 0x00ffff00, CMD_F_RESP
);
443 /* try to get card id */
444 resp
= mmc_cmd(MMC_CMD_ALL_SEND_CID
, 0, CMD_F_RESP
|CMD_F_RESP_LONG
);
447 /* TODO configure mmc driver depending on card
449 mmc_cid_t
*cid
= (mmc_cid_t
*)resp
;
453 sprintf((char *) mmc_dev
.vendor
,
454 "Man %02x%02x%02x Snr %02x%02x%02x",
455 cid
->id
[0], cid
->id
[1], cid
->id
[2],
456 cid
->sn
[0], cid
->sn
[1], cid
->sn
[2]);
457 sprintf((char *) mmc_dev
.product
,"%s",cid
->name
);
458 sprintf((char *) mmc_dev
.revision
,"%x %x",
459 cid
->hwrev
, cid
->fwrev
);
462 struct sd_cid
*cid
= (struct sd_cid
*) resp
;
466 sprintf((char *) mmc_dev
.vendor
,
467 "Man %02 OEM %c%c \"%c%c%c%c%c\"",
468 cid
->mid
, cid
->oid_0
, cid
->oid_1
,
469 cid
->pnm_0
, cid
->pnm_1
, cid
->pnm_2
, cid
->pnm_3
,
471 sprintf((char *) mmc_dev
.product
, "%d",
472 cid
->psn_0
<< 24 | cid
->psn_1
<< 16 |
473 cid
->psn_2
<< 8 | cid
->psn_3
);
474 sprintf((char *) mmc_dev
.revision
, "%d.%d",
475 cid
->prv
>> 4, cid
->prv
& 15);
478 /* fill in device description */
479 mmc_dev
.if_type
= IF_TYPE_MMC
;
480 mmc_dev
.part_type
= PART_TYPE_DOS
;
484 /* FIXME fill in the correct size (is set to 32MByte) */
486 mmc_dev
.lba
= 0x10000;
487 mmc_dev
.removable
= 0;
488 mmc_dev
.block_read
= mmc_bread
;
490 /* MMC exists, get CSD too */
491 resp
= mmc_cmd(MMC_CMD_SET_RCA
, MMC_DEFAULT_RCA
, CMD_F_RESP
);
495 resp
= mmc_cmd(MMC_CMD_SEND_CSD
, rca
<<16, CMD_F_RESP
|CMD_F_RESP_LONG
);
497 mmc_csd_t
*csd
= (mmc_csd_t
*)resp
;
498 memcpy(&mmc_csd
, csd
, sizeof(csd
));
501 /* FIXME add verbose printout for csd */
502 printf("READ_BL_LEN=%u, C_SIZE_MULT=%u, C_SIZE=%u\n",
503 csd
->read_bl_len
, csd
->c_size_mult1
, csd
->c_size
);
504 printf("size = %u\n", mmc_size(csd
));
508 resp
= mmc_cmd(MMC_CMD_SELECT_CARD
, rca
<<16, CMD_F_RESP
);
510 #ifdef CONFIG_MMC_WIDE
512 resp
= mmc_cmd(55, rca
<<16, CMD_F_RESP
);
513 resp
= mmc_cmd(6, 0x02, CMD_F_RESP
);
518 fat_register_device(&mmc_dev
,1); /* partitions start counting with 1 */
524 mmc_ident(block_dev_desc_t
*dev
)
532 /* FIXME hard codes to 32 MB device */
533 if (addr
>= CFG_MMC_BASE
&& addr
< CFG_MMC_BASE
+ 0x02000000)
539 #endif /* defined(CONFIG_MMC) && defined(CONFIG_MMC_S3C) */