Change in osmo-asf4-dfu[master]: add flash ASF4 driver

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

Harald Welte gerrit-no-reply at lists.osmocom.org
Thu Jan 17 12:08:16 UTC 2019


Harald Welte has submitted this change and it was merged. ( https://gerrit.osmocom.org/12501 )

Change subject: add flash ASF4 driver
......................................................................

add flash ASF4 driver

this is imported from the Atmel Start website

Change-Id: I5eccb37da64c7def7b99418773d09b6d98664432
---
A config/hpl_nvmctrl_config.h
M gcc/Makefile
A hal/documentation/flash.rst
A hal/include/hal_flash.h
A hal/include/hpl_flash.h
A hal/include/hpl_user_area.h
A hal/src/hal_flash.c
A hpl/nvmctrl/hpl_nvmctrl.c
8 files changed, 1,705 insertions(+), 3 deletions(-)

Approvals:
  Jenkins Builder: Verified
  Harald Welte: Looks good to me, approved



diff --git a/config/hpl_nvmctrl_config.h b/config/hpl_nvmctrl_config.h
new file mode 100644
index 0000000..daaba85
--- /dev/null
+++ b/config/hpl_nvmctrl_config.h
@@ -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
diff --git a/gcc/Makefile b/gcc/Makefile
index 231ed39..f7c5b65 100644
--- a/gcc/Makefile
+++ b/gcc/Makefile
@@ -41,6 +41,7 @@
 usb/device \
 hpl/oscctrl \
 gcc/gcc \
+hpl/nvmctrl \
 hpl/usb \
 hpl/core \
 hpl/cmcc
@@ -52,6 +53,7 @@
 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/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/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 @@
 "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 @@
 "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 @@
 "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 @@
 	@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 @@
 	@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 @@
 	@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: $<
 
diff --git a/hal/documentation/flash.rst b/hal/documentation/flash.rst
new file mode 100644
index 0000000..fcc86e6
--- /dev/null
+++ b/hal/documentation/flash.rst
@@ -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
diff --git a/hal/include/hal_flash.h b/hal/include/hal_flash.h
new file mode 100644
index 0000000..8216afd
--- /dev/null
+++ b/hal/include/hal_flash.h
@@ -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 */
diff --git a/hal/include/hpl_flash.h b/hal/include/hpl_flash.h
new file mode 100644
index 0000000..3a42c44
--- /dev/null
+++ b/hal/include/hpl_flash.h
@@ -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 */
diff --git a/hal/include/hpl_user_area.h b/hal/include/hpl_user_area.h
new file mode 100644
index 0000000..1f14098
--- /dev/null
+++ b/hal/include/hpl_user_area.h
@@ -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 */
diff --git a/hal/src/hal_flash.c b/hal/src/hal_flash.c
new file mode 100644
index 0000000..b1a58b7
--- /dev/null
+++ b/hal/src/hal_flash.c
@@ -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);
+	}
+}
diff --git a/hpl/nvmctrl/hpl_nvmctrl.c b/hpl/nvmctrl/hpl_nvmctrl.c
new file mode 100644
index 0000000..63f6145
--- /dev/null
+++ b/hpl/nvmctrl/hpl_nvmctrl.c
@@ -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);
+}

-- 
To view, visit https://gerrit.osmocom.org/12501
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-asf4-dfu
Gerrit-Branch: master
Gerrit-MessageType: merged
Gerrit-Change-Id: I5eccb37da64c7def7b99418773d09b6d98664432
Gerrit-Change-Number: 12501
Gerrit-PatchSet: 3
Gerrit-Owner: Kévin Redon <kredon at sysmocom.de>
Gerrit-Reviewer: Harald Welte <laforge at gnumonks.org>
Gerrit-Reviewer: Jenkins Builder (1000002)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20190117/be4073c5/attachment.htm>


More information about the gerrit-log mailing list