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