Skip to content

Commit 4956067

Browse files
jmartinez-silabspull[bot]
authored andcommitted
Rework rs9116 spi communication to use spidrv and clean up semaphore logic (#25834)
1 parent d1d85fb commit 4956067

File tree

1 file changed

+69
-170
lines changed
  • examples/platform/silabs/efr32/rs911x/hal

1 file changed

+69
-170
lines changed

examples/platform/silabs/efr32/rs911x/hal/efx_spi.c

+69-170
Original file line numberDiff line numberDiff line change
@@ -57,22 +57,21 @@
5757
#endif
5858

5959
StaticSemaphore_t xEfxSpiIntfSemaBuffer;
60-
static SemaphoreHandle_t spi_sem;
60+
static SemaphoreHandle_t spiTransferLock;
61+
static TaskHandle_t spiInitiatorTaskHandle = NULL;
6162

6263
#if defined(EFR32MG12)
6364
#include "sl_spidrv_exp_config.h"
6465
extern SPIDRV_Handle_t sl_spidrv_exp_handle;
65-
#endif
66-
67-
#if defined(EFR32MG24)
66+
#define SPI_HANDLE sl_spidrv_exp_handle
67+
#elif defined(EFR32MG24)
6868
#include "sl_spidrv_eusart_exp_config.h"
6969
extern SPIDRV_Handle_t sl_spidrv_eusart_exp_handle;
70+
#define SPI_HANDLE sl_spidrv_eusart_exp_handle
71+
#else
72+
#error "Unknown platform"
7073
#endif
7174

72-
static unsigned int tx_dma_channel;
73-
static unsigned int rx_dma_channel;
74-
75-
static uint32_t dummy_data; /* Used for DMA - when results don't matter */
7675
extern void rsi_gpio_irq_cb(uint8_t irqnum);
7776
//#define RS911X_USE_LDMA
7877

@@ -137,20 +136,8 @@ void sl_wfx_host_reset_chip(void)
137136
****************************************************************/
138137
void rsi_hal_board_init(void)
139138
{
140-
spi_sem = xSemaphoreCreateBinaryStatic(&xEfxSpiIntfSemaBuffer);
141-
xSemaphoreGive(spi_sem);
142-
143-
/* Assign DMA channel from Handle*/
144-
#if defined(EFR32MG12)
145-
/* MG12 + rs9116 combination uses USART driver */
146-
tx_dma_channel = sl_spidrv_exp_handle->txDMACh;
147-
rx_dma_channel = sl_spidrv_exp_handle->rxDMACh;
148-
149-
#elif defined(EFR32MG24)
150-
/* MG24 + rs9116 combination uses EUSART driver */
151-
tx_dma_channel = sl_spidrv_eusart_exp_handle->txDMACh;
152-
rx_dma_channel = sl_spidrv_eusart_exp_handle->rxDMACh;
153-
#endif
139+
spiTransferLock = xSemaphoreCreateBinaryStatic(&xEfxSpiIntfSemaBuffer);
140+
xSemaphoreGive(spiTransferLock);
154141

155142
/* GPIO INIT of MG12 & MG24 : Reset, Wakeup, Interrupt */
156143
WFX_RSI_LOG("RSI_HAL: init GPIO");
@@ -163,115 +150,24 @@ void rsi_hal_board_init(void)
163150
}
164151

165152
/*****************************************************************************
166-
*@fn static bool dma_complete_cb(unsigned int channel, unsigned int sequenceNo, void *userParam)
167-
*
168153
*@brief
169-
* DMA transfer completion callback. Called by the DMA interrupt handler.
170-
* This is being set in the tx/rx of the DMA
171-
*
172-
* @param[in] channel:
173-
* @param[in] sequenceNO: sequence number
174-
* @param[in] userParam :user parameter
154+
* Spi dma transfer is complete Callback
155+
* Notify the task that initiated the SPI transfer that it is completed.
156+
* The callback needs is a SPIDRV_Callback_t function pointer type
157+
* @param[in] pxHandle: spidrv instance handle
158+
* @param[in] transferStatus: Error code linked to the completed spi transfer. As master, the return code is irrelevant
159+
* @param[in] lCount: number of bytes transferred.
175160
*
176161
* @return
177162
* None
178163
******************************************************************************/
179-
static bool dma_complete_cb(unsigned int channel, unsigned int sequenceNo, void * userParam)
164+
static void spi_dmaTransfertComplete(SPIDRV_HandleData_t * pxHandle, Ecode_t transferStatus, int itemsTransferred)
180165
{
166+
configASSERT(spiInitiatorTaskHandle != NULL);
181167
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
182-
// uint8_t *buf = (void *)userParam;
183-
184-
(void) channel;
185-
(void) sequenceNo;
186-
(void) userParam;
187-
188-
// WFX_RSI_LOG ("SPI: DMA done [%x,%x,%x,%x]", buf [0], buf [1], buf [2], buf [3]);
189-
xSemaphoreGiveFromISR(spi_sem, &xHigherPriorityTaskWoken);
168+
vTaskNotifyGiveFromISR(spiInitiatorTaskHandle, &xHigherPriorityTaskWoken);
169+
spiInitiatorTaskHandle = NULL;
190170
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
191-
192-
#if defined(SL_CATALOG_POWER_MANAGER_PRESENT)
193-
sl_power_manager_remove_em_requirement(SL_POWER_MANAGER_EM1);
194-
#endif
195-
196-
return true;
197-
}
198-
199-
/*************************************************************
200-
* @fn static void receiveDMA(uint8_t *rx_buf, uint16_t xlen)
201-
* @brief
202-
* RX buf was specified
203-
* TX buf was not specified by caller - so we
204-
* transmit dummy data (typically 0)
205-
* @param[in] rx_buf:
206-
* @param[in] xlen:
207-
* @return
208-
* None
209-
*******************************************************************/
210-
static void receiveDMA(uint8_t * rx_buf, uint16_t xlen)
211-
{
212-
/*
213-
* The caller wants to receive data -
214-
* The xmit can be dummy data (no src increment for tx)
215-
*/
216-
dummy_data = 0;
217-
#if defined(SL_CATALOG_POWER_MANAGER_PRESENT)
218-
sl_power_manager_add_em_requirement(SL_POWER_MANAGER_EM1);
219-
#endif
220-
221-
// Start receive DMA
222-
DMADRV_PeripheralMemory(rx_dma_channel, MY_USART_RX_SIGNAL, (void *) rx_buf, (void *) &(MY_USART->RXDATA), true, xlen,
223-
dmadrvDataSize1, dma_complete_cb, NULL);
224-
225-
// Start transmit DMA.
226-
DMADRV_MemoryPeripheral(tx_dma_channel, MY_USART_TX_SIGNAL, (void *) &(MY_USART->TXDATA), (void *) &(dummy_data), false, xlen,
227-
dmadrvDataSize1, NULL, NULL);
228-
}
229-
230-
/*****************************************************************************
231-
*@fn static void transmitDMA(void *rx_buf, void *tx_buf, uint8_t xlen)
232-
*@brief
233-
* we have a tx_buf. There are some instances where
234-
* a rx_buf is not specifed. If one is specified then
235-
* the caller wants results (auto increment src)
236-
* @param[in] rx_buf:
237-
* @param[in] tx_buf:
238-
* @param[in] xlen:
239-
* @return
240-
* None
241-
******************************************************************************/
242-
static void transmitDMA(uint8_t * rx_buf, uint8_t * tx_buf, uint16_t xlen)
243-
{
244-
void * buf;
245-
bool srcinc;
246-
/*
247-
* we have a tx_buf. There are some instances where
248-
* a rx_buf is not specifed. If one is specified then
249-
* the caller wants results (auto increment src)
250-
* TODO - the caller specified 8/32 bit - we should use this
251-
* instead of dmadrvDataSize1 always
252-
*/
253-
if (rx_buf == NULL)
254-
{
255-
buf = &dummy_data;
256-
srcinc = false;
257-
}
258-
else
259-
{
260-
buf = rx_buf;
261-
srcinc = true;
262-
/* DEBUG */ rx_buf[0] = 0xAA;
263-
rx_buf[1] = 0x55;
264-
}
265-
#if defined(SL_CATALOG_POWER_MANAGER_PRESENT)
266-
sl_power_manager_add_em_requirement(SL_POWER_MANAGER_EM1);
267-
#endif
268-
269-
// Start receive DMA
270-
DMADRV_PeripheralMemory(rx_dma_channel, MY_USART_RX_SIGNAL, buf, (void *) &(MY_USART->RXDATA), srcinc, xlen, dmadrvDataSize1,
271-
dma_complete_cb, buf);
272-
// Start transmit DMA.
273-
DMADRV_MemoryPeripheral(tx_dma_channel, MY_USART_TX_SIGNAL, (void *) &(MY_USART->TXDATA), (void *) tx_buf, true, xlen,
274-
dmadrvDataSize1, NULL, NULL);
275171
}
276172

277173
/*********************************************************************
@@ -287,57 +183,60 @@ static void transmitDMA(uint8_t * rx_buf, uint8_t * tx_buf, uint16_t xlen)
287183
**************************************************************************/
288184
int16_t rsi_spi_transfer(uint8_t * tx_buf, uint8_t * rx_buf, uint16_t xlen, uint8_t mode)
289185
{
290-
// WFX_RSI_LOG ("SPI: Xfer: tx=%x,rx=%x,len=%d",(uint32_t)tx_buf, (uint32_t)rx_buf, xlen);
291-
if (xlen > MIN_XLEN)
186+
if (xlen <= MIN_XLEN || (tx_buf == NULL && rx_buf == NULL)) // at least one buffer needs to be provided
292187
{
293-
MY_USART->CMD = USART_CMD_CLEARRX | USART_CMD_CLEARTX;
294-
if (xSemaphoreTake(spi_sem, portMAX_DELAY) != pdTRUE)
295-
{
296-
return RSI_FALSE;
297-
}
298-
if (tx_buf == NULL)
299-
{
300-
receiveDMA(rx_buf, xlen);
301-
}
302-
else
303-
{
304-
transmitDMA(rx_buf, tx_buf, xlen);
305-
}
188+
return RSI_ERROR_INVALID_PARAM;
189+
}
306190

307-
/*
308-
* receiveDMA() and transmitDMA() are asynchronous
309-
* Our application design assumes that this function is synchronous
310-
* To make it synchronous, we wait to re-acquire the semaphore before exiting this function
311-
* dma_complete_cb() gives back the semaphore when the SPI transfer is done
312-
*/
313-
if (xSemaphoreTake(spi_sem, pdMS_TO_TICKS(RSI_SEM_BLOCK_MIN_TIMER_VALUE_MS)) == pdTRUE)
314-
{
315-
// Transfer complete
316-
// Give back the semaphore before exiting, so that it may be re-acquired
317-
// in this function, just before the next transfer
318-
xSemaphoreGive(spi_sem);
319-
}
320-
// Temporary patch
321-
// Sometimes the xSemaphoreTake() above is getting stuck indefinitely
322-
// As a workaround, if the transfer is not done within RSI_SEM_BLOCK_MIN_TIMER_VALUE_MS
323-
// stop and start it again
324-
// No need to re-acquire the semaphore since this is the function that acquired it
325-
// TODO: Remove this after a permanent solution is found to the problem of the transfer getting stuck
326-
else
191+
(void) mode; // currently not used;
192+
rsi_error_t rsiError = RSI_ERROR_NONE;
193+
194+
if (xSemaphoreTake(spiTransferLock, portMAX_DELAY) != pdTRUE)
195+
{
196+
return RSI_ERROR_SPI_BUSY;
197+
}
198+
199+
configASSERT(spiInitiatorTaskHandle == NULL); // No other task should currently be waiting for the dma completion
200+
spiInitiatorTaskHandle = xTaskGetCurrentTaskHandle();
201+
202+
Ecode_t spiError;
203+
if (tx_buf == NULL) // Rx operation only
204+
{
205+
spiError = SPIDRV_MReceive(SPI_HANDLE, rx_buf, xlen, spi_dmaTransfertComplete);
206+
}
207+
else if (rx_buf == NULL) // Tx operation only
208+
{
209+
spiError = SPIDRV_MTransmit(SPI_HANDLE, tx_buf, xlen, spi_dmaTransfertComplete);
210+
}
211+
else // Tx and Rx operation
212+
{
213+
spiError = SPIDRV_MTransfer(SPI_HANDLE, tx_buf, rx_buf, xlen, spi_dmaTransfertComplete);
214+
}
215+
216+
if (spiError == ECODE_EMDRV_SPIDRV_OK)
217+
{
218+
// rsi implementation expect a synchronous operation
219+
// wait for the notification that the dma completed in a block state.
220+
// it does not consume any CPU time.
221+
if (ulTaskNotifyTake(pdTRUE, RSI_SEM_BLOCK_MIN_TIMER_VALUE_MS) != pdPASS)
327222
{
328-
uint32_t ldma_flags = 0;
329-
uint32_t rem_len = 0;
330-
rem_len = LDMA_TransferRemainingCount(RSI_LDMA_TRANSFER_CHANNEL_NUM);
331-
LDMA_StopTransfer(RSI_LDMA_TRANSFER_CHANNEL_NUM);
332-
ldma_flags = LDMA_IntGet();
333-
LDMA_IntClear(ldma_flags);
334-
receiveDMA(rx_buf, rem_len);
335-
if (xSemaphoreTake(spi_sem, portMAX_DELAY) == pdTRUE)
336-
{
337-
xSemaphoreGive(spi_sem);
338-
}
223+
int itemsTransferred = 0;
224+
int itemsRemaining = 0;
225+
SPIDRV_GetTransferStatus(SPI_HANDLE, &itemsTransferred, &itemsRemaining);
226+
WFX_RSI_LOG("SPI transfert timed out %d/%d (rx%x rx%x)", itemsTransferred, itemsRemaining, (uint32_t) tx_buf,
227+
(uint32_t) rx_buf);
228+
229+
SPIDRV_AbortTransfer(SPI_HANDLE);
230+
rsiError = RSI_ERROR_SPI_TIMEOUT;
339231
}
340232
}
233+
else
234+
{
235+
WFX_RSI_LOG("SPI transfert failed with err:%x (tx%x rx%x)", spiError, (uint32_t) tx_buf, (uint32_t) rx_buf);
236+
rsiError = RSI_ERROR_SPI_FAIL;
237+
spiInitiatorTaskHandle = NULL; // SPI operation failed. No notification to received.
238+
}
341239

342-
return RSI_ERROR_NONE;
240+
xSemaphoreGive(spiTransferLock);
241+
return rsiError;
343242
}

0 commit comments

Comments
 (0)