shithub: vmxsmp

ref: 2c9eeaf3cfd204ee49d676e8716445bb563249cd
dir: /io_timer.c/

View raw version
/*
 * io_timer.c - PIT and RTC timer portions (REFACTORED)
 *
 * This file contains ONLY the timer-related I/O functions from io.c,
 * updated to use the new timer architecture.
 *
 * CHANGES from original:
 * - Removed timer_set()/timer_cancel() calls for PIT/RTC
 * - CPU0 derives PIT/RTC deadlines from shared state in timer.c
 * - Added timer_kick() call to nudge CPU0 when state changes
 * - Cleaner separation: this file only manages device state,
 *   timer.c handles all scheduling
 */

#include <u.h>
#include <libc.h>
#include <thread.h>
#include <tos.h>
#include "dat.h"
#include "fns.h"

extern uvlong vmtime_ns(void);
extern void timer_kick(void);

extern int debug;
extern int state;

/* ============================================================
 * PIT (8254 Programmable Interval Timer)
 * ============================================================ */

 

extern PitShared *pitshared;

enum { PIT_PERIOD_NS = 838 };  /* ~1.193182 MHz */

static void
pitout_locked(int n, int v)
{
	switch(v){
	case IRQLLOHI: case 1: pitshared->ch[n].output = 1; break;
	case 0: pitshared->ch[n].output = 0; break;
	case IRQLTOGGLE: pitshared->ch[n].output ^= 1; break;
	}
}

/*
 * pitadvance - Check and fire expired PIT timers
 *
 * Called by CPU0 only from timers_advance().
 * No timer_set() calls - CPU0's timer_rearm_locked() reads
 * shared state directly to compute next deadline.
 */
void
pitadvance(void)
{
	int i;
	int nc;
	PITChannel *p;
	vlong nt, t;
	int irq_needed = 0;
	int irq_val = 0;

	if(pitshared == nil)
		return;

	lock(&pitshared->lock);
	
	for(i = 0; i < 3; i++){
		p = &pitshared->ch[i];
		nt = vmtime_ns();
		t = nt - p->lastnsec;
		p->lastnsec = nt;
		
		switch(p->mode){
		case 0:  /* Interrupt on terminal count */
			if(p->state != 0){
				nc = t / PIT_PERIOD_NS;
				if(p->count <= nc){
					pitout_locked(i, 1);
					if(i == 0){ 
						irq_needed = 1; 
						irq_val = 1; 
					}
				}
				p->count -= nc;
				p->lastnsec -= t % PIT_PERIOD_NS;
			}
			break;
			
		case 2:  /* Rate generator */
			if(p->state != 0){
				nc = t / PIT_PERIOD_NS;
				while(p->count <= nc && p->reload > 0){
					nc -= p->count;
					p->count = p->reload;
					pitout_locked(i, IRQLLOHI);
					if(i == 0){ 
						irq_needed = 1; 
						irq_val = IRQLLOHI; 
					}
				}
				p->count -= nc;
				p->lastnsec -= t % PIT_PERIOD_NS;
			}
			break;
			
		case 3:  /* Square wave */
			if(p->state != 0){
				nc = t / PIT_PERIOD_NS;
				while(p->count <= nc){
					nc -= p->count;
					p->count = p->reload;
					pitout_locked(i, IRQLTOGGLE);
					if(i == 0 && p->output){ 
						irq_needed = 1; 
						irq_val = 1; 
					}
				}
				p->count -= nc;
				p->lastnsec -= t % PIT_PERIOD_NS;
			}
			break;
		}
	}
	
	unlock(&pitshared->lock);
	
	/* Fire IRQ outside lock */
	if(irq_needed)
		ioapic_irqline(0, irq_val);
	
	/* No timer_set() needed - CPU0's timer_rearm_locked() will
	 * compute next deadline from p->lastnsec + p->count * PIT_PERIOD_NS */
}

static void
pitsetreload(int n, u16int v)
{
	PITChannel *p = &pitshared->ch[n];
	
	p->reload = v != 0 ? v : 0x10000;
	p->count = p->reload;
	p->state = 1;
	p->lastnsec = vmtime_ns();
	
	if(p->mode == 0)
		pitout_locked(n, 0);
	else if(p->mode == 2 || p->mode == 3)
		pitout_locked(n, 1);
	
	/* No timer_set() - just kick CPU0 to pick up new state */
}

