Peter Mitsis | 96cb05c | 2016-09-15 12:37:58 -0400 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2016 Wind River Systems, Inc. |
| 3 | * |
David B. Kinder | ac74d8b | 2017-01-18 17:01:01 -0800 | [diff] [blame] | 4 | * SPDX-License-Identifier: Apache-2.0 |
Peter Mitsis | 96cb05c | 2016-09-15 12:37:58 -0400 | [diff] [blame] | 5 | */ |
| 6 | |
Anas Nashif | dc3d73b | 2016-12-19 20:25:56 -0500 | [diff] [blame] | 7 | #include <kernel.h> |
Benjamin Walsh | f6ca7de | 2016-11-08 10:36:50 -0500 | [diff] [blame] | 8 | #include <kernel_structs.h> |
Peter Mitsis | 96cb05c | 2016-09-15 12:37:58 -0400 | [diff] [blame] | 9 | #include <toolchain.h> |
Anas Nashif | 397d29d | 2017-06-17 11:30:47 -0400 | [diff] [blame] | 10 | #include <linker/sections.h> |
Peter Mitsis | 96cb05c | 2016-09-15 12:37:58 -0400 | [diff] [blame] | 11 | #include <drivers/system_timer.h> |
| 12 | #include <wait_q.h> |
Peter Mitsis | 429f69a | 2016-10-20 16:43:53 -0400 | [diff] [blame] | 13 | #include <power.h> |
Peter Mitsis | 96cb05c | 2016-09-15 12:37:58 -0400 | [diff] [blame] | 14 | |
| 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 Gala | cc334c7 | 2017-04-21 10:55:34 -0500 | [diff] [blame] | 20 | s32_t _sys_idle_threshold_ticks = CONFIG_TICKLESS_IDLE_THRESH; |
Ramesh Thomas | 89ffd44 | 2017-02-05 19:37:19 -0800 | [diff] [blame] | 21 | |
| 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 Mitsis | 96cb05c | 2016-09-15 12:37:58 -0400 | [diff] [blame] | 30 | #endif /* CONFIG_TICKLESS_IDLE */ |
| 31 | |
| 32 | #ifdef CONFIG_SYS_POWER_MANAGEMENT |
Ramesh Thomas | 3e0f20a | 2016-10-26 21:16:37 -0700 | [diff] [blame] | 33 | /* |
| 34 | * Used to allow _sys_soc_suspend() implementation to control notification |
Ramesh Thomas | 8367056 | 2016-11-10 21:16:12 -0800 | [diff] [blame] | 35 | * of the event that caused exit from kernel idling after pm operations. |
Ramesh Thomas | 3e0f20a | 2016-10-26 21:16:37 -0700 | [diff] [blame] | 36 | */ |
Ramesh Thomas | 8367056 | 2016-11-10 21:16:12 -0800 | [diff] [blame] | 37 | unsigned char _sys_pm_idle_exit_notify; |
Ramesh Thomas | 3e0f20a | 2016-10-26 21:16:37 -0700 | [diff] [blame] | 38 | |
Ramesh Thomas | ded076d | 2016-11-02 23:56:34 -0700 | [diff] [blame] | 39 | void __attribute__((weak)) _sys_soc_resume(void) |
| 40 | { |
| 41 | } |
| 42 | |
Ramesh Thomas | c0cd7ac | 2016-11-09 22:55:14 -0800 | [diff] [blame] | 43 | void __attribute__((weak)) _sys_soc_resume_from_deep_sleep(void) |
| 44 | { |
| 45 | } |
| 46 | |
Peter Mitsis | 96cb05c | 2016-09-15 12:37:58 -0400 | [diff] [blame] | 47 | /** |
| 48 | * |
| 49 | * @brief Indicate that kernel is idling in tickless mode |
| 50 | * |
Anas Nashif | dc3d73b | 2016-12-19 20:25:56 -0500 | [diff] [blame] | 51 | * Sets the kernel data structure idle field to either a positive value or |
Peter Mitsis | 96cb05c | 2016-09-15 12:37:58 -0400 | [diff] [blame] | 52 | * K_FOREVER. |
| 53 | * |
| 54 | * @param ticks the number of ticks to idle |
| 55 | * |
| 56 | * @return N/A |
| 57 | */ |
Kumar Gala | cc334c7 | 2017-04-21 10:55:34 -0500 | [diff] [blame] | 58 | static void set_kernel_idle_time_in_ticks(s32_t ticks) |
Peter Mitsis | 96cb05c | 2016-09-15 12:37:58 -0400 | [diff] [blame] | 59 | { |
Benjamin Walsh | f6ca7de | 2016-11-08 10:36:50 -0500 | [diff] [blame] | 60 | _kernel.idle = ticks; |
Peter Mitsis | 96cb05c | 2016-09-15 12:37:58 -0400 | [diff] [blame] | 61 | } |
| 62 | #else |
| 63 | #define set_kernel_idle_time_in_ticks(x) do { } while (0) |
| 64 | #endif |
| 65 | |
Ramesh Thomas | 89ffd44 | 2017-02-05 19:37:19 -0800 | [diff] [blame] | 66 | static void _sys_power_save_idle(s32_t ticks) |
Peter Mitsis | 96cb05c | 2016-09-15 12:37:58 -0400 | [diff] [blame] | 67 | { |
Ramesh Thomas | 89ffd44 | 2017-02-05 19:37:19 -0800 | [diff] [blame] | 68 | #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 Mitsis | 96cb05c | 2016-09-15 12:37:58 -0400 | [diff] [blame] | 85 | /* |
| 86 | * Stop generating system timer interrupts until it's time for |
| 87 | * the next scheduled kernel timer to expire. |
| 88 | */ |
| 89 | |
Ramesh Thomas | 89ffd44 | 2017-02-05 19:37:19 -0800 | [diff] [blame] | 90 | /* |
| 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 Mitsis | 96cb05c | 2016-09-15 12:37:58 -0400 | [diff] [blame] | 95 | _timer_idle_enter(ticks); |
| 96 | } |
Peter Mitsis | 96cb05c | 2016-09-15 12:37:58 -0400 | [diff] [blame] | 97 | |
| 98 | set_kernel_idle_time_in_ticks(ticks); |
| 99 | #if (defined(CONFIG_SYS_POWER_LOW_POWER_STATE) || \ |
Ramesh Thomas | 3e0f20a | 2016-10-26 21:16:37 -0700 | [diff] [blame] | 100 | defined(CONFIG_SYS_POWER_DEEP_SLEEP)) |
| 101 | |
Ramesh Thomas | 8367056 | 2016-11-10 21:16:12 -0800 | [diff] [blame] | 102 | _sys_pm_idle_exit_notify = 1; |
Ramesh Thomas | 3e0f20a | 2016-10-26 21:16:37 -0700 | [diff] [blame] | 103 | |
Peter Mitsis | 96cb05c | 2016-09-15 12:37:58 -0400 | [diff] [blame] | 104 | /* |
Ramesh Thomas | 3e0f20a | 2016-10-26 21:16:37 -0700 | [diff] [blame] | 105 | * 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 Mitsis | 96cb05c | 2016-09-15 12:37:58 -0400 | [diff] [blame] | 109 | * |
Ramesh Thomas | 3e0f20a | 2016-10-26 21:16:37 -0700 | [diff] [blame] | 110 | * 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 Walsh | c3a2bbb | 2016-12-14 13:04:36 -0500 | [diff] [blame] | 113 | * processing in those cases i.e. skips k_cpu_idle(). The kernel's |
Ramesh Thomas | 3e0f20a | 2016-10-26 21:16:37 -0700 | [diff] [blame] | 114 | * idle processing re-enables interrupts which is essential for |
| 115 | * the kernel's scheduling logic. |
Peter Mitsis | 96cb05c | 2016-09-15 12:37:58 -0400 | [diff] [blame] | 116 | */ |
Ramesh Thomas | 3e0f20a | 2016-10-26 21:16:37 -0700 | [diff] [blame] | 117 | if (_sys_soc_suspend(ticks) == SYS_PM_NOT_HANDLED) { |
Ramesh Thomas | 8367056 | 2016-11-10 21:16:12 -0800 | [diff] [blame] | 118 | _sys_pm_idle_exit_notify = 0; |
Benjamin Walsh | c3a2bbb | 2016-12-14 13:04:36 -0500 | [diff] [blame] | 119 | k_cpu_idle(); |
Peter Mitsis | 96cb05c | 2016-09-15 12:37:58 -0400 | [diff] [blame] | 120 | } |
| 121 | #else |
Benjamin Walsh | c3a2bbb | 2016-12-14 13:04:36 -0500 | [diff] [blame] | 122 | k_cpu_idle(); |
Peter Mitsis | 96cb05c | 2016-09-15 12:37:58 -0400 | [diff] [blame] | 123 | #endif |
| 124 | } |
| 125 | |
Kumar Gala | cc334c7 | 2017-04-21 10:55:34 -0500 | [diff] [blame] | 126 | void _sys_power_save_idle_exit(s32_t ticks) |
Peter Mitsis | 96cb05c | 2016-09-15 12:37:58 -0400 | [diff] [blame] | 127 | { |
Ramesh Thomas | 3e0f20a | 2016-10-26 21:16:37 -0700 | [diff] [blame] | 128 | #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 Thomas | 8367056 | 2016-11-10 21:16:12 -0800 | [diff] [blame] | 132 | * disabled by calling _sys_soc_pm_idle_exit_notification_disable(). |
Ramesh Thomas | 3e0f20a | 2016-10-26 21:16:37 -0700 | [diff] [blame] | 133 | * Alternatively it can be simply ignored if not required. |
Peter Mitsis | 96cb05c | 2016-09-15 12:37:58 -0400 | [diff] [blame] | 134 | */ |
Ramesh Thomas | 8367056 | 2016-11-10 21:16:12 -0800 | [diff] [blame] | 135 | if (_sys_pm_idle_exit_notify) { |
Ramesh Thomas | 3e0f20a | 2016-10-26 21:16:37 -0700 | [diff] [blame] | 136 | _sys_soc_resume(); |
| 137 | } |
Peter Mitsis | 96cb05c | 2016-09-15 12:37:58 -0400 | [diff] [blame] | 138 | #endif |
Peter Mitsis | 96cb05c | 2016-09-15 12:37:58 -0400 | [diff] [blame] | 139 | |
Ramesh Thomas | 89ffd44 | 2017-02-05 19:37:19 -0800 | [diff] [blame] | 140 | if (_must_enter_tickless_idle(ticks)) { |
| 141 | /* Resume normal periodic system timer interrupts */ |
Peter Mitsis | 96cb05c | 2016-09-15 12:37:58 -0400 | [diff] [blame] | 142 | _timer_idle_exit(); |
| 143 | } |
Peter Mitsis | 96cb05c | 2016-09-15 12:37:58 -0400 | [diff] [blame] | 144 | } |
| 145 | |
| 146 | |
Benjamin Walsh | 8450c90 | 2016-11-08 15:38:45 -0500 | [diff] [blame] | 147 | #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 Mitsis | 96cb05c | 2016-09-15 12:37:58 -0400 | [diff] [blame] | 153 | void idle(void *unused1, void *unused2, void *unused3) |
| 154 | { |
| 155 | ARG_UNUSED(unused1); |
| 156 | ARG_UNUSED(unused2); |
| 157 | ARG_UNUSED(unused3); |
| 158 | |
Peter Mitsis | 5f8fa67 | 2016-10-27 15:19:49 -0400 | [diff] [blame] | 159 | #ifdef CONFIG_BOOT_TIME_MEASUREMENT |
| 160 | /* record timestamp when idling begins */ |
| 161 | |
Adithya Baglody | be1cb96 | 2017-06-14 15:15:49 +0530 | [diff] [blame] | 162 | extern u64_t __idle_time_stamp; |
Peter Mitsis | 5f8fa67 | 2016-10-27 15:19:49 -0400 | [diff] [blame] | 163 | |
Adithya Baglody | be1cb96 | 2017-06-14 15:15:49 +0530 | [diff] [blame] | 164 | __idle_time_stamp = (u64_t)k_cycle_get_32(); |
Peter Mitsis | 5f8fa67 | 2016-10-27 15:19:49 -0400 | [diff] [blame] | 165 | #endif |
| 166 | |
Peter Mitsis | 96cb05c | 2016-09-15 12:37:58 -0400 | [diff] [blame] | 167 | for (;;) { |
Benjamin Walsh | 8450c90 | 2016-11-08 15:38:45 -0500 | [diff] [blame] | 168 | (void)irq_lock(); |
Benjamin Walsh | 055262c | 2016-10-05 17:16:01 -0400 | [diff] [blame] | 169 | _sys_power_save_idle(_get_next_timeout_expiry()); |
Peter Mitsis | 96cb05c | 2016-09-15 12:37:58 -0400 | [diff] [blame] | 170 | |
Benjamin Walsh | 8450c90 | 2016-11-08 15:38:45 -0500 | [diff] [blame] | 171 | IDLE_YIELD_IF_COOP(); |
Peter Mitsis | 96cb05c | 2016-09-15 12:37:58 -0400 | [diff] [blame] | 172 | } |
| 173 | } |