source: trunk/modules/sdmmc/caba/source/src/sdmmc.cpp @ 979

Last change on this file since 979 was 709, checked in by cfuguet, 11 years ago

sdmmc:

  • Adding missing cstring header to includes
File size: 14.7 KB
RevLine 
[555]1/*
2 -*- c++ -*-
[552]3 *
4 * SOCLIB_LGPL_HEADER_BEGIN
5 *
6 * This file is part of SoCLib, GNU LGPLv2.1.
7 *
8 * SoCLib is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published
10 * by the Free Software Foundation; version 2.1 of the License.
11 *
12 * SoCLib is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with SoCLib; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 * 02110-1301 USA
21 *
22 * SOCLIB_LGPL_HEADER_END
23 *
24 * Copyright (c) UPMC, Lip6, SoC
25 *         manuel.bouyer@lip6.fr october 2013
26 *
27 * Maintainers: bouyer
28 */
29
30#include <stdint.h>
31#include <errno.h>
[709]32#include <cstring>
[552]33#include <iostream>
34#include <fcntl.h>
35#include <cassert>
36#include "sdmmc.h"
37
38namespace soclib { namespace caba {
39
40using namespace soclib::caba;
41
42////////////////////////
[557]43void SdMMC::genMealy()
[552]44{
45    if(p_resetn.read() == false) 
46    {
[556]47        spi_fsm  = S_IDLE;
[552]48        m_acmd     = false;     
49        m_sdstate  = SD_IDLE;
50        return;
51    } 
52    if (p_spi_ss.read()) {
[556]53        if (spi_fsm != S_IDLE) {
[555]54                std::cerr << name() << " deselect but not idle, state "
[556]55                << std::dec << spi_fsm << " last cmd " << (int)command
56                << " args " << std::hex << args << std::dec
57                << " bitcount " << (int)spi_bitcount
[555]58                << " idx " << m_data_idx << " len_snd " << m_datalen_snd
59                << " len_rcv " << m_datalen_rcv << std::endl;
60        }
[556]61        spi_fsm  = S_IDLE;
62        spi_clk = p_spi_clk;
[574]63        spi_mosi_previous = p_spi_mosi;
[552]64        return;
65    }
66
[556]67    switch(spi_fsm) {
[552]68    case S_IDLE:
[556]69        if (p_spi_clk.read() == 1 && spi_clk == 0) {
[552]70                // rising edge
[574]71                command = (command << 1) | spi_mosi_previous;
[556]72                spi_bitcount = 6;
73                spi_fsm = S_RECEIVE_CMD;
[552]74        }
75        break;
76    case S_RECEIVE_CMD:
[556]77        if (p_spi_clk.read() == 1 && spi_clk == 0) {
[552]78                // rising edge
[574]79                command = (command << 1) | spi_mosi_previous;
[556]80                if (spi_bitcount == 0) {
81                        if ((command & 0x80) == 0) {
82                                spi_fsm = S_RECEIVE_ARGS_START;
[552]83                        } else {
[555]84#ifdef SOCLIB_MODULE_DEBUG0
[556]85                                std::cout << name() << " S_RECEIVE_CMD " << std::hex << (int)command << std::endl;
[552]86#endif
[556]87                                spi_fsm = S_IDLE;
[552]88                        }
[556]89                } else {
90                    spi_bitcount = spi_bitcount - 1;
[552]91                }
92        }
93        break;
94    case S_RECEIVE_ARGS_START:
[556]95        if (p_spi_clk.read() == 1 && spi_clk == 0) {
[552]96                // rising edge
[574]97                args = (args << 1) | spi_mosi_previous;
[556]98                spi_bitcount = 30;
99                spi_fsm = S_RECEIVE_ARGS;
[552]100        }
101        break;
102    case S_RECEIVE_ARGS:
[556]103        if (p_spi_clk.read() == 1 && spi_clk == 0) {
[552]104                // rising edge
[574]105                args = (args << 1) | spi_mosi_previous;
[556]106                if (spi_bitcount == 0) {
107                        spi_bitcount = 7;
108                        spi_fsm = S_RECEIVE_CRC;
109                } else {
110                    spi_bitcount = spi_bitcount - 1;
[552]111                }
112        }
113        break;
114    case S_RECEIVE_CRC:
[556]115        if (p_spi_clk.read() == 1 && spi_clk == 0) {
[552]116                // rising edge
[574]117                cmdcrc = (cmdcrc << 1) | spi_mosi_previous;
[556]118                if (spi_bitcount == 0) {
119                        handle_sdmmc_cmd(command, args);
120                        spi_bitcount = 0; // SEND_DATA will reset it
121                        spi_fsm = S_SEND_DATA;
[552]122                        m_data_idx = 0;
123                } else {
[556]124                        spi_bitcount = spi_bitcount - 1;
[552]125                }
126        }
127        break;
128       
129    case S_SEND_DATA:
[556]130        if (p_spi_clk.read() == 0 && spi_clk == 1) {
[552]131                // falling edge
[556]132                if (spi_bitcount == 0) {
[552]133                        if (m_data_idx != m_datalen_snd) {     
[556]134                                spi_shiftreg = m_databuf[m_data_idx];
135                                spi_bitcount = 7;
136                                spi_fsm = S_SEND_DATA;
[552]137                                m_data_idx++;
[555]138#ifdef SOCLIB_MODULE_DEBUG0
[552]139                        std::cout << name() << " S_SEND_DATA " << std::dec << m_datalen_snd << " idx " << m_data_idx << " " << std::hex << (uint32_t)m_databuf[m_data_idx] << std::endl;
140#endif
141                        } else if (m_datalen_rcv != 0) {
[556]142                                spi_fsm = S_RECEIVE_DATA_WAIT;
143                                spi_bitcount = 7;
[552]144                                m_data_idx = 0;
145                        } else {
[556]146                                spi_fsm = S_IDLE;
[552]147                        }
148                } else {
[556]149                        spi_bitcount = spi_bitcount - 1;
150                        spi_shiftreg = spi_shiftreg << 1;
[552]151                }
152        }
153        break;
[555]154    case S_RECEIVE_DATA_WAIT:
[556]155        if (p_spi_clk.read() == 1 && spi_clk == 0) {
156            // rising edge
[555]157            uint8_t s_data;
[574]158            s_data = (m_databuf[0] << 1) | spi_mosi_previous;
[555]159            m_databuf[0] = s_data;
[556]160            if (spi_bitcount == 0) {
[555]161#ifdef SOCLIB_MODULE_DEBUG
162        std::cout << name() << " S_RECEIVE_DATA_WAIT " << std::dec << (int)s_data << std::endl;
163#endif
[556]164                    spi_bitcount = 7;
[555]165                    if (s_data == 0xfe) { // data start token
[556]166                        spi_fsm = S_RECEIVE_DATA;
[555]167                        m_data_idx = 1;
168                    } else {
169#ifdef SOCLIB_MODULE_DEBUG
170                        std::cout << name() << " S_RECEIVE_DATA_WAIT " << std::hex << (int)s_data << std::endl;
171#endif
[556]172                        spi_fsm = S_RECEIVE_DATA_WAIT;
[555]173                }
[556]174            } else {
175                spi_bitcount = spi_bitcount - 1;
[555]176            }
177        }
178        break;
179        case S_RECEIVE_DATA:
[556]180            if (p_spi_clk.read() == 1 && spi_clk == 0) {
[555]181                // rising edge
[574]182                m_databuf[m_data_idx] = (m_databuf[m_data_idx] << 1) | spi_mosi_previous;
[556]183                if (spi_bitcount == 0) {
[555]184                    m_data_idx++;
185                    if (m_data_idx != m_datalen_rcv) {
[556]186                        spi_fsm = S_RECEIVE_DATA;
187                        spi_bitcount = 7;
[555]188                    } else {
[556]189                        handle_sdmmc_write(command, args);
[555]190                        if (m_datalen_snd > 0) {
[556]191                            spi_bitcount = 0; // SEND_DATA will reset it
192                            spi_fsm = S_SEND_DATA;
[555]193                            m_data_idx = 0;
194                        } else {
[556]195                            spi_fsm = S_IDLE;
[555]196                        }
197                    }
198                } else {
[556]199                        spi_bitcount = spi_bitcount - 1;
[555]200                }
201            }
202            break;
[552]203    }
204
[557]205//// now genrate output signal
206
[556]207    switch(spi_fsm) {
[552]208    case S_IDLE:
209        p_spi_miso = !p_spi_ss.read();
210        break;
211    case S_SEND_DATA:
[556]212        p_spi_miso = (spi_shiftreg & 0x80) != 0;
[552]213        break;
214    default:
215        p_spi_miso = !p_spi_ss.read();
216        break;
217    }
[557]218    spi_clk = p_spi_clk.read();
[574]219    spi_mosi_previous = p_spi_mosi;
[557]220} // end GenMealy()
[552]221
222
223//////////////////////
224void SdMMC::handle_sdmmc_cmd(uint8_t cmd, uint32_t data)
225{
226        m_datalen_rcv = 0;
227        m_databuf[0] = 0x04; // illegal command
228        m_datalen_snd = 1;
229
230        if (m_sdstate == SD_IDLE)
231                m_databuf[0] |= 0x01; // idle
232
233        if ((cmd & 0x40) == 0) {
234                //illegal command
235                return;
236        }
237        cmd &= 0x3f;
238        if (m_acmd) {
[555]239#ifdef SOCLIB_MODULE_DEBUG0
[556]240        std::cout << name() << " new acmd " << std::dec << (int)cmd << " args " << std::hex << data << " crc " << (int)cmdcrc << std::endl;
[552]241#endif
242            m_acmd = false;
243            switch (cmd) {
244            case 41:
245                m_databuf[0] = 0x0; // card ready
246                m_datalen_snd = 1;
247                m_sdstate = SD_READY;
248                break;
249            case 51:
250                // send SCR
251                m_databuf[ 0] = (m_sdstate == SD_IDLE) ? 0x1 : 0x0; // R1
252                m_databuf[ 1] = 0xfe; // data token
253                m_databuf[ 2] = 0x00; // SCR_STRUCTURE / SD_SPEC
254                m_databuf[ 3] = 0x05; // DATA_STAT_AFTER_ERASE, SD_SECURITY, SD_BUS_WIDTHS
255                m_databuf[ 4] = 0;    // SD_SPEC3, EX_SECURITY, SD_SPEC4
256                m_databuf[ 5] = 0;    // CMD_SUPPORT
257                m_databuf[ 6] = 0;    // vendor specific
258                m_databuf[ 7] = 0;    // vendor specific
259                m_databuf[ 8] = 0;    // vendor specific
260                m_databuf[ 9] = 0;    // vendor specific
261                m_databuf[10] = 0x0;  // CRC16
262                m_databuf[11] = 0x0;  // CRC16
263                m_datalen_snd = 12;
264                break;
265            default:
266                std::cout << name() << " unknown acmd " << std::dec
267                    << (int)cmd << std::endl;
268                break; // return illegal command
269            }
270        } else {
[555]271#ifdef SOCLIB_MODULE_DEBUG0
[556]272        std::cout << name() << " new cmd " << std::dec << (int)cmd << " args " << std::hex << data << " crc " << (int)cmdcrc << std::endl;
[552]273#endif
274            switch (cmd) {
275            case 0:
276                m_databuf[0] = 0x1;
277                m_datalen_snd = 1;
278                m_sdstate = SD_IDLE;
279                break;
280            case 8:
281                // reply with illegal command for now
282                break;
283            case 9:
284              {
285                // send CSD
286                // we use a block len of 1024
287                uint32_t csize = ((m_device_size + (512 * 1024) - 1) / (512 * 1024)) - 1;
288                m_databuf[ 0]  = (m_sdstate == SD_IDLE) ? 0x1 : 0x0; // R1
289                m_databuf[ 1]  = 0xfe; // data token
290                m_databuf[ 2]  = 0x00; // CSD_STRUCTURE
291                m_databuf[ 3]  = 0xe;  // TAAC
292                m_databuf[ 4]  = 0;    // NSAC
293                m_databuf[ 5]  = 0x32; // TRAN_SPEED
294                m_databuf[ 6]  = 0x5b; // CCC_H
295                m_databuf[ 7]  = 0x5a; // CCC_L + READ_BL_LEN
296                m_databuf[ 8]  = 0x80; // READ_BL_PARTIAL, R/W_BLK_MISALIGN, DSR_IMP
297                m_databuf[ 8] |= (csize >> 10) & 0x03; // CSIZE[12-11]
298                m_databuf[ 9]  = (csize >>  2) & 0xff; // CSIZE[10-2]
299                m_databuf[10]  = (csize <<  6) & 0xc0; // CSIZE[1-0]
300                m_databuf[10] |= 0;    // R_CURR_MIN, R_CURR_MAX
301                m_databuf[11]  = 0x3;  // W_CURR_MIN, W_CURR_MAX, CSIZE_MULT[2-1];
302                m_databuf[12]  = 0xff; // CSIZE_MULT[1], ERASE_BLK_EN, ERASE_SECTOR_SIZE[6-1]
303                m_databuf[13]  = 0x80; // ERASE_SECTOR_SIZE[0]. WP_GRP_SIZE
304                m_databuf[14]  = 0x0a; // WP_GRP_ENABLE, R2W_FACTOR, WRITE_BL_LEN[2-3]
305                m_databuf[15]  = 0x40; // WRITE_BL_LEN[0-1], WR_BL_PARTIAL
306                m_databuf[16]  = 0;    // FILE_FORMAT
307                m_databuf[17]  = 0x1;  // CRC7
308                m_databuf[18]  = 0x0;  // CRC16
309                m_databuf[19]  = 0x0;  // CRC16
310                m_datalen_snd  = 20;
311                break;
312              }
313            case 10:
314                // send CID
315                m_databuf[ 0] = (m_sdstate == SD_IDLE) ? 0x1 : 0x0; // R1
316                m_databuf[ 1] = 0xfe; // data token
317                m_databuf[ 2] = 0xda; // MID
318                m_databuf[ 3] = 'P';  // OID
319                m_databuf[ 4] = '6';  // OID
320                m_databuf[ 5] = 's';  // PNM
321                m_databuf[ 6] = 'o';  // PNM
322                m_databuf[ 7] = 'c';  // PNM
323                m_databuf[ 8] = 's';  // PNM
324                m_databuf[ 9] = 'd';  // PNM
325                m_databuf[10] = 0x01; // PRV
326                m_databuf[11] = 0xde; // PSN
327                m_databuf[12] = 0xad; // PSN
328                m_databuf[13] = 0xbe; // PSN
329                m_databuf[14] = 0xef; // PSN
330                m_databuf[15] = 10;   // MDT
331                m_databuf[16] = 13;   // MDT
332                m_databuf[17] = 0x1;  // CRC7
333                m_databuf[18] = 0x0;  // CRC16
334                m_databuf[19] = 0x0;  // CRC16
335                m_datalen_snd = 20;
336                break;
337            case 16:
338                // set block size
339                if (m_sdstate != SD_IDLE && data == 512) {
340                        m_databuf[0] = 0x00;
341                        m_datalen_snd = 1;
342                } // else illegal command
343                break;
344            case 17:
345              {
346                int ret;
347                // read data block
348                if (m_sdstate == SD_IDLE) {
349                        // return illegal command
350                        return;
351                }
352                if (data >= m_device_size) {
353                        std::cerr << name() << " read: request " << data
354                            << " past end of file " << m_device_size << std::endl;
355                        m_databuf[0] = 0x00; // R1 OK
356                        m_databuf[1] = 0x08; // error tocken "out of range"
357                        m_datalen_snd = 2;
358                        return;
359                }
360                do {
361                        if (lseek(m_fd, data, SEEK_SET) < 0) {
362                                std::cerr << name() << " lseek: " <<
363                                  strerror(errno) << std::endl;
364                                m_databuf[0] = 0x00; // R1 OK
365                                m_databuf[1] = 0x02; // error tocken "CC err"
366                                m_datalen_snd = 2;
367                                return;
368                        }
369                        ret = read(m_fd, &m_databuf[2], 512);
370                } while (ret < 0 && errno == EINTR);
371                if (ret < 0) {
372                        std::cerr << name() << " read: " <<
373                          strerror(errno) << std::endl;
374                        m_databuf[0] = 0x00; // R1 OK
375                        m_databuf[1] = 0x04; // error tocken "card ECC failed"
376                        m_datalen_snd = 2;
377                        return;
378                }
379                m_databuf[514] = m_databuf[515] = 0; // XXX CRC
380                m_databuf[0] = 0x0; // R1
381                m_databuf[1] = 0xfe; // start block tocken
382                m_datalen_snd = 516;
383                break;
384              }
[555]385            case 24:
386              {
387                // write data block
388                if (m_sdstate == SD_IDLE) {
389                        // return illegal command
390                        return;
391                }
392#ifdef SOCLIB_MODULE_DEBUG
393        std::cout << name() << " new cmd write " << std::dec << (int)cmd << " args " << std::hex << data << std::endl;
394#endif
395                m_databuf[0] = 0x0; // R1
396                m_datalen_snd = 1;
397                m_datalen_rcv = 512 + 2 + 1; // data + tocken + CRC
398                break;
399              }
[552]400            case 55:
401                // app-specific command follow
402                m_acmd = true;
403                m_databuf[0] = (m_sdstate == SD_IDLE) ? 0x1 : 0x0;
404                m_datalen_snd = 1;
405                break;
406            case 58:
407                // send OCR
408                m_databuf[4] = (m_sdstate == SD_IDLE) ? 0x1 : 0x0; // R1
409                m_databuf[3] = 0x80; // power up complete, SDSC
410                m_databuf[2] = 0xff; // all voltages supported
411                m_databuf[1] = 0x00; 
412                m_databuf[0] = 0x00; 
413                m_datalen_snd = 5;
414                break;
415            default:
416                std::cout << name() << " unknown cmd " << std::dec
417                    << (int)cmd << std::endl;
418                break; // return illegal command
419            }
420        }
421}
422
[555]423void SdMMC::handle_sdmmc_write(uint8_t cmd, uint32_t data)
[552]424{
[555]425        m_datalen_rcv = 0;
426        cmd &= 0x3f;
427#ifdef SOCLIB_MODULE_DEBUG
428        std::cout << name() << " cmd write " << std::dec << (int)cmd << " args " << std::hex << data << std::endl;
429#endif
430        switch(cmd) {
431            case 24:
432              {
433                int ret;
434                // write data block
435                assert(m_sdstate != SD_IDLE && "can't write in idle state");
436                if (data >= m_device_size) {
437                        std::cerr << name() << " write: request " << data
438                            << " past end of file " << m_device_size << std::endl;
439                        m_databuf[0] = 0xd; // write error
440                        m_datalen_snd = 1;
441                        return;
442                }
443                do {
444                        if (lseek(m_fd, data, SEEK_SET) < 0) {
445                                std::cerr << name() << " lseek: " <<
446                                  strerror(errno) << std::endl;
447                                m_databuf[0] = 0xd; // write error
448                                m_datalen_snd = 1;
449                                return;
450                        }
451                        ret = write(m_fd, &m_databuf[1], 512);
452                } while (ret < 0 && errno == EINTR);
453                if (ret < 0) {
454                        std::cerr << name() << " write: " <<
455                          strerror(errno) << std::endl;
456                        m_databuf[0] = 0xd; // write error
457                        m_datalen_snd = 1;
458                        return;
459                }
460                m_databuf[0] = 0x5; // write complete
461                m_databuf[1] = 0x0; // busy
462                m_datalen_snd = 2;
463                break;
464              }
465            default:
466                std::cerr << name() << " unkown write cmd " << std::dec <<
467                    (int)cmd << std::endl;
468                m_databuf[0] = 0xd; // write error;
469                m_datalen_snd = 1;
470        }
[552]471        return;
472}
473
474//////////////////////////////////////////////////////////////////////////////
475SdMMC::SdMMC( sc_core::sc_module_name              name, 
476                                const std::string                    &filename,
477                                const uint32_t                       latency)
478
479: caba::BaseModule(name),
480        m_latency(latency),
481        p_clk("p_clk"),
482        p_resetn("p_resetn"),
483        p_spi_ss("p_spi_ss"),
484        p_spi_clk("p_spi_clk"),
485        p_spi_mosi("p_spi_mosi"),
486        p_spi_miso("p_spi_miso")
487{
488    std::cout << "  - Building SdMMC " << name << std::endl;
489
[557]490    SC_METHOD(genMealy);
[552]491    dont_initialize();
492    sensitive << p_clk.neg();
[557]493    sensitive << p_resetn;
494    sensitive << p_spi_ss;
495    sensitive << p_spi_clk;
[552]496
497    m_fd = ::open(filename.c_str(), O_RDWR);
498    if ( m_fd < 0 ) 
499    {
500            std::cout << "Error in component SdMMC : " << name
501                      << " Unable to open file " << filename << std::endl;
502            exit(1);
503    }
504    m_device_size = lseek(m_fd, 0, SEEK_END);
505
506} // end constructor
507
508SdMMC::~SdMMC()
509{
510}
511
512
513//////////////////////////
514void SdMMC::print_trace()
515{
516        const char* spi_str[] = 
517    {
518                "S_IDLE",
519                "S_RECEIVE_CMD",
520                "S_RECEIVE_ARGS_START",
521                "S_RECEIVE_ARGS",
522                "S_RECEIVE_CRC",
[555]523                "S_RECEIVE_DATA_START",
[552]524                "S_RECEIVE_DATA",
525                "S_SEND_DATA",
526                "S_NOP",
527        };
[556]528        if (spi_clk != p_spi_clk.read()) {
529        std::cout << name() << " SPI_FSM : " << spi_str[spi_fsm] 
[552]530            << std::dec
[556]531            << " clk " << spi_clk << "->" << p_spi_clk << " ss " << p_spi_ss
[552]532            << " mosi " << p_spi_mosi << " miso " << p_spi_miso
533            << std::endl;
[556]534        std::cout << "         spi_shiftreg: " << std::hex << (int)spi_shiftreg
535            << " spi_bitcount: " << (int)spi_bitcount
[552]536            << std::endl;
537        }
538}
539}} // end namespace
540
541// Local Variables:
542// tab-width: 4
543// c-basic-offset: 4
544// c-file-offsets:((innamespace . 0)(inline-open . 0))
545// indent-tabs-mode: nil
546// End:
547
548// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
549
Note: See TracBrowser for help on using the repository browser.