This is an automated email from Gerrit.

Mahavir Jain ([email protected]) just uploaded a new patch set to Gerrit, which 
you can find at http://openocd.zylin.com/2280

-- gerrit

commit 4c13031eb72a38b8b861dc05dec3a013a934406b
Author: Mahavir Jain <[email protected]>
Date:   Thu Sep 4 15:31:16 2014 +0530

    flash/nor: add mrvlqspi flash controller driver
    
    This patch adds support for 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/
    
    Following things have been tested on 88MC200 (Winbond W25Q80BV flash chip):
    1. Flash sector level erase
    2. Flash chip erase
    3. Flash write in normal SPI mode
    4. Flash fill (write and verify) in normal SPI mode
    
    Change-Id: If4414ae3f77ff170b84e426a35b66c44590c5e06
    Signed-off-by: Mahavir Jain <[email protected]>

diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am
index 3168147..bae42fd 100644
--- a/src/flash/nor/Makefile.am
+++ b/src/flash/nor/Makefile.am
@@ -43,7 +43,8 @@ NOR_DRIVERS = \
        kinetis.c \
        mini51.c \
        nuc1x.c \
-       nrf51.c
+       nrf51.c \
+       mrvlqspi.c
 
 noinst_HEADERS = \
        core.h \
diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c
index ed631a3..8959f0c 100644
--- a/src/flash/nor/drivers.c
+++ b/src/flash/nor/drivers.c
@@ -56,6 +56,7 @@ extern struct flash_driver mdr_flash;
 extern struct flash_driver mini51_flash;
 extern struct flash_driver nuc1x_flash;
 extern struct flash_driver nrf51_flash;
