Platform: Add new CMSIS Flash driver for Musca

- This patch replaces the former emulated and temporary CMSIS flash
driver (for Musca-A) with a new one which is based on the qspi_ip6514e
flash controller driver and library of the MT25QL flash memory device.
- Integrates the flash driver with MCUBoot and SST service
- The usage of the driver is limited when running from flash.
The erase functions and the flash command (STIG) based implementation
of the ARM_Flash_ProgramData() cannot be used and the initialization
has to be modified to use the default parameters instead of the
optimal.

Change-Id: Ida01cd327d197f0f1bd1adf913ea19aae9662ff3
Signed-off-by: David Vincze <david.vincze@arm.com>
diff --git a/bl2/ext/mcuboot/bl2_main.c b/bl2/ext/mcuboot/bl2_main.c
index c556816..52dece2 100644
--- a/bl2/ext/mcuboot/bl2_main.c
+++ b/bl2/ext/mcuboot/bl2_main.c
@@ -101,6 +101,7 @@
 
     /* Initialize Flash driver */
     FLASH_DEV_NAME.Initialize(NULL);
+
     rc = boot_go(&rsp);
     if (rc != 0) {
         BOOT_LOG_ERR("Unable to find bootable image");
diff --git a/bl2/ext/mcuboot/bootutil/src/loader.c b/bl2/ext/mcuboot/bootutil/src/loader.c
index 154cd7e..3a187be 100644
--- a/bl2/ext/mcuboot/bootutil/src/loader.c
+++ b/bl2/ext/mcuboot/bootutil/src/loader.c
@@ -280,7 +280,11 @@
 
     if ((hdr->ih_magic != IMAGE_MAGIC || boot_image_check(hdr, fap) != 0)) {
         if (slot != 0) {
-            flash_area_erase(fap, 0, fap->fa_size);
+            rc = flash_area_erase(fap, 0, fap->fa_size);
+            if(rc != 0) {
+                flash_area_close(fap);
+                return BOOT_EFLASH;
+            }
             /* Image in slot 1 is invalid. Erase the image and
              * continue booting from slot 0.
              */
diff --git a/platform/ext/musca_a.cmake b/platform/ext/musca_a.cmake
index 9a17143..8c5b9ea 100755
--- a/platform/ext/musca_a.cmake
+++ b/platform/ext/musca_a.cmake
@@ -47,6 +47,7 @@
 embedded_include_directories(PATH "${PLATFORM_DIR}/target/musca_a/Device/Include" ABSOLUTE)
 embedded_include_directories(PATH "${PLATFORM_DIR}/target/musca_a/Native_Driver" ABSOLUTE)
 embedded_include_directories(PATH "${PLATFORM_DIR}/target/musca_a/partition" ABSOLUTE)
+embedded_include_directories(PATH "${PLATFORM_DIR}/target/musca_a/Libraries" ABSOLUTE)
 
 #Gather all source files we need.
 if (NOT DEFINED BUILD_CMSIS_CORE)
@@ -84,6 +85,8 @@
 
   list(APPEND ALL_SRC_C_S "${PLATFORM_DIR}/target/musca_a/Native_Driver/mpc_sie200_drv.c"
               "${PLATFORM_DIR}/target/musca_a/Native_Driver/ppc_sse200_drv.c")
+  list(APPEND ALL_SRC_C "${PLATFORM_DIR}/target/musca_a/Native_Driver/qspi_ip6514e_drv.c")
+  list(APPEND ALL_SRC_C "${PLATFORM_DIR}/target/musca_a/Libraries/mt25ql_flash_lib.c")
 endif()
 
 if (NOT DEFINED BUILD_TIME)
@@ -145,6 +148,7 @@
   # it needs  to create an empty one.
   set(SST_RAM_FS True)
   embedded_include_directories(PATH "${PLATFORM_DIR}/target/musca_a/CMSIS_Driver" ABSOLUTE)
+  embedded_include_directories(PATH "${PLATFORM_DIR}/driver" ABSOLUTE)
 endif()
 
 if (NOT BL2)
diff --git a/platform/ext/target/musca_a/CMSIS_Driver/Config/cmsis_driver_config.h b/platform/ext/target/musca_a/CMSIS_Driver/Config/cmsis_driver_config.h
index 32b51f3..17a3ad4 100644
--- a/platform/ext/target/musca_a/CMSIS_Driver/Config/cmsis_driver_config.h
+++ b/platform/ext/target/musca_a/CMSIS_Driver/Config/cmsis_driver_config.h
@@ -26,6 +26,8 @@
 #define UART0_DEV           UART0_PL011_DEV_NS
 #define UART1_DEV           UART1_PL011_DEV_NS
 
+#define FLASH0_DEV          MT25QL_DEV_NS
+
 #define MPC_ISRAM0_DEV      MPC_ISRAM0_DEV_S
 #define MPC_ISRAM1_DEV      MPC_ISRAM1_DEV_S
 #define MPC_ISRAM2_DEV      MPC_ISRAM2_DEV_S
diff --git a/platform/ext/target/musca_a/CMSIS_Driver/Driver_Flash.c b/platform/ext/target/musca_a/CMSIS_Driver/Driver_Flash.c
index b21167f..2af6992 100755
--- a/platform/ext/target/musca_a/CMSIS_Driver/Driver_Flash.c
+++ b/platform/ext/target/musca_a/CMSIS_Driver/Driver_Flash.c
@@ -1,160 +1,129 @@
 /*
- * Copyright (c) 2013-2018 ARM Limited. All rights reserved.
+ * Copyright (c) 2013-2018 Arm Limited
  *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Licensed under the Apache License, Version 2.0 (the License); you may
- * not use this file except in compliance with the License.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- * www.apache.org/licenses/LICENSE-2.0
+ *     http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an AS IS BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
-#include <string.h>
-#include <stdint.h>
 #include "Driver_Flash.h"
-#include "platform_retarget.h"
+
+#include <stdbool.h>
+#include "cmsis.h"
+#include "cmsis_driver_config.h"
 #include "RTE_Device.h"
 #include "flash_layout.h"
-#include "region_defs.h"
 
-#include <assert.h>
-#include "qspi_ip6514e_drv.h"
 
 #ifndef ARG_UNUSED
 #define ARG_UNUSED(arg)  ((void)arg)
 #endif
 
 /* Driver version */
-#define ARM_FLASH_DRV_VERSION    ARM_DRIVER_VERSION_MAJOR_MINOR(1, 0)
+#define ARM_FLASH_DRV_VERSION   ARM_DRIVER_VERSION_MAJOR_MINOR(1, 0)
 
-/* FIXME: The following utility functions should be moved to a common place. */
-static int32_t memcpy4(void * to, const void * from, size_t size)
-{
-    /* make sure that the parameters are aligned to 4 */
-    if (((uint32_t)to & 0x03u) || ((uint32_t)from & 0x03u) || (size & 0x03u)) {
-        return ARM_DRIVER_ERROR_PARAMETER;
-    }
-
-    for (int i = 0; i < size/4; ++i)
-    {
-        ((uint32_t*)to)[i] = ((uint32_t*)from)[i];
-    }
-
-    return ARM_DRIVER_OK;
-}
-
-static int32_t memset4(void * addr, int pattern, size_t count)
-{
-    uint32_t pattern32;
-
-    /* make sure that the parameters are aligned to 4 */
-    if (((uint32_t)addr & 0x03u) || (count & 0x03u)) {
-        return ARM_DRIVER_ERROR_PARAMETER;
-    }
-
-    pattern32 = (uint32_t)pattern |
-                (uint32_t)pattern << 8 |
-                (uint32_t)pattern << 16 |
-                (uint32_t)pattern << 24;
-
-    for (int i = 0; i < count/4; ++i)
-    {
-        ((uint32_t*)addr)[i] = pattern32;
-    }
-
-    return ARM_DRIVER_OK;
-}
-
-/*
- * ARM FLASH device structure
- */
-struct arm_flash_dev_t {
-    const uint32_t memory_base;   /*!< FLASH memory base address */
-    ARM_FLASH_INFO *data;         /*!< FLASH data */
-};
-
-/* Flash Status */
-static ARM_FLASH_STATUS FlashStatus = {0, 0, 0};
-
-/* Driver Version */
 static const ARM_DRIVER_VERSION DriverVersion = {
-    ARM_FLASH_API_VERSION,
+    ARM_FLASH_API_VERSION,  /* Defined in the CMSIS Flash Driver header file */
     ARM_FLASH_DRV_VERSION
 };
 
+/**
+ * \brief Flash driver capability macro definitions \ref ARM_FLASH_CAPABILITIES
+ */
+/* Flash Ready event generation capability values */
+#define EVENT_READY_NOT_AVAILABLE   (0u)
+#define EVENT_READY_AVAILABLE       (1u)
+/* Data access size values */
+#define DATA_WIDTH_8BIT             (0u)
+#define DATA_WIDTH_16BIT            (1u)
+#define DATA_WIDTH_32BIT            (2u)
+/* Chip erase capability values */
+#define CHIP_ERASE_NOT_SUPPORTED    (0u)
+#define CHIP_ERASE_SUPPORTED        (1u)
+
 /* Driver Capabilities */
 static const ARM_FLASH_CAPABILITIES DriverCapabilities = {
-    0, /* event_ready */
-    2, /* data_width = 0:8-bit, 1:16-bit, 2:32-bit */
-    1  /* erase_chip */
+    EVENT_READY_NOT_AVAILABLE,
+    DATA_WIDTH_32BIT,
+    CHIP_ERASE_SUPPORTED
 };
 
-static int32_t is_range_valid(struct arm_flash_dev_t *flash_dev,
-                              uint32_t offset)
+/**
+ * \brief Flash status macro definitions \ref ARM_FLASH_STATUS
+ */
+/* Busy status values of the Flash driver  */
+#define DRIVER_STATUS_IDLE      (0u)
+#define DRIVER_STATUS_BUSY      (1u)
+/* Error status values of the Flash driver */
+#define DRIVER_STATUS_NO_ERROR  (0u)
+#define DRIVER_STATUS_ERROR     (1u)
+
+/**
+ * \brief Arm Flash device structure.
+ */
+struct arm_flash_dev_t {
+    struct mt25ql_dev_t* dev;   /*!< FLASH memory device structure */
+    ARM_FLASH_INFO *data;       /*!< FLASH memory device data */
+};
+
+/**
+ * \brief      Check if the Flash memory boundaries are not violated.
+ * \param[in]  flash_dev  Flash device structure \ref arm_flash_dev_t
+ * \param[in]  offset     Highest Flash memory address which would be accessed.
+ * \return     Returns true if Flash memory boundaries are not violated, false
+ *             otherwise.
+ */
+static bool is_range_valid(struct arm_flash_dev_t *flash_dev,
+                           uint32_t offset)
 {
     uint32_t flash_limit = 0;
-    int32_t rc = 0;
 
-    flash_limit = (flash_dev->data->sector_count * flash_dev->data->sector_size)
-                   - 1;
+    /* Calculating the highest address of the Flash memory address range */
+    flash_limit = FLASH_TOTAL_SIZE - 1;
 
-    if (offset > flash_limit) {
-        rc = -1;
-    }
-    return rc;
+    return (offset > flash_limit) ? (false) : (true) ;
 }
 
-static int32_t is_write_aligned(struct arm_flash_dev_t *flash_dev,
-                                uint32_t param)
+/**
+ * \brief        Check if the parameter is aligned to program_unit.
+ * \param[in]    flash_dev  Flash device structure \ref arm_flash_dev_t
+ * \param[in]    param      Any number that can be checked against the
+ *                          program_unit, e.g. Flash memory address or
+ *                          data length in bytes.
+ * \return       Returns true if param is aligned to program_unit, false
+ *               otherwise.
+ */
+static bool is_write_aligned(struct arm_flash_dev_t *flash_dev,
+                             uint32_t param)
 {
-    int32_t rc = 0;
-
-    if ((param % flash_dev->data->program_unit) != 0) {
-        rc = -1;
-    }
-    return rc;
-}
-
-static int32_t is_sector_aligned(struct arm_flash_dev_t *flash_dev,
-                                 uint32_t offset)
-{
-    int32_t rc = 0;
-
-    if ((offset % flash_dev->data->sector_size) != 0) {
-        rc = -1;
-    }
-    return rc;
+    return ((param % flash_dev->data->program_unit) != 0) ? (false) : (true);
 }
 
 #if (RTE_FLASH0)
 static ARM_FLASH_INFO ARM_FLASH0_DEV_DATA = {
-    .sector_info  = NULL,                  /* Uniform sector layout */
-    .sector_count = FLASH0_SIZE / FLASH0_SECTOR_SIZE,
-    .sector_size  = FLASH0_SECTOR_SIZE,
-    .page_size    = FLASH0_PAGE_SIZE,
-    .program_unit = FLASH0_PROGRAM_UNIT,
-    .erased_value = 0xFF};
+    .sector_info    = NULL,     /* Uniform sector layout */
+    .sector_count   = FLASH_TOTAL_SIZE / SUBSECTOR_4KB,
+    .sector_size    = SUBSECTOR_4KB,
+    .page_size      = FLASH_PAGE_SIZE,
+    .program_unit   = 1u,       /* Minimum write size in bytes */
+    .erased_value   = 0xFF
+};
 
 static struct arm_flash_dev_t ARM_FLASH0_DEV = {
-#if (__DOMAIN_NS == 1)
-    .memory_base = FLASH0_BASE_NS,
-#else
-    .memory_base = FLASH0_BASE_S,
-#endif /* __DOMAIN_NS == 1 */
-    .data        = &(ARM_FLASH0_DEV_DATA)};
+    .dev    = &FLASH0_DEV,
+    .data   = &(ARM_FLASH0_DEV_DATA)
+};
 
-struct arm_flash_dev_t *FLASH0_DEV = &ARM_FLASH0_DEV;
-
-/*
- * Functions
- */
+/* Flash Status */
+static ARM_FLASH_STATUS ARM_FLASH0_STATUS = {0, 0, 0};
 
 static ARM_DRIVER_VERSION ARM_Flash_GetVersion(void)
 {
@@ -168,100 +137,98 @@
 
 static int32_t ARM_Flash_Initialize(ARM_Flash_SignalEvent_t cb_event)
 {
+    enum mt25ql_error_t err = MT25QL_ERR_NONE;
+
     ARG_UNUSED(cb_event);
-    /* Nothing to be done */
+
+    qspi_ip6514e_enable(ARM_FLASH0_DEV.dev->controller);
+
+    /* Configure QSPI Flash controller and memory for optimal use */
+    err = mt25ql_cfg_optimal(ARM_FLASH0_DEV.dev);
+
+    if(err != MT25QL_ERR_NONE) {
+        return ARM_DRIVER_ERROR;
+    }
+
     return ARM_DRIVER_OK;
 }
 
 static int32_t ARM_Flash_Uninitialize(void)
 {
-    /* Nothing to be done */
+    qspi_ip6514e_disable(ARM_FLASH0_DEV.dev->controller);
+
     return ARM_DRIVER_OK;
 }
 
 static int32_t ARM_Flash_PowerControl(ARM_POWER_STATE state)
 {
-    switch (state) {
+    switch(state) {
     case ARM_POWER_FULL:
         /* Nothing to be done */
         return ARM_DRIVER_OK;
-        break;
-
     case ARM_POWER_OFF:
     case ARM_POWER_LOW:
-    default:
         return ARM_DRIVER_ERROR_UNSUPPORTED;
+    default:
+        return ARM_DRIVER_ERROR_PARAMETER;
     }
 }
 
 static int32_t ARM_Flash_ReadData(uint32_t addr, void *data, uint32_t cnt)
 {
-    volatile uint32_t mem_base = FLASH0_DEV->memory_base;
-    uint32_t start_addr = mem_base + addr;
-    int32_t rc = 0;
+    enum mt25ql_error_t err = MT25QL_ERR_NONE;
+    bool is_valid = true;
 
-    /* Check flash memory boundaries */
-    rc = is_range_valid(FLASH0_DEV, addr + cnt);
-    if (rc != 0) {
+    ARM_FLASH0_STATUS.error = DRIVER_STATUS_NO_ERROR;
+
+    /* Check Flash memory boundaries */
+    is_valid = is_range_valid(&ARM_FLASH0_DEV, addr + cnt);
+    if(is_valid != true) {
+        ARM_FLASH0_STATUS.error = DRIVER_STATUS_ERROR;
         return ARM_DRIVER_ERROR_PARAMETER;
     }
 
-    /* Redirecting SST storage to code sram */
-    if(addr >= SST_FLASH_AREA_ADDR &&
-       addr <= SST_FLASH_AREA_ADDR + FLASH_SST_AREA_SIZE) {
-        start_addr = S_CODE_SRAM_ALIAS_BASE + addr;
-        memcpy(data, (void *)start_addr, cnt);
-    } else {
-        uint32_t window_off = start_addr % SPI_FLASH_ACCESS_LENGTH;
-        uint32_t window_base = start_addr - window_off;
-        /* Might need to handle this case */
-        assert ((window_off + cnt) <= SPI_FLASH_ACCESS_LENGTH);
-        qspi_remap_base(window_base - mem_base);
-        if (cnt % 4 != 0) { /* Some read sizes are smaller than 4 bytes */
-            memcpy(data, (void *)(mem_base + window_off), cnt);
-        } else {
-            /* Need to use memcpy4 instead of memcpy as it's possible that
-             * this method is being used to copy image from Flash to SRAM */
-            memcpy4(data, (void *)(mem_base + window_off), cnt);
-        }
+    ARM_FLASH0_STATUS.busy = DRIVER_STATUS_BUSY;
+
+    err = mt25ql_command_read(ARM_FLASH0_DEV.dev, addr, data, cnt);
+
+    ARM_FLASH0_STATUS.busy = DRIVER_STATUS_IDLE;
+
+    if(err != MT25QL_ERR_NONE) {
+        ARM_FLASH0_STATUS.error = DRIVER_STATUS_ERROR;
+        return ARM_DRIVER_ERROR;
     }
 
     return ARM_DRIVER_OK;
 }
 
-static int32_t ARM_Flash_ProgramData(uint32_t addr, const void *data, uint32_t cnt)
+static int32_t ARM_Flash_ProgramData(uint32_t addr,
+                                     const void *data, uint32_t cnt)
 {
-    volatile uint32_t mem_base = FLASH0_DEV->memory_base;
-    uint32_t start_addr = mem_base + addr;
-    int32_t rc = 0;
+    enum mt25ql_error_t err = MT25QL_ERR_NONE;
 
-    /* Check flash memory boundaries and alignment with minimal write size */
-    rc  = is_range_valid(FLASH0_DEV, addr + cnt);
-    rc |= is_write_aligned(FLASH0_DEV, addr);
-    rc |= is_write_aligned(FLASH0_DEV, cnt);
-    if (rc != 0) {
+    ARM_FLASH0_STATUS.error = DRIVER_STATUS_NO_ERROR;
+
+    /* Check Flash memory boundaries and alignment with minimum write size
+     * (program_unit), data size also needs to be a multiple of program_unit.
+     */
+    if(!(is_range_valid(&ARM_FLASH0_DEV, addr + cnt) &&
+         is_write_aligned(&ARM_FLASH0_DEV, addr)     &&
+         is_write_aligned(&ARM_FLASH0_DEV, cnt)      )) {
+
+        ARM_FLASH0_STATUS.error = DRIVER_STATUS_ERROR;
         return ARM_DRIVER_ERROR_PARAMETER;
     }
 
-    /* Redirecting SST storage to code sram */
-    if(addr >= SST_FLASH_AREA_ADDR &&
-       addr <= SST_FLASH_AREA_ADDR + FLASH_SST_AREA_SIZE) {
-        start_addr = S_CODE_SRAM_ALIAS_BASE + addr;
-        /* Flash interface emulated over CODE SRAM. Writing it on 4 aligned
-         * addresses is necessary, so use a special memcpy function.
-         */
-        rc = memcpy4((void *)start_addr, data, cnt);
-        if (rc != ARM_DRIVER_OK) {
-            return ARM_DRIVER_ERROR_PARAMETER;
-        }
-    } else {
-        /* Flash interface just emulated over SRAM, use memcpy */
-        uint32_t window_off = start_addr % SPI_FLASH_ACCESS_LENGTH;
-        uint32_t window_base = start_addr - window_off;
-        /* Might need to handle this case */
-        assert ((window_off + cnt) <= SPI_FLASH_ACCESS_LENGTH);
-        qspi_remap_base(window_base - mem_base);
-        memcpy((void *)(mem_base + window_off), data, cnt);
+    ARM_FLASH0_STATUS.busy = DRIVER_STATUS_BUSY;
+
+    err = mt25ql_command_write(ARM_FLASH0_DEV.dev, addr, data, cnt);
+
+    ARM_FLASH0_STATUS.busy = DRIVER_STATUS_IDLE;
+
+    if(err != MT25QL_ERR_NONE) {
+        ARM_FLASH0_STATUS.error = DRIVER_STATUS_ERROR;
+        return ARM_DRIVER_ERROR;
     }
 
     return ARM_DRIVER_OK;
@@ -269,37 +236,28 @@
 
 static int32_t ARM_Flash_EraseSector(uint32_t addr)
 {
-    volatile uint32_t mem_base = FLASH0_DEV->memory_base;
-    uint32_t rc = 0;
+    enum mt25ql_error_t err = MT25QL_ERR_NONE;
 
-    rc  = is_range_valid(FLASH0_DEV, addr);
-    rc |= is_sector_aligned(FLASH0_DEV, addr);
-    if (rc != 0) {
-        return ARM_DRIVER_ERROR_PARAMETER;
-    }
+    ARM_FLASH0_STATUS.error = DRIVER_STATUS_NO_ERROR;
+    ARM_FLASH0_STATUS.busy = DRIVER_STATUS_BUSY;
 
-    /* Redirecting SST storage to code sram */
-    if(addr >= SST_FLASH_AREA_ADDR &&
-       addr <= SST_FLASH_AREA_ADDR + FLASH_SST_AREA_SIZE) {
-        /* Flash interface emulated over CODE SRAM. Writing it on 4 aligned
-         * addresses is necessary, so use a special memset function.
-         */
-        rc = memset4((void *)(S_CODE_SRAM_ALIAS_BASE + addr),
-                   FLASH0_DEV->data->erased_value,
-                   FLASH0_DEV->data->sector_size);
-        if (rc != ARM_DRIVER_OK) {
+    /* The erase function checks whether the address is aligned with
+     * the sector or subsector and checks the Flash memory boundaries.
+     */
+    err = mt25ql_erase(ARM_FLASH0_DEV.dev,
+                       addr, ARM_FLASH0_DEV.data->sector_size);
+
+    ARM_FLASH0_STATUS.busy = DRIVER_STATUS_IDLE;
+
+    if(err != MT25QL_ERR_NONE) {
+        ARM_FLASH0_STATUS.error = DRIVER_STATUS_ERROR;
+
+        if((err == MT25QL_ERR_ADDR_NOT_ALIGNED) ||
+           (err == MT25QL_ERR_ADDR_TOO_BIG)     ||
+           (err == MT25QL_ERR_WRONG_ARGUMENT)    ) {
             return ARM_DRIVER_ERROR_PARAMETER;
         }
-    } else {
-        uint32_t start_addr = S_CODE_SRAM_ALIAS_BASE + addr;
-        uint32_t window_off = start_addr % SPI_FLASH_ACCESS_LENGTH;
-        uint32_t window_base = start_addr - window_off;
-        assert ((window_off + FLASH0_DEV->data->sector_size)
-                <= SPI_FLASH_ACCESS_LENGTH);
-        qspi_remap_base(window_base - mem_base);
-        memset((void *)(mem_base + window_off),
-               FLASH0_DEV->data->erased_value,
-               FLASH0_DEV->data->sector_size);
+        return ARM_DRIVER_ERROR;
     }
 
     return ARM_DRIVER_OK;
@@ -307,45 +265,46 @@
 
 static int32_t ARM_Flash_EraseChip(void)
 {
-    uint32_t i;
-    uint32_t addr = FLASH0_DEV->memory_base;
-    int32_t rc = ARM_DRIVER_ERROR_UNSUPPORTED;
+    enum mt25ql_error_t err = MT25QL_ERR_NONE;
 
-    /* Check driver capability erase_chip bit */
-    if (DriverCapabilities.erase_chip == 1) {
-        for (i = 0; i < FLASH0_DEV->data->sector_count; i++) {
-            /* Redirecting SST storage to code sram */
-            if(addr >= SST_FLASH_AREA_ADDR &&
-               addr <= SST_FLASH_AREA_ADDR + FLASH_SST_AREA_SIZE) {
-                rc = memset4((void *)addr - FLASH0_DEV->memory_base
-                               + S_CODE_SRAM_ALIAS_BASE,
-                        FLASH0_DEV->data->erased_value,
-                        FLASH0_DEV->data->sector_size);
-                if (rc != ARM_DRIVER_OK) {
-                    return ARM_DRIVER_ERROR_PARAMETER;
-                }
+    if(DriverCapabilities.erase_chip == 1) {
 
-            } else {
-                /* Flash interface just emulated over SRAM, use memset */
-                memset((void *)addr,
-                       FLASH0_DEV->data->erased_value,
-                       FLASH0_DEV->data->sector_size);
+        ARM_FLASH0_STATUS.error = DRIVER_STATUS_NO_ERROR;
+        ARM_FLASH0_STATUS.busy = DRIVER_STATUS_BUSY;
+
+        /* The erase function checks whether the address is aligned with
+         * the sector or subsector and checks the Flash memory boundaries.
+         */
+        err = mt25ql_erase(ARM_FLASH0_DEV.dev, 0, MT25QL_ERASE_ALL_FLASH);
+
+        ARM_FLASH0_STATUS.busy = DRIVER_STATUS_IDLE;
+
+        if(err != MT25QL_ERR_NONE) {
+            ARM_FLASH0_STATUS.error = DRIVER_STATUS_ERROR;
+
+            if((err == MT25QL_ERR_ADDR_NOT_ALIGNED) ||
+               (err == MT25QL_ERR_ADDR_TOO_BIG)     ||
+               (err == MT25QL_ERR_WRONG_ARGUMENT)    ) {
+                return ARM_DRIVER_ERROR_PARAMETER;
             }
-            addr += FLASH0_DEV->data->sector_size;
-            rc = ARM_DRIVER_OK;
+            return ARM_DRIVER_ERROR;
         }
+
+        return ARM_DRIVER_OK;
+
+    } else {
+        return ARM_DRIVER_ERROR_UNSUPPORTED;
     }
-    return rc;
 }
 
 static ARM_FLASH_STATUS ARM_Flash_GetStatus(void)
 {
-    return FlashStatus;
+    return ARM_FLASH0_STATUS;
 }
 
 static ARM_FLASH_INFO * ARM_Flash_GetInfo(void)
 {
-    return FLASH0_DEV->data;
+    return ARM_FLASH0_DEV.data;
 }
 
 ARM_DRIVER_FLASH Driver_FLASH0 = {
diff --git a/platform/ext/target/musca_a/Device/Config/device_cfg.h b/platform/ext/target/musca_a/Device/Config/device_cfg.h
index 516ecae..55784cc 100755
--- a/platform/ext/target/musca_a/Device/Config/device_cfg.h
+++ b/platform/ext/target/musca_a/Device/Config/device_cfg.h
@@ -67,4 +67,12 @@
 #define CMSDK_TIMER1_S
 #define CMSDK_TIMER1_NS
 
+/* Cadence QSPI Flash Controller */
+#define QSPI_IP6514E_S
+#define QSPI_IP6514E_NS
+
+/* MT25QL Flash memory library */
+#define MT25QL_S
+#define MT25QL_NS
+
 #endif  /* __ARM_LTD_DEVICE_CFG_H__ */
diff --git a/platform/ext/target/musca_a/Device/Include/platform_retarget.h b/platform/ext/target/musca_a/Device/Include/platform_retarget.h
index 1e4b2d8..87adfdc 100755
--- a/platform/ext/target/musca_a/Device/Include/platform_retarget.h
+++ b/platform/ext/target/musca_a/Device/Include/platform_retarget.h
@@ -59,13 +59,5 @@
 #define MPC_QSPI_RANGE_LIMIT_NS       (0x00240000)
 #define MPC_QSPI_RANGE_BASE_S         (0x10200000)
 #define MPC_QSPI_RANGE_LIMIT_S        (0x10240000)
-#define FLASH0_BASE_S                 (MPC_QSPI_RANGE_BASE_S)
-#define FLASH0_BASE_NS                (MPC_QSPI_RANGE_BASE_NS)
-#define FLASH0_SIZE                   (0x00800000)  /* 8 MB */
-#define FLASH0_SECTOR_SIZE            (0x00001000)  /* 4 kB */
-#define FLASH0_PAGE_SIZE              (0x00001000)  /* 4 kB */
-#define FLASH0_PROGRAM_UNIT           (0x4)         /* Minimum write size */
-
-#define QSPI_CONTROLLER_ADDRESS       (0x5010A000)
 
 #endif  /* __ARM_LTD_MUSCA_RETARGET_H__ */
diff --git a/platform/ext/target/musca_a/Device/Include/platform_retarget_dev.h b/platform/ext/target/musca_a/Device/Include/platform_retarget_dev.h
index 26e945d..03cf940 100755
--- a/platform/ext/target/musca_a/Device/Include/platform_retarget_dev.h
+++ b/platform/ext/target/musca_a/Device/Include/platform_retarget_dev.h
@@ -111,6 +111,17 @@
 extern struct timer_cmsdk_dev_t CMSDK_TIMER1_DEV_NS;
 #endif
 
+/* QSPI Flash Controller driver structures */
+#ifdef QSPI_IP6514E_S
+#include "qspi_ip6514e_drv.h"
+extern struct qspi_ip6514e_dev_t QSPI_DEV_S;
+#endif
+
+#ifdef QSPI_IP6514E_NS
+#include "qspi_ip6514e_drv.h"
+extern struct qspi_ip6514e_dev_t QSPI_DEV_NS;
+#endif
+
 /* ARM PPC driver structures */
 #ifdef AHB_PPC0_S
 #include "ppc_sse200_drv.h"
@@ -167,4 +178,16 @@
 extern struct ppc_sse200_dev_t APB_PPCEXP3_DEV_S;
 #endif
 
+/* ======= External peripheral configuration structure declarations ======= */
+
+/* MT25QL Flash memory library structures */
+#if (defined(MT25QL_S) && defined(QSPI_IP6514E_S))
+#include "Libraries/mt25ql_flash_lib.h"
+extern struct mt25ql_dev_t MT25QL_DEV_S;
+#endif
+#if (defined(MT25QL_NS) && defined(QSPI_IP6514E_NS))
+#include "Libraries/mt25ql_flash_lib.h"
+extern struct mt25ql_dev_t MT25QL_DEV_NS;
+#endif
+
 #endif  /* __ARM_LTD_MUSCA_A1_RETARGET_DEV_H__ */
diff --git a/platform/ext/target/musca_a/Device/Source/platform_retarget_dev.c b/platform/ext/target/musca_a/Device/Source/platform_retarget_dev.c
index 26c07ca..dc02ba7 100755
--- a/platform/ext/target/musca_a/Device/Source/platform_retarget_dev.c
+++ b/platform/ext/target/musca_a/Device/Source/platform_retarget_dev.c
@@ -335,6 +335,7 @@
                                                 &(CMSDK_TIMER1_DEV_DATA_NS)};
 #endif
 
+/* ARM UART PL011 driver structures */
 #ifdef UART0_PL011_S
 static const struct uart_pl011_dev_cfg_t UART0_PL011_DEV_CFG_S = {
     .base = MUSCA_UART0_S_BASE,
@@ -393,4 +394,72 @@
     .baudrate = 0};
 struct uart_pl011_dev_t UART1_PL011_DEV_NS = {&(UART1_PL011_DEV_CFG_NS),
                                                 &(UART1_PL011_DEV_DATA_NS)};
-#endif
\ No newline at end of file
+#endif
+
+/* QSPI IP6514E driver structures */
+#ifdef QSPI_IP6514E_S
+static const struct qspi_ip6514e_dev_cfg_t QSPI_DEV_CFG_S = {
+    .base = MUSCA_QSPI_REG_S_BASE,
+    /*
+     * On Musca-A1, only the 18 first address bits are used for any AHB
+     * address in a request coming to the QSPI Flash controller.
+     * It means that direct accesses are limited to the first 256 KiB of the
+     * Flash memory (if the Remap register is not used) and that the Indirect
+     * Trigger zone needs to be inside the first 256 KiB as well.
+     */
+    .addr_mask = (1U << 18) - 1, /* 256 KiB minus 1 byte */
+};
+struct qspi_ip6514e_dev_t QSPI_DEV_S = {
+    &QSPI_DEV_CFG_S
+};
+#endif
+
+#ifdef QSPI_IP6514E_NS
+static const struct qspi_ip6514e_dev_cfg_t QSPI_DEV_CFG_NS = {
+    .base = MUSCA_QSPI_REG_NS_BASE,
+    /*
+     * On Musca-A1, only the 18 first address bits are used for any AHB
+     * address in a request coming to the QSPI Flash controller.
+     * It means that direct accesses are limited to the first 256 KiB of the
+     * Flash memory (if the Remap register is not used) and that the Indirect
+     * Trigger zone needs to be inside the first 256 KiB as well.
+     */
+    .addr_mask = (1U << 18) - 1, /* 256 KiB minus 1 byte */
+};
+struct qspi_ip6514e_dev_t QSPI_DEV_NS = {
+    &QSPI_DEV_CFG_NS
+};
+#endif
+
+/* ======= External peripheral configuration structure definitions ======= */
+
+/* MT25QL Flash memory library structures */
+#if (defined(MT25QL_S) && defined(QSPI_IP6514E_S))
+struct mt25ql_dev_t MT25QL_DEV_S = {
+    .controller = &QSPI_DEV_S,
+    .direct_access_start_addr = MUSCA_QSPI_FLASH_S_BASE,
+    .baud_rate_div = 4U,
+    /*
+     * 8 MiB flash memory are advertised in the Arm Musca-A Test Chip and Board
+     * Technical Reference Manual. The MT25QL Flash device may however contain
+     * more.
+     */
+    .size = 0x00800000U, /* 8 MiB */
+    .func_state = MT25QL_FUNC_STATE_DEFAULT,
+};
+#endif
+
+#if (defined(MT25QL_NS) && defined(QSPI_IP6514E_NS))
+struct mt25ql_dev_t MT25QL_DEV_NS = {
+    .controller = &QSPI_DEV_NS,
+    .direct_access_start_addr = MUSCA_QSPI_FLASH_NS_BASE,
+    .baud_rate_div = 4U,
+    /*
+     * 8 MiB flash memory are advertised in the Arm Musca-A Test Chip and Board
+     * Technical Reference Manual. The MT25QL Flash device may however contain
+     * more.
+     */
+    .size = 0x00800000U, /* 8 MiB */
+    .func_state = MT25QL_FUNC_STATE_DEFAULT,
+};
+#endif
diff --git a/platform/ext/target/musca_a/Libraries/mt25ql_flash_lib.c b/platform/ext/target/musca_a/Libraries/mt25ql_flash_lib.c
new file mode 100644
index 0000000..13ca3f0
--- /dev/null
+++ b/platform/ext/target/musca_a/Libraries/mt25ql_flash_lib.c
@@ -0,0 +1,848 @@
+/*
+ * Copyright (c) 2018 Arm Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+/* Use memcpy function */
+#include <string.h>
+
+#include "mt25ql_flash_lib.h"
+#include "Native_Driver/qspi_ip6514e_drv.h"
+
+/** Setter bit manipulation macro */
+#define SET_BIT(WORD, BIT_INDEX) ((WORD) |= (1U << (BIT_INDEX)))
+/** Clearing bit manipulation macro */
+#define CLR_BIT(WORD, BIT_INDEX) ((WORD) &= ~(1U << (BIT_INDEX)))
+/** Getter bit manipulation macro */
+#define GET_BIT(WORD, BIT_INDEX) (bool)(((WORD) & (1U << (BIT_INDEX))))
+
+#define BITS_PER_WORD  32U
+#define BYTES_PER_WORD 4U
+
+#define ARG_NOT_USED     0
+#define ARG_PTR_NOT_USED NULL
+
+/** MT25QL used command */
+#define WRITE_ENABLE_CMD                    0x06U
+#define READ_ENHANCED_VOLATILE_CFG_REG_CMD  0x65U
+#define WRITE_ENHANCED_VOLATILE_CFG_REG_CMD 0x61U
+#define READ_VOLATILE_CFG_REG_CMD           0x85U
+#define WRITE_VOLATILE_CFG_REG_CMD          0x81U
+#define READ_FLAG_STATUS_REG_CMD            0x70U
+#define SUBSECTOR_ERASE_32KB_CMD            0x52U
+#define SUBSECTOR_ERASE_4KB_CMD             0x20U
+#define SECTOR_ERASE_CMD                    0xD8U
+#define BULK_ERASE_CMD                      0xC7U
+/*
+ * The baud rate divisor in \ref mt25ql_dev_t needs to be configured adequately
+ * to handle those commands.
+ */
+#define QUAD_OUTPUT_FAST_READ_CMD           0x6BU
+#define READ_CMD                            0x03U
+#define QUAD_INPUT_FAST_PROGRAM_CMD         0x32U
+#define PAGE_PROGRAM_CMD                    0x02U
+
+/** MT25QL Enhanced Volatile Configuration Register access */
+#define ENHANCED_VOLATILE_CFG_REG_LEN      1U
+#define ENHANCED_VOLATILE_CFG_REG_QSPI_POS 7U
+#define ENHANCED_VOLATILE_CFG_REG_DSPI_POS 6U
+
+/** MT25QL Volatile Configuration Register access */
+#define VOLATILE_CFG_REG_LEN               1U
+#define VOLATILE_CFG_REG_DUMMY_CYCLES_POS  4U
+#define VOLATILE_CFG_REG_DUMMY_CYCLES_BITS 4U
+
+/** MT25QL Flag Status Register access */
+#define FLAG_STATUS_REG_LEN       1U
+#define FLAG_STATUS_REG_READY_POS 7U
+
+/*
+ * 8 is the minimal number of dummy clock cycles needed to reach the maximal
+ * frequency of the Quad Output Fast Read Command.
+ */
+#define OPTIMAL_READ_DUMMY_CYCLES  8U
+#define CMD_READ_DUMMY_CYCLES      0U
+#define OPTIMAL_WRITE_DUMMY_CYCLES 0U
+#define CMD_WRITE_DUMMY_CYCLES     0U
+
+/* Only up to 8 bytes can be read or written using the Flash commands. */
+#define CMD_DATA_MAX_SIZE 8U
+
+/**
+ * \brief Change specific bits in a 32 bits word.
+ *
+ * \param[in,out] word         Pointer of the word to change
+ * \param[in]     bits         bits_length bits to put at bits_pos in the word
+ *                             pointed
+ * \param[in]     bits_length  Number of bits to change
+ * \param[in]     bits_pos     Position of the bits to change
+ *
+ * \note This function will do nothing if the parameters given are incorrect:
+ *         * word is NULL
+ *         * bits_length + bits_pos > 32
+ *         * bits_length is 0
+ */
+static void change_bits_in_word(volatile uint32_t *word,
+                                uint32_t bits,
+                                uint32_t bits_length,
+                                uint32_t bits_pos)
+{
+    uint32_t mask;
+
+    if ((word == NULL) ||
+        ((bits_length + bits_pos) > BITS_PER_WORD) ||
+        (bits_length == 0U)) {
+        /* Silently fail */
+        return;
+    }
+
+    /* Change all the bits */
+    if (bits_length == BITS_PER_WORD) {
+        *word = bits;
+        return;
+    }
+
+    mask = ((1U << bits_length) - 1);
+    /*
+     * We change the bits in three steps:
+     *   - clear bits_length bits with zeroes at bits_pos in the word
+     *   - mask bits in case it contains more than bits_length bits
+     *   - set the new bits in the cleared word
+     * Because the data pointed by word is only read once, the data will still
+     * be coherent after an interruption that changes it.
+     */
+    *word = ((*word & ~(mask << bits_pos)) | ((bits & mask) << bits_pos));
+}
+
+/**
+ * \brief Send the Write Enable command, needed before any write.
+ *
+ * \param[in] dev     Pointer to MT25QL device structure \ref mt25ql_dev_t
+ */
+static void send_write_enable(struct mt25ql_dev_t* dev)
+{
+    qspi_ip6514e_send_simple_cmd(dev->controller, WRITE_ENABLE_CMD);
+}
+
+/**
+ * \brief Activate the QSPI mode on the flash device and on the controller.
+ *
+ * \param[in] dev     Pointer to MT25QL device structure \ref mt25ql_dev_t
+ *
+ * \return Return error code as specified in \ref mt25ql_error_t
+ */
+static enum mt25ql_error_t activate_qspi_mode(struct mt25ql_dev_t* dev)
+{
+    uint8_t enhanced_volatile_cfg_reg = 0;
+    enum qspi_ip6514e_error_t controller_error;
+
+    /*
+     * Read the Enhanced Volatile Configuration Register, modify it to activate
+     * QSPI mode then write back the modified value to the register. This will
+     * activate QSPI mode on the flash side.
+     */
+    controller_error = qspi_ip6514e_send_read_cmd(
+                                             dev->controller,
+                                             READ_ENHANCED_VOLATILE_CFG_REG_CMD,
+                                             &enhanced_volatile_cfg_reg,
+                                             ENHANCED_VOLATILE_CFG_REG_LEN,
+                                             ARG_NOT_USED,
+                                             ARG_NOT_USED,
+                                             0); /* No dummy cycles needed for
+                                                    this command. */
+    if (controller_error != QSPI_IP6514E_ERR_NONE) {
+        return (enum mt25ql_error_t)controller_error;
+    }
+
+    /*
+     * Disable the Dual-SPI mode and activate QSPI mode.
+     * Clearing the bit enables the mode, setting it disables it.
+     */
+    SET_BIT(enhanced_volatile_cfg_reg, ENHANCED_VOLATILE_CFG_REG_DSPI_POS);
+    CLR_BIT(enhanced_volatile_cfg_reg, ENHANCED_VOLATILE_CFG_REG_QSPI_POS);
+
+    send_write_enable(dev);
+
+    controller_error = qspi_ip6514e_send_write_cmd(
+                                            dev->controller,
+                                            WRITE_ENHANCED_VOLATILE_CFG_REG_CMD,
+                                            &enhanced_volatile_cfg_reg,
+                                            ENHANCED_VOLATILE_CFG_REG_LEN,
+                                            ARG_NOT_USED,
+                                            ARG_NOT_USED,
+                                            0); /* No dummy cycles needed for
+                                                   this command. */
+    if (controller_error != QSPI_IP6514E_ERR_NONE) {
+        return (enum mt25ql_error_t)controller_error;
+    }
+
+    /* Activate the QSPI mode on the controller side as well */
+    controller_error = qspi_ip6514e_set_spi_mode(dev->controller,
+                                                 QSPI_IP6514E_QSPI_MODE,
+                                                 QSPI_IP6514E_QSPI_MODE,
+                                                 QSPI_IP6514E_QSPI_MODE);
+    if (controller_error != QSPI_IP6514E_ERR_NONE) {
+        return (enum mt25ql_error_t)controller_error;
+    }
+
+    return MT25QL_ERR_NONE;
+}
+
+/**
+ * \brief Change the number of dummy clock cycles subsequent to all FAST READ
+ *        commands.
+ *
+ * \param[in] dev          Pointer to MT25QL device structure \ref mt25ql_dev_t
+ * \param[in] dummy_cycles Dummy clock cycles to set
+ *
+ * \return Return error code as specified in \ref mt25ql_error_t
+ */
+static enum mt25ql_error_t change_dummy_cycles(struct mt25ql_dev_t* dev,
+                                               uint32_t dummy_cycles)
+{
+    uint32_t volatile_cfg_reg = 0;
+    enum qspi_ip6514e_error_t controller_error;
+
+    /*
+     * Changes the number of dummy cycles in the Volatile Configuration
+     * Register.
+     */
+    controller_error = qspi_ip6514e_send_read_cmd(dev->controller,
+                                                  READ_VOLATILE_CFG_REG_CMD,
+                                                  &volatile_cfg_reg,
+                                                  VOLATILE_CFG_REG_LEN,
+                                                  ARG_NOT_USED,
+                                                  ARG_NOT_USED,
+                                                  0); /* No dummy cycles needed
+                                                         for this command. */
+    if (controller_error != QSPI_IP6514E_ERR_NONE) {
+        return (enum mt25ql_error_t)controller_error;
+    }
+
+    change_bits_in_word(&volatile_cfg_reg,
+                        dummy_cycles,
+                        VOLATILE_CFG_REG_DUMMY_CYCLES_BITS,
+                        VOLATILE_CFG_REG_DUMMY_CYCLES_POS);
+
+    send_write_enable(dev);
+
+    controller_error = qspi_ip6514e_send_write_cmd(dev->controller,
+                                                   WRITE_VOLATILE_CFG_REG_CMD,
+                                                   &volatile_cfg_reg,
+                                                   VOLATILE_CFG_REG_LEN,
+                                                   ARG_NOT_USED,
+                                                   ARG_NOT_USED,
+                                                   0); /* No dummy cycles needed
+                                                          for this command. */
+    if (controller_error != QSPI_IP6514E_ERR_NONE) {
+        return (enum mt25ql_error_t)controller_error;
+    }
+
+    return MT25QL_ERR_NONE;
+}
+
+/**
+ * \brief Wait until the current program/erase is finished.
+ *
+ * \param[in] dev     Pointer to MT25QL device structure \ref mt25ql_dev_t
+ *
+ * \return Return error code as specified in \ref mt25ql_error_t
+ */
+static enum mt25ql_error_t wait_program_or_erase_complete(
+                                                       struct mt25ql_dev_t* dev)
+{
+    enum qspi_ip6514e_error_t controller_error;
+    uint8_t flag_status_reg = 0;
+
+    /* Wait until the ready bit of the Flag Status Register is set */
+    while (!GET_BIT(flag_status_reg, FLAG_STATUS_REG_READY_POS)) {
+        controller_error = qspi_ip6514e_send_read_cmd(dev->controller,
+                                                      READ_FLAG_STATUS_REG_CMD,
+                                                      &flag_status_reg,
+                                                      FLAG_STATUS_REG_LEN,
+                                                      ARG_NOT_USED,
+                                                      ARG_NOT_USED,
+                                                      0); /* No dummy cycles
+                                                             needed for this
+                                                             command. */
+        if (controller_error != QSPI_IP6514E_ERR_NONE) {
+            return (enum mt25ql_error_t)controller_error;
+        }
+    }
+
+    return MT25QL_ERR_NONE;
+}
+
+/**
+ * \brief Execute a program command that crosses the page size boundary.
+ *
+ * \param[in]  dev               Pointer to MT25QL device structure
+ *                               \ref mt25ql_dev_t
+ * \param[in]  opcode            Opcode for the command.
+ * \param[in]  write_data        Pointer to a memory zone where the write_len
+ *                               number of bytes are located to write for this
+ *                               command.
+ * \param[in]  write_len         Number of bytes to write for the command.
+ *                               Between 1 and 8 bytes (both included) can be
+ *                               written.
+ * \param[in]  addr              Address used for the command
+ * \param[in]  addr_bytes_number Number of address bytes for this command.
+ *                               If an address is not needed for the command,
+ *                               use 0 for argument, otherwise between 1 and
+ *                               4 bytes (both included) can be used.
+ * \param[in]  dummy_cycles      Number of dummy cycles required for the
+ *                               command, between 0 and 31 (both included).
+ *
+ * \return Return error code as specified in \ref mt25ql_error_t
+ *
+ * \note This function will execute two commands: one to program the bytes up to
+ *       the page boundary and another one to program the rest. It will wait
+ *       that bytes are programmed from first command before triggering the
+ *       second one.
+ * \note This function does not send a write enable command before the first
+ *       command and does not check that bytes were programmed after the second
+ *       command.
+ */
+static enum mt25ql_error_t send_boundary_cross_write_cmd(
+                                                     struct mt25ql_dev_t* dev,
+                                                     uint8_t opcode,
+                                                     const void *write_data,
+                                                     uint32_t write_len,
+                                                     uint32_t addr,
+                                                     uint32_t addr_bytes_number,
+                                                     uint32_t dummy_cycles)
+{
+    enum qspi_ip6514e_error_t controller_error;
+    enum mt25ql_error_t library_error;
+    /*
+     * Remaining bytes between the current address and the end of the current
+     * page.
+     */
+    uint32_t page_remainder = FLASH_PAGE_SIZE - (addr % FLASH_PAGE_SIZE);
+
+    /* First write up to the end of the current page. */
+    controller_error = qspi_ip6514e_send_write_cmd(dev->controller,
+                                                   opcode,
+                                                   write_data,
+                                                   page_remainder,
+                                                   addr,
+                                                   addr_bytes_number,
+                                                   dummy_cycles);
+    if (controller_error != QSPI_IP6514E_ERR_NONE) {
+        return (enum mt25ql_error_t)controller_error;
+    }
+
+    write_data = (void *)((uint32_t)write_data + page_remainder);
+    addr += page_remainder;
+
+    /* Wait for the page to be written before sending new commands. */
+    library_error = wait_program_or_erase_complete(dev);
+    if (library_error != MT25QL_ERR_NONE) {
+        return library_error;
+    }
+
+    /* Then write the remaining data of the write_len bytes. */
+    send_write_enable(dev);
+    controller_error = qspi_ip6514e_send_write_cmd(dev->controller,
+                                                   opcode,
+                                                   write_data,
+                                                   write_len - page_remainder,
+                                                   addr,
+                                                   addr_bytes_number,
+                                                   dummy_cycles);
+    if (controller_error != QSPI_IP6514E_ERR_NONE) {
+        return (enum mt25ql_error_t)controller_error;
+    }
+
+    return MT25QL_ERR_NONE;
+}
+
+enum mt25ql_error_t mt25ql_cfg_optimal(struct mt25ql_dev_t* dev)
+{
+    enum qspi_ip6514e_error_t controller_error;
+    enum mt25ql_error_t library_error;
+
+    /*
+     * Assuming that the Flash memory starts in single SPI protocol and as the
+     * QSPI Flash controller also resets in single SPI mode, this function will
+     * first change the Flash memory mode to QSPI and then change the controller
+     * to QSPI.
+     * It will fail if the two sides do not have the same mode when this
+     * function is called.
+     */
+    library_error = activate_qspi_mode(dev);
+    if (library_error != MT25QL_ERR_NONE) {
+        return library_error;
+    }
+
+    /* Change to the optimal number of dummy cycles for read commands. */
+    library_error = change_dummy_cycles(dev, OPTIMAL_READ_DUMMY_CYCLES);
+    if (library_error != MT25QL_ERR_NONE) {
+        return library_error;
+    }
+
+    /* The rest of the configuration needs the controller to be disabled */
+    if (!qspi_ip6514e_is_idle(dev->controller)) {
+        return (enum mt25ql_error_t)QSPI_IP6514E_ERR_CONTROLLER_NOT_IDLE;
+    }
+
+    qspi_ip6514e_disable(dev->controller);
+
+    /* Set the baud rate divisor as configured in the device structure. */
+    controller_error = qspi_ip6514e_set_baud_rate_div(dev->controller,
+                                                      dev->baud_rate_div);
+    if (controller_error != QSPI_IP6514E_ERR_NONE) {
+        return (enum mt25ql_error_t)controller_error;
+    }
+
+    /* Set opcode and dummy cycles needed for read commands. */
+    controller_error = qspi_ip6514e_cfg_reads(dev->controller,
+                                              QUAD_OUTPUT_FAST_READ_CMD,
+                                              OPTIMAL_READ_DUMMY_CYCLES);
+    if (controller_error != QSPI_IP6514E_ERR_NONE) {
+        return (enum mt25ql_error_t)controller_error;
+    }
+
+    /* Set opcode and dummy cycles needed for write commands. */
+    controller_error = qspi_ip6514e_cfg_writes(dev->controller,
+                                               QUAD_INPUT_FAST_PROGRAM_CMD,
+                                               OPTIMAL_WRITE_DUMMY_CYCLES);
+    if (controller_error != QSPI_IP6514E_ERR_NONE) {
+        return (enum mt25ql_error_t)controller_error;
+    }
+
+    /* Set Flash memory constants: bytes per page and address bytes. */
+    controller_error = qspi_ip6514e_cfg_page_size(dev->controller,
+                                                  FLASH_PAGE_SIZE);
+    if (controller_error != QSPI_IP6514E_ERR_NONE) {
+        return (enum mt25ql_error_t)controller_error;
+    }
+
+    controller_error = qspi_ip6514e_cfg_addr_bytes(dev->controller,
+                                                   ADDR_BYTES);
+    if (controller_error != QSPI_IP6514E_ERR_NONE) {
+        return (enum mt25ql_error_t)controller_error;
+    }
+
+    qspi_ip6514e_enable(dev->controller);
+
+    dev->func_state = MT25QL_FUNC_STATE_OPTIMAL;
+
+    return MT25QL_ERR_NONE;
+}
+
+enum mt25ql_error_t mt25ql_direct_read(struct mt25ql_dev_t* dev,
+                                       uint32_t addr,
+                                       void *data,
+                                       uint32_t len)
+{
+    /*
+     * The direct access window size is the size of the memory that can be
+     * accessed with a direct access.
+     */
+    uint32_t direct_access_window_size = dev->controller->cfg->addr_mask + 1;
+    /*
+     * The window number is the number of times it will be needed to remap the
+     * address with the remap register. We move this Direct Access window first
+     * window_number times starting at the beginning address to read full
+     * windows of direct_access_window_size bytes. Then we read the remainder
+     * bytes.
+     */
+    uint32_t window_number = len / direct_access_window_size;
+
+    if (data == NULL || len == 0) {
+        return MT25QL_ERR_WRONG_ARGUMENT;
+    }
+
+    if ((addr + len) >= dev->size) {
+        return MT25QL_ERR_ADDR_TOO_BIG;
+    }
+
+    /*
+     * There is no limitation reading through a Flash page boundary hence we
+     * do not add the same logic here than in the write function.
+     */
+
+    /* Transfer the bytes for the window_number windows first. */
+    for (uint32_t window = 0; window < window_number; window++) {
+        qspi_ip6514e_remap_addr(dev->controller, addr);
+
+        /*
+         * The AHB address to access the Flash memory does not change but it
+         * will be translated differently thanks to the remap function.
+         */
+        memcpy(data,
+               (void *)dev->direct_access_start_addr,
+               direct_access_window_size);
+
+        len -= direct_access_window_size;
+        data = (void *)((uint32_t)data + direct_access_window_size);
+        addr += direct_access_window_size;
+    }
+
+    if (len) {
+        /* Transfer the reminder bytes */
+        qspi_ip6514e_remap_addr(dev->controller, addr);
+
+        memcpy(data, (void *)dev->direct_access_start_addr, len);
+    }
+
+    /* Disable remapping for direct accesses outside of this function. */
+    qspi_ip6514e_disable_remap(dev->controller);
+
+    return MT25QL_ERR_NONE;
+}
+
+enum mt25ql_error_t mt25ql_direct_write(struct mt25ql_dev_t* dev,
+                                        uint32_t addr,
+                                        const void *data,
+                                        uint32_t len)
+{
+    enum mt25ql_error_t library_error;
+    /*
+     * The direct access window size is the size of the memory that can be
+     * accessed with a direct access.
+     */
+    uint32_t direct_access_window_size = dev->controller->cfg->addr_mask + 1;
+    uint32_t window_number;
+    /* Offset between address and the previous 32 bits aligned word */
+    uint32_t word_offset;
+
+    if (data == NULL || len == 0) {
+        return MT25QL_ERR_WRONG_ARGUMENT;
+    }
+
+    if ((addr + len) >= dev->size) {
+        return MT25QL_ERR_ADDR_TOO_BIG;
+    }
+
+    /*
+     * If the remapping address is not aligned on a 32 bits boundary, a direct
+     * access of one word could cross a Flash page boundary. If that happens,
+     * the bytes of that word that are over the page boundary will instead be
+     * written at the beginning of the same page.
+     * To counter this problem, we align the remapping address and add the word
+     * offset to the address of the direct access for the first window only.
+     */
+    word_offset = addr % BYTES_PER_WORD;
+    /* Make address aligned on a 32 bits alignment. */
+    addr -= word_offset;
+    /*
+     * Only direct_access_window_size address locations are available by direct
+     * access. We calculate the number of windows that we will need to transfer
+     * len bytes. We have to add in the window the offset that we add in the
+     * beginning.
+     */
+    window_number = (len + word_offset) / direct_access_window_size;
+
+    /*
+     * This function assumes that the flash has already been erased.
+     * Transfer the bytes for the window_number windows first.
+     */
+    for (uint32_t window = 0; window < window_number; window++) {
+        /* The controller needs to be disabled while remapping is done. */
+        qspi_ip6514e_remap_addr(dev->controller, addr);
+
+        /*
+         * The AHB address to access the Flash memory does not change but it
+         * will be translated differently thanks to the remap function.
+         */
+        memcpy((void *)(dev->direct_access_start_addr + word_offset),
+               data,
+               direct_access_window_size - word_offset);
+
+        len -= (direct_access_window_size - word_offset);
+        data = (void *)((uint32_t)data +
+                        (direct_access_window_size - word_offset));
+        addr += direct_access_window_size;
+
+        /*
+         * The address is now aligned, there is no need to add an offset for the
+         * remaining windows.
+         */
+        word_offset = 0;
+
+        /*
+         * Wait until the last program operation is complete before changing
+         * the remap address.
+         */
+        library_error = wait_program_or_erase_complete(dev);
+        if (library_error != MT25QL_ERR_NONE) {
+            return library_error;
+        }
+    }
+
+    if (len) {
+        /* Transfer the reminder bytes */
+        qspi_ip6514e_remap_addr(dev->controller, addr);
+
+        memcpy((void *)(dev->direct_access_start_addr + word_offset),
+               data,
+               len);
+
+        /* Wait until the last program operation is complete */
+        library_error = wait_program_or_erase_complete(dev);
+        if (library_error != MT25QL_ERR_NONE) {
+            return library_error;
+        }
+    }
+
+    /*
+     * Disable the default remap address for direct accesses outside of this
+     * function.
+     */
+    qspi_ip6514e_disable_remap(dev->controller);
+
+    return MT25QL_ERR_NONE;
+}
+
+enum mt25ql_error_t mt25ql_command_read(struct mt25ql_dev_t* dev,
+                                        uint32_t addr,
+                                        void *data,
+                                        uint32_t len)
+{
+    /* With one single command only 8 bytes can be read. */
+    uint32_t cmd_number = len / CMD_DATA_MAX_SIZE;
+    enum qspi_ip6514e_error_t controller_error;
+    uint8_t opcode;
+    uint32_t dummy_cycles;
+
+    if(dev->func_state == MT25QL_FUNC_STATE_OPTIMAL) {
+        opcode = QUAD_OUTPUT_FAST_READ_CMD;
+        dummy_cycles = OPTIMAL_READ_DUMMY_CYCLES;
+    } else {
+        opcode = READ_CMD;
+        dummy_cycles = CMD_READ_DUMMY_CYCLES;
+    }
+
+    for (uint32_t cmd_index = 0; cmd_index < cmd_number; cmd_index++) {
+        controller_error = qspi_ip6514e_send_read_cmd(
+                                                    dev->controller,
+                                                    opcode,
+                                                    data,
+                                                    CMD_DATA_MAX_SIZE,
+                                                    addr,
+                                                    ADDR_BYTES,
+                                                    dummy_cycles);
+        if (controller_error != QSPI_IP6514E_ERR_NONE) {
+            return (enum mt25ql_error_t)controller_error;
+        }
+
+        data = (void *)((uint32_t)data + CMD_DATA_MAX_SIZE);
+        addr += CMD_DATA_MAX_SIZE;
+        len -= CMD_DATA_MAX_SIZE;
+    }
+
+    if (len) {
+        /* Read the remainder. */
+        controller_error = qspi_ip6514e_send_read_cmd(
+                                                     dev->controller,
+                                                     opcode,
+                                                     data,
+                                                     len,
+                                                     addr,
+                                                     ADDR_BYTES,
+                                                     dummy_cycles);
+        if (controller_error != QSPI_IP6514E_ERR_NONE) {
+            return (enum mt25ql_error_t)controller_error;
+        }
+    }
+
+    return MT25QL_ERR_NONE;
+}
+
+enum mt25ql_error_t mt25ql_command_write(struct mt25ql_dev_t* dev,
+                                         uint32_t addr,
+                                         const void *data,
+                                         uint32_t len)
+{
+    /* With one single command only 8 bytes can be written. */
+    uint32_t cmd_number = len / CMD_DATA_MAX_SIZE;
+    enum qspi_ip6514e_error_t controller_error;
+    enum mt25ql_error_t library_error;
+    uint8_t opcode;
+    uint32_t dummy_cycles;
+
+    if(dev->func_state == MT25QL_FUNC_STATE_OPTIMAL) {
+        opcode = QUAD_INPUT_FAST_PROGRAM_CMD;
+        dummy_cycles = OPTIMAL_WRITE_DUMMY_CYCLES;
+    } else {
+        opcode = PAGE_PROGRAM_CMD;
+        dummy_cycles = CMD_WRITE_DUMMY_CYCLES;
+    }
+
+    for (uint32_t cmd_index = 0; cmd_index < cmd_number; cmd_index++) {
+        send_write_enable(dev);
+
+        /*
+         * Check if this command is not writing over a page boundary: first and
+         * last bytes are in the same page.
+         */
+        if ((addr / FLASH_PAGE_SIZE) !=
+            ((addr + CMD_DATA_MAX_SIZE - 1) / FLASH_PAGE_SIZE)) {
+            /* The CMD_DATA_MAX_SIZE bytes written are crossing the boundary. */
+            library_error = send_boundary_cross_write_cmd(
+                                                    dev,
+                                                    opcode,
+                                                    data,
+                                                    CMD_DATA_MAX_SIZE,
+                                                    addr,
+                                                    ADDR_BYTES,
+                                                    dummy_cycles);
+            if (library_error != MT25QL_ERR_NONE) {
+                return library_error;
+            }
+        } else {
+            /* Normal case: not crossing the boundary. */
+            controller_error = qspi_ip6514e_send_write_cmd(
+                                                    dev->controller,
+                                                    opcode,
+                                                    data,
+                                                    CMD_DATA_MAX_SIZE,
+                                                    addr,
+                                                    ADDR_BYTES,
+                                                    dummy_cycles);
+            if (controller_error != QSPI_IP6514E_ERR_NONE) {
+                return (enum mt25ql_error_t)controller_error;
+            }
+        }
+
+        /* Wait until the write operation is complete. */
+        library_error = wait_program_or_erase_complete(dev);
+        if (library_error != MT25QL_ERR_NONE) {
+            return library_error;
+        }
+
+        data = (void *)((uint32_t)data + CMD_DATA_MAX_SIZE);
+        addr += CMD_DATA_MAX_SIZE;
+        len -= CMD_DATA_MAX_SIZE;
+    }
+
+    if (len) {
+        /* Write the remainder. */
+        send_write_enable(dev);
+        /*
+         * Check if this command is not writing over a page boundary: first and
+         * last bytes are in the same page.
+         */
+        if ((addr / FLASH_PAGE_SIZE) != ((addr + len - 1) / FLASH_PAGE_SIZE)) {
+            /* The CMD_DATA_MAX_SIZE bytes written are crossing the boundary. */
+            library_error = send_boundary_cross_write_cmd(
+                                                    dev,
+                                                    opcode,
+                                                    data,
+                                                    len,
+                                                    addr,
+                                                    ADDR_BYTES,
+                                                    dummy_cycles);
+            if (library_error != MT25QL_ERR_NONE) {
+                return library_error;
+            }
+        } else {
+            /* Normal case: not crossing the boundary. */
+            controller_error = qspi_ip6514e_send_write_cmd(
+                                                    dev->controller,
+                                                    opcode,
+                                                    data,
+                                                    len,
+                                                    addr,
+                                                    ADDR_BYTES,
+                                                    dummy_cycles);
+            if (controller_error != QSPI_IP6514E_ERR_NONE) {
+                return (enum mt25ql_error_t)controller_error;
+            }
+        }
+
+        /* Wait until the write operation is complete. */
+        library_error = wait_program_or_erase_complete(dev);
+        if (library_error != MT25QL_ERR_NONE) {
+            return library_error;
+        }
+    }
+
+
+    return MT25QL_ERR_NONE;
+}
+
+enum mt25ql_error_t mt25ql_erase(struct mt25ql_dev_t* dev,
+                                 uint32_t addr,
+                                 enum mt25ql_erase_t erase_type)
+{
+    enum qspi_ip6514e_error_t controller_error;
+    enum mt25ql_error_t library_error;
+    uint8_t erase_cmd;
+    uint32_t addr_bytes;
+
+    send_write_enable(dev);
+
+    switch (erase_type) {
+        case MT25QL_ERASE_ALL_FLASH:
+            if (addr != 0) {
+                return MT25QL_ERR_ADDR_NOT_ALIGNED;
+            }
+            erase_cmd = BULK_ERASE_CMD;
+            addr_bytes = ARG_NOT_USED;
+            break;
+        case MT25QL_ERASE_SECTOR_64K:
+            erase_cmd = SECTOR_ERASE_CMD;
+            addr_bytes = ADDR_BYTES;
+            if ((addr % SECTOR_64KB) != 0) {
+                return MT25QL_ERR_ADDR_NOT_ALIGNED;
+            }
+            break;
+        case MT25QL_ERASE_SUBSECTOR_32K:
+            erase_cmd = SUBSECTOR_ERASE_32KB_CMD;
+            addr_bytes = ADDR_BYTES;
+            if ((addr % SUBSECTOR_32KB) != 0) {
+                return MT25QL_ERR_ADDR_NOT_ALIGNED;
+            }
+            break;
+        case MT25QL_ERASE_SUBSECTOR_4K:
+            erase_cmd = SUBSECTOR_ERASE_4KB_CMD;
+            addr_bytes = ADDR_BYTES;
+            if ((addr % SUBSECTOR_4KB) != 0) {
+                return MT25QL_ERR_ADDR_NOT_ALIGNED;
+            }
+            break;
+        default:
+            return MT25QL_ERR_WRONG_ARGUMENT;
+    }
+
+    if (addr >= dev->size) {
+        return MT25QL_ERR_ADDR_TOO_BIG;
+    }
+
+    controller_error = qspi_ip6514e_send_cmd(dev->controller,
+                                             erase_cmd,
+                                             ARG_PTR_NOT_USED,
+                                             ARG_NOT_USED,
+                                             ARG_PTR_NOT_USED,
+                                             ARG_NOT_USED,
+                                             addr,
+                                             addr_bytes,
+                                             0); /* No dummy cycles needed for
+                                                    any erase command. */
+    if (controller_error != QSPI_IP6514E_ERR_NONE) {
+        return (enum mt25ql_error_t)controller_error;
+    }
+
+    /* Wait until the erase operation is complete */
+    library_error = wait_program_or_erase_complete(dev);
+    if (library_error != MT25QL_ERR_NONE) {
+         return (enum mt25ql_error_t)controller_error;
+    }
+
+    return MT25QL_ERR_NONE;
+}
diff --git a/platform/ext/target/musca_a/Libraries/mt25ql_flash_lib.h b/platform/ext/target/musca_a/Libraries/mt25ql_flash_lib.h
new file mode 100644
index 0000000..d4827bf
--- /dev/null
+++ b/platform/ext/target/musca_a/Libraries/mt25ql_flash_lib.h
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2018 Arm Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This library provides functions to control the MT25QL256ABA-1EW7-OSIT flash
+ * memory from Micron and should work for similar devices from the same vendor.
+ */
+
+#ifndef __MT25QL_H__
+#define __MT25QL_H__
+
+#include "Native_Driver/qspi_ip6514e_drv.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief MT25QL Flash Memory documentation defined values.
+ */
+#define FLASH_PAGE_SIZE     (256U)          /* 256B */
+#define SUBSECTOR_4KB       (0x00001000U)   /* 4KB */
+#define SUBSECTOR_32KB      (0x00008000U)   /* 32KB */
+#define SECTOR_64KB         (0x00010000U)   /* 64KB */
+#define ADDR_BYTES          (3U)
+
+enum mt25ql_error_t {
+    MT25QL_ERR_NONE               = QSPI_IP6514E_ERR_NONE,
+    MT25QL_ERR_WRONG_ARGUMENT     = QSPI_IP6514E_ERR_WRONG_ARGUMENT,
+    MT25QL_ERR_CTRL_NOT_DISABLED  = QSPI_IP6514E_ERR_CONTROLLER_NOT_DISABLED,
+    MT25QL_ERR_CTRL_NOT_IDLE      = QSPI_IP6514E_ERR_CONTROLLER_NOT_IDLE,
+    MT25QL_ERR_READ_IN_PROGRESS   = QSPI_IP6514E_ERR_READ_IN_PROGRESS,
+    MT25QL_ERR_WRITE_IN_PROGRESS  = QSPI_IP6514E_ERR_WRITE_IN_PROGRESS,
+    MT25QL_ERR_ADDR_NOT_ALIGNED,
+    MT25QL_ERR_ADDR_TOO_BIG,
+};
+
+enum mt25ql_erase_t {
+    MT25QL_ERASE_ALL_FLASH     = 0U,             /*!< Erase all flash */
+    MT25QL_ERASE_SUBSECTOR_4K  = SUBSECTOR_4KB,  /*!< Erase a 4 KB subsector */
+    MT25QL_ERASE_SUBSECTOR_32K = SUBSECTOR_32KB, /*!< Erase a 32 KB subsector */
+    MT25QL_ERASE_SECTOR_64K    = SECTOR_64KB,    /*!< Erase a sector (64 KB) */
+};
+
+enum mt25ql_functional_state_t {
+    MT25QL_FUNC_STATE_DEFAULT   = 0u,
+    MT25QL_FUNC_STATE_OPTIMAL   = 1u,
+};
+
+struct mt25ql_dev_t {
+    struct qspi_ip6514e_dev_t *controller;
+        /*!< QSPI Flash controller. */
+    uint32_t direct_access_start_addr;
+        /*!< AHB address to directly access the contents of the Flash memory
+         *   through the QSPI Controller.
+         */
+    uint32_t baud_rate_div;
+        /*!< Clock divisor that will be used to configure the QSPI Flash
+         *   Controller to access the Flash memory. The clock which frequency is
+         *   divived is the one linked to the QSPI Flash controller. It can only
+         *   be an even number between 2 and 32 (both included). It needs to be
+         *   high enough to support the Quad Output Fast Read command with 8
+         *   dummy cycles and the Quad Input Fast Program with 0 dummy cycles.
+         */
+    uint32_t size; /*!< Total size of the MT25QL Flash memory */
+    enum mt25ql_functional_state_t func_state;
+        /*!< Functional state (operational parameter settings) of the
+         *   QSPI Flash controller and memory.
+         */
+};
+
+/**
+ * \brief Configure the QSPI Flash controller and MT25QL for optimal use.
+ *
+ * \param[in] dev     Pointer to MT25QL device structure \ref mt25ql_dev_t
+ *
+ * \return Return error code as specified in \ref mt25ql_error_t
+ *
+ * \note This function assumes that the Flash memory device currently operates
+ *       with single line SPI protocol with Double Data Rate protocol disabled.
+ *       This function will not work and cause harm if the Flash device is in
+ *       another configuration.
+ * \note The configuration used is the following:
+ *         * QSPI mode (4 lines for instruction, address and data)
+ *         * Read command is Quad Output Fast Read with 8 dummy cycles
+ *         * Write command is Quad Input Fast Program with 0 dummy cycles
+ *         * Bytes per page set to 256
+ *         * Number of address bytes set to 3
+ */
+enum mt25ql_error_t mt25ql_cfg_optimal(struct mt25ql_dev_t* dev);
+
+/**
+ * \brief Read bytes from the flash memory (direct access)
+ *
+ * \param[in]  dev   Pointer to MT25QL device structure \ref mt25ql_dev_t
+ * \param[in]  addr  Flash memory address for the read operation
+ * \param[out] data  Pointer where len bytes read from the flash memory will be
+ *                   written to
+ * \param[in]  len   Number of bytes to read
+ *
+ * \return Return error code as specified in \ref mt25ql_error_t
+ *
+ * \note This function will use direct access to read from the Flash memory. It
+ *       can be used to access above the direct accessible memory zone if
+ *       not all the AHB address wires are connected.
+ * \note The address given should be the address of the data inside the flash
+ *       memory. To read the first byte inside the memory, use 0x00000000.
+ */
+enum mt25ql_error_t mt25ql_direct_read(struct mt25ql_dev_t* dev,
+                                       uint32_t addr,
+                                       void *data,
+                                       uint32_t len);
+
+/**
+ * \brief Write bytes in the flash memory, at a location where data has already
+ *        been erased (direct access)
+ *
+ * \param[in] dev   Pointer to MT25QL device structure \ref mt25ql_dev_t
+ * \param[in] addr  Flash memory address for the write operation
+ * \param[in] data  Pointer to the len bytes that will be written to the flash
+ *                  memory
+ * \param[in] len   Number of bytes to write
+ *
+ * \return Return error code as specified in \ref mt25ql_error_t
+ *
+ * \note This function will use direct access to write to the Flash memory. It
+ *       can be used to access outside of the direct accessible memory zone if
+ *       not all the AHB address wires are connected.
+ * \note The address given should be the address of the data inside the flash
+ *       memory. To write the first byte inside the memory, use 0x00000000.
+ * \note Writing bytes in the flash memory clear them from 1 to 0, for that
+ *       matter the location where data is written needs to be erased
+ *       beforehand.
+ */
+enum mt25ql_error_t mt25ql_direct_write(struct mt25ql_dev_t* dev,
+                                        uint32_t addr,
+                                        const void *data,
+                                        uint32_t len);
+
+/**
+ * \brief Read bytes from the flash memory (using Flash commands)
+ *
+ * \param[in]  dev   Pointer to MT25QL device structure \ref mt25ql_dev_t
+ * \param[in]  addr  Flash memory address for the read operation
+ * \param[out] data  Pointer where len bytes read from the flash memory will be
+ *                   written to
+ * \param[in]  len   Number of bytes to read
+ *
+ * \return Return error code as specified in \ref mt25ql_error_t
+ *
+ * \note This function will use the Software Triggered Instruction Generator to
+ *       read from the Flash memory using Flash commands.
+ * \note The address given should be the address of the data inside the flash
+ *       memory. To read the first byte inside the memory, use 0x00000000.
+ */
+enum mt25ql_error_t mt25ql_command_read(struct mt25ql_dev_t* dev,
+                                        uint32_t addr,
+                                        void *data,
+                                        uint32_t len);
+
+/**
+ * \brief Write bytes in the flash memory, at a location where data has already
+ *        been erased (using Flash commands)
+ *
+ * \param[in] dev   Pointer to MT25QL device structure \ref mt25ql_dev_t
+ * \param[in] addr  Flash memory address for the write operation
+ * \param[in] data  Pointer to the len bytes that will be written to the flash
+ *                  memory
+ * \param[in] len   Number of bytes to write
+ *
+ * \return Return error code as specified in \ref mt25ql_error_t
+ *
+ * \note This function will use the Software Triggered Instruction Generator to
+ *       write to the Flash memory using Flash commands.
+ * \note The address given should be the address of the data inside the flash
+ *       memory. To write the first byte inside the memory, use 0x00000000.
+ * \note Writing bytes in the flash memory clear them from 1 to 0, for that
+ *       matter the location where data is written needs to be erased
+ *       beforehand.
+ */
+enum mt25ql_error_t mt25ql_command_write(struct mt25ql_dev_t* dev,
+                                         uint32_t addr,
+                                         const void *data,
+                                         uint32_t len);
+
+/**
+ * \brief Erase all flash memory, a sector (64 KiB) or a subsector
+ *        (32 KiB or 4 KiB)
+ *
+ * \param[in] dev        Pointer to MT25QL device structure \ref mt25ql_dev_t
+ * \param[in] addr       Address where to erase in the flash memory
+ * \param[in] erase_type Type of what to erase at the specified address:
+ *                         * whole flash memory
+ *                         * a subsector (4 KiB or 32 KiB)
+ *                         * a sector (64 KiB)
+ * \return Return error code as specified in \ref mt25ql_error_t
+ *
+ * \note The address need to be aligned with the size of what is erased or 0 if
+ *       all flash memory is to be erased.
+ */
+enum mt25ql_error_t mt25ql_erase(struct mt25ql_dev_t* dev,
+                                 uint32_t addr,
+                                 enum mt25ql_erase_t erase_type);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MT25QL_H__ */
diff --git a/platform/ext/target/musca_a/Native_Driver/qspi_ip6514e_drv.c b/platform/ext/target/musca_a/Native_Driver/qspi_ip6514e_drv.c
index 5ce569c..8b9f541 100644
--- a/platform/ext/target/musca_a/Native_Driver/qspi_ip6514e_drv.c
+++ b/platform/ext/target/musca_a/Native_Driver/qspi_ip6514e_drv.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018 ARM Limited
+ * Copyright (c) 2018 Arm Limited
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15,25 +15,688 @@
  */
 
 #include <stdint.h>
+#include <stdlib.h>
+#include <stdbool.h>
+/* Use memcpy */
+#include <string.h>
+
 #include "qspi_ip6514e_drv.h"
 
-struct _qspi_ip6514e_reg_t {
+/** Setter bit manipulation macro */
+#define SET_BIT(WORD, BIT_INDEX) ((WORD) |= (1U << (BIT_INDEX)))
+/** Clearing bit manipulation macro */
+#define CLR_BIT(WORD, BIT_INDEX) ((WORD) &= ~(1U << (BIT_INDEX)))
+/** Getter bit manipulation macro */
+#define GET_BIT(WORD, BIT_INDEX) (bool)(((WORD) & (1U << (BIT_INDEX))))
+
+#define WORD_ALIGN_4B_MASK    0x3U /* Mask the first 2 bits */
+#define IS_ADDR_ALIGNED(ADDR) (((uint32_t)(ADDR) & (WORD_ALIGN_4B_MASK)) == 0U)
+
+#define BITS_PER_BYTE               8U
+#define BITS_PER_WORD               32U
+
+#define CFG_READS  true
+#define CFG_WRITES false
+
+#define ARG_NOT_USED     0
+#define ARG_PTR_NOT_USED NULL
+
+#define DATA_REG_NUMBER 2U
+#define DATA_REG_LOWER  0U
+#define DATA_REG_UPPER  1U
+
+#define ERROR_VALUE 0xFFFFFFFFU
+
+/**
+ * \brief QSPI IP6514E register map structure
+ */
+struct _qspi_ip6514e_reg_map_t {
     volatile uint32_t qspi_cfg;                           /*!< 0x00 (R/W) */
-    volatile uint32_t hidden1[8];
+    volatile uint32_t device_read_inst;                   /*!< 0x04 (R/W) */
+    volatile uint32_t device_write_inst;                  /*!< 0x08 (R/W) */
+    volatile uint32_t hidden1[2];
+    volatile uint32_t device_size;                        /*!< 0x14 (R/W) */
+    volatile uint32_t hidden2[3];
     volatile uint32_t remap_addr;                         /*!< 0x24 (R/W) */
+    volatile uint32_t hidden3[26];
+    volatile uint32_t flash_cmd_ctrl;                     /*!< 0x90 (R/W) */
+    volatile uint32_t flash_cmd_addr;                     /*!< 0x94 (R/W) */
+    volatile uint32_t hidden4[2];
+    volatile uint32_t flash_cmd_read_data_lower;          /*!< 0xA0 (R/ ) */
+    volatile uint32_t flash_cmd_read_data_upper;          /*!< 0xA4 (R/ ) */
+    volatile uint32_t flash_cmd_write_data_lower;         /*!< 0xA8 (R/W) */
+    volatile uint32_t flash_cmd_write_data_upper;         /*!< 0xAC (R/W) */
+    volatile uint32_t hidden5[2];
 };
 
-#define  QSPI_CONTROLLER ((volatile struct _qspi_ip6514e_reg_t *) \
-         QSPI_CONTROLLER_ADDRESS)
+/** QSPI Configuration register description (offset 0x00) */
+#define QSPI_CFG_ENABLE_POS            0U
+#define QSPI_CFG_ENABLE_ADDR_REMAP_POS 16U
+#define QSPI_CFG_BAUD_DIV_POS          19U
+    #define QSPI_CFG_BAUD_DIV_MIN          2U
+    #define QSPI_CFG_BAUD_DIV_MAX          32U
+    #define QSPI_CFG_BAUD_DIV_BITS         4U
+#define QSPI_CFG_IDLE_POS              31U
 
-void qspi_remap_base(uint32_t new_addr)
+/**
+ * Device Read/Write Instruction registers description (offset 0x04 and 0x08).
+ * These values are the same for the Device Read Instruction register at offset
+ * 0x04 and the Device Write Instruction register at offset 0x08.
+ */
+#define DEVICE_READ_WRITE_INST_OPCODE_POS        0U
+#define DEVICE_READ_INST_INST_TYPE_POS           8U /* Only applies to the Read
+                                                     * register. */
+#define DEVICE_READ_WRITE_INST_ADDR_TYPE_POS     12U
+#define DEVICE_READ_WRITE_INST_DATA_TYPE_POS     16U
+    #define DEVICE_READ_WRITE_INST_MODE_QSPI         2U
+    #define DEVICE_READ_WRITE_INST_MODE_DSPI         1U
+    #define DEVICE_READ_WRITE_INST_MODE_SPI          0U
+    #define DEVICE_READ_WRITE_INST_MODE_BITS         2U
+#define DEVICE_READ_WRITE_INST_DUMMY_CYCLES_POS  24U
+    #define DEVICE_READ_WRITE_INST_DUMMY_CYCLES_BITS 5U
+    #define DEVICE_READ_WRITE_INST_DUMMY_CYCLES_MAX  31U
+
+/** Device Size Configuration register description (offset 0x14) */
+#define DEVICE_SIZE_ADDR_BYTES_POS  0U
+    #define DEVICE_SIZE_ADDR_BYTES_MIN  1U
+    #define DEVICE_SIZE_ADDR_BYTES_MAX  16U
+    #define DEVICE_SIZE_ADDR_BYTES_BITS 4U
+#define DEVICE_SIZE_PAGE_BYTES_POS  4U
+    #define DEVICE_SIZE_PAGE_BYTES_MAX  4095U
+    #define DEVICE_SIZE_PAGE_BYTES_BITS 12U
+
+/** Flash Command Control register description (offset 0x90) */
+#define FLASH_CMD_CTRL_EXECUTE_POS        0U
+#define FLASH_CMD_CTRL_BUSY_POS           1U
+#define FLASH_CMD_CTRL_DUMMY_CYCLES_POS   7U
+    #define FLASH_CMD_CTRL_DUMMY_CYCLES_MAX   31U
+    #define FLASH_CMD_CTRL_DUMMY_CYCLES_BITS  5U
+#define FLASH_CMD_CTRL_WRITE_BYTES_POS    12U
+    #define FLASH_CMD_CTRL_WRITE_BYTES_MAX    8U
+    #define FLASH_CMD_CTRL_WRITE_BYTES_BITS   3U
+#define FLASH_CMD_CTRL_WRITE_ENABLE_POS   15U
+#define FLASH_CMD_CTRL_ADDR_BYTES_POS     16U
+    #define FLASH_CMD_CTRL_ADDR_BYTES_MAX     4U
+    #define FLASH_CMD_CTRL_ADDR_BYTES_BITS    2U
+#define FLASH_CMD_CTRL_ADDR_ENABLE_POS    19U
+#define FLASH_CMD_CTRL_READ_BYTES_POS     20U
+    #define FLASH_CMD_CTRL_READ_BYTES_MAX     8U
+    #define FLASH_CMD_CTRL_READ_BYTES_BITS    3U
+#define FLASH_CMD_CTRL_READ_ENABLE_POS    23U
+#define FLASH_CMD_CTRL_OPCODE_POS         24U
+
+/**
+ * \brief Change specific bits in a 32 bits word.
+ *
+ * \param[in,out] word         Pointer of the word to change
+ * \param[in]     bits         bits_length bits to put at bits_pos in the word
+ *                             pointed
+ * \param[in]     bits_length  Number of bits to change
+ * \param[in]     bits_pos     Position of the bits to change
+ *
+ * \note This function will do nothing if the parameters given are incorret:
+ *         * word is NULL
+ *         * bits_length + bits_pos > 32
+ *         * bits_length is 0
+ */
+static void change_bits_in_word(volatile uint32_t *word,
+                                uint32_t bits,
+                                uint32_t bits_length,
+                                uint32_t bits_pos)
 {
-    __DSB();
-    __ISB();
-    QSPI_CONTROLLER->qspi_cfg &= ~1U;
-    QSPI_CONTROLLER->remap_addr = new_addr;
-    QSPI_CONTROLLER->qspi_cfg |= (1U << 16);
-    QSPI_CONTROLLER->qspi_cfg |= 1U;
-    __DSB();
-    __ISB();
-}
\ No newline at end of file
+    uint32_t mask;
+
+    if ((word == NULL) ||
+        ((bits_length + bits_pos) > BITS_PER_WORD) ||
+        (bits_length == 0U)) {
+        /* Silently fail */
+        return;
+    }
+
+    /* Change all the bits */
+    if (bits_length == BITS_PER_WORD) {
+        *word = bits;
+        return;
+    }
+
+    mask = ((1U << bits_length) - 1);
+    /*
+     * We change the bits in three steps:
+     *   - clear bits_length bits with zeroes at bits_pos in the word
+     *   - mask bits in case it contains more than bits_length bits
+     *   - set the new bits in the cleared word
+     * Because the data pointed by word is only read once, the data will still
+     * be coherent after an interruption that changes it.
+     */
+    *word = ((*word & ~(mask << bits_pos)) | ((bits & mask) << bits_pos));
+}
+
+/**
+ * \brief Configure reads or writes commands for direct operations.
+ *
+ * \param[in] dev             QSPI IP6514E device struct \ref qspi_ip6514e_dev_t
+ * \param[in] opcode          Read/write opcode that will be used for every
+ *                            direct read/write
+ * \param[in] dummy_cycles    Number of dummy cycles to wait before triggering
+ *                            the command, this value must be between 0 and 31
+ *                            (both included)
+ * \param[in] is_reads_cfg    true to configure direct reads, false to configure
+ *                            direct writes
+ *
+ * \return Returns error code as specified in \ref qspi_ip6514e_error_t
+ *
+ * \note The QSPI controller should be idle before calling this function.
+ */
+static enum qspi_ip6514e_error_t qspi_ip6514e_cfg_reads_writes(
+                                                 struct qspi_ip6514e_dev_t* dev,
+                                                 uint8_t opcode,
+                                                 uint32_t dummy_cycles,
+                                                 bool is_reads_cfg)
+{
+    struct _qspi_ip6514e_reg_map_t *reg_map =
+                               (struct _qspi_ip6514e_reg_map_t *)dev->cfg->base;
+    /*
+     * Select the good register address if we want to configure reads or writes.
+     */
+    volatile uint32_t *device_read_write_inst_reg = is_reads_cfg ?
+                                                  &(reg_map->device_read_inst) :
+                                                  &(reg_map->device_write_inst);
+    uint32_t device_read_write_inst_reg_copy = *device_read_write_inst_reg;
+
+    if (!qspi_ip6514e_is_idle(dev)) {
+        return QSPI_IP6514E_ERR_CONTROLLER_NOT_IDLE;
+    }
+
+    if (dummy_cycles > DEVICE_READ_WRITE_INST_DUMMY_CYCLES_MAX) {
+        return QSPI_IP6514E_ERR_WRONG_ARGUMENT;
+    }
+
+    change_bits_in_word(&device_read_write_inst_reg_copy,
+                        (uint32_t)opcode,
+                        BITS_PER_BYTE,
+                        DEVICE_READ_WRITE_INST_OPCODE_POS);
+    change_bits_in_word(&device_read_write_inst_reg_copy,
+                        dummy_cycles,
+                        DEVICE_READ_WRITE_INST_DUMMY_CYCLES_BITS,
+                        DEVICE_READ_WRITE_INST_DUMMY_CYCLES_POS);
+
+    *device_read_write_inst_reg = device_read_write_inst_reg_copy;
+
+    return QSPI_IP6514E_ERR_NONE;
+}
+
+/**
+ * \brief Given the public SPI mode enumeration, returns the private value it
+ *        maps to in the register field.
+ *
+ * \param[in] spi_mode Read/write opcode that will be used for every direct
+ *            read/write
+ *
+ * \return Return the correct DEVICE_READ_WRITE_INST_MODE value.
+ */
+static uint32_t spi_mode_field_value(enum qspi_ip6514e_spi_mode_t spi_mode)
+{
+    switch (spi_mode) {
+        case QSPI_IP6514E_SPI_MODE:
+            return DEVICE_READ_WRITE_INST_MODE_SPI;
+        case QSPI_IP6514E_DSPI_MODE:
+            return DEVICE_READ_WRITE_INST_MODE_DSPI;
+        case QSPI_IP6514E_QSPI_MODE:
+            return DEVICE_READ_WRITE_INST_MODE_QSPI;
+        default:
+            return ERROR_VALUE;
+    }
+}
+
+bool qspi_ip6514e_is_idle(struct qspi_ip6514e_dev_t* dev)
+{
+    struct _qspi_ip6514e_reg_map_t *reg_map =
+                               (struct _qspi_ip6514e_reg_map_t *)dev->cfg->base;
+
+    return GET_BIT(reg_map->qspi_cfg, QSPI_CFG_IDLE_POS);
+}
+
+bool qspi_ip6514e_is_enabled(struct qspi_ip6514e_dev_t* dev)
+{
+    struct _qspi_ip6514e_reg_map_t *reg_map =
+                               (struct _qspi_ip6514e_reg_map_t *)dev->cfg->base;
+
+    return GET_BIT(reg_map->qspi_cfg, QSPI_CFG_ENABLE_POS);
+}
+
+void qspi_ip6514e_disable(struct qspi_ip6514e_dev_t* dev)
+{
+    struct _qspi_ip6514e_reg_map_t *reg_map =
+                               (struct _qspi_ip6514e_reg_map_t *)dev->cfg->base;
+
+    CLR_BIT(reg_map->qspi_cfg, QSPI_CFG_ENABLE_POS);
+}
+
+void qspi_ip6514e_enable(struct qspi_ip6514e_dev_t* dev)
+{
+    struct _qspi_ip6514e_reg_map_t *reg_map =
+                               (struct _qspi_ip6514e_reg_map_t *)dev->cfg->base;
+
+    SET_BIT(reg_map->qspi_cfg, QSPI_CFG_ENABLE_POS);
+}
+
+enum qspi_ip6514e_error_t qspi_ip6514e_set_baud_rate_div(
+                                                 struct qspi_ip6514e_dev_t* dev,
+                                                 uint32_t div)
+{
+    struct _qspi_ip6514e_reg_map_t *reg_map =
+                               (struct _qspi_ip6514e_reg_map_t *)dev->cfg->base;
+
+    if (!qspi_ip6514e_is_idle(dev)) {
+        return QSPI_IP6514E_ERR_CONTROLLER_NOT_IDLE;
+    }
+
+    /* div should be an even number. */
+    if (((div & 1U) == 1) ||
+        (div < QSPI_CFG_BAUD_DIV_MIN) ||
+        (div > QSPI_CFG_BAUD_DIV_MAX)) {
+        return QSPI_IP6514E_ERR_WRONG_ARGUMENT;
+    }
+
+    /*
+     * The div value (between 2 and 32) needs to be stored in the register on a
+     * 4 bits field.
+     */
+    change_bits_in_word(&(reg_map->qspi_cfg),
+                        (div / 2) - 1,
+                        QSPI_CFG_BAUD_DIV_BITS,
+                        QSPI_CFG_BAUD_DIV_POS);
+
+    return QSPI_IP6514E_ERR_NONE;
+}
+
+enum qspi_ip6514e_error_t qspi_ip6514e_set_spi_mode(
+                                         struct qspi_ip6514e_dev_t* dev,
+                                         enum qspi_ip6514e_spi_mode_t inst_type,
+                                         enum qspi_ip6514e_spi_mode_t addr_type,
+                                         enum qspi_ip6514e_spi_mode_t data_type)
+{
+    struct _qspi_ip6514e_reg_map_t *reg_map =
+                               (struct _qspi_ip6514e_reg_map_t *)dev->cfg->base;
+    uint32_t inst_spi_mode, addr_spi_mode, data_spi_mode;
+    /*
+     * A local copy of the Device Read Instruction and Device Write Instruction
+     * registers is used to limit APB accesses.
+     */
+    uint32_t device_read_inst_cpy = reg_map->device_read_inst;
+    uint32_t device_write_inst_cpy = reg_map->device_write_inst;
+
+    if (!qspi_ip6514e_is_idle(dev)) {
+        return QSPI_IP6514E_ERR_CONTROLLER_NOT_IDLE;
+    }
+
+    /*
+     * First check that the instruction mode is not SPI. If that is the case,
+     * the address and data mode register fields become DO NOT CARE.
+     */
+    inst_spi_mode = spi_mode_field_value(inst_type);
+    if (inst_spi_mode == ERROR_VALUE) {
+        return QSPI_IP6514E_ERR_WRONG_ARGUMENT;
+    }
+    if (inst_type != QSPI_IP6514E_SPI_MODE) {
+        change_bits_in_word(&(reg_map->device_read_inst),
+                            inst_spi_mode,
+                            DEVICE_READ_WRITE_INST_MODE_BITS,
+                            DEVICE_READ_INST_INST_TYPE_POS);
+        return QSPI_IP6514E_ERR_NONE;
+    }
+
+    /* Now check and set address and data modes. */
+    addr_spi_mode = spi_mode_field_value(addr_type);
+    data_spi_mode = spi_mode_field_value(data_type);
+    if ((addr_spi_mode == ERROR_VALUE) || (data_spi_mode == ERROR_VALUE)) {
+        return QSPI_IP6514E_ERR_WRONG_ARGUMENT;
+    }
+
+    /* Change the Device Read Instruction register. */
+    change_bits_in_word(&device_read_inst_cpy,
+                        inst_spi_mode,
+                        DEVICE_READ_WRITE_INST_MODE_BITS,
+                        DEVICE_READ_INST_INST_TYPE_POS);
+    change_bits_in_word(&device_read_inst_cpy,
+                        addr_spi_mode,
+                        DEVICE_READ_WRITE_INST_MODE_BITS,
+                        DEVICE_READ_WRITE_INST_ADDR_TYPE_POS);
+    change_bits_in_word(&device_read_inst_cpy,
+                        data_spi_mode,
+                        DEVICE_READ_WRITE_INST_MODE_BITS,
+                        DEVICE_READ_WRITE_INST_DATA_TYPE_POS);
+
+    /* Change the Device Write Instruction register. */
+    change_bits_in_word(&device_write_inst_cpy,
+                        addr_spi_mode,
+                        DEVICE_READ_WRITE_INST_MODE_BITS,
+                        DEVICE_READ_WRITE_INST_ADDR_TYPE_POS);
+    change_bits_in_word(&device_write_inst_cpy,
+                        data_spi_mode,
+                        DEVICE_READ_WRITE_INST_MODE_BITS,
+                        DEVICE_READ_WRITE_INST_DATA_TYPE_POS);
+
+    /* Save the changes. */
+    reg_map->device_read_inst = device_read_inst_cpy;
+    reg_map->device_write_inst = device_write_inst_cpy;
+
+    return QSPI_IP6514E_ERR_NONE;
+}
+
+enum qspi_ip6514e_error_t qspi_ip6514e_cfg_reads(struct qspi_ip6514e_dev_t* dev,
+                                                 uint8_t opcode,
+                                                 uint32_t dummy_cycles)
+{
+    return qspi_ip6514e_cfg_reads_writes(dev, opcode, dummy_cycles, CFG_READS);
+}
+
+enum qspi_ip6514e_error_t qspi_ip6514e_cfg_writes(
+                                                 struct qspi_ip6514e_dev_t* dev,
+                                                 uint8_t opcode,
+                                                 uint32_t dummy_cycles)
+{
+    return qspi_ip6514e_cfg_reads_writes(dev, opcode, dummy_cycles, CFG_WRITES);
+}
+
+enum qspi_ip6514e_error_t qspi_ip6514e_cfg_page_size(
+                                                 struct qspi_ip6514e_dev_t* dev,
+                                                 uint32_t page_size)
+{
+    struct _qspi_ip6514e_reg_map_t *reg_map =
+                               (struct _qspi_ip6514e_reg_map_t *)dev->cfg->base;
+
+    if (!qspi_ip6514e_is_idle(dev)) {
+        return QSPI_IP6514E_ERR_CONTROLLER_NOT_IDLE;
+    }
+
+    if (page_size > DEVICE_SIZE_PAGE_BYTES_MAX) {
+        return QSPI_IP6514E_ERR_WRONG_ARGUMENT;
+    }
+
+    change_bits_in_word(&(reg_map->device_size),
+                        page_size,
+                        DEVICE_SIZE_PAGE_BYTES_BITS,
+                        DEVICE_SIZE_PAGE_BYTES_POS);
+
+    return QSPI_IP6514E_ERR_NONE;
+}
+
+enum qspi_ip6514e_error_t qspi_ip6514e_cfg_addr_bytes(
+                                                 struct qspi_ip6514e_dev_t* dev,
+                                                 uint32_t bytes_number)
+{
+    struct _qspi_ip6514e_reg_map_t *reg_map =
+                               (struct _qspi_ip6514e_reg_map_t *)dev->cfg->base;
+
+    if (!qspi_ip6514e_is_idle(dev)) {
+        return QSPI_IP6514E_ERR_CONTROLLER_NOT_IDLE;
+    }
+
+    if (bytes_number < DEVICE_SIZE_ADDR_BYTES_MIN ||
+        bytes_number > DEVICE_SIZE_ADDR_BYTES_MAX) {
+        return QSPI_IP6514E_ERR_WRONG_ARGUMENT;
+    }
+
+    change_bits_in_word(&(reg_map->device_size),
+                        bytes_number - 1,
+                        DEVICE_SIZE_ADDR_BYTES_BITS,
+                        DEVICE_SIZE_ADDR_BYTES_POS);
+
+
+    return QSPI_IP6514E_ERR_NONE;
+}
+
+void qspi_ip6514e_remap_addr(struct qspi_ip6514e_dev_t* dev, uint32_t offset)
+{
+    struct _qspi_ip6514e_reg_map_t *reg_map =
+                               (struct _qspi_ip6514e_reg_map_t *)dev->cfg->base;
+    /* Save the enable state to restore it after. */
+    bool is_enabled = qspi_ip6514e_is_enabled(dev);
+
+    if (is_enabled) {
+        qspi_ip6514e_disable(dev);
+    }
+
+    reg_map->remap_addr = offset;
+    SET_BIT(reg_map->qspi_cfg, QSPI_CFG_ENABLE_ADDR_REMAP_POS);
+
+    if (is_enabled) {
+        qspi_ip6514e_enable(dev);
+    }
+}
+
+void qspi_ip6514e_disable_remap(struct qspi_ip6514e_dev_t* dev)
+{
+    struct _qspi_ip6514e_reg_map_t *reg_map =
+                               (struct _qspi_ip6514e_reg_map_t *)dev->cfg->base;
+    /* Save the enable state to restore it after. */
+    bool is_enabled = qspi_ip6514e_is_enabled(dev);
+
+    if (is_enabled) {
+        qspi_ip6514e_disable(dev);
+    }
+
+    CLR_BIT(reg_map->qspi_cfg, QSPI_CFG_ENABLE_ADDR_REMAP_POS);
+
+    if (is_enabled) {
+        qspi_ip6514e_enable(dev);
+    }
+}
+
+enum qspi_ip6514e_error_t qspi_ip6514e_send_cmd(struct qspi_ip6514e_dev_t* dev,
+                                                uint8_t opcode,
+                                                void *read_data,
+                                                uint32_t read_len,
+                                                const void *write_data,
+                                                uint32_t write_len,
+                                                uint32_t addr,
+                                                uint32_t addr_bytes_number,
+                                                uint32_t dummy_cycles)
+{
+    struct _qspi_ip6514e_reg_map_t *reg_map =
+                               (struct _qspi_ip6514e_reg_map_t *)dev->cfg->base;
+    /* To limit APB accesses, we set this reg up locally before */
+    uint32_t flash_cmd_ctrl = 0U;
+    bool read_requested = ((read_data != NULL) && (read_len != 0));
+    bool write_requested = ((write_data != NULL) && (write_len != 0));
+    bool addr_requested = (addr_bytes_number != 0);
+    /*
+     * To prevent unaligned and byte or halfbyte accesses to the APB registers,
+     * a word aligned buffer is used to temporary transfer the data before doing
+     * word accesses on these registers from that buffer.
+     */
+    uint32_t data_regs[DATA_REG_NUMBER] = {0};
+
+    if (read_len > FLASH_CMD_CTRL_READ_BYTES_MAX) {
+        return QSPI_IP6514E_ERR_WRONG_ARGUMENT;
+    }
+
+    if (write_len > FLASH_CMD_CTRL_WRITE_BYTES_MAX) {
+        return QSPI_IP6514E_ERR_WRONG_ARGUMENT;
+    }
+
+    if (addr_bytes_number > FLASH_CMD_CTRL_ADDR_BYTES_MAX) {
+        return QSPI_IP6514E_ERR_WRONG_ARGUMENT;
+    }
+
+    if (dummy_cycles > FLASH_CMD_CTRL_DUMMY_CYCLES_MAX) {
+        return QSPI_IP6514E_ERR_WRONG_ARGUMENT;
+    }
+
+    if (read_requested && write_requested) {
+        return QSPI_IP6514E_ERR_WRONG_ARGUMENT;
+    }
+
+    change_bits_in_word(&flash_cmd_ctrl,
+                        (uint32_t)opcode,
+                        BITS_PER_BYTE,
+                        FLASH_CMD_CTRL_OPCODE_POS);
+
+    /* Enable read if requested */
+    if (read_requested) {
+        SET_BIT(flash_cmd_ctrl, FLASH_CMD_CTRL_READ_ENABLE_POS);
+        change_bits_in_word(&flash_cmd_ctrl,
+                            read_len - 1,
+                            FLASH_CMD_CTRL_READ_BYTES_BITS,
+                            FLASH_CMD_CTRL_READ_BYTES_POS);
+    }
+
+    /* Enable write if requested */
+    if (write_requested) {
+        SET_BIT(flash_cmd_ctrl, FLASH_CMD_CTRL_WRITE_ENABLE_POS);
+        change_bits_in_word(&flash_cmd_ctrl,
+                            write_len - 1,
+                            FLASH_CMD_CTRL_WRITE_BYTES_BITS,
+                            FLASH_CMD_CTRL_WRITE_BYTES_POS);
+
+        if (IS_ADDR_ALIGNED(write_data) && IS_ADDR_ALIGNED(write_len)) {
+            /*
+             * Optimised case when write_data is word aligned and write_len is
+             * 4 or 8.
+             */
+            reg_map->flash_cmd_write_data_lower = *(uint32_t *)write_data;
+            if (write_len == FLASH_CMD_CTRL_WRITE_BYTES_MAX) {
+                reg_map->flash_cmd_write_data_upper =
+                                                  *((uint32_t *)write_data + 1);
+            }
+        } else {
+            /*
+             * data_regs is used as a buffer to only do unaligned access on the
+             * AHB bus and word aligned accesses to the APB registers.
+             */
+            memcpy((void *)data_regs, write_data, write_len);
+            /*
+             * Only write_len bytes will be written even if both data registers
+             * are written.
+             */
+            reg_map->flash_cmd_write_data_lower = data_regs[DATA_REG_LOWER];
+            reg_map->flash_cmd_write_data_upper = data_regs[DATA_REG_UPPER];
+        }
+    }
+
+    /* Enable the address if requested */
+    if (addr_requested) {
+        SET_BIT(flash_cmd_ctrl, FLASH_CMD_CTRL_ADDR_ENABLE_POS);
+        reg_map->flash_cmd_addr = addr;
+        change_bits_in_word(&flash_cmd_ctrl,
+                            addr_bytes_number - 1,
+                            FLASH_CMD_CTRL_ADDR_BYTES_BITS,
+                            FLASH_CMD_CTRL_ADDR_BYTES_POS);
+    }
+
+    /* Put dummy cycles number */
+    change_bits_in_word(&flash_cmd_ctrl,
+                        dummy_cycles,
+                        FLASH_CMD_CTRL_DUMMY_CYCLES_BITS,
+                        FLASH_CMD_CTRL_DUMMY_CYCLES_POS);
+
+    /* Copy the Flash Command Control register and execute the command */
+    reg_map->flash_cmd_ctrl = flash_cmd_ctrl;
+    SET_BIT(reg_map->flash_cmd_ctrl, FLASH_CMD_CTRL_EXECUTE_POS);
+
+    /* Wait for termination */
+    while (GET_BIT(reg_map->flash_cmd_ctrl, FLASH_CMD_CTRL_BUSY_POS));
+
+    /*
+     * Recolt the read data if it was requested. read_len validity has already
+     * been verified at this point.
+     */
+    if (read_requested) {
+        if (IS_ADDR_ALIGNED(read_data) && IS_ADDR_ALIGNED(read_len)) {
+            /*
+             * Optimised case when read_data is word aligned and read_len is
+             * 4 or 8.
+             */
+            *(uint32_t *)read_data = reg_map->flash_cmd_read_data_lower;
+            if (read_len == FLASH_CMD_CTRL_READ_BYTES_MAX) {
+                *((uint32_t *)read_data + 1) =
+                                             reg_map->flash_cmd_read_data_upper;
+            }
+        } else {
+            /*
+             * Only read_len bytes have been written even if both data registers
+             * are written.
+             */
+            data_regs[DATA_REG_LOWER] = reg_map->flash_cmd_read_data_lower;
+            data_regs[DATA_REG_UPPER] = reg_map->flash_cmd_read_data_upper;
+            /*
+             * data_regs is used as a buffer to only do unaligned access on the
+             * AHB bus and word aligned accesses to the APB registers.
+             */
+            memcpy(read_data, (void *)data_regs, read_len);
+        }
+    }
+
+    return QSPI_IP6514E_ERR_NONE;
+}
+
+void qspi_ip6514e_send_simple_cmd(struct qspi_ip6514e_dev_t* dev,
+                                      uint8_t opcode)
+{
+    /*
+     * No read/write data, no address, no dummy cycles.
+     * Given the arguments, this function can not fail.
+     */
+    (void)qspi_ip6514e_send_cmd(dev,
+                                opcode,
+                                ARG_PTR_NOT_USED,
+                                ARG_NOT_USED,
+                                ARG_PTR_NOT_USED,
+                                ARG_NOT_USED,
+                                ARG_NOT_USED,
+                                ARG_NOT_USED,
+                                0);
+}
+
+enum qspi_ip6514e_error_t qspi_ip6514e_send_read_cmd(
+                                                 struct qspi_ip6514e_dev_t* dev,
+                                                 uint8_t opcode,
+                                                 void *read_data,
+                                                 uint32_t read_len,
+                                                 uint32_t addr,
+                                                 uint32_t addr_bytes_number,
+                                                 uint32_t dummy_cycles)
+{
+    /* Read arguments are expected */
+    if (read_data == ARG_PTR_NOT_USED || read_len == ARG_NOT_USED) {
+        return QSPI_IP6514E_ERR_WRONG_ARGUMENT;
+    }
+
+    /* No write data */
+    return qspi_ip6514e_send_cmd(dev,
+                                 opcode,
+                                 read_data,
+                                 read_len,
+                                 ARG_PTR_NOT_USED,
+                                 ARG_NOT_USED,
+                                 addr,
+                                 addr_bytes_number,
+                                 dummy_cycles);
+}
+
+enum qspi_ip6514e_error_t qspi_ip6514e_send_write_cmd(
+                                                 struct qspi_ip6514e_dev_t* dev,
+                                                 uint8_t opcode,
+                                                 const void *write_data,
+                                                 uint32_t write_len,
+                                                 uint32_t addr,
+                                                 uint32_t addr_bytes_number,
+                                                 uint32_t dummy_cycles)
+{
+    /* Write arguments are expected */
+    if (write_data == ARG_PTR_NOT_USED || write_len == ARG_NOT_USED) {
+        return QSPI_IP6514E_ERR_WRONG_ARGUMENT;
+    }
+
+    /* No read data, no dummy cycles */
+    return qspi_ip6514e_send_cmd(dev,
+                                 opcode,
+                                 ARG_PTR_NOT_USED,
+                                 ARG_NOT_USED,
+                                 write_data,
+                                 write_len,
+                                 addr,
+                                 addr_bytes_number,
+                                 dummy_cycles);
+}
diff --git a/platform/ext/target/musca_a/Native_Driver/qspi_ip6514e_drv.h b/platform/ext/target/musca_a/Native_Driver/qspi_ip6514e_drv.h
index fb60ece..112b9fa 100644
--- a/platform/ext/target/musca_a/Native_Driver/qspi_ip6514e_drv.h
+++ b/platform/ext/target/musca_a/Native_Driver/qspi_ip6514e_drv.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018 ARM Limited
+ * Copyright (c) 2018 Arm Limited
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,13 +14,395 @@
  * limitations under the License.
  */
 
+/**
+ * \file qspi_ip6514e_drv.h
+ * \brief Driver for Cadence QSPI Flash Controller IP.
+ *        There are two ways to communicate with the flash memory device:
+ *         - issue AHB requests for direct read and writes in the Flash memory
+ *           mapped address zone. The commands used for those can be configured
+ *           by the driver
+ *         - send a command to the device to access his internal registers and
+ *           do other operations like erasing a sector
+ *        At reset, the QSPI controller will work in a default mode which will
+ *        allow to do basic commands. It should be configured with the
+ *        flash memory device specifications for optimal use for commands and
+ *        direct reads/writes. Here is an example of configuration:
+ *         - send command to activate QSPI mode on the flash memory device
+ *         - send command to change dummy cycles on the flash memory device
+ *         - check if any operation is ungoing
+ *         - disable the QSPI controller
+ *         - change the baud rate divisor
+ *         - activate the QSPI mode on the controller
+ *         - change the dummy cycles number and opcode for reads/writes
+ *         - change the number of bytes per page
+ *         - change the number of address bytes
+ *         - activate the QSPI controller
+ *
+ *        Warning: none of the functions declared here check if the dev
+ *        argument points to NULL.
+ */
+
 #ifndef __QSPI_IP6514E_DRV_H__
 #define __QSPI_IP6514E_DRV_H__
 
-#include "platform_retarget.h"
+#include <stdint.h>
+#include <stdbool.h>
 
-#define SPI_FLASH_ACCESS_LENGTH       (0x40000)
+#ifdef __cplusplus
+extern "C" {
+#endif
 
-void qspi_remap_base(uint32_t new_addr);
+/**
+ * \brief Cadence QSPI IP6514E error enumeration types
+ */
+enum qspi_ip6514e_error_t {
+    QSPI_IP6514E_ERR_NONE,
+    QSPI_IP6514E_ERR_WRONG_ARGUMENT,
+    QSPI_IP6514E_ERR_CONTROLLER_NOT_DISABLED,
+    QSPI_IP6514E_ERR_CONTROLLER_NOT_IDLE,
+    QSPI_IP6514E_ERR_READ_IN_PROGRESS,
+    QSPI_IP6514E_ERR_WRITE_IN_PROGRESS,
+    /* Any new error should be added to the enumeration type error of
+     * the corresponding Flash device library as well.
+     */
+};
 
-#endif /* __QSPI_IP6514E_DRV_H__ */
\ No newline at end of file
+/**
+ * \brief Cadence QSPI IP6514E SPI modes
+ */
+enum qspi_ip6514e_spi_mode_t {
+    QSPI_IP6514E_SPI_MODE,
+        /*!< Use 1 line for Instruction, Address and Data */
+    QSPI_IP6514E_DSPI_MODE,
+        /*!< Use 2 lines for Instruction, Address and Data */
+    QSPI_IP6514E_QSPI_MODE,
+        /*!< Use 4 lines for Instruction, Address and Data */
+};
+
+/**
+ * \brief Cadence QSPI IP6514E device configuration structure
+ */
+struct qspi_ip6514e_dev_cfg_t {
+    const uint32_t base; /*!< QSPI IP6514E base address */
+    /*
+     * If not all the AHB wires are connected to the QSPI Flash Controller the
+     * driver can still access all of the Flash memory. The bits of this value
+     * should be put to 1 for every wire that is connected. Set it to
+     * 0xFFFFFFFFU if all AHB address wires are connected to the
+     * QSPI Flash Controller.
+     */
+    uint32_t addr_mask;
+};
+
+/**
+ * \brief Cadence QSPI IP6514E device structure
+ */
+struct qspi_ip6514e_dev_t {
+    const struct qspi_ip6514e_dev_cfg_t* const cfg;
+                                              /*!< QSPI IP6514E configuration */
+};
+
+/**
+ * \brief Check if the controller is idle.
+ *
+ * \param[in] dev QSPI IP6514E device struct \ref qspi_ip6514e_dev_t
+ *
+ * \return true if the controller is idle, false otherwise.
+ */
+bool qspi_ip6514e_is_idle(struct qspi_ip6514e_dev_t* dev);
+
+/**
+ * \brief Check if the controller is enabled.
+ *
+ * \param[in] dev QSPI IP6514E device struct \ref qspi_ip6514e_dev_t
+ *
+ * \return true if the controller is enabled, false otherwise.
+ */
+bool qspi_ip6514e_is_enabled(struct qspi_ip6514e_dev_t* dev);
+
+/**
+ * \brief Disable the QSPI controller.
+ *
+ * \param[in] dev QSPI IP6514E device struct \ref qspi_ip6514e_dev_t
+ */
+void qspi_ip6514e_disable(struct qspi_ip6514e_dev_t* dev);
+
+/**
+ * \brief Enable the QSPI controller.
+ *
+ * \param[in] dev QSPI IP6514E device struct \ref qspi_ip6514e_dev_t
+ */
+void qspi_ip6514e_enable(struct qspi_ip6514e_dev_t* dev);
+
+/**
+ * \brief Change the baud rate divisor.
+ *
+ * \param[in] dev QSPI IP6514E device struct \ref qspi_ip6514e_dev_t
+ * \param[in] div Baud rate divisor value. It can only be an even number
+ *                    between 2 and 32 (both included).
+ *
+ * \return Returns error code as specified in \ref qspi_ip6514e_error_t
+ *
+ * \note The QSPI frequency is calculated dividing the QSPI controller clock by
+ *       this divisor. Please check Flash memory device specifications to know
+ *       the maximal frequency that can be used.
+ * \note The QSPI controller should be disabled before calling this function.
+ */
+enum qspi_ip6514e_error_t qspi_ip6514e_set_baud_rate_div(
+                                                 struct qspi_ip6514e_dev_t* dev,
+                                                 uint32_t div);
+
+/**
+ * \brief Set SPI mode for instruction, address and data.
+ *
+ * \param[in] dev       QSPI IP6514E device struct \ref qspi_ip6514e_dev_t
+ * \param[in] inst_type SPI mode to use for the instruction part of the command
+ * \param[in] addr_type SPI mode to use for the address part of the command
+ * \param[in] data_type SPI mode to use for the data part of the command
+ *
+ * \return Returns error code as specified in \ref qspi_ip6514e_error_t
+ *
+ * \note The QSPI controller should be idle before calling this function.
+ * \note Changing this setting will affect commands and direct operations.
+ */
+enum qspi_ip6514e_error_t qspi_ip6514e_set_spi_mode(
+                                        struct qspi_ip6514e_dev_t* dev,
+                                        enum qspi_ip6514e_spi_mode_t inst_type,
+                                        enum qspi_ip6514e_spi_mode_t addr_type,
+                                        enum qspi_ip6514e_spi_mode_t data_type);
+
+/**
+ * \brief Configure read commands for direct reads.
+ *
+ * \param[in] dev          QSPI IP6514E device struct \ref qspi_ip6514e_dev_t
+ * \param[in] opcode       Read opcode that will be used for every direct read
+ * \param[in] dummy_cycles Number of dummy cycles to wait before triggering the
+ *                         command, this value must be between 0 and 31
+ *                         (both included)
+ *
+ * \return Returns error code as specified in \ref qspi_ip6514e_error_t
+ *
+ * \note The QSPI controller should be idle before calling this function.
+ */
+enum qspi_ip6514e_error_t qspi_ip6514e_cfg_reads(struct qspi_ip6514e_dev_t* dev,
+                                                 uint8_t opcode,
+                                                 uint32_t dummy_cycles);
+
+/**
+ * \brief Configure write commands for direct writes.
+ *
+ * \param[in] dev          QSPI IP6514E device struct \ref qspi_ip6514e_dev_t
+ * \param[in] opcode       Write opcode that will be used for every direct write
+ * \param[in] dummy_cycles Number of dummy cycles to wait before triggering the
+ *                         command, this value must be between 0 and 31
+ *                         (both included)
+ *
+ * \return Returns error code as specified in \ref qspi_ip6514e_error_t
+ *
+ * \note The QSPI controller should be idle before calling this function.
+ */
+enum qspi_ip6514e_error_t qspi_ip6514e_cfg_writes(
+                                                 struct qspi_ip6514e_dev_t* dev,
+                                                 uint8_t opcode,
+                                                 uint32_t dummy_cycles);
+
+/**
+ * \brief Change the number of bytes per device page.
+ *
+ * \param[in] dev          QSPI IP6514E device struct \ref qspi_ip6514e_dev_t
+ * \param[in] page_size    Number of bytes per device page, must be between 0
+ *                         and 4095 (both included)
+ *
+ * \return Returns error code as specified in \ref qspi_ip6514e_error_t
+ *
+ * \note The QSPI controller should be idle before calling this function.
+ * \note This function will affect direct reads/writes.
+ */
+enum qspi_ip6514e_error_t qspi_ip6514e_cfg_page_size(
+                                                 struct qspi_ip6514e_dev_t* dev,
+                                                 uint32_t page_size);
+
+/**
+ * \brief Change the number of device address bytes.
+ *
+ * \param[in] dev          QSPI IP6514E device struct \ref qspi_ip6514e_dev_t
+ * \param[in] bytes_number Number of device address bytes, must be between 1
+ *                         and 16 (both included)
+ *
+ * \return Returns error code as specified in \ref qspi_ip6514e_error_t
+ *
+ * \note The QSPI controller should be idle before calling this function.
+ * \note This function will affect direct reads/writes.
+ */
+enum qspi_ip6514e_error_t qspi_ip6514e_cfg_addr_bytes(
+                                                 struct qspi_ip6514e_dev_t* dev,
+                                                 uint32_t bytes_number);
+
+/**
+ * \brief Remap the incoming AHB address with an offset for direct accesses.
+ *
+ * \param[in] dev    QSPI IP6514E device struct \ref qspi_ip6514e_dev_t
+ * \param[in] offset Offset that will be added to the incoming AHB address to
+ *                   access the Flash memory
+ *
+ * \note This function will only affect direct reads/writes.
+ * \note This function does not check if the resulting address is out of memory
+ *       bounds.
+ */
+void qspi_ip6514e_remap_addr(struct qspi_ip6514e_dev_t* dev, uint32_t offset);
+
+/**
+ * \brief Disable AHB address remapping for direct accesses.
+ *
+ * \param[in] dev    QSPI IP6514E device struct \ref qspi_ip6514e_dev_t
+ *
+ * \note This function will disable the controller if it is not already
+ *       disabled and enable it again (if it was).
+ * \note This function will only affect direct reads/writes.
+ */
+void qspi_ip6514e_disable_remap(struct qspi_ip6514e_dev_t* dev);
+
+/**
+ * \brief Send a command to the flash memory device using the Software Triggered
+ *        Instruction Generator (STIG).
+ *
+ * \param[in]  dev               QSPI IP6514E device struct
+ *                               \ref qspi_ip6514e_dev_t
+ * \param[in]  opcode            Opcode for the command.
+ * \param[out] read_data         Pointer to a memory zone where the read_len
+ *                               bytes read will be written to. If no data is to
+ *                               be read for the command,
+ *                               this argument should be NULL.
+ * \param[in]  read_len          Number of bytes to read for the command. If
+ *                               no bytes are to be read, use 0 for argument
+ *                               otherwise between 1 and 8 bytes (both
+ *                               included) can be read.
+ * \param[in]  write_data        Pointer to a memory zone where are
+ *                               located the write_len bytes to write for
+ *                               this command. If no bytes are to be written,
+ *                               use NULL as argument.
+ * \param[in]  write_len         Number of bytes to write for the command. If
+ *                               no bytes are to be written, use 0 for
+ *                               argument otherwise between 1 and 8 bytes
+ *                               (both included) can be written.
+ * \param[in]  addr              Address used for the command
+ * \param[in]  addr_bytes_number Number of address bytes for this command.
+ *                               If an address is not needed for the command,
+ *                               use 0 for argument, otherwise between 1 and
+ *                               4 bytes (both included) can be used.
+ * \param[in]  dummy_cycles      Number of dummy cycles required for the
+ *                               command, between 0 and 31 (both included).
+ *
+ * \return Returns error code as specified in \ref qspi_ip6514e_error_t
+ *
+ * \note Check the flash memory device specifications for the possible opcodes
+ *       that can be used and the other informations needed for this function.
+ * \note The SPI mode used for this command is the one set with the
+ *       \ref qspi_ip6514e_activate_qspi_mode function or the default one.
+ */
+enum qspi_ip6514e_error_t qspi_ip6514e_send_cmd(struct qspi_ip6514e_dev_t* dev,
+                                                uint8_t opcode,
+                                                void *read_data,
+                                                uint32_t read_len,
+                                                const void *write_data,
+                                                uint32_t write_len,
+                                                uint32_t addr,
+                                                uint32_t addr_bytes_number,
+                                                uint32_t dummy_cycles);
+
+/**
+ * \brief Send a simple command to the flash memory device using the Software
+ *        Triggered Instruction Generator (STIG) with no data arguments.
+ *        This command can be used for example to send the WRITE ENABLE command.
+ *
+ * \param[in]  dev    QSPI IP6514E device struct \ref qspi_ip6514e_dev_t
+ * \param[in]  opcode Opcode for the command.
+ *
+ * \note Check the flash memory device specifications for the possible opcodes
+ *       that can be used and the other informations needed for this function.
+ * \note The SPI mode used for this command is the one set with the
+ *       \ref qspi_ip6514e_activate_qspi_mode function or the default one.
+ */
+void qspi_ip6514e_send_simple_cmd(struct qspi_ip6514e_dev_t* dev,
+                                  uint8_t opcode);
+
+/**
+ * \brief Send a read command to the flash memory device using the Software
+ *        Triggered Instruction Generator (STIG). This command can be used to
+ *        read Flash memory data or registers.
+ *
+ * \param[in]  dev               QSPI IP6514E device struct
+ *                               \ref qspi_ip6514e_dev_t
+ * \param[in]  opcode            Opcode for the command.
+ * \param[out] read_data         Pointer to a memory zone where the
+ *                               read_len bytes read will be written to.
+ * \param[in]  read_len          Number of bytes to read for the command.
+ *                               Between 1 and 8 bytes (both included) can be
+ *                               read.
+ * \param[in]  addr              Address used for the command
+ * \param[in]  addr_bytes_number Number of address bytes for this command.
+ *                               If an address is not needed for the command,
+ *                               use 0 for argument, otherwise between 1 and
+ *                               4 bytes (both included) can be used.
+ * \param[in]  dummy_cycles      Number of dummy cycles required for the
+ *                               command, between 0 and 31 (both included).
+ *
+ * \return Returns error code as specified in \ref qspi_ip6514e_error_t
+ *
+ * \note Check the flash memory device specifications for the possible opcodes
+ *       that can be used and the other informations needed for this function.
+ * \note The SPI mode used for this command is the one set with the
+ *       \ref qspi_ip6514e_activate_qspi_mode function or the default one.
+ */
+enum qspi_ip6514e_error_t qspi_ip6514e_send_read_cmd(
+                                                 struct qspi_ip6514e_dev_t* dev,
+                                                 uint8_t opcode,
+                                                 void *read_data,
+                                                 uint32_t read_len,
+                                                 uint32_t addr,
+                                                 uint32_t addr_bytes_number,
+                                                 uint32_t dummy_cycles);
+
+/**
+ * \brief Send a write command to the flash memory device using the Software
+ *        Triggered Instruction Generator (STIG). This command can be used to
+ *        write Flash memory or registers.
+ *
+ * \param[in]  dev               QSPI IP6514E device struct
+ *                               \ref qspi_ip6514e_dev_t
+ * \param[in]  opcode            Opcode for the command.
+ * \param[in]  write_data        Pointer to a memory zone where are
+ *                               located the write_len bytes to write for
+ *                               this command.
+ * \param[in]  write_len         Number of bytes to write for the command.
+ *                               Between 1 and 8 bytes (both included) can be
+ *                               written.
+ * \param[in]  addr              Address used for the command
+ * \param[in]  addr_bytes_number Number of address bytes for this command.
+ *                               If an address is not needed for the command,
+ *                               use 0 for argument, otherwise between 1 and
+ *                               4 bytes (both included) can be used.
+ * \param[in]  dummy_cycles      Number of dummy cycles required for the
+ *                               command, between 0 and 31 (both included).
+ *
+ * \return Returns error code as specified in \ref qspi_ip6514e_error_t
+ *
+ * \note Check the flash memory device specifications for the possible opcodes
+ *       that can be used and the other informations needed for this function.
+ * \note The SPI mode used for this command is the one set with the
+ *       \ref qspi_ip6514e_activate_qspi_mode function or the default one.
+ */
+enum qspi_ip6514e_error_t qspi_ip6514e_send_write_cmd(
+                                                 struct qspi_ip6514e_dev_t* dev,
+                                                 uint8_t opcode,
+                                                 const void *write_data,
+                                                 uint32_t write_len,
+                                                 uint32_t addr,
+                                                 uint32_t addr_bytes_number,
+                                                 uint32_t dummy_cycles);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __QSPI_IP6514E_DRV_H__ */
diff --git a/platform/ext/target/musca_a/partition/flash_layout.h b/platform/ext/target/musca_a/partition/flash_layout.h
index 893c638..2cd435b 100755
--- a/platform/ext/target/musca_a/partition/flash_layout.h
+++ b/platform/ext/target/musca_a/partition/flash_layout.h
@@ -51,10 +51,9 @@
  */
 #define FLASH_PARTITION_SIZE            (0x80000) /* 512KB */
 
-/* Sector size of the flash hardware; same as FLASH0_SECTOR_SIZE */
-#define FLASH_AREA_IMAGE_SECTOR_SIZE    (0x1000)     /* 4 kB */
-/* Same as FLASH0_SIZE */
-#define FLASH_TOTAL_SIZE                (0x00800000) /* 8 MB */
+/* Sector size of the flash hardware */
+#define FLASH_AREA_IMAGE_SECTOR_SIZE    (0x1000)   /* 4KB */
+#define FLASH_TOTAL_SIZE                (0x800000) /* 8MB */
 
 /* Flash layout info for BL2 bootloader */
 #define FLASH_BASE_ADDRESS              (0x10200000) /* same as FLASH0_BASE_S */
@@ -85,11 +84,7 @@
 /** Maximum number of image sectors supported by the bootloader. */
 #define BOOT_MAX_IMG_SECTORS            ((2 * FLASH_PARTITION_SIZE) / \
                                          FLASH_AREA_IMAGE_SECTOR_SIZE)
-/*
- * Note: Though the SST_FLASH_AREA_ADDR is pointing to offset in flash, but
- * the actual contents of SST is stored in Code SRAM. See Driver_Flash.c for
- * more details.
- */
+
 #define FLASH_SST_AREA_OFFSET           (FLASH_AREA_IMAGE_SCRATCH_OFFSET + \
                                          FLASH_AREA_IMAGE_SCRATCH_SIZE)
 #define FLASH_SST_AREA_SIZE             (0x5000)   /* 20 KB */
@@ -129,6 +124,6 @@
 /* The sectors must be in consecutive memory location */
 #define SST_NBR_OF_SECTORS  (FLASH_SST_AREA_SIZE / SST_SECTOR_SIZE)
 /* Specifies the smallest flash programmable unit in bytes */
-#define SST_FLASH_PROGRAM_UNIT  0x4
+#define SST_FLASH_PROGRAM_UNIT  0x1
 
 #endif /* __FLASH_LAYOUT_H__ */
diff --git a/secure_fw/services/secure_storage/flash/sst_flash.c b/secure_fw/services/secure_storage/flash/sst_flash.c
index c73af4e..bf47579 100644
--- a/secure_fw/services/secure_storage/flash/sst_flash.c
+++ b/secure_fw/services/secure_storage/flash/sst_flash.c
@@ -78,6 +78,18 @@
     return PSA_SST_ERR_SUCCESS;
 }
 
+enum psa_sst_err_t sst_flash_init(void)
+{
+    int32_t err;
+
+    err = FLASH_DEV_NAME.Initialize(NULL);
+    if(err != ARM_DRIVER_OK) {
+        return PSA_SST_ERR_SYSTEM_ERROR;
+    }
+
+    return PSA_SST_ERR_SUCCESS;
+}
+
 enum psa_sst_err_t sst_flash_read(uint32_t block_id, uint8_t *buff,
                                   uint32_t offset, uint32_t size)
 {
diff --git a/secure_fw/services/secure_storage/flash/sst_flash.h b/secure_fw/services/secure_storage/flash/sst_flash.h
index 7dc6971..c3bffae 100644
--- a/secure_fw/services/secure_storage/flash/sst_flash.h
+++ b/secure_fw/services/secure_storage/flash/sst_flash.h
@@ -40,6 +40,14 @@
 #define SST_BLOCK_INVALID_ID 0xFFFFFFFF
 
 /**
+ * \brief  Initialize the Flash Interface.
+ *
+ * \return Returns PSA_SST_ERR_SUCCESS if the function is executed correctly.
+ *         Otherwise, it returns PSA_SST_ERR_SYSTEM_ERROR.
+ */
+enum psa_sst_err_t sst_flash_init(void);
+
+/**
  * \brief Reads block data from the position specifed by block ID and offset.
  *
  * \param[in]  block_id  Block ID
diff --git a/secure_fw/services/secure_storage/sst_core.c b/secure_fw/services/secure_storage/sst_core.c
index f63df70..2e9646c 100644
--- a/secure_fw/services/secure_storage/sst_core.c
+++ b/secure_fw/services/secure_storage/sst_core.c
@@ -1776,6 +1776,12 @@
 {
     enum psa_sst_err_t err;
 
+    /* Initialize Flash Interface */
+    err = sst_flash_init();
+    if(err != PSA_SST_ERR_SUCCESS) {
+        return err;
+    }
+
 #if SST_ENCRYPTION
     sst_crypto_init();
 #endif