From 10597e3b4d18818d322be95e1a57656855f99852 Mon Sep 17 00:00:00 2001
From: DeepSOIC <vv.titov@gmail.com>
Date: Thu, 21 Mar 2024 19:18:35 +0100
Subject: [PATCH] nRF52840/config: add dcdc voltage parameter

address #2700
---
 embassy-nrf/src/lib.rs | 39 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 39 insertions(+)

diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs
index 718f229a3..3457dd933 100644
--- a/embassy-nrf/src/lib.rs
+++ b/embassy-nrf/src/lib.rs
@@ -225,10 +225,31 @@ pub mod config {
         /// Config for the first stage DCDC (VDDH -> VDD), if disabled LDO will be used.
         #[cfg(feature = "nrf52840")]
         pub reg0: bool,
+        /// Configure the voltage of the first stage DCDC. It is stored in non-volatile memory (UICR.REGOUT0 register); pass None to not touch it.
+        #[cfg(feature = "nrf52840")]
+        pub reg0_voltage: Option<Reg0Voltage>,
         /// Config for the second stage DCDC (VDD -> DEC4), if disabled LDO will be used.
         pub reg1: bool,
     }
 
+    ///  Output voltage setting for REG0 regulator stage.
+    #[cfg(feature = "nrf52840")]
+    pub enum Reg0Voltage {
+        /// 1.8 V
+        _1V8 = 0,
+        /// 2.1 V
+        _2V1 = 1,
+        /// 2.4 V
+        _2V4 = 2,
+        /// 2.7 V
+        _2V7 = 3,
+        /// 3.0 V
+        _3V0 = 4,
+        /// 3.3 V
+        _3v3 = 5,
+        //ERASED = 7, means 1.8V
+    }
+
     /// Settings for enabling the built in DCDC converters.
     #[cfg(feature = "_nrf5340-app")]
     pub struct DcdcConfig {
@@ -279,6 +300,8 @@ pub mod config {
                 dcdc: DcdcConfig {
                     #[cfg(feature = "nrf52840")]
                     reg0: false,
+                    #[cfg(feature = "nrf52840")]
+                    reg0_voltage: None,
                     reg1: false,
                 },
                 #[cfg(feature = "_nrf5340-app")]
@@ -337,6 +360,7 @@ mod consts {
     pub const UICR_PSELRESET2: *mut u32 = 0x10001204 as *mut u32;
     pub const UICR_NFCPINS: *mut u32 = 0x1000120C as *mut u32;
     pub const UICR_APPROTECT: *mut u32 = 0x10001208 as *mut u32;
+    pub const UICR_REGOUT0: *mut u32 = 0x10001304 as *mut u32;
     pub const APPROTECT_ENABLED: u32 = 0x0000_0000;
     pub const APPROTECT_DISABLED: u32 = 0x0000_005a;
 }
@@ -493,6 +517,21 @@ pub fn init(config: config::Config) -> Peripherals {
         }
     }
 
+    #[cfg(feature = "nrf52840")]
+    unsafe {
+        if let Some(value) = config.dcdc.reg0_voltage {
+            let value = value as u32;
+            let res = uicr_write_masked(consts::UICR_REGOUT0, value, 0b00000000_00000000_00000000_00000111);
+            needs_reset |= res == WriteResult::Written;
+            if res == WriteResult::Failed {
+                warn!(
+                    "Failed to set regulator voltage, as UICR is already programmed to some other setting, and can't be changed without erasing it.\n\
+                    To fix this, erase UICR manually, for example using `probe-rs erase` or `nrfjprog --eraseuicr`."
+                );
+            }
+        }
+    }
+
     if needs_reset {
         cortex_m::peripheral::SCB::sys_reset();
     }