/** * * @brief Initialize and enable the system clock * * This routine is used to program the timer to deliver interrupts at the * rate specified via the 'sys_clock_us_per_tick' global variable. * * @return 0 */ int _sys_clock_driver_init(struct device *device) { ARG_UNUSED(device); /* determine the timer counter value (in timer clock cycles/system tick) */ cycles_per_tick = sys_clock_hw_cycles_per_tick; tickless_idle_init(); #ifndef CONFIG_MVIC divide_configuration_register_set(); #endif initial_count_register_set(cycles_per_tick - 1); periodic_mode_set(); #ifdef CONFIG_DEVICE_POWER_MANAGEMENT loapic_timer_device_power_state = DEVICE_PM_ACTIVE_STATE; #endif IRQ_CONNECT(TIMER_IRQ, TIMER_IRQ_PRIORITY, _timer_int_handler, 0, 0); /* Everything has been configured. It is now safe to enable the * interrupt */ irq_enable(TIMER_IRQ); return 0; }
/** * * @brief Initialize and enable the system clock * * This routine is used to program the timer to deliver interrupts at the * rate specified via the 'sys_clock_us_per_tick' global variable. * * @return 0 */ int _sys_clock_driver_init(struct device *device) { ARG_UNUSED(device); /* determine the timer counter value (in timer clock cycles/system tick) */ cycles_per_tick = sys_clock_hw_cycles_per_tick; tickless_idle_init(); divide_configuration_register_set(); initial_count_register_set(cycles_per_tick - 1); periodic_mode_set(); IRQ_CONNECT(CONFIG_LOAPIC_TIMER_IRQ, CONFIG_LOAPIC_TIMER_IRQ_PRIORITY, _timer_int_handler, 0, 0); /* Everything has been configured. It is now safe to enable the * interrupt */ irq_enable(CONFIG_LOAPIC_TIMER_IRQ); return 0; }
void _timer_int_handler(void *unused /* parameter is not used */ ) { ARG_UNUSED(unused); #ifdef CONFIG_TICKLESS_IDLE if (timer_mode == TIMER_MODE_ONE_SHOT) { if (!timer_known_to_have_expired) { uint32_t cycles; /* * The timer fired unexpectedly. This is due to one of two cases: * 1. Entering tickless idle straddled a tick. * 2. Leaving tickless idle straddled the final tick. * Due to the timer reprogramming in _timer_idle_exit(), case #2 * can be handled as a fall-through. * * NOTE: Although the cycle count is supposed to stop decrementing * once it hits zero in one-shot mode, not all targets implement * this properly (and continue to decrement). Thus, we have to * perform a second comparison to check for wrap-around. */ cycles = current_count_register_get(); if ((cycles > 0) && (cycles < programmed_cycles)) { /* Case 1 */ _sys_idle_elapsed_ticks = 0; } } /* Return the timer to periodic mode */ initial_count_register_set(cycles_per_tick - 1); periodic_mode_set(); timer_known_to_have_expired = false; timer_mode = TIMER_MODE_PERIODIC; } _sys_clock_final_tick_announce(); /* track the accumulated cycle count */ accumulated_cycle_count += cycles_per_tick * _sys_idle_elapsed_ticks; #else /* track the accumulated cycle count */ accumulated_cycle_count += cycles_per_tick; _sys_clock_tick_announce(); #endif /*CONFIG_TICKLESS_IDLE*/ }
/** * * @brief System clock tick handler * * This routine handles the system clock tick interrupt. A TICK_EVENT event * is pushed onto the microkernel stack. * * @return N/A */ void _timer_int_handler(void *unused /* parameter is not used */ ) { ARG_UNUSED(unused); #ifdef CONFIG_TICKLESS_IDLE if (timer_mode == TIMER_MODE_ONE_SHOT) { if (!timer_known_to_have_expired) { uint32_t cycles; /* * The timer fired unexpectedly. This is due to one of two cases: * 1. Entering tickless idle straddled a tick. * 2. Leaving tickless idle straddled the final tick. * Due to the timer reprogramming in _timer_idle_exit(), case #2 * can be handled as a fall-through. * * NOTE: Although the cycle count is supposed to stop decrementing * once it hits zero in one-shot mode, not all targets implement * this properly (and continue to decrement). Thus, we have to * perform a second comparison to check for wrap-around. */ cycles = current_count_register_get(); if ((cycles > 0) && (cycles < programmed_cycles)) { /* Case 1 */ _sys_idle_elapsed_ticks = 0; } } /* Return the timer to periodic mode */ initial_count_register_set(cycles_per_tick - 1); periodic_mode_set(); timer_known_to_have_expired = false; timer_mode = TIMER_MODE_PERIODIC; } /* * Increment the tick because _timer_idle_exit() does not account * for the tick due to the timer interrupt itself. Also, if not in * one-shot mode, _sys_idle_elapsed_ticks will be 0. */ #ifdef CONFIG_MICROKERNEL _sys_idle_elapsed_ticks++; #else _sys_idle_elapsed_ticks = 1; #endif /* track the accumulated cycle count */ accumulated_cycle_count += cycles_per_tick * _sys_idle_elapsed_ticks; /* * If we transistion from 0 elapsed ticks to 1 we need to announce the * tick event to the microkernel. Other cases will have already been * covered by _timer_idle_exit(). */ if (_sys_idle_elapsed_ticks == 1) { _sys_clock_tick_announce(); } #else /* track the accumulated cycle count */ accumulated_cycle_count += cycles_per_tick; _sys_clock_tick_announce(); #endif /*CONFIG_TICKLESS_IDLE*/ #ifdef LOAPIC_TIMER_PERIODIC_WORKAROUND /* * On platforms where the LOAPIC timer periodic mode is broken, * re-program the ICR register with the initial count value. This * is only a temporary workaround. */ initial_count_register_set(cycles_per_tick - 1); periodic_mode_set(); #endif /* LOAPIC_TIMER_PERIODIC_WORKAROUND */ }