OSDN Git Service

IR signal -> LED.
[kozos-expbrd/kozos_expbrd.git] / firm / sample / sample1 / os / 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 #include "portconf.h"\r
26 #include "spi.h"\r
27 \r
28 /*-------------------------------------------------------------------------*/\r
29 /* Platform dependent macros and functions needed to be modified           */\r
30 /*-------------------------------------------------------------------------*/\r
31 \r
32 #define INIT_PORT()     do { } while (0)    /* Initialize MMC control port (CS/CLK/DI:output, DO:input) */\r
33 #define DLY_US(n)       do { volatile uint32 dlycnt; for (dlycnt = 0; dlycnt < (n) * 10; dlycnt++) {} } while (0)    /* Delay n microseconds */\r
34 #define FORWARD(d)      do { } while (0)    /* Data in-time processing function (depends on the project) */\r
35 \r
36 /*--------------------------------------------------------------------------\r
37   Module Private Functions\r
38   ---------------------------------------------------------------------------*/\r
39 \r
40 /* Definitions for MMC/SDC command */\r
41 #define CMD0    (0x40+0)        /* GO_IDLE_STATE */\r
42 #define CMD1    (0x40+1)        /* SEND_OP_COND (MMC) */\r
43 #define ACMD41  (0xC0+41)       /* SEND_OP_COND (SDC) */\r
44 #define CMD8    (0x40+8)        /* SEND_IF_COND */\r
45 #define CMD16   (0x40+16)       /* SET_BLOCKLEN */\r
46 #define CMD17   (0x40+17)       /* READ_SINGLE_BLOCK */\r
47 #define CMD24   (0x40+24)       /* WRITE_BLOCK */\r
48 #define CMD55   (0x40+55)       /* APP_CMD */\r
49 #define CMD58   (0x40+58)       /* READ_OCR */\r
50 \r
51 /* Card type flags (CardType) */\r
52 #define CT_MMC                          0x01    /* MMC ver 3 */\r
53 #define CT_SD1                          0x02    /* SD ver 1 */\r
54 #define CT_SD2                          0x04    /* SD ver 2 */\r
55 #define CT_SDC                          (CT_SD1|CT_SD2) /* SD */\r
56 #define CT_BLOCK                        0x08    /* Block addressing */\r
57 \r
58 static BYTE CardType;                   /* b0:MMC, b1:SDv1, b2:SDv2, b3:Block addressing */\r
59 \r
60 /*-----------------------------------------------------------------------*/\r
61 /* Skip bytes on the MMC (bitbanging)                                    */\r
62 /*-----------------------------------------------------------------------*/\r
63 \r
64 /* Number of bytes to skip */\r
65 static void skip_mmc(WORD n)\r
66 {\r
67     do {\r
68         spi_rx();\r
69     } while (--n);\r
70 }\r
71 \r
72 /*-----------------------------------------------------------------------*/\r
73 /* Deselect the card and release SPI bus                                 */\r
74 /*-----------------------------------------------------------------------*/\r
75 \r
76 static void release_spi(void)\r
77 {\r
78     spi_deselect();\r
79     spi_rx();\r
80 }\r
81 \r
82 \r
83 /*-----------------------------------------------------------------------*/\r
84 /* Send a command packet to MMC                                          */\r
85 /*-----------------------------------------------------------------------*/\r
86 \r
87     static\r
88 BYTE send_cmd (\r
89         BYTE cmd,               /* Command byte */\r
90         DWORD arg               /* Argument */\r
91         )\r
92 {\r
93     BYTE n, res;\r
94 \r
95 \r
96     if (cmd & 0x80) {   /* ACMD<n> is the command sequense of CMD55-CMD<n> */\r
97         cmd &= 0x7F;\r
98         res = send_cmd(CMD55, 0);\r
99         if (res > 1) return res;\r
100     }\r
101 \r
102     /* Select the card */\r
103     spi_deselect(); spi_rx();\r
104     spi_select(SpiTarget_SDCARD); spi_rx();\r
105 \r
106     /* Send a command packet */\r
107     spi_tx(cmd);                                        /* Start + Command index */\r
108     spi_tx((BYTE)(arg >> 24));  /* Argument[31..24] */\r
109     spi_tx((BYTE)(arg >> 16));  /* Argument[23..16] */\r
110     spi_tx((BYTE)(arg >> 8));           /* Argument[15..8] */\r
111     spi_tx((BYTE)arg);                  /* Argument[7..0] */\r
112     n = 0x01;                                           /* Dummy CRC + Stop */\r
113     if (cmd == CMD0) n = 0x95;          /* Valid CRC for CMD0(0) */\r
114     if (cmd == CMD8) n = 0x87;          /* Valid CRC for CMD8(0x1AA) */\r
115     spi_tx(n);\r
116 \r
117     /* Receive a command response */\r
118     n = 10;                                                             /* Wait for a valid response in timeout of 10 attempts */\r
119     do {\r
120         res = spi_rx();\r
121     } while ((res & 0x80) && --n);\r
122 \r
123     return res;                 /* Return with the response value */\r
124 }\r
125 \r
126 \r
127 \r
128 /*--------------------------------------------------------------------------\r
129 \r
130   Public Functions\r
131 \r
132   ---------------------------------------------------------------------------*/\r
133 \r
134 \r
135 /*-----------------------------------------------------------------------*/\r
136 /* Initialize Disk Drive                                                 */\r
137 /*-----------------------------------------------------------------------*/\r
138 \r
139 DSTATUS disk_initialize (void)\r
140 {\r
141     BYTE n, cmd, ty, buf[4];\r
142     UINT tmr;\r
143 \r
144     INIT_PORT();\r
145 \r
146     spi_deselect();\r
147     skip_mmc(10);                       /* Dummy clocks */\r
148 \r
149     ty = 0;\r
150     if (send_cmd(CMD0, 0) == 1) {                       /* Enter Idle state */\r
151         if (send_cmd(CMD8, 0x1AA) == 1) {       /* SDv2 */\r
152             for (n = 0; n < 4; n++) buf[n] = spi_rx();  /* Get trailing return value of R7 resp */\r
153             if (buf[2] == 0x01 && buf[3] == 0xAA) {                     /* The card can work at vdd range of 2.7-3.6V */\r
154                 for (tmr = 1000; tmr; tmr--) {                          /* Wait for leaving idle state (ACMD41 with HCS bit) */\r
155                     if (send_cmd(ACMD41, 1UL << 30) == 0) break;\r
156                     DLY_US(1000);\r
157                 }\r
158                 if (tmr && send_cmd(CMD58, 0) == 0) {           /* Check CCS bit in the OCR */\r
159                     for (n = 0; n < 4; n++) buf[n] = spi_rx();\r
160                     ty = (buf[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2;  /* SDv2 (HC or SC) */\r
161                 }\r
162             }\r
163         } else {                                                        /* SDv1 or MMCv3 */\r
164             if (send_cmd(ACMD41, 0) <= 1)       {\r
165                 ty = CT_SD1; cmd = ACMD41;      /* SDv1 */\r
166             } else {\r
167                 ty = CT_MMC; cmd = CMD1;        /* MMCv3 */\r
168             }\r
169             for (tmr = 1000; tmr; tmr--) {                      /* Wait for leaving idle state */\r
170                 if (send_cmd(ACMD41, 0) == 0) break;\r
171                 DLY_US(1000);\r
172             }\r
173             if (!tmr || send_cmd(CMD16, 512) != 0)                      /* Set R/W block length to 512 */\r
174                 ty = 0;\r
175         }\r
176     }\r
177     CardType = ty;\r
178     release_spi();\r
179 \r
180     return ty ? 0 : STA_NOINIT;\r
181 }\r
182 \r
183 \r
184 \r
185 /*-----------------------------------------------------------------------*/\r
186 /* Read partial sector                                                   */\r
187 /*-----------------------------------------------------------------------*/\r
188 \r
189 DRESULT disk_readp (\r
190         BYTE *buff,             /* Pointer to the read buffer (NULL:Read bytes are forwarded to the stream) */\r
191         DWORD lba,              /* Sector number (LBA) */\r
192         WORD ofs,               /* Byte offset to read from (0..511) */\r
193         WORD cnt                /* Number of bytes to read (ofs + cnt mus be <= 512) */\r
194         )\r
195 {\r
196     DRESULT res;\r
197     BYTE d;\r
198     WORD bc, tmr;\r
199 \r
200 \r
201     if (!(CardType & CT_BLOCK)) lba *= 512;             /* Convert to byte address if needed */\r
202 \r
203     res = RES_ERROR;\r
204     if (send_cmd(CMD17, lba) == 0) {            /* READ_SINGLE_BLOCK */\r
205 \r
206         tmr = 1000;\r
207         do {                                                    /* Wait for data packet in timeout of 100ms */\r
208             DLY_US(100);\r
209             d = spi_rx();\r
210         } while (d == 0xFF && --tmr);\r
211 \r
212         if (d == 0xFE) {                                /* A data packet arrived */\r
213             bc = 514 - ofs - cnt;\r
214 \r
215             /* Skip leading bytes */\r
216             if (ofs) skip_mmc(ofs);\r
217 \r
218             /* Receive a part of the sector */\r
219             if (buff) { /* Store data to the memory */\r
220                 do\r
221                     *buff++ = spi_rx();\r
222                 while (--cnt);\r
223             } else {    /* Forward data to the outgoing stream */\r
224                 do {\r
225                     d = spi_rx();\r
226                     FORWARD(d);\r
227                 } while (--cnt);\r
228             }\r
229 \r
230             /* Skip trailing bytes and CRC */\r
231             skip_mmc(bc);\r
232 \r
233             res = RES_OK;\r
234         }\r
235     }\r
236 \r
237     release_spi();\r
238 \r
239     return res;\r
240 }\r
241 \r
242 \r
243 \r
244 /*-----------------------------------------------------------------------*/\r
245 /* Write partial sector                                                  */\r
246 /*-----------------------------------------------------------------------*/\r
247 #if _USE_WRITE\r
248 \r
249 DRESULT disk_writep (\r
250         const BYTE *buff,       /* Pointer to the bytes to be written (NULL:Initiate/Finalize sector write) */\r
251         DWORD sa                        /* Number of bytes to send, Sector number (LBA) or zero */\r
252         )\r
253 {\r
254     DRESULT res;\r
255     WORD bc, tmr;\r
256     static WORD wc;\r
257 \r
258 \r
259     res = RES_ERROR;\r
260 \r
261     if (buff) {         /* Send data bytes */\r
262         bc = (WORD)sa;\r
263         while (bc && wc) {              /* Send data bytes to the card */\r
264             spi_tx(*buff++);\r
265             wc--; bc--;\r
266         }\r
267         res = RES_OK;\r
268     } else {\r
269         if (sa) {       /* Initiate sector write process */\r
270             if (!(CardType & CT_BLOCK)) sa *= 512;      /* Convert to byte address if needed */\r
271             if (send_cmd(CMD24, sa) == 0) {                     /* WRITE_SINGLE_BLOCK */\r
272                 spi_tx(0xFF); spi_tx(0xFE);             /* Data block header */\r
273                 wc = 512;                                                       /* Set byte counter */\r
274                 res = RES_OK;\r
275             }\r
276         } else {        /* Finalize sector write process */\r
277             bc = wc + 2;\r
278             while (bc--) spi_tx(0);     /* Fill left bytes and CRC with zeros */\r
279             if ((spi_rx() & 0x1F) == 0x05) {    /* Receive data resp and wait for end of write process in timeout of 300ms */\r
280                 for (tmr = 10000; spi_rx() != 0xFF && tmr; tmr--)       /* Wait for ready (max 1000ms) */\r
281                     DLY_US(100);\r
282                 if (tmr) res = RES_OK;\r
283             }\r
284             release_spi();\r
285         }\r
286     }\r
287 \r
288     return res;\r
289 }\r
290 #endif\r