123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535 |
- /*
- * @brief SPI slave ROM API declarations and functions
- *
- * @note
- * Copyright(C) NXP Semiconductors, 2014
- * All rights reserved.
- *
- * @par
- * Software that is described herein is for illustrative purposes only
- * which provides customers with programming information regarding the
- * LPC products. This software is supplied "AS IS" without any warranties of
- * any kind, and NXP Semiconductors and its licensor disclaim any and
- * all warranties, express or implied, including all implied warranties of
- * merchantability, fitness for a particular purpose and non-infringement of
- * intellectual property rights. NXP Semiconductors assumes no responsibility
- * or liability for the use of the software, conveys no license or rights under any
- * patent, copyright, mask work right, or any other intellectual property rights in
- * or to any products. NXP Semiconductors reserves the right to make changes
- * in the software without notification. NXP Semiconductors also makes no
- * representation or warranty that such application will be suitable for the
- * specified use without further testing or modification.
- *
- * @par
- * Permission to use, copy, modify, and distribute this software and its
- * documentation is hereby granted, under NXP Semiconductors' and its
- * licensor's relevant copyrights in the software, without fee, provided that it
- * is used in conjunction with NXP Semiconductors microcontrollers. This
- * copyright, permission, and disclaimer notice must appear in all copies of
- * this code.
- */
- #include <stdint.h>
- #include <string.h>
- #include "hw_spisd.h"
- #define DRVVERSION 0x0100
- /* Private callback for FIFO push and pop functions. This privately maps
- to the VFIFO for the SPI slave, but can be overriden if needed. The driver will
- automatically pick the right implementation at run-time based on the transfer
- size in the transfer descriptor. */
- typedef uint16_t (*spisSlaveFifoPushFN)(LPC_SPI_T *pSPI, void *data, uint32_t sendBytes, uint32_t curIndex);
- typedef uint16_t (*spisSlaveFifoPopFN)(LPC_SPI_T *pSPI, void *data, uint32_t bytes, uint32_t curIndex);
- /* Private data structure used for the SPI slave driver, holds the driver and
- peripheral context */
- typedef struct {
- void *pUserData; /*!< Pointer to user data used by driver instance, use NULL if not used */
- LPC_SPI_T *base; /*!< Base address of SPI peripheral to use */
- spisSlaveXferCSAssertCB pAssertCb; /*!< SSEL assertion callback */
- spisSlaveTransmitCB pTranCb; /*!< Transmit data start callback */
- spisSlaveReceiveCB pRecvCb; /*!< Receive data start callback */
- spisSlaveXferCSDeAssertCB pDeassertCb; /*!< SSEL deassertion callback */
- spisSlaveFifoPushFN pPushFN; /*!< Pointer to current FIFO push function */
- spisSlaveFifoPopFN pPopFN; /*!< Pointer to current FIFO pop function */
- ROM_SPIS_XFER_T *pXfer; /*!< Pointer to current transfer descriptor */
- uint32_t sendIdx; /*!< Current transmit buffer index */
- uint32_t recvIdx; /*!< Current receive buffer index */
- ErrorCode_t pendingStatus; /*!< Pending transfer status */
- } SPIS_DATACONTEXT_T;
- /* Maps config registers bits for SPI mode to the transfer descriptor */
- static const uint32_t spiModeBits[4] = {
- (SPI_CFG_CPOL_LO | SPI_CFG_CPHA_FIRST),
- (SPI_CFG_CPOL_LO | SPI_CFG_CPHA_SECOND),
- (SPI_CFG_CPOL_HI | SPI_CFG_CPHA_FIRST),
- (SPI_CFG_CPOL_HI | SPI_CFG_CPHA_SECOND)
- };
- /* FIFO push function using standard SPI FIFO for datum >8 bits */
- static uint16_t _rom_spisSlaveFifoPush16(LPC_SPI_T *pSPI, void *data, uint32_t numData, uint32_t curIndex)
- {
- uint16_t pushed = 0, *p16 = (uint16_t *) data;
- /* Push as 16-bit value */
- while ((numData > 0) && ((pSPI->STAT & SPI_STAT_TXRDY) != 0)) {
- pSPI->TXDAT = (uint32_t) p16[curIndex];
- numData--;
- curIndex++;
- pushed++;
- }
- return pushed;
- }
- /* FIFO pop function using standard SPI FIFO for datum >8 bits */
- static uint16_t _rom_spisSlaveFifoPop16(LPC_SPI_T *pSPI, void *data, uint32_t numData, uint32_t curIndex)
- {
- uint16_t popped = 0, *p16 = (uint16_t *) data;
- /* Pop as 16-bit value */
- while ((numData > 0) && ((pSPI->STAT & SPI_STAT_RXRDY) != 0)) {
- p16[curIndex] = (uint16_t) pSPI->RXDAT;
- numData--;
- curIndex++;
- popped++;
- }
- return popped;
- }
- /* FIFO push function using standard SPI FIFO for datum <=8 bits */
- static uint16_t _rom_spisSlaveFifoPush8(LPC_SPI_T *pSPI, void *data, uint32_t numData, uint32_t curIndex)
- {
- uint16_t pushed = 0;
- uint8_t *p8 = (uint8_t *) data;
- /* Push as 8-bit value */
- while ((numData > 0) && ((pSPI->STAT & SPI_STAT_TXRDY) != 0)) {
- pSPI->TXDAT = (uint32_t) p8[curIndex];
- numData--;
- curIndex++;
- pushed++;
- }
- return pushed;
- }
- /* FIFO pop function using standard SPI FIFO for datum <=8 bits */
- static uint16_t _rom_spisSlaveFifoPop8(LPC_SPI_T *pSPI, void *data, uint32_t numData, uint32_t curIndex)
- {
- uint16_t popped = 0;
- uint8_t *p8 = (uint8_t *) data;
- /* Pop as 16-bit value */
- while ((numData > 0) && ((pSPI->STAT & SPI_STAT_RXRDY) != 0)) {
- p8[curIndex] = (uint8_t) pSPI->RXDAT;
- numData--;
- curIndex++;
- popped++;
- }
- return popped;
- }
- static void _rom_spisTransmitHandler(ROM_SPIS_HANDLE_T pHandle, ROM_SPIS_XFER_T *pXfer)
- {
- SPIS_DATACONTEXT_T *pDrv = (SPIS_DATACONTEXT_T *) pHandle;
- /* This shouldn't be called if there is no transfer descriptor */
- if (!pXfer) {
- pDrv->base->INTENCLR = SPI_INTENSET_TXDYEN;
- return;
- }
- /* Is DMA being used? */
- if ((pXfer->flags & ROM_SPIS_FLAG_DMATX) != 0) {
- /* Call transmit callback, callback is pre-validated by setup */
- if (pDrv->pTranCb) {
- pDrv->pTranCb(pHandle, pXfer);
- }
- pDrv->base->INTENCLR = SPI_INTENSET_TXDYEN;
- }
- else { /* Transfer without using DMA */
- if ((pXfer->flags & ROM_SPIS_FLAG_TXIGNORE) != 0) {
- /* ROM_SPIS_FLAG_TXIGNORE flag is set. Will send 0xFF */
- static const uint16_t sb = 0xFFFF;
- pDrv->sendIdx += pDrv->pPushFN(pDrv->base, (void *) &sb, 1, 0);
- }
- else {
- uint32_t bs;
- /* Normal data transfer */
- if (pDrv->sendIdx >= pXfer->txSz) {
- /* Ran out of data, get more data */
- if (pDrv->pTranCb) {
- pDrv->pTranCb(pHandle, pXfer);
- pDrv->sendIdx = 0;
- }
- }
- if (pDrv->sendIdx < pXfer->txSz) {
- bs = pDrv->pPushFN(pDrv->base, (void *) pXfer->txBuff, 1, pDrv->sendIdx);
- pDrv->sendIdx += bs;
- pXfer->txSent += bs;
- }
- else {
- /* Out of transmit data and no more is provided via the callback,
- so disable the transmit interrupt and let the hardware underflow
- and send 0. It will only underflow if the master requests data
- beyond what is in the transmit FIFO now. The underflow error code
- needs to be handled as part of the hardware since this may not be
- an underflow condition is the master stops on this datum. */
- pDrv->base->INTENCLR = SPI_INTENSET_TXDYEN;
- }
- }
- }
- }
- static void _rom_spisReceiveHandler(ROM_SPIS_HANDLE_T pHandle, ROM_SPIS_XFER_T *pXfer)
- {
- SPIS_DATACONTEXT_T *pDrv = (SPIS_DATACONTEXT_T *) pHandle;
- /* This shouldn't be called if there is no transfer descriptor */
- if (!pXfer) {
- pDrv->base->INTENCLR = SPI_INTENSET_RXDYEN;
- return;
- }
- /* Is DMA being used? */
- if ((pXfer->flags & ROM_SPIS_FLAG_DMARX) != 0) {
- /* Call receive callback, callback is pre-validated by setup */
- if (pDrv->pRecvCb) {
- pDrv->pRecvCb(pHandle, pXfer);
- }
- pDrv->base->INTENCLR = SPI_INTENSET_RXDYEN;
- }
- else { /* Transfer without using DMA */
- if ((pXfer->flags & ROM_SPIS_FLAG_RXIGNORE) != 0) {
- /* ROM_SPIS_FLAG_RXIGNORE flag is set. Toss data */
- uint16_t temp;
- pDrv->recvIdx += pDrv->pPopFN(pDrv->base, (void *) &temp, 1, 0);
- }
- else {
- uint32_t br;
- /* Normal data transfer */
- if (pDrv->recvIdx >= pXfer->rxSz) {
- uint16_t temp;
- /* Ran out of data, overflowing */
- pDrv->pendingStatus = ERR_SPI_RXOVERRUN;
- br = pDrv->pPopFN(pDrv->base, (void *) &temp, 1, 0);/* Flush data */
- }
- else {
- br = pDrv->pPopFN(pDrv->base, (void *) pXfer->rxBuff, 1, pDrv->recvIdx);
- pXfer->rxRecv += br;
- pDrv->recvIdx += br;
- /* Call callback for more data */
- if ((pDrv->recvIdx >= pXfer->rxSz) && (pDrv->pRecvCb)) {
- pDrv->pRecvCb(pHandle, pXfer);
- pDrv->recvIdx = 0;
- }
- }
- }
- }
- }
- /* Determine SSEL associated with the current data value */
- static uint8_t Chip_SPIS_FindSSEL(LPC_SPI_T *pSPI, uint32_t data)
- {
- int i;
- /* Return first active SSEL starting at SSEL0 */
- for (i = 0; i <= 3; i++) {
- if ((data & SPI_RXDAT_RXSSELNUM_INACTIVE(i)) == 0) {
- /* Signal is active on low */
- return (uint8_t) i;
- }
- }
- return 0;
- }
- // **********************************************************
- uint32_t spis_get_mem_size(void)
- {
- return sizeof(SPIS_DATACONTEXT_T);
- }
- ROM_SPIS_HANDLE_T spis_init(void *mem, const ROM_SPIS_INIT_T *pInit)
- {
- SPIS_DATACONTEXT_T *pDrv;
- int i;
- /* Verify alignment is at least 4 bytes */
- if (((uint32_t) mem & 0x3) != 0) {
- return NULL;
- }
- pDrv = (SPIS_DATACONTEXT_T *) mem;
- memset(pDrv, 0, sizeof(SPIS_DATACONTEXT_T));
- /* Save base of peripheral and pointer to user data */
- pDrv->pUserData = pInit->pUserData;
- pDrv->base = (LPC_SPI_T *) pInit->base;
- /* Enable SPI slave interface */
- pDrv->base->CFG = 0;
- pDrv->base->CFG = SPI_CFG_SPI_EN;
- /* Set SPI slave select (SSEL) polarity for each slave signal */
- for (i = 0; i <= 3; i++) {
- if (pInit->spiPol[i] == 0) {
- /* Active low select, high during idle */
- pDrv->base->CFG &= ~(1 << (8 + i));
- }
- else {
- /* Active high, low during idle */
- pDrv->base->CFG |= (1 << (8 + i));
- }
- }
- /* Enable SPI slave interface, deassert all chip selects (not needed for slave) */
- pDrv->base->TXCTRL = SPI_TXDATCTL_DEASSERT_ALL;
- /* Clear pending slave statuses - RXOV, TXUR, SSA, SSD, EOT */
- pDrv->base->STAT = (SPI_STAT_RXOV | SPI_STAT_TXUR | SPI_STAT_SSA |
- SPI_STAT_SSD | SPI_STAT_EOT);
- return (ROM_SPIS_HANDLE_T) pDrv;
- }
- void spis_register_callback(ROM_SPIS_HANDLE_T pHandle, uint32_t cbIndex, void *pCB)
- {
- SPIS_DATACONTEXT_T *pDrv = (SPIS_DATACONTEXT_T *) pHandle;
- switch (cbIndex) {
- case ROM_SPIS_ASSERTSSEL_CB:
- pDrv->pAssertCb = (spisSlaveXferCSAssertCB) pCB;
- break;
- case ROM_SPIS_DATATRANSMIT_CB:
- pDrv->pTranCb = (spisSlaveTransmitCB) pCB;
- break;
- case ROM_SPIS_DATATRECEIVE_CB:
- pDrv->pRecvCb = (spisSlaveReceiveCB) pCB;
- break;
- case ROM_SPIS_DEASSERTSSEL_CB:
- pDrv->pDeassertCb = (spisSlaveXferCSDeAssertCB) pCB;
- break;
- }
- }
- ErrorCode_t spis_setup_slave(ROM_SPIS_HANDLE_T pHandle, ROM_SPIS_SLAVE_T *pCfg)
- {
- SPIS_DATACONTEXT_T *pDrv = (SPIS_DATACONTEXT_T *) pHandle;
- uint32_t reg;
- /* Verify config is valid */
- if (pCfg == NULL) {
- return ERR_SPI_PARAM;
- }
- /* Verify data parameters area valid */
- if ((pCfg->dataBits == 0) || (pCfg->dataBits > 16)) {
- return ERR_SPI_PARAM;
- }
- /* Verify mode */
- if (pCfg->mode > ROM_SPI_CLOCK_MODE3) {
- return ERR_SPI_PARAM;
- }
- /* Setup transfer mode and LSB/MSB first */
- reg = pDrv->base->CFG;
- reg &= ~(SPI_CFG_LSB_FIRST_EN | SPI_CFG_CPHA_SECOND |
- SPI_CFG_CPOL_HI);
- /* Setup SPI transfer configuration (CFG register) */
- reg |= spiModeBits[(int) pCfg->mode];
- if (pCfg->lsbFirst) {
- reg |= SPI_CFG_LSB_FIRST_EN;
- }
- pDrv->base->CFG = reg;
- /* Setup SPI transfer configuration (TXCTRL) for data size, don't alter SPI SSEL states */
- reg = pDrv->base->TXCTRL & ~SPI_TXCTL_FLEN(16);
- pDrv->base->TXCTRL = reg | SPI_TXCTL_FLEN(pCfg->dataBits);
- /* Setup FIFO callbacks based on the data transfer width */
- if (pCfg->dataBits > 8) {
- pDrv->pPushFN = &_rom_spisSlaveFifoPush16;
- pDrv->pPopFN = &_rom_spisSlaveFifoPop16;
- }
- else {
- pDrv->pPushFN = &_rom_spisSlaveFifoPush8;
- pDrv->pPopFN = &_rom_spisSlaveFifoPop8;
- }
- /* Enable SPI interrupts for slave */
- pDrv->base->INTENSET = SPI_INTENSET_SSAEN | SPI_INTENSET_SSDEN |
- SPI_INTENSET_RXOVEN | SPI_INTENSET_TXUREN;
- return LPC_OK;
- }
- void spis_transfer_handler(ROM_SPIS_HANDLE_T pHandle)
- {
- SPIS_DATACONTEXT_T *pDrv = (SPIS_DATACONTEXT_T *) pHandle;
- ROM_SPIS_XFER_T *pXfer = pDrv->pXfer;
- /* Master asserts slave */
- if ((pDrv->base->INTSTAT & SPI_INTSTAT_SSA) != 0) {
- pDrv->base->STAT = SPI_STAT_SSA;
- /* Slave SSEL assertion callback */
- if (pDrv->pAssertCb != NULL) {
- /* If if takes too long to handle this event, there may also be
- recevied data with the asserted SSEL status. The best way to prevent
- this is to have the master provide a SSEL setup delay between the
- start of the SSEL assertion and the transfer of the first datum, or
- disable the assertion callback and ignore this event. */
- /* Note that setting up transfer buffers based on the slave address is
- the assertion callback may result in a transmit underflow. */
- uint32_t data = pDrv->base->RXDAT;
- pDrv->pAssertCb(pHandle, Chip_SPIS_FindSSEL(pDrv->base, data));
- /* Update transfer descriptor */
- pXfer = pDrv->pXfer;
- }
- /* Disable assertion interrupt, enable deassertion interrupt */
- pDrv->base->INTENCLR = SPI_INTENSET_SSAEN;
- pDrv->base->INTENSET = SPI_INTENSET_SSDEN | SPI_INTENSET_RXDYEN | SPI_INTENSET_TXDYEN;
- }
- /* If overrun or underrun errors occured, latch the error */
- if ((pDrv->base->INTSTAT & SPI_INTSTAT_TXUR) != 0) {
- pDrv->pendingStatus = ERR_SPI_TXUNDERRUN;
- pDrv->base->STAT = SPI_STAT_TXUR;
- }
- /* Receive overflow takes precedence over TX */
- if ((pDrv->base->INTSTAT & SPI_INTSTAT_RXOV) != 0) {
- pDrv->pendingStatus = ERR_SPI_RXOVERRUN;
- pDrv->base->STAT = SPI_STAT_RXOV;
- }
- if ((pDrv->base->INTSTAT & SPI_INTSTAT_TXRDY) != 0) {
- /* Handle transmit */
- _rom_spisTransmitHandler(pHandle, pXfer);
- }
- /* Receive data handler */
- if ((pDrv->base->INTSTAT & SPI_INTSTAT_RXRDY) != 0) {
- /* Handle receive */
- _rom_spisReceiveHandler(pHandle, pXfer);
- }
- /* Slave SSEL de-assertion callback */
- if ((pDrv->base->INTSTAT & SPI_INTSTAT_SSD) != 0) {
- pDrv->base->STAT = SPI_STAT_SSD | SPI_STAT_RXOV | SPI_STAT_TXUR;
- pDrv->base->INTENSET = SPI_INTENSET_SSAEN;
- pDrv->base->INTENCLR = SPI_INTENSET_SSDEN | SPI_INTENSET_RXDYEN | SPI_INTENSET_TXDYEN;
- if (pXfer) {
- pXfer->status = pDrv->pendingStatus;
- pDrv->pXfer = NULL;
- }
- /* On de-assertion, flush any data left in the transmmit and receive FIFO.
- This is needed when streaming data and the size of the transfer isn't
- known, so data may be left to transmit when the master deasserts. */
- pDrv->base->CFG &= ~SPI_CFG_SPI_EN;
- pDrv->base->CFG |= SPI_CFG_SPI_EN;
- /* De-assetion event */
- if (pDrv->pDeassertCb != NULL) {
- pDrv->pDeassertCb(pHandle, pXfer);
- }
- }
- }
- ErrorCode_t spis_transfer(ROM_SPIS_HANDLE_T pHandle, ROM_SPIS_XFER_T *pXfer)
- {
- SPIS_DATACONTEXT_T *pDrv = (SPIS_DATACONTEXT_T *) pHandle;
- uint8_t flen;
- /* Get length of a receive value */
- flen = 1 + (uint8_t) ((pDrv->base->TXCTRL >> 24) & 0xF);
- /* Is transfer descriptor valid? */
- if (pXfer == NULL) {
- return ERR_SPI_PARAM;
- }
- /* No need to check RX buffer alignment if ROM_SPIS_FLAG_RXIGNORE flag is set */
- if ((pXfer->flags & ROM_SPIS_FLAG_RXIGNORE) == 0) {
- if ((pXfer->rxSz == 0) || (pXfer->rxBuff == NULL)) {
- pXfer->status = ERR_SPI_PARAM;
- return ERR_SPI_PARAM;
- }
- if ((flen > 8) && ((((uint32_t) pXfer->rxBuff) & 0x1) != 0)) {
- /* Receive buffer not 16-bit aligned or not present */
- pXfer->status = ERR_SPI_PARAM;
- return ERR_SPI_PARAM;
- }
- /* Is DMA being used? */
- if ((pXfer->flags & ROM_SPIS_FLAG_DMARX) != 0) {
- if (pDrv->pRecvCb == NULL) {
- pXfer->status = ERR_SPI_PARAM;
- return ERR_SPI_PARAM;
- }
- }
- }
- /* Check transmit buffer alignment */
- if ((pXfer->flags & ROM_SPIS_FLAG_TXIGNORE) == 0) {
- if ((pXfer->txSz == 0) || (pXfer->txBuff == NULL)) {
- pXfer->status = ERR_SPI_PARAM;
- return ERR_SPI_PARAM;
- }
- if ((flen > 8) && ((((uint32_t) pXfer->txBuff) & 0x1) != 0)) {
- pXfer->status = ERR_SPI_PARAM;
- return ERR_SPI_PARAM;
- }
- /* Is DMA being used? */
- if ((pXfer->flags & ROM_SPIS_FLAG_DMATX) != 0) {
- if (pDrv->pTranCb == NULL) {
- pXfer->status = ERR_SPI_PARAM;
- return ERR_SPI_PARAM;
- }
- }
- }
- /* ROM_SPIS_FLAG_RXIGNORE flag */
- if ((pXfer->flags & ROM_SPIS_FLAG_RXIGNORE) != 0) {
- pDrv->base->TXCTRL |= SPI_TXCTL_RXIGNORE;
- }
- else {
- pDrv->base->TXCTRL &= ~SPI_TXCTL_RXIGNORE;
- }
- /* Save pointer to transfer descriptor and initial status */
- pDrv->pXfer = pXfer;
- pXfer->status = ERR_SPI_BUSY;
- pDrv->pendingStatus = LPC_OK;
- pDrv->sendIdx = pDrv->recvIdx = pXfer->txSent = pXfer->rxRecv = 0;
- pDrv->base->INTENSET = SPI_INTENSET_RXDYEN | SPI_INTENSET_TXDYEN;
- return pXfer->status;
- }
- uint32_t spis_get_driver_version(void)
- {
- return DRVVERSION;
- }
- // *********************************************************
|