From 0ca02de0a2f52097e43b20fce6d5b425aac73f99 Mon Sep 17 00:00:00 2001
From: Hauke Petersen <hauke.petersen@fu-berlin.de>
Date: Fri, 28 Sep 2018 16:49:35 +0200
Subject: [PATCH] tools: add nrf52_resetpin_cfg tool

This new tool allows configuring the reset pin for nRF52-based
boards. As the reset pin configuration is persistent, it does not
make sense to include it into the board code...
---
 dist/tools/nrf52_resetpin_cfg/Makefile  |  24 +++++
 dist/tools/nrf52_resetpin_cfg/README.md |  18 ++++
 dist/tools/nrf52_resetpin_cfg/main.c    | 131 ++++++++++++++++++++++++
 3 files changed, 173 insertions(+)
 create mode 100644 dist/tools/nrf52_resetpin_cfg/Makefile
 create mode 100644 dist/tools/nrf52_resetpin_cfg/README.md
 create mode 100644 dist/tools/nrf52_resetpin_cfg/main.c

diff --git a/dist/tools/nrf52_resetpin_cfg/Makefile b/dist/tools/nrf52_resetpin_cfg/Makefile
new file mode 100644
index 0000000000..23c5e78416
--- /dev/null
+++ b/dist/tools/nrf52_resetpin_cfg/Makefile
@@ -0,0 +1,24 @@
+# name of your application
+APPLICATION = nrf52_resetpin_cfg
+
+# configure RIOT basics
+BOARD ?= nrf52dk
+RIOTBASE ?= $(CURDIR)/../../..
+
+# the RESET_PIN environment variable allows for manually specifying the reset
+# pin that is programmed. Below this Makefile already contains the specific pins
+# for some known platforms
+ifeq (nrf52dk,$(BOARD))
+  RESET_PIN ?= 21
+endif
+ifeq (nrf52840dk,$(BOARD))
+  RESET_PIN ?= 18
+endif
+ifeq (,$(RESET_PIN))
+  $(error Please specify the target reset pin using the RESET_PIN env variable)
+else
+  CFLAGS += -DRESET_PIN=$(RESET_PIN)
+endif
+
+
+include $(RIOTBASE)/Makefile.include
diff --git a/dist/tools/nrf52_resetpin_cfg/README.md b/dist/tools/nrf52_resetpin_cfg/README.md
new file mode 100644
index 0000000000..e7634c924e
--- /dev/null
+++ b/dist/tools/nrf52_resetpin_cfg/README.md
@@ -0,0 +1,18 @@
+Program the reset pin for nRF52x CPUs
+=====================================
+
+Simply compile, flash, and run this tool on your nRF52x-based board. It will
+program the given `RESET_PIN` into the NRF_UICR->PSELRESET registers, hence
+allowing for hardware resets using a button connected to that pin.
+
+
+Context
+=======
+For nRF52x CPUs, the reset pin is programmable. The reset pin configuration is
+store in two persistent registers, that are programmed in the same way as the
+CPUs flash memory.
+
+In most cases, these values should be readily programmed when you get your
+board, and thus do not need to be touched. However, we have seen however a
+number of nrf52xxxdk boards, where this was not the case and the on-board reset
+button did have no effect. Running this tool on those boards solves the issue.
diff --git a/dist/tools/nrf52_resetpin_cfg/main.c b/dist/tools/nrf52_resetpin_cfg/main.c
new file mode 100644
index 0000000000..fd7a721410
--- /dev/null
+++ b/dist/tools/nrf52_resetpin_cfg/main.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2018 Freie Universität Berlin
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     tools
+ * @{
+ *
+ * @file
+ * @brief       Tool for programming the reset pin on nRF52x-based boards
+ *
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ *
+ * @}
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "cpu.h"
+#include "periph/pm.h"
+
+/* guard against bad usage: only allow building for nRF52x CPUs */
+#ifndef CPU_NRF52
+#error This tool is only usable for nRF52x-based platforms
+#endif
+
+#ifndef RESET_PIN
+#error RESET_PIN not specified
+#endif
+
+#define PORT_BIT        (0x00000020)
+#define PORT_POS        (5U)
+#define PIN_MASK        (0x0000001f)
+
+#define RESET_VAL       (0xffffffff)
+
+#define REG_NUM         (sizeof(NRF_UICR_Type) / 4)
+
+/* allocate a copy of the registers in RAM */
+static NRF_UICR_Type _buf;
+
+static void _print_pin(uint32_t p)
+{
+    printf("P%i.%2i", (int)((p & PORT_BIT) >> PORT_POS), (int)(p & PIN_MASK));
+}
+
+static void _copy(volatile uint32_t *dst, volatile uint32_t *src, unsigned num)
+{
+    for (unsigned i = 0; i < num; i++) {
+        *dst++ = *src++;
+    }
+}
+
+static void _save_uicr(void)
+{
+    memcpy(&_buf, (void *)NRF_UICR, sizeof(NRF_UICR_Type));
+}
+
+static void _restore_uicr(void)
+{
+    NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen;
+    while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {}
+
+    /* we copy the values back selectively, skipping the PSELRESET fields */
+    _copy(NRF_UICR->NRFFW, _buf.NRFFW, 15);
+    _copy(NRF_UICR->NRFHW, _buf.NRFHW, 12);
+    _copy(NRF_UICR->CUSTOMER, _buf.CUSTOMER, 32);
+    NRF_UICR->APPROTECT = _buf.APPROTECT;
+    NRF_UICR->NFCPINS = _buf.NFCPINS;
+#ifdef CPU_MODEL_NRF52840XXAA
+    NRF_UICR->EXTSUPPLY = _buf.EXTSUPPLY;
+    NRF_UICR->REGOUT0 = _buf.REGOUT0;
+#endif
+
+    /* we can leave the NVMC in write enable mode when leaving... */
+}
+
+int main(void)
+{
+    uint32_t target = (uint32_t)RESET_PIN;
+
+    if ((NRF_UICR->PSELRESET[0] == target) &&
+        (NRF_UICR->PSELRESET[1] == target)) {
+        puts("\nAll good!");
+        _print_pin(target);
+        puts(" programmed as reset pin\n");
+        puts("Now press the reset button to confirm its function!");
+    }
+    else {
+        puts("\nReset pin is not programmed properly");
+        printf("Will now program it to ");
+        _print_pin(target);
+        puts("\n\nPress any key (meaning send any char) to continue");
+        getchar();
+
+        puts("Progamming the pin now...");
+        if ((NRF_UICR->PSELRESET[0] != RESET_VAL) ||
+            (NRF_UICR->PSELRESET[1] != RESET_VAL)) {
+            /* we can only erase all UICR registers at once, so we need to save
+             * and restore there values for clearing the PSELRESET registers */
+            puts("save uicr");
+            _save_uicr();
+            NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Een;
+            while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {}
+            NRF_NVMC->ERASEUICR = 1;
+            while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {}
+            puts("restore");
+            _restore_uicr();
+        }
+
+        puts("promming new value to PSELRESET");
+        NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen;
+        while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {}
+        NRF_UICR->PSELRESET[0] = target;
+        NRF_UICR->PSELRESET[1] = target;
+        while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {}
+        NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren;
+
+        /* verify result */
+        puts("The changes will only take effect after reboot.\n"
+             "Doing a reboot now...\n");
+        pm_reboot();
+    }
+
+    return 0;
+}
-- 
GitLab