/**********************************************************************************************************************
 *  COPYRIGHT
 *  -------------------------------------------------------------------------------------------------------------------
 *  \verbatim
 *                 TEXAS INSTRUMENTS INCORPORATED PROPRIETARY INFORMATION
 *
 *                 Property of Texas Instruments, Unauthorized reproduction and/or distribution
 *                 is strictly prohibited.  This product  is  protected  under  copyright  law
 *                 and  trade  secret law as an  unpublished work.
 *                 (C) Copyright Texas Instruments - 2012.  All rights reserved.
 *
 *  \endverbatim
 *  -------------------------------------------------------------------------------------------------------------------
 *  FILE DESCRIPTION
 *  -------------------------------------------------------------------------------------------------------------------
 *         File:  Spi.c
 *      Project:  Tms570_AutoSarSpiDriver
 *       Module:  SpiDriver module Spi_Hw.c
 *    Generator:  -
 *
 *  Description:  This file implements the Spi_Hw driver, handling of jobs and sequences.
 *---------------------------------------------------------------------------------------------------------------------
 * Author:  Prathap Srinivasan
 *---------------------------------------------------------------------------------------------------------------------
 * Revision History
 *---------------------------------------------------------------------------------------------------------------------
 * Version        Date         Author               Change ID        Description
 *---------------------------------------------------------------------------------------------------------------------
 * 02.10.00       24Jan2013    Prathap Srinivasan  0000000000000    Initial Version adapted for TMS570LS1227
 * 02.20.00       08Aug2013    Enrique Lizarraga   SDOCM00103201    -Removed unnecessary return statements and multiple
 *                                                                   exit points.
 *                                                                  -Added Sync mode support
 *                                                                  -Added traceabiity tags.
 * 02.21.00       30Sep2014    Enrique Lizarraga   SDOCM00112317    -Updated version info
 *                                                                  -Removed unused code
 *                                                                  -Updated traceability tags
 *                                                                  -MISRA fixes
 *                                                                  -Add critical sections protection when invoking 
 *                                                                   sequence notification functions.
 * 02.21.01       29Feb2016    Enrique Lizarraga   SDOCM00121246    -Update version info
 * 02.22.00       18Jul2016    Prathap Srinivasan  SDOCM00122408    -Update version info
 * 																	-Added Sync Polling Mode Support
 * 02.22.01       10Aug2016    Prathap Srinivasan  SDOCM00122593    -Bug Fix: Spi_IrqTxRxERR function checks for
 *                                                                   wrong error status bit
 * 02.22.02       23Aug2016    Vishwanath Reddy    SDOCM00121246    -Update version info
 * 02.22.03       26Sep2016    Prathap Srinivasan  SDOCM00122695    -Update version info
 * 02.30.00       01Dec2016    Sudhakar Singh         None          -Update version info
 * 05.30.00       15Mar2017    Sudhakar Singh      SDOCM00122834    Version supports TMS570LSxx Family of Devices.
 *
 
 *********************************************************************************************************************/

#define SPI_HW_SOURCE 1

/**********************************************************************************************************************
 *  INCLUDES
 *********************************************************************************************************************/
#include "Spi.h"
#include "Spi_Hw.h"
/**********************************************************************************************************************
 *  VERSION CHECK
 *********************************************************************************************************************/
#define SPI_HW_C_MAJOR_VERSION               (5U)
#define SPI_HW_C_MINOR_VERSION    			 (30U)
#define SPI_HW_C_PATCH_VERSION               (0U)
 
 /* vendor specific version information is BCD coded */
#if (  (SPI_HW_MAJOR_VERSION != SPI_HW_C_MAJOR_VERSION) \
    || (SPI_HW_MINOR_VERSION != SPI_HW_C_MINOR_VERSION))
#error "Software Version Numbers of Spi_Hw.c and Spi_Hw.h are different"
#endif


/**********************************************************************************************************************
 *  LOCAL CONSTANT MACROS
 **********************************************************************************************************************/

/**********************************************************************************************************************
 *  LOCAL FUNCTION MACROS
 **********************************************************************************************************************/

/**********************************************************************************************************************
 *  LOCAL DATA TYPES AND STRUCTURES
 **********************************************************************************************************************/

/**********************************************************************************************************************
 *  LOCAL DATA PROTOTYPES
 **********************************************************************************************************************/

#define SPI_START_SEC_VAR_NO_INIT_UNSPECIFIED
/*SAFETYMCUSW 338 S MR:19.1 <APPROVED> "Based on Autosar Memory Map strategy" */
#include "MemMap.h"

/* static structures */
static VAR(Spi_HwInstType, SPI_VAR_NOINIT) Spi_HwSeqIdInst_t[SPI_MAX_HWUNIT];
/* job event structure, for actual job */
static VAR(Spi_HwEventType, SPI_VAR_NOINIT) Spi_HwJobEvent_at[SPI_MAX_HWUNIT];
/* data structure for Spi_Dlc.c */
static VAR(Spi_DlcEventType, SPI_VAR_NOINIT)Spi_DlcSetEvent_t[SPI_MAX_HWUNIT];

#define SPI_STOP_SEC_VAR_NO_INIT_UNSPECIFIED
/*SAFETYMCUSW 338 S MR:19.1 <APPROVED> "Based on Autosar Memory Map strategy" */
#include "MemMap.h"

/**********************************************************************************************************************
 *  GLOBAL DATA
 **********************************************************************************************************************/

/**********************************************************************************************************************
 *  LOCAL FUNCTION PROTOTYPES
 **********************************************************************************************************************/
/*SAFETYMCUSW 580 S MR:1.1 <APPROVED> "AUTOSAR Memory Mapping approach "*/
#undef SPI_START_SEC_CODE
#define SPI_START_SEC_CODE
/*SAFETYMCUSW 338 S MR:19.1 <APPROVED> "Based on Autosar Memory Map strategy" */
#include "MemMap.h"

