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