This is an automated email from Gerrit.

"Name of user not set <prbac...@gmail.com>" just uploaded a new patch set to 
Gerrit, which you can find at https://review.openocd.org/c/openocd/+/6885

-- gerrit

commit e9e0416802d6ab84cafefa9d29d43a0937e02245
Author: Paul Bachek <prbac...@gmail.com>
Date:   Mon Mar 28 21:59:28 2022 -0400

    flash/nand: Add PL350 NAND Controller Driver
    
    Add new NAND driver to support the PL350 SMC found on Zynq-7000 devices
    Add Winbond NAND device manufacturer ID
    
    Change-Id: I348d5546206537ab2ce835aaa2bcae5378728af4
    Signed-off-by: Paul Bachek <prbac...@gmail.com>

diff --git a/src/flash/nand/Makefile.am b/src/flash/nand/Makefile.am
index abe90f8bb8..8f589e25c8 100644
--- a/src/flash/nand/Makefile.am
+++ b/src/flash/nand/Makefile.am
@@ -26,7 +26,8 @@ NAND_DRIVERS = \
        %D%/s3c2443.c \
        %D%/s3c6400.c \
        %D%/at91sam9.c \
-       %D%/nuc910.c
+       %D%/nuc910.c \
+       %D%/pl350.c
 
 NANDHEADERS = \
        %D%/arm_io.h \
diff --git a/src/flash/nand/arm_io.c b/src/flash/nand/arm_io.c
index 2b0c081bdf..3a79f82581 100644
--- a/src/flash/nand/arm_io.c
+++ b/src/flash/nand/arm_io.c
@@ -164,9 +164,9 @@ int arm_nandwrite(struct arm_nand_data *nand, uint8_t 
*data, int size)
                return retval;
 
        /* set up parameters */
-       init_reg_param(&reg_params[0], "r0", 32, PARAM_IN);
-       init_reg_param(&reg_params[1], "r1", 32, PARAM_IN);
-       init_reg_param(&reg_params[2], "r2", 32, PARAM_IN);
+       init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
+       init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
+       init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);
 
        buf_set_u32(reg_params[0].value, 0, 32, nand->data);
        buf_set_u32(reg_params[1].value, 0, 32, target_buf);
@@ -270,9 +270,9 @@ int arm_nandread(struct arm_nand_data *nand, uint8_t *data, 
uint32_t size)
        target_buf = nand->copy_area->address + target_code_size;
 
        /* set up parameters */
-       init_reg_param(&reg_params[0], "r0", 32, PARAM_IN);
-       init_reg_param(&reg_params[1], "r1", 32, PARAM_IN);
-       init_reg_param(&reg_params[2], "r2", 32, PARAM_IN);
+       init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
+       init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
+       init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);
 
        buf_set_u32(reg_params[0].value, 0, 32, target_buf);
        buf_set_u32(reg_params[1].value, 0, 32, nand->data);
diff --git a/src/flash/nand/core.c b/src/flash/nand/core.c
index c1f1bc4b8b..0245ab1d58 100644
--- a/src/flash/nand/core.c
+++ b/src/flash/nand/core.c
@@ -147,6 +147,7 @@ static struct nand_manufacturer nand_manuf_ids[] = {
        {NAND_MFR_STMICRO, "ST Micro"},
        {NAND_MFR_HYNIX, "Hynix"},
        {NAND_MFR_MICRON, "Micron"},
+       {NAND_MFR_WINBOND, "Winbond"},
        {0x0, NULL},
 };
 
