/*
 * Copyright (c) 2015-2017, 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/cc32xx/inc/hw_mcspi.h>
#include <ti/devices/cc32xx/inc/hw_types.h>
#include <ti/devices/cc32xx/inc/hw_memmap.h>
#include <ti/devices/cc32xx/inc/hw_ocp_shared.h>
#include <ti/devices/cc32xx/driverlib/rom.h>
#include <ti/devices/cc32xx/driverlib/rom_map.h>
#include <ti/devices/cc32xx/driverlib/prcm.h>
#include <ti/devices/cc32xx/driverlib/spi.h>
#include <ti/devices/cc32xx/driverlib/udma.h>

#include <ti/drivers/dma/UDMACC32XX.h>
#include <ti/drivers/dpl/HwiP.h>
#include <ti/drivers/dpl/SemaphoreP.h>
#include <ti/drivers/Power.h>
#include <ti/drivers/power/PowerCC32XX.h>
#include <ti/drivers/spi/SPICC32XXDMA.h>

#define MAX_DMA_TRANSFER_AMOUNT (1024)

#define PAD_CONFIG_BASE (OCP_SHARED_BASE + OCP_SHARED_O_GPIO_PAD_CONFIG_0)
#define PAD_RESET_STATE 0xC61

void SPICC32XXDMA_close(SPI_Handle handle);
int_fast16_t SPICC32XXDMA_control(SPI_Handle handle, uint_fast16_t cmd,
    void *arg);
void SPICC32XXDMA_init(SPI_Handle handle);
SPI_Handle SPICC32XXDMA_open(SPI_Handle handle, SPI_Params *params);
bool SPICC32XXDMA_transfer(SPI_Handle handle, SPI_Transaction *transaction);
void SPICC32XXDMA_transferCancel(SPI_Handle handle);

/* SPI function table for SPICC32XXDMA implementation */
const SPI_FxnTable SPICC32XXDMA_fxnTable = {
    SPICC32XXDMA_close,
    SPICC32XXDMA_control,
    SPICC32XXDMA_init,
    SPICC32XXDMA_open,
    SPICC32XXDMA_transfer,
    SPICC32XXDMA_transferCancel
};

static const uint32_t mode[] = {
    SPI_MODE_MASTER,
    SPI_MODE_SLAVE
};

/*
 * This lookup table is used to configure the DMA channels for the appropriate
 * (8bit, 16bit or 32bit) transfer sizes.
 * Table for an SPI DMA RX channel.
 */
static const uint32_t dmaRxConfig[] = {
    UDMA_SIZE_8  | UDMA_SRC_INC_NONE | UDMA_DST_INC_8  | UDMA_ARB_1,
    UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_1,
    UDMA_SIZE_32 | UDMA_SRC_INC_NONE | UDMA_DST_INC_32 | UDMA_ARB_1
};

/*
 * This lookup table is used to configure the DMA channels for the appropriate
 * (8bit, 16bit or 32bit) transfer sizes.
 * Table for an SPI DMA TX channel
 */
static const uint32_t dmaTxConfig[] = {
    UDMA_SIZE_8  | UDMA_SRC_INC_8  | UDMA_DST_INC_NONE | UDMA_ARB_1,
    UDMA_SIZE_16 | UDMA_SRC_INC_16 | UDMA_DST_INC_NONE | UDMA_ARB_1,
    UDMA_SIZE_32 | UDMA_SRC_INC_32 | UDMA_DST_INC_NONE | UDMA_ARB_1
};

/*
 * This lookup table is used to configure the DMA channels for the appropriate
 * (8bit, 16bit or 32bit) transfer sizes when either txBuf or rxBuf are NULL.
 */
static const uint32_t dmaNullConfig[] = {
    UDMA_SIZE_8  | UDMA_SRC_INC_NONE | UDMA_DST_INC_NONE | UDMA_ARB_1,
    UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_NONE | UDMA_ARB_1,
    UDMA_SIZE_32 | UDMA_SRC_INC_NONE | UDMA_DST_INC_NONE | UDMA_ARB_1
};

