/*
 * time.c
 *
 * Copyright 2002, Minoru Murashima. All rights reserved.
 * Distributed under the terms of the BSD License.
 */


#include"types.h"
#include"lib.h"
#include"errno.h"
#include"interrupt.h"
#include"proc.h"
#include"sp.h"
#include"mp.h"
#include"lock.h"
#include"time.h"


enum{
	MAX_TIMER=4,			/* 最大保持タイマー数 */

	REAL_TIMER_RATE=500,	/* Real-clock timer rate(ms) */

	/* Timer type */
	TYP_INTERVAL=1,			/* Interval timer */
	TYP_REAL=2,				/* Real-clock timer */
};


typedef struct{
	PROC *wait_next;	/* ウエイトリンクの次のプロセス,PROCのnextと同じ位置 */
	PROC *wait_prev;	/* ウエイトリンクの前のプロセス,PROCのprevと同じ位置 */
	int timer_count;	/* Counter for timer */
	int lock_gate;
}TIMER_LINK;

/*typedef struct{
	int timer_count;

}TIMER;
*/

int clock_1micro;						/* 1micros */
int clock_1m;							/* 1ms */
static TIMER_LINK interval_timer=		/* Interval timer link queue */
	{(PROC*)&interval_timer,(PROC*)&interval_timer,0,0};
static uint tasking_count=				/* シングルプロセッサタスキング用タイマーカウント */
	PIT_HZ/(1000/TASK_TIME);
static int interval_timer_num=0;		/* Number of used interval timer */
static int interval_current_count=0;	/* Interval timer current count */
static TIMER_LINK real_timer=			/* Real timer link queue */
	{(PROC*)&real_timer,(PROC*)&real_timer,0,0};
static int real_timer_num=0;			/* Number of used real timer */
uchar year_low;					/* 年100未満 */
uchar year_high;				/* 年100以上 */


static int interval_timer_handler();
static int set_interval_timer(int);
static void del_interval_timer(PROC*);
static int real_timer_handler();
static int set_real_timer(int);
static void del_real_timer(PROC*);


/***************************************************************************************************
 *
 * inline functions
 *
 ***************************************************************************************************/


/*
 * CMOS read and write
 */
extern inline uchar read_cmos(int addr)
{
	outb(CMOS_ADR,addr|NMI_ENABLE);
	return inb(CMOS_DTR);
}

extern inline void write_cmos(int addr,uchar value)
{
	outb(CMOS_ADR,addr|NMI_ENABLE);
	outb(CMOS_DTR,value);
}

extern inline uchar bcd_to_bin(uchar value)
{
	return value-6*(value>>4);
}


/***************************************************************************************************
 *
 * misc functions
 *
 ***************************************************************************************************/


/*
 * cpuクロック数を計測する
 * 返り値 : クロック数
 */
int count_cpu_clock()
{
	uint cpu_clock;
	int low,high;
	long long r_beg,r_end;
	double time;


 	outb(PIT_CTR,0x30);	/* CLK0,LSB.MSB,モード0,binary */
	outb(PIT_CNR0,0);	/* LSB */
	outb(PIT_CNR0,0);	/* MSB */

 	r_beg=rdtsc();		/* clock counter */

	for(;;)if(rdtsc()-r_beg>0x400000)break;		/* 遅延 */

	r_end=rdtsc();

	low=inb(PIT_CNR0);
	high=inb(PIT_CNR0);

	time=(0xffff-low-(high<<8))/(double)PIT_HZ;
	cpu_clock=(r_end-r_beg)/time;

 	clock_1micro=cpu_clock/1000000+1;		/* 1micros */
 	clock_1m=cpu_clock/1000+1;				/* 1ms */

	return cpu_clock;
}


/*
 * Set timer
 * parameters : time(ms単位)
 * rturn : Task switch flag or error number
 */
int set_timer(int time)
{
	if(time==0)return 0;

	if(time<REAL_TIMER_RATE)return set_interval_timer(time);
	else return set_real_timer(time);
}


/*
 * Delete timer from timer link
 * parameters : Process number
 */
void del_timer(PROC *proc)
{
	if(proc->timer_type==TYP_INTERVAL)del_interval_timer(proc);
	if(proc->timer_type==TYP_REAL)del_real_timer(proc);
}


/*
 * Get current time
 * parameters : TIME struct point
 */
