diff --git a/drivers/mspi/Kconfig.dw b/drivers/mspi/Kconfig.dw index 1ab82da0e85..06d2e09c8af 100644 --- a/drivers/mspi/Kconfig.dw +++ b/drivers/mspi/Kconfig.dw @@ -1,4 +1,5 @@ # Copyright (c) 2024 Nordic Semiconductor ASA +# Copyright (c) 2025 Tenstorrent AI ULC # SPDX-License-Identifier: Apache-2.0 config MSPI_DW @@ -7,3 +8,24 @@ config MSPI_DW depends on DT_HAS_SNPS_DESIGNWARE_SSI_ENABLED select PINCTRL if $(dt_compat_any_has_prop,$(DT_COMPAT_SNPS_DESIGNWARE_SSI),pinctrl-0) imply MSPI_XIP + imply MSPI_TIMING + +if MSPI_DW + +config MSPI_DW_TXD_DIV + int "Designware SSI TX Drive edge divisor" + default 4 + help + Division factor to apply to calculated BAUDR value when writing it + to the TXD_DRIVE_EDGE register in DDR mode. Note that the maximum + value of this register is (BAUDR / 2) - 1. + +config MSPI_DW_TXD_MUL + int "Designware SSI TX Drive edge multiplier" + default 1 + help + Multiplication factor to apply to calculated BAUDR value when writing + it to the TXD_DRIVE_EDGE register in DDR mode. Note that the maximum + value of this register is (BAUDR / 2) - 1. + +endif # MSPI_DW diff --git a/drivers/mspi/mspi_dw.c b/drivers/mspi/mspi_dw.c index e57511b409a..66f257839aa 100644 --- a/drivers/mspi/mspi_dw.c +++ b/drivers/mspi/mspi_dw.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2024 Nordic Semiconductor ASA + * Copyright (c) 2025 Tenstorrent AI ULC * * SPDX-License-Identifier: Apache-2.0 */ @@ -8,12 +9,15 @@ #include #include +#if defined(CONFIG_PINCTRL) #include +#endif #include #include #include #include #include +#include #include "mspi_dw.h" @@ -45,6 +49,7 @@ struct mspi_dw_data { uint32_t ctrlr0; uint32_t spi_ctrlr0; uint32_t baudr; + uint32_t rx_sample_dly; #if defined(CONFIG_MSPI_XIP) uint32_t xip_freq; @@ -110,7 +115,9 @@ DEFINE_MM_REG_WR(imr, 0x2c) DEFINE_MM_REG_RD(isr, 0x30) DEFINE_MM_REG_RD(risr, 0x34) DEFINE_MM_REG_RD_WR(dr, 0x60) +DEFINE_MM_REG_WR(rx_sample_dly, 0xf0) DEFINE_MM_REG_WR(spi_ctrlr0, 0xf4) +DEFINE_MM_REG_WR(txd_drive_edge, 0xf8) #if defined(CONFIG_MSPI_XIP) DEFINE_MM_REG_WR(xip_incr_inst, 0x100) @@ -684,18 +691,30 @@ static int _api_dev_config(const struct device *dev, } if (param_mask & MSPI_DEVICE_CONFIG_DATA_RATE) { - /* TODO: add support for DDR */ - if (cfg->data_rate != MSPI_DATA_RATE_SINGLE) { - LOG_ERR("Only single data rate is supported."); + dev_data->spi_ctrlr0 &= ~(SPI_CTRLR0_SPI_DDR_EN_BIT | + SPI_CTRLR0_INST_DDR_EN_BIT); + switch (cfg->data_rate) { + case MSPI_DATA_RATE_SINGLE: + break; + case MSPI_DATA_RATE_DUAL: + dev_data->spi_ctrlr0 |= SPI_CTRLR0_INST_DDR_EN_BIT; + /* Also need to set DDR_EN bit */ + __fallthrough; + case MSPI_DATA_RATE_S_D_D: + dev_data->spi_ctrlr0 |= SPI_CTRLR0_SPI_DDR_EN_BIT; + break; + default: + LOG_ERR("Data rate %d not supported", + cfg->data_rate); return -ENOTSUP; } } if (param_mask & MSPI_DEVICE_CONFIG_DQS) { - /* TODO: add support for DQS */ + dev_data->spi_ctrlr0 &= ~SPI_CTRLR0_SPI_RXDS_EN_BIT; + if (cfg->dqs_enable) { - LOG_ERR("DQS line is not supported."); - return -ENOTSUP; + dev_data->spi_ctrlr0 |= SPI_CTRLR0_SPI_RXDS_EN_BIT; } } @@ -817,8 +836,9 @@ static int start_next_packet(const struct device *dev, k_timeout_t timeout) dev_data->dummy_bytes = 0; dev_data->bytes_to_discard = 0; - dev_data->ctrlr0 &= ~CTRLR0_TMOD_MASK - & ~CTRLR0_DFS_MASK; + dev_data->ctrlr0 &= ~(CTRLR0_TMOD_MASK) + & ~(CTRLR0_DFS_MASK) + & ~(CTRLR0_DFS32_MASK); dev_data->spi_ctrlr0 &= ~SPI_CTRLR0_WAIT_CYCLES_MASK; @@ -827,16 +847,20 @@ static int start_next_packet(const struct device *dev, k_timeout_t timeout) dev_data->xfer.addr_length != 0)) { dev_data->bytes_per_frame_exp = 0; dev_data->ctrlr0 |= FIELD_PREP(CTRLR0_DFS_MASK, 7); + dev_data->ctrlr0 |= FIELD_PREP(CTRLR0_DFS32_MASK, 7); } else { if ((packet->num_bytes % 4) == 0) { dev_data->bytes_per_frame_exp = 2; dev_data->ctrlr0 |= FIELD_PREP(CTRLR0_DFS_MASK, 31); + dev_data->ctrlr0 |= FIELD_PREP(CTRLR0_DFS32_MASK, 31); } else if ((packet->num_bytes % 2) == 0) { dev_data->bytes_per_frame_exp = 1; dev_data->ctrlr0 |= FIELD_PREP(CTRLR0_DFS_MASK, 15); + dev_data->ctrlr0 |= FIELD_PREP(CTRLR0_DFS32_MASK, 15); } else { dev_data->bytes_per_frame_exp = 0; dev_data->ctrlr0 |= FIELD_PREP(CTRLR0_DFS_MASK, 7); + dev_data->ctrlr0 |= FIELD_PREP(CTRLR0_DFS32_MASK, 7); } } @@ -928,7 +952,16 @@ static int start_next_packet(const struct device *dev, k_timeout_t timeout) : 0); write_spi_ctrlr0(dev, dev_data->spi_ctrlr0); write_baudr(dev, dev_data->baudr); - write_ser(dev, BIT(dev_data->dev_id->dev_idx)); + write_rx_sample_dly(dev, dev_data->rx_sample_dly); + if (dev_data->spi_ctrlr0 & (SPI_CTRLR0_SPI_DDR_EN_BIT | + SPI_CTRLR0_INST_DDR_EN_BIT)) { + int txd = (CONFIG_MSPI_DW_TXD_MUL * dev_data->baudr) / + CONFIG_MSPI_DW_TXD_DIV; + + write_txd_drive_edge(dev, txd); + } else { + write_txd_drive_edge(dev, 0); + } if (xip_enabled) { write_ssienr(dev, SSIENR_SSIC_EN_BIT); @@ -1015,8 +1048,17 @@ static int start_next_packet(const struct device *dev, k_timeout_t timeout) } } + /* Prefill TX FIFO with any data we can */ + if (dev_data->dummy_bytes && tx_dummy_bytes(dev)) { + imr = IMR_RXFIM_BIT; + } else if (packet->dir == MSPI_TX && packet->num_bytes) { + tx_data(dev, packet); + } + /* Enable interrupts now and wait until the packet is done. */ write_imr(dev, imr); + /* Write SER to start transfer */ + write_ser(dev, BIT(dev_data->dev_id->dev_idx)); rc = k_sem_take(&dev_data->finished, timeout); if (read_risr(dev) & RISR_RXOIR_BIT) { @@ -1046,6 +1088,8 @@ static int start_next_packet(const struct device *dev, k_timeout_t timeout) } else { write_ssienr(dev, 0); } + /* Clear SER */ + write_ser(dev, 0); if (dev_data->dev_id->ce.port) { int rc2; @@ -1148,6 +1192,22 @@ static int api_transceive(const struct device *dev, return rc; } +#if defined(CONFIG_MSPI_TIMING) +static int api_timing_config(const struct device *dev, + const struct mspi_dev_id *dev_id, + const uint32_t param_mask, void *cfg) +{ + struct mspi_dw_data *dev_data = dev->data; + struct mspi_dw_timing_cfg *config = cfg; + + if (param_mask & MSPI_DW_RX_TIMING_CFG) { + dev_data->rx_sample_dly = config->rx_sample_dly; + return 0; + } + return -ENOTSUP; +} +#endif /* defined(CONFIG_MSPI_TIMING) */ + #if defined(CONFIG_MSPI_XIP) static int _api_xip_config(const struct device *dev, const struct mspi_dev_id *dev_id, @@ -1381,6 +1441,9 @@ static int dev_init(const struct device *dev) } } + /* Make sure controller is disabled. */ + write_ssienr(dev, 0); + #if defined(CONFIG_PINCTRL) if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) { rc = pinctrl_apply_state(dev_config->pcfg, PINCTRL_STATE_SLEEP); @@ -1399,16 +1462,19 @@ static DEVICE_API(mspi, drv_api) = { .dev_config = api_dev_config, .get_channel_status = api_get_channel_status, .transceive = api_transceive, +#if defined(CONFIG_MSPI_TIMING) + .timing_config = api_timing_config, +#endif #if defined(CONFIG_MSPI_XIP) .xip_config = api_xip_config, #endif }; #define MSPI_DW_INST_IRQ(idx, inst) \ - IRQ_CONNECT(DT_INST_IRQ_BY_IDX(inst, idx, irq), \ + IRQ_CONNECT(DT_INST_IRQN_BY_IDX(inst, idx), \ DT_INST_IRQ_BY_IDX(inst, idx, priority), \ mspi_dw_isr, DEVICE_DT_INST_GET(inst), 0); \ - irq_enable(DT_INST_IRQ_BY_IDX(inst, idx, irq)) + irq_enable(DT_INST_IRQN_BY_IDX(inst, idx)) #define MSPI_DW_MMIO_ROM_INIT(node_id) \ COND_CODE_1(DT_REG_HAS_NAME(node_id, core), \ diff --git a/drivers/mspi/mspi_dw.h b/drivers/mspi/mspi_dw.h index c35778cb1c5..28e4bed016e 100644 --- a/drivers/mspi/mspi_dw.h +++ b/drivers/mspi/mspi_dw.h @@ -1,32 +1,42 @@ /* * Copyright (c) 2024 Nordic Semiconductor ASA + * Copyright (c) 2025 Tenstorrent AI ULC * * SPDX-License-Identifier: Apache-2.0 */ +#if DT_HAS_COMPAT_STATUS_OKAY(snps_designware_ssi_v2) +/* + * Later versions of the SSI have different register offsets. Define a macro + * to use these. + */ +#define SSI_VERSION_2 1 +#endif + /* * This header is part of mspi_dw.c extracted only for clarity. * It is not supposed to be included by any file other than mspi_dw.c. */ /* CTRLR0 - Control Register 0 */ -#define CTRLR0_SPI_FRF_MASK GENMASK(23, 22) +#define CTRLR0_SPI_FRF_MASK COND_CODE_1(SSI_VERSION_2, GENMASK(22, 21), GENMASK(23, 22)) #define CTRLR0_SPI_FRF_STANDARD 0UL #define CTRLR0_SPI_FRF_DUAL 1UL #define CTRLR0_SPI_FRF_QUAD 2UL #define CTRLR0_SPI_FRF_OCTAL 3UL -#define CTRLR0_TMOD_MASK GENMASK(11, 10) +#define CTRLR0_TMOD_MASK COND_CODE_1(SSI_VERSION_2, GENMASK(9, 8), GENMASK(11, 10)) #define CTRLR0_TMOD_TX_RX 0UL #define CTRLR0_TMOD_TX 1UL #define CTRLR0_TMOD_RX 2UL #define CTRLR0_TMOD_EEPROM 3UL -#define CTRLR0_SCPOL_BIT BIT(9) -#define CTRLR0_SCPH_BIT BIT(8) -#define CTRLR0_FRF_MASK GENMASK(7, 6) +#define CTRLR0_SCPOL_BIT COND_CODE_1(SSI_VERSION_2, BIT(7), BIT(9)) +#define CTRLR0_SCPH_BIT COND_CODE_1(SSI_VERSION_2, BIT(6), BIT(8)) +#define CTRLR0_FRF_MASK COND_CODE_1(SSI_VERSION_2, GENMASK(5, 4), GENMASK(7, 6)) #define CTRLR0_FRF_SPI 0UL #define CTRLR0_FRF_SSP 1UL #define CTRLR0_FRF_MICROWIRE 2UL -#define CTRLR0_DFS_MASK GENMASK(4, 0) +#define CTRLR0_DFS_MASK COND_CODE_1(SSI_VERSION_2, GENMASK(3, 0), GENMASK(4, 0)) +#define CTRLR0_DFS32_MASK COND_CODE_1(SSI_VERSION_2, GENMASK(20, 16), (0)) /* CTRLR1- Control Register 1 */ #define CTRLR1_NDF_MASK GENMASK(15, 0) diff --git a/drivers/mspi/mspi_dw_vendor_specific.h b/drivers/mspi/mspi_dw_vendor_specific.h index e34d8a5db60..d32a53ac293 100644 --- a/drivers/mspi/mspi_dw_vendor_specific.h +++ b/drivers/mspi/mspi_dw_vendor_specific.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2024 Nordic Semiconductor ASA + * Copyright (c) 2025 Tenstorrent AI ULC * * SPDX-License-Identifier: Apache-2.0 */ @@ -96,4 +97,41 @@ static inline int vendor_specific_xip_disable(const struct device *dev, } #endif /* defined(CONFIG_MSPI_XIP) */ +#else +static inline void vendor_specific_init(const struct device *dev) +{ + ARG_UNUSED(dev); +} +static inline void vendor_specific_suspend(const struct device *dev) +{ + ARG_UNUSED(dev); +} +static inline void vendor_specific_resume(const struct device *dev) +{ + ARG_UNUSED(dev); +} +static inline void vendor_specific_irq_clear(const struct device *dev) +{ + ARG_UNUSED(dev); +} +static inline int vendor_specific_xip_enable(const struct device *dev, + const struct mspi_dev_id *dev_id, + const struct mspi_xip_cfg *cfg) +{ + ARG_UNUSED(dev); + ARG_UNUSED(dev_id); + ARG_UNUSED(cfg); + + return 0; +} +static inline int vendor_specific_xip_disable(const struct device *dev, + const struct mspi_dev_id *dev_id, + const struct mspi_xip_cfg *cfg) +{ + ARG_UNUSED(dev); + ARG_UNUSED(dev_id); + ARG_UNUSED(cfg); + + return 0; +} #endif /* DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_exmif) */ diff --git a/include/zephyr/drivers/mspi/mspi_dw.h b/include/zephyr/drivers/mspi/mspi_dw.h new file mode 100644 index 00000000000..b51c9b9cdf0 --- /dev/null +++ b/include/zephyr/drivers/mspi/mspi_dw.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2025 Tenstorrent AI ULC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_MSPI_DW_H_ +#define ZEPHYR_INCLUDE_DRIVERS_MSPI_DW_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Designware MSPI configuration structure- this should be passed to the + * MSPI driver when calling mspi_timing_config + */ +struct mspi_dw_timing_cfg { + uint32_t rx_sample_dly; /* RX sample delay, written to RX_SAMPLE_DLY register */ +}; + +/* Configure RX_SAMPLE_DLY register for MSPI DW SSI */ +#define MSPI_DW_RX_TIMING_CFG BIT(0) + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_DRIVERS_MSPI_DW_H_ */