1
0
mirror of https://github.com/corundum/corundum.git synced 2025-01-16 08:12:53 +08:00

Add firmware update utility

This commit is contained in:
Alex Forencich 2020-10-04 00:56:06 -07:00
parent dd3374626f
commit 9ec0b71a1a
8 changed files with 2875 additions and 65 deletions

View File

@ -15,7 +15,7 @@ mqnic-config: mqnic-config.c mqnic.c timespec.c
mqnic-dump: mqnic-dump.c mqnic.c
$(CC) $(CFLAGS) $^ -o $@
mqnic-fw: mqnic-fw.c mqnic.c
mqnic-fw: mqnic-fw.c mqnic.c flash.c flash_spi.c flash_bpi.c fpga_id.c bitfile.c
$(CC) $(CFLAGS) $^ -o $@
perout: perout.c timespec.c

183
utils/bitfile.c Normal file
View File

@ -0,0 +1,183 @@
/*
Copyright 2020, The Regents of the University of California.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS
IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of The Regents of the University of California.
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "bitfile.h"
struct bitfile *bitfile_create_from_file(const char *bit_file_name)
{
struct bitfile *bf;
FILE *fp;
char *buffer;
char *data;
size_t len;
fp = fopen(bit_file_name, "rb");
if (!fp)
{
fprintf(stderr, "Failed to open file\n");
return 0;
}
fseek(fp, 0, SEEK_END);
len = ftell(fp);
rewind(fp);
buffer = calloc(len + sizeof(struct bitfile), 1);
if (!buffer)
{
fprintf(stderr, "Failed to allocate memory\n");
goto fail_file;
}
bf = (struct bitfile *)buffer;
data = buffer + sizeof(struct bitfile);
if (fread(data, 1, len, fp) < len)
{
fprintf(stderr, "Error reading file\n");
goto fail_buffer;
}
fclose(fp);
if (bitfile_parse(bf, data, len))
{
fprintf(stderr, "Failed to parse bitfile\n");
goto fail_buffer;
}
return bf;
fail_buffer:
free(buffer);
fail_file:
fclose(fp);
return 0;
}
struct bitfile *bitfile_create_from_buffer(char *buffer, size_t len)
{
struct bitfile *bf;
bf = calloc(1, sizeof(struct bitfile));
if (!bf)
{
fprintf(stderr, "Failed to allocate memory\n");
return 0;
}
if (bitfile_parse(bf, buffer, len))
{
fprintf(stderr, "Failed to parse bitfile\n");
free(bf);
return 0;
}
return bf;
}
int bitfile_parse(struct bitfile *bf, char *buffer, size_t len)
{
char *ptr;
size_t l;
ptr = buffer;
bf->header = ptr;
// drop unknown field
l = be16toh(*((uint16_t *)ptr));
ptr += 2+l;
// drop unknown field
ptr += 2;
while (1)
{
int field_type = *ptr;
ptr += 1;
if (field_type == 'e')
{
l = be32toh(*((uint32_t *)ptr));
bf->data_len = l;
bf->data = ptr+4;
return 0;
}
else
{
l = be16toh(*((uint16_t *)ptr));
ptr += 2;
}
switch (field_type)
{
case 'a':
bf->name = ptr;
break;
case 'b':
bf->part = ptr;
break;
case 'c':
bf->date = ptr;
break;
case 'd':
bf->time = ptr;
break;
default:
fprintf(stderr, "Unknown field type 0x%02x\n", field_type);
goto fail;
}
ptr += l;
}
fail:
return -1;
}
void bitfile_close(struct bitfile *bf)
{
if (bf)
{
free(bf);
}
}

56
utils/bitfile.h Normal file
View File

@ -0,0 +1,56 @@
/*
Copyright 2020, The Regents of the University of California.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS
IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of The Regents of the University of California.
*/
#ifndef BITFILE_H
#define BITFILE_H
struct bitfile {
char *header;
char *name;
char *part;
char *date;
char *time;
size_t data_len;
char *data;
};
struct bitfile *bitfile_create_from_file(const char *bit_file_name);
struct bitfile *bitfile_create_from_buffer(char *buffer, size_t len);
int bitfile_parse(struct bitfile *bf, char *buffer, size_t len);
void bitfile_close(struct bitfile *bf);
#endif // BITFILE_H

136
utils/flash.c Normal file
View File

@ -0,0 +1,136 @@
/*
Copyright 2020, The Regents of the University of California.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS
IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of The Regents of the University of California.
*/
#include "flash.h"
#include <stdlib.h>
extern const struct flash_driver spi_flash_driver;
extern const struct flash_driver bpi_flash_driver;
struct flash_device *flash_open_spi(int data_width, volatile uint8_t *ctrl_reg)
{
struct flash_device *fdev;
if (!ctrl_reg)
return NULL;
fdev = calloc(1, sizeof(struct flash_device));
if (!fdev)
return NULL;
fdev->driver = &spi_flash_driver;
fdev->data_width = data_width;
fdev->ctrl_reg = ctrl_reg;
if (fdev->driver->init(fdev))
{
goto err;
}
return fdev;
err:
flash_release(fdev);
return NULL;
}
struct flash_device *flash_open_bpi(int data_width, volatile uint8_t *ctrl_reg, volatile uint8_t *addr_reg, volatile uint8_t *data_reg)
{
struct flash_device *fdev;
if (!ctrl_reg || !addr_reg || !data_reg)
return NULL;
fdev = calloc(1, sizeof(struct flash_device));
if (!fdev)
return NULL;
fdev->driver = &bpi_flash_driver;
fdev->data_width = data_width;
fdev->ctrl_reg = ctrl_reg;
fdev->addr_reg = addr_reg;
fdev->data_reg = data_reg;
if (fdev->driver->init(fdev))
{
goto err;
}
return fdev;
err:
flash_release(fdev);
return NULL;
}
void flash_release(struct flash_device *fdev)
{
if (!fdev)
return;
fdev->driver->release(fdev);
free(fdev);
}
int flash_read(struct flash_device *fdev, size_t addr, size_t len, void* dest)
{
if (!fdev)
return -1;
return fdev->driver->read(fdev, addr, len, dest);
}
int flash_write(struct flash_device *fdev, size_t addr, size_t len, void* src)
{
if (!fdev)
return -1;
return fdev->driver->write(fdev, addr, len, src);
}
int flash_erase(struct flash_device *fdev, size_t addr, size_t len)
{
if (!fdev)
return -1;
return fdev->driver->erase(fdev, addr, len);
}

96
utils/flash.h Normal file
View File