static FUNC(void, SPI_CODE) Spi_HwJobConsumeEvent(uint32 HW_Inst_Nr_u32);
static FUNC(void, SPI_CODE) Spi_HwSeqConsumeEvent(uint32 HW_Inst_Nr_u32);
static FUNC(void, SPI_CODE) Spi_HwSeqNext(uint32 HW_Inst_Nr_u32);
static FUNC(void, SPI_CODE) Spi_HwSeqComplete(uint32 HW_Inst_Nr_u32);
static FUNC(void, SPI_CODE) Spi_HwSeqCleanUp(uint32 HW_Inst_Nr_u32);
static FUNC(void, SPI_CODE) Spi_HwSeqAdjust(uint32 SequenceID);

static FUNC(void, SPI_CODE) Spi_HwJobsSort(void);
static FUNC(void, SPI_CODE) Spi_HwJobTransmit(uint32 HW_Inst_Nr_u32);
static FUNC(void, SPI_CODE) Spi_HwJobClear(uint32 SequenceID);
static FUNC(Std_ReturnType, SPI_CODE) Spi_HwJobQueue(uint32 SequenceID);

/**********************************************************************************************************************
 *  LOCAL FUNCTIONS
 **********************************************************************************************************************/

/***********************************************************************************************************************
 *  Spi_HwSeqConsumeEvent
 **********************************************************************************************************************/
/*! \brief      This method consumes pending sequence events.
 *  \param[in]  HW_Inst_Nr_u32
 *  \param[out] none
 *  \return     none
 *  \pre        none
 *  \context    Function is called from task level
 *  \note       Specification of module event handling
 **********************************************************************************************************************/
static FUNC(void, SPI_CODE) Spi_HwSeqConsumeEvent(uint32 HW_Inst_Nr_u32)
{
    uint32 SeqId_u32;
    Spi_HwEventDefType ev_loc_t ;

    SeqId_u32  = Spi_HwSeqIdInst_t[HW_Inst_Nr_u32].SeqIdAkt_u8;

    Spi_GlobalSuspend();
    /* back it up */
    ev_loc_t = (Spi_HwEventDefType) Spi_HwSeqEvent_at[SeqId_u32].Event_u8 ;
    /* consume event in global structure */
    Spi_HwSeqEvent_at[SeqId_u32].Event_u8 = SPI_HW_EV_CONSUMED;
    Spi_GlobalRestore();

    switch (ev_loc_t)
    {
        case SPI_HW_EV_INIT:
        break;

        case SPI_HW_EV_SEQ_START:
        Spi_HwJobEvent_at[HW_Inst_Nr_u32].Event_u8 = SPI_HW_EV_JOB_START ;  /* start first job */
        Spi_HwJobConsumeEvent(HW_Inst_Nr_u32) ;
        break;

        case SPI_HW_EV_CANCEL:
        break;

        case SPI_HW_EV_FAILED:
        /* if a job fails, a sequence also fails ! See Spi_HwJobConsumeEvent() */
        break;

        default:
        break;
    }
}

/***********************************************************************************************************************
 *  Spi_JobConsumeEvent
 **********************************************************************************************************************/
/*! \brief      This method consumes pending job events.
 *  \param[in]  HW_Inst_Nr_u32
 *  \param[out] none
 *  \return     none
 *  \pre        Input parameter must not be NULL.
 *  \context    Function is called from task level
 *  \note       module event handling
 **********************************************************************************************************************/
static FUNC(void, SPI_CODE) Spi_HwJobConsumeEvent(uint32 HW_Inst_Nr_u32)
{
    Spi_HwEventDefType ev_loc ;

    Spi_GlobalSuspend();
    ev_loc = (Spi_HwEventDefType) Spi_HwJobEvent_at[HW_Inst_Nr_u32].Event_u8 ;
    /* consume event in global structure */
    Spi_HwJobEvent_at[HW_Inst_Nr_u32].Event_u8 = SPI_HW_EV_CONSUMED ;
    Spi_GlobalRestore();

    switch (ev_loc)
    {
        case SPI_HW_EV_INIT:
        break;

        case SPI_HW_EV_JOB_START:
        /* start a job. If this job is synchronous it has to be decoupled from the ISR context */
        if(Spi_DlcSetEvent_t[HW_Inst_Nr_u32].Event_u8 == SPI_DLC_EV_SYNCTX)
        {
            if(Spi_DlcSetEvent(&Spi_DlcSetEvent_t[HW_Inst_Nr_u32]) == E_NOT_OK)
            {
                /* job transfer request was declined */
                Spi_JobsSorted_at[Spi_HwJobEvent_at[HW_Inst_Nr_u32].TaskID_u8].JobStatus_u8 = SPI_JOB_PENDING ;
                Spi_HwJobEvent_at[HW_Inst_Nr_u32].Event_u8 = SPI_HW_EV_JOB_START;
                Spi_DlcSetEvent_t[HW_Inst_Nr_u32].Event_u8 = SPI_DLC_EV_SYNCTX;
            }
        }
        else
        {
            Spi_HwJobTransmit(HW_Inst_Nr_u32) ;
        }
        break;

        case SPI_HW_EV_JOB_FINISHED:
        /* check if a sequence has completed */
        Spi_HwSeqComplete(HW_Inst_Nr_u32);
        /* transmit a new job if it is pending */
        Spi_HwJobTransmit(HW_Inst_Nr_u32) ;
        break;

        case SPI_HW_EV_CANCEL:
        break;

        case SPI_HW_EV_FAILED:
        /* clean up the failed sequence */
        Spi_HwSeqCleanUp(HW_Inst_Nr_u32);
        /* transmit a new job if it is pending */
        Spi_HwJobTransmit(HW_Inst_Nr_u32) ;
        break;

        default:
        break;
    }
}

/***********************************************************************************************************************
 *  Spi_HwJobsSort
 **********************************************************************************************************************/
/*! \brief      This method sorts all jobs by priority.
 *  \param[in]  none
 *  \param[out] none
 *  \return     none
 *  \pre        Input parameter must not be NULL.
 *  \context    Function is called from task level
 *  \note       Specification of module job handling
 **********************************************************************************************************************/
