From f0861ccda50495de472219e8ebbd3ec19d964233 Mon Sep 17 00:00:00 2001 From: Marcus Chang Date: Fri, 22 May 2015 17:30:08 +0100 Subject: [PATCH] SPI transfers larger than what the DMA can handle are split up in multiple transfers. --- .../TARGET_EFM32/spi_api.c | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/libraries/mbed/targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/spi_api.c b/libraries/mbed/targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/spi_api.c index bc1bc5738e..0a3dbd6e93 100644 --- a/libraries/mbed/targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/spi_api.c +++ b/libraries/mbed/targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/spi_api.c @@ -767,8 +767,26 @@ static void spi_activate_dma(spi_t *obj, void* rxdata, void* txdata, int tx_leng DMA_CfgDescr_TypeDef rxDescrCfg; DMA_CfgDescr_TypeDef txDescrCfg; + /* Split up transfers if the tx length is larger than what the DMA supports. */ + const int DMA_MAX_TRANSFER = (_DMA_CTRL_N_MINUS_1_MASK >> _DMA_CTRL_N_MINUS_1_SHIFT); + + if (tx_length > DMA_MAX_TRANSFER) + { + uint32_t max_length = DMA_MAX_TRANSFER; + + /* Make sure only an even amount of bytes are transferred + if the width is larger than 8 bits. */ + if (obj->spi.bits > 8) + { + max_length = DMA_MAX_TRANSFER - (DMA_MAX_TRANSFER & 0x01); + } + + /* Update length for current transfer. */ + tx_length = max_length; + } + /* Save amount of TX done by DMA */ - obj->tx_buff.pos = tx_length; + obj->tx_buff.pos += tx_length; if(obj->spi.bits != 9) { /* Only activate RX DMA if a receive buffer is specified */ @@ -966,6 +984,19 @@ uint32_t spi_irq_handler_asynch(spi_t* obj) if (obj->spi.dmaOptionsTX.dmaUsageState == DMA_USAGE_ALLOCATED || obj->spi.dmaOptionsTX.dmaUsageState == DMA_USAGE_TEMPORARY_ALLOCATED) { /* DMA implementation */ + /* If there is still data in the TX buffer, setup a new transfer. */ + if (obj->tx_buff.pos < obj->tx_buff.length) + { + /* Find position and remaining length without modifying tx_buff. */ + void* tx_pointer = obj->tx_buff.buffer + obj->tx_buff.pos; + uint32_t tx_length = obj->tx_buff.length - obj->tx_buff.pos; + + /* Begin transfer. Rely on spi_activate_dma to split up the transfer further. */ + spi_activate_dma(obj, obj->rx_buff.buffer, tx_pointer, tx_length, obj->rx_buff.length); + + return 0; + } + /* If there is an RX transfer ongoing, wait for it to finish */ if (DMA_ChannelEnabled(obj->spi.dmaOptionsRX.dmaChannel)) { /* Check if we need to kick off TX transfer again to force more incoming data. */