/*
 *  ======== blockingTransferCallback ========
 */
static void blockingTransferCallback(SPI_Handle handle,
    SPI_Transaction *transaction)
{
    SPICC32XXDMA_Object *object = handle->object;

    SemaphoreP_post(object->transferComplete);
}

/*
 *  ======== configDMA ========
 *  This functions configures the transmit and receive DMA channels for a given
 *  SPI_Handle and SPI_Transaction
 */
static void configDMA(SPICC32XXDMA_Object *object,
    SPICC32XXDMA_HWAttrsV1 const *hwAttrs, SPI_Transaction *transaction)
{
    uintptr_t  key;
    void      *buf;
    uint32_t   channelControlOptions;
    uint8_t    dataFrameSizeInBytes;
    uint8_t    optionsIndex;

    /* DMA options used vary according to data size. */
    if (object->dataSize < 9) {
        optionsIndex = 0;
        dataFrameSizeInBytes = sizeof(uint8_t);
    }
    else if (object->dataSize < 17) {
        optionsIndex = 1;
        dataFrameSizeInBytes = sizeof(uint16_t);;
    }
    else {
        optionsIndex = 2;
        dataFrameSizeInBytes = sizeof(uint32_t);
    }

    /*
     * 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 ((transaction->count - object->amtDataXferred) > MAX_DMA_TRANSFER_AMOUNT) {
        object->currentXferAmt = MAX_DMA_TRANSFER_AMOUNT;
    }
    else {
        object->currentXferAmt = (transaction->count - object->amtDataXferred);
    }

    if (transaction->txBuf) {
        channelControlOptions = dmaTxConfig[optionsIndex];
        /*
         * Add an offset for the amount of data transfered.  The offset is
         * calculated by: object->amtDataXferred * (dataFrameSizeInBytes).
         * This accounts for 8, 16 or 32-bit sized transfers.
         */
        buf = (void *) ((uint32_t) transaction->txBuf +
            ((uint32_t) object->amtDataXferred * dataFrameSizeInBytes));
    }
    else {
        channelControlOptions = dmaNullConfig[optionsIndex];
        *hwAttrs->scratchBufPtr = hwAttrs->defaultTxBufValue;
        buf = hwAttrs->scratchBufPtr;
    }

    /* Setup the TX transfer characteristics & buffers */
    MAP_uDMAChannelControlSet(hwAttrs->txChannelIndex | UDMA_PRI_SELECT,
        channelControlOptions);
    MAP_uDMAChannelAttributeDisable(hwAttrs->txChannelIndex,
        UDMA_ATTR_ALTSELECT);
    MAP_uDMAChannelTransferSet(hwAttrs->txChannelIndex | UDMA_PRI_SELECT,
        UDMA_MODE_BASIC, buf, (void *) (hwAttrs->baseAddr + MCSPI_O_TX0),
        object->currentXferAmt);

    if (transaction->rxBuf) {
        channelControlOptions = dmaRxConfig[optionsIndex];
        /*
         * Add an offset for the amount of data transfered.  The offset is
         * calculated by: object->amtDataXferred * (dataFrameSizeInBytes).
         * This accounts for 8 or 16-bit sized transfers.
         */
        buf = (void *) ((uint32_t) transaction->rxBuf +
            ((uint32_t) object->amtDataXferred * dataFrameSizeInBytes));
    }
    else {
        channelControlOptions = dmaNullConfig[optionsIndex];
        buf = hwAttrs->scratchBufPtr;
    }

    /* Setup the RX transfer characteristics & buffers */
    MAP_uDMAChannelControlSet(hwAttrs->rxChannelIndex | UDMA_PRI_SELECT,
        channelControlOptions);
    MAP_uDMAChannelAttributeDisable(hwAttrs->rxChannelIndex,
        UDMA_ATTR_ALTSELECT);
    MAP_uDMAChannelTransferSet(hwAttrs->rxChannelIndex | UDMA_PRI_SELECT,
        UDMA_MODE_BASIC, (void *) (hwAttrs->baseAddr + MCSPI_O_RX0), buf,
        object->currentXferAmt);

    /* A lock is needed because we are accessing shared uDMA memory */
    key = HwiP_disable();

    MAP_uDMAChannelAssign(hwAttrs->rxChannelIndex);
    MAP_uDMAChannelAssign(hwAttrs->txChannelIndex);

    /* Enable DMA to generate interrupt on SPI peripheral */
    MAP_SPIDmaEnable(hwAttrs->baseAddr, SPI_RX_DMA | SPI_TX_DMA);
    MAP_SPIIntClear(hwAttrs->baseAddr, SPI_INT_DMARX);
    MAP_SPIIntEnable(hwAttrs->baseAddr, SPI_INT_DMARX);
    MAP_SPIWordCountSet(hwAttrs->baseAddr, object->currentXferAmt);

    /* Enable channels & start DMA transfers */
    MAP_uDMAChannelEnable(hwAttrs->txChannelIndex);
    MAP_uDMAChannelEnable(hwAttrs->rxChannelIndex);

    HwiP_restore(key);

    MAP_SPIEnable(hwAttrs->baseAddr);
    MAP_SPICSEnable(hwAttrs->baseAddr);
}

