/*
 * dev_icu.h - ICU (Interrupt Controler Unit) generic device API.
 *
 * Authors   Alain Greiner  (2016)
 *
 * Copyright (c) UPMC Sorbonne Universites
 *
 * This file is part of ALMOS-MKH.
 *
 * ALMOS-MKH is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2.0 of the License.
 *
 * ALMOS-MKH is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ALMOS-MKH; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef _DEV_ICU_H_
#define _DEV_ICU_H_

#include <almos_config.h>
#include <hal_types.h>
#include <spinlock.h>

/*****************************************************************************************
 *     Generic Internal Interrupt Controler definition
 *
 * The ICU (Interrupt Controller Unit) device describes an internal peripheral, 
 * acting in all clusters containing cores. He is in charge of concentrating all IRQs
 * (interrupt requests) generated by peripherals to signal the completion of an I/O 
 * operation. Each IRQ should be routed to the core that started the I/O operation. 
 * The ICU device must also help the kernel to select the ISR (Interrupt Service Routine)
 * that must be executed by the target core.
 *
 * This component can be implemented as a dedicated hardware, centralized or distributed
 * in all clusters, or emulated in software, as long as it implements the specified API.
 * For the TSAR architecture, this generic ICU device is implemented by the two hardware
 * components soclib_xicu and and soclib_iopic, and their associated drivers.
 *
 * ALMOS-MKH defines three types of IRQs, that are handled iby this ICU device:
 * - HWI : The HardWare Interrupts are generated by local internal peripherals.
 *   They are connected to the local ICU, to be routed to a given local core.
 * - WTI : The Write Triggered Interrupts are actually mailboxes implemented in the
 *   local ICU. They are used to implement software IPIs (Inter-Processor-Interrupts),
 *   or to route an IRQ generated by an external peripheral to a local core.
 * - PTI : The Programmable Timer Interrupts are actually timers generating periodic
 *   interrupts controled by softare and contained in the local ICU, and routed to
 *   a local core.
 *
 * The max numbers of interrupts of each type in a given cluster are defined by the 
 * CONFIG_IRQ_HWI_MAX, CONFIG_IRQ_WTI_MAX, and CONFIG_IRQ_PTI_MAX parameters. 
 * The actual numbers (hwi_nr / wti_nr / pti_nr) for a given manycore architecture
 * are defined in the boot_info file and stored in the ICU device extension.
 *
 * The generic ICU device provides three main services:
 * 1) It allows the kernel to selectively enable/disable any IRQ (identified by its type
 *    and index) for a given core. It is the kernel responsibility to enable a given IRQ
 *    to the core that started the I/O operation core, as a given IRQ event should be 
 *    handled by only one core.
 * 2) It makes a global OR between all enabled IRQs for a given core, to interrupt
 *    the core when at least one enabled IRQ is active.
 * 3) It is capable to return the highest priority active IRQ of each type. The priority 
 *    scheme depends on the ICU device implementation.
 *
 * To select the ISR to be executed for a given IRQ routed to a given core, ALMOS-MKH 
 * uses three interrupts vectors, implemented as three arrays (HWI/WTI/PTI),
 * stored in the core descriptor. Each entry in one interrupt vector array contains
 * an extended pointer on the device descriptor that is the "source" of the interrupt.
 * This device descriptor contains a link to the ISR to be executed.
 *
 * The ICU peripheral does not execute I/O operations, but is just acting as a 
 * dynamically configurable interrupt router for another I/O operation.
 * Therefore, ALMOS-MKH does not use the iCU device waiting queue, but calls directly 
 * the ICU driver blocking functions, using the device lock to get exclusive access to 
 * the ICU device state.
 ****************************************************************************************/
 
/****  Forward declarations  ****/

struct device_s;

/*****************************************************************************************
 * This defines the (implementation independant) extension for the generic ICU device.
 ****************************************************************************************/

typedef struct icu_extend_s
{
    uint32_t    hwi_nr;         /*! actual number of HWI IRQs in the ICU device         */
    uint32_t    wti_nr;         /*! actual number of WTI IRQs in the ICU device         */
    uint32_t    pti_nr;         /*! actual number of PTI IRQs in the ICU device         */
    uint32_t    wti_bitmap;     /*! WTI mailbox allocator / 0 == free entry             */
    spinlock_t  wti_lock;       /*! lock protecting WTI mailbox allocator               */
}
icu_extend_t;

/*****************************************************************************************
 * This enum defines the various implementations of the ICU internal interrupt controller.
 * This array must be kept consistent with the define in arch_info.h file
 ****************************************************************************************/

enum icu_impl_e
{
    IMPL_ICU_XCU =   0,         /* vci_xicu component used in TSAR                      */
    IMPL_ICU_I86 =   1          /* TBD                                                  */
}
icu_impl_t;

/*****************************************************************************************
 * This enum defines the three types of interrupts.
 ****************************************************************************************/

typedef enum irq_type
{
    HWI_TYPE,                   /*! Hardware Interrupt                                  */
    WTI_TYPE,                   /*! Write Triggered Interrupt                           */
    PTI_TYPE                    /*! Programmable Timer Interrupt                        */
}
irq_type_t;


/*****************************************************************************************
 * This function makes two initialisations:
 * - It initialises the ICU specific fields of the device descriptor.
 * - it initialises the implementation specific ICU hardware device and associated
 *   data structures if required.
 * It must be executed once in the cluster containing the ICU device descriptor.
 *****************************************************************************************
 * @ icu_xp     : extended pointer on ICU device descriptor.
 * @ hwi_nr     : number of HWI irqs.
 * @ wti_nr     : number of WTI irqs.
 * @ pti_nr     : number of PTI irqs.
 ****************************************************************************************/
void dev_icu_init( xptr_t    icu_xp,
                   uint32_t  hwi_nr,
                   uint32_t  wti_nr,
                   uint32_t  pti_nr );

/*****************************************************************************************
 * This function enables the routing of a given IRQ, to a given core in a remote cluster.
 * This IRQ is identified by its type (HWI/WTI/PTI) and index in the remote ICU.
 * The target core is identified by its local index.
 * As it uses remote accesses, it can be executed by any thread in any cluster.
 * - It unmask the selected IRQ in the remote ICU. 
 * - It registers the extended pointer on the "source" device descriptor in the
 *   relevant interrupt vector of the selected core.
 * - It register the IRQ type and index in the "source" device descriptor.
 *****************************************************************************************
 * @ cxy        : remote cluster identifier (can be the local custer).
 * @ lid        : local index of selected core in remote cluster.
 * @ irq_type   : HWI/WTI/PTI.
 * @ irq_id     : IRQ index in remote ICU
 * @ src_dev_xp : extended pointer on device descriptor source of IRQ.
 ****************************************************************************************/
void dev_icu_enable_irq( cxy_t      cxy,
                         lid_t      lid,
                         uint32_t   irq_type,
                         uint32_t   irq_id,
                         xptr_t     src_dev_xp );

/*****************************************************************************************
 * This function disables one given IRQ for a given core in a remote cluster.
 * This IRQ is identified by its type (HWI/WTI/PTI) and index in the remote ICU.
 * The core is identified by its local index.
 * As it uses remote accesses, it can be executed by any thread in any cluster.
 * - It mask the selected IRQ in the remote ICU. 
 * - It removes the extended pointer on the "source" device descriptor from the
 *   relevant interrupt vector of the selected core.
 * - The IRQ type and index fields are not modified in the "source" device descriptor.
 *****************************************************************************************
 * @ cxy       : remote cluster identifier (can be the local custer).
 * @ lid       : local index of selected core in remote cluster.
 * @ irq_type  : HWI/WTI/PTI.
 * @ irq_id    : IRQ index.
 ****************************************************************************************/
void dev_icu_disable_irq( cxy_t      cxy,
                          lid_t      lid,
                          uint32_t   irq_type,
                          uint32_t   irq_id );
                        
/*****************************************************************************************
 * This function set the period value for a timer identified by the PTI index,
 * in the local ICU device descriptor.
 *****************************************************************************************
 * @ pti_id    : PTI index.
 * @ period    : number of cycles.
 ****************************************************************************************/
void dev_icu_set_period( uint32_t   pti_id,
                         uint32_t   period );

/*****************************************************************************************
 * This function acknowledge a PTI IRQ for a timer identified by the PTI index,
 * in the local ICU device descriptor.
 *****************************************************************************************
 * @ pti_id    : PTI index.
 ****************************************************************************************/
void dev_icu_ack_timer( uint32_t   pti_id );

/*****************************************************************************************
 * This function send an IPI (Inter Processor Interrupt) to a core identified by
 * its cluster identifier and local index. 
 * This IPI force a context switch, and the handling of pending RPC(s).
 *****************************************************************************************
 * @ cxy      : destination cluster identifier.
 * @ lid      : destination core local index.
 * @ return 0 if success (IPI registered) / returns EINVAL if illegal cxy or lid.
 ****************************************************************************************/
void dev_icu_send_ipi( cxy_t    cxy,
                       lid_t    lid );

/*****************************************************************************************
 * This function is called by a core when it receives an IRQ from the ICU device.
 * It access the local ICU device descriptor to get the highest priority IRQ
 * of each type (HWI / WTI / PTI).
 * - If it is a WTI or an HWI, the function uses the calling core interrupt-vectors
 *   to call the relevant ISR, registered in the source device descriptor.
 * - If it is a PTI, the source device descriptor is the ICU device itself, and it 
 *   call the core_clock() function to execute all operations related to the TICK event.
 ****************************************************************************************/
void dev_icu_irq_handler();

/*****************************************************************************************
 * This function implements the WTI mailbox allocator. It is used by all I/O operations
 * for completion signaling.
 * If there is no mailbox available, the client thread must deschedule and retry later.
 * If N is the total number of WTI mailboxes in a cluster, and NC the number of cores,
 * the number of dynamically allocatable WTI mailboxes is (N-NC) because the first N
 * WTI mailboxes are used by the IPIs (one IPI per core).
 * It does not access the hardware device.
 *****************************************************************************************
 * @ return WTI index if success / returns 0xFFFFFFFF if no mailbox available.
 ****************************************************************************************/
uint32_t dev_icu_wti_alloc();

/*****************************************************************************************
 * This function releases a dynamically allocated WTI mailbox.
 * It does not access the hardware device.
 *****************************************************************************************
 * @ wti_id   : WTI mailbox index.
 ****************************************************************************************/
void dev_icu_wti_release( uint32_t wti_id );

/*****************************************************************************************
 * This function returns an extended pointer on a WTI mailbox identified by its
 * cluster identifier and local index in ICU.
 * WARNING: we assume that all ICU hardware devices have the same local base address.
 *****************************************************************************************
 * @ cxy      : mailbox cluster identifier
 * @ wti_id   : mailbox WTI index.
 * @ returns extended pointer on mailbox register.
 ****************************************************************************************/
xptr_t  dev_icu_wti_xptr( cxy_t    cxy,
                          uint32_t wti_id );

#endif  /* _DEV_ICU_H_ */