u32int
pitio(int isin, u16int port, u32int val, int sz, void *)
{
	PITChannel *p;
	int n;
	u32int ret = 0;
	int state_changed = 0;

	if(sz != 1 && port != 0x61)
		vmerror("pit: access size %d != 1", sz);
	val = (u8int)val;
	n = port & 3;
	
	lock(&pitshared->lock);
	
	switch(isin << 16 | port){
	case 0x40: case 0x41: case 0x42:
		p = &pitshared->ch[n];
		switch(p->access){
		case 1:
			p->count = val;
			pitsetreload(n, p->count);
			state_changed = 1;
			break;
		case 2:
			p->count = val << 8;
			pitsetreload(n, p->count);
			state_changed = 1;
			break;
		case 3:
			if(p->writestate == READLO){
				p->count = p->count & 0xff00 | val;
				p->writestate = READHI;
			}else{
				p->count = p->count & 0xff | val << 8;
				p->writestate = READLO;
				pitsetreload(n, p->count);
				state_changed = 1;
			}
			break;
		}
		break;
		
	case 0x43:
		n = val >> 6;
		if(n == 3){
			unlock(&pitshared->lock);
			return 0;  /* Read-back - not implemented */
		}
		p = &pitshared->ch[n];
		if((val & 0x30) == 0){
			p->latch = p->count;
			p->readstate = READLATLO;
		}else{
			p->access = val >> 4 & 3;
			p->mode = val >> 1 & 7;
			p->bcd = val & 1;
			p->writestate = READLO;
			p->state = 0;
		}
		break;
		
	case 0x61:
		pitshared->port61 = val;
		break;
		
	case 0x10040: case 0x10041: case 0x10042:
		p = &pitshared->ch[n];
		switch(p->readstate){
		case READLO:
			ret = p->count;
			if(p->access == 3)
				p->readstate = READHI;
			break;
		case READHI:
			ret = p->count >> 8;
			if(p->access == 3)
				p->readstate = READLO;
			break;
		case READLATLO:
			ret = p->latch;
			p->readstate = READLATHI;
			break;
		case READLATHI:
			ret = p->latch >> 8;
			p->readstate = READLO;
			break;
		}
		break;
		
	case 0x10043:
		ret = 0;
		break;
		
	case 0x10061:
		ret = pitshared->port61 & 0x0f | pitshared->ch[2].output << 5;
		break;
		
	default:
		unlock(&pitshared->lock);
		return iowhine(isin, port, val, sz, "pit");
	}
	
	unlock(&pitshared->lock);
	
	/* Kick CPU0 to recompute deadlines if state changed */
	if(state_changed)
		timer_kick();
	
	return ret;
}

void
pit_init(void)
{
	extern void *vioalloc(ulong);
	pitshared = vioalloc(sizeof(PitShared));
	memset(pitshared, 0, sizeof(PitShared));
	pitshared->ch[0].state = 1;
	dprint("PIT: initialized (shared)\n");
}

/* ============================================================
 * RTC (Real-Time Clock)
 * ============================================================ */

 
extern RtcShared *rtcshared;

static uchar
bcd(uchar m, uchar c)
{
	return (m & 1) != 0 ? c : c / 10 << 4 | c % 10;
}

/*
 * rtcadvance - Check and fire expired RTC periodic interrupt
 *
 * Called by CPU0 only from timers_advance().
 * No timer_set() calls - CPU0's timer_rearm_locked() reads
 * rtcshared->rtcnext directly to compute next deadline.
 */
void
rtcadvance(void)
{
	vlong t;
	int irqval;
	
	if(rtcshared == nil)
		return;
	
	lock(&rtcshared->lock);
	
	if(rtcshared->rtcnext != -1){
		t = vmtime_ns();
		if(t >= rtcshared->rtcnext){
			rtcshared->cmos[0xc] |= 0x40;
			rtcshared->rtcnext = -1;
			/* No timer_cancel() needed */
		}
		/* No timer_set() needed - CPU0 reads rtcnext directly */
	}
	
	irqval = (rtcshared->cmos[0xc] & rtcshared->cmos[0xb] & 0x70) != 0;
	
	unlock(&rtcshared->lock);
	
	ioapic_irqline(8, irqval);
}

/* Called with rtcshared->lock held */
static void
rtcadvance_locked(void)
{
	vlong t;
	
	if(rtcshared->rtcnext != -1){
		t = vmtime_ns();
		if(t >= rtcshared->rtcnext){
			rtcshared->cmos[0xc] |= 0x40;
			rtcshared->rtcnext = -1;
		}
	}
}

static void
rtcset(void)
{
	vlong t, b;
	int d;

	/* Caller must hold rtcshared->lock */
	rtcadvance_locked();
	
	if((rtcshared->cmos[0xa] >> 4) > 2 || 
	   (rtcshared->cmos[0xa] & 15) == 0 || 
	   (rtcshared->cmos[0xc] & 0x40) != 0){
		rtcshared->rtcnext = -1;
		return;
	}
	
	switch(rtcshared->cmos[0xa]){
	case 0x21: d = 12; break;
	case 0x22: d = 13; break;
	default: d = 4 + (rtcshared->cmos[0xa] & 0xf);
	}
	
	b = (1000000000ULL << d) / 1048576;
	t = vmtime_ns();
	rtcshared->rtcnext = t + b - t % b;
	
	/* No timer_set() - CPU0 reads rtcnext directly */
}