/*
 *  ======== getDmaRemainingXfers ========
 */
static inline uint32_t getDmaRemainingXfers(SPICC32XXDMA_HWAttrsV1 const *hwAttrs) {
    uint32_t          controlWord;
    tDMAControlTable *controlTable;

    controlTable = MAP_uDMAControlBaseGet();
    controlWord = controlTable[(hwAttrs->rxChannelIndex & 0x3f)].ulControl;

    return (((controlWord & UDMA_CHCTL_XFERSIZE_M) >> 4) + 1);
}

/*
 *  ======== getPowerMgrId ========
 */
static uint16_t getPowerMgrId(uint32_t baseAddr)
{
    switch (baseAddr) {
        case GSPI_BASE:
            return (PowerCC32XX_PERIPH_GSPI);
        case LSPI_BASE:
            return (PowerCC32XX_PERIPH_LSPI);
        default:
            return (~0);
    }
}

/*
 *  ======== initHw ========
 */
static void initHw(SPICC32XXDMA_Object *object,
    SPICC32XXDMA_HWAttrsV1 const *hwAttrs)
{
    /*
     * SPI peripheral should remain disabled until a transfer is requested.
     * This is done to prevent the RX FIFO from gathering data from other
     * transfers.
     */
    MAP_SPICSDisable(hwAttrs->baseAddr);
    MAP_SPIDisable(hwAttrs->baseAddr);
    MAP_SPIReset(hwAttrs->baseAddr);

    MAP_SPIConfigSetExpClk(hwAttrs->baseAddr,
        MAP_PRCMPeripheralClockGet(hwAttrs->spiPRCM), object->bitRate,
        mode[object->spiMode], object->frameFormat,
        (hwAttrs->csControl | hwAttrs->pinMode | hwAttrs->turboMode |
        hwAttrs->csPolarity | ((object->dataSize - 1) << 7)));

    MAP_SPIFIFOEnable(hwAttrs->baseAddr, SPI_RX_FIFO | SPI_TX_FIFO);
    MAP_SPIFIFOLevelSet(hwAttrs->baseAddr, object->txFifoTrigger,
        object->rxFifoTrigger);
}

/*
 *  ======== postNotifyFxn ========
 */
static int postNotifyFxn(unsigned int eventType, uintptr_t eventArg,
    uintptr_t clientArg)
{
    SPICC32XXDMA_Object          *object = ((SPI_Handle) clientArg)->object;
    SPICC32XXDMA_HWAttrsV1 const *hwAttrs = ((SPI_Handle) clientArg)->hwAttrs;

    initHw(object, hwAttrs);

    return (Power_NOTIFYDONE);
}

/*
 *  ======== spiHwiFxn ========
 */
