/*
    ChibiOS - Copyright (C) 2016 Andrew Wygle aka awygle

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

/**
 * @file    MSP430X/hal_dma_lld.c
 * @brief   MSP430X DMA subsystem low level driver header.
 * @note    This driver is used as a DMA engine for the other
 *          low level drivers.
 *
 * @addtogroup MSP430X_DMA
 * @{
 */

#ifndef HAL_MSP430X_DMA_H
#define HAL_MSP430X_DMA_H

#if (HAL_USE_DMA == TRUE) || defined(__DOXYGEN__)

/*===========================================================================*/
/* Driver constants.                                                         */
/*===========================================================================*/

#define MSP430X_DMA_SINGLE DMADT_0
#define MSP430X_DMA_BLOCK DMADT_1
#define MSP430X_DMA_BURST DMADT_2

#define MSP430X_DMA_SRCINCR DMASRCINCR_3
#define MSP430X_DMA_SRCDECR DMASRCINCR_2
#define MSP430X_DMA_DSTINCR DMADSTINCR_3
#define MSP430X_DMA_DSTDECR DMADSTINCR_2

#define MSP430X_DMA_SRCBYTE DMASRCBYTE
#define MSP430X_DMA_DSTBYTE DMADSTBYTE
#define MSP430X_DMA_SRCWORD 0
#define MSP430X_DMA_DSTWORD 0

/*===========================================================================*/
/* Driver pre-compile time settings.                                         */
/*===========================================================================*/

/*===========================================================================*/
/* Derived constants and error checks.                                       */
/*===========================================================================*/

#if !defined(DMA_BASE) && !defined(MSP430X_DMA_SOFTWARE)
#error "The MSP430 device in use does not support DMA. Explicitly enable"
#error "software emulation by defining MSP430X_DMA_SOFTWARE."
#endif

#if defined(__MSP430_HAS_DMAX_1__) || defined(__MSP430X_HAS_DMA_1__)
#define MSP430X_DMA_CHANNELS 1
#elif defined(__MSP430_HAS_DMAX_3__) || defined(__MSP430X_HAS_DMA_3__)
#define MSP430X_DMA_CHANNELS 3
#elif defined(__MSP430_HAS_DMAX_6__) || defined(__MSP430X_HAS_DMA_6__)
#define MSP430X_DMA_CHANNELS 6
#else
#error "Unexpected error - how many DMA channels does your MSP have?"
#endif

/*===========================================================================*/
/* Driver data structures and types.                                         */
/*===========================================================================*/

/**
 * @brief     Type of DMA callback function pointer.
 */
typedef void (*msp430x_dma_cbp_t)(void * args);

/**
 * @brief     DMA callback, function and argument.
 */
typedef struct {
  msp430x_dma_cbp_t callback; /**< @brief Callback function pointer   */
  void * args;                /**< @brief Callback function arguments */
} msp430x_dma_cb_t;

/**
 * @brief     MSP430X DMA request structure.
 */
typedef struct {
  const void * source_addr;  /**< @brief Source address                       */
  void * dest_addr;          /**< @brief Destination address                  */
  uint16_t size;             /**< @brief Number of values to transfer         */
  uint16_t addr_mode;        /**< @brief Address manipulation mode            */
  uint16_t data_mode;        /**< @brief Data sizes (b2b, w2w, b2w, w2b)      */
  uint16_t transfer_mode;    /**< @brief Transfer mode (single, block, burst) */
  uint16_t trigger;          /**< @brief Triggering event (see datasheet)     */
  msp430x_dma_cb_t callback; /**< @brief Callback function and arguments      */
} msp430x_dma_req_t;

/**
 * @brief     MSP430X DMA channel register structure.
 */
typedef struct {
  volatile uint16_t ctl; /**< @brief Control register             */
  volatile uint32_t sa;  /**< @brief Source address register      */
  volatile uint32_t da;  /**< @brief Destination address register */
  volatile uint16_t sz;  /**< @brief Size register                */
  volatile uint16_t pad1;
  volatile uint16_t pad2;
} msp430x_dma_ch_reg_t;

/**
 * @brief     MSP430X DMA controller register structure.
 */
typedef struct {
  volatile uint8_t tsel0; /**< @brief Trigger select for channel 0 */
  volatile uint8_t tsel1; /**< @brief Trigger select for channel 1 */
  volatile uint8_t tsel2; /**< @brief Trigger select for channel 2 */
  volatile uint8_t tsel3; /**< @brief Trigger select for channel 3 */
  volatile uint8_t tsel4; /**< @brief Trigger select for channel 4 */
  volatile uint8_t tsel5; /**< @brief Trigger select for channel 5 */
  volatile uint8_t tsel6; /**< @brief Trigger select for channel 6 */
  volatile uint8_t tsel7; /**< @brief Trigger select for channel 7 */
  volatile uint16_t ctl4; /**< @brief Controller register 4        */
} msp430x_dma_ctl_reg_t;

/**
 * @brief     MSP430X DMA channel structure.
 */
typedef struct {
  msp430x_dma_ch_reg_t * registers; /**< @brief Pointer to channel registers */
  uint8_t index; /**< @brief Index of channel trigger control register    */
  msp430x_dma_cb_t * cb; /**< @brief Pointer to callback function  and args  */
} msp430x_dma_ch_t;

/*===========================================================================*/
/* Driver macros.                                                            */
/*===========================================================================*/

/**
 * @brief     Identifies a DMA trigger using a mnemonic.
 *
 * @param[in] mnem    The mnemonic for the trigger, e.g. UCA0RXIFG to trigger
 *                    on UART receive.
 */
#define DMA_TRIGGER_MNEM(mnem) DMA0TSEL__##mnem

/** @} */

/*===========================================================================*/
/* External declarations.                                                    */
/*===========================================================================*/

#ifdef __cplusplus
extern "C" {
#endif
void dmaInit(void);
bool dmaRequest(msp430x_dma_req_t * request, systime_t timeout);
bool dmaAcquire(msp430x_dma_ch_t * channel, uint8_t index);
void dmaTransfer(msp430x_dma_ch_t * channel, msp430x_dma_req_t * request);
void dmaRelease(msp430x_dma_ch_t * channel);

#ifdef __cplusplus
}
#endif

#endif /* HAL_USE_DMA == true */

#endif /* HAL_MSP430X_DMA_H */
