|
| 1 | +/* |
| 2 | + * Copyright (c) 2021 Nordic Semiconductor ASA |
| 3 | + * |
| 4 | + * SPDX-License-Identifier: Apache-2.0 |
| 5 | + */ |
| 6 | + |
| 7 | +#include <string.h> |
| 8 | +#include <zephyr.h> |
| 9 | +#include <devicetree.h> |
| 10 | +#include <sys/byteorder.h> |
| 11 | +#include <sys/crc.h> |
| 12 | +#include <hal/nrf_power.h> |
| 13 | +#include "retained.h" |
| 14 | + |
| 15 | +/* nRF52 RAM (really, RAM AHB slaves) are partitioned as: |
| 16 | + * * Up to 8 blocks of two 4 KiBy byte "small" sections |
| 17 | + * * A 9th block of with 32 KiBy "large" sections |
| 18 | + * |
| 19 | + * At time of writing the maximum number of large sections is 6, all |
| 20 | + * within the first large block. Theoretically there could be more |
| 21 | + * sections in the 9th block, and possibly more blocks. |
| 22 | + */ |
| 23 | + |
| 24 | +/* Inclusive address of RAM start */ |
| 25 | +#define SRAM_BEGIN (uintptr_t)DT_REG_ADDR(DT_NODELABEL(sram0)) |
| 26 | + |
| 27 | +/* Exclusive address of RAM end */ |
| 28 | +#define SRAM_END (SRAM_BEGIN + (uintptr_t)DT_REG_SIZE(DT_NODELABEL(sram0))) |
| 29 | + |
| 30 | +/* Size of a controllable RAM section in the small blocks */ |
| 31 | +#define SMALL_SECTION_SIZE 4096 |
| 32 | + |
| 33 | +/* Number of controllable RAM sections in each of the lower blocks */ |
| 34 | +#define SMALL_SECTIONS_PER_BLOCK 2 |
| 35 | + |
| 36 | +/* Span of a small block */ |
| 37 | +#define SMALL_BLOCK_SIZE (SMALL_SECTIONS_PER_BLOCK * SMALL_SECTION_SIZE) |
| 38 | + |
| 39 | +/* Number of small blocks */ |
| 40 | +#define SMALL_BLOCK_COUNT 8 |
| 41 | + |
| 42 | +/* Span of the SRAM area covered by small sections */ |
| 43 | +#define SMALL_SECTION_SPAN (SMALL_BLOCK_COUNT * SMALL_BLOCK_SIZE) |
| 44 | + |
| 45 | +/* Inclusive address of the RAM range covered by large sections */ |
| 46 | +#define LARGE_SECTION_BEGIN (SRAM_BEGIN + SMALL_SECTION_SPAN) |
| 47 | + |
| 48 | +/* Size of a controllable RAM section in large blocks */ |
| 49 | +#define LARGE_SECTION_SIZE 32768 |
| 50 | + |
| 51 | +/* Set or clear RAM retention in SYSTEM_OFF for the provided object. |
| 52 | + * |
| 53 | + * @note This only works for nRF52 with the POWER module. The other |
| 54 | + * Nordic chips use a different low-level API, which is not currently |
| 55 | + * used by this function. |
| 56 | + * |
| 57 | + * @param ptr pointer to the start of the retainable object |
| 58 | + * |
| 59 | + * @param len length of the retainable object |
| 60 | + * |
| 61 | + * @param enable true to enable retention, false to clear retention |
| 62 | + */ |
| 63 | +static int ram_range_retain(const void *ptr, |
| 64 | + size_t len, |
| 65 | + bool enable) |
| 66 | +{ |
| 67 | + uintptr_t addr = (uintptr_t)ptr; |
| 68 | + uintptr_t addr_end = addr + len; |
| 69 | + |
| 70 | + /* Error if the provided range is empty or doesn't lie |
| 71 | + * entirely within the SRAM address space. |
| 72 | + */ |
| 73 | + if ((len == 0U) |
| 74 | + || (addr < SRAM_BEGIN) |
| 75 | + || (addr > (SRAM_END - len))) { |
| 76 | + return -EINVAL; |
| 77 | + } |
| 78 | + |
| 79 | + /* Iterate over each section covered by the range, setting the |
| 80 | + * corresponding RAM OFF retention bit in the parent block. |
| 81 | + */ |
| 82 | + do { |
| 83 | + uintptr_t block_base = SRAM_BEGIN; |
| 84 | + uint32_t section_size = SMALL_SECTION_SIZE; |
| 85 | + uint32_t sections_per_block = SMALL_SECTIONS_PER_BLOCK; |
| 86 | + bool is_large = (addr >= LARGE_SECTION_BEGIN); |
| 87 | + uint8_t block = 0; |
| 88 | + |
| 89 | + if (is_large) { |
| 90 | + block = 8; |
| 91 | + block_base = LARGE_SECTION_BEGIN; |
| 92 | + section_size = LARGE_SECTION_SIZE; |
| 93 | + |
| 94 | + /* RAM[x] supports only 16 sections, each its own bit |
| 95 | + * for POWER (0..15) and RETENTION (16..31). We don't |
| 96 | + * know directly how many sections are present, so |
| 97 | + * assume they all are; the true limit will be |
| 98 | + * determined by the SRAM size. |
| 99 | + */ |
| 100 | + sections_per_block = 16; |
| 101 | + } |
| 102 | + |
| 103 | + uint32_t section = (addr - block_base) / section_size; |
| 104 | + |
| 105 | + if (section >= sections_per_block) { |
| 106 | + block += section / sections_per_block; |
| 107 | + section %= sections_per_block; |
| 108 | + } |
| 109 | + |
| 110 | + uint32_t section_mask = |
| 111 | + (POWER_RAM_POWERSET_S0RETENTION_On |
| 112 | + << (section + POWER_RAM_POWERSET_S0RETENTION_Pos)); |
| 113 | + |
| 114 | + if (enable) { |
| 115 | + nrf_power_rampower_mask_on(NRF_POWER, block, section_mask); |
| 116 | + } else { |
| 117 | + nrf_power_rampower_mask_off(NRF_POWER, block, section_mask); |
| 118 | + } |
| 119 | + |
| 120 | + /* Move to the first address in the next section. */ |
| 121 | + addr += section_size - (addr % section_size); |
| 122 | + } while (addr < addr_end); |
| 123 | + |
| 124 | + return 0; |
| 125 | +} |
| 126 | + |
| 127 | +/* Retained data must be defined in a no-init section to prevent the C |
| 128 | + * runtime initialization from zeroing it before anybody can see it. |
| 129 | + */ |
| 130 | +__noinit struct retained_data retained; |
| 131 | + |
| 132 | +#define RETAINED_CRC_OFFSET offsetof(struct retained_data, crc) |
| 133 | +#define RETAINED_CHECKED_SIZE (RETAINED_CRC_OFFSET + sizeof(retained.crc)) |
| 134 | + |
| 135 | +bool retained_validate(void) |
| 136 | +{ |
| 137 | + /* The residue of a CRC is what you get from the CRC over the |
| 138 | + * message catenated with its CRC. This is the post-final-xor |
| 139 | + * residue for CRC-32 (CRC-32/ISO-HDLC) which Zephyr calls |
| 140 | + * crc32_ieee. |
| 141 | + */ |
| 142 | + const uint32_t residue = 0x2144df1c; |
| 143 | + uint32_t crc = crc32_ieee((const uint8_t *)&retained, |
| 144 | + RETAINED_CHECKED_SIZE); |
| 145 | + bool valid = (crc == residue); |
| 146 | + |
| 147 | + /* If the CRC isn't valid, reset the retained data. */ |
| 148 | + if (!valid) { |
| 149 | + memset(&retained, 0, sizeof(retained)); |
| 150 | + } |
| 151 | + |
| 152 | + /* Reset to accrue runtime from this session. */ |
| 153 | + retained.uptime_latest = 0; |
| 154 | + |
| 155 | + /* Reconfigure to retain the state during system off, regardless of |
| 156 | + * whether validation succeeded. Although these values can sometimes |
| 157 | + * be observed to be preserved across System OFF, the product |
| 158 | + * specification states they are not retained in that situation, and |
| 159 | + * that can also be observed. |
| 160 | + */ |
| 161 | + (void)ram_range_retain(&retained, RETAINED_CHECKED_SIZE, true); |
| 162 | + |
| 163 | + return valid; |
| 164 | +} |
| 165 | + |
| 166 | +void retained_update(void) |
| 167 | +{ |
| 168 | + uint64_t now = k_uptime_ticks(); |
| 169 | + |
| 170 | + retained.uptime_sum += (now - retained.uptime_latest); |
| 171 | + retained.uptime_latest = now; |
| 172 | + |
| 173 | + uint32_t crc = crc32_ieee((const uint8_t *)&retained, |
| 174 | + RETAINED_CRC_OFFSET); |
| 175 | + |
| 176 | + retained.crc = sys_cpu_to_le32(crc); |
| 177 | +} |
0 commit comments