| /***************************************************************************** |
| * Copyright Statement: |
| * -------------------- |
| * This software is protected by Copyright and the information contained |
| * herein is confidential. The software may not be copied and the information |
| * contained herein may not be used or disclosed except with the written |
| * permission of MediaTek Inc. (C) 2018 |
| * |
| * BY OPENING THIS FILE, BUYER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES |
| * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE") |
| * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO BUYER ON |
| * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. |
| * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE |
| * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR |
| * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND BUYER AGREES TO LOOK ONLY TO SUCH |
| * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. MEDIATEK SHALL ALSO |
| * NOT BE RESPONSIBLE FOR ANY MEDIATEK SOFTWARE RELEASES MADE TO BUYER'S |
| * SPECIFICATION OR TO CONFORM TO A PARTICULAR STANDARD OR OPEN FORUM. |
| * |
| * BUYER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND CUMULATIVE |
| * LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE, |
| * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE, |
| * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY BUYER TO |
| * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE. |
| * |
| * THE TRANSACTION CONTEMPLATED HEREUNDER SHALL BE CONSTRUED IN ACCORDANCE |
| * WITH THE LAWS OF THE STATE OF CALIFORNIA, USA, EXCLUDING ITS CONFLICT OF |
| * LAWS PRINCIPLES. ANY DISPUTES, CONTROVERSIES OR CLAIMS ARISING THEREOF AND |
| * RELATED THERETO SHALL BE SETTLED BY ARBITRATION IN SAN FRANCISCO, CA, UNDER |
| * THE RULES OF THE INTERNATIONAL CHAMBER OF COMMERCE (ICC). |
| * |
| *****************************************************************************/ |
| |
| /***************************************************************************** |
| * |
| * Filename: |
| * --------- |
| * mer_kernel_scheduler.h |
| * |
| * Project: |
| * -------- |
| * MERTOS |
| * |
| * Description: |
| * ------------ |
| * MERTOS scheduler header |
| * |
| * Author: |
| * ------- |
| * ------- |
| * |
| *****************************************************************************/ |
| #ifndef _MER_KERNEL_SCHEDULER_H_ |
| #define _MER_KERNEL_SCHEDULER_H_ |
| #include "mer_kernel_config_public.h" |
| |
| #if defined(__MD97__) || defined(__MD97P__) |
| |
| #define MER_KERNEL_SCHEDULER_WAKE_UP_STATE 0x2 |
| #define MER_KERNEL_SCHEDULER_BUSY_STATE 0x0 |
| #define MER_KERNEL_SCHEDULER_IDLE_STATE 0x1 |
| |
| #define MER_KERNEL_SCHEDULER_CORE0_MASK 0x7 |
| #define MER_KERNEL_SCHEDULER_CORE1_MASK 0x38 |
| |
| #else |
| #error "unsupport platform" |
| #endif /* __MD97__ || __MD97P__ */ |
| |
| #define MER_KERNEL_SCHEDULER_JOB_INDEX_MASK (0xFFFFFFF) |
| #define MER_KERNEL_SCHEDULER_JOB_PRIORITY_MASK (~(MER_KERNEL_SCHEDULER_JOB_INDEX_MASK)) |
| |
| #define MER_KERNEL_SCHEDULER_LOCK_IS_RELEASED (0x0) |
| #define MER_KERNEL_SCHEDULER_LOCK_IS_NOT_RELEASED (0x1) |
| |
| //#define __MER_KERNEL_SCHEDULER_ENCODE_DI_PRIORITY__ |
| |
| #ifndef MER_KERNEL_IS_ASSEMBLY |
| #error "please include public header" |
| #endif /* MER_KERNEL_IS_ASSEMBLY */ |
| |
| #if !MER_KERNEL_IS_ASSEMBLY |
| |
| #include "mer_service_types.h" |
| #include "mer_kernel_utility_target.h" |
| #include "mer_kernel.h" |
| #include "mer_kernel_dpc.h" |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Internal type |
| /////////////////////////////////////////////////////////////////////////////// |
| typedef struct { |
| mer_uint32 priority; |
| mer_uint32 affinity_mask; |
| mer_uint32 job_index; |
| mer_uint32 self_adjust; |
| } mer_kernel_scheduler_job; |
| |
| typedef void (*mer_kernel_scheduler_job_callback)(mer_uint32); |
| //typedef void (*mer_kernel_scheduler_set_job_callback)(mer_uint32); |
| |
| typedef enum { |
| MER_KERNEL_SCHEDULER_INSERT_TO_HEAD = 0, |
| MER_KERNEL_SCHEDULER_INSERT_TO_TAIL = 2, |
| MER_KERNEL_SCHEDULER_INSERT_TO_HEAD_AND_RETURN = 0x10, |
| MER_KERNEL_SCHEDULER_INSERT_TO_TAIL_AND_RETURN = 0x12, |
| } mer_kernel_scheduler_job_insert_type; |
| |
| typedef enum { |
| MER_KERNEL_SCHEDULER_CONTEXT_TASK = 1, |
| MER_KERNEL_SCHEDULER_CONTEXT_DPC = 2, |
| MER_KERNEL_SCHEDULER_CONTEXT_ISR = 3, |
| MER_KERNEL_SCHEDULER_CONTEXT_NONE = 4, |
| } mer_kernel_scheduler_context_type; |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // External Variable |
| /////////////////////////////////////////////////////////////////////////////// |
| extern mer_kernel_scheduler_job mer_kernel_scheduler_running_job[MIPS_HW_VPE_NUM]; |
| extern mer_kernel_scheduler_job mer_kernel_scheduler_ready_job[MIPS_HW_VPE_NUM]; |
| extern mer_kernel_scheduler_job_callback mer_kernel_scheduler_job_insert[]; |
| extern mer_uint32 mer_kernel_scheduler_idle_flag_mips[MIPS_HW_VPE_NUM]; |
| |
| extern mer_uint32 mer_kernel_dpc_info_table_begin; |
| extern mer_uint32 mer_kernel_dpc_info_table_end; |
| extern mer_uint32 mer_kernel_task_info_table_begin; |
| extern mer_uint32 mer_kernel_task_info_table_end; |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // External Function |
| /////////////////////////////////////////////////////////////////////////////// |
| extern void mer_kernel_scheduler_reschedule_job(); |
| mer_uint32 mer_kernel_scheduler_insert_job(mer_uint32 new_job_priority, |
| mer_uint32 new_job_affinity_mask, |
| mer_uint32 new_job_index, |
| mer_kernel_scheduler_job_insert_type new_job_insert_position); |
| void mer_kernel_scheduler_switch_local_job(mer_uint32 current_vpe); |
| void mer_kernel_scheduler_adjust_priority(mer_uint32 current_vpe); |
| |
| /* Low power */ |
| mer_uint32 mer_kernel_scheduler_remove_vpes_from_scheduling(); |
| void mer_kernel_scheduler_restore_current_vpe_to_scheduling(mer_uint32 core); |
| void mer_kernel_scheduler_set_slave_vpe_idle_flag(); |
| void mer_kernel_scheduler_remove_slave_vpe_idle_flag(); |
| |
| void mer_kernel_scheduler_initialization(); |
| |
| mer_kernel_scheduler_context_type mer_kernel_scheduler_get_current_context_level(); |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Static Inline Function |
| /////////////////////////////////////////////////////////////////////////////// |
| /** |
| * Check whether the job priority of VPE is in ISR |
| * Time complexity O(1) |
| * |
| * @param[out] 1 if in ISR, otherwise 0 if in DPC/Task |
| * @param[in] priority |
| * |
| */ |
| static inline mer_uint32 mer_kernel_scheduler_is_in_isr(mer_uint32 priority){ |
| |
| mer_uint32 non_isr_mask = 0xC0000000; |
| return ((priority & non_isr_mask) == 0); |
| } |
| |
| /** |
| * Encode DI to current running priority |
| * Time complexity O(1) |
| * |
| * @param[out] current_vpe |
| * @param[in] N/A |
| * |
| */ |
| static inline void mer_kernel_scheduler_di(mer_uint32 current_vpe){ |
| |
| #if defined(__MER_KERNEL_SCHEDULER_ENCODE_DI_PRIORITY__) |
| #error "should not compile" |
| #if defined(__MER_KERNEL_USE_MIPS_CP0_CONTEXT_REG__) |
| if (MER_KERNEL_GET_INTERRUPT_COUNT()){ |
| #else |
| if (mer_kernel_interrupt_count[current_vpe]){ |
| #endif /* __MER_KERNEL_USE_MIPS_CP0_CONTEXT_REG__ */ |
| return; |
| } |
| |
| mer_uint32 priority = mer_kernel_scheduler_running_job[current_vpe].priority; |
| mer_uint32 level_mask = ((priority & (~MER_KERNEL_SCHEDULER_JOB_INDEX_MASK)) >> 2); |
| mer_uint32 job_mask = (priority & MER_KERNEL_SCHEDULER_JOB_INDEX_MASK); |
| |
| mer_kernel_scheduler_running_job[current_vpe].priority = level_mask | job_mask; |
| mer_kernel_utility_coherence_sync(); |
| |
| #endif /* __MER_KERNEL_SCHEDULER_ENCODE_DI_PRIORITY__ */ |
| } |
| |
| /** |
| * Encode EI to current running priority |
| * Time complexity O(1) |
| * |
| * @param[out] N/A |
| * @param[in] current_vpe |
| * |
| */ |
| static inline void mer_kernel_scheduler_ei(mer_uint32 current_vpe){ |
| |
| #if defined(__MER_KERNEL_SCHEDULER_ENCODE_DI_PRIORITY__) |
| #error "should not compile" |
| #if defined(__MER_KERNEL_USE_MIPS_CP0_CONTEXT_REG__) |
| if (MER_KERNEL_GET_INTERRUPT_COUNT()){ |
| #else |
| if (mer_kernel_interrupt_count[current_vpe]){ |
| #endif /* __MER_KERNEL_USE_MIPS_CP0_CONTEXT_REG__ */ |
| |
| mer_uint32 priority = mer_kernel_scheduler_running_job[current_vpe].priority; |
| mer_uint32 level_mask = ((priority & (~MER_KERNEL_SCHEDULER_JOB_INDEX_MASK)) << 2); |
| mer_uint32 job_mask = (priority & MER_KERNEL_SCHEDULER_JOB_INDEX_MASK); |
| |
| mer_kernel_scheduler_running_job[current_vpe].priority = level_mask | job_mask; |
| mer_kernel_utility_coherence_sync(); |
| |
| #endif /* __MER_KERNEL_SCHEDULER_ENCODE_DI_PRIORITY__ */ |
| |
| } |
| |
| /** |
| * Set wake up status for child VPE (VPE1 in 93/95) before it reset |
| * Time complexity O(1) |
| * |
| * @param[out] N/A |
| * @param[in] N/A |
| * |
| */ |
| static inline void mer_kernel_scheduler_notify_child_vpe(){ |
| |
| #if defined(__MD97__) || defined(__MD97P__) |
| mer_uint32 current_vpe = mer_kernel_utility_get_current_vpe_id(); |
| mer_kernel_scheduler_idle_flag_mips[current_vpe+1] = MER_KERNEL_SCHEDULER_WAKE_UP_STATE; |
| mer_kernel_scheduler_idle_flag_mips[current_vpe+2] = MER_KERNEL_SCHEDULER_WAKE_UP_STATE; |
| #else |
| #error "should porting to new platform" |
| #endif |
| |
| } |
| |
| /** |
| * check if current context level is in dpc or not |
| * Time complexity O(1) |
| * |
| * @param[out] TRUE/FALSE |
| * @param[in] N/A |
| * |
| */ |
| __attribute__((always_inline)) static inline mer_uint32 mer_kernel_scheduler_is_in_dpc(){ |
| |
| #if defined(__MER_KERNEL_USE_MIPS_CP0_CONTEXT_REG__) |
| #if 0 |
| /* under construction !*/ |
| /* under construction !*/ |
| /* under construction !*/ |
| /* under construction !*/ |
| /* under construction !*/ |
| #else |
| /* Use the type in structure instead to have a better profiling result */ |
| mer_uint32 control_block = MER_KERNEL_GET_CURRENT_CONTROL_BLOCK(); |
| return ((MER_KERNEL_GET_INTERRUPT_COUNT() == 0) && \ |
| (control_block) && \ |
| (((mer_service_dpc_cb *)(control_block))->job_type == MER_SERVICE_TYPE_DPC)); |
| #endif |
| #else |
| #error "should porting this part" |
| #endif |
| } |
| |
| /** |
| * check if current context level is in task or not |
| * Time complexity O(1) |
| * |
| * @param[out] TRUE/FALSE |
| * @param[in] N/A |
| * |
| */ |
| __attribute__((always_inline)) static inline mer_uint32 mer_kernel_scheduler_is_in_task(){ |
| |
| #if defined(__MER_KERNEL_USE_MIPS_CP0_CONTEXT_REG__) |
| #if 0 |
| /* under construction !*/ |
| /* under construction !*/ |
| /* under construction !*/ |
| /* under construction !*/ |
| /* under construction !*/ |
| #else |
| /* Use the type in structure instead to have a better profiling result */ |
| mer_uint32 control_block = MER_KERNEL_GET_CURRENT_CONTROL_BLOCK(); |
| return ((MER_KERNEL_GET_INTERRUPT_COUNT() == 0) && \ |
| (control_block) && \ |
| (((mer_service_dpc_cb *)(control_block))->job_type == MER_SERVICE_TYPE_TASK)); |
| #endif |
| |
| #else |
| #error "should porting this part" |
| #endif |
| } |
| |
| /** |
| * atomic read |
| * |
| * @param[out] value |
| * @param[in] address |
| * |
| */ |
| static inline mer_uint32 mer_kernel_scheduler_atmoic_read(mer_uint32 *ptr){ |
| |
| mer_uint32 ret_value = 0; |
| __asm__ __volatile__( |
| "ll %0, 0(%1) \n" \ |
| :"=d"(ret_value) \ |
| :"d"(ptr) \ |
| :"memory" \ |
| ); |
| |
| return ret_value; |
| } |
| |
| /** |
| * atomic write |
| * |
| * @param[out] result |
| * @param[in] address |
| * |
| */ |
| static inline mer_uint32 mer_kernel_scheduler_atomic_write(mer_uint32 *ptr, mer_uint32 result){ |
| |
| __asm__ __volatile__( |
| "sc %0, 0(%1) \n" \ |
| :"=d"(result) \ |
| :"d"(ptr), "0"(result) \ |
| :"memory" \ |
| ); |
| |
| return result; |
| } |
| |
| /** |
| * Wipe out the VPE running priority when entering ISR |
| * |
| * Time complexity O(1) |
| * |
| * @param[out] N/A |
| * @param[in] vpe_id, job_stack_pointer |
| * |
| */ |
| __attribute__((always_inline)) static inline void mer_kernel_scheduler_enter_isr(mer_uint32 vpe_id, mer_uint32 job_stack_pointer) |
| { |
| /* Note that we do not take schedule lock here since we DO NOT allow ignore |
| ISR priority. It means, when I am in ISR, NO other VPE will send OS |
| interrupt to me even when I am a extremely short ISR from idle. |
| By doing so, we can make sure when sending an interrupt, the target |
| should receive OS interrupt immediately (unless it disable interrupt) |
| This reduce the chance that multiple OS interrupt send to same VPE. |
| */ |
| |
| |
| #if defined(__MER_KERNEL_DYNAMIC_BALANCE_JOB__) |
| mer_uint32 *job_sp = (mer_uint32 *)(MER_KERNEL_GET_CURRENT_CONTROL_BLOCK()); |
| *job_sp = job_stack_pointer; |
| #else |
| mer_uint32 *job_sp = (mer_uint32 *)(MER_KERNEL_GET_CURRENT_CONTROL_BLOCK()); |
| *job_sp = job_stack_pointer; |
| #endif |
| |
| mer_uint32 result; |
| |
| do { |
| //mer_kernel_utility_coherence_sync(); |
| |
| mer_uint32 *ptr = &mer_kernel_scheduler_running_job[vpe_id].priority; |
| |
| mer_uint32 ll_value = mer_kernel_scheduler_atmoic_read(ptr); |
| |
| mer_uint32 level_mask = ((ll_value & (~MER_KERNEL_SCHEDULER_JOB_INDEX_MASK)) >> 2); |
| mer_uint32 job_mask = (ll_value & MER_KERNEL_SCHEDULER_JOB_INDEX_MASK); |
| |
| result = mer_kernel_scheduler_atomic_write(ptr, (level_mask | job_mask)); |
| |
| }while(result == 0); |
| |
| //mer_kernel_utility_coherence_sync(); |
| |
| } |
| |
| /** |
| * Recover out the VPE running priority when leaving ISR |
| * |
| * Time complexity O(1) |
| * |
| * @param[out] N/A |
| * @param[in] N/A |
| * |
| */ |
| __attribute__((always_inline)) static inline mer_bool mer_kernel_scheduler_recover_job_info(mer_uint32 vpe_id){ |
| |
| mer_uint32 result, self_reorder, ready_job = mer_kernel_scheduler_ready_job[vpe_id].priority; |
| mer_uint32 *ptr = &mer_kernel_scheduler_running_job[vpe_id].priority; |
| |
| do { |
| |
| mer_uint32 ll_value = mer_kernel_scheduler_atmoic_read(ptr); |
| |
| mer_uint32 level_mask = ((ll_value & (~MER_KERNEL_SCHEDULER_JOB_INDEX_MASK)) << 2); |
| mer_uint32 job_mask = (ll_value & MER_KERNEL_SCHEDULER_JOB_INDEX_MASK); |
| mer_uint32 origin_mask = (level_mask | job_mask); |
| |
| self_reorder = (ready_job < origin_mask); |
| |
| result = mer_kernel_scheduler_atomic_write(ptr, origin_mask); |
| |
| }while(result == 0); |
| |
| return self_reorder; |
| |
| } |
| |
| #endif /* !MER_IS_ASSEMBLY */ |
| |
| #endif /* _MER_KERNEL_SCHEDULER_H_ */ |
| |