NRF51_UICR_BASE = 0x10001000, /* User Information
* Configuration Regsters */
+ NRF51_UICR_SIZE = 0x100,
+
#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 &&
0x00000001);
}
-static int nrf51_erase_page(struct nrf51_info *chip, struct flash_sector *sector)
+static int nrf51_erase_page(struct flash_bank *bank,
+ struct nrf51_info *chip,
+ struct flash_sector *sector)
{
int res;
if (sector->is_protected)
return ERROR_FAIL;
- res = nrf51_nvmc_generic_erase(chip,
- NRF51_NVMC_ERASEPAGE,
- sector->offset);
+ if (bank->base == 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) {
+ /* We can't erase the UICR. Double-check to
+ see if it's already erased before complaining. */
+ default_flash_blank_check(bank);
+ if (sector->is_erased == 1)
+ return ERROR_OK;
+
+ 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 (sector->is_erased != 1) {
+ res = nrf51_erase_page(bank, chip, sector);
+ 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;
/* For each sector to be erased */
for (int s = first; s <= last && res == ERROR_OK; s++)
- res = nrf51_erase_page(chip, &bank->sectors[s]);
+ res = nrf51_erase_page(bank, chip, &bank->sectors[s]);
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 != 1) {
+ res = nrf51_erase_page(bank, chip, sector);
+ if (res != ERROR_OK)
+ return res;
+ }
+
+ res = nrf51_nvmc_write_enable(chip);
+ if (res != ERROR_OK)
+ return res;
- 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);
+ 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);
+}
+
+
+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)
if (res != ERROR_OK)
return res;
- struct {
- uint32_t address, value;
+ static struct {
+ const uint32_t address;
+ uint32_t value;
} ficr[] = {
{ .address = NRF51_FICR_CODEPAGESIZE },
{ .address = NRF51_FICR_CODESIZE },