|
| 1 | +/** |
| 2 | + * Access and reset u-boot's "bootcount" counter for the TI AM625 and AM62A SoCs |
| 3 | + * which is stored in the RTC_SCRATCH2_REG. |
| 4 | + * |
| 5 | + * see: |
| 6 | + * - AM625 TRM: https://www.ti.com/lit/pdf/spruiv7 |
| 7 | + * Section 12.7.3.3.4 Scratch Registers |
| 8 | + * Section 14.8.7.3.12.1 RTC_RTC_SCRATCH0_N Register |
| 9 | + * |
| 10 | + * - AM62ax TRM: https://www.ti.com/lit/pdf/spruj16 |
| 11 | + * Section 12.8.3.3.4 Scratch Registers |
| 12 | + * Section 14.9.7.3.12.1 RTC_RTC_SCRATCH0_N Register |
| 13 | + */ |
| 14 | + |
| 15 | +#include <stdlib.h> |
| 16 | +#include <stdint.h> |
| 17 | +#include <stdio.h> |
| 18 | +#include <string.h> |
| 19 | + |
| 20 | +#include "./constants.h" |
| 21 | +#include "./memory.h" |
| 22 | +#include "./dt.h" |
| 23 | +#include "./am62x.h" |
| 24 | + |
| 25 | +// spruiv7.pdf Section 14.8.7.3.12 RTC_RTC_RTC_SCRATCH0_N Register |
| 26 | +// spruj16.pdf Section 14.9.7.3.12 RTC_RTC_RTC_SCRATCH0_N Register |
| 27 | +#define AM62_RTCSS 0x2B1F0000ul |
| 28 | +#define AM62_SCRATCH2_REG_OFFSET (0x30ul * 2) |
| 29 | +#define AM62_REG_SIZE 4ul // registers are 4 bytes/ 32bit |
| 30 | + |
| 31 | +// spruiv7.pdf Section 14.8.7.3.19 RTC_RTC_RTC_KICK0 Registers |
| 32 | +// spruj16.pdf Section 14.9.7.3.19 RTC_RTC_RTC_KICK0 Registers |
| 33 | +#define AM62_KICK0R_REG_OFFSET 0x70ul |
| 34 | +#define AM62_KICK1R_REG_OFFSET 0x74ul |
| 35 | +#define AM62_KICK0_MAGIC 0x83e70b13ul |
| 36 | +#define AM62_KICK1_MAGIC 0x95a4f1e0ul |
| 37 | + |
| 38 | +#define AM62_MEM_OFFSET (AM62_RTCSS + AM62_SCRATCH2_REG_OFFSET) |
| 39 | +// We need to map the RTCSS block from SCRATCH2 up to the end of KICK1R: |
| 40 | +#define AM62_MEM_LEN (AM62_KICK1R_REG_OFFSET + AM62_REG_SIZE - AM62_SCRATCH2_REG_OFFSET) |
| 41 | + |
| 42 | +bool is_am62() { |
| 43 | + return is_compatible_soc("ti,am625") || is_compatible_soc("ti,am62a7"); |
| 44 | +} |
| 45 | + |
| 46 | +int am62_read_bootcount(uint16_t* val) { |
| 47 | + |
| 48 | + uint32_t *scratch2_addr = memory_open(AM62_MEM_OFFSET, AM62_MEM_LEN); |
| 49 | + if ( scratch2_addr == (void *)E_DEVICE ) { |
| 50 | + return E_DEVICE; |
| 51 | + } |
| 52 | + |
| 53 | + uint32_t scratch2_val = memory_read(scratch2_addr); |
| 54 | + // low two bytes are the value, high two bytes are magic |
| 55 | + if ((scratch2_val & 0xffff0000) != (BOOTCOUNT_MAGIC & 0xffff0000)) { |
| 56 | + return E_BADMAGIC; |
| 57 | + } |
| 58 | + |
| 59 | + *val = (uint16_t)(scratch2_val & 0x0000ffff); |
| 60 | + return 0; |
| 61 | +} |
| 62 | + |
| 63 | + |
| 64 | +int am62_write_bootcount(uint16_t val) { |
| 65 | + // NOTE: These must be volatile. |
| 66 | + // See https://github.com/brgl/busybox/blob/master/miscutils/devmem.c |
| 67 | + volatile uint32_t *scratch2_addr = |
| 68 | + (volatile uint32_t *)memory_open(AM62_MEM_OFFSET, AM62_MEM_LEN); |
| 69 | + if ( scratch2_addr == (void *)E_DEVICE ) { |
| 70 | + return E_DEVICE; |
| 71 | + } |
| 72 | + |
| 73 | + volatile uint32_t *kick0r = scratch2_addr + (AM62_KICK0R_REG_OFFSET - AM62_SCRATCH2_REG_OFFSET) / 4; |
| 74 | + volatile uint32_t *kick1r = kick0r + 1; // (AM62_KICK1R_REG_OFFSET - AM62_SCRATCH2_REG_OFFSET) / 4; |
| 75 | + |
| 76 | + // Disable write protection, then write to SCRATCH2 |
| 77 | + *kick0r = AM62_KICK0_MAGIC; |
| 78 | + *kick1r = AM62_KICK1_MAGIC; |
| 79 | + uint32_t scratch2_val = (BOOTCOUNT_MAGIC & 0xffff0000) | (val & 0xffff); |
| 80 | + memory_write(scratch2_addr, scratch2_val); |
| 81 | + |
| 82 | + // re-lock the write protection register |
| 83 | + *kick1r = 0; |
| 84 | + |
| 85 | + // read back to verify: |
| 86 | + uint16_t read_val = 0; |
| 87 | + am33_read_bootcount(&read_val); |
| 88 | + if ( read_val != val ) { |
| 89 | + return E_WRITE_FAILED; |
| 90 | + } |
| 91 | + |
| 92 | + |
| 93 | + return 0; |
| 94 | +} |
0 commit comments