openocd: fix SPDX tag format for files .c
[fw/openocd] / src / flash / nor / nrf5.c
index 657af9b6aa2170eb8af8c3615df82a64f61af651..d5de4a4644b338942fc9610ea7df315f3993b564 100644 (file)
@@ -1,21 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
 /***************************************************************************
  *   Copyright (C) 2013 Synapse Product Development                        *
  *   Andrey Smirnov <andrew.smironv@gmail.com>                             *
  *   Angus Gratton <gus@projectgus.com>                                    *
  *   Erdem U. Altunyurt <spamjunkeater@gmail.com>                          *
- *                                                                         *
- *   This program is free software; you can redistribute it and/or modify  *
- *   it under the terms of the GNU General Public License as published by  *
- *   the Free Software Foundation; either version 2 of the License, or     *
- *   (at your option) any later version.                                   *
- *                                                                         *
- *   This program is distributed in the hope that it will be useful,       *
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
- *   GNU General Public License for more details.                          *
- *                                                                         *
- *   You should have received a copy of the GNU General Public License     *
- *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
  ***************************************************************************/
 
 #ifdef HAVE_CONFIG_H
@@ -23,6 +12,7 @@
 #endif
 
 #include "imp.h"
+#include <helper/binarybuffer.h>
 #include <target/algorithm.h>
 #include <target/armv7m.h>
 #include <helper/types.h>
@@ -146,7 +136,7 @@ struct nrf5_device_spec {
 };
 
 struct nrf5_info {
-       uint32_t refcount;
+       unsigned int refcount;
 
        struct nrf5_bank {
                struct nrf5_info *chip;
@@ -158,7 +148,7 @@ struct nrf5_info {
        bool ficr_info_valid;
        struct nrf52_ficr_info ficr_info;
        const struct nrf5_device_spec *spec;
-       uint32_t hwid;
+       uint16_t hwid;
        enum nrf5_features features;
        unsigned int flash_size_kb;
        unsigned int ram_size_kb;
@@ -289,11 +279,11 @@ static const struct nrf5_device_package nrf5_packages_table[] = {
 
 const struct flash_driver nrf5_flash, nrf51_flash;
 
-static int nrf5_bank_is_probed(struct flash_bank *bank)
+static bool nrf5_bank_is_probed(const struct flash_bank *bank)
 {
        struct nrf5_bank *nbank = bank->driver_priv;
 
-       assert(nbank != NULL);
+       assert(nbank);
 
        return nbank->probed;
 }
@@ -309,13 +299,10 @@ static int nrf5_get_probed_chip_if_halted(struct flash_bank *bank, struct nrf5_i
        struct nrf5_bank *nbank = bank->driver_priv;
        *chip = nbank->chip;
 
-       int probed = nrf5_bank_is_probed(bank);
-       if (probed < 0)
-               return probed;
-       else if (!probed)
-               return nrf5_probe(bank);
-       else
+       if (nrf5_bank_is_probed(bank))
                return ERROR_OK;
+
+       return nrf5_probe(bank);
 }
 
 static int nrf5_wait_for_nvmc(struct nrf5_info *chip)
@@ -328,7 +315,7 @@ static int nrf5_wait_for_nvmc(struct nrf5_info *chip)
        do {
                res = target_read_u32(chip->target, NRF5_NVMC_READY, &ready);
                if (res != ERROR_OK) {
-                       LOG_ERROR("Couldn't read NVMC_READY register");
+                       LOG_ERROR("Error waiting NVMC_READY: generic flash write/erase error (check protection etc...)");
                        return res;
                }
 
@@ -440,12 +427,44 @@ error:
        return ERROR_FAIL;
 }
 
+static int nrf5_protect_check_clenr0(struct flash_bank *bank)
+{
+       int res;
+       uint32_t clenr0;
+       struct nrf5_bank *nbank = bank->driver_priv;
+       struct nrf5_info *chip = nbank->chip;
+
+       assert(chip);
+
+       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 (unsigned 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 nrf5_protect_check_bprot(struct flash_bank *bank)
 {
        struct nrf5_bank *nbank = bank->driver_priv;
        struct nrf5_info *chip = nbank->chip;
 
-       assert(chip != NULL);
+       assert(chip);
 
        static uint32_t nrf5_bprot_offsets[4] = { 0x600, 0x604, 0x610, 0x614 };
        uint32_t bprot_reg = 0;
@@ -469,9 +488,6 @@ static int nrf5_protect_check_bprot(struct flash_bank *bank)
 
 static int nrf5_protect_check(struct flash_bank *bank)
 {
-       int res;
-       uint32_t clenr0;
-
        /* UICR cannot be write protected so just return early */
        if (bank->base == NRF5_UICR_BASE)
                return ERROR_OK;
@@ -479,37 +495,16 @@ static int nrf5_protect_check(struct flash_bank *bank)
        struct nrf5_bank *nbank = bank->driver_priv;
        struct nrf5_info *chip = nbank->chip;
 
-       assert(chip != NULL);
+       assert(chip);
 
        if (chip->features & NRF5_FEATURE_BPROT)
                return nrf5_protect_check_bprot(bank);
 
-       if (!(chip->features & NRF5_FEATURE_SERIES_51)) {
-               LOG_WARNING("Flash protection of this nRF device is not supported");
-               return ERROR_FLASH_OPER_UNSUPPORTED;
-       }
-
-       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 (unsigned int i = 0; i < bank->num_sectors; i++)
-               bank->sectors[i].is_protected =
-                       clenr0 != 0xFFFFFFFF && bank->sectors[i].offset < clenr0;
+       if (chip->features & NRF5_FEATURE_SERIES_51)
+               return nrf5_protect_check_clenr0(bank);
 
-       return ERROR_OK;
+       LOG_WARNING("Flash protection of this nRF device is not supported");
+       return ERROR_FLASH_OPER_UNSUPPORTED;
 }
 
 static int nrf5_protect_clenr0(struct flash_bank *bank, int set, unsigned int first,
@@ -569,8 +564,6 @@ static int nrf5_protect_clenr0(struct flash_bank *bank, int set, unsigned int fi
 error:
        nrf5_nvmc_read_only(chip);
 
-       nrf5_protect_check(bank);
-
        return res;
 }
 
@@ -621,35 +614,42 @@ static const char *nrf5_decode_info_package(uint32_t package)
        return "xx";
 }
 
-static int nrf5_info(struct flash_bank *bank, char *buf, int buf_size)
+static int get_nrf5_chip_type_str(const struct nrf5_info *chip, char *buf, unsigned int buf_size)
 {
-       struct nrf5_bank *nbank = bank->driver_priv;
-       struct nrf5_info *chip = nbank->chip;
        int res;
-
        if (chip->spec) {
-               res = snprintf(buf, buf_size,
-                               "nRF%s-%s(build code: %s)",
+               res = snprintf(buf, buf_size, "nRF%s-%s(build code: %s)",
                                chip->spec->part, chip->spec->variant, chip->spec->build_code);
-
        } else if (chip->ficr_info_valid) {
                char variant[5];
                nrf5_info_variant_to_str(chip->ficr_info.variant, variant);
-               res = snprintf(buf, buf_size,
-                               "nRF%" PRIx32 "-%s%.2s(build code: %s)",
+               res = snprintf(buf, buf_size, "nRF%" PRIx32 "-%s%.2s(build code: %s)",
                                chip->ficr_info.part,
                                nrf5_decode_info_package(chip->ficr_info.package),
                                variant, &variant[2]);
-
        } else {
-               res = snprintf(buf, buf_size, "nRF51xxx (HWID 0x%08" PRIx32 ")",
-                               chip->hwid);
+               res = snprintf(buf, buf_size, "nRF51xxx (HWID 0x%04" PRIx16 ")", chip->hwid);
        }
-       if (res <= 0)
+
+       /* safety: */
+       if (res <= 0 || (unsigned int)res >= buf_size) {
+               LOG_ERROR("BUG: buffer problem in %s", __func__);
+               return ERROR_FAIL;
+       }
+       return ERROR_OK;
+}
+
+static int nrf5_info(struct flash_bank *bank, struct command_invocation *cmd)
+{
+       struct nrf5_bank *nbank = bank->driver_priv;
+       struct nrf5_info *chip = nbank->chip;
+
+       char chip_type_str[256];
+       if (get_nrf5_chip_type_str(chip, chip_type_str, sizeof(chip_type_str)) != ERROR_OK)
                return ERROR_FAIL;
 
-       snprintf(buf + res, buf_size - res, " %ukB Flash, %ukB RAM",
-                               chip->flash_size_kb, chip->ram_size_kb);
+       command_print_sameline(cmd, "%s %ukB Flash, %ukB RAM",
+                       chip_type_str, chip->flash_size_kb, chip->ram_size_kb);
        return ERROR_OK;
 }
 
@@ -759,14 +759,15 @@ static int nrf5_probe(struct flash_bank *bank)
        struct nrf5_info *chip = nbank->chip;
        struct target *target = chip->target;
 
-       res = target_read_u32(target, NRF5_FICR_CONFIGID, &chip->hwid);
+       uint32_t configid;
+       res = target_read_u32(target, NRF5_FICR_CONFIGID, &configid);
        if (res != ERROR_OK) {
                LOG_ERROR("Couldn't read CONFIGID register");
                return res;
        }
 
-       chip->hwid &= 0xFFFF;   /* HWID is stored in the lower two
-                        * bytes of the CONFIGID register */
+       /* HWID is stored in the lower two bytes of the CONFIGID register */
+       chip->hwid = configid & 0xFFFF;
 
        /* guess a nRF51 series if the device has no FICR INFO and we don't know HWID */
        chip->features = NRF5_FEATURE_SERIES_51;
@@ -820,13 +821,15 @@ static int nrf5_probe(struct flash_bank *bank)
        chip->flash_size_kb = num_sectors * flash_page_size / 1024;
 
        if (!chip->bank[0].probed && !chip->bank[1].probed) {
-               char buf[80];
-               nrf5_info(bank, buf, sizeof(buf));
-               if (!chip->spec && !chip->ficr_info_valid) {
-                       LOG_INFO("Unknown device: %s", buf);
-               } else {
-                       LOG_INFO("%s", buf);
-               }
+               char chip_type_str[256];
+               if (get_nrf5_chip_type_str(chip, chip_type_str, sizeof(chip_type_str)) != ERROR_OK)
+                       return ERROR_FAIL;
+               const bool device_is_unknown = (!chip->spec && !chip->ficr_info_valid);
+               LOG_INFO("%s%s %ukB Flash, %ukB RAM",
+                               device_is_unknown ? "Unknown device: " : "",
+                               chip_type_str,
+                               chip->flash_size_kb,
+                               chip->ram_size_kb);
        }
 
        free(bank->sectors);
@@ -845,8 +848,6 @@ static int nrf5_probe(struct flash_bank *bank)
                if (!bank->sectors)
                        return ERROR_FAIL;
 
-               nrf5_protect_check(bank);
-
                chip->bank[0].probed = true;
 
        } else {
@@ -867,14 +868,10 @@ static int nrf5_probe(struct flash_bank *bank)
 
 static int nrf5_auto_probe(struct flash_bank *bank)
 {
-       int probed = nrf5_bank_is_probed(bank);
-
-       if (probed < 0)
-               return probed;
-       else if (probed)
+       if (nrf5_bank_is_probed(bank))
                return ERROR_OK;
-       else
-               return nrf5_probe(bank);
+
+       return nrf5_probe(bank);
 }
 
 static int nrf5_erase_all(struct nrf5_info *chip)
@@ -1008,7 +1005,7 @@ static int nrf5_ll_flash_write(struct nrf5_info *chip, uint32_t address, const u
                        0, NULL,
                        ARRAY_SIZE(reg_params), reg_params,
                        source->address, source->size,
-                       write_algorithm->address, 0,
+                       write_algorithm->address, write_algorithm->address + sizeof(nrf5_flash_write_code) - 2,
                        &armv7m_info);
 
        target_free_working_area(target, source);
@@ -1036,6 +1033,34 @@ static int nrf5_write(struct flash_bank *bank, const uint8_t *buffer,
        assert(offset % 4 == 0);
        assert(count % 4 == 0);
 
+       /* UICR CLENR0 based protection used on nRF51 is somewhat clumsy:
+        * RM reads: Code running from code region 1 will not be able to write
+        * to code region 0.
+        * Unfortunately the flash loader running from RAM can write to both
+        * code regions without any hint the protection is violated.
+        *
+        * Update protection state and check if any flash sector to be written
+        * is protected. */
+       if (chip->features & NRF5_FEATURE_SERIES_51) {
+
+               res = nrf5_protect_check_clenr0(bank);
+               if (res != ERROR_OK)
+                       return res;
+
+               for (unsigned int sector = 0; sector < bank->num_sectors; sector++) {
+                       struct flash_sector *bs = &bank->sectors[sector];
+
+                       /* Start offset in or before this sector? */
+                       /* End offset in or behind this sector? */
+                       if ((offset < (bs->offset + bs->size))
+                                       && ((offset + count - 1) >= bs->offset)
+                                       && bs->is_protected == 1) {
+                               LOG_ERROR("Write refused, sector %d is protected", sector);
+                               return ERROR_FLASH_PROTECTED;
+                       }
+               }
+       }
+
        res = nrf5_nvmc_write_enable(chip);
        if (res != ERROR_OK)
                goto error;
@@ -1062,18 +1087,43 @@ static int nrf5_erase(struct flash_bank *bank, unsigned int first,
        if (res != ERROR_OK)
                return res;
 
+       /* UICR CLENR0 based protection used on nRF51 prevents erase
+        * absolutely silently. NVMC has no flag to indicate the protection
+        * was violated.
+        *
+        * Update protection state and check if any flash sector to be erased
+        * is protected. */
+       if (chip->features & NRF5_FEATURE_SERIES_51) {
+
+               res = nrf5_protect_check_clenr0(bank);
+               if (res != ERROR_OK)
+                       return res;
+       }
+
        /* For each sector to be erased */
-       for (unsigned int s = first; s <= last && res == ERROR_OK; s++)
+       for (unsigned int s = first; s <= last && res == ERROR_OK; s++) {
+
+               if (chip->features & NRF5_FEATURE_SERIES_51
+                               && bank->sectors[s].is_protected == 1) {
+                       LOG_ERROR("Flash sector %d is protected", s);
+                       return ERROR_FLASH_PROTECTED;
+               }
+
                res = nrf5_erase_page(bank, chip, &bank->sectors[s]);
+               if (res != ERROR_OK) {
+                       LOG_ERROR("Error erasing sector %d", s);
+                       return res;
+               }
+       }
 
-       return res;
+       return ERROR_OK;
 }
 
 static void nrf5_free_driver_priv(struct flash_bank *bank)
 {
        struct nrf5_bank *nbank = bank->driver_priv;
        struct nrf5_info *chip = nbank->chip;
-       if (chip == NULL)
+       if (!chip)
                return;
 
        chip->refcount--;
@@ -1137,7 +1187,7 @@ FLASH_BANK_COMMAND_HANDLER(nrf5_flash_bank_command)
                nbank = &chip->bank[1];
                break;
        }
-       assert(nbank != NULL);
+       assert(nbank);
 
        chip->refcount++;
        nbank->chip = chip;
@@ -1158,7 +1208,7 @@ COMMAND_HANDLER(nrf5_handle_mass_erase_command)
        if (res != ERROR_OK)
                return res;
 
-       assert(bank != NULL);
+       assert(bank);
 
        struct nrf5_info *chip;
 
@@ -1183,23 +1233,16 @@ COMMAND_HANDLER(nrf5_handle_mass_erase_command)
        }
 
        res = nrf5_erase_all(chip);
-       if (res != ERROR_OK) {
-               LOG_ERROR("Failed to erase the chip");
-               nrf5_protect_check(bank);
-               return res;
-       }
+       if (res == ERROR_OK) {
+               LOG_INFO("Mass erase completed.");
+               if (chip->features & NRF5_FEATURE_SERIES_51)
+                       LOG_INFO("A reset or power cycle is required if the flash was protected before.");
 
-       res = nrf5_protect_check(bank);
-       if (res != ERROR_OK) {
-               LOG_ERROR("Failed to check chip's write protection");
-               return res;
+       } else {
+               LOG_ERROR("Failed to erase the chip");
        }
 
-       res = get_flash_bank_by_addr(target, NRF5_UICR_BASE, true, &bank);
-       if (res != ERROR_OK)
-               return res;
-
-       return ERROR_OK;
+       return res;
 }
 
 COMMAND_HANDLER(nrf5_handle_info_command)
@@ -1212,7 +1255,7 @@ COMMAND_HANDLER(nrf5_handle_info_command)
        if (res != ERROR_OK)
                return res;
 
-       assert(bank != NULL);
+       assert(bank);
 
        struct nrf5_info *chip;