First integration of hsmci into sd driver.
authorasterix <asterix@38d2e660-2303-0410-9eaa-f027e97ec537>
Mon, 5 Sep 2011 13:40:05 +0000 (13:40 +0000)
committerasterix <asterix@38d2e660-2303-0410-9eaa-f027e97ec537>
Mon, 5 Sep 2011 13:40:05 +0000 (13:40 +0000)
git-svn-id: https://src.develer.com/svnoss/bertos/trunk@5017 38d2e660-2303-0410-9eaa-f027e97ec537

bertos/cpu/cortex-m3/drv/hsmci_sam3.c
bertos/cpu/cortex-m3/drv/hsmci_sam3.h
bertos/drv/sd.c

index b797f7b5a81753460842e60571bfcc35dcd2ce87..cfc1c8b6eb4677815ec39bb2f8d797647c435d05 100644 (file)
@@ -160,7 +160,7 @@ INLINE void hsmci_setBlockSize(size_t blk_size)
        HSMCI_BLKR = blk_size << HSMCI_BLKR_BLKLEN_SHIFT;
 }
 
-void hsmci_prgTxDMA(uint32_t *buf, size_t word_num, size_t blk_size)
+void hsmci_prgTxDMA(const uint32_t *buf, size_t word_num, size_t blk_size)
 {
 
        hsmci_setBlockSize(blk_size);
index 61fb878d5b495dc2096d1da3a8bf1b5991f326ab..71cdd99bc19f0c43b8f5e8b6001be624a68be887 100644 (file)
@@ -105,7 +105,7 @@ void hsmci_readResp(uint32_t *resp, size_t len);
 bool hsmci_sendCmd(uint8_t index, uint32_t argument, uint32_t reply_type);
 
 void hsmci_prgRxDMA(uint32_t *buf, size_t word_num, size_t blk_size);
-void hsmci_prgTxDMA(uint32_t *buf, size_t word_num, size_t blk_size);
+void hsmci_prgTxDMA(const uint32_t *buf, size_t word_num, size_t blk_size);
 void hsmci_waitTransfer(void);
 
 void hsmci_setSpeed(uint32_t data_rate, int flag);
index cfac8f740a2962b28ced535b9a5ec3c1724f0175..53e422f8bc8ca6c38e5637515e513f1425fe86f6 100644 (file)
@@ -235,397 +235,10 @@ static int16_t sd_getCSD(Sd *sd, CardCSD *csd)
                return EOF;
 }
 
