#include "kernel/kernel.h" #include "kernel/clock.h" #include #include #include #include #include #include #include #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 high_frc; struct omap_timer_registers; struct omap_timer { vir_bytes base; int irq_nr; struct omap_timer_registers *regs; }; struct omap_timer_registers { vir_bytes TIDR; vir_bytes TIOCP_CFG; vir_bytes TISTAT; vir_bytes TISR; vir_bytes TIER; vir_bytes TWER; vir_bytes TCLR; vir_bytes TCRR; vir_bytes TLDR; vir_bytes TTGR; vir_bytes TWPS; vir_bytes TMAR; vir_bytes TCAR1; vir_bytes TSICR; vir_bytes TCAR2; vir_bytes TPIR; vir_bytes TNIR; vir_bytes TCVR; vir_bytes TOCR; vir_bytes TOWR; }; static struct omap_timer_registers regs_v1 = { .TIDR = OMAP3_TIMER_TIDR, .TIOCP_CFG = OMAP3_TIMER_TIOCP_CFG, .TISTAT = OMAP3_TIMER_TISTAT, .TISR = OMAP3_TIMER_TISR, .TIER = OMAP3_TIMER_TIER, .TWER = OMAP3_TIMER_TWER, .TCLR = OMAP3_TIMER_TCLR, .TCRR = OMAP3_TIMER_TCRR, .TLDR = OMAP3_TIMER_TLDR, .TTGR = OMAP3_TIMER_TTGR, .TWPS = OMAP3_TIMER_TWPS, .TMAR = OMAP3_TIMER_TMAR, .TCAR1 = OMAP3_TIMER_TCAR1, .TSICR = OMAP3_TIMER_TSICR, .TCAR2 = OMAP3_TIMER_TCAR2, .TPIR = OMAP3_TIMER_TPIR, .TNIR = OMAP3_TIMER_TNIR, .TCVR = OMAP3_TIMER_TCVR, .TOCR = OMAP3_TIMER_TOCR, .TOWR = OMAP3_TIMER_TOWR, }; #ifdef DM37XX static struct omap_timer timer = { .base = OMAP3_GPTIMER1_BASE, .irq_nr = OMAP3_GPT1_IRQ, .regs = ®s_v1 }; /* free running timer */ static struct omap_timer fr_timer = { .base = OMAP3_GPTIMER10_BASE, .irq_nr = OMAP3_GPT10_IRQ, .regs = ®s_v1 }; #endif #ifdef AM335X /* AM335X has a different ip block for the non 1ms timers */ static struct omap_timer_registers regs_v2 = { .TIDR = AM335X_TIMER_TIDR, .TIOCP_CFG = AM335X_TIMER_TIOCP_CFG, .TISTAT = AM335X_TIMER_IRQSTATUS_RAW, .TISR = AM335X_TIMER_IRQSTATUS, .TIER = AM335X_TIMER_IRQENABLE_SET, .TWER = AM335X_TIMER_IRQWAKEEN, .TCLR = AM335X_TIMER_TCLR, .TCRR = AM335X_TIMER_TCRR, .TLDR = AM335X_TIMER_TLDR, .TTGR = AM335X_TIMER_TTGR, .TWPS = AM335X_TIMER_TWPS, .TMAR = AM335X_TIMER_TMAR, .TCAR1 = AM335X_TIMER_TCAR1, .TSICR = AM335X_TIMER_TSICR, .TCAR2 = AM335X_TIMER_TCAR2, .TPIR = -1 , /* UNDEF */ .TNIR = -1 , /* UNDEF */ .TCVR = -1 , /* UNDEF */ .TOCR = -1 , /* UNDEF */ .TOWR = -1 /* UNDEF */ }; /* normal timer */ static struct omap_timer timer = { .base = AM335X_DMTIMER1_1MS_BASE, .irq_nr = AM335X_INT_TINT1_1MS, .regs = ®s_v1 }; /* free running timer */ static struct omap_timer fr_timer = { .base = AM335X_DMTIMER7_BASE, .irq_nr = AM335X_INT_TINT7, .regs = ®s_v2 }; #endif static int done = 0; 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 = timer.irq_nr; put_irq_handler(&omap3_timer_hook, timer.irq_nr, handler); /* only unmask interrupts after registering */ omap3_irq_unmask(timer.irq_nr); return 0; } /* meta data for remapping */ static kern_phys_map timer_phys_map; static kern_phys_map fr_timer_phys_map; void omap3_frclock_init(void) { u32_t tisr; kern_phys_map_ptr(fr_timer.base,ARM_PAGE_SIZE, &fr_timer_phys_map, (vir_bytes) &fr_timer.base); /* enable the clock */ #ifdef AM335X /* Disable the module and wait for the module to be disabled */ set32(CM_PER_TIMER7_CLKCTRL, CM_MODULEMODE_MASK,CM_MODULEMODE_DISABLED); while( (mmio_read(CM_PER_TIMER7_CLKCTRL) & CM_CLKCTRL_IDLEST) != CM_CLKCTRL_IDLEST_DISABLE); set32(CLKSEL_TIMER7_CLK,CLKSEL_TIMER7_CLK_SEL_MASK, CLKSEL_TIMER7_CLK_SEL_SEL2); while( (read32(CLKSEL_TIMER7_CLK) & CLKSEL_TIMER7_CLK_SEL_MASK) != CLKSEL_TIMER7_CLK_SEL_SEL2); /* enable the module and wait for the module to be ready */ set32(CM_PER_TIMER7_CLKCTRL,CM_MODULEMODE_MASK,CM_MODULEMODE_ENABLE); while( (mmio_read(CM_PER_TIMER7_CLKCTRL) & CM_CLKCTRL_IDLEST) != CM_CLKCTRL_IDLEST_FUNC); #endif /* Stop timer */ mmio_clear(fr_timer.base + fr_timer.regs->TCLR, OMAP3_TCLR_ST); #ifdef DM37XX /* Use functional clock source for GPTIMER10 */ mmio_set(OMAP3_CM_CLKSEL_CORE, OMAP3_CLKSEL_GPT10); #endif #ifdef DM37XX /* 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(fr_timer.base + fr_timer.regs->TCLR, (2 << OMAP3_TCLR_PTV)); #endif #ifdef AM335X /* 24Mhz / 16 = 1.5 Mhz */ mmio_set(fr_timer.base + fr_timer.regs->TCLR, (3 << OMAP3_TCLR_PTV)); #endif /* Start and auto-reload at 0 */ mmio_write(fr_timer.base + fr_timer.regs->TLDR, 0x0); mmio_write(fr_timer.base + fr_timer.regs->TCRR, 0x0); /* Set up overflow interrupt */ tisr = OMAP3_TISR_MAT_IT_FLAG | OMAP3_TISR_OVF_IT_FLAG | OMAP3_TISR_TCAR_IT_FLAG; mmio_write(fr_timer.base + fr_timer.regs->TISR, tisr); /* Clear interrupt status */ mmio_write(fr_timer.base + fr_timer.regs->TIER, OMAP3_TIER_OVF_IT_ENA); /* Start timer */ mmio_set(fr_timer.base + fr_timer.regs->TCLR, OMAP3_TCLR_OVF_TRG|OMAP3_TCLR_AR|OMAP3_TCLR_ST|OMAP3_TCLR_PRE); done = 1; } void omap3_frclock_stop() { mmio_clear(fr_timer.base + fr_timer.regs->TCLR, OMAP3_TCLR_ST); } void omap3_timer_init(unsigned freq) { /* we only support 1ms resolution */ u32_t tisr; kern_phys_map_ptr(timer.base,ARM_PAGE_SIZE, &timer_phys_map, (vir_bytes) &timer.base); #ifdef AM335X /* disable the module and wait for the module to be disabled */ set32(CM_WKUP_TIMER1_CLKCTRL, CM_MODULEMODE_MASK,CM_MODULEMODE_DISABLED); while( (mmio_read(CM_WKUP_TIMER1_CLKCTRL) & CM_CLKCTRL_IDLEST) != CM_CLKCTRL_IDLEST_DISABLE); set32(CLKSEL_TIMER1MS_CLK,CLKSEL_TIMER1MS_CLK_SEL_MASK, CLKSEL_TIMER1MS_CLK_SEL_SEL2); while( (read32(CLKSEL_TIMER1MS_CLK) & CLKSEL_TIMER1MS_CLK_SEL_MASK) != CLKSEL_TIMER1MS_CLK_SEL_SEL2); /* enable the module and wait for the module to be ready */ set32(CM_WKUP_TIMER1_CLKCTRL,CM_MODULEMODE_MASK,CM_MODULEMODE_ENABLE); while( (mmio_read(CM_WKUP_TIMER1_CLKCTRL) & CM_CLKCTRL_IDLEST) != CM_CLKCTRL_IDLEST_FUNC); #endif /* Stop timer */ mmio_clear(timer.base + fr_timer.regs->TCLR, OMAP3_TCLR_ST); #ifdef DM37XX /* Use 32 KHz clock source for GPTIMER1 */ mmio_clear(OMAP3_CM_CLKSEL_WKUP, OMAP3_CLKSEL_GPT1); #endif /* Use 1-ms tick mode for GPTIMER1 TRM 16.2.4.2.1 */ mmio_write(timer.base + timer.regs->TPIR, 232000); mmio_write(timer.base + timer.regs->TNIR, -768000); mmio_write(timer.base + timer.regs->TLDR, 0xffffffff - (32768 / freq) +1); mmio_write(timer.base + timer.regs->TCRR, 0xffffffff - (32768 / freq) +1); /* Set up overflow interrupt */ tisr = OMAP3_TISR_MAT_IT_FLAG | OMAP3_TISR_OVF_IT_FLAG | OMAP3_TISR_TCAR_IT_FLAG; mmio_write(timer.base + timer.regs->TISR, tisr); /* Clear interrupt status */ mmio_write(timer.base + timer.regs->TIER, OMAP3_TIER_OVF_IT_ENA); /* Start timer */ mmio_set(timer.base + timer.regs->TCLR, OMAP3_TCLR_OVF_TRG|OMAP3_TCLR_AR|OMAP3_TCLR_ST); } void omap3_timer_stop() { mmio_clear(timer.base + timer.regs->TCLR, OMAP3_TCLR_ST); } static u32_t read_frc(void) { if (done == 0) return 0; return mmio_read(fr_timer.base + fr_timer.regs->TCRR); } static void frc_overflow_check(void) { static int prev_frc_valid; static u32_t prev_frc; u32_t cur_frc = read_frc(); if(prev_frc_valid && prev_frc > cur_frc) high_frc++; prev_frc = cur_frc; prev_frc_valid = 1; } void omap3_timer_int_handler() { /* Clear all interrupts */ u32_t tisr; /* when the kernel itself is running interrupts are disabled. * We should therefore also read the overflow counter to detect * this as to not miss events. */ tisr = OMAP3_TISR_MAT_IT_FLAG | OMAP3_TISR_OVF_IT_FLAG | OMAP3_TISR_TCAR_IT_FLAG; mmio_write(timer.base + timer.regs->TISR, tisr); frc_overflow_check(); } /* Use the free running clock as TSC */ void read_tsc_64(u64_t *t) { u32_t now; frc_overflow_check(); now = read_frc(); *t = (u64_t) now + (high_frc << 32); }