add flash ASF4 driver

this is imported from the Atmel Start website

Change-Id: I5eccb37da64c7def7b99418773d09b6d98664432
This commit is contained in:
Kevin Redon 2019-01-03 17:54:03 +01:00
parent 2257c6c32a
commit 9bebe583ae
8 changed files with 1705 additions and 3 deletions

View File

@ -0,0 +1,36 @@
/* Auto-generated config file hpl_nvmctrl_config.h */
#ifndef HPL_NVMCTRL_CONFIG_H
#define HPL_NVMCTRL_CONFIG_H
// <<< Use Configuration Wizard in Context Menu >>>
// <h> Basic Settings
// <o> Power Reduction Mode During Sleep
// <0x00=> Wake On Access
// <0x01=> Wake Up Instant
// <0x03=> Disabled
// <id> nvm_arch_sleepprm
#ifndef CONF_NVM_SLEEPPRM
#define CONF_NVM_SLEEPPRM 0
#endif
// <q> AHB0 Cache Disable
// <i> Indicate whether AHB0 cache is disable or not
// <id> nvm_arch_cache0
#ifndef CONF_NVM_CACHE0
#define CONF_NVM_CACHE0 0
#endif
// <q> AHB1 Cache Disable
// <i> Indicate whether AHB1 cache is disable or not
// <id> nvm_arch_cache1
#ifndef CONF_NVM_CACHE1
#define CONF_NVM_CACHE1 0
#endif
// </h>
// <<< end of configuration section >>>
#endif // HPL_NVMCTRL_CONFIG_H

View File

@ -41,6 +41,7 @@ hpl/gclk \
usb/device \
hpl/oscctrl \
gcc/gcc \
hpl/nvmctrl \
hpl/usb \
hpl/core \
hpl/cmcc
@ -52,6 +53,7 @@ hpl/core/hpl_core_m4.o \
usb/class/dfu/device/dfudf.o \
hal/utils/src/utils_syscalls.o \
hpl/dmac/hpl_dmac.o \
hpl/nvmctrl/hpl_nvmctrl.o \
gcc/system_same54.o \
hpl/usb/hpl_usb.o \
hal/src/hal_delay.o \
@ -65,6 +67,7 @@ hpl/oscctrl/hpl_oscctrl.o \
hpl/mclk/hpl_mclk.o \
hpl/ramecc/hpl_ramecc.o \
usb/usb_protocol.o \
hal/src/hal_flash.o \
hal/src/hal_init.o \
gcc/gcc/startup_same54.o \
hal/src/hal_usb_device.o \
@ -81,12 +84,14 @@ usb_dfu_main.o \
usb/device/usbdc.o \
hal/src/hal_atomic.o
OBJS_AS_ARGS += \
"hal/src/hal_io.o" \
"hpl/core/hpl_core_m4.o" \
"usb/class/dfu/device/dfudf.o" \
"hal/utils/src/utils_syscalls.o" \
"hpl/dmac/hpl_dmac.o" \
"hpl/nvmctrl/hpl_nvmctrl.o" \
"gcc/system_same54.o" \
"hpl/usb/hpl_usb.o" \
"hal/src/hal_delay.o" \
@ -100,6 +105,7 @@ OBJS_AS_ARGS += \
"hpl/mclk/hpl_mclk.o" \
"hpl/ramecc/hpl_ramecc.o" \
"usb/usb_protocol.o" \
"hal/src/hal_flash.o" \
"hal/src/hal_init.o" \
"gcc/gcc/startup_same54.o" \
"hal/src/hal_usb_device.o" \
@ -125,6 +131,7 @@ DEPS_AS_ARGS += \
"hpl/ramecc/hpl_ramecc.d" \
"hpl/core/hpl_core_m4.d" \
"hal/utils/src/utils_syscalls.d" \
"hpl/nvmctrl/hpl_nvmctrl.d" \
"usb/class/dfu/device/dfudf.d" \
"gcc/gcc/startup_same54.d" \
"hpl/usb/hpl_usb.d" \
@ -136,6 +143,7 @@ DEPS_AS_ARGS += \
"hpl/core/hpl_init.d" \
"hpl/pm/hpl_pm.d" \
"usb/usb_protocol.d" \
"hal/src/hal_flash.d" \
"hpl/gclk/hpl_gclk.d" \
"hal/src/hal_usb_device.d" \
"hpl/dmac/hpl_dmac.d" \
@ -199,7 +207,7 @@ $(OUTPUT_FILE_PATH): $(OBJS)
@echo ARM/GNU C Compiler
$(QUOTE)arm-none-eabi-gcc$(QUOTE) -x c -mthumb -DDEBUG -Os -ffunction-sections -mlong-calls -g3 -Wall -c -std=gnu99 \
-D__SAME54P20A__ -mcpu=cortex-m4 -mfloat-abi=softfp -mfpu=fpv4-sp-d16 \
-I"../" -I"../config" -I"../examples" -I"../hal/include" -I"../hal/utils/include" -I"../hpl/cmcc" -I"../hpl/core" -I"../hpl/dmac" -I"../hpl/gclk" -I"../hpl/mclk" -I"../hpl/osc32kctrl" -I"../hpl/oscctrl" -I"../hpl/pm" -I"../hpl/port" -I"../hpl/ramecc" -I"../hpl/usb" -I"../hri" -I"../" -I"../config" -I"../usb" -I"../usb/class/dfu" -I"../usb/class/dfu/device" -I"../usb/device" -I"../" -I"../CMSIS/Include" -I"../include" \
-I"../" -I"../config" -I"../examples" -I"../hal/include" -I"../hal/utils/include" -I"../hpl/cmcc" -I"../hpl/core" -I"../hpl/dmac" -I"../hpl/gclk" -I"../hpl/mclk" -I"../hpl/nvmctrl" -I"../hpl/osc32kctrl" -I"../hpl/oscctrl" -I"../hpl/pm" -I"../hpl/port" -I"../hpl/ramecc" -I"../hpl/usb" -I"../hri" -I"../" -I"../config" -I"../usb" -I"../usb/class/dfu" -I"../usb/class/dfu/device" -I"../usb/device" -I"../" -I"../CMSIS/Include" -I"../include" \
-MD -MP -MF "$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -MT"$(@:%.o=%.o)" -o "$@" "$<"
@echo Finished building: $<
@ -208,7 +216,7 @@ $(OUTPUT_FILE_PATH): $(OBJS)
@echo ARM/GNU Assembler
$(QUOTE)arm-none-eabi-as$(QUOTE) -x c -mthumb -DDEBUG -Os -ffunction-sections -mlong-calls -g3 -Wall -c -std=gnu99 \
-D__SAME54P20A__ -mcpu=cortex-m4 -mfloat-abi=softfp -mfpu=fpv4-sp-d16 \
-I"../" -I"../config" -I"../examples" -I"../hal/include" -I"../hal/utils/include" -I"../hpl/cmcc" -I"../hpl/core" -I"../hpl/dmac" -I"../hpl/gclk" -I"../hpl/mclk" -I"../hpl/osc32kctrl" -I"../hpl/oscctrl" -I"../hpl/pm" -I"../hpl/port" -I"../hpl/ramecc" -I"../hpl/usb" -I"../hri" -I"../" -I"../config" -I"../usb" -I"../usb/class/dfu" -I"../usb/class/dfu/device" -I"../usb/device" -I"../" -I"../CMSIS/Include" -I"../include" \
-I"../" -I"../config" -I"../examples" -I"../hal/include" -I"../hal/utils/include" -I"../hpl/cmcc" -I"../hpl/core" -I"../hpl/dmac" -I"../hpl/gclk" -I"../hpl/mclk" -I"../hpl/nvmctrl" -I"../hpl/osc32kctrl" -I"../hpl/oscctrl" -I"../hpl/pm" -I"../hpl/port" -I"../hpl/ramecc" -I"../hpl/usb" -I"../hri" -I"../" -I"../config" -I"../usb" -I"../usb/class/dfu" -I"../usb/class/dfu/device" -I"../usb/device" -I"../" -I"../CMSIS/Include" -I"../include" \
-MD -MP -MF "$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -MT"$(@:%.o=%.o)" -o "$@" "$<"
@echo Finished building: $<
@ -217,7 +225,7 @@ $(OUTPUT_FILE_PATH): $(OBJS)
@echo ARM/GNU Preprocessing Assembler
$(QUOTE)arm-none-eabi-gcc$(QUOTE) -x c -mthumb -DDEBUG -Os -ffunction-sections -mlong-calls -g3 -Wall -c -std=gnu99 \
-D__SAME54P20A__ -mcpu=cortex-m4 -mfloat-abi=softfp -mfpu=fpv4-sp-d16 \
-I"../" -I"../config" -I"../examples" -I"../hal/include" -I"../hal/utils/include" -I"../hpl/cmcc" -I"../hpl/core" -I"../hpl/dmac" -I"../hpl/gclk" -I"../hpl/mclk" -I"../hpl/osc32kctrl" -I"../hpl/oscctrl" -I"../hpl/pm" -I"../hpl/port" -I"../hpl/ramecc" -I"../hpl/usb" -I"../hri" -I"../" -I"../config" -I"../usb" -I"../usb/class/dfu" -I"../usb/class/dfu/device" -I"../usb/device" -I"../" -I"../CMSIS/Include" -I"../include" \
-I"../" -I"../config" -I"../examples" -I"../hal/include" -I"../hal/utils/include" -I"../hpl/cmcc" -I"../hpl/core" -I"../hpl/dmac" -I"../hpl/gclk" -I"../hpl/mclk" -I"../hpl/nvmctrl" -I"../hpl/osc32kctrl" -I"../hpl/oscctrl" -I"../hpl/pm" -I"../hpl/port" -I"../hpl/ramecc" -I"../hpl/usb" -I"../hri" -I"../" -I"../config" -I"../usb" -I"../usb/class/dfu" -I"../usb/class/dfu/device" -I"../usb/device" -I"../" -I"../CMSIS/Include" -I"../include" \
-MD -MP -MF "$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -MT"$(@:%.o=%.o)" -o "$@" "$<"
@echo Finished building: $<

