From 9ec0b71a1aefb46b152ff0f4f0493137aa5fc4a9 Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Sun, 4 Oct 2020 00:56:06 -0700 Subject: [PATCH] Add firmware update utility --- utils/Makefile | 2 +- utils/bitfile.c | 183 +++++++ utils/bitfile.h | 56 +++ utils/flash.c | 136 +++++ utils/flash.h | 96 ++++ utils/flash_bpi.c | 671 +++++++++++++++++++++++++ utils/flash_spi.c | 584 ++++++++++++++++++++++ utils/mqnic-fw.c | 1212 ++++++++++++++++++++++++++++++++++++++++++--- 8 files changed, 2875 insertions(+), 65 deletions(-) create mode 100644 utils/bitfile.c create mode 100644 utils/bitfile.h create mode 100644 utils/flash.c create mode 100644 utils/flash.h create mode 100644 utils/flash_bpi.c create mode 100644 utils/flash_spi.c diff --git a/utils/Makefile b/utils/Makefile index db005d0b8..9d294fcdf 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -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 diff --git a/utils/bitfile.c b/utils/bitfile.c new file mode 100644 index 000000000..e74eee5ae --- /dev/null +++ b/utils/bitfile.c @@ -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 +#include +#include + +#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); + } +} + diff --git a/utils/bitfile.h b/utils/bitfile.h new file mode 100644 index 000000000..43298d12f --- /dev/null +++ b/utils/bitfile.h @@ -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 diff --git a/utils/flash.c b/utils/flash.c new file mode 100644 index 000000000..b6a651508 --- /dev/null +++ b/utils/flash.c @@ -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 + +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); +} + diff --git a/utils/flash.h b/utils/flash.h new file mode 100644 index 000000000..74647b839 --- /dev/null +++ b/utils/flash.h @@ -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 +#include + +#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 */ diff --git a/utils/flash_bpi.c b/utils/flash_bpi.c new file mode 100644 index 000000000..dadc139d7 --- /dev/null +++ b/utils/flash_bpi.c @@ -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 +#include + +#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 +}; + diff --git a/utils/flash_spi.c b/utils/flash_spi.c new file mode 100644 index 000000000..773cd1de8 --- /dev/null +++ b/utils/flash_spi.c @@ -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 +#include + +#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 +}; + diff --git a/utils/mqnic-fw.c b/utils/mqnic-fw.c index 7dd1f0884..0990e4365 100644 --- a/utils/mqnic-fw.c +++ b/utils/mqnic-fw.c @@ -31,47 +31,473 @@ either expressed or implied, of The Regents of the University of California. */ -#include +#include #include -#include +#include #include #include #include -#include #include #include -#include -#include +#include #include "mqnic.h" +#include "bitfile.h" +#include "flash.h" +#include "fpga_id.h" + +#define MAX_SEGMENTS 8 + +uint32_t reverse_bits_32(uint32_t x) +{ + x = ((x & 0x55555555) << 1) | ((x & 0xAAAAAAAA) >> 1); + x = ((x & 0x33333333) << 2) | ((x & 0xCCCCCCCC) >> 2); + x = ((x & 0x0F0F0F0F) << 4) | ((x & 0xF0F0F0F0) >> 4); + x = ((x & 0x00FF00FF) << 8) | ((x & 0xFF00FF00) >> 8); + x = ((x & 0x0000FFFF) << 16) | ((x & 0xFFFF0000) >> 16); + return x; +} + +uint16_t reverse_bits_16(uint16_t x) +{ + x = ((x & 0x5555) << 1) | ((x & 0xAAAA) >> 1); + x = ((x & 0x3333) << 2) | ((x & 0xCCCC) >> 2); + x = ((x & 0x0F0F) << 4) | ((x & 0xF0F0) >> 4); + x = ((x & 0x00FF) << 8) | ((x & 0xFF00) >> 8); + return x; +} + +uint8_t reverse_bits_8(uint8_t x) +{ + x = ((x & 0x55) << 1) | ((x & 0xAA) >> 1); + x = ((x & 0x33) << 2) | ((x & 0xCC) >> 2); + x = ((x & 0x0F) << 4) | ((x & 0xF0) >> 4); + return x; +} + +char* stristr(const char *str1, const char *str2) +{ + const char* p1 = str1; + const char* p2 = str2; + const char* r = *p2 == 0 ? str1 : 0; + + while (*p1 != 0 && *p2 != 0) + { + if (tolower(*p1) == tolower(*p2)) + { + if (r == 0) + { + r = p1; + } + + p2++; + } + else + { + p2 = str2; + if (r != 0) + { + p1 = r + 1; + } + + if (tolower(*p1) == tolower(*p2)) + { + r = p1; + p2++; + } + else + { + r = 0; + } + } + + p1++; + } + + return *p2 == 0 ? (char *)r : 0; +} static void usage(char *name) { fprintf(stderr, "usage: %s [options]\n" - " -d name device to open (/dev/mqnic0)\n", + " -d name device to open (/dev/mqnic0)\n" + " -s slot slot to program (default 1)\n" + " -r file read flash to file\n" + " -w file write and verify flash from file\n" + " -b boot FPGA from flash\n" + " -t hot reset FPGA\n", name); } +int flash_read_progress(struct flash_device *fdev, size_t addr, size_t len, void* dest) +{ + int ret; + size_t remain = len; + size_t seg; + int step = 0x10000; + + printf("Start address: 0x%08lx\n", addr); + printf("Length: 0x%08lx\n", len); + + while (remain > 0) + { + if (remain > step) + { + // longer than step, trim + if ((addr + step) & (step-1)) + { + // align to step size + seg = step - ((addr + step) & (step-1)); + } + else + { + // already aligned + seg = step; + } + } + else + { + // shorter than step + seg = remain; + } + + printf("Read address 0x%08lx, length 0x%08lx (%ld%%)\r", addr, seg, ((100*(len-remain))/len)); + fflush(stdout); + + ret = flash_read(fdev, addr, seg, dest); + + if (ret) + return ret; + + addr += seg; + remain -= seg; + dest += seg; + } + + printf("\n"); + + return 0; +} + +int flash_write_progress(struct flash_device *fdev, size_t addr, size_t len, void* src) +{ + int ret; + size_t remain = len; + size_t seg; + int step = 0x10000; + + printf("Start address: 0x%08lx\n", addr); + printf("Length: 0x%08lx\n", len); + + step = fdev->write_buffer_size > step ? fdev->write_buffer_size : step; + + while (remain > 0) + { + if (remain > step) + { + // longer than step, trim + if ((addr + step) & (step-1)) + { + // align to step size + seg = step - ((addr + step) & (step-1)); + } + else + { + // already aligned + seg = step; + } + } + else + { + // shorter than step + seg = remain; + } + + printf("Write address 0x%08lx, length 0x%08lx (%ld%%)\r", addr, seg, ((100*(len-remain))/len)); + fflush(stdout); + + ret = flash_write(fdev, addr, seg, src); + + if (ret) + return ret; + + addr += seg; + remain -= seg; + src += seg; + } + + printf("\n"); + + return 0; +} + +int flash_erase_progress(struct flash_device *fdev, size_t addr, size_t len) +{ + int ret; + size_t remain = len; + size_t seg; + int step = 0x10000; + + printf("Start address: 0x%08lx\n", addr); + printf("Length: 0x%08lx\n", len); + + step = fdev->erase_block_size > step ? fdev->erase_block_size : step; + + while (remain > 0) + { + if (remain > step) + { + // longer than step, trim + if ((addr + step) & (step-1)) + { + // align to step size + seg = step - ((addr + step) & (step-1)); + } + else + { + // already aligned + seg = step; + } + } + else + { + // shorter than step + seg = remain; + } + + printf("Erase address 0x%08lx, length 0x%08lx (%ld%%)\r", addr, seg, ((100*(len-remain))/len)); + fflush(stdout); + + ret = flash_erase(fdev, addr, seg); + + if (ret) + return ret; + + addr += seg; + remain -= seg; + } + + printf("\n"); + + return 0; +} + +int write_str_to_file(const char *file_name, const char *str) +{ + int ret = 0; + FILE *fp = fopen(file_name, "w"); + + if (!fp) + { + perror("failed to open file"); + return -1; + } + + if (fputs(str, fp) == EOF) + { + perror("failed to write to file"); + ret = -1; + } + + fclose(fp); + return ret; +} + +int write_1_to_file(const char *file_name) +{ + return write_str_to_file(file_name, "1"); +} + +#define FILE_TYPE_BIN 0 +#define FILE_TYPE_HEX 1 +#define FILE_TYPE_BIT 2 + +int file_type_from_ext(const char *file_name) +{ + char *ptr; + char buffer[32]; + + ptr = strrchr(file_name, '.'); + + if (!ptr) + { + return FILE_TYPE_BIN; + } + + ptr++; + + for (int i = 0; i < sizeof(buffer)-1 && *ptr; i++) + { + buffer[i] = tolower(*ptr++); + buffer[i+1] = 0; + } + + if (strcmp(buffer, "hex") == 0 || strcmp(buffer, "mcs") == 0) + { + return FILE_TYPE_HEX; + } + + if (strcmp(buffer, "bit") == 0) + { + return FILE_TYPE_BIT; + } + + return FILE_TYPE_BIN; +} + +int pcie_hot_reset(const char *pcie_port_path) +{ + int fd; + char path[PATH_MAX+32]; + char buf[32]; + + snprintf(path, sizeof(path), "%s/config", pcie_port_path); + + fd = open(path, O_RDWR); + + if (!fd) + { + perror("Failed to open config region of port"); + return -1; + } + + // set and then clear secondary bus reset bit (mask 0x0040) + // in the bridge control register (offset 0x3e) + pread(fd, buf, 2, 0x3e); + + buf[2] = buf[0] | 0x40; + buf[3] = buf[1]; + + pwrite(fd, buf+2, 2, 0x3e); + + usleep(10000); + + pwrite(fd, buf, 2, 0x3e); + + close(fd); + + return 0; +} + +int pcie_disable_fatal_err(const char *pcie_port_path) +{ + int fd; + char path[PATH_MAX+32]; + char buf[32]; + int offset; + + snprintf(path, sizeof(path), "%s/config", pcie_port_path); + + fd = open(path, O_RDWR); + + if (!fd) + { + perror("Failed to open config region of port"); + return -1; + } + + // clear SERR bit (mask 0x0100) in command register (offset 0x04) + pread(fd, buf, 2, 0x04); + + buf[1] &= ~0x01; + + pwrite(fd, buf, 2, 0x04); + + // clear fatal error reporting bit (mask 0x0004) in + // PCIe capability device control register (offset 0x08) + + // find PCIe capability (ID 0x10) + pread(fd, buf, 1, 0x34); + + offset = buf[0] & 0xfc; + + while (offset > 0) + { + pread(fd, buf, 2, offset); + + if (buf[0] == 0x10) + break; + + offset = buf[1] & 0xfc; + } + + // clear bit + if (offset) + { + pread(fd, buf, 2, offset+0x08); + + buf[0] &= ~0x04; + + pwrite(fd, buf, 2, offset+0x08); + } + + close(fd); + + return 0; +} + int main(int argc, char *argv[]) { char *name; int opt; + int ret = 0; char *device = NULL; + char *read_file_name = NULL; + FILE *read_file = NULL; + char *write_file_name = NULL; + FILE *write_file = NULL; - struct mqnic *dev; + char path[PATH_MAX+32]; + char device_path[PATH_MAX]; + char port_path[PATH_MAX]; + char *ptr; + + int slot = -1; + + char action_read = 0; + char action_write = 0; + char action_boot = 0; + char action_reset = 0; + + struct mqnic *dev = NULL; + + struct flash_device *pri_flash = NULL; + struct flash_device *sec_flash = NULL; + + int flash_segment_count = 0; + size_t flash_segment_start[MAX_SEGMENTS]; + size_t flash_segment_length[MAX_SEGMENTS]; name = strrchr(argv[0], '/'); name = name ? 1+name : argv[0]; - while ((opt = getopt(argc, argv, "d:h?")) != EOF) + while ((opt = getopt(argc, argv, "d:s:r:w:bth?")) != EOF) { switch (opt) { case 'd': device = optarg; break; + case 's': + slot = atoi(optarg); + break; + case 'r': + action_read = 1; + read_file_name = optarg; + break; + case 'w': + action_write = 1; + write_file_name = optarg; + break; + case 'b': + action_boot = 1; + action_reset = 1; + break; + case 't': + action_reset = 1; + break; case 'h': case '?': usage(name); @@ -97,81 +523,739 @@ int main(int argc, char *argv[]) return -1; } + // determine sysfs path of PCIe device + // first, try to find via miscdevice + ptr = strrchr(device, '/'); + ptr = ptr ? ptr+1 : device; + + snprintf(path, sizeof(path), "/sys/class/misc/%s/device", ptr); + + if (!realpath(path, device_path)) + { + // that failed, perhaps it was a PCIe resource + strcpy(path, device); + ptr = strrchr(path, '/'); + if (ptr) + *ptr = 0; + + if (!realpath(path, device_path)) + { + perror("failed to determine device path"); + ret = -1; + goto err; + } + } + + // PCIe device will have a config space, so check for that + snprintf(path, sizeof(path), "%s/config", device_path); + + if (access(path, F_OK) == -1) + { + perror("failed to determine device path"); + ret = -1; + goto err; + } + + // determine sysfs path of upstream port + strcpy(port_path, device_path); + ptr = strrchr(port_path, '/'); + if (ptr) + *ptr = 0; + printf("FW ID: 0x%08x\n", dev->fw_id); printf("FW version: %d.%d\n", dev->fw_ver >> 16, dev->fw_ver & 0xffff); printf("Board ID: 0x%08x\n", dev->board_id); printf("Board version: %d.%d\n", dev->board_ver >> 16, dev->board_ver & 0xffff); - printf("PHC count: %d\n", dev->phc_count); - printf("PHC offset: 0x%08x\n", dev->phc_offset); - printf("IF count: %d\n", dev->if_count); - printf("IF stride: 0x%08x\n", dev->if_stride); - printf("IF CSR offset: 0x%08x\n", dev->if_csr_offset); - // dump regs - printf("Flash ID: %08x\n", mqnic_reg_read32(dev->regs, 0x140)); - printf("Flash Addr: %08x\n", mqnic_reg_read32(dev->regs, 0x144)); - printf("Flash Data: %08x\n", mqnic_reg_read32(dev->regs, 0x148)); - printf("Flash Ctrl: %08x\n", mqnic_reg_read32(dev->regs, 0x14c)); + uint32_t flash_id = mqnic_reg_read32(dev->regs, MQNIC_REG_FLASH_ID); + uint32_t fpga_id = mqnic_reg_read32(dev->regs, MQNIC_REG_FPGA_ID); + const char *fpga_part = get_fpga_part(fpga_id); - // release control lines - mqnic_reg_write32(dev->regs, 0x14c, 0x0000000f); + uint8_t flash_type = flash_id >> 0; + uint8_t flash_configuration = flash_id >> 8; + uint8_t flash_data_width = flash_id >> 16; + uint8_t flash_addr_width = flash_id >> 24; - // write RCR to put flash in async mode - mqnic_reg_write32(dev->regs, 0x144, 0x0000f94f); - mqnic_reg_write32(dev->regs, 0x148, 0x0060); - mqnic_reg_write32(dev->regs, 0x14c, 0x00000102); - mqnic_reg_write32(dev->regs, 0x14c, 0x0000000f); - mqnic_reg_write32(dev->regs, 0x144, 0x0000f94f); - mqnic_reg_write32(dev->regs, 0x148, 0x0003); - mqnic_reg_write32(dev->regs, 0x14c, 0x00000102); - mqnic_reg_write32(dev->regs, 0x14c, 0x0000000f); + printf("Flash ID: 0x%08x\n", flash_id); + printf("FPGA ID: 0x%08x\n", fpga_id); + printf("FPGA part: %s\n", fpga_part); - // read flash ID - mqnic_reg_write32(dev->regs, 0x144, 0x00000000); - mqnic_reg_write32(dev->regs, 0x148, 0x0090); - mqnic_reg_write32(dev->regs, 0x14c, 0x00000102); - mqnic_reg_write32(dev->regs, 0x14c, 0x00000004); + if (flash_id == 0 || flash_id == 0xffffffff) + { + fprintf(stderr, "Invalid flash ID\n"); + ret = -1; + goto err; + } - // dump regs - printf("Flash Addr: %08x\n", mqnic_reg_read32(dev->regs, 0x144)); - printf("Flash Data: %08x\n", mqnic_reg_read32(dev->regs, 0x148)); - printf("Flash Ctrl: %08x\n", mqnic_reg_read32(dev->regs, 0x14c)); + if (fpga_id == 0 || fpga_id == 0xffffffff) + { + fprintf(stderr, "Invalid FPGA ID\n"); + ret = -1; + goto err; + } - // read rest of flash ID - mqnic_reg_write32(dev->regs, 0x144, 0x00000001); - mqnic_reg_write32(dev->regs, 0x14c, 0x00000004); + int bitswap = 0; + int word_size = 8; + int dual_qspi = 0; - // dump regs - printf("Flash Addr: %08x\n", mqnic_reg_read32(dev->regs, 0x144)); - printf("Flash Data: %08x\n", mqnic_reg_read32(dev->regs, 0x148)); - printf("Flash Ctrl: %08x\n", mqnic_reg_read32(dev->regs, 0x14c)); + size_t flash_size; + size_t segment_size; + size_t segment_offset; - // release control lines - mqnic_reg_write32(dev->regs, 0x14c, 0x0000000f); + if (flash_type == 0 || flash_type == 2) + { + printf("SPI flash\n"); + printf("Data width: %d\n", flash_data_width); - // try reading a word from flash - // read array - mqnic_reg_write32(dev->regs, 0x144, 0x00000000 >> 1); - mqnic_reg_write32(dev->regs, 0x148, 0x00ff); - mqnic_reg_write32(dev->regs, 0x14c, 0x00000102); - mqnic_reg_write32(dev->regs, 0x14c, 0x0000000f); + if (flash_data_width > 4) + { + dual_qspi = 1; + pri_flash = flash_open_spi(4, dev->regs+MQNIC_REG_FLASH_SPI_0_CTRL); + sec_flash = flash_open_spi(4, dev->regs+MQNIC_REG_FLASH_SPI_1_CTRL); - // read word - mqnic_reg_write32(dev->regs, 0x144, 0x00000050 >> 1); - mqnic_reg_write32(dev->regs, 0x14c, 0x00000004); + if (!pri_flash || !sec_flash) + { + fprintf(stderr, "Failed to connect to flash device\n"); + ret = -1; + goto err; + } - // dump regs - printf("Flash Addr: %08x\n", mqnic_reg_read32(dev->regs, 0x144)); - printf("Flash Data: %08x\n", mqnic_reg_read32(dev->regs, 0x148)); - printf("Flash Ctrl: %08x\n", mqnic_reg_read32(dev->regs, 0x14c)); + flash_size = pri_flash->size+sec_flash->size; + } + else + { + pri_flash = flash_open_spi(flash_data_width, + dev->regs+MQNIC_REG_FLASH_SPI_0_CTRL); - // release address and control lines - mqnic_reg_write32(dev->regs, 0x144, 0x00000000); - mqnic_reg_write32(dev->regs, 0x14c, 0x0000000f); + if (!pri_flash) + { + fprintf(stderr, "Failed to connect to flash device\n"); + ret = -1; + goto err; + } + + flash_size = pri_flash->size; + } + } + else if (flash_type == 1) + { + printf("BPI flash\n"); + printf("Data width: %d\n", flash_data_width); + printf("Address width: %d\n", flash_addr_width); + + bitswap = 1; + + if (flash_data_width == 16) + { + word_size = 16; + } + + pri_flash = flash_open_bpi(flash_data_width, + dev->regs+MQNIC_REG_FLASH_BPI_CTRL, + dev->regs+MQNIC_REG_FLASH_BPI_ADDR, + dev->regs+MQNIC_REG_FLASH_BPI_DATA); + + if (!pri_flash) + { + fprintf(stderr, "Failed to connect to flash device\n"); + ret = -1; + goto err; + } + + flash_size = pri_flash->size; + } + else + { + fprintf(stderr, "Unknown flash type: %d\n", flash_type); + ret = -1; + goto err; + } + + switch (flash_configuration) + { + case 0: + case 1: + flash_segment_count = 1; + flash_segment_start[0] = 0; + flash_segment_length[0] = flash_size; + break; + case 2: + flash_segment_count = 2; + flash_segment_start[0] = 0; + flash_segment_length[0] = flash_size >> 1; + flash_segment_start[1] = flash_segment_start[0]+flash_segment_length[0]; + flash_segment_length[1] = flash_size >> 1; + break; + case 4: + flash_segment_count = 4; + flash_segment_start[0] = 0; + flash_segment_length[0] = flash_size >> 2; + for (int k = 1; k < 4; k++) + { + flash_segment_start[k] = flash_segment_start[k-1]+flash_segment_length[k-1]; + flash_segment_length[k] = flash_size >> 2; + } + break; + case 8: + flash_segment_count = 8; + flash_segment_start[0] = 0; + flash_segment_length[0] = flash_size >> 3; + for (int k = 1; k < 8; k++) + { + flash_segment_start[k] = flash_segment_start[k-1]+flash_segment_length[k-1]; + flash_segment_length[k] = flash_size >> 3; + } + break; + case 0x81: + // Alveo boards + flash_segment_count = 2; + flash_segment_start[0] = 0; + flash_segment_length[0] = 0x01002000; + flash_segment_start[1] = flash_segment_start[0]+flash_segment_length[0]; + flash_segment_length[1] = flash_size - flash_segment_start[1]; + break; + default: + fprintf(stderr, "Unknown flash configuration (0x%02x)\n", flash_configuration); + ret = -1; + goto err; + } + + for (int k = 0; k < flash_segment_count; k++) + { + printf("Flash segment %d start 0x%08lx length 0x%08lx\n", k, flash_segment_start[k], flash_segment_length[k]); + } + + if (slot < 0) + { + if (flash_segment_count > 1) + { + slot = 1; + } + else + { + slot = 0; + } + } + + if (slot < 0 || slot >= flash_segment_count) + { + fprintf(stderr, "Requested slot is not valid (%d)\n", slot); + ret = -1; + goto err; + } + + segment_offset = flash_segment_start[slot]; + segment_size = flash_segment_length[slot]; + + printf("Selected segment %d start 0x%08lx length 0x%08lx\n", slot, segment_offset, segment_size); + + if (action_write) + { + char *segment = calloc(segment_size, 1); + memset(segment, 0xff, segment_size); + size_t len; + + int file_type = file_type_from_ext(write_file_name); + + if (file_type == FILE_TYPE_BIN) + { + // read binary file + printf("Reading binary file \"%s\"...\n", write_file_name); + write_file = fopen(write_file_name, "rb"); + + if (!write_file) + { + fprintf(stderr, "Failed to open file\n"); + free(segment); + ret = -1; + goto err; + } + + fseek(write_file, 0, SEEK_END); + len = ftell(write_file); + rewind(write_file); + + if (len > segment_size) + { + fprintf(stderr, "File larger than segment (%ld > %ld)\n", len, segment_size); + fclose(write_file); + free(segment); + ret = -1; + goto err; + } + + if (fread(segment, 1, len, write_file) < len) + { + fprintf(stderr, "Error reading file\n"); + fclose(write_file); + free(segment); + ret = -1; + goto err; + } + + fclose(write_file); + } + else if (file_type == FILE_TYPE_BIT) + { + // read bit file + struct bitfile *bf; + + bf = bitfile_create_from_file(write_file_name); + + if (!bf) + { + fprintf(stderr, "Error reading bit file\n"); + free(segment); + ret = -1; + goto err; + } + + if (stristr(bf->part, fpga_part) != bf->part) + { + fprintf(stderr, "Device mismatch (target is %s, file is %s)\n", fpga_part, bf->part); + bitfile_close(bf); + free(segment); + ret = -1; + goto err; + } + + if (bf->data_len > segment_size) + { + fprintf(stderr, "File larger than segment (%ld > %ld)\n", bf->data_len, segment_size); + bitfile_close(bf); + free(segment); + ret = -1; + goto err; + } + + len = bf->data_len; + memcpy(segment, bf->data, bf->data_len); + + bitfile_close(bf); + } + else if (file_type == FILE_TYPE_HEX) + { + fprintf(stderr, "Hex files are not currently supported\n"); + free(segment); + ret = -1; + goto err; + } + else + { + fprintf(stderr, "Unsupported file type\n"); + free(segment); + ret = -1; + goto err; + } + + // check sync word + if (memcmp(segment+0x50, "\xAA\x99\x55\x66", 4)) + { + fprintf(stderr, "Bitstream sync word not found\n"); + free(segment); + ret = -1; + goto err; + } + + // TODO check for and confirm FPGA ID + + if (bitswap) + { + if (word_size == 16) + { + uint16_t *p = (uint16_t *)segment; + + for (size_t k = 0; k < segment_size; k += 2) + { + *p = reverse_bits_16(*p); + p++; + } + } + else + { + uint8_t *p = (uint8_t *)segment; + + for (size_t k = 0; k < segment_size; k++) + { + *p = reverse_bits_8(*p); + p++; + } + } + } + + if (dual_qspi) + { + // Dual QSPI flash + + // check sync word for dual QSPI re-sync + if (memcmp(segment+0x70, "\xAA\x99\x55\x66", 4)) + { + fprintf(stderr, "Bitstream sync word not found for dual QSPI re-sync\n"); + free(segment); + ret = -1; + goto err; + } + + char *pri_buf = calloc(segment_size/2, 1); + char *sec_buf = calloc(segment_size/2, 1); + memset(pri_buf, 0xff, segment_size/2); + memset(sec_buf, 0xff, segment_size/2); + + int offset = 0x68; + + size_t len_int = (len - offset) / 2 + offset; + + if (len_int > segment_size/2) + len_int = segment_size/2; + + memcpy(pri_buf, segment, offset); + + char *c1 = pri_buf+offset; + char *c2 = sec_buf+offset; + + for (size_t k = offset; k < segment_size-offset; k += 2) + { + *c1 = (segment[k+1] & 0x0f) | ((segment[k] << 4) & 0xf0); + *c2 = ((segment[k+1] >> 4) & 0x0f) | (segment[k] & 0xf0); + c1++; + c2++; + } + + // round up length to block size + if ((segment_offset/2 + len_int) & (pri_flash->erase_block_size-1)) + { + len_int += pri_flash->erase_block_size - ((segment_offset/2 + len_int) & (pri_flash->erase_block_size-1)); + } + + printf("Erasing primary flash...\n"); + if (flash_erase_progress(pri_flash, segment_offset/2, len_int)) + { + fprintf(stderr, "Erase failed!\n"); + ret = -1; + free(segment); + free(pri_buf); + free(sec_buf); + goto err; + } + + printf("Erasing secondary flash...\n"); + if (flash_erase_progress(sec_flash, segment_offset/2, len_int)) + { + fprintf(stderr, "Erase failed!\n"); + ret = -1; + free(segment); + free(pri_buf); + free(sec_buf); + goto err; + } + + printf("Writing primary flash...\n"); + if (flash_write_progress(pri_flash, segment_offset/2, len_int, pri_buf)) + { + fprintf(stderr, "Write failed!\n"); + ret = -1; + free(segment); + free(pri_buf); + free(sec_buf); + goto err; + } + + printf("Writing secondary flash...\n"); + if (flash_write_progress(sec_flash, segment_offset/2, len_int, sec_buf)) + { + fprintf(stderr, "Write failed!\n"); + ret = -1; + free(segment); + free(pri_buf); + free(sec_buf); + goto err; + } + + char *pri_check_buf = calloc(segment_size/2, 1); + char *sec_check_buf = calloc(segment_size/2, 1); + memset(pri_check_buf, 0xff, segment_size/2); + memset(sec_check_buf, 0xff, segment_size/2); + + printf("Verifying primary flash...\n"); + flash_read_progress(pri_flash, segment_offset/2, len_int, pri_check_buf); + printf("Verifying secondary flash...\n"); + flash_read_progress(sec_flash, segment_offset/2, len_int, sec_check_buf); + + if (memcmp(pri_buf, pri_check_buf, len_int) || memcmp(sec_buf, sec_check_buf, len_int)) + { + fprintf(stderr, "Verify failed!\n"); + ret = -1; + } + else + { + printf("Programming succeeded!\n"); + } + + free(pri_check_buf); + free(sec_check_buf); + + free(pri_buf); + free(sec_buf); + } + else + { + // SPI or BPI flash + + // round up length to block size + if ((segment_offset + len) & (pri_flash->erase_block_size-1)) + { + len += pri_flash->erase_block_size - ((segment_offset + len) & (pri_flash->erase_block_size-1)); + } + + printf("Erasing flash...\n"); + if (flash_erase_progress(pri_flash, segment_offset, len)) + { + fprintf(stderr, "Erase failed!\n"); + ret = -1; + free(segment); + goto err; + } + + printf("Writing flash...\n"); + if (flash_write_progress(pri_flash, segment_offset, len, segment)) + { + fprintf(stderr, "Write failed!\n"); + ret = -1; + free(segment); + goto err; + } + + char *check_buf = calloc(segment_size, 1); + memset(check_buf, 0xff, segment_size); + + printf("Verifying flash...\n"); + flash_read_progress(pri_flash, segment_offset, len, check_buf); + + if (memcmp(segment, check_buf, len)) + { + fprintf(stderr, "Verify failed!\n"); + ret = -1; + } + else + { + printf("Programming succeeded!\n"); + } + + free(check_buf); + } + + free(segment); + } + + if (action_read) + { + char *segment = calloc(segment_size, 1); + memset(segment, 0xff, segment_size); + + if (dual_qspi) + { + char *pri_buf = calloc(segment_size/2, 1); + char *sec_buf = calloc(segment_size/2, 1); + + printf("Reading primary flash...\n"); + flash_read_progress(pri_flash, segment_offset/2, segment_size/2, pri_buf); + printf("Reading secondary flash...\n"); + flash_read_progress(sec_flash, segment_offset/2, segment_size/2, sec_buf); + + int offset = 0x68; + + memcpy(segment, pri_buf, offset); + + char *c1 = pri_buf+offset; + char *c2 = sec_buf+offset; + + for (size_t k = offset; k < segment_size-offset; k += 2) + { + segment[k] = ((*c1 >> 4) & 0x0f) | (*c2 & 0xf0); + segment[k+1] = (*c1 & 0x0f) | ((*c2 << 4) & 0xf0); + c1++; + c2++; + } + + free(pri_buf); + free(sec_buf); + } + else + { + printf("Reading flash...\n"); + flash_read_progress(pri_flash, segment_offset, segment_size, segment); + } + + if (bitswap) + { + if (word_size == 16) + { + uint16_t *p = (uint16_t *)segment; + + for (size_t k = 0; k < segment_size; k += 2) + { + *p = reverse_bits_16(*p); + p++; + } + } + else + { + uint8_t *p = (uint8_t *)segment; + + for (size_t k = 0; k < segment_size; k++) + { + *p = reverse_bits_8(*p); + p++; + } + } + } + + int file_type = file_type_from_ext(read_file_name); + + if (file_type == FILE_TYPE_BIN) + { + // write binary file + printf("Writing binary file \"%s\"...\n", read_file_name); + read_file = fopen(read_file_name, "wb"); + fwrite(segment, 1, segment_size, read_file); + fclose(read_file); + } + else if (file_type == FILE_TYPE_HEX) + { + fprintf(stderr, "Hex files are not currently supported\n"); + free(segment); + ret = -1; + goto err; + } + else + { + fprintf(stderr, "Unsupported file type\n"); + free(segment); + ret = -1; + goto err; + } + + free(segment); + } + + flash_release(pri_flash); + pri_flash = NULL; + flash_release(sec_flash); + sec_flash = NULL; + + if (action_boot || action_reset) + { + printf("Preparing to reset device...\n"); + + // disable fatal error reporting on port (to prevent IPMI-triggered reboot) + printf("Disabling PCIe fatal error reporting on port...\n"); + pcie_disable_fatal_err(port_path); + + // disconnect from device + mqnic_close(dev); + dev = NULL; + + // attempt to disconnect driver + ptr = strrchr(device_path, '/'); + if (ptr) + { + snprintf(path, sizeof(path), "%s/driver/unbind", device_path); + + if (access(path, F_OK) != -1) + { + printf("Unbinding driver...\n"); + write_str_to_file(path, ptr+1); + } + else + { + printf("No driver bound\n"); + } + } + + sleep(1); + + // trigger FPGA reload + if (action_boot) + { + // reconnect directly to device + snprintf(path, sizeof(path), "%s/resource0", device_path); + dev = mqnic_open(path); + + if (!dev) + { + fprintf(stderr, "Failed to open device\n"); + ret = -1; + goto err; + } + + // reload FPGA + printf("Triggering IPROG to reload FPGA...\n"); + mqnic_reg_write32(dev->regs, MQNIC_REG_FPGA_ID, 0xFEE1DEAD); + + // disconnect + mqnic_close(dev); + dev = NULL; + } + + // remove PCIe device + printf("Removing device...\n"); + + snprintf(path, sizeof(path), "%s/remove", device_path); + + if (write_1_to_file(path)) + { + fprintf(stderr, "Failed to remove device!\n"); + ret = -1; + goto err; + } + + if (action_boot) + { + // give FPGA some time to boot from flash + sleep(4); + } + + sleep(1); + + printf("Performing hot reset on upstream port...\n"); + pcie_hot_reset(port_path); + + sleep(2); + + printf("Rescanning on upstream port...\n"); + + snprintf(path, sizeof(path), "%s/rescan", port_path); + + if (write_1_to_file(path)) + { + fprintf(stderr, "Rescan failed!\n"); + ret = -1; + goto err; + } + + // PCIe device will have a config space, so check for that + snprintf(path, sizeof(path), "%s/config", device_path); + + if (access(path, F_OK) != -1) + { + printf("Success, device is online!\n"); + } + else + { + fprintf(stderr, "Rescan failed, device is offline!\n"); + ret = -1; + goto err; + } + } + +err: + + flash_release(pri_flash); + flash_release(sec_flash); mqnic_close(dev); - return 0; + return ret; }