| /* |
| * Copyright (c) 2022 Microchip Technology Inc. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT microchip_mpfs_qspi |
| |
| #include <zephyr/device.h> |
| #include <zephyr/drivers/spi.h> |
| #include <zephyr/drivers/spi/rtio.h> |
| #include <zephyr/sys/sys_io.h> |
| #include <zephyr/sys/util.h> |
| #include <zephyr/logging/log.h> |
| #include <zephyr/irq.h> |
| |
| 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, dev, 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, |
| spi_callback_t cb, |
| void *userdata) |
| { |
| 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, cb, userdata, 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, 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, |
| spi_callback_t cb, |
| void *userdata) |
| { |
| return mss_qspi_transceive(dev, spi_cfg, tx_bufs, rx_bufs, true, |
| cb, userdata); |
| } |
| #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 */ |
| #ifdef CONFIG_SPI_RTIO |
| .iodev_submit = spi_rtio_iodev_default_submit, |
| #endif |
| .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) |