View File

@ -0,0 +1,52 @@
The Flash Driver
================
Flash is a re-programmable memory that retains program and data
storage even with power off.
User can write or read several bytes from any valid address in a flash.
As to the erase/lock/unlock command, the input parameter of address should
be a bytes address aligned with the page start, otherwise, the command will fail
to be executed. At the meantime, the number of pages that can be locked or unlocked
at once depends on region size of the flash. User can get the real number
from the function return value which could be different for the different devices.
Features
--------
* Initialization/de-initialization
* Writing/Reading bytes
* Locking/Unlocking/Erasing pages
* Notifications about errors or being ready for a new command
Applications
------------
* Mini disk which can retain program and data storage
* Boot loader
* Non volatile storage
Dependencies
------------
The peripheral which controls a re-programmable flash memory.
Concurrency
-----------
N/A
Limitations
-----------
User should pay attention to set a proper stack size in their application,
since the driver manages a temporary buffer in stack to cache unchanged data
when calling flash write and erase function.
Due to flash memory architecture of SAMD21/D20/L21/L22/C20/C21/D09/D10/D11/R21,
write operation erazes row content before each write.
Known issues and workarounds
----------------------------
N/A

209
hal/include/hal_flash.h Normal file
View File

@ -0,0 +1,209 @@
/**
* \file
*
* \brief Flash related functionality declaration.
*
* Copyright (c) 2015-2018 Microchip Technology Inc. and its subsidiaries.
*
* \asf_license_start
*
* \page License
*
* Subject to your compliance with these terms, you may use Microchip
* software and any derivatives exclusively with Microchip products.
* It is your responsibility to comply with third party license terms applicable
* to your use of third party software (including open source software) that
* may accompany Microchip software.
*
* THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES,
* WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE,
* INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY,
* AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE
* LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL
* LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE
* SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE
* POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT
* ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY
* RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
* THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
*
* \asf_license_stop
*
*/
#ifndef _HAL_FLASH_H_INCLUDED
#define _HAL_FLASH_H_INCLUDED
#include <hpl_flash.h>
/**
* \addtogroup doc_driver_hal_flash
*
* @{
*/
#ifdef __cplusplus
extern "C" {
#endif
/* Forward declaration of flash_descriptor. */
struct flash_descriptor;
/** The callback types */
enum flash_cb_type {
/** Callback type for ready to accept a new command */
FLASH_CB_READY,
/** Callback type for error */
FLASH_CB_ERROR,
FLASH_CB_N
};
/** \brief Prototype of callback on FLASH
*
*/
typedef void (*flash_cb_t)(struct flash_descriptor *const descr);
/** \brief FLASH HAL callbacks
*
*/
struct flash_callbacks {
/** Callback invoked when ready to accept a new command */
flash_cb_t cb_ready;
/** Callback invoked when error occurs */
flash_cb_t cb_error;
};
/** \brief FLASH HAL driver struct for asynchronous access
*/
struct flash_descriptor {
/** Pointer to FLASH device instance */
struct _flash_device dev;
/** Callbacks for asynchronous transfer */
struct flash_callbacks callbacks;
};
/** \brief Initialize the FLASH HAL instance and hardware for callback mode
*
* Initialize FLASH HAL with interrupt mode (uses callbacks).
*
* \param[in, out] flash Pointer to the HAL FLASH instance.
* \param[in] hw Pointer to the hardware base.
* \return Initialize status.
*/
int32_t flash_init(struct flash_descriptor *flash, void *const hw);
/** \brief Deinitialize the FLASH HAL instance
*
* Abort transfer, disable and reset FLASH, and deinitialize software.
*
* \param[in, out] flash Pointer to the HAL FLASH instance.
* \return Deinitialze status.
*/
int32_t flash_deinit(struct flash_descriptor *flash);
/** \brief Writes a number of bytes to a page in the internal Flash
*
* \param[in, out] flash Pointer to the HAL FLASH instance.
* \param[in] dst_addr Destination bytes address to write into flash
* \param[in] buffer Pointer to a buffer where the content
* will be written to the flash
* \param[in] length Number of bytes to write
* \return Write status.
*/
int32_t flash_write(struct flash_descriptor *flash, uint32_t dst_addr, uint8_t *buffer, uint32_t length);
/** \brief Appends a number of bytes to a page in the internal Flash
*
* This functions never erases the flash before writing.
*
* \param[in, out] flash Pointer to the HAL FLASH instance.
* \param[in] dst_addr Destination bytes address to write to flash
* \param[in] buffer Pointer to a buffer with data to write to flash
* \param[in] length Number of bytes to append
* \return Append status.
*/
int32_t flash_append(struct flash_descriptor *flash, uint32_t dst_addr, uint8_t *buffer, uint32_t length);
/** \brief Reads a number of bytes to a page in the internal Flash
*
* \param[in, out] flash Pointer to the HAL FLASH instance.
* \param[in] src_addr Source bytes address to read from flash
* \param[out] buffer Pointer to a buffer where the content
* of the read pages will be stored
* \param[in] length Number of bytes to read
* \return Read status.
*/
int32_t flash_read(struct flash_descriptor *flash, uint32_t src_addr, uint8_t *buffer, uint32_t length);
/** \brief Register a function as FLASH transfer completion callback
*
* \param[in, out] flash Pointer to the HAL FLASH instance.
* \param[in] type Callback type (\ref flash_cb_type).
* \param[in] func Pointer to callback function.
* \retval 0 Success
* \retval -1 Error
*/
int32_t flash_register_callback(struct flash_descriptor *flash, const enum flash_cb_type type, flash_cb_t func);
/** \brief Execute lock in the internal flash
*
* \param[in, out] flash Pointer to the HAL FLASH instance.
* \param[in] dst_addr Destination bytes address aligned with page
* start to be locked
* \param[in] page_nums Number of pages to be locked
*
* \return Real locked numbers of pages.
*/
int32_t flash_lock(struct flash_descriptor *flash, const uint32_t dst_addr, const uint32_t page_nums);
/** \brief Execute unlock in the internal flash
*
* \param[in, out] flash Pointer to the HAL FLASH instance.
* \param[in] dst_addr Destination bytes address aligned with page
* start to be unlocked
* \param[in] page_nums Number of pages to be unlocked
*
* \return Real unlocked numbers of pages.
*/
int32_t flash_unlock(struct flash_descriptor *flash, const uint32_t dst_addr, const uint32_t page_nums);
/** \brief Execute erase in the internal flash
*
* \param[in, out] flash Pointer to the HAL FLASH instance.
* \param[in] dst_addr Destination bytes address aligned with page
* start to be erased
* \param[in] page_nums Number of pages to be erased
* \retval 0 Success
* \retval -1 Error
*/
int32_t flash_erase(struct flash_descriptor *flash, const uint32_t dst_addr, const uint32_t page_nums);
/**
* \brief Get the flash page size
*
* \param[in, out] flash Pointer to the HAL FLASH instance
*
* \return The flash page size
*/
uint32_t flash_get_page_size(struct flash_descriptor *flash);
/**
* \brief Get the number of flash page
*
* \param[in, out] flash Pointer to the HAL FLASH instance.
*
* \return The flash total page numbers
*/
uint32_t flash_get_total_pages(struct flash_descriptor *flash);
/** \brief Retrieve the current driver version
*
* \return Current driver version.
*/
uint32_t flash_get_version(void);
#ifdef __cplusplus
}
#endif
/**@}*/
#endif /* ifndef _HAL_FLASH_H_INCLUDED */

