This is an automated email from Gerrit.

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

-- gerrit

commit 3cbe4f801ede8247ba8bb577c3e0c6199c9ac04c
Author: Andrew Shelley <andrewjohnshel...@gmail.com>
Date:   Tue Dec 31 20:10:04 2024 +0000

    flash/nor: flash driver for PIC32CZ family
    
    Add new flash driver for internal flash of Microchip PIC32CZ family.
    The CZ family is unrelated to the original PIC32's, it is an ARM
    Cortex 7 based MCU at the higher performance end of the Microchip
    MCUs. Whilst the flash controller is internally not dissimilar to
    the PIC32MX familiy it is sufficiently different to warrant a new
    flash driver implementation.
    
    Change-Id: I84fb1eb33240e1824f2f53771c854a286a01f11a
    Signed-off-by: Andrew Shelley <andrewjohnshel...@gmail.com>

diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am
index afa11e7d40..bb74eacc53 100644
--- a/src/flash/nor/Makefile.am
+++ b/src/flash/nor/Makefile.am
@@ -51,6 +51,7 @@ NOR_DRIVERS = \
        %D%/nrf5.c \
        %D%/numicro.c \
        %D%/ocl.c \
+       %D%/pic32cz.c \
        %D%/pic32mx.c \
        %D%/psoc4.c \
        %D%/psoc5lp.c \
diff --git a/src/flash/nor/driver.h b/src/flash/nor/driver.h
index 211661e214..13e9a14b2b 100644
--- a/src/flash/nor/driver.h
+++ b/src/flash/nor/driver.h
@@ -279,6 +279,7 @@ extern const struct flash_driver nrf51_flash;
 extern const struct flash_driver nrf5_flash;
 extern const struct flash_driver numicro_flash;
 extern const struct flash_driver ocl_flash;
+extern const struct flash_driver pic32cz_flash;
 extern const struct flash_driver pic32mx_flash;
 extern const struct flash_driver psoc4_flash;
 extern const struct flash_driver psoc5lp_eeprom_flash;
diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c
index dd9995ecba..70bccb7d18 100644
--- a/src/flash/nor/drivers.c
+++ b/src/flash/nor/drivers.c
@@ -56,6 +56,7 @@ static const struct flash_driver * const flash_drivers[] = {
        &nrf51_flash,
        &numicro_flash,
        &ocl_flash,
+       &pic32cz_flash,
        &pic32mx_flash,
        &psoc4_flash,
        &psoc5lp_flash,
diff --git a/src/flash/nor/pic32cz.c b/src/flash/nor/pic32cz.c
new file mode 100644
index 0000000000..cd020ac9a8
--- /dev/null
+++ b/src/flash/nor/pic32cz.c
@@ -0,0 +1,346 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/***************************************************************************
+ *   Copyright (C) 2025 by Andrew Shelley                                  *
+ *   andrewjohnshel...@gmail.com                                           *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <jtag/jtag.h>
+#include "imp.h"
+
+/* PIC32CZ DSU Registers */
+#define PIC32CZ_DSU ((uint32_t)0x44000000)
+#define PIC32CZ_DSU_DID (PIC32CZ_DSU + 0x0120)
+
+/* PIC32CZ NVM Registers */
+#define PIC32CZ_NVM ((uint32_t)0x44002000)
+#define PIC32CZ_NVM_CTRLA (PIC32CZ_NVM + 0x0000)
+#define PIC32CZ_NVM_MUTEX (PIC32CZ_NVM + 0x0008)
+#define PIC32CZ_NVM_STATUS (PIC32CZ_NVM + 0x0018)
+#define PIC32CZ_NVM_KEY (PIC32CZ_NVM + 0x001C)
+#define PIC32CZ_NVM_ADDR (PIC32CZ_NVM + 0x0020)
+#define PIC32CZ_NVM_DATA0 (PIC32CZ_NVM + 0x0028 + 0x00)
+#define PIC32CZ_NVM_DATA1 (PIC32CZ_NVM + 0x0028 + 0x04)
+
+/**
+ * PIC32CZ NVMOP Codes
+ * Default enabling the pre-program bit as recommended by Microchip, it might 
be prudent
+ * to make this a user option in the future.
+ */
+#define PIC32CZ_NVMOP_ERASE_PFM (0x80 | 0x07)
+#define PIC32CZ_NVMOP_ERASE_PAGE (0x80 | 0x04)
+#define PIC32CZ_NVMOP_WRITE_ROW (0x80 | 0x03)
+#define PIC32CZ_NVMOP_WRITE_QUAD (0x80 | 0x02)
+#define PIC32CZ_NVMOP_WRITE_SINGLE (0x80 | 0x01)
+
+/**
+ * PIC32CZ Timeouts
+ * The datasheet is not the best on flash timings but from what I can deduce 
the erase
+ * operations are 20ms and the write operations in the order of 1ms max. So 
the timeouts
+ * are going to be as follows to allow some slop.
+ */
+#define PIC32CZ_TIMEOUT_ERASE 100
+#define PIC32CZ_TIMEOUT_WRITE 10
+
+/* All products share the same base addresses for the boot-flash, not to be
+ confused with the boot-rom which is an immutable block of built in 
bootloader. It is
+ 128KBytes on all products split into two 64K banks, buts lets not run before 
we can
+ walk, one bank for now. */
+#define FLASH_BANK_BFM_BASE 0x08000000
+#define FLASH_BANK_BFM_SIZE (2 * 64 * 1024)
+#define FLASH_BANK_BFM_PAGE (4 * 1024)
+
+/* All products share the same base address to the programme flash, but the 
sizes
+ vary between 1 and 8MBytes and will need to be deduced from the device ID. */
+#define FLASH_BANK_PFM_BASE 0x0C000000
+#define FLASH_BANK_PFM_PAGE (4 * 1024)
+
+/* Canned list of all the PIC32CZ chips with the bits of config we're going to 
need */
+struct pic32cz_chip_info {
+       uint8_t devsel;
+       uint8_t flashsize;
+       const char *name;
+};
+static const struct pic32cz_chip_info pic32cz_known_chips[] = {
+       {0x00, 8, "PIC32CZ8110CA80208"},
+       {0x03, 8, "PIC32CZ8110CA80176"},
+       {0x06, 8, "PIC32CZ8110CA80144"},
+       {0x09, 8, "PIC32CZ8110CA80100"},
+       {0x0C, 8, "PIC32CZ8110CA90208"},
+       {0x0F, 8, "PIC32CZ8110CA90176"},
+       {0x12, 8, "PIC32CZ8110CA90144"},
+       {0x15, 8, "PIC32CZ8110CA90100"}};
+
+/**
+ * Private Fetch the type of chip this is from the device ID.
+ */
+static const struct pic32cz_chip_info *_pic32cz_get_chip_info(struct target 
*target)
+{
+       uint32_t id = 0;
+       int res = target_read_u32(target, PIC32CZ_DSU_DID, &id);
+       if (res == ERROR_OK) {
+               /* Product ID and Manufacturer ID are the same for all PIC32CZ 
parts*/
+               uint8_t product = (id >> 20) & 0xFF;
+               uint16_t manufacturer = (id >> 1) & 0x7FF;
+               if (product == 0x92 && manufacturer == 0x29) {
+                       uint8_t devsel = (id >> 12) & 0xFF;
+                       for (unsigned long i = 0; i < 
ARRAY_SIZE(pic32cz_known_chips); i++) {
+                               if (pic32cz_known_chips[i].devsel == devsel)
+                                       return &pic32cz_known_chips[i];
+                       }
+               }
+       }
+       LOG_ERROR("Not a Microchip PIC32CZ device");
+       return NULL;
+}
+
+/**
+ * Private PIC32CZ NVM Sequence
+ *
+ * This is the startup sequence defined for write and erase operations on the 
PIC32CZ
+ * internal flash memories. It is defined in the PIC32CZ Family Reference 
Manual:
+ *
+ * 31.2.13 FCW Sequencer User Model
+ *
+ */
+static int _pic32cz_nvm_sequence(struct flash_bank *bank, uint32_t addr, 
uint8_t *data, uint32_t operation)
+{
+       uint32_t tmp = 0;
+       uint32_t timeout = (operation == PIC32CZ_NVMOP_ERASE_PFM || operation 
== PIC32CZ_NVMOP_ERASE_PAGE)
+               ? PIC32CZ_TIMEOUT_ERASE : PIC32CZ_TIMEOUT_WRITE;
+
+       // 1. Lock the hardware write mutex by setting the LOCK bit to ‘1’ and 
the OWNER field to ‘01’
+       // simultaneously to the MUTEX register. Ensure that these bits are set 
correctly before proceeding,
+       // if the OWNER field is not ‘01’ and the LOCK bit is ‘1’ another 
system has ownership of the
+       // hardware write mutex and this operation must be attempted again when 
that system releases
+       // the mutex.
+       target_write_u32(bank->target, PIC32CZ_NVM_MUTEX, 0x3);
+       target_read_u32(bank->target, PIC32CZ_NVM_MUTEX, &tmp);
+       if (tmp != 0x3) {
+               LOG_ERROR("Cannot lock hardware write mutex");
+               return ERROR_FAIL;
+       }
+
+       // 2.Setup ADDR and if programming either DATAx (Single/Quad) or 
SRCADDR (Row Write).
+       target_write_u32(bank->target, PIC32CZ_NVM_ADDR, addr);
+       if (operation == PIC32CZ_NVMOP_WRITE_QUAD) {
+               for (int i = 0; i < 32; i += 4)
+                       target_write_u32(bank->target,
+                               PIC32CZ_NVM_DATA0 + i,
+                               data[i] | data[i + 1] << 8 | data[i + 2] << 16 
| data[i + 3] << 24);
+       }
+
+       // 3. Write WRKEY to KEY.KEY.
+       uint32_t keycode = 0x91C32C00 | 0x01;
+       target_write_u32(bank->target, PIC32CZ_NVM_KEY, keycode);
+
+       // 4. Write CTRLA.NVMOP to <Desired NVMOP>.
+       target_write_u32(bank->target, PIC32CZ_NVM_CTRLA, operation);
+
+       // 5. The FCW generates an interrupt when it clears STATUS.BUSY and 
sets INTFLAG.DONE.
+       tmp = 0;
+       while (timeout--) {
+               target_read_u32(bank->target, PIC32CZ_NVM_STATUS, &tmp);
+               if ((tmp & 0x1) == 0)
+                       return ERROR_OK;
+               alive_sleep(1);
+       }
+
+       LOG_ERROR("Timed out waiting for NVM operation to complete");
+       return ERROR_FAIL;
+}
+
+/**
+ * Request Human Readable Information about the PIC32CZ flash banks.
+ *
+ * Not returning anything meaningful as I can't see what I can say here that
+ * would not be provided elsewhere, for examples from "flash banks" command.
+ */
+static int pic32cz_info(struct flash_bank *bank, struct command_invocation 
*cmd)
+{
+       (void)bank;
+       (void)cmd;
+       return ERROR_OK;
+}
+
+/**
+ * Driver specific extensions to the flash bank command
+ *
+ * Currently do not have any specific extensions.
+ */
+FLASH_BANK_COMMAND_HANDLER(pic32cz_flash_bank_command)
+{
+       (void)bank;
+       (void)cmd;
+       return ERROR_OK;
+}
+
+/**
+ * Probe the PIC32CZ device to determine the flash bank configuration.
+ *
+ * Invoked by the probe script this probes the actual device to determine
+ * additional properties of the indicated flash bank as these will vary for
+ * different PIC32CZ devices.
+ *
+ */
+static int pic32cz_probe(struct flash_bank *bank)
+{
+       // Since all banks have a non zero size we'll use this as a test for
+       // whether we have already probed the device for this specific bank.
+       if (bank->size != 0)
+               return ERROR_OK;
+
+       // We need to know exactly which device this is so we can size the 
banks correctly.
+       const struct pic32cz_chip_info *details = 
_pic32cz_get_chip_info(bank->target);
+       if (!details) {
+               LOG_WARNING("Cannot identify target as a PIC32CZ device");
+               return ERROR_FLASH_OPERATION_FAILED;
+       }
+
+       // Now we know the device we can size the banks, there are two banks, 
the BFM and the PFM.
+       switch (bank->base) {
+       case FLASH_BANK_BFM_BASE:
+               bank->size = FLASH_BANK_BFM_SIZE;
+               bank->num_sectors = bank->size / FLASH_BANK_BFM_PAGE;
+               break;
+
+       case FLASH_BANK_PFM_BASE:
+               bank->size = details->flashsize * 1024 * 1024;
+               bank->num_sectors = bank->size / FLASH_BANK_PFM_PAGE;
+               break;
+
+       default:
+               LOG_ERROR("Bank not found at 0x%08x ", (unsigned 
int)bank->base);
+               return ERROR_FAIL;
+       }
+
+       // We are expected to allocate tracking structures for the sectors in 
the bank
+       // and store these in the bank structure.
+       bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
+       if (bank->sectors) {
+               for (int i = 0; i < (int)bank->num_sectors; i++) {
+                       bank->sectors[i].offset = i * (bank->size / 
bank->num_sectors);
+                       bank->sectors[i].size = (bank->size / 
bank->num_sectors);
+                       bank->sectors[i].is_erased = -1;
+                       bank->sectors[i].is_protected = 1;
+               }
+       }
+
+       return ERROR_OK;
+}
+
+/**
+ * Erase the specified range of sectors in the flash bank.
+ *
+ * Optimisations catch the cases where the defined first to last range voers an
+ * entire bank but most of the time this is dealing in 4Kbyte flash pages.
+ */
+static int pic32cz_erase(struct flash_bank *bank, unsigned int first, unsigned 
int last)
+{
+       if (bank->target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       // Trap the special cases where the entire block is erased (not 
applicable to BFM)
+       if (first == 0 && (last == (bank->num_sectors - 1)) && bank->base == 
FLASH_BANK_PFM_BASE) {
+               LOG_DEBUG("Erasing bank at 0x%08x", (uint32_t)(bank->base));
+               _pic32cz_nvm_sequence(bank, bank->base, 0, 
PIC32CZ_NVMOP_ERASE_PFM);
+               return ERROR_OK;
+       }
+
+       // Erase pages from first to last exclusive
+       for (unsigned int i = first; i <= last; i++) {
+               LOG_DEBUG("Erasing page %d at 0x%08x", i, (uint32_t)(bank->base 
+ bank->sectors[i].offset));
+               _pic32cz_nvm_sequence(bank, bank->base + 
bank->sectors[i].offset, 0, PIC32CZ_NVMOP_ERASE_PAGE);
+       }
+
+       return ERROR_OK;
+}
+
+/**
+ * Write contigous block of data to the flash bank
+ *
+ * Supports any alignment down to individual bytes using read/modify/write
+ * where demanded by the alignment. Flash write granularities in PIC32CZ are:
+ *
+ * "Single"    (1 x Double Word) = 64bits of data  (Data0 + Data1)
+ * "Quad"    (4 x Double Words) = 256bits of data  (Data0 thru Data7)
+ * "Row"    (16 x Double Words) = 1024bits of data (SRAM Transfer)
+ *
+ * Currently this implementation uses the "Quad" option only and works by
+ * processing the range of quads completely spanning the supplied range. Where
+ * the quad strays outside of the supplied data the quad is populated with
+ * the current content of the memory address read from the target.
+ *
+ * Performance might be improved by implementing the ROW mechanism but this
+ * will require an SRAM area on the target to be populated with the write
+ * data. Whilst this potentially quarters the number of flash operations
+ * it might still be held back by the SWD/JTAG transfers.
+ *
+ */
+static int pic32cz_write(struct flash_bank *bank, const uint8_t *buffer, 
uint32_t offset, uint32_t count)
+{
+       if (bank->target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       LOG_DEBUG("writing to flash at address " TARGET_ADDR_FMT " at offset 
0x%8.8" PRIx32
+                         " count: 0x%8.8" PRIx32 "",
+                         bank->base, offset, count);
+
+       // The inclusive range of addresses for which the caller is providing 
data
+       uint32_t addr_first = bank->base + offset;
+       uint32_t addr_last = bank->base + offset + count - 1;
+
+       // Iterating over the range of quads spanning the callers data
+       uint8_t quad[32];
+       uint32_t addr_quad = addr_first & ~(ARRAY_SIZE(quad) - 1);
+       while (addr_quad <= addr_last) {
+               for (unsigned long i = 0; i < ARRAY_SIZE(quad); ++i) {
+                       uint32_t addr = addr_quad + i;
+                       if (addr >= addr_first && addr <= addr_last)
+                               quad[i] = buffer[addr - addr_first];
+                       else
+                               target_read_u8(bank->target, addr, &quad[i]);
+               }
+               _pic32cz_nvm_sequence(bank, addr_quad, quad, 
PIC32CZ_NVMOP_WRITE_QUAD);
+               addr_quad += ARRAY_SIZE(quad);
+       }
+
+       return ERROR_OK;
+}
+
+static const struct command_registration pic32cz_exec_command_handlers[] = {
+       COMMAND_REGISTRATION_DONE};
+
+static const struct command_registration pic32cz_command_handlers[] = {
+       {
+               .name = "pic32cz",
+               .mode = COMMAND_ANY,
+               .help = "pic32cz flash command group",
+               .usage = "",
+               .chain = pic32cz_exec_command_handlers,
+       },
+       COMMAND_REGISTRATION_DONE};
+
+const struct flash_driver pic32cz_flash = {
+       .name = "pic32cz",
+       .commands = pic32cz_command_handlers,
+       .flash_bank_command = pic32cz_flash_bank_command,
+       .erase = pic32cz_erase,
+       .protect = NULL,
+       .write = pic32cz_write,
+       .read = default_flash_read,
+       .probe = pic32cz_probe,
+       .auto_probe = pic32cz_probe,
+       .erase_check = default_flash_blank_check,
+       .protect_check = NULL,
+       .info = pic32cz_info,
+       .free_driver_priv = default_flash_free_driver_priv,
+};
diff --git a/tcl/target/pic32cz.cfg b/tcl/target/pic32cz.cfg
new file mode 100644
index 0000000000..93802ce230
--- /dev/null
+++ b/tcl/target/pic32cz.cfg
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+if { [info exists CHIPNAME] } {
+   set _CHIPNAME $CHIPNAME
+} else {
+   set _CHIPNAME pic32cz
+}
+
+if { [info exists ENDIAN] } {
+   set _ENDIAN $ENDIAN
+} else {
+   set _ENDIAN little
+}
+
+if { [info exists CPUTAPID] } {
+   set _CPUTAPID $CPUTAPID
+} else {
+   set _CPUTAPID 0x2ba01477
+}
+
+transport select swd
+
+swd newdap $_CHIPNAME cpu -enable
+dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu
+target create $_CHIPNAME.cpu cortex_m -dap $_CHIPNAME.dap
+
+flash bank $_CHIPNAME.bfm pic32cz 0x08000000 0 0 0 $_CHIPNAME.cpu
+flash bank $_CHIPNAME.pfm pic32cz 0x0C000000 0 0 0 $_CHIPNAME.cpu
+
+reset_config srst_only srst_nogate connect_assert_srst

-- 

Reply via email to