/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: import/chips/p9/procedures/ppe_closed/cme/stop_cme/p9_cme_stop_init.c $ */
/*                                                                        */
/* OpenPOWER HCODE Project                                                */
/*                                                                        */
/* COPYRIGHT 2016,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                                                     */

#include "p9_cme_stop.h"
#include "p9_cme_copy_scan_ring.h"
#include "p9_hcode_image_defines.H"

extern CmeStopRecord G_cme_stop_record;
extern CmeRecord G_cme_record;

#if TEST_ONLY_BCE_IRR
    extern BceIrritator  G_bce_irr;
#endif


void
p9_cme_stop_init()
{
    uint32_t   entry_first = 0;
    uint32_t   exit_first  = 0;
    uint32_t   cme_flags   = 0;
    uint32_t   core_mask   = 0;

    //--------------------------------------------------------------------------
    // Basic Software Settings
    //--------------------------------------------------------------------------

    // Create a semaphore, updated by a single interrupt handler that services
    // two STOP threads. Thus, max sem count set to two.
#if !defined(__IOTA__)
    pk_semaphore_create(&(G_cme_stop_record.sem[0]), 0, 2);
    pk_semaphore_create(&(G_cme_stop_record.sem[1]), 0, 2);
#endif

    //--------------------------------------------------------------------------
    // Parse CME Flags and Initialize Core States
    //--------------------------------------------------------------------------

    if (!G_cme_record.core_enabled)
    {
        PK_TRACE_ERR("ERROR: No Partial Good Core Configured to this CME. HALT CME!");
        PK_PANIC(CME_STOP_NO_PARTIAL_GOOD_CORE);
    }

    // partial_good and entry_first:  unmask entry
    // partial_good and !entry_first: unmask exit
    // !partial_good:                 dont unmask
    cme_flags   = (in32(CME_LCL_FLAGS) & 0xF);
    entry_first = ( (cme_flags >> 2) & cme_flags & CME_MASK_BC);
    exit_first  = (~(cme_flags >> 2) & cme_flags & CME_MASK_BC);

    G_cme_stop_record.core_running = entry_first;
    G_cme_stop_record.core_stopgpe = 0;

    // use SISR[2:3] PM_BLOCK_INTERRUPTS to init block wakeup status
    G_cme_stop_record.core_blockpc = ((in32(CME_LCL_SISR) & BITS32(2, 2)) >> SHIFT32(3));
    G_cme_stop_record.core_blockwu = G_cme_stop_record.core_blockpc;
    G_cme_stop_record.core_blockey = 0;

    // use SISR[16:17] SPECIAL_WKUP_DONE to init special wakeup status
    G_cme_stop_record.core_in_spwu = ((in32(CME_LCL_SISR) & BITS32(16, 2)) >> SHIFT32(17));

    PK_TRACE_INF("Setup: cme_flags[%x] entry_first[%x] exit_first[%x]",
                 cme_flags, entry_first, exit_first);

    for (core_mask = 2; core_mask; core_mask--)
    {
        if (((~G_cme_record.core_enabled) | exit_first) & core_mask)
        {
            G_cme_stop_record.req_level[core_mask & 1] = STOP_LEVEL_11;
            G_cme_stop_record.act_level[core_mask & 1] = STOP_LEVEL_11;
        }
    }

    p9_cme_stop_eval_eimr_override();

    //--------------------------------------------------------------------------
    // BCE Core Specific Scan Ring
    //--------------------------------------------------------------------------

#if !SKIP_BCE_SCAN_RING

    PK_TRACE_DBG("Setup: BCE Setup Kickoff to Copy Core Specific Scan Ring");

    cmeHeader_t* pCmeImgHdr = (cmeHeader_t*)(CME_SRAM_HEADER_ADDR);

    //right now a blocking call. Need to confirm this.
    start_cme_block_copy(CME_BCEBAR_1,
                         (CME_IMAGE_CPMR_OFFSET + (pCmeImgHdr->g_cme_core_spec_ring_offset << 5)),
                         pCmeImgHdr->g_cme_core_spec_ring_offset,
                         pCmeImgHdr->g_cme_max_spec_ring_length);

    PK_TRACE_DBG("Setup: BCE Check for Copy Completed");

    if( BLOCK_COPY_SUCCESS != check_cme_block_copy() )
    {
        PK_TRACE_ERR("ERROR: BCE Copy of Core Specific Scan Ring Failed. HALT CME!");
        PK_PANIC(CME_STOP_BCE_CORE_RING_FAILED);
    }

#endif

#if TEST_ONLY_BCE_IRR

    bce_irr_setup();

#if SKIP_BCE_SCOM_RESTORE
    ppe42_fit_setup((PkIrqHandler)bce_irr_run, 0);
#endif

#endif

    //--------------------------------------------------------------------------
    // Common Hardware Settings
    //--------------------------------------------------------------------------

    PK_TRACE("Drop reset_state_indicator on good core via GPMMR[15]");
    CME_PUTSCOM(PPM_GPMMR_CLR, G_cme_record.core_enabled, BIT64(15));

#if HW386841_NDD1_DSL_STOP1_FIX

    PK_TRACE("Disable the Auto-STOP1 function for Nimbus DD1 via LMCR[18,19]");
    out32(CME_LCL_LMCR_OR, BITS32(18, 2));

#endif

    PK_TRACE("Drop STOP override mode and active mask via LMCR[16,17]");
    out32(CME_LCL_LMCR_CLR, BITS32(16, 2));

    PK_TRACE_INF("Setup: SPWU Interrupt Polority[%d]", G_cme_stop_record.core_in_spwu);
    out32(CME_LCL_EIPR_CLR, (G_cme_stop_record.core_in_spwu << SHIFT32(15)));
    out32(CME_LCL_EIPR_OR,  (((~G_cme_stop_record.core_in_spwu) & CME_MASK_BC) << SHIFT32(15)));

    PK_TRACE_INF("Setup: Umask STOP Interrupts Now Based on Entry_First Flag");
    out32(CME_LCL_EIMR_CLR, (entry_first << SHIFT32(21)) |
          (exit_first  << SHIFT32(13)) |
#if SPWU_AUTO
          (exit_first  << SHIFT32(15)) |
#else
          (CME_MASK_BC << SHIFT32(15)) | // always unmask spwu
#endif
          (exit_first  << SHIFT32(17)));

    // unmask db2 for block stop protocol
    out32_sh(CME_LCL_EIMR_CLR, (CME_MASK_BC << SHIFT64SH(41)));
    out32(CME_LCL_EIMR_CLR, (CME_MASK_BC << SHIFT32(19)));

    //--------------------------------------------------------------------------
    // CME Init Completed
    //--------------------------------------------------------------------------

    PK_TRACE_INF("Setup: CME STOP READY");
    out32(CME_LCL_FLAGS_OR, BIT32(CME_FLAGS_STOP_READY));

#if EPM_P9_TUNING
    asm volatile ("tw 0, 31, 0");
#endif

}