-
-#define SD_READ_SINGLEBLOCK 0x51
-
-static size_t sd_readDirect(struct KBlock *b, block_idx_t idx, void *buf, size_t offset, size_t size)
-{
-
-       Sd *sd = SD_CAST(b);
-       LOG_INFO("reading from block %ld, offset %d, size %d\n", idx, offset, size);
-
-       if (sd->tranfer_len != size)
-       {
-               if ((sd->r1 = sd_setBlockLen(sd, size)))
-               {
-                       LOG_ERR("setBlockLen failed: %04X\n", sd->r1);
-                       return 0;
-               }
-               sd->tranfer_len = size;
-       }
-
-       SD_SELECT(sd);
-
-       sd->r1 = sd_sendCommand(sd, SD_READ_SINGLEBLOCK, idx * SD_DEFAULT_BLOCKLEN + offset, 0);
-
-       if (sd->r1)
-       {
-               LOG_ERR("read single block failed: %04X\n", sd->r1);
-               sd_select(sd, false);
-               return 0;
-       }
-
-       bool res = sd_getBlock(sd, buf, size);
-       sd_select(sd, false);
-       if (!res)
-       {
-               LOG_ERR("read single block failed reading data\n");
-               return 0;
-       }
-       else
-               return size;
-}
-
-#define SD_WRITE_SINGLEBLOCK 0x58
-#define SD_DATA_ACCEPTED     0x05
-
-static size_t sd_writeDirect(KBlock *b, block_idx_t idx, const void *buf, size_t offset, size_t size)
-{
-       Sd *sd = SD_CAST(b);
-       KFile *fd = sd->ch;
-       ASSERT(offset == 0);
-       ASSERT(size == SD_DEFAULT_BLOCKLEN);
-
-       LOG_INFO("writing block %ld\n", idx);
-       if (sd->tranfer_len != SD_DEFAULT_BLOCKLEN)
-       {
-               if ((sd->r1 = sd_setBlockLen(sd, SD_DEFAULT_BLOCKLEN)))
-               {
-                       LOG_ERR("setBlockLen failed: %04X\n", sd->r1);
-                       return 0;
-               }
-               sd->tranfer_len = SD_DEFAULT_BLOCKLEN;
-       }
-
-       SD_SELECT(sd);
-
-       sd->r1 = sd_sendCommand(sd, SD_WRITE_SINGLEBLOCK, idx * SD_DEFAULT_BLOCKLEN, 0);
-
-       if (sd->r1)
-       {
-               LOG_ERR("write single block failed: %04X\n", sd->r1);
-               sd_select(sd, false);
-               return 0;
-       }
-
-       kfile_putc(SD_STARTTOKEN, fd);
-       kfile_write(fd, buf, SD_DEFAULT_BLOCKLEN);
-       /* send fake crc */
-       kfile_putc(0, fd);
-       kfile_putc(0, fd);
-
-       uint8_t dataresp = kfile_getc(fd);
-       sd_select(sd, false);
-
-       if ((dataresp & 0x1f) != SD_DATA_ACCEPTED)
-       {
-               LOG_ERR("write block %ld failed: %02X\n", idx, dataresp);
-               return EOF;
-       }
-
-       return SD_DEFAULT_BLOCKLEN;
-}
-
-void sd_writeTest(Sd *sd)
-{
-       uint8_t buf[SD_DEFAULT_BLOCKLEN];
-       memset(buf, 0, sizeof(buf));
-
-       for (block_idx_t i = 0; i < sd->b.blk_cnt; i++)
-       {
-               LOG_INFO("writing block %ld: %s\n", i, (sd_writeDirect(&sd->b, i, buf, 0, SD_DEFAULT_BLOCKLEN) == SD_DEFAULT_BLOCKLEN) ? "OK" : "FAIL");
-       }
-}
-
-
-bool sd_test(Sd *sd)
-{
-       uint8_t buf[SD_DEFAULT_BLOCKLEN];
-
-       if (sd_readDirect(&sd->b, 0, buf, 0, sd->b.blk_size) != sd->b.blk_size)
-               return false;
-
-       kputchar('\n');
-       for (int i = 0; i < SD_DEFAULT_BLOCKLEN; i++)
-       {
-               kprintf("%02X ", buf[i]);
-               buf[i] = i;
-               if (!((i+1) % 16))
-                       kputchar('\n');
-       }
-
-       if (sd_writeDirect(&sd->b, 0, buf, 0, SD_DEFAULT_BLOCKLEN) != SD_DEFAULT_BLOCKLEN)
-               return false;
-
-       memset(buf, 0, sizeof(buf));
-       if (sd_readDirect(&sd->b, 0, buf, 0, sd->b.blk_size) != sd->b.blk_size)
-               return false;
-
-       kputchar('\n');
-       for (block_idx_t i = 0; i < sd->b.blk_size; i++)
-       {
-               kprintf("%02X ", buf[i]);
-               buf[i] = i;
-               if (!((i+1) % 16))
-                       kputchar('\n');
-       }
-
-       return true;
-}
-
-static int sd_error(KBlock *b)
-{
-       Sd *sd = SD_CAST(b);
-       return sd->r1;
-}
-
-static void sd_clearerr(KBlock *b)
-{
-       Sd *sd = SD_CAST(b);
-       sd->r1 = 0;
-}
-
-static const KBlockVTable sd_unbuffered_vt =
-{
-       .readDirect = sd_readDirect,
-       .writeDirect = sd_writeDirect,
-
-       .error = sd_error,
-       .clearerr = sd_clearerr,
-};
-
-static const KBlockVTable sd_buffered_vt =
-{
-       .readDirect = sd_readDirect,
-       .writeDirect = sd_writeDirect,
-
-       .readBuf = kblock_swReadBuf,
-       .writeBuf = kblock_swWriteBuf,
-       .load = kblock_swLoad,
-       .store = kblock_swStore,
-
-       .error = sd_error,
-       .clearerr = sd_clearerr,
-};
-
-#define SD_GO_IDLE_STATE     0x40
-#define SD_GO_IDLE_STATE_CRC 0x95
-#define SD_SEND_OP_COND      0x41
-#define SD_SEND_OP_COND_CRC  0xF9
-
-#define SD_START_DELAY  10
-#define SD_INIT_TIMEOUT ms_to_ticks(2000)
-#define SD_IDLE_RETRIES 4
-
-static bool sd_blockInit(Sd *sd, KFile *ch)
-{
-       ASSERT(sd);
-       ASSERT(ch);
-       memset(sd, 0, sizeof(*sd));
-       DB(sd->b.priv.type = KBT_SD);
-       sd->ch = ch;
-
-
-#if CPU_CM3_SAM3X8
-
-       /* Wait a few moments for supply voltage to stabilize */
-       timer_delay(SD_START_DELAY);
-
-       sd_sendInit();
-       sd_goIdle();
-
-       sd_sendIfCond(sd);
-
-       ticks_t start = timer_clock();
-       bool sd_power_on = false;
-       do
-       {
-               if (!sd_sendAppOpCond(sd))
-               {
-                       sd_power_on = true;
-                       break;
-               }
-               cpu_relax();
-       }
-       while (timer_clock() - start < SD_INIT_TIMEOUT);
-
-
-       if (sd_power_on)
-       {
-
-               if(!sd_getCid(sd, 0, SD_SEND_ALL_CID))
-               {
-                       sd_dumpCid(sd);
-               }
-
-               if (!sd_getRelativeAddr(sd))
-               {
-                       LOG_INFO("RCA: %0lx\n", sd->addr);
-               }
-
-
-               if (!sd_getCsd(sd))
-               {
-                       sd_dumpCsd(sd);
-               }
-
-               if (!sd_appStatus(sd))
-               {
-                       LOG_INFO("STATUS: %ld\n", sd->status);
-               }
-
-               if (sd->status & SD_CARD_IS_LOCKED)
-               {
-                       LOG_INFO("SD is locked!\n");
-               }
-               else if (sd->status & SD_READY_FOR_DATA)
-               {
-                       sd_selectCard(sd);
-                       sd_set_BlockLen(sd, SD_DEFAULT_BLOCKLEN);
-                       sd_setBus4bit(sd);
-                       sd_setHightSpeed(sd);
-                       sd_deSelectCard(sd);
-               }
-
-       }
-#else
-       SD_CS_INIT();
-       SD_CS_OFF();
-
-       /* Wait a few moments for supply voltage to stabilize */
-       timer_delay(SD_START_DELAY);
-
-       /* Give 80 clk pulses to wake up the card */
-       for (int i = 0; i < 10; i++)
-               kfile_putc(0xff, ch);
-       kfile_flush(ch);
-
-       for (int i = 0; i < SD_IDLE_RETRIES; i++)
-       {
-               SD_SELECT(sd);
-               sd->r1 = sd_sendCommand(sd, SD_GO_IDLE_STATE, 0, SD_GO_IDLE_STATE_CRC);
-               sd_select(sd, false);
-
-               if (sd->r1 == SD_IN_IDLE)
-                       break;
-       }
-
-       if (sd->r1 != SD_IN_IDLE)
-       {
-               LOG_ERR("go_idle_state failed: %04X\n", sd->r1);
-               return false;
-       }
-
-       ticks_t start = timer_clock();
-
-       /* Wait for card to start */
-       do
-       {
-               SD_SELECT(sd);
-               sd->r1 = sd_sendCommand(sd, SD_SEND_OP_COND, 0, SD_SEND_OP_COND_CRC);
-               sd_select(sd, false);
-               cpu_relax();
-       }
-       while (sd->r1 != 0 && timer_clock() - start < SD_INIT_TIMEOUT);
-
-       if (sd->r1)
-       {
-               LOG_ERR("send_op_cond failed: %04X\n", sd->r1);
-               return false;
-       }
-
-       sd->r1 = sd_setBlockLen(sd, SD_DEFAULT_BLOCKLEN);
-       sd->tranfer_len = SD_DEFAULT_BLOCKLEN;
-
-       if (sd->r1)
-       {
-               LOG_ERR("setBlockLen failed: %04X\n", sd->r1);
-               return false;
-       }
-
-       /* Avoid warning for uninitialized csd use (gcc bug?) */
-       CardCSD csd = csd;
-
-       sd->r1 = sd_getCSD(sd, &csd);
-
-       if (sd->r1)
-       {
-               LOG_ERR("getCSD failed: %04X\n", sd->r1);
-               return false;
-       }
-
-       sd->b.blk_size = SD_DEFAULT_BLOCKLEN;
-       sd->b.blk_cnt = csd.block_num * (csd.block_len / SD_DEFAULT_BLOCKLEN);
-       LOG_INFO("blk_size %d, blk_cnt %ld\n", sd->b.blk_size, sd->b.blk_cnt);
-#endif
-
-#if CONFIG_SD_AUTOASSIGN_FAT
-       disk_assignDrive(&sd->b, 0);
-#endif
-
-       return true;
-}
-
 #if CPU_CM3_SAM3X8
 
 #include <drv/hsmci_sam3.h>
 
