rjw | 6c1fd8f | 2022-11-30 14:33:01 +0800 | [diff] [blame] | 1 | /***************************************************************************** |
| 2 | * Copyright Statement: |
| 3 | * -------------------- |
| 4 | * This software is protected by Copyright and the information contained |
| 5 | * herein is confidential. The software may not be copied and the information |
| 6 | * contained herein may not be used or disclosed except with the written |
| 7 | * permission of MediaTek Inc. (C) 2018 |
| 8 | * |
| 9 | * BY OPENING THIS FILE, BUYER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES |
| 10 | * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE") |
| 11 | * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO BUYER ON |
| 12 | * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES, |
| 13 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF |
| 14 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. |
| 15 | * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE |
| 16 | * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR |
| 17 | * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND BUYER AGREES TO LOOK ONLY TO SUCH |
| 18 | * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. MEDIATEK SHALL ALSO |
| 19 | * NOT BE RESPONSIBLE FOR ANY MEDIATEK SOFTWARE RELEASES MADE TO BUYER'S |
| 20 | * SPECIFICATION OR TO CONFORM TO A PARTICULAR STANDARD OR OPEN FORUM. |
| 21 | * |
| 22 | * BUYER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND CUMULATIVE |
| 23 | * LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE, |
| 24 | * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE, |
| 25 | * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY BUYER TO |
| 26 | * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE. |
| 27 | * |
| 28 | * THE TRANSACTION CONTEMPLATED HEREUNDER SHALL BE CONSTRUED IN ACCORDANCE |
| 29 | * WITH THE LAWS OF THE STATE OF CALIFORNIA, USA, EXCLUDING ITS CONFLICT OF |
| 30 | * LAWS PRINCIPLES. ANY DISPUTES, CONTROVERSIES OR CLAIMS ARISING THEREOF AND |
| 31 | * RELATED THERETO SHALL BE SETTLED BY ARBITRATION IN SAN FRANCISCO, CA, UNDER |
| 32 | * THE RULES OF THE INTERNATIONAL CHAMBER OF COMMERCE (ICC). |
| 33 | * |
| 34 | *****************************************************************************/ |
| 35 | |
| 36 | /***************************************************************************** |
| 37 | * |
| 38 | * Filename: |
| 39 | * --------- |
| 40 | * mer_kernel_scheduler.h |
| 41 | * |
| 42 | * Project: |
| 43 | * -------- |
| 44 | * MERTOS |
| 45 | * |
| 46 | * Description: |
| 47 | * ------------ |
| 48 | * MERTOS scheduler header |
| 49 | * |
| 50 | * Author: |
| 51 | * ------- |
| 52 | * ------- |
| 53 | * |
| 54 | *****************************************************************************/ |
| 55 | #ifndef _MER_KERNEL_SCHEDULER_H_ |
| 56 | #define _MER_KERNEL_SCHEDULER_H_ |
| 57 | #include "mer_kernel_config_public.h" |
| 58 | |
| 59 | #if defined(__MD97__) || defined(__MD97P__) |
| 60 | |
| 61 | #define MER_KERNEL_SCHEDULER_WAKE_UP_STATE 0x2 |
| 62 | #define MER_KERNEL_SCHEDULER_BUSY_STATE 0x0 |
| 63 | #define MER_KERNEL_SCHEDULER_IDLE_STATE 0x1 |
| 64 | |
| 65 | #define MER_KERNEL_SCHEDULER_CORE0_MASK 0x7 |
| 66 | #define MER_KERNEL_SCHEDULER_CORE1_MASK 0x38 |
| 67 | |
| 68 | #else |
| 69 | #error "unsupport platform" |
| 70 | #endif /* __MD97__ || __MD97P__ */ |
| 71 | |
| 72 | #define MER_KERNEL_SCHEDULER_JOB_INDEX_MASK (0xFFFFFFF) |
| 73 | #define MER_KERNEL_SCHEDULER_JOB_PRIORITY_MASK (~(MER_KERNEL_SCHEDULER_JOB_INDEX_MASK)) |
| 74 | |
| 75 | #define MER_KERNEL_SCHEDULER_LOCK_IS_RELEASED (0x0) |
| 76 | #define MER_KERNEL_SCHEDULER_LOCK_IS_NOT_RELEASED (0x1) |
| 77 | |
| 78 | //#define __MER_KERNEL_SCHEDULER_ENCODE_DI_PRIORITY__ |
| 79 | |
| 80 | #ifndef MER_KERNEL_IS_ASSEMBLY |
| 81 | #error "please include public header" |
| 82 | #endif /* MER_KERNEL_IS_ASSEMBLY */ |
| 83 | |
| 84 | #if !MER_KERNEL_IS_ASSEMBLY |
| 85 | |
| 86 | #include "mer_service_types.h" |
| 87 | #include "mer_kernel_utility_target.h" |
| 88 | #include "mer_kernel.h" |
| 89 | #include "mer_kernel_dpc.h" |
| 90 | |
| 91 | /////////////////////////////////////////////////////////////////////////////// |
| 92 | // Internal type |
| 93 | /////////////////////////////////////////////////////////////////////////////// |
| 94 | typedef struct { |
| 95 | mer_uint32 priority; |
| 96 | mer_uint32 affinity_mask; |
| 97 | mer_uint32 job_index; |
| 98 | mer_uint32 self_adjust; |
| 99 | } mer_kernel_scheduler_job; |
| 100 | |
| 101 | typedef void (*mer_kernel_scheduler_job_callback)(mer_uint32); |
| 102 | //typedef void (*mer_kernel_scheduler_set_job_callback)(mer_uint32); |
| 103 | |
| 104 | typedef enum { |
| 105 | MER_KERNEL_SCHEDULER_INSERT_TO_HEAD = 0, |
| 106 | MER_KERNEL_SCHEDULER_INSERT_TO_TAIL = 2, |
| 107 | MER_KERNEL_SCHEDULER_INSERT_TO_HEAD_AND_RETURN = 0x10, |
| 108 | MER_KERNEL_SCHEDULER_INSERT_TO_TAIL_AND_RETURN = 0x12, |
| 109 | } mer_kernel_scheduler_job_insert_type; |
| 110 | |
| 111 | typedef enum { |
| 112 | MER_KERNEL_SCHEDULER_CONTEXT_TASK = 1, |
| 113 | MER_KERNEL_SCHEDULER_CONTEXT_DPC = 2, |
| 114 | MER_KERNEL_SCHEDULER_CONTEXT_ISR = 3, |
| 115 | MER_KERNEL_SCHEDULER_CONTEXT_NONE = 4, |
| 116 | } mer_kernel_scheduler_context_type; |
| 117 | |
| 118 | /////////////////////////////////////////////////////////////////////////////// |
| 119 | // External Variable |
| 120 | /////////////////////////////////////////////////////////////////////////////// |
| 121 | extern mer_kernel_scheduler_job mer_kernel_scheduler_running_job[MIPS_HW_VPE_NUM]; |
| 122 | extern mer_kernel_scheduler_job mer_kernel_scheduler_ready_job[MIPS_HW_VPE_NUM]; |
| 123 | extern mer_kernel_scheduler_job_callback mer_kernel_scheduler_job_insert[]; |
| 124 | extern mer_uint32 mer_kernel_scheduler_idle_flag_mips[MIPS_HW_VPE_NUM]; |
| 125 | |
| 126 | extern mer_uint32 mer_kernel_dpc_info_table_begin; |
| 127 | extern mer_uint32 mer_kernel_dpc_info_table_end; |
| 128 | extern mer_uint32 mer_kernel_task_info_table_begin; |
| 129 | extern mer_uint32 mer_kernel_task_info_table_end; |
| 130 | |
| 131 | /////////////////////////////////////////////////////////////////////////////// |
| 132 | // External Function |
| 133 | /////////////////////////////////////////////////////////////////////////////// |
| 134 | extern void mer_kernel_scheduler_reschedule_job(); |
| 135 | mer_uint32 mer_kernel_scheduler_insert_job(mer_uint32 new_job_priority, |
| 136 | mer_uint32 new_job_affinity_mask, |
| 137 | mer_uint32 new_job_index, |
| 138 | mer_kernel_scheduler_job_insert_type new_job_insert_position); |
| 139 | void mer_kernel_scheduler_switch_local_job(mer_uint32 current_vpe); |
| 140 | void mer_kernel_scheduler_adjust_priority(mer_uint32 current_vpe); |
| 141 | |
| 142 | /* Low power */ |
| 143 | mer_uint32 mer_kernel_scheduler_remove_vpes_from_scheduling(); |
| 144 | void mer_kernel_scheduler_restore_current_vpe_to_scheduling(mer_uint32 core); |
| 145 | void mer_kernel_scheduler_set_slave_vpe_idle_flag(); |
| 146 | void mer_kernel_scheduler_remove_slave_vpe_idle_flag(); |
| 147 | |
| 148 | void mer_kernel_scheduler_initialization(); |
| 149 | |
| 150 | mer_kernel_scheduler_context_type mer_kernel_scheduler_get_current_context_level(); |
| 151 | |
| 152 | /////////////////////////////////////////////////////////////////////////////// |
| 153 | // Static Inline Function |
| 154 | /////////////////////////////////////////////////////////////////////////////// |
| 155 | /** |
| 156 | * Check whether the job priority of VPE is in ISR |
| 157 | * Time complexity O(1) |
| 158 | * |
| 159 | * @param[out] 1 if in ISR, otherwise 0 if in DPC/Task |
| 160 | * @param[in] priority |
| 161 | * |
| 162 | */ |
| 163 | static inline mer_uint32 mer_kernel_scheduler_is_in_isr(mer_uint32 priority){ |
| 164 | |
| 165 | mer_uint32 non_isr_mask = 0xC0000000; |
| 166 | return ((priority & non_isr_mask) == 0); |
| 167 | } |
| 168 | |
| 169 | /** |
| 170 | * Encode DI to current running priority |
| 171 | * Time complexity O(1) |
| 172 | * |
| 173 | * @param[out] current_vpe |
| 174 | * @param[in] N/A |
| 175 | * |
| 176 | */ |
| 177 | static inline void mer_kernel_scheduler_di(mer_uint32 current_vpe){ |
| 178 | |
| 179 | #if defined(__MER_KERNEL_SCHEDULER_ENCODE_DI_PRIORITY__) |
| 180 | #error "should not compile" |
| 181 | #if defined(__MER_KERNEL_USE_MIPS_CP0_CONTEXT_REG__) |
| 182 | if (MER_KERNEL_GET_INTERRUPT_COUNT()){ |
| 183 | #else |
| 184 | if (mer_kernel_interrupt_count[current_vpe]){ |
| 185 | #endif /* __MER_KERNEL_USE_MIPS_CP0_CONTEXT_REG__ */ |
| 186 | return; |
| 187 | } |
| 188 | |
| 189 | mer_uint32 priority = mer_kernel_scheduler_running_job[current_vpe].priority; |
| 190 | mer_uint32 level_mask = ((priority & (~MER_KERNEL_SCHEDULER_JOB_INDEX_MASK)) >> 2); |
| 191 | mer_uint32 job_mask = (priority & MER_KERNEL_SCHEDULER_JOB_INDEX_MASK); |
| 192 | |
| 193 | mer_kernel_scheduler_running_job[current_vpe].priority = level_mask | job_mask; |
| 194 | mer_kernel_utility_coherence_sync(); |
| 195 | |
| 196 | #endif /* __MER_KERNEL_SCHEDULER_ENCODE_DI_PRIORITY__ */ |
| 197 | } |
| 198 | |
| 199 | /** |
| 200 | * Encode EI to current running priority |
| 201 | * Time complexity O(1) |
| 202 | * |
| 203 | * @param[out] N/A |
| 204 | * @param[in] current_vpe |
| 205 | * |
| 206 | */ |
| 207 | static inline void mer_kernel_scheduler_ei(mer_uint32 current_vpe){ |
| 208 | |
| 209 | #if defined(__MER_KERNEL_SCHEDULER_ENCODE_DI_PRIORITY__) |
| 210 | #error "should not compile" |
| 211 | #if defined(__MER_KERNEL_USE_MIPS_CP0_CONTEXT_REG__) |
| 212 | if (MER_KERNEL_GET_INTERRUPT_COUNT()){ |
| 213 | #else |
| 214 | if (mer_kernel_interrupt_count[current_vpe]){ |
| 215 | #endif /* __MER_KERNEL_USE_MIPS_CP0_CONTEXT_REG__ */ |
| 216 | |
| 217 | mer_uint32 priority = mer_kernel_scheduler_running_job[current_vpe].priority; |
| 218 | mer_uint32 level_mask = ((priority & (~MER_KERNEL_SCHEDULER_JOB_INDEX_MASK)) << 2); |
| 219 | mer_uint32 job_mask = (priority & MER_KERNEL_SCHEDULER_JOB_INDEX_MASK); |
| 220 | |
| 221 | mer_kernel_scheduler_running_job[current_vpe].priority = level_mask | job_mask; |
| 222 | mer_kernel_utility_coherence_sync(); |
| 223 | |
| 224 | #endif /* __MER_KERNEL_SCHEDULER_ENCODE_DI_PRIORITY__ */ |
| 225 | |
| 226 | } |
| 227 | |
| 228 | /** |
| 229 | * Set wake up status for child VPE (VPE1 in 93/95) before it reset |
| 230 | * Time complexity O(1) |
| 231 | * |
| 232 | * @param[out] N/A |
| 233 | * @param[in] N/A |
| 234 | * |
| 235 | */ |
| 236 | static inline void mer_kernel_scheduler_notify_child_vpe(){ |
| 237 | |
| 238 | #if defined(__MD97__) || defined(__MD97P__) |
| 239 | mer_uint32 current_vpe = mer_kernel_utility_get_current_vpe_id(); |
| 240 | mer_kernel_scheduler_idle_flag_mips[current_vpe+1] = MER_KERNEL_SCHEDULER_WAKE_UP_STATE; |
| 241 | mer_kernel_scheduler_idle_flag_mips[current_vpe+2] = MER_KERNEL_SCHEDULER_WAKE_UP_STATE; |
| 242 | #else |
| 243 | #error "should porting to new platform" |
| 244 | #endif |
| 245 | |
| 246 | } |
| 247 | |
| 248 | /** |
| 249 | * check if current context level is in dpc or not |
| 250 | * Time complexity O(1) |
| 251 | * |
| 252 | * @param[out] TRUE/FALSE |
| 253 | * @param[in] N/A |
| 254 | * |
| 255 | */ |
| 256 | __attribute__((always_inline)) static inline mer_uint32 mer_kernel_scheduler_is_in_dpc(){ |
| 257 | |
| 258 | #if defined(__MER_KERNEL_USE_MIPS_CP0_CONTEXT_REG__) |
| 259 | #if 0 |
| 260 | /* under construction !*/ |
| 261 | /* under construction !*/ |
| 262 | /* under construction !*/ |
| 263 | /* under construction !*/ |
| 264 | /* under construction !*/ |
| 265 | #else |
| 266 | /* Use the type in structure instead to have a better profiling result */ |
| 267 | mer_uint32 control_block = MER_KERNEL_GET_CURRENT_CONTROL_BLOCK(); |
| 268 | return ((MER_KERNEL_GET_INTERRUPT_COUNT() == 0) && \ |
| 269 | (control_block) && \ |
| 270 | (((mer_service_dpc_cb *)(control_block))->job_type == MER_SERVICE_TYPE_DPC)); |
| 271 | #endif |
| 272 | #else |
| 273 | #error "should porting this part" |
| 274 | #endif |
| 275 | } |
| 276 | |
| 277 | /** |
| 278 | * check if current context level is in task or not |
| 279 | * Time complexity O(1) |
| 280 | * |
| 281 | * @param[out] TRUE/FALSE |
| 282 | * @param[in] N/A |
| 283 | * |
| 284 | */ |
| 285 | __attribute__((always_inline)) static inline mer_uint32 mer_kernel_scheduler_is_in_task(){ |
| 286 | |
| 287 | #if defined(__MER_KERNEL_USE_MIPS_CP0_CONTEXT_REG__) |
| 288 | #if 0 |
| 289 | /* under construction !*/ |
| 290 | /* under construction !*/ |
| 291 | /* under construction !*/ |
| 292 | /* under construction !*/ |
| 293 | /* under construction !*/ |
| 294 | #else |
| 295 | /* Use the type in structure instead to have a better profiling result */ |
| 296 | mer_uint32 control_block = MER_KERNEL_GET_CURRENT_CONTROL_BLOCK(); |
| 297 | return ((MER_KERNEL_GET_INTERRUPT_COUNT() == 0) && \ |
| 298 | (control_block) && \ |
| 299 | (((mer_service_dpc_cb *)(control_block))->job_type == MER_SERVICE_TYPE_TASK)); |
| 300 | #endif |
| 301 | |
| 302 | #else |
| 303 | #error "should porting this part" |
| 304 | #endif |
| 305 | } |
| 306 | |
| 307 | /** |
| 308 | * atomic read |
| 309 | * |
| 310 | * @param[out] value |
| 311 | * @param[in] address |
| 312 | * |
| 313 | */ |
| 314 | static inline mer_uint32 mer_kernel_scheduler_atmoic_read(mer_uint32 *ptr){ |
| 315 | |
| 316 | mer_uint32 ret_value = 0; |
| 317 | __asm__ __volatile__( |
| 318 | "ll %0, 0(%1) \n" \ |
| 319 | :"=d"(ret_value) \ |
| 320 | :"d"(ptr) \ |
| 321 | :"memory" \ |
| 322 | ); |
| 323 | |
| 324 | return ret_value; |
| 325 | } |
| 326 | |
| 327 | /** |
| 328 | * atomic write |
| 329 | * |
| 330 | * @param[out] result |
| 331 | * @param[in] address |
| 332 | * |
| 333 | */ |
| 334 | static inline mer_uint32 mer_kernel_scheduler_atomic_write(mer_uint32 *ptr, mer_uint32 result){ |
| 335 | |
| 336 | __asm__ __volatile__( |
| 337 | "sc %0, 0(%1) \n" \ |
| 338 | :"=d"(result) \ |
| 339 | :"d"(ptr), "0"(result) \ |
| 340 | :"memory" \ |
| 341 | ); |
| 342 | |
| 343 | return result; |
| 344 | } |
| 345 | |
| 346 | /** |
| 347 | * Wipe out the VPE running priority when entering ISR |
| 348 | * |
| 349 | * Time complexity O(1) |
| 350 | * |
| 351 | * @param[out] N/A |
| 352 | * @param[in] vpe_id, job_stack_pointer |
| 353 | * |
| 354 | */ |
| 355 | __attribute__((always_inline)) static inline void mer_kernel_scheduler_enter_isr(mer_uint32 vpe_id, mer_uint32 job_stack_pointer) |
| 356 | { |
| 357 | /* Note that we do not take schedule lock here since we DO NOT allow ignore |
| 358 | ISR priority. It means, when I am in ISR, NO other VPE will send OS |
| 359 | interrupt to me even when I am a extremely short ISR from idle. |
| 360 | By doing so, we can make sure when sending an interrupt, the target |
| 361 | should receive OS interrupt immediately (unless it disable interrupt) |
| 362 | This reduce the chance that multiple OS interrupt send to same VPE. |
| 363 | */ |
| 364 | |
| 365 | |
| 366 | #if defined(__MER_KERNEL_DYNAMIC_BALANCE_JOB__) |
| 367 | mer_uint32 *job_sp = (mer_uint32 *)(MER_KERNEL_GET_CURRENT_CONTROL_BLOCK()); |
| 368 | *job_sp = job_stack_pointer; |
| 369 | #else |
| 370 | mer_uint32 *job_sp = (mer_uint32 *)(MER_KERNEL_GET_CURRENT_CONTROL_BLOCK()); |
| 371 | *job_sp = job_stack_pointer; |
| 372 | #endif |
| 373 | |
| 374 | mer_uint32 result; |
| 375 | |
| 376 | do { |
| 377 | //mer_kernel_utility_coherence_sync(); |
| 378 | |
| 379 | mer_uint32 *ptr = &mer_kernel_scheduler_running_job[vpe_id].priority; |
| 380 | |
| 381 | mer_uint32 ll_value = mer_kernel_scheduler_atmoic_read(ptr); |
| 382 | |
| 383 | mer_uint32 level_mask = ((ll_value & (~MER_KERNEL_SCHEDULER_JOB_INDEX_MASK)) >> 2); |
| 384 | mer_uint32 job_mask = (ll_value & MER_KERNEL_SCHEDULER_JOB_INDEX_MASK); |
| 385 | |
| 386 | result = mer_kernel_scheduler_atomic_write(ptr, (level_mask | job_mask)); |
| 387 | |
| 388 | }while(result == 0); |
| 389 | |
| 390 | //mer_kernel_utility_coherence_sync(); |
| 391 | |
| 392 | } |
| 393 | |
| 394 | /** |
| 395 | * Recover out the VPE running priority when leaving ISR |
| 396 | * |
| 397 | * Time complexity O(1) |
| 398 | * |
| 399 | * @param[out] N/A |
| 400 | * @param[in] N/A |
| 401 | * |
| 402 | */ |
| 403 | __attribute__((always_inline)) static inline mer_bool mer_kernel_scheduler_recover_job_info(mer_uint32 vpe_id){ |
| 404 | |
| 405 | mer_uint32 result, self_reorder, ready_job = mer_kernel_scheduler_ready_job[vpe_id].priority; |
| 406 | mer_uint32 *ptr = &mer_kernel_scheduler_running_job[vpe_id].priority; |
| 407 | |
| 408 | do { |
| 409 | |
| 410 | mer_uint32 ll_value = mer_kernel_scheduler_atmoic_read(ptr); |
| 411 | |
| 412 | mer_uint32 level_mask = ((ll_value & (~MER_KERNEL_SCHEDULER_JOB_INDEX_MASK)) << 2); |
| 413 | mer_uint32 job_mask = (ll_value & MER_KERNEL_SCHEDULER_JOB_INDEX_MASK); |
| 414 | mer_uint32 origin_mask = (level_mask | job_mask); |
| 415 | |
| 416 | self_reorder = (ready_job < origin_mask); |
| 417 | |
| 418 | result = mer_kernel_scheduler_atomic_write(ptr, origin_mask); |
| 419 | |
| 420 | }while(result == 0); |
| 421 | |
| 422 | return self_reorder; |
| 423 | |
| 424 | } |
| 425 | |
| 426 | #endif /* !MER_IS_ASSEMBLY */ |
| 427 | |
| 428 | #endif /* _MER_KERNEL_SCHEDULER_H_ */ |
| 429 | |