static void spiHwiFxn(uintptr_t arg)
{
    SPI_Transaction              *msg;
    SPICC32XXDMA_Object          *object = ((SPI_Handle)arg)->object;
    SPICC32XXDMA_HWAttrsV1 const *hwAttrs = ((SPI_Handle)arg)->hwAttrs;

    /* RX DMA channel has completed */
    MAP_SPIDmaDisable(hwAttrs->baseAddr, SPI_RX_DMA | SPI_TX_DMA);
    MAP_SPIIntDisable(hwAttrs->baseAddr, SPI_INT_DMARX);
    MAP_SPIIntClear(hwAttrs->baseAddr, SPI_INT_DMARX);

    if (object->transaction->count - object->amtDataXferred >
        MAX_DMA_TRANSFER_AMOUNT) {
        /* Data still remaining, configure another DMA transfer */
        object->amtDataXferred += object->currentXferAmt;

        configDMA(object, hwAttrs, object->transaction);
    }
    else {
        /*
         * All data sent; disable peripheral, set status, perform
         * callback & return
         */
        MAP_SPICSDisable(hwAttrs->baseAddr);
        MAP_SPIDisable(hwAttrs->baseAddr);
        object->transaction->status = SPI_TRANSFER_COMPLETED;

        /* Release constraint since transaction is done */
        Power_releaseConstraint(PowerCC32XX_DISALLOW_LPDS);

        /*
         * Use a temporary transaction pointer in case the callback function
         * attempts to perform another SPI_transfer call
         */
        msg = object->transaction;

        /* Indicate we are done with this transfer */
        object->transaction = NULL;

        object->transferCallbackFxn((SPI_Handle) arg, msg);
    }
}

/*
 *  ======== spiPollingTransfer ========
 */
static inline void spiPollingTransfer(SPICC32XXDMA_Object *object,
    SPICC32XXDMA_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 = hwAttrs->scratchBufPtr;
    }

    if (transaction->txBuf) {
        txBuf = transaction->txBuf;
    }
    else {
        *hwAttrs->scratchBufPtr = hwAttrs->defaultTxBufValue;
        txBuf = hwAttrs->scratchBufPtr;
    }

    if (object->dataSize < 9) {
        increment = sizeof(uint8_t);
    }
    else if (object->dataSize < 17) {
        increment = sizeof(uint16_t);
    }
    else {
        increment = sizeof(uint32_t);
    }

    transferCount = transaction->count;

    /*
     * Start the polling transfer - we MUST set word count to 0; not doing so
     * will raise spurious RX interrupts flags (though interrupts are not
     * enabled).
     */
    MAP_SPIWordCountSet(hwAttrs->baseAddr, 0);
    MAP_SPIEnable(hwAttrs->baseAddr);
    MAP_SPICSEnable(hwAttrs->baseAddr);

    while (transferCount--) {
        if (object->dataSize < 9) {
            MAP_SPIDataPut(hwAttrs->baseAddr, *((uint8_t *) txBuf));
            MAP_SPIDataGet(hwAttrs->baseAddr, (unsigned long *)&dummyBuffer);
            *((uint8_t *) rxBuf) = (uint8_t) dummyBuffer;
        }
        else if (object->dataSize < 17) {
            MAP_SPIDataPut(hwAttrs->baseAddr, *((uint16_t *) txBuf));
            MAP_SPIDataGet(hwAttrs->baseAddr, (unsigned long *) &dummyBuffer);
            *((uint16_t *) rxBuf) = (uint16_t) dummyBuffer;
        }
        else {
            MAP_SPIDataPut(hwAttrs->baseAddr, *((uint32_t *) txBuf));
            MAP_SPIDataGet(hwAttrs->baseAddr, (unsigned long * ) rxBuf);
        }

        /* 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);
        }
    }

    MAP_SPICSDisable(hwAttrs->baseAddr);
    MAP_SPIDisable(hwAttrs->baseAddr);
}
static inline void spiPollingWrite32(SPI_Transaction *transaction)
{
    uint8_t   increment;
    uint32_t  dummyBuffer;
    size_t    transferCount;
    void     *rxBuf;
    uint32_t* txBuf;

    txBuf = (uint32_t *)transaction->txBuf;


    increment = sizeof(uint32_t);
    transferCount = transaction->count;

    /*
     * Start the polling transfer - we MUST set word count to 0; not doing so
     * will raise spurious RX interrupts flags (though interrupts are not
     * enabled).
     */
    MAP_SPIWordCountSet(LSPI_BASE, 0);
    MAP_SPIEnable(LSPI_BASE);
    MAP_SPICSEnable(LSPI_BASE);

    while (transferCount--) {
        MAP_SPIDataPut(LSPI_BASE, *( txBuf));
        MAP_SPIDataGet(LSPI_BASE, (unsigned long * ) &dummyBuffer);

        /* Only increment source & destination if buffers were provided */
        txBuf++;
    }

    MAP_SPICSDisable(LSPI_BASE);
    MAP_SPIDisable(LSPI_BASE);
}

