diff --git a/dist/tools/nrf52_resetpin_cfg/Makefile b/dist/tools/nrf52_resetpin_cfg/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..23c5e784169c4d763216c8e8690b312c9201c499 --- /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 0000000000000000000000000000000000000000..e7634c924ee86f8d9f37f3b990b524bc89826a9a --- /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 0000000000000000000000000000000000000000..fd7a7214101907478d48d6ef287d23d557259de5 --- /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; +}