Esempio n. 1
0
/*---------------------------------------------------------------------------*/
static int
write(unsigned long addr, const unsigned char * buffer, unsigned long len)
{
  enum status_code ret;
  unsigned long page_addr;
  int copy_len, i;

  if (!opened) {
    return -1;
  }

  /* Check if the row address is valid */
  if ((addr + len) >
      ((uint32_t)parameters.page_size * parameters.nvm_number_of_pages)) {
    WARN("addr is outside flash region");
    return -1;
  }

  while (len > 0) {
    /* Determine start address of page */
    page_addr = (addr / SECTOR_SIZE) * SECTOR_SIZE;

    /* Copy content of current page to tmp_buffer */
    memcpy(tmp_buffer, (void *)page_addr, SECTOR_SIZE);

    /* Wait until flash is ready */
    while (!nvm_is_ready());

    /* Erase current page */
    nvm_erase_row(page_addr);

    /* Determine number of bytes to copy */
    copy_len = min(len, (page_addr + SECTOR_SIZE) - addr);

    /* Update tmp_buffer with content from buffer */
    memcpy(&tmp_buffer[addr - page_addr], buffer, copy_len);

    /* Update buffer, len and addr */
    len    -= copy_len;
    addr   += copy_len;
    buffer += copy_len;

    /* Wait until flash is ready */
    while (!nvm_is_ready());

    /* Write contents of tmp_buffer to flash in NVMCTRL_ROW_PAGES steps */
    for (i = 0; i < NVMCTRL_ROW_PAGES; i++) {
      if ((ret = nvm_write_buffer(page_addr + i * PAGE_SIZE, &tmp_buffer[i * PAGE_SIZE], PAGE_SIZE)) != STATUS_OK) {
        WARN("nvm_write_buffer error: %d", ret);
        return -1;
      }
    }
  }

  /* Return OK */
  return 1;
}
Esempio n. 2
0
/*---------------------------------------------------------------------------*/
static int
erase(unsigned long from, unsigned long to)
{
  enum status_code ret;
  unsigned long addr;

  if (!opened) {
    return -1;
  }

  from = (from / SECTOR_SIZE) * SECTOR_SIZE;
  to   = (to   / SECTOR_SIZE) * SECTOR_SIZE;

  for(addr = from; addr < to; addr += SECTOR_SIZE) {
    TRACE("addr: %ld", addr);
    if ((ret = nvm_erase_row(addr)) != STATUS_OK) {
      WARN("nvm_erase_row error: %d", ret);
      return -1;
    }
    watchdog_periodic();
  }

  while (!nvm_is_ready());

  return 1;
}
/**
 * \brief Reads a number of bytes from a page in the NVM memory region.
 *
 * Reads a given number of bytes from a given page address in the NVM memory
 * space into a buffer.
 *
 * \param[in]  source_address  Source page address to read from
 * \param[out] buffer          Pointer to a buffer where the content of the read
 *                             page will be stored
 * \param[in]  length          Number of bytes in the page to read
 *
 * \return Status of the page read attempt.
 *
 * \retval STATUS_OK               Requested NVM memory page was successfully
 *                                 read
 * \retval STATUS_BUSY             NVM controller was busy when the operation
 *                                 was attempted
 * \retval STATUS_ERR_BAD_ADDRESS  The requested address was outside the
 *                                 acceptable range of the NVM memory region or
 *                                 not aligned to the start of a page
 * \retval STATUS_ERR_INVALID_ARG  The supplied read length was invalid
 */
