* arch/arm/src/samv7/sam_spi_slave.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <debug.h>
#include <nuttx/arch.h>
#include <nuttx/kmalloc.h>
#include <nuttx/irq.h>
#include <nuttx/mutex.h>
#include <nuttx/spi/slave.h>
#include "arm_internal.h"
#include "sam_config.h"
#include "sam_gpio.h"
#include "sam_periphclks.h"
#include "sam_spi.h"
#include "hardware/sam_spi.h"
#include "hardware/sam_pinmap.h"
#include <arch/board/board.h>
#ifdef CONFIG_SAMV7_SPI_SLAVE
* Pre-processor Definitions
****************************************************************************/
#ifndef CONFIG_SAMV7_SPI_SLAVE_QSIZE
# define CONFIG_SAMV7_SPI_SLAVE_QSIZE 8
#endif
#ifndef CONFIG_DEBUG_SPI_INFO
# undef CONFIG_SAMV7_SPI_REGDEBUG
#endif
* Private Types
****************************************************************************/
struct sam_spidev_s
{
struct spi_slave_ctrlr_s ctrlr;
struct spi_slave_dev_s *dev;
xcpt_t handler;
uint32_t base;
mutex_t spilock;
uint16_t outval;
uint16_t irq;
uint8_t mode;
uint8_t nbits;
uint8_t spino;
bool initialized;
bool nss;
uint8_t head;
uint8_t tail;
uint16_t outq[CONFIG_SAMV7_SPI_SLAVE_QSIZE];
#ifdef CONFIG_SAMV7_SPI_REGDEBUG
bool wrlast;
uint32_t addresslast;
uint32_t valuelast;
int ntimes;
#endif
};
* Private Function Prototypes
****************************************************************************/
#ifdef CONFIG_SAMV7_SPI_REGDEBUG
static bool spi_checkreg(struct sam_spidev_s *priv, bool wr,
uint32_t value, uint32_t address);
#else
# define spi_checkreg(priv,wr,value,address) (false)
#endif
static uint32_t spi_getreg(struct sam_spidev_s *priv,
unsigned int offset);
static void spi_putreg(struct sam_spidev_s *priv, uint32_t value,
unsigned int offset);
#ifdef CONFIG_DEBUG_SPI_INFO
static void spi_dumpregs(struct sam_spidev_s *priv, const char *msg);
#else
# define spi_dumpregs(priv,msg)
#endif
static int spi_interrupt(int irq, void *context, void *arg);
static uint16_t spi_dequeue(struct sam_spidev_s *priv);
static void spi_setmode(struct sam_spidev_s *priv,
enum spi_slave_mode_e mode);
static void spi_setbits(struct sam_spidev_s *priv,
int nbits);
static void spi_bind(struct spi_slave_ctrlr_s *ctrlr,
struct spi_slave_dev_s *dev,
enum spi_slave_mode_e mode,
int nbits);
static void spi_unbind(struct spi_slave_ctrlr_s *ctrlr);
static int spi_enqueue(struct spi_slave_ctrlr_s *ctrlr,
const void *data, size_t len);
static bool spi_qfull(struct spi_slave_ctrlr_s *ctrlr);
static void spi_qflush(struct spi_slave_ctrlr_s *ctrlr);
* Private Data
****************************************************************************/
static const uint8_t g_csroffset[4] =
{
SAM_SPI_CSR0_OFFSET, SAM_SPI_CSR1_OFFSET,
SAM_SPI_CSR2_OFFSET, SAM_SPI_CSR3_OFFSET
};
static const struct spi_slave_ctrlrops_s g_ctrlr_ops =
{
.bind = spi_bind,
.unbind = spi_unbind,
.enqueue = spi_enqueue,
.qfull = spi_qfull,
.qflush = spi_qflush,
};
#ifdef CONFIG_SAMV7_SPI0_SLAVE
static struct sam_spidev_s g_spi0_ctrlr =
{
.ctrlr =
{
.ops = g_ctrlr_ops,
},
.base = SAM_SPI0_BASE,
.spilock = NXMUTEX_INITIALIZER,
.irq = SAM_IRQ_SPI0,
.nbits = 8,
.spino = 0,
.nss = true,
};
#endif
#ifdef CONFIG_SAMV7_SPI1_SLAVE
static struct sam_spidev_s g_spi1_ctrlr =
{
.ctrlr =
{
.ops = g_ctrlr_ops,
},
.base = SAM_SPI1_BASE,
.spilock = NXMUTEX_INITIALIZER,
.irq = SAM_IRQ_SPI0,
.nbits = 8,
.spino = 1,
.nss = true,
};
#endif
* Public Data
****************************************************************************/
* Private Functions
****************************************************************************/
* Name: spi_checkreg
*
* Description:
* Check if the current register access is a duplicate of the preceding.
*
* Input Parameters:
* value - The value to be written
* address - The address of the register to write to
*
* Returned Value:
* true: This is the first register access of this type.
* flase: This is the same as the preceding register access.
*
****************************************************************************/
#ifdef CONFIG_SAMV7_SPI_REGDEBUG
static bool spi_checkreg(struct sam_spidev_s *priv, bool wr, uint32_t value,
uint32_t address)
{
if (wr == priv->wrlast &&
value == priv->valuelast &&
address == priv->addresslast)
{
priv->ntimes++;
return false;
}
else
{
if (priv->ntimes > 0)
{
spiinfo("...[Repeats %d times]...\n", priv->ntimes);
}
priv->wrlast = wr;
priv->valuelast = value;
priv->addresslast = address;
priv->ntimes = 0;
}
return true;
}
#endif
* Name: spi_getreg
*
* Description:
* Read an SPI register
*
****************************************************************************/
static uint32_t spi_getreg(struct sam_spidev_s *priv, unsigned int offset)
{
uint32_t address = priv->base + offset;
uint32_t value = getreg32(address);
#ifdef CONFIG_SAMV7_SPI_REGDEBUG
if (spi_checkreg(priv, false, value, address))
{
spiinfo("%08x->%08x\n", address, value);
}
#endif
return value;
}
* Name: spi_putreg
*
* Description:
* Write a value to an SPI register
*
****************************************************************************/
static void spi_putreg(struct sam_spidev_s *priv, uint32_t value,
unsigned int offset)
{
uint32_t address = priv->base + offset;
#ifdef CONFIG_SAMV7_SPI_REGDEBUG
if (spi_checkreg(priv, true, value, address))
{
spiinfo("%08x<-%08x\n", address, value);
}
#endif
putreg32(value, address);
}
* Name: spi_dumpregs
*
* Description:
* Dump the contents of all SPI registers
*
* Input Parameters:
* priv - The SPI controller to dump
* msg - Message to print before the register data
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_DEBUG_SPI_INFO
static void spi_dumpregs(struct sam_spidev_s *priv, const char *msg)
{
spiinfo("%s:\n", msg);
spiinfo(" MR:%08x SR:%08x IMR:%08x\n",
getreg32(priv->base + SAM_SPI_MR_OFFSET),
getreg32(priv->base + SAM_SPI_SR_OFFSET),
getreg32(priv->base + SAM_SPI_IMR_OFFSET));
spiinfo(" CSR0:%08x CSR1:%08x CSR2:%08x CSR3:%08x\n",
getreg32(priv->base + SAM_SPI_CSR0_OFFSET),
getreg32(priv->base + SAM_SPI_CSR1_OFFSET),
getreg32(priv->base + SAM_SPI_CSR2_OFFSET),
getreg32(priv->base + SAM_SPI_CSR3_OFFSET));
spiinfo(" WPCR:%08x WPSR:%08x\n",
getreg32(priv->base + SAM_SPI_WPCR_OFFSET),
getreg32(priv->base + SAM_SPI_WPSR_OFFSET));
}
#endif
* Name: spi_interrupt
*
* Description:
* Common SPI interrupt handler
*
* Input Parameters:
* priv - SPI controller CS state
*
* Returned Value:
* Standard interrupt return value.
*
****************************************************************************/
static int spi_interrupt(int irq, void *context, void *arg)
{
struct sam_spidev_s *priv = (struct sam_spidev_s *)arg;
uint32_t sr;
uint32_t imr;
uint32_t pending;
uint32_t regval;
DEBUGASSERT(priv != NULL);
* RDRF interrupt and we might be able to catch it in this handler
* execution.
*/
for (; ; )
{
sr = spi_getreg(priv, SAM_SPI_SR_OFFSET);
imr = spi_getreg(priv, SAM_SPI_IMR_OFFSET);
pending = sr & imr;
* been processed.
*/
if (pending == 0)
{
return OK;
}
* clock from an external master. When NSS falls, the clock is
* validated and the data is loaded in the SPI_RDR depending on the
* BITS field configured in the SPI_CSR0. These bits are processed
* following a phase and a polarity defined respectively by the NCPHA
* and CPOL bits in the SPI_CSR0.
*
* When all bits are processed, the received data is transferred in
* the SPI_RDR and the RDRF bit rises. If the SPI_RDR has not been
* read before new data is received, the Overrun Error Status (OVRES)
* bit in the SPI_SR is set. As long as this flag is set, data is
* loaded in the SPI_RDR. The user must read SPI_SR to clear the OVRES
* bit.
*/
#ifdef CONFIG_DEBUG_SPI_ERROR
if ((pending & SPI_INT_OVRES) != 0)
{
spierr("ERROR: Overrun (OVRES): %08x\n", pending);
}
#endif
if ((pending & SPI_INT_RDRF) != 0)
{
uint16_t data;
* here then it must have fallen.
*/
if (priv->nss)
{
priv->nss = false;
SPIS_DEV_SELECT(priv->dev, true);
}
* interrupt.
*/
regval = spi_getreg(priv, SAM_SPI_RDR_OFFSET);
data = (uint16_t)
((regval & SPI_RDR_RD_MASK) >> SPI_RDR_RD_SHIFT);
regval = (SPI_INT_TDRE | SPI_INT_UNDES);
spi_putreg(priv, regval, SAM_SPI_IER_OFFSET);
SPIS_DEV_RECEIVE(priv->dev, (const uint16_t *)&data,
sizeof(data));
SPIS_DEV_NOTIFY(priv->dev, SPISLAVE_RX_COMPLETE);
}
* in the Shift register. If no data has been written in the SPI_TDR,
* the last data received is transferred. If no data has been received
* since the last reset, all bits are transmitted low, as the Shift
* register resets to 0.
*
* When a first data is written in the SPI_TDR, it is transferred
* immediately in the Shift register and the TDRE flag rises. If new
* data is written, it remains in the SPI_TDR until a transfer occurs,
* i.e., NSS falls and there is a valid clock on the SPCK pin. When
* the transfer occurs, the last data written in the SPI_TDR is
* transferred in the Shift register and the TDRE flag rises. This
* enables frequent updates of critical variables with single
* transfers.
*
* Then, new data is loaded in the Shift register from the SPI_TDR. If
* no character is ready to be transmitted, i.e., no character has been
* written in the SPI_TDR since the last load from the SPI_TDR to the
* Shift register, the SPI_TDR is retransmitted. In this case the
* Underrun Error Status Flag (UNDES) is set in the SPI_SR.
*/
#ifdef CONFIG_DEBUG_SPI_ERROR
if ((pending & SPI_INT_UNDES) != 0)
{
spierr("ERROR: Underrun (UNDEX): %08x\n", pending);
}
#endif
if ((pending & SPI_INT_TDRE) != 0)
{
* The TDRE interrupt is cleared by writing to the from RDR.
*/
regval = spi_dequeue(priv);
spi_putreg(priv, regval, SAM_SPI_TDR_OFFSET);
SPIS_DEV_NOTIFY(priv->dev, SPISLAVE_TX_COMPLETE);
}
* which may or many not happen at the end of a transfer. NSSR was
* cleared by the status read.
*/
if ((pending & SPI_INT_NSSR) != 0)
{
regval = (SPI_INT_TDRE | SPI_INT_UNDES);
spi_putreg(priv, regval, SAM_SPI_IDR_OFFSET);
priv->nss = true;
SPIS_DEV_SELECT(priv->dev, false);
}
}
return OK;
}
* Name: spi_dequeue
*
* Description:
* Return the next queued output value. If nothing is in the output queue,
* then return the last value obtained from getdata();
*
* Input Parameters:
* priv - SPI controller CS state
*
* Assumptions:
* Called only from the SPI interrupt handler so all interrupts are
* disabled.
*
****************************************************************************/
static uint16_t spi_dequeue(struct sam_spidev_s *priv)
{
uint32_t regval;
uint16_t ret;
int next;
if (priv->head != priv->tail)
{
ret = priv->outq[priv->tail];
next = priv->tail + 1;
if (next >= CONFIG_SAMV7_SPI_SLAVE_QSIZE)
{
next = 0;
}
priv->tail = next;
* spi_enqueue() is called or until we received another command. We
* do this only for the case where NSS is non-functional (tied to
* ground) and we need to end transfers in some fashion.
*/
if (priv->head == next)
{
regval = (SPI_INT_TDRE | SPI_INT_UNDES);
spi_putreg(priv, regval, SAM_SPI_IDR_OFFSET);
}
}
else
{
ret = priv->outval;
regval = (SPI_INT_TDRE | SPI_INT_UNDES);
spi_putreg(priv, regval, SAM_SPI_IDR_OFFSET);
}
return ret;
}
* Name: spi_setmode
*
* Description:
* Set the SPI mode. See enum spi_slave_mode_e for mode definitions
*
* Input Parameters:
* priv - SPI device data structure
* mode - The SPI mode requested
*
* Returned Value:
* none
*
****************************************************************************/
static void spi_setmode(struct sam_spidev_s *priv,
enum spi_slave_mode_e mode)
{
uint32_t regval;
spiinfo("mode=%d\n", mode);
if (mode != priv->mode)
{
*
* SPI CPOL NCPHA
* MODE
* 0 0 1
* 1 0 0
* 2 1 1
* 3 1 0
*/
regval = spi_getreg(priv, SAM_SPI_CSR0_OFFSET);
regval &= ~(SPI_CSR_CPOL | SPI_CSR_NCPHA);
switch (mode)
{
case SPISLAVE_MODE0:
regval |= SPI_CSR_NCPHA;
break;
case SPISLAVE_MODE1:
break;
case SPISLAVE_MODE2:
regval |= (SPI_CSR_CPOL | SPI_CSR_NCPHA);
break;
case SPISLAVE_MODE3:
regval |= SPI_CSR_CPOL;
break;
default:
DEBUGASSERT(FALSE);
return;
}
spi_putreg(priv, regval, SAM_SPI_CSR0_OFFSET);
spiinfo("csr0=%08x\n", regval);
priv->mode = mode;
}
}
* Name: spi_setbits
*
* Description:
* Set the number if bits per word.
*
* Input Parameters:
* priv - SPI device data structure
* nbits - The number of bits requested
*
* Returned Value:
* none
*
****************************************************************************/
static void spi_setbits(struct sam_spidev_s *priv, int nbits)
{
uint32_t regval;
spiinfo("nbits=%d\n", nbits);
DEBUGASSERT(priv && nbits > 7 && nbits < 17);
if (nbits != priv->nbits)
{
regval = spi_getreg(priv, SAM_SPI_CSR0_OFFSET);
regval &= ~SPI_CSR_BITS_MASK;
regval |= SPI_CSR_BITS(nbits);
spi_putreg(priv, regval, SAM_SPI_CSR0_OFFSET);
spiinfo("csr0=%08x\n", regval);
* faster.
*/
priv->nbits = nbits;
}
}
* Name: spi_bind
*
* Description:
* Bind the SPI slave device interface to the SPI slave controller
* interface and configure the SPI interface. Upon return, the SPI
* slave controller driver is fully operational and ready to perform
* transfers.
*
* Input Parameters:
* ctrlr - SPI Slave controller interface instance
* dev - SPI Slave device interface instance
* mode - The SPI Slave mode requested
* nbits - The number of bits requested.
* If value is greater than 0, then it implies MSB first
* If value is less than 0, then it implies LSB first with -nbits
*
* Returned Value:
* None.
*
****************************************************************************/
static void spi_bind(struct spi_slave_ctrlr_s *ctrlr,
struct spi_slave_dev_s *dev,
enum spi_slave_mode_e mode,
int nbits)
{
struct sam_spidev_s *priv = (struct sam_spidev_s *)ctrlr;
uint32_t regval;
int ret;
const void *data;
spiinfo("dev=%p mode=%d nbits=%d\n", sdv, mode, nbits);
DEBUGASSERT(priv != NULL && priv->dev == NULL && dev != NULL);
ret = nxmutex_lock(&priv->spilock);
if (ret < 0)
{
* occur if the calling task was canceled.
*/
spierr("RROR: nxmutex_lock failed: %d\n", ret);
return;
}
* controller interface.
*/
priv->dev = dev;
* the initial state of the chip select and command/data discretes.
*
* NOTE: Unless we reconfigure the NSS GPIO pin, it may not be possible
* to read the NSS pin value (I haven't actually tried just reading it).
* And, since the is no interrupt on the falling edge of NSS, we get no
* notification when we are selected... not until the arrival of data.
*
* REVISIT: A board-level interface would be required in order to support
* the Command/Data indication (not yet impklemented).
*/
SPIS_DEV_SELECT(dev, false);
#warning Missing logic
SPIS_DEV_CMDDATA(dev, false);
priv->head = 0;
priv->tail = 0;
* be shifted out the SPI clock is detected.
*/
SPIS_DEV_GETDATA(dev, &data);
priv->outval = *(const uint16_t *)data;
spi_putreg(priv, priv->outval, SAM_SPI_TDR_OFFSET);
spi_setmode(priv, mode);
spi_setbits(priv, nbits);
regval = spi_getreg(priv, SAM_SPI_SR_OFFSET);
UNUSED(regval);
*
* Data Transfer:
* SPI_INT_RDRF - Receive Data Register Full Interrupt
* SPI_INT_TDRE - Transmit Data Register Empty Interrupt
* SPI_INT_NSSR - NSS Rising Interrupt
*
* Transfer Errors (for DEBUG purposes only):
* SPI_INT_OVRES - Overrun Error Interrupt
* SPI_INT_UNDES - Underrun Error Status Interrupt (slave)
*
* Not Used:
* SPI_INT_MODF - Mode Fault Error Interrupt
* SPI_INT_TXEMPTY - Transmission Registers Empty Interrupt
*
* TX interrupts (SPI_INT_TDRE and SPI_INT_UNDES) are not enabled until
* the transfer of data actually starts.
*/
regval = (SPI_INT_RDRF | SPI_INT_NSSR);
#ifdef CONFIG_DEBUG_SPI_ERROR
regval |= SPI_INT_OVRES;
#endif
spi_putreg(priv, regval, SAM_SPI_IER_OFFSET);
nxmutex_unlock(&priv->spilock);
}
* Name: spi_unbind
*
* Description:
* Un-bind the SPI slave device interface from the SPI slave controller
* interface. Reset the SPI interface and restore the SPI slave
* controller driver to its initial state.
*
* Input Parameters:
* ctrlr - SPI Slave controller interface instance
*
* Returned Value:
* None.
*
****************************************************************************/
static void spi_unbind(struct spi_slave_ctrlr_s *ctrlr)
{
struct sam_spidev_s *priv = (struct sam_spidev_s *)ctrlr;
DEBUGASSERT(priv != NULL);
spiinfo("Unbinding %p\n", priv->dev);
DEBUGASSERT(priv->dev != NULL);
nxmutex_lock(&priv->spilock);
spi_putreg(priv, SPI_INT_ALL, SAM_SPI_IDR_OFFSET);
priv->dev = NULL;
spi_putreg(priv, SPI_CR_SPIDIS, SAM_SPI_CR_OFFSET);
spi_putreg(priv, SPI_CR_SWRST, SAM_SPI_CR_OFFSET);
spi_putreg(priv, SPI_CR_SWRST, SAM_SPI_CR_OFFSET);
nxmutex_unlock(&priv->spilock);
}
* Name: spi_enqueue
*
* Description:
* Enqueue the next value to be shifted out from the interface. This adds
* the word the controller driver for a subsequent transfer but has no
* effect on any in-process or currently "committed" transfers.
*
* Input Parameters:
* ctrlr - SPI Slave controller interface instance
* data - Pointer to the command/data mode data to be shifted out.
* The data width must be aligned to the nbits parameter which was
* previously provided to the bind() method.
* len - Number of units of "nbits" wide to enqueue,
* "nbits" being the data width previously provided to the bind()
* method.
*
* Returned Value:
* Zero if the word was successfully queue; A negated errno valid is
* returned on any failure to enqueue the word (such as if the queue is
* full).
*
****************************************************************************/
static int spi_enqueue(struct spi_slave_ctrlr_s *ctrlr,
const void *data, size_t len)
{
struct sam_spidev_s *priv = (struct sam_spidev_s *)ctrlr;
irqstate_t flags;
uint32_t regval;
int next;
int ret;
spiinfo("data=%04x\n", *(const uint16_t *)data);
DEBUGASSERT(priv != NULL && priv->dev != NULL);
ret = nxmutex_lock(&priv->spilock);
if (ret < 0)
{
return ret;
}
*
* Interrupts are disabled briefly.
*/
flags = enter_critical_section();
next = priv->head + 1;
if (next >= CONFIG_SAMV7_SPI_SLAVE_QSIZE)
{
next = 0;
}
if (next == priv->tail)
{
ret = -ENOSPC;
}
else
{
* word written to the TX data registers is "committed" and will not
* be overwritten.
*/
priv->outq[priv->head] = *(const uint16_t *)data;
priv->head = next;
ret = OK;
if (!priv->nss)
{
regval = (SPI_INT_TDRE | SPI_INT_UNDES);
spi_putreg(priv, regval, SAM_SPI_IER_OFFSET);
}
}
leave_critical_section(flags);
nxmutex_unlock(&priv->spilock);
return ret;
}
* Name: spi_qfull
*
* Description:
* Return true if the queue is full or false if there is space to add an
* additional word to the queue.
*
* Input Parameters:
* ctrlr - SPI Slave controller interface instance
*
* Returned Value:
* true if the output queue is full, false otherwise.
*
****************************************************************************/
static bool spi_qfull(struct spi_slave_ctrlr_s *ctrlr)
{
struct sam_spidev_s *priv = (struct sam_spidev_s *)ctrlr;
irqstate_t flags;
bool bret;
int ret;
int next;
DEBUGASSERT(priv != NULL && priv->dev != NULL);
ret = nxmutex_lock(&priv->spilock);
if (ret < 0)
{
* occurr if the calling task was canceled.
*/
spierr("RROR: nxmutex_lock failed: %d\n", ret);
return true;
}
*
* Interrupts are disabled briefly.
*/
flags = enter_critical_section();
next = priv->head + 1;
if (next >= CONFIG_SAMV7_SPI_SLAVE_QSIZE)
{
next = 0;
}
bret = (next == priv->tail);
leave_critical_section(flags);
nxmutex_unlock(&priv->spilock);
return bret;
}
* Name: spi_qflush
*
* Description:
* Discard all saved values in the output queue. On return from this
* function the output queue will be empty. Any in-progress or otherwise
* "committed" output values may not be flushed.
*
* Input Parameters:
* ctrlr - SPI Slave controller interface instance
*
* Returned Value:
* None.
*
****************************************************************************/
static void spi_qflush(struct spi_slave_ctrlr_s *ctrlr)
{
struct sam_spidev_s *priv = (struct sam_spidev_s *)ctrlr;
irqstate_t flags;
spiinfo("data=%04x\n", data);
DEBUGASSERT(priv != NULL && priv->dev != NULL);
nxmutex_lock(&priv->spilock);
flags = enter_critical_section();
priv->head = 0;
priv->tail = 0;
leave_critical_section(flags);
nxmutex_unlock(&priv->spilock);
}
* Public Functions
****************************************************************************/
* Name: sam_spi_slave_initialize
*
* Description:
* Initialize the selected SPI port in slave mode.
*
* Input Parameters:
* port - Chip select number identifying the "logical" SPI port. Includes
* encoded port and chip select information.
*
* Returned Value:
* Valid SPI device structure reference on success; a NULL on failure
*
****************************************************************************/
struct spi_slave_ctrlr_s *sam_spi_slave_initialize(int port)
{
struct sam_spidev_s *priv;
int spino = (port & __SPI_SPI_MASK) >> __SPI_SPI_SHIFT;
irqstate_t flags;
uint32_t regval;
spiinfo("port: %d spino: %d\n", port, spino);
#if defined(CONFIG_SAMV7_SPI0_SLAVE) && defined(CONFIG_SAMV7_SPI1_SLAVE)
DEBUGASSERT(spino >= 0 && spino <= 1);
#elif defined(CONFIG_SAMV7_SPI0_SLAVE)
DEBUGASSERT(spino == 0);
#else
DEBUGASSERT(spino == 1);
#endif
#if defined(CONFIG_SAMV7_SPI0_SLAVE) && defined(CONFIG_SAMV7_SPI1_SLAVE)
if (spino == 0)
{
priv = &g_spi0_ctrlr;
}
else
{
priv = &g_spi1_ctrlr;
}
#elif defined(CONFIG_SAMV7_SPI0_SLAVE)
priv = &g_spi0_ctrlr;
#elif defined(CONFIG_SAMV7_SPI1_SLAVE)
priv = &g_spi1_ctrlr;
#endif
if (!priv->initialized)
{
flags = enter_critical_section();
#if defined(CONFIG_SAMV7_SPI0_SLAVE) && defined(CONFIG_SAMV7_SPI1_SLAVE)
if (spino == 0)
#endif
#if defined(CONFIG_SAMV7_SPI0_SLAVE)
{
sam_spi0_enableclk();
sam_configgpio(GPIO_SPI0_MISO);
sam_configgpio(GPIO_SPI0_MOSI);
sam_configgpio(GPIO_SPI0_SPCK);
sam_configgpio(GPIO_SPI0_NSS);
}
#endif
#if defined(CONFIG_SAMV7_SPI0_SLAVE) && defined(CONFIG_SAMV7_SPI1_SLAVE)
else
#endif
#if defined(CONFIG_SAMV7_SPI1_SLAVE)
{
sam_spi1_enableclk();
sam_configgpio(GPIO_SPI1_MISO);
sam_configgpio(GPIO_SPI1_MOSI);
sam_configgpio(GPIO_SPI1_SPCK);
sam_configgpio(GPIO_SPI0_NSS);
}
#endif
spi_putreg(priv, SPI_CR_SPIDIS, SAM_SPI_CR_OFFSET);
spi_putreg(priv, SPI_CR_SWRST, SAM_SPI_CR_OFFSET);
spi_putreg(priv, SPI_CR_SWRST, SAM_SPI_CR_OFFSET);
leave_critical_section(flags);
spi_putreg(priv, SPI_MR_SLAVE | SPI_MR_MODFDIS, SAM_SPI_MR_OFFSET);
spi_putreg(priv, SPI_CR_SPIEN, SAM_SPI_CR_OFFSET);
up_mdelay(20);
spi_getreg(priv, SAM_SPI_SR_OFFSET);
spi_getreg(priv, SAM_SPI_RDR_OFFSET);
priv->initialized = true;
spi_putreg(priv, SPI_INT_ALL, SAM_SPI_IDR_OFFSET);
DEBUGVERIFY(irq_attach(priv->irq, spi_interrupt, priv));
up_enable_irq(priv->irq);
spi_dumpregs(priv, "After initialization");
}
regval = spi_getreg(priv, SAM_SPI_CSR0_OFFSET);
regval &= ~(SPI_CSR_CPOL | SPI_CSR_NCPHA | SPI_CSR_BITS_MASK);
regval |= (SPI_CSR_NCPHA | SPI_CSR_BITS(8));
spi_putreg(priv, regval, SAM_SPI_CSR0_OFFSET);
spiinfo("csr[offset=%02x]=%08x\n", offset, regval);
return &priv->ctrlr;
}
#endif