diff options
-rw-r--r-- | doc/openocd.texi | 4 | ||||
-rw-r--r-- | src/flash/nor/nrf5.c | 255 |
2 files changed, 215 insertions, 44 deletions
diff --git a/doc/openocd.texi b/doc/openocd.texi index 440f434..af1684d 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -6363,6 +6363,10 @@ works only for chips that do not have factory pre-programmed region 0 code. @end deffn +@deffn Command {nrf5 info} +Decodes and shows informations from FICR and UICR registers. +@end deffn + @end deffn @deffn {Flash Driver} ocl diff --git a/src/flash/nor/nrf5.c b/src/flash/nor/nrf5.c index 4addb61..426f287 100644 --- a/src/flash/nor/nrf5.c +++ b/src/flash/nor/nrf5.c @@ -71,14 +71,19 @@ enum nrf5_ficr_registers { NRF5_FICR_BLE_1MBIT2 = NRF5_FICR_REG(0x0F4), NRF5_FICR_BLE_1MBIT3 = NRF5_FICR_REG(0x0F8), NRF5_FICR_BLE_1MBIT4 = NRF5_FICR_REG(0x0FC), + + /* Following registers are available on nRF52 and on nRF51 since rev 3 */ + NRF5_FICR_INFO_PART = NRF5_FICR_REG(0x100), + NRF5_FICR_INFO_VARIANT = NRF5_FICR_REG(0x104), + NRF5_FICR_INFO_PACKAGE = NRF5_FICR_REG(0x108), + NRF5_FICR_INFO_RAM = NRF5_FICR_REG(0x10C), + NRF5_FICR_INFO_FLASH = NRF5_FICR_REG(0x110), }; enum nrf5_uicr_registers { NRF5_UICR_BASE = 0x10001000, /* User Information * Configuration Regsters */ - NRF5_UICR_SIZE = 0x100, - #define NRF5_UICR_REG(offset) (NRF5_UICR_BASE + offset) NRF5_UICR_CLENR0 = NRF5_UICR_REG(0x000), @@ -107,8 +112,23 @@ enum nrf5_nvmc_config_bits { }; +struct nrf52_ficr_info { + uint32_t part; + uint32_t variant; + uint32_t package; + uint32_t ram; + uint32_t flash; +}; + +struct nrf5_device_spec { + uint16_t hwid; + const char *part; + const char *variant; + const char *build_code; + unsigned int flash_size_kb; +}; + struct nrf5_info { - uint32_t code_page_size; uint32_t refcount; struct nrf5_bank { @@ -116,13 +136,12 @@ struct nrf5_info { bool probed; } bank[2]; struct target *target; -}; -struct nrf5_device_spec { - uint16_t hwid; - const char *part; - const char *variant; - const char *build_code; + /* chip identification stored in nrf5_probe() for use in nrf5_info() */ + bool ficr_info_valid; + struct nrf52_ficr_info ficr_info; + const struct nrf5_device_spec *spec; + uint32_t hwid; unsigned int flash_size_kb; }; @@ -207,6 +226,9 @@ static const struct nrf5_device_spec nrf5_known_devices_table[] = { NRF5_DEVICE_DEF(0x007A, "51422", "CEAA", "C0", 256), NRF5_DEVICE_DEF(0x0088, "51422", "CFAC", "A0", 256), + /* The driver fully autodects nRF52 series devices by FICR INFO, + * no need for nRF52xxx HWIDs in this table */ +#if 0 /* nRF52810 Devices */ NRF5_DEVICE_DEF(0x0142, "52810", "QFAA", "B0", 192), NRF5_DEVICE_DEF(0x0143, "52810", "QCAA", "C0", 192), @@ -218,6 +240,21 @@ static const struct nrf5_device_spec nrf5_known_devices_table[] = { /* nRF52840 Devices */ NRF5_DEVICE_DEF(0x0150, "52840", "QIAA", "C0", 1024), +#endif +}; + +struct nrf5_device_package { + uint32_t package; + const char *code; +}; + +/* Newer devices have FICR INFO.PACKAGE. + * This table converts its value to two character code */ +static const struct nrf5_device_package nrf5_packages_table[] = { + { 0x2000, "QF" }, + { 0x2001, "CH" }, + { 0x2002, "CI" }, + { 0x2005, "CK" }, }; static int nrf5_bank_is_probed(struct flash_bank *bank) @@ -463,62 +500,177 @@ static int nrf5_protect(struct flash_bank *bank, int set, int first, int last) return ERROR_OK; } +static bool nrf5_info_variant_to_str(uint32_t variant, char *bf) +{ + h_u32_to_be((uint8_t *)bf, variant); + bf[4] = '\0'; + if (isalnum(bf[0]) && isalnum(bf[1]) && isalnum(bf[2]) && isalnum(bf[3])) + return true; + + strcpy(bf, "xxxx"); + return false; +} + +static const char *nrf5_decode_info_package(uint32_t package) +{ + for (size_t i = 0; i < ARRAY_SIZE(nrf5_packages_table); i++) { + if (nrf5_packages_table[i].package == package) + return nrf5_packages_table[i].code; + } + return "xx"; +} + +static int nrf5_info(struct flash_bank *bank, char *buf, int buf_size) +{ + struct nrf5_bank *nbank = bank->driver_priv; + struct nrf5_info *chip = nbank->chip; + + if (chip->spec) { + snprintf(buf, buf_size, + "nRF%s-%s(build code: %s) %ukB Flash", + chip->spec->part, chip->spec->variant, chip->spec->build_code, + chip->flash_size_kb); + + } else if (chip->ficr_info_valid) { + char variant[5]; + nrf5_info_variant_to_str(chip->ficr_info.variant, variant); + snprintf(buf, buf_size, + "nRF%" PRIx32 "-%s%.2s(build code: %s) %" PRIu32 + "kB Flash, %" PRIu32 "kB RAM", + chip->ficr_info.part, + nrf5_decode_info_package(chip->ficr_info.package), + variant, &variant[2], + chip->flash_size_kb, + chip->ficr_info.ram); + + } else { + snprintf(buf, buf_size, "nRF51xxx (HWID 0x%04" PRIx16 ") %ukB Flash", + chip->hwid, chip->flash_size_kb); + } + return ERROR_OK; +} + +static int nrf5_read_ficr_info(struct nrf5_info *chip) +{ + int res; + struct target *target = chip->target; + + chip->ficr_info_valid = false; + + res = target_read_u32(target, NRF5_FICR_INFO_PART, &chip->ficr_info.part); + if (res != ERROR_OK) { + LOG_DEBUG("Couldn't read FICR INFO.PART register"); + return res; + } + + uint32_t series = chip->ficr_info.part & 0xfffff000; + if (!(series == 0x51000 || series == 0x52000)) { + LOG_DEBUG("FICR INFO likely not implemented. Invalid PART value 0x%08" + PRIx32, chip->ficr_info.part); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + /* Now we know the device has FICR INFO filled by something relevant: + * Although it is not documented, the tested nRF51 rev 3 devices + * have FICR INFO.PART, RAM and FLASH of the same format as nRF52. + * VARIANT and PACKAGE coding is unknown for a nRF51 device. + * nRF52 devices have FICR INFO documented and always filled. */ + + res = target_read_u32(target, NRF5_FICR_INFO_VARIANT, &chip->ficr_info.variant); + if (res != ERROR_OK) + return res; + + res = target_read_u32(target, NRF5_FICR_INFO_PACKAGE, &chip->ficr_info.package); + if (res != ERROR_OK) + return res; + + res = target_read_u32(target, NRF5_FICR_INFO_RAM, &chip->ficr_info.ram); + if (res != ERROR_OK) + return res; + + res = target_read_u32(target, NRF5_FICR_INFO_FLASH, &chip->ficr_info.flash); + if (res != ERROR_OK) + return res; + + chip->ficr_info_valid = true; + return ERROR_OK; +} + static int nrf5_probe(struct flash_bank *bank) { - uint32_t hwid; int res; struct nrf5_bank *nbank = bank->driver_priv; struct nrf5_info *chip = nbank->chip; + struct target *target = chip->target; - res = target_read_u32(chip->target, NRF5_FICR_CONFIGID, &hwid); + res = target_read_u32(target, NRF5_FICR_CONFIGID, &chip->hwid); if (res != ERROR_OK) { LOG_ERROR("Couldn't read CONFIGID register"); return res; } - hwid &= 0xFFFF; /* HWID is stored in the lower two + chip->hwid &= 0xFFFF; /* HWID is stored in the lower two * bytes of the CONFIGID register */ - const struct nrf5_device_spec *spec = NULL; + chip->spec = NULL; for (size_t i = 0; i < ARRAY_SIZE(nrf5_known_devices_table); i++) { - if (hwid == nrf5_known_devices_table[i].hwid) { - spec = &nrf5_known_devices_table[i]; + if (chip->hwid == nrf5_known_devices_table[i].hwid) { + chip->spec = &nrf5_known_devices_table[i]; break; } } - if (!chip->bank[0].probed && !chip->bank[1].probed) { - if (spec) - LOG_INFO("nRF%s-%s(build code: %s) %ukB Flash", - spec->part, spec->variant, spec->build_code, - spec->flash_size_kb); - else - LOG_WARNING("Unknown device (HWID 0x%08" PRIx32 ")", hwid); + /* Don't bail out on error for the case that some old engineering + * sample has FICR INFO registers unreadable. We can proceed anyway. */ + (void)nrf5_read_ficr_info(chip); + + if (chip->spec && chip->ficr_info_valid) { + /* check if HWID table gives the same part as FICR INFO */ + if (chip->ficr_info.part != strtoul(chip->spec->part, NULL, 16)) + LOG_WARNING("HWID 0x%04" PRIx32 " mismatch: FICR INFO.PART %" + PRIx32, chip->hwid, chip->ficr_info.part); } - if (bank->base == NRF5_FLASH_BASE) { - /* The value stored in NRF5_FICR_CODEPAGESIZE is the number of bytes in one page of FLASH. */ - res = target_read_u32(chip->target, NRF5_FICR_CODEPAGESIZE, - &chip->code_page_size); - if (res != ERROR_OK) { - LOG_ERROR("Couldn't read code page size"); - return res; - } + /* The value stored in NRF5_FICR_CODEPAGESIZE is the number of bytes in one page of FLASH. */ + uint32_t flash_page_size; + res = target_read_u32(chip->target, NRF5_FICR_CODEPAGESIZE, + &flash_page_size); + if (res != ERROR_OK) { + LOG_ERROR("Couldn't read code page size"); + return res; + } - /* Note the register name is misleading, - * NRF5_FICR_CODESIZE is the number of pages in flash memory, not the number of bytes! */ - uint32_t num_sectors; - res = target_read_u32(chip->target, NRF5_FICR_CODESIZE, &num_sectors); - if (res != ERROR_OK) { - LOG_ERROR("Couldn't read code memory size"); - return res; + /* Note the register name is misleading, + * NRF5_FICR_CODESIZE is the number of pages in flash memory, not the number of bytes! */ + uint32_t num_sectors; + res = target_read_u32(chip->target, NRF5_FICR_CODESIZE, &num_sectors); + if (res != ERROR_OK) { + LOG_ERROR("Couldn't read code memory size"); + return res; + } + + chip->flash_size_kb = num_sectors * flash_page_size / 1024; + + if (!chip->bank[0].probed && !chip->bank[1].probed) { + char buf[80]; + nrf5_info(bank, buf, sizeof(buf)); + if (!chip->spec && !chip->ficr_info_valid) { + LOG_INFO("Unknown device: %s", buf); + } else { + LOG_INFO("%s", buf); } + } + + if (bank->base == NRF5_FLASH_BASE) { bank->num_sectors = num_sectors; - bank->size = num_sectors * chip->code_page_size; + bank->size = num_sectors * flash_page_size; - if (spec && bank->size / 1024 != spec->flash_size_kb) + /* Sanity check */ + if (chip->spec && chip->flash_size_kb != chip->spec->flash_size_kb) LOG_WARNING("Chip's reported Flash capacity does not match expected one"); + if (chip->ficr_info_valid && chip->flash_size_kb != chip->ficr_info.flash) + LOG_WARNING("Chip's reported Flash capacity does not match FICR INFO.FLASH"); bank->sectors = calloc(bank->num_sectors, sizeof((bank->sectors)[0])); @@ -528,8 +680,8 @@ static int nrf5_probe(struct flash_bank *bank) /* Fill out the sector information: all NRF5 sectors are the same size and * there is always a fixed number of them. */ for (int i = 0; i < bank->num_sectors; i++) { - bank->sectors[i].size = chip->code_page_size; - bank->sectors[i].offset = i * chip->code_page_size; + bank->sectors[i].size = flash_page_size; + bank->sectors[i].offset = i * flash_page_size; /* mark as unknown */ bank->sectors[i].is_erased = -1; @@ -540,7 +692,7 @@ static int nrf5_probe(struct flash_bank *bank) chip->bank[0].probed = true; } else { - bank->size = NRF5_UICR_SIZE; + bank->size = flash_page_size; bank->num_sectors = 1; bank->sectors = calloc(bank->num_sectors, sizeof((bank->sectors)[0])); @@ -887,9 +1039,17 @@ COMMAND_HANDLER(nrf5_handle_mass_erase_command) return ERROR_OK; } -static int nrf5_info(struct flash_bank *bank, char *buf, int buf_size) +COMMAND_HANDLER(nrf5_handle_info_command) { int res; + struct flash_bank *bank = NULL; + struct target *target = get_current_target(CMD_CTX); + + res = get_flash_bank_by_addr(target, NRF5_FLASH_BASE, true, &bank); + if (res != ERROR_OK) + return res; + + assert(bank != NULL); struct nrf5_info *chip; @@ -960,7 +1120,7 @@ static int nrf5_info(struct flash_bank *bank, char *buf, int buf_size) } } - snprintf(buf, buf_size, + command_print(CMD, "\n[factory information control block]\n\n" "code page size: %"PRIu32"B\n" "code memory size: %"PRIu32"kB\n" @@ -1019,6 +1179,13 @@ static const struct command_registration nrf5_exec_command_handlers[] = { .help = "Erase all flash contents of the chip.", .usage = "", }, + { + .name = "info", + .handler = nrf5_handle_info_command, + .mode = COMMAND_EXEC, + .help = "Show FICR and UICR info.", + .usage = "", + }, COMMAND_REGISTRATION_DONE }; |