bios: add litesdcard test routines to boot menu
authorGabriel Somlo <gsomlo@gmail.com>
Wed, 8 Jan 2020 17:49:52 +0000 (12:49 -0500)
committerGabriel Somlo <gsomlo@gmail.com>
Thu, 20 Feb 2020 01:16:13 +0000 (20:16 -0500)
This is a straightforward import of the sdcard initialization and
testing routines from the LiteSDCard demo example, made available
as mainline LiteX bios boot-prompt commands.

Signed-off-by: Gabriel Somlo <gsomlo@gmail.com>
litex/soc/software/bios/Makefile
litex/soc/software/bios/main.c
litex/soc/software/bios/sdcard.c [new file with mode: 0644]
litex/soc/software/bios/sdcard.h [new file with mode: 0644]

index 7c34c9872cd785b0e0e30b5a197c4835ffcb1a6c..66be372c70c21ef37562d1587155ccb9226046eb 100755 (executable)
@@ -10,7 +10,7 @@ ifdef TFTP_SERVER_PORT
 CFLAGS += -DTFTP_SERVER_PORT=$(TFTP_SERVER_PORT)
 endif
 
-OBJECTS=isr.o sdram.o main.o boot-helper-$(CPU).o boot.o
+OBJECTS=isr.o sdram.o sdcard.o main.o boot-helper-$(CPU).o boot.o
 
 all: bios.bin
 
index febe8ca9fbbed80bd992ba8f1b8fd73473d107c0..a4a358a31e9fef90f46f3e34fab689e8b0ae53e9 100644 (file)
@@ -40,6 +40,7 @@
 #endif
 
 #include "sdram.h"
+#include "sdcard.h"
 #include "boot.h"
 
 /* General address space functions */
@@ -375,6 +376,12 @@ static void help(void)
        puts("");
 #ifdef CSR_SDRAM_BASE
        puts("memtest    - run a memory test");
+#endif
+       puts("");
+#ifdef CSR_SDCORE_BASE
+       puts("sdclk <freq>   - SDCard set clk frequency (Mhz)");
+       puts("sdinit         - SDCard initialization");
+       puts("sdtest <loops> - SDCard test");
 #endif
 }
 
@@ -459,6 +466,13 @@ static void do_command(char *c)
 #endif
        else if(strcmp(token, "memtest") == 0) memtest();
 #endif
+
+#ifdef CSR_SDCORE_BASE
+       else if(strcmp(token, "sdclk") == 0) sdclk_set_clk(atoi(get_token(&c)));
+       else if(strcmp(token, "sdinit") == 0) sdcard_init();
+       else if(strcmp(token, "sdtest") == 0) sdcard_test(atoi(get_token(&c)));
+#endif
+
        else if(strcmp(token, "") != 0)
                printf("Command not found\n");
 }