/*
 *  ======== SPICC32XXDMA_close ========
 */
void SPICC32XXDMA_close(SPI_Handle handle)
{
    uint32_t                      padRegister;
    SPICC32XXDMA_Object          *object = handle->object;
    SPICC32XXDMA_HWAttrsV1 const *hwAttrs = handle->hwAttrs;

    MAP_SPICSDisable(hwAttrs->baseAddr);
    MAP_SPIDisable(hwAttrs->baseAddr);
    MAP_SPIFIFODisable(hwAttrs->baseAddr, SPI_RX_FIFO | SPI_TX_FIFO);

    /* Release power dependency on SPI. */
    Power_releaseDependency(getPowerMgrId(hwAttrs->baseAddr));
    Power_unregisterNotify(&(object->notifyObj));

    if (object->hwiHandle) {
        HwiP_delete(object->hwiHandle);
    }
    if (object->transferComplete) {
        SemaphoreP_delete(object->transferComplete);
    }

    if (object->dmaHandle) {
        UDMACC32XX_close(object->dmaHandle);
    }

    /* Restore pin pads to their reset states */
    if (hwAttrs->mosiPin != SPICC32XXDMA_PIN_NO_CONFIG) {
        padRegister = (PinToPadGet((hwAttrs->mosiPin) & 0xff)<<2)
            + PAD_CONFIG_BASE;
        HWREG(padRegister) = PAD_RESET_STATE;
    }
    if (hwAttrs->misoPin != SPICC32XXDMA_PIN_NO_CONFIG) {
        padRegister = (PinToPadGet((hwAttrs->misoPin) & 0xff)<<2)
            + PAD_CONFIG_BASE;
        HWREG(padRegister) = PAD_RESET_STATE;
    }
    if (hwAttrs->clkPin != SPICC32XXDMA_PIN_NO_CONFIG) {
        padRegister = (PinToPadGet((hwAttrs->clkPin) & 0xff)<<2)
            + PAD_CONFIG_BASE;
        HWREG(padRegister) = PAD_RESET_STATE;
    }
    if ((hwAttrs->pinMode == SPI_4PIN_MODE) &&
        (hwAttrs->csPin != SPICC32XXDMA_PIN_NO_CONFIG)) {
        padRegister = (PinToPadGet((hwAttrs->csPin) & 0xff)<<2)
            + PAD_CONFIG_BASE;
        HWREG(padRegister) = PAD_RESET_STATE;
    }

    object->isOpen = false;
}

/*
 *  ======== SPICC32XXDMA_control ========
 */
int_fast16_t SPICC32XXDMA_control(SPI_Handle handle, uint_fast16_t cmd, void *arg)
{
    return (SPI_STATUS_UNDEFINEDCMD);
}

/*
 *  ======== SPICC32XXDMA_init ========
 */
void SPICC32XXDMA_init(SPI_Handle handle)
{
    UDMACC32XX_init();
}

/*
 *  ======== SPICC32XXDMA_open ========
 */
