Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bios/spiflash: fix write/ erase, add write from sdcard and range erase #1677

Merged
merged 4 commits into from
Apr 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 90 additions & 21 deletions litex/soc/software/bios/cmds/cmd_spiflash.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,26 @@
#include "../command.h"
#include "../helpers.h"

#include <libbase/progress.h>
#include <liblitespi/spiflash.h>
#include <libfatfs/ff.h>

/**
* 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 <offset> <value> [count]");
printf("flash_write <offset> <mem_addr> [count (bytes)]");
return;
}

Expand All @@ -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;
}

Expand All @@ -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 <filename>");
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);

define_command(flash_erase, flash_erase_handler, "Erase whole flash", SPIFLASH_CMDS);
#endif
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 <offset> <count (bytes)>");
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_range, flash_erase_range_handler, "Erase flash range", SPIFLASH_CMDS);
#endif
143 changes: 143 additions & 0 deletions litex/soc/software/liblitespi/spiflash.c
Original file line number Diff line number Diff line change
Expand Up @@ -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<len; i+=SPI_FLASH_ERASE_SIZE) {
printf("Erase SPI Flash @0x%08lx", ((uint32_t)addr+i));
spiflash_write_enable();
spiflash_sector_erase(addr+i);

while (spiflash_read_status_register() & 1) {
printf(".");
cdelay(CONFIG_CLOCK_FREQUENCY/25);
}
printf("\n");

/* check if region was really erased */
for (j = 0; j < SPI_FLASH_ERASE_SIZE; j++) {
uint8_t* peek = (((uint8_t*)SPIFLASH_BASE)+addr+i+j);
if (*peek != 0xff) {
printf("Error: location 0x%08lx not erased (%0x2x)\n", addr+i+j, *peek);
}
}
}
}

int spiflash_write_stream(uint32_t addr, uint8_t *stream, uint32_t len)
{
int res = 0;
uint32_t w_len = min(len, SPI_FLASH_BLOCK_SIZE);
uint32_t offset = 0;
uint32_t j = 0;

#if SPIFLASH_DEBUG
printf("Write SPI Flash @0x%08lx", ((uint32_t)addr));
#endif

while(w_len) {
spiflash_write_enable();
page_program(addr+offset, stream+offset, w_len);

while(spiflash_read_status_register() & 1) {
#if SPIFLASH_DEBUG
printf(".");
#endif
}

for (j = 0; j < w_len; j++) {
uint8_t* peek = (((uint8_t*)SPIFLASH_BASE)+addr+offset+j);
if (*peek != stream[offset+j]) {
printf("Error: verify failed at 0x%08lx (0x%02x should be 0x%02x)\n", (uint32_t)peek, *peek, stream[offset+j]);
}
}

offset += w_len;
w_len = min(len-offset, SPI_FLASH_BLOCK_SIZE);
res = offset;
}
#if SPIFLASH_DEBUG
printf("\n");
#endif
return res;
}

#endif

void spiflash_memspeed(void) {
Expand Down
2 changes: 2 additions & 0 deletions litex/soc/software/liblitespi/spiflash.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ int spiflash_freq_init(void);
void spiflash_dummy_bits_setup(unsigned int dummy_bits);
void spiflash_memspeed(void);
void spiflash_init(void);
int spiflash_write_stream(uint32_t addr, uint8_t *stream, uint32_t len);
void spiflash_erase_range(uint32_t addr, uint32_t len);

#ifdef __cplusplus
}
Expand Down