Ejemplo n.º 1
0
static int flash_nrf5_erase(struct device *dev, off_t addr, size_t size)
{
	uint32_t pg_size = NRF_FICR->CODEPAGESIZE;
	uint32_t n_pages = size / pg_size;

	/* Erase can only be done per page */
	if (((addr % pg_size) != 0) || ((size % pg_size) != 0) ||
			(n_pages == 0)) {
		return -EINVAL;
	}

	if (!is_addr_valid(addr, size)) {
		return -EINVAL;
	}

	/* Erase uses a specific configuration register */
	NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Een << NVMC_CONFIG_WEN_Pos;
	nvmc_wait_ready();

	for (uint32_t i = 0; i < n_pages; i++) {
		NRF_NVMC->ERASEPAGE = (uint32_t)addr + (i * pg_size);
		nvmc_wait_ready();
	}

	NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos;
	nvmc_wait_ready();

	return 0;
}
Ejemplo n.º 2
0
static int flash_nrf5_write(struct device *dev, off_t addr,
			     const void *data, size_t len)
{
	uint32_t addr_word;
	uint32_t tmp_word;
	void *data_word;
	uint32_t remaining = len;
	uint32_t count = 0;

	if (!is_addr_valid(addr, len)) {
		return -EINVAL;
	}

	/* Start with a word-aligned address and handle the offset */
	addr_word = addr & ~0x3;

	/* If not aligned, read first word, update and write it back */
	if (!is_aligned_32(addr)) {
		tmp_word = *(uint32_t *)(addr_word);
		count = sizeof(uint32_t) - (addr & 0x3);
		if (count > len) {
			count = len;
		}
		memcpy((uint8_t *)&tmp_word + (addr & 0x3), data, count);
		nvmc_wait_ready();
		*(uint32_t *)addr_word = tmp_word;
		addr_word = addr + count;
		remaining -= count;
	}

	/* Write all the 4-byte aligned data */
	data_word = (void *) data + count;
	while (remaining >= sizeof(uint32_t)) {
		nvmc_wait_ready();
		*(uint32_t *)addr_word = *(uint32_t *)data_word;
		addr_word += sizeof(uint32_t);
		data_word += sizeof(uint32_t);
		remaining -= sizeof(uint32_t);
	}

	/* Write remaining data */
	if (remaining) {
		tmp_word = *(uint32_t *)(addr_word);
		memcpy((uint8_t *)&tmp_word, data_word, remaining);
		nvmc_wait_ready();
		*(uint32_t *)addr_word = tmp_word;
	}

	nvmc_wait_ready();

	return 0;
}
Ejemplo n.º 3
0
static int flash_nrf5_write_protection(struct device *dev, bool enable)
{
	if (enable) {
		NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos;
	} else {
		NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos;
	}
	nvmc_wait_ready();

	return 0;
}
Ejemplo n.º 4
0
static int write_op(void *context)
{
	struct flash_context *w_ctx = context;
	u32_t addr_word;
	u32_t tmp_word;
	u32_t count;

#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
	u32_t ticks_begin = 0;
	u32_t ticks_diff;
	u32_t i = 1;

	if (w_ctx->enable_time_limit) {
		ticks_begin = ticker_ticks_now_get();
	}
#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */

	/* Start with a word-aligned address and handle the offset */
	addr_word = (u32_t)w_ctx->flash_addr & ~0x3;

	/* If not aligned, read first word, update and write it back */
	if (!is_aligned_32(w_ctx->flash_addr)) {
		tmp_word = *(u32_t *)(addr_word);

		count = sizeof(u32_t) - (w_ctx->flash_addr & 0x3);
		if (count > w_ctx->len) {
			count = w_ctx->len;
		}

		memcpy((u8_t *)&tmp_word + (w_ctx->flash_addr & 0x3),
		       (void *)w_ctx->data_addr,
		       count);
		nvmc_wait_ready();
		*(u32_t *)addr_word = tmp_word;

		shift_write_context(count, w_ctx);

#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
		if (w_ctx->enable_time_limit) {
			ticks_diff =
				ticker_ticks_diff_get(ticker_ticks_now_get(),
						      ticks_begin);
			if (2 * ticks_diff >
			    HAL_TICKER_US_TO_TICKS(w_ctx->slot)) {
				nvmc_wait_ready();
				return FLASH_OP_ONGOING;
			}
		}
#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */
	}

	/* Write all the 4-byte aligned data */
	while (w_ctx->len >= sizeof(u32_t)) {
		nvmc_wait_ready();
		*(u32_t *)w_ctx->flash_addr =
				UNALIGNED_GET((u32_t *)w_ctx->data_addr);

		shift_write_context(sizeof(u32_t), w_ctx);

#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
		i++;

		if (w_ctx->enable_time_limit) {
			ticks_diff =
				ticker_ticks_diff_get(ticker_ticks_now_get(),
						      ticks_begin);
			if (ticks_diff + ticks_diff/i >
			    HAL_TICKER_US_TO_TICKS(w_ctx->slot)) {
				nvmc_wait_ready();
				return FLASH_OP_ONGOING;
			}
		}
#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */
	}

	/* Write remaining data */
	if (w_ctx->len) {
		tmp_word = *(u32_t *)(w_ctx->flash_addr);
		memcpy((u8_t *)&tmp_word, (void *)w_ctx->data_addr, w_ctx->len);
		nvmc_wait_ready();
		*(u32_t *)w_ctx->flash_addr = tmp_word;

		shift_write_context(w_ctx->len, w_ctx);
	}

	nvmc_wait_ready();

	return FLASH_OP_DONE;
}
Ejemplo n.º 5
0
static int erase_in_timeslice(u32_t addr, u32_t size)
{
	struct flash_context context = {
		.flash_addr = addr,
		.len = size,
		.enable_time_limit = 1, /* enable time limit */
		.interval = FLASH_INTERVAL_ERASE,
		.slot = FLASH_SLOT_ERASE
	};

	struct flash_op_desc flash_op_desc = {
		.handler = erase_op,
		.context = &context
	};

	return work_in_time_slice(&flash_op_desc);
}

