mirror of
https://github.com/thelsing/knx.git
synced 2025-10-08 11:14:29 +02:00
* 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
1197 lines
44 KiB
C
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(¶msUnion.hwiParams);
|
|
paramsUnion.hwiParams.arg = (uintptr_t) handle;
|
|
paramsUnion.hwiParams.priority = hwAttrs->intPriority;
|
|
HwiP_construct(&(object->hwi), (int) hwAttrs->intNum, SPICC26XXDMA_hwiFxn, ¶msUnion.hwiParams);
|
|
|
|
/* Create Swi object for this SPI peripheral */
|
|
SwiP_Params_init(¶msUnion.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);
|
|
}
|