SPI_Handle SPICC32XXDMA_open(SPI_Handle handle, SPI_Params *params)
{
    uintptr_t                     key;
    uint16_t                      pin;
    uint16_t                      mode;
    uint8_t                      powerMgrId;
    HwiP_Params                   hwiParams;
    SPICC32XXDMA_Object          *object = handle->object;
    SPICC32XXDMA_HWAttrsV1 const *hwAttrs = handle->hwAttrs;

    key = HwiP_disable();

    if (object->isOpen) {
        HwiP_restore(key);

        return (NULL);
    }
    object->isOpen = true;

    HwiP_restore(key);

    /* SPI_TI & SPI_MW are not supported */
    if (params->frameFormat == SPI_TI || params->frameFormat == SPI_MW) {
        object->isOpen = false;

        return (NULL);
    }

    /* Register power dependency - i.e. power up and enable clock for SPI. */
    powerMgrId = getPowerMgrId(hwAttrs->baseAddr);
    if (powerMgrId > PowerCC32XX_NUMRESOURCES) {
        object->isOpen = false;

        return (NULL);
    }
    Power_setDependency(powerMgrId);
    Power_registerNotify(&(object->notifyObj), PowerCC32XX_AWAKE_LPDS,
        postNotifyFxn, (uintptr_t) handle);

    /* Configure the pins */
    if (hwAttrs->mosiPin != SPICC32XXDMA_PIN_NO_CONFIG) {
        pin = (hwAttrs->mosiPin) & 0xff;
        mode = (hwAttrs->mosiPin >> 8) & 0xff;
        MAP_PinTypeSPI((unsigned long) pin, (unsigned long) mode);
    }

    if (hwAttrs->misoPin != SPICC32XXDMA_PIN_NO_CONFIG) {
        pin = (hwAttrs->misoPin) & 0xff;
        mode = (hwAttrs->misoPin >> 8) & 0xff;
        MAP_PinTypeSPI((unsigned long) pin, (unsigned long) mode);
    }

    if (hwAttrs->clkPin != SPICC32XXDMA_PIN_NO_CONFIG) {
        pin = (hwAttrs->clkPin) & 0xff;
        mode = (hwAttrs->clkPin >> 8) & 0xff;
        MAP_PinTypeSPI((unsigned long) pin, (unsigned long) mode);
    }

    if (hwAttrs->pinMode == SPI_4PIN_MODE) {
        if (hwAttrs->csPin != SPICC32XXDMA_PIN_NO_CONFIG) {
            pin = (hwAttrs->csPin) & 0xff;
            mode = (hwAttrs->csPin >> 8) & 0xff;
            MAP_PinTypeSPI((unsigned long) pin, (unsigned long) mode);
        }
    }

    object->dmaHandle = UDMACC32XX_open();
    if (object->dmaHandle == NULL) {
        SPICC32XXDMA_close(handle);

        return (NULL);
    }

    HwiP_Params_init(&hwiParams);
    hwiParams.arg = (uintptr_t) handle;
    hwiParams.priority = hwAttrs->intPriority;
    object->hwiHandle = HwiP_create(hwAttrs->intNum, spiHwiFxn, &hwiParams);
    if (object->hwiHandle == NULL) {
        SPICC32XXDMA_close(handle);

        return (NULL);
    }

    if (params->transferMode == SPI_MODE_BLOCKING) {
        /*
         * Create a semaphore to block task execution for the duration of the
         * SPI transfer
         */
        object->transferComplete = SemaphoreP_createBinary(0);
        if (object->transferComplete == NULL) {
            SPICC32XXDMA_close(handle);

            return (NULL);
        }

        object->transferCallbackFxn = blockingTransferCallback;
    }
    else {
        if (params->transferCallbackFxn == NULL) {
            SPICC32XXDMA_close(handle);

            return (NULL);
        }

        object->transferCallbackFxn = params->transferCallbackFxn;
    }

    object->bitRate = params->bitRate;
    object->dataSize = params->dataSize;
    object->frameFormat = params->frameFormat;
    object->spiMode = params->mode;
    object->transaction = NULL;
    object->transferMode = params->transferMode;
    object->transferTimeout = params->transferTimeout;

    /* SPI FIFO trigger sizes vary based on data frame size */
    if (object->dataSize < 9) {
        object->rxFifoTrigger = sizeof(uint8_t);
        object->txFifoTrigger = sizeof(uint8_t);
    }
    else if (object->dataSize < 17) {
        object->rxFifoTrigger = sizeof(uint16_t);
        object->txFifoTrigger = sizeof(uint16_t);
    }
    else {
        object->rxFifoTrigger = sizeof(uint32_t);
        object->txFifoTrigger = sizeof(uint32_t);
    }

    initHw(object, hwAttrs);

    return (handle);
}

