/** * @file * * @ingroup arm_beagle * * @brief Clock driver configuration. */ /* * Copyright (c) 2014 Ben Gras . * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.org/license/LICENSE. */ #include #include #include #include static struct timecounter beagle_clock_tc; static omap_timer_registers_t 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, }; #if IS_AM335X /* AM335X has a different ip block for the non 1ms timers */ static omap_timer_registers_t 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 */ }; #endif /* which timers are in use? target-dependent. * initialize at compile time. */ #if IS_DM3730 static omap_timer_t dm37xx_timer = { .base = OMAP3_GPTIMER1_BASE, .irq_nr = OMAP3_GPT1_IRQ, .regs = ®s_v1 }; /* free running timer */ static omap_timer_t dm37xx_fr_timer = { .base = OMAP3_GPTIMER10_BASE, .irq_nr = OMAP3_GPT10_IRQ, .regs = ®s_v1 }; static struct omap_timer *fr_timer = &dm37xx_fr_timer; static struct omap_timer *timer = &dm37xx_timer; #endif #if IS_AM335X /* normal timer */ static omap_timer_t am335x_timer = { .base = AM335X_DMTIMER1_1MS_BASE, .irq_nr = AM335X_INT_TINT1_1MS, .regs = ®s_v1 }; /* free running timer */ static omap_timer_t am335x_fr_timer = { .base = AM335X_DMTIMER7_BASE, .irq_nr = AM335X_INT_TINT7, .regs = ®s_v2 }; static struct omap_timer *fr_timer = &am335x_fr_timer; static struct omap_timer *timer = &am335x_timer; #endif #if IS_AM335X #define FRCLOCK_HZ (16*1500000) #endif #if IS_DM3730 #define FRCLOCK_HZ (8*1625000) #endif #ifndef FRCLOCK_HZ #error expected IS_AM335X or IS_DM3730 to be defined. #endif static void omap3_frclock_init(void) { uint32_t tisr; #if IS_DM3730 /* 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); #endif #if IS_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); /* Stop timer */ mmio_clear(fr_timer->base + fr_timer->regs->TCLR, OMAP3_TCLR_ST); #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; /* 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, without prescaler */ mmio_set(fr_timer->base + fr_timer->regs->TCLR, OMAP3_TCLR_OVF_TRG | OMAP3_TCLR_AR | OMAP3_TCLR_ST); } static uint32_t beagle_clock_get_timecount(struct timecounter *tc) { return mmio_read(fr_timer->base + fr_timer->regs->TCRR); } static void beagle_clock_initialize(void) { uint32_t freq = 1000000UL/rtems_configuration_get_microseconds_per_tick(); /* we only support 1ms resolution */ uint32_t tisr; #if IS_DM3730 /* 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); #endif #if IS_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); /* Stop timer */ mmio_clear(timer->base + timer->regs->TCLR, OMAP3_TCLR_ST); #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; /* 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(); #if IS_AM335X /* Disable AM335X watchdog */ mmio_write(AM335X_WDT_BASE+AM335X_WDT_WSPR, 0xAAAA); while(mmio_read(AM335X_WDT_BASE+AM335X_WDT_WWPS) != 0) ; mmio_write(AM335X_WDT_BASE+AM335X_WDT_WSPR, 0x5555); while(mmio_read(AM335X_WDT_BASE+AM335X_WDT_WWPS) != 0) ; #endif /* Install timecounter */ \ beagle_clock_tc.tc_get_timecount = beagle_clock_get_timecount; beagle_clock_tc.tc_counter_mask = 0xffffffff; beagle_clock_tc.tc_frequency = FRCLOCK_HZ; beagle_clock_tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER; rtems_timecounter_install(&beagle_clock_tc); } static void beagle_clock_at_tick(void) { mmio_write(timer->base + timer->regs->TISR, OMAP3_TISR_MAT_IT_FLAG | OMAP3_TISR_OVF_IT_FLAG | OMAP3_TISR_TCAR_IT_FLAG); } static rtems_interrupt_handler clock_isr = NULL; static void beagle_clock_handler_install(rtems_interrupt_handler isr) { rtems_status_code sc = RTEMS_SUCCESSFUL; sc = rtems_interrupt_handler_install( timer->irq_nr, "Clock", RTEMS_INTERRUPT_UNIQUE, isr, NULL ); if (sc != RTEMS_SUCCESSFUL) { rtems_fatal_error_occurred(0xdeadbeef); } clock_isr = isr; } static void beagle_clock_cleanup(void) { rtems_status_code sc = RTEMS_SUCCESSFUL; /* Disable timer */ mmio_clear(timer->base + timer->regs->TCLR, OMAP3_TCLR_ST); /* Remove interrupt handler */ sc = rtems_interrupt_handler_remove( timer->irq_nr, clock_isr, NULL ); if (sc != RTEMS_SUCCESSFUL) { rtems_fatal_error_occurred(0xdeadbeef); } clock_isr = NULL; /* stop frclock */ mmio_clear(fr_timer->base + fr_timer->regs->TCLR, OMAP3_TCLR_ST); } #define Clock_driver_support_at_tick() beagle_clock_at_tick() #define Clock_driver_support_initialize_hardware() beagle_clock_initialize() #define Clock_driver_support_install_isr(isr) \ beagle_clock_handler_install(isr) #define Clock_driver_support_shutdown_hardware() beagle_clock_cleanup() /* Include shared source clock driver code */ #include "../../shared/clockdrv_shell.h"