@ -0,0 +1,96 @@
/*
Copyright 2020, The Regents of the University of California.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS
IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of The Regents of the University of California.
*/
#ifndef FLASH_H
#define FLASH_H
#include <stdint.h>
#include <unistd.h>
#define FLASH_ERASE_REGIONS 2
struct flash_driver;
struct flash_ops;
struct flash_erase_region_info {
size_t block_count;
size_t block_size;
size_t region_start;
size_t region_end;
};
struct flash_device {
const struct flash_driver *driver;
const struct flash_ops *ops;
volatile uint8_t *ctrl_reg;
volatile uint8_t *addr_reg;
volatile uint8_t *data_reg;
size_t size;
int data_width;
size_t write_buffer_size;
size_t erase_block_size;
int protocol;
int bulk_protocol;
int read_dummy_cycles;
int erase_region_count;
struct flash_erase_region_info erase_region[FLASH_ERASE_REGIONS];
};
struct flash_ops {
void (*init)(struct flash_device *fdev);
int (*sector_erase)(struct flash_device *fdev, size_t addr);
int (*buffered_program)(struct flash_device *fdev, size_t addr, size_t len, void *src);
};
struct flash_driver {
int (*init)(struct flash_device *fdev);
void (*release)(struct flash_device *fdev);
int (*read)(struct flash_device *fdev, size_t addr, size_t len, void* dest);
int (*write)(struct flash_device *fdev, size_t addr, size_t len, void* src);
int (*erase)(struct flash_device *fdev, size_t addr, size_t len);
};
struct flash_device *flash_open_spi(int data_width, volatile uint8_t *ctrl_reg);
struct flash_device *flash_open_bpi(int data_width, volatile uint8_t *ctrl_reg, volatile uint8_t *addr_reg, volatile uint8_t *data_reg);
void flash_release(struct flash_device *fdev);
int flash_read(struct flash_device *fdev, size_t addr, size_t len, void* dest);
int flash_write(struct flash_device *fdev, size_t addr, size_t len, void* src);
int flash_erase(struct flash_device *fdev, size_t addr, size_t len);
#endif /* FLASH_H */

671
utils/flash_bpi.c Normal file
View File

