ARM: kernel: fix timer resolution and implement basic timekeeping.

The previously used method for programming the timer did not take
into account that our timespec is 64bit while the register we poke
it into is 32 bit. Since the PXA (SoC in Verdex target) has a limited
scale of resolution (us,ms,second) we dynamicly determine the one
that we can most closely match, and set that.

For f.ex. snooze to work however, we also need system_time to work.
The current implementation uses a system timer at microsecond
resolution to keep track of time.

Although the code is far from perfect, committing it now before
it gets lost, since I'm working on the infrastructure code
to properly factor out the SoC specific code out of the core
ARM architecture code (so the kernel can support more then
our poor old Verdex QEMU target ;))
This commit is contained in:
Ithamar R. Adema 2013-09-17 03:03:17 +02:00 committed by Rene Gollent
parent 4f4270c990
commit ba06f07660
2 changed files with 54 additions and 20 deletions

View File

@ -35,35 +35,63 @@
#define PXA_OSSR 0x05
#define PXA_OIER 0x07
#define PXA_OSCR4 0x10
#define PXA_OSCR5 0x11
#define PXA_OSMR4 0x20
#define PXA_OSMR5 0x21
#define PXA_OMCR4 0x30
#define PXA_OMCR5 0x31
#define PXA_RES_S (3 << 0)
#define PXA_RES_MS (1 << 1)
#define PXA_RES_US (1 << 2)
static area_id sPxaTimersArea;
static uint32 *sPxaTimersBase;
#define US2S(bt) ((bt) / 1000000ULL)
#define US2MS(bt) ((bt) / 1000ULL)
static area_id sPxaTimersArea = B_ERROR;
static uint32 *sPxaTimersBase = NULL;
static bigtime_t sSystemTime = 0;
static int32
pxa_timer_interrupt(void *data)
{
int32 ret = timer_interrupt();
sPxaTimersBase[PXA_OSSR] |= (1 << 4);
if (sPxaTimersBase[PXA_OSSR] & (1 << 4)) {
sPxaTimersBase[PXA_OSSR] |= (1 << 4);
return timer_interrupt();
}
return ret;
if (sPxaTimersBase[PXA_OSSR] & (1 << 5)) {
sPxaTimersBase[PXA_OSSR] |= (1 << 5);
sSystemTime += UINT_MAX + 1ULL;
}
return B_HANDLED_INTERRUPT;
}
void
arch_timer_set_hardware_timer(bigtime_t timeout)
{
TRACE(("arch_timer_set_hardware_timer(%lld)\n", timeout));
uint32 val = timeout & UINT_MAX;
uint32 res = PXA_RES_US;
if (sPxaTimersBase) {
sPxaTimersBase[PXA_OIER] |= (1 << 4);
sPxaTimersBase[PXA_OMCR4] = 4; // set to exactly single milisecond resolution
sPxaTimersBase[PXA_OSMR4] = timeout;
sPxaTimersBase[PXA_OSCR4] = 0; // start counting from 0 again
if (timeout & ~UINT_MAX) {
// Does not fit, so scale resolution down to milliseconds
if (US2MS(timeout) & ~UINT_MAX) {
// Still does not fit, scale down to seconds as last ditch attempt
val = US2S(timeout) & UINT_MAX;
res = PXA_RES_S;
} else {
// Fits in millisecond resolution
val = US2MS(timeout) & UINT_MAX;
res = PXA_RES_MS;
}
}
TRACE(("arch_timer_set_hardware_timer(val=%lu, res=%lu)\n", val, res));
sPxaTimersBase[PXA_OIER] |= (1 << 4);
sPxaTimersBase[PXA_OMCR4] = res;
sPxaTimersBase[PXA_OSMR4] = val;
sPxaTimersBase[PXA_OSCR4] = 0; // start counting from 0 again
}
@ -72,13 +100,10 @@ arch_timer_clear_hardware_timer()
{
TRACE(("arch_timer_clear_hardware_timer\n"));
if (sPxaTimersBase) {
sPxaTimersBase[PXA_OMCR4] = 0; // disable our timer
sPxaTimersBase[PXA_OIER] &= ~(4 << 1);
}
sPxaTimersBase[PXA_OMCR4] = 0; // disable our timer
sPxaTimersBase[PXA_OIER] &= ~(1 << 4);
}
int
arch_init_timer(kernel_args *args)
{
@ -88,9 +113,20 @@ arch_init_timer(kernel_args *args)
if (sPxaTimersArea < 0)
return sPxaTimersArea;
sPxaTimersBase[PXA_OMCR4] = 0; // disable our timer
sPxaTimersBase[PXA_OIER] |= (1 << 5); // enable timekeeping timer
sPxaTimersBase[PXA_OMCR5] = PXA_RES_US | (1 << 7);
sPxaTimersBase[PXA_OSMR5] = UINT_MAX;
sPxaTimersBase[PXA_OSCR5] = 0;
install_io_interrupt_handler(PXA_TIMERS_INTERRUPT, &pxa_timer_interrupt, NULL, 0);
return B_OK;
}
bigtime_t
system_time(void)
{
return (sPxaTimersBase != NULL) ?
sSystemTime + sPxaTimersBase[PXA_OSCR5] :
0ULL;
}

View File

@ -11,8 +11,6 @@ SEARCH_SOURCE += [ FDirName $(librootSources) os arch generic ] ;
KernelMergeObject kernel_os_arch_$(TARGET_ARCH).o :
atomic.S
byteorder.S
# system_time_asm.S
system_time.c
generic_system_time_nsecs.cpp