nrf51: Implement the support for Nordic's nRF51 devices 45/1945/11
authorAndrey Smirnov <andrew.smirnov@gmail.com>
Fri, 7 Feb 2014 19:54:57 +0000 (11:54 -0800)
committerSpencer Oliver <spen@spen-soft.co.uk>
Mon, 24 Feb 2014 12:09:04 +0000 (12:09 +0000)
Add support for Nordic's nRF51 chip series. Tested with nRF51822.

Change-Id: Id70f6fd76888cc595a353aefb84d25c4cd325d7d
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
Reviewed-on: http://openocd.zylin.com/1945
Tested-by: jenkins
Reviewed-by: Andrey Yurovsky <yurovsky@gmail.com>
Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk>
README
doc/openocd.texi
src/flash/nor/Makefile.am
src/flash/nor/drivers.c
src/flash/nor/nrf51.c [new file with mode: 0644]
tcl/target/nrf51_stlink.tcl [new file with mode: 0644]

diff --git a/README b/README
index a9ccd0018d8da0a12aa76818332b749748f380f5..287cc922f5494399e849a9486fbac194b2f65666 100644 (file)
--- a/README
+++ b/README
@@ -124,7 +124,7 @@ Flash drivers
 
 ADUC702x, AT91SAM, AVR, CFI, DSP5680xx, EFM32, EM357, FM3, Kinetis,
 LPC2000, LPC2900, LPCSPIFI, Milandr, NuMicro, PIC32mx, Stellaris,
 
 ADUC702x, AT91SAM, AVR, CFI, DSP5680xx, EFM32, EM357, FM3, Kinetis,
 LPC2000, LPC2900, LPCSPIFI, Milandr, NuMicro, PIC32mx, Stellaris,
-STM32, STMSMI, STR7x, STR9x; NAND controllers of AT91SAM9, LPC3180,
+STM32, STMSMI, STR7x, STR9x, nRF51; NAND controllers of AT91SAM9, LPC3180,
 LPC32xx, i.MX31, MXC, NUC910, Orion/Kirkwood, S3C24xx, S3C6400.
 
 
 LPC32xx, i.MX31, MXC, NUC910, Orion/Kirkwood, S3C24xx, S3C6400.
 
 
index 76e335b8f23c2466606b37baaa65a18b4b759223..814a239ac8b8f391cea0e1d4dd3049bfe4405975 100644 (file)
@@ -5687,6 +5687,23 @@ unlock str9 device.
 
 @end deffn
 
 
 @end deffn
 
+@deffn {Flash Driver} nrf51
+All members of the nRF51 microcontroller families from Nordic Semiconductor
+include internal flash and use ARM Cortex-M0 core.
+
+@example
+flash bank $_FLASHNAME nrf51 0 0x00000000 0 0 $_TARGETNAME
+@end example
+
+Some nrf51-specific commands are defined:
+
+@deffn Command {nrf51 mass_erase}
+Erases the contents of the code memory and user information
+configuration registers as well. It must be noted that this command
+works only for chips that do not have factory pre-programmed region 0
+code.
+@end deffn
+@end deffn
 
 @section mFlash
 
 
 @section mFlash
 
index d817cc69cf0ac08ae0b0419756dc3f52b8711a0e..316814738d5e5e9518e63483a37b56bb79844fb9 100644 (file)
@@ -42,7 +42,8 @@ NOR_DRIVERS = \
        dsp5680xx_flash.c \
        kinetis.c \
        mini51.c \
        dsp5680xx_flash.c \
        kinetis.c \
        mini51.c \
-       nuc1x.c
+       nuc1x.c \
+       nrf51.c
 
 noinst_HEADERS = \
        core.h \
 
 noinst_HEADERS = \
        core.h \
index 39c2f9fcd7ea949062e000e8713b3a82a06e95a7..ed631a3b0ba812e43ea6d61f7370c0653fb87e2b 100644 (file)
@@ -55,6 +55,7 @@ extern struct flash_driver efm32_flash;
 extern struct flash_driver mdr_flash;
 extern struct flash_driver mini51_flash;
 extern struct flash_driver nuc1x_flash;
 extern struct flash_driver mdr_flash;
 extern struct flash_driver mini51_flash;
 extern struct flash_driver nuc1x_flash;
