/*************************************************************************** * Copyright (C) 2014 by Mahavir Jain * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ /* * This is QSPI flash controller driver for Marvell's Wireless * Microcontroller platform. * * For more information please refer, * https://origin-www.marvell.com/microcontrollers/wi-fi-microcontroller-platform/ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "imp.h" #include "spi.h" #include #include #include #define QSPI_R_EN (0x0) #define QSPI_W_EN (0x1) #define QSPI_SS_DISABLE (0x0) #define QSPI_SS_ENABLE (0x1) #define WRITE_DISBALE (0x0) #define WRITE_ENABLE (0x1) #define QSPI_TIMEOUT (1000) #define FIFO_FLUSH_TIMEOUT (1000) #define BLOCK_ERASE_TIMEOUT (1000) #define CHIP_ERASE_TIMEOUT (10000) #define SS_EN (1 << 0) #define XFER_RDY (1 << 1) #define RFIFO_EMPTY (1 << 4) #define WFIFO_EMPTY (1 << 6) #define WFIFO_FULL (1 << 7) #define FIFO_FLUSH (1 << 9) #define RW_EN (1 << 13) #define XFER_STOP (1 << 14) #define XFER_START (1 << 15) #define CONF_MASK (0x7) #define CONF_OFFSET (10) #define INS_WRITE_ENABLE 0x06 #define INS_WRITE_DISABLE 0x04 #define INS_READ_STATUS 0x05 #define INS_PAGE_PROGRAM 0x02 #define CNTL 0x0 /* QSPI_BASE + 0x0 */ #define CONF 0x4 #define DOUT 0x8 #define DIN 0xc #define INSTR 0x10 #define ADDR 0x14 #define RDMODE 0x18 #define HDRCNT 0x1c #define DINCNT 0x20 struct mrvlqspi_flash_bank { int probed; uint32_t reg_base; uint32_t bank_num; const struct flash_device *dev; }; static inline uint32_t mrvlqspi_get_reg(struct flash_bank *bank, uint32_t reg) { struct mrvlqspi_flash_bank *mrvlqspi_info = bank->driver_priv; return reg + mrvlqspi_info->reg_base; } static inline int mrvlqspi_set_din_cnt(struct flash_bank *bank, uint32_t count) { struct target *target = bank->target; return target_write_u32(target, mrvlqspi_get_reg(bank, DINCNT), count); } static inline int mrvlqspi_set_addr(struct flash_bank *bank, uint32_t addr) { struct target *target = bank->target; return target_write_u32(target, mrvlqspi_get_reg(bank, ADDR), addr); } static inline int mrvlqspi_set_instr(struct flash_bank *bank, uint32_t instr) { struct target *target = bank->target; return target_write_u32(target, mrvlqspi_get_reg(bank, INSTR), instr); } static inline int mrvlqspi_set_hdr_cnt(struct flash_bank *bank, uint32_t hdr_cnt) { struct target *target = bank->target; return target_write_u32(target, mrvlqspi_get_reg(bank, HDRCNT), hdr_cnt); } static int mrvlqspi_set_conf(struct flash_bank *bank, uint32_t conf_val) { int retval; uint32_t regval; struct target *target = bank->target; retval = target_read_u32(target, mrvlqspi_get_reg(bank, CONF), ®val); if (retval != ERROR_OK) return retval; regval &= ~(CONF_MASK << CONF_OFFSET); regval |= (conf_val << CONF_OFFSET); return target_write_u32(target, mrvlqspi_get_reg(bank, CONF), regval); } static int mrvlqspi_set_ss_state(struct flash_bank *bank, bool state, int timeout) { int retval; uint32_t regval; struct target *target = bank->target; retval = target_read_u32(target, mrvlqspi_get_reg(bank, CNTL), ®val); if (retval != ERROR_OK) return retval; if (state) regval |= SS_EN; else regval &= ~(SS_EN); retval = target_write_u32(target, mrvlqspi_get_reg(bank, CNTL), regval); if (retval != ERROR_OK) return retval; /* wait for xfer_ready to set */ for (;;) { retval = target_read_u32(target, mrvlqspi_get_reg(bank, CNTL), ®val); if (retval != ERROR_OK) return retval; LOG_DEBUG("status: 0x%08" PRIx32, regval); if ((regval & XFER_RDY) == XFER_RDY) break; if (timeout-- <= 0) { LOG_ERROR("timed out waiting for flash"); return ERROR_FAIL; } alive_sleep(1); } return ERROR_OK; } static int mrvlqspi_start_transfer(struct flash_bank *bank, bool rw_mode) { int retval; uint32_t regval; struct target *target = bank->target; retval = mrvlqspi_set_ss_state(bank, QSPI_SS_ENABLE, QSPI_TIMEOUT); if (retval != ERROR_OK) return retval; retval = target_read_u32(target, mrvlqspi_get_reg(bank, CONF), ®val); if (retval != ERROR_OK) return retval; if (rw_mode) regval |= RW_EN; else regval &= ~(RW_EN); regval |= XFER_START; retval = target_write_u32(target, mrvlqspi_get_reg(bank, CONF), regval); if (retval != ERROR_OK) return retval; return ERROR_OK; } static int mrvlqspi_stop_transfer(struct flash_bank *bank) { int retval; uint32_t regval; struct target *target = bank->target; int timeout = QSPI_TIMEOUT; /* wait for xfer_ready and wfifo_empty to set */ for (;;) { retval = target_read_u32(target, mrvlqspi_get_reg(bank, CNTL), ®val); if (retval != ERROR_OK) return retval; LOG_DEBUG("status: 0x%08" PRIx32, regval); if ((regval & (XFER_RDY | WFIFO_EMPTY)) == (XFER_RDY | WFIFO_EMPTY)) break; if (timeout-- <= 0) { LOG_ERROR("timed out waiting for flash"); return ERROR_FAIL; } alive_sleep(1); } retval = target_read_u32(target, mrvlqspi_get_reg(bank, CONF), ®val); if (retval != ERROR_OK) return retval; regval |= XFER_STOP; retval = target_write_u32(target, mrvlqspi_get_reg(bank, CONF), regval); if (retval != ERROR_OK) return retval; /* wait for xfer_start to reset */ for (;;) { retval = target_read_u32(target, mrvlqspi_get_reg(bank, CONF), ®val); if (retval != ERROR_OK) return retval; LOG_DEBUG("status: 0x%08" PRIx32, regval); if ((regval & XFER_START) == 0) break; if (timeout-- <= 0) { LOG_ERROR("timed out waiting for flash"); return ERROR_FAIL; } alive_sleep(1); } retval = mrvlqspi_set_ss_state(bank, QSPI_SS_DISABLE, QSPI_TIMEOUT); if (retval != ERROR_OK) return retval; return ERROR_OK; } static int mrvlqspi_fifo_flush(struct flash_bank *bank, int timeout) { int retval; uint32_t val; struct target *target = bank->target; retval = target_read_u32(target, mrvlqspi_get_reg(bank, CONF), &val); if (retval != ERROR_OK) return retval; val |= FIFO_FLUSH; retval = target_write_u32(target, mrvlqspi_get_reg(bank, CONF), val); if (retval != ERROR_OK) return retval; /* wait for fifo_flush to clear */ for (;;) { retval = target_read_u32(target, mrvlqspi_get_reg(bank, CONF), &val); if (retval != ERROR_OK) return retval; LOG_DEBUG("status: 0x%08" PRIX32, val); if ((val & FIFO_FLUSH) == 0) break; if (timeout-- <= 0) { LOG_ERROR("timed out waiting for flash"); return ERROR_FAIL; } alive_sleep(1); } return ERROR_OK; } static int mrvlqspi_read_byte(struct flash_bank *bank, uint8_t *data) { int retval; uint32_t val; struct target *target = bank->target; /* wait for rfifo_empty to reset */ for (;;) { retval = target_read_u32(target, mrvlqspi_get_reg(bank, CNTL), &val); if (retval != ERROR_OK) return retval; LOG_DEBUG("status: 0x%08" PRIx32, val); if ((val & RFIFO_EMPTY) == 0) break; usleep(10); } retval = target_read_u32(target, mrvlqspi_get_reg(bank, DIN), &val); if (retval != ERROR_OK) return retval; *data = val & 0xFF; return ERROR_OK; } static int mrvlqspi_flash_busy_status(struct flash_bank *bank, int timeout) { uint8_t val; int retval; /* Flush read/write fifo's */ retval = mrvlqspi_fifo_flush(bank, FIFO_FLUSH_TIMEOUT); if (retval != ERROR_OK) return retval; /* Set instruction/addr count value */ retval = mrvlqspi_set_hdr_cnt(bank, 0x1); if (retval != ERROR_OK) return retval; /* Read flash status register in continuous manner */ retval = mrvlqspi_set_din_cnt(bank, 0x0); if (retval != ERROR_OK) return retval; /* Set instruction */ retval = mrvlqspi_set_instr(bank, INS_READ_STATUS); if (retval != ERROR_OK) return retval; /* Set data and addr pin length */ retval = mrvlqspi_set_conf(bank, 0x0); if (retval != ERROR_OK) return retval; /* Enable read mode transfer */ retval = mrvlqspi_start_transfer(bank, QSPI_R_EN); if (retval != ERROR_OK) return retval; for (;;) { retval = mrvlqspi_read_byte(bank, &val); if (retval != ERROR_OK) return retval; if (!(val & 0x1)) break; if (timeout-- <= 0) { LOG_ERROR("timed out waiting for flash"); return ERROR_FAIL; } alive_sleep(1); } return mrvlqspi_stop_transfer(bank); } static int mrvlqspi_set_write_status(struct flash_bank *bank, bool mode) { int retval; uint32_t instr; /* Flush read/write fifo's */ retval = mrvlqspi_fifo_flush(bank, FIFO_FLUSH_TIMEOUT); if (retval != ERROR_OK) return retval; /* Set instruction/addr count value */ retval = mrvlqspi_set_hdr_cnt(bank, 0x1); if (retval != ERROR_OK) return retval; if (mode) instr = INS_WRITE_ENABLE; else instr = INS_WRITE_DISABLE; /* Set instruction */ retval = mrvlqspi_set_instr(bank, instr); if (retval != ERROR_OK) return retval; retval = mrvlqspi_start_transfer(bank, QSPI_W_EN); if (retval != ERROR_OK) return retval; retval = mrvlqspi_stop_transfer(bank); if (retval != ERROR_OK) return retval; return retval; } static int mrvlqspi_read_id(struct flash_bank *bank, uint32_t *id) { uint8_t id_buf[3] = {0, 0, 0}; int retval, i; LOG_DEBUG("Getting ID"); /* Flush read/write fifo's */ retval = mrvlqspi_fifo_flush(bank, FIFO_FLUSH_TIMEOUT); if (retval != ERROR_OK) return retval; /* Set instruction/addr count value */ retval = mrvlqspi_set_hdr_cnt(bank, 0x1); if (retval != ERROR_OK) return retval; /* Set count for number of bytes to read */ retval = mrvlqspi_set_din_cnt(bank, 0x3); if (retval != ERROR_OK) return retval; /* Set instruction */ retval = mrvlqspi_set_instr(bank, SPIFLASH_READ_ID); if (retval != ERROR_OK) return retval; /* Set data and addr pin length */ retval = mrvlqspi_set_conf(bank, 0x0); if (retval != ERROR_OK) return retval; retval = mrvlqspi_start_transfer(bank, QSPI_R_EN); if (retval != ERROR_OK) return retval; for (i = 0; i < 3; i++) { retval = mrvlqspi_read_byte(bank, &id_buf[i]); if (retval != ERROR_OK) return retval; } LOG_DEBUG("ID is 0x%02" PRIx8 " 0x%02" PRIx8 " 0x%02" PRIx8, id_buf[0], id_buf[1], id_buf[2]); retval = mrvlqspi_set_ss_state(bank, QSPI_SS_DISABLE, QSPI_TIMEOUT); if (retval != ERROR_OK) return retval; *id = id_buf[2] << 16 | id_buf[1] << 8 | id_buf[0]; return ERROR_OK; } static int mrvlqspi_block_erase(struct flash_bank *bank, uint32_t offset) { int retval; struct mrvlqspi_flash_bank *mrvlqspi_info = bank->driver_priv; /* Set flash write enable */ retval = mrvlqspi_set_write_status(bank, WRITE_ENABLE); if (retval != ERROR_OK) return retval; /* Set instruction/addr count value */ retval = mrvlqspi_set_hdr_cnt(bank, (0x1 | (0x3 << 4))); if (retval != ERROR_OK) return retval; /* Set read offset address */ retval = mrvlqspi_set_addr(bank, offset); if (retval != ERROR_OK) return retval; /* Set instruction */ retval = mrvlqspi_set_instr(bank, mrvlqspi_info->dev->erase_cmd); if (retval != ERROR_OK) return retval; retval = mrvlqspi_start_transfer(bank, QSPI_W_EN); if (retval != ERROR_OK) return retval; retval = mrvlqspi_stop_transfer(bank); if (retval != ERROR_OK) return retval; return mrvlqspi_flash_busy_status(bank, BLOCK_ERASE_TIMEOUT); } static int mrvlqspi_bulk_erase(struct flash_bank *bank) { int retval; struct mrvlqspi_flash_bank *mrvlqspi_info = bank->driver_priv; /* Set flash write enable */ retval = mrvlqspi_set_write_status(bank, WRITE_ENABLE); if (retval != ERROR_OK) return retval; /* Set instruction */ retval = mrvlqspi_set_instr(bank, mrvlqspi_info->dev->chip_erase_cmd); if (retval != ERROR_OK) return retval; retval = mrvlqspi_start_transfer(bank, QSPI_W_EN); if (retval != ERROR_OK) return retval; retval = mrvlqspi_stop_transfer(bank); if (retval != ERROR_OK) return retval; return mrvlqspi_flash_busy_status(bank, CHIP_ERASE_TIMEOUT); } static int mrvlqspi_flash_erase(struct flash_bank *bank, int first, int last) { struct target *target = bank->target; struct mrvlqspi_flash_bank *mrvlqspi_info = bank->driver_priv; int retval = ERROR_OK; int sector; LOG_DEBUG("erase from sector %d to sector %d", first, last); if (target->state != TARGET_HALTED) { LOG_ERROR("Target not halted"); return ERROR_TARGET_NOT_HALTED; } if ((first < 0) || (last < first) || (last >= bank->num_sectors)) { LOG_ERROR("Flash sector invalid"); return ERROR_FLASH_SECTOR_INVALID; } if (!(mrvlqspi_info->probed)) { LOG_ERROR("Flash bank not probed"); return ERROR_FLASH_BANK_NOT_PROBED; } for (sector = first; sector <= last; sector++) { if (bank->sectors[sector].is_protected) { LOG_ERROR("Flash sector %d protected", sector); return ERROR_FAIL; } } /* If we're erasing the entire chip and the flash supports * it, use a bulk erase instead of going sector-by-sector. */ if (first == 0 && last == (bank->num_sectors - 1) && mrvlqspi_info->dev->chip_erase_cmd != mrvlqspi_info->dev->erase_cmd) { LOG_DEBUG("Chip supports the bulk erase command."\ " Will use bulk erase instead of sector-by-sector erase."); retval = mrvlqspi_bulk_erase(bank); if (retval == ERROR_OK) { return retval; } else LOG_WARNING("Bulk flash erase failed." " Falling back to sector-by-sector erase."); } for (sector = first; sector <= last; sector++) { retval = mrvlqspi_block_erase(bank, sector * mrvlqspi_info->dev->sectorsize); if (retval != ERROR_OK) return retval; } return retval; } static int mrvlqspi_flash_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, uint32_t count) { struct target *target = bank->target; struct mrvlqspi_flash_bank *mrvlqspi_info = bank->driver_priv; int retval = ERROR_OK; uint32_t page_size, fifo_size; struct working_area *fifo; struct reg_param reg_params[6]; struct armv7m_algorithm armv7m_info; struct working_area *write_algorithm; int sector; LOG_DEBUG("offset=0x%08" PRIx32 " count=0x%08" PRIx32, offset, count); if (target->state != TARGET_HALTED) { LOG_ERROR("Target not halted"); return ERROR_TARGET_NOT_HALTED; } if (offset + count > mrvlqspi_info->dev->size_in_bytes) { LOG_WARNING("Writes past end of flash. Extra data discarded."); count = mrvlqspi_info->dev->size_in_bytes - offset; } /* Check sector protection */ for (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 %d protected", sector); return ERROR_FAIL; } } page_size = mrvlqspi_info->dev->pagesize; /* See contrib/loaders/flash/mrvlqspi.S for src */ static const uint8_t mrvlqspi_flash_write_code[] = { 0x4f, 0xf0, 0x00, 0x0a, 0xa2, 0x44, 0x92, 0x45, 0x7f, 0xf6, 0xfc, 0xaf, 0x00, 0xf0, 0x6b, 0xf8, 0x5f, 0xf0, 0x01, 0x08, 0xc5, 0xf8, 0x1c, 0x80, 0x5f, 0xf0, 0x06, 0x08, 0xc5, 0xf8, 0x10, 0x80, 0x5f, 0xf0, 0x01, 0x09, 0x00, 0xf0, 0x6b, 0xf8, 0x00, 0xf0, 0x7d, 0xf8, 0x5f, 0xf0, 0x31, 0x08, 0xc5, 0xf8, 0x1c, 0x80, 0x90, 0x46, 0xc5, 0xf8, 0x14, 0x80, 0x5f, 0xf0, 0x02, 0x08, 0xc5, 0xf8, 0x10, 0x80, 0x5f, 0xf0, 0x01, 0x09, 0x00, 0xf0, 0x5a, 0xf8, 0xd0, 0xf8, 0x00, 0x80, 0xb8, 0xf1, 0x00, 0x0f, 0x00, 0xf0, 0x8b, 0x80, 0x47, 0x68, 0x47, 0x45, 0x3f, 0xf4, 0xf6, 0xaf, 0x17, 0xf8, 0x01, 0x9b, 0x00, 0xf0, 0x30, 0xf8, 0x8f, 0x42, 0x28, 0xbf, 0x00, 0xf1, 0x08, 0x07, 0x47, 0x60, 0x01, 0x3b, 0x00, 0x2b, 0x00, 0xf0, 0x05, 0x80, 0x02, 0xf1, 0x01, 0x02, 0x92, 0x45, 0x7f, 0xf4, 0xe4, 0xaf, 0x00, 0xf0, 0x50, 0xf8, 0xa2, 0x44, 0x00, 0xf0, 0x2d, 0xf8, 0x5f, 0xf0, 0x01, 0x08, 0xc5, 0xf8, 0x1c, 0x80, 0x5f, 0xf0, 0x00, 0x08, 0xc5, 0xf8, 0x20, 0x80, 0x5f, 0xf0, 0x05, 0x08, 0xc5, 0xf8, 0x10, 0x80, 0x5f, 0xf0, 0x00, 0x09, 0x00, 0xf0, 0x29, 0xf8, 0x00, 0xf0, 0x13, 0xf8, 0x09, 0xf0, 0x01, 0x09, 0xb9, 0xf1, 0x00, 0x0f, 0xf8, 0xd1, 0x00, 0xf0, 0x34, 0xf8, 0x00, 0x2b, 0xa4, 0xd1, 0x00, 0xf0, 0x53, 0xb8, 0xd5, 0xf8, 0x00, 0x80, 0x5f, 0xea, 0x08, 0x68, 0xfa, 0xd4, 0xc5, 0xf8, 0x08, 0x90, 0x70, 0x47, 0xd5, 0xf8, 0x00, 0x80, 0x5f, 0xea, 0xc8, 0x68, 0xfa, 0xd4, 0xd5, 0xf8, 0x0c, 0x90, 0x70, 0x47, 0xd5, 0xf8, 0x04, 0x80, 0x48, 0xf4, 0x00, 0x78, 0xc5, 0xf8, 0x04, 0x80, 0xd5, 0xf8, 0x04, 0x80, 0x5f, 0xea, 0x88, 0x58, 0xfa, 0xd4, 0x70, 0x47, 0xd5, 0xf8, 0x00, 0x80, 0x48, 0xf0, 0x01, 0x08, 0xc5, 0xf8, 0x00, 0x80, 0xd5, 0xf8, 0x00, 0x80, 0x5f, 0xea, 0x88, 0x78, 0xfa, 0xd5, 0xd5, 0xf8, 0x04, 0x80, 0x69, 0xf3, 0x4d, 0x38, 0x48, 0xf4, 0x00, 0x48, 0xc5, 0xf8, 0x04, 0x80, 0x70, 0x47, 0xd5, 0xf8, 0x00, 0x80, 0x5f, 0xea, 0x88, 0x78, 0xfa, 0xd5, 0xd5, 0xf8, 0x00, 0x80, 0x5f, 0xea, 0x48, 0x68, 0xfa, 0xd5, 0xd5, 0xf8, 0x04, 0x80, 0x48, 0xf4, 0x80, 0x48, 0xc5, 0xf8, 0x04, 0x80, 0xd5, 0xf8, 0x04, 0x80, 0x5f, 0xea, 0x08, 0x48, 0xfa, 0xd4, 0xd5, 0xf8, 0x00, 0x80, 0x28, 0xf0, 0x01, 0x08, 0xc5, 0xf8, 0x00, 0x80, 0xd5, 0xf8, 0x00, 0x80, 0x5f, 0xea, 0x88, 0x78, 0xfa, 0xd5, 0x70, 0x47, 0x00, 0x20, 0x50, 0x60, 0x30, 0x46, 0x00, 0xbe }; if (target_alloc_working_area(target, sizeof(mrvlqspi_flash_write_code), &write_algorithm) != ERROR_OK) { LOG_ERROR("Insufficient working area. You must configure"\ " a working area > %zdB in order to write to SPIFI flash.", sizeof(mrvlqspi_flash_write_code)); return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } retval = target_write_buffer(target, write_algorithm->address, sizeof(mrvlqspi_flash_write_code), mrvlqspi_flash_write_code); if (retval != ERROR_OK) { target_free_working_area(target, write_algorithm); return retval; } /* FIFO allocation */ fifo_size = target_get_working_area_avail(target); if (fifo_size == 0) { /* if we already allocated the writing code but failed to get fifo * space, free the algorithm */ target_free_working_area(target, write_algorithm); LOG_ERROR("Insufficient working area. Please allocate at least"\ " %zdB of working area to enable flash writes.", sizeof(mrvlqspi_flash_write_code) + 1 ); return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } else if (fifo_size < page_size) LOG_WARNING("Working area size is limited; flash writes may be"\ " slow. Increase working area size to at least %zdB"\ " to reduce write times.", (size_t)(sizeof(mrvlqspi_flash_write_code) + page_size) ); if (target_alloc_working_area(target, fifo_size, &fifo) != ERROR_OK) { target_free_working_area(target, write_algorithm); return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } armv7m_info.common_magic = ARMV7M_COMMON_MAGIC; armv7m_info.core_mode = ARM_MODE_THREAD; init_reg_param(®_params[0], "r0", 32, PARAM_IN_OUT); /* buffer start, status (out) */ init_reg_param(®_params[1], "r1", 32, PARAM_OUT); /* buffer end */ init_reg_param(®_params[2], "r2", 32, PARAM_OUT); /* target address */ init_reg_param(®_params[3], "r3", 32, PARAM_OUT); /* count (halfword-16bit) */ init_reg_param(®_params[4], "r4", 32, PARAM_OUT); /* page size */ init_reg_param(®_params[5], "r5", 32, PARAM_OUT); /* qspi base address */ buf_set_u32(reg_params[0].value, 0, 32, fifo->address); buf_set_u32(reg_params[1].value, 0, 32, fifo->address + fifo->size); buf_set_u32(reg_params[2].value, 0, 32, offset); buf_set_u32(reg_params[3].value, 0, 32, count); buf_set_u32(reg_params[4].value, 0, 32, page_size); buf_set_u32(reg_params[5].value, 0, 32, (uint32_t) mrvlqspi_info->reg_base); retval = target_run_flash_async_algorithm(target, buffer, count, 1, 0, NULL, 6, reg_params, fifo->address, fifo->size, write_algorithm->address, 0, &armv7m_info ); if (retval != ERROR_OK) LOG_ERROR("Error executing flash write algorithm"); target_free_working_area(target, fifo); target_free_working_area(target, write_algorithm); destroy_reg_param(®_params[0]); destroy_reg_param(®_params[1]); destroy_reg_param(®_params[2]); destroy_reg_param(®_params[3]); destroy_reg_param(®_params[4]); destroy_reg_param(®_params[5]); return retval; } int mrvlqspi_flash_read(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) { struct target *target = bank->target; struct mrvlqspi_flash_bank *mrvlqspi_info = bank->driver_priv; int retval; uint32_t i; if (target->state != TARGET_HALTED) { LOG_ERROR("Target not halted"); return ERROR_TARGET_NOT_HALTED; } if (!(mrvlqspi_info->probed)) { LOG_ERROR("Flash bank not probed"); return ERROR_FLASH_BANK_NOT_PROBED; } /* Flush read/write fifo's */ retval = mrvlqspi_fifo_flush(bank, FIFO_FLUSH_TIMEOUT); if (retval != ERROR_OK) return retval; /* Set instruction/addr count value */ retval = mrvlqspi_set_hdr_cnt(bank, (0x1 | (0x3 << 4))); if (retval != ERROR_OK) return retval; /* Set count for number of bytes to read */ retval = mrvlqspi_set_din_cnt(bank, count); if (retval != ERROR_OK) return retval; /* Set read address */ retval = mrvlqspi_set_addr(bank, offset); if (retval != ERROR_OK) return retval; /* Set instruction */ retval = mrvlqspi_set_instr(bank, SPIFLASH_READ); if (retval != ERROR_OK) return retval; /* Set data and addr pin length */ retval = mrvlqspi_set_conf(bank, 0x0); if (retval != ERROR_OK) return retval; retval = mrvlqspi_start_transfer(bank, QSPI_R_EN); if (retval != ERROR_OK) return retval; for (i = 0; i < count; i++) { retval = mrvlqspi_read_byte(bank, &buffer[i]); if (retval != ERROR_OK) return retval; } retval = mrvlqspi_set_ss_state(bank, QSPI_SS_DISABLE, QSPI_TIMEOUT); if (retval != ERROR_OK) return retval; return ERROR_OK; } static int mrvlqspi_probe(struct flash_bank *bank) { struct target *target = bank->target; struct mrvlqspi_flash_bank *mrvlqspi_info = bank->driver_priv; uint32_t id = 0; int retval; struct flash_sector *sectors; /* If we've already probed, we should be fine to skip this time. */ if (mrvlqspi_info->probed) return ERROR_OK; if (target->state != TARGET_HALTED) { LOG_ERROR("Target not halted"); return ERROR_TARGET_NOT_HALTED; } mrvlqspi_info->probed = 0; mrvlqspi_info->bank_num = bank->bank_number; /* Read flash JEDEC ID */ retval = mrvlqspi_read_id(bank, &id); if (retval != ERROR_OK) return retval; mrvlqspi_info->dev = NULL; for (const struct flash_device *p = flash_devices; p->name ; p++) if (p->device_id == id) { mrvlqspi_info->dev = p; break; } if (!mrvlqspi_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, mrvlqspi_info->dev->name, mrvlqspi_info->dev->device_id); /* Set correct size value */ bank->size = mrvlqspi_info->dev->size_in_bytes; /* create and fill sectors array */ bank->num_sectors = mrvlqspi_info->dev->size_in_bytes / mrvlqspi_info->dev->sectorsize; sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); if (sectors == NULL) { LOG_ERROR("not enough memory"); return ERROR_FAIL; } for (int sector = 0; sector < bank->num_sectors; sector++) { sectors[sector].offset = sector * mrvlqspi_info->dev->sectorsize; sectors[sector].size = mrvlqspi_info->dev->sectorsize; sectors[sector].is_erased = -1; sectors[sector].is_protected = 0; } bank->sectors = sectors; mrvlqspi_info->probed = 1; return ERROR_OK; } static int mrvlqspi_auto_probe(struct flash_bank *bank) { struct mrvlqspi_flash_bank *mrvlqspi_info = bank->driver_priv; if (mrvlqspi_info->probed) return ERROR_OK; return mrvlqspi_probe(bank); } static int mrvlqspi_flash_erase_check(struct flash_bank *bank) { /* Not implemented yet */ return ERROR_OK; } int mrvlqspi_get_info(struct flash_bank *bank, char *buf, int buf_size) { struct mrvlqspi_flash_bank *mrvlqspi_info = bank->driver_priv; if (!(mrvlqspi_info->probed)) { snprintf(buf, buf_size, "\nQSPI flash bank not probed yet\n"); return ERROR_OK; } snprintf(buf, buf_size, "\nQSPI flash information:\n" " Device \'%s\' ID 0x%08" PRIx32 "\n", mrvlqspi_info->dev->name, mrvlqspi_info->dev->device_id); return ERROR_OK; } FLASH_BANK_COMMAND_HANDLER(mrvlqspi_flash_bank_command) { struct mrvlqspi_flash_bank *mrvlqspi_info; if (CMD_ARGC < 7) return ERROR_COMMAND_SYNTAX_ERROR; mrvlqspi_info = malloc(sizeof(struct mrvlqspi_flash_bank)); if (mrvlqspi_info == NULL) { LOG_ERROR("not enough memory"); return ERROR_FAIL; } /* Get QSPI controller register map base address */ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[6], mrvlqspi_info->reg_base); bank->driver_priv = mrvlqspi_info; mrvlqspi_info->probed = 0; return ERROR_OK; } struct flash_driver mrvlqspi_flash = { .name = "mrvlqspi", .flash_bank_command = mrvlqspi_flash_bank_command, .erase = mrvlqspi_flash_erase, .write = mrvlqspi_flash_write, .read = mrvlqspi_flash_read, .probe = mrvlqspi_probe, .auto_probe = mrvlqspi_auto_probe, .erase_check = mrvlqspi_flash_erase_check, .info = mrvlqspi_get_info, .free_driver_priv = default_flash_free_driver_priv, };