@ -0,0 +1,671 @@
/*
Copyright 2020, The Regents of the University of California.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS
IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of The Regents of the University of California.
*/
#include "flash.h"
#include <stdio.h>
#include <stdlib.h>
#define reg_read32(reg) (*((volatile uint32_t *)(reg)))
#define reg_write32(reg, val) (*((volatile uint32_t *)(reg))) = (val)
#define CFI_QUERY_ADDR 0x55
#define CFI_QUERY_DATA 0x98
#define CFI_READ_ARRAY 0xFF
#define CFI_READ_ARRAY_ALT 0xF0
#define CFI_ID_0 0x10
#define CFI_ID_1 0x11
#define CFI_ID_2 0x12
#define CFI_PRI_CMD_SET_0 0x13
#define CFI_PRI_CMD_SET_1 0x14
#define CFI_DEVICE_SIZE 0x27
#define CFI_WRITE_BUFFER_SIZE_0 0x2A
#define CFI_WRITE_BUFFER_SIZE_1 0x2B
#define CFI_ERASE_REGION_COUNT 0x2C
#define CFI_ERASE_REGION_1_INFO_0 0x2D
#define CFI_ERASE_REGION_1_INFO_1 0x2E
#define CFI_ERASE_REGION_1_INFO_2 0x2F
#define CFI_ERASE_REGION_1_INFO_3 0x30
#define CFI_ERASE_REGION_2_INFO_0 0x31
#define CFI_ERASE_REGION_2_INFO_1 0x32
#define CFI_ERASE_REGION_2_INFO_2 0x33
#define CFI_ERASE_REGION_2_INFO_3 0x34
#define BPI_INTEL_READ_ARRAY 0xFF
#define BPI_INTEL_READ_STATUS_REG 0x70
#define BPI_INTEL_READ_ID 0x90
#define BPI_INTEL_CLEAR_STATUS_REG 0x50
#define BPI_INTEL_READ_CONFIG_REG_SETUP 0x60
#define BPI_INTEL_SET_READ_CONFIG_REG 0x03
#define BPI_INTEL_BLOCK_LOCK_SETUP 0x60
#define BPI_INTEL_BLOCK_LOCK 0x01
#define BPI_INTEL_BLOCK_UNLOCK 0xD0
#define BPI_INTEL_BLOCK_ERASE_SETUP 0x20
#define BPI_INTEL_BLOCK_ERASE_CONFIRM 0xD0
#define BPI_INTEL_BUFFERED_PROGRAM_SETUP 0xE8
#define BPI_INTEL_BUFFERED_PROGRAM_CONFIRM 0xD0
#define BPI_AMD_UNLOCK_ADDR_1 0x555
#define BPI_AMD_UNLOCK_DATA_1 0xAA
#define BPI_AMD_UNLOCK_ADDR_2 0x2AA
#define BPI_AMD_UNLOCK_DATA_2 0x55
#define BPI_AMD_UNLOCK_BYPASS_ENTER_ADDR 0x555
#define BPI_AMD_UNLOCK_BYPASS_ENTER_DATA 0x20
#define BPI_AMD_UNLOCK_BYPASS_RESET_1 0x90
#define BPI_AMD_UNLOCK_BYPASS_RESET_2 0x00
#define BPI_AMD_BLOCK_ERASE_SETUP 0x80
#define BPI_AMD_BLOCK_ERASE_CONFIRM 0x30
#define BPI_AMD_BUFFERED_PROGRAM_SETUP 0x25
#define BPI_AMD_BUFFERED_PROGRAM_CONFIRM 0x29
#define BPI_AMD_READ_ARRAY 0xF0
#define BPI_MICRON_READ_ARRAY 0xFF
#define BPI_MICRON_READ_STATUS_REG 0x70
#define BPI_MICRON_READ_ID 0x90
#define BPI_MICRON_CLEAR_STATUS_REG 0x50
#define BPI_MICRON_READ_CONFIG_REG_SETUP 0x60
#define BPI_MICRON_SET_READ_CONFIG_REG 0x03
#define BPI_MICRON_BLOCK_LOCK_SETUP 0x60
#define BPI_MICRON_BLOCK_LOCK 0x01
#define BPI_MICRON_BLOCK_UNLOCK 0xD0
#define BPI_MICRON_BLOCK_ERASE_SETUP 0x20
#define BPI_MICRON_BLOCK_ERASE_CONFIRM 0xD0
#define BPI_MICRON_BUFFERED_PROGRAM_SETUP 0xE9
#define BPI_MICRON_BUFFERED_PROGRAM_CONFIRM 0xD0
#define FLASH_CE_N (1 << 0)
#define FLASH_OE_N (1 << 1)
#define FLASH_WE_N (1 << 2)
#define FLASH_ADV_N (1 << 3)
#define FLASH_DQ_OE (1 << 8)
#define FLASH_REGION_OE (1 << 16)
void bpi_flash_set_addr(struct flash_device *fdev, size_t addr)
{
reg_write32(fdev->addr_reg, addr);
}
uint16_t bpi_flash_read_cur(struct flash_device *fdev)
{
uint16_t val;
reg_write32(fdev->ctrl_reg, FLASH_REGION_OE | FLASH_WE_N);
reg_read32(fdev->data_reg); // dummy read
val = reg_read32(fdev->data_reg);
reg_write32(fdev->ctrl_reg, FLASH_OE_N | FLASH_WE_N | FLASH_ADV_N);
return val;
}
uint16_t bpi_flash_read_word(struct flash_device *fdev, size_t addr)
{
bpi_flash_set_addr(fdev, addr);
return bpi_flash_read_cur(fdev);
}
void bpi_flash_write_cur(struct flash_device *fdev, uint16_t data)
{
reg_write32(fdev->data_reg, data);
reg_write32(fdev->ctrl_reg, FLASH_REGION_OE | FLASH_DQ_OE | FLASH_OE_N);
reg_read32(fdev->data_reg); // dummy read
reg_write32(fdev->ctrl_reg, FLASH_OE_N | FLASH_WE_N | FLASH_ADV_N);
}
void bpi_flash_write_word(struct flash_device *fdev, size_t addr, uint16_t data)
{
bpi_flash_set_addr(fdev, addr);
bpi_flash_write_cur(fdev, data);
}
void bpi_flash_deselect(struct flash_device *fdev)
{
bpi_flash_write_word(fdev, 0, CFI_READ_ARRAY);
reg_write32(fdev->ctrl_reg, FLASH_CE_N | FLASH_OE_N | FLASH_WE_N | FLASH_ADV_N);
}
// Intel flash ops (0x0001)
uint16_t bpi_flash_intel_read_status_register(struct flash_device *fdev)
{
bpi_flash_write_cur(fdev, BPI_INTEL_READ_STATUS_REG);
return bpi_flash_read_cur(fdev);
}
void bpi_flash_intel_clear_status_register(struct flash_device *fdev)
{
bpi_flash_write_cur(fdev, BPI_INTEL_CLEAR_STATUS_REG);
}
void bpi_flash_intel_init(struct flash_device *fdev)
{
bpi_flash_intel_clear_status_register(fdev);
}
int bpi_flash_intel_sector_erase(struct flash_device *fdev, size_t addr)
{
bpi_flash_set_addr(fdev, addr);
bpi_flash_write_cur(fdev, BPI_INTEL_BLOCK_LOCK_SETUP);
bpi_flash_write_cur(fdev, BPI_INTEL_BLOCK_UNLOCK);
if (bpi_flash_intel_read_status_register(fdev) & 0x30)
{
fprintf(stderr, "Failed to unlock block\n");
return -1;
}
bpi_flash_write_cur(fdev, BPI_INTEL_BLOCK_ERASE_SETUP);
bpi_flash_write_cur(fdev, BPI_INTEL_BLOCK_ERASE_CONFIRM);
while (!(bpi_flash_intel_read_status_register(fdev) & 0x80)) {};
if (bpi_flash_intel_read_status_register(fdev) & 0x30)
{
fprintf(stderr, "Failed to erase block\n");
return -1;
}
return 0;
}
int bpi_flash_intel_buffered_program(struct flash_device *fdev, size_t addr, size_t len, void *src)
{
uint8_t *s = src;
bpi_flash_set_addr(fdev, addr);
bpi_flash_write_cur(fdev, BPI_INTEL_BUFFERED_PROGRAM_SETUP);
bpi_flash_write_cur(fdev, len-1);
for (size_t i = 0; i < len; i++)
{
bpi_flash_write_word(fdev, addr+i, s[0] | (s[1] << 8));
s += 2;
}
bpi_flash_set_addr(fdev, addr);
bpi_flash_write_cur(fdev, BPI_INTEL_BUFFERED_PROGRAM_CONFIRM);
while (!(bpi_flash_intel_read_status_register(fdev) & 0x80)) {};
if (bpi_flash_intel_read_status_register(fdev) & 0x30)
{
fprintf(stderr, "Failed to write block\n");
return -1;
}
return 0;
}
const struct flash_ops bpi_flash_intel_ops = {
.init = bpi_flash_intel_init,
.sector_erase = bpi_flash_intel_sector_erase,
.buffered_program = bpi_flash_intel_buffered_program
};
// AMD flash ops (0x0002)
void bpi_flash_amd_unlock(struct flash_device *fdev)
{
bpi_flash_write_word(fdev, BPI_AMD_UNLOCK_ADDR_1, BPI_AMD_UNLOCK_DATA_1);
bpi_flash_write_word(fdev, BPI_AMD_UNLOCK_ADDR_2, BPI_AMD_UNLOCK_DATA_2);
}
void bpi_flash_amd_write_buffer_abort_reset(struct flash_device *fdev)
{
bpi_flash_amd_unlock(fdev);
bpi_flash_write_word(fdev, BPI_AMD_UNLOCK_ADDR_1, BPI_AMD_READ_ARRAY);
}
void bpi_flash_amd_init(struct flash_device *fdev)
{
// write-to-buffer-abort reset (just in case)
bpi_flash_amd_write_buffer_abort_reset(fdev);
}
int bpi_flash_amd_wait_for_operation(struct flash_device *fdev, uint16_t stop_mask)
{
uint16_t read_1, read_2, read_3;
while (1)
{
read_1 = bpi_flash_read_cur(fdev);
read_2 = bpi_flash_read_cur(fdev);
read_3 = bpi_flash_read_cur(fdev);
if ((read_1 ^ read_2) & (read_2 ^ read_3) & 0x40)
{
if (read_1 & stop_mask)
{
return read_1;
}
}
else
{
return 0;
}
}
}
int bpi_flash_amd_sector_erase(struct flash_device *fdev, size_t addr)
{
bpi_flash_amd_unlock(fdev);
bpi_flash_write_word(fdev, BPI_AMD_UNLOCK_ADDR_1, BPI_AMD_BLOCK_ERASE_SETUP);
bpi_flash_amd_unlock(fdev);
bpi_flash_write_word(fdev, addr, BPI_AMD_BLOCK_ERASE_CONFIRM);
while (!(bpi_flash_read_cur(fdev) & 0x08)) {};
if (bpi_flash_amd_wait_for_operation(fdev, 0x20) & 0x20)
{
// write-to-buffer-abort reset
bpi_flash_amd_write_buffer_abort_reset(fdev);
fprintf(stderr, "Failed to erase block\n");
return -1;
}
return 0;
}
int bpi_flash_amd_buffered_program(struct flash_device *fdev, size_t addr, size_t len, void *src)
{
uint8_t *s = src;
bpi_flash_amd_unlock(fdev);
bpi_flash_write_word(fdev, addr, BPI_AMD_BUFFERED_PROGRAM_SETUP);
bpi_flash_write_cur(fdev, len-1);
for (size_t i = 0; i < len; i++)
{
bpi_flash_write_word(fdev, addr+i, s[0] | (s[1] << 8));
s += 2;
}
bpi_flash_set_addr(fdev, addr);
bpi_flash_write_cur(fdev, BPI_AMD_BUFFERED_PROGRAM_CONFIRM);
if (bpi_flash_amd_wait_for_operation(fdev, 0x22) & 0x22)
{
// write-to-buffer-abort reset
bpi_flash_amd_write_buffer_abort_reset(fdev);
fprintf(stderr, "Failed to write block\n");
return -1;
}
return 0;
}
const struct flash_ops bpi_flash_amd_ops = {
.init = bpi_flash_amd_init,
.sector_erase = bpi_flash_amd_sector_erase,
.buffered_program = bpi_flash_amd_buffered_program
};
// Micron flash ops (0x0002)
uint16_t bpi_flash_micron_read_status_register(struct flash_device *fdev)
{
bpi_flash_write_cur(fdev, BPI_MICRON_READ_STATUS_REG);
return bpi_flash_read_cur(fdev);
}
void bpi_flash_micron_clear_status_register(struct flash_device *fdev)
{
bpi_flash_write_cur(fdev, BPI_MICRON_CLEAR_STATUS_REG);
}
void bpi_flash_micron_init(struct flash_device *fdev)
{
bpi_flash_micron_clear_status_register(fdev);
}
int bpi_flash_micron_sector_erase(struct flash_device *fdev, size_t addr)
{
bpi_flash_set_addr(fdev, addr);
bpi_flash_write_cur(fdev, BPI_MICRON_BLOCK_LOCK_SETUP);
bpi_flash_write_cur(fdev, BPI_MICRON_BLOCK_UNLOCK);
if (bpi_flash_micron_read_status_register(fdev) & 0x30)
{
fprintf(stderr, "Failed to unlock block\n");
bpi_flash_write_cur(fdev, CFI_READ_ARRAY);
return -1;
}
bpi_flash_write_cur(fdev, BPI_MICRON_BLOCK_ERASE_SETUP);
bpi_flash_write_cur(fdev, BPI_MICRON_BLOCK_ERASE_CONFIRM);
while (!(bpi_flash_micron_read_status_register(fdev) & 0x80)) {};
if (bpi_flash_micron_read_status_register(fdev) & 0x30)
{
fprintf(stderr, "Failed to erase block\n");
bpi_flash_write_cur(fdev, CFI_READ_ARRAY);
return -1;
}
bpi_flash_write_cur(fdev, CFI_READ_ARRAY);
return 0;
}
int bpi_flash_micron_buffered_program(struct flash_device *fdev, size_t addr, size_t len, void *src)
{
uint8_t *s = src;
bpi_flash_set_addr(fdev, addr);
bpi_flash_write_cur(fdev, BPI_MICRON_BUFFERED_PROGRAM_SETUP);
bpi_flash_write_cur(fdev, len-1);
for (size_t i = 0; i < len; i++)
{
bpi_flash_write_word(fdev, addr+i, s[0] | (s[1] << 8));
s += 2;
}
bpi_flash_set_addr(fdev, addr);
bpi_flash_write_cur(fdev, BPI_MICRON_BUFFERED_PROGRAM_CONFIRM);
while (!(bpi_flash_micron_read_status_register(fdev) & 0x80)) {};
if (bpi_flash_micron_read_status_register(fdev) & 0x30)
{
fprintf(stderr, "Failed to write block\n");
bpi_flash_write_cur(fdev, CFI_READ_ARRAY);
return -1;
}
bpi_flash_write_cur(fdev, CFI_READ_ARRAY);
return 0;
}
const struct flash_ops bpi_flash_micron_ops = {
.init = bpi_flash_micron_init,
.sector_erase = bpi_flash_micron_sector_erase,
.buffered_program = bpi_flash_micron_buffered_program
};
void bpi_flash_release(struct flash_device *fdev)
{
bpi_flash_deselect(fdev);
}
int bpi_flash_init(struct flash_device *fdev)
{
int ret = 0;
if (!fdev)
return -1;
// CFI query
bpi_flash_write_word(fdev, CFI_QUERY_ADDR, CFI_QUERY_DATA);
if (bpi_flash_read_word(fdev, CFI_ID_0) != 'Q')
{
// may be Intel flash in sync read mode; attempt switch to async
bpi_flash_write_word(fdev, 0xf94f, BPI_INTEL_READ_CONFIG_REG_SETUP);
bpi_flash_write_word(fdev, 0xf94f, BPI_INTEL_SET_READ_CONFIG_REG);
bpi_flash_write_word(fdev, CFI_QUERY_ADDR, CFI_QUERY_DATA);
}
if (bpi_flash_read_word(fdev, CFI_ID_0) != 'Q' && ((bpi_flash_read_cur(fdev) ^ bpi_flash_read_cur(fdev)) & 0x44))
{
// may be AMD flash in write buffer abort; perform write buffer abort reset
bpi_flash_amd_write_buffer_abort_reset(fdev);
bpi_flash_write_word(fdev, CFI_QUERY_ADDR, CFI_QUERY_DATA);
}
if (bpi_flash_read_word(fdev, CFI_ID_0) != 'Q' ||
bpi_flash_read_word(fdev, CFI_ID_1) != 'R' ||
bpi_flash_read_word(fdev, CFI_ID_2) != 'Y')
{
fprintf(stderr, "Failed to read flash ID\n");
ret = -1;
goto err;
}
fdev->protocol = bpi_flash_read_word(fdev, CFI_PRI_CMD_SET_0) | (bpi_flash_read_word(fdev, CFI_PRI_CMD_SET_1) << 8);
printf("Command set: %d\n", fdev->protocol);
switch (fdev->protocol)
{
case 0x0001:
// Intel command set (P30)
fdev->ops = &bpi_flash_intel_ops;
break;
case 0x0002:
// AMD command set (S29, MT28)
fdev->ops = &bpi_flash_amd_ops;
break;
case 0x0200:
// Micron
fdev->ops = &bpi_flash_micron_ops;
break;
default:
fprintf(stderr, "Unknown command set: %d\n", fdev->protocol);
ret = -1;
goto err;
}
uint8_t flash_size = bpi_flash_read_word(fdev, CFI_DEVICE_SIZE);
fdev->size = ((size_t)1) << flash_size;
printf("Flash size: %d MB\n", 1 << (flash_size-20));
uint16_t write_buffer_size = bpi_flash_read_word(fdev, CFI_WRITE_BUFFER_SIZE_0) | (bpi_flash_read_word(fdev, CFI_WRITE_BUFFER_SIZE_1) << 8);
fdev->write_buffer_size = ((size_t)1) << write_buffer_size;
printf("Write buffer size: %ld B\n", fdev->write_buffer_size);
fdev->erase_region_count = bpi_flash_read_word(fdev, CFI_ERASE_REGION_COUNT);
printf("Erase regions: %d\n", fdev->erase_region_count);
if (fdev->erase_region_count > 0)
{
fdev->erase_region[0].block_count = (bpi_flash_read_word(fdev, CFI_ERASE_REGION_1_INFO_0) | (bpi_flash_read_word(fdev, CFI_ERASE_REGION_1_INFO_1) << 8)) + 1;
fdev->erase_region[0].block_size = (bpi_flash_read_word(fdev, CFI_ERASE_REGION_1_INFO_2) | (bpi_flash_read_word(fdev, CFI_ERASE_REGION_1_INFO_3) << 8)) * 256;
fdev->erase_region[0].region_start = 0;
fdev->erase_region[0].region_end = fdev->erase_region[0].region_start + fdev->erase_region[0].block_count * fdev->erase_region[0].block_size;
fdev->erase_block_size = fdev->erase_region[0].block_size;
printf("Erase region 0 block count: %ld\n", fdev->erase_region[0].block_count);
printf("Erase region 0 block size: %ld B\n", fdev->erase_region[0].block_size);
printf("Erase region 0 start: 0x%08lx\n", fdev->erase_region[0].region_start);
printf("Erase region 0 end: 0x%08lx\n", fdev->erase_region[0].region_end);
}
else
{
fprintf(stderr, "No erase regions found!\n");
ret = -1;
goto err;
}
if (fdev->erase_region_count > 1)
{
fdev->erase_region[1].block_count = (bpi_flash_read_word(fdev, CFI_ERASE_REGION_2_INFO_0) | (bpi_flash_read_word(fdev, CFI_ERASE_REGION_2_INFO_1) << 8)) + 1;
fdev->erase_region[1].block_size = (bpi_flash_read_word(fdev, CFI_ERASE_REGION_2_INFO_2) | (bpi_flash_read_word(fdev, CFI_ERASE_REGION_2_INFO_3) << 8)) * 256;
fdev->erase_region[1].region_start = fdev->erase_region[0].region_end;
fdev->erase_region[1].region_end = fdev->erase_region[1].region_start + fdev->erase_region[1].block_count * fdev->erase_region[1].block_size;
if (fdev->erase_region[1].block_size > fdev->erase_block_size)
{
fdev->erase_block_size = fdev->erase_region[1].block_size;
}
printf("Erase region 1 block count: %ld\n", fdev->erase_region[1].block_count);
printf("Erase region 1 block size: %ld B\n", fdev->erase_region[1].block_size);
printf("Erase region 1 start: 0x%08lx\n", fdev->erase_region[1].region_start);
printf("Erase region 1 end: 0x%08lx\n", fdev->erase_region[1].region_end);
}
printf("Erase block size: %ld B\n", fdev->erase_block_size);
fdev->ops->init(fdev);
err:
bpi_flash_release(fdev);
return ret;
}
int bpi_flash_read(struct flash_device *fdev, size_t addr, size_t len, void *dest)
{
char *d = dest;
bpi_flash_write_word(fdev, 0, CFI_READ_ARRAY);
if (addr & 1)
{
*d = bpi_flash_read_word(fdev, addr >> 1) >> 8;
addr++;
len--;
d++;
}
while (len > 1)
{
*((uint16_t *)d) = bpi_flash_read_word(fdev, addr >> 1);
addr += 2;
len -= 2;
d += 2;
}
if (len)
{
*d = bpi_flash_read_word(fdev, addr >> 1);
addr++;
len--;
d++;
}
bpi_flash_deselect(fdev);
return 0;
}
int bpi_flash_write(struct flash_device *fdev, size_t addr, size_t len, void *src)
{
char *s = src;
while (len > 0)
{
size_t seg = len;
// align to buffer size
if (seg > fdev->write_buffer_size - (addr & (fdev->write_buffer_size-1)))
{
seg = fdev->write_buffer_size - (addr & (fdev->write_buffer_size-1));
}
if (fdev->ops->buffered_program(fdev, addr >> 1, seg >> 1, s))
{
fprintf(stderr, "Buffered write failed\n");
bpi_flash_deselect(fdev);
return -1;
}
addr += seg;
len -= seg;
s += seg;
}
bpi_flash_deselect(fdev);
return 0;
}
int bpi_flash_erase(struct flash_device *fdev, size_t addr, size_t len)
{
size_t erase_block_size = fdev->erase_block_size;
while (len > 0)
{
// determine sector size
erase_block_size = 0;
for (int k = 0; k < fdev->erase_region_count; k++)
{
if (addr >= fdev->erase_region[k].region_start && addr < fdev->erase_region[k].region_end)
{
erase_block_size = fdev->erase_region[k].block_size;
break;
}
}
if (!erase_block_size)
{
fprintf(stderr, "Address does not match an erase region\n");
bpi_flash_deselect(fdev);
return -1;
}
// check size and alignment
if (addr & (erase_block_size-1) || len < erase_block_size)
{
fprintf(stderr, "Invalid erase request\n");
bpi_flash_deselect(fdev);
return -1;
}
// block erase
if (fdev->ops->sector_erase(fdev, addr >> 1))
{
fprintf(stderr, "Failed to erase sector\n");
bpi_flash_deselect(fdev);
return -1;
}
if (len <= erase_block_size)
break;
addr += erase_block_size;
len -= erase_block_size;
}
bpi_flash_deselect(fdev);
return 0;
}
const struct flash_driver bpi_flash_driver = {
.init = bpi_flash_init,
.release = bpi_flash_release,
.read = bpi_flash_read,
.write = bpi_flash_write,
.erase = bpi_flash_erase
};

