diff --git a/boards/ambiq/apollo3_evb/apollo3_evb-pinctrl.dtsi b/boards/ambiq/apollo3_evb/apollo3_evb-pinctrl.dtsi index 7d1716236f7fd..9ec4426cbd9f2 100644 --- a/boards/ambiq/apollo3_evb/apollo3_evb-pinctrl.dtsi +++ b/boards/ambiq/apollo3_evb/apollo3_evb-pinctrl.dtsi @@ -147,14 +147,33 @@ }; group2 { + pinmux = ; + drive-strength = "1.0"; + ambiq,iom-mspi = <0>; + ambiq,iom-num = <6>; + }; + + group3 { pinmux = ; drive-push-pull; drive-strength = "0.5"; + ambiq,iom-mspi = <0>; ambiq,nce-src = <0>; ambiq,iom-num = <6>; }; }; + mspi0_sleep: mspi0_sleep { + group1 { + pinmux = , + , + , + , + , + ; + }; + }; + bleif_default: bleif_default { group1 { pinmux = , diff --git a/drivers/mspi/Kconfig.ambiq b/drivers/mspi/Kconfig.ambiq index b417077768126..54687e451dd69 100644 --- a/drivers/mspi/Kconfig.ambiq +++ b/drivers/mspi/Kconfig.ambiq @@ -18,7 +18,7 @@ config MSPI_AMBIQ_AP3 depends on SOC_SERIES_APOLLO3X imply MSPI_XIP imply MSPI_SCRAMBLE - imply MSPI_TIMING + imply MSPI_TIMING if SOC_APOLLO3P_BLUE help Enable driver for Ambiq MSPI. @@ -35,6 +35,7 @@ config MSPI_AMBIQ_AP5 config MSPI_AMBIQ_BUFF_RAM_LOCATION hex "Byte offset to SRAM_BASE_ADDRESS" default SOC_AMBIQ_DMA_BUFF_LOCATION if SOC_SERIES_APOLLO5X + default 0x40000 if SOC_APOLLO3_BLUE default 0x50000 help This option specifies the mspi buffer/heap start address diff --git a/drivers/mspi/mspi_ambiq_ap3.c b/drivers/mspi/mspi_ambiq_ap3.c index ee947039838e8..253d3a957322c 100644 --- a/drivers/mspi/mspi_ambiq_ap3.c +++ b/drivers/mspi/mspi_ambiq_ap3.c @@ -168,6 +168,7 @@ static am_hal_mspi_device_e mspi_set_line(const struct mspi_ambiq_config *cfg, } } +#if defined(CONFIG_SOC_APOLLO3P_BLUE) static am_hal_mspi_dma_boundary_e mspi_set_mem_boundary(uint32_t mem_boundary) { switch (mem_boundary) { @@ -197,6 +198,7 @@ static am_hal_mspi_dma_boundary_e mspi_set_mem_boundary(uint32_t mem_boundary) return AM_HAL_MSPI_BOUNDARY_MAX; } } +#endif /* CONFIG_SOC_APOLLO3P_BLUE */ static inline void mspi_context_ce_control(struct mspi_context *ctx, bool on) { @@ -443,8 +445,10 @@ static int mspi_xfer_config(const struct device *controller, hal_dev_cfg.bTurnaround = (xfer->rx_dummy != 0); hal_dev_cfg.ui8TurnAround = (uint8_t)xfer->rx_dummy; +#if defined(CONFIG_SOC_APOLLO3P_BLUE) hal_dev_cfg.bEnWriteLatency = (xfer->tx_dummy != 0); hal_dev_cfg.ui8WriteLatency = (uint8_t)xfer->tx_dummy; +#endif ret = am_hal_mspi_device_configure(data->mspiHandle, &hal_dev_cfg); if (ret) { @@ -724,6 +728,7 @@ static int mspi_ambiq_dev_config(const struct device *controller, goto e_return; } hal_dev_cfg.eInstrCfg = dev_cfg->cmd_length - 1; +#if defined(CONFIG_SOC_APOLLO3P_BLUE) ret = am_hal_mspi_control(data->mspiHandle, AM_HAL_MSPI_REQ_ISIZE_SET, &hal_dev_cfg.eInstrCfg); @@ -733,6 +738,7 @@ static int mspi_ambiq_dev_config(const struct device *controller, ret = -EHOSTDOWN; goto e_return; } +#endif /* CONFIG_SOC_APOLLO3P_BLUE */ data->dev_cfg.cmd_length = dev_cfg->cmd_length; } @@ -744,6 +750,7 @@ static int mspi_ambiq_dev_config(const struct device *controller, goto e_return; } hal_dev_cfg.eAddrCfg = dev_cfg->addr_length - 1; +#if defined(CONFIG_SOC_APOLLO3P_BLUE) ret = am_hal_mspi_control(data->mspiHandle, AM_HAL_MSPI_REQ_ASIZE_SET, &hal_dev_cfg.eAddrCfg); @@ -753,6 +760,7 @@ static int mspi_ambiq_dev_config(const struct device *controller, ret = -EHOSTDOWN; goto e_return; } +#endif /* CONFIG_SOC_APOLLO3P_BLUE */ data->dev_cfg.addr_length = dev_cfg->addr_length; } @@ -782,8 +790,10 @@ static int mspi_ambiq_dev_config(const struct device *controller, } hal_dev_cfg.eSpiMode = dev_cfg->cpp; +#if defined(CONFIG_SOC_APOLLO3P_BLUE) hal_dev_cfg.bEnWriteLatency = (dev_cfg->tx_dummy != 0); hal_dev_cfg.ui8WriteLatency = dev_cfg->tx_dummy; +#endif hal_dev_cfg.bTurnaround = (dev_cfg->rx_dummy != 0); hal_dev_cfg.ui8TurnAround = dev_cfg->rx_dummy; @@ -827,6 +837,7 @@ static int mspi_ambiq_dev_config(const struct device *controller, hal_dev_cfg.ui8ReadInstr = (uint8_t)dev_cfg->read_cmd; hal_dev_cfg.ui8WriteInstr = (uint8_t)dev_cfg->write_cmd; +#if defined(CONFIG_SOC_APOLLO3P_BLUE) hal_dev_cfg.eDMABoundary = mspi_set_mem_boundary(dev_cfg->mem_boundary); if (hal_dev_cfg.eDMABoundary >= AM_HAL_MSPI_BOUNDARY_MAX) { LOG_INST_ERR(cfg->log, "%u, mem_boundary too large.", __LINE__); @@ -836,6 +847,15 @@ static int mspi_ambiq_dev_config(const struct device *controller, /** ui16DMATimeLimit unit is in 0.1us */ hal_dev_cfg.ui16DMATimeLimit = dev_cfg->time_to_break * 10; +#else + if (dev_cfg->mem_boundary != 0 || dev_cfg->time_to_break != 0) { + LOG_INST_ERR(cfg->log, + "%u, mem_boundary/time_to_break not supported on this SoC.", + __LINE__); + ret = -ENOTSUP; + goto e_return; + } +#endif ret = am_hal_mspi_disable(data->mspiHandle); if (ret) { @@ -977,6 +997,7 @@ static int mspi_ambiq_timing_config(const struct device *controller, const uint32_t param_mask, void *timing_cfg) { +#if defined(CONFIG_SOC_APOLLO3P_BLUE) struct mspi_ambiq_data *data = controller->data; am_hal_mspi_dev_config_t hal_dev_cfg = data->hal_dev_cfg; struct mspi_ambiq_timing_cfg *time_cfg = timing_cfg; @@ -1030,6 +1051,13 @@ static int mspi_ambiq_timing_config(const struct device *controller, data->hal_dev_cfg = hal_dev_cfg; return ret; +#else + ARG_UNUSED(controller); + ARG_UNUSED(dev_id); + ARG_UNUSED(param_mask); + ARG_UNUSED(timing_cfg); + return -ENOTSUP; +#endif /* CONFIG_SOC_APOLLO3P_BLUE */ } static int mspi_ambiq_get_channel_status(const struct device *controller, uint8_t ch) @@ -1090,10 +1118,12 @@ static int mspi_pio_prepare(const struct device *controller, trans->bSendAddr = (xfer->addr_length != 0); trans->bSendInstr = (xfer->cmd_length != 0); trans->bTurnaround = (xfer->rx_dummy != 0); +#if defined(CONFIG_SOC_APOLLO3P_BLUE) trans->bEnWRLatency = (xfer->tx_dummy != 0); trans->bDCX = false; - trans->bQuadCmd = false; trans->bContinue = false; +#endif + trans->bQuadCmd = false; if (xfer->cmd_length > AM_HAL_MSPI_INSTR_2_BYTE + 1) { LOG_INST_ERR(MSPI_LOG_HANDLE(controller), "%u, invalid cmd_length.", @@ -1103,6 +1133,7 @@ static int mspi_pio_prepare(const struct device *controller, if (xfer->cmd_length != 0) { am_hal_mspi_instr_e eInstrCfg = xfer->cmd_length - 1; +#if defined(CONFIG_SOC_APOLLO3P_BLUE) ret = am_hal_mspi_control(data->mspiHandle, AM_HAL_MSPI_REQ_ISIZE_SET, &eInstrCfg); if (ret) { LOG_INST_ERR(MSPI_LOG_HANDLE(controller), @@ -1110,6 +1141,7 @@ static int mspi_pio_prepare(const struct device *controller, __LINE__); return -EHOSTDOWN; } +#endif data->hal_dev_cfg.eInstrCfg = eInstrCfg; } data->dev_cfg.cmd_length = xfer->cmd_length; @@ -1123,6 +1155,7 @@ static int mspi_pio_prepare(const struct device *controller, if (xfer->addr_length != 0) { am_hal_mspi_addr_e eAddrCfg = xfer->addr_length - 1; +#if defined(CONFIG_SOC_APOLLO3P_BLUE) ret = am_hal_mspi_control(data->mspiHandle, AM_HAL_MSPI_REQ_ASIZE_SET, &eAddrCfg); if (ret) { LOG_INST_ERR(MSPI_LOG_HANDLE(controller), @@ -1130,6 +1163,7 @@ static int mspi_pio_prepare(const struct device *controller, __LINE__); return -EHOSTDOWN; } +#endif data->hal_dev_cfg.eAddrCfg = eAddrCfg; } data->dev_cfg.addr_length = xfer->addr_length; @@ -1451,10 +1485,10 @@ static DEVICE_API(mspi, mspi_ambiq_driver_api) = { struct pinctrl_dev_config Z_PINCTRL_DEV_CONFIG_NAME(node_id) = \ Z_PINCTRL_DEV_CONFIG_INIT(node_id) -#define MSPI_CONFIG(n) \ +#define MSPI_CONFIG(n) \ { \ - .channel_num = (DT_INST_REG_ADDR(n) - MSPI_BASE_ADDR) / \ - MSPI_ADDR_INTERVAL, \ + .channel_num = (uint8_t)((DT_INST_REG_ADDR(n) - \ + MSPI_BASE_ADDR) / MSPI_ADDR_INTERVAL), \ .op_mode = MSPI_OP_MODE_CONTROLLER, \ .duplex = MSPI_HALF_DUPLEX, \ .max_freq = MSPI_MAX_FREQ, \ @@ -1463,30 +1497,36 @@ static DEVICE_API(mspi, mspi_ambiq_driver_api) = { .sw_multi_periph = DT_INST_PROP(n, software_multiperipheral), \ } -#define MSPI_HAL_DEVICE_CONFIG(n, cmdq, cmdq_size) \ - { \ +#if defined(CONFIG_SOC_APOLLO3P_BLUE) +#define MSPI_HAL_DEVICE_CONFIG_AP3P_FIELDS \ .ui8WriteLatency = 0, \ - .ui8TurnAround = 0, \ - .eAddrCfg = 0, \ - .eInstrCfg = 0, \ - .ui8ReadInstr = 0, \ - .ui8WriteInstr = 0, \ - .eDeviceConfig = AM_HAL_MSPI_FLASH_SERIAL_CE0, \ - .eSpiMode = AM_HAL_MSPI_SPI_MODE_0, \ + .bEnWriteLatency = false, \ + .ui16DMATimeLimit = 0, \ + .eDMABoundary = AM_HAL_MSPI_BOUNDARY_NONE, +#else +#define MSPI_HAL_DEVICE_CONFIG_AP3P_FIELDS +#endif + +#define MSPI_HAL_DEVICE_CONFIG(n, cmdq, cmdq_size) \ + { \ + .ui8TurnAround = 0, \ + .eAddrCfg = 0, \ + .eInstrCfg = 0, \ + .ui8ReadInstr = 0, \ + .ui8WriteInstr = 0, \ + .eDeviceConfig = AM_HAL_MSPI_FLASH_SERIAL_CE0, \ + .eSpiMode = AM_HAL_MSPI_SPI_MODE_0, \ .eClockFreq = MSPI_MAX_FREQ / DT_INST_PROP_OR(n, \ clock_frequency, \ MSPI_MAX_FREQ), \ - .bEnWriteLatency = false, \ + MSPI_HAL_DEVICE_CONFIG_AP3P_FIELDS \ .bSendAddr = false, \ - .bSendInstr = false, \ - .bTurnaround = false, \ - .bEmulateDDR = false, \ - .ui16DMATimeLimit = 0, \ - .eDMABoundary = AM_HAL_MSPI_BOUNDARY_NONE, \ - .ui32TCBSize = cmdq_size, \ - .pTCB = cmdq, \ - .scramblingStartAddr = 0, \ - .scramblingEndAddr = 0, \ + .bSendInstr = false, \ + .bTurnaround = false, \ + .ui32TCBSize = cmdq_size, \ + .pTCB = cmdq, \ + .scramblingStartAddr = 0, \ + .scramblingEndAddr = 0, \ } #define AMBIQ_MSPI_DEFINE(n) \ diff --git a/dts/arm/ambiq/ambiq_apollo3_blue.dtsi b/dts/arm/ambiq/ambiq_apollo3_blue.dtsi index f53e72985fc3b..d837731e6060b 100644 --- a/dts/arm/ambiq/ambiq_apollo3_blue.dtsi +++ b/dts/arm/ambiq/ambiq_apollo3_blue.dtsi @@ -434,9 +434,10 @@ #io-channel-cells = <1>; }; - mspi0: spi@40020000 { - compatible = "ambiq,mspi"; + mspi0: mspi@40020000 { + compatible = "ambiq,mspi-controller"; reg = <0x40020000 0x400>; + clock-frequency = <48000000>; interrupts = <20 0>; #address-cells = <1>; #size-cells = <0>; diff --git a/samples/boards/ambiq/apollo3_mspi_tx_scope/CMakeLists.txt b/samples/boards/ambiq/apollo3_mspi_tx_scope/CMakeLists.txt new file mode 100644 index 0000000000000..eedccc4f68cfe --- /dev/null +++ b/samples/boards/ambiq/apollo3_mspi_tx_scope/CMakeLists.txt @@ -0,0 +1,9 @@ +# Copyright (c) 2026 Ambiq Micro Inc. +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(apollo3_mspi_tx_scope) + +target_sources(app PRIVATE src/main.c) diff --git a/samples/boards/ambiq/apollo3_mspi_tx_scope/README.rst b/samples/boards/ambiq/apollo3_mspi_tx_scope/README.rst new file mode 100644 index 0000000000000..b3f1a3dd0ca1e --- /dev/null +++ b/samples/boards/ambiq/apollo3_mspi_tx_scope/README.rst @@ -0,0 +1,39 @@ +.. zephyr:code-sample:: apollo3_mspi_tx_scope + :name: Apollo3 MSPI single-board TX scope test + :relevant-api: mspi_interface + + Exercise the Apollo3 ``ambiq,mspi-controller`` driver on a single EVB + without any external MSPI memory chip. + +Overview +******** + +This sample is a transmit-focused Apollo3 MSPI scope test. It declares a stub +``ambiq,mspi-device`` child node so the driver has a target to address, +starts in single-bit mode, then transitions to dual/quad mode and continuously +issues 32-byte TX transactions so line activity can be observed on a scope. + +No external chip is required. With a logic analyzer or oscilloscope on the +following pins, the generated waveforms can be observed directly: + +* **MSPI0_D0** - P22 +* **MSPI0_D1** - P26 +* **MSPI0_SCK** - P24 +* **NCE19** - P19 + +Building and running +******************** + +.. zephyr-app-commands:: + :zephyr-app: samples/boards/ambiq/apollo3_mspi_tx_scope + :board: apollo3_evb + :goals: build flash + +No console output is required for this sample; use a scope/logic analyzer +to verify MSPI clock/data activity. + +Two-board test +************** + +For an end-to-end MSPI <-> SPI peripheral test using two EVBs, see +``samples/boards/ambiq/apollo3_mspi_pair``. diff --git a/samples/boards/ambiq/apollo3_mspi_tx_scope/boards/apollo3_evb.conf b/samples/boards/ambiq/apollo3_mspi_tx_scope/boards/apollo3_evb.conf new file mode 100644 index 0000000000000..73f1fec857adf --- /dev/null +++ b/samples/boards/ambiq/apollo3_mspi_tx_scope/boards/apollo3_evb.conf @@ -0,0 +1,20 @@ +# Copyright (c) 2026 Ambiq Micro Inc. +# SPDX-License-Identifier: Apache-2.0 + +# Apollo3 Blue has 320 KB of SRAM (vs 704 KB on Apollo3 Plus). Trim the +# default kernel allocations so the sample fits. +CONFIG_HEAP_MEM_POOL_SIZE=0 +CONFIG_MAIN_STACK_SIZE=768 +CONFIG_IDLE_STACK_SIZE=256 +CONFIG_ISR_STACK_SIZE=768 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=384 + +# Disable console output completely on apollo3_evb. +CONFIG_CONSOLE=n +CONFIG_SERIAL=n +CONFIG_UART_CONSOLE=n +CONFIG_STDOUT_CONSOLE=n +CONFIG_PRINTK=n + +# MSPI driver still uses pinctrl helpers; keep pinctrl enabled. +CONFIG_PINCTRL=y diff --git a/samples/boards/ambiq/apollo3_mspi_tx_scope/boards/apollo3_evb.overlay b/samples/boards/ambiq/apollo3_mspi_tx_scope/boards/apollo3_evb.overlay new file mode 100644 index 0000000000000..b782646a7fd33 --- /dev/null +++ b/samples/boards/ambiq/apollo3_mspi_tx_scope/boards/apollo3_evb.overlay @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2026 Ambiq Micro Inc. + * SPDX-License-Identifier: Apache-2.0 + * + * Apollo3 MSPI single-board self-test overlay. No external memory device is + * required: a stub MSPI device is declared just so the controller has a + * target to address. Issuing transfers will toggle the MSPI pins (visible on + * a logic analyzer / scope on P22 D0, P24 SCK, P19 nCE) and exercise the + * ambiq,mspi-controller driver code paths. + */ + +/ { + aliases { + mspi-stub = &stub_dev; + }; +}; + +&mspi0 { + pinctrl-0 = <&mspi0_default>; + pinctrl-names = "default"; + status = "okay"; + + clock-frequency = <48000000>; + + ce-gpios = <&gpio0_31 19 GPIO_ACTIVE_LOW>; + + cmdq-buffer-location = ".mspi_buff"; + cmdq-buffer-size = <256>; + + stub_dev: stub_dev@0 { + compatible = "ambiq,mspi-device"; + status = "okay"; + reg = <0>; + mspi-max-frequency = <1000000>; + mspi-io-mode = "MSPI_IO_MODE_SINGLE"; + mspi-data-rate = "MSPI_DATA_RATE_SINGLE"; + mspi-cpp-mode = "MSPI_CPP_MODE_0"; + mspi-endian = "MSPI_BIG_ENDIAN"; + mspi-ce-polarity = "MSPI_CE_ACTIVE_LOW"; + mspi-hardware-ce-num = <0>; + read-command = <0x03>; + write-command = <0x02>; + command-length = "INSTR_1_BYTE"; + address-length = "ADDR_3_BYTE"; + rx-dummy = <0>; + tx-dummy = <0>; + ce-break-config = <0 0>; + ambiq,timing-config-mask = <0>; + }; +}; + +/* Keep P4 dedicated to MSPI D2 for this sample. */ +&ios0 { + status = "disabled"; +}; diff --git a/samples/boards/ambiq/apollo3_mspi_tx_scope/boards/apollo3p_evb.overlay b/samples/boards/ambiq/apollo3_mspi_tx_scope/boards/apollo3p_evb.overlay new file mode 100644 index 0000000000000..d736a4861a352 --- /dev/null +++ b/samples/boards/ambiq/apollo3_mspi_tx_scope/boards/apollo3p_evb.overlay @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2026 Ambiq Micro Inc. + * SPDX-License-Identifier: Apache-2.0 + * + * Apollo3 Plus single-board self-test overlay (regression of the + * Apollo3 Blue overlay). MSPI0 D0=P22, D1=P26, D2=P4, D3=P23, SCK=P24, + * nCE0=P37. No external memory is required: a stub MSPI device is + * declared so the controller has a target to address. + */ + +/ { + aliases { + mspi-stub = &stub_dev; + }; +}; + +&mspi0 { + pinctrl-0 = <&mspi0_default>; + pinctrl-names = "default"; + status = "okay"; + + clock-frequency = <48000000>; + + ce-gpios = <&gpio32_63 5 GPIO_ACTIVE_LOW>; + + cmdq-buffer-location = ".mspi_buff"; + cmdq-buffer-size = <256>; + + stub_dev: stub_dev@0 { + compatible = "ambiq,mspi-device"; + status = "okay"; + reg = <0>; + mspi-max-frequency = <1000000>; + mspi-io-mode = "MSPI_IO_MODE_SINGLE"; + mspi-data-rate = "MSPI_DATA_RATE_SINGLE"; + mspi-cpp-mode = "MSPI_CPP_MODE_0"; + mspi-endian = "MSPI_BIG_ENDIAN"; + mspi-ce-polarity = "MSPI_CE_ACTIVE_LOW"; + mspi-hardware-ce-num = <0>; + read-command = <0x03>; + write-command = <0x02>; + command-length = "INSTR_1_BYTE"; + address-length = "ADDR_3_BYTE"; + rx-dummy = <0>; + tx-dummy = <0>; + ce-break-config = <0 0>; + ambiq,timing-config-mask = <0>; + }; +}; + +/* Keep P4 dedicated to MSPI D2 for this sample. */ +&ios0 { + status = "disabled"; +}; diff --git a/samples/boards/ambiq/apollo3_mspi_tx_scope/prj.conf b/samples/boards/ambiq/apollo3_mspi_tx_scope/prj.conf new file mode 100644 index 0000000000000..c2e9c83802e83 --- /dev/null +++ b/samples/boards/ambiq/apollo3_mspi_tx_scope/prj.conf @@ -0,0 +1,8 @@ +# Copyright (c) 2026 Ambiq Micro Inc. +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_STDOUT_CONSOLE=n + +CONFIG_MSPI=y +CONFIG_MSPI_AMBIQ_CONTROLLER=y +CONFIG_MSPI_INIT_PRIORITY=40 diff --git a/samples/boards/ambiq/apollo3_mspi_tx_scope/sample.yaml b/samples/boards/ambiq/apollo3_mspi_tx_scope/sample.yaml new file mode 100644 index 0000000000000..f36aedd09cee4 --- /dev/null +++ b/samples/boards/ambiq/apollo3_mspi_tx_scope/sample.yaml @@ -0,0 +1,22 @@ +# Copyright (c) 2026 Ambiq Micro Inc. +# SPDX-License-Identifier: Apache-2.0 + +sample: + name: Apollo3 MSPI single-board TX scope test + description: > + Single-board Apollo3 MSPI transmit scope test. Starts with single-bit + transfer against a stub MSPI device, then transitions to dual/quad and + continuously transmits 32-byte packets to generate observable line + activity without requiring external MSPI memory. +tests: + sample.boards.ambiq.apollo3_mspi_tx_scope: + tags: + - drivers + - mspi + - samples + filter: dt_compat_enabled("ambiq,mspi-controller") + platform_allow: + - apollo3_evb + integration_platforms: + - apollo3_evb + build_only: true diff --git a/samples/boards/ambiq/apollo3_mspi_tx_scope/src/main.c b/samples/boards/ambiq/apollo3_mspi_tx_scope/src/main.c new file mode 100644 index 0000000000000..bf647333dd766 --- /dev/null +++ b/samples/boards/ambiq/apollo3_mspi_tx_scope/src/main.c @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2026 Ambiq Micro Inc. + * SPDX-License-Identifier: Apache-2.0 + * + * Apollo3 MSPI transmit-only test for scope observation. + * Continuously transmits 32 bytes in QUAD or DUAL mode. + * + * No external memory chip is required. With a logic analyzer or scope on + * MSPI data lines (D0-D3 for QUAD, D0-D1 for DUAL), MSPI0_SCK, and NCE + * the generated waveforms can be observed. + * + * Change TX_IO_MODE below to switch between MSPI_IO_MODE_QUAD and MSPI_IO_MODE_DUAL + */ + +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(apollo3_mspi_tx_scope, LOG_LEVEL_INF); + +#define MSPI_BUS DT_BUS(DT_ALIAS(mspi_stub)) +#define MSPI_TARGET DT_ALIAS(mspi_stub) + +/* Use QUAD mode for data transmission (change to MSPI_IO_MODE_DUAL for dual mode) */ +#define TX_IO_MODE MSPI_IO_MODE_QUAD + +#define PAYLOAD_LEN 32 +#define TX_TIMEOUT_MS 100 + +static uint8_t tx_buf[PAYLOAD_LEN]; + +static int run_tx(const struct device *bus, const struct mspi_dev_id *id, uint32_t addr) +{ + struct mspi_xfer_packet pkt = { + .dir = MSPI_TX, + .cmd = 0x02, + .address = addr, + .num_bytes = PAYLOAD_LEN, + .data_buf = tx_buf, + .cb_mask = MSPI_BUS_NO_CB, + }; + struct mspi_xfer xfer = { + .async = false, + .xfer_mode = MSPI_PIO, + .cmd_length = 1, + .addr_length = 3, + .priority = 1, + .packets = &pkt, + .num_packet = 1, + .timeout = TX_TIMEOUT_MS, + }; + + return mspi_transceive(bus, id, &xfer); +} + +int main(void) +{ + const struct device *bus = DEVICE_DT_GET(MSPI_BUS); + struct mspi_dev_id id = MSPI_DEVICE_ID_DT(MSPI_TARGET); + struct mspi_dev_cfg dev_cfg; + int ret; + + if (!device_is_ready(bus)) { + return -ENODEV; + } + + k_msleep(2000); + + /* + * Use devicetree-defined target configuration and only lock/select device. + * This avoids full reconfigure path stalls seen on AP3 with CONFIG_ALL. + */ + ret = mspi_dev_config(bus, &id, MSPI_DEVICE_CONFIG_NONE, NULL); + if (ret) { + (void)mspi_get_channel_status(bus, 0); + return ret; + } + + /* Initialize transmit buffer with pattern for scope observation */ + for (int i = 0; i < PAYLOAD_LEN; i++) { + tx_buf[i] = (uint8_t)(0x55 ^ i); + } + + memset(&dev_cfg, 0, sizeof(dev_cfg)); + dev_cfg.io_mode = TX_IO_MODE; + dev_cfg.data_rate = MSPI_DATA_RATE_SINGLE; + dev_cfg.ce_num = 0; + + ret = mspi_dev_config(bus, &id, + MSPI_DEVICE_CONFIG_IO_MODE | + MSPI_DEVICE_CONFIG_DATA_RATE | + MSPI_DEVICE_CONFIG_CE_NUM, + &dev_cfg); + if (ret) { + (void)mspi_get_channel_status(bus, 0); + return ret; + } + + /* Continuous transmission loop for scope observation */ + while (1) { + ret = run_tx(bus, &id, 0); + if (ret) { + k_msleep(1); + continue; + } + + /* Short delay between transmissions for scope triggering */ + k_msleep(100); + } + + return 0; +}