This is an automated email from Gerrit.

"Peter Lawrence <majbt...@gmail.com>" just uploaded a new patch set to Gerrit, 
which you can find at https://review.openocd.org/c/openocd/+/6956

-- gerrit

commit c540aed97e48f6cc6038c02109882b842a0e330b
Author: Peter Lawrence <majbt...@gmail.com>
Date:   Thu Apr 28 15:48:37 2022 -0500

    flash/nor/eoss3: add Quicklogic EOS S3 flash algorithm support
    
    Signed-off-by: Peter Lawrence <majbt...@gmail.com>
    Change-Id: Ib16110f7a52c5e3d6ebdd26a3a14c3c4f075fe6b

diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am
index a5ef422104..1260d4f8fd 100644
--- a/src/flash/nor/Makefile.am
+++ b/src/flash/nor/Makefile.am
@@ -26,6 +26,7 @@ NOR_DRIVERS = \
        %D%/dsp5680xx_flash.c \
        %D%/efm32.c \
        %D%/em357.c \
+       %D%/eoss3.c \
        %D%/esirisc_flash.c \
        %D%/faux.c \
        %D%/fespi.c \
diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c
index 3e35c0954d..f2525714ac 100644
--- a/src/flash/nor/drivers.c
+++ b/src/flash/nor/drivers.c
@@ -39,6 +39,7 @@ extern const struct flash_driver cfi_flash;
 extern const struct flash_driver dsp5680xx_flash;
 extern const struct flash_driver efm32_flash;
 extern const struct flash_driver em357_flash;
+extern const struct flash_driver eoss3_flash;
 extern const struct flash_driver esirisc_flash;
 extern const struct flash_driver faux_flash;
 extern const struct flash_driver fm3_flash;
