diff options
author | Gerda Zsejke, More <gerdazsejke.more@arm.com> | 2021-03-01 16:06:39 +0100 |
---|---|---|
committer | Anton Komlev <Anton.Komlev@arm.com> | 2021-03-10 13:51:24 +0100 |
commit | 36023aabb7848cdef5f7c5bb727e0051ba873ebf (patch) | |
tree | 933ef9f3702c200ffb3b8844f5980bbb5aaa480a | |
parent | ed3980e7405599b39cbf19aef2d7dbbc8506f28a (diff) | |
download | trusted-firmware-m-36023aabb7848cdef5f7c5bb727e0051ba873ebf.tar.gz |
Platform: Enabled QSPI flash on Musca_S1 to store PS content
- Added QSPI flash driver implementation
- Changed flash_layout to set PS service for QSPI flash
- QSPI flash controller is set secure
Signed-off-by: Gerda Zsejke, More <gerdazsejke.more@arm.com>
Change-Id: Ica537baa2cc11c9470376aa9b7e53dc6e4ae7c14
14 files changed, 2806 insertions, 36 deletions
diff --git a/lib/ext/cryptocell-312-runtime/shared/hw/include/musca_s1/dx_reg_base_host.h b/lib/ext/cryptocell-312-runtime/shared/hw/include/musca_s1/dx_reg_base_host.h index 41a121176a..baec7048ac 100644 --- a/lib/ext/cryptocell-312-runtime/shared/hw/include/musca_s1/dx_reg_base_host.h +++ b/lib/ext/cryptocell-312-runtime/shared/hw/include/musca_s1/dx_reg_base_host.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2020, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2001-2021, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -18,7 +18,7 @@ * Same as FLASH_TFM_CRYPTO_KEY_AREA as found in * platform/ext/target/musca_s1/partition/flash_layout.h */ -#define DX_MRAM_CC 0x1A1EE000 +#define DX_MRAM_CC 0x1A1E9000 #define DX_BASE_ENV_REGS 0x500A0000 //TODO need confirm diff --git a/platform/ext/target/musca_s1/CMSIS_Driver/Config/RTE_Device.h b/platform/ext/target/musca_s1/CMSIS_Driver/Config/RTE_Device.h index 39dbb754b5..8fb06312d9 100644 --- a/platform/ext/target/musca_s1/CMSIS_Driver/Config/RTE_Device.h +++ b/platform/ext/target/musca_s1/CMSIS_Driver/Config/RTE_Device.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019 Arm Limited. All rights reserved. + * Copyright (c) 2018-2021 Arm Limited. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -92,4 +92,9 @@ #define RTE_FLASH0 1 // </e> FLASH (Flash Memory) [Driver_FLASH0] +// <e> FLASH (Flash Memory) [Driver_QSPI_FLASH0] +// <i> Configuration settings for Driver_QSPI_FLASH0 in component ::Drivers:FLASH +#define RTE_QSPI_FLASH0 1 +// </e> FLASH (Flash Memory) [Driver_QSPI_FLASH0] + #endif /* __RTE_DEVICE_H__ */ diff --git a/platform/ext/target/musca_s1/CMSIS_Driver/Config/cmsis_driver_config.h b/platform/ext/target/musca_s1/CMSIS_Driver/Config/cmsis_driver_config.h index a55e0130c9..2a8c723b12 100644 --- a/platform/ext/target/musca_s1/CMSIS_Driver/Config/cmsis_driver_config.h +++ b/platform/ext/target/musca_s1/CMSIS_Driver/Config/cmsis_driver_config.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020 Arm Limited. All rights reserved. + * Copyright (c) 2018-2021 Arm Limited. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,4 +43,6 @@ #define MUSCA_S1_SCC_DEV MUSCA_S1_SCC_DEV_S #define SSE_200_CACHE_DEV SSE_200_CACHE_DEV_S +#define QSPI_FLASH0_DEV MT25QL_DEV_S + #endif /* __CMSIS_DRIVER_CONFIG_H__ */ diff --git a/platform/ext/target/musca_s1/CMSIS_Driver/Driver_QSPI_Flash.c b/platform/ext/target/musca_s1/CMSIS_Driver/Driver_QSPI_Flash.c new file mode 100644 index 0000000000..440ba94914 --- /dev/null +++ b/platform/ext/target/musca_s1/CMSIS_Driver/Driver_QSPI_Flash.c @@ -0,0 +1,352 @@ +/* + * Copyright (c) 2013-2021 Arm Limited. All rights reserved. + * + * 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 "Driver_Flash.h" + +#include <stdbool.h> +#include <string.h> +#include "cmsis_driver_config.h" +#include "RTE_Device.h" +#include "flash_layout.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) + +static const ARM_DRIVER_VERSION DriverVersion = { + 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 = { + EVENT_READY_NOT_AVAILABLE, + DATA_WIDTH_32BIT, + CHIP_ERASE_SUPPORTED +}; + +/* Valid entries for data item width */ +static const uint32_t data_width_byte[] = { + sizeof(uint8_t), + sizeof(uint16_t), + sizeof(uint32_t), +}; + +/** + * \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; + + /* Calculating the highest address of the Flash memory address range */ + flash_limit = FLASH_TOTAL_SIZE - 1; + + return (offset > flash_limit) ? (false) : (true) ; +} + +/** + * \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) +{ + return ((param % flash_dev->data->program_unit) != 0) ? (false) : (true); +} + +#if (RTE_QSPI_FLASH0) +static ARM_FLASH_INFO ARM_FLASH0_DEV_DATA = { + .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 = { + .dev = &QSPI_FLASH0_DEV, + .data = &(ARM_FLASH0_DEV_DATA) +}; + +/* Flash Status */ +static ARM_FLASH_STATUS ARM_FLASH0_STATUS = {0, 0, 0}; + +static ARM_DRIVER_VERSION ARM_Flash_GetVersion(void) +{ + return DriverVersion; +} + +static ARM_FLASH_CAPABILITIES ARM_Flash_GetCapabilities(void) +{ + return DriverCapabilities; +} + +static int32_t ARM_Flash_Initialize(ARM_Flash_SignalEvent_t cb_event) +{ + enum mt25ql_error_t err = MT25QL_ERR_NONE; + + ARG_UNUSED(cb_event); + + qspi_ip6514e_enable(ARM_FLASH0_DEV.dev->controller); + + /* Configure QSPI Flash controller to operate in single SPI mode and + * to use fast Flash commands */ + err = mt25ql_config_mode(ARM_FLASH0_DEV.dev, MT25QL_FUNC_STATE_FAST); + if(err != MT25QL_ERR_NONE) { + return ARM_DRIVER_ERROR; + } + + return ARM_DRIVER_OK; +} + +static int32_t ARM_Flash_Uninitialize(void) +{ + enum mt25ql_error_t err = MT25QL_ERR_NONE; + + /* Restores the QSPI Flash controller and MT25QL to reset state */ + err = mt25ql_restore_reset_state(ARM_FLASH0_DEV.dev); + if(err != MT25QL_ERR_NONE) { + return ARM_DRIVER_ERROR; + } + + return ARM_DRIVER_OK; +} + +static int32_t ARM_Flash_PowerControl(ARM_POWER_STATE state) +{ + switch(state) { + case ARM_POWER_FULL: + /* Nothing to be done */ + return ARM_DRIVER_OK; + case ARM_POWER_OFF: + case ARM_POWER_LOW: + 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) +{ + /* Function includes a workaround for Musca-A QSPI flash when read data + * could be corrupted if read size is not 4 byte aligned. + */ + enum mt25ql_error_t err = MT25QL_ERR_NONE; + bool is_valid = true; + uint32_t extra_bytes = 0; + uint32_t extra_word = 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; + } + + ARM_FLASH0_STATUS.busy = DRIVER_STATUS_BUSY; + + if (cnt % data_width_byte[DriverCapabilities.data_width] != 0) { + extra_bytes = cnt % data_width_byte[DriverCapabilities.data_width]; + cnt -= extra_bytes; + } + + err = mt25ql_command_read(ARM_FLASH0_DEV.dev, addr, data, cnt); + + if (extra_bytes != 0) { + err = mt25ql_command_read(ARM_FLASH0_DEV.dev, addr + cnt, &extra_word, + data_width_byte[DriverCapabilities.data_width]); + memcpy((char *) data + cnt, &extra_word, extra_bytes); + } + + 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) +{ + enum mt25ql_error_t err = MT25QL_ERR_NONE; + + 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; + } + + 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; +} + +static int32_t ARM_Flash_EraseSector(uint32_t addr) +{ + enum mt25ql_error_t err = MT25QL_ERR_NONE; + + 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, addr, + (enum mt25ql_erase_t) 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; + } + return ARM_DRIVER_ERROR; + } + + return ARM_DRIVER_OK; +} + +static int32_t ARM_Flash_EraseChip(void) +{ + enum mt25ql_error_t err = MT25QL_ERR_NONE; + + if(DriverCapabilities.erase_chip == 1) { + + 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; + } + return ARM_DRIVER_ERROR; + } + + return ARM_DRIVER_OK; + + } else { + return ARM_DRIVER_ERROR_UNSUPPORTED; + } +} + +static ARM_FLASH_STATUS ARM_Flash_GetStatus(void) +{ + return ARM_FLASH0_STATUS; +} + +static ARM_FLASH_INFO * ARM_Flash_GetInfo(void) +{ + return ARM_FLASH0_DEV.data; +} + +ARM_DRIVER_FLASH Driver_QSPI_FLASH0 = { + ARM_Flash_GetVersion, + ARM_Flash_GetCapabilities, + ARM_Flash_Initialize, + ARM_Flash_Uninitialize, + ARM_Flash_PowerControl, + ARM_Flash_ReadData, + ARM_Flash_ProgramData, + ARM_Flash_EraseSector, + ARM_Flash_EraseChip, + ARM_Flash_GetStatus, + ARM_Flash_GetInfo +}; +#endif /* RTE_QSPI_FLASH0 */ diff --git a/platform/ext/target/musca_s1/CMakeLists.txt b/platform/ext/target/musca_s1/CMakeLists.txt index 33bcb85049..4d07d5342b 100644 --- a/platform/ext/target/musca_s1/CMakeLists.txt +++ b/platform/ext/target/musca_s1/CMakeLists.txt @@ -68,11 +68,13 @@ target_include_directories(platform_s Native_Driver partition services/include + Libraries ) target_sources(platform_s PRIVATE $<$<STREQUAL:${CRYPTO_HW_ACCELERATOR_OTP_STATE},ENABLED>:${CMAKE_CURRENT_SOURCE_DIR}/crypto_keys.c> + CMSIS_Driver/Driver_QSPI_Flash.c CMSIS_Driver/Driver_Flash_MRAM.c CMSIS_Driver/Driver_MPC.c CMSIS_Driver/Driver_PPC.c @@ -85,11 +87,13 @@ target_sources(platform_s Native_Driver/gpio_cmsdk_drv.c Native_Driver/uart_pl011_drv.c Native_Driver/musca_s1_scc_drv.c + Native_Driver/qspi_ip6514e_drv.c Native_Driver/cache_drv.c spm_hal.c tfm_hal_isolation.c target_cfg.c Native_Driver/timer_cmsdk_drv.c + Libraries/mt25ql_flash_lib.c ${CMAKE_SOURCE_DIR}/platform/ext/common/tfm_hal_isolation_mpu_v8m.c $<$<BOOL:${TFM_PARTITION_PLATFORM}>:${CMAKE_CURRENT_SOURCE_DIR}/plat_test.c> $<$<BOOL:${TFM_PARTITION_PLATFORM}>:${CMAKE_CURRENT_SOURCE_DIR}/services/src/tfm_platform_system.c> @@ -121,6 +125,7 @@ target_include_directories(platform_ns Device/Include Native_Driver services/include + Libraries ) #========================= Platform BL2 =======================================# @@ -143,6 +148,7 @@ if(BL2) PUBLIC partition Device/Include + Libraries PRIVATE . CMSIS_Driver/Config diff --git a/platform/ext/target/musca_s1/Device/Config/device_cfg.h b/platform/ext/target/musca_s1/Device/Config/device_cfg.h index 4a5e645518..7f9dae1605 100644 --- a/platform/ext/target/musca_s1/Device/Config/device_cfg.h +++ b/platform/ext/target/musca_s1/Device/Config/device_cfg.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Arm Limited. All rights reserved. + * Copyright (c) 2017-2021 Arm Limited. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -68,4 +68,10 @@ /* Default UART baud rate */ #define DEFAULT_UART_BAUDRATE 115200 +/* Cadence QSPI Flash Controller */ +#define QSPI_IP6514E_S + +/* MT25QL Flash memory library */ +#define MT25QL_S + #endif /* __DEVICE_CFG_H__ */ diff --git a/platform/ext/target/musca_s1/Device/Include/device_definition.h b/platform/ext/target/musca_s1/Device/Include/device_definition.h index 2e589f509d..b0a7828d98 100644 --- a/platform/ext/target/musca_s1/Device/Include/device_definition.h +++ b/platform/ext/target/musca_s1/Device/Include/device_definition.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Arm Limited. All rights reserved. + * Copyright (c) 2017-2021 Arm Limited. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -188,6 +188,17 @@ extern struct spi_ip6524_dev_t SPI0_DEV_S; extern struct spi_ip6524_dev_t SPI0_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" @@ -235,6 +246,19 @@ extern struct musca_s1_scc_dev_t MUSCA_S1_SCC_DEV_NS; extern struct arm_cache_dev_t SSE_200_CACHE_DEV_S; #endif +/* ======= External peripheral configuration structure declarations ======= */ + +/* MT25QL Flash memory library structures */ +#if (defined(MT25QL_NS) && defined(QSPI_IP6514E_NS)) +#include "mt25ql_flash_lib.h" +extern struct mt25ql_dev_t MT25QL_DEV_NS; +#endif + +#if (defined(MT25QL_S) && defined(QSPI_IP6514E_S)) +#include "mt25ql_flash_lib.h" +extern struct mt25ql_dev_t MT25QL_DEV_S; +#endif + #ifdef __cplusplus } #endif diff --git a/platform/ext/target/musca_s1/Device/Source/device_definition.c b/platform/ext/target/musca_s1/Device/Source/device_definition.c index 3176b76c0c..58b1689d96 100644 --- a/platform/ext/target/musca_s1/Device/Source/device_definition.c +++ b/platform/ext/target/musca_s1/Device/Source/device_definition.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Arm Limited. All rights reserved. + * Copyright (c) 2017-2021 Arm Limited. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -494,3 +494,47 @@ static const struct arm_cache_dev_cfg_t SSE_200_CACHE_CFG_S = { .base = MUSCA_S1_CPU_ELEMENT_S_BASE}; struct arm_cache_dev_t SSE_200_CACHE_DEV_S = {&(SSE_200_CACHE_CFG_S)}; #endif + +/* QSPI IP6514E driver structures */ +#ifdef QSPI_IP6514E_NS +static const struct qspi_ip6514e_dev_cfg_t QSPI_DEV_CFG_NS = { + .base = MUSCA_S1_QSPI_REG_NS_BASE, + .addr_mask = (1U << 25) - 1, +}; +struct qspi_ip6514e_dev_t QSPI_DEV_NS = { + &QSPI_DEV_CFG_NS +}; +#endif + +#ifdef QSPI_IP6514E_S +static const struct qspi_ip6514e_dev_cfg_t QSPI_DEV_CFG_S = { + .base = MUSCA_S1_QSPI_REG_S_BASE, + .addr_mask = (1U << 25) - 1, +}; +struct qspi_ip6514e_dev_t QSPI_DEV_S = { + &QSPI_DEV_CFG_S +}; +#endif + +/* ======= External peripheral configuration structure definitions ======= */ + +/* MT25QL Flash memory library structures */ +#if (defined(MT25QL_NS) && defined(QSPI_IP6514E_NS)) +struct mt25ql_dev_t MT25QL_DEV_NS = { + .controller = &QSPI_DEV_NS, + .direct_access_start_addr = MUSCA_S1_QSPI_FLASH_NS_BASE, + .baud_rate_div = 4U, + .size = 0x02000000U, /* 32 MiB */ + .config_state = { MT25QL_FUNC_STATE_NOT_INITED }, +}; +#endif + +#if (defined(MT25QL_S) && defined(QSPI_IP6514E_S)) +struct mt25ql_dev_t MT25QL_DEV_S = { + .controller = &QSPI_DEV_S, + .direct_access_start_addr = MUSCA_S1_QSPI_FLASH_S_BASE, + .baud_rate_div = 4U, + .size = 0x02000000U, /* 32 MiB */ + .config_state = { MT25QL_FUNC_STATE_NOT_INITED }, +}; +#endif diff --git a/platform/ext/target/musca_s1/Libraries/mt25ql_flash_lib.c b/platform/ext/target/musca_s1/Libraries/mt25ql_flash_lib.c new file mode 100644 index 0000000000..f748ecf92f --- /dev/null +++ b/platform/ext/target/musca_s1/Libraries/mt25ql_flash_lib.c @@ -0,0 +1,901 @@ +/* + * Copyright (c) 2018-2020 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 "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 FAST_READ_CMD 0x0BU +#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 + +/* + * 10 is the minimal number of dummy clock cycles needed to reach the maximal + * frequency of the Quad Output Fast Read Command. + */ +#define QUAD_OUTPUT_FAST_READ_DUMMY_CYCLES 10U +#define FAST_READ_DUMMY_CYCLES 8U +#define RESET_STATE_DUMMY_CYCLES 8U +#define DEFAULT_READ_DUMMY_CYCLES 0U +#define QUAD_INPUT_FAST_PROGRAM_DUMMY_CYCLES 0U +#define PAGE_PROGRAM_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 Set SPI mode on the flash device and on the controller. + * + * \param[in] dev Pointer to MT25QL device structure \ref mt25ql_dev_t + * \param[in] spi_mode SPI mode to be set on flash device and controller + * \ref qspi_ip6514e_spi_mode_t + * + * \return Return error code as specified in \ref mt25ql_error_t + */ +static enum mt25ql_error_t set_spi_mode(struct mt25ql_dev_t* dev, + enum qspi_ip6514e_spi_mode_t spi_mode) +{ + uint8_t enhanced_volatile_cfg_reg = 0; + enum qspi_ip6514e_error_t controller_error; + + /* Read the Enhanced Volatile Configuration Register, modify it according + * to the requested SPI mode then write back the modified value to the + * register. This will activate the SPI 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; + } + + switch(spi_mode) { + case QSPI_IP6514E_SPI_MODE: + /* Disable the Dual- and Quad-SPI modes. + * Clearing the bit enables the mode, setting it disables it. + */ + SET_BIT(enhanced_volatile_cfg_reg, ENHANCED_VOLATILE_CFG_REG_DSPI_POS); + SET_BIT(enhanced_volatile_cfg_reg, ENHANCED_VOLATILE_CFG_REG_QSPI_POS); + break; + case QSPI_IP6514E_DSPI_MODE: + /* Disable the Quad-SPI mode and activate DSPI mode. + * Clearing the bit enables the mode, setting it disables it. + */ + CLR_BIT(enhanced_volatile_cfg_reg, ENHANCED_VOLATILE_CFG_REG_DSPI_POS); + SET_BIT(enhanced_volatile_cfg_reg, ENHANCED_VOLATILE_CFG_REG_QSPI_POS); + break; + case QSPI_IP6514E_QSPI_MODE: + /* 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); + break; + default: + return MT25QL_ERR_WRONG_ARGUMENT; + } + + 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 requested SPI mode on the controller side as well. */ + controller_error = qspi_ip6514e_set_spi_mode(dev->controller, + spi_mode, + spi_mode, + spi_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_config_mode(struct mt25ql_dev_t* dev, + enum mt25ql_functional_state_t f_state) +{ + enum qspi_ip6514e_error_t controller_error; + enum mt25ql_error_t library_error; + + switch(f_state) { + case MT25QL_FUNC_STATE_DEFAULT: + dev->config_state.spi_mode = QSPI_IP6514E_SPI_MODE; + dev->config_state.opcode_read = READ_CMD; + dev->config_state.dummy_cycles_read = DEFAULT_READ_DUMMY_CYCLES; + dev->config_state.opcode_write = PAGE_PROGRAM_CMD; + dev->config_state.dummy_cycles_write = PAGE_PROGRAM_DUMMY_CYCLES; + break; + case MT25QL_FUNC_STATE_FAST: + dev->config_state.spi_mode = QSPI_IP6514E_SPI_MODE; + dev->config_state.opcode_read = FAST_READ_CMD; + dev->config_state.dummy_cycles_read = FAST_READ_DUMMY_CYCLES; + dev->config_state.opcode_write = PAGE_PROGRAM_CMD; + dev->config_state.dummy_cycles_write = PAGE_PROGRAM_DUMMY_CYCLES; + break; + case MT25QL_FUNC_STATE_QUAD_FAST: + dev->config_state.spi_mode = QSPI_IP6514E_QSPI_MODE; + dev->config_state.opcode_read = QUAD_OUTPUT_FAST_READ_CMD; + dev->config_state.dummy_cycles_read = + QUAD_OUTPUT_FAST_READ_DUMMY_CYCLES; + dev->config_state.opcode_write = QUAD_INPUT_FAST_PROGRAM_CMD; + dev->config_state.dummy_cycles_write = + QUAD_INPUT_FAST_PROGRAM_DUMMY_CYCLES; + break; + default: + return MT25QL_ERR_WRONG_ARGUMENT; + } + + dev->config_state.func_state = f_state; + + /* This function will first set the Flash memory SPI mode and then set + * the controller's SPI mode. It will fail if the two sides do not have + * the same mode when this function is called. + */ + library_error = set_spi_mode(dev, dev->config_state.spi_mode); + if (library_error != MT25QL_ERR_NONE) { + return library_error; + } + + /* Set the number of dummy cycles for read commands. */ + library_error = change_dummy_cycles( + dev, dev->config_state.dummy_cycles_read); + if (library_error != MT25QL_ERR_NONE) { + return library_error; + } + + /* The rest of the configuration needs the controller to be disabled */ + while(!qspi_ip6514e_is_idle(dev->controller)); + 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, dev->config_state.opcode_read, + dev->config_state.dummy_cycles_read); + 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, dev->config_state.opcode_write, + dev->config_state.dummy_cycles_write); + 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); + + return MT25QL_ERR_NONE; +} + +enum mt25ql_error_t mt25ql_restore_reset_state(struct mt25ql_dev_t* dev) +{ + enum mt25ql_error_t library_error; + + /* + * This function will first change the Flash memory mode to single SPI and + * then change the controller to single SPI. It will fail if the two sides + * do not have the same mode when this function is called. + */ + library_error = set_spi_mode(dev, QSPI_IP6514E_SPI_MODE); + if (library_error != MT25QL_ERR_NONE) { + return library_error; + } + + /* Set the default number of dummy cycles for direct read commands. */ + library_error = change_dummy_cycles(dev, RESET_STATE_DUMMY_CYCLES); + if (library_error != MT25QL_ERR_NONE) { + return library_error; + } + + /* The rest of the configuration needs the controller to be disabled */ + while(!qspi_ip6514e_is_idle(dev->controller)); + qspi_ip6514e_disable(dev->controller); + + /* Restore the default value of the QSPI controller registers. */ + qspi_ip6514e_reset_regs(dev->controller); + + qspi_ip6514e_enable(dev->controller); + + dev->config_state = (struct mt25ql_config_state_t){ MT25QL_FUNC_STATE_NOT_INITED }; + dev->config_state.func_state = MT25QL_FUNC_STATE_NOT_INITED; + + 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; + + if (dev->config_state.func_state == MT25QL_FUNC_STATE_NOT_INITED) { + return MT25QL_ERR_NOT_INITED; + } + + for (uint32_t cmd_index = 0; cmd_index < cmd_number; cmd_index++) { + controller_error = qspi_ip6514e_send_read_cmd( + dev->controller, + dev->config_state.opcode_read, + data, CMD_DATA_MAX_SIZE, addr, + ADDR_BYTES, + dev->config_state.dummy_cycles_read); + 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, + dev->config_state.opcode_read, + data, len, addr, ADDR_BYTES, + dev->config_state.dummy_cycles_read); + 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; + + if (dev->config_state.func_state == MT25QL_FUNC_STATE_NOT_INITED) { + return MT25QL_ERR_NOT_INITED; + } + + 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, dev->config_state.opcode_write, + data, CMD_DATA_MAX_SIZE, addr, + ADDR_BYTES, + dev->config_state.dummy_cycles_write); + 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, + dev->config_state.opcode_write, + data, CMD_DATA_MAX_SIZE, addr, + ADDR_BYTES, + dev->config_state.dummy_cycles_write); + 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, dev->config_state.opcode_write, + data, len, addr, ADDR_BYTES, + dev->config_state.dummy_cycles_write); + 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, + dev->config_state.opcode_write, + data, len, addr, ADDR_BYTES, + dev->config_state.dummy_cycles_write); + 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; + + if (dev->config_state.func_state == MT25QL_FUNC_STATE_NOT_INITED) { + return MT25QL_ERR_NOT_INITED; + } + + 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_s1/Libraries/mt25ql_flash_lib.h b/platform/ext/target/musca_s1/Libraries/mt25ql_flash_lib.h new file mode 100644 index 0000000000..c2dac2ca21 --- /dev/null +++ b/platform/ext/target/musca_s1/Libraries/mt25ql_flash_lib.h @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2018-2019 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 "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_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_NOT_INITED, + 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_NOT_INITED = 0U, + /*!< QSPI Flash controller is not initialized, only direct read + * is guaranteed to be working + */ + MT25QL_FUNC_STATE_DEFAULT = 1U, + /*!< The QSPI Flash controller and memory is in default state, + * using basic read/write commands + */ + MT25QL_FUNC_STATE_FAST = 2U, + /*!< The QSPI Flash controller and memory is configured to operate in + * single SPI mode and fast Flash commands could be used for read and + * program operations. + */ + MT25QL_FUNC_STATE_QUAD_FAST = 3U, + /*!< The QSPI Flash controller and memory is configured to operate in + * Quad SPI mode and fast Flash commands could be used for read and + * program operations. + */ +}; + +struct mt25ql_config_state_t { + enum mt25ql_functional_state_t func_state; + /*!< Functional state id */ + enum qspi_ip6514e_spi_mode_t spi_mode; + /*!< SPI mode for the current functional state */ + uint8_t opcode_read; + /*!< Read opcode for the current functional state */ + uint8_t opcode_write; + /*!< Write opcode for the current functional state */ + uint32_t dummy_cycles_read; + /*!< Dummy cycles for the read command for the current functional state */ + uint32_t dummy_cycles_write; + /*!< Dummy cycles for the write command for the current functional state */ +}; + +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 */ + struct mt25ql_config_state_t config_state; + /*!< Configured functional state (with parameter settings) of the + * QSPI Flash controller and memory. + */ + +}; + +/** + * \brief Change configuration of the QSPI Flash controller and MT25QL memory + * + * Changes the configuration of the QSPI Flash controller and MT25QL + * Flash memory to operate in the specified SPI mode and to use the + * appropriate Flash commands for read and program operations. + * It also sets: + * + The number of dummy cycles for each operation + * + The bytes per page constant to 256 (MT25QL Flash specific) + * + The number of address bytes to 3 + * + * \param[in] dev Pointer to MT25QL device structure \ref mt25ql_dev_t + * \param[in] f_state Functional state to be set on flash controller + * and device \ref mt25ql_functional_state_t + * + * \return Return error code as specified in \ref mt25ql_error_t + * + * \note This function assumes that the Flash memory device and the QSPI Flash + * controller operates with the same SPI protocol. This function will fail + * if the Flash device is in a different configuration. + */ +enum mt25ql_error_t mt25ql_config_mode(struct mt25ql_dev_t* dev, + enum mt25ql_functional_state_t f_state); + +/** + * \brief Restore the QSPI Flash controller and MT25QL to reset state. + * + * \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 and the QSPI Flash + * controller operates with the same SPI protocol. This function will fail + * if the Flash device is in a different configuration. + */ +enum mt25ql_error_t mt25ql_restore_reset_state(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_s1/Native_Driver/qspi_ip6514e_drv.c b/platform/ext/target/musca_s1/Native_Driver/qspi_ip6514e_drv.c new file mode 100644 index 0000000000..bb13a4219b --- /dev/null +++ b/platform/ext/target/musca_s1/Native_Driver/qspi_ip6514e_drv.c @@ -0,0 +1,755 @@ +/* + * Copyright (c) 2018-2019 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 <stdint.h> +#include <stdlib.h> +#include <stdbool.h> +/* Use memcpy */ +#include <string.h> + +#include "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 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 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]; +}; + +/** 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 + +/** + * 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 + +/** Default register values of the QSPI Flash controller */ +#define QSPI_CFG_REG_RESET_VALUE (0x80080080U) +#define DEVICE_READ_INSTR_REG_RESET_VALUE (0x080220EBU) +#define DEVICE_WRITE_INSTR_REG_RESET_VALUE (0x00000002U) +#define DEVICE_SIZE_CFG_REG_RESET_VALUE (0x00101002U) +#define REMAP_ADDR_REG_RESET_VALUE (0x00000000U) +#define FLASH_CMD_CONTROL_REG_RESET_VALUE (0x00000000U) +#define FLASH_CMD_ADDRESS_REG_RESET_VALUE (0x00000000U) +#define FLASH_CMD_WRITE_DATA_REG_RESET_VALUE (0x00000000U) + +/** + * \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) +{ + 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; + + /* + * Wait for the Serial Interface and QSPI pipeline to be IDLE when + * all low level synchronization has been done. + */ + while(!qspi_ip6514e_is_idle(dev)); + + 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; + + /* + * Wait for the Serial Interface and QSPI pipeline to be IDLE when + * all low level synchronization has been done. + */ + while(!qspi_ip6514e_is_idle(dev)); + + /* 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; + + /* + * Wait for the Serial Interface and QSPI pipeline to be IDLE when + * all low level synchronization has been done. + */ + while(!qspi_ip6514e_is_idle(dev)); + + /* + * 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; + + /* + * Wait for the Serial Interface and QSPI pipeline to be IDLE when + * all low level synchronization has been done. + */ + while(!qspi_ip6514e_is_idle(dev)); + + 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; + + /* + * Wait for the Serial Interface and QSPI pipeline to be IDLE when + * all low level synchronization has been done. + */ + while(!qspi_ip6514e_is_idle(dev)); + + 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); + } +} + +void qspi_ip6514e_reset_regs(struct qspi_ip6514e_dev_t* dev) +{ + struct _qspi_ip6514e_reg_map_t *reg_map = + (struct _qspi_ip6514e_reg_map_t *)dev->cfg->base; + + /* Restore the default value of the QSPI Configuration register. */ + reg_map->qspi_cfg = QSPI_CFG_REG_RESET_VALUE; + + /* Restore the default value of the Device R/W Instruction registers. */ + reg_map->device_read_inst = DEVICE_READ_INSTR_REG_RESET_VALUE; + reg_map->device_write_inst = DEVICE_WRITE_INSTR_REG_RESET_VALUE; + + /* Restore the default value of the Device Size Configuration register. */ + reg_map->device_size = DEVICE_SIZE_CFG_REG_RESET_VALUE; + + /* Restore the default value of the Remap Address register. */ + reg_map->remap_addr = REMAP_ADDR_REG_RESET_VALUE; + + /* Restore the default value of the Flash Command Control register. */ + reg_map->flash_cmd_ctrl = FLASH_CMD_CONTROL_REG_RESET_VALUE; + /* Restore the default value of the Flash Command Address register. */ + reg_map->flash_cmd_addr = FLASH_CMD_ADDRESS_REG_RESET_VALUE; + + /* Restore the default value of the Flash Command Write Data registers. */ + reg_map->flash_cmd_write_data_lower = FLASH_CMD_WRITE_DATA_REG_RESET_VALUE; + reg_map->flash_cmd_write_data_upper = FLASH_CMD_WRITE_DATA_REG_RESET_VALUE; + + /* + * This function does not affect the Flash Command Read Data registers + * which are completely Read-Only. + */ +} + +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_s1/Native_Driver/qspi_ip6514e_drv.h b/platform/ext/target/musca_s1/Native_Driver/qspi_ip6514e_drv.h new file mode 100644 index 0000000000..69929255dd --- /dev/null +++ b/platform/ext/target/musca_s1/Native_Driver/qspi_ip6514e_drv.h @@ -0,0 +1,416 @@ +/* + * 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. + */ + +/** + * \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 <stdint.h> +#include <stdbool.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \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_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. + */ +}; + +/** + * \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 Restore the default value of the QSPI controller registers. + * + * \param[in] dev QSPI IP6514E device struct \ref qspi_ip6514e_dev_t + * + * \note The QSPI controller should be disabled before calling this function. + */ +void qspi_ip6514e_reset_regs(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_s1/partition/flash_layout.h b/platform/ext/target/musca_s1/partition/flash_layout.h index 2c3d4d2686..bec8cc84e2 100644 --- a/platform/ext/target/musca_s1/partition/flash_layout.h +++ b/platform/ext/target/musca_s1/partition/flash_layout.h @@ -25,13 +25,14 @@ * 0x0A10_0000 Secure image secondary (384 KB) * 0x0A16_0000 Non-secure image secondary (512 KB) * 0x0A1E_0000 Scratch Area (16 KB) - * 0x0A1E_4000 Protected Storage Area (20 KB) - * 0x0A1E_9000 Internal Trusted Storage Area (16 KB) - * 0x0A1E_D000 NV counters area (4 KB) - * 0x0A1E_E000 TF-M key area (256 bytes) This area is referred to in + * 0x0A1E_4000 Internal Trusted Storage Area (16 KB) + * 0x0A1E_8000 NV counters area (4 KB) + * 0x0A1E_9000 TF-M key area (256 bytes) This area is referred to in * /lib/ext/cryptocell-312-runtime/shared/hw/include/musca_s1/ \ * dx_reg_base_host.h Do not change one without changing the other. - * 0x0A1E_E100 Unused + * 0x0A1E_9100 Unused + * 0x0020_0000 Protected storage area (20 KB) This area is placed in the QSPI + * flash * * Flash layout on Musca-S1 with BL2(single image boot): * 0x0A00_0000 BL2 - MCUBoot(128 KB) @@ -42,13 +43,14 @@ * 0x0A10_0000 Secure image secondary (384 KB) * 0x0A16_0000 Non-secure image secondary (512 KB) * 0x0A1E_0000 Scratch Area (16 KB) - * 0x0A1E_4000 Protected Storage Area (20 KB) - * 0x0A1E_9000 Internal Trusted Storage Area (16 KB) - * 0x0A1E_D000 NV counters area (4 KB) - * 0x0A1E_E000 TF-M key area (256 bytes) This area is referred to in + * 0x0A1E_4000 Internal Trusted Storage Area (16 KB) + * 0x0A1E_8000 NV counters area (4 KB) + * 0x0A1E_9000 TF-M key area (256 bytes) This area is referred to in * /lib/ext/cryptocell-312-runtime/shared/hw/include/musca_s1/ \ * dx_reg_base_host.h Do not change one without changing the other. - * 0x0A1E_E100 Unused + * 0x0A1E_9100 Unused + * 0x0020_0000 Protected storage area (20 KB) This area is placed in the QSPI + * flash * * Flash layout on Musca-S1 without BL2: * 0x0A00_0000 Secure image @@ -140,17 +142,9 @@ #error "Only MCUBOOT_IMAGE_NUMBER 1 and 2 are supported!" #endif /* MCUBOOT_IMAGE_NUMBER */ -/* Note: FLASH_PS_AREA_OFFSET, FLASH_ITS_AREA_OFFSET and - * FLASH_NV_COUNTERS_AREA_OFFSET point to offsets in flash, but reads and writes - * to these addresses are redirected to Code SRAM by Driver_Flash.c. - */ -#define FLASH_PS_AREA_OFFSET (FLASH_AREA_SCRATCH_OFFSET + \ - FLASH_AREA_SCRATCH_SIZE) -#define FLASH_PS_AREA_SIZE (0x5000) /* 20 KB */ - /* Internal Trusted Storage (ITS) Service definitions */ -#define FLASH_ITS_AREA_OFFSET (FLASH_PS_AREA_OFFSET + \ - FLASH_PS_AREA_SIZE) +#define FLASH_ITS_AREA_OFFSET (FLASH_AREA_SCRATCH_OFFSET + \ + FLASH_AREA_SCRATCH_SIZE) #define FLASH_ITS_AREA_SIZE (0x4000) /* 16 KB */ /* NV Counters definitions */ @@ -180,15 +174,15 @@ * Note: Further documentation of these definitions can be found in the * TF-M PS Integration Guide. */ -#define TFM_HAL_PS_FLASH_DRIVER Driver_FLASH0 +#define TFM_HAL_PS_FLASH_DRIVER Driver_QSPI_FLASH0 /* In this target the CMSIS driver requires only the offset from the base * address instead of the full memory address. */ /* Base address of dedicated flash area for PS */ -#define TFM_HAL_PS_FLASH_AREA_ADDR FLASH_PS_AREA_OFFSET +#define TFM_HAL_PS_FLASH_AREA_ADDR 0x0 /* Size of dedicated flash area for PS */ -#define TFM_HAL_PS_FLASH_AREA_SIZE FLASH_PS_AREA_SIZE +#define TFM_HAL_PS_FLASH_AREA_SIZE (0x5000) /* 20 KB */ #define PS_RAM_FS_SIZE TFM_HAL_PS_FLASH_AREA_SIZE /* Number of physical erase sectors per logical FS block */ #define TFM_HAL_PS_SECTORS_PER_BLOCK (1) diff --git a/platform/ext/target/musca_s1/target_cfg.c b/platform/ext/target/musca_s1/target_cfg.c index 75a1e77b38..a512953933 100644 --- a/platform/ext/target/musca_s1/target_cfg.c +++ b/platform/ext/target/musca_s1/target_cfg.c @@ -635,12 +635,6 @@ int32_t ppc_init_cfg(void) if (ret != ARM_DRIVER_OK) { return ret; } - ret = Driver_APB_PPCEXP1.ConfigPeriph(MUSCA_S1_QSPI_APB_PPC_POS, - ARM_PPC_NONSECURE_ONLY, - ARM_PPC_PRIV_AND_NONPRIV); - if (ret != ARM_DRIVER_OK) { - return ret; - } ret = Driver_APB_PPCEXP1.ConfigPeriph(MUSCA_S1_GPTIMER0_APB_PPC_POS, ARM_PPC_NONSECURE_ONLY, ARM_PPC_PRIV_AND_NONPRIV); |