584
utils/flash_spi.c Normal file
View File

@ -0,0 +1,584 @@
/*
Copyright 2020, The Regents of the University of California.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS
IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of The Regents of the University of California.
*/
#include "flash.h"
#include <stdio.h>
#include <stdlib.h>
#define reg_read32(reg) (*((volatile uint32_t *)(reg)))
#define reg_write32(reg, val) (*((volatile uint32_t *)(reg))) = (val)
#define SPI_CMD_RESET_ENABLE 0x66
#define SPI_CMD_RESET_MEMORY 0x99
#define SPI_CMD_READ_ID 0x9F
#define SPI_CMD_READ 0x03
#define SPI_CMD_FAST_READ 0x0B
#define SPI_CMD_FAST_READ_DUAL_OUT 0x3B
#define SPI_CMD_FAST_READ_DUAL_IO 0xBB
#define SPI_CMD_FAST_READ_QUAD_OUT 0x6B
#define SPI_CMD_FAST_READ_QUAD_IO 0xEB
#define SPI_CMD_DTR_FAST_READ 0x0D
#define SPI_CMD_DTR_FAST_READ_DUAL_OUT 0x3D
#define SPI_CMD_DTR_FAST_READ_DUAL_IO 0xBD
#define SPI_CMD_DTR_FAST_READ_QUAD_OUT 0x6D
#define SPI_CMD_DTR_FAST_READ_QUAD_IO 0xED
#define SPI_CMD_4B_READ 0x13
#define SPI_CMD_4B_FAST_READ 0x0C
#define SPI_CMD_4B_FAST_READ_DUAL_OUT 0x3C
#define SPI_CMD_4B_FAST_READ_DUAL_IO 0xBC
#define SPI_CMD_4B_FAST_READ_QUAD_OUT 0x6C
#define SPI_CMD_4B_FAST_READ_QUAD_IO 0xEC
#define SPI_CMD_4B_DTR_FAST_READ 0x0E
#define SPI_CMD_4B_DTR_FAST_READ_DUAL_IO 0xBE
#define SPI_CMD_4B_DTR_FAST_READ_QUAD_IO 0xEE
#define SPI_CMD_WRITE_ENABLE 0x06
#define SPI_CMD_WRITE_DISABLE 0x04
#define SPI_CMD_READ_STATUS_REG 0x05
#define SPI_CMD_READ_FLAG_STATUS_REG 0x70
#define SPI_CMD_READ_NV_CONFIG_REG 0xB5
#define SPI_CMD_READ_V_CONFIG_REG 0x85
#define SPI_CMD_READ_EV_CONFIG_REG 0x65
#define SPI_CMD_READ_EXT_ADDR_REG 0xC8
#define SPI_CMD_WRITE_STATUS_REG 0x01
#define SPI_CMD_WRITE_NV_CONFIG_REG 0xB1
#define SPI_CMD_WRITE_V_CONFIG_REG 0x81
#define SPI_CMD_WRITE_EV_CONFIG_REG 0x61
#define SPI_CMD_WRITE_EXT_ADDR_REG 0xC5
#define SPI_CMD_CLEAR_FLAG_STATUS_REG 0x50
#define SPI_CMD_PAGE_PROGRAM 0x02
#define SPI_CMD_PAGE_PROGRAM_DUAL_IN 0xA2
#define SPI_CMD_PAGE_PROGRAM_DUAL_IN_EXT 0xD2
#define SPI_CMD_PAGE_PROGRAM_QUAD_IN 0x32
#define SPI_CMD_PAGE_PROGRAM_QUAD_IN_EXT 0x38
#define SPI_CMD_4B_PAGE_PROGRAM 0x12
#define SPI_CMD_4B_PAGE_PROGRAM_QUAD_IN 0x34
#define SPI_CMD_4B_PAGE_PROGRAM_QUAD_IN_EXT 0x3E
#define SPI_CMD_32KB_SUBSECTOR_ERASE 0x52
#define SPI_CMD_4KB_SUBSECTOR_ERASE 0x20
#define SPI_CMD_SECTOR_ERASE 0xD8
#define SPI_CMD_BULK_ERASE 0xC7
#define SPI_CMD_4B_4KB_SUBSECTOR_ERASE 0x21
#define SPI_CMD_4B_SECTOR_ERASE 0xDC
#define SPI_CMD_PROGRAM_SUSPEND 0x75
#define SPI_CMD_PROGRAM_RESUME 0x7A
#define SPI_CMD_READ_OTP_ARRAY 0x4B
#define SPI_CMD_PROGRAM_OTP_ARRAY 0x42
#define SPI_CMD_ENTER_4B_ADDR_MODE 0xB7
#define SPI_CMD_EXIT_4B_ADDR_MODE 0xE9
#define SPI_CMD_ENTER_QUAD_IO_MODE 0x35
#define SPI_CMD_EXIT_QUAD_IO_MODE 0xF5
#define SPI_CMD_ENTER_DEEP_POWER_DOWN 0xB9
#define SPI_CMD_EXIT_DEEP_POWER_DOWN 0xAB
#define SPI_PROTO_STR 0
#define SPI_PROTO_DTR 1
#define SPI_PROTO_DUAL_STR 2
#define SPI_PROTO_DUAL_DTR 3
#define SPI_PROTO_QUAD_STR 4
#define SPI_PROTO_QUAD_DTR 5
#define SPI_PAGE_SIZE 0x100
#define SPI_SUBSECTOR_SIZE 0x1000
#define SPI_SECTOR_SIZE 0x10000
#define FLASH_D_0 (1 << 0)
#define FLASH_D_1 (1 << 1)
#define FLASH_D_2 (1 << 2)
#define FLASH_D_3 (1 << 3)
#define FLASH_D_01 (FLASH_D_0 | FLASH_D_1)
#define FLASH_D_0123 (FLASH_D_0 | FLASH_D_1 | FLASH_D_2 | FLASH_D_3)
#define FLASH_OE_0 (1 << 8)
#define FLASH_OE_1 (1 << 9)
#define FLASH_OE_2 (1 << 10)
#define FLASH_OE_3 (1 << 11)
#define FLASH_OE_01 (FLASH_OE_0 | FLASH_OE_1)
#define FLASH_OE_0123 (FLASH_OE_0 | FLASH_OE_1 | FLASH_OE_2 | FLASH_OE_3)
#define FLASH_CLK (1 << 16)
#define FLASH_CS_N (1 << 17)
void spi_flash_select(struct flash_device *fdev)
{
reg_write32(fdev->ctrl_reg, 0);
}
void spi_flash_deselect(struct flash_device *fdev)
{
reg_write32(fdev->ctrl_reg, FLASH_CS_N);
}
uint8_t spi_flash_read_byte(struct flash_device *fdev, int protocol)
{
uint8_t val = 0;
switch (protocol)
{
case SPI_PROTO_STR:
for (int i = 7; i >= 0; i--)
{
reg_write32(fdev->ctrl_reg, 0);
val |= ((reg_read32(fdev->ctrl_reg) & FLASH_D_1) != 0) << i;
reg_write32(fdev->ctrl_reg, FLASH_CLK);
}
break;
case SPI_PROTO_DTR:
break;
case SPI_PROTO_DUAL_STR:
for (int i = 6; i >= 0; i -= 2)
{
reg_write32(fdev->ctrl_reg, 0);
val |= (reg_read32(fdev->ctrl_reg) & FLASH_D_01) << i;
reg_write32(fdev->ctrl_reg, FLASH_CLK);
}
break;
case SPI_PROTO_DUAL_DTR:
break;
case SPI_PROTO_QUAD_STR:
for (int i = 4; i >= 0; i -= 4)
{
reg_write32(fdev->ctrl_reg, 0);
val |= (reg_read32(fdev->ctrl_reg) & FLASH_D_0123) << i;
reg_write32(fdev->ctrl_reg, FLASH_CLK);
}
break;
case SPI_PROTO_QUAD_DTR:
break;
}
reg_write32(fdev->ctrl_reg, 0);
return val;
}
void spi_flash_write_byte(struct flash_device *fdev, uint8_t val, int protocol)
{
uint8_t bit;
switch (protocol)
{
case SPI_PROTO_STR:
for (int i = 7; i >= 0; i--)
{
bit = (val >> i) & 0x1;
reg_write32(fdev->ctrl_reg, bit | FLASH_OE_0);
reg_write32(fdev->ctrl_reg, bit | FLASH_OE_0 | FLASH_CLK);
}
break;
case SPI_PROTO_DTR:
break;
case SPI_PROTO_DUAL_STR:
for (int i = 6; i >= 0; i -= 2)
{
bit = (val >> i) & 0x3;
reg_write32(fdev->ctrl_reg, bit | FLASH_OE_01);
reg_write32(fdev->ctrl_reg, bit | FLASH_OE_01 | FLASH_CLK);
}
break;
case SPI_PROTO_DUAL_DTR:
break;
case SPI_PROTO_QUAD_STR:
for (int i = 4; i >= 0; i -= 4)
{
bit = (val >> i) & 0xf;
reg_write32(fdev->ctrl_reg, bit | FLASH_OE_0123);
reg_write32(fdev->ctrl_reg, bit | FLASH_OE_0123 | FLASH_CLK);
}
break;
case SPI_PROTO_QUAD_DTR:
break;
}
reg_write32(fdev->ctrl_reg, 0);
}
void spi_flash_write_addr(struct flash_device *fdev, size_t addr, int protocol)
{
spi_flash_write_byte(fdev, (addr >> 16) & 0xff, protocol);
spi_flash_write_byte(fdev, (addr >> 8) & 0xff, protocol);
spi_flash_write_byte(fdev, (addr >> 0) & 0xff, protocol);
}
void spi_flash_write_addr_4b(struct flash_device *fdev, size_t addr, int protocol)
{
spi_flash_write_byte(fdev, (addr >> 24) & 0xff, protocol);
spi_flash_write_byte(fdev, (addr >> 16) & 0xff, protocol);
spi_flash_write_byte(fdev, (addr >> 8) & 0xff, protocol);
spi_flash_write_byte(fdev, (addr >> 0) & 0xff, protocol);
}
void spi_flash_write_enable(struct flash_device *fdev, int protocol)
{
spi_flash_write_byte(fdev, SPI_CMD_WRITE_ENABLE, protocol);
spi_flash_deselect(fdev);
}
void spi_flash_write_disable(struct flash_device *fdev, int protocol)
{
spi_flash_write_byte(fdev, SPI_CMD_WRITE_DISABLE, protocol);
spi_flash_deselect(fdev);
}
uint8_t spi_flash_read_status_register(struct flash_device *fdev, int protocol)
{
uint8_t val;
spi_flash_write_byte(fdev, SPI_CMD_READ_STATUS_REG, protocol);
val = spi_flash_read_byte(fdev, protocol);
spi_flash_deselect(fdev);
return val;
}
void spi_flash_write_status_register(struct flash_device *fdev, uint8_t val, int protocol)
{
spi_flash_write_byte(fdev, SPI_CMD_WRITE_STATUS_REG, protocol);
spi_flash_write_byte(fdev, val, protocol);
spi_flash_deselect(fdev);
}
uint8_t spi_flash_read_flag_status_register(struct flash_device *fdev, int protocol)
{
uint8_t val;
spi_flash_write_byte(fdev, SPI_CMD_READ_FLAG_STATUS_REG, protocol);
val = spi_flash_read_byte(fdev, protocol);
spi_flash_deselect(fdev);
return val;
}
void spi_flash_clear_flag_status_register(struct flash_device *fdev, int protocol)
{
spi_flash_write_byte(fdev, SPI_CMD_CLEAR_FLAG_STATUS_REG, protocol);
spi_flash_deselect(fdev);
}
uint8_t spi_flash_read_volatile_config_register(struct flash_device *fdev, int protocol)
{
uint8_t val;
spi_flash_write_byte(fdev, SPI_CMD_READ_V_CONFIG_REG, protocol);
val = spi_flash_read_byte(fdev, protocol);
spi_flash_deselect(fdev);
return val;
}
void spi_flash_write_volatile_config_register(struct flash_device *fdev, uint8_t val, int protocol)
{
spi_flash_write_byte(fdev, SPI_CMD_WRITE_V_CONFIG_REG, protocol);
spi_flash_write_byte(fdev, val, protocol);
spi_flash_deselect(fdev);
}
void spi_flash_reset(struct flash_device *fdev, int protocol)
{
spi_flash_deselect(fdev);
spi_flash_write_byte(fdev, SPI_CMD_RESET_ENABLE, protocol);
spi_flash_deselect(fdev);
reg_read32(fdev->ctrl_reg); // dummy read
reg_read32(fdev->ctrl_reg); // dummy read
spi_flash_write_byte(fdev, SPI_CMD_RESET_MEMORY, protocol);
spi_flash_deselect(fdev);
reg_read32(fdev->ctrl_reg); // dummy read
reg_read32(fdev->ctrl_reg); // dummy read
}
void spi_flash_release(struct flash_device *fdev)
{
spi_flash_deselect(fdev);
}
int spi_flash_init(struct flash_device *fdev)
{
int ret = 0;
if (!fdev)
return -1;
spi_flash_reset(fdev, SPI_PROTO_STR);
spi_flash_write_byte(fdev, SPI_CMD_READ_ID, SPI_PROTO_STR);
int mfr_id = spi_flash_read_byte(fdev, SPI_PROTO_STR);
int mem_type = spi_flash_read_byte(fdev, SPI_PROTO_STR);
int mem_capacity = spi_flash_read_byte(fdev, SPI_PROTO_STR);
spi_flash_deselect(fdev);
printf("Manufacturer ID: 0x%02x\n", mfr_id);
printf("Memory type: 0x%02x\n", mem_type);
printf("Memory capacity: 0x%02x\n", mem_capacity);
// convert from BCD
mem_capacity = (mem_capacity & 0xf) + (((mem_capacity >> 4) & 0xf) * 10);
fdev->size = ((size_t)1) << (mem_capacity+6);
printf("Flash size: %ld MB\n", fdev->size / (1 << 20));
fdev->protocol = SPI_PROTO_STR;
fdev->bulk_protocol = SPI_PROTO_STR;
fdev->read_dummy_cycles = 0;
fdev->write_buffer_size = SPI_PAGE_SIZE;
fdev->erase_block_size = SPI_SUBSECTOR_SIZE;
printf("Write buffer size: %ld B\n", fdev->write_buffer_size);
printf("Erase block size: %ld B\n", fdev->erase_block_size);
if (fdev->data_width == 4)
{
spi_flash_write_volatile_config_register(fdev, 0xFB, SPI_PROTO_STR);
fdev->bulk_protocol = SPI_PROTO_QUAD_STR;
fdev->read_dummy_cycles = 10;
}
spi_flash_release(fdev);
return ret;
}
int spi_flash_read(struct flash_device *fdev, size_t addr, size_t len, void *dest)
{
char *d = dest;
int protocol = SPI_PROTO_STR;
if (fdev->data_width == 4)
{
protocol = SPI_PROTO_QUAD_STR;
}
if (addr > 0xffffff)
{
// four byte address read
if (protocol == SPI_PROTO_QUAD_STR)
{
spi_flash_write_byte(fdev, SPI_CMD_4B_FAST_READ_QUAD_IO, SPI_PROTO_STR);
}
else
{
spi_flash_write_byte(fdev, SPI_CMD_4B_READ, SPI_PROTO_STR);
}
spi_flash_write_addr_4b(fdev, addr, protocol);
}
else
{
// normal read
if (protocol == SPI_PROTO_QUAD_STR)
{
spi_flash_write_byte(fdev, SPI_CMD_FAST_READ_QUAD_IO, SPI_PROTO_STR);
}
else
{
spi_flash_write_byte(fdev, SPI_CMD_READ, SPI_PROTO_STR);
}
spi_flash_write_addr(fdev, addr, protocol);
}
if (protocol != SPI_PROTO_STR)
{
// dummy cycles
for (int i = 0; i < fdev->read_dummy_cycles; i++)
{
reg_write32(fdev->ctrl_reg, FLASH_CLK);
reg_write32(fdev->ctrl_reg, 0);
}
}
while (len > 0)
{
*d = spi_flash_read_byte(fdev, protocol);
len--;
d++;
}
spi_flash_deselect(fdev);
return 0;
}
int spi_flash_write(struct flash_device *fdev, size_t addr, size_t len, void *src)
{
char *s = src;
int protocol = SPI_PROTO_STR;
if (fdev->data_width == 4)
{
protocol = SPI_PROTO_QUAD_STR;
}
while (len > 0)
{
spi_flash_write_enable(fdev, SPI_PROTO_STR);
if (!(spi_flash_read_status_register(fdev, SPI_PROTO_STR) & 0x02))
{
fprintf(stderr, "Failed to enable writing\n");
spi_flash_deselect(fdev);
return -1;
}
if (addr > 0xffffff)
{
// four byte address page program
if (protocol == SPI_PROTO_QUAD_STR)
{
spi_flash_write_byte(fdev, SPI_CMD_4B_PAGE_PROGRAM_QUAD_IN_EXT, SPI_PROTO_STR);
}
else
{
spi_flash_write_byte(fdev, SPI_CMD_4B_PAGE_PROGRAM, SPI_PROTO_STR);
}
spi_flash_write_addr_4b(fdev, addr, protocol);
}
else
{
// normal page program
if (protocol == SPI_PROTO_QUAD_STR)
{
spi_flash_write_byte(fdev, SPI_CMD_PAGE_PROGRAM_QUAD_IN_EXT, SPI_PROTO_STR);
}
else
{
spi_flash_write_byte(fdev, SPI_CMD_PAGE_PROGRAM, SPI_PROTO_STR);
}
spi_flash_write_addr(fdev, addr, protocol);
}
while (len > 0)
{
spi_flash_write_byte(fdev, *s, protocol);
addr++;
s++;
len--;
if ((addr & 0xff) == 0)
break;
}
spi_flash_deselect(fdev);
// wait for operation to complete
while (spi_flash_read_status_register(fdev, SPI_PROTO_STR) & 0x01) {};
}
spi_flash_deselect(fdev);
return 0;
}
int spi_flash_erase(struct flash_device *fdev, size_t addr, size_t len)
{
size_t erase_block_size = fdev->erase_block_size;
while (len > 0)
{
// determine sector size
erase_block_size = 0;
if ((addr & (SPI_SECTOR_SIZE-1)) == 0 && len >= SPI_SECTOR_SIZE)
{
erase_block_size = SPI_SECTOR_SIZE;
}
else if ((addr & (SPI_SUBSECTOR_SIZE-1)) == 0 && len >= SPI_SUBSECTOR_SIZE)
{
erase_block_size = SPI_SUBSECTOR_SIZE;
}
// check size and alignment
if (!erase_block_size)
{
fprintf(stderr, "Invalid erase request\n");
spi_flash_deselect(fdev);
return -1;
}
// enable writing
spi_flash_write_enable(fdev, SPI_PROTO_STR);
if (!(spi_flash_read_status_register(fdev, SPI_PROTO_STR) & 0x02))
{
fprintf(stderr, "Failed to enable writing\n");
spi_flash_deselect(fdev);
return -1;
}
// block erase
if (addr > 0xffffff)
{
if (erase_block_size == SPI_SECTOR_SIZE)
{
// four byte address sector erase
spi_flash_write_byte(fdev, SPI_CMD_4B_SECTOR_ERASE, SPI_PROTO_STR);
spi_flash_write_addr_4b(fdev, addr, SPI_PROTO_STR);
}
else if (erase_block_size == SPI_SUBSECTOR_SIZE)
{
// normal 4KB subsector erase
spi_flash_write_byte(fdev, SPI_CMD_4B_4KB_SUBSECTOR_ERASE, SPI_PROTO_STR);
spi_flash_write_addr_4b(fdev, addr, SPI_PROTO_STR);
}
}
else
{
if (erase_block_size == SPI_SECTOR_SIZE)
{
// normal sector erase
spi_flash_write_byte(fdev, SPI_CMD_SECTOR_ERASE, SPI_PROTO_STR);
spi_flash_write_addr(fdev, addr, SPI_PROTO_STR);
}
else if (erase_block_size == SPI_SUBSECTOR_SIZE)
{
// normal 4KB subsector erase
spi_flash_write_byte(fdev, SPI_CMD_4KB_SUBSECTOR_ERASE, SPI_PROTO_STR);
spi_flash_write_addr(fdev, addr, SPI_PROTO_STR);
}
}
spi_flash_deselect(fdev);
// wait for operation to complete
while (spi_flash_read_status_register(fdev, SPI_PROTO_STR) & 0x01) {};
if (len <= erase_block_size)
break;
addr += erase_block_size;
len -= erase_block_size;
}
spi_flash_deselect(fdev);
return 0;
}
const struct flash_driver spi_flash_driver = {
.init = spi_flash_init,
.release = spi_flash_release,
.read = spi_flash_read,
.write = spi_flash_write,
.erase = spi_flash_erase
};

File diff suppressed because it is too large Load Diff