NRF51_UICR_BASE = 0x10001000, /* User Information
* Configuration Regsters */
+ NRF51_UICR_SIZE = 252,
+
#define NRF51_UICR_REG(offset) (NRF51_UICR_BASE + offset)
NRF51_UICR_CLENR0 = NRF51_UICR_REG(0x000),
uint32_t code_page_size;
uint32_t code_memory_size;
- bool probed;
+ struct {
+ bool probed;
+ int (*write) (struct flash_bank *bank,
+ struct nrf51_info *chip,
+ const uint8_t *buffer, uint32_t offset, uint32_t count);
+ } bank[2];
struct target *target;
};
+struct nrf51_device_spec {
+ uint16_t hwid;
+ const char *variant;
+ const char *build_code;
+ unsigned int flash_size_kb;
+};
+
+static const struct nrf51_device_spec nrf51_known_devices_table[] = {
+ {
+ .hwid = 0x001D,
+ .variant = "QFAA",
+ .build_code = "CA/C0",
+ .flash_size_kb = 256,
+ },
+ {
+ .hwid = 0x002A,
+ .variant = "QFAA",
+ .build_code = "FA",
+ .flash_size_kb = 256,
+ },
+ {
+ .hwid = 0x0044,
+ .variant = "QFAA",
+ .build_code = "GC",
+ .flash_size_kb = 256,
+ },
+ {
+ .hwid = 0x003C,
+ .variant = "QFAA",
+ .build_code = "G0",
+ .flash_size_kb = 256,
+ },
+
+ {
+ .hwid = 0x0020,
+ .variant = "CEAA",
+ .build_code = "BA",
+ .flash_size_kb = 256,
+ },
+ {
+ .hwid = 0x002F,
+ .variant = "CEAA",
+ .build_code = "B0",
+ .flash_size_kb = 256,
+ },
+ {
+ .hwid = 0x0040,
+ .variant = "CEAA",
+ .build_code = "CA",
+ .flash_size_kb = 256,
+ },
+ {
+ .hwid = 0x0047,
+ .variant = "CEAA",
+ .build_code = "DA",
+ .flash_size_kb = 256,
+ },
+ {
+ .hwid = 0x004D,
+ .variant = "CEAA",
+ .build_code = "D0",
+ .flash_size_kb = 256,
+ },
+
+ {
+ .hwid = 0x0026,
+ .variant = "QFAB",
+ .build_code = "AA",
+ .flash_size_kb = 128,
+ },
+ {
+ .hwid = 0x0027,
+ .variant = "QFAB",
+ .build_code = "A0",
+ .flash_size_kb = 128,
+ },
+ {
+ .hwid = 0x004C,
+ .variant = "QFAB",
+ .build_code = "B0",
+ .flash_size_kb = 128,
+ },
+
+};
+
+static int nrf51_bank_is_probed(struct flash_bank *bank)
+{
+ struct nrf51_info *chip = bank->driver_priv;
+
+ assert(chip != NULL);
+
+ return chip->bank[bank->bank_number].probed;
+}
static int nrf51_probe(struct flash_bank *bank);
static int nrf51_get_probed_chip_if_halted(struct flash_bank *bank, struct nrf51_info **chip)
return ERROR_TARGET_NOT_HALTED;
}
- *chip = (struct nrf51_info *)bank->driver_priv;
+ *chip = bank->driver_priv;
- if (!(*chip)->probed)
+ int probed = nrf51_bank_is_probed(bank);
+ if (probed < 0)
+ return probed;
+ else if (!probed)
return nrf51_probe(bank);
-
- return ERROR_OK;
+ else
+ return ERROR_OK;
}
static int nrf51_wait_for_nvmc(struct nrf51_info *chip)
int res;
uint32_t clenr0;
- struct nrf51_info *chip = (struct nrf51_info *)bank->driver_priv;
+ /* UICR cannot be write protected so just return early */
+ if (bank->base == NRF51_UICR_BASE)
+ return ERROR_OK;
+
+ struct nrf51_info *chip = bank->driver_priv;
assert(chip != NULL);
uint32_t clenr0, ppfc;
struct nrf51_info *chip;
+ /* UICR cannot be write protected so just bail out early */
+ if (bank->base == NRF51_UICR_BASE)
+ return ERROR_FAIL;
+
res = nrf51_get_probed_chip_if_halted(bank, &chip);
if (res != ERROR_OK)
return res;
static int nrf51_probe(struct flash_bank *bank)
{
- uint32_t id;
+ uint32_t hwid;
int res;
- struct nrf51_info *chip = (struct nrf51_info *)bank->driver_priv;
+ struct nrf51_info *chip = bank->driver_priv;
- res = target_read_u32(chip->target, NRF51_FICR_DEVICEID0, &id);
+ res = target_read_u32(chip->target, NRF51_FICR_CONFIGID, &hwid);
if (res != ERROR_OK) {
- LOG_ERROR("Couldn't read Device ID 0 register");
+ LOG_ERROR("Couldn't read CONFIGID register");
return res;
}
- res = target_read_u32(chip->target, NRF51_FICR_DEVICEID1, &id);
- if (res != ERROR_OK) {
- LOG_ERROR("Couldn't read Device ID 1 register");
- return res;
- }
+ hwid &= 0xFFFF; /* HWID is stored in the lower two
+ * bytes of the CONFIGID register */
- res = target_read_u32(chip->target, NRF51_FICR_CODEPAGESIZE,
- &chip->code_page_size);
- if (res != ERROR_OK) {
- LOG_ERROR("Couldn't read code page size");
- return res;
- }
+ const struct nrf51_device_spec *spec = NULL;
+ for (size_t i = 0; i < ARRAY_SIZE(nrf51_known_devices_table); i++)
+ if (hwid == nrf51_known_devices_table[i].hwid) {
+ spec = &nrf51_known_devices_table[i];
+ break;
+ }
- res = target_read_u32(chip->target, NRF51_FICR_CODESIZE,
- &chip->code_memory_size);
- if (res != ERROR_OK) {
- LOG_ERROR("Couldn't read code memory size");
- return res;
+ if (!chip->bank[0].probed && !chip->bank[1].probed) {
+ if (spec)
+ LOG_INFO("nRF51822-%s(build code: %s) %ukB Flash",
+ spec->variant, spec->build_code, spec->flash_size_kb);
+ else
+ LOG_WARNING("Unknown device (HWID 0x%08" PRIx32 ")", hwid);
}
- bank->size = chip->code_memory_size * 1024;
- bank->num_sectors = bank->size / chip->code_page_size;
- bank->sectors = calloc(bank->num_sectors,
- sizeof((bank->sectors)[0]));
- if (!bank->sectors)
- return ERROR_FLASH_BANK_NOT_PROBED;
- /* Fill out the sector information: all NRF51 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;
+ if (bank->base == NRF51_FLASH_BASE) {
+ res = target_read_u32(chip->target, NRF51_FICR_CODEPAGESIZE,
+ &chip->code_page_size);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Couldn't read code page size");
+ return res;
+ }
- /* mark as unknown */
- bank->sectors[i].is_erased = -1;
- bank->sectors[i].is_protected = -1;
- }
+ res = target_read_u32(chip->target, NRF51_FICR_CODESIZE,
+ &chip->code_memory_size);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Couldn't read code memory size");
+ return res;
+ }
- nrf51_protect_check(bank);
+ if (spec && chip->code_memory_size != spec->flash_size_kb) {
+ LOG_ERROR("Chip's reported Flash capacity does not match expected one");
+ return ERROR_FAIL;
+ }
- chip->probed = true;
+ bank->size = chip->code_memory_size * 1024;
+ bank->num_sectors = bank->size / chip->code_page_size;
+ bank->sectors = calloc(bank->num_sectors,
+ sizeof((bank->sectors)[0]));
+ if (!bank->sectors)
+ return ERROR_FLASH_BANK_NOT_PROBED;
+
+ /* Fill out the sector information: all NRF51 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;
+
+ /* mark as unknown */
+ bank->sectors[i].is_erased = -1;
+ bank->sectors[i].is_protected = -1;
+ }
+
+ nrf51_protect_check(bank);
+
+ chip->bank[0].probed = true;
+ } else {
+ bank->size = NRF51_UICR_SIZE;
+ bank->num_sectors = 1;
+ bank->sectors = calloc(bank->num_sectors,
+ sizeof((bank->sectors)[0]));
+ if (!bank->sectors)
+ return ERROR_FLASH_BANK_NOT_PROBED;
+
+ bank->sectors[0].size = bank->size;
+ bank->sectors[0].offset = 0;
+
+ /* mark as unknown */
+ bank->sectors[0].is_erased = 0;
+ bank->sectors[0].is_protected = 0;
+
+ chip->bank[1].probed = true;
+ }
return ERROR_OK;
}
static int nrf51_auto_probe(struct flash_bank *bank)
{
- struct nrf51_info *chip = (struct nrf51_info *)bank->driver_priv;
+ int probed = nrf51_bank_is_probed(bank);
- if (chip->probed)
+ if (probed < 0)
+ return probed;
+ else if (probed)
return ERROR_OK;
-
- return nrf51_probe(bank);
+ else
+ return nrf51_probe(bank);
}
static struct flash_sector *nrf51_find_sector_by_address(struct flash_bank *bank, uint32_t address)
{
- struct nrf51_info *chip = (struct nrf51_info *)bank->driver_priv;
+ struct nrf51_info *chip = bank->driver_priv;
for (int i = 0; i < bank->num_sectors; i++)
if (bank->sectors[i].offset <= address &&
if (sector->is_protected)
return ERROR_FAIL;
- res = nrf51_nvmc_generic_erase(chip,
- NRF51_NVMC_ERASEPAGE,
- sector->offset);
+ if (sector->offset == NRF51_UICR_BASE) {
+ uint32_t ppfc;
+ res = target_read_u32(chip->target, NRF51_FICR_PPFC,
+ &ppfc);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Couldn't read PPFC register");
+ return res;
+ }
+
+ if ((ppfc & 0xFF) == 0xFF) {
+ LOG_ERROR("The chip was not pre-programmed with SoftDevice stack and UICR cannot be erased separately. Please issue mass erase before trying to write to this region");
+ return ERROR_FAIL;
+ };
+
+ res = nrf51_nvmc_generic_erase(chip,
+ NRF51_NVMC_ERASEUICR,
+ 0x00000001);
+
+
+ } else {
+ res = nrf51_nvmc_generic_erase(chip,
+ NRF51_NVMC_ERASEPAGE,
+ sector->offset);
+ }
+
if (res == ERROR_OK)
sector->is_erased = 1;
return res;
}
-static int nrf51_write_page(struct flash_bank *bank, uint32_t offset, uint8_t *buffer)
+static int nrf51_ll_flash_write(struct nrf51_info *chip, uint32_t offset, const uint8_t *buffer, uint32_t buffer_size)
{
- assert(offset % 4 == 0);
+ int res;
+ assert(buffer_size % 4 == 0);
+
+ for (; buffer_size > 0; buffer_size -= 4) {
+ res = target_write_memory(chip->target, offset, 4, 1, buffer);
+ if (res != ERROR_OK)
+ return res;
+
+ res = nrf51_wait_for_nvmc(chip);
+ if (res != ERROR_OK)
+ return res;
+
+ offset += 4;
+ buffer += 4;
+ }
+ return ERROR_OK;
+}
+
+static int nrf51_write_page(struct flash_bank *bank, uint32_t offset, const uint8_t *buffer)
+{
+ assert(offset % 4 == 0);
int res = ERROR_FAIL;
- struct nrf51_info *chip = (struct nrf51_info *)bank->driver_priv;
+ struct nrf51_info *chip = bank->driver_priv;
struct flash_sector *sector = nrf51_find_sector_by_address(bank, offset);
if (!sector)
- goto error;
+ return ERROR_FLASH_SECTOR_INVALID;
if (sector->is_protected)
goto error;
if (!sector->is_erased) {
res = nrf51_erase_page(chip, sector);
- if (res != ERROR_OK)
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to erase sector @ 0x%08"PRIx32, sector->offset);
goto error;
+ }
}
res = nrf51_nvmc_write_enable(chip);
goto error;
sector->is_erased = 0;
- res = target_write_memory(bank->target, offset, 4,
- chip->code_page_size / 4, buffer);
+
+ res = nrf51_ll_flash_write(chip, offset, buffer, chip->code_page_size);
if (res != ERROR_OK)
goto set_read_only;
return res;
}
-static int nrf51_write(struct flash_bank *bank, uint8_t *buffer,
- uint32_t offset, uint32_t count)
+static int nrf51_code_flash_write(struct flash_bank *bank,
+ struct nrf51_info *chip,
+ const uint8_t *buffer, uint32_t offset, uint32_t count)
{
int res;
struct {
uint32_t start, end;
} region;
- struct nrf51_info *chip;
-
- res = nrf51_get_probed_chip_if_halted(bank, &chip);
- if (res != ERROR_OK)
- return res;
region.start = offset;
region.end = offset + count;
struct {
size_t length;
- uint8_t *buffer;
+ const uint8_t *buffer;
} start_extra, end_extra;
start_extra.length = region.start % chip->code_page_size;
return ERROR_OK;
}
-FLASH_BANK_COMMAND_HANDLER(nrf51_flash_bank_command)
+static int nrf51_uicr_flash_write(struct flash_bank *bank,
+ struct nrf51_info *chip,
+ const uint8_t *buffer, uint32_t offset, uint32_t count)
{
- struct nrf51_info *chip;
+ int res;
+ uint8_t uicr[NRF51_UICR_SIZE];
+ struct flash_sector *sector = &bank->sectors[0];
- /* Create a new chip */
- chip = calloc(1, sizeof(*chip));
- if (!chip)
+ if ((offset + count) > NRF51_UICR_SIZE)
return ERROR_FAIL;
- chip->target = bank->target;
- chip->probed = false;
+ res = target_read_memory(bank->target,
+ NRF51_UICR_BASE,
+ 1,
+ NRF51_UICR_SIZE,
+ uicr);
- bank->driver_priv = chip;
+ if (res != ERROR_OK)
+ return res;
+
+ if (!sector->is_erased) {
+ res = nrf51_erase_page(chip, sector);
+ if (res != ERROR_OK)
+ return res;
+ }
+
+ res = nrf51_nvmc_write_enable(chip);
+ if (res != ERROR_OK)
+ return res;
+
+ memcpy(&uicr[offset], buffer, count);
+
+ res = nrf51_ll_flash_write(chip, NRF51_UICR_BASE, uicr, NRF51_UICR_SIZE);
+ if (res != ERROR_OK) {
+ nrf51_nvmc_read_only(chip);
+ return res;
+ }
+
+ return nrf51_nvmc_read_only(chip);
+}
+
+
+static int nrf51_write(struct flash_bank *bank, const uint8_t *buffer,
+ uint32_t offset, uint32_t count)
+{
+ int res;
+ struct nrf51_info *chip;
+
+ res = nrf51_get_probed_chip_if_halted(bank, &chip);
+ if (res != ERROR_OK)
+ return res;
+
+ return chip->bank[bank->bank_number].write(bank, chip, buffer, offset, count);
+}
- if (bank->base != NRF51_FLASH_BASE) {
- LOG_ERROR("Address 0x%08" PRIx32 " invalid bank address (try 0x%08" PRIx32
- "[nrf51 series] )",
- bank->base, NRF51_FLASH_BASE);
+
+FLASH_BANK_COMMAND_HANDLER(nrf51_flash_bank_command)
+{
+ static struct nrf51_info *chip;
+
+ switch (bank->base) {
+ case NRF51_FLASH_BASE:
+ bank->bank_number = 0;
+ break;
+ case NRF51_UICR_BASE:
+ bank->bank_number = 1;
+ break;
+ default:
+ LOG_ERROR("Invalid bank address 0x%08" PRIx32, bank->base);
return ERROR_FAIL;
}
+ if (!chip) {
+ /* Create a new chip */
+ chip = calloc(1, sizeof(*chip));
+ if (!chip)
+ return ERROR_FAIL;
+
+ chip->target = bank->target;
+ }
+
+ switch (bank->base) {
+ case NRF51_FLASH_BASE:
+ chip->bank[bank->bank_number].write = nrf51_code_flash_write;
+ break;
+ case NRF51_UICR_BASE:
+ chip->bank[bank->bank_number].write = nrf51_uicr_flash_write;
+ break;
+ }
+
+ chip->bank[bank->bank_number].probed = false;
+ bank->driver_priv = chip;
+
return ERROR_OK;
}
COMMAND_HANDLER(nrf51_handle_mass_erase_command)
{
int res;
- struct flash_bank *bank;
+ struct flash_bank *bank = NULL;
+ struct target *target = get_current_target(CMD_CTX);
- res = get_flash_bank_by_num(0, &bank);
+ res = get_flash_bank_by_addr(target, NRF51_FLASH_BASE, true, &bank);
if (res != ERROR_OK)
return res;
+ assert(bank != NULL);
+
struct nrf51_info *chip;
res = nrf51_get_probed_chip_if_halted(bank, &chip);
uint32_t ppfc;
- res = target_read_u32(chip->target, NRF51_FICR_PPFC,
+ res = target_read_u32(target, NRF51_FICR_PPFC,
&ppfc);
if (res != ERROR_OK) {
LOG_ERROR("Couldn't read PPFC register");
for (int i = 0; i < bank->num_sectors; i++)
bank->sectors[i].is_erased = 1;
- return nrf51_protect_check(bank);
+ res = nrf51_protect_check(bank);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to check chip's write protection");
+ return res;
+ }
+
+ res = get_flash_bank_by_addr(target, NRF51_UICR_BASE, true, &bank);
+ if (res != ERROR_OK)
+ return res;
+
+ bank->sectors[0].is_erased = 1;
+
+ return ERROR_OK;
}
static int nrf51_info(struct flash_bank *bank, char *buf, int buf_size)
"ram block 1 size: %"PRIu32"B\n"
"ram block 2 size: %"PRIu32"B\n"
"ram block 3 size: %"PRIu32 "B\n"
+ "config id: %" PRIx32 "\n"
+ "device id: 0x%"PRIx32"%08"PRIx32"\n"
"encryption root: 0x%08"PRIx32"%08"PRIx32"%08"PRIx32"%08"PRIx32"\n"
"identity root: 0x%08"PRIx32"%08"PRIx32"%08"PRIx32"%08"PRIx32"\n"
"device address type: 0x%"PRIx32"\n"
"device address: 0x%"PRIx32"%08"PRIx32"\n"
- "override enable: %"PRIu32"\n"
+ "override enable: %"PRIx32"\n"
"NRF_1MBIT values: %"PRIx32" %"PRIx32" %"PRIx32" %"PRIx32" %"PRIx32"\n"
"BLE_1MBIT values: %"PRIx32" %"PRIx32" %"PRIx32" %"PRIx32" %"PRIx32"\n"
"\n[user information control block]\n\n"
(ficr[6].value == 0xFFFFFFFF) ? 0 : ficr[6].value,
(ficr[7].value == 0xFFFFFFFF) ? 0 : ficr[7].value,
(ficr[8].value == 0xFFFFFFFF) ? 0 : ficr[8].value,
- ficr[9].value, ficr[9].value, ficr[9].value, ficr[9].value,
- ficr[10].value, ficr[11].value, ficr[12].value, ficr[13].value,
- ficr[14].value,
- ficr[15].value, ficr[16].value,
- ficr[17].value,
- ficr[18].value, ficr[19].value, ficr[20].value, ficr[21].value, ficr[22].value,
- ficr[23].value, ficr[24].value, ficr[25].value, ficr[26].value, ficr[27].value,
+ ficr[9].value,
+ ficr[10].value, ficr[11].value,
+ ficr[12].value, ficr[13].value, ficr[14].value, ficr[15].value,
+ ficr[16].value, ficr[17].value, ficr[18].value, ficr[19].value,
+ ficr[20].value,
+ ficr[21].value, ficr[22].value,
+ ficr[23].value,
+ ficr[24].value, ficr[25].value, ficr[26].value, ficr[27].value, ficr[28].value,
+ ficr[29].value, ficr[30].value, ficr[31].value, ficr[32].value, ficr[33].value,
(uicr[0].value == 0xFFFFFFFF) ? 0 : uicr[0].value / 1024,
uicr[1].value & 0xFFFF,
uicr[2].value & 0xFF,