/*****************************************************************************
* $Revision: 423 $
* $Date: 2015-03-27 15:10:42 +0900$
*****************************************************************************/
/* __DISCLAIMER_START__                                                      */
/******************************************************************************
* (c)2017, Cypress Semiconductor Corporation
* or a subsidiary of Cypress Semiconductor Corporation. All rights
* reserved.
*
* This software, including source code, documentation and related
* materials ( "Software" ), is owned by Cypress Semiconductor
* Corporation or one of its subsidiaries ( "Cypress" ) and is protected by
* and subject to worldwide patent protection (United States and foreign),
* United States copyright laws and international treaty provisions.
* Therefore, you may use this Software only as provided in the license
* agreement accompanying the software package from which you
* obtained this Software ( "EULA" ).
*
* If no EULA applies, Cypress hereby grants you a personal, nonexclusive,
* non-transferable license to copy, modify, and compile the
* Software source code solely for use in connection with Cypress' s
* integrated circuit products. Any reproduction, modification, translation,
* compilation, or representation of this Software except as specified
* above is prohibited without the express written permission of Cypress.
*
* Disclaimer: THIS SOFTWARE IS PROVIDED AS-IS, WITH NO
* WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING,
* BUT NOT LIMITED TO, NONINFRINGEMENT, IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE. Cypress reserves the right to make
* changes to the Software without notice. Cypress does not assume any
* liability arising out of the application or use of the Software or any
* product or circuit described in the Software. Cypress does not
* authorize its products for use in any products where a malfunction or
* failure of the Cypress product may reasonably be expected to result in
* significant property damage, injury or death ( "High Risk Product" ). By
* including Cypress' s product in a High Risk Product, the manufacturer
* of such system or application assumes all risk of such use and in doing
* so agrees to indemnify Cypress against all liability.
******************************************************************************/
/* __DISCLAIMER_END__                                                        */
/*****************************************************************************/
/** \file dma.h
**
** Headerfile for DMA functions
**
**
** History:
** - 2015-02-10  1.0  MAs  First version for DMA
*****************************************************************************/

#ifndef __BSP_DMA_H__
#define __BSP_DMA_H__

/** DMA **********************************************************************/
/*****************************************************************************/
/* Include files                                                             */
/*****************************************************************************/

#include "base_types.h"
#include <interrupts.h>

extern uint32_t *paddr;
extern volatile boolean_t DmaCompleted;

/**
*****************************************************************************
** \defgroup DmaGroup Direct Memory Access (DMA)
**
** \brief This section describes the interface for the DMA.
**
** Provided functions of DMA module:
**- Dma_Init()
**- Dma_SetChannelConfig()
**- Dma_GetChannelConfig()
**- Dma_SetTransferParameter()
**- Dma_EnableChannel()
**- Dma_DisableChannel()
**- Dma_TriggerChannel()
**- Dma_PauseChannel()
**- Dma_ResumeChannel()
**- Dma_PauseDma()
**- Dma_ResumeDma()
**- Dma_DeInit()
**
** \brief How to use the DMA module:
** For all provided DMA functions the DMA module must be initialised first with
** Dma_Init(). Any function call will return the error code ErrorUninitialized if
** there is a call of a DMA function when the DMA module has not been initalised
** before. The DMA module is disabled by using Dma_DeInit() function.
** Any DMA module function call must be done in privileged mode, otherwise the error
** code ErrorAccessRights will be returned!
**
** Secondly a channel must be connected to a client. For setting up a client
** configuration for a selected channel the function Dma_SetChannelConfig() is to be used.
** There should be only one specific channel  assigned to a specific client.
** Connecting more channels to a client will result in an unpredictable behavior.
** Calling  Dma_SetChannelConfig() on a specific channel is only successful when
** this channel is in disabled state i.e. after reset or by calling
** Dma_DisableChannel() before.
**
** Along with client and channel related settings the user can set a transfer
** callback function. The callback function will be called in case of
** any normal completion or erroneous stop. The channel number and stop status
** will be passed (type enStopStatus). After successful DMA
** transfer or an aborted or erroneous DMA transfer the channel is disabled
** automatically.
**
** Before starting a transfer Dma_SetTransferParameter() for setting
** the source and destination address as well as transfer size must be called
** (please refer to the hardware manual for further information on how to set up the
** transfer size and the different transfer modes). The channel must be disabled
** when calling Dma_SetTransferParameter(), otherwise the error code
** ErrorInvalidMode will be returned. The corresponding channel must be
** configured before (by using Dma_SetChannelConfig()), otherwise
** the error code ErrorUninitialized will be returned.
**
** After DMA general initialization, channel configuration and transfer parameters
** have been setup, the DMA channel can be enabled by calling Dma_EnableChannel().
** If the channel was configured for #DmaTriggerHardware, the DMA channel will accept
** any trigger signal coming from the connected client.
** If the channel was configured for #DmaTriggerSoftware, the DMA is waiting for
** the SW trigger, which can be issued directly by Dma_EnableChannel() or afterwards
** by dedicated function Dma_TriggerChannel().
**
** The current transfer will be aborted if there is a call of Dma_DisableChannel()
** while a transfer is ongoing (maybe after some gap, see hardware manual for details)
** and a transfer error interrupt is generated. The transfer callback with stop status will
** be triggered if it was set by the user with Dma_SetChannelConfig() function.
** A transfer complete interrupt is triggered after a successful transfer, and
** a transfer callback is generated with stop status accordingly.
**
** To pause an ongoing DMA transfer on a specified channel the user can call
** Dma_PauseChannel() function, the transfer will not be aborted.
** To resume the paused DMA transfer call Dma_ResumeChannel() for the same channel.
**
** To pause/resume all DMA channels at once the function Dma_PauseDma() and
** Dma_ResumeDma() can be used.
*****************************************************************************/
//@{

