OSDN Git Service

Copied from Petit FAT File System Module by ChaN.
[kozos-expbrd/kozos_expbrd.git] / firm / 07 / bootload / mmc.c
1 /*------------------------------------------------------------------------/\r
2 /  Bitbanging MMCv3/SDv1/SDv2 (in SPI mode) control module for PFF\r
3 /-------------------------------------------------------------------------/\r
4 /\r
5 /  Copyright (C) 2010, ChaN, all right reserved.\r
6 /\r
7 / * This software is a free software and there is NO WARRANTY.\r
8 / * No restriction on use. You can use, modify and redistribute it for\r
9 /   personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY.\r
10 / * Redistributions of source code must retain the above copyright notice.\r
11 /\r
12 /--------------------------------------------------------------------------/\r
13  Features:\r
14 \r
15  * Very Easy to Port\r
16    It uses only 4-6 bit of GPIO port. No interrupt, no SPI port is used.\r
17 \r
18  * Platform Independent\r
19    You need to modify only a few macros to control GPIO ports.\r
20 \r
21 /-------------------------------------------------------------------------*/\r
22 \r
23 \r
24 #include "diskio.h"\r
25 \r
26 \r
27 /*-------------------------------------------------------------------------*/\r
28 /* Platform dependent macros and functions needed to be modified           */\r
29 /*-------------------------------------------------------------------------*/\r
30 \r
31 #include <hardware.h>                   /* Include hardware specific declareation file here */\r
32 \r
33 #define INIT_PORT()     init_port()     /* Initialize MMC control port (CS/CLK/DI:output, DO:input) */\r
34 #define DLY_US(n)       dly_us(n)       /* Delay n microseconds */\r
35 #define FORWARD(d)      forward(d)      /* Data in-time processing function (depends on the project) */\r
36 \r
37 #define CS_H()          bset(P0)        /* Set MMC CS "high" */\r
38 #define CS_L()          bclr(P0)        /* Set MMC CS "low" */\r
39 #define CK_H()          bset(P1)        /* Set MMC SCLK "high" */\r
40 #define CK_L()          bclr(P1)        /* Set MMC SCLK "low" */\r
41 #define DI_H()          bset(P2)        /* Set MMC DI "high" */\r
42 #define DI_L()          bclr(P2)        /* Set MMC DI "low" */\r
43 #define DO                      btest(P3)       /* Get MMC DO value (high:true, low:false) */\r
44 \r
45 \r
46 \r
47 /*--------------------------------------------------------------------------\r
48 \r
49    Module Private Functions\r
50 \r
51 ---------------------------------------------------------------------------*/\r
52 \r
53 /* Definitions for MMC/SDC command */\r
54 #define CMD0    (0x40+0)        /* GO_IDLE_STATE */\r
55 #define CMD1    (0x40+1)        /* SEND_OP_COND (MMC) */\r
56 #define ACMD41  (0xC0+41)       /* SEND_OP_COND (SDC) */\r
57 #define CMD8    (0x40+8)        /* SEND_IF_COND */\r
58 #define CMD16   (0x40+16)       /* SET_BLOCKLEN */\r
59 #define CMD17   (0x40+17)       /* READ_SINGLE_BLOCK */\r
60 #define CMD24   (0x40+24)       /* WRITE_BLOCK */\r
61 #define CMD55   (0x40+55)       /* APP_CMD */\r
62 #define CMD58   (0x40+58)       /* READ_OCR */\r
63 \r
64 /* Card type flags (CardType) */\r
65 #define CT_MMC                          0x01    /* MMC ver 3 */\r
66 #define CT_SD1                          0x02    /* SD ver 1 */\r
67 #define CT_SD2                          0x04    /* SD ver 2 */\r
68 #define CT_SDC                          (CT_SD1|CT_SD2) /* SD */\r
69 #define CT_BLOCK                        0x08    /* Block addressing */\r
70 \r
71 \r
72 \r
73 static\r
74 BYTE CardType;                  /* b0:MMC, b1:SDv1, b2:SDv2, b3:Block addressing */\r
75 \r
76 \r
77 \r
78 /*-----------------------------------------------------------------------*/\r
79 /* Transmit a byte to the MMC (bitbanging)                               */\r
80 /*-----------------------------------------------------------------------*/\r
81 \r
82 static\r
83 void xmit_mmc (\r
84         BYTE d                  /* Data to be sent */\r
85 )\r
86 {\r
87         if (d & 0x80) DI_H(); else DI_L();      /* bit7 */\r
88         CK_H(); CK_L();\r
89         if (d & 0x40) DI_H(); else DI_L();      /* bit6 */\r
90         CK_H(); CK_L();\r
91         if (d & 0x20) DI_H(); else DI_L();      /* bit5 */\r
92         CK_H(); CK_L();\r
93         if (d & 0x10) DI_H(); else DI_L();      /* bit4 */\r
94         CK_H(); CK_L();\r
95         if (d & 0x08) DI_H(); else DI_L();      /* bit3 */\r
96         CK_H(); CK_L();\r
97         if (d & 0x04) DI_H(); else DI_L();      /* bit2 */\r
98         CK_H(); CK_L();\r
99         if (d & 0x02) DI_H(); else DI_L();      /* bit1 */\r
100         CK_H(); CK_L();\r
101         if (d & 0x01) DI_H(); else DI_L();      /* bit0 */\r
102         CK_H(); CK_L();\r
103 }\r
104 \r
105 \r
106 \r
107 /*-----------------------------------------------------------------------*/\r
108 /* Receive a byte from the MMC (bitbanging)                              */\r
109 /*-----------------------------------------------------------------------*/\r
110 \r
111 static\r
112 BYTE rcvr_mmc (void)\r
113 {\r
114         BYTE r;\r
115 \r
116 \r
117         DI_H(); /* Send 0xFF */\r
118 \r
119         r = 0;   if (DO) r++;   /* bit7 */\r
120         CK_H(); CK_L();\r
121         r <<= 1; if (DO) r++;   /* bit6 */\r
122         CK_H(); CK_L();\r
123         r <<= 1; if (DO) r++;   /* bit5 */\r
124         CK_H(); CK_L();\r
125         r <<= 1; if (DO) r++;   /* bit4 */\r
126         CK_H(); CK_L();\r
127         r <<= 1; if (DO) r++;   /* bit3 */\r
128         CK_H(); CK_L();\r
129         r <<= 1; if (DO) r++;   /* bit2 */\r
130         CK_H(); CK_L();\r
131         r <<= 1; if (DO) r++;   /* bit1 */\r
132         CK_H(); CK_L();\r
133         r <<= 1; if (DO) r++;   /* bit0 */\r
134         CK_H(); CK_L();\r
135 \r
136         return r;\r
137 }\r
138 \r
139 \r
140 \r
141 /*-----------------------------------------------------------------------*/\r
142 /* Skip bytes on the MMC (bitbanging)                                    */\r
143 /*-----------------------------------------------------------------------*/\r
144 \r
145 static\r
146 void skip_mmc (\r
147         WORD n          /* Number of bytes to skip */\r
148 )\r
149 {\r
150         DI_H(); /* Send 0xFF */\r
151 \r
152         do {\r
153                 CK_H(); CK_L();\r
154                 CK_H(); CK_L();\r
155                 CK_H(); CK_L();\r
156                 CK_H(); CK_L();\r
157                 CK_H(); CK_L();\r
158                 CK_H(); CK_L();\r
159                 CK_H(); CK_L();\r
160                 CK_H(); CK_L();\r
161         } while (--n);\r
162 }\r
163 \r
164 \r
165 \r
166 /*-----------------------------------------------------------------------*/\r
167 /* Deselect the card and release SPI bus                                 */\r
168 /*-----------------------------------------------------------------------*/\r
169 \r
170 static\r
171 void release_spi (void)\r
172 {\r
173         CS_H();\r
174         rcvr_mmc();\r
175 }\r
176 \r
177 \r
178 /*-----------------------------------------------------------------------*/\r
179 /* Send a command packet to MMC                                          */\r
180 /*-----------------------------------------------------------------------*/\r
181 \r
182 static\r
183 BYTE send_cmd (\r
184         BYTE cmd,               /* Command byte */\r
185         DWORD arg               /* Argument */\r
186 )\r
187 {\r
188         BYTE n, res;\r
189 \r
190 \r
191         if (cmd & 0x80) {       /* ACMD<n> is the command sequense of CMD55-CMD<n> */\r
192                 cmd &= 0x7F;\r
193                 res = send_cmd(CMD55, 0);\r
194                 if (res > 1) return res;\r
195         }\r
196 \r
197         /* Select the card */\r
198         CS_H(); rcvr_mmc();\r
199         CS_L(); rcvr_mmc();\r
200 \r
201         /* Send a command packet */\r
202         xmit_mmc(cmd);                                  /* Start + Command index */\r
203         xmit_mmc((BYTE)(arg >> 24));    /* Argument[31..24] */\r
204         xmit_mmc((BYTE)(arg >> 16));    /* Argument[23..16] */\r
205         xmit_mmc((BYTE)(arg >> 8));             /* Argument[15..8] */\r
206         xmit_mmc((BYTE)arg);                    /* Argument[7..0] */\r
207         n = 0x01;                                               /* Dummy CRC + Stop */\r
208         if (cmd == CMD0) n = 0x95;              /* Valid CRC for CMD0(0) */\r
209         if (cmd == CMD8) n = 0x87;              /* Valid CRC for CMD8(0x1AA) */\r
210         xmit_mmc(n);\r
211 \r
212         /* Receive a command response */\r
213         n = 10;                                                         /* Wait for a valid response in timeout of 10 attempts */\r
214         do {\r
215                 res = rcvr_mmc();\r
216         } while ((res & 0x80) && --n);\r
217 \r
218         return res;                     /* Return with the response value */\r
219 }\r
220 \r
221 \r
222 \r
223 /*--------------------------------------------------------------------------\r
224 \r
225    Public Functions\r
226 \r
227 ---------------------------------------------------------------------------*/\r
228 \r
229 \r
230 /*-----------------------------------------------------------------------*/\r
231 /* Initialize Disk Drive                                                 */\r
232 /*-----------------------------------------------------------------------*/\r
233 \r
234 DSTATUS disk_initialize (void)\r
235 {\r
236         BYTE n, cmd, ty, buf[4];\r
237         UINT tmr;\r
238 \r
239 \r
240         INIT_PORT();\r
241 \r
242         CS_H();\r
243         skip_mmc(10);                   /* Dummy clocks */\r
244 \r
245         ty = 0;\r
246         if (send_cmd(CMD0, 0) == 1) {                   /* Enter Idle state */\r
247                 if (send_cmd(CMD8, 0x1AA) == 1) {       /* SDv2 */\r
248                         for (n = 0; n < 4; n++) buf[n] = rcvr_mmc();    /* Get trailing return value of R7 resp */\r
249                         if (buf[2] == 0x01 && buf[3] == 0xAA) {                 /* The card can work at vdd range of 2.7-3.6V */\r
250                                 for (tmr = 1000; tmr; tmr--) {                          /* Wait for leaving idle state (ACMD41 with HCS bit) */\r
251                                         if (send_cmd(ACMD41, 1UL << 30) == 0) break;\r
252                                         DLY_US(1000);\r
253                                 }\r
254                                 if (tmr && send_cmd(CMD58, 0) == 0) {           /* Check CCS bit in the OCR */\r
255                                         for (n = 0; n < 4; n++) buf[n] = rcvr_mmc();\r
256                                         ty = (buf[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2;      /* SDv2 (HC or SC) */\r
257                                 }\r
258                         }\r
259                 } else {                                                        /* SDv1 or MMCv3 */\r
260                         if (send_cmd(ACMD41, 0) <= 1)   {\r
261                                 ty = CT_SD1; cmd = ACMD41;      /* SDv1 */\r
262                         } else {\r
263                                 ty = CT_MMC; cmd = CMD1;        /* MMCv3 */\r
264                         }\r
265                         for (tmr = 1000; tmr; tmr--) {                  /* Wait for leaving idle state */\r
266                                 if (send_cmd(ACMD41, 0) == 0) break;\r
267                                 DLY_US(1000);\r
268                         }\r
269                         if (!tmr || send_cmd(CMD16, 512) != 0)                  /* Set R/W block length to 512 */\r
270                                 ty = 0;\r
271                 }\r
272         }\r
273         CardType = ty;\r
274         release_spi();\r
275 \r
276         return ty ? 0 : STA_NOINIT;\r
277 }\r
278 \r
279 \r
280 \r
281 /*-----------------------------------------------------------------------*/\r
282 /* Read partial sector                                                   */\r
283 /*-----------------------------------------------------------------------*/\r
284 \r
285 DRESULT disk_readp (\r
286         BYTE *buff,             /* Pointer to the read buffer (NULL:Read bytes are forwarded to the stream) */\r
287         DWORD lba,              /* Sector number (LBA) */\r
288         WORD ofs,               /* Byte offset to read from (0..511) */\r
289         WORD cnt                /* Number of bytes to read (ofs + cnt mus be <= 512) */\r
290 )\r
291 {\r
292         DRESULT res;\r
293         BYTE d;\r
294         WORD bc, tmr;\r
295 \r
296 \r
297         if (!(CardType & CT_BLOCK)) lba *= 512;         /* Convert to byte address if needed */\r
298 \r
299         res = RES_ERROR;\r
300         if (send_cmd(CMD17, lba) == 0) {                /* READ_SINGLE_BLOCK */\r
301 \r
302                 tmr = 1000;\r
303                 do {                                                    /* Wait for data packet in timeout of 100ms */\r
304                         DLY_US(100);\r
305                         d = rcvr_mmc();\r
306                 } while (d == 0xFF && --tmr);\r
307 \r
308                 if (d == 0xFE) {                                /* A data packet arrived */\r
309                         bc = 514 - ofs - cnt;\r
310 \r
311                         /* Skip leading bytes */\r
312                         if (ofs) skip_mmc(ofs);\r
313 \r
314                         /* Receive a part of the sector */\r
315                         if (buff) {     /* Store data to the memory */\r
316                                 do\r
317                                         *buff++ = rcvr_mmc();\r
318                                 while (--cnt);\r
319                         } else {        /* Forward data to the outgoing stream */\r
320                                 do {\r
321                                         d = rcvr_mmc();\r
322                                         FORWARD(d);\r
323                                 } while (--cnt);\r
324                         }\r
325 \r
326                         /* Skip trailing bytes and CRC */\r
327                         skip_mmc(bc);\r
328 \r
329                         res = RES_OK;\r
330                 }\r
331         }\r
332 \r
333         release_spi();\r
334 \r
335         return res;\r
336 }\r
337 \r
338 \r
339 \r
340 /*-----------------------------------------------------------------------*/\r
341 /* Write partial sector                                                  */\r
342 /*-----------------------------------------------------------------------*/\r
343 #if _USE_WRITE\r
344 \r
345 DRESULT disk_writep (\r
346         const BYTE *buff,       /* Pointer to the bytes to be written (NULL:Initiate/Finalize sector write) */\r
347         DWORD sa                        /* Number of bytes to send, Sector number (LBA) or zero */\r
348 )\r
349 {\r
350         DRESULT res;\r
351         WORD bc, tmr;\r
352         static WORD wc;\r
353 \r
354 \r
355         res = RES_ERROR;\r
356 \r
357         if (buff) {             /* Send data bytes */\r
358                 bc = (WORD)sa;\r
359                 while (bc && wc) {              /* Send data bytes to the card */\r
360                         xmit_mmc(*buff++);\r
361                         wc--; bc--;\r
362                 }\r
363                 res = RES_OK;\r
364         } else {\r
365                 if (sa) {       /* Initiate sector write process */\r
366                         if (!(CardType & CT_BLOCK)) sa *= 512;  /* Convert to byte address if needed */\r
367                         if (send_cmd(CMD24, sa) == 0) {                 /* WRITE_SINGLE_BLOCK */\r
368                                 xmit_mmc(0xFF); xmit_mmc(0xFE);         /* Data block header */\r
369                                 wc = 512;                                                       /* Set byte counter */\r
370                                 res = RES_OK;\r
371                         }\r
372                 } else {        /* Finalize sector write process */\r
373                         bc = wc + 2;\r
374                         while (bc--) xmit_mmc(0);       /* Fill left bytes and CRC with zeros */\r
375                         if ((rcvr_mmc() & 0x1F) == 0x05) {      /* Receive data resp and wait for end of write process in timeout of 300ms */\r
376                                 for (tmr = 10000; rcvr_mmc() != 0xFF && tmr; tmr--)     /* Wait for ready (max 1000ms) */\r
377                                         DLY_US(100);\r
378                                 if (tmr) res = RES_OK;\r
379                         }\r
380                         release_spi();\r
381                 }\r
382         }\r
383 \r
384         return res;\r
385 }\r
386 #endif\r