diff --git a/CODEOWNERS b/CODEOWNERS index 665973a2a996..8b936be555e0 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -294,6 +294,7 @@ /drivers/gpio/ @nrfconnect/ncs-co-drivers @nrfconnect/ncs-ll-ursus /drivers/hw_cc3xx/ @nrfconnect/ncs-co-drivers @nrfconnect/ncs-aegir /drivers/mpsl/ @nrfconnect/ncs-co-drivers @nrfconnect/ncs-dragoon +/drivers/mspi/ @nrfconnect/ncs-co-drivers @nrfconnect/ncs-ll-ursus /drivers/net/ @nrfconnect/ncs-co-drivers @doki-nordic /drivers/serial/ @nrfconnect/ncs-co-drivers @nordic-krch /drivers/sensor/bh1749/ @nrfconnect/ncs-co-drivers @nrfconnect/ncs-cia @@ -330,6 +331,7 @@ /include/drivers/flash/ @nrfconnect/ncs-co-drivers /include/drivers/gpio/ @nrfconnect/ncs-co-drivers @nrfconnect/ncs-ll-ursus /include/drivers/bme68x_iaq.h @nrfconnect/ncs-co-drivers @nrfconnect/ncs-cia +/include/drivers/mspi/nrfe_mspi.h @nrfconnect/ncs-co-drivers @nrfconnect/ncs-ll-ursus /include/drivers/sensor_sim.h @nrfconnect/ncs-co-drivers @nrfconnect/ncs-cia /include/drivers/sensor_stub.h @nrfconnect/ncs-co-drivers @nrfconnect/ncs-cia /include/emds/ @balaklaka @nrfconnect/ncs-paladin @@ -670,6 +672,7 @@ /scripts/print_docker_image.sh @nrfconnect/ncs-ci /scripts/print_toolchain_checksum.sh @nrfconnect/ncs-ci /scripts/sdp/ @nrfconnect/ncs-ll-ursus +/scripts/twister/alt/zephyr/tests/drivers/mspi/api/testcase.yaml @nrfconnect/ncs-ll-ursus /scripts/docker/*.rst @nrfconnect/ncs-doc-leads /scripts/hid_configurator/*.rst @nrfconnect/ncs-si-bluebagel-doc diff --git a/applications/sdp/mspi/boards/nrf54l15dk_nrf54l15_cpuflpr.overlay b/applications/sdp/mspi/boards/nrf54l15dk_nrf54l15_cpuflpr.overlay index 4afa34f53f35..c3c8081a6be0 100644 --- a/applications/sdp/mspi/boards/nrf54l15dk_nrf54l15_cpuflpr.overlay +++ b/applications/sdp/mspi/boards/nrf54l15dk_nrf54l15_cpuflpr.overlay @@ -4,6 +4,58 @@ * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause */ +/ { + soc { + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + + sram_tx: memory@2003c000 { + reg = <0x2003c000 0x0800>; + }; + + sram_rx: memory@2003c800 { + reg = <0x2003c800 0x0800>; + }; + }; + }; + + ipc { + ipc0: ipc0 { + compatible = "zephyr,ipc-icmsg"; + tx-region = <&sram_tx>; + rx-region = <&sram_rx>; + mboxes = <&cpuflpr_vevif_rx 16>, <&cpuflpr_vevif_tx 20>; + mbox-names = "rx", "tx"; + status = "okay"; + }; + }; +}; + +&cpuflpr_rram { + reg = <0x17a000 DT_SIZE_K(12)>; +}; + +&cpuflpr_code_partition { + reg = <0x0 DT_SIZE_K(12)>; +}; + +&cpuflpr_sram { + reg = <0x2003d000 DT_SIZE_K(12)>; + ranges = <0x0 0x2003d000 0x3000>; +}; + +&cpuflpr_vevif_rx { + status = "okay"; + interrupts = <16 NRF_DEFAULT_IRQ_PRIORITY>; + nordic,tasks = <1>; + nordic,tasks-mask = <0x00010000>; +}; + +&cpuflpr_vevif_tx { + status = "okay"; +}; + &gpio0 { status = "disabled"; }; diff --git a/applications/sdp/mspi/prj.conf b/applications/sdp/mspi/prj.conf index 33d849aaadd3..5db8be05d329 100644 --- a/applications/sdp/mspi/prj.conf +++ b/applications/sdp/mspi/prj.conf @@ -1,3 +1,3 @@ -CONFIG_MBOX=n -CONFIG_IPC_SERVICE=n -CONFIG_IPC_SERVICE_BACKEND_ICMSG=n +CONFIG_MBOX=y +CONFIG_IPC_SERVICE=y +CONFIG_IPC_SERVICE_BACKEND_ICMSG=y diff --git a/applications/sdp/mspi/sample.yaml b/applications/sdp/mspi/sample.yaml index 5648893e2e5b..46e595632fe4 100644 --- a/applications/sdp/mspi/sample.yaml +++ b/applications/sdp/mspi/sample.yaml @@ -5,7 +5,7 @@ common: integration_platforms: - nrf54l15dk/nrf54l15/cpuflpr tests: - applications.sdp.mspi.icmsg: + applications.sdp.mspi: build_only: true sysbuild: true platform_allow: nrf54l15dk/nrf54l15/cpuflpr diff --git a/applications/sdp/mspi/src/hrt/hrt.c b/applications/sdp/mspi/src/hrt/hrt.c index 35e8bd3ff8bb..b4588856e1e9 100644 --- a/applications/sdp/mspi/src/hrt/hrt.c +++ b/applications/sdp/mspi/src/hrt/hrt.c @@ -22,12 +22,10 @@ void write_single_by_word(volatile struct hrt_ll_xfer xfer_ll_params) NRFX_ASSERT(xfer_ll_params.word_size <= MAX_WORD_SIZE); /* Configuration step */ dir = nrf_vpr_csr_vio_dir_get(); - - nrf_vpr_csr_vio_dir_set(dir | PIN_DIR_OUT_MASK(D0_PIN)); + nrf_vpr_csr_vio_dir_set(dir | PIN_DIR_OUT_MASK(VIO(NRFE_MSPI_DQ0_PIN_NUMBER))); out = nrf_vpr_csr_vio_out_get(); - - nrf_vpr_csr_vio_out_set(out | PIN_OUT_LOW_MASK(D0_PIN)); + nrf_vpr_csr_vio_out_set(out | PIN_OUT_LOW_MASK(VIO(NRFE_MSPI_DQ0_PIN_NUMBER))); nrf_vpr_csr_vio_mode_out_set(&out_mode); nrf_vpr_csr_vio_mode_in_buffered_set(NRF_VPR_CSR_VIO_MODE_IN_CONTINUOUS); @@ -59,9 +57,9 @@ void write_single_by_word(volatile struct hrt_ll_xfer xfer_ll_params) /* Enable CS */ out = nrf_vpr_csr_vio_out_get(); - out &= ~PIN_OUT_HIGH_MASK(CS_PIN); - out |= xfer_ll_params.ce_enable_state ? PIN_OUT_HIGH_MASK(CS_PIN) - : PIN_OUT_LOW_MASK(CS_PIN); + out &= ~PIN_OUT_HIGH_MASK(VIO(NRFE_MSPI_CS0_PIN_NUMBER)); + out |= xfer_ll_params.ce_enable_state ? PIN_OUT_HIGH_MASK(VIO(NRFE_MSPI_CS0_PIN_NUMBER)) + : PIN_OUT_LOW_MASK(VIO(NRFE_MSPI_CS0_PIN_NUMBER)); nrf_vpr_csr_vio_out_set(out); /* Start counter */ @@ -84,9 +82,11 @@ void write_single_by_word(volatile struct hrt_ll_xfer xfer_ll_params) /* Disable CS */ if (!xfer_ll_params.ce_hold) { out = nrf_vpr_csr_vio_out_get(); - out &= ~(PIN_OUT_HIGH_MASK(CS_PIN) | PIN_OUT_HIGH_MASK(SCLK_PIN)); - out |= xfer_ll_params.ce_enable_state ? PIN_OUT_LOW_MASK(CS_PIN) - : PIN_OUT_HIGH_MASK(CS_PIN); + out &= ~(PIN_OUT_HIGH_MASK(VIO(NRFE_MSPI_CS0_PIN_NUMBER)) | + PIN_OUT_HIGH_MASK(VIO(NRFE_MSPI_SCK_PIN_NUMBER))); + out |= xfer_ll_params.ce_enable_state + ? PIN_OUT_LOW_MASK(VIO(NRFE_MSPI_CS0_PIN_NUMBER)) + : PIN_OUT_HIGH_MASK(VIO(NRFE_MSPI_CS0_PIN_NUMBER)); nrf_vpr_csr_vio_out_set(out); } @@ -109,13 +109,17 @@ void write_quad_by_word(volatile struct hrt_ll_xfer xfer_ll_params) /* Configuration step */ dir = nrf_vpr_csr_vio_dir_get(); - nrf_vpr_csr_vio_dir_set(dir | PIN_DIR_OUT_MASK(D0_PIN) | PIN_DIR_OUT_MASK(D1_PIN) | - PIN_DIR_OUT_MASK(D2_PIN) | PIN_DIR_OUT_MASK(D3_PIN)); + nrf_vpr_csr_vio_dir_set(dir | PIN_DIR_OUT_MASK(VIO(NRFE_MSPI_DQ0_PIN_NUMBER)) | + PIN_DIR_OUT_MASK(VIO(NRFE_MSPI_DQ1_PIN_NUMBER)) | + PIN_DIR_OUT_MASK(VIO(NRFE_MSPI_DQ2_PIN_NUMBER)) | + PIN_DIR_OUT_MASK(VIO(NRFE_MSPI_DQ3_PIN_NUMBER))); out = nrf_vpr_csr_vio_out_get(); - nrf_vpr_csr_vio_out_set(out | PIN_OUT_LOW_MASK(D0_PIN) | PIN_OUT_LOW_MASK(D1_PIN) | - PIN_OUT_LOW_MASK(D2_PIN) | PIN_OUT_LOW_MASK(D3_PIN)); + nrf_vpr_csr_vio_out_set(out | PIN_OUT_LOW_MASK(VIO(NRFE_MSPI_DQ0_PIN_NUMBER)) | + PIN_OUT_LOW_MASK(VIO(NRFE_MSPI_DQ1_PIN_NUMBER)) | + PIN_OUT_LOW_MASK(VIO(NRFE_MSPI_DQ2_PIN_NUMBER)) | + PIN_OUT_LOW_MASK(VIO(NRFE_MSPI_DQ3_PIN_NUMBER))); nrf_vpr_csr_vio_mode_out_set(&out_mode); nrf_vpr_csr_vio_mode_in_buffered_set(NRF_VPR_CSR_VIO_MODE_IN_CONTINUOUS); @@ -147,9 +151,9 @@ void write_quad_by_word(volatile struct hrt_ll_xfer xfer_ll_params) /* Enable CS */ out = nrf_vpr_csr_vio_out_get(); - out &= ~PIN_OUT_HIGH_MASK(CS_PIN); - out |= xfer_ll_params.ce_enable_state ? PIN_OUT_HIGH_MASK(CS_PIN) - : PIN_OUT_LOW_MASK(CS_PIN); + out &= ~PIN_OUT_HIGH_MASK(VIO(NRFE_MSPI_CS0_PIN_NUMBER)); + out |= xfer_ll_params.ce_enable_state ? PIN_OUT_HIGH_MASK(VIO(NRFE_MSPI_CS0_PIN_NUMBER)) + : PIN_OUT_LOW_MASK(VIO(NRFE_MSPI_CS0_PIN_NUMBER)); nrf_vpr_csr_vio_out_set(out); /* Start counter */ @@ -171,9 +175,11 @@ void write_quad_by_word(volatile struct hrt_ll_xfer xfer_ll_params) /* Disable CS */ if (!xfer_ll_params.ce_hold) { out = nrf_vpr_csr_vio_out_get(); - out &= ~(PIN_OUT_HIGH_MASK(CS_PIN) | PIN_OUT_HIGH_MASK(SCLK_PIN)); - out |= xfer_ll_params.ce_enable_state ? PIN_OUT_LOW_MASK(CS_PIN) - : PIN_OUT_HIGH_MASK(CS_PIN); + out &= ~(PIN_OUT_HIGH_MASK(VIO(NRFE_MSPI_CS0_PIN_NUMBER)) | + PIN_OUT_HIGH_MASK(VIO(NRFE_MSPI_SCK_PIN_NUMBER))); + out |= xfer_ll_params.ce_enable_state + ? PIN_OUT_LOW_MASK(VIO(NRFE_MSPI_CS0_PIN_NUMBER)) + : PIN_OUT_HIGH_MASK(VIO(NRFE_MSPI_CS0_PIN_NUMBER)); nrf_vpr_csr_vio_out_set(out); } diff --git a/applications/sdp/mspi/src/hrt/hrt.h b/applications/sdp/mspi/src/hrt/hrt.h index c5e47618720d..94b88a515796 100644 --- a/applications/sdp/mspi/src/hrt/hrt.h +++ b/applications/sdp/mspi/src/hrt/hrt.h @@ -9,13 +9,7 @@ #include #include - -#define SCLK_PIN 0 -#define D0_PIN 1 -#define D1_PIN 2 -#define D2_PIN 3 -#define D3_PIN 4 -#define CS_PIN 5 +#include /* Max word size. */ #define MAX_WORD_SIZE NRF_VPR_CSR_VIO_SHIFT_CNT_OUT_BUFFERED_MAX diff --git a/applications/sdp/mspi/src/main.c b/applications/sdp/mspi/src/main.c index 69a85649a489..2aa528dae0ab 100644 --- a/applications/sdp/mspi/src/main.c +++ b/applications/sdp/mspi/src/main.c @@ -4,10 +4,12 @@ * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause */ -#include "./hrt/hrt.h" +#include "hrt/hrt.h" -#include #include +#include +#include + #include #include #include @@ -30,6 +32,26 @@ #define VEVIF_IRQN(vevif) VEVIF_IRQN_1(vevif) #define VEVIF_IRQN_1(vevif) VPRCLIC_##vevif##_IRQn +typedef struct __packed { + uint8_t opcode; + struct mspi_cfg cfg; +} nrfe_mspi_cfg_t; + +typedef struct __packed { + uint8_t opcode; + struct mspi_dev_cfg cfg; +} nrfe_mspi_dev_cfg_t; + +typedef struct __packed { + uint8_t opcode; + struct mspi_xfer xfer; +} nrfe_mspi_xfer_t; + +typedef struct __packed { + uint8_t opcode; + struct mspi_xfer_packet packet; +} nrfe_mspi_xfer_packet_t; + struct mspi_config { uint8_t *data; uint8_t data_len; @@ -58,6 +80,90 @@ volatile struct hrt_ll_xfer xfer_ll_params = { .ce_enable_state = false, }; +static struct ipc_ept ep; +static atomic_t ipc_atomic_sem = ATOMIC_INIT(0); + +static void process_packet(const void *data, size_t len, void *priv); + +static void ep_bound(void *priv) +{ + (void)priv; + + atomic_set_bit(&ipc_atomic_sem, NRFE_MSPI_EP_BOUNDED); +} + +static struct ipc_ept_cfg ep_cfg = { + .cb = { + .bound = ep_bound, + .received = process_packet, + }, +}; + +static void process_packet(const void *data, size_t len, void *priv) +{ + (void)priv; + (void)len; + nrfe_mspi_flpr_response_t response; + uint8_t opcode = *(uint8_t *)data; + + response.opcode = opcode; + + switch (opcode) { + case NRFE_MSPI_CONFIG_PINS: { + /* TODO: Process pinctrl config data + * nrfe_mspi_pinctrl_soc_pin_t *pins_cfg = (nrfe_mspi_pinctrl_soc_pin_t *)data; + * response.opcode = pins_cfg->opcode; + * + * for (uint8_t i = 0; i < NRFE_MSPI_PINS_MAX; i++) { + * uint32_t psel = NRF_GET_PIN(pins_cfg->pin[i]); + * uint32_t fun = NRF_GET_FUN(pins_cfg->pin[i]); + * NRF_GPIO_Type *reg = nrf_gpio_pin_port_decode(&psel); + * } + */ + break; + } + case NRFE_MSPI_CONFIG_CTRL: { + /* TODO: Process controller config data + * nrfe_mspi_cfg_t *cfg = (nrfe_mspi_cfg_t *)data; + * response.opcode = cfg->opcode; + */ + break; + } + case NRFE_MSPI_CONFIG_DEV: { + /* TODO: Process device config data + * nrfe_mspi_dev_cfg_t *cfg = (nrfe_mspi_dev_cfg_t *)data; + * response.opcode = cfg->opcode; + */ + break; + } + case NRFE_MSPI_CONFIG_XFER: { + /* TODO: Process xfer config data + * nrfe_mspi_xfer_t *xfer = (nrfe_mspi_xfer_t *)data; + * response.opcode = xfer->opcode; + */ + break; + } + case NRFE_MSPI_TX: + case NRFE_MSPI_TXRX: { + nrfe_mspi_xfer_packet_t *packet = (nrfe_mspi_xfer_packet_t *)data; + + response.opcode = packet->opcode; + + if (packet->packet.dir == MSPI_RX) { + /* TODO: Process received data */ + } else if (packet->packet.dir == MSPI_TX) { + /* TODO: Send data */ + } + break; + } + default: + response.opcode = NRFE_MSPI_WRONG_OPCODE; + break; + } + + ipc_service_send(&ep, (const void *)&response.opcode, sizeof(response)); +} + void configure_clock(enum mspi_cpp_mode cpp_mode) { nrf_vpr_csr_vio_config_t vio_config = { @@ -65,27 +171,27 @@ void configure_clock(enum mspi_cpp_mode cpp_mode) .stop_cnt = 0, }; - nrf_vpr_csr_vio_dir_set(PIN_DIR_OUT_MASK(SCLK_PIN)); + nrf_vpr_csr_vio_dir_set(PIN_DIR_OUT_MASK(VIO(NRFE_MSPI_SCK_PIN_NUMBER))); switch (cpp_mode) { case MSPI_CPP_MODE_0: { vio_config.clk_polarity = 0; - nrf_vpr_csr_vio_out_set(PIN_OUT_LOW_MASK(SCLK_PIN)); + nrf_vpr_csr_vio_out_set(PIN_OUT_LOW_MASK(VIO(NRFE_MSPI_SCK_PIN_NUMBER))); break; } case MSPI_CPP_MODE_1: { vio_config.clk_polarity = 1; - nrf_vpr_csr_vio_out_set(PIN_OUT_LOW_MASK(SCLK_PIN)); + nrf_vpr_csr_vio_out_set(PIN_OUT_LOW_MASK(VIO(NRFE_MSPI_SCK_PIN_NUMBER))); break; } case MSPI_CPP_MODE_2: { vio_config.clk_polarity = 1; - nrf_vpr_csr_vio_out_set(PIN_OUT_HIGH_MASK(SCLK_PIN)); + nrf_vpr_csr_vio_out_set(PIN_OUT_HIGH_MASK(VIO(NRFE_MSPI_SCK_PIN_NUMBER))); break; } case MSPI_CPP_MODE_3: { vio_config.clk_polarity = 0; - nrf_vpr_csr_vio_out_set(PIN_OUT_HIGH_MASK(SCLK_PIN)); + nrf_vpr_csr_vio_out_set(PIN_OUT_HIGH_MASK(VIO(NRFE_MSPI_SCK_PIN_NUMBER))); break; } } @@ -150,23 +256,48 @@ __attribute__((interrupt)) void hrt_handler_write_quad(void) write_quad_by_word(xfer_ll_params); } +static int backend_init(void) +{ + int ret = 0; + const struct device *ipc0_instance; + volatile uint32_t delay = 0; + +#if !defined(CONFIG_SYS_CLOCK_EXISTS) + /* Wait a little bit for IPC service to be ready on APP side */ + while (delay < 1000) { + delay++; + } +#endif + + ipc0_instance = DEVICE_DT_GET(DT_NODELABEL(ipc0)); + + ret = ipc_service_open_instance(ipc0_instance); + if ((ret < 0) && (ret != -EALREADY)) { + return ret; + } + + ret = ipc_service_register_endpoint(ipc0_instance, &ep, &ep_cfg); + if (ret < 0) { + return ret; + } + + /* Wait for endpoint to be bound */ + while (!atomic_test_and_clear_bit(&ipc_atomic_sem, NRFE_MSPI_EP_BOUNDED)) { + } + + return 0; +} + int main(void) { + int ret = 0; uint16_t direction; uint16_t output; - /* This is temporary sample data. */ - uint8_t data[30] = {0xa3, 0x21, 0x54, 0x3a, 0x55, 0xa5, 0x45, 0x35, 0x34, 0x23, - 0xa3, 0xad, 0x97, 0xb2, 0x56, 0x54, 0x38, 0x88, 0x0, 0x5, - 0x33, 0x6, 0x34, 0x6, 0x57, 0x7, 0xbb, 0xba, 0xa3, 0xf6}; - - /* This pin initialization is temporary until code is merged with app with GPIO - * initialization - */ - nrf_gpio_pin_dir_t dir = NRF_GPIO_PIN_DIR_OUTPUT; - nrf_gpio_pin_input_t input = NRF_GPIO_PIN_INPUT_DISCONNECT; - nrf_gpio_pin_pull_t pull = NRF_GPIO_PIN_NOPULL; - nrf_gpio_pin_drive_t drive = NRF_GPIO_PIN_E0E1; + ret = backend_init(); + if (ret < 0) { + return 0; + } IRQ_DIRECT_CONNECT(HRT_VEVIF_IDX_WRITE_SINGLE, HRT_IRQ_PRIORITY, hrt_handler_write_single, 0); @@ -177,48 +308,23 @@ int main(void) nrf_vpr_csr_rtperiph_enable_set(true); - /* This pin initialization is temporary until code is merged with app with GPIO - * initialization - */ - nrfy_gpio_reconfigure(NRF_GPIO_PIN_MAP(2, SCLK_PIN), &dir, &input, &pull, &drive, NULL); - nrfy_gpio_reconfigure(NRF_GPIO_PIN_MAP(2, D0_PIN), &dir, &input, &pull, &drive, NULL); - nrfy_gpio_reconfigure(NRF_GPIO_PIN_MAP(2, D1_PIN), &dir, &input, &pull, &drive, NULL); - nrfy_gpio_reconfigure(NRF_GPIO_PIN_MAP(2, D2_PIN), &dir, &input, &pull, &drive, NULL); - nrfy_gpio_reconfigure(NRF_GPIO_PIN_MAP(2, D3_PIN), &dir, &input, &pull, &drive, NULL); - nrfy_gpio_reconfigure(NRF_GPIO_PIN_MAP(2, CS_PIN), &dir, &input, &pull, &drive, NULL); - - nrfy_gpio_pin_control_select(NRF_GPIO_PIN_MAP(2, SCLK_PIN), NRF_GPIO_PIN_SEL_VPR); - nrfy_gpio_pin_control_select(NRF_GPIO_PIN_MAP(2, D0_PIN), NRF_GPIO_PIN_SEL_VPR); - nrfy_gpio_pin_control_select(NRF_GPIO_PIN_MAP(2, D1_PIN), NRF_GPIO_PIN_SEL_VPR); - nrfy_gpio_pin_control_select(NRF_GPIO_PIN_MAP(2, D2_PIN), NRF_GPIO_PIN_SEL_VPR); - nrfy_gpio_pin_control_select(NRF_GPIO_PIN_MAP(2, D3_PIN), NRF_GPIO_PIN_SEL_VPR); - nrfy_gpio_pin_control_select(NRF_GPIO_PIN_MAP(2, CS_PIN), NRF_GPIO_PIN_SEL_VPR); - configure_clock(MSPI_CPP_MODE_0); direction = nrf_vpr_csr_vio_dir_get(); - - nrf_vpr_csr_vio_dir_set(direction | PIN_DIR_OUT_MASK(CS_PIN)); + nrf_vpr_csr_vio_dir_set(direction | PIN_DIR_OUT_MASK(VIO(NRFE_MSPI_CS0_PIN_NUMBER))); output = nrf_vpr_csr_vio_out_get(); + nrf_vpr_csr_vio_out_set(output | PIN_OUT_HIGH_MASK(VIO(NRFE_MSPI_CS0_PIN_NUMBER))); - nrf_vpr_csr_vio_out_set(output | PIN_OUT_HIGH_MASK(CS_PIN)); - + /* This initialization is temporary until code is merged with APP core part */ mspi_dev_configs.ce_polarity = MSPI_CE_ACTIVE_LOW; mspi_dev_configs.io_mode = MSPI_IO_MODE_SINGLE; mspi_dev_configs.cmd_length = 32; mspi_dev_configs.addr_length = 32; - /* This is here only temporarly to set command and address parameters. */ data_buffer[XFER_COMMAND_IDX] = 0xe5b326c1; data_buffer[XFER_ADDRESS_IDX] = 0xaabbccdd; - prepare_and_send_data(data, 30); - - mspi_dev_configs.io_mode = MSPI_IO_MODE_QUAD; - - prepare_and_send_data(data, 30); - while (true) { k_cpu_idle(); } diff --git a/cmake/sysbuild/sdp.cmake b/cmake/sysbuild/sdp.cmake index f440b687796b..3dc1d8c0f734 100644 --- a/cmake/sysbuild/sdp.cmake +++ b/cmake/sysbuild/sdp.cmake @@ -18,6 +18,9 @@ if(SB_CONFIG_SDP) set(snippet_name "sdp-gpio-icbmsg") endif() endif() + if(SB_CONFIG_SDP_MSPI) + set(snippet_name "sdp-mspi") + endif() sdp_apply_snippets(${snippet_name}) set(snippet_name) diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index 3a178f3e1a93..685c1253103a 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -11,6 +11,7 @@ add_subdirectory(hw_cc3xx) if (CONFIG_MPSL AND NOT CONFIG_MPSL_FEM_ONLY) add_subdirectory(mpsl) endif() +add_subdirectory_ifdef(CONFIG_MSPI mspi) add_subdirectory_ifdef(CONFIG_NETWORKING net) add_subdirectory_ifdef(CONFIG_SENSOR sensor) add_subdirectory(serial) diff --git a/drivers/Kconfig b/drivers/Kconfig index 554b6a917249..3004a83b241e 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -16,6 +16,7 @@ if MPSL && !MPSL_FEM_ONLY rsource "mpsl/Kconfig" endif +rsource "mspi/Kconfig" rsource "net/Kconfig" rsource "sensor/Kconfig" rsource "serial/Kconfig" @@ -23,6 +24,7 @@ rsource "serial/Kconfig" config NRFE bool default y if GPIO_NRFE + default y if MSPI_NRFE # Temporary kconfig to include DPPI channel allocation for NRFE endmenu diff --git a/drivers/mspi/CMakeLists.txt b/drivers/mspi/CMakeLists.txt new file mode 100644 index 000000000000..9e00cb992f4c --- /dev/null +++ b/drivers/mspi/CMakeLists.txt @@ -0,0 +1,8 @@ +# +# Copyright (c) 2024 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +zephyr_library_amend() +zephyr_library_sources_ifdef(CONFIG_MSPI_NRFE mspi_nrfe.c) diff --git a/drivers/mspi/Kconfig b/drivers/mspi/Kconfig new file mode 100644 index 000000000000..a2f4c8979ff9 --- /dev/null +++ b/drivers/mspi/Kconfig @@ -0,0 +1,8 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + +if MSPI + +rsource "Kconfig.nrfe" + +endif # MSPI diff --git a/drivers/mspi/Kconfig.nrfe b/drivers/mspi/Kconfig.nrfe new file mode 100644 index 000000000000..765f104ed2cc --- /dev/null +++ b/drivers/mspi/Kconfig.nrfe @@ -0,0 +1,28 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + +# +# MSPI_NRFE Driver +# +menuconfig MSPI_NRFE + bool "SDP MSPI driver" + default y + depends on DT_HAS_NORDIC_NRFE_MSPI_CONTROLLER_ENABLED + select MBOX + select IPC_SERVICE + select IPC_SERVICE_BACKEND_ICMSG + help + Enable SDP MSPI driver. + +if MSPI_NRFE + +config MSPI_NRFE_INIT_PRIORITY + int "SDP MSPI init priority" + depends on MSPI_NRFE + default MSPI_INIT_PRIORITY + help + SDP MSPI driver device initialization priority. + SDP MSPI initialization depends on IPC initialization + which is done at the same init level and has init priority equal to 46. + +endif # MSPI_NRFE diff --git a/drivers/mspi/mspi_nrfe.c b/drivers/mspi/mspi_nrfe.c new file mode 100644 index 000000000000..96eb895e812d --- /dev/null +++ b/drivers/mspi/mspi_nrfe.c @@ -0,0 +1,716 @@ +/* + * Copyright (c) 2024, Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#define DT_DRV_COMPAT nordic_nrfe_mspi_controller + +#include +#include +#include +#include +#include +#if !defined(CONFIG_MULTITHREADING) +#include +#endif +#include +LOG_MODULE_REGISTER(mspi_nrfe, CONFIG_MSPI_LOG_LEVEL); + +#include +#include + +#define MSPI_NRFE_NODE DT_DRV_INST(0) +#define MAX_TX_MSG_SIZE (DT_REG_SIZE(DT_NODELABEL(sram_tx))) +#define MAX_RX_MSG_SIZE (DT_REG_SIZE(DT_NODELABEL(sram_rx))) +#define CONFIG_TIMEOUT_MS 100 +#define EP_SEND_TIMEOUT_MS 10 + +#define SDP_MPSI_PINCTRL_DEV_CONFIG_INIT(node_id) \ + { \ + .reg = PINCTRL_REG_NONE, \ + .states = Z_PINCTRL_STATES_NAME(node_id), \ + .state_cnt = ARRAY_SIZE(Z_PINCTRL_STATES_NAME(node_id)), \ + } + +#define SDP_MSPI_PINCTRL_DT_DEFINE(node_id) \ + LISTIFY(DT_NUM_PINCTRL_STATES(node_id), Z_PINCTRL_STATE_PINS_DEFINE, (;), node_id); \ + Z_PINCTRL_STATES_DEFINE(node_id) \ + Z_PINCTRL_DEV_CONFIG_STATIC Z_PINCTRL_DEV_CONFIG_CONST struct pinctrl_dev_config \ + Z_PINCTRL_DEV_CONFIG_NAME(node_id) = SDP_MPSI_PINCTRL_DEV_CONFIG_INIT(node_id) + +SDP_MSPI_PINCTRL_DT_DEFINE(MSPI_NRFE_NODE); + +static struct ipc_ept ep; +static size_t ipc_received; +static uint8_t *ipc_receive_buffer; + +#if defined(CONFIG_MULTITHREADING) +static K_SEM_DEFINE(ipc_sem, 0, 1); +static K_SEM_DEFINE(ipc_sem_cfg, 0, 1); +static K_SEM_DEFINE(ipc_sem_xfer, 0, 1); +#else +static atomic_t ipc_atomic_sem = ATOMIC_INIT(0); +#endif + +#define MSPI_CONFIG \ + { \ + .channel_num = 0, \ + .op_mode = DT_PROP_OR(MSPI_NRFE_NODE, op_mode, MSPI_OP_MODE_CONTROLLER), \ + .duplex = DT_PROP_OR(MSPI_NRFE_NODE, duplex, MSPI_FULL_DUPLEX), \ + .dqs_support = DT_PROP_OR(MSPI_NRFE_NODE, dqs_support, false), \ + .num_periph = DT_CHILD_NUM(MSPI_NRFE_NODE), \ + .max_freq = DT_PROP(MSPI_NRFE_NODE, clock_frequency), \ + .re_init = true, \ + .sw_multi_periph = false, \ + } + +struct mspi_nrfe_data { + struct mspi_xfer xfer; + struct mspi_dev_id dev_id; + struct mspi_dev_cfg dev_cfg; +}; + +static struct mspi_nrfe_data dev_data; + +struct mspi_nrfe_config { + struct mspi_cfg mspicfg; + const struct pinctrl_dev_config *pcfg; +}; + +static const struct mspi_nrfe_config dev_config = { + .mspicfg = MSPI_CONFIG, + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(0), +}; + +static void ipc_recv_clbk(const void *data, size_t len); + +static void ep_bound(void *priv) +{ + ipc_received = 0; +#if defined(CONFIG_MULTITHREADING) + k_sem_give(&ipc_sem); +#else + atomic_set_bit(&ipc_atomic_sem, NRFE_MSPI_EP_BOUNDED); +#endif + LOG_DBG("Ep bounded"); +} + +static void ep_recv(const void *data, size_t len, void *priv) +{ + (void)priv; + + ipc_recv_clbk(data, len); +} + +static struct ipc_ept_cfg ep_cfg = { + .cb = { + .bound = ep_bound, + .received = ep_recv, + }, +}; + +/** + * @brief IPC receive callback function. + * + * This function is called by the IPC stack when a message is received from the + * other core. The function checks the opcode of the received message and takes + * appropriate action. + * + * @param data Pointer to the received message. + * @param len Length of the received message. + */ +static void ipc_recv_clbk(const void *data, size_t len) +{ + nrfe_mspi_flpr_response_t *response = (nrfe_mspi_flpr_response_t *)data; + + switch (response->opcode) { + case NRFE_MSPI_CONFIG_PINS: { +#if defined(CONFIG_MULTITHREADING) + k_sem_give(&ipc_sem_cfg); +#else + atomic_set_bit(&ipc_atomic_sem, NRFE_MSPI_CONFIG_PINS); +#endif + break; + } + case NRFE_MSPI_CONFIG_CTRL: { +#if defined(CONFIG_MULTITHREADING) + k_sem_give(&ipc_sem_cfg); +#else + atomic_set_bit(&ipc_atomic_sem, NRFE_MSPI_CONFIG_CTRL); +#endif + break; + } + case NRFE_MSPI_CONFIG_DEV: { +#if defined(CONFIG_MULTITHREADING) + k_sem_give(&ipc_sem_cfg); +#else + atomic_set_bit(&ipc_atomic_sem, NRFE_MSPI_CONFIG_DEV); +#endif + break; + } + case NRFE_MSPI_CONFIG_XFER: { +#if defined(CONFIG_MULTITHREADING) + k_sem_give(&ipc_sem_cfg); +#else + atomic_set_bit(&ipc_atomic_sem, NRFE_MSPI_CONFIG_XFER); +#endif + break; + } + case NRFE_MSPI_TX: { +#if defined(CONFIG_MULTITHREADING) + k_sem_give(&ipc_sem_xfer); +#else + atomic_set_bit(&ipc_atomic_sem, NRFE_MSPI_TX); +#endif + break; + } + case NRFE_MSPI_TXRX: { + if (len > 0) { + ipc_received = len - 1; + ipc_receive_buffer = (uint8_t *)&response->data; + } +#if defined(CONFIG_MULTITHREADING) + k_sem_give(&ipc_sem_xfer); +#else + atomic_set_bit(&ipc_atomic_sem, NRFE_MSPI_TXRX); +#endif + break; + } + default: { + LOG_ERR("Invalid response opcode: %d", response->opcode); + break; + } + } + + LOG_DBG("Received msg with opcode: %d", response->opcode); +} + +/** + * @brief Send data to the flpr with the given opcode. + * + * @param opcode The opcode of the message to send. + * @param data The data to send. + * @param len The length of the data to send. + * + * @return 0 on success, -ENOMEM if there is no space in the buffer, + * -ETIMEDOUT if the transfer timed out. + */ +static int mspi_ipc_data_send(enum nrfe_mspi_opcode opcode, const void *data, size_t len) +{ + int rc; + + LOG_DBG("Sending msg with opcode: %d", (uint8_t)opcode); +#if defined(CONFIG_SYS_CLOCK_EXISTS) + uint32_t start = k_uptime_get_32(); +#else + uint32_t repeat = EP_SEND_TIMEOUT_MS; +#endif +#if !defined(CONFIG_MULTITHREADING) + atomic_clear_bit(&ipc_atomic_sem, opcode); +#endif + + do { + rc = ipc_service_send(&ep, data, len); +#if defined(CONFIG_SYS_CLOCK_EXISTS) + if ((k_uptime_get_32() - start) > EP_SEND_TIMEOUT_MS) { +#else + repeat--; + if ((rc < 0) && (repeat == 0)) { +#endif + break; + }; + } while (rc == -ENOMEM); /* No space in the buffer. Retry. */ + + return rc; +} + +/** + * @brief Waits for a response from the peer with the given opcode. + * + * @param opcode The opcode of the response to wait for. + * @param timeout The timeout in milliseconds. + * + * @return 0 on success, -ETIMEDOUT if the operation timed out. + */ +static int nrfe_mspi_wait_for_response(enum nrfe_mspi_opcode opcode, uint32_t timeout) +{ +#if defined(CONFIG_MULTITHREADING) + int ret = 0; + + switch (opcode) { + case NRFE_MSPI_CONFIG_PINS: + case NRFE_MSPI_CONFIG_CTRL: + case NRFE_MSPI_CONFIG_DEV: + case NRFE_MSPI_CONFIG_XFER: { + ret = k_sem_take(&ipc_sem_cfg, K_MSEC(timeout)); + break; + } + case NRFE_MSPI_TX: + case NRFE_MSPI_TXRX: + ret = k_sem_take(&ipc_sem_xfer, K_MSEC(timeout)); + break; + default: + break; + } + + if (ret < 0) { + return -ETIMEDOUT; + } +#else +#if defined(CONFIG_SYS_CLOCK_EXISTS) + uint32_t start = k_uptime_get_32(); +#else + uint32_t repeat = timeout * 1000; /* Convert ms to us */ +#endif + while (!atomic_test_and_clear_bit(&ipc_atomic_sem, opcode)) { +#if defined(CONFIG_SYS_CLOCK_EXISTS) + if ((k_uptime_get_32() - start) > timeout) { + return -ETIMEDOUT; + }; +#else + repeat--; + if (!repeat) { + return -ETIMEDOUT; + }; +#endif + k_sleep(K_USEC(1)); + } +#endif /* CONFIG_MULTITHREADING */ + return 0; +} + +/** + * @brief Send a data struct to the FLPR core using the IPC service. + * + * The function sends a data structure to the FLPR core, + * inserting a byte at the beginning responsible for the opcode. + * + * @param opcode The NRFE MSPI opcode. + * @param data The data to send. + * @param len The length of the data to send. + * + * @return 0 on success, negative errno code on failure. + */ +static int send_with_opcode(enum nrfe_mspi_opcode opcode, const void *data, size_t len) +{ + uint8_t buffer[len + 1]; + + buffer[0] = (uint8_t)opcode; + memcpy(&buffer[1], data, len); + + return mspi_ipc_data_send(opcode, buffer, sizeof(buffer)); +} + +/** + * @brief Send a configuration struct to the FLPR core using the IPC service. + * + * @param opcode The configuration packet opcode to send. + * @param config The data to send. + * @param len The length of the data to send. + * + * @return 0 on success, negative errno code on failure. + */ +static int send_config(enum nrfe_mspi_opcode opcode, const void *config, size_t len) +{ + int rc; + + rc = send_with_opcode(opcode, config, len); + if (rc < 0) { + LOG_ERR("Configuration send failed: %d", rc); + return rc; + } + + rc = nrfe_mspi_wait_for_response(opcode, CONFIG_TIMEOUT_MS); + if (rc < 0) { + LOG_ERR("FLPR config: %d response timeout: %d!", opcode, rc); + } + + return rc; +} + +/** + * @brief Configures the MSPI controller based on the provided spec. + * + * This function configures the MSPI controller according to the provided + * spec. It checks if the spec is valid and sends the configuration to + * the FLPR. + * + * @param spec The MSPI spec to use for configuration. + * + * @return 0 on success, negative errno code on failure. + */ +static int api_config(const struct mspi_dt_spec *spec) +{ + int ret; + const struct mspi_cfg *config = &spec->config; + const struct mspi_nrfe_config *drv_cfg = spec->bus->config; + + if (config->op_mode != MSPI_OP_MODE_CONTROLLER) { + LOG_ERR("Only MSPI controller mode is supported."); + return -ENOTSUP; + } + + if (config->dqs_support) { + LOG_ERR("DQS mode is not supported."); + return -ENOTSUP; + } + + if (config->max_freq > drv_cfg->mspicfg.max_freq) { + LOG_ERR("max_freq is too large."); + return -ENOTSUP; + } + + /* Create pinout configuration */ + uint8_t state_id; + nrfe_mspi_pinctrl_soc_pin_t pins_cfg; + + for (state_id = 0; state_id < drv_cfg->pcfg->state_cnt; state_id++) { + if (drv_cfg->pcfg->states[state_id].id == PINCTRL_STATE_DEFAULT) { + break; + } + } + + if (drv_cfg->pcfg->states[state_id].pin_cnt > NRFE_MSPI_PINS_MAX) { + LOG_ERR("Too many pins defined. Max: %d", NRFE_MSPI_PINS_MAX); + return -ENOTSUP; + } + + if (drv_cfg->pcfg->states[state_id].id != PINCTRL_STATE_DEFAULT) { + LOG_ERR("Pins default state not found."); + return -ENOTSUP; + } + + for (uint8_t i = 0; i < drv_cfg->pcfg->states[state_id].pin_cnt; i++) { + pins_cfg.pin[i] = drv_cfg->pcfg->states[state_id].pins[i]; + } + + /* Send pinout configuration to FLPR */ + ret = send_config(NRFE_MSPI_CONFIG_PINS, (const void *)pins_cfg.pin, + sizeof(pins_cfg)); + if (ret < 0) { + return ret; + } + + /* Send controller configuration to FLPR */ + return send_config(NRFE_MSPI_CONFIG_CTRL, (const void *)config, + sizeof(struct mspi_cfg)); +} + +static int check_io_mode(enum mspi_io_mode io_mode) +{ + switch (io_mode) { + case MSPI_IO_MODE_SINGLE: + case MSPI_IO_MODE_QUAD: + case MSPI_IO_MODE_QUAD_1_1_4: + case MSPI_IO_MODE_QUAD_1_4_4: + break; + default: + LOG_ERR("IO mode %d not supported", io_mode); + return -ENOTSUP; + } + + return 0; +} + +/** + * @brief Configure a device on the MSPI bus. + * + * @param dev MSPI controller device. + * @param dev_id Device ID to configure. + * @param param_mask Bitmask of parameters to configure. + * @param cfg Device configuration. + * + * @return 0 on success, negative errno code on failure. + */ +static int api_dev_config(const struct device *dev, const struct mspi_dev_id *dev_id, + const enum mspi_dev_cfg_mask param_mask, const struct mspi_dev_cfg *cfg) +{ + const struct mspi_nrfe_config *drv_cfg = dev->config; + struct mspi_nrfe_data *drv_data = dev->data; + int rc; + + if (param_mask & MSPI_DEVICE_CONFIG_MEM_BOUND) { + if (cfg->mem_boundary) { + LOG_ERR("Memory boundary is not supported."); + return -ENOTSUP; + } + } + + if (param_mask & MSPI_DEVICE_CONFIG_BREAK_TIME) { + if (cfg->time_to_break) { + LOG_ERR("Transfer break is not supported."); + return -ENOTSUP; + } + } + + if (param_mask & MSPI_DEVICE_CONFIG_FREQUENCY) { + if (cfg->freq > drv_cfg->mspicfg.max_freq) { + LOG_ERR("Invalid frequency: %u, MAX: %u", cfg->freq, + drv_cfg->mspicfg.max_freq); + return -EINVAL; + } + } + + if (param_mask & MSPI_DEVICE_CONFIG_IO_MODE) { + rc = check_io_mode(cfg->io_mode); + if (rc < 0) { + return rc; + } + } + + if (param_mask & MSPI_DEVICE_CONFIG_DATA_RATE) { + if (cfg->data_rate != MSPI_DATA_RATE_SINGLE) { + LOG_ERR("Only single data rate is supported."); + return -ENOTSUP; + } + } + + if (param_mask & MSPI_DEVICE_CONFIG_DQS) { + if (cfg->dqs_enable) { + LOG_ERR("DQS signal is not supported."); + return -ENOTSUP; + } + } + + memcpy((void *)&drv_data->dev_cfg, (void *)cfg, sizeof(drv_data->dev_cfg)); + drv_data->dev_id = *dev_id; + + return send_config(NRFE_MSPI_CONFIG_DEV, (void *)cfg, sizeof(struct mspi_dev_cfg)); +} + +static int api_get_channel_status(const struct device *dev, uint8_t ch) +{ + return 0; +} + +/** + * @brief Send a transfer packet to the eMSPI controller. + * + * @param dev eMSPI controller device + * @param packet Transfer packet containing the data to be transferred + * @param timeout Timeout in milliseconds + * + * @retval 0 on success + * @retval -ENOTSUP if the packet is not supported + * @retval -ENOMEM if there is no space in the buffer + * @retval -ETIMEDOUT if the transfer timed out + */ +static int xfer_packet(struct mspi_xfer_packet *packet, uint32_t timeout) +{ + int rc; + uint32_t struct_size = sizeof(struct mspi_xfer_packet); + uint32_t len = struct_size + packet->num_bytes + 1; + uint8_t buffer[len]; + enum nrfe_mspi_opcode opcode = (packet->dir == MSPI_RX) ? NRFE_MSPI_TXRX : NRFE_MSPI_TX; + + buffer[0] = (uint8_t)opcode; + memcpy((void *)&buffer[1], (void *)packet, struct_size); + memcpy((void *)(&buffer[1] + struct_size), (void *)packet->data_buf, packet->num_bytes); + + rc = mspi_ipc_data_send(opcode, buffer, len); + if (rc < 0) { + LOG_ERR("Packet transfer error: %d", rc); + } + + rc = nrfe_mspi_wait_for_response(opcode, timeout); + if (rc < 0) { + LOG_ERR("FLPR Xfer response timeout: %d", rc); + return rc; + } + + /* Wait for the transfer to complete and receive data. */ + if ((packet->dir == MSPI_RX) && (ipc_receive_buffer != NULL) && (ipc_received > 0)) { + memcpy((void *)packet->data_buf, (void *)ipc_receive_buffer, ipc_received); + packet->num_bytes = ipc_received; + + /* Clear the receive buffer pointer and size */ + ipc_receive_buffer = NULL; + ipc_received = 0; + } + + return rc; +} + +/** + * @brief Initiates the transfer of the next packet in an MSPI transaction. + * + * This function prepares and starts the transmission of the next packet + * specified in the MSPI transfer configuration. It checks if the packet + * size is within the allowable limits before initiating the transfer. + * + * @param xfer Pointer to the mspi_xfer structure. + * @param packets_done Number of packets that have already been processed. + * + * @retval 0 If the packet transfer is successfully started. + * @retval -EINVAL If the packet size exceeds the maximum transmission size. + */ +static int start_next_packet(struct mspi_xfer *xfer, uint32_t packets_done) +{ + struct mspi_xfer_packet *packet = (struct mspi_xfer_packet *)&xfer->packets[packets_done]; + + if (packet->num_bytes >= MAX_TX_MSG_SIZE) { + LOG_ERR("Packet size to large: %u. Increase SRAM data region.", packet->num_bytes); + return -EINVAL; + } + + return xfer_packet(packet, xfer->timeout); +} + +/** + * @brief Send a multi-packet transfer request to the host. + * + * This function sends a multi-packet transfer request to the host and waits + * for the host to complete the transfer. This function does not support + * asynchronous transfers. + * + * @param dev Pointer to the device structure. + * @param dev_id Pointer to the device identification structure. + * @param req Pointer to the xfer structure. + * + * @retval 0 If successful. + * @retval -ENOTSUP If asynchronous transfers are requested. + * @retval -EIO If an I/O error occurs. + */ +static int api_transceive(const struct device *dev, const struct mspi_dev_id *dev_id, + const struct mspi_xfer *req) +{ + (void)dev_id; + struct mspi_nrfe_data *drv_data = dev->data; + uint32_t packets_done = 0; + int rc; + + /* TODO: add support for asynchronous transfers */ + if (req->async) { + return -ENOTSUP; + } + + if (req->num_packet == 0 || !req->packets || + req->timeout > CONFIG_MSPI_COMPLETION_TIMEOUT_TOLERANCE) { + return -EFAULT; + } + + drv_data->xfer = *req; + + rc = send_config(NRFE_MSPI_CONFIG_XFER, (void *)&drv_data->xfer, sizeof(struct mspi_xfer)); + if (rc < 0) { + LOG_ERR("Send xfer config error: %d", rc); + return rc; + } + + while (packets_done < drv_data->xfer.num_packet) { + rc = start_next_packet(&drv_data->xfer, packets_done); + if (rc < 0) { + LOG_ERR("Start next packet error: %d", rc); + return rc; + } + ++packets_done; + } + + return 0; +} + +#if CONFIG_PM_DEVICE +/** + * @brief Callback function to handle power management actions. + * + * This function is responsible for handling power management actions + * such as suspend and resume for the given device. It performs the + * necessary operations when the device is requested to transition + * between different power states. + * + * @param dev Pointer to the device structure. + * @param action The power management action to be performed. + * + * @retval 0 If successful. + * @retval -ENOTSUP If the action is not supported. + */ +static int dev_pm_action_cb(const struct device *dev, enum pm_device_action action) +{ + switch (action) { + case PM_DEVICE_ACTION_SUSPEND: + /* TODO: Handle PM suspend state */ + break; + case PM_DEVICE_ACTION_RESUME: + /* TODO: Handle PM resume state */ + break; + default: + return -ENOTSUP; + } + + return 0; +} +#endif + +/** + * @brief Initialize the MSPI NRFE driver. + * + * This function initializes the MSPI NRFE driver. It is responsible for + * setting up the hardware and registering the IPC endpoint for the + * driver. + * + * @param dev Pointer to the device structure for the MSPI NRFE driver. + * + * @retval 0 If successful. + * @retval -errno If an error occurs. + */ +static int nrfe_mspi_init(const struct device *dev) +{ + int ret; + const struct device *ipc_instance = DEVICE_DT_GET(DT_NODELABEL(ipc0)); + const struct mspi_nrfe_config *drv_cfg = dev->config; + const struct mspi_dt_spec spec = { + .bus = dev, + .config = drv_cfg->mspicfg, + }; + + ret = pinctrl_apply_state(drv_cfg->pcfg, PINCTRL_STATE_DEFAULT); + if (ret) { + return ret; + } + + ret = ipc_service_open_instance(ipc_instance); + if ((ret < 0) && (ret != -EALREADY)) { + LOG_ERR("ipc_service_open_instance() failure"); + return ret; + } + + ret = ipc_service_register_endpoint(ipc_instance, &ep, &ep_cfg); + if (ret < 0) { + LOG_ERR("ipc_service_register_endpoint() failure"); + return ret; + } + + /* Wait for ep to be bounded */ +#if defined(CONFIG_MULTITHREADING) + k_sem_take(&ipc_sem, K_FOREVER); +#else + while (!atomic_test_and_clear_bit(&ipc_atomic_sem, NRFE_MSPI_EP_BOUNDED)) { + } +#endif + + ret = api_config(&spec); + if (ret < 0) { + return ret; + } + +#if CONFIG_PM_DEVICE + ret = pm_device_driver_init(dev, dev_pm_action_cb); + if (ret < 0) { + return ret; + } +#endif + return ret; +} + +static const struct mspi_driver_api drv_api = { + .config = api_config, + .dev_config = api_dev_config, + .get_channel_status = api_get_channel_status, + .transceive = api_transceive, +}; + +PM_DEVICE_DT_INST_DEFINE(0, dev_pm_action_cb); + +DEVICE_DT_INST_DEFINE(0, nrfe_mspi_init, PM_DEVICE_DT_INST_GET(0), &dev_data, &dev_config, + POST_KERNEL, CONFIG_MSPI_NRFE_INIT_PRIORITY, &drv_api); diff --git a/dts/bindings/mspi/nordic,nrfe-mspi-controller.yaml b/dts/bindings/mspi/nordic,nrfe-mspi-controller.yaml new file mode 100644 index 000000000000..999d10286b78 --- /dev/null +++ b/dts/bindings/mspi/nordic,nrfe-mspi-controller.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + +description: Nordic SDP MSPI controller + +compatible: "nordic,nrfe-mspi-controller" + +include: [mspi-controller.yaml, pinctrl-device.yaml] diff --git a/include/drivers/mspi/nrfe_mspi.h b/include/drivers/mspi/nrfe_mspi.h new file mode 100644 index 000000000000..467b6806739c --- /dev/null +++ b/include/drivers/mspi/nrfe_mspi.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef DRIVERS_MSPI_NRFE_MSPI_H +#define DRIVERS_MSPI_NRFE_MSPI_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef CONFIG_SOC_NRF54L15 +#define NRFE_MSPI_PORT_NUMBER 2 /* Physical port number */ +#define NRFE_MSPI_SCK_PIN_NUMBER 1 /* Physical pins number on port 2 */ +#define NRFE_MSPI_DQ0_PIN_NUMBER 2 +#define NRFE_MSPI_DQ1_PIN_NUMBER 4 +#define NRFE_MSPI_DQ2_PIN_NUMBER 3 +#define NRFE_MSPI_DQ3_PIN_NUMBER 0 +#define NRFE_MSPI_CS0_PIN_NUMBER 5 +#define NRFE_MSPI_PINS_MAX 6 + +#define NRFE_MSPI_SCK_PIN_NUMBER_VIO 0 /* FLPR VIO SCLK pin number */ +#define NRFE_MSPI_DQ0_PIN_NUMBER_VIO 1 +#define NRFE_MSPI_DQ1_PIN_NUMBER_VIO 2 +#define NRFE_MSPI_DQ2_PIN_NUMBER_VIO 3 +#define NRFE_MSPI_DQ3_PIN_NUMBER_VIO 4 +#define NRFE_MSPI_CS0_PIN_NUMBER_VIO 5 + +#define VIO(_pin_) _pin_##_VIO +#else +#error "Unsupported SoC for SDP MSPI" +#endif + +#define NRFE_MSPI_MAX_CE_PINS_COUNT 5 /* Ex. CE0 CE1 CE2 CE3 CE4 */ + +/** @brief eMSPI opcodes. */ +enum nrfe_mspi_opcode { + NRFE_MSPI_EP_BOUNDED = 0, + NRFE_MSPI_CONFIG_PINS, + NRFE_MSPI_CONFIG_CTRL, /* struct mspi_cfg */ + NRFE_MSPI_CONFIG_DEV, /* struct mspi_dev_cfg */ + NRFE_MSPI_CONFIG_XFER, /* struct mspi_xfer */ + NRFE_MSPI_TX, + NRFE_MSPI_TXRX, + NRFE_MSPI_WRONG_OPCODE, + NRFE_MSPI_ALL_OPCODES = NRFE_MSPI_WRONG_OPCODE, +}; + +typedef struct __packed { + uint8_t opcode; /* nrfe_mspi_opcode */ + pinctrl_soc_pin_t pin[NRFE_MSPI_PINS_MAX]; +} nrfe_mspi_pinctrl_soc_pin_t; + +typedef struct __packed { + uint8_t opcode; /* nrfe_mspi_opcode */ + uint8_t data; +} nrfe_mspi_flpr_response_t; + +#ifdef __cplusplus +} +#endif + +#endif /* DRIVERS_MSPI_NRFE_MSPI_H */ diff --git a/scripts/twister/alt/zephyr/tests/drivers/mspi/api/testcase.yaml b/scripts/twister/alt/zephyr/tests/drivers/mspi/api/testcase.yaml new file mode 100644 index 000000000000..8248dc26ec00 --- /dev/null +++ b/scripts/twister/alt/zephyr/tests/drivers/mspi/api/testcase.yaml @@ -0,0 +1,29 @@ +tests: + drivers.mspi.api: + tags: + - drivers + - mspi + - api + filter: dt_compat_enabled("zephyr,mspi-emul-controller") or + dt_compat_enabled("ambiq,mspi-controller") + harness: ztest + platform_allow: + - native_sim + - apollo3p_evb + integration_platforms: + - native_sim + drivers.mspi.sdp: + tags: + - drivers + - mspi + - api + harness: ztest + platform_allow: + - nrf54l15dk/nrf54l15/cpuapp + integration_platforms: + - nrf54l15dk/nrf54l15/cpuapp + extra_args: + - SB_CONFIG_VPR_LAUNCHER=n + - SB_CONFIG_PARTITION_MANAGER=n + - SB_CONFIG_SDP=y + - SB_CONFIG_SDP_MSPI=y diff --git a/snippets/sdp/mspi/app.conf b/snippets/sdp/mspi/app.conf new file mode 100644 index 000000000000..0bf532300b85 --- /dev/null +++ b/snippets/sdp/mspi/app.conf @@ -0,0 +1,4 @@ +CONFIG_MSPI=y +CONFIG_MBOX=y +CONFIG_IPC_SERVICE=y +CONFIG_IPC_SERVICE_BACKEND_ICMSG=y diff --git a/snippets/sdp/mspi/sdp-mspi-app.overlay b/snippets/sdp/mspi/sdp-mspi-app.overlay new file mode 100644 index 000000000000..e210e5b108ea --- /dev/null +++ b/snippets/sdp/mspi/sdp-mspi-app.overlay @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +&cpuflpr_vpr { + status = "okay"; + + sdp_mspi: sdp_mspi { + compatible = "nordic,nrfe-mspi-controller"; + #address-cells = <1>; + #size-cells = <0>; + }; +}; diff --git a/snippets/sdp/mspi/snippet.yml b/snippets/sdp/mspi/snippet.yml new file mode 100644 index 000000000000..5c82c4655978 --- /dev/null +++ b/snippets/sdp/mspi/snippet.yml @@ -0,0 +1,16 @@ +# +# Copyright (c) 2024 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +name: sdp-mspi + +boards: + /.*/cpuapp/: + append: + EXTRA_DTC_OVERLAY_FILE: sdp-mspi-app.overlay + EXTRA_CONF_FILE: app.conf + /.*/nrf54l15/cpuapp/: + append: + EXTRA_DTC_OVERLAY_FILE: soc/nrf54l15_cpuapp.overlay diff --git a/snippets/sdp/mspi/soc/nrf54l15_cpuapp.overlay b/snippets/sdp/mspi/soc/nrf54l15_cpuapp.overlay new file mode 100644 index 000000000000..07f040a1fd9a --- /dev/null +++ b/snippets/sdp/mspi/soc/nrf54l15_cpuapp.overlay @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/ { + soc { + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + + cpuflpr_code_partition: image@17a000 { + reg = <0x17a000 DT_SIZE_K(12)>; + }; + + sram_rx: memory@2003c000 { + reg = <0x2003c000 0x0800>; + }; + + sram_tx: memory@2003c800 { + reg = <0x2003c800 0x0800>; + }; + }; + + + cpuflpr_sram_code_data: memory@2003d000 { + compatible = "mmio-sram"; + reg = <0x2003d000 DT_SIZE_K(12)>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x2003d000 0x3000>; + }; + }; + + ipc { + ipc0: ipc0 { + compatible = "zephyr,ipc-icmsg"; + tx-region = <&sram_tx>; + rx-region = <&sram_rx>; + mboxes = <&cpuapp_vevif_rx 20>, <&cpuapp_vevif_tx 16>; + mbox-names = "rx", "tx"; + status = "okay"; + }; + }; +}; + +&cpuapp_rram { + reg = <0x0 DT_SIZE_K(1512)>; +}; + +&cpuapp_sram { + reg = <0x20000000 DT_SIZE_K(244)>; + ranges = <0x0 0x20000000 0x3d000>; +}; + +&cpuflpr_vpr { + execution-memory = <&cpuflpr_sram_code_data>; + source-memory = <&cpuflpr_code_partition>; +}; + +&gpio2 { + status = "okay"; +}; + +&cpuapp_vevif_rx { + status = "okay"; +}; + +&cpuapp_vevif_tx { + status = "okay"; +}; + +&pinctrl { + /omit-if-no-ref/ sdp_mspi_default: sdp_mspi_default { + group1 { + psels = , + , + , + , + , + ; + nordic,drive-mode = ; + }; + }; + /omit-if-no-ref/ sdp_mspi_sleep: sdp_mspi_sleep { + group1 { + psels = , + , + , + , + , + ; + low-power-enable; + }; + }; +}; + +&sdp_mspi { + clock-frequency = ; + pinctrl-0 = <&sdp_mspi_default>; + pinctrl-1 = <&sdp_mspi_sleep>; + pinctrl-names = "default", "sleep"; + status = "okay"; +}; diff --git a/sysbuild/Kconfig.sdp b/sysbuild/Kconfig.sdp index 7529d937e07f..a84fbdc745f4 100644 --- a/sysbuild/Kconfig.sdp +++ b/sysbuild/Kconfig.sdp @@ -12,6 +12,9 @@ if SDP config SDP_GPIO bool "SDP GPIO application" +config SDP_MSPI + bool "SDP MSPI application" + if SDP_GPIO choice SDP_GPIO_BACKEND diff --git a/sysbuild/sdp.cmake b/sysbuild/sdp.cmake index 2167bfac5655..34d779ac1782 100644 --- a/sysbuild/sdp.cmake +++ b/sysbuild/sdp.cmake @@ -2,18 +2,29 @@ # # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause -# If it is enabled, include the SDP GPIO application in the build -if(SB_CONFIG_SDP_GPIO) +if(SB_CONFIG_SDP) # Extract SoC name from related variables string(REPLACE "/" ";" split_board_qualifiers "${BOARD_QUALIFIERS}") list(GET split_board_qualifiers 1 target_soc) set(board_target_flpr "${BOARD}/${target_soc}/cpuflpr") set(target_soc) + # Select the SDP application + if(SB_CONFIG_SDP_GPIO) + set(sdp_app_dir "${ZEPHYR_NRF_MODULE_DIR}/applications/sdp/gpio") + elseif(SB_CONFIG_SDP_MSPI) + set(sdp_app_dir "${ZEPHYR_NRF_MODULE_DIR}/applications/sdp/mspi") + else() + message(FATAL_ERROR "Unknown SDP application type") + endif() + + # Include the SDP application in the build ExternalZephyrProject_Add( APPLICATION sdp - SOURCE_DIR ${ZEPHYR_NRF_MODULE_DIR}/applications/sdp/gpio + SOURCE_DIR ${sdp_app_dir} BOARD ${board_target_flpr} BOARD_REVISION ${BOARD_REVISION} ) + set(sdp_app_dir) + set(board_target_flpr) endif()