Skip to content

Commit ab9b9bf

Browse files
decsnydkalowsk
authored andcommitted
spi_nxp_lpspi: Add slave mode support
Add rudimentary slave mode support to the interrupt based LPSPI driver. Signed-off-by: Declan Snyder <declan.snyder@nxp.com>
1 parent 72b42e1 commit ab9b9bf

File tree

1 file changed

+63
-20
lines changed

1 file changed

+63
-20
lines changed

drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi.c

Lines changed: 63 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ LOG_MODULE_DECLARE(spi_lpspi, CONFIG_SPI_LOG_LEVEL);
1313

1414
struct lpspi_driver_data {
1515
uint8_t word_size_bytes;
16+
uint8_t lpspi_op_mode;
1617
};
1718

1819
static inline uint8_t rx_fifo_cur_len(LPSPI_Type *base)
@@ -192,10 +193,20 @@ static inline void lpspi_handle_tx_irq(const struct device *dev)
192193
{
193194
LPSPI_Type *base = (LPSPI_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base);
194195
struct lpspi_data *data = dev->data;
196+
struct lpspi_driver_data *lpspi_data = (struct lpspi_driver_data *)data->driver_data;
197+
uint8_t op_mode = lpspi_data->lpspi_op_mode;
195198
struct spi_context *ctx = &data->ctx;
199+
uint32_t status_flags = base->SR;
196200

197201
base->SR = LPSPI_SR_TDF_MASK;
198202

203+
if (op_mode == SPI_OP_MODE_SLAVE && (status_flags & LPSPI_SR_TEF_MASK)) {
204+
/* handling err051588 */
205+
base->SR = LPSPI_SR_TEF_MASK;
206+
/* workaround is to reset the transmit fifo before writing any new data */
207+
base->CR |= LPSPI_CR_RTF_MASK;
208+
}
209+
199210
/* If we receive a TX interrupt but no more data is available,
200211
* we can be sure that all data has been written to the fifo.
201212
* Disable the interrupt to signal that we are done.
@@ -219,8 +230,8 @@ static inline void lpspi_end_xfer(const struct device *dev)
219230
NVIC_ClearPendingIRQ(config->irqn);
220231
if (!(ctx->config->operation & SPI_HOLD_ON_CS)) {
221232
base->TCR &= ~(LPSPI_TCR_CONT_MASK | LPSPI_TCR_CONTC_MASK);
233+
/* don't need to wait for TCR since we are at end of xfer + in IRQ context */
222234
}
223-
lpspi_wait_tx_fifo_empty(dev);
224235
spi_context_cs_control(ctx, false);
225236
spi_context_release(&data->ctx, 0);
226237
}
@@ -232,6 +243,7 @@ static void lpspi_isr(const struct device *dev)
232243
struct lpspi_data *data = dev->data;
233244
struct lpspi_driver_data *lpspi_data = (struct lpspi_driver_data *)data->driver_data;
234245
uint8_t word_size_bytes = lpspi_data->word_size_bytes;
246+
uint8_t op_mode = lpspi_data->lpspi_op_mode;
235247
struct spi_context *ctx = &data->ctx;
236248
uint32_t status_flags = base->SR;
237249

@@ -253,9 +265,9 @@ static void lpspi_isr(const struct device *dev)
253265
}
254266

255267
/* the lpspi v1 has an errata where it doesn't clock the last bit
256-
* in continuous mode until you write the TCR
268+
* in continuous master mode until you write the TCR
257269
*/
258-
bool likely_stalling_v1 = data->major_version < 2 &&
270+
bool likely_stalling_v1 = (data->major_version < 2) && (op_mode == SPI_OP_MODE_MASTER) &&
259271
(DIV_ROUND_UP(spi_context_rx_len_left(ctx, word_size_bytes), word_size_bytes) == 1);
260272

261273
if (spi_context_rx_on(ctx)) {
@@ -313,14 +325,35 @@ static void lpspi_isr(const struct device *dev)
313325
}
314326
}
315327

