Пример #1
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;
}
Пример #2
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();

#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;
}
Пример #3
0
/**
 *
 * @brief Stop announcing ticks into the kernel
 *
 * This routine simply disables the LOAPIC counter such that interrupts are no
 * longer delivered.
 *
 * @return N/A
 */
void sys_clock_disable(void)
{
	unsigned int key; /* interrupt lock level */

	key = irq_lock();

	irq_disable(TIMER_IRQ);
	initial_count_register_set(0);

	irq_unlock(key);
}
Пример #4
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*/

}
Пример #5
0
/**
 *
 * @brief Stop announcing ticks into the kernel
 *
 * This routine simply disables the LOAPIC counter such that interrupts are no
 * longer delivered.
 *
 * @return N/A
 */
void sys_clock_disable(void)
{
    unsigned int key; /* interrupt lock level */

    key = irq_lock();

    timer_interrupt_mask();
    initial_count_register_set(0);

    irq_unlock(key);

    /* disable interrupt in the interrupt controller */

    irq_disable(CONFIG_LOAPIC_TIMER_IRQ);
}
Пример #6
0
/**
 *
 * @brief Place system timer into idle state
 *
 * Re-program the timer to enter into the idle state for the given number of
 * ticks. It is placed into one shot mode where it will fire in the number of
 * ticks supplied or the maximum number of ticks that can be programmed into
 * hardware. A value of -1 means inifinite number of ticks.
 *
 * @return N/A
 */
void _timer_idle_enter(int32_t ticks /* system ticks */
				)
{
	uint32_t  cycles;

	/*
	 * Although interrupts are disabled, the LOAPIC timer is still counting
	 * down. Take a snapshot of current count register to get the number of
	 * cycles remaining in the timer before it signals an interrupt and apply
	 * that towards the one-shot calculation to maintain accuracy.
	 *
	 * NOTE: If entering tickless idle straddles a tick, 'programmed_cycles'
	 * and 'programmmed_full_ticks' may be incorrect as we do not know which
	 * side of the tick the snapshot occurred.  This is not a problem as the
	 * values will be corrected once the straddling is detected.
	 */

	cycles = current_count_register_get();

	if ((ticks == TICKS_UNLIMITED) || (ticks > max_system_ticks)) {
		/*
		 * The number of cycles until the timer must fire next might not fit
		 * in the 32-bit counter register. To work around this, program
		 * the counter to fire in the maximum number of ticks (plus any
		 * remaining cycles).
		 */

		programmed_full_ticks = max_system_ticks;
		programmed_cycles = cycles + cycles_per_max_ticks;
	} else {
		programmed_full_ticks = ticks - 1;
		programmed_cycles = cycles + (programmed_full_ticks * cycles_per_tick);
	}

	/* Set timer to one-shot mode */
	initial_count_register_set(programmed_cycles);
	one_shot_mode_set();
	timer_mode = TIMER_MODE_ONE_SHOT;
}
Пример #7
0
static int sys_clock_resume(struct device *dev)
{
	ARG_UNUSED(dev);

	*_REG_TIMER = reg_timer_save;
#ifndef CONFIG_MVIC
	*_REG_TIMER_CFG = reg_timer_cfg_save;
#endif

	/*
	 * It is difficult to accurately know the time spent in DS.
	 * We can use TSC or RTC but that will create a dependency
	 * on those components. Other issue is about what to do
	 * with pending timers. Following are some options :-
	 *
	 * 1) Expire all timers based on time spent found using some
	 *    source like TSC
	 * 2) Expire all timers anyway
	 * 3) Expire only the timer at the top
	 * 4) Contine from where the timer left
	 *
	 * 1 and 2 require change to how timers are handled. 4 may not
	 * give a good user experience. After waiting for a long period
	 * in DS, the system would appear dead if it waits again.
	 *
	 * Current implementation uses option 3. The top most timer is
	 * expired. Following code will set the counter to a low number
	 * so it would immediately expire and generate timer interrupt
	 * which will process the top most timer. Note that timer IC
	 * cannot be set to 0.  Setting it to 0 will stop the timer.
	 */

	initial_count_register_set(1);
	loapic_timer_device_power_state = DEVICE_PM_ACTIVE_STATE;

	return 0;
}
Пример #8
0
/**
 *
 * @brief Handling of tickless idle when interrupted
 *
 * The routine is responsible for taking the timer out of idle mode and
 * generating an interrupt at the next tick interval.
 *
 * Note that in this routine, _sys_idle_elapsed_ticks must be zero because the
 * ticker has done its work and consumed all the ticks. This has to be true
 * otherwise idle mode wouldn't have been entered in the first place.
 *
 * @return N/A
 */
void _timer_idle_exit(void)
{
	uint32_t remaining_cycles;
	uint32_t remaining_full_ticks;

	/*
	 * Interrupts are locked and idling has ceased. The cause of the cessation
	 * is unknown. It may be due to one of three cases.
	 *  1. The timer, which was previously placed into one-shot mode has
	 *     counted down to zero and signaled an interrupt.
	 *  2. A non-timer interrupt occurred. Note that the LOAPIC timer will
	 *     still continue to decrement and may yet signal an interrupt.
	 *  3. The LOAPIC timer signaled an interrupt while the timer was being
	 *     programmed for one-shot mode.
	 *
	 * 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 a second comparison is required to
	 * check for wrap-around.
	 */

	remaining_cycles = current_count_register_get();

	if ((remaining_cycles == 0) ||
		(remaining_cycles >= programmed_cycles)) {
		/*
		 * The timer has expired. The handler _timer_int_handler() is
		 * guaranteed to execute. Track the number of elapsed ticks. The
		 * handler _timer_int_handler() will account for the final tick.
		 */

		_sys_idle_elapsed_ticks = programmed_full_ticks;

		/*
		 * Announce elapsed ticks to the kernel. Note we are guaranteed
		 * that the timer ISR will execute before the tick event is serviced.
		 * (The timer ISR reprograms the timer for the next tick.)
		 */

		_sys_clock_tick_announce();

		timer_known_to_have_expired = true;

		return;
	}

	timer_known_to_have_expired = false;

	/*
	 * Either a non-timer interrupt occurred, or we straddled a tick when
	 * entering tickless idle. It is impossible to determine which occurred
	 * at this point. Regardless of the cause, ensure that the timer will
	 * expire at the end of the next tick in case the ISR makes any tasks
	 * and/or fibers ready to run.
	 *
	 * NOTE #1: In the case of a straddled tick, the '_sys_idle_elapsed_ticks'
	 * calculation below may result in either 0 or 1. If 1, then this may
	 * result in a harmless extra call to _sys_clock_tick_announce().
	 *
	 * NOTE #2: In the case of a straddled tick, it is assumed that when the
	 * timer is reprogrammed, it will be reprogrammed with a cycle count
	 * sufficiently close to one tick that the timer will not expire before
	 * _timer_int_handler() is executed.
	 */

	remaining_full_ticks = remaining_cycles / cycles_per_tick;

	_sys_idle_elapsed_ticks = programmed_full_ticks - remaining_full_ticks;

	if (_sys_idle_elapsed_ticks > 0) {
		_sys_clock_tick_announce();
	}

	if (remaining_full_ticks > 0) {
		/*
		 * Re-program the timer (still in one-shot mode) to fire at the end of
		 * the tick, being careful to not program zero thus stopping the timer.
		 */

		programmed_cycles = 1 + ((remaining_cycles - 1) % cycles_per_tick);

		initial_count_register_set(programmed_cycles);
	}
}
Пример #9
0
/**
 *
 * @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 */
}