blob: 02f3a65fd585fbc0eded2f293784bb230144fc7f [file] [log] [blame]
/* Copyright 2012 The ChromiumOS Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/* Task scheduling / events module for Chrome EC operating system */
#ifndef __CROS_EC_TASK_H
#define __CROS_EC_TASK_H
#ifdef __cplusplus
extern "C" {
#endif
#include "atomic_t.h"
#include "common.h"
#include "compile_time_macros.h"
#include "task_id.h"
#include <stdbool.h>
/* Task event bitmasks */
/* Tasks may use the bits in TASK_EVENT_CUSTOM_BIT for their own events */
#define TASK_EVENT_CUSTOM_BIT(x) BUILD_CHECK_INLINE(BIT(x), BIT(x) & 0x0ffff)
/* Used to signal that sysjump preparation has completed */
#define TASK_EVENT_SYSJUMP_READY BIT(16)
/* Used to signal that IPC layer is available for sending new data */
#define TASK_EVENT_IPC_READY BIT(17)
#define TASK_EVENT_PD_AWAKE BIT(18)
/* npcx peci event */
#define TASK_EVENT_PECI_DONE BIT(19)
/* I2C tx/rx interrupt handler completion event. */
#ifdef CHIP_STM32
#define TASK_EVENT_I2C_COMPLETION(port) (1 << ((port) + 20))
#define TASK_EVENT_I2C_IDLE (TASK_EVENT_I2C_COMPLETION(0))
#define TASK_EVENT_MAX_I2C 6
#ifdef I2C_PORT_COUNT
#if (I2C_PORT_COUNT > TASK_EVENT_MAX_I2C)
#error "Too many i2c ports for i2c events"
#endif
#endif
#else
#define TASK_EVENT_I2C_IDLE BIT(20)
#define TASK_EVENT_PS2_DONE BIT(21)
#endif
/* DMA transmit complete event */
#define TASK_EVENT_DMA_TC BIT(26)
/* ADC interrupt handler event */
#define TASK_EVENT_ADC_DONE BIT(27)
/*
* task_reset() that was requested has been completed
*
* For test-only builds, may be used by some tasks to restart themselves.
*/
#define TASK_EVENT_RESET_DONE BIT(28)
/* task_wake() called on task */
#define TASK_EVENT_WAKE BIT(29)
/* Mutex unlocking */
#define TASK_EVENT_MUTEX BIT(30)
/*
* Timer expired. For example, task_wait_event() timed out before receiving
* another event.
*/
#define TASK_EVENT_TIMER (1U << 31)
/* Maximum time for task_wait_event() */
#define TASK_MAX_WAIT_US 0x7fffffff
/**
* Disable CPU interrupt bit.
*
* This might break the system so think really hard before using these. There
* are usually better ways of accomplishing this.
*/
void interrupt_disable(void);
/**
* Enable CPU interrupt bit.
*/
void interrupt_enable(void);
/**
* Check if interrupts are enabled
*/
bool is_interrupt_enabled(void);
/*
* Define irq_lock and irq_unlock that match the function signatures to Zephyr's
* functions. In reality, these simply call the current implementation of
* interrupt_disable() and interrupt_enable().
*/
#ifndef CONFIG_ZEPHYR
/**
* Perform the same operation as interrupt_disable but allow nesting. The
* return value from this function should be used as the argument to
* irq_unlock. Do not attempt to parse the value, it is a representation
* of the state and not an indication of any form of count.
*
* For more information see:
* https://docs.zephyrproject.org/latest/reference/kernel/other/interrupts.html#c.irq_lock
*
* @return Lock key to use for restoring the state via irq_unlock.
*/
uint32_t irq_lock(void);
/**
* Perform the same operation as interrupt_enable but allow nesting. The key
* should be the unchanged value returned by irq_lock.
*
* For more information see:
* https://docs.zephyrproject.org/latest/reference/kernel/other/interrupts.html#c.irq_unlock
*
* @param key The lock-out key used to restore the interrupt state.
*/
void irq_unlock(uint32_t key);
#endif /* CONFIG_ZEPHYR */
/**
* Return true if we are in interrupt context.
*/
bool in_interrupt_context(void);
/**
* Return true if we are in software interrupt context.
*/
bool in_soft_interrupt_context(void);
/**
* Return current interrupt mask with disabling interrupt. Meaning is
* chip-specific and should not be examined; just pass it to set_int_mask() to
* restore a previous interrupt state after interrupt disable.
*/
uint32_t read_clear_int_mask(void);
/**
* Set interrupt mask. As with interrupt_disable(), use with care.
*/
void set_int_mask(uint32_t val);
/**
* Set a task event.
*
* If the task is higher priority than the current task, this will cause an
* immediate context switch to the new task.
*
* Can be called both in interrupt context and task context.
*
* @param tskid Task to set event for
* @param event Event bitmap to set (TASK_EVENT_*)
*/
void task_set_event(task_id_t tskid, uint32_t event);
/**
* Wake a task. This sends it the TASK_EVENT_WAKE event.
*
* @param tskid Task to wake
*/
static inline void task_wake(task_id_t tskid)
{
task_set_event(tskid, TASK_EVENT_WAKE);
}
/**
* Return the identifier of the task currently running.
*/
task_id_t task_get_current(void);
#ifdef CONFIG_ZEPHYR
/**
* Check if this current task is running in deferred context
*/
bool in_deferred_context(void);
#else
/* All ECOS deferred calls run from the HOOKS task */
static inline bool in_deferred_context(void)
{
#ifdef HAS_TASK_HOOKS
return (task_get_current() == TASK_ID_HOOKS);
#else
return false;
#endif /* HAS_TASK_HOOKS */
}
#endif /* CONFIG_ZEPHYR */
/**
* Return a pointer to the bitmap of events of the task.
*/
atomic_t *task_get_event_bitmap(task_id_t tskid);
/**
* Wait for the next event.
*
* If one or more events are already pending, returns immediately. Otherwise,
* it de-schedules the calling task and wakes up the next one in the priority
* order. Automatically clears the bitmap of received events before returning
* the events which are set.
*
* @param timeout_us If > 0, sets a timer to produce the TASK_EVENT_TIMER
* event after the specified micro-second duration.
*
* @return The bitmap of received events.
*/
uint32_t task_wait_event(int timeout_us);
/**
* Wait for any event included in an event mask.
*
* If one or more events are already pending, returns immediately. Otherwise,
* it de-schedules the calling task and wakes up the next one in the priority
* order. Automatically clears the bitmap of received events before returning
* the events which are set.
*
* @param event_mask Bitmap of task events to wait for.
*
* @param timeout_us If > 0, sets a timer to produce the TASK_EVENT_TIMER
* event after the specified micro-second duration.
*
* @return The bitmap of received events. Includes
* TASK_EVENT_TIMER if the timeout is reached.
*/
uint32_t task_wait_event_mask(uint32_t event_mask, int timeout_us);
/**
* Prints the list of tasks.
*
* Uses the command output channel. May be called from interrupt level.
*/
void task_print_list(void);
/**
* Returns the name of the task.
*/
const char *task_get_name(task_id_t tskid);
#ifdef CONFIG_TASK_PROFILING
/**
* Start tracking an interrupt.
*
* This must be called from interrupt context (!) before the interrupt routine
* is called.
*/
void task_start_irq_handler(void *excep_return);
void task_end_irq_handler(void *excep_return);
#else
#define task_start_irq_handler(excep_return)
#endif
/**
* Change the task scheduled to run after returning from the exception.
*
* If task_send_event() has been called and has set need_resched flag,
* re-computes which task is running and eventually swaps the context
* saved on the process stack to restore the new one at exception exit.
*
* This must be called from interrupt context (!) and is designed to be the
* last call of the interrupt handler.
*/
void task_resched_if_needed(void *excep_return);
/**
* Initialize tasks and interrupt controller.
*/
void task_pre_init(void);
/**
* Start task scheduling. Does not normally return.
*/
int task_start(void);
/**
* Return non-zero if task_start() has been called and task scheduling has
* started.
*/
int task_start_called(void);
#ifdef CONFIG_FPU
/**
* Clear floating-point used flag for currently executing task. This means the
* FPU regs will not be stored on context switches until the next time floating
* point is used for currently executing task.
*/
void task_clear_fp_used(void);
#endif
/**
* Mark all tasks as ready to run and reschedule the highest priority task.
*/
void task_enable_all_tasks(void);
/**
* Enable a task.
*/
void task_enable_task(task_id_t tskid);
/**
* Task is enabled.
*/
bool task_enabled(task_id_t tskid);
/**
* Disable a task.
*
* If the task disable itself, this will cause an immediate reschedule.
*/
void task_disable_task(task_id_t tskid);
/**
* Enable an interrupt.
*/
void task_enable_irq(int irq);
/**
* Disable an interrupt.
*/
void task_disable_irq(int irq);
/**
* Software-trigger an interrupt.
*/
void task_trigger_irq(int irq);
/*
* A task that supports resets may call this to indicate that it may be reset
* at any point between this call and the next call to task_disable_resets().
*
* Calling this function will trigger any resets that were requested while
* resets were disabled.
*
* It is not expected for this to be called if resets are already enabled.
*/
void task_enable_resets(void);
/*
* A task that supports resets may call this to indicate that it may not be
* reset until the next call to task_enable_resets(). Any calls to task_reset()
* during this time will cause a reset request to be queued, and executed
* the next time task_enable_resets() is called.
*
* Must not be called if resets are already disabled.
*/
void task_disable_resets(void);
/*
* If the current task was reset, completes the reset operation.
*
* Returns a non-zero value if the task was reset; tasks with state outside
* of the stack should perform any necessary cleanup immediately after calling
* this function.
*
* Tasks that support reset must call this function once at startup before
* doing anything else.
*
* Must only be called once at task startup.
*/
int task_reset_cleanup(void);
/*
* Resets the specified task, which must not be the current task,
* to initial state.
*
* Returns EC_SUCCESS, or EC_ERROR_INVAL if the specified task does
* not support resets.
*
* If wait is true, blocks until the task has been reset. Otherwise,
* returns immediately - in this case the task reset may be delayed until
* that task can be safely reset. The duration of this delay depends on the
* task implementation.
*/
int task_reset(task_id_t id, int wait);
/**
* Clear a pending interrupt.
*
* Note that most interrupts can be removed from the pending state simply by
* handling whatever caused the interrupt in the first place. This only needs
* to be called if an interrupt handler disables itself without clearing the
* reason for the interrupt, and then the interrupt is re-enabled from a
* different context.
*/
void task_clear_pending_irq(int irq);
/**
* Check if irq is pending.
*
* Returns true if interrupt with given number is pending, false otherwise.
*/
bool task_is_irq_pending(int irq);
#ifdef CONFIG_ZEPHYR
typedef struct k_mutex mutex_t;
#define mutex_lock(mtx) (k_mutex_lock(mtx, K_FOREVER))
#define mutex_unlock(mtx) (k_mutex_unlock(mtx))
#else
struct mutex {
uint32_t lock;
atomic_t waiters;
};
typedef struct mutex mutex_t;
/**
* K_MUTEX_DEFINE is a macro normally provided by the Zephyr kernel,
* and allows creation of a static mutex without the need to
* initialize it. We provide the same macro for CrOS EC OS so that we
* can use it in shared code.
*/
#define K_MUTEX_DEFINE(name) static mutex_t name = {}
/**
* Lock a mutex.
*
* This tries to lock the mutex mtx. If the mutex is already locked by another
* task, de-schedules the current task until the mutex is again unlocked.
*
* Must not be used in interrupt context!
*/
void mutex_lock(mutex_t *mtx);
/**
* Release a mutex previously locked by the same task.
*/
void mutex_unlock(mutex_t *mtx);
/** Zephyr will try to init the mutex using `k_mutex_init()`. */
#define k_mutex_init(mutex) 0
#endif /* CONFIG_ZEPHYR */
struct irq_priority {
uint8_t irq;
uint8_t priority;
};
/*
* Some cores may make use of this struct for mapping irqs to handlers
* for DECLARE_IRQ in a linker-script defined section.
*/
struct irq_def {
int irq;
/* The routine which was declared as an IRQ */
void (*routine)(void);
/*
* The routine usually needs wrapped so the core can handle it
* as an IRQ.
*/
void (*handler)(void);
};
/*
* Implement the DECLARE_IRQ(irq, routine, priority) macro which is
* a core specific helper macro to declare an interrupt handler "routine".
*/
#ifndef CONFIG_ZEPHYR
#ifdef CONFIG_COMMON_RUNTIME
#include "irq_handler.h"
#else
#define IRQ_HANDLER(irqname) CONCAT3(irq_, irqname, _handler)
#define IRQ_HANDLER_OPT(irqname) CONCAT3(irq_, irqname, _handler_optional)
#define DECLARE_IRQ(irq, routine, priority) DECLARE_IRQ_(irq, routine, priority)
#define DECLARE_IRQ_(irq, routine, priority) \
static void __keep routine(void); \
void IRQ_HANDLER_OPT(irq)(void) __attribute__((alias(#routine)))
/* Include ec.irqlist here for compilation dependency */
#define ENABLE_IRQ(x)
#if !defined(CONFIG_DFU_BOOTMANAGER_MAIN)
#include "ec.irqlist"
#endif /* !defined(CONFIG_DFU_BOOTMANAGER_MAIN) */
#endif /* CONFIG_COMMON_RUNTIME */
#endif /* !CONFIG_ZEPHYR */
#ifdef __cplusplus
}
#endif
#endif /* __CROS_EC_TASK_H */