/*
 * core.c - core descriptor access function.
 *
 * Author  Ghassan Almaless (2008,2009,2010,2011,2012)
 *         Alain Greiner (2016,2017)
 *
 * 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
 */

#include <kernel_config.h>
#include <hal_types.h>
#include <hal_special.h>
#include <errno.h>
#include <printk.h>
#include <thread.h>
#include <chdev.h>
#include <dev_pic.h>
#include <rpc.h>
#include <cluster.h>
#include <kmem.h>
#include <dqdt.h>
#include <core.h>

/////////////////////////////////
void core_init( core_t    * core,
                lid_t       lid,
                gid_t       gid )
{
	core->lid               = lid;
	core->gid               = gid;
	core->cycles            = 0;
	core->time_stamp        = 0;
	core->ticks_nr          = 0;
	core->ticks_period      = CONFIG_SCHED_TICK_PERIOD;
	core->usage             = 0;
	core->spurious_irqs     = 0;
	core->thread_idle       = NULL;
	core->fpu_owner         = NULL;
	core->rand_last         = hal_time_stamp() & 0xFFF;

    // initialize scheduler
	sched_init( core );
}

//////////////////////////////////////////////
inline uint32_t core_get_rand( core_t * core )
{
	uint32_t value  = ((core->rand_last * CONFIG_RDNG_PARAM_A) +
	                    CONFIG_RDNG_PARAM_C) ^ (hal_get_cycles() & 0xFFF);
	core->rand_last = value;
	return value;
}

////////////////////////////////////
void core_get_time( core_t   * core,
                    uint32_t * tm_s, 
                    uint32_t * tm_us )
{
	uint64_t cycles = hal_get_cycles();

	*tm_s  = (cycles / CONFIG_CYCLES_PER_MS);
	*tm_us = (cycles % CONFIG_CYCLES_PER_MS) / (CONFIG_CYCLES_PER_MS / 1000000);
}

//////////////////////////////////////
void core_time_update( core_t * core )
{
	uint32_t elapsed;
	uint32_t ticks_nr   = core->ticks_nr;
	uint64_t cycles     = core->cycles;
	uint32_t time_stamp = core->time_stamp;
	uint32_t time_now   = hal_get_cycles();

	// compute number of elapsed cycles taking into account 32 bits register wrap
	if( time_now < time_stamp ) elapsed = (0xFFFFFFFF - time_stamp) + time_now;
	else                        elapsed = time_now - time_stamp;

	cycles  += elapsed;
	ticks_nr = elapsed / core->ticks_period;

	core->time_stamp     = time_now;
	core->cycles         = cycles + elapsed;
	core->ticks_nr       = ticks_nr + (elapsed / core->ticks_period);
	hal_fence();
}

////////////////////////////////
void core_clock( core_t * core )
{
	uint32_t ticks;

	// update cycles and ticks counter
	core_time_update( core );

	// get current ticks number
	ticks = core->ticks_nr;

	// handle pending alarms TODO ??? [AG]
	// alarm_clock( &core->alarm_mgr , ticks );

	// handle scheduler TODO  improve the scheduling condition ... AG
	if( (ticks % 10) == 0 ) sched_yield( NULL );

	// update DQDT TODO  This update should depend on the cluster identifier,
	// to avoid simultaneous updates from various clusters ... AG
	if( ((ticks % CONFIG_DQDT_PERIOD) == 0) && (core->lid == 0) ) dqdt_global_update();
}

////////////////////////////////////////
void core_compute_stats( core_t * core )
{
	thread_t * idle  = core->thread_idle;
	uint32_t   ticks = core->ticks_nr;

	uint32_t   idle_percent;
	uint32_t   busy_percent;
	uint32_t   usage;

	// compute cumulated usage
	ticks         = (ticks) ? ticks : 1;
	idle_percent  = (idle->ticks_nr * 100) / ticks;
	idle_percent  = (idle_percent > 100) ? 100 : idle_percent;
	busy_percent  = 100 - idle_percent;
	usage         = (busy_percent + core->usage) / 2;

	// update core descriptor
	core->usage = usage;
	hal_fence();

#if CONFIG_SHOW_CPU_USAGE
	printk(INFO, "INFO: core %d in cluster %x : busy_percent = %d / cumulated_usage = %d\n",
	       core->lid, local_cxy , busy_percent , usage );
#endif

	core->ticks_nr = 0;
	idle->ticks_nr = 0;
}

/////////////////////////////////////
void core_reset_stats( core_t * core )
{
	core_time_update(core);

	core->ticks_nr              = 0;
	core->usage                 = 0;
	core->thread_idle->ticks_nr = 0;
	hal_fence();
}


/* deprecated [AG] july 20017
///////////////////////////////////////////////////
void core_set_irq_vector_entry( core_t   * core,
                                uint32_t   irq_type,
                                uint32_t   irq_id,
                                chdev_t  * chdev )
{
	if     ( irq_type == WTI_TYPE ) core->wti_vector[irq_id] = chdev;
	else if( irq_type == HWI_TYPE ) core->hwi_vector[irq_id] = chdev;
	else                            core->pti_vector[irq_id] = chdev;
}
*/