-/* SD commands                           type  argument     response */
-  /* class 0 */
-/* This is basically the same command as for MMC with some quirks. */
-#define SD_SEND_RELATIVE_ADDR     3   /* bcr                     R6  */
-#define SD_SEND_IF_COND           8   /* bcr  [11:0] See below   R7  */
-#define SD_SWITCH_VOLTAGE         11  /* ac                      R1  */
-
-  /* class 10 */
-#define SD_SWITCH                 6   /* adtc [31:0] See below   R1  */
-
-  /* class 5 */
-#define SD_ERASE_WR_BLK_START    32   /* ac   [31:0] data addr   R1  */
-#define SD_ERASE_WR_BLK_END      33   /* ac   [31:0] data addr   R1  */
-
-  /* Application commands */
-#define SD_APP_SET_BUS_WIDTH      6   /* ac   [1:0] bus width    R1  */
-#define SD_APP_SD_STATUS         13   /* adtc                    R1  */
-#define SD_APP_SEND_NUM_WR_BLKS  22   /* adtc                    R1  */
-#define SD_APP_OP_COND           41   /* bcr  [31:0] OCR         R3  */
-#define SD_APP_SEND_SCR          51   /* adtc                    R1  */
-
-/* OCR bit definitions */
-#define SD_OCR_S18R     (1 << 24)    /* 1.8V switching request */
-#define SD_ROCR_S18A    SD_OCR_S18R  /* 1.8V switching accepted by card */
-#define SD_OCR_XPC      (1 << 28)    /* SDXC power control */
-
-/*
- * SD_SWITCH argument format:
- *
- *      [31] Check (0) or switch (1)
- *      [30:24] Reserved (0)
- *      [23:20] Function group 6
- *      [19:16] Function group 5
- *      [15:12] Function group 4
- *      [11:8] Function group 3
- *      [7:4] Function group 2
- *      [3:0] Function group 1
- */
-
-/*
- * SD_SEND_IF_COND argument format:
- *
- *  [31:12] Reserved (0)
- *  [11:8] Host Voltage Supply Flags
- *  [7:0] Check Pattern (0xAA)
- */
-
-/*
- * SCR field definitions
- */
-
-#define SCR_SPEC_VER_0      0   /* Implements system specification 1.0 - 1.01 */
-#define SCR_SPEC_VER_1      1   /* Implements system specification 1.10 */
-#define SCR_SPEC_VER_2      2   /* Implements system specification 2.00-3.0X */
-
-
 #define SD_ADDR_TO_RCA(addr)    (uint32_t)(((addr) << 16) & 0xFFFF0000)
 #define SD_GET_STATE(status)    (uint8_t)(((status) & SD_STATUS_CURR_MASK) >> SD_STATUS_CURR_SHIFT)
 
@@ -649,19 +262,21 @@ static void dump(const char *label, uint32_t *r, size_t len)
                        kputs("\n");
                        j = 0;
                }
-               kprintf("%08lx", r[i]);
+               kprintf("%08lx ", r[i]);
                j++;
        }
        kprintf("\n] len=%d\n\n", i);
 }
 )
 
