walnux/include/nuttx/spi/slave.h
2021-06-05 04:50:34 -07:00

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 */