/* * Copyright (c) 2022 Renesas Electronics Corporation * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT renesas_smartbond_spi #define LOG_LEVEL CONFIG_SPI_LOG_LEVEL #include LOG_MODULE_REGISTER(spi_smartbond); #include "spi_context.h" #include #include #include #include #include #include #include #include #include #include #include #define DIVN_CLK 32000000 /* DIVN clock: fixed @32MHz */ #define SCLK_FREQ_2MHZ (DIVN_CLK / 14) /* 2.285714 MHz*/ #define SCLK_FREQ_4MHZ (DIVN_CLK / 8) /* 4 MHz */ #define SCLK_FREQ_8MHZ (DIVN_CLK / 4) /* 8 MHz */ #define SCLK_FREQ_16MHZ (DIVN_CLK / 2) /* 16 MHz */ enum spi_smartbond_transfer { SPI_SMARTBOND_TRANSFER_TX_ONLY, SPI_SMARTBOND_TRANSFER_RX_ONLY, SPI_SMARTBOND_TRANSFER_TX_RX, SPI_SMARTBOND_TRANSFER_NONE }; enum spi_smartbond_dma_channel { SPI_SMARTBOND_DMA_TX_CHANNEL, SPI_SMARTBOND_DMA_RX_CHANNEL }; enum spi_smartbond_fifo_mode { /* Bi-directional mode */ SPI_SMARTBOND_FIFO_MODE_TX_RX, /* TX FIFO single depth, no flow control */ SPI_SMARTBOND_FIFO_MODE_RX_ONLY, /* RX FIFO single depth, no flow control */ SPI_SMARTBOND_FIFO_MODE_TX_ONLY, SPI_SMARTBOND_FIFO_NONE }; struct spi_smartbond_cfg { SPI_Type *regs; int periph_clock_config; const struct pinctrl_dev_config *pcfg; #ifdef CONFIG_SPI_SMARTBOND_DMA int tx_dma_chan; int rx_dma_chan; uint8_t tx_slot_mux; uint8_t rx_slot_mux; const struct device *tx_dma_ctrl; const struct device *rx_dma_ctrl; #endif }; struct spi_smartbond_data { struct spi_context ctx; uint8_t dfs; #if defined(CONFIG_PM_DEVICE) uint32_t spi_ctrl_reg; #endif #ifdef CONFIG_SPI_SMARTBOND_DMA struct dma_config tx_dma_cfg; struct dma_config rx_dma_cfg; struct dma_block_config tx_dma_block_cfg; struct dma_block_config rx_dma_block_cfg; struct k_sem rx_dma_sync; struct k_sem tx_dma_sync; ATOMIC_DEFINE(dma_channel_atomic_flag, 2); #endif #if defined(CONFIG_SPI_ASYNC) || defined(CONFIG_SPI_SMARTBOND_DMA) size_t rx_len; size_t tx_len; size_t transferred; enum spi_smartbond_transfer transfer_mode; #endif }; #define SPI_CTRL_REG_SET_FIELD(_field, _var, _val) \ (_var) = \ (((_var) & ~SPI_SPI_CTRL_REG_ ## _field ## _Msk) | \ (((_val) << SPI_SPI_CTRL_REG_ ## _field ## _Pos) & SPI_SPI_CTRL_REG_ ## _field ## _Msk)) static inline void spi_smartbond_enable(const struct spi_smartbond_cfg *cfg, bool enable) { if (enable) { cfg->regs->SPI_CTRL_REG |= SPI_SPI_CTRL_REG_SPI_ON_Msk; cfg->regs->SPI_CTRL_REG &= ~SPI_SPI_CTRL_REG_SPI_RST_Msk; } else { cfg->regs->SPI_CTRL_REG &= ~SPI_SPI_CTRL_REG_SPI_ON_Msk; cfg->regs->SPI_CTRL_REG |= SPI_SPI_CTRL_REG_SPI_RST_Msk; } } static inline bool spi_smartbond_isenabled(const struct spi_smartbond_cfg *cfg) { return (!!(cfg->regs->SPI_CTRL_REG & SPI_SPI_CTRL_REG_SPI_ON_Msk)) && (!(cfg->regs->SPI_CTRL_REG & SPI_SPI_CTRL_REG_SPI_RST_Msk)); } static inline void spi_smartbond_write_word(const struct device *dev) { const struct spi_smartbond_cfg *cfg = dev->config; struct spi_smartbond_data *data = dev->data; /* * No need to typecast the register address as the controller will automatically * generate the necessary clock cycles based on the data size. */ switch (data->dfs) { case 1: cfg->regs->SPI_RX_TX_REG = *(uint8_t *)data->ctx.tx_buf; break; case 2: cfg->regs->SPI_RX_TX_REG = sys_get_le16(data->ctx.tx_buf); break; case 4: cfg->regs->SPI_RX_TX_REG = sys_get_le32(data->ctx.tx_buf); break; } } static inline void spi_smartbond_write_dummy(const struct device *dev) { const struct spi_smartbond_cfg *cfg = dev->config; cfg->regs->SPI_RX_TX_REG = 0x0; } static inline void spi_smartbond_read_word(const struct device *dev) { const struct spi_smartbond_cfg *cfg = dev->config; struct spi_smartbond_data *data = dev->data; switch (data->dfs) { case 1: *(uint8_t *)data->ctx.rx_buf = cfg->regs->SPI_RX_TX_REG; break; case 2: sys_put_le16((uint16_t)cfg->regs->SPI_RX_TX_REG, data->ctx.rx_buf); break; case 4: sys_put_le32(cfg->regs->SPI_RX_TX_REG, data->ctx.rx_buf); break; } } static inline void spi_smartbond_read_discard(const struct device *dev) { const struct spi_smartbond_cfg *cfg = dev->config; (void)cfg->regs->SPI_RX_TX_REG; } static inline int spi_smartbond_set_speed(const struct spi_smartbond_cfg *cfg, const uint32_t frequency) { if (frequency < SCLK_FREQ_2MHZ) { LOG_ERR("Frequency is lower than minimal SCLK %d", SCLK_FREQ_2MHZ); return -ENOTSUP; } else if (frequency < SCLK_FREQ_4MHZ) { cfg->regs->SPI_CTRL_REG = (cfg->regs->SPI_CTRL_REG & ~SPI_SPI_CTRL_REG_SPI_CLK_Msk) | 3UL << SPI_SPI_CTRL_REG_SPI_CLK_Pos; } else if (frequency < SCLK_FREQ_8MHZ) { cfg->regs->SPI_CTRL_REG = (cfg->regs->SPI_CTRL_REG & ~SPI_SPI_CTRL_REG_SPI_CLK_Msk); } else if (frequency < SCLK_FREQ_16MHZ) { cfg->regs->SPI_CTRL_REG = (cfg->regs->SPI_CTRL_REG & ~SPI_SPI_CTRL_REG_SPI_CLK_Msk) | 1UL << SPI_SPI_CTRL_REG_SPI_CLK_Pos; } else { cfg->regs->SPI_CTRL_REG = (cfg->regs->SPI_CTRL_REG & ~SPI_SPI_CTRL_REG_SPI_CLK_Msk) | 2UL << SPI_SPI_CTRL_REG_SPI_CLK_Pos; } return 0; } static inline int spi_smartbond_set_word_size(const struct spi_smartbond_cfg *cfg, struct spi_smartbond_data *data, const uint32_t operation) { switch (SPI_WORD_SIZE_GET(operation)) { case 8: data->dfs = 1; cfg->regs->SPI_CTRL_REG = (cfg->regs->SPI_CTRL_REG & ~SPI_SPI_CTRL_REG_SPI_WORD_Msk); break; case 16: data->dfs = 2; cfg->regs->SPI_CTRL_REG = (cfg->regs->SPI_CTRL_REG & ~SPI_SPI_CTRL_REG_SPI_WORD_Msk) | (1UL << SPI_SPI_CTRL_REG_SPI_WORD_Pos); break; case 32: data->dfs = 4; cfg->regs->SPI_CTRL_REG = (cfg->regs->SPI_CTRL_REG & ~SPI_SPI_CTRL_REG_SPI_WORD_Msk) | (2UL << SPI_SPI_CTRL_REG_SPI_WORD_Pos); break; default: LOG_ERR("Word size not supported"); return -ENOTSUP; } return 0; } static inline void spi_smartbond_pm_policy_state_lock_get(const struct device *dev) { #if defined(CONFIG_PM_DEVICE) /* * Prevent the SoC from entering the normal sleep state as PDC does not support * waking up the application core following SPI events. */ pm_policy_state_lock_get(PM_STATE_STANDBY, PM_ALL_SUBSTATES); pm_device_runtime_get(dev); #endif } static inline void spi_smartbond_pm_policy_state_lock_put(const struct device *dev) { #if defined(CONFIG_PM_DEVICE) pm_device_runtime_put(dev); /* * Allow the SoC to enter the normal sleep state once SPI transactions are done. */ pm_policy_state_lock_put(PM_STATE_STANDBY, PM_ALL_SUBSTATES); #endif } static int spi_smartbond_configure(const struct spi_smartbond_cfg *cfg, struct spi_smartbond_data *data, const struct spi_config *spi_cfg) { int rc; if (spi_context_configured(&data->ctx, spi_cfg)) { #ifdef CONFIG_PM_DEVICE spi_smartbond_enable(cfg, true); #endif return 0; } if (spi_cfg->operation & SPI_OP_MODE_SLAVE) { LOG_ERR("Slave mode not yet supported"); return -ENOTSUP; } if (spi_cfg->operation & SPI_HALF_DUPLEX) { LOG_ERR("Half-duplex not supported"); return -ENOTSUP; } if (IS_ENABLED(CONFIG_SPI_EXTENDED_MODES) && (spi_cfg->operation & SPI_LINES_MASK) != SPI_LINES_SINGLE) { LOG_ERR("Only single line mode is supported"); return -ENOTSUP; } if (spi_cfg->operation & SPI_MODE_LOOP) { LOG_ERR("Loopback mode is not supported"); return -ENOTSUP; } if (spi_smartbond_isenabled(cfg)) { spi_smartbond_enable(cfg, false); } rc = spi_smartbond_set_speed(cfg, spi_cfg->frequency); if (rc) { return rc; } cfg->regs->SPI_CTRL_REG = (spi_cfg->operation & SPI_MODE_CPOL) ? (cfg->regs->SPI_CTRL_REG | SPI_SPI_CTRL_REG_SPI_POL_Msk) : (cfg->regs->SPI_CTRL_REG & ~SPI_SPI_CTRL_REG_SPI_POL_Msk); cfg->regs->SPI_CTRL_REG = (spi_cfg->operation & SPI_MODE_CPHA) ? (cfg->regs->SPI_CTRL_REG | SPI_SPI_CTRL_REG_SPI_PHA_Msk) : (cfg->regs->SPI_CTRL_REG & ~SPI_SPI_CTRL_REG_SPI_PHA_Msk); rc = spi_smartbond_set_word_size(cfg, data, spi_cfg->operation); if (rc) { return rc; } cfg->regs->SPI_CTRL_REG &= ~(SPI_SPI_CTRL_REG_SPI_FIFO_MODE_Msk); spi_smartbond_enable(cfg, true); cfg->regs->SPI_CTRL_REG &= ~SPI_SPI_CTRL_REG_SPI_MINT_Msk; data->ctx.config = spi_cfg; return 0; } #if defined(CONFIG_SPI_ASYNC) || defined(CONFIG_SPI_SMARTBOND_DMA) static inline void spi_smartbond_isr_set_status(const struct device *dev, bool status) { const struct spi_smartbond_cfg *cfg = dev->config; if (status) { cfg->regs->SPI_CTRL_REG |= SPI_SPI_CTRL_REG_SPI_MINT_Msk; } else { cfg->regs->SPI_CTRL_REG &= ~SPI_SPI_CTRL_REG_SPI_MINT_Msk; } } static inline bool spi_smartbond_is_busy(const struct device *dev) { const struct spi_smartbond_cfg *cfg = dev->config; return (cfg->regs->SPI_CTRL_REG & SPI_SPI_CTRL_REG_SPI_BUSY_Msk); } static inline void spi_smartbond_clear_interrupt(const struct device *dev) { const struct spi_smartbond_cfg *cfg = dev->config; cfg->regs->SPI_CLEAR_INT_REG = 0x1; } /* 0 = No RX data available, 1 = data has been transmitted and received */ static inline bool spi_smartbond_is_rx_data(const struct device *dev) { const struct spi_smartbond_cfg *cfg = dev->config; return (cfg->regs->SPI_CTRL_REG & SPI_SPI_CTRL_REG_SPI_INT_BIT_Msk); } static inline uint8_t spi_smartbond_get_fifo_mode(const struct device *dev) { const struct spi_smartbond_cfg *cfg = dev->config; return ((cfg->regs->SPI_CTRL_REG & SPI_SPI_CTRL_REG_SPI_FIFO_MODE_Msk) >> SPI_SPI_CTRL_REG_SPI_FIFO_MODE_Pos); } static void spi_smartbond_set_fifo_mode(const struct device *dev, enum spi_smartbond_fifo_mode mode) { const struct spi_smartbond_cfg *cfg = dev->config; bool is_enabled = spi_smartbond_isenabled(cfg); enum spi_smartbond_fifo_mode current_mode = spi_smartbond_get_fifo_mode(dev); uint32_t spi_ctrl_reg = cfg->regs->SPI_CTRL_REG; #ifdef CONFIG_SPI_SMARTBOND_DMA struct spi_smartbond_data *data = dev->data; #endif if ((current_mode != mode) #ifdef CONFIG_SPI_SMARTBOND_DMA || (data->dfs == 4) #endif ) { if (current_mode != SPI_SMARTBOND_FIFO_MODE_RX_ONLY) { while (spi_smartbond_is_busy(dev)) { ; } } /* Controller should be disabled when FIFO mode is updated */ cfg->regs->SPI_CTRL_REG &= ~SPI_SPI_CTRL_REG_SPI_ON_Msk; #ifdef CONFIG_SPI_SMARTBOND_DMA /* * Workaround for the controller that cannot generate DMA requests * for 4-byte bus length. */ if (data->dfs == 4) { mode = SPI_SMARTBOND_FIFO_NONE; } #endif SPI_CTRL_REG_SET_FIELD(SPI_FIFO_MODE, spi_ctrl_reg, mode); if (mode != SPI_SMARTBOND_FIFO_NONE) { SPI_CTRL_REG_SET_FIELD(SPI_DMA_TXREQ_MODE, spi_ctrl_reg, 0); } else { SPI_CTRL_REG_SET_FIELD(SPI_DMA_TXREQ_MODE, spi_ctrl_reg, 1); } if (is_enabled) { SPI_CTRL_REG_SET_FIELD(SPI_ON, spi_ctrl_reg, 1); } cfg->regs->SPI_CTRL_REG = spi_ctrl_reg; } } static int spi_smartbond_transfer_mode_get(const struct device *dev) { struct spi_smartbond_data *data = dev->data; struct spi_context *ctx = &data->ctx; if (spi_context_rx_buf_on(ctx) || spi_context_tx_buf_on(ctx)) { /* * Check only buffers' length as it might happen that current buffer is NULL. * In such a case the context should be updated and a dummy write/read should * take place. */ if (ctx->rx_len || ctx->tx_len) { spi_smartbond_set_fifo_mode(dev, SPI_SMARTBOND_FIFO_MODE_TX_RX); return SPI_SMARTBOND_TRANSFER_TX_RX; } if (!spi_context_rx_buf_on(ctx)) { spi_smartbond_set_fifo_mode(dev, SPI_SMARTBOND_FIFO_MODE_TX_ONLY); return SPI_SMARTBOND_TRANSFER_TX_ONLY; } if (!spi_context_tx_buf_on(ctx)) { /* * Use the TX/RX mode with TX being dummy. Using the RX only mode * is a bit tricky as the controller should generate clock cycles * automatically and immediately after the ISR is enabled. */ spi_smartbond_set_fifo_mode(dev, SPI_SMARTBOND_FIFO_MODE_TX_RX); return SPI_SMARTBOND_TRANSFER_RX_ONLY; } } /* Return waiting updating the fifo mode */ return SPI_SMARTBOND_TRANSFER_NONE; } static inline void spi_smartbond_transfer_mode_check_and_update(const struct device *dev) { struct spi_smartbond_data *data = dev->data; data->transfer_mode = spi_smartbond_transfer_mode_get(dev); } #endif #ifdef CONFIG_SPI_ASYNC static inline bool spi_smartbond_is_tx_full(const struct device *dev) { const struct spi_smartbond_cfg *cfg = dev->config; return (cfg->regs->SPI_CTRL_REG & SPI_SPI_CTRL_REG_SPI_TXH_Msk); } static void spi_smartbond_write(const struct device *dev) { struct spi_smartbond_data *data = dev->data; struct spi_context *ctx = &data->ctx; while (spi_context_tx_buf_on(ctx)) { /* Check if TX FIFO is full as otherwise undefined data should be transmitted. */ if (spi_smartbond_is_tx_full(dev)) { spi_smartbond_clear_interrupt(dev); break; } /* Send to TX FIFO and update buffer pointer. */ spi_smartbond_write_word(dev); spi_context_update_tx(ctx, data->dfs, 1); /* * It might happen that a NULL buffer with a non-zero length is provided. * In that case, the bytes should be consumed. */ if (ctx->rx_len && !ctx->rx_buf) { spi_smartbond_read_discard(dev); spi_context_update_rx(ctx, data->dfs, 1); } } } static void spi_smartbond_transfer(const struct device *dev) { struct spi_smartbond_data *data = dev->data; struct spi_context *ctx = &data->ctx; while (data->rx_len) { /* Zero means that RX FIFO or register is empty */ if (!spi_smartbond_is_rx_data(dev)) { break; } if (ctx->rx_buf) { spi_smartbond_read_word(dev); } else { spi_smartbond_read_discard(dev); } spi_context_update_rx(ctx, data->dfs, 1); spi_smartbond_clear_interrupt(dev); data->rx_len--; data->transferred++; } while (data->tx_len) { /* Check if TX FIFO is full as otherwise undefined data should be transmitted. */ if (spi_smartbond_is_tx_full(dev)) { break; } if (ctx->tx_buf) { spi_smartbond_write_word(dev); } else { spi_smartbond_write_dummy(dev); } spi_context_update_tx(ctx, data->dfs, 1); data->tx_len--; } } static void spi_smartbond_read(const struct device *dev) { struct spi_smartbond_data *data = dev->data; struct spi_context *ctx = &data->ctx; while (spi_context_rx_buf_on(ctx)) { /* Zero means that RX FIFO or register is empty */ if (!spi_smartbond_is_rx_data(dev)) { break; } spi_smartbond_read_word(dev); spi_context_update_rx(ctx, data->dfs, 1); spi_smartbond_clear_interrupt(dev); } /* Perform dummy access to generate the required clock cycles */ while (data->tx_len) { if (spi_smartbond_is_tx_full(dev)) { break; } spi_smartbond_write_dummy(dev); data->tx_len--; } } static void spi_smartbond_isr_trigger(const struct device *dev) { struct spi_smartbond_data *data = dev->data; struct spi_context *ctx = &data->ctx; data->transfer_mode = spi_smartbond_transfer_mode_get(dev); switch (data->transfer_mode) { case SPI_SMARTBOND_TRANSFER_RX_ONLY: data->tx_len = spi_context_total_rx_len(ctx); spi_smartbond_read(dev); break; case SPI_SMARTBOND_TRANSFER_TX_ONLY: spi_smartbond_write(dev); break; case SPI_SMARTBOND_TRANSFER_TX_RX: /* * Each sub-transfer in the descriptor list should be exercised * separately as it might happen that a buffer is NULL with * non-zero length. */ data->rx_len = spi_context_max_continuous_chunk(ctx); data->tx_len = data->rx_len; spi_smartbond_transfer(dev); break; case SPI_SMARTBOND_TRANSFER_NONE: __fallthrough; default: __ASSERT_MSG_INFO("Invalid transfer mode"); break; } spi_smartbond_isr_set_status(dev, true); } static int spi_smartbond_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) { const struct spi_smartbond_cfg *cfg = dev->config; struct spi_smartbond_data *data = dev->data; struct spi_context *ctx = &data->ctx; int rc; spi_context_lock(ctx, true, cb, userdata, spi_cfg); spi_smartbond_pm_policy_state_lock_get(dev); rc = spi_smartbond_configure(cfg, data, spi_cfg); if (rc == 0) { spi_context_buffers_setup(ctx, tx_bufs, rx_bufs, data->dfs); spi_context_cs_control(ctx, true); /* * PM constraints will be released within ISR once all transfers * are exercised along with de-asserting the #CS line. */ spi_smartbond_isr_trigger(dev); } /* * Context will actually be released when \sa spi_context_complete * is called. */ spi_context_release(ctx, rc); return rc; } #endif #if defined(CONFIG_SPI_ASYNC) || defined(CONFIG_SPI_SMARTBOND_DMA) static void spi_smartbond_isr(void *args) { #ifdef CONFIG_SPI_ASYNC struct device *dev = args; struct spi_smartbond_data *data = dev->data; struct spi_context *ctx = &data->ctx; switch (data->transfer_mode) { case SPI_SMARTBOND_TRANSFER_RX_ONLY: spi_smartbond_read(dev); break; case SPI_SMARTBOND_TRANSFER_TX_ONLY: spi_smartbond_write(dev); break; case SPI_SMARTBOND_TRANSFER_TX_RX: /* Exersice the type of the next sub-transfer */ if (!data->rx_len && !data->tx_len) { spi_smartbond_transfer_mode_check_and_update(dev); if (data->transfer_mode == SPI_SMARTBOND_TRANSFER_RX_ONLY) { data->tx_len = spi_context_total_rx_len(ctx) - data->transferred; /* Clear in case another truncated transfer should be executed */ data->transferred = 0; spi_smartbond_read(dev); } else if (data->transfer_mode == SPI_SMARTBOND_TRANSFER_TX_ONLY) { spi_smartbond_write(dev); } else if (data->transfer_mode == SPI_SMARTBOND_TRANSFER_TX_RX) { data->rx_len = spi_context_max_continuous_chunk(ctx); data->tx_len = data->rx_len; spi_smartbond_transfer(dev); } } else { spi_smartbond_transfer(dev); } break; case SPI_SMARTBOND_TRANSFER_NONE: __fallthrough; default: __ASSERT_MSG_INFO("Invalid transfer mode"); break; } /* All buffers have been exercised, signal completion */ if (!spi_context_tx_buf_on(ctx) && !spi_context_rx_buf_on(ctx)) { spi_smartbond_isr_set_status(dev, false); /* Mark completion to trigger callback function */ spi_context_complete(ctx, dev, 0); spi_context_cs_control(ctx, false); spi_smartbond_pm_policy_state_lock_put(data); } #endif } #endif #ifdef CONFIG_SPI_SMARTBOND_DMA static uint32_t spi_smartbond_read_dummy_buf; /* * Should be used to flush the RX FIFO in case a transaction is requested * with NULL pointer and non-zero length. In such a case, data will be * shifted into the RX FIFO (regardless of whether or not the RX mode is * disabled) which should then be flushed. Otherwise, a next read operation * will result in fetching old bytes. */ static void spi_smartbond_flush_rx_fifo(const struct device *dev) { while (spi_smartbond_is_busy(dev)) { }; while (spi_smartbond_is_rx_data(dev)) { spi_smartbond_read_discard(dev); spi_smartbond_clear_interrupt(dev); } } static int spi_smartbond_dma_tx_channel_request(const struct device *dev) { struct spi_smartbond_data *data = dev->data; const struct spi_smartbond_cfg *config = dev->config; if (!atomic_test_and_set_bit(data->dma_channel_atomic_flag, SPI_SMARTBOND_DMA_TX_CHANNEL)) { if (dma_request_channel(config->tx_dma_ctrl, (void *)&config->tx_dma_chan) < 0) { atomic_clear_bit(data->dma_channel_atomic_flag, SPI_SMARTBOND_DMA_TX_CHANNEL); return -EIO; } } return 0; } #ifdef CONFIG_PM_DEVICE static void spi_smartbond_dma_tx_channel_release(const struct device *dev) { struct spi_smartbond_data *data = dev->data; const struct spi_smartbond_cfg *config = dev->config; if (atomic_test_and_clear_bit(data->dma_channel_atomic_flag, SPI_SMARTBOND_DMA_TX_CHANNEL)) { dma_release_channel(config->tx_dma_ctrl, config->tx_dma_chan); } } #endif static int spi_smartbond_dma_rx_channel_request(const struct device *dev) { struct spi_smartbond_data *data = dev->data; const struct spi_smartbond_cfg *config = dev->config; if (!atomic_test_and_set_bit(data->dma_channel_atomic_flag, SPI_SMARTBOND_DMA_RX_CHANNEL)) { if (dma_request_channel(config->rx_dma_ctrl, (void *)&config->rx_dma_chan) < 0) { atomic_clear_bit(data->dma_channel_atomic_flag, SPI_SMARTBOND_DMA_RX_CHANNEL); return -EIO; } } return 0; } #ifdef CONFIG_PM_DEVICE static void spi_smartbond_dma_rx_channel_release(const struct device *dev) { struct spi_smartbond_data *data = dev->data; const struct spi_smartbond_cfg *config = dev->config; if (atomic_test_and_clear_bit(data->dma_channel_atomic_flag, SPI_SMARTBOND_DMA_RX_CHANNEL)) { dma_release_channel(config->rx_dma_ctrl, config->rx_dma_chan); } } #endif static void spi_smartbond_tx_dma_cb(const struct device *dma, void *arg, uint32_t id, int status) { const struct device *dev = arg; struct spi_smartbond_data *data = dev->data; struct spi_context *ctx = &data->ctx; if (status < 0) { LOG_WRN("DMA transfer did not complete"); } spi_context_update_tx(ctx, data->dfs, data->tx_len); k_sem_give(&data->tx_dma_sync); } static void spi_smartbond_rx_dma_cb(const struct device *dma, void *arg, uint32_t id, int status) { const struct device *dev = arg; struct spi_smartbond_data *data = dev->data; struct spi_context *ctx = &data->ctx; if (status < 0) { LOG_WRN("DMA transfer did not complete"); } spi_context_update_rx(ctx, data->dfs, data->rx_len); k_sem_give(&data->rx_dma_sync); } #ifdef CONFIG_PM_DEVICE static void spi_smartbond_dma_deconfig(const struct device *dev) { const struct spi_smartbond_cfg *config = dev->config; if (config->rx_dma_ctrl && config->tx_dma_ctrl) { dma_stop(config->rx_dma_ctrl, config->rx_dma_chan); dma_stop(config->tx_dma_ctrl, config->tx_dma_chan); spi_smartbond_dma_rx_channel_release(dev); spi_smartbond_dma_tx_channel_release(dev); } } #endif static int spi_smartbond_dma_config(const struct device *dev) { struct spi_smartbond_data *data = dev->data; const struct spi_smartbond_cfg *config = dev->config; struct dma_config *tx = &data->tx_dma_cfg; struct dma_config *rx = &data->rx_dma_cfg; struct dma_block_config *tx_block = &data->tx_dma_block_cfg; struct dma_block_config *rx_block = &data->rx_dma_block_cfg; /* * DMA RX should be assigned an even number and * DMA TX should be assigned the right next * channel (odd number). */ if (!(config->tx_dma_chan & 0x1) || (config->rx_dma_chan & 0x1) || (config->tx_dma_chan != (config->rx_dma_chan + 1))) { LOG_ERR("Invalid RX/TX channel selection"); return -EINVAL; } if (config->tx_slot_mux != config->rx_slot_mux) { LOG_ERR("TX/RX DMA slots mismatch"); return -EINVAL; } if (!device_is_ready(config->tx_dma_ctrl) || !device_is_ready(config->rx_dma_ctrl)) { LOG_ERR("TX/RX DMA device is not ready"); return -ENODEV; } if (spi_smartbond_dma_tx_channel_request(dev) < 0) { LOG_ERR("TX DMA channel is already occupied"); return -EIO; } if (spi_smartbond_dma_rx_channel_request(dev) < 0) { LOG_ERR("RX DMA channel is already occupied"); return -EIO; } tx->channel_direction = MEMORY_TO_PERIPHERAL; tx->dma_callback = spi_smartbond_tx_dma_cb; tx->user_data = (void *)dev; tx->block_count = 1; tx->head_block = &data->tx_dma_block_cfg; tx->error_callback_dis = 1; tx->dma_slot = config->tx_slot_mux; tx->channel_priority = 2; /* Burst mode is not using when DREQ is one */ tx->source_burst_length = 1; tx->dest_burst_length = 1; /* Source and destination data size should reflect DFS value */ tx->source_data_size = 0; tx->dest_data_size = 0; /* Do not change */ tx_block->dest_addr_adj = 0x2; /* Incremental */ tx_block->source_addr_adj = 0x0; tx_block->dest_address = (uint32_t)&config->regs->SPI_RX_TX_REG; /* * To be filled when a transaction is requested and * should reflect the total number of bytes. */ tx_block->block_size = 0; /* Should reflect the TX buffer */ tx_block->source_address = 0; rx->channel_direction = PERIPHERAL_TO_MEMORY; rx->dma_callback = spi_smartbond_rx_dma_cb; rx->user_data = (void *)dev; rx->block_count = 1; rx->head_block = &data->rx_dma_block_cfg; rx->error_callback_dis = 1; rx->dma_slot = config->rx_slot_mux; rx->channel_priority = 2; /* Burst mode is not using when DREQ is one */ rx->source_burst_length = 1; rx->dest_burst_length = 1; /* Source and destination data size should reflect DFS value */ rx->source_data_size = 0; rx->dest_data_size = 0; /* Do not change */ rx_block->source_addr_adj = 0x2; /* Incremenetal */ rx_block->dest_addr_adj = 0x0; rx_block->source_address = (uint32_t)&config->regs->SPI_RX_TX_REG; /* * To be filled when a transaction is requested and * should reflect the total number of bytes. */ rx_block->block_size = 0; /* Should reflect the RX buffer */ rx_block->dest_address = 0; return 0; } static int spi_smartbond_dma_trigger(const struct device *dev) { struct spi_smartbond_data *data = dev->data; const struct spi_smartbond_cfg *config = dev->config; struct spi_context *ctx = &data->ctx; struct dma_config *tx = &data->tx_dma_cfg; struct dma_config *rx = &data->rx_dma_cfg; struct dma_block_config *tx_block = &data->tx_dma_block_cfg; struct dma_block_config *rx_block = &data->rx_dma_block_cfg; rx->source_data_size = data->dfs; rx->dest_data_size = data->dfs; tx->source_data_size = data->dfs; tx->dest_data_size = data->dfs; data->transfer_mode = spi_smartbond_transfer_mode_get(dev); do { switch (data->transfer_mode) { case SPI_SMARTBOND_TRANSFER_RX_ONLY: spi_smartbond_flush_rx_fifo(dev); data->rx_len = spi_context_max_continuous_chunk(ctx); data->tx_len = data->rx_len; rx_block->block_size = data->rx_len * data->dfs; tx_block->block_size = rx_block->block_size; rx_block->dest_address = (uint32_t)ctx->rx_buf; rx_block->dest_addr_adj = 0x0; tx_block->source_address = (uint32_t)&spi_smartbond_read_dummy_buf; /* Non-incremental */ tx_block->source_addr_adj = 0x2; if (dma_config(config->tx_dma_ctrl, config->tx_dma_chan, tx) < 0) { LOG_ERR("TX DMA configuration failed"); return -EINVAL; } if (dma_config(config->rx_dma_ctrl, config->rx_dma_chan, rx) < 0) { LOG_ERR("RX DMA configuration failed"); return -EINVAL; } dma_start(config->rx_dma_ctrl, config->rx_dma_chan); dma_start(config->tx_dma_ctrl, config->tx_dma_chan); /* Wait for the current DMA transfer to complete */ k_sem_take(&data->tx_dma_sync, K_FOREVER); k_sem_take(&data->rx_dma_sync, K_FOREVER); break; case SPI_SMARTBOND_TRANSFER_TX_ONLY: spi_smartbond_flush_rx_fifo(dev); data->tx_len = spi_context_max_continuous_chunk(ctx); data->rx_len = data->tx_len; tx_block->block_size = data->tx_len * data->dfs; tx_block->source_address = (uint32_t)ctx->tx_buf; tx_block->source_addr_adj = 0x0; if (dma_config(config->tx_dma_ctrl, config->tx_dma_chan, tx) < 0) { LOG_ERR("TX DMA configuration failed"); return -EINVAL; } dma_start(config->tx_dma_ctrl, config->tx_dma_chan); /* Wait for the current DMA transfer to complete */ k_sem_take(&data->tx_dma_sync, K_FOREVER); break; case SPI_SMARTBOND_TRANSFER_TX_RX: spi_smartbond_flush_rx_fifo(dev); data->rx_len = spi_context_max_continuous_chunk(ctx); data->tx_len = data->rx_len; /* * DMA block size represents total number of bytes whilist, * context length is divided by the data size (dfs). */ tx_block->block_size = data->tx_len * data->dfs; rx_block->block_size = tx_block->block_size; if (ctx->tx_buf) { tx_block->source_address = (uint32_t)ctx->tx_buf; tx_block->source_addr_adj = 0x0; } else { tx_block->source_address = (uint32_t)&spi_smartbond_read_dummy_buf; tx_block->source_addr_adj = 0x2; } if (ctx->rx_buf) { rx_block->dest_address = (uint32_t)ctx->rx_buf; rx_block->dest_addr_adj = 0x0; } else { rx_block->dest_address = (uint32_t)&spi_smartbond_read_dummy_buf; rx_block->dest_addr_adj = 0x2; } if (dma_config(config->tx_dma_ctrl, config->tx_dma_chan, tx) < 0) { LOG_ERR("TX DMA configuration failed"); return -EINVAL; } if (dma_config(config->rx_dma_ctrl, config->rx_dma_chan, rx) < 0) { LOG_ERR("RX DMA configuration failed"); return -EINVAL; } dma_start(config->rx_dma_ctrl, config->rx_dma_chan); dma_start(config->tx_dma_ctrl, config->tx_dma_chan); k_sem_take(&data->tx_dma_sync, K_FOREVER); k_sem_take(&data->rx_dma_sync, K_FOREVER); /* * Regardless of whether or not the RX FIFO is enabled, received * bytes are pushed into it. As such, the RXI FIFO should be * flushed so that a next read access retrives the correct bytes * and not old ones. */ if (!ctx->rx_buf) { spi_smartbond_flush_rx_fifo(dev); } break; case SPI_SMARTBOND_TRANSFER_NONE: __fallthrough; default: __ASSERT_MSG_INFO("Invalid transfer mode"); break; } spi_smartbond_transfer_mode_check_and_update(dev); } while (data->transfer_mode != SPI_SMARTBOND_TRANSFER_NONE); return 0; } #endif static int spi_smartbond_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) { const struct spi_smartbond_cfg *cfg = dev->config; struct spi_smartbond_data *data = dev->data; struct spi_context *ctx = &data->ctx; int rc; spi_context_lock(&data->ctx, false, NULL, NULL, spi_cfg); spi_smartbond_pm_policy_state_lock_get(dev); rc = spi_smartbond_configure(cfg, data, spi_cfg); if (rc == 0) { spi_context_buffers_setup(ctx, tx_bufs, rx_bufs, data->dfs); spi_context_cs_control(ctx, true); #ifdef CONFIG_SPI_SMARTBOND_DMA rc = spi_smartbond_dma_trigger(dev); /* Mark completion to trigger callback function */ spi_context_complete(ctx, dev, 0); #else while (spi_context_tx_buf_on(ctx) || spi_context_rx_buf_on(ctx)) { if (spi_context_tx_buf_on(ctx)) { spi_smartbond_write_word(dev); spi_context_update_tx(ctx, data->dfs, 1); } else { spi_smartbond_write_dummy(dev); } while (!(cfg->regs->SPI_CTRL_REG & SPI_SPI_CTRL_REG_SPI_INT_BIT_Msk)) { }; if (spi_context_rx_buf_on(ctx)) { spi_smartbond_read_word(dev); spi_context_update_rx(ctx, data->dfs, 1); } else { spi_smartbond_read_discard(dev); /* * It might happen that a NULL buffer with a non-zero length * is provided. In that case, the bytes should be consumed. */ if (ctx->rx_len) { spi_context_update_rx(ctx, data->dfs, 1); } } cfg->regs->SPI_CLEAR_INT_REG = 1UL; } #endif spi_context_cs_control(ctx, false); } spi_context_release(&data->ctx, rc); spi_smartbond_pm_policy_state_lock_put(dev); return rc; } static int spi_smartbond_release(const struct device *dev, const struct spi_config *spi_cfg) { struct spi_smartbond_data *data = dev->data; struct spi_context *ctx = &data->ctx; if (!spi_context_configured(ctx, spi_cfg)) { LOG_ERR("SPI configuration was not the last one to be used"); return -EINVAL; } spi_context_unlock_unconditionally(ctx); return 0; } static DEVICE_API(spi, spi_smartbond_driver_api) = { .transceive = spi_smartbond_transceive, #ifdef CONFIG_SPI_ASYNC .transceive_async = spi_smartbond_transceive_async, #endif #ifdef CONFIG_SPI_RTIO .iodev_submit = spi_rtio_iodev_default_submit, #endif .release = spi_smartbond_release, }; static int spi_smartbond_resume(const struct device *dev) { const struct spi_smartbond_cfg *cfg = dev->config; struct spi_smartbond_data *data = dev->data; int rc; CRG_COM->RESET_CLK_COM_REG = cfg->periph_clock_config << 1; CRG_COM->SET_CLK_COM_REG = cfg->periph_clock_config; rc = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT); if (rc < 0) { LOG_ERR("Failed to configure SPI pins"); return rc; } rc = spi_context_cs_configure_all(&data->ctx); if (rc < 0) { LOG_ERR("Failed to configure CS pins: %d", rc); return rc; } #ifdef CONFIG_SPI_SMARTBOND_DMA rc = spi_smartbond_dma_config(dev); if (rc < 0) { LOG_ERR("Failed to configure DMA"); return rc; } #endif spi_context_unlock_unconditionally(&data->ctx); return 0; } #if defined(CONFIG_PM_DEVICE) static int spi_smartbond_suspend(const struct device *dev) { int ret; const struct spi_smartbond_cfg *config = dev->config; struct spi_smartbond_data *data = dev->data; data->spi_ctrl_reg = config->regs->SPI_CTRL_REG; /* Disable the SPI digital block */ config->regs->SPI_CTRL_REG &= ~SPI_SPI_CTRL_REG_SPI_EN_CTRL_Msk; /* Gate SPI clocking */ CRG_COM->RESET_CLK_COM_REG = config->periph_clock_config; ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_SLEEP); if (ret < 0) { LOG_WRN("Failed to configure the SPI pins to inactive state"); } #ifdef CONFIG_SPI_SMARTBOND_DMA spi_smartbond_dma_deconfig(dev); #endif return ret; } static int spi_smartbond_pm_action(const struct device *dev, enum pm_device_action action) { int ret; switch (action) { case PM_DEVICE_ACTION_RESUME: da1469x_pd_acquire(MCU_PD_DOMAIN_COM); ret = spi_smartbond_resume(dev); break; case PM_DEVICE_ACTION_SUSPEND: ret = spi_smartbond_suspend(dev); da1469x_pd_release(MCU_PD_DOMAIN_COM); break; default: ret = -ENOTSUP; } return ret; } #endif #define SPI_SMARTBOND_ISR_CONNECT \ IRQ_CONNECT(DT_IRQN(DT_NODELABEL(spi)), DT_IRQ(DT_NODELABEL(spi), priority), \ spi_smartbond_isr, DEVICE_DT_GET(DT_NODELABEL(spi)), 0); \ irq_enable(DT_IRQN(DT_NODELABEL(spi))); #define SPI2_SMARTBOND_ISR_CONNECT \ IRQ_CONNECT(DT_IRQN(DT_NODELABEL(spi2)), DT_IRQ(DT_NODELABEL(spi2), priority), \ spi_smartbond_isr, DEVICE_DT_GET(DT_NODELABEL(spi2)), 0); \ irq_enable(DT_IRQN(DT_NODELABEL(spi2))); #if defined(CONFIG_SPI_ASYNC) || defined(CONFIG_SPI_SMARTBOND_DMA) static int spi_smartbond_isr_connect(const struct device *dev) { const struct spi_smartbond_cfg *cfg = dev->config; switch ((uint32_t)cfg->regs) { case (uint32_t)SPI: COND_CODE_1(DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(spi)), (SPI_SMARTBOND_ISR_CONNECT), (NULL)); break; case (uint32_t)SPI2: COND_CODE_1(DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(spi2)), (SPI2_SMARTBOND_ISR_CONNECT), (NULL)); break; default: return -EINVAL; } return 0; } #endif static int spi_smartbond_init(const struct device *dev) { int ret; struct spi_smartbond_data *data = dev->data; #if defined(CONFIG_SPI_ASYNC) || defined(CONFIG_SPI_SMARTBOND_DMA) data->transfer_mode = SPI_SMARTBOND_TRANSFER_NONE; #endif #ifdef CONFIG_SPI_SMARTBOND_DMA k_sem_init(&data->tx_dma_sync, 0, 1); k_sem_init(&data->rx_dma_sync, 0, 1); #endif #ifdef CONFIG_PM_DEVICE_RUNTIME /* Make sure device state is marked as suspended */ pm_device_init_suspended(dev); ret = pm_device_runtime_enable(dev); #else da1469x_pd_acquire(MCU_PD_DOMAIN_COM); ret = spi_smartbond_resume(dev); #endif spi_context_unlock_unconditionally(&data->ctx); #if defined(CONFIG_SPI_ASYNC) || defined(CONFIG_SPI_SMARTBOND_DMA) ret = spi_smartbond_isr_connect(dev); #endif return ret; } #ifdef CONFIG_SPI_SMARTBOND_DMA #define SPI_SMARTBOND_DMA_TX_INIT(id) \ .tx_dma_chan = DT_INST_DMAS_CELL_BY_NAME(id, tx, channel), \ .tx_slot_mux = (uint8_t)DT_INST_DMAS_CELL_BY_NAME(id, tx, config), \ .tx_dma_ctrl = DEVICE_DT_GET(DT_INST_DMAS_CTLR_BY_NAME(id, tx)), #else #define SPI_SMARTBOND_DMA_TX_INIT(id) #endif #ifdef CONFIG_SPI_SMARTBOND_DMA #define SPI_SMARTBOND_DMA_RX_INIT(id) \ .rx_dma_chan = DT_INST_DMAS_CELL_BY_NAME(id, rx, channel), \ .rx_slot_mux = (uint8_t)DT_INST_DMAS_CELL_BY_NAME(id, rx, config), \ .rx_dma_ctrl = DEVICE_DT_GET(DT_INST_DMAS_CTLR_BY_NAME(id, rx)), #else #define SPI_SMARTBOND_DMA_RX_INIT(id) #endif #ifdef CONFIG_SPI_SMARTBOND_DMA #define SPI_SMARTBOND_DMA_TX_INVALIDATE(id) \ .tx_dma_chan = 255, \ .tx_slot_mux = 255, \ .tx_dma_ctrl = NULL, #else #define SPI_SMARTBOND_DMA_TX_INVALIDATE(id) #endif #ifdef CONFIG_SPI_SMARTBOND_DMA #define SPI_SMARTBOND_DMA_RX_INVALIDATE(id) \ .rx_dma_chan = 255, \ .rx_slot_mux = 255, \ .rx_dma_ctrl = NULL, #else #define SPI_SMARTBOND_DMA_RX_INVALIDATE(id) #endif #define SPI_SMARTBOND_DEVICE(id) \ PINCTRL_DT_INST_DEFINE(id); \ static const struct spi_smartbond_cfg spi_smartbond_##id##_cfg = { \ .regs = (SPI_Type *)DT_INST_REG_ADDR(id), \ .periph_clock_config = DT_INST_PROP(id, periph_clock_config), \ .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(id), \ COND_CODE_1(DT_INST_DMAS_HAS_NAME(id, tx), \ (SPI_SMARTBOND_DMA_TX_INIT(id)), \ (SPI_SMARTBOND_DMA_TX_INVALIDATE(id))) \ COND_CODE_1(DT_INST_DMAS_HAS_NAME(id, rx), \ (SPI_SMARTBOND_DMA_RX_INIT(id)), \ (SPI_SMARTBOND_DMA_RX_INVALIDATE(id))) \ }; \ static struct spi_smartbond_data spi_smartbond_##id##_data = { \ SPI_CONTEXT_INIT_LOCK(spi_smartbond_##id##_data, ctx), \ SPI_CONTEXT_INIT_SYNC(spi_smartbond_##id##_data, ctx), \ SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(id), ctx)}; \ PM_DEVICE_DT_INST_DEFINE(id, spi_smartbond_pm_action); \ SPI_DEVICE_DT_INST_DEFINE(id, \ spi_smartbond_init, \ PM_DEVICE_DT_INST_GET(id), \ &spi_smartbond_##id##_data, \ &spi_smartbond_##id##_cfg, \ POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, \ &spi_smartbond_driver_api); DT_INST_FOREACH_STATUS_OKAY(SPI_SMARTBOND_DEVICE)