This is an automated email from Gerrit.

"Walter Ji <walter...@oss.cipunited.com>" just uploaded a new patch set to 
Gerrit, which you can find at https://review.openocd.org/c/openocd/+/7873

-- gerrit

commit d9adfdaf3ea87e87f7381c68e5c17e4be68f438d
Author: Walter Ji <walter...@oss.cipunited.com>
Date:   Mon Aug 28 11:16:22 2023 +0800

    pic32: add support for pic32mz family
    
    pic32mz series use different flashing algorithms than pic32mx series.
    The target config file is attached with the commit.
    
    Change-Id: I148be5d67b7ff5e30b01ba6238a8a4a31cba772d
    Signed-off-by: Walter Ji <walter...@oss.cipunited.com>

diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am
index 534a7a804e..da224b99ee 100644
--- a/src/flash/nor/Makefile.am
+++ b/src/flash/nor/Makefile.am
@@ -51,6 +51,7 @@ NOR_DRIVERS = \
        %D%/numicro.c \
        %D%/ocl.c \
        %D%/pic32mx.c \
+       %D%/pic32mz.c \
        %D%/psoc4.c \
        %D%/psoc5lp.c \
        %D%/psoc6.c \
diff --git a/src/flash/nor/driver.h b/src/flash/nor/driver.h
index a63b72c8fa..9f4c9a3dc5 100644
--- a/src/flash/nor/driver.h
+++ b/src/flash/nor/driver.h
@@ -279,6 +279,7 @@ 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 pic32mx_flash;
+extern const struct flash_driver pic32mz_flash;
 extern const struct flash_driver psoc4_flash;
 extern const struct flash_driver psoc5lp_eeprom_flash;
 extern const struct flash_driver psoc5lp_flash;
diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c
index 3157bd3292..dd795d0e81 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[] = {
        &numicro_flash,
        &ocl_flash,
        &pic32mx_flash,
+       &pic32mz_flash,
        &psoc4_flash,
        &psoc5lp_flash,
        &psoc5lp_eeprom_flash,
diff --git a/src/flash/nor/pic32mz.c b/src/flash/nor/pic32mz.c
new file mode 100644
index 0000000000..b80725f2c1
--- /dev/null
+++ b/src/flash/nor/pic32mz.c
@@ -0,0 +1,1161 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/* Copyright (C) 2023 CIP United */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <jtag/jtag.h>
+#include "imp.h"
+#include <target/algorithm.h>
+#include <target/mips32.h>
+#include <target/mips_m4k.h>
+
+
+/* In PIC32MZ EC devices, the Flash page size is 16 KB (4096 IW) and the row 
size is 2 KB (512 IW). */
+
+#define PIC32MZ_MANUF_ID       0x029
+
+/* PIC32MZ memory locations */
+
+#define PIC32MZ_PHYS_RAM               0x00000000
+#define PIC32MZ_PHYS_PGM_FLASH         0x1D000000
+#define PIC32MZ_PHYS_PERIPHERALS       0x1F800000
+#define PIC32MZ_PHYS_BOOT_FLASH                0x1FC00000
+#define PIC32MZ_PHYS_LOWER_BOOT_ALIAS  0x1FC00000
+#define PIC32MZ_PHYS_UPPER_BOOT_ALIAS  0x1FC20000
+#define PIC32MZ_PHYS_BOOT_FLASH_END    0x1FC74000
+#define PIC32MZ_PAGE_NUMBYTES (16 * 1024)
+#define PIC32MZ_BOOT_BLOCK_NUMBYTES     (80 * 1024)
+#define PIC32MZ_BOOT_BLOCK_NUMPAGES     (PIC32MZ_BOOT_BLOCK_NUMBYTES / 
PIC32MZ_PAGE_NUMBYTES)
+
+
+/*
+ * Translate Virtual and Physical addresses.
+ * Note: These macros only work for KSEG0/KSEG1 addresses.
+ */
+
+#define virt2phys(v)   ((v) & 0x1FFFFFFF)
+
+/* PIC32MZ configuration register locations */
+
+#define PIC32MZ_DEVCP0                 0xBFC4FFDC
+
+#define PIC32MZ_NVMBWP_LBWP0_OFFSET    8
+#define PIC32MZ_NVMBWP_UBWP0_OFFSET    0
+
+#define PIC32MZ_NVMBWP_LBWPULOCK       0x00008000
+#define PIC32MZ_NVMBWP_UBWPULOCK       0x00000080
+
+#define PIC32MZ_NVMPWP_PWPULOCK                0x80000000
+#define PIC32MZ_NVMPWP_PWP             0x00FFFFFF
+
+/* PIC32MZ flash controller register locations */
+
+#define PIC32MZ_NVMCON                 0xBF800600
+#define PIC32MZ_NVMCONCLR              0xBF800604
+#define PIC32MZ_NVMCONSET              0xBF800608
+#define PIC32MZ_NVMCONINV              0xBF80060C
+#define NVMCON_NVMWR                   BIT(15)
+#define NVMCON_NVMWREN                 BIT(14)
+#define NVMCON_NVMERR                  BIT(13)
+#define NVMCON_LVDERR                  BIT(12)
+
+#define NVMCON_OP_PFM_ERASE            0x7
+#define NVMCON_OP_PAGE_ERASE           0x4
+#define NVMCON_OP_ROW_PROG             0x3
+#define NVMCON_OP_WORD_PROG            0x1
+#define NVMCON_OP_QUAD_WORD_PROG       0x2
+#define NVMCON_OP_NOP                  0x0
+
+#define PIC32MZ_NVMKEY                 0xBF800610
+#define PIC32MZ_NVMADDR                        0xBF800620
+#define PIC32MZ_NVMADDRCLR             0xBF800624
+#define PIC32MZ_NVMADDRSET             0xBF800628
+#define PIC32MZ_NVMADDRINV             0xBF80062C
+#define PIC32MZ_NVMDATA0               0xBF800630
+#define PIC32MZ_NVMDATA1               0xBF800640
+#define PIC32MZ_NVMDATA2               0xBF800650
+#define PIC32MZ_NVMDATA3               0xBF800660
+#define PIC32MZ_NVMSRCADDR             0xBF800670
+#define PIC32MZ_DEVCFG3                        0xBFC0FFC0
+#define PIC32MZ_NVMPWP                 0xBF800680
+#define PIC32MZ_NVMBWP                 0xBF800690
+#define PIC32MZ_NVMBWPCLR              0xBF800694
+
+#define PIC32MZ_CFGCON                 0xBF800000
+#define CFGCON_ECC                     0x30
+
+/* flash unlock keys */
+#define NVMKEY1                                0xAA996655
+#define NVMKEY2                                0x556699AA
+
+
+
+struct pic32mz_flash_bank {
+       int probed;
+       int dev_type;           /* Default 0. 1 for Pic32mz1XX/2XX variant */
+};
+
+/*
+ * DEVID values as per PIC32MZ Flash Programming Specification Rev J
+ */
+
+static const struct pic32mz_devs_s {
+       uint32_t devid;
+       const char *name;
+       uint32_t kb_program_flash;
+} pic32mz_devs[] = {
+       /* PIC32MZ EMBEDDED CONNECTIVITY (EC) FAMILY DEVICE IDS */
+       {0x05103053, "1024ECG064", 1024},
+       {0x05108053, "1024ECH064", 1024},
+       {0x05130053, "1024ECM064", 1024},
+       {0x05104053, "2048ECG064", 2048},
+       {0x05109053, "2048ECH064", 2048},
+       {0x05131053, "2048ECM064", 2048},
+       {0x0510D053, "1024ECG100", 1024},
+       {0x05112053, "1024ECH100", 1024},
+       {0x0513A053, "1024ECM100", 1024},
+       {0x0510E053, "2048ECG100", 2048},
+       {0x05113053, "2048ECH100", 2048},
+       {0x0513B053, "2048ECM100", 2048},
+       {0x05117053, "1024ECG124", 1024},
+       {0x0511C053, "1024ECH124", 1024},
+       {0x05144053, "1024ECM124", 1024},
+       {0x05118053, "2048ECG124", 2048},
+       {0x0511D053, "2048ECH124", 2048},
+       {0x05145053, "2048ECM124", 2048},
+       {0x05121053, "1024ECG144", 1024},
+       {0x05126053, "1024ECH144", 1024},
+       {0x0514E053, "1024ECM144", 1024},
+       {0x05122053, "2048ECG144", 2048},
+       {0x05127053, "2048ECH144", 2048},
+       {0x0514F053, "2048ECM144", 2048},
+       /* PIC32MZ EMBEDDED CONNECTIVITY WITH FPU (EF) FAMILY DEVICE IDS */
+       {0x07201053, "0512EFE064", 512},
+       {0x07206053, "0512EFF064", 512},
+       {0x0722E053, "0512EFK064", 512},
+       {0x07202053, "1024EFE064", 1024},
+       {0x07207053, "1024EFF064", 1024},
+       {0x0722F053, "1024EFK064", 1024},
+       {0x07203053, "1024EFG064", 1024},
+       {0x07208053, "1024EFH064", 1024},
+       {0x07230053, "1024EFM064", 1024},
+       {0x07204053, "2048EFG064", 2048},
+       {0x07209053, "2048EFH064", 2048},
+       {0x07231053, "2048EFM064", 2048},
+       {0x0720B053, "0512EFE100", 512},
+       {0x07210053, "0512EFF100", 512},
+       {0x07238053, "0512EFK100", 512},
+       {0x0720C053, "1024EFE100", 1024},
+       {0x07211053, "1024EFF100", 1024},
+       {0x07239053, "1024EFK100", 1024},
+       {0x0720D053, "1024EFG100", 1024},
+       {0x07212053, "1024EFH100", 1024},
+       {0x0723A053, "1024EFM100", 1024},
+       {0x0720E053, "2048EFG100", 2048},
+       {0x07213053, "2048EFH100", 2048},
+       {0x0723B053, "2048EFM100", 2048},
+       {0x07215053, "0512EFE124", 512},
+       {0x0721A053, "0512EFF124", 512},
+       {0x07242053, "0512EFK124", 512},
+       {0x07216053, "1024EFE124", 1024},
+       {0x0721B053, "1024EFF124", 1024},
+       {0x07243053, "1024EFK124", 1024},
+       {0x07217053, "1024EFG124", 1024},
+       {0x0721C053, "1024EFH124", 1024},
+       {0x07244053, "1024EFM124", 1024},
+       {0x07218053, "2048EFG124", 2048},
+       {0x0721D053, "2048EFH124", 2048},
+       {0x07245053, "2048EFM124", 2048},
+       {0x0721F053, "0512EFE144", 512},
+       {0x07224053, "0512EFF144", 512},
+       {0x0724C053, "0512EFK144", 512},
+       {0x07220053, "1024EFE144", 1024},
+       {0x07225053, "1024EFF144", 1024},
+       {0x0724D053, "1024EFK144", 1024},
+       {0x07221053, "1024EFG144", 1024},
+       {0x07226053, "1024EFH144", 1024},
+       {0x0724E053, "1024EFM144", 1024},
+       {0x07222053, "2048EFG144", 2048},
+       {0x07227053, "2048EFH144", 2048},
+       {0x0724F053, "2048EFM144", 2048},
+       /* PIC32MZ GRAPHICS (DA) FAMILY DEVICE IDS */
+       {0x05F0C053, "1025DAA169", 1024},
+       {0x05F0D053, "1025DAB169", 1024},
+       {0x05F0F053, "1064DAA169", 1024},
+       {0x05F10053, "1064DAB169", 1024},
+       {0x05F15053, "2025DAA169", 2048},
+       {0x05F16053, "2025DAB169", 2048},
+       {0x05F18053, "2064DAA169", 2048},
+       {0x05F19053, "2064DAB169", 2048},
+       {0x05F42053, "1025DAG169", 1024},
+       {0x05F43053, "1025DAH169", 1024},
+       {0x05F45053, "1064DAG169", 1024},
+       {0x05F46053, "1064DAH169", 1024},
+       {0x05F4B053, "2025DAG169", 2048},
+       {0x05F4C053, "2025DAH169", 2048},
+       {0x05F4E053, "2064DAG169", 2048},
+       {0x05F4F053, "2064DAH169", 2048},
+       {0x05F78053, "1025DAA176", 1024},
+       {0x05F79053, "1025DAB176", 1024},
+       {0x05F7B053, "1064DAA176", 1024},
+       {0x05F7C053, "1064DAB176", 1024},
+       {0x05F81053, "2025DAA176", 2048},
+       {0x05F82053, "2025DAB176", 2048},
+       {0x05F84053, "2064DAA176", 2048},
+       {0x05F85053, "2064DAB176", 2048},
+       {0x05FAE053, "1025DAG176", 1024},
+       {0x05FAF053, "1025DAH176", 1024},
+       {0x05FB1053, "1064DAG176", 1024},
+       {0x05FB2053, "1064DAH176", 1024},
+       {0x05FB7053, "2025DAG176", 2048},
+       {0x05FB8053, "2025DAH176", 2048},
+       {0x05FBA053, "2064DAG176", 2048},
+       {0x05FBB053, "2064DAH176", 2048},
+       {0x05F5D053, "1025DAA288", 1024},
+       {0x05F5E053, "1025DAB288", 1024},
+       {0x05F60053, "1064DAA288", 1024},
+       {0x05F61053, "1064DAB288", 1024},
+       {0x05F66053, "2025DAA288", 2048},
+       {0x05F67053, "2025DAB288", 2048},
+       {0x05F69053, "2064DAA288", 2048},
+       {0x05F6A053, "2064DAB288", 2048},
+       {0x00000000, NULL, 0}
+};
+
+static const struct pic32mz_devs_s *pic32mz_lookup_device(uint32_t device_id)
+{
+       int i;
+
+       for (i = 0; pic32mz_devs[i].name != NULL; i++) {
+               if (pic32mz_devs[i].devid == (device_id & 0x0fffffff))
+                       return &pic32mz_devs[i];
+       }
+       return NULL;
+}
+
+/* flash bank pic32mz <base> <size> 0 0 <target#>
+ */
+FLASH_BANK_COMMAND_HANDLER(pic32mz_flash_bank_command)
+{
+       struct pic32mz_flash_bank *pic32mz_info;
+
+       if (CMD_ARGC < 6)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       pic32mz_info = malloc(sizeof(struct pic32mz_flash_bank));
+       bank->driver_priv = pic32mz_info;
+
+       pic32mz_info->probed = 0;
+       pic32mz_info->dev_type = 0;
+
+       return ERROR_OK;
+}
+
+static uint32_t pic32mz_get_flash_status(struct flash_bank *bank)
+{
+       struct target *target = bank->target;
+       uint32_t status;
+
+       target_read_u32(target, PIC32MZ_NVMCON, &status);
+
+       return status;
+}
+
+static uint32_t pic32mz_wait_status_busy(struct flash_bank *bank, int timeout)
+{
+       uint32_t status;
+
+       /* wait for busy to clear */
+       while (((status = pic32mz_get_flash_status(bank)) & NVMCON_NVMWR) && 
(timeout-- > 0)) {
+               LOG_DEBUG("status: 0x%" PRIx32, status);
+               alive_sleep(1);
+       }
+       if (timeout <= 0)
+               LOG_DEBUG("timeout: status: 0x%" PRIx32, status);
+
+       return status;
+}
+
+static int pic32mz_nvm_exec(struct flash_bank *bank, uint32_t op, uint32_t 
timeout)
+{
+       struct target *target = bank->target;
+       uint32_t status;
+
+       target_write_u32(target, PIC32MZ_NVMCON, NVMCON_NVMWREN | op);
+
+       /* unlock flash registers
+        * write of 0 might not have been required for PIC32MX,
+        * but it is suggested for PIC32MZ */
+       target_write_u32(target, PIC32MZ_NVMKEY, 0);
+       target_write_u32(target, PIC32MZ_NVMKEY, NVMKEY1);
+       target_write_u32(target, PIC32MZ_NVMKEY, NVMKEY2);
+
+       /* start operation */
+       target_write_u32(target, PIC32MZ_NVMCONSET, NVMCON_NVMWR);
+
+       status = pic32mz_wait_status_busy(bank, timeout);
+
+       /* lock flash registers */
+       target_write_u32(target, PIC32MZ_NVMCONCLR, NVMCON_NVMWREN);
+
+       return status;
+}
+
+static int pic32mz_protect_check(struct flash_bank *bank)
+{
+       struct target *target = bank->target;
+
+       uint32_t cp0_address;
+       uint32_t nvmpwp_address;
+       uint32_t nvmbwp_address;
+       uint32_t devcp0;
+       uint32_t nvmpwp;
+       uint32_t nvmbwp;
+       uint64_t s;
+       uint64_t num_pages;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       cp0_address = PIC32MZ_DEVCP0;
+       nvmpwp_address = PIC32MZ_NVMPWP;
+       nvmbwp_address = PIC32MZ_NVMBWP;
+
+       target_read_u32(target, cp0_address, &devcp0);
+
+
+
+       if ((devcp0 & (1 << 28)) == 0) { /* code protect bit */
+               /* all pages protected */
+               for (s = 0; s < bank->num_sectors; s++)
+                       bank->sectors[s].is_protected = 1;
+       } else if (virt2phys(bank->base) == PIC32MZ_PHYS_LOWER_BOOT_ALIAS) {
+               /* Boot flash write protection is divided into pages and is 
enabled by the LBWPx bits in the NVMBWP register.*/
+               target_read_u32(target, nvmbwp_address, &nvmbwp);
+               for (s = 0; s < bank->num_sectors && s < 
PIC32MZ_BOOT_BLOCK_NUMPAGES; s++) {
+                       int bit = (nvmbwp >> (PIC32MZ_NVMBWP_LBWP0_OFFSET + s)) 
& 0x1;
+                       bank->sectors[s].is_protected = (bit != 0);
+               }
+       } else if (virt2phys(bank->base) == PIC32MZ_PHYS_UPPER_BOOT_ALIAS) {
+               /* Boot Flash write protection is divided into pages and is 
enabled by the UBWPx bits in the NVMBWP register.*/
+               target_read_u32(target, nvmbwp_address, &nvmbwp);
+               for (s = 0; s < bank->num_sectors && s < 
PIC32MZ_BOOT_BLOCK_NUMPAGES; s++) {
+                       int bit = (nvmbwp >> (PIC32MZ_NVMBWP_UBWP0_OFFSET + s)) 
& 0x1;
+                       bank->sectors[s].is_protected = (bit != 0);
+               }
+       } else {
+               /* pgm flash */
+               target_read_u32(target, nvmpwp_address, &nvmpwp);
+               nvmpwp &= 0x00FFFFFF;
+               num_pages = (nvmpwp / PIC32MZ_PAGE_NUMBYTES)+1;
+               for (s = 0; s < bank->num_sectors; s++) {
+                       // protected if nvmpwp is not zero, and if the page 
index is less than or equal to the page index of nvmpwp
+                       bank->sectors[s].is_protected = (nvmpwp != 0x0) && (s < 
num_pages);
+               }
+       }
+
+       return ERROR_OK;
+}
+
+static int pic32mz_erase(struct flash_bank *bank, unsigned int first, unsigned 
int last)
+{
+       struct target *target = bank->target;
+       unsigned int i;
+       uint32_t status;
+
+       if (bank->target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       if ((first == 0) && (last == (bank->num_sectors - 1))
+               && (virt2phys(bank->base) == PIC32MZ_PHYS_PGM_FLASH)) {
+               /* this will only erase the Program Flash (PFM), not the Boot 
Flash (BFM)
+                * we need to use the MTAP to perform a full erase */
+               LOG_DEBUG("Erasing entire program flash");
+               status = pic32mz_nvm_exec(bank, NVMCON_OP_PFM_ERASE, 50);
+               if (status & NVMCON_NVMERR)
+                       return ERROR_FLASH_OPERATION_FAILED;
+               if (status & NVMCON_LVDERR)
+                       return ERROR_FLASH_OPERATION_FAILED;
+               return ERROR_OK;
+       }
+
+       for (i = first; i <= last; i++) {
+               target_write_u32(target, PIC32MZ_NVMADDR, virt2phys(bank->base 
+ bank->sectors[i].offset));
+
+               status = pic32mz_nvm_exec(bank, NVMCON_OP_PAGE_ERASE, 10);
+
+               if (status & NVMCON_NVMERR)
+                       return ERROR_FLASH_OPERATION_FAILED;
+               if (status & NVMCON_LVDERR)
+                       return ERROR_FLASH_OPERATION_FAILED;
+               bank->sectors[i].is_erased = 1;
+       }
+
+       return ERROR_OK;
+}
+
+static int pic32mz_protect(struct flash_bank *bank, int set, unsigned int 
first, unsigned int last)
+{
+       struct target *target = bank->target;
+       unsigned int sector;
+       uint32_t nvmbwp;
+       uint32_t nvmpwp;
+       uint32_t nvmpwp_23_0;
+       unsigned int num_pages;
+
+       /* the calling function(s) validate that first and last are valid 
sector indices, no need to validate here */
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       if (virt2phys(bank->base) == PIC32MZ_PHYS_LOWER_BOOT_ALIAS) {
+               /* Boot flash write protection is divided into pages and is 
enabled by the LBWPx bits in the NVMBWP register.*/
+               target_read_u32(target, PIC32MZ_NVMBWP, &nvmbwp);
+               if ((nvmbwp & PIC32MZ_NVMBWP_LBWPULOCK) == 0) {
+                       // can't unprotect the page
+                       LOG_ERROR("Boot flash write protect registers are 
locked; unlocking requires device reset");
+                       return ERROR_FLASH_OPERATION_FAILED;
+               }
+               for (sector = first; sector <= last; sector++) {
+                       if (set)
+                               nvmbwp |= (1 << (PIC32MZ_NVMBWP_LBWP0_OFFSET + 
sector));
+                       else
+                               nvmbwp &= ~(1 << (PIC32MZ_NVMBWP_LBWP0_OFFSET + 
sector));
+               }
+               /* changing the write protect bits requires unlocking flash 
registers */
+               /* write of 0 might not have been required for PIC32MX, but it 
is suggested for PIC32MZ */
+               target_write_u32(target, PIC32MZ_NVMKEY, 0);
+               target_write_u32(target, PIC32MZ_NVMKEY, NVMKEY1);
+               target_write_u32(target, PIC32MZ_NVMKEY, NVMKEY2);
+               /* write back nvmbwp */
+               target_write_u32(target, PIC32MZ_NVMBWP, nvmbwp);
+       } else if (virt2phys(bank->base) == PIC32MZ_PHYS_UPPER_BOOT_ALIAS) {
+               /* Boot Flash write protection is divided into pages and is 
enabled by the UBWPx bits in the NVMBWP register.*/
+               target_read_u32(target, PIC32MZ_NVMBWP, &nvmbwp);
+               if ((nvmbwp & PIC32MZ_NVMBWP_UBWPULOCK) == 0) {
+                       // can't unprotect the page
+                       LOG_ERROR("Boot flash write protect registers are 
locked; unlocking requires device reset");
+                       return ERROR_FLASH_OPERATION_FAILED;
+               }
+
+               for (sector = first; sector <= last; sector++) {
+                       if (set)
+                               nvmbwp |= (1 << (PIC32MZ_NVMBWP_UBWP0_OFFSET + 
sector));
+                       else
+                               nvmbwp &= ~(1 << (PIC32MZ_NVMBWP_UBWP0_OFFSET + 
sector));
+               }
+               /* changing the write protect bits requires unlocking flash 
registers */
+               /* write of 0 might not have been required for PIC32MX, but it 
is suggested for PIC32MZ */
+               target_write_u32(target, PIC32MZ_NVMKEY, 0);
+               target_write_u32(target, PIC32MZ_NVMKEY, NVMKEY1);
+               target_write_u32(target, PIC32MZ_NVMKEY, NVMKEY2);
+               /* write back nvmbwp */
+               target_write_u32(target, PIC32MZ_NVMBWP, nvmbwp);
+       } else {
+               /* pgm flash */
+               target_read_u32(target, PIC32MZ_NVMPWP, &nvmpwp);
+               if ((nvmpwp & PIC32MZ_NVMPWP_PWPULOCK) == 0) {
+                       // can't unprotect the page
+                       LOG_ERROR("Program flash write protect register is 
locked; unlocking requires device reset");
+                       return ERROR_FLASH_OPERATION_FAILED;
+               }
+               nvmpwp_23_0 = nvmpwp & PIC32MZ_NVMPWP_PWP;
+               num_pages = (nvmpwp_23_0 == 0 ? 0 : (nvmpwp_23_0 / 
PIC32MZ_PAGE_NUMBYTES)+1);
+               if (set) {
+                       if (first > num_pages) {
+                               LOG_ERROR("protected space must be contiguous 
below unprotected space");
+                               return ERROR_FLASH_SECTOR_INVALID;
+                       }
+                       /* need 4 offset just because 0 is special sentinel 
value and must be avoided */
+                       nvmpwp = (nvmpwp & PIC32MZ_NVMPWP_PWPULOCK) | ((last * 
PIC32MZ_PAGE_NUMBYTES) + 4);
+               } else {
+                       if (last < num_pages - 1) {
+                               LOG_ERROR("protected space must be contiguous 
below unprotected space");
+                               return ERROR_FLASH_SECTOR_INVALID;
+                       }
+                       if (first == 0)
+                               nvmpwp = (nvmpwp & PIC32MZ_NVMPWP_PWPULOCK);
+                       else /* need 4 offset just because 0 is special 
sentinel value and must be avoided */
+                               nvmpwp = (nvmpwp & PIC32MZ_NVMPWP_PWPULOCK) | 
(((first - 1) * PIC32MZ_PAGE_NUMBYTES) + 4);
+               }
+               /* changing the write protect bits requires unlocking flash 
registers */
+               /* write of 0 might not have been required for PIC32MX, but it 
is suggested for PIC32MZ */
+               target_write_u32(target, PIC32MZ_NVMKEY, 0);
+               target_write_u32(target, PIC32MZ_NVMKEY, NVMKEY1);
+               target_write_u32(target, PIC32MZ_NVMKEY, NVMKEY2);
+               /* write back nvmpwp */
+               target_write_u32(target, PIC32MZ_NVMPWP, nvmpwp);
+       }
+
+       return ERROR_OK;
+}
+
+/* see contib/loaders/flash/pic32mz.s for src */
+
+static uint32_t pic32mz_flash_write_code[] = {
+                                       /* write: */
+       /* nvmkey1 = t0 = 0xAA996655 */
+       0x3C08AA99,             /* lui $t0, 0xaa99 */
+       0x35086655,             /* ori $t0, 0x6655 */
+       /* nvmkey2 = t1 = 0x556699aa */
+       0x3C095566,             /* lui $t1, 0x5566 */
+       0x352999AA,             /* ori $t1, 0x99aa */
+       /* nvmcon_addr = t2 = 0xBF800600 */
+       0x3C0ABF80,             /* lui $t2, 0xbf80 */
+       0x354A0600,             /* ori $t2, 0x0600 */
+       /* nvmcon_value = t3 = 0x4003 */
+       0x340B4003,             /* ori $t3, $zero, 0x4003 */
+       /* nvmconset_value = t4 = 0x8000 */
+       0x340C8000,             /* ori $t4, $zero, 0x8000 */
+                                       /* write_row: */
+       /* while (1) { */
+       /* nvmconclr_value = t5 = 0x4000 */
+       /*   if (param_words_left < 512) {
+            goto write_word;
+            }
+       */
+       0x2CD30200,             /* sltiu $s3, $a2, 512 */   /* modified for MZ 
-- 512 words */
+       0x16600008,             /* bne $s3, $zero, write_word */
+       0x340D4000,             /* ori $t5, $zero, 0x4000 */
+       /* *nvmaddr_addr = param_dest_address; */
+       0xAD450020,             /* sw $a1, 32($t2) */
+       /* *srcaddr_addr = param_source_address; */
+       0xAD440070,             /* sw $a0, 112($t2) */
+       /* param_source_address += 2048 */
+       /* progflash() */
+       0x04110016,             /* bal progflash */
+       0x24840800,             /* addiu $a0, $a0, 2048 */
+       /* param_dest_address += 2048; */
+       0x24A50800,             /* addiu $a1, $a1, 2048 */
+       /* param_words_left -= 512;
+        } end while */
+       0x1000FFF7,             /* beq $zero, $zero, write_row */
+       0x24C6FE00,             /* addiu $a2, $a2, -512 */
+                                       /* write_word: */
+       /* param_source_address |= 0xA0000000; */
+       0x3C15A000,             /* lui $s5, 0xa000 */
+       0x36B50000,             /* ori $s5, $s5, 0x0 */
+       0x00952025,             /* or $a0, $a0, $s5 */
+       /* nvmcon_value = 0x4001; */
+       /* goto next_word; */
+       0x10000008,             /* beq $zero, $zero, next_word */
+       0x340B4001,             /* ori $t3, $zero, 0x4001 */
+                                       /* prog_word: */
+       /* do { */
+       /*    *nvmdata_addr = *param_source_address; */
+       0x8C940000,             /* lw $s4, 0($a0) */
+       0xAD540030,             /* sw $s4, 48($t2) */
+       /*    *nvmaddr_addr = param_dest_address; */
+       0xAD450020,             /* sw $a1, 32($t2) */
+       /*    param_source_address += 4; */
+       /*    progflash() */
+       0x04110009,             /* bal progflash */
+       0x24840004,             /* addiu $a0, $a0, 4 */
+       /*    param_dest_address += 4; */
+       0x24A50004,             /* addiu $a1, $a1, 4 */
+       /*    param_words_left -= 1; */
+       0x24C6FFFF,             /* addiu $a2, $a2, -1 */
+                                       /* next_word: */
+       /* } while (param_words_left != 0); */
+       0x14C0FFF8,             /* bne $a2, $zero, prog_word */
+       0x00000000,             /* nop */
+                                       /* done: */
+       /* param_result = 0; */
+       /* goto exit; */
+       0x10000002,             /* beq $zero, $zero, exit */
+       0x24040000,             /* addiu $a0, $zero, 0 */
+                                       /* error: */
+       /* param_result = nvmcon_result; */
+       0x26240000,             /* addiu $a0, $s1, 0 */
+                                       /* exit: */
+       0x7000003F,             /* sdbbp */
+                                       /* progflash: */
+       /* *nvmcon_addr = nvmcon_value; */
+       0xAD4B0000,             /* sw $t3, 0($t2) */
+       /* *nvmkey_addr = nvmkey1; */
+       0xAD480010,             /* sw $t0, 16($t2) */
+       /* *nvmkey_addr = nvmkey2; */
+       0xAD490010,             /* sw $t1, 16($t2) */
+       /* *nvmconset_addr = nvmconset_value; */
+       0xAD4C0008,             /* sw $t4, 8($t2) */
+                                       /* waitflash: */
+       /*    do { } while ((*NVMCON & nvmconset_value) != 0); */
+       0x8D500000,             /* lw $s0, 0($t2) */
+       0x020C8024,             /* and $s0, $s0, $t4 */
+       0x1600FFFD,             /* bne $s0, $zero, waitflash */
+       0x00000000,             /* nop */
+       0x00000000,             /* nop */
+       0x00000000,             /* nop */
+       0x00000000,             /* nop */
+       0x00000000,             /* nop */
+       /*    if ((*NVMCON & 0x3000) != 0) {
+                     goto error;
+             }
+       */
+       0x8D510000,             /* lw $s1, 0($t2) */
+       0x32313000,             /* andi $s1, $s1, 0x3000 */
+       0x1620FFEF,             /* bne $s1, $zero, error */
+       /* *nvmconclr_addr = nvmconclr_value; */
+       0xAD4D0004,             /* sw $t5, 4($t2) */
+       /* return */
+       0x03E00008,             /* jr $ra */
+       0x00000000              /* nop */
+};
+
+static int pic32mz_write_block(struct flash_bank *bank, const uint8_t *buffer,
+               uint32_t offset, uint32_t count)
+{
+       struct target *target = bank->target;
+       uint32_t buffer_size = 16384;
+       struct working_area *write_algorithm;
+       struct working_area *source;
+       uint32_t address = bank->base + offset;
+       struct reg_param reg_params[3];
+       uint32_t row_size;
+       int retval = ERROR_OK;
+
+       // struct pic32mz_flash_bank *pic32mz_info = bank->driver_priv;
+       struct mips32_algorithm mips32_info;
+
+       /* flash write code */
+       if (target_alloc_working_area(target, sizeof(pic32mz_flash_write_code),
+                       &write_algorithm) != ERROR_OK) {
+               LOG_WARNING("no working area available, can't do block memory 
writes");
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       };
+
+       /* 2K byte row */
+       row_size = 2 * 1024;
+
+       uint8_t code[sizeof(pic32mz_flash_write_code)];
+       target_buffer_set_u32_array(target, code, 
ARRAY_SIZE(pic32mz_flash_write_code),
+                       pic32mz_flash_write_code);
+       retval = target_write_buffer(target, write_algorithm->address, 
sizeof(code), code);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* memory buffer */
+       while (target_alloc_working_area_try(target, buffer_size, &source) != 
ERROR_OK) {
+               buffer_size /= 2;
+               if (buffer_size <= 256) {
+                       /* we already allocated the writing code, but failed to 
get a
+                        * buffer, free the algorithm */
+                       target_free_working_area(target, write_algorithm);
+
+                       LOG_WARNING("no large enough working area available, 
can't do block memory writes");
+                       return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+               }
+       }
+
+       mips32_info.common_magic = MIPS32_COMMON_MAGIC;
+       mips32_info.isa_mode = MIPS32_ISA_MIPS32;
+
+       init_reg_param(&reg_params[0], "r4", 32, PARAM_IN_OUT);
+       init_reg_param(&reg_params[1], "r5", 32, PARAM_OUT);
+       init_reg_param(&reg_params[2], "r6", 32, PARAM_OUT);
+
+       int row_offset = offset % row_size;
+       uint8_t *new_buffer = NULL;
+       if (row_offset && (count >= (row_size / 4))) {
+               new_buffer = malloc(buffer_size);
+               if (new_buffer == NULL) {
+                       LOG_ERROR("Out of memory");
+                       return ERROR_FAIL;
+               }
+               memset(new_buffer,  0xff, row_offset);
+               address -= row_offset;
+       } else
+               row_offset = 0;
+
+       while (count > 0) {
+               uint32_t status;
+               uint32_t thisrun_count;
+
+               if (row_offset) {
+                       thisrun_count = (count > ((buffer_size - row_offset) / 
4)) ?
+                               ((buffer_size - row_offset) / 4) : count;
+
+                       memcpy(new_buffer + row_offset, buffer, thisrun_count * 
4);
+
+                       retval = target_write_buffer(target, source->address,
+                               row_offset + thisrun_count * 4, new_buffer);
+                       if (retval != ERROR_OK)
+                               break;
+               } else {
+                       thisrun_count = (count > (buffer_size / 4)) ?
+                                       (buffer_size / 4) : count;
+
+                       retval = target_write_buffer(target, source->address,
+                                       thisrun_count * 4, buffer);
+                       if (retval != ERROR_OK)
+                               break;
+               }
+
+               buf_set_u32(reg_params[0].value, 0, 32, 
virt2phys(source->address));
+               buf_set_u32(reg_params[1].value, 0, 32, virt2phys(address));
+               buf_set_u32(reg_params[2].value, 0, 32, thisrun_count + 
row_offset / 4);
+
+               retval = target_run_algorithm(target, 0, NULL, 3, reg_params,
+                               write_algorithm->address,
+                               0, 10000, &mips32_info);
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("error executing pic32mz flash write 
algorithm");
+                       retval = ERROR_FLASH_OPERATION_FAILED;
+                       break;
+               }
+
+               status = buf_get_u32(reg_params[0].value, 0, 32);
+
+               if (status & NVMCON_NVMERR) {
+                       LOG_ERROR("Flash write error NVMERR (status = 0x%08" 
PRIx32 ")", status);
+                       retval = ERROR_FLASH_OPERATION_FAILED;
+                       break;
+               }
+
+               if (status & NVMCON_LVDERR) {
+                       LOG_ERROR("Flash write error LVDERR (status = 0x%08" 
PRIx32 ")", status);
+                       retval = ERROR_FLASH_OPERATION_FAILED;
+                       break;
+               }
+
+               buffer += thisrun_count * 4;
+               address += thisrun_count * 4;
+               count -= thisrun_count;
+               if (row_offset) {
+                       address += row_offset;
+                       row_offset = 0;
+               }
+       }
+
+       target_free_working_area(target, source);
+       target_free_working_area(target, write_algorithm);
+
+       destroy_reg_param(&reg_params[0]);
+       destroy_reg_param(&reg_params[1]);
+       destroy_reg_param(&reg_params[2]);
+
+       if (new_buffer != NULL)
+               free(new_buffer);
+       return retval;
+}
+
+static int pic32mz_write_word(struct flash_bank *bank, uint32_t address, 
uint32_t word)
+{
+       struct target *target = bank->target;
+
+       target_write_u32(target, PIC32MZ_NVMADDR, virt2phys(address));
+       target_write_u32(target, PIC32MZ_NVMDATA0, word);
+
+       return pic32mz_nvm_exec(bank, NVMCON_OP_WORD_PROG, 5);
+}
+
+static int pic32mz_write_quad_word(struct flash_bank *bank, uint32_t address, 
uint32_t word0,
+                                                                       
uint32_t word1, uint32_t word2, uint32_t word3)
+{
+       struct target *target = bank->target;
+
+       target_write_u32(target, PIC32MZ_NVMADDR, virt2phys(address));
+       target_write_u32(target, PIC32MZ_NVMDATA0, word0);
+       target_write_u32(target, PIC32MZ_NVMDATA1, word1);
+       target_write_u32(target, PIC32MZ_NVMDATA2, word2);
+       target_write_u32(target, PIC32MZ_NVMDATA3, word3);
+
+       return pic32mz_nvm_exec(bank, NVMCON_OP_QUAD_WORD_PROG, 5);
+}
+
+static int pic32mz_write_quad_words(struct flash_bank *bank, uint32_t address, 
const uint8_t *buffer, uint32_t numwords)
+{
+       uint32_t words_remaining = numwords;
+       uint32_t curaddr = address;
+       int status;
+
+       if (address & 0xF) {
+               LOG_WARNING("address 0x%" PRIx32 "breaks required 16-byte 
alignment for PIC32MZ quad-word programming",
+                                               address);
+               return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
+       }
+
+       while (words_remaining != 0) {
+               uint32_t chunk_words = (words_remaining > 4 ? 4 : 
words_remaining);
+               uint32_t words[4];
+
+               for (uint32_t i = 0; i < 4; i++) {
+                       if (chunk_words > i)
+                               memcpy(&words[i], buffer + (curaddr - address) 
+ (i * 4), sizeof(uint32_t));
+                       else
+                               words[i] = 0xFFFFFFFF;
+               }
+
+               status = pic32mz_write_quad_word(bank, curaddr, words[0], 
words[1], words[2], words[3]);
+
+               if (status & NVMCON_NVMERR) {
+                       LOG_ERROR("Flash write error NVMERR (status = 0x%08" 
PRIx32 ")", status);
+                       return ERROR_FLASH_OPERATION_FAILED;
+               }
+
+               if (status & NVMCON_LVDERR) {
+                       LOG_ERROR("Flash write error LVDERR (status = 0x%08" 
PRIx32 ")", status);
+                       return ERROR_FLASH_OPERATION_FAILED;
+               }
+
+               curaddr += chunk_words * 4;
+               words_remaining -= chunk_words;
+       }
+
+       return ERROR_OK;
+}
+
+static int pic32mz_write(struct flash_bank *bank, const uint8_t *buffer, 
uint32_t offset, uint32_t count)
+{
+       uint32_t words_remaining = (count / 4);
+       uint32_t bytes_remaining = (count & 0x00000003);
+       uint32_t address = bank->base + offset;
+       uint32_t bytes_written = 0;
+       uint32_t status;
+       int retval;
+       uint32_t cfgcon_address;
+       uint32_t cfgcon;
+       bool ecc_is_on;
+
+       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);
+
+       cfgcon_address = PIC32MZ_CFGCON;
+       target_read_u32(bank->target, cfgcon_address, &cfgcon);
+       ecc_is_on = ((cfgcon & CFGCON_ECC) == 0);
+
+       if (ecc_is_on || virt2phys(bank->base) == PIC32MZ_PHYS_LOWER_BOOT_ALIAS
+                                       || virt2phys(bank->base) == 
PIC32MZ_PHYS_UPPER_BOOT_ALIAS)
+               return pic32mz_write_quad_words(bank, address, buffer, count / 
4);
+
+       if (offset & 0x3) {
+               LOG_WARNING("offset 0x%" PRIx32 "breaks required 4-byte 
alignment", offset);
+               return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
+       }
+
+       /* multiple words (4-byte) to be programmed? */
+       if (words_remaining > 0) {
+               /* try using a block write */
+               retval = pic32mz_write_block(bank, buffer, offset, 
words_remaining);
+               if (retval != ERROR_OK) {
+                       if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
+                               /* if block write failed (no sufficient working 
area),
+                                * we use normal (slow) single dword accesses */
+                               LOG_WARNING("couldn't use block writes, falling 
back to single memory accesses");
+                       } else if (retval == ERROR_FLASH_OPERATION_FAILED) {
+                               LOG_ERROR("flash writing failed");
+                               return retval;
+                       }
+               } else {
+                       buffer += words_remaining * 4;
+                       address += words_remaining * 4;
+                       words_remaining = 0;
+               }
+       }
+
+       while (words_remaining > 0) {
+               uint32_t value;
+               memcpy(&value, buffer + bytes_written, sizeof(uint32_t));
+
+               status = pic32mz_write_word(bank, address, value);
+
+               if (status & NVMCON_NVMERR) {
+                       LOG_ERROR("Flash write error NVMERR (status = 0x%08" 
PRIx32 ")", status);
+                       return ERROR_FLASH_OPERATION_FAILED;
+               }
+
+               if (status & NVMCON_LVDERR) {
+                       LOG_ERROR("Flash write error LVDERR (status = 0x%08" 
PRIx32 ")", status);
+                       return ERROR_FLASH_OPERATION_FAILED;
+               }
+
+               bytes_written += 4;
+               words_remaining--;
+               address += 4;
+       }
+
+       if (bytes_remaining) {
+               uint32_t value = 0xffffffff;
+               memcpy(&value, buffer + bytes_written, bytes_remaining);
+
+               status = pic32mz_write_word(bank, address, value);
+
+               if (status & NVMCON_NVMERR) {
+                       LOG_ERROR("Flash write error NVMERR (status = 0x%08" 
PRIx32 ")", status);
+                       return ERROR_FLASH_OPERATION_FAILED;
+               }
+
+               if (status & NVMCON_LVDERR) {
+                       LOG_ERROR("Flash write error LVDERR (status = 0x%08" 
PRIx32 ")", status);
+                       return ERROR_FLASH_OPERATION_FAILED;
+               }
+       }
+
+       return ERROR_OK;
+}
+
+static int pic32mz_probe(struct flash_bank *bank)
+{
+       struct target *target = bank->target;
+       struct pic32mz_flash_bank *pic32mz_info = bank->driver_priv;
+       struct mips32_common *mips32 = target->arch_info;
+       struct mips_ejtag *ejtag_info = &mips32->ejtag_info;
+       int i;
+       uint32_t num_pages = 0;
+       uint32_t num_bytes = 0;
+       uint32_t device_id;
+       int page_size;
+       const struct pic32mz_devs_s *pdev = NULL;
+
+       pic32mz_info->probed = 0;
+
+       device_id = ejtag_info->idcode;
+       LOG_INFO("device id = 0x%08" PRIx32 " (manuf 0x%03x dev 0x%04x, ver 
0x%02x)",
+                         device_id,
+                         (unsigned int)((device_id >> 1) & 0x7ff),
+                         (unsigned int)((device_id >> 12) & 0xffff),
+                         (unsigned int)((device_id >> 28) & 0xf));
+
+       if (((device_id >> 1) & 0x7ff) != PIC32MZ_MANUF_ID) {
+               LOG_WARNING("Cannot identify target as a PIC32MZ family.");
+               return ERROR_FLASH_OPERATION_FAILED;
+       }
+
+       page_size = 16 * 1024;  // fixed size pages for PIC32MZ
+
+       pdev = pic32mz_lookup_device(device_id);
+       if (virt2phys(bank->base) == PIC32MZ_PHYS_LOWER_BOOT_ALIAS
+               || virt2phys(bank->base) == PIC32MZ_PHYS_UPPER_BOOT_ALIAS) {
+               /* 0x1FC00000 or 0x1FC20000: Boot flash size */
+               num_bytes = PIC32MZ_BOOT_BLOCK_NUMBYTES;
+       } else if (pdev != NULL) {
+               num_bytes = (pdev->kb_program_flash * 1024);
+       } else {
+               num_bytes = (512 * 1024);  /* unknown device, assume 512 KB 
program flash */
+       }
+
+       LOG_INFO("flash size = %" PRId32 "kbytes", num_bytes / 1024);
+
+       if (bank->sectors) {
+               free(bank->sectors);
+               bank->sectors = NULL;
+       }
+
+       /* calculate numbers of pages */
+       num_pages = num_bytes / page_size;
+       bank->size = (num_pages * page_size);
+       bank->num_sectors = num_pages;
+       bank->sectors = malloc(sizeof(struct flash_sector) * num_pages);
+
+       for (i = 0; i < (int)num_pages; i++) {
+               bank->sectors[i].offset = i * page_size;
+               bank->sectors[i].size = page_size;
+               bank->sectors[i].is_erased = -1;
+               bank->sectors[i].is_protected = 1;
+       }
+
+       pic32mz_protect_check(bank);
+
+       pic32mz_info->probed = 1;
+
+       return ERROR_OK;
+}
+
+static int pic32mz_auto_probe(struct flash_bank *bank)
+{
+       struct pic32mz_flash_bank *pic32mz_info = bank->driver_priv;
+       if (pic32mz_info->probed)
+               return ERROR_OK;
+       return pic32mz_probe(bank);
+}
+
+static int pic32mz_info(struct flash_bank *bank, struct command_invocation 
*cmd)
+{
+       struct target *target = bank->target;
+       struct mips32_common *mips32 = target->arch_info;
+       struct mips_ejtag *ejtag_info = &mips32->ejtag_info;
+       uint32_t device_id;
+       size_t i;
+
+       device_id = ejtag_info->idcode;
+
+       if (((device_id >> 1) & 0x7ff) != PIC32MZ_MANUF_ID) {
+               command_print_sameline(cmd,
+                                "Cannot identify target as a PIC32MZ family 
(manufacturer 0x%03x != 0x%03x)\n",
+                                (unsigned int)((device_id >> 1) & 0x7ff),
+                                PIC32MZ_MANUF_ID);
+               return ERROR_FLASH_OPERATION_FAILED;
+       }
+
+       for (i = 0; pic32mz_devs[i].name != NULL; i++) {
+               if (pic32mz_devs[i].devid == (device_id & 0x0fffffff)) {
+                       command_print_sameline(cmd, "PIC32MZ%s", 
pic32mz_devs[i].name);
+                       break;
+               }
+       }
+
+       if (pic32mz_devs[i].name == NULL)
+               command_print_sameline(cmd, "Unknown");
+
+
+       command_print_sameline(cmd, " Ver: 0x%02x",
+                       (unsigned int)((device_id >> 28) & 0xf));
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(pic32mz_handle_pgm_word_command)
+{
+       uint32_t address, value;
+       int status, res;
+
+       if (CMD_ARGC != 3)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], address);
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value);
+
+       struct flash_bank *bank;
+       int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 2, &bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       if (address < bank->base || address >= (bank->base + bank->size)) {
+               command_print(CMD, "flash address '%s' is out of bounds", 
CMD_ARGV[0]);
+               return ERROR_OK;
+       }
+
+       res = ERROR_OK;
+       status = pic32mz_write_word(bank, address, value);
+       if (status & NVMCON_NVMERR)
+               res = ERROR_FLASH_OPERATION_FAILED;
+       if (status & NVMCON_LVDERR)
+               res = ERROR_FLASH_OPERATION_FAILED;
+
+       if (res == ERROR_OK)
+               command_print(CMD, "pic32mz pgm word complete");
+       else
+               command_print(CMD, "pic32mz pgm word failed (status = 0x%x)", 
status);
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(pic32mz_handle_unlock_command)
+{
+       uint32_t mchip_cmd;
+       struct target *target = NULL;
+       struct mips_m4k_common *mips_m4k;
+       struct mips_ejtag *ejtag_info;
+       int timeout = 10;
+
+       if (CMD_ARGC < 1) {
+               command_print(CMD, "pic32mz unlock <bank>");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       struct flash_bank *bank;
+       int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       target = bank->target;
+       mips_m4k = target_to_m4k(target);
+       ejtag_info = &mips_m4k->mips32.ejtag_info;
+
+       /* we have to use the MTAP to perform a full erase */
+       mips_ejtag_set_instr(ejtag_info, MTAP_SW_MTAP);
+       mips_ejtag_set_instr(ejtag_info, MTAP_COMMAND);
+
+       /* first check status of device */
+       mchip_cmd = MCHP_STATUS;
+       mips_ejtag_drscan_8_out(ejtag_info, mchip_cmd);
+       if (mchip_cmd & (1 << 7)) {
+               /* device is not locked */
+               command_print(CMD, "pic32mz is already unlocked, erasing 
anyway");
+       }
+
+       /* unlock/erase device */
+       mips_ejtag_drscan_8_out(ejtag_info, MCHP_ASERT_RST);
+       jtag_add_sleep(200);
+
+       mips_ejtag_drscan_8_out(ejtag_info, MCHP_ERASE);
+
+       do {
+               mchip_cmd = MCHP_STATUS;
+               mips_ejtag_drscan_8_out(ejtag_info, mchip_cmd);
+               if (timeout-- == 0) {
+                       LOG_DEBUG("timeout waiting for unlock: 0x%" PRIx32 "", 
mchip_cmd);
+                       break;
+               }
+               alive_sleep(1);
+       } while ((mchip_cmd & (1 << 2)) || (!(mchip_cmd & (1 << 3))));
+
+       mips_ejtag_drscan_8_out(ejtag_info, MCHP_DE_ASSERT_RST);
+
+       /* select ejtag tap */
+       mips_ejtag_set_instr(ejtag_info, MTAP_SW_ETAP);
+
+       command_print(CMD, "pic32mz unlocked.\n"
+                       "INFO: a reset or power cycle is required "
+                       "for the new settings to take effect.");
+
+       return ERROR_OK;
+}
+
+static const struct command_registration pic32mz_exec_command_handlers[] = {
+       {
+               .name = "pgm_word",
+               .usage = "<addr> <value> <bank>",
+               .handler = pic32mz_handle_pgm_word_command,
+               .mode = COMMAND_EXEC,
+               .help = "program a word",
+       },
+       {
+               .name = "unlock",
+               .handler = pic32mz_handle_unlock_command,
+               .mode = COMMAND_EXEC,
+               .usage = "[bank_id]",
+               .help = "Unlock/Erase entire device.",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration pic32mz_command_handlers[] = {
+       {
+               .name = "pic32mz",
+               .mode = COMMAND_ANY,
+               .help = "pic32mz flash command group",
+               .usage = "",
+               .chain = pic32mz_exec_command_handlers,
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+const struct flash_driver pic32mz_flash = {
+       .name = "pic32mz",
+       .commands = pic32mz_command_handlers,
+       .flash_bank_command = pic32mz_flash_bank_command,
+       .erase = pic32mz_erase,
+       .protect = pic32mz_protect,
+       .write = pic32mz_write,
+       .read = default_flash_read,
+       .probe = pic32mz_probe,
+       .auto_probe = pic32mz_auto_probe,
+       .erase_check = default_flash_blank_check,
+       .protect_check = pic32mz_protect_check,
+       .info = pic32mz_info,
+};
diff --git a/tcl/target/pic32mz.cfg b/tcl/target/pic32mz.cfg
new file mode 100644
index 0000000000..a0851a82f2
--- /dev/null
+++ b/tcl/target/pic32mz.cfg
@@ -0,0 +1,93 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+if { [info exists CHIPNAME] } {
+       set _CHIPNAME $CHIPNAME
+} else {
+       set _CHIPNAME pic32mz
+}
+
+if { [info exists ENDIAN] } {
+       set _ENDIAN $ENDIAN
+} else {
+       set _ENDIAN little
+}
+
+if { [info exists CPUTAPID] } {
+       set _CPUTAPID $CPUTAPID
+} else {
+       set _CPUTAPID 0x25127053
+}
+
+# default working area is 16384
+if { [info exists WORKAREASIZE] } {
+       set _WORKAREASIZE $WORKAREASIZE
+} else {
+       set _WORKAREASIZE 0x4000
+}
+
+adapter srst delay 500
+jtag_ntrst_delay 500
+reset_config none
+
+#jtag scan chain
+#format L IRC IRCM IDCODE (Length, IR Capture, IR Capture Mask, IDCODE)
+jtag newtap $_CHIPNAME cpu -irlen 5 -ircapture 0x1 -irmask 0x1f -expected-id 
$_CPUTAPID
+
+set _TARGETNAME $_CHIPNAME.cpu
+target create $_TARGETNAME mips_mAptiv -endian $_ENDIAN -chain-position 
$_TARGETNAME
+
+mips32 scan_delay 5000
+
+#
+# On PIC32MZ devices, a new System Bus is utilized that supports
+# using RAM memory for program or data without the need for
+# special configuration. Therefore, no special registers are
+# associated with the System Bus to configure these features.
+#
+
+global _PIC32MZ_DATASIZE
+global _WORKAREASIZE
+set _PIC32MZ_DATASIZE 0x800
+set _PIC32MZ_PROGSIZE [expr {($_WORKAREASIZE - $_PIC32MZ_DATASIZE)}]
+
+$_TARGETNAME configure -work-area-phys 0x80018000 -work-area-size 0x1000 
-work-area-backup 0
+
+$_TARGETNAME configure -event reset-init {
+       global _PIC32MZ_DATASIZE
+       global _WORKAREASIZE
+
+       #
+       # Set system clock to 8MHz if the default clock configuration is set
+       #
+
+       # SYSKEY register, make sure OSCCON is locked
+       mww 0xbf800030 0x0
+       # SYSKEY register, write unlock sequence
+       mww 0xbf800030 0xaa996655
+       mww 0xbf800030 0x556699aa
+       # OSCCON register + 4, clear OSCCON FRCDIV bits: 24, 25 and 26, divided 
by 1
+       mww 0xbf801204 0x07000000
+       # SYSKEY register, relock OSCCON
+       mww 0xbf800030 0x0
+}
+
+set _FLASHNAME $_CHIPNAME.flash0
+flash bank $_FLASHNAME pic32mz 0x1fc00000 0 0 0 $_TARGETNAME
+
+# add virtual banks for kseg0 and kseg1
+flash bank vbank0 virtual 0xbfc00000 0 0 0 $_TARGETNAME $_FLASHNAME
+flash bank vbank1 virtual 0x9fc00000 0 0 0 $_TARGETNAME $_FLASHNAME
+
+set _FLASHNAME $_CHIPNAME.flash0_upper
+flash bank $_FLASHNAME pic32mz 0x1fc20000 0 0 0 $_TARGETNAME
+
+# add virtual banks for kseg0 and kseg1
+flash bank vbank0_upper virtual 0xbfc20000 0 0 0 $_TARGETNAME $_FLASHNAME
+flash bank vbank1_upper virtual 0x9fc20000 0 0 0 $_TARGETNAME $_FLASHNAME
+
+set _FLASHNAME $_CHIPNAME.flash1
+flash bank $_FLASHNAME pic32mz 0x1d000000 0 0 0 $_TARGETNAME
+
+# add virtual banks for kseg0 and kseg1
+flash bank vbank2 virtual 0xbd000000 0 0 0 $_TARGETNAME $_FLASHNAME
+flash bank vbank3 virtual 0x9d000000 0 0 0 $_TARGETNAME $_FLASHNAME

-- 


Reply via email to