ARM: provide free running clock to replace ccnt

The Cycle CouNTer on ARM cannot be used reliably as it wraps around
rather quickly and can be altered by user space (on Minix). Furthermore,
it's buggy when wrapping and is not implemented at all on the Linaro
Beagleboard emulator.

This patch programs GPTIMER10 as a free running clock at 1.625 MHz (it
doesn't generate interrupts). It's memory mapped into every process,
which enables libsys to provide micro_delay().

Change-Id: Iba004c6c62976762fe154ea390d69e518eec1531
This commit is contained in:
Thomas Veerman 2013-01-29 20:58:00 +01:00
parent e3e5cf6d34
commit db8c1ee9d0
12 changed files with 381 additions and 103 deletions

View file

@ -61,6 +61,12 @@ u32_t tsc_64_to_micros(u64_t tsc);
u32_t tsc_to_micros(u32_t low, u32_t high);
u32_t tsc_get_khz(void);
u32_t micros_to_ticks(u32_t micros);
#if defined(__arm__)
u32_t read_frclock(void);
u32_t delta_frclock(u32_t base, u32_t cur);
u64_t read_frclock_64(void);
u64_t delta_frclock_64(u64_t base, u64_t cur);
#endif
void ser_putc(char c);
void get_randomness(struct k_randomness *, int);
u32_t sqrt_approx(u32_t);

View file

@ -168,7 +168,7 @@ struct minix_kerninfo {
u32_t kerninfo_magic;
u32_t minix_feature_flags; /* features in minix kernel */
u32_t ki_flags; /* what is present in this struct */
u32_t flags_unused2;
u32_t minix_frclock;
u32_t flags_unused3;
u32_t flags_unused4;
struct kinfo *kinfo;

View file

@ -25,6 +25,7 @@ static unsigned tsc_per_ms[CONFIG_MAX_CPUS];
int init_local_timer(unsigned freq)
{
omap3_timer_init(freq);
omap3_frclock_init();
/* always only 1 cpu in the system */
tsc_per_ms[0] = 1;

View file

@ -18,6 +18,7 @@
#include "arch_proto.h"
#include "kernel/proto.h"
#include "kernel/debug.h"
#include "omap_timer.h"
phys_bytes device_mem_vaddr = 0;
@ -667,6 +668,7 @@ void arch_proc_init(struct proc *pr, const u32_t ip, const u32_t sp, char *name)
}
static int device_mem_mapping_index = -1,
frclock_index = -1,
usermapped_glo_index = -1,
usermapped_index = -1, first_um_idx = -1;
@ -685,7 +687,9 @@ int arch_phys_map(const int index,
(u32_t) &usermapped_start;
if(first) {
memset(&minix_kerninfo, 0, sizeof(minix_kerninfo));
device_mem_mapping_index = freeidx++;
frclock_index = freeidx++;
if(glo_len > 0) {
usermapped_glo_index = freeidx++;
}
@ -717,6 +721,12 @@ int arch_phys_map(const int index,
*flags = VMMF_UNCACHED | VMMF_WRITE;
return OK;
}
else if (index == frclock_index) {
*addr = OMAP3_GPTIMER10_BASE;
*len = ARM_PAGE_SIZE;
*flags = VMMF_USER;
return OK;
}
return EINVAL;
}
@ -727,7 +737,6 @@ int arch_phys_map_reply(const int index, const vir_bytes addr)
u32_t usermapped_offset;
assert(addr > (u32_t) &usermapped_start);
usermapped_offset = addr - (u32_t) &usermapped_start;
memset(&minix_kerninfo, 0, sizeof(minix_kerninfo));
#define FIXEDPTR(ptr) (void *) ((u32_t)ptr + usermapped_offset)
#define FIXPTR(ptr) ptr = FIXEDPTR(ptr)
#define ASSIGN(minixstruct) minix_kerninfo.minixstruct = FIXEDPTR(&minixstruct)
@ -746,10 +755,15 @@ int arch_phys_map_reply(const int index, const vir_bytes addr)
return OK;
}
if(index == usermapped_index) return OK;
if (index == device_mem_mapping_index) {
device_mem_vaddr = addr;
if (index == usermapped_index) {
return OK;
}
else if (index == device_mem_mapping_index) {
device_mem_vaddr = addr;
return OK;
}
else if (index == frclock_index) {
minix_kerninfo.minix_frclock = addr;
return OK;
}

View file

@ -21,6 +21,42 @@ int omap3_register_timer_handler(const irq_handler_t handler)
return 0;
}
void omap3_frclock_init(void)
{
u32_t tisr;
/* Stop timer */
mmio_clear(OMAP3_GPTIMER10_TCLR, OMAP3_TCLR_ST);
/* Use functional clock source for GPTIMER10 */
mmio_set(OMAP3_CM_CLKSEL_CORE, OMAP3_CLKSEL_GPT10);
/* Scale timer down to 13/8 = 1.625 Mhz to roughly get microsecond ticks */
/* The scale is computed as 2^(PTV+1). So if PTV == 2, we get 2^3 = 8.
*/
mmio_set(OMAP3_GPTIMER10_TCLR, (2 << OMAP3_TCLR_PTV));
/* Start and auto-reload at 0 */
mmio_write(OMAP3_GPTIMER10_TLDR, 0x0);
mmio_write(OMAP3_GPTIMER10_TCRR, 0x0);
/* Set up overflow interrupt */
tisr = OMAP3_TISR_MAT_IT_FLAG | OMAP3_TISR_OVF_IT_FLAG |
OMAP3_TISR_TCAR_IT_FLAG;
mmio_write(OMAP3_GPTIMER10_TISR, tisr); /* Clear interrupt status */
mmio_write(OMAP3_GPTIMER10_TIER, OMAP3_TIER_OVF_IT_ENA);
/* Start timer */
mmio_set(OMAP3_GPTIMER10_TCLR,
OMAP3_TCLR_OVF_TRG|OMAP3_TCLR_AR|OMAP3_TCLR_ST|OMAP3_TCLR_PRE);
}
void omap3_frclock_stop()
{
mmio_clear(OMAP3_GPTIMER10_TCLR, OMAP3_TCLR_ST);
}
void omap3_timer_init(unsigned freq)
{
u32_t tisr;
@ -63,6 +99,7 @@ void omap3_timer_int_handler()
OMAP3_TISR_TCAR_IT_FLAG;
mmio_write(OMAP3_GPTIMER1_TISR, tisr);
tsc++;
}
/* Don't use libminlib's read_tsc_64, but our own version instead. We emulate

View file

@ -1,92 +1,14 @@
#ifndef _OMAP_TIMER_H
#define _OMAP_TIMER_H
/* General-purpose timer register map */
#define OMAP3_GPTIMER1_BASE 0x48318000 /* GPTIMER1 physical address */
#define OMAP3_GPTIMER2_BASE 0x49032000 /* GPTIMER2 physical address */
#define OMAP3_GPTIMER3_BASE 0x49034000 /* GPTIMER3 physical address */
#define OMAP3_GPTIMER4_BASE 0x49036000 /* GPTIMER4 physical address */
#define OMAP3_GPTIMER5_BASE 0x49038000 /* GPTIMER5 physical address */
#define OMAP3_GPTIMER6_BASE 0x4903A000 /* GPTIMER6 physical address */
#define OMAP3_GPTIMER7_BASE 0x4903C000 /* GPTIMER7 physical address */
#define OMAP3_GPTIMER8_BASE 0x4903E000 /* GPTIMER8 physical address */
#define OMAP3_GPTIMER9_BASE 0x49040000 /* GPTIMER9 physical address */
#define OMAP3_GPTIMER10_BASE 0x48086000 /* GPTIMER10 physical address */
#define OMAP3_GPTIMER11_BASE 0x48088000 /* GPTIMER11 physical address */
/* General-purpose timer registers */
#define OMAP3_TIDR 0x000 /* IP revision code */
#define OMAP3_TIOCP_CFG 0x010 /* Controls params for GP timer L4 interface */
#define OMAP3_TISTAT 0x014 /* Status (excl. interrupt status) */
#define OMAP3_TISR 0x018 /* Pending interrupt status */
#define OMAP3_TIER 0x01C /* Interrupt enable */
#define OMAP3_TWER 0x020 /* Wakeup enable */
#define OMAP3_TCLR 0x024 /* Controls optional features */
#define OMAP3_TCRR 0x028 /* Internal counter value */
#define OMAP3_TLDR 0x02C /* Timer load value */
#define OMAP3_TTGR 0x030 /* Triggers counter reload */
#define OMAP3_TWPS 0x034 /* Indicates if Write-Posted pending */
#define OMAP3_TMAR 0x038 /* Value to be compared with counter */
#define OMAP3_TCAR1 0x03C /* First captured value of counter register */
#define OMAP3_TSICR 0x040 /* Control posted mode and functional SW reset */
#define OMAP3_TCAR2 0x044 /* Second captured value of counter register */
#define OMAP3_TPIR 0x048 /* Positive increment (1 ms tick) */
#define OMAP3_TNIR 0x04C /* Negative increment (1 ms tick) */
#define OMAP3_TCVR 0x050 /* Defines TCRR is sub/over-period (1 ms tick) */
#define OMAP3_TOCR 0x054 /* Masks tick interrupt */
#define OMAP3_TOWR 0x058 /* Number of masked overflow interrupts */
/* Interrupt status register fields */
#define OMAP3_TISR_MAT_IT_FLAG (1 << 0) /* Pending match interrupt status */
#define OMAP3_TISR_OVF_IT_FLAG (1 << 1) /* Pending overflow interrupt status */
#define OMAP3_TISR_TCAR_IT_FLAG (1 << 2) /* Pending capture interrupt status */
/* Interrupt enable register fields */
#define OMAP3_TIER_MAT_IT_ENA (1 << 0) /* Enable match interrupt */
#define OMAP3_TIER_OVF_IT_ENA (1 << 1) /* Enable overflow interrupt */
#define OMAP3_TIER_TCAR_IT_ENA (1 << 2) /* Enable capture interrupt */
/* Timer control fields */
#define OMAP3_TCLR_ST (1 << 0) /* Start/stop timer */
#define OMAP3_TCLR_AR (1 << 1) /* Autoreload or one-shot mode */
#define OMAP3_TCLR_OVF_TRG (1 << 10) /* Overflow trigger */
#define OMAP3_GPTIMER1_TIDR (OMAP3_GPTIMER1_BASE + OMAP3_TIDR)
#define OMAP3_GPTIMER1_TIOCP_CFG (OMAP3_GPTIMER1_BASE + OMAP3_TIOCP_CFG)
#define OMAP3_GPTIMER1_TISTAT (OMAP3_GPTIMER1_BASE + OMAP3_TISTAT)
#define OMAP3_GPTIMER1_TISR (OMAP3_GPTIMER1_BASE + OMAP3_TISR)
#define OMAP3_GPTIMER1_TIER (OMAP3_GPTIMER1_BASE + OMAP3_TIER)
#define OMAP3_GPTIMER1_TWER (OMAP3_GPTIMER1_BASE + OMAP3_TWER)
#define OMAP3_GPTIMER1_TCLR (OMAP3_GPTIMER1_BASE + OMAP3_TCLR)
#define OMAP3_GPTIMER1_TCRR (OMAP3_GPTIMER1_BASE + OMAP3_TCRR)
#define OMAP3_GPTIMER1_TLDR (OMAP3_GPTIMER1_BASE + OMAP3_TLDR)
#define OMAP3_GPTIMER1_TTGR (OMAP3_GPTIMER1_BASE + OMAP3_TTGR)
#define OMAP3_GPTIMER1_TWPS (OMAP3_GPTIMER1_BASE + OMAP3_TWPS)
#define OMAP3_GPTIMER1_TMAR (OMAP3_GPTIMER1_BASE + OMAP3_TMAR)
#define OMAP3_GPTIMER1_TCAR1 (OMAP3_GPTIMER1_BASE + OMAP3_TCAR1)
#define OMAP3_GPTIMER1_TSICR (OMAP3_GPTIMER1_BASE + OMAP3_TSICR)
#define OMAP3_GPTIMER1_TCAR2 (OMAP3_GPTIMER1_BASE + OMAP3_TCAR2)
#define OMAP3_GPTIMER1_TPIR (OMAP3_GPTIMER1_BASE + OMAP3_TPIR)
#define OMAP3_GPTIMER1_TNIR (OMAP3_GPTIMER1_BASE + OMAP3_TNIR)
#define OMAP3_GPTIMER1_TCVR (OMAP3_GPTIMER1_BASE + OMAP3_TCVR)
#define OMAP3_GPTIMER1_TOCR (OMAP3_GPTIMER1_BASE + OMAP3_TOCR)
#define OMAP3_GPTIMER1_TOWR (OMAP3_GPTIMER1_BASE + OMAP3_TOWR)
#define OMAP3_CM_CLKSEL_WKUP 0x48004c40 /* source clock selection */
#define OMAP3_CLKSEL_GPT1 (1 << 0) /* Selects GPTIMER 1 source
* clock:
*
* 0: use 32KHz clock
* 1: sys clock)
*/
#define TIMER_FREQ 1000 /* clock frequency for OMAP timer (1ms) */
#define TIMER_COUNT(freq) (TIMER_FREQ/(freq)) /* initial value for counter*/
#include "omap_timer_registers.h"
#ifndef __ASSEMBLY__
void omap3_timer_init(unsigned freq);
void omap3_timer_stop(void);
void omap3_frclock_init(void);
void omap3_frclock_stop(void);
int omap3_register_timer_handler(const irq_handler_t handler);
void omap3_timer_int_handler(void);

View file

@ -0,0 +1,119 @@
#ifndef _OMAP_TIMER_REGISTERS_H
#define _OMAP_TIMER_REGISTERS_H
/* General-purpose timer register map */
#define OMAP3_GPTIMER1_BASE 0x48318000 /* GPTIMER1 physical address */
#define OMAP3_GPTIMER2_BASE 0x49032000 /* GPTIMER2 physical address */
#define OMAP3_GPTIMER3_BASE 0x49034000 /* GPTIMER3 physical address */
#define OMAP3_GPTIMER4_BASE 0x49036000 /* GPTIMER4 physical address */
#define OMAP3_GPTIMER5_BASE 0x49038000 /* GPTIMER5 physical address */
#define OMAP3_GPTIMER6_BASE 0x4903A000 /* GPTIMER6 physical address */
#define OMAP3_GPTIMER7_BASE 0x4903C000 /* GPTIMER7 physical address */
#define OMAP3_GPTIMER8_BASE 0x4903E000 /* GPTIMER8 physical address */
#define OMAP3_GPTIMER9_BASE 0x49040000 /* GPTIMER9 physical address */
#define OMAP3_GPTIMER10_BASE 0x48086000 /* GPTIMER10 physical address */
#define OMAP3_GPTIMER11_BASE 0x48088000 /* GPTIMER11 physical address */
/* General-purpose timer registers */
#define OMAP3_TIDR 0x000 /* IP revision code */
#define OMAP3_TIOCP_CFG 0x010 /* Controls params for GP timer L4 interface */
#define OMAP3_TISTAT 0x014 /* Status (excl. interrupt status) */
#define OMAP3_TISR 0x018 /* Pending interrupt status */
#define OMAP3_TIER 0x01C /* Interrupt enable */
#define OMAP3_TWER 0x020 /* Wakeup enable */
#define OMAP3_TCLR 0x024 /* Controls optional features */
#define OMAP3_TCRR 0x028 /* Internal counter value */
#define OMAP3_TLDR 0x02C /* Timer load value */
#define OMAP3_TTGR 0x030 /* Triggers counter reload */
#define OMAP3_TWPS 0x034 /* Indicates if Write-Posted pending */
#define OMAP3_TMAR 0x038 /* Value to be compared with counter */
#define OMAP3_TCAR1 0x03C /* First captured value of counter register */
#define OMAP3_TSICR 0x040 /* Control posted mode and functional SW reset */
#define OMAP3_TCAR2 0x044 /* Second captured value of counter register */
#define OMAP3_TPIR 0x048 /* Positive increment (1 ms tick) */
#define OMAP3_TNIR 0x04C /* Negative increment (1 ms tick) */
#define OMAP3_TCVR 0x050 /* Defines TCRR is sub/over-period (1 ms tick) */
#define OMAP3_TOCR 0x054 /* Masks tick interrupt */
#define OMAP3_TOWR 0x058 /* Number of masked overflow interrupts */
/* Interrupt status register fields */
#define OMAP3_TISR_MAT_IT_FLAG (1 << 0) /* Pending match interrupt status */
#define OMAP3_TISR_OVF_IT_FLAG (1 << 1) /* Pending overflow interrupt status */
#define OMAP3_TISR_TCAR_IT_FLAG (1 << 2) /* Pending capture interrupt status */
/* Interrupt enable register fields */
#define OMAP3_TIER_MAT_IT_ENA (1 << 0) /* Enable match interrupt */
#define OMAP3_TIER_OVF_IT_ENA (1 << 1) /* Enable overflow interrupt */
#define OMAP3_TIER_TCAR_IT_ENA (1 << 2) /* Enable capture interrupt */
/* Timer control fields */
#define OMAP3_TCLR_ST (1 << 0) /* Start/stop timer */
#define OMAP3_TCLR_AR (1 << 1) /* Autoreload or one-shot mode */
#define OMAP3_TCLR_PRE (1 << 5) /* Prescaler on */
#define OMAP3_TCLR_PTV 2
#define OMAP3_TCLR_OVF_TRG (1 << 10) /* Overflow trigger */
#define OMAP3_GPTIMER1_TIDR (OMAP3_GPTIMER1_BASE + OMAP3_TIDR)
#define OMAP3_GPTIMER1_TIOCP_CFG (OMAP3_GPTIMER1_BASE + OMAP3_TIOCP_CFG)
#define OMAP3_GPTIMER1_TISTAT (OMAP3_GPTIMER1_BASE + OMAP3_TISTAT)
#define OMAP3_GPTIMER1_TISR (OMAP3_GPTIMER1_BASE + OMAP3_TISR)
#define OMAP3_GPTIMER1_TIER (OMAP3_GPTIMER1_BASE + OMAP3_TIER)
#define OMAP3_GPTIMER1_TWER (OMAP3_GPTIMER1_BASE + OMAP3_TWER)
#define OMAP3_GPTIMER1_TCLR (OMAP3_GPTIMER1_BASE + OMAP3_TCLR)
#define OMAP3_GPTIMER1_TCRR (OMAP3_GPTIMER1_BASE + OMAP3_TCRR)
#define OMAP3_GPTIMER1_TLDR (OMAP3_GPTIMER1_BASE + OMAP3_TLDR)
#define OMAP3_GPTIMER1_TTGR (OMAP3_GPTIMER1_BASE + OMAP3_TTGR)
#define OMAP3_GPTIMER1_TWPS (OMAP3_GPTIMER1_BASE + OMAP3_TWPS)
#define OMAP3_GPTIMER1_TMAR (OMAP3_GPTIMER1_BASE + OMAP3_TMAR)
#define OMAP3_GPTIMER1_TCAR1 (OMAP3_GPTIMER1_BASE + OMAP3_TCAR1)
#define OMAP3_GPTIMER1_TSICR (OMAP3_GPTIMER1_BASE + OMAP3_TSICR)
#define OMAP3_GPTIMER1_TCAR2 (OMAP3_GPTIMER1_BASE + OMAP3_TCAR2)
#define OMAP3_GPTIMER1_TPIR (OMAP3_GPTIMER1_BASE + OMAP3_TPIR)
#define OMAP3_GPTIMER1_TNIR (OMAP3_GPTIMER1_BASE + OMAP3_TNIR)
#define OMAP3_GPTIMER1_TCVR (OMAP3_GPTIMER1_BASE + OMAP3_TCVR)
#define OMAP3_GPTIMER1_TOCR (OMAP3_GPTIMER1_BASE + OMAP3_TOCR)
#define OMAP3_GPTIMER1_TOWR (OMAP3_GPTIMER1_BASE + OMAP3_TOWR)
#define OMAP3_GPTIMER10_TIDR (OMAP3_GPTIMER10_BASE + OMAP3_TIDR)
#define OMAP3_GPTIMER10_TIOCP_CFG (OMAP3_GPTIMER10_BASE + OMAP3_TIOCP_CFG)
#define OMAP3_GPTIMER10_TISTAT (OMAP3_GPTIMER10_BASE + OMAP3_TISTAT)
#define OMAP3_GPTIMER10_TISR (OMAP3_GPTIMER10_BASE + OMAP3_TISR)
#define OMAP3_GPTIMER10_TIER (OMAP3_GPTIMER10_BASE + OMAP3_TIER)
#define OMAP3_GPTIMER10_TWER (OMAP3_GPTIMER10_BASE + OMAP3_TWER)
#define OMAP3_GPTIMER10_TCLR (OMAP3_GPTIMER10_BASE + OMAP3_TCLR)
#define OMAP3_GPTIMER10_TCRR (OMAP3_GPTIMER10_BASE + OMAP3_TCRR)
#define OMAP3_GPTIMER10_TLDR (OMAP3_GPTIMER10_BASE + OMAP3_TLDR)
#define OMAP3_GPTIMER10_TTGR (OMAP3_GPTIMER10_BASE + OMAP3_TTGR)
#define OMAP3_GPTIMER10_TWPS (OMAP3_GPTIMER10_BASE + OMAP3_TWPS)
#define OMAP3_GPTIMER10_TMAR (OMAP3_GPTIMER10_BASE + OMAP3_TMAR)
#define OMAP3_GPTIMER10_TCAR1 (OMAP3_GPTIMER10_BASE + OMAP3_TCAR1)
#define OMAP3_GPTIMER10_TSICR (OMAP3_GPTIMER10_BASE + OMAP3_TSICR)
#define OMAP3_GPTIMER10_TCAR2 (OMAP3_GPTIMER10_BASE + OMAP3_TCAR2)
#define OMAP3_GPTIMER10_TPIR (OMAP3_GPTIMER10_BASE + OMAP3_TPIR)
#define OMAP3_GPTIMER10_TNIR (OMAP3_GPTIMER10_BASE + OMAP3_TNIR)
#define OMAP3_GPTIMER10_TCVR (OMAP3_GPTIMER10_BASE + OMAP3_TCVR)
#define OMAP3_GPTIMER10_TOCR (OMAP3_GPTIMER10_BASE + OMAP3_TOCR)
#define OMAP3_GPTIMER10_TOWR (OMAP3_GPTIMER10_BASE + OMAP3_TOWR)
#define OMAP3_CM_CLKSEL_GFX 0x48004b40
#define OMAP3_CM_CLKEN_PLL 0x48004d00
#define OMAP3_CM_FCLKEN1_CORE 0x48004A00
#define OMAP3_CM_CLKSEL_CORE 0x48004A40 /* GPT10 src clock sel. */
#define OMAP3_CM_FCLKEN_PER 0x48005000
#define OMAP3_CM_CLKSEL_PER 0x48005040
#define OMAP3_CM_CLKSEL_WKUP 0x48004c40 /* GPT1 source clock selection */
#define OMAP3_CLKSEL_GPT1 (1 << 0) /* Selects GPTIMER 1 source
* clock:
*
* 0: use 32KHz clock
* 1: sys clock)
*/
#define OMAP3_CLKSEL_GPT10 (1 << 6)
#define OMAP3_CLKSEL_GPT11 (1 << 7)
#define TIMER_FREQ 1000 /* clock frequency for OMAP timer (1ms) */
#define TIMER_COUNT(freq) (TIMER_FREQ/(freq)) /* initial value for counter*/
#endif /* _OMAP_TIMER_REGISTERS_H */

