diff --git a/litex/soc/software/bios/cmds/cmd_spiflash.c b/litex/soc/software/bios/cmds/cmd_spiflash.c index 1038f7a79..7b1dacc62 100644 --- a/litex/soc/software/bios/cmds/cmd_spiflash.c +++ b/litex/soc/software/bios/cmds/cmd_spiflash.c @@ -8,23 +8,26 @@ #include "../command.h" #include "../helpers.h" +#include +#include +#include + /** * Command "flash_write" * * Write data from a memory buffer to SPI flash * */ -#if (defined CSR_SPIFLASH_BASE && defined SPIFLASH_PAGE_SIZE) +#if (defined CSR_SPIFLASH_CORE_MASTER_CS_ADDR) static void flash_write_handler(int nb_params, char **params) { char *c; unsigned int addr; - unsigned int value; + unsigned int mem_addr; unsigned int count; - unsigned int i; if (nb_params < 2) { - printf("flash_write [count]"); + printf("flash_write [count (bytes)]"); return; } @@ -34,9 +37,9 @@ static void flash_write_handler(int nb_params, char **params) return; } - value = strtoul(params[1], &c, 0); + mem_addr = strtoul(params[1], &c, 0); if (*c != 0) { - printf("Incorrect value"); + printf("Incorrect mem_addr"); return; } @@ -50,26 +53,92 @@ static void flash_write_handler(int nb_params, char **params) } } - for (i = 0; i < count; i++) - write_to_flash(addr + i * 4, (unsigned char *)&value, 4); + spiflash_write_stream(addr, (unsigned char *)mem_addr, count); } define_command(flash_write, flash_write_handler, "Write to flash", SPIFLASH_CMDS); -#endif -/** - * Command "flash_erase" - * - * Flash erase - * - */ -#if (defined CSR_SPIFLASH_BASE && defined SPIFLASH_PAGE_SIZE) -static void flash_erase_handler(int nb_params, char **params) +static void flash_from_sdcard_handler(int nb_params, char **params) { - erase_flash(); - printf("Flash erased\n"); + FRESULT fr; + FATFS fs; + FIL file; + uint32_t br; + uint32_t offset; + unsigned long length; + uint8_t buf[512]; + + if (nb_params < 1) { + printf("flash_from_sdcard "); + return; + } + + char* filename = params[0]; + + fr = f_mount(&fs, "", 1); + if (fr != FR_OK) + return; + fr = f_open(&file, filename, FA_READ); + if (fr != FR_OK) { + printf("%s file not found.\n", filename); + f_mount(0, "", 0); + return; + } + + length = f_size(&file); + printf("Copying %s to SPI flash (%ld bytes)...\n", filename, length); + init_progression_bar(length); + offset = 0; + for (;;) { + fr = f_read(&file, (void*) buf, 512, (UINT *)&br); + if (fr != FR_OK) { + printf("file read error.\n"); + f_close(&file); + f_mount(0, "", 0); + return; + } + if (br == 0) { + break; + } else { + spiflash_write_stream(offset, buf, br); + } + + offset += br; + show_progress(offset); + } + show_progress(offset); + printf("\n"); + + f_close(&file); + f_mount(0, "", 0); +} +define_command(flash_from_sdcard, flash_from_sdcard_handler, "Write file from SD card to flash", SPIFLASH_CMDS); + +static void flash_erase_range_handler(int nb_params, char **params) +{ + char *c; + uint32_t addr; + uint32_t count; + + if (nb_params < 2) { + printf("flash_erase "); + return; + } + + addr = strtoul(params[0], &c, 0); + if (*c != 0) { + printf("Incorrect offset"); + return; + } + + count = strtoul(params[1], &c, 0); + if (*c != 0) { + printf("Incorrect count"); + return; + } + + spiflash_erase_range(addr, count); } -define_command(flash_erase, flash_erase_handler, "Erase whole flash", SPIFLASH_CMDS); +define_command(flash_erase_range, flash_erase_range_handler, "Erase flash range", SPIFLASH_CMDS); #endif - diff --git a/litex/soc/software/liblitespi/spiflash.c b/litex/soc/software/liblitespi/spiflash.c index cdc4552e1..5bd827318 100644 --- a/litex/soc/software/liblitespi/spiflash.c +++ b/litex/soc/software/liblitespi/spiflash.c @@ -91,6 +91,149 @@ static void spiflash_master_write(uint32_t val, size_t len, size_t width, uint32 spiflash_core_master_cs_write(0); } +static volatile uint8_t w_buf[SPI_FLASH_BLOCK_SIZE + 4]; +static volatile uint8_t r_buf[SPI_FLASH_BLOCK_SIZE + 4]; + +static uint32_t transfer_byte(uint8_t b) +{ + /* wait for tx ready */ + while (!spiflash_core_master_status_tx_ready_read()); + + spiflash_core_master_rxtx_write((uint32_t)b); + + /* wait for rx ready */ + while (!spiflash_core_master_status_rx_ready_read()); + + return spiflash_core_master_rxtx_read(); +} + +static void transfer_cmd(uint8_t *bs, uint8_t *resp, int len) +{ + spiflash_core_master_phyconfig_len_write(8); + spiflash_core_master_phyconfig_width_write(1); + spiflash_core_master_phyconfig_mask_write(1); + spiflash_core_master_cs_write(1); + + flush_cpu_dcache(); + for (int i=0; i < len; i++) { + resp[i] = transfer_byte(bs[i]); + } + + spiflash_core_master_cs_write(0); + flush_cpu_dcache(); +} + +static uint32_t spiflash_read_status_register(void) +{ + volatile uint8_t buf[4]; + w_buf[0] = 0x05; + w_buf[1] = 0x00; + transfer_cmd(w_buf, buf, 4); + +#if SPIFLASH_DEBUG + printf("[SR: %02x %02x %02x %02x]", buf[0], buf[1], buf[2], buf[3]); +#endif + + /* FIXME normally the status should be in buf[1], + but we have to read it a few more times to be + stable for unknown reasons */ + return buf[3]; +} + +static void spiflash_write_enable(void) +{ + uint8_t buf[1]; + w_buf[0] = 0x06; + transfer_cmd(w_buf, buf, 1); +} + +static void page_program(uint32_t addr, uint8_t *data, int len) +{ + w_buf[0] = 0x02; + w_buf[1] = addr>>16; + w_buf[2] = addr>>8; + w_buf[3] = addr>>0; + memcpy(w_buf+4, data, len); + transfer_cmd(w_buf, r_buf, len+4); +} + +static void spiflash_sector_erase(uint32_t addr) +{ + w_buf[0] = 0xd8; + w_buf[1] = addr>>16; + w_buf[2] = addr>>8; + w_buf[3] = addr>>0; + transfer_cmd(w_buf, r_buf, 4); +} + +/* erase page size in bytes, check flash datasheet */ +#define SPI_FLASH_ERASE_SIZE (64*1024) + +#define min(x, y) (((x) < (y)) ? (x) : (y)) + +void spiflash_erase_range(uint32_t addr, uint32_t len) +{ + uint32_t i = 0; + uint32_t j = 0; + for (i=0; i