From e55e1a0e666da61eb901533dae4af2ec44d216d8 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Wed, 4 Jan 2006 04:55:04 +0000 Subject: [PATCH] Implemented the PPC specific RTC support. We search for an "rtc" device in the Open Firmware implementation of boot loader and pass its path to the kernel, where it's opened and used for getting/setting the real time. The expensive atomic_*64() on PPC 32-bit make things a bit more complicated. Moreover, missing 64 bit multiplication and division instructions won't really allow system_time() to be anywhere near as fast as on x86. :-/ git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@15837 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/private/kernel/arch/ppc/arch_cpu.h | 5 ++ .../private/kernel/arch/ppc/arch_platform.h | 7 ++ .../kernel/arch/ppc/arch_real_time_data.h | 36 +++++++++++ .../openfirmware/platform_kernel_args.h | 1 + src/system/boot/platform/openfirmware/Jamfile | 1 + .../platform/openfirmware/real_time_clock.cpp | 30 +++++++++ .../platform/openfirmware/real_time_clock.h | 21 ++++++ src/system/boot/platform/openfirmware/start.c | 4 ++ src/system/kernel/arch/ppc/arch_platform.cpp | 64 ++++++++++++++++++- .../kernel/arch/ppc/arch_real_time_clock.cpp | 57 +++++++++++++++-- src/system/kernel/lib/Jamfile | 3 +- src/system/libroot/os/arch/ppc/Jamfile | 3 +- src/system/libroot/os/arch/ppc/system_time.c | 38 +++++++++++ .../ppc/{system_time.S => system_time_asm.S} | 0 src/system/libroot/os/arch/ppc/time.c | 27 ++++++-- src/system/libroot/os/arch/x86/Jamfile | 2 +- src/system/libroot/os/arch/x86/system_time.c | 4 ++ .../x86/{system_time.S => system_time_asm.S} | 0 18 files changed, 288 insertions(+), 15 deletions(-) create mode 100644 headers/private/kernel/arch/ppc/arch_real_time_data.h create mode 100644 src/system/boot/platform/openfirmware/real_time_clock.cpp create mode 100644 src/system/boot/platform/openfirmware/real_time_clock.h create mode 100644 src/system/libroot/os/arch/ppc/system_time.c rename src/system/libroot/os/arch/ppc/{system_time.S => system_time_asm.S} (100%) create mode 100644 src/system/libroot/os/arch/x86/system_time.c rename src/system/libroot/os/arch/x86/{system_time.S => system_time_asm.S} (100%) diff --git a/headers/private/kernel/arch/ppc/arch_cpu.h b/headers/private/kernel/arch/ppc/arch_cpu.h index 407ba61028..18ef2b81c6 100644 --- a/headers/private/kernel/arch/ppc/arch_cpu.h +++ b/headers/private/kernel/arch/ppc/arch_cpu.h @@ -105,6 +105,11 @@ extern void reset_dbats(void); //extern void setl2cr(unsigned int val); extern long long get_time_base(void); +void __ppc_setup_system_time(vint64 *cvFactor); + // defined in libroot: os/arch/system_time.c +int64 __ppc_get_time_base(void); + // defined in libroot: os/arch/system_time_asm.S + extern void ppc_context_switch(void **_oldStackPointer, void *newStackPointer); #ifdef __cplusplus diff --git a/headers/private/kernel/arch/ppc/arch_platform.h b/headers/private/kernel/arch/ppc/arch_platform.h index 8637eeeada..a770b4c95b 100644 --- a/headers/private/kernel/arch/ppc/arch_platform.h +++ b/headers/private/kernel/arch/ppc/arch_platform.h @@ -7,6 +7,8 @@ #include +struct real_time_data; + namespace BPrivate { class PPCPlatform { @@ -19,9 +21,14 @@ public: virtual status_t Init(struct kernel_args *kernelArgs) = 0; virtual status_t InitSerialDebug(struct kernel_args *kernelArgs) = 0; virtual status_t InitPostVM(struct kernel_args *kernelArgs) = 0; + virtual status_t InitRTC(struct kernel_args *kernelArgs, + struct real_time_data *data) = 0; virtual char SerialDebugGetChar() = 0; virtual void SerialDebugPutChar(char c) = 0; + + virtual void SetHardwareRTC(uint32 seconds) = 0; + virtual uint32 GetHardwareRTC() = 0; }; } // namespace BPrivate diff --git a/headers/private/kernel/arch/ppc/arch_real_time_data.h b/headers/private/kernel/arch/ppc/arch_real_time_data.h new file mode 100644 index 0000000000..c1e88cd381 --- /dev/null +++ b/headers/private/kernel/arch/ppc/arch_real_time_data.h @@ -0,0 +1,36 @@ +/* + * Copyright 2006, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef _KERNEL_ARCH_REAL_TIME_DATA_H +#define _KERNEL_ARCH_REAL_TIME_DATA_H + +#include +#include + + +struct ppc_real_time_data { + vint64 system_time_offset; +}; + +struct arch_real_time_data { + struct ppc_real_time_data data[2]; + vint64 system_time_conversion_factor; + vint32 version; + // Since there're no cheap atomic_{set,get,add}64() on PPC 32 (i.e. one + // that doesn't involve a syscall), we can't have just a single + // system_time_offset and set/get it atomically. + // That's why have our data twice. One set is current (indexed by + // version % 2). When setting the offset, we do that with disabled + // interrupts and protected by a spinlock. We write the new values + // into the other array element and increment the version. + // A reader first reads the version, then the date of interest, and + // finally rechecks the version. If it hasn't changed in the meantime, + // the read value is fine, otherwise it runs the whole procedure again. + // + // system_time_conversion_factor is currently consider constant, + // although that is not necessarily true. We simply don't support + // changing conversion factors at the moment. +}; + +#endif /* _KERNEL_ARCH_REAL_TIME_DATA_H */ diff --git a/headers/private/kernel/boot/platform/openfirmware/platform_kernel_args.h b/headers/private/kernel/boot/platform/openfirmware/platform_kernel_args.h index affddc604a..94c8ab6c31 100644 --- a/headers/private/kernel/boot/platform/openfirmware/platform_kernel_args.h +++ b/headers/private/kernel/boot/platform/openfirmware/platform_kernel_args.h @@ -18,6 +18,7 @@ typedef struct { void *openfirmware_entry; + char rtc_path[128]; } platform_kernel_args; #endif /* KERNEL_BOOT_PLATFORM_OPENFIRMWARE_KERNEL_ARGS_H */ diff --git a/src/system/boot/platform/openfirmware/Jamfile b/src/system/boot/platform/openfirmware/Jamfile index 4a14c20c80..a5ae2e9d52 100644 --- a/src/system/boot/platform/openfirmware/Jamfile +++ b/src/system/boot/platform/openfirmware/Jamfile @@ -11,6 +11,7 @@ KernelMergeObject boot_platform_openfirmware.o : menu.cpp mmu.cpp network.cpp + real_time_clock.cpp start.c support.cpp video.cpp diff --git a/src/system/boot/platform/openfirmware/real_time_clock.cpp b/src/system/boot/platform/openfirmware/real_time_clock.cpp new file mode 100644 index 0000000000..957d73008b --- /dev/null +++ b/src/system/boot/platform/openfirmware/real_time_clock.cpp @@ -0,0 +1,30 @@ +/* + * Copyright 2006, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include "real_time_clock.h" + +#include + +#include +#include +#include + +#include "devices.h" + +status_t +init_real_time_clock(void) +{ + // find RTC + int rtcCookie = 0; + if (platform_get_next_device(&rtcCookie, 0, "rtc", + gKernelArgs.platform_args.rtc_path, + sizeof(gKernelArgs.platform_args.rtc_path)) != B_OK) { + printf("init_real_time_clock(): Found no RTC device!"); + return B_ERROR; + } + + return B_OK; +} + diff --git a/src/system/boot/platform/openfirmware/real_time_clock.h b/src/system/boot/platform/openfirmware/real_time_clock.h new file mode 100644 index 0000000000..36b4f9a3a5 --- /dev/null +++ b/src/system/boot/platform/openfirmware/real_time_clock.h @@ -0,0 +1,21 @@ +/* + * Copyright 2006, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#ifndef OPENFIRMWARE_REAL_TIME_CLOCK_H +#define OPENFIRMWARE_REAL_TIME_CLOCK_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +status_t init_real_time_clock(void); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* OPENFIRMWARE_REAL_TIME_CLOCK_H */ diff --git a/src/system/boot/platform/openfirmware/start.c b/src/system/boot/platform/openfirmware/start.c index cf004e2c65..c2259f30d0 100644 --- a/src/system/boot/platform/openfirmware/start.c +++ b/src/system/boot/platform/openfirmware/start.c @@ -15,6 +15,7 @@ #include "console.h" #include "machine.h" +#include "real_time_clock.h" #define HEAP_SIZE 65536 @@ -148,6 +149,9 @@ start(void *openFirmwareEntry) if (boot_arch_cpu_init() != B_OK) platform_exit(); + if (init_real_time_clock() != B_OK) + platform_exit(); + gKernelArgs.platform_args.openfirmware_entry = openFirmwareEntry; main(&args); diff --git a/src/system/kernel/arch/ppc/arch_platform.cpp b/src/system/kernel/arch/ppc/arch_platform.cpp index 41cdbb065d..b4e7f09eb6 100644 --- a/src/system/kernel/arch/ppc/arch_platform.cpp +++ b/src/system/kernel/arch/ppc/arch_platform.cpp @@ -11,6 +11,7 @@ #include #include +#include #include @@ -48,13 +49,19 @@ public: virtual status_t Init(struct kernel_args *kernelArgs); virtual status_t InitSerialDebug(struct kernel_args *kernelArgs); virtual status_t InitPostVM(struct kernel_args *kernelArgs); + virtual status_t InitRTC(struct kernel_args *kernelArgs, + struct real_time_data *data); virtual char SerialDebugGetChar(); virtual void SerialDebugPutChar(char c); + virtual void SetHardwareRTC(uint32 seconds); + virtual uint32 GetHardwareRTC(); + private: - int fInput; - int fOutput; + int fInput; + int fOutput; + int fRTC; }; } // namespace BPrivate @@ -85,7 +92,8 @@ debug_command_of_enter(int argc, char **argv) // constructor PPCOpenFirmware::PPCOpenFirmware() : fInput(-1), - fOutput(-1) + fOutput(-1), + fRTC(-1) { } @@ -127,6 +135,23 @@ PPCOpenFirmware::InitPostVM(struct kernel_args *kernelArgs) return B_OK; } +// InitRTC +status_t +PPCOpenFirmware::InitRTC(struct kernel_args *kernelArgs, + struct real_time_data *data) +{ +kprintf("PPCOpenFirmware::InitRTC(): opening \"%s\"...\n", kernelArgs->platform_args.rtc_path); + // open RTC + fRTC = of_open(kernelArgs->platform_args.rtc_path); +kprintf(" of_open() returned: %d\n", fRTC); + if (fRTC == OF_FAILED) { + dprintf("PPCOpenFirmware::InitRTC(): Failed open RTC device!\n"); + return B_ERROR; + } + + return B_OK; +} + // DebugSerialGetChar char PPCOpenFirmware::SerialDebugGetChar() @@ -147,6 +172,39 @@ PPCOpenFirmware::SerialDebugPutChar(char c) of_write(fOutput, &c, 1); } +// SetHardwareRTC +void +PPCOpenFirmware::SetHardwareRTC(uint32 seconds) +{ + struct tm t; + rtc_secs_to_tm(seconds, &t); + + t.tm_year += RTC_EPOCHE_BASE_YEAR; + t.tm_mon++; + + if (of_call_method(fRTC, "set-time", 6, 0, t.tm_year, t.tm_mon, t.tm_mday, + t.tm_hour, t.tm_min, t.tm_sec) == OF_FAILED) { + dprintf("PPCOpenFirmware::SetHardwareRTC(): Failed to set RTC!\n"); + } +} + +// GetHardwareRTC +uint32 +PPCOpenFirmware::GetHardwareRTC() +{ + struct tm t; + if (of_call_method(fRTC, "get-time", 0, 6, &t.tm_year, &t.tm_mon, + &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec) == OF_FAILED) { + dprintf("PPCOpenFirmware::GetHardwareRTC(): Failed to get RTC!\n"); + return 0; + } + + t.tm_year -= RTC_EPOCHE_BASE_YEAR; + t.tm_mon--; + + return rtc_tm_to_secs(&t); +} + // # pragma mark - diff --git a/src/system/kernel/arch/ppc/arch_real_time_clock.cpp b/src/system/kernel/arch/ppc/arch_real_time_clock.cpp index e2d6902e8c..f074536487 100644 --- a/src/system/kernel/arch/ppc/arch_real_time_clock.cpp +++ b/src/system/kernel/arch/ppc/arch_real_time_clock.cpp @@ -1,16 +1,34 @@ /* - * Copyright 2005, Axel Dörfler, axeld@pinc-software.de - * Distributed under the terms of the MIT License. + * Copyright 2006, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. */ - #include -#include +#include +#include +#include + + +static spinlock sSetArchDataLock; status_t arch_rtc_init(kernel_args *args, struct real_time_data *data) { + // init the platform RTC service + status_t error = PPCPlatform::Default()->InitRTC(args, data); + if (error != B_OK) + return error; + + // init the arch specific part of the real_time_data + data->arch_data.data[0].system_time_offset = 0; + data->arch_data.system_time_conversion_factor + = args->arch_args.time_base_frequency; + data->arch_data.version = 0; + + // init spinlock + sSetArchDataLock = 0; + return B_OK; } @@ -18,12 +36,41 @@ arch_rtc_init(kernel_args *args, struct real_time_data *data) uint32 arch_rtc_get_hw_time(void) { - return 0; + return PPCPlatform::Default()->GetHardwareRTC(); } void arch_rtc_set_hw_time(uint32 seconds) { + PPCPlatform::Default()->SetHardwareRTC(seconds); } + +void +arch_rtc_set_system_time_offset(struct real_time_data *data, bigtime_t offset) +{ + cpu_status state = disable_interrupts(); + acquire_spinlock(&sSetArchDataLock); + + int32 version = data->arch_data.version + 1; + data->arch_data.data[version % 2].system_time_offset = offset; + data->arch_data.version = version; + + release_spinlock(&sSetArchDataLock); + restore_interrupts(state); +} + + +bigtime_t +arch_rtc_get_system_time_offset(struct real_time_data *data) +{ + int32 version; + bigtime_t offset; + do { + version = data->arch_data.version; + offset = data->arch_data.data[version % 2].system_time_offset; + } while (version != data->arch_data.version); + + return offset; +} diff --git a/src/system/kernel/lib/Jamfile b/src/system/kernel/lib/Jamfile index d26ab47646..44de4de928 100644 --- a/src/system/kernel/lib/Jamfile +++ b/src/system/kernel/lib/Jamfile @@ -111,7 +111,8 @@ SEARCH_SOURCE += [ FDirName $(librootSources) os arch $(TARGET_ARCH) ] ; KernelMergeObject kernel_os_arch_$(TARGET_ARCH).o : atomic.S byteorder.S - system_time.S + system_time_asm.S + system_time.c : $(TARGET_KERNEL_PIC_CCFLAGS) ; diff --git a/src/system/libroot/os/arch/ppc/Jamfile b/src/system/libroot/os/arch/ppc/Jamfile index 8bbcf87822..8474e3d05d 100644 --- a/src/system/libroot/os/arch/ppc/Jamfile +++ b/src/system/libroot/os/arch/ppc/Jamfile @@ -8,7 +8,8 @@ MergeObject os_arch_$(TARGET_ARCH).o : compatibility.c # only here until the places where those functions are used # are fixed # systeminfo.c - system_time.S + system_time.c + system_time_asm.S thread.c time.c tls.c diff --git a/src/system/libroot/os/arch/ppc/system_time.c b/src/system/libroot/os/arch/ppc/system_time.c new file mode 100644 index 0000000000..8eecc81a60 --- /dev/null +++ b/src/system/libroot/os/arch/ppc/system_time.c @@ -0,0 +1,38 @@ +/* + * Copyright 2006, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include + +#include +#include +#include + + +static vint64 *sConversionFactor; + +void +__ppc_setup_system_time(vint64 *cvFactor) +{ + sConversionFactor = cvFactor; +} + + +// Note: We don't implement system_time() in assembly since the 64 bit +// divisions/multiplications are a bit tough to implement for a 32 bit +// PPC. +bigtime_t +system_time(void) +{ + uint64 timeBase = __ppc_get_time_base(); + // TODO: The multiplication doesn't look that nice. The value can easily + // overflow when timeBase gets big enough. The limit for timebase is + // about 2^(64 - 20). This might sound a lot, but the conversion factor + // might be quite big. Assuming a worst case factor of 2^32, + // this would leave us with only about 2^12 = 4096 seconds we can + // represent. The actual factor for my Mac mini is about 40 * 10^6, i.e. + // the overflow limit is ca. 100 times greater, but that isn't more than + // five days either. + return (timeBase * 1000000ULL) / *sConversionFactor; +} diff --git a/src/system/libroot/os/arch/ppc/system_time.S b/src/system/libroot/os/arch/ppc/system_time_asm.S similarity index 100% rename from src/system/libroot/os/arch/ppc/system_time.S rename to src/system/libroot/os/arch/ppc/system_time_asm.S diff --git a/src/system/libroot/os/arch/ppc/time.c b/src/system/libroot/os/arch/ppc/time.c index 1ee63294fc..b8e9ab6e43 100644 --- a/src/system/libroot/os/arch/ppc/time.c +++ b/src/system/libroot/os/arch/ppc/time.c @@ -10,14 +10,33 @@ #include +static struct arch_real_time_data *sRealTimeData; + void __arch_init_time(struct real_time_data *data, bool setDefaults) { + sRealTimeData = &data->arch_data; + if (setDefaults) { - data->arch_data.data[0].system_time_offset = 0; - data->arch_data.system_time_conversion_factor = 1000000000LL; - data->arch_data.version = 0; + sRealTimeData->data[0].system_time_offset = 0; + sRealTimeData->system_time_conversion_factor = 1000000000LL; + sRealTimeData->version = 0; } - __ppc_setup_system_time(&data->arch_data.system_time_conversion_factor); + __ppc_setup_system_time(&sRealTimeData->system_time_conversion_factor); } + + +bigtime_t +__arch_get_system_time_offset(struct real_time_data *data) +{ + int32 version; + bigtime_t offset; + do { + version = sRealTimeData->version; + offset = sRealTimeData->data[version % 2].system_time_offset; + } while (version != sRealTimeData->version); + + return offset; +} + diff --git a/src/system/libroot/os/arch/x86/Jamfile b/src/system/libroot/os/arch/x86/Jamfile index 4fb1abe019..08eb738494 100644 --- a/src/system/libroot/os/arch/x86/Jamfile +++ b/src/system/libroot/os/arch/x86/Jamfile @@ -8,7 +8,7 @@ MergeObject os_arch_$(TARGET_ARCH).o : compatibility.c get_stack_frame.S system_info.c - system_time.S + system_time_asm.S thread.c time.c tls.c diff --git a/src/system/libroot/os/arch/x86/system_time.c b/src/system/libroot/os/arch/x86/system_time.c new file mode 100644 index 0000000000..6b4e3d7df9 --- /dev/null +++ b/src/system/libroot/os/arch/x86/system_time.c @@ -0,0 +1,4 @@ +/* + Just a dummy to avoid a special case in the build system. system_time() + is implemented in system_time_asm.S. +*/ diff --git a/src/system/libroot/os/arch/x86/system_time.S b/src/system/libroot/os/arch/x86/system_time_asm.S similarity index 100% rename from src/system/libroot/os/arch/x86/system_time.S rename to src/system/libroot/os/arch/x86/system_time_asm.S