OSDN Git Service

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