/* SourceId : SPI_SourceId_033 */
/* DesignId : SPI_DesignId_001 */
/* Requirements : AR_SPI_SR53, AR_SPI_SR54, AR_SPI_SR55 */
static FUNC(void, SPI_CODE) Spi_HwJobsSort(void)
{
    uint32 JobIdx_u32;    /* job Id index for loop  */
    uint32 SortedIdx_u32; /* Sorted index for priority array */
    uint32 PrioIdx_u32;   /* priority index for loop*/
    uint32 Prio_u32;      /* priority of a job */
    uint8  JobId;

    /* sort jobs by priority */
    /* sorting looks like this:
     *  Id             prio             ->  Id          prio
     * Job0             1          ------>  Job1         3
     * Job1             3  -------/         Job2         2
     * Job2             2                   Job4         2
     * Job3             0                   Job0         1
     * Job4             2                   Job3         0*/

    /* this loop decreases the priority from max to min */
    Prio_u32 = SPI_MAX_JOB_PRIO ;
    SortedIdx_u32 = 0U;
    for(PrioIdx_u32 = 0U ; PrioIdx_u32 <= SPI_MAX_JOB_PRIO ; PrioIdx_u32++)
    {
        /* this loop searches for all jobs with the same priority */
        for(JobIdx_u32 = 0U ; JobIdx_u32 < Spi_ConfigPtr_pt->Spi_MaxJobs ; JobIdx_u32++)
        {
            /* scan for actual priority */
            if(Spi_ConfigPtr_pt->Spi_JobCfg[JobIdx_u32].Spi_JobPrio == Prio_u32)
            {
                JobId = Spi_ConfigPtr_pt->Spi_JobCfg[JobIdx_u32].Spi_JobID;
                /* assign first job with highest priority [3,2,1,0] */
                Spi_JobsSorted_at[SortedIdx_u32].JobID_u8 = (uint8)JobId;
                Spi_JobPrioConvTbl_at[JobId] = (uint8)SortedIdx_u32;
                SortedIdx_u32++;
                /* job assigned, scan for next */
            }
            Spi_JobsSorted_at[JobIdx_u32].SeqID_u8  = 0xFFU ;
        }
        /* scan all jobs for next lower priority */
        Prio_u32--;
    }
}

/***********************************************************************************************************************
 *  Spi_HwJobQueue
 **********************************************************************************************************************/
/*! \brief      This method queues jobs by priority.
 *  \param[in]  none
 *  \param[out] none
 *  \return     none
 *  \pre        Input parameter must not be NULL.
 *  \context    Function is called from task level
 *  \note       Specification of module job handling
 **********************************************************************************************************************/
static FUNC (Std_ReturnType, SPI_CODE)Spi_HwJobQueue(uint32 SequenceID)
{
    uint32 JobCnt_u32; /* loop over the number of jobs in a sequence */
    uint32 JobIdx_u32; /* index to loop over the max number of jobs */
    uint32 JobId_u32;  /* ID of a job*/
    P2VAR(Spi_JobSortedType, AUTOMATIC, SPI_VAR_NOINIT) JobChain_pt; /* pointer for a sequence chain of jobs */
    
	/*SAFETYMCUSW 7 C MR:14.7 <APPROVED> "Reason - Error check. Improve execution time. "*/
    /* first check all jobs if they are allowed to be queued */
    for(JobCnt_u32 = 2U ; JobCnt_u32 <= ((uint32)Spi_ConfigPtr_pt->Spi_SeqCfg[SequenceID].Spi_JobList[1U] + 1U) ; JobCnt_u32++)
    {
        /* get first assigned job of a sequence, starts a index 1 */
        JobId_u32  = Spi_ConfigPtr_pt->Spi_SeqCfg[SequenceID].Spi_JobList[JobCnt_u32];
        JobIdx_u32 = Spi_JobPrioConvTbl_at[JobId_u32];
        /* check the job for collision  */
        if(  (Spi_JobsSorted_at[JobIdx_u32].JobStatus_u8 == SPI_JOB_PENDING)
          || (Spi_JobsSorted_at[JobIdx_u32].JobStatus_u8 == SPI_JOB_ACTIVE))
        {
            /* the sequence is rejected due to common used jobs */
        	/*SAFETYMCUSW 7 C MR:14.7 <APPROVED> "Reason - Needs more control flow change to address this"*/
            return(E_NOT_OK) ;
        }
    }
    /* checking passed, continue to queue
     * clear last index marker
     */
    JobChain_pt = (P2VAR(Spi_JobSortedType, AUTOMATIC, SPI_VAR_NOINIT))NULL_PTR;
    /* loop over all jobs sorted by priority */
    for(JobCnt_u32 = 2U ; JobCnt_u32 <= ((uint32)Spi_ConfigPtr_pt->Spi_SeqCfg[SequenceID].Spi_JobList[1U] + 1U) ; JobCnt_u32++)
    {
        /* get first assigned job of a sequence, starts a index 1 */
        JobId_u32  = Spi_ConfigPtr_pt->Spi_SeqCfg[SequenceID].Spi_JobList[JobCnt_u32] ;
        JobIdx_u32 = Spi_JobPrioConvTbl_at[JobId_u32];
        /* assign the first job with highest priority in a sequence */
        Spi_JobsSorted_at[JobIdx_u32].SeqID_u8   = (uint8)SequenceID ;
        /* job is marked for transfer */
        Spi_JobsSorted_at[JobIdx_u32].JobStatus_u8 = SPI_JOB_PENDING ;
        /* now build up job chain */
        if(JobChain_pt != NULL_PTR)
        {
            /* set next pointer to second assigned job */
            JobChain_pt->JobNext_pt = &Spi_JobsSorted_at[JobIdx_u32];
        }

        /* set global job status*/
        Spi_ConfigPtr_pt->Spi_GlobalStatus_pt->Spi_JobResult[JobId_u32] = SPI_JOB_QUEUED;
        /* save actual pointer to chain pointer for later use */
        JobChain_pt = &Spi_JobsSorted_at[JobIdx_u32];
    }
    /* all jobs accepted and queued,
     * sequence successfully added
     */
    Spi_HwSeqIdInst_t[Spi_ConfigPtr_pt->Spi_SeqCfg[SequenceID].Spi_JobList[0U]].SeqNrPend_u8++ ;
    return(E_OK) ;
}

/***********************************************************************************************************************
 *  Spi_HwJobClear
 **********************************************************************************************************************/
/*! \brief      This method clears queued jobs.
 *  \param[in]  none
 *  \param[out] none
 *  \return     none
 *  \pre        Input parameter must not be NULL.
 *  \context    Function is called from task level
 *  \note       Specification of module job handling
 **********************************************************************************************************************/
