diff options
author | Gregory Nutt <gnutt@nuttx.org> | 2013-09-03 14:59:48 -0600 |
---|---|---|
committer | Gregory Nutt <gnutt@nuttx.org> | 2013-09-03 14:59:48 -0600 |
commit | dceb2d4a91921794ce9277e43c26821816a93135 (patch) | |
tree | e1b86ca39583b5b0cfba274b88dad04216872b76 /nuttx/drivers/wireless/cc3000/spi.c | |
parent | b4bb68d62079c291a1ff35330c82e8f078a117a0 (diff) | |
download | px4-nuttx-dceb2d4a91921794ce9277e43c26821816a93135.tar.gz px4-nuttx-dceb2d4a91921794ce9277e43c26821816a93135.tar.bz2 px4-nuttx-dceb2d4a91921794ce9277e43c26821816a93135.zip |
Initial cut of a driver for the TI CC3000 network module with support on the Freescale KL25Z board from Alan Carvalho de Assis
Diffstat (limited to 'nuttx/drivers/wireless/cc3000/spi.c')
-rw-r--r-- | nuttx/drivers/wireless/cc3000/spi.c | 683 |
1 files changed, 683 insertions, 0 deletions
diff --git a/nuttx/drivers/wireless/cc3000/spi.c b/nuttx/drivers/wireless/cc3000/spi.c new file mode 100644 index 000000000..b7e31ff3b --- /dev/null +++ b/nuttx/drivers/wireless/cc3000/spi.c @@ -0,0 +1,683 @@ +/************************************************************************** +* +* spi. - SPI functions to connect an Arduidno to the TI CC3000 +* +* This code uses the Arduino hardware SPI library (or a bit-banged +* SPI for the Teensy 3.0) to send & receive data between the library +* API calls and the CC3000 hardware. Every +* +* Version 1.0.1b +* +* Copyright (C) 2013 Chris Magagna - cmagagna@yahoo.com +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* Don't sue me if my code blows up your board and burns down your house +* +****************************************************************************/ + +#include <stdio.h> +#include <debug.h> +#include <string.h> +#include <unistd.h> +#include <nuttx/config.h> +#include <nuttx/spi/spi.h> +#include <arch/board/kl_wifi.h> +#include <nuttx/cc3000/hci.h> +#include <nuttx/cc3000/spi.h> +//#include <nuttx/cc3000/ArduinoCC3000Core.h> + +// This flag lets the interrupt handler know if it should respond to +// the WL_SPI_IRQ pin going low or not +short SPIInterruptsEnabled=0; + + +#define READ 3 +#define WRITE 1 + +#define HI(value) (((value) & 0xFF00) >> 8) +#define LO(value) ((value) & 0x00FF) + +#define HEADERS_SIZE_EVNT (SPI_HEADER_SIZE + 5) + +#define SPI_HEADER_SIZE (5) + +#define eSPI_STATE_POWERUP (0) +#define eSPI_STATE_INITIALIZED (1) +#define eSPI_STATE_IDLE (2) +#define eSPI_STATE_WRITE_IRQ (3) +#define eSPI_STATE_WRITE_FIRST_PORTION (4) +#define eSPI_STATE_WRITE_EOT (5) +#define eSPI_STATE_READ_IRQ (6) +#define eSPI_STATE_READ_FIRST_PORTION (7) +#define eSPI_STATE_READ_EOT (8) + +/* !!!HACK!!!*/ +#define KL_PORTA_ISFR 0x400490a0 +#define PIN16 16 +#define getreg32(a) (*(volatile uint32_t *)(a)) +#define putreg32(v,a) (*(volatile uint32_t *)(a) = (v)) + + +#undef SPI_DEBUG /* Define to enable debug */ +#undef SPI_VERBOSE /* Define to enable verbose debug */ + +#ifdef SPI_DEBUG +# define spidbg lldbg +# ifdef SPI_VERBOSE +# define spivdbg lldbg +# else +# define spivdbg(x...) +# endif +#else +# undef SPI_VERBOSE +# define spidbg(x...) +# define spivdbg(x...) +#endif + + +#ifdef CONFIG_KL_SPI0 +void kl_spi0select(FAR struct spi_dev_s *dev, enum spi_dev_e devid, + bool selected) +{ + spivdbg("devid: %d CS: %s\n", + (int)devid, selected ? "assert" : "de-assert"); +} +#endif + +#ifdef CONFIG_KL_SPI1 +void kl_spi1select(FAR struct spi_dev_s *dev, enum spi_dev_e devid, + bool selected) +{ + spivdbg("devid: %d CS: %s\n", + (int)devid, selected ? "assert" : "de-assert"); +} +#endif + + +#ifdef CONFIG_KL_SPI0 +uint8_t kl_spi0status(FAR struct spi_dev_s *dev, enum spi_dev_e devid) +{ + return 0; +} +#endif + +#ifdef CONFIG_KL_SPI1 +uint8_t kl_spi1status(FAR struct spi_dev_s *dev, enum spi_dev_e devid) +{ + return 0; +} +#endif + + + +typedef struct +{ + gcSpiHandleRx SPIRxHandler; + + unsigned short usTxPacketLength; + unsigned short usRxPacketLength; + unsigned long ulSpiState; + unsigned char *pTxPacket; + unsigned char *pRxPacket; + +}tSpiInformation; + + +tSpiInformation sSpiInformation; + +// +// Static buffer for 5 bytes of SPI HEADER +// +unsigned char tSpiReadHeader[] = {READ, 0, 0, 0, 0}; + + +// The magic number that resides at the end of the TX/RX buffer (1 byte after the allocated size) +// for the purpose of detection of the overrun. The location of the memory where the magic number +// resides shall never be written. In case it is written - the overrun occured and either recevie function +// or send function will stuck forever. +#define CC3000_BUFFER_MAGIC_NUMBER (0xDE) + +char spi_buffer[CC3000_RX_BUFFER_SIZE]; +unsigned char wlan_tx_buffer[CC3000_TX_BUFFER_SIZE]; + +struct spi_dev_s *spi = NULL; + +unsigned int SPIPump(unsigned char data) +{ + uint8_t rx; + + printf("SPIPump tx = 0x%X ", data); + + if(!spi){ + spi = up_spiinitialize(1); + SPI_SETBITS(spi, 8); + SPI_SETMODE(spi, SPIDEV_MODE1); + } + + SPI_EXCHANGE(spi, &data, &rx, 1); + + printf(" rx = 0x%X\n", rx); + + return rx; +} + + +//***************************************************************************** +// +//! This function enter point for write flow +//! +//! \param SpiPauseSpi +//! +//! \return none +//! +//! \brief The function triggers a user provided callback for +// +//***************************************************************************** + +void SpiPauseSpi(void) +{ + SPIInterruptsEnabled = 0; +} + + +//***************************************************************************** +// +//! This function enter point for write flow +//! +//! \param SpiResumeSpi +//! +//! \return none +//! +//! \brief The function triggers a user provided callback for +// +//***************************************************************************** + +void SpiResumeSpi(void) +{ + SPIInterruptsEnabled = 1; +} + + +//***************************************************************************** +// +//! This function enter point for write flow +//! +//! \param SpiTriggerRxProcessing +//! +//! \return none +//! +//! \brief The function triggers a user provided callback for +// +//***************************************************************************** +void SpiTriggerRxProcessing(void) +{ + // + // Trigger Rx processing + // + SpiPauseSpi(); + DeassertWlanCS(); + + // The magic number that resides at the end of the TX/RX buffer (1 byte after the allocated size) + // for the purpose of detection of the overrun. If the magic number is overriten - buffer overrun + // occurred - and we will stuck here forever! + if (sSpiInformation.pRxPacket[CC3000_RX_BUFFER_SIZE - 1] != CC3000_BUFFER_MAGIC_NUMBER) + { + while (1) + ; + } + + sSpiInformation.ulSpiState = eSPI_STATE_IDLE; + sSpiInformation.SPIRxHandler(sSpiInformation.pRxPacket + SPI_HEADER_SIZE); +} + + +//***************************************************************************** +// +//! This function enter point for write flow +//! +//! \param buffer +//! +//! \return none +//! +//! \brief ... +// +//***************************************************************************** +void SpiReadDataSynchronous(unsigned char *data, unsigned short size) +{ + long i = 0; + unsigned char *data_to_send = tSpiReadHeader; + + for (i = 0; i < size; i ++) { + data[i] = SPIPump(data_to_send[0]); + } +} + + +//***************************************************************************** +// +//! This function enter point for write flow +//! +//! \param buffer +//! +//! \return none +//! +//! \brief ... +// +//***************************************************************************** +void SpiWriteDataSynchronous(unsigned char *data, unsigned short size) +{ + + while (size) + { + SPIPump(*data); + size --; + data++; + } +} + + +//***************************************************************************** +// +//! This function enter point for write flow +//! +//! \param buffer +//! +//! \return none +//! +//! \brief ... +// +//***************************************************************************** +long SpiFirstWrite(unsigned char *ucBuf, unsigned short usLength) +{ + // + // workaround for first transaction + // + int i = 0, j = 1; + AssertWlanCS(); + + usleep(70); + + // SPI writes first 4 bytes of data + SpiWriteDataSynchronous(ucBuf, 4); + + usleep(70); + + SpiWriteDataSynchronous(ucBuf + 4, usLength - 4); + + sSpiInformation.ulSpiState = eSPI_STATE_IDLE; + + DeassertWlanCS(); + + //printf("Executed SpiFirstWrite!\n"); + + return(0); +} + + +//***************************************************************************** +// +//! This function enter point for write flow +//! +//! \param buffer +//! +//! \return none +//! +//! \brief ... +// +//***************************************************************************** +long SpiWrite(unsigned char *pUserBuffer, unsigned short usLength) +{ + unsigned char ucPad = 0; + + // + // Figure out the total length of the packet in order to figure out if there is padding or not + // + if(!(usLength & 0x0001)) + { + ucPad++; + } + + + pUserBuffer[0] = WRITE; + pUserBuffer[1] = HI(usLength + ucPad); + pUserBuffer[2] = LO(usLength + ucPad); + pUserBuffer[3] = 0; + pUserBuffer[4] = 0; + + usLength += (SPI_HEADER_SIZE + ucPad); + + // The magic number that resides at the end of the TX/RX buffer (1 byte after the allocated size) + // for the purpose of overrun detection. If the magic number is overwritten - buffer overrun + // occurred - and we will be stuck here forever! + if (wlan_tx_buffer[CC3000_TX_BUFFER_SIZE - 1] != CC3000_BUFFER_MAGIC_NUMBER) + { + while (1) + ; + } + + if (sSpiInformation.ulSpiState == eSPI_STATE_POWERUP) + { + while (sSpiInformation.ulSpiState != eSPI_STATE_INITIALIZED) { + } + ; + } + + if (sSpiInformation.ulSpiState == eSPI_STATE_INITIALIZED) + { + // + // This is time for first TX/RX transactions over SPI: + // the IRQ is down - so need to send read buffer size command + // + SpiFirstWrite(pUserBuffer, usLength); + } + else + { + // + // We need to prevent here race that can occur in case two back to back packets are sent to the + // device, so the state will move to IDLE and once again to not IDLE due to IRQ + // + tSLInformation.WlanInterruptDisable(); + + while (sSpiInformation.ulSpiState != eSPI_STATE_IDLE) + { + ; + } + + + sSpiInformation.ulSpiState = eSPI_STATE_WRITE_IRQ; + sSpiInformation.pTxPacket = pUserBuffer; + sSpiInformation.usTxPacketLength = usLength; + + // + // Assert the CS line and wait till SSI IRQ line is active and then initialize write operation + // + AssertWlanCS(); + + // + // Re-enable IRQ - if it was not disabled - this is not a problem... + // + tSLInformation.WlanInterruptEnable(); + + // + // check for a missing interrupt between the CS assertion and enabling back the interrupts + // + if (tSLInformation.ReadWlanInterruptPin() == 0) + { + SpiWriteDataSynchronous(sSpiInformation.pTxPacket, sSpiInformation.usTxPacketLength); + + sSpiInformation.ulSpiState = eSPI_STATE_IDLE; + + DeassertWlanCS(); + } + } + + + // + // Due to the fact that we are currently implementing a blocking situation + // here we will wait till end of transaction + // + + while (eSPI_STATE_IDLE != sSpiInformation.ulSpiState) + ; + + return(0); +} + + +//***************************************************************************** +// +//! This function processes received SPI Header and in accordance with it - continues reading +//! the packet +//! +//! \param None +//! +//! \return None +//! +//! \brief ... +// +//***************************************************************************** +long SpiReadDataCont(void) +{ + long data_to_recv; + unsigned char *evnt_buff, type; + + + // + //determine what type of packet we have + // + evnt_buff = sSpiInformation.pRxPacket; + data_to_recv = 0; + STREAM_TO_UINT8((char *)(evnt_buff + SPI_HEADER_SIZE), HCI_PACKET_TYPE_OFFSET, type); + + switch(type) + { + case HCI_TYPE_DATA: + { + // + // We need to read the rest of data.. + // + STREAM_TO_UINT16((char *)(evnt_buff + SPI_HEADER_SIZE), HCI_DATA_LENGTH_OFFSET, data_to_recv); + if (!((HEADERS_SIZE_EVNT + data_to_recv) & 1)) + { + data_to_recv++; + } + + if (data_to_recv) + { + SpiReadDataSynchronous(evnt_buff + 10, data_to_recv); + } + break; + } + case HCI_TYPE_EVNT: + { + // + // Calculate the rest length of the data + // + STREAM_TO_UINT8((char *)(evnt_buff + SPI_HEADER_SIZE), HCI_EVENT_LENGTH_OFFSET, data_to_recv); + data_to_recv -= 1; + + // + // Add padding byte if needed + // + if ((HEADERS_SIZE_EVNT + data_to_recv) & 1) + { + + data_to_recv++; + } + + if (data_to_recv) + { + SpiReadDataSynchronous(evnt_buff + 10, data_to_recv); + } + + sSpiInformation.ulSpiState = eSPI_STATE_READ_EOT; + break; + } + } + + return (0); +} + + +//***************************************************************************** +// +//! This function enter point for write flow +//! +//! \param SSIContReadOperation +//! +//! \return none +//! +//! \brief The function triggers a user provided callback for +// +//***************************************************************************** + +void SSIContReadOperation(void) +{ + // + // The header was read - continue with the payload read + // + if (!SpiReadDataCont()) + { + + + // + // All the data was read - finalize handling by switching to teh task + // and calling from task Event Handler + // + SpiTriggerRxProcessing(); + } +} + + +//***************************************************************************** +// +//! This function enter point for read flow: first we read minimal 5 SPI header bytes and 5 Event +//! Data bytes +//! +//! \param buffer +//! +//! \return none +//! +//! \brief ... +// +//***************************************************************************** +void SpiReadHeader(void) +{ + SpiReadDataSynchronous(sSpiInformation.pRxPacket, 10); +} + + +//***************************************************************************** +// +//! The IntSpiGPIOHandler interrupt handler +//! +//! \param none +//! +//! \return none +//! +//! \brief GPIO A interrupt handler. When the external SSI WLAN device is +//! ready to interact with Host CPU it generates an interrupt signal. +//! After that Host CPU has registrated this interrupt request +//! it set the corresponding /CS in active state. +// +//***************************************************************************** +//#pragma vector=PORT2_VECTOR +//__interrupt void IntSpiGPIOHandler(void) +int CC3000InterruptHandler(int irq, void *context) +{ + + uint32_t regval = 0; + + regval = getreg32(KL_PORTA_ISFR); + if (regval & (1 << PIN16)) + { + //printf("\nAn interrupt was issued!\n"); + + if (!SPIInterruptsEnabled) { + goto out; + } + + //printf("\nSPIInterrupt was enabled!\n"); + + if (sSpiInformation.ulSpiState == eSPI_STATE_POWERUP) + { + /* This means IRQ line was low call a callback of HCI Layer to inform on event */ + sSpiInformation.ulSpiState = eSPI_STATE_INITIALIZED; + } + else if (sSpiInformation.ulSpiState == eSPI_STATE_IDLE) + { + sSpiInformation.ulSpiState = eSPI_STATE_READ_IRQ; + + /* IRQ line goes down - start reception */ + AssertWlanCS(); + + // + // Wait for TX/RX Complete which will come as DMA interrupt + // + SpiReadHeader(); + + sSpiInformation.ulSpiState = eSPI_STATE_READ_EOT; + + SSIContReadOperation(); + } + else if (sSpiInformation.ulSpiState == eSPI_STATE_WRITE_IRQ) + { + + SpiWriteDataSynchronous(sSpiInformation.pTxPacket, sSpiInformation.usTxPacketLength); + + sSpiInformation.ulSpiState = eSPI_STATE_IDLE; + + DeassertWlanCS(); + } + else { + } + +out: + regval = (1 << PIN16); + putreg32(regval, KL_PORTA_ISFR); + } + return 0; +} + + +//***************************************************************************** +// +//! SpiClose +//! +//! \param none +//! +//! \return none +//! +//! \brief Cofigure the SSI +// +//***************************************************************************** +void SpiOpen(gcSpiHandleRx pfRxHandler) +{ + sSpiInformation.ulSpiState = eSPI_STATE_POWERUP; + + memset(spi_buffer, 0, sizeof(spi_buffer)); + memset(wlan_tx_buffer, 0, sizeof(spi_buffer)); + + sSpiInformation.SPIRxHandler = pfRxHandler; + sSpiInformation.usTxPacketLength = 0; + sSpiInformation.pTxPacket = NULL; + sSpiInformation.pRxPacket = (unsigned char *)spi_buffer; + sSpiInformation.usRxPacketLength = 0; + spi_buffer[CC3000_RX_BUFFER_SIZE - 1] = CC3000_BUFFER_MAGIC_NUMBER; + wlan_tx_buffer[CC3000_TX_BUFFER_SIZE - 1] = CC3000_BUFFER_MAGIC_NUMBER; + + // + // Enable interrupt on the GPIO pin of WLAN IRQ + // + tSLInformation.WlanInterruptEnable(); + +} + + +//***************************************************************************** +// +//! SpiClose +//! +//! \param none +//! +//! \return none +//! +//! \brief Cofigure the SSI +// +//***************************************************************************** +void SpiClose(void) +{ + if (sSpiInformation.pRxPacket) + { + sSpiInformation.pRxPacket = 0; + } + + // + // Disable Interrupt in GPIOA module... + // + tSLInformation.WlanInterruptDisable(); +} + |