spi_nxp_lpspi: Support SPI_HOLD_ON_CS FLAG
Support SPI_HOLD_ON_CS flag in the CPU-based driver. To do this we will set CONTC bit to continue previous command. Technically it may not be necessary right now, and could just not clear CONT bit... but in the future in the lpspi driver we will decouple the config/init of a transfer from the SDK and therefore have more control over TCR, and when we write the TCR, we need to take CONTC bit into account otherwise a new command will be made. So this approach is how it should be handled in the driver going forward in my opinion, even if it might be possible without this bit right now, I want to introduce it's usage now. This commit also does a minor refactor in the ISR and adds some comments to make the strange CS behavior and strange handling code more clear to future readers. Also, make the early predicted SPI xfer end code only happen for spi versions where it is necessary, since I think that code is really the best we can do but might have a race condition, where possible the last word is not finished sending when we end the xfer. So limit the potential affect to v1 lpspi where the workaround is actually required due to stalling behavior. Lastly, set the LPSPI into master mode at active low in init, due to it being the most common case, we want the SPI CS lines to be initialized at init of driver. I don't think it's worth it to make it configurable at this time, but in the future it could be if needed. Signed-off-by: Declan Snyder <declan.snyder@nxp.com>
This commit is contained in:
parent
17ec70c9c1
commit
e71aa649b2
1 changed files with 42 additions and 10 deletions
|
@ -219,6 +219,23 @@ static inline void lpspi_handle_tx_irq(const struct device *dev)
|
|||
lpspi_next_tx_fill(data->dev);
|
||||
}
|
||||
|
||||
static inline void lpspi_end_xfer(const struct device *dev)
|
||||
{
|
||||
LPSPI_Type *base = (LPSPI_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base);
|
||||
const struct lpspi_config *config = dev->config;
|
||||
struct lpspi_data *data = dev->data;
|
||||
struct spi_context *ctx = &data->ctx;
|
||||
|
||||
spi_context_complete(ctx, dev, 0);
|
||||
NVIC_ClearPendingIRQ(config->irqn);
|
||||
if (!(ctx->config->operation & SPI_HOLD_ON_CS)) {
|
||||
base->TCR &= ~(LPSPI_TCR_CONT_MASK | LPSPI_TCR_CONTC_MASK);
|
||||
}
|
||||
lpspi_wait_tx_fifo_empty(dev);
|
||||
spi_context_cs_control(ctx, false);
|
||||
spi_context_release(&data->ctx, 0);
|
||||
}
|
||||
|
||||
static void lpspi_isr(const struct device *dev)
|
||||
{
|
||||
LPSPI_Type *base = (LPSPI_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base);
|
||||
|
@ -253,15 +270,13 @@ static void lpspi_isr(const struct device *dev)
|
|||
lpspi_data->fill_len = fill_len;
|
||||
}
|
||||
|
||||
if (spi_context_rx_len_left(ctx) == 1) {
|
||||
base->TCR &= ~LPSPI_TCR_CONT_MASK;
|
||||
if (spi_context_rx_len_left(ctx) == 1 && (LPSPI_VERID_MAJOR(base->VERID) < 2)) {
|
||||
/* Due to stalling behavior on older LPSPI,
|
||||
* need to end xfer in order to get last bit clocked out on bus.
|
||||
*/
|
||||
base->TCR |= LPSPI_TCR_CONT_MASK;
|
||||
} else if (spi_context_rx_len_left(ctx) == 0) {
|
||||
spi_context_complete(ctx, dev, 0);
|
||||
NVIC_ClearPendingIRQ(config->irqn);
|
||||
base->TCR &= ~LPSPI_TCR_CONT_MASK;
|
||||
lpspi_wait_tx_fifo_empty(dev);
|
||||
spi_context_cs_control(ctx, false);
|
||||
spi_context_release(&data->ctx, 0);
|
||||
lpspi_end_xfer(dev);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -302,8 +317,16 @@ static int transceive(const struct device *dev, const struct spi_config *spi_cfg
|
|||
|
||||
base->CR |= LPSPI_CR_MEN_MASK;
|
||||
|
||||
/* keep the chip select asserted until the end of the zephyr xfer */
|
||||
base->TCR |= LPSPI_TCR_CONT_MASK;
|
||||
/* keep the chip select asserted until the end of the zephyr xfer by using
|
||||
* continunous transfer mode. If SPI_HOLD_ON_CS is requested, we need
|
||||
* to also set CONTC in order to continue the previous command to keep CS
|
||||
* asserted.
|
||||
*/
|
||||
if (spi_cfg->operation & SPI_HOLD_ON_CS || base->TCR & LPSPI_TCR_CONTC_MASK) {
|
||||
base->TCR |= LPSPI_TCR_CONTC_MASK | LPSPI_TCR_CONT_MASK;
|
||||
} else {
|
||||
base->TCR |= LPSPI_TCR_CONT_MASK;
|
||||
}
|
||||
/* tcr is written to tx fifo */
|
||||
lpspi_wait_tx_fifo_empty(dev);
|
||||
|
||||
|
@ -352,6 +375,7 @@ static DEVICE_API(spi, lpspi_driver_api) = {
|
|||
|
||||
static int lpspi_init(const struct device *dev)
|
||||
{
|
||||
LPSPI_Type *base = (LPSPI_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base);
|
||||
struct lpspi_data *data = dev->data;
|
||||
int err = 0;
|
||||
|
||||
|
@ -360,6 +384,14 @@ static int lpspi_init(const struct device *dev)
|
|||
return err;
|
||||
}
|
||||
|
||||
/* Starting config should be master with active low CS, to make sure
|
||||
* the CS lines are configured properly at init for the most common use
|
||||
* cases. This can be changed later on transceive call if user specifies
|
||||
* different spi configuration.
|
||||
*/
|
||||
base->CFGR1 |= LPSPI_CFGR1_MASTER_MASK;
|
||||
base->CFGR1 &= ~LPSPI_CFGR1_PCSPOL_MASK;
|
||||
|
||||
spi_context_unlock_unconditionally(&data->ctx);
|
||||
|
||||
return 0;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue