blob: d4e925536b7a6c5f6b9875d8eb1dc576df6f0aa8 [file] [log] [blame]
Peter Mitsis96cb05c2016-09-15 12:37:58 -04001/*
2 * Copyright (c) 2016 Wind River Systems, Inc.
3 *
David B. Kinderac74d8b2017-01-18 17:01:01 -08004 * SPDX-License-Identifier: Apache-2.0
Peter Mitsis96cb05c2016-09-15 12:37:58 -04005 */
6
Anas Nashifdc3d73b2016-12-19 20:25:56 -05007#include <kernel.h>
Benjamin Walshf6ca7de2016-11-08 10:36:50 -05008#include <kernel_structs.h>
Peter Mitsis96cb05c2016-09-15 12:37:58 -04009#include <toolchain.h>
Anas Nashif397d29d2017-06-17 11:30:47 -040010#include <linker/sections.h>
Peter Mitsis96cb05c2016-09-15 12:37:58 -040011#include <drivers/system_timer.h>
12#include <wait_q.h>
Peter Mitsis429f69a2016-10-20 16:43:53 -040013#include <power.h>
Peter Mitsis96cb05c2016-09-15 12:37:58 -040014
15#if defined(CONFIG_TICKLESS_IDLE)
16/*
17 * Idle time must be this value or higher for timer to go into tickless idle
18 * state.
19 */
Kumar Galacc334c72017-04-21 10:55:34 -050020s32_t _sys_idle_threshold_ticks = CONFIG_TICKLESS_IDLE_THRESH;
Ramesh Thomas89ffd442017-02-05 19:37:19 -080021
22#if defined(CONFIG_TICKLESS_KERNEL)
23#define _must_enter_tickless_idle(ticks) (1)
24#else
25#define _must_enter_tickless_idle(ticks) \
26 ((ticks == K_FOREVER) || (ticks >= _sys_idle_threshold_ticks))
27#endif
28#else
29#define _must_enter_tickless_idle(ticks) ((void)ticks, (0))
Peter Mitsis96cb05c2016-09-15 12:37:58 -040030#endif /* CONFIG_TICKLESS_IDLE */
31
32#ifdef CONFIG_SYS_POWER_MANAGEMENT
Ramesh Thomas3e0f20a2016-10-26 21:16:37 -070033/*
34 * Used to allow _sys_soc_suspend() implementation to control notification
Ramesh Thomas83670562016-11-10 21:16:12 -080035 * of the event that caused exit from kernel idling after pm operations.
Ramesh Thomas3e0f20a2016-10-26 21:16:37 -070036 */
Ramesh Thomas83670562016-11-10 21:16:12 -080037unsigned char _sys_pm_idle_exit_notify;
Ramesh Thomas3e0f20a2016-10-26 21:16:37 -070038
Ramesh Thomasded076d2016-11-02 23:56:34 -070039void __attribute__((weak)) _sys_soc_resume(void)
40{
41}
42
Ramesh Thomasc0cd7ac2016-11-09 22:55:14 -080043void __attribute__((weak)) _sys_soc_resume_from_deep_sleep(void)
44{
45}
46
Peter Mitsis96cb05c2016-09-15 12:37:58 -040047/**
48 *
49 * @brief Indicate that kernel is idling in tickless mode
50 *
Anas Nashifdc3d73b2016-12-19 20:25:56 -050051 * Sets the kernel data structure idle field to either a positive value or
Peter Mitsis96cb05c2016-09-15 12:37:58 -040052 * K_FOREVER.
53 *
54 * @param ticks the number of ticks to idle
55 *
56 * @return N/A
57 */
Kumar Galacc334c72017-04-21 10:55:34 -050058static void set_kernel_idle_time_in_ticks(s32_t ticks)
Peter Mitsis96cb05c2016-09-15 12:37:58 -040059{
Benjamin Walshf6ca7de2016-11-08 10:36:50 -050060 _kernel.idle = ticks;
Peter Mitsis96cb05c2016-09-15 12:37:58 -040061}
62#else
63#define set_kernel_idle_time_in_ticks(x) do { } while (0)
64#endif
65
Ramesh Thomas89ffd442017-02-05 19:37:19 -080066static void _sys_power_save_idle(s32_t ticks)
Peter Mitsis96cb05c2016-09-15 12:37:58 -040067{
Ramesh Thomas89ffd442017-02-05 19:37:19 -080068#ifdef CONFIG_TICKLESS_KERNEL
69 if (ticks != K_FOREVER) {
70 ticks -= _get_elapsed_program_time();
71 if (!ticks) {
72 /*
73 * Timer has expired or about to expire
74 * No time for power saving operations
75 *
76 * Note that it will never be zero unless some time
77 * had elapsed since timer was last programmed.
78 */
79 k_cpu_idle();
80 return;
81 }
82 }
83#endif
84 if (_must_enter_tickless_idle(ticks)) {
Peter Mitsis96cb05c2016-09-15 12:37:58 -040085 /*
86 * Stop generating system timer interrupts until it's time for
87 * the next scheduled kernel timer to expire.
88 */
89
Ramesh Thomas89ffd442017-02-05 19:37:19 -080090 /*
91 * In the case of tickless kernel, timer driver should
92 * reprogram timer only if the currently programmed time
93 * duration is smaller than the idle time.
94 */
Peter Mitsis96cb05c2016-09-15 12:37:58 -040095 _timer_idle_enter(ticks);
96 }
Peter Mitsis96cb05c2016-09-15 12:37:58 -040097
98 set_kernel_idle_time_in_ticks(ticks);
99#if (defined(CONFIG_SYS_POWER_LOW_POWER_STATE) || \
Ramesh Thomas3e0f20a2016-10-26 21:16:37 -0700100 defined(CONFIG_SYS_POWER_DEEP_SLEEP))
101
Ramesh Thomas83670562016-11-10 21:16:12 -0800102 _sys_pm_idle_exit_notify = 1;
Ramesh Thomas3e0f20a2016-10-26 21:16:37 -0700103
Peter Mitsis96cb05c2016-09-15 12:37:58 -0400104 /*
Ramesh Thomas3e0f20a2016-10-26 21:16:37 -0700105 * Call the suspend hook function of the soc interface to allow
106 * entry into a low power state. The function returns
107 * SYS_PM_NOT_HANDLED if low power state was not entered, in which
108 * case, kernel does normal idle processing.
Peter Mitsis96cb05c2016-09-15 12:37:58 -0400109 *
Ramesh Thomas3e0f20a2016-10-26 21:16:37 -0700110 * This function is entered with interrupts disabled. If a low power
111 * state was entered, then the hook function should enable inerrupts
112 * before exiting. This is because the kernel does not do its own idle
Benjamin Walshc3a2bbb2016-12-14 13:04:36 -0500113 * processing in those cases i.e. skips k_cpu_idle(). The kernel's
Ramesh Thomas3e0f20a2016-10-26 21:16:37 -0700114 * idle processing re-enables interrupts which is essential for
115 * the kernel's scheduling logic.
Peter Mitsis96cb05c2016-09-15 12:37:58 -0400116 */
Ramesh Thomas3e0f20a2016-10-26 21:16:37 -0700117 if (_sys_soc_suspend(ticks) == SYS_PM_NOT_HANDLED) {
Ramesh Thomas83670562016-11-10 21:16:12 -0800118 _sys_pm_idle_exit_notify = 0;
Benjamin Walshc3a2bbb2016-12-14 13:04:36 -0500119 k_cpu_idle();
Peter Mitsis96cb05c2016-09-15 12:37:58 -0400120 }
121#else
Benjamin Walshc3a2bbb2016-12-14 13:04:36 -0500122 k_cpu_idle();
Peter Mitsis96cb05c2016-09-15 12:37:58 -0400123#endif
124}
125
Kumar Galacc334c72017-04-21 10:55:34 -0500126void _sys_power_save_idle_exit(s32_t ticks)
Peter Mitsis96cb05c2016-09-15 12:37:58 -0400127{
Ramesh Thomas3e0f20a2016-10-26 21:16:37 -0700128#if defined(CONFIG_SYS_POWER_LOW_POWER_STATE)
129 /* Some CPU low power states require notification at the ISR
130 * to allow any operations that needs to be done before kernel
131 * switches task or processes nested interrupts. This can be
Ramesh Thomas83670562016-11-10 21:16:12 -0800132 * disabled by calling _sys_soc_pm_idle_exit_notification_disable().
Ramesh Thomas3e0f20a2016-10-26 21:16:37 -0700133 * Alternatively it can be simply ignored if not required.
Peter Mitsis96cb05c2016-09-15 12:37:58 -0400134 */
Ramesh Thomas83670562016-11-10 21:16:12 -0800135 if (_sys_pm_idle_exit_notify) {
Ramesh Thomas3e0f20a2016-10-26 21:16:37 -0700136 _sys_soc_resume();
137 }
Peter Mitsis96cb05c2016-09-15 12:37:58 -0400138#endif
Peter Mitsis96cb05c2016-09-15 12:37:58 -0400139
Ramesh Thomas89ffd442017-02-05 19:37:19 -0800140 if (_must_enter_tickless_idle(ticks)) {
141 /* Resume normal periodic system timer interrupts */
Peter Mitsis96cb05c2016-09-15 12:37:58 -0400142 _timer_idle_exit();
143 }
Peter Mitsis96cb05c2016-09-15 12:37:58 -0400144}
145
146
Benjamin Walsh8450c902016-11-08 15:38:45 -0500147#if K_IDLE_PRIO < 0
148#define IDLE_YIELD_IF_COOP() k_yield()
149#else
150#define IDLE_YIELD_IF_COOP() do { } while ((0))
151#endif
152
Peter Mitsis96cb05c2016-09-15 12:37:58 -0400153void idle(void *unused1, void *unused2, void *unused3)
154{
155 ARG_UNUSED(unused1);
156 ARG_UNUSED(unused2);
157 ARG_UNUSED(unused3);
158
Peter Mitsis5f8fa672016-10-27 15:19:49 -0400159#ifdef CONFIG_BOOT_TIME_MEASUREMENT
160 /* record timestamp when idling begins */
161
Adithya Baglodybe1cb962017-06-14 15:15:49 +0530162 extern u64_t __idle_time_stamp;
Peter Mitsis5f8fa672016-10-27 15:19:49 -0400163
Adithya Baglodybe1cb962017-06-14 15:15:49 +0530164 __idle_time_stamp = (u64_t)k_cycle_get_32();
Peter Mitsis5f8fa672016-10-27 15:19:49 -0400165#endif
166
Peter Mitsis96cb05c2016-09-15 12:37:58 -0400167 for (;;) {
Benjamin Walsh8450c902016-11-08 15:38:45 -0500168 (void)irq_lock();
Benjamin Walsh055262c2016-10-05 17:16:01 -0400169 _sys_power_save_idle(_get_next_timeout_expiry());
Peter Mitsis96cb05c2016-09-15 12:37:58 -0400170
Benjamin Walsh8450c902016-11-08 15:38:45 -0500171 IDLE_YIELD_IF_COOP();
Peter Mitsis96cb05c2016-09-15 12:37:58 -0400172 }
173}