265
hal/include/hpl_flash.h Normal file
View File

@ -0,0 +1,265 @@
/**
* \file
*
* \brief FLASH related functionality declaration.
*
* Copyright (c) 2015-2018 Microchip Technology Inc. and its subsidiaries.
*
* \asf_license_start
*
* \page License
*
* Subject to your compliance with these terms, you may use Microchip
* software and any derivatives exclusively with Microchip products.
* It is your responsibility to comply with third party license terms applicable
* to your use of third party software (including open source software) that
* may accompany Microchip software.
*
* THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES,
* WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE,
* INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY,
* AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE
* LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL
* LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE
* SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE
* POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT
* ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY
* RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
* THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
*
* \asf_license_stop
*
*/
#ifndef _HPL_FLASH_H_INCLUDED
#define _HPL_FLASH_H_INCLUDED
/**
* \addtogroup hpl__flash__group FLASH HPL APIs
*
*/
/**@{*/
#include <compiler.h>
#include "hpl_irq.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief FLASH device structure
*
* The FLASH device structure forward declaration.
*/
struct _flash_device;
/** The callback types */
enum _flash_cb_type { FLASH_DEVICE_CB_READY, FLASH_DEVICE_CB_ERROR, FLASH_DEVICE_CB_N };
/**
* \brief FLASH interrupt handlers structure
*/
struct _flash_callback {
/** Ready to accept new command handler */
void (*ready_cb)(struct _flash_device *device);
/** Error handler */
void (*error_cb)(struct _flash_device *device);
};
/**
* \brief FLASH descriptor device structure.
*/
struct _flash_device {
struct _flash_callback flash_cb; /*!< Interrupt handers */
struct _irq_descriptor irq; /*!< Interrupt descriptor */
void * hw; /*!< Hardware module instance handler */
};
/**
* \brief Initialize FLASH.
*
* This function does low level FLASH configuration.
*
* \param[in] device The pointer to FLASH device instance
* \param[in] hw The pointer to hardware instance
*
* \return Initialize status.
*/
int32_t _flash_init(struct _flash_device *const device, void *const hw);
/**
* \brief Deinitialize FLASH.
*
* \param[in] device The pointer to FLASH device instance
*/
void _flash_deinit(struct _flash_device *const device);
/**
* \brief Reads a number of bytes in the internal Flash.
*
* \param[in] device The pointer to FLASH device instance
* \param[in] src_addr Source bytes address to read from flash
* \param[out] buffer Pointer to a buffer where the content
* of the read page will be stored
* \param[in] length Number of bytes to read
*/
void _flash_read(struct _flash_device *const device, const uint32_t src_addr, uint8_t *buffer, uint32_t length);
/**
* \brief Writes a number of bytes in the internal Flash.
*
* \param[in] device The pointer to FLASH device instance
* \param[in] dst_addr Destination bytes address to write into flash
* \param[in] buffer Pointer to buffer where the data to
* write is stored
* \param[in] length Number of bytes to write
*/
void _flash_write(struct _flash_device *const device, const uint32_t dst_addr, uint8_t *buffer, uint32_t length);
/**
* \brief Appends a number of bytes in the internal Flash.
*
* \param[in] device The pointer to FLASH device instance
* \param[in] dst_addr Destination bytes address to write into flash
* \param[in] buffer Pointer to buffer with data to write to flash
* \param[in] length Number of bytes to write
*/
void _flash_append(struct _flash_device *const device, const uint32_t dst_addr, uint8_t *buffer, uint32_t length);
/** \brief Execute lock in the internal flash
* \param[in] device The pointer to FLASH device instance
* \param[in] dst_addr Destination bytes address aligned with page
* start to be locked
* \param[in] page_nums Number of pages to be locked
*
* \return Real locked numbers of pages.
*/
int32_t _flash_lock(struct _flash_device *const device, const uint32_t dst_addr, uint32_t page_nums);
/** \brief Execute unlock in the internal flash
* \param[in] device The pointer to FLASH device instance
* \param[in] dst_addr Destination bytes address aligned with page
* start to be unlocked
* \param[in] page_nums Number of pages to be unlocked
*
* \return Real unlocked numbers of pages.
*/
int32_t _flash_unlock(struct _flash_device *const device, const uint32_t dst_addr, uint32_t page_nums);
/** \brief check whether the region which is pointed by address
* is locked
* \param[in] device The pointer to FLASH device instance
* \param[in] dst_addr Destination bytes address to check
*
* \return The lock status of assigned address.
*/
bool _flash_is_locked(struct _flash_device *const device, const uint32_t dst_addr);
/** \brief Execute erase in the internal flash
* \param[in] device The pointer to FLASH device instance
* \param[in] dst_addr Destination bytes address aligned with page
* start to be erased
* \param[in] page_nums Number of pages to be erased
*/
void _flash_erase(struct _flash_device *const device, const uint32_t dst_addr, uint32_t page_nums);
/**
* \brief Get the flash page size.
*
* \param[in] device The pointer to FLASH device instance
*
* \return The flash page size
*/
uint32_t _flash_get_page_size(struct _flash_device *const device);
/**
* \brief Get the numbers of flash page.
*
* \param[in] device The pointer to FLASH device instance
*
* \return The flash total page numbers
*/
uint32_t _flash_get_total_pages(struct _flash_device *const device);
/**
* \brief Get the number of wait states for read and write operations.
*
* \param[in] device The pointer to FLASH device instance
*
* \return The number of wait states for read and write operations
*/
uint8_t _flash_get_wait_state(struct _flash_device *const device);
/**
* \brief Set the number of wait states for read and write operations.
*
* \param[in] device The pointer to FLASH device instance
* \param[in] state The number of wait states
*
*/
void _flash_set_wait_state(struct _flash_device *const device, uint8_t state);
/**
* \brief Enable/disable Flash interrupt
*
* param[in] device The pointer to Flash device instance
* param[in] type The type of interrupt to disable/enable if applicable
* param[in] state Enable or disable
*/
void _flash_set_irq_state(struct _flash_device *const device, const enum _flash_cb_type type, const bool state);
/*
* Below RWW flash APIs are only available for device which has RWWEE
* flash array, such as SAM C20/C21/D21/L21/L22/R30/DA1/HA1 etc.
*/
/**
* \brief Get the RWWEE flash page size.
*
* \param[in] device The pointer to FLASH device instance
*
* \return The flash page size
*/
uint32_t _rww_flash_get_page_size(struct _flash_device *const device);
/**
* \brief Get the total page numbers of RWWEE flash.
*
* \param[in] device The pointer to FLASH device instance
*
* \return The flash total page numbers
*/
uint32_t _rww_flash_get_total_pages(struct _flash_device *const device);
/**
* \brief Reads a number of bytes in the internal RWWEE Flash.
*
* \param[in] device The pointer to FLASH device instance
* \param[in] src_addr Source bytes address to read from flash
* \param[out] buffer Pointer to a buffer where the content
* of the read page will be stored
* \param[in] length Number of bytes to read
*
* \return Read status, ERR_NONE for successful read.
*/
int32_t _rww_flash_read(struct _flash_device *const device, const uint32_t src_addr, uint8_t *buffer, uint32_t length);
/**
* \brief Writes a number of bytes in the internal RWWEE Flash.
*
* \param[in] device The pointer to FLASH device instance
* \param[in] dst_addr Destination bytes address to write into flash
* \param[in] buffer Pointer to buffer where the data to
* write is stored
* \param[in] length Number of bytes to write
*
* \return Write status, ERR_NONE for successful write.
*/
int32_t _rww_flash_write(struct _flash_device *const device, const uint32_t dst_addr, uint8_t *buffer, uint32_t length);
#ifdef __cplusplus
}
#endif
/**@}*/
#endif /* _HPL_FLASH_H_INCLUDED */

