diff --git a/headers/private/kernel/arch/real_time_clock.h b/headers/private/kernel/arch/real_time_clock.h new file mode 100644 index 0000000000..0b4be8ad25 --- /dev/null +++ b/headers/private/kernel/arch/real_time_clock.h @@ -0,0 +1,15 @@ +/* +** Copyright 2003, Jeff Ward, jeff@r2d2.stcloudstate.edu. All rights reserved. +** Distributed under the terms of the OpenBeOS License. +*/ +#ifndef KERNEL_ARCH_REAL_TIME_CLOCK_H +#define KERNEL_ARCH_REAL_TIME_CLOCK_H + +#include + +void arch_rtc_set_hw_time(uint32 seconds); + // Set HW clock to 'seconds' since 1/1/1970 +uint32 arch_rtc_get_hw_time(void); + // Returns number of seconds since 1/1/1970 as stored in HW + +#endif /* KERNEL_ARCH_REAL_TIME_CLOCK_H */ diff --git a/headers/private/kernel/real_time_clock.h b/headers/private/kernel/real_time_clock.h new file mode 100644 index 0000000000..54e7b8e2e1 --- /dev/null +++ b/headers/private/kernel/real_time_clock.h @@ -0,0 +1,22 @@ +/* +** Copyright 2003, Jeff Ward, jeff@r2d2.stcloudstate.edu. All rights reserved. +** Distributed under the terms of the OpenBeOS License. +*/ +#ifndef _KERNEL_REAL_TIME_CLOCK_H +#define _KERNEL_REAL_TIME_CLOCK_H + + +#include + + +int rtc_init(kernel_args *ka); +// Initialize the real-time clock. + +void rtc_set_system_time(uint32 current_time); +// Set the system time. 'current_time' is the number of seconds elapsed since +// Jan. 1, 1970. + +bigtime_t rtc_boot_time(void); +// Returns the time at which the system was booted in microseconds since Jan 1, 1970. + +#endif /* _KERNEL_REAL_TIME_CLOCK_H */ diff --git a/src/kernel/core/Jamfile b/src/kernel/core/Jamfile index 03baa278b7..8600103b8a 100644 --- a/src/kernel/core/Jamfile +++ b/src/kernel/core/Jamfile @@ -20,7 +20,8 @@ KernelMergeObject kernel_core.o : <$(SOURCE_GRIST)>module.c # <$(SOURCE_GRIST)>pools.c <$(SOURCE_GRIST)>port.c - <$(SOURCE_GRIST)>queue.c + <$(SOURCE_GRIST)>queue.c + <$(SOURCE_GRIST)>real_time_clock.c <$(SOURCE_GRIST)>scheduler.c <$(SOURCE_GRIST)>sem.c <$(SOURCE_GRIST)>signal.c diff --git a/src/kernel/core/arch/x86/Jamfile b/src/kernel/core/arch/x86/Jamfile index 2c793d8801..b38e231b05 100644 --- a/src/kernel/core/arch/x86/Jamfile +++ b/src/kernel/core/arch/x86/Jamfile @@ -8,6 +8,7 @@ KernelStaticLibrary libx86 : <$(SOURCE_GRIST)>arch_faults.c <$(SOURCE_GRIST)>arch_int.c # <$(SOURCE_GRIST)>arch_selector.c + <$(SOURCE_GRIST)>arch_real_time_clock.c <$(SOURCE_GRIST)>arch_smp.c <$(SOURCE_GRIST)>arch_thread.c <$(SOURCE_GRIST)>arch_timer.c diff --git a/src/kernel/core/arch/x86/arch_real_time_clock.c b/src/kernel/core/arch/x86/arch_real_time_clock.c new file mode 100644 index 0000000000..735f26167f --- /dev/null +++ b/src/kernel/core/arch/x86/arch_real_time_clock.c @@ -0,0 +1,264 @@ +/* +** Copyright 2003, Jeff Ward, jeff@r2d2.stcloudstate.edu. All rights reserved. +** Distributed under the terms of the OpenBeOS License. +*/ + + +#include +#include + +#define CMOS_ADDR_PORT 0x70 +#define CMOS_DATA_PORT 0x71 +#define BASE_YEAR 1970 +#define SECONDS_31 2678400 +#define SECONDS_30 2592000 +#define SECONDS_28 2419200 +#define SECONDS_DAY 86400 + +typedef struct { + uint8 second; + uint8 minute; + uint8 hour; + uint8 day; + uint8 month; + uint8 year; + uint8 century; +} cmos_time; + +uint32 secs_per_month[12] = {SECONDS_31, SECONDS_28, SECONDS_31, SECONDS_30, + SECONDS_31, SECONDS_30, SECONDS_31, SECONDS_31, SECONDS_30, SECONDS_31, SECONDS_30, + SECONDS_31}; + +static uint32 +bcd_to_int(uint8 bcd) { + uint32 numl; + uint32 numh; + + numl = bcd & 0x0f; + numh = (bcd & 0xf0) >> 4; + + return numh * 10 + numl; +} + +static uint8 +int_to_bcd(uint32 number) { + uint8 low; + uint8 high; + + if (number > 99) + return 0; + + high = number / 10; + low = number % 10; + + return (high << 4) | low; +} + +static int +leap_year(uint32 year) { + if (year % 400 == 0) + return 1; + + if (year % 100 == 0) + return 0; + + if (year % 4 == 0) + return 1; + + return 0; +} + +static int +same_time(const cmos_time* time1, const cmos_time* time2) { + return (time1->second == time2->second) && + (time1->minute == time2->minute) && + (time1->hour == time2->hour) && + (time1->day == time2->day) && + (time1->month == time2->month) && + (time1->year == time2->year) && + (time1->century == time2->century); +} + +static uint8 +cmos_read(uint8 addr) { + int wait_time; + + wait_time = 10000; + +// Wait until bit 7 of Status Register A (indicating whether or not an update is in +// progress) is clear if we are reading one of the clock data registers... + if (addr < 0x0a) { + out8(0x0a, CMOS_ADDR_PORT); + while ( (in8(CMOS_DATA_PORT) & 0x80) && --wait_time ); + } + +// then read the value. + out8(addr, CMOS_ADDR_PORT); + return in8(CMOS_DATA_PORT); +} + +static void +cmos_write(uint8 addr, uint8 data) { + out8(addr, CMOS_ADDR_PORT); + out8(data, CMOS_DATA_PORT); +} + +static void +set_24_hour_mode(void) { + uint8 status_b; + + status_b = cmos_read(0x0b); + status_b |= 0x02; + cmos_write(0x0b, status_b); +} + +static void +read_cmos_clock(cmos_time* cmos) { + set_24_hour_mode(); + + cmos->century = cmos_read(0x32); + cmos->year = cmos_read(0x09); + cmos->month = cmos_read(0x08); + cmos->day = cmos_read(0x07); + cmos->hour = cmos_read(0x04); + cmos->minute = cmos_read(0x02); + cmos->second = cmos_read(0x00); +} + +static void +write_cmos_clock(cmos_time* cmos) { + set_24_hour_mode(); + + cmos_write(0x32, cmos->century); + cmos_write(0x09, cmos->year); + cmos_write(0x08, cmos->month); + cmos_write(0x07, cmos->day); + cmos_write(0x04, cmos->hour); + cmos_write(0x02, cmos->minute); + cmos_write(0x00, cmos->second); +} + +static uint32 +cmos_to_secs(const cmos_time* cmos) { + uint32 whole_year; + uint32 time = 0; + int i; + + whole_year = bcd_to_int(cmos->century) * 100 + bcd_to_int(cmos->year); + +// Add up the seconds from all years since 1970 that have elapsed. + for (i = BASE_YEAR; i < whole_year; ++i) { + if ( leap_year(i) ) + time += 31622400; + + else + time += 31536000; + } + +// Add up the seconds from all months passed this year. + for (i = 0; i < bcd_to_int(cmos->month) - 1 && i < 12; ++i) + time += secs_per_month[i]; + +// Add up the seconds from all days passed this month. + if ( leap_year(whole_year) && bcd_to_int(cmos->month) > 2 ) + time += SECONDS_DAY; + + time += ( bcd_to_int(cmos->day) - 1 ) * SECONDS_DAY; + time += bcd_to_int(cmos->hour) * 3600; + time += bcd_to_int(cmos->minute) * 60; + time += bcd_to_int(cmos->second); + + return time; +} + +static void +secs_to_cmos(uint32 seconds, cmos_time* cmos) { + uint32 whole_year = BASE_YEAR; + uint32 secs_this_year; + int i; + int temp; + int is_leapyear; + int month; + int keep_looping; + + keep_looping = 1; + +// Determine the current year by starting at 1970 and incrementing whole_year as long as +// we can keep subtracting secs_this_year from seconds. + while (keep_looping) { + if ( leap_year(whole_year) ) + secs_this_year = 31622400; + + else + secs_this_year = 31536000; + + if (seconds >= secs_this_year) { + seconds -= secs_this_year; + ++whole_year; + } + + else + keep_looping = 0; + } + + cmos->century = int_to_bcd(whole_year / 100); + cmos->year = int_to_bcd(whole_year % 100); + +// Determine the current month + month = 1; + is_leapyear = leap_year(whole_year); + do { + temp = seconds - secs_per_month[month - 1]; + + if (is_leapyear && month == 2) + temp -= SECONDS_DAY; + + if (temp >= 0) { + seconds = temp; + ++month; + } + } while (temp >= 0 && month < 13); + + cmos->month = int_to_bcd(month); + + cmos->day = int_to_bcd(seconds / SECONDS_DAY + 1); + seconds = seconds % SECONDS_DAY; + + cmos->hour = int_to_bcd(seconds / 3600); + seconds = seconds % 3600; + + cmos->minute = int_to_bcd(seconds / 60); + seconds = seconds % 60; + + cmos->second = int_to_bcd(seconds); +} + +uint32 +arch_rtc_get_hw_time(void) { + int wait_time; + cmos_time cmos1; + cmos_time cmos2; + + wait_time = 1000; + +// We will read the clock twice and make sure both reads are equal. This will prevent +// problems that would occur if the clock is read during an update (e.g. if we read the hour +// at 8:59:59, the clock gets changed, and then we read the minute and second, we would +// be off by a whole hour) + do { + read_cmos_clock(&cmos1); + read_cmos_clock(&cmos2); + } while ( !same_time(&cmos1, &cmos2) && --wait_time ); + +// Convert the CMOS data to seconds since 1970. + return cmos_to_secs(&cmos1); +} + +void +arch_rtc_set_hw_time(uint32 seconds) { + cmos_time cmos; + uint32 read_back; + + secs_to_cmos(seconds, &cmos); + write_cmos_clock(&cmos); +} diff --git a/src/kernel/core/main.c b/src/kernel/core/main.c index 2045a56f0d..ea90d39ee6 100644 --- a/src/kernel/core/main.c +++ b/src/kernel/core/main.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -83,6 +84,7 @@ _start(kernel_args *oldka, int cpu_num) faults_init(&ka); smp_init(&ka); + rtc_init(&ka); timer_init(&ka); arch_cpu_init2(&ka); diff --git a/src/kernel/core/real_time_clock.c b/src/kernel/core/real_time_clock.c new file mode 100644 index 0000000000..dde1589ff7 --- /dev/null +++ b/src/kernel/core/real_time_clock.c @@ -0,0 +1,84 @@ +/* +** Copyright 2003, Jeff Ward, jeff@r2d2.stcloudstate.edu. All rights reserved. +** Distributed under the terms of the OpenBeOS License. +*/ + + +#include +#include +#include + +#include +#include + +static bigtime_t boot_time; + +static void +rtc_print(void) { + uint32 current_time; + + current_time = (boot_time + system_time()) / 1000000; + dprintf("system_time: %u\n", (unsigned)system_time()); + dprintf("boot_time: %u\n", (unsigned)boot_time); + dprintf("current_time: %u\n", (unsigned)current_time); +} + +static int +rtc_debug(int argc, char** argv) { +// If no arguments were given, output all usefull data. + if (argc < 2) + rtc_print(); + +// If there was an argument, reset the system and hw time. + else { + rtc_set_system_time( strtoul(argv[1], NULL, 10) ); + rtc_system_to_hw(); + } + + return 0; +} + +int +rtc_init(kernel_args *ka) { + dprintf("rtc_init: entry\n"); + add_debugger_command("rtc", &rtc_debug, "Set and test the real-time clock"); + + rtc_hw_to_system(); + + return 0; +} + +void +rtc_set_system_time(uint32 current_time) { + uint64 useconds; + + useconds = (uint64)current_time * 1000000; + boot_time = useconds - system_time(); +} + + +/** Write the system time to CMOS. */ + +void +rtc_system_to_hw(void) { + uint32 seconds; + + seconds = (boot_time + system_time()) / 1000000; + arch_rtc_set_hw_time(seconds); +} + + +/** Read the CMOS clock and update the system time accordingly. */ + +void +rtc_hw_to_system(void) { + uint32 current_time; + + current_time = arch_rtc_get_hw_time(); + rtc_set_system_time(current_time); +} + +bigtime_t +rtc_boot_time(void) { + return boot_time; +}