From 1005cd9fa29ae7db2189d9f0aa19908da4cdc23a Mon Sep 17 00:00:00 2001 From: Tri Nguyen Date: Wed, 14 Aug 2024 16:27:06 +0700 Subject: [PATCH] drivers: flash: Initial support OSPI flash driver on RA8 boards Support OSPI flash driver on EK-RA8M1 and EK-RA8D1 with ospi_b and S28HL512T flash. Signed-off-by: Tri Nguyen Signed-off-by: Thao Luong --- drivers/flash/CMakeLists.txt | 1 + drivers/flash/Kconfig | 1 + drivers/flash/Kconfig.renesas_ra_ospi | 18 + drivers/flash/flash_renesas_ra_ospi_b.c | 785 ++++++++++++++++++ drivers/flash/flash_renesas_ra_ospi_b.h | 297 +++++++ drivers/flash/spi_nor_s28hx512t.h | 47 ++ .../renesas,ra-ospi-b-nor.yaml | 70 ++ dts/bindings/ospi/renesas,ra-ospi-b.yaml | 23 + modules/Kconfig.renesas | 5 + 9 files changed, 1247 insertions(+) create mode 100644 drivers/flash/Kconfig.renesas_ra_ospi create mode 100644 drivers/flash/flash_renesas_ra_ospi_b.c create mode 100644 drivers/flash/flash_renesas_ra_ospi_b.h create mode 100644 drivers/flash/spi_nor_s28hx512t.h create mode 100644 dts/bindings/flash_controller/renesas,ra-ospi-b-nor.yaml create mode 100644 dts/bindings/ospi/renesas,ra-ospi-b.yaml diff --git a/drivers/flash/CMakeLists.txt b/drivers/flash/CMakeLists.txt index b72aa37a360..3682dc83ed7 100644 --- a/drivers/flash/CMakeLists.txt +++ b/drivers/flash/CMakeLists.txt @@ -37,6 +37,7 @@ zephyr_library_sources_ifdef(CONFIG_FLASH_MSPI_EMUL_DEVICE flash_mspi_emul_devic zephyr_library_sources_ifdef(CONFIG_FLASH_MSPI_NOR flash_mspi_nor.c) zephyr_library_sources_ifdef(CONFIG_FLASH_NPCX_FIU_NOR flash_npcx_fiu_nor.c) zephyr_library_sources_ifdef(CONFIG_FLASH_NPCX_FIU_QSPI flash_npcx_fiu_qspi.c) +zephyr_library_sources_ifdef(CONFIG_FLASH_RENESAS_RA_OSPI_B flash_renesas_ra_ospi_b.c) zephyr_library_sources_ifdef(CONFIG_FLASH_RPI_PICO flash_rpi_pico.c) zephyr_library_sources_ifdef(CONFIG_FLASH_STM32_OSPI flash_stm32_ospi.c) zephyr_library_sources_ifdef(CONFIG_FLASH_STM32_QSPI flash_stm32_qspi.c) diff --git a/drivers/flash/Kconfig b/drivers/flash/Kconfig index cfa34d3e1c4..758a43287a2 100644 --- a/drivers/flash/Kconfig +++ b/drivers/flash/Kconfig @@ -191,6 +191,7 @@ source "drivers/flash/Kconfig.numaker" source "drivers/flash/Kconfig.numaker_rmc" source "drivers/flash/Kconfig.nxp_s32" source "drivers/flash/Kconfig.renesas_ra" +source "drivers/flash/Kconfig.renesas_ra_ospi" source "drivers/flash/Kconfig.rpi_pico" source "drivers/flash/Kconfig.rts5912" source "drivers/flash/Kconfig.rv32m1" diff --git a/drivers/flash/Kconfig.renesas_ra_ospi b/drivers/flash/Kconfig.renesas_ra_ospi new file mode 100644 index 00000000000..1624f0c8258 --- /dev/null +++ b/drivers/flash/Kconfig.renesas_ra_ospi @@ -0,0 +1,18 @@ +# Renesas RA Family + +# Copyright (c) 2024-2025 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +config FLASH_RENESAS_RA_OSPI_B + bool "Renesas RA Octal-SPI driver" + default y + depends on DT_HAS_RENESAS_RA_OSPI_B_NOR_ENABLED + select FLASH_HAS_DRIVER_ENABLED + select FLASH_HAS_PAGE_LAYOUT + select FLASH_HAS_EXPLICIT_ERASE + select USE_RA_FSP_OSPI_B_NOR_FLASH + select FLASH_JESD216 + select FLASH_HAS_EX_OP + select PINCTRL + help + Enable Octal-SPI Nor flash driver for RA series diff --git a/drivers/flash/flash_renesas_ra_ospi_b.c b/drivers/flash/flash_renesas_ra_ospi_b.c new file mode 100644 index 00000000000..89ad48c28fc --- /dev/null +++ b/drivers/flash/flash_renesas_ra_ospi_b.c @@ -0,0 +1,785 @@ +/* + * Copyright (c) 2024-2025 Renesas Electronics Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT renesas_ra_ospi_b_nor + +#include +#include +#include +#include +#include +#include +#include +#include "flash_renesas_ra_ospi_b.h" + +LOG_MODULE_REGISTER(flash_renesas_ra_ospi_b, CONFIG_FLASH_LOG_LEVEL); + +struct flash_renesas_ra_ospi_b_data { + ospi_b_instance_ctrl_t ospi_b_ctrl; + spi_flash_cfg_t ospi_b_cfg; + ospi_b_timing_setting_t ospi_b_timing_settings; + ospi_b_xspi_command_set_t ospi_b_high_speed_command_set; + ospi_b_extended_cfg_t ospi_b_config_extend; + ospi_b_table_t const xspi_command_set; + struct k_sem sem; +}; + +struct flash_renesas_ra_ospi_b_config { + size_t flash_size; + int protocol; + int data_rate; + uint32_t max_frequency; + const struct device *clock_dev; + struct clock_control_ra_subsys_cfg clock_subsys; + const struct pinctrl_dev_config *pcfg; +}; + +static const struct flash_parameters ospi_b_ra_param = { + .write_block_size = DT_PROP(RA_OSPI_B_NOR_NODE, write_block_size), + .erase_value = ERASE_VALUE, +}; + +static void acquire_device(const struct device *dev) +{ + struct flash_renesas_ra_ospi_b_data *dev_data = dev->data; + + k_sem_take(&dev_data->sem, K_FOREVER); +} + +static void release_device(const struct device *dev) +{ + struct flash_renesas_ra_ospi_b_data *dev_data = dev->data; + + k_sem_give(&dev_data->sem); +} + +static int flash_renesas_ra_ospi_b_wait_operation(ospi_b_instance_ctrl_t *p_ctrl, uint32_t timeout) +{ + spi_flash_status_t status = {RESET_VALUE}; + + status.write_in_progress = true; + while (status.write_in_progress) { + /* Get device status */ + R_OSPI_B_StatusGet(p_ctrl, &status); + if (timeout == RESET_VALUE) { + LOG_DBG("Time out for operation"); + return -EIO; + } + k_sleep(K_USEC(50)); + timeout--; + } + + return 0; +} + +static int flash_renesas_ra_ospi_b_write_enable(ospi_b_instance_ctrl_t *p_ctrl) +{ + spi_flash_direct_transfer_t transfer = {RESET_VALUE}; + fsp_err_t err; + + /* Transfer write enable command */ + transfer = (SPI_FLASH_PROTOCOL_EXTENDED_SPI == p_ctrl->spi_protocol) + ? direct_transfer[TRANSFER_WRITE_ENABLE_SPI] + : direct_transfer[TRANSFER_WRITE_ENABLE_OSPI]; + err = R_OSPI_B_DirectTransfer(p_ctrl, &transfer, SPI_FLASH_DIRECT_TRANSFER_DIR_WRITE); + if (err != FSP_SUCCESS) { + return -EIO; + } + /* Read Status Register */ + transfer = (SPI_FLASH_PROTOCOL_EXTENDED_SPI == p_ctrl->spi_protocol) + ? direct_transfer[TRANSFER_READ_STATUS_SPI] + : direct_transfer[TRANSFER_READ_STATUS_OSPI]; + err = R_OSPI_B_DirectTransfer(p_ctrl, &transfer, SPI_FLASH_DIRECT_TRANSFER_DIR_READ); + /* Check Write Enable bit in Status Register */ + if ((err != FSP_SUCCESS) || (SPI_NOR_WREN_MASK != (transfer.data & SPI_NOR_WREN_MASK))) { + return -EIO; + } + + return 0; +} + +static int flash_renesas_ra_ospi_b_setup_calibrate_data(ospi_b_instance_ctrl_t *p_ctrl) +{ + uint32_t autocalibration_data[] = {0xFFFF0000U, 0x000800FFU, 0x00FFF700U, 0xF700F708U}; + + /* Verify auto-calibration data */ + if (memcmp((uint8_t *)APP_ADDRESS(SECTOR_THREE), &autocalibration_data, + sizeof(autocalibration_data)) != RESET_VALUE) { + fsp_err_t err; + + /* Erase the flash sector that stores auto-calibration data */ + err = R_OSPI_B_Erase(p_ctrl, (uint8_t *)APP_ADDRESS(SECTOR_THREE), + SPI_NOR_SECTOR_SIZE); + if (err != FSP_SUCCESS) { + LOG_DBG("Erase the flash sector Failed"); + return -EIO; + } + + /* Wait until erase operation completes */ + err = flash_renesas_ra_ospi_b_wait_operation(p_ctrl, TIME_ERASE_4K); + if (err != FSP_SUCCESS) { + LOG_DBG("Wait for erase operation completes Failed"); + return -EIO; + } + + /* Write auto-calibration data to the flash */ + err = R_OSPI_B_Write(p_ctrl, (uint8_t *)&autocalibration_data, + (uint8_t *)APP_ADDRESS(SECTOR_THREE), + sizeof(autocalibration_data)); + if (err != FSP_SUCCESS) { + LOG_DBG("Write auto-calibration data Failed"); + return -EIO; + } + + /* Wait until write operation completes */ + err = flash_renesas_ra_ospi_b_wait_operation(p_ctrl, TIME_WRITE); + if (err != FSP_SUCCESS) { + LOG_DBG("Wait for write operation completes Failed"); + return -EIO; + } + } + + return 0; +} + +static int flash_renesas_ra_ospi_b_spi_mode_init(ospi_b_instance_ctrl_t *p_ctrl, + spi_flash_cfg_t *p_cfg) +{ + /* By default, the flash device is in SPI mode, so it is necessary to open the OSPI module + * in SPI mode + */ + spi_flash_direct_transfer_t transfer = {RESET_VALUE}; + fsp_err_t err; + + /* Open OSPI module */ + err = R_OSPI_B_Open(p_ctrl, p_cfg); + if (err != FSP_SUCCESS) { + LOG_DBG("OSPI open Failed"); + return -EIO; + } + + /* DDR sampling window extend */ + R_XSPI->LIOCFGCS_b[p_ctrl->channel].DDRSMPEX = 1; + + /* Switch OSPI module to 1S-1S-1S mode to configure flash device */ + err = R_OSPI_B_SpiProtocolSet(p_ctrl, SPI_FLASH_PROTOCOL_EXTENDED_SPI); + if (err != FSP_SUCCESS) { + LOG_DBG("Switch OSPI module to 1S-1S-1S mode Failed"); + return -EIO; + } + + /* Reset flash device by driving OM_RESET pin */ + R_XSPI->LIOCTL_b.RSTCS0 = 0; + k_sleep(K_USEC(500)); + R_XSPI->LIOCTL_b.RSTCS0 = 1; + k_sleep(K_NSEC(50)); + + /* Transfer write enable command */ + err = flash_renesas_ra_ospi_b_write_enable(p_ctrl); + if (err != FSP_SUCCESS) { + LOG_DBG("Enable write Failed"); + return -EIO; + } + + /* Write to CFR2V to configure Address Byte Length and Memory Array Read Latency */ + transfer = direct_transfer[TRANSFER_WRITE_CFR2V_SPI]; + transfer.address_length = ADDRESS_LENGTH_THREE; + err = R_OSPI_B_DirectTransfer(p_ctrl, &transfer, SPI_FLASH_DIRECT_TRANSFER_DIR_WRITE); + if (err != FSP_SUCCESS) { + LOG_DBG("Configure Address Byte Length and Memory Array Read Latency Failed"); + return -EIO; + } + + /* Transfer write enable command */ + err = flash_renesas_ra_ospi_b_write_enable(p_ctrl); + if (err != FSP_SUCCESS) { + LOG_DBG("Enable write Failed"); + return -EIO; + } + + /* Write to CFR3V to configure Volatile Register Read Latency */ + transfer = direct_transfer[TRANSFER_WRITE_CFR3V_SPI]; + err = R_OSPI_B_DirectTransfer(p_ctrl, &transfer, SPI_FLASH_DIRECT_TRANSFER_DIR_WRITE); + if (err != FSP_SUCCESS) { + LOG_DBG("Configure Volatile Register Read Latency Failed"); + return -EIO; + } + + /* Read back and verify CFR2V register data */ + transfer = direct_transfer[TRANSFER_READ_CFR2V_SPI]; + err = R_OSPI_B_DirectTransfer(p_ctrl, &transfer, SPI_FLASH_DIRECT_TRANSFER_DIR_READ); + if (err != FSP_SUCCESS) { + LOG_DBG("Read back CFR2V register Failed"); + return -EIO; + } + + if (DATA_CFR2V_REGISTER != (uint8_t)transfer.data) { + LOG_DBG("Verify CFR2V register data Failed"); + return -EIO; + } + + /* Read back and verify CFR3V register data */ + transfer = direct_transfer[TRANSFER_READ_CFR3V_SPI]; + err = R_OSPI_B_DirectTransfer(p_ctrl, &transfer, SPI_FLASH_DIRECT_TRANSFER_DIR_READ); + if (err != FSP_SUCCESS) { + LOG_DBG("Read back CFR3V register Failed"); + return -EIO; + } + + if (DATA_CFR3V_REGISTER != (uint8_t)transfer.data) { + LOG_DBG("Verify CFR3V register data Failed"); + return -EIO; + } + + /* Setup calibrate data */ + err = flash_renesas_ra_ospi_b_setup_calibrate_data(p_ctrl); + if (err != FSP_SUCCESS) { + LOG_DBG("Setup calibrate data Failed"); + return -EIO; + } + + return 0; +} + +static int flash_renesas_ra_ospi_b_set_protocol_to_opi(ospi_b_instance_ctrl_t *p_ctrl, + const struct device *dev) +{ + spi_flash_direct_transfer_t transfer = {RESET_VALUE}; + fsp_err_t err; + + /* Transfer write enable command */ + err = flash_renesas_ra_ospi_b_write_enable(p_ctrl); + if (err != FSP_SUCCESS) { + LOG_DBG("Enable write Failed"); + return -EIO; + } + + /* Write to CFR5V Register to Configure flash device interface mode */ + transfer = direct_transfer[TRANSFER_WRITE_CFR5V_SPI]; + transfer.data = DATA_SET_OSPI_CFR5V_REGISTER; + err = R_OSPI_B_DirectTransfer(p_ctrl, &transfer, SPI_FLASH_DIRECT_TRANSFER_DIR_WRITE); + if (err != FSP_SUCCESS) { + LOG_DBG("Configure flash device interface mode Failed"); + return -EIO; + } + + /* Switch OSPI module mode to OPI mode */ + err = R_OSPI_B_SpiProtocolSet(p_ctrl, SPI_FLASH_PROTOCOL_8D_8D_8D); + if (err != FSP_SUCCESS) { + LOG_DBG("Switch to OPI mode Failed"); + return -EIO; + } + + /* Read back and verify CFR5V register data */ + transfer = direct_transfer[TRANSFER_READ_CFR5V_OSPI]; + err = R_OSPI_B_DirectTransfer(p_ctrl, &transfer, SPI_FLASH_DIRECT_TRANSFER_DIR_READ); + if (err != FSP_SUCCESS) { + LOG_DBG("Read back CFR5V register Failed"); + return -EIO; + } + + if (DATA_SET_OSPI_CFR5V_REGISTER != (uint8_t)transfer.data) { + LOG_DBG("CFR5V register data is Incorrect"); + return -EIO; + } + + return 0; +} + +static inline bool flash_renesas_ra_ospi_b_is_valid_address(const struct device *dev, off_t offset, + size_t len) +{ + const struct flash_renesas_ra_ospi_b_config *config = dev->config; + + return (offset >= 0 && (offset < (config->flash_size)) && + (len <= (config->flash_size - offset))); +} + +#if defined(CONFIG_FLASH_EX_OP_ENABLED) +static int flash_renesas_ra_ospi_b_ex_op(const struct device *dev, uint16_t code, + const uintptr_t in, void *out) +{ + struct flash_renesas_ra_ospi_b_data *ospi_b_data = dev->data; + spi_flash_direct_transfer_t transfer = {RESET_VALUE}; + int ret = -ENOTSUP; + + ARG_UNUSED(in); + ARG_UNUSED(out); + + acquire_device(dev); + + if (code == FLASH_EX_OP_RESET) { + fsp_err_t err = flash_renesas_ra_ospi_b_write_enable(&ospi_b_data->ospi_b_ctrl); + + if (err == FSP_SUCCESS) { + /* Enable reset */ + transfer = (SPI_FLASH_PROTOCOL_EXTENDED_SPI == + ospi_b_data->ospi_b_ctrl.spi_protocol) + ? direct_transfer[TRANSFER_RESET_ENABLE_SPI] + : direct_transfer[TRANSFER_RESET_ENABLE_OSPI]; + err = R_OSPI_B_DirectTransfer(&ospi_b_data->ospi_b_ctrl, &transfer, + SPI_FLASH_DIRECT_TRANSFER_DIR_WRITE); + } + + if (err == FSP_SUCCESS) { + /* Reset Register */ + transfer = (SPI_FLASH_PROTOCOL_EXTENDED_SPI == + ospi_b_data->ospi_b_ctrl.spi_protocol) + ? direct_transfer[TRANSFER_RESET_MEM_SPI] + : direct_transfer[TRANSFER_RESET_MEM_OSPI]; + err = R_OSPI_B_DirectTransfer(&ospi_b_data->ospi_b_ctrl, &transfer, + SPI_FLASH_DIRECT_TRANSFER_DIR_WRITE); + } + + if (err == FSP_SUCCESS) { + ret = 0; + } else { + ret = -EIO; + } + } + + release_device(dev); + + return ret; +} +#endif /* CONFIG_FLASH_EX_OP_ENABLED */ + +#if defined(CONFIG_FLASH_PAGE_LAYOUT) +#define SET_PAGES(node_id) \ + {.pages_count = DT_PROP(node_id, pages_count), .pages_size = DT_PROP(node_id, pages_size)}, + +static const struct flash_pages_layout ospi_b_flash_ra_layout[] = { + DT_FOREACH_CHILD(DT_NODELABEL(pages_layout), SET_PAGES)}; + +void flash_renesas_ra_ospi_b_page_layout(const struct device *dev, + const struct flash_pages_layout **layout, + size_t *layout_size) +{ + ARG_UNUSED(dev); + *layout = ospi_b_flash_ra_layout; + *layout_size = ARRAY_SIZE(ospi_b_flash_ra_layout); +} +#endif /* CONFIG_FLASH_PAGE_LAYOUT */ + +#if defined(CONFIG_FLASH_JESD216_API) +static int flash_renesas_ra_ospi_b_read_device_id(ospi_b_instance_ctrl_t *p_ctrl, + uint8_t *const p_id) +{ + spi_flash_direct_transfer_t transfer = {RESET_VALUE}; + fsp_err_t err; + + /* Read and check flash device ID */ + transfer = (SPI_FLASH_PROTOCOL_EXTENDED_SPI == p_ctrl->spi_protocol) + ? direct_transfer[TRANSFER_READ_DEVICE_ID_SPI] + : direct_transfer[TRANSFER_READ_DEVICE_ID_OSPI]; + err = R_OSPI_B_DirectTransfer(p_ctrl, &transfer, SPI_FLASH_DIRECT_TRANSFER_DIR_READ); + if (err != FSP_SUCCESS) { + return -EIO; + } + + /* Get flash device ID */ + memcpy(p_id, &transfer.data, sizeof(transfer.data)); + + return 0; +} + +static int flash_renesas_ra_ospi_b_read_jedec_id(const struct device *dev, uint8_t *id) +{ + struct flash_renesas_ra_ospi_b_data *ospi_b_data = dev->data; + int ret; + + if (id == NULL) { + return -EINVAL; + } + + acquire_device(dev); + + ret = flash_renesas_ra_ospi_b_read_device_id(&ospi_b_data->ospi_b_ctrl, id); + if (ret) { + LOG_ERR("Failed to read jedec id"); + } else { + LOG_INF("Manuf ID = %02x Memory Type = %02x Memory Density = %02x ID Length " + "= %02x\n", + id[0], id[1], id[2], id[3]); + } + + release_device(dev); + + return ret; +} + +static int flash_renesas_ra_ospi_b_sfdp_read(const struct device *dev, off_t offset, void *data, + size_t len) +{ + struct flash_renesas_ra_ospi_b_data *ospi_b_data = dev->data; + spi_flash_direct_transfer_t transfer = {RESET_VALUE}; + size_t size; + fsp_err_t err; + + if (len == 0) { + return 0; + } + + if (data == NULL) { + LOG_ERR("The data buffer is NULL"); + return -EINVAL; + } + acquire_device(dev); + + if (ospi_b_data->ospi_b_ctrl.spi_protocol == SPI_FLASH_PROTOCOL_EXTENDED_SPI) { + transfer = direct_transfer[TRANSFER_READ_SFDP_ID_SPI]; + } else { + transfer = direct_transfer[TRANSFER_READ_SFDP_ID_OSPI]; + } + + while (len > 0) { + size = len > transfer.data_length ? transfer.data_length : len; + transfer.address = offset; + transfer.data_length = size; + + err = R_OSPI_B_DirectTransfer(&ospi_b_data->ospi_b_ctrl, &transfer, + SPI_FLASH_DIRECT_TRANSFER_DIR_READ); + + if (err != FSP_SUCCESS) { + LOG_ERR("Failed to read SFDP id"); + release_device(dev); + return -EIO; + } + + memcpy(data, &transfer.data, size); + + len -= size; + offset += size; + data += size; + } + + release_device(dev); + + return 0; +} +#endif /* CONFIG_FLASH_JESD216_API */ + +static int flash_renesas_ra_ospi_b_erase(const struct device *dev, off_t offset, size_t len) +{ + struct flash_renesas_ra_ospi_b_data *ospi_b_data = dev->data; + const struct flash_renesas_ra_ospi_b_config *config = dev->config; + uint32_t erase_size, erase_timeout; + fsp_err_t err; + struct flash_pages_info page_info_start, page_info_end; + int ret; + + if (!len) { + return 0; + } else if (len % SPI_NOR_SECTOR_SIZE != 0) { + LOG_ERR("Wrong sector size 0x%x", len); + return -EINVAL; + } + + if (!flash_renesas_ra_ospi_b_is_valid_address(dev, offset, len)) { + LOG_ERR("Address or size exceeds expected values: " + "Address 0x%lx, size %u", + (long)offset, len); + return -EINVAL; + } + + /* check offset and len that valid in sector layout */ + ret = flash_get_page_info_by_offs(dev, offset, &page_info_start); + if ((ret != 0) || (offset != page_info_start.start_offset)) { + LOG_ERR("The offset 0x%lx is not aligned with the starting sector", (long)offset); + return -EINVAL; + } + + ret = flash_get_page_info_by_offs(dev, (offset + len), &page_info_end); + if ((ret != 0) || ((offset + len) != page_info_end.start_offset)) { + LOG_ERR("The size %u is not aligned with the ending sector", len); + return -EINVAL; + } + + acquire_device(dev); + + while (len > 0) { + if (offset == 0 && len == config->flash_size) { + /* Chip erase */ + LOG_INF("Chip Erase"); + + erase_size = SPI_FLASH_ERASE_SIZE_CHIP_ERASE; + erase_timeout = UINT32_MAX; + } else if ((offset) <= (off_t)(ospi_b_flash_ra_layout[0].pages_size * + (ospi_b_flash_ra_layout[0].pages_count))) { + erase_size = SPI_NOR_SECTOR_SIZE; + erase_timeout = TIME_ERASE_4K; + } else { + erase_size = SECTOR_SIZE_256K; + erase_timeout = TIME_ERASE_256K; + } + + err = R_OSPI_B_Erase( + &ospi_b_data->ospi_b_ctrl, + (uint8_t *)(BSP_FEATURE_OSPI_B_DEVICE_1_START_ADDRESS + offset), + erase_size); + if (err != FSP_SUCCESS) { + LOG_ERR("Erase at address 0x%lx, size %zu Failed", offset, erase_size); + ret = -EIO; + break; + } + + err = flash_renesas_ra_ospi_b_wait_operation(&ospi_b_data->ospi_b_ctrl, + erase_timeout); + if (err != FSP_SUCCESS) { + LOG_ERR("Wait for erase to finish timeout"); + ret = -EIO; + break; + } + + offset += erase_size; + len -= len < erase_size ? len : erase_size; + } + + release_device(dev); + + return ret; +} + +static int flash_renesas_ra_ospi_b_read(const struct device *dev, off_t offset, void *data, + size_t len) +{ + if (!len) { + return 0; + } + + if (!flash_renesas_ra_ospi_b_is_valid_address(dev, offset, len)) { + LOG_ERR("Address or size exceeds expected values: " + "Address 0x%lx, size %zu", + (long)offset, len); + return -EINVAL; + } + + memcpy(data, (uint8_t *)(BSP_FEATURE_OSPI_B_DEVICE_1_START_ADDRESS) + offset, len); + + return 0; +} + +static int flash_renesas_ra_ospi_b_write(const struct device *dev, off_t offset, const void *data, + size_t len) +{ + struct flash_renesas_ra_ospi_b_data *ospi_b_data = dev->data; + fsp_err_t err; + int ret = 0; + size_t size; + const uint8_t *p_src; + + if (!len) { + return 0; + } + + if (data != NULL) { + p_src = data; + } else { + LOG_ERR("The data buffer is NULL"); + return -EINVAL; + } + + if (!flash_renesas_ra_ospi_b_is_valid_address(dev, offset, len)) { + LOG_ERR("Address or size exceeds expected values: " + "Address 0x%lx, size %zu", + (long)offset, len); + return -EINVAL; + } + + acquire_device(dev); + + while (len > 0) { + size = len > ospi_b_data->ospi_b_cfg.page_size_bytes + ? ospi_b_data->ospi_b_cfg.page_size_bytes + : len; + + err = R_OSPI_B_Write( + &ospi_b_data->ospi_b_ctrl, p_src, + (uint8_t *)(BSP_FEATURE_OSPI_B_DEVICE_1_START_ADDRESS + offset), size); + if (err != FSP_SUCCESS) { + LOG_ERR("Write at address 0x%lx, size %zu", offset, size); + ret = -EIO; + break; + } + + err = flash_renesas_ra_ospi_b_wait_operation(&ospi_b_data->ospi_b_ctrl, TIME_WRITE); + if (err != FSP_SUCCESS) { + LOG_ERR("Wait for write to finish timeout"); + ret = -EIO; + break; + } + + len -= size; + offset += size; + p_src = p_src + size; + } + + release_device(dev); + + return ret; +} + +static const struct flash_parameters * +flash_renesas_ra_ospi_b_get_parameters(const struct device *dev) +{ + ARG_UNUSED(dev); + + return &ospi_b_ra_param; +} + +static int flash_renesas_ra_ospi_b_get_size(const struct device *dev, uint64_t *size) +{ + const struct flash_renesas_ra_ospi_b_config *config = dev->config; + *size = (uint64_t)config->flash_size; + + return 0; +} + +static DEVICE_API(flash, flash_renesas_ra_ospi_b_api) = { + .erase = flash_renesas_ra_ospi_b_erase, + .write = flash_renesas_ra_ospi_b_write, + .read = flash_renesas_ra_ospi_b_read, + .get_parameters = flash_renesas_ra_ospi_b_get_parameters, + .get_size = flash_renesas_ra_ospi_b_get_size, +#ifdef CONFIG_FLASH_PAGE_LAYOUT + .page_layout = flash_renesas_ra_ospi_b_page_layout, +#endif /* CONFIG_FLASH_PAGE_LAYOUT */ +#if defined(CONFIG_FLASH_JESD216_API) + .sfdp_read = flash_renesas_ra_ospi_b_sfdp_read, + .read_jedec_id = flash_renesas_ra_ospi_b_read_jedec_id, +#endif /* CONFIG_FLASH_JESD216_API */ +#if defined(CONFIG_FLASH_EX_OP_ENABLED) + .ex_op = flash_renesas_ra_ospi_b_ex_op, +#endif /* CONFIG_FLASH_EX_OP_ENABLED */ +}; + +static int flash_renesas_ra_ospi_b_init(const struct device *dev) +{ + const struct flash_renesas_ra_ospi_b_config *config = dev->config; + struct flash_renesas_ra_ospi_b_data *data = dev->data; + uint32_t clock_freq; + int ret; + + /* protocol/data_rate of XSPI checking */ + if (config->protocol == XSPI_DUAL_MODE || config->protocol == XSPI_QUAD_MODE) { + LOG_ERR("XSPI mode DUAL|QUAD currently not support"); + return -ENOTSUP; + } else if (((config->protocol != XSPI_OCTO_MODE) && + (config->data_rate == XSPI_DTR_TRANSFER)) || + ((config->protocol == XSPI_OCTO_MODE) && + (config->data_rate == XSPI_STR_TRANSFER))) { + LOG_ERR("XSPI mode SPI/DTR or OPI/STR is not valid"); + return -ENOTSUP; + } + + if (!device_is_ready(config->clock_dev)) { + LOG_ERR("Clock control device not ready"); + return -ENODEV; + } + + ret = clock_control_on(config->clock_dev, (clock_control_subsys_t)&config->clock_subsys); + if (ret < 0) { + LOG_ERR("Could not initialize clock (%d)", ret); + return ret; + } + + ret = clock_control_get_rate(config->clock_dev, + (clock_control_subsys_t)&config->clock_subsys, &clock_freq); + if (ret) { + LOG_ERR("Failed to get clock frequency (%d)", ret); + return ret; + } + + if ((config->protocol == XSPI_SPI_MODE && (config->max_frequency / 2) < clock_freq) || + (config->protocol == XSPI_OCTO_MODE && (config->max_frequency) < clock_freq)) { + LOG_ERR("Invalid clock frequency (%u)", clock_freq); + return -EINVAL; + } + + ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); + if (ret) { + LOG_ERR("Failed to configure pins (%d)", ret); + return ret; + } + + k_sem_init(&data->sem, 1, 1); + + ret = flash_renesas_ra_ospi_b_spi_mode_init(&data->ospi_b_ctrl, &data->ospi_b_cfg); + if (ret) { + LOG_ERR("Init SPI mode failed"); + return ret; + } + + if (config->protocol == XSPI_OCTO_MODE) { + ret = flash_renesas_ra_ospi_b_set_protocol_to_opi(&data->ospi_b_ctrl, dev); + if (ret) { + LOG_ERR("Init OPI mode failed"); + return ret; + } + } + + LOG_INF("Mode: %d Freq: %u", config->protocol, clock_freq); + + return ret; +} + +PINCTRL_DT_DEFINE(DT_INST_PARENT(0)); +static const struct flash_renesas_ra_ospi_b_config ospi_b_config = { + .flash_size = DT_REG_SIZE(RA_OSPI_B_NOR_NODE), + .protocol = DT_PROP(RA_OSPI_B_NOR_NODE, protocol_mode), + .data_rate = DT_PROP(RA_OSPI_B_NOR_NODE, data_rate), + .max_frequency = DT_PROP(RA_OSPI_B_NOR_NODE, ospi_max_frequency), + .clock_dev = DEVICE_DT_GET(DT_CLOCKS_CTLR(DT_INST_PARENT(0))), + .clock_subsys = {.mstp = (uint32_t)DT_CLOCKS_CELL(DT_INST_PARENT(0), mstp), + .stop_bit = (uint32_t)DT_CLOCKS_CELL(DT_INST_PARENT(0), stop_bit)}, + .pcfg = PINCTRL_DT_DEV_CONFIG_GET(DT_INST_PARENT(0))}; + +static struct flash_renesas_ra_ospi_b_data ospi_b_data = { + .ospi_b_timing_settings = {.command_to_command_interval = OSPI_B_COMMAND_INTERVAL_CLOCKS_2, + .cs_pullup_lag = OSPI_B_COMMAND_CS_PULLUP_CLOCKS_NO_EXTENSION, + .cs_pulldown_lead = + OSPI_B_COMMAND_CS_PULLDOWN_CLOCKS_NO_EXTENSION}, + .ospi_b_high_speed_command_set = {.protocol = SPI_FLASH_PROTOCOL_8D_8D_8D, + .command_bytes = OSPI_B_COMMAND_BYTES_2, + .read_command = S28HX512T_SPI_NOR_OCMD_READ, + .page_program_command = S28HX512T_SPI_NOR_OCMD_PP_4B, + .write_enable_command = S28HX512T_SPI_NOR_OCMD_WEN, + .status_command = S28HX512T_SPI_NOR_OCMD_RSR, + .read_dummy_cycles = S28HX512T_SPI_NOR_DUMMY_RD_MEM_OCTAL, + .program_dummy_cycles = S28HX512T_SPI_NOR_DUMMY_WR_OCTAL, + .status_dummy_cycles = + S28HX512T_SPI_NOR_DUMMY_RD_REG_OCTAL, + .p_erase_commands = &high_speed_erase_commands}, + .xspi_command_set = {.p_table = &ospi_b_data.ospi_b_high_speed_command_set, .length = 1U}, + .ospi_b_config_extend = {.channel = OSPI_B_DEVICE_NUMBER_1, + .data_latch_delay_clocks = 0, + .p_timing_settings = &ospi_b_data.ospi_b_timing_settings, + .p_xspi_command_set = &ospi_b_data.xspi_command_set, + .p_autocalibration_preamble_pattern_addr = + APP_ADDRESS(SECTOR_THREE), + .read_dummy_cycles = S28HX512T_SPI_NOR_DUMMY_RD_MEM, + .program_dummy_cycles = S28HX512T_SPI_NOR_DUMMY_WR, + .status_dummy_cycles = 0}, + .ospi_b_cfg = {.spi_protocol = SPI_FLASH_PROTOCOL_1S_1S_1S, + .read_mode = SPI_FLASH_READ_MODE_STANDARD, + .address_bytes = SPI_FLASH_ADDRESS_BYTES_4, + .dummy_clocks = SPI_FLASH_DUMMY_CLOCKS_DEFAULT, + .page_program_address_lines = (spi_flash_data_lines_t)0U, + .page_size_bytes = PAGE_SIZE_BYTE, + .write_status_bit = WRITE_STATUS_BIT, + .write_enable_bit = WRITE_ENABLE_BIT, + .page_program_command = SPI_NOR_CMD_PP_4B, + .write_enable_command = SPI_NOR_CMD_WREN, + .status_command = SPI_NOR_CMD_RDSR, + .read_command = SPI_NOR_CMD_READ_FAST, + .xip_enter_command = 0U, + .xip_exit_command = 0U, + .erase_command_list_length = ERASE_COMMAND_LENGTH(erase_command_list), + .p_erase_command_list = &erase_command_list[0], + .p_extend = &ospi_b_data.ospi_b_config_extend}}; + +DEVICE_DT_INST_DEFINE(0, flash_renesas_ra_ospi_b_init, NULL, &ospi_b_data, &ospi_b_config, + POST_KERNEL, CONFIG_FLASH_INIT_PRIORITY, &flash_renesas_ra_ospi_b_api); diff --git a/drivers/flash/flash_renesas_ra_ospi_b.h b/drivers/flash/flash_renesas_ra_ospi_b.h new file mode 100644 index 00000000000..b6316bb15b2 --- /dev/null +++ b/drivers/flash/flash_renesas_ra_ospi_b.h @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2024 Renesas Electronics Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_FLASH_RENESAS_RA_OSPI_B_H_ +#define ZEPHYR_DRIVERS_FLASH_RENESAS_RA_OSPI_B_H_ + +#include +#include +#include +#include +#include +#include "spi_nor_s28hx512t.h" +#include "spi_nor.h" + +/* Device node */ +#define RA_OSPI_B_NOR_NODE DT_INST(0, renesas_ra_ospi_b_nor) + +/* Flash erase value */ +#define ERASE_VALUE (0xff) + +/* Page size */ +#define PAGE_SIZE_BYTE 64 + +/* Flash device sector size */ +#define SECTOR_SIZE_128K (0x20000) +#define SECTOR_SIZE_256K (0x40000) + +/* Flash device timing */ +#define TIME_ERASE_256K (16000) +#define TIME_ERASE_4K (1000U) +#define TIME_WRITE (1000U) + +/* Bit status */ +#define WRITE_STATUS_BIT (0) +#define WRITE_ENABLE_BIT (1) + +/* Calibration sector */ +#define SECTOR_THREE (2U) + +/* Command length */ +#define COMMAND_LENGTH_SPI (1U) +#define COMMAND_LENGTH_OSPI (2U) + +/* Transfer address length */ +#define ADDRESS_DUMMY (0U) +#define ADDRESS_LENGTH_ZERO (0U) +#define ADDRESS_LENGTH_THREE (3U) +#define ADDRESS_LENGTH_FOUR (4U) + +/* Transfer data length */ +#define DATA_DUMMY (0U) +#define DATA_LENGTH_ZERO (0U) +#define DATA_LENGTH_ONE (1U) +#define DATA_LENGTH_TWO (2U) +#define DATA_LENGTH_FOUR (4U) +#define DATA_LENGTH_EIGHT (8U) + +/* Configure flash device */ +#define DATA_CFR2V_REGISTER (0x83) +#define DATA_CFR3V_REGISTER (0x40) +#define DATA_SET_SPI_CFR5V_REGISTER (0x40) +#define DATA_SET_OSPI_CFR5V_REGISTER (0x43) + +/* Flash device address space mapping */ +#define APP_ADDRESS(sector_no) \ + ((uint8_t *)(BSP_FEATURE_OSPI_B_DEVICE_1_START_ADDRESS + \ + ((sector_no) * SPI_NOR_SECTOR_SIZE))) + +/* Erase command */ +static spi_flash_erase_command_t erase_command_list[] = { + {.command = SPI_NOR_CMD_SE_4B, .size = SPI_NOR_SECTOR_SIZE}, + {.command = S28HX512T_SPI_NOR_CMD_SE_256KB, .size = SECTOR_SIZE_256K}, + {.command = S28HX512T_SPI_NOR_CMD_ERCHP, .size = SPI_FLASH_ERASE_SIZE_CHIP_ERASE}}; + +static spi_flash_erase_command_t high_speed_erase_command_list[] = { + {.command = S28HX512T_SPI_NOR_OCMD_SE_4KB, .size = SPI_NOR_SECTOR_SIZE}, + {.command = S28HX512T_SPI_NOR_OCMD_SE_256KB, .size = SECTOR_SIZE_256K}, + {.command = S28HX512T_SPI_NOR_OCMD_ERCHP, .size = SPI_FLASH_ERASE_SIZE_CHIP_ERASE}}; + +/* Erase command length */ +#define ERASE_COMMAND_LENGTH(arr) (sizeof(arr) / sizeof((arr)[0])) + +static ospi_b_table_t const high_speed_erase_commands = { + .p_table = &high_speed_erase_command_list, + .length = ERASE_COMMAND_LENGTH(high_speed_erase_command_list), +}; + +/* Reset value */ +#define RESET_VALUE (0x00) + +/* Transfer table */ +typedef enum e_transfer { + TRANSFER_WRITE_ENABLE_SPI = 0, + TRANSFER_WRITE_CFR2V_SPI, + TRANSFER_WRITE_CFR3V_SPI, + TRANSFER_WRITE_CFR5V_SPI, + TRANSFER_READ_STATUS_SPI, + TRANSFER_READ_CFR2V_SPI, + TRANSFER_READ_CFR3V_SPI, + TRANSFER_READ_CFR5V_SPI, + TRANSFER_READ_DEVICE_ID_SPI, + TRANSFER_READ_SFDP_ID_SPI, + TRANSFER_RESET_ENABLE_SPI, + TRANSFER_RESET_MEM_SPI, + + TRANSFER_WRITE_ENABLE_OSPI, + TRANSFER_WRITE_CFR2V_OSPI, + TRANSFER_WRITE_CFR3V_OSPI, + TRANSFER_WRITE_CFR5V_OSPI, + TRANSFER_READ_STATUS_OSPI, + TRANSFER_READ_CFR2V_OSPI, + TRANSFER_READ_CFR3V_OSPI, + TRANSFER_READ_CFR5V_OSPI, + TRANSFER_READ_DEVICE_ID_OSPI, + TRANSFER_READ_SFDP_ID_OSPI, + TRANSFER_RESET_ENABLE_OSPI, + TRANSFER_RESET_MEM_OSPI, + TRANSFER_MAX +} transfer_t; + +spi_flash_direct_transfer_t direct_transfer[TRANSFER_MAX] = { + /* Transfer structure for SPI mode */ + [TRANSFER_WRITE_ENABLE_SPI] = {.command = SPI_NOR_CMD_WREN, + .address = ADDRESS_DUMMY, + .data = DATA_DUMMY, + .command_length = COMMAND_LENGTH_SPI, + .address_length = ADDRESS_LENGTH_ZERO, + .data_length = DATA_LENGTH_ZERO, + .dummy_cycles = S28HX512T_SPI_NOR_DUMMY_WR}, + [TRANSFER_WRITE_CFR2V_SPI] = {.command = S28HX512T_SPI_NOR_CMD_WR_WRARG, + .address = S28HX512T_SPI_NOR_CFR2V_ADDR, + .data = DATA_CFR2V_REGISTER, + .command_length = COMMAND_LENGTH_SPI, + .address_length = ADDRESS_LENGTH_FOUR, + .data_length = DATA_LENGTH_ONE, + .dummy_cycles = S28HX512T_SPI_NOR_DUMMY_WR}, + [TRANSFER_WRITE_CFR3V_SPI] = {.command = S28HX512T_SPI_NOR_CMD_WR_WRARG, + .address = S28HX512T_SPI_NOR_CFR3V_ADDR, + .data = DATA_CFR3V_REGISTER, + .command_length = COMMAND_LENGTH_SPI, + .address_length = ADDRESS_LENGTH_FOUR, + .data_length = DATA_LENGTH_ONE, + .dummy_cycles = S28HX512T_SPI_NOR_DUMMY_WR}, + [TRANSFER_WRITE_CFR5V_SPI] = {.command = S28HX512T_SPI_NOR_CMD_WR_WRARG, + .address = S28HX512T_SPI_NOR_CFR5V_ADDR, + .data = DATA_DUMMY, + .command_length = COMMAND_LENGTH_SPI, + .address_length = ADDRESS_LENGTH_FOUR, + .data_length = DATA_LENGTH_ONE, + .dummy_cycles = S28HX512T_SPI_NOR_DUMMY_WR}, + [TRANSFER_READ_STATUS_SPI] = {.command = SPI_NOR_CMD_RDSR, + .address = ADDRESS_DUMMY, + .data = DATA_DUMMY, + .command_length = COMMAND_LENGTH_SPI, + .address_length = ADDRESS_LENGTH_ZERO, + .data_length = DATA_LENGTH_ONE, + .dummy_cycles = S28HX512T_SPI_NOR_DUMMY_RD_STATUS}, + [TRANSFER_READ_CFR2V_SPI] = {.command = S28HX512T_SPI_NOR_CMD_RREG, + .address = S28HX512T_SPI_NOR_CFR2V_ADDR, + .data = DATA_DUMMY, + .command_length = COMMAND_LENGTH_SPI, + .address_length = ADDRESS_LENGTH_FOUR, + .data_length = DATA_LENGTH_ONE, + .dummy_cycles = S28HX512T_SPI_NOR_DUMMY_RD_REG}, + [TRANSFER_READ_CFR3V_SPI] = {.command = S28HX512T_SPI_NOR_CMD_RREG, + .address = S28HX512T_SPI_NOR_CFR3V_ADDR, + .data = DATA_DUMMY, + .command_length = COMMAND_LENGTH_SPI, + .address_length = ADDRESS_LENGTH_FOUR, + .data_length = DATA_LENGTH_ONE, + .dummy_cycles = S28HX512T_SPI_NOR_DUMMY_RD_REG}, + [TRANSFER_READ_CFR5V_SPI] = {.command = S28HX512T_SPI_NOR_CMD_RREG, + .address = S28HX512T_SPI_NOR_CFR5V_ADDR, + .data = DATA_DUMMY, + .command_length = COMMAND_LENGTH_SPI, + .address_length = ADDRESS_LENGTH_FOUR, + .data_length = DATA_LENGTH_ONE, + .dummy_cycles = S28HX512T_SPI_NOR_DUMMY_RD_REG}, + [TRANSFER_READ_DEVICE_ID_SPI] = {.command = SPI_NOR_CMD_RDID, + .address = ADDRESS_DUMMY, + .data = DATA_DUMMY, + .command_length = COMMAND_LENGTH_SPI, + .address_length = ADDRESS_LENGTH_ZERO, + .data_length = DATA_LENGTH_FOUR, + .dummy_cycles = S28HX512T_SPI_NOR_DUMMY_RD_STATUS}, + [TRANSFER_READ_SFDP_ID_SPI] = {.command = S28HX512T_SPI_NOR_CMD_RSFDPID, + .address = ADDRESS_DUMMY, + .data = DATA_DUMMY, + .command_length = COMMAND_LENGTH_SPI, + .address_length = ADDRESS_LENGTH_THREE, + .data_length = DATA_LENGTH_EIGHT, + .dummy_cycles = S28HX512T_SPI_NOR_DUMMY_RD_SFDP}, + [TRANSFER_RESET_ENABLE_SPI] = {.command = SPI_NOR_CMD_RESET_EN, + .address = ADDRESS_DUMMY, + .data = DATA_DUMMY, + .command_length = COMMAND_LENGTH_SPI, + .address_length = ADDRESS_LENGTH_ZERO, + .data_length = DATA_LENGTH_ZERO, + .dummy_cycles = S28HX512T_SPI_NOR_DUMMY_WR}, + [TRANSFER_RESET_MEM_SPI] = {.command = SPI_NOR_CMD_RESET_MEM, + .address = ADDRESS_DUMMY, + .data = DATA_DUMMY, + .command_length = COMMAND_LENGTH_SPI, + .address_length = ADDRESS_LENGTH_ZERO, + .data_length = DATA_LENGTH_ZERO, + .dummy_cycles = S28HX512T_SPI_NOR_DUMMY_WR}, + /* Transfer structure for OPI mode */ + [TRANSFER_WRITE_ENABLE_OSPI] = {.command = S28HX512T_SPI_NOR_OCMD_WEN, + .address = ADDRESS_DUMMY, + .data = DATA_DUMMY, + .command_length = COMMAND_LENGTH_OSPI, + .address_length = ADDRESS_LENGTH_ZERO, + .data_length = DATA_LENGTH_ZERO, + .dummy_cycles = S28HX512T_SPI_NOR_DUMMY_WR_OCTAL}, + [TRANSFER_WRITE_CFR2V_OSPI] = {.command = S28HX512T_SPI_NOR_OCMD_WR_REG2, + .address = S28HX512T_SPI_NOR_CFR2V_ADDR, + .data = DATA_CFR2V_REGISTER, + .command_length = COMMAND_LENGTH_OSPI, + .address_length = ADDRESS_LENGTH_FOUR, + .data_length = DATA_LENGTH_TWO, + .dummy_cycles = S28HX512T_SPI_NOR_DUMMY_WR_OCTAL}, + [TRANSFER_WRITE_CFR3V_OSPI] = {.command = S28HX512T_SPI_NOR_OCMD_WR_REG2, + .address = S28HX512T_SPI_NOR_CFR3V_ADDR, + .data = DATA_CFR3V_REGISTER, + .command_length = COMMAND_LENGTH_OSPI, + .address_length = ADDRESS_LENGTH_FOUR, + .data_length = DATA_LENGTH_TWO, + .dummy_cycles = S28HX512T_SPI_NOR_DUMMY_WR_OCTAL}, + [TRANSFER_WRITE_CFR5V_OSPI] = {.command = S28HX512T_SPI_NOR_OCMD_WR_REG2, + .address = S28HX512T_SPI_NOR_CFR5V_ADDR, + .data = DATA_DUMMY, + .command_length = COMMAND_LENGTH_OSPI, + .address_length = ADDRESS_LENGTH_FOUR, + .data_length = DATA_LENGTH_TWO, + .dummy_cycles = S28HX512T_SPI_NOR_DUMMY_WR_OCTAL}, + [TRANSFER_READ_STATUS_OSPI] = {.command = S28HX512T_SPI_NOR_OCMD_RSR, + .address = ADDRESS_DUMMY, + .data = DATA_DUMMY, + .command_length = COMMAND_LENGTH_OSPI, + .address_length = ADDRESS_LENGTH_FOUR, + .data_length = DATA_LENGTH_TWO, + .dummy_cycles = S28HX512T_SPI_NOR_DUMMY_RD_STATUS_OCTAL}, + [TRANSFER_READ_CFR2V_OSPI] = {.command = S28HX512T_SPI_NOR_OCMD_RSR, + .address = S28HX512T_SPI_NOR_CFR2V_ADDR, + .data = DATA_DUMMY, + .command_length = COMMAND_LENGTH_OSPI, + .address_length = ADDRESS_LENGTH_FOUR, + .data_length = DATA_LENGTH_TWO, + .dummy_cycles = S28HX512T_SPI_NOR_DUMMY_RD_REG_OCTAL}, + [TRANSFER_READ_CFR3V_OSPI] = {.command = S28HX512T_SPI_NOR_OCMD_RSR, + .address = S28HX512T_SPI_NOR_CFR3V_ADDR, + .data = DATA_DUMMY, + .command_length = COMMAND_LENGTH_OSPI, + .address_length = ADDRESS_LENGTH_FOUR, + .data_length = DATA_LENGTH_TWO, + .dummy_cycles = S28HX512T_SPI_NOR_DUMMY_RD_REG_OCTAL}, + [TRANSFER_READ_CFR5V_OSPI] = {.command = S28HX512T_SPI_NOR_OCMD_RREG, + .address = S28HX512T_SPI_NOR_CFR5V_ADDR, + .data = DATA_DUMMY, + .command_length = COMMAND_LENGTH_OSPI, + .address_length = ADDRESS_LENGTH_FOUR, + .data_length = DATA_LENGTH_TWO, + .dummy_cycles = S28HX512T_SPI_NOR_DUMMY_RD_REG_OCTAL}, + [TRANSFER_READ_DEVICE_ID_OSPI] = {.command = S28HX512T_SPI_NOR_OCMD_RDID, + .address = ADDRESS_DUMMY, + .data = DATA_DUMMY, + .command_length = COMMAND_LENGTH_OSPI, + .address_length = ADDRESS_LENGTH_FOUR, + .data_length = DATA_LENGTH_FOUR, + .dummy_cycles = S28HX512T_SPI_NOR_DUMMY_RD_STATUS_OCTAL}, + [TRANSFER_READ_SFDP_ID_OSPI] = {.command = S28HX512T_SPI_NOR_OCMD_RSFDPID, + .address = ADDRESS_DUMMY, + .data = DATA_DUMMY, + .command_length = COMMAND_LENGTH_OSPI, + .address_length = ADDRESS_LENGTH_FOUR, + .data_length = DATA_LENGTH_EIGHT, + .dummy_cycles = S28HX512T_SPI_NOR_DUMMY_RD_SFDP_OCTAL}, + [TRANSFER_RESET_ENABLE_OSPI] = {.command = S28HX512T_SPI_NOR_OCMD_RST_EN, + .address = ADDRESS_DUMMY, + .data = DATA_DUMMY, + .command_length = COMMAND_LENGTH_OSPI, + .address_length = ADDRESS_LENGTH_ZERO, + .data_length = DATA_LENGTH_ZERO, + .dummy_cycles = S28HX512T_SPI_NOR_DUMMY_WR_OCTAL}, + [TRANSFER_RESET_MEM_OSPI] = {.command = S28HX512T_SPI_NOR_OCMD_RST_MEM, + .address = ADDRESS_DUMMY, + .data = DATA_DUMMY, + .command_length = COMMAND_LENGTH_OSPI, + .address_length = ADDRESS_LENGTH_ZERO, + .data_length = DATA_LENGTH_ZERO, + .dummy_cycles = S28HX512T_SPI_NOR_DUMMY_WR_OCTAL}, +}; + +#endif /* ZEPHYR_DRIVERS_FLASH_RENESAS_RA_OSPI_B_H_ */ diff --git a/drivers/flash/spi_nor_s28hx512t.h b/drivers/flash/spi_nor_s28hx512t.h new file mode 100644 index 00000000000..138fc824af4 --- /dev/null +++ b/drivers/flash/spi_nor_s28hx512t.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024-2025 Renesas Electronics Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __SPI_NOR_S28HX512T_H__ +#define __SPI_NOR_S28HX512T_H__ + +#define S28HX512T_SPI_NOR_CMD_WR_WRARG 0x71 /* Write Any Register */ +#define S28HX512T_SPI_NOR_CMD_RSFDPID 0x5A /* Read SFDP ID */ +#define S28HX512T_SPI_NOR_CMD_RREG 0x65 /* Read Any Register */ +#define S28HX512T_SPI_NOR_CMD_SE_256KB 0xDC /* Sector Erase 256KB */ +#define S28HX512T_SPI_NOR_CMD_ERCHP 0x60 /* Erase Chip */ + +#define S28HX512T_SPI_NOR_OCMD_WEN 0x0606 /* Octal Write enable */ +#define S28HX512T_SPI_NOR_OCMD_RSR 0x0505 /* Octal Read status register */ +#define S28HX512T_SPI_NOR_OCMD_WR_REG2 0x7171 /* Octal Write config register 2 */ +#define S28HX512T_SPI_NOR_OCMD_RDID 0x9F9F /* Octal Read JEDEC ID */ +#define S28HX512T_SPI_NOR_OCMD_RSFDPID 0x5A5A /* Octal Read SFDP ID */ +#define S28HX512T_SPI_NOR_OCMD_RREG 0x6565 /* Octal Read Any Register */ +#define S28HX512T_SPI_NOR_OCMD_PP_4B 0x1212 /* Octal Page Program 4 Byte Address */ +#define S28HX512T_SPI_NOR_OCMD_READ 0xEEEE /* Octal Read data */ +#define S28HX512T_SPI_NOR_OCMD_RST_EN 0x6666 /* Octal Reset Enable */ +#define S28HX512T_SPI_NOR_OCMD_RST_MEM 0x9999 /* Reset Memory */ +#define S28HX512T_SPI_NOR_OCMD_SE_4KB 0x2121 /* Octal Sector Erase 4Kb address */ +#define S28HX512T_SPI_NOR_OCMD_SE_256KB 0xDCDC /* Octal Sector Erase 256Kb address */ +#define S28HX512T_SPI_NOR_OCMD_ERCHP 0x6060 /* Octal Erase Chip */ + +#define S28HX512T_SPI_NOR_DUMMY_WR 0U +#define S28HX512T_SPI_NOR_DUMMY_WR_OCTAL 0U +#define S28HX512T_SPI_NOR_DUMMY_RD_STATUS 0U +#define S28HX512T_SPI_NOR_DUMMY_RD_STATUS_OCTAL 4U +#define S28HX512T_SPI_NOR_DUMMY_RD_REG 1U +#define S28HX512T_SPI_NOR_DUMMY_RD_REG_OCTAL 4U +#define S28HX512T_SPI_NOR_DUMMY_RD_MEM 3U +#define S28HX512T_SPI_NOR_DUMMY_RD_MEM_OCTAL 10U +#define S28HX512T_SPI_NOR_DUMMY_RD_SFDP 8U +#define S28HX512T_SPI_NOR_DUMMY_RD_SFDP_OCTAL 8U + +#define S28HX512T_SPI_NOR_CFR1V_ADDR 0x00800002 +#define S28HX512T_SPI_NOR_CFR2V_ADDR 0x00800003 +#define S28HX512T_SPI_NOR_CFR3V_ADDR 0x00800004 +#define S28HX512T_SPI_NOR_CFR4V_ADDR 0x00800005 +#define S28HX512T_SPI_NOR_CFR5V_ADDR 0x00800006 + +#endif /*__SPI_NOR_S28HX512T_H__*/ diff --git a/dts/bindings/flash_controller/renesas,ra-ospi-b-nor.yaml b/dts/bindings/flash_controller/renesas,ra-ospi-b-nor.yaml new file mode 100644 index 00000000000..0a4c53b02cc --- /dev/null +++ b/dts/bindings/flash_controller/renesas,ra-ospi-b-nor.yaml @@ -0,0 +1,70 @@ +# Copyright (c) 2024 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +description: Renesas RA OSPI FLASH + +compatible: "renesas,ra-ospi-b-nor" + +include: [base.yaml] + +on-bus: ospi + +properties: + reg: + required: true + description: Flash Memory base address and size in bytes + + size: + type: int + required: true + description: Flash Memory size + + protocol-mode: + type: int + required: true + description: | + The width and rate of XSPI bus to which flash memory is connected. + + Possible values are : + - XSPI_SPI_MODE <1> = SPI mode on 1 data line + - XSPI_DUAL_MODE <2> = Dual mode on 2 data lines + - XSPI_QUAD_MODE <4> = Quad mode on 4 data lines + - XSPI_OCTO_MODE <8> = Octo mode on 8 data lines + enum: + - 1 + - 2 + - 4 + - 8 + + data-rate: + type: int + required: true + description: | + The SPI data Rate is STR or DTR + + Possible values are : + - XSPI_STR_TRANSFER <1> = Single Rate Transfer + - XSPI_DTR_TRANSFER <2> = Dual Rate Transfer (only with XSPI_OCTO_MODE) + + ospi-max-frequency: + type: int + required: true + description: Max frequency input on OSPI + + write-block-size: + type: int + description: Address alignment required by flash write operations + +child-binding: + description: OSPI Flash page layout description + child-binding: + description: Individual flash page layout entry + properties: + pages-count: + description: Number of consecutive pages with size pages-size bytes + type: int + required: true + + pages-size: + type: int + required: true diff --git a/dts/bindings/ospi/renesas,ra-ospi-b.yaml b/dts/bindings/ospi/renesas,ra-ospi-b.yaml new file mode 100644 index 00000000000..3484dd524eb --- /dev/null +++ b/dts/bindings/ospi/renesas,ra-ospi-b.yaml @@ -0,0 +1,23 @@ +# Copyright (c) 2024 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +description: Renesas RA OSPI + +compatible: "renesas,ra-ospi-b" + +include: [base.yaml, pinctrl-device.yaml] + +bus: ospi + +properties: + reg: + required: true + + pinctrl-0: + required: true + + pinctrl-names: + required: true + + clocks: + required: true diff --git a/modules/Kconfig.renesas b/modules/Kconfig.renesas index 1a3ec169072..86498ce24fb 100644 --- a/modules/Kconfig.renesas +++ b/modules/Kconfig.renesas @@ -181,6 +181,11 @@ config USE_RA_FSP_LPM help Enable RA FSP LPM driver +config USE_RA_FSP_OSPI_B_NOR_FLASH + bool + help + Enable RA FSP Octal-SPI driver + endif # HAS_RENESAS_RA_FSP if HAS_RENESAS_RZ_FSP