void get_current_time(TIME *tm)
{
	do
	{
		tm->second=bcd_to_bin(read_cmos(CMOS_SCD));
		tm->minute=bcd_to_bin(read_cmos(CMOS_MNT));
		tm->hour=bcd_to_bin(read_cmos(CMOS_HOR));
		tm->day=bcd_to_bin(read_cmos(CMOS_DAY));
		tm->month=bcd_to_bin(read_cmos(CMOS_MTH));
		tm->year_low=bcd_to_bin(read_cmos(CMOS_YER));
	}while(bcd_to_bin(read_cmos(CMOS_SCD))<tm->second);

	tm->year_high=(tm->year_low>=year_low)?year_high:++year_high;
}


/***************************************************************************************************
 *
 * 8254 interval timer
 *
 ***************************************************************************************************/


/*
 * 8254 interval timer handler
 */
int interval_timer_handler()
{
	PROC *p;

/****************************************************************/
	printk("Interrupt IRQ0\n");
/*****************************************************************/
	enter_spinlock(&interval_timer.lock_gate);
	{
		/* タイムアウトのプロセスを復活させる */
		for(p=interval_timer.wait_next;p->timer_count==interval_current_count;p=p->wait_next)
		{
			p->timer_count=0;
			p->wait_prev->wait_next=p->wait_next;
			p->wait_next->wait_prev=p->wait_prev;
			add_to_schedule(p);
			--interval_timer_num;
		}

		/* No timer */
		if(interval_timer_num==0)
		{
			if(activ_cpu==0)
			{
				tasking_count=PIT_HZ/(1000/TASK_TIME);
				init_sp_task();
			}
		}
		else
		{
			/* 各プロセスのカウントを減らす */
			for(p=interval_timer.wait_next;p!=(PROC*)&interval_timer;p=p->wait_next)
				p->timer_count-=interval_current_count;

			if(interval_timer.wait_prev->timer_count>0x10000)interval_current_count=0x10000;
			else interval_current_count=interval_timer.wait_prev->timer_count;

			if(activ_cpu==0)
			{
				if((tasking_count-=interval_current_count)==0)
					tasking_count=PIT_HZ/(1000/TASK_TIME);

				if(tasking_count<interval_current_count)interval_current_count=tasking_count;
			}

			/* タイマーを設定する */
			outb(PIT_CNR0,interval_current_count&0xff);
			outb(PIT_CNR0,interval_current_count>>8);
		}
	}
	exit_spinlock(&interval_timer.lock_gate);

	return 1;
}


/*
 * Set interval timer
 * parameters : time(ms単位)
 * rturn : Task switch flag or error number
 */
int set_interval_timer(int time)
{
	uint count;
	PROC *proc;
	PROC *p;


	if(interval_timer_num==MAX_TIMER)return -ENOTIMER;

	proc=cputask[get_current_cpu()].current_task;

	time=PIT_HZ*time/1000;				/* クロック数に変換 */

	cli();
	enter_spinlock(&interval_timer.lock_gate);
	{
		if(interval_timer_num==0)
		{
			proc->timer_count=time;
			interval_timer.wait_next=proc;
			interval_timer.wait_prev=proc;
			proc->wait_next=(PROC*)&interval_timer;
			proc->wait_prev=(PROC*)&interval_timer;

			if(activ_cpu==0)
			{
				irq_entry[IRQ0]=interval_timer_handler;
				outb(PIT_CTR,0x30);					/* One shot timer(mode 0) */
				if(time>PIT_HZ/(1000/TASK_TIME))
					time=PIT_HZ/(1000/TASK_TIME);
			}

			/* タイマー設定 */
			if(time>0x10000)interval_current_count=0x10000;
			else interval_current_count=time;
			outb(PIT_CNR0,interval_current_count&0xff);
			outb(PIT_CNR0,interval_current_count>>8);
		}
		else
		{
			count=inb(PIT_CNR0);
			count+=inb(PIT_CNR0)<<8;
			proc->timer_count=time+count;

			/* タイマーリンクに接続する */
			for(p=interval_timer.wait_next;p!=(PROC*)&interval_timer;p=p->wait_next)
				if(p->timer_count>=proc->timer_count)break;
			proc->wait_next=p->wait_next;
			proc->wait_prev=p;
			p->wait_next->wait_prev=proc;
			p->wait_next=proc;

			if(proc->timer_count<interval_current_count)
			{
				/* タイマー設定 */
				interval_current_count=proc->timer_count;
				outb(PIT_CNR0,time&0xff);
				outb(PIT_CNR0,time>>8);
			}
		}

		++interval_timer_num;
	}
	exit_spinlock(&interval_timer.lock_gate);
	sti();

	proc->timer_type=TYP_INTERVAL;

	return 1;
}


