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
This commit is contained in:
Ingo Weinhold
2006-01-04 04:55:04 +00:00
parent 850f611735
commit e55e1a0e66
18 changed files with 288 additions and 15 deletions
@@ -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
@@ -7,6 +7,8 @@
#include <arch/platform.h>
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
@@ -0,0 +1,36 @@
/*
* Copyright 2006, Ingo Weinhold <[email protected]>.
* 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 <StorageDefs.h>
#include <SupportDefs.h>
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 */
@@ -18,6 +18,7 @@
typedef struct {
void *openfirmware_entry;
char rtc_path[128];
} platform_kernel_args;
#endif /* KERNEL_BOOT_PLATFORM_OPENFIRMWARE_KERNEL_ARGS_H */
@@ -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
@@ -0,0 +1,30 @@
/*
* Copyright 2006, Ingo Weinhold <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "real_time_clock.h"
#include <stdio.h>
#include <boot/kernel_args.h>
#include <boot/stage2.h>
#include <platform/openfirmware/openfirmware.h>
#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;
}
@@ -0,0 +1,21 @@
/*
* Copyright 2006, Ingo Weinhold <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef OPENFIRMWARE_REAL_TIME_CLOCK_H
#define OPENFIRMWARE_REAL_TIME_CLOCK_H
#include <SupportDefs.h>
#ifdef __cplusplus
extern "C" {
#endif
status_t init_real_time_clock(void);
#ifdef __cplusplus
} // extern "C"
#endif
#endif /* OPENFIRMWARE_REAL_TIME_CLOCK_H */
@@ -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);
+61 -3
View File
@@ -11,6 +11,7 @@
#include <boot/kernel_args.h>
#include <platform/openfirmware/openfirmware.h>
#include <real_time_clock.h>
#include <util/kernel_cpp.h>
@@ -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 -
@@ -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 <bonefish@cs.tu-berlin.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include <arch/real_time_clock.h>
#include <real_time_data.h>
#include <arch_platform.h>
#include <real_time_data.h>
#include <smp.h>
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;
}
+2 -1
View File
@@ -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)
;
+2 -1
View File
@@ -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
@@ -0,0 +1,38 @@
/*
* Copyright 2006, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include <OS.h>
#include <arch_cpu.h>
#include <libroot_private.h>
#include <real_time_data.h>
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;
}
+23 -4
View File
@@ -10,14 +10,33 @@
#include <real_time_data.h>
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;
}
+1 -1
View File
@@ -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
@@ -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.
*/