diff --git a/drivers/spi/CMakeLists.txt b/drivers/spi/CMakeLists.txt index a27328b9df5..786dc7e2740 100644 --- a/drivers/spi/CMakeLists.txt +++ b/drivers/spi/CMakeLists.txt @@ -29,5 +29,6 @@ zephyr_library_sources_ifdef(CONFIG_SPI_NPCX_FIU spi_npcx_fiu.c) zephyr_library_sources_ifdef(CONFIG_SPI_BITBANG spi_bitbang.c) zephyr_library_sources_ifdef(CONFIG_SPI_XEC_QMSPI_LDMA spi_xec_qmspi_ldma.c) zephyr_library_sources_ifdef(CONFIG_SPI_GD32 spi_gd32.c) +zephyr_library_sources_ifdef(CONFIG_SPI_MCHP_QSPI spi_mchp_mss_qspi.c) zephyr_library_sources_ifdef(CONFIG_USERSPACE spi_handlers.c) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 5147aed8940..596b08b9488 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -101,4 +101,6 @@ source "drivers/spi/Kconfig.xec_qmspi_ldma" source "drivers/spi/Kconfig.gd32" +source "drivers/spi/Kconfig.mchp_mss_qspi" + endif # SPI diff --git a/drivers/spi/Kconfig.mchp_mss_qspi b/drivers/spi/Kconfig.mchp_mss_qspi new file mode 100644 index 00000000000..5a7b3ceb1bf --- /dev/null +++ b/drivers/spi/Kconfig.mchp_mss_qspi @@ -0,0 +1,11 @@ +# Microchip Polarfire SOC QSPI + +# Copyright (c) 2022 Microchip Technology Inc. +# SPDX-License-Identifier: Apache-2.0 + +config SPI_MCHP_QSPI + bool "Microchip Polarfire SOC QSPI driver" + default y + depends on SOC_MPFS + help + Enable support for the Polarfire SOC QSPI driver. diff --git a/drivers/spi/spi_mchp_mss_qspi.c b/drivers/spi/spi_mchp_mss_qspi.c new file mode 100644 index 00000000000..2af1002406a --- /dev/null +++ b/drivers/spi/spi_mchp_mss_qspi.c @@ -0,0 +1,597 @@ +/* + * Copyright (c) 2022 Microchip Technology Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT microchip_mpfs_qspi + +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(mss_qspi, CONFIG_SPI_LOG_LEVEL); +#include "spi_context.h" + +/*MSS QSPI Register offsets */ +#define MSS_QSPI_REG_CONTROL (0x00) +#define MSS_QSPI_REG_FRAMES (0x04) +#define MSS_QSPI_REG_IEN (0x0c) +#define MSS_QSPI_REG_STATUS (0x10) +#define MSS_QSPI_REG_DIRECT_ACCESS (0x14) +#define MSS_QSPI_REG_UPPER_ACCESS (0x18) +#define MSS_QSPI_REG_RX_DATA (0x40) +#define MSS_QSPI_REG_TX_DATA (0x44) +#define MSS_QSPI_REG_X4_RX_DATA (0x48) +#define MSS_QSPI_REG_X4_TX_DATA (0x4c) +#define MSS_QSPI_REG_FRAMESUP (0x50) + +/* QSPICR bit definitions */ +#define MSS_QSPI_CONTROL_ENABLE BIT(0) +#define MSS_QSPI_CONTROL_MASTER BIT(1) +#define MSS_QSPI_CONTROL_XIP BIT(2) +#define MSS_QSPI_CONTROL_XIPADDR BIT(3) +#define MSS_QSPI_CONTROL_CLKIDLE BIT(10) +#define MSS_QSPI_CONTROL_SAMPLE_MSK (3 << 11) +#define MSS_QSPI_CONTROL_MODE0 BIT(13) +#define MSS_QSPI_CONTROL_MODE_EXQUAD (0x6 << 13) +#define MSS_QSPI_CONTROL_MODE_EXDUAL (0x2 << 13) +#define MSS_QSPI_CONTROL_MODE12_MSK (3 << 14) +#define MSS_QSPI_CONTROL_FLAGSX4 BIT(16) +#define MSS_QSPI_CONTROL_CLKRATE_MSK (0xf << 24) +#define MSS_QSPI_CONTROL_CLKRATE 24 + +/* QSPIFRAMES bit definitions */ +#define MSS_QSPI_FRAMES_TOTALBYTES_MSK (0xffff << 0) +#define MSS_QSPI_FRAMES_TOTALBYTES_MSK (0xffff << 0) +#define MSS_QSPI_FRAMES_CMDBYTES_MSK (0x1ff << 16) +#define MSS_QSPI_FRAMES_CMDBYTES 16 +#define MSS_QSPI_FRAMES_QSPI BIT(25) +#define MSS_QSPI_FRAMES_IDLE_MSK (0xf << 26) +#define MSS_QSPI_FRAMES_FLAGBYTE BIT(30) +#define MSS_QSPI_FRAMES_FLAGWORD BIT(31) + +/* QSPIIEN bit definitions */ +#define MSS_QSPI_IEN_TXDONE BIT(0) +#define MSS_QSPI_IEN_RXDONE BIT(1) +#define MSS_QSPI_IEN_RXAVAILABLE BIT(2) +#define MSS_QSPI_IEN_TXAVAILABLE BIT(3) +#define MSS_QSPI_IEN_RXFIFOEMPTY BIT(4) +#define MSS_QSPI_IEN_TXFIFOFULL BIT(5) +#define MSS_QSPI_IEN_FLAGSX4 BIT(8) + +/* QSPIST bit definitions */ +#define MSS_QSPI_STATUS_TXDONE BIT(0) +#define MSS_QSPI_STATUS_RXDONE BIT(1) +#define MSS_QSPI_STATUS_RXAVAILABLE BIT(2) +#define MSS_QSPI_STATUS_TXAVAILABLE BIT(3) +#define MSS_QSPI_STATUS_RXFIFOEMPTY BIT(4) +#define MSS_QSPI_STATUS_TXFIFOFULL BIT(5) +#define MSS_QSPI_STATUS_READY BIT(7) +#define MSS_QSPI_STATUS_FLAGSX4 BIT(8) + +/* QSPIDA bit definitions */ +#define MSS_QSPI_DA_EN_SSEL BIT(0) +#define MSS_QSPI_DA_OP_SSEL BIT(1) +#define MSS_QSPI_DA_EN_SCLK BIT(2) +#define MSS_QSPI_DA_OP_SCLK BIT(3) +#define MSS_QSPI_DA_EN_SDO_MSK (0xf << 4) +#define MSS_QSPI_DA_OP_SDO_MSK (0xf << 8) +#define MSS_QSPI_DA_OP_SDATA_MSK (0xf << 12) +#define MSS_QSPI_DA_IP_SDI_MSK (0xf << 16) +#define MSS_QSPI_DA_IP_SCLK BIT(21) +#define MSS_QSPI_DA_IP_SSEL BIT(22) +#define MSS_QSPI_DA_IDLE BIT(23) +#define MSS_QSPI_RXDATA_MSK (0xff << 0) +#define MSS_QSPI_TXDATA_MSK (0xff << 0) + +/* QSPIFRAMESUP bit definitions */ +#define MSS_QSPI_FRAMESUP_UP_BYTES_MSK (0xFFFF << 16) +#define MSS_QSPI_FRAMESUP_LO_BYTES_MSK (0xFFFF << 0) + +/* + * Private data structure for an SPI slave + */ +struct mss_qspi_config { + mm_reg_t base; + void (*irq_config_func)(const struct device *dev); + int irq; + uint32_t clock_freq; +}; + +/* Device run time data */ +struct mss_qspi_data { + struct spi_context ctx; +}; + +static inline uint32_t mss_qspi_read(const struct mss_qspi_config *cfg, + mm_reg_t offset) +{ + return sys_read32(cfg->base + offset); +} + +static inline void mss_qspi_write(const struct mss_qspi_config *cfg, + uint32_t val, mm_reg_t offset) +{ + sys_write32(val, cfg->base + offset); +} + +static void mss_qspi_enable_ints(const struct mss_qspi_config *s) +{ + uint32_t mask = MSS_QSPI_IEN_TXDONE | + MSS_QSPI_IEN_RXDONE | + MSS_QSPI_IEN_RXAVAILABLE; + + mss_qspi_write(s, mask, MSS_QSPI_REG_IEN); +} + +static void mss_qspi_disable_ints(const struct mss_qspi_config *s) +{ + uint32_t mask = 0; + + mss_qspi_write(s, mask, MSS_QSPI_REG_IEN); +} + +static inline void mss_qspi_transmit_x8(const struct device *dev, uint32_t len) +{ + const struct mss_qspi_config *s = dev->config; + struct mss_qspi_data *data = dev->data; + struct spi_context *ctx = &data->ctx; + uint32_t count, skips; + + skips = mss_qspi_read(s, MSS_QSPI_REG_CONTROL); + skips &= ~MSS_QSPI_CONTROL_FLAGSX4; + mss_qspi_write(s, skips, MSS_QSPI_REG_CONTROL); + for (count = 0; count < len; ++count) { + while (mss_qspi_read(s, MSS_QSPI_REG_STATUS) + & MSS_QSPI_STATUS_TXFIFOFULL) + ; + if (spi_context_tx_buf_on(ctx)) { + mss_qspi_write(s, ctx->tx_buf[0], MSS_QSPI_REG_TX_DATA); + spi_context_update_tx(ctx, 1, 1); + } + } +} + +static inline void mss_qspi_transmit_x32(const struct device *dev, uint32_t len) +{ + const struct mss_qspi_config *s = dev->config; + struct mss_qspi_data *data = dev->data; + struct spi_context *ctx = &data->ctx; + uint32_t count, ctrl, wdata; + + ctrl = mss_qspi_read(s, MSS_QSPI_REG_CONTROL); + ctrl |= MSS_QSPI_CONTROL_FLAGSX4; + mss_qspi_write(s, ctrl, MSS_QSPI_REG_CONTROL); + for (count = 0; count < len / 4; ++count) { + while (mss_qspi_read(s, MSS_QSPI_REG_STATUS) + & MSS_QSPI_STATUS_TXFIFOFULL) + ; + if (spi_context_tx_buf_on(ctx)) { + wdata = UNALIGNED_GET((uint32_t *)(ctx->tx_buf)); + mss_qspi_write(s, wdata, MSS_QSPI_REG_X4_TX_DATA); + spi_context_update_tx(ctx, 1, 4); + } + } +} + +static inline void mss_qspi_receive_x32(const struct device *dev, uint32_t len) +{ + const struct mss_qspi_config *s = dev->config; + struct mss_qspi_data *data = dev->data; + struct spi_context *ctx = &data->ctx; + uint32_t count, ctrl, temp; + + ctrl = mss_qspi_read(s, MSS_QSPI_REG_CONTROL); + ctrl |= MSS_QSPI_CONTROL_FLAGSX4; + mss_qspi_write(s, ctrl, MSS_QSPI_REG_CONTROL); + for (count = 0; count < len / 4; ++count) { + while ((mss_qspi_read(s, MSS_QSPI_REG_STATUS) + & MSS_QSPI_STATUS_RXFIFOEMPTY)) + ; + if (spi_context_rx_buf_on(ctx)) { + temp = mss_qspi_read(s, MSS_QSPI_REG_X4_RX_DATA); + UNALIGNED_PUT(temp, (uint32_t *)ctx->rx_buf); + spi_context_update_rx(ctx, 1, 4); + } + } +} + +static inline void mss_qspi_receive_x8(const struct device *dev, uint32_t len) +{ + const struct mss_qspi_config *s = dev->config; + struct mss_qspi_data *data = dev->data; + struct spi_context *ctx = &data->ctx; + uint32_t rdata, count; + + rdata = mss_qspi_read(s, MSS_QSPI_REG_CONTROL); + rdata &= ~MSS_QSPI_CONTROL_FLAGSX4; + mss_qspi_write(s, rdata, MSS_QSPI_REG_CONTROL); + for (count = 0; count < len; ++count) { + while (mss_qspi_read(s, MSS_QSPI_REG_STATUS) + & MSS_QSPI_STATUS_RXFIFOEMPTY) + ; + if (spi_context_rx_buf_on(ctx)) { + rdata = mss_qspi_read(s, MSS_QSPI_REG_RX_DATA); + UNALIGNED_PUT(rdata, (uint8_t *)ctx->rx_buf); + spi_context_update_rx(ctx, 1, 1); + } + } +} + +static inline void mss_qspi_config_frames(const struct device *dev, + uint32_t total_bytes, + uint32_t cmd_bytes, bool x8) +{ + const struct mss_qspi_config *s = dev->config; + uint32_t skips; + + mss_qspi_write(s, (total_bytes & MSS_QSPI_FRAMESUP_UP_BYTES_MSK), + MSS_QSPI_REG_FRAMESUP); + skips = (total_bytes & MSS_QSPI_FRAMESUP_LO_BYTES_MSK); + if (cmd_bytes) { + skips |= ((cmd_bytes << MSS_QSPI_FRAMES_CMDBYTES) & MSS_QSPI_FRAMES_CMDBYTES_MSK); + } else { + skips |= ((total_bytes << MSS_QSPI_FRAMES_CMDBYTES) & MSS_QSPI_FRAMES_CMDBYTES_MSK); + } + if (mss_qspi_read(s, MSS_QSPI_REG_CONTROL) & MSS_QSPI_CONTROL_MODE0) + skips |= MSS_QSPI_FRAMES_QSPI; + + skips &= ~MSS_QSPI_FRAMES_IDLE_MSK; + if (x8) + skips |= MSS_QSPI_FRAMES_FLAGBYTE; + else + skips |= MSS_QSPI_FRAMES_FLAGWORD; + + mss_qspi_write(s, skips, MSS_QSPI_REG_FRAMES); +} + +static inline void mss_qspi_transmit(const struct device *dev) +{ + const struct mss_qspi_config *s = dev->config; + struct mss_qspi_data *data = dev->data; + struct spi_context *ctx = &data->ctx; + uint32_t total_byte_cnt, cmd_bytes; + + cmd_bytes = spi_context_longest_current_buf(ctx); + total_byte_cnt = spi_context_total_tx_len(ctx); + + /* + * As per the MSS QSPI IP spec, + * The number of command and data bytes are controlled by the frames register + * for each SPI sequence. This supports the SPI flash memory read and writes + * sequences as below. so configure the cmd and total bytes accordingly. + * --------------------------------------------------------------------- + * TOTAL BYTES | CMD BYTES | What happens | + * ______________________________________________________________________ + * | | | + * 1 | 1 | The SPI core will transmit a single byte | + * | | and receive data is discarded | + * | | | + * 1 | 0 | The SPI core will transmit a single byte | + * | | and return a single byte | + * | | | + * 10 | 4 | The SPI core will transmit 4 command | + * | | bytes discarding the receive data and | + * | | transmits 6 dummy bytes returning the 6 | + * | | received bytes and return a single byte | + * | | | + * 10 | 10 | The SPI core will transmit 10 command | + * | | | + * 10 | 0 | The SPI core will transmit 10 command | + * | | bytes and returning 10 received bytes | + * ______________________________________________________________________ + */ + if (!ctx->rx_buf) { + if (total_byte_cnt - cmd_bytes) { + mss_qspi_config_frames(dev, total_byte_cnt, 0, false); + mss_qspi_transmit_x8(dev, cmd_bytes); + mss_qspi_transmit_x32(dev, (total_byte_cnt - cmd_bytes)); + + } else { + mss_qspi_config_frames(dev, total_byte_cnt, cmd_bytes, true); + mss_qspi_transmit_x8(dev, cmd_bytes); + } + } else { + mss_qspi_config_frames(dev, total_byte_cnt, cmd_bytes, true); + mss_qspi_transmit_x8(dev, cmd_bytes); + } + + mss_qspi_enable_ints(s); +} + +static inline void mss_qspi_receive(const struct device *dev) +{ + const struct mss_qspi_config *s = dev->config; + struct mss_qspi_data *data = dev->data; + struct spi_context *ctx = &data->ctx; + uint32_t rd_bytes, skips, idx, rdata; + + /* + * Point the rx buffer where the actual read data + * will be stored + */ + spi_context_update_rx(ctx, 1, ctx->rx_len); + + rd_bytes = spi_context_longest_current_buf(ctx); + if (rd_bytes) { + if (rd_bytes >= 4) + mss_qspi_receive_x32(dev, rd_bytes); + + skips = mss_qspi_read(s, MSS_QSPI_REG_CONTROL); + skips &= ~MSS_QSPI_CONTROL_FLAGSX4; + mss_qspi_write(s, skips, MSS_QSPI_REG_CONTROL); + idx = (rd_bytes - (rd_bytes % 4u)); + for (; idx < rd_bytes; ++idx) { + while (mss_qspi_read(s, MSS_QSPI_REG_STATUS) & MSS_QSPI_STATUS_RXFIFOEMPTY) + ; + if (spi_context_rx_buf_on(ctx)) { + rdata = mss_qspi_read(s, MSS_QSPI_REG_RX_DATA); + UNALIGNED_PUT(rdata, (uint8_t *)ctx->rx_buf); + spi_context_update_rx(ctx, 1, 1); + } + } + } +} + +static inline int mss_qspi_clk_gen_set(const struct mss_qspi_config *s, + const struct spi_config *spi_cfg) +{ + uint32_t control = mss_qspi_read(s, MSS_QSPI_REG_CONTROL); + uint32_t idx, clkrate, val = 0, speed; + + if (spi_cfg->frequency > s->clock_freq) + speed = s->clock_freq / 2; + + for (idx = 1; idx < 16; idx++) { + clkrate = s->clock_freq / (2 * idx); + if (clkrate <= spi_cfg->frequency) { + val = idx; + break; + } + } + + if (val) { + control = mss_qspi_read(s, MSS_QSPI_REG_CONTROL); + control &= ~MSS_QSPI_CONTROL_CLKRATE_MSK; + control |= (val << MSS_QSPI_CONTROL_CLKRATE); + mss_qspi_write(s, control, MSS_QSPI_REG_CONTROL); + } else { + return -1; + } + + return 0; +} + +static inline int mss_qspi_hw_mode_set(const struct mss_qspi_config *s, + uint16_t mode) +{ + uint32_t ctrl = mss_qspi_read(s, MSS_QSPI_REG_CONTROL); + + if ((mode & SPI_MODE_CPHA) && (mode & SPI_MODE_CPOL)) { + /* mode 3 */ + ctrl |= MSS_QSPI_CONTROL_CLKIDLE; + } else if (!(mode & SPI_MODE_CPHA) && !(mode & SPI_MODE_CPOL)) { + /* mode 0 */ + ctrl &= ~MSS_QSPI_CONTROL_CLKIDLE; + } else { + return -1; + } + + if ((mode & SPI_LINES_QUAD)) { + /* Quad mode */ + ctrl &= ~(MSS_QSPI_CONTROL_MODE0); + ctrl |= (MSS_QSPI_CONTROL_MODE_EXQUAD); + } else if ((mode & SPI_LINES_DUAL)) { + /* Dual mode */ + ctrl &= ~(MSS_QSPI_CONTROL_MODE0); + ctrl |= (MSS_QSPI_CONTROL_MODE_EXDUAL); + } else { + /* Normal mode */ + ctrl &= ~(MSS_QSPI_CONTROL_MODE0); + } + + mss_qspi_write(s, ctrl, MSS_QSPI_REG_CONTROL); + + return 0; +} + +static int mss_qspi_release(const struct device *dev, + const struct spi_config *config) +{ + struct mss_qspi_data *data = dev->data; + const struct mss_qspi_config *cfg = dev->config; + uint32_t control = mss_qspi_read(cfg, MSS_QSPI_REG_CONTROL); + + mss_qspi_disable_ints(cfg); + + control &= ~MSS_QSPI_CONTROL_ENABLE; + mss_qspi_write(cfg, control, MSS_QSPI_REG_CONTROL); + + spi_context_unlock_unconditionally(&data->ctx); + + return 0; +} + +static void mss_qspi_interrupt(const struct device *dev) +{ + const struct mss_qspi_config *cfg = dev->config; + struct mss_qspi_data *data = dev->data; + struct spi_context *ctx = &data->ctx; + + int intfield = mss_qspi_read(cfg, MSS_QSPI_REG_STATUS); + int ienfield = mss_qspi_read(cfg, MSS_QSPI_REG_IEN); + + if ((intfield & ienfield) == 0) + return; + + if (intfield & MSS_QSPI_IEN_TXDONE) { + mss_qspi_write(cfg, MSS_QSPI_IEN_TXDONE, MSS_QSPI_REG_STATUS); + } + + if (intfield & MSS_QSPI_IEN_RXAVAILABLE) { + mss_qspi_write(cfg, MSS_QSPI_IEN_RXAVAILABLE, MSS_QSPI_REG_STATUS); + mss_qspi_receive(dev); + } + + if ((intfield & MSS_QSPI_IEN_RXDONE)) { + mss_qspi_write(cfg, MSS_QSPI_IEN_RXDONE, MSS_QSPI_REG_STATUS); + spi_context_complete(ctx, 0); + } + + if (intfield & MSS_QSPI_IEN_TXAVAILABLE) { + mss_qspi_write(cfg, MSS_QSPI_IEN_TXAVAILABLE, MSS_QSPI_REG_STATUS); + } + + if (intfield & MSS_QSPI_IEN_RXFIFOEMPTY) { + mss_qspi_write(cfg, MSS_QSPI_IEN_RXFIFOEMPTY, MSS_QSPI_REG_STATUS); + } + + if (intfield & MSS_QSPI_IEN_TXFIFOFULL) { + mss_qspi_write(cfg, MSS_QSPI_IEN_TXFIFOFULL, MSS_QSPI_REG_STATUS); + } +} + +static int mss_qspi_configure(const struct device *dev, + const struct spi_config *spi_cfg) +{ + const struct mss_qspi_config *cfg = dev->config; + + if (spi_cfg->operation & SPI_OP_MODE_SLAVE) { + LOG_ERR("Slave mode is not supported\n\r"); + return -ENOTSUP; + } + + if (spi_cfg->operation & SPI_MODE_LOOP) { + LOG_ERR("Loop back mode is not supported\n\r"); + return -ENOTSUP; + } + + if (spi_cfg->operation & (SPI_TRANSFER_LSB) || + ((IS_ENABLED(CONFIG_SPI_EXTENDED_MODES) && + (spi_cfg->operation & (SPI_LINES_DUAL | + SPI_LINES_QUAD | + SPI_LINES_OCTAL))))) { + LOG_ERR("Unsupported configuration\n\r"); + return -ENOTSUP; + } + + if (mss_qspi_clk_gen_set(cfg, spi_cfg)) { + LOG_ERR("can't set clk divider\n"); + return -EINVAL; + } + + return 0; +} + +static int mss_qspi_transceive(const struct device *dev, + const struct spi_config *spi_cfg, + const struct spi_buf_set *tx_bufs, + const struct spi_buf_set *rx_bufs, + bool async, struct k_poll_signal *sig) +{ + const struct mss_qspi_config *config = dev->config; + struct mss_qspi_data *data = dev->data; + struct spi_context *ctx = &data->ctx; + int ret = 0; + + spi_context_lock(ctx, async, sig, spi_cfg); + ret = mss_qspi_configure(dev, spi_cfg); + if (ret) { + goto out; + } + + mss_qspi_hw_mode_set(config, spi_cfg->operation); + spi_context_buffers_setup(ctx, tx_bufs, rx_bufs, + 1); + mss_qspi_transmit(dev); + ret = spi_context_wait_for_completion(ctx); +out: + spi_context_release(ctx, ret); + mss_qspi_disable_ints(config); + + return ret; +} + +static int mss_qspi_transceive_blocking(const struct device *dev, + const struct spi_config *spi_cfg, + const struct spi_buf_set *tx_bufs, + const struct spi_buf_set *rx_bufs) +{ + return mss_qspi_transceive(dev, spi_cfg, tx_bufs, rx_bufs, false, + NULL); +} + +#ifdef CONFIG_SPI_ASYNC +static int mss_qspi_transceive_async(const struct device *dev, + const struct spi_config *spi_cfg, + const struct spi_buf_set *tx_bufs, + const struct spi_buf_set *rx_bufs, + struct k_poll_signal *async) +{ + return mss_qspi_transceive(dev, spi_cfg, tx_bufs, rx_bufs, true, + async); +} +#endif /* CONFIG_SPI_ASYNC */ + +static int mss_qspi_init(const struct device *dev) +{ + const struct mss_qspi_config *cfg = dev->config; + struct mss_qspi_data *data = dev->data; + unsigned int ret = 0; + uint32_t control = 0; + + cfg->irq_config_func(dev); + + control &= ~(MSS_QSPI_CONTROL_SAMPLE_MSK); + control &= ~(MSS_QSPI_CONTROL_MODE0); + control |= (MSS_QSPI_CONTROL_CLKRATE_MSK); + control &= ~(MSS_QSPI_CONTROL_XIP); + control |= (MSS_QSPI_CONTROL_CLKIDLE | MSS_QSPI_CONTROL_ENABLE); + mss_qspi_write(cfg, control, MSS_QSPI_REG_CONTROL); + mss_qspi_disable_ints(cfg); + spi_context_unlock_unconditionally(&data->ctx); + + return ret; +} + +static const struct spi_driver_api mss_qspi_driver_api = { + .transceive = mss_qspi_transceive_blocking, +#ifdef CONFIG_SPI_ASYNC + .transceive_async = mss_qspi_transceive_async, +#endif /* CONFIG_SPI_ASYNC */ + .release = mss_qspi_release, +}; + +#define MSS_QSPI_INIT(n) \ + static void mss_qspi_config_func_##n(const struct device *dev); \ + \ + static const struct mss_qspi_config mss_qspi_config_##n = { \ + .base = DT_INST_REG_ADDR(n), \ + .irq_config_func = mss_qspi_config_func_##n, \ + .clock_freq = DT_INST_PROP(n, clock_frequency), \ + }; \ + \ + static struct mss_qspi_data mss_qspi_data_##n = { \ + SPI_CONTEXT_INIT_LOCK(mss_qspi_data_##n, ctx), \ + SPI_CONTEXT_INIT_SYNC(mss_qspi_data_##n, ctx), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, &mss_qspi_init, \ + NULL, \ + &mss_qspi_data_##n, \ + &mss_qspi_config_##n, POST_KERNEL, \ + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ + &mss_qspi_driver_api); \ + \ + static void mss_qspi_config_func_##n(const struct device *dev) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), \ + mss_qspi_interrupt, \ + DEVICE_DT_INST_GET(n), 0); \ + irq_enable(DT_INST_IRQN(n)); \ + } + +DT_INST_FOREACH_STATUS_OKAY(MSS_QSPI_INIT)