From bc19a69a0a5ca36a65b2a513dbdbd3ddf6ef5846 Mon Sep 17 00:00:00 2001 From: Jakub Zymelka Date: Mon, 18 Nov 2024 10:33:20 +0100 Subject: [PATCH] drivers: mspi: Add SDP MSPI driver Add SDP MSPI driver, dts and include files. Signed-off-by: Jakub Zymelka --- CODEOWNERS | 2 + drivers/CMakeLists.txt | 1 + drivers/Kconfig | 2 + drivers/mspi/CMakeLists.txt | 8 + drivers/mspi/Kconfig | 8 + drivers/mspi/Kconfig.nrfe | 28 + drivers/mspi/mspi_nrfe.c | 651 ++++++++++++++++++ .../mspi/nordic,nrfe-mspi-controller.yaml | 12 + include/drivers/mspi/nrfe_mspi.h | 37 + 9 files changed, 749 insertions(+) create mode 100644 drivers/mspi/CMakeLists.txt create mode 100644 drivers/mspi/Kconfig create mode 100644 drivers/mspi/Kconfig.nrfe create mode 100644 drivers/mspi/mspi_nrfe.c create mode 100644 dts/bindings/mspi/nordic,nrfe-mspi-controller.yaml create mode 100644 include/drivers/mspi/nrfe_mspi.h diff --git a/CODEOWNERS b/CODEOWNERS index 35a62f07f935..a2b9c4858f75 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -283,6 +283,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 @@ -317,6 +318,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 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..e13bfdb9d3d7 --- /dev/null +++ b/drivers/mspi/mspi_nrfe.c @@ -0,0 +1,651 @@ +/* + * Copyright (c) 2024, Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#define DT_DRV_COMPAT nordic_nrfe_mspi_controller + +#include +#include +#include +#if CONFIG_PM_DEVICE +#include +#endif +#include + +#include +LOG_MODULE_REGISTER(mspi_nrfe, LOG_LEVEL_DBG); // CONFIG_MSPI_LOG_LEVEL); + +#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 + +#if CONFIG_PINCTRL +#include +PINCTRL_DT_INST_DEFINE(0); +#endif + +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(conf_sem, 0, 1); +static K_SEM_DEFINE(xfer_sem, 0, 1); +static K_SEM_DEFINE(recv_sem, 0, 1); +#else +static volatile uint32_t ipc_sem = 0; +static volatile uint32_t conf_sem = 0; +static volatile uint32_t xfer_sem = 0; +static volatile uint32_t recv_sem = 0; +#endif + +static struct gpio_dt_spec ce_gpios[] = MSPI_CE_GPIOS_DT_SPEC_GET(MSPI_NRFE_NODE); + +#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), \ + .ce_group = (struct gpio_dt_spec *)ce_gpios, \ + .num_ce_gpios = ARRAY_SIZE(ce_gpios), \ + .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; +#if CONFIG_PINCTRL + const struct pinctrl_dev_config *pcfg; +#endif +}; + +static const struct mspi_nrfe_config dev_config = { + .mspicfg = MSPI_CONFIG, +#if CONFIG_PINCTRL + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(0), +#endif +}; + +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 + ipc_sem = 1; +#endif + LOG_DBG("Eb 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 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) +{ + int rc; +#if defined(CONFIG_SYS_CLOCK_EXISTS) + uint32_t start = k_uptime_get_32(); +#endif + uint8_t buffer[len + 1]; + buffer[0] = (uint8_t)opcode; + + memcpy(&buffer[1], data, len); + + LOG_DBG("Sending msg with opcode: %d", (uint8_t)opcode); + + do { + rc = ipc_service_send(&ep, buffer, len + 1); +#if defined(CONFIG_SYS_CLOCK_EXISTS) + if ((k_uptime_get_32() - start) > EP_SEND_TIMEOUT_MS) { +#else + if (rc < 0) { +#endif + return rc; + }; + } while (rc == -ENOMEM); /* No space in the buffer. Retry. */ + + return rc; +} + +/** + * @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 failed: %d", rc); + return rc; + } + +#if defined(CONFIG_MULTITHREADING) + rc = k_sem_take(&conf_sem, K_MSEC(CONFIG_TIMEOUT_MS)); + if (rc < 0) { + rc = -ETIMEDOUT; + LOG_ERR("Configuration timed out: %d", rc); + } +#else +#if defined(CONFIG_SYS_CLOCK_EXISTS) + uint32_t start = k_uptime_get_32(); +#endif + while (!conf_sem) { +#if defined(CONFIG_SYS_CLOCK_EXISTS) + if ((k_uptime_get_32() - start) > CONFIG_TIMEOUT_MS) { + LOG_ERR("Xfer timed out: %d", rc); + break; + }; +#endif + } + conf_sem = 0; +#endif + + return rc; +} + +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_CTRL: + case NRFE_MSPI_CONFIG_DEV: + case NRFE_MSPI_CONFIG_XFER: { +#ifdef CONFIG_MULTITHREADING + k_sem_give(&conf_sem); +#else + conf_sem = 1; +#endif + break; + } + case NRFE_MSPI_TX: { +#ifdef CONFIG_MULTITHREADING + k_sem_give(&xfer_sem); +#else + xfer_sem = 1; +#endif + break; + } + case NRFE_MSPI_RX: { + if (len) { + ipc_received = len - 1; + ipc_receive_buffer = (uint8_t *)&response->data; + } +#ifdef CONFIG_MULTITHREADING + k_sem_give(&recv_sem); +#else + recv_sem = 1; +#endif + break; + } + default: { + LOG_ERR("Invalid reponse opcode: %d", response->opcode); + break; + } + } + + LOG_DBG("Received msg with opcode: %d", response->opcode); +} + +/** + * @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) +{ + const struct mspi_cfg *config = &spec->config; + const struct mspi_nrfe_config *drv_cfg = spec->bus->config; + const struct gpio_dt_spec *ce_gpio; + + if (config->op_mode != MSPI_OP_MODE_CONTROLLER) { + LOG_ERR("%u, only support MSPI controller mode.", __LINE__); + return -ENOTSUP; + } + + if (config->dqs_support) { + LOG_ERR("%u, only support non-DQS mode.", __LINE__); + return -ENOTSUP; + } + + if (config->max_freq > drv_cfg->mspicfg.max_freq) { + LOG_ERR("%u, max_freq too large.", __LINE__); + return -ENOTSUP; + } + + for (ce_gpio = config->ce_group; ce_gpio < &config->ce_group[config->num_ce_gpios]; + ce_gpio++) { + if (ce_gpio->pin < NRFE_MSPI_MIN_CE_PIN_NUMBER) { + LOG_ERR("%u, wrong GPIO pin %d number! Needs to be >= %d", __LINE__, + ce_gpio->pin, NRFE_MSPI_MIN_CE_PIN_NUMBER); + return -ENODEV; + } + } + + if (config->re_init) { + /* Send controller configuration to FLPR */ + return send_config(NRFE_MSPI_CONFIG_CTRL, (const void *)config, + sizeof(struct mspi_cfg)); + } + + return 0; +} + +static int check_io_mode(enum mspi_io_mode io_mode) +{ + switch (io_mode) { + case MSPI_IO_MODE_SINGLE: + case MSPI_IO_MODE_DUAL: + case MSPI_IO_MODE_DUAL_1_1_2: + case MSPI_IO_MODE_DUAL_1_2_2: + case MSPI_IO_MODE_QUAD: + case MSPI_IO_MODE_QUAD_1_1_4: + case MSPI_IO_MODE_QUAD_1_4_4: +#if !defined(CONFIG_SOC_NRF54L15) + case MSPI_IO_MODE_OCTAL: + case MSPI_IO_MODE_OCTAL_1_1_8: + case MSPI_IO_MODE_OCTAL_1_8_8: + case MSPI_IO_MODE_HEX: + case MSPI_IO_MODE_HEX_8_8_16: + case MSPI_IO_MODE_HEX_8_16_16: +#endif + 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("Auto CE break is not supported."); + return -ENOTSUP; + } + } + + if (param_mask & MSPI_DEVICE_CONFIG_BREAK_TIME) { + if (cfg->time_to_break) { + LOG_ERR("Auto CE 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]; + + buffer[0] = (uint8_t)NRFE_MSPI_XFER; + memcpy((void *)&buffer[1], (void *)packet, struct_size); + memcpy((void *)(&buffer[1] + struct_size), (void *)packet->data_buf, packet->num_bytes); + +#if defined(CONFIG_SYS_CLOCK_EXISTS) + uint32_t start = k_uptime_get_32(); +#endif + + do { + rc = ipc_service_send(&ep, buffer, len + 1); +#if defined(CONFIG_SYS_CLOCK_EXISTS) + if ((k_uptime_get_32() - start) > EP_SEND_TIMEOUT_MS) { +#else + if (rc < 0) { +#endif + break; + }; + } while (rc == -ENOMEM); /* No space in the buffer. Retry. */ + + if (rc < 0) { + LOG_ERR("Xfer failed: %d", rc); + return rc; + } + + /* Wait for the transfer to complete and receive data. */ + if (packet->dir == MSPI_RX) { +#if defined(CONFIG_MULTITHREADING) + rc = k_sem_take(&recv_sem, K_MSEC(timeout)); + if (rc < 0) { + rc = -ETIMEDOUT; + LOG_ERR("Xfer timed out: %d", rc); + } +#else +#if defined(CONFIG_SYS_CLOCK_EXISTS) + start = k_uptime_get_32(); +#endif + while (!recv_sem) { +#if defined(CONFIG_SYS_CLOCK_EXISTS) + if ((k_uptime_get_32() - start) > timeout) { + LOG_ERR("Xfer timed out: %d", rc); + break; + }; +#endif + } + recv_sem = 0; +#endif + if ((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 mspi_nrfe_config *drv_cfg = dev->config; + const struct mspi_dt_spec spec = { + .bus = dev, + .config = drv_cfg->mspicfg, + }; + +#if CONFIG_PINCTRL + ret = pinctrl_apply_state(drv_cfg->pcfg, PINCTRL_STATE_DEFAULT); + if (ret) { + return ret; + } +#endif + + const struct device *ipc0_instance = DEVICE_DT_GET(DT_NODELABEL(ipc0)); + ret = ipc_service_open_instance(ipc0_instance); + + if ((ret < 0) && (ret != -EALREADY)) { + LOG_ERR("ipc_service_open_instance() failure"); + return ret; + } + + ret = ipc_service_register_endpoint(ipc0_instance, &ep, &ep_cfg); + if (ret < 0) { + LOG_ERR("ipc_service_register_endpoint() failure"); + return ret; + } + +#if defined(CONFIG_MULTITHREADING) + k_sem_take(&ipc_sem, K_FOREVER); +#else + while (ipc_sem == 0) { + } + ipc_sem = 0; +#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, +}; + +#if CONFIG_PM_DEVICE +PM_DEVICE_DT_INST_DEFINE(0, dev_pm_action_cb); +#endif + +DEVICE_DT_INST_DEFINE(0, nrfe_mspi_init, NULL /*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..deb28cc662d8 --- /dev/null +++ b/dts/bindings/mspi/nordic,nrfe-mspi-controller.yaml @@ -0,0 +1,12 @@ +# 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] + +properties: + ce-gpios: + required: true diff --git a/include/drivers/mspi/nrfe_mspi.h b/include/drivers/mspi/nrfe_mspi.h new file mode 100644 index 000000000000..8dafa3b92a2a --- /dev/null +++ b/include/drivers/mspi/nrfe_mspi.h @@ -0,0 +1,37 @@ +/* + * 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 + +#ifdef __cplusplus +extern "C" { +#endif + +#define NRFE_MSPI_MIN_CE_PIN_NUMBER 5 /* Physical pin number on port */ +#define NRFE_MSPI_MAX_CE_PINS_COUNT 5 /* Ex. CE0 CE1 CE2 CE3 CE4 */ + +/** @brief eMSPI opcodes. */ +enum nrfe_mspi_opcode { + NRFE_MSPI_CONFIG_CTRL = 0, /* struct mspi_cfg */ + NRFE_MSPI_CONFIG_DEV, /* struct mspi_dev_cfg */ + NRFE_MSPI_CONFIG_XFER, /* struct mspi_xfer */ + NRFE_MSPI_XFER, + NRFE_MSPI_TX, + NRFE_MSPI_RX, + NRFE_MSPI_WRONG_OPCODE, +}; + +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 */