Programming and reading OTP

I want to store some board info (serial number etc.) in OTP during board production. I managed to program OTP[189] trough J-Link commander. Now I want to read it back in the application.

My first attempt was to read the address:

Fullscreen
1
reg = *((uint32_t *)0x00FF83FC);
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

This results in a bus fault. Reading trough earlier topics on this subject I suspect the OTP can only be read by secure code? All earlier topics mention the secure partition manager, which is now deprecated (using SDK 2.1.0).

I tried to convert to TF-M, using the following code:

Fullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static uint32_t secure_read_word(intptr_t ptr)
{
uint32_t err = 0;
uint32_t val;
enum tfm_platform_err_t plt_err;
plt_err = tfm_platform_mem_read(&val, ptr, 4, &err);
if (plt_err != TFM_PLATFORM_ERR_SUCCESS || err != 0) {
printk("tfm_..._mem_read failed: plt_err: 0x%x, err: 0x%x\n",
plt_err, err);
return -1;
}
return val;
}
uint32_t read_otp(void)
{
uint32_t val = secure_read_word((intptr_t)&NRF_UICR_S->OTP[189]);// *((uint32_t *)0x00FF83FC);
if(val == 0xFFFFFFFF)
{
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

But this always gives the default serial, and the following line in the console:

tfm_..._mem_read failed: plt_err: 0x2, err: 0xffffffff

I guess I need to add the address to some whitelisted addresses that the non-secure code can read?