Skip to content

Commit a44795c

Browse files
committed
Patch to support new NRF revision
1 parent ffef5ee commit a44795c

File tree

1 file changed

+336
-0
lines changed

1 file changed

+336
-0
lines changed
Lines changed: 336 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,336 @@
1+
diff --git a/boards/nordic/nrf52_components/src/startup.rs b/boards/nordic/nrf52_components/src/startup.rs
2+
index 7b3ea5a59..87a566550 100644
3+
--- a/boards/nordic/nrf52_components/src/startup.rs
4+
+++ b/boards/nordic/nrf52_components/src/startup.rs
5+
@@ -36,6 +36,13 @@ impl<'a> Component for NrfStartupComponent<'a> {
6+
type StaticInput = ();
7+
type Output = ();
8+
unsafe fn finalize(self, _s: Self::StaticInput) -> Self::Output {
9+
+ // Disable APPROTECT in software. This is required as of newer nRF52
10+
+ // hardware revisions. See
11+
+ // https://devzone.nordicsemi.com/nordic/nordic-blog/b/blog/posts/working-with-the-nrf52-series-improved-approtect.
12+
+ // If run on older HW revisions this function will do nothing.
13+
+ let approtect = nrf52::approtect::Approtect::new();
14+
+ approtect.sw_disable_approtect();
15+
+
16+
// Make non-volatile memory writable and activate the reset button
17+
let uicr = nrf52::uicr::Uicr::new();
18+
19+
@@ -56,6 +63,12 @@ impl<'a> Component for NrfStartupComponent<'a> {
20+
// Avoid killing the DFU bootloader if present
21+
let (dfu_start_addr, dfu_settings_addr) = uicr.get_dfu_params();
22+
23+
+ // On new nRF52 variants we need to ensure that the APPROTECT field in UICR is
24+
+ // set to `HwDisable`.
25+
+ if uicr.is_ap_protect_enabled() {
26+
+ erase_uicr = true;
27+
+ }
28+
+
29+
if erase_uicr {
30+
self.nvmc.erase_uicr();
31+
}
32+
@@ -102,6 +115,12 @@ impl<'a> Component for NrfStartupComponent<'a> {
33+
needs_soft_reset = true;
34+
}
35+
36+
+ if uicr.is_ap_protect_enabled() {
37+
+ uicr.disable_ap_protect();
38+
+ while !self.nvmc.is_ready() {}
39+
+ needs_soft_reset = true;
40+
+ }
41+
+
42+
// Any modification of UICR needs a soft reset for the changes to be taken into account.
43+
if needs_soft_reset {
44+
cortexm4::scb::reset();
45+
diff --git a/chips/nrf52/src/approtect.rs b/chips/nrf52/src/approtect.rs
46+
new file mode 100644
47+
index 000000000..5900e10ec
48+
--- /dev/null
49+
+++ b/chips/nrf52/src/approtect.rs
50+
@@ -0,0 +1,107 @@
51+
+// Licensed under the Apache License, Version 2.0 or the MIT License.
52+
+// SPDX-License-Identifier: Apache-2.0 OR MIT
53+
+// Copyright Tock Contributors 2023.
54+
+
55+
+//! Access port protection
56+
+//!
57+
+//! <https://infocenter.nordicsemi.com/index.jsp?topic=%2Fps_nrf52840%2Fdif.html&cp=5_0_0_3_7_1&anchor=register.DISABLE>
58+
+//!
59+
+//! The logic around APPROTECT was changed in newer revisions of the nRF52
60+
+//! series chips (Oct 2021) and later which requires more careful disabling of
61+
+//! the access port (JTAG), both in the UICR register and in a software written
62+
+//! register. This module enables the kernel to disable the protection on boot.
63+
+//!
64+
+//! Example code to disable the APPROTECT protection in software:
65+
+//!
66+
+//! ```rust,ignore
67+
+//! let approtect = nrf52::approtect::Approtect::new();
68+
+//! approtect.sw_disable_approtect();
69+
+//! ```
70+
+
71+
+use crate::ficr;
72+
+use kernel::utilities::registers::interfaces::Writeable;
73+
+use kernel::utilities::registers::{register_bitfields, register_structs, ReadWrite};
74+
+use kernel::utilities::StaticRef;
75+
+
76+
+const APPROTECT_BASE: StaticRef<ApprotectRegisters> =
77+
+ unsafe { StaticRef::new(0x40000000 as *const ApprotectRegisters) };
78+
+
79+
+register_structs! {
80+
+ ApprotectRegisters {
81+
+ (0x000 => _reserved0),
82+
+ (0x550 => forceprotect: ReadWrite<u32, Forceprotect::Register>),
83+
+ (0x554 => _reserved1),
84+
+ (0x558 => disable: ReadWrite<u32, Disable::Register>),
85+
+ (0x55c => @END),
86+
+ }
87+
+}
88+
+
89+
+register_bitfields! [u32,
90+
+ Forceprotect [
91+
+ FORCEPROTECT OFFSET(0) NUMBITS(8) [
92+
+ FORCE = 0
93+
+ ]
94+
+ ],
95+
+ /// Access port protection
96+
+ Disable [
97+
+ DISABLE OFFSET(0) NUMBITS(8) [
98+
+ SWDISABLE = 0x5a
99+
+ ]
100+
+ ]
101+
+];
102+
+
103+
+pub struct Approtect {
104+
+ registers: StaticRef<ApprotectRegisters>,
105+
+}
106+
+
107+
+impl Approtect {
108+
+ pub const fn new() -> Approtect {
109+
+ Approtect {
110+
+ registers: APPROTECT_BASE,
111+
+ }
112+
+ }
113+
+
114+
+ /// Software disable the Access Port Protection mechanism.
115+
+ ///
116+
+ /// On newer variants of the nRF52, to enable JTAG, APPROTECT must be
117+
+ /// disabled both in the UICR register (hardware) and in this register
118+
+ /// (software). For older variants this is just a no-op.
119+
+ ///
120+
+ /// - <https://devzone.nordicsemi.com/f/nordic-q-a/96590/how-to-disable-approtect-permanently-dfu-is-needed>
121+
+ /// - <https://devzone.nordicsemi.com/nordic/nordic-blog/b/blog/posts/working-with-the-nrf52-series-improved-approtect>
122+
+ pub fn sw_disable_approtect(&self) {
123+
+ let factory_config = ficr::Ficr::new();
124+
+ match factory_config.variant() {
125+
+ ficr::Variant::AAF0 | ficr::Variant::Unspecified => {
126+
+ // Newer revisions of the chip require setting the APPROTECT
127+
+ // software register to `SwDisable`. We assume that an unspecified
128+
+ // version means that it is new and the FICR module hasn't been
129+
+ // updated to recognize it.
130+
+ self.registers.disable.write(Disable::DISABLE::SWDISABLE);
131+
+ }
132+
+
133+
+ // Exhaustively list variants here to produce compiler error on
134+
+ // adding a new variant, which would otherwise not match the above
135+
+ // condition.
136+
+ ficr::Variant::AAA0
137+
+ | ficr::Variant::AAAA
138+
+ | ficr::Variant::AAAB
139+
+ | ficr::Variant::AAB0
140+
+ | ficr::Variant::AABA
141+
+ | ficr::Variant::AABB
142+
+ | ficr::Variant::AAC0
143+
+ | ficr::Variant::AACA
144+
+ | ficr::Variant::AACB
145+
+ | ficr::Variant::AAD0
146+
+ | ficr::Variant::AAD1
147+
+ | ficr::Variant::AADA
148+
+ | ficr::Variant::AAE0
149+
+ | ficr::Variant::AAEA
150+
+ | ficr::Variant::ABBA
151+
+ | ficr::Variant::BAAA
152+
+ | ficr::Variant::CAAA => {
153+
+ // All other revisions don't need this.
154+
+ }
155+
+ }
156+
+ }
157+
+}
158+
diff --git a/chips/nrf52/src/ficr.rs b/chips/nrf52/src/ficr.rs
159+
index ddd6a4093..00e37bd37 100644
160+
--- a/chips/nrf52/src/ficr.rs
161+
+++ b/chips/nrf52/src/ficr.rs
162+
@@ -167,8 +167,16 @@ register_bitfields! [u32,
163+
ABBA = 0x41424241,
164+
/// AAD0
165+
AAD0 = 0x41414430,
166+
+ /// AAD1
167+
+ AAD1 = 0x41414431,
168+
+ /// AADA
169+
+ AADA = 0x41414441,
170+
/// AAE0
171+
AAE0 = 0x41414530,
172+
+ /// AAEA
173+
+ AAEA = 0x41414541,
174+
+ /// AAF0
175+
+ AAF0 = 0x41414630,
176+
/// BAAA
177+
BAAA = 0x42414141,
178+
/// CAAA
179+
@@ -234,7 +242,7 @@ register_bitfields! [u32,
180+
/// Variant describes part variant, hardware version, and production configuration.
181+
#[derive(PartialEq, Debug)]
182+
#[repr(u32)]
183+
-enum Variant {
184+
+pub(crate) enum Variant {
185+
AAA0 = 0x41414130,
186+
AAAA = 0x41414141,
187+
AAAB = 0x41414142,
188+
@@ -244,9 +252,13 @@ enum Variant {
189+
AAC0 = 0x41414330,
190+
AACA = 0x41414341,
191+
AACB = 0x41414342,
192+
- ABBA = 0x41424241,
193+
AAD0 = 0x41414430,
194+
+ AAD1 = 0x41414431,
195+
+ AADA = 0x41414441,
196+
AAE0 = 0x41414530,
197+
+ AAEA = 0x41414541,
198+
+ AAF0 = 0x41414630,
199+
+ ABBA = 0x41424241,
200+
BAAA = 0x42414141,
201+
CAAA = 0x43414141,
202+
Unspecified = 0xffffffff,
203+
@@ -306,7 +318,7 @@ pub struct Ficr {
204+
}
205+
206+
impl Ficr {
207+
- const fn new() -> Ficr {
208+
+ pub(crate) const fn new() -> Ficr {
209+
Ficr {
210+
registers: FICR_BASE,
211+
}
212+
@@ -321,7 +333,9 @@ impl Ficr {
213+
}
214+
}
215+
216+
- fn variant(&self) -> Variant {
217+
+ pub(crate) fn variant(&self) -> Variant {
218+
+ // If you update this, make sure to update
219+
+ // `has_updated_approtect_logic()` as well.
220+
match self.registers.info_variant.get() {
221+
0x41414130 => Variant::AAA0,
222+
0x41414141 => Variant::AAAA,
223+
@@ -334,13 +348,32 @@ impl Ficr {
224+
0x41414342 => Variant::AACB,
225+
0x41424241 => Variant::ABBA,
226+
0x41414430 => Variant::AAD0,
227+
+ 0x41414431 => Variant::AAD1,
228+
+ 0x41414441 => Variant::AADA,
229+
0x41414530 => Variant::AAE0,
230+
+ 0x41414541 => Variant::AAEA,
231+
+ 0x41414630 => Variant::AAF0,
232+
0x42414141 => Variant::BAAA,
233+
0x43414141 => Variant::CAAA,
234+
_ => Variant::Unspecified,
235+
}
236+
}
237+
238+
+ /// Returns if this variant of the nRF52 has the updated APPROTECT logic.
239+
+ /// This changed occurred towards the end of 2021 with chips becoming widely
240+
+ /// available/used in 2023.
241+
+ ///
242+
+ /// See <https://devzone.nordicsemi.com/nordic/nordic-blog/b/blog/posts/working-with-the-nrf52-series-improved-approtect>.
243+
+ /// for more information.
244+
+ pub(crate) fn has_updated_approtect_logic(&self) -> bool {
245+
+ // We assume that an unspecified version means that it is new and this
246+
+ // module hasn't been updated to recognize it.
247+
+ match self.variant() {
248+
+ Variant::AAF0 | Variant::Unspecified => true,
249+
+ _ => false,
250+
+ }
251+
+ }
252+
+
253+
fn package(&self) -> Package {
254+
match self.registers.info_package.get() {
255+
0x2000 => Package::QF,
256+
diff --git a/chips/nrf52/src/lib.rs b/chips/nrf52/src/lib.rs
257+
index 5ea4263c7..5293a7c63 100644
258+
--- a/chips/nrf52/src/lib.rs
259+
+++ b/chips/nrf52/src/lib.rs
260+
@@ -4,6 +4,7 @@
261+
262+
pub mod acomp;
263+
pub mod adc;
264+
+pub mod approtect;
265+
pub mod ble_radio;
266+
pub mod chip;
267+
pub mod clock;
268+
diff --git a/chips/nrf52/src/uicr.rs b/chips/nrf52/src/uicr.rs
269+
index 3086394aa..cfaa6ba60 100644
270+
--- a/chips/nrf52/src/uicr.rs
271+
+++ b/chips/nrf52/src/uicr.rs
272+
@@ -1,5 +1,6 @@
273+
//! User information configuration registers
274+
275+
+use crate::ficr;
276+
use enum_primitive::cast::FromPrimitive;
277+
use kernel::utilities::registers::interfaces::{ReadWriteable, Readable, Writeable};
278+
use kernel::utilities::registers::{register_bitfields, register_structs, ReadWrite};
279+
@@ -60,7 +61,9 @@ register_bitfields! [u32,
280+
/// Enable
281+
ENABLED = 0x00,
282+
/// Disable
283+
- DISABLED = 0xff
284+
+ DISABLED = 0xff,
285+
+ /// Disable for later nRF52 variants
286+
+ HWDISABLE = 0x5a
287+
]
288+
],
289+
290+
@@ -207,14 +210,39 @@ impl Uicr {
291+
}
292+
293+
pub fn is_ap_protect_enabled(&self) -> bool {
294+
- // Here we compare to DISABLED value because any other value should enable the protection.
295+
- !self
296+
- .registers
297+
- .approtect
298+
- .matches_all(ApProtect::PALL::DISABLED)
299+
+ // We need to understand the variant of this nRF52 chip to correctly
300+
+ // implement this function. Newer versions use a different value to
301+
+ // indicate disabled.
302+
+ let factory_config = ficr::Ficr::new();
303+
+ let disabled_val = if factory_config.has_updated_approtect_logic() {
304+
+ ApProtect::PALL::HWDISABLE
305+
+ } else {
306+
+ ApProtect::PALL::DISABLED
307+
+ };
308+
+
309+
+ // Here we compare to the correct DISABLED value because any other value
310+
+ // should enable the protection.
311+
+ !self.registers.approtect.matches_all(disabled_val)
312+
}
313+
314+
pub fn set_ap_protect(&self) {
315+
self.registers.approtect.write(ApProtect::PALL::ENABLED);
316+
}
317+
+
318+
+ /// Disable the access port protection in the UICR register. This is stored
319+
+ /// in flash and is persistent. This behavior can also be accomplished
320+
+ /// outside of tock by running `nrfjprog --recover`.
321+
+ pub fn disable_ap_protect(&self) {
322+
+ // We need to understand the variant of this nRF52 chip to correctly
323+
+ // implement this function.
324+
+ let factory_config = ficr::Ficr::new();
325+
+ if factory_config.has_updated_approtect_logic() {
326+
+ // Newer revisions of the chip require setting the APPROTECT
327+
+ // register to `HwDisable`.
328+
+ self.registers.approtect.write(ApProtect::PALL::HWDISABLE);
329+
+ } else {
330+
+ // All other revisions just use normal disable.
331+
+ self.registers.approtect.write(ApProtect::PALL::DISABLED);
332+
+ }
333+
+ }
334+
}
335+
--
336+
2.39.5

0 commit comments

Comments
 (0)