/*
 * Delete interval timer from link
 * parameters : Process number
 */
void del_interval_timer(PROC *proc)
{
	if(interval_timer_num==0)return;

	cli();
	enter_spinlock(&interval_timer.lock_gate);
	{
		proc->timer_count=0;
		proc->wait_prev->wait_next=proc->wait_next;
		proc->wait_next->wait_prev=proc->wait_prev;

		if(--interval_timer_num==0)
		{
			if(activ_cpu==0)
			{
				tasking_count=PIT_HZ/(1000/TASK_TIME);
				init_sp_task();
			}
			else outb(PIT_CTR,0x30);		/* タイマーストップ */
		}
	}
	exit_spinlock(&interval_timer.lock_gate);
	sti();
}


/***************************************************************************************************
 *
 * MC146818 real-clock timer
 *
 ***************************************************************************************************/


/*
 * MC146818 real timer handler
 */
int real_timer_handler()
{
	PROC *p;


/**********************************************/
	printk("Interrupt IRQ8\n");
/***********************************************/

	/* Status register C を読んで割り込みをリセットする。 */
	read_cmos(CMOS_STRC);

	/* タイムアウトプロセスを調べる */
	cli();
	enter_spinlock(&real_timer.lock_gate);
	{
		for(p=real_timer.wait_next;p!=(PROC*)&real_timer;p=p->wait_next)
			if((p->timer_count-=REAL_TIMER_RATE)<=0)		/* タイムアウト */
			{
				p->wait_prev->wait_next=p->wait_next;
				p->wait_next->wait_prev=p->wait_prev;
				--real_timer_num;
			}

		if(real_timer_num==0)
			write_cmos(CMOS_STRB,0x2);	/* 周期割り込みオフ */
	}
	exit_spinlock(&real_timer.lock_gate);
	sti();

	return 0;
}


/*
 * Set real_clock timer
 * parameters : time(ms単位)
 * rturn : Task switch flag or error number
 */
int set_real_timer(int time)
{
	PROC *proc;


	if(real_timer_num==MAX_TIMER)return -ENOTIMER;

	proc=cputask[get_current_cpu()].current_task;

	proc->timer_count=time;

	/* リンクに接続する */
	cli();
	enter_spinlock(&real_timer.lock_gate);
	{
		proc->wait_next=real_timer.wait_next;
		proc->wait_prev=(PROC*)&real_timer;
		real_timer.wait_next->wait_prev=proc;
		real_timer.wait_next=proc;

		if(++real_timer_num==1)
			write_cmos(CMOS_STRB,0x42);		/* 周期割り込みオン */
	}
	exit_spinlock(&real_timer.lock_gate);
	sti();

	proc->timer_type=TYP_REAL;

	return 1;
}


/*
 * Delete real-clock timer
 * prameters : process
 */
void del_real_timer(PROC *proc)
{
	if(real_timer_num==0)return;

	cli();
	enter_spinlock(&real_timer.lock_gate);
	{
		proc->wait_prev->wait_next=proc->wait_next;
		proc->wait_next->wait_prev=proc->wait_prev;

		if(--real_timer_num==0)
			write_cmos(CMOS_STRB,0x2);		/* 周期割り込みオフ */
	}
	exit_spinlock(&real_timer.lock_gate);
	sti();
}


/***************************************************************************************************
 *
 * 初期化
 *
 ***************************************************************************************************/


/*
 * Init time
 */
void init_time()
{
	/* マルチプロセッサ用設定 */
	if(activ_cpu>0)
	{
		irq_entry[IRQ0]=interval_timer_handler;
		outb(PIT_CTR,0x30);						/* One shot timer(mode 0) */
		release_irq_mask(0);
	}

	/* Init MC146818 real time clock */
	irq_entry[IRQ8]=real_timer_handler;
	write_cmos(CMOS_STRA,0x2f);			/* time bese 0x010(default)|周期タイマーレート65536/(2の0xf乗)=2HZ(500ms) */
	write_cmos(CMOS_STRB,0x2);			/* 24hour */
	year_low=(year_low&0xf)+((year_low>>4)*10);
	year_high=(year_high&0xf)+((year_high>>4)*10);
	release_irq_mask(8);
}
/*************************************************************************************/
void test_time()
{
	TIME tm;

	get_current_time(&tm);
	printk("%d.%d.%d %d:%d:%d\n",tm.year_high*100+tm.year_low,tm.month,tm.day,tm.hour,tm.minute,tm.second);
}
/**************************************************************************************/
