// SPDX-License-Identifier: GPL-2.0-or-later /*************************************************************************** * Copyright (C) 2010 by Antonio Borneo * * Modified by Megan Wachs from the original stmsmi.c * ***************************************************************************/ /* The Freedom E SPI controller is a SPI bus controller * specifically designed for SPI Flash Memories on Freedom E platforms. * * Two working modes are available: * - SW mode: the SPI is controlled by SW. Any custom commands can be sent * on the bus. Writes are only possible in this mode. * - HW mode: Memory content is directly * accessible in CPU memory space. CPU can read and execute memory content. */ /* ATTENTION: * To have flash memory mapped in CPU memory space, the controller * must have "HW mode" enabled. * 1) The command "reset init" has to initialize the controller and put * it in HW mode (this is actually the default out of reset for Freedom E systems). * 2) every command in this file have to return to prompt in HW mode. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "imp.h" #include "spi.h" #include #include #include #include "target/riscv/riscv.h" /* Register offsets */ #define FESPI_REG_SCKDIV 0x00 #define FESPI_REG_SCKMODE 0x04 #define FESPI_REG_CSID 0x10 #define FESPI_REG_CSDEF 0x14 #define FESPI_REG_CSMODE 0x18 #define FESPI_REG_DCSSCK 0x28 #define FESPI_REG_DSCKCS 0x2a #define FESPI_REG_DINTERCS 0x2c #define FESPI_REG_DINTERXFR 0x2e #define FESPI_REG_FMT 0x40 #define FESPI_REG_TXFIFO 0x48 #define FESPI_REG_RXFIFO 0x4c #define FESPI_REG_TXCTRL 0x50 #define FESPI_REG_RXCTRL 0x54 #define FESPI_REG_FCTRL 0x60 #define FESPI_REG_FFMT 0x64 #define FESPI_REG_IE 0x70 #define FESPI_REG_IP 0x74 /* Fields */ #define FESPI_SCK_POL 0x1 #define FESPI_SCK_PHA 0x2 #define FESPI_FMT_PROTO(x) ((x) & 0x3) #define FESPI_FMT_ENDIAN(x) (((x) & 0x1) << 2) #define FESPI_FMT_DIR(x) (((x) & 0x1) << 3) #define FESPI_FMT_LEN(x) (((x) & 0xf) << 16) /* TXCTRL register */ #define FESPI_TXWM(x) ((x) & 0xffff) /* RXCTRL register */ #define FESPI_RXWM(x) ((x) & 0xffff) #define FESPI_IP_TXWM 0x1 #define FESPI_IP_RXWM 0x2 #define FESPI_FCTRL_EN 0x1 #define FESPI_INSN_CMD_EN 0x1 #define FESPI_INSN_ADDR_LEN(x) (((x) & 0x7) << 1) #define FESPI_INSN_PAD_CNT(x) (((x) & 0xf) << 4) #define FESPI_INSN_CMD_PROTO(x) (((x) & 0x3) << 8) #define FESPI_INSN_ADDR_PROTO(x) (((x) & 0x3) << 10) #define FESPI_INSN_DATA_PROTO(x) (((x) & 0x3) << 12) #define FESPI_INSN_CMD_CODE(x) (((x) & 0xff) << 16) #define FESPI_INSN_PAD_CODE(x) (((x) & 0xff) << 24) /* Values */ #define FESPI_CSMODE_AUTO 0 #define FESPI_CSMODE_HOLD 2 #define FESPI_CSMODE_OFF 3 #define FESPI_DIR_RX 0 #define FESPI_DIR_TX 1 #define FESPI_PROTO_S 0 #define FESPI_PROTO_D 1 #define FESPI_PROTO_Q 2 #define FESPI_ENDIAN_MSB 0 #define FESPI_ENDIAN_LSB 1 /* Timeout in ms */ #define FESPI_CMD_TIMEOUT (100) #define FESPI_PROBE_TIMEOUT (100) #define FESPI_MAX_TIMEOUT (3000) struct fespi_flash_bank { bool probed; target_addr_t ctrl_base; const struct flash_device *dev; }; struct fespi_target { char *name; uint32_t tap_idcode; uint32_t ctrl_base; }; /* TODO !!! What is the right naming convention here? */ static const struct fespi_target target_devices[] = { /* name, tap_idcode, ctrl_base */ { "Freedom E310-G000 SPI Flash", 0x10e31913, 0x10014000 }, { "Freedom E310-G002 SPI Flash", 0x20000913, 0x10014000 }, { NULL, 0, 0 } }; FLASH_BANK_COMMAND_HANDLER(fespi_flash_bank_command) { struct fespi_flash_bank *fespi_info; LOG_DEBUG("%s", __func__); if (CMD_ARGC < 6) return ERROR_COMMAND_SYNTAX_ERROR; fespi_info = malloc(sizeof(struct fespi_flash_bank)); if (!fespi_info) { LOG_ERROR("not enough memory"); return ERROR_FAIL; } bank->driver_priv = fespi_info; fespi_info->probed = false; fespi_info->ctrl_base = 0; if (CMD_ARGC >= 7) { COMMAND_PARSE_ADDRESS(CMD_ARGV[6], fespi_info->ctrl_base); LOG_DEBUG("ASSUMING FESPI device at ctrl_base = " TARGET_ADDR_FMT, fespi_info->ctrl_base); } return ERROR_OK; } static int fespi_read_reg(struct flash_bank *bank, uint32_t *value, target_addr_t address) { struct target *target = bank->target; struct fespi_flash_bank *fespi_info = bank->driver_priv; int result = target_read_u32(target, fespi_info->ctrl_base + address, value); if (result != ERROR_OK) { LOG_ERROR("fespi_read_reg() error at " TARGET_ADDR_FMT, fespi_info->ctrl_base + address); return result; } return ERROR_OK; } static int fespi_write_reg(struct flash_bank *bank, target_addr_t address, uint32_t value) { struct target *target = bank->target; struct fespi_flash_bank *fespi_info = bank->driver_priv; int result = target_write_u32(target, fespi_info->ctrl_base + address, value); if (result != ERROR_OK) { LOG_ERROR("fespi_write_reg() error writing 0x%" PRIx32 " to " TARGET_ADDR_FMT, value, fespi_info->ctrl_base + address); return result; } return ERROR_OK; } static int fespi_disable_hw_mode(struct flash_bank *bank) { uint32_t fctrl; if (fespi_read_reg(bank, &fctrl, FESPI_REG_FCTRL) != ERROR_OK) return ERROR_FAIL; return fespi_write_reg(bank, FESPI_REG_FCTRL, fctrl & ~FESPI_FCTRL_EN); } static int fespi_enable_hw_mode(struct flash_bank *bank) { uint32_t fctrl; if (fespi_read_reg(bank, &fctrl, FESPI_REG_FCTRL) != ERROR_OK) return ERROR_FAIL; return fespi_write_reg(bank, FESPI_REG_FCTRL, fctrl | FESPI_FCTRL_EN); } static int fespi_set_dir(struct flash_bank *bank, bool dir) { uint32_t fmt; if (fespi_read_reg(bank, &fmt, FESPI_REG_FMT) != ERROR_OK) return ERROR_FAIL; return fespi_write_reg(bank, FESPI_REG_FMT, (fmt & ~(FESPI_FMT_DIR(0xFFFFFFFF))) | FESPI_FMT_DIR(dir)); } static int fespi_txwm_wait(struct flash_bank *bank) { int64_t start = timeval_ms(); while (1) { uint32_t ip; if (fespi_read_reg(bank, &ip, FESPI_REG_IP) != ERROR_OK) return ERROR_FAIL; if (ip & FESPI_IP_TXWM) break; int64_t now = timeval_ms(); if (now - start > 1000) { LOG_ERROR("ip.txwm didn't get set."); return ERROR_TARGET_TIMEOUT; } } return ERROR_OK; } static int fespi_tx(struct flash_bank *bank, uint8_t in) { int64_t start = timeval_ms(); while (1) { uint32_t txfifo; if (fespi_read_reg(bank, &txfifo, FESPI_REG_TXFIFO) != ERROR_OK) return ERROR_FAIL; if (!(txfifo >> 31)) break; int64_t now = timeval_ms(); if (now - start > 1000) { LOG_ERROR("txfifo stayed negative."); return ERROR_TARGET_TIMEOUT; } } return fespi_write_reg(bank, FESPI_REG_TXFIFO, in); } static int fespi_rx(struct flash_bank *bank, uint8_t *out) { int64_t start = timeval_ms(); uint32_t value; while (1) { if (fespi_read_reg(bank, &value, FESPI_REG_RXFIFO) != ERROR_OK) return ERROR_FAIL; if (!(value >> 31)) break; int64_t now = timeval_ms(); if (now - start > 1000) { LOG_ERROR("rxfifo didn't go positive (value=0x%" PRIx32 ").", value); return ERROR_TARGET_TIMEOUT; } } if (out) *out = value & 0xff; return ERROR_OK; } /* TODO!!! Why don't we need to call this after writing? */ static int fespi_wip(struct flash_bank *bank, int timeout) { int64_t endtime; fespi_set_dir(bank, FESPI_DIR_RX); if (fespi_write_reg(bank, FESPI_REG_CSMODE, FESPI_CSMODE_HOLD) != ERROR_OK) return ERROR_FAIL; endtime = timeval_ms() + timeout; fespi_tx(bank, SPIFLASH_READ_STATUS); if (fespi_rx(bank, NULL) != ERROR_OK) return ERROR_FAIL; do { alive_sleep(1); fespi_tx(bank, 0); uint8_t rx; if (fespi_rx(bank, &rx) != ERROR_OK) return ERROR_FAIL; if ((rx & SPIFLASH_BSY_BIT) == 0) { if (fespi_write_reg(bank, FESPI_REG_CSMODE, FESPI_CSMODE_AUTO) != ERROR_OK) return ERROR_FAIL; fespi_set_dir(bank, FESPI_DIR_TX); return ERROR_OK; } } while (timeval_ms() < endtime); LOG_ERROR("timeout"); return ERROR_FAIL; } static int fespi_erase_sector(struct flash_bank *bank, int sector) { struct fespi_flash_bank *fespi_info = bank->driver_priv; int retval; retval = fespi_tx(bank, SPIFLASH_WRITE_ENABLE); if (retval != ERROR_OK) return retval; retval = fespi_txwm_wait(bank); if (retval != ERROR_OK) return retval; if (fespi_write_reg(bank, FESPI_REG_CSMODE, FESPI_CSMODE_HOLD) != ERROR_OK) return ERROR_FAIL; retval = fespi_tx(bank, fespi_info->dev->erase_cmd); if (retval != ERROR_OK) return retval; sector = bank->sectors[sector].offset; if (bank->size > 0x1000000) { retval = fespi_tx(bank, sector >> 24); if (retval != ERROR_OK) return retval; } retval = fespi_tx(bank, sector >> 16); if (retval != ERROR_OK) return retval; retval = fespi_tx(bank, sector >> 8); if (retval != ERROR_OK) return retval; retval = fespi_tx(bank, sector); if (retval != ERROR_OK) return retval; retval = fespi_txwm_wait(bank); if (retval != ERROR_OK) return retval; if (fespi_write_reg(bank, FESPI_REG_CSMODE, FESPI_CSMODE_AUTO) != ERROR_OK) return ERROR_FAIL; retval = fespi_wip(bank, FESPI_MAX_TIMEOUT); if (retval != ERROR_OK) return retval; return ERROR_OK; } static int fespi_erase(struct flash_bank *bank, unsigned int first, unsigned int last) { struct target *target = bank->target; struct fespi_flash_bank *fespi_info = bank->driver_priv; int retval = ERROR_OK; LOG_DEBUG("%s: from sector %u to sector %u", __func__, first, last); if (target->state != TARGET_HALTED) { LOG_ERROR("Target not halted"); return ERROR_TARGET_NOT_HALTED; } if ((last < first) || (last >= bank->num_sectors)) { LOG_ERROR("Flash sector invalid"); return ERROR_FLASH_SECTOR_INVALID; } if (!(fespi_info->probed)) { LOG_ERROR("Flash bank not probed"); return ERROR_FLASH_BANK_NOT_PROBED; } for (unsigned int sector = first; sector <= last; sector++) { if (bank->sectors[sector].is_protected) { LOG_ERROR("Flash sector %u protected", sector); return ERROR_FAIL; } } if (fespi_info->dev->erase_cmd == 0x00) return ERROR_FLASH_OPER_UNSUPPORTED; if (fespi_write_reg(bank, FESPI_REG_TXCTRL, FESPI_TXWM(1)) != ERROR_OK) return ERROR_FAIL; retval = fespi_txwm_wait(bank); if (retval != ERROR_OK) { LOG_ERROR("WM Didn't go high before attempting."); return retval; } /* Disable Hardware accesses*/ if (fespi_disable_hw_mode(bank) != ERROR_OK) return ERROR_FAIL; /* poll WIP */ retval = fespi_wip(bank, FESPI_PROBE_TIMEOUT); if (retval != ERROR_OK) goto done; for (unsigned int sector = first; sector <= last; sector++) { retval = fespi_erase_sector(bank, sector); if (retval != ERROR_OK) goto done; keep_alive(); } /* Switch to HW mode before return to prompt */ done: if (fespi_enable_hw_mode(bank) != ERROR_OK) return ERROR_FAIL; return retval; } static int fespi_protect(struct flash_bank *bank, int set, unsigned int first, unsigned int last) { for (unsigned int sector = first; sector <= last; sector++) bank->sectors[sector].is_protected = set; return ERROR_OK; } static int slow_fespi_write_buffer(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, uint32_t len) { struct fespi_flash_bank *fespi_info = bank->driver_priv; uint32_t ii; /* TODO!!! assert that len < page size */ if (fespi_tx(bank, SPIFLASH_WRITE_ENABLE) != ERROR_OK) return ERROR_FAIL; if (fespi_txwm_wait(bank) != ERROR_OK) return ERROR_FAIL; if (fespi_write_reg(bank, FESPI_REG_CSMODE, FESPI_CSMODE_HOLD) != ERROR_OK) return ERROR_FAIL; if (fespi_tx(bank, fespi_info->dev->pprog_cmd) != ERROR_OK) return ERROR_FAIL; if (bank->size > 0x1000000 && fespi_tx(bank, offset >> 24) != ERROR_OK) return ERROR_FAIL; if (fespi_tx(bank, offset >> 16) != ERROR_OK) return ERROR_FAIL; if (fespi_tx(bank, offset >> 8) != ERROR_OK) return ERROR_FAIL; if (fespi_tx(bank, offset) != ERROR_OK) return ERROR_FAIL; for (ii = 0; ii < len; ii++) { if (fespi_tx(bank, buffer[ii]) != ERROR_OK) return ERROR_FAIL; } if (fespi_txwm_wait(bank) != ERROR_OK) return ERROR_FAIL; if (fespi_write_reg(bank, FESPI_REG_CSMODE, FESPI_CSMODE_AUTO) != ERROR_OK) return ERROR_FAIL; keep_alive(); return ERROR_OK; } static const uint8_t riscv32_bin[] = { #include "../../../contrib/loaders/flash/fespi/riscv32_fespi.inc" }; static const uint8_t riscv64_bin[] = { #include "../../../contrib/loaders/flash/fespi/riscv64_fespi.inc" }; static int fespi_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, uint32_t count) { struct target *target = bank->target; struct fespi_flash_bank *fespi_info = bank->driver_priv; uint32_t cur_count, page_size; int retval = ERROR_OK; LOG_DEBUG("bank->size=0x%x offset=0x%08" PRIx32 " count=0x%08" PRIx32, bank->size, offset, count); if (target->state != TARGET_HALTED) { LOG_ERROR("Target not halted"); return ERROR_TARGET_NOT_HALTED; } if (offset + count > fespi_info->dev->size_in_bytes) { LOG_WARNING("Write past end of flash. Extra data discarded."); count = fespi_info->dev->size_in_bytes - offset; } /* Check sector protection */ for (unsigned int sector = 0; sector < bank->num_sectors; sector++) { /* Start offset in or before this sector? */ /* End offset in or behind this sector? */ if ((offset < (bank->sectors[sector].offset + bank->sectors[sector].size)) && ((offset + count - 1) >= bank->sectors[sector].offset) && bank->sectors[sector].is_protected) { LOG_ERROR("Flash sector %u protected", sector); return ERROR_FAIL; } } struct riscv_info *riscv = riscv_info(target); if (!is_riscv(riscv)) { LOG_ERROR("Unexpected target type"); return ERROR_FAIL; } unsigned int xlen = riscv_xlen(target); struct working_area *algorithm_wa = NULL; struct working_area *data_wa = NULL; const uint8_t *bin; size_t bin_size; if (xlen == 32) { bin = riscv32_bin; bin_size = sizeof(riscv32_bin); } else { bin = riscv64_bin; bin_size = sizeof(riscv64_bin); } unsigned data_wa_size = 0; if (target_alloc_working_area(target, bin_size, &algorithm_wa) == ERROR_OK) { retval = target_write_buffer(target, algorithm_wa->address, bin_size, bin); if (retval != ERROR_OK) { LOG_ERROR("Failed to write code to " TARGET_ADDR_FMT ": %d", algorithm_wa->address, retval); target_free_working_area(target, algorithm_wa); algorithm_wa = NULL; } else { data_wa_size = MIN(target_get_working_area_avail(target), count); if (data_wa_size < 128) { LOG_WARNING("Couldn't allocate data working area."); target_free_working_area(target, algorithm_wa); algorithm_wa = NULL; } else if (target_alloc_working_area(target, data_wa_size, &data_wa) != ERROR_OK) { target_free_working_area(target, algorithm_wa); algorithm_wa = NULL; } } } else { LOG_WARNING("Couldn't allocate %zd-byte working area.", bin_size); algorithm_wa = NULL; } /* If no valid page_size, use reasonable default. */ page_size = fespi_info->dev->pagesize ? fespi_info->dev->pagesize : SPIFLASH_DEF_PAGESIZE; if (algorithm_wa) { struct reg_param reg_params[6]; init_reg_param(®_params[0], "a0", xlen, PARAM_IN_OUT); init_reg_param(®_params[1], "a1", xlen, PARAM_OUT); init_reg_param(®_params[2], "a2", xlen, PARAM_OUT); init_reg_param(®_params[3], "a3", xlen, PARAM_OUT); init_reg_param(®_params[4], "a4", xlen, PARAM_OUT); init_reg_param(®_params[5], "a5", xlen, PARAM_OUT); while (count > 0) { cur_count = MIN(count, data_wa_size); buf_set_u64(reg_params[0].value, 0, xlen, fespi_info->ctrl_base); buf_set_u64(reg_params[1].value, 0, xlen, page_size); buf_set_u64(reg_params[2].value, 0, xlen, data_wa->address); buf_set_u64(reg_params[3].value, 0, xlen, offset); buf_set_u64(reg_params[4].value, 0, xlen, cur_count); buf_set_u64(reg_params[5].value, 0, xlen, fespi_info->dev->pprog_cmd | (bank->size > 0x1000000 ? 0x100 : 0)); retval = target_write_buffer(target, data_wa->address, cur_count, buffer); if (retval != ERROR_OK) { LOG_DEBUG("Failed to write %d bytes to " TARGET_ADDR_FMT ": %d", cur_count, data_wa->address, retval); goto err; } LOG_DEBUG("write(ctrl_base=0x%" TARGET_PRIxADDR ", page_size=0x%x, " "address=0x%" TARGET_PRIxADDR ", offset=0x%" PRIx32 ", count=0x%" PRIx32 "), buffer=%02x %02x %02x %02x %02x %02x ..." PRIx32, fespi_info->ctrl_base, page_size, data_wa->address, offset, cur_count, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5]); retval = target_run_algorithm(target, 0, NULL, ARRAY_SIZE(reg_params), reg_params, algorithm_wa->address, 0, cur_count * 2, NULL); if (retval != ERROR_OK) { LOG_ERROR("Failed to execute algorithm at " TARGET_ADDR_FMT ": %d", algorithm_wa->address, retval); goto err; } uint64_t algorithm_result = buf_get_u64(reg_params[0].value, 0, xlen); if (algorithm_result != 0) { LOG_ERROR("Algorithm returned error %" PRId64, algorithm_result); retval = ERROR_FAIL; goto err; } buffer += cur_count; offset += cur_count; count -= cur_count; } target_free_working_area(target, data_wa); target_free_working_area(target, algorithm_wa); } else { fespi_txwm_wait(bank); /* Disable Hardware accesses*/ if (fespi_disable_hw_mode(bank) != ERROR_OK) return ERROR_FAIL; /* poll WIP */ retval = fespi_wip(bank, FESPI_PROBE_TIMEOUT); if (retval != ERROR_OK) goto err; uint32_t page_offset = offset % page_size; /* central part, aligned words */ while (count > 0) { /* clip block at page boundary */ if (page_offset + count > page_size) cur_count = page_size - page_offset; else cur_count = count; retval = slow_fespi_write_buffer(bank, buffer, offset, cur_count); if (retval != ERROR_OK) goto err; page_offset = 0; buffer += cur_count; offset += cur_count; count -= cur_count; } /* Switch to HW mode before return to prompt */ if (fespi_enable_hw_mode(bank) != ERROR_OK) return ERROR_FAIL; } return ERROR_OK; err: target_free_working_area(target, data_wa); target_free_working_area(target, algorithm_wa); /* Switch to HW mode before return to prompt */ if (fespi_enable_hw_mode(bank) != ERROR_OK) return ERROR_FAIL; return retval; } /* Return ID of flash device */ /* On exit, SW mode is kept */ static int fespi_read_flash_id(struct flash_bank *bank, uint32_t *id) { struct target *target = bank->target; int retval; if (target->state != TARGET_HALTED) { LOG_ERROR("Target not halted"); return ERROR_TARGET_NOT_HALTED; } fespi_txwm_wait(bank); /* poll WIP */ retval = fespi_wip(bank, FESPI_PROBE_TIMEOUT); if (retval != ERROR_OK) return retval; fespi_set_dir(bank, FESPI_DIR_RX); /* Send SPI command "read ID" */ if (fespi_write_reg(bank, FESPI_REG_CSMODE, FESPI_CSMODE_HOLD) != ERROR_OK) return ERROR_FAIL; fespi_tx(bank, SPIFLASH_READ_ID); /* Send dummy bytes to actually read the ID.*/ fespi_tx(bank, 0); fespi_tx(bank, 0); fespi_tx(bank, 0); /* read ID from Receive Register */ *id = 0; if (fespi_rx(bank, NULL) != ERROR_OK) return ERROR_FAIL; uint8_t rx; if (fespi_rx(bank, &rx) != ERROR_OK) return ERROR_FAIL; *id = rx; if (fespi_rx(bank, &rx) != ERROR_OK) return ERROR_FAIL; *id |= (rx << 8); if (fespi_rx(bank, &rx) != ERROR_OK) return ERROR_FAIL; *id |= (rx << 16); if (fespi_write_reg(bank, FESPI_REG_CSMODE, FESPI_CSMODE_AUTO) != ERROR_OK) return ERROR_FAIL; fespi_set_dir(bank, FESPI_DIR_TX); return ERROR_OK; } static int fespi_probe(struct flash_bank *bank) { struct target *target = bank->target; struct fespi_flash_bank *fespi_info = bank->driver_priv; struct flash_sector *sectors; uint32_t id = 0; /* silence uninitialized warning */ const struct fespi_target *target_device; int retval; uint32_t sectorsize; if (fespi_info->probed) free(bank->sectors); fespi_info->probed = false; if (fespi_info->ctrl_base == 0) { for (target_device = target_devices ; target_device->name ; ++target_device) if (target_device->tap_idcode == target->tap->idcode) break; if (!target_device->name) { LOG_ERROR("Device ID 0x%" PRIx32 " is not known as FESPI capable", target->tap->idcode); return ERROR_FAIL; } fespi_info->ctrl_base = target_device->ctrl_base; LOG_DEBUG("Valid FESPI on device %s at address " TARGET_ADDR_FMT, target_device->name, bank->base); } else { LOG_DEBUG("Assuming FESPI as specified at address " TARGET_ADDR_FMT " with ctrl at " TARGET_ADDR_FMT, fespi_info->ctrl_base, bank->base); } /* read and decode flash ID; returns in SW mode */ if (fespi_write_reg(bank, FESPI_REG_TXCTRL, FESPI_TXWM(1)) != ERROR_OK) return ERROR_FAIL; fespi_set_dir(bank, FESPI_DIR_TX); /* Disable Hardware accesses*/ if (fespi_disable_hw_mode(bank) != ERROR_OK) return ERROR_FAIL; retval = fespi_read_flash_id(bank, &id); if (fespi_enable_hw_mode(bank) != ERROR_OK) return ERROR_FAIL; if (retval != ERROR_OK) return retval; fespi_info->dev = NULL; for (const struct flash_device *p = flash_devices; p->name ; p++) if (p->device_id == id) { fespi_info->dev = p; break; } if (!fespi_info->dev) { LOG_ERROR("Unknown flash device (ID 0x%08" PRIx32 ")", id); return ERROR_FAIL; } LOG_INFO("Found flash device \'%s\' (ID 0x%08" PRIx32 ")", fespi_info->dev->name, fespi_info->dev->device_id); /* Set correct size value */ bank->size = fespi_info->dev->size_in_bytes; if (bank->size <= (1UL << 16)) LOG_WARNING("device needs 2-byte addresses - not implemented"); /* if no sectors, treat whole bank as single sector */ sectorsize = fespi_info->dev->sectorsize ? fespi_info->dev->sectorsize : fespi_info->dev->size_in_bytes; /* create and fill sectors array */ bank->num_sectors = fespi_info->dev->size_in_bytes / sectorsize; sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); if (!sectors) { LOG_ERROR("not enough memory"); return ERROR_FAIL; } for (unsigned int sector = 0; sector < bank->num_sectors; sector++) { sectors[sector].offset = sector * sectorsize; sectors[sector].size = sectorsize; sectors[sector].is_erased = -1; sectors[sector].is_protected = 0; } bank->sectors = sectors; fespi_info->probed = true; return ERROR_OK; } static int fespi_auto_probe(struct flash_bank *bank) { struct fespi_flash_bank *fespi_info = bank->driver_priv; if (fespi_info->probed) return ERROR_OK; return fespi_probe(bank); } static int fespi_protect_check(struct flash_bank *bank) { /* Nothing to do. Protection is only handled in SW. */ return ERROR_OK; } static int get_fespi_info(struct flash_bank *bank, struct command_invocation *cmd) { struct fespi_flash_bank *fespi_info = bank->driver_priv; if (!(fespi_info->probed)) { command_print_sameline(cmd, "\nFESPI flash bank not probed yet\n"); return ERROR_OK; } command_print_sameline(cmd, "\nFESPI flash information:\n" " Device \'%s\' (ID 0x%08" PRIx32 ")\n", fespi_info->dev->name, fespi_info->dev->device_id); return ERROR_OK; } const struct flash_driver fespi_flash = { .name = "fespi", .flash_bank_command = fespi_flash_bank_command, .erase = fespi_erase, .protect = fespi_protect, .write = fespi_write, .read = default_flash_read, .probe = fespi_probe, .auto_probe = fespi_auto_probe, .erase_check = default_flash_blank_check, .protect_check = fespi_protect_check, .info = get_fespi_info, .free_driver_priv = default_flash_free_driver_priv };