This is an automated email from Gerrit.

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

-- gerrit

commit 774c70dd58d5b69f3c4a2bbcabb8bbb297ac06ab
Author: Esben Haabendal <[email protected]>
Date:   Mon Sep 4 09:19:13 2017 +0200

    fslqspi: Implement support for Freescale/NXP QuadSPI controller
    
    Add support for Freescale/NXP chips with QuadSPI module (controller).
    Tested with LS1021A.
    
    Change-Id: I155151de0e04e7a6acc1704893d64096b59c1cb6
    Signed-off-by: Esben Haabendal <[email protected]>

diff --git a/README b/README
index f2d704b..4a174ef 100644
--- a/README
+++ b/README
@@ -124,7 +124,7 @@ Flash drivers
 -------------
 
 ADUC702x, AT91SAM, ATH79, AVR, CFI, DSP5680xx, EFM32, EM357, FM3, FM4, Kinetis,
-LPC8xx/LPC1xxx/LPC2xxx/LPC541xx, LPC2900, LPCSPIFI, Marvell QSPI,
+LPC8xx/LPC1xxx/LPC2xxx/LPC541xx, LPC2900, LPCSPIFI, LS1021A QSPI, Marvell QSPI,
 Milandr, NIIET, NuMicro, PIC32mx, PSoC4, SiM3x, Stellaris, STM32, STMSMI,
 STR7x, STR9x, nRF51; NAND controllers of AT91SAM9, LPC3180, LPC32xx,
 i.MX31, MXC, NUC910, Orion/Kirkwood, S3C24xx, S3C6400, XMC1xxx, XMC4xxx.
diff --git a/doc/openocd.texi b/doc/openocd.texi
index 89ee5eb..6295496 100644
--- a/doc/openocd.texi
+++ b/doc/openocd.texi
@@ -5042,6 +5042,28 @@ flash bank flash2 ath79 0x20000000 0 0 0 $_TARGETNAME cs2
 
 @end deffn
 
+@deffn {Flash Driver} fslqspi
+@cindex Freescale QuadSPI driver
+@cindex fslqspi
+This driver supports the QuadSPI flash controller of various
+Freescale/NXP/Qualcomm devices.  Currently only tested on LS1021A.
+
+There controller supports 4 chip selects (A1, A2, B1, B2).  When
+declaring flash banks, they must be declared in correct order. A1
+before A2, A2 before B1, B1 before B2.
+
+The @var{base} parameter should match. For A1 @var{base} should be
+QSPI_AMBA_BASE.  For the other chip selects, the @var{base} should not
+leave a hole in the memory map.  See example below.
+
+@example
+flash bank flash0 fslqspi 0x40000000 0x400000 0 0 $_TARGETNAME a1
+flash bank flash0 fslqspi 0x40400000 0x400000 0 0 $_TARGETNAME a2
+flash bank flash0 fslqspi 0x40800000 0x400000 0 0 $_TARGETNAME b1
+@end example
+
+@end deffn
+
 @subsection Internal Flash (Microcontrollers)
 
 @deffn {Flash Driver} aduc702x
diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am
index 5a992fe..5124641 100644
--- a/src/flash/nor/Makefile.am
+++ b/src/flash/nor/Makefile.am
@@ -25,6 +25,7 @@ NOR_DRIVERS = \
        %D%/faux.c \
        %D%/fm3.c \
        %D%/fm4.c \
+       %D%/fslqspi.c \
        %D%/jtagspi.c \
        %D%/kinetis.c \
        %D%/kinetis_ke.c \
diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c
index 4ad1d92..7672c66 100644
--- a/src/flash/nor/drivers.c
+++ b/src/flash/nor/drivers.c
@@ -38,6 +38,7 @@ extern struct flash_driver em357_flash;
 extern struct flash_driver faux_flash;
 extern struct flash_driver fm3_flash;
 extern struct flash_driver fm4_flash;
+extern struct flash_driver fslqspi_flash;
 extern struct flash_driver jtagspi_flash;
 extern struct flash_driver kinetis_flash;
 extern struct flash_driver kinetis_ke_flash;