View file

@ -804,6 +804,7 @@ int arch_phys_map(const int index,
(u32_t) &usermapped_start;
if(first) {
memset(&minix_kerninfo, 0, sizeof(minix_kerninfo));
video_mem_mapping_index = freeidx++;
if(glo_len > 0) {
usermapped_glo_index = freeidx++;
@ -921,7 +922,6 @@ int arch_phys_map_reply(const int index, const vir_bytes addr)
extern u32_t usermapped_offset;
assert(addr > (u32_t) &usermapped_start);
usermapped_offset = addr - (u32_t) &usermapped_start;
memset(&minix_kerninfo, 0, sizeof(minix_kerninfo));
#define FIXEDPTR(ptr) (void *) ((u32_t)ptr + usermapped_offset)
#define FIXPTR(ptr) ptr = FIXEDPTR(ptr)
#define ASSIGN(minixstruct) minix_kerninfo.minixstruct = FIXEDPTR(&minixstruct)

View file

@ -5,8 +5,11 @@ HERE=${.CURDIR}/arch/${MACHINE_ARCH}
.PATH: ${HERE}
SRCS+= \
frclock_util.c \
spin.c \
tsc_util.c
CPPFLAGS+= -I${HERE}/../../
CPPFLAGS+= -I${HERE}/../../
CPPFLAGS+= -I${NETBSDSRCDIR} -I${NETBSDSRCDIR}/kernel/arch/${MACHINE_ARCH}/

View file

@ -0,0 +1,89 @@
/* Some utility functions around the free running clock on ARM. The clock is
* 32-bits wide, but we provide 64-bit wrapper functions to make it look
* similar to the read_tsc functions. On hardware we could actually make use
* of the timer overflow counter, but emulator doesn't emulate it. */
#include "omap_timer_registers.h"
#include <minix/minlib.h>
#include <minix/sysutil.h>
#include <minix/type.h>
#include <sys/errno.h>
#include <sys/types.h>
static u64_t calib_hz = 1625000, Hz;
#define MICROHZ 1000000ULL /* number of micros per second */
#define MICROSPERTICK(h) (MICROHZ/(h)) /* number of micros per HZ tick */
int
micro_delay(u32_t micros)
{
u64_t start, delta, delta_end;
Hz = sys_hz();
/* Start of delay. */
start = read_frclock_64();
delta_end = (calib_hz * micros) / MICROHZ;
/* If we have to wait for at least one HZ tick, use the regular
* tickdelay first. Round downwards on purpose, so the average
* half-tick we wait short (depending on where in the current tick
* we call tickdelay). We can correct for both overhead of tickdelay
* itself and the short wait in the busywait later.
*/
if (micros >= MICROSPERTICK(Hz))
tickdelay(micros*Hz/MICROHZ);
/* Wait (the rest) of the delay time using busywait. */
do {
delta = read_frclock_64();
} while (delta_frclock_64(start, delta) < delta_end);
return 0;
}
u32_t frclock_64_to_micros(u64_t tsc)
{
return (u32_t) tsc / calib_hz;
}
u32_t
read_frclock(void)
{
extern struct minix_kerninfo *_minix_kerninfo;
volatile u32_t *frclock;
frclock = (u32_t *)((u8_t *) _minix_kerninfo->minix_frclock+OMAP3_TCRR);
return (u64_t) *frclock;
}
u32_t
delta_frclock(u32_t base, u32_t cur)
{
u32_t delta;
if (cur < base) {
/* We have wrapped around, so delta is base to wrapping point
* plus starting point (0) to cur. This supports wrapping once
* only. */
delta = (UINT_MAX - base) + cur;
} else {
delta = cur - base;
}
return delta;
}
u64_t
read_frclock_64(void)
{
return (u64_t) read_frclock();
}
u64_t
delta_frclock_64(u64_t base, u64_t cur)
{
return delta_frclock((u32_t) base, (u32_t) cur);
}

View file

@ -0,0 +1,98 @@
/* Helper functions that allow driver writers to easily busy-wait (spin) for a
* condition to become satisfied within a certain maximum time span.
*/
/* This implementation first spins without making any system calls for a
* while, and then starts using system calls (specifically, the system call to
* obtain the current time) while spinning. The reason for this is that in
* many cases, the condition to be checked will become satisfied rather
* quickly, and we want to avoid getting descheduled in that case. However,
* after a while, running out of scheduling quantum will cause our priority to
* be lowered, and we can avoid this by voluntarily giving up the CPU, by
* making a system call.
*/
#include "sysutil.h"
#include <minix/spin.h>
#include <minix/minlib.h>
/* Number of microseconds to keep spinning initially, without performing a
* system call. We pick a value somewhat smaller than a typical clock tick.
* Note that for the above reasons, we want to avoid using sys_hz() here.
*/
#define TSC_SPIN 1000 /* in microseconds */
/* Internal spin states. */
enum {
STATE_INIT, /* simply check the condition (once) */
STATE_BASE_TS, /* get the initial TSC value (once) */
STATE_TS, /* use the TSC to spin (up to TSC_SPIN us) */
STATE_UPTIME /* use the clock to spin */
};
void spin_init(spin_t *s, u32_t usecs)
{
/* Initialize the given spin state structure, set to spin at most the
* given number of microseconds.
*/
s->s_state = STATE_INIT;
s->s_usecs = usecs;
s->s_timeout = FALSE;
}
int spin_check(spin_t *s)
{
/* Check whether a timeout has taken place. Return TRUE if the caller
* should continue spinning, and FALSE if a timeout has occurred. The
* implementation assumes that it is okay to spin a little bit too long
* (up to a full clock tick extra).
*/
u64_t cur_tsc, tsc_delta;
clock_t now, micro_delta;
switch (s->s_state) {
case STATE_INIT:
s->s_state = STATE_BASE_TS;
break;
case STATE_BASE_TS:
s->s_state = STATE_TS;
s->s_base_tsc = read_frclock_64();
break;
case STATE_TS:
cur_tsc = read_frclock_64();
tsc_delta = delta_frclock_64(s->s_base_tsc, cur_tsc);
micro_delta = frclock_64_to_micros(tsc_delta);
if (micro_delta >= s->s_usecs) {
s->s_timeout = TRUE;
return FALSE;
}
if (micro_delta >= TSC_SPIN) {
s->s_usecs -= micro_delta;
getuptime(&s->s_base_uptime);
s->s_state = STATE_UPTIME;
}
break;
case STATE_UPTIME:
getuptime(&now);
/* We assume that sys_hz() caches its return value. */
micro_delta = ((now - s->s_base_uptime) * 1000 / sys_hz()) *
1000;
if (micro_delta >= s->s_usecs) {
s->s_timeout = TRUE;
return FALSE;
}
break;
default:
panic("spin_check: invalid state %d", s->s_state);
}
return TRUE;
}

View file

@ -18,25 +18,14 @@
#define MICROHZ 1000000 /* number of micros per second */
#define MICROSPERTICK(h) (MICROHZ/(h)) /* number of micros per HZ tick */
static u32_t calib_mhz = 1000, Hz = 1000;
int
micro_delay(u32_t micros)
{
return OK;
}
static u32_t calib_hz = 600000000;
u32_t tsc_64_to_micros(u64_t tsc)
{
u64_t tmp;
tmp = div64u64(tsc, calib_mhz);
if (ex64hi(tmp)) {
printf("tsc_64_to_micros: more than 2^32ms\n");
return ~0UL;
} else {
return ex64lo(tmp);
}
tmp = tsc / calib_hz;
return (u32_t) tmp;
}
u32_t tsc_to_micros(u32_t low, u32_t high)