/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: import/chips/p9/procedures/ppe_closed/cme/p9_cme_irq.c $      */
/*                                                                        */
/* OpenPOWER HCODE Project                                                */
/*                                                                        */
/* COPYRIGHT 2015,2017                                                    */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
//-----------------------------------------------------------------------------
// *! (C) Copyright International Business Machines Corp. 2014
// *! All Rights Reserved -- Property of IBM
// *! *** IBM Confidential ***
//-----------------------------------------------------------------------------

/// \file   cme_irq_common.c
/// \brief  Shared and global file for CME H-codes.
/// \owner  Michael Olsen   Email: cmolsen@us.ibm.com
/// \owner  David Du        Email: daviddu@us.ibm.com
///

#include "pk.h"
#include "p9_cme_irq.h"
#include "ppehw_common.h"

//-------------------------------------------------------------------//
//            DO NOT modify this file unless you're the owner        //
//-------------------------------------------------------------------//

uint32_t      g_current_prty_level = NUM_EXT_IRQ_PRTY_LEVELS - 1;
uint8_t       g_eimr_stack[NUM_EXT_IRQ_PRTY_LEVELS];
int           g_eimr_stack_ctr = -1;
uint64_t      g_eimr_override_stack[NUM_EXT_IRQ_PRTY_LEVELS];
uint64_t      g_eimr_override = 0x0000000000000000;

// Unified IRQ priority and masking handler.
// - Locates the highest priority IRQ task vector that has at least one of its
//   interrupts in the current external PK interrupt vector.
void pk_unified_irq_prty_mask_handler(void)
{
    uint8_t   iPrtyLvl, bFound;
    uint64_t  ext_irq_vector_pk;

    // 1. Identify the priority level of the interrupt.
    ext_irq_vector_pk = in64(STD_LCL_EISTR);
    bFound   = 0;
    iPrtyLvl = 0;

    PK_TRACE_DBG("DEBUG: Phantom IRQ Check, EISTR=%x %x.",
                 UPPER32(ext_irq_vector_pk), LOWER32(ext_irq_vector_pk));

    do
    {
        if (ext_irq_vectors_cme[iPrtyLvl][IDX_PRTY_VEC] & ext_irq_vector_pk)
        {
            bFound = 1;
            break;
        }
    }
    while(++iPrtyLvl < (NUM_EXT_IRQ_PRTY_LEVELS - 1)); //No need to check DISABLED.

    // Only manipulate EIMR masks for task level prty levels.
    // Let shared non-task IRQs (iPrtyLvl=0) be processed by
    // the PK kernel in usual fashion.
    if (bFound)
    {
        // 2. Save current mask (assume current prty level).
        //    Note, reading EIMR is NOT safe because overrides may already have
        //      happened to the EIMR. And we always want to restore
        //      the EIMR to a known value when we exit our thread.
        if (++g_eimr_stack_ctr < NUM_EXT_IRQ_PRTY_LEVELS)
        {
            // Make a note of present prty level and
            // then update tracker to new prty level.
            g_eimr_stack[g_eimr_stack_ctr] = g_current_prty_level;
            g_current_prty_level = iPrtyLvl; // Update prty level tracker.
            g_eimr_override_stack[g_eimr_stack_ctr] = g_eimr_override;
        }
        else
        {
            PK_TRACE_ERR("ERROR: EIMR S/R stack counter=%d  >=  max=%d. HALT CME!",
                         g_eimr_stack_ctr, NUM_EXT_IRQ_PRTY_LEVELS);
            PK_PANIC(CME_UIH_EIMR_STACK_OVERFLOW);
        }

        // 3. Write the new mask for this priority level.
        out64(STD_LCL_EIMR, ext_irq_vectors_cme[iPrtyLvl][IDX_MASK_VEC] |
              g_eimr_override);

    }
    else
    {
        PK_TRACE_ERR("ERROR: Phantom IRQ Fired, EISTR=%x %x. HALT CME!",
                     UPPER32(ext_irq_vector_pk), LOWER32(ext_irq_vector_pk));
#if !EPM_P9_TUNING
        PK_PANIC(CME_UIH_PHANTOM_INTERRUPT);
#endif
    }


    // 4. Return the priority vector in d5 and let hwmacro_get_ext_irq do the
    //    rest, i.e. route first found IRQ in the returned priority vector
    //    to the registered [unified] interrupt handler.
    uint32_t  register l_vecH asm("r5");
    //uint32_t  register l_vecL asm("r6");

    //l_vecL = 0;
    asm volatile ("lvd %[data], 0(%[addr]) \n" \
                  : [data]"=r"(l_vecH) \
                  : [addr]"r"(&(ext_irq_vectors_cme[iPrtyLvl][IDX_PRTY_VEC])) );
}