-static const uint32_t tran_exp[] = {
+static const uint32_t tran_exp[] =
+{
     10000,      100000,     1000000,    10000000,
     0,      0,      0,      0
 };
 
-static const uint8_t tran_mant[] = {
+static const uint8_t tran_mant[] =
+{
     0,  10, 12, 13, 15, 20, 25, 30,
     35, 40, 45, 50, 55, 60, 70, 80,
 };
@@ -926,276 +541,635 @@ int sd_getRelativeAddr(Sd *sd)
        hsmci_readResp(&sd->addr, 1);
        sd->addr = sd->addr >> 16;
 
-       LOG_INFOB(dump("RCA", &sd->addr, 1););
+       LOG_INFOB(dump("RCA", &sd->addr, 1););
+
+       return 0;
+}
+
+int sd_appStatus(Sd *sd)
+{
+       ASSERT(sd);
+       LOG_INFO("Send to RCA: %lx\n", SD_ADDR_TO_RCA(sd->addr));
+       if (hsmci_sendCmd(13, SD_ADDR_TO_RCA(sd->addr), HSMCI_CMDR_RSPTYP_48_BIT))
+       {
+               LOG_ERR("STATUS: %lx\n", HSMCI_SR);
+               return -1;
+       }
+
+       hsmci_readResp(&(sd->status), 1);
+       LOG_INFOB(dump("STATUS", &(sd->status), 1););
+
+       LOG_INFO("State[%d]\n", SD_GET_STATE(sd->status));
+
+       if (sd->status & SD_STATUS_READY)
+               return 0;
+
+       return -1;
+}
+
+
+INLINE int sd_cardSelection(Sd *sd, uint32_t rca)
+{
+       ASSERT(sd);
+       LOG_INFO("Select RCA: %lx\n", rca);
+       if (hsmci_sendCmd(7, rca, HSMCI_CMDR_RSPTYP_R1B))
+       {
+               LOG_ERR("SELECT_SD: %lx\n", HSMCI_SR);
+               return -1;
+       }
+
+       HSMCI_CHECK_BUSY();
+       hsmci_readResp(&(sd->status), 1);
+       LOG_INFOB(dump("SELECT_SD", &(sd->status), 1););
+
+       LOG_INFO("State[%d]\n", SD_GET_STATE(sd->status));
+
+       if (sd->status & SD_STATUS_READY)
+               return 0;
+
+       return -1;
+}
+
+int sd_selectCard(Sd *sd)
+{
+       ASSERT(sd);
+       uint32_t rca = SD_ADDR_TO_RCA(sd->addr);
+       LOG_INFO("Select RCA: %lx\n", rca);
+       if (hsmci_sendCmd(7, rca, HSMCI_CMDR_RSPTYP_R1B))
+       {
+               LOG_ERR("SELECT_SD: %lx\n", HSMCI_SR);
+               return -1;
+       }
+
+       HSMCI_CHECK_BUSY();
+       hsmci_readResp(&(sd->status), 1);
+
+       LOG_INFOB(dump("SELECT_SD", &(sd->status), 1););
+       LOG_INFO("State[%d]\n", SD_GET_STATE(sd->status));
+
+       if (sd->status & SD_STATUS_READY)
+               return 0;
+
+       return -1;
+}
+
+int sd_deSelectCard(Sd *sd)
+{
+       ASSERT(sd);
+
+       uint32_t rca = 0;
+       if (!sd->addr)
+               rca = SD_ADDR_TO_RCA(sd->addr + 1);
+
+       LOG_INFO("Select RCA: %lx\n", rca);
+
+       if (hsmci_sendCmd(7, rca, HSMCI_CMDR_RSPTYP_NORESP))
+       {
+               LOG_ERR("DESELECT_SD: %lx\n", HSMCI_SR);
+               return -1;
+       }
+
+       return 0;
+
+}
+
+int sd_setBusWidth(Sd *sd, size_t len)
+{
+       ASSERT(sd);
+
+       if (hsmci_sendCmd(55, SD_ADDR_TO_RCA(sd->addr), HSMCI_CMDR_RSPTYP_48_BIT))
+       {
+               LOG_ERR("APP_CMD %lx\n", HSMCI_SR);
+               return -1;
+       }
+
+       hsmci_readResp(&(sd->status), 1);
+       if ((sd->status) & (SD_STATUS_APP_CMD | SD_STATUS_READY))
+       {
+               hsmci_setBusWidth(len);
+
+               uint8_t arg = 0;
+               if (len == 4)
+                       arg = 2;
+
+               if (hsmci_sendCmd(6, arg, HSMCI_CMDR_RSPTYP_48_BIT))
+               {
+                       LOG_ERR("SET_BUS_WIDTH CMD: %lx\n", HSMCI_SR);
+                       return -1;
+               }
+
+               hsmci_readResp(&(sd->status), 1);
+
+               LOG_INFOB(dump("SET_BUS_WIDTH", &(sd->status), 1););
+               LOG_INFO("State[%d]\n", SD_GET_STATE(sd->status));
+
+               if (sd->status & SD_STATUS_READY)
+                       return 0;
+       }
+
+       LOG_ERR("SET_BUS_WIDTH REP %lx\n", (sd->status));
+       return -1;
+}
+
+
+int sd_set_BlockLen(Sd *sd, size_t len)
+{
+       ASSERT(sd);
+
+       if (hsmci_sendCmd(16, len, HSMCI_CMDR_RSPTYP_48_BIT))
+       {
+               LOG_ERR("SET_BLK_LEN: %lx\n", HSMCI_SR);
+               return -1;
+       }
+
+       hsmci_readResp(&(sd->status), 1);
+
+       LOG_INFOB(dump("SET_BLK_LEN", &(sd->status), 1););
+       LOG_INFO("State[%d]\n", SD_GET_STATE(sd->status));
+
+       sd->csd.blk_len = len;
+
+       if (sd->status & SD_STATUS_READY)
+               return 0;
 
-       return 0;
+       return -1;
 }
 
-int sd_appStatus(Sd *sd)
+int sd_getStatus(Sd *sd, uint32_t *buf, size_t words)
 {
        ASSERT(sd);
-       LOG_INFO("Send to RCA: %lx\n", SD_ADDR_TO_RCA(sd->addr));
-       if (hsmci_sendCmd(13, SD_ADDR_TO_RCA(sd->addr), HSMCI_CMDR_RSPTYP_48_BIT))
+
+       // Status reply with 512bit data, so the block size in byte is 64
+       hsmci_prgRxDMA(buf, words, 64);
+
+       if (hsmci_sendCmd(55, SD_ADDR_TO_RCA(sd->addr), HSMCI_CMDR_RSPTYP_48_BIT))
        {
-               LOG_ERR("STATUS: %lx\n", HSMCI_SR);
+               LOG_ERR("APP_CMD %lx\n", HSMCI_SR);
                return -1;
        }
 
-       hsmci_readResp(&(sd->status), 1);
-       LOG_INFOB(dump("STATUS", &(sd->status), 1););
+       uint32_t status = HSMCI_RSPR;
+       if (status & (SD_STATUS_APP_CMD | SD_STATUS_READY))
+       {
+               if (hsmci_sendCmd(13, 0, HSMCI_CMDR_RSPTYP_48_BIT |
+                               BV(HSMCI_CMDR_TRDIR) | HSMCI_CMDR_TRCMD_START_DATA | HSMCI_CMDR_TRTYP_SINGLE))
+               {
+                       LOG_ERR("STATUS CMD: %lx\n", HSMCI_SR);
+                       return -1;
+               }
 
-       LOG_INFO("State[%d]\n", SD_GET_STATE(sd->status));
+               hsmci_readResp(&(sd->status), 1);
+               LOG_INFOB(dump("STATUS", &(sd->status), 1););
+               LOG_INFO("State[%d]\n", SD_GET_STATE(sd->status));
 
-       if (sd->status & SD_STATUS_READY)
-               return 0;
+               if (sd->status & SD_STATUS_READY)
+               {
+                       hsmci_waitTransfer();
+
+                       LOG_INFOB(dump("STATUS", buf, words););
+                       memset(&(sd->ssr), 0, sizeof(SDssr));
+
+                       sd->ssr.bus_width  = UNSTUFF_BITS(buf, 510, 2);
+                       sd->ssr.card_type  = UNSTUFF_BITS(buf, 480, 16);
+                       sd->ssr.au_size  = UNSTUFF_BITS(buf, 432, 8);
+                       sd->ssr.speed_class  = UNSTUFF_BITS(buf, 440, 8);
+                       sd->ssr.erase_size = UNSTUFF_BITS(buf, 408, 24);
+
+                       return 0;
+               }
+       }
 
        return -1;
 }
 
 
-INLINE int sd_cardSelection(Sd *sd, uint32_t rca)
+void sd_setHightSpeed(Sd *sd)
 {
-       ASSERT(sd);
-       LOG_INFO("Select RCA: %lx\n", rca);
-       if (hsmci_sendCmd(7, rca, HSMCI_CMDR_RSPTYP_R1B))
+       (void)sd;
+       hsmci_setSpeed(2100000, true);
+}
+
+#endif
+
+
+#define SD_START_DELAY  10
+#define SD_INIT_TIMEOUT ms_to_ticks(2000)
+#define SD_IDLE_RETRIES 4
+
+#if CPU_CM3_SAM3X8
+
+static size_t sd_SdReadDirect(struct KBlock *b, block_idx_t idx, void *buf, size_t offset, size_t size)
+{
+       ASSERT(buf);
+       Sd *sd = SD_CAST(b);
+       LOG_INFO("reading from block %ld, offset %d, size %d\n", idx, offset, size);
+
+       if (sd_selectCard(sd) < 0)
+               return -1;
+
+       hsmci_prgRxDMA(buf, size / 4, sd->csd.blk_len);
+
+       if (hsmci_sendCmd(17, idx * sd->csd.blk_len + offset, HSMCI_CMDR_RSPTYP_48_BIT |
+                       BV(HSMCI_CMDR_TRDIR) | HSMCI_CMDR_TRCMD_START_DATA | HSMCI_CMDR_TRTYP_SINGLE))
        {
-               LOG_ERR("SELECT_SD: %lx\n", HSMCI_SR);
+               LOG_ERR("SIGLE_BLK_READ: %lx\n", HSMCI_SR);
+               sd_deSelectCard(sd);
                return -1;
        }
 
-       HSMCI_CHECK_BUSY();
        hsmci_readResp(&(sd->status), 1);
-       LOG_INFOB(dump("SELECT_SD", &(sd->status), 1););
 
+       LOG_INFOB(dump("SIGLE_BLK_READ", &(sd->status), 1););
        LOG_INFO("State[%d]\n", SD_GET_STATE(sd->status));
 
        if (sd->status & SD_STATUS_READY)
-               return 0;
+       {
+               hsmci_waitTransfer();
+               sd_deSelectCard(sd);
+               return size;
+       }
 
+       sd_deSelectCard(sd);
        return -1;
 }
 
-int sd_selectCard(Sd *sd)
+static size_t sd_SdWriteDirect(KBlock *b, block_idx_t idx, const void *buf, size_t offset, size_t size)
 {
-       ASSERT(sd);
-       uint32_t rca = SD_ADDR_TO_RCA(sd->addr);
-       LOG_INFO("Select RCA: %lx\n", rca);
-       if (hsmci_sendCmd(7, rca, HSMCI_CMDR_RSPTYP_R1B))
+       ASSERT(buf);
+       Sd *sd = SD_CAST(b);
+       const uint32_t *_buf = (const uint32_t *)buf;
+       LOG_INFO("reading from block %ld, offset %d, size %d\n", idx, offset, size);
+
+       if (sd_selectCard(sd) < 0)
+               return 0;
+
+       hsmci_prgTxDMA(_buf, size / 4, sd->csd.blk_len);
+
+       if (hsmci_sendCmd(24, idx * sd->csd.blk_len + offset, HSMCI_CMDR_RSPTYP_48_BIT |
+                                               HSMCI_CMDR_TRCMD_START_DATA | HSMCI_CMDR_TRTYP_SINGLE))
        {
-               LOG_ERR("SELECT_SD: %lx\n", HSMCI_SR);
+               LOG_ERR("SIGLE_BLK_WRITE: %lx\n", HSMCI_SR);
+               sd_deSelectCard(sd);
                return -1;
        }
 
-       HSMCI_CHECK_BUSY();
        hsmci_readResp(&(sd->status), 1);
 
-       LOG_INFOB(dump("SELECT_SD", &(sd->status), 1););
+       LOG_INFOB(dump("SIGLE_BLK_WR", &(sd->status), 1););
        LOG_INFO("State[%d]\n", SD_GET_STATE(sd->status));
 
        if (sd->status & SD_STATUS_READY)
-               return 0;
+       {
+               hsmci_waitTransfer();
+               sd_deSelectCard(sd);
+               return size;
+       }
 
+       sd_deSelectCard(sd);
        return -1;
 }
 
-int sd_deSelectCard(Sd *sd)
+
+static int sd_SdError(KBlock *b)
 {
-       ASSERT(sd);
+       Sd *sd = SD_CAST(b);
+       return 0;//sd->status;
+}
 
-       uint32_t rca = 0;
-       if (!sd->addr)
-               rca = SD_ADDR_TO_RCA(sd->addr + 1);
+static void sd_SdClearerr(KBlock *b)
+{
+       Sd *sd = SD_CAST(b);
+       sd->status = 0;
+}
 
-       LOG_INFO("Select RCA: %lx\n", rca);
 
-       if (hsmci_sendCmd(7, rca, HSMCI_CMDR_RSPTYP_NORESP))
+
+static bool sd_SdBlockInit(Sd *sd)
+{
+       /* Wait a few moments for supply voltage to stabilize */
+       timer_delay(SD_START_DELAY);
+
+       sd_sendInit();
+       sd_goIdle();
+
+       sd_sendIfCond(sd);
+
+       ticks_t start = timer_clock();
+       bool sd_power_on = false;
+       do
        {
-               LOG_ERR("DESELECT_SD: %lx\n", HSMCI_SR);
-               return -1;
+               if (!sd_sendAppOpCond(sd))
+               {
+                       sd_power_on = true;
+                       break;
+               }
+               cpu_relax();
        }
+       while (timer_clock() - start < SD_INIT_TIMEOUT);
 
-       return 0;
 
+       if (sd_power_on)
+       {
+
+               if(sd_getCid(sd, 0, SD_SEND_ALL_CID) < 0)
+                       return false;
+               else
+               {
+                       sd_dumpCid(sd);
+               }
+
+               if (sd_getRelativeAddr(sd) < 0)
+                       return false;
+               else
+               {
+                       LOG_INFO("RCA: %0lx\n", sd->addr);
+               }
+
+
+               if (sd_getCsd(sd) < 0)
+                       return false;
+               else
+               {
+                       sd_dumpCsd(sd);
+               }
+
+               if (sd_appStatus(sd) < 0)
+               {
+                       LOG_INFO("STATUS: %ld\n", sd->status);
+                       return false;
+               }
+
+               if (sd->status & SD_CARD_IS_LOCKED)
+               {
+                       LOG_INFO("SD is locked!\n");
+                       return false;
+               }
+
+               if (sd->status & SD_READY_FOR_DATA)
+               {
+                       sd_selectCard(sd);
+                       sd_set_BlockLen(sd, SD_DEFAULT_BLOCKLEN);
+                       sd_setBus4bit(sd);
+                       sd_setHightSpeed(sd);
+                       sd_deSelectCard(sd);
+
+                       sd->b.blk_size = SD_DEFAULT_BLOCKLEN;
+                       sd->b.blk_cnt = sd->csd.blk_num * (sd->csd.blk_len / SD_DEFAULT_BLOCKLEN);
+                       LOG_INFO("blk_size %d, blk_cnt %ld\n", sd->b.blk_size, sd->b.blk_cnt);
+
+                       return true;
+               }
+       }
+
+       return false;
 }
 
+#else
+
+#define SD_READ_SINGLEBLOCK 0x51
 
-int sd_setBusWidth(Sd *sd, size_t len)
+static size_t sd_SpiReadDirect(struct KBlock *b, block_idx_t idx, void *buf, size_t offset, size_t size)
 {
-       ASSERT(sd);
 
-       if (hsmci_sendCmd(55, SD_ADDR_TO_RCA(sd->addr), HSMCI_CMDR_RSPTYP_48_BIT))
+       Sd *sd = SD_CAST(b);
+       LOG_INFO("reading from block %ld, offset %d, size %d\n", idx, offset, size);
+
+       if (sd->tranfer_len != size)
        {
-               LOG_ERR("APP_CMD %lx\n", HSMCI_SR);
-               return -1;
+               if ((sd->r1 = sd_setBlockLen(sd, size)))
+               {
+                       LOG_ERR("setBlockLen failed: %04X\n", sd->r1);
+                       return 0;
+               }
+               sd->tranfer_len = size;
        }
 
-       hsmci_readResp(&(sd->status), 1);
-       if ((sd->status) & (SD_STATUS_APP_CMD | SD_STATUS_READY))
-       {
-               hsmci_setBusWidth(len);
+       SD_SELECT(sd);
 
-               uint8_t arg = 0;
-               if (len == 4)
-                       arg = 2;
+       sd->r1 = sd_sendCommand(sd, SD_READ_SINGLEBLOCK, idx * SD_DEFAULT_BLOCKLEN + offset, 0);
 
-               if (hsmci_sendCmd(6, arg, HSMCI_CMDR_RSPTYP_48_BIT))
-               {
-                       LOG_ERR("SET_BUS_WIDTH CMD: %lx\n", HSMCI_SR);
-                       return -1;
-               }
+       if (sd->r1)
+       {
+               LOG_ERR("read single block failed: %04X\n", sd->r1);
+               sd_select(sd, false);
+               return 0;
+       }
 
-               hsmci_readResp(&(sd->status), 1);
+       bool res = sd_getBlock(sd, buf, size);
+       sd_select(sd, false);
+       if (!res)
+       {
+               LOG_ERR("read single block failed reading data\n");
+               return 0;
+       }
+       else
+               return size;
+}
 
-               LOG_INFOB(dump("SET_BUS_WIDTH", &(sd->status), 1););
-               LOG_INFO("State[%d]\n", SD_GET_STATE(sd->status));
+#define SD_WRITE_SINGLEBLOCK 0x58
+#define SD_DATA_ACCEPTED     0x05
 
-               if (sd->status & SD_STATUS_READY)
+static size_t sd_SpiWriteDirect(KBlock *b, block_idx_t idx, const void *buf, size_t offset, size_t size)
+{
+       Sd *sd = SD_CAST(b);
+       KFile *fd = sd->ch;
+       ASSERT(offset == 0);
+       ASSERT(size == SD_DEFAULT_BLOCKLEN);
+
+       LOG_INFO("writing block %ld\n", idx);
+       if (sd->tranfer_len != SD_DEFAULT_BLOCKLEN)
+       {
+               if ((sd->r1 = sd_setBlockLen(sd, SD_DEFAULT_BLOCKLEN)))
+               {
+                       LOG_ERR("setBlockLen failed: %04X\n", sd->r1);
                        return 0;
+               }
+               sd->tranfer_len = SD_DEFAULT_BLOCKLEN;
        }
 
-       LOG_ERR("SET_BUS_WIDTH REP %lx\n", (sd->status));
-       return -1;
-}
-
+       SD_SELECT(sd);
 
-int sd_set_BlockLen(Sd *sd, size_t len)
-{
-       ASSERT(sd);
+       sd->r1 = sd_sendCommand(sd, SD_WRITE_SINGLEBLOCK, idx * SD_DEFAULT_BLOCKLEN, 0);
 
-       if (hsmci_sendCmd(16, len, HSMCI_CMDR_RSPTYP_48_BIT))
+       if (sd->r1)
        {
-               LOG_ERR("SET_BLK_LEN: %lx\n", HSMCI_SR);
-               return -1;
+               LOG_ERR("write single block failed: %04X\n", sd->r1);
+               sd_select(sd, false);
+               return 0;
        }
 
-       hsmci_readResp(&(sd->status), 1);
+       kfile_putc(SD_STARTTOKEN, fd);
+       kfile_write(fd, buf, SD_DEFAULT_BLOCKLEN);
+       /* send fake crc */
+       kfile_putc(0, fd);
+       kfile_putc(0, fd);
 
-       LOG_INFOB(dump("SET_BLK_LEN", &(sd->status), 1););
-       LOG_INFO("State[%d]\n", SD_GET_STATE(sd->status));
+       uint8_t dataresp = kfile_getc(fd);
+       sd_select(sd, false);
 
-       sd->csd.blk_len = len;
+       if ((dataresp & 0x1f) != SD_DATA_ACCEPTED)
+       {
+               LOG_ERR("write block %ld failed: %02X\n", idx, dataresp);
+               return EOF;
+       }
 
-       if (sd->status & SD_STATUS_READY)
-               return 0;
+       return SD_DEFAULT_BLOCKLEN;
+}
 
-       return -1;
+static int sd_SpiError(KBlock *b)
+{
+       Sd *sd = SD_CAST(b);
+       return sd->r1;
 }
 
-int sd_getStatus(Sd *sd, uint32_t *buf, size_t words)
+static void sd_SpiClearerr(KBlock *b)
 {
-       ASSERT(sd);
+       Sd *sd = SD_CAST(b);
+       sd->r1 = 0;
+}
 
-       // Status reply with 512bit data, so the block size in byte is 64
-       hsmci_prgRxDMA(buf, words, 64);
 
-       if (hsmci_sendCmd(55, SD_ADDR_TO_RCA(sd->addr), HSMCI_CMDR_RSPTYP_48_BIT))
-       {
-               LOG_ERR("APP_CMD %lx\n", HSMCI_SR);
-               return -1;
-       }
 
-       uint32_t status = HSMCI_RSPR;
-       if (status & (SD_STATUS_APP_CMD | SD_STATUS_READY))
-       {
-               if (hsmci_sendCmd(13, 0, HSMCI_CMDR_RSPTYP_48_BIT |
-                               BV(HSMCI_CMDR_TRDIR) | HSMCI_CMDR_TRCMD_START_DATA | HSMCI_CMDR_TRTYP_SINGLE))
-               {
-                       LOG_ERR("STATUS CMD: %lx\n", HSMCI_SR);
-                       return -1;
-               }
+#define SD_GO_IDLE_STATE     0x40
+#define SD_GO_IDLE_STATE_CRC 0x95
+#define SD_SEND_OP_COND      0x41
+#define SD_SEND_OP_COND_CRC  0xF9
 
-               hsmci_readResp(&(sd->status), 1);
-               LOG_INFOB(dump("STATUS", &(sd->status), 1););
-               LOG_INFO("State[%d]\n", SD_GET_STATE(sd->status));
+static bool sd_SpiBlockInit(Sd *sd, KFile *ch)
+{
+       sd->ch = ch;
 
-               if (sd->status & SD_STATUS_READY)
-               {
-                       hsmci_waitTransfer();
+       SD_CS_INIT();
+       SD_CS_OFF();
 
-                       LOG_INFOB(dump("STATUS", buf, words););
-                       memset(&(sd->ssr), 0, sizeof(SDssr));
+       /* Wait a few moments for supply voltage to stabilize */
+       timer_delay(SD_START_DELAY);
 
-                       sd->ssr.bus_width  = UNSTUFF_BITS(buf, 510, 2);
-                       sd->ssr.card_type  = UNSTUFF_BITS(buf, 480, 16);
-                       sd->ssr.au_size  = UNSTUFF_BITS(buf, 432, 8);
-                       sd->ssr.speed_class  = UNSTUFF_BITS(buf, 440, 8);
-                       sd->ssr.erase_size = UNSTUFF_BITS(buf, 408, 24);
+       /* Give 80 clk pulses to wake up the card */
+       for (int i = 0; i < 10; i++)
+               kfile_putc(0xff, ch);
+       kfile_flush(ch);
 
-                       return 0;
-               }
+       for (int i = 0; i < SD_IDLE_RETRIES; i++)
+       {
+               SD_SELECT(sd);
+               sd->r1 = sd_sendCommand(sd, SD_GO_IDLE_STATE, 0, SD_GO_IDLE_STATE_CRC);
+               sd_select(sd, false);
+
+               if (sd->r1 == SD_IN_IDLE)
+                       break;
        }
 
-       return -1;
-}
+       if (sd->r1 != SD_IN_IDLE)
+       {
+               LOG_ERR("go_idle_state failed: %04X\n", sd->r1);
+               return false;
+       }
 
+       ticks_t start = timer_clock();
 
+       /* Wait for card to start */
+       do
+       {
+               SD_SELECT(sd);
+               sd->r1 = sd_sendCommand(sd, SD_SEND_OP_COND, 0, SD_SEND_OP_COND_CRC);
+               sd_select(sd, false);
+               cpu_relax();
+       }
+       while (sd->r1 != 0 && timer_clock() - start < SD_INIT_TIMEOUT);
 
-int sd_readSingleBlock(Sd *sd, size_t index, uint32_t *buf, size_t words)
-{
-       ASSERT(sd);
-       ASSERT(buf);
+       if (sd->r1)
+       {
+               LOG_ERR("send_op_cond failed: %04X\n", sd->r1);
+               return false;
+       }
 
-       hsmci_prgRxDMA(buf, words, sd->csd.blk_len);
+       sd->r1 = sd_setBlockLen(sd, SD_DEFAULT_BLOCKLEN);
+       sd->tranfer_len = SD_DEFAULT_BLOCKLEN;
 
-       if (hsmci_sendCmd(17, index * sd->csd.blk_len, HSMCI_CMDR_RSPTYP_48_BIT |
-                       BV(HSMCI_CMDR_TRDIR) | HSMCI_CMDR_TRCMD_START_DATA | HSMCI_CMDR_TRTYP_SINGLE))
+       if (sd->r1)
        {
-               LOG_ERR("SIGLE_BLK_READ: %lx\n", HSMCI_SR);
-               return -1;
+               LOG_ERR("setBlockLen failed: %04X\n", sd->r1);
+               return false;
        }
-       hsmci_readResp(&(sd->status), 1);
 
-       LOG_INFOB(dump("SIGLE_BLK_READ", &(sd->status), 1););
-       LOG_INFO("State[%d]\n", SD_GET_STATE(sd->status));
+       /* Avoid warning for uninitialized csd use (gcc bug?) */
+       CardCSD csd = csd;
 
-       if (sd->status & SD_STATUS_READY)
+       sd->r1 = sd_getCSD(sd, &csd);
+
+       if (sd->r1)
        {
-               hsmci_waitTransfer();
-               return words;
+               LOG_ERR("getCSD failed: %04X\n", sd->r1);
+               return false;
        }
 
-       return -1;
+       sd->b.blk_size = SD_DEFAULT_BLOCKLEN;
+       sd->b.blk_cnt = csd.block_num * (csd.block_len / SD_DEFAULT_BLOCKLEN);
+       LOG_INFO("blk_size %d, blk_cnt %ld\n", sd->b.blk_size, sd->b.blk_cnt);
+       return true;
 }
+#endif
 
 
-int sd_writeSingleBlock(Sd *sd, size_t index, uint32_t *buf, size_t words)
+static bool sd_blockInit(Sd *sd, KFile *ch)
 {
        ASSERT(sd);
-       ASSERT(buf);
-
-       hsmci_prgTxDMA(buf, words, sd->csd.blk_len);
-
-       if (hsmci_sendCmd(24, index * sd->csd.blk_len, HSMCI_CMDR_RSPTYP_48_BIT |
-                                               HSMCI_CMDR_TRCMD_START_DATA | HSMCI_CMDR_TRTYP_SINGLE))
-       {
-               LOG_ERR("SIGLE_BLK_WRITE: %lx\n", HSMCI_SR);
-               return -1;
-       }
-       hsmci_readResp(&(sd->status), 1);
+       ASSERT(ch);
+       memset(sd, 0, sizeof(*sd));
+       DB(sd->b.priv.type = KBT_SD);
 
-       LOG_INFOB(dump("SIGLE_BLK_WR", &(sd->status), 1););
-       LOG_INFO("State[%d]\n", SD_GET_STATE(sd->status));
+#if CPU_CM3_SAM3X8
+       if (!sd_SdBlockInit(sd))
+               return false;
+#else
+       if (!sd_SpiBlockInit(sd, ch))
+               return false;
+#endif
 
-       if (sd->status & SD_STATUS_READY)
-       {
-               hsmci_waitTransfer();
-               return words;
-       }
+#if CONFIG_SD_AUTOASSIGN_FAT
+       disk_assignDrive(&sd->b, 0);
+#endif
 
-       return -1;
+       return true;
 }
 
+#if CPU_CM3_SAM3X8
+#define sd_writeDirect     sd_SdWriteDirect
+#define sd_readDirect      sd_SdReadDirect
+#define sd_error           sd_SdError
+#define sd_clearerr        sd_SdClearerr
 
+#else
+#define sd_writeDirect     sd_SpiWriteDirect
+#define sd_readDirect      sd_SpiReadDirect
+#define sd_error           sd_SpiError
+#define sd_clearerr        sd_SpiClearerr
 
-void sd_setHightSpeed(Sd *sd)
+#endif
+
+static const KBlockVTable sd_unbuffered_vt =
 {
-       (void)sd;
-       hsmci_setSpeed(2100000, true);
-}
+       .readDirect = sd_readDirect,
+       .writeDirect = sd_writeDirect,
 
+       .error = sd_error,
+       .clearerr = sd_clearerr,
+};
 
-#endif
+static const KBlockVTable sd_buffered_vt =
+{
+       .readDirect = sd_readDirect,
+       .writeDirect = sd_writeDirect,
 
+       .readBuf = kblock_swReadBuf,
+       .writeBuf = kblock_swWriteBuf,
+       .load = kblock_swLoad,
+       .store = kblock_swStore,
+
+       .error = sd_error,
+       .clearerr = sd_clearerr,
+};
 
 bool sd_initUnbuf(Sd *sd, KFile *ch)
 {
@@ -1225,4 +1199,52 @@ bool sd_initBuf(Sd *sd, KFile *ch)
 }
 
 
+void sd_writeTest(Sd *sd)
+{
+       uint8_t buf[SD_DEFAULT_BLOCKLEN];
+       memset(buf, 0, sizeof(buf));
+
+       for (block_idx_t i = 0; i < sd->b.blk_cnt; i++)
+       {
+               LOG_INFO("writing block %ld: %s\n", i, (sd_writeDirect(&sd->b, i, buf, 0, SD_DEFAULT_BLOCKLEN) == SD_DEFAULT_BLOCKLEN) ? "OK" : "FAIL");
+       }
+}
+
+
+bool sd_test(Sd *sd)
+{
+       uint8_t buf[SD_DEFAULT_BLOCKLEN];
+
+       if (sd_readDirect(&sd->b, 0, buf, 0, sd->b.blk_size) != sd->b.blk_size)
+               return false;
+
+       kputchar('\n');
+       for (int i = 0; i < SD_DEFAULT_BLOCKLEN; i++)
+       {
+               kprintf("%02X ", buf[i]);
+               buf[i] = i;
+               if (!((i+1) % 16))
+                       kputchar('\n');
+       }
+
+       if (sd_writeDirect(&sd->b, 0, buf, 0, SD_DEFAULT_BLOCKLEN) != SD_DEFAULT_BLOCKLEN)
+               return false;
+
+       memset(buf, 0, sizeof(buf));
+       if (sd_readDirect(&sd->b, 0, buf, 0, sd->b.blk_size) != sd->b.blk_size)
+               return false;
+
+       kputchar('\n');
+       for (block_idx_t i = 0; i < sd->b.blk_size; i++)
+       {
+               kprintf("%02X ", buf[i]);
+               buf[i] = i;
+               if (!((i+1) % 16))
+                       kputchar('\n');
+       }
+
+       return true;
+}
+
+