#include <types.h>
#include <mach/message.h>
#include <kern/thread.h>
#include <kern/clock.h>
#include <kern/spl.h>
#include <kern/processor.h>
#include <kern/misc_protos.h>
#include <i386/cpu_data.h>
#include <i386/cpu_number.h>
#include <i386/pio.h>
#include <i386/AT386/rtc.h>
#include <i386/AT386/bbclock_entries.h>
static int month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
extern char dectohexdec(
int n);
extern int hexdectodec(
char c);
extern int yeartoday(
int yr);
extern void rtcput(
struct rtc_st * regs);
extern int rtcget(
struct rtc_st * regs);
#define LOCK_BBC() splclock()
#define UNLOCK_BBC(s) splx(s)
int
bbc_config(void)
{
int BbcFlag;
struct rtc_st rtclk;
mp_disable_preemption();
if (cpu_number() != master_cpu) {
mp_enable_preemption();
return(1);
}
outb(RTC_ADDR, RTC_A);
outb(RTC_DATA, RTC_DIV2 | RTC_RATE6);
outb(RTC_ADDR, RTC_B);
outb(RTC_DATA, RTC_HM);
BbcFlag = (rtcget(&rtclk) ? 0 : 1);
if (BbcFlag)
printf("battery clock configured\n");
else
printf("WARNING: Battery Clock Failure!\n");
mp_enable_preemption();
return (BbcFlag);
}
kern_return_t
bbc_gettime(
mach_timespec_t *cur_time)
{
struct rtc_st rtclk;
time_t n;
int sec, min, hr, dom, mon, yr;
int i, days = 0;
spl_t s;
thread_t thread;
if ((thread = current_thread()) != THREAD_NULL) {
thread_bind(thread, master_processor);
mp_disable_preemption();
if (current_processor() != master_processor) {
mp_enable_preemption();
thread_block(THREAD_CONTINUE_NULL);
} else {
mp_enable_preemption();
}
}
s = LOCK_BBC();
rtcget(&rtclk);
sec = hexdectodec(rtclk.rtc_sec);
min = hexdectodec(rtclk.rtc_min);
hr = hexdectodec(rtclk.rtc_hr);
dom = hexdectodec(rtclk.rtc_dom);
mon = hexdectodec(rtclk.rtc_mon);
yr = hexdectodec(rtclk.rtc_yr);
yr = (yr < 70) ? yr+100 : yr;
n = sec + 60 * min + 3600 * hr;
n += (dom - 1) * 3600 * 24;
if (yeartoday(yr) == 366)
month[1] = 29;
for (i = mon - 2; i >= 0; i--)
days += month[i];
month[1] = 28;
for (i = 70; i < yr; i++)
days += yeartoday(i);
n += days * 3600 * 24;
cur_time->tv_sec = n;
cur_time->tv_nsec = 0;
UNLOCK_BBC(s);
if (thread != THREAD_NULL)
thread_bind(thread, PROCESSOR_NULL);
return (KERN_SUCCESS);
}
kern_return_t
bbc_settime(
mach_timespec_t *new_time)
{
struct rtc_st rtclk;
time_t n;
int diff, i, j;
spl_t s;
thread_t thread;
if ((thread = current_thread()) != THREAD_NULL) {
thread_bind(thread, master_processor);
mp_disable_preemption();
if (current_processor() != master_processor) {
mp_enable_preemption();
thread_block(THREAD_CONTINUE_NULL);
} else {
mp_enable_preemption();
}
}
s = LOCK_BBC();
rtcget(&rtclk);
diff = 0;
n = (new_time->tv_sec - diff) % (3600 * 24);
rtclk.rtc_sec = dectohexdec(n%60);
n /= 60;
rtclk.rtc_min = dectohexdec(n%60);
rtclk.rtc_hr = dectohexdec(n/60);
n = (new_time->tv_sec - diff) / (3600 * 24);
rtclk.rtc_dow = (n + 4) % 7;
for (j = 70; n >= (i = yeartoday(j)); j++)
n -= i;
rtclk.rtc_yr = dectohexdec(j % 100);
if (yeartoday(j) == 366)
month[1] = 29;
for (i = 0; n >= month[i]; i++)
n -= month[i];
month[1] = 28;
rtclk.rtc_mon = dectohexdec(++i);
rtclk.rtc_dom = dectohexdec(++n);
rtcput(&rtclk);
UNLOCK_BBC(s);
if (thread != THREAD_NULL)
thread_bind(current_thread(), PROCESSOR_NULL);
return (KERN_SUCCESS);
}
int
rtcget(
struct rtc_st * regs)
{
outb(RTC_ADDR, RTC_D);
if ((inb(RTC_DATA) & RTC_VRT) == 0)
return (-1);
outb(RTC_ADDR, RTC_A);
while (inb(RTC_DATA) & RTC_UIP)
outb(RTC_ADDR, RTC_A);
load_rtc((unsigned char *)regs);
return (0);
}
void
rtcput(
struct rtc_st * regs)
{
register unsigned char x;
outb(RTC_ADDR, RTC_B);
x = inb(RTC_DATA);
outb(RTC_ADDR, RTC_B);
outb(RTC_DATA, x | RTC_SET);
save_rtc((unsigned char *)regs);
outb(RTC_ADDR, RTC_B);
outb(RTC_DATA, x & ~RTC_SET);
}
int
yeartoday(
int year)
{
year += 1900;
return((year % 4) ? 365 :
((year % 100) ? 366 : ((year % 400) ? 365: 366)));
}
int
hexdectodec(
char n)
{
return ((((n >> 4) & 0x0F) * 10) + (n & 0x0F));
}
char
dectohexdec(
int n)
{
return ((char)(((n / 10) << 4) & 0xF0) | ((n % 10) & 0x0F));
}