123
hal/include/hpl_user_area.h Normal file
View File

@ -0,0 +1,123 @@
/**
* \file
*
* \brief Special user data area access
*
* Copyright (c) 2016-2018 Microchip Technology Inc. and its subsidiaries.
*
* \asf_license_start
*
* \page License
*
* Subject to your compliance with these terms, you may use Microchip
* software and any derivatives exclusively with Microchip products.
* It is your responsibility to comply with third party license terms applicable
* to your use of third party software (including open source software) that
* may accompany Microchip software.
*
* THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES,
* WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE,
* INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY,
* AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE
* LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL
* LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE
* SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE
* POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT
* ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY
* RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
* THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
*
* \asf_license_stop
*
*/
#ifndef _HPL_USER_DATA_H_INCLUDED
#define _HPL_USER_DATA_H_INCLUDED
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief Read data from user data area
*
* The user data area could be the area that stores user data that is not erased
* with the flash contents, e.g.,
* - NVM Software Calibration Area of SAM D/L/C family
* - User Signature of SAM E/S/V 70
*
* \param[in] base The base address of the user area
* \param[in] offset The byte offset of the data to be read inside the area
* \param[out] buf Pointer to buffer to place the read data
* \param[in] size Size of data in number of bytes
*
* \return Operation status or bytes read.
* \retval ERR_NONE Data read successfully
* \retval ERR_UNSUPPORTED_OP base address not in any supported user area
* \retval ERR_BAD_ADDRESS offset not in right area
* \retval ERR_INVALID_ARG offset and size exceeds the right area
*/
int32_t _user_area_read(const void *base, const uint32_t offset, uint8_t *buf, const uint32_t size);
/**
* \brief Read no more than 32 bits data from user data area
*
* When reading bits, the bitfield can cross 32-bis boundaries.
*
* \param[in] base The base address of the user area
* \param[in] bit_offset Offset in number of bits
* \param[in] n_bits Number of bits to read
* \return data read, assert if anything wrong (address not in user area
* offset, size error, etc.).
*/
uint32_t _user_area_read_bits(const void *base, const uint32_t bit_offset, const uint8_t n_bits);
/**
* \brief Write data to user data area
*
* The user data area could be the area that stores user data that is not erased
* with the flash contents, e.g.,
* - NVM Software Calibration Area of SAM D/L/C family
* - User Signature of SAM E/S/V 70
*
* When assigned offset and size exceeds the data area, error is reported.
*
* \param[out] base The base address of the user area
* \param[in] offset The offset of the data to be written inside the area
* \param[in] buf Pointer to buffer to place the written data
* \param[in] size Size of data in number of bytes
*
* \return Operation status or bytes writting.
* \retval ERR_NONE Data written successfully
* \retval ERR_UNSUPPORTED_OP base address not in any supported user area
* \retval ERR_DENIED Security bit is set
* \retval ERR_BAD_ADDRESS offset not in right area
* \retval ERR_INVALID_ARG offset and size exceeds the right area
*/
int32_t _user_area_write(void *base, const uint32_t offset, const uint8_t *buf, const uint32_t size);
/**
* \brief Write no more than 32 bits data to user data area
*
* When writting bits, the bitfield can cross 32-bis boundaries.
*
* \param[out] base The base address of the user area
* \param[in] bit_offset Offset in number of bits
* \param[in] bits The data content
* \param[in] n_bits Number of bits to write
* \return Operation result
* \retval ERR_NONE Data written successfully
* \retval ERR_UNSUPPORTED_OP base address not in any supported user area
* \retval ERR_DENIED Security bit is set
* \retval ERR_BAD_ADDRESS offset not in right area
* \retval ERR_INVALID_ARG offset and size exceeds the right area
*/
int32_t _user_area_write_bits(void *base, const uint32_t bit_offset, const uint32_t bits, const uint8_t n_bits);
#ifdef __cplusplus
}
#endif
#endif /* _HPL_USER_DATA_H_INCLUDED */

314
hal/src/hal_flash.c Normal file
View File