/*
 *  ======== SPICC32XXDMA_transfer ========
 */
bool SPICC32XXDMA_transfer(SPI_Handle handle, SPI_Transaction *transaction)
{
    uintptr_t                     key;
    uint8_t                       alignMask;
    bool                          buffersAligned;
    SPICC32XXDMA_Object          *object = handle->object;
    SPICC32XXDMA_HWAttrsV1 const *hwAttrs = handle->hwAttrs;

    if ((transaction->count == 0) ||
        (transaction->rxBuf == NULL && transaction->txBuf == NULL)) {
        return (false);
    }

    key = HwiP_disable();

    /*
     * alignMask is used to determine if the RX/TX buffers addresses are
     * aligned to the data frame size.
     */
    alignMask = (object->rxFifoTrigger - 1);
    buffersAligned = ((((uint32_t) transaction->rxBuf & alignMask) == 0) &&
        (((uint32_t) transaction->txBuf & alignMask) == 0));

    if (object->transaction) {
        HwiP_restore(key);

        return (false);
    }
    else {
        object->transaction = transaction;
        object->transaction->status = SPI_TRANSFER_STARTED;
        object->amtDataXferred = 0;
        object->currentXferAmt = 0;
    }

    HwiP_restore(key);

    /* Set constraints to guarantee transaction */
    Power_setConstraint(PowerCC32XX_DISALLOW_LPDS);

    /* Polling transfer if BLOCKING mode & transaction->count < threshold */
    if ((object->transferMode == SPI_MODE_BLOCKING &&
        transaction->count < hwAttrs->minDmaTransferSize) || !buffersAligned) {
        spiPollingTransfer(object, hwAttrs, transaction);

        /* Transaction completed; set status & mark SPI ready */
        object->transaction->status = SPI_TRANSFER_COMPLETED;
        object->transaction = NULL;

        Power_releaseConstraint(PowerCC32XX_DISALLOW_LPDS);
    }
    else {
        /* Perform a DMA backed SPI transfer */
        configDMA(object, hwAttrs, transaction);

        if (object->transferMode == SPI_MODE_BLOCKING) {
            if (SemaphoreP_pend(object->transferComplete,
                object->transferTimeout) != SemaphoreP_OK) {
                /* Timeout occurred; cancel the transfer */
                object->transaction->status = SPI_TRANSFER_FAILED;
                SPICC32XXDMA_transferCancel(handle);

                /*
                 * TransferCancel() performs callback which posts
                 * transferComplete semaphore. This call consumes this extra post.
                 */
                SemaphoreP_pend(object->transferComplete, SemaphoreP_NO_WAIT);

                return (false);
            }
        }
    }

    return (true);
}
/*
 *  ======== SPICC32XXDMA_transfer ========
 */