diff --git a/src/flash/nand/core.h b/src/flash/nand/core.h
index 12fc2b7681..f342bb30ff 100644
--- a/src/flash/nand/core.h
+++ b/src/flash/nand/core.h
@@ -83,6 +83,7 @@ enum {
        NAND_MFR_STMICRO = 0x20,
        NAND_MFR_HYNIX = 0xad,
        NAND_MFR_MICRON = 0x2c,
+       NAND_MFR_WINBOND = 0xef,
 };
 
 struct nand_manufacturer {
diff --git a/src/flash/nand/driver.c b/src/flash/nand/driver.c
index b525f3d0a3..631fa549d4 100644
--- a/src/flash/nand/driver.c
+++ b/src/flash/nand/driver.c
@@ -40,6 +40,7 @@ extern struct nand_flash_controller mxc_nand_flash_controller;
 extern struct nand_flash_controller imx31_nand_flash_controller;
 extern struct nand_flash_controller at91sam9_nand_controller;
 extern struct nand_flash_controller nuc910_nand_controller;
+extern struct nand_flash_controller pl350_nand_controller;
 
 /* extern struct nand_flash_controller boundary_scan_nand_controller; */
 
@@ -58,6 +59,7 @@ static struct nand_flash_controller *nand_flash_controllers[] 
= {
        &imx31_nand_flash_controller,
        &at91sam9_nand_controller,
        &nuc910_nand_controller,
+       &pl350_nand_controller,
 /*     &boundary_scan_nand_controller, */
        NULL
 };
diff --git a/src/flash/nand/pl350.c b/src/flash/nand/pl350.c
new file mode 100644
index 0000000000..f81aaed055
--- /dev/null
+++ b/src/flash/nand/pl350.c
@@ -0,0 +1,895 @@
+/*
+ * Copyright (C) 2022 by Paul Bachek
+ * prbac...@gmail.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <target/target.h>
+#include <helper/log.h>
+#include "imp.h"
+#include "arm_io.h"
+
+#define AXI_COMMAND_PHASE           (0 << 19)
+#define AXI_DATA_PHASE              (1 << 19)
+
+#define CMD_TYPE_UPDATE_REGS_AXI    (0 << 21)
+#define CMD_TYPE_MODE_REG           (1 << 21)
+#define CMD_TYPE_UPDATE_REGS        (2 << 21)
+#define CMD_TYPE_BOTH               (3 << 21)
+
+#define SMC_STATE_NOT_READY         (1 << 0)
+
+#define ECC_STATUS_BUSY             (1 << 6)
+#define ECC_PAGE_SIZE_0x512         (0 << 0)
+#define ECC_PAGE_SIZE_1x512         (1 << 0)
+#define ECC_PAGE_SIZE_2x512         (2 << 0)
+#define ECC_PAGE_SIZE_4x512         (3 << 0)
+#define ECC_MODE_BYPASSED           (0 << 2)
+#define ECC_MODE_CALC_ONLY          (1 << 2)
+#define ECC_MODE_CALC_RDWR          (2 << 2)
+#define ECC_READ_END_BLOCK          (0 << 4)
+#define ECC_READ_END_PAGE           (1 << 4)
+#define ECC_JUMP_NO                 (0 << 5)
+#define ECC_JUMP_COL_CMD            (1 << 5)
+#define ECC_JUMP_FULL_CMD           (2 << 5)
+#define ECC_IGNORE_ADD_EIGHT        (1 << 7)
+#define ECC_EXTRA_BLOCK             (1 << 10)
+#define ECC_EXTRA_BLOCK_SIZE_4B     (0 << 11)
+#define ECC_EXTRA_BLOCK_SIZE_8B     (1 << 11)
+#define ECC_EXTRA_BLOCK_SIZE_16B    (2 << 11)
+#define ECC_EXTRA_BLOCK_SIZE_32B    (3 << 11)
+
+// PL350 Register map
+enum {
+       REG_OFFSET_MEMC_STATUS          = 0x000,
+       REG_OFFSET_MEMIF_CFG            = 0x004,
+       REG_OFFSET_MEMIF_CFG_SET        = 0x008,
+       REG_OFFSET_MEMIF_CFG_CLR        = 0x00C,
+       REG_OFFSET_DIRECT_CMD           = 0x010,
+       REG_OFFSET_SET_CYCLES           = 0x014,
+       REG_OFFSET_SET_OPMODE           = 0x018,
+       REG_OFFSET_REFRESH_PERIOD_0     = 0x020,
+       REG_OFFSET_REFRESH_PERIOD_1     = 0x024,
+       REG_OFFSET_CHIP_CFG_IF0_C0      = 0x100,
+       REG_OFFSET_CHIP_CFG_IF0_C1      = 0x120,
+       REG_OFFSET_CHIP_CFG_IF0_C2      = 0x140,
+       REG_OFFSET_CHIP_CFG_IF0_C3      = 0x160,
+       REG_OFFSET_CHIP_CFG_IF1_C0      = 0x180,
+       REG_OFFSET_CHIP_CFG_IF1_C1      = 0x1A0,
+       REG_OFFSET_CHIP_CFG_IF1_C2      = 0x1C0,
+       REG_OFFSET_CHIP_CFG_IF1_C3      = 0x1E0,
+       REG_OFFSET_USR_STATUS           = 0x200,
+       REG_OFFSET_USR_CFG              = 0x204,
+       REG_OFFSET_ECC_IF0              = 0x300,
+       REG_OFFSET_ECC_IF1              = 0x400,
+       REG_OFFSET_INTEGRATION_TEST     = 0xE00,
+       REG_OFFSET_PERIPH_ID0           = 0xFE0,
+       REG_OFFSET_PERIPH_ID1           = 0xFE4,
+       REG_OFFSET_PERIPH_ID2           = 0xFE8,
+       REG_OFFSET_PERIPH_ID3           = 0xFEC,
+       REG_OFFSET_PCELL_ID0            = 0xFF0,
+       REG_OFFSET_PCELL_ID1            = 0xFF4,
+       REG_OFFSET_PCELL_ID2            = 0xFF8,
+       REG_OFFSET_PCELL_ID3            = 0xFFC,
+};
+
+// Config register offsets per chip select
+enum {
+       REG_OFFSET_NAND_CYCLES          = 0x00,
+       REG_OFFSET_OPMODE               = 0x04,
+};
+
+// ECC register offsets per interface
+enum {
+       REG_OFFSET_ECC_STATUS           = 0x00,
+       REG_OFFSET_ECC_MEMCFG           = 0x04,
+       REG_OFFSET_ECC_MEMCMD1          = 0x08,
+       REG_OFFSET_ECC_MEMCMD2          = 0x0C,
+       REG_OFFSET_ECC_ADDR0            = 0x10,
+       REG_OFFSET_ECC_ADDR1            = 0x14,
+       REG_OFFSET_ECC_VAL0             = 0x18,
+       REG_OFFSET_ECC_VAL1             = 0x1C,
+       REG_OFFSET_ECC_VAL2             = 0x20,
+       REG_OFFSET_ECC_VAL3             = 0x24,
+       REG_OFFSET_ECC_VAL4             = 0x28,
+};
+
+// Private data
+struct pl350_priv {
+       // Base addresses of buses
+       uint32_t axi_addr;
+       uint32_t apb_addr;
+
+       // Inteface/Chip Select #
+       uint8_t if_cs;
+
+       // Buffer command cycle operations
+       uint8_t cmd_buf;
+       uint8_t cmd_buf_valid;
+       uint8_t addr_buf[7];
+       uint8_t addr_buf_cnt;
+
+       // Info for ARM hosted operations
+       struct arm_nand_data io;
+};
+
+/*
+ * Handle the initial NAND device command
+ */
+NAND_DEVICE_COMMAND_HANDLER(pl350_device_command)
+{
+       uint32_t axi_addr = 0, apb_addr = 0;
+       uint8_t intf, cs;
+       struct pl350_priv *priv;
+       int retval = ERROR_OK;
+
+       LOG_DEBUG("PL350 NAND Device Command");
+
+       if (CMD_ARGC != 4 && CMD_ARGC != 6) {
+               command_print(CMD,
+                       "parameters: mem_base_addr "
+                       "config_base_addr [interface(0-1) chip_select(0-3)]");
+               retval = ERROR_COMMAND_ARGUMENT_INVALID;
+       }
+
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], axi_addr);
+       if (axi_addr == 0) {
+               command_print(CMD, "invalid mem_base_addr: %s", CMD_ARGV[2]);
+               retval = ERROR_COMMAND_ARGUMENT_INVALID;
+       }
+
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[3], apb_addr);
+       if (apb_addr == 0) {
+               command_print(CMD, "invalid config_base_addr: %s", CMD_ARGV[3]);
+               retval = ERROR_COMMAND_ARGUMENT_INVALID;
+       }
+
+       if (CMD_ARGC == 6) {
+               COMMAND_PARSE_NUMBER(u8, CMD_ARGV[4], intf);
+               if (intf > 1) {
+                       command_print(CMD, "invalid interface(0-1): %s", 
CMD_ARGV[4]);
+                       retval = ERROR_COMMAND_ARGUMENT_INVALID;
+               }
+       
+               COMMAND_PARSE_NUMBER(u8, CMD_ARGV[5], cs);
+               if (cs > 3) {
+                       command_print(CMD, "invalid chip_select(0-3): %s", 
CMD_ARGV[5]);
+                       retval = ERROR_COMMAND_ARGUMENT_INVALID;
+               }
+       } else {
+               intf = -1;
+               cs = -1;
+       }
+
+       priv = calloc(1, sizeof(*priv));
+       if (!priv) {
+               command_print(CMD,
+                       "Unable to allocate space for controller private data");
+               retval = ERROR_COMMAND_ARGUMENT_INVALID;
+       }
+
+       if (retval == ERROR_OK) {
+               priv->axi_addr = axi_addr;
+               priv->apb_addr = apb_addr;
+               priv->if_cs = (intf << 2) | cs;
+               priv->cmd_buf_valid = 0;
+               priv->addr_buf_cnt = 0;
+       
+               priv->io.target = nand->target;
+               priv->io.op = ARM_NAND_NONE;
+       
+               nand->controller_priv = priv;
+       }
+
+       return retval;
+}
+
+/*
+ * Sets the timing parameters for the memory device
+ */
+COMMAND_HANDLER(handle_pl350_set_cycles)
+{
+       struct nand_device *nand;
+       struct pl350_priv *priv;
+       uint8_t cycles[7];
+       uint32_t cycles_val, nand_num;
+       int retval = ERROR_OK;
+
+       if (CMD_ARGC != 8) {
+               command_print(CMD, "parameters: bank_id tRC tWC tREA tWP tCLR 
tAR tRR");
+               retval = ERROR_COMMAND_ARGUMENT_INVALID;
+       }
+
+       if (retval == ERROR_OK) {
+               COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], nand_num);
+               nand = get_nand_device_by_num(nand_num);
+               if (!nand) {
+                       command_print(CMD, "invalid nand device number: %s", 
CMD_ARGV[0]);
+                       retval = ERROR_COMMAND_ARGUMENT_INVALID;
+               } else {
+                       priv = nand->controller_priv;
+               }
+       }
+
+       if (retval == ERROR_OK && priv->if_cs > 7) {
+               command_print(CMD,
+                       "nand device must have interface and chip_select 
parameters");
+               retval = ERROR_COMMAND_ARGUMENT_INVALID;
+       }
+
+       if (retval == ERROR_OK) {
+               for (uint8_t i = 0; i < 7; i++) {
+                       COMMAND_PARSE_NUMBER(u8, CMD_ARGV[i + 1], cycles[i]);
+               }
+       
+               cycles_val = (cycles[6] & 0xF) << 20 | \
+                                        (cycles[5] & 0x7) << 17 | \
+                                        (cycles[4] & 0x7) << 14 | \
+                                        (cycles[3] & 0x7) << 11 | \
+                                        (cycles[2] & 0x7) << 8  | \
+                                        (cycles[1] & 0xF) << 4  | \
+                                        (cycles[0] & 0xF) << 0;
+                               
+               retval = target_write_u32(nand->target,
+                       priv->apb_addr | REG_OFFSET_SET_CYCLES,
+                       cycles_val);
+       }
+
+       if (retval == ERROR_OK) {
+               retval = target_write_u32(nand->target,
+                       priv->apb_addr | REG_OFFSET_DIRECT_CMD,
+                       priv->if_cs << 23 | CMD_TYPE_UPDATE_REGS);
+       }
+
+       return retval;
+}
+
+/*
+ * Sets the interface bus width to 8 or 16 bits
+ */
+COMMAND_HANDLER(handle_pl350_set_bus_width)
+{
+       struct nand_device *nand;
+       struct pl350_priv *priv;
+       uint8_t opmode_val;
+       uint32_t nand_num;
+       int retval = ERROR_OK;
+
+       if (CMD_ARGC != 2) {
+               command_print(CMD, "parameters: bank_id width (8,16)");
+               retval = ERROR_COMMAND_ARGUMENT_INVALID;
+       }
+
+       if (retval == ERROR_OK) {
+               COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], nand_num);
+               nand = get_nand_device_by_num(nand_num);
+               if (!nand) {
+                       command_print(CMD, "invalid nand device number: %s", 
CMD_ARGV[0]);
+                       retval = ERROR_COMMAND_ARGUMENT_INVALID;
+               } else {
+                       priv = nand->controller_priv;
+               }
+       }
+
+       if (retval == ERROR_OK && priv->if_cs > 7) {
+               command_print(CMD,
+                       "nand device must have interface and chip_select 
parameters");
+               retval = ERROR_COMMAND_ARGUMENT_INVALID;
+       }
+
+       if (retval == ERROR_OK) {
+               COMMAND_PARSE_NUMBER(u8, CMD_ARGV[1], opmode_val);
+               if (opmode_val != 8 && opmode_val != 16) {
+                       command_print(CMD, "Invalid bus width (8,16)");
+                       retval = ERROR_COMMAND_ARGUMENT_INVALID;
+               }
+       }
+
+       if (retval == ERROR_OK) {
+               retval = target_write_u32(nand->target,
+                       priv->apb_addr | REG_OFFSET_SET_OPMODE,
+                       opmode_val >> 4);
+       }
+
+       if (retval == ERROR_OK) {
+               retval = target_write_u32(nand->target,
+                       priv->apb_addr | REG_OFFSET_DIRECT_CMD,
+                       priv->if_cs << 23 | CMD_TYPE_UPDATE_REGS);
+       }
+
+       return retval;
+}
+
+/*
+ * Sub-Command handlers
+ */
+static const struct command_registration pl350_sub_command_registration[] = {
+       {
+               .name = "set_cycles",
+               .handler = handle_pl350_set_cycles,
+               .mode = COMMAND_EXEC,
+               .help = "Sets the timing parameters for the memory device",
+               .usage = "parameters: bank_id tRC tWC tREA tWP tCLR tAR tRR",
+       },
+       {
+               .name = "set_bus_width",
+               .handler = handle_pl350_set_bus_width,
+               .mode = COMMAND_EXEC,
+               .help = "Sets the interface bus width to 8 or 16 bits",
+               .usage = "parameters: bank_id width (8,16)",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+/*
+ * Command handlers
+ */
+static const struct command_registration pl350_command_registration[] = {
+       {
+               .name = "pl350",
+               .handler = NULL,
+               .mode = COMMAND_ANY,
+               .help = "PL350 NAND flash controller",
+               .usage = "parameters: mem_base_addr "
+                       "config_base_addr [interface(0-1) chip_select(0-3)]",
+               .chain = pl350_sub_command_registration,
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+/*
+ * Initialize NAND controller
+ */
+static int pl350_init(struct nand_device *nand)
+{
+       int retval = ERROR_OK;
+
+       if (nand->target->state != TARGET_HALTED) {
+               LOG_ERROR("Target must be halted to use NAND controller");
+               retval = ERROR_NAND_OPERATION_FAILED;
+       }
+
+       return retval;
+}
+
+/*
+ * Reset NAND controller
+ */
+static int pl350_reset(struct nand_device *nand)
+{
+       // No software reset capability
+       return ERROR_OK;
+}
+/*
+ * Send a command and all stored address bytes from the buffer to
+ * the memory device
+ */
+static int flush_cmd_buf(struct nand_device *nand)
+{
+       struct pl350_priv *priv;
+       int retval = ERROR_OK;
+
+       priv = nand->controller_priv;
+
+       if (priv->cmd_buf_valid) {
+               uint32_t addr;
+               uint8_t i = 0;
+       
+               addr = priv->axi_addr | priv->addr_buf_cnt << 21 | 
AXI_COMMAND_PHASE | priv->cmd_buf << 3;
+               do {
+                       retval = target_write_u8(nand->target, addr + i, 
priv->addr_buf[i]);
+               } while ((++i < priv->addr_buf_cnt) && (retval == ERROR_OK));
+               priv->cmd_buf_valid = 0;
+               priv->addr_buf_cnt = 0;
+       }
+
+       return retval;
+}
+/*
+ * Send a single command byte to the memory device
+ * The PL35x does not support sending single address bytes
+ * so commands are buffered until they can be sent together with
+ * the accompanying address bytes.
+ */
+static int pl350_command(struct nand_device *nand, uint8_t command)
+{
+       struct pl350_priv *priv;
+       int retval = ERROR_OK;
+       
+       priv = nand->controller_priv;
+
+       if (retval == ERROR_OK) {
+               // Flush the command/address buffer
+               retval = flush_cmd_buf(nand);
+       }
+       
+       if (retval == ERROR_OK) {
+               uint32_t addr;
+       
+               // Do not buffer reset command
+               if (command == NAND_CMD_RESET){
+                       addr = priv->axi_addr | AXI_COMMAND_PHASE | command << 
3;
+                       retval = target_write_u8(nand->target, addr, 0);
+               } else {
+                       priv->cmd_buf = command;
+                       priv->cmd_buf_valid = 1;
+               }
+       }
+
+       return retval;
+}
+
+/*
+ * Send a single address byte to the memory device
+ * The PL35x does not support sending single address bytes
+ * so they are buffered until they can all be sent together
+ * with the accompanying command.
+ */
+static int pl350_address(struct nand_device *nand, uint8_t address)
+{
+       struct pl350_priv *priv;
+       int retval = ERROR_OK;
+
+       priv = nand->controller_priv;
+
+       // Check address buffer count
+       if (priv->addr_buf_cnt >= 7) {
+               LOG_ERROR("Too many addresses sent");
+               retval = ERROR_NAND_OPERATION_FAILED;
+       }
+       if (retval == ERROR_OK) {
+               // Write to address buffer
+               priv->addr_buf[priv->addr_buf_cnt++] = address;
+       }
+
+       return retval;
+}
+
+/*
+ * Write a byte of data to memory device
+ */
+static int pl350_write_data(struct nand_device *nand, uint16_t data)
+{
+       struct pl350_priv *priv;
+       uint32_t addr;
+       int retval = ERROR_OK;
+
+       priv = nand->controller_priv;
+
+       addr = priv->axi_addr | AXI_DATA_PHASE;
+
+       if (retval == ERROR_OK)
+               // Flush the command/address buffer
+               retval = flush_cmd_buf(nand);
+       
+       if (retval == ERROR_OK)
+               // Write a byte of data to external memory
+               retval = target_write_u8(nand->target, addr, data);
+
+       return retval;
+}
+
+/*
+ * Read a byte of data from memory device
+ */
+static int pl350_read_data(struct nand_device *nand, void *data)
+{
+       struct pl350_priv *priv;
+       uint32_t addr;
+       int retval = ERROR_OK;
+
+       priv = nand->controller_priv;
+
+       addr = priv->axi_addr | AXI_DATA_PHASE;
+
+       if (retval == ERROR_OK)
+               // Flush the command/address buffer
+               retval = flush_cmd_buf(nand);
+       
+       if (retval == ERROR_OK)
+               // Read a byte of data from external memory
+               retval = target_read_u8(nand->target, addr, data);
+
+       return retval;
+}
+
+/*
+ * Write a block of data to memory device
+ */
+static int pl350_write_block_data(struct nand_device *nand, uint8_t *data, int 
size)
+{
+       struct pl350_priv *priv;
+       struct arm_nand_data *io;
+       uint32_t addr;
+       int retval = ERROR_OK;
+
+       priv = nand->controller_priv;
+       io = &priv->io;
+
+       addr = priv->axi_addr | AXI_DATA_PHASE;
+
+       if (retval == ERROR_OK)
+               // Flush the command/address buffer
+               retval = flush_cmd_buf(nand);
+
+       io->chunk_size = nand->page_size;
+       io->data = addr;
+
+       if (retval == ERROR_OK)
+               // Execute hosted external memory write
+               retval = arm_nandwrite(io, data, size);
+
+       return retval;
+}
+
+/*
+ * Read a block of data from memory device
+ */
+static int pl350_read_block_data(struct nand_device *nand, uint8_t *data, int 
size)
+{
+       struct pl350_priv *priv;
+       struct arm_nand_data *io;
+       uint32_t addr;
+       int retval = ERROR_OK;
+
+       priv = nand->controller_priv;
+       io = &priv->io;
+
+       addr = priv->axi_addr | AXI_DATA_PHASE;
+
+       if (retval == ERROR_OK)
+               // Flush the command/address buffer
+               retval = flush_cmd_buf(nand);
+
+       io->chunk_size = nand->page_size;
+       io->data = addr;
+
+       if (retval == ERROR_OK)
+               // Execute hosted external memory read
+               retval = arm_nandread(io, data, size);
+
+       return retval;
+}
+
+/*
+ * Calculate ECC Hamming code for a data buffer of size bytes
+ */
+static uint32_t calc_ecc(uint8_t *data, uint32_t size)
+{
+       uint32_t odd, even, numDataBits, numParityBits;
+
+       numDataBits = size * 8;
+       numParityBits = sizeof(numDataBits) * CHAR_BIT - 
__builtin_clz(numDataBits) - 1;
+
+       odd = 0;
+       even = 0;
+
+       // Iterate over all input bits
+       for (uint32_t i = 0; i < numDataBits; i++) {
+               // If data bit is set, XOR select bits in ECC
+               if ((data[i/8] >> i%8) & 1) {
+                       odd ^= i;
+                       even ^= ~i;
+               }
+       }
+
+       // Mask out extra upper bits
+       odd &= (numDataBits - 1);
+       even &= (numDataBits - 1);
+
+       return (even << numParityBits) | odd;
+}
+
+/*
+ * Check ECC Hamming codes and attempt to correct a data buffer of size bytes
+ */
+static int check_ecc(uint8_t *data, uint32_t size, uint32_t ecc_mem, uint32_t 
ecc_calc)
+{
+       uint32_t ecc_xor, numDataBits, numParityBits;
+       uint8_t ecc_numOnes;
+       int retval;
+
+       numDataBits = size * 8;
+       numParityBits = sizeof(uint32_t) * CHAR_BIT - 
__builtin_clz(numDataBits) - 1;
+
+       ecc_xor = ecc_calc ^ ecc_mem;
+       ecc_numOnes = __builtin_popcount(ecc_xor);
+
+       if (ecc_numOnes == 0) {
+               // No errors
+               retval = 0;
+       } else if (ecc_numOnes == numParityBits) {
+               // Single correctable data error
+               data[(ecc_xor >> 3) & (size - 1)] ^= (1 << (ecc_xor & 0x7));
+               retval = 1;
+       } else if (ecc_numOnes == 1) {
+               // Single ECC code error
+               retval = 2;
+       } else {
+               // Multiple errors
+               retval = -1;
+       }
+
+       return retval;
+}
+
+/*
+ * Write a page to memory and attempt to detect data errors
+ */
+static int pl350_write_page(struct nand_device *nand, uint32_t page,
+       uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size)
+{
+       struct pl350_priv *priv;
+       uint8_t *oob_buf, ecc_page_size, num_512_blocks;
+       uint32_t ecc_sw, ecc_hw;
+       int retval = ERROR_OK;
+
+       priv = nand->controller_priv;
+
+       // Set parameters for different page size devices
+       num_512_blocks = nand->page_size / 512;
+       if (!oob_size)
+               oob_size = num_512_blocks * 16;
+       if (num_512_blocks == 1)
+               ecc_page_size = ECC_PAGE_SIZE_1x512;
+       else if (num_512_blocks == 2)
+               ecc_page_size = ECC_PAGE_SIZE_2x512;
+       else if (num_512_blocks == 4)
+               ecc_page_size = ECC_PAGE_SIZE_4x512;
+       else
+               retval = ERROR_NAND_OPERATION_FAILED;
+
+       if (retval == ERROR_OK) {
+               // Set up ECC module
+               retval = target_write_u32(nand->target,
+                       priv->apb_addr | REG_OFFSET_ECC_IF1 | 
REG_OFFSET_ECC_MEMCFG,
+                       ECC_JUMP_FULL_CMD | ECC_READ_END_PAGE | 
ECC_MODE_CALC_ONLY | ecc_page_size);
+       }
+
+       oob_buf = oob;
+       if (retval == ERROR_OK) {
+               // Allocate OOB if buffer isn't provided
+               if (!oob_buf) {
+                       oob_buf = malloc(oob_size);
+                       if (!oob_buf) {
+                               LOG_ERROR("Could not allocate memory for OOB");
+                               retval = ERROR_NAND_OPERATION_FAILED;
+                       }
+               }
+       }
+
+       // Calculate ECC codes for each 512 byte block
+       for (uint8_t i = 0; data && i < num_512_blocks; i++){
+               if (retval == ERROR_OK) {
+                       // Calculate software ECC code
+                       ecc_sw = calc_ecc(&data[i*512], 512);
+                       // Invert ECC code to write to flash (UG585 11.2.4)
+                       ecc_sw ^= 0x00FFFFFF;
+                       // Set OOB buffer
+                       target_buffer_set_u24(nand->target,
+                               &oob_buf[oob_size + 3*(i - num_512_blocks)],
+                               ecc_sw);
+               }
+       }
+
+       if (retval == ERROR_OK) {
+               // Write page to memory
+               retval = nand_write_page_raw(nand, page, data, data_size, 
oob_buf, oob_size);
+       }
+
+       if (retval == ERROR_OK) {
+               uint32_t status;
+       
+               // Poll ECC module busy
+               do {
+                       // Read status register
+                       retval = target_read_u32(nand->target,
+                               priv->apb_addr | REG_OFFSET_ECC_IF1 | 
REG_OFFSET_ECC_STATUS,
+                               &status);
+               } while ((status & ECC_STATUS_BUSY) && (retval == ERROR_OK));
+       }
+
+       // Check ECC codes for each 512 byte block
+       for (uint8_t i = 0; data && i < num_512_blocks; i++){
+               if (retval == ERROR_OK) {
+                       // Read ECC module calculated code
+                       retval = target_read_u32(nand->target,
+                               (priv->apb_addr | REG_OFFSET_ECC_IF1 | 
REG_OFFSET_ECC_VAL0) + i*4,
+                               &ecc_hw);
+                       ecc_hw &= 0x00FFFFFF;
+               }
+               if (retval == ERROR_OK) {
+                       // Get previously software calculated ECC code
+                       ecc_sw = target_buffer_get_u24(nand->target,
+                               &oob_buf[oob_size + 3*(i - num_512_blocks)]);
+                       // Uninvert ECC code
+                       ecc_sw ^= 0x00FFFFFF;
+       
+                       // Compare software and hardware calculated ECC
+                       if (ecc_hw != ecc_sw) {
+                               LOG_ERROR("SW/HW ECC mismatch, page write 
failed");
+                               retval = ERROR_NAND_OPERATION_FAILED;
+                       }
+               }
+       }
+
+       // Deallocate OOB buffer if caller doesn't want it
+       if (!oob)
+               free(oob_buf);
+
+       return retval;
+}
+
+/*
+ * Read a page from memory and attempt to detect/correct data errors
+ */
+static int pl350_read_page(struct nand_device *nand, uint32_t page,
+       uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size)
+{
+       struct pl350_priv *priv;
+       uint8_t *oob_buf, ecc_page_size, num_512_blocks, retry;
+       uint32_t ecc_sw, ecc_hw, ecc_mem;
+       int retval = ERROR_OK;
+
+       priv = nand->controller_priv;
+
+       // Set parameters for different page size devices
+       num_512_blocks = nand->page_size / 512;
+       if (!oob_size)
+               oob_size = num_512_blocks * 16;
+       if (num_512_blocks == 1)
+               ecc_page_size = ECC_PAGE_SIZE_1x512;
+       else if (num_512_blocks == 2)
+               ecc_page_size = ECC_PAGE_SIZE_2x512;
+       else if (num_512_blocks == 4)
+               ecc_page_size = ECC_PAGE_SIZE_4x512;
+       else
+               retval = ERROR_NAND_OPERATION_FAILED;
+
+       if (retval == ERROR_OK) {
+               // Set up ECC module
+               retval = target_write_u32(nand->target,
+                       priv->apb_addr | REG_OFFSET_ECC_IF1 | 
REG_OFFSET_ECC_MEMCFG,
+                       ECC_JUMP_FULL_CMD | ECC_READ_END_PAGE | 
ECC_MODE_CALC_ONLY | ecc_page_size);
+       }
+
+       oob_buf = oob;
+       if (retval == ERROR_OK) {
+               // Allocate OOB if buffer isn't provided
+               if (!oob_buf) {
+                       oob_buf = malloc(oob_size);
+                       if (!oob_buf) {
+                               LOG_ERROR("Could not allocate memory for OOB");
+                               retval = ERROR_NAND_OPERATION_FAILED;
+                       }
+               }
+       }
+
+       if (retval == ERROR_OK) {
+               // Read page from memory
+               retval = nand_read_page_raw(nand, page, data, data_size, 
oob_buf, oob_size);
+       }
+
+       if (retval == ERROR_OK) {
+               uint32_t status;
+       
+               // Poll ECC module busy
+               do {
+                       // Read status register
+                       retval = target_read_u32(nand->target,
+                               priv->apb_addr | REG_OFFSET_ECC_IF1 | 
REG_OFFSET_ECC_STATUS,
+                               &status);
+               } while ((status & ECC_STATUS_BUSY) && (retval == ERROR_OK));
+       }
+
+       // Check ECC codes for each 512 byte block
+       for (uint8_t i = 0; data && i < num_512_blocks; i++){
+               if (retval == ERROR_OK) {
+                       // Read ECC module calculated code
+                       retval = target_read_u32(nand->target,
+                               (priv->apb_addr | REG_OFFSET_ECC_IF1 | 
REG_OFFSET_ECC_VAL0) + i*4,
+                               &ecc_hw);
+                       ecc_hw &= 0x00FFFFFF;
+               }
+               if (retval == ERROR_OK) {
+                       // Calculate software ECC code
+                       ecc_sw = calc_ecc(&data[i*512], 512);
+                       // Get ECC code from memory
+                       ecc_mem = target_buffer_get_u24(nand->target,
+                               &oob_buf[oob_size + 3*(i - num_512_blocks)]);
+                       // Uninvert ECC code stored in flash (UG585 11.2.4)
+                       ecc_mem ^= 0x00FFFFFF;
+       
+                       // Compare software and hardware calculated ECC
+                       retry = 0;
+                       if (ecc_hw != ecc_sw) {
+                               LOG_ERROR("SW/HW ECC mismatch, retrying page 
read");
+                               retry = 1;
+                               break;
+                       }
+               
+                       // Check parity values
+                       switch (check_ecc(&data[i*512], 512, ecc_mem, ecc_hw)) {
+                       case 1: LOG_INFO("ECC single error corrected");
+                               break;
+                       case 2: LOG_INFO("ECC code error detected");
+                               break;
+                       case -1: LOG_ERROR("ECC multiple errors detected");
+                               break;
+                       }
+               }
+       }
+
+       // Deallocate OOB buffer if caller doesn't want it
+       if (!oob)
+               free(oob_buf);
+
+       // Retry page read if communication error detected
+       if (retry)
+               retval = pl350_read_page(nand, page, data, data_size, oob, 
oob_size);
+
+       return retval;
+}
+
+/*
+ * Check if NAND controller is ready
+ */
+static int pl350_nand_ready(struct nand_device *nand, int timeout)
+{
+       struct pl350_priv *priv;
+       uint32_t status;
+       int retval = ERROR_OK;
+
+       priv = nand->controller_priv;
+
+       // Read memory controller status
+       retval = target_read_u32(nand->target,
+               priv->apb_addr | REG_OFFSET_MEMC_STATUS,
+               &status);
+
+       if (retval == ERROR_OK) {
+               // Check if memory controller is ready
+               if (status & SMC_STATE_NOT_READY) {
+                       retval = ERROR_NAND_OPERATION_FAILED;
+               }
+       }
+
+       return (retval == ERROR_OK);
+}
+
+/*
+ * NAND Flash Controller structure to register the driver
+ */
+struct nand_flash_controller pl350_nand_controller = {
+       .name = "pl350",
+       .usage = "pl350 static memory controller",
+       .commands = pl350_command_registration,
+       .nand_device_command = pl350_device_command,
+       .init = pl350_init,
+       .reset = pl350_reset,
+       .command = pl350_command,
+       .address = pl350_address,
+       .write_data = pl350_write_data,
+       .read_data = pl350_read_data,
+       .write_block_data = pl350_write_block_data,
+       .read_block_data = pl350_read_block_data,
+       .write_page = pl350_write_page,
+       .read_page = pl350_read_page,
+       .nand_ready = pl350_nand_ready,
+};
\ No newline at end of file

-- 

Reply via email to