flash/nor: add mrvlqspi flash controller driver
[openocd.git] / src / flash / nor / mrvlqspi.c
diff --git a/src/flash/nor/mrvlqspi.c b/src/flash/nor/mrvlqspi.c
new file mode 100644 (file)
index 0000000..a5cc1ca
--- /dev/null
@@ -0,0 +1,960 @@
+/***************************************************************************
+ *   Copyright (C) 2014 by Mahavir Jain <mjain@marvell.com>                *
+ *                                                                         *
+ *   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, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.           *
+ *                                                                         *
+ ***************************************************************************/
+
+ /*
+  * 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 <helper/binarybuffer.h>
+#include <target/algorithm.h>
+#include <target/armv7m.h>
+
+#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), &regval);
+       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), &regval);
+       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), &regval);
+               if (retval != ERROR_OK)
+                       return retval;
+               LOG_DEBUG("status: 0x%x", 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), &regval);
+       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), &regval);
+               if (retval != ERROR_OK)
+                       return retval;
+               LOG_DEBUG("status: 0x%x", 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), &regval);
+       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), &regval);
+               if (retval != ERROR_OK)
+                       return retval;
+               LOG_DEBUG("status: 0x%x", 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%x", 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%x", 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%x 0x%x 0x%x", 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(&reg_params[0], "r0", 32, PARAM_IN_OUT); /* buffer start, status (out) */
+       init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);    /* buffer end */
+       init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);    /* target address */
+       init_reg_param(&reg_params[3], "r3", 32, PARAM_OUT);    /* count (halfword-16bit) */
+       init_reg_param(&reg_params[4], "r4", 32, PARAM_OUT);    /* page size */
+       init_reg_param(&reg_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(&reg_params[0]);
+       destroy_reg_param(&reg_params[1]);
+       destroy_reg_param(&reg_params[2]);
+       destroy_reg_param(&reg_params[3]);
+       destroy_reg_param(&reg_params[4]);
+       destroy_reg_param(&reg_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%08x", id);
+               return ERROR_FAIL;
+       }
+
+       LOG_INFO("Found flash device \'%s\' ID 0x%08x",
+               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;
+}
+
+static int mrvlqspi_protect_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%08x\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,
+       .protect = NULL,
+       .write = mrvlqspi_flash_write,
+       .read = mrvlqspi_flash_read,
+       .probe = mrvlqspi_probe,
+       .auto_probe = mrvlqspi_auto_probe,
+       .erase_check = mrvlqspi_flash_erase_check,
+       .protect_check = mrvlqspi_protect_check,
+       .info = mrvlqspi_get_info,
+};

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)