source: trunk/softs/tsar_boot/src/sdcard.c @ 501

Last change on this file since 501 was 501, checked in by bouyer, 11 years ago

Add support for >= v2, SDHC cards (larger than 2GB).
Tested with a 512MB < v2 and a 4GB cards.

File size: 8.7 KB
Line 
1/**
2 * \file    : sdcard.c
3 * \date    : 30 August 2012
4 * \author  : Cesar Fuguet
5 *
6 * This file defines the driver of a SD Card device using an SPI controller
7 */
8
9#include <sdcard.h>
10#include <boot_tty.h>
11
12/**
13 * \param   sdcard: Initialized pointer to the block device
14 *
15 * \return  void
16 *
17 * \brief   Enable SD Card select signal
18 */
19static void _sdcard_enable(struct sdcard_dev * sdcard)
20{
21    spi_ss_assert(sdcard->spi, sdcard->slave_id);
22}
23
24/**
25 * \param   sdcard: Initialized pointer to the block device
26 *
27 * \return  void
28 *
29 * \brief   Disable SD Card select signal
30 */
31static void _sdcard_disable(struct sdcard_dev * sdcard)
32{
33    spi_ss_deassert(sdcard->spi, sdcard->slave_id);
34}
35
36/**
37 * \param   tick_count: SD Card clock ticks number
38 *
39 * \return  void
40 *
41 * \brief   Enable SD Card clock
42 *          The tick count is byte measured (1 tick, 8 clock)
43 */
44static void _sdcard_gen_tick(struct sdcard_dev * sdcard, unsigned int tick_count)
45{
46    register int i = 0;
47    while(i++ < tick_count) spi_put_tx(sdcard->spi, 0xFF, 0);
48}
49
50/**
51 * \param   sdcard: Initialized pointer to the block device
52 *
53 * \return  char from the SD card
54 *
55 * \brief   Get a byte from the SD Card
56 */
57static unsigned char _sdcard_receive_char(struct sdcard_dev * sdcard)
58{
59    _sdcard_gen_tick(sdcard, 1);
60
61    return spi_get_rx(sdcard->spi, 0);
62}
63
64/**
65 * \param   sdcard: Initialized pointer to the block device
66 *
67 * \return  sdcard response
68 *
69 * \brief   Wait for a valid response after the send of a command
70 *          This function can return if one of the next two conditions are true:
71 *           1. Bit valid received
72 *           2. Timeout (not valid bit received after SDCARD_COMMAND_TIMEOUT
73 *              wait ticks)
74 */
75static unsigned char _sdcard_wait_response(struct sdcard_dev * sdcard)
76{
77    unsigned char sdcard_rsp;
78    register int  iter;
79
80    iter       = 0;
81    sdcard_rsp = _sdcard_receive_char(sdcard);
82    while (
83            (iter < SDCARD_COMMAND_TIMEOUT) &&
84            !SDCARD_CHECK_R1_VALID(sdcard_rsp)
85          )
86    {
87        sdcard_rsp = _sdcard_receive_char(sdcard);
88        iter++;
89    }
90
91    return sdcard_rsp;
92}
93
94/**
95 * \params  sdcard: Initialized pointer to the block device
96 *
97 * \return  void
98 *
99 * \brief   Wait data block start marker
100 */
101static void _sdcard_wait_data_block(struct sdcard_dev * sdcard)
102{
103        while (_sdcard_receive_char(sdcard) != 0xFE);
104}
105
106/**
107 * \param   sdcard  : Initialized pointer to block device
108 * \param   index   : SD card CMD index
109 * \param   app     : Type of command, 0 for normal command or 1 for application specific
110 * \param   args    : SD card CMD arguments
111 *
112 * \return  response first byte
113 *
114 * \brief   Send command to the SD card
115 */
116static int _sdcard_send_command    (
117        struct sdcard_dev * sdcard ,
118        int                 index  ,
119        int                 app    ,
120        void *              args   ,
121        unsigned            crc7   )
122{
123    unsigned char sdcard_rsp;
124    unsigned char * _args;
125
126    _sdcard_gen_tick(sdcard, 5); 
127
128    if (app == SDCARD_ACMD)
129    {
130        spi_put_tx(sdcard->spi, 0x40 | 55         , 0 );/* CMD and START bit */
131        spi_put_tx(sdcard->spi, 0x00              , 0 );/* Argument[0]       */
132        spi_put_tx(sdcard->spi, 0x00              , 0 );/* Argument[1]       */
133        spi_put_tx(sdcard->spi, 0x00              , 0 );/* Argument[2]       */
134        spi_put_tx(sdcard->spi, 0x00              , 0 );/* Argument[3]       */
135        spi_put_tx(sdcard->spi, 0x01 | (crc7 << 1), 0 );/* END bit           */
136
137        sdcard_rsp = _sdcard_wait_response(sdcard);
138        if (SDCARD_CHECK_R1_ERROR(sdcard_rsp))
139        {
140            return sdcard_rsp;       
141        }
142    }
143
144    _args = (unsigned char *) args;
145
146    _sdcard_gen_tick(sdcard, 1); 
147
148    spi_put_tx(sdcard->spi, 0x40 | index      , 0 );
149    spi_put_tx(sdcard->spi, _args[0]          , 0 );
150    spi_put_tx(sdcard->spi, _args[1]          , 0 );
151    spi_put_tx(sdcard->spi, _args[2]          , 0 );
152    spi_put_tx(sdcard->spi, _args[3]          , 0 );
153    spi_put_tx(sdcard->spi, 0x01 | (crc7 << 1), 0 );
154
155    return _sdcard_wait_response(sdcard);
156}
157
158int sdcard_dev_open(struct sdcard_dev * sdcard, struct spi_dev * spi, int ss)
159{
160        unsigned char args[4];
161        unsigned char sdcard_rsp;
162        unsigned int  iter, ersp;
163
164        sdcard->spi      = spi;
165        sdcard->slave_id = ss;
166
167        /*
168        * Supply SD card ramp up time (min 74 cycles)
169        */
170        _sdcard_gen_tick(sdcard, 10);
171
172        /*
173        * Assert slave select signal
174        * Send CMD0 (Reset Command)
175        * Deassert slave select signal
176        */
177        _sdcard_enable(sdcard);
178
179        args[0] = 0;
180        args[1] = 0;
181        args[2] = 0;
182        args[3] = 0;
183        sdcard_rsp = _sdcard_send_command(sdcard, 0, SDCARD_CMD, args, 0x4A);
184        if ( sdcard_rsp != 0x01 )
185        {
186                boot_puts("card CMD0 failed ");
187                return sdcard_rsp;
188        }
189
190        _sdcard_disable(sdcard);
191        /*
192         * send CMD8. If card is pre-v2, It will reply with illegal command.
193         * Otherwise we announce sdhc support.
194         */
195        _sdcard_enable(sdcard);
196        args[0] = 0;
197        args[1] = 0;
198        args[2] = 0x01;
199        args[3] = 0x01;
200        sdcard_rsp = _sdcard_send_command(sdcard, 8, SDCARD_CMD, args, 0x63);
201        if (!SDCARD_CHECK_R1_VALID(sdcard_rsp)) {
202                boot_puts("card CMD8 failed ");
203                return sdcard_rsp;
204        }
205        if (!SDCARD_CHECK_R1_ERROR(sdcard_rsp)) {
206                /* no error, command accepted. get whole reply */
207                ersp = _sdcard_receive_char(sdcard);
208                ersp = (ersp << 8) | _sdcard_receive_char(sdcard);
209                ersp = (ersp << 8) | _sdcard_receive_char(sdcard);
210                ersp = (ersp << 8) | _sdcard_receive_char(sdcard);
211                if ((ersp & 0xffff) != 0x0101) {
212                        /* voltage mismatch */
213                        boot_puts("card CMD8 mismatch: ");
214                        boot_putx(ersp);
215                        return sdcard_rsp;
216                }
217                boot_puts("v2 or later ");
218                sdcard->sdhc = 1;
219        } else if ((sdcard_rsp & SDCARD_R1_ILLEGAL_CMD) == 0) {
220                /* other error */
221                boot_puts("card CMD8 error ");
222                return sdcard_rsp;
223        } else {
224                sdcard->sdhc = 0;
225        }
226        _sdcard_disable(sdcard);
227        /* send CMD41, enabling the card */
228        _sdcard_enable(sdcard);
229        args[0] = sdcard->sdhc ? 0x40: 0;
230        args[1] = 0;
231        args[2] = 0;
232        args[3] = 0;
233
234        iter = 0;
235        while( iter++ < SDCARD_COMMAND_TIMEOUT )
236        {
237                sdcard_rsp = _sdcard_send_command(sdcard, 41, SDCARD_ACMD, args, 0x00);
238                if( sdcard_rsp == 0x01 )
239                {
240                        continue;
241                }
242
243                break;
244        }
245
246        _sdcard_disable(sdcard);
247        if (sdcard_rsp) {
248                boot_puts("SD ACMD41 failed ");
249                return sdcard_rsp;
250        }
251        if (sdcard->sdhc != 0) {
252                /* get the card capacity to see if it's really HC */
253                _sdcard_enable(sdcard);
254                args[0] = sdcard->sdhc ? 0x40: 0;
255                args[1] = 0;
256                args[2] = 0;
257                args[3] = 0;
258                sdcard_rsp = _sdcard_send_command(sdcard, 58, SDCARD_CMD,
259                    args, 0x00);
260                if (sdcard_rsp) {
261                        boot_puts("SD CMD58 failed ");
262                        return sdcard_rsp;
263                }
264                ersp = _sdcard_receive_char(sdcard);
265                ersp = (ersp << 8) | _sdcard_receive_char(sdcard);
266                ersp = (ersp << 8) | _sdcard_receive_char(sdcard);
267                ersp = (ersp << 8) | _sdcard_receive_char(sdcard);
268                if (ersp & 0x40000000) {
269                        boot_puts("SDHC ");
270                } else {
271                        sdcard->sdhc = 0;
272                }
273                _sdcard_disable(sdcard);
274        }
275        boot_puts("card detected ");
276        return 0;
277}
278
279int sdcard_dev_read(struct sdcard_dev * sdcard, void * buf, unsigned int count)
280{
281    unsigned char args[4];
282    unsigned char sdcard_rsp;
283    register int  i;
284
285    for (i = 0; i < 4; i++)
286    {
287        args[i] = (sdcard->access_pointer >> (32 - (i+1)*8)) & 0xFF;
288    }
289
290    _sdcard_enable(sdcard);
291
292    sdcard_rsp = _sdcard_send_command(sdcard, 17, SDCARD_CMD, args, 0x00);
293    if ( SDCARD_CHECK_R1_ERROR(sdcard_rsp) )
294    {
295        _sdcard_disable(sdcard);
296        return sdcard_rsp;
297    }
298
299    _sdcard_wait_data_block(sdcard);
300
301    spi_get_data(sdcard->spi, buf, count);
302
303    /*
304     * Get the remainder of the block bytes and the CRC16 (comes
305     * at the end of the data block)
306     */
307    i = count;
308    while( i++ < (512 + 2) ) _sdcard_receive_char(sdcard);
309
310    _sdcard_disable(sdcard);
311
312    /*
313     * Move the access pointer to the next block
314     */
315    sdcard->access_pointer += sdcard->block_length;
316
317    return 0;
318}
319
320unsigned int sdcard_dev_write(struct sdcard_dev *sdcard, void * buf, unsigned int count)
321{
322        return 0;
323}
324
325void sdcard_dev_lseek(struct sdcard_dev * sdcard, unsigned int blk_pos)
326{
327    sdcard->access_pointer = sdcard->block_length * blk_pos;
328}
329
330int sdcard_dev_set_blocklen(struct sdcard_dev * sdcard, unsigned int len)
331{
332    unsigned char args[4];
333    unsigned char sdcard_rsp;
334    register int i;
335
336    if (len != 512)
337        return 1;
338
339    if (sdcard->sdhc) {
340        sdcard->block_length = 1;
341        return 0;
342    }
343
344    for (i = 0; i < 4; i++)
345        args[i] = (len >> (32 - (i+1)*8)) & 0xFF;
346
347    _sdcard_enable(sdcard);
348
349    sdcard_rsp = _sdcard_send_command(sdcard, 16, SDCARD_CMD, args, 0x00);
350    if ( SDCARD_CHECK_R1_ERROR(sdcard_rsp) )
351    {
352        _sdcard_disable(sdcard);
353        return sdcard_rsp;
354    }
355
356    _sdcard_disable(sdcard);
357
358    sdcard->block_length = len;
359
360        return 0;
361}
362
363/*
364 * vim: tabstop=4 : shiftwidth=4 : expandtab : softtabstop=4
365 */
Note: See TracBrowser for help on using the repository browser.