328+
static void lpspi_master_setup_native_cs(const struct device *dev, const struct spi_config *spi_cfg)
329+
{
330+
LPSPI_Type *base = (LPSPI_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base);
331+
332+
/* keep the chip select asserted until the end of the zephyr xfer by using
333+
* continunous transfer mode. If SPI_HOLD_ON_CS is requested, we need
334+
* to also set CONTC in order to continue the previous command to keep CS
335+
* asserted.
336+
*/
337+
if (spi_cfg->operation & SPI_HOLD_ON_CS || base->TCR & LPSPI_TCR_CONTC_MASK) {
338+
base->TCR |= LPSPI_TCR_CONTC_MASK | LPSPI_TCR_CONT_MASK;
339+
} else {
340+
base->TCR |= LPSPI_TCR_CONT_MASK;
341+
}
342+
343+
/* tcr is written to tx fifo */
344+
lpspi_wait_tx_fifo_empty(dev);
345+
}
346+
316347
static int transceive(const struct device *dev, const struct spi_config *spi_cfg,
317348
const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs,
318349
bool asynchronous, spi_callback_t cb, void *userdata)
319350
{
320351
LPSPI_Type *base = (LPSPI_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base);
352+
const struct lpspi_config *config = dev->config;
321353
struct lpspi_data *data = dev->data;
322354
struct lpspi_driver_data *lpspi_data = (struct lpspi_driver_data *)data->driver_data;
323355
struct spi_context *ctx = &data->ctx;
356+
uint8_t op_mode = SPI_OP_MODE_GET(spi_cfg->operation);
324357
int ret = 0;
325358

326359
spi_context_lock(&data->ctx, asynchronous, cb, userdata, spi_cfg);
@@ -333,37 +366,47 @@ static int transceive(const struct device *dev, const struct spi_config *spi_cfg
333366
goto error;
334367
}
335368

369+
if (op_mode == SPI_OP_MODE_SLAVE && !(spi_cfg->operation & SPI_MODE_CPHA)) {
370+
LOG_ERR("CPHA=0 not supported with LPSPI peripheral mode");
371+
ret = -ENOTSUP;
372+
goto error;
373+
}
374+
336375
spi_context_buffers_setup(ctx, tx_bufs, rx_bufs, lpspi_data->word_size_bytes);
376+
lpspi_data->lpspi_op_mode = op_mode;
337377

338378
ret = lpspi_configure(dev, spi_cfg);
339379
if (ret) {
340380
goto error;
341381
}
342382

343-
base->CR |= LPSPI_CR_RTF_MASK | LPSPI_CR_RRF_MASK; /* flush fifos */
344-
base->IER = 0; /* disable all interrupts */
345-
base->FCR = 0; /* set watermarks to 0 */
383+
base->CR |= LPSPI_CR_RRF_MASK;
384+
base->IER = 0;
346385
base->SR |= LPSPI_INTERRUPT_BITS;
347386

348387
LOG_DBG("Starting LPSPI transfer");
349388
spi_context_cs_control(ctx, true);
350389

351-
base->CR |= LPSPI_CR_MEN_MASK;
352-
353-
/* keep the chip select asserted until the end of the zephyr xfer by using
354-
* continunous transfer mode. If SPI_HOLD_ON_CS is requested, we need
355-
* to also set CONTC in order to continue the previous command to keep CS
356-
* asserted.
357-
*/
358-
if (spi_cfg->operation & SPI_HOLD_ON_CS || base->TCR & LPSPI_TCR_CONTC_MASK) {
359-
base->TCR |= LPSPI_TCR_CONTC_MASK | LPSPI_TCR_CONT_MASK;
390+
if (op_mode == SPI_OP_MODE_MASTER) {
391+
/* set watermarks to 0 so get tx interrupt when fifo empty
392+
* and rx interrupt when any data received
393+
*/
394+
base->FCR = 0;
360395
} else {
361-
base->TCR |= LPSPI_TCR_CONT_MASK;
396+
/* set watermarks so that we are as responsive to master as possible and don't
397+
* miss any communication. This means RX interrupt at 0 so that if we ever
398+
* get any data, we get interrupt and handle immediately, and TX interrupt
399+
* to one less than the max of the fifo (-2 of size) so that we have as much
400+
* data ready to send to master as possible at any time
401+
*/
402+
base->FCR = LPSPI_FCR_TXWATER(config->tx_fifo_size - 1);
403+
base->CFGR1 |= LPSPI_CFGR1_AUTOPCS_MASK;
362404
}
363-
/* tcr is written to tx fifo */
364-
ret = lpspi_wait_tx_fifo_empty(dev);
365-
if (ret) {
366-
return ret;
405+
406+
base->CR |= LPSPI_CR_MEN_MASK;
407+
408+
if (op_mode == SPI_OP_MODE_MASTER) {
409+
lpspi_master_setup_native_cs(dev, spi_cfg);
367410
}
368411

369412
/* start the transfer sequence which are handled by irqs */

0 commit comments

Comments
 (0)