/*****************************************************************************/
/* Global pre-processor symbols/macros ('define')                            */
/*****************************************************************************/

/** DMA channel count */
    #define DMA_CHANNEL_COUNT           16

/** DMA total client count */
    #define DMA_CLIENT_COUNT            128

/** DMA maximum block count i.e. items for one block */
    #define DMA_MAX_BLOCK_COUNT         16

/** DMA priority (i.e. highest value means lowest priority) */
    #define DMA_LOWEST_PRIORITY         127
    #define DMA_HIGHEST_PRIORITY        0

/**
*****************************************************************************
** \brief DMA priority type
** Select the arbitration scheme of the DMA channel arbiter. This setting
** will define the behavior in case multiple DMA channels request access to
** the AHB/AXI bus.
*****************************************************************************/
    typedef enum en_dma_priority_type
    {
        DmaPriorityTypeFixed        = 0,    ///< Fixed priority scheme. The channel priority can be defined individually, lower number has higher priority.
        DmaPriorityTypeDynamic      = 1,    ///< Dynamic priority scheme. Dynamic adjustment of DMA channel priority number (refer to HWM for details).
        DmaPriorityTypeRoundRobin   = 2     ///< Round robin.
    } en_dma_priority_type_t;
    
    /**
    *****************************************************************************
    ** \brief DMA debug behavior
    *****************************************************************************/
    typedef enum en_dma_debug_behavior
    {
        DmaDebugBehaviorContinueOnBreak = 0,    ///< DMA operation will continue while CPU is in break
        DmaDebugBehaviorHaltOnBreak     = 1,    ///< DMA operation will pause while CPU is in break and resume after break
        DmaDebugBehaviorStopOnBreak     = 2     ///< DMA operation will stop while CPU is in break and will not continue after break
    } en_dma_debug_behavior_t;
    
    /**
    *****************************************************************************
    ** \brief DMA stop status
    ** Is used to indicate the end code of a DMA transfer.
    *****************************************************************************/
    typedef enum en_dma_stop_status
    {
        DmaStopStatusStopRequest            = 2,    ///< Stopped by stop request (DSTP), channel disable or DMA disable
        DmaStopStatusSourceAccessError      = 3,    ///< Source access error (e.g. bus error response)
        DmaStopStatusDestAccessError        = 4,    ///< Destination access error (e.g. bus error response)
        DmaStopStatusNormalEnd              = 5     ///< No error
    } en_dma_stop_status_t;
    
    /**
    *****************************************************************************
    ** \brief DMA trigger input select
    ** These bits are used to select the trigger of a DMA transfer request.
    *****************************************************************************/
    typedef enum en_dma_trigger
    {
        DmaTriggerSoftware = 0,  ///< Trigger of a DMA transfer must be a software request.
        DmaTriggerHardware = 1   ///< Trigger of a DMA transfer must be a hardware request (DREQ).
    } en_dma_trigger_t;
    
    /**
    *****************************************************************************
    ** \brief DMA beat limit
    **
    ** Beat limit controls the maximum number of items the AHB Master can
    ** transfer within one bus access i.e. to allow burst accesses. A burst
    ** access will transfer consecutive items of selected size (enTransferWidth)
    ** at a glance by transferring the address just once, reading/writing the
    ** items consecutively from that base address (which is automatically incremented
    ** by the corresponding bus master).
    **
    ** Beat limits higher than DmaBeatLimitSingle make sense only, if the accessed
    ** items are address-consecutively located in memory i.e. allowing burst access.
    ** To use settings higher than DmaBeatLimitSingle, the item address modes
    ** must be set to DmaItemAddressModeIncrement.
    *****************************************************************************/
    typedef enum en_dma_beat_limit
    {
        DmaBeatLimitSingle = 0, ///< do single accesses only
        DmaBeatLimitInc4   = 1, ///< do burst 4x accesses if possible
        DmaBeatLimitInc8   = 2, ///< do burst 8x accesses if possible
        DmaBeatLimitInc16  = 3  ///< do burst 16x accesses if possible
    } en_dma_beat_limit_t;
    
    /**
    *****************************************************************************
    ** \brief DMA tranfer mode
    *****************************************************************************/
    typedef enum en_dma_transfer_mode
    {
        DmaTransferModeBlock  = 0,  ///< Block transfer mode, transfer one block of data on each trigger signal
        DmaTransferModeBurst  = 1,  ///< Burst transfer mode, transfer all data at once on one trigger signal
        DmaTransferModeDemand = 2   ///< Demand transfer mode, transfer data as long as request signal is active
    } en_dma_transfer_mode_t;
    
    /**
    *****************************************************************************
    ** \brief DMA tranfer width
    **
    ** The configured transfer width automatically determines the address
    ** increment/decrement size (if enabled).
    *****************************************************************************/
    typedef enum en_dma_transfer_width
    {
        DmaTransferWidthByte      = 0,  ///< Byte (1 byte).
        DmaTransferWidthHalfWord  = 1,  ///< Half word (2 byte).
        DmaTransferWidthWord      = 2,  ///< Word (4 byte).
        DmaTransfeWidthDoubleWord = 3   ///< Double word (8 byte).
    } en_dma_transfer_width_t;
    
    /**
    *****************************************************************************
    ** \brief DMA item address behavior
    **
    ** Setting defines the behavior of the destination/source address after
    ** one item (of transfer width) has been transferred.
    *****************************************************************************/
    typedef enum en_dma_item_address_mode
    {
        DmaItemAddressModeFixed,        ///< address will be the same for each access (single bus accesses possible only)
        DmaItemAddressModeIncrement,    ///< address will be incremented by transfer width (single or burst bus accesses possible)
        DmaItemAddressModeDecrement     ///< address will be decremented by transfer width (single bus accesses possible only)
    } en_dma_item_address_mode_t;
    
    /**
    *****************************************************************************
    ** \brief DMA block address behavior
    **
    ** Setting defines the behavior of the base address of a block of data after
    ** a block has been transferred.
    **
    ** The block address mode setting is only used if item source/dest. address
    ** mode is set to either increment or decrement (i.e. != DmaItemAddressModeFixed)
    ** and if the transfer mode is either DmaTransferModeBlock or DmaTransferModeBurst.
    *****************************************************************************/
    typedef enum en_dma_block_address_mode
    {
        DmaBlockAddressModeFixed,   ///< Block address will be the same for each block transfer
        DmaBlockAddressModeUpdate   ///< Block address will be updated with the next logical address following the last item address in a block
    } en_dma_block_address_mode_t;
    
    /**
    *****************************************************************************
    ** \brief DMA access rights
    **
    ** This setting defines whether a DMA access to source or destination address
    ** is done with User or Privileged access rights.
    *****************************************************************************/
    typedef enum en_dma_access_rights
    {
        DmaAccessRightUser       = 0,   ///< User access rights (limited access, e.g. access might be blocked if PPU is enabled)
        DmaAccessRightPrivileged = 1    ///< Privileged access rights
    } en_dma_access_rights_t;
    
    /**
    *****************************************************************************
    ** \brief Global DMA configuration
    *****************************************************************************/
    typedef struct stc_dma_config
    {
        en_dma_priority_type_t  enPriorityType;     ///< See description of en_dma_priority_type_t.
        en_dma_debug_behavior_t enDebugBehavior;    ///< See description of en_dma_debug_behavior_t.
    } stc_dma_config_t;
    
    /**
    *****************************************************************************
    ** \brief DMA channel configuration.
    **
    *****************************************************************************/
    typedef struct stc_dma_channel_config
    {
        uint16_t                                    u16ClientIndex;             ///< Valid range from 0 to #DMA_CHANNEL_COUNT-1.
        ///< Please refer to device's datasheet for available client numbers.
        ///< Don't care in case of #DmaTriggerSoftware.
        en_dma_transfer_mode_t                      enTransferMode;             ///< See description of en_dma_transfer_mode_t.
        en_dma_transfer_width_t                     enTransferWidth;            ///< See description of en_dma_transfer_width_t.
        en_dma_beat_limit_t                         enBeatLimit;                ///< See description of en_dma_beat_limit_t.
        uint8_t                                     u8BlockCount;               ///< Is used to specify the total length of a single block in block/burst transfer mode. Valid range from 1 to #DMA_MAX_BLOCK_COUNT items.
        en_dma_trigger_t                            enTrigger;                  ///< HW or SW trigger input. See description of en_dma_trigger_t.
        en_dma_item_address_mode_t                  enSourceItemAddressMode;    ///< See description of en_dma_item_address_mode_t.
        en_dma_item_address_mode_t                  enDestItemAddressMode;      ///< See description of en_dma_item_address_mode_t.
        en_dma_block_address_mode_t                 enSourceBlockAddressMode;   ///< See description of en_dma_block_address_mode_t.
        en_dma_block_address_mode_t                 enDestBlockAddressMode;     ///< See description of en_dma_block_address_mode_t.
        en_dma_access_rights_t                      enSourceAccessRights;       ///< User/privileged access rights for reading from source address. See description of en_dma_access_right_t.
        en_dma_access_rights_t                      enDestAccessRights;         ///< User/privileged access rights for writing to destination address. See description of en_dma_access_right_t.
        boolean_t                                   bUpdateSourceAddress;       ///< Update source address register after transfer completion with next logical address (to allow consecutive tansfers without new address setup)
        boolean_t                                   bUpdateDestAddress;         ///< Update destination address register after transfer completion with next logical address (to allow consecutive tansfers without new address setup)
        uint8_t                                     u8PriorityNumber;           ///< Channel priority number (0 to #DMA_LOWEST_PRIORITY, lower value has higher priority). Is used in case DmaPriorityTypeFixed or DmaPriorityTypeDynamic is selected.
    } stc_dma_channel_config_t;
    
    /// DMA callback function type, providing the channel number, stop status and address information
    /**
    *****************************************************************************
    ** \brief DMA callback function type for normal completion or error indication
    **
    ** When this function is called, the corresponding DMA channel will be
    ** disabled already i.e. the transfer paramters for a next transfer can be
    ** configured immediately by calling Dma_SetTransferParameter(), Dma_EnableChannel()
    ** and (optionally) Dma_TriggerChannel().
    **
    ** \param u8Channel    DMA channel signaling completion or error
    ** \param enStopStatus Stop status, see description for en_dma_stop_status_t
    ** \param u32Address   Error address information, in case of error.
    **                     Address depends on value of #enStopStatus:
    **                         - #DmaStopStatusSourceAccessError: #u32Address is last source address causing the read error
    **                         - #DmaStopStatusDestAccessError:   #u32Address is last destination address causing the write error
    **                         - #DmaStopStatusStopRequest/#DmaStopStatusNormalEnd: #u32Address is undefined
    **
    ** \return None
    *****************************************************************************/
    typedef void (*dma_func_ptr_t)(uint8_t u8Channel, en_dma_stop_status_t enStopStatus, uint32_t u32ErrorAddress);
    /*****************************************************************************/
    /* Global variable declarations ('extern', definition in C source)           */
    /*****************************************************************************/
    
    /*****************************************************************************/
    /* Global function prototypes ('extern', definition in C source)             */
    /*****************************************************************************/
    
    /*****************************************************************************
    * Global function prototypes
    *****************************************************************************/
    
    
    /** Macro to return the number of enabled DMA instances */
    #define DMA_INSTANCE_COUNT (uint32_t)(sizeof(m_astcDmaInstanceDataLut) / sizeof(m_astcDmaInstanceDataLut[0]))
    
    /******************************************************************************/
    /* Global variable definitions (declared in header file with 'extern')        */
    /******************************************************************************/
    
    /******************************************************************************/
    /* Local type definitions ('typedef')                                         */
    /******************************************************************************/
    
    // Enumeration to define an index for each enabled DMA instance
    typedef enum en_dma_instance_index
    {
        DmaInstanceIndexDma0,
        DmaInstanceIndexMax
    } en_dma_instance_index_t;
    /**
    *****************************************************************************
    ** \brief Data type for holding internal data for callbacks needed
    **
    ** This struct is used to store the interrupt callback function and
    ** status of configuration.
    *****************************************************************************/
    typedef struct stc_dma_intern_data
    {
        boolean_t           abChannelConfigured[DMA_CHANNEL_COUNT];     ///< Channel configuration status
        dma_func_ptr_t      apfnCallbackFunction[DMA_CHANNEL_COUNT];    ///< Callback function for channels
    } stc_dma_intern_data_t;
    
    // DMA instance data type
    typedef struct stc_dma_instance_data
    {
        volatile stc_dma0_t* pstcInstance;   ///< pointer to registers of an instance
        stc_dma_intern_data_t stcInternData; ///< module internal data of instance
    } stc_dma_instance_data_t;
    
    
    /*****************************************************************************/
    /* Local function prototypes ('static')                                      */
    /*****************************************************************************/
    
    // IRQ Handler
    extern void DmaIrqHandler(volatile stc_dma0_t* pstcDma,
    stc_dma_intern_data_t* pstcDmaInternData,
    uint8_t u8Channel);
    // Error IRQ Handler
    extern void DmaIrqHandlerError(volatile stc_dma0_t* pstcDma,
    stc_dma_intern_data_t* pstcDmaInternData);
    
    extern boolean_t IsCpuInPrivilegedMode(void);
    
    extern en_result_t Dma_Init(const stc_dma_config_t* pstcConfig);
    extern en_result_t Dma_DeInit(void);
    extern en_result_t Dma_SetChannelConfig(uint8_t u8Channel, const stc_dma_channel_config_t* pstcConfig, dma_func_ptr_t pfnCallbackFunction);
    extern en_result_t Dma_GetChannelConfig(uint8_t u8Channel, stc_dma_channel_config_t* pstcConfig);
    extern en_result_t Dma_SetTransferParameter(uint8_t u8Channel,  uint32_t pvSourceAddress, uint32_t pvDestinationAddress, uint32_t u32TransferSizeByte);
    extern en_result_t Dma_EnableChannel(uint8_t u8Channel, boolean_t bDoSwTrigger);
    extern en_result_t Dma_DisableChannel(uint8_t u8Channel);
    extern en_result_t Dma_TriggerChannel(uint8_t u8Channel);
    extern en_result_t Dma_PauseChannel(uint8_t u8Channel);
    extern en_result_t Dma_ResumeChannel(uint8_t u8Channel);
    extern en_result_t Dma_PauseDma(void);
    extern en_result_t Dma_ResumeDma(void);
    extern void DmaCallback(uint8_t u8Channel, en_dma_stop_status_t enStopStatus, uint32_t u32ErrorAddress);
    extern en_result_t BSP_DMA_Init(void);
    extern stc_dma_intern_data_t* DmaGetInternDataPtr(volatile stc_dma0_t* pstcDma);
    

#endif /* __DMA_H__ */