static FUNC (void, SPI_CODE)Spi_HwJobClear(uint32 SequenceID)
{
    uint32 JobIdx_u32; /* index to loop over the max number of jobs */
    uint32 JobId_u32;  /* ID of a job*/
    P2VAR(Spi_JobSortedType, AUTOMATIC, SPI_VAR_NOINIT) JobChain_pt; /* pointer for a sequence chain of jobs */
    P2VAR(Spi_JobSortedType, AUTOMATIC, SPI_VAR_NOINIT) JobAkt_pt;   /* pointer for a job chain in a sequence */

    /* get first assigned job of a sequence, starts a index 1 */
    JobId_u32   = Spi_ConfigPtr_pt->Spi_SeqCfg[SequenceID].Spi_JobList[2U] ;
    JobIdx_u32  = Spi_JobPrioConvTbl_at[JobId_u32];
    JobChain_pt = &Spi_JobsSorted_at[JobIdx_u32];

    /* now clean up the remaining chained jobs */
    while(JobChain_pt != NULL_PTR)
    {
        /* clear all pending jobs belonging to this sequence */
        JobId_u32 = JobChain_pt->JobID_u8;
        if(  (JobChain_pt->JobStatus_u8 == SPI_JOB_PENDING)
          || (JobChain_pt->JobStatus_u8 == SPI_JOB_ACTIVE))
        {
            /* set job state */
            JobChain_pt->JobStatus_u8  = SPI_JOB_OK;
            Spi_ConfigPtr_pt->Spi_GlobalStatus_pt->Spi_JobResult[JobId_u32] = SPI_JOB_OK;
        }
        /* save current job */
        JobAkt_pt   = JobChain_pt;
        /* get next job */
        JobChain_pt = JobAkt_pt->JobNext_pt;
        /* and remove the reference */
        JobAkt_pt->JobNext_pt = NULL_PTR;
    }
    /* clear number of jobs */
    Spi_HwSeqEvent_at[SequenceID].NrOfJobs_u8 = 0U ;
    return;
}

/***********************************************************************************************************************
 *  Spi_HwJobTransmit
 **********************************************************************************************************************/
/*! \brief      This method transmits queued jobs.
 *  \param[in]  none
 *  \param[out] none
 *  \return     none
 *  \pre        Input parameter must not be NULL.
 *  \context    Function is called from task level
 *  \note       Specification of module job handling
 **********************************************************************************************************************/
static FUNC (void, SPI_CODE)Spi_HwJobTransmit(uint32 HW_Inst_Nr_u32)
{
    uint32 JobCnt_u32;
    uint32 JobIdx_u32;    /* index to loop over the max number of jobs */
    uint32 SeqId_u32;     /* ID of a sequence */
    uint32 SeqIdAt_u32;   /* ID of a sequence attached to a job */

    /* skip the remaining code, if no sequence has been queued */
    if(Spi_HwSeqIdInst_t[HW_Inst_Nr_u32].SeqNrPend_u8 == 0U)
    {
        /* Do nothing */
    }
    else
    {
        /* get actual sequence ID */
        SeqId_u32    = Spi_HwSeqIdInst_t[HW_Inst_Nr_u32].SeqIdAkt_u8;

        /* search for serviceable jobs in the Seq*/
        for(JobCnt_u32 = 2U; JobCnt_u32 <= ((uint32)Spi_ConfigPtr_pt->Spi_SeqCfg[SeqId_u32].Spi_JobList[1U] + 1U) ; JobCnt_u32++)
        {
            /* get first assigned job of a sequence, starts a index 1 */
            JobIdx_u32 = Spi_JobPrioConvTbl_at[Spi_ConfigPtr_pt->Spi_SeqCfg[SeqId_u32].Spi_JobList[JobCnt_u32]];

            /* check all jobs if they are marked for transmission     */
            /* one of these jobs will be transmitted, done by queuing */
            if(  (Spi_JobsSorted_at[JobIdx_u32].JobStatus_u8 == SPI_JOB_PENDING)
              || (Spi_JobsSorted_at[JobIdx_u32].JobStatus_u8 == SPI_JOB_ACTIVE ))
            {
                /* if a job is marked for transmission, but belongs not to the actual
                 * sequence, it can be transmitted because the JobIdx_u32 loop goes from highest to lowest priority
                 * get Job ID
                 */
                Spi_DlcSetEvent_t[HW_Inst_Nr_u32].JobID_u8 = Spi_JobsSorted_at[JobIdx_u32].JobID_u8;
                SeqIdAt_u32                = Spi_JobsSorted_at[JobIdx_u32].SeqID_u8;

                Spi_DlcSetEvent_t[HW_Inst_Nr_u32].Event_u8 = SPI_DLC_EV_ASYNCTX;

                /* if the sequence interruptible and a job is not attached to this sequence */
                if(  (Spi_ConfigPtr_pt->Spi_SeqCfg[SeqId_u32].Spi_SeqInt == TRUE)
                  && (SeqIdAt_u32 != SeqId_u32))
                {
                    /* prepare job transmission ! */
                    Spi_JobsSorted_at[JobIdx_u32].JobStatus_u8 = SPI_JOB_ACTIVE ;
                    /* Set Global Status */
                    Spi_ConfigPtr_pt->Spi_GlobalStatus_pt->Spi_JobResult[Spi_JobsSorted_at[JobIdx_u32].JobID_u8] = SPI_JOB_PENDING;
                }
                /* sequence is not interruptible */
                else
                {
                    if(SeqIdAt_u32 == SeqId_u32)
                    {
                        /* job transmission is asynchronous */
                        Spi_JobsSorted_at[JobIdx_u32].JobStatus_u8 = SPI_JOB_ACTIVE ;
                        /* Set Global Status */
                        Spi_ConfigPtr_pt->Spi_GlobalStatus_pt->Spi_JobResult[Spi_JobsSorted_at[JobIdx_u32].JobID_u8] = SPI_JOB_PENDING;
                    }
                }
                if(Spi_JobsSorted_at[JobIdx_u32].JobStatus_u8 == SPI_JOB_ACTIVE)
                {
                    if(Spi_DlcSetEvent(&Spi_DlcSetEvent_t[HW_Inst_Nr_u32]) == E_NOT_OK)
                    {
                        /* job transfer request was declined */
                        Spi_JobsSorted_at[JobIdx_u32].JobStatus_u8 = SPI_JOB_PENDING ;
                        Spi_HwJobEvent_at[HW_Inst_Nr_u32].Event_u8 = SPI_HW_EV_JOB_START;
                        Spi_DlcSetEvent_t[HW_Inst_Nr_u32].Event_u8 = SPI_DLC_EV_CONSUMED;
                    }
                    break ;
                }
             }
        }
    }
}