static int write_in_timeslice(off_t addr, const void *data, size_t len)
{
	struct flash_context context = {
		.data_addr = (u32_t) data,
		.flash_addr = addr,
		.len = len,
		.enable_time_limit = 1, /* enable time limit */
		.interval = FLASH_INTERVAL_WRITE,
		.slot = FLASH_SLOT_WRITE
	};

	struct flash_op_desc flash_op_desc = {
		.handler = write_op,
		.context = &context
	};

	return  work_in_time_slice(&flash_op_desc);
}

#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */

static int erase_op(void *context)
{
	u32_t prev_nvmc_cfg = NRF_NVMC->CONFIG;
	u32_t pg_size = NRF_FICR->CODEPAGESIZE;
	struct flash_context *e_ctx = context;

#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
	u32_t ticks_begin = 0;
	u32_t ticks_diff;
	u32_t i = 0;

	if (e_ctx->enable_time_limit) {
		ticks_begin = ticker_ticks_now_get();
	}
#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */

	/* Erase uses a specific configuration register */
	NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Een << NVMC_CONFIG_WEN_Pos;
	nvmc_wait_ready();

	do {
		NRF_NVMC->ERASEPAGE = e_ctx->flash_addr;
		nvmc_wait_ready();

		e_ctx->len -= pg_size;
		e_ctx->flash_addr += pg_size;

#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
		i++;

		if (e_ctx->enable_time_limit) {
			ticks_diff =
				ticker_ticks_diff_get(ticker_ticks_now_get(),
						      ticks_begin);
			if (ticks_diff + ticks_diff/i >
			    HAL_TICKER_US_TO_TICKS(e_ctx->slot)) {
				break;
			}
		}
#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */

	} while (e_ctx->len > 0);

	NRF_NVMC->CONFIG = prev_nvmc_cfg;
	nvmc_wait_ready();

	return (e_ctx->len > 0) ? FLASH_OP_ONGOING : FLASH_OP_DONE;
}

static void shift_write_context(u32_t shift, struct flash_context *w_ctx)
{
	w_ctx->flash_addr += shift;
	w_ctx->data_addr += shift;
	w_ctx->len -= shift;
}