enum status_code nvm_read_buffer(
		const uint32_t source_address,
		uint8_t *const buffer,
		uint16_t length)
{
	/* Check if the source address is valid */
	if (source_address >
			((uint32_t)_nvm_dev.page_size * _nvm_dev.number_of_pages)) {
#ifdef FEATURE_NVM_RWWEE
		if (source_address >= ((uint32_t)NVMCTRL_RWW_EEPROM_SIZE + NVMCTRL_RWW_EEPROM_ADDR)
			|| source_address < NVMCTRL_RWW_EEPROM_ADDR){
			return STATUS_ERR_BAD_ADDRESS;
		}
#else
		return STATUS_ERR_BAD_ADDRESS;
#endif
	}

	/* Check if the read address is not aligned to the start of a page */
	if (source_address & (_nvm_dev.page_size - 1)) {
		return STATUS_ERR_BAD_ADDRESS;
	}

	/* Check if the write length is longer than a NVM page */
	if (length > _nvm_dev.page_size) {
		return STATUS_ERR_INVALID_ARG;
	}

	/* Get a pointer to the module hardware instance */
	Nvmctrl *const nvm_module = NVMCTRL;

	/* Check if the module is busy */
	if (!nvm_is_ready()) {
		return STATUS_BUSY;
	}

	/* Clear error flags */
	nvm_module->STATUS.reg |= NVMCTRL_STATUS_MASK;

	uint32_t page_address = source_address / 2;

	/* NVM _must_ be accessed as a series of 16-bit words, perform manual copy
	 * to ensure alignment */
	for (uint16_t i = 0; i < length; i += 2) {
		/* Fetch next 16-bit chunk from the NVM memory space */
		uint16_t data = NVM_MEMORY[page_address++];

		/* Copy first byte of the 16-bit chunk to the destination buffer */
		buffer[i] = (data & 0xFF);

		/* If we are not at the end of a read request with an odd byte count,
		 * store the next byte of data as well */
		if (i < (length - 1)) {
			buffer[i + 1] = (data >> 8);
		}
	}
Esempio n. 4
0
otError utilsFlashStatusWait(uint32_t aTimeout)
{
    otError  error = OT_ERROR_BUSY;
    uint32_t start = otPlatAlarmMilliGetNow();

    do
    {
        if (nvm_is_ready())
        {
            error = OT_ERROR_NONE;
            break;
        }
    } while (aTimeout && ((otPlatAlarmMilliGetNow() - start) < aTimeout));

    return error;
}
status_code_t nvm_sam0_read(mem_type_t mem, uint32_t address,
		uint8_t *const buffer,
		uint32_t len)
{
	switch (mem) {
		/* Get a pointer to the module hardware instance */
		Nvmctrl *const nvm_module = NVMCTRL;

	case INT_FLASH:

		/* Check if the module is busy */
		if (!nvm_is_ready()) {
			return STATUS_BUSY;
		}

		/* Clear error flags */
		nvm_module->STATUS.reg |= NVMCTRL_STATUS_MASK;

		uint32_t page_address = address / 2;

		/* NVM _must_ be accessed as a series of 16-bit words, perform
		 * manual copy
		 * to ensure alignment */
		for (uint16_t i = 0; i < len; i += 2) {
			/* Fetch next 16-bit chunk from the NVM memory space */
			uint16_t data = NVM_MEMORY[page_address++];

			/* Copy first byte of the 16-bit chunk to the
			 *destination buffer */
			buffer[i] = (data & 0xFF);

			/* If we are not at the end of a read request with an
			 * odd byte count,
			 * store the next byte of data as well */
			if (i < (len - 1)) {
				buffer[i + 1] = (data >> 8);
			}
		}

		break;

	default:
		return ERR_INVALID_ARG;
	}
Esempio n. 6
0
/**
 * \brief Sets the up the NVM hardware module based on the configuration.
 *
 * Writes a given configuration of a NVM controller configuration to the
 * hardware module, and initializes the internal device struct
 *
 * \param[in] config    Configuration settings for the NVM controller
 *
 * \note The security bit must be cleared in order successfully use this
 *       function. This can only be done by a chip erase.
 *
 * \return Status of the configuration procedure.
 *
 * \retval STATUS_OK      If the initialization was a success
 * \retval STATUS_BUSY    If the module was busy when the operation was attempted
 * \retval STATUS_ERR_IO  If the security bit has been set, preventing the
 *                        EEPROM and/or auxiliary space configuration from being
 *                        altered
 */
enum status_code nvm_set_config(
		const struct nvm_config *const config)
{
	/* Sanity check argument */
	Assert(config);

	/* Get a pointer to the module hardware instance */
	Nvmctrl *const nvm_module = NVMCTRL;

	/* Turn on the digital interface clock */
	system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBB, PM_APBBMASK_NVMCTRL);

	/* Clear error flags */
	nvm_module->STATUS.reg |= NVMCTRL_STATUS_MASK;

	/* Check if the module is busy */
	if (!nvm_is_ready()) {
		return STATUS_BUSY;
	}

	/* Writing configuration to the CTRLB register */
	nvm_module->CTRLB.reg =
			NVMCTRL_CTRLB_SLEEPPRM(config->sleep_power_mode) |
			((config->manual_page_write & 0x01) << NVMCTRL_CTRLB_MANW_Pos) |
			NVMCTRL_CTRLB_RWS(config->wait_states) |
			((config->disable_cache & 0x01) << NVMCTRL_CTRLB_CACHEDIS_Pos) |
			NVMCTRL_CTRLB_READMODE(config->cache_readmode);


	/* Initialize the internal device struct */
	_nvm_dev.page_size         = (8 << nvm_module->PARAM.bit.PSZ);
	_nvm_dev.number_of_pages   = nvm_module->PARAM.bit.NVMP;
	_nvm_dev.manual_page_write = config->manual_page_write;

	/* If the security bit is set, the auxiliary space cannot be written */
	if (nvm_module->STATUS.reg & NVMCTRL_STATUS_SB) {
		return STATUS_ERR_IO;
	}

	return STATUS_OK;
}
Esempio n. 7
0
/*---------------------------------------------------------------------------*/
static int
open(void)
{
  if (!opened) {
    struct nvm_config config_nvm;
    nvm_get_config_defaults(&config_nvm);
    nvm_set_config(&config_nvm);
    nvm_get_parameters(&parameters);

    TRACE("bootloader_number_of_pages: %ld, eeprom_number_of_pages: %ld, nvm_number_of_pages: %d, page_size: %d",
          parameters.bootloader_number_of_pages,
          parameters.eeprom_number_of_pages,
          parameters.nvm_number_of_pages,
          parameters.page_size);

    while (!nvm_is_ready());

    opened = 1;
  }

  return 1;
}
/**
 * \brief Writes a number of bytes to a page in the NVM memory region.
 *
 * Writes from a buffer to a given page address in the NVM memory.
 *
 * \param[in]  destination_address  Destination page address to write to
 * \param[in]  buffer               Pointer to buffer where the data to write is
 *                                  stored
 * \param[in]  length               Number of bytes in the page to write
 *
 * \note If writing to a page that has previously been written to, the page's
 *       row should be erased (via \ref nvm_erase_row()) before attempting to
 *       write new data to the page.
 *
 * \note For SAMD21 RWW devices, see \c SAMD21_64K, command \c NVM_COMMAND_RWWEE_WRITE_PAGE
 * must be executed before any other commands after writing a page,
 * refer to errata 13588.
 *
 * \note If manual write mode is enable, write command must be executed after
 * this function, otherwise the data will not write to NVM from page buffer.
 *
 * \return Status of the attempt to write a page.
 *
 * \retval STATUS_OK               Requested NVM memory page was successfully
 *                                 read
 * \retval STATUS_BUSY             NVM controller was busy when the operation
 *                                 was attempted
 * \retval STATUS_ERR_BAD_ADDRESS  The requested address was outside the
 *                                 acceptable range of the NVM memory region or
 *                                 not aligned to the start of a page
 * \retval STATUS_ERR_INVALID_ARG  The supplied write length was invalid
 */
enum status_code nvm_write_buffer(
		const uint32_t destination_address,
		const uint8_t *buffer,
		uint16_t length)
{
#ifdef FEATURE_NVM_RWWEE
	bool is_rww_eeprom = false;
#endif

	/* Check if the destination address is valid */
	if (destination_address >
			((uint32_t)_nvm_dev.page_size * _nvm_dev.number_of_pages)) {
#ifdef FEATURE_NVM_RWWEE
		if (destination_address >= ((uint32_t)NVMCTRL_RWW_EEPROM_SIZE + NVMCTRL_RWW_EEPROM_ADDR)
			|| destination_address < NVMCTRL_RWW_EEPROM_ADDR){
			return STATUS_ERR_BAD_ADDRESS;
		}
		is_rww_eeprom = true;
#else
		return STATUS_ERR_BAD_ADDRESS;
#endif
	}

	/* Check if the write address not aligned to the start of a page */
	if (destination_address & (_nvm_dev.page_size - 1)) {
		return STATUS_ERR_BAD_ADDRESS;
	}

	/* Check if the write length is longer than a NVM page */
	if (length > _nvm_dev.page_size) {
		return STATUS_ERR_INVALID_ARG;
	}

	/* Get a pointer to the module hardware instance */
	Nvmctrl *const nvm_module = NVMCTRL;

	/* Check if the module is busy */
	if (!nvm_is_ready()) {
		return STATUS_BUSY;
	}

	/* Erase the page buffer before buffering new data */
	nvm_module->CTRLA.reg = NVM_COMMAND_PAGE_BUFFER_CLEAR | NVMCTRL_CTRLA_CMDEX_KEY;

	/* Check if the module is busy */
	while (!nvm_is_ready()) {
		/* Force-wait for the buffer clear to complete */
	}

	/* Clear error flags */
	nvm_module->STATUS.reg |= NVMCTRL_STATUS_MASK;

	uint32_t nvm_address = destination_address / 2;

	/* NVM _must_ be accessed as a series of 16-bit words, perform manual copy
	 * to ensure alignment */
	for (uint16_t i = 0; i < length; i += 2) {
		uint16_t data;

		/* Copy first byte of the 16-bit chunk to the temporary buffer */
		data = buffer[i];

		/* If we are not at the end of a write request with an odd byte count,
		 * store the next byte of data as well */
		if (i < (length - 1)) {
			data |= (buffer[i + 1] << 8);
		}

		/* Store next 16-bit chunk to the NVM memory space */
		NVM_MEMORY[nvm_address++] = data;
	}

	/* If automatic page write mode is enable, then perform a manual NVM
	 * write when the length of data to be programmed is less than page size
	 */
	if ((_nvm_dev.manual_page_write == false) && (length < NVMCTRL_PAGE_SIZE)) {
#ifdef FEATURE_NVM_RWWEE
	 return ((is_rww_eeprom) ?
				(nvm_execute_command(NVM_COMMAND_RWWEE_WRITE_PAGE,destination_address, 0)):
	 			(nvm_execute_command(NVM_COMMAND_WRITE_PAGE,destination_address, 0)));
#else
		return nvm_execute_command(NVM_COMMAND_WRITE_PAGE,
				destination_address, 0);
#endif
	}

	return STATUS_OK;
}
/**
 * \brief Executes a command on the NVM controller.
 *
 * Executes an asynchronous command on the NVM controller, to perform a requested
 * action such as a NVM page read or write operation.
 *
 * \note The function will return before the execution of the given command is
 *       completed.
 *
 * \param[in] command    Command to issue to the NVM controller
 * \param[in] address    Address to pass to the NVM controller in NVM memory
 *                       space
 * \param[in] parameter  Parameter to pass to the NVM controller, not used
 *                       for this driver
 *
 * \return Status of the attempt to execute a command.
 *
 * \retval STATUS_OK               If the command was accepted and execution
 *                                 is now in progress
 * \retval STATUS_BUSY             If the NVM controller was already busy
 *                                 executing a command when the new command
 *                                 was issued
 * \retval STATUS_ERR_IO           If the command was invalid due to memory or
 *                                 security locking
 * \retval STATUS_ERR_INVALID_ARG  If the given command was invalid or
 *                                 unsupported
 * \retval STATUS_ERR_BAD_ADDRESS  If the given address was invalid
 */
enum status_code nvm_execute_command(
		const enum nvm_command command,
		const uint32_t address,
		const uint32_t parameter)
{
	uint32_t temp;

	/* Check that the address given is valid  */
	if (address > ((uint32_t)_nvm_dev.page_size * _nvm_dev.number_of_pages)
		&& !(address >= NVMCTRL_AUX0_ADDRESS && address <= NVMCTRL_AUX1_ADDRESS )){
#ifdef FEATURE_NVM_RWWEE
		if (address >= ((uint32_t)NVMCTRL_RWW_EEPROM_SIZE + NVMCTRL_RWW_EEPROM_ADDR)
			|| address < NVMCTRL_RWW_EEPROM_ADDR){
			return STATUS_ERR_BAD_ADDRESS;
		}
#else
		return STATUS_ERR_BAD_ADDRESS;
#endif
	}

	/* Get a pointer to the module hardware instance */
	Nvmctrl *const nvm_module = NVMCTRL;

	/* turn off cache before issuing flash commands */
	temp = nvm_module->CTRLB.reg;
#if (SAMC20) || (SAMC21)
	nvm_module->CTRLB.reg = ((temp &(~(NVMCTRL_CTRLB_CACHEDIS(0x2)))) 
							| NVMCTRL_CTRLB_CACHEDIS(0x1));
#else
	nvm_module->CTRLB.reg = temp | NVMCTRL_CTRLB_CACHEDIS;
#endif

	/* Clear error flags */
	nvm_module->STATUS.reg |= NVMCTRL_STATUS_MASK;

	/* Check if the module is busy */
	if (!nvm_is_ready()) {
		return STATUS_BUSY;
	}

	switch (command) {

		/* Commands requiring address (protected) */
		case NVM_COMMAND_ERASE_AUX_ROW:
		case NVM_COMMAND_WRITE_AUX_ROW:

			/* Auxiliary space cannot be accessed if the security bit is set */
			if (nvm_module->STATUS.reg & NVMCTRL_STATUS_SB) {
				return STATUS_ERR_IO;
			}

			/* Set address, command will be issued elsewhere */
			nvm_module->ADDR.reg = (uintptr_t)&NVM_MEMORY[address / 4];
			break;

		/* Commands requiring address (unprotected) */
		case NVM_COMMAND_ERASE_ROW:
		case NVM_COMMAND_WRITE_PAGE:
		case NVM_COMMAND_LOCK_REGION:
		case NVM_COMMAND_UNLOCK_REGION:
#ifdef FEATURE_NVM_RWWEE
		case NVM_COMMAND_RWWEE_ERASE_ROW:
		case NVM_COMMAND_RWWEE_WRITE_PAGE:
#endif

			/* Set address, command will be issued elsewhere */
			nvm_module->ADDR.reg = (uintptr_t)&NVM_MEMORY[address / 4];
			break;

		/* Commands not requiring address */
		case NVM_COMMAND_PAGE_BUFFER_CLEAR:
		case NVM_COMMAND_SET_SECURITY_BIT:
		case NVM_COMMAND_ENTER_LOW_POWER_MODE:
		case NVM_COMMAND_EXIT_LOW_POWER_MODE:
			break;

		default:
			return STATUS_ERR_INVALID_ARG;
	}

	/* Set command */
	nvm_module->CTRLA.reg = command | NVMCTRL_CTRLA_CMDEX_KEY;

	/* Wait for the nvm controller to become ready */
	while (!nvm_is_ready()) {
	}

	/* restore the setting */
	nvm_module->CTRLB.reg = temp;

	return STATUS_OK;
}