+extern struct flash_driver mrvlqspi_flash;
 
 /**
  * The list of built-in flash drivers.
@@ -96,6 +97,7 @@ static struct flash_driver *flash_drivers[] = {
        &mini51_flash,
        &nuc1x_flash,
        &nrf51_flash,
+       &mrvlqspi_flash,
        NULL,
 };
 
diff --git a/src/flash/nor/mrvlqspi.c b/src/flash/nor/mrvlqspi.c
new file mode 100644
index 0000000..a3d8fa3
--- /dev/null
+++ b/src/flash/nor/mrvlqspi.c
@@ -0,0 +1,972 @@
+/***************************************************************************
+ *   Copyright (C) 2014 by Mahavir Jain <[email protected]>                *
+ *                                                                         *
+ *   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
+
+struct qspi_reg {
+       uint32_t cntl; /* QSPI_BASE + 0x0 */
+       uint32_t conf;
+       uint32_t dout;
+       uint32_t din;
+       uint32_t instr;
+       uint32_t addr;
+       uint32_t rdmode;
+       uint32_t hdrcnt;
+       uint32_t dincnt; /* QSPI_BASE + 0x20 */
+};
+
+struct mrvlqspi_flash_bank {
+       int probed;
+       struct qspi_reg *reg_base;
+       uint32_t bank_num;
+       const struct flash_device *dev;
+};
+
+static inline int mrvlqspi_set_din_cnt(struct flash_bank *bank, uint32_t count)
+{
+       struct target *target = bank->target;
+       struct mrvlqspi_flash_bank *mrvlqspi_info = bank->driver_priv;
+
+       return target_write_u32(target,
+                       (uint32_t) &mrvlqspi_info->reg_base->dincnt, count);
+}
+
+static inline int mrvlqspi_set_addr(struct flash_bank *bank, uint32_t addr)
+{
+       struct target *target = bank->target;
+       struct mrvlqspi_flash_bank *mrvlqspi_info = bank->driver_priv;
+
+       return target_write_u32(target,
+                       (uint32_t) &mrvlqspi_info->reg_base->addr, addr);
+}
+
+static inline int mrvlqspi_set_instr(struct flash_bank *bank, uint32_t instr)
+{
+       struct target *target = bank->target;
+       struct mrvlqspi_flash_bank *mrvlqspi_info = bank->driver_priv;
+
+       return target_write_u32(target,
+                       (uint32_t) &mrvlqspi_info->reg_base->instr, instr);
+}
+
+static inline int mrvlqspi_set_hdr_cnt(struct flash_bank *bank, uint32_t 
hdr_cnt)
+{
+       struct target *target = bank->target;
+       struct mrvlqspi_flash_bank *mrvlqspi_info = bank->driver_priv;
+
+       return target_write_u32(target,
+                       (uint32_t) &mrvlqspi_info->reg_base->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;
+       struct mrvlqspi_flash_bank *mrvlqspi_info = bank->driver_priv;
+
+       retval = target_read_u32(target,
+                       (uint32_t) &mrvlqspi_info->reg_base->conf, &regval);
+       if (retval != ERROR_OK)
+               return retval;
+
+       regval &= ~(CONF_MASK << CONF_OFFSET);
+       regval |= (conf_val << CONF_OFFSET);
+
+       return target_write_u32(target,
+                       (uint32_t) &mrvlqspi_info->reg_base->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;
+       struct mrvlqspi_flash_bank *mrvlqspi_info = bank->driver_priv;
+
+       retval = target_read_u32(target,
+                       (uint32_t) &mrvlqspi_info->reg_base->cntl, &regval);
+       if (retval != ERROR_OK)
+               return retval;
+
+       if (state)
+               regval |= SS_EN;
+       else
+               regval &= ~(SS_EN);
+
+       retval = target_write_u32(target,
+                       (uint32_t) &mrvlqspi_info->reg_base->cntl, regval);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* wait for xfer_ready to set */
+       for (;;) {
+               retval = target_read_u32(target,
+                               (uint32_t) &mrvlqspi_info->reg_base->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;
+       struct mrvlqspi_flash_bank *mrvlqspi_info = bank->driver_priv;
+
+       retval = mrvlqspi_set_ss_state(bank, QSPI_SS_ENABLE, QSPI_TIMEOUT);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = target_read_u32(target,
+                       (uint32_t) &mrvlqspi_info->reg_base->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,
+                       (uint32_t) &mrvlqspi_info->reg_base->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;
+       struct mrvlqspi_flash_bank *mrvlqspi_info = bank->driver_priv;
+
+       /* wait for xfer_ready and wfifo_empty to set */
+       for (;;) {
+               retval = target_read_u32(target,
+                               (uint32_t) &mrvlqspi_info->reg_base->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,
+                       (uint32_t) &mrvlqspi_info->reg_base->conf, &regval);
+       if (retval != ERROR_OK)
+               return retval;
+
+       regval |= XFER_STOP;
+
+       retval = target_write_u32(target,
+                       (uint32_t) &mrvlqspi_info->reg_base->conf, regval);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* wait for xfer_start to reset */
+       for (;;) {
+               retval = target_read_u32(target,
+                               (uint32_t) &mrvlqspi_info->reg_base->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;
+       struct mrvlqspi_flash_bank *mrvlqspi_info = bank->driver_priv;
+
+       retval = target_read_u32(target,
+                       (uint32_t) &mrvlqspi_info->reg_base->conf, &val);
+       if (retval != ERROR_OK)
+               return retval;
+
+       val |= FIFO_FLUSH;
+
+       retval = target_write_u32(target,
+                       (uint32_t) &mrvlqspi_info->reg_base->conf, val);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* wait for fifo_flush to clear */
+       for (;;) {
+               retval = target_read_u32(target,
+                               (uint32_t) &mrvlqspi_info->reg_base->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;
+       struct mrvlqspi_flash_bank *mrvlqspi_info = bank->driver_priv;
+
+       /* wait for rfifo_empty to reset */
+       for (;;) {
+               retval = target_read_u32(target,
+                               (uint32_t) &mrvlqspi_info->reg_base->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,
+                       (uint32_t) &mrvlqspi_info->reg_base->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;
+       uint32_t reg_base;
+
+       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], reg_base);
+       mrvlqspi_info->reg_base = (struct qspi_reg *) 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,
+};

-- 

------------------------------------------------------------------------------
Slashdot TV.  
Video for Nerds.  Stuff that matters.
http://tv.slashdot.org/
_______________________________________________
OpenOCD-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openocd-devel

Reply via email to