bool SPICC32XXDMA_write32(SPI_Handle handle, SPI_Transaction *transaction)
{
    uintptr_t                     key;
    uint8_t                       alignMask;
    bool                          buffersAligned;
    SPICC32XXDMA_Object          *object = handle->object;
    SPICC32XXDMA_HWAttrsV1 const *hwAttrs = handle->hwAttrs;

    if (transaction->count == 0) {
        return (false);
    }

    key = HwiP_disable();

    /*
     * alignMask is used to determine if the RX/TX buffers addresses are
     * aligned to the data frame size.
     */
    alignMask = (object->rxFifoTrigger - 1);
    buffersAligned = ((((uint32_t) transaction->txBuf & alignMask) == 0));

    if (object->transaction) {
        HwiP_restore(key);

        return (false);
    }
    else {
        object->transaction = transaction;
        object->transaction->status = SPI_TRANSFER_STARTED;
        object->amtDataXferred = 0;
        object->currentXferAmt = 0;
    }

    HwiP_restore(key);

    /* Set constraints to guarantee transaction */
    Power_setConstraint(PowerCC32XX_DISALLOW_LPDS);

    /* Polling transfer if BLOCKING mode & transaction->count < threshold */
    if ((transaction->count < hwAttrs->minDmaTransferSize) || !buffersAligned) {
        spiPollingWrite32(transaction);

        /* Transaction completed; set status & mark SPI ready */
        object->transaction->status = SPI_TRANSFER_COMPLETED;
        object->transaction = NULL;

        Power_releaseConstraint(PowerCC32XX_DISALLOW_LPDS);
    }
    else {
        /* Perform a DMA backed SPI transfer */
        configDMA(object, hwAttrs, transaction);

        if (SemaphoreP_pend(object->transferComplete,
                object->transferTimeout) != SemaphoreP_OK) {
            /* Timeout occurred; cancel the transfer */
            object->transaction->status = SPI_TRANSFER_FAILED;
            SPICC32XXDMA_transferCancel(handle);

            /*
             * TransferCancel() performs callback which posts
             * transferComplete semaphore. This call consumes this extra post.
             */
            SemaphoreP_pend(object->transferComplete, SemaphoreP_NO_WAIT);

            return (false);
        }
    }

    return (true);
}

/*
 *  ======== SPICC32XXDMA_transferCancel ========
 */
void SPICC32XXDMA_transferCancel(SPI_Handle handle)
{
    uintptr_t                     key;
    SPI_Transaction              *msg;
    SPICC32XXDMA_Object          *object = handle->object;
    SPICC32XXDMA_HWAttrsV1 const *hwAttrs = handle->hwAttrs;

    /*
     * 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_CALLBACK ||
        object->transaction->status == SPI_TRANSFER_FAILED) {

        key = HwiP_disable();

        if (object->transaction == NULL || object->cancelInProgress) {
            HwiP_restore(key);

            return;
        }
        object->cancelInProgress = true;

        /* Prevent interrupt from occurring while canceling the transfer */
        HwiP_disableInterrupt(hwAttrs->intNum);
        HwiP_clearInterrupt(hwAttrs->intNum);

        /* Clear DMA configuration */
        MAP_uDMAChannelDisable(hwAttrs->rxChannelIndex);
        MAP_uDMAChannelDisable(hwAttrs->txChannelIndex);

        MAP_SPIIntDisable(hwAttrs->baseAddr, SPI_INT_DMARX);
        MAP_SPIIntClear(hwAttrs->baseAddr, SPI_INT_DMARX);
        MAP_SPIDmaDisable(hwAttrs->baseAddr, SPI_RX_DMA | SPI_TX_DMA);

        HwiP_restore(key);

        /*
         * Disables peripheral, clears all registers & reinitializes it to
         * parameters used in SPI_open()
         */
        initHw(object, hwAttrs);

        HwiP_enableInterrupt(hwAttrs->intNum);

        /*
         * Calculate amount of data which has already been sent & store
         * it in transaction->count
         */
        object->transaction->count = object->amtDataXferred +
            (object->currentXferAmt - getDmaRemainingXfers(hwAttrs));

        /* Set status CANCELED if we did not cancel due to timeout  */
        if (object->transaction->status == SPI_TRANSFER_STARTED) {
            object->transaction->status = SPI_TRANSFER_CANCELED;
        }

        /* Release constraint set during transaction */
        Power_releaseConstraint(PowerCC32XX_DISALLOW_LPDS);

        /*
         * Use a temporary transaction pointer in case the callback function
         * attempts to perform another SPI_transfer call
         */
        msg = object->transaction;

        /* Indicate we are done with this transfer */
        object->transaction = NULL;
        object->cancelInProgress = false;
        object->transferCallbackFxn(handle, msg);
    }
}