@ -0,0 +1,314 @@
/**
* \file
*
* \brief Flash functionality implementation.
*
* Copyright (c) 2015-2018 Microchip Technology Inc. and its subsidiaries.
*
* \asf_license_start
*
* \page License
*
* Subject to your compliance with these terms, you may use Microchip
* software and any derivatives exclusively with Microchip products.
* It is your responsibility to comply with third party license terms applicable
* to your use of third party software (including open source software) that
* may accompany Microchip software.
*
* THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES,
* WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE,
* INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY,
* AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE
* LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL
* LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE
* SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE
* POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT
* ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY
* RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
* THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
*
* \asf_license_stop
*
*/
#include "hal_flash.h"
#include <utils_assert.h>
#include <utils.h>
#include <hal_atomic.h>
/**
* \brief Driver version
*/
#define DRIVER_VERSION 0x00000001u
static void flash_ready(struct _flash_device *device);
static void flash_error(struct _flash_device *device);
static int32_t flash_is_address_aligned(struct flash_descriptor *flash, const uint32_t flash_addr);
/**
* \brief Initialize the FLASH HAL instance and hardware for callback mode.
*/
int32_t flash_init(struct flash_descriptor *flash, void *const hw)
{
int32_t rc;
ASSERT(flash && hw);
rc = _flash_init(&flash->dev, hw);
if (rc) {
return rc;
}
flash->dev.flash_cb.ready_cb = flash_ready;
flash->dev.flash_cb.error_cb = flash_error;
return ERR_NONE;
}
/**
* \brief Deinitialize the FLASH HAL instance.
*/
int32_t flash_deinit(struct flash_descriptor *flash)
{
ASSERT(flash);
_flash_deinit(&flash->dev);
return ERR_NONE;
}
/**
* \brief Reads a number of bytes to a page in the internal Flash
*/
int32_t flash_read(struct flash_descriptor *flash, uint32_t src_addr, uint8_t *buffer, uint32_t length)
{
ASSERT(flash && buffer && length);
uint32_t page_size = _flash_get_page_size(&flash->dev);
uint32_t total_pages = _flash_get_total_pages(&flash->dev);
/* Check if the address is valid */
if ((src_addr > page_size * total_pages) || (src_addr + length > page_size * total_pages)) {
return ERR_BAD_ADDRESS;
}
_flash_read(&flash->dev, src_addr, buffer, length);
return ERR_NONE;
}
/**
* \brief Updates several bytes to the internal Flash
*/
int32_t flash_write(struct flash_descriptor *flash, uint32_t dst_addr, uint8_t *buffer, uint32_t length)
{
ASSERT(flash && buffer && length);
uint32_t page_size = _flash_get_page_size(&flash->dev);
uint32_t total_pages = _flash_get_total_pages(&flash->dev);
/* Check if the address is valid */
if ((dst_addr > page_size * total_pages) || (dst_addr + length > page_size * total_pages)) {
return ERR_BAD_ADDRESS;
}
if (_flash_is_locked(&flash->dev, dst_addr)) {
return ERR_DENIED;
}
_flash_write(&flash->dev, dst_addr, buffer, length);
return ERR_NONE;
}
/**
* \brief Appends a number of bytes to a page in the internal Flash
*/
int32_t flash_append(struct flash_descriptor *flash, uint32_t dst_addr, uint8_t *buffer, uint32_t length)
{
ASSERT(flash && buffer && length);
uint32_t page_size = _flash_get_page_size(&flash->dev);
uint32_t total_pages = _flash_get_total_pages(&flash->dev);
/* Check if the address is valid */
if ((dst_addr > page_size * total_pages) || (dst_addr + length > page_size * total_pages)) {
return ERR_BAD_ADDRESS;
}
if (_flash_is_locked(&flash->dev, dst_addr)) {
return ERR_DENIED;
}
_flash_append(&flash->dev, dst_addr, buffer, length);
return ERR_NONE;
}
/**
* \brief Execute erase in the internal flash
*/
int32_t flash_erase(struct flash_descriptor *flash, const uint32_t dst_addr, const uint32_t page_nums)
{
ASSERT(flash && page_nums);
uint32_t page_size = _flash_get_page_size(&flash->dev);
uint32_t total_pages = _flash_get_total_pages(&flash->dev);
int32_t rc;
rc = flash_is_address_aligned(flash, dst_addr);
if (rc) {
return rc;
}
if ((page_nums > total_pages) || (dst_addr / page_size + page_nums > total_pages)) {
return ERR_INVALID_ARG;
}
_flash_erase(&flash->dev, dst_addr, page_nums);
return ERR_NONE;
}
/**
* \brief Register a function as FLASH transfer completion callback
*/
int32_t flash_register_callback(struct flash_descriptor *flash, const enum flash_cb_type type, flash_cb_t func)
{
ASSERT(flash);
switch (type) {
case FLASH_CB_READY:
flash->callbacks.cb_ready = func;
break;
case FLASH_CB_ERROR:
flash->callbacks.cb_error = func;
break;
default:
return ERR_INVALID_ARG;
}
_flash_set_irq_state(&flash->dev, (enum _flash_cb_type)type, NULL != func);
return ERR_NONE;
}
/**
* \brief Execute lock in the internal flash
*/
int32_t flash_lock(struct flash_descriptor *flash, const uint32_t dst_addr, const uint32_t page_nums)
{
ASSERT(flash && page_nums);
uint32_t page_size = _flash_get_page_size(&flash->dev);
uint32_t total_pages = _flash_get_total_pages(&flash->dev);
int32_t rc;
rc = flash_is_address_aligned(flash, dst_addr);
if (rc) {
return rc;
}
if ((page_nums > total_pages) || (dst_addr / page_size + page_nums > total_pages)) {
return ERR_INVALID_ARG;
}
return _flash_lock(&flash->dev, dst_addr, page_nums);
}
/**
* \brief Execute unlock in the internal flash
*/
int32_t flash_unlock(struct flash_descriptor *flash, const uint32_t dst_addr, const uint32_t page_nums)
{
ASSERT(flash && page_nums);
uint32_t page_size = _flash_get_page_size(&flash->dev);
uint32_t total_pages = _flash_get_total_pages(&flash->dev);
int32_t rc;
rc = flash_is_address_aligned(flash, dst_addr);
if (rc) {
return rc;
}
if ((page_nums > total_pages) || (dst_addr / page_size + page_nums > total_pages)) {
return ERR_INVALID_ARG;
}
return _flash_unlock(&flash->dev, dst_addr, page_nums);
}
/**
* \brief Get the flash page size.
*/
uint32_t flash_get_page_size(struct flash_descriptor *flash)
{
ASSERT(flash);
return _flash_get_page_size(&flash->dev);
}
/**
* \brief Get the numbers of flash page.
*/
uint32_t flash_get_total_pages(struct flash_descriptor *flash)
{
ASSERT(flash);
return _flash_get_total_pages(&flash->dev);
}
/**
* \brief Retrieve the current driver version
*/
uint32_t flash_get_version(void)
{
return DRIVER_VERSION;
}
/**
* \internal check the address whether it is aligned
* \param[in, out] flash Pointer to the HAL FLASH instance.
* \param[in] flash_addr address to be check in flash
*
* \return whether it is valid
* \retval 0 Valid.
* \retval -1 Error, invalid.
*/
static int32_t flash_is_address_aligned(struct flash_descriptor *flash, const uint32_t flash_addr)
{
ASSERT(flash);
uint32_t page_size = _flash_get_page_size(&flash->dev);
/* Check if the read address not aligned to the start of a page */
if (flash_addr & (page_size - 1)) {
return ERR_BAD_ADDRESS;
}
return ERR_NONE;
}
/**
* \internal Ready for a new flash command
*
* \param[in] device The pointer to flash device structure
*/
static void flash_ready(struct _flash_device *device)
{
struct flash_descriptor *const descr = CONTAINER_OF(device, struct flash_descriptor, dev);
if (descr->callbacks.cb_ready) {
descr->callbacks.cb_ready(descr);
}
}
/**
* \internal Error occurs in flash command
*
* \param[in] device The pointer to flash device structure
*/
static void flash_error(struct _flash_device *device)
{
struct flash_descriptor *const descr = CONTAINER_OF(device, struct flash_descriptor, dev);
if (descr->callbacks.cb_error) {
descr->callbacks.cb_error(descr);
}
}

695
hpl/nvmctrl/hpl_nvmctrl.c Normal file
View File