@@ -91,6 +92,7 @@ static struct flash_driver *flash_drivers[] = {
        &faux_flash,
        &fm3_flash,
        &fm4_flash,
+       &fslqspi_flash,
        &jtagspi_flash,
        &kinetis_flash,
        &kinetis_ke_flash,
diff --git a/src/flash/nor/fslqspi.c b/src/flash/nor/fslqspi.c
new file mode 100644
index 0000000..28fb49f
--- /dev/null
+++ b/src/flash/nor/fslqspi.c
@@ -0,0 +1,1076 @@
+/***************************************************************************
+ *   Copyright (C) 2017 DEIF A/S                                           *
+ *   by Esben Haabendal <[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.                                        *
+ *                                                                         *
+ ***************************************************************************/
+/*
+ * Freescale Quad Serial Peripheral Interface (QSPI) driver
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "imp.h"
+#include "spi.h"
+#include <helper/time_support.h>
+#include <helper/types.h>
+#include <jtag/jtag.h>
+
+struct fslqspi_target {
+       char *name;
+       uint32_t tap_idcode;
+       uint32_t amba_base;
+       uint32_t amba_size;
+       uint32_t reg_base;
+       unsigned txbuf_len; /* Length of TX buffer (bytes) */
+};
+
+static struct fslqspi_target target_devices[] = {
+       /* name,         tap_idcode, amba_base,  amba_size,  reg_base, 
txbuf_len */
+       { "LS1021A/SAP", 0x16b0001d, 0x40000000, 0x20000000, 0x01550000, 64 },
+       { "LS1021A/DAP", 0x5ba00477, 0x40000000, 0x20000000, 0x01550000, 64 },
+       { NULL,          0,          0,          0,          0,          0 }
+};
+
+enum fslqspi_chipselect {
+       PCSFA1,
+       PCSFA2,
+       PCSFB1,
+       PCSFB2
+};
+
+enum fslqspi_endianness {
+       BE64 = 0,
+       LE32 = 1,
+       BE32 = 2,
+       LE64 = 3
+};
+
+struct fslqspi_flash_bank {
+       enum fslqspi_chipselect chipselect;
+       int probed;
+       struct fslqspi_target *target_device;
+       const struct flash_device *dev;
+       enum fslqspi_endianness endianness;
+};
+
+static inline bool is_little_endian(struct flash_bank *bank)
+{
+       struct fslqspi_flash_bank *fslqspi_info = bank->driver_priv;
+       /* LE32 & LE64 */
+       return (fslqspi_info->endianness & 0x1) == 1;
+}
+
+static inline bool need_word_swap(struct flash_bank *bank)
+{
+       struct fslqspi_flash_bank *fslqspi_info = bank->driver_priv;
+       /* BE32 & LE64 */
+       return (fslqspi_info->endianness & 0x2) == 2;
+}
+
+/* Flash command timeouts (in ms) */
+#define CMD_TIMEOUT    100
+#define ERASE_TIMEOUT  3000
+#define PROGRAM_TIMEOUT        100
+#define TXB_TIMEOUT    100
+
+/* Register addresses (offset from register base) */
+#define FSLQSPI_MCR    0x0000
+#define FSLQSPI_IPCR   0x0008
+#define FSLQSPI_FLSHCR 0x000c
+#define FSLQSPI_BUFnCR 0x0010
+#define FSLQSPI_BFGENCR        0x0020
+#define FSLQSPI_BUFnIND        0x0030
+#define FSLQSPI_SFAR   0x0100
+#define FSLQSPI_SMPR   0x0108
+#define FSLQSPI_RBSR   0x010c
+#define FSLQSPI_RBCT   0x0110
+#define FSLQSPI_TBSR   0x0150
+#define FSLQSPI_TBDR   0x0154
+#define FSLQSPI_SR     0x015c
+#define FSLQSPI_FR     0x0160
+#define FSLQSPI_RSER   0x0164
+#define FSLQSPI_SPNDST 0x0168
+#define FSLQSPI_SPTRCLR        0x016c
+#define FSLQSPI_SFA1AD 0x0180
+#define FSLQSPI_SFA2AD 0x0184
+#define FSLQSPI_SFB1AD 0x0188
+#define FSLQSPI_SFB2AD 0x018c
+#define FSLQSPI_RBDRn  0x0200
+#define FSLQSPI_LUTKEY 0x0300
+#define FSLQSPI_LCKCR  0x0304
+#define FSLQSPI_LUTn   0x0310
+
+/* Module configuration register bits */
+#define FSLQSPI_MCR_CFG_MASK   0xffff000c
+#define FSLQSPI_MCR_MDIS       0x00004000
+#define FSLQSPI_MCR_CLR_TXF    0x00000800
+#define FSLQSPI_MCR_CLR_RXF    0x00000400
+#define FSLQSPI_MCR_END_CFG(mcr) ((mcr & 0xc) >> 2)
+#define FSLQSPI_MCR_END_64BE   0x00000000
+#define FSLQSPI_MCR_END_32LE   0x00000004
+#define FSLQSPI_MCR_END_32BE   0x00000008
+#define FSLQSPI_MCR_END_64LE   0x0000000c
+
+/* IP configuration register */
+#define FSLQSPI_IPCR_VALUE(seqid, paren, idatsz) \
+       (((seqid & 0xf) << 24) | ((!!paren) << 16) | (idatsz & 0xffff))
+
+/* RX buffer status register */
+#define FSLQSPI_RBSR_RDCTR(rbsr) (rbsr >> 16)
+#define FSLQSPI_RBSR_RDBFL(rbsr) ((rbsr & 0x00003f00) >> 8)
+
+/* RX buffer control register */
+#define FSLQSPI_RBCT_RXBRD     0x00000100
+#define FSLQSPI_RBCT_WMRK(wmrk)        ((wmrk) & 0x1f)
+
+/* TX buffer status register */
+#define FSLQSPI_TBSR_TRCTR(tbsr) (tbsr >> 16)
+#define FSLQSPI_TBSR_TRBFL(tbsr) ((tbsr & 0x00001f00) >> 8)
+
+/* Status register bits */
+#define FSLQSPI_SR_TXFULL      0x08000000
+#define FSLQSPI_SR_TXNE                0x01000000
+#define FSLQSPI_SR_RXDMA       0x00800000
+#define FSLQSPI_SR_RXFULL      0x00080000
+#define FSLQSPI_SR_RXWE                0x00010000
+#define FSLQSPI_SR_AHBGNT      0x00000020
+#define FSLQSPI_SR_AHB_ACC     0x00000004
+#define FSLQSPI_SR_IP_ACC      0x00000002
+#define FSLQSPI_SR_BUSY                0x00000001
+
+/* Flag register bits */
+#define FSLQSPI_FR_TBFF                0x08000000
+#define FSLQSPI_FR_TBUF                0x04000000
+#define FSLQSPI_FR_ILLINE      0x00800000
+#define FSLQSPI_FR_RBOF                0x00020000
+#define FSLQSPI_FR_RBDF                0x00010000
+#define FSLQSPI_FR_IUEF                0x00001000
+#define FSLQSPI_FR_IPAEF       0x00000080
+#define FSLQSPI_FR_IPIEF       0x00000040
+#define FSLQSPI_FR_IPGEF       0x00000010
+#define FSLQSPI_FR_TFF         0x00000001
+
+/* Sequence pointer clear bits */
+#define FSLQSPI_SPTRCLR_IPPTRC 0x00000100
+#define FSLQSPI_SPTRCLR_BFPTRC 0x00000001
+
+#define FSLQSPI_INSTR(instr, pads, operand) \
+       (((instr & 0x3f) << 10) | ((pads & 0x3) << 8) | (operand & 0xff))
+
+#define STOP_INSTR     0x00
+#define CMD_INSTR      0x01
+#define ADDR_INSTR     0x02
+#define DUMMY_INSTR    0x03
+#define MODE_INSTR     0x04
+#define MODE2_INSTR    0x05
+#define MODE4_INSTR    0x06
+#define READ_INSTR     0x07
+#define WRITE_INSTR    0x08
+#define JMP_ON_CS_INSTR        0x09
+
+struct fslqspi_sequence {
+       char *name;
+       bool prepared;
+       uint16_t instr[8];
+};
+
+enum fslqspi_seqid {
+       AHB_READ_SEQ = 0,
+       READ_ID_SEQ,
+       READ_STATUS_SEQ,
+       READ_CONFIG_SEQ,
+       WREN_SEQ,
+       SECTOR_ERASE_SEQ,
+       PAGE_PROGRAM_SEQ,
+       READ_SEQ,
+};
+
+struct fslqspi_sequence fslqspi_sequences[] = {
+
+       { .name = "AHB read",
+         .instr = {
+                       /* Read Data bytes command on one pad */
+                       FSLQSPI_INSTR(CMD_INSTR, 0, 0x03),
+                       /* Send 24 address bits on one pad */
+                       FSLQSPI_INSTR(ADDR_INSTR, 0, 0x18),
+                       /* Read 64 bits (8 bytes) on one pad */
+                       FSLQSPI_INSTR(READ_INSTR, 0, 0x08),
+                       /* Jump to instruction 0 (CMD) */
+                       FSLQSPI_INSTR(JMP_ON_CS_INSTR, 0, 0x00) },
+       },
+
+       { .name = "RDID (Read Identification)",
+         .instr = {
+                       /* Read Identification command on one pad */
+                       FSLQSPI_INSTR(CMD_INSTR, 0, 0x9f),
+                       /* Read 32 bits (4 bytes) on one pad */
+                       FSLQSPI_INSTR(READ_INSTR, 0, 0x04),
+                       /* Stop execution (deassert CS) */
+                       FSLQSPI_INSTR(STOP_INSTR, 0, 0x00) },
+       },
+
+       { .name = "RDSR (Read Status Register)",
+         .instr = {
+                       /* Read Status Register command on one pad */
+                       FSLQSPI_INSTR(CMD_INSTR, 0, 0x05),
+                       /* Read 8 bits (1 byte) on one pad */
+                       FSLQSPI_INSTR(READ_INSTR, 0, 0x01),
+                       /* Stop execution (deassert CS) */
+                       FSLQSPI_INSTR(STOP_INSTR, 0, 0x00) },
+       },
+
+       { .name = "RCR (Read Configuration Register)",
+         .instr = {
+                       /* Read Configuration Register command on one pad */
+                       FSLQSPI_INSTR(CMD_INSTR, 0, 0x35),
+                       /* Read 8 bits (1 byte) on one pad */
+                       FSLQSPI_INSTR(READ_INSTR, 0, 0x01),
+                       /* Stop execution (deassert CS) */
+                       FSLQSPI_INSTR(STOP_INSTR, 0, 0x00) },
+       },
+
+       { .name = "WREN (Write Enable)",
+         .instr = {
+                       /* Write Enable command on one pad */
+                       FSLQSPI_INSTR(CMD_INSTR, 0, 0x06),
+                       /* Stop execution (deassert CS) */
+                       FSLQSPI_INSTR(STOP_INSTR, 0, 0x00) },
+       },
+
+       { .name = "SE (64 kByte Sector Erase)",
+         .instr = {
+                       /* Sector Erase (64 kB) command on one pad */
+                       FSLQSPI_INSTR(CMD_INSTR, 0, 0xd8),
+                       /* Send 24 address bits on one pad */
+                       FSLQSPI_INSTR(ADDR_INSTR, 0, 0x18),
+                       /* Stop execution (deassert CS) */
+                       FSLQSPI_INSTR(STOP_INSTR, 0, 0x00) },
+       },
+
+       { .name = "PP (Page Programming)",
+         .instr = {
+                       /* Page Programming command on one pad */
+                       FSLQSPI_INSTR(CMD_INSTR, 0, 0x02),
+                       /* Send 24 address bits on one pad */
+                       FSLQSPI_INSTR(ADDR_INSTR, 0, 0x18),
+                       /* Write data bytes on one pad */
+                       FSLQSPI_INSTR(WRITE_INSTR, 0, 0x00),
+                       /* Stop execution (deassert CS) */
+                       FSLQSPI_INSTR(STOP_INSTR, 0, 0x00) },
+       },
+
+       { .name = "READ (Read Data Bytes)",
+         .instr = {
+                       /* Read Data bytes command on one pad */
+                       FSLQSPI_INSTR(CMD_INSTR, 0, 0x03),
+                       /* Send 24 address bits on one pad */
+                       FSLQSPI_INSTR(ADDR_INSTR, 0, 0x18),
+                       /* Read data on one pad */
+                       FSLQSPI_INSTR(READ_INSTR, 0, 0x00),
+                       /* Stop execution (deassert CS) */
+                       FSLQSPI_INSTR(STOP_INSTR, 0, 0x00) },
+       },
+};
+
+FLASH_BANK_COMMAND_HANDLER(fslqspi_flash_bank_command)
+{
+       struct fslqspi_flash_bank *fslqspi_info;
+       enum fslqspi_chipselect chipselect = PCSFA1;
+
+       if (CMD_ARGC < 6 || CMD_ARGC > 7)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       if (CMD_ARGC == 7) {
+               if (strcmp(CMD_ARGV[6], "a1") == 0)
+                       chipselect = PCSFA1;  /* default */
+               else if (strcmp(CMD_ARGV[6], "a2") == 0)
+                       chipselect = PCSFA2;
+               else if (strcmp(CMD_ARGV[6], "b1") == 0)
+                       chipselect = PCSFA2;
+               else if (strcmp(CMD_ARGV[6], "b2") == 0)
+                       chipselect = PCSFA2;
+               else {
+                       LOG_ERROR("Unknown arg: %s", CMD_ARGV[6]);
+                       return ERROR_COMMAND_SYNTAX_ERROR;
+               }
+       }
+
+       fslqspi_info = calloc(1, sizeof(struct fslqspi_flash_bank));
+       if (!fslqspi_info) {
+               LOG_ERROR("not enough memory");
+               return ERROR_FAIL;
+       }
+
+       fslqspi_info->chipselect = chipselect;
+       bank->driver_priv = fslqspi_info;
+
+       return ERROR_OK;
+}
+
+/* Read QSPI controller register */
+static inline int fslqspi_reg_read(struct flash_bank *bank,
+                                  uint32_t offset, uint32_t *value)
+{
+       struct target *target = bank->target;
+       struct fslqspi_flash_bank *fslqspi_info = bank->driver_priv;
+       struct fslqspi_target *target_device = fslqspi_info->target_device;
+
+       return target_read_u32(target, target_device->reg_base + offset,
+                              value);
+}
+
+/* Write QSPI controller register */
+static inline int fslqspi_reg_write(struct flash_bank *bank,
+                                   uint32_t offset, uint32_t value)
+{
+       struct target *target = bank->target;
+       struct fslqspi_flash_bank *fslqspi_info = bank->driver_priv;
+       struct fslqspi_target *target_device = fslqspi_info->target_device;
+
+       return target_write_u32(target, target_device->reg_base + offset,
+                               value);
+}
+
+/* Poll QSPI controller register until mask is matched, or timeout happens */
+static int fslqspi_reg_poll(struct flash_bank *bank,
+                           uint32_t reg, uint32_t *reg_value,
+                           uint32_t mask, uint32_t value,
+                           int timeout,
+                           int timeout_retval, const char *timeout_errmsg)
+{
+       int64_t endtime;
+       uint32_t _reg_value;
+       int retval;
+
+       if (reg_value == NULL)
+               reg_value = &_reg_value;
+
+       endtime = timeval_ms() + timeout;
+       do {
+               retval = fslqspi_reg_read(bank, reg, reg_value);
+               if (retval != ERROR_OK)
+                       return retval;
+               if ((*reg_value & mask) == value)
+                       return ERROR_OK;
+               alive_sleep(1);
+       } while (timeval_ms() < endtime);
+
+       if (timeout_errmsg != NULL)
+               LOG_ERROR("%s", timeout_errmsg);
+       return timeout_retval;
+}
+
+/* Helper macros to avoid numerous `if (retval != ERROR_OK) return retval`
+ * statements */
+
+/* Note: This macro uses the GCC extension "braced-group within expression",
+ * which makes it usable as an r-value */
+#define FSLQSPI_REG_READ(offset) ({                                    \
+       uint32_t __value;                                               \
+       int __retval = fslqspi_reg_read(bank, offset, &__value);        \
+       if (__retval != ERROR_OK) return __retval;                      \
+       __value; })
+
+#define FSLQSPI_REG_WRITE(offset, value) {                             \
+       int __retval = fslqspi_reg_write(bank, offset, value);          \
+       if (__retval != ERROR_OK) return __retval; }
+
+#define FSLQSPI_REG_POLL(r, rv, m, v, to, to_rv, to_em) {              \
+       int __retval = fslqspi_reg_poll(bank, r, rv, m, v, to, to_rv, to_em); \
+       if (__retval != ERROR_OK) return __retval; }
+
+static int clear_rxbuf(struct flash_bank *bank)
+{
+       uint32_t mcr;
+
+       /* Set MCR[CLR_RXF] */
+       mcr = FSLQSPI_REG_READ(FSLQSPI_MCR);
+       mcr &= FSLQSPI_MCR_CFG_MASK;
+       mcr |= FSLQSPI_MCR_CLR_RXF;
+       FSLQSPI_REG_WRITE(FSLQSPI_MCR, mcr);
+
+       /* Wait for RBSR[RDBFL] and RBSR[RDCTR] to reset to 0 */
+       FSLQSPI_REG_POLL(FSLQSPI_RBSR, NULL,
+                        FSLQSPI_RBSR_RDBFL(0x3f) | FSLQSPI_RBSR_RDCTR(0xffff),
+                        0, CMD_TIMEOUT, ERROR_FAIL,
+                        "Timeout while waiting for RBSR reset");
+
+       return ERROR_OK;
+}
+
+static int clear_txbuf(struct flash_bank *bank)
+{
+       uint32_t mcr;
+
+       if (!(FSLQSPI_REG_READ(FSLQSPI_SR) & FSLQSPI_SR_TXNE))
+               /* TX buffer is already empty */
+               return ERROR_OK;
+
+       /* Set MCR[CLR_TXF] */
+       mcr = FSLQSPI_REG_READ(FSLQSPI_MCR);
+       mcr &= FSLQSPI_MCR_CFG_MASK;
+       mcr |= FSLQSPI_MCR_CLR_TXF;
+       FSLQSPI_REG_WRITE(FSLQSPI_MCR, mcr);
+
+       /* Wait for buffer to become empty */
+       FSLQSPI_REG_POLL(FSLQSPI_SR, NULL, FSLQSPI_SR_TXNE, 0,
+                        CMD_TIMEOUT, ERROR_FAIL, "Timeout polling SR[TXNE]");
+
+       return ERROR_OK;
+}
+
+static int write_txbuf(struct flash_bank *bank,
+                      const uint8_t *buf, size_t count)
+{
+       assert(count <= 64);
+
+       while (count) {
+               uint32_t word = 0;
+               unsigned byte, bytes = (count < 4 ? count : 4);
+               if (is_little_endian(bank)) {
+                       for (byte = 0; byte < bytes; byte++)
+                               word |= *(buf++) << (byte * 8);
+               } else {
+                       for (byte = 0; byte < bytes; byte++)
+                               word |= *(buf++) << (24 - (byte * 8));
+               }
+               FSLQSPI_REG_WRITE(FSLQSPI_TBDR, word);
+               count -= bytes;
+       }
+
+       return ERROR_OK;
+}
+
+static int prepare_sequence(struct flash_bank *bank, enum fslqspi_seqid seqid)
+{
+       struct fslqspi_sequence *seq = &fslqspi_sequences[seqid];
+       uint32_t reg_offset, reg_num, reg_val;
+
+       if (seq->prepared)
+               return ERROR_OK;
+
+       /* Unlock LUT (Look-up-table) */
+       FSLQSPI_REG_WRITE(FSLQSPI_LUTKEY, 0x5af05af0);
+       FSLQSPI_REG_WRITE(FSLQSPI_LCKCR, 2);
+
+       /* Write instruction-operand pairs to LUT */
+       reg_offset = FSLQSPI_LUTn + (seqid * 0x10);
+       for (reg_num = 0; reg_num < 4; reg_num++) {
+               /* Pack two instruction-operand pairs for one LUT register,
+                * with the first pair in LSB and second pair in MSB */
+               reg_val = (seq->instr[reg_num * 2]) |
+                       ((seq->instr[(reg_num * 2) + 1]) << 16);
+               FSLQSPI_REG_WRITE(reg_offset + (reg_num * 4), reg_val);
+       }
+
+       return ERROR_OK;
+}
+
+static int check_fr_error_flags(uint32_t fr)
+{
+       if (fr & FSLQSPI_FR_TBUF) {
+               LOG_ERROR("TX buffer underflow (0x%08" PRIx32 ")", fr);
+               return ERROR_FLASH_OPERATION_FAILED;
+       } else if (fr & FSLQSPI_FR_ILLINE) {
+               LOG_ERROR("Illegal instruction (0x%08" PRIx32 ")", fr);
+               return ERROR_FLASH_OPERATION_FAILED;
+       } else if (fr & FSLQSPI_FR_RBOF) {
+               LOG_ERROR("RX buffer overflow (0x%08" PRIx32 ")", fr);
+               return ERROR_FLASH_OPERATION_FAILED;
+       } else if (fr & FSLQSPI_FR_IUEF) {
+               LOG_ERROR("IP command usage error (0x%08" PRIx32 ")", fr);
+               return ERROR_FLASH_OPERATION_FAILED;
+       } else if (fr & FSLQSPI_FR_IPAEF) {
+               LOG_ERROR("IP command trigger during AHB access (0x%08" PRIx32 
")", fr);
+               return ERROR_FLASH_OPERATION_FAILED;
+       } else if (fr & FSLQSPI_FR_IPIEF) {
+               LOG_ERROR("IP command trigger could not be executed (0x%08" 
PRIx32 ")", fr);
+               return ERROR_FLASH_OPERATION_FAILED;
+       } else if (fr & FSLQSPI_FR_IPGEF) {
+               LOG_ERROR("IP command trigger during AHB grant (0x%08" PRIx32 
")", fr);
+               return ERROR_FLASH_OPERATION_FAILED;
+       }
+       return ERROR_OK;
+}
+
+static int start_ip_command(struct flash_bank *bank, enum fslqspi_seqid seqid,
+                           bool par_en, uint16_t idatsz)
+{
+       LOG_DEBUG("IP command: %s", fslqspi_sequences[seqid].name);
+
+       /* Configure IP command and trigger transaction */
+       FSLQSPI_REG_WRITE(FSLQSPI_IPCR,
+                         FSLQSPI_IPCR_VALUE(seqid, par_en, idatsz));
+
+       /* Check error flags indicating trigger failure */
+       return check_fr_error_flags(FSLQSPI_REG_READ(FSLQSPI_FR));
+}
+
+/* Execute IP command sequence that does no data read or write */
+static int spi_command_nodata(struct flash_bank *bank, enum fslqspi_seqid 
seqid,
+                             uint32_t addr, unsigned timeout)
+{
+       int retval;
+       uint32_t fr;
+
+       retval = prepare_sequence(bank, seqid);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Clear various flags */
+       FSLQSPI_REG_WRITE(FSLQSPI_FR, FSLQSPI_FR_TFF |
+                         FSLQSPI_FR_TBUF | FSLQSPI_FR_ILLINE |
+                         FSLQSPI_FR_RBOF | FSLQSPI_FR_IUEF | FSLQSPI_FR_IPAEF |
+                         FSLQSPI_FR_IPIEF | FSLQSPI_FR_IPGEF);
+
+       /* Write address to SFAR register */
+       FSLQSPI_REG_WRITE(FSLQSPI_SFAR, bank->base + addr);
+
+       /* Configure and trigger IP command */
+       retval = start_ip_command(bank, seqid, 0, 0);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Wait for IP command to finish */
+       FSLQSPI_REG_POLL(FSLQSPI_FR, &fr, FSLQSPI_FR_TFF, FSLQSPI_FR_TFF,
+                        timeout, ERROR_FLASH_OPERATION_FAILED,
+                        "Timeout while polling SR[TFF]");
+
+       return check_fr_error_flags(fr);
+}
+
+static int spi_command_read(struct flash_bank *bank,
+                           enum fslqspi_seqid seqid, size_t bytes)
+{
+       int retval;
+       uint32_t fr;
+
+       retval = prepare_sequence(bank, seqid);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Clear RX buffer */
+       retval = clear_rxbuf(bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Clear various flags */
+       FSLQSPI_REG_WRITE(FSLQSPI_FR, FSLQSPI_FR_TFF | FSLQSPI_FR_RBDF |
+                         FSLQSPI_FR_TBUF | FSLQSPI_FR_ILLINE |
+                         FSLQSPI_FR_RBOF | FSLQSPI_FR_IUEF | FSLQSPI_FR_IPAEF |
+                         FSLQSPI_FR_IPIEF | FSLQSPI_FR_IPGEF);
+
+       /* Write flash base address to SFAR register */
+       FSLQSPI_REG_WRITE(FSLQSPI_SFAR, bank->base);
+
+       /* Configure and trigger IP command */
+       retval = start_ip_command(bank, seqid, 0, bytes);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Wait for IP command to finish */
+       FSLQSPI_REG_POLL(FSLQSPI_FR, &fr, FSLQSPI_FR_TFF, FSLQSPI_FR_TFF,
+                        CMD_TIMEOUT, ERROR_FLASH_OPERATION_FAILED,
+                        "Timeout while polling SR[TFF]");
+
+       /* Bail out on error */
+       retval = check_fr_error_flags(fr);
+       if (retval != ERROR_OK)
+               return retval;
+
+       return ERROR_OK;
+}
+
+/* Execute IP command sequence that read a single byte from external flash
+ * chip */
+static int spi_command_read_byte(struct flash_bank *bank,
+                                enum fslqspi_seqid seqid, uint32_t *byte)
+{
+       int retval;
+       uint32_t buf;
+
+       retval = spi_command_read(bank, seqid, 1);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Read byte from RX buffer */
+       buf = FSLQSPI_REG_READ(FSLQSPI_RBDRn);
+       if (is_little_endian(bank))
+               /* in bits 0:7 for little endian byte order*/
+               *byte = buf & 0xFF;
+       else
+               /* in bits 31:24 for big endian byte order */
+               *byte = buf >> 24;
+
+       return ERROR_OK;
+}
+
+static int spi_command_read_word(struct flash_bank *bank,
+                                enum fslqspi_seqid seqid, uint32_t *word)
+{
+       int retval;
+       uint32_t buf;
+
+       retval = spi_command_read(bank, seqid, 4);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Read word from RX buffer */
+       buf = FSLQSPI_REG_READ(FSLQSPI_RBDRn);
+       if (is_little_endian(bank))
+               *word = le_to_h_u32((const uint8_t *)&buf);
+       else
+               *word = be_to_h_u32((const uint8_t *)&buf);
+
+       return ERROR_OK;
+}
+
+/* Execute IP command sequence that write data to external flash chip */
+static int spi_command_write_data(struct flash_bank *bank,
+                                 enum fslqspi_seqid seqid,
+                                 const uint8_t *buf, uint32_t addr,
+                                 size_t count, unsigned timeout)
+{
+       int retval;
+       uint32_t fr;
+
+       retval = prepare_sequence(bank, seqid);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Clear TX buffer */
+       retval = clear_txbuf(bank);
+
+       /* Clear various flags */
+       FSLQSPI_REG_WRITE(FSLQSPI_FR, FSLQSPI_FR_TFF | FSLQSPI_FR_TBFF |
+                         FSLQSPI_FR_TBUF | FSLQSPI_FR_ILLINE |
+                         FSLQSPI_FR_RBOF | FSLQSPI_FR_IUEF | FSLQSPI_FR_IPAEF |
+                         FSLQSPI_FR_IPIEF | FSLQSPI_FR_IPGEF);
+
+       /* Write flash base address to SFAR register */
+       FSLQSPI_REG_WRITE(FSLQSPI_SFAR, bank->base + addr);
+
+       /* Fill TX buffer */
+       retval = write_txbuf(bank, buf, count);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Configure and trigger IP command */
+       retval = start_ip_command(bank, seqid, 0, count);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Wait for IP command to finish */
+       FSLQSPI_REG_POLL(FSLQSPI_FR, &fr, FSLQSPI_FR_TFF, FSLQSPI_FR_TFF,
+                        timeout, ERROR_FLASH_OPERATION_FAILED,
+                        "Timeout while polling SR[TFF]");
+
+       return check_fr_error_flags(fr);
+}
+
+/* Read the status register of the external SPI flash chip */
+static inline int read_status_command(struct flash_bank *bank, uint32_t 
*status)
+{
+       return spi_command_read_byte(bank, READ_STATUS_SEQ, status);
+}
+
+/* Read manufacturer and device ID and length of CFI tables */
+static inline int read_id_command(struct flash_bank *bank, uint32_t *id)
+{
+       return spi_command_read_word(bank, READ_ID_SEQ, id);
+}
+
+/* Execute a page program command */
+static inline int program_command(struct flash_bank *bank, uint32_t offset,
+                                 const uint8_t *buffer, uint32_t count)
+{
+       int retval;
+       int64_t endtime;
+       uint32_t sr;
+
+       LOG_DEBUG("offset=0x%08" PRIx32 " count=0x%08" PRIx32, offset, count);
+
+       retval = spi_command_nodata(bank, WREN_SEQ, 0, CMD_TIMEOUT);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = spi_command_write_data(bank, PAGE_PROGRAM_SEQ,
+                                       buffer, offset, count,
+                                       CMD_TIMEOUT);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Poll external flash for completion of programming operation
+        * (SR[WIP]) */
+       endtime = timeval_ms() + PROGRAM_TIMEOUT;
+       do {
+               retval = read_status_command(bank, &sr);
+               if (retval != ERROR_OK)
+                       return retval;
+               if ((sr & SPIFLASH_BSY_BIT) == 0)
+                       break;
+               alive_sleep(1);
+       } while (timeval_ms() < endtime);
+       if (sr & SPIFLASH_BSY_BIT) {
+               LOG_ERROR("Timeout waiting for program operation");
+               return ERROR_FLASH_OPERATION_FAILED;
+       }
+
+       return ERROR_OK;
+}
+
+/* Execute a sector erase command */
+static inline int erase_command(struct flash_bank *bank, int sector)
+{
+       uint32_t sector_offset = bank->sectors[sector].offset;
+       int retval;
+       int64_t endtime;
+       uint32_t sr;
+
+       retval = spi_command_nodata(bank, WREN_SEQ, 0, CMD_TIMEOUT);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = spi_command_nodata(bank, SECTOR_ERASE_SEQ,
+                                   sector_offset, CMD_TIMEOUT);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Poll external flash for completion of programming operation
+        * (SR[WIP]) */
+       endtime = timeval_ms() + ERASE_TIMEOUT;
+       do {
+               retval = read_status_command(bank, &sr);
+               if (retval != ERROR_OK)
+                       return retval;
+               if ((sr & SPIFLASH_BSY_BIT) == 0)
+                       break;
+               alive_sleep(1);
+       } while (timeval_ms() < endtime);
+       if (sr & SPIFLASH_BSY_BIT) {
+               LOG_ERROR("Timeout waiting for program operation");
+               return ERROR_FLASH_OPERATION_FAILED;
+       }
+
+       return ERROR_OK;
+}
+
+static int fslqspi_erase(struct flash_bank *bank, int first, int last)
+{
+       struct target *target = bank->target;
+       struct fslqspi_flash_bank *fslqspi_info = bank->driver_priv;
+       int retval = ERROR_OK;
+       int sector;
+
+       LOG_DEBUG("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;
+       }
+
+       // TODO: test if auto_probe is called, and if so, remove this block
+       if (!fslqspi_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;
+               }
+       }
+
+       for (sector = first; sector <= last; sector++) {
+               retval = erase_command(bank, sector);
+               if (retval != ERROR_OK)
+                       break;
+               keep_alive();
+       }
+
+       return retval;
+}
+
+static int fslqspi_protect(struct flash_bank *bank, int set,
+                        int first, int last)
+{
+       int sector;
+       for (sector = first; sector <= last; sector++)
+               bank->sectors[sector].is_protected = set;
+       return ERROR_OK;
+}
+
+static int fslqspi_write(struct flash_bank *bank, const uint8_t *buffer,
+                        uint32_t offset, uint32_t count)
+{
+       struct target *target = bank->target;
+       struct fslqspi_flash_bank *fslqspi_info = bank->driver_priv;
+       int sector;
+       uint32_t page_size, page_offset, cmd_count;
+       int retval;
+
+       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 > fslqspi_info->dev->size_in_bytes) {
+               LOG_WARNING("Write pasts end of flash. Extra data discarded.");
+               count = fslqspi_info->dev->size_in_bytes - offset;
+       }
+
+       if (offset % 4 || count % 4)
+               LOG_WARNING("Unaligned (not 32-bit aligned) write: YMMV");
+
+       /* 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? */
+               struct flash_sector *bs = &bank->sectors[sector];
+               if ((offset < (bs->offset + bs->size)) &&
+                   ((offset + count - 1) >= bs->offset) &&
+                   bs->is_protected) {
+                       LOG_ERROR("Flash sector %d protected", sector);
+                       return ERROR_FAIL;
+               }
+       }
+
+       page_size = fslqspi_info->dev->pagesize;
+
+       /* Program one page and not more than the size of the TX buffer at
+        * a time */
+       while (count) {
+               page_offset = offset % page_size;
+               /* Clip at page boundary */
+               if (page_offset + count > page_size)
+                       cmd_count = page_size - page_offset;
+               else
+                       cmd_count = count;
+               if (cmd_count > fslqspi_info->target_device->txbuf_len)
+                       cmd_count = fslqspi_info->target_device->txbuf_len;
+               retval = program_command(bank, offset, buffer, cmd_count);
+               if (retval != ERROR_OK)
+                       return retval;
+               buffer += cmd_count;
+               offset += cmd_count;
+               count -= cmd_count;
+       }
+
+       return ERROR_OK;
+}
+
+/* Return ID of flash device */
+static int read_flash_id(struct flash_bank *bank, uint32_t *id)
+{
+       struct target *target = bank->target;
+       int retval;
+       uint32_t buf;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       /* Flash command: RDID (Read Identification) */
+       retval = read_id_command(bank, &buf);
+       if (retval != ERROR_OK)
+               return retval;
+
+       if (buf == 0xffffffff) {
+               LOG_ERROR("No SPI flash found");
+               return ERROR_FAIL;
+       }
+
+       /* Mask according to what spi.c code expects */
+       *id = buf & 0x00ffffff;
+
+       return ERROR_OK;
+}
+
+static int fslqspi_probe(struct flash_bank *bank)
+{
+       struct target *target = bank->target;
+       struct fslqspi_flash_bank *fslqspi_info = bank->driver_priv;
+       struct flash_sector *sectors;
+       struct fslqspi_target *target_device;
+       int retval;
+       uint32_t mcr, id, top_addr_reg, bottom_addr;
+
+       if (fslqspi_info->probed) {
+               free(bank->sectors);
+               fslqspi_info->probed = 0;
+       }
+
+       for (target_device = target_devices; target_device->name;
+               ++target_device)
+               if (target_device->tap_idcode == target->tap->idcode)
+                       break;
+
+       if (!target_device->name) {
+               LOG_ERROR("Device ID 0x%" PRIx32
+                         " is not known as FSL QSPI capable",
+                         target->tap->idcode);
+               return ERROR_FAIL;
+       }
+
+       fslqspi_info->target_device = target_device;
+
+       /* Enable QSPI module */
+       mcr = FSLQSPI_REG_READ(FSLQSPI_MCR);
+       /* Only keep CFG bits, thus clearing MDIS bit */
+       mcr &= FSLQSPI_MCR_CFG_MASK;
+       FSLQSPI_REG_WRITE(FSLQSPI_MCR, mcr);
+
+       /* Remember endianness configuration */
+       fslqspi_info->endianness = FSLQSPI_MCR_END_CFG(mcr);
+       if (need_word_swap(bank)) {
+               LOG_INFO("MCR[END_CFG]=0x%x requires word swapping for flash 
programming", fslqspi_info->endianness);
+               LOG_WARNING("word swapping not implemented: programming will 
not do the right thing");
+       }
+
+       retval = read_flash_id(bank, &id);
+       if (retval != ERROR_OK)
+               return retval;
+
+       fslqspi_info->dev = NULL;
+       for (const struct flash_device *p = flash_devices; p->name; p++)
+               if (p->device_id == id) {
+                       fslqspi_info->dev = p;
+                       break;
+               }
+
+       if (!fslqspi_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 ")",
+                fslqspi_info->dev->name, fslqspi_info->dev->device_id);
+
+       /* Set correct size value */
+       if (bank->size != fslqspi_info->dev->size_in_bytes) {
+               LOG_WARNING("Bad flash bank size: 0x%" PRIx32 " != 0x%lx",
+                           bank->size, fslqspi_info->dev->size_in_bytes);
+               bank->size = fslqspi_info->dev->size_in_bytes;
+       }
+
+       /* Set top address for flash device */
+       switch (fslqspi_info->chipselect) {
+       case PCSFA1:
+               bottom_addr = target_device->amba_base;
+               top_addr_reg = FSLQSPI_SFA1AD;
+               retval = ERROR_OK;
+               break;
+       case PCSFA2:
+               bottom_addr = FSLQSPI_REG_READ(FSLQSPI_SFA1AD);
+               top_addr_reg = FSLQSPI_SFA2AD;
+               break;
+       case PCSFB1:
+               bottom_addr = FSLQSPI_REG_READ(FSLQSPI_SFA2AD);
+               top_addr_reg = FSLQSPI_SFB1AD;
+               break;
+       case PCSFB2:
+               bottom_addr = FSLQSPI_REG_READ(FSLQSPI_SFB1AD);
+               top_addr_reg = FSLQSPI_SFB2AD;
+               break;
+       }
+       if (retval != ERROR_OK)
+               return retval;
+       if (bank->base != bottom_addr) {
+               LOG_WARNING("Bad flash bank base address: 0x%" PRIx32
+                           " != 0x%" PRIx32, bank->base, bottom_addr);
+               return ERROR_FLASH_BANK_INVALID;
+       }
+       FSLQSPI_REG_WRITE(top_addr_reg, bottom_addr + bank->size);
+
+       /* create and fill sectors array */
+       bank->num_sectors =
+               fslqspi_info->dev->size_in_bytes
+               / fslqspi_info->dev->sectorsize;
+       sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
+       if (!sectors) {
+               LOG_ERROR("not enough memory");
+               return ERROR_FAIL;
+       }
+       for (int sector = 0; sector < bank->num_sectors; sector++) {
+               sectors[sector].offset = sector * fslqspi_info->dev->sectorsize;
+               sectors[sector].size = fslqspi_info->dev->sectorsize;
+               sectors[sector].is_erased = -1;
+               sectors[sector].is_protected = 1;
+       }
+       bank->sectors = sectors;
+
+       fslqspi_info->probed = 1;
+       return ERROR_OK;
+}
+
+static int fslqspi_auto_probe(struct flash_bank *bank)
+{
+       struct fslqspi_flash_bank *fslqspi_info = bank->driver_priv;
+
+       if (fslqspi_info->probed)
+               return ERROR_OK;
+
+       return fslqspi_probe(bank);
+}
+
+static int fslqspi_protect_check(struct flash_bank *bank)
+{
+       /* Not implemented */
+       return ERROR_OK;
+}
+
+static int fslqspi_info(struct flash_bank *bank, char *buf, int buf_size)
+{
+       struct fslqspi_flash_bank *fslqspi_info = bank->driver_priv;
+
+       if (!fslqspi_info->probed) {
+               snprintf(buf, buf_size,
+                        "\nFSL QSPI flash bank not probed yet\n");
+               return ERROR_OK;
+       }
+
+       snprintf(buf, buf_size, "\nFSL QSPI flash information:\n"
+               "  Device \'%s\' (ID 0x%08" PRIx32 ")\n",
+               fslqspi_info->dev->name, fslqspi_info->dev->device_id);
+
+       return ERROR_OK;
+}
+
+struct flash_driver fslqspi_flash = {
+       .name = "fslqspi",
+       .flash_bank_command = fslqspi_flash_bank_command,
+       .erase = fslqspi_erase,
+       .protect = fslqspi_protect,
+       .write = fslqspi_write,
+       .read = default_flash_read,
+       .probe = fslqspi_probe,
+       .auto_probe = fslqspi_auto_probe,
+       .erase_check = default_flash_blank_check,
+       .protect_check = fslqspi_protect_check,
+       .info = fslqspi_info,
+};

-- 

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
OpenOCD-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openocd-devel

Reply via email to