blob: 8eca4e32e6c200b3a42ee3732c0b172d1c37ca32 [file] [log] [blame]
/*****************************************************************************
* 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_ */