Root/qiboot/src/cpu/s3c2410/s3c24xx-mci.c

1/*
2 * qi s3c24xx SD card driver
3 * Author: Andy Green <andy@openmoko.com>
4 * based on ---->
5 *
6 * u-boot S3C2410 MMC/SD card driver
7 * (C) Copyright 2006 by OpenMoko, Inc.
8 * Author: Harald Welte <laforge@openmoko.org>
9 *
10 * based on u-boot pxa MMC driver and linux/drivers/mmc/s3c2410mci.c
11 * (C) 2005-2005 Thomas Kleffel
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License as
15 * published by the Free Software Foundation; either version 2 of
16 * the License, or (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
26 * MA 02111-1307 USA
27 */
28
29#include <qi.h>
30#include <mmc.h>
31#include <s3c24xx-regs-sdi.h>
32#include <string.h>
33
34#define SDICON (*(u32 *)0x5a000000)
35#define SDIPRE (*(u32 *)0x5a000004)
36#define SDICARG (*(u32 *)0x5a000008)
37#define SDICCON (*(u32 *)0x5a00000c)
38#define SDICSTA (*(u32 *)0x5a000010)
39#define SDIRSP0 (*(u32 *)0x5a000014)
40#define SDIRSP1 (*(u32 *)0x5a000018)
41#define SDIRSP2 (*(u32 *)0x5a00001c)
42#define SDIRSP3 (*(u32 *)0x5a000020)
43#define SDIDTIMER (*(u32 *)0x5a000024)
44#define SDIBSIZE (*(u32 *)0x5a000028)
45#define SDIDCON (*(u32 *)0x5a00002c)
46#define SDIDCNT (*(u32 *)0x5a000030)
47#define SDIDSTA (*(u32 *)0x5a000034)
48#define SDIFSTA (*(u32 *)0x5a000038)
49/* s3c2410 in GTA01 has these two last ones the other way around!!! */
50#define SDIIMSK (*(u32 *)0x5a00003c)
51#define SDIDAT (*(u32 *)0x5a000040)
52#define SDIDAT2410 (*(u32 *)0x5a00003c)
53#define SDIIMSK2410 (*(u32 *)0x5a000040)
54
55#define CFG_MMC_BASE 0xff000000
56
57int am_i_s3c2410(void)
58{
59    return 1;
60}
61
62#define CONFIG_MMC_WIDE
63#define MMC_BLOCK_SIZE 512
64
65/*
66 * FIXME needs to read cid and csd info to determine block size
67 * and other parameters
68 */
69static u8 mmc_buf[MMC_BLOCK_SIZE];
70static mmc_csd_t mmc_csd;
71static int mmc_ready = 0;
72static int wide = 0;
73static int is_sdhc = 0;
74
75
76#define CMD_F_RESP 0x01
77#define CMD_F_RESP_LONG 0x02
78
79static u32 *mmc_cmd(u16 cmd, u32 arg, u16 flags)
80{
81    static u32 resp[5];
82
83    u32 ccon, csta;
84    u32 csta_rdy_bit = S3C2410_SDICMDSTAT_CMDSENT;
85
86    memset(resp, 0, sizeof(resp));
87
88// debug("mmc_cmd CMD%d arg=0x%08x flags=%x\n", cmd, arg, flags);
89
90    SDICSTA = 0xffffffff;
91    SDIDSTA = 0xffffffff;
92    SDIFSTA = 0xffffffff;
93
94    SDICARG = arg;
95
96    ccon = cmd & S3C2410_SDICMDCON_INDEX;
97    ccon |= S3C2410_SDICMDCON_SENDERHOST|S3C2410_SDICMDCON_CMDSTART;
98
99    if (flags & CMD_F_RESP) {
100        ccon |= S3C2410_SDICMDCON_WAITRSP;
101        csta_rdy_bit = S3C2410_SDICMDSTAT_RSPFIN; /* 1 << 9 */
102    }
103
104    if (flags & CMD_F_RESP_LONG)
105        ccon |= S3C2410_SDICMDCON_LONGRSP;
106
107    SDICCON = ccon;
108
109    while (1) {
110        csta = SDICSTA;
111        if (csta & csta_rdy_bit)
112            break;
113        if (csta & S3C2410_SDICMDSTAT_CMDTIMEOUT) {
114            puts("===============> MMC CMD Timeout\n");
115            SDICSTA |= S3C2410_SDICMDSTAT_CMDTIMEOUT;
116            break;
117        }
118    }
119
120// debug("final MMC CMD status 0x%x\n", csta);
121
122    SDICSTA |= csta_rdy_bit;
123
124    if (flags & CMD_F_RESP) {
125        resp[0] = SDIRSP0;
126        resp[1] = SDIRSP1;
127        resp[2] = SDIRSP2;
128        resp[3] = SDIRSP3;
129    }
130
131    return resp;
132}
133
134#define FIFO_FILL() ((SDIFSTA & S3C2410_SDIFSTA_COUNTMASK) >> 2)
135
136static int mmc_block_read(u8 *dst, u32 src, u32 len)
137{
138    u32 dcon, fifo;
139    u32 *dst_u32 = (u32 *)dst;
140    u32 *resp;
141
142    if (len == 0)
143        return 0;
144
145// debug("mmc_block_rd dst %lx src %lx len %d\n", (u32)dst, src, len);
146
147    /* set block len */
148    resp = mmc_cmd(MMC_CMD_SET_BLOCKLEN, len, CMD_F_RESP);
149    SDIBSIZE = len;
150
151    //SDIPRE = 0xff;
152
153    /* setup data */
154    dcon = (len >> 9) & S3C2410_SDIDCON_BLKNUM;
155    dcon |= S3C2410_SDIDCON_BLOCKMODE;
156    dcon |= S3C2410_SDIDCON_RXAFTERCMD|S3C2410_SDIDCON_XFER_RXSTART;
157    if (wide)
158        dcon |= S3C2410_SDIDCON_WIDEBUS;
159
160    if (!am_i_s3c2410())
161        dcon |= S3C2440_SDIDCON_DS_WORD | S3C2440_SDIDCON_DATSTART;
162
163    SDIDCON = dcon;
164
165    /* send read command */
166    if (!is_sdhc)
167        resp = mmc_cmd(MMC_CMD_READ_BLOCK, src, CMD_F_RESP);
168    else
169        resp = mmc_cmd(MMC_CMD_READ_BLOCK, src / MMC_BLOCK_SIZE, CMD_F_RESP);
170
171    while (len > 0) {
172        u32 sdidsta = SDIDSTA;
173        fifo = FIFO_FILL();
174        if (sdidsta & (S3C2410_SDIDSTA_FIFOFAIL|
175                S3C2410_SDIDSTA_CRCFAIL|
176                S3C2410_SDIDSTA_RXCRCFAIL|
177                S3C2410_SDIDSTA_DATATIMEOUT)) {
178            puts("mmc_block_read: err SDIDSTA=0x");
179            print32(sdidsta);
180            puts("\n");
181            return -1;
182        }
183
184        if (am_i_s3c2410()) {
185            while (fifo--) {
186                //debug("dst_u32 = 0x%08x\n", dst_u32);
187                *(dst_u32++) = SDIDAT2410;
188                if (len >= 4)
189                    len -= 4;
190                else {
191                    len = 0;
192                    break;
193                }
194            }
195        } else {
196            while (fifo--) {
197                //debug("dst_u32 = 0x%08x\n", dst_u32);
198                *(dst_u32++) = SDIDAT;
199                if (len >= 4)
200                    len -= 4;
201                else {
202                    len = 0;
203                    break;
204                }
205            }
206        }
207    }
208
209// debug("waiting for SDIDSTA (currently 0x%08x\n", SDIDSTA);
210    while (!(SDIDSTA & (1 << 4))) {}
211// debug("done waiting for SDIDSTA (currently 0x%08x\n", SDIDSTA);
212
213    SDIDCON = 0;
214
215    if (!(SDIDSTA & S3C2410_SDIDSTA_XFERFINISH))
216        puts("mmc_block_read; transfer not finished!\n");
217
218    return 0;
219}
220
221static int mmc_block_write(u32 dst, u8 *src, int len)
222{
223    puts("MMC block write not yet supported on S3C2410!\n");
224    return -1;
225}
226
227
228int s3c24xx_mmc_read(u32 src, u8 *dst, int size)
229{
230    u32 end, part_start, part_end, part_len, aligned_start, aligned_end;
231    u32 mmc_block_size, mmc_block_address;
232
233    if (size == 0)
234        return 0;
235
236    if (!mmc_ready) {
237        puts("Please initialize the MMC first\n");
238        return -1;
239    }
240
241    mmc_block_size = MMC_BLOCK_SIZE;
242    mmc_block_address = ~(mmc_block_size - 1);
243
244    src -= CFG_MMC_BASE;
245    end = src + size;
246    part_start = ~mmc_block_address & src;
247    part_end = ~mmc_block_address & end;
248    aligned_start = mmc_block_address & src;
249    aligned_end = mmc_block_address & end;
250
251    /* all block aligned accesses */
252// debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
253// src, (u32)dst, end, part_start, part_end, aligned_start, aligned_end);
254    if (part_start) {
255        part_len = mmc_block_size - part_start;
256// debug("ps src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
257// src, (u32)dst, end, part_start, part_end, aligned_start, aligned_end);
258        if ((mmc_block_read(mmc_buf, aligned_start, mmc_block_size)) < 0)
259            return -1;
260
261        memcpy(dst, mmc_buf+part_start, part_len);
262        dst += part_len;
263        src += part_len;
264    }
265// debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
266// src, (u32)dst, end, part_start, part_end, aligned_start, aligned_end);
267    for (; src < aligned_end; src += mmc_block_size, dst += mmc_block_size) {
268// debug("al src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
269// src, (u32)dst, end, part_start, part_end, aligned_start, aligned_end);
270        if ((mmc_block_read((u8 *)(dst), src, mmc_block_size)) < 0)
271            return -1;
272    }
273// debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
274// src, (u32)dst, end, part_start, part_end, aligned_start, aligned_end);
275    if (part_end && src < end) {
276// debug("pe src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
277// src, (u32)dst, end, part_start, part_end, aligned_start, aligned_end);
278        if ((mmc_block_read(mmc_buf, aligned_end, mmc_block_size)) < 0)
279            return -1;
280
281        memcpy(dst, mmc_buf, part_end);
282    }
283    return 0;
284}
285
286int s3c24xx_mmc_write(u8 *src, u32 dst, int size)
287{
288    u32 end, part_start, part_end, part_len, aligned_start, aligned_end;
289    u32 mmc_block_size, mmc_block_address;
290
291    if (size == 0)
292        return 0;
293
294    if (!mmc_ready) {
295        puts("Please initialize the MMC first\n");
296        return -1;
297    }
298
299    mmc_block_size = MMC_BLOCK_SIZE;
300    mmc_block_address = ~(mmc_block_size - 1);
301
302    dst -= CFG_MMC_BASE;
303    end = dst + size;
304    part_start = ~mmc_block_address & dst;
305    part_end = ~mmc_block_address & end;
306    aligned_start = mmc_block_address & dst;
307    aligned_end = mmc_block_address & end;
308
309    /* all block aligned accesses */
310// debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
311// src, (u32)dst, end, part_start, part_end, aligned_start, aligned_end);
312    if (part_start) {
313        part_len = mmc_block_size - part_start;
314// debug("ps src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
315// (u32)src, dst, end, part_start, part_end, aligned_start, aligned_end);
316        if ((mmc_block_read(mmc_buf, aligned_start, mmc_block_size)) < 0)
317            return -1;
318
319        memcpy(mmc_buf+part_start, src, part_len);
320        if ((mmc_block_write(aligned_start, mmc_buf, mmc_block_size)) < 0)
321            return -1;
322
323        dst += part_len;
324        src += part_len;
325    }
326// debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
327// src, (u32)dst, end, part_start, part_end, aligned_start, aligned_end);
328    for (; dst < aligned_end; src += mmc_block_size, dst += mmc_block_size) {
329// debug("al src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
330// src, (u32)dst, end, part_start, part_end, aligned_start, aligned_end);
331        if ((mmc_block_write(dst, (u8 *)src, mmc_block_size)) < 0)
332            return -1;
333
334    }
335// debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
336// src, (u32)dst, end, part_start, part_end, aligned_start, aligned_end);
337    if (part_end && dst < end) {
338// debug("pe src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
339// src, (u32)dst, end, part_start, part_end, aligned_start, aligned_end);
340        if ((mmc_block_read(mmc_buf, aligned_end, mmc_block_size)) < 0)
341            return -1;
342
343        memcpy(mmc_buf, src, part_end);
344        if ((mmc_block_write(aligned_end, mmc_buf, mmc_block_size)) < 0)
345            return -1;
346
347    }
348    return 0;
349}
350
351u32 s3c24xx_mmc_bread(int dev_num, u32 blknr, u32 blkcnt, void *dst)
352{
353    int mmc_block_size = MMC_BLOCK_SIZE;
354    u32 src = blknr * mmc_block_size + CFG_MMC_BASE;
355
356    s3c24xx_mmc_read(src, dst, blkcnt*mmc_block_size);
357    return blkcnt;
358}
359
360/* MMC_DEFAULT_RCA should probably be just 1, but this may break other code
361   that expects it to be shifted. */
362static u_int16_t rca = MMC_DEFAULT_RCA >> 16;
363
364#if 0
365static u32 mmc_size(const struct mmc_csd *csd)
366{
367    u32 block_len, mult, blocknr;
368
369    block_len = csd->read_bl_len << 12;
370    mult = csd->c_size_mult1 << 8;
371    blocknr = (csd->c_size+1) * mult;
372
373    return blocknr * block_len;
374}
375#endif
376
377struct sd_cid {
378    char pnm_0; /* product name */
379    char oid_1; /* OEM/application ID */
380    char oid_0;
381    uint8_t mid; /* manufacturer ID */
382    char pnm_4;
383    char pnm_3;
384    char pnm_2;
385    char pnm_1;
386    uint8_t psn_2; /* product serial number */
387    uint8_t psn_1;
388    uint8_t psn_0; /* MSB */
389    uint8_t prv; /* product revision */
390    uint8_t crc; /* CRC7 checksum, b0 is unused and set to 1 */
391    uint8_t mdt_1; /* manufacturing date, LSB, RRRRyyyy yyyymmmm */
392    uint8_t mdt_0; /* MSB */
393    uint8_t psn_3; /* LSB */
394};
395
396static void print_mmc_cid(mmc_cid_t *cid)
397{
398    puts("MMC found. Card desciption is:\n");
399    puts("Manufacturer ID = ");
400    print8(cid->id[0]);
401    print8(cid->id[1]);
402    print8(cid->id[2]);
403    puts("\nHW/FW Revision = ");
404    print8(cid->hwrev);
405    print8(cid->fwrev);
406    cid->hwrev = cid->fwrev = 0; /* null terminate string */
407    puts("Product Name = ");
408    puts((char *)cid->name);
409    puts("\nSerial Number = ");
410    print8(cid->sn[0]);
411    print8(cid->sn[1]);
412    print8(cid->sn[2]);
413    puts("\nMonth = ");
414    printdec(cid->month);
415    puts("\nYear = ");
416    printdec(1997 + cid->year);
417    puts("\n");
418}
419
420static void print_sd_cid(const struct sd_cid *cid)
421{
422    puts("Manufacturer: 0x");
423    print8(cid->mid);
424    puts("OEM \"");
425    this_board->putc(cid->oid_0);
426    this_board->putc(cid->oid_1);
427    puts("\"\nProduct name: \"");
428    this_board->putc(cid->pnm_0);
429    this_board->putc(cid->pnm_1);
430    this_board->putc(cid->pnm_2);
431    this_board->putc(cid->pnm_3);
432    this_board->putc(cid->pnm_4);
433    puts("\", revision ");
434    printdec(cid->prv >> 4);
435    puts(".");
436    printdec(cid->prv & 15);
437    puts("\nSerial number: ");
438    printdec(cid->psn_0 << 24 | cid->psn_1 << 16 | cid->psn_2 << 8 |
439        cid->psn_3);
440    puts("\nManufacturing date: ");
441    printdec(cid->mdt_1 & 15);
442    puts("/");
443    printdec(2000+((cid->mdt_0 & 15) << 4)+((cid->mdt_1 & 0xf0) >> 4));
444    puts("\nCRC: 0x");
445    print8(cid->crc >> 1);
446    puts(" b0 = ");
447    print8(cid->crc & 1);
448    puts("\n");
449}
450
451int s3c24xx_mmc_init(int verbose)
452{
453     int retries, rc = -2;
454    int is_sd = 0;
455    u32 *resp;
456    u32 hcs = 0;
457
458    SDICON = S3C2410_SDICON_FIFORESET | S3C2410_SDICON_CLOCKTYPE;
459    SDIBSIZE = 512;
460    if (am_i_s3c2410()) {
461        /* S3C2410 has some bug that prevents reliable operation at higher speed */
462        //SDIPRE = 0x3e; /* SDCLK = PCLK/2 / (SDIPRE+1) = 396kHz */
463        SDIPRE = 0x02; /* 2410: SDCLK = PCLK/2 / (SDIPRE+1) = 11MHz */
464        SDIDTIMER = 0xffff;
465        SDIIMSK2410 = 0x0;
466    } else {
467        SDIPRE = 0x05; /* 2410: SDCLK = PCLK / (SDIPRE+1) = 11MHz */
468        SDIDTIMER = 0x7fffff;
469        SDIIMSK = 0x0;
470    }
471
472    udelay(1250000); /* FIXME: 74 SDCLK cycles */
473
474    mmc_csd.c_size = 0;
475
476    puts("Sending reset...\n");
477
478    /* reset */
479    retries = 10;
480    resp = mmc_cmd(MMC_CMD_RESET, 0, 0);
481
482    resp = mmc_cmd(8, 0x000001aa, CMD_F_RESP);
483    if ((resp[0] & 0xff) == 0xaa) {
484        puts("The card is either SD2.0 or SDHC\n");
485        hcs = 0x40000000;
486    }
487 
488    puts("trying to detect SD Card...\n");
489    while (retries--) {
490        udelay(1000000);
491        resp = mmc_cmd(55, 0x00000000, CMD_F_RESP);
492        resp = mmc_cmd(41, hcs | 0x00300000, CMD_F_RESP);
493
494        if (resp[0] & (1 << 30))
495            is_sdhc = 1;
496
497        if (resp[0] & (1 << 31)) {
498            is_sd = 1;
499            break;
500        }
501    }
502
503    if (retries < 0 && !is_sd)
504        return -3;
505
506    /* try to get card id */
507    resp = mmc_cmd(MMC_CMD_ALL_SEND_CID, hcs, CMD_F_RESP|CMD_F_RESP_LONG);
508    if (resp) {
509        if (!is_sd) {
510            /* TODO configure mmc driver depending on card
511               attributes */
512            mmc_cid_t *cid = (mmc_cid_t *)resp;
513
514            if (verbose)
515                print_mmc_cid(cid);
516#if 0
517            sprintf((char *) mmc_dev.vendor,
518                "Man %02x%02x%02x Snr %02x%02x%02x",
519                cid->id[0], cid->id[1], cid->id[2],
520                cid->sn[0], cid->sn[1], cid->sn[2]);
521            sprintf((char *) mmc_dev.product,"%s",cid->name);
522            sprintf((char *) mmc_dev.revision,"%x %x",
523                cid->hwrev, cid->fwrev);
524#endif
525        }
526        else {
527            struct sd_cid *cid = (struct sd_cid *) resp;
528
529            if (verbose)
530                print_sd_cid(cid);
531#if 0
532            sprintf((char *) mmc_dev.vendor,
533                "Man %02 OEM %c%c \"%c%c%c%c%c\"",
534                cid->mid, cid->oid_0, cid->oid_1,
535                cid->pnm_0, cid->pnm_1, cid->pnm_2, cid->pnm_3,
536                cid->pnm_4);
537            sprintf((char *) mmc_dev.product, "%d",
538                cid->psn_0 << 24 | cid->psn_1 << 16 |
539                cid->psn_2 << 8 | cid->psn_3);
540            sprintf((char *) mmc_dev.revision, "%d.%d",
541                cid->prv >> 4, cid->prv & 15);
542#endif
543        }
544
545
546        /* MMC exists, get CSD too */
547        resp = mmc_cmd(MMC_CMD_SET_RCA, MMC_DEFAULT_RCA, CMD_F_RESP);
548        if (is_sd)
549            rca = resp[0] >> 16;
550
551        resp = mmc_cmd(MMC_CMD_SEND_CSD, rca<<16, CMD_F_RESP|CMD_F_RESP_LONG);
552        if (resp) {
553            mmc_csd_t *csd = (mmc_csd_t *)resp;
554            memcpy(&mmc_csd, csd, sizeof(csd));
555            rc = 0;
556            mmc_ready = 1;
557#if 0
558            /* FIXME add verbose printout for csd */
559            printf("READ_BL_LEN=%u, C_SIZE_MULT=%u, C_SIZE=%u\n",
560                csd->read_bl_len, csd->c_size_mult1, csd->c_size);
561            printf("size = %u\n", mmc_size(csd));
562#endif
563        }
564    }
565
566    resp = mmc_cmd(MMC_CMD_SELECT_CARD, rca<<16, CMD_F_RESP);
567
568#ifdef CONFIG_MMC_WIDE
569    if (is_sd) {
570        resp = mmc_cmd(55, rca<<16, CMD_F_RESP);
571        resp = mmc_cmd(6, 0x02, CMD_F_RESP);
572        wide = 1;
573    }
574#endif
575
576    return rc;
577}
578
579
580

Archive Download this file



interactive