Add handy functions for handling non recurrent timeouts.
[bertos.git] / bertos / drv / sd_spi.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2007 Develer S.r.l. (http://www.develer.com/)
30  * -->
31  *
32  * \brief Function library for secure digital memory.
33  *
34  * \author Francesco Sacchi <batt@develer.com>
35  */
36
37
38 #include "hw/hw_sd.h"
39 #include "cfg/cfg_sd.h"
40
41 #include <io/kfile.h>
42 #include <io/kblock.h>
43
44 #include <drv/sd.h>
45 #include <drv/timer.h>
46
47 #include <fs/fat.h>
48
49 #define LOG_LEVEL  SD_LOG_LEVEL
50 #define LOG_FORMAT SD_LOG_FORMAT
51 #include <cfg/log.h>
52 #include <cpu/power.h>
53
54 #include <string.h> /* memset */
55
56
57 struct SdHardware
58 {
59         uint16_t tranfer_len; ///< Lenght for the read/write commands, cached in order to increase speed.
60 };
61
62
63 #define SD_IN_IDLE    0x01
64 #define SD_STARTTOKEN 0xFE
65
66 #define TIMEOUT_NAC   16384
67 #define SD_BUSY_TIMEOUT ms_to_ticks(200)
68
69 static bool sd_select(Sd *sd, bool state)
70 {
71         KFile *fd = sd->ch;
72
73         if (state)
74         {
75                 SD_CS_ON();
76
77                 ticks_t start = timer_clock();
78                 do
79                 {
80                         if (kfile_getc(fd) == 0xff)
81                                 return true;
82
83                         cpu_relax();
84                 }
85                 while (timer_clock() - start < SD_BUSY_TIMEOUT);
86
87                 SD_CS_OFF();
88                 LOG_ERR("sd_select timeout\n");
89                 return false;
90         }
91         else
92         {
93                 kfile_putc(0xff, fd);
94                 kfile_flush(fd);
95                 SD_CS_OFF();
96                 return true;
97         }
98 }
99
100 static int16_t sd_waitR1(Sd *sd)
101 {
102         uint8_t datain;
103
104         for (int i = 0; i < TIMEOUT_NAC; i++)
105         {
106                 datain = kfile_getc(sd->ch);
107                 if (datain != 0xff)
108                         return (int16_t)datain;
109         }
110         LOG_ERR("Timeout waiting R1\n");
111         return EOF;
112 }
113
114 static int16_t sd_sendCommand(Sd *sd, uint8_t cmd, uint32_t param, uint8_t crc)
115 {
116         KFile *fd = sd->ch;
117         /* The 7th bit of command must be a 1 */
118         kfile_putc(cmd | 0x40, fd);
119
120         /* send parameter */
121         kfile_putc((param >> 24) & 0xFF, fd);
122         kfile_putc((param >> 16) & 0xFF, fd);
123         kfile_putc((param >> 8) & 0xFF, fd);
124         kfile_putc((param) & 0xFF, fd);
125
126         kfile_putc(crc, fd);
127
128         return sd_waitR1(sd);
129 }
130
131 static bool sd_getBlock(Sd *sd, void *buf, size_t len)
132 {
133         uint8_t token;
134         uint16_t crc;
135
136         KFile *fd = sd->ch;
137
138         for (int i = 0; i < TIMEOUT_NAC; i++)
139         {
140                 token = kfile_getc(fd);
141                 if (token != 0xff)
142                 {
143                         if (token == SD_STARTTOKEN)
144                         {
145                                 if (kfile_read(fd, buf, len) == len)
146                                 {
147                                         if (kfile_read(fd, &crc, sizeof(crc)) == sizeof(crc))
148                                                 /* check CRC here if needed */
149                                                 return true;
150                                         else
151                                                 LOG_ERR("get_block error getting crc\n");
152                                 }
153                                 else
154                                         LOG_ERR("get_block len error: %d\n", (int)len);
155                         }
156                         else
157                                 LOG_ERR("get_block token error: %02X\n", token);
158
159                         return false;
160                 }
161         }
162
163         LOG_ERR("get_block timeout waiting token\n");
164         return false;
165 }
166
167 #define SD_SELECT(sd) \
168 do \
169 { \
170         if (!sd_select((sd), true)) \
171         { \
172                 LOG_ERR("%s failed, card busy\n", __func__); \
173                 return EOF; \
174         } \
175 } \
176 while (0)
177
178 #define SD_SETBLOCKLEN 0x50
179
180 static int16_t sd_setBlockLen(Sd *sd, uint32_t newlen)
181 {
182         SD_SELECT(sd);
183
184         sd->status = sd_sendCommand(sd, SD_SETBLOCKLEN, newlen, 0);
185
186         sd_select(sd, false);
187         return sd->status;
188 }
189
190 #define SD_SEND_CSD 0x49
191
192 static int16_t sd_getCSD(Sd *sd, SdCSD *csd)
193 {
194         SD_SELECT(sd);
195
196         int16_t r1 = sd_sendCommand(sd, SD_SEND_CSD, 0, 0);
197
198         if (r1)
199         {
200                 LOG_ERR("send_csd failed: %08lX\n", sd->status);
201                 sd_select(sd, false);
202                 return r1;
203         }
204
205         uint8_t buf[16];
206         bool res = sd_getBlock(sd, buf, sizeof(buf));
207         sd_select(sd, false);
208
209         if (res)
210         {
211                 #if LOG_LEVEL >= LOG_LVL_INFO
212                         LOG_INFO("CSD: [");
213                         for (int i = 0; i < 16; i++)
214                                 kprintf("%02X ", buf[i]);
215                         kprintf("]\n");
216                 #endif
217
218                 uint16_t mult = (1L << ((((buf[9] & 0x03) << 1) | ((buf[10] & 0x80) >> 7)) + 2));
219                 uint16_t c_size = (((uint16_t)(buf[6] & 0x03)) << 10) | (((uint16_t)buf[7]) << 2) |
220                                   (((uint16_t)(buf[8] & 0xC0)) >> 6);
221
222                 csd->block_len = (1L << (buf[5] & 0x0F));
223                 csd->block_num = (c_size + 1) * mult;
224                 csd->capacity = (csd->block_len * csd->block_num) >> 20; // in MB
225
226                 LOG_INFO("block_len %d bytes, block_num %ld, total capacity %dMB\n", csd->block_len, csd->block_num, csd->capacity);
227                 return 0;
228         }
229         else
230                 return EOF;
231 }
232
233 #define SD_START_DELAY  10
234 #define SD_INIT_TIMEOUT ms_to_ticks(2000)
235 #define SD_IDLE_RETRIES 4
236 #define SD_READ_SINGLEBLOCK 0x51
237
238 static size_t sd_SpiReadDirect(struct KBlock *b, block_idx_t idx, void *buf, size_t offset, size_t size)
239 {
240
241         Sd *sd = SD_CAST(b);
242         LOG_INFO("reading from block %ld, offset %d, size %d\n", idx, offset, size);
243
244         if (sd->hw->tranfer_len != size)
245         {
246                 if ((sd->status = sd_setBlockLen(sd, size)))
247                 {
248                         LOG_ERR("setBlockLen failed: %08lX\n", sd->status);
249                         return 0;
250                 }
251                 sd->hw->tranfer_len = size;
252         }
253
254         SD_SELECT(sd);
255
256         sd->status = sd_sendCommand(sd, SD_READ_SINGLEBLOCK, idx * SD_DEFAULT_BLOCKLEN + offset, 0);
257
258         if (sd->status)
259         {
260                 LOG_ERR("read single block failed: %08lX\n", sd->status);
261                 sd_select(sd, false);
262                 return 0;
263         }
264
265         bool res = sd_getBlock(sd, buf, size);
266         sd_select(sd, false);
267         if (!res)
268         {
269                 LOG_ERR("read single block failed reading data\n");
270                 return 0;
271         }
272         else
273                 return size;
274 }
275
276 #define SD_WRITE_SINGLEBLOCK 0x58
277 #define SD_DATA_ACCEPTED     0x05
278
279 static size_t sd_SpiWriteDirect(KBlock *b, block_idx_t idx, const void *buf, size_t offset, size_t size)
280 {
281         Sd *sd = SD_CAST(b);
282         KFile *fd = sd->ch;
283         ASSERT(offset == 0);
284         ASSERT(size == SD_DEFAULT_BLOCKLEN);
285
286         LOG_INFO("writing block %ld\n", idx);
287         if (sd->hw->tranfer_len != SD_DEFAULT_BLOCKLEN)
288         {
289                 if ((sd->status = sd_setBlockLen(sd, SD_DEFAULT_BLOCKLEN)))
290                 {
291                         LOG_ERR("setBlockLen failed: %08lX\n", sd->status);
292                         return 0;
293                 }
294                 sd->hw->tranfer_len = SD_DEFAULT_BLOCKLEN;
295         }
296
297         SD_SELECT(sd);
298
299         sd->status = sd_sendCommand(sd, SD_WRITE_SINGLEBLOCK, idx * SD_DEFAULT_BLOCKLEN, 0);
300
301         if (sd->status)
302         {
303                 LOG_ERR("write single block failed: %08lX\n", sd->status);
304                 sd_select(sd, false);
305                 return 0;
306         }
307
308         kfile_putc(SD_STARTTOKEN, fd);
309         kfile_write(fd, buf, SD_DEFAULT_BLOCKLEN);
310         /* send fake crc */
311         kfile_putc(0, fd);
312         kfile_putc(0, fd);
313
314         uint8_t dataresp = kfile_getc(fd);
315         sd_select(sd, false);
316
317         if ((dataresp & 0x1f) != SD_DATA_ACCEPTED)
318         {
319                 LOG_ERR("write block %ld failed: %02X\n", idx, dataresp);
320                 return EOF;
321         }
322
323         return SD_DEFAULT_BLOCKLEN;
324 }
325
326 static int sd_SpiError(KBlock *b)
327 {
328         Sd *sd = SD_CAST(b);
329         return sd->status;
330 }
331
332 static void sd_SpiClearerr(KBlock *b)
333 {
334         Sd *sd = SD_CAST(b);
335         sd->status = 0;
336 }
337
338
339
340 #define SD_GO_IDLE_STATE     0x40
341 #define SD_GO_IDLE_STATE_CRC 0x95
342 #define SD_SEND_OP_COND      0x41
343 #define SD_SEND_OP_COND_CRC  0xF9
344
345 static bool sd_blockInit(Sd *sd, KFile *ch)
346 {
347         ASSERT(sd);
348         ASSERT(ch);
349         memset(sd, 0, sizeof(*sd));
350         DB(sd->b.priv.type = KBT_SD);
351
352         sd->ch = ch;
353
354         SD_CS_INIT();
355         SD_CS_OFF();
356
357         /* Wait a few moments for supply voltage to stabilize */
358         timer_delay(SD_START_DELAY);
359
360         /* Give 80 clk pulses to wake up the card */
361         for (int i = 0; i < 10; i++)
362                 kfile_putc(0xff, ch);
363         kfile_flush(ch);
364
365         for (int i = 0; i < SD_IDLE_RETRIES; i++)
366         {
367                 SD_SELECT(sd);
368                 sd->status = sd_sendCommand(sd, SD_GO_IDLE_STATE, 0, SD_GO_IDLE_STATE_CRC);
369                 sd_select(sd, false);
370
371                 if (sd->status == SD_IN_IDLE)
372                         break;
373         }
374
375         if (sd->status != SD_IN_IDLE)
376         {
377                 LOG_ERR("go_idle_state failed: %08lX\n", sd->status);
378                 return false;
379         }
380
381         ticks_t start = timer_clock();
382
383         /* Wait for card to start */
384         do
385         {
386                 SD_SELECT(sd);
387                 sd->status = sd_sendCommand(sd, SD_SEND_OP_COND, 0, SD_SEND_OP_COND_CRC);
388                 sd_select(sd, false);
389                 cpu_relax();
390         }
391         while (sd->status != 0 && timer_clock() - start < SD_INIT_TIMEOUT);
392
393         if (sd->status)
394         {
395                 LOG_ERR("send_op_cond failed: %08lX\n", sd->status);
396                 return false;
397         }
398
399         sd->status = sd_setBlockLen(sd, SD_DEFAULT_BLOCKLEN);
400         sd->hw->tranfer_len = SD_DEFAULT_BLOCKLEN;
401
402         if (sd->status)
403         {
404                 LOG_ERR("setBlockLen failed: %08lX\n", sd->status);
405                 return false;
406         }
407
408         /* Avoid warning for uninitialized csd use (gcc bug?) */
409         SdCSD csd = csd;
410
411         sd->status = sd_getCSD(sd, &csd);
412
413         if (sd->status)
414         {
415                 LOG_ERR("getCSD failed: %08lX\n", sd->status);
416                 return false;
417         }
418
419         sd->b.blk_size = SD_DEFAULT_BLOCKLEN;
420         sd->b.blk_cnt = csd.block_num * (csd.block_len / SD_DEFAULT_BLOCKLEN);
421         LOG_INFO("blk_size %d, blk_cnt %ld\n", sd->b.blk_size, sd->b.blk_cnt);
422
423 #if CONFIG_SD_AUTOASSIGN_FAT
424         disk_assignDrive(&sd->b, 0);
425 #endif
426
427         return true;
428 }
429
430 static const KBlockVTable sd_unbuffered_vt =
431 {
432         .readDirect = sd_SpiReadDirect,
433         .writeDirect = sd_SpiWriteDirect,
434
435         .error = sd_SpiError,
436         .clearerr = sd_SpiClearerr,
437 };
438
439 static const KBlockVTable sd_buffered_vt =
440 {
441         .readDirect = sd_SpiReadDirect,
442         .writeDirect = sd_SpiWriteDirect,
443
444         .readBuf = kblock_swReadBuf,
445         .writeBuf = kblock_swWriteBuf,
446         .load = kblock_swLoad,
447         .store = kblock_swStore,
448
449         .error = sd_SpiError,
450         .clearerr = sd_SpiClearerr,
451 };
452
453 struct SdHardware sd_spi_hw;
454
455 bool sd_spi_initUnbuf(Sd *sd, KFile *ch)
456 {
457         if (sd_blockInit(sd, ch))
458         {
459                 sd->b.priv.vt = &sd_unbuffered_vt;
460                 sd->hw = &sd_spi_hw;
461                 return true;
462         }
463         else
464                 return false;
465 }
466
467 static uint8_t sd_buf[SD_DEFAULT_BLOCKLEN];
468
469 bool sd_spi_initBuf(Sd *sd, KFile *ch)
470 {
471         if (sd_blockInit(sd, ch))
472         {
473                 sd->b.priv.buf = sd_buf;
474                 sd->b.priv.flags |= KB_BUFFERED | KB_PARTIAL_WRITE;
475                 sd->b.priv.vt = &sd_buffered_vt;
476                 sd->b.priv.vt->load(&sd->b, 0);
477                 sd->hw = &sd_spi_hw;
478                 return true;
479         }
480         else
481                 return false;
482 }
483
484