minix/kernel/arch/earm/omap_timer.c
Thomas Veerman db8c1ee9d0 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
2013-01-31 15:19:11 +00:00

112 lines
3 KiB
C

#include "kernel/kernel.h"
#include "kernel/clock.h"
#include <sys/types.h>
#include <machine/cpu.h>
#include <io.h>
#include "arch_proto.h"
#include "omap_timer.h"
#include "omap_intr.h"
static irq_hook_t omap3_timer_hook; /* interrupt handler hook */
static u64_t tsc;
int omap3_register_timer_handler(const irq_handler_t handler)
{
/* Initialize the CLOCK's interrupt hook. */
omap3_timer_hook.proc_nr_e = NONE;
omap3_timer_hook.irq = OMAP3_GPT1_IRQ;
put_irq_handler(&omap3_timer_hook, OMAP3_GPT1_IRQ, 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;
/* Stop timer */
mmio_clear(OMAP3_GPTIMER1_TCLR, OMAP3_TCLR_ST);
/* Use 32 KHz clock source for GPTIMER1 */
mmio_clear(OMAP3_CM_CLKSEL_WKUP, OMAP3_CLKSEL_GPT1);
/* Use 1-ms tick mode for GPTIMER1 TRM 16.2.4.2.1 */
mmio_write(OMAP3_GPTIMER1_TPIR, 232000);
mmio_write(OMAP3_GPTIMER1_TNIR, -768000);
mmio_write(OMAP3_GPTIMER1_TLDR, 0xffffffe0);
mmio_write(OMAP3_GPTIMER1_TCRR, 0xffffffe0);
/* Set up overflow interrupt */
tisr = OMAP3_TISR_MAT_IT_FLAG | OMAP3_TISR_OVF_IT_FLAG |
OMAP3_TISR_TCAR_IT_FLAG;
mmio_write(OMAP3_GPTIMER1_TISR, tisr); /* Clear interrupt status */
mmio_write(OMAP3_GPTIMER1_TIER, OMAP3_TIER_OVF_IT_ENA);
omap3_irq_unmask(OMAP3_GPT1_IRQ);
/* Start timer */
mmio_set(OMAP3_GPTIMER1_TCLR,
OMAP3_TCLR_OVF_TRG|OMAP3_TCLR_AR|OMAP3_TCLR_ST);
}
void omap3_timer_stop()
{
mmio_clear(OMAP3_GPTIMER1_TCLR, OMAP3_TCLR_ST);
}
void omap3_timer_int_handler()
{
/* Clear all interrupts */
u32_t tisr;
tisr = OMAP3_TISR_MAT_IT_FLAG | OMAP3_TISR_OVF_IT_FLAG |
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
* the ARM Cycle Counter (CCNT) with 1 cycle per ms. We can't rely on the
* actual counter hardware to be working (i.e., qemu doesn't emulate it at all)
*/
void read_tsc_64(u64_t *t)
{
*t = tsc;
}