knx/examples/knx-cc1310/coresdk_cc13xx_cc26xx/source/ti/drivers/spi/SPICC26XXDMA.c
nanosonde d90843ba45
Add support for CC1310 platform based on SimpleLink SDK (#94)
* Initial commit

* Clean up

* Remove display code

* Change cmake build

* Add SimpleLink SDK for CC13xx/CC26xx as submodule

* Remove commented line from build.sh

* Working build

* Remove SDK submodule

* Squashed 'examples/knx-cc1310/coresdk_cc13xx_cc26xx/' content from commit 0d78d32

git-subtree-dir: examples/knx-cc1310/coresdk_cc13xx_cc26xx
git-subtree-split: 0d78d3280357416a5c0388148cda13717c9ffaa5

* Add more comments and enable Power_idleFunc() for NoRTOS variant.
Internal SDK driver functions which have to wait for something
will cause Power_idleFunc to be called instead of doing busy wait.

* Move CC1310 platform init around

* Optimize a bit more in debug build config as the binary does not fit into 128Kb flash otherwise.

* Explicitly list each source/header file in build config. Use linker group to resolve circular dependencies.

* Ignore vscode settings.json

* Increase stacks size

* Only compile CC1310 source code if #define DeviceFamily_CC13X0

* initial commit of CC1310 RF driver with first working RX version

* Better handling of buttonUp() across platforms

* Start cleanup

* continue cleanup

* Fix bau2920 compilation

* Continue cleanup

* Fix compilation in other examples

* Fix compilation

* htons() and ntohs() only for SAMD and STM32, but not for Linux and ESP8266 and ESP32

* htons(9 and ntohs() needed for CC13x0

* Continue cleanup

* Add CC1310 platform to CI

* Fix CI

* Use more recent toolchain from ARM

* Fix travis

* Use Ubuntu Focal

* Fix toolchain for travis

* Fix package name

* Fix toolchain

* Add libstdc++-dev package

* Add newlib packages

* Remove commented commands from CI

* Fix travis

* Fix compilation of knxPython

* Clean up linefeeds

* Fix RX callback

* Move RF CRC16-DNP to bits.cpp

* Fix TX

* Optimization: do not calc CRC for block1 again in rf_data_link_layer

* Make newline optional in printHex

* Cleanup. First working version: ETS5 programming of individual address via KNX/RF coupler.

* Use LEDs and Buttons to control ProgMode and Flash Erase

* Remove settings.json (VScode)

* Add README.md

* Update README.md

* Update README.md

* Fix typo
2020-11-10 21:52:38 +01:00

1197 lines
44 KiB
C

/*
* Copyright (c) 2015-2018, Texas Instruments Incorporated
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Texas Instruments Incorporated nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <ti/devices/DeviceFamily.h>
#include DeviceFamily_constructPath(inc/hw_memmap.h)
#include DeviceFamily_constructPath(inc/hw_ints.h)
#include DeviceFamily_constructPath(inc/hw_types.h)
#include DeviceFamily_constructPath(driverlib/ssi.h)
#include DeviceFamily_constructPath(driverlib/sys_ctrl.h)
#include DeviceFamily_constructPath(driverlib/udma.h)
#include DeviceFamily_constructPath(driverlib/ioc.h)
#include DeviceFamily_constructPath(driverlib/prcm.h)
#include DeviceFamily_constructPath(driverlib/rom.h)
#include <ti/drivers/dpl/DebugP.h>
#include <ti/drivers/dpl/HwiP.h>
#include <ti/drivers/dpl/SemaphoreP.h>
#include <ti/drivers/dpl/SwiP.h>
#include <ti/drivers/Power.h>
#include <ti/drivers/power/PowerCC26XX.h>
#include <ti/drivers/spi/SPICC26XXDMA.h>
#include <ti/drivers/dma/UDMACC26XX.h>
#define MAX_DMA_TRANSFER_AMOUNT (1024)
/* SPI test control register */
#define SSI_O_TCR 0x00000080
#define SSI_TCR_TESTFIFO_ENABLE 0x2
#define SSI_TCR_TESTFIFO_DISABLE 0x0
/* SPI test data register */
#define SSI_O_TDR 0x0000008C
/* Local typedef only. Used to easily migrate hwattrs versions */
typedef SPICC26XXDMA_HWAttrsV1 SPICC26XXDMA_HWAttrs;
/* Allocate space for DMA control table entries */
ALLOCATE_CONTROL_TABLE_ENTRY(dmaSpi0TxControlTableEntry, UDMA_CHAN_SSI0_TX);
ALLOCATE_CONTROL_TABLE_ENTRY(dmaSpi0RxControlTableEntry, UDMA_CHAN_SSI0_RX);
ALLOCATE_CONTROL_TABLE_ENTRY(dmaSpi1TxControlTableEntry, UDMA_CHAN_SSI1_TX);
ALLOCATE_CONTROL_TABLE_ENTRY(dmaSpi1RxControlTableEntry, UDMA_CHAN_SSI1_RX);
/* SPICC26XX functions */
void SPICC26XXDMA_close(SPI_Handle handle);
int_fast16_t SPICC26XXDMA_control(SPI_Handle handle, uint_fast16_t cmd, void *arg);
void SPICC26XXDMA_init(SPI_Handle handle);
SPI_Handle SPICC26XXDMA_open(SPI_Handle handle, SPI_Params *params);
bool SPICC26XXDMA_transfer(SPI_Handle handle, SPI_Transaction *transaction);
void SPICC26XXDMA_transferCancel(SPI_Handle handle);
/* SPICC26XX internal functions */
static void SPICC26XXDMA_transferCallback(SPI_Handle handle, SPI_Transaction *msg);
static void SPICC26XXDMA_csnCallback(PIN_Handle handle, PIN_Id pinId);
static void SPICC26XXDMA_initHw(SPI_Handle handle);
static bool SPICC26XXDMA_initIO(SPI_Handle handle);
static void SPICC26XXDMA_flushFifos(SPI_Handle handle);
/* Internal status macro */
static inline bool txFifoEmpty(SPICC26XXDMA_HWAttrs const *hwAttrs);
/* Internal power functions */
static int spiPostNotify(unsigned int eventType, uintptr_t eventArg, uintptr_t clientArg);
/* SPI function table for SPICC26XXDMA implementation */
const SPI_FxnTable SPICC26XXDMA_fxnTable = {
SPICC26XXDMA_close,
SPICC26XXDMA_control,
SPICC26XXDMA_init,
SPICC26XXDMA_open,
SPICC26XXDMA_transfer,
SPICC26XXDMA_transferCancel
};
/* Mapping SPI mode from generic driver to CC26XX driverlib */
static const uint32_t mode[] = {
SSI_MODE_MASTER, /* SPI_MASTER */
SSI_MODE_SLAVE /* SPI_SLAVE */
};
/* Mapping SPI frame format from generic driver to CC26XX driverlib */
static const uint32_t frameFormat[] = {
SSI_FRF_MOTO_MODE_0, /* SPI_POLO_PHA0 */
SSI_FRF_MOTO_MODE_1, /* SPI_POLO_PHA1 */
SSI_FRF_MOTO_MODE_2, /* SPI_POL1_PHA0 */
SSI_FRF_MOTO_MODE_3, /* SPI_POL1_PHA1 */
SSI_FRF_TI, /* SPI_TI */
SSI_FRF_NMW /* SPI_MW */
};
/*
* This lookup table is used to configure the DMA channels for the appropriate
* (8bit or 16bit) transfer sizes.
* Table for an SPI DMA TX channel
*/
static const unsigned long dmaTxConfig[] = {
UDMA_MODE_BASIC | UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE | UDMA_ARB_4, /* 8bit */
UDMA_MODE_BASIC | UDMA_SIZE_16 | UDMA_SRC_INC_16 | UDMA_DST_INC_NONE | UDMA_ARB_4 /* 16bit */
};
/*
* This lookup table is used to configure the DMA channels for the appropriate
* (8bit or 16bit) transfer sizes.
* Table for an SPI DMA RX channel
*/
static const unsigned long dmaRxConfig[] = {
UDMA_MODE_BASIC | UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 | UDMA_ARB_4, /* 8bit */
UDMA_MODE_BASIC | UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_4 /* 16bit */
};
/*
* This lookup table is used to configure the DMA channels for the appropriate
* (8bit or 16bit) transfer sizes when either txBuf or rxBuf are NULL
*/
static const uint32_t dmaNullConfig[] = {
UDMA_MODE_BASIC | UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_NONE | UDMA_ARB_4, /* 8bit */
UDMA_MODE_BASIC | UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_NONE | UDMA_ARB_4 /* 16bit */
};
/*
* Ensure safe setting of the standby disallow constraint.
*/
static inline void threadSafeConstraintSet(uint32_t txBufAddr, SPICC26XXDMA_Object *object) {
unsigned int key;
/* Disable interrupts */
key = HwiP_disable();
if (!object->spiPowerConstraint) {
/* Ensure flash is available if TX buffer is in flash. Flash starts with 0x0..*/
if (((txBufAddr & 0xF0000000) == 0x0) && (txBufAddr)) {
Power_setConstraint(PowerCC26XX_NEED_FLASH_IN_IDLE);
Power_setConstraint(PowerCC26XX_DISALLOW_XOSC_HF_SWITCHING);
}
/* Set constraints to guarantee operation */
Power_setConstraint(PowerCC26XX_DISALLOW_STANDBY);
object->spiPowerConstraint = true;
}
/* Re-enable interrupts */
HwiP_restore(key);
}
/*
* Ensure safe releasing of the standby disallow constraint.
*/
static inline void threadSafeConstraintRelease(uint32_t txBufAddr, SPICC26XXDMA_Object *object) {
unsigned int key;
/* Disable interrupts */
key = HwiP_disable();
if (object->spiPowerConstraint) {
/* Release need flash if buffer was in flash. */
if (((txBufAddr & 0xF0000000) == 0x0) && (txBufAddr)) {
Power_releaseConstraint(PowerCC26XX_DISALLOW_XOSC_HF_SWITCHING);
Power_releaseConstraint(PowerCC26XX_NEED_FLASH_IN_IDLE);
}
/* Release standby constraint since operation is done. */
Power_releaseConstraint(PowerCC26XX_DISALLOW_STANDBY);
object->spiPowerConstraint = false;
}
/* Re-enable interrupts */
HwiP_restore(key);
}
/*
* ======== spiPollingTransfer ========
*/
static inline void spiPollingTransfer(SPICC26XXDMA_Object *object,
SPICC26XXDMA_HWAttrsV1 const *hwAttrs, SPI_Transaction *transaction)
{
uint8_t increment;
uint32_t dummyBuffer;
size_t transferCount;
void *rxBuf;
void *txBuf;
if (transaction->rxBuf) {
rxBuf = transaction->rxBuf;
}
else {
rxBuf = &(object->scratchBuf);
}
if (transaction->txBuf) {
txBuf = transaction->txBuf;
}
else {
object->scratchBuf = hwAttrs->defaultTxBufValue;
txBuf = &(object->scratchBuf);
}
increment = (object->dataSize < 9) ? sizeof(uint8_t) : sizeof(uint16_t);
transferCount = transaction->count;
while (transferCount--) {
if (object->dataSize < 9) {
SSIDataPut(hwAttrs->baseAddr, *((uint8_t *) txBuf));
SSIDataGet(hwAttrs->baseAddr, &dummyBuffer);
*((uint8_t *) rxBuf) = (uint8_t) dummyBuffer;
}
else {
SSIDataPut(hwAttrs->baseAddr, *((uint16_t *) txBuf));
SSIDataGet(hwAttrs->baseAddr, &dummyBuffer);
*((uint16_t *) rxBuf) = (uint16_t) dummyBuffer;
}
/* Only increment source & destination if buffers were provided */
if (transaction->rxBuf) {
rxBuf = (void *) (((uint32_t) rxBuf) + increment);
}
if (transaction->txBuf) {
txBuf = (void *) (((uint32_t) txBuf) + increment);
}
}
while (!txFifoEmpty(hwAttrs)) {}
}
/*!
* @brief Function to close a given CC26XX SPI peripheral specified by the
* SPI handle.
*
* Will disable the SPI, disable all SPI interrupts and release the
* dependency on the corresponding power domain.
*
* @pre SPICC26XXDMA_open() has to be called first.
* Calling context: Task
*
* @param handle A SPI_Handle returned from SPI_open()
*
* @sa SPICC26XXDMA_open
*/
void SPICC26XXDMA_close(SPI_Handle handle)
{
SPICC26XXDMA_Object *object;
SPICC26XXDMA_HWAttrs const *hwAttrs;
/* Get the pointer to the object and hwAttrs */
hwAttrs = handle->hwAttrs;
object = handle->object;
/* Release the uDMA dependency and potentially power down uDMA. */
UDMACC26XX_close(object->udmaHandle);
/* Deallocate pins */
PIN_close(object->pinHandle);
/* Disable the SPI */
SSIDisable(hwAttrs->baseAddr);
/* Destroy the Hwi */
HwiP_destruct(&(object->hwi));
/* Destroy the Swi */
SwiP_destruct(&(object->swi));
/* Release power dependency on SPI. */
Power_releaseDependency(hwAttrs->powerMngrId);
if (object->transferMode == SPI_MODE_BLOCKING) {
SemaphoreP_destruct(&(object->transferComplete));
}
/* Unregister power notification objects */
Power_unregisterNotify(&object->spiPostObj);
/* Mark the module as available */
object->isOpen = false;
}
/*!
* @brief Function for setting control parameters of the SPI driver
* after it has been opened.
*
* @pre SPICC26XXDMA_open() has to be called first.
* Calling context: Hwi, Swi, Task
*
* @param handle A SPI handle returned from SPICC26XXDMA_open()
*
* @param cmd The command to execute, supported commands are:
* | Command | Description |
* |-------------------------------------- |-------------------------|
* | ::SPICC26XXDMA_CMD_RETURN_PARTIAL_ENABLE | Enable RETURN_PARTIAL |
* | ::SPICC26XXDMA_CMD_RETURN_PARTIAL_DISABLE | Disable RETURN_PARTIAL |
* | ::SPICC26XXDMA_CMD_SET_CSN_PIN | Re-configure chip select pin |
*
* @param *arg Pointer to command arguments.
*
* @return ::SPI_STATUS_SUCCESS if success, or error code if error.
*/
int_fast16_t SPICC26XXDMA_control(SPI_Handle handle, uint_fast16_t cmd, void *arg)
{
SPICC26XXDMA_Object *object;
SPICC26XXDMA_HWAttrs const *hwAttrs;
PIN_Config pinConfig;
PIN_Id pinId;
/* Get the pointer to the object and hwAttr */
hwAttrs = handle->hwAttrs;
object = handle->object;
/* Initialize return value*/
int ret = SPI_STATUS_ERROR;
/* Perform command */
switch(cmd) {
case SPICC26XXDMA_CMD_RETURN_PARTIAL_ENABLE:
/* Enable RETURN_PARTIAL if slave mode is enabled */
if(object->mode == SPI_SLAVE){
object->returnPartial = true;
ret = SPI_STATUS_SUCCESS;
}
else{
/* Partial return not available in master mode. */
ret = SPI_STATUS_ERROR;
}
break;
case SPICC26XXDMA_CMD_RETURN_PARTIAL_DISABLE:
/* Disable RETURN_PARTIAL */
object->returnPartial = false;
ret = SPI_STATUS_SUCCESS;
break;
case SPICC26XXDMA_CMD_SET_CSN_PIN:
pinId = ((*(PIN_Id *) arg));
/* Configure CSN pin and remap PIN_ID to new CSN pin specified by arg */
if (object->mode == SPI_SLAVE) {
pinConfig = PIN_INPUT_EN | PIN_PULLUP | pinId;
}
else {
pinConfig = PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_PUSHPULL | PIN_INPUT_DIS | PIN_DRVSTR_MED | pinId;
}
if (pinId != PIN_UNASSIGNED) {
/* Attempt to add the new pin */
if (PIN_add(object->pinHandle, pinConfig) == PIN_SUCCESS) {
/* Configure pin mux */
PINCC26XX_setMux(object->pinHandle, pinId, (hwAttrs->baseAddr == SSI0_BASE ? IOC_PORT_MCU_SSI0_FSS : IOC_PORT_MCU_SSI1_FSS));
/* Remove old pin and revert to default setting specified in the board file */
PIN_remove(object->pinHandle, object->csnPin);
/* Keep track of current CSN pin */
object->csnPin = pinId;
/* Set return value to indicate success */
ret = SPI_STATUS_SUCCESS;
}
}
else {
/* We want to use software ctrl CSN. Hence, undo any prior hardware CSN pin muxing */
/* Remove old pin and revert to default setting specified in the board file (implicitly sets IO muxing to GPIO mode) */
PIN_remove(object->pinHandle, object->csnPin);
/* Keep track of current CSN pin */
object->csnPin = pinId;
/* Set return value to indicate success */
ret = SPI_STATUS_SUCCESS;
}
break;
default:
/* This command is not defined */
ret = SPI_STATUS_UNDEFINEDCMD;
break;
}
return (ret);
}
/*
* ======== SPICC26XXDMA_configDMA ========
* This functions configures the transmit and receive DMA channels for a given
* SPI_Handle and SPI_Transaction
*
* @pre Function assumes that the handle and transaction is not NULL
*/
static void SPICC26XXDMA_configDMA(SPICC26XXDMA_Object *object,
SPICC26XXDMA_HWAttrs const *hwAttrs)
{
volatile tDMAControlTable *dmaControlTableEntry;
uint16_t numberOfBytes;
/*
* The DMA has a max transfer amount of 1024. If the transaction is
* greater; we must transfer it in chunks. object->amtDataXferred has
* how much data has already been sent.
*/
if ((object->currentTransaction->count - object->amtDataXferred) >
MAX_DMA_TRANSFER_AMOUNT) {
object->currentXferAmt = MAX_DMA_TRANSFER_AMOUNT;
}
else {
object->currentXferAmt = (object->currentTransaction->count -
object->amtDataXferred);
}
/* Calculate the number of bytes for the transfer */
numberOfBytes = ((uint16_t) object->currentXferAmt) << (object->frameSize);
/* Setup RX side */
/* Set pointer to Rx control table entry */
dmaControlTableEntry = (hwAttrs->baseAddr == SSI0_BASE ? &dmaSpi0RxControlTableEntry : &dmaSpi1RxControlTableEntry);
if (object->currentTransaction->rxBuf) {
dmaControlTableEntry->ui32Control = dmaRxConfig[object->frameSize];
/*
* Add an offset for the amount of data transfered. The offset is
* calculated by: object->amtDataXferred * (object->frameSize + 1).
* This accounts for 8 or 16-bit sized transfers.
*/
dmaControlTableEntry->pvDstEndAddr =
(void *)((uint32_t) object->currentTransaction->rxBuf +
((uint32_t) object->amtDataXferred * (object->frameSize + 1)) +
numberOfBytes - 1);
}
else {
dmaControlTableEntry->ui32Control = dmaNullConfig[object->frameSize];
dmaControlTableEntry->pvDstEndAddr = (void *) &(object->scratchBuf);
}
dmaControlTableEntry->pvSrcEndAddr = (void *)(hwAttrs->baseAddr + SSI_O_DR);
dmaControlTableEntry->ui32Control |= UDMACC26XX_SET_TRANSFER_SIZE((uint16_t) object->currentXferAmt);
/* Setup TX side */
/* Set pointer to Tx control table entry */
dmaControlTableEntry = (hwAttrs->baseAddr == SSI0_BASE ? &dmaSpi0TxControlTableEntry : &dmaSpi1TxControlTableEntry);
if (object->currentTransaction->txBuf) {
dmaControlTableEntry->ui32Control = dmaTxConfig[object->frameSize];
/*
* Add an offset for the amount of data transfered. The offset is
* calculated by: object->amtDataXferred * (object->frameSize + 1).
* This accounts for 8 or 16-bit sized transfers.
*/
dmaControlTableEntry->pvSrcEndAddr =
(void *)((uint32_t) object->currentTransaction->txBuf +
((uint32_t) object->amtDataXferred * (object->frameSize + 1)) +
numberOfBytes - 1);
}
else {
object->scratchBuf = hwAttrs->defaultTxBufValue;
dmaControlTableEntry->ui32Control = dmaNullConfig[object->frameSize];
dmaControlTableEntry->pvSrcEndAddr = (void *) &(object->scratchBuf);
}
dmaControlTableEntry->pvDstEndAddr = (void *)(hwAttrs->baseAddr + SSI_O_DR);
dmaControlTableEntry->ui32Control |= UDMACC26XX_SET_TRANSFER_SIZE((uint16_t) object->currentXferAmt);
/* Enable the channels */
UDMACC26XX_channelEnable(object->udmaHandle, (hwAttrs->rxChannelBitMask) | (hwAttrs->txChannelBitMask));
/* Enable the required DMA channels in the SPI module to start the transaction */
SSIDMAEnable(hwAttrs->baseAddr, SSI_DMA_TX | SSI_DMA_RX);
}
/*
* ======== SPICC26XXDMA_hwiFxn ========
* HWI ISR for the SPI when we use the UDMA
*/
static void SPICC26XXDMA_hwiFxn (uintptr_t arg) {
SPICC26XXDMA_Object *object;
SPICC26XXDMA_HWAttrs const *hwAttrs;
uint32_t intStatus;
/* Get the pointer to the object and hwAttrs */
object = ((SPI_Handle)arg)->object;
hwAttrs = ((SPI_Handle)arg)->hwAttrs;
/* Get the interrupt status of the SPI controller
*/
intStatus = SSIIntStatus(hwAttrs->baseAddr, true);
SSIIntClear(hwAttrs->baseAddr, intStatus);
/* Error handling:
* Overrun in the RX Fifo -> at least one sample in the shift
* register has been discarded */
if (intStatus & SSI_RXOR) {
/* disable the interrupt */
SSIIntDisable(hwAttrs->baseAddr, SSI_RXOR);
/* If the RX overrun occurred during a transfer */
if (object->currentTransaction) {
/* Then cancel the ongoing transfer */
SPICC26XXDMA_transferCancel((SPI_Handle)arg);
}
else {
/* Otherwise disable the SPI and DMA modules and flush FIFOs */
SSIDisable(hwAttrs->baseAddr);
/* Disable SPI TX/RX DMA and clear DMA done interrupt just in case it finished */
SSIDMADisable(hwAttrs->baseAddr, SSI_DMA_TX | SSI_DMA_RX);
UDMACC26XX_clearInterrupt(object->udmaHandle, (hwAttrs->rxChannelBitMask) | (hwAttrs->txChannelBitMask));
/* Clear out the FIFO by resetting SPI module and re-initting */
SPICC26XXDMA_flushFifos((SPI_Handle)arg);
}
}
else {
/*
* Determine if the TX DMA channel has completed... In SPI slave mode
* this interrupt may occur immediately (without the RX DMA channel).
*
* All transfers will set up both TX and RX DMA channels and both will finish.
* Even if the transaction->rxBuf == NULL, it will setup a dummy RX transfer to
* a scratch memory location which is then discarded.
*/
if (UDMACC26XX_channelDone(object->udmaHandle, hwAttrs->txChannelBitMask)) {
/* Disable SPI TX DMA and clear DMA done interrupt. */
SSIDMADisable(hwAttrs->baseAddr, SSI_DMA_TX);
UDMACC26XX_clearInterrupt(object->udmaHandle, hwAttrs->txChannelBitMask);
}
/*
* Determine if the RX DMA channel has completed... In slave mode this interrupt
* occurrence depends on when the SPI master starts sending data.
*/
if (UDMACC26XX_channelDone(object->udmaHandle, hwAttrs->rxChannelBitMask)) {
/* Disable SPI RX DMA and clear DMA done interrupt. */
SSIDMADisable(hwAttrs->baseAddr, SSI_DMA_RX);
UDMACC26XX_clearInterrupt(object->udmaHandle, hwAttrs->rxChannelBitMask);
/* Post SWI to handle remaining clean up and invocation of callback */
SwiP_post(&(object->swi));
}
}
}
/*
* ======== SPICC26XXDMA_swiFxn ========
* SWI ISR for the SPI when we use the UDMA
* Determine if the RX DMA channel has completed... In slave mode this interrupt
* occurrence depends on when the SPI master starts sending data.
*/
static void SPICC26XXDMA_swiFxn (uintptr_t arg0, uintptr_t arg1) {
SPI_Transaction *transaction;
SPICC26XXDMA_Object *object;
SPICC26XXDMA_HWAttrs const *hwAttrs;
/* Get the pointer to the object and hwAttrs */
object = ((SPI_Handle) arg0)->object;
hwAttrs = ((SPI_Handle) arg0)->hwAttrs;
/* Check if there is an active transaction */
if (object->currentTransaction == NULL) {
/* If the currentTransaction is NULL, this SWI was posted already by either the ISR or transferCancel.
* Do nothing and return.
* There is no need to disable interrupts and set up a temporary pointer for this fxn. The only place
* currentTransaction can be set after initialization is in this function and this swi cannot preempt
* itself to cause a race condition in the middle of it.
*/
return;
}
if (object->currentTransaction->count - object->amtDataXferred >
MAX_DMA_TRANSFER_AMOUNT) {
/* Data still remaining, configure another DMA transfer */
object->amtDataXferred += object->currentXferAmt;
SPICC26XXDMA_configDMA(object, hwAttrs);
}
else {
/* Transaction is complete */
if (object->currentTransaction->status == SPI_TRANSFER_STARTED) {
object->currentTransaction->status = SPI_TRANSFER_COMPLETED;
}
/* Use a temporary transaction pointer in case the callback function
* attempts to perform another SPI_transfer call
*/
transaction = object->currentTransaction;
/* Indicate we are done with this transfer */
object->currentTransaction = NULL;
/* Release constraint since transaction is done */
threadSafeConstraintRelease((uint32_t)(transaction->txBuf), object);
/* Perform callback */
object->transferCallbackFxn((SPI_Handle)arg0, transaction);
}
}
/*
* ======== SPICC26XXDMA_flushTxFifo ========
*/
void SPICC26XXDMA_flushFifos(SPI_Handle handle) {
/* Locals */
SPICC26XXDMA_HWAttrs const *hwAttrs;
/* Get the pointer to the hwAttrs */
hwAttrs = handle->hwAttrs;
/* Flush RX FIFO */
while(HWREG(hwAttrs->baseAddr + SSI_O_SR) & SSI_RX_NOT_EMPTY) {
/* Read element from RX FIFO and discard */
HWREG(hwAttrs->baseAddr + SSI_O_DR);
}
/* Enable TESTFIFO mode */
HWREG(hwAttrs->baseAddr + SSI_O_TCR) = SSI_TCR_TESTFIFO_ENABLE;
/* Flush TX FIFO */
while(!(HWREG(hwAttrs->baseAddr + SSI_O_SR) & SSI_TX_EMPTY)) {
/* Read element from TX FIFO and discard */
HWREG(hwAttrs->baseAddr + SSI_O_TDR);
}
/* Disable TESTFIFO mode */
HWREG(hwAttrs->baseAddr + SSI_O_TCR) = SSI_TCR_TESTFIFO_DISABLE;
}
/*!
* @brief SPI CC26XX initialization
*
* The function will set the isOpen flag to false.
*
* @pre Calling context: Hwi, Swi, Task, Main
*
* @param handle A SPI_Handle
*
*/
void SPICC26XXDMA_init(SPI_Handle handle)
{
SPICC26XXDMA_Object *object;
/* Get the pointer to the object */
object = handle->object;
/* Mark the object as available */
object->isOpen = false;
/* Init power constraint flag. */
object->spiPowerConstraint = false;
}
/*!
* @brief Function to initialize the CC26XX SPI peripheral specified by the
* particular handle. The parameter specifies which mode the SPI
* will operate.
*
* The function will set a dependency on it power domain, i.e. power up the
* module and enable the clock. The IOs are allocated. Neither the SPI nor UDMA module
* will be enabled.
*
* @pre SPI controller has been initialized.
* Calling context: Task
*
* @param handle A SPI_Handle
*
* @param params Pointer to a parameter block, if NULL it will use
* default values
*
* @return A SPI_Handle on success or a NULL on an error or if it has been
* already opened
*
* @sa SPICC26XXDMA_close()
*/
SPI_Handle SPICC26XXDMA_open(SPI_Handle handle, SPI_Params *params)
{
/* Use union to save on stack allocation */
union {
HwiP_Params hwiParams;
SwiP_Params swiParams;
} paramsUnion;
SPICC26XXDMA_Object *object;
SPICC26XXDMA_HWAttrs const *hwAttrs;
unsigned int key;
/* Get the pointer to the object and hwAttrs */
object = handle->object;
hwAttrs = handle->hwAttrs;
/* Disable preemption while checking if the SPI is open. */
key = HwiP_disable();
/* Check if the SPI is open already with the base addr. */
if (object->isOpen) {
HwiP_restore(key);
return (NULL);
}
/* Mark the handle as being used */
object->isOpen = true;
HwiP_restore(key);
DebugP_assert((params->dataSize >= 4) && (params->dataSize <= 16));
/* Initialize the SPI object */
object->currentTransaction = NULL;
object->bitRate = params->bitRate;
object->dataSize = params->dataSize;
object->frameFormat = params->frameFormat;
object->mode = params->mode;
object->transferMode = params->transferMode;
object->transferTimeout = params->transferTimeout;
object->returnPartial = false;
/* Determine if we need to use an 8-bit or 16-bit framesize for the DMA */
object->frameSize = (params->dataSize < 9) ? SPICC26XXDMA_8bit : SPICC26XXDMA_16bit;
/* Register power dependency - i.e. power up and enable clock for SPI. */
Power_setDependency(hwAttrs->powerMngrId);
/* Configure the hardware module */
SPICC26XXDMA_initHw(handle);
/* CSN is initialized using hwAttrs initially, but can be re-configured later */
object->csnPin = hwAttrs->csnPin;
/* Configure IOs after hardware has been initialized so that IOs aren't */
/* toggled unnecessary and make sure it was successful */
if (!SPICC26XXDMA_initIO(handle)) {
/* Trying to use SPI driver when some other driver or application
* has already allocated these pins, error! */
/* Release power dependency - i.e. potentially power down serial domain. */
Power_releaseDependency(hwAttrs->powerMngrId);
object->isOpen = false;
/* Signal back to application that SPI driver was not successfully opened */
return (NULL);
}
/* Create the Hwi for this SPI peripheral. */
HwiP_Params_init(&paramsUnion.hwiParams);
paramsUnion.hwiParams.arg = (uintptr_t) handle;
paramsUnion.hwiParams.priority = hwAttrs->intPriority;
HwiP_construct(&(object->hwi), (int) hwAttrs->intNum, SPICC26XXDMA_hwiFxn, &paramsUnion.hwiParams);
/* Create Swi object for this SPI peripheral */
SwiP_Params_init(&paramsUnion.swiParams);
paramsUnion.swiParams.arg0 = (uintptr_t)handle;
paramsUnion.swiParams.priority = hwAttrs->swiPriority;
SwiP_construct(&(object->swi), SPICC26XXDMA_swiFxn, &(paramsUnion.swiParams));
/* Declare the dependency on the UDMA driver */
object->udmaHandle = UDMACC26XX_open();
/* Configure PIN driver for CSN callback in optional RETURN_PARTIAL slave mode */
if (object->mode == SPI_SLAVE) {
PIN_registerIntCb(object->pinHandle, SPICC26XXDMA_csnCallback);
PIN_setUserArg(object->pinHandle, (uintptr_t) handle);
}
/* Register notification functions */
Power_registerNotify(&object->spiPostObj, PowerCC26XX_AWAKE_STANDBY, (Power_NotifyFxn)spiPostNotify, (uint32_t)handle);
/* Check the transfer mode */
if (object->transferMode == SPI_MODE_BLOCKING) {
/* Create a semaphore to block task execution for the duration of the
* SPI transfer */
SemaphoreP_constructBinary(&(object->transferComplete), 0);
/* Store internal callback function */
object->transferCallbackFxn = SPICC26XXDMA_transferCallback;
}
else {
/* Check to see if a callback function was defined for async mode */
DebugP_assert(params->transferCallbackFxn != NULL);
/* Save the callback function pointer */
object->transferCallbackFxn = params->transferCallbackFxn;
}
return (handle);
}
/*!
* @brief Function for transferring using the SPI interface.
*
* The function will enable the SPI and UDMA modules and disallow
* the device from going into standby.
*
* In ::SPI_MODE_BLOCKING, SPI_transfer will block task execution until the transfer
* has ended.
*
* In ::SPI_MODE_CALLBACK, SPI_transfer does not block task execution, but calls a
* callback function specified by transferCallback when the transfer has ended.
*
* @pre SPICC26XXDMA_open() has to be called first.
* Calling context: Hwi and Swi (only if using ::SPI_MODE_CALLBACK), Task
*
* @param handle A SPI handle returned from SPICC26XXDMA_open()
*
* @param *transaction Pointer to transaction struct
*
* @return True if transfer is successful and false if not
*
* @sa SPICC26XXDMA_open(), SPICC26XXDMA_transferCancel()
*/
bool SPICC26XXDMA_transfer(SPI_Handle handle, SPI_Transaction *transaction)
{
unsigned int key;
uint8_t alignMask;
bool buffersAligned;
SPICC26XXDMA_Object *object;
SPICC26XXDMA_HWAttrs const *hwAttrs;
/* Get the pointer to the object and hwAttr*/
object = handle->object;
hwAttrs = handle->hwAttrs;
if (transaction->count == 0 ||
(transaction->rxBuf == NULL && transaction->txBuf == NULL)) {
return (false);
}
/*
* Make sure that the buffers are aligned properly.
* alignMask is used to check if the RX/TX buffers addresses
* are aligned to the frameSize.
*/
alignMask = (object->frameSize == SPICC26XXDMA_16bit) ? 0x1 : 0x0;
buffersAligned = ((((uint32_t) transaction->rxBuf & alignMask) == 0) &&
(((uint32_t) transaction->txBuf & alignMask) == 0));
if (!buffersAligned) {
return (false);
}
/* Disable preemption while checking if a transfer is in progress */
key = HwiP_disable();
if (object->currentTransaction) {
HwiP_restore(key);
/* Flag that the transfer failed to start */
transaction->status = SPI_TRANSFER_FAILED;
/* Transfer is in progress */
return (false);
}
/* Make sure to flag that a transaction is now active */
transaction->status = SPI_TRANSFER_STARTED;
object->currentTransaction = transaction;
object->amtDataXferred = 0;
object->currentXferAmt = 0;
HwiP_restore(key);
/* In slave mode, optionally enable callback on CSN de-assert */
if (object->returnPartial) {
PIN_setInterrupt(object->pinHandle, object->csnPin | PIN_IRQ_POSEDGE);
}
/* Set constraints to guarantee transaction */
threadSafeConstraintSet((uint32_t)(transaction->txBuf), object);
/* Enable the SPI module */
SSIEnable(hwAttrs->baseAddr);
/*
* Polling transfer if BLOCKING mode & transaction->count < threshold
* Slaves not allowed to use polling unless timeout is disabled
*/
if (object->transferMode == SPI_MODE_BLOCKING &&
transaction->count < hwAttrs->minDmaTransferSize &&
(object->mode == SPI_MASTER ||
object->transferTimeout == SPI_WAIT_FOREVER)) {
HwiP_restore(key);
spiPollingTransfer(object, hwAttrs, transaction);
/* Disable the SPI */
SSIDisable(hwAttrs->baseAddr);
/* Release constraint since transaction is done */
threadSafeConstraintRelease((uint32_t)(transaction->txBuf), object);
/* Transaction completed; set status & mark SPI ready */
object->currentTransaction->status = SPI_TRANSFER_COMPLETED;
object->currentTransaction = NULL;
return (true);
}
/* Setup DMA transfer. */
SPICC26XXDMA_configDMA(object, hwAttrs);
/* Enable the RX overrun interrupt in the SSI module */
SSIIntEnable(hwAttrs->baseAddr, SSI_RXOR);
if (object->transferMode == SPI_MODE_BLOCKING) {
if (SemaphoreP_OK != SemaphoreP_pend(&(object->transferComplete),
object->transferTimeout)) {
/* Mark the transfer as failed. Otherwise SPICC26XXDMA_transferCancel will set it to canceled. */
if (object->currentTransaction->status == SPI_TRANSFER_STARTED) {
object->currentTransaction->status = SPI_TRANSFER_FAILED;
}
/* Cancel the transfer, if we experience a timeout */
SPICC26XXDMA_transferCancel(handle);
/*
* SPICC26XXDMA_transferCancel performs a callback which posts a
* transferComplete semaphore. This call consumes this extra post.
*/
SemaphoreP_pend(&(object->transferComplete), SemaphoreP_NO_WAIT);
return (false);
}
}
return (true);
}
/*!
* @brief Function that cancels a SPI transfer. Will disable SPI and UDMA modules
* and allow standby.
*
* @pre SPICC26XXDMA_open() has to be called first.
* Calling context: Task
*
* @param handle The SPI_Handle for ongoing transaction.
*/
void SPICC26XXDMA_transferCancel(SPI_Handle handle) {
SPICC26XXDMA_Object *object;
SPICC26XXDMA_HWAttrs const *hwAttrs;
SPI_Transaction *transaction;
volatile tDMAControlTable *dmaControlTableEntry;
/* Get the pointer to the object and hwAttrs */
object = handle->object;
hwAttrs = handle->hwAttrs;
/* Disable interrupts to ensure that this function is not interrupted. The calls to SPICC26XXDMA_transferCancel
* within the driver are safe. However, this function may be called by the application in callback mode.
* Hence, it might be preempted by the SPI hwi and swi or the PIN hwi and swi.
*/
uint32_t key = HwiP_disable();
/* Check if there is an active transaction */
if(object->currentTransaction == NULL) {
HwiP_restore(key);
return;
}
transaction = object->currentTransaction;
HwiP_restore(key);
/* Disable the SPI module */
SSIDisable(hwAttrs->baseAddr);
/* Disable SPI TX/RX DMA and clear DMA done interrupt just in case it finished */
SSIDMADisable(hwAttrs->baseAddr, SSI_DMA_TX | SSI_DMA_RX);
UDMACC26XX_clearInterrupt(object->udmaHandle, (hwAttrs->rxChannelBitMask) | (hwAttrs->txChannelBitMask));
/* Disable and clear any pending interrupts */
SSIIntDisable(hwAttrs->baseAddr, SSI_RXOR);
SSIIntClear(hwAttrs->baseAddr, SSI_RXOR);
/* Flush the FIFOs and re-initialize module */
SPICC26XXDMA_flushFifos(handle);
/* Release constraint since transaction is done */
threadSafeConstraintRelease((uint32_t)(transaction->txBuf), object);
/* Mark the transaction as failed if we didn't end up here due to a CSN deassertion */
if (transaction->status == SPI_TRANSFER_STARTED) {
transaction->status = SPI_TRANSFER_CANCELED;
}
/* Disable the UDMA channels */
UDMACC26XX_channelDisable(object->udmaHandle, (hwAttrs->rxChannelBitMask) | (hwAttrs->txChannelBitMask));
/* Update the SPI_Transaction.count parameter */
/* rxChannel always finishes after txChannel so remaining bytes of the rxChannel is used to update count */
dmaControlTableEntry = (hwAttrs->baseAddr == SSI0_BASE ? &dmaSpi0RxControlTableEntry : &dmaSpi1RxControlTableEntry);
transaction->count = object->amtDataXferred + (object->currentXferAmt -
UDMACC26XX_GET_TRANSFER_SIZE(dmaControlTableEntry->ui32Control));
/* Post SWI to handle remaining clean up and invocation of callback */
SwiP_post(&(object->swi));
}
/*
* ======== SPICC26XXDMA_transferCallback ========
* Callback function for when the SPI is in SPI_MODE_BLOCKING
*
* @pre Function assumes that the handle is not NULL
*/
static void SPICC26XXDMA_transferCallback(SPI_Handle handle, SPI_Transaction *msg)
{
SPICC26XXDMA_Object *object;
/* Get the pointer to the object */
object = handle->object;
/* Post the semaphore */
SemaphoreP_post(&(object->transferComplete));
}
/*
* ======== SPICC26XXDMA_csnDeassertCallback ========
* Slave mode optional callback function for when the CSN is deasserted
*
* @pre Function assumes that the handle is not NULL
*/
static void SPICC26XXDMA_csnCallback(PIN_Handle handle, PIN_Id pinId)
{
SPICC26XXDMA_Object *object;
SPI_Handle spiHandle;
PIN_Config csnConfig;
/* Get the pointer to the SPI object */
spiHandle = (SPI_Handle) PIN_getUserArg(handle);
object = spiHandle->object;
/* Get current CSN config */
csnConfig = PIN_getConfig(object->csnPin);
/* Disable all interrupts */
PIN_setInterrupt(handle, object->csnPin);
/* Cancel transfer if POSEDGE interrupt */
/* TODO: Consider doing this in a SWI */
if ((csnConfig & PIN_IRQ_POSEDGE) == PIN_IRQ_POSEDGE) {
/* Indicate why the transaction completed */
if ((object->currentTransaction != NULL) && (object->currentTransaction->status == SPI_TRANSFER_STARTED)) {
object->currentTransaction->status = SPI_TRANSFER_CSN_DEASSERT;
}
/* Cancel the current transaction */
SPICC26XXDMA_transferCancel(spiHandle);
}
}
/*
* ======== SPICC26XXDMA_hwInit ========
* This functions initializes the SPI hardware module.
*
* @pre Function assumes that the SPI handle is pointing to a hardware
* module which has already been opened.
*/
static void SPICC26XXDMA_initHw(SPI_Handle handle) {
SPICC26XXDMA_Object *object;
SPICC26XXDMA_HWAttrs const *hwAttrs;
ClockP_FreqHz freq;
/* Get the pointer to the object and hwAttrs */
object = handle->object;
hwAttrs = handle->hwAttrs;
/* Disable SSI operation */
SSIDisable(hwAttrs->baseAddr);
/* Disable SPI module interrupts */
SSIIntDisable(hwAttrs->baseAddr, SSI_RXOR | SSI_RXFF | SSI_RXTO | SSI_TXFF);
SSIIntClear(hwAttrs->baseAddr, SSI_RXOR | SSI_RXTO);
/* Set the SPI configuration */
ClockP_getCpuFreq(&freq);
SSIConfigSetExpClk(hwAttrs->baseAddr, freq.lo, frameFormat[object->frameFormat],
mode[object->mode], object->bitRate, object->dataSize);
}
/*
* ======== SPICC26XXDMA_hwInit ========
* This functions initializes the SPI IOs.
*
* @pre Function assumes that the SPI handle is pointing to a hardware
* module which has already been opened.
*/
static bool SPICC26XXDMA_initIO(SPI_Handle handle) {
SPICC26XXDMA_Object *object;
SPICC26XXDMA_HWAttrs const *hwAttrs;
PIN_Config spiPinTable[5];
uint32_t i = 0;
/* Get the pointer to the object and hwAttrs */
object = handle->object;
hwAttrs = handle->hwAttrs;
/* Configure IOs */
/* Build local list of pins, allocate through PIN driver and map HW ports */
if (object->mode == SPI_SLAVE) {
/* Configure IOs for slave mode */
spiPinTable[i++] = hwAttrs->mosiPin | PIN_INPUT_EN;
spiPinTable[i++] = hwAttrs->misoPin | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_INPUT_DIS | PIN_DRVSTR_MED;
spiPinTable[i++] = hwAttrs->clkPin | PIN_INPUT_EN;
spiPinTable[i++] = object->csnPin | PIN_INPUT_EN | PIN_PULLUP;
}
else {
/* Configure IOs for master mode */
spiPinTable[i++] = hwAttrs->mosiPin | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_INPUT_DIS | PIN_DRVSTR_MED;
spiPinTable[i++] = hwAttrs->misoPin | PIN_INPUT_EN | PIN_PULLDOWN;
/* Output low signal on SCLK until SPI module drives signal if clock polarity is configured to '0' */
/* Output high signal on SCLK until SPI module drives signal if clock polarity is configured to '1' */
if (object->frameFormat == SPI_POL0_PHA0 || object->frameFormat == SPI_POL0_PHA1) {
spiPinTable[i++] = hwAttrs->clkPin | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_INPUT_DIS | PIN_DRVSTR_MED;
}
else {
spiPinTable[i++] = hwAttrs->clkPin | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_PUSHPULL | PIN_INPUT_DIS | PIN_DRVSTR_MED;
}
/* If CSN isn't SW controlled, drive it high until SPI module drives signal to avoid glitches */
if(object->csnPin != PIN_UNASSIGNED) {
spiPinTable[i++] = object->csnPin | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_PUSHPULL | PIN_INPUT_DIS | PIN_DRVSTR_MED;
}
}
spiPinTable[i++] = PIN_TERMINATE;
/* Open and assign pins through pin driver */
if (!(object->pinHandle = PIN_open(&(object->pinState), spiPinTable))) {
return false;
}
/* Set IO muxing for the SPI pins */
if (mode[object->mode] == SSI_MODE_SLAVE) {
/* Configure IOs for slave mode */
PINCC26XX_setMux(object->pinHandle, hwAttrs->mosiPin, (hwAttrs->baseAddr == SSI0_BASE ? IOC_PORT_MCU_SSI0_RX : IOC_PORT_MCU_SSI1_RX));
PINCC26XX_setMux(object->pinHandle, hwAttrs->misoPin, (hwAttrs->baseAddr == SSI0_BASE ? IOC_PORT_MCU_SSI0_TX : IOC_PORT_MCU_SSI1_TX));
PINCC26XX_setMux(object->pinHandle, hwAttrs->clkPin, (hwAttrs->baseAddr == SSI0_BASE ? IOC_PORT_MCU_SSI0_CLK : IOC_PORT_MCU_SSI1_CLK));
PINCC26XX_setMux(object->pinHandle, object->csnPin, (hwAttrs->baseAddr == SSI0_BASE ? IOC_PORT_MCU_SSI0_FSS : IOC_PORT_MCU_SSI1_FSS));
}
else {
/* Configure IOs for master mode */
PINCC26XX_setMux(object->pinHandle, hwAttrs->mosiPin, (hwAttrs->baseAddr == SSI0_BASE ? IOC_PORT_MCU_SSI0_TX : IOC_PORT_MCU_SSI1_TX));
PINCC26XX_setMux(object->pinHandle, hwAttrs->misoPin, (hwAttrs->baseAddr == SSI0_BASE ? IOC_PORT_MCU_SSI0_RX : IOC_PORT_MCU_SSI1_RX));
PINCC26XX_setMux(object->pinHandle, hwAttrs->clkPin, (hwAttrs->baseAddr == SSI0_BASE ? IOC_PORT_MCU_SSI0_CLK : IOC_PORT_MCU_SSI1_CLK));
if(object->csnPin != PIN_UNASSIGNED) {
PINCC26XX_setMux(object->pinHandle, object->csnPin, (hwAttrs->baseAddr == SSI0_BASE ? IOC_PORT_MCU_SSI0_FSS : IOC_PORT_MCU_SSI1_FSS));
}
}
return true;
}
/*
* ======== spiPostNotify ========
* This functions is called to notify the SPI driver of an ongoing transition
* out of sleep mode.
*
* @pre Function assumes that the SPI handle (clientArg) is pointing to a
* hardware module which has already been opened.
*/
static int spiPostNotify(unsigned int eventType, uintptr_t eventArg, uintptr_t clientArg)
{
SPI_Handle spiHandle;
/* Get the pointers to SPI objects */
spiHandle = (SPI_Handle) clientArg;
/* Reconfigure the hardware when returning from standby */
SPICC26XXDMA_initHw(spiHandle);
return Power_NOTIFYDONE;
}
/*
* ======== txFifoEmpty ========
*/
static inline bool txFifoEmpty(SPICC26XXDMA_HWAttrs const *hwAttrs)
{
return(HWREG(hwAttrs->baseAddr + SSI_O_SR) & SSI_SR_TFE);
}