/* idle.c - microkernel idle logic */ /* * Copyright (c) 1997-2010, 2012-2014 Wind River Systems, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1) Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2) Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3) Neither the name of Wind River Systems nor the names of its contributors * may be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* DESCRIPTION Microkernel idle logic. Different forms of idling are performed by the idle task, depending on how the kernel is configured. */ #include #include #include #include #include #include #include #include #if defined(CONFIG_WORKLOAD_MONITOR) unsigned int _k_workload_slice = 0x0; unsigned int _k_workload_ticks = 0x0; unsigned int _k_workload_ref_time = 0x0; unsigned int _k_workload_t0 = 0x0; unsigned int _k_workload_t1 = 0x0; volatile unsigned int _k_workload_n0 = 0x0; volatile unsigned int _k_workload_n1 = 0x0; volatile unsigned int _k_workload_i = 0x0; volatile unsigned int _k_workload_i0 = 0x0; volatile unsigned int _k_workload_delta = 0x0; volatile unsigned int _k_workload_start_time = 0x0; volatile unsigned int _k_workload_end_time = 0x0; #ifdef WL_SCALE extern uint32_t _k_workload_scale; #endif #define MSEC_PER_SEC 1000 /******************************************************************************* * * _workload_loop - shared code between workload calibration and monitoring * * Perform idle task "dummy work". * * This routine increments _k_workload_i and checks it against _k_workload_n1. _k_workload_n1 is updated * by the system tick handler, and both are kept in close synchronization. * * RETURNS: N/A * */ static void _workload_loop(void) { volatile int x = 87654321; volatile int y = 4; while (++_k_workload_i != _k_workload_n1) /* except for the calibration phase, * this while loop should always be true. */ { unsigned int s_iCountDummyProc = 0; while (64 != s_iCountDummyProc++) { /* 64 == 2^6 */ x >>= y; x <<= y; y++; x >>= y; x <<= y; y--; } } } /******************************************************************************* * * workload_monitor_calibrate - calibrate the workload monitoring subsystem * * Measures the time required to do a fixed amount of "dummy work", and * sets default values for the workload measuring period. * * RETURNS: N/A * */ void workload_monitor_calibrate(void) { _k_workload_n0 = _k_workload_i = 0; _k_workload_n1 = 1000; _k_workload_t0 = timer_read(); _workload_loop(); _k_workload_t1 = timer_read(); _k_workload_delta = _k_workload_t1 - _k_workload_t0; _k_workload_i0 = _k_workload_i; #ifdef WL_SCALE _k_workload_ref_time = (_k_workload_t1 - _k_workload_t0) >> (_k_workload_scale); #else _k_workload_ref_time = (_k_workload_t1 - _k_workload_t0) >> (4 + 6); #endif _k_workload_slice = 100; _k_workload_ticks = 100; } #endif /* CONFIG_WORKLOAD_MONITOR */ #ifdef CONFIG_WORKLOAD_MONITOR void K_workload(struct k_args *P) { unsigned int k, t; signed int iret; k = (_k_workload_i - _k_workload_n0) * _k_workload_ref_time; #ifdef WL_SCALE t = (timer_read() - _k_workload_t0) >> (_k_workload_scale); #else t = (timer_read() - _k_workload_t0) >> (4 + 6); #endif iret = MSEC_PER_SEC - k / t; /* * Due to calibration at startup, could be slightly negative. * Ensure a negative value is never returned. */ if (iret < 0) { iret = 0; } P->Args.u1.rval = iret; } #else void K_workload(struct k_args *P) { P->Args.u1.rval = 0; } #endif /******************************************************************************* * * task_node_workload_get - read the processor workload * * This routine returns the workload as a number ranging from 0 to 1000. * * Each unit equals 0.1% of the time the CPU was not idle during the period * set by workload_time_slice_set(). * * RETURNS: workload */ int task_node_workload_get(void) { struct k_args A; A.Comm = READWL; KERNEL_ENTRY(&A); return A.Args.u1.rval; } /******************************************************************************* * * workload_time_slice_set - set workload period * * This routine specifies the workload measuring period for task_node_workload_get(). * * RETURNS: N/A */ void workload_time_slice_set(int32_t t) { #ifdef CONFIG_WORKLOAD_MONITOR if (t < 10) t = 10; if (t > 1000) t = 1000; _k_workload_slice = t; #else ARG_UNUSED(t); #endif } #if defined(CONFIG_ADVANCED_POWER_MANAGEMENT) /******************************************************************************* * * _get_next_timer_expiry - obtain number of ticks until next timer expires * * Must be called with interrupts locked to prevent the timer queues from * changing. * * RETURNS: Number of ticks until next timer expires. * */ static inline int32_t _get_next_timer_expiry(void) { if (_k_timer_list_head) return _k_timer_list_head->Ti; return TICKS_UNLIMITED; } #endif /******************************************************************************* * * _power_save - power saving when idle * * If the BSP sets the _sys_power_save_flag flag, this routine will call the * _SysPowerSaveIdle() routine in an infinite loop. If the flag is not set, * this routine will fall through and kernel_idle() will try the next idling * mechanism. * * RETURNS: N/A * */ static void _power_save(void) { extern void nano_cpu_idle(void); extern unsigned char _sys_power_save_flag; extern void _SysPowerSaveIdle(int32_t ticks); if (_sys_power_save_flag) { for (;;) { irq_lock_inline(); #ifdef CONFIG_ADVANCED_POWER_MANAGEMENT _SysPowerSaveIdle(_get_next_timer_expiry()); #else /* * nano_cpu_idle () is invoked here directly only if APM * is * disabled. Otherwise BSP decides either to invoke it * or * to implement advanced idle functionality */ nano_cpu_idle(); #endif } /* * Code analyzers may complain that _power_save() uses an * infinite loop * unless we indicate that this is intentional */ CODE_UNREACHABLE; } } /******************************************************************************* * * kernel_idle - microkernel idle task * * If power save is on, we sleep. If power save is off, we will try to do * workload monitoring. If power save is off and workload monitoring * is not included, we have to busy wait. * * RETURNS: N/A * */ int kernel_idle(void) { _power_save(); /* never returns if power saving is enabled */ #ifdef CONFIG_BOOT_TIME_MEASUREMENT /* Power saving not enabled, so record timestamp when idle begins here */ extern uint64_t __idle_tsc; __idle_tsc = _NanoTscRead(); #endif #ifdef CONFIG_WORKLOAD_MONITOR _workload_loop(); #endif for (;;) { /* do nothing */ } /* * Code analyzers may complain that kernel_idle() uses an infinite loop * unless we indicate that this is intentional */ CODE_UNREACHABLE; }