/***********************************************************************************************************************
 *  Spi_HwSeqAdjust
 **********************************************************************************************************************/
/*! \brief      This method reassigns the pointers for pending sequences
 *  \param[in]  none
 *  \param[out] none
 *  \return     none
 *  \pre        none
 *  \context    Function is called from task or interrupt level
 *  \note       called from: Spi_HwSeqNext or Spi_HwSetEvent
 **********************************************************************************************************************/
static FUNC(void, SPI_CODE) Spi_HwSeqAdjust(uint32 SequenceID)
{
    if(Spi_HwSeqEvent_at[SequenceID].SeqPrev_pt != NULL_PTR)
    {
        /* adjust pointer to prev sequence */
        if(Spi_HwSeqEvent_at[SequenceID].SeqNext_pt != NULL_PTR)
        {
            Spi_HwSeqEvent_at[SequenceID].SeqNext_pt->SeqPrev_pt = Spi_HwSeqEvent_at[SequenceID].SeqPrev_pt;
        }
    }
    if(Spi_HwSeqEvent_at[SequenceID].SeqNext_pt != NULL_PTR)
    {
        /* adjust pointer to next sequence */
        if(Spi_HwSeqEvent_at[SequenceID].SeqPrev_pt != NULL_PTR)
        {
            Spi_HwSeqEvent_at[SequenceID].SeqPrev_pt->SeqNext_pt = Spi_HwSeqEvent_at[SequenceID].SeqNext_pt;
        }
    }
    /* clear pointer's */
    Spi_HwSeqEvent_at[SequenceID].SeqNext_pt = NULL_PTR;
    Spi_HwSeqEvent_at[SequenceID].SeqPrev_pt = NULL_PTR;
}

/***********************************************************************************************************************
 *  Spi_HwSeqNext
 **********************************************************************************************************************/
/*! \brief      This method checks for a pending sequence
 *  \param[in]  none
 *  \param[out] none
 *  \return     none
 *  \pre        Input parameter must not be NULL.
 *  \context    Function is called from task level
 *  \note       called from: Spi_HwSeqCleanUp, Spi_HwSeqComplete
 **********************************************************************************************************************/
/* SourceId : SPI_SourceId_034 */
/* DesignId : SPI_DesignId_022 */
/* Requirements : AR_SPI_SR52 */
static FUNC(void, SPI_CODE) Spi_HwSeqNext(uint32 HW_Inst_Nr_u32)
{
    uint32 SeqId_u32;    /* Id of a sequence */

    /* this sequence was reported as finished */
    SeqId_u32 = Spi_HwSeqIdInst_t[HW_Inst_Nr_u32].SeqIdRep_u8;
    /* we need to check the sequence which was reported by JobComplete */
    if(Spi_HwSeqIdInst_t[HW_Inst_Nr_u32].SeqNrPend_u8 == 0U)
    {
        /* all sequences were executed, set global status to IDLE */
        /* clear pointer's */
        Spi_HwSeqEvent_at[SeqId_u32].SeqNext_pt = NULL_PTR;
        Spi_HwSeqEvent_at[SeqId_u32].SeqPrev_pt = NULL_PTR;
        Spi_HwSeqIdInst_t[HW_Inst_Nr_u32].SeqIdAkt_u8  = 0xFFU;
        Spi_HwSeqIdInst_t[HW_Inst_Nr_u32].SeqIdPend_u8 = 0xFFU;
        Spi_HwSeqIdInst_t[HW_Inst_Nr_u32].SeqIdRep_u8  = 0xFFU;
        Spi_DrvStatus = SPI_IDLE;
    }
    else
    {
        /* check if this sequence is the one actually being exectuted */
        if(Spi_HwSeqIdInst_t[HW_Inst_Nr_u32].SeqIdAkt_u8 == SeqId_u32)
        {
            /* set next sequence to execute */
            Spi_HwSeqIdInst_t[HW_Inst_Nr_u32].SeqIdAkt_u8 = Spi_HwSeqEvent_at[SeqId_u32].SeqNext_pt->TaskID_u8;
        }
        /* due to the priority of the jobs, any sequence may be reported as finished */
        /* so the pointers need to be adjusted and the next sequence in line has to be executed */
        Spi_HwSeqAdjust(SeqId_u32);
    }
}


/***********************************************************************************************************************
 *  Spi_HwSeqCleanUp
 **********************************************************************************************************************/
/*! \brief      This method cleans up the remaining jobs of a failed sequence.
 *  \param[in]  none
 *  \param[out] none
 *  \return     none
 *  \pre        Input parameter must not be NULL.
 *  \context    Function is called from task level
 *  \note       Specification of module sequence handling
 **********************************************************************************************************************/
