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

/**
 * @addtogroup HAL
 * @{
 *   @defgroup HALSpi HAL SPI
 *   @{
 */

#ifndef H_HAL_SPI_
#define H_HAL_SPI_

#ifdef __cplusplus
extern "C" {
#endif

#include <inttypes.h>

/* SPI Type of Master */
#define HAL_SPI_TYPE_MASTER         (0)
/* SPI Type of Slave */
#define HAL_SPI_TYPE_SLAVE          (1)

/** SPI mode 0 */
#define HAL_SPI_MODE0               (0)
/** SPI mode 1 */
#define HAL_SPI_MODE1               (1)
/** SPI mode 2 */
#define HAL_SPI_MODE2               (2)
/** SPI mode 3 */
#define HAL_SPI_MODE3               (3)

/** SPI data order, most significant bit first */
#define HAL_SPI_MSB_FIRST           (0)
/** SPI data order, least significant bit first */
#define HAL_SPI_LSB_FIRST           (1)

/** SPI word size 8 bit */
#define HAL_SPI_WORD_SIZE_8BIT      (0)
/** SPI word size 9 bit */
#define HAL_SPI_WORD_SIZE_9BIT      (1)

/* Prototype for tx/rx callback */
typedef void (*hal_spi_txrx_cb)(void *arg, int len);

/** SPI controller hardware settings */
struct hal_spi_hw_settings {
    int pin_sck;
    int pin_mosi;
    int pin_miso;
    int pin_ss;
};

/**
 * since one spi device can control multiple devices, some configuration
 * can be changed on the fly from the hal
 */
struct hal_spi_settings {
    /** Data mode of SPI driver, defined by HAL_SPI_MODEn */
    uint8_t         data_mode;
    /** Data order, either HAL_SPI_MSB_FIRST or HAL_SPI_LSB_FIRST */
    uint8_t         data_order;
    /** The word size of the SPI transaction, either 8-bit or 9-bit */
    uint8_t         word_size;
    /** Baudrate in kHz */
    uint32_t        baudrate;
};

/**
 * Initialize the SPI, given by spi_num.
 *
 * @param spi_num The number of the SPI to initialize
 * @param cfg HW/MCU specific configuration,
 *            passed to the underlying implementation, providing extra
 *            configuration.
 * @param spi_type SPI type (master or slave)
 *
 * @return int 0 on success, non-zero error code on failure.
 */
int hal_spi_init(int spi_num, void *cfg, uint8_t spi_type);

/**
 * Initialize SPI controller
 *
 * This initializes SPI controller hardware before 1st use. Shall be called
 * only once.
 *
 * @param spi_num  Number of SPI controller
 * @param cfg      Configuration
 *
 * @return 0 on success, non-zero error code on failure
 */
int hal_spi_init_hw(uint8_t spi_num, uint8_t spi_type,
                    const struct hal_spi_hw_settings *cfg);

/**
 * Configure the spi. Must be called after the spi is initialized (after
 * hal_spi_init is called) and when the spi is disabled (user must call
 * hal_spi_disable if the spi has been enabled through hal_spi_enable prior
 * to calling this function). Can also be used to reconfigure an initialized
 * SPI (assuming it is disabled as described previously).
 *
 * @param spi_num The number of the SPI to configure.
 * @param psettings The settings to configure this SPI with
 *
 * @return int 0 on success, non-zero error code on failure.
 */
int hal_spi_config(int spi_num, struct hal_spi_settings *psettings);

/**
 * Sets the txrx callback (executed at interrupt context) when the
 * buffer is transferred by the master or the slave using the non-blocking API.
 * Cannot be called when the spi is enabled. This callback will also be called
 * when chip select is de-asserted on the slave.
 *
 * NOTE: This callback is only used for the non-blocking interface and must
 * be called prior to using the non-blocking API.
 *
 * @param spi_num   SPI interface on which to set callback
 * @param txrx      Callback function
 * @param arg       Argument to be passed to callback function
 *
 * @return int 0 on success, non-zero error code on failure.
 */
int hal_spi_set_txrx_cb(int spi_num, hal_spi_txrx_cb txrx_cb, void *arg);

/**
 * Enables the SPI. This does not start a transmit or receive operation;
 * it is used for power mgmt. Cannot be called when a SPI transfer is in
 * progress.
 *
 * @param spi_num
 *
 * @return int 0 on success, non-zero error code on failure.
 */
int hal_spi_enable(int spi_num);

/**
 * Disables the SPI. Used for power mgmt. It will halt any current SPI transfers
 * in progress.
 *
 * @param spi_num
 *
 * @return int 0 on success, non-zero error code on failure.
 */
int hal_spi_disable(int spi_num);

/**
 * Blocking call to send a value on the SPI. Returns the value received from the
 * SPI slave.
 *
 * MASTER: Sends the value and returns the received value from the slave.
 * SLAVE: Invalid API. Returns 0xFFFF
 *
 * @param spi_num   Spi interface to use
 * @param val       Value to send
 *
 * @return uint16_t Value received on SPI interface from slave. Returns 0xFFFF
 * if called when the SPI is configured to be a slave
 */
uint16_t hal_spi_tx_val(int spi_num, uint16_t val);

/**
 * Blocking interface to send a buffer and store the received values from the
 * slave. The transmit and receive buffers are either arrays of 8-bit (uint8_t)
 * values or 16-bit values depending on whether the spi is configured for 8 bit
 * data or more than 8 bits per value. The 'cnt' parameter is the number of
 * 8-bit or 16-bit values. Thus, if 'cnt' is 10, txbuf/rxbuf would point to an
 * array of size 10 (in bytes) if the SPI is using 8-bit data; otherwise
 * txbuf/rxbuf would point to an array of size 20 bytes (ten, uint16_t values).
 *
 * NOTE: these buffers are in the native endian-ness of the platform.
 *
 *     MASTER: master sends all the values in the buffer and stores the
 *             stores the values in the receive buffer if rxbuf is not NULL.
 *             The txbuf parameter cannot be NULL.
 *     SLAVE: cannot be called for a slave; returns -1
 *
 * @param spi_num   SPI interface to use
 * @param txbuf     Pointer to buffer where values to transmit are stored.
 * @param rxbuf     Pointer to buffer to store values received from peer.
 * @param cnt       Number of 8-bit or 16-bit values to be transferred.
 *
 * @return int 0 on success, non-zero error code on failure.
 */
int hal_spi_txrx(int spi_num, void *txbuf, void *rxbuf, int cnt);

/**
 * Non-blocking interface to send a buffer and store received values. Can be
 * used for both master and slave SPI types. The user must configure the
 * callback (using hal_spi_set_txrx_cb); the txrx callback is executed at
 * interrupt context when the buffer is sent.
 *
 * The transmit and receive buffers are either arrays of 8-bit (uint8_t)
 * values or 16-bit values depending on whether the spi is configured for 8 bit
 * data or more than 8 bits per value. The 'cnt' parameter is the number of
 * 8-bit or 16-bit values. Thus, if 'cnt' is 10, txbuf/rxbuf would point to an
 * array of size 10 (in bytes) if the SPI is using 8-bit data; otherwise
 * txbuf/rxbuf would point to an array of size 20 bytes (ten, uint16_t values).
 *
 * NOTE: these buffers are in the native endian-ness of the platform.
 *
 *     MASTER: master sends all the values in the buffer and stores the
 *             stores the values in the receive buffer if rxbuf is not NULL.
 *             The txbuf parameter cannot be NULL
 *     SLAVE: Slave "preloads" the data to be sent to the master (values
 *            stored in txbuf) and places received data from master in rxbuf
 *            (if not NULL). The txrx callback occurs when len values are
 *            transferred or master de-asserts chip select. If txbuf is NULL,
 *            the slave transfers its default byte. Both rxbuf and txbuf cannot
 *            be NULL.
 *
 * @param spi_num   SPI interface to use
 * @param txbuf     Pointer to buffer where values to transmit are stored.
 * @param rxbuf     Pointer to buffer to store values received from peer.
 * @param cnt       Number of 8-bit or 16-bit values to be transferred.
 *
 * @return int 0 on success, non-zero error code on failure.
 */
int hal_spi_txrx_noblock(int spi_num, void *txbuf, void *rxbuf, int cnt);

/**
 * Sets the default value transferred by the slave. Not valid for master
 *
 * @param spi_num SPI interface to use
 *
 * @return int 0 on success, non-zero error code on failure.
 */
int hal_spi_slave_set_def_tx_val(int spi_num, uint16_t val);

/**
 * This aborts the current transfer but keeps the spi enabled.
 *
 * @param spi_num   SPI interface on which transfer should be aborted.
 *
 * @return int 0 on success, non-zero error code on failure.
 *
 * NOTE: does not return an error if no transfer was in progress.
 */
int hal_spi_abort(int spi_num);

/**
 * Extracts CPOL and CPHA values from a data-mode constant.
 * Utility function, defined once for every MCU.
 *
 * @param data_mode             The HAL_SPI_MODE value to convert.
 * @param out_cpol              The CPOL gets written here on success.
 * @param out_cpha              The CPHA gets written here on success.
 *
 * @return                      0 on success; nonzero on invalid input.
 */
int hal_spi_data_mode_breakout(uint8_t data_mode,
                               int *out_cpol, int *out_cpha);


#ifdef __cplusplus
}
#endif

#endif /* H_HAL_SPI_ */

/**
 *   @} HALSpi
 * @} HAL
 */
