/*******************************************************************************
 * (c) Copyright 2016-2018 Microsemi SoC Products Group.  All rights reserved.
 *
 * @file riscv_plic.h
 * @author Microsemi SoC Products Group
 * @brief Mi-V soft processor PLIC and PRCI access data structures and functions.
 *
 * SVN $Revision: 9838 $
 * SVN $Date: 2018-03-19 19:22:54 +0530 (Mon, 19 Mar 2018) $
 */
#ifndef RISCV_PLIC_H
#define RISCV_PLIC_H

#include <stdint.h>

#include "encoding.h"

#ifdef __cplusplus
extern "C" {
#endif

#define PLIC_NUM_SOURCES 31
#define PLIC_NUM_PRIORITIES 0

/*==============================================================================
 * Interrupt numbers:
 */
typedef enum
{
    NoInterrupt_IRQn = 0,
    External_1_IRQn  = 1,
    External_2_IRQn  = 2,
    External_3_IRQn  = 3, 
    External_4_IRQn  = 4,
    External_5_IRQn  = 5,
    External_6_IRQn  = 6,
    External_7_IRQn  = 7,
    External_8_IRQn  = 8,
    External_9_IRQn  = 9,
    External_10_IRQn = 10,
    External_11_IRQn = 11,
    External_12_IRQn = 12,
    External_13_IRQn = 13,
    External_14_IRQn = 14,
    External_15_IRQn = 15,
    External_16_IRQn = 16,
    External_17_IRQn = 17,
    External_18_IRQn = 18,
    External_19_IRQn = 19,
    External_20_IRQn = 20,
    External_21_IRQn = 21,
    External_22_IRQn = 22,
    External_23_IRQn = 23,
    External_24_IRQn = 24,
    External_25_IRQn = 25,
    External_26_IRQn = 26,
    External_27_IRQn = 27,
    External_28_IRQn = 28,
    External_29_IRQn = 29,
    External_30_IRQn = 30,
    External_31_IRQn = 31
} IRQn_Type;


/*==============================================================================
 * PLIC: Platform Level Interrupt Controller
 */
#define PLIC_BASE_ADDR 0x40000000UL

typedef struct
{
    volatile uint32_t PRIORITY_THRESHOLD;
    volatile uint32_t CLAIM_COMPLETE;
    volatile uint32_t reserved[1022];
} IRQ_Target_Type;

typedef struct
{
    volatile uint32_t ENABLES[32];
} Target_Enables_Type;

typedef struct
{
    /*-------------------- Source Priority --------------------*/
    volatile uint32_t SOURCE_PRIORITY[1024];
    
    /*-------------------- Pending array --------------------*/
    volatile const uint32_t PENDING_ARRAY[32];
    volatile uint32_t RESERVED1[992];
    
    /*-------------------- Target enables --------------------*/
    volatile Target_Enables_Type TARGET_ENABLES[15808];

    volatile uint32_t RESERVED2[16384];
    
    /*--- Target Priority threshold and claim/complete---------*/
    IRQ_Target_Type TARGET[15872];
    
} PLIC_Type;


#define PLIC    ((PLIC_Type *)PLIC_BASE_ADDR)

/*==============================================================================
 * PRCI: Power, Reset, Clock, Interrupt
 */
#define PRCI_BASE   0x44000000UL

typedef struct
{
    volatile uint32_t MSIP[4095];
    volatile uint32_t reserved;
    volatile uint64_t MTIMECMP[4095];
    volatile const uint64_t MTIME;
} PRCI_Type;

#define PRCI    ((PRCI_Type *)PRCI_BASE) 

/*==============================================================================
 * The function PLIC_init() initializes the PLIC controller and enables the 
 * global external interrupt bit.
 */
static inline void PLIC_init(void)
{
    uint32_t inc;
    unsigned long hart_id = read_csr(mhartid);

    /* Disable all interrupts for the current hart. */
    for(inc = 0; inc < ((PLIC_NUM_SOURCES + 32u) / 32u); ++inc)
    {
        PLIC->TARGET_ENABLES[hart_id].ENABLES[inc] = 0;
    }

    /* Set priorities to zero. */
    /* Should this really be done??? Calling PLIC_init() on one hart will cause
    * the priorities previously set by other harts to be messed up. */
    for(inc = 0; inc < PLIC_NUM_SOURCES; ++inc)
    {
        PLIC->SOURCE_PRIORITY[inc] = 0;
    }

    /* Set the threshold to zero. */
    PLIC->TARGET[hart_id].PRIORITY_THRESHOLD = 0;

    /* Enable machine external interrupts. */
    set_csr(mie, MIP_MEIP);
}

/*==============================================================================
 * The function PLIC_EnableIRQ() enables the external interrupt for the interrupt
 * number indicated by the parameter IRQn.
 */
static inline void PLIC_EnableIRQ(IRQn_Type IRQn)
{
    unsigned long hart_id = read_csr(mhartid);
    uint32_t current = PLIC->TARGET_ENABLES[hart_id].ENABLES[IRQn / 32];
    current |= (uint32_t)1 << (IRQn % 32);
    PLIC->TARGET_ENABLES[hart_id].ENABLES[IRQn / 32] = current;
}

/*==============================================================================
 * The function PLIC_DisableIRQ() disables the external interrupt for the interrupt
 * number indicated by the parameter IRQn.

 * NOTE:
 * 	This function can be used to disable the external interrupt from outside
 * 	external interrupt handler function.
 * 	This function MUST NOT be used from within the External Interrupt handler.
 * 	If you wish to disable the external interrupt while the interrupt handler
 * 	for that external interrupt is executing then you must use the return value
 * 	EXT_IRQ_DISABLE to return from the extern interrupt handler.
 */
static inline void PLIC_DisableIRQ(IRQn_Type IRQn)
{
    unsigned long hart_id = read_csr(mhartid);
    uint32_t current = PLIC->TARGET_ENABLES[hart_id].ENABLES[IRQn / 32];

    current &= ~((uint32_t)1 << (IRQn % 32));

    PLIC->TARGET_ENABLES[hart_id].ENABLES[IRQn / 32] = current;
}

/*==============================================================================
 * The function PLIC_SetPriority() sets the priority for the external interrupt 
 * for the interrupt number indicated by the parameter IRQn.
 */
static inline void PLIC_SetPriority(IRQn_Type IRQn, uint32_t priority) 
{
    PLIC->SOURCE_PRIORITY[IRQn] = priority;
}

/*==============================================================================
 * The function PLIC_GetPriority() returns the priority for the external interrupt 
 * for the interrupt number indicated by the parameter IRQn.
 */
static inline uint32_t PLIC_GetPriority(IRQn_Type IRQn)
{
    return PLIC->SOURCE_PRIORITY[IRQn];
}

/*==============================================================================
 * The function PLIC_ClaimIRQ() claims the interrupt from the PLIC controller.
 */
static inline uint32_t PLIC_ClaimIRQ(void)
{
    unsigned long hart_id = read_csr(mhartid);

    return PLIC->TARGET[hart_id].CLAIM_COMPLETE;
}

/*==============================================================================
 * The function PLIC_CompleteIRQ() indicates to the PLIC controller the interrupt
 * is processed and claim is complete.
 */
static inline void PLIC_CompleteIRQ(uint32_t source)
{
    unsigned long hart_id = read_csr(mhartid);

    PLIC->TARGET[hart_id].CLAIM_COMPLETE = source;
}

/*==============================================================================
 * The function raise_soft_interrupt() raises a synchronous software interrupt by
 * writing into the MSIP register.
 */
static inline void raise_soft_interrupt()
{
    unsigned long hart_id = read_csr(mhartid);

    /*You need to make sure that the global interrupt is enabled*/
    set_csr(mie, MIP_MSIP);       /*Enable software interrupt bit */
    PRCI->MSIP[hart_id] = 0x01;   /*raise soft interrupt for hart0*/
}

/*==============================================================================
 * The function clear_soft_interrupt() clears a synchronous software interrupt by
 * clearing the MSIP register.
 */
static inline void clear_soft_interrupt()
{
    unsigned long hart_id = read_csr(mhartid);
    PRCI->MSIP[hart_id] = 0x00;   /*clear soft interrupt for hart0*/
}

#ifdef __cplusplus
}
#endif

#endif  /* RISCV_PLIC_H */