/* SourceId : SPI_SourceId_035 */
/* DesignId : SPI_DesignId_015 */
/* Requirements : AR_SPI_SR63 */
static FUNC(void, SPI_CODE) Spi_HwSeqCleanUp(uint32 HW_Inst_Nr_u32)
{
    uint32 JobIdx_u32;    /* Index of a job   */
    uint32 JobId_u32;     /* ID of a job      */
    uint32 SeqId_u32;    /* Id of a sequence */

    P2VAR(Spi_JobSortedType, AUTOMATIC, SPI_VAR_NOINIT) JobChain_pt; /* pointer for a job chain in a sequence */
    P2VAR(Spi_JobSortedType, AUTOMATIC, SPI_VAR_NOINIT) JobAkt_pt;   /* pointer for a job chain in a sequence */

    /* this sequence was reported as finished */
    SeqId_u32 = Spi_HwSeqIdInst_t[HW_Inst_Nr_u32].SeqIdRep_u8;
    /* this is set by the DLC module, every time a job has completed */
    JobId_u32   = Spi_HwJobEvent_at[HW_Inst_Nr_u32].TaskID_u8;
    JobIdx_u32  = Spi_JobPrioConvTbl_at[JobId_u32];
    /* set chain pointer */
    JobChain_pt = &Spi_JobsSorted_at[JobIdx_u32];

    /* Enter Critical Section */
    Spi_GlobalSuspend();

    /* next seek all jobs belonging to this sequence  and clear these jobs */
    while(JobChain_pt != NULL_PTR)
    {
        JobId_u32 = JobChain_pt->JobID_u8;
        /* reset sequence ID */
        JobChain_pt->SeqID_u8 = 0xFFU;
        /* set jobstate */
        JobChain_pt->JobStatus_u8  = SPI_JOB_FAILED;
        Spi_ConfigPtr_pt->Spi_GlobalStatus_pt->Spi_JobResult[JobId_u32] = SPI_JOB_FAILED;
        /* save current job */
        JobAkt_pt = JobChain_pt;
        /* get next job */
        JobChain_pt = JobAkt_pt->JobNext_pt;
        /* and remove the reference */
        JobAkt_pt->JobNext_pt = NULL_PTR;
    }
    /* clear number of jobs */
    Spi_HwSeqEvent_at[SeqId_u32].NrOfJobs_u8 = 0U;
    /* clear transmission mode */
    Spi_HwSeqEvent_at[SeqId_u32].Guard_u8    = SPI_HW_UNDEF;

    Spi_HwSeqIdInst_t[HW_Inst_Nr_u32].SeqNrPend_u8--;
    /* set sequence result */
    Spi_ConfigPtr_pt->Spi_GlobalStatus_pt->Spi_SeqResult[SeqId_u32] = SPI_SEQ_FAILED ;
    /* goto next sequence */
    Spi_HwSeqNext(HW_Inst_Nr_u32);
    /* now call configured sequence error notification */
    if(Spi_ConfigPtr_pt->Spi_SeqCfg[SeqId_u32].Spi_SequenceEndNotification != NULL_PTR)
    {
        Spi_ConfigPtr_pt->Spi_SeqCfg[SeqId_u32].Spi_SequenceEndNotification() ;
    }

    /* Exit Critical Section */
    Spi_GlobalRestore();
}

/***********************************************************************************************************************
 *  Spi_HwSeqComplete
 **********************************************************************************************************************/
/*! \brief      This method checks and reports a completed sequence.
 *  \param[in]  none
 *  \param[out] none
 *  \return     none
 *  \pre        Input parameter must not be NULL.
 *  \context    Function is called from task level
 *  \note       Specification of module sequence handling
 **********************************************************************************************************************/
/* SourceId : SPI_SourceId_036 */
/* DesignId : SPI_DesignId_015 */
/* Requirements : AR_SPI_SR61, AR_SPI_SR84, AR_SPI_SR86, AR_SPI_SR287, AR_SPI_SR289, AR_SPI_SR294, AR_SPI_SR296 */
static FUNC(void, SPI_CODE) Spi_HwSeqComplete(uint32 HW_Inst_Nr_u32)
{
    uint32 SeqId_u32;    /* Id of a sequence */

    /* this sequence was reported as finished */
    SeqId_u32 = Spi_HwSeqIdInst_t[HW_Inst_Nr_u32].SeqIdRep_u8;

    /* now check if a sequence has completed */
    if(Spi_HwSeqEvent_at[SeqId_u32].NrOfJobs_u8 == 0U)
    {
        /* Enter Critical Section */
        Spi_GlobalSuspend();
        /* check if sequence has been canceled */
        if(Spi_ConfigPtr_pt->Spi_GlobalStatus_pt->Spi_SeqResult[SeqId_u32] != SPI_SEQ_CANCELLED)
        {
            /* this part has already been done in the cancel part */
            Spi_HwSeqIdInst_t[HW_Inst_Nr_u32].SeqNrPend_u8--;
            Spi_ConfigPtr_pt->Spi_GlobalStatus_pt->Spi_SeqResult[SeqId_u32] = SPI_SEQ_OK;
        }
        /* clear transmission mode */
        Spi_HwSeqEvent_at[SeqId_u32].Guard_u8 = SPI_HW_UNDEF;
        /* goto next sequence, handled in the Processing function */
        Spi_HwSeqNext(HW_Inst_Nr_u32);
        /* call configured sequence end notification */
        if(Spi_ConfigPtr_pt->Spi_SeqCfg[SeqId_u32].Spi_SequenceEndNotification != NULL_PTR )
        {
            Spi_ConfigPtr_pt->Spi_SeqCfg[SeqId_u32].Spi_SequenceEndNotification() ;
        }
        /* Exit Critical Section */
        Spi_GlobalRestore();
    }
}

/**********************************************************************************************************************
 *  GLOBAL FUNCTIONS
 **********************************************************************************************************************/

/***********************************************************************************************************************
 *  Spi_HwJobComplete
 **********************************************************************************************************************/
/*! \brief      This method handles the job completition.
 *  \param[in]  none
 *  \param[out] none
 *  \return     none
 *  \context    Function is called from interrupt level
 *  \note        Specification of module job transfer
 **********************************************************************************************************************/
