#include "kernel/kernel.h" #include "kernel/clock.h" #include #include #include #include #include #include #include #include #include "arch_proto.h" #include "bsp_timer.h" #include "omap_timer_registers.h" #include "omap_intr_registers.h" #include "bsp_intr.h" /* interrupt handler hook */ static irq_hook_t omap3_timer_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, }; /* 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 */ }; static struct omap_timer dm37xx_timer = { .base = OMAP3_GPTIMER1_BASE, .irq_nr = OMAP3_GPT1_IRQ, .regs = ®s_v1 }; /* free running timer */ static struct omap_timer dm37xx_fr_timer = { .base = OMAP3_GPTIMER10_BASE, .irq_nr = OMAP3_GPT10_IRQ, .regs = ®s_v1 }; /* normal timer */ static struct omap_timer am335x_timer = { .base = AM335X_DMTIMER1_1MS_BASE, .irq_nr = AM335X_INT_TINT1_1MS, .regs = ®s_v1 }; /* free running timer */ static struct omap_timer am335x_fr_timer = { .base = AM335X_DMTIMER7_BASE, .irq_nr = AM335X_INT_TINT7, .regs = ®s_v2 }; static struct omap_timer *timer; static struct omap_timer *fr_timer; static int done = 0; int bsp_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 */ bsp_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; static kern_phys_map fr_timer_user_phys_map; /* struct for when the free */ /* running timer is mapped to */ /* userland */ /* callback for when the free running clock gets mapped */ int kern_phys_fr_user_mapped(vir_bytes id, phys_bytes address) { /* the only thing we need to do at this stage is to set the address */ /* in the kerninfo struct */ if (BOARD_IS_BBXM(machine.board_id)) { minix_kerninfo.minix_frclock_tcrr = address + OMAP3_TIMER_TCRR; minix_kerninfo.minix_arm_frclock_hz = 1625000; } else if (BOARD_IS_BB(machine.board_id)) { minix_kerninfo.minix_frclock_tcrr = address + AM335X_TIMER_TCRR; minix_kerninfo.minix_arm_frclock_hz = 1500000; } return 0; } void omap3_frclock_init(void) { u32_t tisr; /* enable the clock */ if (BOARD_IS_BBXM(machine.board_id)) { fr_timer = &dm37xx_fr_timer; kern_phys_map_ptr(fr_timer->base, ARM_PAGE_SIZE, VMMF_UNCACHED | VMMF_WRITE, &fr_timer_phys_map, (vir_bytes) & fr_timer->base); /* the timer is also mapped in user space hence the this */ /* second mapping and callback to set kerninfo frclock_tcrr */ kern_req_phys_map(fr_timer->base, ARM_PAGE_SIZE, VMMF_UNCACHED | VMMF_USER, &fr_timer_user_phys_map, kern_phys_fr_user_mapped, 0); /* Stop timer */ mmio_clear(fr_timer->base + fr_timer->regs->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(fr_timer->base + fr_timer->regs->TCLR, (2 << OMAP3_TCLR_PTV)); } else if (BOARD_IS_BB(machine.board_id)) { fr_timer = &am335x_fr_timer; kern_phys_map_ptr(fr_timer->base, ARM_PAGE_SIZE, VMMF_UNCACHED | VMMF_WRITE, &fr_timer_phys_map, (vir_bytes) & fr_timer->base); /* the timer is also mapped in user space hence the this */ /* second mapping and callback to set kerninfo frclock_tcrr */ kern_req_phys_map(fr_timer->base, ARM_PAGE_SIZE, VMMF_UNCACHED | VMMF_USER, &fr_timer_user_phys_map, kern_phys_fr_user_mapped, 0); /* 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); /* Stop timer */ mmio_clear(fr_timer->base + fr_timer->regs->TCLR, OMAP3_TCLR_ST); /* 24Mhz / 16 = 1.5 Mhz */ mmio_set(fr_timer->base + fr_timer->regs->TCLR, (3 << OMAP3_TCLR_PTV)); } /* 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; /* Clear interrupt status */ mmio_write(fr_timer->base + fr_timer->regs->TISR, tisr); 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 bsp_timer_init(unsigned freq) { /* we only support 1ms resolution */ u32_t tisr; if (BOARD_IS_BBXM(machine.board_id)) { timer = &dm37xx_timer; kern_phys_map_ptr(timer->base, ARM_PAGE_SIZE, VMMF_UNCACHED | VMMF_WRITE, &timer_phys_map, (vir_bytes) & timer->base); /* Stop timer */ mmio_clear(timer->base + timer->regs->TCLR, OMAP3_TCLR_ST); /* Use 32 KHz clock source for GPTIMER1 */ mmio_clear(OMAP3_CM_CLKSEL_WKUP, OMAP3_CLKSEL_GPT1); } else if (BOARD_IS_BB(machine.board_id)) { timer = &am335x_timer; kern_phys_map_ptr(timer->base, ARM_PAGE_SIZE, VMMF_UNCACHED | VMMF_WRITE, &timer_phys_map, (vir_bytes) & timer->base); /* 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); /* Stop timer */ mmio_clear(timer->base + timer->regs->TCLR, OMAP3_TCLR_ST); } /* 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; /* Clear interrupt status */ mmio_write(timer->base + timer->regs->TISR, tisr); 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); /* also initilize the free runnning timer */ omap3_frclock_init(); } void bsp_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); } /* * Check if the free running clock has overflown and * increase the high free running clock counter if * so. This method takes the current timer value as * parameter to ensure the overflow check is done * on the current timer value. * * To compose the current timer value (64 bits) you * need to follow the following sequence: * read the current timer value. * call the overflow check * compose the 64 bits time based on the current timer value * and high_frc. */ static void frc_overflow_check(u32_t cur_frc) { static int prev_frc_valid; static u32_t prev_frc; if (prev_frc_valid && prev_frc > cur_frc) { high_frc++; } prev_frc = cur_frc; prev_frc_valid = 1; } void bsp_timer_int_handler() { /* Clear all interrupts */ u32_t tisr, now; /* 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); now = read_frc(); frc_overflow_check(now); } /* Use the free running clock as TSC */ void read_tsc_64(u64_t * t) { u32_t now; now = read_frc(); frc_overflow_check(now); *t = (u64_t) now + (high_frc << 32); }