582 lines
23 KiB
C
582 lines
23 KiB
C
/****************************************************************************
|
|
* include/nuttx/spi/slave.h
|
|
*
|
|
* 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.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifndef __INCLUDE_NUTTX_SPI_SLAVE_H
|
|
#define __INCLUDE_NUTTX_SPI_SLAVE_H
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
/* Configuration ************************************************************/
|
|
|
|
/* CONFIG_SPI_SLAVE - Enable support for SPI slave features
|
|
* CONFIG_SPI_SLAVE_DMA - Enable support for DMA data transfers (not
|
|
* implemented in initial version).
|
|
*/
|
|
|
|
/* Access macros ************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: SPIS_CTRLR_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.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#define SPIS_CTRLR_BIND(c,d,m,n) ((c)->ops->bind(c,d,m,n))
|
|
|
|
/****************************************************************************
|
|
* Name: SPIS_CTRLR_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.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#define SPIS_CTRLR_UNBIND(c) ((c)->ops->unbind(c))
|
|
|
|
/****************************************************************************
|
|
* Name: SPIS_CTRLR_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:
|
|
* Number of data items successfully queued, or a negated errno:
|
|
* - "len" if all the data was successfully queued
|
|
* - "0..len-1" if queue is full
|
|
* - "-errno" in any other error
|
|
*
|
|
****************************************************************************/
|
|
|
|
#define SPIS_CTRLR_ENQUEUE(c,v,l) ((c)->ops->enqueue(c,v,l))
|
|
|
|
/****************************************************************************
|
|
* Name: SPIS_CTRLR_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.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#define SPIS_CTRLR_QFULL(c) ((c)->ops->qfull(c))
|
|
|
|
/****************************************************************************
|
|
* Name: SPIS_CTRLR_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.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#define SPIS_CTRLR_QFLUSH(c) ((c)->ops->qflush(c))
|
|
|
|
/****************************************************************************
|
|
* Name: SPIS_CTRLR_QPOLL
|
|
*
|
|
* Description:
|
|
* Tell the controller to output all the receive queue data.
|
|
*
|
|
* This will cause 1..n SPIS_DEV_RECEIVE calls back to the slave device,
|
|
* offering blocks of data to the device. From each call, the slave device
|
|
* driver will return the number of data units it accepted/read out.
|
|
*
|
|
* The poll will return when:
|
|
* 1. slave device returns that it received less data than what was
|
|
* offered to it
|
|
* OR
|
|
* 2. all the buffered data has been offered to the slave device
|
|
*
|
|
* If the slave device wants the poll to return and leave data into the
|
|
* controller driver's queue, it shall return any number less than what was
|
|
* offered. The controller driver will discard the amount of data that the
|
|
* slave device accepted. The data left to the buffers will be offered
|
|
* again in the next qpoll.
|
|
*
|
|
* If the slave device wants the poll to return and let the controller
|
|
* driver throw away the buffered data it shall just return the same number
|
|
* of bytes that was offered to each receive call.
|
|
*
|
|
* Input Parameters:
|
|
* ctrlr - SPI Slave controller interface instance
|
|
*
|
|
* Returned Value:
|
|
* Number of units of width "nbits" left in the RX queue. If the device
|
|
* accepted all the data, the return value will be 0.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#define SPIS_CTRLR_QPOLL(c) ((c)->ops->qpoll(c))
|
|
|
|
/****************************************************************************
|
|
* Name: SPIS_DEV_SELECT
|
|
*
|
|
* Description:
|
|
* This is a SPI device callback that is used when the SPI controller
|
|
* driver detects any change in the chip select pin.
|
|
*
|
|
* Input Parameters:
|
|
* dev - SPI Slave device interface instance
|
|
* selected - Indicates whether the chip select is in active state
|
|
*
|
|
* Returned Value:
|
|
* None.
|
|
*
|
|
* Assumptions:
|
|
* May be called from an interrupt handler. Processing should be as
|
|
* brief as possible.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#define SPIS_DEV_SELECT(d,s) ((d)->ops->select(d,s))
|
|
|
|
/****************************************************************************
|
|
* Name: SPIS_DEV_CMDDATA
|
|
*
|
|
* Description:
|
|
* This is a SPI device callback that is used when the SPI controller
|
|
* driver detects any change in the command/data condition.
|
|
*
|
|
* Normally only LCD devices distinguish command and data. For devices
|
|
* that do not distinguish between command and data, this method may be
|
|
* a stub. For devices that do make that distinction, they should treat
|
|
* all subsequent calls to getdata() or receive() appropriately for the
|
|
* current command/data selection.
|
|
*
|
|
* Input Parameters:
|
|
* dev - SPI Slave device interface instance
|
|
* data - True: Data is selected
|
|
*
|
|
* Returned Value:
|
|
* None.
|
|
*
|
|
* Assumptions:
|
|
* May be called from an interrupt handler. Processing should be as
|
|
* brief as possible.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#define SPIS_DEV_CMDDATA(d,i) ((d)->ops->cmddata(d,i))
|
|
|
|
/****************************************************************************
|
|
* Name: SPIS_DEV_GETDATA
|
|
*
|
|
* Description:
|
|
* This is a SPI device callback that is used when the SPI controller
|
|
* requires data be shifted out at the next leading clock edge. This
|
|
* is necessary to "prime the pump" so that the SPI controller driver
|
|
* can keep pace with the shifted-in data.
|
|
*
|
|
* The SPI controller driver will prime for both command and data
|
|
* transfers as determined by a preceding call to the device cmddata()
|
|
* method. Normally only LCD devices distinguish command and data.
|
|
*
|
|
* Input Parameters:
|
|
* dev - SPI Slave device interface instance
|
|
* data - Pointer to the data buffer pointer to be shifed out.
|
|
* The device will set the data buffer pointer to the actual data
|
|
*
|
|
* Returned Value:
|
|
* The number of data units to be shifted out from the data buffer.
|
|
*
|
|
* Assumptions:
|
|
* May be called from an interrupt handler and the response is usually
|
|
* time-critical.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#define SPIS_DEV_GETDATA(d,v) ((d)->ops->getdata(d,v))
|
|
|
|
/****************************************************************************
|
|
* Name: SPIS_DEV_RECEIVE
|
|
*
|
|
* Description:
|
|
* This is a SPI device callback that is used when the SPI controller
|
|
* receives a new value shifted in. Notice that these values may be out of
|
|
* synchronization by several words.
|
|
*
|
|
* Input Parameters:
|
|
* dev - SPI Slave device interface instance
|
|
* data - Pointer to the new data that has been shifted in
|
|
* len - Length of the new data in units of nbits wide,
|
|
* nbits being the data width previously provided to the bind()
|
|
* method.
|
|
*
|
|
* Returned Value:
|
|
* Number of units accepted by the device. In other words,
|
|
* number of units to be removed from controller's receive queue.
|
|
*
|
|
* Assumptions:
|
|
* May be called from an interrupt handler and in time-critical
|
|
* circumstances. A good implementation might just add the newly
|
|
* received word to a queue, post a processing task, and return as
|
|
* quickly as possible to avoid any data overrun problems.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#define SPIS_DEV_RECEIVE(d,v,l) ((d)->ops->receive(d,v,l))
|
|
|
|
/****************************************************************************
|
|
* Public Types
|
|
****************************************************************************/
|
|
|
|
/* There are two interfaces defined for the implementation of SPI slave:
|
|
*
|
|
* 1) struct spi_slave_ctrlr_s: Defines one interface between the SPI
|
|
* slave device and the SPI slave controller hardware. This interface
|
|
* is implemented by the SPI slave device controller lower-half driver
|
|
* and is provided to the SPI slave device driver when that driver
|
|
* is initialized. That SPI slave device initialization function is
|
|
* unique to the SPI slave implementation. The prototype is probably
|
|
* something like:
|
|
*
|
|
* FAR struct spi_slave_ctrlr_s *xyz_spi_slave_initialize(int port);
|
|
*
|
|
* Given an SPI port number, this function returns an instance of the
|
|
* SPI slave controller interface.
|
|
*
|
|
* The actual prototype and more detailed usage instructions should
|
|
* appear in a header file associated with the specific SPI slave
|
|
* implementation.
|
|
*
|
|
* 2) struct spi_slave_dev_s: Defines the second interface between the SPI
|
|
* slave device and the SPI slave controller hardware. This interface
|
|
* is implemented by the SPI slave device. The slave device passes this
|
|
* interface to the struct spi_slave_ctrlr_s during initialization
|
|
* by calling the bind() method of the struct spi_slave_ctrlr_s
|
|
* interface.
|
|
*
|
|
* The basic initialization steps are:
|
|
*
|
|
* 1) Board-specific logic calls board- or chip-specific logic to create an
|
|
* instance of the SPI slave controller interface,
|
|
* struct spi_slave_ctrlr_s.
|
|
*
|
|
* 2) Board-specific logic then calls up_dev_initialize() to initialize
|
|
* the SPI slave device. The board-specific logic passes the instance
|
|
* of struct spi_slave_ctrlr_s to support the initialization.
|
|
*
|
|
* 3) The SPI slave device driver creates and initializes an instance of
|
|
* struct spi_slave_dev_s; it passes this instance to the bind() method of
|
|
* of the SPI slave controller interface.
|
|
*
|
|
* 4) The SPI slave controller will (1) call the slave device's select()
|
|
* and cmddata() methods to indicate the initial state of the chip select
|
|
* and any command/data selection, then (2) call the slave device's
|
|
* getdata() method to get the value that will be shifted out once the SPI
|
|
* clock is detected. The kind of data returned by the getdata() method
|
|
* may be contingent on the current command/data setting reported by the
|
|
* device cmddata() method. The driver may enqueue additional words
|
|
* to be shifted out at any time by calling the SPI slave controller's
|
|
* enqueue() method.
|
|
*
|
|
* 5) Upon return from the bind method, the SPI slave controller will be
|
|
* fully "armed" and ready to begin normal SPI data transfers.
|
|
*
|
|
* A typical (non-DMA) data transfer proceeds as follows:
|
|
*
|
|
* 1) Internally, the SPI slave driver detects that the SPI chip select
|
|
* has gone low, selecting this device for data transfer. The SPI
|
|
* slave controller will notify the slave device by calling its
|
|
* select() method.
|
|
*
|
|
* 2) If the command/data state changes any time before, during, or after the
|
|
* chip is selected, that new command/data state will be reported to the
|
|
* device driver via the cmddata() method.
|
|
*
|
|
* 3) As the first word is shifted in, the command or data word obtained
|
|
* by the initial call to getdata() will be shifted out. As soon as
|
|
* the clock is detected, the SPI controller driver will call the
|
|
* getdata() method again to get a default second word to be shifted
|
|
* out. NOTES: (1) the SPI slave device has only one word in bit
|
|
* times to provide this value! (2) The SPI device probably cannot
|
|
* really output anything meaningful until it receives and decodes the
|
|
* first word received from the master.
|
|
*
|
|
* 4) When the first word from the master is shifted in, the SPI
|
|
* controller driver will call the device's receive() method to
|
|
* provide the master with the command word that was just shifted
|
|
* in.
|
|
*
|
|
* For the case of bi-directional data transfer or of a transfer of
|
|
* data from the SPI device to the master, the SPI device driver
|
|
* should call the controller's enqueue() method to provide the next
|
|
* value(s) to be shifted out. If the SPI device responds with this
|
|
* value before clocking begins for the next word, then that value
|
|
* will be used. Otherwise, the value obtained from getdata() in
|
|
* step 3 will be shifted out.
|
|
*
|
|
* 5) The SPI device's receive() method will be called in a similar
|
|
* way after each subsequent word is clocked in.
|
|
*
|
|
* For the case of bi-directional data transfer or of a uni-directional
|
|
* transfer of data from the SPI device to the master, the SPI device
|
|
* driver can call the enqueue() methods as it has new data to be shifted
|
|
* out. The goal of the SPI device driver for this kind of transfer is
|
|
* to supply valid output data at such a rate that data underruns do not
|
|
* occur. In the event of a data underrun, the SPI slave controller
|
|
* driver will fallback to the default output value obtained from the
|
|
* last getdata() call.
|
|
*
|
|
* The SPI device driver can detect if there is space to enqueue
|
|
* additional data by calling the qfull() method.
|
|
*
|
|
* For the case of uni-directional transfer of data from the master to
|
|
* the SPI device, there is no need to call the enqueue() method at all;
|
|
* the value that is shifted out is not important that fallback behavior
|
|
* is sufficient.
|
|
*
|
|
* The SPI slave controller driver, of course, has no sense of the
|
|
* directionality of a data transfer; its role is only to exchange the
|
|
* data shifted in from the master with new data to be shifted out from
|
|
* the SPI device driver.
|
|
*
|
|
* 6) The activity of 5) will continue until the master raises the chip
|
|
* select signal. In that case, the SPI slave controller driver will
|
|
* again call the SPI device's select() method. At this point, the SPI
|
|
* controller driver may have several words enqueued. It will not
|
|
* discard these unless the SPI device driver calls the qflush()
|
|
* method.
|
|
*
|
|
* Some master side implementations may simply tie the chip select signal
|
|
* to ground if there are no other devices on the SPI bus. In that case,
|
|
* the initial indication of chip selected will be the only call to the
|
|
* select() method that is made.
|
|
*
|
|
* Other SPI peripherals (such as Atmel) do not make the state of the
|
|
* chip select pin available (only the final rising edge transitions).
|
|
* So the SPI device driver implementation may use the chip select
|
|
* reports to optimize performance, but the design should never depend
|
|
* upon it.
|
|
*
|
|
* A typical DMA data transfer processes as follows:
|
|
* To be provided -- I do not have a design in mind to support DMA on the
|
|
* Slave side. The design might be very complex because:
|
|
*
|
|
* 1) You need DMA buffers of fixed size, but you cannot know the size of a
|
|
* transfer in advance, it could be much larger than your buffer or much
|
|
* smaller. The DMA would fail in either case.
|
|
*
|
|
* 2) You cannot setup the DMA before the transfer. In most SPI protocols,
|
|
* the first word send is a command to read or write something following
|
|
* by a sequence of transfers to implement the write. So you have very,
|
|
* very limited time window to setup the correct DMA to respond to the
|
|
* command. I am not certain that it can be done reliably.
|
|
*
|
|
* Inserting dummy words into the protocol between the first command word
|
|
* and the remaining data transfer could allow time to set up the DMA.
|
|
*
|
|
* 3) I mentioned that you do not know the size of the transfer in advance.
|
|
* If you set up the DMA to terminate too soon, then you lose the last
|
|
* part of the transfer. If you set the DMA up to be too large, then you
|
|
* will get no indication when the transfer completes.
|
|
*
|
|
* The chip select going high would be one possibility to detect the end
|
|
* of a transfer. You could cancel a DMA in progress if the CS changes,
|
|
* but I do not know if that would work. If there is only one device on
|
|
* the SPI bus, then most board designs will save a pin and simply tie CS
|
|
* to ground. So the CS is not always a reliable indicator of when the
|
|
* transfer completes.
|
|
*
|
|
* 4) Another option is to use a timer but that would really slow down the
|
|
* transfers if each DMA has to end with a timeout. It would be faster
|
|
* to perform non-DMA transfers.
|
|
*
|
|
* If the device has a very restricted protocol, like just register reads
|
|
* and writes, then it might be possible to implement DMA. However, that
|
|
* solution would not be general and probably not an appropriate part of
|
|
* a general OS. But if the interface is unpredictable, such as reading
|
|
* variable amounts of data from FLASH, there is more risk. A general
|
|
* solution might not be possible.
|
|
*/
|
|
|
|
enum spi_slave_mode_e
|
|
{
|
|
SPISLAVE_MODE0 = 0, /* CPOL=0 CPHA=0 */
|
|
SPISLAVE_MODE1, /* CPOL=0 CPHA=1 */
|
|
SPISLAVE_MODE2, /* CPOL=1 CPHA=0 */
|
|
SPISLAVE_MODE3 /* CPOL=1 CPHA=1 */
|
|
};
|
|
|
|
/* The SPI slave controller driver vtable */
|
|
|
|
struct spi_slave_ctrlr_s; /* Forward reference */
|
|
struct spi_slave_dev_s; /* Forward reference */
|
|
|
|
struct spi_slave_ctrlrops_s
|
|
{
|
|
CODE void (*bind)(FAR struct spi_slave_ctrlr_s *ctrlr,
|
|
FAR struct spi_slave_dev_s *sdev,
|
|
enum spi_slave_mode_e mode, int nbits);
|
|
CODE void (*unbind)(FAR struct spi_slave_ctrlr_s *ctrlr);
|
|
CODE int (*enqueue)(FAR struct spi_slave_ctrlr_s *ctrlr,
|
|
FAR const void *data, size_t nwords);
|
|
CODE bool (*qfull)(FAR struct spi_slave_ctrlr_s *ctrlr);
|
|
CODE void (*qflush)(FAR struct spi_slave_ctrlr_s *ctrlr);
|
|
CODE size_t (*qpoll)(FAR struct spi_slave_ctrlr_s *ctrlr);
|
|
};
|
|
|
|
/* SPI slave controller private data. This structure only defines the
|
|
* initial fields of the structure visible to the SPI device driver. The
|
|
* specific implementation may add additional, device specific fields after
|
|
* the vtable structure pointer.
|
|
*/
|
|
|
|
struct spi_slave_ctrlr_s
|
|
{
|
|
FAR const struct spi_slave_ctrlrops_s *ops;
|
|
|
|
/* Private SPI slave controller driver data may follow */
|
|
};
|
|
|
|
/* The SPI slave device driver vtable */
|
|
|
|
struct spi_slave_devops_s
|
|
{
|
|
CODE void (*select)(FAR struct spi_slave_dev_s *sdev, bool selected);
|
|
CODE void (*cmddata)(FAR struct spi_slave_dev_s *sdev, bool data);
|
|
CODE size_t (*getdata)(FAR struct spi_slave_dev_s *sdev,
|
|
FAR const void **data);
|
|
CODE size_t (*receive)(FAR struct spi_slave_dev_s *sdev,
|
|
FAR const void *data, size_t nwords);
|
|
};
|
|
|
|
/* SPI slave device private data. This structure only defines the initial
|
|
* fields of the structure visible to the SPI slave controller driver. The
|
|
* specific implementation may add additional, device specific fields after
|
|
* the vtable structure pointer.
|
|
*/
|
|
|
|
struct spi_slave_dev_s
|
|
{
|
|
FAR const struct spi_slave_devops_s *ops;
|
|
|
|
/* Private SPI slave device driver data may follow */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Public Data
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Public Types
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Public Functions Definitions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: spi_slave_register
|
|
*
|
|
* Description:
|
|
* Register the SPI Slave character device driver as 'devpath'.
|
|
*
|
|
* Input Parameters:
|
|
* ctrlr - An instance of the SPI Slave interface to use to communicate
|
|
* with the SPI Slave device
|
|
* bus - The SPI Slave bus number. This will be used as the SPI device
|
|
* minor number. The SPI Slave character device will be
|
|
* registered as /dev/spislvN where N is the minor number
|
|
*
|
|
* Returned Value:
|
|
* Zero (OK) on success; a negated errno value on failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_SPI_SLAVE_DRIVER
|
|
int spi_slave_register(FAR struct spi_slave_ctrlr_s *ctrlr, int bus);
|
|
#endif /* CONFIG_SPI_SLAVE_DRIVER */
|
|
|
|
#undef EXTERN
|
|
#if defined(__cplusplus)
|
|
#define EXTERN extern "C"
|
|
extern "C"
|
|
{
|
|
#else
|
|
#define EXTERN extern
|
|
#endif
|
|
|
|
#undef EXTERN
|
|
#if defined(__cplusplus)
|
|
}
|
|
#endif
|
|
|
|
#endif /* __INCLUDE_NUTTX_SPI_SLAVE_H */
|