/* SourceId : SPI_SourceId_037 */
/* DesignId : SPI_DesignId_014 */
/* Requirements : AR_SPI_SR60, AR_SPI_SR62, AR_SPI_SR83, AR_SPI_SR286, AR_SPI_SR289, AR_SPI_SR291, AR_SPI_SR293 */
FUNC(void, SPI_CODE) Spi_HwJobComplete(uint32 Job_u16, uint32 DLC_u16)
{
    uint32 JobIdx_u32;    /* Index of a job*/
    uint32 SeqId_u32;     /* ID of a sequence */

    /* this is set by the DLC module, every time a job has completed */
    JobIdx_u32 = Spi_JobPrioConvTbl_at[Job_u16];
    /* reset job in priority queue */
    /* backup sequence ID */
    SeqId_u32 = Spi_JobsSorted_at[JobIdx_u32].SeqID_u8 ;

    Spi_HwSeqIdInst_t[DLC_u16].SeqIdRep_u8 = (uint8)SeqId_u32;
    Spi_HwJobEvent_at[DLC_u16].TaskID_u8   = (uint8)Job_u16;
    /* reset sequence ID */
    Spi_JobsSorted_at[JobIdx_u32].SeqID_u8 = 0xFFU;

    /* one job less remaining in this sequence */
    if(Spi_HwSeqEvent_at[SeqId_u32].NrOfJobs_u8 > 0U)
    {
        Spi_HwSeqEvent_at[SeqId_u32].NrOfJobs_u8-- ;
    }
    /* call notification */
    if(Spi_ConfigPtr_pt->Spi_JobCfg[Job_u16].Spi_JobEndNotification != NULL_PTR)
    {
        /* call job end notification */
        Spi_ConfigPtr_pt->Spi_JobCfg[Job_u16].Spi_JobEndNotification() ;
    }
    /* set result of the JobID */
    /* this is important to do this after the callback notification */
    /* otherwise if a sequence is queued with the same job, the job is reloaded endless */
    /* Now check if an error was detected and update the status */
    if(Spi_ConfigPtr_pt->Spi_GlobalStatus_pt->Spi_DlcResult[DLC_u16] == SPI_DLC_FAILED)
    {
        Spi_ConfigPtr_pt->Spi_GlobalStatus_pt->Spi_JobResult[Job_u16] = SPI_JOB_FAILED ;
        Spi_JobsSorted_at[JobIdx_u32].JobStatus_u8 = SPI_JOB_FAILED;
        Spi_HwJobEvent_at[DLC_u16].Event_u8 = SPI_HW_EV_FAILED ;
    }
    else
    {
        Spi_ConfigPtr_pt->Spi_GlobalStatus_pt->Spi_JobResult[Spi_HwJobEvent_at[DLC_u16].TaskID_u8] = SPI_JOB_OK ;
        Spi_JobsSorted_at[JobIdx_u32].JobStatus_u8 = SPI_JOB_OK;
        Spi_HwJobEvent_at[DLC_u16].Event_u8 = SPI_HW_EV_JOB_FINISHED ;
    }
    /* job was serviced */
    Spi_DlcSetEvent_t[DLC_u16].Event_u8 = SPI_DLC_EV_CONSUMED;

    /* reload the next transmission in the current context (interrupt) */
    Spi_HwJobConsumeEvent(DLC_u16) ;
}

/***********************************************************************************************************************
 *  Spi_HwInit
 **********************************************************************************************************************/
/*! \brief      This method handles the initialization.
 *  \param[in]  none
 *  \param[out] none
 *  \return     none
 *  \pre        Input parameter must not be NULL.
 *  \context    Function is called from task level
 *  \note       Specification of module initialization
 **********************************************************************************************************************/
FUNC(void, SPI_CODE) Spi_HwInit(void)
{
    uint32 SeqIdx_u32 ;
    uint32 HW_Inst_Nr_u32;   /* Instance of the SPI Hw */

    /* reset global data variables */

    for(HW_Inst_Nr_u32 = 0U ; HW_Inst_Nr_u32 < Spi_ConfigPtr_pt->Spi_MaxDlcs ; HW_Inst_Nr_u32++)
    {
      /* reset the working index of current processed sequence in the sequence event array */
      Spi_HwSeqIdInst_t[HW_Inst_Nr_u32].SeqIdAkt_u8  = 0xFFU;
      /* reset the working index of a pending sequence in the sequence event array */
      Spi_HwSeqIdInst_t[HW_Inst_Nr_u32].SeqIdPend_u8 = 0xFFU;
      /* reset the number of current queued sequences */
      Spi_HwSeqIdInst_t[HW_Inst_Nr_u32].SeqNrPend_u8 = 0U;
      /* reset the number of reported sequences */
      Spi_HwSeqIdInst_t[HW_Inst_Nr_u32].SeqIdRep_u8 = 0xFFU;
      Spi_HwJobEvent_at[HW_Inst_Nr_u32].Event_u8 = SPI_HW_EV_CONSUMED ;
      Spi_DlcSetEvent_t[HW_Inst_Nr_u32].Event_u8 = SPI_DLC_EV_CONSUMED;
    }

    for(SeqIdx_u32 = 0U ; SeqIdx_u32 < Spi_ConfigPtr_pt->Spi_MaxSequences ; SeqIdx_u32++)
    {
        Spi_HwSeqEvent_at[SeqIdx_u32].Event_u8 = SPI_HW_EV_CONSUMED ;
        Spi_HwSeqEvent_at[SeqIdx_u32].Guard_u8 = SPI_HW_UNDEF ;
    }
    Spi_HwJobsSort() ;
    Spi_DlcInit() ;
}

/***********************************************************************************************************************
 *  Spi_HwDeInit
 **********************************************************************************************************************/
/*! \brief      This method handles the de-initialization.
 *  \param[in]  none
 *  \param[out] none
 *  \return     none
 *  \pre        Input parameter must not be NULL.
 *  \context    Function is called from task level
 *  \note       Specification of module initialization
 **********************************************************************************************************************/
FUNC(void, SPI_CODE) Spi_HwDeInit(void)
{
    Spi_DlcClose() ;
}

/***********************************************************************************************************************
 *  Spi_HwSetEvent
 **********************************************************************************************************************/
/*! \brief      This method handles incoming events.
 *  \param[in]  Spi_HwEventType           Hw_Event_pt, event structure
 *  \param[out] none
 *  \return     E_OK                      event accepted
 *  \return     E_NOT_OK                  event rejected
 *  \pre        Input parameter must not be NULL.
 *  \context    Function is called from task level
 *  \note       Specification of module event handling
 **********************************************************************************************************************/