u32int
rtcio(int isin, u16int port, u32int val, int sz, void *)
{
	static u8int addr;
	uintptr basemem, extmem;
	int i, s;
	Tm *tm;
	u32int ret = 0;
	int state_changed = 0;
	
	if(sz != 1) vmerror("rtc: access size %d != 1", sz);
	val = (u8int)val;
	
	lock(&rtcshared->lock);
	
	if(!rtcshared->cmosinit){
		basemem = gavail(gptr(0, 0)) >> 10;
		if(basemem > 640) basemem = 640;
		extmem = gavail(gptr(1<<20, 0)) >> 10;
		if(extmem >= 65535) extmem = 65535;
		rtcshared->cmos[0x15] = basemem;
		rtcshared->cmos[0x16] = basemem >> 8;
		rtcshared->cmos[0x17] = extmem;
		rtcshared->cmos[0x18] = extmem >> 8;
		s = 0;
		for(i = 0x10; i < 0x2e; i++)
			s += rtcshared->cmos[i];
		rtcshared->cmos[0x2e] = s >> 8;
		rtcshared->cmos[0x2f] = s;
		rtcshared->cmosinit = 1;
	}
	
	switch(isin << 16 | port){
	case 0x70:
		addr = val;
		break;
	case 0x71:
		switch(addr){
		case 0xa:
			rtcshared->cmos[addr] = val & 0x7f;
			rtcset();
			state_changed = 1;
			break;
		case 0xb:
			rtcshared->cmos[addr] = val | 2;
			rtcadvance_locked();
			state_changed = 1;
			break;
		case 0xc: case 0xd:
			goto no;
		default:
			if(addr < nelem(rtcshared->cmos))
				rtcshared->cmos[addr] = val;
			else no:
				vmerror("rtc: write to unknown address %#x (val=%#x)", addr, val);
			break;
		}
		break;
	case 0x10070:
		ret = addr;
		break;
	case 0x10071:
		tm = gmtime(time(nil));
		switch(addr){
		case 0x00: ret = bcd(rtcshared->cmos[0xb], tm->sec); break;
		case 0x02: ret = bcd(rtcshared->cmos[0xb], tm->min); break;
		case 0x04: ret = bcd(rtcshared->cmos[0xb], tm->hour); break;
		case 0x06: ret = bcd(rtcshared->cmos[0xb], tm->wday + 1); break;
		case 0x07: ret = bcd(rtcshared->cmos[0xb], tm->mday); break;
		case 0x08: ret = bcd(rtcshared->cmos[0xb], tm->mon + 1); break;
		case 0x09: ret = bcd(rtcshared->cmos[0xb], tm->year % 100); break;
		case 0x0c:
			i = rtcshared->cmos[0xc] | ((rtcshared->cmos[0xc] & rtcshared->cmos[0xb] & 0x70) != 0) << 7;
			rtcshared->cmos[0xc] = 0;
			rtcset();
			state_changed = 1;
			ret = i;
			break;
		case 0x32: ret = bcd(rtcshared->cmos[0xb], tm->year / 100 + 19); break;
		default:
			if(addr < nelem(rtcshared->cmos))
				ret = rtcshared->cmos[addr];
			else
				vmerror("rtc: read from unknown address %#x", addr);
			break;
		}
		break;
	default:
		unlock(&rtcshared->lock);
		return iowhine(isin, port, val, sz, "rtc");
	}
	
	unlock(&rtcshared->lock);
	
	/* Kick CPU0 to recompute deadlines if state changed */
	if(state_changed)
		timer_kick();
	
	return ret;
}

void
rtc_init(void)
{
	extern void *vioalloc(ulong);
	rtcshared = vioalloc(sizeof(RtcShared));
	memset(rtcshared, 0, sizeof(RtcShared));
	
	/* Initial CMOS values */
	rtcshared->cmos[1] = 0xff;
	rtcshared->cmos[3] = 0xff;
	rtcshared->cmos[5] = 0xff;
	rtcshared->cmos[0xa] = 0x26;
	rtcshared->cmos[0xb] = 1<<1;
	rtcshared->cmos[0xd] = 1<<7;    /* cmos valid */
	rtcshared->cmos[0xf] = 0x56;    /* cmos tests pass */
	rtcshared->cmos[0x11] = 0x80;   /* mouse enabled */
	rtcshared->cmos[0x14] = 0x2e;   /* cga 80-column */
	rtcshared->cmos[0x2d] = 0x1c;   /* caches + fpu enabled */
	rtcshared->rtcnext = -1;
	rtcshared->cmosinit = 0;
	
	dprint("RTC: initialized (shared)\n");
}