/* * Clock Tick Device Driver using Timer Library implemented * by the GRLIB GPTIMER / LEON2 Timer drivers. * * COPYRIGHT (c) 2010. * Cobham Gaisler AB. * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.com/license/LICENSE. * */ #include #include #include #include #ifdef RTEMS_DRVMGR_STARTUP /* Undefine this to save space in standard LEON configurations, * it will assume that Prescaler is running at 1MHz. */ #undef CLOCK_DRIVER_DONT_ASSUME_PRESCALER_1MHZ /* Set the below defines from bsp.h if function needed. #undef CLOCK_DRIVER_ISRS_PER_TICK #undef CLOCK_DRIVER_USE_FAST_IDLE */ #define Clock_driver_support_at_tick() /* * Number of Clock ticks since initialization */ volatile uint32_t Clock_driver_ticks; /* * Timer Number in Timer Library. Defaults to the first Timer in * the System. */ int Clock_timer = 0; /* * Timer Handle in Timer Library */ void *Clock_handle = NULL; #ifdef CLOCK_DRIVER_DONT_ASSUME_PRESCALER_1MHZ unsigned int Clock_basefreq; #endif void Clock_exit(void); void Clock_isr(void *arg_unused); /* * Major and minor number. */ rtems_device_major_number rtems_clock_major = UINT32_MAX; rtems_device_minor_number rtems_clock_minor; /* * Clock_isr * * This is the clock tick interrupt handler. * * Input parameters: * vector - vector number * * Output parameters: NONE * * Return values: NONE * */ void Clock_isr(void *arg_unused) { /* * Support for shared interrupts. Ack IRQ at source, only handle * interrupts generated from the tick-timer. Clearing pending bit * is also needed for Clock_nanoseconds_since_last_tick() to work. */ if ( tlib_interrupt_pending(Clock_handle, 1) == 0 ) return; /* * Accurate count of ISRs */ Clock_driver_ticks += 1; #ifdef CLOCK_DRIVER_USE_FAST_IDLE do { rtems_clock_tick(); } while ( _Thread_Executing == _Thread_Idle && _Thread_Heir == _Thread_Executing); Clock_driver_support_at_tick(); return; #else /* * Add custom handling at every tick from bsp.h */ Clock_driver_support_at_tick(); #ifdef CLOCK_DRIVER_ISRS_PER_TICK /* * The driver is multiple ISRs per clock tick. */ if ( !Clock_driver_isrs ) { rtems_clock_tick(); Clock_driver_isrs = CLOCK_DRIVER_ISRS_PER_TICK; } Clock_driver_isrs--; #else /* * The driver is one ISR per clock tick. */ rtems_clock_tick(); #endif #endif } /* * Clock_exit * * This routine allows the clock driver to exit by masking the interrupt and * disabling the clock's counter. * * Input parameters: NONE * * Output parameters: NONE * * Return values: NONE * */ void Clock_exit( void ) { /* Stop all activity of the Timer, no more ISRs. * We could be using tlib_close(), however tlib_stop() is quicker * and independent of IRQ unregister code. */ if ( Clock_handle ) { tlib_stop(Clock_handle); Clock_handle = NULL; } } static uint32_t Clock_nanoseconds_since_last_tick(void) { uint32_t clicks; int ip; if ( !Clock_handle ) return 0; tlib_get_counter(Clock_handle, (unsigned int *)&clicks); /* protect against timer counter underflow/overflow */ ip = tlib_interrupt_pending(Clock_handle, 0); if (ip) tlib_get_counter(Clock_handle, (unsigned int *)&clicks); #ifdef CLOCK_DRIVER_DONT_ASSUME_PRESCALER_1MHZ { /* Down counter. Calc from BaseFreq. */ uint64_t tmp; if (ip) clicks += Clock_basefreq; tmp = ((uint64_t)clicks * 1000000000) / ((uint64_t)Clock_basefreq); return (uint32_t)tmp; } #else /* Down counter. Timer base frequency is initialized to 1 MHz */ return (uint32_t) ((rtems_configuration_get_microseconds_per_tick() << ip) - clicks) * 1000; #endif } /* * Clock_initialize * * This routine initializes the clock driver and starts the Clock. * * Input parameters: * major - clock device major number * minor - clock device minor number * parg - pointer to optional device driver arguments * * Output parameters: NONE * * Return values: * rtems_device_driver status code */ rtems_device_driver Clock_initialize( rtems_device_major_number major, rtems_device_minor_number minor, void *pargp ) { unsigned int tick_hz; /* * Take Timer that should be used as system timer. * */ Clock_handle = tlib_open(Clock_timer); if ( Clock_handle == NULL ) { /* System Clock Timer not found */ return RTEMS_NOT_DEFINED; } /* * Install Clock ISR before starting timer */ tlib_irq_register(Clock_handle, Clock_isr, NULL); /* Set Timer Frequency to tick at Configured value. The Timer * Frequency is set in multiples of the timer base frequency. * * In standard LEON3 designs the base frequency is is 1MHz, to * save instructions undefine CLOCK_DRIVER_DONT_ASSUME_PRESCALER_1MHZ * to avoid 64-bit calculation. */ #ifdef CLOCK_DRIVER_DONT_ASSUME_PRESCALER_1MHZ { uint64_t tmp; tlib_get_freq(Clock_handle, &Clock_basefreq, NULL); tmp = (uint64_t)Clock_basefreq; tmp = tmp * (uint64_t)rtems_configuration_get_microseconds_per_tick(); tick_hz = tmp / 1000000; } #else tick_hz = rtems_configuration_get_microseconds_per_tick(); #endif tlib_set_freq(Clock_handle, tick_hz); rtems_clock_set_nanoseconds_extension(Clock_nanoseconds_since_last_tick); /* * IRQ and Frequency is setup, now we start the Timer. IRQ is still * disabled globally during startup, so IRQ will hold for a while. */ tlib_start(Clock_handle, 0); /* * Register function called at system shutdown */ atexit( Clock_exit ); /* * make major/minor avail to others such as shared memory driver */ rtems_clock_major = major; rtems_clock_minor = minor; /* * If we are counting ISRs per tick, then initialize the counter. */ #ifdef CLOCK_DRIVER_ISRS_PER_TICK Clock_driver_isrs = CLOCK_DRIVER_ISRS_PER_TICK; #endif return RTEMS_SUCCESSFUL; } /*** Timer Driver Interface ***/ /* Set system clock timer instance */ void Clock_timer_register(int timer_number) { Clock_timer = timer_number; } #endif