knx/examples/knx-cc1310/coresdk_cc13xx_cc26xx/source/ti/drivers/spi/SPICC26X2DMA.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

1557 lines
56 KiB
C

/*
* Copyright (c) 2015-2019, 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 <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#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/dma/UDMACC26XX.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.h>
#include <ti/drivers/spi/SPICC26X2DMA.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)
#define PARAMS_DATASIZE_MIN (4)
#define PARAMS_DATASIZE_MAX (16)
/* 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);
ALLOCATE_CONTROL_TABLE_ENTRY(dmaSpi0TxAltControlTableEntry,
(UDMA_CHAN_SSI0_TX | UDMA_ALT_SELECT));
ALLOCATE_CONTROL_TABLE_ENTRY(dmaSpi0RxAltControlTableEntry,
(UDMA_CHAN_SSI0_RX | UDMA_ALT_SELECT));
ALLOCATE_CONTROL_TABLE_ENTRY(dmaSpi1TxAltControlTableEntry,
(UDMA_CHAN_SSI1_TX | UDMA_ALT_SELECT));
ALLOCATE_CONTROL_TABLE_ENTRY(dmaSpi1RxAltControlTableEntry,
(UDMA_CHAN_SSI1_RX | UDMA_ALT_SELECT));
/* API Function Prototypes */
void SPICC26X2DMA_close(SPI_Handle handle);
int_fast16_t SPICC26X2DMA_control(SPI_Handle handle,
uint_fast16_t cmd,
void *arg);
void SPICC26X2DMA_init(SPI_Handle handle);
SPI_Handle SPICC26X2DMA_open(SPI_Handle handle, SPI_Params *params);
static void SPICC26X2DMA_swiFxn (uintptr_t arg0, uintptr_t arg1);
bool SPICC26X2DMA_transfer(SPI_Handle handle, SPI_Transaction *transaction);
void SPICC26X2DMA_transferCancel(SPI_Handle handle);
/* Local Function Prototypes */
static void blockingTransferCallback(SPI_Handle handle,
SPI_Transaction *msg);
static void configNextTransfer(SPICC26X2DMA_Object *object,
SPICC26X2DMA_HWAttrs const *hwAttrs);
static void csnCallback(PIN_Handle handle, PIN_Id pinId);
static void flushFifos(SPICC26X2DMA_HWAttrs const *hwAttrs);
static inline uint32_t getDmaChannelNumber(uint32_t x);
static void initHw(SPI_Handle handle);
static bool initIO(SPI_Handle handle);
static inline void primeTransfer(SPICC26X2DMA_Object *object,
SPICC26X2DMA_HWAttrs const *hwAttrs);
static inline void releaseConstraint(uint32_t txBufAddr);
static inline void setConstraint(uint32_t txBufAddr);
static inline void spiPollingTransfer(SPICC26X2DMA_Object *object,
SPICC26X2DMA_HWAttrs const *hwAttrs,
SPI_Transaction *transaction);
static int spiPostNotify(unsigned int eventType,
uintptr_t eventArg,
uintptr_t clientArg);
static inline bool spiBusy(SPICC26X2DMA_Object *object,
SPICC26X2DMA_HWAttrs const *hwAttrs);
/* SPI function table for SPICC26X2DMA implementation */
const SPI_FxnTable SPICC26X2DMA_fxnTable = {
SPICC26X2DMA_close,
SPICC26X2DMA_control,
SPICC26X2DMA_init,
SPICC26X2DMA_open,
SPICC26X2DMA_transfer,
SPICC26X2DMA_transferCancel
};
/* 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 */
};
/*
* These lookup tables are used to configure the DMA channels for the
* appropriate (8bit or 16bit) transfer sizes.
*/
static const uint32_t dmaTxConfig[] = {
UDMA_MODE_PINGPONG | UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE |
UDMA_ARB_4,
UDMA_MODE_PINGPONG | UDMA_SIZE_16 | UDMA_SRC_INC_16 | UDMA_DST_INC_NONE |
UDMA_ARB_4
};
static const uint32_t dmaRxConfig[] = {
UDMA_MODE_PINGPONG | UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 |
UDMA_ARB_4,
UDMA_MODE_PINGPONG | UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 |
UDMA_ARB_4
};
static const uint32_t dmaNullConfig[] = {
UDMA_MODE_PINGPONG | UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_NONE |
UDMA_ARB_4,
UDMA_MODE_PINGPONG | UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_NONE |
UDMA_ARB_4
};
/*
* ======== SPICC26X2DMA_close ========
*/
void SPICC26X2DMA_close(SPI_Handle handle)
{
SPICC26X2DMA_Object *object = handle->object;
SPICC26X2DMA_HWAttrs const *hwAttrs = handle->hwAttrs;
SSIDisable(hwAttrs->baseAddr);
HwiP_destruct(&(object->hwi));
UDMACC26XX_close(object->udmaHandle);
SwiP_destruct(&(object->swi));
if (object->transferMode == SPI_MODE_BLOCKING) {
SemaphoreP_destruct(&(object->transferComplete));
}
PIN_close(object->pinHandle);
Power_releaseDependency(hwAttrs->powerMngrId);
Power_unregisterNotify(&object->spiPostObj);
object->isOpen = false;
}
/*!
* @brief Function for setting control parameters of the SPI driver
* after it has been opened.
*
* @pre SPICC26X2DMA_open() has to be called first.
* Calling context: Hwi, Swi, Task
*
* @param handle A SPI handle returned from SPICC26X2DMA_open()
*
* @param cmd The command to execute, supported commands are:
* | Command | Description |
* |-------------------------------------------|------------------------------|
* | ::SPICC26X2DMA_CMD_RETURN_PARTIAL_ENABLE | Enable RETURN_PARTIAL |
* | ::SPICC26X2DMA_CMD_RETURN_PARTIAL_DISABLE | Disable RETURN_PARTIAL |
* | ::SPICC26X2DMA_CMD_SET_CSN_PIN | Re-configure chip select pin |
* | ::SPICC26X2DMA_CMD_SET_MANUAL | Enable manual start mode |
* | ::SPICC26X2DMA_CMD_CLR_MANUAL | Disable manual start mode |
* | ::SPICC26X2DMA_CMD_MANUAL_START | Perform a manual start |
*
* @param *arg Pointer to command arguments.
*
* @return ::SPI_STATUS_SUCCESS if success, or error code if error.
*/
int_fast16_t SPICC26X2DMA_control(SPI_Handle handle,
uint_fast16_t cmd,
void *arg)
{
SPICC26X2DMA_Object *object = handle->object;
SPICC26X2DMA_HWAttrs const *hwAttrs = handle->hwAttrs;
PIN_Config pinConfig;
PIN_Id pinId;
/* Initialize return value*/
int ret = SPI_STATUS_ERROR;
/* Perform command */
switch(cmd) {
case SPICC26X2DMA_CMD_RETURN_PARTIAL_ENABLE:
/* Enable RETURN_PARTIAL if slave mode is enabled */
if(object->mode == SPI_SLAVE){
object->returnPartial = SPICC26X2DMA_retPartEnabledIntNotSet;
ret = SPI_STATUS_SUCCESS;
}
else{
/* Partial return not available in master mode. */
ret = SPI_STATUS_ERROR;
}
break;
case SPICC26X2DMA_CMD_RETURN_PARTIAL_DISABLE:
/* Disable RETURN_PARTIAL */
PIN_setInterrupt(object->pinHandle, object->csnPin);
object->returnPartial = SPICC26X2DMA_retPartDisabled;
ret = SPI_STATUS_SUCCESS;
break;
case SPICC26X2DMA_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;
case SPICC26X2DMA_CMD_SET_MANUAL:
/* If a transaction is queued, do not modify */
if (object->headPtr == NULL) {
object->manualStart = true;
ret = SPI_STATUS_SUCCESS;
}
break;
case SPICC26X2DMA_CMD_CLR_MANUAL:
/* If a transaction is queued, do not modify */
if (object->headPtr == NULL) {
object->manualStart = false;
ret = SPI_STATUS_SUCCESS;
}
break;
case SPICC26X2DMA_CMD_MANUAL_START:
if (object->headPtr != NULL &&
object->manualStart) {
SSIDMAEnable(hwAttrs->baseAddr, SSI_DMA_TX | SSI_DMA_RX);
UDMACC26XX_channelEnable(object->udmaHandle,
hwAttrs->rxChannelBitMask |
hwAttrs->txChannelBitMask);
SSIEnable(hwAttrs->baseAddr);
ret = SPI_STATUS_SUCCESS;
}
break;
default:
/* This command is not defined */
ret = SPI_STATUS_UNDEFINEDCMD;
break;
}
return (ret);
}
/*
* ======== SPICC26X2DMA_hwiFxn ========
*/
static void SPICC26X2DMA_hwiFxn (uintptr_t arg)
{
uint32_t freeChannel;
uint32_t intStatus;
uintptr_t key;
SPI_Transaction *completedList;
size_t *transferSize;
volatile tDMAControlTable *rxDmaTableEntry;
volatile tDMAControlTable *txDmaTableEntry;
SPICC26X2DMA_Object *object = ((SPI_Handle) arg)->object;
SPICC26X2DMA_HWAttrs const *hwAttrs = ((SPI_Handle) arg)->hwAttrs;
uint8_t i;
intStatus = SSIIntStatus(hwAttrs->baseAddr, true);
SSIIntClear(hwAttrs->baseAddr, intStatus);
if (intStatus & SSI_RXOR) {
key = HwiP_disable();
if (object->headPtr != NULL) {
/*
* RX overrun during a transfer; mark the current transfer
* as failed & cancel all remaining transfers.
*/
object->headPtr->status = SPI_TRANSFER_FAILED;
HwiP_restore(key);
SPICC26X2DMA_transferCancel((SPI_Handle) arg);
}
else {
SSIDisable(hwAttrs->baseAddr);
/* Disable DMA and clear DMA interrupts */
SSIDMADisable(hwAttrs->baseAddr, SSI_DMA_TX | SSI_DMA_RX);
UDMACC26XX_channelDisable(object->udmaHandle,
hwAttrs->rxChannelBitMask |
hwAttrs->txChannelBitMask);
UDMACC26XX_clearInterrupt(object->udmaHandle,
hwAttrs->rxChannelBitMask |
hwAttrs->txChannelBitMask);
SSIIntDisable(hwAttrs->baseAddr, SSI_RXOR);
SSIIntClear(hwAttrs->baseAddr, SSI_RXOR);
/* Clear out the FIFO by resetting SPI module and re-initting */
flushFifos(hwAttrs);
HwiP_restore(key);
}
}
else {
UDMACC26XX_clearInterrupt(object->udmaHandle,
hwAttrs->rxChannelBitMask |
hwAttrs->txChannelBitMask);
/*
* We check both channels for completion; this is done in case the
* second channel finishes while we are still configuring the first.
*/
for (i = 0; i < 2; i++) {
if (object->headPtr == NULL){
/* When i was 0, we finished the last transaction */
break;
}
if (object->activeChannel == UDMA_PRI_SELECT) {
transferSize = &object->priTransferSize;
rxDmaTableEntry = (hwAttrs->baseAddr == SSI0_BASE) ?
&dmaSpi0RxControlTableEntry :
&dmaSpi1RxControlTableEntry;
txDmaTableEntry = (hwAttrs->baseAddr == SSI0_BASE) ?
&dmaSpi0TxControlTableEntry :
&dmaSpi1TxControlTableEntry;
}
else {
transferSize = &object->altTransferSize;
rxDmaTableEntry = (hwAttrs->baseAddr == SSI0_BASE) ?
&dmaSpi0RxAltControlTableEntry :
&dmaSpi1RxAltControlTableEntry;
txDmaTableEntry = (hwAttrs->baseAddr == SSI0_BASE) ?
&dmaSpi0TxAltControlTableEntry :
&dmaSpi1TxAltControlTableEntry;
}
/*
* The SPI TX FIFO continuously requests the DMA to fill it if there
* is space available. If there are no more frames to put in the
* FIFO we run into a situation where DMA TX will cause undesired
* interrupts. To prevent many undesired interrupts disable DMA_TX
* uf there are no more frames to load into the FIFO & there are no
* pending queued transactions.
*/
if (UDMACC26XX_channelDone(object->udmaHandle, hwAttrs->txChannelBitMask) &&
(txDmaTableEntry->ui32Control & UDMA_MODE_M) == UDMA_MODE_STOP &&
object->framesQueued == object->headPtr->count &&
object->headPtr->nextPtr == NULL){
SSIDMADisable(hwAttrs->baseAddr, SSI_DMA_TX);
UDMACC26XX_clearInterrupt(object->udmaHandle,
hwAttrs->txChannelBitMask);
}
if((rxDmaTableEntry->ui32Control & UDMA_MODE_M) == UDMA_MODE_STOP &&
*transferSize != 0) {
key = HwiP_disable();
object->framesTransferred += *transferSize;
freeChannel = object->activeChannel;
object->activeChannel = (freeChannel == UDMA_PRI_SELECT) ?
UDMA_ALT_SELECT : UDMA_PRI_SELECT;
/*
* Set the channel's transfer size to 0; 0 lets
* configNextTransfer() know that there is a free channel.
*/
*transferSize = 0;
if ((object->framesQueued) < (object->headPtr->count) ||
(object->framesTransferred) < (object->headPtr->count)) {
/*
* In this case we need to reconfigure the channel to
* continue transferring frames. configNextTransfer() will
* continue queuing frames for the current transfer or
* start the following transaction if necessary.
*/
configNextTransfer(object, hwAttrs);
if (object->manualStart &&
UDMACC26XX_channelDone(object->udmaHandle, hwAttrs->txChannelBitMask)) {
/* Ping pong flow was broken, restart */
UDMACC26XX_channelEnable(object->udmaHandle,
hwAttrs->txChannelBitMask);
}
HwiP_restore(key);
}
else {
/*
* All data has been transferred for the current
* transaction. Set status & move the transaction to
* object->completedList. This is required because
* object->headPtr is moved to the following transaction.
* Also, transaction callbacks are executed in the driver
* SWI which will be posted later.
*/
object->headPtr->status = SPI_TRANSFER_COMPLETED;
if (object->completedTransfers == NULL) {
/* List is empty; just add the transaction */
object->completedTransfers = object->headPtr;
completedList = object->completedTransfers;
}
else {
/* Traverse to the last element */
completedList = object->completedTransfers;
while (completedList->nextPtr != NULL) {
completedList = completedList->nextPtr;
}
/* Store the completed transaction at end of list */
completedList->nextPtr = object->headPtr;
/*
* Make sure we are pointing to the end of the list;
* we need to clear references in completed transfer
* after we move object->headPtr forward.
*/
completedList = completedList->nextPtr;
}
/* Move the object->headPtr to the next transaction */
object->headPtr = object->headPtr->nextPtr;
/* Clear references in completed transfer */
completedList->nextPtr = NULL;
/* Update object variables for the following transfer. */
object->framesQueued =
(object->activeChannel == UDMA_PRI_SELECT) ?
object->priTransferSize : object->altTransferSize;
object->framesTransferred = 0;
if (object->headPtr != NULL) {
/* Reconfigure channel for following transaction */
configNextTransfer(object, hwAttrs);
if (object->manualStart &&
UDMACC26XX_channelDone(object->udmaHandle, hwAttrs->txChannelBitMask)) {
/* Ping pong flow was broken, restart */
UDMACC26XX_channelEnable(object->udmaHandle,
hwAttrs->txChannelBitMask);
}
}
else {
/* No more queued transfers; disable DMA & SPI */
SSIDMADisable(hwAttrs->baseAddr,
SSI_DMA_TX |
SSI_DMA_RX);
/*
* For this driver implementation the peripheral is kept
* active until either a FIFO-overrun occurs or
* SPI_transferCancel() is executed.
*/
}
HwiP_restore(key);
/* Post driver SWI to execute transaction callbacks */
SwiP_post(&(object->swi));
}
}
}
}
}
/*
* ======== SPICC26X2DMA_init ========
*/
void SPICC26X2DMA_init(SPI_Handle handle)
{
((SPICC26X2DMA_Object *) handle->object)->isOpen = false;
}
/*
* ======== SPICC26X2DMA_open ========
*/
SPI_Handle SPICC26X2DMA_open(SPI_Handle handle, SPI_Params *params)
{
union {
HwiP_Params hwiParams;
SwiP_Params swiParams;
} paramsUnion;
uint32_t key;
SPICC26X2DMA_Object *object = handle->object;
SPICC26X2DMA_HWAttrs const *hwAttrs = handle->hwAttrs;
key = HwiP_disable();
/* Failure conditions */
if (object->isOpen ||
params->dataSize > PARAMS_DATASIZE_MAX ||
params->dataSize < PARAMS_DATASIZE_MIN) {
HwiP_restore(key);
return (NULL);
}
object->isOpen = true;
HwiP_restore(key);
DebugP_assert((params->dataSize >= 4) && (params->dataSize <= 16));
object->bitRate = params->bitRate;
object->dataSize = params->dataSize;
object->mode = params->mode;
object->transferMode = params->transferMode;
object->transferTimeout = params->transferTimeout;
object->returnPartial = SPICC26X2DMA_retPartDisabled;
object->headPtr = NULL;
object->tailPtr = NULL;
object->completedTransfers = NULL;
object->format = frameFormat[params->frameFormat];
object->txScratchBuf = hwAttrs->defaultTxBufValue;
object->busyBit = (params->mode == SPI_MASTER ? SSI_SR_BSY : SSI_SR_TFE);
object->manualStart = false;
Power_setDependency(hwAttrs->powerMngrId);
initHw(handle);
/* CSN is initialized using hwAttrs, but can be re-configured later */
object->csnPin = hwAttrs->csnPin;
/*
* Configure IOs after hardware has been initialized so that IOs aren't
* toggled unnecessary
*/
if (!initIO(handle)) {
/*
* Trying to use SPI driver when some other driver or application
* has already allocated these pins, error!
*/
Power_releaseDependency(hwAttrs->powerMngrId);
object->isOpen = false;
return (NULL);
}
HwiP_Params_init(&paramsUnion.hwiParams);
paramsUnion.hwiParams.arg = (uintptr_t) handle;
paramsUnion.hwiParams.priority = hwAttrs->intPriority;
HwiP_construct(&(object->hwi),
(int) hwAttrs->intNum, SPICC26X2DMA_hwiFxn,
&paramsUnion.hwiParams);
SwiP_Params_init(&paramsUnion.swiParams);
paramsUnion.swiParams.arg0 = (uintptr_t)handle;
paramsUnion.swiParams.priority = hwAttrs->swiPriority;
SwiP_construct(&(object->swi),
SPICC26X2DMA_swiFxn,
&(paramsUnion.swiParams));
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, csnCallback);
PIN_setUserArg(object->pinHandle, (uintptr_t) handle);
}
Power_registerNotify(&object->spiPostObj,
PowerCC26XX_AWAKE_STANDBY,
(Power_NotifyFxn) spiPostNotify,
(uint32_t) handle);
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);
object->transferCallbackFxn = blockingTransferCallback;
}
else {
DebugP_assert(params->transferCallbackFxn != NULL);
object->transferCallbackFxn = params->transferCallbackFxn;
}
return (handle);
}
/*
* ======== SPICC26X2DMA_swiFxn ========
*/
static void SPICC26X2DMA_swiFxn(uintptr_t arg0, uintptr_t arg1) {
SPI_Transaction *transaction;
SPICC26X2DMA_Object *object = ((SPI_Handle) arg0)->object;
while (object->completedTransfers != NULL) {
transaction = object->completedTransfers;
/* Move object->completedTransfers to the next transaction */
object->completedTransfers = object->completedTransfers->nextPtr;
transaction->nextPtr = NULL;
/* Transaction complete; release power constraints */
releaseConstraint((uint32_t) transaction->txBuf);
/* Execute callback function for completed transfer */
object->transferCallbackFxn((SPI_Handle) arg0, transaction);
}
}
/*
* ======== SPICC26X2DMA_transfer ========
*/
bool SPICC26X2DMA_transfer(SPI_Handle handle, SPI_Transaction *transaction)
{
uint8_t alignMask;
bool buffersAligned;
uintptr_t key;
SPICC26X2DMA_Object *object = handle->object;
SPICC26X2DMA_HWAttrs const *hwAttrs = handle->hwAttrs;
if (transaction->count == 0) {
return (false);
}
key = HwiP_disable();
/*
* 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->dataSize < 9) ? 0x0 : 0x01;
buffersAligned = ((((uint32_t) transaction->rxBuf & alignMask) == 0) &&
(((uint32_t) transaction->txBuf & alignMask) == 0));
if (!buffersAligned ||
(object->headPtr && object->transferMode == SPI_MODE_BLOCKING)) {
transaction->status = SPI_TRANSFER_FAILED;
HwiP_restore(key);
return (false);
}
else {
if (object->headPtr) {
object->tailPtr->nextPtr = transaction;
object->tailPtr = transaction;
object->tailPtr->status = SPI_TRANSFER_QUEUED;
}
else {
object->headPtr = transaction;
object->tailPtr = transaction;
object->framesQueued = 0;
object->framesTransferred = 0;
object->priTransferSize = 0;
object->altTransferSize = 0;
object->tailPtr->status =
(object->returnPartial != SPICC26X2DMA_retPartDisabled) ?
SPI_TRANSFER_PEND_CSN_ASSERT :
SPI_TRANSFER_STARTED;
}
object->tailPtr->nextPtr = NULL;
}
/* In slave mode, optionally enable callback on CSN de-assert */
if (object->returnPartial == SPICC26X2DMA_retPartEnabledIntNotSet) {
object->returnPartial = SPICC26X2DMA_retPartEnabledIntSet;
PIN_setInterrupt(object->pinHandle, object->csnPin | PIN_IRQ_BOTHEDGES);
}
/* Set constraints to guarantee transaction */
setConstraint((uint32_t)transaction->txBuf);
/*
* 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);
/* Release constraint since transaction is done */
releaseConstraint((uint32_t) transaction->txBuf);
/* Transaction completed; set status & mark SPI ready */
object->headPtr->status = SPI_TRANSFER_COMPLETED;
object->headPtr = NULL;
object->tailPtr = NULL;
}
else {
/*
* Perform a DMA backed SPI transfer; we need exclusive access while
* priming the transfer to prevent race conditions with
* SPICC26X2DMA_transferCancel().
*/
primeTransfer(object, hwAttrs);
/* Enable the RX overrun interrupt in the SSI module */
SSIIntEnable(hwAttrs->baseAddr, SSI_RXOR);
HwiP_restore(key);
if (object->transferMode == SPI_MODE_BLOCKING) {
if (SemaphoreP_OK != SemaphoreP_pend(&(object->transferComplete),
object->transferTimeout)) {
/* Timeout occurred; cancel the transfer */
object->headPtr->status = SPI_TRANSFER_FAILED;
SPICC26X2DMA_transferCancel(handle);
/*
* SPICC26X2DMA_transferCancel() performs callback which posts
* transferComplete semaphore. This call consumes this extra
* post.
*/
SemaphoreP_pend(&(object->transferComplete),
SemaphoreP_NO_WAIT);
return (false);
}
}
}
return (true);
}
/*
* ======== SPICC26X2DMA_transferCancel ========
*/
void SPICC26X2DMA_transferCancel(SPI_Handle handle) {
uintptr_t key;
uint32_t temp;
SPI_Transaction *tempPtr;
volatile tDMAControlTable *rxDmaTableEntry;
volatile tDMAControlTable *rxDmaTableAltEntry;
SPICC26X2DMA_Object *object = handle->object;
SPICC26X2DMA_HWAttrs const *hwAttrs = handle->hwAttrs;
/*
* Acquire exclusive access to the driver. Required to prevent race
* conditions if preempted by code trying to configure another transfer.
*/
key = HwiP_disable();
if (object->headPtr == NULL) {
HwiP_restore(key);
return;
}
/*
* There are 2 use cases in which to call transferCancel():
* 1. The driver is in CALLBACK mode.
* 2. The driver is in BLOCKING mode & there has been a transfer timeout.
*/
if (object->transferMode != SPI_MODE_BLOCKING ||
object->headPtr->status == SPI_TRANSFER_FAILED ||
object->headPtr->status == SPI_TRANSFER_CSN_DEASSERT) {
/* Prevent interrupt while canceling the transfer */
HwiP_disableInterrupt(hwAttrs->intNum);
/*
* Disable the TX DMA channel first to stop feeding more frames to
* the FIFO. Next, wait until the TX FIFO is empty (all frames in
* FIFO have been sent). RX DMA channel is disabled later to allow
* the DMA to move all frames already in FIFO to memory.
*/
UDMACC26XX_channelDisable(object->udmaHandle,
hwAttrs->txChannelBitMask);
if (object->mode == SPI_MASTER) {
/*
* Wait until the TX FIFO is empty; this is to make sure the
* chip select is deasserted before disabling the SPI.
*/
while (SSIBusy(hwAttrs->baseAddr)) {}
}
SSIDisable(hwAttrs->baseAddr);
/* Now disable the RX, DMA & interrupts */
UDMACC26XX_channelDisable(object->udmaHandle,
hwAttrs->rxChannelBitMask);
SSIDMADisable(hwAttrs->baseAddr,
SSI_DMA_TX | SSI_DMA_RX);
UDMACC26XX_clearInterrupt(object->udmaHandle,
hwAttrs->rxChannelBitMask |
hwAttrs->txChannelBitMask);
SSIIntDisable(hwAttrs->baseAddr, SSI_RXOR);
SSIIntClear(hwAttrs->baseAddr, SSI_RXOR);
/*
* Update transaction->count with the amount of frames which have
* been transferred.
*/
if (hwAttrs->baseAddr == SSI0_BASE) {
rxDmaTableEntry = &dmaSpi0RxControlTableEntry;
rxDmaTableAltEntry = &dmaSpi0RxAltControlTableEntry;
}
else {
rxDmaTableEntry = &dmaSpi1RxControlTableEntry;
rxDmaTableAltEntry = &dmaSpi1RxAltControlTableEntry;
}
object->headPtr->count = object->framesTransferred;
if (object->priTransferSize) {
temp = UDMACC26XX_GET_TRANSFER_SIZE(rxDmaTableEntry->ui32Control);
if (temp <= object->priTransferSize) {
object->headPtr->count += (object->priTransferSize - temp);
}
}
if (object->altTransferSize) {
temp =
UDMACC26XX_GET_TRANSFER_SIZE(rxDmaTableAltEntry->ui32Control);
if (temp <= object->altTransferSize) {
object->headPtr->count += (object->altTransferSize - temp);
}
}
/*
* Disables peripheral, clears all registers & reinitializes it to
* parameters used in SPI_open()
*/
initHw(handle);
HwiP_clearInterrupt(hwAttrs->intNum);
HwiP_enableInterrupt(hwAttrs->intNum);
/*
* Go through all queued transfers; set status CANCELED (if we did
* not cancel due to timeout). The object->headPtr->count is
* stored/restored temporarily.
*/
temp = object->headPtr->count;
tempPtr = object->headPtr;
while (tempPtr != NULL) {
if (tempPtr->status != SPI_TRANSFER_FAILED &&
tempPtr->status != SPI_TRANSFER_CSN_DEASSERT) {
tempPtr->status = SPI_TRANSFER_CANCELED;
}
tempPtr->count = 0;
tempPtr = tempPtr->nextPtr;
}
object->headPtr->count = temp;
/* Add all cancelled transactions to object->completedTransfers */
tempPtr = object->completedTransfers;
if (tempPtr == NULL) {
/* Empty list; just add all of the cancelled transactions */
object->completedTransfers = object->headPtr;
}
else {
/* Move through the list until we reach the last element */
while (tempPtr->nextPtr != NULL) {
tempPtr = tempPtr->nextPtr;
}
/* Add all of the cancelled transactions */
tempPtr->nextPtr = object->headPtr;
}
/* Clear all driver object variables*/
object->headPtr = NULL;
object->tailPtr = NULL;
object->framesQueued = 0;
object->framesTransferred = 0;
object->priTransferSize = 0;
object->altTransferSize = 0;
HwiP_restore(key);
/*
* All transactions have been marked as cancelled & added to
* object->completedTransfers. Post the driver SWI to execute
* callback functions.
*/
SwiP_post(&(object->swi));
/* Must return here; do not call HwiP_restore() twice */
return;
}
HwiP_restore(key);
}
/*
* ======== blockingTransferCallback ========
*/
static void blockingTransferCallback(SPI_Handle handle, SPI_Transaction *msg)
{
SPICC26X2DMA_Object *object = handle->object;
SemaphoreP_post(&(object->transferComplete));
}
/*
* ======== configNextTransfer ========
* This function must be executed with interrupts disabled.
*/
static void configNextTransfer(SPICC26X2DMA_Object *object,
SPICC26X2DMA_HWAttrs const *hwAttrs)
{
size_t framesQueued;
uint32_t transferAmt;
SPI_Transaction *transaction;
volatile tDMAControlTable *rxDmaTableEntry;
volatile tDMAControlTable *txDmaTableEntry;
uint8_t optionsIndex;
/*
* The DMA options vary according to data frame size; options for 8-bit
* data (or smaller) are in index 0. Options for larger frame sizes are
* in index 1.
*
* optionsIndex was originally calculated by:
* optionsIndex = (object->dataSize < 9) ? 0x00 : 0x01;
*
* However, the IAR compiler generated incorrect assembly:
*
* configNextTransfer:
* 0x5140: 0xe92d 0x41fc PUSH.W {R2-R8, LR}
* optionsIndex = (object->dataSize < 9) ? 0x00 : 0x01;
* 0x5144: 0xf100 0x0594 ADD.W R5, R0, #148 ; 0x94
* 0x5148: 0x6aaa LDR R2, [R5, #0x28]
* 0x514a: 0x2a08 CMP R2, #8
* 0x514c: 0x419b SBCS R3, R3, R3
* 0x514e: 0x43db MVNS R3, R3
* 0x5150: 0x0fdb LSRS R3, R3, #31
*
* To work around this issue is calculated as follows:
*/
optionsIndex = ((int32_t)(object->dataSize - 0x08) > 0) ? 0x01 : 0x00;
/*
* object->framesQueued keeps track of how many frames (of the current
* transaction) have been configured for DMA transfer. If
* object->framesQueued == transaction->count; all frames have been queued
* & we should configure the free DMA channel to send the next transaction.
* When the current transaction has completed; object->framesQueued
* will be updated (in the ISR) to reflect the amount of frames queued
* of the following transaction.
*/
transaction = object->headPtr;
if (object->framesQueued < transaction->count) {
framesQueued = object->framesQueued;
}
else {
transaction = object->headPtr->nextPtr;
if (transaction == NULL) {
/* There are no queued transactions */
return;
}
framesQueued = 0;
transaction->status = SPI_TRANSFER_STARTED;
}
/*
* The DMA has a max transfer amount of 1024. If the transaction is
* greater; we must transfer it in chunks. framesQueued keeps track of
* how much data has been queued for transfer.
*/
if ((transaction->count - framesQueued) > MAX_DMA_TRANSFER_AMOUNT) {
transferAmt = MAX_DMA_TRANSFER_AMOUNT;
}
else {
transferAmt = transaction->count - framesQueued;
}
/* Determine free channel & mark it as used by setting transfer size */
if (object->priTransferSize == 0) {
object->priTransferSize = transferAmt;
if (hwAttrs->baseAddr == SSI0_BASE) {
rxDmaTableEntry = &dmaSpi0RxControlTableEntry;
txDmaTableEntry = &dmaSpi0TxControlTableEntry;
}
else {
rxDmaTableEntry = &dmaSpi1RxControlTableEntry;
txDmaTableEntry = &dmaSpi1TxControlTableEntry;
}
}
else {
object->altTransferSize = transferAmt;
if (hwAttrs->baseAddr == SSI0_BASE) {
rxDmaTableEntry = &dmaSpi0RxAltControlTableEntry;
txDmaTableEntry = &dmaSpi0TxAltControlTableEntry;
}
else {
rxDmaTableEntry = &dmaSpi1RxAltControlTableEntry;
txDmaTableEntry = &dmaSpi1TxAltControlTableEntry;
}
}
/* Setup the TX transfer buffers & characteristics */
if (transaction->txBuf) {
txDmaTableEntry->ui32Control = dmaTxConfig[optionsIndex];
/*
* Add an offset for the amount of data transfered. The offset is
* calculated by: object->framesQueued * (optionsIndex + 1). This
* accounts for 8 or 16-bit sized transfers.
*/
txDmaTableEntry->pvSrcEndAddr =
(void *)((uint32_t) transaction->txBuf +
(uint32_t) (framesQueued * (optionsIndex + 1)) +
(transferAmt << optionsIndex) - 1);
}
else {
txDmaTableEntry->ui32Control = dmaNullConfig[optionsIndex];
txDmaTableEntry->pvSrcEndAddr = (void *) &(object->txScratchBuf);
}
txDmaTableEntry->pvDstEndAddr = (void *) (hwAttrs->baseAddr + SSI_O_DR);
txDmaTableEntry->ui32Control |=
UDMACC26XX_SET_TRANSFER_SIZE((uint16_t) transferAmt);
/* Setup the RX transfer buffers & characteristics */
if (transaction->rxBuf) {
rxDmaTableEntry->ui32Control = dmaRxConfig[optionsIndex];
/*
* Add an offset for the amount of data transfered. The offset is
* calculated by: object->framesQueued * (optionsIndex + 1). This
* accounts for 8 or 16-bit sized transfers.
*/
rxDmaTableEntry->pvDstEndAddr =
(void *) ((uint32_t) transaction->rxBuf +
(uint32_t) (framesQueued * (optionsIndex+ 1)) +
(transferAmt << optionsIndex) - 1);
}
else {
rxDmaTableEntry->ui32Control = dmaNullConfig[optionsIndex];
rxDmaTableEntry->pvDstEndAddr = &object->rxScratchBuf;
}
rxDmaTableEntry->pvSrcEndAddr = (void *) (hwAttrs->baseAddr + SSI_O_DR);
rxDmaTableEntry->ui32Control |=
UDMACC26XX_SET_TRANSFER_SIZE((uint16_t) transferAmt);
if (transaction == object->headPtr) {
/*
* Only update object->framesQueued if we are configuring a DMA
* channel for the current transaction.
*/
object->framesQueued += transferAmt;
}
if (!object->manualStart) {
/* Enable DMA to generate interrupt on SPI peripheral */
SSIDMAEnable(hwAttrs->baseAddr, SSI_DMA_TX | SSI_DMA_RX);
UDMACC26XX_channelEnable(object->udmaHandle,
hwAttrs->rxChannelBitMask |
hwAttrs->txChannelBitMask);
}
return;
}
/*
* ======== csnCallback ========
* Slave mode optional callback function for when the CSN is asserted &
* deasserted.
*/
static void csnCallback(PIN_Handle handle, PIN_Id pinId)
{
uintptr_t key;
SPICC26X2DMA_Object *object;
SPI_Handle spiHandle;
spiHandle = (SPI_Handle) PIN_getUserArg(handle);
object = spiHandle->object;
/* Transfer started if CSN low */
if (!PIN_getInputValue(object->csnPin)) {
key = HwiP_disable();
if (object->headPtr != NULL) {
/* Indicate transaction started */
object->headPtr->status = SPI_TRANSFER_STARTED;
}
else {
/* Disable all interrupts */
PIN_setInterrupt(handle, object->csnPin);
object->returnPartial = SPICC26X2DMA_retPartEnabledIntNotSet;
}
HwiP_restore(key);
}
/* Cancel transfer if CSN high */
if (PIN_getInputValue(object->csnPin)) {
key = HwiP_disable();
/* Disable all interrupts */
PIN_setInterrupt(handle, object->csnPin);
object->returnPartial = SPICC26X2DMA_retPartEnabledIntNotSet;
/* Indicate why the transaction completed */
if (object->headPtr != NULL) {
object->headPtr->status = SPI_TRANSFER_CSN_DEASSERT;
}
HwiP_restore(key);
/* Cancel the current transaction */
SPICC26X2DMA_transferCancel(spiHandle);
}
}
/*
* ======== flushFifos ========
*/
static void flushFifos(SPICC26X2DMA_HWAttrs const *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;
}
/*
* ======== getDmaChannelNumber ========
*/
static inline uint32_t getDmaChannelNumber(uint32_t x) {
#if defined(__TI_COMPILER_VERSION__)
return ((uint32_t) __clz(__rbit(x)));
#elif defined(__GNUC__)
return ((uint32_t) __builtin_ctz(x));
#elif defined(__IAR_SYSTEMS_ICC__)
return ((uint32_t) __CLZ(__RBIT(x)));
#else
#error "Unsupported compiler"
#endif
}
/*
* ======== initHw ========
*/
static void initHw(SPI_Handle handle) {
ClockP_FreqHz freq;
SPICC26X2DMA_Object *object = handle->object;
SPICC26X2DMA_HWAttrs const *hwAttrs = handle->hwAttrs;
flushFifos(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,
object->format,
object->mode,
object->bitRate,
object->dataSize);
}
/*
* ======== initIO ========
* 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 initIO(SPI_Handle handle) {
uint32_t i = 0;
SPICC26X2DMA_Object *object = handle->object;
SPICC26X2DMA_HWAttrs const *hwAttrs = handle->hwAttrs;
PIN_Config spiPinTable[5];
/* Build local list of pins, allocate through PIN driver and map HW ports */
if (object->mode == SPI_SLAVE) {
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 {
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->format == SSI_FRF_MOTO_MODE_0 ||
object->format == SSI_FRF_MOTO_MODE_1) {
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 (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);
}
/*
* ======== primeTransfer ========
* Function must be executed with interrupts disabled.
*/
static inline void primeTransfer(SPICC26X2DMA_Object *object,
SPICC26X2DMA_HWAttrs const *hwAttrs)
{
if (object->priTransferSize != 0 && object->altTransferSize != 0) {
/*
* Both primary & alternate channels are configured for a transfer.
* In this case no work is required; the Hwi will configure channels
* as transfers continue & complete.
*/
}
else if (object->priTransferSize == 0 && object->altTransferSize == 0) {
/*
* Primary & alternate channels are disabled; no active transfer,
* configure a new transfer.
*
* DMA based transfers use the DMA in ping-pong mode. If the transfer is
* larger than what the primary channel can handle; alternate channel is
* configured to continue where the primary channel left off. Channels
* are continuously reconfigured until the transfer is completed.
*
* We disable the alternate channel initially. This however causes an
* undesired interrupt to be triggered; so we need to
* disable/clear/re-enable the interrupt.
*/
HwiP_disableInterrupt(hwAttrs->intNum);
/* Set the primary DMA structure as active */
UDMACC26XX_disableAttribute(object->udmaHandle,
getDmaChannelNumber(hwAttrs->rxChannelBitMask),
UDMA_ATTR_ALTSELECT);
UDMACC26XX_disableAttribute(object->udmaHandle,
getDmaChannelNumber(hwAttrs->txChannelBitMask),
UDMA_ATTR_ALTSELECT);
HwiP_clearInterrupt(hwAttrs->intNum);
HwiP_enableInterrupt(hwAttrs->intNum);
/* Configure RX & TX DMA transfers */
configNextTransfer(object, hwAttrs);
object->activeChannel = UDMA_PRI_SELECT;
if (object->headPtr->count > MAX_DMA_TRANSFER_AMOUNT) {
configNextTransfer(object, hwAttrs);
}
/* Enable DMA to generate interrupt on SPI peripheral */
if (!object->manualStart) {
SSIEnable(hwAttrs->baseAddr);
}
}
else {
/* One of the channels is active; configure the other channel */
configNextTransfer(object, hwAttrs);
}
}
/*
* ======== releaseConstraint ========
*/
static inline void releaseConstraint(uint32_t txBufAddr)
{
/* 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);
}
/*
* ======== setConstraint ========
*/
static inline void setConstraint(uint32_t txBufAddr)
{
/*
* 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);
}
/*
* ======== spiPollingTransfer ========
*/
static inline void spiPollingTransfer(SPICC26X2DMA_Object *object,
SPICC26X2DMA_HWAttrs 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->rxScratchBuf);
}
if (transaction->txBuf) {
txBuf = transaction->txBuf;
}
else {
txBuf = (void *) &(object->txScratchBuf);
}
increment = (object->dataSize < 9) ? sizeof(uint8_t) : sizeof(uint16_t);
transferCount = transaction->count;
SSIEnable(hwAttrs->baseAddr);
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 (spiBusy(object, hwAttrs)) {}
/*
* For this driver implementation the peripheral is kept active until
* either a FIFO-overrun occurs or SPI_transferCancel() is executed.
*
* SSIDisable(hwAttrs->baseAddr);
*/
}
/*
* ======== spiPostNotify ========
*/
static int spiPostNotify(unsigned int eventType, uintptr_t eventArg,
uintptr_t clientArg)
{
initHw((SPI_Handle) clientArg);
return (Power_NOTIFYDONE);
}
/*
* ======== spiBusy ========
* HW is busy when in master mode and BSY bit is set, or when in slave mode
* and TFE bit is not set.
*/
static inline bool spiBusy(SPICC26X2DMA_Object *object,
SPICC26X2DMA_HWAttrs const *hwAttrs)
{
bool registerBit = (bool)(HWREG(hwAttrs->baseAddr + SSI_O_SR) & (object->busyBit));
if (object->busyBit == SSI_SR_BSY){
return(registerBit);
}
else
{
return(!registerBit);
}
}