From 8ef0792eec4c0a20a8a07693d9a22666bb0ad1bb Mon Sep 17 00:00:00 2001 From: Swift Tian Date: Mon, 21 Apr 2025 12:04:33 +0800 Subject: [PATCH] drivers: mspi: add APMemory APS Z8 pSRAM driver The APS Z8 driver would just support APS51216BA for now. Signed-off-by: Swift Tian --- boards/ambiq/apollo510_evb/Kconfig.defconfig | 7 + boards/ambiq/apollo510_evb/apollo510_evb.dts | 41 ++ drivers/memc/CMakeLists.txt | 3 +- drivers/memc/Kconfig.mspi | 9 +- drivers/memc/memc_mspi_aps_z8.c | 619 ++++++++++++++++++ drivers/memc/memc_mspi_aps_z8.h | 115 ++++ dts/bindings/mtd/mspi-aps-z8.yaml | 8 + samples/drivers/memc/CMakeLists.txt | 2 +- .../drivers/memc/boards/apollo510_evb.conf | 6 + .../drivers/memc/boards/apollo510_evb.overlay | 19 + samples/drivers/memc/src/main.c | 14 +- 11 files changed, 837 insertions(+), 6 deletions(-) create mode 100644 drivers/memc/memc_mspi_aps_z8.c create mode 100644 drivers/memc/memc_mspi_aps_z8.h create mode 100644 dts/bindings/mtd/mspi-aps-z8.yaml create mode 100644 samples/drivers/memc/boards/apollo510_evb.conf create mode 100644 samples/drivers/memc/boards/apollo510_evb.overlay diff --git a/boards/ambiq/apollo510_evb/Kconfig.defconfig b/boards/ambiq/apollo510_evb/Kconfig.defconfig index 8fd84d081a0..423e225185a 100644 --- a/boards/ambiq/apollo510_evb/Kconfig.defconfig +++ b/boards/ambiq/apollo510_evb/Kconfig.defconfig @@ -19,4 +19,11 @@ config MSPI_INIT_PRIORITY endif # MSPI +if MEMC + +config MEMC_INIT_PRIORITY + default 50 + +endif # MEMC + endif # BOARD_APOLLO510_EVB diff --git a/boards/ambiq/apollo510_evb/apollo510_evb.dts b/boards/ambiq/apollo510_evb/apollo510_evb.dts index 4f6114670a0..e38a0aa22b8 100644 --- a/boards/ambiq/apollo510_evb/apollo510_evb.dts +++ b/boards/ambiq/apollo510_evb/apollo510_evb.dts @@ -120,6 +120,47 @@ status = "okay"; }; +&mspi0 { + pinctrl-0 = <&mspi0_default>; + pinctrl-1 = <&mspi0_sleep>; + pinctrl-2 = <&mspi0_psram>; + pinctrl-names = "default","sleep","psram"; + status = "disabled"; + zephyr,pm-device-runtime-auto; + + ce-gpios = <&gpio192_223 7 GPIO_ACTIVE_LOW>; + + cmdq-buffer-location = "SRAM_NO_CACHE"; + cmdq-buffer-size = <256>; + + ambiq,apmemory; + + dqs-support; + + aps51216ba: aps_z8@0 { + compatible = "ambiq,mspi-device", "mspi-aps-z8"; + size = ; + reg = <0>; + status = "disabled"; + mspi-max-frequency = <125000000>; + mspi-io-mode = "MSPI_IO_MODE_HEX_8_8_16"; + mspi-data-rate = "MSPI_DATA_RATE_S_D_D"; + mspi-hardware-ce-num = <0>; + mspi-dqs-enable; + read-command = <0x20>; + write-command = <0xA0>; + command-length = "INSTR_1_BYTE"; + address-length = "ADDR_4_BYTE"; + rx-dummy = <7>; + tx-dummy = <8>; + xip-config = <1 0 DT_SIZE_M(64) 0>; + ce-break-config = <1024 4>; + ambiq,timing-config-mask = <0x62>; + ambiq,timing-config = <8 7 1 0 0 3 10 0>; + zephyr,pm-device-runtime-auto; + }; +}; + &gpio0_31 { status = "okay"; }; diff --git a/drivers/memc/CMakeLists.txt b/drivers/memc/CMakeLists.txt index c1ec964e427..acf307ab583 100644 --- a/drivers/memc/CMakeLists.txt +++ b/drivers/memc/CMakeLists.txt @@ -15,7 +15,8 @@ if((DEFINED CONFIG_FLASH_MCUX_FLEXSPI_XIP) AND (DEFINED CONFIG_FLASH)) endif() zephyr_library_sources_ifdef(CONFIG_MEMC_MSPI_APS6404L memc_mspi_aps6404l.c) -zephyr_library_include_directories_ifdef(CONFIG_MEMC_MSPI_APS6404L ${ZEPHYR_BASE}/drivers/mspi) +zephyr_library_sources_ifdef(CONFIG_MEMC_MSPI_APS_Z8 memc_mspi_aps_z8.c) +zephyr_library_include_directories_ifdef(CONFIG_MEMC_MSPI ${ZEPHYR_BASE}/drivers/mspi) zephyr_library_sources_ifdef(CONFIG_MEMC_NXP_S32_QSPI memc_nxp_s32_qspi.c) zephyr_library_sources_ifdef(CONFIG_MEMC_RENESAS_RA_SDRAM memc_renesas_ra_sdram.c) diff --git a/drivers/memc/Kconfig.mspi b/drivers/memc/Kconfig.mspi index 5f295007625..e86a8c211c3 100644 --- a/drivers/memc/Kconfig.mspi +++ b/drivers/memc/Kconfig.mspi @@ -1,4 +1,4 @@ -# Copyright (c) 2024, Ambiq Micro Inc. +# Copyright (c) 2025, Ambiq Micro Inc. # SPDX-License-Identifier: Apache-2.0 menu "MSPI MEMC device driver" @@ -16,4 +16,11 @@ config MEMC_MSPI_APS6404L select MEMC_MSPI select MSPI_AMBIQ_CONTROLLER if SOC_FAMILY_AMBIQ +config MEMC_MSPI_APS_Z8 + bool "MSPI AP Memory gen Z8 pSRAM driver" + default y + depends on DT_HAS_MSPI_APS_Z8_ENABLED + select MEMC_MSPI + select MSPI_AMBIQ_CONTROLLER if SOC_FAMILY_AMBIQ + endmenu diff --git a/drivers/memc/memc_mspi_aps_z8.c b/drivers/memc/memc_mspi_aps_z8.c new file mode 100644 index 00000000000..fe1a1299b37 --- /dev/null +++ b/drivers/memc/memc_mspi_aps_z8.c @@ -0,0 +1,619 @@ +/* + * Copyright (c) 2025, Ambiq Micro Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT mspi_aps_z8 +#include +#include +#include +#include +#include +#include +#include +#include +#include "memc_mspi_aps_z8.h" +#if CONFIG_SOC_FAMILY_AMBIQ +#include "mspi_ambiq.h" +typedef struct mspi_ambiq_timing_cfg mspi_timing_cfg; +typedef enum mspi_ambiq_timing_param mspi_timing_param; +#else +typedef struct mspi_timing_cfg mspi_timing_cfg; +typedef enum mspi_timing_param mspi_timing_param; +#endif + +LOG_MODULE_REGISTER(memc_mspi_aps_z8, CONFIG_MEMC_LOG_LEVEL); + +#define APM_VENDOR_ID 0xD + +#define APS_Z8_SYNC_WRITE 0x80 +#define APS_Z8_SYNC_READ 0x00 +#define APS_Z8_LINEAR_BURST_WRITE 0xA0 +#define APS_Z8_LINEAR_BURST_READ 0x20 +#define APS_Z8_GLOBAL_RESET 0xFF +#define APS_Z8_WRITE_REGISTER 0xC0 +#define APS_Z8_READ_REGISTER 0x40 + +struct memc_mspi_aps_z8_config { + uint32_t port; + uint32_t mem_size; + + const struct device *bus; + struct mspi_dev_id dev_id; + struct mspi_dev_cfg octal_cfg; + struct mspi_dev_cfg tar_dev_cfg; + + MSPI_XIP_CFG_STRUCT_DECLARE(tar_xip_cfg) + MSPI_SCRAMBLE_CFG_STRUCT_DECLARE(tar_scramble_cfg) + MSPI_TIMING_CFG_STRUCT_DECLARE(tar_timing_cfg) + MSPI_TIMING_PARAM_DECLARE(timing_cfg_mask) + + bool sw_multi_periph; + bool pm_dev_rt_auto; +}; + +struct memc_mspi_aps_z8_data { + struct memc_mspi_aps_z8_reg regs; + struct mspi_dev_cfg dev_cfg; + struct mspi_xip_cfg xip_cfg; + struct mspi_scramble_cfg scramble_cfg; + mspi_timing_cfg timing_cfg; + struct mspi_xfer trans; + struct mspi_xfer_packet packet; + + struct k_sem lock; + uint16_t dummy; +}; + +static int memc_mspi_aps_z8_command_write(const struct device *psram, uint8_t cmd, + uint32_t addr, uint8_t *wdata, uint32_t length) +{ + const struct memc_mspi_aps_z8_config *cfg = psram->config; + struct memc_mspi_aps_z8_data *data = psram->data; + int ret; + + data->packet.dir = MSPI_TX; + data->packet.cmd = cmd; + data->packet.address = addr; + data->packet.data_buf = wdata; + data->packet.num_bytes = length; + + data->trans.async = false; + data->trans.xfer_mode = MSPI_PIO; + data->trans.tx_dummy = 0; + data->trans.rx_dummy = data->dev_cfg.rx_dummy; + data->trans.cmd_length = data->dev_cfg.cmd_length; + data->trans.addr_length = data->dev_cfg.addr_length; + data->trans.hold_ce = false; + data->trans.packets = &data->packet; + data->trans.num_packet = 1; + data->trans.timeout = 10; + + ret = mspi_transceive(cfg->bus, &cfg->dev_id, (const struct mspi_xfer *)&data->trans); + if (ret) { + LOG_ERR("MSPI write transaction failed with code: %d/%u", ret, __LINE__); + return -EIO; + } + return ret; +} + +static int memc_mspi_aps_z8_command_read(const struct device *psram, uint8_t cmd, + uint32_t addr, uint8_t *rdata, uint32_t length) +{ + const struct memc_mspi_aps_z8_config *cfg = psram->config; + struct memc_mspi_aps_z8_data *data = psram->data; + int ret; + + data->packet.dir = MSPI_RX; + data->packet.cmd = cmd; + data->packet.address = addr; + data->packet.data_buf = rdata; + data->packet.num_bytes = length; + + data->trans.async = false; + data->trans.xfer_mode = MSPI_PIO; + data->trans.tx_dummy = data->dev_cfg.tx_dummy; + data->trans.rx_dummy = data->dev_cfg.rx_dummy; + data->trans.cmd_length = data->dev_cfg.cmd_length; + data->trans.addr_length = data->dev_cfg.addr_length; + data->trans.hold_ce = false; + data->trans.packets = &data->packet; + data->trans.num_packet = 1; + data->trans.timeout = 10; + + ret = mspi_transceive(cfg->bus, &cfg->dev_id, (const struct mspi_xfer *)&data->trans); + if (ret) { + LOG_ERR("MSPI read transaction failed with code: %d/%u", ret, __LINE__); + return -EIO; + } + return ret; +} + +static int memc_mspi_aps_z8_enter_command_mode(const struct device *psram) +{ + const struct memc_mspi_aps_z8_config *cfg = psram->config; + struct memc_mspi_aps_z8_data *data = psram->data; + int ret; + + if (cfg->octal_cfg.io_mode == data->dev_cfg.io_mode) { + return 0; + } + + ret = mspi_dev_config(cfg->bus, &cfg->dev_id, + MSPI_DEVICE_CONFIG_ALL, &cfg->octal_cfg); + if (ret) { + LOG_ERR("Failed to reconfigure MSPI while entering command mode/%u", __LINE__); + return -EIO; + } + data->dev_cfg = cfg->octal_cfg; + + data->regs.MR8_b.IOM = 0x0; + ret = memc_mspi_aps_z8_command_write(psram, APS_Z8_WRITE_REGISTER, 8, &data->regs.MR8, 1); + if (ret) { + LOG_ERR("Failed to exit hex mode/%u", __LINE__); + return -EIO; + } + return 0; +} + +static int memc_mspi_aps_z8_exit_command_mode(const struct device *psram) +{ + const struct memc_mspi_aps_z8_config *cfg = psram->config; + struct memc_mspi_aps_z8_data *data = psram->data; + int ret; + + if (cfg->tar_dev_cfg.io_mode == data->dev_cfg.io_mode) { + return 0; + } + + data->regs.MR8_b.IOM = 0x1; + ret = memc_mspi_aps_z8_command_write(psram, APS_Z8_WRITE_REGISTER, 8, &data->regs.MR8, 1); + if (ret) { + LOG_ERR("Failed to enter hex mode/%u", __LINE__); + return -EIO; + } + + ret = mspi_dev_config(cfg->bus, &cfg->dev_id, + MSPI_DEVICE_CONFIG_ALL, &cfg->tar_dev_cfg); + if (ret) { + LOG_ERR("Failed to reconfigure MSPI while exiting command mode/%u", __LINE__); + return -EIO; + } + data->dev_cfg = cfg->tar_dev_cfg; + return 0; +} + +#if CONFIG_PM_DEVICE +static void acquire(const struct device *psram) +{ + const struct memc_mspi_aps_z8_config *cfg = psram->config; + struct memc_mspi_aps_z8_data *data = psram->data; + + k_sem_take(&data->lock, K_FOREVER); + + if (cfg->sw_multi_periph) { + while (mspi_dev_config(cfg->bus, &cfg->dev_id, + MSPI_DEVICE_CONFIG_ALL, &data->dev_cfg)) { + ; + } + } else { + while (mspi_dev_config(cfg->bus, &cfg->dev_id, + MSPI_DEVICE_CONFIG_NONE, NULL)) { + ; + } + } +} +#endif /* CONFIG_PM_DEVICE */ + +static void release(const struct device *psram) +{ + const struct memc_mspi_aps_z8_config *cfg = psram->config; + struct memc_mspi_aps_z8_data *data = psram->data; + + while (mspi_get_channel_status(cfg->bus, cfg->port)) { + ; + } + + k_sem_give(&data->lock); +} + +static int memc_mspi_aps_z8_reset(const struct device *psram) +{ + struct memc_mspi_aps_z8_data *data = psram->data; + int ret = 0; + + LOG_DBG("Resetting APS Z8/%u", __LINE__); + + ret = memc_mspi_aps_z8_command_write(psram, APS_Z8_GLOBAL_RESET, 0, + (uint8_t *)&data->dummy, 2); + if (ret) { + return ret; + } + + /** We need to delay 2us minimum to allow APS pSRAM to reinitialize */ + k_busy_wait(2); + return ret; +} + +static int memc_mspi_aps_z8_get_vendor_id(const struct device *psram) +{ + struct memc_mspi_aps_z8_data *data = psram->data; + int ret; + + ret = memc_mspi_aps_z8_command_read(psram, APS_Z8_READ_REGISTER, 1, &data->regs.MR1, 1); + LOG_DBG("MR1 reg: %x/%u", data->regs.MR1, __LINE__); + + if (data->regs.MR1_b.VID != APM_VENDOR_ID) { + return -EIO; + } + + return ret; +} + +static int memc_mspi_aps_z8_get_rlc(uint8_t rx_dummy, enum memc_mspi_aps_z8_rlc *rlc) +{ + switch (rx_dummy) { + case 4: + *rlc = MEMC_MSPI_APS_Z8_RLC_4; + break; + case 5: + *rlc = MEMC_MSPI_APS_Z8_RLC_5; + break; + case 6: + *rlc = MEMC_MSPI_APS_Z8_RLC_6; + break; + case 7: + *rlc = MEMC_MSPI_APS_Z8_RLC_7; + break; + case 8: + *rlc = MEMC_MSPI_APS_Z8_RLC_8; + break; + case 9: + *rlc = MEMC_MSPI_APS_Z8_RLC_9; + break; + default: + return -EINVAL; + } + return 0; +} + +static int memc_mspi_aps_z8_get_wlc(uint8_t tx_dummy, enum memc_mspi_aps_z8_wlc *wlc) +{ + switch (tx_dummy) { + case 5: + *wlc = MEMC_MSPI_APS_Z8_WLC_5; + break; + case 6: + *wlc = MEMC_MSPI_APS_Z8_WLC_6; + break; + case 7: + *wlc = MEMC_MSPI_APS_Z8_WLC_7; + break; + case 8: + *wlc = MEMC_MSPI_APS_Z8_WLC_8; + break; + case 9: + *wlc = MEMC_MSPI_APS_Z8_WLC_9; + break; + case 10: + *wlc = MEMC_MSPI_APS_Z8_WLC_10; + break; + default: + return -EINVAL; + } + return 0; +} + +#if CONFIG_PM_DEVICE +static int memc_mspi_aps_z8_half_sleep_enter(const struct device *psram) +{ + const struct memc_mspi_aps_z8_config *cfg = psram->config; + struct memc_mspi_aps_z8_data *data = psram->data; + struct mspi_xip_cfg xip_cfg = data->xip_cfg; + int ret; + + if (xip_cfg.enable) { + sys_cache_data_flush_and_invd_all(); + } + +#if CONFIG_MSPI_XIP + xip_cfg.enable = false; + if (mspi_xip_config(cfg->bus, &cfg->dev_id, &xip_cfg)) { + LOG_ERR("Failed to disable XIP/%u", __LINE__); + return -EIO; + } +#endif /* CONFIG_MSPI_XIP */ + + LOG_DBG("Putting APS Z8 to half sleep/%u", __LINE__); + ret = memc_mspi_aps_z8_enter_command_mode(psram); + if (ret) { + return ret; + } + + data->regs.MR6 = 0xF0; + ret = memc_mspi_aps_z8_command_write(psram, APS_Z8_WRITE_REGISTER, 6, &data->regs.MR6, 1); + if (ret) { + LOG_ERR("Failed to enter half sleep/%u", __LINE__); + return ret; + } + /* Minimum half sleep duration time(tHS) */ + k_busy_wait(150); + + return ret; +} + +static int memc_mspi_aps_z8_half_sleep_exit(const struct device *psram) +{ + const struct memc_mspi_aps_z8_config *cfg = psram->config; + struct memc_mspi_aps_z8_data *data = psram->data; + int ret = 0; + + LOG_DBG("Waking up aps_z8 from half sleep/%u", __LINE__); + ret = memc_mspi_aps_z8_command_write(psram, 0, 0, (uint8_t *)&data->dummy, 2); + if (ret) { + LOG_ERR("Failed to exit from half sleep/%u", __LINE__); + return ret; + } + /* Minimum half sleep exit CE to CLK setup time(tXHS) */ + k_busy_wait(150); + + ret = memc_mspi_aps_z8_exit_command_mode(psram); + if (ret) { + return ret; + } + +#if CONFIG_MSPI_XIP + if (mspi_xip_config(cfg->bus, &cfg->dev_id, &data->xip_cfg)) { + LOG_ERR("Failed to enable XIP/%u", __LINE__); + return -EIO; + } +#endif /* CONFIG_MSPI_XIP */ + + return ret; +} + +static int memc_mspi_aps_z8_pm_action(const struct device *psram, enum pm_device_action action) +{ + struct memc_mspi_aps_z8_data *data = psram->data; + + switch (action) { + case PM_DEVICE_ACTION_RESUME: + if (data->regs.MR1_b.ULP) { + acquire(psram); + memc_mspi_aps_z8_half_sleep_exit(psram); + } + break; + + case PM_DEVICE_ACTION_SUSPEND: + if (data->regs.MR1_b.ULP) { + memc_mspi_aps_z8_half_sleep_enter(psram); + release(psram); + } + break; + + default: + return -ENOTSUP; + } + + return 0; +} +#endif /* IS_ENABLED(CONFIG_PM_DEVICE) */ + +static int memc_mspi_aps_z8_init(const struct device *psram) +{ + const struct memc_mspi_aps_z8_config *cfg = psram->config; + struct memc_mspi_aps_z8_data *data = psram->data; + + if (!device_is_ready(cfg->bus)) { + LOG_ERR("Controller device not ready/%u", __LINE__); + return -ENODEV; + } + + switch (cfg->tar_dev_cfg.io_mode) { + case MSPI_IO_MODE_OCTAL: + case MSPI_IO_MODE_HEX_8_8_16: + break; + default: + LOG_ERR("Bus mode %d not supported/%u", cfg->tar_dev_cfg.io_mode, __LINE__); + return -EIO; + } + + if (cfg->tar_dev_cfg.data_rate != MSPI_DATA_RATE_S_D_D) { + LOG_ERR("Data rate %d not supported/%u", cfg->tar_dev_cfg.data_rate, __LINE__); + return -EIO; + } + + if (mspi_dev_config(cfg->bus, &cfg->dev_id, MSPI_DEVICE_CONFIG_ALL, &cfg->octal_cfg)) { + LOG_ERR("Failed to config mspi controller/%u", __LINE__); + return -EIO; + } + data->dev_cfg = cfg->octal_cfg; + + if (memc_mspi_aps_z8_reset(psram)) { + LOG_ERR("Could not reset pSRAM/%u", __LINE__); + return -EIO; + } + + if (memc_mspi_aps_z8_get_vendor_id(psram)) { + LOG_ERR("Could not read vendor id/%u", __LINE__); + return -EIO; + } + + if (memc_mspi_aps_z8_command_read(psram, APS_Z8_READ_REGISTER, 0, &data->regs.MR0, 1)) { + LOG_ERR("Could not read MR0 register/%u", __LINE__); + return -EIO; + } + + if (memc_mspi_aps_z8_command_read(psram, APS_Z8_READ_REGISTER, 2, &data->regs.MR2, 1)) { + LOG_ERR("Could not read MR2 register/%u", __LINE__); + return -EIO; + } + + if (memc_mspi_aps_z8_command_read(psram, APS_Z8_READ_REGISTER, 3, &data->regs.MR3, 1)) { + LOG_ERR("Could not read MR3 register/%u", __LINE__); + return -EIO; + } + + if (memc_mspi_aps_z8_command_read(psram, APS_Z8_READ_REGISTER, 4, &data->regs.MR4, 1)) { + LOG_ERR("Could not read MR4 register/%u", __LINE__); + return -EIO; + } + + if (memc_mspi_aps_z8_command_read(psram, APS_Z8_READ_REGISTER, 6, &data->regs.MR6, 1)) { + LOG_ERR("Could not read MR2 register/%u", __LINE__); + return -EIO; + } + + if (memc_mspi_aps_z8_command_read(psram, APS_Z8_READ_REGISTER, 8, &data->regs.MR8, 1)) { + LOG_ERR("Could not read MR8 register/%u", __LINE__); + return -EIO; + } + + enum memc_mspi_aps_z8_rlc rlc; + + if (memc_mspi_aps_z8_get_rlc(cfg->tar_dev_cfg.rx_dummy, &rlc)) { + LOG_ERR("rx_dummy:%d not supported/%u", cfg->tar_dev_cfg.rx_dummy, __LINE__); + return -EIO; + } + data->regs.MR0_b.RLC = rlc; + + enum memc_mspi_aps_z8_wlc wlc; + + if (memc_mspi_aps_z8_get_wlc(cfg->tar_dev_cfg.tx_dummy, &wlc)) { + LOG_ERR("tx_dummy:%d not supported/%u", cfg->tar_dev_cfg.tx_dummy, __LINE__); + return -EIO; + } + data->regs.MR4_b.WLC = wlc; + + data->regs.MR0_b.LT = !cfg->tar_dev_cfg.dqs_enable; + + if (memc_mspi_aps_z8_command_write(psram, APS_Z8_WRITE_REGISTER, 0, &data->regs.MR0, 1)) { + LOG_ERR("Could not write MR0 register/%u", __LINE__); + return -EIO; + } + + if (memc_mspi_aps_z8_command_write(psram, APS_Z8_WRITE_REGISTER, 4, &data->regs.MR4, 1)) { + LOG_ERR("Could not write MR4 register/%u", __LINE__); + return -EIO; + } + + if (cfg->tar_dev_cfg.io_mode == MSPI_IO_MODE_HEX_8_8_16) { + data->regs.MR8_b.IOM = 1; + if (memc_mspi_aps_z8_command_write(psram, APS_Z8_WRITE_REGISTER, 8, + &data->regs.MR8, 1)) { + LOG_ERR("Could not write MR8 register/%u", __LINE__); + return -EIO; + } + } + + if (mspi_dev_config(cfg->bus, &cfg->dev_id, MSPI_DEVICE_CONFIG_ALL, &cfg->tar_dev_cfg)) { + LOG_ERR("Failed to config mspi controller/%u", __LINE__); + return -EIO; + } + data->dev_cfg = cfg->tar_dev_cfg; + +#if CONFIG_MSPI_TIMING + if (mspi_timing_config(cfg->bus, &cfg->dev_id, cfg->timing_cfg_mask, + (void *)&cfg->tar_timing_cfg)) { + LOG_ERR("Failed to config mspi timing/%u", __LINE__); + return -EIO; + } + data->timing_cfg = cfg->tar_timing_cfg; +#endif /* CONFIG_MSPI_TIMING */ + +#if CONFIG_MSPI_XIP + if (cfg->tar_xip_cfg.enable) { + if (mspi_xip_config(cfg->bus, &cfg->dev_id, &cfg->tar_xip_cfg)) { + LOG_ERR("Failed to enable XIP/%u", __LINE__); + return -EIO; + } + data->xip_cfg = cfg->tar_xip_cfg; + } +#endif /* CONFIG_MSPI_XIP */ + +#if CONFIG_MSPI_SCRAMBLE + if (cfg->tar_scramble_cfg.enable) { + if (mspi_scramble_config(cfg->bus, &cfg->dev_id, &cfg->tar_scramble_cfg)) { + LOG_ERR("Failed to enable scrambling/%u", __LINE__); + return -EIO; + } + data->scramble_cfg = cfg->tar_scramble_cfg; + } +#endif /* MSPI_SCRAMBLE */ + + if (!IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME) || + !cfg->pm_dev_rt_auto) { + release(psram); + } + + return 0; +} + +#define MSPI_DEVICE_CONFIG_OCTAL(n) \ + { \ + .ce_num = DT_INST_PROP(n, mspi_hardware_ce_num), \ + .freq = 24000000, \ + .io_mode = MSPI_IO_MODE_OCTAL, \ + .data_rate = MSPI_DATA_RATE_S_D_D, \ + .cpp = MSPI_CPP_MODE_0, \ + .endian = MSPI_XFER_LITTLE_ENDIAN, \ + .ce_polarity = MSPI_CE_ACTIVE_LOW, \ + .dqs_enable = DT_INST_PROP(n, mspi_dqs_enable), \ + .rx_dummy = MEMC_MSPI_APS_Z8_RX_DUMMY_DEFAULT, \ + .tx_dummy = MEMC_MSPI_APS_Z8_TX_DUMMY_DEFAULT, \ + .read_cmd = APS_Z8_LINEAR_BURST_READ, \ + .write_cmd = APS_Z8_LINEAR_BURST_WRITE, \ + .cmd_length = MEMC_MSPI_APS_Z8_CMD_LENGTH_DEFAULT, \ + .addr_length = MEMC_MSPI_APS_Z8_ADDR_LENGTH_DEFAULT, \ + .mem_boundary = 1024, \ + .time_to_break = 4, \ + } +#define MSPI_TIMING_CONFIG(n) \ + COND_CODE_1(CONFIG_SOC_FAMILY_AMBIQ, \ + (MSPI_AMBIQ_TIMING_CONFIG(n)), ({})) \ + +#define MSPI_TIMING_CONFIG_MASK(n) \ + COND_CODE_1(CONFIG_SOC_FAMILY_AMBIQ, \ + (MSPI_AMBIQ_TIMING_CONFIG_MASK(n)), (MSPI_TIMING_PARAM_DUMMY)) \ + +#define MSPI_PORT(n) \ + COND_CODE_1(CONFIG_SOC_FAMILY_AMBIQ, \ + (MSPI_AMBIQ_PORT(n)), (0)) \ + +#define MEMC_MSPI_APS_Z8(n) \ + static const struct memc_mspi_aps_z8_config \ + memc_mspi_aps_z8_config_##n = { \ + .port = MSPI_PORT(n), \ + .mem_size = DT_INST_PROP(n, size) / 8, \ + .bus = DEVICE_DT_GET(DT_INST_BUS(n)), \ + .dev_id = MSPI_DEVICE_ID_DT_INST(n), \ + .octal_cfg = MSPI_DEVICE_CONFIG_OCTAL(n), \ + .tar_dev_cfg = MSPI_DEVICE_CONFIG_DT_INST(n), \ + MSPI_OPTIONAL_CFG_STRUCT_INIT(CONFIG_MSPI_XIP, \ + tar_xip_cfg, MSPI_XIP_CONFIG_DT_INST(n)) \ + MSPI_OPTIONAL_CFG_STRUCT_INIT(CONFIG_MSPI_SCRAMBLE, \ + tar_scramble_cfg, MSPI_SCRAMBLE_CONFIG_DT_INST(n)) \ + MSPI_OPTIONAL_CFG_STRUCT_INIT(CONFIG_MSPI_TIMING, \ + tar_timing_cfg, MSPI_TIMING_CONFIG(n)) \ + MSPI_OPTIONAL_CFG_STRUCT_INIT(CONFIG_MSPI_TIMING, \ + timing_cfg_mask, MSPI_TIMING_CONFIG_MASK(n)) \ + .sw_multi_periph = DT_PROP(DT_INST_BUS(n), software_multiperipheral), \ + .pm_dev_rt_auto = DT_INST_PROP(n, zephyr_pm_device_runtime_auto) \ + }; \ + static struct memc_mspi_aps_z8_data \ + memc_mspi_aps_z8_data_##n = { \ + .lock = Z_SEM_INITIALIZER(memc_mspi_aps_z8_data_##n.lock, 0, 1), \ + .dummy = 0, \ + }; \ + PM_DEVICE_DT_INST_DEFINE(n, memc_mspi_aps_z8_pm_action); \ + DEVICE_DT_INST_DEFINE(n, \ + memc_mspi_aps_z8_init, \ + PM_DEVICE_DT_INST_GET(n), \ + &memc_mspi_aps_z8_data_##n, \ + &memc_mspi_aps_z8_config_##n, \ + POST_KERNEL, \ + CONFIG_MEMC_INIT_PRIORITY, \ + NULL); + +DT_INST_FOREACH_STATUS_OKAY(MEMC_MSPI_APS_Z8) diff --git a/drivers/memc/memc_mspi_aps_z8.h b/drivers/memc/memc_mspi_aps_z8.h new file mode 100644 index 00000000000..537675faf0c --- /dev/null +++ b/drivers/memc/memc_mspi_aps_z8.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2025, Ambiq Micro Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +enum memc_mspi_aps_z8_rlc { + MEMC_MSPI_APS_Z8_RLC_4, + MEMC_MSPI_APS_Z8_RLC_5, + MEMC_MSPI_APS_Z8_RLC_6, + MEMC_MSPI_APS_Z8_RLC_7, + MEMC_MSPI_APS_Z8_RLC_8, + MEMC_MSPI_APS_Z8_RLC_9, +}; + +enum memc_mspi_aps_z8_wlc { + MEMC_MSPI_APS_Z8_WLC_3, /* reserved for APS51216BA */ + MEMC_MSPI_APS_Z8_WLC_7, + MEMC_MSPI_APS_Z8_WLC_5, + MEMC_MSPI_APS_Z8_WLC_9, /* reserved for APS25616N */ + MEMC_MSPI_APS_Z8_WLC_4, /* reserved for APS51216BA */ + MEMC_MSPI_APS_Z8_WLC_8, /* reserved for APS25616N */ + MEMC_MSPI_APS_Z8_WLC_6, + MEMC_MSPI_APS_Z8_WLC_10, /* reserved for APS25616N */ +}; + +/* default for APS51216BA */ +#define MEMC_MSPI_APS_Z8_RX_DUMMY_DEFAULT 6 +#define MEMC_MSPI_APS_Z8_TX_DUMMY_DEFAULT 6 + +#define MEMC_MSPI_APS_Z8_CMD_LENGTH_DEFAULT 1 +#define MEMC_MSPI_APS_Z8_ADDR_LENGTH_DEFAULT 4 + +enum memc_mspi_aps_z8_ds { + MEMC_MSPI_APS_Z8_DRIVE_STRENGTH_FULL, + MEMC_MSPI_APS_Z8_DRIVE_STRENGTH_HALF, + MEMC_MSPI_APS_Z8_DRIVE_STRENGTH_QUARTER, + MEMC_MSPI_APS_Z8_DRIVE_STRENGTH_OCTUPLE, +}; + +struct memc_mspi_aps_z8_reg { + union { + uint8_t MR0; /* Mode register 0 */ + + struct { + uint8_t DS: 2; /* [0..1] drive strength */ + uint8_t RLC: 3; /* [2..4] Read latency code. */ + uint8_t LT: 1; /* [5..5] Latency type */ + uint8_t: 1; + uint8_t TSO: 1; /* [7..7] Temperature Sensor Override */ + } MR0_b; + }; + + union { + uint8_t MR1; /* Mode register 1 */ + + struct { + uint8_t VID: 5; /* [0..4] Vendor ID */ + uint8_t: 2; + uint8_t ULP: 1; /* [7..7] Half sleep. */ + } MR1_b; + }; + + union { + uint8_t MR2; /* Mode register 2 */ + + struct { + uint8_t DENSITY: 3; /* [0..2] Density */ + uint8_t GENERATION: 2; /* [3..4] Generation */ + uint8_t GB: 3; /* [5..7] Good or bad */ + } MR2_b; + }; + + union { + uint8_t MR3; /* Mode register 3 */ + + struct { + uint8_t: 4; + uint8_t SRF: 2; /* [4..5] Self refresh flag */ + uint8_t: 1; + uint8_t RBXen: 1; /* [7..7] Row Boundary Crossing Enable */ + } MR3_b; + }; + + union { + uint8_t MR4; /* Mode register 4 */ + + struct { + uint8_t PASR: 3; /* [0..2] control refresh address space */ + uint8_t RFS: 2; /* [3..4] Refresh Frequency setting */ + uint8_t WLC: 3; /* [5..7] Write latency code. */ + } MR4_b; + }; + + union { + uint8_t MR6; /* Mode register 6 */ + + struct { + uint8_t ULPM: 8; /* [0..7] ULP modes */ + } MR6_b; + }; + + union { + uint8_t MR8; /* Mode register 8 */ + + struct { + uint8_t BL: 2; /* [0..1] Burst Length */ + uint8_t BT: 1; /* [2..2] Burst type */ + uint8_t RBX: 1; /* [3..3] Row Boundary Crossing Read EN */ + uint8_t: 2; + uint8_t IOM: 1; /* [6..6] IO mode */ + uint8_t: 1; + } MR8_b; + }; +}; diff --git a/dts/bindings/mtd/mspi-aps-z8.yaml b/dts/bindings/mtd/mspi-aps-z8.yaml new file mode 100644 index 00000000000..204539f137f --- /dev/null +++ b/dts/bindings/mtd/mspi-aps-z8.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2025, Ambiq Micro Inc. +# SPDX-License-Identifier: Apache-2.0 + +description: AP Memory pSRAM Version Z8 on MSPI bus + +compatible: "mspi-aps-z8" + +include: [mspi-device.yaml, "jedec,jesd216.yaml"] diff --git a/samples/drivers/memc/CMakeLists.txt b/samples/drivers/memc/CMakeLists.txt index 3b73ac1e197..eddbf10bc1a 100644 --- a/samples/drivers/memc/CMakeLists.txt +++ b/samples/drivers/memc/CMakeLists.txt @@ -10,7 +10,7 @@ if(CONFIG_MEMC_MCUX_FLEXSPI) target_include_directories(app PRIVATE ${ZEPHYR_BASE}/drivers/memc) endif() -if(CONFIG_MEMC_MSPI_APS6404L) +if(CONFIG_MEMC_MSPI) target_include_directories(app PRIVATE ${ZEPHYR_BASE}/include/zephyr/drivers) target_include_directories(app PRIVATE ${ZEPHYR_BASE}/drivers/mspi) endif() diff --git a/samples/drivers/memc/boards/apollo510_evb.conf b/samples/drivers/memc/boards/apollo510_evb.conf new file mode 100644 index 00000000000..ab7b3c67fb9 --- /dev/null +++ b/samples/drivers/memc/boards/apollo510_evb.conf @@ -0,0 +1,6 @@ +# Copyright (c) 2025 Ambiq Micro Inc. +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_MEMC=y +CONFIG_LOG=y +CONFIG_LOG_DEFAULT_LEVEL=0 diff --git a/samples/drivers/memc/boards/apollo510_evb.overlay b/samples/drivers/memc/boards/apollo510_evb.overlay new file mode 100644 index 00000000000..db61fc0677c --- /dev/null +++ b/samples/drivers/memc/boards/apollo510_evb.overlay @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2025, Ambiq Micro Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + aliases { + psram0 = &aps51216ba; + }; +}; + +&mspi0 { + status = "okay"; +}; + +&aps51216ba { + status = "okay"; +}; diff --git a/samples/drivers/memc/src/main.c b/samples/drivers/memc/src/main.c index 7d3bd961452..b0e9ef99687 100644 --- a/samples/drivers/memc/src/main.c +++ b/samples/drivers/memc/src/main.c @@ -7,23 +7,27 @@ #include #include #include +#include #if DT_HAS_COMPAT_STATUS_OKAY(nxp_imx_flexspi) /* Use memc API to get AHB base address for the device */ #include "memc_mcux_flexspi.h" #define FLEXSPI_DEV DEVICE_DT_GET(DT_PARENT(DT_ALIAS(sram_ext))) +#define MEMC_DEV DT_ALIAS(sram_ext) #define MEMC_PORT DT_REG_ADDR(DT_ALIAS(sram_ext)) #define MEMC_BASE ((void *)memc_flexspi_get_ahb_address(FLEXSPI_DEV, MEMC_PORT, 0)) #define MEMC_SIZE (DT_PROP(DT_ALIAS(sram_ext), size) / 8) #elif DT_HAS_COMPAT_STATUS_OKAY(renesas_smartbond_nor_psram) #include +#define MEMC_DEV DT_ALIAS(sram_ext) #define MEMC_BASE ((void *)MCU_QSPIR_M_BASE) #define MEMC_SIZE (DT_PROP(DT_ALIAS(sram_ext), dev_size) / 8) #elif DT_HAS_COMPAT_STATUS_OKAY(ambiq_mspi_controller) -#define MSPI_ DT_BUS(DT_ALIAS(psram0)) +#define MEMC_DEV DT_ALIAS(psram0) +#define MSPI_BUS DT_BUS(MEMC_DEV) #define mspi_get_xip_address(controller) DT_REG_ADDR_BY_IDX(controller, 1) -#define MEMC_BASE (void *)(mspi_get_xip_address(MSPI_)) -#define MEMC_SIZE (DT_PROP(DT_ALIAS(psram0), size) / 8) +#define MEMC_BASE (void *)(mspi_get_xip_address(MSPI_BUS)) +#define MEMC_SIZE (DT_PROP(MEMC_DEV, size) / 8) #else #error At least one driver should be selected! #endif @@ -65,6 +69,9 @@ int main(void) for (i = 0; i < BUF_SIZE; i++) { memc_write_buffer[i] = (uint8_t)i; } + + pm_device_runtime_get(DEVICE_DT_GET(MEMC_DEV)); + printk("Writing to memory region with base %p, size 0x%0x\n\n", memc, MEMC_SIZE); /* Copy write buffer into memc region */ @@ -99,6 +106,7 @@ int main(void) printk("First 1KB of Data in memory:\n"); printk("===========================\n"); dump_memory(memc, MIN(MEMC_SIZE, KB(1))); + pm_device_runtime_put(DEVICE_DT_GET(MEMC_DEV)); printk("Read data matches written data\n"); return 0; }