blob: 9ea11013ec1c2e7f96cdedf267b752f86503b518 [file] [log] [blame]
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL CORPORATION OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __QM_SPI_H__
#define __QM_SPI_H__
#include "qm_common.h"
#include "qm_soc_regs.h"
#include "qm_dma.h"
/**
* SPI peripheral driver for Quark Microcontrollers.
*
* @defgroup groupSPI SPI
* @{
*/
/**
* QM SPI frame size type.
*/
typedef enum {
QM_SPI_FRAME_SIZE_4_BIT = 3, /**< 4 bit frame. */
QM_SPI_FRAME_SIZE_5_BIT, /**< 5 bit frame. */
QM_SPI_FRAME_SIZE_6_BIT, /**< 6 bit frame. */
QM_SPI_FRAME_SIZE_7_BIT, /**< 7 bit frame. */
QM_SPI_FRAME_SIZE_8_BIT, /**< 8 bit frame. */
QM_SPI_FRAME_SIZE_9_BIT, /**< 9 bit frame. */
QM_SPI_FRAME_SIZE_10_BIT, /**< 10 bit frame. */
QM_SPI_FRAME_SIZE_11_BIT, /**< 11 bit frame. */
QM_SPI_FRAME_SIZE_12_BIT, /**< 12 bit frame. */
QM_SPI_FRAME_SIZE_13_BIT, /**< 13 bit frame. */
QM_SPI_FRAME_SIZE_14_BIT, /**< 14 bit frame. */
QM_SPI_FRAME_SIZE_15_BIT, /**< 15 bit frame. */
QM_SPI_FRAME_SIZE_16_BIT, /**< 16 bit frame. */
QM_SPI_FRAME_SIZE_17_BIT, /**< 17 bit frame. */
QM_SPI_FRAME_SIZE_18_BIT, /**< 18 bit frame. */
QM_SPI_FRAME_SIZE_19_BIT, /**< 19 bit frame. */
QM_SPI_FRAME_SIZE_20_BIT, /**< 20 bit frame. */
QM_SPI_FRAME_SIZE_21_BIT, /**< 21 bit frame. */
QM_SPI_FRAME_SIZE_22_BIT, /**< 22 bit frame. */
QM_SPI_FRAME_SIZE_23_BIT, /**< 23 bit frame. */
QM_SPI_FRAME_SIZE_24_BIT, /**< 24 bit frame. */
QM_SPI_FRAME_SIZE_25_BIT, /**< 25 bit frame. */
QM_SPI_FRAME_SIZE_26_BIT, /**< 26 bit frame. */
QM_SPI_FRAME_SIZE_27_BIT, /**< 27 bit frame. */
QM_SPI_FRAME_SIZE_28_BIT, /**< 28 bit frame. */
QM_SPI_FRAME_SIZE_29_BIT, /**< 29 bit frame. */
QM_SPI_FRAME_SIZE_30_BIT, /**< 30 bit frame. */
QM_SPI_FRAME_SIZE_31_BIT, /**< 31 bit frame. */
QM_SPI_FRAME_SIZE_32_BIT /**< 32 bit frame. */
} qm_spi_frame_size_t;
/**
* SPI transfer mode type.
*/
typedef enum {
QM_SPI_TMOD_TX_RX, /**< Transmit & Receive. */
QM_SPI_TMOD_TX, /**< Transmit Only. */
QM_SPI_TMOD_RX, /**< Receive Only. */
QM_SPI_TMOD_EEPROM_READ /**< EEPROM Read. */
} qm_spi_tmode_t;
/**
* SPI bus mode type.
*/
typedef enum {
QM_SPI_BMODE_0, /**< Clock Polarity = 0, Clock Phase = 0. */
QM_SPI_BMODE_1, /**< Clock Polarity = 0, Clock Phase = 1. */
QM_SPI_BMODE_2, /**< Clock Polarity = 1, Clock Phase = 0. */
QM_SPI_BMODE_3 /**< Clock Polarity = 1, Clock Phase = 1. */
} qm_spi_bmode_t;
/**
* SPI slave select type.
*
* Slave selects can combined by logical OR if multiple slaves are selected
* during one transfer. Setting only QM_SPI_SS_DISABLED prevents the controller
* from starting the transfer.
*/
typedef enum {
QM_SPI_SS_DISABLED = 0, /**< Slave select disable. */
QM_SPI_SS_0 = BIT(0), /**< Slave Select 0. */
QM_SPI_SS_1 = BIT(1), /**< Slave Select 1. */
QM_SPI_SS_2 = BIT(2), /**< Slave Select 2. */
QM_SPI_SS_3 = BIT(3), /**< Slave Select 3. */
} qm_spi_slave_select_t;
/**
* SPI status
*/
typedef enum {
QM_SPI_IDLE, /**< SPI device is not in use. */
QM_SPI_BUSY, /**< SPI device is busy. */
QM_SPI_RX_OVERFLOW, /**< RX transfer has overflown. */
QM_SPI_RX_FULL, /**< Appl. Rx buffer full (slave only). */
QM_SPI_TX_EMPTY /**< Appl. Tx buffer empty (slave only) . */
} qm_spi_status_t;
/**
* QM SPI Frame Format
*/
typedef enum {
/**< Standard SPI mode */
QM_SPI_FRAME_FORMAT_STANDARD = 0x0,
} qm_spi_frame_format_t;
/*
* SPI update type
*
* Used by qm_spi_irq_update to know what to update, RX or TX.
* Logical OR can be used in order to update both RX and TX.
*/
typedef enum {
QM_SPI_UPDATE_RX = BIT(0), /* Update RX. */
QM_SPI_UPDATE_TX = BIT(1), /* Update TX. */
} qm_spi_update_t;
/*
* SPI configuration type.
*/
typedef struct {
qm_spi_frame_size_t frame_size; /**< Frame Size. */
qm_spi_tmode_t transfer_mode; /**< Transfer mode (enum). */
qm_spi_bmode_t bus_mode; /**< Bus mode (enum). */
qm_spi_frame_format_t frame_format; /* Data frame format for TX/RX */
/**
* SCK = SPI_clock/clk_divider.
*
* A value of 0 will disable SCK.
*/
uint16_t clk_divider;
} qm_spi_config_t;
/**
* SPI aynchronous transfer type.
*
* If the frame size is 8 bits or less, 1 byte is needed per data frame. If the
* frame size is 9-16 bits, 2 bytes are needed per data frame and frames of more
* than 16 bits require 4 bytes. In each case, the least significant bits are
* sent while the extra bits are discarded. The most significant bits of the
* frame are sent first.
*/
typedef struct {
void *tx; /**< Write data. */
void *rx; /**< Read data. */
uint16_t tx_len; /**< Number of data frames to write. */
uint16_t rx_len; /**< Number of data frames to read. */
bool keep_enabled; /**< Keep device on once transfer is done. */
/**
* Transfer callback.
*
* Called after all data is transmitted/received or if the driver
* detects an error during the SPI transfer.
* For slave device it also allows the application to update
* transfer information by calling the qm_spi_irq_update function.
*
* @param[in] data The callback user data.
* @param[in] error 0 on success.
* Negative @ref errno for possible error codes.
* @param[in] status SPI driver status.
* @param[in] len Length of the SPI transfer if successful, 0
* otherwise.
*/
void (*callback)(void *data, int error, qm_spi_status_t status,
uint16_t len);
void *callback_data; /**< Callback user data. */
} qm_spi_async_transfer_t;
/**
* SPI synchronous transfer type.
*
* If the frame size is 8 bits or less, 1 byte is needed per data frame. If the
* frame size is 9-16 bits, 2 bytes are needed per data frame and frames of more
* than 16 bits require 4 bytes. In each case, the least significant bits are
* sent while the extra bits are discarded. The most significant bits of the
* frame are sent first.
*/
typedef struct {
void *tx; /**< Write data. */
void *rx; /**< Read data. */
uint16_t tx_len; /**< Number of data frames to write. */
uint16_t rx_len; /**< Number of data frames to read. */
} qm_spi_transfer_t;
/**
* Set SPI configuration.
*
* Change the configuration of a SPI module.
* This includes transfer mode, bus mode, clock divider and data frame size.
*
* @param[in] spi Which SPI module to configure.
* @param[in] cfg New configuration for SPI. This must not be NULL.
*
* @return Standard errno return type for QMSI.
* @retval 0 on success.
* @retval Negative @ref errno for possible error codes.
*/
int qm_spi_set_config(const qm_spi_t spi, const qm_spi_config_t *const cfg);
/**
* Select which slave to perform SPI transmissions on.
*
* @param[in] spi Which SPI module to configure.
* @param[in] ss Which slave select line to enable when doing transmissions.
*
* @return Standard errno return type for QMSI.
* @retval 0 on success.
* @retval Negative @ref errno for possible error codes.
*/
int qm_spi_slave_select(const qm_spi_t spi, const qm_spi_slave_select_t ss);
/**
* Get SPI bus status.
*
* Retrieve SPI bus status. Return QM_SPI_BUSY if transmitting data or SPI TX
* FIFO not empty; QM_SPI_IDLE is available for transfer; QM_SPI_RX_OVERFLOW if
* an RX overflow has occurred.
*
* The user may call this function before performing an SPI transfer in order to
* guarantee that the SPI interface is available.
*
* @param[in] spi Which SPI to read the status of.
* @param[out] status Current SPI status. This must not be null.
*
* @return Standard errno return type for QMSI.
* @retval 0 on success.
* @retval Negative @ref errno for possible error codes.
*/
int qm_spi_get_status(const qm_spi_t spi, qm_spi_status_t *const status);
/**
* Multi-frame read / write on SPI.
*
* Perform a multi-frame read/write on the SPI bus. This is a blocking
* synchronous call. If the SPI is currently in use, the function will wait
* until the SPI is free before beginning the transfer. If transfer mode is
* full duplex (QM_SPI_TMOD_TX_RX), then tx_len and rx_len must be equal.
* Similarly, for transmit-only transfers (QM_SPI_TMOD_TX) rx_len must be 0,
* while for receive-only transfers (QM_SPI_TMOD_RX) tx_len must be 0.
*
* For starting a transfer, this controller demands at least one slave
* select line (SS) to be enabled. Thus, a call to qm_spi_slave_select()
* with one of the four SS valid lines is mandatory. This is true even if
* the native slave select line is not used (i.e. when a GPIO is used to
* drive the SS signal manually).
*
* @param[in] spi Which SPI to read/write on.
* @param[in] xfer Structure containing pre-allocated write and read data
* buffers. This must not be NULL.
* @param[out] status Get spi status.
*
* @return Standard errno return type for QMSI.
* @retval 0 on success.
* @retval Negative @ref errno for possible error codes.
*/
int qm_spi_transfer(const qm_spi_t spi, const qm_spi_transfer_t *const xfer,
qm_spi_status_t *const status);
/**
* Interrupt based transfer on SPI.
*
* Perform an interrupt based transfer on the SPI bus. The function will
* replenish/empty TX/RX FIFOs on SPI empty/full interrupts. If transfer
* mode is full duplex (QM_SPI_TMOD_TX_RX), then tx_len and rx_len must be
* equal. For transmit-only transfers (QM_SPI_TMOD_TX) rx_len must be 0
* while for receive-only transfers (QM_SPI_TMOD_RX) tx_len must be 0.
*
* For starting a transfer, this controller demands at least one slave
* select line (SS) to be enabled. Thus, a call to qm_spi_slave_select()
* with one of the four SS valid lines is mandatory. This is true even if
* the native slave select line is not used (i.e. when a GPIO is used to
* drive the SS signal manually).
*
* @param[in] spi Which SPI to transfer to / from.
* @param[in] xfer Transfer structure includes write / read buffers, length,
* user callback function and the callback context data.
* The structure must not be NULL and must be kept valid until
* the transfer is complete.
*
* @return Standard errno return type for QMSI.
* @retval 0 on success.
* @retval Negative @ref errno for possible error codes.
*/
int qm_spi_irq_transfer(const qm_spi_t spi,
volatile const qm_spi_async_transfer_t *const xfer);
/**
* Update parameters of Interrupt based transfer on SPI.
*
* Allow the application to transmit and/or receive more data over the current
* SPI communication.
* The application is supposed to call this function only inside the registered
* callback, once notified from the driver.
* It is strongly recommended to use this function for slave-based applications
* only, as slave controllers usually do not know how many frames an external
* master will send or request before starting the communication.
* Master controllers should not use this function as it will most likely
* corrupt the transaction.
*
* @param[in] spi Which SPI to transfer to / from.
* @param[in] xfer Transfer structure includes write / read buffers, length,
* user callback function and the callback context data.
* The structure must not be NULL and must be kept valid until
* the transfer is complete.
* @param[in] update Specify if only RX has to be updated, or only TX or both.
*
* @return Standard errno return type for QMSI.
* @retval 0 on success.
* @retval Negative @ref errno for possible error codes.
*/
int qm_spi_irq_update(const qm_spi_t spi,
volatile const qm_spi_async_transfer_t *const xfer,
const qm_spi_update_t update);
/**
* Configure a DMA channel with a specific transfer direction.
*
* The user is responsible for managing the allocation of the pool of DMA
* channels provided by each DMA core to the different peripheral drivers
* that require them.
*
* Note that a SPI controller cannot use different DMA cores to manage
* transfers in different directions.
*
* This function configures DMA channel parameters that are unlikely to change
* between transfers, like transaction width, burst size, and handshake
* interface parameters. The user will likely only call this function once for
* the lifetime of an application unless the channel needs to be repurposed.
*
* This function configures the DMA source transfer width according to the
* currently set SPI frame size. Therefore, whenever the SPI frame is updated
* (using qm_spi_set_config) this function needs to be called again as the
* previous source transfer width configuration is no longer valid. Note that if
* the current frame size lies between 17 and 24 bits, this function fails
* (returning -EINVAL) as the DMA core cannot handle 3-byte source width
* transfers with buffers containing 1 padding byte between consecutive frames.
*
* Note that qm_dma_init() must first be called before configuring a channel.
*
* @param[in] spi SPI controller identifier.
* @param[in] dma_ctrl_id DMA controller identifier.
* @param[in] dma_channel_id DMA channel identifier.
* @param[in] dma_channel_direction DMA channel direction, either
* QM_DMA_MEMORY_TO_PERIPHERAL (TX transfer) or QM_DMA_PERIPHERAL_TO_MEMORY
* (RX transfer).
*
* @return Standard errno return type for QMSI.
* @retval 0 on success.
* @retval Negative @ref errno for possible error codes.
*/
int qm_spi_dma_channel_config(
const qm_spi_t spi, const qm_dma_t dma_ctrl_id,
const qm_dma_channel_id_t dma_channel_id,
const qm_dma_channel_direction_t dma_channel_direction);
/**
* Perform a DMA-based transfer on the SPI bus.
*
* If transfer mode is full duplex (QM_SPI_TMOD_TX_RX), then tx_len and
* rx_len must be equal. Similarly, for transmit-only transfers (QM_SPI_TMOD_TX)
* rx_len must be 0 while for receive-only transfers (QM_SPI_TMOD_RX) tx_len
* must be 0. Transfer length is limited to 4KB.
*
* For starting a transfer, this controller demands at least one slave
* select line (SS) to be enabled. Thus, a call to qm_spi_slave_select()
* with one of the four SS valid lines is mandatory. This is true even if
* the native slave select line is not used (i.e. when a GPIO is used to
* drive the SS signal manually).
*
* Note that qm_spi_dma_channel_config() must first be called in order to
* configure all DMA channels needed for a transfer.
*
* @param[in] spi SPI controller identifier.
* @param[in] xfer Structure containing pre-allocated write and read data
* buffers and callback functions. This must not be NULL and
* must be kept valid until the transfer is complete.
*
* @return Standard errno return type for QMSI.
* @retval 0 on success.
* @retval Negative @ref errno for possible error codes.
*/
int qm_spi_dma_transfer(const qm_spi_t spi,
const qm_spi_async_transfer_t *const xfer);
/**
* Terminate SPI IRQ transfer.
*
* Terminate the current IRQ transfer on the SPI bus.
* This will cause the user callback to be called with
* error code set to -ECANCELED.
*
* @param[in] spi Which SPI to cancel the current transfer.
*
* @return Standard errno return type for QMSI.
* @retval 0 on success.
* @retval Negative @ref errno for possible error codes.
*/
int qm_spi_irq_transfer_terminate(const qm_spi_t spi);
/**
* Terminate the current DMA transfer on the SPI bus.
*
* Terminate the current DMA transfer on the SPI bus.
* This will cause the relevant callbacks to be invoked.
*
* @param[in] spi SPI controller identifier.
*
* @return Standard errno return type for QMSI.
* @retval 0 on success.
* @retval Negative @ref errno for possible error codes.
*/
int qm_spi_dma_transfer_terminate(const qm_spi_t spi);
/**
* Save SPI context.
*
* Saves the configuration of the specified SPI peripheral
* before entering sleep.
*
* @param[in] spi SPI controller identifier.
* @param[out] ctx SPI context structure. This must not be NULL.
*
* @return Standard errno return type for QMSI.
* @retval 0 on success.
* @retval Negative @ref errno for possible error codes.
*/
int qm_spi_save_context(const qm_spi_t spi, qm_spi_context_t *const ctx);
/**
* Restore SPI context.
*
* Restore the configuration of the specified SPI peripheral
* after exiting sleep.
*
* @param[in] spi SPI controller identifier.
* @param[in] ctx SPI context structure. This must not be NULL.
*
* @return Standard errno return type for QMSI.
* @retval 0 on success.
* @retval Negative @ref errno for possible error codes.
*/
int qm_spi_restore_context(const qm_spi_t spi,
const qm_spi_context_t *const ctx);
/**
* @}
*/
#endif /* __QM_SPI_H__ */