diff --git a/litex/soc/software/bios/sdcard.c b/litex/soc/software/bios/sdcard.c
new file mode 100644 (file)
index 0000000..0966434
--- /dev/null
@@ -0,0 +1,664 @@
+// This file is Copyright (c) 2017 Florent Kermarrec <florent@enjoy-digital.fr>
+// This file is Copyright (c) 2019 Kees Jongenburger <kees.jongenburger@gmail.com>
+// This file is Copyright (c) 2018 bunnie <bunnie@kosagi.com>
+// License: BSD
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <generated/csr.h>
+#include <generated/mem.h>
+#include <hw/flags.h>
+#include <system.h>
+
+#include "sdcard.h"
+
+#ifdef CSR_SDCORE_BASE
+
+#define SDCARD_DEBUG
+
+/* clocking */
+
+#ifdef CSR_SDCLK_CMD_DATA_ADDR
+
+static void sdclk_dcm_write(int cmd, int data)
+{
+       int word;
+       word = (data << 2) | cmd;
+       sdclk_cmd_data_write(word);
+       sdclk_send_cmd_data_write(1);
+       while(sdclk_status_read() & CLKGEN_STATUS_BUSY);
+}
+
+/* FIXME: add vco frequency check */
+static void sdclk_get_config(unsigned int freq, unsigned int *best_m, unsigned int *best_d)
+{
+       unsigned int ideal_m, ideal_d;
+       unsigned int bm, bd;
+       unsigned int m, d;
+       unsigned int diff_current;
+       unsigned int diff_tested;
+
+       ideal_m = freq;
+       ideal_d = 5000;
+
+       bm = 1;
+       bd = 0;
+       for(d=1;d<=256;d++)
+               for(m=2;m<=256;m++) {
+                       /* common denominator is d*bd*ideal_d */
+                       diff_current = abs(d*ideal_d*bm - d*bd*ideal_m);
+                       diff_tested = abs(bd*ideal_d*m - d*bd*ideal_m);
+                       if(diff_tested < diff_current) {
+                               bm = m;
+                               bd = d;
+                       }
+               }
+       *best_m = bm;
+       *best_d = bd;
+}
+
+void sdclk_set_clk(unsigned int freq) {
+       unsigned int clk_m, clk_d;
+
+       sdclk_get_config(100*freq, &clk_m, &clk_d);
+       sdclk_dcm_write(0x1, clk_d-1);
+       sdclk_dcm_write(0x3, clk_m-1);
+       sdclk_send_go_write(1);
+       while(!(sdclk_status_read() & CLKGEN_STATUS_PROGDONE));
+       while(!(sdclk_status_read() & CLKGEN_STATUS_LOCKED));
+}
+
+#else
+
+static void sdclk_mmcm_write(unsigned int adr, unsigned int data) {
+       sdclk_mmcm_adr_write(adr);
+       sdclk_mmcm_dat_w_write(data);
+       sdclk_mmcm_write_write(1);
+       while(!sdclk_mmcm_drdy_read());
+}
+
+
+static void sdclk_set_config(unsigned int m, unsigned int d) {
+       /* clkfbout_mult = m */
+       if(m%2)
+               sdclk_mmcm_write(0x14, 0x1000 | ((m/2)<<6) | (m/2 + 1));
+       else
+               sdclk_mmcm_write(0x14, 0x1000 | ((m/2)<<6) | m/2);
+       /* divclk_divide = d */
+       if (d == 1)
+               sdclk_mmcm_write(0x16, 0x1000);
+       else if(d%2)
+               sdclk_mmcm_write(0x16, ((d/2)<<6) | (d/2 + 1));
+       else
+               sdclk_mmcm_write(0x16, ((d/2)<<6) | d/2);
+       /* clkout0_divide = 10 */
+       sdclk_mmcm_write(0x8, 0x1000 | (5<<6) | 5);
+       /* clkout1_divide = 2 */
+       sdclk_mmcm_write(0xa, 0x1000 | (1<<6) | 1);
+}
+
+/* FIXME: add vco frequency check */
+static void sdclk_get_config(unsigned int freq, unsigned int *best_m, unsigned int *best_d) {
+       unsigned int ideal_m, ideal_d;
+       unsigned int bm, bd;
+       unsigned int m, d;
+       unsigned int diff_current;
+       unsigned int diff_tested;
+
+       ideal_m = freq;
+       ideal_d = 10000;
+
+       bm = 1;
+       bd = 0;
+       for(d=1;d<=128;d++)
+               for(m=2;m<=128;m++) {
+                       /* common denominator is d*bd*ideal_d */
+                       diff_current = abs(d*ideal_d*bm - d*bd*ideal_m);
+                       diff_tested = abs(bd*ideal_d*m - d*bd*ideal_m);
+                       if(diff_tested < diff_current) {
+                               bm = m;
+                               bd = d;
+                       }
+               }
+       *best_m = bm;
+       *best_d = bd;
+}
+
+void sdclk_set_clk(unsigned int freq) {
+       unsigned int clk_m, clk_d;
+
+       sdclk_get_config(1000*freq, &clk_m, &clk_d);
+       sdclk_set_config(clk_m, clk_d);
+}
+
+#endif
+
+/* command utils */
+
+static void busy_wait(unsigned int ms)
+{
+       timer0_en_write(0);
+       timer0_reload_write(0);
+       timer0_load_write(CONFIG_CLOCK_FREQUENCY/1000*ms);
+       timer0_en_write(1);
+       timer0_update_value_write(1);
+       while(timer0_value_read()) timer0_update_value_write(1);
+}
+
+static void sdtimer_init(void)
+{
+       sdtimer_en_write(0);
+       sdtimer_load_write(0xffffffff);
+       sdtimer_reload_write(0xffffffff);
+       sdtimer_en_write(1);
+}
+
+static unsigned int sdtimer_get(void)
+{
+       sdtimer_update_value_write(1);
+       return sdtimer_value_read();
+}
+
+unsigned int sdcard_response[4];
+
+int sdcard_wait_cmd_done(void) {
+       unsigned int cmdevt;
+       while (1) {
+               cmdevt = sdcore_cmdevt_read();
+#ifdef SDCARD_DEBUG
+               printf("cmdevt: %08x\n", cmdevt);
+#endif
+               if (cmdevt & 0x1) {
+                       if (cmdevt & 0x4) {
+#ifdef SDCARD_DEBUG
+                               printf("cmdevt: SD_TIMEOUT\n");
+#endif
+                               return SD_TIMEOUT;
+                       }
+                       else if (cmdevt & 0x8) {
+#ifdef SDCARD_DEBUG
+                               printf("cmdevt: SD_CRCERROR\n");
+                               return SD_CRCERROR;
+#endif
+                       }
+                       return SD_OK;
+               }
+       }
+}
+
+int sdcard_wait_data_done(void) {
+       unsigned int dataevt;
+       while (1) {
+               dataevt = sdcore_dataevt_read();
+#ifdef SDCARD_DEBUG
+               printf("dataevt: %08x\n", dataevt);
+#endif
+               if (dataevt & 0x1) {
+                       if (dataevt & 0x4)
+                               return SD_TIMEOUT;
+                       else if (dataevt & 0x8)
+                               return SD_CRCERROR;
+                       return SD_OK;
+               }
+       }
+}
+
+int sdcard_wait_response(void) {
+       int status = sdcard_wait_cmd_done();
+
+       csr_rd_buf_uint32(CSR_SDCORE_RESPONSE_ADDR, sdcard_response, 4);
+#ifdef SDCARD_DEBUG
+       printf("sdcard_response = [%08x, %08x, %08x, %08x];\n",
+               sdcard_response[0], sdcard_response[1],
+               sdcard_response[2], sdcard_response[3]);
+#endif
+
+       return status;
+}
+
+/* commands */
+
+void sdcard_go_idle(void) {
+#ifdef SDCARD_DEBUG
+       printf("CMD0: GO_IDLE\n");
+#endif
+       sdcore_argument_write(0x00000000);
+       sdcore_command_write((0 << 8) | SDCARD_CTRL_RESPONSE_NONE);
+}
+
+int sdcard_send_ext_csd(void) {
+#ifdef SDCARD_DEBUG
+       printf("CMD8: SEND_EXT_CSD\n");
+#endif
+       sdcore_argument_write(0x000001aa);
+       sdcore_command_write((8 << 8) | SDCARD_CTRL_RESPONSE_NONE);
+       busy_wait(1);
+       return sdcard_wait_response();
+}
+
+int sdcard_app_cmd(int rca) {
+#ifdef SDCARD_DEBUG
+       printf("CMD55: APP_CMD\n");
+#endif
+       sdcore_argument_write(rca << 16);
+       sdcore_command_write((55 << 8) | SDCARD_CTRL_RESPONSE_SHORT);
+       busy_wait(1);
+       return sdcard_wait_response();
+}
+
+int sdcard_app_send_op_cond(int hcs, int s18r) {
+       unsigned int arg;
+#ifdef SDCARD_DEBUG
+       printf("ACMD41: APP_SEND_OP_COND\n");
+#endif
+       arg = 0x10ff8000;
+       if (hcs)
+               arg |= 0x60000000;
+       if (s18r)
+               arg |= 0x01000000;
+       sdcore_argument_write(arg);
+       sdcore_command_write((41 << 8) | SDCARD_CTRL_RESPONSE_SHORT);
+       busy_wait(1);
+       return sdcard_wait_response();
+}
+
+int sdcard_all_send_cid(void) {
+#ifdef SDCARD_DEBUG
+       printf("CMD2: ALL_SEND_CID\n");
+#endif
+       sdcore_argument_write(0x00000000);
+       sdcore_command_write((2 << 8) | SDCARD_CTRL_RESPONSE_LONG);
+       busy_wait(1);
+       return sdcard_wait_response();
+}
+
+int sdcard_set_relative_address(void) {
+#ifdef SDCARD_DEBUG
+       printf("CMD3: SET_RELATIVE_ADDRESS\n");
+#endif
+       sdcore_argument_write(0x00000000);
+       sdcore_command_write((3 << 8) | SDCARD_CTRL_RESPONSE_SHORT);
+       busy_wait(1);
+       return sdcard_wait_response();
+}
+
+int sdcard_send_cid(unsigned int rca) {
+#ifdef SDCARD_DEBUG
+       printf("CMD10: SEND_CID\n");
+#endif
+       sdcore_argument_write(rca << 16);
+       sdcore_command_write((10 << 8) | SDCARD_CTRL_RESPONSE_LONG);
+       busy_wait(1);
+       return sdcard_wait_response();
+}
+
+int sdcard_send_csd(unsigned int rca) {
+#ifdef SDCARD_DEBUG
+       printf("CMD9: SEND_CSD\n");
+#endif
+       sdcore_argument_write(rca << 16);
+       sdcore_command_write((9 << 8) | SDCARD_CTRL_RESPONSE_LONG);
+       busy_wait(1);
+       return sdcard_wait_response();
+}
+
+int sdcard_select_card(unsigned int rca) {
+#ifdef SDCARD_DEBUG
+       printf("CMD7: SELECT_CARD\n");
+#endif
+       sdcore_argument_write(rca << 16);
+       sdcore_command_write((7 << 8) | SDCARD_CTRL_RESPONSE_SHORT);
+       busy_wait(1);
+       return sdcard_wait_response();
+}
+
+int sdcard_app_set_bus_width(void) {
+#ifdef SDCARD_DEBUG
+       printf("ACMD6: SET_BUS_WIDTH\n");
+#endif
+       sdcore_argument_write(0x00000002);
+       sdcore_command_write((6 << 8) | SDCARD_CTRL_RESPONSE_SHORT);
+       busy_wait(1);
+       return sdcard_wait_response();
+}
+
+int sdcard_switch(unsigned int mode, unsigned int group, unsigned int value, unsigned int dstaddr) {
+       unsigned int arg;
+
+#ifdef SDCARD_DEBUG
+       printf("CMD6: SWITCH_FUNC\n");
+#endif
+       arg = (mode << 31) | 0xffffff;
+       arg &= ~(0xf << (group * 4));
+       arg |= value << (group * 4);
+
+       sdcore_argument_write(arg);
+       sdcore_blocksize_write(64);
+       sdcore_blockcount_write(1);
+       sdcore_command_write((6 << 8) | SDCARD_CTRL_RESPONSE_SHORT |
+                                                (SDCARD_CTRL_DATA_TRANSFER_READ << 5));
+       busy_wait(1);
+       sdcard_wait_response();
+       busy_wait(1);
+       return sdcard_wait_data_done();
+}
+
+int sdcard_app_send_scr(void) {
+#ifdef SDCARD_DEBUG
+       printf("CMD51: APP_SEND_SCR\n");
+#endif
+       sdcore_argument_write(0x00000000);
+       sdcore_blocksize_write(8);
+       sdcore_blockcount_write(1);
+       sdcore_command_write((51 << 8) | SDCARD_CTRL_RESPONSE_SHORT |
+                                                (SDCARD_CTRL_DATA_TRANSFER_READ << 5));
+       busy_wait(1);
+       sdcard_wait_response();
+       busy_wait(1);
+       return sdcard_wait_data_done();
+}
+
+
+int sdcard_app_set_blocklen(unsigned int blocklen) {
+#ifdef SDCARD_DEBUG
+       printf("CMD16: SET_BLOCKLEN\n");
+#endif
+       sdcore_argument_write(blocklen);
+       sdcore_command_write((16 << 8) | SDCARD_CTRL_RESPONSE_SHORT);
+       busy_wait(1);
+       return sdcard_wait_response();
+}
+
+int sdcard_write_single_block(unsigned int blockaddr) {
+#ifdef SDCARD_DEBUG
+       printf("CMD24: WRITE_SINGLE_BLOCK\n");
+#endif
+       int cmd_response = -1;
+       while (cmd_response != SD_OK) {
+               sdcore_argument_write(blockaddr);
+               sdcore_blocksize_write(512);
+               sdcore_blockcount_write(1);
+               sdcore_command_write((24 << 8) | SDCARD_CTRL_RESPONSE_SHORT |
+                                                        (SDCARD_CTRL_DATA_TRANSFER_WRITE << 5));
+               cmd_response = sdcard_wait_response();
+       }
+       return cmd_response;
+}
+
+int sdcard_write_multiple_block(unsigned int blockaddr, unsigned int blockcnt) {
+#ifdef SDCARD_DEBUG
+       printf("CMD25: WRITE_MULTIPLE_BLOCK\n");
+#endif
+       int cmd_response = -1;
+       while (cmd_response != SD_OK) {
+               sdcore_argument_write(blockaddr);
+               sdcore_blocksize_write(512);
+               sdcore_blockcount_write(blockcnt);
+               sdcore_command_write((25 << 8) | SDCARD_CTRL_RESPONSE_SHORT |
+                                                        (SDCARD_CTRL_DATA_TRANSFER_WRITE << 5));
+               cmd_response = sdcard_wait_response();
+       }
+       return cmd_response;
+}
+
+int sdcard_read_single_block(unsigned int blockaddr) {
+#ifdef SDCARD_DEBUG
+       printf("CMD17: READ_SINGLE_BLOCK\n");
+#endif
+int cmd_response = -1;
+       while (cmd_response != SD_OK) {
+               sdcore_argument_write(blockaddr);
+               sdcore_blocksize_write(512);
+               sdcore_blockcount_write(1);
+               sdcore_command_write((17 << 8) | SDCARD_CTRL_RESPONSE_SHORT |
+                                                        (SDCARD_CTRL_DATA_TRANSFER_READ << 5));
+               cmd_response = sdcard_wait_response();
+       }
+       return sdcard_wait_data_done();
+}
+
+int sdcard_read_multiple_block(unsigned int blockaddr, unsigned int blockcnt) {
+#ifdef SDCARD_DEBUG
+       printf("CMD18: READ_MULTIPLE_BLOCK\n");
+#endif
+int cmd_response = -1;
+       while (cmd_response != SD_OK) {
+               sdcore_argument_write(blockaddr);
+               sdcore_blocksize_write(512);
+               sdcore_blockcount_write(blockcnt);
+               sdcore_command_write((18 << 8) | SDCARD_CTRL_RESPONSE_SHORT |
+                                                        (SDCARD_CTRL_DATA_TRANSFER_READ << 5));
+               cmd_response = sdcard_wait_response();
+       }
+       return cmd_response;
+}
+
+int sdcard_stop_transmission(void) {
+#ifdef SDCARD_DEBUG
+       printf("CMD12: STOP_TRANSMISSION\n");
+#endif
+       sdcore_argument_write(0x0000000);
+       sdcore_command_write((12 << 8) | SDCARD_CTRL_RESPONSE_SHORT);
+       busy_wait(1);
+       return sdcard_wait_response();
+}
+
+int sdcard_send_status(unsigned int rca) {
+#ifdef SDCARD_DEBUG
+       printf("CMD13: SEND_STATUS\n");
+#endif
+       sdcore_argument_write(rca << 16);
+       sdcore_command_write((13 << 8) | SDCARD_CTRL_RESPONSE_SHORT);
+       busy_wait(1);
+       return sdcard_wait_response();
+}
+
+int sdcard_set_block_count(unsigned int blockcnt) {
+#ifdef SDCARD_DEBUG
+       printf("CMD23: SET_BLOCK_COUNT\n");
+#endif
+       sdcore_argument_write(blockcnt);
+       sdcore_command_write((23 << 8) | SDCARD_CTRL_RESPONSE_SHORT);
+       busy_wait(1);
+       return sdcard_wait_response();
+}
+
+void sdcard_decode_cid(void) {
+       printf(
+               "CID Register: 0x%08x%08x%08x%08x\n"
+               "Manufacturer ID: 0x%x\n"
+               "Application ID 0x%x\n"
+               "Product name: %c%c%c%c%c\n",
+                       sdcard_response[0],
+                       sdcard_response[1],
+                       sdcard_response[2],
+                       sdcard_response[3],
+
+                       (sdcard_response[0] >> 16) & 0xffff,
+
+                       sdcard_response[0] & 0xffff,
+
+                       (sdcard_response[1] >> 24) & 0xff,
+                       (sdcard_response[1] >> 16) & 0xff,
+                       (sdcard_response[1] >>  8) & 0xff,
+                       (sdcard_response[1] >>  0) & 0xff,
+                       (sdcard_response[2] >> 24) & 0xff
+               );
+}
+
+void sdcard_decode_csd(void) {
+       /* FIXME: only support CSR structure version 2.0 */
+       printf(
+               "CSD Register: 0x%x%08x%08x%08x\n"
+               "Max data transfer rate: %d MB/s\n"
+               "Max read block length: %d bytes\n"
+               "Device size: %d GB\n",
+                       sdcard_response[0],
+                       sdcard_response[1],
+                       sdcard_response[2],
+                       sdcard_response[3],
+
+                       (sdcard_response[1] >> 24) & 0xff,
+
+                       (1 << ((sdcard_response[1] >> 8) & 0xf)),
+
+                       ((sdcard_response[2] >> 8) & 0x3fffff)*512/(1024*1024)
+       );
+}
+
+/* bist */
+
+void sdcard_bist_generator_start(unsigned int blockcnt) {
+       bist_generator_reset_write(1);
+       bist_generator_count_write(blockcnt);
+       bist_generator_start_write(1);
+}
+
+void sdcard_bist_generator_wait(void) {
+       while((bist_generator_done_read() & 0x1) == 0);
+}
+
+
+void sdcard_bist_checker_start(unsigned int blockcnt) {
+       bist_checker_reset_write(1);
+       bist_checker_count_write(blockcnt);
+       bist_checker_start_write(1);
+}
+
+void sdcard_bist_checker_wait(void) {
+       while((bist_checker_done_read() & 0x1) == 0);
+}
+
+/* user */
+
+int sdcard_init(void) {
+        unsigned short rca;
+
+        /* initialize SD driver parameters */
+       sdcore_cmdtimeout_write(1<<19);
+       sdcore_datatimeout_write(1<<19);
+
+       sdtimer_init();
+
+       /* reset card */
+       sdcard_go_idle();
+       busy_wait(1);
+       sdcard_send_ext_csd();
+
+       /* wait for card to be ready */
+       /* FIXME: 1.8v support */
+       for(;;) {
+               sdcard_app_cmd(0);
+               sdcard_app_send_op_cond(1, 0);
+               if (sdcard_response[3] & 0x80000000) {
+                       break;
+               }
+               busy_wait(1);
+       }
+
+       /* send identification */
+       sdcard_all_send_cid();
+#ifdef SDCARD_DEBUG
+       sdcard_decode_cid();
+#endif
+
+       /* set relative card address */
+       sdcard_set_relative_address();
+       rca = (sdcard_response[3] >> 16) & 0xffff;
+
+       /* set cid */
+       sdcard_send_cid(rca);
+#ifdef SDCARD_DEBUG
+       /* FIXME: add cid decoding (optional) */
+#endif
+
+       /* set csd */
+       sdcard_send_csd(rca);
+#ifdef SDCARD_DEBUG
+       sdcard_decode_csd();
+#endif
+
+       /* select card */
+       sdcard_select_card(rca);
+
+       /* set bus width */
+       sdcard_app_cmd(rca);
+       sdcard_app_set_bus_width();
+
+       /* switch speed */
+       sdcard_switch(SD_SWITCH_SWITCH, SD_GROUP_ACCESSMODE, SD_SPEED_SDR104, SRAM_BASE);
+
+       /* switch driver strength */
+       sdcard_switch(SD_SWITCH_SWITCH, SD_GROUP_DRIVERSTRENGTH, SD_DRIVER_STRENGTH_D, SRAM_BASE);
+
+       /* send scr */
+       /* FIXME: add scr decoding (optional) */
+       sdcard_app_cmd(rca);
+       sdcard_app_send_scr();
+
+       /* set block length */
+       sdcard_app_set_blocklen(512);
+
+       return 0;
+}
+
+int sdcard_test(unsigned int loops) {
+       unsigned int i;
+       unsigned int length;
+       unsigned int blocks;
+       unsigned int start;
+       unsigned int end;
+       unsigned int errors = 0;
+       unsigned long write_speed, read_speed;
+
+       sdcore_cmdtimeout_write(1<<19);
+       sdcore_datatimeout_write(1<<19);
+
+       sdtimer_init();
+
+       length = 4*1024*1024;
+       blocks = length/512;
+
+       for(i=0; i<loops; i++) {
+               /* write */
+               start = sdtimer_get();
+               sdcard_set_block_count(blocks);
+               sdcard_bist_generator_start(blocks);
+               sdcard_write_multiple_block(i, blocks);
+               sdcard_bist_generator_wait();
+               sdcard_stop_transmission();
+               end = sdtimer_get();
+               write_speed = length*(CONFIG_CLOCK_FREQUENCY/100000)/((start - end)/100000);
+
+               /* delay FIXME */
+               busy_wait(200);
+
+               /* read */
+               start = sdtimer_get();
+               sdcard_set_block_count(blocks);
+               sdcard_bist_checker_start(blocks);
+               sdcard_read_multiple_block(i, blocks);
+               sdcard_bist_checker_wait();
+               end = sdtimer_get();
+               read_speed = length*(CONFIG_CLOCK_FREQUENCY/100000)/((start - end)/100000);
+
+               /* errors */
+               errors = bist_checker_errors_read();
+
+               /* infos */
+               if ((i%8) == 0)
+                       printf("LOOP WRITE_SPEED  READ_SPEED ERRORS\n");
+               printf("%4d %6d MB/s %6d MB/s %6d\n",
+                       i,
+                       write_speed/(1024*1024),
+                       read_speed/(1024*1024),
+                       errors
+               );
+       }
+
+       return errors;
+}
+
+#endif /* CSR_SDCORE_BASE */
diff --git a/litex/soc/software/bios/sdcard.h b/litex/soc/software/bios/sdcard.h
new file mode 100644 (file)
index 0000000..58f97c3
--- /dev/null
@@ -0,0 +1,99 @@
+// This file is Copyright (c) 2017 Florent Kermarrec <florent@enjoy-digital.fr>
+// License: BSD
+
+#ifndef __SDCARD_H
+#define __SDCARD_H
+
+#include <generated/csr.h>
+
+#ifdef CSR_SDCORE_BASE
+
+#define SD_OK         0
+#define SD_CRCERROR   1
+#define SD_TIMEOUT    2
+#define SD_WRITEERROR 3
+
+#define SD_SWITCH_CHECK  0
+#define SD_SWITCH_SWITCH 1
+
+#define SD_SPEED_SDR12  0
+#define SD_SPEED_SDR25  1
+#define SD_SPEED_SDR50  2
+#define SD_SPEED_SDR104 3
+#define SD_SPEED_DDR50  4
+
+#define SD_DRIVER_STRENGTH_B 0
+#define SD_DRIVER_STRENGTH_A 1
+#define SD_DRIVER_STRENGTH_C 2
+#define SD_DRIVER_STRENGTH_D 3
+
+#define SD_GROUP_ACCESSMODE     0
+#define SD_GROUP_COMMANDSYSTEM  1
+#define SD_GROUP_DRIVERSTRENGTH 2
+#define SD_GROUP_POWERLIMIT     3
+
+#define SDCARD_STREAM_STATUS_OK           0b000
+#define SDCARD_STREAM_STATUS_TIMEOUT      0b001
+#define SDCARD_STREAM_STATUS_DATAACCEPTED 0b010
+#define SDCARD_STREAM_STATUS_CRCERROR     0b101
+#define SDCARD_STREAM_STATUS_WRITEERROR   0b110
+
+#define SDCARD_CTRL_DATA_TRANSFER_NONE  0
+#define SDCARD_CTRL_DATA_TRANSFER_READ  1
+#define SDCARD_CTRL_DATA_TRANSFER_WRITE 2
+
+#define SDCARD_CTRL_RESPONSE_NONE  0
+#define SDCARD_CTRL_RESPONSE_SHORT 1
+#define SDCARD_CTRL_RESPONSE_LONG  2
+
+/* clocking */
+
+void sdclk_set_clk(unsigned int freq);
+
+/* command utils */
+
+int sdcard_wait_cmd_done(void);
+int sdcard_wait_data_done(void);
+int sdcard_wait_response(void);
+
+/* commands */
+
+void sdcard_go_idle(void);
+int sdcard_send_ext_csd(void);
+int sdcard_app_cmd(int rca);
+int sdcard_app_send_op_cond(int hcc, int s18r);
+int sdcard_all_send_cid(void);
+int sdcard_set_relative_address(void);
+
+int sdcard_send_cid(unsigned int rca);
+void sdcard_decode_cid(void);
+int sdcard_send_csd(unsigned int rca);
+void sdcard_decode_csd(void);
+int sdcard_select_card(unsigned int rca);
+int sdcard_app_set_bus_width(void);
+int sdcard_switch(unsigned int mode, unsigned int group, unsigned int value, unsigned int dstaddr);
+int sdcard_app_send_scr(void);
+int sdcard_app_set_blocklen(unsigned int blocklen);
+int sdcard_write_single_block(unsigned int blockaddr);
+int sdcard_write_multiple_block(unsigned int blockaddr, unsigned int blockcnt);
+int sdcard_read_single_block(unsigned int blockaddr);
+int sdcard_read_multiple_block(unsigned int blockaddr, unsigned int blockcnt);
+int sdcard_stop_transmission(void);
+int sdcard_send_status(unsigned int rca);
+int sdcard_set_block_count(unsigned int blockcnt);
+
+/* bist */
+
+void sdcard_bist_generator_start(unsigned int blockcnt);
+void sdcard_bist_generator_wait(void);
+void sdcard_bist_checker_start(unsigned int blockcnt);
+void sdcard_bist_checker_wait(void);
+
+/* user */
+
+int sdcard_init(void);
+int sdcard_test(unsigned int loops);
+
+#endif /* CSR_SDCORE_BASE */
+
+#endif /* __SDCARD_H */