/* * Copyright (c) 2016, Freescale Semiconductor, Inc. * Copyright (c) 2017,2019, NXP * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT nxp_lpc_spi #include #include #include #include #include #ifdef CONFIG_SPI_MCUX_FLEXCOMM_DMA #include #endif #include #include #include #include LOG_MODULE_REGISTER(spi_mcux_flexcomm, CONFIG_SPI_LOG_LEVEL); #include "spi_context.h" #define SPI_CHIP_SELECT_COUNT 4 #define SPI_MAX_DATA_WIDTH 16 struct spi_mcux_config { SPI_Type *base; const struct device *clock_dev; clock_control_subsys_t clock_subsys; void (*irq_config_func)(const struct device *dev); uint32_t pre_delay; uint32_t post_delay; uint32_t frame_delay; uint32_t transfer_delay; uint32_t def_char; const struct pinctrl_dev_config *pincfg; const struct reset_dt_spec reset; }; #ifdef CONFIG_SPI_MCUX_FLEXCOMM_DMA #define SPI_MCUX_FLEXCOMM_DMA_ERROR_FLAG 0x01 #define SPI_MCUX_FLEXCOMM_DMA_RX_DONE_FLAG 0x02 #define SPI_MCUX_FLEXCOMM_DMA_TX_DONE_FLAG 0x04 #define SPI_MCUX_FLEXCOMM_DMA_DONE_FLAG \ (SPI_MCUX_FLEXCOMM_DMA_RX_DONE_FLAG | SPI_MCUX_FLEXCOMM_DMA_TX_DONE_FLAG) struct stream { const struct device *dma_dev; uint32_t channel; /* stores the channel for dma */ struct dma_config dma_cfg; struct dma_block_config dma_blk_cfg[2]; }; #endif struct spi_mcux_data { const struct device *dev; spi_master_handle_t handle; struct spi_context ctx; size_t transfer_len; #ifdef CONFIG_SPI_MCUX_FLEXCOMM_DMA volatile uint32_t status_flags; struct stream dma_rx; struct stream dma_tx; /* dummy value used for transferring NOP when tx buf is null */ uint32_t dummy_tx_buffer; /* Used to send the last word */ uint32_t last_word; #endif }; static void spi_mcux_transfer_next_packet(const struct device *dev) { const struct spi_mcux_config *config = dev->config; struct spi_mcux_data *data = dev->data; SPI_Type *base = config->base; struct spi_context *ctx = &data->ctx; spi_transfer_t transfer; status_t status; if ((ctx->tx_len == 0) && (ctx->rx_len == 0)) { /* nothing left to rx or tx, we're done! */ spi_context_cs_control(&data->ctx, false); spi_context_complete(&data->ctx, dev, 0); return; } transfer.configFlags = 0; if (ctx->tx_len == 0) { /* rx only, nothing to tx */ transfer.txData = NULL; transfer.rxData = ctx->rx_buf; transfer.dataSize = ctx->rx_len; } else if (ctx->rx_len == 0) { /* tx only, nothing to rx */ transfer.txData = (uint8_t *) ctx->tx_buf; transfer.rxData = NULL; transfer.dataSize = ctx->tx_len; } else if (ctx->tx_len == ctx->rx_len) { /* rx and tx are the same length */ transfer.txData = (uint8_t *) ctx->tx_buf; transfer.rxData = ctx->rx_buf; transfer.dataSize = ctx->tx_len; } else if (ctx->tx_len > ctx->rx_len) { /* Break up the tx into multiple transfers so we don't have to * rx into a longer intermediate buffer. Leave chip select * active between transfers. */ transfer.txData = (uint8_t *) ctx->tx_buf; transfer.rxData = ctx->rx_buf; transfer.dataSize = ctx->rx_len; } else { /* Break up the rx into multiple transfers so we don't have to * tx from a longer intermediate buffer. Leave chip select * active between transfers. */ transfer.txData = (uint8_t *) ctx->tx_buf; transfer.rxData = ctx->rx_buf; transfer.dataSize = ctx->tx_len; } if (ctx->tx_count <= 1 && ctx->rx_count <= 1) { transfer.configFlags = kSPI_FrameAssert; } data->transfer_len = transfer.dataSize; status = SPI_MasterTransferNonBlocking(base, &data->handle, &transfer); if (status != kStatus_Success) { LOG_ERR("Transfer could not start"); } } static void spi_mcux_isr(const struct device *dev) { const struct spi_mcux_config *config = dev->config; struct spi_mcux_data *data = dev->data; SPI_Type *base = config->base; SPI_MasterTransferHandleIRQ(base, &data->handle); } static void spi_mcux_transfer_callback(SPI_Type *base, spi_master_handle_t *handle, status_t status, void *userData) { struct spi_mcux_data *data = userData; spi_context_update_tx(&data->ctx, 1, data->transfer_len); spi_context_update_rx(&data->ctx, 1, data->transfer_len); spi_mcux_transfer_next_packet(data->dev); } static uint8_t spi_clock_cycles(uint32_t delay_ns, uint32_t sck_frequency_hz) { /* Convert delay_ns to an integer number of clock cycles of frequency * sck_frequency_hz. The maximum delay is 15 clock cycles. */ uint8_t delay_cycles = (uint64_t)delay_ns * sck_frequency_hz / NSEC_PER_SEC; delay_cycles = MIN(delay_cycles, 15); return delay_cycles; } static int spi_mcux_configure(const struct device *dev, const struct spi_config *spi_cfg) { const struct spi_mcux_config *config = dev->config; struct spi_mcux_data *data = dev->data; SPI_Type *base = config->base; uint32_t clock_freq; uint32_t word_size; if (spi_context_configured(&data->ctx, spi_cfg)) { /* This configuration is already in use */ return 0; } if (spi_cfg->operation & SPI_HALF_DUPLEX) { LOG_ERR("Half-duplex not supported"); return -ENOTSUP; } word_size = SPI_WORD_SIZE_GET(spi_cfg->operation); if (word_size > SPI_MAX_DATA_WIDTH) { LOG_ERR("Word size %d is greater than %d", word_size, SPI_MAX_DATA_WIDTH); return -EINVAL; } /* * Do master or slave initialisation, depending on the * mode requested. */ if (SPI_OP_MODE_GET(spi_cfg->operation) == SPI_OP_MODE_MASTER) { spi_master_config_t master_config; SPI_MasterGetDefaultConfig(&master_config); if (!device_is_ready(config->clock_dev)) { LOG_ERR("clock control device not ready"); return -ENODEV; } /* Get the clock frequency */ if (clock_control_get_rate(config->clock_dev, config->clock_subsys, &clock_freq)) { return -EINVAL; } if (spi_cfg->slave > SPI_CHIP_SELECT_COUNT) { LOG_ERR("Slave %d is greater than %d", spi_cfg->slave, SPI_CHIP_SELECT_COUNT); return -EINVAL; } master_config.sselNum = spi_cfg->slave; master_config.sselPol = kSPI_SpolActiveAllLow; master_config.dataWidth = word_size - 1; master_config.polarity = (SPI_MODE_GET(spi_cfg->operation) & SPI_MODE_CPOL) ? kSPI_ClockPolarityActiveLow : kSPI_ClockPolarityActiveHigh; master_config.phase = (SPI_MODE_GET(spi_cfg->operation) & SPI_MODE_CPHA) ? kSPI_ClockPhaseSecondEdge : kSPI_ClockPhaseFirstEdge; master_config.direction = (spi_cfg->operation & SPI_TRANSFER_LSB) ? kSPI_LsbFirst : kSPI_MsbFirst; master_config.baudRate_Bps = spi_cfg->frequency; spi_delay_config_t *delayConfig = &master_config.delayConfig; delayConfig->preDelay = spi_clock_cycles(config->pre_delay, spi_cfg->frequency); delayConfig->postDelay = spi_clock_cycles(config->post_delay, spi_cfg->frequency); delayConfig->frameDelay = spi_clock_cycles(config->frame_delay, spi_cfg->frequency); delayConfig->transferDelay = spi_clock_cycles(config->transfer_delay, spi_cfg->frequency); SPI_MasterInit(base, &master_config, clock_freq); SPI_SetDummyData(base, (uint8_t)config->def_char); SPI_MasterTransferCreateHandle(base, &data->handle, spi_mcux_transfer_callback, data); data->ctx.config = spi_cfg; } else { spi_slave_config_t slave_config; SPI_SlaveGetDefaultConfig(&slave_config); slave_config.polarity = (SPI_MODE_GET(spi_cfg->operation) & SPI_MODE_CPOL) ? kSPI_ClockPolarityActiveLow : kSPI_ClockPolarityActiveHigh; slave_config.phase = (SPI_MODE_GET(spi_cfg->operation) & SPI_MODE_CPHA) ? kSPI_ClockPhaseSecondEdge : kSPI_ClockPhaseFirstEdge; slave_config.direction = (spi_cfg->operation & SPI_TRANSFER_LSB) ? kSPI_LsbFirst : kSPI_MsbFirst; /* SS pin active low */ slave_config.sselPol = kSPI_SpolActiveAllLow; slave_config.dataWidth = word_size - 1; SPI_SlaveInit(base, &slave_config); SPI_SetDummyData(base, (uint8_t)config->def_char); SPI_SlaveTransferCreateHandle(base, &data->handle, spi_mcux_transfer_callback, data); data->ctx.config = spi_cfg; } return 0; } #ifdef CONFIG_SPI_MCUX_FLEXCOMM_DMA /* Dummy buffer used as a sink when rc buf is null */ uint32_t dummy_rx_buffer; /* This function is executed in the interrupt context */ static void spi_mcux_dma_callback(const struct device *dev, void *arg, uint32_t channel, int status) { /* arg directly holds the spi device */ const struct device *spi_dev = arg; struct spi_mcux_data *data = spi_dev->data; if (status < 0) { LOG_ERR("DMA callback error with channel %d.", channel); data->status_flags |= SPI_MCUX_FLEXCOMM_DMA_ERROR_FLAG; } else { /* identify the origin of this callback */ if (channel == data->dma_tx.channel) { /* this part of the transfer ends */ data->status_flags |= SPI_MCUX_FLEXCOMM_DMA_TX_DONE_FLAG; } else if (channel == data->dma_rx.channel) { /* this part of the transfer ends */ data->status_flags |= SPI_MCUX_FLEXCOMM_DMA_RX_DONE_FLAG; } else { LOG_ERR("DMA callback channel %d is not valid.", channel); data->status_flags |= SPI_MCUX_FLEXCOMM_DMA_ERROR_FLAG; } } spi_context_complete(&data->ctx, spi_dev, 0); } static void spi_mcux_prepare_txlastword(uint32_t *txLastWord, const uint8_t *buf, const struct spi_config *spi_cfg, size_t len) { uint32_t word_size; word_size = SPI_WORD_SIZE_GET(spi_cfg->operation); if (word_size > 8) { *txLastWord = (((uint32_t)buf[len - 1U] << 8U) | (buf[len - 2U])); } else { *txLastWord = buf[len - 1U]; } *txLastWord |= (uint32_t)SPI_FIFOWR_EOT_MASK; *txLastWord |= ((uint32_t)SPI_DEASSERT_ALL & (~(uint32_t)SPI_DEASSERTNUM_SSEL((uint32_t)spi_cfg->slave))); /* set width of data - range asserted at entry */ *txLastWord |= SPI_FIFOWR_LEN(word_size - 1); } static void spi_mcux_prepare_txdummy(uint32_t *dummy, bool last_packet, const struct spi_config *spi_cfg) { uint32_t word_size; word_size = SPI_WORD_SIZE_GET(spi_cfg->operation); if (last_packet) { *dummy |= (uint32_t)SPI_FIFOWR_EOT_MASK; } *dummy |= ((uint32_t)SPI_DEASSERT_ALL & (~(uint32_t)SPI_DEASSERTNUM_SSEL((uint32_t)spi_cfg->slave))); /* set width of data - range asserted at entry */ *dummy |= SPI_FIFOWR_LEN(word_size - 1); } static int spi_mcux_dma_tx_load(const struct device *dev, const uint8_t *buf, const struct spi_config *spi_cfg, size_t len, bool last_packet) { const struct spi_mcux_config *cfg = dev->config; struct spi_mcux_data *data = dev->data; struct dma_block_config *blk_cfg; int ret; SPI_Type *base = cfg->base; uint32_t word_size; word_size = SPI_WORD_SIZE_GET(spi_cfg->operation); /* remember active TX DMA channel (used in callback) */ struct stream *stream = &data->dma_tx; blk_cfg = &stream->dma_blk_cfg[0]; /* prepare the block for this TX DMA channel */ memset(blk_cfg, 0, sizeof(struct dma_block_config)); /* tx direction has memory as source and periph as dest. */ if (buf == NULL) { data->dummy_tx_buffer = 0; data->last_word = 0; spi_mcux_prepare_txdummy(&data->dummy_tx_buffer, last_packet, spi_cfg); if (last_packet && ((word_size > 8) ? (len > 2U) : (len > 1U))) { spi_mcux_prepare_txdummy(&data->last_word, last_packet, spi_cfg); blk_cfg->source_gather_en = 1; blk_cfg->source_address = (uint32_t)&data->dummy_tx_buffer; blk_cfg->dest_address = (uint32_t)&base->FIFOWR; blk_cfg->block_size = (word_size > 8) ? (len - 2U) : (len - 1U); blk_cfg->next_block = &stream->dma_blk_cfg[1]; blk_cfg->source_addr_adj = DMA_ADDR_ADJ_NO_CHANGE; blk_cfg->dest_addr_adj = DMA_ADDR_ADJ_NO_CHANGE; blk_cfg = &stream->dma_blk_cfg[1]; /* prepare the block for this TX DMA channel */ memset(blk_cfg, 0, sizeof(struct dma_block_config)); blk_cfg->source_address = (uint32_t)&data->last_word; blk_cfg->dest_address = (uint32_t)&base->FIFOWR; blk_cfg->block_size = sizeof(uint32_t); blk_cfg->source_addr_adj = DMA_ADDR_ADJ_NO_CHANGE; blk_cfg->dest_addr_adj = DMA_ADDR_ADJ_NO_CHANGE; } else { blk_cfg->source_address = (uint32_t)&data->dummy_tx_buffer; blk_cfg->dest_address = (uint32_t)&base->FIFOWR; blk_cfg->block_size = len; blk_cfg->source_addr_adj = DMA_ADDR_ADJ_NO_CHANGE; blk_cfg->dest_addr_adj = DMA_ADDR_ADJ_NO_CHANGE; } } else { if (last_packet) { spi_mcux_prepare_txlastword(&data->last_word, buf, spi_cfg, len); } /* If last packet and data transfer frame is bigger then 1, * use dma descriptor to send the last data. */ if (last_packet && ((word_size > 8) ? (len > 2U) : (len > 1U))) { blk_cfg->source_gather_en = 1; blk_cfg->source_address = (uint32_t)buf; blk_cfg->dest_address = (uint32_t)&base->FIFOWR; blk_cfg->block_size = (word_size > 8) ? (len - 2U) : (len - 1U); blk_cfg->next_block = &stream->dma_blk_cfg[1]; blk_cfg = &stream->dma_blk_cfg[1]; /* prepare the block for this TX DMA channel */ memset(blk_cfg, 0, sizeof(struct dma_block_config)); blk_cfg->source_address = (uint32_t)&data->last_word; blk_cfg->dest_address = (uint32_t)&base->FIFOWR; blk_cfg->block_size = sizeof(uint32_t); blk_cfg->source_addr_adj = DMA_ADDR_ADJ_NO_CHANGE; blk_cfg->dest_addr_adj = DMA_ADDR_ADJ_NO_CHANGE; } else { blk_cfg->source_address = (uint32_t)buf; blk_cfg->dest_address = (uint32_t)&base->FIFOWR; blk_cfg->block_size = len; } } /* Enables the DMA request from SPI txFIFO */ base->FIFOCFG |= SPI_FIFOCFG_DMATX_MASK; /* direction is given by the DT */ stream->dma_cfg.head_block = &stream->dma_blk_cfg[0]; /* give the client dev as arg, as the callback comes from the dma */ stream->dma_cfg.user_data = (struct device *)dev; /* pass our client origin to the dma: data->dma_tx.dma_channel */ ret = dma_config(data->dma_tx.dma_dev, data->dma_tx.channel, &stream->dma_cfg); /* the channel is the actual stream from 0 */ if (ret != 0) { return ret; } uint32_t tmpData = 0U; spi_mcux_prepare_txdummy(&tmpData, last_packet, spi_cfg); /* Setup the control info. * Halfword writes to just the control bits (offset 0xE22) doesn't push * anything into the FIFO. And the data access type of control bits must * be uint16_t, byte writes or halfword writes to FIFOWR will push the * data and the current control bits into the FIFO. */ if ((last_packet) && ((word_size > 8) ? (len == 2U) : (len == 1U))) { *((uint16_t *)((uint32_t)&base->FIFOWR) + 1) = (uint16_t)(tmpData >> 16U); } else { /* Clear the SPI_FIFOWR_EOT_MASK bit when data is not the last */ tmpData &= (~(uint32_t)SPI_FIFOWR_EOT_MASK); *((uint16_t *)((uint32_t)&base->FIFOWR) + 1) = (uint16_t)(tmpData >> 16U); } /* gives the request ID */ return dma_start(data->dma_tx.dma_dev, data->dma_tx.channel); } static int spi_mcux_dma_rx_load(const struct device *dev, uint8_t *buf, size_t len) { const struct spi_mcux_config *cfg = dev->config; struct spi_mcux_data *data = dev->data; struct dma_block_config *blk_cfg; int ret; SPI_Type *base = cfg->base; /* retrieve active RX DMA channel (used in callback) */ struct stream *stream = &data->dma_rx; blk_cfg = &stream->dma_blk_cfg[0]; /* prepare the block for this RX DMA channel */ memset(blk_cfg, 0, sizeof(struct dma_block_config)); blk_cfg->block_size = len; /* rx direction has periph as source and mem as dest. */ if (buf == NULL) { /* if rx buff is null, then write data to dummy address. */ blk_cfg->dest_address = (uint32_t)&dummy_rx_buffer; blk_cfg->dest_addr_adj = DMA_ADDR_ADJ_NO_CHANGE; } else { blk_cfg->dest_address = (uint32_t)buf; } blk_cfg->source_address = (uint32_t)&base->FIFORD; /* direction is given by the DT */ stream->dma_cfg.head_block = blk_cfg; stream->dma_cfg.user_data = (struct device *)dev; /* Enables the DMA request from SPI rxFIFO */ base->FIFOCFG |= SPI_FIFOCFG_DMARX_MASK; /* pass our client origin to the dma: data->dma_rx.channel */ ret = dma_config(data->dma_rx.dma_dev, data->dma_rx.channel, &stream->dma_cfg); /* the channel is the actual stream from 0 */ if (ret != 0) { return ret; } /* gives the request ID */ return dma_start(data->dma_rx.dma_dev, data->dma_rx.channel); } static int spi_mcux_dma_move_buffers(const struct device *dev, size_t len, const struct spi_config *spi_cfg, bool last_packet) { struct spi_mcux_data *data = dev->data; int ret; ret = spi_mcux_dma_rx_load(dev, data->ctx.rx_buf, len); if (ret != 0) { return ret; } ret = spi_mcux_dma_tx_load(dev, data->ctx.tx_buf, spi_cfg, len, last_packet); return ret; } static int wait_dma_rx_tx_done(const struct device *dev) { struct spi_mcux_data *data = dev->data; int ret = -1; while (1) { ret = spi_context_wait_for_completion(&data->ctx); if (data->status_flags & SPI_MCUX_FLEXCOMM_DMA_ERROR_FLAG) { return -EIO; } if ((data->status_flags & SPI_MCUX_FLEXCOMM_DMA_DONE_FLAG) == SPI_MCUX_FLEXCOMM_DMA_DONE_FLAG) { return 0; } } } static int transceive_dma(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 asynchronous, spi_callback_t cb, void *userdata) { const struct spi_mcux_config *config = dev->config; struct spi_mcux_data *data = dev->data; SPI_Type *base = config->base; int ret; uint32_t word_size; uint16_t data_size; spi_context_lock(&data->ctx, asynchronous, cb, userdata, spi_cfg); ret = spi_mcux_configure(dev, spi_cfg); if (ret) { goto out; } spi_context_buffers_setup(&data->ctx, tx_bufs, rx_bufs, 1); spi_context_cs_control(&data->ctx, true); word_size = SPI_WORD_SIZE_GET(spi_cfg->operation); data_size = (word_size > 8) ? (sizeof(uint16_t)) : (sizeof(uint8_t)); data->dma_rx.dma_cfg.source_data_size = data_size; data->dma_rx.dma_cfg.dest_data_size = data_size; data->dma_tx.dma_cfg.source_data_size = data_size; data->dma_tx.dma_cfg.dest_data_size = data_size; while (data->ctx.rx_len > 0 || data->ctx.tx_len > 0) { size_t dma_len; /* last is used to deassert chip select if this * is the last transfer in the set. */ bool last = false; if (data->ctx.rx_len == 0) { dma_len = data->ctx.tx_len; last = true; } else if (data->ctx.tx_len == 0) { dma_len = data->ctx.rx_len; last = true; } else if (data->ctx.tx_len == data->ctx.rx_len) { dma_len = data->ctx.rx_len; last = true; } else { dma_len = MIN(data->ctx.tx_len, data->ctx.rx_len); last = false; } /* at this point, last just means whether or not * this transfer will completely cover * the current tx/rx buffer in data->ctx * or require additional transfers because the * the two buffers are not the same size. * * if it covers the current ctx tx/rx buffers, then * we'll move to the next pair of buffers (if any) * after the transfer, but if there are * no more buffer pairs, then this is the last * transfer in the set and we need to deassert CS. */ if (last) { /* this dma transfer should cover * the entire current data->ctx set * of buffers. if there are more * buffers in the set, then we don't * want to deassert CS. */ if ((data->ctx.tx_count > 1) || (data->ctx.rx_count > 1)) { /* more buffers to transfer so * this isn't last */ last = false; } } data->status_flags = 0; ret = spi_mcux_dma_move_buffers(dev, dma_len, spi_cfg, last); if (ret != 0) { break; } ret = wait_dma_rx_tx_done(dev); if (ret != 0) { break; } /* wait until TX FIFO is really empty */ while (0U == (base->FIFOSTAT & SPI_FIFOSTAT_TXEMPTY_MASK)) { } spi_context_update_tx(&data->ctx, 1, dma_len); spi_context_update_rx(&data->ctx, 1, dma_len); } base->FIFOCFG &= ~SPI_FIFOCFG_DMATX_MASK; base->FIFOCFG &= ~SPI_FIFOCFG_DMARX_MASK; spi_context_cs_control(&data->ctx, false); out: spi_context_release(&data->ctx, ret); return ret; } #endif static int 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 asynchronous, spi_callback_t cb, void *userdata) { struct spi_mcux_data *data = dev->data; int ret; spi_context_lock(&data->ctx, asynchronous, cb, userdata, spi_cfg); ret = spi_mcux_configure(dev, spi_cfg); if (ret) { goto out; } spi_context_buffers_setup(&data->ctx, tx_bufs, rx_bufs, 1); spi_context_cs_control(&data->ctx, true); spi_mcux_transfer_next_packet(dev); ret = spi_context_wait_for_completion(&data->ctx); out: spi_context_release(&data->ctx, ret); return ret; } static int spi_mcux_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) { #ifdef CONFIG_SPI_MCUX_FLEXCOMM_DMA return transceive_dma(dev, spi_cfg, tx_bufs, rx_bufs, false, NULL, NULL); #endif return transceive(dev, spi_cfg, tx_bufs, rx_bufs, false, NULL, NULL); } #ifdef CONFIG_SPI_ASYNC static int spi_mcux_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) { #ifdef CONFIG_SPI_MCUX_FLEXCOMM_DMA return transceive_dma(dev, spi_cfg, tx_bufs, rx_bufs, true, cb, userdata); #endif return transceive(dev, spi_cfg, tx_bufs, rx_bufs, true, cb, userdata); } #endif /* CONFIG_SPI_ASYNC */ static int spi_mcux_release(const struct device *dev, const struct spi_config *spi_cfg) { struct spi_mcux_data *data = dev->data; spi_context_unlock_unconditionally(&data->ctx); return 0; } static int spi_mcux_init(const struct device *dev) { const struct spi_mcux_config *config = dev->config; struct spi_mcux_data *data = dev->data; int err = 0; if (!device_is_ready(config->reset.dev)) { LOG_ERR("Reset device not ready"); return -ENODEV; } err = reset_line_toggle(config->reset.dev, config->reset.id); if (err) { return err; } config->irq_config_func(dev); data->dev = dev; err = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT); if (err) { return err; } #ifdef CONFIG_SPI_MCUX_FLEXCOMM_DMA if (!device_is_ready(data->dma_tx.dma_dev)) { LOG_ERR("%s device is not ready", data->dma_tx.dma_dev->name); return -ENODEV; } if (!device_is_ready(data->dma_rx.dma_dev)) { LOG_ERR("%s device is not ready", data->dma_rx.dma_dev->name); return -ENODEV; } #endif /* CONFIG_SPI_MCUX_FLEXCOMM_DMA */ err = spi_context_cs_configure_all(&data->ctx); if (err < 0) { return err; } spi_context_unlock_unconditionally(&data->ctx); return 0; } static const struct spi_driver_api spi_mcux_driver_api = { .transceive = spi_mcux_transceive, #ifdef CONFIG_SPI_ASYNC .transceive_async = spi_mcux_transceive_async, #endif .release = spi_mcux_release, }; #define SPI_MCUX_FLEXCOMM_IRQ_HANDLER_DECL(id) \ static void spi_mcux_config_func_##id(const struct device *dev) #define SPI_MCUX_FLEXCOMM_IRQ_HANDLER_FUNC(id) \ .irq_config_func = spi_mcux_config_func_##id, #define SPI_MCUX_FLEXCOMM_IRQ_HANDLER(id) \ static void spi_mcux_config_func_##id(const struct device *dev) \ { \ IRQ_CONNECT(DT_INST_IRQN(id), \ DT_INST_IRQ(id, priority), \ spi_mcux_isr, DEVICE_DT_INST_GET(id), \ 0); \ irq_enable(DT_INST_IRQN(id)); \ } #ifndef CONFIG_SPI_MCUX_FLEXCOMM_DMA #define SPI_DMA_CHANNELS(id) #else #define SPI_DMA_CHANNELS(id) \ .dma_tx = { \ .dma_dev = DEVICE_DT_GET(DT_INST_DMAS_CTLR_BY_NAME(id, tx)), \ .channel = \ DT_INST_DMAS_CELL_BY_NAME(id, tx, channel), \ .dma_cfg = { \ .channel_direction = MEMORY_TO_PERIPHERAL, \ .dma_callback = spi_mcux_dma_callback, \ .complete_callback_en = true, \ .block_count = 2, \ } \ }, \ .dma_rx = { \ .dma_dev = DEVICE_DT_GET(DT_INST_DMAS_CTLR_BY_NAME(id, rx)), \ .channel = \ DT_INST_DMAS_CELL_BY_NAME(id, rx, channel), \ .dma_cfg = { \ .channel_direction = PERIPHERAL_TO_MEMORY, \ .dma_callback = spi_mcux_dma_callback, \ .block_count = 1, \ } \ } #endif #define SPI_MCUX_FLEXCOMM_DEVICE(id) \ SPI_MCUX_FLEXCOMM_IRQ_HANDLER_DECL(id); \ PINCTRL_DT_INST_DEFINE(id); \ static const struct spi_mcux_config spi_mcux_config_##id = { \ .base = \ (SPI_Type *)DT_INST_REG_ADDR(id), \ .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(id)), \ .clock_subsys = \ (clock_control_subsys_t)DT_INST_CLOCKS_CELL(id, name),\ SPI_MCUX_FLEXCOMM_IRQ_HANDLER_FUNC(id) \ .pre_delay = DT_INST_PROP_OR(id, pre_delay, 0), \ .post_delay = DT_INST_PROP_OR(id, post_delay, 0), \ .frame_delay = DT_INST_PROP_OR(id, frame_delay, 0), \ .transfer_delay = DT_INST_PROP_OR(id, transfer_delay, 0), \ .def_char = DT_INST_PROP_OR(id, def_char, 0), \ .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(id), \ .reset = RESET_DT_SPEC_INST_GET(id), \ }; \ static struct spi_mcux_data spi_mcux_data_##id = { \ SPI_CONTEXT_INIT_LOCK(spi_mcux_data_##id, ctx), \ SPI_CONTEXT_INIT_SYNC(spi_mcux_data_##id, ctx), \ SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(id), ctx) \ SPI_DMA_CHANNELS(id) \ }; \ DEVICE_DT_INST_DEFINE(id, \ &spi_mcux_init, \ NULL, \ &spi_mcux_data_##id, \ &spi_mcux_config_##id, \ POST_KERNEL, \ CONFIG_SPI_INIT_PRIORITY, \ &spi_mcux_driver_api); \ \ SPI_MCUX_FLEXCOMM_IRQ_HANDLER(id) DT_INST_FOREACH_STATUS_OKAY(SPI_MCUX_FLEXCOMM_DEVICE)