Boot Linux faster!

Check our new training course

Boot Linux faster!

Check our new training course
and Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

/*
 * Copyright (c) 1997-2010, 2012-2015 Wind River Systems, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @file
 * @brief Microkernel tick event handler
 *
 * This module implements the microkernel's tick event handler.
 */

#include <nanokernel.h>
#include <arch/cpu.h>

#include <micro_private.h>
#include <drivers/system_timer.h>
#include <microkernel.h>
#include <microkernel/ticks.h>
#include <toolchain.h>
#include <sections.h>
#include <init.h>

#ifdef CONFIG_TIMESLICING
static int32_t slice_count = (int32_t)0;
static int32_t slice_time = (int32_t)CONFIG_TIMESLICE_SIZE;
static kpriority_t slice_prio =
	(kpriority_t)CONFIG_TIMESLICE_PRIORITY;
#endif /* CONFIG_TIMESLICING */

#ifdef CONFIG_TICKLESS_IDLE
/* Number of ticks elapsed that have not been announced to the microkernel */
int32_t _sys_idle_elapsed_ticks; /* Initial value must be 0 */
#endif

/**
 * @internal
 * @brief Task level debugging tick handler
 *
 * If task level debugging is configured this routine updates the low resolution
 * debugging timer and determines if task level processing should be suspended.
 *
 * @return 0 if task level processing should be halted or 1 if not
 *
 */
#ifdef CONFIG_TASK_DEBUG
uint32_t __noinit _k_debug_sys_clock_tick_count;

static inline int _TlDebugUpdate(int32_t ticks)
{
	_k_debug_sys_clock_tick_count += ticks;
	return !_k_debug_halt;
}
#else
#define _TlDebugUpdate(ticks) 1
#endif

/**
 * @internal
 * @brief Tick handler time slice logic
 *
 * This routine checks to see if it is time for the current task
 * to relinquish control, and yields CPU if so.
 *
 * @return N/A
 *
 */
static inline void _TimeSliceUpdate(void)
{
#ifdef CONFIG_TIMESLICING
	int yield = slice_time && (_k_current_task->priority >= slice_prio) &&
		    (++slice_count >= slice_time);
	if (yield) {
		slice_count = 0;
		_k_task_yield(NULL);
	}
#else
/* do nothing */
#endif /* CONFIG_TIMESLICING */
}

/**
 * @internal
 * @brief Get elapsed ticks
 *
 * If tickless idle support is configured this routine returns the number
 * of ticks since going idle and then resets the global elapsed tick counter back
 * to zero indicating all elapsed ticks have been consumed. This is done with
 * interrupts locked to prevent the timer ISR from modifying the global elapsed
 * tick counter.
 * If tickless idle support is not configured in it simply returns 1.
 *
 * @return number of ticks to process
 */
static inline int32_t _SysIdleElapsedTicksGet(void)
{
#ifdef CONFIG_TICKLESS_IDLE
	int32_t ticks;
	int key;

	key = irq_lock();
	ticks = _sys_idle_elapsed_ticks;
	_sys_idle_elapsed_ticks = 0;
	irq_unlock(key);
	return ticks;
#else
	/* A single tick always elapses when not in tickless mode */
	return 1;
#endif
}

/**
 *
 * @brief Microkernel tick handler
 *
 * This routine informs other microkernel subsystems that a tick event has
 * occurred.
 * @param even Event
 * @return 1
 */
int _k_ticker(int event)
{
	(void)event; /* prevent "unused argument" compiler warning */
	int32_t ticks;

	ticks = _SysIdleElapsedTicksGet();

	_k_workload_monitor_update();

	if (_TlDebugUpdate(ticks)) {
		_TimeSliceUpdate();
		_k_timer_list_update(ticks);
		_nano_sys_clock_tick_announce(ticks);
	}

	return 1;
}

#ifdef CONFIG_SYS_CLOCK_EXISTS
static void _sys_clock_tick_announce_pre_micro_nop(kevent_t e)
{
	ARG_UNUSED(e);
	/* do nothing */
}

void (*_do_sys_clock_tick_announce)(kevent_t) =
	_sys_clock_tick_announce_pre_micro_nop;

static int _sys_clock_tick_announce_install(struct device *dev)
{
	ARG_UNUSED(dev);
	_do_sys_clock_tick_announce = isr_event_send;
	return 0;
}

SYS_INIT(_sys_clock_tick_announce_install, MICROKERNEL, 0);
#endif	/* CONFIG_SYS_CLOCK_EXISTS */

#ifdef CONFIG_TIMESLICING

void sys_scheduler_time_slice_set(int32_t t, kpriority_t p)
{
	slice_time = t;
	slice_prio = p;
}

#endif /* CONFIG_TIMESLICING */