]> git.sur5r.net Git - u-boot/blob - cpu/pxa/mmc.c
* Code cleanup:
[u-boot] / cpu / pxa / mmc.c
1 /*
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
32 extern int
33 fat_register_read(int(*block_read)(int device, ulong blknr, ulong blkcnt, uchar *buffer));
34
35 /*
36  * FIXME needs to read cid and csd info to determine block size
37  * and other parameters
38  */
39 static uchar mmc_buf[MMC_BLOCK_SIZE];
40 static mmc_csd_t mmc_csd;
41 static int mmc_ready = 0;
42
43
44 static uchar *
45 /****************************************************/
46 mmc_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));
57         MMC_CMD    = cmd;
58         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:
76                         words = 3;
77                         break;
78
79                 case MMC_CMDAT_R2:
80                         words = 8;
81                         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
104 int
105 /****************************************************/
106 mmc_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;
133         resp = mmc_cmd(MMC_CMD_READ_BLOCK, argh, argl,
134                         MMC_CMDAT_R1|MMC_CMDAT_READ|MMC_CMDAT_BLOCK|MMC_CMDAT_DATA_EN);
135
136
137         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
163 int
164 /****************************************************/
165 mmc_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;
191         resp = mmc_cmd(MMC_CMD_WRITE_BLOCK, argh, argl,
192                         MMC_CMDAT_R1|MMC_CMDAT_WRITE|MMC_CMDAT_BLOCK|MMC_CMDAT_DATA_EN);
193
194         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
232 int
233 /****************************************************/
234 mmc_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
303 int
304 /****************************************************/
305 mmc_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
382 int
383 /****************************************************/
384 mmc_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
394 int
395 /****************************************************/
396 mmc_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;
411         MMC_RESTO  = MMC_RES_TO_MAX;
412         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");
434                         printf("Manufacturer ID = %02x%02x%02x\n",
435                                                         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);
439                         printf("Serial Number = %02x%02x%02x\n",
440                                                         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
466 int
467 mmc_ident(block_dev_desc_t *dev)
468 {
469         return 0;
470 }
471
472 int
473 mmc2info(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