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:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
Reference in New Issue
Block a user