FUNC(Std_ReturnType, SPI_CODE) Spi_HwSetEvent
(
    P2CONST(Spi_HwEventType, AUTOMATIC, SPI_VAR) Hw_Event_pt
)
{
    uint32 SeqId_u32;       /* id of a sequence, used as index of the sequence array */
    uint32 JobNr_u32;       /* number of jobs in a configuration */
    uint32 HW_Inst_Nr_u32;   /* Instance of the SPI Hw */
    Std_ReturnType Ret_loc ;

    SeqId_u32 = (uint32)Hw_Event_pt->TaskID_u8;
    HW_Inst_Nr_u32 =  Spi_ConfigPtr_pt->Spi_SeqCfg[SeqId_u32].Spi_JobList[0U];
    Ret_loc = E_NOT_OK;

    /* Switch to disable or enable Interrupt based on mode */
    if(Hw_Event_pt->Guard_u8 == SPI_HW_SYNC)
    {
    	Spi_DlcDisableInt(HW_Inst_Nr_u32);
    }
    else
    {
    	Spi_DlcEnableInt(HW_Inst_Nr_u32);
    }
	
    if (Hw_Event_pt->Event_u8 == SPI_HW_EV_CANCEL)
    {
        /* start critical section */
        Spi_GlobalSuspend();
        /* only a pending sequence can be cancelled */
        if(Spi_ConfigPtr_pt->Spi_GlobalStatus_pt->Spi_SeqResult[SeqId_u32] == SPI_SEQ_PENDING)
        {
            /* only a pending sequence can be canceled */
            /* adjust pointers to next and previous sequence */
            Spi_HwSeqAdjust(SeqId_u32);
            Spi_HwSeqEvent_at[SeqId_u32].Event_u8 = SPI_HW_EV_CANCEL;
            Spi_HwSeqEvent_at[SeqId_u32].Guard_u8 = SPI_HW_UNDEF;
            Spi_HwSeqEvent_at[SeqId_u32].NrOfJobs_u8 = 0U;
            Spi_HwSeqIdInst_t[HW_Inst_Nr_u32].SeqNrPend_u8--;
            /* clear all jobs belonging to this sequence */
            Spi_HwJobClear(SeqId_u32) ;
            Spi_ConfigPtr_pt->Spi_GlobalStatus_pt->Spi_SeqResult[SeqId_u32] = SPI_SEQ_CANCELLED ;
        }
        /* stop critical section */
         Spi_GlobalRestore();
    }
    else
    {
        /* start critical section */
        Spi_GlobalSuspend();
        /* add a pending sequence ? */
        if(Spi_ConfigPtr_pt->Spi_GlobalStatus_pt->Spi_SeqResult[SeqId_u32] != SPI_SEQ_PENDING) /* sequence pending ? */
        {
            /* sequence ID is the index of this array Spi_HwSeqEvent_at[] */
            /* try to queue the jobs, if a job belonging to a sequence is still pending, the check fails */
            Ret_loc = Spi_HwJobQueue(SeqId_u32) ;
            if(Ret_loc == E_OK)
            {
                /* set event ID */
                Spi_HwSeqEvent_at[SeqId_u32].Event_u8  = Hw_Event_pt->Event_u8;
                /* set sequence ID */
                Spi_HwSeqEvent_at[SeqId_u32].TaskID_u8 = (uint8)SeqId_u32;
                /* set sequence to pending */
                Spi_ConfigPtr_pt->Spi_GlobalStatus_pt->Spi_SeqResult[SeqId_u32] = SPI_SEQ_PENDING ;
                Spi_DrvStatus = SPI_BUSY ;
                /* guard is used for sync or async tx */
                Spi_HwSeqEvent_at[SeqId_u32].Guard_u8 = Hw_Event_pt->Guard_u8;
                /* get number of jobs from the configuration */
                JobNr_u32 = Spi_ConfigPtr_pt->Spi_SeqCfg[SeqId_u32].Spi_JobList[1U] ;
                Spi_HwSeqEvent_at[SeqId_u32].NrOfJobs_u8 = (uint8)JobNr_u32 ;
                /* assign a queue number to this sequence */
                if(Spi_HwSeqIdInst_t[HW_Inst_Nr_u32].SeqNrPend_u8 == 1U)
                {
                    /* start transmit if no sequence was pending  -> Spi_HwSeqConsumeEvent */
                    Spi_HwSeqIdInst_t[HW_Inst_Nr_u32].SeqIdAkt_u8  = (uint8)SeqId_u32;
                    Spi_HwSeqIdInst_t[HW_Inst_Nr_u32].SeqIdPend_u8 = (uint8)SeqId_u32;
                    /* stop critical section */
                    Spi_GlobalRestore();
                    Spi_HwSeqConsumeEvent(HW_Inst_Nr_u32); /* consume event */
                }
                else
                {
                    /* assign pointer to next sequence */
                    Spi_HwSeqEvent_at[Spi_HwSeqIdInst_t[HW_Inst_Nr_u32].SeqIdPend_u8].SeqNext_pt = &Spi_HwSeqEvent_at[SeqId_u32];
                    /* assign last queued sequence to prev pointer */
                    Spi_HwSeqEvent_at[SeqId_u32].SeqPrev_pt = &Spi_HwSeqEvent_at[Spi_HwSeqIdInst_t[HW_Inst_Nr_u32].SeqIdPend_u8];
                    /* stop critical section */
                    Spi_GlobalRestore();
                    /* any other transmit is done by the queued sequences, the queue */
                    /* is checked any time a job has completed */
                }
                /* all settings done, now update pending variable */
                Spi_HwSeqIdInst_t[HW_Inst_Nr_u32].SeqIdPend_u8 = (uint8)SeqId_u32;
            }
            else
            {
                /* stop critical section */
                /* shared job still pending !*/
                Spi_GlobalRestore();
            }
        }
        else
        {
            /* sequence is pending! */
            /* stop critical section */
            Spi_GlobalRestore();
        }
    }
    return (Ret_loc);
}

/***********************************************************************************************************************
 *  Spi_HwProcessState
 **********************************************************************************************************************/
/*! \brief      This method handles the internal states.
 *  \param[in]  none
 *  \param[out] none
 *  \return     none
 *  \context    Function is called from task level
 *  \note       Specification of module event handling
 **********************************************************************************************************************/
FUNC(void, SPI_CODE) Spi_HwProcessState(uint32 HW_Inst_Nr_u32)
{
   /* schedule the event, check if a sequence has completed */
   Spi_HwJobConsumeEvent(HW_Inst_Nr_u32);
}
#undef SPI_STOP_SEC_CODE
#define SPI_STOP_SEC_CODE
/*SAFETYMCUSW 338 S MR:19.1 <APPROVED> "Based on Autosar Memory Map strategy" */
#include "MemMap.h"

/**********************************************************************************************************************
 *  END OF FILE: Spi_Hw.c
 *********************************************************************************************************************/
