nuttx/arch/arm/src/lpc17xx_40xx/lpc17_40_ssp.c
jjlange e5dfd805e6 Merged in jjlange/nuttx/lpc40xx (pull request #946)
Add support for LPC40xx family chips

* Corrected a few peripheral definitions and pin functions for the LPC17xx family.
    Added configuration options, chip definitions, and additional pin functions for the LPC40xx family.
    Added board configurations for Embedded Artists LPC4088 Quickstart board and LPC4088 Developer's kit.  These configurations are still something of a work in progress.  In particular, the LCD functionality is untested.

* First pass rename in *.c and *.h files.

* Renamed LPC17XX to LPC17XX_40XX in config files

* Rplaced LPC17xx with LPC17xx/LPC40xx in .c files

* Replaced LPC17xx with LPC17xx/LPC40xx in .h files

* Updated some documentation

* Working on moving directories

* moved arch/arm/src/lpc17xx and arch/arm/include/lpc17xx to lpc17xx_40xx

* Renamed LPC17_* constants / configuration options to LPC17_40_*

* Updated chip family name defines

* Renamed some chip-specific files

* Updated references to renamed files

* Updated references to lpc17_ to lpc17_40_

* Renamed source files from lpc17_* to lpc17_40_*

* Clean up white space

Approved-by: Gregory Nutt <gnutt@nuttx.org>
2019-07-11 16:50:00 +00:00

1047 lines
30 KiB
C

/****************************************************************************
* arch/arm/src/lpc17xx_40xx/lpc17_40_ssp.c
*
* Copyright (C) 2010-2013, 2016-2017 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
* 3. Neither the name NuttX 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.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <semaphore.h>
#include <errno.h>
#include <debug.h>
#include <arch/board/board.h>
#include <nuttx/irq.h>
#include <nuttx/arch.h>
#include <nuttx/spi/spi.h>
#include "up_internal.h"
#include "up_arch.h"
#include "chip.h"
#include "hardware/lpc17_40_syscon.h"
#include "lpc17_40_gpio.h"
#include "lpc17_40_ssp.h"
#if defined(CONFIG_LPC17_40_SSP0) || defined(CONFIG_LPC17_40_SSP1) || \
defined(CONFIG_LPC17_40_SSP2)
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Configuration ************************************************************/
/* This driver does not support the SPI exchange method. */
#ifdef CONFIG_SPI_EXCHANGE
# error "CONFIG_SPI_EXCHANGE must not be defined in the configuration"
#endif
/* SSP Clocking *************************************************************/
#if defined(LPC176x)
/* The CPU clock by 1, 2, 4, or 8 to get the SSP peripheral clock (SSP_CLOCK).
* SSP_CLOCK may be further divided by 2-254 to get the SSP clock. If we
* want a usable range of 4KHz to 25MHz for the SSP, then:
*
* 1. SSPCLK must be greater than (2*25MHz) = 50MHz, and
* 2. SSPCLK must be less than (254*40Khz) = 101.6MHz.
*
* If we assume that CCLK less than or equal to 100MHz, we can just
* use the CCLK undivided to get the SSP_CLOCK.
*/
# if LPC17_40_CCLK > 100000000
# error "CCLK <= 100,000,000 assumed"
# endif
# define SSP_PCLKSET_DIV SYSCON_PCLKSEL_CCLK
# define SSP_CLOCK LPC17_40_CCLK
#elif defined(LPC178x_40xx)
/* All peripherals are clocked by the same peripheral clock in the LPC178x/40xx
* family.
*/
# define SSP_CLOCK BOARD_PCLK_FREQUENCY
#endif
/****************************************************************************
* Private Types
****************************************************************************/
/* This structure descibes the state of the SSP driver */
struct lpc17_40_sspdev_s
{
struct spi_dev_s spidev; /* Externally visible part of the SPI interface */
uint32_t sspbase; /* SPIn base address */
#ifdef CONFIG_LPC17_40_SSP_INTERRUPTS
uint8_t sspirq; /* SPI IRQ number */
#endif
sem_t exclsem; /* Held while chip is selected for mutual exclusion */
uint32_t frequency; /* Requested clock frequency */
uint32_t actual; /* Actual clock frequency */
uint8_t nbits; /* Width of word in bits (4 to 16) */
uint8_t mode; /* Mode 0,1,2,3 */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* Helpers */
static inline uint32_t ssp_getreg(FAR struct lpc17_40_sspdev_s *priv, uint8_t offset);
static inline void ssp_putreg(FAR struct lpc17_40_sspdev_s *priv, uint8_t offset,
uint32_t value);
/* SPI methods */
static int ssp_lock(FAR struct spi_dev_s *dev, bool lock);
static uint32_t ssp_setfrequency(FAR struct spi_dev_s *dev, uint32_t frequency);
static void ssp_setmode(FAR struct spi_dev_s *dev, enum spi_mode_e mode);
static void ssp_setbits(FAR struct spi_dev_s *dev, int nbits);
static uint16_t ssp_send(FAR struct spi_dev_s *dev, uint16_t ch);
static void ssp_sndblock(FAR struct spi_dev_s *dev, FAR const void *buffer, size_t nwords);
static void ssp_recvblock(FAR struct spi_dev_s *dev, FAR void *buffer, size_t nwords);
/* Initialization */
#ifdef CONFIG_LPC17_40_SSP0
static inline FAR struct lpc17_40_sspdev_s *lpc17_40_ssp0initialize(void);
#endif
#ifdef CONFIG_LPC17_40_SSP1
static inline FAR struct lpc17_40_sspdev_s *lpc17_40_ssp1initialize(void);
#endif
#ifdef CONFIG_LPC17_40_SSP2
static inline FAR struct lpc17_40_sspdev_s *lpc17_40_ssp2initialize(void);
#endif
/****************************************************************************
* Private Data
****************************************************************************/
#ifdef CONFIG_LPC17_40_SSP0
static const struct spi_ops_s g_spi0ops =
{
.lock = ssp_lock,
.select = lpc17_40_ssp0select, /* Provided externally */
.setfrequency = ssp_setfrequency,
.setmode = ssp_setmode,
.setbits = ssp_setbits,
#ifdef CONFIG_SPI_HWFEATURES
.hwfeatures = 0, /* Not supported */
#endif
.status = lpc17_40_ssp0status, /* Provided externally */
#ifdef CONFIG_SPI_CMDDATA
.cmddata = lpc17_40_ssp0cmddata, /* Provided externally */
#endif
.send = ssp_send,
.sndblock = ssp_sndblock,
.recvblock = ssp_recvblock,
#ifdef CONFIG_SPI_CALLBACK
.registercallback = lpc17_40_ssp0register, /* Provided externally */
#else
.registercallback = 0, /* Not implemented */
#endif
};
static struct lpc17_40_sspdev_s g_ssp0dev =
{
.spidev = { &g_spi0ops },
.sspbase = LPC17_40_SSP0_BASE,
#ifdef CONFIG_LPC17_40_SSP_INTERRUPTS
.sspirq = LPC17_40_IRQ_SSP0,
#endif
};
#endif /* CONFIG_LPC17_40_SSP0 */
#ifdef CONFIG_LPC17_40_SSP1
static const struct spi_ops_s g_spi1ops =
{
.lock = ssp_lock,
.select = lpc17_40_ssp1select, /* Provided externally */
.setfrequency = ssp_setfrequency,
.setmode = ssp_setmode,
.setbits = ssp_setbits,
.status = lpc17_40_ssp1status, /* Provided externally */
#ifdef CONFIG_SPI_CMDDATA
.cmddata = lpc17_40_ssp1cmddata, /* Provided externally */
#endif
.send = ssp_send,
.sndblock = ssp_sndblock,
.recvblock = ssp_recvblock,
#ifdef CONFIG_SPI_CALLBACK
.registercallback = lpc17_40_ssp1register, /* Provided externally */
#else
.registercallback = 0, /* Not implemented */
#endif
};
static struct lpc17_40_sspdev_s g_ssp1dev =
{
.spidev = { &g_spi1ops },
.sspbase = LPC17_40_SSP1_BASE,
#ifdef CONFIG_LPC17_40_SSP_INTERRUPTS
.sspirq = LPC17_40_IRQ_SSP1,
#endif
};
#endif /* CONFIG_LPC17_40_SSP1 */
#ifdef CONFIG_LPC17_40_SSP2
static const struct spi_ops_s g_spi2ops =
{
.lock = ssp_lock,
.select = lpc17_40_ssp2select, /* Provided externally */
.setfrequency = ssp_setfrequency,
.setmode = ssp_setmode,
.setbits = ssp_setbits,
.status = lpc17_40_ssp2status, /* Provided externally */
#ifdef CONFIG_SPI_CMDDATA
.cmddata = lpc17_40_ssp2cmddata, /* Provided externally */
#endif
.send = ssp_send,
.sndblock = ssp_sndblock,
.recvblock = ssp_recvblock,
#ifdef CONFIG_SPI_CALLBACK
.registercallback = lpc17_40_ssp2register, /* Provided externally */
#else
.registercallback = 0, /* Not implemented */
#endif
};
static struct lpc17_40_sspdev_s g_ssp2dev =
{
.spidev = { &g_spi2ops },
.sspbase = LPC17_40_SSP2_BASE,
#ifdef CONFIG_LPC17_40_SSP_INTERRUPTS
.sspirq = LPC17_40_IRQ_SSP2,
#endif
};
#endif /* CONFIG_LPC17_40_SSP2 */
/****************************************************************************
* Public Data
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: ssp_getreg
*
* Description:
* Get the contents of the SPI register at offset
*
* Input Parameters:
* priv - private SPI device structure
* offset - offset to the register of interest
*
* Returned Value:
* The contents of the 32-bit register
*
****************************************************************************/
static inline uint32_t ssp_getreg(FAR struct lpc17_40_sspdev_s *priv, uint8_t offset)
{
return getreg32(priv->sspbase + (uint32_t)offset);
}
/****************************************************************************
* Name: ssp_putreg
*
* Description:
* Write a 32-bit value to the SPI register at offset
*
* Input Parameters:
* priv - private SPI device structure
* offset - offset to the register of interest
* value - the 16-bit value to be written
*
* Returned Value:
* None
*
****************************************************************************/
static inline void ssp_putreg(FAR struct lpc17_40_sspdev_s *priv, uint8_t offset, uint32_t value)
{
putreg32(value, priv->sspbase + (uint32_t)offset);
}
/****************************************************************************
* Name: ssp_lock
*
* Description:
* On SPI busses where there are multiple devices, it will be necessary to
* lock SPI to have exclusive access to the busses for a sequence of
* transfers. The bus should be locked before the chip is selected. After
* locking the SPI bus, the caller should then also call the setfrequency,
* setbits, and setmode methods to make sure that the SPI is properly
* configured for the device. If the SPI buss is being shared, then it
* may have been left in an incompatible state.
*
* Input Parameters:
* dev - Device-specific state data
* lock - true: Lock spi bus, false: unlock SPI bus
*
* Returned Value:
* None
*
****************************************************************************/
static int ssp_lock(FAR struct spi_dev_s *dev, bool lock)
{
FAR struct lpc17_40_sspdev_s *priv = (FAR struct lpc17_40_sspdev_s *)dev;
int ret;
if (lock)
{
/* Take the semaphore (perhaps waiting) */
do
{
ret = nxsem_wait(&priv->exclsem);
/* The only case that an error should occur here is if the wait
* was awakened by a signal.
*/
DEBUGASSERT(ret == OK || ret == -EINTR);
}
while (ret == -EINTR);
}
else
{
(void)nxsem_post(&priv->exclsem);
ret = OK;
}
return ret;
}
/****************************************************************************
* Name: ssp_setfrequency
*
* Description:
* Set the SPI frequency.
*
* Input Parameters:
* dev - Device-specific state data
* frequency - The SPI frequency requested
*
* Returned Value:
* Returns the actual frequency selected
*
****************************************************************************/
static uint32_t ssp_setfrequency(FAR struct spi_dev_s *dev, uint32_t frequency)
{
FAR struct lpc17_40_sspdev_s *priv = (FAR struct lpc17_40_sspdev_s *)dev;
uint32_t cpsdvsr;
uint32_t scr;
uint32_t regval;
uint32_t actual;
/* Check if the requested frequency is the same as the frequency selection */
DEBUGASSERT(priv && frequency <= SSP_CLOCK / 2);
if (priv->frequency == frequency)
{
/* We are already at this frequency. Return the actual. */
return priv->actual;
}
/* The SSP bit frequency is given by:
*
* frequency = SSP_CLOCK / (CPSDVSR * (SCR+1)).
*
* Let's try for a solution with the smallest value of SCR. NOTES:
* (1) In the calculations below, the value of the variable 'scr' is
* (SCR+1) in the above equation. (2) On slower LPC17xx/LPC40xx parts, SCR
* will probably always be zero.
*/
for (scr = 1; scr <= 256; scr++)
{
/* CPSDVSR = SSP_CLOCK / (SCR + 1) / frequency */
cpsdvsr = SSP_CLOCK / (scr * frequency);
/* Break out on the first solution we find with the smallest value
* of SCR and with CPSDVSR within the maximum range or 254.
*/
if (cpsdvsr < 255)
{
break;
}
}
DEBUGASSERT(scr <= 256 && cpsdvsr <= 255);
/* "In master mode, CPSDVSRmin = 2 or larger (even numbers only)" */
if (cpsdvsr < 2)
{
/* Clip to the minimum value. */
cpsdvsr = 2;
}
else if (cpsdvsr > 254)
{
/* This should never happen */
cpsdvsr = 254;
}
/* Force even */
cpsdvsr = (cpsdvsr + 1) & ~1;
/* Save the new CPSDVSR and SCR values */
ssp_putreg(priv, LPC17_40_SSP_CPSR_OFFSET, cpsdvsr);
regval = ssp_getreg(priv, LPC17_40_SSP_CR0_OFFSET);
regval &= ~SSP_CR0_SCR_MASK;
regval |= ((scr - 1) << SSP_CR0_SCR_SHIFT);
ssp_putreg(priv, LPC17_40_SSP_CR0_OFFSET, regval);
/* Calculate the new actual */
actual = SSP_CLOCK / (cpsdvsr * scr);
/* Save the frequency setting */
priv->frequency = frequency;
priv->actual = actual;
spiinfo("Frequency %d->%d\n", frequency, actual);
return actual;
}
/****************************************************************************
* Name: ssp_setmode
*
* Description:
* Set the SPI mode. Optional. See enum spi_mode_e for mode definitions
*
* Input Parameters:
* dev - Device-specific state data
* mode - The SPI mode requested
*
* Returned Value:
* none
*
****************************************************************************/
static void ssp_setmode(FAR struct spi_dev_s *dev, enum spi_mode_e mode)
{
FAR struct lpc17_40_sspdev_s *priv = (FAR struct lpc17_40_sspdev_s *)dev;
uint32_t regval;
/* Has the mode changed? */
if (mode != priv->mode)
{
/* Yes... Set CR0 appropriately */
regval = ssp_getreg(priv, LPC17_40_SSP_CR0_OFFSET);
regval &= ~(SSP_CR0_CPOL | SSP_CR0_CPHA);
switch (mode)
{
case SPIDEV_MODE0: /* CPOL=0; CPHA=0 */
break;
case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */
regval |= SSP_CR0_CPHA;
break;
case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */
regval |= SSP_CR0_CPOL;
break;
case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */
regval |= (SSP_CR0_CPOL | SSP_CR0_CPHA);
break;
default:
spierr("ERROR: Bad mode: %d\n", mode);
DEBUGASSERT(FALSE);
return;
}
ssp_putreg(priv, LPC17_40_SSP_CR0_OFFSET, regval);
/* Save the mode so that subsequent re-configurations will be faster */
priv->mode = mode;
}
}
/****************************************************************************
* Name: ssp_setbits
*
* Description:
* Set the number if bits per word.
*
* Input Parameters:
* dev - Device-specific state data
* nbits - The number of bits requests
*
* Returned Value:
* none
*
****************************************************************************/
static void ssp_setbits(FAR struct spi_dev_s *dev, int nbits)
{
FAR struct lpc17_40_sspdev_s *priv = (FAR struct lpc17_40_sspdev_s *)dev;
uint32_t regval;
/* Has the number of bits changed? */
DEBUGASSERT(priv && nbits > 3 && nbits < 17);
if (nbits != priv->nbits)
{
/* Yes... Set CR1 appropriately */
regval = ssp_getreg(priv, LPC17_40_SSP_CR0_OFFSET);
regval &= ~SSP_CR0_DSS_MASK;
regval |= ((nbits - 1) << SSP_CR0_DSS_SHIFT);
ssp_putreg(priv, LPC17_40_SSP_CR0_OFFSET, regval);
/* Save the selection so the subsequence re-configurations will be faster */
priv->nbits = nbits;
}
}
/****************************************************************************
* Name: ssp_send
*
* Description:
* Exchange one word on SPI
*
* Input Parameters:
* dev - Device-specific state data
* wd - The word to send. the size of the data is determined by the
* number of bits selected for the SPI interface.
*
* Returned Value:
* response
*
****************************************************************************/
static uint16_t ssp_send(FAR struct spi_dev_s *dev, uint16_t wd)
{
FAR struct lpc17_40_sspdev_s *priv = (FAR struct lpc17_40_sspdev_s *)dev;
register uint32_t regval;
/* Wait while the TX FIFO is full */
while (!(ssp_getreg(priv, LPC17_40_SSP_SR_OFFSET) & SSP_SR_TNF));
/* Write the byte to the TX FIFO */
ssp_putreg(priv, LPC17_40_SSP_DR_OFFSET, (uint32_t)wd);
/* Wait for the RX FIFO not empty */
while (!(ssp_getreg(priv, LPC17_40_SSP_SR_OFFSET) & SSP_SR_RNE));
/* Get the value from the RX FIFO and return it */
regval = ssp_getreg(priv, LPC17_40_SSP_DR_OFFSET);
spiinfo("%04x->%04x\n", wd, regval);
return (uint16_t)regval;
}
/****************************************************************************
* Name: ssp_sndblock
*
* Description:
* Send a block of data on SPI
*
* Input Parameters:
* dev - Device-specific state data
* buffer - A pointer to the buffer of data to be sent
* nwords - the length of data to send from the buffer in number of words.
* The wordsize is determined by the number of bits-per-word
* selected for the SPI interface. If nbits <= 8, the data is
* packed into uint8_t's; if nbits >8, the data is packed into uint16_t's
*
* Returned Value:
* None
*
****************************************************************************/
static void ssp_sndblock(FAR struct spi_dev_s *dev, FAR const void *buffer, size_t nwords)
{
FAR struct lpc17_40_sspdev_s *priv = (FAR struct lpc17_40_sspdev_s *)dev;
union
{
FAR const uint8_t *p8;
FAR const uint16_t *p16;
FAR const void *pv;
} u;
uint32_t data;
uint32_t sr;
/* Loop while thre are bytes remaining to be sent */
spiinfo("nwords: %d\n", nwords);
u.pv = buffer;
while (nwords > 0)
{
/* While the TX FIFO is not full and there are bytes left to send */
while ((ssp_getreg(priv, LPC17_40_SSP_SR_OFFSET) & SSP_SR_TNF) && nwords)
{
/* Fetch the data to send */
if (priv->nbits > 8)
{
data = (uint32_t)*u.p16++;
}
else
{
data = (uint32_t)*u.p8++;
}
/* Send the data */
ssp_putreg(priv, LPC17_40_SSP_DR_OFFSET, data);
nwords--;
}
}
/* Then discard all card responses until the RX & TX FIFOs are emptied. */
spiinfo("discarding\n");
do
{
/* Is there anything in the RX fifo? */
sr = ssp_getreg(priv, LPC17_40_SSP_SR_OFFSET);
if ((sr & SSP_SR_RNE) != 0)
{
/* Yes.. Read and discard */
(void)ssp_getreg(priv, LPC17_40_SSP_DR_OFFSET);
}
/* There is a race condition where TFE may go true just before
* RNE goes true and this loop terminates prematurely. The nasty little
* delay in the following solves that (it could probably be tuned
* to improve performance).
*/
else if ((sr & SSP_SR_TFE) != 0)
{
up_udelay(100);
sr = ssp_getreg(priv, LPC17_40_SSP_SR_OFFSET);
}
}
while ((sr & SSP_SR_RNE) != 0 || (sr & SSP_SR_TFE) == 0);
}
/****************************************************************************
* Name: ssp_recvblock
*
* Description:
* Revice a block of data from SPI
*
* Input Parameters:
* dev - Device-specific state data
* buffer - A pointer to the buffer in which to recieve data
* nwords - the length of data that can be received in the buffer in number
* of words. The wordsize is determined by the number of bits-per-word
* selected for the SPI interface. If nbits <= 8, the data is
* packed into uint8_t's; if nbits >8, the data is packed into uint16_t's
*
* Returned Value:
* None
*
****************************************************************************/
static void ssp_recvblock(FAR struct spi_dev_s *dev, FAR void *buffer, size_t nwords)
{
FAR struct lpc17_40_sspdev_s *priv = (FAR struct lpc17_40_sspdev_s *)dev;
union
{
FAR uint8_t *p8;
FAR uint16_t *p16;
FAR void *pv;
} u;
uint32_t data;
uint32_t rxpending = 0;
/* While there is remaining to be sent (and no synchronization error has occurred) */
spiinfo("nwords: %d\n", nwords);
u.pv = buffer;
while (nwords || rxpending)
{
/* Fill the transmit FIFO with 0xffff...
* Write 0xff to the data register while (1) the TX FIFO is
* not full, (2) we have not exceeded the depth of the TX FIFO,
* and (3) there are more bytes to be sent.
*/
spiinfo("TX: rxpending: %d nwords: %d\n", rxpending, nwords);
while ((ssp_getreg(priv, LPC17_40_SSP_SR_OFFSET) & SSP_SR_TNF) &&
(rxpending < LPC17_40_SSP_FIFOSZ) && nwords)
{
ssp_putreg(priv, LPC17_40_SSP_DR_OFFSET, 0xffff);
nwords--;
rxpending++;
}
/* Now, read the RX data from the RX FIFO while the RX FIFO is not empty */
spiinfo("RX: rxpending: %d\n", rxpending);
while (ssp_getreg(priv, LPC17_40_SSP_SR_OFFSET) & SSP_SR_RNE)
{
data = (uint8_t)ssp_getreg(priv, LPC17_40_SSP_DR_OFFSET);
if (priv->nbits > 8)
{
*u.p16++ = (uint16_t)data;
}
else
{
*u.p8++ = (uint8_t)data;
}
rxpending--;
}
}
}
/****************************************************************************
* Name: lpc17_40_ssp0initialize
*
* Description:
* Initialize the SSP0
*
* Input Parameters:
* None
*
* Returned Value:
* Valid SPI device structure reference on succcess; a NULL on failure
*
****************************************************************************/
#ifdef CONFIG_LPC17_40_SSP0
static inline FAR struct lpc17_40_sspdev_s *lpc17_40_ssp0initialize(void)
{
irqstate_t flags;
uint32_t regval;
/* Configure multiplexed pins as connected on the board. Chip select
* pins must be configured by board-specific logic. All SSP0 pins and
* one SSP1 pin (SCK) have multiple, alternative pin selection.
* Definitions in the board.h file must be provided to resolve the
* board-specific pin configuration like:
*
* #define GPIO_SSP0_SCK GPIO_SSP0_SCK_1
*/
flags = enter_critical_section();
lpc17_40_configgpio(GPIO_SSP0_SCK);
lpc17_40_configgpio(GPIO_SSP0_MISO);
lpc17_40_configgpio(GPIO_SSP0_MOSI);
/* Configure clocking */
#ifdef LPC176x
regval = getreg32(LPC17_40_SYSCON_PCLKSEL1);
regval &= ~SYSCON_PCLKSEL1_SSP0_MASK;
regval |= (SSP_PCLKSET_DIV << SYSCON_PCLKSEL1_SSP0_SHIFT);
putreg32(regval, LPC17_40_SYSCON_PCLKSEL1);
#endif
/* Enable peripheral clocking to SSP0 */
regval = getreg32(LPC17_40_SYSCON_PCONP);
regval |= SYSCON_PCONP_PCSSP0;
putreg32(regval, LPC17_40_SYSCON_PCONP);
leave_critical_section(flags);
return &g_ssp0dev;
}
#endif
/****************************************************************************
* Name: lpc17_40_ssp1initialize
*
* Description:
* Initialize the SSP1
*
* Input Parameters:
* None
*
* Returned Value:
* Valid SPI device structure reference on succcess; a NULL on failure
*
****************************************************************************/
#ifdef CONFIG_LPC17_40_SSP1
static inline FAR struct lpc17_40_sspdev_s *lpc17_40_ssp1initialize(void)
{
irqstate_t flags;
uint32_t regval;
/* Configure multiplexed pins as connected on the board. Chip select
* pins must be configured by board-specific logic. All SSP0 pins and
* one SSP1 pin (SCK) have multiple, alternative pin selection.
* Definitions in the board.h file must be provided to resolve the
* board-specific pin configuration like:
*
* #define GPIO_SSP0_SCK GPIO_SSP0_SCK_1
*/
flags = enter_critical_section();
lpc17_40_configgpio(GPIO_SSP1_SCK);
lpc17_40_configgpio(GPIO_SSP1_MISO);
lpc17_40_configgpio(GPIO_SSP1_MOSI);
/* Configure clocking */
#ifdef LPC176x
regval = getreg32(LPC17_40_SYSCON_PCLKSEL0);
regval &= ~SYSCON_PCLKSEL0_SSP1_MASK;
regval |= (SSP_PCLKSET_DIV << SYSCON_PCLKSEL0_SSP1_SHIFT);
putreg32(regval, LPC17_40_SYSCON_PCLKSEL0);
#endif
/* Enable peripheral clocking to SSP0 and SSP1 */
regval = getreg32(LPC17_40_SYSCON_PCONP);
regval |= SYSCON_PCONP_PCSSP1;
putreg32(regval, LPC17_40_SYSCON_PCONP);
leave_critical_section(flags);
return &g_ssp1dev;
}
#endif
/****************************************************************************
* Name: lpc17_40_ssp2initialize
*
* Description:
* Initialize the SSP2
*
* Input Parameters:
* None
*
* Returned Value:
* Valid SPI device structure reference on succcess; a NULL on failure
*
****************************************************************************/
#ifdef CONFIG_LPC17_40_SSP2
static inline FAR struct lpc17_40_sspdev_s *lpc17_40_ssp2initialize(void)
{
irqstate_t flags;
uint32_t regval;
/* Configure multiplexed pins as connected on the board. Chip select
* pins must be configured by board-specific logic. All SSP2 pins have
* multiple, alternative pin selection. Definitions in the board.h file
* must be provided to resolve the board-specific pin configuration like:
*
* #define GPIO_SSP2_SCK GPIO_SSP2_SCK_1
*/
flags = enter_critical_section();
lpc17_40_configgpio(GPIO_SSP2_SCK);
lpc17_40_configgpio(GPIO_SSP2_MISO);
lpc17_40_configgpio(GPIO_SSP2_MOSI);
/* Configure clocking */
#ifdef LPC176x
regval = getreg32(LPC17_40_SYSCON_PCLKSEL0);
regval &= ~SYSCON_PCLKSEL0_SSP2_MASK;
regval |= (SSP_PCLKSET_DIV << SYSCON_PCLKSEL0_SSP2_SHIFT);
putreg32(regval, LPC17_40_SYSCON_PCLKSEL0);
#endif
/* Enable peripheral clocking to SSP0 and SSP1 */
regval = getreg32(LPC17_40_SYSCON_PCONP);
regval |= SYSCON_PCONP_PCSSP2;
putreg32(regval, LPC17_40_SYSCON_PCONP);
leave_critical_section(flags);
return &g_ssp2dev;
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: lpc17_40_sspbus_initialize
*
* Description:
* Initialize the selected SSP port.
*
* Input Parameters:
* Port number (for hardware that has mutiple SPI interfaces)
*
* Returned Value:
* Valid SPI device structure reference on succcess; a NULL on failure
*
****************************************************************************/
FAR struct spi_dev_s *lpc17_40_sspbus_initialize(int port)
{
FAR struct lpc17_40_sspdev_s *priv;
uint32_t regval;
int i;
/* Only the SSP0 and SSP1 interfaces are supported */
switch (port)
{
#ifdef CONFIG_LPC17_40_SSP0
case 0:
priv = lpc17_40_ssp0initialize();
break;
#endif
#ifdef CONFIG_LPC17_40_SSP1
case 1:
priv = lpc17_40_ssp1initialize();
break;
#endif
#ifdef CONFIG_LPC17_40_SSP2
case 2:
priv = lpc17_40_ssp2initialize();
break;
#endif
default:
return NULL;
}
/* Configure 8-bit SPI mode */
ssp_putreg(priv, LPC17_40_SSP_CR0_OFFSET, SSP_CR0_DSS_8BIT | SSP_CR0_FRF_SPI);
/* Disable the SSP and all interrupts (we'll poll for all data) */
ssp_putreg(priv, LPC17_40_SSP_CR1_OFFSET, 0);
ssp_putreg(priv, LPC17_40_SSP_IMSC_OFFSET, 0);
/* Set the initial SSP configuration */
priv->frequency = 0;
priv->nbits = 8;
priv->mode = SPIDEV_MODE0;
/* Select a default frequency of approx. 400KHz */
ssp_setfrequency((FAR struct spi_dev_s *)priv, 400000);
/* Initialize the SPI semaphore that enforces mutually exclusive access */
nxsem_init(&priv->exclsem, 0, 1);
/* Enable the SPI */
regval = ssp_getreg(priv, LPC17_40_SSP_CR1_OFFSET);
ssp_putreg(priv, LPC17_40_SSP_CR1_OFFSET, regval | SSP_CR1_SSE);
for (i = 0; i < LPC17_40_SSP_FIFOSZ; i++)
{
(void)ssp_getreg(priv, LPC17_40_SSP_DR_OFFSET);
}
return &priv->spidev;
}
/****************************************************************************
* Name: ssp_flush
*
* Description:
* Flush and discard any words left in the RX fifo. This can be done
* after a device is deselected if you worry about such things.
*
* Input Parameters:
* dev - Device-specific state data
*
* Returned Value:
* None
*
****************************************************************************/
void ssp_flush(FAR struct spi_dev_s *dev)
{
FAR struct lpc17_40_sspdev_s *priv = (FAR struct lpc17_40_sspdev_s *)dev;
/* Wait for the TX FIFO not full indication */
while (!(ssp_getreg(priv, LPC17_40_SSP_SR_OFFSET) & SSP_SR_TNF));
ssp_putreg(priv, LPC17_40_SSP_DR_OFFSET, 0xff);
/* Wait until TX FIFO and TX shift buffer are empty */
while (ssp_getreg(priv, LPC17_40_SSP_SR_OFFSET) & SSP_SR_BSY);
/* Wait until RX FIFO is not empty */
while (!(ssp_getreg(priv, LPC17_40_SSP_SR_OFFSET) & SSP_SR_RNE));
/* Then read and discard bytes until the RX FIFO is empty */
do
{
(void)ssp_getreg(priv, LPC17_40_SSP_DR_OFFSET);
}
while (ssp_getreg(priv, LPC17_40_SSP_SR_OFFSET) & SSP_SR_RNE);
}
#endif /* CONFIG_LPC17_40_SSP0/1 */