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-31 15:19:11 +00:00
parent e3e5cf6d34
commit db8c1ee9d0
12 changed files with 381 additions and 103 deletions
+1
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;
+19 -5
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;
}
+37
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
+3 -81
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);
+119
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 */
+1 -1
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)