@ -0,0 +1,695 @@
/**
* \file
*
* \brief Non-Volatile Memory Controller
*
* Copyright (c) 2016-2018 Microchip Technology Inc. and its subsidiaries.
*
* \asf_license_start
*
* \page License
*
* Subject to your compliance with these terms, you may use Microchip
* software and any derivatives exclusively with Microchip products.
* It is your responsibility to comply with third party license terms applicable
* to your use of third party software (including open source software) that
* may accompany Microchip software.
*
* THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES,
* WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE,
* INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY,
* AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE
* LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL
* LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE
* SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE
* POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT
* ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY
* RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
* THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
*
* \asf_license_stop
*
*/
#include <hpl_flash.h>
#include <hpl_user_area.h>
#include <string.h>
#include <utils_assert.h>
#include <utils.h>
#include <hpl_nvmctrl_config.h>
#define NVM_MEMORY ((volatile uint32_t *)FLASH_ADDR)
#define NVMCTRL_BLOCK_PAGES (NVMCTRL_BLOCK_SIZE / NVMCTRL_PAGE_SIZE)
#define NVMCTRL_REGIONS_NUM 32
#define NVMCTRL_INTFLAG_ERR \
(NVMCTRL_INTFLAG_ADDRE | NVMCTRL_INTFLAG_PROGE | NVMCTRL_INTFLAG_LOCKE | NVMCTRL_INTFLAG_ECCSE \
| NVMCTRL_INTFLAG_NVME | NVMCTRL_INTFLAG_SEESOVF)
/**
* \brief NVM configuration type
*/
struct nvm_configuration {
hri_nvmctrl_ctrlb_reg_t ctrla; /*!< Control B Register */
};
/**
* \brief Array of NVM configurations
*/
static struct nvm_configuration _nvm
= {(CONF_NVM_CACHE0 << NVMCTRL_CTRLA_CACHEDIS0_Pos) | (CONF_NVM_CACHE1 << NVMCTRL_CTRLA_CACHEDIS1_Pos)
| (NVMCTRL_CTRLA_PRM(CONF_NVM_SLEEPPRM))};
/*!< Pointer to hpl device */
static struct _flash_device *_nvm_dev = NULL;
static void _flash_erase_block(void *const hw, const uint32_t dst_addr);
static void _flash_program(void *const hw, const uint32_t dst_addr, const uint8_t *buffer, const uint16_t size);
/**
* \brief Initialize NVM
*/
int32_t _flash_init(struct _flash_device *const device, void *const hw)
{
uint32_t ctrla;
ASSERT(device && (hw == NVMCTRL));
device->hw = hw;
ctrla = hri_nvmctrl_read_CTRLA_reg(hw);
ctrla &= ~(NVMCTRL_CTRLA_CACHEDIS0 | NVMCTRL_CTRLA_CACHEDIS1 | NVMCTRL_CTRLA_PRM_Msk);
ctrla |= _nvm.ctrla;
hri_nvmctrl_write_CTRLA_reg(hw, ctrla);
_nvm_dev = device;
NVIC_DisableIRQ(NVMCTRL_0_IRQn);
NVIC_DisableIRQ(NVMCTRL_1_IRQn);
NVIC_ClearPendingIRQ(NVMCTRL_0_IRQn);
NVIC_ClearPendingIRQ(NVMCTRL_1_IRQn);
NVIC_EnableIRQ(NVMCTRL_0_IRQn);
NVIC_EnableIRQ(NVMCTRL_1_IRQn);
return ERR_NONE;
}
/**
* \brief De-initialize NVM
*/
void _flash_deinit(struct _flash_device *const device)
{
device->hw = NULL;
NVIC_DisableIRQ(NVMCTRL_0_IRQn);
NVIC_DisableIRQ(NVMCTRL_1_IRQn);
}
/**
* \brief Get the flash page size.
*/
uint32_t _flash_get_page_size(struct _flash_device *const device)
{
(void)device;
return (uint32_t)NVMCTRL_PAGE_SIZE;
}
/**
* \brief Get the numbers of flash page.
*/
uint32_t _flash_get_total_pages(struct _flash_device *const device)
{
(void)device;
return (uint32_t)hri_nvmctrl_read_PARAM_NVMP_bf(device->hw);
}
/**
* \brief Get the number of wait states for read and write operations.
*/
uint8_t _flash_get_wait_state(struct _flash_device *const device)
{
return hri_nvmctrl_get_CTRLA_reg(device->hw, NVMCTRL_CTRLA_RWS_Msk);
}
/**
* \brief Set the number of wait states for read and write operations.
*/
void _flash_set_wait_state(struct _flash_device *const device, uint8_t state)
{
hri_nvmctrl_write_CTRLA_RWS_bf(device->hw, state);
}
/**
* \brief Reads a number of bytes to a page in the internal Flash.
*/
void _flash_read(struct _flash_device *const device, const uint32_t src_addr, uint8_t *buffer, uint32_t length)
{
uint8_t *nvm_addr = (uint8_t *)NVM_MEMORY;
uint32_t i;
/* Check if the module is busy */
while (!hri_nvmctrl_get_STATUS_READY_bit(device->hw)) {
/* Wait until this module isn't busy */
}
for (i = 0; i < length; i++) {
buffer[i] = nvm_addr[src_addr + i];
}
}
/**
* \brief Writes a number of bytes to a page in the internal Flash.
*/
void _flash_write(struct _flash_device *const device, const uint32_t dst_addr, uint8_t *buffer, uint32_t length)
{
uint8_t tmp_buffer[NVMCTRL_BLOCK_PAGES][NVMCTRL_PAGE_SIZE];
uint32_t block_start_addr, block_end_addr;
uint32_t i, j, k;
uint32_t wr_start_addr = dst_addr;
do {
block_start_addr = wr_start_addr & ~(NVMCTRL_BLOCK_SIZE - 1);
block_end_addr = block_start_addr + NVMCTRL_BLOCK_SIZE - 1;
/* store the erase data into temp buffer before write */
for (i = 0; i < NVMCTRL_BLOCK_PAGES; i++) {
_flash_read(device, block_start_addr + i * NVMCTRL_PAGE_SIZE, tmp_buffer[i], NVMCTRL_PAGE_SIZE);
}
/* temp buffer update */
j = (wr_start_addr - block_start_addr) / NVMCTRL_PAGE_SIZE;
k = wr_start_addr - block_start_addr - j * NVMCTRL_PAGE_SIZE;
while ((wr_start_addr <= block_end_addr) && (length > 0)) {
tmp_buffer[j][k] = *buffer;
k = (k + 1) % NVMCTRL_PAGE_SIZE;
if (0 == k) {
j++;
}
wr_start_addr++;
buffer++;
length--;
}
/* erase row before write */
_flash_erase_block(device->hw, block_start_addr);
/* write buffer to flash */
for (i = 0; i < NVMCTRL_BLOCK_PAGES; i++) {
_flash_program(device->hw, block_start_addr + i * NVMCTRL_PAGE_SIZE, tmp_buffer[i], NVMCTRL_PAGE_SIZE);
}
} while (block_end_addr < (wr_start_addr + length - 1));
}
/**
* \brief Appends a number of bytes in the internal Flash.
*/
void _flash_append(struct _flash_device *const device, const uint32_t dst_addr, uint8_t *buffer, uint32_t length)
{
uint32_t page_start_addr = dst_addr & ~(NVMCTRL_PAGE_SIZE - 1);
uint32_t size;
uint32_t offset = 0;
if (dst_addr != page_start_addr) {
/* Need to write some data to the end of a page */
size = min(length, NVMCTRL_PAGE_SIZE - (dst_addr - page_start_addr));
_flash_program(device->hw, dst_addr, buffer, size);
page_start_addr += NVMCTRL_PAGE_SIZE;
offset += size;
}
while (offset < length) {
size = min(length - offset, NVMCTRL_PAGE_SIZE);
_flash_program(device->hw, page_start_addr, buffer + offset, size);
page_start_addr += NVMCTRL_PAGE_SIZE;
offset += size;
}
}
/**
* \brief Execute erase in the internal flash
*/
void _flash_erase(struct _flash_device *const device, uint32_t dst_addr, uint32_t page_nums)
{
uint8_t tmp_buffer[NVMCTRL_PAGE_SIZE];
uint32_t block_start_addr;
uint32_t i;
block_start_addr = dst_addr & ~(NVMCTRL_BLOCK_SIZE - 1);
memset(tmp_buffer, 0xFF, NVMCTRL_PAGE_SIZE);
/* when address is not aligned with block start address */
if (dst_addr != block_start_addr) {
block_start_addr += NVMCTRL_BLOCK_SIZE;
for (i = 0; i < NVMCTRL_BLOCK_PAGES - 1; i++) {
_flash_write(device, dst_addr, tmp_buffer, NVMCTRL_PAGE_SIZE);
if (--page_nums == 0) {
return;
}
dst_addr += NVMCTRL_PAGE_SIZE;
if (dst_addr == block_start_addr) {
break;
}
}
}
while (page_nums >= NVMCTRL_BLOCK_PAGES) {
_flash_erase_block(device->hw, block_start_addr);
block_start_addr += NVMCTRL_BLOCK_SIZE;
page_nums -= NVMCTRL_BLOCK_PAGES;
}
if (page_nums != 0) {
for (i = 0; i < page_nums; i++) {
_flash_write(device, block_start_addr, tmp_buffer, NVMCTRL_PAGE_SIZE);
block_start_addr += NVMCTRL_PAGE_SIZE;
}
}
}
/**
* \brief Execute lock in the internal flash
*/
int32_t _flash_lock(struct _flash_device *const device, const uint32_t dst_addr, uint32_t page_nums)
{
uint32_t region_pages;
uint32_t block_start_addr;
region_pages = (uint32_t)FLASH_SIZE / (NVMCTRL_REGIONS_NUM * NVMCTRL_PAGE_SIZE);
block_start_addr = dst_addr & ~(NVMCTRL_BLOCK_SIZE - 1);
if ((page_nums != region_pages) || (dst_addr != block_start_addr)) {
return ERR_INVALID_ARG;
}
while (!hri_nvmctrl_get_STATUS_READY_bit(device->hw)) {
/* Wait until this module isn't busy */
}
hri_nvmctrl_write_ADDR_reg(device->hw, dst_addr);
hri_nvmctrl_write_CTRLB_reg(device->hw, NVMCTRL_CTRLB_CMD_LR | NVMCTRL_CTRLB_CMDEX_KEY);
return (int32_t)FLASH_SIZE / (NVMCTRL_REGIONS_NUM * NVMCTRL_PAGE_SIZE);
}
/**
* \brief Execute unlock in the internal flash
*/
int32_t _flash_unlock(struct _flash_device *const device, const uint32_t dst_addr, uint32_t page_nums)
{
uint32_t region_pages;
uint32_t block_start_addr;
region_pages = (uint32_t)FLASH_SIZE / (NVMCTRL_REGIONS_NUM * NVMCTRL_PAGE_SIZE);
block_start_addr = dst_addr & ~(NVMCTRL_BLOCK_SIZE - 1);
if ((page_nums != region_pages) || (dst_addr != block_start_addr)) {
return ERR_INVALID_ARG;
}
while (!hri_nvmctrl_get_STATUS_READY_bit(device->hw)) {
/* Wait until this module isn't busy */
}
hri_nvmctrl_write_ADDR_reg(device->hw, dst_addr);
hri_nvmctrl_write_CTRLB_reg(device->hw, NVMCTRL_CTRLB_CMD_UR | NVMCTRL_CTRLB_CMDEX_KEY);
return (int32_t)FLASH_SIZE / (NVMCTRL_REGIONS_NUM * NVMCTRL_PAGE_SIZE);
}
/**
* \brief check whether the region which is pointed by address
*/
bool _flash_is_locked(struct _flash_device *const device, const uint32_t dst_addr)
{
uint16_t region_id;
/* Get region for given page */
region_id = dst_addr / (FLASH_SIZE / NVMCTRL_REGIONS_NUM);
return !(hri_nvmctrl_get_RUNLOCK_reg(device->hw, 1 << region_id));
}
/**
* \brief Enable/disable Flash interrupt
*/
void _flash_set_irq_state(struct _flash_device *const device, const enum _flash_cb_type type, const bool state)
{
ASSERT(device);
if (FLASH_DEVICE_CB_READY == type) {
hri_nvmctrl_write_INTEN_DONE_bit(device->hw, state);
} else if (FLASH_DEVICE_CB_ERROR == type) {
if (state) {
hri_nvmctrl_write_INTEN_reg(device->hw, NVMCTRL_INTFLAG_ERR);
} else {
hri_nvmctrl_clear_INTEN_reg(device->hw, NVMCTRL_INTFLAG_ERR);
}
}
}
/**
* \internal erase a row in flash
* \param[in] hw The pointer to hardware instance
* \param[in] dst_addr Destination page address to erase
*/
static void _flash_erase_block(void *const hw, const uint32_t dst_addr)
{
while (!hri_nvmctrl_get_STATUS_READY_bit(hw)) {
/* Wait until this module isn't busy */
}
/* Set address and command */
hri_nvmctrl_write_ADDR_reg(hw, dst_addr);
hri_nvmctrl_write_CTRLB_reg(hw, NVMCTRL_CTRLB_CMD_EB | NVMCTRL_CTRLB_CMDEX_KEY);
}
/**
* \internal write a page in flash
* \param[in] hw The pointer to hardware instance
* \param[in] dst_addr Destination page address to write
* \param[in] buffer Pointer to buffer where the data to
* write is stored
* \param[in] size The size of data to write to a page
*/
static void _flash_program(void *const hw, const uint32_t dst_addr, const uint8_t *buffer, const uint16_t size)
{
uint32_t *ptr_read = (uint32_t *)buffer;
uint32_t nvm_address = dst_addr / 4;
uint16_t i;
while (!hri_nvmctrl_get_STATUS_READY_bit(hw)) {
/* Wait until this module isn't busy */
}
hri_nvmctrl_write_CTRLB_reg(hw, NVMCTRL_CTRLB_CMD_PBC | NVMCTRL_CTRLB_CMDEX_KEY);
while (!hri_nvmctrl_get_STATUS_READY_bit(hw)) {
/* Wait until this module isn't busy */
}
/* Writes to the page buffer must be 32 bits, perform manual copy
* to ensure alignment */
for (i = 0; i < size; i += 4) {
NVM_MEMORY[nvm_address++] = *ptr_read;
ptr_read++;
}
while (!hri_nvmctrl_get_STATUS_READY_bit(hw)) {
/* Wait until this module isn't busy */
}
hri_nvmctrl_write_ADDR_reg(hw, dst_addr);
hri_nvmctrl_write_CTRLB_reg(hw, NVMCTRL_CTRLB_CMD_WP | NVMCTRL_CTRLB_CMDEX_KEY);
}
/**
* \internal NVM interrupt handler
*
* \param[in] p The pointer to interrupt parameter
*/
static void _nvm_interrupt_handler(struct _flash_device *device)
{
void *const hw = device->hw;
if (hri_nvmctrl_get_INTFLAG_DONE_bit(hw)) {
hri_nvmctrl_clear_INTFLAG_DONE_bit(hw);
if (NULL != device->flash_cb.ready_cb) {
device->flash_cb.ready_cb(device);
}
} else if (hri_nvmctrl_read_INTFLAG_reg(hw) && ~NVMCTRL_INTFLAG_ERR) {
hri_nvmctrl_clear_INTFLAG_reg(hw, NVMCTRL_INTFLAG_ERR);
if (NULL != device->flash_cb.error_cb) {
device->flash_cb.error_cb(device);
}
}
}
/**
* \internal NVM 0 interrupt handler
*/
void NVMCTRL_0_Handler(void)
{
_nvm_interrupt_handler(_nvm_dev);
}
/**
* \internal NVM 1 interrupt handler
*/
void NVMCTRL_1_Handler(void)
{
_nvm_interrupt_handler(_nvm_dev);
}
/*
The NVM User Row contains calibration data that are automatically read at device
power on.
The NVM User Row can be read at address 0x804000.
The first eight 32-bit words (32 Bytes) of the Non Volatile Memory (NVM) User
Page contain calibration data that are automatically read at device power-on.
The remaining 480 Bytes can be used for storing custom parameters.
*/
#ifndef _NVM_USER_ROW_BASE
#define _NVM_USER_ROW_BASE 0x804000
#endif
#define _NVM_USER_ROW_N_BITS 4096
#define _NVM_USER_ROW_N_BYTES (_NVM_USER_ROW_N_BITS / 8)
#define _NVM_USER_ROW_END (((uint8_t *)_NVM_USER_ROW_BASE) + _NVM_USER_ROW_N_BYTES - 1)
#define _IS_NVM_USER_ROW(b) \
(((uint8_t *)(b) >= (uint8_t *)(_NVM_USER_ROW_BASE)) && ((uint8_t *)(b) <= (uint8_t *)(_NVM_USER_ROW_END)))
#define _IN_NVM_USER_ROW(b, o) (((uint8_t *)(b) + (o)) <= (uint8_t *)(_NVM_USER_ROW_END))
/*
The NVM Software Calibration Area can be read at address 0x00800080.
The NVM Software Calibration Area can not be written.
*/
#ifndef _NVM_SW_CALIB_AREA_BASE
#define _NVM_SW_CALIB_AREA_BASE 0x00800080
#endif
#define _NVM_SW_CALIB_AREA_N_BITS 45
#define _NVM_SW_CALIB_AREA_N_BYTES (_NVM_SW_CALIB_AREA_N_BITS / 8)
#define _NVM_SW_CALIB_AREA_END (((uint8_t *)_NVM_SW_CALIB_AREA_BASE) + _NVM_SW_CALIB_AREA_N_BYTES - 1)
#define _IS_NVM_SW_CALIB_AREA(b) \
(((uint8_t *)(b) >= (uint8_t *)_NVM_SW_CALIB_AREA_BASE) && ((uint8_t *)(b) <= (uint8_t *)_NVM_SW_CALIB_AREA_END))
#define _IN_NVM_SW_CALIB_AREA(b, o) (((uint8_t *)(b) + (o)) <= (uint8_t *)(_NVM_SW_CALIB_AREA_END))
/**
* \internal Read left aligned data bits
* \param[in] base Base address for the data
* \param[in] bit_offset Offset for the bitfield start
* \param[in] n_bits Number of bits in the bitfield
*/
static inline uint32_t _user_area_read_l32_bits(const volatile uint32_t *base, const uint32_t bit_offset,
const uint8_t n_bits)
{
return base[bit_offset >> 5] & ((1 << n_bits) - 1);
}
/**
* \internal Read right aligned data bits
* \param[in] base Base address for the data
* \param[in] bit_offset Offset for the bitfield start
* \param[in] n_bits Number of bits in the bitfield
*/
static inline uint32_t _user_area_read_r32_bits(const volatile uint32_t *base, const uint32_t bit_offset,
const uint8_t n_bits)
{
return (base[bit_offset >> 5] >> (bit_offset & 0x1F)) & ((1 << n_bits) - 1);
}
int32_t _user_area_read(const void *base, const uint32_t offset, uint8_t *buf, uint32_t size)
{
ASSERT(buf);
/** Parameter check. */
if (_IS_NVM_USER_ROW(base)) {
if (!_IN_NVM_USER_ROW(base, offset)) {
return ERR_BAD_ADDRESS;
}
/* Cut off if request too many bytes */
if (!_IN_NVM_USER_ROW(base, offset + size - 1)) {
return ERR_INVALID_ARG;
}
} else if (_IS_NVM_SW_CALIB_AREA(base)) {
if (!_IN_NVM_SW_CALIB_AREA(base, offset)) {
return ERR_BAD_ADDRESS;
}
/* Cut off if request too many bytes */
if (!_IN_NVM_SW_CALIB_AREA(base, offset + size - 1)) {
return ERR_INVALID_ARG;
}
} else {
return ERR_UNSUPPORTED_OP;
}
/* Copy data */
memcpy(buf, ((uint8_t *)base) + offset, size);
return ERR_NONE;
}
uint32_t _user_area_read_bits(const void *base, const uint32_t bit_offset, const uint8_t n_bits)
{
volatile uint32_t *mem_base = (volatile uint32_t *)base;
uint32_t l_off, l_bits;
uint32_t r_off, r_bits;
/** Parameter check. */
if (_IS_NVM_USER_ROW(base)) {
ASSERT(_IN_NVM_USER_ROW(base, bit_offset >> 3) && _IN_NVM_USER_ROW(base, (bit_offset + n_bits - 1) >> 3));
} else if (_IS_NVM_SW_CALIB_AREA(base)) {
ASSERT(_IN_NVM_SW_CALIB_AREA(base, bit_offset >> 3)
&& _IN_NVM_SW_CALIB_AREA(base, (bit_offset + n_bits - 1) >> 3));
} else {
ASSERT(false);
}
/* Since the bitfield can cross 32-bits boundaries,
* left and right bits are read from 32-bit aligned address
* and then combined together. */
l_off = bit_offset & (~(32 - 1));
r_off = l_off + 32;
l_bits = 32 - (bit_offset & (32 - 1));
if (n_bits > l_bits) {
r_bits = n_bits - l_bits;
} else {
l_bits = n_bits;
r_bits = 0;
}
return _user_area_read_r32_bits(mem_base, bit_offset, l_bits)
+ (_user_area_read_l32_bits(mem_base, r_off, r_bits) << l_bits);
}
/** \internal Write 4096-bit user row
* \param[in] _row Pointer to 4096-bit user row data.
*/
static int32_t _user_row_write_exec(const uint32_t *_row)
{
Nvmctrl *hw = NVMCTRL;
uint32_t ctrla = hri_nvmctrl_read_CTRLA_reg(NVMCTRL);
uint32_t i;
/* Denied if Security Bit is set */
if (DSU->STATUSB.bit.PROT) {
return ERR_DENIED;
}
/* Do Save */
/* - Prepare. */
while (!hri_nvmctrl_get_STATUS_READY_bit(hw)) {
/* Wait until this module isn't busy */
}
hri_nvmctrl_clear_CTRLA_WMODE_bf(NVMCTRL, NVMCTRL_CTRLA_WMODE_Msk);
/* - Erase AUX row. */
hri_nvmctrl_write_ADDR_reg(hw, (hri_nvmctrl_addr_reg_t)_NVM_USER_ROW_BASE);
hri_nvmctrl_write_CTRLB_reg(hw, NVMCTRL_CTRLB_CMD_EP | NVMCTRL_CTRLB_CMDEX_KEY);
while (!hri_nvmctrl_get_STATUS_READY_bit(hw)) {
/* Wait until this module isn't busy */
}
for (i = 0; i < 32; i++) { /* 32 Quad words for User row: 32 * (4 bytes * 4) = 512 bytes */
/* - Page buffer clear & write. */
hri_nvmctrl_write_CTRLB_reg(hw, NVMCTRL_CTRLB_CMD_PBC | NVMCTRL_CTRLB_CMDEX_KEY);
while (!hri_nvmctrl_get_STATUS_READY_bit(hw)) {
/* Wait until this module isn't busy */
}
*(((uint32_t *)NVMCTRL_USER) + i * 4) = _row[i * 4];
*(((uint32_t *)NVMCTRL_USER) + i * 4 + 1) = _row[i * 4 + 1];
*(((uint32_t *)NVMCTRL_USER) + i * 4 + 2) = _row[i * 4 + 2];
*(((uint32_t *)NVMCTRL_USER) + i * 4 + 3) = _row[i * 4 + 3];
/* - Write AUX row. */
hri_nvmctrl_write_ADDR_reg(hw, (hri_nvmctrl_addr_reg_t)(_NVM_USER_ROW_BASE + i * 16));
hri_nvmctrl_write_CTRLB_reg(hw, NVMCTRL_CTRLB_CMD_WQW | NVMCTRL_CTRLB_CMDEX_KEY);
while (!hri_nvmctrl_get_STATUS_READY_bit(hw)) {
/* Wait until this module isn't busy */
}
}
/* Restore CTRLA */
hri_nvmctrl_write_CTRLA_reg(NVMCTRL, ctrla);
return ERR_NONE;
}
int32_t _user_area_write(void *base, const uint32_t offset, const uint8_t *buf, const uint32_t size)
{
uint32_t _row[NVMCTRL_PAGE_SIZE / 4]; /* Copy of user row. */
/** Parameter check. */
if (_IS_NVM_USER_ROW(base)) {
if (!_IN_NVM_USER_ROW(base, offset)) {
return ERR_BAD_ADDRESS;
} else if (!_IN_NVM_USER_ROW(base, offset + size - 1)) {
return ERR_INVALID_ARG;
}
} else if (_IS_NVM_SW_CALIB_AREA(base)) {
return ERR_DENIED;
} else {
return ERR_UNSUPPORTED_OP;
}
memcpy(_row, base, NVMCTRL_PAGE_SIZE); /* Store previous data. */
memcpy((uint8_t *)_row + offset, buf, size); /* Modify with buf data. */
return _user_row_write_exec(_row);
}
int32_t _user_area_write_bits(void *base, const uint32_t bit_offset, const uint32_t bits, const uint8_t n_bits)
{
uint32_t _row[NVMCTRL_PAGE_SIZE / 4]; /* Copy of user row. */
uint32_t l_off, l_bits;
uint32_t r_off, r_bits;
/** Parameter check. */
if (_IS_NVM_USER_ROW(base)) {
if (!_IN_NVM_USER_ROW(base, bit_offset >> 3)) {
return ERR_BAD_ADDRESS;
} else if (!_IN_NVM_USER_ROW(base, (bit_offset + n_bits - 1) >> 3)) {
return ERR_INVALID_ARG;
}
} else if (_IS_NVM_SW_CALIB_AREA(base)) {
return ERR_DENIED;
} else {
return ERR_UNSUPPORTED_OP;
}
/* Since the bitfield can cross 32-bits boundaries,
* left and right bits are splitted for 32-bit aligned address
* and then saved. */
l_off = bit_offset & (~(32 - 1));
r_off = l_off + 32;
l_bits = 32 - (bit_offset & (32 - 1));
if (n_bits > l_bits) {
r_bits = n_bits - l_bits;
} else {
l_bits = n_bits;
r_bits = 0;
}
memcpy(_row, base, NVMCTRL_PAGE_SIZE); /* Store previous data. */
if (l_bits) {
uint32_t l_mask = ((1 << l_bits) - 1) << (bit_offset & (32 - 1));
_row[bit_offset >> 5] &= ~l_mask;
_row[bit_offset >> 5] |= (bits << (bit_offset & (32 - 1))) & l_mask;
}
if (r_bits) {
uint32_t r_mask = (1 << r_bits) - 1;
_row[r_off >> 5] &= ~r_mask;
_row[r_off >> 5] |= bits >> l_bits;
}
return _user_row_write_exec(_row);
}