+extern struct flash_driver nrf51_flash;
 
 /**
  * The list of built-in flash drivers.
 
 /**
  * The list of built-in flash drivers.
@@ -94,6 +95,7 @@ static struct flash_driver *flash_drivers[] = {
        &mdr_flash,
        &mini51_flash,
        &nuc1x_flash,
        &mdr_flash,
        &mini51_flash,
        &nuc1x_flash,
+       &nrf51_flash,
        NULL,
 };
 
        NULL,
 };
 
diff --git a/src/flash/nor/nrf51.c b/src/flash/nor/nrf51.c
new file mode 100644 (file)
index 0000000..79e6d49
--- /dev/null
@@ -0,0 +1,790 @@
+/***************************************************************************
+ *   Copyright (C) 2013 Synapse Product Development                        *
+ *   Andrey Smirnov <andrew.smironv@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, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.           *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "imp.h"
+
+enum {
+       NRF51_FLASH_BASE = 0x00000000,
+};
+
+enum nrf51_ficr_registers {
+       NRF51_FICR_BASE = 0x10000000, /* Factory Information Configuration Registers */
+
+#define NRF51_FICR_REG(offset) (NRF51_FICR_BASE + offset)
+
+       NRF51_FICR_CODEPAGESIZE         = NRF51_FICR_REG(0x010),
+       NRF51_FICR_CODESIZE             = NRF51_FICR_REG(0x014),
+       NRF51_FICR_CLENR0               = NRF51_FICR_REG(0x028),
+       NRF51_FICR_PPFC                 = NRF51_FICR_REG(0x02C),
+       NRF51_FICR_NUMRAMBLOCK          = NRF51_FICR_REG(0x034),
+       NRF51_FICR_SIZERAMBLOCK0        = NRF51_FICR_REG(0x038),
+       NRF51_FICR_SIZERAMBLOCK1        = NRF51_FICR_REG(0x03C),
+       NRF51_FICR_SIZERAMBLOCK2        = NRF51_FICR_REG(0x040),
+       NRF51_FICR_SIZERAMBLOCK3        = NRF51_FICR_REG(0x044),
+       NRF51_FICR_CONFIGID             = NRF51_FICR_REG(0x05C),
+       NRF51_FICR_DEVICEID0            = NRF51_FICR_REG(0x060),
+       NRF51_FICR_DEVICEID1            = NRF51_FICR_REG(0x064),
+       NRF51_FICR_ER0                  = NRF51_FICR_REG(0x080),
+       NRF51_FICR_ER1                  = NRF51_FICR_REG(0x084),
+       NRF51_FICR_ER2                  = NRF51_FICR_REG(0x088),
+       NRF51_FICR_ER3                  = NRF51_FICR_REG(0x08C),
+       NRF51_FICR_IR0                  = NRF51_FICR_REG(0x090),
+       NRF51_FICR_IR1                  = NRF51_FICR_REG(0x094),
+       NRF51_FICR_IR2                  = NRF51_FICR_REG(0x098),
+       NRF51_FICR_IR3                  = NRF51_FICR_REG(0x09C),
+       NRF51_FICR_DEVICEADDRTYPE       = NRF51_FICR_REG(0x0A0),
+       NRF51_FICR_DEVICEADDR0          = NRF51_FICR_REG(0x0A4),
+       NRF51_FICR_DEVICEADDR1          = NRF51_FICR_REG(0x0A8),
+       NRF51_FICR_OVERRIDEN            = NRF51_FICR_REG(0x0AC),
+       NRF51_FICR_NRF_1MBIT0           = NRF51_FICR_REG(0x0B0),
+       NRF51_FICR_NRF_1MBIT1           = NRF51_FICR_REG(0x0B4),
+       NRF51_FICR_NRF_1MBIT2           = NRF51_FICR_REG(0x0B8),
+       NRF51_FICR_NRF_1MBIT3           = NRF51_FICR_REG(0x0BC),
+       NRF51_FICR_NRF_1MBIT4           = NRF51_FICR_REG(0x0C0),
+       NRF51_FICR_BLE_1MBIT0           = NRF51_FICR_REG(0x0EC),
+       NRF51_FICR_BLE_1MBIT1           = NRF51_FICR_REG(0x0F0),
+       NRF51_FICR_BLE_1MBIT2           = NRF51_FICR_REG(0x0F4),
+       NRF51_FICR_BLE_1MBIT3           = NRF51_FICR_REG(0x0F8),
+       NRF51_FICR_BLE_1MBIT4           = NRF51_FICR_REG(0x0FC),
+};
+
+enum nrf51_uicr_registers {
+       NRF51_UICR_BASE = 0x10001000, /* User Information
+                                      * Configuration Regsters */
+
+#define NRF51_UICR_REG(offset) (NRF51_UICR_BASE + offset)
+
+       NRF51_UICR_CLENR0       = NRF51_UICR_REG(0x000),
+       NRF51_UICR_RBPCONF      = NRF51_UICR_REG(0x004),
+       NRF51_UICR_XTALFREQ     = NRF51_UICR_REG(0x008),
+       NRF51_UICR_FWID         = NRF51_UICR_REG(0x010),
+};
+
+enum nrf51_nvmc_registers {
+       NRF51_NVMC_BASE = 0x4001E000, /* Non-Volatile Memory
+                                      * Controller Regsters */
+
+#define NRF51_NVMC_REG(offset) (NRF51_NVMC_BASE + offset)
+
+       NRF51_NVMC_READY        = NRF51_NVMC_REG(0x400),
+       NRF51_NVMC_CONFIG       = NRF51_NVMC_REG(0x504),
+       NRF51_NVMC_ERASEPAGE    = NRF51_NVMC_REG(0x508),
+       NRF51_NVMC_ERASEALL     = NRF51_NVMC_REG(0x50C),
+       NRF51_NVMC_ERASEUICR    = NRF51_NVMC_REG(0x514),
+};
+
+enum nrf51_nvmc_config_bits {
+       NRF51_NVMC_CONFIG_REN = 0x00,
+       NRF51_NVMC_CONFIG_WEN = 0x01,
+       NRF51_NVMC_CONFIG_EEN = 0x02,
+
+};
+
+struct nrf51_info {
+       uint32_t code_page_size;
+       uint32_t code_memory_size;
+
+       bool probed;
+       struct target *target;
+};
+
+static int nrf51_probe(struct flash_bank *bank);
+
+static int nrf51_get_probed_chip_if_halted(struct flash_bank *bank, struct nrf51_info **chip)
+{
+       if (bank->target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       *chip = (struct nrf51_info *)bank->driver_priv;
+
+       if (!(*chip)->probed)
+               return nrf51_probe(bank);
+
+       return ERROR_OK;
+}
+
+static int nrf51_wait_for_nvmc(struct nrf51_info *chip)
+{
+       uint32_t ready;
+       int res;
+       int timeout = 100;
+
+       do {
+               res = target_read_u32(chip->target, NRF51_NVMC_READY, &ready);
+               if (res != ERROR_OK) {
+                       LOG_ERROR("Couldn't read NVMC_READY register");
+                       return res;
+               }
+
+               if (ready == 0x00000001)
+                       return ERROR_OK;
+
+               alive_sleep(1);
+       } while (timeout--);
+
+       return ERROR_FLASH_BUSY;
+}
+
+static int nrf51_nvmc_erase_enable(struct nrf51_info *chip)
+{
+       int res;
+       res = target_write_u32(chip->target,
+                              NRF51_NVMC_CONFIG,
+                              NRF51_NVMC_CONFIG_EEN);
+
+       if (res != ERROR_OK) {
+               LOG_ERROR("Failed to enable erase operation");
+               return res;
+       }
+
+       /*
+         According to NVMC examples in Nordic SDK busy status must be
+         checked after writing to NVMC_CONFIG
+        */
+       res = nrf51_wait_for_nvmc(chip);
+       if (res != ERROR_OK)
+               LOG_ERROR("Erase enable did not complete");
+
+       return res;
+}
+
+static int nrf51_nvmc_write_enable(struct nrf51_info *chip)
+{
+       int res;
+       res = target_write_u32(chip->target,
+                              NRF51_NVMC_CONFIG,
+                              NRF51_NVMC_CONFIG_WEN);
+
+       if (res != ERROR_OK) {
+               LOG_ERROR("Failed to enable write operation");
+               return res;
+       }
+
+       /*
+         According to NVMC examples in Nordic SDK busy status must be
+         checked after writing to NVMC_CONFIG
+        */
+       res = nrf51_wait_for_nvmc(chip);
+       if (res != ERROR_OK)
+               LOG_ERROR("Write enable did not complete");
+
+       return res;
+}
+
+static int nrf51_nvmc_read_only(struct nrf51_info *chip)
+{
+       int res;
+       res = target_write_u32(chip->target,
+                              NRF51_NVMC_CONFIG,
+                              NRF51_NVMC_CONFIG_REN);
+
+       if (res != ERROR_OK) {
+               LOG_ERROR("Failed to enable read-only operation");
+               return res;
+       }
+       /*
+         According to NVMC examples in Nordic SDK busy status must be
+         checked after writing to NVMC_CONFIG
+        */
+       res = nrf51_wait_for_nvmc(chip);
+       if (res != ERROR_OK)
+               LOG_ERROR("Read only enable did not complete");
+
+       return res;
+}
+
+static int nrf51_nvmc_generic_erase(struct nrf51_info *chip,
+                              uint32_t erase_register, uint32_t erase_value)
+{
+       int res;
+
+       res = nrf51_nvmc_erase_enable(chip);
+       if (res != ERROR_OK)
+               goto error;
+
+       res = target_write_u32(chip->target,
+                              erase_register,
+                              erase_value);
+       if (res != ERROR_OK)
+               goto set_read_only;
+
+       res = nrf51_wait_for_nvmc(chip);
+       if (res != ERROR_OK)
+               goto set_read_only;
+
+       return nrf51_nvmc_read_only(chip);
+
+set_read_only:
+       nrf51_nvmc_read_only(chip);
+error:
+       LOG_ERROR("Failed to erase reg: 0x%08"PRIx32" val: 0x%08"PRIx32,
+                 erase_register, erase_value);
+       return ERROR_FAIL;
+}
+
+static int nrf51_protect_check(struct flash_bank *bank)
+{
+       int res;
+       uint32_t clenr0;
+
+       struct nrf51_info *chip = (struct nrf51_info *)bank->driver_priv;
+
+       assert(chip != NULL);
+
+       res = target_read_u32(chip->target, NRF51_FICR_CLENR0,
+                             &clenr0);
+       if (res != ERROR_OK) {
+               LOG_ERROR("Couldn't read code region 0 size[FICR]");
+               return res;
+       }
+
+       if (clenr0 == 0xFFFFFFFF) {
+               res = target_read_u32(chip->target, NRF51_UICR_CLENR0,
+                                     &clenr0);
+               if (res != ERROR_OK) {
+                       LOG_ERROR("Couldn't read code region 0 size[UICR]");
+                       return res;
+               }
+       }
+
+       for (int i = 0; i < bank->num_sectors; i++)
+               bank->sectors[i].is_protected =
+                       clenr0 != 0xFFFFFFFF && bank->sectors[i].offset < clenr0;
+
+       return ERROR_OK;
+}
+
+static int nrf51_protect(struct flash_bank *bank, int set, int first, int last)
+{
+       int res;
+       uint32_t clenr0, ppfc;
+       struct nrf51_info *chip;
+
+       res = nrf51_get_probed_chip_if_halted(bank, &chip);
+       if (res != ERROR_OK)
+               return res;
+
+       if (first != 0) {
+               LOG_ERROR("Code region 0 must start at the begining of the bank");
+               return ERROR_FAIL;
+       }
+
+       res = target_read_u32(chip->target, NRF51_FICR_PPFC,
+                             &ppfc);
+       if (res != ERROR_OK) {
+               LOG_ERROR("Couldn't read PPFC register");
+               return res;
+       }
+
+       if ((ppfc & 0xFF) == 0x00) {
+               LOG_ERROR("Code region 0 size was pre-programmed at the factory, can't change flash protection settings");
+               return ERROR_FAIL;
+       };
+
+       res = target_read_u32(chip->target, NRF51_UICR_CLENR0,
+                             &clenr0);
+       if (res != ERROR_OK) {
+               LOG_ERROR("Couldn't read code region 0 size[UICR]");
+               return res;
+       }
+
+       if (clenr0 == 0xFFFFFFFF) {
+               res = target_write_u32(chip->target, NRF51_UICR_CLENR0,
+                                      clenr0);
+               if (res != ERROR_OK) {
+                       LOG_ERROR("Couldn't write code region 0 size[UICR]");
+                       return res;
+               }
+
+       } else {
+               LOG_ERROR("You need to perform chip erase before changing the protection settings");
+       }
+
+       nrf51_protect_check(bank);
+
+       return ERROR_OK;
+}
+
+static int nrf51_probe(struct flash_bank *bank)
+{
+       uint32_t id;
+       int res;
+       struct nrf51_info *chip = (struct nrf51_info *)bank->driver_priv;
+
+       res = target_read_u32(chip->target, NRF51_FICR_DEVICEID0, &id);
+       if (res != ERROR_OK) {
+               LOG_ERROR("Couldn't read Device ID 0 register");
+               return res;
+       }
+
+       res = target_read_u32(chip->target, NRF51_FICR_DEVICEID1, &id);
+       if (res != ERROR_OK) {
+               LOG_ERROR("Couldn't read Device ID 1 register");
+               return res;
+       }
+
+       res = target_read_u32(chip->target, NRF51_FICR_CODEPAGESIZE,
+                             &chip->code_page_size);
+       if (res != ERROR_OK) {
+               LOG_ERROR("Couldn't read code page size");
+               return res;
+       }
+
+       res = target_read_u32(chip->target, NRF51_FICR_CODESIZE,
+                             &chip->code_memory_size);
+       if (res != ERROR_OK) {
+               LOG_ERROR("Couldn't read code memory size");
+               return res;
+       }
+
+       bank->size = chip->code_memory_size * 1024;
+       bank->num_sectors = bank->size / chip->code_page_size;
+       bank->sectors = calloc(bank->num_sectors,
+                              sizeof((bank->sectors)[0]));
+       if (!bank->sectors)
+               return ERROR_FLASH_BANK_NOT_PROBED;
+
+       /* Fill out the sector information: all NRF51 sectors are the same size and
+        * there is always a fixed number of them. */
+       for (int i = 0; i < bank->num_sectors; i++) {
+               bank->sectors[i].size = chip->code_page_size;
+               bank->sectors[i].offset = i * chip->code_page_size;
+
+               /* mark as unknown */
+               bank->sectors[i].is_erased = -1;
+               bank->sectors[i].is_protected = -1;
+       }
+
+       nrf51_protect_check(bank);
+
+       chip->probed = true;
+
+       return ERROR_OK;
+}
+
+static int nrf51_auto_probe(struct flash_bank *bank)
+{
+       struct nrf51_info *chip = (struct nrf51_info *)bank->driver_priv;
+
+       if (chip->probed)
+               return ERROR_OK;
+
+       return nrf51_probe(bank);
+}
+
+static struct flash_sector *nrf51_find_sector_by_address(struct flash_bank *bank, uint32_t address)
+{
+       struct nrf51_info *chip = (struct nrf51_info *)bank->driver_priv;
+
+       for (int i = 0; i < bank->num_sectors; i++)
+               if (bank->sectors[i].offset <= address &&
+                   address < (bank->sectors[i].offset + chip->code_page_size))
+                       return &bank->sectors[i];
+       return NULL;
+}
+
+static int nrf51_erase_all(struct nrf51_info *chip)
+{
+       return nrf51_nvmc_generic_erase(chip,
+                                       NRF51_NVMC_ERASEALL,
+                                       0x00000001);
+}
+
+static int nrf51_erase_page(struct nrf51_info *chip, struct flash_sector *sector)
+{
+       int res;
+
+       if (sector->is_protected)
+               return ERROR_FAIL;
+
+       res = nrf51_nvmc_generic_erase(chip,
+                                      NRF51_NVMC_ERASEPAGE,
+                                      sector->offset);
+       if (res == ERROR_OK)
+               sector->is_erased = 1;
+
+       return res;
+}
+
+static int nrf51_write_page(struct flash_bank *bank, uint32_t offset, uint8_t *buffer)
+{
+       assert(offset % 4 == 0);
+
+       int res = ERROR_FAIL;
+       struct nrf51_info *chip = (struct nrf51_info *)bank->driver_priv;
+       struct flash_sector *sector = nrf51_find_sector_by_address(bank, offset);
+
+       if (!sector)
+               goto error;
+
+       if (sector->is_protected)
+               goto error;
+
+       if (!sector->is_erased) {
+               res = nrf51_erase_page(chip, sector);
+               if (res != ERROR_OK)
+                       goto error;
+       }
+
+       res = nrf51_nvmc_write_enable(chip);
+       if (res != ERROR_OK)
+               goto error;
+
+       sector->is_erased = 0;
+       res = target_write_memory(bank->target, offset, 4,
+                                 chip->code_page_size / 4, buffer);
+       if (res != ERROR_OK)
+               goto set_read_only;
+
+       return nrf51_nvmc_read_only(chip);
+
+set_read_only:
+       nrf51_nvmc_read_only(chip);
+error:
+       LOG_ERROR("Failed to write sector @ 0x%08"PRIx32, sector->offset);
+       return res;
+}
+
+static int nrf51_erase(struct flash_bank *bank, int first, int last)
+{
+       int res;
+       struct nrf51_info *chip;
+
+       res = nrf51_get_probed_chip_if_halted(bank, &chip);
+       if (res != ERROR_OK)
+               return res;
+
+       /* For each sector to be erased */
+       for (int s = first; s <= last && res == ERROR_OK; s++)
+               res = nrf51_erase_page(chip, &bank->sectors[s]);
+
+       return res;
+}
+
+static int nrf51_write(struct flash_bank *bank, uint8_t *buffer,
+                      uint32_t offset, uint32_t count)
+{
+       int res;
+       struct {
+               uint32_t start, end;
+       } region;
+       struct nrf51_info *chip;
+
+       res = nrf51_get_probed_chip_if_halted(bank, &chip);
+       if (res != ERROR_OK)
+               return res;
+
+       region.start = offset;
+       region.end   = offset + count;
+
+       struct {
+               size_t   length;
+               uint8_t *buffer;
+       }  start_extra, end_extra;
+
+       start_extra.length      = region.start % chip->code_page_size;
+       start_extra.buffer      = buffer;
+       end_extra.length        = region.end  % chip->code_page_size;
+       end_extra.buffer        = buffer + count - end_extra.length;
+
+       if (start_extra.length) {
+               uint8_t page[chip->code_page_size];
+
+               res = target_read_memory(bank->target,
+                                        region.start - start_extra.length,
+                                        1, start_extra.length, page);
+               if (res != ERROR_OK)
+                       return res;
+
+               memcpy(page + start_extra.length,
+                      start_extra.buffer,
+                      chip->code_page_size - start_extra.length);
+
+               res = nrf51_write_page(bank,
+                                      region.start - start_extra.length,
+                                      page);
+               if (res != ERROR_OK)
+                       return res;
+       }
+
+       if (end_extra.length) {
+               uint8_t page[chip->code_page_size];
+
+               /* Retrieve the full row contents from Flash */
+               res = target_read_memory(bank->target,
+                                        region.end,
+                                        1,
+                                        (chip->code_page_size - end_extra.length),
+                                        page + end_extra.length);
+               if (res != ERROR_OK)
+                       return res;
+
+               memcpy(page, end_extra.buffer, end_extra.length);
+
+               res = nrf51_write_page(bank,
+                                      region.end - end_extra.length,
+                                      page);
+               if (res != ERROR_OK)
+                       return res;
+       }
+
+
+       region.start += start_extra.length;
+       region.end   -= end_extra.length;
+
+       for (uint32_t address = region.start; address < region.end;
+            address += chip->code_page_size) {
+               res = nrf51_write_page(bank, address, &buffer[address - region.start]);
+
+               if (res != ERROR_OK)
+                       return res;
+
+       }
+
+       return ERROR_OK;
+}
+
+FLASH_BANK_COMMAND_HANDLER(nrf51_flash_bank_command)
+{
+       struct nrf51_info *chip;
+
+       /* Create a new chip */
+       chip = calloc(1, sizeof(*chip));
+       if (!chip)
+               return ERROR_FAIL;
+
+       chip->target = bank->target;
+       chip->probed = false;
+
+       bank->driver_priv = chip;
+
+       if (bank->base != NRF51_FLASH_BASE) {
+               LOG_ERROR("Address 0x%08" PRIx32 " invalid bank address (try 0x%08" PRIx32
+                               "[nrf51 series] )",
+                               bank->base, NRF51_FLASH_BASE);
+               return ERROR_FAIL;
+       }
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(nrf51_handle_mass_erase_command)
+{
+       int res;
+       struct flash_bank *bank;
+
+       res = get_flash_bank_by_num(0, &bank);
+       if (res != ERROR_OK)
+               return res;
+
+       struct nrf51_info *chip;
+
+       res = nrf51_get_probed_chip_if_halted(bank, &chip);
+       if (res != ERROR_OK)
+               return res;
+
+       uint32_t ppfc;
+
+       res = target_read_u32(chip->target, NRF51_FICR_PPFC,
+                             &ppfc);
+       if (res != ERROR_OK) {
+               LOG_ERROR("Couldn't read PPFC register");
+               return res;
+       }
+
+       if ((ppfc & 0xFF) == 0x00) {
+               LOG_ERROR("Code region 0 size was pre-programmed at the factory, "
+                         "mass erase command won't work.");
+               return ERROR_FAIL;
+       };
+
+       res = nrf51_erase_all(chip);
+       if (res != ERROR_OK) {
+               LOG_ERROR("Failed to erase the chip");
+               nrf51_protect_check(bank);
+               return res;
+       }
+
+       for (int i = 0; i < bank->num_sectors; i++)
+               bank->sectors[i].is_erased = 1;
+
+       return nrf51_protect_check(bank);
+}
+
+static int nrf51_info(struct flash_bank *bank, char *buf, int buf_size)
+{
+       int res;
+
+       struct nrf51_info *chip;
+
+       res = nrf51_get_probed_chip_if_halted(bank, &chip);
+       if (res != ERROR_OK)
+               return res;
+
+       struct {
+               uint32_t address, value;
+       } ficr[] = {
+               { .address = NRF51_FICR_CODEPAGESIZE    },
+               { .address = NRF51_FICR_CODESIZE        },
+               { .address = NRF51_FICR_CLENR0          },
+               { .address = NRF51_FICR_PPFC            },
+               { .address = NRF51_FICR_NUMRAMBLOCK     },
+               { .address = NRF51_FICR_SIZERAMBLOCK0   },
+               { .address = NRF51_FICR_SIZERAMBLOCK1   },
+               { .address = NRF51_FICR_SIZERAMBLOCK2   },
+               { .address = NRF51_FICR_SIZERAMBLOCK3   },
+               { .address = NRF51_FICR_CONFIGID        },
+               { .address = NRF51_FICR_DEVICEID0       },
+               { .address = NRF51_FICR_DEVICEID1       },
+               { .address = NRF51_FICR_ER0             },
+               { .address = NRF51_FICR_ER1             },
+               { .address = NRF51_FICR_ER2             },
+               { .address = NRF51_FICR_ER3             },
+               { .address = NRF51_FICR_IR0             },
+               { .address = NRF51_FICR_IR1             },
+               { .address = NRF51_FICR_IR2             },
+               { .address = NRF51_FICR_IR3             },
+               { .address = NRF51_FICR_DEVICEADDRTYPE  },
+               { .address = NRF51_FICR_DEVICEADDR0     },
+               { .address = NRF51_FICR_DEVICEADDR1     },
+               { .address = NRF51_FICR_OVERRIDEN       },
+               { .address = NRF51_FICR_NRF_1MBIT0      },
+               { .address = NRF51_FICR_NRF_1MBIT1      },
+               { .address = NRF51_FICR_NRF_1MBIT2      },
+               { .address = NRF51_FICR_NRF_1MBIT3      },
+               { .address = NRF51_FICR_NRF_1MBIT4      },
+               { .address = NRF51_FICR_BLE_1MBIT0      },
+               { .address = NRF51_FICR_BLE_1MBIT1      },
+               { .address = NRF51_FICR_BLE_1MBIT2      },
+               { .address = NRF51_FICR_BLE_1MBIT3      },
+               { .address = NRF51_FICR_BLE_1MBIT4      },
+       }, uicr[] = {
+               { .address = NRF51_UICR_CLENR0,         },
+               { .address = NRF51_UICR_RBPCONF         },
+               { .address = NRF51_UICR_XTALFREQ        },
+               { .address = NRF51_UICR_FWID            },
+       };
+
+       for (size_t i = 0; i < ARRAY_SIZE(ficr); i++) {
+               res = target_read_u32(chip->target, ficr[i].address,
+                                     &ficr[i].value);
+               if (res != ERROR_OK) {
+                       LOG_ERROR("Couldn't read %" PRIx32, ficr[i].address);
+                       return res;
+               }
+       }
+
+       for (size_t i = 0; i < ARRAY_SIZE(uicr); i++) {
+               res = target_read_u32(chip->target, uicr[i].address,
+                                     &uicr[i].value);
+               if (res != ERROR_OK) {
+                       LOG_ERROR("Couldn't read %" PRIx32, uicr[i].address);
+                       return res;
+               }
+       }
+
+       snprintf(buf, buf_size,
+                "\n[factory information control block]\n\n"
+                "code page size: %"PRIu32"B\n"
+                "code memory size: %"PRIu32"kB\n"
+                "code region 0 size: %"PRIu32"kB\n"
+                "pre-programmed code: %s\n"
+                "number of ram blocks: %"PRIu32"\n"
+                "ram block 0 size: %"PRIu32"B\n"
+                "ram block 1 size: %"PRIu32"B\n"
+                "ram block 2 size: %"PRIu32"B\n"
+                "ram block 3 size: %"PRIu32 "B\n"
+                "encryption root: 0x%08"PRIx32"%08"PRIx32"%08"PRIx32"%08"PRIx32"\n"
+                "identity root: 0x%08"PRIx32"%08"PRIx32"%08"PRIx32"%08"PRIx32"\n"
+                "device address type: 0x%"PRIx32"\n"
+                "device address: 0x%"PRIx32"%08"PRIx32"\n"
+                "override enable: %"PRIu32"\n"
+                "NRF_1MBIT values: %"PRIx32" %"PRIx32" %"PRIx32" %"PRIx32" %"PRIx32"\n"
+                "BLE_1MBIT values: %"PRIx32" %"PRIx32" %"PRIx32" %"PRIx32" %"PRIx32"\n"
+                "\n[user information control block]\n\n"
+                "code region 0 size: %"PRIu32"kB\n"
+                "read back protection configuration: %"PRIx32"\n"
+                "reset value for XTALFREQ: %"PRIx32"\n"
+                "firmware id: 0x%04"PRIx32,
+                ficr[0].value,
+                ficr[1].value,
+                (ficr[2].value == 0xFFFFFFFF) ? 0 : ficr[2].value / 1024,
+                ((ficr[3].value & 0xFF) == 0x00) ? "present" : "not present",
+                ficr[4].value,
+                ficr[5].value,
+                (ficr[6].value == 0xFFFFFFFF) ? 0 : ficr[6].value,
+                (ficr[7].value == 0xFFFFFFFF) ? 0 : ficr[7].value,
+                (ficr[8].value == 0xFFFFFFFF) ? 0 : ficr[8].value,
+                ficr[9].value, ficr[9].value, ficr[9].value, ficr[9].value,
+                ficr[10].value, ficr[11].value, ficr[12].value, ficr[13].value,
+                ficr[14].value,
+                ficr[15].value, ficr[16].value,
+                ficr[17].value,
+                ficr[18].value, ficr[19].value, ficr[20].value, ficr[21].value, ficr[22].value,
+                ficr[23].value, ficr[24].value, ficr[25].value, ficr[26].value, ficr[27].value,
+                (uicr[0].value == 0xFFFFFFFF) ? 0 : uicr[0].value / 1024,
+                uicr[1].value & 0xFFFF,
+                uicr[2].value & 0xFF,
+                uicr[3].value & 0xFFFF);
+
+       return ERROR_OK;
+}
+
+static const struct command_registration nrf51_exec_command_handlers[] = {
+       {
+               .name           = "mass_erase",
+               .handler        = nrf51_handle_mass_erase_command,
+               .mode           = COMMAND_EXEC,
+               .help           = "Erase all flash contents of the chip.",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration nrf51_command_handlers[] = {
+       {
+               .name   = "nrf51",
+               .mode   = COMMAND_ANY,
+               .help   = "nrf51 flash command group",
+               .usage  = "",
+               .chain  = nrf51_exec_command_handlers,
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+struct flash_driver nrf51_flash = {
+       .name                   = "nrf51",
+       .commands               = nrf51_command_handlers,
+       .flash_bank_command     = nrf51_flash_bank_command,
+       .info                   = nrf51_info,
+       .erase                  = nrf51_erase,
+       .protect                = nrf51_protect,
+       .write                  = nrf51_write,
+       .read                   = default_flash_read,
+       .probe                  = nrf51_probe,
+       .auto_probe             = nrf51_auto_probe,
+       .erase_check            = default_flash_blank_check,
+       .protect_check          = nrf51_protect_check,
+};
diff --git a/tcl/target/nrf51_stlink.tcl b/tcl/target/nrf51_stlink.tcl
new file mode 100644 (file)
index 0000000..d37ec94
--- /dev/null
@@ -0,0 +1,70 @@
+#
+# script for Nordic nRF51 series, a CORTEX-M0 chip
+#
+
+source [find target/swj-dp.tcl]
+
+if { [info exists CHIPNAME] } {
+   set _CHIPNAME $CHIPNAME
+} else {
+   set _CHIPNAME nrf51
+}
+
+if { [info exists ENDIAN] } {
+   set _ENDIAN $ENDIAN
+} else {
+   set _ENDIAN little
+}
+
+# Work-area is a space in RAM used for flash programming
+# By default use 2kB
+if { [info exists WORKAREASIZE] } {
+   set _WORKAREASIZE $WORKAREASIZE
+} else {
+   set _WORKAREASIZE 0x800
+}
+
+if { [info exists CPUTAPID] } {
+   set _CPUTAPID $CPUTAPID
+} else {
+   set _CPUTAPID 0x0bb11477
+}
+
+if { [info exists TRANSPORT] } {
+   set _TRANSPORT $TRANSPORT
+   if { $TRANSPORT == "hla_jtag" } {
+      if { [info exists CPUTAPID] == 0 } {
+         # jtag requires us to use the jtag tap id
+         set _CPUTAPID 0x3ba00477
+      }
+   }
+} else {
+   set _TRANSPORT hla_swd
+}
+
+# add deprecated transport name check
+if { $_TRANSPORT == "stlink_swd" } {
+       set _TRANSPORT "hla_swd"
+       echo "DEPRECATED! use 'hla_swd' transport not 'stlink_swd'"
+}
+
+if { $_TRANSPORT == "stlink_jtag" } {
+       set _TRANSPORT "hla_jtag"
+       echo "DEPRECATED! use 'hla_jtag' transport not 'stlink_jtag'"
+}
+# end deprecated checks
+
+transport select $_TRANSPORT
+hla newtap $_CHIPNAME cpu -expected-id $_CPUTAPID
+
+set _TARGETNAME $_CHIPNAME.cpu
+target create $_TARGETNAME hla_target -chain-position $_TARGETNAME
+
+$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0
+
+# The chip supports standard ARM/Cortex-M0 SYSRESETREQ signal, so for
+# non-"hla" targets it would be useful to have the following in the config.
+# cortex_m reset_config sysresetreq
+
+set _FLASHNAME $_CHIPNAME.flash
+flash bank $_FLASHNAME nrf51 0x00000000 0 1 1 $_TARGETNAME

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)