@@ -114,6 +115,7 @@ static const struct flash_driver * const flash_drivers[] = {
        &dsp5680xx_flash,
        &efm32_flash,
        &em357_flash,
+       &eoss3_flash,
        &esirisc_flash,
        &faux_flash,
        &fm3_flash,
diff --git a/src/flash/nor/eoss3.c b/src/flash/nor/eoss3.c
new file mode 100644
index 0000000000..88ddcdd9b1
--- /dev/null
+++ b/src/flash/nor/eoss3.c
@@ -0,0 +1,370 @@
+/***************************************************************************
+ *   Copyright (C) 2022 by Peter Lawrence                                  *
+ *                                                                         *
+ *   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 <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+/*
+With the EOS S3, the flash memory is never memory mapped into the target's
+memory space, hence the '.verify' implementation below.  Because it is not
+memory mapped, the de facto OpenOCD "verify" argument to "program" will not
+work.  Instead, consider "flash verify_image" (available via the console).
+
+In "Host Mode", the EOS S3 utilizes an external SPI flash memory; in
+"Companion Mode", the EOS S3 is fed initialization data from another device.
+
+For "Host Mode" the flash contents must be formatted according to the TRM,
+and this image is copied by the 'Configuration Manager' sub-system (when
+GPIO_19 is not configured to allow the debugger) into internal SRAM after a
+hardware or power-on reset.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "imp.h"
+#include "spi.h"
+#include <helper/time_support.h>
+
+#define SPI_MS_BASE 0x40007000
+
+#define CTRLR0 (SPI_MS_BASE + 0x00)
+#define CTRLR0_DFS_8_BIT    (0x7 << 0)
+#define CTRLR0_TMOD_TX      (0x1 << 8)
+#define CTRLR0_TMOD_RX      (0x2 << 8)
+#define CTRLR0_TMOD_EEPROM  (0x3 << 8)
+
+#define CTRLR1 (SPI_MS_BASE + 0x04)
+#define SSIENR (SPI_MS_BASE + 0x08)
+#define SSIENR_SSI_DISABLE  (0x0 << 0)
+#define SSIENR_SSI_EN       (0x1 << 0)
+
+#define SER    (SPI_MS_BASE + 0x10)
+#define SER_SS_0_N_SELECTED (0x1 << 0)
+
+#define BAUDR  (SPI_MS_BASE + 0x14)
+
+#define SR     (SPI_MS_BASE + 0x28)
+#define SR_BUSY             (0x1 << 0)
+#define SR_TFE              (0x1 << 2)
+#define SR_RFNE             (0x1 << 3)
+
+#define IMR    (SPI_MS_BASE + 0x2C)
+#define ICR    (SPI_MS_BASE + 0x48)
+#define DR0    (SPI_MS_BASE + 0x60)
+
+static int spi_wait_until_txfifo_empty(struct target *target)
+{
+       int err = ERROR_OK;
+       for (;;) {
+               uint32_t status;
+               err = target_read_u32(target, SR, &status);
+               if (ERROR_OK != err)
+                       break;
+               if ((status & SR_TFE) && !(status & SR_BUSY))
+                       break;
+       }
+
+       return err;
+}
+
+static int spi_xfer(struct target *target, const uint8_t *tx, uint32_t tx_len, 
uint8_t *rx, uint32_t rx_len)
+{
+       int err = ERROR_OK;
+
+       const uint32_t ctrlr0_mode = CTRLR0_DFS_8_BIT | (0 /* CPOL */ << 7) | 
(0 /* CPHA */ << 6);
+
+       /* disable peripheral */
+       target_write_u32(target, SSIENR, SSIENR_SSI_DISABLE);
+
+       /* clear and disable all interrupts */
+       target_write_u32(target, ICR, 0xFF);
+       target_write_u32(target, IMR, 0);
+
+       target_write_u32(target, BAUDR, 2);
+
+       /* disable slave select */
+       target_write_u32(target, SER, 0);
+
+       /* configure SPI transfer mode */
+       if (rx_len) {
+               target_write_u32(target, CTRLR0, ((tx_len) ? CTRLR0_TMOD_EEPROM 
: CTRLR0_TMOD_RX) | ctrlr0_mode);
+               target_write_u32(target, CTRLR1, rx_len - 1);
+       } else {
+               target_write_u32(target, CTRLR0, CTRLR0_TMOD_TX | ctrlr0_mode);
+       }
+
+       spi_wait_until_txfifo_empty(target);
+       if (ERROR_OK != err)
+               return err;
+
+       /* enable peripheral */
+       target_write_u32(target, SSIENR, SSIENR_SSI_EN);
+
+       /* fill TX FIFO */
+       while (tx_len) {
+               target_write_u32(target, DR0, *tx++);
+               tx_len--;
+       }
+
+       /* enable slave select */
+       target_write_u32(target, SER, SER_SS_0_N_SELECTED);
+
+       spi_wait_until_txfifo_empty(target);
+       if (ERROR_OK != err)
+               return err;
+
+       /* retrieve data from RX FIFO */
+       while (rx_len) {
+               uint32_t status;
+               err = target_read_u32(target, SR, &status);
+               if (ERROR_OK != err)
+                       break;
+               if (status & SR_RFNE) {
+                       err = target_read_u8(target, DR0, rx++);
+                       if (ERROR_OK != err)
+                               break;
+                       rx_len--;
+               }
+       }
+
+       return err;
+}
+
+static int spi_cmd(struct target *target, uint8_t cmd)
+{
+       return spi_xfer(target, &cmd, 1, NULL, 0);
+}
+
+static int spi_wait_for_write(struct target *target, int timeout)
+{
+       int64_t endtime = timeval_ms() + timeout;
+       const uint8_t cmd = SPIFLASH_READ_STATUS;
+       uint8_t status;
+
+       do {
+               int err = spi_xfer(target, &cmd, 1, &status, 1);
+               if (ERROR_OK != err)
+                       return err;
+               if (!(status & SPIFLASH_BSY_BIT))
+                       return ERROR_OK;
+       } while (timeval_ms() < endtime);
+
+       LOG_ERROR("Timeout while polling BSY");
+       return ERROR_FLASH_OPERATION_FAILED;
+}
+
+struct eoss3_flash_bank {
+       bool probed;
+       const struct flash_device *dev;
+};
+
+FLASH_BANK_COMMAND_HANDLER(eoss3_flash_bank_command)
+{
+       struct eoss3_flash_bank *priv;
+       priv = malloc(sizeof(struct eoss3_flash_bank));
+
+       priv->probed = false;
+       bank->driver_priv = priv;
+
+       return ERROR_OK;
+}
+
+static int eoss3_flash_erase(struct flash_bank *bank, unsigned int first, 
unsigned int last)
+{
+       struct eoss3_flash_bank *priv = bank->driver_priv;
+       struct target *target = bank->target;
+       int err = ERROR_OK;
+
+       for (unsigned int index = first; index <= last; index++) {
+               err = spi_cmd(target, SPIFLASH_WRITE_ENABLE);
+               if (ERROR_OK != err)
+                       break;
+
+               uint8_t tx[4];
+               tx[0] = priv->dev->erase_cmd;
+               tx[1] = bank->sectors[index].offset >> 16;
+               tx[2] = bank->sectors[index].offset >> 8;
+               tx[3] = bank->sectors[index].offset >> 0;
+
+               err = spi_xfer(target, tx, sizeof(tx), NULL, 0);
+               if (ERROR_OK != err)
+                       break;
+
+               err = spi_wait_for_write(target, 1000);
+               if (ERROR_OK != err)
+                       break;
+       }
+
+       return err;
+}
+
+static int eoss3_flash_write(struct flash_bank *bank, const uint8_t *buffer, 
uint32_t offset, uint32_t count)
+{
+       struct eoss3_flash_bank *priv = bank->driver_priv;
+       struct target *target = bank->target;
+       uint8_t tx[131 /* max depth of TX FIFO */];
+       const uint32_t page_size = priv->dev->pagesize;
+       const uint32_t max_write = sizeof(tx) - 4;
+       int err = ERROR_OK;
+
+       while (count) {
+               uint32_t chunk_size = count;
+               if (chunk_size > max_write)
+                       chunk_size = max_write;
+               uint32_t remaining_bytes_in_page = page_size - (offset & 
(page_size - 1));
+               if (chunk_size > remaining_bytes_in_page)
+                       chunk_size = remaining_bytes_in_page;
+
+               err = spi_cmd(target, SPIFLASH_WRITE_ENABLE);
+               if (ERROR_OK != err)
+                       break;
+
+               tx[0] = SPIFLASH_PAGE_PROGRAM;
+               tx[1] = offset >> 16;
+               tx[2] = offset >> 8;
+               tx[3] = offset >> 0;
+               memcpy(tx + 4, buffer, chunk_size);
+               err = spi_xfer(target, tx, 4 + chunk_size, NULL, 0);
+               if (ERROR_OK != err)
+                       break;
+
+               err = spi_wait_for_write(target, 500);
+               if (ERROR_OK != err)
+                       break;
+
+               offset += chunk_size; count -= chunk_size; buffer += chunk_size;
+       }
+
+       return err;
+}
+
+static int eoss3_internal_flash_read_verify(struct flash_bank *bank, uint8_t 
*buffer, uint32_t offset, uint32_t count,
+bool verify)
+{
+       struct target *target = bank->target;
+       uint8_t scratchpad[8 /* polled I/O transfers are limited to eight bytes 
*/];
+       int err;
+
+       while (count) {
+               uint32_t chunk_size = count;
+               if (chunk_size > sizeof(scratchpad))
+                       chunk_size = sizeof(scratchpad);
+
+               uint8_t tx[4];
+               tx[0] = SPIFLASH_READ;
+               tx[1] = offset >> 16;
+               tx[2] = offset >> 8;
+               tx[3] = offset >> 0;
+               err = spi_xfer(target, tx, sizeof(tx), (verify) ? scratchpad : 
buffer, chunk_size);
+               if (ERROR_OK != err)
+                       return err;
+               if (verify)
+                       if (memcmp(scratchpad, buffer, chunk_size))
+                               return ERROR_FAIL;
+
+               offset += chunk_size; count -= chunk_size; buffer += chunk_size;
+       }
+
+       return ERROR_OK;
+}
+
+static int eoss3_flash_read(struct flash_bank *bank, uint8_t *buffer, uint32_t 
offset, uint32_t count)
+{
+       return eoss3_internal_flash_read_verify(bank, buffer, offset, count, 
false);
+}
+
+static int eoss3_flash_verify(struct flash_bank *bank, const uint8_t *buffer, 
uint32_t offset, uint32_t count)
+{
+       return eoss3_internal_flash_read_verify(bank, (uint8_t *)buffer, 
offset, count, true);
+}
+
+static int eoss3_flash_probe(struct flash_bank *bank)
+{
+       struct eoss3_flash_bank *priv = bank->driver_priv;
+       struct target *target = bank->target;
+       int err;
+
+       uint8_t cmd[1] = { SPIFLASH_READ_ID };
+       uint8_t resp[4] = { 0, 0, 0, 0 };
+
+       err = spi_xfer(target, cmd, sizeof(cmd), resp, sizeof(resp));
+       if (ERROR_OK != err)
+               return err;
+
+       uint32_t device_id;
+       device_id  = resp[2] << 16;
+       device_id |= resp[1] << 8;
+       device_id |= resp[0] << 0;
+
+       priv->dev = NULL;
+       for (const struct flash_device *p = flash_devices; p->name ; p++)
+               if (p->device_id == device_id) {
+                       priv->dev = p;
+                       break;
+               }
+
+       if (!priv->dev) {
+               LOG_ERROR("Unknown flash device (ID 0x%08" PRIx32 ")", 
device_id);
+               return ERROR_FAIL;
+       }
+
+       LOG_INFO("Found flash device \'%s\' (ID 0x%08" PRIx32 ")",
+               priv->dev->name, priv->dev->device_id);
+
+       bank->write_start_alignment = priv->dev->pagesize;
+       bank->write_end_alignment = priv->dev->pagesize;
+
+       bank->size = priv->dev->size_in_bytes;
+
+       bank->num_sectors = bank->size / priv->dev->sectorsize;
+       bank->sectors = alloc_block_array(0, priv->dev->sectorsize, 
bank->num_sectors);
+
+       if (!bank->sectors)
+               return ERROR_FAIL;
+
+       priv->probed = true;
+
+       return ERROR_OK;
+}
+
+static int eoss3_flash_auto_probe(struct flash_bank *bank)
+{
+       struct eoss3_flash_bank *priv = bank->driver_priv;
+
+       if (priv->probed)
+               return ERROR_OK;
+
+       return eoss3_flash_probe(bank);
+}
+
+static void eoss3_flash_free_driver_priv(struct flash_bank *bank)
+{
+       free(bank->driver_priv);
+       bank->driver_priv = NULL;
+}
+
+struct flash_driver eoss3_flash = {
+       .name = "eoss3",
+       .flash_bank_command = eoss3_flash_bank_command,
+       .erase = eoss3_flash_erase,
+       .write = eoss3_flash_write,
+       .read = eoss3_flash_read,
+       .verify = eoss3_flash_verify,
+       .probe = eoss3_flash_probe,
+       .auto_probe = eoss3_flash_auto_probe,
+       .erase_check = default_flash_blank_check,
+       .free_driver_priv = eoss3_flash_free_driver_priv
+};
diff --git a/tcl/board/quicklogic_quickfeather.cfg 
b/tcl/board/quicklogic_quickfeather.cfg
index b522eff7e4..61083cc46e 100644
--- a/tcl/board/quicklogic_quickfeather.cfg
+++ b/tcl/board/quicklogic_quickfeather.cfg
@@ -2,8 +2,9 @@
 # QuickLogic EOS S3 QuickFeather
 # https://www.quicklogic.com/products/eos-s3/quickfeather-development-kit/
 
-source [find target/eos_s3.cfg]
-
 reset_config srst_only
 
 transport select swd
+
+source [find target/eos_s3.cfg]
+
diff --git a/tcl/target/eos_s3.cfg b/tcl/target/eos_s3.cfg
index 150ef4e31c..c21b006a24 100644
--- a/tcl/target/eos_s3.cfg
+++ b/tcl/target/eos_s3.cfg
@@ -35,6 +35,11 @@ target create $_TARGETNAME cortex_m -dap $_CHIPNAME.dap
 # For now we use SRAM only for software upload
 $_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size 
$_WORKAREASIZE -work-area-backup 0
 
+set _FLASHNAME $_CHIPNAME.flash
+set _FLASHSIZE 0x200000
+set _FLASHBASE 0x00000000
+flash bank $_FLASHNAME eoss3 $_FLASHBASE $_FLASHSIZE 1 32 $_TARGETNAME
+
 adapter speed 4000
 
 if {![using_hla]} {

-- 

Reply via email to